简体   繁体   English

二进制PLC通信的校验和

[英]Checksum for binary PLC communication

I've been scratching my head around calculating a checksum to communicate with Unitronics PLCs using binary commands. 我一直在努力计算使用二进制命令与Unitronics PLC通信的校验和。 They offer the source code but it's in a Windows-only C# implementation, which is of little help to me other than basic syntax. 他们提供了源代码,但是它是在仅Windows的C#实现中进行的,除了基本语法之外,对我几乎没有帮助。

Specification PDF (the checksum calculation is near the end) 规格PDF (校验和计算即将结束)

C# driver source (checksum calculation in Utils.cs) C#驱动程序源 (Utils.cs中的校验和计算)

Intended Result 预期结果

Below is the byte index, message description and the sample which does work. 以下是字节索引,消息描述和起作用的示例。

#  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

The specification calls for calculating the accumuated value of the 22 byte message header and, seperately, the 6+ byte detail, getting the value of sum modulo 65536, and then returning two's complement of that value. 该规范要求计算22字节消息头的累加值,以及分别计算6+字节细节的累加值,得到和模65536的值,然后返回该值的二进制补码。

Attempt #1 尝试#1

My understanding is the tilde (~) operator in Python is directly derived from C/C++. 我的理解是Python中的波浪号(〜)运算符直接来自C / C ++。 After a day of writing the Python that creates the message I came up with this (stripped down version): 在写了一天创建消息的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

Message: 2F5F4F504C4300FE010000004D000000000001000600600L010001FF010001005C 讯息: 2F5F4F504C4300FE010000004D000000000001000600600L010001FF010001005C

I see an L in there, which is a different result to yesterday, which wasn't any closer. 我在那儿看到了L,这与昨天的结果是不同的,直到今天为止。 If you want a quick formula result based on the rest of the message: Checksum(Header) should return F1FC and Checksum(Details) should return FEFE . 如果您需要基于消息其余部分的快速公式结果: Checksum(Header)应该返回F1FC,而Checksum(Details)应该返回FEFE

The value it returns is nowhere near the same as the specification's example. 它返回的值与规范的示例几乎没有相同。 I believe the issue may be one or two things: the Checksum method isn't calculating the sum of the hex string correctly or the Python ~ operator is not equivalent to the C++ ~ operator. 我认为,问题可能是一个或两件事情:校验方法不正确计算十六进制字符串的总和或Python的~运营商是不是等同于C ++ ~运营商。

Attempt #2 尝试#2

A friend has given me his C++ interpretation of what the calculation SHOULD be, I just can't get my head around this code, my C++ knowledge is minimal. 一位朋友给了我有关计算的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;
}

I'm not entirely sure what the correct code should be… but if the intention is for Checksum(Header) to return f705, and it's returning 08fb, here's the problem: 我不确定要正确的代码是什么……但是如果要让Checksum(Header)返回f705,而返回的是08fb,那就是问题所在:

x = ( ~( x % 0x10000 ) ) + 1

The short version is that you want this: 简短的版本是您想要的:

x = (( ~( x % 0x10000 ) ) + 1) % 0x10000

The problem here isn't that ~ means something different. 这里的问题不是~意味着不同。 As the documentation says, ~x returns "the bits of x inverted", which is effectively the same thing it means in C (at least on 2s-complement platforms, which includes all Windows platforms). 由于文档说, ~x回报“的位x倒”,这实际上是它意味着在C同样的事情(至少在二进制补码平台,其中包括所有Windows平台)。

You can run into a problem with the difference between C and Python types here (C integral types are fixed-size, and overflow; Python integral types are effectively infinite-sized, and grow as needed). 您可能会遇到C和Python类型之间的差异的问题(C整数类型是固定大小且有溢出; Python整数类型实际上是无穷大,并根据需要增长)。 But I don't think that's your problem here. 但我认为这不是您的问题。

The problem is just a matter of how you convert the result to a string. 问题仅在于如何将结果转换为字符串。

The result of calling Checksum(Header) , up to the formatting, is -2299, or -0x08fb, in both versions. 在两个版本中,调用Checksum(Header)的结果Checksum(Header)直至格式)均为-2299或-0x08fb。

In C, you can pretty much treat a signed integer as an unsigned integer of the same size (although you may have to ignore warnings to do so, in some cases). 在C语言中,您几乎可以将带符号整数视为相同大小的无符号整数(尽管在某些情况下,您可能不得不忽略警告)。 What exactly that does depends on your platform, but on a 2s-complement platform, signed short -0x08fb is the bit-for-bit equivalent of unsigned 0xf705. 确切的功能取决于您的平台,但在2s补码平台上,带符号的短-0x08fb与无符号的0xf705逐位等效。 So, for example, if you do sprintf(buf, "%04hx", -0x08fb) , it works just fine—and it gives you (on most platforms, including everything Windows) the unsigned equivalent, f705 . 因此,例如,如果您执行sprintf(buf, "%04hx", -0x08fb) ,它就可以正常工作-并且(在大多数平台上,包括所有Windows上)为您提供无符号的等效值f705

But in Python, there are no unsigned integers. 但是在Python中,没有无符号整数。 The int -0x08fb has nothing to do with 0xf705. int -0x08fb与0xf705无关。 If you do "%04hx" % -0x08fb , you'll get -8fb , and there's no way to forcibly "cast it to unsigned" or anything like that. 如果您执行"%04hx" % -0x08fb ,您将得到-8fb ,并且无法强制“将其强制转换为未签名”或类似的东西。

Your code actually does hex(-0x08fb) , which gives you -0x8fb , which you then split on the x , giving you 8fb , which you zfill to 08fb , which makes the problem a bit harder to notice (because that looks like a perfectly valid pair of hex bytes, instead of a minus sign and three hex digits), but it's the same problem. 您的代码实际上执行hex(-0x08fb) ,给您-0x8fb ,然后在xsplit ,给您8fb ,您将其zfill08fb ,这使问题更难注意到(因为看起来很完美)有效的十六进制字节对,而不是减号和三个十六进制数字),但这是相同的问题。

Anyway, you have to explicitly decide what you mean by "unsigned equivalent", and write the code to do that. 无论如何,您必须明确确定“无符号等效项”的含义,并编写代码来做到这一点。 Since you're trying to match what C does on a 2s-complement platform, you can write that explicit conversion as % 0x10000 . 由于您试图匹配C在2s补码平台上的功能,因此可以将该显式转换写为% 0x10000 If you do "%04hx" % (-0x08fb % 0x10000) , you'll get f705 , just as you did in C. And likewise for your existing code. 如果执行"%04hx" % (-0x08fb % 0x10000)f705得到f705 ,就像在C中所做的一样。对于现有代码也是如此。

It's quite simple. 这很简单。 I checked your friend's algorithm by adding all the header bytes manually on a calculator, and it yields the correct result ( 0xfcf1 ). 我通过在计算器上手动添加所有标头字节来检查您朋友的算法,它产生正确的结果( 0xfcf1 )。

Now, I don't actually know python, but it looks to me like you are adding up half-byte values. 现在,我实际上并不了解python,但是在我看来,这就像您要添加半字节值一样。 You have made your header string like this: 您已使标题字符串如下所示:

Header = '2F5F4F504C4300FE010000004D000000000001000600'

And then you go through converting each byte in that string from hex and adding it. 然后,您将十六进制转换为字符串中的每个字节并将其相加。 That means you are dealing with values from 0 to 15. You need to consider every two bytes as a pair and convert that (values from 0 to 255). 这意味着您正在处理0到15之间的值。您需要将每两个字节视为一对,并将其转换(值从0到255)。 Or you need to use actual binary data instead of a text representation of the binary data. 或者,您需要使用实际的二进制数据而不是二进制数据的文本表示形式。

At the end of the algorithm, you don't really need to do the ~ operator if you don't trust it. 在算法的最后,如果您不信任它,则实际上不需要执行~运算符。 Instead you can do (0xffff - (x % 0x10000)) + 1 . 相反,您可以执行(0xffff - (x % 0x10000)) + 1 Bear in mind that prior to adding 1, the value might actually be 0xffff , so you need to modulo the entire result by 0x10000 afterwards. 请记住,在加1之前,该值实际上可能是0xffff ,因此此后需要对整个结果乘以0x10000 Your friend's C++ version uses the short datatype so no modulo is necessary at all because the short will naturally overflow 您朋友的C ++版本使用short数据类型,因此根本不需要模,因为short会自然溢出

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM