Files
uarep-ctl/scenes/scripts/yau07.gd

250 lines
12 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

extends Node
## Протокол ячейки ЯУ-07
const BUFFER_SIZE = 2048 ## Размер буфера для приёма датаграмм
const ONLINE_TIMEOUT = 5.0 ## Время ожидания пакета от ячейки, секунды
const RESEND_TIMEOUT = 0.3 ## Задержка для повторной отправки команды, секунды
const RETRY_COUNT = 5 ## Количество повторных отправок
## Ячейка ЯУ-07
class YaU07:
## Коды выполняемых команд
enum CmdCode {
READ_BLOCK = 61, ## Код команды "читать блок данных ISA" внешней шины ячейки ЯУ-07
READ_ISA = 0, ## Код команды "читать порт ISA" ячейки ЯУ-07
WRITE_BLOCK = 60, ## Код команды "записать блок данных ISA" внешней шины ячейки ЯУ-07
WRITE_ISA = 1, ## Код команды "записать порт ISA" ячейки ЯУ-07
}
## Состояние автомата выполнения команды
enum CmdState {
DONE, ## Команда выполнена
FAIL, ## Команда не выполнена
SEND, ## Буфер готов для отправки
UNCK, ## Номер команды не ининциирован
WAIT ## Ожидание выполнения
}
signal command_done(unit: YaU07) ## Вызывается после выполнения команды
signal command_fail(unit: YaU07) ## Вызывается если возникла ошибка при выполнении команды и команда не выполнена
signal line_changed(unit: YaU07) ## Вызывается когда меняется состояние связи с ячейкой
signal parse_failed(unit: YaU07) ## Вызывается если возникла ошибка при разборе пакета
var cmd_data: = PackedByteArray() ## Данные команды. Устанавливается в ЯУ-07
var cmd_num_rx: = 0 ## Номер команды принятый
var cmd_num_tx: = 0 ## Номер команды переданный
var cmd_result: = 1 ## Результат выполнения команды. Устанавливается в ЯУ-07
var cmd_retry: = 0 ## Счётчик оставшихся попыток выполнить команду
var cmd_state: = CmdState.UNCK ## Состояние выполнения команды
var cmd_tick_rx: = 0 ## Время последнего приёма команды
var cmd_tick_tx: = 0 ## Время последней отправки команды
var isa_block: = {} ## Результат выполнения команды "читать массив из порта"
var isa_ports: = {} ## Результат выполнения команды "читать порт"
var name: = String() ## Имя
var online: = false ## Состояние связи с ячейкой
var status: = PackedByteArray() ## Поле "состояние прибора"
var tx_data: = PackedByteArray() ## Буфер отправленный
var tx_len: = 0 ## Длина последнего отправленного пакета
func _to_string() -> String: return String('яу07: %s %s' % [self.name, ['отключен', 'на связи'][int(online)]])
func _init(nm):
self.name = nm
tx_data.resize(BUFFER_SIZE)
tx_data.fill(0)
func process(tick: float):
if online and ((tick - cmd_tick_rx) > ONLINE_TIMEOUT):
online = false
emit_signal('line_changed', self)
var rc = Error.ERR_BUSY
if cmd_state == CmdState.SEND:
rc = Error.OK
cmd_tick_tx = tick
cmd_state = CmdState.WAIT
cmd_retry = RETRY_COUNT
elif cmd_state == CmdState.WAIT:
if cmd_retry:
if (tick - cmd_tick_tx) > RESEND_TIMEOUT:
rc = Error.OK
cmd_tick_tx = tick
cmd_retry -= 1
else:
cmd_state = CmdState.FAIL
emit_signal('command_fail', self)
rc = Error.FAILED
return rc
func send_isa(cmd_code: int, ports_data: Array) -> bool:
if (cmd_state == CmdState.WAIT) \
or (cmd_state == CmdState.SEND) \
or (cmd_state == CmdState.UNCK):
return false
if not ((cmd_code == CmdCode.READ_ISA) or (cmd_code == CmdCode.WRITE_ISA)):
return false
cmd_num_tx = (cmd_num_rx + 1) % 0xffff
var ports_count = ports_data.size()
tx_len = ports_count * 2 + 8
tx_data.encode_u16(0, cmd_retry) # Исправление для сетевого стека "nm-callback" процессоров STM
tx_data.encode_u16(2, cmd_num_tx)
if cmd_code == CmdCode.WRITE_ISA:
ports_count /= 2
tx_data[6] = cmd_code
tx_data[7] = ports_count
var i: = 0
for word in ports_data:
tx_data.encode_u16(8 + i, word)
i += 2
cmd_state = CmdState.SEND
return true
func send_block(cmd_code: int, block_len: int, port: Array) -> bool:
if (cmd_state == CmdState.WAIT) \
or (cmd_state == CmdState.SEND) \
or (cmd_state == CmdState.UNCK):
return false
if not ((cmd_code == CmdCode.READ_BLOCK) or (cmd_code == CmdCode.WRITE_BLOCK)):
return false
cmd_num_tx = (cmd_num_rx + 1) % 0xffff
tx_len = 11
tx_data.encode_u16(0, cmd_retry) # Исправление для сетевого стека "nm-callback" процессоров STM
tx_data.encode_u16(2, cmd_num_tx)
if cmd_code == CmdCode.WRITE_BLOCK:
block_len /= 2
tx_data[6] = cmd_code
tx_data.encode_u16(7, block_len)
tx_data.encode_u16(9, int(port[0]))
cmd_state = CmdState.SEND
return true
## Возвращает код команды ЯУ-07.
## [param rx_data] - Принятые данные.[br]
## Возвращает [b]null[/b] - Если код команды не найден.[br]
## Возвращает [b]0...255[/b] - Код команды.[br]
func get_cmd_code(rx_data: PackedByteArray):
var rx_data_len = rx_data.size()
if rx_data_len < 7:
return null
var status_len = rx_data.decode_u16(6) # Длина массива байтового "состояние прибора"
if rx_data_len <= (status_len + 6): # Нет поля "ответ на команду", разбор пакета завершить
return null
var cmd_code_pos = status_len + 8 # Положение в приёмном буфере "кода команды"
if rx_data_len <= cmd_code_pos:
return null
return rx_data[cmd_code_pos] # "код команды"
func parse_isa(rx_data: PackedByteArray, status_len) -> bool:
var ports_count_pos = status_len + 10 # Положение в приёмном буфере количества портов
if rx_data.size() < ports_count_pos:
return false
var ports_count = rx_data[ports_count_pos] # Количество портов
var counted_len = status_len + (ports_count * 4) + 11 # Вычисленная длина пакета
if rx_data.size() < counted_len:
return false
var addr_pos = status_len + 11 # Смещение положения адреса в приёмном буфере
var data_pos = status_len + 13 # Смещение положения данных в приёмном буфере
var i: = 0
while i < ports_count:
var shift: = i * 4
var isa_addr = rx_data.decode_u16(addr_pos + shift) # Адрес порта ISA яу-07
var isa_data = rx_data.decode_u16(data_pos + shift) # Данные порта ISA яу-07
isa_ports[isa_addr] = isa_data
i += 1
cmd_data = rx_data.slice(addr_pos, (addr_pos + ports_count * 4) - 1)
return i > 0
func parse_block(rx_data: PackedByteArray, status_len) -> bool:
var ports_count_pos = status_len + 7 # Положение в приёмном буфере количества портов
if rx_data.size() < ports_count_pos:
return false
var block_len = rx_data.decode_u16(ports_count_pos + 3)
var block_adr = rx_data.decode_u16(ports_count_pos + 5)
var block_data = []
for i in block_len:
var shift: int = i * 2
block_data.append(rx_data.decode_u16(ports_count_pos + 7 + shift))
isa_block[block_adr] = block_data
return true
## Заполняет состояние прибора данными прочитанными из портов ЯУ-07
## для команд "читать порты" или "записать порты"[br]
## [param rx_data] - Принятые данные.[br]
## Возвращает [b]true[/b] - Данные обработаны успешно.[br]
## Возвращает [b]false[/b] - Данные не обработаны из за ошибки.[br]
func parse(rx_data: PackedByteArray, tick: int) -> bool:
cmd_tick_rx = tick
if not online:
online = true
emit_signal('line_changed', self)
var rx_data_len = rx_data.size()
if rx_data_len < 11:
return false
cmd_num_rx = rx_data.decode_u16(2)
if cmd_state == CmdState.UNCK:
cmd_state = CmdState.DONE
var status_len = rx_data.decode_u16(6) # Длина массива байтового "состояние прибора"
if rx_data_len < (status_len + 6):
return false
var i: = 0
var len_rx_data: = rx_data.size()
status.resize(status_len)
while i < status_len:
var byte_i = i + 8
if byte_i < len_rx_data:
status[i] = rx_data[byte_i] # Массив байтовый "состояние прибора"
i += 1
if rx_data_len == (status_len + 6):
return true # Нет поля "ответ на команду", разбор пакета завершить
var cmd_code_pos = status_len + 8 # Положение в приёмном буфере "код команды"
if rx_data_len <= cmd_code_pos:
return false
var cmd_status_pos = status_len + 9 # Положение в приёмном буфере "состояния выполнения команды"
if rx_data_len <= cmd_status_pos:
return false
cmd_result = rx_data[cmd_status_pos]
if cmd_result != 1: # Команда не выполнена
return false
var cmd_code: CmdCode = rx_data[cmd_code_pos] as CmdCode # "код команды"
var rc = true
if (cmd_code == CmdCode.READ_ISA) or (cmd_code == CmdCode.WRITE_ISA):
rc = parse_isa(rx_data, status_len)
elif (cmd_code == CmdCode.READ_BLOCK) or (cmd_code == CmdCode.WRITE_BLOCK):
rc = parse_block(rx_data, status_len)
if rc:
if (cmd_state == CmdState.WAIT) and (cmd_num_rx == cmd_num_tx):
cmd_state = CmdState.DONE
emit_signal('command_done', self)
else:
emit_signal('parse_failed', self)
return true