diff --git a/autocoder/Stars.py b/autocoder/Stars.py index 5e67998..b55e5d6 100755 --- a/autocoder/Stars.py +++ b/autocoder/Stars.py @@ -184,13 +184,13 @@ def getQmRoot(modelFileName: str) -> Tuple[ElementTreeType, XmiModel] : checkFaults.checkStateMachine(qmRoot) if args.backend == "c++": - cppcoder.generateCode(qmRoot, args.noImpl) + cppcoder.generateCode(xmiModel, args.noImpl) if args.backend == "c": ccoder.generateCode(qmRoot, args.noImpl) if args.backend == "qf": - qfcoder.generateCode(qmRoot, args.noImpl, args.noSignals) + qfcoder.generateCode(xmiModel, args.noImpl, args.noSignals) if args.backend == "test": testcoder.generateCode(qmRoot) diff --git a/autocoder/checkFaults.py b/autocoder/checkFaults.py index 6ddc8a3..e952bbe 100644 --- a/autocoder/checkFaults.py +++ b/autocoder/checkFaults.py @@ -360,7 +360,8 @@ def checkAction(action: str, tran: str): args = function[slice(s,e)] strippedArgs = args.strip(' ') - if (strippedArgs != '') and (not strippedArgs.isnumeric()) and (strippedArgs != 'e'): + + if (args == '' and (s != e)): raise ActionArg(tran, function) # ----------------------------------------------------------------------- diff --git a/autocoder/cpp_backend/cppcoder.py b/autocoder/cpp_backend/cppcoder.py index dc35dc6..21f3c5a 100644 --- a/autocoder/cpp_backend/cppcoder.py +++ b/autocoder/cpp_backend/cppcoder.py @@ -14,8 +14,11 @@ from cpp_backend.cpptemplates import CppTemplate from cpp_backend.cppUnitTestTemplates import CppUnitTestTemplate from cpp_backend.cppImplTemplates import CppImplTemplate -from typing import List, Dict, Tuple, Any, Optional, IO +from typing import List, Dict, Tuple, Any, Optional, IO, TextIO from qmlib import ElementTreeType +from anytree import Node, PreOrderIter +import re +from xmiModelApi import XmiModel # Initialize the global variables codeTemplate = CppTemplate() @@ -28,48 +31,155 @@ # # Print the state-machine header file # ----------------------------------------------------------------------- -def printSmHeader(smname: str, root: ElementTreeType): +def printSmHeader(xmiModel: XmiModel): + stateMachine = xmiModel.tree.stateMachine - hFile = open(smname + ".h", "w") - - eventList = [] - trans = root.iter('tran') - for tran in trans: - event = tran.get('trig').upper() + "_SIG" - if event not in eventList: - eventList.append(event) + hFile = open(f"{stateMachine}.h", "w") + + states = list() + (actions, guards, signals) = getStateMachineMethods(xmiModel) + + #trans = root.iter('tran') + + #print(f"signals {signals}") + + isSuperstate = False + + for child in PreOrderIter(xmiModel.tree): + if child.name == "STATE": + for grandchild in PreOrderIter(child): + # if the node is a superstate, do not add it to the state list + if (grandchild.name == "STATE"): + if (child.stateName != grandchild.stateName): + isSuperstate = True + break + if (not isSuperstate): + states.append(child.stateName) + + isSuperstate = False + + signals = {signal.upper() + "_SIG" for signal in signals} - stateList = [] - states = root.iter('state') - for state in states: - stateList.append(state.get('name')) + actions = sorted(actions) + guards = sorted(guards) + signals = sorted(signals) + #states = sorted(states) - guardFunctions = qmlib.get_guard_functions(root) - stateFunctions = qmlib.get_state_functions(root) - transFunctions = qmlib.get_trans_functions(root) + #guardFunctions = qmlib.get_guard_functions(root) + #stateFunctions = qmlib.get_state_functions(root) + #transFunctions = qmlib.get_trans_functions(root) + + #getInitTransitions(xmiModel) - actionFunctions = stateFunctions + transFunctions + #actionFunctions = stateFunctions + transFunctions - funcList = [] - sigList = [] - for func in guardFunctions: - actionName, actionArgs = qmlib.parse_action(func) - sig = codeTemplate.guardSignature(smname, actionName, actionArgs) - if sig not in sigList: - sigList.append(sig) - funcList.append(sig) + #funcList = [] + #sigList = [] + #for guard in guards: + #print(f"guard: {guard}") + #actionName, actionArgs = qmlib.parse_action(func) + # sig = codeTemplate.guardSignature(stateMachine, action, actionArgs) + #if sig not in sigList: + #sigList.append(sig) + #funcList.append(sig) - sigList = [] - for func in actionFunctions: - actionName, actionArgs = qmlib.parse_action(func) - sig = codeTemplate.actionSignature(smname, actionName, actionArgs) - if sig not in sigList: - sigList.append(sig) - funcList.append(sig) + #sigList = [] + #for action in actions: + #print(f"acton: {action}") + #actionName, actionArgs = qmlib.parse_action(func) + #sig = codeTemplate.actionSignature(smname, actionName, actionArgs) + #if sig not in sigList: + #sigList.append(sig) + #funcList.append(sig) - hFile.write(codeTemplate.fileHeader(smname, stateList, eventList, funcList)) - - + hFile.write(codeTemplate.fileHeader(stateMachine, states, signals, actions)) + +# ----------------------------------------------------------------------- +# getInitTranstions +# +# Update the xmi model to add Initial Transitions from Transitions +# ----------------------------------------------------------------------- +def getInitTransitions(xmiModel: XmiModel): + + psuedoStateList = xmiModel.psuedoStateList + transTargetSet = xmiModel.transTargets + + for trans in PreOrderIter(xmiModel.tree): + if trans.name == "TRANSITION": + # If the transition source is a psuedostate and no other transition goes into that psuedostate + if (trans.source in psuedoStateList) and (trans.source not in transTargetSet): + xmiModel.addInitial(trans) + +def getActionNames(input_string: str, fullSpecifier: bool): + if input_string is None: + return None + + # Use regex to find all procedural names before the '(' and ignore everything after + procedural_names = re.findall(r'\b\w+(?=\()', input_string) + # Join the names with commas + output_string = ', '.join(procedural_names) + + if fullSpecifier: + output_string = output_string + getActionDataType(input_string) + + return output_string + +def getActionDataType(inputString: str): + if inputString is None: + return "" + + outputString = None + + # Get this index of the opening and closing parenthesis for function parameter list + start = inputString.index('(') + 1 + end = inputString.index(')') + + # If there is any character between the parenthesis, treat it as a FPP datatype + if (start != end): + outputString = (": " + inputString[slice(start,end)]) + else: + outputString = "" + + return outputString + +def getStateMachineMethods(xmiModel: XmiModel): + + actionSet = set() + guardSet = set() + signalSet = set() + + for child in PreOrderIter(xmiModel.tree): + #print(child.name) + if child.name == "STATE": + #actionSet.add(getActionNames(child.entry, True)) + actionSet.add(child.entry) + actionSet.add(child.exit) + if child.name == "TRANSITION": + actionSet.add(child.action) + guardSet.add(getActionNames(child.guard, False)) + if (child.event != None): + #print(f"{child.name} - {child} - {child.event}") + signalSet.add((child.event)) + if child.name == "JUNCTION": + actionSet.add(child.ifAction) + actionSet.add(child.elseAction) + guardSet.add(child.guard) + if child.name == "INITIAL": + actionSet.add(child.action) + + # Remove empty strings + actionSet = {item for item in actionSet if item} + guardSet = {item for item in guardSet if item} + signalSet = {item for item in signalSet if item} + + #for item in guardSet: + #print("item: " + str(item)) + + flatActions = {a.strip() for action in actionSet for a in action.split(',')} + + + return (flatActions, guardSet, signalSet) + # --------------------------------------------------------------------------- # formatTarget # @@ -137,40 +247,122 @@ def printTransition(smname: str, tran: ElementTreeType): rstr = rstr + printTransition(smname, elseChoice) rstr.append("}") + #print(f"transition {rstr}") + return rstr +# ----------------------------------------------------------------------- +# getJunctions +# +# Update the xmi model to add Junctions +# ----------------------------------------------------------------------- +def getJunctions(xmiModel: XmiModel): + + for ps in PreOrderIter(xmiModel.tree): + if ps.name == "PSUEDOSTATE": + psId = ps.id + transList = [] + for child in PreOrderIter(xmiModel.tree): + # Get the transitions that exit this psuedo state + if child.name == "TRANSITION": + if psId == child.source: + transList.append(child) + if len(transList) == 2: + xmiModel.addJunction(transList, ps) + +# ----------------------------------------------------------------------- +# moveTransitions +# +# Transitions that start from a state are to be moved under that state +# ----------------------------------------------------------------------- +def moveTransitions(xmiModel: XmiModel): + for child in PreOrderIter(xmiModel.tree): + if child.name == "TRANSITION": + # Look up where this transition is supposed to go + state = xmiModel.idMap[child.source] + # Move the transition under the source state + xmiModel.moveTransition(child, state) + + if child.name == "JUNCTION": + for sourceTransition in PreOrderIter(xmiModel.tree): + if (sourceTransition.name == "TRANSITION") and (sourceTransition.target == child.id): + #state = xmiModel.idMap[parentState.source] + child.parent = sourceTransition.parent.parent + # Move the transition under the source state + # --------------------------------------------------------------------------- # printStateTransition # # Print a transition from a state # --------------------------------------------------------------------------- -def printStateTransition(smname: str, tran: ElementTreeType, cFile: IO): +def printStateTransition(smname: str, tran: Node, cFile: IO): signal = tran.get('trig').upper() + "_SIG" transition = qmlib.format_C(printTransition(smname, tran), 24) cFile.write(codeTemplate.stateTransition(signal, transition)) + +def getStates(xmiModel: XmiModel): + states = list() + + isSuperstate = False + + for child in PreOrderIter(xmiModel.tree): + if child.name == "STATE": + for grandchild in PreOrderIter(child): + # if the node is a superstate, do not add it to the state list + if (grandchild.name == "STATE"): + if (child.stateName != grandchild.stateName): + isSuperstate = True + break + if (not isSuperstate): + states.append(child.stateName) + + isSuperstate = False + + return states + +def resolveTransition(xmiModel: XmiModel, node: Node, states: List): + if xmiModel.idMap[node.target].stateName in states: + return xmiModel.idMap[node.target].stateName + else: + for child in xmiModel.idMap[node.target].children: + if child.name == "INITIAL": + return xmiModel.idMap[child.target].stateName + + # ----------------------------------------------------------------------- # printSmCode # # Print the state-machine C file -# ----------------------------------------------------------------------- -def printSmCode(smname: str, root: ElementTreeType): +# ----------------------------------------------------------------------- +def printSmCode(node: Node, xmiModel: XmiModel, cFile: TextIO, level: int = 4): + stateMachine = xmiModel.tree.stateMachine - cFile = open(smname + ".cpp", "w") + states = getStates(xmiModel) - initialTran = root.find('initial') - initialCode = qmlib.format_C(printTransition(smname, initialTran), 4) - cFile.write(codeTemplate.stateMachineInit(smname, initialCode)) - - states = root.iter("state") + #initialTran = root.find('initial') + + defaultIndent = " " + + indent = defaultIndent * level + for state in states: - cFile.write(codeTemplate.stateMachineState(state.get('name'))) - trans = state.findall('tran') - for tran in trans: - printStateTransition(smname, tran, cFile) + cFile.write(codeTemplate.stateMachineState(state)) + + for child in PreOrderIter(xmiModel.tree): + if child.name == "TRANSITION": + #print(xmiModel.idMap[child.source]) + if (xmiModel.idMap[child.source].stateName == state): + print(f"State match with {state}") + guardExpr = f" if {getActionNames(child.guard, False)}" if child.guard else "" + transition = f"\n{indent}this->state = {resolveTransition(xmiModel, child, states)};" if child.kind is None else "" + action = f"\n{indent}{getActionNames(child.action, False)}();" if child.action else "" + if (action != "" or transition != ""): + cFile.write(f"{defaultIndent}case {child.event.upper() + "_SIG:"}{guardExpr}{action}{transition}\n{indent}break;\n") + cFile.write(codeTemplate.stateMachineBreak()) cFile.write(codeTemplate.stateMachineFinalBreak()) @@ -277,33 +469,83 @@ def printImplCode(smname: str, root: ElementTreeType): # # Print the state-machine C file # ----------------------------------------------------------------------- -def generateCode(qmRoot: ElementTreeType, noImpl: bool): +def generateCode(xmiModel: XmiModel, noImpl: bool): global codeTemplate global unitTestTemplate global codeImplTemplate - qmRoot, smname = qmlib.get_state_machine(qmRoot) + stateMachine = xmiModel.tree.stateMachine + + currentNode = xmiModel.tree + + #qmRoot, smname = qmlib.get_state_machine(qmRoot) - print ("Generating flat C++ code for {0}".format(smname)) + print (f"Generating flat C++ code for {stateMachine}") + + cFile = open(f"{stateMachine}.cpp", "w") + + getInitTransitions(xmiModel) + + getJunctions(xmiModel) + + (actions, guards, signals) = getStateMachineMethods(xmiModel) + + moveTransitions(xmiModel) + + xmiModel.print() + + #initialCode = qmlib.format_C(printTransition(stateMachine, currentNode), 4) + #cFile.write(codeTemplate.stateMachineInit(stateMachine, initialCode)) - flatchart : ElementTreeType = flatt.flatten_state_machine(qmRoot) + #flatchart : ElementTreeType = flatt.flatten_state_machine(currentNode) if noImpl == False: # Generate the Impl files - print ("Generating " + smname + "Impl.cpp") - printImplCode(smname, flatchart) + print (f"Generating {stateMachine}Impl.cpp") + #printImplCode(smname, flatchart) # Generate the unit test files print ("Generating main.cpp") print ("Generating sendEvent.h") print ("Generating sendEvent.cpp") - printUnitCode(smname, flatchart) + #printUnitCode(smname, flatchart) # Generate the header file - print ("Generating " + smname + ".cpp") - printSmHeader(smname, flatchart) + print (f"Generating {stateMachine}.cpp") + printSmHeader(xmiModel) # Generate the C file - print ("Generating " + smname + ".h") - printSmCode(smname, flatchart) + print (f"Generating {stateMachine}.h") + + initialCode = str() + target = str() + + for child in currentNode.children: + #print(f"{child.name}") + + if child.name == "INITIAL": + #print(child.target) + target = xmiModel.idMap[child.target].stateName + + for child in currentNode.children: + if (child.name == "STATE"): + #print(child.stateName) + + if (child.stateName == target): + #print("node found") + if child.entry: + initialCode = " " + child.entry + ";" + + initialCode = initialCode + "\n this->state = " + target + ";" + + break + + xmiModel.flattenModel() + + xmiModel.print() + + #initialCode = qmlib.format_C(printTransition(stateMachine, Node), 4) + cFile.write(codeTemplate.stateMachineInit(stateMachine, initialCode, "")) + + printSmCode(currentNode, xmiModel, cFile) diff --git a/autocoder/cpp_backend/cpptemplates.py b/autocoder/cpp_backend/cpptemplates.py index d8fe8fa..6910c48 100644 --- a/autocoder/cpp_backend/cpptemplates.py +++ b/autocoder/cpp_backend/cpptemplates.py @@ -174,7 +174,7 @@ class $(smname) { // state machine implementation functions #for $function in $implFunctions - $function; + void $function; #end for }; @@ -192,7 +192,7 @@ class $(smname) { # ------------------------------------------------------------------------------- # stateMachineInit # ------------------------------------------------------------------------------- - def stateMachineInit(self, smname: str, transition: str) -> str: + def stateMachineInit(self, smname: str, initial: str, transition: str) -> str: template = Template(""" \#include "stdio.h" @@ -202,15 +202,16 @@ def stateMachineInit(self, smname: str, transition: str) -> str: void $(smname)::init() { -$transition +$initial } void $(smname)::update(EventSignal *e) { - switch (this->state) { + switch (this->state) { """) template.smname = smname + template.initial = initial template.transition = transition return str(template) @@ -219,15 +220,17 @@ def stateMachineInit(self, smname: str, transition: str) -> str: # stateMachineState # ------------------------------------------------------------------------------- def stateMachineState(self, state: str) -> str: - template = Template(""" - /** - * state $state - */ - case $state: + template = Template("""/** + * state $state + */ + + case $state: - switch (e->sig) { -""") + switch (e->sig) { + """) + template.state = state + return str(template) @@ -236,10 +239,11 @@ def stateMachineState(self, state: str) -> str: # ------------------------------------------------------------------------------- def stateMachineBreak(self) -> str: template = Template(""" - default: - break; - } - break; + default: + break; + } + break; + """) return str(template) diff --git a/autocoder/fprime_backend/fppcoder.py b/autocoder/fprime_backend/fppcoder.py index 1cddbd5..ec72109 100644 --- a/autocoder/fprime_backend/fppcoder.py +++ b/autocoder/fprime_backend/fppcoder.py @@ -10,15 +10,37 @@ from typing import TextIO -def getActionNames(input_string: str): +def getActionNames(input_string: str, fullSpecifier: bool): if input_string is None: return None + # Use regex to find all procedural names before the '(' and ignore everything after procedural_names = re.findall(r'\b\w+(?=\()', input_string) # Join the names with commas output_string = ', '.join(procedural_names) + + if fullSpecifier: + output_string = output_string + getActionDataType(input_string) + return output_string +def getActionDataType(inputString: str): + if inputString is None: + return "" + + outputString = None + + # Get this index of the opening and closing parenthesis for function parameter list + start = inputString.index('(') + 1 + end = inputString.index(')') + + # If there is any character between the parenthesis, treat it as a FPP datatype + if (start != end): + outputString = (": " + inputString[slice(start,end)]) + else: + outputString = "" + + return outputString # ----------------------------------------------------------------------- # processNode @@ -36,29 +58,28 @@ def processNode(node: Node, if child.name == "INITIAL": target = xmiModel.idMap[child.target].stateName - doExpr = f" do {{ {getActionNames(child.action)} }}" if child.action else "" + doExpr = f" do {{ {getActionNames(child.action, False)} }}" if child.action else "" fppFile.write(f"{indent}initial{doExpr} enter {target}\n") if child.name == "JUNCTION": ifTarget = xmiModel.idMap[child.ifTarget].stateName elseTarget = xmiModel.idMap[child.elseTarget].stateName - doIfExpr = f" do {{ {getActionNames(child.ifAction)} }}" if child.ifAction else "" - doElseExpr = f" do {{ {getActionNames(child.elseAction)} }}" if child.elseAction else "" - fppFile.write(f"{indent}junction {child.stateName} {{\n") - fppFile.write(f"{indent} if {getActionNames(child.guard)}{doIfExpr} enter {ifTarget} \\\n") - fppFile.write(f"{indent} else{doElseExpr} enter {elseTarget}\n") + doIfExpr = f" do {{ {getActionNames(child.ifAction, False)} }}" if child.ifAction else "" + doElseExpr = f" do {{ {getActionNames(child.elseAction, False)} }}" if child.elseAction else "" + fppFile.write(f"{indent}choice {child.stateName} {{\n") + fppFile.write(f"{indent} if {child.guard}{doIfExpr} enter {ifTarget} else{doElseExpr} enter {elseTarget}\n") fppFile.write(f"{indent}}}\n") if child.name == "TRANSITION": - guardExpr = f" if {getActionNames(child.guard)}" if child.guard else "" + guardExpr = f" if {getActionNames(child.guard, False)}" if child.guard else "" enterExpr = f" enter {xmiModel.idMap[child.target].stateName}" if child.kind is None else "" - doExpr = f" do {{ {getActionNames(child.action)} }}" if child.action else "" + doExpr = f" do {{ {getActionNames(child.action, False)} }}" if child.action else "" fppFile.write(f"{indent}on {child.event}{guardExpr}{doExpr}{enterExpr}\n") if child.name == "STATE": stateName = child.stateName - enterExpr = f" entry do {{ {getActionNames(child.entry)} }}" if child.entry else "" - exitExpr = f" exit do {{ {getActionNames(child.exit)} }}" if child.exit else "" + enterExpr = f" entry do {{ {getActionNames(child.entry, False)} }}" if child.entry else "" + exitExpr = f" exit do {{ {getActionNames(child.exit, False)} }}" if child.exit else "" fppFile.write(f"{indent}state {stateName} {{\n") if enterExpr: fppFile.write(f"{indent}{enterExpr}\n") @@ -108,13 +129,19 @@ def getJunctions(xmiModel: XmiModel): # Transitions that start from a state are to be moved under that state # ----------------------------------------------------------------------- def moveTransitions(xmiModel: XmiModel): - - for trans in PreOrderIter(xmiModel.tree): - if trans.name == "TRANSITION": + for child in PreOrderIter(xmiModel.tree): + if child.name == "TRANSITION": # Look up where this transition is supposed to go - state = xmiModel.idMap[trans.source] + state = xmiModel.idMap[child.source] # Move the transition under the source state - xmiModel.moveTransition(trans, state) + xmiModel.moveTransition(child, state) + + if child.name == "JUNCTION": + for sourceTransition in PreOrderIter(xmiModel.tree): + if (sourceTransition.name == "TRANSITION") and (sourceTransition.target == child.id): + #state = xmiModel.idMap[parentState.source] + child.parent = sourceTransition.parent.parent + # Move the transition under the source state def getStateMachineMethods(xmiModel: XmiModel): @@ -124,25 +151,29 @@ def getStateMachineMethods(xmiModel: XmiModel): signalSet = set() for child in PreOrderIter(xmiModel.tree): + #print(child.name) if child.name == "STATE": - actionSet.add(getActionNames(child.entry)) - actionSet.add(getActionNames(child.exit)) + actionSet.add(getActionNames(child.entry, True)) + actionSet.add(getActionNames(child.exit, True)) if child.name == "TRANSITION": - actionSet.add(getActionNames(child.action)) - guardSet.add(getActionNames(child.guard)) - signalSet.add(child.event) + actionSet.add(getActionNames(child.action, True)) + guardSet.add(getActionNames(child.guard, False)) + signalSet.add((child.event + getActionDataType(child.action))) if child.name == "JUNCTION": - actionSet.add(getActionNames(child.ifAction)) - actionSet.add(getActionNames(child.elseAction)) - guardSet.add(getActionNames(child.guard)) + actionSet.add(getActionNames(child.ifAction, True)) + actionSet.add(getActionNames(child.elseAction, True)) + guardSet.add(child.guard) if child.name == "INITIAL": - actionSet.add(getActionNames(child.action)) + actionSet.add(getActionNames(child.action, True)) # Remove empty strings actionSet = {item for item in actionSet if item} guardSet = {item for item in guardSet if item} signalSet = {item for item in signalSet if item} + #for item in guardSet: + #print("item: " + str(item)) + flatActions = {a.strip() for action in actionSet for a in action.split(',')} @@ -155,11 +186,13 @@ def getStateMachineMethods(xmiModel: XmiModel): # ----------------------------------------------------------------------- def generateCode(xmiModel: XmiModel): + #xmiModel.print() + stateMachine = xmiModel.tree.stateMachine - print ("Generating " + stateMachine + ".fpp") + print ("Generating " + stateMachine + "_State_Machine.fpp") - fppFile = open(stateMachine +".fpp", "w") + fppFile = open(stateMachine +"_State_Machine.fpp", "w") currentNode = xmiModel.tree @@ -167,9 +200,11 @@ def generateCode(xmiModel: XmiModel): getJunctions(xmiModel) + (actions, guards, signals) = getStateMachineMethods(xmiModel) + moveTransitions(xmiModel) - (actions, guards, signals) = getStateMachineMethods(xmiModel) + xmiModel.print() fppFile.write(f"state machine {xmiModel.tree.stateMachine} {{\n\n") diff --git a/autocoder/fprime_backend/fprimeImplTemplates.py b/autocoder/fprime_backend/fprimeImplTemplates.py index 123d62e..64afdc3 100644 --- a/autocoder/fprime_backend/fprimeImplTemplates.py +++ b/autocoder/fprime_backend/fprimeImplTemplates.py @@ -18,17 +18,15 @@ class FprimeImplTemplate: # stateEnumFpp # ------------------------------------------------------------------------------- def stateEnumFpp(self, smname: str, namespace: str, stateList: List[str]): - template = Template(""" - - enum $(smname)States { - #set $counter = 0 - #for $state in $stateList - $(state) = $counter - #set $counter = $counter + 1 - #end for - } - -""") + template = Template( +"""enum $(smname)States { +#set $counter = 0 +#for $state in $stateList + $(state) = $counter +#set $counter = $counter + 1 +#end for +}""" +) template.smname = smname template.namespace = namespace template.stateList = stateList diff --git a/autocoder/fprime_backend/fprimecoder.py b/autocoder/fprime_backend/fprimecoder.py index 3bc7522..48a7514 100644 --- a/autocoder/fprime_backend/fprimecoder.py +++ b/autocoder/fprime_backend/fprimecoder.py @@ -401,7 +401,7 @@ def printEnumFpp(smname: str, # Open the generated files - fileName = smname + ".fppi" + fileName = smname + "_Enum.fpp" file = open(fileName, "w") print(f'Generating {fileName}') @@ -461,13 +461,13 @@ def generateCode(qmRoot: ElementTreeType, printUnitCode(smname, implHdr, component, namespace, flatchart) # Generate the state-machine header file - print ("Generating " + smname + ".cpp") - print ("Generating " + smname + ".trans") - printSmHeader(smname, flatchart, namespace) + #print ("Generating " + smname + ".cpp") + #print ("Generating " + smname + ".trans") + #printSmHeader(smname, flatchart, namespace) # Generate the state-machine implementation file - print ("Generating " + smname + ".hpp") - printSmCode(smname, flatchart, namespace) + #print ("Generating " + smname + ".hpp") + #printSmCode(smname, flatchart, namespace) # Generate the states enumeration fpp printEnumFpp(smname, flatchart, namespace) diff --git a/autocoder/qf_backend/qfcoder.py b/autocoder/qf_backend/qfcoder.py index b46caf8..4631612 100644 --- a/autocoder/qf_backend/qfcoder.py +++ b/autocoder/qf_backend/qfcoder.py @@ -19,6 +19,8 @@ from qf_backend.qfUnitTestTemplates import QFUnitTestTemplate from qf_backend.qfImplTemplates import QFImplTemplate from qmlib import ElementTreeType +from anytree import Node, PreOrderIter +from xmiModelApi import XmiModel # --------------------------------------------------------------------------- # formatTarget @@ -313,9 +315,10 @@ def printUnitCode(smname, root): # # Recursively change state names to include the names of the parent states # ----------------------------------------------------------------------- -def changeStateNames(root, parentName): +def changeStateNames(xmiModel: XmiModel, root: Node, parentName): stateNames = [] - states = root.findall('state') + print(xmiModel.getStatesList()) + states = "" for state in states: if parentName is not None: newName = parentName + '_' + state.get('name') @@ -329,7 +332,7 @@ def changeStateNames(root, parentName): # # Print the state-machine C file # ----------------------------------------------------------------------- -def generateCode(qmRoot: ElementTreeType, noImpl: bool, noSignals: bool): +def generateCode(xmiModel: XmiModel, noImpl: bool, noSignals: bool): global backend global cFile global hFile @@ -338,10 +341,13 @@ def generateCode(qmRoot: ElementTreeType, noImpl: bool, noSignals: bool): global unitTestTemplate global codeImplTemplate - qmRoot, smname = qmlib.get_state_machine(qmRoot) + qmRoot = xmiModel.tree + smname = qmRoot.stateMachine + + print(f"{qmRoot.name}\n{type(qmRoot.name)}") # Change the state names in the xml to reflect the state hierarchy - changeStateNames(qmRoot, None) + changeStateNames(xmiModel, qmRoot, None) backend = "C Quantum Framework" diff --git a/autocoder/test_backend/testcoder.py b/autocoder/test_backend/testcoder.py old mode 100644 new mode 100755 diff --git a/autocoder/test_backend/testtemplates.py b/autocoder/test_backend/testtemplates.py old mode 100644 new mode 100755 diff --git a/autocoder/xmiModelApi.py b/autocoder/xmiModelApi.py index 9c4bcd7..dd0f11b 100755 --- a/autocoder/xmiModelApi.py +++ b/autocoder/xmiModelApi.py @@ -7,7 +7,7 @@ # mypy: ignore-errors from lxml import etree -from anytree import Node, RenderTree +from anytree import Node, RenderTree, PreOrderIter import anytree import sys from copy import deepcopy @@ -181,6 +181,114 @@ def getStatesList(self, treeNode = tree): thisList = thisList + self.getStatesList(node) return thisList + + def getStates(self): + stateList = [] + + for child in PreOrderIter(self.tree): + if child.name == "STATE": + stateList.append(child.stateName) + + print(stateList) + + return stateList + + def flattenModel(self): + flattenedTransitions = set() + rootNode = self.tree + + #self.addState("State 7", rootNode, None, None, 7) + + for child in rootNode.children: + if child.name == "STATE": + if (self.isSuperstate(child)): + self.flattenSuperstate(child, flattenedTransitions) + + print("break loop") + + break + + def flattenSuperstate(self, superstate: Node, flattenedTransitions: set): + initial = self.getSuperstateInitial(superstate) + + self.retargetAllTransitionsToSuperstate(initial, superstate) + + for child in superstate.children: + if child.name == "TRANSITION": + if (child.target == None): + flattenedTransitions.add(child) + if child.name == "STATE": + if (self.isSuperstate(child)): + print(f"{superstate.stateName} is a superstate. Moving transitions.") + + self.flattenSuperstate(child, flattenedTransitions) + else: + print(f"{child.stateName} is innermost state of {superstate.stateName}") + + for transition in superstate.children: + if transition.name == "TRANSITION": + if transition.target == None: + if (self.inheritTransition(child, transition)): + self.addTransition(child.id, None, transition.event, transition.guard, transition.action, transition.kind, child) + elif transition.source == superstate.id: + self.addTransition(child.id, transition.target, transition.event, transition.guard, transition.action, transition.kind, child) + + child.parent = superstate.parent + + #self.resolveSuperstateTransitions(initial) + + print(f"Removing superstate: {superstate.stateName}") + + superstate.parent = None + + def retargetAllTransitionsToSuperstate(self, initialTransition: Node, superstate: Node): + target = superstate.id + + for child in PreOrderIter(self.tree): + if child.name == "TRANSITION": + if child.target == target: + child.target = initialTransition.target + + def inheritTransition(self, state: Node, transition: Node): + for child in state.children: + if child.name == "TRANSITION": + if (child.event == transition.event): + return False + + return True + + def getSuperstateInitial(self, node: Node): + for child in node.children: + if child.name == "INITIAL": + return child + + def resolveSuperstateTransitions(self, node: Node): + flattenedTransitions = set() + target = node.target + + for child in PreOrderIter(self.tree): + if child.name == "TRANSITION": + if child.target == target: + flattenedTransitions.add(child) + + def isSuperstate(self, node: Node): + if node == None: + print("Node is none. Cannot evaluate children") + + return False + else: + if node.name == "STATE": + for child in node.children: + if child.name == "STATE": + print(f"Substate of {node.stateName} is {child.stateName}") + + return True + else: + print("Node is not a state. Children cannot be evaluated") + + return False + + # -------------------------------------------------------- # print