327 lines
16 KiB
GDScript
327 lines
16 KiB
GDScript
class_name yau_07 extends Node
|
||
|
||
## Протокол ячейки ЯУ-07.
|
||
|
||
const ONLINE_TIMEOUT: = 5000 ## Время ожидания пакета от ячейки, милисекунды
|
||
const RESEND_TIMEOUT: = 300 ## Задержка для повторной отправки команды, милисекунды
|
||
const RESEND_TIMEOUT_FLASH: = 1500 ## Задержка для повторной отправки команды флеш, милисекунды
|
||
const RETRY_COUNT: = 5 ## Количество повторных отправок
|
||
const BUFFER_SIZE: = 2048 ## Размер буфера для приёма датаграмм
|
||
|
||
|
||
## Ячейка ЯУ-07.
|
||
class YaU07 extends unit.Unit:
|
||
|
||
## Коды выполняемых команд.
|
||
enum CmdCode {
|
||
READ_ISA = 0, ## Код команды "читать порт ISA" ячейки ЯУ-07
|
||
WRITE_ISA = 1, ## Код команды "записать порт ISA" ячейки ЯУ-07
|
||
ERASE_FLASH = 49, ## Код команды "стереть данные из FLASH" ячейки ЯУ-07
|
||
WRITE_FLASH = 50, ## Код команды "записать данные в FLASH" ячейки ЯУ-07
|
||
READ_FLASH = 51, ## Код команды "прочитать данные из FLASH" ячейки ЯУ-07
|
||
LOAD_FLASH = 52, ## Код команды "записать данные из FLASH в КЭМС" ячейки ЯУ-07
|
||
READ_FLASH_MP550 = 56, ## Код команды "прочитать данные из FLASH" ячейки ЯУ-07
|
||
WRITE_FLASH_MP550 = 55, ## Код команды "записать данные из FLASH в КЭМС" ячейки ЯУ-07
|
||
WRITE_BLOCK = 60, ## Код команды "записать блок данных ISA" внешней шины ячейки ЯУ-07
|
||
READ_BLOCK = 61, ## Код команды "читать блок данных ISA" внешней шины ячейки ЯУ-07
|
||
LOAD_EMS_DONE = 70, ## Код команды "читать блок данных ISA" внешней шины ячейки ЯУ-07
|
||
}
|
||
|
||
var cmd_num_rx: = 0 ## Номер команды принятый
|
||
var cmd_num_tx: = 0 ## Номер команды переданный
|
||
var cmd_result: = 1 ## Результат выполнения команды. Устанавливается в ЯУ-07
|
||
var cmd_data: = PackedByteArray() ## Данные команды. Устанавливается в ЯУ-07
|
||
var cmd_retry: = RETRY_COUNT ## Счётчик оставшихся попыток выполнить команду
|
||
var cmd_code_rx: = 0 ## Код последней выполненой команды
|
||
var cmd_code_tx: = 0 ## Код последней отправленной команды
|
||
var cmd_tick_tx: = 0 ## Время последней отправки команды
|
||
var cmd_tick_rx: = 0 ## Время последнего приёма команды
|
||
var status: = PackedByteArray() ## Поле "состояние прибора"
|
||
var isa_ports: = {} ## Результат выполнения команды "читать порт"
|
||
var isa_block: = {} ## Результат выполнения команды "читать массив из порта"
|
||
var flash_data: = PackedByteArray() ## Данные прочитанные из Flash
|
||
var flash_len: = 0 ## Длинна данных прочитанных из Flash
|
||
|
||
|
||
func _to_string() -> String: return String('яу07: %s %s' % [self.name, ['отключен', 'на связи'][int(online)]])
|
||
|
||
|
||
func _init(unit_name: StringName):
|
||
self.name = unit_name
|
||
tx_data.resize(BUFFER_SIZE)
|
||
tx_data.fill(0)
|
||
|
||
|
||
func process(tick: int):
|
||
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:
|
||
var res_timeout = RESEND_TIMEOUT
|
||
if cmd_code_tx == CmdCode.WRITE_FLASH_MP550:
|
||
res_timeout = RESEND_TIMEOUT_FLASH
|
||
if (tick - cmd_tick_tx) > res_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_flash(cmd_code: int, fl_data: Array, num_byte_rd: int, id_sector: int = 0) -> bool:
|
||
if (cmd_state == CmdState.WAIT) \
|
||
or (cmd_state == CmdState.SEND) \
|
||
or (cmd_state == CmdState.UNCK):
|
||
return false
|
||
if not ((cmd_code == CmdCode.ERASE_FLASH) or (cmd_code == CmdCode.WRITE_FLASH) \
|
||
or (cmd_code == CmdCode.READ_FLASH) or (cmd_code == CmdCode.LOAD_FLASH)
|
||
or (cmd_code == CmdCode.READ_FLASH_MP550) or (cmd_code == CmdCode.WRITE_FLASH_MP550)):
|
||
return false
|
||
cmd_num_tx = (cmd_num_rx + 1) % 0xffff
|
||
tx_data.encode_u16(0, cmd_retry)
|
||
tx_data.encode_u16(2, cmd_num_tx)
|
||
tx_data.encode_u8(6, cmd_code)
|
||
if cmd_code == CmdCode.ERASE_FLASH:
|
||
tx_len = 7
|
||
elif cmd_code == CmdCode.WRITE_FLASH:
|
||
tx_data.encode_u8(7, 1)
|
||
tx_data.encode_u16(8, 0)
|
||
tx_data.encode_u16(10, fl_data.size())
|
||
tx_len = 11 + fl_data.size() * 2
|
||
for i in fl_data.size():
|
||
tx_data.encode_u16(12 + i * 2, fl_data[i])
|
||
elif cmd_code == CmdCode.WRITE_FLASH_MP550:
|
||
tx_data.encode_u8(7, id_sector)
|
||
tx_data.encode_u16(8, 0)
|
||
tx_data.encode_u16(10, fl_data.size() * 2)
|
||
tx_len = 12 + fl_data.size() * 2
|
||
for i in fl_data.size():
|
||
tx_data.encode_u16(12 + i * 2, fl_data[i])
|
||
elif cmd_code == CmdCode.READ_FLASH:
|
||
tx_len = 11
|
||
tx_data.encode_u16(7, 0)
|
||
tx_data.encode_u16(9, num_byte_rd)
|
||
elif cmd_code == CmdCode.READ_FLASH_MP550:
|
||
tx_len = 12
|
||
tx_data.encode_u8(7, id_sector)
|
||
tx_data.encode_u16(8, 0)
|
||
tx_data.encode_u16(10, num_byte_rd)
|
||
cmd_state = CmdState.SEND
|
||
cmd_code_tx = cmd_code
|
||
return true
|
||
|
||
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
|
||
cmd_code_tx = cmd_code
|
||
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
|
||
cmd_code_tx = cmd_code
|
||
return true
|
||
|
||
func send_load_ems_done(cmd_code: int):
|
||
if (cmd_state == CmdState.WAIT) \
|
||
or (cmd_state == CmdState.SEND) \
|
||
or (cmd_state == CmdState.UNCK):
|
||
return false
|
||
if cmd_code != CmdCode.LOAD_EMS_DONE:
|
||
return false
|
||
cmd_num_tx = (cmd_num_rx + 1) % 0xffff
|
||
tx_data.encode_u16(0, cmd_retry)
|
||
tx_data.encode_u16(2, cmd_num_tx)
|
||
tx_data.encode_u8(6, cmd_code)
|
||
tx_len = 7
|
||
cmd_state = CmdState.SEND
|
||
cmd_code_tx = cmd_code
|
||
|
||
## Возвращает код команды ЯУ-07.
|
||
## rx_data - Принятые данные.
|
||
## Возвращает - Код команды не найден, 0...255 - код команды.
|
||
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: int) -> 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
|
||
if rx_data.size() < (data_pos + shift):
|
||
return false
|
||
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: int) -> bool:
|
||
var ports_count_pos: = status_len + 7 # Положение в приёмном буфере количества портов
|
||
if rx_data.size() < ports_count_pos:
|
||
return false
|
||
if rx_data.size() < (ports_count_pos + 5):
|
||
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: = []
|
||
if rx_data.size() < (ports_count_pos + 7 + (block_len - 1) * 2):
|
||
return false
|
||
for i: int 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
|
||
|
||
func parse_flash(rx_data: PackedByteArray, status_len: int, cmd_code: CmdCode) -> bool:
|
||
if cmd_code == CmdCode.READ_FLASH:
|
||
if rx_data.size() < (status_len + 14):
|
||
return false
|
||
flash_len = rx_data.decode_u16(status_len + 12)
|
||
flash_data = rx_data.slice(status_len + 14)
|
||
if cmd_code == CmdCode.READ_FLASH_MP550:
|
||
if rx_data.size() < (status_len + 15):
|
||
return false
|
||
flash_len = rx_data.decode_u16(status_len + 13)
|
||
flash_data = rx_data.slice(status_len + 15)
|
||
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: int = rx_data.size()
|
||
if rx_data_len < 10:
|
||
return false
|
||
|
||
cmd_num_rx = rx_data.decode_u16(2)
|
||
if cmd_state == CmdState.UNCK:
|
||
cmd_state = CmdState.DONE
|
||
|
||
var status_len: int = 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
|
||
emit_signal('data_received', self)
|
||
|
||
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: int = rx_data[cmd_code_pos] # Код команды
|
||
|
||
if (cmd_state == CmdState.WAIT) and (cmd_num_rx == cmd_num_tx) and (cmd_code == cmd_code_tx):
|
||
cmd_code_rx = cmd_code
|
||
cmd_state = CmdState.DONE
|
||
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)
|
||
elif (cmd_code == CmdCode.ERASE_FLASH) or (cmd_code == CmdCode.WRITE_FLASH) \
|
||
or (cmd_code == CmdCode.READ_FLASH_MP550) or (cmd_code == CmdCode.WRITE_FLASH_MP550) \
|
||
or (cmd_code == CmdCode.READ_FLASH) or (cmd_code == CmdCode.LOAD_FLASH):
|
||
rc = parse_flash(rx_data, status_len, cmd_code)
|
||
|
||
if rc:
|
||
emit_signal('command_done', self)
|
||
else:
|
||
emit_signal('parse_failed', self)
|
||
|
||
return true
|