Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
### [KUKA] KRC5 raw data exchange, coordinate-mirror diagnostics, and auto-mode severity fix ([#1151](https://github.com/Inxton/AXOpen/pull/1151))

**Note:** KRC5-only change in `src/components.kuka.robotics/`. `AxoKrc5` now diverges from `AxoKrc4` in three respects — `AxoKrc4` is intentionally left unchanged in this PR. Fixes #1148.

- feat: `AxoKrc5` exposes raw application-defined passthrough members `DataFromPlcToRobot : ARRAY[0..19] OF BYTE` (PLC → robot, output bytes `_data[44..63]`) and `DataFromRobotToPlc : ARRAY[0..15] OF BYTE` (robot → PLC, input bytes `_data[48..63]`). Both carry `RenderIgnore`; `Run()` transports them verbatim without interpretation. Wrapped in the new `<AxoKrc5DataExchangeDeclaration>` source region.
- feat: `AxoKrc5` adds per-axis coordinate-mirror task-`potential` identifiers `1501–1506` (`StartMotorsProgramAndMovements`) and `1511–1516` (`StartMovements`), raised via `TaskMessenger` while waiting for each `Inputs.Coordinates.{X,Y,Z,Rx,Ry,Rz}` to mirror the commanded value within `0.01` tolerance. Matching `.NET` twin entries added to both the `TaskMessenger` text list and `errorDescriptionDict` in `AxoKrc5.cs`.
- fix: `AxoKrc5` safety message `20002` (`Inputs.Automatic = FALSE` while a task is busy) is now raised as category `Info` instead of `Error` — losing auto mode mid-task is informational on KRC5, not a hard fault.
- feat: Showcase `AxoKrc5_v_5_x_x_Showcase.st` gained an "Exchange raw data with the robot" sequencer step (in `//<DataExchange>` markers); `Steps` widened `[0..19]` → `[0..20]`, `_lastByteFromRobot` status field added.
- docs: `AxoKrc5.md` relaxed the "identical public API to `AxoKrc4`" wording, added a **Data exchange** section (library declaration + showcase usage refs), and a note listing the three KRC5-only differences. `TROUBLES.md` flags the per-class differences (1501–1516, 20002 severity split). Appended `0.54.0` entry to `src/components.kuka.robotics/docs/CHANGELOG.md`.

**Impact:**
- Applications driving a KRC5 can now push and pull arbitrary byte payloads alongside the structured motion interface, without a library change.
- Operators see which specific axis is holding up a movement (per-coordinate `potential` IDs) rather than a single "coordinates not mirrored" wait.
- Dropping out of automatic mode mid-task no longer latches a KRC5 error state.

**Risks/Review:**
- `AxoKrc4` and `AxoKrc5` are no longer API/behaviour-identical. Documentation now states the divergence explicitly; if a follow-up backports these changes to `AxoKrc4`, the "KRC5-only" wording in `AxoKrc5.md` / `TROUBLES.md` must be reverted.
- The PLC→robot (`44..63`) and robot→PLC (`48..63`) windows overlap on the same physical I/O block in opposite directions — verify the controller-side mapping matches before relying on the passthrough.

**Testing:**
- `apax ib` in `src/showcase/app/` — the new KRC5 data-exchange sequencer step builds and runs through to `CompleteSequence`.
- `dotnet build` on `src/showcase/app/ix-blazor/showcase.blazor/` — the `AxoKrc5.cs` messenger/error-dictionary additions compile.
- `scripts/_build_documentation.ps1` — the new `[!code-pascal[]]` (showcase `DataExchange`) and `[!code-smalltalk[]]` (`AxoKrc5DataExchangeDeclaration`) directives resolve.

### [CORE] AxoToggleTaskView aligned with AxoTaskView ([#1143](https://github.com/Inxton/AXOpen/pull/1143))

**Note:** Blazor UI-only change in `src/core/src/AXOpen.Core.Blazor/AxoToggleTask/`. The PLC `AxoToggleTask` class and its public API (`SwitchOn()`, `SwitchOff()`, `Toggle()`, `IsSwitchOn()`, `IsSwitchOff()`, event-like overrides) are unchanged. Bundled with the AxoCmmtAs view expansion under the same PR; the toggle-view refactor is the core-library portion.
Expand Down
97 changes: 96 additions & 1 deletion src/components.kuka.robotics/ctrl/src/AxoKrc5/v_5_x_x/AxoKrc5.st
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,15 @@ NAMESPACE AXOpen.Components.Kuka.Robotics.v_5_x_x
HardwareDiagnosticsTask : AXOpen.Io.AxoHardwareDiagnostics;
END_VAR

// <AxoKrc5DataExchangeDeclaration>
VAR PUBLIC //Data
{#ix-attr:[RenderIgnore()]}
DataFromPlcToRobot : ARRAY[0..19] OF BYTE;
{#ix-attr:[RenderIgnore()]}
DataFromRobotToPlc : ARRAY[0..15] OF BYTE;
END_VAR
// </AxoKrc5DataExchangeDeclaration>

///<summary>
/// Runs tasks and logic of this component.
/// >[!IMPORTANT] This method must or one of its overloads be called cyclically.
Expand Down Expand Up @@ -388,6 +397,23 @@ NAMESPACE AXOpen.Components.Kuka.Robotics.v_5_x_x
_dword.%B2 := _data[46];
_dword.%B3 := _data[47];
Inputs.EventId := TO_UDINT(_dword);

DataFromRobotToPlc[0] := _data[48];
DataFromRobotToPlc[1] := _data[49];
DataFromRobotToPlc[2] := _data[50];
DataFromRobotToPlc[3] := _data[51];
DataFromRobotToPlc[4] := _data[52];
DataFromRobotToPlc[5] := _data[53];
DataFromRobotToPlc[6] := _data[54];
DataFromRobotToPlc[7] := _data[55];
DataFromRobotToPlc[8] := _data[56];
DataFromRobotToPlc[9] := _data[57];
DataFromRobotToPlc[10] := _data[58];
DataFromRobotToPlc[11] := _data[59];
DataFromRobotToPlc[12] := _data[60];
DataFromRobotToPlc[13] := _data[61];
DataFromRobotToPlc[14] := _data[62];
DataFromRobotToPlc[15] := _data[63];
//*******************************************
_context := THIS.GetContext();

Expand Down Expand Up @@ -819,6 +845,30 @@ NAMESPACE AXOpen.Components.Kuka.Robotics.v_5_x_x
TaskMessenger.Activate( UINT#533, eAxoMessageCategory#Potential);
Status.Error.Id := UINT#533;//<#Waiting for Inputs.UserSpecSpeed2 to be equal to MovementParameters.UserSpecSpeed2!
END_IF;
IF NOT AXOpen.Components.Robotics.IsNearlyEqual(Inputs.Coordinates.X,Status.CurrentMovementParameters.Coordinates.X,REAL#0.01) THEN
TaskMessenger.Activate( UINT#1501, eAxoMessageCategory#Potential);
Status.Error.Id := UINT#1501;//<#Waiting for Inputs.Coordinates.X to be equal to MovementParameters.Coordinates.X!
END_IF;
IF NOT AXOpen.Components.Robotics.IsNearlyEqual(Inputs.Coordinates.Y,Status.CurrentMovementParameters.Coordinates.Y,REAL#0.01) THEN
TaskMessenger.Activate( UINT#1502, eAxoMessageCategory#Potential);
Status.Error.Id := UINT#1502;//<#Waiting for Inputs.Coordinates.Y to be equal to MovementParameters.Coordinates.Y!
END_IF;
IF NOT AXOpen.Components.Robotics.IsNearlyEqual(Inputs.Coordinates.Z,Status.CurrentMovementParameters.Coordinates.Z,REAL#0.01) THEN
TaskMessenger.Activate( UINT#1503, eAxoMessageCategory#Potential);
Status.Error.Id := UINT#1503;//<#Waiting for Inputs.Coordinates.Z to be equal to MovementParameters.Coordinates.Z!
END_IF;
IF NOT AXOpen.Components.Robotics.IsNearlyEqual(Inputs.Coordinates.Rx,Status.CurrentMovementParameters.Coordinates.Rx,REAL#0.01) THEN
TaskMessenger.Activate( UINT#1504, eAxoMessageCategory#Potential);
Status.Error.Id := UINT#1504;//<#Waiting for Inputs.Coordinates.Rx to be equal to MovementParameters.Coordinates.Rx!
END_IF;
IF NOT AXOpen.Components.Robotics.IsNearlyEqual(Inputs.Coordinates.Ry,Status.CurrentMovementParameters.Coordinates.Ry,REAL#0.01) THEN
TaskMessenger.Activate( UINT#1505, eAxoMessageCategory#Potential);
Status.Error.Id := UINT#1505;//<#Waiting for Inputs.Coordinates.Ry to be equal to MovementParameters.Coordinates.Ry!
END_IF;
IF NOT AXOpen.Components.Robotics.IsNearlyEqual(Inputs.Coordinates.Rz,Status.CurrentMovementParameters.Coordinates.Rz,REAL#0.01) THEN
TaskMessenger.Activate( UINT#1506, eAxoMessageCategory#Potential);
Status.Error.Id := UINT#1506;//<#Waiting for Inputs.Coordinates.Rz to be equal to MovementParameters.Coordinates.Rz!
END_IF;
END_IF;

IF Inputs.GlobalSpeed = Status.CurrentMovementParameters.GlobalSpeed AND
Expand Down Expand Up @@ -1098,6 +1148,30 @@ NAMESPACE AXOpen.Components.Kuka.Robotics.v_5_x_x
TaskMessenger.Activate( UINT#559, eAxoMessageCategory#Potential);
Status.Error.Id := UINT#559;//<#Waiting for Inputs.UserSpecSpeed2 to be equal to MovementParameters.UserSpecSpeed2!
END_IF;
IF NOT AXOpen.Components.Robotics.IsNearlyEqual(Inputs.Coordinates.X,Status.CurrentMovementParameters.Coordinates.X,REAL#0.01) THEN
TaskMessenger.Activate( UINT#1511, eAxoMessageCategory#Potential);
Status.Error.Id := UINT#1511;//<#Waiting for Inputs.Coordinates.X to be equal to MovementParameters.Coordinates.X!
END_IF;
IF NOT AXOpen.Components.Robotics.IsNearlyEqual(Inputs.Coordinates.Y,Status.CurrentMovementParameters.Coordinates.Y,REAL#0.01) THEN
TaskMessenger.Activate( UINT#1512, eAxoMessageCategory#Potential);
Status.Error.Id := UINT#1512;//<#Waiting for Inputs.Coordinates.Y to be equal to MovementParameters.Coordinates.Y!
END_IF;
IF NOT AXOpen.Components.Robotics.IsNearlyEqual(Inputs.Coordinates.Z,Status.CurrentMovementParameters.Coordinates.Z,REAL#0.01) THEN
TaskMessenger.Activate( UINT#1513, eAxoMessageCategory#Potential);
Status.Error.Id := UINT#1513;//<#Waiting for Inputs.Coordinates.Z to be equal to MovementParameters.Coordinates.Z!
END_IF;
IF NOT AXOpen.Components.Robotics.IsNearlyEqual(Inputs.Coordinates.Rx,Status.CurrentMovementParameters.Coordinates.Rx,REAL#0.01) THEN
TaskMessenger.Activate( UINT#1514, eAxoMessageCategory#Potential);
Status.Error.Id := UINT#1514;//<#Waiting for Inputs.Coordinates.Rx to be equal to MovementParameters.Coordinates.Rx!
END_IF;
IF NOT AXOpen.Components.Robotics.IsNearlyEqual(Inputs.Coordinates.Ry,Status.CurrentMovementParameters.Coordinates.Ry,REAL#0.01) THEN
TaskMessenger.Activate( UINT#1515, eAxoMessageCategory#Potential);
Status.Error.Id := UINT#1515;//<#Waiting for Inputs.Coordinates.Ry to be equal to MovementParameters.Coordinates.Ry!
END_IF;
IF NOT AXOpen.Components.Robotics.IsNearlyEqual(Inputs.Coordinates.Rz,Status.CurrentMovementParameters.Coordinates.Rz,REAL#0.01) THEN
TaskMessenger.Activate( UINT#1516, eAxoMessageCategory#Potential);
Status.Error.Id := UINT#1516;//<#Waiting for Inputs.Coordinates.Rz to be equal to MovementParameters.Coordinates.Rz!
END_IF;
END_IF;

IF Inputs.GlobalSpeed = Status.CurrentMovementParameters.GlobalSpeed AND
Expand Down Expand Up @@ -1702,7 +1776,7 @@ NAMESPACE AXOpen.Components.Kuka.Robotics.v_5_x_x
Status.Error.Id := UINT#20001;
END_IF;
IF(NOT Inputs.Automatic) THEN
Messenger.Activate(UINT#20002,eAxoMessageCategory#Error);
Messenger.Activate(UINT#20002,eAxoMessageCategory#Info);
Status.Error.Id := UINT#20002;
END_IF;
IF(NOT Inputs.AlarmStopActive) THEN
Expand Down Expand Up @@ -1814,6 +1888,27 @@ NAMESPACE AXOpen.Components.Kuka.Robotics.v_5_x_x
_data[42] := _dword.%B2;
_data[43] := _dword.%B3;

_data[44] := DataFromPlcToRobot[0];
_data[45] := DataFromPlcToRobot[1];
_data[46] := DataFromPlcToRobot[2];
_data[47] := DataFromPlcToRobot[3];
_data[48] := DataFromPlcToRobot[4];
_data[49] := DataFromPlcToRobot[5];
_data[50] := DataFromPlcToRobot[6];
_data[51] := DataFromPlcToRobot[7];
_data[52] := DataFromPlcToRobot[8];
_data[53] := DataFromPlcToRobot[9];
_data[54] := DataFromPlcToRobot[10];
_data[55] := DataFromPlcToRobot[11];
_data[56] := DataFromPlcToRobot[12];
_data[57] := DataFromPlcToRobot[13];
_data[58] := DataFromPlcToRobot[14];
_data[59] := DataFromPlcToRobot[15];
_data[60] := DataFromPlcToRobot[16];
_data[61] := DataFromPlcToRobot[17];
_data[62] := DataFromPlcToRobot[18];
_data[63] := DataFromPlcToRobot[19];

_retVal := Siemens.Simatic.DistributedIO.WriteData((Config.HWIDs.HwID_512_DI_DO),_data);
IF _retVal > WORD#0 THEN
Messenger.Activate(UINT#1231, eAxoMessageCategory#Error);
Expand Down
56 changes: 48 additions & 8 deletions src/components.kuka.robotics/docs/AxoKrc5.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,32 @@ inside the component's `Run()` call.

## Relationship to `AxoKrc4`

`AxoKrc5` exposes the **same public API** as [`AxoKrc4`](AxoKrc4.md) — the
tasks, configuration members, status type, error catalogue, and `Run(inParent,
hwID)` signature are identical, and so is the AXOpen slot layout (slot 1
reserved/empty, slot 2 = `DIO512` with 64-byte cyclic I/O). Refer to the
[`AxoKrc4`](AxoKrc4.md) page for:
`AxoKrc5` shares **almost all** of its public API with
[`AxoKrc4`](AxoKrc4.md) — the tasks, configuration members, status type,
`Run(inParent, hwID)` signature, and the AXOpen slot layout (slot 1
reserved/empty, slot 2 = `DIO512` with 64-byte cyclic I/O) are the same.
Refer to the [`AxoKrc4`](AxoKrc4.md) page for:

- The full capabilities list, configuration parameter table, and Config /
HWIDs declarations.
- The .NET twin and Blazor wiring patterns (the patterns transfer 1:1 — only
the type name changes).
- The error-state semantics (programming errors 700/701, hardware bring-up
errors 702/710/720–726/1130–1133, transport errors 1201/1231, runtime
safety errors 20001–20005, task-`potential` IDs in the 500-range).
- The shared error-state semantics (programming errors 700/701, hardware
bring-up errors 702/710/720–726/1130–1133, transport errors 1201/1231,
runtime safety errors 20001–20005, task-`potential` IDs in the 500-range).

> [!NOTE]
> Since the KRC5 fix in **#1148**, `AxoKrc5` has diverged from `AxoKrc4` in
> three KRC5-only respects (none of these are present on `AxoKrc4`):
>
> - It exposes the raw byte-array data-exchange members
> `DataFromPlcToRobot` / `DataFromRobotToPlc` (see [Data exchange](#data-exchange)).
> - It raises additional coordinate-mirror task-`potential` identifiers
> **1501–1506** and **1511–1516** (see the
> [TROUBLES error reference](TROUBLES.md#task-potential-waiting-on-input-identifiers)).
> - Safety message **20002** (`Inputs.Automatic = FALSE` while a task is busy)
> is raised as `Info` on `AxoKrc5`, where `AxoKrc4` still raises it as
> `Error`.

The differences between KRC4 and KRC5 are confined to:

Expand All @@ -48,6 +61,33 @@ each field.

[!code-smalltalk[](../ctrl/src/AxoKrc5/v_5_x_x/TypesStructuresAndEnums/AxoKrc5_HWIDs.st?name=AxoKrc5HWIDsDeclaration)]

## Data exchange

`AxoKrc5` reserves part of the 64-byte cyclic I/O block for raw,
application-defined payloads that pass through the component untouched
(both members carry `RenderIgnore`, so they are excluded from the proxy
view). This is a KRC5-only addition — `AxoKrc4` does not expose these
members.

| Member | Direction | Mapped onto |
|--------|-----------|-------------|
| `DataFromPlcToRobot : ARRAY[0..19] OF BYTE` | PLC → robot | Output bytes `_data[44..63]`, written each cycle in the output-pack phase of `Run()`. |
| `DataFromRobotToPlc : ARRAY[0..15] OF BYTE` | robot → PLC | Input bytes `_data[48..63]`, copied each cycle after the input-unpack phase of `Run()`. |

Write to `DataFromPlcToRobot` and read from `DataFromRobotToPlc` from
application code; the component transports the bytes verbatim and does not
interpret them. The two ranges overlap on the wire because the PLC→robot
window (`44..63`) is wider than the robot→PLC window (`48..63`) — they occupy
the same physical I/O block in opposite directions.

[!code-smalltalk[](../ctrl/src/AxoKrc5/v_5_x_x/AxoKrc5.st?name=AxoKrc5DataExchangeDeclaration)]

The showcase demonstrates the round-trip — writing an application payload to
`DataFromPlcToRobot` and reading the robot's published bytes from
`DataFromRobotToPlc`:

[!code-pascal[](../../showcase/app/src/components.kuka.robotics/Documentation/AxoKrc5_v_5_x_x_Showcase.st?name=DataExchange)]

# [CONTROLLER](#tab/controller)

## Declare component
Expand Down
Loading
Loading