Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 78.7% 396 / 0 / 503
Functions: -% 0 / 1 / 1
Branches: 41.1% 116 / 0 / 282

OMCompiler/Compiler/FrontEnd/StateMachineFlatten.mo
Line Branch Exec Source
1 /*
2 * This file is part of OpenModelica.
3 *
4 * Copyright (c) 1998-2026, Open Source Modelica Consortium (OSMC),
5 * c/o Linköpings universitet, Department of Computer and Information Science,
6 * SE-58183 Linköping, Sweden.
7 *
8 * All rights reserved.
9 *
10 * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF AGPL VERSION 3 LICENSE OR
11 * THIS OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.8.
12 * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES
13 * RECIPIENT'S ACCEPTANCE OF THE OSMC PUBLIC LICENSE OR THE GNU AGPL
14 * VERSION 3, ACCORDING TO RECIPIENTS CHOICE.
15 *
16 * The OpenModelica software and the OSMC (Open Source Modelica Consortium)
17 * Public License (OSMC-PL) are obtained from OSMC, either from the above
18 * address, from the URLs:
19 * http://www.openmodelica.org or
20 * https://github.com/OpenModelica/ or
21 * http://www.ida.liu.se/projects/OpenModelica,
22 * and in the OpenModelica distribution.
23 *
24 * GNU AGPL version 3 is obtained from:
25 * https://www.gnu.org/licenses/licenses.html#GPL
26 *
27 * This program is distributed WITHOUT ANY WARRANTY; without
28 * even the implied warranty of MERCHANTABILITY or FITNESS
29 * FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH
30 * IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL.
31 *
32 * See the full OSMC Public License conditions for more details.
33 *
34 */
35
36 encapsulated package StateMachineFlatten
37 " file: StateMachineFlatten.mo
38 package: StateMachineFlatten
39 description: Flattening of state machines
40
41 This module contains functions to transform an instantiated state machine to flat data-flow equations.
42 This approach is a rather direct implementation of the state machine to data-flow equations transformation
43 described in the specification. A more efficient implementation could avoid that transformation to
44 data-flow and instead keep the state machine structure in the back-end in order to generate optimized
45 code (in terms of memory requirements and minimized conditional statements).
46 "
47 public import Absyn;
48 public import DAE;
49 public import FCore;
50
51 protected import List;
52 protected import ComponentReference;
53 protected import ComponentReferenceBasics;
54 protected import Expression;
55 protected import DAEUtil;
56 protected import Util;
57 protected import DAEDump;
58 protected import Error;
59 protected import HashTableCrToExpOption;
60 protected import Flags;
61 protected import SCode;
62 protected import Types;
63 protected import ExpressionBasics;
64
65 protected
66 uniontype Transition "
67 Properties of a transition"
68 record TRANSITION
69 Integer from;
70 Integer to;
71 DAE.Exp condition;
72 Boolean immediate = true;
73 Boolean reset = true;
74 Boolean synchronize = false;
75 Integer priority = 1;
76 end TRANSITION;
77 end Transition;
78
79
80 public
81 uniontype FlatSmSemantics "
82 Structure that combines states of flat state machine in
83 canonical order with governing semantic equations."
84 record FLAT_SM_SEMANTICS
85 DAE.Ident ident;
86 array<DAE.Element> smComps "First element is the initial state";
87 // Flat State machine semantics (SMS)
88 list<Transition> t "List/Array of transition data sorted in priority";
89 list<DAE.Exp> c "Transition conditions sorted in priority";
90 list<DAE.Element> vars "SMS veriables";
91 list<DAE.Element> knowns "SMS constants/parameters";
92 list<DAE.Element> eqs "SMS equations";
93 // Activation and Reset propagation through hierarchy
94 list<DAE.Element> pvars "Propagation related variables";
95 list<DAE.Element> peqs "Propagation equations";
96 Option<DAE.ComponentRef> enclosingState "Cref to enclosing state if any"; // FIXME needed?
97 end FLAT_SM_SEMANTICS;
98 end FlatSmSemantics;
99
100 constant String SMS_PRE = "smOf" "prefix for crefs of fresh State Machine Semantics variables/knowns";
101
102 public function stateMachineToDataFlow "
103 Author: BTH
104 Transform state machines to data-flow equations
105 "
106 input FCore.Cache cache; // FIXME need to update this somewhere?
107 input FCore.Graph env; // FIXME need to update this somewhere?
108 input DAE.DAElist inDAElist;
109 output DAE.DAElist outDAElist;
110 protected
111 list<DAE.Element> elementLst, flatSmLst, otherLst, elementLst2, elementLst3;
112 Integer nOfSubstitutions;
113
114 // COMP
115 DAE.Ident ident;
116 list<DAE.Element> dAElist "a component with subelements, normally only used at top level.";
117 DAE.ElementSource source "the origin of the component/equation/algorithm";
118 Option<SCode.Comment> comment;
119 algorithm
120 1097 DAE.DAE(elementLst=elementLst) := inDAElist;
121
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1097 times.
1097 assert(listLength(elementLst) == 1, "Internal compiler error: Handling of elementLst != 1 not supported\n");
122
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1097 times.
1097 DAE.COMP(ident, dAElist, source, comment) := listHead(elementLst);
123
124
2/2
✓ Branch 1 taken 1094 times.
✓ Branch 2 taken 3 times.
1097 if not List.any(dAElist, isFlatSm) then
125 outDAElist := inDAElist;
126 1094 return;
127 end if;
128
129 3 (flatSmLst, otherLst) := List.extractOnTrue(dAElist, isFlatSm);
130 3 elementLst2 := List.fold2(flatSmLst, flatSmToDataFlow, NONE(), NONE(), {});
131
132 // HACK1 Wrap semantic state machine equations in when clauses for continuous-time state machines
133
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if Flags.getConfigBool(Flags.CT_STATE_MACHINES) then
134 ✗ elementLst2 := wrapHack(cache, elementLst2);
135 end if;
136
137 3 elementLst3 := listAppend(otherLst, elementLst2);
138 6 outDAElist := DAE.DAE({DAE.COMP(ident, elementLst3, source, comment)});
139 // print("StateMachineFlatten.stateMachineToDataFlow: outDAElist before global subs:\n" + DAEDump.dumpStr(outDAElist,FCore.getFunctionTree(cache)));
140
141 // traverse dae expressions for making substitutions activeState(x) -> x.active
142 3 (outDAElist, _, (_,nOfSubstitutions)) := DAEUtil.traverseDAE(outDAElist, FCore.getFunctionTree(cache), Expression.traverseSubexpressionsHelper, (traversingSubsActiveState, 0));
143
144
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 if Flags.getConfigBool(Flags.CT_STATE_MACHINES) then
145 // HACK2 traverse dae expressions for making substitutions previous(x) -> pre(x)
146 ✗ (outDAElist, _, (_,nOfSubstitutions)) := DAEUtil.traverseDAE(outDAElist, FCore.getFunctionTree(cache), Expression.traverseSubexpressionsHelper, (traversingSubsPreForPrevious, 0));
147 // FIXME not needed any more? HACK3 traverse dae expressions for making substitutions sample(x, _) -> x
148 // (outDAElist, _, (_,nOfSubstitutions)) := DAEUtil.traverseDAE(outDAElist, FCore.getFunctionTree(cache), Expression.traverseSubexpressionsHelper, (traversingSubsXForSampleX, 0));
149 end if;
150 //print("StateMachineFlatten.stateMachineToDataFlow: outDAElist:\n" + DAEDump.dumpStr(outDAElist,FCore.getFunctionTree(cache)));
151 end stateMachineToDataFlow;
152
153 protected function traversingSubsActiveState "
154 Author: BTH
155 Helper function to traverse subexpressions
156 Substitutes 'activeState(x)' by 'x.active' "
157 input DAE.Exp inExp;
158 input Integer inHitCount;
159 output DAE.Exp outExp;
160 output Integer outHitCount;
161 algorithm
162 (outExp,outHitCount) := match inExp
163 local
164 DAE.ComponentRef componentRef;
165 case DAE.CALL(path=Absyn.IDENT("activeState"), expLst={DAE.CREF(componentRef=componentRef)})
166 9 then (DAE.CREF(ComponentReference.crefPrependIdent(componentRef, "active", {}, DAE.T_BOOL_DEFAULT), DAE.T_BOOL_DEFAULT), inHitCount + 1);
167 else (inExp,inHitCount);
168 end match;
169 end traversingSubsActiveState;
170
171 protected function flatSmToDataFlow "
172 Author: BTH
173 Transform a flat state machine to data-flow equations
174 "
175 input DAE.Element inFlatSm "flat state machine that is to be transformed to data-flow equations";
176 input Option<DAE.ComponentRef> inEnclosingStateCrefOption "Cref of state that encloses the flat state machiene (NONE() if at top hierarchy)";
177 input Option<FlatSmSemantics> inEnclosingFlatSmSemanticsOption "The flat state machine semantics structure governing the enclosing state (NONE() if at top hierarchy)";
178 input list<DAE.Element> accElems;
179 output list<DAE.Element> outElems = accElems;
180 protected
181 DAE.Ident ident;
182 list<DAE.Element> dAElist, smCompsLst, otherLst1, transitionLst, otherLst2,
183 otherLst3, eqnLst, otherLst4, smCompsLst2;
184 DAE.Element initialStateOp, initialStateComp;
185 DAE.ComponentRef crefInitialState;
186
187 FlatSmSemantics flatSmSemanticsBasics, flatSmSemanticsWithPropagation, flatSmSemantics;
188 list<DAE.Element> vars "SMS veriables";
189 list<DAE.Element> knowns "SMS constants/parameters";
190 list<DAE.Element> eqs "SMS equations";
191 list<DAE.Element> pvars "Propagation related variables";
192 list<DAE.Element> peqs "Propagation equations";
193 // Option<DAE.ComponentRef> enclosingState "Cref to enclosing state if any"; // FIXME needed?
194 algorithm
195
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 DAE.FLAT_SM(ident=ident, dAElist=dAElist) := inFlatSm;
196
197 // break Elements into different groups
198 7 (smCompsLst, otherLst1) := List.extractOnTrue(dAElist, isSMComp);
199 7 (transitionLst, otherLst2) := List.extractOnTrue(otherLst1, isTransition);
200
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 7 times.
7 ({initialStateOp}, otherLst3) := List.extractOnTrue(otherLst2, isInitialState);
201 7 (eqnLst, otherLst4) := List.extractOnTrue(otherLst3, isEquation);
202
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 assert(listEmpty(otherLst4), "Internal compiler error. Unexpected elements in flat state machine.");
203
204
8/16
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 7 times.
✓ Branch 9 taken 7 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 7 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 7 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 7 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 7 times.
7 DAE.NORETCALL(exp=DAE.CALL(path=Absyn.IDENT("initialState"), expLst={DAE.CREF(componentRef=crefInitialState)})) := initialStateOp;
205
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 7 times.
7 ({initialStateComp}, smCompsLst2) := List.extract1OnTrue(smCompsLst, sMCompEqualsRef, crefInitialState);
206
207 // Create basic semantic equations (MLS 17.3.4 Semantics Summary)
208 7 flatSmSemanticsBasics := basicFlatSmSemantics(ident, initialStateComp::smCompsLst2, transitionLst);
209
210 // Add activation and reset propagation related equations
211 7 flatSmSemanticsWithPropagation := addPropagationEquations(flatSmSemanticsBasics, inEnclosingStateCrefOption, inEnclosingFlatSmSemanticsOption);
212
213 // Elaborate on ticksInState() and timeInState() operators (MLS 17.1 Transitions)
214 7 flatSmSemantics := elabXInStateOps(flatSmSemanticsWithPropagation, inEnclosingStateCrefOption);
215
216
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
7 if Flags.getConfigBool(Flags.CT_STATE_MACHINES) then
217 // Allow ticksInState() in state components (BTH not needed really needed for CT, delete this stuff?)
218 ✗ smCompsLst := List.map(smCompsLst, elabXInStateOps_CT);
219 end if;
220
221 // Extract semantic equations for flat state machine and add the elements to the DAE list
222 7 FLAT_SM_SEMANTICS(vars=vars, knowns=knowns, eqs=eqs, pvars=pvars, peqs=peqs) := flatSmSemantics;
223 7 outElems := List.flatten({outElems, eqnLst, vars, knowns, eqs, pvars, peqs});
224
225 // Extract DAE.Elements from state components (and recurse into potential FLAT_SMs in the state component)
226 7 outElems := List.fold1(smCompsLst, smCompToDataFlow, flatSmSemantics, outElems);
227 end flatSmToDataFlow;
228
229 protected function elabXInStateOps_CT "
230 Author: BTH
231 For continuous-time state machines, support ticksInState() operators in state components
232 "
233 input DAE.Element inSmComp;
234 output DAE.Element outSmComp;
235 protected
236 Integer nOfHits = 0;
237 DAE.ComponentRef componentRef;
238 list<DAE.Element> dAElist1, dAElist2;
239 AvlTreePathFunction.Tree emptyTree;
240 algorithm
241 ✗ DAE.SM_COMP(componentRef, dAElist1) := inSmComp;
242 emptyTree := AvlTreePathFunction.Tree.EMPTY();
243 ✗ (DAE.DAE(dAElist2), _, (_,(_, nOfHits))) := DAEUtil.traverseDAE(DAE.DAE(dAElist1), emptyTree, Expression.traverseSubexpressionsHelper, (traversingSubsTicksInState, (componentRef, 0)));
244 ✗ outSmComp := DAE.SM_COMP(componentRef, dAElist2);
245 end elabXInStateOps_CT;
246
247 protected function traversingSubsTicksInState "
248 Author: BTH
249 Helper function to elabXInStateOps_CT for traversing subexpressions
250 Substitutes ticksInState() by enclosingStateComponent.$ticksInState '
251 "
252 input DAE.Exp inExp;
253 input tuple<DAE.ComponentRef, Integer> inCref_HitCount "tuple of cref of enclosing state component and substitution hit counter";
254 output DAE.Exp outExp;
255 output tuple<DAE.ComponentRef, Integer> outCref_HitCount;
256 protected
257 DAE.ComponentRef cref;
258 Integer hitCount;
259 algorithm
260 ✗ (cref, hitCount) := inCref_HitCount;
261 (outExp,outCref_HitCount) := match inExp
262 local
263 DAE.Type ty;
264 DAE.ComponentRef crefTicksInState;
265 case DAE.CALL(path=Absyn.IDENT("ticksInState"), expLst={}, attr=DAE.CALL_ATTR(ty=ty))
266 algorithm
267 ✗ crefTicksInState := ComponentReference.joinCrefs(cref, DAE.CREF_IDENT("$ticksInState", ty, {}));
268 ✗ then (DAE.CREF(crefTicksInState, ty), (cref, hitCount + 1));
269 else (inExp,inCref_HitCount);
270 end match;
271 end traversingSubsTicksInState;
272
273 protected function elabXInStateOps "
274 Author: BTH
275 Transform ticksInState() and timeInState() operators to data-flow equations
276 "
277 input FlatSmSemantics inFlatSmSemantics;
278 input Option<DAE.ComponentRef> inEnclosingStateCrefOption "Cref of state that encloses the flat state machiene (NONE() if at top hierarchy)";
279 output FlatSmSemantics outFlatSmSemantics;
280 protected
281 Integer i;
282 Boolean found;
283 DAE.Exp c2, c3, c4, substTickExp, substTimeExp;
284 DAE.ComponentRef stateRef;
285 Transition t2;
286 list<Transition> tElab = {} "Elaborated transitions";
287 list<DAE.Exp> cElab = {} "Elaborated conditions";
288 list<DAE.Element> smeqsElab = {} "Elaborated smeqs";
289 // FLAT_SM_SEMANTICS
290 DAE.Ident ident;
291 array<DAE.Element> smComps "First element is the initial state";
292 list<Transition> t "List/Array of transition data sorted in priority";
293 list<DAE.Exp> c "Transition conditions sorted in priority";
294 list<DAE.Element> smvars "SMS veriables";
295 list<DAE.Element> smknowns "SMS constants/parameters";
296 list<DAE.Element> smeqs "SMS equations";
297 list<DAE.Element> pvars = {} "Propagation related variables";
298 list<DAE.Element> peqs = {} "Propagation equations";
299 Option<DAE.ComponentRef> enclosingStateOption "Cref to enclosing state if any"; // FIXME needed?
300 // TRANSITION
301 Integer from;
302 Integer to;
303 DAE.Exp condition;
304 Boolean immediate;
305 Boolean reset;
306 Boolean synchronize;
307 Integer priority;
308 algorithm
309 7 FLAT_SM_SEMANTICS(ident, smComps, t, c, smvars, smknowns, smeqs, pvars, peqs, enclosingStateOption) := inFlatSmSemantics;
310
311 // We have some redundancy here (t[:].condition == c[:]) and thus need to update both
312 i := 0;
313
2/2
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 7 times.
21 for tc in List.zip(t,c) loop
314 14 i := i + 1;
315 14 (t2, c2) := tc;
316 14 TRANSITION(from, to, condition, immediate, reset, synchronize, priority) := t2;
317
318 // Need to access decorations attached to 'from' state
319
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
14 DAE.SM_COMP(componentRef=stateRef) := arrayGet(smComps, from);
320
321 // == Search whether condition contains a subexpression 'ticksInState()', if so, substitute them by 'smComps[from].$ticksInState' ==
322 14 substTickExp := DAE.CREF(qCref("$ticksInState", DAE.T_INTEGER_DEFAULT, {}, stateRef), DAE.T_INTEGER_DEFAULT);
323 14 (c3, (_, _, found)) := Expression.traverseExpTopDown(c2, traversingSubsXInState, ("ticksInState", substTickExp, false));
324
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
14 if found and isSome(inEnclosingStateCrefOption) then
325 // MLS 3.3 17.1: "can only be used in transition conditions of state machines not present in states of hierarchical state machines" violated
326 ✗ Error.addCompilerError("Found 'ticksInState()' within a state of an hierarchical state machine.");
327 ✗ fail();
328 end if;
329 // if a transition was updated we also need to update the semantic equation containing that transition's logic
330
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 smeqsElab := if found then List.map5(smeqs, smeqsSubsXInState, arrayGet(smComps, 1), i, listLength(t), substTickExp, "ticksInState") else smeqs;
331 smeqs := smeqsElab; // use updated smeqs
332
333 // == Search whether condition contains a subexpression 'timeInState()', if so, substitute them by 'smComps[from].$timeInState' ==
334 14 substTimeExp := DAE.CREF(qCref("$timeInState", DAE.T_REAL_DEFAULT, {}, stateRef), DAE.T_REAL_DEFAULT);
335 14 (c4, (_, _, found)) := Expression.traverseExpTopDown(c2, traversingSubsXInState, ("timeInState", substTimeExp, false));
336
4/6
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 time.
14 if found and isSome(inEnclosingStateCrefOption) then
337 // MLS 3.3 17.1: "can only be used in transition conditions of state machines not present in states of hierarchical state machines" violated
338 ✗ Error.addCompilerError("Found 'timeInState()' within a state of an hierarchical state machine.");
339 ✗ fail();
340 end if;
341 // if a transition was updated we also need to update the semantic equation containing that transition's logic
342
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 13 times.
15 smeqsElab := if found then List.map5(smeqs, smeqsSubsXInState, arrayGet(smComps, 1), i, listLength(t), substTimeExp, "timeInState") else smeqs;
343 smeqs := smeqsElab; // use updated smeqs
344
345
3/6
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
42 tElab := TRANSITION(from, to, c4, immediate, reset, synchronize, priority) :: tElab;
346 cElab := c4 :: cElab;
347 end for;
348
349 7 outFlatSmSemantics := FLAT_SM_SEMANTICS(ident, smComps, listReverse(tElab), listReverse(cElab), smvars, smknowns, smeqsElab, pvars, peqs, enclosingStateOption);
350 end elabXInStateOps;
351
352 protected function smeqsSubsXInState "
353 Author: BTH
354 Helper function to elabXInStateOps.
355 Replace 'xInState()' in RHS of semantic equations by 'substExp', but only within the transition
356 condition specified by the remaining function arguments.
357 "
358 input DAE.Element inSmeqs "SMS equation";
359 input DAE.Element initialStateComp "Initial state component of governing flat state machine";
360 input Integer i "Index of transition";
361 input Integer nTransitions;
362 input DAE.Exp substExp;
363 input String xInState "Name of function that is to be replaced, e.g., 'timeInState', or 'tickInState'";
364 output DAE.Element outSmeqs "SMS equation";
365 protected
366 DAE.ComponentRef preRef, cref, lhsRef, crefInitialState;
367 DAE.Type tArrayBool;
368 DAE.ElementSource elemSource;
369 DAE.Exp lhsExp, rhsExp, rhsExp2;
370 DAE.Type ty;
371 algorithm
372 // Cref to initial state of governing flat state machine
373
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 DAE.SM_COMP(componentRef=crefInitialState) := initialStateComp;
374 18 preRef := ComponentReference.crefPrefixString(SMS_PRE, crefInitialState);
375 36 tArrayBool := DAE.T_ARRAY(DAE.T_BOOL_DEFAULT,{DAE.DIM_INTEGER(nTransitions)});
376 36 cref := qCref("cImmediate", tArrayBool, {DAE.INDEX(DAE.ICONST(i))}, preRef);
377
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 DAE.EQUATION(lhsExp, rhsExp, elemSource) := inSmeqs;
378
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 DAE.CREF(lhsRef, ty) := lhsExp;
379 // print("StateMachineFlatten.smeqsSubsXInState: cref: " + ComponentReferenceBasics.printComponentRefStr(cref) + "\n");
380 // print("StateMachineFlatten.smeqsSubsXInState: lhsRef: " + ComponentReferenceBasics.printComponentRefStr(lhsRef) + "\n");
381
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 17 times.
18 if ComponentReferenceBasics.crefEqual(cref, lhsRef) then
382 // print("StateMachineFlatten.smeqsSubsXInState: rhsExp: " + ExpressionBasics.printExpStr(rhsExp) + "\n");
383 1 (rhsExp2, _) := Expression.traverseExpTopDown(rhsExp, traversingSubsXInState, (xInState, substExp, false));
384 // print("StateMachineFlatten.smeqsSubsXInState: rhsExp2: " + ExpressionBasics.printExpStr(rhsExp2) + "\n");
385 else
386 rhsExp2 := rhsExp;
387 end if;
388 18 outSmeqs := DAE.EQUATION(lhsExp, rhsExp2, elemSource);
389 end smeqsSubsXInState;
390
391 protected function traversingSubsXInState "
392 Author: BTH
393 Helper function to elabXInStateOps and smeqsSubsXInState.
394 Replace 'XInState()' operators (first element of inXSubstHit) by expression given in second element of inXSubstHit tuple.
395 "
396 input DAE.Exp inExp;
397 input tuple<String, DAE.Exp, Boolean> inXSubstHit;
398 output DAE.Exp outExp;
399 output Boolean cont = true;
400 output tuple<String, DAE.Exp, Boolean> outXSubstHit;
401 algorithm
402 (outExp, outXSubstHit) := match (inExp, inXSubstHit)
403 local
404 DAE.Exp subsExp;
405 String xInState, name;
406 case (DAE.CALL(path=Absyn.IDENT(name)), (xInState, subsExp, _)) guard name == xInState
407 2 then (subsExp, (xInState, subsExp, true));
408 else (inExp, inXSubstHit);
409 end match;
410 end traversingSubsXInState;
411
412 protected function smCompToDataFlow "
413 Author: BTH
414 Transform state machine component to data-flow equations
415 "
416 input DAE.Element inSMComp;
417 input FlatSmSemantics inEnclosingFlatSmSemantics "The flat state machine semantics structure governing the state component";
418 input list<DAE.Element> accElems;
419 output list<DAE.Element> outElems = accElems;
420 protected
421 list<DAE.Element> varLst1, varLst2, assignedVarLst, stateVarLst, otherLst1, equationLst1, equationLst2, otherLst2, flatSmLst, otherLst3;
422 DAE.ComponentRef componentRef;
423 list<DAE.ComponentRef> stateVarCrefs;
424 list<Option<DAE.Exp>> startValuesOpt;
425 list<tuple<DAE.ComponentRef, Option<DAE.Exp>>> varCrefStartVal;
426 list<DAE.Element> dAElist "a component with subelements";
427 HashTableCrToExpOption.HashTable crToExpOpt "Table that maps the cref of a variable to its start value";
428 algorithm
429
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 DAE.SM_COMP(componentRef=componentRef, dAElist=dAElist) := inSMComp;
430
431 17 (varLst1, otherLst1) := List.extractOnTrue(dAElist, isVar);
432
433 // FIXME More general handling requires supporting all valid elements, e.g., also IF_EQUATION (also in downstream functions), but not sure what can be possibly encountered here
434 17 (equationLst1, otherLst2) := List.extractOnTrue(otherLst1, isEquationOrWhenEquation);
435
436 // FIXME More general handling might require assignment matching algorithm. Current restriction relies on that any assigned variable appears at the LHS of an assignment equation.
437 // FIXME Maybe better to just filter out variables declared as "inputs" and assume that the rest are assigned variables?
438 // Retain all variables for which there exits an assignment equation
439 17 assignedVarLst := List.filterOnTrue(varLst1, function List.exist1(inList=equationLst1, inFindFunc=isVarAtLHS));
440 // Retain all variables which have "previous(x)" applied
441 17 stateVarLst := List.filterOnTrue(varLst1, function List.exist1(inList=equationLst1, inFindFunc=isPreviousAppliedToVar));
442 //print("StateMachineFlatten.smCompToDataFlow: stateVarLst:\n" + DAEDump.dumpElementsStr(stateVarLst) +"\n");
443
444 17 stateVarCrefs := List.map(stateVarLst, DAEUtil.varCref);
445 17 startValuesOpt := List.map(stateVarLst, getStartAttrOption);
446 17 varCrefStartVal := List.zip(stateVarCrefs, startValuesOpt);
447 17 crToExpOpt := HashTableCrToExpOption.emptyHashTableSized(listLength(varCrefStartVal) + 1);
448 // create table that maps the cref of a variable to its start value
449 17 crToExpOpt := List.fold(varCrefStartVal, BaseHashTable.add, crToExpOpt);
450 //print("StateMachineFlatten.smCompToDataFlow: crToExpOpt:\n"); BaseHashTable.dumpHashTable(crToExpOpt);
451
452 // 1. Make equations conditional so that they are only active if enclosing state is active
453 // 2. Add reset equations for discrete-time states declared in the component
454 17 (equationLst2, varLst2) := List.fold3(equationLst1, addStateActivationAndReset, inSMComp, inEnclosingFlatSmSemantics, crToExpOpt, ({},{}));
455
456 17 (flatSmLst, otherLst3) := List.extractOnTrue(otherLst2, isFlatSm);
457
458 // append non FLAT_SM elements to accumulator
459 34 outElems := List.flatten({outElems, varLst1, varLst2, equationLst2, otherLst3});
460
461 // recurse into FLAT_SM elements (if any)
462 17 outElems := List.fold2(flatSmLst, flatSmToDataFlow, SOME(componentRef), SOME(inEnclosingFlatSmSemantics), outElems);
463 end smCompToDataFlow;
464
465
466 protected function addStateActivationAndReset "
467 Author: BTH
468 The real work is done in helper function addStateActivationAndReset1.
469 This top-level function just handles the recursive descent if inEqn is a DAE.WHEN_EQUATION().
470 "
471 input DAE.Element inEqn "Expects DAE.EQUATION() or DAE.WHEN_EQUATION()";
472 input DAE.Element inEnclosingSMComp "The state component enclosing the equation";
473 input FlatSmSemantics inEnclosingFlatSmSemantics "The flat state machine semantics structure governing the state component";
474 input HashTableCrToExpOption.HashTable crToExpOpt "Table mapping variable declaration in the enclosing state to start values";
475 input tuple<list<DAE.Element>,list<DAE.Element>> accEqnsVars "Tuple for accumulating equations and variable definitions";
476 output tuple<list<DAE.Element>,list<DAE.Element>> outEqnsVars;
477 protected
478 list<DAE.Element> equations1;
479 list<DAE.Element> vars1;
480 // WHEN_EQUATION
481 DAE.Exp condition;
482 list<DAE.Element> equations;
483 DAE.ElementSource source;
484 algorithm
485 outEqnsVars := match inEqn
486 12 case DAE.EQUATION() then addStateActivationAndReset1(inEqn, inEnclosingSMComp, inEnclosingFlatSmSemantics, crToExpOpt, accEqnsVars);
487 case DAE.WHEN_EQUATION(condition,equations,NONE(),source)
488 algorithm
489 2 (equations1,vars1) := List.fold3(equations, addStateActivationAndReset, inEnclosingSMComp, inEnclosingFlatSmSemantics, crToExpOpt, ({},{}));
490 4 then (DAE.WHEN_EQUATION(condition,equations1,NONE(),source)::Util.tuple21(accEqnsVars), listAppend(vars1,Util.tuple22(accEqnsVars)));
491 case DAE.WHEN_EQUATION(elsewhen_=SOME(_))
492 algorithm
493 ✗ Error.addCompilerError("Encountered elsewhen part in a when clause of a clocked state machine.\n");
494 ✗ then fail();
495 else
496 algorithm
497 ✗ Error.addCompilerError("Internal compiler error: StateMachineFlatten.addStateActivationAndReset(..) called with unexpected argument.\n");
498 ✗ then fail();
499 end match;
500 end addStateActivationAndReset;
501
502 protected function addStateActivationAndReset1 "
503 Author: BTH
504 The function has following purpose:
505 1. Make equations conditional so that they are only active if enclosing state is active
506 2. Add reset equations for discrete-time states declared in the component
507
508 FIXME 2017-02-17: There is problem with the approach taken in this function of transforming s.th. similar to
509 Real x(start=1.1);
510 x = previous(x) + 1
511 to something like
512 x = if stateActive then x_previous + 1 else x_previous;
513 x_previous = if active and (activeReset or activeResetStates[1]) then 1.1 else previous(x);
514 While this gives the correct reset semantics for x, one gets a wrong result for previous(x) at the reset instant:
515 'x_previous' is set to the correct result value, but during the reset instant in general there will be 'previous(x) != x_previous'!
516 The transformation below replaces all occurances of 'previous(x)' within the state's equations to 'x_previous', so that the
517 state machine will show the correct behavior. However, if 'x' is accessed with 'previous(x)' from outside the state, it will hold
518 the wrong value. Also, when plotting 'previous(x)' will show a wrong value during reset.
519 Hence, one needs another mechanism to reset 'previous(x)' correctly, but I don't see how this can be easily done by an equation
520 transformation to standard clocked synchronous equations in the front-end. Probably one could add a dedicated internal marker/operator
521 which is then handled specially in the back-end.
522 "
523 input DAE.Element inEqn;
524 input DAE.Element inEnclosingSMComp "The state component enclosing the equation";
525 input FlatSmSemantics inEnclosingFlatSmSemantics "The flat state machine semantics structure governing the state component";
526 input HashTableCrToExpOption.HashTable crToExpOpt "Table mapping variable declaration in the enclosing state to start values";
527 input tuple<list<DAE.Element>,list<DAE.Element>> accEqnsVars "Tuple for accumulating equations and variable definitions";
528 output tuple<list<DAE.Element>,list<DAE.Element>> outEqnsVars;
529 protected
530 list<DAE.ComponentRef> stateVarCrefs;
531
532 DAE.ComponentRef crefLHS, enclosingStateRef, cref2;
533 Boolean found;
534 DAE.Type tyLHS;
535 DAE.Element eqn, eqn1, eqn2, var2, varDecl;
536 DAE.CallAttributes attr;
537 list<DAE.Element> dAElist;
538 Boolean isOuterVar;
539 // EQUATION
540 DAE.Exp exp;
541 DAE.Exp scalar, scalarNew;
542 DAE.ElementSource source;
543 algorithm
544
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 DAE.EQUATION(exp, scalar, source) := inEqn;
545
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 DAE.SM_COMP(componentRef=enclosingStateRef, dAElist=dAElist) := inEnclosingSMComp;
546 12 stateVarCrefs := BaseHashTable.hashTableKeyList(crToExpOpt);
547
548 try
549 // Handle case with LHS component reference
550
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 DAE.CREF(componentRef=crefLHS, ty=tyLHS) := exp;
551 // For all {x1,x2,..}, search whether the RHS of an equation 'x=exp' contains a subexpression 'previous(x)', if so, substitute them by 'x_previous'
552 12 (scalarNew, (_, found)) := Expression.traverseExpTopDown(scalar, traversingSubsPreviousCrefs, (stateVarCrefs, false));
553 12 eqn := DAE.EQUATION(exp, scalarNew, source);
554
555 // If it is an assigning state equation, transform equation 'a.x = e' to 'a.x = if a.active then e else a.x_previous'
556
2/2
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 6 times.
12 if List.any(stateVarCrefs, function ComponentReferenceBasics.crefEqual(inComponentRef1=crefLHS)) then
557 // Transform equation 'a.x = e' to 'a.x = if a.active then e else a.x_previous'
558 6 eqn1 := wrapInStateActivationConditional(eqn, enclosingStateRef, true);
559
560 // Create fresh variable 'a.x_previous'
561 6 var2 := createVarWithDefaults(ComponentReference.appendStringLastIdent("_previous", crefLHS), DAE.DISCRETE(), tyLHS, {});
562 // Create fresh reset equation: 'a.x_previous = if a.active and (smOf.a.activeReset or smOf.fsm_of_a.activeResetStates[i] then x_start else previous(a.x)'
563 6 eqn2 := createResetEquation(crefLHS, tyLHS, enclosingStateRef, inEnclosingFlatSmSemantics, crToExpOpt);
564
565 18 outEqnsVars := (eqn1 :: eqn2 :: Util.tuple21(accEqnsVars), var2 :: Util.tuple22(accEqnsVars));
566 else
567 12 outEqnsVars := (wrapInStateActivationConditional(eqn, enclosingStateRef, false)::Util.tuple21(accEqnsVars), Util.tuple22(accEqnsVars));
568 end if;
569
570 else
571 try
572 // Handle case with LHS derivative (der(a.x))
573 ✗ if Flags.getConfigBool(Flags.CT_STATE_MACHINES) then
574 // BTH CT_STATE_MACHINES is experimental code
575
576 ✗ DAE.CALL(Absyn.IDENT("der"), {DAE.CREF(componentRef=crefLHS, ty=tyLHS)}, attr) := exp;
577
578 // Find variable declaration that corresponds to crefLHS
579 try
580 ✗ varDecl := List.find1(dAElist, isCrefInVar, crefLHS);
581 else
582 ✗ Error.addCompilerError("Couldn't find variable declaration matching to cref " + ComponentReference.crefStr(crefLHS) + "\n");
583 ✗ fail();
584 end try;
585
586 ✗ isOuterVar := DAEUtil.isOuterVar(varDecl);
587
588 ✗ if isOuterVar then
589 // Create fresh variable 'a.x_der$'
590 ✗ cref2 := ComponentReference.appendStringLastIdent("_der$", crefLHS);
591 ✗ var2 := createVarWithDefaults(cref2, DAE.VARIABLE(), tyLHS, {});
592
593 // Change equation 'der(a.x) = e' to 'a.x_der$ = e'
594 ✗ eqn1 := DAE.EQUATION(DAE.CREF(cref2, tyLHS), scalar, source);
595
596 ✗ outEqnsVars := (eqn1 :: Util.tuple21(accEqnsVars), var2 :: Util.tuple22(accEqnsVars));
597 else
598 // Transform equation 'der(a.x) = e' to 'der(a.x) = if a.active then e else 0'
599 ✗ eqn1 := wrapInStateActivationConditionalCT(inEqn, enclosingStateRef);
600
601 // Create fresh reinit equation: 'when a.active and (smOf.a.activeReset or smOf.fsm_of_a.activeResetStates[i]) then reinit(a.x, a.x_start) end when'
602 ✗ eqn2 := createResetEquationCT(crefLHS, tyLHS, enclosingStateRef, inEnclosingFlatSmSemantics, crToExpOpt);
603
604 ✗ outEqnsVars := (eqn1 :: eqn2 :: Util.tuple21(accEqnsVars), Util.tuple22(accEqnsVars));
605 end if;
606 else
607 ✗ fail();
608 end if;
609 else
610 ✗ if Flags.getConfigBool(Flags.CT_STATE_MACHINES) then
611 ✗ Error.addCompilerError("Currently, only equations in state machines with a LHS component reference, e.g., x=.., or its derivative, e.g., der(x)=.., are supported");
612 else
613 ✗ Error.addCompilerError("Currently, only equations in state machines with a LHS component reference, e.g., x=.., are supported");
614 end if;
615 ✗ fail();
616 end try;
617 end try;
618
619 end addStateActivationAndReset1;
620
621
622 protected function isVarAtLHS "
623 Author: BTH
624 Return true if variable appears as LHS assignment in a scalar equation or in the body of a when equation.
625 "
626 input DAE.Element eqn "Expects DAE.EQUATION() or DAE.WHEN_EQUATION()";
627 input DAE.Element var "Expects DAE.VAR())";
628 output Boolean res;
629 protected
630 DAE.ComponentRef cref, crefLHS;
631 DAE.Type tyLHS;
632 // EQUATION
633 DAE.Exp exp;
634 // WHEN_EQUATION
635 list<DAE.Element> equations;
636 Option<DAE.Element> elsewhen_;
637 algorithm
638 res := match eqn
639 case DAE.EQUATION(exp, _, _)
640 algorithm
641 14 cref := DAEUtil.varCref(var);
642 try
643 // Handle case with LHS component reference
644
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 DAE.CREF(componentRef=crefLHS, ty=tyLHS) := exp;
645 14 res := ComponentReferenceBasics.crefEqual(crefLHS, cref);
646 else
647 res := false;
648 end try;
649 then res;
650 case DAE.WHEN_EQUATION(equations=equations,elsewhen_=NONE())
651 2 then List.exist1(equations, isVarAtLHS, var);
652 case DAE.WHEN_EQUATION(elsewhen_=SOME(_))
653 algorithm
654 ✗ Error.addCompilerError("Encountered elsewhen part in a when clause of a clocked state machine.\n");
655 ✗ then fail();
656 else
657 algorithm
658 ✗ Error.addCompilerError("Internal compiler error: StateMachineFlatten.isVarAtLHS(..) called with unexpected argument.\n");
659 ✗ then fail();
660 end match;
661 end isVarAtLHS;
662
663 protected function isPreviousAppliedToVar "
664 Author: BTH
665 Return true if variable x appears as previous(x) in the RHS of a scalar equation or in the body of a when equation.
666 "
667 input DAE.Element eqn "Expects DAE.EQUATION() or DAE.WHEN_EQUATION()";
668 input DAE.Element var "Expects DAE.VAR())";
669 output Boolean found = false;
670 protected
671 DAE.ComponentRef cref;
672 // EQUATION
673 DAE.Exp scalar;
674 // WHEN_EQUATION
675 list<DAE.Element> equations;
676 Option<DAE.Element> elsewhen_;
677 algorithm
678 found := match eqn
679 case DAE.EQUATION(_, scalar, _)
680 algorithm
681 15 cref := DAEUtil.varCref(var);
682 15 (_, (_, found)) := Expression.traverseExpTopDown(scalar, traversingFindPreviousCref, (cref, false));
683 then found;
684 case DAE.WHEN_EQUATION(equations=equations,elsewhen_=NONE())
685 2 then List.exist1(equations, isPreviousAppliedToVar, var);
686 case DAE.WHEN_EQUATION(elsewhen_=SOME(_))
687 algorithm
688 ✗ Error.addCompilerError("Encountered elsewhen part in a when clause of a clocked state machine.\n");
689 ✗ then fail();
690 else
691 algorithm
692 ✗ Error.addCompilerError("Internal compiler error: StateMachineFlatten.isPreviousAppliedToVar(..) called with unexpected argument.\n");
693 ✗ then fail();
694 end match;
695 end isPreviousAppliedToVar;
696
697
698 protected function traversingFindPreviousCref "
699 Author: BTH
700 Given a cref 'x', find if the expression has subexpressions 'previous(x)' and indicate success.
701 "
702 input DAE.Exp inExp;
703 input tuple<DAE.ComponentRef, Boolean> inCrefHit;
704 output DAE.Exp outExp;
705 output Boolean cont = true;
706 output tuple<DAE.ComponentRef, Boolean> outCrefHit;
707 algorithm
708 (outExp, outCrefHit) := match (inExp, inCrefHit)
709 local
710 DAE.ComponentRef cr, cref;
711 case (DAE.CALL(Absyn.IDENT("previous"), {DAE.CREF(cr, _)}, _), (cref, _)) guard ComponentReferenceBasics.crefEqual(cr, cref)
712 6 then (inExp, (cref, true));
713 else (inExp, inCrefHit);
714 end match;
715
716 end traversingFindPreviousCref;
717
718
719 protected function createResetEquationCT "
720 Author: BTH
721 Given LHS 'a.x' and its start value 'x_start', as well as its enclosing state component 'a' with index 'i' in its governing FLAT_SM 'fsm_of_a' return eqn
722 'when a.active and (smOf.a.activeReset or smOf.fsm_of_a.activeResetStates[i]) then reinit(a.x, a.x_start) end when'
723 "
724 input DAE.ComponentRef inLHSCref "LHS cref";
725 input DAE.Type inLHSty "LHS type";
726 input DAE.ComponentRef inStateCref "Component reference of state enclosing the equation";
727 input FlatSmSemantics inEnclosingFlatSmSemantics "The flat state machine semantics structure governing the state component";
728 input HashTableCrToExpOption.HashTable crToExpOpt "Table mapping variable declaration in the enclosing state to start values";
729 output DAE.Element outEqn;
730 protected
731 DAE.Exp activeExp, activeResetExp, activeResetStatesExp, orExp, andExp, startValueExp;
732 DAE.Element reinitElem;
733 Option<DAE.Exp> startValueOpt;
734 DAE.ComponentRef initStateRef, preRef;
735 Integer i, nStates;
736 array<DAE.Element> enclosingFlatSMComps;
737 DAE.Type tArrayBool;
738 algorithm
739 ✗ FLAT_SM_SEMANTICS(smComps=enclosingFlatSMComps) := inEnclosingFlatSmSemantics;
740 ✗ DAE.SM_COMP(componentRef=initStateRef) := arrayGet(enclosingFlatSMComps, 1); // initial state
741
742 // prefix for state machine semantics equations of the governing flat state machine
743 ✗ preRef := ComponentReference.crefPrefixString(SMS_PRE, initStateRef);
744
745 // position of enclosing state in the array of states of its governing flat state machine
746 ✗ i := List.position1OnTrue(arrayList(enclosingFlatSMComps), sMCompEqualsRef, inStateCref);
747
748 // smOf.a.activeReset
749 ✗ activeResetExp := DAE.CREF(qCref("activeReset", DAE.T_BOOL_DEFAULT, {}, preRef), DAE.T_BOOL_DEFAULT);
750
751 nStates := arrayLength(enclosingFlatSMComps);
752 ✗ tArrayBool := DAE.T_ARRAY(DAE.T_BOOL_DEFAULT,{DAE.DIM_INTEGER(nStates)});
753 // smOf.fsm_of_a.activeResetStates[i]
754 ✗ activeResetStatesExp := DAE.CREF(qCref("activeResetStates", tArrayBool, {DAE.INDEX(DAE.ICONST(i))}, preRef), DAE.T_BOOL_DEFAULT);
755
756 // smOf.fsm_of_a.activeReset or smOf.fsm_of_a.activeResetStates[i]
757 ✗ orExp := DAE.LBINARY(activeResetExp, DAE.OR(DAE.T_BOOL_DEFAULT), activeResetStatesExp);
758
759 // a.active (reference the active indicator for this state)
760 ✗ activeExp := DAE.CREF(qCref("active", DAE.T_BOOL_DEFAULT, {}, inStateCref), DAE.T_BOOL_DEFAULT);
761
762 // a.active and (smOf.fsm_of_a.activeReset or smOf.fsm_of_a.activeResetStates[i])
763 ✗ andExp := DAE.LBINARY(activeExp, DAE.AND(DAE.T_BOOL_DEFAULT), orExp);
764 //callAttributes := DAE.CALL_ATTR(inLHSty,false,true,false,false,DAE.NO_INLINE(),DAE.NO_TAIL(),DAE.NoReturn.RETURNS);
765 // pre(activeExp)
766 //preExp := DAE.CALL(Absyn.IDENT("pre"), {activeExp}, callAttributes);
767 //andExp := DAE.LBINARY(activeExp, DAE.AND(DAE.T_BOOL_DEFAULT), DAE.LUNARY(DAE.NOT(DAE.T_BOOL_DEFAULT), preExp));
768
769 ✗ startValueOpt := BaseHashTable.get(inLHSCref, crToExpOpt);
770 ✗ if isSome(startValueOpt) then
771 ✗ startValueExp := Util.getOption(startValueOpt);
772 else
773 // No start value given for the variable, default to "0"
774 startValueExp := match inLHSty
775 case DAE.T_INTEGER()
776 algorithm
777 ✗ Error.addCompilerWarning("Variable "+ComponentReference.crefStr(inLHSCref)+" lacks start value. Defaulting to start=0.\n");
778 then DAE.ICONST(0);
779 case DAE.T_REAL()
780 algorithm
781 ✗ Error.addCompilerWarning("Variable "+ComponentReference.crefStr(inLHSCref)+" lacks start value. Defaulting to start=0.\n");
782 then DAE.RCONST(0);
783 case DAE.T_BOOL()
784 algorithm
785 ✗ Error.addCompilerWarning("Variable "+ComponentReference.crefStr(inLHSCref)+" lacks start value. Defaulting to start=false.\n");
786 then DAE.BCONST(false);
787 case DAE.T_STRING()
788 algorithm
789 ✗ Error.addCompilerWarning("Variable "+ComponentReference.crefStr(inLHSCref)+" lacks start value. Defaulting to start=\"\".\n");
790 then DAE.SCONST("");
791 case DAE.T_ENUMERATION()
792 algorithm
793 ✗ Error.addCompilerWarning("Variable "+ComponentReference.crefStr(inLHSCref)+" lacks start value. Defaulting to start=\"\".\n");
794 ✗ then Types.getNthEnumLiteral(inLHSty, 1);
795 else
796 algorithm
797 ✗ Error.addCompilerError("Variable "+ComponentReference.crefStr(inLHSCref)+" lacks start value.\n");
798 ✗ then fail();
799 end match;
800 end if;
801
802 // reinit(a.x, a.x_start)
803 ✗ reinitElem := DAE.REINIT(inLHSCref, startValueExp, DAE.emptyElementSource);
804
805 // when a.active and (smOf.a.activeReset or smOf.fsm_of_a.activeResetStates[i]) then reinit(a.x, a.x_start) end when;
806 ✗ outEqn := DAE.WHEN_EQUATION(andExp, {reinitElem}, NONE(), DAE.emptyElementSource);
807
808 end createResetEquationCT;
809
810 protected function isCrefInVar "
811 Author: BTH
812 Return true if element is a VAR containing the cref, otherwise false"
813 input DAE.Element inElement;
814 input DAE.ComponentRef inCref;
815 output Boolean result;
816 algorithm
817 result := match inElement
818 local
819 DAE.ComponentRef cref;
820 case DAE.VAR(componentRef=cref) guard ComponentReferenceBasics.crefEqual(cref, inCref) then true;
821 else then false;
822 end match;
823 end isCrefInVar;
824
825 protected function createResetEquation "
826 Author: BTH
827 Given LHS 'a.x' and its start value 'x_start', as well as its enclosing state component 'a' with index 'i' in its governing FLAT_SM 'fsm_of_a' return eqn
828 'a.x_previous = if a.active and (smOf.a.activeReset or smOf.fsm_of_a.activeResetStates[i] then x_start else previous(a.x)'
829 "
830 input DAE.ComponentRef inLHSCref "LHS cref";
831 input DAE.Type inLHSty "LHS type";
832 input DAE.ComponentRef inStateCref "Component reference of state enclosing the equation";
833 input FlatSmSemantics inEnclosingFlatSmSemantics "The flat state machine semantics structure governing the state component";
834 input HashTableCrToExpOption.HashTable crToExpOpt "Table mapping variable declaration in the enclosing state to start values";
835 output DAE.Element outEqn;
836 protected
837 DAE.Exp activeExp, lhsExp, activeResetExp, activeResetStatesExp, orExp, andExp, previousExp, startValueExp, ifExp;
838 Option<DAE.Exp> startValueOpt;
839 DAE.ComponentRef initStateRef, preRef;
840 Integer i, nStates;
841 array<DAE.Element> enclosingFlatSMComps;
842 DAE.Type tArrayBool;
843 DAE.CallAttributes callAttributes;
844 algorithm
845
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 FLAT_SM_SEMANTICS(smComps=enclosingFlatSMComps) := inEnclosingFlatSmSemantics;
846
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 DAE.SM_COMP(componentRef=initStateRef) := arrayGet(enclosingFlatSMComps, 1); // initial state
847
848 // prefix for state machine semantics equations of the governing flat state machine
849 6 preRef := ComponentReference.crefPrefixString(SMS_PRE, initStateRef);
850
851 // position of enclosing state in the array of states of its governing flat state machine
852 6 i := List.position1OnTrue(arrayList(enclosingFlatSMComps), sMCompEqualsRef, inStateCref);
853
854 // smOf.a.activeReset
855 6 activeResetExp := DAE.CREF(qCref("activeReset", DAE.T_BOOL_DEFAULT, {}, preRef), DAE.T_BOOL_DEFAULT);
856
857 nStates := arrayLength(enclosingFlatSMComps);
858 12 tArrayBool := DAE.T_ARRAY(DAE.T_BOOL_DEFAULT,{DAE.DIM_INTEGER(nStates)});
859 // smOf.fsm_of_a.activeResetStates[i]
860 12 activeResetStatesExp := DAE.CREF(qCref("activeResetStates", tArrayBool, {DAE.INDEX(DAE.ICONST(i))}, preRef), DAE.T_BOOL_DEFAULT);
861
862 // smOf.fsm_of_a.activeReset or smOf.fsm_of_a.activeResetStates[i]
863 6 orExp := DAE.LBINARY(activeResetExp, DAE.OR(DAE.T_BOOL_DEFAULT), activeResetStatesExp);
864
865 // a.active (reference the active indicator for this state)
866 6 activeExp := DAE.CREF(qCref("active", DAE.T_BOOL_DEFAULT, {}, inStateCref), DAE.T_BOOL_DEFAULT);
867
868 // a.active and (smOf.fsm_of_a.activeReset or smOf.fsm_of_a.activeResetStates[i])
869 6 andExp := DAE.LBINARY(activeExp, DAE.AND(DAE.T_BOOL_DEFAULT), orExp);
870
871 6 callAttributes := DAE.CALL_ATTR(inLHSty,false,true,false,false,DAE.NO_INLINE(),DAE.NO_TAIL(),DAE.NoReturn.RETURNS);
872 // previous(a.x)
873 12 previousExp := DAE.CALL(Absyn.IDENT("previous"), {DAE.CREF(inLHSCref, inLHSty)}, callAttributes);
874
875 6 startValueOpt := BaseHashTable.get(inLHSCref, crToExpOpt);
876
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
6 if isSome(startValueOpt) then
877 6 startValueExp := Util.getOption(startValueOpt);
878 else
879 // No start value given for the variable, default to "0"
880 startValueExp := match inLHSty
881 case DAE.T_INTEGER()
882 algorithm
883 ✗ Error.addCompilerWarning("Variable "+ComponentReference.crefStr(inLHSCref)+" lacks start value. Defaulting to start=0.\n");
884 then DAE.ICONST(0);
885 case DAE.T_REAL()
886 algorithm
887 ✗ Error.addCompilerWarning("Variable "+ComponentReference.crefStr(inLHSCref)+" lacks start value. Defaulting to start=0.\n");
888 then DAE.RCONST(0);
889 case DAE.T_BOOL()
890 algorithm
891 ✗ Error.addCompilerWarning("Variable "+ComponentReference.crefStr(inLHSCref)+" lacks start value. Defaulting to start=false.\n");
892 then DAE.BCONST(false);
893 case DAE.T_STRING()
894 algorithm
895 ✗ Error.addCompilerWarning("Variable "+ComponentReference.crefStr(inLHSCref)+" lacks start value. Defaulting to start=\"\".\n");
896 then DAE.SCONST("");
897 case DAE.T_ENUMERATION()
898 algorithm
899 ✗ Error.addCompilerWarning("Variable "+ComponentReference.crefStr(inLHSCref)+" lacks start value. Defaulting to start=\"\".\n");
900 ✗ then Types.getNthEnumLiteral(inLHSty, 1);
901 else
902 algorithm
903 ✗ Error.addCompilerError("Variable "+ComponentReference.crefStr(inLHSCref)+" lacks start value.\n");
904 ✗ then fail();
905 end match;
906 end if;
907
908 // if a.active and (smOf.fsm_of_a.activeReset or smOf.fsm_of_a.activeResetStates[i]) than x_start else previous(a.x)
909 6 ifExp := DAE.IFEXP(andExp, startValueExp, previousExp);
910
911 // a.x_previous
912 6 lhsExp := DAE.CREF(ComponentReference.appendStringLastIdent("_previous", inLHSCref), inLHSty);
913
914 // a.x_previous = if a.active and (smOf.a.activeReset or smOf.fsm_of_a.activeResetStates[i] then x_start else previous(a.x)
915 6 outEqn := DAE.EQUATION(lhsExp, ifExp, DAE.emptyElementSource);
916
917 end createResetEquation;
918
919 protected function wrapInStateActivationConditional "
920 Author: BTH
921 Transform an equation 'a.x = e' to 'a.x = if a.active then e else previous(a.x)' (isResetEquation=false)
922 Transform an equation 'a.x = e' to 'a.x = if a.active then e else x_previous' (isResetEquation=true)
923 "
924 input DAE.Element inEqn;
925 input DAE.ComponentRef inStateCref "Component reference of state enclosing the equation";
926 input Boolean isResetEquation "Reset equations";
927 output DAE.Element outEqn;
928 protected
929 DAE.Exp exp, scalar, scalar1, activeRef, expElse;
930 DAE.Type ty;
931 DAE.CallAttributes callAttributes;
932 DAE.ElementSource source;
933 DAE.ComponentRef cref;
934 algorithm
935
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 DAE.EQUATION(exp, scalar, source) := inEqn;
936 try
937
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 DAE.CREF(cref, ty) := exp;
938 else
939 ✗ Error.addCompilerError("The LHS of equations in state machines needs to be a component reference");
940 ✗ fail();
941 end try;
942 // reference the active indicator for this state
943 12 activeRef := DAE.CREF(qCref("active", DAE.T_BOOL_DEFAULT, {}, inStateCref), DAE.T_BOOL_DEFAULT);
944 12 callAttributes := DAE.CALL_ATTR(ty,false,true,false,false,DAE.NO_INLINE(),DAE.NO_TAIL(),DAE.NoReturn.RETURNS);
945
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if isResetEquation then // x_previous
946 6 expElse := DAE.CREF(ComponentReference.appendStringLastIdent("_previous", cref), ty);
947 else // previous(x)
948 6 expElse := DAE.CALL(Absyn.IDENT("previous"), {exp}, callAttributes);
949 end if;
950 12 scalar1 := DAE.IFEXP(activeRef, scalar, expElse);
951 // state.x = if state.active then .. else expElse
952 12 outEqn := DAE.EQUATION(exp, scalar1, source);
953 end wrapInStateActivationConditional;
954
955 protected function wrapInStateActivationConditionalCT "
956 Author: BTH
957 Transform an equation 'der(a.x) = e' to 'der(a.x) = if a.active then e else 0' (isResetEquation=false)
958 TODO: Implement reset equations for continuous time. Should that be done in this function or somewhere else?
959 FIXME: Merge with wrapInStateActivationConditional(..)?
960 "
961 input DAE.Element inEqn;
962 input DAE.ComponentRef inStateCref "Component reference of state enclosing the equation";
963 output DAE.Element outEqn;
964 protected
965 DAE.Exp exp, scalar, scalar1, activeRef, expElse;
966 DAE.Type ty;
967 DAE.CallAttributes callAttributes;
968 DAE.ElementSource source;
969 DAE.ComponentRef cref;
970 algorithm
971 ✗ DAE.EQUATION(exp, scalar, source) := inEqn;
972 try
973 ✗ DAE.CALL(Absyn.IDENT("der"), {DAE.CREF(componentRef=cref, ty=ty)}, _) := exp;
974 else
975 ✗ Error.addCompilerError("The LHS of equations in state machines needs to be a component reference, e.g., x = .., or its derivative, e.g., der(x) = ..");
976 ✗ fail();
977 end try;
978 // reference the active indicator for this state
979 ✗ activeRef := DAE.CREF(qCref("active", DAE.T_BOOL_DEFAULT, {}, inStateCref), DAE.T_BOOL_DEFAULT);
980 ✗ callAttributes := DAE.CALL_ATTR(ty,false,true,false,false,DAE.NO_INLINE(),DAE.NO_TAIL(),DAE.NoReturn.RETURNS);
981 expElse := DAE.RCONST(0);
982 ✗ scalar1 := DAE.IFEXP(activeRef, scalar, expElse);
983 // state.x = if state.active then .. else expElse
984 ✗ outEqn := DAE.EQUATION(exp, scalar1, source);
985 end wrapInStateActivationConditionalCT;
986
987 protected function traversingSubsPreviousCref "
988 Author: BTH
989 Given a cref 'x', find if the expression has subexpressions 'previous(x)' and replace them by 'x_previous'
990 and return an indication if any substitutions took place.
991 "
992 input DAE.Exp inExp;
993 input tuple<DAE.ComponentRef, Boolean> inCrefHit;
994 output DAE.Exp outExp;
995 output Boolean cont = true;
996 output tuple<DAE.ComponentRef, Boolean> outCrefHit;
997 algorithm
998 (outExp, outCrefHit) := match (inExp, inCrefHit)
999 local
1000 DAE.ComponentRef cr, cref, substituteRef;
1001 DAE.Type ty;
1002 case (DAE.CALL(Absyn.IDENT("previous"), {DAE.CREF(cr, ty)}, _),
1003 (cref, _)) guard ComponentReferenceBasics.crefEqual(cr, cref)
1004 algorithm
1005 ✗ print("StateMachineFlatten.traversingSubsPreviousCref: cr: "+ComponentReference.crefStr(cr)+", cref: "+ComponentReference.crefStr(cref)+"\n");
1006 ✗ substituteRef := ComponentReference.appendStringLastIdent("_previous", cref);
1007 ✗ then (DAE.CREF(substituteRef, ty), (cref, true));
1008 else (inExp, inCrefHit);
1009 end match;
1010
1011 end traversingSubsPreviousCref;
1012
1013 protected function traversingSubsPreviousCrefs "
1014 Author: BTH
1015 Given a list of crefs '{x1,x2,...}', find if the expression has subexpressions 'previous(x)' and replace them by 'x_previous'
1016 and return an indication if any substitutions took place.
1017 "
1018 input DAE.Exp inExp;
1019 input tuple<list<DAE.ComponentRef>, Boolean> inCrefsHit;
1020 output DAE.Exp outExp;
1021 output Boolean cont = true;
1022 output tuple<list<DAE.ComponentRef>, Boolean> outCrefsHit;
1023 algorithm
1024 (outExp, outCrefsHit) := match (inExp, inCrefsHit)
1025 local
1026 DAE.ComponentRef cr, substituteRef;
1027 list<DAE.ComponentRef> crefs;
1028 DAE.Type ty;
1029 case (DAE.CALL(Absyn.IDENT("previous"), {DAE.CREF(cr, ty)}, _), (crefs, _))
1030 guard List.any(crefs, function ComponentReferenceBasics.crefEqual(inComponentRef1=cr))
1031 algorithm
1032 // print("StateMachineFlatten.traversingSubsPreviousCrefs: cr: "+ComponentReference.crefStr(cr)+", crefs: " + stringDelimitList(List.map(crefs, ComponentReference.crefStr), ",")+"\n");
1033 6 substituteRef := ComponentReference.appendStringLastIdent("_previous", cr);
1034 6 then (DAE.CREF(substituteRef, ty), (crefs, true));
1035 else (inExp, inCrefsHit);
1036 end match;
1037
1038 end traversingSubsPreviousCrefs;
1039
1040 protected function getStartAttrOption "
1041 Helper function to smCompToDataFlow
1042 "
1043 input DAE.Element inElt;
1044 output Option<DAE.Exp> outExpOpt;
1045 protected
1046 DAE.Exp start;
1047 DAE.Type ty;
1048 Option<DAE.VariableAttributes> varAttrOpt;
1049 algorithm
1050
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 DAE.VAR(variableAttributesOption=varAttrOpt, ty=ty) := inElt;
1051
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 if isSome(varAttrOpt) then
1052 6 start := DAEUtil.getStartAttr(varAttrOpt, ty);
1053 outExpOpt := SOME(start);
1054 else
1055 outExpOpt := NONE();
1056 end if;
1057 end getStartAttrOption;
1058
1059 protected function addPropagationEquations "
1060 Author: BTH
1061 Add activation and reset propagation related equation and variables to flat state machine
1062 "
1063 input FlatSmSemantics inFlatSmSemantics;
1064 input Option<DAE.ComponentRef> inEnclosingStateCrefOption "Cref of state that encloses the flat state machiene (NONE() if at top hierarchy)";
1065 input Option<FlatSmSemantics> inEnclosingFlatSmSemanticsOption "The flat state machine semantics structure governing the enclosing state (NONE() if at top hierarchy)";
1066 output FlatSmSemantics outFlatSmSemantics;
1067 protected
1068 DAE.ComponentRef preRef, initStateRef, initRef, resetRef, activeRef, stateRef, activePlotIndicatorRef;
1069 DAE.Element initVar, activePlotIndicatorVar, ticksInStateVar, timeEnteredStateVar, timeInStateVar;
1070 DAE.Element activePlotIndicatorEqn, ticksInStateEqn, timeEnteredStateEqn, timeInStateEqn;
1071 DAE.Exp rhs, andExp, eqExp;
1072 DAE.Type tArrayBool, tArrayInteger;
1073
1074 // FLAT_SM_SEMANTICS
1075 DAE.Ident ident;
1076 array<DAE.Element> smComps "First element is the initial state";
1077 list<Transition> t "List/Array of transition data sorted in priority";
1078 list<DAE.Exp> c "Transition conditions sorted in priority";
1079 list<DAE.Element> smvars "SMS veriables";
1080 list<DAE.Element> smknowns "SMS constants/parameters";
1081 list<DAE.Element> smeqs "SMS equations";
1082 // FIXME needed?
1083 list<DAE.Element> pvars = {} "Propagation related variables";
1084 list<DAE.Element> peqs = {} "Propagation equations";
1085
1086 // Enclosing FLAT_SM_SEMANTICS
1087 DAE.ComponentRef enclosingStateCref, enclosingPreRef, enclosingActiveResetStateRef, enclosingActiveResetRef, enclosingActiveStateRef;
1088 FlatSmSemantics enclosingFlatSMSemantics;
1089 array<DAE.Element> enclosingFlatSMComps "First element is the initial state";
1090 DAE.ComponentRef enclosingFlatSMInitStateRef;
1091 Integer posOfEnclosingSMComp, nStates;
1092
1093
1094 algorithm
1095
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 FLAT_SM_SEMANTICS(ident=ident, smComps=smComps, t=t, c=c, vars=smvars, knowns=smknowns, eqs=smeqs) := inFlatSmSemantics;
1096
1097
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 DAE.SM_COMP(componentRef=initStateRef) := arrayGet(smComps, 1); // initial state
1098 // cref prefix for semantics equations governing flat state machine
1099 7 preRef := ComponentReference.crefPrefixString(SMS_PRE, initStateRef);
1100
1101 // MLS 17.3.4 Semantics Summary: "active" and "reset" are *inputs* to the state machine semantics. They are defined below
1102 7 activeRef := qCref("active", DAE.T_BOOL_DEFAULT, {}, preRef);
1103 7 resetRef := qCref("reset", DAE.T_BOOL_DEFAULT, {}, preRef);
1104
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 4 times.
7 if isNone(inEnclosingFlatSmSemanticsOption) then
1105 // toplevel flat state machines need to "self-reset" at their first clock tick. After that reset is always false
1106 // Boolean preRef.init(start=true) = false
1107 3 initRef := qCref("init", DAE.T_BOOL_DEFAULT, {}, preRef);
1108 3 initVar := createVarWithDefaults(initRef, DAE.DISCRETE(), DAE.T_BOOL_DEFAULT, {});
1109 3 initVar := setVarFixedStartValue(initVar, DAE.BCONST(true));
1110 pvars := initVar :: pvars;
1111 3 peqs := DAE.EQUATION(DAE.CREF(initRef, DAE.T_BOOL_DEFAULT), DAE.BCONST(false), DAE.emptyElementSource) :: peqs;
1112
1113 // preRef.reset = previous(preRef.init)
1114 6 rhs := DAE.CALL(Absyn.IDENT("previous"), {DAE.CREF(initRef, DAE.T_BOOL_DEFAULT)}, DAE.callAttrBuiltinImpureBool);
1115 3 peqs := DAE.EQUATION(DAE.CREF(resetRef, DAE.T_BOOL_DEFAULT), rhs, DAE.emptyElementSource) :: peqs;
1116
1117 // input Boolean active "true if the state machine is active";
1118 // set to "true", since toplevel state machines is always active
1119 3 peqs := DAE.EQUATION(DAE.CREF(activeRef, DAE.T_BOOL_DEFAULT), DAE.BCONST(true), DAE.emptyElementSource) :: peqs;
1120 else
1121 // We have an enclosing state: propagate reset handling and activation handling to refined state machine
1122 4 enclosingStateCref := Util.getOption(inEnclosingStateCrefOption);
1123 4 enclosingFlatSMSemantics := Util.getOption(inEnclosingFlatSmSemanticsOption);
1124
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 FLAT_SM_SEMANTICS(smComps=enclosingFlatSMComps) := enclosingFlatSMSemantics;
1125 // initial state of enclosing flat state machine
1126
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 DAE.SM_COMP(componentRef=enclosingFlatSMInitStateRef) := arrayGet(enclosingFlatSMComps, 1);
1127 // cref prefix for semantics equations governing enclosing SM
1128 4 enclosingPreRef := ComponentReference.crefPrefixString(SMS_PRE, enclosingFlatSMInitStateRef);
1129
1130 // Position of enclosing state in enclosing flat state machine
1131 4 posOfEnclosingSMComp := List.position1OnTrue(arrayList(enclosingFlatSMComps), sMCompEqualsRef, enclosingStateCref);
1132
1133 // == Create equation for SMS_PRE.initStateRef.reset ==
1134 nStates := arrayLength(enclosingFlatSMComps);
1135 8 tArrayBool := DAE.T_ARRAY(DAE.T_BOOL_DEFAULT,{DAE.DIM_INTEGER(nStates)});
1136 8 tArrayInteger := DAE.T_ARRAY(DAE.T_INTEGER_DEFAULT,{DAE.DIM_INTEGER(nStates)});
1137 8 enclosingActiveResetStateRef := qCref("activeResetStates", tArrayBool, {DAE.INDEX(DAE.ICONST(posOfEnclosingSMComp))}, enclosingPreRef);
1138 4 enclosingActiveResetRef := qCref("activeReset", DAE.T_BOOL_DEFAULT, {}, enclosingPreRef);
1139 4 enclosingActiveStateRef := qCref("activeState", DAE.T_INTEGER_DEFAULT, {}, enclosingPreRef);
1140 // SMS_PRE.enclosingFlatSMInitStateRef.activeState == posOfEnclosingSMComp
1141 4 eqExp := DAE.RELATION(DAE.CREF(enclosingActiveStateRef, DAE.T_INTEGER_DEFAULT), DAE.EQUAL(DAE.T_INTEGER_DEFAULT), DAE.ICONST(posOfEnclosingSMComp),-1, NONE());
1142 // SMS_PRE.enclosingFlatSMInitStateRef.activeReset and SMS_PRE.enclosingFlatSMInitStateRef.activeState == posOfEnclosingSMComp
1143 4 andExp := DAE.LBINARY(DAE.CREF(enclosingActiveResetRef, DAE.T_BOOL_DEFAULT), DAE.AND(DAE.T_BOOL_DEFAULT), eqExp);
1144 4 rhs := DAE.LBINARY(DAE.CREF(enclosingActiveResetStateRef, DAE.T_BOOL_DEFAULT), DAE.OR(DAE.T_BOOL_DEFAULT), andExp);
1145 // SMS_PRE.initStateRef.reset = SMS_PRE.enclosingFlatSMInitStateRef.activeResetStates[posOfEnclosingSMComp]
1146 // or (SMS_PRE.enclosingFlatSMInitStateRef.activeReset and SMS_PRE.enclosingFlatSMInitStateRef.activeState == posOfEnclosingSMComp)
1147 4 peqs := DAE.EQUATION(DAE.CREF(resetRef, DAE.T_BOOL_DEFAULT), rhs, DAE.emptyElementSource) :: peqs;
1148
1149 // == Create equation for SMS_PRE.initStateRef.active ==
1150 4 rhs := DAE.RELATION(DAE.CREF(enclosingActiveStateRef, DAE.T_INTEGER_DEFAULT), DAE.EQUAL(DAE.T_INTEGER_DEFAULT), DAE.ICONST(posOfEnclosingSMComp),-1, NONE());
1151 // SMS_PRE.initStateRef.active = SMS_PRE.enclosingFlatSMInitStateRef.activeState == posOfEnclosingSMComp
1152 4 peqs := DAE.EQUATION(DAE.CREF(activeRef, DAE.T_BOOL_DEFAULT), rhs, DAE.emptyElementSource) :: peqs;
1153 end if;
1154
1155 // Decorate state with additional information
1156
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
24 for i in 1:arrayLength(smComps) loop
1157 // Add indication for plotting whether a state is active or not (stateRef.active)
1158
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
17 DAE.SM_COMP(componentRef=stateRef) := arrayGet(smComps, i);
1159 17 (activePlotIndicatorVar, activePlotIndicatorEqn) := createActiveIndicator(stateRef, preRef, i);
1160 pvars := activePlotIndicatorVar :: pvars;
1161 17 peqs := activePlotIndicatorEqn :: peqs;
1162
1163 // Add ticksInState counter (stateRef.$ticksInState = if stateRef.active then previous(stateRef.$ticksInState) + 1 else 0)
1164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 DAE.VAR(componentRef=activePlotIndicatorRef) := activePlotIndicatorVar;
1165 17 (ticksInStateVar, ticksInStateEqn) := createTicksInStateIndicator(stateRef, activePlotIndicatorRef);
1166 pvars := ticksInStateVar :: pvars;
1167 17 peqs := ticksInStateEqn :: peqs;
1168
1169 // == Add timeInState Indicator (stateRef.$timeInState(start=0)) ==
1170 // Auxiliary variable holding time when state was entred (stateRef.$timeEnteredState(start=0)
1171 // stateRef.$timeEnteredState = if previous(stateRef.active) == false and stateRef.active == true then sample(time) else previous(stateRef.$timeEnteredState)
1172 17 (timeEnteredStateVar, timeEnteredStateEqn) := createTimeEnteredStateIndicator(stateRef, activePlotIndicatorRef);
1173 // stateRef.$timeInState = if stateRef.active then sample(time) - stateRef.$timeEnteredState else 0
1174 17 (timeInStateVar, timeInStateEqn) := createTimeInStateIndicator(stateRef, activePlotIndicatorRef, timeEnteredStateVar);
1175 pvars := timeEnteredStateVar :: timeInStateVar :: pvars;
1176 17 peqs := timeEnteredStateEqn :: timeInStateEqn :: peqs;
1177 end for;
1178
1179 7 outFlatSmSemantics := FLAT_SM_SEMANTICS(ident, smComps, t, c, smvars, smknowns, smeqs, pvars, peqs, inEnclosingStateCrefOption);
1180
1181 end addPropagationEquations;
1182
1183 protected function createTimeInStateIndicator "
1184 Author: BTH
1185 Helper function to addPropagationEquations.
1186 Create variable that indicates the time duration since a transition was made to the currently active state"
1187 input DAE.ComponentRef stateRef "cref of state to which timeInState variable shall be added";
1188 input DAE.ComponentRef stateActiveRef "cref of active indicator corresponding to stateRef";
1189 input DAE.Element timeEnteredStateVar "Auxiliary variable generated in createTimeEnteredStateIndicator(..)";
1190 output DAE.Element timeInStateVar;
1191 output DAE.Element timeInStateEqn;
1192 protected
1193 DAE.ComponentRef timeInStateRef, timeEnteredStateRef;
1194 DAE.Type ty;
1195 DAE.Exp timeInStateExp, timeEnteredStateExp, stateActiveExp, expCond, expSampleTime, expThen, expElse;
1196 algorithm
1197 // Create Variable stateRef.$timeEnteredState
1198 17 timeInStateRef := qCref("$timeInState", DAE.T_REAL_DEFAULT, {}, stateRef);
1199 17 timeInStateVar := createVarWithDefaults(timeInStateRef, DAE.DISCRETE(), DAE.T_REAL_DEFAULT, {});
1200 17 timeInStateVar := setVarFixedStartValue(timeInStateVar, DAE.RCONST(0));
1201 17 timeInStateExp := DAE.CREF(timeInStateRef, DAE.T_REAL_DEFAULT);
1202
1203
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 DAE.VAR(componentRef=timeEnteredStateRef, ty=ty) := timeEnteredStateVar;
1204 17 timeEnteredStateExp := DAE.CREF(timeEnteredStateRef, ty);
1205
1206 17 stateActiveExp := Expression.crefExp(stateActiveRef);
1207
1208 // == $timeInState = if active then sample(time) - $timeEnteredState else 0; ==
1209 // active
1210 17 expCond := Expression.crefExp(stateActiveRef);
1211 // sample(time)
1212 expSampleTime := DAE.CALL(Absyn.IDENT("sample"),
1213 { DAE.CREF(DAE.CREF_IDENT("time", DAE.T_REAL_DEFAULT, {}), DAE.T_REAL_DEFAULT),
1214 DAE.CLKCONST(DAE.INFERRED_CLOCK())},
1215 DAE.callAttrBuiltinImpureReal);
1216 // sample(time) - $timeEnteredState
1217 17 expThen := DAE.BINARY(expSampleTime, DAE.SUB(DAE.T_REAL_DEFAULT), timeEnteredStateExp);
1218 // 0
1219 expElse := DAE.RCONST(0);
1220 // $timeInState = if active then sample(time) - $timeEnteredState else 0;
1221 17 timeInStateEqn := DAE.EQUATION(timeInStateExp, DAE.IFEXP(expCond, expThen, expElse), DAE.emptyElementSource);
1222 end createTimeInStateIndicator;
1223
1224 protected function createTimeEnteredStateIndicator "
1225 Author: BTH
1226 Helper function to addPropagationEquations.
1227 Create auxiliary variable that remembers the time in which a state is entered"
1228 input DAE.ComponentRef stateRef "cref of state to which timeEnteredState variable shall be added";
1229 input DAE.ComponentRef stateActiveRef "cref of active indicator corresponding to stateRef";
1230 output DAE.Element timeEnteredStateVar;
1231 output DAE.Element timeEnteredStateEqn;
1232 protected
1233 DAE.ComponentRef timeEnteredStateRef;
1234 DAE.Exp timeEnteredStateExp, stateActiveExp, expCond, expThen, expElse;
1235 algorithm
1236 // Create Variable stateRef.$timeEnteredState
1237 17 timeEnteredStateRef := qCref("$timeEnteredState", DAE.T_REAL_DEFAULT, {}, stateRef);
1238 17 timeEnteredStateVar := createVarWithDefaults(timeEnteredStateRef, DAE.DISCRETE(), DAE.T_REAL_DEFAULT, {});
1239 17 timeEnteredStateVar := setVarFixedStartValue(timeEnteredStateVar, DAE.RCONST(0));
1240 17 timeEnteredStateExp := DAE.CREF(timeEnteredStateRef, DAE.T_REAL_DEFAULT);
1241
1242 17 stateActiveExp := Expression.crefExp(stateActiveRef);
1243
1244 // == $timeEnteredState = if previous(active) == false and active == true then sample(time) else previous($timeEnteredState); ==
1245 // previous(active) == false and active == true
1246 17 expCond := DAE.LBINARY(
1247 DAE.RELATION( DAE.CALL(Absyn.IDENT("previous"), {stateActiveExp}, DAE.callAttrBuiltinImpureBool), DAE.EQUAL(DAE.T_BOOL_DEFAULT), DAE.BCONST(false), -1, NONE()), // previous(active) == false
1248 DAE.AND(DAE.T_BOOL_DEFAULT), // and
1249 DAE.RELATION( stateActiveExp, DAE.EQUAL(DAE.T_BOOL_DEFAULT), DAE.BCONST(true), -1, NONE()) // active == true
1250 );
1251 // sample(time)
1252 expThen := DAE.CALL(Absyn.IDENT("sample"),
1253 { DAE.CREF(DAE.CREF_IDENT("time", DAE.T_REAL_DEFAULT, {}), DAE.T_REAL_DEFAULT),
1254 DAE.CLKCONST(DAE.INFERRED_CLOCK())},
1255 DAE.callAttrBuiltinImpureReal);
1256 // previous($timeEnteredState)
1257 17 expElse := DAE.CALL(Absyn.IDENT("previous"), {timeEnteredStateExp}, DAE.callAttrBuiltinImpureReal);
1258 // $timeEnteredState = if previous(active) == false and active == true then sample(time) else previous($timeEnteredState);
1259 17 timeEnteredStateEqn := DAE.EQUATION(timeEnteredStateExp, DAE.IFEXP(expCond, expThen, expElse), DAE.emptyElementSource);
1260 end createTimeEnteredStateIndicator;
1261
1262 protected function createTicksInStateIndicator "
1263 Author: BTH
1264 Helper function to addPropagationEquations.
1265 Create variable that counts ticks within a state"
1266 input DAE.ComponentRef stateRef "cref of state to which ticksInState counter shall be added";
1267 input DAE.ComponentRef stateActiveRef "cref of active indicator corresponding to stateRef";
1268 output DAE.Element ticksInStateVar;
1269 output DAE.Element ticksInStateEqn;
1270 protected
1271 DAE.ComponentRef ticksInStateRef;
1272 DAE.Exp ticksInStateExp, expCond, expThen, expElse;
1273 algorithm
1274 // Create Variable stateRef.$ticksInState
1275 17 ticksInStateRef := qCref("$ticksInState", DAE.T_INTEGER_DEFAULT, {}, stateRef);
1276 17 ticksInStateVar := createVarWithDefaults(ticksInStateRef, DAE.DISCRETE(), DAE.T_INTEGER_DEFAULT, {});
1277 17 ticksInStateVar := setVarFixedStartValue(ticksInStateVar, DAE.ICONST(0));
1278
1279 // $ticksInState = if active then previous($ticksInState) + 1 else 0;
1280 17 ticksInStateExp := DAE.CREF(ticksInStateRef, DAE.T_INTEGER_DEFAULT);
1281 17 expCond := Expression.crefExp(stateActiveRef);
1282 // previous($ticksInState) + 1
1283 17 expThen := DAE.BINARY(DAE.CALL(Absyn.IDENT("previous"), {ticksInStateExp}, DAE.callAttrBuiltinImpureInteger), DAE.ADD(DAE.T_INTEGER_DEFAULT), DAE.ICONST(1));
1284 expElse := DAE.ICONST(0);
1285 17 ticksInStateEqn := DAE.EQUATION(ticksInStateExp, DAE.IFEXP(expCond, expThen, expElse), DAE.emptyElementSource);
1286 end createTicksInStateIndicator;
1287
1288 protected function createActiveIndicator "
1289 Author: BTH
1290 Helper function to addPropagationEquations.
1291 Create indication (e.g., for plotting) whether a state is active or not"
1292 input DAE.ComponentRef stateRef "cref of state to which activation indication shall be added";
1293 input DAE.ComponentRef preRef "cref of prefix where variables of governing semantic equations for stateRef are located";
1294 input Integer i "index of state within flat state machine state array";
1295 output DAE.Element activePlotIndicatorVar;
1296 output DAE.Element eqn;
1297 protected
1298 DAE.ComponentRef activeRef, activePlotIndicatorRef, activeStateRef;
1299 DAE.Exp andExp, eqExp;
1300 algorithm
1301 // Create Variable stateRef.active
1302 // FIXME Use name that cannot possible conflict with user variable (or is .active reserved for state machines?)
1303 17 activePlotIndicatorRef := qCref("active", DAE.T_BOOL_DEFAULT, {}, stateRef);
1304 17 activePlotIndicatorVar := createVarWithStartValue(activePlotIndicatorRef, DAE.DISCRETE(), DAE.T_BOOL_DEFAULT, DAE.BCONST(false), {});
1305
1306 // stateRef.active := SMS_PRE.initialState.active and (SMS_PRE.initialState.activeState==i)
1307 17 activeRef := qCref("active", DAE.T_BOOL_DEFAULT, {}, preRef);
1308 17 activeStateRef := qCref("activeState", DAE.T_INTEGER_DEFAULT, {}, preRef);
1309 // SMS_PRE.initialState.activeState==i
1310 17 eqExp := DAE.RELATION(DAE.CREF(activeStateRef, DAE.T_INTEGER_DEFAULT), DAE.EQUAL(DAE.T_INTEGER_DEFAULT), DAE.ICONST(i),-1, NONE());
1311 // SMS_PRE.initialState.active and (SMS_PRE.initialState.activeState==i)
1312 17 andExp := DAE.LBINARY(DAE.CREF(activeRef, DAE.T_BOOL_DEFAULT), DAE.AND(DAE.T_BOOL_DEFAULT), eqExp);
1313 17 eqn := DAE.EQUATION(DAE.CREF(activePlotIndicatorRef, DAE.T_BOOL_DEFAULT), andExp, DAE.emptyElementSource);
1314 end createActiveIndicator;
1315
1316
1317 protected function setVarFixedStartValue "
1318 Author: BTH
1319 Set a fixed start value to a variable
1320 "
1321 input DAE.Element inVar;
1322 input DAE.Exp inExp;
1323 output DAE.Element outVar;
1324 protected
1325 Option<DAE.VariableAttributes> vao;
1326 algorithm
1327
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 116 times.
116 DAE.VAR(variableAttributesOption=vao) := inVar;
1328 116 vao := DAEUtil.setStartAttrOption(vao, SOME(inExp));
1329 116 vao := DAEUtil.setFixedAttr(vao, SOME(DAE.BCONST(true)));
1330 116 outVar := DAEUtil.setVariableAttributes(inVar, vao);
1331 end setVarFixedStartValue;
1332
1333 protected function basicFlatSmSemantics "
1334 Author: BTH
1335 Helper function to flatSmToDataFlow.
1336 Create variables/parameters and equations for defining the state machine semantic (SMS) equations.
1337 "
1338 input DAE.Ident ident;
1339 input list<DAE.Element> q "state components";
1340 input list<DAE.Element> inTransitions;
1341 output FlatSmSemantics flatSmSemantics;
1342 protected
1343 DAE.ComponentRef crefInitialState, preRef;
1344
1345 // Modeling variables and parameters/constants
1346 DAE.Element defaultIntVar, defaultBoolVar;
1347 list<DAE.Element> vars "SMS veriables", knowns "SMS constants/parameters";
1348 Integer i;
1349
1350 DAE.ComponentRef preRef, nStatesRef, activeRef, resetRef, selectedStateRef, selectedResetRef, firedRef, activeStateRef, activeResetRef, nextStateRef, nextResetRef, stateMachineInFinalStateRef;
1351 DAE.Element nStatesVar, activeVar, resetVar, selectedStateVar, selectedResetVar, firedVar, activeStateVar, activeResetVar, nextStateVar, nextResetVar, stateMachineInFinalStateVar;
1352
1353 // Modeling arrays with size nStates
1354 Integer nStates;
1355 DAE.InstDims nStatesDims;
1356 DAE.Type nStatesArrayBool;
1357 array<DAE.ComponentRef> activeResetStatesRefs, nextResetStatesRefs, finalStatesRefs;
1358 array<DAE.Element> activeResetStatesVars, nextResetStatesVars, finalStatesVars;
1359
1360 // Modeling Transitions "t":
1361 list<Transition> t;
1362 Integer nTransitions;
1363 DAE.InstDims tDims;
1364 DAE.Type tArrayInteger, tArrayBool;
1365 array<DAE.ComponentRef> tFromRefs, tToRefs, tImmediateRefs, tResetRefs, tSynchronizeRefs, tPriorityRefs;
1366 array<DAE.Element> tFromVars, tToVars, tImmediateVars, tResetVars, tSynchronizeVars, tPriorityVars;
1367 // TRANSITION
1368 Integer from;
1369 Integer to;
1370 Boolean immediate;
1371 Boolean reset;
1372 Boolean synchronize;
1373 Integer priority;
1374
1375 // Modeling Conditions "c":
1376 list<DAE.Exp> cExps;
1377 array<DAE.ComponentRef> cRefs, cImmediateRefs;
1378 array<DAE.Element> cVars, cImmediateVars;
1379
1380 // Modeling Equations
1381 list<DAE.Element> eqs "SMS equations";
1382 DAE.Element selectedStateEqn, selectedResetEqn, firedEqn, activeStateEqn, activeResetEqn, nextStateEqn, nextResetEqn;
1383 DAE.Exp exp, rhs, expCond, expThen, expElse, exp1, exp2, expIf;
1384 list<DAE.Exp> expLst;
1385 Option<DAE.Exp> bindExp;
1386
1387
1388 algorithm
1389 // make sure that created vars won't clutter up the variable space
1390
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
7 DAE.SM_COMP(componentRef=crefInitialState) := listHead(q);
1391 7 preRef := ComponentReference.crefPrefixString(SMS_PRE, crefInitialState);
1392
1393 7 (t, cExps) := createTandC(q, inTransitions);
1394 // print("StateMachineFlatten.basicFlatSmSemantics: transitions:\n\t" + stringDelimitList(List.map(t, dumpTransitionStr), "\n\t") + "\n");
1395 // print("StateMachineFlatten.basicFlatSmSemantics: conditions\n\t" + stringDelimitList(List.map(cExps, ExpressionBasics.printExpStr), "\n\t") + "\n");
1396
1397 7 defaultIntVar := createVarWithDefaults(ComponentReference.makeDummyCref(), DAE.DISCRETE(), DAE.T_INTEGER_DEFAULT, {});
1398 7 defaultBoolVar := createVarWithDefaults(ComponentReference.makeDummyCref(), DAE.DISCRETE(), DAE.T_BOOL_DEFAULT, {});
1399 knowns := {};
1400 vars := {};
1401
1402 // ***** Create new variable declarations needed for semantic equations *****
1403 7 nStates := listLength(q);
1404 7 nStatesRef := qCref("nState", DAE.T_INTEGER_DEFAULT, {}, preRef);
1405 7 nStatesVar := createVarWithDefaults(nStatesRef, DAE.PARAM(), DAE.T_INTEGER_DEFAULT, {});
1406 14 nStatesVar := DAEUtil.setElementVarBinding(nStatesVar, SOME(DAE.ICONST(nStates)));
1407 knowns := nStatesVar :: knowns;
1408
1409 // parameter Transition t[:] "Array of transition data sorted in priority";
1410 7 nTransitions := listLength(t);
1411 7 tDims := {DAE.DIM_INTEGER(nTransitions)};
1412 7 tArrayInteger := DAE.T_ARRAY(DAE.T_INTEGER_DEFAULT,tDims);
1413 7 tArrayBool := DAE.T_ARRAY(DAE.T_BOOL_DEFAULT,tDims);
1414 7 tFromRefs := arrayCreate(nTransitions, ComponentReference.makeDummyCref());
1415 7 tToRefs := arrayCreate(nTransitions, ComponentReference.makeDummyCref());
1416 7 tImmediateRefs := arrayCreate(nTransitions, ComponentReference.makeDummyCref());
1417 7 tResetRefs := arrayCreate(nTransitions, ComponentReference.makeDummyCref());
1418 7 tSynchronizeRefs := arrayCreate(nTransitions, ComponentReference.makeDummyCref());
1419 7 tPriorityRefs := arrayCreate(nTransitions, ComponentReference.makeDummyCref());
1420 7 tFromVars := arrayCreate(nTransitions, defaultIntVar);
1421 7 tToVars := arrayCreate(nTransitions, defaultIntVar);
1422 7 tImmediateVars := arrayCreate(nTransitions, defaultBoolVar);
1423 7 tResetVars := arrayCreate(nTransitions, defaultBoolVar);
1424 7 tSynchronizeVars := arrayCreate(nTransitions, defaultBoolVar);
1425 7 tPriorityVars := arrayCreate(nTransitions, defaultIntVar);
1426 i := 0;
1427
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 7 times.
21 for t1 in t loop
1428 14 i := i+1;
1429 14 TRANSITION(from,to,_,immediate,reset,synchronize,priority) := t1;
1430 28 tFromRefs := arrayUpdate(tFromRefs, i, qCref("tFrom", tArrayInteger, {DAE.INDEX(DAE.ICONST(i))}, preRef));
1431 14 tFromVars := arrayUpdate(tFromVars, i, createVarWithDefaults(arrayGet(tFromRefs,i), DAE.PARAM(), DAE.T_INTEGER_DEFAULT, tDims));
1432 28 tFromVars := arrayUpdate(tFromVars, i, DAEUtil.setElementVarBinding(arrayGet(tFromVars,i), SOME(DAE.ICONST(from))));
1433 14 knowns := arrayGet(tFromVars,i) :: knowns;
1434
1435 28 tToRefs := arrayUpdate(tToRefs, i, qCref("tTo", tArrayInteger, {DAE.INDEX(DAE.ICONST(i))}, preRef));
1436 14 tToVars := arrayUpdate(tToVars, i, createVarWithDefaults(arrayGet(tToRefs,i), DAE.PARAM(), DAE.T_INTEGER_DEFAULT, tDims));
1437 28 tToVars := arrayUpdate(tToVars, i, DAEUtil.setElementVarBinding(arrayGet(tToVars,i), SOME(DAE.ICONST(to))));
1438 14 knowns := arrayGet(tToVars,i) :: knowns;
1439
1440 28 tImmediateRefs := arrayUpdate(tImmediateRefs, i, qCref("tImmediate", tArrayBool, {DAE.INDEX(DAE.ICONST(i))}, preRef));
1441 14 tImmediateVars := arrayUpdate(tImmediateVars, i, createVarWithDefaults(arrayGet(tImmediateRefs,i), DAE.PARAM(), DAE.T_BOOL_DEFAULT, tDims));
1442
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
42 tImmediateVars := arrayUpdate(tImmediateVars, i, DAEUtil.setElementVarBinding(arrayGet(tImmediateVars,i), SOME(DAE.BCONST(immediate))));
1443 14 knowns := arrayGet(tImmediateVars,i) :: knowns;
1444
1445 28 tResetRefs := arrayUpdate(tResetRefs, i, qCref("tReset", tArrayBool, {DAE.INDEX(DAE.ICONST(i))}, preRef));
1446 14 tResetVars := arrayUpdate(tResetVars, i, createVarWithDefaults(arrayGet(tResetRefs,i), DAE.PARAM(), DAE.T_BOOL_DEFAULT, tDims));
1447
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
28 tResetVars := arrayUpdate(tResetVars, i, DAEUtil.setElementVarBinding(arrayGet(tResetVars,i), SOME(DAE.BCONST(reset))));
1448 14 knowns := arrayGet(tResetVars,i) :: knowns;
1449
1450 28 tSynchronizeRefs := arrayUpdate(tSynchronizeRefs, i, qCref("tSynchronize", tArrayBool, {DAE.INDEX(DAE.ICONST(i))}, preRef));
1451 14 tSynchronizeVars := arrayUpdate(tSynchronizeVars, i, createVarWithDefaults(arrayGet(tSynchronizeRefs,i), DAE.PARAM(), DAE.T_BOOL_DEFAULT, tDims));
1452
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
42 tSynchronizeVars := arrayUpdate(tSynchronizeVars, i, DAEUtil.setElementVarBinding(arrayGet(tSynchronizeVars,i), SOME(DAE.BCONST(synchronize))));
1453 14 knowns := arrayGet(tSynchronizeVars,i) :: knowns;
1454
1455 28 tPriorityRefs := arrayUpdate(tPriorityRefs, i, qCref("tPriority", tArrayInteger, {DAE.INDEX(DAE.ICONST(i))}, preRef));
1456 14 tPriorityVars := arrayUpdate(tPriorityVars, i, createVarWithDefaults(arrayGet(tPriorityRefs,i), DAE.PARAM(), DAE.T_INTEGER_DEFAULT, tDims));
1457 28 tPriorityVars := arrayUpdate(tPriorityVars, i, DAEUtil.setElementVarBinding(arrayGet(tPriorityVars,i), SOME(DAE.ICONST(priority))));
1458 14 knowns := arrayGet(tPriorityVars,i) :: knowns;
1459 end for;
1460
1461 // input Boolean c[size(t,1)] "Transition conditions sorted in priority";
1462 // input Boolean cImmediate[size(t,1)];
1463 /* IMPLEMENTATION NOTE in respect to MLS: cImmediate is introduced in order to delay transitions by simply doing c[i] = previous(cImmediate[i]) for delayed transitons.
1464 Now, all c[i] can be treated as immediate transitions. Hence, different to MLS 17.3.4 there are no distinguished equations for 'immediate' or 'delayed' transitions needed.
1465 This avoids seemingly algebraic dependency loops for delayed transitions that are introduced if MLS 17.3.4 equations are implemented directly
1466 (this is because in MLS 17.3.4 delayed transitions c[i] appear in a non-delayed form in the equation for 'immediate';
1467 actually, MLS 17.3.4 doesn't introduce 'real' algebraic loops for delayed transitions since if-conditions "exclude" the paths that would lead to algebraic loops during execution;
1468 however, it requires sophisticated analysis for a tool to statically deduce that fact)
1469 */
1470 7 cRefs := arrayCreate(nTransitions, ComponentReference.makeDummyCref());
1471 7 cImmediateRefs := arrayCreate(nTransitions, ComponentReference.makeDummyCref());
1472 7 cVars := arrayCreate(nTransitions, defaultBoolVar);
1473 7 cImmediateVars := arrayCreate(nTransitions, defaultBoolVar);
1474 i := 0;
1475
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 7 times.
21 for exp in cExps loop
1476 14 i := i+1;
1477 28 cRefs := arrayUpdate(cRefs, i, qCref("c", tArrayBool, {DAE.INDEX(DAE.ICONST(i))}, preRef));
1478 28 cImmediateRefs := arrayUpdate(cImmediateRefs, i, qCref("cImmediate", tArrayBool, {DAE.INDEX(DAE.ICONST(i))}, preRef));
1479 14 cVars := arrayUpdate(cVars, i, createVarWithDefaults(arrayGet(cRefs,i), DAE.DISCRETE(), DAE.T_BOOL_DEFAULT, tDims));
1480 14 cImmediateVars := arrayUpdate(cImmediateVars, i, createVarWithStartValue(arrayGet(cImmediateRefs,i), DAE.DISCRETE(), DAE.T_BOOL_DEFAULT, DAE.BCONST(false), tDims));
1481 // TODO Binding probably needs to be turned into a proper equation. Done below
1482 // cVars := arrayUpdate(cVars, i, BackendVariable.setBindExp(arrayGet(cVars,i), SOME(exp)));
1483 14 vars := arrayGet(cVars, i) :: vars;
1484 14 vars := arrayGet(cImmediateVars, i) :: vars;
1485 end for;
1486 //input Boolean active "true if the state machine is active";
1487 7 activeRef := qCref("active", DAE.T_BOOL_DEFAULT, {}, preRef);
1488 7 activeVar := createVarWithDefaults(activeRef, DAE.DISCRETE(), DAE.T_BOOL_DEFAULT, {});
1489 vars := activeVar :: vars;
1490 //input Boolean reset "true when the state machine should be reset";
1491 7 resetRef := qCref("reset", DAE.T_BOOL_DEFAULT, {}, preRef);
1492 7 resetVar := createVarWithDefaults(resetRef, DAE.DISCRETE(), DAE.T_BOOL_DEFAULT, {});
1493 vars := resetVar :: vars;
1494 //Integer selectedState
1495 7 selectedStateRef := qCref("selectedState", DAE.T_INTEGER_DEFAULT, {}, preRef);
1496 7 selectedStateVar := createVarWithDefaults(selectedStateRef, DAE.DISCRETE(), DAE.T_INTEGER_DEFAULT, {});
1497 vars := selectedStateVar :: vars;
1498 //Boolean selectedReset
1499 7 selectedResetRef := qCref("selectedReset", DAE.T_BOOL_DEFAULT, {}, preRef);
1500 7 selectedResetVar := createVarWithDefaults(selectedResetRef, DAE.DISCRETE(), DAE.T_BOOL_DEFAULT, {});
1501 vars := selectedResetVar :: vars;
1502 // Integer fired
1503 7 firedRef := qCref("fired", DAE.T_INTEGER_DEFAULT, {}, preRef);
1504 7 firedVar := createVarWithDefaults(firedRef, DAE.DISCRETE(), DAE.T_INTEGER_DEFAULT, {});
1505 vars := firedVar :: vars;
1506 // output Integer activeState
1507 7 activeStateRef := qCref("activeState", DAE.T_INTEGER_DEFAULT, {}, preRef);
1508 7 activeStateVar := createVarWithDefaults(activeStateRef, DAE.DISCRETE(), DAE.T_INTEGER_DEFAULT, {});
1509 vars := activeStateVar :: vars;
1510 // output Boolean activeReset
1511 7 activeResetRef := qCref("activeReset", DAE.T_BOOL_DEFAULT, {}, preRef);
1512 7 activeResetVar := createVarWithDefaults(activeResetRef, DAE.DISCRETE(), DAE.T_BOOL_DEFAULT, {});
1513 vars := activeResetVar :: vars;
1514 // Integer nextState
1515 7 nextStateRef := qCref("nextState", DAE.T_INTEGER_DEFAULT, {}, preRef);
1516 7 nextStateVar := createVarWithStartValue(nextStateRef, DAE.DISCRETE(), DAE.T_INTEGER_DEFAULT, DAE.ICONST(0), {}); // is state -> start value, but value not specified in spec
1517 vars := nextStateVar :: vars;
1518 // Boolean nextReset
1519 7 nextResetRef := qCref("nextReset", DAE.T_BOOL_DEFAULT, {}, preRef);
1520 7 nextResetVar := createVarWithStartValue(nextResetRef, DAE.DISCRETE(), DAE.T_BOOL_DEFAULT, DAE.BCONST(false), {}); // is state -> start value, but not value specified in spec
1521 vars := nextResetVar :: vars;
1522 // ***** arrays with size nStates *****
1523 7 nStatesDims := {DAE.DIM_INTEGER(nStates)};
1524 7 nStatesArrayBool := DAE.T_ARRAY(DAE.T_BOOL_DEFAULT,nStatesDims);
1525 //output Boolean activeResetStates[nStates]
1526 7 activeResetStatesRefs := arrayCreate(nStates, ComponentReference.makeDummyCref());
1527 7 activeResetStatesVars := arrayCreate(nStates, defaultBoolVar);
1528
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
24 for i in 1:nStates loop
1529 34 activeResetStatesRefs := arrayUpdate(activeResetStatesRefs, i, qCref("activeResetStates", nStatesArrayBool, {DAE.INDEX(DAE.ICONST(i))}, preRef));
1530 17 activeResetStatesVars := arrayUpdate(activeResetStatesVars, i, createVarWithDefaults(arrayGet(activeResetStatesRefs,i), DAE.DISCRETE(), DAE.T_BOOL_DEFAULT, nStatesDims));
1531 17 vars := arrayGet(activeResetStatesVars, i) :: vars;
1532 end for;
1533 // Boolean nextResetStates[nStates]
1534 7 nextResetStatesRefs := arrayCreate(nStates, ComponentReference.makeDummyCref());
1535 7 nextResetStatesVars := arrayCreate(nStates, defaultBoolVar);
1536
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
24 for i in 1:nStates loop
1537 34 nextResetStatesRefs := arrayUpdate(nextResetStatesRefs, i, qCref("nextResetStates", nStatesArrayBool, {DAE.INDEX(DAE.ICONST(i))}, preRef));
1538 17 nextResetStatesVars := arrayUpdate(nextResetStatesVars, i, createVarWithStartValue(arrayGet(nextResetStatesRefs,i), DAE.DISCRETE(), DAE.T_BOOL_DEFAULT, DAE.BCONST(false), nStatesDims));
1539 17 vars := arrayGet(nextResetStatesVars, i) :: vars;
1540 end for;
1541 // Boolean finalStates[nStates]
1542 7 finalStatesRefs := arrayCreate(nStates, ComponentReference.makeDummyCref());
1543 7 finalStatesVars := arrayCreate(nStates, defaultBoolVar);
1544
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
24 for i in 1:nStates loop
1545 34 finalStatesRefs := arrayUpdate(finalStatesRefs, i, qCref("finalStates", nStatesArrayBool, {DAE.INDEX(DAE.ICONST(i))}, preRef));
1546 17 finalStatesVars := arrayUpdate(finalStatesVars, i, createVarWithDefaults(arrayGet(finalStatesRefs,i), DAE.DISCRETE(), DAE.T_BOOL_DEFAULT, nStatesDims));
1547 17 vars := arrayGet(finalStatesVars, i) :: vars;
1548 end for;
1549 // Boolean stateMachineInFinalState
1550 7 stateMachineInFinalStateRef := qCref("stateMachineInFinalState", DAE.T_BOOL_DEFAULT, {}, preRef);
1551 7 stateMachineInFinalStateVar := createVarWithDefaults(stateMachineInFinalStateRef, DAE.DISCRETE(), DAE.T_BOOL_DEFAULT, {});
1552 vars := stateMachineInFinalStateVar :: vars;
1553
1554 // ***** Create new governing equations *****
1555 eqs := {};
1556
1557 //input Boolean c[size(t,1)] "Transition conditions sorted in priority";
1558 // Delayed transitions are realized by "c[i] = previous(cImmediate[i])"
1559 i := 0;
1560
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 7 times.
21 for cExp in cExps loop
1561 14 i := i+1;
1562 14 exp := DAE.CREF(arrayGet(cImmediateRefs,i), DAE.T_BOOL_DEFAULT);
1563 14 eqs := DAE.EQUATION(exp, cExp, DAE.emptyElementSource) :: eqs;
1564 14 exp1 := DAE.CREF(arrayGet(cRefs,i), DAE.T_BOOL_DEFAULT);
1565
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
14 DAE.VAR(binding=bindExp) := arrayGet(tImmediateVars,i);
1566 // Check whether it is an immediate or an delayed transition
1567
1/2
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
28 rhs := if Util.applyOptionOrDefault(bindExp, function ExpressionBasics.expEqual(inExp1=DAE.BCONST(true)), false) then
1568 // immediate transition
1569 exp else
1570 // delayed transition
1571 DAE.CALL(Absyn.IDENT("previous"), {exp}, DAE.callAttrBuiltinImpureBool);
1572 14 eqs := DAE.EQUATION(exp1, rhs, DAE.emptyElementSource) :: eqs;
1573 end for;
1574
1575 // Integer selectedState = if reset then 1 else previous(nextState);
1576 7 exp := DAE.CREF(selectedStateRef, DAE.T_INTEGER_DEFAULT);
1577 7 expCond := DAE.CREF(resetRef, DAE.T_BOOL_DEFAULT);
1578 expThen := DAE.ICONST(1);
1579 14 expElse := DAE.CALL(Absyn.IDENT("previous"), {DAE.CREF(nextStateRef, DAE.T_INTEGER_DEFAULT)}, DAE.callAttrBuiltinImpureInteger);
1580 7 rhs := DAE.IFEXP(expCond, expThen, expElse);
1581 7 selectedStateEqn := DAE.EQUATION(exp, rhs, DAE.emptyElementSource);
1582 eqs := selectedStateEqn :: eqs;
1583
1584 // Boolean selectedReset = if reset then true else previous(nextReset);
1585 7 exp := DAE.CREF(selectedResetRef, DAE.T_BOOL_DEFAULT);
1586 7 expCond := DAE.CREF(resetRef, DAE.T_BOOL_DEFAULT);
1587 expThen := DAE.BCONST(true);
1588 14 expElse := DAE.CALL(Absyn.IDENT("previous"), {DAE.CREF(nextResetRef, DAE.T_BOOL_DEFAULT)}, DAE.callAttrBuiltinImpureBool);
1589 7 rhs := DAE.IFEXP(expCond, expThen, expElse);
1590 7 selectedResetEqn := DAE.EQUATION(exp, rhs, DAE.emptyElementSource);
1591 eqs := selectedResetEqn :: eqs;
1592
1593 /* Following semantic activation equations are specified in MLS 17.3.4:
1594 Integer delayed= max(if (if not t[i].immediate and t[i].from == nextState then c[i] else false) then i else 0 for i in 1:size(t,1));
1595 Integer immediate = max(if (if t[i].immediate and t[i].from == selectedState then c[i] else false) then i else 0 for i in 1:size(t,1));
1596 Integer fired = max(previous(delayed), immediate);
1597 This implementation doesn't implement them directly.
1598 Recall that delayed transitions have been previously modeled as c[i] = previous(cImmediate[i]), so that the firing conditions is simplified to:
1599 Integer fired = max(if (if t[i].from == selectedState then c[i] else false) then i else 0 for i in 1: size(t ,1)); */
1600 7 exp := DAE.CREF(firedRef, DAE.T_INTEGER_DEFAULT);
1601 expLst := {};
1602
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
21 for i in 1:nTransitions loop
1603 // t[i].from == selectedState:
1604 14 expCond := DAE.RELATION(DAE.CREF(arrayGet(tFromRefs,i), DAE.T_INTEGER_DEFAULT), DAE.EQUAL(DAE.T_INTEGER_DEFAULT), DAE.CREF(selectedStateRef, DAE.T_INTEGER_DEFAULT),-1, NONE());
1605 14 expThen := DAE.CREF(arrayGet(cRefs,i), DAE.T_BOOL_DEFAULT);
1606 expElse := DAE.BCONST(false);
1607 // if (t[i].from == selectedState) then (c[i]) else (false)
1608 14 expIf := DAE.IFEXP(expCond, expThen, expElse);
1609 // if (if t[i].from == selectedState then c[i] else false) then i else 0
1610 14 expLst := DAE.IFEXP(expIf, DAE.ICONST(i), DAE.ICONST(0)) :: expLst;
1611 end for;
1612
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 3 times.
11 rhs := if listLength(expLst) > 1 then DAE.CALL(Absyn.IDENT("max"), {Expression.makeScalarArray(expLst, DAE.T_INTEGER_DEFAULT)}, DAE.callAttrBuiltinInteger)
1613 else listHead(expLst); // runtime can't handle 'max({x})'. Hence, replace 'max({x})' by 'x'.
1614 7 firedEqn := DAE.EQUATION(exp, rhs, DAE.emptyElementSource);
1615 eqs := firedEqn :: eqs;
1616
1617 // output Integer activeState = if reset then 1 elseif fired > 0 then t[fired].to else selectedState;
1618 7 exp := DAE.CREF(activeStateRef, DAE.T_INTEGER_DEFAULT);
1619 7 expCond := DAE.CREF(resetRef, DAE.T_BOOL_DEFAULT);
1620 expThen := DAE.ICONST(1);
1621 // fired > 0:
1622 7 exp1 := DAE.RELATION(DAE.CREF(firedRef, DAE.T_INTEGER_DEFAULT), DAE.GREATER(DAE.T_INTEGER_DEFAULT), DAE.ICONST(0), -1, NONE());
1623 // t[fired].to:
1624 14 exp2 := DAE.CREF(qCref("tTo", tArrayInteger, {DAE.INDEX(DAE.CREF(firedRef,DAE.T_INTEGER_DEFAULT))}, preRef), DAE.T_INTEGER_DEFAULT);
1625 // elsif fired > 0 then t[fired].to else selectedState:
1626 7 expElse := DAE.IFEXP(exp1, exp2, DAE.CREF(selectedStateRef, DAE.T_INTEGER_DEFAULT));
1627 7 rhs := DAE.IFEXP(expCond, expThen, expElse);
1628 7 activeStateEqn := DAE.EQUATION(exp, rhs, DAE.emptyElementSource);
1629 eqs := activeStateEqn :: eqs;
1630
1631 // output Boolean activeReset = if reset then true elseif fired > 0 then t[fired].reset else selectedReset;
1632 7 exp := DAE.CREF(activeResetRef, DAE.T_BOOL_DEFAULT);
1633 7 expCond := DAE.CREF(resetRef, DAE.T_BOOL_DEFAULT);
1634 expThen := DAE.BCONST(true);
1635 // fired > 0:
1636 7 exp1 := DAE.RELATION(DAE.CREF(firedRef, DAE.T_INTEGER_DEFAULT), DAE.GREATER(DAE.T_INTEGER_DEFAULT), DAE.ICONST(0), -1, NONE());
1637 // t[fired].reset:
1638 14 exp2 := DAE.CREF(qCref("tReset", tArrayBool, {DAE.INDEX(DAE.CREF(firedRef,DAE.T_INTEGER_DEFAULT))}, preRef), DAE.T_INTEGER_DEFAULT);
1639 // elseif fired > 0 then t[fired].reset else selectedReset:
1640 7 expElse := DAE.IFEXP(exp1, exp2, DAE.CREF(selectedResetRef, DAE.T_BOOL_DEFAULT));
1641 7 rhs := DAE.IFEXP(expCond, expThen, expElse);
1642 7 activeResetEqn := DAE.EQUATION(exp, rhs, DAE.emptyElementSource);
1643 eqs := activeResetEqn :: eqs;
1644
1645 // Integer nextState = if active then activeState else previous(nextState);
1646 7 exp := DAE.CREF(nextStateRef, DAE.T_INTEGER_DEFAULT);
1647 7 expCond := DAE.CREF(activeRef, DAE.T_BOOL_DEFAULT);
1648 7 expThen := DAE.CREF(activeStateRef, DAE.T_INTEGER_DEFAULT);
1649 14 expElse := DAE.CALL(Absyn.IDENT("previous"), {DAE.CREF(nextStateRef, DAE.T_INTEGER_DEFAULT)}, DAE.callAttrBuiltinImpureInteger);
1650 7 rhs := DAE.IFEXP(expCond, expThen, expElse);
1651 7 nextStateEqn := DAE.EQUATION(exp, rhs, DAE.emptyElementSource);
1652 eqs := nextStateEqn :: eqs;
1653
1654 // Boolean nextReset = if active then false else previous(nextReset);
1655 7 exp := DAE.CREF(nextResetRef, DAE.T_BOOL_DEFAULT);
1656 7 expCond := DAE.CREF(activeRef, DAE.T_BOOL_DEFAULT);
1657 expThen := DAE.BCONST(false);
1658 14 expElse := DAE.CALL(Absyn.IDENT("previous"), {DAE.CREF(nextResetRef, DAE.T_BOOL_DEFAULT)}, DAE.callAttrBuiltinImpureBool);
1659 7 rhs := DAE.IFEXP(expCond, expThen, expElse);
1660 7 nextResetEqn := DAE.EQUATION(exp, rhs, DAE.emptyElementSource);
1661 eqs := nextResetEqn :: eqs;
1662
1663 // output Boolean activeResetStates[nStates] = {if reset then true else previous(nextResetStates[i]) for i in 1:nStates};
1664
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
24 for i in 1:nStates loop
1665 17 exp := DAE.CREF(arrayGet(activeResetStatesRefs,i), DAE.T_BOOL_DEFAULT);
1666 17 expCond := DAE.CREF(resetRef, DAE.T_BOOL_DEFAULT);
1667 expThen := DAE.BCONST(true);
1668 34 expElse := DAE.CALL(Absyn.IDENT("previous"), {DAE.CREF(arrayGet(nextResetStatesRefs,i), DAE.T_BOOL_DEFAULT)}, DAE.callAttrBuiltinImpureBool);
1669 17 rhs := DAE.IFEXP(expCond, expThen, expElse);
1670 17 eqs := DAE.EQUATION(exp, rhs, DAE.emptyElementSource) :: eqs;
1671 end for;
1672
1673 // Boolean nextResetStates[nStates] = if active then {if selectedState == i then false else activeResetStates[i] for i in 1:nStates} else previous(nextResetStates);
1674 // 2017-10-10 BTH NOTE: Replaced "selectedState" from MLS v3.3r1 by "activeState"!!!
1675
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
24 for i in 1:nStates loop
1676 17 exp := DAE.CREF(arrayGet(nextResetStatesRefs,i), DAE.T_BOOL_DEFAULT);
1677 17 expCond := DAE.CREF(activeRef, DAE.T_BOOL_DEFAULT);
1678 /*===== MLS v3.3r1 specification semantics (probably wrong!): ===== */
1679 // selectedState == i:
1680 //exp1 := DAE.RELATION(DAE.CREF(selectedStateRef, DAE.T_INTEGER_DEFAULT), DAE.EQUAL(DAE.T_INTEGER_DEFAULT), DAE.ICONST(i),-1, NONE());
1681 // if (selectedState == i) then false else activeResetStates[i]
1682 //expThen := DAE.IFEXP(exp1, DAE.BCONST(false), DAE.CREF(arrayGet(activeResetStatesRefs,i), DAE.T_BOOL_DEFAULT));
1683 /*===== FIXED semantics: ===== */
1684 // activeState == i:
1685 17 exp1 := DAE.RELATION(DAE.CREF(activeStateRef, DAE.T_INTEGER_DEFAULT), DAE.EQUAL(DAE.T_INTEGER_DEFAULT), DAE.ICONST(i),-1, NONE());
1686 // if (activeState == i) then false else activeResetStates[i]
1687 17 expThen := DAE.IFEXP(exp1, DAE.BCONST(false), DAE.CREF(arrayGet(activeResetStatesRefs,i), DAE.T_BOOL_DEFAULT));
1688 /*========== */
1689 // previous(nextResetStates[i])
1690 34 expElse := DAE.CALL(Absyn.IDENT("previous"), {DAE.CREF(arrayGet(nextResetStatesRefs,i), DAE.T_BOOL_DEFAULT)}, DAE.callAttrBuiltinImpureBool);
1691 // if active then (if selectedState == i then false else activeResetStates[i]) else previous(nextResetStates[i])
1692 17 rhs := DAE.IFEXP(expCond, expThen, expElse);
1693 // Ignore:
1694 //rhs := DAE.LUNARY(DAE.NOT(DAE.T_BOOL_DEFAULT), DAE.CALL(Absyn.IDENT("previous"), {DAE.CREF(arrayGet(nextResetStatesRefs,i), DAE.T_BOOL_DEFAULT)}, DAE.callAttrBuiltinImpureBool));
1695 17 eqs := DAE.EQUATION(exp, rhs, DAE.emptyElementSource) :: eqs;
1696 end for;
1697
1698 // Boolean finalStates[nStates] = {max(if t[j].from == i then 1 else 0 for j in 1:size(t,1)) == 0 for i in 1:nStates};
1699
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
24 for i in 1:nStates loop
1700 17 exp := DAE.CREF(arrayGet(finalStatesRefs,i), DAE.T_BOOL_DEFAULT);
1701 expLst := {};
1702
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
56 for j in 1:nTransitions loop
1703 // t[j].from == i:
1704 39 expCond := DAE.RELATION(DAE.CREF(arrayGet(tFromRefs,j), DAE.T_INTEGER_DEFAULT), DAE.EQUAL(DAE.T_INTEGER_DEFAULT), DAE.ICONST(i),-1, NONE());
1705 // if t[j].from == i then 1 else 0:
1706 39 expLst := DAE.IFEXP(expCond, DAE.ICONST(1), DAE.ICONST(0)) :: expLst;
1707 end for;
1708 // max(if t[j].from == i then 1 else 0 for j in 1:size(t,1))
1709
2/2
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 6 times.
28 exp1 := if listLength(expLst) > 1 then DAE.CALL(Absyn.IDENT("max"), {Expression.makeScalarArray(expLst, DAE.T_INTEGER_DEFAULT)}, DAE.callAttrBuiltinInteger)
1710 else listHead(expLst); // runtime can't handle 'max({x})'. Hence, replace 'max({x})' by 'x'.
1711 // max(if t[j].from == i then 1 else 0 for j in 1:size(t,1)) == 0
1712 17 rhs := DAE.RELATION(exp1, DAE.EQUAL(DAE.T_INTEGER_DEFAULT), DAE.ICONST(0),-1, NONE());
1713 17 eqs := DAE.EQUATION(exp, rhs, DAE.emptyElementSource) :: eqs;
1714 end for;
1715
1716 // Boolean stateMachineInFinalState = finalStates[activeState];
1717 7 exp := DAE.CREF(stateMachineInFinalStateRef, DAE.T_BOOL_DEFAULT);
1718 14 rhs := DAE.CREF(qCref("finalStates", nStatesArrayBool, {DAE.INDEX(DAE.CREF(activeStateRef,DAE.T_INTEGER_DEFAULT))}, preRef), DAE.T_BOOL_DEFAULT);
1719 7 eqs := DAE.EQUATION(exp, rhs, DAE.emptyElementSource) :: eqs;
1720
1721 // Now return the semantics equations of the flat state machine and associated variables and parameters
1722 7 flatSmSemantics := FLAT_SM_SEMANTICS(ident, listArray(q), t, cExps, vars, knowns, eqs, {}, {}, NONE());
1723 end basicFlatSmSemantics;
1724
1725 protected function qCref "
1726 Author: BTH
1727 Helper function to basicFlatSmSemantics"
1728 input DAE.Ident ident;
1729 input DAE.Type identType "type of the identifier, without considering the subscripts";
1730 input list<DAE.Subscript> subscriptLst;
1731 input DAE.ComponentRef componentRef;
1732 output DAE.ComponentRef outQual;
1733 algorithm
1734 468 outQual := ComponentReference.joinCrefs(componentRef,DAE.CREF_IDENT(ident,identType,subscriptLst));
1735 end qCref;
1736
1737 protected function createVarWithDefaults "
1738 Author: BTH
1739 Create a DAE.VAR with some defaults"
1740 input DAE.ComponentRef componentRef;
1741 input DAE.VarKind kind;
1742 input DAE.Type ty;
1743 input DAE.InstDims dims;
1744 output DAE.Element var;
1745 algorithm
1746 269 var := DAE.VAR(componentRef, kind, DAE.BIDIR(), DAE.NON_PARALLEL(), DAE.PUBLIC(), ty, NONE(), dims,
1747 DAE.NON_CONNECTOR(), DAE.emptyElementSource, NONE() /* VariableAttributes */, NONE(), Absyn.NOT_INNER_OUTER(), false);
1748 end createVarWithDefaults;
1749
1750 protected function createVarWithStartValue "
1751 Author: BTH
1752 Create a DAE.VAR with fixed start value and some defaults"
1753 input DAE.ComponentRef componentRef;
1754 input DAE.VarKind kind;
1755 input DAE.Type ty;
1756 input DAE.Exp startExp;
1757 input DAE.InstDims dims;
1758 output DAE.Element outVar;
1759 protected
1760 DAE.Element var;
1761 algorithm
1762 62 var := DAE.VAR(componentRef, kind, DAE.BIDIR(), DAE.NON_PARALLEL(), DAE.PUBLIC(), ty, NONE(), dims,
1763 DAE.NON_CONNECTOR(), DAE.emptyElementSource, NONE() /* VariableAttributes */, NONE(), Absyn.NOT_INNER_OUTER(), false);
1764 62 outVar := setVarFixedStartValue(var, startExp);
1765 end createVarWithStartValue;
1766
1767 protected function createTandC "
1768 Author: BTH
1769 Helper function to basicFlatSmSemantics"
1770 input list<DAE.Element> inSMComps;
1771 input list<DAE.Element> inTransitions;
1772 output list<Transition> t;
1773 output list<DAE.Exp> c;
1774 protected
1775 list<Transition> transitions;
1776 algorithm
1777 7 transitions := List.map1(inTransitions, createTransition, inSMComps);
1778 //print("\nStateMachineFlatten.createTandC: UNSORTED:\n"+ stringDelimitList(List.map(transitions,dumpTransitionStr), "\n"));
1779
1780 // sort transtion according to priority
1781 7 t := List.sort(transitions, priorityLt);
1782 //print("\nStateMachineFlatten.createTandC: SORTED:\n"+ stringDelimitList(List.map(t,dumpTransitionStr), "\n"));
1783
1784 // TODO Check that if several transitions could fire from the same state, all transitions have different priorities
1785
1786 // extract condtions from ordered transitions
1787 7 c := List.map(t, extractCondtionFromTransition);
1788 end createTandC;
1789
1790 protected function extractCondtionFromTransition
1791 input Transition trans;
1792 output DAE.Exp condition;
1793 algorithm
1794 14 TRANSITION(condition=condition) := trans;
1795 end extractCondtionFromTransition;
1796
1797 protected function priorityLt "
1798 Compare priority of transitions
1799 "
1800 input Transition inTrans1;
1801 input Transition inTrans2;
1802 output Boolean res;
1803 protected
1804 Integer priority1, priority2;
1805 algorithm
1806 9 TRANSITION(priority=priority1) := inTrans1;
1807 9 TRANSITION(priority=priority2) := inTrans2;
1808 9 res := intLt(priority1, priority2);
1809 end priorityLt;
1810
1811 protected function createTransition "
1812 Author: BTH
1813 Helper function to flatSmToDataFlow
1814 "
1815 input DAE.Element transitionElem;
1816 input list<DAE.Element> states;
1817 output Transition trans;
1818 protected
1819 DAE.ComponentRef crefFrom, crefTo;
1820 // Transition
1821 Integer from;
1822 Integer to;
1823 DAE.Exp condition;
1824 Boolean immediate = true;
1825 Boolean reset = true;
1826 Boolean synchronize = false;
1827 Integer priority = 1;
1828 algorithm
1829
1830
19/38
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 14 times.
✓ Branch 9 taken 14 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 14 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 14 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 14 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 14 times.
✗ Branch 23 not taken.
✓ Branch 24 taken 14 times.
✗ Branch 26 not taken.
✓ Branch 27 taken 14 times.
✗ Branch 29 not taken.
✓ Branch 30 taken 14 times.
✗ Branch 32 not taken.
✓ Branch 33 taken 14 times.
✗ Branch 35 not taken.
✓ Branch 36 taken 14 times.
✗ Branch 38 not taken.
✓ Branch 39 taken 14 times.
✗ Branch 41 not taken.
✓ Branch 42 taken 14 times.
✗ Branch 44 not taken.
✓ Branch 45 taken 14 times.
✗ Branch 47 not taken.
✓ Branch 48 taken 14 times.
✗ Branch 50 not taken.
✓ Branch 51 taken 14 times.
✗ Branch 53 not taken.
✓ Branch 54 taken 14 times.
14 DAE.NORETCALL(exp=DAE.CALL(path=Absyn.IDENT("transition"),
1831 expLst=DAE.CREF(componentRef=crefFrom)::DAE.CREF(componentRef=crefTo)::condition::
1832 DAE.BCONST(immediate) :: DAE.BCONST(reset) :: DAE.BCONST(synchronize)
1833 :: DAE.ICONST(priority)::{})) := transitionElem;
1834
1835 14 from := List.position1OnTrue(states, sMCompEqualsRef, crefFrom);
1836 14 to := List.position1OnTrue(states, sMCompEqualsRef, crefTo);
1837
1838
3/6
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
42 trans := TRANSITION(from, to, condition, immediate, reset, synchronize, priority);
1839 end createTransition;
1840
1841 protected function isFlatSm "
1842 Author: BTH
1843 Check if element is a FLAT_SM.
1844 "
1845 input DAE.Element inElement;
1846 output Boolean outResult;
1847 algorithm
1848 outResult := match inElement
1849 case DAE.FLAT_SM() then true;
1850 else false;
1851 end match;
1852 end isFlatSm;
1853
1854 protected function isSMComp "
1855 Author: BTH
1856 Check if element is a SM_COMP.
1857 "
1858 input DAE.Element inElement;
1859 output Boolean outResult;
1860 algorithm
1861 outResult := match inElement
1862 case DAE.SM_COMP() then true;
1863 else false;
1864 end match;
1865 end isSMComp;
1866
1867
1868 protected function isTransition "
1869 Author: BTH
1870 Return true if element is a transition, otherwise false"
1871 input DAE.Element inElement;
1872 output Boolean result;
1873 algorithm
1874 result := match inElement
1875 case DAE.NORETCALL(exp=DAE.CALL(path=Absyn.IDENT("transition"))) then true;
1876 else false;
1877 end match;
1878 end isTransition;
1879
1880 protected function isInitialState "
1881 Author: BTH
1882 Return true if element is an initialState, otherwise false"
1883 input DAE.Element inElement;
1884 output Boolean result;
1885 algorithm
1886 result := match inElement
1887 case DAE.NORETCALL(exp=DAE.CALL(path=Absyn.IDENT("initialState"))) then true;
1888 else false;
1889 end match;
1890 end isInitialState;
1891
1892 protected function isEquation "
1893 Author: BTH
1894 Return true if element is an EQUATION, otherwise false"
1895 input DAE.Element inElement;
1896 output Boolean result;
1897 algorithm
1898 result := match inElement
1899 case DAE.EQUATION() then true;
1900 else false;
1901 end match;
1902 end isEquation;
1903
1904 protected function isEquationOrWhenEquation "
1905 Author: BTH
1906 Return true if element is an EQUATION or WHEN_EQUATION, otherwise false"
1907 input DAE.Element inElement;
1908 output Boolean result;
1909 algorithm
1910 result := match inElement
1911 case DAE.EQUATION() then true;
1912 case DAE.WHEN_EQUATION() then true;
1913 else false;
1914 end match;
1915 end isEquationOrWhenEquation;
1916
1917 protected function isPreOrPreviousEquation "
1918 Author: BTH
1919 Return true if element is an EQUATION with at least one pre(..) or previous(..) expression, otherwise false"
1920 input DAE.Element inElement;
1921 output Boolean result;
1922 algorithm
1923 result := match inElement
1924 local
1925 DAE.Exp exp;
1926 DAE.Exp scalar;
1927 ✗ case DAE.EQUATION(exp, scalar, _) then
1928 Expression.expHasPre(exp) or Expression.expHasPre(scalar) or
1929 Expression.expHasPrevious(exp) or Expression.expHasPrevious(scalar);
1930 else then false;
1931 end match;
1932 end isPreOrPreviousEquation;
1933
1934 protected function isVar "
1935 Author: BTH
1936 Return true if element is an VAR, otherwise false"
1937 input DAE.Element inElement;
1938 output Boolean result;
1939 algorithm
1940 result := match inElement
1941 case DAE.VAR() then true;
1942 else false;
1943 end match;
1944 end isVar;
1945
1946 protected function sMCompEqualsRef "
1947 Author: BTH
1948 Return true if the componentRef of the second argument equals the componentRef of the SMComp (first argument)
1949 "
1950 input DAE.Element inElement;
1951 input DAE.ComponentRef inCref;
1952 output Boolean result;
1953 algorithm
1954 result := match inElement
1955 local
1956 DAE.ComponentRef cref;
1957 case DAE.SM_COMP(cref) guard ComponentReferenceBasics.crefEqual(cref, inCref) then true;
1958 else false;
1959 end match;
1960 end sMCompEqualsRef;
1961
1962 public function dumpTransitionStr "
1963 Author: BTH
1964 Dump transition to string."
1965 input Transition transition;
1966 output String transitionStr;
1967 protected
1968 Integer from;
1969 Integer to;
1970 DAE.Exp condition;
1971 Boolean immediate;
1972 Boolean reset;
1973 Boolean synchronize;
1974 Integer priority;
1975 algorithm
1976 ✗ TRANSITION(from, to, condition, immediate, reset, synchronize, priority) := transition;
1977 ✗ transitionStr := "TRANSITION(from="+intString(from)+", to="+intString(to)+
1978 ", condition="+ExpressionBasics.printExpStr(condition)+
1979 ", immediate="+boolString(immediate)+", reset="+boolString(reset)+
1980 ", synchronize="+boolString(synchronize)+", priority="+intString(priority)+")";
1981 end dumpTransitionStr;
1982
1983 protected function wrapHack "
1984 Author: BTH
1985 Wrap equations in when-clauses as long as Synchronous Features are not supported"
1986 input FCore.Cache cache;
1987 input list<DAE.Element> inElementLst;
1988 output list<DAE.Element> outElementLst;
1989 protected
1990 list<DAE.Element> eqnLst, otherLst;
1991 DAE.Element whenEq;
1992 DAE.Exp cond1, cond2, condition;
1993 list<DAE.Exp> condLst;
1994 DAE.Type tArrayBool;
1995 algorithm
1996
1997 // == {initial(), sample(DEFAULT_CLOCK_PERIOD, DEFAULT_CLOCK_PERIOD)} ==
1998 cond1 := DAE.CALL(Absyn.IDENT("initial"),
1999 {}, DAE.callAttrBuiltinImpureBool);
2000 ✗ cond2 := DAE.CALL(Absyn.IDENT("sample"),
2001 {DAE.RCONST(Flags.getConfigReal(Flags.DEFAULT_CLOCK_PERIOD)),
2002 DAE.RCONST(Flags.getConfigReal(Flags.DEFAULT_CLOCK_PERIOD))}, DAE.callAttrBuiltinImpureBool);
2003 tArrayBool := DAE.T_ARRAY(DAE.T_BOOL_DEFAULT,{DAE.DIM_INTEGER(2)});
2004
2005 ✗ if Flags.getConfigBool(Flags.CT_STATE_MACHINES) then
2006 // Extract transition conditions
2007 ✗ condLst := List.filterMap1(inElementLst, extractSmOfExps, "cImmediate");
2008 ✗ (eqnLst, otherLst) := List.extractOnTrue(inElementLst, isPreOrPreviousEquation);
2009 ✗ condition := DAE.ARRAY(tArrayBool, true, cond1 :: condLst);
2010 else
2011 ✗ (eqnLst, otherLst) := List.extractOnTrue(inElementLst, isEquation);
2012 ✗ condition := DAE.ARRAY(tArrayBool, true, {cond1, cond2});
2013 end if;
2014
2015 // when {initial(), sample(DEFAULT_CLOCK_PERIOD, DEFAULT_CLOCK_PERIOD)} then .. end when;
2016 ✗ whenEq := DAE.WHEN_EQUATION(condition,
2017 eqnLst, NONE(), DAE.emptyElementSource);
2018
2019 ✗ outElementLst := listAppend(otherLst, {whenEq});
2020 end wrapHack;
2021
2022 protected function extractSmOfExps "
2023 Hack for extracting DAE.CREFs from flat state machine semantics equations.
2024 "
2025 input DAE.Element inElem;
2026 input DAE.Ident inLastIdent;
2027 output DAE.Exp outExp;
2028 algorithm
2029 outExp := match inElem
2030 local
2031 DAE.Exp exp;
2032 DAE.ComponentRef cref;
2033 DAE.Ident firstIdent;
2034 DAE.Ident lastIdent;
2035 case DAE.EQUATION(exp=exp)
2036 algorithm
2037 ✗ DAE.CREF(componentRef=cref) := exp;
2038 ✗ firstIdent := ComponentReferenceBasics.crefFirstIdent(cref);
2039 ✗ true := firstIdent == "smOf";
2040 ✗ lastIdent := ComponentReferenceBasics.crefLastIdent(cref);
2041 ✗ true := lastIdent == inLastIdent;
2042 then exp;
2043 end match;
2044 end extractSmOfExps;
2045
2046
2047 protected function traversingSubsPreForPrevious "
2048 Author: BTH
2049 Helper function to traverse subexpressions
2050 Substitutes 'previous(x)' by 'pre(x)' "
2051 input DAE.Exp inExp;
2052 input Integer inHitCount;
2053 output DAE.Exp outExp;
2054 output Integer outHitCount;
2055 algorithm
2056 (outExp,outHitCount) := match inExp
2057 local
2058 list<DAE.Exp> expLst;
2059 DAE.CallAttributes attr;
2060 case DAE.CALL(Absyn.IDENT("previous"), expLst, attr)
2061 ✗ then (DAE.CALL(Absyn.IDENT("pre"), expLst, attr), inHitCount + 1);
2062 else (inExp,inHitCount);
2063 end match;
2064 end traversingSubsPreForPrevious;
2065
2066 protected function traversingSubsXForSampleX "
2067 Author: BTH
2068 Helper function to traverse subexpressions
2069 Substitutes 'sample(x, _)' by 'x' "
2070 input DAE.Exp inExp;
2071 input Integer inHitCount;
2072 output DAE.Exp outExp;
2073 output Integer outHitCount;
2074 algorithm
2075 (outExp,outHitCount) := match inExp
2076 local
2077 DAE.Exp expX;
2078 case DAE.CALL(Absyn.IDENT("sample"),
2079 { expX,
2080 DAE.CLKCONST(DAE.INFERRED_CLOCK())},
2081 _)
2082 ✗ then (expX, inHitCount + 1);
2083 else (inExp,inHitCount);
2084 end match;
2085 end traversingSubsXForSampleX;
2086
2087 annotation(__OpenModelica_Interface="frontend");
2088 end StateMachineFlatten;
2089