From e3b6df621c2bcbbc9f7f32ad862ef9f5ad50482a Mon Sep 17 00:00:00 2001 From: MaD_CaT Date: Mon, 20 Jan 2025 11:30:11 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=BA=D0=B0.=20=D0=92=D1=81=D1=82=D1=80=D0=BE=D0=B5=D0=BD?= =?UTF-8?q?=D0=BD=D1=8B=D0=B5=20=D0=BA=D0=BB=D0=B8=D0=B5=D0=BD=D1=82=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D1=82=D0=BE=D0=BA=D0=BE=D0=BB=D0=B0=20modbus?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D0=A93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scenes/pribor-uyep/sch3-view.gd | 2 + scripts/network.gd | 53 +++++++++++- scripts/sch3.gd | 140 ++++++++++++++++++++------------ scripts/settings.gd | 6 +- 4 files changed, 142 insertions(+), 59 deletions(-) diff --git a/scenes/pribor-uyep/sch3-view.gd b/scenes/pribor-uyep/sch3-view.gd index 0f9a586..b8d5acd 100644 --- a/scenes/pribor-uyep/sch3-view.gd +++ b/scenes/pribor-uyep/sch3-view.gd @@ -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 # "Для контроля состояния управляющего элемента подачи электроэнергии на приборы ..." diff --git a/scripts/network.gd b/scripts/network.gd index c14c04d..2053be9 100644 --- a/scripts/network.gd +++ b/scripts/network.gd @@ -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] diff --git a/scripts/sch3.gd b/scripts/sch3.gd index 56dbf8a..978edff 100644 --- a/scripts/sch3.gd +++ b/scripts/sch3.gd @@ -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) diff --git a/scripts/settings.gd b/scripts/settings.gd index 35940e3..b2ea650 100644 --- a/scripts/settings.gd +++ b/scripts/settings.gd @@ -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)'],