diff --git a/autocoder/fprime_backend/fppcoder.py b/autocoder/fprime_backend/fppcoder.py
index 8f6413d..1c2d259 100644
--- a/autocoder/fprime_backend/fppcoder.py
+++ b/autocoder/fprime_backend/fppcoder.py
@@ -22,10 +22,11 @@ def format_funcs(s: str, fullSpecifier: bool) -> str:
continue
name, arg = m.groups()
+ escaped_name = escape_fpp_keyword(name)
if arg and fullSpecifier:
- result.append(f"{name}: {arg}")
+ result.append(f"{escaped_name}: {arg}")
else:
- result.append(name)
+ result.append(escaped_name)
return ", ".join(result)
@@ -61,6 +62,72 @@ def getActionDataType(inputString: str):
return outputString
+# -----------------------------------------------------------------------
+# escape_fpp_keyword
+#
+# Escape FPP reserved keywords to avoid syntax errors.
+# -----------------------------------------------------------------------
+def escape_fpp_keyword(name: str) -> str:
+ """
+ Escape FPP reserved keywords by appending '_state' suffix.
+ FPP reserved keywords are case-sensitive and include: on, state, initial,
+ choice, if, else, do, enter, exit, entry, signal, action, guard, etc.
+ """
+ fpp_keywords = {
+ 'on', 'state', 'initial', 'choice', 'if', 'else',
+ 'do', 'enter', 'exit', 'entry', 'signal', 'action', 'guard'
+ }
+
+ if name in fpp_keywords:
+ return f"{name}_state"
+ return name
+
+# -----------------------------------------------------------------------
+# get_qualified_target
+#
+# Get the qualified target name for a transition.
+# Returns just the stateName if target is at same level or up the hierarchy,
+# otherwise returns qualified path (e.g., Parent.Child) when going down.
+# -----------------------------------------------------------------------
+def get_qualified_target(source_node: Node, target_id, xmiModel: XmiModel) -> str:
+ target_node = xmiModel.idMap[target_id]
+ target_name = target_node.stateName
+
+ # Build list of ancestors from source to root
+ source_ancestors = []
+ node = source_node
+ while node is not None:
+ source_ancestors.append(node)
+ node = node.parent
+
+ # Build list of ancestors from target to root
+ target_ancestors = []
+ node = target_node
+ while node is not None:
+ target_ancestors.append(node)
+ node = node.parent
+
+ # Find common ancestor
+ common_ancestor = None
+ for s_anc in source_ancestors:
+ if s_anc in target_ancestors:
+ common_ancestor = s_anc
+ break
+
+ # Build qualified path from common ancestor down to target
+ path = []
+ node = target_node
+ while node != common_ancestor and node is not None:
+ if hasattr(node, 'stateName'):
+ path.insert(0, escape_fpp_keyword(node.stateName))
+ node = node.parent
+
+ # If path has more than one element, we need qualified notation
+ if len(path) > 1:
+ return ".".join(path)
+ else:
+ return escape_fpp_keyword(target_name)
+
# -----------------------------------------------------------------------
# processNode
#
@@ -81,20 +148,13 @@ def processNode(node: Node,
# Process INITIAL children (typically only one, maintain order)
for child in initials:
- target = xmiModel.idMap[child.target].stateName
+ qualified_target = get_qualified_target(node, child.target, xmiModel)
doExpr = f" do {{ {format_funcs(child.action, False)} }}" if child.action else ""
- fppFile.write(f"{indent}initial{doExpr} enter {target}\n")
+ fppFile.write(f"{indent}initial{doExpr} enter {qualified_target}\n")
- # Process TRANSITION children in sorted order by event name for consistency
- for child in sorted(transitions, key=lambda t: t.event if t.event 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 {{ {format_funcs(child.action, False)} }}" if child.action else ""
- fppFile.write(f"{indent}on {child.event}{guardExpr}{doExpr}{enterExpr}\n")
-
- # Process STATE children in sorted (alphabetical) order for consistency
+ # Process STATE children first (in sorted alphabetical order) to avoid forward references
for child in sorted(states, key=lambda s: s.stateName):
- stateName = child.stateName
+ stateName = escape_fpp_keyword(child.stateName)
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")
@@ -105,14 +165,27 @@ def processNode(node: Node,
processNode(child, xmiModel, fppFile, level+1)
fppFile.write(f"{indent}}}\n\n")
+ # Process TRANSITION children after states (in sorted order by event name for consistency)
+ for child in sorted(transitions, key=lambda t: t.event if t.event else ""):
+ guardExpr = f" if {format_funcs(child.guard, False)}" if child.guard else ""
+ if child.kind is None:
+ qualified_target = get_qualified_target(node, child.target, xmiModel)
+ enterExpr = f" enter {qualified_target}"
+ else:
+ enterExpr = ""
+ doExpr = f" do {{ {format_funcs(child.action, False)} }}" if child.action else ""
+ fppFile.write(f"{indent}on {child.event}{guardExpr}{doExpr}{enterExpr}\n")
+
# Process JUNCTION children last (maintain order, typically referenced by transitions)
for child in junctions:
- ifTarget = xmiModel.idMap[child.ifTarget].stateName
- elseTarget = xmiModel.idMap[child.elseTarget].stateName
+ ifTarget = get_qualified_target(node, child.ifTarget, xmiModel)
+ elseTarget = get_qualified_target(node, child.elseTarget, xmiModel)
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")
+ guardExpr = format_funcs(child.guard, False) if child.guard else child.guard
+ junctionName = escape_fpp_keyword(child.stateName)
+ fppFile.write(f"{indent}choice {junctionName} {{\n")
+ fppFile.write(f"{indent} if {guardExpr}{doIfExpr} enter {ifTarget} else{doElseExpr} enter {elseTarget}\n")
fppFile.write(f"{indent}}}\n")
# -----------------------------------------------------------------------
@@ -189,7 +262,7 @@ def getStateMachineMethods(xmiModel: XmiModel):
if child.name == "JUNCTION":
actionSet.add(format_funcs(child.ifAction, True))
actionSet.add(format_funcs(child.elseAction, True))
- guardSet.add(child.guard)
+ guardSet.add(format_funcs(child.guard, False))
if child.name == "INITIAL":
actionSet.add(format_funcs(child.action, True))
@@ -234,15 +307,18 @@ def generateCode(xmiModel: XmiModel):
fppFile.write(f"state machine {xmiModel.tree.stateMachine} {{\n\n")
for action in sorted(actions):
- fppFile.write(f" action {action}\n")
+ escaped_action = escape_fpp_keyword(action.split(':')[0].strip()) + (f": {action.split(':', 1)[1].strip()}" if ':' in action else "")
+ fppFile.write(f" action {escaped_action}\n")
fppFile.write("\n")
for guard in sorted(guards):
- fppFile.write(f" guard {guard}\n")
+ escaped_guard = escape_fpp_keyword(guard)
+ fppFile.write(f" guard {escaped_guard}\n")
fppFile.write("\n")
for signal in sorted(signals):
- fppFile.write(f" signal {signal}\n")
+ escaped_signal = escape_fpp_keyword(signal.split(':')[0].strip()) + (f": {signal.split(':', 1)[1].strip()}" if ':' in signal else "")
+ fppFile.write(f" signal {escaped_signal}\n")
fppFile.write("\n")
processNode(currentNode, xmiModel, fppFile, 1)
diff --git a/debug/cpp/Simple.h b/debug/cpp/Simple.h
new file mode 100644
index 0000000..218ac85
--- /dev/null
+++ b/debug/cpp/Simple.h
@@ -0,0 +1,30 @@
+
+#ifndef SIMPLE_H_
+#define SIMPLE_H_
+
+// forward declaration of EventSignal
+struct EventSignal;
+
+class Simple {
+ public:
+
+ enum SimpleStates {
+ S1,
+ S2,
+ };
+
+ enum SimpleEvents {
+ EV1_SIG,
+ };
+
+ enum SimpleStates state;
+
+ void init();
+ void update(EventSignal *e);
+
+ // state machine implementation functions
+ void s1Entry();
+
+};
+
+#endif
diff --git a/debug/cpp/sendEvent.h b/debug/cpp/sendEvent.h
new file mode 100644
index 0000000..5cccca1
--- /dev/null
+++ b/debug/cpp/sendEvent.h
@@ -0,0 +1,13 @@
+
+#ifndef _SEND_EVENT_H
+#define _SEND_EVENT_H
+
+class Simple;
+
+typedef struct EventSignal {
+ unsigned int sig;
+} EventSignal;
+
+void sendEvent_send(unsigned int signal);
+
+#endif
diff --git a/debug/cpp/testDrv.h b/debug/cpp/testDrv.h
new file mode 100644
index 0000000..c91d72c
--- /dev/null
+++ b/debug/cpp/testDrv.h
@@ -0,0 +1,7 @@
+
+#ifndef _TEST_DRV_H_
+#define _TEST_DRV_H_
+
+void testDrv();
+
+#endif
diff --git a/models/TestModels/Actions/README.adoc b/models/TestModels/Actions/README.adoc
index 6f5e22a..888dcf4 100644
--- a/models/TestModels/Actions/README.adoc
+++ b/models/TestModels/Actions/README.adoc
@@ -1,16 +1,2 @@
image::Actions.png[]
-
-* Run the autocoder and build the executable model for C and QF
-** make
-
-* Run the unit tests for C and QF
-** make ut
-
-* Run the autocoder and build executable model for C
-** make C
-
-* Run the autocoder and build executable model for QF
-** make QF
-
-
diff --git a/models/TestModels/Arg_Actions/.Arg_Actions b/models/TestModels/Arg_Actions/.Arg_Actions
index 6721ba7..2286e47 100644
--- a/models/TestModels/Arg_Actions/.Arg_Actions
+++ b/models/TestModels/Arg_Actions/.Arg_Actions
@@ -9,7 +9,7 @@
- 0
- - 0,0,1274,925,*
+ - 0,0,2254,1340,*
- 2032128
diff --git a/models/TestModels/Arg_Actions/README.adoc b/models/TestModels/Arg_Actions/README.adoc
index 68e1bb9..7f0337c 100644
--- a/models/TestModels/Arg_Actions/README.adoc
+++ b/models/TestModels/Arg_Actions/README.adoc
@@ -1,16 +1,2 @@
image::Arg_Actions.png[]
-
-* Run the autocoder and build the executable model for C and QF
-** make
-
-* Run the unit tests for C and QF
-** make ut
-
-* Run the autocoder and build executable model for C
-** make C
-
-* Run the autocoder and build executable model for QF
-** make QF
-
-
diff --git a/models/TestModels/Arg_Actions_FP/Arg_Actions_FP.plantuml b/models/TestModels/Arg_Actions_FP/Arg_Actions_FP.plantuml
new file mode 100644
index 0000000..f401fac
--- /dev/null
+++ b/models/TestModels/Arg_Actions_FP/Arg_Actions_FP.plantuml
@@ -0,0 +1,20 @@
+
+@startuml
+
+[*] --> S1
+
+state S1 {
+ S1:Entry: s1Entry(); s1Entry2(); foo()
+ S1:Exit: s1Exit(); s1Exit2(); foo()
+}
+
+state S2 {
+ S2:Entry: s2Entry(); s2Entry2(); foo()
+ S2:Exit: s2Exit(); s2Exit2()
+}
+
+S1 --> S2: EV1[g1(U16)]
+S1 --> S2: EV2[g2()]
+S2 --> S1: EV1/a1(U16); a2(); foo()
+
+@enduml
diff --git a/models/TestModels/Arg_Actions_FP/testDrv.txt b/models/TestModels/Arg_Actions_FP/testDrv.txt
new file mode 100644
index 0000000..7ca0826
--- /dev/null
+++ b/models/TestModels/Arg_Actions_FP/testDrv.txt
@@ -0,0 +1,14 @@
+ g1 = false
+ EV1
+
+ g1 = true
+ EV1
+
+ EV1
+
+ g2 = false
+ EV2
+
+ g2 = true
+ EV2
+
diff --git a/models/TestModels/Cameo/README.adoc b/models/TestModels/Cameo/README.adoc
index 56f560d..86d787b 100644
--- a/models/TestModels/Cameo/README.adoc
+++ b/models/TestModels/Cameo/README.adoc
@@ -52,18 +52,3 @@ StateS1 --> Running: Ev2/a2()
....
image::CameoUML.png[]
-
-
-* Run the autocoder and build the executable model for C and QF
-** make
-
-* Run the unit tests for C and QF
-** make ut
-
-* Run the autocoder and build executable model for C
-** make C
-
-* Run the autocoder and build executable model for QF
-** make QF
-
-
diff --git a/models/TestModels/Cases/.Cases b/models/TestModels/Cases/.Cases
deleted file mode 100644
index 87ad2ea..0000000
--- a/models/TestModels/Cases/.Cases
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
-
-
-
- - 1
- - 0
- - 3
- - 0
-
-
- - 0,0,1434,1224,*
-
-
- - 2032128
- - 0
-
-
-
-
- - 0
-
-
-
-
- - 0
-
-
- - 0
-
-
-
-
- - 0
-
-
- - 0
-
-
-
-
- - 0
-
-
- - 0
-
-
-
-
- - 0
-
-
- - 0
-
-
-
-
- - 0
-
-
-
diff --git a/models/TestModels/Cases/README.adoc b/models/TestModels/Cases/README.adoc
index 811f93f..9f02292 100644
--- a/models/TestModels/Cases/README.adoc
+++ b/models/TestModels/Cases/README.adoc
@@ -1,16 +1,2 @@
image::Cases.png[]
-
-* Run the autocoder and build the executable model for C and QF
-** make
-
-* Run the unit tests for C and QF
-** make ut
-
-* Run the autocoder and build executable model for C
-** make C
-
-* Run the autocoder and build executable model for QF
-** make QF
-
-
diff --git a/models/TestModels/Complex_Composite/.Complex_Composite b/models/TestModels/Complex_Composite/.Complex_Composite
deleted file mode 100644
index 4395734..0000000
--- a/models/TestModels/Complex_Composite/.Complex_Composite
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
-
-
-
- - 1
- - 0
- - 3
- - 0
-
-
- - 0,0,2071,1192,*
-
-
- - 2032128
- - 0
-
-
-
-
- - 0
-
-
-
-
- - 0
-
-
- - 0
-
-
-
-
- - 0
-
-
- - 0
-
-
-
-
- - 0
-
-
- - 0
-
-
-
-
- - 0
-
-
- - 0
-
-
-
-
- - 0
-
-
-
diff --git a/models/TestModels/Complex_Composite/README.adoc b/models/TestModels/Complex_Composite/README.adoc
index f82d600..7c36ce8 100644
--- a/models/TestModels/Complex_Composite/README.adoc
+++ b/models/TestModels/Complex_Composite/README.adoc
@@ -1,16 +1,2 @@
image::Complex_Composite.png[]
-
-* Run the autocoder and build the executable model for C and QF
-** make
-
-* Run the unit tests for C and QF
-** make ut
-
-* Run the autocoder and build executable model for C
-** make C
-
-* Run the autocoder and build executable model for QF
-** make QF
-
-
diff --git a/models/TestModels/Complex_Junction/README.adoc b/models/TestModels/Complex_Junction/README.adoc
index c45598f..d49b871 100644
--- a/models/TestModels/Complex_Junction/README.adoc
+++ b/models/TestModels/Complex_Junction/README.adoc
@@ -1,16 +1,2 @@
image::Complex_Junction.png[]
-
-* Run the autocoder and build the executable model for C and QF
-** make
-
-* Run the unit tests for C and QF
-** make ut
-
-* Run the autocoder and build executable model for C
-** make C
-
-* Run the autocoder and build executable model for QF
-** make QF
-
-
diff --git a/models/TestModels/Multiple_Actions/README.adoc b/models/TestModels/Multiple_Actions/README.adoc
index a2a3cd1..a3400d7 100644
--- a/models/TestModels/Multiple_Actions/README.adoc
+++ b/models/TestModels/Multiple_Actions/README.adoc
@@ -1,16 +1,2 @@
image::Multiple_Actions.png[]
-
-* Run the autocoder and build the executable model for C and QF
-** make
-
-* Run the unit tests for C and QF
-** make ut
-
-* Run the autocoder and build executable model for C
-** make C
-
-* Run the autocoder and build executable model for QF
-** make QF
-
-
diff --git a/models/TestModels/Simple/.Simple b/models/TestModels/Simple/.Simple
deleted file mode 100644
index 8ea6874..0000000
--- a/models/TestModels/Simple/.Simple
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
-
-
-
- - 1
- - 0
- - 3
- - 0
-
-
- - 0,0,2481,1217,*
-
-
- - 2032128
- - 0
-
-
-
-
- - 0
-
-
-
-
- - 0
-
-
- - 0
-
-
-
-
- - 0
-
-
- - 0
-
-
-
-
- - 0
-
-
- - 0
-
-
-
-
- - 0
-
-
- - 0
-
-
-
-
- - 0
-
-
-
diff --git a/models/TestModels/Simple/README.adoc b/models/TestModels/Simple/README.adoc
index cfa7e0a..29c0849 100644
--- a/models/TestModels/Simple/README.adoc
+++ b/models/TestModels/Simple/README.adoc
@@ -1,16 +1,2 @@
image::Simple.png[]
-
-* Run the autocoder and build the executable model for C and QF
-** make
-
-* Run the unit tests for C and QF
-** make ut
-
-* Run the autocoder and build executable model for C
-** make C
-
-* Run the autocoder and build executable model for QF
-** make QF
-
-
diff --git a/models/TestModels/String_Guards/README.adoc b/models/TestModels/String_Guards/README.adoc
index c45598f..d49b871 100644
--- a/models/TestModels/String_Guards/README.adoc
+++ b/models/TestModels/String_Guards/README.adoc
@@ -1,16 +1,2 @@
image::Complex_Junction.png[]
-
-* Run the autocoder and build the executable model for C and QF
-** make
-
-* Run the unit tests for C and QF
-** make ut
-
-* Run the autocoder and build executable model for C
-** make C
-
-* Run the autocoder and build executable model for QF
-** make QF
-
-
diff --git a/models/TestModels/Transitions/README.adoc b/models/TestModels/Transitions/README.adoc
index 3d7b655..f8f6ddd 100644
--- a/models/TestModels/Transitions/README.adoc
+++ b/models/TestModels/Transitions/README.adoc
@@ -1,16 +1,2 @@
image::Transitions.png[]
-
-* Run the autocoder and build the executable model for C and QF
-** make
-
-* Run the unit tests for C and QF
-** make ut
-
-* Run the autocoder and build executable model for C
-** make C
-
-* Run the autocoder and build executable model for QF
-** make QF
-
-
diff --git a/models/TestModels/golden/Arg_Actions/fprime.fppi b/models/TestModels/golden/Arg_Actions_FP/fprime.fppi
similarity index 88%
rename from models/TestModels/golden/Arg_Actions/fprime.fppi
rename to models/TestModels/golden/Arg_Actions_FP/fprime.fppi
index 33f9c27..baa1ac5 100644
--- a/models/TestModels/golden/Arg_Actions/fprime.fppi
+++ b/models/TestModels/golden/Arg_Actions_FP/fprime.fppi
@@ -1,6 +1,6 @@
-state machine Arg_Actions {
+state machine Arg_Actions_FP {
- action a1: e
+ action a1: U16
action a2
action foo
action s1Entry
@@ -15,7 +15,7 @@ state machine Arg_Actions {
guard g1
guard g2
- signal EV1: e
+ signal EV1: U16
signal EV2
initial enter S1
diff --git a/models/TestModels/golden/Cameo/fprime.fppi b/models/TestModels/golden/Cameo/fprime.fppi
index f7c1093..3909f53 100644
--- a/models/TestModels/golden/Cameo/fprime.fppi
+++ b/models/TestModels/golden/Cameo/fprime.fppi
@@ -12,10 +12,10 @@ state machine Cameo {
action s3Entry
action s3Exit
- guard guard()
guard guard2
- guard guard3()
- guard guard4()
+ guard guard3
+ guard guard4
+ guard guard_state
signal Ev1
signal Ev2
@@ -28,10 +28,6 @@ state machine Cameo {
entry do { runningEntry }
exit do { runningExit }
initial enter J2
- on Ev1 if guard2 do { a1 } enter StateS1
- on Ev3 enter J1
- on Ev4 do { a4 } enter J1
- on Ev5 enter Running
state StateS2 {
entry do { s2Entry }
exit do { s2Exit }
@@ -42,8 +38,12 @@ state machine Cameo {
exit do { s3Exit }
}
+ on Ev1 if guard2 do { a1 } enter StateS1
+ on Ev3 enter J1
+ on Ev4 do { a4 } enter J1
+ on Ev5 enter Running
choice J2 {
- if guard4() enter StateS3 else enter StateS2
+ if guard4 enter StateS3 else enter StateS2
}
}
@@ -54,9 +54,9 @@ state machine Cameo {
}
choice J0 {
- if guard() enter StateS1 else enter Running
+ if guard_state enter StateS1 else enter Running
}
choice J1 {
- if guard3() enter StateS1 else do { a2 } enter Running
+ if guard3 enter StateS1 else do { a2 } enter Running
}
}
diff --git a/models/TestModels/golden/Cases/fprime.fppi b/models/TestModels/golden/Cases/fprime.fppi
index 9fa03fa..29c829b 100644
--- a/models/TestModels/golden/Cases/fprime.fppi
+++ b/models/TestModels/golden/Cases/fprime.fppi
@@ -10,7 +10,7 @@ state machine Cases {
action OnExit
action offexit
- guard G1()
+ guard G1
signal EV1
signal ev2
@@ -20,27 +20,27 @@ state machine Cases {
entry do { OFFENTRY }
exit do { offexit }
initial enter Diag
- on EV1 enter J0
state Diag {
entry do { DiagEntry }
exit do { DiagExit }
}
+ on EV1 enter J0
}
- state on {
+ state on_state {
entry do { OnEntry }
exit do { OnExit }
initial enter Init
- on ev2 enter OFF
state Init {
entry do { InitEntry }
exit do { InitExit }
}
+ on ev2 enter OFF
}
choice J0 {
- if G1() enter OFF else do { A1 } enter on
+ if G1 enter OFF else do { A1 } enter on_state
}
}
diff --git a/models/TestModels/golden/Complex_Composite/fprime.fppi b/models/TestModels/golden/Complex_Composite/fprime.fppi
index c27f238..f695867 100644
--- a/models/TestModels/golden/Complex_Composite/fprime.fppi
+++ b/models/TestModels/golden/Complex_Composite/fprime.fppi
@@ -29,7 +29,6 @@ state machine Complex_Composite {
entry do { s1Entry }
exit do { s1Exit }
initial enter S11
- on Ev1 enter S2
state S11 {
entry do { s11Entry }
exit do { s11Exit }
@@ -38,14 +37,14 @@ state machine Complex_Composite {
entry do { s111Entry }
exit do { s111Exit }
initial enter s1111
- on Ev1 enter S12
state s1111 {
entry do { s1111Entry }
exit do { s1111Exit }
- on Ev2 enter S21
- on Ev3 enter S121
+ on Ev2 enter S2.S21
+ on Ev3 enter S12.S121
}
+ on Ev1 enter S12
}
}
@@ -54,27 +53,28 @@ state machine Complex_Composite {
entry do { s12Entry }
exit do { s12Exit }
initial enter S121
- on Ev2 enter S11
state S121 {
entry do { s121Entry }
exit do { s121Exit }
- on Ev4 enter S111
+ on Ev4 enter S11.S111
}
+ on Ev2 enter S11
}
+ on Ev1 enter S2
}
state S2 {
entry do { s2Entry }
exit do { s2Exit }
initial enter S21
- on Ev1 enter S1
state S21 {
entry do { s21Entry }
exit do { s21Exit }
}
+ on Ev1 enter S1
}
}
diff --git a/models/TestModels/golden/Complex_Junction/fprime.fppi b/models/TestModels/golden/Complex_Junction/fprime.fppi
index 5c949e4..0480807 100644
--- a/models/TestModels/golden/Complex_Junction/fprime.fppi
+++ b/models/TestModels/golden/Complex_Junction/fprime.fppi
@@ -15,9 +15,9 @@ state machine Complex_Junction {
action a5
action a6
- guard g1()
- guard g2()
- guard g3()
+ guard g1
+ guard g2
+ guard g3
signal Ev1
@@ -26,33 +26,33 @@ state machine Complex_Junction {
entry do { OffEntry }
exit do { OffExit }
initial enter Diag
- on Ev1 enter J0
state Diag {
entry do { DiagEntry }
exit do { DiagExit }
}
+ on Ev1 enter J0
}
state ON {
entry do { OnEntry }
exit do { OnExit }
initial enter Init
- on Ev1 enter OFF
state Init {
entry do { InitEntry }
exit do { InitExit }
}
+ on Ev1 enter OFF
}
choice J0 {
- if g1() do { a1 } enter OFF else do { a2 } enter J1
+ if g1 do { a1 } enter OFF else do { a2 } enter J1
}
choice J1 {
- if g2() do { a5 } enter ON else do { a3 } enter J2
+ if g2 do { a5 } enter ON else do { a3 } enter J2
}
choice J2 {
- if g3() do { a6 } enter ON else do { a4 } enter OFF
+ if g3 do { a6 } enter ON else do { a4 } enter OFF
}
}
diff --git a/models/TestModels/golden/Simple_Composite/fprime.fppi b/models/TestModels/golden/Simple_Composite/fprime.fppi
index e6cade9..73ed918 100644
--- a/models/TestModels/golden/Simple_Composite/fprime.fppi
+++ b/models/TestModels/golden/Simple_Composite/fprime.fppi
@@ -18,7 +18,6 @@ state machine Simple_Composite {
entry do { s1Entry }
exit do { s1Exit }
initial enter S11
- on Ev1 enter S2
state S11 {
entry do { s11Entry }
exit do { s11Exit }
@@ -31,6 +30,7 @@ state machine Simple_Composite {
on Ev2 enter S11
}
+ on Ev1 enter S2
}
state S2 {
diff --git a/models/TestModels/golden/Simple_Junction/fprime.fppi b/models/TestModels/golden/Simple_Junction/fprime.fppi
index 2586f3f..220fe96 100644
--- a/models/TestModels/golden/Simple_Junction/fprime.fppi
+++ b/models/TestModels/golden/Simple_Junction/fprime.fppi
@@ -10,7 +10,7 @@ state machine Simple_Junction {
action OnExit
action a1
- guard g1()
+ guard g1
signal Ev1
@@ -19,27 +19,27 @@ state machine Simple_Junction {
entry do { OffEntry }
exit do { OffExit }
initial enter Diag
- on Ev1 enter J0
state Diag {
entry do { DiagEntry }
exit do { DiagExit }
}
+ on Ev1 enter J0
}
state ON {
entry do { OnEntry }
exit do { OnExit }
initial enter Init
- on Ev1 enter OFF
state Init {
entry do { InitEntry }
exit do { InitExit }
}
+ on Ev1 enter OFF
}
choice J0 {
- if g1() enter OFF else do { a1 } enter ON
+ if g1 enter OFF else do { a1 } enter ON
}
}
diff --git a/models/TestModels/golden/String_Guards/fprime.fppi b/models/TestModels/golden/String_Guards/fprime.fppi
index a6d2288..9e493f0 100644
--- a/models/TestModels/golden/String_Guards/fprime.fppi
+++ b/models/TestModels/golden/String_Guards/fprime.fppi
@@ -15,11 +15,11 @@ state machine String_Guards {
action farExit
action initialAction
- guard g1()
- guard g2()
- guard g3()
+ guard g1
+ guard g2
+ guard g3
guard g4
- guard g5()
+ guard g5
signal Ev1
signal Ev2
@@ -27,35 +27,35 @@ state machine String_Guards {
initial do { initialAction } enter OFF
state OFF {
initial enter Far
- on Ev1 enter J0
- on Ev2 if g4 do { a5 } enter J3
state Far {
entry do { farEntry }
exit do { farExit }
}
+ on Ev1 enter J0
+ on Ev2 if g4 do { a5 } enter J3
}
state ON {
initial enter Bar
- on Ev2 enter OFF
state Bar {
entry do { barEntry }
exit do { barExit }
}
+ on Ev2 enter OFF
}
choice J0 {
- if g1() do { b2 } enter J1 else do { a1 } enter OFF
+ if g1 do { b2 } enter J1 else do { a1 } enter OFF
}
choice J1 {
- if g2() do { b3 } enter J2 else do { a2 } enter OFF
+ if g2 do { b3 } enter J2 else do { a2 } enter OFF
}
choice J2 {
- if g3() do { b4 } enter ON else do { a3 } enter OFF
+ if g3 do { b4 } enter ON else do { a3 } enter OFF
}
choice J3 {
- if g5() do { a4 } enter OFF else do { a6 } enter ON
+ if g5 do { a4 } enter OFF else do { a6 } enter ON
}
}
diff --git a/models/TestModels/golden/Transitions/fprime.fppi b/models/TestModels/golden/Transitions/fprime.fppi
index 231e503..0380db7 100644
--- a/models/TestModels/golden/Transitions/fprime.fppi
+++ b/models/TestModels/golden/Transitions/fprime.fppi
@@ -30,18 +30,18 @@ state machine Transitions {
entry do { s1Entry }
exit do { s1Exit }
initial enter S11
- on Ev1 enter S1
- on Ev2 do { a1 }
- on Ev3 if g1 do { a2 }
- on Ev5 if g2 enter S1
- on Ev6 if g3 do { a4 } enter S1
- on Ev7 enter S2
state S11 {
entry do { s11Entry }
exit do { s11Exit }
on Ev4 do { a3 } enter S1
}
+ on Ev1 enter S1
+ on Ev2 do { a1 }
+ on Ev3 if g1 do { a2 }
+ on Ev5 if g2 enter S1
+ on Ev6 if g3 do { a4 } enter S1
+ on Ev7 enter S2
}
state S2 {
diff --git a/models/TestModels/pytest.ini b/models/TestModels/pytest.ini
index 3b790e8..c70b124 100644
--- a/models/TestModels/pytest.ini
+++ b/models/TestModels/pytest.ini
@@ -10,6 +10,7 @@ markers =
Simple_Junction: tests for Simple_Junction model
Actions: tests for Actions model
Arg_Actions: tests for Arg_Actions model
+ Arg_Actions_FP: tests for Arg_Actions_FP model
Cases: tests for Cases model
Complex_Composite: tests for Complex_Composite model
Complex_Junction: tests for Complex_Junction model
diff --git a/models/TestModels/tests/test_autocoder.py b/models/TestModels/tests/test_autocoder.py
index 295cad3..8a643a0 100644
--- a/models/TestModels/tests/test_autocoder.py
+++ b/models/TestModels/tests/test_autocoder.py
@@ -12,7 +12,8 @@
build_c_backend,
build_cpp_backend,
build_qf_backend,
- run_test_executable
+ run_test_executable,
+ validate_fpp_file
)
@@ -143,6 +144,14 @@ def substitute_vars(value):
output_file = tmp_path / f"{model_base}_State_Machine.fppi"
if not output_file.exists():
pytest.fail(f"Fprime output file not found: {output_file}")
+
+ # Validate FPP syntax with fpp-check
+ print("Running fpp-check...")
+ try:
+ validate_fpp_file(output_file)
+ except RuntimeError as e:
+ pytest.fail(str(e))
+
output = output_file.read_text()
else:
diff --git a/models/TestModels/tests/test_config.yaml b/models/TestModels/tests/test_config.yaml
index 98a254f..8562594 100644
--- a/models/TestModels/tests/test_config.yaml
+++ b/models/TestModels/tests/test_config.yaml
@@ -40,6 +40,21 @@ models:
Arg_Actions:
<<: *model_template
# Any model-specific overrides can be added here
+ backends: [c, c++, qf]
+ test_driver: testDrv.txt # ${model_name}/testDrv.txt
+ golden:
+ c: "${model_name}/c.txt" # golden/${model_name}/c.txt
+ c++: "${model_name}/cpp.txt" # golden/${model_name}/cpp.txt
+ qf: "${model_name}/qf.txt" # golden/${model_name}/qf.txt
+
+ # Arg_Actions_FP model
+ Arg_Actions_FP:
+ inputs:
+ plantuml: "${model_name}.plantuml" # ${model_name}/${model_name}.plantuml
+ backends: [fprime]
+ test_driver: testDrv.txt # ${model_name}/testDrv.txt
+ golden:
+ fprime: "${model_name}/fprime.fppi" # golden/${model_name}/fprime.fppi
# Complex_Composite model
Complex_Composite:
diff --git a/models/TestModels/tests/utils.py b/models/TestModels/tests/utils.py
index 31a7167..e397e36 100644
--- a/models/TestModels/tests/utils.py
+++ b/models/TestModels/tests/utils.py
@@ -175,3 +175,29 @@ def run_test_executable(tmp_path):
text=True
)
return result.stdout
+
+
+def validate_fpp_file(fppi_file):
+ """
+ Run fpp-check on a generated .fppi file to validate syntax.
+
+ Args:
+ fppi_file: Path to the .fppi file to check
+
+ Raises:
+ RuntimeError: If fpp-check fails with validation errors
+ """
+ result = subprocess.run(
+ ["fpp-check", str(fppi_file)],
+ capture_output=True,
+ text=True
+ )
+
+ if result.returncode != 0:
+ raise RuntimeError(
+ f"fpp-check validation failed for {fppi_file.name}\n"
+ f"stdout: {result.stdout}\n"
+ f"stderr: {result.stderr}"
+ )
+
+ print(f"✓ fpp-check passed for {fppi_file.name}")