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 cmd_num_tx = (cmd_num_rx + 1) % 0xffff tx_data.encode_u16(2, cmd_num_tx) else: cmd_state = CmdState.FAIL emit_signal('command_fail', self) rc = Error.FAILED return rc func send_flash(cmd_code: CmdCode, 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 in [CmdCode.ERASE_FLASH, CmdCode.WRITE_FLASH, CmdCode.READ_FLASH, CmdCode.LOAD_FLASH, CmdCode.READ_FLASH_MP550, 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: CmdCode, 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: CmdCode, 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: CmdCode) -> bool: 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 return true ## Возвращает код команды ЯУ-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] # Код команды cmd_code_rx = cmd_code if (cmd_state == CmdState.WAIT) and (cmd_num_rx == cmd_num_tx) and (cmd_code == cmd_code_tx): 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: cmd_state = CmdState.DONE emit_signal('command_done', self) else: emit_signal('parse_failed', self) return true