我有以下用于网络协议实现的代码。由于该协议采用大端字节序,因此我想使用 Bit_Order 属性和 High_Order_First 值,但看来我弄错了。
With Ada.Unchecked_Conversion;
with Ada.Text_IO; use Ada.Text_IO;
with System; use System;
procedure Bit_Extraction is
type Byte is range 0 .. (2**8)-1 with Size => 8;
type Command is (Read_Coils,
Read_Discrete_Inputs
) with Size => 7;
for Command use (Read_Coils => 1,
Read_Discrete_Inputs => 4);
type has_exception is new Boolean with Size => 1;
type Frame is record
Function_Code : Command;
Is_Exception : has_exception := False;
end record
with Pack => True,
Size => 8;
for Frame use
record
Function_Code at 0 range 0 .. 6;
Is_Exception at 0 range 7 .. 7;
end record;
for Frame'Bit_Order use High_Order_First;
for Frame'Scalar_Storage_Order use High_Order_First;
function To_Frame is new Ada.Unchecked_Conversion (Byte, Frame);
my_frame : Frame;
begin
my_frame := To_Frame (Byte'(16#32#)); -- Big endian version of 16#4#
Put_Line (Command'Image (my_frame.Function_Code)
& " "
& has_exception'Image (my_frame.Is_Exception));
end Bit_Extraction;
编译可以,但是结果是
raised CONSTRAINT_ERROR : bit_extraction.adb:39 invalid data
我忘记或误解了什么?
更新
实际上的真实记录是
type Frame is record
Transaction_Id : Transaction_Identifier;
Protocol_Id : Word := 0;
Frame_Length : Length;
Unit_Id : Unit_Identifier;
Function_Code : Command;
Is_Exception : Boolean := False;
end record with Size => 8 * 8, Pack => True;
for Frame use
record
Transaction_Id at 0 range 0 .. 15;
Protocol_Id at 2 range 0 .. 15;
Frame_Length at 4 range 0 .. 15;
Unit_id at 6 range 0 .. 7;
Function_Code at 7 range 0 .. 6;
Is_Exception at 7 range 7 .. 7;
end record;
其中 Transaction_Identifier , Word 和 Length 的宽度为16位。
如果我删除 Is_Exception 字段并将 Function_Code 扩展到8位,则这些将正确显示。
要解码的帧的转储如下:
00000000 00 01 00 00 00 09 11 03 06 02 2b 00 64 00 7f
所以我唯一的问题实际上是提取最后一个字节的第8位。
答案 0 :(得分:3)
看看这个AdaCore post on bit order and byte order,看看他们如何处理。阅读这些内容之后,您可能会发现帧值的位顺序实际上是16#08#,可能不是您所期望的。
Big Endian / Little Endian通常是指字节顺序而不是位顺序,因此当您看到网络协议是Big Endian时,它们表示字节顺序。避免为您的记录设置Bit_Order。在现代系统中,您几乎永远不需要它。
您的记录只有一个字节,因此字节顺序对其本身并不重要。当字段值较大(> 8位长)时,字节顺序起作用。
答案 1 :(得分:3)
所以
for Frame'Bit_Order use System.High_Order_First;
似乎您希望Is_Exception是最后一个字节的LSB?
使用16#32#
,最低有效位(LSB)将是第7位,
(而且-- Big endian version of 16#4#
永远不会是 Unit_ID at 6 range 0..7;
Function_Code at 6 range 8 .. 14;
Is_Exception at 6 range 15 .. 15;
,位模式根本不匹配)
相对于它们所使用的单词(而不是字节),指定所有字段可能更为直观明了:
Command
鉴于上面 with Interfaces;
的定义,最后一个字节的合法值为:
顺便说一句, 通过将您的更新应用于原始程序,并添加/更改以下内容,您的程序就可以为我工作
添加
type Byte_Array is array(1..8) of Byte with Pack;
添加
Transaction_ID : Interfaces.Unsigned_16;
Protocol_ID : Interfaces.Unsigned_16;
Frame_Length : Interfaces.Unsigned_16;
Unit_ID : Interfaces.Unsigned_8;
更改,因为我们不知道定义
function To_Frame is new Ada.Unchecked_Conversion (Byte_Array, Frame);
更改
my_frame := To_Frame (Byte_Array'(00, 01, 00, 00, 00, 09, 16#11#, 16#9#));
更改
$js .= '"createdRow": function( row, data, dataIndex){
$(row).addClass(\'redClass\');
}';
$js.='customize: function(xlsx){
var sheet = xlsx.xl.worksheets["sheet1.xml"];
$("row c[r*="10"]",sheet).attr("s","25");
},';
答案 2 :(得分:3)
您的原始记录声明工作正常(GNAT抱怨Pack
,warning: pragma Pack has no effect, no unplaced components
)。问题在于计算小端Byte
。
---------------------------------
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | BE bit numbers
---------------------------------
| c c c c c c c | e |
---------------------------------
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | LE bit numbers
---------------------------------
因此,如果您希望Command
为Read_Discrete_Inputs
,则Byte
必须设置BE位4(LE位3),即LE 16#8#
。
答案 3 :(得分:3)
我终于找到了问题所在。
实际上,Modbus Ethernet Frame definition提到,在发生异常的情况下,返回的代码应为功能代码加128(0x80)(请参阅explanation on Wikipedia)。这就是为什么我想通过 Boolean 值来表示它但我的表示子句错误的原因。
正确的子句是这些:
for Frame use
record
Transaction_Id at 0 range 0 .. 15;
Protocol_Id at 2 range 0 .. 15;
Frame_Length at 4 range 0 .. 15;
Unit_id at 6 range 0 .. 7;
Is_Exception at 6 range 8 .. 8;
Function_Code at 6 range 9 .. 15;
end record;
通过这种方式,可以正确地对Modbus网络协议进行建模(或者至少可以,但我的代码正在运行)。
我非常感谢egilhh和simonwright使我发现问题所在并解释了各个方面的语义。
很明显,我不知道是谁奖励的:)
答案 4 :(得分:0)
bit_order编译指示不会颠倒位在内存中出现的顺序。它仅定义当解释表示形式中字节位置的First_Bit和Last_Bit偏移时,是将最高有效位(最左端)逻辑上称为零(High_Order_First),还是将最低有效位称为零(Low_Order_First)。条款。请记住,这些偏移量取自记录组件属于AS A VALUE的标量的MSB或LSB。因此,为了使字节位置在小字节序CPU上具有与在大字节序CPU上相同的含义(以及多字节机器标量的内存表示形式,当一个或多个记录组件具有相同字节时存在)位置的last_bit值超过单个字节的容量),则必须 指定“ Scalar_Storage_Order”。