sourceperl / pyModbusTCP

A simple Modbus/TCP library for Python
MIT License
297 stars 104 forks source link

MBAP checking error #70

Closed FrancoTampieri closed 1 year ago

FrancoTampieri commented 1 year ago

Hi I have this sample code:

pyModbusTCP == 0.2.0 python 3.9.2

from pyModbusTCP.client import ModbusClient
c = ModbusClient(host="192.168.1.134", port=1024, unit_id=10, debug=True)

c.open()

if c.is_open == False:
    print('not connected')
else:
    print('connected')

print(c.unit_id)

c.read_input_registers(1,1)

c.close()

when I try to connect and read the input register 1 (300001) I get this error:

connected
10
Tx
[D6 7F 00 00 00 06 0A] 04 00 01 00 01
Rx
[57 65 6C 63 6F 6D 65]
MBAP checking error

I used a GUI modbus client and I read the register correctly is 3, why I got this error? Someone can help me? Because of course if I try to print the register I get a None response that is not true.

sourceperl commented 1 year ago

Hi,

In the MBAP header, bytes 2-3 are "protocol identifier" and it must be equal to 0 for modbus/TCP. In your debug trace, server return 6C-63 at this place.

Also, if your convert your RAW MBAP data to string it give you 'Welcome' string:

# return 'Welcome'
print(b'\x57\x65\x6c\x63\x6f\x6d\x65'.decode())

Are you sure you connect to a modbus TCP server socket ?

more at: https://en.wikipedia.org/wiki/Modbus#Modbus_TCP_frame_format

FrancoTampieri commented 1 year ago

Hi, the producer of the machine told me this... of course I think is a custom modbus server and not developed to be full protocol compliant. I tried with nodes and the modus-serial libs that support both rtu and tcp server and I get the integer value of the register that is 3. Thank you to giving me the frame format. Is possible to create a custom frame format to handle this case? The only things that I know is the that the server is a .net server on windows 7 I can use Wireshark for that, today or tomorrow I will try to get all the communications frame.

I get using pymodbus as debugger this:

DEBUG:pymodbus.logging:Connection to Modbus server established. Socket ('192.168.1.125', 46270)
DEBUG:pymodbus.logging:Current transaction state - IDLE
DEBUG:pymodbus.logging:Running transaction 1
DEBUG:pymodbus.logging:SEND: 0x0 0x1 0x0 0x0 0x0 0x6 0x0 0x4 0x0 0x1 0x0 0x1
DEBUG:pymodbus.logging:New Transaction state "SENDING"
DEBUG:pymodbus.logging:Changing transaction state from "SENDING" to "WAITING FOR REPLY"
DEBUG:pymodbus.logging:Incomplete message received, Expected 28531 bytes Received 19 bytes !!!!
DEBUG:pymodbus.logging:Changing transaction state from "WAITING FOR REPLY" to "PROCESSING REPLY"
DEBUG:pymodbus.logging:RECV: 0x57 0x65 0x6c 0x63 0x6f 0x6d 0x65 0x20 0x74 0x6f 0x20 0x54 0x63 0x70 0x53 0x72 0x76 0xd 0xa
DEBUG:pymodbus.logging:Processing: 0x57 0x65 0x6c 0x63 0x6f 0x6d 0x65 0x20 0x74 0x6f 0x20 0x54 0x63 0x70 0x53 0x72 0x76 0xd 0xa
DEBUG:pymodbus.logging:Frame check failed, ignoring!!
DEBUG:pymodbus.logging:Resetting frame - Current Frame in buffer - 0x57 0x65 0x6c 0x63 0x6f 0x6d 0x65 0x20 0x74 0x6f 0x20 0x54 0x63 0x70 0x53 0x72 0x76 0xd 0xa
DEBUG:pymodbus.logging:Getting transaction 1
DEBUG:pymodbus.logging:Changing transaction state from "PROCESSING REPLY" to "TRANSACTION_COMPLETE"
Modbus Error: [Input/Output] No Response received from the remote slave/Unable to decode response

And the complete response look like say:

print(b'\x57\x65\x6c\x63\x6f\x6d\x65\x20\x74\x6f\x20\x54\x63\x70\x53\x72\x76'.decode())
Welcome to TcpSrv

the last two "0xd 0xa" give me an error on decoding

sourceperl commented 1 year ago

There is no way to deal with badly formatted MBAP header in pyModbusTCP. You can only create a custom PDU frame (part after MBAP header) with custom_request method, but that's useless here.

Perhaps you can try to change pyModbusTCP code for create a custom version to deal with this specific server but it can be very time-consuming. Another way could be to write a python TCP proxy for remove/reformat data in front of client.

FrancoTampieri commented 1 year ago

Yeah I solved developing a program with nodes 18 and the modus-serial libs.

But I will share the tests that I made, because can be useful for someone that face of the same problem with some custom modbus server that was developed without follow the standards :/

Python is the standard of Debian 11 and is versione 3.9.2

I activated the debug for both libs:

from pyModbusTCP.client import ModbusClient
c = ModbusClient(host="192.168.1.134", port=1024, unit_id=10, debug=True)

c.open()

if c.is_open == False:
    print('not connected')
else:
    print('connected')

print(c.unit_id)

c.read_input_registers(1,1)

c.close()

from pymodbus.client import ModbusTcpClient
from pymodbus.transaction import ModbusSocketFramer as ModbusFramer

import logging
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)

ip = "192.168.1.134"
port_num = 1024
unit_num = 10

client = ModbusTcpClient(ip, port=port_num, framer=ModbusFramer)
client.connect()
rr = client.read_input_registers(1,1, unit=unit_num)
print(rr)

The output of the two libs are that:

connected
10
Tx
[DD 02 00 00 00 06 0A] 04 00 01 00 01
Rx
[57 65 6C 63 6F 6D 65]
MBAP checking error

DEBUG:pymodbus.logging:Connection to Modbus server established. Socket ('192.168.1.125', 46270)
DEBUG:pymodbus.logging:Current transaction state - IDLE
DEBUG:pymodbus.logging:Running transaction 1
DEBUG:pymodbus.logging:SEND: 0x0 0x1 0x0 0x0 0x0 0x6 0x0 0x4 0x0 0x1 0x0 0x1
DEBUG:pymodbus.logging:New Transaction state "SENDING"
DEBUG:pymodbus.logging:Changing transaction state from "SENDING" to "WAITING FOR REPLY"
DEBUG:pymodbus.logging:Incomplete message received, Expected 28531 bytes Received 19 bytes !!!!
DEBUG:pymodbus.logging:Changing transaction state from "WAITING FOR REPLY" to "PROCESSING REPLY"
DEBUG:pymodbus.logging:RECV: 0x57 0x65 0x6c 0x63 0x6f 0x6d 0x65 0x20 0x74 0x6f 0x20 0x54 0x63 0x70 0x53 0x72 0x76 0xd 0xa
DEBUG:pymodbus.logging:Processing: 0x57 0x65 0x6c 0x63 0x6f 0x6d 0x65 0x20 0x74 0x6f 0x20 0x54 0x63 0x70 0x53 0x72 0x76 0xd 0xa
DEBUG:pymodbus.logging:Frame check failed, ignoring!!
DEBUG:pymodbus.logging:Resetting frame - Current Frame in buffer - 0x57 0x65 0x6c 0x63 0x6f 0x6d 0x65 0x20 0x74 0x6f 0x20 0x54 0x63 0x70 0x53 0x72 0x76 0xd 0xa
DEBUG:pymodbus.logging:Getting transaction 1
DEBUG:pymodbus.logging:Changing transaction state from "PROCESSING REPLY" to "TRANSACTION_COMPLETE"
Modbus Error: [Input/Output] No Response received from the remote slave/Unable to decode response

Does someone have face off the same error with some software modbus server from some machine producers? All suggestions are welcome :)

Edit:

Hi! I try a session with mbpoll as u suggest and this is what I got:

mbpoll 192.168.1.134 -a 10 -p 1024 -t 3 -r 1
mbpoll 1.5-2 - ModBus(R) Master Simulator
Copyright (c) 2015-2023 Pascal JEAN, https://github.com/epsilonrt/mbpoll
This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under certain conditions; type 'mbpoll -w' for details.

Protocol configuration: ModBus TCP
Slave configuration...: address = [10]
                        start reference = 1, count = 1
Communication.........: 192.168.1.134, port 1024, t/o 1.00 s, poll rate 1000 ms
Data type.............: 16-bit register, input register table

-- Polling slave 10... Ctrl-C to stop)
Read input register failed: Invalid data
-- Polling slave 10... Ctrl-C to stop)
Read input register failed: Invalid data
-- Polling slave 10... Ctrl-C to stop)
Read input register failed: Invalid data
-- Polling slave 10... Ctrl-C to stop)
Read input register failed: Invalid data
-- Polling slave 10... Ctrl-C to stop)
Read input register failed: Invalid data
-- Polling slave 10... Ctrl-C to stop)
Read input register failed: Invalid data
-- Polling slave 10... Ctrl-C to stop)
Read input register failed: Invalid data
-- Polling slave 10... Ctrl-C to stop)
Read input register failed: Invalid data
-- Polling slave 10... Ctrl-C to stop)
Read input register failed: Invalid data
-- Polling slave 10... Ctrl-C to stop)
Read input register failed: Invalid data
-- Polling slave 10... Ctrl-C to stop)
Read input register failed: Invalid data
-- Polling slave 10... Ctrl-C to stop)
Read input register failed: Invalid data
^C--- 192.168.1.134 poll statistics ---
12 frames transmitted, 0 received, 12 errors, 100.0% frame loss

everything was closed.
Have a nice day !

with modpoll (with tcp and try rtu encapsulated in tcp):

./modpoll -m enc -a 10 -p 1024 -t 3 -r 1 192.168.1.134
modpoll 3.10 - FieldTalk(tm) Modbus(R) Master Simulator
Copyright (c) 2002-2021 proconX Pty Ltd
Visit https://www.modbusdriver.com for Modbus libraries and tools.

Protocol configuration: Encapsulated RTU over TCP, FC4
Slave configuration...: address = 10, start reference = 1, count = 1
Communication.........: 192.168.1.134, port 1024, t/o 1.00 s, poll rate 1000 ms
Data type.............: 16-bit register, input register table

-- Polling slave... (Ctrl-C to stop)
Checksum error!
-- Polling slave... (Ctrl-C to stop)
Reply time-out!
-- Polling slave... (Ctrl-C to stop)
Checksum error!
^C

./modpoll -m tcp -a 10 -p 1024 -t 3 -r 1 192.168.1.134
modpoll 3.10 - FieldTalk(tm) Modbus(R) Master Simulator
Copyright (c) 2002-2021 proconX Pty Ltd
Visit https://www.modbusdriver.com for Modbus libraries and tools.

Protocol configuration: MODBUS/TCP, FC4
Slave configuration...: address = 10, start reference = 1, count = 1
Communication.........: 192.168.1.134, port 1024, t/o 1.00 s, poll rate 1000 ms
Data type.............: 16-bit register, input register table

-- Polling slave... (Ctrl-C to stop)
Invalid MPAB identifier!
-- Polling slave... (Ctrl-C to stop)
Reply time-out!
-- Polling slave... (Ctrl-C to stop)
Invalid MPAB identifier!
-- Polling slave... (Ctrl-C to stop)
Reply time-out!
^C

This is the simple code of the nodes program that I try and works:

// create an empty modbus client
const ModbusRTU = require("modbus-serial");
const client = new ModbusRTU();

// open connection to a tcp line
client.connectTCP("192.168.1.134", { port: 1024 });
client.setID(10);

// read the values of 10 registers starting at address 0
// on device number 1. and log the values to the console.
setInterval(function() {
    client.readInputRegisters(0, 10, function(err, data) {
        console.log(data.data);
    });
}, 1000);

and this is the result:

node modbus.js
[
  3, 53, 2, 0, 0,
  0,  0, 0, 0, 0
]
[
  3, 53, 2, 0, 0,
  0,  0, 0, 0, 0
]
[
  3, 53, 2, 0, 0,
  0,  0, 0, 0, 0
]
[
  3, 53, 2, 0, 0,
  0,  0, 0, 0, 0
]
[
  3, 53, 2, 0, 0,
  0,  0, 0, 0, 0
]
[
  3, 53, 2, 0, 0,
  0,  0, 0, 0, 0
]
^C

Probably the nodes lib don't make some checks on the server and get the data rightly but probably is out of modbus standard standard.