sourceperl / pyModbusTCP

A simple Modbus/TCP library for Python
MIT License
308 stars 106 forks source link

word_list issue #61

Closed shnabin closed 2 years ago

shnabin commented 2 years ago

Hi There,

I am reading data from a field device and trying to set that data to modbus server as input registers, so different clients can connect to this modbus server and read that data, but I am getting the following issue.

===== RESTART: C:/Users/Guta/AppData/Local/Programs/Python/Python310/ppp.py ====
[366.3800048828125, 18.209999084472656]
Traceback (most recent call last):
  File "C:/Users/Guta/AppData/Local/Programs/Python/Python310/ppp.py", line 18, in <module>
    DataBank.set_input_registers(0, [data]) #Assigning the Server with values so client can read
TypeError: DataBank.set_input_registers() missing 1 required positional argument: 'word_list'

My code is as follows:

from pyModbusTCP.client import ModbusClient
from pyModbusTCP.server import ModbusServer, DataBank
from pyModbusTCP.utils import encode_ieee, decode_ieee, \
                              long_list_to_word, word_list_to_long

c = ModbusClient(host='192.168.46.114', port=502, auto_open=True)
address = 51
number = 2
regs = c.read_input_registers(address, number * 2)
if regs:
    data = [decode_ieee(f) for f in word_list_to_long(regs)]
    print(data)
else:
    print("None")

server = ModbusServer("127.0.0.1", 5022, no_block=True) #Server connection   
server.start() 
DataBank.set_input_registers(0, [data]) #Assigning the Server with values so client can read
state = DataBank.get_input_registers(1,2)
print(state)
c.close()

The variable "data" is type list, but i am not sure what exactly is the word_list. Please support.

shnabin commented 2 years ago

sorry! my bad. The following solved the issue:

server.data_bank.set_input_registers(0, data) #Assigning the Server with values so client can read
state = server.data_bank.get_input_registers(0,12)

can you suggest how to get the float values from server, because the values i am assigning to the server are float but the server is giving values without decimals


===== RESTART: C:\Users\Guta\AppData\Local\Programs\Python\Python310\ppp.py ====
_**from field device**_ : [11.770000457763672, 8.720000267028809, 11.779999732971191, 81.5999984741211, 982.9400024414062, 4.940000057220459, 218.39999389648438, 0.0, 0.0, 0.0, 0.0, 0.0]
_**Server output**_ : [11, 8, 11, 81, 982, 4, 218, 0, 0, 0, 0, 0]
charliehuddleston commented 2 years ago

Input and holding registers are unsigned 16-bit integers. You can either package the floating point data on the server into multiple words, or what I do is multiply the decimal value by whatever resolution I want (say x1000) then divide by the same 1000 on the client side.

So instead of setting the input register to 11.7700004... you x100 to get 1177, then when you read that register divide 1177 by 100 = 11.77.

shnabin commented 2 years ago

@charliehuddleston : Thank you for support. The way around of multiplying by 100 is working for some values. However, for the larger number i might have to use the first option you suggested. Can you please provide an example for packing the value in server.

Ex: when i am writing a value 124222.564 to the server --> the clients connected to the server are reading 58686

shnabin commented 2 years ago

I have to do multiple encoding and decoding to get the float value as it is from field --> server --> client. For any one who is having same issue, following is the code. This seems to be not a nice way to do it but its working !

##From field 
reg4 = c.read_input_registers(51, 2)
irr = [decode_ieee(f) for f in word_list_to_long(reg4)]
rad = utils.encode_ieee(irr[0])
##At server
server.data_bank.set_holding_registers(10, [(rad&0xffff0000)>>16, rad&0xffff]) 
state = server.data_bank.get_holding_registers(10,2)
tag1 = [decode_ieee(f) for f in word_list_to_long(state)]

It would be nice if we can have float support for input and holding registers at server.

Thank you!