From e626140a8142772b6d0cdeb74a709a55599da1f2 Mon Sep 17 00:00:00 2001 From: watney Date: Tue, 26 May 2026 17:29:30 -0700 Subject: [PATCH 1/2] Fix the fprime_backend to pass the fpp-check on all the models --- autocoder/fprime_backend/fppcoder.py | 118 ++++++++++++++---- debug/cpp/Simple.h | 30 +++++ debug/cpp/sendEvent.h | 13 ++ debug/cpp/testDrv.h | 7 ++ models/TestModels/Arg_Actions/.Arg_Actions | 2 +- .../Arg_Actions_FP/Arg_Actions_FP.plantuml | 20 +++ models/TestModels/Arg_Actions_FP/testDrv.txt | 14 +++ models/TestModels/Cases/.Cases | 61 --------- .../Complex_Composite/.Complex_Composite | 61 --------- models/TestModels/Simple/.Simple | 61 --------- .../fprime.fppi | 6 +- models/TestModels/golden/Cameo/fprime.fppi | 20 +-- models/TestModels/golden/Cases/fprime.fppi | 10 +- .../golden/Complex_Composite/fprime.fppi | 14 +-- .../golden/Complex_Junction/fprime.fppi | 16 +-- .../golden/Simple_Composite/fprime.fppi | 2 +- .../golden/Simple_Junction/fprime.fppi | 8 +- .../golden/String_Guards/fprime.fppi | 22 ++-- .../TestModels/golden/Transitions/fprime.fppi | 12 +- models/TestModels/pytest.ini | 1 + models/TestModels/tests/test_autocoder.py | 11 +- models/TestModels/tests/test_config.yaml | 15 +++ models/TestModels/tests/utils.py | 26 ++++ 23 files changed, 289 insertions(+), 261 deletions(-) create mode 100644 debug/cpp/Simple.h create mode 100644 debug/cpp/sendEvent.h create mode 100644 debug/cpp/testDrv.h create mode 100644 models/TestModels/Arg_Actions_FP/Arg_Actions_FP.plantuml create mode 100644 models/TestModels/Arg_Actions_FP/testDrv.txt delete mode 100644 models/TestModels/Cases/.Cases delete mode 100644 models/TestModels/Complex_Composite/.Complex_Composite delete mode 100644 models/TestModels/Simple/.Simple rename models/TestModels/golden/{Arg_Actions => Arg_Actions_FP}/fprime.fppi (88%) 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/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_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/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/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/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/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}") From e5c1b102326d4ae896167eeabca6c19c0208e07e Mon Sep 17 00:00:00 2001 From: watney Date: Tue, 26 May 2026 17:33:16 -0700 Subject: [PATCH 2/2] Cleanup the model README.adoc files --- models/TestModels/Actions/README.adoc | 14 -------------- models/TestModels/Arg_Actions/README.adoc | 14 -------------- models/TestModels/Cameo/README.adoc | 15 --------------- models/TestModels/Cases/README.adoc | 14 -------------- models/TestModels/Complex_Composite/README.adoc | 14 -------------- models/TestModels/Complex_Junction/README.adoc | 14 -------------- models/TestModels/Multiple_Actions/README.adoc | 14 -------------- models/TestModels/Simple/README.adoc | 14 -------------- models/TestModels/String_Guards/README.adoc | 14 -------------- models/TestModels/Transitions/README.adoc | 14 -------------- 10 files changed, 141 deletions(-) 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/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/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/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/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/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 - -