Skip to content
Open
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 @@ -187,7 +187,7 @@ def getQmRoot(modelFileName: str) -> Tuple[ElementTreeType, XmiModel] :
cppcoder.generateCode(xmiModel, args.noImpl)

if args.backend == "c":
ccoder.generateCode(qmRoot, args.noImpl)
ccoder.generateCode(xmiModel, args.noImpl)

if args.backend == "qf":
qfcoder.generateCode(xmiModel, args.noImpl, args.noSignals)
Expand Down
266 changes: 229 additions & 37 deletions autocoder/c_backend/ccoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@
from c_backend.ctemplates import CTemplate
from c_backend.cUnitTestTemplates import CUnitTestTemplate
from c_backend.cImplTemplates import CImplTemplate
from typing import List, Dict, Tuple, Any, Optional, IO
from typing import List, Dict, Tuple, Any, Optional, IO, TextIO
from qmlib import ElementTreeType
from lxml import etree
import re
from xmiModelApi import XmiModel
from anytree import Node, PreOrderIter


# Initialize global variables
Expand All @@ -32,23 +35,90 @@
#
# 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")
hFile = open(f"{stateMachine}.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)
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)

#eventList = []
#trans = root.iter('tran')
#for tran in trans:
#event = tran.get('trig').upper() + "_SIG"
#if event not in eventList:
#eventList.append(event)

#stateList = []
#states = root.iter('state')
#for state in states:
#stateList.append(state.get('name'))

hFile.write(codeTemplate.fileHeader(smname, stateList, eventList))
hFile.write(codeTemplate.fileHeader(stateMachine, states, signals))

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)


# ---------------------------------------------------------------------------
Expand All @@ -58,6 +128,38 @@ def printSmHeader(smname: str, root: ElementTreeType):
# ---------------------------------------------------------------------------
def formatTarget(targ: str) -> str:
return codeTemplate.target(targ)

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

# ---------------------------------------------------------------------------
# printTransition
Expand Down Expand Up @@ -117,7 +219,33 @@ def printTransition(smname: str, tran: ElementTreeType) -> List[str]:

return rstr


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

# ---------------------------------------------------------------------------
# printStateTransition
Expand All @@ -135,20 +263,41 @@ def printStateTransition(smname: str, tran: ElementTreeType, cFile: IO):
#
# Print the state-machine C file
# -----------------------------------------------------------------------
def printSmCode(smname: str, root: ElementTreeType):
def printSmCode(smname: str, xmiModel: XmiModel, cFile: TextIO, level = 1):
stateMachine = xmiModel.tree.stateMachine

states = getStates(xmiModel)

defaultIndent = " "

indent = defaultIndent * level

cFile = open(smname + ".c", "w")
#cFile = open(smname + ".c", "w")

initialTran = root.find('initial')
initialCode = qmlib.format_C(printTransition(smname, initialTran), 4)
cFile.write(codeTemplate.stateMachineInit(smname, initialCode))
#initialTran = root.find('initial')
#initialCode = qmlib.format_C(printTransition(smname, initialTran), 4)
#cFile.write(codeTemplate.stateMachineInit(smname, initialCode))

states = root.iter("state")
#states = root.iter("state")
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.get('name')))
cFile.write(codeTemplate.stateMachineState(state))

#trans = state.findall('tran')
#for tran in trans:
#printStateTransition(smname, tran, cFile)

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}self->sm.state = {resolveTransition(xmiModel, child, states)};" if child.kind is None else ""
action = f"\n{indent}{stateMachine}Impl_{getActionNames(child.action, False)}(self);" 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())

Expand Down Expand Up @@ -275,34 +424,77 @@ def printImplCode(smname: str, root: ElementTreeType):
# everything else is only important for the tool developer.

# -----------------------------------------------------------------------
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(stateMachine))

print ("Generating flat C code for {0}".format(smname))
#flatchart : ElementTreeType = flatt.flatten_state_machine(qmRoot)

flatchart : ElementTreeType = flatt.flatten_state_machine(qmRoot)
xmiModel.getInitTransitions()

xmiModel.getJunctions()

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

xmiModel.moveTransitions()

#xmiModel.print()

cFile = open(f"{stateMachine}.c", "w")

if noImpl == False:
# Generate the Impl files
print ("Generating " + smname + "Impl.c")
print ("Generating " + smname + "Impl.h")
printImplCode(smname, flatchart)
print ("Generating " + stateMachine + "Impl.c")
print ("Generating " + stateMachine + "Impl.h")
#printImplCode(StateMachine, flatchart)

# Generate the unit test files
print ("Generating main.c")
print ("Generating sendEvent.h")
print ("Generating sendEvent.c")

printUnitCode(smname, flatchart)
#printUnitCode(smname, flatchart)

# Generate the header file
print ("Generating " + smname + ".c")
printSmHeader(smname, flatchart)
print ("Generating " + stateMachine + ".c")
printSmHeader(xmiModel)

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 = " " + stateMachine + "Impl_" + child.entry + ";"

initialCode = initialCode + "\n self->sm.state = " + target + ";"

break

xmiModel.flattenModel()

cFile.write(codeTemplate.stateMachineInit(stateMachine, initialCode))

# Generate the C file
print ("Generating " + smname + ".h")
printSmCode(smname, flatchart)
print ("Generating " + stateMachine + ".h")
printSmCode(stateMachine, xmiModel, cFile)
Loading