cabbi / modbus_client

BSD 3-Clause "New" or "Revised" License
9 stars 4 forks source link

[feature] number format support #7

Closed FourLeafTec closed 1 day ago

FourLeafTec commented 1 week ago

In modbus, the data maybe have 4 formats:

big-endian: AB CD little-endian: DC BA big-endian byte swap: BA CD little-endian byte swap: CD AB

It seems should have an option to support.

cabbi commented 1 week ago

I knew somewhere in time such a request would come :)

Most commonly Modbus Endianness is about the WORD swap and then there is the BYTE Swap. Referring to your example, if you have this number: 0A0B0C0D (32 bits -> 4 Bytes, 2 Words) then in my understanding (maybe I'm wrong) the "common" Modbus Endianness (i.e. Word wise) you would have: Big-Endian: 0A0B 0C0D Little-Endian: 0C0D 0A0B

My "big" question/doubt is how does this translates with the endianness of the host/client architecture. Let's suppose the host/client CPU architecture is Little-Endian and the Modbus device is Big-Endian, what should the library swap: bytes, words or both?

What I'm trying to say/understand is how we best set parameters so that it would be clear what will be the final result. I'm asking you for suggestions, should we add two parameters like:

FourLeafTec commented 1 week ago

Yes, this issue is about the the WORD swap and the BYTE Swap. It's about numeric record int16,uint16,int32,uint32,float and double.

I think we should be CPU-agnostic when handling this issue, and only concerned with the byte order in Modbus. Such as the 32bit number 123456798(hex:075BCD15) in address [0x0000,0x0001]:

  1. AB CD
    Addr   Data
    0x0000 07 5B
    0x0001 CD 15
  2. DC BA
    Addr   Data
    0x0000 15 CD
    0x0001 5B 07
  3. BA CD
    Addr   Data
    0x0000 5B 07
    0x0001 15 CD
  4. CD AB
    Addr   Data
    0x0000 CD 15
    0x0001 07 5B

The Register should have a parameter to indicate the endianness of the data within this Modbus address. This will allow the data to be organized into an endianness that Dart's ByteData can easily handle, based on the known endianness. This can be done either by specifying the endianness or by reformatting it to Endian.host or Endian.little. Then, ByteData.getUint32(0, Endian) can be called to transform Dart number.

The parameter could be only one parameter with

Enum {
  ABCD,
  DCBA,
  BACD,
  CDAB
}

Just like Modbus poll

FourLeafTec commented 1 week ago

Word swap of the double should be AB CD EF GH -> GH EF CD AB First step AB CD -> CD AB, EF GH -> GH EF Then [CD AB] [GH EF] -> [GH EF] [CD AB]

cabbi commented 1 week ago

I'm implementing this new feature but it will take a bit longer since I want to modify a bit the way package is currently handling requests to make it more flexible. I will publish it asap since I'm making it during my private spare time.

cabbi commented 1 week ago

I've pushed a new 'endianness' branch. This is far from be a release candidate but you can give a try and make suggestions and eventually fixes. Take a look to the 'write_request_example'

FourLeafTec commented 1 week ago

It's great work and works fine on write.

I've create some test for this. It seems run endianness.getEndianBytes twice on read.

Maybe my test function is wrong.

I've used read.internalSetElementData to emulate read data.

      var num = 4159429653
      var bytes = Uint8List.fromList([0xDC, 0x15, 0xF7, 0xEB]);
      var reg = ModbusUint32Register(
          name: "int32",
          address: 14,
          type: ModbusElementType.holdingRegister,
          endianness: ModbusEndianness.CDAB);

      var read = reg.getReadRequest();
      read.internalSetElementData(Uint8List.fromList(bytes));
      expect(read.element.value, 4159429653);

The test fork: modbus_endianness_test

cabbi commented 1 week ago

Your call to internalSetElementData in not needed. It is called internally once the request gets the data from device and will swap them. You need to send the request and wait the response.

FourLeafTec commented 1 week ago

How can I create tests without using a real client? It's really inconvenient to have to use a real client every time.

Is there a way to create a mock?

cabbi commented 4 days ago

I've fixed the double call to endianness byte adjustment. Please give a try. For testing I'm using client simulators, not real devices.

FourLeafTec commented 1 day ago

Works fine, and i create a pr with all test used.