У меня есть следующий код для реализации сетевого протокола. Поскольку протокол имеет обратный порядок байтов, я хотел использовать атрибут 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-й бит последнего байта.
Это всего лишь отрывок из настоящей записи. В реальном есть поле размером с слово, поэтому я думал, что оно мне понадобится, но я могу ошибаться.
@Jean-FrançoisFabre, кроме того, вы не можете установить для Scalar_Storage_Order значение, отличное от Bit_Order, как это имеет место.
Я просто не согласен с Byte'(16#32#)); -- Big endian version of 16#4#
. Большой или прямой порядок байтов влияет на байты, а не на биты. На уровне байтов порядок байтов отсутствует
Да, но это не работает с 16#4#.
Я видел, как это делается раньше, придется стереть память о том, как.
Взгляните на этот Сообщение AdaCore о порядке битов и порядке байтов, чтобы увидеть, как они с этим справляются. Прочитав это, вы, вероятно, обнаружите, что порядок битов значения вашего кадра действительно 16 # 08 #, что, вероятно, не то, что вы ожидаете.
Big Endian / Little Endian обычно относится к порядку байтов, а не к порядку битов, поэтому, когда вы видите, что сетевые протоколы имеют порядок байтов, они имеют в виду порядок байтов. Не устанавливайте Bit_Order для своих записей. В современных системах вам это почти никогда не понадобится.
Ваша запись имеет размер всего один байт, поэтому порядок байтов сам по себе не имеет значения. Порядок байтов вступает в игру, когда у вас есть большие значения поля (длиной> 8 бит).
Я уже прочитал это и до сих пор не могу найти способ заставить его работать. Когда вы устанавливаете Scalar_Storage_Order в High_Order_First, вы также должны установить Bit_Order в то же значение. Если нет, то ошибка компиляции.
Так,
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
, допустимые значения для последнего байта будут следующими:
КСТАТИ, применив ваше обновление к вашей исходной программе и добавив/изменив следующее, ваша программа работает для меня
Добавить
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#));
Хорошо, я думаю, что понял благодаря вам и макету Саймона.
Ваше исходное объявление записи работает нормально (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#
.
Я добавил прагму Пакет, потому что в первую очередь использовал логический. Теперь, когда я использую новый тип, определяющий аспект Размер, я думаю, что этого достаточно.
Я, наконец, нашел, что было не так.
Фактически, в Определение кадра 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 необходимо указать также.
Вы уверены, что нуждаетесь в
Bit_Order
? сетевое соглашение имеет прямой порядок байтов, а не битов. Вероятно, вам просто нужен Scalar_Storage_Order.