This is an automated email from the ASF dual-hosted git repository. sruehl pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/plc4x.git
commit ce8cff4d2f71d47771aebcd0ea0c26b2b6ae10eb Author: Sebastian Rühl <[email protected]> AuthorDate: Mon Jul 6 15:24:24 2026 +0200 fix(plc4j): align serial transport options with the plc4go vocabulary Remove the unused break-enabled, receive-buffer-size and send-buffer-size options, drop the RTS_CTS_XON_XOFF combined flow-control mode, and reject invalid parity/flow-control values with a TransportException naming the option instead of silently falling back to defaults. Values are now case-insensitive with "-"/"_" interchangeable (canonical: none, odd, even, mark, space; none, rts-cts, xon-xoff). Website config-doc partials regenerate from the updated annotations at the next site build. --- RELEASE_NOTES | 7 +++ .../transport/serial/SerialTransportInstance.java | 51 +++++++-------- .../config/SerialTransportConfiguration.java | 36 ++--------- .../serial/SerialTransportInstanceTest.java | 72 ++++++++++++++++------ .../java/transport/serial/SerialTransportTest.java | 2 +- .../config/SerialTransportConfigurationTest.java | 40 ------------ 6 files changed, 93 insertions(+), 115 deletions(-) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 8278a7be72..dffd8b8e0e 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -34,6 +34,13 @@ Incompatible changes (default 1000 ms; set to 0 for the previous blocking behavior), and invalid serial option values now fail connection creation instead of being silently ignored. +- The Java serial transport removed the unused options + "break-enabled", "receive-buffer-size" and "send-buffer-size", + removed the combined "RTS_CTS_XON_XOFF" flow-control mode, and now + rejects invalid parity/flow-control values instead of silently + falling back to defaults. Option values are case-insensitive and + accept "-" or "_" as separator (canonical forms: none, odd, even, + mark, space; none, rts-cts, xon-xoff). - The 'plc4x' proxy driver now defaults to the TLS transport instead of plaintext TCP. Existing plaintext connections must switch to an explicit transport prefix (e.g. "plc4x:tcp://..."). diff --git a/plc4j/transports/serial/src/main/java/org/apache/plc4x/java/transport/serial/SerialTransportInstance.java b/plc4j/transports/serial/src/main/java/org/apache/plc4x/java/transport/serial/SerialTransportInstance.java index 961a46fffb..05422834a7 100644 --- a/plc4j/transports/serial/src/main/java/org/apache/plc4x/java/transport/serial/SerialTransportInstance.java +++ b/plc4j/transports/serial/src/main/java/org/apache/plc4x/java/transport/serial/SerialTransportInstance.java @@ -33,6 +33,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.OutputStream; +import java.util.Locale; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; @@ -187,39 +188,41 @@ public class SerialTransportInstance extends BaseTransportInstance<SerialTranspo } /** - * Converts parity string to jSerialComm constant. + * Converts a parity option value to the matching jSerialComm constant. + * Values are case-insensitive and accept "-" or "_" as separator. */ - private int parseParity(String parity) { - return switch (parity.toUpperCase()) { - case "NONE" -> SerialPort.NO_PARITY; - case "ODD" -> SerialPort.ODD_PARITY; - case "EVEN" -> SerialPort.EVEN_PARITY; - case "MARK" -> SerialPort.MARK_PARITY; - case "SPACE" -> SerialPort.SPACE_PARITY; - default -> { - LOGGER.warn("Unknown parity '{}', using NONE", parity); - yield SerialPort.NO_PARITY; - } + static int parseParity(String parity) throws TransportException { + return switch (normalizeOptionValue(parity)) { + case "none" -> SerialPort.NO_PARITY; + case "odd" -> SerialPort.ODD_PARITY; + case "even" -> SerialPort.EVEN_PARITY; + case "mark" -> SerialPort.MARK_PARITY; + case "space" -> SerialPort.SPACE_PARITY; + default -> throw new TransportException( + "Invalid value '" + parity + "' for option 'parity' (must be one of: none, odd, even, mark, space)"); }; } /** - * Converts flow control string to jSerialComm constant. + * Converts a flow-control option value to the matching jSerialComm + * constant. Values are case-insensitive and accept "-" or "_" as + * separator. Combining hardware and software flow control is not + * supported (matching the plc4go serial transport). */ - private int parseFlowControl(String flowControl) { - return switch (flowControl.toUpperCase()) { - case "NONE" -> SerialPort.FLOW_CONTROL_DISABLED; - case "RTS_CTS", "RTSCTS" -> SerialPort.FLOW_CONTROL_RTS_ENABLED | SerialPort.FLOW_CONTROL_CTS_ENABLED; - case "XON_XOFF", "XONXOFF" -> SerialPort.FLOW_CONTROL_XONXOFF_IN_ENABLED | SerialPort.FLOW_CONTROL_XONXOFF_OUT_ENABLED; - case "RTS_CTS_XON_XOFF" -> SerialPort.FLOW_CONTROL_RTS_ENABLED | SerialPort.FLOW_CONTROL_CTS_ENABLED | - SerialPort.FLOW_CONTROL_XONXOFF_IN_ENABLED | SerialPort.FLOW_CONTROL_XONXOFF_OUT_ENABLED; - default -> { - LOGGER.warn("Unknown flow control '{}', using NONE", flowControl); - yield SerialPort.FLOW_CONTROL_DISABLED; - } + static int parseFlowControl(String flowControl) throws TransportException { + return switch (normalizeOptionValue(flowControl)) { + case "none" -> SerialPort.FLOW_CONTROL_DISABLED; + case "rts-cts", "rtscts" -> SerialPort.FLOW_CONTROL_RTS_ENABLED | SerialPort.FLOW_CONTROL_CTS_ENABLED; + case "xon-xoff", "xonxoff" -> SerialPort.FLOW_CONTROL_XONXOFF_IN_ENABLED | SerialPort.FLOW_CONTROL_XONXOFF_OUT_ENABLED; + default -> throw new TransportException( + "Invalid value '" + flowControl + "' for option 'flow-control' (must be one of: none, rts-cts, xon-xoff)"); }; } + private static String normalizeOptionValue(String value) { + return value.toLowerCase(Locale.ROOT).replace('_', '-'); + } + @Override public boolean isOpen() { return open && port.isOpen(); diff --git a/plc4j/transports/serial/src/main/java/org/apache/plc4x/java/transport/serial/config/SerialTransportConfiguration.java b/plc4j/transports/serial/src/main/java/org/apache/plc4x/java/transport/serial/config/SerialTransportConfiguration.java index 475456a1f8..41d4c6cafe 100644 --- a/plc4j/transports/serial/src/main/java/org/apache/plc4x/java/transport/serial/config/SerialTransportConfiguration.java +++ b/plc4j/transports/serial/src/main/java/org/apache/plc4x/java/transport/serial/config/SerialTransportConfiguration.java @@ -52,19 +52,19 @@ public class SerialTransportConfiguration implements TransportConfiguration { public int stopBits; /** - * Parity: NONE, ODD, EVEN, MARK, SPACE + * Parity: none, odd, even, mark, space (case-insensitive) */ @ConfigurationParameter( "parity") - @Description( "Parity: NONE, ODD, EVEN, MARK, SPACE") - @StringDefaultValue("NONE") + @Description( "Parity: none, odd, even, mark, space (case-insensitive)") + @StringDefaultValue("none") public String parity; /** - * Flow control: NONE, RTS_CTS, XON_XOFF, RTS_CTS_XON_XOFF + * Flow control: none, rts-cts, xon-xoff (case-insensitive) */ @ConfigurationParameter( "flow-control") - @Description( "Flow control: NONE, RTS_CTS, XON_XOFF, RTS_CTS_XON_XOFF") - @StringDefaultValue("NONE") + @Description( "Flow control: none, rts-cts, xon-xoff (case-insensitive)") + @StringDefaultValue("none") public String flowControl; /** @@ -110,30 +110,6 @@ public class SerialTransportConfiguration implements TransportConfiguration { @BooleanDefaultValue(false) public boolean reusePort; - /** - * Receive buffer size in bytes. 0 uses system default. - */ - @ConfigurationParameter( "receive-buffer-size") - @Description( "Receive buffer size in bytes. 0 uses system default.") - @IntDefaultValue(4096) - public int receiveBufferSize; - - /** - * Send buffer size in bytes. 0 uses system default. - */ - @ConfigurationParameter( "send-buffer-size") - @Description( "Send buffer size in bytes. 0 uses system default.") - @IntDefaultValue(4096) - public int sendBufferSize; - - /** - * Enable break signal - */ - @ConfigurationParameter( "break-enabled") - @Description( "Enable break signal") - @BooleanDefaultValue(false) - public boolean breakEnabled; - /** * Interframe delay in milliseconds for protocols that need spacing between messages. * Only applies when reusePort is true. diff --git a/plc4j/transports/serial/src/test/java/org/apache/plc4x/java/transport/serial/SerialTransportInstanceTest.java b/plc4j/transports/serial/src/test/java/org/apache/plc4x/java/transport/serial/SerialTransportInstanceTest.java index 8fc4cce0e7..5ad458f4b1 100644 --- a/plc4j/transports/serial/src/test/java/org/apache/plc4x/java/transport/serial/SerialTransportInstanceTest.java +++ b/plc4j/transports/serial/src/test/java/org/apache/plc4x/java/transport/serial/SerialTransportInstanceTest.java @@ -69,8 +69,6 @@ class SerialTransportInstanceTest { config.stopBits = 1; config.parity = "NONE"; config.flowControl = "NONE"; - config.receiveBufferSize = 4096; - config.sendBufferSize = 4096; transportInstance = new SerialTransportInstance(new SharedSerialPortManager(), "/tmp/ttyV0"/*port.getSystemPortName()*/, config, AuditLog.builder().build()); } @@ -314,7 +312,7 @@ class SerialTransportInstanceTest { transportInstance.close(); } - // Create config with unknown parity - should default to NONE + // Create config with unknown parity - should now fail fast SerialTransportConfiguration configWithUnknownParity = new SerialTransportConfiguration(); configWithUnknownParity.baudRate = 9600; configWithUnknownParity.dataBits = 8; @@ -323,11 +321,8 @@ class SerialTransportInstanceTest { configWithUnknownParity.flowControl = "NONE"; configWithUnknownParity.reusePort = false; - SerialTransportInstance instanceWithUnknownParity = new SerialTransportInstance( - new SharedSerialPortManager(), "/tmp/ttyV0", configWithUnknownParity, AuditLog.builder().build()); - - assertTrue(instanceWithUnknownParity.isOpen()); - instanceWithUnknownParity.close(); + assertThrows(TransportException.class, () -> new SerialTransportInstance( + new SharedSerialPortManager(), "/tmp/ttyV0", configWithUnknownParity, AuditLog.builder().build())); } @Test @@ -337,7 +332,7 @@ class SerialTransportInstanceTest { transportInstance.close(); } - // Create config with unknown flow control - should default to NONE + // Create config with unknown flow control - should now fail fast SerialTransportConfiguration configWithUnknownFlowControl = new SerialTransportConfiguration(); configWithUnknownFlowControl.baudRate = 9600; configWithUnknownFlowControl.dataBits = 8; @@ -346,11 +341,8 @@ class SerialTransportInstanceTest { configWithUnknownFlowControl.flowControl = "UNKNOWN_FLOW_CONTROL"; // Unknown flow control configWithUnknownFlowControl.reusePort = false; - SerialTransportInstance instanceWithUnknownFlowControl = new SerialTransportInstance( - new SharedSerialPortManager(), "/tmp/ttyV0", configWithUnknownFlowControl, AuditLog.builder().build()); - - assertTrue(instanceWithUnknownFlowControl.isOpen()); - instanceWithUnknownFlowControl.close(); + assertThrows(TransportException.class, () -> new SerialTransportInstance( + new SharedSerialPortManager(), "/tmp/ttyV0", configWithUnknownFlowControl, AuditLog.builder().build())); } @Test @@ -383,7 +375,7 @@ class SerialTransportInstanceTest { transportInstance.close(); } - // Create config with RTS_CTS_XON_XOFF flow control + // Create config with RTS_CTS_XON_XOFF flow control - combined mode is no longer supported SerialTransportConfiguration configWithCombinedFlowControl = new SerialTransportConfiguration(); configWithCombinedFlowControl.baudRate = 9600; configWithCombinedFlowControl.dataBits = 8; @@ -392,11 +384,8 @@ class SerialTransportInstanceTest { configWithCombinedFlowControl.flowControl = "RTS_CTS_XON_XOFF"; configWithCombinedFlowControl.reusePort = false; - SerialTransportInstance instanceWithCombinedFlowControl = new SerialTransportInstance( - new SharedSerialPortManager(), "/tmp/ttyV0", configWithCombinedFlowControl, AuditLog.builder().build()); - - assertTrue(instanceWithCombinedFlowControl.isOpen()); - instanceWithCombinedFlowControl.close(); + assertThrows(TransportException.class, () -> new SerialTransportInstance( + new SharedSerialPortManager(), "/tmp/ttyV0", configWithCombinedFlowControl, AuditLog.builder().build())); } @Test @@ -424,4 +413,47 @@ class SerialTransportInstanceTest { instanceWithParity.close(); } } + + @Test + void parseParityAcceptsCaseInsensitiveForms() throws Exception { + assertEquals(SerialPort.NO_PARITY, SerialTransportInstance.parseParity("none")); + assertEquals(SerialPort.EVEN_PARITY, SerialTransportInstance.parseParity("even")); + assertEquals(SerialPort.EVEN_PARITY, SerialTransportInstance.parseParity("EVEN")); + assertEquals(SerialPort.ODD_PARITY, SerialTransportInstance.parseParity("Odd")); + assertEquals(SerialPort.MARK_PARITY, SerialTransportInstance.parseParity("MARK")); + assertEquals(SerialPort.SPACE_PARITY, SerialTransportInstance.parseParity("space")); + } + + @Test + void parseParityRejectsUnknownValues() { + TransportException e = assertThrows(TransportException.class, + () -> SerialTransportInstance.parseParity("strong")); + assertTrue(e.getMessage().contains("parity")); + assertTrue(e.getMessage().contains("strong")); + } + + @Test + void parseFlowControlAcceptsCanonicalAndLegacyForms() throws Exception { + int rtsCts = SerialPort.FLOW_CONTROL_RTS_ENABLED | SerialPort.FLOW_CONTROL_CTS_ENABLED; + assertEquals(rtsCts, SerialTransportInstance.parseFlowControl("rts-cts")); + assertEquals(rtsCts, SerialTransportInstance.parseFlowControl("RTS_CTS")); + assertEquals(rtsCts, SerialTransportInstance.parseFlowControl("RTSCTS")); + int xonXoff = SerialPort.FLOW_CONTROL_XONXOFF_IN_ENABLED | SerialPort.FLOW_CONTROL_XONXOFF_OUT_ENABLED; + assertEquals(xonXoff, SerialTransportInstance.parseFlowControl("xon-xoff")); + assertEquals(xonXoff, SerialTransportInstance.parseFlowControl("XON_XOFF")); + assertEquals(SerialPort.FLOW_CONTROL_DISABLED, SerialTransportInstance.parseFlowControl("NONE")); + } + + @Test + void parseFlowControlRejectsCombinedMode() { + TransportException e = assertThrows(TransportException.class, + () -> SerialTransportInstance.parseFlowControl("RTS_CTS_XON_XOFF")); + assertTrue(e.getMessage().contains("flow-control")); + } + + @Test + void parseFlowControlRejectsUnknownValues() { + assertThrows(TransportException.class, + () -> SerialTransportInstance.parseFlowControl("magic")); + } } diff --git a/plc4j/transports/serial/src/test/java/org/apache/plc4x/java/transport/serial/SerialTransportTest.java b/plc4j/transports/serial/src/test/java/org/apache/plc4x/java/transport/serial/SerialTransportTest.java index 5021b41b3d..155169baf6 100644 --- a/plc4j/transports/serial/src/test/java/org/apache/plc4x/java/transport/serial/SerialTransportTest.java +++ b/plc4j/transports/serial/src/test/java/org/apache/plc4x/java/transport/serial/SerialTransportTest.java @@ -214,7 +214,7 @@ class SerialTransportTest { SerialPort[] ports = SerialPort.getCommPorts(); assumeTrue(ports.length > 0, "No serial ports available for testing"); - String[] flowControlOptions = {"NONE", "RTS_CTS", "XON_XOFF", "RTS_CTS_XON_XOFF"}; + String[] flowControlOptions = {"NONE", "RTS_CTS", "XON_XOFF"}; for (String flowControl : flowControlOptions) { SerialTransportConfiguration config = new SerialTransportConfiguration(); diff --git a/plc4j/transports/serial/src/test/java/org/apache/plc4x/java/transport/serial/config/SerialTransportConfigurationTest.java b/plc4j/transports/serial/src/test/java/org/apache/plc4x/java/transport/serial/config/SerialTransportConfigurationTest.java index d485b0a9f5..9dbfe9a774 100644 --- a/plc4j/transports/serial/src/test/java/org/apache/plc4x/java/transport/serial/config/SerialTransportConfigurationTest.java +++ b/plc4j/transports/serial/src/test/java/org/apache/plc4x/java/transport/serial/config/SerialTransportConfigurationTest.java @@ -119,30 +119,6 @@ class SerialTransportConfigurationTest { assertTrue(config.reusePort); } - @Test - void testReceiveBufferSizeField() { - SerialTransportConfiguration config = new SerialTransportConfiguration(); - config.receiveBufferSize = 65536; - assertEquals(65536, config.receiveBufferSize); - } - - @Test - void testSendBufferSizeField() { - SerialTransportConfiguration config = new SerialTransportConfiguration(); - config.sendBufferSize = 32768; - assertEquals(32768, config.sendBufferSize); - } - - @Test - void testBreakEnabledField() { - SerialTransportConfiguration config = new SerialTransportConfiguration(); - config.breakEnabled = false; - assertFalse(config.breakEnabled); - - config.breakEnabled = true; - assertTrue(config.breakEnabled); - } - @Test void testInterframeDelayField() { SerialTransportConfiguration config = new SerialTransportConfiguration(); @@ -164,9 +140,6 @@ class SerialTransportConfigurationTest { config.dtr = true; config.rts = true; config.reusePort = true; - config.receiveBufferSize = 8192; - config.sendBufferSize = 8192; - config.breakEnabled = true; config.interframeDelay = 10; assertEquals(115200, config.baudRate); @@ -179,9 +152,6 @@ class SerialTransportConfigurationTest { assertTrue(config.dtr); assertTrue(config.rts); assertTrue(config.reusePort); - assertEquals(8192, config.receiveBufferSize); - assertEquals(8192, config.sendBufferSize); - assertTrue(config.breakEnabled); assertEquals(10, config.interframeDelay); } @@ -195,16 +165,6 @@ class SerialTransportConfigurationTest { assertEquals(0, config.writeTimeout); } - @Test - void testZeroBufferSizes() { - SerialTransportConfiguration config = new SerialTransportConfiguration(); - config.sendBufferSize = 0; - config.receiveBufferSize = 0; - - assertEquals(0, config.sendBufferSize); - assertEquals(0, config.receiveBufferSize); - } - @Test void testZeroInterframeDelay() { SerialTransportConfiguration config = new SerialTransportConfiguration();
