Example #1
0
 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;
     }
 }
Example #2
0
        /// <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));
        }
Example #3
0
        // 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);
        }
Example #4
0
        /// <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));
        }
Example #5
0
 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;
     }
 }
Example #6
0
        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();
        }
Example #7
0
        ///	<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);
        }
Example #8
0
        /// <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));
        }
Example #9
0
        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]);
                }
            }
        }
Example #10
0
 /// <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");
            }
        }