Modbus TCP, byte order & bit order
От: plastictown Норвегия  
Дата: 04.12.17 15:29
Оценка:
Доброго времени суток! Не ругайтесь, что в который раз про модбас, но я отказываюсь верить в оф. доки-) Если начинать с истоков, то пишу я абстрактный модбас мастер, который по пинку от юзера цепляет конкретный протокол. Сейчас я занялся modbus tcp. Долго боролся с бустом, чтобы его заставить синхронно получать пакеты и не виснуть при обрыве связи. Но это детали. И вот я добрался до реализации протокола и тут начались чудеса. С Big Endian можно смириться, если понятно в каких именно случаях нужно делать своп байтов. А это не очень понятно. Но это ведь не самое страшное. Страшное — это обратный порядок бит в байте. Причем разные симуляторы модбаса возвращают разные результаты на одинаковые запросы.
У меня пока сложилась такая картина:
• Если переменная (в обе стороны) больше байта, нужно поменять байты на Big Endian и наоборот после приема
• Если принимается пачка DI или 'coils — пусть будет контакт' — то длина переменной — 1 байт, и менять порядок негде, но нужно перевернуть весь пакет данных.
• Очень странная штука при использовании mod_RSsim. Если ему воткнуть в di по адресу 0 нулевой бит, в ответе 128. Т.е. биты перевернуты, но в доках как-то особо ничего про это я не нашел. В Modbus Slave такого не замечено.

Не могли бы вы внести ясность в эти вопросы?
Спасибо!

P.S. в основном руководствуюсь MODBUS Application Protocol Specification V1.1
modbus tcp byte_order
Re: Modbus TCP, byte order & bit order
От: kov_serg Россия  
Дата: 04.12.17 16:30
Оценка:
Здравствуйте, plastictown, Вы писали:

P>• Очень странная штука при использовании mod_RSsim. Если ему воткнуть в di по адресу 0 нулевой бит, в ответе 128. Т.е. биты перевернуты, но в доках как-то особо ничего про это я не нашел. В Modbus Slave такого не замечено.

P>Не могли бы вы внести ясность в эти вопросы?
Первый раз такое слышу

http://www.simplymodbus.ca/FC01.htm
Re[2]: Modbus TCP, byte order & bit order
От: plastictown Норвегия  
Дата: 04.12.17 17:03
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Первый раз такое слышу


_>http://www.simplymodbus.ca/FC01.htm


Ну здорово, что сказать. Первый раз вижу настолько неопределенный протокол:

The Modbus specification doesn't define exactly how the data is stored in the registers. Therefore, some manufacturers implemented modbus in their equipment to store and transmit the higher byte first followed by the lower byte. (AE before 41).
Alternatively, others store and transmit the lower byte first (41 before AE).

Similarly, when registers are combined to represent 32-bit data types, Some devices store the higher 16 bits (high word) in the first register and the remaining low word in the second (AE41 before 5652) while others do the opposite (5652 before AE41)


Если принять как факт, что биты трогать не нужно, тогда остается один вопрос:
Если юзер попросил 2 байта, то менять их местами, это понятно, а если 4, то переворачивать все 4 или поменять местами слова? Ну т.е. если предположить, что всегда big endian, независимо от производителя железки. Или эту информацию должен каким-то образом предоставить пользователь моего клиента?
Re[3]: Modbus TCP, byte order & bit order
От: andrey.desman  
Дата: 04.12.17 18:22
Оценка:
Здравствуйте, plastictown, Вы писали:

P>Если принять как факт, что биты трогать не нужно, тогда остается один вопрос:

P>Если юзер попросил 2 байта, то менять их местами, это понятно, а если 4, то переворачивать все 4 или поменять местами слова? Ну т.е. если предположить, что всегда big endian, независимо от производителя железки. Или эту информацию должен каким-то образом предоставить пользователь моего клиента?

Не надо ничего менять. Даже просто представление регистра в uint16_t не самая лучшая идея, потому что мне приходится знать, в каком порядке твоя библиотека их пакует, если я это не указываю явно. В варианте uint8_t[2] это было бы очевидно.
Например, один регистр может отвечать за регулировку двух параметров: первый в первом байте, второй во втором. (Есть у меня такой девайс).

Можешь каких-то хэлперов наделать. Вот у libmodbus есть такое:
MODBUS_API float modbus_get_float_abcd(const uint16_t *src);
MODBUS_API float modbus_get_float_dcba(const uint16_t *src);
MODBUS_API float modbus_get_float_badc(const uint16_t *src);
MODBUS_API float modbus_get_float_cdab(const uint16_t *src);
Re[3]: Modbus TCP, byte order & bit order
От: kov_serg Россия  
Дата: 04.12.17 18:28
Оценка:
Здравствуйте, plastictown, Вы писали:

_>>Первый раз такое слышу


P>Если принять как факт, что биты трогать не нужно, тогда остается один вопрос:

P>Если юзер попросил 2 байта, то менять их местами, это понятно, а если 4, то переворачивать все 4 или поменять местами слова? Ну т.е. если предположить, что всегда big endian, независимо от производителя железки. Или эту информацию должен каким-то образом предоставить пользователь моего клиента?
Мне кажется что вы что-то путаете коилы и регистры вообще никак не должны быть связаны
есть однобитные coils и discret inputs, а есть 16бит-ые регистры (которые ro и rw-holding )
связь этих 4 сущностей к протоколу никакого отношения не имеет.

  ModBus.txt
ReadColils(start,count,out data) // read bits
    ->|    SA,0x01,start[2],count[2],crc[2]
    <-|    SA,0x01,N,data[N],crc[2]
    <-|    SA,0x81,ERR,crc[2]

    N=(count+7)/8
    ERR=1 - not supported
        2 - count<1 || count>0x07D0
        3 - start || start+count invalid
        4 - problem with reading discrete outputs

ReadDiscreteInputs(start,count,out data)
    ->|    SA,0x02,start[2],count[2],crc[2]
    <-| SA,0x02,N,data[N],crc[2]
    <-| SA,0x82,ERR,crc[2]

    N=(count+7)/8

ReadHoldingRegisters(start,count,out data)
    ->| SA,0x03,start[2],count[2],crc[2]
    <-| SA,0x03,N,data[N],crc[2]
    <-| SA,0x83,ERR,crc[2]

    N=count*2

ReadInputRegisters(start,count,out data)
    ->| SA,0x04,start[2],count[2],crc[2]
    <-| SA,0x04,N,data[N],crc[2]
    <-| SA,0x84,ERR,crc[2]

    N=count*2

WriteSingleCoil(start,value)
    ->| SA,0x05,start[2],value[2],crc[2]
    <-| SA,0x05,start[2],value[2],crc[2]
    <-| SA,0x85,ERR,crc[2]

    value={ 0x0000, 0xFF00 }

WriteSingleRegister(start,value)
    ->| SA,0x06,start[2],value[2],crc[2]
    <-| SA,0x06,start[2],value[2],crc[2]
    <-| SA,0x86,ERR,crc[2]

ReadExceptionStatus(out status) (SerialLine only)
    ->| SA,0x07,crc[2]
    <-| SA,0x07,status,crc[2]
    <-| SA,0x87,ERR,crc[2]

    status=bit fields - 8bits

Diagnostics(subfn,data) (SerialLine only)
    ->| SA,0x08,subfn[2],data[2*N],crc[2]
    <-| SA,0x08,subfn[2],data[2*N],crc[2]
    <-| SA,0x88,ERR,crc[2]

    subfn
        00    Return query data
        01    Restart Communication Option
        02    Return Diagnostic Register
        03    Change ASCII Input Delimeter
        04    Force Listen Only Mode
        0A    Clear Counters and Diagnostic Register
        0B    Return Bus Message Count
        0C    Return BUs Communication Error Count
        0D    Return Bus Exception Error Count
        0E    Return Slave Message Count
        0F    Return Slave No Response Count
        10    Return Slave NAK Count
        11    Return Slave Busy Count
        12    Return Bus Character Overrun Count
        14    Clear Overrun Counter and Flag

GetCommEventCounter(out status,out eventcount) (SerialLine only)
    ->|    SA,0x0B,crc[2]
    <-|    SA,0x0B,Status[2],EventCount[2],crc[2]
    <-|    SA,0x8B,ERR,crc[2]

GetCommEventLog (SerialLine only)
    ->|    SA,0x0C,crc[2]
    <-|    SA,0x0C,N,Status[2],EventCount[2],MessageCount[2],Events[N-6],crc[2]
    <-|    SA,0x8C,ERR,crc[2]

    ...

WriteMultipleCoils(start,count,coils)
    ->|    SA,0x0F,start[2],count[2],N,data[N],crc[2]
    <-|    SA,0x0F,start[2],count[2],crc[2]
    <-|    SA,0x8F,ERR,crc[2]

    N=(count+7)/8

WriteMultipleRegisters
    ->|    SA,0x10,start[2],count[2],N,data[N],crc[2]
    <-|    SA,0x10,start[2],count[2],crc[2]
    <-|    SA,0x90,ERR,crc[2]

ReportSlaveID (SerialLine only)
    ->|    SA,0x11,crc[2]
    <-|    SA,0x11,N,ID[],RunIndStatus,AdditionalData[],crc[2]
    <-|    SA,0x91,ERR,crc[2]

ReadFileRecord
    ->|    SA,0x14,ByteCount,{ RefType=6,FileNum[2],RecNum[2],RecLen[2] }[],crc[2]
    <-|    SA,0x14,N, { FileRespLen,RefType=6,Data[RecLen*2] }[],crc[2]
    <-|    SA,0x94,ERR,crc[2]

WriteFileRecord
    ->|    SA,0x15,ByteCount,{ RefType=6,FileNum[2],RecNum[2],RecLen[2],Data[RecLen*2] }[],crc[2]
    <-|    SA,0x15,ByteCount,{ RefType=6,FileNum[2],RecNum[2],RecLen[2],Data[RecLen*2] }[],crc[2]
    <-|    SA,0x95,ERR,crc[2]

MaskWriteRegister
    ->|    SA,0x16,start[2],AndMask[2],OrMask[2],crc[2]
    <-|    SA,0x16,start[2],AndMask[2],OrMask[2],crc[2]
    <-|    SA,0x96,ERR,crc[2]

ReadWriteMultipleRegisters
    ->|    SA,0x17,rstart[2],rcnt[2],wstart[2],wcnt[2],wN,data[wN],crc[2]
    <-|    SA,0x17,rN,data[rN],crc[2]
    <-|    SA,0x97,ERR,crc[2]

ReadFIFOQueue
    ->|    SA,0x18,addr[2],crc[2]
    <-|    SA,0x18,len[2],count[2],data[count*2],crc[2]
    <-|    SA,0x98,ERR,crc[2]

    len=2+count*2

EncapsulatedInterfaceTransport
    ->|    SA,0x2B,MEIType,Data[],crc[2]
    <-| SA,0x2B,MeIType,Data[],crc[2]
    <-| SA,0xAB,ERR,crc[2]
Отредактировано 04.12.2017 18:38 kov_serg . Предыдущая версия . Еще …
Отредактировано 04.12.2017 18:38 kov_serg . Предыдущая версия .
Re[4]: Modbus TCP, byte order & bit order
От: plastictown Норвегия  
Дата: 04.12.17 20:15
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Мне кажется что вы что-то путаете коилы и регистры вообще никак не должны быть связаны

_>есть однобитные coils и discret inputs, а есть 16битные регистры (которые ro и rw-holding )
_>связь этих 4 сущностей к протоколу никакого отношения не имеет.

я не говорю, что они связаны. Просто клиент может попросить меня выдать ему 4 регистра, например. Мне придет 8 байт, а в каком порядке лежат данные я понятия не имею, кроме того, что в спецификации написано, что принято использовать big endian. Получается, нужно поменять байты в каждом регистре отдельно. Но исходя из цитаты ранее, получается, что порядок слов также может быть обратным. Ну и плюс ко всему вся эта аморфная ктулху приходит задом наперед.

Про порядок бит в байте, это вероятно все-таки симулятор чудит что-то.
Re[5]: Modbus TCP, byte order & bit order
От: kov_serg Россия  
Дата: 04.12.17 22:41
Оценка: 3 (1) +2
Здравствуйте, plastictown, Вы писали:

P>я не говорю, что они связаны. Просто клиент может попросить меня выдать ему 4 регистра, например. Мне придет 8 байт, а в каком порядке лежат данные я понятия не имею, кроме того, что в спецификации написано, что принято использовать big endian. Получается, нужно поменять байты в каждом регистре отдельно. Но исходя из цитаты ранее, получается, что порядок слов также может быть обратным. Ну и плюс ко всему вся эта аморфная ктулху приходит задом наперед.

Всегда использовался сетевой порядок байт и 16битные регистры в modbus. В них упаковывали 32х битные значения, но что бы такое значение прочитать надо читать сразу два регистра или больше, а вот в каком порядке лежат значения 32х битных и больше значений зависит от устройства, тут карту регистров надо смотреть.

P>Про порядок бит в байте, это вероятно все-таки симулятор чудит что-то.

Попробуйте другой. https://habrahabr.ru/post/281430/ http://www.plcsimulator.org/
Re[6]: Modbus TCP, byte order & bit order
От: plastictown Норвегия  
Дата: 05.12.17 10:14
Оценка:
Здравствуйте, kov_serg, Вы писали:

P>>Про порядок бит в байте, это вероятно все-таки симулятор чудит что-то.

_>Попробуйте другой. https://habrahabr.ru/post/281430/ http://www.plcsimulator.org/
_>Image: 633601a0db318202ee47aa7d5d5f84a9.jpg

Так уж вышло, что это и есть тот симулятор, с которым проблемы
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.