Skip to content

[modbus-tcp / s7] Reconnect after disconnection via ethernet #1781

Open
@baraldi-l-proxaut

Description

@baraldi-l-proxaut

I wrote a Camel route that reads signals from a Phoenix I/O via modbus protocol every 500 milliseconds.
The I/o Phoenix is connected to my laptop via ethernet cable.

Initially the connection was opened and closed for each read instruction, but subsequently I moved to a managed pool of connections to reuse the same connection.
In this way I managed to reduce reading times and avoided saturating the PLC connections.
Before, opening and closing the connection, took me a total of 1 (max 2) seconds to read each reading.
With a managed pool of already open connections, I can read within the expected 500 ms.

The problem I'm facing now is that if the ethernet cable is disconnected, an Exception is thrown that I can't catch and subsequently the connection to that IP is no longer usable until the route is restarted.

Do you have any idea on how to catch the java.net.SocketException Connection reset?

This is the java.net.SocketException thrown:

2024-09-23T18:18:44.111+02:00 WARN 29312 --- [ntLoopGroup-3-1] i.netty.channel.DefaultChannelPipeline : An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception. java.net.SocketException: Connection reset at java.base/sun.nio.ch.SocketChannelImpl.throwConnectionReset(SocketChannelImpl.java:394) ~[na:na] at java.base/sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:426) ~[na:na] at io.netty.buffer.PooledByteBuf.setBytes(PooledByteBuf.java:254) ~[netty-buffer-4.1.97.Final.jar:4.1.97.Final] at io.netty.buffer.AbstractByteBuf.writeBytes(AbstractByteBuf.java:1132) ~[netty-buffer-4.1.97.Final.jar:4.1.97.Final] at io.netty.channel.socket.nio.NioSocketChannel.doReadBytes(NioSocketChannel.java:357) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final] at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:151) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final] at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final] at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final] at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final] at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final] at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) ~[netty-common-4.1.97.Final.jar:4.1.97.Final] at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.97.Final.jar:4.1.97.Final] at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.97.Final.jar:4.1.97.Final] at java.base/java.lang.Thread.run(Thread.java:840) ~[na:na]

This is the Camel route:

fromF("timer:%s?period=%d", tagGroupName, interval)
        .routeId(tagGroupName)
        .process(exchange -> {
            Map<String, Object> result = tagReaderService.directRead(tagGroupTriggerData.getHost(),
                    tagGroupTriggerData.getPort(),
                    DevicePlugin.MODBUS,
                    tagGroupTriggerData.getTagList());
            exchange.getIn().setBody(result);
        })
        .marshal().json(JsonLibrary.Jackson)
        .process(exchange -> {
            String body = exchange.getIn().getBody(String.class);
            tagReaderService.put(tagGroupName, body);
        });

This the tagReaderService.directRead method:

    public Map<String, Object> directRead(final String host, final Integer port, final DevicePlugin devicePlugin,
                                          final List<TagTriggerData> tags) throws PlcConnectionException, ExecutionException, InterruptedException {
        if (devicePlugin == DevicePlugin.OMRON_FINS_UDP) {
            Map<String, Object> result = new HashMap<>(tags.size());
            for (var tagDTO : tags) {
                OmronReadRequest request = new OmronReadRequest();
                request.setAddress(tagDTO.getAddress());
                OmronReadResponse response = producerTemplate.requestBody("direct:read-omron-single", request, OmronReadResponse.class);
                result.put(tagDTO.getName(), response.getResponse());
            }
            return result;
        } else {
            return plcPoolConnectionService.readValues(host, port, devicePlugin, tags);
        }
    }

This is the plcPoolConnectionService.readValues invoked:

    public Map<String, Object> readValues(final String host, final Integer port, final DevicePlugin devicePlugin, final List<TagTriggerData> tags) throws PlcConnectionException, ExecutionException, InterruptedException {
        ConnectionWrapper connection = getConnectionWrapper(host, port, devicePlugin);
        return connection.readValues(tags);
    }

This is the getConnectionWrapper:

    private ConnectionWrapper getConnectionWrapper(final String host, final Integer port, final DevicePlugin devicePlugin) throws PlcConnectionException {
        String key = host + ofNullable(port).map(Object::toString).orElse("");
        ConnectionWrapper connection = connections.get(key);
        if (connection == null) {
            connection = createNewConnection(host, port, devicePlugin);
            connections.put(key, connection);
        }
        return connection;
    }

And here i create the connection:

    private ConnectionWrapper createNewConnection(final String host, final Integer port, final DevicePlugin devicePlugin) throws PlcConnectionException {
        return switch (devicePlugin) {
            case S7COMM -> ConnectionWrapper.s7Connection(driverManager, host);
            case MODBUS -> ConnectionWrapper.modbusTcpConnection(driverManager, host, port);
            default -> throw new RuntimeException("Unsupported device plugin: " + devicePlugin);
        };
    }

    public static ConnectionWrapper modbusTcpConnection(final PlcDriverManager driverManager, final String host, final Integer port) throws PlcConnectionException {
        String connectionString = String.format(MODBUS_TCP_CONNECTION_STRING, host);
        return new ConnectionWrapper(driverManager, connectionString);
    }

    private ConnectionWrapper(final PlcDriverManager driverManager, final String connectionString) throws PlcConnectionException {
        connection = driverManager.getConnection(connectionString);
    }

TagGroupRouteBuilder.zip

Feel free to write to me for any information and if there are any points to explore further.
Thank you,
Luca

Metadata

Metadata

Assignees

No one assigned

    Labels

    Ethernet/IPhttps://plc4x.apache.org/users/protocols/eip.htmlModbushttps://plc4x.apache.org/users/protocols/modbus.htmlS7https://plc4x.apache.org/users/protocols/s7.htmlawaiting-feedbackThis label is applied when an issue has been opened and we need more information from the issuer.javaPull requests that update Java code

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions