From 86ce72a0e3ba619d647194825afae8b4a2623847 Mon Sep 17 00:00:00 2001 From: sasha80 Date: Fri, 7 Jul 2023 15:53:40 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=20=D0=B2?= =?UTF-8?q?=20=D0=BF=D1=80=D0=BE=D1=86=D0=B5=D1=81=D1=81=D0=B5.=20=D0=9E?= =?UTF-8?q?=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA=D0=B0=20=D0=BE=D0=B1?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D0=B0=20=D0=BF=D0=BE=20=D1=81=D0=B5=D1=82?= =?UTF-8?q?=D0=B8=20=D1=81=20=D0=AF=D0=A3-07?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scenes/repnetwork.gd | 36 ++++++++ scenes/yau07.gd | 199 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 235 insertions(+) create mode 100644 scenes/repnetwork.gd create mode 100644 scenes/yau07.gd diff --git a/scenes/repnetwork.gd b/scenes/repnetwork.gd new file mode 100644 index 00000000..ab1ecb7e --- /dev/null +++ b/scenes/repnetwork.gd @@ -0,0 +1,36 @@ +extends Node + + +var sock: PacketPeerUDP +var units: Dictionary + + +func _ready() -> void: + var addr = REPSettings.addr['уареп-пульт'][1] + var port = REPSettings.addr['уареп-пульт'][2] + sock = PacketPeerUDP.new() + sock.bind(port, addr) + for key in REPSettings.addr: + var prof = REPSettings.addr[key] + if prof[0] == 'yau07': + addr = REPSettings.addr[key][1] + port = REPSettings.addr[key][2] + var unit = yau07.YaU07.new([addr, port]) + var addr_port: String = '%s:%d' % [addr, port] + units[addr_port] = unit + Log.info('ЯУ-07: %s' % unit) + + +func _process(delta: float) -> void: + for unit in units.values() as Array[yau07.YaU07]: + unit.process(delta, sock) + + while sock.get_available_packet_count() > 0: + var data = sock.get_packet() + var addr = sock.get_packet_ip() + var port = sock.get_packet_port() + var addr_port: String = '%s:%d' % [addr, port] + if addr_port in units: + var unit: yau07.YaU07 = units[addr_port] + unit.parse(data) + return diff --git a/scenes/yau07.gd b/scenes/yau07.gd new file mode 100644 index 00000000..4ca571bc --- /dev/null +++ b/scenes/yau07.gd @@ -0,0 +1,199 @@ +extends Node + +const ONLINE_TIMEOUT = 5.0 # Время ожидания пакета от ячейки, секунды +const RESEND_TIMEOUT = 0.3 # Задержка для повторной отправки команды, секунды +const RETRY_COUNT = 5 # Количество повторных отправок +const DEFAULT_ADDR = ['localhost', 50000] +const BUFFER_SIZE = 2048 + +# Ячейка ЯУ-07 +class YaU07: + + enum CmdCode { + READ_ISA = 0, # Код команды "читать порт ISA" ячейки ЯУ-07 + WRITE_ISA = 1 } # Код команды "записать порт ISA" ячейки ЯУ-07 + + enum CmdState { + UNCK, # Номер команды не ининцирован + SEND, # Буфер готов для отправки + WAIT, # Ожидание выполнения + DONE, # Команда выполнена + FAIL } # Команда не выполнена + + signal online_changed(yau07: YaU07) + signal command_done(yau07: YaU07) + signal command_fail(yau07: YaU07) + + var src_addr: = DEFAULT_ADDR # Адрес источника + var cmd_num_rx: = 0 # Номер команды принятый + var cmd_num_tx: = 0 # Номер команды переданный + var cmd_state = CmdState.UNCK # Состояние выполнения команды + var cmd_result: = 1 # Результат выполнения команды. Устанавливается в ЯУ-07 + var cmd_data: = PackedByteArray() # Данные команды. Устанавливается в ЯУ-07 + var cmd_code = null # Код команды + var cmd_retry = 0 + var tick = 0.0 + var cmd_tick_tx = 0.0 + var cmd_tick_rx = 0.0 + var status: = PackedByteArray() # Поле "состояние прибора" + var tx_data: = [] # Буфер отправленный + var tx_len = 0 + var isa_ports = {} # Результат выполнения команды "читать порт" + var online = false + + func _to_string() -> String: + return '%s %s:%d cmd_num:%d' % [['отключен', 'на связи'][int(online)], src_addr[0], src_addr[1], cmd_num_rx] + + + func _init(addr: Array): + src_addr = addr + tx_data.resize(BUFFER_SIZE) + tx_data.fill(0) + + + func process(delta: float, sock: PacketPeerUDP): + tick += delta + if cmd_state == CmdState.SEND: + sock.set_dest_address(src_addr[0], src_addr[1]) + if sock.put_packet(tx_data.slice(0, tx_len)) == 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: + sock.set_dest_address(src_addr[0], src_addr[1]) + if sock.put_packet(tx_data.slice(0, tx_len)) == OK: + cmd_tick_tx = tick + else: + print_debug('ошибка: пакет не отправлен на: ', src_addr) + cmd_retry -= 1 + else: + cmd_state = CmdState.FAIL + emit_signal('command_fail', self) + if online and ((tick - cmd_tick_rx) > ONLINE_TIMEOUT): + online = false + emit_signal('online_changed', self) + + + func send_isa(cmd_code: int, ports_data: Array) -> bool: + if (cmd_state == CmdState.WAIT) or (cmd_state == CmdState.SEND): + return false + if not ((cmd_code == CmdCode.READ_ISA) or (cmd_code == CmdCode.WRITE_ISA)): + return false + if cmd_state == CmdState.UNCK: + return false + + cmd_num_tx = (cmd_num_rx + 1) % 0xffff + + var ports_count = ports_data.size() + tx_len = ports_count * 2 + 8 + Tools.set_wordi(tx_data, 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: + Tools.set_wordi(tx_data, 8 + i, word) + i += 2 + cmd_state = CmdState.SEND + return true + + + func get_cmd_code(rx_data): + """ Возвращает код команды ЯУ-07 + :param rx_data: Принятые данные + :return: None - Код команды не найден, 0...255 - код команды """ + + var rx_data_len = rx_data.size() + if rx_data_len < 7: + return null + var status_len = Tools.ibytes_to_word(rx_data, 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, 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 = Tools.ibytes_to_word(rx_data, addr_pos + shift) # Адрес порта ISA яу-07 + var isa_data = Tools.ibytes_to_word(rx_data, data_pos + shift) # Данные порта ISA яу-07 + isa_ports[isa_addr] = isa_data + i += 1 + cmd_data = rx_data.subarray(addr_pos, (addr_pos + ports_count * 4) - 1) + return i > 0 + + + func parse(rx_data: PackedByteArray) -> bool: + """ + Заполняет состояние прибора данными прочитанными из портов ЯУ-07 + для команд "читать порты" или "записать порты" + :param rx_data: Принятые данные + :return: True - Данные обработаны успешно, + False - Данные не обработаны из за ошибки """ + + if not online: + online = true + emit_signal('online_changed', self) + + var rx_data_len = rx_data.size() + if rx_data_len < 11: + return false + + cmd_num_rx = Tools.ibytes_to_word(rx_data, 2) + if cmd_state == CmdState.UNCK: + cmd_state = CmdState.DONE + + var status_len = Tools.ibytes_to_word(rx_data, 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): + cmd_code = null + return true # Нет поля "ответ на команду", разбор пакета завершить + + var cmd_code_pos = status_len + 8 # Положение в приёмном буфере "код команды" + if rx_data_len <= cmd_code_pos: + return false + cmd_code = rx_data[cmd_code_pos] # "код команды" + + 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 + + if (cmd_code == CmdCode.READ_ISA) or (cmd_code == CmdCode.WRITE_ISA): + parse_isa(rx_data, status_len) + + if (cmd_state == CmdState.WAIT) and (cmd_num_rx == cmd_num_tx): + cmd_state = CmdState.DONE + emit_signal('command_done', self) + + return true