Compare commits

...

10 Commits

13 changed files with 1402 additions and 353 deletions

View File

@@ -36,3 +36,4 @@
- После клонирования запустить скрипт `git-hooks-config.sh`
- В среде **Windows** запуск скрипта `git-hooks-config.sh` производить из оболочки `git bash`
- Нужно обновить `settings.json`
- поиск циклических зависимостей

164
check-graph.py Normal file
View File

@@ -0,0 +1,164 @@
# -*- coding: utf-8 -*-
import os
import re
import networkx as nx
# pip install networkx plotly numpy scipy
def build_class_graph(directory):
"""Builds a directed acyclic graph of class dependencies."""
# Create a graph to store the dependencies
graph = nx.DiGraph()
dependencies = {}
# Find all .gd files in the directory
for root, dirs, files in os.walk(directory):
for file in files:
if not file.endswith(".gd"):
continue
file_path = os.path.join(root, file)
# Find the class name in the file
with open(file_path, "r", encoding="utf-8") as f:
contents = f.read()
match = re.search(r"^class_name\s+(\w+)", contents, re.MULTILINE)
if not match:
continue
class_name = match.group(1)
graph.add_node(class_name)
# Find the dependencies of the class
for dep_match in re.finditer(
r"^\s*extends\s+(\w+)", contents, re.MULTILINE
):
dep_name = dep_match.group(1)
graph.add_edge(class_name, dep_name)
# Find all uses of the class name in other files
for root2, dirs2, files2 in os.walk(directory):
for file2 in files2:
if not file2.endswith(".gd"):
continue
if file_path == os.path.join(root2, file2):
continue
with open(os.path.join(root2, file2), "r", encoding="utf-8") as f2:
contents2 = f2.read()
if re.search(rf"\b{class_name}\b", contents2):
print(
file2.ljust(30, " "),
f"--> class_name {class_name}".ljust(50, " "),
f" from {file}",
)
graph.add_edge(class_name, file2)
dependencies[file2] = file
pos = nx.planar_layout(graph)
# Set the node positions in the graph
nx.set_node_attributes(graph, pos, "pos")
return graph, dependencies
def debug_graph(graph):
"""Builds and visualizes a graph of class dependencies in the directory."""
import plotly.graph_objects as go
# Create the plotly figure
fig = go.Figure()
# Add the nodes to the figure
for node in graph.nodes():
x, y = graph.nodes[node]["pos"]
fig.add_trace(
go.Scatter(
x=[x],
y=[y],
text=[node],
hovertext=[f"Class: {node}"],
mode="markers",
marker=dict(
symbol="circle", size=20, line=dict(width=1, color="black"),
),
name=node,
)
)
# Add the edges to the figure
for edge in graph.edges():
x0, y0 = graph.nodes[edge[0]]["pos"]
x1, y1 = graph.nodes[edge[1]]["pos"]
fig.add_trace(
go.Scatter(
x=[x0, x1],
y=[y0, y1],
text=[f"{edge[0]} -> {edge[1]}", ""],
hovertext=[f"Depends on: {edge[1]}", ""],
mode="lines+markers+text",
line=dict(width=2, color="black"),
marker=dict(size=0),
textposition="middle right",
showlegend=True,
)
)
# Customize the layout and style of the figure
fig.update_layout(
title="Class Dependency Graph",
title_font_size=24,
margin=dict(l=20, r=20, t=50, b=20),
hovermode="closest",
plot_bgcolor="white",
showlegend=True,
)
# Show the figure
fig.show()
def find_circular_dependencies(dependencies):
"""Finds circular dependencies between classes in the directory."""
circular_dependencies = []
for file, dependency in dependencies.items():
visited = set()
path = [file]
while dependency and dependency not in visited:
visited.add(dependency)
path.append(dependency)
dependency = dependencies.get(dependency)
# Circular check
if dependency in path:
start_index = path.index(dependency)
circular_dependency = path[start_index:]
if circular_dependency not in circular_dependencies:
circular_dependencies.append(circular_dependency)
break
# Convert circular_dependencies to dependency paths
dependency_paths = []
for circular_dependency in circular_dependencies:
dependency_path = " -> ".join(circular_dependency)
dependency_paths.append(dependency_path)
return dependency_paths
# Example usage
graph, dependencies = build_class_graph("./")
debug_graph(graph)
circular_deps = find_circular_dependencies(dependencies)
print("----------------------------------")
if circular_deps:
print(" Circular dependencies found!")
for dep in circular_deps:
print("\t", dep)
else:
print(" No circular dependencies found.")
print("----------------------------------")

View File

@@ -5,8 +5,8 @@
[ext_resource type="PackedScene" uid="uid://lwmw4egynmd1" path="res://scenes/контроль/контроль.tscn" id="3_txp0s"]
[ext_resource type="PackedScene" uid="uid://trt0q8th3bn2" path="res://scenes/журнал/журнал.tscn" id="4_cu5k8"]
[ext_resource type="PackedScene" uid="uid://dab6loryocc73" path="res://scenes/эмс/эмс.tscn" id="5_u71bh"]
[ext_resource type="PackedScene" uid="uid://musb21x2u0xs" path="res://scenes/эмс2/эмс2.tscn" id="6_41d34"]
[ext_resource type="PackedScene" uid="uid://bnptm4rlp60dq" path="res://scenes/настройки/настройки.tscn" id="6_i8iv3"]
[ext_resource type="PackedScene" uid="uid://musb21x2u0xs" path="res://scenes/эмс2/эмс2.tscn" id="6_rsg03"]
[ext_resource type="Script" uid="uid://b5ykwyk5vpi6" path="res://scenes/tabs-switch/lbl_ready.gd" id="8_tidwt"]
[ext_resource type="Script" uid="uid://roajn6c6wvc1" path="res://scenes/tabs-switch/тренаж_режим.gd" id="9_41d34"]
@@ -69,7 +69,7 @@ visible = false
layout_mode = 2
metadata/_tab_index = 3
[node name="ЭМС ТГ" parent="tab_switch" instance=ExtResource("6_rsg03")]
[node name="ЭМС ТГ" parent="tab_switch" instance=ExtResource("6_41d34")]
visible = false
layout_mode = 2
metadata/_tab_index = 4

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -3,6 +3,5 @@
[ext_resource type="Texture2D" uid="uid://cj1f2uy6qfvki" path="res://scenes/tilemap/23900.png" id="1_v0nwa"]
[node name="23900" type="Sprite2D"]
rotation = 4.71239
scale = Vector2(0.496934, 0.496934)
texture = ExtResource("1_v0nwa")

View File

@@ -380,22 +380,33 @@ func _process(delta) -> void:
update_scale_label()
func _response(result, code, _headers: Array, body: PackedByteArray, req, tile: Tile) -> void:
func process_online(result):
# ERROR: res://scenes/tilemap/tilemap.gd:396 - Invalid access to property or key 'online' on a base object of type 'Nil'.
var unit_instance: = network.get_unit_instance('уарэп-карта')
_req_count -= 1
remove_child(req)
req.queue_free()
if not unit_instance: return
if result != HTTPRequest.RESULT_SUCCESS:
if unit_instance.online:
unit_instance.online = false
unit_instance.emit_signal('line_changed', unit_instance)
_queue[tile.i] = tile
return
return false
unit_instance.rx_tick = Time.get_ticks_msec()
if not unit_instance.online:
unit_instance.online = true
unit_instance.emit_signal('line_changed', unit_instance)
return true
func _response(result, code, _headers: Array, body: PackedByteArray, req, tile: Tile) -> void:
_req_count -= 1
remove_child(req)
req.queue_free()
if process_online(result):
_queue[tile.i] = tile
else:
return
if code == 404:
push_error('на сервере нет \"%s\"' % tile.url)
@@ -405,11 +416,6 @@ func _response(result, code, _headers: Array, body: PackedByteArray, req, tile:
push_error('не удалось получить изображение (неверный формат?) из \"%s\"' % tile.url)
return
unit_instance.rx_tick = Time.get_ticks_msec()
if not unit_instance.online:
unit_instance.online = true
unit_instance.emit_signal('line_changed', unit_instance)
tile.texture = ImageTexture.create_from_image(image)
_cache[tile.i] = tile
@@ -711,7 +717,7 @@ func set_coordinates(lon: float, lat: float, course: float) -> void:
add_mark_from_lon_lat(ship_id, lon, lat, ship_instance, 0.0)
else:
_marks[ship_id].position = lonlat_to_world(lon, lat)
ship_instance.rotation_degrees = fmod(270.0 + course, 360.0)
ship_instance.rotation_degrees = fmod(course, 360.0)
queue_redraw()

View File

@@ -177,7 +177,7 @@ func _ready():
signaller.connect('map_user_panning', Callable(self, 'on_user_panning'))
signaller.connect('update_coordinates_map', Callable(self, 'on_update_coordinates_label'))
signaller.connect('update_scale_map', Callable(self, 'on_update_scale_label'))
signaller.connect('sector_klaster', Callable(self, 'sector_klaster'))
signaller.connect('sector_klaster', Callable(self, 'sector_klaster'))
signaller.connect('clear_klaster', Callable(self, 'clear_all_klaster'))
var unit_nav = network.get_unit_instance('навигация')
unit_nav.connect('data_received', on_navi_data_received)

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=18 format=3 uid="uid://b276iygic5itk"]
[gd_scene load_steps=19 format=3 uid="uid://b276iygic5itk"]
[ext_resource type="Script" uid="uid://daudc0s3oox3i" path="res://scenes/работа/работа.gd" id="1_niok4"]
[ext_resource type="PackedScene" uid="uid://nl1vklubr5kr" path="res://scenes/bip/bip.tscn" id="2_br3s6"]
@@ -13,6 +13,7 @@
[ext_resource type="Texture2D" uid="uid://csdw3q5dtvu4w" path="res://data/power-1.png" id="11_xp4y6"]
[ext_resource type="Texture2D" uid="uid://0sk43ticjaxk" path="res://data/navi-center-0.png" id="12_5ffal"]
[ext_resource type="Texture2D" uid="uid://bk4ssfho1murp" path="res://data/navi-center-1.png" id="13_ggrwd"]
[ext_resource type="Texture2D" uid="uid://c6booa8753u5t" path="res://data/состояние-исправности-2.png" id="14_ggrwd"]
[sub_resource type="Animation" id="Animation_5ffal"]
resource_name = "goto"
@@ -338,6 +339,42 @@ offset_bottom = 302.0
theme_override_font_sizes/font_size = 20
text = ""
[node name="btn_close" parent="." instance=ExtResource("8_k0iv2")]
layout_mode = 0
offset_left = 10.0
offset_top = 917.0
offset_right = 109.0
offset_bottom = 937.0
size_flags_horizontal = 3
size_flags_vertical = 3
tooltip_text = "Кнопка для выбора сектора при помощи клика мышкой"
strips_rotation = 25.0
text = "Сек. запрета"
metadata/state = 0
[node name="btn_work" parent="." instance=ExtResource("8_k0iv2")]
layout_mode = 2
offset_left = 10.0
offset_top = 890.0
offset_right = 109.0
offset_bottom = 910.0
size_flags_horizontal = 3
size_flags_vertical = 3
tooltip_text = "Кнопка для выбора сектора при помощи клика мышкой"
strips_rotation = 25.0
text = "Сек. работы"
metadata/state = 0
[node name="btn_all_work" type="TextureButton" parent="."]
modulate = Color(1, 1, 1, 0.356863)
layout_mode = 0
offset_left = 112.0
offset_top = 920.0
offset_right = 336.0
offset_bottom = 1144.0
scale = Vector2(0.06, 0.06)
texture_normal = ExtResource("14_ggrwd")
[connection signal="drag_begin" from="." to="." method="_on_drag_begin"]
[connection signal="drag_continue" from="." to="." method="_on_drag_continue"]
[connection signal="toggled" from="btn_view" to="." method="on_button_view_toggled"]
@@ -346,7 +383,8 @@ text = ""
[connection signal="toggled" from="btn_activate" to="." method="on_btn_activate"]
[connection signal="toggled" from="btn_center" to="." method="on_btn_center_toggled"]
[connection signal="button_down" from="zoom_plus" to="tilemap" method="_on_zoom_plus_button_down"]
[connection signal="button_up" from="zoom_plus" to="tilemap" method="_on_zoom_plus_button_up"]
[connection signal="button_up" from="zoom_plus" to="tilemap" method="_on_zoom_button_up"]
[connection signal="button_up" from="zoom_plus" to="tilemap" method="_on_zoom_plus_button_up"]
[connection signal="button_down" from="zoom_minus" to="tilemap" method="_on_zoom_minus_button_down"]
[connection signal="button_up" from="zoom_minus" to="tilemap" method="_on_zoom_button_up"]
[connection signal="pressed" from="btn_all_work" to="." method="_on_btn_all_work_pressed"]

File diff suppressed because it is too large Load Diff

View File

@@ -351,8 +351,8 @@ func map_msvk_to_ecms(unit_instance: unit.Unit, ecms: Dictionary):
## Извлекает массив пространственно-частотных фильтров
func map_mpchf_to_seczaps(unit_instance: unit.Unit, seczaps: Dictionary):
var json_dic: = unit_instance.json_dic as Dictionary
func map_mpchf_to_seczaps(unit_instance: unit.Unit, _seczaps: Dictionary):
var json_dic = unit_instance.json_dic
var mpchf = json_dic.get('mpchf', [])
if mpchf is not Array: return
for item in mpchf:

View File

@@ -22,18 +22,20 @@ class SocketSerial extends SerialPort:
'O': SerialPort.PARITY_ODD }
static var tick: int
var unit_instance: unit.Unit
var failed: = {}
func _init():
connect('got_error', on_serial_got_error)
func on_serial_got_error(where: String, what: String):
if where not in failed:
push_error([where, what, unit_instance])
failed[where] = what
func on_serial_data(data: PackedByteArray):
failed.clear()
unit_instance.parse(data, tick)
func on_settings_changed():
try_close()
var rc = try_open()
if rc != Error.OK:
log.message(log.ERROR, 'невозможно открыть %s для \"%s\"' % [self, unit_instance.name])
else:
log.message(log.INFO, '%s для \"%s\"' % [self, unit_instance.name])
func format_buffer(data: PackedByteArray):
var buffer: PackedByteArray
buffer.resize(64)
@@ -50,16 +52,16 @@ class SocketSerial extends SerialPort:
buffer.resize(i)
return buffer
func send_to(data: PackedByteArray):
func send_to(_data: PackedByteArray):
if not is_open():
try_open()
call_deferred('try_open')
return
# var buffer = format_buffer(data) # Не проверено, но должно работать
# Эта последовательность для запроса состояния, получена от разработчиков СПТ-25
const tx_tula: = [0x7e, 0x01, 0x04, 0xbb, 0x50, 0x7e]
var sz = write_raw(tx_tula)
if sz != tx_tula.size():
try_open()
call_deferred('try_close')
func _to_string() -> String: return 'последовательный порт \"%s\" @ %d (%s)' % [self.port, self.baudrate, 'открыт' if is_open() else 'закрыт']
@@ -144,6 +146,15 @@ var send_sockets: Dictionary[StringName, SocketUDP] ## Таблица <имя
var addr_port_to_unit_name: Dictionary[StringName, StringName] ## Преобразование адрес:порт в имя устройства
func on_settings_changed(serial: SocketSerial):
serial.try_close()
var rc = serial.try_open()
if rc != Error.OK:
log.message(log.ERROR, 'невозможно открыть %s для \"%s\"' % [serial.port, serial.unit_instance.name])
else:
log.message(log.INFO, 'открыт %s для \"%s\"' % [serial.port, serial.unit_instance.name])
## [param unit_name] - Уникальное имя устройства[br]
func create_socket_udp(unit_name: StringName) -> SocketUDP:
var st = settings.UnitProfiles[unit_name][1]
@@ -202,13 +213,10 @@ func create_serial(unit_instance: unit.Unit) -> SocketSerial:
serial.bytesize = st[4]
serial.stopbits = st[5]
serial.unit_instance = unit_instance
ProjectSettings.connect('settings_changed', func(): on_settings_changed(serial))
return serial
func on_serial_got_error(where: String, what: String, serial: SocketSerial, unit_instance: unit.Unit):
push_error([where, what, serial])
## [param unit_name] - Уникальное имя устройства[br]
func create_modbus(unit_name: StringName) -> sch_3.Sch3:
var unit_modbus: = sch_3.Sch3.new(unit_name)
@@ -286,8 +294,6 @@ func _ready() -> void:
if proto in SERIAL_PROTO:
var unit_instance = SERIAL_PROTO[proto].new(unit_name)
var serial: = create_serial(unit_instance)
serial.connect('got_error', on_serial_got_error.bind(serial, unit_instance))
ProjectSettings.connect('settings_changed', serial.on_settings_changed)
units_serial[unit_name] = unit_instance
units[unit_name] = unit_instance
serials[unit_name] = serial

View File

@@ -84,7 +84,6 @@ class Trassa extends unit.Unit:
sector_draw(json_dic)
emit_signal('data_received', self)
else:
var msg = json_conv.get_error_message()
emit_signal('parse_failed', self)
else:
emit_signal('parse_failed', self)
@@ -110,7 +109,6 @@ class Trassa extends unit.Unit:
var hfb = int(data["hfb"])
var dap = int(data["dap"])
var daa = int(data["daa"])
var key = str(lfb) + "_" + str(hfb)
for i in range(set_klaster.size()):
var mapping = set_klaster[i]
if lfb == mapping[0] and hfb == mapping[1]:

View File

@@ -3,10 +3,9 @@ const float DS = 0.004; /* Относитель
float random_timed(vec2 uv)
{
return fract(sin(TIME * dot(uv.xy, vec2(12.9898, 78.233))) * 43758.5453123);
return fract(sin(dot(uv + TIME, vec2(12.9898, 78.233))) * 43758.5453);
}
float smooth_px(float r, float R, float ds)
{
return 1.0 - smoothstep(R - ds, R + ds, r);