extends Node const ONLINE_TIMEOUT = 5.0 ## Время ожидания пакета от ячейки, секунды class ModbusClient: ## Состояние автомата выполнения команды enum CmdState { DONE, ## Команда выполнена FAIL, ## Команда не выполнена SEND, ## Буфер готов для отправки WAIT ## Ожидание выполнения } var name: String var unit: = 1 ## Адрес устройства Modbus var cmd_state: = CmdState.DONE ## Код текущей команды var tx_data: PackedByteArray ## Данные для отправки var online: = false ## Флаг. Если установлен, устройство на связи. Активный уровень - [b]true[/b]. var tick_rx: = 0 ## Время приёма крайнего сообщения var tick_tx: = 0 ## Время отправки крайнего сообщения static var json_conv: = JSON.new() signal command_done() ## Вызывается при успешном выполнении команды signal command_fail() ## Вызывается при возникновении ошибки во время выполнения команды signal line_changed() ## Вызывается при смене состояния связи с ячейкой signal data_received() ## Вызывается при приёме сообщения, до его разбора signal parse_failed(msg: String) ## Вызывается при возникновении ошибки во время разбора пакета. Передаёт сообщение об ошибке signal holding_received(addr: int, data: Array) ## Вызывается при получении сообщения c данными из регистров хранения signal input_received(addr: int, data: Array) ## Вызывается при получении сообщения c данными из регистров входов signal info_received(msg: String) ## Вызывается при получении информационного сообщения signal except_received(msg: String) ## Вызывается при получении сообщения об исключительной ситуации func _init(nm: String, u: int = 1): unit = u name = nm ## Изменяет состояние. Вызывать периодически[br] ## [param tick] - Текущщее время в мс[br] func process(tick: int): if online and ((tick - tick_rx) > ONLINE_TIMEOUT): online = false emit_signal('line_changed') if (cmd_state == CmdState.WAIT) or (cmd_state == CmdState.SEND): cmd_state = CmdState.FAIL emit_signal('command_fail') var rc = Error.ERR_BUSY if cmd_state == CmdState.SEND: rc = Error.OK tick_tx = tick cmd_state = CmdState.WAIT return rc ## Производит разбор принятых данных. Вызывать при приёме данных на сокете[br] ## [param data] - Данные принятые на сокете[br] ## [param tick] - Текущщее время в мс[br] func parse(data: PackedByteArray, tick: int) -> bool: tick_rx = tick if not online: online = true emit_signal('line_changed') var utf8_data = data.get_string_from_utf8() if Error.OK == json_conv.parse(utf8_data): emit_signal('data_received') 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: 'read_input': var addr = message_data[0] var regs = message_data.slice(1) emit_signal('input_received', addr, regs) 'read_holding': var addr = message_data[0] var regs = message_data.slice(1) emit_signal('holding_received', addr, regs) 'info': var msg = message['data'] emit_signal('info_received', msg) 'exception': var msg = message['data'] emit_signal('except_received', msg) rc = false _: var msg: String = 'код команды \"%s\" - не распознан' % message_code 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') else: var message = json_conv.get_error_message() emit_signal('parse_failed', message) return true ## Отправляет команду "записать в регистры".[br] ## [param addr] - Начальный адрес регистров.[br] ## [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} tx_data = json_conv.stringify(json_dict).to_utf8_buffer() cmd_state = CmdState.SEND return true ## Отправляет команду "читать регистры хранения".[br] ## [param addr] - Начальный адрес регистров.[br] ## [param count] - Количество регистров от начального адреса.[br] func read_holdind(addr: int, count: int = 1) -> bool: if cmd_state != CmdState.DONE: return false var json_dict = {'code': 'read_holding', 'data': [addr, count]} tx_data = json_conv.stringify(json_dict).to_utf8_buffer() cmd_state = CmdState.SEND return true ## Отправляет команду "читать регистры входов".[br] ## [param addr] - Начальный адрес регистров.[br] ## [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]} tx_data = json_conv.stringify(json_dict).to_utf8_buffer() cmd_state = CmdState.SEND return true