diff --git a/plc4j/drivers/canopen/src/main/java/org/apache/plc4x/java/canopen/protocol/CANOpenProtocolLogic.java b/plc4j/drivers/canopen/src/main/java/org/apache/plc4x/java/canopen/protocol/CANOpenProtocolLogic.java index 9124cab003d..2dadd4d4b27 100644 --- a/plc4j/drivers/canopen/src/main/java/org/apache/plc4x/java/canopen/protocol/CANOpenProtocolLogic.java +++ b/plc4j/drivers/canopen/src/main/java/org/apache/plc4x/java/canopen/protocol/CANOpenProtocolLogic.java @@ -191,6 +191,10 @@ public CompletableFuture write(PlcWriteRequest writeRequest) { writeInternally((DefaultPlcWriteRequest) writeRequest, (CANOpenPDOTag) tag, response); return response; } + if (tag instanceof CANOpenNMTTag) { + writeInternally((DefaultPlcWriteRequest) writeRequest, (CANOpenNMTTag) tag, response); + return response; + } response.completeExceptionally(new IllegalArgumentException("Only CANOpenSDOTag instances are supported")); return response; @@ -238,6 +242,29 @@ private void writeInternally(DefaultPlcWriteRequest writeRequest, CANOpenPDOTag } } + private void writeInternally(DefaultPlcWriteRequest writeRequest, CANOpenNMTTag tag, CompletableFuture response) { + PlcValue writeValue = writeRequest.getPlcValues().get(0); + + NMTStateRequest request = null; + if (writeValue.isInteger() || writeValue.isShort() || writeValue.isByte()) { + request = NMTStateRequest.enumForValue(writeValue.getShort()); + } + if (request == null) { + response.completeExceptionally(new PlcRuntimeException("Unsupported NMTStateRequest value: " + writeValue)); + return; + } + + try { + String tagName = writeRequest.getTagNames().iterator().next(); + + final CANOpenNetworkPayload payload = new CANOpenNetworkPayload(request, Integer.valueOf(tag.getNodeId()).byteValue()); + conversationContext.sendToWire(new CANOpenFrame((short) 0, tag.getService(), payload)); + response.complete(new DefaultPlcWriteResponse(writeRequest, Collections.singletonMap(tagName, PlcResponseCode.OK))); + } catch (Exception e) { + response.completeExceptionally(e); + } + } + public CompletableFuture read(PlcReadRequest readRequest) { CompletableFuture response = new CompletableFuture<>(); if (readRequest.getTagNames().size() != 1) { diff --git a/plc4j/drivers/canopen/src/main/java/org/apache/plc4x/java/canopen/tag/CANOpenNMTTag.java b/plc4j/drivers/canopen/src/main/java/org/apache/plc4x/java/canopen/tag/CANOpenNMTTag.java index 6f93a585cd6..bd8a22e5e20 100644 --- a/plc4j/drivers/canopen/src/main/java/org/apache/plc4x/java/canopen/tag/CANOpenNMTTag.java +++ b/plc4j/drivers/canopen/src/main/java/org/apache/plc4x/java/canopen/tag/CANOpenNMTTag.java @@ -51,12 +51,12 @@ public boolean isWildcard() { @Override public String getAddressString() { - return "NMT:" + getNodeId(); + return isWildcard() ? "NMT" : "NMT:" + getNodeId(); } @Override public PlcValueType getPlcValueType() { - return PlcValueType.NULL; + return PlcValueType.USINT; } @Override diff --git a/plc4j/drivers/canopen/src/test/java/org/apache/plc4x/java/canopen/CANOpenDriverNMTIT.java b/plc4j/drivers/canopen/src/test/java/org/apache/plc4x/java/canopen/CANOpenDriverNMTIT.java new file mode 100644 index 00000000000..8c7eaac300b --- /dev/null +++ b/plc4j/drivers/canopen/src/test/java/org/apache/plc4x/java/canopen/CANOpenDriverNMTIT.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.plc4x.java.canopen; + +import org.apache.plc4x.test.driver.DriverTestsuiteRunner; +import org.junit.jupiter.api.Disabled; + +class CANOpenDriverNMTIT extends DriverTestsuiteRunner { + + public CANOpenDriverNMTIT() { + super("/protocols/canopen/CANOpenDriverNMTIT.xml", true); + } + +} diff --git a/plc4j/drivers/canopen/src/test/java/org/apache/plc4x/java/canopen/CANOpenDriverSDOIT.java b/plc4j/drivers/canopen/src/test/java/org/apache/plc4x/java/canopen/CANOpenDriverSDOIT.java index 764486be76b..61a7b229956 100644 --- a/plc4j/drivers/canopen/src/test/java/org/apache/plc4x/java/canopen/CANOpenDriverSDOIT.java +++ b/plc4j/drivers/canopen/src/test/java/org/apache/plc4x/java/canopen/CANOpenDriverSDOIT.java @@ -21,7 +21,6 @@ import org.apache.plc4x.test.driver.DriverTestsuiteRunner; import org.junit.jupiter.api.Disabled; -@Disabled("Fix this") class CANOpenDriverSDOIT extends DriverTestsuiteRunner { public CANOpenDriverSDOIT() { diff --git a/protocols/canopen/src/test/resources/protocols/canopen/CANOpenDriverNMTIT.xml b/protocols/canopen/src/test/resources/protocols/canopen/CANOpenDriverNMTIT.xml new file mode 100644 index 00000000000..3a12d03cd66 --- /dev/null +++ b/protocols/canopen/src/test/resources/protocols/canopen/CANOpenDriverNMTIT.xml @@ -0,0 +1,122 @@ + + + + + CANOpen SDO Segmentation + + canopen + read-write + + canopen + + + + nodeId + 15 + + + request-timeout + 5000 + + + + + Send NMT write request + + Trigger write using CANOpen NMT requesting specific node to change its state. + + + + + + + boot_up_request +
NMT:1
+ + 128 + +
+
+
+
+ + + 0 + + 0 + + 0 + + + + + 128 + + 0 + 1 + + + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + + + + + + + + + NMT + 1 + + + + 128 + + + + + + + + + + 1 + + + + + 1000 +
+
+ +
\ No newline at end of file diff --git a/protocols/canopen/src/test/resources/protocols/canopen/CANOpenDriverSDOIT.xml b/protocols/canopen/src/test/resources/protocols/canopen/CANOpenDriverSDOIT.xml index af0bbb47a14..d9d19060969 100644 --- a/protocols/canopen/src/test/resources/protocols/canopen/CANOpenDriverSDOIT.xml +++ b/protocols/canopen/src/test/resources/protocols/canopen/CANOpenDriverSDOIT.xml @@ -183,20 +183,26 @@ - - - - - - 1 - 1000 - 22 - UNSIGNED32 - - - - - + + + + + + + + + 1 + 1000 + 22 + UNSIGNED32 + + + + + + + + @@ -312,20 +318,26 @@ - - - - - - 1 - 9186 - 1 - UNSIGNED8 - - - - - + + + + + + + + + 1 + 9186 + 1 + UNSIGNED8 + + + + + + + + @@ -488,20 +500,26 @@ - - - - - - 1 - 1000 - 22 - UNSIGNED32 - - - - - + + + + + + + + + 1 + 1000 + 22 + UNSIGNED32 + + + + + + + + @@ -819,35 +837,32 @@ - - - - - - 2 - 2000 - 44 - RECORD - - - - - + + + + + + + + + 2 + 2000 + 44 + RECORD + + + + + + + + OK - - 0x61 - 0x73 - 0x64 - 0x66 - 0x61 - 0x73 - 0x64 - 0x66 - + 0x6173646661736466 @@ -949,20 +964,26 @@ - - - - - - 1 - 1000 - 22 - UNSIGNED32 - - - - - + + + + + + + + + 1 + 1000 + 22 + UNSIGNED32 + + + + + + + + diff --git a/website/asciidoc/modules/users/pages/protocols/canopen.adoc b/website/asciidoc/modules/users/pages/protocols/canopen.adoc index 771cbedbbcf..5ca106189a5 100644 --- a/website/asciidoc/modules/users/pages/protocols/canopen.adoc +++ b/website/asciidoc/modules/users/pages/protocols/canopen.adoc @@ -125,12 +125,23 @@ Subscriber will be notified with value mapped to type defined in field syntax. | - NMT - NMT:nodeId | - `subscribe` +- `write` | NMT messages are sent using CAN node ID `0`. They have the highest priority on the bus. -Messages of this kind indicate operating state of an node (booted, operational). +Messages of this kind indicate operating state of a node (booted, operational). Subscriptions to this service receive structure with two fields: `node` (USINT) and `state` (USINT). If subscription sets `nodeId` to 0 it will receive state updates for all bus participants. +Supported write values (USINT): + + * `0x01` -> START + * `0x02` -> STOP + * `0x80` -> PRE_OPERATIONAL + * `0x81` -> RESET_NODE + * `0x82` -> RESET_COMMUNICATION + +On application side, for convenience, you can use `NMTStateRequest` enumeration instead of plain numbers. +Be aware that passed number is parsed as an enum, so vendor customization on unused bits are not supported. | HEARTBEAT | - HEARTBEAT