Compare commits

...

22 Commits

Author SHA1 Message Date
Tot Maxim
d5261f4788 Доработка журнала 2025-12-18 23:38:13 +03:00
Tot Maxim
e2fbe19d84 Добавление лог менеджера 2025-12-18 22:33:11 +03:00
TotMaxim
fe95902a1d Исправление коннекта 2025-12-07 16:03:16 +03:00
TotMaxim
4d50585cc1 Доработка УФ 2025-12-07 16:01:46 +03:00
TotMaxim
ed2fa02d2d Доработка УКП Н 2025-11-30 17:29:21 +03:00
TotMaxim
c405b578ab Доработка УКП К 2025-11-30 17:08:52 +03:00
TotMaxim
e448951153 Merge remote-tracking branch 'origin/master' 2025-11-30 15:57:36 +03:00
TotMaxim
aa9747be6a Доработка ЭМС-Г 2025-11-30 15:57:24 +03:00
Tot Maxim
6817b9b29e исправление 2025-11-29 04:10:47 +03:00
TotMaxim
2a090d02a3 Доработка ЭМС 2025-11-27 21:47:12 +03:00
TotMaxim
1f93815d25 Доработка команды 70 (Флаг ЕМС Г) 2025-11-23 16:24:17 +03:00
TotMaxim
d5e812581f Доработка инизиализация вкладок 2025-11-23 14:41:43 +03:00
TotMaxim
bdb50989f9 Доработка УГ 0х10А порта 2025-11-23 13:58:11 +03:00
TotMaxim
e9811ae42b Merge remote-tracking branch 'origin/master'
# Conflicts:
#	main.gd
2025-11-21 14:08:53 +03:00
TotMaxim
95174f8e9c Отладка согласований кнопок между приборами 2025-11-21 14:06:23 +03:00
TotMaxim
b355884260 Отладка согласований кнопок между приборами 2025-11-21 14:04:51 +03:00
TotMaxim
39bef3a351 Дорабтока атт и статус менеджера 2025-11-21 13:02:32 +03:00
TotMaxim
3648e740a0 Отладка read_isa 2025-11-21 00:49:02 +03:00
TotMaxim
6624110192 Доработка УГ 2025-11-20 21:37:17 +03:00
TotMaxim
667f6d997e Рефактор и доработка read_isa, write_isa 2025-11-20 16:55:55 +03:00
TotMaxim
e1aaa0b39f Доработка ДКМ 2025-11-14 22:53:46 +03:00
TotMaxim
98be72fece Доработка ПРД-К 2025-11-14 18:14:36 +03:00
31 changed files with 4622 additions and 369 deletions

60
StatusManager.gd Normal file
View File

@@ -0,0 +1,60 @@
class_name StatusManager
extends RefCounted
var _device
func _init(device):
_device = device
Callable(_emit_initial_ports).call_deferred()
func _emit_initial_ports() -> void:
"""Вывод начального состояния через существующий сигнал UDPBroadcast"""
# Просто устанавливаем текущие значения (это вызовет update_isa_ports)
var current_106 = _device.get_isa_port(0x106)
var current_108 = _device.get_isa_port(0x108)
var current_10A = _device.get_isa_port(0x10A)
# Триггерим обновление через установку тех же значений
_device.set_isa_port(0x106, current_106)
_device.set_isa_port(0x108, current_108)
_device.set_isa_port(0x10A, current_10A)
# Универсальные методы для работы с битовыми полями
func set_status_field(port: int, value: int, bit_mask: int, shift: int = 0) -> void:
var current_value = _device.get_isa_port(port)
var masked_value = (value & bit_mask) << shift
var clear_mask = ~(bit_mask << shift) & 0xFF
var new_value = (current_value & clear_mask) | masked_value
_device.set_isa_port(port, new_value)
func get_status_field(port: int, bit_mask: int, shift: int = 0) -> int:
return (_device.get_isa_port(port) >> shift) & bit_mask
# Специфичные методы для каждого статуса
func set_dou2_status(status: int) -> void:
set_status_field(0x106, status, 0b111, 0)
func get_dou2_status() -> int:
return get_status_field(0x106, 0b111, 0)
func set_att1_status(status: int) -> void:
set_status_field(0x10A, status, 0b111, 0)
func set_att2_status(status: int) -> void:
set_status_field(0x106, status, 0b111, 3)
func get_att2_status() -> int:
return get_status_field(0x106, 0b111, 3)
func set_dou3_status(status: int) -> void:
set_status_field(0x108, status, 0b111, 0)
func get_dou3_status() -> int:
return get_status_field(0x108, 0b111, 0)
func set_att3_status(status: int) -> void:
set_status_field(0x108, status, 0b111, 3)
func get_att3_status() -> int:
return get_status_field(0x108, 0b111, 3)

1
StatusManager.gd.uid Normal file
View File

@@ -0,0 +1 @@
uid://ddgp4l3kdjwg2

136
config/default_data.json Normal file
View File

@@ -0,0 +1,136 @@
{
"default_data": {
"ПРД-Н1": {
"temperature_ukp": [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31],
"power_ukp": [1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011],
"sockets": [true, true, true, true, true],
"dkm": [false, false, false, false, false, false, false],
"dou2": [false, false, false, false, false, false, false],
"dou3": [false, false, false, false, false, false, false],
"att2": [false, false, false, false, false, false, false, false],
"att3": [false, false, false, false, false, false, false, false],
"ip": [false, false, false, false, false, false, false, false]
},
"ПРД-В1": {
"temperature_ukp": [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31],
"power_ukp": [1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011],
"sockets": [true, true, true, true, true],
"dkm": [false, false, false, false, false, false, false],
"dou2": [false, false, false, false, false, false, false],
"dou3": [false, false, false, false, false, false, false],
"att2": [false, false, false, false, false, false, false, false],
"att3": [false, false, false, false, false, false, false, false],
"ip": [false, false, false, false, false, false, false, false]
},
"ПРД-К1": {
"temperature_ukp": [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32],
"power_ukp": [1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012],
"sockets": [true, true, true, true, true],
"dkm": [false, false, false, false, false, false, false],
"dou2": [false, false, false, false, false, false, false],
"dou3": [false, false, false, false, false, false, false],
"att2": [false, false, false, false, false, false, false, false],
"att3": [false, false, false, false, false, false, false, false],
"ip": [false, false, false, false, false, false, false, false]
},
"ПРД-Н2": {
"temperature_ukp": [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31],
"power_ukp": [1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011],
"sockets": [false, false, false, false, false],
"dkm": [false, false, false, false, false, false, false],
"dou2": [false, false, false, false, false, false, false],
"dou3": [false, false, false, false, false, false, false],
"att2": [false, false, false, false, false, false, false, false],
"att3": [false, false, false, false, false, false, false, false],
"ip": [false, false, false, false, false, false, false, false]
},
"ПРД-В2": {
"temperature_ukp": [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31],
"power_ukp": [1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011],
"sockets": [false, false, false, false, false],
"dkm": [false, false, false, false, false, false, false],
"dou2": [false, false, false, false, false, false, false],
"dou3": [false, false, false, false, false, false, false],
"att2": [false, false, false, false, false, false, false, false],
"att3": [false, false, false, false, false, false, false, false],
"ip": [false, false, false, false, false, false, false, false]
},
"ПРД-К2": {
"temperature_ukp": [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32],
"power_ukp": [1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012],
"sockets": [false, false, false, false, false],
"dkm": [false, false, false, false, false, false, false],
"dou2": [false, false, false, false, false, false, false],
"dou3": [false, false, false, false, false, false, false],
"att2": [false, false, false, false, false, false, false, false],
"att3": [false, false, false, false, false, false, false, false],
"ip": [false, false, false, false, false, false, false, false]
},
"ПРД-Н3": {
"temperature_ukp": [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31],
"power_ukp": [1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011],
"sockets": [false, false, false, false, false],
"dkm": [false, false, false, false, false, false, false],
"dou2": [false, false, false, false, false, false, false],
"dou3": [false, false, false, false, false, false, false],
"att2": [false, false, false, false, false, false, false, false],
"att3": [false, false, false, false, false, false, false, false],
"ip": [false, false, false, false, false, false, false, false]
},
"ПРД-В3": {
"temperature_ukp": [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31],
"power_ukp": [1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011],
"sockets": [false, false, false, false, false],
"dkm": [false, false, false, false, false, false, false],
"dou2": [false, false, false, false, false, false, false],
"dou3": [false, false, false, false, false, false, false],
"att2": [false, false, false, false, false, false, false, false],
"att3": [false, false, false, false, false, false, false, false],
"ip": [false, false, false, false, false, false, false, false]
},
"ПРД-К3": {
"temperature_ukp": [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32],
"power_ukp": [1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012],
"sockets": [false, false, false, false, false],
"dkm": [false, false, false, false, false, false, false],
"dou2": [false, false, false, false, false, false, false],
"dou3": [false, false, false, false, false, false, false],
"att2": [false, false, false, false, false, false, false, false],
"att3": [false, false, false, false, false, false, false, false],
"ip": [false, false, false, false, false, false, false, false]
},
"ПРД-Н4": {
"temperature_ukp": [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31],
"power_ukp": [1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011],
"sockets": [false, false, false, false, false],
"dkm": [false, false, false, false, false, false, false],
"dou2": [false, false, false, false, false, false, false],
"dou3": [false, false, false, false, false, false, false],
"att2": [false, false, false, false, false, false, false, false],
"att3": [false, false, false, false, false, false, false, false],
"ip": [false, false, false, false, false, false, false, false]
},
"ПРД-В4": {
"temperature_ukp": [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31],
"power_ukp": [1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011],
"sockets": [false, false, false, false, false],
"dkm": [false, false, false, false, false, false, false],
"dou2": [false, false, false, false, false, false, false],
"dou3": [false, false, false, false, false, false, false],
"att2": [false, false, false, false, false, false, false, false],
"att3": [false, false, false, false, false, false, false, false],
"ip": [false, false, false, false, false, false, false, false]
},
"ПРД-К4": {
"temperature_ukp": [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32],
"power_ukp": [1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012],
"sockets": [false, false, false, false, false],
"dkm": [false, false, false, false, false, false, false],
"dou2": [false, false, false, false, false, false, false],
"dou3": [false, false, false, false, false, false, false],
"att2": [false, false, false, false, false, false, false, false],
"att3": [false, false, false, false, false, false, false, false],
"ip": [false, false, false, false, false, false, false, false]
}
}
}

517
main.gd
View File

@@ -1,27 +1,166 @@
extends Control
const CONFIG_PATH = "res://config/default_data.json"
const BUFFER_SIZE = 32
var dictionary_yau07: Dictionary
var udp_broadcast: UDPBroadcast
var uf_broadcast: UF_Broadcast
var default_data: Dictionary = {}
var dictionary_yau07: Dictionary
var dictionary_sockets_bit: Dictionary ## Байт 8 для отправки о состоянии ячеек
func _load_default_data():
var file = FileAccess.open(CONFIG_PATH, FileAccess.READ)
if file:
var json = JSON.new()
var error = json.parse(file.get_as_text())
if error == OK:
default_data = json.data["default_data"]
else:
push_error("JSON Parse Error: ", json.get_error_message())
file.close()
else:
push_error("Failed to load config file: ", CONFIG_PATH)
func _apply_default_data():
var prd_nodes = get_tree().get_nodes_in_group('prd')
for prd in prd_nodes:
var device_name = prd.get_meta('yau07')
if default_data.has(device_name):
var device_data = default_data[device_name]
_fill_ukp_fields(prd, device_data)
# Заполняем поля температур
func _fill_ukp_fields(prd_node: Control, device_data: Dictionary) -> void:
var device_name = prd_node.get_meta('yau07')
# Заполняем поля температур
var temp_fields = _get_ukp_nodes_for_device(device_name, [prd_node], "temperature_ukp")
if device_data.has("temperature_ukp"):
for i in range(min(temp_fields.size(), device_data["temperature_ukp"].size())):
temp_fields[i].text = str(device_data["temperature_ukp"][i])
# Заполняем поля мощности
var power_fields = _get_ukp_nodes_for_device(device_name, [prd_node], "power_ukp")
if device_data.has("power_ukp"):
for i in range(min(power_fields.size(), device_data["power_ukp"].size())):
power_fields[i].text = str(device_data["power_ukp"][i])
# Устанавливаем состояния сокетов
if device_data.has("sockets"):
var socket_fields = _get_ukp_nodes_for_device(device_name, [prd_node], "socket")
for i in range(min(socket_fields.size(), device_data["sockets"].size())):
socket_fields[i].set_pressed_no_signal(device_data["sockets"][i])
# Устанавливаем состояния ДКМ
if device_data.has("dkm"):
var dkm_fields = _get_ukp_nodes_for_device(device_name, [prd_node], "dkm")
for i in range(min(dkm_fields.size(), device_data["dkm"].size())):
dkm_fields[i].set_pressed_no_signal(device_data["dkm"][i])
# Устанавливаем состояния ДОУ2
if device_data.has("dou2"):
var dou2_fields = _get_ukp_nodes_for_device(device_name, [prd_node], "dou2")
for i in range(min(dou2_fields.size(), device_data["dou2"].size())):
dou2_fields[i].set_pressed_no_signal(device_data["dou2"][i])
# Устанавливаем состояния ДОУ3
if device_data.has("dou3"):
var dou3_fields = _get_ukp_nodes_for_device(device_name, [prd_node], "dou3")
for i in range(min(dou3_fields.size(), device_data["dou3"].size())):
dou3_fields[i].set_pressed_no_signal(device_data["dou3"][i])
# Устанавливаем состояния аттенюаторов 1
if device_data.has("att1"):
var att1_fields = _get_ukp_nodes_for_device(device_name, [prd_node], "att1")
for i in range(min(att1_fields.size(), device_data["att1"].size())):
att1_fields[i].set_pressed_no_signal(device_data["att1"][i])
# Устанавливаем состояния аттенюаторов 2
if device_data.has("att2"):
var att2_fields = _get_ukp_nodes_for_device(device_name, [prd_node], "att2")
for i in range(min(att2_fields.size(), device_data["att2"].size())):
att2_fields[i].set_pressed_no_signal(device_data["att2"][i])
# Устанавливаем состояния аттенюаторов 3
if device_data.has("att3"):
var att3_fields = _get_ukp_nodes_for_device(device_name, [prd_node], "att3")
for i in range(min(att3_fields.size(), device_data["att3"].size())):
att3_fields[i].set_pressed_no_signal(device_data["att3"][i])
# Устанавливаем состояния источников питания (с инверсией)
if device_data.has("ip"):
var ip_fields = _get_ukp_nodes_for_device(device_name, [prd_node], "ip")
for i in range(min(ip_fields.size(), device_data["ip"].size())):
# Для IP используется инверсия: true в JSON = нажато = бит 0
var is_pressed = not device_data["ip"][i]
ip_fields[i].set_pressed_no_signal(is_pressed)
func _ready() -> void:
var device_yau07: Array = get_tree().get_nodes_in_group('device-addr')
for device in device_yau07:
device.connect('toggled', on_modify_device.bind(device.get_meta('device')))
var sockets: Array = get_tree().get_nodes_in_group('socket')
for soc in sockets:
soc.connect('toggled', _on_modify_sockets.bind(soc.get_meta('bit'), soc.get_meta('device'))) ## Мета bit от 0 до 3, мета ПРД
var power_ukp: Array = get_tree().get_nodes_in_group('power_ukp')
for socket_power in power_ukp:
var power_ukp_arr_node: Array = get_tree().get_nodes_in_group('power_ukp')
for socket_power in power_ukp_arr_node:
socket_power.connect('text_submitted', _on_ukp_submitted.bind(socket_power.get_meta('device')))
var temperature_ukp: Array = get_tree().get_nodes_in_group('temperature_ukp')
for socket_temp in temperature_ukp:
var temperature_ukp_arr_node: Array = get_tree().get_nodes_in_group('temperature_ukp')
for socket_temp in temperature_ukp_arr_node:
socket_temp.connect('text_submitted', _on_ukp_submitted.bind(socket_temp.get_meta('device')))
var dkm_arr_node: Array = get_tree().get_nodes_in_group('dkm')
for dkm in dkm_arr_node:
dkm.connect('toggled', _on_modify_dkm.bind(dkm.get_meta('bit'), dkm.get_meta('device')))
var dou2_arr_node: Array = get_tree().get_nodes_in_group('dou2')
for dou2 in dou2_arr_node:
dou2.toggled.connect(_on_modify_status.bind(dou2.get_meta('bit'), dou2.get_meta('device'), "dou2"))
var dou3_arr_node: Array = get_tree().get_nodes_in_group('dou3')
for dou3 in dou3_arr_node:
dou3.toggled.connect(_on_modify_status.bind(dou3.get_meta('bit'), dou3.get_meta('device'), "dou3"))
var att1_arr_node: Array = get_tree().get_nodes_in_group('att1')
for att1 in att1_arr_node:
att1.toggled.connect(_on_modify_status.bind(att1.get_meta('bit'), att1.get_meta('device'), "att1"))
var att2_arr_node: Array = get_tree().get_nodes_in_group('att2')
for att2 in att2_arr_node:
att2.toggled.connect(_on_modify_status.bind(att2.get_meta('bit'), att2.get_meta('device'), "att2"))
var att3_arr_node: Array = get_tree().get_nodes_in_group('att3')
for att3 in att3_arr_node:
att3.toggled.connect(_on_modify_status.bind(att3.get_meta('bit'), att3.get_meta('device'), "att3"))
var ip_arr_node: Array = get_tree().get_nodes_in_group('ip')
for ip in ip_arr_node:
ip.connect('toggled', _on_modify_ip.bind(ip.get_meta('bit'), ip.get_meta('device')))
var ems_arr_node: Array = get_tree().get_nodes_in_group('ems_g')
for ems in ems_arr_node:
ems.connect('bits_changed', _on_modify_ems_g_status.bind(ems.get_meta('device')))
$uf_button.connect('toggled', on_modify_uf.bind($uf_button.get_meta('device')))
for bit in get_tree().get_nodes_in_group('uf_status'):
bit.connect('toggled', _on_update_uf_status.bind(bit.get_meta('bit')))
_update_tab_container()
_load_default_data()
_apply_default_data()
func _process(_delta: float) -> void:
for device in dictionary_yau07:
dictionary_yau07[device]._on_poll_timeout()
func _exit_tree():
for device in dictionary_yau07:
@@ -31,119 +170,311 @@ func _exit_tree():
func on_modify_device(toggled: bool, name_device: String) -> void:
if toggled:
udp_broadcast = UDPBroadcast.new(name_device)
udp_broadcast.add_timer_to_scene(self)
var logger_node: Node
for node in get_tree().get_nodes_in_group('logger'):
if node.get_meta('device') == name_device:
logger_node = node
break
udp_broadcast = UDPBroadcast.new(name_device, logger_node)
udp_broadcast.add_timers_to_scene(self)
udp_broadcast.start_broadcast()
dictionary_yau07[name_device] = udp_broadcast
print("Устройство %s активировано" % name_device)
dictionary_yau07[name_device].connect('update_isa_ports', _on_update_isa_ports)
dictionary_yau07[name_device].connect('update_socket_status', _on_update_socket_status)
var device_data = default_data[name_device]
_on_ukp_submitted('', name_device)
dictionary_yau07[name_device].status_manager.set_dou2_status(bool_array_to_number(device_data["dou2"]))
dictionary_yau07[name_device].status_manager.set_att2_status(bool_array_to_number(device_data["att2"]))
dictionary_yau07[name_device].status_manager.set_dou3_status(bool_array_to_number(device_data["dou3"]))
dictionary_yau07[name_device].status_manager.set_att3_status(bool_array_to_number(device_data["att3"]))
dictionary_yau07[name_device].set_ip_status(bool_array_to_number(device_data["ip"]))
dictionary_yau07[name_device].set_sockets_status(bool_array_to_number(device_data["sockets"]))
else:
dictionary_yau07[name_device].close_udp_unit()
if dictionary_yau07.has(name_device) and dictionary_yau07[name_device]:
dictionary_yau07[name_device].close_udp_unit()
dictionary_yau07.erase(name_device)
print("Устройство %s деактивировано" % name_device)
# Обновляем вкладки и переключаемся при активации
_update_tab_container(toggled, name_device)
func _process(_delta: float) -> void:
for device in dictionary_yau07:
if dictionary_yau07[device]:
dictionary_yau07[device].poll_receive()
func on_modify_uf(toggled: bool, name_device: String) -> void:
if toggled:
uf_broadcast = UF_Broadcast.new(name_device)
uf_broadcast.add_timers_to_scene(self)
uf_broadcast.start_broadcast()
dictionary_yau07[name_device] = uf_broadcast
print("Устройство %s активировано" % name_device)
else:
if dictionary_yau07.has(name_device) and dictionary_yau07[name_device]:
dictionary_yau07[name_device].close_udp_unit()
dictionary_yau07.erase(name_device)
print("Устройство %s деактивировано" % name_device)
static func _set_bit(bits: int, index: int, val: bool) -> int:
return bits | (1 << index) if val else bits & ~(1 << index)
func _update_tab_container(just_activated: bool = false, activated_device: String = ""):
var tab_container = $TabContainer
var device_nodes = get_tree().get_nodes_in_group('device-addr')
var has_visible_tabs = false
for i in range(tab_container.get_tab_count()):
var tab_control = tab_container.get_tab_control(i)
var device_name = tab_control.get_meta('yau07', '')
var device_button = null
for device in device_nodes:
if device.get_meta('device') == device_name:
device_button = device
break
if device_button:
var is_enabled = device_button.button_pressed
tab_container.set_tab_disabled(i, !is_enabled)
if is_enabled:
has_visible_tabs = true
if just_activated and device_name == activated_device:
tab_container.current_tab = i
tab_container.visible = has_visible_tabs
func _on_update_socket_status(status: int, device_name: String)->void:
var socket_arr = _get_nodes_for_device(device_name, 'socket')
for socket in socket_arr:
var bit_index = socket.get_meta('bit')
var is_bit_set = (status >> bit_index) & 1
socket.set_pressed_no_signal(is_bit_set == 1)
func _on_update_uf_status(toggled: bool, bit_index: int)->void:
if not dictionary_yau07.has('УФ'): return
var status = dictionary_yau07['УФ'].get_status_uf()
if bit_index == 6:
status = dictionary_yau07['УФ'].get_power_bit_uf()
if toggled: # Установить бит в 1
status = status | (1 << bit_index)
else: # Сбросить бит в 0
status = status & ~(1 << bit_index)
if bit_index == 6: dictionary_yau07['УФ'].set_power_bit_uf(status)
else: dictionary_yau07['УФ'].set_status_uf(status)
func _on_update_isa_ports(isa_ports: Dictionary, device_name: String)->void:
for port_addr in isa_ports:
var value = isa_ports[port_addr]
var port_configs = {
0x106: [ # Порт 0x106
{'group': 'dou2', 'mask': 0b111, 'shift': 0},
{'group': 'att2', 'mask': 0b111, 'shift': 3}
],
0x108: [ # Порт 0x108
{'group': 'dou3', 'mask': 0b111, 'shift': 0},
{'group': 'att3', 'mask': 0b111, 'shift': 3}
],
0x10A: [ # Порт 0x10A
{'group': 'att1', 'mask': 0b111, 'shift': 0}
]
}
if port_configs.has(port_addr):
for config in port_configs[port_addr]:
var status = (value >> config.shift) & config.mask
_update_specific_device_buttons(config.group, status, device_name)
func _update_specific_device_buttons(group_name: String, status: int, target_device: String) -> void:
"""Обновление кнопок только для конкретного устройства"""
var buttons = _get_nodes_for_device(target_device, group_name)
for button in buttons:
if button.has_meta('bit'):
var bit_value = button.get_meta('bit')
var should_be_pressed = (status == bit_value)
if button.button_pressed != should_be_pressed:
button.set_pressed_no_signal(should_be_pressed)
if should_be_pressed:
button.grab_focus()
func _get_nodes_for_device(device: String, group_name: String) -> Array:
"""Получение всех нод в группе, принадлежащих указанному устройству"""
var prd_nodes = get_tree().get_nodes_in_group('prd')
var result = []
for prd in prd_nodes:
if prd.get_meta('yau07') == device:
for node in get_tree().get_nodes_in_group(group_name):
if prd.is_ancestor_of(node) and node.get_meta('device') == device:
result.append(node)
break
return result
## Функция для установки статусов ячеек на связи (УГ, ЭМС-Г, УКП)
func _on_modify_sockets(toggled: bool, bit: int, device: String)->void:
if dictionary_yau07.has(device):
var udp_unit = dictionary_yau07[device]
udp_unit.sockets_status_bit = _set_bit(udp_unit.sockets_status_bit, bit, toggled)
var current_status = udp_unit.get_sockets_status() # Используем геттер
var new_status = _set_bit(current_status, bit, toggled)
udp_unit.set_sockets_status(new_status) # Используем сеттер
print("Статус сокетов устройства %s обновлен: 0x%02X" % [device, new_status])
## Функция для установки статусов ДКМ в ПРД
func _on_modify_dkm(toggled: bool, bit: int, device: String)->void:
if dictionary_yau07.has(device):
var udp_unit = dictionary_yau07[device]
var current_status = udp_unit.get_dkm_status() # Используем геттер
var new_status = _set_bit(current_status, bit, toggled)
udp_unit.set_dkm_status(new_status) # Используем сеттер
print("Статус DKM устройства %s обновлен: 0x%02X" % [device, new_status])
func _on_modify_ems_g_status(input_impulse: int, input_const: int, output_impulse: int, output_const: int, device: String)->void:
if not dictionary_yau07.has(device):
return
var udp_unit = dictionary_yau07[device]
udp_unit.set_ems_g_status(input_impulse, input_const, output_impulse, output_const)
func _on_modify_status(toggled: bool, bit: int, device: String, status_type: String) -> void:
if not dictionary_yau07.has(device):
return
var udp_unit = dictionary_yau07[device]
var button_group = _get_nodes_for_device(device, status_type)
if not toggled:
# Восстанавливаем предыдущее состояние
for button in button_group:
if button.has_meta('bit') and button.get_meta('bit') == bit:
button.set_pressed(true)
return
# Выключаем другие кнопки в группе
for button in button_group:
if button.has_meta('bit') and button.get_meta('bit') != bit and button.button_pressed:
button.set_pressed_no_signal(false)
# Устанавливаем статус через менеджер
match status_type:
"dou2": udp_unit.status_manager.set_dou2_status(bit)
"att1": udp_unit.status_manager.set_att1_status(bit)
"att2": udp_unit.status_manager.set_att2_status(bit)
"dou3": udp_unit.status_manager.set_dou3_status(bit)
"att3": udp_unit.status_manager.set_att3_status(bit)
func _on_modify_ip(toggled: bool, bit: int, device: String):
if dictionary_yau07.has(device):
var udp_unit = dictionary_yau07[device]
var current_status = udp_unit.get_ip_status()
# Инверсия: нажата = 0, отжата = 1
var bit_value = 0 if toggled else 1
var new_status
if bit_value == 0: new_status = current_status & ~(1 << bit) # Устанавливаем бит в 0
else: new_status = current_status | (1 << bit) # Устанавливаем бит в 1
udp_unit.set_ip_status(new_status)
print("Статус ИП устройства %s обновлен: 0x%02X" % [device, new_status])
## Функция для установки значений в ячейках УКП
func _on_ukp_submitted(_new_text: String, device: String) -> void:
if not dictionary_yau07.has(device):
return
var prep_temperature_ukp1_array: Array = _on_text_temperature_changed(device, 1)
var prep_temperature_ukp2_array: Array = _on_text_temperature_changed(device, 2)
var prep_power_ukp1_array: Array = _on_text_power_changed(device, 1)
var prep_power_ukp2_array: Array = _on_text_power_changed(device, 2)
var prep_power_ukp2_array: Array = _on_text_power_changed(device, 2)
var combine_data_ukp1: Array
combine_data_ukp1 = _combine_arrays(combine_data_ukp1, prep_temperature_ukp1_array, prep_power_ukp1_array)
var combine_data_ukp2: Array
combine_data_ukp2 = _combine_arrays(combine_data_ukp2, prep_temperature_ukp2_array, prep_power_ukp2_array)
_form_buffers(device,combine_data_ukp1, 1, dictionary_yau07)
_form_buffers(device,combine_data_ukp2, 2, dictionary_yau07)
var combine_data_ukp1: Array = _combine_arrays(prep_temperature_ukp1_array, prep_power_ukp1_array)
var combine_data_ukp2: Array = _combine_arrays(prep_temperature_ukp2_array, prep_power_ukp2_array)
_form_buffers(device, combine_data_ukp1, 1, dictionary_yau07)
_form_buffers(device, combine_data_ukp2, 2, dictionary_yau07)
func _on_text_temperature_changed(device: String, number_device: int) -> Array:
return _process_ukp_data(device, number_device, "temperature_ukp", _convert_temperature)
func _on_text_power_changed(device: String, number_device: int) -> Array:
return _process_ukp_data(device, number_device, "power_ukp", _convert_power)
func _process_ukp_data(device: String, number_device: int, group_name: String, converter: Callable) -> Array:
var prd_module_arr = get_tree().get_nodes_in_group('prd')
var current_change_nodes: Array = _get_ukp_nodes_for_device(device, prd_module_arr, group_name)
var result_arr: Array = [0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff]
var config: Dictionary = _get_ukp_config(number_device)
for change_node in current_change_nodes:
var place = change_node.get_meta('place')
if change_node.get_meta('place') in config.meta_range:
if number_device == 2: place -= 8
var value = _safe_convert_to_int(change_node.text)
result_arr[place] = converter.call(value)
return result_arr
static func _get_ukp_nodes_for_device(device: String, prd_module_arr: Array, group_name: String) -> Array:
for prd in prd_module_arr:
if device == prd.get_meta('yau07'):
var nodes = []
for node in prd.get_tree().get_nodes_in_group(group_name):
if prd.is_ancestor_of(node):
nodes.append(node)
return nodes
return []
static func _get_ukp_config(number_device: int) -> Dictionary:
if number_device == 1:
return {
"meta_range": [0, 1, 2, 3 ,4, 5, 6, 7]
}
else:
return {
"meta_range": [8, 9, 10, 11, 12, 13, 14, 15]
}
static func _convert_temperature(value: int) -> int:
const CONST_MIN_TEMP: int = -25
const MAXIMUM_CODE_ADC: int = 3796
const MINIMUM_CODE_AD: int = 2660
const TEMP: float = 115.0 / (MAXIMUM_CODE_ADC - MINIMUM_CODE_AD)
const TEMP: float = 115.0 / (MAXIMUM_CODE_ADC - MINIMUM_CODE_AD)
var current_change: Array
for prd in get_tree().get_nodes_in_group('prd'):
if device == prd.get_meta('yau07'):
for temperature_node in prd.get_tree().get_nodes_in_group('temperature_ukp'):
if prd.is_ancestor_of(temperature_node):
current_change.append(temperature_node)
break
var temp_arr: Array = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
if number_device == 1:
for i in 6:
if not current_change[i].text == null:
var value = _safe_convert_to_int(current_change[i].text)
var normal_temp: int = MAXIMUM_CODE_ADC - (value - CONST_MIN_TEMP - 3) / TEMP
temp_arr[i] = normal_temp
elif number_device == 2:
for i in 6:
if not current_change[i+6].text == null:
var value = _safe_convert_to_int(current_change[i+6].text)
var normal_temp: int = MAXIMUM_CODE_ADC - (value - CONST_MIN_TEMP - 3) / TEMP
temp_arr[i] = normal_temp
return temp_arr
var result: float = MAXIMUM_CODE_ADC - (value - CONST_MIN_TEMP - 3) / TEMP
return int(result)
func _on_text_power_changed(device: String, number_device: int) -> Array:
var current_change: Array
for prd in get_tree().get_nodes_in_group('prd'):
if device == prd.get_meta('yau07'):
for power_node in prd.get_tree().get_nodes_in_group('power_ukp'):
if prd.is_ancestor_of(power_node):
current_change.append(power_node)
break
var power_arr: Array = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
if number_device == 1:
for i in 6:
if not current_change[i].text == null:
var value = _safe_convert_to_int(current_change[i].text)
power_arr[i] = value
elif number_device == 2:
for i in 6:
if not current_change[i+6].text == null:
var value = _safe_convert_to_int(current_change[i+6].text)
power_arr[i] = value
return power_arr
#_update_buffers(power_arr_1, device)
static func _convert_power(value: int) -> int:
return value
static func _safe_convert_to_int(text: String) -> int:
text = text.strip_edges()
text = text.strip_edges().to_lower()
if text.is_valid_int():
return int(text)
elif text.is_valid_float():
return roundi(float(text))
elif text.begins_with("0x") and text.substr(2).is_valid_hex_number():
return text.hex_to_int()
else:
return 0
static func _combine_arrays(ukp_data: Array, power_arr: Array, temp_arr: Array)-> Array:
for i in temp_arr.size():
ukp_data.append(temp_arr[i])
for i in power_arr.size():
ukp_data.append(power_arr[i])
return ukp_data
static func _combine_arrays(power_arr: Array, temp_arr: Array)-> Array:
return temp_arr + power_arr
static func _form_buffers(device: String, ukp_data: Array, number_device: int, dic_yau07: Dictionary):
@@ -155,5 +486,39 @@ static func _form_buffers(device: String, ukp_data: Array, number_device: int, d
var udp_unit = dic_yau07[device]
if number_device == 1:
udp_unit.ukp0_data = buffer
print("Данные UKP0 устройства %s обновлены" % device)
elif number_device == 2:
udp_unit.ukp1_data = buffer
print("Данные UKP1 устройства %s обновлены" % device)
static func _set_bit(bits: int, index: int, val: bool) -> int:
return bits | (1 << index) if val else bits & ~(1 << index)
static func bool_array_to_number(bool_array: Array)-> int:
var result: int = 0
for i in range(bool_array.size()):
if bool_array[i]:
result |= (1 << i)
return result
## Метод для работы с ISA портами
func read_isa_ports(device: String, port_addresses: Array) -> Dictionary:
"""Чтение ISA портов указанного устройства"""
if dictionary_yau07.has(device):
return dictionary_yau07[device].read_isa_ports(port_addresses)
else:
push_error("Устройство %s не найдено" % device)
return {}
## Метод для записи ISA портов
func write_isa_port(device: String, port_address: int, value: int) -> bool:
"""Запись значения в ISA порт указанного устройства"""
if dictionary_yau07.has(device):
return dictionary_yau07[device].set_isa_port(port_address, value)
else:
push_error("Устройство %s не найдено" % device)
return false

1758
main.tscn

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,19 @@
shader_type canvas_item;
uniform float outerRadius : hint_range(0.0, 50.0) = 1.0;
uniform float MainAlpha : hint_range(0.0, 1.0) = 1.0;
uniform vec4 circle_color : source_color = vec4(1.0, 0.0, 0.0, 1.0);
uniform vec4 circle_color2 : source_color = vec4(0.0, 0.0, 1.0, 1.0);
void fragment() {
float x = abs(UV.x-.35)*2.0;
float y = abs(UV.y-.3)*2.0;
float v = (sqrt((x*x)+(y*y))/outerRadius);
// Плавный переход прозрачности
float alpha = 1.0 - smoothstep(0.0, 1.0, v);
// Создаем градиент от circle_color к circle_color2
vec4 gradient_color = mix(circle_color, circle_color2, v);
COLOR = vec4(gradient_color.rgb, alpha * MainAlpha * gradient_color.a);
}

View File

@@ -0,0 +1 @@
uid://cp2p2g3mgxm8o

View File

@@ -17,10 +17,5 @@ config/icon="res://icon.svg"
[display]
window/size/viewport_width=1200
window/size/viewport_height=600
[rendering]
renderer/rendering_method="gl_compatibility"
renderer/rendering_method.mobile="gl_compatibility"
window/size/viewport_width=1500
window/size/viewport_height=850

View File

@@ -0,0 +1,21 @@
extends PanelContainer
@onready var animated_sprite = $SZI_x
signal update_frame(bit: int, frame: int)
func _ready():
gui_input.connect(_on_gui_input)
mouse_filter = Control.MOUSE_FILTER_PASS
func _on_gui_input(event):
if event is InputEventMouseButton:
if event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
_switch_frame()
func _switch_frame():
var current_frame = animated_sprite.frame
var next_frame = (current_frame + 1) % 3
animated_sprite.frame = next_frame
emit_signal('update_frame', get_meta("bit"), next_frame)

View File

@@ -0,0 +1 @@
uid://bg8n8wt3kjqpm

View File

@@ -0,0 +1,46 @@
[gd_scene load_steps=7 format=3 uid="uid://dn3jp4r8ij3rf"]
[ext_resource type="Texture2D" uid="uid://cwabmu3cljbpr" path="res://sockets/ems_g/эмс-бланк.png" id="1_bsrew"]
[ext_resource type="Script" uid="uid://bg8n8wt3kjqpm" path="res://sockets/ems_g/socket_status.gd" id="1_kydjn"]
[ext_resource type="Texture2D" uid="uid://sx6xcpus2yf" path="res://sockets/ems_g/эмс-бланк-пост.png" id="2_b2kjm"]
[ext_resource type="Texture2D" uid="uid://snpqhalopnp" path="res://sockets/ems_g/эмс-бланк-перем.png" id="3_odpjv"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_q1ixs"]
bg_color = Color(0, 0, 0, 0.490196)
corner_radius_top_left = 5
corner_radius_top_right = 5
corner_radius_bottom_right = 5
corner_radius_bottom_left = 5
[sub_resource type="SpriteFrames" id="SpriteFrames_a7q1i"]
animations = [{
"frames": [{
"duration": 1.0,
"texture": ExtResource("1_bsrew")
}, {
"duration": 1.0,
"texture": ExtResource("2_b2kjm")
}, {
"duration": 1.0,
"texture": ExtResource("3_odpjv")
}, {
"duration": 1.0,
"texture": null
}],
"loop": true,
"name": &"default",
"speed": 5.0
}]
[node name="PanelContainer" type="PanelContainer"]
custom_minimum_size = Vector2(50, 35)
offset_left = -3.0
offset_right = 47.0
offset_bottom = 35.0
theme_override_styles/panel = SubResource("StyleBoxFlat_q1ixs")
script = ExtResource("1_kydjn")
[node name="SZI_x" type="AnimatedSprite2D" parent="."]
position = Vector2(25, 17)
scale = Vector2(1.2, 1.2)
sprite_frames = SubResource("SpriteFrames_a7q1i")

View File

@@ -0,0 +1,341 @@
[gd_scene load_steps=3 format=3 uid="uid://bhh0m5tbxjfmy"]
[ext_resource type="PackedScene" uid="uid://dn3jp4r8ij3rf" path="res://sockets/ems_g/socket_status.tscn" id="2_fbuee"]
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_deko5"]
[node name="ЕМС-Г" type="Panel" groups=["ems_g"]]
offset_top = -8.0
offset_bottom = -8.0
theme_override_styles/panel = SubResource("StyleBoxTexture_deko5")
metadata/_tab_index = 3
metadata/device = "ПРД-В1"
[node name="output" type="GridContainer" parent="."]
layout_mode = 2
offset_left = 161.0
offset_top = 15.0
offset_right = 1021.0
offset_bottom = 77.0
columns = 16
[node name="Label" type="Label" parent="output"]
layout_mode = 2
text = "СИ1"
[node name="Label2" type="Label" parent="output"]
layout_mode = 2
text = "СИ2"
[node name="Label3" type="Label" parent="output"]
layout_mode = 2
text = "СИ3"
[node name="Label4" type="Label" parent="output"]
layout_mode = 2
text = "Мод1"
[node name="Label5" type="Label" parent="output"]
layout_mode = 2
text = "Мод2"
[node name="Label6" type="Label" parent="output"]
layout_mode = 2
text = "Мод3"
[node name="Label7" type="Label" parent="output"]
layout_mode = 2
[node name="Label8" type="Label" parent="output"]
layout_mode = 2
[node name="Label9" type="Label" parent="output"]
layout_mode = 2
text = "ФГОЗ"
[node name="Label10" type="Label" parent="output"]
layout_mode = 2
text = "ФГОЗ"
[node name="Label11" type="Label" parent="output"]
layout_mode = 2
text = "МодМ"
[node name="Label12" type="Label" parent="output"]
layout_mode = 2
[node name="Label13" type="Label" parent="output"]
layout_mode = 2
[node name="Label14" type="Label" parent="output"]
layout_mode = 2
[node name="Label15" type="Label" parent="output"]
layout_mode = 2
[node name="Label16" type="Label" parent="output"]
layout_mode = 2
[node name="PanelContainer0" parent="output" groups=["output"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 0
metadata/device = "ПРД-В"
[node name="PanelContainer1" parent="output" groups=["output"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 1
metadata/device = "ПРД-В"
[node name="PanelContainer2" parent="output" groups=["output"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 2
metadata/device = "ПРД-В"
[node name="PanelContainer3" parent="output" groups=["output"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 3
metadata/device = "ПРД-В"
[node name="PanelContainer4" parent="output" groups=["output"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 4
metadata/device = "ПРД-В"
[node name="PanelContainer5" parent="output" groups=["output"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 5
metadata/device = "ПРД-В"
[node name="PanelContainer6" parent="output" groups=["output"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 6
metadata/device = "ПРД-В"
[node name="PanelContainer7" parent="output" groups=["output"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 7
metadata/device = "ПРД-В"
[node name="PanelContainer8" parent="output" groups=["output"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 8
metadata/device = "ПРД-В"
[node name="PanelContainer9" parent="output" groups=["output"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 9
metadata/device = "ПРД-В"
[node name="PanelContainer10" parent="output" groups=["output"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 10
metadata/device = "ПРД-В"
[node name="PanelContainer11" parent="output" groups=["output"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 11
metadata/device = "ПРД-В"
[node name="PanelContainer12" parent="output" groups=["output"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 12
metadata/device = "ПРД-В"
[node name="PanelContainer13" parent="output" groups=["output"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 13
metadata/device = "ПРД-В"
[node name="PanelContainer14" parent="output" groups=["output"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 14
metadata/device = "ПРД-В"
[node name="PanelContainer15" parent="output" groups=["output"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 15
metadata/device = "ПРД-В"
[node name="input" type="GridContainer" parent="."]
layout_mode = 2
offset_left = 9.0
offset_top = 83.0
offset_right = 155.0
offset_bottom = 703.0
columns = 2
[node name="Label" type="Label" parent="input"]
layout_mode = 2
text = "СЗИ1 от УФ"
[node name="PanelContainer0" parent="input" groups=["input"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 0
metadata/device = "ПРД-В"
[node name="Label2" type="Label" parent="input"]
layout_mode = 2
text = "СЗИ2 от УФ"
[node name="PanelContainer1" parent="input" groups=["input"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 1
metadata/device = "ПРД-В"
[node name="Label3" type="Label" parent="input"]
layout_mode = 2
text = "СЗИ3 от УФ"
[node name="PanelContainer2" parent="input" groups=["input"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 2
metadata/device = "ПРД-В"
[node name="Label4" type="Label" parent="input"]
layout_mode = 2
text = "СЗИ4 от УФ"
[node name="PanelContainer3" parent="input" groups=["input"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 3
metadata/device = "ПРД-В"
[node name="Label5" type="Label" parent="input"]
layout_mode = 2
text = "СЗИ5 от УФ"
[node name="PanelContainer4" parent="input" groups=["input"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 4
metadata/device = "ПРД-В"
[node name="Label6" type="Label" parent="input"]
layout_mode = 2
text = "СЗИ6 от УФ"
[node name="PanelContainer5" parent="input" groups=["input"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 5
metadata/device = "ПРД-В"
[node name="Label7" type="Label" parent="input"]
layout_mode = 2
text = "СЗИ7 от УФ"
[node name="PanelContainer6" parent="input" groups=["input"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 6
metadata/device = "ПРД-В"
[node name="Label8" type="Label" parent="input"]
layout_mode = 2
text = "Х"
[node name="PanelContainer7" parent="input" groups=["input"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 7
metadata/device = "ПРД-В"
[node name="Label9" type="Label" parent="input"]
layout_mode = 2
text = "СИ1 от ФС"
[node name="PanelContainer8" parent="input" groups=["input"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 8
metadata/device = "ПРД-В"
[node name="Label10" type="Label" parent="input"]
layout_mode = 2
text = "СИ2 от ФС"
[node name="PanelContainer9" parent="input" groups=["input"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 9
metadata/device = "ПРД-В"
[node name="Label11" type="Label" parent="input"]
layout_mode = 2
text = "СИ3 от ФС"
[node name="PanelContainer10" parent="input" groups=["input"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 10
metadata/device = "ПРД-В"
[node name="Label12" type="Label" parent="input"]
layout_mode = 2
text = "СИ4 от ФС"
[node name="PanelContainer11" parent="input" groups=["input"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 11
metadata/device = "ПРД-В"
[node name="Label13" type="Label" parent="input"]
layout_mode = 2
text = "СИ5 от ФС"
[node name="PanelContainer12" parent="input" groups=["input"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 12
metadata/device = "ПРД-В"
[node name="Label14" type="Label" parent="input"]
layout_mode = 2
text = "СИ6 от ФС"
[node name="PanelContainer13" parent="input" groups=["input"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 13
metadata/device = "ПРД-В"
[node name="Label15" type="Label" parent="input"]
layout_mode = 2
text = "СИ7 от ФС"
[node name="PanelContainer14" parent="input" groups=["input"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 14
metadata/device = "ПРД-В"
[node name="Label16" type="Label" parent="input"]
layout_mode = 2
text = "Х"
[node name="PanelContainer15" parent="input" groups=["input"] instance=ExtResource("2_fbuee")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 15
metadata/device = "ПРД-В"

View File

@@ -0,0 +1,342 @@
[gd_scene load_steps=4 format=3 uid="uid://pq47qp2lye4o"]
[ext_resource type="Script" uid="uid://jyoti1htitwg" path="res://sockets/ems_g/емс_г_к.gd" id="1_rhd7l"]
[ext_resource type="PackedScene" uid="uid://dn3jp4r8ij3rf" path="res://sockets/ems_g/socket_status.tscn" id="2_0kk21"]
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_deko5"]
[node name="ЕМС-Г" type="Panel" groups=["ems_g"]]
visible = false
theme_override_styles/panel = SubResource("StyleBoxTexture_deko5")
script = ExtResource("1_rhd7l")
metadata/_tab_index = 4
metadata/device = "ПРД-К1"
[node name="output" type="GridContainer" parent="."]
layout_mode = 2
offset_left = 161.0
offset_top = 15.0
offset_right = 1021.0
offset_bottom = 77.0
columns = 16
[node name="Label" type="Label" parent="output"]
layout_mode = 2
text = "СИ1"
[node name="Label2" type="Label" parent="output"]
layout_mode = 2
text = "СИ2"
[node name="Label3" type="Label" parent="output"]
layout_mode = 2
text = "СИ3"
[node name="Label4" type="Label" parent="output"]
layout_mode = 2
text = "Мод1"
[node name="Label5" type="Label" parent="output"]
layout_mode = 2
text = "Мод2"
[node name="Label6" type="Label" parent="output"]
layout_mode = 2
text = "Мод3"
[node name="Label7" type="Label" parent="output"]
layout_mode = 2
[node name="Label8" type="Label" parent="output"]
layout_mode = 2
[node name="Label9" type="Label" parent="output"]
layout_mode = 2
text = "ФГОЗ"
[node name="Label10" type="Label" parent="output"]
layout_mode = 2
text = "ФГОЗ"
[node name="Label11" type="Label" parent="output"]
layout_mode = 2
text = "МодМ"
[node name="Label12" type="Label" parent="output"]
layout_mode = 2
[node name="Label13" type="Label" parent="output"]
layout_mode = 2
[node name="Label14" type="Label" parent="output"]
layout_mode = 2
[node name="Label15" type="Label" parent="output"]
layout_mode = 2
[node name="Label16" type="Label" parent="output"]
layout_mode = 2
[node name="PanelContainer0" parent="output" groups=["output"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 0
metadata/device = "ПРД-К"
[node name="PanelContainer1" parent="output" groups=["output"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 1
metadata/device = "ПРД-К"
[node name="PanelContainer2" parent="output" groups=["output"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 2
metadata/device = "ПРД-К"
[node name="PanelContainer3" parent="output" groups=["output"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 3
metadata/device = "ПРД-К"
[node name="PanelContainer4" parent="output" groups=["output"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 4
metadata/device = "ПРД-К"
[node name="PanelContainer5" parent="output" groups=["output"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 5
metadata/device = "ПРД-К"
[node name="PanelContainer6" parent="output" groups=["output"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 6
metadata/device = "ПРД-К"
[node name="PanelContainer7" parent="output" groups=["output"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 7
metadata/device = "ПРД-К"
[node name="PanelContainer8" parent="output" groups=["output"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 8
metadata/device = "ПРД-К"
[node name="PanelContainer9" parent="output" groups=["output"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 9
metadata/device = "ПРД-К"
[node name="PanelContainer10" parent="output" groups=["output"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 10
metadata/device = "ПРД-К"
[node name="PanelContainer11" parent="output" groups=["output"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 11
metadata/device = "ПРД-К"
[node name="PanelContainer12" parent="output" groups=["output"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 12
metadata/device = "ПРД-К"
[node name="PanelContainer13" parent="output" groups=["output"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 13
metadata/device = "ПРД-К"
[node name="PanelContainer14" parent="output" groups=["output"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 14
metadata/device = "ПРД-К"
[node name="PanelContainer15" parent="output" groups=["output"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 15
metadata/device = "ПРД-К"
[node name="input" type="GridContainer" parent="."]
layout_mode = 2
offset_left = 9.0
offset_top = 83.0
offset_right = 155.0
offset_bottom = 703.0
columns = 2
[node name="Label" type="Label" parent="input"]
layout_mode = 2
text = "СЗИ1 от УФ"
[node name="PanelContainer0" parent="input" groups=["input"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 0
metadata/device = "ПРД-К"
[node name="Label2" type="Label" parent="input"]
layout_mode = 2
text = "СЗИ2 от УФ"
[node name="PanelContainer1" parent="input" groups=["input"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 1
metadata/device = "ПРД-К"
[node name="Label3" type="Label" parent="input"]
layout_mode = 2
text = "СЗИ3 от УФ"
[node name="PanelContainer2" parent="input" groups=["input"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 2
metadata/device = "ПРД-К"
[node name="Label4" type="Label" parent="input"]
layout_mode = 2
text = "СЗИ4 от УФ"
[node name="PanelContainer3" parent="input" groups=["input"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 3
metadata/device = "ПРД-К"
[node name="Label5" type="Label" parent="input"]
layout_mode = 2
text = "СЗИ5 от УФ"
[node name="PanelContainer4" parent="input" groups=["input"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 4
metadata/device = "ПРД-К"
[node name="Label6" type="Label" parent="input"]
layout_mode = 2
text = "СЗИ6 от УФ"
[node name="PanelContainer5" parent="input" groups=["input"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 5
metadata/device = "ПРД-К"
[node name="Label7" type="Label" parent="input"]
layout_mode = 2
text = "СЗИ7 от УФ"
[node name="PanelContainer6" parent="input" groups=["input"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 6
metadata/device = "ПРД-К"
[node name="Label8" type="Label" parent="input"]
layout_mode = 2
text = "Х"
[node name="PanelContainer7" parent="input" groups=["input"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 7
metadata/device = "ПРД-К"
[node name="Label9" type="Label" parent="input"]
layout_mode = 2
text = "СИ1 от ФС"
[node name="PanelContainer8" parent="input" groups=["input"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 8
metadata/device = "ПРД-К"
[node name="Label10" type="Label" parent="input"]
layout_mode = 2
text = "СИ2 от ФС"
[node name="PanelContainer9" parent="input" groups=["input"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 9
metadata/device = "ПРД-К"
[node name="Label11" type="Label" parent="input"]
layout_mode = 2
text = "СИ3 от ФС"
[node name="PanelContainer10" parent="input" groups=["input"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 10
metadata/device = "ПРД-К"
[node name="Label12" type="Label" parent="input"]
layout_mode = 2
text = "СИ4 от ФС"
[node name="PanelContainer11" parent="input" groups=["input"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 11
metadata/device = "ПРД-К"
[node name="Label13" type="Label" parent="input"]
layout_mode = 2
text = "СИ5 от ФС"
[node name="PanelContainer12" parent="input" groups=["input"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 12
metadata/device = "ПРД-К"
[node name="Label14" type="Label" parent="input"]
layout_mode = 2
text = "СИ6 от ФС"
[node name="PanelContainer13" parent="input" groups=["input"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 13
metadata/device = "ПРД-К"
[node name="Label15" type="Label" parent="input"]
layout_mode = 2
text = "СИ7 от ФС"
[node name="PanelContainer14" parent="input" groups=["input"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 14
metadata/device = "ПРД-К"
[node name="Label16" type="Label" parent="input"]
layout_mode = 2
text = "Х"
[node name="PanelContainer15" parent="input" groups=["input"] instance=ExtResource("2_0kk21")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 15
metadata/device = "ПРД-К"

View File

@@ -0,0 +1,341 @@
[gd_scene load_steps=3 format=3 uid="uid://2rjgbflmbi7e"]
[ext_resource type="PackedScene" uid="uid://dn3jp4r8ij3rf" path="res://sockets/ems_g/socket_status.tscn" id="1_deko5"]
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_deko5"]
[node name="ЕМС-Г" type="Panel"]
offset_top = -9.0
offset_right = 789.0
offset_bottom = 386.0
theme_override_styles/panel = SubResource("StyleBoxTexture_deko5")
metadata/_tab_index = 3
[node name="output" type="GridContainer" parent="."]
layout_mode = 2
offset_left = 161.0
offset_top = 15.0
offset_right = 1021.0
offset_bottom = 77.0
columns = 16
[node name="Label" type="Label" parent="output"]
layout_mode = 2
text = "СИ1"
[node name="Label2" type="Label" parent="output"]
layout_mode = 2
text = "СИ2"
[node name="Label3" type="Label" parent="output"]
layout_mode = 2
text = "СИ3"
[node name="Label4" type="Label" parent="output"]
layout_mode = 2
text = "Мод1"
[node name="Label5" type="Label" parent="output"]
layout_mode = 2
text = "Мод2"
[node name="Label6" type="Label" parent="output"]
layout_mode = 2
text = "Мод3"
[node name="Label7" type="Label" parent="output"]
layout_mode = 2
[node name="Label8" type="Label" parent="output"]
layout_mode = 2
[node name="Label9" type="Label" parent="output"]
layout_mode = 2
text = "ФГОЗ"
[node name="Label10" type="Label" parent="output"]
layout_mode = 2
text = "ФГОЗ"
[node name="Label11" type="Label" parent="output"]
layout_mode = 2
text = "МодМ"
[node name="Label12" type="Label" parent="output"]
layout_mode = 2
[node name="Label13" type="Label" parent="output"]
layout_mode = 2
[node name="Label14" type="Label" parent="output"]
layout_mode = 2
[node name="Label15" type="Label" parent="output"]
layout_mode = 2
[node name="Label16" type="Label" parent="output"]
layout_mode = 2
[node name="PanelContainer0" parent="output" groups=["output"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 0
metadata/device = "ПРД-Н"
[node name="PanelContainer1" parent="output" groups=["output"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 1
metadata/device = "ПРД-Н"
[node name="PanelContainer2" parent="output" groups=["output"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 2
metadata/device = "ПРД-Н"
[node name="PanelContainer3" parent="output" groups=["output"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 3
metadata/device = "ПРД-Н"
[node name="PanelContainer4" parent="output" groups=["output"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 4
metadata/device = "ПРД-Н"
[node name="PanelContainer5" parent="output" groups=["output"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 5
metadata/device = "ПРД-Н"
[node name="PanelContainer6" parent="output" groups=["output"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 6
metadata/device = "ПРД-Н"
[node name="PanelContainer7" parent="output" groups=["output"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 7
metadata/device = "ПРД-Н"
[node name="PanelContainer8" parent="output" groups=["output"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 8
metadata/device = "ПРД-Н"
[node name="PanelContainer9" parent="output" groups=["output"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 9
metadata/device = "ПРД-Н"
[node name="PanelContainer10" parent="output" groups=["output"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 10
metadata/device = "ПРД-Н"
[node name="PanelContainer11" parent="output" groups=["output"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 11
metadata/device = "ПРД-Н"
[node name="PanelContainer12" parent="output" groups=["output"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 12
metadata/device = "ПРД-Н"
[node name="PanelContainer13" parent="output" groups=["output"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 13
metadata/device = "ПРД-Н"
[node name="PanelContainer14" parent="output" groups=["output"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 14
metadata/device = "ПРД-Н"
[node name="PanelContainer15" parent="output" groups=["output"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 15
metadata/device = "ПРД-Н"
[node name="input" type="GridContainer" parent="."]
layout_mode = 2
offset_left = 9.0
offset_top = 83.0
offset_right = 155.0
offset_bottom = 703.0
columns = 2
[node name="Label" type="Label" parent="input"]
layout_mode = 2
text = "СЗИ1 от УФ"
[node name="PanelContainer0" parent="input" groups=["input"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 0
metadata/device = "ПРД-Н"
[node name="Label2" type="Label" parent="input"]
layout_mode = 2
text = "СЗИ2 от УФ"
[node name="PanelContainer1" parent="input" groups=["input"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 1
metadata/device = "ПРД-Н"
[node name="Label3" type="Label" parent="input"]
layout_mode = 2
text = "СЗИ3 от УФ"
[node name="PanelContainer2" parent="input" groups=["input"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 2
metadata/device = "ПРД-Н"
[node name="Label4" type="Label" parent="input"]
layout_mode = 2
text = "СЗИ4 от УФ"
[node name="PanelContainer3" parent="input" groups=["input"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 3
metadata/device = "ПРД-Н"
[node name="Label5" type="Label" parent="input"]
layout_mode = 2
text = "СЗИ5 от УФ"
[node name="PanelContainer4" parent="input" groups=["input"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 4
metadata/device = "ПРД-Н"
[node name="Label6" type="Label" parent="input"]
layout_mode = 2
text = "СЗИ6 от УФ"
[node name="PanelContainer5" parent="input" groups=["input"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 5
metadata/device = "ПРД-Н"
[node name="Label7" type="Label" parent="input"]
layout_mode = 2
text = "СЗИ7 от УФ"
[node name="PanelContainer6" parent="input" groups=["input"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 6
metadata/device = "ПРД-Н"
[node name="Label8" type="Label" parent="input"]
layout_mode = 2
text = "Х"
[node name="PanelContainer7" parent="input" groups=["input"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 7
metadata/device = "ПРД-Н"
[node name="Label9" type="Label" parent="input"]
layout_mode = 2
text = "СИ1 от ФС"
[node name="PanelContainer8" parent="input" groups=["input"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 8
metadata/device = "ПРД-Н"
[node name="Label10" type="Label" parent="input"]
layout_mode = 2
text = "СИ2 от ФС"
[node name="PanelContainer9" parent="input" groups=["input"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 9
metadata/device = "ПРД-Н"
[node name="Label11" type="Label" parent="input"]
layout_mode = 2
text = "СИ3 от ФС"
[node name="PanelContainer10" parent="input" groups=["input"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 10
metadata/device = "ПРД-Н"
[node name="Label12" type="Label" parent="input"]
layout_mode = 2
text = "СИ4 от ФС"
[node name="PanelContainer11" parent="input" groups=["input"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 11
metadata/device = "ПРД-Н"
[node name="Label13" type="Label" parent="input"]
layout_mode = 2
text = "СИ5 от ФС"
[node name="PanelContainer12" parent="input" groups=["input"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 12
metadata/device = "ПРД-Н"
[node name="Label14" type="Label" parent="input"]
layout_mode = 2
text = "СИ6 от ФС"
[node name="PanelContainer13" parent="input" groups=["input"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 13
metadata/device = "ПРД-Н"
[node name="Label15" type="Label" parent="input"]
layout_mode = 2
text = "СИ7 от ФС"
[node name="PanelContainer14" parent="input" groups=["input"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 14
metadata/device = "ПРД-Н"
[node name="Label16" type="Label" parent="input"]
layout_mode = 2
text = "Х"
[node name="PanelContainer15" parent="input" groups=["input"] instance=ExtResource("1_deko5")]
layout_mode = 2
mouse_default_cursor_shape = 2
metadata/bit = 15
metadata/device = "ПРД-Н"

View File

@@ -0,0 +1,96 @@
extends Panel
@onready var output_status = $output
# Константы состояний фреймов
const DEVICE: String = 'ПРД-В'
const FRAME_OFF := 0
const FRAME_CONST := 1
const FRAME_IMPULSE := 2
var _input_impulse: int = 0
var _input_const: int = 0
var _output_impulse: int = 0
var _output_const: int = 0
#region Сигналы для уведомления об изменениях
signal input_bits_updated(input_impulse: int, input_const: int)
signal output_bits_updated(output_impulse: int, output_const: int)
signal bits_changed(input_impulse: int, input_const: int, output_impulse: int, output_const: int)
#endregion
func _ready():
_setup_connections()
func _setup_connections():
var _all_input_nodes = get_tree().get_nodes_in_group('input')
var _all_outputs_nodes= get_tree().get_nodes_in_group('output')
var _filtered_inputs = _all_input_nodes.filter(
func(node): return node.get_meta('device') == DEVICE)
var _filtered_outputs = _all_outputs_nodes.filter(
func(node): return node.get_meta('device') == DEVICE)
for node in _filtered_inputs:
if node.has_signal('update_frame'):
node.update_frame.connect(on_update_frame_input)
for node in _filtered_outputs:
if node.has_signal('update_frame'):
node.update_frame.connect(on_update_frame_output)
func on_update_frame_input(bit: int, frame: int)-> void:
var mask = 1 << bit
var old_impulse = _input_impulse
var old_const = _input_const
_input_impulse = _input_impulse & ~mask | (mask if frame == FRAME_IMPULSE else 0)
_input_const = _input_const & ~mask | (mask if frame == FRAME_CONST else 0)
# Отправляем сигналы только если значения изменились
if _input_impulse != old_impulse || _input_const != old_const:
input_bits_updated.emit(get_input_impulse(), get_input_const())
bits_changed.emit(get_input_impulse(), get_input_const(), get_output_impulse(), get_output_const())
func on_update_frame_output(bit: int, frame: int)-> void:
var mask = 1 << bit
var old_impulse = _output_impulse
var old_const = _output_const
_output_impulse = _output_impulse & ~mask | (mask if frame == FRAME_IMPULSE else 0)
_output_const = _output_const & ~mask | (mask if frame == FRAME_CONST else 0)
# Отправляем сигналы только если значения изменились
if _output_impulse != old_impulse || _output_const != old_const:
output_bits_updated.emit(get_output_impulse(), get_output_const())
bits_changed.emit(get_input_impulse(), get_input_const(), get_output_impulse(), get_output_const())
func _invert_bits(value: int) -> int:
'''Инвертирует все биты (0 становится 0xFFFF, 0xFFFF становится 0)'''
return value ^ 0xFFFF
#region Геттеры для безопасного доступа к данным
func get_input_impulse() -> int:
return _input_impulse
func get_input_const() -> int:
return _input_const
func get_output_impulse() -> int:
return _output_impulse
func get_output_const() -> int:
return _output_const
#endregion
#region Функция для получения всех данных сразу
func get_all_bits() -> Dictionary:
return {
'input_impulse': _input_impulse,
'input_const': _input_const,
'output_impulse': _output_impulse,
'output_const': _output_const
}
#endregion

View File

@@ -0,0 +1 @@
uid://cgtvbvw1xfqay

View File

@@ -0,0 +1,96 @@
extends Panel
@onready var output_status = $output
# Константы состояний фреймов
const DEVICE: String = 'ПРД-К'
const FRAME_OFF := 0
const FRAME_CONST := 1
const FRAME_IMPULSE := 2
var _input_impulse: int = 0
var _input_const: int = 0
var _output_impulse: int = 0
var _output_const: int = 0
#region Сигналы для уведомления об изменениях
signal input_bits_updated(input_impulse: int, input_const: int)
signal output_bits_updated(output_impulse: int, output_const: int)
signal bits_changed(input_impulse: int, input_const: int, output_impulse: int, output_const: int)
#endregion
func _ready():
_setup_connections()
func _setup_connections():
var _all_input_nodes = get_tree().get_nodes_in_group('input')
var _all_outputs_nodes= get_tree().get_nodes_in_group('output')
var _filtered_inputs = _all_input_nodes.filter(
func(node): return node.get_meta('device') == DEVICE)
var _filtered_outputs = _all_outputs_nodes.filter(
func(node): return node.get_meta('device') == DEVICE)
for node in _filtered_inputs:
if node.has_signal('update_frame'):
node.update_frame.connect(on_update_frame_input)
for node in _filtered_outputs:
if node.has_signal('update_frame'):
node.update_frame.connect(on_update_frame_output)
func on_update_frame_input(bit: int, frame: int)-> void:
var mask = 1 << bit
var old_impulse = _input_impulse
var old_const = _input_const
_input_impulse = _input_impulse & ~mask | (mask if frame == FRAME_IMPULSE else 0)
_input_const = _input_const & ~mask | (mask if frame == FRAME_CONST else 0)
# Отправляем сигналы только если значения изменились
if _input_impulse != old_impulse || _input_const != old_const:
input_bits_updated.emit(get_input_impulse(), get_input_const())
bits_changed.emit(get_input_impulse(), get_input_const(), get_output_impulse(), get_output_const())
func on_update_frame_output(bit: int, frame: int)-> void:
var mask = 1 << bit
var old_impulse = _output_impulse
var old_const = _output_const
_output_impulse = _output_impulse & ~mask | (mask if frame == FRAME_IMPULSE else 0)
_output_const = _output_const & ~mask | (mask if frame == FRAME_CONST else 0)
# Отправляем сигналы только если значения изменились
if _output_impulse != old_impulse || _output_const != old_const:
output_bits_updated.emit(get_output_impulse(), get_output_const())
bits_changed.emit(get_input_impulse(), get_input_const(), get_output_impulse(), get_output_const())
func _invert_bits(value: int) -> int:
'''Инвертирует все биты (0 становится 0xFFFF, 0xFFFF становится 0)'''
return value ^ 0xFFFF
#region Геттеры для безопасного доступа к данным
func get_input_impulse() -> int:
return _input_impulse
func get_input_const() -> int:
return _input_const
func get_output_impulse() -> int:
return _output_impulse
func get_output_const() -> int:
return _output_const
#endregion
#region Функция для получения всех данных сразу
func get_all_bits() -> Dictionary:
return {
'input_impulse': _input_impulse,
'input_const': _input_const,
'output_impulse': _output_impulse,
'output_const': _output_const
}
#endregion

View File

@@ -0,0 +1 @@
uid://jyoti1htitwg

View File

@@ -0,0 +1,96 @@
extends Panel
@onready var output_status = $output
# Константы состояний фреймов
const DEVICE: String = 'ПРД-Н'
const FRAME_OFF := 0
const FRAME_CONST := 1
const FRAME_IMPULSE := 2
var _input_impulse: int = 0
var _input_const: int = 0
var _output_impulse: int = 0
var _output_const: int = 0
#region Сигналы для уведомления об изменениях
signal input_bits_updated(input_impulse: int, input_const: int)
signal output_bits_updated(output_impulse: int, output_const: int)
signal bits_changed(input_impulse: int, input_const: int, output_impulse: int, output_const: int)
#endregion
func _ready():
_setup_connections()
func _setup_connections():
var _all_input_nodes = get_tree().get_nodes_in_group('input')
var _all_outputs_nodes= get_tree().get_nodes_in_group('output')
var _filtered_inputs = _all_input_nodes.filter(
func(node): return node.get_meta('device') == DEVICE)
var _filtered_outputs = _all_outputs_nodes.filter(
func(node): return node.get_meta('device') == DEVICE)
for node in _filtered_inputs:
if node.has_signal('update_frame'):
node.update_frame.connect(on_update_frame_input)
for node in _filtered_outputs:
if node.has_signal('update_frame'):
node.update_frame.connect(on_update_frame_output)
func on_update_frame_input(bit: int, frame: int)-> void:
var mask = 1 << bit
var old_impulse = _input_impulse
var old_const = _input_const
_input_impulse = _input_impulse & ~mask | (mask if frame == FRAME_IMPULSE else 0)
_input_const = _input_const & ~mask | (mask if frame == FRAME_CONST else 0)
# Отправляем сигналы только если значения изменились
if _input_impulse != old_impulse || _input_const != old_const:
input_bits_updated.emit(get_input_impulse(), get_input_const())
bits_changed.emit(get_input_impulse(), get_input_const(), get_output_impulse(), get_output_const())
func on_update_frame_output(bit: int, frame: int)-> void:
var mask = 1 << bit
var old_impulse = _output_impulse
var old_const = _output_const
_output_impulse = _output_impulse & ~mask | (mask if frame == FRAME_IMPULSE else 0)
_output_const = _output_const & ~mask | (mask if frame == FRAME_CONST else 0)
# Отправляем сигналы только если значения изменились
if _output_impulse != old_impulse || _output_const != old_const:
output_bits_updated.emit(get_output_impulse(), get_output_const())
bits_changed.emit(get_input_impulse(), get_input_const(), get_output_impulse(), get_output_const())
func _invert_bits(value: int) -> int:
'''Инвертирует все биты (0 становится 0xFFFF, 0xFFFF становится 0)'''
return value ^ 0xFFFF
#region Геттеры для безопасного доступа к данным
func get_input_impulse() -> int:
return _input_impulse
func get_input_const() -> int:
return _input_const
func get_output_impulse() -> int:
return _output_impulse
func get_output_const() -> int:
return _output_const
#endregion
#region Функция для получения всех данных сразу
func get_all_bits() -> Dictionary:
return {
'input_impulse': _input_impulse,
'input_const': _input_const,
'output_impulse': _output_impulse,
'output_const': _output_const
}
#endregion

View File

@@ -0,0 +1 @@
uid://b3hcliukdo2wm

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

View File

@@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://snpqhalopnp"
path="res://.godot/imported/эмс-бланк-перем.png-91f487c2526e52fdad35ee87d52f0d71.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://sockets/ems_g/эмс-бланк-перем.png"
dest_files=["res://.godot/imported/эмс-бланк-перем.png-91f487c2526e52fdad35ee87d52f0d71.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 B

View File

@@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://sx6xcpus2yf"
path="res://.godot/imported/эмс-бланк-пост.png-7c8eb3ac5e6dec0cbe7af0e932cdf19f.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://sockets/ems_g/эмс-бланк-пост.png"
dest_files=["res://.godot/imported/эмс-бланк-пост.png-7c8eb3ac5e6dec0cbe7af0e932cdf19f.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 787 B

View File

@@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cwabmu3cljbpr"
path="res://.godot/imported/эмс-бланк.png-15fb9a2c8256ce49f20eda63d19d1abc.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://sockets/ems_g/эмс-бланк.png"
dest_files=["res://.godot/imported/эмс-бланк.png-15fb9a2c8256ce49f20eda63d19d1abc.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View File

@@ -1,143 +1,638 @@
class_name UDPBroadcast
const addres_list: Dictionary = {
'ПРД-Н1': [50011, "10.1.1.11"],
'ПРД-В1': [50012, "10.1.1.12"],
'ПРД-К1': [50013, "10.1.1.13"],
'ПРД-Н2': [50021, "10.1.1.21"],
'ПРД-В2': [50022, "10.1.1.22"],
'ПРД-К2': [50023, "10.1.1.23"],
'ПРД-Н3': [50031, "10.1.1.31"],
'ПРД-В3': [50032, "10.1.1.32"],
'ПРД-К3': [50033, "10.1.1.33"],
'ПРД-Н4': [50041, "10.1.1.41"],
'ПРД-В4': [50042, "10.1.1.42"],
'ПРД-К4': [50043, "10.1.1.43"],
# Константы для размеров данных и смещений
const STATUS_PACKET_SIZE: int = 103
const HEADER_SIZE: int = 8
const EMS_G_DATA_OFFSET: int = 9
const EMS_G_DATA_SIZE: int = 26 # 13 * 2 байта
const UG_DATA_OFFSET: int = 35
const UKP0_DATA_OFFSET: int = 39
const UKP1_DATA_OFFSET: int = 71
const UKP_DATA_SIZE: int = 32
# Константы команд ISA
const CMD_READ_ISA: int = 0
const CMD_WRITE_ISA: int = 1
const CMD_FLAG_EMS_G: int = 70
# Минимальный размер пакета для валидации
const MIN_PACKET_SIZE: int = 7
# Словарь адресов устройств
const ADDRESS_LIST: Dictionary = {
'ПРД-Н1': [50011, '10.1.1.11'],
'ПРД-В1': [50012, '10.1.1.12'],
'ПРД-К1': [50013, '10.1.1.13'],
'ПРД-Н2': [50021, '10.1.1.21'],
'ПРД-В2': [50022, '10.1.1.22'],
'ПРД-К2': [50023, '10.1.1.23'],
'ПРД-Н3': [50031, '10.1.1.31'],
'ПРД-В3': [50032, '10.1.1.32'],
'ПРД-К3': [50033, '10.1.1.33'],
'ПРД-Н4': [50041, '10.1.1.41'],
'ПРД-В4': [50042, '10.1.1.42'],
'ПРД-К4': [50043, '10.1.1.43'],
}
const broadcast_addr: Array = [50000, "10.1.1.255"]
#const bind_addr: Array = [50_000, "10.1.1.70"] TODO: Порт по умолчанию
# Широковещательный адрес
const BROADCAST_ADDR: Array = [50000, '10.1.1.255']
# Приватные свойства
var status_manager: StatusManager
var logs_manager: LogsRich
var _socket: SocketUDP
var self_name: String
var _self_name: String
var _isa_ports: Dictionary = {}
var _written_isa_ports: Array = []
var _timer: Timer
var sockets_status_bit: int = 0
var ukp0_data: PackedByteArray:
set(v):
ukp0_data = v
print('Произошло Обновление данных ukp0_data')
var _sockets_status_bit: int = 0
var _dkm_status_bit: int = 0
var _dry_contact_status_bit: int = 0xFF
var _cmd_number_count: int = 0
var _last_received_command: int = -1
var _input_impulse: int = 0xffff
var _input_const: int = 0xffff
var _output_impulse: int = 0xffff
var _output_const: int = 0xffff
var ukp0_data: PackedByteArray
var ukp1_data: PackedByteArray
var ukp1_data: PackedByteArray:
set(v):
ukp1_data = v
print('Произошло Обновление данных ukp1_data')
signal update_isa_ports(_isa_ports: Dictionary)
signal update_socket_status(_sockets_status_bit: int)
func _init(name: String):
self_name = name
_socket = SocketUDP.new()
_timer = Timer.new()
_timer.wait_time = 1.0 / 3.0
_timer.timeout.connect(_on_timer_timeout)
ukp0_data.resize(32)
ukp0_data.fill(0)
ukp1_data.resize(32)
ukp1_data.fill(0)
func _init(name: String, logs_node: RichTextLabel):
'''Инициализация UDP broadcast клиента'''
status_manager = StatusManager.new(self)
_self_name = name
initialize_logs_manager(logs_node)
_initialize_socket()
_initialize_ukp_data()
_initialize_timers()
_initialize_isa_ports()
if addres_list.has(name):
var port: int = addres_list[name][0]
var addr: String = addres_list[name][1]
bind_unit_addres(port, addr)
else:
push_error("Неизвестное устройство: %s" % name)
if not _bind_to_device_address(name):
push_error('Не удалось инициализировать устройство: %s' % name)
func bind_unit_addres(port_for_bind: int, addr_for_bind: String) -> void:
var bind_result = _socket.bind(port_for_bind, addr_for_bind)
func initialize_logs_manager(logs_node: LogsRich) -> void:
'''Инициализация менеджера логов'''
logs_manager = logs_node
logs_manager.info("Логирование инициализировано для %s" % _self_name)
func _initialize_socket() -> void:
'''Инициализация UDP сокета'''
_socket = SocketUDP.new()
if logs_manager:
logs_manager.debug("UDP сокет инициализирован")
func _initialize_timers() -> void:
'''Инициализация таймеров'''
# Таймер для широковещательной рассылки (3 раза в секунду)
_timer = Timer.new()
_timer.wait_time = 1.0 / 5.0
_timer.timeout.connect(_on_broadcast_timeout)
if logs_manager:
logs_manager.debug("Таймеры инициализированы")
func _initialize_ukp_data() -> void:
'''Инициализация данных UKP нулевыми значениями'''
ukp0_data = PackedByteArray()
ukp0_data.resize(UKP_DATA_SIZE)
ukp0_data.fill(0)
ukp1_data = PackedByteArray()
ukp1_data.resize(UKP_DATA_SIZE)
ukp1_data.fill(0)
if logs_manager:
logs_manager.debug("Данные UKP инициализированы")
func _initialize_isa_ports() -> void:
'''Инициализация ISA портов начальными значениями'''
# Инициализируем порты, которые могут запрашиваться
set_isa_port(0x0106, 0) # DOU2 статус
set_isa_port(0x0108, 0) # DOU3 статус
set_isa_port(0x010A, 0) # ATT
if logs_manager:
logs_manager.debug("ISA порты инициализированы")
func _bind_to_device_address(device_name: String) -> bool:
'''Привязка к адресу устройства по его имени'''
if not ADDRESS_LIST.has(device_name):
_log_error('Неизвестное устройство: %s' % device_name)
return false
var port: int = ADDRESS_LIST[device_name][0]
var address: String = ADDRESS_LIST[device_name][1]
return bind_unit_address(port, address)
func bind_unit_address(port: int, address: String) -> bool:
'''Привязка сокета к указанному порту и адресу'''
if port <= 0 or port > 65535:
_log_error('Некорректный номер порта: %d' % port)
return false
var bind_result = _socket.bind(port, address)
if bind_result != OK:
push_error("Не удалось привязаться к %s: %s" % [addr_for_bind, error_string(bind_result)])
func start_broadcast() -> void:
_timer.start()
func stop_broadcast() -> void:
_timer.stop()
func send_broadcast() -> bool:
_socket.set_broadcast_enabled(true)
var data: PackedByteArray = _form_status_packet()
# добавление 11 байтов вперед вохоже
_socket.send_to(broadcast_addr[1], broadcast_addr[0], data)
_log_error('Не удалось привязаться к %s:%d - %s' % [address, port, error_string(bind_result)])
return false
_log_info('Успешно привязано к %s:%d' % [address, port])
return true
func close_udp_unit() -> void:
func add_timers_to_scene(parent: Node) -> void:
'''Добавление таймеров в дерево сцены'''
# Проверяем что таймеры еще не имеют родителя
if not _timer.get_parent():
parent.add_child(_timer)
elif _timer.get_parent() != parent:
_log_warning('Таймер broadcast уже добавлен в другую сцену')
else:
_log_debug("Таймеры добавлены в сцену")
func start_broadcast() -> void:
'''Запуск широковещательной рассылки и опроса сокета'''
_timer.start()
_log_info('Широковещательная рассылка запущена')
func stop_broadcast() -> void:
'''Остановка широковещательной рассылки'''
_timer.stop()
_log_info('Широковещательная рассылка остановлена')
func close_udp_unit() -> void:
'''Закрытие UDP соединения'''
stop_broadcast()
_socket.close()
_log_info('UDP соединение закрыто')
func add_timer_to_scene(parent: Node) -> void:
parent.add_child(_timer)
#region Публичные методы для управления состоянием
func _handle_set_flag_ems_g(set_flag: bool)->void:
'''Установка или сброс 5-го бита (бит 4) в статусе сокетов'''
var current_status = get_sockets_status()
if set_flag:
current_status = current_status | (1 << 4)
_log_info('Установлен флаг загрузки EMS-G')
set_sockets_status(current_status)
func poll_receive() -> void:
while _socket.get_available_packet_count() > 0:
var broadcast_packet = _socket.get_packet()
var addr_receive = _socket.get_packet_ip()
#var port_receive = _socket.get_packet_port()
#var parsed_data = _parse_packet(broadcast_packet)
_handle_received_data(broadcast_packet, addr_receive)
func set_sockets_status(status: int) -> void:
'''Установка статуса сокетов'''
_sockets_status_bit = status
emit_signal('update_socket_status', _sockets_status_bit, _self_name)
_log_debug("Статус сокетов обновлен: 0x%02X" % status)
func _handle_received_data(data: PackedByteArray, addr: String) -> void:
print("данные от %s: %s" % [addr, data])
# Здесь можно эмитировать сигналы или обрабатывать данные
func get_sockets_status() -> int:
'''Получение статуса сокетов'''
return _sockets_status_bit
func _on_timer_timeout() -> void:
func set_dkm_status(status: int) -> void:
'''Установка статуса DKM'''
_dkm_status_bit = status
_log_debug("Статус DKM установлен: 0x%02X" % status)
func get_dkm_status() -> int:
'''Получение статуса DKM'''
return _dkm_status_bit
func set_ip_status(status: int)->void:
'''Установка статуса ИП'''
_dry_contact_status_bit = status
_log_debug("Статус ИП установлен: 0x%02X" % status)
func get_ip_status()->int:
'''Получение статуса ИП'''
return _dry_contact_status_bit
func set_ems_g_status(in_imp: int, in_const: int, out_imp: int, out_const: int) -> void:
'''Установка статуса входов и выходов ЕМС'''
_input_impulse = in_imp
_input_const = in_const
_output_impulse = out_imp
_output_const = out_const
_log_debug("Статус EMS-G обновлен: IN_C=0x%04X, IN_I=0x%04X, OUT_C=0x%04X, OUT_I=0x%04X" % [in_const, in_imp, out_const, out_imp])
func set_isa_port(port_addr: int, value: int) -> bool:
'''Установка значения ISA порта'''
if port_addr < 0 or port_addr > 0xFFFF:
_log_error('Некорректный адрес порта: 0x%03X' % port_addr)
return false
_isa_ports[port_addr] = value
emit_signal('update_isa_ports', _isa_ports, _self_name)
_log_debug("Порт ISA 0x%04X установлен: 0x%04X" % [port_addr, value])
return true
func get_isa_port(port_addr: int) -> int:
'''Получение значения ISA порта'''
return _isa_ports.get(port_addr, 0xFFFF)
func read_isa_ports(port_addresses: Array) -> Dictionary:
'''Чтение значений нескольких ISA портов'''
var result = {}
for port_addr in port_addresses:
if port_addr is int and port_addr >= 0 and port_addr <= 0xFFFF:
result[port_addr] = get_isa_port(port_addr)
else:
_log_error('Некорректный адрес порта: ' + str(port_addr))
return result
#endregion
#region Обработчики таймеров
func _on_broadcast_timeout() -> void:
'''Обработчик таймера широковещательной рассылки'''
send_broadcast()
func _form_status_packet() -> PackedByteArray:
var buf = PackedByteArray()
buf.resize(126) # Увеличиваем размер до 126 байт
func _on_poll_timeout() -> void:
'''Обработчик таймера опроса сокета'''
_poll_socket()
#endregion
#region Работа с сокетом
func send_broadcast() -> bool:
'''Отправка широковещательного пакета'''
_socket.set_broadcast_enabled(true)
var data: PackedByteArray = _form_status_device_packet()
var send_result = _socket.send_to(BROADCAST_ADDR[1], BROADCAST_ADDR[0], data)
# Заголовок (0-5 байты)
buf.encode_u16(0, 0) # 00 00
buf.encode_u16(2, 0x0cff) # ff 0c
buf.encode_u16(4, 0) # 00 00
# Идентификатор пакета
buf[6] = 103 # 67
# Статус устройств
buf[7] = 0
buf[8] = sockets_status_bit
# Данные EMS-G (байты 9-34)
var ems_g_data = [0x9e00, 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0x0000,
0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff]
for i in range(13):
buf.encode_u16(9 + i * 2, ems_g_data[i])
# Данные UG (байты 35-38)
buf.encode_u16(35, 0x0000) # 00 00
buf.encode_u16(37, 0x059e) # 9e 05
# Данные UKP0 (байты 39-70)
for i in ukp0_data.size():
buf[39 + i] = ukp0_data[i]
# Данные UKP1 (байты 71-102)
for i in ukp1_data.size():
buf[71 + i] = ukp1_data[i]
return buf
if send_result == OK:
return true
else:
_log_error('Ошибка отправки широковещательного пакета')
return false
####
func _poll_socket() -> void:
'''Опрос сокета для получения входящих пакетов'''
while _socket.get_available_packet_count() > 0:
var packet = _socket.get_packet()
var address = _socket.get_packet_ip()
var port = _socket.get_packet_port()
_handle_received_data(packet, address, port)
#endregion
#region Формирование пакета статуса устройства
func _form_status_device_packet() -> PackedByteArray:
var buffer = PackedByteArray()
# Базовый размер пакета + дополнительные байты для записанных портов
# + 2 байта для полей команды (код + результат)
var header_size = 8
var ports_count_size = 1
var cmd_fields_size = 0
var isa_ports_size = 0
if _written_isa_ports.size() > 0:
isa_ports_size = _written_isa_ports.size() * 4
cmd_fields_size = 4
var total_size = header_size + STATUS_PACKET_SIZE + ports_count_size + cmd_fields_size + isa_ports_size
buffer.resize(total_size)
buffer.fill(0)
_encode_packet_header(buffer)
_encode_device_data(buffer)
# Добавляем поле команды после статуса
if _written_isa_ports.size() > 0:
var cmd_offset = header_size + STATUS_PACKET_SIZE
if cmd_offset < buffer.size():
buffer.encode_u16(cmd_offset, _last_received_command) # код команды (0 для статуса)
if (cmd_offset + 1) < buffer.size():
buffer.encode_u16(cmd_offset+1, 1) # результат выполнения
var ports_count_offset = cmd_offset + 2
# Затем ISA порты
buffer.encode_u8(ports_count_offset, _written_isa_ports.size())
_encode_written_isa_ports(buffer, ports_count_offset+1)
return buffer
func _encode_packet_header(buffer: PackedByteArray) -> void:
'''Кодирование заголовка пакета'''
# 0-1: Reserved (0x0000)
buffer.encode_u16(0, 0)
# 2-3: Номер команды
buffer.encode_u16(2, _cmd_number_count)
# 4-5: Reserved (0x0000)
buffer.encode_u16(4, 0)
# 6-7: Длина статуса
buffer.encode_u16(6, STATUS_PACKET_SIZE)
func _encode_written_isa_ports(buffer: PackedByteArray, offset: int) -> void:
'''Добавление данных о записанных ISA портах в конец пакета'''
for i in range(_written_isa_ports.size()):
var port_data = _written_isa_ports[i]
# Добавляем порт и значение (по 2 байта каждый)
buffer.encode_u16(offset, port_data[0])
buffer.encode_u16(offset + 2, port_data[1])
offset += 4
func _encode_device_data(buffer: PackedByteArray) -> void:
'''Кодирование данных устройств в пакет'''
# 8-9: Статус сокетов
buffer.encode_u16(8, _sockets_status_bit)
# 9-34: Данные EMS-G
_encode_ems_g_data(buffer)
# 35-38: Данные UG
_encode_ug_data(buffer)
# 39-70: Данные UKP0
# 71-102: Данные UKP1
_encode_ukp_data(buffer)
func _encode_ems_g_data(buffer: PackedByteArray) -> void:
'''Кодирование данных EMS-G'''
buffer.encode_u16(EMS_G_DATA_OFFSET, _input_const)
buffer.encode_u16(EMS_G_DATA_OFFSET + 6, _input_impulse)
buffer.encode_u16(EMS_G_DATA_OFFSET + 12, _output_const)
buffer.encode_u16(EMS_G_DATA_OFFSET + 18, _output_impulse)
func _encode_ug_data(buffer: PackedByteArray) -> void:
'''Кодирование данных UG'''
# D15...D8 = ДКМ (старший байт)
buffer.encode_u8(UG_DATA_OFFSET + 1, _dkm_status_bit & 0xFF)
# D7...D0 = Сухие контакты (младший байт)
buffer.encode_u8(UG_DATA_OFFSET, _dry_contact_status_bit & 0xFF)
func _encode_ukp_data(buffer: PackedByteArray) -> void:
'''Кодирование данных UKP'''
for i in range(UKP_DATA_SIZE):
if i < ukp0_data.size():
buffer[UKP0_DATA_OFFSET + i] = ukp0_data[i]
if i < ukp1_data.size():
buffer[UKP1_DATA_OFFSET + i] = ukp1_data[i]
#endregion
#region Прямая отправка ответа
func _send_immediate_response(address: String, port: int) -> void:
'''Немедленная отправка пакета на указанный адрес и порт'''
var data: PackedByteArray = _form_status_device_packet()
_socket.set_broadcast_enabled(false) # Отключаем broadcast для точечной отправки
var send_result = _socket.send_to(address, port, data)
_socket.set_broadcast_enabled(true) # Включаем обратно для broadcast
if send_result == OK:
_log_debug('Отправлен немедленный ответ на %s:%d' % [address, port])
else:
_log_error('Ошибка отправки ответа на %s:%d' % [address, port])
#endregion
#region Обработка входящих данных
func _handle_received_data(data: PackedByteArray, address: String, port: int) -> void:
'''Обработка входящих данных'''
if not _validate_received_packet(data):
return
var command_type = _get_command_type(data)
_cmd_number_count = data.decode_u16(2)
if command_type == -1: # Проверяем на -1 вместо null
return
_handle_isa_command(command_type, data, address, port)
func _validate_received_packet(data: PackedByteArray) -> bool:
'''Валидация входящего пакета'''
if data.size() < MIN_PACKET_SIZE:
_log_warning('Получен слишком короткий пакет: %d байт' % data.size())
return false
return true
func _get_command_type(data: PackedByteArray) -> int:
'''Получение типа команды из данных пакета'''
if data.size() < 6:
return -1 # Используем -1 для обозначения ошибки
return data.decode_u8(6)
func _handle_isa_command(command_code: int, data: PackedByteArray, address: String, port: int) -> void:
'''Обработка команд ISA'''
_last_received_command = command_code # Запоминаем последнюю команду
match command_code:
CMD_READ_ISA:
_handle_read_isa_command(data, address, port)
CMD_WRITE_ISA:
_handle_write_isa_command(data, address, port)
CMD_FLAG_EMS_G:
_handle_set_flag_ems_g(true)
_:
_log_warning('Неизвестная команда ISA: %d от %s:%d' % [command_code, address, port])
func _handle_read_isa_command(data: PackedByteArray, address: String, port: int) -> void:
'''Обработка команды чтения ISA портов'''
var ports = _parse_command_ports(data)
if ports.is_empty():
return
var port_data = _get_isa_ports_data(ports)
_log_info('Принята команда на чтение ISA портов от %s:%d: %s' % [address, port, port_data])
_written_isa_ports = []
for i in range(ports.size()):
_written_isa_ports.append([ports[i], port_data[i]])
# Отправляем ответ с данными запрошенных портов
_send_immediate_response(address, port)
func _handle_write_isa_command(data: PackedByteArray, address: String, port: int) -> void:
'''Обработка команды записи ISA портов'''
var ports = _parse_command_ports(data)
var data_to_write = _parse_command_ports_data(data)
if ports.size() != data_to_write.size():
_log_warning('Несоответствие размеров портов и данных от %s:%d' % [address, port])
return
# Обновляем или добавляем записанные порты
for i in range(ports.size()):
var port_addr = ports[i]
var value = data_to_write[i]
set_isa_port(port_addr, value)
# Ищем порт в массиве, если есть - обновляем, если нет - добавляем
var found = false
for j in range(_written_isa_ports.size()):
if _written_isa_ports[j][0] == port_addr:
_written_isa_ports[j][1] = value # Обновляем значение
found = true
break
if not found:
_written_isa_ports.append([port_addr, value]) # Добавляем новый порт
_log_info('Принята команда на запись в ISA порты от %s:%d: %s' % [address, port, _isa_ports])
# Немедленная отправка ответа отправителю
_send_immediate_response(address, port)
#endregion
#region Парсинг команд ISA
func _parse_command_ports(data: PackedByteArray) -> Array:
'''Парсинг адресов портов из команды'''
if data.size() < 7:
return []
var ports_count = data.decode_u8(7)
if data.size() <= (ports_count + 8):
return []
var ports = []
if _get_command_type(data) == CMD_READ_ISA:
for i in range(ports_count):
var port_address = data.decode_u16(8 + i * 2)
ports.append(port_address)
elif _get_command_type(data) == CMD_WRITE_ISA:
for i in range(ports_count):
var port_address = data.decode_u16(8 + i * 4)
ports.append(port_address)
return ports
func _parse_command_ports_data(data: PackedByteArray) -> Array:
'''Парсинг данных портов из команды записи'''
if data.size() < 8:
return []
var ports_count = data.decode_u8(7)
if data.size() <= (ports_count + 10):
return []
var ports_data = []
var data_offset = 10 # Смещение для данных портов
for i in range(ports_count):
if data_offset + i * 4 < data.size():
var port = data.decode_u16(8 + i * 4)
var port_data = data.decode_u16(data_offset + i * 4)
ports_data.append(port_data)
_log_debug('Данные порта 0x%02X: 0x%02X' % [port, port_data])
return ports_data
func _get_isa_ports_data(ports: Array) -> Array:
'''Получение данных указанных ISA портов'''
var result = []
for port_address in ports:
result.append(get_isa_port(port_address))
return result
func _set_isa_ports_data(ports: Array, data: Array) -> void:
'''Установка данных для указанных ISA портов'''
for i in range(ports.size()):
if i < data.size():
set_isa_port(ports[i], data[i])
#endregion
#region Методы логирования
func _log_info(message: String) -> void:
'''Логирование информационного сообщения'''
if logs_manager:
logs_manager.info("[%s] %s" % [_self_name, message])
else:
print("[%s] INFO: %s" % [_self_name, message])
func _log_warning(message: String) -> void:
'''Логирование предупреждения'''
if logs_manager:
logs_manager.warning("[%s] %s" % [_self_name, message])
else:
print("[%s] WARNING: %s" % [_self_name, message])
func _log_error(message: String) -> void:
'''Логирование ошибки'''
if logs_manager:
logs_manager.error("[%s] %s" % [_self_name, message])
else:
print("[%s] ERROR: %s" % [_self_name, message])
func _log_debug(message: String) -> void:
'''Логирование отладочной информации'''
if logs_manager:
logs_manager.debug("[%s] %s" % [_self_name, message])
else:
print("[%s] DEBUG: %s" % [_self_name, message])
#endregion
#region Вложенный класс SocketUDP
class SocketUDP extends PacketPeerUDP:
func send_to(addr: String, port: int, data: PackedByteArray):
set_dest_address(addr, port)
var rc: = put_packet(data)
if rc != OK:
push_error('%s: %s:%s' % [error_string(rc), addr, port])
'''Расширенный класс UDP сокета с улучшенной обработкой ошибок'''
func send_to(address: String, port: int, data: PackedByteArray) -> int:
'''Отправка данных на указанный адрес и порт'''
set_dest_address(address, port)
var result = put_packet(data)
if result != OK:
push_error('Ошибка отправки: %s - %s:%d' % [error_string(result), address, port])
return result
#endregion
#region Методы управления записанными портами
func clear_written_ports() -> void:
'''Очистка списка записанных портов (ручное управление)'''
_written_isa_ports.clear()
_log_info('Список записанных портов очищен')
func get_written_ports_count() -> int:
'''Получение количества записанных портов'''
return _written_isa_ports.size()
func remove_written_port(port_addr: int) -> bool:
'''Удаление конкретного порта из списка записанных'''
for i in range(_written_isa_ports.size()):
if _written_isa_ports[i][0] == port_addr:
_written_isa_ports.remove_at(i)
_log_info('Порт 0x%04X удален из списка записанных' % port_addr)
return true
return false
#endregion

198
uf_broadcast.gd Normal file
View File

@@ -0,0 +1,198 @@
class_name UF_Broadcast
const ADDRESS_LIST: Dictionary = {
'УФ': [50052, '10.1.1.52']}
const BROADCAST_ADDR: Array = [50000, '10.1.1.255']
const MIN_PACKET_SIZE: int = 7
var _self_name: String
var _socket: SocketUDP
var _timer: Timer
var _cmd_number_count: int = 0
var status: int = 0
var ip: int = 0
func _init(name: String):
'''Инициализация UDP broadcast UF клиента'''
_self_name = name
_initialize_socket()
_initialize_timers()
if not _bind_to_device_address(name):
push_error('Не удалось инициализировать устройство: %s' % name)
func _initialize_socket() -> void:
'''Инициализация UDP сокета'''
_socket = SocketUDP.new()
func _initialize_timers() -> void:
'''Инициализация таймеров'''
# Таймер для широковещательной рассылки (3 раза в секунду)
_timer = Timer.new()
_timer.wait_time = 1.0 / 5.0
_timer.timeout.connect(_on_broadcast_timeout)
func add_timers_to_scene(parent: Node) -> void:
'''Добавление таймеров в дерево сцены'''
# Проверяем что таймеры еще не имеют родителя
if not _timer.get_parent():
parent.add_child(_timer)
elif _timer.get_parent() != parent:
push_warning('Таймер broadcast уже добавлен в другую сцену')
func _bind_to_device_address(device_name: String) -> bool:
'''Привязка к адресу устройства по его имени'''
if not ADDRESS_LIST.has(device_name):
push_error('Неизвестное устройство: %s' % device_name)
return false
var port: int = ADDRESS_LIST[device_name][0]
var address: String = ADDRESS_LIST[device_name][1]
return bind_unit_address(port, address)
func bind_unit_address(port: int, address: String) -> bool:
'''Привязка сокета к указанному порту и адресу'''
if port <= 0 or port > 65535:
push_error('Некорректный номер порта: %d' % port)
return false
var bind_result = _socket.bind(port, address)
if bind_result != OK:
push_error('Не удалось привязаться к %s:%d - %s' % [address, port, error_string(bind_result)])
return false
print('Успешно привязано к %s:%d' % [address, port])
return true
func _on_broadcast_timeout() -> void:
'''Обработчик таймера широковещательной рассылки'''
send_broadcast()
func send_broadcast() -> bool:
'''Отправка широковещательного пакета'''
_socket.set_broadcast_enabled(true)
var data: PackedByteArray = _form_status_device_packet()
var send_result = _socket.send_to(BROADCAST_ADDR[1], BROADCAST_ADDR[0], data)
if send_result == OK:
return true
else:
push_error('Ошибка отправки широковещательного пакета')
return false
func _form_status_device_packet() -> PackedByteArray:
var uf: Array = [0x00, 0x00, _cmd_number_count, 0x05, 0x00, 0x00, 0x4f, 0x00, status, 0x00, ip, 0x00, 0x00, 0xff, 0xff, 0x00,\
0x00, 0x00, 0x00, 0xff, 0xff, 0xc0, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff,\
0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00,\
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x04, 0x24, 0x01, 0x00, 0x00, 0x26, 0x01,\
0x00, 0x00, 0x28, 0x01, 0x00, 0x00, 0x2a, 0x01, 0xff, 0xff]
var buffer = PackedByteArray()
for byte in uf:
buffer.append(byte)
return buffer
func start_broadcast() -> void:
'''Запуск широковещательной рассылки и опроса сокета'''
_timer.start()
print('Широковещательная рассылка запущена')
func stop_broadcast() -> void:
'''Остановка широковещательной рассылки'''
_timer.stop()
print('Широковещательная рассылка остановлена')
func close_udp_unit() -> void:
'''Закрытие UDP соединения'''
stop_broadcast()
_socket.close()
print('UDP соединение закрыто')
func _on_poll_timeout() -> void:
'''Обработчик таймера опроса сокета'''
_poll_socket()
func _poll_socket() -> void:
'''Опрос сокета для получения входящих пакетов'''
while _socket.get_available_packet_count() > 0:
var packet = _socket.get_packet()
_handle_received_data(packet)
func _handle_received_data(data: PackedByteArray) -> void:
'''Обработка входящих данных'''
if not _validate_received_packet(data):
return
var command_type = _get_command_type(data)
_cmd_number_count = data.decode_u16(2)
if command_type == -1: # Проверяем на -1 вместо null
return
func _validate_received_packet(data: PackedByteArray) -> bool:
'''Валидация входящего пакета'''
if data.size() < MIN_PACKET_SIZE:
push_warning('Получен слишком короткий пакет: %d байт' % data.size())
return false
return true
func _get_command_type(data: PackedByteArray) -> int:
'''Получение типа команды из данных пакета'''
if data.size() < 6:
return -1 # Используем -1 для обозначения ошибки
return data.decode_u8(6)
func set_status_uf(new_status: int) -> void:
if status != new_status:
status = new_status
print('Установлен статус УФ: %s' % status)
func get_status_uf() -> int:
return status
func set_power_bit_uf(new_status)-> void:
if ip != new_status:
ip = new_status
func get_power_bit_uf() -> int:
return ip
#region Вложенный класс SocketUDP
class SocketUDP extends PacketPeerUDP:
'''Расширенный класс UDP сокета с улучшенной обработкой ошибок'''
func send_to(address: String, port: int, data: PackedByteArray) -> int:
'''Отправка данных на указанный адрес и порт'''
set_dest_address(address, port)
var result = put_packet(data)
if result != OK:
push_error('Ошибка отправки: %s - %s:%d' % [error_string(result), address, port])
return result
#endregion

1
uf_broadcast.gd.uid Normal file
View File

@@ -0,0 +1 @@
uid://lju7b4vxgjpd

88
журнал.gd Normal file
View File

@@ -0,0 +1,88 @@
class_name LogsRich
extends RichTextLabel
enum LogLevel {
INFO,
WARNING,
ERROR,
DEBUG
}
var _level_colors: Dictionary = {
LogLevel.INFO: Color.WHITE,
LogLevel.WARNING: Color.YELLOW,
LogLevel.ERROR: Color.RED,
LogLevel.DEBUG: Color.CYAN
}
var _level_prefixes: Dictionary = {
LogLevel.INFO: "[INFO]",
LogLevel.WARNING: "[WARNING]",
LogLevel.ERROR: "[ERROR]",
LogLevel.DEBUG: "[DEBUG]"
}
var max_lines: int = 1000
func _ready():
scroll_active = true
#scroll_following = true
bbcode_enabled = true
fit_content = false
clear_logs()
func add_log(message: String, level: LogLevel = LogLevel.INFO) -> void:
if not is_inside_tree():
push_warning("LogsRich не добавлен в дерево сцены!")
return
var timestamp = Time.get_time_string_from_system()
var prefix = _level_prefixes.get(level, "[INFO]")
var color = _level_colors.get(level, Color.WHITE)
var formatted_message = "[%s] %s %s" % [timestamp, prefix, message]
var color_hex = color.to_html(false)
# Просто добавляем текст (RichTextLabel сам обрабатывает BBcode)
append_text("[color=#%s]%s[/color]\n" % [color_hex, formatted_message])
# Прокручиваем вниз
if is_inside_tree():
scroll_to_line(get_line_count())
# Ограничиваем количество строк
limit_line_count_simple()
func info(message: String) -> void:
add_log(message, LogLevel.INFO)
func warning(message: String) -> void:
add_log(message, LogLevel.WARNING)
func error(message: String) -> void:
add_log(message, LogLevel.ERROR)
func debug(message: String) -> void:
add_log(message, LogLevel.DEBUG)
func clear_logs() -> void:
text = ""
func limit_line_count_simple() -> void:
var lines = get_line_count()
if lines > max_lines:
# Получаем чистый текст без BBcode
var raw_text = get_parsed_text()
var text_lines = raw_text.split("\n")
if text_lines.size() > max_lines:
# Удаляем первые строки
text_lines = text_lines.slice(text_lines.size() - max_lines)
# Очищаем и перезаписываем
clear_logs()
for line in text_lines:
if line.strip_edges().length() > 0:
# Добавляем как INFO (теряем цвета, но это проще)
append_text("[color=#ffffff]%s[/color]\n" % line)

1
журнал.gd.uid Normal file
View File

@@ -0,0 +1 @@
uid://bwll70u61ocw6