Skip to content

Commit 92b4b95

Browse files
author
watney
committed
Merge branch 'toFpp' into main
2 parents ecdc763 + 457e17c commit 92b4b95

173 files changed

Lines changed: 13449 additions & 5643 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

autocoder/CameoParser.py

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,26 @@
66
# -----------------------------------------------------------------------
77
# mypy: ignore-errors
88

9-
from lxml import etree
10-
from anytree import Node, RenderTree
11-
import anytree
12-
import sys
9+
from anytree import Node
1310
from copy import deepcopy
1411
import xmiModelApi
15-
import xmiToQm
12+
from typing import Tuple, Dict
13+
14+
15+
from lxml.etree import _ElementTree
16+
ElementTreeType = _ElementTree
17+
from xmiModelApi import XmiModel
18+
19+
1620

1721
# -------------------------------------------------------------------------
1822
# createEventSignalMap
1923
#
2024
# Create a mapping of event id's to signal names and return the map
2125
#
2226
# --------------------------------------------------------------------------
23-
def createEventSignalMap(xmlFileNodeModel, xmlFileNodeStateMachine):
27+
def createEventSignalMap(xmlFileNodeModel: ElementTreeType,
28+
xmlFileNodeStateMachine: ElementTreeType):
2429
global XMI_ID
2530

2631
eventToSignalMap = {}
@@ -51,7 +56,9 @@ def createEventSignalMap(xmlFileNodeModel, xmlFileNodeStateMachine):
5156
#
5257
# Recursively parse the xmi file root and populate the xmiModel
5358
# --------------------------------------------------------------------------
54-
def parseStateTree(xmlFileNode, xmiModelNode):
59+
def parseStateTree(xmlFileNode: ElementTreeType,
60+
xmiModelNode: Node):
61+
5562
global XMI_TYPE
5663
global XMI_ID
5764
global eventToNameMap
@@ -130,83 +137,80 @@ def parseStateTree(xmlFileNode, xmiModelNode):
130137
#
131138
# Recursively parse the input xml File nodes root and populate the xmiModel
132139
# --------------------------------------------------------------------------
133-
def populateXmiModel(xmlFileNode, xmiModel):
140+
def populateXmiModel(xmlRoot: ElementTreeType, xmiModel: XmiModel):
141+
134142
global XMI_TYPE
135143
global XMI_ID
136144
global eventToNameMap
137145

138-
stateMachine = getXmlStateMachine(xmlFileNode)
146+
smRoot: ElementTreeType = getXmlStateMachine(xmlRoot)
139147

140-
eventToNameMap = createEventSignalMap(xmlFileNode, stateMachine)
148+
eventToNameMap = createEventSignalMap(xmlRoot, smRoot)
141149

142-
parseStateTree(stateMachine, xmiModel.getRoot())
150+
parseStateTree(smRoot, xmiModel.getRoot())
143151

144152

145153
#-----------------------------------------------------------------------
146154
# getXmlFileNode
147155
#
148156
# Return the namespace and model element from the input xml file
149157
# -----------------------------------------------------------------------
150-
def getXmlFileNode(xmlfile):
151-
tree = etree.parse(xmlfile)
152-
root = tree.getroot()
158+
def getXmlFileNode(cameoRoot: ElementTreeType) -> Tuple[Dict[str, str], ElementTreeType]:
159+
160+
root = cameoRoot.getroot()
153161
nsmap = root.nsmap
154162
doc = root.find('xmi:Documentation', nsmap)
155163
version = doc.find('xmi:exporterVersion', nsmap)
156164
print("MagicDraw version = {0}".format(version.text))
157165

158166
# Get the Model
159-
model = root.find('uml:Model', nsmap)
167+
xmlRoot = root.find('uml:Model', nsmap)
160168

161169
XMI_TYPE = "{"+nsmap['xmi']+"}type"
162170

163-
return(nsmap, model)
171+
return(nsmap, xmlRoot)
164172

165173
# -----------------------------------------------------------------------------
166174
# getXmlStateMachine
167175
#
168176
# Given the xml File element, find and return the uml:StateMachine xml element
169177
# ------------------------------------------------------------------------------
170-
def getXmlStateMachine(xmlFileNode):
178+
def getXmlStateMachine(xmlRoot: ElementTreeType) -> ElementTreeType:
179+
171180
# Get the StateMachine node from the xml File.
172-
pe = xmlFileNode.findall("packagedElement")
181+
pe = xmlRoot.findall("packagedElement")
173182
for e in pe:
174183
if e.get(XMI_TYPE) == "uml:StateMachine":
175184
return e
176185

177186
# -----------------------------------------------------------------------
178-
# processCameo
187+
# getXmiModel
179188
#
180-
# Process the input CAMEO xmi xml file and return a qm file
189+
# Process the input CAMEO xmi and return an xmiModel
181190
# -----------------------------------------------------------------------
182-
def processCameo(xmlfile, debug):
191+
def getXmiModel(cameoRoot: ElementTreeType) -> XmiModel:
192+
183193
global XMI_ID, XMI_TYPE, xmiModel
184-
print("Hello, welcome to the CAMEO xml parser")
185-
186-
(nsmap, xmlFileNode) = getXmlFileNode(xmlfile)
194+
195+
(nsmap, xmlRoot) = getXmlFileNode(cameoRoot)
187196

188197
XMI_ID = "{"+nsmap['xmi']+"}id"
189198
XMI_TYPE = "{"+nsmap['xmi']+"}type"
190199

191200
#
192201
# Instantiate the xmi model
193202
#
194-
packageName = xmlFileNode.get('name')
195-
stateMachine = getXmlStateMachine(xmlFileNode)
196-
xmiModel = xmiModelApi.xmiModel(packageName, stateMachine.get('name'))
203+
packageName = xmlRoot.get('name')
204+
smRoot = getXmlStateMachine(xmlRoot)
205+
xmiModel = xmiModelApi.XmiModel(packageName, smRoot.get('name'))
197206

198207
#
199208
# Populate the xmi model
200209
#
201-
populateXmiModel(xmlFileNode, xmiModel)
210+
populateXmiModel(xmlRoot, xmiModel)
202211

203-
if debug:
204-
xmiModel.print()
212+
return xmiModel
205213

206-
#
207-
# Convert the xmi model to the qm model
208-
#
209-
return xmiToQm.translateXmiModelToQmFile(xmiModel, debug)
210214

211215

212216

autocoder/QmParser.py

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
#!/usr/bin/env python3
2+
# -----------------------------------------------------------------------
3+
# QmParser.py
4+
#
5+
#
6+
# -----------------------------------------------------------------------
7+
# mypy: ignore-errors
8+
9+
from copy import deepcopy
10+
import xmiModelApi
11+
import flattenstatemachine as flatt
12+
import qmlib
13+
from anytree import Node
14+
from xmiModelApi import XmiModel
15+
16+
from lxml.etree import _ElementTree
17+
ElementTreeType = _ElementTree
18+
19+
20+
class UniqueNumberGenerator:
21+
def __init__(self):
22+
self.generator = self.unique_number_generator() # Create a generator instance
23+
24+
def unique_number_generator(self):
25+
counter = 0
26+
while True:
27+
yield counter
28+
counter += 1
29+
30+
def get_unique_number(self):
31+
return next(self.generator) # Return the next unique number
32+
33+
# -------------------------------------------------------------------------
34+
# parseTrans
35+
#
36+
# Recursively parse a qm transition and populate the xmiModel
37+
# --------------------------------------------------------------------------
38+
def parseTrans(qmTrans: ElementTreeType,
39+
xmiModel: XmiModel,
40+
xmiNode: Node,
41+
number_gen: UniqueNumberGenerator):
42+
43+
source = xmiNode.id
44+
target = int(qmTrans.get('target')) if qmTrans.get('target') else None
45+
kind = qmTrans.get('kind')
46+
guard = qmlib.pick_guard(qmTrans)
47+
action = flatt.pick_action(qmTrans)
48+
event = qmTrans.get('trig')
49+
50+
if target is None:
51+
choices = qmTrans.findall("choice")
52+
if len(choices) == 2:
53+
psId = number_gen.get_unique_number()
54+
psNode = xmiModel.addPsuedostate(psId)
55+
xmiModel.addTransition(source, psId, event, guard, action, kind)
56+
for choice in choices:
57+
parseTrans(choice, xmiModel, psNode, number_gen)
58+
else:
59+
xmiModel.addTransition(source, target, event, guard, action, kind)
60+
else:
61+
xmiModel.addTransition(source, target, event, guard, action, kind, xmiNode.parent)
62+
63+
# -------------------------------------------------------------------------
64+
# parseStateTree
65+
#
66+
# Recursively parse the qm model and populate the xmiModel
67+
# --------------------------------------------------------------------------
68+
def parseStateTree(qmRoot: ElementTreeType,
69+
xmiModel: XmiModel,
70+
xmiNode: Node,
71+
number_gen: UniqueNumberGenerator):
72+
73+
for init in qmRoot.findall("initial"):
74+
psNode = xmiModel.addPsuedostate(number_gen.get_unique_number(), xmiNode)
75+
parseTrans(init, xmiModel, psNode, number_gen)
76+
77+
for tran in qmRoot.findall("tran"):
78+
parseTrans(tran, xmiModel, xmiNode, number_gen)
79+
80+
for state in qmRoot.findall("state"):
81+
stateName = state.get('name')
82+
entry = flatt.pick_entry(state)
83+
exit = flatt.pick_exit(state)
84+
state_id = int(state.get('id'))
85+
thisNode = xmiModel.addState(stateName, xmiNode, entry, exit, state_id)
86+
parseStateTree(state, xmiModel, thisNode, number_gen)
87+
88+
# -------------------------------------------------------------------------
89+
# populateXmiModel
90+
#
91+
# Recursively parse the qm model and populate the xmiModel
92+
# --------------------------------------------------------------------------
93+
def populateXmiModel(qmRoot: ElementTreeType,
94+
smname: str) -> XmiModel:
95+
96+
qmFix = fixQMThing(qmRoot)
97+
number_gen = UniqueNumberGenerator()
98+
99+
xmiModel = xmiModelApi.XmiModel(smname + "Package", smname)
100+
101+
# Add a unique ID to every state
102+
states = qmFix.iter("state")
103+
for state in states:
104+
state.set("id", str(number_gen.get_unique_number()))
105+
106+
# Replace the relative target attributes with ID's
107+
for node in qmFix.iter():
108+
target = node.get("target")
109+
if target is not None:
110+
targetId = flatt.state_from_target(node).get("id")
111+
node.set("target", str(targetId))
112+
113+
# Search for internal transitions. Internal transitions do not have a
114+
# target and do not have an option child. Mark the transition as internal
115+
for tran in qmFix.iter("tran"):
116+
if tran.get('target') is None:
117+
# Look for choice nodes
118+
choices = list(tran.iter("choice"))
119+
if len(choices) == 0:
120+
tran.set("kind", "internal")
121+
122+
parseStateTree(qmFix, xmiModel, xmiModel.tree, number_gen)
123+
124+
return xmiModel
125+
126+
# -----------------------------------------------------------------------
127+
# fixQMThing
128+
#
129+
# The QM modeling tool does not support guards on transitions.
130+
# These are specified in the xml file as a transition to a single
131+
# choice.
132+
#
133+
# This routine makes a fix where it looks for transitions with a single
134+
# choice and then adds the children directly under the transition,
135+
# essentilly moving everything up.
136+
# -----------------------------------------------------------------------
137+
def fixQMThing(qmRoot: ElementTreeType) -> ElementTreeType:
138+
139+
qmFix = deepcopy(qmRoot)
140+
for tran in qmFix.iter("tran"):
141+
choices = tran.findall("choice")
142+
if len(choices) == 1:
143+
144+
choice_children = list(choices[0])
145+
146+
for child in choice_children:
147+
tran.append(child)
148+
149+
# Remove the <choice> node after moving its children
150+
# But first check if the choice had a target attribute
151+
target = choices[0].get('target')
152+
if target is not None:
153+
tran.set('target', target)
154+
tran.remove(choices[0])
155+
156+
# Look for all the targets and move them up as well.
157+
for child in list(tran.iter()):
158+
target = child.get('target')
159+
if target is not None:
160+
child.set('target', target[3:])
161+
162+
return qmFix
163+
164+
165+
# -----------------------------------------------------------------------
166+
# getXmiModel
167+
#
168+
# Process the input qmRoot and return an xmiModel
169+
# -----------------------------------------------------------------------
170+
def getXmiModel(qmRoot: ElementTreeType) -> XmiModel:
171+
172+
smRoot, smname = qmlib.get_state_machine(qmRoot)
173+
174+
xmiModel = populateXmiModel(smRoot, smname)
175+
176+
return xmiModel
177+
178+
179+
180+
181+
182+
183+
184+

0 commit comments

Comments
 (0)