Я ломал голову над вычислением контрольной суммы для связи с ПЛК Unitronics с помощью бинарных команд. Они предлагают исходный код, но он находится в реализации C # только для Windows, что мне мало помогает, кроме базового синтаксиса.
Спецификация PDF (расчет контрольной суммы близок к концу)
C # источник драйвера (расчет контрольной суммы в Utils.cs)
Намеченный результат
Ниже указывается байтовый индекс, описание сообщения и пример, который работает.
# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 24 25 26 27 28 29 | 30 31 32
# sx--------------- id FE 01 00 00 00 cn 00 specific--------- lengt CHKSM | numbr ot FF addr- | CHKSM ex
# 2F 5F 4F 50 4C 43 00 FE 01 00 00 00 4D 00 00 00 00 00 01 00 06 00 F1 FC | 01 00 01 FF 01 00 | FE FE 5C
Спецификация требует вычисления накопленного значения 22-байтового заголовка сообщения и, отдельно, 6-байтовой детализации, получения значения суммы по модулю 65536 и последующего возврата двух дополнений к этому значению.
Попытка № 1
Насколько я понимаю, оператор тильды (~) в Python является производным от C / C ++. После дня написания Python, который создает сообщение, я придумал это (урезанная версия):
#!/usr/bin/env python
def Checksum( s ):
x = ( int( s, 16 ) ) % 0x10000
x = ( ~x ) + 1
return hex( x ).split( 'x' )[1].zfill( 4 )
Details = ''
Footer = ''
Header = ''
Message = ''
Details += '0x010001FF0100'
Header += '0x2F5F4F504C4300FE010000004D000000000001000600'
Header += Checksum( Header )
Footer += Checksum( Details )
Footer += '5C'
Message += Header.split( 'x' )[1].zfill( 4 )
Message += Details.split( 'x' )[1].zfill( 4 )
Message += Footer
print Message
Сообщение: 2F5F4F504C4300FE010000004D000000000001000600600L010001FF010001005C
Я вижу там букву L, которая отличается от вчерашнего, который не был ближе. Если вы хотите быстрый результат формулы, основанный на остальной части сообщения: Контрольная сумма (Заголовок) должна возвращать F1FC, а Контрольная сумма (Подробности) должна возвращать FEFE.
Возвращаемое значение нигде не совпадает с примером спецификации. Я полагаю, что проблема может быть в одной или двух вещах: метод Checksum неправильно вычисляет сумму шестнадцатеричной строки или Python ~
оператор не эквивалентен C ++ ~
оператор.
Попытка № 2
Друг дал мне свою C ++ интерпретацию того, что ДОЛЖНЫ быть вычисления, я просто не могу разобраться с этим кодом, мои знания C ++ минимальны.
short PlcBinarySpec::CalcHeaderChecksum( std::vector<byte> _header ) {
short bytesum = 0;
for ( std::vector<byte>::iterator it = _header.begin(); it != _header.end(); ++it ) {
bytesum = bytesum + ( *it );
}
return ( ~( bytesum % 0x10000 ) ) + 1;
}
Я не совсем уверен, каким должен быть правильный код … но если намерение для Checksum(Header)
вернуть f705, и он возвращает 08fb, вот проблема:
x = ( ~( x % 0x10000 ) ) + 1
Короткая версия, что вы хотите это:
x = (( ~( x % 0x10000 ) ) + 1) % 0x10000
Проблема здесь не в том ~
означает что-то другое. Как документация говорит, ~x
возвращает «биты x
инвертированный », что фактически означает то же, что и в С (по крайней мере, на платформах с 2-мя дополнениями, включая все платформы Windows).
Здесь вы можете столкнуться с проблемой различия между типами C и Python (целочисленные типы C имеют фиксированный размер и переполняются; целочисленные типы Python имеют практически бесконечный размер и растут по мере необходимости). Но я не думаю, что это твоя проблема здесь.
Проблема заключается только в том, как вы конвертируете результат в строку.
Результат звонка Checksum(Header)
с точностью до форматирования -2299 или -0x08fb в обеих версиях.
В C вы можете в значительной степени рассматривать целое число со знаком как целое число без знака того же размера (хотя в некоторых случаях вам, возможно, придется игнорировать предупреждения, чтобы сделать это). Что именно это делает, зависит от вашей платформы, но на платформе с дополнением 2s, подписанный короткий -0x08fb является побитовым эквивалентом беззнакового 0xf705. Так, например, если вы делаете sprintf(buf, "%04hx", -0x08fb)
, он работает просто отлично — и дает вам (на большинстве платформ, включая все Windows) эквивалент без знака, f705
,
Но в Python нет целых чисел без знака. Int -0x08fb не имеет ничего общего с 0xf705. Если вы делаете "%04hx" % -0x08fb
, ты получишь -8fb
и нет никакого способа принудительно «привести его к неподписанному» или что-то в этом роде.
Ваш код на самом деле делает hex(-0x08fb)
, который дает вам -0x8fb
, который ты тогда split
на x
, давая вам 8fb
который ты zfill
в 08fb
, что делает проблему немного труднее заметить (потому что это выглядит как совершенно правильная пара шестнадцатеричных байтов вместо знака минус и трех шестнадцатеричных цифр), но это та же проблема.
В любом случае, вы должны явно решить, что вы подразумеваете под «беззнаковым эквивалентом», и написать код для этого. Поскольку вы пытаетесь сопоставить то, что делает C на платформе с комплементом 2s, вы можете записать это явное преобразование как % 0x10000
, Если вы делаете "%04hx" % (-0x08fb % 0x10000)
, ты получишь f705
так же, как вы сделали в C. И аналогично для вашего существующего кода.
Это довольно просто. Я проверил алгоритм вашего друга, добавив все байты заголовка вручную на калькуляторе, и он дает правильный результат (0xfcf1
).
На самом деле я не знаю Python, но мне кажется, что вы складываете полубайтовые значения. Вы сделали свою строку заголовка так:
Header = '2F5F4F504C4300FE010000004D000000000001000600'
И затем вы выполняете преобразование каждого байта в этой строке из шестнадцатеричного кода и добавление его. Это означает, что вы имеете дело со значениями от 0 до 15. Вам необходимо рассматривать каждые два байта как пару и преобразовывать их (значения от 0 до 255). Или вам нужно использовать реальные двоичные данные вместо текстового представления двоичных данных.
В конце алгоритма вам не нужно делать ~
оператор, если вы не доверяете ему. Вместо этого вы можете сделать (0xffff - (x % 0x10000)) + 1
, Имейте в виду, что до добавления 1 значение может быть 0xffff
так что вам нужно по модулю весь результат 0x10000
после этого. C ++ версия вашего друга использует short
тип данных, поэтому по модулю не нужно вообще, потому что short
естественно переполнится