Files
uarep-ctl/scripts/network.gd

385 lines
17 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: = {'spt25': spt25.SPT25}
const MODBUS_PROTO: = {'mbcs': sch3.Sch3}
const UDPRX_PROTO: = {'udprx': navi.NaviSource}
var tick: int = 0
class Spt25Serial extends SerialPort:
func send_to(data: PackedByteArray):
tools.allways(data.size() >= 2, 'длина буфера меньше необходимого')
var i_sum: = data.size() - 3
var csd: = data.slice(0, i_sum)
var v_sum: = get_check_sum(csd)
data.encode_u16(i_sum, v_sum)
return write_raw(data)
func _to_string() -> String: return 'последовательный порт \"%s\" @ %d (%s)' % [self.port, self.baudrate, 'открыт' if is_open() else 'закрыт']
func get_check_sum(src: PackedByteArray) -> int:
var P_DIV: = 0xF0B8
var FCS = ~((src[2]<<8) + src[1])
for i in range(3, src.size()):
var C = src[i]
for j in 8:
if C & 1:
if FCS & 1:
FCS = ((FCS >> 1) | 0x8000) ^ P_DIV
else:
FCS = ((FCS >> 1) | 0x8000)
else:
if FCS & 1:
FCS = (FCS >> 1) ^ P_DIV
else:
FCS = FCS >> 1
C >>= 1
for j in 16:
if FCS & 1:
FCS = (FCS >> 1) ^ P_DIV
else:
FCS = FCS >> 1
return ~FCS
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 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])
class SocketTCP extends TCPServer:
const RX_TIMEOUT: int = 1000 ## Время сброса счётчика принятия части пакета.
var peerstream: = PacketPeerStream.new()
var unit_name: 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: PackedByteArray):
var peer = peerstream.get_stream_peer()
peer.put_data(data)
var poll_sockets: Array[SocketUDP] ## Сокеты для непрерывного опроса наличия новых данных
var tcp_sockets: Array[Array] ## Сокеты для TCP-соединений
var units: Dictionary[StringName, unit.Unit] ## Экземпляры всех сетевых устройств
var units_udp: 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 serials: Dictionary[StringName, Spt25Serial] ## Последовательные порты (uart)
var dst_ports: Dictionary[StringName, int] ## Преобразование имя устройства в порт назначения
var dst_addrs: Dictionary[StringName, StringName] ## Преобразование имя устройства в адрес назначения
var unit_keys: Dictionary[int, StringName] ## Преобразование порта назначения в имя устройства
var sock_unicast: SocketUDP ## Сокет для приёма широковещательных сообщений от ЯУ-07
var sock_capsrpb: SocketUDP ## Сокет для приёма сообщений от КАСПРПБ
var logger_page: Node ## Ссылка на журнал работы
var send_sockets: Dictionary[StringName, SocketUDP] ## Таблица <имяустройста>: <сокет> для отправки сообщений
var addr_port_to_unit_name: Dictionary[StringName, StringName] ## Преобразование адрес:порт в имя устройства
func on_serial_data(data, unit): unit.parse(data, tick)
## [param unit_name] - Уникальное имя устройства[br]
func create_socket_udp(unit_name: StringName) -> SocketUDP:
var st = settings.UnitProfiles[unit_name]
var addr = st[1]
var port = st[2][0]
var broad = st[3]
var bind = st[4]
var sock: = SocketUDP.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' % [unit_name, addr, port, ['нет', 'да'][int(bind)], error_string(rc)])
return sock
## [param unit_name] - Уникальное имя устройства[br]
func create_socket_tcp(unit_name: StringName) -> SocketTCP:
var st = settings.UnitProfiles[unit_name]
var addr = st[1]
var port = st[2][0]
var sock: = SocketTCP.new()
sock.unit_name = 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
## [param unit_name] - Уникальное имя устройства[br]
func create_serial(unit_name: StringName) -> Spt25Serial:
var st = settings.UnitProfiles[unit_name]
var port = st[1]
var baud = st[2][0]
var serial: = Spt25Serial.new()
serial.port = port
serial.baudrate = baud
serial.timeout = 20000
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:
poll_sockets.append(create_socket_udp('уарэп-яу07-частный'))
poll_sockets.append(create_socket_udp('уарэп-яу07-общий'))
poll_sockets.append(create_socket_udp('уарэп-капсрпб'))
poll_sockets.append(create_socket_udp('навигация'))
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 unit_profile = settings.UnitProfiles[unit_name]
var proto = unit_profile[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] = unit_profile[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] = unit_profile[2][0]
dst_addrs[unit_name] = unit_profile[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] = unit_profile[2][1]
dst_addrs[unit_name] = unit_profile[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 = unit_profile[1]
unit_modbus.params = unit_profile[2]
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_socket_tcp('уарэп-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: 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 unit_keys.has(port):
var unit_name: = unit_keys[port]
if units.has(unit_name):
units[unit_name].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:
while true:
var peer_len_data: = peer.get_available_bytes()
if peer_len_data <= 0:
break
get_client_data(sock, peer, peer_len_data)
if sock.unit_name in units_tcp and sock.rx_all:
units_tcp[sock.unit_name].parse(sock.rx_data, tick)
return false
func get_client_data(sock: SocketTCP, peer: StreamPeer, peer_len_data: int) -> void:
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: StreamPeer, len_rx: int):
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 unit_name: StringName in units_udp:
var unit_udp: = units_udp[unit_name]
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[unit_name]
var dst_addr: = dst_addrs[unit_name]
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]
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)
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)
## [param unit_name] - Уникальное имя устройства[br]
func get_unit_instance(unit_name: StringName) -> unit.Unit:
tools.allways(unit_name in units, 'нет такого устройства: \"%s\"' % unit_name)
return units[unit_name]