177 lines
7.5 KiB
GDScript
177 lines
7.5 KiB
GDScript
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')
|