@tool class_name Network extends Node const YAU07_PROTO: = {'yau07tx': yau07.YaU07} const IYEMS_PROTO: = {'iyems-tx': yems_info.YeMSInfo} const UDP_PROTO: = {'capsrpb': capsrpb.CapsRpb, 'trassa': trassa.Trassa} const TCP_PROTO: = {'5p28': tcp5p28.TCP5P28} const SERIAL_PROTO: = {'spt25': spt25.SPT25} const MODBUS_PROTO: = {'mbcs': sch3.Sch3} const NAVI_PROTO: = {'navi': navi.NaviSource} const NULL_PROTO: = {null: unit.Unit} var tick: int = 0 class SocketSerial extends SerialPort: const P_DIV: = 0x8408 const FLAG: = 0x7e const FLAG_MASK: = 0x20 const FSC_MASK: = 0x8000 const parity_code = { 'N': SerialPort.PARITY_NONE, 'E': SerialPort.PARITY_EVEN, 'O': SerialPort.PARITY_ODD } static var tick: int var unit_instance: unit.Unit var failed: = {} func _init(): connect('got_error', on_serial_got_error) func on_serial_got_error(where: String, what: String): if where not in failed: push_error([where, what, unit_instance]) failed[where] = what func on_serial_data(data: PackedByteArray): failed.clear() unit_instance.parse(data, tick) func format_buffer(data: PackedByteArray): var buffer: PackedByteArray buffer.resize(64) var i: = 1 for v in data: if v != 0x7e: buffer[i] = v; i += 1 else: buffer[i] = FLAG; i += 1 buffer[i] = FLAG ^ FLAG_MASK; i += 1 var v_sum: = get_check_sum(data) buffer.encode_u16(i, v_sum); i += 2 buffer[i] = FLAG; i += 1 buffer.resize(i) return buffer func send_to(_data: PackedByteArray): if not is_open(): call_deferred('try_open') return # var buffer = format_buffer(data) # Не проверено, но должно работать # Эта последовательность для запроса состояния, получена от разработчиков СПТ-25 const tx_tula: = [0x7e, 0x01, 0x04, 0xbb, 0x50, 0x7e] var sz = write_raw(tx_tula) if sz != tx_tula.size(): call_deferred('try_close') func _to_string() -> String: return 'последовательный порт \"%s\" @ %d (%s)' % [self.port, self.baudrate, 'открыт' if is_open() else 'закрыт'] func get_check_sum(src: PackedByteArray) -> int: var fcs: = (~((src[1] << 8) + src[0])) & 0xffff for i: int in range(2, src.size()): var chr: = src[i] for j: int in 8: if chr & 1: if fcs & 1: fcs = ((fcs >> 1) | FSC_MASK) ^ P_DIV else: fcs = ((fcs >> 1) | FSC_MASK) else: fcs = (fcs >> 1) ^ P_DIV if fcs & 1 else fcs >> 1 chr >>= 1 for j: int in 16: fcs = (fcs >> 1) ^ P_DIV if fcs & 1 else fcs >> 1 return (~fcs) & 0xffff func try_open() -> Error: if is_open(): return Error.OK var rc: = open(port) if rc == Error.OK: data_received.connect(on_serial_data) start_monitoring(timeout) return rc func try_close(): if not is_open(): return Error.FAILED stop_monitoring() close() var connections: = data_received.get_connections() for conn in connections: data_received.disconnect(conn.callable) return Error.OK class SocketUDP extends PacketPeerUDP: func send_to(addr: Array, data: PackedByteArray): set_dest_address(addr[0], addr[1]) var rc: = put_packet(data) if rc != Error.OK: push_error('%s: %s', [error_string(rc), addr]) var poll_sockets: Array[SocketUDP] ## Сокеты для непрерывного опроса наличия новых данных var tcp_sockets: Array[Array] ## Сокеты для TCP-соединений var units: Dictionary[StringName, unit.Unit] ## Экземпляры всех сетевых устройств var units_udptx: Dictionary[StringName, unit.Unit] ## Экземпляры устройств с udp-соединением которым нужно передавать данные var units_udprx: Dictionary[StringName, unit.Unit] ## Экземпляры устройств с udp-соединением от которых приходят данные var units_tcp: Dictionary[StringName, unit.Unit] ## Экземпляры устройств с tcp-соединением var units_serial: Dictionary[StringName, unit.Unit] ## Экземпляры устройств с uart-соединением var units_modbus: Dictionary[StringName, unit.Unit] ## Экземпляры устройств с modbusserial-соединением var units_nullp: Dictionary[StringName, unit.Unit] ## Экземпляры устройств без протокола var serials: Dictionary[StringName, SocketSerial] ## Последовательные порты (uart) var dst_ports: Dictionary[StringName, int] ## Преобразование имя устройства в порт назначения var dst_addrs: Dictionary[StringName, StringName] ## Преобразование имя устройства в адрес назначения var port_to_unit_name: Dictionary[int, StringName] ## Преобразование порта назначения в имя устройства var logger_page: Node ## Ссылка на журнал работы var send_sockets: Dictionary[StringName, SocketUDP] ## Таблица <имяустройста>: <сокет> для отправки сообщений var addr_port_to_unit_name: Dictionary[StringName, StringName] ## Преобразование адрес:порт в имя устройства func on_settings_changed(serial: SocketSerial): serial.try_close() var rc = serial.try_open() if rc != Error.OK: log.message(log.ERROR, 'невозможно открыть %s для \"%s\"' % [serial.port, serial.unit_instance.name]) else: log.message(log.INFO, 'открыт %s для \"%s\"' % [serial.port, serial.unit_instance.name]) ## [param unit_name] - Уникальное имя устройства[br] func create_socket_udp(unit_name: StringName) -> SocketUDP: var st = settings.UnitProfiles[unit_name][1] var addr = st[0] var port = st[1] var broad = st[2] var bind = st[3] var sock: = SocketUDP.new() sock.set_broadcast_enabled(broad) var rc: = sock.bind(port, addr) if rc == Error.OK: log.info('\"%s\" привязан к %s:%d' % [unit_name, addr, port]) else: var errmsg: = error_string(rc) log.error('привязка \"%s\" к %s:%d не удалась: \"%s\"' % [unit_name, addr, port, errmsg]) return sock func create_socket_caps(unit_name: StringName) -> SocketUDP: var st = settings.UnitProfiles[unit_name][1] var addr = st[1] var port = st[2] var sock: = SocketUDP.new() var rc: = sock.bind(port, addr) if rc == Error.OK: log.info('\"%s\" привязан к %s:%d' % [unit_name, addr, port]) else: var errmsg = error_string(rc) log.error('привязка \"%s\" к %s:%d не удалась: \"%s\"' % [unit_name, addr, port, errmsg]) return sock ## [param unit_name] - Уникальное имя устройства[br] func create_serial(unit_instance: unit.Unit) -> SocketSerial: var st = settings.UnitProfiles[unit_instance.name][1] var serial: = SocketSerial.new() serial.port = st[0] serial.baudrate = st[2] serial.parity = SocketSerial.parity_code.get(st[3], SocketSerial.PARITY_NONE) serial.timeout = 20000 serial.bytesize = st[4] serial.stopbits = st[5] serial.unit_instance = unit_instance ProjectSettings.connect('settings_changed', func(): on_settings_changed(serial)) return serial ## [param unit_name] - Уникальное имя устройства[br] func create_modbus(unit_name: StringName) -> sch_3.Sch3: var unit_modbus: = sch_3.Sch3.new(unit_name) return unit_modbus func _ready() -> void: if Engine.is_editor_hint(): return var sock_unicast: = create_socket_udp('уарэп-яу07-частный') var sock_broadcast: = create_socket_udp('уарэп-яу07-общий') var sock_navi: = create_socket_udp('навигация') var sock_caps: = create_socket_caps('уарэп-капсрпб') var sock_trassa: = create_socket_caps('уарэп-трасса') poll_sockets = [sock_unicast, sock_broadcast, sock_caps, sock_navi, sock_trassa] send_sockets['уарэп-яу07-частный'] = sock_unicast send_sockets['уарэп-яу07-общий'] = sock_broadcast send_sockets['уарэп-капсрпб'] = sock_caps send_sockets['уарэп-трасса'] = sock_trassa send_sockets['уарэп-эмс'] = sock_unicast send_sockets['уарэп-яу07-2в'] = sock_unicast send_sockets['уарэп-яу07-2н'] = sock_unicast send_sockets['уарэп-яу07-2к'] = sock_unicast send_sockets['уарэп-яу07-4в'] = sock_unicast send_sockets['уарэп-яу07-4н'] = sock_unicast send_sockets['уарэп-яу07-4к'] = sock_unicast send_sockets['уарэп-яу07-1в'] = sock_unicast send_sockets['уарэп-яу07-1н'] = sock_unicast send_sockets['уарэп-яу07-1к'] = sock_unicast send_sockets['уарэп-яу07-3в'] = sock_unicast send_sockets['уарэп-яу07-3н'] = sock_unicast send_sockets['уарэп-яу07-3к'] = sock_unicast var bind_serials: = [] var bind_modbuses: = [] for unit_name in settings.UnitProfiles: var unit_profile = settings.UnitProfiles[unit_name][1] var proto = settings.UnitProfiles[unit_name][0] if proto in NAVI_PROTO: var new_unit = NAVI_PROTO[proto].new(unit_name) units[unit_name] = new_unit units_udprx[unit_name] = new_unit dst_ports[unit_name] = unit_profile[1] if proto in YAU07_PROTO: var new_unit = YAU07_PROTO[proto].new(unit_name) units[unit_name] = new_unit units_udptx[unit_name] = new_unit dst_addrs[unit_name] = unit_profile[0] dst_ports[unit_name] = unit_profile[1] var addr_port: = StringName('%s:%d' % [unit_profile[0], unit_profile[1]]) addr_port_to_unit_name[addr_port] = unit_name if proto in UDP_PROTO: var new_unit = UDP_PROTO[proto].new(unit_name) units[unit_name] = new_unit units_udptx[unit_name] = new_unit dst_addrs[unit_name] = unit_profile[0] dst_ports[unit_name] = unit_profile[3] if proto in TCP_PROTO: var addr = settings.UnitProfiles[unit_name][1] var unit_tcp = TCP_PROTO[proto].new(unit_name) units[unit_name] = unit_tcp units_tcp[unit_name] = unit_tcp unit_tcp.open(addr[0], addr[1]) if proto in MODBUS_PROTO: var unit_modbus = MODBUS_PROTO[proto].new(unit_name) unit_modbus.port = unit_profile[0] unit_modbus.params = unit_profile.slice(1) units[unit_name] = unit_modbus units_modbus[unit_name] = unit_modbus bind_modbuses.append([unit_modbus, unit_name]) if proto in SERIAL_PROTO: var unit_instance = SERIAL_PROTO[proto].new(unit_name) var serial: = create_serial(unit_instance) units_serial[unit_name] = unit_instance units[unit_name] = unit_instance serials[unit_name] = serial bind_serials.append([serial, unit_instance, unit_name]) if proto in NULL_PROTO: var unit_null = NULL_PROTO[proto].new(unit_name) units[unit_name] = unit_null units_nullp[unit_name] = unit_null for key: StringName in dst_ports: port_to_unit_name[dst_ports[key]] = key if logger_page: for key in units: var u = units[key] u.connect('line_changed', Callable(logger_page, 'on_line_changed')) u.connect('command_fail', Callable(logger_page, 'on_command_fail')) func _on_settings_changed_modbus(bind_modbuses: Array): var _external_ctl = ProjectSettings.get_setting('application/config/Внешнее управление', false) for bm in bind_modbuses: var _unit_modbus = bm[0] var _unit_name = bm[1] func poll_receive_udp(sock: SocketUDP) -> bool: 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: = StringName('%s:%d' % [addr, port]) if addr_port_to_unit_name.has(addr_port): var unit_name: = addr_port_to_unit_name[addr_port] units[unit_name].parse(data, tick) continue if port_to_unit_name.has(port): var unit_name: = port_to_unit_name[port] if units.has(unit_name): units[unit_name].parse(data, tick) continue var local_port: = sock.get_local_port() if port_to_unit_name.has(local_port): var unit_name: = port_to_unit_name[local_port] var unit_udprx: = units[unit_name] unit_udprx.source_addr = [addr, port] unit_udprx.parse(data, tick) continue return false func _process(_delta: float) -> void: if Engine.is_editor_hint(): return tick = Time.get_ticks_msec() RenderingServer.global_shader_parameter_set('tick_curent', tick) RenderingServer.global_shader_parameter_set('random_value', randf()) for unit_name: StringName in units_udptx: var unit_udptx: = units_udptx[unit_name] var sock: = send_sockets[unit_name] var dst_port: = dst_ports[unit_name] var dst_addr: = dst_addrs[unit_name] while Error.OK == unit_udptx.process(tick): var tx_data: = unit_udptx.tx_data.slice(0, unit_udptx.tx_len) sock.send_to([dst_addr, dst_port], tx_data) for unit_name: StringName in units_udprx: var unit_udprx: = units_udprx[unit_name] unit_udprx.process(tick) for unit_name: StringName in units_serial: var unit_serial: = units_serial[unit_name] var serial: = serials[unit_name] serial.tick = tick if Error.OK == unit_serial.process(tick): serial.send_to(unit_serial.tx_data) for unit_name: StringName in units_modbus: var unit_modbus: = units_modbus[unit_name] unit_modbus.process(tick) poll_sockets.any(poll_receive_udp) ## [param unit_name] - Уникальное имя устройства[br] func get_unit_instance(unit_name: StringName) -> unit.Unit: return units.get(unit_name, null)