我如何 encode/decode 这个专有校验和(Athena 16C PID 控制器)?

How can I encode/decode this proprietary checksum (Athena 16C PID controller)?

我有一个通过 RS232 控制的 Athena 16C 控制器。它的消息传递协议需要专有校验和:

"CHKSUM:这是一个双字符消息代码编号系统,表示消息中所有字符(不包括 START、CHAR、END CHAR 和 CHKSM 本身)的所有 ASCII 值的总和。使用以下公式计算总和: CHKSM = SUM(All Message Characters)%256 其中 % 表示取模运算符。"

示例消息(来自他们的文档)是这样的:

$Ø1Ø1RØ5C1<CR>

又可以分解为:

$ [START CHAR] 01 [ID] 01 [ZONE] R [TYPE] 05 [PARAM] C1 [CHKSM] <CR> [END CHAR]

我已将此消息发送到控制器,它按预期工作。

我正在用 JS 编写我的代码,并且有以下应该计算 CHKSM 的信息放在消息的末尾:

var sum = 'Ø1Ø1RØ5'
   .split('')
   .map(function(char) {
       return char.charCodeAt(0);
   })
   .reduce(function(current, previous) {
       return previous + current;
   });

var chksm = (sum % 256);
console.log(chksm.toString(16));

根据消息格式,校验和应该是'C1'。但是计算出的总和是 377,这导致校验和为 121,等于十六进制的 79。

// 0 = 48, 1 = 49, R = 82, 5 = 53 (ASCII values)
// 48 + 49 + 48 + 49 + 82 + 48 + 53 = 377
// 377 % 256 = 121 (decimal) = 79 (hex)

Athena 的一位工程师向我发送了以下 VB 代码,但我无法理解其中的逻辑,尤其是语法。一般而言,这个问题是否缺少一些基本的东西?

' Covert the mod % 256 checksum to the 2 chars:
' Will set First and Second chars for encoded value. Pass in the value (Checksum mod 256)
' and where to return the 1st and 2nd chars to.
Public Sub EncodeIt(ByVal Value As Integer, ByRef FirstChar As Integer, ByRef SecondChar As Integer)

    If Value > 359 Then 'Z9 = 359, absolute max possible
        Value = 359
    End If

    'Note: backslash '\' means integer divide, not floating point!!
    If Value > 99 Then
        FirstChar = (Value \ 10) + 65 - 10    '65 = ascii "A"
    Else
        FirstChar = (Value \ 10) + 48          '48 = ascii "0"
    End If

    SecondChar = (Value Mod 10) + 48

End Sub

' Convert the two chars received in a message back to normal integer.
' Take the 2 chars and return a decoded integer value
Public Function DecodeIt(ByVal FirstChar As Integer, ByVal SecondChar As Integer) As Integer

    '65 = ascii "A", 48 = ascii "0"
    If FirstChar > 57 Then '57 = ascii "9"
        Return ((FirstChar - 65 + 10) * 10) + (SecondChar - 48)
    Else
        Return ((FirstChar - 48) * 10) + (SecondChar - 48)

    End If

End Function

十进制到字符串的编码是自定义的,不是base16。这就是 (121).toString(16) 不等于 C1 的原因。 从 post 的 VBA 中, encoding/decoding 函数应该是:

function compute_checksum(message) {
    var sum = 0;
    for(var i=0; i<message.length; i++)
        sum += message.charCodeAt(i);
    return sum % 256;
}

function encode_checksum(value) {
    value = Math.min(value, 359);
    var c1 = ((value / 10) | 0) + (value > 99 ? 55 : 48);
    var c2 = (value % 10) + 48;
    return String.fromCharCode(c1, c2);
}

function decode_checksum(text) {
    var c1 = text.charCodeAt(0);
    var c2 = text.charCodeAt(1);
    return (c1 > 57 ? c1 - 55 : c1 - 48) * 10 + (c2 - 48)
}

这是一个用法示例:

var checksum = compute_checksum('0101R05');
console.log('checksum: ' + checksum);

var CHKSM = encode_checksum(checksum);
console.log('encoded checksum: ' + CHKSM);
console.log('decoded checksum: ' + decode_checksum(CHKSM));

我刚刚为 C# 完成了这个,并在上面的帮助下,想出了这个有效的代码(对我来说): 'reqtemp' 是包含控制器 ID#、区域、参数等的字符串,没有起始字符和校验和字符。

   // generate checksum for Athena
            int x = 0;
            int sl = reqtemp.Length;
            int FirstChar = 0; //checksum 1st character
            int SecondChar = 0; //checksum 2nd char

            string crcr; // crc for requests

            for (int c = 0; c < sl; c++)
            {
                string sel = reqtemp.Substring(c, 1);

                x = x + Convert.ToChar(sel);
            }
            x = x % 256; //modular 256
            x = Math.Min(x, 359); // don't allow > 359
            if (x > 99)
            { FirstChar = (x / 10) + 65 - 10; }
            else
            { FirstChar = (x / 10) + 48; }

            SecondChar = (x % 10) + 48;

            crcr = Char.ConvertFromUtf32(FirstChar) + Char.ConvertFromUtf32(SecondChar);
            //     MessageBox.Show(crcr);


            string reqtempfull = "$"+ reqtemp + crcr + (char)13;
            crc.Text = reqtempfull;  //display the full sp string 
            if (ComPort.IsOpen)
            {
                ComPort.Write(reqtempfull);  // send it`enter code here`
            }