From faccee8fe0601d9aba890b7de57f26236abbb6a9 Mon Sep 17 00:00:00 2001 From: Ivan Maras Date: Thu, 3 Sep 2026 15:21:05 +0200 Subject: [PATCH] Fix code and documentation related to Processing Unit Application handling --- .../sources/models/hardware_inputs.py | 4 +- .../prompts/configurationdesk_prompts.py | 4 +- .../prompts/individual_setup_prompts.py | 9 +- .../sources/resources/domain_resources.py | 4 +- ConfigurationDeskMCP/sources/server/app.py | 4 +- .../services/app_management_service.py | 31 +++++++ .../sources/services/bus_access_service.py | 6 +- .../sources/services/hardware_service.py | 31 +------ .../services/model_topology_service.py | 2 +- .../sources/tools/app_management.py | 23 +++++ .../sources/tools/bus_access.py | 2 +- .../sources/tools/hardware.py | 26 +----- .../sources/tools/model_topology.py | 2 +- ConfigurationDeskMCP/tests/_fake_bridge.py | 2 +- .../tests/domains/test_app_management.py | 5 + .../tests/domains/test_hardware.py | 5 - ConfigurationDeskMCP/tests/mcp_inventory.json | 2 +- .../tests/test_small_model_host_profile.py | 2 +- .../domains/app_management_com.py | 65 +++++++++++++ .../domains/hardware_com.py | 92 ++++--------------- .../domains/model_topology_com.py | 2 +- docs/prompts/tool-map.md | 2 +- docs/small-model-host-profile.md | 2 +- docs/tools/app-management-mcp-tools.md | 1 + docs/tools/hardware-management-mcp-tools.md | 7 +- 25 files changed, 173 insertions(+), 162 deletions(-) diff --git a/ConfigurationDeskMCP/sources/models/hardware_inputs.py b/ConfigurationDeskMCP/sources/models/hardware_inputs.py index 57c1c01..65d16e9 100644 --- a/ConfigurationDeskMCP/sources/models/hardware_inputs.py +++ b/ConfigurationDeskMCP/sources/models/hardware_inputs.py @@ -8,11 +8,11 @@ class AddHardwarePlatformInput(BaseModel): ip_addresses: List[str] = Field( - description="Address(es) of the platform to register, e.g. ['192.0.2.10']. VEOS is NOT a platform - do not use this for VEOS." + description="Address(es) of the platform to register, e.g. ['192.0.2.10']. VEOS is not a real-time hardware platform - do not use this for VEOS." ) platform_type: str = Field( default="SCALEXIO", - description="Platform type: 'SCALEXIO', 'MicroAutoBox III', or 'MicroLabBox II'. VEOS is NOT a platform - use add_application_processing_unit / generate_bus_containers instead.", + description="Platform type: 'SCALEXIO', 'MicroAutoBox III', or 'MicroLabBox II'. VEOS is not a real-time hardware platform - use add_processing_unit_application / generate_bus_containers instead.", ) diff --git a/ConfigurationDeskMCP/sources/prompts/configurationdesk_prompts.py b/ConfigurationDeskMCP/sources/prompts/configurationdesk_prompts.py index 519b471..c075b6f 100644 --- a/ConfigurationDeskMCP/sources/prompts/configurationdesk_prompts.py +++ b/ConfigurationDeskMCP/sources/prompts/configurationdesk_prompts.py @@ -71,8 +71,8 @@ def bus_manager_restbus_simulation( Then `add_model_to_signal_chain` to expose the model ports. ## Step 6 — Provide scheduling -A processing unit must exist. For real hardware, register it with -`add_hardware_platform`; for VEOS/offline, call `add_application_processing_unit`. +A processing unit application must exist. For real hardware, register a platform with +`add_hardware_platform`; for a VEOS/no-hardware build, call `add_processing_unit_application`. Then `create_application_process` (auto-assigned to "{bus_config_name}"). ## Step 7 — Connect the ports diff --git a/ConfigurationDeskMCP/sources/prompts/individual_setup_prompts.py b/ConfigurationDeskMCP/sources/prompts/individual_setup_prompts.py index 9cdd2ab..455e0fa 100644 --- a/ConfigurationDeskMCP/sources/prompts/individual_setup_prompts.py +++ b/ConfigurationDeskMCP/sources/prompts/individual_setup_prompts.py @@ -258,7 +258,7 @@ def create_application_process( ## Prerequisite A ProcessingUnitApplication must exist: register a hardware platform -(`add_hardware_platform`) or, for VEOS/no-hardware, call `add_application_processing_unit`. +(`add_hardware_platform`) or, for VEOS/no-hardware, call `add_processing_unit_application`. ## Step 1 — Create the process (default periodic task) Call `create_application_process` with name="{process_name}". This sets @@ -386,7 +386,7 @@ def build_application( @mcp.prompt( name="register_hardware", - description="Single task: provide the hardware topology — register a SCALEXIO/MicroAutoBox III/MicroLabBox II platform, import an .htfx file, or add a VEOS processing unit", + description="Single task: provide the hardware topology — register a SCALEXIO/MicroAutoBox III/MicroLabBox II platform, import an .htfx file, or add a processing unit application for VEOS", ) def register_hardware( platform_type: str = "SCALEXIO", @@ -410,7 +410,8 @@ def register_hardware( Call `import_hardware_topology` with path="C:/HW/topology.htfx". ### Option C — VEOS / no hardware -Call `add_application_processing_unit`. VEOS is NOT a platform — do NOT call +Call `add_processing_unit_application`. VEOS is not a registered real-time hardware +platform — do NOT call `add_hardware_platform` for it. The deliverable is the generated BSC. ## Verify / maintain @@ -477,7 +478,7 @@ def assign_bus_hardware( ## Prerequisite - A bus configuration with assigned ECUs (see `create_bus_configuration`). - Hardware present: `add_hardware_platform` / `import_hardware_topology`, or - `add_application_processing_unit` for VEOS (then skip channel assignment). + `add_processing_unit_application` for VEOS (then skip channel assignment). ## Step 1 — Inspect what needs hardware Call `list_bus_access_requests` — each cluster/part generates one request to assign. diff --git a/ConfigurationDeskMCP/sources/resources/domain_resources.py b/ConfigurationDeskMCP/sources/resources/domain_resources.py index e50964a..a1a055a 100644 --- a/ConfigurationDeskMCP/sources/resources/domain_resources.py +++ b/ConfigurationDeskMCP/sources/resources/domain_resources.py @@ -91,7 +91,7 @@ ## Hardware Platforms SCALEXIO, MicroAutoBox III, and MicroLabBox II platforms are registered by address and scanned to create the hardware topology. Use `list_platforms` to see -registered hardware. VEOS is NOT a platform — use `add_application_processing_unit`. +registered hardware. VEOS is not a registered real-time hardware platform — use `add_processing_unit_application`. """ _TOOL_CATEGORIES = """\ @@ -597,7 +597,7 @@ - `SCALEXIO` - `MicroAutoBox III` - `MicroLabBox II` -(VEOS is NOT a platform — use `add_application_processing_unit`.) +(VEOS is not a registered real-time hardware platform — use `add_processing_unit_application`.) ## `bus_type` (create_io_function_block) - `CAN` diff --git a/ConfigurationDeskMCP/sources/server/app.py b/ConfigurationDeskMCP/sources/server/app.py index 4535ee2..808f63a 100644 --- a/ConfigurationDeskMCP/sources/server/app.py +++ b/ConfigurationDeskMCP/sources/server/app.py @@ -104,8 +104,8 @@ async def _lifespan(server: FastMCP) -> Any: # type: ignore[type-arg] 13. Hardware topology → ASK USER which approach: - `add_hardware_platform` → register SCALEXIO, MicroAutoBox III, or MicroLabBox II hardware (needs address from user) - `import_hardware_topology` → import .htfx file (needs file path from user) - - `add_application_processing_unit` → no physical hardware / VEOS workflow - - VEOS does NOT need platform registration. Use generate_bus_containers for BSC files. + - `add_processing_unit_application` → no physical hardware / VEOS workflow + - VEOS is not a registered real-time hardware platform; use generate_bus_containers to produce the BSC files it consumes. 14. `create_io_function_block` → create CAN/LIN/Ethernet I/O block 15. `set_io_function_block_property` → set BaudRate 16. `assign_bus_access` → link bus access requests to function block diff --git a/ConfigurationDeskMCP/sources/services/app_management_service.py b/ConfigurationDeskMCP/sources/services/app_management_service.py index 0c4ce44..dd67eac 100644 --- a/ConfigurationDeskMCP/sources/services/app_management_service.py +++ b/ConfigurationDeskMCP/sources/services/app_management_service.py @@ -81,3 +81,34 @@ async def list_applications() -> str: except Exception as e: logger.exception("Error listing applications") return error_response(str(e), transient=False) + + +async def add_processing_unit_application() -> str: + """Add a processing unit application to the executable application. + + A processing unit application hosts one or more application processes. Add one + explicitly when no registered hardware or imported topology already provides + one — typically a no-hardware or VEOS build. + """ + try: + conn = await _get_live_connection() + result = await dispatch(app_management_com.add_processing_unit_application, conn) + pu_created = result.get("processing_unit_created", False) + if not pu_created: + return error_response( + f"ProcessingUnitApplication could not be added: {result.get('processing_unit_detail', '')}. " + "You may need to add it manually in ConfigurationDesk.", + transient=False, + ) + return success_response( + message=( + "ProcessingUnitApplication added. " + "For VEOS: use generate_bus_containers to produce BSC files." + ), + verified=True, + ) + except BridgeError as exc: + return tool_error_result(exc) + except Exception as e: + logger.exception("Error adding processing unit application") + return error_response(str(e), transient=False) diff --git a/ConfigurationDeskMCP/sources/services/bus_access_service.py b/ConfigurationDeskMCP/sources/services/bus_access_service.py index 0f497c6..1b3db83 100644 --- a/ConfigurationDeskMCP/sources/services/bus_access_service.py +++ b/ConfigurationDeskMCP/sources/services/bus_access_service.py @@ -272,7 +272,7 @@ async def auto_assign_channel_set(function_block_name: str, bus_type: str = "CAN f"Function block '{function_block_name}' not found. " f"Verify it exists with list_io_function_block_properties. " f"If the block is LIN, pass bus_type='LIN'. " - f"Also ensure a hardware topology exists via add_application_processing_unit or add_hardware_platform." + f"Also ensure a hardware topology exists (add_hardware_platform or import_hardware_topology), or add a processing unit application (add_processing_unit_application) for a no-hardware build." ), ) return error_response(detail, transient=False) @@ -390,7 +390,7 @@ async def create_preconfigured_application_process(model_name: str) -> str: next_action=( "Pre-configured application process creation failed. " "Verify the model exists in the topology and a ProcessingUnitApplication " - "is available (registered hardware or `add_application_processing_unit`). " + "is available (registered hardware or `add_processing_unit_application`). " "Do NOT retry with the same parameters." ), ) @@ -406,7 +406,7 @@ async def create_preconfigured_application_process(model_name: str) -> str: "No new application process became observable after the pre-configured creation call.", transient=False, next_action=( - "Verify a ProcessingUnitApplication exists (use `add_application_processing_unit` " + "Verify a ProcessingUnitApplication exists (use `add_processing_unit_application` " "for VEOS workflows, or register hardware). Then call `create_application_process` " "as the manual fallback." ), diff --git a/ConfigurationDeskMCP/sources/services/hardware_service.py b/ConfigurationDeskMCP/sources/services/hardware_service.py index 5d8df38..666f4e7 100644 --- a/ConfigurationDeskMCP/sources/services/hardware_service.py +++ b/ConfigurationDeskMCP/sources/services/hardware_service.py @@ -27,7 +27,7 @@ async def add_hardware_platform(ip_addresses: list[str], platform_type: str = "S transient=False, retryable=False, next_action=( - "VEOS is not a hardware platform. For VEOS workflows: " + "VEOS is not a registered real-time hardware platform. For VEOS workflows: " "1) Use generate_bus_containers to create BSC files, " "2) Import BSC files into VEOS. " "For SCALEXIO: ensure hardware is powered on and reachable." @@ -160,32 +160,3 @@ async def add_hardware_element(element_type: str) -> str: except Exception as e: logger.exception("Error adding hardware element") return error_response(str(e), transient=False) - - -async def add_application_processing_unit() -> str: - """Add a ProcessingUnitApplication to the application configuration. - - Used for VEOS-targeted or no-hardware workflows. - """ - try: - conn = get_connection() - result = await dispatch(hardware_com.add_application_processing_unit, conn) - pu_created = result.get("processing_unit_created", False) - if not pu_created: - return error_response( - f"ProcessingUnitApplication could not be added: {result.get('processing_unit_detail', '')}. " - "You may need to add it manually in ConfigurationDesk.", - transient=False, - ) - return success_response( - message=( - "ProcessingUnitApplication added. " - "For VEOS: use generate_bus_containers to produce BSC files." - ), - verified=True, - ) - except BridgeError as exc: - return tool_error_result(exc) - except Exception as e: - logger.exception("Error adding application processing unit") - return error_response(str(e), transient=False) diff --git a/ConfigurationDeskMCP/sources/services/model_topology_service.py b/ConfigurationDeskMCP/sources/services/model_topology_service.py index 6dc31ba..cdecf4b 100644 --- a/ConfigurationDeskMCP/sources/services/model_topology_service.py +++ b/ConfigurationDeskMCP/sources/services/model_topology_service.py @@ -148,7 +148,7 @@ async def create_application_process( "No new application process became observable after the creation call.", transient=False, next_action=( - "Verify a ProcessingUnitApplication exists (use `add_application_processing_unit` " + "Verify a ProcessingUnitApplication exists (use `add_processing_unit_application` " "for VEOS workflows or register a hardware platform), then retry." ), ) diff --git a/ConfigurationDeskMCP/sources/tools/app_management.py b/ConfigurationDeskMCP/sources/tools/app_management.py index f46deef..ee9dbd7 100644 --- a/ConfigurationDeskMCP/sources/tools/app_management.py +++ b/ConfigurationDeskMCP/sources/tools/app_management.py @@ -7,6 +7,7 @@ RemoveApplicationInput, ) from sources.server.app import mcp +from sources.server.preconditions import with_preconditions from sources.services import app_management_service as svc @@ -69,3 +70,25 @@ async def remove_application(input: RemoveApplicationInput) -> str: ) async def list_applications() -> str: return await svc.list_applications() + + +@mcp.tool( + name="add_processing_unit_application", + description=( + "Add a processing unit application to the executable application. " + "A processing unit application is a component of every executable application " + "that hosts one or more application processes. Add one explicitly when no " + "registered hardware or imported topology already provides one — typically a " + "no-hardware or VEOS build. VEOS is not a registered real-time hardware platform; " + "it consumes generated Bus Simulation Containers (BSC)." + ), + annotations={ + "readOnlyHint": False, + "destructiveHint": False, + "idempotentHint": True, + "openWorldHint": False, + }, +) +@with_preconditions("connection", "project", "application") +async def add_processing_unit_application() -> str: + return await svc.add_processing_unit_application() diff --git a/ConfigurationDeskMCP/sources/tools/bus_access.py b/ConfigurationDeskMCP/sources/tools/bus_access.py index 4e6cdd1..3423b7a 100644 --- a/ConfigurationDeskMCP/sources/tools/bus_access.py +++ b/ConfigurationDeskMCP/sources/tools/bus_access.py @@ -387,7 +387,7 @@ async def auto_connect_matching_io_function_blocks_to_model_ports() -> str: "Create a pre-configured application process for one specific model. " "Calls Algorithms.CreatePreConfiguredApplicationProcessAutomatically([model], None). " "A new ProcessingUnitApplication is created automatically when no Parent is supplied " - "(VEOS workflows: ensure `add_application_processing_unit` was called first). " + "(VEOS workflows: ensure `add_processing_unit_application` was called first). " "For all-models behavior, prefer create_application_process or pass `model_names` to it." ), annotations={ diff --git a/ConfigurationDeskMCP/sources/tools/hardware.py b/ConfigurationDeskMCP/sources/tools/hardware.py index a54df21..e51a1e2 100644 --- a/ConfigurationDeskMCP/sources/tools/hardware.py +++ b/ConfigurationDeskMCP/sources/tools/hardware.py @@ -16,8 +16,7 @@ "BEFORE calling this tool, ASK the user which hardware approach they want: " "1) Provide address of SCALEXIO, MicroAutoBox III, or MicroLabBox II hardware → use this tool, " "2) Import an .htfx topology file → use import_hardware_topology, " - "3) Create empty topology (VEOS/no hardware) → use add_application_processing_unit. " - "VEOS is NOT a platform - never call this for VEOS. " + "VEOS is not a registered real-time hardware platform - never call this for VEOS. " "Returns the unique platform name for subsequent hardware operations." ), annotations={ @@ -38,7 +37,7 @@ async def add_hardware_platform( platform_type: Annotated[ str, Field( - description="Platform type: 'SCALEXIO', 'MicroAutoBox III', or 'MicroLabBox II'. VEOS is not a platform.", + description="Platform type: 'SCALEXIO', 'MicroAutoBox III', or 'MicroLabBox II'. VEOS is not a real-time hardware platform.", ), ] = "SCALEXIO", ) -> str: @@ -167,24 +166,3 @@ async def add_hardware_element( ], ) -> str: return await svc.add_hardware_element(element_type) - - -@mcp.tool( - name="add_application_processing_unit", - description=( - "Add a ProcessingUnitApplication to the application configuration. " - "Use this for VEOS-targeted or no-hardware workflows where the project needs " - "an application processing unit to host I/O function blocks and application processes. " - "VEOS does NOT use registered hardware platforms; it consumes generated Bus " - "Simulation Containers (BSC)." - ), - annotations={ - "readOnlyHint": False, - "destructiveHint": False, - "idempotentHint": True, - "openWorldHint": False, - }, -) -@with_preconditions("connection", "project", "application") -async def add_application_processing_unit() -> str: - return await svc.add_application_processing_unit() diff --git a/ConfigurationDeskMCP/sources/tools/model_topology.py b/ConfigurationDeskMCP/sources/tools/model_topology.py index 492f6c3..3b4794d 100644 --- a/ConfigurationDeskMCP/sources/tools/model_topology.py +++ b/ConfigurationDeskMCP/sources/tools/model_topology.py @@ -112,7 +112,7 @@ async def analyze_models() -> str: "Pass `bus_config_names` to scope the assignment to specific configurations, or pass an " "empty list `[]` to skip assignment entirely. " "PRECONDITION: a ProcessingUnitApplication must exist (register a hardware platform or call " - "`add_application_processing_unit` for VEOS workflows)." + "`add_processing_unit_application` for VEOS workflows)." ), annotations={ "readOnlyHint": False, diff --git a/ConfigurationDeskMCP/tests/_fake_bridge.py b/ConfigurationDeskMCP/tests/_fake_bridge.py index afaf0fe..9f00f13 100644 --- a/ConfigurationDeskMCP/tests/_fake_bridge.py +++ b/ConfigurationDeskMCP/tests/_fake_bridge.py @@ -201,7 +201,7 @@ def _create_preconfigured_application_process(model_name, *args): "hardware_items": [etype or "Element"], "verified": True, }, - "add_application_processing_unit": { + "add_processing_unit_application": { "processing_unit_created": True, "processing_unit_detail": "created", }, diff --git a/ConfigurationDeskMCP/tests/domains/test_app_management.py b/ConfigurationDeskMCP/tests/domains/test_app_management.py index 94e0a26..4e10088 100644 --- a/ConfigurationDeskMCP/tests/domains/test_app_management.py +++ b/ConfigurationDeskMCP/tests/domains/test_app_management.py @@ -10,6 +10,7 @@ "activate_application", "remove_application", "list_applications", + "add_processing_unit_application", ) @@ -28,3 +29,7 @@ def test_remove_application(fake_bridge): def test_list_applications(fake_bridge): run_ok(appmgmt_svc.list_applications()) + + +def test_add_processing_unit_application(fake_bridge): + run_ok(appmgmt_svc.add_processing_unit_application()) diff --git a/ConfigurationDeskMCP/tests/domains/test_hardware.py b/ConfigurationDeskMCP/tests/domains/test_hardware.py index 08de93d..e1a4c77 100644 --- a/ConfigurationDeskMCP/tests/domains/test_hardware.py +++ b/ConfigurationDeskMCP/tests/domains/test_hardware.py @@ -8,7 +8,6 @@ COVERS = ( "add_hardware_platform", "add_hardware_element", - "add_application_processing_unit", "import_hardware_topology", "scan_hardware", "remove_hardware", @@ -27,10 +26,6 @@ def test_add_hardware_element(fake_bridge): assert payload["element_name"] == "DS1513" -def test_add_application_processing_unit(fake_bridge): - run_ok(hw_svc.add_application_processing_unit()) - - def test_import_hardware_topology(fake_bridge): run_ok(hw_svc.import_hardware_topology("D:/topology.htfx")) diff --git a/ConfigurationDeskMCP/tests/mcp_inventory.json b/ConfigurationDeskMCP/tests/mcp_inventory.json index 0948042..6a8a99c 100644 --- a/ConfigurationDeskMCP/tests/mcp_inventory.json +++ b/ConfigurationDeskMCP/tests/mcp_inventory.json @@ -2,7 +2,6 @@ "tools": [ "activate_application", "add_application", - "add_application_processing_unit", "add_communication_matrix", "add_feature_to_bus_element", "add_hardware_element", @@ -11,6 +10,7 @@ "add_model", "add_model_port_to_signal_chain", "add_model_to_signal_chain", + "add_processing_unit_application", "analyze_models", "assign_bus_access", "assign_bus_config_to_application_process", diff --git a/ConfigurationDeskMCP/tests/test_small_model_host_profile.py b/ConfigurationDeskMCP/tests/test_small_model_host_profile.py index 57be6c4..c4617b1 100644 --- a/ConfigurationDeskMCP/tests/test_small_model_host_profile.py +++ b/ConfigurationDeskMCP/tests/test_small_model_host_profile.py @@ -39,7 +39,7 @@ "`set_io_function_block_property`", "`add_hardware_platform`", "`import_hardware_topology`", - "`add_application_processing_unit`", + "`add_processing_unit_application`", "`add_model_to_signal_chain`", "`add_model_port_to_signal_chain`", "`create_preconfigured_application_process`", diff --git a/configurationdesk_com_bridge/domains/app_management_com.py b/configurationdesk_com_bridge/domains/app_management_com.py index 71e4be3..1b08eea 100644 --- a/configurationdesk_com_bridge/domains/app_management_com.py +++ b/configurationdesk_com_bridge/domains/app_management_com.py @@ -102,3 +102,68 @@ def list_applications(connection) -> dict[str, Any]: except Exception: pass return {"applications": apps, "active": active} + + +def _create_processing_unit_application(connection) -> tuple[bool, str]: + """Create a ProcessingUnitApplication child under the top ApplicationConfiguration node. + + Returns (created, detail). ``created=False`` means the call did not succeed + but was not fatal. + """ + try: + atm_relation = connection.relations.Item("ApplicationConfiguration") + except Exception as exc: + return False, f"ApplicationConfiguration relation not available: {exc}" + + try: + top_nodes = atm_relation.GetTopNodes() + if top_nodes.Count == 0: + return False, "ApplicationConfiguration has no top-level execution application" + exec_application = top_nodes.Item(0) + except Exception as exc: + return False, f"Cannot read execution application: {exc}" + + # Preferred path: GetCreatableTypes + CreateDataObject (matches COM examples). + try: + creatable = atm_relation.GetCreatableTypes(exec_application) + target_type = None + for idx in range(1, creatable.Count + 1): + cand = creatable.Item(idx) + try: + cand_name = cand.Name + except Exception: + continue + if cand_name == "ProcessingUnitApplication": + target_type = cand + break + if target_type is None and creatable.Count > 0: + target_type = creatable.Item(1) + if target_type is not None: + atm_relation.CreateDataObject(target_type, exec_application) + return True, "Created via GetCreatableTypes/CreateDataObject" + except Exception as exc: + _log.debug("CreateDataObject path failed: %s", exc) + + # Fallback: CreateChild on the execution application using DataObjectTypes. + try: + type_obj = exec_application.DataObjectTypes.Item("ProcessingUnitApplication") + exec_application.CreateChild(type_obj) + return True, "Created via CreateChild" + except Exception as exc: + return False, f"Cannot create ProcessingUnitApplication: {exc}" + + +def add_processing_unit_application(connection) -> dict[str, Any]: + """Add a processing unit application to the executable application. + + A processing unit application is a component of every executable application + that hosts one or more application processes. This adds one explicitly under + the top-level ApplicationConfiguration node, which is needed when no registered + hardware or imported topology already provides one — typically a no-hardware or + VEOS/BSC build. + """ + pu_created, pu_detail = _create_processing_unit_application(connection) + return { + "processing_unit_created": pu_created, + "processing_unit_detail": pu_detail, + } diff --git a/configurationdesk_com_bridge/domains/hardware_com.py b/configurationdesk_com_bridge/domains/hardware_com.py index 5251c2d..e818d67 100644 --- a/configurationdesk_com_bridge/domains/hardware_com.py +++ b/configurationdesk_com_bridge/domains/hardware_com.py @@ -5,13 +5,18 @@ IMPORTANT CONCEPTS: - Hardware platforms (SCALEXIO, MicroAutoBox III, MicroLabBox II) are registered via PlatformManagement.RegisterPlatform and then scanned to create a hardware topology. -- VEOS is NOT a platform. VEOS uses Bus Simulation Containers (BSC) generated by - BusManager.Configure("GenerateContainers", []). BSCs are then imported into VEOS - to build an offline simulation application. -- Hardware topology can be created in three ways: + In ConfigurationDesk a "platform" is a registered dSPACE real-time hardware system. +- VEOS is a PC-based simulation platform, not a registered real-time hardware platform, + so it is not registered here. For VEOS, the Bus Manager generates Bus Simulation + Containers (BSC) via BusManager.Configure("GenerateContainers", []); the BSCs are then + imported into VEOS, which builds the offline simulation application. ConfigurationDesk + itself always builds a real-time application. +- A hardware topology is a separate object from a processing unit application. A hardware + topology can be created in three ways: Mode 0: Scan registered hardware platform Mode 1: Import .htfx file - Mode 2: Create empty topology (for VEOS or no-hardware scenarios) + Mode 2: Create empty topology (no-hardware/VEOS scenarios); a processing unit + application is added separately to host application processes """ from __future__ import annotations @@ -146,20 +151,21 @@ def add_hardware_platform( ) -> dict[str, Any]: """Register and scan a SCALEXIO hardware platform by IP address(es). - VEOS is NOT a platform. For VEOS workflows: + VEOS is not a registered real-time hardware platform. For VEOS workflows: - Use generate_bus_containers to create BSC files - - Import BSC files into VEOS separately - - Or use add_application_processing_unit for an empty topology (no download needed) + - Import the BSC files into VEOS, which builds the offline simulation application + - Or use add_processing_unit_application to add a processing unit application for a + no-hardware build (no download needed) """ if platform_type.upper() == "VEOS": return { "error": True, "detail": ( - "VEOS is not a hardware platform and cannot be registered. " + "VEOS is not a registered real-time hardware platform and cannot be registered here. " "For VEOS workflows: 1) Configure your bus configuration normally, " "2) Call generate_bus_containers to generate BSC files, " - "3) Import the BSC files into VEOS to build an offline simulation application. " - "If you need a hardware topology for the build, use add_application_processing_unit." + "3) Import the BSC files into VEOS, which builds the offline simulation application. " + "If you need a processing unit application for the build, use add_processing_unit_application." ), } @@ -257,70 +263,6 @@ def add_hardware_platform( } -def _create_processing_unit_application(connection) -> tuple[bool, str]: - """Create a ProcessingUnitApplication child under the top ApplicationConfiguration node. - - Returns (created, detail). ``created=False`` means the call did not succeed - but was not fatal — the empty topology was still created. - """ - try: - atm_relation = connection.relations.Item("ApplicationConfiguration") - except Exception as exc: - return False, f"ApplicationConfiguration relation not available: {exc}" - - try: - top_nodes = atm_relation.GetTopNodes() - if top_nodes.Count == 0: - return False, "ApplicationConfiguration has no top-level execution application" - exec_application = top_nodes.Item(0) - except Exception as exc: - return False, f"Cannot read execution application: {exc}" - - # Preferred path: GetCreatableTypes + CreateDataObject (matches COM examples). - try: - creatable = atm_relation.GetCreatableTypes(exec_application) - target_type = None - for idx in range(1, creatable.Count + 1): - cand = creatable.Item(idx) - try: - cand_name = cand.Name - except Exception: - continue - if cand_name == "ProcessingUnitApplication": - target_type = cand - break - if target_type is None and creatable.Count > 0: - target_type = creatable.Item(1) - if target_type is not None: - atm_relation.CreateDataObject(target_type, exec_application) - return True, "Created via GetCreatableTypes/CreateDataObject" - except Exception as exc: - _log.debug("CreateDataObject path failed: %s", exc) - - # Fallback: CreateChild on the execution application using DataObjectTypes. - try: - type_obj = exec_application.DataObjectTypes.Item("ProcessingUnitApplication") - exec_application.CreateChild(type_obj) - return True, "Created via CreateChild" - except Exception as exc: - return False, f"Cannot create ProcessingUnitApplication: {exc}" - - -def add_application_processing_unit(connection) -> dict[str, Any]: - """Add a ProcessingUnitApplication to the application configuration. - - Used for VEOS-targeted workflows or applications with no physical hardware. - A ProcessingUnitApplication is added under the top-level - ApplicationConfiguration node so that I/O function blocks and application - processes can later be hosted by it. - """ - pu_created, pu_detail = _create_processing_unit_application(connection) - return { - "processing_unit_created": pu_created, - "processing_unit_detail": pu_detail, - } - - def import_hardware_topology(connection, path: str) -> dict[str, Any]: """Import hardware topology from an HTFX file.""" abs_path = os.path.abspath(path) diff --git a/configurationdesk_com_bridge/domains/model_topology_com.py b/configurationdesk_com_bridge/domains/model_topology_com.py index 2d50ea9..1ea8039 100644 --- a/configurationdesk_com_bridge/domains/model_topology_com.py +++ b/configurationdesk_com_bridge/domains/model_topology_com.py @@ -148,7 +148,7 @@ def _resolve_processing_unit_application(connection, atm_relation): if top_nodes.Count == 0: return None, ( "ApplicationConfiguration has no top-level executable application. " - "Register a hardware platform or call add_application_processing_unit first." + "Register a hardware platform or call add_processing_unit_application first." ) exec_app = top_nodes.Item(0) except Exception as exc: diff --git a/docs/prompts/tool-map.md b/docs/prompts/tool-map.md index e266e09..2a56a47 100644 --- a/docs/prompts/tool-map.md +++ b/docs/prompts/tool-map.md @@ -87,7 +87,7 @@ The table is checked by `ConfigurationDeskMCP/tests/test_prompt_tool_map.py` so | `list_platforms` | Hardware management | Prompt: `register_hardware` | List registered physical platforms before choosing one to rescan, modify, or remove. | | `refresh_platforms` | Hardware management | Prompt: `register_hardware` | Refresh information for registered platforms when their current status must be checked. | | `add_hardware_element` | Hardware management | [Hardware Management](../tools/hardware-management-mcp-tools.md) | Add the supported hardware element `DS6311` to the active hardware topology only when the required element type is known. | -| `add_application_processing_unit` | Hardware management | Prompt: `register_hardware` | For a VEOS or no-hardware workflow, add an application processing unit; do not register a physical platform. | +| `add_processing_unit_application` | Application management | [Application Management](../tools/app-management-mcp-tools.md) | For a VEOS or no-hardware workflow, add a processing unit application; do not register a physical platform. | | `create_io_function_block` | Bus access | Prompt: `assign_bus_hardware` | After creating `CAN_Restbus`, create physical CAN I/O function block `CAN_Body` for one bus channel. | | `set_io_function_block_property` | Bus access | Prompt: `assign_bus_hardware` | Set hardware I/O property `BaudRate=500000` on CAN function block `CAN_Body`; do not use a bus-feature property tool. | | `list_io_function_block_properties` | Bus access | Prompt: `assign_bus_hardware` | List the supported properties and current values of `CAN_Body` before setting an uncertain hardware I/O value. | diff --git a/docs/small-model-host-profile.md b/docs/small-model-host-profile.md index 21c97c0..46835ef 100644 --- a/docs/small-model-host-profile.md +++ b/docs/small-model-host-profile.md @@ -36,7 +36,7 @@ authoritative. hardware I/O value with `set_io_function_block_property`. - Use `add_hardware_platform` only for physical SCALEXIO, MicroAutoBox III, or MicroLabBox II hardware with an address. Use `import_hardware_topology` for - an `.htfx` file. Use `add_application_processing_unit` for VEOS or a + an `.htfx` file. Use `add_processing_unit_application` for VEOS or a no-hardware workflow. - Use `add_model_to_signal_chain` for every port of one model and `add_model_port_to_signal_chain` only for a named port. Use diff --git a/docs/tools/app-management-mcp-tools.md b/docs/tools/app-management-mcp-tools.md index f4a5be0..f3ade93 100644 --- a/docs/tools/app-management-mcp-tools.md +++ b/docs/tools/app-management-mcp-tools.md @@ -13,6 +13,7 @@ for project-scoped model, matrix, bus, hardware, and build work. | `activate_application` | Make an existing application active. | Safe when the named application is already active. | | `remove_application` | Remove an application from the project. | Destructive; inspect current state first. | | `list_applications` | List applications and their active status. | Read-only. | +| `add_processing_unit_application` | Add a processing unit application (a component of the executable application that hosts application processes). | Requires an active application. | ## Typical Workflow diff --git a/docs/tools/hardware-management-mcp-tools.md b/docs/tools/hardware-management-mcp-tools.md index ed1057c..0aeb31f 100644 --- a/docs/tools/hardware-management-mcp-tools.md +++ b/docs/tools/hardware-management-mcp-tools.md @@ -1,8 +1,8 @@ # Hardware Management Tools -**Domain:** Physical hardware platforms, imported topologies, and no-hardware workflows +**Domain:** Physical hardware platforms and imported topologies -Use this domain to create the topology required by hardware-facing I/O blocks. Choose a physical platform, an imported `.htfx` topology, or a no-hardware path before assigning channels. +Use this domain to create the topology required by hardware-facing I/O blocks. Choose a physical platform or an imported `.htfx` topology before assigning channels. For a no-hardware or VEOS build, add a processing unit application via [Application Management](app-management-mcp-tools.md) instead. ## Tool Contract @@ -15,7 +15,6 @@ Use this domain to create the topology required by hardware-facing I/O blocks. C | `list_platforms` | List registered platforms. | Read-only. | | `refresh_platforms` | Refresh platform information. | May contact physical hardware. | | `add_hardware_element` | Add a generic hardware element to a platform. | Use the runtime schema for supported element values. | -| `add_application_processing_unit` | Add processing support for VEOS or no-hardware workflows. | Do not use a physical-platform tool for this path. | ## Supported Platform Types @@ -25,7 +24,7 @@ Use this domain to create the topology required by hardware-facing I/O blocks. C 1. **Physical hardware:** Call `add_hardware_platform`, then use its returned platform name for later scan or assignment operations. 2. **Existing topology file:** Call `import_hardware_topology` with an `.htfx`path. -3. **VEOS or no physical hardware:** Call `add_application_processing_unit`. +3. **VEOS or no physical hardware:** Add a processing unit application — see [Application Management](app-management-mcp-tools.md). After physical hardware is available, use the Bus Access tools to assign bus I/O function blocks and channel sets.