我一直在绞尽脑汁地计算校验和,以便使用二进制命令与Unitronics PLC通信。他们提供的源代码,但它是在一个只有Windows的C#实现,这对我没有什么帮助,除了基本的语法。
规格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+字节细节,得到sum 65536的值,然后返回该值的两个补充。
尝试#1
我的理解是Python中的tilde (~)操作符是直接从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,校验和(详细信息)应该返回。
它返回的值与规范的示例完全不同。我认为问题可能是一个或两个问题:校验和方法没有正确计算十六进制字符串的和,或者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;
}发布于 2013-04-09 23:38:08
我不完全确定正确的代码应该是…但是如果Checksum(Header)的目的是返回f705,并且返回08fb,那么问题是:
x = ( ~( x % 0x10000 ) ) + 1简短的版本是,您希望这样做:
x = (( ~( x % 0x10000 ) ) + 1) % 0x10000这里的问题不是~的含义不同。正如文献资料所说,~x返回“倒置的x位”,这实际上与C中的意思相同(至少在包括所有Windows平台的2S补充平台上是如此)。
在这里,您可能会遇到C和Python类型之间的差异问题(C积分类型是固定大小的,并且溢出;Python积分类型实际上是无限大小的,并根据需要增长)。但我不认为这是你的问题。
问题只是如何将结果转换为字符串。
在这两个版本中,调用Checksum(Header) (直到格式化)的结果是-2299或-0x08fb。
在C中,您几乎可以将有符号整数视为相同大小的无符号整数(但在某些情况下,您可能不得不忽略这样做的警告)。这具体取决于您的平台,但在一个2s补码平台上,签名短-0x08fb是位对位等价于无符号0xf705。因此,例如,如果您使用sprintf(buf, "%04hx", -0x08fb),它就可以正常工作--并且它提供给您(在大多数平台上,包括所有Windows)无符号等效的f705。
但是在Python中,没有无符号整数。Int-0x08fb与0xf 705无关。如果您执行"%04hx" % -0x08fb,您将得到-8fb,并且没有办法强制“将其强制转换为未签名的”或类似的东西。
您的代码实际上是hex(-0x08fb),它为您提供了-0x8fb,然后在x上进行了split,给出了8fb,您将其zfill给了08fb,这使得问题更加难以注意到(因为这看起来像一对非常有效的十六进制字节,而不是一个减号和三个十六进制数字),但这是同一个问题。
无论如何,您必须明确地决定“无符号等效”是什么意思,并编写代码来做到这一点。由于您试图匹配C在2S-补码平台上所做的工作,所以可以将显式转换编写为% 0x10000。如果您执行"%04hx" % (-0x08fb % 0x10000),您将得到f705,就像您在C中所做的一样,对于您现有的代码也是如此。
发布于 2013-04-09 23:46:46
很简单。通过在计算器上手动添加所有头字节,我检查了您朋友的算法,并得到了正确的结果(0xfcf1)。
现在,我并不知道python,但在我看来,它似乎是在加半字节的值。您的头字符串如下所示:
Header = '2F5F4F504C4300FE010000004D000000000001000600'然后将字符串中的每个字节从十六进制中转换出来,然后添加它。这意味着您正在处理0到15之间的值。您需要将每两个字节视为对,并将其转换(值从0到255)。或者您需要使用实际的二进制数据,而不是二进制数据的文本表示。
在算法的末尾,如果您不信任~操作符,您就不需要做它了。相反,您可以做(0xffff - (x % 0x10000)) + 1。请记住,在添加1之前,该值可能实际上是0xffff,因此需要随后由0x10000对整个结果进行模块化。您朋友的C++版本使用short数据类型,所以根本不需要模块,因为short自然会溢出
https://stackoverflow.com/questions/15914299
复制相似问题