/// <summary> /// Associates a trigger with corresponding guards. /// </summary> /// <param name="trigger">The trigger to associate.</param> private void CalculateTriggerDependencies(ProductTrigger trigger) { var ct = trigger.PreCondition; var gtpost = trigger.PostCondition; var gtpre = ct; TraceDependencies("trigger '{0}' conditions [{1}, {2}] ...", trigger.Name, gtpre, gtpost); if (!trigger.ModifiedVariables.Any()) { TraceDependencies(" does not modify state."); return; } foreach (var guard in Guards) { var gg = guard.PreCondition; TraceDependencies(" guard '{0}' precondition [{1}] ...", guard.Name, gg); var tg = new TriggerGuard(trigger, guard); if (guard.Transitions.Any()) { TraceDependencies(" state change guard ..."); if (!guard.ModifiedVariables.Intersect(trigger.ModifiedVariables).Any()) { TraceDependencies(" no affected variable."); continue; } var gatepre = Gate.ComposeAND(gtpre, gg); if (gatepre.Type == GateType.Fixed) { TraceDependencies(" precondition does not match."); continue; } var gatepost = Gate.ComposeAND(gtpost, guard.PostCondition); if (gatepost.Type == GateType.Fixed) { TraceDependencies(" postcondition does not match."); continue; } TraceDependencies(" combination ({0}, {1}) handler.", gatepre, gatepost); tg.PreCondition = gatepre; tg.PostCondition = gatepost; } else { TraceDependencies(" static condition [{0}] ...", gg); Debug.Assert(guard.PreCondition.ID == guard.PostCondition.ID); IGate genter, gleave; if (guard.Type == GuardType.ENTER) { // PRE[trigger] AND NOT POST[guard] // trigger condition met, guard condition not met genter = Gate.ComposeAND(trigger.PreCondition, Gate.Invert(guard.PreCondition)); // PRE[trigger] AND PRE[guard] // trigger condition met, guard conditon not met gleave = Gate.ComposeAND(trigger.PostCondition, guard.PostCondition); } else { genter = Gate.ComposeAND(trigger.PreCondition, guard.PreCondition); gleave = Gate.ComposeAND(trigger.PostCondition, Gate.Invert(guard.PostCondition)); } var tgenter = genter; var tgleave = gleave; Gate.TraceDependencies(genter, gleave, "guard product"); // set all factors not appearing in the trigger to 1 genter = ReplaceTransitionVariables(trigger.Transitions, genter, true); gleave = ReplaceTransitionVariables(trigger.Transitions, gleave, true); Gate.TraceDependencies(genter, gleave, "inter product"); // set all factors not appearing in the guard to 1 genter = ReplaceNonGuardVariables(genter, guard); gleave = ReplaceNonGuardVariables(gleave, guard); /*genter = ReplaceTransitionVariables(guard.Transitions, genter, true); * gleave = ReplaceTransitionVariables(guard.Transitions, gleave, true);*/ Gate.TraceDependencies(genter, gleave, "guard match"); //if (genter is FalseGate || gleave is FalseGate) if (genter.Type.IsFixed() || gleave.Type.IsFixed()) { TraceDependencies(" condition does not match."); continue; } /*tg.PreCondition = guard.PreCondition; * tg.PostCondition = guard.PostCondition;*/ tg.PreCondition = tgenter; tg.PostCondition = tgleave; Gate.TraceDependencies(tgenter, tgleave, "guard conditions"); // see if the transition set is non-empty var transx = trigger.Transitions.Match(genter, gleave).ToArray(); if (transx.Length > 0) { if (guard.Type == GuardType.ENTER) { TraceDependencies(" entry handler."); } else if (guard.Type == GuardType.LEAVE) { TraceDependencies(" exit handler."); } else { throw new InvalidOperationException("unexpected guard type."); } } else { TraceDependencies(" transition does not match."); continue; } } // associate the trigger with the guard and vice versa trigger.AddGuard(tg); } }
internal static IGate Simplify(this IGate gate) { return(Gate.Simplify(gate)); }
/// <summary> /// Adds a trigger to the state machine. /// </summary> /// <param name="trigger">The trigger object to add.</param> public void AddTrigger(Trigger trigger) { var e = trigger.Event; SetModify(); try { TraceDependencies("add trigger '{0}' ...", trigger.Name); // ValidateTrigger(trigger); var tlist = new List <ProductTrigger>(); // examinate the precondition ... var precondition = trigger.PreCondition; if (precondition.Type == GateType.Fixed) { // constant if (precondition is FalseGate) { Trace("SMG033: warning: trigger '{0}' precondition is never met.", trigger); } else if (precondition is TrueGate) { Trace("SMG034: warning: trigger '{0}' precondition is always true.", trigger); tlist.Add(new ProductTrigger(trigger)); } } else if (precondition.Type == GateType.OR) { // OR combination, split into multiple simple triggers var inputs = precondition.GetInputs(); TraceDependencies("split trigger [{0}] into its branches ...", inputs.ToSeparatorList()); // analyze conditions // TraceDependencies("{0}", trigger.Transitions.ToDebugString()); // split into multiple triggers foreach (var product in inputs) { // extract the transition subset for the guard condition var tset = new TransitionSet(trigger.Transitions, product); TraceDependencies(" transition set of branch [{0}]: {1}", product, tset.ToDebugString()); var pre = ReplaceVariableCondition(tset, product, false); var post = ReplaceVariableCondition(tset, product, true); TraceDependencies(" branch conditions [{0} => {1}].", pre, post); // add branch trigger tlist.Add(new ProductTrigger(trigger, tset, pre, post)); } } else { // add original trigger tlist.Add(new ProductTrigger(trigger)); } // process resulting triggers foreach (var t1 in tlist) { TraceDependencies("product trigger {0} ...", t1); TraceDependencies(" conditions [{0}, {1}] ...", t1.PreCondition, t1.PostCondition); TraceDependencies(" transitions {0}", trigger.Transitions.ToDebugString()); t1.Qualify(); // check if any existing trigger is conflicting foreach (var existingtrigger in e.Triggers) { // the new trigger precondition must not intersect any existing precondition var product = Gate.ComposeAND(existingtrigger.PreCondition, t1.PreCondition); product = ReplaceTransitionVariables(existingtrigger.Transitions, product, false); if (!product.IsFalse()) { throw new CompilerException(ErrorCode.AmbigousPreCondition, "ambigous transition conditions [" + existingtrigger.PreCondition + ", " + t1.PreCondition + "]."); } } e.Triggers.Add(t1); } } catch (CompilerException ex) { AddError(ex); } }