Synchronous Modbus TCP Server

1,288 views
Skip to first unread message

anana

unread,
Aug 15, 2011, 8:58:41 AM8/15/11
to pymodbus
Is the synchronous Server using twisted? I cannot afford installing
twisted. I am looking forward to a not-so-heavy solution for Modbus
TCP.

Moreover, I am having some troubles when testing the synchronous
Server:

I first run the example (http://packages.python.org/pymodbus/examples/
synchronous-server.html) and then try to poll some registers using
modpoll (http://www.modbusdriver.com/modpoll.html).
As long as the client polls with 1 s rate, it works:

root@Ordenador:~/pymodbus$ sudo python ModbusServer.py
DEBUG:pymodbus.server.sync:Started thread to serve client at
('127.0.0.1', 56614)
DEBUG:pymodbus.server.sync:Client Connected [127.0.0.1:56614]
DEBUG:pymodbus.server.sync:0x0 0x1 0x0 0x0 0x0 0x6 0x1 0x3 0x0 0x0 0x0
0x1
DEBUG:pymodbus.transaction:0x0 0x1 0x0 0x0 0x0 0x6 0x1 0x3 0x0 0x0 0x0
0x1
DEBUG:pymodbus.factory:Factory Request[3]
DEBUG:pymodbus.datastore.context:validate[3] 1:1
DEBUG:pymodbus.datastore.context:getValues[3] 1:1
DEBUG:pymodbus.server.sync:send: 0001000000050103020011
...

But if I stop the Client (modpoll), the following will appear so
often, that it is impossible to see the data when polling again.

DEBUG:pymodbus.transaction:
DEBUG:pymodbus.server.sync:
DEBUG:pymodbus.transaction:
DEBUG:pymodbus.server.sync:
...


If I run the example synchronous Client (http://packages.python.org/
pymodbus/examples/synchronous-client.html), the result is much worse
since it will only poll once and the the server stays in the previous
state forever.


DEBUG:pymodbus.server.sync:Started thread to serve client at
('127.0.0.1', 34947)
DEBUG:pymodbus.server.sync:Client Connected [127.0.0.1:34947]
DEBUG:pymodbus.server.sync:0x0 0x1 0x0 0x0 0x0 0x6 0x0 0x6 0x0 0x1 0x0
0xa
DEBUG:pymodbus.transaction:0x0 0x1 0x0 0x0 0x0 0x6 0x0 0x6 0x0 0x1 0x0
0xa
DEBUG:pymodbus.factory:Factory Request[6]
DEBUG:pymodbus.datastore.context:validate[6] 2:1
DEBUG:pymodbus.datastore.context:setValues[6] 2:1
DEBUG:pymodbus.datastore.context:getValues[6] 2:1
DEBUG:pymodbus.server.sync:send: 00010000000600060001000a
DEBUG:pymodbus.server.sync:0x0 0x2 0x0 0x0 0x0 0x6 0x0 0x3 0x0 0x1 0x0
0x1
DEBUG:pymodbus.transaction:0x0 0x2 0x0 0x0 0x0 0x6 0x0 0x3 0x0 0x1 0x0
0x1
DEBUG:pymodbus.factory:Factory Request[3]
DEBUG:pymodbus.datastore.context:validate[3] 2:1
DEBUG:pymodbus.datastore.context:getValues[3] 2:1
DEBUG:pymodbus.server.sync:send: 000200000005000302000a
DEBUG:pymodbus.server.sync:0x0 0x3 0x0 0x0 0x0 0x17 0x0 0x10 0x0 0x1
0x0 0x8 0x10 0x0 0xa 0x0 0xa 0x0 0xa 0x0 0xa 0x0 0xa 0x0 0xa 0x0 0xa
0x0 0xa
DEBUG:pymodbus.transaction:0x0 0x3 0x0 0x0 0x0 0x17 0x0 0x10 0x0 0x1
0x0 0x8 0x10 0x0 0xa 0x0 0xa 0x0 0xa 0x0 0xa 0x0 0xa 0x0 0xa 0x0 0xa
0x0 0xa
DEBUG:pymodbus.factory:Factory Request[16]
DEBUG:pymodbus.datastore.context:validate[16] 2:8
DEBUG:pymodbus.datastore.context:setValues[16] 2:8
DEBUG:pymodbus.server.sync:send: 000300000006001000010008
DEBUG:pymodbus.server.sync:0x0 0x4 0x0 0x0 0x0 0x6 0x0 0x4 0x0 0x1 0x0
0x8
DEBUG:pymodbus.transaction:0x0 0x4 0x0 0x0 0x0 0x6 0x0 0x4 0x0 0x1 0x0
0x8
DEBUG:pymodbus.factory:Factory Request[4]
DEBUG:pymodbus.datastore.context:validate[4] 2:8
DEBUG:pymodbus.datastore.context:getValues[4] 2:8
DEBUG:pymodbus.server.sync:send:
00040000001300041000110011001100110011001100110011
DEBUG:pymodbus.transaction:
DEBUG:pymodbus.server.sync:
DEBUG:pymodbus.transaction:
DEBUG:pymodbus.server.sync:


And on the client side:

DEBUG:pymodbus.client.sync:Running transaction 1
DEBUG:pymodbus.transaction:0x0 0x1 0x0 0x0 0x0 0x6 0x0 0x6 0x0 0x1 0x0
0xa
DEBUG:pymodbus.factory:Factory Response[6]
DEBUG:pymodbus.client.sync:Running transaction 2
DEBUG:pymodbus.transaction:0x0 0x2 0x0 0x0 0x0 0x5 0x0 0x3 0x2 0x0 0xa
DEBUG:pymodbus.factory:Factory Response[3]
DEBUG:pymodbus.client.sync:Running transaction 3
DEBUG:pymodbus.transaction:0x0 0x3 0x0 0x0 0x0 0x6 0x0 0x10 0x0 0x1
0x0 0x8
DEBUG:pymodbus.factory:Factory Response[16]
DEBUG:pymodbus.client.sync:Running transaction 4
DEBUG:pymodbus.transaction:0x0 0x4 0x0 0x0 0x0 0x13 0x0 0x4 0x10 0x0
0x11 0x0 0x11 0x0 0x11 0x0 0x11 0x0 0x11 0x0 0x11 0x0 0x11 0x0 0x11
DEBUG:pymodbus.factory:Factory Response[4]
Traceback (most recent call last):
File "ModbusClient.py", line 69, in <module>
assert(rr.registers == [10]*8) # test the expected value
AssertionError



Is there a better way to test it? Am I doing something wrong?

Thanks, ana.

Galen Collins

unread,
Aug 15, 2011, 3:28:54 PM8/15/11
to pymo...@googlegroups.com

Ana,

The synchronous version in fact does not use twisted and can be installed and run without the other dependencies (simply comment them out in the setup.py).

As for the errors, they are causes by me not finding a good way to keep the sever thread listening until the client disconnects. In this case, an error should be thrown and the polling loop should exit. Anyways, that wasn't happening and I didn't get a chance to fix it (see the handle function in sync.py). As for the client failing the last assert, I would news to check that out as it should work fine.

Anyways, I am open to suggestions on how to fix the server, otherwise I can work on fixing it when I return from my trip.

Galen

anana

unread,
Aug 25, 2011, 6:34:48 AM8/25/11
to pymodbus
Hello again,

Coming forward with the synchronous TCP server, thank you for the
help, this is a very useful tool!

I have a new issue: how to initialize the data store.
Is it needed? If I later try to save a value in a higher register,
will it produce a validation exception? Why not initializing it to
Modbus maximal size (according to Modbus application protocol 1.1b
"For each of the primary tables, the protocol allows individual
selection of 65536 data items")?

I still don't understand why the Server context is made out of a slave
context and what this single=True means.


#---------------------------------------------------------------------------
#
# initialize your data store
#---------------------------------------------------------------------------
#
store = ModbusSlaveContext(
di = ModbusSequentialDataBlock(0, [17]*100),
co = ModbusSequentialDataBlock(0, [17]*100),
hr = ModbusSequentialDataBlock(0, [17]*100),
ir = ModbusSequentialDataBlock(0, [17]*100))
context = ModbusServerContext(slaves=store, single=True)




On Aug 15, 9:28 pm, Galen Collins <bashw...@gmail.com> wrote:
> Ana,
>
> The synchronous version in fact does not use twisted and can be installed
> and run without the other dependencies (simply comment them out in the
> setup.py).
>
> As for the errors, they are causes by me not finding a good way to keep the
> sever thread listening until the client disconnects. In this case, an error
> should be thrown and the polling loop should exit. Anyways, that wasn't
> happening and I didn't get a chance to fix it (see the handle function in
> sync.py). As for the client failing the last assert, I would news to check
> that out as it should work fine.
>
> Anyways, I am open to suggestions on how to fix the server, otherwise I can
> work on fixing it when I return from my trip.
>
> Galen

Galen Collins

unread,
Aug 25, 2011, 8:54:47 AM8/25/11
to pymo...@googlegroups.com


On Aug 25, 2011 12:34 PM, "anana" <anr...@hotmail.com> wrote:
>
> Hello again,
>
> Coming forward with the synchronous TCP server, thank you for the
> help, this is a very useful tool!
>

No problem; I am glad you are finding it useful.

> I have a new issue: how to initialize the data store.
> Is it needed?

The data store is needed if you are planning on saving or retrieving data.

If I later try to save a value in a higher register,
> will it produce a validation exception?

Yes

Why not initializing it to
> Modbus maximal size (according to Modbus application protocol 1.1b
> "For each of the primary tables, the protocol allows individual
> selection of 65536 data items")?
>

Because many devices (read: the ones I had to emulate) do not use the full data space. I should make it easier to null populate the full space though.

> I still don't understand why the Server context is made out of a slave

This is because a server can actually point to many different contexts or devices. Meaning, you can write to register 1 of 255 contexts.

> context and what this single=True means.
>

This means that regardless of what context you request, it will always point to the same one. Meaning, register 1 of context 1 is the same as register 1 of context 255.

anana

unread,
Aug 26, 2011, 4:04:48 AM8/26/11
to pymodbus
So you mean, if single=True, we have one only context with until four
modules (discrete inputs, coils, input registers and holding
registers), each of them with until 65536 registers.
But if single=x our server will manage the data of x other devices
with different identifiers? Each of these devices could then write and
read on the other, with the help of our server?

Thank you again.

On Aug 25, 2:54 pm, Galen Collins <bashw...@gmail.com> wrote:

Galen Collins

unread,
Aug 26, 2011, 2:16:17 PM8/26/11
to pymo...@googlegroups.com


On Aug 26, 2011 10:04 AM, "anana" <anr...@hotmail.com> wrote:
>
> So you mean, if single=True, we have one only context with until four
> modules (discrete inputs, coils, input registers and holding
> registers), each of them with until 65536 registers.
> But if single=x our server will manage the data of x other devices
> with different identifiers? Each of these devices could then write and
> read on the other, with the help of our server?
>

So you would set single=False and then supply a dictionary of the contexts for each context:

(slaves={0:c1, 1:c2}, single=False)

I will make the documentation more explicit and possibly easier to initialize.

Bashwork

unread,
Aug 31, 2011, 10:46:08 AM8/31/11
to pymodbus
Okay,

The issues with the synchronous versions of the library should be
fixed now. Feel free to pull
the latest from trunk and let me know if there are any issues with the
fixes.

Galen
> > #-------------------------------------------------------------------------- -> #
Reply all
Reply to author
Forward
0 new messages