void OnDestroy() { // ask Laalys to quit if (networkStream != null) { byte[] sendBytes = Encoding.UTF8.GetBytes("Quit"); networkStream.Write(sendBytes, 0, sendBytes.Length); } // close Socket if (clientSocket != null) { clientSocket.Close(); } if (serverSocket != null) { serverSocket.Stop(); } // Stop process if (LaalysProcess != null && !LaalysProcess.HasExited) { // Stop to capture output stream LaalysProcess.Exited -= LaalysEH; LaalysProcess.Kill(); } // Save traces if (Application.isPlaying && debugLogs) { XmlHandler.saveTraces(SceneManager.GetActiveScene().name); } if (Instance == this) { Instance = null; } }
/// <summary> /// Trace game action. /// </summary> /// <param name="family">The monitored Family to use to build trace.</param> /// <param name="actionName">Action name you want to trace, this name has to match with a transition defined into associated Petri Net of the "family" parameter <see cref="ComponentMonitoring.PnmlFile"/>.</param> /// <param name="performedBy">Specify who perform this action, the player or the system. <see cref="MonitoringManager.Source"/></param> /// <param name="processLinks">Set to false if the logic expression associated to the action include "+" operators AND the action performed by the player is not allowed by the system. In this case fourth parameters will not be processed. True (default) means fourth parameter will be analysed.</param> /// <param name="linksConcerned">links label concerned by this action. You can leave empty if only "*" operators are used in logic expression. Must be defined if logic expression associated to the action include "+" operators. For instance, if logic expression is "(l0+l1)*l3" you have to indicate which links to use to build the trace: l0 and l3 OR l1 and l3 => <code>MonitoringManager.trace(..., "l0", "l3");</code> OR <code>MonitoringManager.trace(..., "l1", "l3");</code></param> /// <returns> labels found for this game action if in game analysis is enabled (see: MonitoringManager). return empty Array else </returns> public static string[] trace(Family family, string actionName, string performedBy, bool processLinks = true, params string[] linksConcerned) { System.Diagnostics.StackFrame stackFrame = new System.Diagnostics.StackFrame(1, true); // get caller stackFrame with informations string exceptionStackTrace = "(at " + stackFrame.GetFileName() + ":" + stackFrame.GetFileLineNumber().ToString() + ")"; // to point where this function was called if (MonitoringManager.Instance == null) { throw new TraceAborted("No MonitoringManager found. You must add MonitoringManager component to one of your GameObject first (the Main_Loop for instance).", null); } FamilyMonitoring fm = MonitoringManager.Instance.getFamilyMonitoring(family); if (fm == null) { throw new TraceAborted("No monitor found for this family.", null); } string internalName = fm.getInternalName(actionName, exceptionStackTrace, processLinks, linksConcerned); if (fm.fullPnSelected >= MonitoringManager.Instance.PetriNetsName.Count) { fm.fullPnSelected = 1; } string pnName = MonitoringManager.Instance.PetriNetsName[fm.fullPnSelected]; return(MonitoringManager.processTrace(pnName, internalName, performedBy)); }
// Extract from Laalys actions' name the associated ComponentMonitoring and game action private static List <KeyValuePair <ComponentMonitoring, string> > extractTuplesFromActionsName(string [] actions) { List <KeyValuePair <ComponentMonitoring, string> > results = new List <KeyValuePair <ComponentMonitoring, string> >(); // extract Monitoring id from actions' name foreach (string action in actions) { string[] tokens = action.Split('_'); // last token is id int id; if (tokens.Length > 2 && Int32.TryParse(tokens[tokens.Length - 1], out id)) { ComponentMonitoring cm = MonitoringManager.getMonitorById(id); if (cm != null) { // second to last is game action name => Add pair results.Add(new KeyValuePair <ComponentMonitoring, string>(cm, tokens[tokens.Length - 2])); } else { UnityEngine.Debug.LogError("No MonitoringComponent with id: " + id); } } else { UnityEngine.Debug.LogError("Action name malformed: " + action); } } return(results); }
/// <summary> /// Get next actions to perform in order to reach targeted game action. /// </summary> /// <param name="family">The monitored Family on which you want reach action.</param> /// <param name="targetedActionName">Action name you want to reach, this name has to match with a transition defined into associated Petri Net of the "family" parameter <see cref="ComponentMonitoring.PnmlFile"/> The special key word "##playerObjectives##" enable to target all player objective actions defined inside full Petri Net from which the monitor is part of (in this special case, "linksConcerned" parameter will be ignore).</param> /// <param name="maxActions">Maximum number of actions returned.</param> /// <param name="linksConcerned">links label concerned by this action. You can leave empty if only "*" operators are used in logic expression. Must be defined if logic expression associated to the action include "+" operators. For instance, if logic expression is "(l0+l1)*l3" you have to indicate which links to use to look for the trace: l0 and l3 OR l1 and l3 => <code>MonitoringManager.getNextActionToReach(..., "l0", "l3");</code> OR <code>MonitoringManager.getNextActionToReach(..., "l1", "l3");</code></param> /// <returns>List of Pairs including a ComponentMonitoring and its associated game action useful to reach the targeted action, the number of actions returned is less or equal to maxActions parameters.</returns> public static List <KeyValuePair <ComponentMonitoring, string> > getNextActionsToReach(Family family, string targetedActionName, int maxActions, params string[] linksConcerned) { System.Diagnostics.StackFrame stackFrame = new System.Diagnostics.StackFrame(1, true); // get caller stackFrame with informations string exceptionStackTrace = "(at " + stackFrame.GetFileName() + ":" + stackFrame.GetFileLineNumber().ToString() + ")"; // to point where this function was called if (MonitoringManager.Instance == null) { throw new TraceAborted("No MonitoringManager found. You must add MonitoringManager component to one of your GameObject first (the Main_Loop for instance).", null); } FamilyMonitoring fm = MonitoringManager.Instance.getFamilyMonitoring(family); if (fm == null) { throw new TraceAborted("No monitor found for this family", null); } string internalName = targetedActionName; if (!targetedActionName.Equals("##playerObjectives##")) { internalName = fm.getInternalName(targetedActionName, exceptionStackTrace, true, linksConcerned); } if (fm.fullPnSelected >= MonitoringManager.Instance.PetriNetsName.Count) { fm.fullPnSelected = 1; } string pnName = MonitoringManager.Instance.PetriNetsName[fm.fullPnSelected]; return(MonitoringManager.getNextActionsToReach(pnName, internalName, maxActions)); }
void OnDestroy() { // close Socket if (clientSocket != null) { clientSocket.Close(); } if (serverSocket != null) { serverSocket.Stop(); } // Stop process if (LaalysProcess != null && !LaalysProcess.HasExited) { // Stop to capture output stream LaalysProcess.Exited -= LaalysEH; LaalysProcess.Kill(); } // Save traces if (Application.isPlaying) { XmlHandler.saveTraces(SceneManager.GetActiveScene().name); } if (Instance == this) { Instance = null; } }
internal void computeUniqueId() { mut.WaitOne(); try { MonitoringManager mm = MonitoringManager.Instance; // Check if we have to compute a new Id. if (id == -1) { // Get all used ids List <int> ids = new List <int>(); foreach (ComponentMonitoring _cm in mm.c_monitors) { if (_cm) { ids.Add(_cm.id); } } foreach (FamilyMonitoring _fm in mm.f_monitors) { if (_fm) { ids.Add(_fm.id); } } ids.Sort(); // Find the first hole available int newId = 0; int iterationLength = ids.Count + 1; for (; newId < iterationLength; newId++) { if (!ids.Contains(newId)) { break; } } id = newId; // update id of petrinet if (petriNet != null) { petriNet.attachID(id); } } // register this monitor mm.registerMonitor(this); } catch (Exception e) { mut.ReleaseMutex(); throw e; } mut.ReleaseMutex(); }
/// <summary> /// Check if action named actionName is still reachable inside the Petri net associated to this monitor /// </summary> public bool isStillReachable(string actionName) { List <List <string> > setOfLinksConcerned = getPossibleSetOfLinks(actionName); foreach (List <string> linksConcerned in setOfLinksConcerned) { if (MonitoringManager.getNextActionsToReach(this, actionName, int.MaxValue, linksConcerned.ToArray()).Count != 0) { return(true); } } return(false); }
/// <summary> /// Get next actions to perform in order to reach the player objective of the Petri net. /// </summary> /// <param name="pnName">The Petri net name to process.</param> /// <param name="maxActions">Maximum number of actions returned.</param> /// <returns>List of Pairs including a ComponentMonitoring and its associated game action useful to reach the player objective, the number of actions returned is less or equal to maxActions parameters.</returns> public static List <KeyValuePair <ComponentMonitoring, string> > getNextActionsToReachPlayerObjective(string pnName, int maxActions) { System.Diagnostics.StackFrame stackFrame = new System.Diagnostics.StackFrame(1, true); // get caller stackFrame with informations string exceptionStackTrace = "(at " + stackFrame.GetFileName() + ":" + stackFrame.GetFileLineNumber().ToString() + ")"; // to point where this function was called if (MonitoringManager.Instance == null) { throw new TraceAborted("No MonitoringManager found. You must add MonitoringManager component to one of your GameObject first (the Main_Loop for instance).", null); } if (!MonitoringManager.Instance.PetriNetsName.Contains(pnName)) { throw new TraceAborted("No Petri net with name \"" + pnName + "\" found", null); } string internalName = "##playerObjectives##"; return(MonitoringManager.getNextActionsToReach(pnName, internalName, maxActions)); }
private void ActionProcessing(GameObject go) { if (!this.Pause) { ActionPerformed[] listAP = go.GetComponents <ActionPerformed>(); int nb = listAP.Length; ActionPerformed ap; for (int j = 0; j < nb; j++) { ap = listAP[j]; ComponentMonitoring cMonitoring = null; string tmpActionName = ""; if (ap.name != "" && ap.overrideName != "") { //look for the ComponentMonitoring corresponding to name and overridename int matched = 0; foreach (ComponentMonitoring cm in go.GetComponents <ComponentMonitoring>()) { foreach (TransitionLink tl in cm.transitionLinks) { if (tl.transition.label == ap.name && tl.transition.overridedLabel == ap.overrideName) { if (matched == 0) { cMonitoring = cm; tmpActionName = ap.name; tmpString = cm.id.ToString(); } else { tmpString = string.Concat(tmpString, ", ", cm.id); } matched++; } } if (matched > 1) { Debug.LogException(new WarningException(string.Concat("Several ComponentMonitoring on ", go.name, " are matching the name \"", ap.name, "\" and the overrideName \"", ap.overrideName, "\". By default, the first one found is used to trace.", Environment.NewLine, "The ", matched, " corresponding are: ", tmpString), ap.exceptionStackTrace)); break; } } if (matched == 0 && ap.family == null) { throw new InvalidTraceException(string.Concat("Unable to trace action on \"", go.name, "\" because the name \"", ap.name, "\" and the overrideName \"", ap.overrideName, "\" in its ActionPerformed don't correspond to any ComponentMonitoring."), ap.exceptionStackTrace); } } else if (ap.name != "") { //look for the ComponentMonitoring corresponding to the name int matched = 0; foreach (ComponentMonitoring cm in go.GetComponents <ComponentMonitoring>()) { foreach (TransitionLink tl in cm.transitionLinks) { if (tl.transition.label == ap.name) { if (matched == 0) { cMonitoring = cm; tmpActionName = ap.name; tmpString = cm.id.ToString(); } else { tmpString = string.Concat(tmpString, ", ", cm.id); } matched++; } } if (matched > 1) { Debug.LogException(new WarningException(string.Concat("Several ComponentMonitoring on ", go.name, " are matching the name \"", ap.name, "\". By default, the first one found is used to trace.", Environment.NewLine, "The ", matched, " corresponding are: ", tmpString), ap.exceptionStackTrace)); break; } } if (matched == 0 && ap.family == null) { throw new InvalidTraceException(string.Concat("Unable to trace action on \"", go.name, "\" because the name \"", ap.name, "\" in its ActionPerformed doesn't correspond to any ComponentMonitoring."), ap.exceptionStackTrace); } } else if (ap.overrideName != "") { //look for the ComponentMonitoring corresponding to the overridename int matched = 0; foreach (ComponentMonitoring cm in go.GetComponents <ComponentMonitoring>()) { foreach (TransitionLink tl in cm.transitionLinks) { if (tl.transition.overridedLabel == ap.overrideName) { if (matched == 0) { cMonitoring = cm; tmpActionName = tl.transition.label; tmpString = cm.id.ToString(); } else { tmpString = string.Concat(tmpString, ", ", cm.id); } matched++; } } if (matched > 1) { Debug.LogException(new WarningException(string.Concat("Several ComponentMonitoring on ", go.name, " are matching the overrideName \"", ap.overrideName, "\". By default, the first one found is used to trace.", Environment.NewLine, "The ", matched, " corresponding are: ", tmpString), ap.exceptionStackTrace)); break; } } if (matched == 0 && ap.family == null) { throw new InvalidTraceException(string.Concat("Unable to trace action on \"", go.name, "\" because the overrideName \"", ap.overrideName, "\" in its ActionPerformed don't correspond to any ComponentMonitoring."), ap.exceptionStackTrace); } } else { throw new InvalidTraceException(string.Concat("Unable to trace action on \"", go.name, "\" because no name given in its ActionPerformed component."), ap.exceptionStackTrace); } if (cMonitoring) { if (ap.performedBy == "system") { tmpPerformer = ap.performedBy; } else { tmpPerformer = "player"; } if (ap.orLabels == null) { tmpLabels = MonitoringManager.trace(cMonitoring, tmpActionName, tmpPerformer); } else { tmpLabels = MonitoringManager.trace(cMonitoring, tmpActionName, tmpPerformer, true, ap.orLabels); } GameObjectManager.addComponent <Trace>(traces, new { actionName = tmpActionName, componentMonitoring = cMonitoring, performedBy = tmpPerformer, time = Time.time, orLabels = ap.orLabels, labels = tmpLabels }); // if (tmpLabels.Length != 0) // tmpString = tmpLabels[0]; // for (int i = 1; i < tmpLabels.Length; i++) // { // tmpString = string.Concat(tmpString, " ", tmpLabels[i]); // } //Debug.Log(string.Concat(tmpPerformer, " ", tmpActionName, " ", go.name, System.Environment.NewLine, tmpString)); //File.AppendAllText("Data/UnityLogs.txt", string.Concat(System.Environment.NewLine, "[", DateTime.Now.ToString("yyyy.MM.dd.hh.mm"), "] Log - ", tmpPerformer, " ", tmpActionName, " ", go.name, System.Environment.NewLine, tmpString)); } else if (ap.family != null) { try { tmpActionName = ap.name; if (ap.performedBy == "system") { tmpPerformer = ap.performedBy; } else { tmpPerformer = "player"; } if (ap.orLabels == null) { tmpLabels = MonitoringManager.trace(ap.family, tmpActionName, tmpPerformer); } else { tmpLabels = MonitoringManager.trace(ap.family, tmpActionName, tmpPerformer, true, ap.orLabels); } GameObjectManager.addComponent <Trace>(traces, new { actionName = tmpActionName, family = ap.family, performedBy = tmpPerformer, time = Time.time, orLabels = ap.orLabels, labels = tmpLabels }); // if (tmpLabels.Length != 0) // tmpString = tmpLabels[0]; // for (int i = 1; i < tmpLabels.Length; i++) // { // tmpString = string.Concat(tmpString, " ", tmpLabels[i]); // } //Debug.Log(string.Concat(tmpPerformer, " ", tmpActionName, " ", go.name, System.Environment.NewLine, tmpString)); //File.AppendAllText("Data/UnityLogs.txt", string.Concat(System.Environment.NewLine, "[", DateTime.Now.ToString("yyyy.MM.dd.hh.mm"), "] Log - ", tmpPerformer, " ", tmpActionName, " ", go.name, System.Environment.NewLine, tmpString)); } catch (global::System.Exception) { throw new InvalidTraceException(string.Concat("Unable to trace action \"", tmpActionName, "\" on the family because of invalid arguments in the ActionPerformed component or the family is not monitored."), ap.exceptionStackTrace); } } } for (int j = nb - 1; j > -1; j--) { GameObjectManager.removeComponent(listAP[j]); } } }
/// <summary> Set singleton instance of MonitoringManager </summary> // singleton => only one Monitoring Manager (see Awake) public MonitoringManager() { // Set the last object of MonitoringManager as current Instance Instance = this; }
/// <summary></summary> public override void OnInspectorGUI() { serializedObject.Update(); MonitoringManager mm = (MonitoringManager)target; // Guarantee that the list include at least one parameter with the scene name EditorGUILayout.PropertyField(_pnName, new GUIContent("Petri Nets Name", "Add names to this list to define available Full Petri nets name. The first element of this list is reserved to the scene name."), true); if (mm.PetriNetsName.Count <= 0) { mm.PetriNetsName.Add(SceneManager.GetActiveScene().name); } if (mm.PetriNetsName[0] != SceneManager.GetActiveScene().name) { mm.PetriNetsName[0] = SceneManager.GetActiveScene().name; } // Check that no elements equal the first one or equal "" for (int i = 1; i < mm.PetriNetsName.Count; i++) { if (mm.PetriNetsName[0] == mm.PetriNetsName[i] || mm.PetriNetsName[i] == "") { mm.PetriNetsName[i] = "Unnamed_" + i; } } if (GUILayout.Button("Build full Petri nets and features")) { GeneratePNandFeatures(); } bool newInGameAnalysis = EditorGUILayout.ToggleLeft(new GUIContent("Enable in game analysis", "If enabled, Laalys will be launched in game and each game action traced will be analysed depending on Full and Filtered Petri nets and features."), _inGameAnalysis.boolValue); if (newInGameAnalysis != _inGameAnalysis.boolValue) { Undo.RecordObject(mm, "Update In Game Analysis"); _inGameAnalysis.boolValue = newInGameAnalysis; } if (!newInGameAnalysis) { GUI.enabled = false; // disable following fields if in game analysis is disabled } bool newDebugLogs = EditorGUILayout.ToggleLeft(new GUIContent("Enable debug logs", "If enabled, Laalys will print debug logs."), _debugLogs.boolValue); if (newDebugLogs != _debugLogs.boolValue) { Undo.RecordObject(mm, "Update Debug Logs"); _debugLogs.boolValue = newDebugLogs; } string newFullPath = EditorGUILayout.TextField(new GUIContent("Full P. nets path:", "The full Petri nets location to use in case of in game analysis is enabled (default: \"./completeNets/\")."), _fullPetriNetsPath.stringValue); if (newFullPath != _fullPetriNetsPath.stringValue) { Undo.RecordObject(mm, "Update Full Petri nets path"); _fullPetriNetsPath.stringValue = newFullPath; } string newFilteredPath = EditorGUILayout.TextField(new GUIContent("Filtered P. nets path:", "The filtered Petri nets location to use in case of in game analysis is enabled (default: \"./filteredNets/\")."), _filteredPetriNetsPath.stringValue); if (newFilteredPath != _filteredPetriNetsPath.stringValue) { Undo.RecordObject(mm, "Update Filtered Petri nets path"); _filteredPetriNetsPath.stringValue = newFilteredPath; } string newFeaturesPath = EditorGUILayout.TextField(new GUIContent("Features path:", "The features location to use in case of in game analysis is enabled (default: \"./features/\")."), _featuresPath.stringValue); if (newFeaturesPath != _featuresPath.stringValue) { Undo.RecordObject(mm, "Update Features path"); _featuresPath.stringValue = newFeaturesPath; } if (!newInGameAnalysis) { GUI.enabled = true; // reset default } EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); string newLaalysPath = EditorGUILayout.TextField(new GUIContent("Laalys path:", "The path to the Laalys jar file."), _laalysPath.stringValue); if (newLaalysPath != _laalysPath.stringValue) { Undo.RecordObject(mm, "Update Laalys path"); _laalysPath.stringValue = newLaalysPath; } if (GUILayout.Button("Launch Laalys")) { Process LaalysProcess = new Process(); LaalysProcess.StartInfo.FileName = "java.exe"; LaalysProcess.StartInfo.Arguments = "-jar " + _laalysPath.stringValue; LaalysProcess.Start(); } serializedObject.ApplyModifiedProperties(); }
private void GeneratePNandFeatures() { // List of full Petri nets // Key => Petri net name defined inside monitor.fullPn // Value => Pair <PetriNet, offsetX> Dictionary <string, KeyValuePair <PetriNet, float> > petriNets = new Dictionary <string, KeyValuePair <PetriNet, float> >(); bool abort = false; MonitoringManager mm = (MonitoringManager)target; // Fill final PN foreach (ComponentMonitoring monitor in Resources.FindObjectsOfTypeAll <ComponentMonitoring> ()) { // Get full Petri net for this monitor if (monitor.fullPnSelected >= mm.PetriNetsName.Count) { monitor.fullPnSelected = 0; } string fullName = mm.PetriNetsName[monitor.fullPnSelected]; if (!petriNets.ContainsKey(fullName)) { petriNets[fullName] = new KeyValuePair <PetriNet, float>(new PetriNet(), 0); } PetriNet fullPn = petriNets[fullName].Key; float offsetX = petriNets[fullName].Value; // Check if PN exists if (monitor.PetriNet != null && !abort) { // Make a copy of local PN in order to organise it spatially without changing original PN PetriNet tmpPN = new PetriNet(monitor.PetriNet, monitor.gameObject.name); tmpPN.addWidth(offsetX); fullPn.addSubNet(tmpPN); // Process links foreach (TransitionLink transitionLink in monitor.transitionLinks) { // Check if all links target the same fullPn foreach (Link curLink in transitionLink.links) { if (!curLink.isCompatibleWithPnName(monitor.fullPnSelected)) { abort = true; EditorUtility.DisplayDialog("Error, building aborted", "The action \"" + transitionLink.transition.label + "\" of monitor \"" + monitor.gameObject.name + " (ref: " + monitor.id + ")\" is linked with GameObject \"" + curLink.linkedObject.name + "\" (label: \"" + curLink.label + "\"). But these two elements are not affected to the same full Petri net.\n\nPlease review your configurations and try again.", "Close"); } } // Make a copy of current transition and prefix its name with its game object name Node curTransition_copy = new Node(transitionLink.transition); string publicLabel = curTransition_copy.label + " "; if (curTransition_copy.overridedLabel != null && !curTransition_copy.overridedLabel.Equals("")) { publicLabel = curTransition_copy.overridedLabel + " "; } if (monitor is FamilyMonitoring) { publicLabel = publicLabel + ((FamilyMonitoring)monitor).equivalentName; } else { publicLabel = publicLabel + monitor.gameObject.name; } curTransition_copy.label = monitor.gameObject.name + "_" + curTransition_copy.label; // Add this transition to Features XmlHandler.addFeature(fullName, curTransition_copy.label + "_" + monitor.id, publicLabel, transitionLink.isSystemAction, transitionLink.isEndAction); Node oldTransition = curTransition_copy; if (isNullOrWhiteSpace(transitionLink.logic)) { // Default : And of all link foreach (Link curLink in transitionLink.links) { if (curLink.linkedObject != null) { // Make a copy of linked place and prefix its name with its game object name Node linkedPlace = curLink.getPlace(); if (linkedPlace != null) { Node linkedPlace_copy = new Node(linkedPlace); linkedPlace_copy.label = curLink.linkedObject.name + "_" + linkedPlace_copy.label; // Define arc type ArcType arcType = curLink.type == 2 ? Arc.stringToArcType(Arc.optType.ElementAt(curLink.flagsType)) : ArcType.regular; // Create arc between Transition and linked place (depends on Get/Produce/Require diffusion state) fullPn.arcs.Add(curLink.type != 1 ? new Arc(linkedPlace_copy, curTransition_copy, arcType, curLink.weight) : new Arc(curTransition_copy, linkedPlace_copy, arcType, curLink.weight)); } } } } else { if (ExpressionParser.isValid(transitionLink)) { // Logic expression is valid // Distribute expression string[] exp = ExpressionParser.getDistribution(transitionLink.logic); int or = 0; // Parse distributed expression foreach (string token in exp) { // Check if current token is an operator if (!token.Equals("+") && !token.Equals("*")) { // It's not an operator => we load the link Link curLink = transitionLink.getLabeledLink(token); if (curLink.linkedObject != null) { // Make a copy of linked place and prefix its name with its game object name Node linkedPlace = curLink.getPlace(); if (linkedPlace != null) { Node linkedPlace_copy = new Node(linkedPlace); linkedPlace_copy.label = curLink.linkedObject.name + "_" + linkedPlace_copy.label; // Define arc type ArcType arcType = curLink.type == 2 ? Arc.stringToArcType(Arc.optType.ElementAt(curLink.flagsType)) : ArcType.regular; // Create arc between Transition and linked place (depends on Get/Produce/Require diffusion state) fullPn.arcs.Add(curLink.type != 1 ? new Arc(linkedPlace_copy, curTransition_copy, arcType, curLink.weight) : new Arc(curTransition_copy, linkedPlace_copy, arcType, curLink.weight)); } } } else if (token.Equals("+")) { // We detect OR operator => add a new transition and set it as current node // Build new transition, we keep old transition to build links after // Add offset to position curTransition_copy.position.x += offsetX; curTransition_copy.position.y += 50; curTransition_copy = new Node("or" + (or++) + "_" + oldTransition.label, curTransition_copy.id, curTransition_copy.offset, curTransition_copy.initialMarking, curTransition_copy.position); // Add this new transition to PN fullPn.transitions.Add(curTransition_copy); // and to features XmlHandler.addFeature(fullName, curTransition_copy.label + "_" + monitor.id, publicLabel, transitionLink.isSystemAction, transitionLink.isEndAction); // Duplicate arcs from old transition foreach (Arc a in tmpPN.getConcernedArcs(oldTransition)) { if (a.target.label.Equals(oldTransition.label)) { fullPn.arcs.Add(new Arc(a.source, curTransition_copy, a.type, a.weight)); } else if (a.source.label.Equals(oldTransition.label)) { fullPn.arcs.Add(new Arc(curTransition_copy, a.target, a.type, a.weight)); } } } } } else { UnityEngine.Debug.LogError("Petri Net Building aborted: Logic expression of \"" + transitionLink.transition.label + "\" action from \"" + monitor.gameObject.name + "\" is not valid => \"" + transitionLink.logic + "\". Please check it from Monitor edit view."); } } } petriNets[fullName] = new KeyValuePair <PetriNet, float>(fullPn, offsetX + monitor.PetriNet.getWidth() + 50); // Add spaces between PN } } if (!abort) { // Save all Petri nets foreach (KeyValuePair <string, KeyValuePair <PetriNet, float> > pn in petriNets) { PnmlParser.SaveAtPath(pn.Value.Key, pn.Key + ".pnml"); } // and features XmlHandler.saveFeatures(); EditorUtility.DisplayDialog("Files built", "Files have been saved into \"completeNets\" and \"features\" folders of this Unity project", "Close"); } }