diff --git a/project.godot b/project.godot index cd9e880c..45e5c31f 100644 --- a/project.godot +++ b/project.godot @@ -57,6 +57,7 @@ interfer="*res://scripts/interfer.gd" hotkeys="*res://scripts/hotkeys.gd" mercator="*res://scripts/mercator.gd" modbus="*res://modbus/modbus.gd" +tcp5p28="*res://scripts/tcp5p28.gd" [debug] diff --git a/scripts/repnetwork.gd b/scripts/repnetwork.gd index b2ff6cfd..9ac7e228 100644 --- a/scripts/repnetwork.gd +++ b/scripts/repnetwork.gd @@ -2,7 +2,7 @@ extends Node const PROTOCOL_TYPES: = {'yau07tx': yau07.YaU07, 'jsontx': capsrpb.CapsRpb} - +const TCP_PROTO: = {'5p28': tcp5p28.TCP5P28} class Socket extends PacketPeerUDP: func send_to(addr, data): @@ -10,8 +10,24 @@ class Socket extends PacketPeerUDP: self.put_packet(data) +class SocketTCP extends TCPServer: + const RX_TIMEOUT: int = 1000 # Время сброса счётчика принятия части пакета + var peerstream: = PacketPeerStream.new() + var unit_key: Array # Клюс юнита + var head_len: int # Длинна заголовка команды + var len_place: int # Номер начального байта с длинной блока данных + var type_len: int # Количество байт в размере длинны + var rx_tick: int # Время приёма последнего пакета + var rx_all: bool = true # Флаг, что пакет принят полностью + var rx_len: int # Количество принятых байт + var rx_data: PackedByteArray # Принятые данные + var data_len: int # Размер ожидаемых данных + + var poll_sockets: Array[Socket] +var tcp_sockets: Array[SocketTCP] var units: Dictionary +var units_tcp: Dictionary var sock_unicast: Socket var sock_capsrpb: Socket var logger_page: Node @@ -30,12 +46,27 @@ func create_socket(nm) -> Socket: return sock +func create_tcpsocket(nm) -> SocketTCP: + var addr = repsettings.UnitProfiles[nm][1] + var port = repsettings.UnitProfiles[nm][2] + var sock = SocketTCP.new() + sock.unit_key = repsettings.get_unit_key(nm) + sock.head_len = repsettings.UnitProfiles[nm][3] + sock.len_place = repsettings.UnitProfiles[nm][4] + sock.type_len = repsettings.UnitProfiles[nm][5] + sock.listen(port, addr) + log.message(log.INFO, '%s: сокет:%s:%d привязанный:' % [nm, addr, port]) + return sock + + func _ready() -> void: log.info('Модуль работы с сетью готов') poll_sockets.append(create_socket('уарэп-яу07-частный')) poll_sockets.append(create_socket('уарэп-яу07-общий')) poll_sockets.append(create_socket('уарэп-капсрпб-пол')) - + + tcp_sockets.append(create_tcpsocket('уарэп-5п28')) + sock_unicast = poll_sockets[0] sock_capsrpb = poll_sockets[2] @@ -49,9 +80,17 @@ func _ready() -> void: if logger_page: unit.connect('line_changed', Callable(logger_page, 'on_line_changed').bind(unit_key)) unit.connect('command_fail', Callable(logger_page, 'on_command_fail').bind(unit_key)) - + + if proto in TCP_PROTO: + var unit = TCP_PROTO[proto].new(unit_name) + var unit_key = repsettings.get_unit_key(unit_name) + units_tcp[unit_key] = unit + if logger_page: + unit.connect('line_changed', Callable(logger_page, 'on_line_changed').bind(unit_key)) + unit.connect('command_fail', Callable(logger_page, 'on_command_fail').bind(unit_key)) + for key in units: log.info('%s %s:%d' % [units[key].name, key[0], key[1]]) - + for key in units_tcp: log.info('%s %s:%d' % [units_tcp[key].name, key[0], key[1]]) func poll_receive(sock: Socket, tick: int) -> bool: while sock.get_available_packet_count() > 0: @@ -63,6 +102,51 @@ func poll_receive(sock: Socket, tick: int) -> bool: return false +func poll_receive_tcp(sock: SocketTCP, tick: int) -> bool: + if sock.is_connection_available(): # check if someone's trying to connect + var client = sock.take_connection() # accept connection + sock.peerstream.set_stream_peer(client) # bind peerstream to new client + 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, tick) + if sock.unit_key in units and sock.rx_all: units[sock.unit_key].parse(sock.rx_data, tick) + return false + + +func get_client_data(sock: SocketTCP, peer_len_data, peer, tick): + 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: var tick = Time.get_ticks_msec() for addr in units: @@ -71,3 +155,4 @@ func _process(_delta: float) -> void: Error.OK: sock_unicast.send_to(addr, unit.tx_data) Error.FAILED: emit_signal('socket_error', 'ошибка: %s %s:%d' % [unit, addr[0], addr[1]]) poll_sockets.any(func(sock): poll_receive(sock, tick)) + tcp_sockets.any(func(sock): poll_receive_tcp(sock, tick)) diff --git a/scripts/repsettings.gd b/scripts/repsettings.gd index 620b520f..17bed5dd 100644 --- a/scripts/repsettings.gd +++ b/scripts/repsettings.gd @@ -28,7 +28,8 @@ extends Node 'уарэп-яу07-3в': ['yau07tx', '10.1.1.50', 50050, false, false, 'IP-адрес ЯУ-07 прибора ПРД-В'], 'уарэп-яу07-3н': ['yau07tx', '10.1.1.49', 50049, false, false, 'IP-адрес ЯУ-07 прибора ПРД-Н'], 'уарэп-яу07-3к': ['yau07tx', '10.1.1.51', 50051, false, false, 'IP-адрес ЯУ-07 прибора ПРД-Н'], - 'уарэп-щ3': ['uart', '/dev/stty0', 115200, 1, 1, false, 1000, 'Прибор Щ3'] } # скорость, стартовых, стоповых, чётность, таймаут в мкс + 'уарэп-5п28': ['5p28', '127.0.0.1', 50000, 8, 4, 4, 'IP-адрес комплекса 5П-28'], # IP Адресс, порт, мин. длинна пакета, длинна место, длинна длинны + 'уарэп-щ3': ['uart', '/dev/stty0', 115200, 1, 1, false, 1000, 'Прибор Щ3'] } # скорость, стартовых, стоповых, чётность, таймаут в мкс ## Параметры цели diff --git a/scripts/tcp5p28.gd b/scripts/tcp5p28.gd new file mode 100644 index 00000000..7981316c --- /dev/null +++ b/scripts/tcp5p28.gd @@ -0,0 +1,43 @@ +extends Node + +## Реализация "Протокол информационного сопряжения +## СПО АСУ изделия «МП‑550» с СПО 5П28" + + +const ONLINE_TIMEOUT = 3000 ## Время ожидания пакета от КАПС РПБ, мс +const BUFFER_SIZE = 2048 ## Размер приёмного буфера для датаграмм + +class TCP5P28: + signal line_changed(unit: TCP5P28) ## Сигнал состояние связи изменилось + signal data_received(unit: TCP5P28) ## Сигнал данные приняты + signal command_fail(unit: TCP5P28) ## Сигнал при не выполнении команды + signal parse_error(unit: TCP5P28, msg: String) ## Сигнал ошибки разбора сообщения + + static var json_conv: = JSON.new() + var online: = false ## Состояние связи с источником сообщений + var name: String ## Имя экземпляра + var json_dic: Dictionary ## Аппарат для разбора сообщений + var tick: = 0 ## Тик время последнего обновления, мс + var tx_data: = PackedByteArray() ## Пакет для отправки + var tx_stack: Array ## Массив пакетов для отправки + + func _to_string() -> String: return String('капсрпб: "%s" %s ' % [self.name, ['отключен', 'на связи'][int(online)]]) + func _init(nm): self.name = nm + + func parse(data: PackedByteArray, now_tick: int): + tick = now_tick + if not online: + online = true + emit_signal('line_changed', self) + + + func process(now_tick: int): + if online and ((now_tick - tick) > ONLINE_TIMEOUT): + online = false + emit_signal('line_changed', self) + if len(tx_stack): + tx_data = tx_stack[0] + tx_stack.remove_at(0) + return Error.OK + else: + return Error.ERR_UNAVAILABLE