Объедини Modbus в один тип с функциями и количеством 1–4

This commit is contained in:
2026-10-23 16:15:11 +03:00
parent f17c124f0e
commit 41f5b879fc
14 changed files with 247 additions and 354 deletions
+2 -2
View File
@@ -53,8 +53,8 @@ class BusDemoSource:
data = tag + ("%04d" % sid).encode()
elif kind == "modbus":
adr, cnt = self._rng.randint(0, 0x200), self._rng.randint(1, 4)
cid = pc.ProtoCanId.build(0, 1, dev_type, dev_id, pc.MsgType.MODBUS_HOLDING,
pc.merge_modbus(adr, cnt))
cid = pc.ProtoCanId.build(0, 1, dev_type, dev_id, pc.MsgType.MODBUS, # Демонстрация использует единый тип 0x5 вместо прежнего Holding 0x6.
pc.merge_modbus(adr, cnt, pc.ModbusFunction.HOLDING)) # Функция 3 обозначает банк Holding, количество по-прежнему находится в диапазоне 1…4.
data = b"".join(self._rng.randint(0, 0xFFFF).to_bytes(2, "little")
for _ in range(cnt))
elif kind == "gas":
@@ -64,7 +64,7 @@ class CanFrameFields(QWidget): # Компонент содержит тольк
arg_layout.addWidget(self.settings_body) # Два байта SETTINGS заменяют единое поле аргумента.
arg_layout.addStretch(1) # Свободная ширина остаётся справа от редакторов.
self.count = QSpinBox() # RegCount используется протокольными типами Modbus.
self.count.setRange(0, 15) # Поле количества занимает четыре бита Body Modbus.
self.count.setRange(1, 4) # Два младших бита Body кодируют количество минус один, допустимы 1…4 регистра или бита.
self.count.setValue(1) # Исходный запрос относится к одному регистру.
self.sub_label = QLabel("Подтип") # Подпись уточняется при выборе типа сообщения.
self.arg_label = QLabel("SensorID / адрес") # Назначение аргумента зависит от MsgType.
@@ -116,10 +116,11 @@ class CanFrameFields(QWidget): # Компонент содержит тольк
settings = msg_type == pc.MsgType.SETTINGS # Только SETTINGS редактируется отдельными байтами Z и Y.
self.arg.setVisible(not settings) # Единый числовой аргумент скрывается для SETTINGS.
self.settings_body.setVisible(settings) # Двухбайтовый редактор появляется вместо числового аргумента.
table = {pc.MsgType.BROADCAST: (pc.BroadcastType, "Broadcast.Type", "Body (0..15)"), pc.MsgType.DISCRETE: (pc.DiscreteType, "Discrete.Type", "Body (0..4095)"), pc.MsgType.ANALOG: (pc.AnalogType, "Analog.Type", "SensorID (0..4095)")} # Таблица определяет визуальные подписи существующих протокольных перечислений.
table = {pc.MsgType.BROADCAST: (pc.BroadcastType, "Broadcast.Type", "Body (0..15)"), pc.MsgType.DISCRETE: (pc.DiscreteType, "Discrete.Type", "Body (0..4095)"), pc.MsgType.ANALOG: (pc.AnalogType, "Analog.Type", "SensorID (0..4095)"), pc.MsgType.MODBUS: (pc.ModbusFunction, "Функция Modbus (1..4)", "StrAdr (0..4095)")} # Для единого Modbus селектор подтипа выбирает функцию Coil/Discrete/Holding/Input.
self.count.setEnabled(msg_type == pc.MsgType.MODBUS) # Количество доступно только для типа, использующего это поле Body.
current = self.sub.currentText() # Сохраняем выбранный подтип, если он допустим для нового сообщения.
self.sub.clear() # Старые варианты не должны остаться в новом типе сообщения.
self.sub.setEnabled(msg_type in table) # Отдельный подтип доступен только у трёх перечисленных типов.
self.sub.setEnabled(msg_type in table) # Подтип доступен для Broadcast/Discrete/Analog, а у Modbus выбирается функция 1…4.
if msg_type in table: # Подписи и значения перечислений берутся из одного места.
enum, sub_label, arg_label = table[msg_type] # Получаем визуальное описание выбранного MsgType.
self.sub.addItems([item.name for item in enum]) # Значения берутся из общего протокольного каталога.
@@ -128,9 +129,7 @@ class CanFrameFields(QWidget): # Компонент содержит тольк
else: # Типы без отдельного подтипа используют Body по собственному назначению.
sub_label = "—" # Подпись явно показывает отсутствие подтипа.
arg_label = "Body (обычно 0)" # Для PULSE сохраняем исходное описание аргумента.
if msg_type in (pc.MsgType.MODBUS_DISCRETE, pc.MsgType.MODBUS_HOLDING, pc.MsgType.MODBUS_INPUT): # Только сохранённые типы 0x5…0x7 используют Modbus-адрес и количество.
arg_label = "StrAdr (0..4095)" # Двенадцатибитный адрес показан в прежних единицах регистров.
elif msg_type == pc.MsgType.GENERAL_ADDRESS_SPACE: # GAS использует всё поле Body под адрес.
if msg_type == pc.MsgType.GENERAL_ADDRESS_SPACE: # GAS использует всё поле Body под адрес.
arg_label = "Адрес регистра (0..65535)" # Полный 16-битный диапазон указан в подписи.
elif msg_type == pc.MsgType.GAS_SLOT: # Новый тип 0x4 использует весь Body под номер слота, а не StrAdr/RegCount.
arg_label = "Адрес слота (0..65535), Data: маска + 3 слова" # Напоминаем оператору о Word0 с маской и фиксированных позициях трёх значений.
@@ -148,8 +147,8 @@ class CanFrameFields(QWidget): # Компонент содержит тольк
return pc.merge_discrete(pc.DiscreteType[self.sub.currentText()], arg) # Поле собирается общим протокольным модулем.
if msg_type == pc.MsgType.ANALOG: # Аналоговое сообщение передаёт SensorID в Body.
return pc.merge_analog(pc.AnalogType[self.sub.currentText()], arg) # Существующий кодек задаёт расположение подтипа и SensorID.
if msg_type in (pc.MsgType.MODBUS_DISCRETE, pc.MsgType.MODBUS_HOLDING, pc.MsgType.MODBUS_INPUT): # Формат Modbus применяется только к трём оставшимся типам 0x5…0x7.
return pc.merge_modbus(arg, count) # Адрес и количество объединяются общей функцией протокола.
if msg_type == pc.MsgType.MODBUS: # Один MsgType содержит адрес, количество 1…4 и выбранную функцию.
return pc.merge_modbus(arg, count, pc.ModbusFunction[self.sub.currentText()]) # Общий упаковщик проверяет диапазоны и кодирует функцию и количество со смещением на единицу.
if msg_type == pc.MsgType.ERROR: # Сохраняем существующую категорию ошибки с нулевым кодом источника.
return pc.merge_error(0, arg) # Размещение кода ошибки остаётся ответственностью общего ядра.
return arg & 0xFFFF # Остальные типы сохраняют прежнее ограничение Body шестнадцатью битами.