Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion autocoder/Stars.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ def getQmRoot(modelFileName: str) -> Tuple[ElementTreeType, XmiModel] :
exit(0)
else:
fppcoder.generateCode(xmiModel)
fprimecoder.generateCode(qmRoot, args.noImpl, args.namespace)
#fprimecoder.generateCode(qmRoot, args.noImpl, args.namespace)



Expand Down
2 changes: 1 addition & 1 deletion autocoder/UmlParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ def createStateIdMap(parseList: ParserElement) -> Dict[str, int]:
stateIdMap = {}
createStateList(parseList, stateList)
stateList = nodups(stateList)
stateId = 1
stateId = 0
for state in stateList:
stateIdMap[state] = stateId
stateId = stateId + 1
Expand Down
3 changes: 2 additions & 1 deletion autocoder/checkFaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

# -----------------------------------------------------------------------
Expand Down
123 changes: 85 additions & 38 deletions autocoder/fprime_backend/fppcoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,56 @@
from typing import TextIO


def getActionNames(input_string: str):
if input_string is None:
def format_funcs(s: str, fullSpecifier: bool) -> str:
if s 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)
return output_string

parts = [p.strip() for p in s.split(';') if p.strip()]
result = []

for part in parts:
m = re.match(r'(\w+)\s*\(\s*(.*?)\s*\)', part)
if not m:
continue

name, arg = m.groups()
if arg and fullSpecifier:
result.append(f"{name}: {arg}")
else:
result.append(name)

return ", ".join(result)

def dedupe_events(values: set[str]) -> set[str]:
best = {}

for v in values:
if ':' in v:
name, arg = v.split(':', 1)
best[name.strip()] = f"{name.strip()}: {arg.strip()}"
else:
name = v.strip()
# only keep bare name if we haven't seen a version with arg
best.setdefault(name, name)

return set(best.values())

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
Expand All @@ -36,29 +77,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 {{ {format_funcs(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 {{ {format_funcs(child.ifAction, False)} }}" if child.ifAction else ""
doElseExpr = f" do {{ {format_funcs(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 {format_funcs(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 {{ {format_funcs(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 {{ {format_funcs(child.entry, False)} }}" if child.entry else ""
exitExpr = f" exit do {{ {format_funcs(child.exit, False)} }}" if child.exit else ""
fppFile.write(f"{indent}state {stateName} {{\n")
if enterExpr:
fppFile.write(f"{indent}{enterExpr}\n")
Expand Down Expand Up @@ -108,13 +148,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):
Expand All @@ -124,19 +170,20 @@ 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(format_funcs(child.entry, True))
actionSet.add(format_funcs(child.exit, True))
if child.name == "TRANSITION":
actionSet.add(getActionNames(child.action))
guardSet.add(getActionNames(child.guard))
signalSet.add(child.event)
actionSet.add(format_funcs(child.action, True))
guardSet.add(format_funcs(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(format_funcs(child.ifAction, True))
actionSet.add(format_funcs(child.elseAction, True))
guardSet.add(child.guard)
if child.name == "INITIAL":
actionSet.add(getActionNames(child.action))
actionSet.add(format_funcs(child.action, True))

# Remove empty strings
actionSet = {item for item in actionSet if item}
Expand All @@ -146,7 +193,7 @@ def getStateMachineMethods(xmiModel: XmiModel):
flatActions = {a.strip() for action in actionSet for a in action.split(',')}


return (flatActions, guardSet, signalSet)
return (flatActions, guardSet, dedupe_events(signalSet))

# -----------------------------------------------------------------------
# printFpp
Expand All @@ -157,20 +204,20 @@ def generateCode(xmiModel: XmiModel):

stateMachine = xmiModel.tree.stateMachine

print ("Generating " + stateMachine + ".fpp")
print ("Generating " + stateMachine + "_State_Machine.fppi")

fppFile = open(stateMachine +".fpp", "w")
fppFile = open(stateMachine +"_State_Machine.fppi", "w")

currentNode = xmiModel.tree

getInitTransitions(xmiModel)

getJunctions(xmiModel)

moveTransitions(xmiModel)

(actions, guards, signals) = getStateMachineMethods(xmiModel)

moveTransitions(xmiModel)

fppFile.write(f"state machine {xmiModel.tree.stateMachine} {{\n\n")

for action in sorted(actions):
Expand Down
20 changes: 9 additions & 11 deletions autocoder/fprime_backend/fprimeImplTemplates.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 6 additions & 6 deletions autocoder/fprime_backend/fprimecoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}')

Expand Down Expand Up @@ -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)
Empty file modified autocoder/test_backend/testcoder.py
100644 → 100755
Empty file.
Empty file modified autocoder/test_backend/testtemplates.py
100644 → 100755
Empty file.
Loading