Giter Club home page Giter Club logo

nifty-modbus's Introduction

SolarNetwork

SolarNetwork is a platform for the monitoring and control of distributed energy generation and consumption. This repository serves as an umbrella for overall project information and governance. See solarnetwork.github.io for more information.

nifty-modbus's People

Contributors

msqr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

nifty-modbus's Issues

call client.start().get(); on stopped client causes error.

example code:

NettyRtuModbusClientConfig config = new NettyRtuModbusClientConfig(uart.uartConfig.file, params);
final ModbusClient  client = new RtuNettyModbusClient(config, new JscSerialPortProvider());

client.start().get();

RegistersModbusMessage request = RegistersModbusMessage.readRegistersRequest(ModbusBlockType.valueOf(type.getType()), unitId, address, 1);
ModbusMessage response = client.send(request);
RegistersModbusMessage responseMessage = response.unwrap(RegistersModbusMessage.class);

client.stop();

client.start().get();

17:11:25.512 [globalEventExecutor-2-2 ] WARN io.netty.util.concurrent.DefaultPromise ::notifyListener0 - An exception was thrown by net.solarnetwork.io.modbus.netty.handler.NettyModbusClient$$Lambda$207/0xa93b22d0.operationComplete(): java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@12662c2[Not completed, task = java.util.concurrent.Executors$RunnableAdapter@133e8d6[Wrapped task = net.solarnetwork.io.modbus.netty.handler.NettyModbusClient$$Lambda$211/0xa93f8480@e97581]] rejected from java.util.concurrent.ScheduledThreadPoolExecutor@1395e7e[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
at java.base/java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2057)
at java.base/java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:827)
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor.delayedExecute(ScheduledThreadPoolExecutor.java:340)
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor.schedule(ScheduledThreadPoolExecutor.java:562)
at java.base/java.util.concurrent.Executors$DelegatedScheduledExecutorService.schedule(Executors.java:779)
at net.solarnetwork.io.modbus.netty.handler.NettyModbusClient.handleCloseAndScheduleReconnectIfRequired(NettyModbusClient.java:254)
at net.solarnetwork.io.modbus.netty.handler.NettyModbusClient.lambda$handleConnect$2(NettyModbusClient.java:240)
at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:577)
at io.netty.util.concurrent.DefaultPromise.access$300(DefaultPromise.java:35)
at io.netty.util.concurrent.DefaultPromise$2.run(DefaultPromise.java:531)
at io.netty.util.concurrent.GlobalEventExecutor$TaskRunner.run(GlobalEventExecutor.java:243)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:831)

tried on different serialPort implenentation with same result.

Docs for interface allow that.

/**
 * Stop the client.
 * 
 * <p>
 * This method shuts the client down, disconnecting it from whatever Modbus
 * network it had been connected to. It can be started again by calling
 * {@link #start()}. **Calling this method on a client that has already been
 * stopped is allowed and will not result in any error.**
 * </p>
 * 
 * <p>
 * After calling this method {@link #isStarted()} will return
 * {@literal false}.
 * </p>
 */
void stop();

After about 24 hours of operation, starting the client before reading and closing after reading in 60s intervals, I get an endless loop of the client cannot be started because there is a timeout on closing

          After about 24 hours of operation, starting the client before reading and closing after reading in 60s intervals, I get an endless loop of the client cannot be started because there is a timeout on closing
Jul 28 19:16:52 boneio-2 java[30152]: 19:16:52.537 [sbc2ha-1-thread-1             ] WARN  arnetwork.io.modbus.rtu.netty.RtuNettyModbusClient::stop                                - Timeout waiting for /dev/ttyS4 9600 8N1 EventLoopGroup to shutdown
Jul 28 19:16:52 boneio-2 java[30152]: 19:16:52.576 [sbc2ha-1-thread-1             ] ERROR com.dfi.sbc2ha.modbus.Modbus                      ::readMany                            - read many: java.util.concurrent.ExecutionException: java.util.concu
Jul 28 19:16:52 boneio-2 java[30152]:         at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:395)
Jul 28 19:16:52 boneio-2 java[30152]:         at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2069)
Jul 28 19:16:52 boneio-2 java[30152]:         at com.dfi.sbc2ha.modbus.Modbus.readMany(Modbus.java:85)
Jul 28 19:16:52 boneio-2 java[30152]:         at com.dfi.sbc2ha.sensor.modbus.ModbusSensor.run(ModbusSensor.java:99)
Jul 28 19:16:52 boneio-2 java[30152]:         at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
Jul 28 19:16:52 boneio-2 java[30152]:         at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305)
Jul 28 19:16:52 boneio-2 java[30152]:         at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305)
Jul 28 19:16:52 boneio-2 java[30152]:         at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
Jul 28 19:16:52 boneio-2 java[30152]:         at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
Jul 28 19:16:52 boneio-2 java[30152]:         at java.base/java.lang.Thread.run(Thread.java:831)
Jul 28 19:16:52 boneio-2 java[30152]: Caused by: java.util.concurrent.RejectedExecutionException: shutting down
Jul 28 19:16:52 boneio-2 java[30152]:         at io.netty.channel.ThreadPerChannelEventLoopGroup.nextChild(ThreadPerChannelEventLoopGroup.java:308)
Jul 28 19:16:52 boneio-2 java[30152]:         at io.netty.channel.ThreadPerChannelEventLoopGroup.register(ThreadPerChannelEventLoopGroup.java:277)
Jul 28 19:16:52 boneio-2 java[30152]:         at io.netty.bootstrap.AbstractBootstrap.initAndRegister(AbstractBootstrap.java:323)
Jul 28 19:16:52 boneio-2 java[30152]:         at io.netty.bootstrap.Bootstrap.doResolveAndConnect(Bootstrap.java:155)
Jul 28 19:16:52 boneio-2 java[30152]:         at io.netty.bootstrap.Bootstrap.connect(Bootstrap.java:116)
Jul 28 19:16:52 boneio-2 java[30152]:         at net.solarnetwork.io.modbus.rtu.netty.RtuNettyModbusClient.connect(RtuNettyModbusClient.java:177)
Jul 28 19:16:52 boneio-2 java[30152]:         at net.solarnetwork.io.modbus.netty.handler.NettyModbusClient.handleConnect(NettyModbusClient.java:238)
Jul 28 19:16:52 boneio-2 java[30152]:         at net.solarnetwork.io.modbus.netty.handler.NettyModbusClient.start(NettyModbusClient.java:185)

Originally posted by @dafik in #7 (comment)

TcpNettyModbusClient and Multithreading

This is not really an issue but a question how to use nifty-modbus in a multithreaded environment.

In my case I use nifty-modbus in a Reactor + Kotlin environment. I want to read multiple Modbus registers multihreaded.

Here is my most relvant code:

@Singleton
class NiftyModbusAdapter : ModbusAdapter {

    @Value("\${modbus.server.ip}")
    var server: String = ""

    @Value("\${modbus.server.port}")
    var serverPort: Int = 0

    lateinit var client: TcpNettyModbusClient

    private var LOG = LoggerFactory.getLogger(NiftyModbusAdapter::class.java)

    @PostConstruct
    override fun connect(): Boolean {

        val config = NettyTcpModbusClientConfig(server, serverPort)
        client = TcpNettyModbusClient(config, NioEventLoopGroup(4), null)
        client.start().get()
        return true
    }

    @PreDestroy
    override fun disconnect(): Boolean {
        client.stop()
        return true
    }

    override fun warmUpInverter(registerCache: RegisterCache, deviceNumber: Int): Mono<RegisterCache> =
        fillRegisterCache(
            registerCache = registerCache,
            deviceNumber = deviceNumber,
            registerAddress = 40000,
            count = 110
        )
            .map { registerCache }

    override fun minimalWarmUpInverter(registerCache: RegisterCache, deviceNumber: Int): Mono<RegisterCache> =
        fillRegisterCache(
            registerCache = registerCache,
            deviceNumber = deviceNumber,
            registerAddress = 40083,
            count = 2
        ).map { registerCache }


    override fun warmUpMeter1(registerCache: RegisterCache, deviceNumber: Int): Mono<RegisterCache> =

        Mono.`when`(
            fillRegisterCache(
                registerCache = registerCache,
                deviceNumber = deviceNumber,
                registerAddress = 40121,
                count = 100
            ),
            fillRegisterCache(
                registerCache = registerCache,
                deviceNumber = deviceNumber,
                registerAddress = 40221,
                count = 72
            )
        ).then(Mono.just(registerCache))


    override fun minimalWarmUpMeter1(registerCache: RegisterCache, deviceNumber: Int): Mono<RegisterCache> =
        fillRegisterCache(
            registerCache = registerCache,
            deviceNumber = deviceNumber,
            registerAddress = 40206,
            count = 10
        ).map { registerCache }


    override fun warmUpBattery1(registerCache: RegisterCache, deviceNumber: Int): Mono<RegisterCache> =
        Mono.`when`(
            fillRegisterCache(
                registerCache = registerCache,
                deviceNumber = deviceNumber,
                registerAddress = 57600,
                count = 110
            ),
            fillRegisterCache(
                registerCache = registerCache,
                deviceNumber = deviceNumber,
                registerAddress = 57710,
                count = 36
            )
        )
            .then(Mono.just(registerCache))


    override fun minimalWarmUpBattery1(registerCache: RegisterCache, deviceNumber: Int): Mono<RegisterCache> =
        fillRegisterCache(
            registerCache = registerCache,
            deviceNumber = deviceNumber,
            registerAddress = 57716,
            count = 22
        ).map { registerCache }

    fun fillRegisterCache(
        registerCache: RegisterCache,
        deviceNumber: Int,
        registerAddress: Int,
        count: Int,
    ): Mono<Boolean> {

        val request =
            RegistersModbusMessage.readRegistersRequest(ModbusBlockType.Holding, deviceNumber, registerAddress, count)

        return Mono.fromFuture {
            LOG.debug("sending modbus request to device $deviceNumber and address $registerAddress and length $count")
            client.sendAsync(request) as CompletableFuture<ModbusMessage>
        }.subscribeOn(Schedulers.boundedElastic())
            .map {
                LOG.debug("received modbus answer to device $deviceNumber and address $registerAddress and length $count")
                readByteArrayAsRegisterList(it.unwrap(RegistersModbusMessage::class.java))
            }
            .flatMap { registerCache.fillCacheValues(registerStartAddress = registerAddress, data = it) }
            .doOnError { LOG.error("unable to read modbus", it) }
            .map { true }
            .onErrorResume { Mono.just(false) }

    }

    /**
     * The registerModbusMessage's body contains a list of bytes. Two bytes are the content of a Register.
     */
    private fun readByteArrayAsRegisterList(registersModbusMessage: RegistersModbusMessage?): List<Register> {

        val registerList = mutableListOf<Register>()

        if (registersModbusMessage == null) {
            return registerList
        }

        val bytes = registersModbusMessage.dataCopy()
        LOG.debug("read from modbus {}", bytes)

        for (i in bytes.size downTo 0 step 2) {
            if (i > 1) {
                registerList.add(Register(byteArrayOf(bytes[(i - 1) - 1], bytes[i - 1])))
            }

        }
        registerList.reverse()
        return registerList
    }

}

The connection is done while Bean-Creation, this is only once.

In my application I can see the warmup-functions are started at the same time but the results are computed sequentially. The configured EventLoopGroup in the TcpNettyModbusClient just changed where the results were computed sequentially.

Can you give me some advice how to compute the results in parallel?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.