From dab8efe18f3a0ac36ee41549a2c1748c994ff46d Mon Sep 17 00:00:00 2001
From: Marco Lampacrescia <marco.lampacrescia@de.bosch.com>
Date: Wed, 9 Oct 2024 17:07:06 +0200
Subject: [PATCH] Check type of BtTickChild id

Signed-off-by: Marco Lampacrescia <marco.lampacrescia@de.bosch.com>
---
 .../scxml_converter/scxml_entries/bt_utils.py |  5 +++
 .../scxml_entries/scxml_bt_ticks.py           | 36 +++++++++++++++++--
 .../scxml_entries/scxml_transition.py         | 18 +++++-----
 3 files changed, 48 insertions(+), 11 deletions(-)

diff --git a/src/as2fm/scxml_converter/scxml_entries/bt_utils.py b/src/as2fm/scxml_converter/scxml_entries/bt_utils.py
index 00fda812..eb4cb222 100644
--- a/src/as2fm/scxml_converter/scxml_entries/bt_utils.py
+++ b/src/as2fm/scxml_converter/scxml_entries/bt_utils.py
@@ -44,6 +44,11 @@ def str_to_int(resp_str: str) -> int:
         raise ValueError(f"Error: {resp_str} is an invalid BT Status type.")
 
 
+def generate_bt_tick_event(instance_id: str) -> str:
+    """Generate the BT tick event name for a given BT node instance."""
+    return f"bt_{instance_id}_tick"
+
+
 def is_bt_event(event_name: str) -> bool:
     """Given an event name, returns whether it is related to a BT event or not."""
     bt_events = [f"bt_{suffix}" for suffix in ["tick", "running", "success", "failure"]]
diff --git a/src/as2fm/scxml_converter/scxml_entries/scxml_bt_ticks.py b/src/as2fm/scxml_converter/scxml_entries/scxml_bt_ticks.py
index 1b6b9b85..f2c1b3cb 100644
--- a/src/as2fm/scxml_converter/scxml_entries/scxml_bt_ticks.py
+++ b/src/as2fm/scxml_converter/scxml_entries/scxml_bt_ticks.py
@@ -17,17 +17,20 @@
 SCXML entries related to Behavior Tree Ticks and related responses.
 """
 
-from typing import Optional, Union
+from typing import List, Optional, Union
 
 from lxml import etree as ET
 
 from as2fm.scxml_converter.scxml_entries import (
     ScxmlExecutionBody,
+    ScxmlIf,
     ScxmlSend,
     ScxmlTransition,
     execution_body_from_xml,
+    instantiate_exec_body_bt_events,
 )
-from as2fm.scxml_converter.scxml_entries.bt_utils import BtResponse
+from as2fm.scxml_converter.scxml_entries.bt_utils import BtResponse, generate_bt_tick_event
+from as2fm.scxml_converter.scxml_entries.utils import is_non_empty_string
 from as2fm.scxml_converter.scxml_entries.xml_utils import assert_xml_tag_ok, get_xml_argument
 
 
@@ -56,6 +59,14 @@ def __init__(
     ):
         super().__init__(target, ["bt_tick"], condition, body)
 
+    def check_validity(self) -> bool:
+        return super().check_validity()
+
+    def instantiate_bt_events(self, instance_id: int, children_ids: List[int]) -> ScxmlTransition:
+        self._events = [generate_bt_tick_event(instance_id)]
+        instantiate_exec_body_bt_events(self._body, instance_id, children_ids)
+        return ScxmlTransition(self._target, self._events, self._condition, self._body)
+
     def as_xml(self) -> ET.Element:
         xml_bt_tick = ET.Element(BtTick.get_tag_name(), {"target": self._target})
         if self._condition is not None:
@@ -84,6 +95,27 @@ def __init__(self, child_id: Union[str, int]):
             child_id, (str, int)
         ), f"Error: SCXML BT Tick Child: invalid child id type {type(child_id)}."
         self._child = child_id
+        if isinstance(child_id, str):
+            child_id = child_id.strip()
+            try:
+                self._child = int(child_id)
+            except ValueError:
+                self._child = child_id
+                assert is_non_empty_string(BtTickChild, "id", self._child)
+                assert (
+                    self._child.isidentifier()
+                ), f"Error: SCXML BT Tick Child: invalid child id '{self._child}'."
+
+    def check_validity(self) -> bool:
+        return True
+
+    def instantiate_bt_events(
+        self, instance_id: int, children_ids: List[int]
+    ) -> Union[ScxmlIf, ScxmlSend]:
+        """
+        Convert the BtTickChild to ScxmlSend if the child id is constant and an ScxmlIf otherwise.
+        """
+        raise NotImplementedError("Error: SCXML BT Tick Child: instantiation not implemented.")
 
     def as_xml(self) -> ET.Element:
         xml_bt_tick_child = ET.Element(BtTickChild.get_tag_name(), {"id": str(self._child)})
diff --git a/src/as2fm/scxml_converter/scxml_entries/scxml_transition.py b/src/as2fm/scxml_converter/scxml_entries/scxml_transition.py
index f3613db5..6b65fe86 100644
--- a/src/as2fm/scxml_converter/scxml_entries/scxml_transition.py
+++ b/src/as2fm/scxml_converter/scxml_entries/scxml_transition.py
@@ -39,7 +39,11 @@
     valid_execution_body,
     valid_execution_body_entry_types,
 )
-from as2fm.scxml_converter.scxml_entries.utils import CallbackType, get_plain_expression
+from as2fm.scxml_converter.scxml_entries.utils import (
+    CallbackType,
+    get_plain_expression,
+    is_non_empty_string,
+)
 
 
 class ScxmlTransition(ScxmlBase):
@@ -145,22 +149,18 @@ def append_body_executable_entry(self, exec_entry: ScxmlExecutableEntry):
         ), "Error SCXML transition: invalid body entry found after extension."
 
     def check_validity(self) -> bool:
-        valid_target = isinstance(self._target, str) and len(self._target) > 0
+        valid_target = is_non_empty_string(type(self), "target", self._target)
+        valid_condition = self._condition is None or (
+            is_non_empty_string(type(self), "condition", self._condition)
+        )
         valid_events = self._events is None or (
             isinstance(self._events, list) and all(isinstance(ev, str) for ev in self._events)
         )
-        valid_condition = self._condition is None or (
-            isinstance(self._condition, str) and len(self._condition) > 0
-        )
         valid_body = self._body is None or valid_execution_body(self._body)
-        if not valid_target:
-            print("Error: SCXML transition: target is not valid.")
         if not valid_events:
             print("Error: SCXML transition: events are not valid.\nList of events:")
             for event in self._events:
                 print(f"\t-'{event}'.")
-        if not valid_condition:
-            print("Error: SCXML transition: condition is not valid.")
         if not valid_body:
             print("Error: SCXML transition: executable content is not valid.")
         return valid_target and valid_events and valid_condition and valid_body