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 |