Files
uarep-ctl/scripts/network.gd

361 lines
16 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.

@tool
class_name Network extends Node
const YAU07_PROTO: = {'yau07tx': yau07.YaU07, '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)
if broad:
log.info('\"%s\" - установлен широковещательный режим' % unit_name)
if bind:
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('уарэп-трасса')
var sock_yems: = create_socket_udp('эмс-ведущий')
poll_sockets = [sock_unicast, sock_broadcast, sock_caps, sock_navi, sock_trassa, sock_yems]
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
send_sockets['эмс-ор63'] = sock_yems
send_sockets['эмс-454'] = sock_yems
send_sockets['эмс-го'] = sock_yems
send_sockets['эмс-рлс1'] = sock_yems
send_sockets['эмс-рлс2'] = sock_yems
send_sockets['эмс-ннвс'] = sock_yems
send_sockets['эмс-изделие7'] = sock_yems
send_sockets['эмс-изделие8'] = sock_yems
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)