ada - 从大端数据中提取记录

标签 ada endianness gnat

我有以下用于网络协议(protocol)实现的代码。由于协议(protocol)是大端的,我想使用 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 位。

最佳答案

所以,

    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;

看来您希望 Is_Exception 成为最后一个字节的 LSB?
for Frame'Bit_Order use System.High_Order_First; LSB 将是第 7 位,

(另外, 16#32# 永远不会是 -- 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 的定义上面,最后一个字节的合法值将是:
  • 2 -> READ_COILS FALSE
  • 3 -> READ_COILS TRUE
  • 8 -> READ_DISCRETE_INPUTS FALSE
  • 9 -> READ_DISCRETE_INPUTS 真

  • 顺便提一句,
    通过将您的更新应用到您的原始程序,并添加/更改以下内容,您的程序对我有用

    添加

        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#));
    

    关于ada - 从大端数据中提取记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56656184/

    相关文章:

    ios - 在 iOS 上使用 AudioQueue 录制小端 PCM

    ada - 如何确定 Ada 文件是使用静态还是动态精化编译的

    ada - 在 Ada Last_Chance_Handler 中打印异常消息

    calendar - 如何在 Ada 中获得自 unix epoch 以来的秒数?

    ada - 如何求一个整数的10对数?

    function - 艾达函数 "+" "*" "-" "/"

    memory-leaks - Ada任务声明导致内存泄漏

    c - 为什么 8 位字段具有字节顺序?

    c++ - 检测二进制文件数据的字节顺序

    ada - 为什么任何迭代都可能无法保留我的循环不变量?