/// <summary> /// Registers a transition into this group. /// </summary> /// <param name="transition">The transition you want to register.</param> /// <param name="type">The type of the registered transition.</param> public void RegisterTransition(PNTransition transition, PetriNet.PNTransitionType type) { this.transitions.Add(transition, type); /// Every transition is fireable at the beginning. TransitionBecameFireable(transition, true); }
/// <summary> /// Creates an edge from this place to the given transition with the given weight. /// </summary> /// <param name="target">The target transition of the edge.</param> /// <param name="weight">The weight of the edge.</param> public void CreateOutputEdge(PNTransition target, int weight) { if (!this.outputEdges.ContainsKey(target)) { this.outputEdges.Add(target, weight); } else { throw new ArgumentException("Output edge to the given target already exists!"); } }
/// <summary> /// This function is called by a transition that is being fired, when it wants to remove the tokens from it's /// input places. /// </summary> /// <param name="caller"> /// The transition that called this function. /// </param> public void RequestTokens(PNTransition caller) { if (this.outputEdges.ContainsKey(caller)) { int tokenNum = this.outputEdges[caller]; this.tokens = (this.tokens >= tokenNum) ? (this.tokens - tokenNum) : (0); foreach (KeyValuePair <PNTransition, int> edge in this.outputEdges) { if (this.tokens < edge.Value) { edge.Key.FireConditionChange(this, false); } } } else { throw new PetriNetException("Not enough tokens for the given transition!"); } }
/// <summary> /// This function is called if a transition became fireable or unfireable. /// </summary> /// <param name="caller">The caller transition.</param> /// <param name="fireable">True if the transition became fireable, false if it became unfireable.</param> public void TransitionBecameFireable(PNTransition caller, bool fireable) { if (fireable) { /// Add the transition to the fireable transition set, and release the event if this is the first /// fireable transition. if (this.fireableTransitions.Count == 0) { this.releaseEvent.Set(); } this.fireableTransitions.Add(caller); } else { /// Remove the transition from the fireable transition set and reset the release event if necessary. this.fireableTransitions.Remove(caller); if (this.fireableTransitions.Count == 0) { this.releaseEvent.Reset(); } } }
/// <summary> /// Call this function from a thread to attach that thread to the Petri-network. The Petri-network will automatically /// synchronize the attached threads depending on it's structure. /// </summary> /// <param name="externalTransitions"> /// List of the external transitions that the thread wants to fire. The thread will be blocked until at least one /// of these transitions becomes fireable. /// </param> /// <param name="callbackFunctions"> /// This map has to assign a callback function for each callback transitions. /// </param> /// <remarks> /// The caller thread will be blocked until at least one of the given external transitions becomes fireable. Then /// the Petri-network will synchronize the thread while a callback transition becomes fireable. Then this transition /// will be fired, the assigned callback function will be called, and the thread will be detached from the /// Petri-network. /// </remarks> public void AttachThread(int[] externalTransitions, Dictionary <int, PNCallback> callbackFunctions) { lock (this.disposeLock) { if (this.pnDisposed) { throw new ObjectDisposedException("PetriNet"); } } if (!this.buildFinished) { throw new PetriNetException("Petri-network is under construction!"); } if (externalTransitions == null || externalTransitions.Length == 0) { throw new ArgumentNullException("externalTransitions"); } if (callbackFunctions == null || callbackFunctions.Count == 0) { throw new ArgumentNullException("callbackFunctions"); } RCSet <PNTransition> extTransitions = new RCSet <PNTransition>(); Dictionary <PNTransition, PNCallback> callbacks = new Dictionary <PNTransition, PNCallback>(); PNTransition firstExt = null; for (int i = 0; i < externalTransitions.Length; ++i) { int trIdx = externalTransitions[i]; if (trIdx >= 0 && trIdx < this.transitions.Length) { if (!callbackFunctions.ContainsKey(trIdx)) { extTransitions.Add(this.transitions[trIdx]); if (firstExt == null) { firstExt = this.transitions[trIdx]; } } else { throw new ArgumentException("Transition " + trIdx + " already exists in externalTransitions[" + i + "]!", "callbackFunctions"); } } else { throw new ArgumentException("Transition " + trIdx + " doesn't exist!", "externalTransitions[" + i + "]"); } } foreach (KeyValuePair <int, PNCallback> item in callbackFunctions) { int trIdx = item.Key; if (trIdx >= 0 && trIdx < this.transitions.Length) { callbacks.Add(this.transitions[trIdx], item.Value); } else { throw new ArgumentException("Transition " + trIdx + " doesn't exist!", "callbackFunctions"); } } firstExt.Group.AttachThread(extTransitions, callbacks); }
/// <see cref="PetriNet.AttachThread"/> public void AttachThread(RCSet <PNTransition> extTransitions, Dictionary <PNTransition, PetriNet.PNCallback> callbacks) { if (this.threadAttached.WaitOne(0)) { try { /// Check the parameters. CheckThreadAttachParameters(extTransitions, callbacks); /// First we only allowed to fire external transitions. PetriNet.PNTransitionType stage = PetriNet.PNTransitionType.EXTERNAL; /// Start the thread control loop. while (true) { /// Wait for a fireable transition. this.releaseEvent.WaitOne(); lock (this.lockObject) { while (this.fireableTransitions.Count > 0) { if (stage == PetriNet.PNTransitionType.EXTERNAL) { /// Find the first fireable external transition PNTransition firstExtTr = null; foreach (PNTransition fireableTr in this.fireableTransitions) { if (this.transitions[fireableTr] == PetriNet.PNTransitionType.CALLBACK) { throw new PetriNetException("Invalid type of fireable transition detected!"); } if (firstExtTr == null && this.transitions[fireableTr] == PetriNet.PNTransitionType.EXTERNAL && extTransitions.Contains(fireableTr)) { firstExtTr = fireableTr; } } /// Fire the transition. if (firstExtTr != null) { firstExtTr.Fire(); stage = PetriNet.PNTransitionType.INTERNAL; } else { /// No fireable external transitions have been found. break; } } else if (stage == PetriNet.PNTransitionType.INTERNAL) { /// Find the first fireable internal transition or a callback transition PNTransition trToFire = null; foreach (PNTransition fireableTr in this.fireableTransitions) { if (this.transitions[fireableTr] == PetriNet.PNTransitionType.INTERNAL) { if (trToFire == null) { trToFire = fireableTr; } } else if (this.transitions[fireableTr] == PetriNet.PNTransitionType.CALLBACK) { if (this.fireableTransitions.Count == 1) { /// End of thread attach: fire the transition, invoke the callback and return. fireableTr.Fire(); callbacks[fireableTr](fireableTr.Index); /// Detach the thread and exit from the function. this.threadAttached.Release(); return; } else { throw new PetriNetException("Invalid state during a thread attach!"); } } else { throw new PetriNetException("Invalid type of fireable transition detected!"); } } if (trToFire != null) { trToFire.Fire(); } else { /// No fireable internal transitions have been found. break; } } } } } } catch { /// Error occured --> Detach the thread and re-throw the exception. this.threadAttached.Release(); throw; } } else { throw new PetriNetException("You cannot attach more than one threads to the same transition group at the same time!"); } }