Доработка. Встроенные клиент протокола modbus для Щ3

This commit is contained in:
MaD_CaT
2025-01-20 11:30:11 +03:00
parent e7d6056799
commit e3b6df621c
4 changed files with 142 additions and 59 deletions

View File

@@ -154,6 +154,8 @@ func on_resp_holding(base_addr: int, data: Array):
## [param base_addr] - Начальный адрес регистров.[br]
## [param data] - Массив значений прочитанных из регистров.[br]
func on_resp_input(base_addr: int, data: Array):
if !is_inside_tree():
return
if (base_addr != base_input_address) or (data.size() < regs_input_count):
return
# "Для контроля состояния управляющего элемента подачи электроэнергии на приборы ..."

View File

@@ -1,9 +1,10 @@
class_name Network extends Node
const PROTOCOL_TYPES: = {'yau07tx': yau07.YaU07, 'json-capsrpb': capsrpb.CapsRpb, 'udp-json-mbcs': sch3.Sch3}
const PROTOCOL_TYPES: = {'yau07tx': yau07.YaU07, 'json-capsrpb': capsrpb.CapsRpb}
const TCP_PROTO: = {'5p28': tcp5p28.TCP5P28}
const SERIAL_PROTO: = {'uart': spt25.SPT25 }
const SERIAL_PROTO: = {'uart': spt25.SPT25}
const MODBUS_PROTO: = {'mbcs': sch3.Sch3}
var tick: int = 0
@@ -47,13 +48,26 @@ class SocketTCP extends TCPServer:
peer.put_data(data)
class Modbus extends ModbusClientRtu:
func send_to(data):
if data['code'] == 'write':
var addr = data['data'].pop_at(0)
request_write(addr, data['data'])
elif data['code'] == 'read_holding':
request_read(data['data'][0], data['data'][1])
elif data['code'] == 'read_input':
request_read_input(data['data'][0], data['data'][1])
var poll_sockets: Array[Socket]
var tcp_sockets: Array
var units: Dictionary
var units_udp: Dictionary
var units_tcp: Dictionary
var units_serial: Dictionary
var serials: Dictionary
var units_modbus: Dictionary
var serials: Dictionary # Последовательные порты
var mbcs: Dictionary # Клиенты modbus.
var dst_ports: Dictionary
var unit_keys: Dictionary
var sock_unicast: Socket
@@ -97,11 +111,23 @@ func create_serial(nm) -> Serial:
return sp
func create_modbus(nm, mbc_unit) -> Modbus:
var port = settings.UnitProfiles[nm][1]
var baud = settings.UnitProfiles[nm][2]
var params = settings.UnitProfiles[nm][3]
var mbc: = Modbus.new()
mbc.connect('read', mbc_unit.on_read)
mbc.connect('read_input', mbc_unit.on_read_input)
mbc.connect('write', mbc_unit.on_write)
var _rc = mbc.open(port, params[0], baud[0], params[1], params[2], params[3])
mbc.thread_run()
return mbc
func _ready() -> void:
poll_sockets.append(create_socket('уарэп-яу07-частный'))
poll_sockets.append(create_socket('уарэп-яу07-общий'))
poll_sockets.append(create_socket('уарэп-капсрпб'))
poll_sockets.append(create_socket('уарэп-щ3'))
sock_unicast = poll_sockets[0]
sock_capsrpb = poll_sockets[2]
@@ -130,6 +156,19 @@ func _ready() -> void:
unit_tcp.connect('line_changed', Callable(logger_page, 'on_line_changed').bind(unit_key))
unit_tcp.connect('command_fail', Callable(logger_page, 'on_command_fail').bind(unit_key))
if proto in MODBUS_PROTO:
var unit_modbus = MODBUS_PROTO[proto].new(unit_name)
var unit_key = settings.get_unit_key(unit_name)
if logger_page:
unit_modbus.connect('line_changed', Callable(logger_page, 'on_line_changed').bind(unit_key))
unit_modbus.connect('command_fail', Callable(logger_page, 'on_command_fail').bind(unit_key))
var mbc = create_modbus(unit_name, unit_modbus)
units_modbus[unit_key] = unit_modbus
units[unit_key] = unit_modbus
mbcs[unit_key] = mbc
if proto in SERIAL_PROTO:
var unit_serial = SERIAL_PROTO[proto].new(unit_name)
var unit_key = settings.get_unit_key(unit_name)
@@ -226,6 +265,12 @@ func _process(_delta: float) -> void:
match unit_serial.process(tick):
Error.OK: serial.send_to(unit_serial.tx_data)
Error.FAILED: emit_signal('socket_error', [unit_serial, serial])
for key in units_modbus:
var unit_modbus = units_modbus[key]
var mbc = mbcs[key]
match unit_modbus.process(tick):
Error.OK: mbc.send_to(unit_modbus.mb_data)
Error.FAILED: emit_signal('socket_error', [unit_modbus, mbc])
for sock_unit in tcp_sockets:
var sock = sock_unit[0]
var unit_tcp = sock_unit[1]

View File

@@ -9,10 +9,11 @@ const REQUEST_PERIOD: int = 1000 ## Переиод запроса, мс
## Представление прибора Щ3
class Sch3 extends unit.Unit:
var mb_addr: = 0 ## Адрес устройства Modbus.
var cmd_state: = CmdState.DONE ## Состояние выполнения текущей команды.
var tick_rx: = 0 ## Время приёма крайнего сообщения.
var tick_tx: = 0 ## Время отправки крайнего сообщения.
var mb_addr: = 0 ## Адрес устройства Modbus.
var cmd_state: = CmdState.DONE ## Состояние выполнения текущей команды.
var tick_rx: = 0 ## Время приёма крайнего сообщения.
var tick_tx: = 0 ## Время отправки крайнего сообщения.
var mb_data: Dictionary = {} ## Буфер для записи в регистры модбас
static var json_conv: = JSON.new()
signal resp_input(addr: int, data: Array) ## Вызывается при получении сообщения c данными из регистров входов.
@@ -48,50 +49,43 @@ class Sch3 extends unit.Unit:
## Производит разбор принятых данных. Вызывать при приёме данных на сокете.[br]
## [param data] - Данные принятые на сокете.[br]
## [param tick] - Текущщее время в мс.[br]
func parse(data: PackedByteArray, tick: int) -> bool:
func parse(message: Dictionary, tick: int) -> bool:
rx_tick = tick
if not online:
online = true
emit_signal('line_changed', self)
var utf8_data = data.get_string_from_utf8()
if Error.OK == json_conv.parse(utf8_data):
emit_signal('data_received', data)
var message: Dictionary = json_conv.get_data()
if message.has('code') and message.has('data'):
var rc: = true
var message_code = message['code']
var message_data = message['data']
match message_code:
'resp_holding', 'resp_input':
var addr = int(message_data[0])
var regs = message_data.slice(1)
for i in regs.size():
regs[i] = int(regs[i])
emit_signal(message_code, addr, regs)
'resp_write':
emit_signal(message_code)
'info':
var msg = message['data']
emit_signal('info_received', msg)
'exception':
var msg = message['data']
cmd_state = CmdState.FAIL
emit_signal('except_received', msg)
rc = false
_:
var msg: String = 'код команды \"%s\" - не распознан' % message_code
cmd_state = CmdState.FAIL
emit_signal('parse_failed', msg)
rc = false
if (cmd_state == CmdState.WAIT) and rc:
cmd_state = CmdState.DONE
emit_signal('command_done')
elif (cmd_state == CmdState.WAIT) and (not rc):
if message.has('code') and message.has('data'):
var rc: = true
var message_code = message['code']
var message_data = message['data']
match message_code:
'resp_holding', 'resp_input':
var addr = int(message_data[0])
var regs = message_data.slice(1)
for i in regs.size():
regs[i] = int(regs[i])
emit_signal(message_code, addr, regs)
'resp_write':
emit_signal(message_code)
'info':
var msg = message['data']
emit_signal('info_received', msg)
'exception':
var msg = message['data']
cmd_state = CmdState.FAIL
emit_signal('command_fail', self)
else:
var message = json_conv.get_error_message()
emit_signal('parse_failed', message)
emit_signal('except_received', msg)
rc = false
_:
var msg: String = 'код команды \"%s\" - не распознан' % message_code
cmd_state = CmdState.FAIL
emit_signal('parse_failed', msg)
rc = false
if (cmd_state == CmdState.WAIT) and rc:
cmd_state = CmdState.DONE
emit_signal('command_done')
elif (cmd_state == CmdState.WAIT) and (not rc):
cmd_state = CmdState.FAIL
emit_signal('command_fail', self)
return true
## Отправляет команду "записать в регистры".[br]
@@ -99,9 +93,7 @@ class Sch3 extends unit.Unit:
## [param count] - Данные для записи в регистры от начального адреса.[br]
func write(addr: int, data: Array) -> bool:
if cmd_state != CmdState.DONE: return false
var json_dict: = {'code': 'write', 'data': [addr] + data, 'unit': mb_addr}
tx_data = JSON.stringify(json_dict).to_utf8_buffer()
tx_len = len(tx_data)
mb_data = {'code': 'write', 'data': [addr] + data, 'unit': mb_addr}
cmd_state = CmdState.SEND
return true
@@ -110,9 +102,7 @@ class Sch3 extends unit.Unit:
## [param count] - Количество регистров от начального адреса.[br]
func read_holding(addr: int, count: int = 1) -> bool:
if cmd_state != CmdState.DONE: return false
var json_dict: = {'code': 'read_holding', 'data': [addr, count], 'unit': mb_addr}
tx_data = JSON.stringify(json_dict).to_utf8_buffer()
tx_len = len(tx_data)
mb_data = {'code': 'read_holding', 'data': [addr, count], 'unit': mb_addr}
cmd_state = CmdState.SEND
return true
@@ -121,8 +111,54 @@ class Sch3 extends unit.Unit:
## [param count] - Количество регистров от начального адреса.[br]
func read_input(addr: int, count: int = 1) -> bool:
if cmd_state != CmdState.DONE: return false
var json_dict: = {'code': 'read_input', 'data': [addr, count], 'unit': mb_addr}
tx_data = JSON.stringify(json_dict).to_utf8_buffer()
tx_len = len(tx_data)
mb_data = {'code': 'read_input', 'data': [addr, count], 'unit': mb_addr}
cmd_state = CmdState.SEND
return true
## Принимает результат команды "читать регистры хранения".[br]
## [param return_code] - Код выполнения команды.[br]
## [param base_addr] - Начальный адрес регистров.[br]
## [param data] - Данные прочитанные из регистров.[br]
## [param tick] - Время приёма пакета.[br]
func on_read(return_code, base_addr, data):
var tick = Time.get_ticks_msec()
var msg: Dictionary
if return_code == 0:
msg['code'] = 'resp_holding'
msg['data'] = [base_addr] + data
else:
msg['code'] = 'exception'
parse(msg, tick)
## Принимает результат команды "читать регистры входов".[br]
## [param return_code] - Код выполнения команды.[br]
## [param base_addr] - Начальный адрес регистров.[br]
## [param data] - Данные прочитанные из регистров.[br]
## [param tick] - Время приёма пакета.[br]
func on_read_input(return_code, base_addr, data):
var tick = Time.get_ticks_msec()
var msg: Dictionary
if return_code == 0:
msg['code'] = 'resp_input'
msg['data'] = [base_addr] + data
else:
msg['code'] = 'exception'
parse(msg, tick)
## Принимает результат команды "записать в регистры".[br]
## [param return_code] - Код выполнения команды.[br]
## [param base_addr] - Начальный адрес регистров.[br]
## [param data] - Данные прочитанные из регистров.[br]
## [param tick] - Время приёма пакета.[br]
func on_write(return_code, _base_addr, data):
var tick = Time.get_ticks_msec()
var msg: Dictionary
msg['data'] = 'resp_write'
if return_code == 0:
msg['code'] = 'resp_write'
else:
msg['code'] = 'exception'
parse(msg, tick)

View File

@@ -9,7 +9,7 @@ var settings_program: Dictionary
@export var UnitProfiles: = { \
# Название | Протокол | Адрес | Порт | Широковещательное | Привязывать к адресу | Комментарий
'уарэп-яу07-частный': ['yau07rx', '10.1.1.14', [50014], false, true, 'IP-адрес на котором принимают частные сообщения от ЯУ-07'],
'уарэп-яу07-частный': ['yau07rx', '10.1.1.2', [50002], false, true, 'IP-адрес на котором принимают частные сообщения от ЯУ-07'],
'уарэп-яу07-общий': ['yau07rx', '*', [50000], true, true, 'IP-адрес на котором принимают широковещательные сообщения от ЯУ-07'],
'уарэп-капсрпб': ['json-capsrpb', '127.0.0.1', [42023, 42022], false, true, 'IP-адрес на котором принимают сообщения от КАПС РПБ'],
'уарэп-эмс': ['yau07tx', '10.1.1.52', [50052], false, false, 'IP-адрес ЯУ-07 прибора УФ'],
@@ -29,8 +29,8 @@ var settings_program: Dictionary
# Название: [ Протокол, Адрес, Порт получения, Комментарий ]
'уарэп-5п28': [ '5p28', '192.168.3.109', [50000], 'IP-адрес комплекса 5П-28'],
# Название: [ Протокол, Адрес, [ Порт получения, Порт отправки], ??, ??, Комментарий
'уарэп-щ3': ['udp-json-mbcs', '127.0.0.1', [48000, 44100], false, true, 'IP-адрес и порты через которые производят обмен сообщения с драйвером клиента Modbus Serial'],
# Название: [ Протокол, Адрес, Скорость, ??, ??, Комментарий
'уарэп-щ3': ['mbcs', '/home/sasha/dev/tty1', [19200], [3, 'N', 8, 1], 'Параметры клиента Modbus Serial'],
# Название: [ Протокол, Имя порта, Скорость, Чётность, ??, Комментарий
'уарэп-спт25-1': ['uart', '/home/sasha/dev/tty0', [19200, false, false], 'Адрес прибора СПТ-25 (G1)'],