Files
uarep-ctl/scripts/network.gd
2025-08-27 08:37:30 +03:00

344 lines
14 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.

class_name Network extends Node
const UDPTX_PROTO: = {'yau07tx': yau07.YaU07}
const UDPJSON_PROTO: = {'json-capsrpb': capsrpb.CapsRpb}
const TCP_PROTO: = {'5p28': tcp5p28.TCP5P28}
const SERIAL_PROTO: = {'uart': spt25.SPT25}
const MODBUS_PROTO: = {'mbcs': sch3.Sch3}
const UDPRX_PROTO: = {'udprx': navi.NaviSource}
var tick: int = 0
class Serial extends SerialPort:
func send_to(data: PackedByteArray): return write_raw(data)
func _to_string() -> String: return 'последовательный порт \"%s\" @ %d (%s)' % [self.port, self.baudrate, 'открыт' if is_open() else 'закрыт']
func try_open(parsing_proc: Callable):
if is_open():
return Error.OK
var rc = open(port)
if rc == Error.OK:
data_received.connect(parsing_proc)
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 Socket extends PacketPeerUDP:
func send_to(addr, data):
set_dest_address(addr[0], addr[1])
var rc: = put_packet(data)
if rc != Error.OK:
push_error('%s: %s', [error_string(rc), addr])
class SocketTCP extends TCPServer:
const RX_TIMEOUT: int = 1000 ## Время сброса счётчика принятия части пакета.
var peerstream: = PacketPeerStream.new()
var unit_key: StringName ## Ключ юнита.
var head_len: int = 8 ## Длина заголовка команды.
var len_place: int = 4 ## Номер начального байта с длинной блока данных.
var type_len: int = 4 ## Количество байт в размере длинны.
var rx_tick: int ## Время приёма последнего пакета.
var rx_all: bool = true ## Флаг, что пакет принят полностью.
var rx_len: int ## Количество принятых байт.
var rx_data: PackedByteArray ## Принятые данные.
var data_len: int ## Размер ожидаемых данных.
func send_to(data):
var peer = peerstream.get_stream_peer()
peer.put_data(data)
var poll_sockets: Array[Socket]
var tcp_sockets: Array[Array]
var units: Dictionary[StringName, unit.Unit]
var units_udp: Dictionary[StringName, unit.Unit]
var units_udprx: Dictionary[StringName, unit.Unit]
var units_tcp: Dictionary[StringName, unit.Unit]
var units_serial: Dictionary[StringName, unit.Unit]
var units_modbus: Dictionary[StringName, unit.Unit]
var serials: Dictionary[StringName, Serial] # Последовательные порты
var mbcs: Dictionary[StringName, unit.Unit] # Клиенты modbus
var dst_ports: Dictionary[StringName, int]
var dst_addrs: Dictionary[StringName, StringName]
var unit_keys: Dictionary[int, StringName]
var sock_unicast: Socket
var sock_capsrpb: Socket
var logger_page: Node
var send_sockets: Dictionary[StringName, Socket] # Таблица <имяустройста>: <сокет> для отправки сообщений
var addr_port_to_unit_name: Dictionary[StringName, StringName]
func on_serial_data(data, unit): unit.parse(data, tick)
func create_socket(nm) -> Socket:
var addr = settings.UnitProfiles[nm][1]
var port = settings.UnitProfiles[nm][2][0]
var broad = settings.UnitProfiles[nm][3]
var bind = settings.UnitProfiles[nm][4]
var sock: = Socket.new()
sock.set_broadcast_enabled(broad)
var rc = Error.OK if not bind else sock.bind(port, addr)
var errlevel = log.INFO if rc == Error.OK else log.ERROR
log.message(errlevel, '\"%s\" привязан к %s:%d? - %s, результат: %s' % [nm, addr, port, ['нет', 'да'][int(bind)], error_string(rc)])
return sock
func create_tcpsocket(unit_name: StringName) -> SocketTCP:
var addr = settings.UnitProfiles[unit_name][1]
var port = settings.UnitProfiles[unit_name][2][0]
var sock: = SocketTCP.new()
sock.unit_key = unit_name
var rc: = sock.listen(port, addr)
if rc == Error.OK:
log.message(log.INFO, '\"%s\" слушает на %s:%d' % [unit_name, addr, port])
else:
log.message(log.ERROR, '\"%s\" %s:%d \"%s\"' % [unit_name, addr, port, error_string(rc)])
return sock
func create_serial(nm) -> Serial:
var port = settings.UnitProfiles[nm][1]
var baud = settings.UnitProfiles[nm][2][0]
var sp: = Serial.new()
sp.port = port
sp.baudrate = baud
sp.timeout = 20000
return sp
func create_modbus(nm) -> sch_3.Sch3:
var unit_sch3: = sch_3.Sch3.new(nm)
return unit_sch3
func _ready() -> void:
poll_sockets.append(create_socket('уарэп-яу07-частный'))
poll_sockets.append(create_socket('уарэп-яу07-общий'))
poll_sockets.append(create_socket('уарэп-капсрпб'))
poll_sockets.append(create_socket('навигация'))
sock_unicast = poll_sockets[0]
sock_capsrpb = poll_sockets[2]
send_sockets['уарэп-яу07-частный'] = sock_unicast
send_sockets['уарэп-яу07-общий'] = poll_sockets[1]
send_sockets['уарэп-капсрпб'] = sock_capsrpb
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 st = settings.UnitProfiles[unit_name]
var proto = st[0]
if proto in UDPRX_PROTO:
var new_unit = UDPRX_PROTO[proto].new(unit_name)
units[unit_name] = new_unit
units_udprx[unit_name] = new_unit
dst_ports[unit_name] = st[2][0]
if proto in UDPTX_PROTO:
var new_unit = UDPTX_PROTO[proto].new(unit_name)
units[unit_name] = new_unit
units_udp[unit_name] = new_unit
dst_ports[unit_name] = st[2][0]
dst_addrs[unit_name] = st[1]
if proto in UDPJSON_PROTO:
var new_unit = UDPJSON_PROTO[proto].new(unit_name)
units[unit_name] = new_unit
units_udp[unit_name] = new_unit
dst_ports[unit_name] = st[2][1]
dst_addrs[unit_name] = st[1]
if proto in TCP_PROTO:
var unit_tcp = TCP_PROTO[proto].new(unit_name)
units[unit_name] = unit_tcp
units_tcp[unit_name] = unit_tcp
if proto in MODBUS_PROTO:
var unit_modbus = MODBUS_PROTO[proto].new(unit_name)
unit_modbus.port = settings.UnitProfiles[unit_name][1] # TODO: убрать это отсюда (обращение к глобальным данным из экземпляра)
unit_modbus.params = settings.UnitProfiles[unit_name][2] # TODO: убрать это отсюда (обращение к глобальным данным из экземпляра)
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_serial = SERIAL_PROTO[proto].new(unit_name)
var serial = create_serial(unit_name)
units_serial[unit_name] = unit_serial
units[unit_name] = unit_serial
serials[unit_name] = serial
ProjectSettings.connect('settings_changed', Callable(self, '_on_settings_changed_serial').bind(bind_serials))
tcp_sockets.append([create_tcpsocket('уарэп-5п28'), units_tcp['уарэп-5п28']])
for key: StringName in dst_ports:
unit_keys[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'))
for key in settings.UnitProfiles:
var item = settings.UnitProfiles[key]
var addr_port: = StringName('%s:%d' % [item[1], item[2][0]])
addr_port_to_unit_name[addr_port] = StringName(key)
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 _on_settings_changed_serial(bind_serials: Array):
var external_ctl = ProjectSettings.get_setting('application/config/Внешнее управление', false)
for bs in bind_serials:
var serial = bs[0]
var unit_serial = bs[1]
var unit_name = bs[2]
serial.try_close()
if not external_ctl:
var rc = serial.try_open(on_serial_data.bind(unit_serial))
if rc != Error.OK:
log.message(log.ERROR, 'невозможно открыть %s для \"%s\"' % [serial, unit_name])
else:
log.message(log.INFO, '%s для \"%s\"' % [serial, unit_name])
func poll_receive_udp(sock: Socket) -> 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 unit_keys.has(port):
var unit_key: = unit_keys[port]
if units.has(unit_key):
units[unit_key].parse(data, tick)
continue
var local_port: = sock.get_local_port()
if unit_keys.has(local_port):
var unit_name: = unit_keys[local_port]
var unit_udprx: = units[unit_name]
unit_udprx.source_addr = [addr, port]
unit_udprx.parse(data, tick)
continue
return false
func poll_receive_tcp(sock: SocketTCP) -> bool:
if sock.is_connection_available(): # Проверить, если кто то пытается подключиться
var client: = sock.take_connection() # Принять соединение
sock.peerstream.set_stream_peer(client) # Привязать поток к новому клиенту
log.info('подключен: %s:%d к порту: %d' % [ client.get_connected_host(), client.get_connected_port(), client.get_local_port() ] )
var peer = sock.peerstream.get_stream_peer()
if peer:
var peer_len_data = peer.get_available_bytes()
if peer_len_data > 0:
get_client_data(sock, peer_len_data, peer)
if sock.unit_key in units_tcp and sock.rx_all:
units_tcp[sock.unit_key].parse(sock.rx_data, tick)
return false
func get_client_data(sock: SocketTCP, peer_len_data, peer):
if tick - sock.rx_tick > sock.RX_TIMEOUT:
sock.rx_all = true
sock.rx_tick = tick
if sock.rx_all:
if peer_len_data >= sock.head_len:
sock.rx_all = false
sock.rx_len = sock.head_len
var head = peer.get_data(sock.head_len)
sock.data_len = head[1].decode_u32(sock.len_place)
var len_data_in_buf = peer.get_available_bytes()
if len_data_in_buf > 0:
sock.rx_data = head[1]
var len_rx = len_data_in_buf
if len_data_in_buf >= sock.data_len:
len_rx = sock.data_len
get_tcp_data(sock, peer, len_rx)
else:
var len_rx = (sock.head_len + sock.data_len) - sock.rx_len
if peer_len_data < len_rx:
len_rx = peer_len_data
get_tcp_data(sock, peer, len_rx)
func get_tcp_data(sock: SocketTCP, peer, len_rx):
var temp_data = peer.get_data(len_rx)[1]
sock.rx_data.append_array(temp_data)
sock.rx_len += len_rx
if sock.rx_len >= sock.head_len + sock.data_len:
sock.rx_all = true
func _process(_delta: float) -> void:
tick = Time.get_ticks_msec()
RenderingServer.global_shader_parameter_set('tick_curent', tick)
for key: StringName in units_udp:
var unit_udp: = units_udp[key]
if Error.OK == unit_udp.process(tick):
var sock: = send_sockets[unit_udp.name]
var tx_data: = unit_udp.tx_data.slice(0, unit_udp.tx_len)
var dst_port: = dst_ports[key]
var dst_addr: = dst_addrs[key]
sock.send_to([dst_addr, dst_port], tx_data)
for key: StringName in units_udprx:
var unit_udprx: = units_udprx[key]
unit_udprx.process(tick)
for key: StringName in units_serial:
var unit_serial: = units_serial[key]
var serial: = serials[key]
if Error.OK == unit_serial.process(tick):
serial.send_to(unit_serial.tx_data)
for key: StringName in units_modbus:
var unit_modbus: = units_modbus[key]
unit_modbus.process(tick)
for sock_unit: Array in tcp_sockets:
var sock = sock_unit[0]
var unit_tcp = sock_unit[1]
if Error.OK == unit_tcp.process(tick):
sock.send_to(unit_tcp.tx_data)
poll_receive_tcp(sock)
poll_sockets.any(poll_receive_udp)
func get_unit_instance(unit_name: StringName) -> unit.Unit:
tools.allways(unit_name in units, 'нет такого устройства: \"%s\"' % unit_name)
return units[unit_name]