Извлечение записи из данных с обратным порядком байтов

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

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

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

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

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

Вы уверены, что нуждаетесь в Bit_Order? сетевое соглашение имеет прямой порядок байтов, а не битов. Вероятно, вам просто нужен Scalar_Storage_Order.

Jean-François Fabre 18.06.2019 21:56

Это всего лишь отрывок из настоящей записи. В реальном есть поле размером с слово, поэтому я думал, что оно мне понадобится, но я могу ошибаться.

Frédéric Praca 18.06.2019 22:01

@Jean-FrançoisFabre, кроме того, вы не можете установить для Scalar_Storage_Order значение, отличное от Bit_Order, как это имеет место.

Frédéric Praca 18.06.2019 22:22

Я просто не согласен с Byte'(16#32#)); -- Big endian version of 16#4#. Большой или прямой порядок байтов влияет на байты, а не на биты. На уровне байтов порядок байтов отсутствует

Jean-François Fabre 18.06.2019 22:25

Да, но это не работает с 16#4#.

Frédéric Praca 18.06.2019 22:27

Я видел, как это делается раньше, придется стереть память о том, как.

darkestkhan 19.06.2019 08:35
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
5
6
618
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

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

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

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

Я уже прочитал это и до сих пор не могу найти способ заставить его работать. Когда вы устанавливаете Scalar_Storage_Order в High_Order_First, вы также должны установить Bit_Order в то же значение. Если нет, то ошибка компиляции.

Frédéric Praca 19.06.2019 07:27

Так,

    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; 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 ЛОЖЬ
  • 3 -> READ_COILS ИСТИНА
  • 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#));

Хорошо, я думаю, что понял благодаря вам и макету Саймона.

Frédéric Praca 19.06.2019 17:31

Ваше исходное объявление записи работает нормально (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 должен быть установлен бит 4 BE (бит 3 LE), то есть LE 16#8#.

Я добавил прагму Пакет, потому что в первую очередь использовал логический. Теперь, когда я использую новый тип, определяющий аспект Размер, я думаю, что этого достаточно.

Frédéric Praca 19.06.2019 17:26
Ответ принят как подходящий

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

Фактически, в Определение кадра Modbus Ethernet упоминается, что в случае исключения возвращаемый код должен быть кодом функции плюс 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 моделируется правильно (или нет, но, по крайней мере, мой код работает).

Я очень благодарен эгилхх и Саймонрайт за то, что они помогли мне найти ошибку и объяснить семантику аспектов.

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

Прагма bit_order не меняет порядок расположения битов в памяти. Он просто определяет, будет ли старший значащий бит (самый левый) логически называться нулем (High_Order_First) или младший значащий бит будет называться нулем (Low_Order_First) при интерпретации смещений First_Bit и Last_Bit относительно позиции байта в представлении. пункт. Имейте в виду, что эти смещения берутся из MSB или LSB скаляра, компонент записи принадлежит КАК ЗНАЧЕНИЕ. Таким образом, для того, чтобы позиции байтов имели то же значение на ЦП с прямым порядком байтов, что и на ЦП с обратным порядком байтов (а также представление в памяти многобайтовых машинных скаляров, которые существуют, когда один или несколько компонентов записи с одним и тем же байтом position имеют значение last_bit, превышающее емкость одного байта), то для Scalar_Storage_Order необходимо указать также.

Другие вопросы по теме