class_name sch_3 extends Node const ONLINE_TIMEOUT = 5000 ## Представление прибора Щ3 class Sch3 extends unit.Unit: enum Sch3Fsm { READ_INPUT, READ_HOLDING, WRITE, CONNECT, IDLE, DISCONNECT } var step_fsm: Sch3Fsm = Sch3Fsm.CONNECT var base_input_address: int = 0 var regs_input_count: int = 5 var base_holding_address: int = 0 var regs_holding_count: int = 0 var request_period: int = 300 var reconnect_period: int = ONLINE_TIMEOUT var reconnect_failed: bool = false var write_stack: Array var tick_fsm: int = 0 var mbc: = ModbusClientRtu.new() var tick_fsm_delta: = reconnect_period var port: String var params: Array func try_disconnect(): mbc.thread_stop() mbc.queue_clear() mbc.flush() mbc.close() func try_connect() -> bool: var rc: = mbc.open(port, params[0], params[1], params[2], params[3], params[4]) if rc != Error.OK: # TODO: убрать это отсюда (неуместный вывод сообщения) if not reconnect_failed: log.error('%s: невозможно открыть \"%s\" %s' % [name, port, params]) reconnect_failed = true return false if reconnect_failed: # TODO: убрать это отсюда (неуместный вывод сообщения) log.warning('%s: открыто после сбоя \"%s\" %s' % [name, port, params]) reconnect_failed = false mbc.set_response_timeout(params[5]) mbc.thread_run() return true func push_write_holding(regs: Dictionary): var keys: = regs.keys() keys.sort() var data: = [] for k in keys: data.append(regs[k]) write_stack.append([keys[0], data]) func _on_timeout(tick: int) -> void: if step_fsm == Sch3Fsm.WRITE: while write_stack.size(): var write_command = write_stack.pop_front() mbc.request_write(write_command[0], write_command[1]) cmd_state = CmdState.WAIT tx_tick = tick step_fsm = Sch3Fsm.READ_INPUT elif step_fsm == Sch3Fsm.READ_INPUT: mbc.request_read_input(base_input_address, regs_input_count) cmd_state = CmdState.WAIT tx_tick = tick step_fsm = Sch3Fsm.WRITE elif step_fsm == Sch3Fsm.CONNECT: try_disconnect() try_connect() mbc.request_read_input(base_input_address, regs_input_count) cmd_state = CmdState.WAIT tx_tick = tick elif step_fsm == Sch3Fsm.DISCONNECT: try_disconnect() step_fsm = Sch3Fsm.IDLE elif step_fsm == Sch3Fsm.IDLE: pass signal write (addr: int, data: Array) ## Вызывается при получении сообщения подтверждения записи signal read (addr: int, data: Array) ## Вызывается при получении сообщения c данными из регистров входов. signal read_input (addr: int, data: Array) ## Вызывается при получении сообщения c данными из регистров хранения. signal error(msg: String) ## Вызывается при получении сообщения об исключительной ситуации. func _init(unit_name: String): name = unit_name mbc.connect('read', on_read) mbc.connect('read_input', on_read_input) mbc.connect('write', on_write) ProjectSettings.connect('settings_changed', _on_settings_changed) func _on_settings_changed(): var external_ctl = ProjectSettings.get_setting('application/config/Внешнее управление', false) if external_ctl and (step_fsm not in [Sch3Fsm.DISCONNECT, Sch3Fsm.IDLE]): step_fsm = Sch3Fsm.DISCONNECT elif (not external_ctl) and (step_fsm in [Sch3Fsm.DISCONNECT, Sch3Fsm.IDLE]): var unit_sch3 = network.get_unit_instance('уарэп-щ3') var unit_prof = ProjectSettings.get_setting('application/config/%s' % unit_sch3.name) if unit_prof: unit_sch3.port = unit_prof[0] unit_sch3.params = unit_prof.slice(1, -1) step_fsm = Sch3Fsm.CONNECT func parse(return_code: Error, base_addr: int, data: Array, code: String): if return_code == Error.OK: rx_tick = Time.get_ticks_msec() if cmd_state == CmdState.WAIT: cmd_state = CmdState.DONE emit_signal('command_done', self) emit_signal(code, base_addr, data) if not online: online = true emit_signal('line_changed', self) step_fsm = Sch3Fsm.READ_INPUT tick_fsm_delta = request_period else: if cmd_state == CmdState.WAIT: cmd_state = CmdState.FAIL if online: emit_signal('command_fail', self) emit_signal('error', [code, return_code, base_addr, data]) ## Изменяет состояние. Вызывается периодически.[br] ## [param tick] - Текущее время в мс.[br] ## Возвращает [b]Error.OK[/b] - если данные готовы для отправки.[br] ## Возвращает [b]Error.ERR_BUSY[/b] - если данных для отправки нет.[br] func process(tick: int) -> Error: if online: if (tick - rx_tick) > ONLINE_TIMEOUT: online = false if cmd_state == CmdState.WAIT: cmd_state = CmdState.FAIL emit_signal('command_fail', self) emit_signal('line_changed', self) if step_fsm != Sch3Fsm.IDLE: step_fsm = Sch3Fsm.CONNECT tick_fsm_delta = reconnect_period if (tick - tick_fsm) > tick_fsm_delta: tick_fsm = tick _on_timeout(tick) return Error.ERR_BUSY ## Принимает результат команды "читать регистры хранения".[br] ## [param return_code] - Код выполнения команды.[br] ## [param base_addr] - Начальный адрес регистров.[br] ## [param data] - Данные прочитанные из регистров.[br] func on_read(return_code: Error, base_addr: int, data: Array): parse(return_code, base_addr, data, 'read') ## Принимает результат команды "читать регистры входов".[br] ## [param return_code] - Код выполнения команды.[br] ## [param base_addr] - Начальный адрес регистров.[br] ## [param data] - Данные прочитанные из регистров.[br] func on_read_input(return_code: Error, base_addr: int, data: Array): parse(return_code, base_addr, data, 'read_input') ## Принимает результат команды "записать в регистры".[br] ## [param return_code] - Код выполнения команды.[br] ## [param base_addr] - Начальный адрес регистров.[br] ## [param data] - Данные прочитанные из регистров.[br] func on_write(return_code: Error, base_addr: int, data: Array): parse(return_code, base_addr, data, 'write')