Извлечение записи из данных с прямым порядком байтов - PullRequest
4 голосов
/ 18 июня 2019

У меня есть следующий код для реализации сетевого протокола.Поскольку протокол имеет прямой порядок байтов, я хотел использовать атрибут 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 и Длина имеют ширину 16 бит.

Эти значения отображаются правильно, если я удаляю Is_Exception поле и расширение Function_Code до 8 бит.

Дамп кадра для декодирования выглядит следующим образом:

00000000  00 01 00 00 00 09 11 03  06 02 2b 00 64 00 7f

Так что моя единственная проблема заключается вреально извлечь 8-й бит последнего байта.

Ответы [ 4 ]

3 голосов
/ 19 июня 2019

Я наконец-то нашел, что было не так.

Фактически, в определении кадра Ethernet Modbus упоминается, что в случае исключения возвращаемый код должен быть кодом функции плюс 128 (0x80) (см. объяснение в Википедии ).Вот почему я хотел представить его с помощью логического значения, но мои условия представления были неправильными.

Вот правильные предложения:

   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 за то, что я нашел то, чтобыл неправ и объяснил семантику за аспектами.

Очевидно, я не знаю, кто наградит:)

3 голосов
/ 19 июня 2019

Итак,

    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 был младшим битом последнего байта?При for Frame'Bit_Order use System.High_Order_First; младший бит будет равен 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*

Кстати, применив ваше обновление к исходной программе и добавив / изменив следующее, ваша программа работает для меня

add

    with Interfaces;

add

    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#));
3 голосов
/ 19 июня 2019

Ваша первоначальная декларация записей работает нормально (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 bit 3), т.е. LE 16#8#.

3 голосов
/ 19 июня 2019

Взгляните на этот пост AdaCore о порядке следования битов и байтов , чтобы узнать, как они справляются с этим.Прочитав это, вы, вероятно, обнаружите, что битовый порядок значения вашего кадра действительно 16 # 08 #, что, вероятно, не то, что вы ожидаете.

Big Endian / Little Endian обычно относится к порядку байтов, а небитовый порядок, поэтому, когда вы видите, что сетевые протоколы имеют Big Endian, они означают байтовый порядок.Избегайте установки Bit_Order для ваших записей.В современных системах вам это почти никогда не понадобится.

Ваша запись имеет размер только один байт, поэтому порядок следования байтов для нее сам по себе не имеет значения.Порядок байтов вступает в игру, когда у вас есть большие значения поля (> 8 бит длиной).

...