-
Notifications
You must be signed in to change notification settings - Fork 290
/
StateMachineFlatten.mo
2089 lines (1885 loc) · 99.2 KB
/
StateMachineFlatten.mo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* This file is part of OpenModelica.
*
* Copyright (c) 1998-2015, Open Source Modelica Consortium (OSMC),
* c/o Linköpings universitet, Department of Computer and Information Science,
* SE-58183 Linköping, Sweden.
*
* All rights reserved.
*
* THIS PROGRAM IS PROVIDED UNDER THE TERMS OF GPL VERSION 3 LICENSE OR
* THIS OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.2.
* ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES
* RECIPIENT'S ACCEPTANCE OF THE OSMC PUBLIC LICENSE OR THE GPL VERSION 3,
* ACCORDING TO RECIPIENTS CHOICE.
*
* The OpenModelica software and the Open Source Modelica
* Consortium (OSMC) Public License (OSMC-PL) are obtained
* from OSMC, either from the above address,
* from the URLs: http://www.ida.liu.se/projects/OpenModelica or
* http://www.openmodelica.org, and in the OpenModelica distribution.
* GNU version 3 is obtained from: http://www.gnu.org/copyleft/gpl.html.
*
* This program is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH
* IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL.
*
* See the full OSMC Public License conditions for more details.
*
*/
encapsulated package StateMachineFlatten
" file: StateMachineFlatten.mo
package: StateMachineFlatten
description: Flattening of state machines
This module contains functions to transform an instantiated state machine to flat data-flow equations.
This approach is a rather direct implementation of the state machine to data-flow equations transformation
described in the specification. A more efficient implemention could avoid that transformation to
data-flow and instead keep the state machine structure in the back-end in order to generate optimized
code (in terms of memory requirements and minimized conditional statements).
"
public import Absyn;
public import DAE;
public import FCore;
protected import List;
protected import ComponentReference;
protected import ExpressionDump;
protected import DAEUtil;
protected import Util;
protected import DAEDump;
protected import Error;
protected import HashTableCrToExpOption;
protected import Flags;
protected
uniontype Transition "
Properties of a transition"
record TRANSITION
Integer from;
Integer to;
DAE.Exp condition;
Boolean immediate = true;
Boolean reset = true;
Boolean synchronize = false;
Integer priority = 1;
end TRANSITION;
end Transition;
public
uniontype FlatSmSemantics "
Structure that combines states of flat state machine in
canonical order with governing semantic equations."
record FLAT_SM_SEMANTICS
DAE.Ident ident;
array<DAE.Element> smComps "First element is the initial state";
// Flat State machine semantics (SMS)
list<Transition> t "List/Array of transition data sorted in priority";
list<DAE.Exp> c "Transition conditions sorted in priority";
list<DAE.Element> vars "SMS veriables";
list<DAE.Element> knowns "SMS constants/parameters";
list<DAE.Element> eqs "SMS equations";
// Activation and Reset propagation through hierarchy
list<DAE.Element> pvars "Propagation related variables";
list<DAE.Element> peqs "Propagation equations";
Option<DAE.ComponentRef> enclosingState "Cref to enclosing state if any"; // FIXME needed?
end FLAT_SM_SEMANTICS;
end FlatSmSemantics;
constant String SMS_PRE = "smOf" "prefix for crefs of fresh State Machine Semantics variables/knowns";
public function stateMachineToDataFlow "
Author: BTH
Transform state machines to data-flow equations
"
input FCore.Cache cache; // FIXME need to update this somewhere?
input FCore.Graph env; // FIXME need to update this somewhere?
input DAE.DAElist inDAElist;
output DAE.DAElist outDAElist;
protected
list<DAE.Element> elementLst, elementLst1, flatSmLst, otherLst, elementLst2, elementLst3;
list<Transition> t;
DAE.Element compElem;
Integer nOfSubstitutions;
// COMP
DAE.Ident ident;
list<DAE.Element> dAElist "a component with subelements, normally only used at top level.";
DAE.ElementSource source "the origin of the component/equation/algorithm";
Option<SCode.Comment> comment;
algorithm
DAE.DAE(elementLst=elementLst) := inDAElist;
assert(listLength(elementLst) == 1, "Internal compiler error: Handling of elementLst != 1 not supported\n");
DAE.COMP(ident, dAElist, source, comment) := listHead(elementLst);
if not List.exist(dAElist, isFlatSm) then
outDAElist := inDAElist;
return;
end if;
(flatSmLst, otherLst) := List.extractOnTrue(dAElist, isFlatSm);
elementLst2 := List.fold2(flatSmLst, flatSmToDataFlow, NONE(), NONE(), {});
// HACK1 Wrap semantic state machine equations in when clauses for continuous-time state machines
if Flags.getConfigBool(Flags.CT_STATE_MACHINES) then
elementLst2 := wrapHack(cache, elementLst2);
end if;
elementLst3 := listAppend(otherLst, elementLst2);
outDAElist := DAE.DAE({DAE.COMP(ident, elementLst3, source, comment)});
// print("StateMachineFlatten.stateMachineToDataFlow: outDAElist before global subs:\n" + DAEDump.dumpStr(outDAElist,FCore.getFunctionTree(cache)));
// traverse dae expressions for making substitutions activeState(x) -> x.active
(outDAElist, _, (_,nOfSubstitutions)) := DAEUtil.traverseDAE(outDAElist, FCore.getFunctionTree(cache), Expression.traverseSubexpressionsHelper, (traversingSubsActiveState, 0));
if Flags.getConfigBool(Flags.CT_STATE_MACHINES) then
// HACK2 traverse dae expressions for making substitutions previous(x) -> pre(x)
(outDAElist, _, (_,nOfSubstitutions)) := DAEUtil.traverseDAE(outDAElist, FCore.getFunctionTree(cache), Expression.traverseSubexpressionsHelper, (traversingSubsPreForPrevious, 0));
// FIXME not needed any more? HACK3 traverse dae expressions for making substitutions sample(x, _) -> x
// (outDAElist, _, (_,nOfSubstitutions)) := DAEUtil.traverseDAE(outDAElist, FCore.getFunctionTree(cache), Expression.traverseSubexpressionsHelper, (traversingSubsXForSampleX, 0));
end if;
//print("StateMachineFlatten.stateMachineToDataFlow: outDAElist:\n" + DAEDump.dumpStr(outDAElist,FCore.getFunctionTree(cache)));
end stateMachineToDataFlow;
protected function traversingSubsActiveState "
Author: BTH
Helper function to traverse subexpressions
Substitutes 'activeState(x)' by 'x.active' "
input DAE.Exp inExp;
input Integer inHitCount;
output DAE.Exp outExp;
output Integer outHitCount;
algorithm
(outExp,outHitCount) := match inExp
local
DAE.ComponentRef componentRef;
case DAE.CALL(path=Absyn.IDENT("activeState"), expLst={DAE.CREF(componentRef=componentRef)})
then (DAE.CREF(ComponentReference.crefPrependIdent(componentRef, "active", {}, DAE.T_BOOL_DEFAULT), DAE.T_BOOL_DEFAULT), inHitCount + 1);
else (inExp,inHitCount);
end match;
end traversingSubsActiveState;
protected function flatSmToDataFlow "
Author: BTH
Transform a flat state machine to data-flow equations
"
input DAE.Element inFlatSm "flat state machine that is to be transformed to data-flow equations";
input Option<DAE.ComponentRef> inEnclosingStateCrefOption "Cref of state that encloses the flat state machiene (NONE() if at top hierarchy)";
input Option<FlatSmSemantics> inEnclosingFlatSmSemanticsOption "The flat state machine semantics structure governing the enclosing state (NONE() if at top hierarchy)";
input list<DAE.Element> accElems;
output list<DAE.Element> outElems = accElems;
protected
DAE.Ident ident;
list<DAE.Element> dAElist, smCompsLst, otherLst1, transitionLst, otherLst2,
otherLst3, eqnLst, otherLst4, smCompsLst2;
DAE.Element initialStateOp, initialStateComp;
DAE.ComponentRef crefInitialState;
FlatSmSemantics flatSmSemanticsBasics, flatSmSemanticsWithPropagation, flatSmSemantics;
list<Transition> transitions;
list<DAE.Element> vars "SMS veriables";
list<DAE.Element> knowns "SMS constants/parameters";
list<DAE.Element> eqs "SMS equations";
list<DAE.Element> pvars "Propagation related variables";
list<DAE.Element> peqs "Propagation equations";
// Option<DAE.ComponentRef> enclosingState "Cref to enclosing state if any"; // FIXME needed?
algorithm
DAE.FLAT_SM(ident=ident, dAElist=dAElist) := inFlatSm;
// break Elements into different groups
(smCompsLst, otherLst1) := List.extractOnTrue(dAElist, isSMComp);
(transitionLst, otherLst2) := List.extractOnTrue(otherLst1, isTransition);
({initialStateOp}, otherLst3) := List.extractOnTrue(otherLst2, isInitialState);
(eqnLst, otherLst4) := List.extractOnTrue(otherLst3, isEquation);
assert(listLength(otherLst4) == 0, "Internal compiler error. Unexpected elements in flat state machine.");
DAE.NORETCALL(exp=DAE.CALL(path=Absyn.IDENT("initialState"), expLst={DAE.CREF(componentRef=crefInitialState)})) := initialStateOp;
({initialStateComp}, smCompsLst2) := List.extract1OnTrue(smCompsLst, sMCompEqualsRef, crefInitialState);
// Create basic semantic equations (MLS 17.3.4 Semantics Summary)
flatSmSemanticsBasics := basicFlatSmSemantics(ident, initialStateComp::smCompsLst2, transitionLst);
// Add activation and reset propagation related equations
flatSmSemanticsWithPropagation := addPropagationEquations(flatSmSemanticsBasics, inEnclosingStateCrefOption, inEnclosingFlatSmSemanticsOption);
// Elaborate on ticksInState() and timeInState() operators (MLS 17.1 Transitions)
flatSmSemantics := elabXInStateOps(flatSmSemanticsWithPropagation, inEnclosingStateCrefOption);
if Flags.getConfigBool(Flags.CT_STATE_MACHINES) then
// Allow ticksInState() in state components (BTH not needed really needed for CT, delete this stuff?)
smCompsLst := List.map(smCompsLst, elabXInStateOps_CT);
end if;
// Extract semantic equations for flat state machine and add the elements to the DAE list
FLAT_SM_SEMANTICS(vars=vars, knowns=knowns, eqs=eqs, pvars=pvars, peqs=peqs) := flatSmSemantics;
outElems := List.flatten({outElems, eqnLst, vars, knowns, eqs, pvars, peqs});
// Extract DAE.Elements from state components (and recurse into potential FLAT_SMs in the state component)
outElems := List.fold1(smCompsLst, smCompToDataFlow, flatSmSemantics, outElems);
end flatSmToDataFlow;
protected function elabXInStateOps_CT "
Author: BTH
For continuous-time state machines, support ticksInState() operators in state components
"
input DAE.Element inSmComp;
output DAE.Element outSmComp;
protected
Integer nOfHits = 0;
DAE.ComponentRef componentRef;
list<DAE.Element> dAElist1, dAElist2;
DAE.FunctionTree emptyTree;
algorithm
DAE.SM_COMP(componentRef, dAElist1) := inSmComp;
emptyTree := DAE.AvlTreePathFunction.Tree.EMPTY();
(DAE.DAE(dAElist2), _, (_,(_, nOfHits))) := DAEUtil.traverseDAE(DAE.DAE(dAElist1), emptyTree, Expression.traverseSubexpressionsHelper, (traversingSubsTicksInState, (componentRef, 0)));
outSmComp := DAE.SM_COMP(componentRef, dAElist2);
end elabXInStateOps_CT;
protected function traversingSubsTicksInState "
Author: BTH
Helper function to elabXInStateOps_CT for traversing subexpressions
Substitutes ticksInState() by enclosingStateComponent.$ticksInState '
"
input DAE.Exp inExp;
input tuple<DAE.ComponentRef, Integer> inCref_HitCount "tuple of cref of enclosing state component and substitution hit counter";
output DAE.Exp outExp;
output tuple<DAE.ComponentRef, Integer> outCref_HitCount;
protected
DAE.ComponentRef cref;
Integer hitCount;
algorithm
(cref, hitCount) := inCref_HitCount;
(outExp,outCref_HitCount) := match inExp
local
DAE.Type ty;
DAE.ComponentRef crefTicksInState;
case DAE.CALL(path=Absyn.IDENT("ticksInState"), expLst={}, attr=DAE.CALL_ATTR(ty=ty))
algorithm
crefTicksInState := ComponentReference.joinCrefs(cref, DAE.CREF_IDENT("$ticksInState", ty, {}));
then (DAE.CREF(crefTicksInState, ty), (cref, hitCount + 1));
else (inExp,inCref_HitCount);
end match;
end traversingSubsTicksInState;
protected function elabXInStateOps "
Author: BTH
Transform ticksInState() and timeInState() operators to data-flow equations
"
input FlatSmSemantics inFlatSmSemantics;
input Option<DAE.ComponentRef> inEnclosingStateCrefOption "Cref of state that encloses the flat state machiene (NONE() if at top hierarchy)";
output FlatSmSemantics outFlatSmSemantics;
protected
Integer i;
Boolean found;
DAE.Exp c2, c3, c4, conditionNew, substTickExp, substTimeExp;
DAE.ComponentRef stateRef;
Transition t2;
list<Transition> tElab = {} "Elaborated transitions";
list<DAE.Exp> cElab = {} "Elaborated conditions";
list<DAE.Element> smeqsElab = {} "Elaborated smeqs";
// FLAT_SM_SEMANTICS
DAE.Ident ident;
array<DAE.Element> smComps "First element is the initial state";
list<Transition> t "List/Array of transition data sorted in priority";
list<DAE.Exp> c "Transition conditions sorted in priority";
list<DAE.Element> smvars "SMS veriables";
list<DAE.Element> smknowns "SMS constants/parameters";
list<DAE.Element> smeqs "SMS equations";
list<DAE.Element> pvars = {} "Propagation related variables";
list<DAE.Element> peqs = {} "Propagation equations";
Option<DAE.ComponentRef> enclosingStateOption "Cref to enclosing state if any"; // FIXME needed?
// TRANSITION
Integer from;
Integer to;
DAE.Exp condition;
Boolean immediate;
Boolean reset;
Boolean synchronize;
Integer priority;
algorithm
FLAT_SM_SEMANTICS(ident, smComps, t, c, smvars, smknowns, smeqs, pvars, peqs, enclosingStateOption) := inFlatSmSemantics;
// We have some redundancy here (t[:].condition == c[:]) and thus need to update both
i := 0;
for tc in List.zip(t,c) loop
i := i + 1;
(t2, c2) := tc;
TRANSITION(from, to, condition, immediate, reset, synchronize, priority) := t2;
// Need to access decorations attached to 'from' state
DAE.SM_COMP(componentRef=stateRef) := arrayGet(smComps, from);
// == Search whether condition contains a subexpression 'ticksInState()', if so, substitute them by 'smComps[from].$ticksInState' ==
substTickExp := DAE.CREF(qCref("$ticksInState", DAE.T_INTEGER_DEFAULT, {}, stateRef), DAE.T_INTEGER_DEFAULT);
(c3, (_, _, found)) := Expression.traverseExpTopDown(c2, traversingSubsXInState, ("ticksInState", substTickExp, false));
if found and isSome(inEnclosingStateCrefOption) then
// MLS 3.3 17.1: "can only be used in transition conditions of state machines not present in states of hierarchical state machines" violated
Error.addCompilerError("Found 'ticksInState()' within a state of an hierarchical state machine.");
fail();
end if;
// if a transition was updated we also need to update the semantic equation containing that transition's logic
smeqsElab := if found then List.map5(smeqs, smeqsSubsXInState, arrayGet(smComps, 1), i, listLength(t), substTickExp, "ticksInState") else smeqs;
smeqs := smeqsElab; // use updated smeqs
// == Search whether condition contains a subexpression 'timeInState()', if so, substitute them by 'smComps[from].$timeInState' ==
substTimeExp := DAE.CREF(qCref("$timeInState", DAE.T_REAL_DEFAULT, {}, stateRef), DAE.T_REAL_DEFAULT);
(c4, (_, _, found)) := Expression.traverseExpTopDown(c2, traversingSubsXInState, ("timeInState", substTimeExp, false));
if found and isSome(inEnclosingStateCrefOption) then
// MLS 3.3 17.1: "can only be used in transition conditions of state machines not present in states of hierarchical state machines" violated
Error.addCompilerError("Found 'timeInState()' within a state of an hierarchical state machine.");
fail();
end if;
// if a transition was updated we also need to update the semantic equation containing that transition's logic
smeqsElab := if found then List.map5(smeqs, smeqsSubsXInState, arrayGet(smComps, 1), i, listLength(t), substTimeExp, "timeInState") else smeqs;
smeqs := smeqsElab; // use updated smeqs
tElab := TRANSITION(from, to, c4, immediate, reset, synchronize, priority) :: tElab;
cElab := c4 :: cElab;
end for;
outFlatSmSemantics := FLAT_SM_SEMANTICS(ident, smComps, listReverse(tElab), listReverse(cElab), smvars, smknowns, smeqsElab, pvars, peqs, enclosingStateOption);
end elabXInStateOps;
protected function smeqsSubsXInState "
Author: BTH
Helper function to elabXInStateOps.
Replace 'xInState()' in RHS of semantic equations by 'substExp', but only within the transition
condition specified by the remaining function arguments.
"
input DAE.Element inSmeqs "SMS equation";
input DAE.Element initialStateComp "Initial state component of governing flat state machine";
input Integer i "Index of transition";
input Integer nTransitions;
input DAE.Exp substExp;
input String xInState "Name of function that is to be replaced, e.g., 'timeInState', or 'tickInState'";
output DAE.Element outSmeqs "SMS equation";
protected
DAE.ComponentRef preRef, cref, lhsRef, crefInitialState;
DAE.Type tArrayBool;
DAE.ElementSource elemSource;
DAE.Exp lhsExp, rhsExp, rhsExp2;
DAE.Type ty;
algorithm
// Cref to initial state of governing flat state machine
DAE.SM_COMP(componentRef=crefInitialState) := initialStateComp;
preRef := ComponentReference.crefPrefixString(SMS_PRE, crefInitialState);
tArrayBool := DAE.T_ARRAY(DAE.T_BOOL_DEFAULT,{DAE.DIM_INTEGER(nTransitions)});
cref := qCref("cImmediate", tArrayBool, {DAE.INDEX(DAE.ICONST(i))}, preRef);
DAE.EQUATION(lhsExp, rhsExp, elemSource) := inSmeqs;
DAE.CREF(lhsRef, ty) := lhsExp;
// print("StateMachineFlatten.smeqsSubsXInState: cref: " + ComponentReference.printComponentRefStr(cref) + "\n");
// print("StateMachineFlatten.smeqsSubsXInState: lhsRef: " + ComponentReference.printComponentRefStr(lhsRef) + "\n");
if ComponentReference.crefEqual(cref, lhsRef) then
// print("StateMachineFlatten.smeqsSubsXInState: rhsExp: " + ExpressionDump.printExpStr(rhsExp) + "\n");
(rhsExp2, _) := Expression.traverseExpTopDown(rhsExp, traversingSubsXInState, (xInState, substExp, false));
// print("StateMachineFlatten.smeqsSubsXInState: rhsExp2: " + ExpressionDump.printExpStr(rhsExp2) + "\n");
else
rhsExp2 := rhsExp;
end if;
outSmeqs := DAE.EQUATION(lhsExp, rhsExp2, elemSource);
end smeqsSubsXInState;
protected function traversingSubsXInState "
Author: BTH
Helper function to elabXInStateOps and smeqsSubsXInState.
Replace 'XInState()' operators (first element of inXSubstHit) by expression given in second element of inXSubstHit tuple.
"
input DAE.Exp inExp;
input tuple<String, DAE.Exp, Boolean> inXSubstHit;
output DAE.Exp outExp;
output Boolean cont = true;
output tuple<String, DAE.Exp, Boolean> outXSubstHit;
algorithm
(outExp, outXSubstHit) := match (inExp, inXSubstHit)
local
DAE.Exp subsExp;
Boolean hit;
String xInState, name;
case (DAE.CALL(path=Absyn.IDENT(name)), (xInState, subsExp, _)) guard name == xInState
then (subsExp, (xInState, subsExp, true));
else (inExp, inXSubstHit);
end match;
end traversingSubsXInState;
protected function smCompToDataFlow "
Author: BTH
Transform state machine component to data-flow equations
"
input DAE.Element inSMComp;
input FlatSmSemantics inEnclosingFlatSmSemantics "The flat state machine semantics structure governing the state component";
input list<DAE.Element> accElems;
output list<DAE.Element> outElems = accElems;
protected
list<DAE.Element> varLst1, varLst2, assignedVarLst, stateVarLst, otherLst1, equationLst1, equationLst2, otherLst2, flatSmLst, otherLst3;
DAE.ComponentRef componentRef;
list<DAE.ComponentRef> stateVarCrefs;
list<Option<DAE.VariableAttributes>> variableAttributesOptions;
list<Option<DAE.Exp>> startValuesOpt;
list<tuple<DAE.ComponentRef, Option<DAE.Exp>>> varCrefStartVal;
list<DAE.Element> dAElist "a component with subelements";
HashTableCrToExpOption.HashTable crToExpOpt "Table that maps the cref of a variable to its start value";
algorithm
DAE.SM_COMP(componentRef=componentRef, dAElist=dAElist) := inSMComp;
(varLst1, otherLst1) := List.extractOnTrue(dAElist, isVar);
// 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
(equationLst1, otherLst2) := List.extractOnTrue(otherLst1, isEquationOrWhenEquation);
// 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.
// FIXME Maybe better to just filter out variables declared as "inputs" and assume that the rest are assigned variables?
// Retain all variables for which there exits an assignment equation
assignedVarLst := List.filterOnTrue(varLst1, function List.exist1(inList=equationLst1, inFindFunc=isVarAtLHS));
// Retain all variables which have "previous(x)" applied
stateVarLst := List.filterOnTrue(varLst1, function List.exist1(inList=equationLst1, inFindFunc=isPreviousAppliedToVar));
//print("StateMachineFlatten.smCompToDataFlow: stateVarLst:\n" + DAEDump.dumpElementsStr(stateVarLst) +"\n");
stateVarCrefs := List.map(stateVarLst, DAEUtil.varCref);
variableAttributesOptions := List.map(stateVarLst, DAEUtil.getVariableAttributes);
startValuesOpt := List.map(variableAttributesOptions, getStartAttrOption);
varCrefStartVal := List.zip(stateVarCrefs, startValuesOpt);
crToExpOpt := HashTableCrToExpOption.emptyHashTableSized(listLength(varCrefStartVal) + 1);
// create table that maps the cref of a variable to its start value
crToExpOpt := List.fold(varCrefStartVal, BaseHashTable.add, crToExpOpt);
//print("StateMachineFlatten.smCompToDataFlow: crToExpOpt:\n"); BaseHashTable.dumpHashTable(crToExpOpt);
// 1. Make equations conditional so that they are only active if enclosing state is active
// 2. Add reset equations for discrete-time states declared in the component
(equationLst2, varLst2) := List.fold3(equationLst1, addStateActivationAndReset, inSMComp, inEnclosingFlatSmSemantics, crToExpOpt, ({},{}));
(flatSmLst, otherLst3) := List.extractOnTrue(otherLst2, isFlatSm);
// append non FLAT_SM elements to accumulator
outElems := List.flatten({outElems, varLst1, varLst2, equationLst2, otherLst3});
// recurse into FLAT_SM elements (if any)
outElems := List.fold2(flatSmLst, flatSmToDataFlow, SOME(componentRef), SOME(inEnclosingFlatSmSemantics), outElems);
end smCompToDataFlow;
protected function addStateActivationAndReset "
Author: BTH
The real work is done in helper function addStateActivationAndReset1.
This top-level function just handles the recursive descent if inEqn is a DAE.WHEN_EQUATION().
"
input DAE.Element inEqn "Expects DAE.EQUATION() or DAE.WHEN_EQUATION()";
input DAE.Element inEnclosingSMComp "The state component enclosing the equation";
input FlatSmSemantics inEnclosingFlatSmSemantics "The flat state machine semantics structure governing the state component";
input HashTableCrToExpOption.HashTable crToExpOpt "Table mapping variable declaration in the enclosing state to start values";
input tuple<list<DAE.Element>,list<DAE.Element>> accEqnsVars "Tuple for accumulating equations and variable definitions";
output tuple<list<DAE.Element>,list<DAE.Element>> outEqnsVars;
protected
list<DAE.Element> equations1;
list<DAE.Element> vars1;
// WHEN_EQUATION
DAE.Exp condition;
list<DAE.Element> equations;
DAE.ElementSource source;
algorithm
outEqnsVars := match (inEqn)
case DAE.EQUATION() then addStateActivationAndReset1(inEqn, inEnclosingSMComp, inEnclosingFlatSmSemantics, crToExpOpt, accEqnsVars);
case DAE.WHEN_EQUATION(condition,equations,NONE(),source)
algorithm
(equations1,vars1) := List.fold3(equations, addStateActivationAndReset, inEnclosingSMComp, inEnclosingFlatSmSemantics, crToExpOpt, ({},{}));
then (DAE.WHEN_EQUATION(condition,equations1,NONE(),source)::Util.tuple21(accEqnsVars), listAppend(vars1,Util.tuple22(accEqnsVars)));
case DAE.WHEN_EQUATION(elsewhen_=SOME(_))
algorithm
Error.addCompilerError("Encountered elsewhen part in a when clause of a clocked state machine.\n");
then fail();
else
algorithm
Error.addCompilerError("Internal compiler error: StateMachineFlatten.addStateActivationAndReset(..) called with unexpected argument.\n");
then fail();
end match;
end addStateActivationAndReset;
protected function addStateActivationAndReset1 "
Author: BTH
The function has following purpose:
1. Make equations conditional so that they are only active if enclosing state is active
2. Add reset equations for discrete-time states declared in the component
FIXME 2017-02-17: There is problem with the approach taken in this function of transforming s.th. similar to
Real x(start=1.1);
x = previous(x) + 1
to something like
x = if stateActive then x_previous + 1 else x_previous;
x_previous = if active and (activeReset or activeResetStates[1]) then 1.1 else previous(x);
While this gives the correct reset semantics for x, one gets a wrong result for previous(x) at the reset instant:
'x_previous' is set to the correct result value, but during the reset instant in general there will be 'previous(x) != x_previous'!
The transformation below replaces all occurances of 'previous(x)' within the state's equations to 'x_previous', so that the
state machine will show the correct behavior. However, if 'x' is accessed with 'previous(x)' from outside the state, it will hold
the wrong value. Also, when plotting 'previous(x)' will show a wrong value during reset.
Hence, one needs another mechanism to reset 'previous(x)' correctly, but I don't see how this can be easily done by an equation
transformation to standard clocked synchronous equations in the front-end. Probably one could add a dedicated internal marker/operator
which is then handled specially in the back-end.
"
input DAE.Element inEqn;
input DAE.Element inEnclosingSMComp "The state component enclosing the equation";
input FlatSmSemantics inEnclosingFlatSmSemantics "The flat state machine semantics structure governing the state component";
input HashTableCrToExpOption.HashTable crToExpOpt "Table mapping variable declaration in the enclosing state to start values";
input tuple<list<DAE.Element>,list<DAE.Element>> accEqnsVars "Tuple for accumulating equations and variable definitions";
output tuple<list<DAE.Element>,list<DAE.Element>> outEqnsVars;
protected
list<DAE.ComponentRef> stateVarCrefs;
DAE.ComponentRef crefLHS, enclosingStateRef, substituteRef, activeResetRef, activeResetStatesRef, cref2;
Boolean found, is;
DAE.Type tyLHS;
DAE.Element eqn, eqn1, eqn2, var2, varDecl;
DAE.CallAttributes attr;
list<DAE.Element> dAElist;
Boolean isOuterVar;
// EQUATION
DAE.Exp exp;
DAE.Exp scalar, scalarNew;
DAE.ElementSource source;
algorithm
DAE.EQUATION(exp, scalar, source) := inEqn;
DAE.SM_COMP(componentRef=enclosingStateRef, dAElist=dAElist) := inEnclosingSMComp;
stateVarCrefs := BaseHashTable.hashTableKeyList(crToExpOpt);
try
// Handle case with LHS component reference
DAE.CREF(componentRef=crefLHS, ty=tyLHS) := exp;
// 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'
(scalarNew, (_, found)) := Expression.traverseExpTopDown(scalar, traversingSubsPreviousCrefs, (stateVarCrefs, false));
eqn := DAE.EQUATION(exp, scalarNew, source);
// If it is an assigning state equation, transform equation 'a.x = e' to 'a.x = if a.active then e else a.x_previous'
if List.exist(stateVarCrefs, function ComponentReference.crefEqual(inComponentRef1=crefLHS)) then
// Transform equation 'a.x = e' to 'a.x = if a.active then e else a.x_previous'
eqn1 := wrapInStateActivationConditional(eqn, enclosingStateRef, true);
// Create fresh variable 'a.x_previous'
var2 := createVarWithDefaults(ComponentReference.appendStringLastIdent("_previous", crefLHS), DAE.DISCRETE(), tyLHS, {});
// 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)'
eqn2 := createResetEquation(crefLHS, tyLHS, enclosingStateRef, inEnclosingFlatSmSemantics, crToExpOpt);
outEqnsVars := (eqn1 :: eqn2 :: Util.tuple21(accEqnsVars), var2 :: Util.tuple22(accEqnsVars));
else
outEqnsVars := (wrapInStateActivationConditional(eqn, enclosingStateRef, false)::Util.tuple21(accEqnsVars), Util.tuple22(accEqnsVars));
end if;
else
try
// Handle case with LHS derivative (der(a.x))
if Flags.getConfigBool(Flags.CT_STATE_MACHINES) then
// BTH CT_STATE_MACHINES is experimental code
DAE.CALL(Absyn.IDENT("der"), {DAE.CREF(componentRef=crefLHS, ty=tyLHS)}, attr) := exp;
// Find variable declaration that corresponds to crefLHS
try
varDecl := List.find1(dAElist, isCrefInVar, crefLHS);
else
Error.addCompilerError("Couldn't find variable declaration matching to cref " + ComponentReference.crefStr(crefLHS) + "\n");
fail();
end try;
isOuterVar := DAEUtil.isOuterVar(varDecl);
if isOuterVar then
// Create fresh variable 'a.x_der$'
cref2 := ComponentReference.appendStringLastIdent("_der$", crefLHS);
var2 := createVarWithDefaults(cref2, DAE.VARIABLE(), tyLHS, {});
// Change equation 'der(a.x) = e' to 'a.x_der$ = e'
eqn1 := DAE.EQUATION(DAE.CREF(cref2, tyLHS), scalar, source);
outEqnsVars := (eqn1 :: Util.tuple21(accEqnsVars), var2 :: Util.tuple22(accEqnsVars));
else
// Transform equation 'der(a.x) = e' to 'der(a.x) = if a.active then e else 0'
eqn1 := wrapInStateActivationConditionalCT(inEqn, enclosingStateRef);
// 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'
eqn2 := createResetEquationCT(crefLHS, tyLHS, enclosingStateRef, inEnclosingFlatSmSemantics, crToExpOpt);
outEqnsVars := (eqn1 :: eqn2 :: Util.tuple21(accEqnsVars), Util.tuple22(accEqnsVars));
end if;
else
fail();
end if;
else
if Flags.getConfigBool(Flags.CT_STATE_MACHINES) then
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");
else
Error.addCompilerError("Currently, only equations in state machines with a LHS component reference, e.g., x=.., are supported");
end if;
fail();
end try;
end try;
end addStateActivationAndReset1;
protected function isVarAtLHS "
Author: BTH
Return true if variable appears as LHS assignment in a scalar equation or in the body of a when equation.
"
input DAE.Element eqn "Expects DAE.EQUATION() or DAE.WHEN_EQUATION()";
input DAE.Element var "Expects DAE.VAR())";
output Boolean res;
protected
DAE.ComponentRef cref, crefLHS;
DAE.Type tyLHS;
// EQUATION
DAE.Exp exp;
DAE.Exp scalar, scalarNew;
DAE.ElementSource source;
// WHEN_EQUATION
list<DAE.Element> equations;
Option<DAE.Element> elsewhen_;
algorithm
res := match (eqn)
case DAE.EQUATION(exp, scalar, source)
algorithm
cref := DAEUtil.varCref(var);
try
// Handle case with LHS component reference
DAE.CREF(componentRef=crefLHS, ty=tyLHS) := exp;
res := ComponentReference.crefEqual(crefLHS, cref);
else
res := false;
end try;
then res;
case DAE.WHEN_EQUATION(equations=equations,elsewhen_=NONE())
then List.exist1(equations, isVarAtLHS, var);
case DAE.WHEN_EQUATION(elsewhen_=SOME(_))
algorithm
Error.addCompilerError("Encountered elsewhen part in a when clause of a clocked state machine.\n");
then fail();
else
algorithm
Error.addCompilerError("Internal compiler error: StateMachineFlatten.isVarAtLHS(..) called with unexpected argument.\n");
then fail();
end match;
end isVarAtLHS;
protected function isPreviousAppliedToVar "
Author: BTH
Return true if variable x appears as previous(x) in the RHS of a scalar equation or in the body of a when equation.
"
input DAE.Element eqn "Expects DAE.EQUATION() or DAE.WHEN_EQUATION()";
input DAE.Element var "Expects DAE.VAR())";
output Boolean found = false;
protected
DAE.ComponentRef cref;
// EQUATION
DAE.Exp exp;
DAE.Exp scalar, scalarNew;
DAE.ElementSource source;
// WHEN_EQUATION
list<DAE.Element> equations;
Option<DAE.Element> elsewhen_;
algorithm
found := match (eqn)
case DAE.EQUATION(exp, scalar, source)
algorithm
cref := DAEUtil.varCref(var);
(_, (_, found)) := Expression.traverseExpTopDown(scalar, traversingFindPreviousCref, (cref, false));
then found;
case DAE.WHEN_EQUATION(equations=equations,elsewhen_=NONE())
then List.exist1(equations, isPreviousAppliedToVar, var);
case DAE.WHEN_EQUATION(elsewhen_=SOME(_))
algorithm
Error.addCompilerError("Encountered elsewhen part in a when clause of a clocked state machine.\n");
then fail();
else
algorithm
Error.addCompilerError("Internal compiler error: StateMachineFlatten.isPreviousAppliedToVar(..) called with unexpected argument.\n");
then fail();
end match;
end isPreviousAppliedToVar;
protected function traversingFindPreviousCref "
Author: BTH
Given a cref 'x', find if the expression has subexpressions 'previous(x)' and indicate success.
"
input DAE.Exp inExp;
input tuple<DAE.ComponentRef, Boolean> inCrefHit;
output DAE.Exp outExp;
output Boolean cont = true;
output tuple<DAE.ComponentRef, Boolean> outCrefHit;
algorithm
(outExp, outCrefHit) := match (inExp, inCrefHit)
local
DAE.ComponentRef cr, cref;
case (DAE.CALL(Absyn.IDENT("previous"), {DAE.CREF(cr, _)}, _), (cref, _)) guard ComponentReference.crefEqual(cr, cref)
then (inExp, (cref, true));
else (inExp, inCrefHit);
end match;
end traversingFindPreviousCref;
protected function createResetEquationCT "
Author: BTH
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
'when a.active and (smOf.a.activeReset or smOf.fsm_of_a.activeResetStates[i]) then reinit(a.x, a.x_start) end when'
"
input DAE.ComponentRef inLHSCref "LHS cref";
input DAE.Type inLHSty "LHS type";
input DAE.ComponentRef inStateCref "Component reference of state enclosing the equation";
input FlatSmSemantics inEnclosingFlatSmSemantics "The flat state machine semantics structure governing the state component";
input HashTableCrToExpOption.HashTable crToExpOpt "Table mapping variable declaration in the enclosing state to start values";
output DAE.Element outEqn;
protected
DAE.Exp activeExp, activeResetExp, activeResetStatesExp, orExp, andExp, startValueExp, preExp;
DAE.Element reinitElem;
Option<DAE.Exp> startValueOpt;
DAE.ComponentRef initStateRef, preRef;
Integer i, nStates;
array<DAE.Element> enclosingFlatSMComps;
DAE.Type tArrayBool;
DAE.CallAttributes callAttributes;
algorithm
FLAT_SM_SEMANTICS(smComps=enclosingFlatSMComps) := inEnclosingFlatSmSemantics;
DAE.SM_COMP(componentRef=initStateRef) := arrayGet(enclosingFlatSMComps, 1); // initial state
// prefix for state machine semantics equations of the governing flat state machine
preRef := ComponentReference.crefPrefixString(SMS_PRE, initStateRef);
// position of enclosing state in the array of states of its governing flat state machine
i := List.position1OnTrue(arrayList(enclosingFlatSMComps), sMCompEqualsRef, inStateCref);
// smOf.a.activeReset
activeResetExp := DAE.CREF(qCref("activeReset", DAE.T_BOOL_DEFAULT, {}, preRef), DAE.T_BOOL_DEFAULT);
nStates := arrayLength(enclosingFlatSMComps);
tArrayBool := DAE.T_ARRAY(DAE.T_BOOL_DEFAULT,{DAE.DIM_INTEGER(nStates)});
// smOf.fsm_of_a.activeResetStates[i]
activeResetStatesExp := DAE.CREF(qCref("activeResetStates", tArrayBool, {DAE.INDEX(DAE.ICONST(i))}, preRef), DAE.T_BOOL_DEFAULT);
// smOf.fsm_of_a.activeReset or smOf.fsm_of_a.activeResetStates[i]
orExp := DAE.LBINARY(activeResetExp, DAE.OR(DAE.T_BOOL_DEFAULT), activeResetStatesExp);
// a.active (reference the active indicator for this state)
activeExp := DAE.CREF(qCref("active", DAE.T_BOOL_DEFAULT, {}, inStateCref), DAE.T_BOOL_DEFAULT);
// a.active and (smOf.fsm_of_a.activeReset or smOf.fsm_of_a.activeResetStates[i])
andExp := DAE.LBINARY(activeExp, DAE.AND(DAE.T_BOOL_DEFAULT), orExp);
//callAttributes := DAE.CALL_ATTR(inLHSty,false,true,false,false,DAE.NO_INLINE(),DAE.NO_TAIL());
// pre(activeExp)
//preExp := DAE.CALL(Absyn.IDENT("pre"), {activeExp}, callAttributes);
//andExp := DAE.LBINARY(activeExp, DAE.AND(DAE.T_BOOL_DEFAULT), DAE.LUNARY(DAE.NOT(DAE.T_BOOL_DEFAULT), preExp));
startValueOpt := BaseHashTable.get(inLHSCref, crToExpOpt);
if isSome(startValueOpt) then
startValueExp := Util.getOption(startValueOpt);
else
// No start value given for the variable, default to "0"
startValueExp := match inLHSty
case DAE.T_INTEGER()
algorithm
Error.addCompilerWarning("Variable "+ComponentReference.crefStr(inLHSCref)+" lacks start value. Defaulting to start=0.\n");
then DAE.ICONST(0);
case DAE.T_REAL()
algorithm
Error.addCompilerWarning("Variable "+ComponentReference.crefStr(inLHSCref)+" lacks start value. Defaulting to start=0.\n");
then DAE.RCONST(0);
case DAE.T_BOOL()
algorithm
Error.addCompilerWarning("Variable "+ComponentReference.crefStr(inLHSCref)+" lacks start value. Defaulting to start=false.\n");
then DAE.BCONST(false);
case DAE.T_STRING()
algorithm
Error.addCompilerWarning("Variable "+ComponentReference.crefStr(inLHSCref)+" lacks start value. Defaulting to start=\"\".\n");
then DAE.SCONST("");
else
algorithm
Error.addCompilerError("Variable "+ComponentReference.crefStr(inLHSCref)+" lacks start value.\n");
then fail();
end match;
end if;
// reinit(a.x, a.x_start)
reinitElem := DAE.REINIT(inLHSCref, startValueExp, DAE.emptyElementSource);
// when a.active and (smOf.a.activeReset or smOf.fsm_of_a.activeResetStates[i]) then reinit(a.x, a.x_start) end when;
outEqn := DAE.WHEN_EQUATION(andExp, {reinitElem}, NONE(), DAE.emptyElementSource);
end createResetEquationCT;
protected function isCrefInVar "
Author: BTH
Return true if element is a VAR containing the cref, otherwise false"
input DAE.Element inElement;
input DAE.ComponentRef inCref;
output Boolean result;
algorithm
result := match (inElement)
local
DAE.ComponentRef cref;
case DAE.VAR(componentRef=cref) guard ComponentReference.crefEqual(cref, inCref) then true;
else then false;
end match;
end isCrefInVar;
protected function createResetEquation "
Author: BTH
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
'a.x_previous = if a.active and (smOf.a.activeReset or smOf.fsm_of_a.activeResetStates[i] then x_start else previous(a.x)'
"
input DAE.ComponentRef inLHSCref "LHS cref";
input DAE.Type inLHSty "LHS type";
input DAE.ComponentRef inStateCref "Component reference of state enclosing the equation";
input FlatSmSemantics inEnclosingFlatSmSemantics "The flat state machine semantics structure governing the state component";
input HashTableCrToExpOption.HashTable crToExpOpt "Table mapping variable declaration in the enclosing state to start values";
output DAE.Element outEqn;
protected
DAE.Exp activeExp, lhsExp, activeResetExp, activeResetStatesExp, orExp, andExp, previousExp, startValueExp, ifExp;
Option<DAE.Exp> startValueOpt;
DAE.ComponentRef initStateRef, preRef;
Integer i, nStates;
array<DAE.Element> enclosingFlatSMComps;
DAE.Type tArrayBool;
DAE.CallAttributes callAttributes;
algorithm
FLAT_SM_SEMANTICS(smComps=enclosingFlatSMComps) := inEnclosingFlatSmSemantics;
DAE.SM_COMP(componentRef=initStateRef) := arrayGet(enclosingFlatSMComps, 1); // initial state
// prefix for state machine semantics equations of the governing flat state machine
preRef := ComponentReference.crefPrefixString(SMS_PRE, initStateRef);
// position of enclosing state in the array of states of its governing flat state machine
i := List.position1OnTrue(arrayList(enclosingFlatSMComps), sMCompEqualsRef, inStateCref);
// smOf.a.activeReset
activeResetExp := DAE.CREF(qCref("activeReset", DAE.T_BOOL_DEFAULT, {}, preRef), DAE.T_BOOL_DEFAULT);
nStates := arrayLength(enclosingFlatSMComps);
tArrayBool := DAE.T_ARRAY(DAE.T_BOOL_DEFAULT,{DAE.DIM_INTEGER(nStates)});
// smOf.fsm_of_a.activeResetStates[i]
activeResetStatesExp := DAE.CREF(qCref("activeResetStates", tArrayBool, {DAE.INDEX(DAE.ICONST(i))}, preRef), DAE.T_BOOL_DEFAULT);
// smOf.fsm_of_a.activeReset or smOf.fsm_of_a.activeResetStates[i]
orExp := DAE.LBINARY(activeResetExp, DAE.OR(DAE.T_BOOL_DEFAULT), activeResetStatesExp);
// a.active (reference the active indicator for this state)
activeExp := DAE.CREF(qCref("active", DAE.T_BOOL_DEFAULT, {}, inStateCref), DAE.T_BOOL_DEFAULT);
// a.active and (smOf.fsm_of_a.activeReset or smOf.fsm_of_a.activeResetStates[i])
andExp := DAE.LBINARY(activeExp, DAE.AND(DAE.T_BOOL_DEFAULT), orExp);
callAttributes := DAE.CALL_ATTR(inLHSty,false,true,false,false,DAE.NO_INLINE(),DAE.NO_TAIL());
// previous(a.x)
previousExp := DAE.CALL(Absyn.IDENT("previous"), {DAE.CREF(inLHSCref, inLHSty)}, callAttributes);
startValueOpt := BaseHashTable.get(inLHSCref, crToExpOpt);
if isSome(startValueOpt) then
startValueExp := Util.getOption(startValueOpt);
else
// No start value given for the variable, default to "0"
startValueExp := match inLHSty
case DAE.T_INTEGER()
algorithm
Error.addCompilerWarning("Variable "+ComponentReference.crefStr(inLHSCref)+" lacks start value. Defaulting to start=0.\n");
then DAE.ICONST(0);
case DAE.T_REAL()
algorithm
Error.addCompilerWarning("Variable "+ComponentReference.crefStr(inLHSCref)+" lacks start value. Defaulting to start=0.\n");
then DAE.RCONST(0);
case DAE.T_BOOL()
algorithm
Error.addCompilerWarning("Variable "+ComponentReference.crefStr(inLHSCref)+" lacks start value. Defaulting to start=false.\n");
then DAE.BCONST(false);
case DAE.T_STRING()
algorithm
Error.addCompilerWarning("Variable "+ComponentReference.crefStr(inLHSCref)+" lacks start value. Defaulting to start=\"\".\n");
then DAE.SCONST("");
else
algorithm
Error.addCompilerError("Variable "+ComponentReference.crefStr(inLHSCref)+" lacks start value.\n");
then fail();
end match;
end if;
// if a.active and (smOf.fsm_of_a.activeReset or smOf.fsm_of_a.activeResetStates[i]) than x_start else previous(a.x)
ifExp := DAE.IFEXP(andExp, startValueExp, previousExp);
// a.x_previous
lhsExp := DAE.CREF(ComponentReference.appendStringLastIdent("_previous", inLHSCref), inLHSty);
// a.x_previous = if a.active and (smOf.a.activeReset or smOf.fsm_of_a.activeResetStates[i] then x_start else previous(a.x)
outEqn := DAE.EQUATION(lhsExp, ifExp, DAE.emptyElementSource);
end createResetEquation;
protected function wrapInStateActivationConditional "
Author: BTH
Transform an equation 'a.x = e' to 'a.x = if a.active then e else previous(a.x)' (isResetEquation=false)
Transform an equation 'a.x = e' to 'a.x = if a.active then e else x_previous' (isResetEquation=true)
"
input DAE.Element inEqn;
input DAE.ComponentRef inStateCref "Component reference of state enclosing the equation";
input Boolean isResetEquation "Reset equations";
output DAE.Element outEqn;
protected
DAE.Exp exp, scalar, scalar1, activeRef, expElse;
DAE.Type ty;
DAE.CallAttributes callAttributes;
DAE.ElementSource source;
DAE.ComponentRef cref;
algorithm
DAE.EQUATION(exp, scalar, source) := inEqn;
try
DAE.CREF(cref, ty) := exp;
else
Error.addCompilerError("The LHS of equations in state machines needs to be a component reference");
fail();
end try;
// reference the active indicator for this state
activeRef := DAE.CREF(qCref("active", DAE.T_BOOL_DEFAULT, {}, inStateCref), DAE.T_BOOL_DEFAULT);
callAttributes := DAE.CALL_ATTR(ty,false,true,false,false,DAE.NO_INLINE(),DAE.NO_TAIL());
if isResetEquation then // x_previous
expElse := DAE.CREF(ComponentReference.appendStringLastIdent("_previous", cref), ty);
else // previous(x)
expElse := DAE.CALL(Absyn.IDENT("previous"), {exp}, callAttributes);
end if;
scalar1 := DAE.IFEXP(activeRef, scalar, expElse);
// state.x = if state.active then .. else expElse
outEqn := DAE.EQUATION(exp, scalar1, source);
end wrapInStateActivationConditional;
protected function wrapInStateActivationConditionalCT "
Author: BTH
Transform an equation 'der(a.x) = e' to 'der(a.x) = if a.active then e else 0' (isResetEquation=false)
TODO: Implement reset equations for continuous time. Should that be done in this function or somewhere else?
FIXME: Merge with wrapInStateActivationConditional(..)?
"
input DAE.Element inEqn;
input DAE.ComponentRef inStateCref "Component reference of state enclosing the equation";
output DAE.Element outEqn;
protected
DAE.Exp exp, scalar, scalar1, activeRef, expElse;
DAE.Type ty;
DAE.CallAttributes callAttributes;
DAE.ElementSource source;
DAE.ComponentRef cref;
algorithm
DAE.EQUATION(exp, scalar, source) := inEqn;
try
DAE.CALL(Absyn.IDENT("der"), {DAE.CREF(componentRef=cref, ty=ty)}, _) := exp;
else
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) = ..");
fail();
end try;
// reference the active indicator for this state
activeRef := DAE.CREF(qCref("active", DAE.T_BOOL_DEFAULT, {}, inStateCref), DAE.T_BOOL_DEFAULT);
callAttributes := DAE.CALL_ATTR(ty,false,true,false,false,DAE.NO_INLINE(),DAE.NO_TAIL());
expElse := DAE.RCONST(0);
scalar1 := DAE.IFEXP(activeRef, scalar, expElse);
// state.x = if state.active then .. else expElse
outEqn := DAE.EQUATION(exp, scalar1, source);
end wrapInStateActivationConditionalCT;
protected function traversingSubsPreviousCref "
Author: BTH
Given a cref 'x', find if the expression has subexpressions 'previous(x)' and replace them by 'x_previous'
and return an indication if any substitutions took place.
"
input DAE.Exp inExp;
input tuple<DAE.ComponentRef, Boolean> inCrefHit;
output DAE.Exp outExp;
output Boolean cont = true;
output tuple<DAE.ComponentRef, Boolean> outCrefHit;
algorithm
(outExp, outCrefHit) := match (inExp, inCrefHit)
local
DAE.ComponentRef cr, cref, substituteRef;
Boolean hit;
DAE.CallAttributes attr;
DAE.Type ty;
case (DAE.CALL(Absyn.IDENT("previous"), {DAE.CREF(cr, ty)}, _),
(cref, _)) guard ComponentReference.crefEqual(cr, cref)