Merge remote-tracking branch 'refs/remotes/Maxim/master'

# Conflicts:
#	scenes/tilemap/tilemap.gd
#	scenes/настройки/настройки.gd
#	scenes/настройки/настройки.tscn
#	scripts/mercator.gd
#	scripts/repnetwork.gd
This commit is contained in:
lepshiy
2024-09-30 11:28:11 +03:00
48 changed files with 2688 additions and 1971 deletions

View File

@@ -14,36 +14,36 @@ const LOGGER_OUTPUT_ICON = "res://addons/godot-logger/icons/logger.svg"
func _def_settings(pname: String, default: Variant) -> void:
if not ProjectSettings.has_setting(pname):
ProjectSettings.set_setting(pname, default)
if not ProjectSettings.has_setting(pname):
ProjectSettings.set_setting(pname, default)
ProjectSettings.set_initial_value(pname, default)
ProjectSettings.set_initial_value(pname, default)
func _enter_tree() -> void:
_def_settings("plugins/logger/log_enabled", true)
_def_settings("plugins/logger/default_output", false)
_def_settings("plugins/logger/log_enabled", true)
_def_settings("plugins/logger/default_output", false)
_def_settings("plugins/logger/level", 31) # The magic number is - INFO | DEBUG | WARNING | ERROR | FATAL
ProjectSettings.add_property_info(
{
"name": "plugins/logger/level",
"type": TYPE_INT,
"hint": PROPERTY_HINT_FLAGS,
"hint_string": "Info,Debug,Warning,Error,Fatal",
}
)
_def_settings("plugins/logger/level", 31) # The magic number is - INFO | DEBUG | WARNING | ERROR | FATAL
ProjectSettings.add_property_info(
{
"name": "plugins/logger/level",
"type": TYPE_INT,
"hint": PROPERTY_HINT_FLAGS,
"hint_string": "Info,Debug,Warning,Error,Fatal",
}
)
_def_settings("plugins/logger/file/file_path", "user://uarep-ctl.log")
_def_settings("plugins/logger/file/log_file_write", true)
_def_settings("plugins/logger/file/file_path", "user://uarep-ctl.log")
_def_settings("plugins/logger/file/log_file_write", true)
_def_settings("plugins/logger/default_output_format", "[{year}-{month}-{day} {hour}:{minute}:{second}][{level}]{text}")
_def_settings("plugins/logger/file_format", "[{year}-{month}-{day} {hour}:{minute}:{second}][{level}]{text}")
_def_settings("plugins/logger/default_output_format", "[{year}-{month}-{day} {hour}:{minute}:{second}][{level}]{text}")
_def_settings("plugins/logger/file_format", "[{year}-{month}-{day} {hour}:{minute}:{second}][{level}]{text}")
add_autoload_singleton(AUTOLOAD_NAME, AUTOLOAD_PATH)
add_custom_type(LOGGER_OUTPUT, "RichTextLabel", load(LOGGER_OUTPUT_SCRIPT), load(LOGGER_OUTPUT_ICON))
add_autoload_singleton(AUTOLOAD_NAME, AUTOLOAD_PATH)
add_custom_type(LOGGER_OUTPUT, "RichTextLabel", load(LOGGER_OUTPUT_SCRIPT), load(LOGGER_OUTPUT_ICON))
func _exit_tree() -> void:
remove_custom_type(LOGGER_OUTPUT)
remove_autoload_singleton(AUTOLOAD_NAME)
remove_custom_type(LOGGER_OUTPUT)
remove_autoload_singleton(AUTOLOAD_NAME)

View File

@@ -9,12 +9,12 @@ signal logged(message: Dictionary)
enum {
INFO = 1, ## Информационный уровень.
DEBUG = 1<<1, ## Уровень предупреждения.
WARNING = 1<<2, ## Уровень отладки.
ERROR = 1<<3, ## Уровень ошибок.
FATAL = 1<<4, ## Критический уровень.
MAX = FATAL, ## Побитовый сдвиг влево для пользовательских уровней.
INFO = 1, ## Информационный уровень.
DEBUG = 1<<1, ## Уровень предупреждения.
WARNING = 1<<2, ## Уровень отладки.
ERROR = 1<<3, ## Уровень ошибок.
FATAL = 1<<4, ## Критический уровень.
MAX = FATAL, ## Побитовый сдвиг влево для пользовательских уровней.
}
@@ -31,235 +31,235 @@ var _format_default_output : String
var _format_file : String
var _names = {
INFO: "ИНФО",
DEBUG: "ОТЛАДКА",
WARNING: "ВНИМАНИЕ",
ERROR: "ОШИБКА",
FATAL: "АВАРИЯ"
INFO: "ИНФО",
DEBUG: "ОТЛАДКА",
WARNING: "ВНИМАНИЕ",
ERROR: "ОШИБКА",
FATAL: "АВАРИЯ"
}
var _msg_array: Array
var _filters: Array
## Преопределение пользовательских значений по умолчанию.
func _enter_tree() -> void:
set_log_enabled(ProjectSettings.get_setting("plugins/logger/log_enabled", true))
set_default_output_enabled(ProjectSettings.get_setting("plugins/logger/default_output", false))
set_log_enabled(ProjectSettings.get_setting("plugins/logger/log_enabled", true))
set_default_output_enabled(ProjectSettings.get_setting("plugins/logger/default_output", false))
_level = ProjectSettings.get_setting("plugins/logger/level", INFO | DEBUG | WARNING | ERROR | FATAL)
_level = ProjectSettings.get_setting("plugins/logger/level", INFO | DEBUG | WARNING | ERROR | FATAL)
set_file_path(ProjectSettings.get_setting("plugins/logger/file/file_path", "user://uarep-ctl.log"))
set_file_write_enabled(ProjectSettings.get_setting("plugins/logger/file/log_file_write", true))
set_file_path(ProjectSettings.get_setting("plugins/logger/file/file_path", "user://uarep-ctl.log"))
set_file_write_enabled(ProjectSettings.get_setting("plugins/logger/file/log_file_write", true))
_format_default_output = ProjectSettings.get_setting("plugins/logger/default_output_format", "[{year}-{month}-{day} {hour}:{minute}:{second}][{level}]{text}")
_format_file = ProjectSettings.get_setting("plugins/logger/file_format", "[{year}-{month}-{day} {hour}:{minute}:{second}][{level}]{text}")
_format_default_output = ProjectSettings.get_setting("plugins/logger/default_output_format", "[{year}-{month}-{day} {hour}:{minute}:{second}][{level}]{text}")
_format_file = ProjectSettings.get_setting("plugins/logger/file_format", "[{year}-{month}-{day} {hour}:{minute}:{second}][{level}]{text}")
func _exit_tree() -> void:
_close_file()
_close_file()
## Возвращает [param true], если у журнала события есть уровень.
func has_level(level: int) -> bool:
return _names.has(level)
return _names.has(level)
## Установить включенный уровень.
func set_level(level: int, enabled: bool) -> void:
assert(has_level(level), "Invalid level.")
if not has_level(level):
return
assert(has_level(level), "Invalid level.")
if not has_level(level):
return
if enabled:
_level |= level
else:
_level &= ~level
if enabled:
_level |= level
else:
_level &= ~level
## Добавить пользовательский уровень журнала. Значение уровня должно быть уникальным и превышать [member MAX].
## Для вызова пользовательского уровня используйте метод - [method message].
func add_level(level: int, nm: String) -> void:
assert(not has_level(level), "Has level.")
assert(level > MAX and not level % 2, "Invalid level.")
assert(name, "Invalid name.")
assert(not has_level(level), "Has level.")
assert(level > MAX and not level % 2, "Invalid level.")
assert(name, "Invalid name.")
if not has_level(level) and level > MAX and not level % MAX and not name.is_empty():
_names[level] = nm
set_level(level, true)
if not has_level(level) and level > MAX and not level % MAX and not name.is_empty():
_names[level] = nm
set_level(level, true)
## Возвращает имя уровня.
func get_level_name(level: int) -> String:
return _names[level]
return _names[level]
## Установить включенный журнал. Отключить ВСЕ сообщения.
func set_log_enabled(enabled: bool) -> void:
_log_enabled = enabled
_log_enabled = enabled
## Возвращает [param true], если журнал включен.
func is_log_enabled() -> bool:
return _log_enabled
return _log_enabled
## Установка выхода по умолчанию включена.
func set_default_output_enabled(enabled: bool) -> void:
_default_output_enabled = enabled
_default_output_enabled = enabled
## Возвращает [param true], если вывод по умолчанию включен.
func is_default_output_enabled() -> bool:
return _default_output_enabled
return _default_output_enabled
## Установить разрешение на запись файла.
func set_file_write_enabled(enabled: bool) -> void:
if _file_write_enabled == enabled:
return
if _file_write_enabled == enabled:
return
_file_write_enabled = enabled
_file_write_enabled = enabled
if enabled:
_open_file()
else:
_close_file()
if enabled:
_open_file()
else:
_close_file()
## Возвращает [param true], если запись в файл разрешена.
func is_file_write_enabled() -> bool:
return _file_write_enabled
return _file_write_enabled
## Задать путь к файлу журнала.
func set_file_path(path: String) -> void:
assert(path.is_absolute_path(), "Invalid path.")
if not path.is_absolute_path() or _file_path == path:
return
assert(path.is_absolute_path(), "Invalid path.")
if not path.is_absolute_path() or _file_path == path:
return
_file_path = path
_file_path = path
if is_file_write_enabled():
_open_file()
if is_file_write_enabled():
_open_file()
## Возвращает путь к файлу журнала.
func get_file_path() -> String:
return _file_path
return _file_path
## Создать информационное сообщение.
func info(text: String) -> void:
message(INFO, text)
message(INFO, text)
## Создать отладочное сообщение. Только для отладочной сборки.
func debug(text: String) -> void:
if OS.is_debug_build():
message(DEBUG, text)
if OS.is_debug_build():
message(DEBUG, text)
## Создать предупреждающее сообщение.
func warning(text: String) -> void:
message(WARNING, text)
message(WARNING, text)
## Создать сообщение об ошибке.
func error(text: String) -> void:
message(ERROR, text)
message(ERROR, text)
## Создать сообщение о критической ошибке.
func fatal(text: String) -> void:
message(FATAL, text)
message(FATAL, text)
## Форматировать строку для файла.
func format_file(msg: Dictionary) -> String:
return _format_file.format(
{
"year": "%02d" % msg["year"],
"month": "%02d" % msg["month"],
"day": "%02d" % msg["day"],
"hour": "%02d" % msg["hour"],
"minute":"%02d" % msg["minute"],
"second":"%02d" % msg["second"],
"level": _names[msg["level"]],
"text": msg["text"],
}
)
return _format_file.format(
{
"year": "%02d" % msg["year"],
"month": "%02d" % msg["month"],
"day": "%02d" % msg["day"],
"hour": "%02d" % msg["hour"],
"minute":"%02d" % msg["minute"],
"second":"%02d" % msg["second"],
"level": _names[msg["level"]],
"text": msg["text"],
}
)
## Форматировать строку для вывода редактора.
func format_stdout(msg: Dictionary) -> String:
return _format_default_output.format(
{
"year": "%02d" % msg["year"],
"month": "%02d" % msg["month"],
"day": "%02d" % msg["day"],
"hour": "%02d" % msg["hour"],
"minute":"%02d" % msg["minute"],
"second":"%02d" % msg["second"],
"level": _names[msg["level"]],
"text": msg["text"],
}
)
return _format_default_output.format(
{
"year": "%02d" % msg["year"],
"month": "%02d" % msg["month"],
"day": "%02d" % msg["day"],
"hour": "%02d" % msg["hour"],
"minute":"%02d" % msg["minute"],
"second":"%02d" % msg["second"],
"level": _names[msg["level"]],
"text": msg["text"],
}
)
## Создать сообщение с пользовательским уровнем.
func message(level: int, text: String) -> void:
assert(has_level(level), "Has not level.")
assert(text, "Invalid message text.")
assert(has_level(level), "Has not level.")
assert(text, "Invalid message text.")
if _log_enabled and _level & level:
var msg : Dictionary = Time.get_datetime_dict_from_system()
msg["level"] = level
msg["level_name"] = _names[level]
msg["text"] = " " + text
if _log_enabled and _level & level:
var msg : Dictionary = Time.get_datetime_dict_from_system()
msg["level"] = level
msg["level_name"] = _names[level]
msg["text"] = " " + text
_msg_array.append(msg)
if (_flt_msg(msg)):
logged.emit(msg)
_msg_array.append(msg)
if (_flt_msg(msg)):
logged.emit(msg)
if _file_write_enabled:
_file.store_line(format_file(msg))
_file.call_deferred('flush')
if _file_write_enabled:
_file.store_line(format_file(msg))
_file.call_deferred('flush')
if _default_output_enabled:
print(format_stdout(msg))
if _default_output_enabled:
print_debug(format_stdout(msg))
func _open_file() -> void:
if is_instance_valid(_file) and _file.is_open():
_file.close()
if is_instance_valid(_file) and _file.is_open():
_file.close()
_file = FileAccess.open(get_file_path(), FileAccess.READ_WRITE)
_file = FileAccess.open(get_file_path(), FileAccess.READ_WRITE)
var err := FileAccess.get_open_error()
if err:
_file = FileAccess.open(get_file_path(), FileAccess.WRITE)
_file.close()
_file = FileAccess.open(get_file_path(), FileAccess.READ_WRITE)
return print_debug(error_string(err))
var err := FileAccess.get_open_error()
if err:
_file = FileAccess.open(get_file_path(), FileAccess.WRITE)
_file.close()
_file = FileAccess.open(get_file_path(), FileAccess.READ_WRITE)
return print_debug(error_string(err))
err = _file.get_error()
if err:
return print_debug(error_string(err))
_file.seek_end(0)
err = _file.get_error()
if err:
return print_debug(error_string(err))
_file.seek_end(0)
func _close_file() -> void:
if is_instance_valid(_file) and _file.is_open():
_file = null
if is_instance_valid(_file) and _file.is_open():
_file = null
func save_file(path):
var f = FileAccess.open(path, FileAccess.WRITE)
var err := FileAccess.get_open_error()
if err:
print_debug(error_string(err))
var log_text = _file.get_as_text()
f.store_line(log_text)
f.close()
var f = FileAccess.open(path, FileAccess.WRITE)
var err := FileAccess.get_open_error()
if err:
print_debug(error_string(err))
var log_text = _file.get_as_text()
f.store_line(log_text)
f.close()
func set_filtrs(flt_array: Array):
_filters = flt_array
_filters = flt_array
func _flt_msg(msg):
var flt_arr: Array = []
for item in _filters:
if item.button_pressed:
flt_arr.append(item.get_meta('filter'))
if msg['text'].find(item.get_meta('filter')) != -1:
return true
if msg['level_name'] in flt_arr:
return true
return false
var flt_arr: Array = []
for item in _filters:
if item.button_pressed:
flt_arr.append(item.get_meta('filter'))
if msg['text'].find(item.get_meta('filter')) != -1:
return true
if msg['level_name'] in flt_arr:
return true
return false
func add_msgs():
for msg in _msg_array:
if (_flt_msg(msg)):
logged.emit(msg)
for msg in _msg_array:
if (_flt_msg(msg)):
logged.emit(msg)

View File

@@ -11,11 +11,11 @@ signal handled()
@export var format_text : String = "[{year}-{month}-{day} {hour}:{minute}:{second}][{level}]{text}"
@export var colors : Dictionary = {
Logger.INFO: Color.WHITE,
Logger.DEBUG: Color.GRAY,
Logger.WARNING: Color.YELLOW,
Logger.ERROR: Color.RED,
Logger.FATAL: Color.RED,
Logger.INFO: Color.WHITE,
Logger.DEBUG: Color.GRAY,
Logger.WARNING: Color.YELLOW,
Logger.ERROR: Color.RED,
Logger.FATAL: Color.RED,
}
@@ -23,55 +23,55 @@ var _logger : Logger
func _enter_tree() -> void:
var logger := get_node_or_null("/root/log") as Logger
if is_instance_valid(logger):
set_logger(logger)
var logger := get_node_or_null("/root/log") as Logger
if is_instance_valid(logger):
set_logger(logger)
func _exit_tree() -> void:
if is_instance_valid(_logger) and _logger.logged.is_connected(handle_message):
_logger.logged.disconnect(handle_message)
if is_instance_valid(_logger) and _logger.logged.is_connected(handle_message):
_logger.logged.disconnect(handle_message)
## Вернуть цвет уровня или [member Color.WHITE].
func get_color(level: int) -> Color:
return colors.get(level, Color.WHITE)
return colors.get(level, Color.WHITE)
## Установить журнал событий.
func set_logger(logger: Logger) -> void:
assert(is_instance_valid(logger), "Invalid Logger.")
if not is_instance_valid(logger) or is_same(_logger, logger):
return
assert(is_instance_valid(logger), "Invalid Logger.")
if not is_instance_valid(logger) or is_same(_logger, logger):
return
if is_instance_valid(_logger) and _logger.logged.is_connected(handle_message):
_logger.logged.disconnect(handle_message)
if is_instance_valid(_logger) and _logger.logged.is_connected(handle_message):
_logger.logged.disconnect(handle_message)
if not logger.logged.is_connected(handle_message):
logger.logged.connect(handle_message)
if not logger.logged.is_connected(handle_message):
logger.logged.connect(handle_message)
_logger = logger
_logger = logger
## Вернуть журнал событий.
func get_logger() -> Logger:
return _logger
return _logger
## Вернуть строку после форматирования из сообщения.
func format(message: Dictionary) -> String:
return format_text.format(
{
"year": "%02d" % message["year"],
"month": "%02d" % message["month"],
"day": "%02d" % message["day"],
"hour": "%02d" % message["hour"],
"minute": "%02d" % message["minute"],
"second": "%02d" % message["second"],
"level": message["level_name"],
"text": message["text"],
}
)
return format_text.format(
{
"year": "%02d" % message["year"],
"month": "%02d" % message["month"],
"day": "%02d" % message["day"],
"hour": "%02d" % message["hour"],
"minute": "%02d" % message["minute"],
"second": "%02d" % message["second"],
"level": message["level_name"],
"text": message["text"],
}
)
## Обработать сообщение.
func handle_message(message: Dictionary) -> void:
push_color(get_color(message["level"]))
append_text(format(message) + "\n")
pop()
handled.emit()
push_color(get_color(message["level"]))
append_text(format(message) + "\n")
pop()
handled.emit()

24
build.sh Normal file
View File

@@ -0,0 +1,24 @@
#!/usr/bin/env bash
#
# Описание
#
# Этот скрипт создаёт исполняемый файл (загружаемый модуль) из файла проекта Godot
#
# Имя выходного исполняемого файла (загружаемый модуль)
TARGET_BIN="uarep-ctl.linux.x86_64"
# Целевой путь папки в которой будет расположен исполняемый файл (загружаемый модуль)
TARGET_PATH="./bin"
# Путь к исполняемому файлу редактора Godot
EDITOR="/opt/godot/Godot_v4.2.2-stable_linux.x86_64"
# Если используется своя сборка движка, то соответствующий файл
# (исполняемый модуль движка, шаблон экспорта) нужно заменить
# в этой папке: "~/.local/share/godot/export_templates/4.2.2.stable/"
# Путь к файлу проекта Godot
PROJECT_PATH="$(pwd)"
${EDITOR} --path "${PROJECT_PATH}" --export-release "Linux-X11" "${TARGET_PATH}/${TARGET_BIN}"

View File

@@ -11,6 +11,7 @@ cache/0/16/0/underline_thickness = 0.0
cache/0/16/0/scale = 1.0
cache/0/16/0/kerning_overrides/16/0 = Vector2(0, 0)
cache/0/16/0/kerning_overrides/14/0 = Vector2(0, 0)
cache/0/16/0/kerning_overrides/50/0 = Vector2(0, 0)
cache/0/14/0/ascent = 0.0
cache/0/14/0/descent = 0.0
cache/0/14/0/underline_position = 0.0
@@ -18,3 +19,12 @@ cache/0/14/0/underline_thickness = 0.0
cache/0/14/0/scale = 1.0
cache/0/14/0/kerning_overrides/16/0 = Vector2(0, 0)
cache/0/14/0/kerning_overrides/14/0 = Vector2(0, 0)
cache/0/14/0/kerning_overrides/50/0 = Vector2(0, 0)
cache/0/50/0/ascent = 0.0
cache/0/50/0/descent = 0.0
cache/0/50/0/underline_position = 0.0
cache/0/50/0/underline_thickness = 0.0
cache/0/50/0/scale = 1.0
cache/0/50/0/kerning_overrides/16/0 = Vector2(0, 0)
cache/0/50/0/kerning_overrides/14/0 = Vector2(0, 0)
cache/0/50/0/kerning_overrides/50/0 = Vector2(0, 0)

View File

@@ -26,3 +26,4 @@ HEAD="$(git rev-parse --abbrev-ref HEAD) $(git rev-parse --short HEAD) $(git log
echo "extends Node" > ${GDSCRIPT_FILE}
echo "# Файл сгенерирован автоматически! Не изменять вручную!" >> ${GDSCRIPT_FILE}
echo "const VCS_HEAD = '${HEAD}'" >> ${GDSCRIPT_FILE}

7
git_hooks_run.sh Normal file
View File

@@ -0,0 +1,7 @@
#!/usr/bin/env bash
echo ./git-hook.sh> ./.git/hooks/post-checkout
echo ./git-hook.sh> ./.git/hooks/post-commit
echo ./git-hook.sh> ./.git/hooks/post-merge
./git-hook.sh

View File

@@ -24,9 +24,7 @@ config/map_tile_size=256
config/map_minimum_zoom=5
config/map_maximum_zoom=19
config/map_server_addr="https://b.tile.openstreetmap.org"
config/map_image_size_rto=Vector2i(512, 512)
config/map_image_size_rls=Vector2i(1024, 1024)
interfer/center_offset=Vector2(0, 25)
interfer/center_offset=Vector2(200, 225)
interfer/default_color0=Vector3(0.38, 0.47, 0.51)
interfer/default_color1=Vector3(0.39, 0.44, 0.5)
interfer/select_color=Vector3(1, 1, 1)
@@ -89,12 +87,55 @@ window/vsync/vsync_mode=false
[editor]
movie_writer/mjpeg_quality=0.5
movie_writer/fps=24
version_control/autoload_on_startup=true
[editor_plugins]
enabled=PackedStringArray()
[file_customization]
folder_colors={
"res://addons/": "blue",
"res://data/": "yellow",
"res://modbus/": "blue",
"res://scenes/": "teal",
"res://scenes/bip/": "blue",
"res://scenes/button-flat/": "blue",
"res://scenes/button-scale/": "blue",
"res://scenes/cell/": "blue",
"res://scenes/count-all/": "blue",
"res://scenes/count-danger/": "blue",
"res://scenes/diagram/": "blue",
"res://scenes/frame-ecm-list/": "blue",
"res://scenes/frame-ecm-params/": "blue",
"res://scenes/frame-params/": "blue",
"res://scenes/frame-threats/": "blue",
"res://scenes/frame/": "blue",
"res://scenes/grid-buttons-ecm/": "blue",
"res://scenes/grid-side-panel/": "blue",
"res://scenes/pribor-afsp/": "purple",
"res://scenes/pribor-prd-k/": "purple",
"res://scenes/pribor-prd-n/": "purple",
"res://scenes/pribor-prd-v/": "purple",
"res://scenes/pribor-rtr/": "purple",
"res://scenes/pribor-uf/": "purple",
"res://scenes/pribor-uyep/": "purple",
"res://scenes/tabs-switch/": "blue",
"res://scenes/tilemap/": "blue",
"res://scenes/журнал/": "green",
"res://scenes/контроль/": "green",
"res://scenes/настройки/": "green",
"res://scenes/работа/": "green",
"res://scenes/эмс/": "green",
"res://scenes/эмс2/": "green",
"res://scripts/": "pink",
"res://shaders/": "pink",
"res://table/": "blue"
}
[global]
map_server_addr=false
@@ -152,7 +193,6 @@ common/enable_object_picking=false
renderer/rendering_method="gl_compatibility"
renderer/rendering_method.web="mobile"
gl_compatibility/item_buffer_size=8192
shader_compiler/shader_cache/strip_debug=true
textures/default_filters/anisotropic_filtering_level=0
anti_aliasing/screen_space_roughness_limiter/enabled=false

View File

@@ -4,8 +4,8 @@ var selected: bool
## Функция присваивает заданое значение видимости селектору.
func set_sel_visible(value: bool):
selected = value
$selector.visible = value
selected = value
$selector.visible = value
## Функция присваивает заданое значение видимости селектору.
@@ -15,11 +15,15 @@ func set_ecm_visible(value: bool): $ecm_done.visible = value
func _on_bip_gui_input(event):
if event is InputEventMouseButton and event.pressed:
match event.button_index:
MOUSE_BUTTON_LEFT:
signaller.emit_signal('bip_pressed', name.to_int())
MOUSE_BUTTON_RIGHT:
if selected:
signaller.emit_signal('rto_threat_unsel', name.to_int())
if event is InputEventMouseButton and event.pressed:
match event.button_index:
MOUSE_BUTTON_LEFT:
signaller.emit_signal('bip_pressed', name.to_int())
MOUSE_BUTTON_RIGHT:
if selected:
signaller.emit_signal('rto_threat_unsel', name.to_int())
func update():
$bip.material.set('shader_parameter/tick_update', float(Time.get_ticks_msec()/1000))

View File

@@ -6,9 +6,7 @@
[sub_resource type="ShaderMaterial" id="ShaderMaterial_2dulj"]
shader = ExtResource("2_rt0la")
shader_parameter/color = Color(0.972549, 0.12549, 0.411765, 1)
shader_parameter/color_up = Color(1, 1, 1, 1)
shader_parameter/tick_update = 0
shader_parameter/tick_update = 0.0
[sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_w5pye"]

View File

@@ -64,7 +64,6 @@ texture = SubResource("PlaceholderTexture2D_6nup5")
centered = false
script = SubResource("GDScript_uyl5d")
speed = 0.0
disabled = true
[node name="state" type="NinePatchRect" parent="."]
layout_mode = 2

View File

@@ -12,66 +12,66 @@ func is_toggle_mode(): return $button.is_toggle_mode()
@export var texture_state0: Texture2D = preload('res://data/кнопка-квадрат-0.png'):
set(val): $state.set_texture(val)
set(val): $state.set_texture(val)
@export var texture_state1: Texture2D = preload('res://data/кнопка-квадрат-1.png'):
set(val): $state.set_texture(val)
set(val): $state.set_texture(val)
@export var strips_rotation: float = 0.0:
set(val):
strips_rotation = val
$back.strips_rotation = deg_to_rad(val)
get: return strips_rotation
set(val):
strips_rotation = val
$back.strips_rotation = deg_to_rad(val)
get: return strips_rotation
@export var text: String = '':
set(val): $button.set_text(val)
get: return $button.get_text()
set(val): $button.set_text(val)
get: return $button.get_text()
@export var disabled: bool = false:
set(val):
disabled = val
$button.set_disabled(val)
$back.disabled = val
$state.modulate.a = 0.5 if val else 1.0
get: return disabled
set(val):
disabled = val
$button.set_disabled(val)
$back.disabled = val
$state.modulate.a = 0.5 if val else 1.0
get: return disabled
@export var pressed: bool = false:
set(val):
pressed = val
$button.set_pressed(val)
get:
return pressed
set(val):
pressed = val
$button.set_pressed(val)
get:
return pressed
@export var toggle_mode: bool = false:
set(val):
toggle_mode = val
$button.set_toggle_mode(val)
get:
return toggle_mode
set(val):
toggle_mode = val
$button.set_toggle_mode(val)
get:
return toggle_mode
func _on_resized() -> void:
$back.scale = size
$back.material.set('shader_parameter/scale', size / Vector2(20.0, 20.0))
$back.scale = size
$back.material.set('shader_parameter/scale', size / Vector2(20.0, 20.0))
func _enter_tree() -> void:
$back.scale = size
$back.material.set('shader_parameter/scale', size / Vector2(20.0, 20.0))
$button.set_toggle_mode(true)
$button.connect('toggled', Callable(self, '_on_button_toggled'))
$back.scale = size
$back.material.set('shader_parameter/scale', size / Vector2(20.0, 20.0))
$button.set_toggle_mode(true)
$button.connect('toggled', Callable(self, '_on_button_toggled'))
func set_toggle_mode(val: bool):
$button.set_toggle_mode(val)
$button.set_toggle_mode(val)
func _on_button_toggled(val: bool):
if val: $state.set_texture(texture_state1)
else: $state.set_texture(texture_state0)
if val: $state.set_texture(texture_state1)
else: $state.set_texture(texture_state0)

View File

@@ -1,5 +1,5 @@
extends Control
@export var text: String = '':
set(val): $label.set_text(val)
get: return $label.get_text()
set(val): $label.set_text(val)
get: return $label.get_text()

View File

@@ -3,8 +3,8 @@ class_name Diagram extends Sprite2D
var fs_default_colors: Array ## Цвета по умолчанию для блоков ФС.
func _ready() -> void:
signaller.connect('map_mode_changed', Callable(self, 'set_view_mode'))
set_def_colors()
signaller.connect('map_mode_changed', Callable(self, 'set_view_mode'))
set_def_colors()
func sv(val: float) -> float: return val / ($строб.scale.x / 2.0) * 0.125
@@ -23,22 +23,22 @@ func get_strob_width() -> float: return $строб.material.get('shader_par
func get_strob_band() -> float: return $строб.material.get('shader_parameter/band') * 125.0
func get_strob_radius() -> float: return $строб.material.get('shader_parameter/radius') * 125.0
func get_radius_outter() -> float: return material.get('shader_parameter/radius_outter')
func get_radius_inner_0() -> float: return material.get('shader_parameter/radius_inner_0')
func get_radius_inner_1() -> float: return material.get('shader_parameter/radius_inner_1')
func get_radius_outter() -> float: return material.get('shader_parameter/radius_outter')
func get_radius_inner_0() -> float: return material.get('shader_parameter/radius_inner_0')
func get_radius_inner_1() -> float: return material.get('shader_parameter/radius_inner_1')
func set_view_mode(val: int): material.set('shader_parameter/mode', val);
func set_band_colors(val: Array): material.set('shader_parameter/ant_band_c', val)
## Закрашивание по умолчанию блоков ФС цветами из настроек.
func set_def_colors():
var color0: Vector3 = ProjectSettings.get_setting('application/interfer/default_color0', Vector3(0.38, 0.47, 0.51))
var color1: Vector3 = ProjectSettings.get_setting('application/interfer/default_color1', Vector3(0.39, 0.44, 0.50))
var col_arr: Array = [color0, color1]
var col = false
for i in range(7):
for j in range (4):
fs_default_colors.append(col_arr[int(col)])
col = not col
col = not col
set_band_colors(fs_default_colors)
var color0: Vector3 = ProjectSettings.get_setting('application/interfer/default_color0', Vector3(0.38, 0.47, 0.51))
var color1: Vector3 = ProjectSettings.get_setting('application/interfer/default_color1', Vector3(0.39, 0.44, 0.50))
var col_arr: Array = [color0, color1]
var col = false
for i in range(7):
for j in range (4):
fs_default_colors.append(col_arr[int(col)])
col = not col
col = not col
set_band_colors(fs_default_colors)

View File

@@ -3,10 +3,10 @@ class_name frm_ecm_list extends NinePatchRect
## Called when the node enters the scene tree for the first time.
func _ready():
$grd_forced/btn_cancel.button_connect('pressed', Callable(self, 'on_btn_cancel_pressed').bind($grd_forced/btn_cancel))
$grd_forced/btn_cancel.button_connect('pressed', Callable(self, 'on_btn_cancel_pressed').bind($grd_forced/btn_cancel))
func on_btn_cancel_pressed(btn):
signaller.emit_signal('interfer_off_all')
await get_tree().create_timer(0.1).timeout
btn.pressed = false
signaller.emit_signal('interfer_off_all')
await get_tree().create_timer(0.1).timeout
btn.pressed = false

View File

@@ -55,6 +55,7 @@ layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
tooltip_text = "Включает принудительную помеху"
strips_rotation = 15.0
text = "СРН"
metadata/rfi_name = "ozp1"
@@ -63,6 +64,7 @@ editor_description = "Кнопка для принудительного вкл
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
strips_rotation = 15.0
text = "СПРС"
metadata/rfi_name = "ozp1"
@@ -71,6 +73,7 @@ layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
tooltip_text = "Кнопка для принудительного включения группы секторов в режим КУПД"
strips_rotation = 15.0
text = "КУПД"
metadata/rfi_name = "ozp1"
@@ -78,5 +81,6 @@ metadata/rfi_name = "ozp1"
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
strips_rotation = 15.0
text = "Отключить все"
metadata/rfi_name = "<off>"

View File

@@ -84,7 +84,6 @@ func _ready():
interfer.connect('interfer_update', Callable(self, 'on_interfer_update').bind(table_index))
$table.connect('row_pressed', Callable(self, 'on_row_pressed'))
call_deferred('init_table')
#$btn_cancel.button_connect('pressed', Callable(self, 'on_btn_cancel_pressed').bind($btn_cancel))
func init_table():

View File

@@ -11,82 +11,82 @@ const TABLE_HEADERS_TEXT = ['Параметр', 'Значение' ] ##
const TABLE_COLUMN_SIZE = [ 370, 85 ] ## Ширины колонок.
const TABLE_PARAMS = {
'kni': [0, 'Курсовой угол, гр.', false, 0.0, 360.0],
'powp': [1, 'Мощность, %', true, 0.0, 100.0],
'freq': [2, 'Частота, МГц', true, 400.0, 6000.0],
'width': [3, 'Занимая полоса частот, МГц', true, 0.0, 120.0],
'dur': [4, 'Длительность импульса, мкс', true, 0.0, 1000000.0],
'period': [5, 'Период повтора, мкс', true, 0.0, 1000000.0] }
'kni': [0, 'Курсовой угол, гр.', false, 0.0, 360.0],
'powp': [1, 'Мощность, %', true, 0.0, 100.0],
'freq': [2, 'Частота, МГц', true, 400.0, 6000.0],
'width': [3, 'Занимая полоса частот, МГц', true, 0.0, 120.0],
'dur': [4, 'Длительность импульса, мкс', true, 0.0, 1000000.0],
'period': [5, 'Период повтора, мкс', true, 0.0, 1000000.0] }
func init_table():
for i in TABLE_PARAMS.size(): $table.add_row(TABLE_ROW)
for key in TABLE_PARAMS.keys():
var p = TABLE_PARAMS[key]
$table.set_node_text(0, p[0], p[1])
$table.set_node_alligment(0, p[0], HORIZONTAL_ALIGNMENT_LEFT)
$table.set_node_editable(0, p[0], false)
$table.set_node_editable(1, p[0], false)
$table.set_node_user_data(1, p[0], key)
$table.connect_columns(self, 'text_submitted', 'on_edit_entry', [1], [])
$table.set_columns_min_size(TABLE_COLUMN_SIZE)
$table.set_meta('id', 0)
for i in TABLE_PARAMS.size(): $table.add_row(TABLE_ROW)
for key in TABLE_PARAMS.keys():
var p = TABLE_PARAMS[key]
$table.set_node_text(0, p[0], p[1])
$table.set_node_alligment(0, p[0], HORIZONTAL_ALIGNMENT_LEFT)
$table.set_node_editable(0, p[0], false)
$table.set_node_editable(1, p[0], false)
$table.set_node_user_data(1, p[0], key)
$table.connect_columns(self, 'text_submitted', 'on_edit_entry', [1], [])
$table.set_columns_min_size(TABLE_COLUMN_SIZE)
$table.set_meta('id', 0)
## Called when the node enters the scene tree for the first time.
func _ready():
signaller.connect('interfer_selected', Callable(self, 'on_interfer_selected'))
signaller.connect('rto_threat_unsel', Callable(self, 'on_rto_threat_unsel'))
call_deferred('init_table')
signaller.connect('interfer_selected', Callable(self, 'on_interfer_selected'))
signaller.connect('rto_threat_unsel', Callable(self, 'on_rto_threat_unsel'))
call_deferred('init_table')
func on_edit_entry(text : String, entry):
var ecm = $table.get_meta('ecm')
if $table.has_meta('ecm') == false: return
var indexis = $table.get_key_indexes(entry[0].name)
var row_i = indexis[1]
var param = $table.get_node_user_data(1, row_i)
var p = TABLE_PARAMS[param]
var old_val = ecm.get_value(param)
var val = check_value(text, p[3], p[4], old_val)
ecm.set_value(val, param)
signaller.emit_signal('interfer_new', ecm)
var ecm = $table.get_meta('ecm')
if $table.has_meta('ecm') == false: return
var indexis = $table.get_key_indexes(entry[0].name)
var row_i = indexis[1]
var param = $table.get_node_user_data(1, row_i)
var p = TABLE_PARAMS[param]
var old_val = ecm.get_value(param)
var val = check_value(text, p[3], p[4], old_val)
ecm.set_value(val, param)
signaller.emit_signal('interfer_new', ecm)
func check_value(val, min_val, max_val, old_val):
if val.is_valid_float():
if float(val) < min_val: return min_val
if float(val) > max_val: return max_val
return float(val)
else: return old_val
if val.is_valid_float():
if float(val) < min_val: return min_val
if float(val) > max_val: return max_val
return float(val)
else: return old_val
func on_interfer_selected(ecm):
if ecm != null:
$table.set_meta('ecm', ecm)
else:
$table.set_meta('ecm', null)
on_ecm_update()
if ecm != null:
$table.set_meta('ecm', ecm)
else:
$table.set_meta('ecm', null)
on_ecm_update()
func on_ecm_update():
for item in len(TABLE_PARAMS):
$table.set_node_text(1, item, '')
$table.set_node_editable(1, item, false)
if $table.has_meta('ecm') == false: return
var ecm: interfer.Interfer = $table.get_meta('ecm')
var mnms = tools.get_members_names(ecm)
for item in ecm['params'].keys():
mnms.append(item)
for member_name in mnms:
if not TABLE_PARAMS.has(member_name): continue
var p = TABLE_PARAMS[member_name]
var val = ecm.get_value(member_name)
$table.set_node_text(1, p[0], '%s' % val)
$table.set_node_editable(1, p[0], p[2])
for item in len(TABLE_PARAMS):
$table.set_node_text(1, item, '')
$table.set_node_editable(1, item, false)
if $table.has_meta('ecm') == false: return
var ecm: interfer.Interfer = $table.get_meta('ecm')
var mnms = tools.get_members_names(ecm)
for item in ecm['params'].keys():
mnms.append(item)
for member_name in mnms:
if not TABLE_PARAMS.has(member_name): continue
var p = TABLE_PARAMS[member_name]
var val = ecm.get_value(member_name)
$table.set_node_text(1, p[0], '%s' % val)
$table.set_node_editable(1, p[0], p[2])
func on_rto_threat_unsel(_th_id):
for item in range(len(TABLE_PARAMS)):
$table.set_node_text(1, item, '')
$table.set_node_editable(1, item, false)
for item in range(len(TABLE_PARAMS)):
$table.set_node_text(1, item, '')
$table.set_node_editable(1, item, false)

View File

@@ -15,51 +15,51 @@ const TABLE_COLUMN_SIZE = [ 370, 85 ] ## Ширины к
## Параметры цели для отображения в таблице.[br]
## [b]val[0][/b] - Индекс строки.[br][b]val[1][/b] - Содержание.
const TABLE_PARAMS = {
'baud': [0, 'Модуляционная скорость, бод'],
'freqm': [1, 'Режим смены частоты.'],
'power': [2, 'Уровень сигнала, дБм.'],
'tmod': [3, 'Тип модуляции'],
'width': [4, 'Занимая полоса частот, МГц.'],
'rkrp': [5, 'Рекомендуемая помеха'] }
'baud': [0, 'Модуляционная скорость, бод'],
'freqm': [1, 'Режим смены частоты.'],
'power': [2, 'Уровень сигнала, дБм.'],
'tmod': [3, 'Тип модуляции'],
'width': [4, 'Занимая полоса частот, МГц.'],
'rkrp': [5, 'Рекомендуемая помеха'] }
## Обрабатывает сигнал [signal threats.threat_selected].
func on_threat_selected(th):
$table.set_meta('id', th.id)
on_threat_update(th)
$table.set_meta('id', th.id)
on_threat_update(th)
func init_table():
for i in TABLE_PARAMS.size(): $table.add_row(TABLE_ROW)
for p in TABLE_PARAMS.values():
$table.set_node_text(0, p[0], p[1])
$table.set_node_alligment(0, p[0], HORIZONTAL_ALIGNMENT_LEFT)
$table.set_node_editable(0, p[0], false)
$table.set_node_editable(1, p[0], false)
$table.set_columns_min_size(TABLE_COLUMN_SIZE)
$table.set_meta('id', 0)
for i in TABLE_PARAMS.size(): $table.add_row(TABLE_ROW)
for p in TABLE_PARAMS.values():
$table.set_node_text(0, p[0], p[1])
$table.set_node_alligment(0, p[0], HORIZONTAL_ALIGNMENT_LEFT)
$table.set_node_editable(0, p[0], false)
$table.set_node_editable(1, p[0], false)
$table.set_columns_min_size(TABLE_COLUMN_SIZE)
$table.set_meta('id', 0)
func _ready() -> void:
signaller.connect('threat_selected', Callable(self, 'on_threat_selected'))
signaller.connect('rto_threat_unsel', Callable(self, 'on_rto_threat_unsel'))
threats.connect('threat_update', Callable(self, 'on_threat_update'))
call_deferred('init_table')
signaller.connect('threat_selected', Callable(self, 'on_threat_selected'))
signaller.connect('rto_threat_unsel', Callable(self, 'on_rto_threat_unsel'))
threats.connect('threat_update', Callable(self, 'on_threat_update'))
call_deferred('init_table')
## Обрабатывает сигнал [signal threats.threat_update].
## Обновляет отображение только для выбранной цели.
func on_threat_update(th):
if th.selected == false: return
if th.id != $table.get_meta('id'): return
var mnms = tools.get_members_names(th)
for member_name in mnms:
if not TABLE_PARAMS.has(member_name): continue
var p = TABLE_PARAMS[member_name]
var val = th.get(member_name)
$table.set_node_text(1, p[0], '%s' % val)
if th.selected == false: return
if th.id != $table.get_meta('id'): return
var mnms = tools.get_members_names(th)
for member_name in mnms:
if not TABLE_PARAMS.has(member_name): continue
var p = TABLE_PARAMS[member_name]
var val = th.get(member_name)
$table.set_node_text(1, p[0], '%s' % val)
func on_rto_threat_unsel(_th_id):
for item in range(len(TABLE_PARAMS)):
$table.set_node_text(1, item, '')
for item in range(len(TABLE_PARAMS)):
$table.set_node_text(1, item, '')

View File

@@ -16,87 +16,87 @@ const TABLE_COLUMN_SIZE = [ 65, 145, 145, 90 ]
## Вызывается при нажатии указателем мыши на выбранный ряд.
func on_row_pressed(i_row: int):
row_pressed(i_row)
signaller.emit_signal('rto_threat_sel', $table.get_node_user_data(0, i_row).id)
row_pressed(i_row)
signaller.emit_signal('rto_threat_sel', $table.get_node_user_data(0, i_row).id)
func row_pressed(i_row: int):
$table.clear_rows_selected()
$table.set_row_selected(i_row, true)
$table.ensure_row_visible(i_row)
var threat = $table.get_node_user_data(0, i_row)
signaller.emit_signal('threat_selected', threat)
$table.clear_rows_selected()
$table.set_row_selected(i_row, true)
$table.ensure_row_visible(i_row)
var threat = $table.get_node_user_data(0, i_row)
signaller.emit_signal('threat_selected', threat)
func init_table():
$table.set_header(TABLE_HEADER)
$table.set_header_text(TABLE_HEADERS_TEXT)
$table.set_columns_min_size(TABLE_COLUMN_SIZE)
$table.pin_header(self, get_parent())
$table.set_header(TABLE_HEADER)
$table.set_header_text(TABLE_HEADERS_TEXT)
$table.set_columns_min_size(TABLE_COLUMN_SIZE)
$table.pin_header(self, get_parent())
func _ready() -> void:
var table_index: = Dictionary()
threats.connect('threat_new', Callable(self, 'on_threat_new').bind(table_index))
threats.connect('threat_lost', Callable(self, 'on_threat_lost').bind(table_index))
threats.connect('threat_update', Callable(self, 'on_threat_update').bind(table_index))
signaller.connect('rto_threat_sel', Callable(self, 'on_rto_threat_sel').bind($table))
signaller.connect('rto_threat_unsel', Callable(self, 'on_rto_threat_unsel'))
$table.connect('row_pressed', Callable(self, 'on_row_pressed'))
call_deferred('init_table')
var table_index: = Dictionary()
threats.connect('threat_new', Callable(self, 'on_threat_new').bind(table_index))
threats.connect('threat_lost', Callable(self, 'on_threat_lost').bind(table_index))
threats.connect('threat_update', Callable(self, 'on_threat_update').bind(table_index))
signaller.connect('rto_threat_sel', Callable(self, 'on_rto_threat_sel').bind($table))
signaller.connect('rto_threat_unsel', Callable(self, 'on_rto_threat_unsel'))
$table.connect('row_pressed', Callable(self, 'on_row_pressed'))
call_deferred('init_table')
## Перестраивает индекс таблицы. Вызывать при удалении строки.
func rehash(table_index: Dictionary):
var rows_count = $table.get_rows_count()
for i_row in rows_count:
var item = $table.get_node2(0, i_row)
table_index[int(item.text)] = i_row
var rows_count = $table.get_rows_count()
for i_row in rows_count:
var item = $table.get_node2(0, i_row)
table_index[int(item.text)] = i_row
## Обрабатывает сигнал [b]threat_update[/b].
func on_threat_update(th, table_index: Dictionary):
var row = map_th_to_row(th)
var i_row = table_index[th.id]
$table.set_row_text(i_row, row)
var row = map_th_to_row(th)
var i_row = table_index[th.id]
$table.set_row_text(i_row, row)
## Обрабатывает сигнал [b]threat_lost[/b].
func on_threat_lost(th, table_index: Dictionary):
$table.remove_row(table_index[th.id])
table_index.clear()
rehash(table_index)
$table.remove_row(table_index[th.id])
table_index.clear()
rehash(table_index)
## Обрабатывает сигнал [b]threat_new[/b].
func on_threat_new(th, table_index):
var i_row = $table.get_rows_count()
$table.add_row(TABLE_ROW)
var row = map_th_to_row(th)
$table.set_row_text(i_row, row)
$table.set_row_editable(i_row, false)
$table.set_node_user_data(0, i_row, th)
table_index[th.id] = i_row
var i_row = $table.get_rows_count()
$table.add_row(TABLE_ROW)
var row = map_th_to_row(th)
$table.set_row_text(i_row, row)
$table.set_row_editable(i_row, false)
$table.set_node_user_data(0, i_row, th)
table_index[th.id] = i_row
## Преобразует цель в строку таблицы.
func map_th_to_row(th) -> Array:
var row: = Array()
row.resize(TABLE_COLUMN_PARAM.size())
var i_col: int = 0
for param_name in TABLE_COLUMN_PARAM:
row[i_col] = '%s' % th.get(param_name)
i_col += 1
return row
var row: = Array()
row.resize(TABLE_COLUMN_PARAM.size())
var i_col: int = 0
for param_name in TABLE_COLUMN_PARAM:
row[i_col] = '%s' % th.get(param_name)
i_col += 1
return row
## Выделяет выбранную цель в таблице.
func on_rto_threat_sel(th_id, tbl):
for i in tbl.get_rows_count():
var node = tbl.get_node2(0, i)
if node.text.to_int() == th_id:
row_pressed(i)
break
for i in tbl.get_rows_count():
var node = tbl.get_node2(0, i)
if node.text.to_int() == th_id:
row_pressed(i)
break
## Снимает выделение цели.

View File

@@ -4,15 +4,15 @@ extends NinePatchRect
@export var horizontal_alignment: HorizontalAlignment = HorizontalAlignment.HORIZONTAL_ALIGNMENT_CENTER:
get: return $header.horizontal_alignment if is_inside_tree() else HorizontalAlignment.HORIZONTAL_ALIGNMENT_CENTER
set(val): if is_inside_tree(): $header.horizontal_alignment = val
get: return $header.horizontal_alignment if is_inside_tree() else HorizontalAlignment.HORIZONTAL_ALIGNMENT_CENTER
set(val): if is_inside_tree(): $header.horizontal_alignment = val
@export var vertical_alignment: VerticalAlignment = VerticalAlignment.VERTICAL_ALIGNMENT_CENTER:
get: return $header.vertical_alignment if is_inside_tree() else VerticalAlignment.VERTICAL_ALIGNMENT_CENTER
set(val): if is_inside_tree(): $header.vertical_alignment = val
get: return $header.vertical_alignment if is_inside_tree() else VerticalAlignment.VERTICAL_ALIGNMENT_CENTER
set(val): if is_inside_tree(): $header.vertical_alignment = val
@export var text: String = 'caption text':
get: return $header.text if is_inside_tree() else ''
set(val): if is_inside_tree(): $header.text = val
get: return $header.text if is_inside_tree() else ''
set(val): if is_inside_tree(): $header.text = val

View File

@@ -6,13 +6,13 @@ var test_addr: = String()
var test_data: = {}
func _ready():
test_req = HTTPRequest.new()
add_child(test_req)
test_req.request_completed.connect(_map_request_completed)
test_req = HTTPRequest.new()
add_child(test_req)
test_req.request_completed.connect(_map_request_completed)
func _map_request_completed():
pass
pass
func on_req_test_data_timer():
test_req.request(test_addr)
test_req.request(test_addr)

View File

@@ -9,94 +9,94 @@ static var json_parser: JSON = JSON.new()
## [param paths] - Массив содержащий отдельные элементы пути.[br]
## [param tokens] - Разделители используемые во входной строке для разделения элементов пути.[br]
func map_solid_to_array(solid: String, paths: Array, tokens: Array = ['[', ']', '.']) -> void:
paths.clear()
var i_chs = []
for sep in tokens:
var i = 0
while i != -1:
i = solid.find(sep, i + 1)
i_chs.append(i)
i_chs.pop_back()
i_chs.sort()
paths.clear()
var i_chs = []
for sep in tokens:
var i = 0
while i != -1:
i = solid.find(sep, i + 1)
i_chs.append(i)
i_chs.pop_back()
i_chs.sort()
var i0 = 0
for i1 in i_chs:
var ss: = [String()]
tools.get_string_slice(solid, ss, i0, i1)
if ss[0].is_valid_int():
paths.append(ss[0].to_int())
else:
if ss[0]:
paths.append(ss[0])
i0 = i1 + 1
var i0 = 0
for i1 in i_chs:
var ss: = [String()]
tools.get_string_slice(solid, ss, i0, i1)
if ss[0].is_valid_int():
paths.append(ss[0].to_int())
else:
if ss[0]:
paths.append(ss[0])
i0 = i1 + 1
func _on_request_rtr_request_completed(result, _response_code, headers, body: PackedByteArray):
if result != 0:
var rs = $request_rtr.get_result_string(result)
log.error('ошибка: \"%s\"' % rs)
return
if result != 0:
var rs = $request_rtr.get_result_string(result)
log.error('ошибка: \"%s\"' % rs)
return
var json_str: String = body.get_string_from_utf8()
if not json_str:
push_error('ошибка: не удаётся получить строку utf8 из данных (%s)' % headers)
return
var json_str: String = body.get_string_from_utf8()
if not json_str:
push_error('ошибка: не удаётся получить строку utf8 из данных (%s)' % headers)
return
var rc = json_parser.parse(json_str)
if rc != Error.OK:
var err = json_parser.get_error_message()
push_error('ошибка: разбор json прерван - \"%s\" (%s)' % [err, headers])
return
var rc = json_parser.parse(json_str)
if rc != Error.OK:
var err = json_parser.get_error_message()
push_error('ошибка: разбор json прерван - \"%s\" (%s)' % [err, headers])
return
var pp = ['DeviceDescription', 'DeviceName']
var ret_val = [null, false]
var pp_copy = pp.duplicate(true)
tools.get_value_by_path(json_parser.data, pp, ret_val)
if not ret_val[1]:
push_error('ошибка: путь %s не найден (%s)' % [pp_copy, headers])
return
var pp = ['DeviceDescription', 'DeviceName']
var ret_val = [null, false]
var pp_copy = pp.duplicate(true)
tools.get_value_by_path(json_parser.data, pp, ret_val)
if not ret_val[1]:
push_error('ошибка: путь %s не найден (%s)' % [pp_copy, headers])
return
var bort_rtr_name_left: StringName = $left.get_meta('DeviceName', '')
var bort_rtr_name_right: StringName = $right.get_meta('DeviceName', '')
var bb = {bort_rtr_name_left: $left, bort_rtr_name_right: $right}
if not bb.has(ret_val[0]):
return
var bort_rtr_name_left: StringName = $left.get_meta('DeviceName', '')
var bort_rtr_name_right: StringName = $right.get_meta('DeviceName', '')
var bb = {bort_rtr_name_left: $left, bort_rtr_name_right: $right}
if not bb.has(ret_val[0]):
return
var bort_rtr = bb[ret_val[0]]
for child in bort_rtr.get_node('konverter/grid').get_children():
map_value_to_item(json_parser.data, child, 'value0')
map_value_to_item(json_parser.data, child, 'value1')
map_value_to_item(json_parser.data, child, 'value2')
map_error_codes_to_item(json_parser.data, child, 'state')
for child in bort_rtr.get_node('antenna1/grid').get_children():
map_value_to_item(json_parser.data, child, 'value0')
map_value_to_item(json_parser.data, child, 'value1')
map_error_codes_to_item(json_parser.data, child, 'state')
for child in bort_rtr.get_node('antenna2/grid').get_children():
map_value_to_item(json_parser.data, child, 'value0')
map_value_to_item(json_parser.data, child, 'value1')
map_error_codes_to_item(json_parser.data, child, 'state')
for child in bort_rtr.get_children():
map_value_to_item(json_parser.data, child, 'value0')
map_value_to_item(json_parser.data, child, 'value1')
map_error_codes_to_item(json_parser.data, child, 'state')
var bort_rtr = bb[ret_val[0]]
for child in bort_rtr.get_node('konverter/grid').get_children():
map_value_to_item(json_parser.data, child, 'value0')
map_value_to_item(json_parser.data, child, 'value1')
map_value_to_item(json_parser.data, child, 'value2')
map_error_codes_to_item(json_parser.data, child, 'state')
for child in bort_rtr.get_node('antenna1/grid').get_children():
map_value_to_item(json_parser.data, child, 'value0')
map_value_to_item(json_parser.data, child, 'value1')
map_error_codes_to_item(json_parser.data, child, 'state')
for child in bort_rtr.get_node('antenna2/grid').get_children():
map_value_to_item(json_parser.data, child, 'value0')
map_value_to_item(json_parser.data, child, 'value1')
map_error_codes_to_item(json_parser.data, child, 'state')
for child in bort_rtr.get_children():
map_value_to_item(json_parser.data, child, 'value0')
map_value_to_item(json_parser.data, child, 'value1')
map_error_codes_to_item(json_parser.data, child, 'state')
func map_value_to_item(data, item, value_name: StringName):
var meta = item.get_meta(value_name, '')
if meta == '':
return
if not (meta is String):
return
var paths: = Array()
map_solid_to_array(meta, paths)
var ret_val: = [null, false]
var paths_copy = paths.duplicate(true)
tools.get_value_by_path(data, paths, ret_val)
if ret_val[1]:
item.set(value_name, ret_val[0])
else:
push_error('ошибка: путь %s для $%s.%s не найден' % [paths_copy, item.name, value_name])
var meta = item.get_meta(value_name, '')
if meta == '':
return
if not (meta is String):
return
var paths: = Array()
map_solid_to_array(meta, paths)
var ret_val: = [null, false]
var paths_copy = paths.duplicate(true)
tools.get_value_by_path(data, paths, ret_val)
if ret_val[1]:
item.set(value_name, ret_val[0])
else:
push_error('ошибка: путь %s для $%s.%s не найден' % [paths_copy, item.name, value_name])
## 1. Поле [b]'ErCode'[/b] содержит [b]0[/b] - если нет ошибки, и другое значение - если есть ошибка.[br]
@@ -105,29 +105,29 @@ func map_value_to_item(data, item, value_name: StringName):
## 4. [i]Нет результата порверки[/i] - поле [param state] принимает значение [b]0[/b], [i]всё
## исправно[/i] - [b]1[/b], [i]хотябы один[/i] параметр с ошибкой - [b]2[/b].[br]
func map_error_codes_to_item(data, item, value_name: StringName):
var metas = item.get_meta(value_name, '')
if not (metas is Array):
return
var paths: = Array()
var values: = Array()
for meta in metas:
map_solid_to_array(meta, paths)
var ret_val: = [null, false]
var paths_copy = paths.duplicate(true)
tools.get_value_by_path(data, paths, ret_val)
if ret_val[1]:
values.append(int(ret_val[0]))
else:
push_error('ошибка: путь %s для $%s.%s не найден' % [paths_copy, item.name, value_name])
var test_res = 1 if values.count(int(0)) == values.size() else 2
item.set(value_name, test_res)
var metas = item.get_meta(value_name, '')
if not (metas is Array):
return
var paths: = Array()
var values: = Array()
for meta in metas:
map_solid_to_array(meta, paths)
var ret_val: = [null, false]
var paths_copy = paths.duplicate(true)
tools.get_value_by_path(data, paths, ret_val)
if ret_val[1]:
values.append(int(ret_val[0]))
else:
push_error('ошибка: путь %s для $%s.%s не найден' % [paths_copy, item.name, value_name])
var test_res = 1 if values.count(int(0)) == values.size() else 2
item.set(value_name, test_res)
## Вызывается при клике мыши на элементе контроля.
## [param block_a] - Кликнутый элемент контроля.
func _on_item_pressed(block_a: Control):
var metas = block_a.get_meta_list()
print_debug(block_a)
for n in metas:
var v = block_a.get_meta(n)
print('%s:%s' % [n, v])
var metas = block_a.get_meta_list()
print_debug(block_a)
for n in metas:
var v = block_a.get_meta(n)
print_debug('%s:%s' % [n, v])

View File

@@ -1,174 +1,665 @@
class_name main_tilemap extends Sprite2D
@tool
extends CanvasGroup
class_name MercatorTileMap
## Возможные состояния конечного автомата заданий.
enum TASK_STATE {
DONE, ## Выполнено.
WAIT, ## Ожидание плитки.
RUN, ## Запуск выполнения.
CHECK, ## Проверка пустого места.
ERROR ## Ошибка.
}
## Отображение карт WEB Mercator
#http://10.1.1.220/osm_tiles/0/0/0.png
#https://tile.openstreetmap.org/{z}/{x}/{y}.png
@export var base_url: String = 'https://tile.openstreetmap.org/{z}/{x}/{y}.png':
set(v):
if base_url != v:
base_url = v
_clean_all()
queue_redraw()
@export var zoom_step: float = 0.025
@export var tile_width: float = 256.0
@export var tile_height: float = 256.0
@export_range(1, 1000, 1) var max_concurrent_requests:int = 5
@export_range(100, 100000, 1) var max_cached_tiles: int = 200
@export_range(0, 25, 1) var max_zoom_level: int = 19
@export_range(0, 25, 1) var min_zoom_level: int = 0
@export_color_no_alpha() var back_color: = Color.BLACK
@export var animation_time: float = 1.0
## Координаты для проверок [lng, lat].
const TEST_DATA: = {
'Калиниград': [ 20.51095, 54.70649 ],
'Москва': [ 37.6156, 55.7522 ],
'Нью-Йорк': [ -74.006, 40.7143 ],
'Сидней': [ 151.20732, -33.8678500 ],
'Таганрог': [ 38.8969, 47.2362 ],
'Хельсинки': [ 24.9354, 60.1695 ],
'Азорские о-ва': [ 28.3996582, 38.5397568 ],
'Мехико': [ -99.12766, 19.42847 ],
'Белое море': [ 68.136606, 42.045546 ],
'море Беллинсгаузена': [ -72.406587, -74.857997 ],
'Фиджи': [ 178.065032, -17.713371 ],
'Ноль': [ 0.0, 0.0 ],
'Точка1': [ 170.0, 0.0 ],
'Точка2': [-170.0, 0.0 ],
'Море' : [ 19.4, 54.91 ] }
@export var size: Vector2i = Vector2i(400, 400):
set(v):
$canvas.size = v
_on_resize()
queue_redraw()
get:
return $canvas.size
const MAP_MODES: = {
0: 'application/config/map_image_size_rto',
1: 'application/config/map_image_size_rls' }
@export var ax: float = 0.0:
set(v):
_xyz.x = v
queue_redraw()
get:
return _xyz.x
const MAP_RELATIVE_RADIUSES: = {
0: 0.39,
1: 0.466
}
@export var ay: float = 0.0:
set(v):
_xyz.y = v
queue_redraw()
get:
return _xyz.y
@export var az: float = 0.0:
set(v):
_xyz.z = v
queue_redraw()
get:
return _xyz.z
var _xyz: Vector3 = Vector3(0, 0, min_zoom_level)
var _cache: Dictionary = {}
var _queue: Dictionary = {}
var _dragging: bool = false
var _drag_pos: Vector2 = Vector2.ZERO
var _last_cache_check: float = 0
var _rollover: bool = false
var _marks: Dictionary = {}
var _req_count: = 0
var image: = Image.new()
var image_load_proc: Callable
var now: = 0
var spr_mask: = Sprite2D.new()
var visible_marks_count: = 0
var mouse_wheel_count: = 0
var map_server_available: = false
## Вызывается после того, как загрузка плиток завершена
signal load_completed()
## Вызывается при запуске загрузки плиток
signal load_started()
## Вызывается при получении ответа от сервера карт
signal map_server_online()
## Вызывается при отсутствии ответа от сервера карт
signal map_server_offline()
const image_load_proc_names = {
'png': 'load_png_from_buffer',
'jpg': 'load_jpg_from_buffer',
'tga': 'load_tga_from_buffer',
'webp': 'load_webp_from_buffer' }
@export_enum('png', 'jpg', 'jpeg', 'tga', 'webp') var image_type: String = 'png':
set(v): image_load_proc = Callable(image, image_load_proc_names[v])
var map_image: Image ## Изображение с картой.
var map_tile_state: TASK_STATE ## Текущее состояние конечного автомата плитки.
var map_bcs: Vector3 ## Яркость, контраст насыщенность.
var map_server_addr: String ## Адрес сервера изображений карт в проекции Меркатора.
var map_tile_format: String ##
var map_tile_stack: Array ## Стек для хранения плиток карты.
var map_request: HTTPRequest ## Обработчик запросов http.
var map_zoom: int ## Уровень детализации карты.
var map_tile: Rect2i ## Область одной плитки: [x-координата области, y-координата области, x-индекс плитки, y-индекс плитки].
var map_mode: int ## Режим карты: 0 - приоритет РТО, 1 - приоритет РЛС.
var prev_tiles_dic: Dictionary ##
var lnglat: Array ## Текущие координаты.
var root_rect: Rect2i
func _ready():
lnglat = TEST_DATA['Таганрог']
map_tile_state = TASK_STATE.DONE
map_request = HTTPRequest.new()
add_child(map_request)
map_request.request_completed.connect(self._map_request_completed)
map_bcs = ProjectSettings.get_setting('application/config/map_bcs', Vector3(1.0, 1.0, 1.0))
map_server_addr = ProjectSettings.get_setting('application/config/map_server_addr')
map_tile_format = ProjectSettings.get_setting('application/config/map_tile_format', '%s/%d/%d/%d.png')
var map_image_size = ProjectSettings.get_setting(MAP_MODES[0], Vector2i(512, 512))
var map_tile_size = ProjectSettings.get_setting('application/config/map_tile_size', 256)
map_image = Image.create(map_image_size.x, map_image_size.y, true, Image.FORMAT_RGB8)
map_tile = Rect2i(0, 0, map_tile_size, map_tile_size)
signaller.connect('zoom_changed', Callable(self, '_on_zoom_changed'))
signaller.connect('map_mode_changed', Callable(self, '_on_map_mode_changed'))
log.info('Адрес сервера поставщика карт: \"%s\"' % map_server_addr)
log.info('Модуль географической карты готов')
$canvas.connect('mouse_entered', func(): _rollover = true)
$canvas.connect('mouse_exited', func(): _rollover = false)
_on_resize()
image_type = 'png'
spr_mask.name = StringName('spr_mask')
spr_mask.material = CanvasItemMaterial.new()
spr_mask.material.blend_mode = CanvasItemMaterial.BLEND_MODE_SUB
spr_mask.add_to_group('gui_items')
$canvas.add_child(spr_mask)
func _on_map_mode_changed(mode: int) -> void:
var map_image_size = ProjectSettings.get_setting(MAP_MODES[mode])
material.set('shader_parameter/radius', MAP_RELATIVE_RADIUSES[mode])
map_image.fill(Color(0, 0, 0, 0))
map_image.resize(map_image_size.x, map_image_size.y)
map_tile_state = TASK_STATE.DONE
map_mode = mode
var tile: = mercator.create_tile(lnglat[0], lnglat[1], map_zoom)
map_tile_stack.clear()
map_tile_stack.append(tile)
prev_tiles_dic.clear()
func _on_resize() -> void:
var img_mask: = Image.create($canvas.size.x, $canvas.size.y, false, Image.FORMAT_RGBA8)
var c = $canvas.size / 2.0
var r = min($canvas.size.x, $canvas.size.y) / 2.0
var color = Color.WHITE
var sz = img_mask.get_size()
for x in sz.x:
for y in sz.y:
var dist = c.distance_to(Vector2(x, y))
color.a = smoothstep(r - 2.0, r + 2.0, dist)
img_mask.set_pixel(x, y, color)
spr_mask.texture = ImageTexture.create_from_image(img_mask)
spr_mask.position = $canvas.position + $canvas.size / 2.0
func mark_image_center(image: Image, color: Color = Color.RED):
var c = image.get_size() / 2
map_image.set_pixel(c.x - 1, c.y - 1, color)
map_image.set_pixel(c.x - 1, c.y, color)
map_image.set_pixel(c.x, c.y - 1, color)
map_image.set_pixel(c.x, c.y, color)
## Создаёт новую отметку из координат экрана
func add_mark_from_screen_pos(id: int, x: float, y: float, mark: Node):
if _marks.has(id):
return false
var wrld_pos = screen_to_world(_xyz, x, y)
var p = world_to_tile(wrld_pos.x, wrld_pos.y, _xyz.z)
var tile_index = xyz_to_idx(p.x, p.y, _xyz.z)
var dot = { 'position': wrld_pos, 'tile_index': tile_index, 'mark': mark }
_marks[id] = dot
add_child(mark)
fix_mark_position(dot)
return true
func _on_zoom_changed(zoom: int):
if map_tile_stack.size() != 0:
func get_marks_count(): return _marks.size()
func delete_mark(id: int):
if not _marks.has(id):
return false
_marks[id].mark.queue_free()
_marks.erase(id)
queue_redraw()
## Создаёт новую отметку из долготы и широты
func add_mark_from_lon_lat(id: int, lon: float, lat: float, mark: Node, gap_size: float = 32):
if _marks.has(id):
return false
var wrld_pos = lonlat_to_world(lon, lat)
var scr_pos = world_to_screen(wrld_pos.x, wrld_pos.y)
mark.set_global_position(scr_pos)
_marks[id] = {'position': wrld_pos, 'mark': mark, 'gap_size': gap_size }
add_child(mark)
queue_redraw()
return true
## Возвращает новую позицию для точки, если она выходит из поля видимости
func fix_mark_position(dot):
if is_mark_visible(dot):
return
map_zoom = zoom
var tile: = mercator.create_tile(lnglat[0], lnglat[1], zoom)
map_tile_stack.clear()
map_tile_stack.append(tile)
prev_tiles_dic.clear()
var v = 2 * tile_width * pow(2, _xyz.z)
if dot.position.x < _xyz.x - $canvas.size.x / v:
dot.position.x = _xyz.x - $canvas.size.x / v
elif dot.position.x > _xyz.x + $canvas.size.x / v:
dot.position.x = _xyz.x + $canvas.size.x / v
func img_tile_to_map_image(image: Image, dst_pos: Vector2i):
image.adjust_bcs(map_bcs.x, map_bcs.y, map_bcs.z)
map_image.blit_rect(image, map_tile, dst_pos)
func is_mark_visible(dot):
var screen_pos = world_to_screen(dot.position.x, dot.position.y)
return screen_pos.x > 0 and screen_pos.x < $canvas.size.x
func _process(_delta: float):
if (map_tile_state == TASK_STATE.DONE) and map_tile_stack.size():
var tile: mercator.Tile = map_tile_stack[-1]
var tile_addr: = map_tile_format % [map_server_addr, tile.z, tile.x , tile.y]
var rc = map_request.request(tile_addr)
if rc == OK:
map_tile_state = TASK_STATE.WAIT
func _input(e):
var lmp = get_local_mouse_position()
if not _rollover:
return
if e is InputEventMouseButton:
if e.pressed:
if e.button_index == MOUSE_BUTTON_WHEEL_DOWN:
mouse_wheel_count -= 1
queue_redraw()
elif e.button_index == MOUSE_BUTTON_WHEEL_UP:
mouse_wheel_count += 1
queue_redraw()
if e.button_index == MOUSE_BUTTON_LEFT:
_dragging = e.pressed
_drag_pos = screen_to_world(_xyz, lmp.x, lmp.y)
elif e is InputEventMouseMotion:
if _dragging:
var wp = screen_to_world(_xyz, lmp.x, lmp.y)
var diff = _drag_pos - wp
_xyz.x += diff.x
_xyz.y += diff.y
queue_redraw()
## Возвращает true, если видна область за краем карты
func is_border_visible():
var z = min(max_zoom_level, _xyz.z)
return screen_to_tile(0, 0, z).y != 0
func multiply_zoom(multiplier: float, pivot: Vector2):
var p1: = screen_to_world(_xyz, pivot.x, pivot.y)
_xyz.z = clampf(_xyz.z * multiplier, min_zoom_level, max_zoom_level)
var p2: = screen_to_world(_xyz, pivot.x, pivot.y)
_xyz.x -= p2.x - p1.x
_xyz.y -= p2.y - p1.y
func set_zoom(zoom: float, pivot=null):
if pivot == null:
pivot = $canvas.size / 2.0
var p1: = screen_to_world(_xyz, pivot.x, pivot.y)
_xyz.z = clampf(zoom, max_zoom_level, min_zoom_level)
var p2: = screen_to_world(_xyz, pivot.x, pivot.y)
_xyz.x -= p2.x - p1.x
_xyz.y -= p2.y - p1.y
func _draw():
var lmp = get_local_mouse_position()
if mouse_wheel_count > 0:
multiply_zoom(1.0 + zoom_step, lmp)
mouse_wheel_count -= 1
elif mouse_wheel_count < 0:
multiply_zoom(1.0 - zoom_step, lmp)
mouse_wheel_count += 1
var z = min(max_zoom_level, _xyz.z)
var t1 = screen_to_tile(0, 0, z)
var t2 = screen_to_tile($canvas.size.x, $canvas.size.y, z)
for tx in range(t1.x, t2.x + 1):
for ty in range(t1.y, t2.y + 1):
_draw_tile(tx, ty, z)
var n = pow(2, z)
var dx = n * tile_width
var c = $canvas.size / 2.0
var r = min($canvas.size.x, $canvas.size.y) / 2.0
visible_marks_count = 0
for dot in _marks.values():
var pos = world_to_screen(dot.position.x, dot.position.y)
if pos.x >= 0:
pos.x = fmod(pos.x, dx)
else:
log.error('%s: Невозможно сформировать запрос HTTP' % self)
map_tile_state = TASK_STATE.ERROR
elif map_tile_state == TASK_STATE.CHECK:
var root_tile: = mercator.Tile.new(root_rect.size.x, root_rect.size.y, map_zoom)
var neighbors_tiles = mercator.get_perimetr_tiles(root_tile, (map_image.get_size().x / map_tile.size.x) / 2)
var neighbors_tiles_dic = {}
for tile in neighbors_tiles:
neighbors_tiles_dic[[tile.x, tile.y]] = false
if neighbors_tiles_dic != prev_tiles_dic:
for tile in neighbors_tiles:
tile.is_root = false
map_tile_stack.push_front(tile)
prev_tiles_dic[[tile.x, tile.y]] = false
else:
if map_tile_stack.size() == 0:
signaller.emit_signal('zoom_complete', map_zoom)
map_tile_state = TASK_STATE.DONE
pos.x = -pos.x - 1
pos.x = int(dx - 1) - fmod(pos.x, dx)
dot.mark.position = pos
dot.mark.visible = c.distance_to(pos) < (r - dot.gap_size)
visible_marks_count += int(dot.mark.visible)
func _map_request_completed(result, response_code, headers, data):
func _clean_all():
_queue.clear()
_cache.clear()
_marks.clear()
_req_count = 0
for c in get_children().filter(func (item): return not item.is_in_group('gui_items')):
remove_child(c)
c.queue_free()
# get the tile from queue with the newest timestamp
func get_next_in_queue():
var tile = null
for idx in _queue:
var t = _queue.get(idx)
if not tile or t.t > tile.t:
tile = t
return tile
# delete oldest tiles from cache
func _clean_cache():
var overflow = _cache.size() - max_cached_tiles
if overflow <= 0:
return
var vals = _cache.values()
vals.sort_custom(func(t1, t2): return t1.t < t2.t)
for i in overflow:
_cache.erase(vals[i].i)
func _process(delta):
now += delta
# if cache is overflowing - clean it
if not _last_cache_check or now - _last_cache_check > 5:
_last_cache_check = now
_clean_cache()
while not _queue.is_empty() and (_req_count < max_concurrent_requests):
var tile = get_next_in_queue()
if not tile:
break
var req = HTTPRequest.new()
_req_count += 1
req.set_meta('tile', tile)
add_child(req)
req.name = NodePath(str(tile.i))
_queue.erase(tile.i)
req.request_completed.connect(_response.bind(req, tile))
if req.request(tile.url) != OK:
tile.t = now
remove_child(req)
req.queue_free()
_req_count -= 1
push_error('ошибка: запрос плитки \"%s\" не отправлен' % tile.url)
if _queue.is_empty():
emit_signal('load_completed')
# result, response_code, headers, body
func _response(result, code, _headers: Array, body: PackedByteArray, req, tile: Tile):
_req_count -= 1
remove_child(req)
req.queue_free()
if result != HTTPRequest.RESULT_SUCCESS:
log.error('%s: Запрос не обработан. result: \"%s\", response_code: \"%s\", headers: \"%s\"' % [self, result, response_code, headers])
map_tile_state = TASK_STATE.ERROR
if map_server_available:
map_server_available = false
emit_signal('map_server_offline')
_queue[tile.i] = tile
return
var img_tile = Image.new()
var error = img_tile.load_png_from_buffer(data)
if error != OK:
log.error('%s: Полученые данные невозможно использовать для формироания изображения. result: \"%s\", response_code: \"%s\", headers: \"%s\"' % [self, result, response_code, headers])
map_tile_state = TASK_STATE.ERROR
if not map_server_available:
map_server_available = true
emit_signal('map_server_online')
if code == 404:
push_error('ошибка: на сервере нет \"%s\"' % tile.url)
return
var tile = map_tile_stack.pop_back()
map_tile_state = TASK_STATE.CHECK
if Error.OK != image_load_proc.call(body):
push_error('ошибка: не удалось получить изображение (неверный формат?) из \"%s\"' % tile.url)
return
if tile.is_root:
# Отображение корневой плитки
map_image.fill(Color(0, 0, 0, 0))
var bounds: = mercator.bounds(tile)
var lng_c = (bounds.east + bounds.west) / 2.0
var lat_c = (bounds.north + bounds.south) / 2.0
var sx: float = float(map_tile.size.x) / (bounds.east - bounds.west) * (lng_c - tile.lng) + map_tile.size.x / 2.0 # Cмещение в пискселях центра плитки от заданной точки по горизонтали.
var sy: float = float(map_tile.size.y) / (bounds.north - bounds.south) * (lat_c - tile.lat) - map_tile.size.y / 2.0 # Cмещение в пискселях центра плитки от заданной точки по вертикали.
var dst_pos: Vector2i = Vector2i(int(sx), int(-sy)) + map_tile.size * map_mode
img_tile_to_map_image(img_tile, dst_pos)
root_rect = Rect2i(dst_pos.x, dst_pos.y, tile.x, tile.y)
# create texture from image and add it to the cache
tile.texture = ImageTexture.create_from_image(image)
_cache[tile.i] = tile
# redraw the map
queue_redraw()
func _draw_subtile(tx: int, ty: int, tz: int, origx: int, origy: int, origz: float) -> bool:
var subtile = get_tile(tx, ty, tz)
if not subtile:
return false
var p1 = tile_to_screen(origx, origy, origz)
var p2 = tile_to_screen(origx + 1, origy + 1, origz)
var x1 = tile_to_screen(tx, ty, tz)
var x2 = tile_to_screen(tx + 1, ty + 1, tz)
var xdiff = x2.x - x1.x
var xrat1 = (p1.x - x1.x) / xdiff
var xrat2 = (p2.x - x1.x) / xdiff
var xwidth = xrat2 - xrat1
var ydiff = x2.y - x1.y
var yrat1 = (p1.y - x1.y) / ydiff
var yrat2 = (p2.y - x1.y) / ydiff
var yheight = yrat2 - yrat1
var rect = Rect2(xrat1 * tile_width, yrat1 * tile_height, xwidth * tile_width, yheight * tile_height)
if subtile.texture:
draw_texture_rect_region(subtile.texture, Rect2(p1, p2 - p1), rect)
return true
return false
func _draw_tile(tx: int, ty: int, z: float):
var tz = floor(z)
var p1 = tile_to_screen(tx, ty, z)
var p2 = tile_to_screen(tx + 1, ty + 1, z)
var tile = get_tile(tx, ty, tz)
if tile:
if tile.texture:
draw_texture_rect(tile.texture, Rect2(p1, p2 - p1), false, Color.WHITE, false)
else:
var zzz = tz
var txx = tx
var tyy = ty
while zzz > 1:
zzz -= 1
txx = floor(txx / 2.0)
tyy = floor(tyy / 2.0)
if _draw_subtile(txx, tyy, zzz, tx, ty,z):
break
## convert lon/lat to world coords
func lonlat_to_world(lon: float, lat: float) -> Vector2:
var x = lon / 180.0
var latsin = sin(deg_to_rad(lat) * sign(lat))
var y = (sign(lat) * (log((1.0 + latsin) / (1.0 - latsin)) / 2.0)) / PI
return Vector2(x, y)
## convert lon/lat to screen coords
func lonlat_to_screen(lon: float, lat: float) -> Vector2:
var w = lonlat_to_world(lon, lat)
return world_to_screen(w.x, w.y)
## convert lon/lat to tile coords
func lonlat_to_tile(lon: float, lat: float, z: float) -> Vector2:
var w = lonlat_to_world(lon,lat)
return world_to_tile(w.x, w.y, z)
## convert world coords to lon/lat
func world_to_lonlat(wx: float, wy: float) -> Vector2:
var lon = wx * 180.0
var lat = rad_to_deg(atan(sinh(wy * PI)))
return Vector2(lon, lat)
## convert screen coords to lon/lat
func screen_to_lonlat(sx: float, sy: float) -> Vector2:
var w = screen_to_world(_xyz, sx, sy)
return world_to_lonlat(w.x, w.y)
## convert tile coords to lon/lat
func tile_to_lonlat(tx: float, ty: float, tz: float) -> Vector2:
var w = tile_to_world(tx, ty, tz)
return world_to_lonlat(w.x, w.y)
## convert screen coords to world coords
func screen_to_world(xyz: Vector3, sx: float, sy: float) -> Vector2:
var n = pow(2.0, xyz.z)
var span_w = n * tile_width
var span_h = n * tile_height
var px = sx - $canvas.size.x / 2 + span_w / 2
var py = sy - $canvas.size.y / 2 + span_h / 2
var xr = px / span_w
var yr = py / span_h
var x = (xr * 2.0 - 1.0) + xyz.x
var y = ((-yr * 2.0) + 1.0) + xyz.y
return Vector2(x, y)
## convert screen coords to tile coords
func screen_to_tile(sx: float, sy: float, z: float) -> Vector2:
var world = screen_to_world(_xyz, sx, sy)
return world_to_tile(world.x, world.y, z)
## convert tile coords to screen coords
func tile_to_screen(tx: float, ty: float, tz: float) -> Vector2:
var w = tile_to_world(tx, ty, tz)
return world_to_screen(w.x, w.y)
## convert tile coords to world coords
func tile_to_world(tx: float, ty: float, tz: float) -> Vector2:
var n = pow(2.0, floor(tz))
var x = (tx / n) * 2.0 - 1.0
var y = -((ty / n) * 2.0 - 1.0)
return Vector2(x, y)
## convert world coords to tile coords
func world_to_tile(wx: float, wy: float, z: float) -> Vector2:
var n: = pow(2.0, floor(z))
var tx: = ((wx + 1.0) / 2.0) * n
var ty: = ((-wy + 1.0) / 2.0) * n
tx = floor(tx)
ty = floor(ty)
return Vector2(tx, ty)
## convert world coords to screen coords
func world_to_screen(wx: float, wy: float) -> Vector2:
var n: = pow(2.0, _xyz.z)
var w: = n * tile_width
var h: = n * tile_height
var xr: = ((wx - _xyz.x) + 1.0) / 2.0
var yr: = (-(wy - _xyz.y) + 1.0) / 2.0
var x = w * xr - w / 2.0 + $canvas.size.x / 2.0
var y = h * yr - h / 2.0 + $canvas.size.y / 2.0
return Vector2(x, y)
## get the tile index from xyz tile coords
func xyz_to_idx(x: float, y: float, z: float) -> int:
var i: = (pow(4.0, z) - 1.0) / 3.0
var n: = pow(2.0, z)
return int(i + (y * n + x))
func get_tile(x: int, y: int, z: int, create: bool = true) -> Tile:
var n = pow(2, z)
if x >= 0:
x %= int(n)
else:
# Отображение соседних плиток
var delta: = Vector2i(tile.x - root_rect.size.x, tile.y - root_rect.size.y)
var dst_pos: Vector2i = root_rect.position + delta * map_tile.size
img_tile_to_map_image(img_tile, dst_pos)
mark_image_center(map_image)
texture = ImageTexture.create_from_image(map_image)
x = -x - 1
x = int(n - 1) - (x % int(n))
# out of bounds
if z < 0 or y < 0 or y >= n:
return null
# get tile index
var idx = xyz_to_idx(x, y, z)
var tile = null
# retrieve from current requests...
var req = get_node_or_null(NodePath(str(idx)))
if req:
tile = req.get_meta('tile')
tile.t = now
return tile
# retrieve from cache
tile = _cache.get(idx)
if tile:
tile.t = now
return tile
# retrieve from queue
tile = _queue.get(idx)
if tile:
tile.t = now
return tile
# create a new tile - add it to queue
var queue_size: = _queue.size()
if create and (not $canvas/player.is_playing()):
tile = Tile.new(idx, x, y, z)
tile.url = base_url.replace('{x}', str(x)).replace('{y}', str(y)).replace('{z}', str(z))
tile.t = now
_queue[idx] = tile
if queue_size == 0 and _queue.size():
emit_signal('load_started')
return tile
func lonlat_to_dms(lon: float, lat: float) -> String:
var pf = 'N' if lat >= 0 else 'S'
lat = abs(lat)
var deg = floor(lat)
var minute = (lat - deg) * 60
var second = (minute - floor(minute)) * 60
var text = ('%02d' % deg) + ('%02d' % floor(minute)) + ('%02d' % floor(second)) + pf + ' '
pf = 'E' if lon >= 0 else 'W'
lon = abs(lon)
deg = floor(lon)
minute = (lon - deg) * 60
second = (minute - floor(minute)) * 60
text += ('%02d' % deg) + ('%02d' % floor(minute)) + ('%02d' % floor(second)) + pf
return text
class Tile:
var i: int = 0
var x: int = 0
var y: int = 0
var z: int = 0
var t: int = 0
var url: String = ''
var texture: Texture2D = null
func _init(i0: int, x0: int, y0: int, z0: int):
self.i = i0
self.x = x0
self.y = y0
self.z = z0
func haversine_m(lat1, lon1, lat2, lon2):
# Радиус Земли в метрах
var R: = 6372795.0
# Преобразование градусов в радианы
var rlat1: = deg_to_rad(lat1)
var rlon1: = deg_to_rad(lon1)
var rlat2: = deg_to_rad(lat2)
var rlon2: = deg_to_rad(lon2)
var cl1: = cos(rlat1)
var cl2: = cos(rlat2)
var sl1: = sin(rlat1)
var sl2: = sin(rlat2)
var delta: = rlon2 - rlon1
var cdelta: = cos(delta)
var sdelta: = sin(delta)
# вычисления длины большого круга
var y: = sqrt(pow(cl2 * sdelta, 2) + pow(cl1 * sl2 - sl1 * cl2 * cdelta, 2))
var x: = sl1 * sl2 + cl1 * cl2 * cdelta
var ad: = atan2(y, x)
var dist: = ad * R
return dist
func remove_closest_dot(screen_pos):
var closest_dot = find_at_screen_position(screen_pos)
if not closest_dot:
return
remove_child(closest_dot.mark)
closest_dot.mark.queue_free()
_marks.erase(closest_dot)
func find_at_screen_position(screen_pos: Vector2):
var closest_dot = null
var closest_distance = INF
for mark in _marks.values():
var dot_screen_pos = world_to_screen(mark.position.x, mark.position.y)
var distance = dot_screen_pos.distance_to(screen_pos)
if distance < closest_distance:
closest_distance = distance
closest_dot = mark
return closest_dot
func clear() -> void:
_clean_all()
queue_redraw()
func set_coordinates(lon: float, lat: float):
var anim: Animation = $canvas/player.get_animation('goto')
var wrld_pos = lonlat_to_world(lon, lat)
anim.track_insert_key(0, 0, _xyz.x)
anim.track_insert_key(1, 0, _xyz.y)
anim.track_insert_key(0, animation_time, wrld_pos.x)
anim.track_insert_key(1, animation_time, wrld_pos.y)
anim.length = animation_time
$canvas/player.play('goto')
func blend_mask(val: bool) -> void:
spr_mask.visible = val
queue_redraw()

View File

@@ -1,41 +1,27 @@
[gd_scene load_steps=6 format=3 uid="uid://bbq6nklh36yij"]
[gd_scene load_steps=3 format=3 uid="uid://ceknxxfu1loo5"]
[ext_resource type="Script" path="res://scenes/tilemap/tilemap.gd" id="1_mkfgj"]
[ext_resource type="Shader" path="res://shaders/tilemap.gdshader" id="1_tsdui"]
[ext_resource type="Script" path="res://scenes/tilemap/Node2D.gd" id="3_lr4ti"]
[ext_resource type="Script" path="res://scenes/tilemap/tilemap.gd" id="1_l64tp"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_pmqvj"]
resource_local_to_scene = true
shader = ExtResource("1_tsdui")
shader_parameter/radius = 0.39
[sub_resource type="CanvasTexture" id="CanvasTexture_58cl1"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ys5v4"]
[node name="tilemap" type="CanvasGroup" groups=["gui_items"]]
script = ExtResource("1_l64tp")
max_zoom_level = 21
az = 2.0
[node name="tilemap" type="Sprite2D"]
material = SubResource("ShaderMaterial_pmqvj")
position = Vector2(560, 560)
script = ExtResource("1_mkfgj")
[node name="canvas" type="TextureRect" parent="." groups=["gui_items"]]
clip_contents = true
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_right = 400.0
offset_bottom = 400.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 4
size_flags_vertical = 4
texture = SubResource("CanvasTexture_58cl1")
[node name="Panel" type="Panel" parent="."]
offset_left = -563.0
offset_top = -557.0
offset_right = 1022.0
offset_bottom = 641.0
theme_override_styles/panel = SubResource("StyleBoxEmpty_ys5v4")
[node name="player" type="AnimationPlayer" parent="canvas" groups=["gui_items"]]
[node name="Node2D" type="Node2D" parent="."]
script = ExtResource("3_lr4ti")
[node name="LineEdit" type="LineEdit" parent="Node2D"]
offset_left = -490.0
offset_top = 518.0
offset_right = -244.0
offset_bottom = 565.0
[node name="VBoxContainer" type="VBoxContainer" parent="Node2D"]
offset_left = -499.0
offset_top = 200.0
offset_right = -239.0
offset_bottom = 430.0
[connection signal="gui_input" from="Panel" to="." method="_on_panel_gui_input"]
[connection signal="resized" from="canvas" to="." method="_on_resize"]

View File

@@ -9,58 +9,58 @@ func on_interfer_off(ecm, _ecms, _thrs,msg: String): log.message(Logger.INFO, '%
func on_set_program_mode(msg: String): log.message(Logger.INFO, msg)
func on_interfer_new(ecm, msg: String):
if ecm.krp != interfer.INTERFER_OFF:
log.message(Logger.INFO, '%s: %s' % [msg, ecm.to_string()])
if ecm.krp != interfer.INTERFER_OFF:
log.message(Logger.INFO, '%s: %s' % [msg, ecm.to_string()])
func _enter_tree():
var flts: Array = []
for item in $filtrs.get_children():
flts.append(item)
log.set_filtrs(flts)
var flts: Array = []
for item in $filtrs.get_children():
flts.append(item)
log.set_filtrs(flts)
func _ready() -> void:
log.info('Модуль журнала работы готов')
log.info('Аргументы коммандной строки: \"%s\"' % OS.get_cmdline_args())
log.info('Операционная система: %s' % [OS.get_distribution_name()])
log.info('Текущая версия ПО УАРЭП: %s' % commit.VCS_HEAD)
threats.connect('threat_new', Callable(self, 'on_threats_changed').bind('Цель обнаружена'))
threats.connect('threat_lost', Callable(self, 'on_threats_changed').bind('Цель потеряна'))
signaller.conn(signaller.interfer_new, Callable(self, 'on_interfer_new').bind('Создан сеанс помехи'))
signaller.conn(signaller.interfer_off, Callable(self, 'on_interfer_off').bind('Сеанс помехи отключен'))
signaller.conn(signaller.режим_работа, Callable(log, 'info').bind('Программа в режиме \"Работа\"'))
signaller.conn(signaller.режимастройки, Callable(log, 'info').bind('Программа в режиме \"Настройки\"'))
signaller.conn(signaller.режим_эмс, Callable(log, 'info').bind('Программа в режиме \"ЭМС\"'))
signaller.conn(signaller.режимурнал, Callable(log, 'info').bind('Программа в режиме \"Журнал\"'))
signaller.conn(signaller.режимонтроль, Callable(log, 'info').bind('Программа в режиме \"Контроль\"'))
log.info('Модуль журнала работы готов')
log.info('Аргументы коммандной строки: \"%s\"' % OS.get_cmdline_args())
log.info('Операционная система: %s' % [OS.get_distribution_name()])
log.info('Текущая версия ПО УАРЭП: %s' % commit.VCS_HEAD)
threats.connect('threat_new', Callable(self, 'on_threats_changed').bind('Цель обнаружена'))
threats.connect('threat_lost', Callable(self, 'on_threats_changed').bind('Цель потеряна'))
signaller.conn(signaller.interfer_new, Callable(self, 'on_interfer_new').bind('Создан сеанс помехи'))
signaller.conn(signaller.interfer_off, Callable(self, 'on_interfer_off').bind('Сеанс помехи отключен'))
signaller.conn(signaller.режим_работа, Callable(log, 'info').bind('Программа в режиме \"Работа\"'))
signaller.conn(signaller.режимастройки, Callable(log, 'info').bind('Программа в режиме \"Настройки\"'))
signaller.conn(signaller.режим_эмс, Callable(log, 'info').bind('Программа в режиме \"ЭМС\"'))
signaller.conn(signaller.режимурнал, Callable(log, 'info').bind('Программа в режиме \"Журнал\"'))
signaller.conn(signaller.режимонтроль, Callable(log, 'info').bind('Программа в режиме \"Контроль\"'))
func _on_clear_button_down() -> void:
$content.clear()
log.info('Cодержание журнала очищено оператором')
$content.clear()
log.info('Cодержание журнала очищено оператором')
func _on_btn_save_button_down() -> void:
$content.hide()
$btn_clear.hide()
$btn_save.hide()
$filtrs.hide()
$dlg_file.show()
$content.hide()
$btn_clear.hide()
$btn_save.hide()
$filtrs.hide()
$dlg_file.show()
func _on_dlg_file_confirmed() -> void:
$content.show()
$btn_clear.show()
$btn_save.show()
$filtrs.show()
$dlg_file.hide()
$content.show()
$btn_clear.show()
$btn_save.show()
$filtrs.show()
$dlg_file.hide()
func _on_dlg_file_file_selected(path):
log.save_file(path)
log.save_file(path)
func _on_check_box_pressed():
$content.clear()
log.add_msgs()
$content.clear()
log.add_msgs()

View File

@@ -7,11 +7,11 @@ extends NinePatchRect
"""
@export var fname: String:
set(v):
fname = v
$lbl_fname.text = v
set(v):
fname = v
$lbl_fname.text = v
@export var rname: String:
set(v):
rname = v
set(v):
rname = v

View File

@@ -7,7 +7,7 @@ extends Node
func _on_toggled(toggled_on): $frame.visible = toggled_on
@export var state: int:
set(v):
v = 0 if v < 0 else v % state_colors.size()
state = v
$label.self_modulate = state_colors[v]
set(v):
v = 0 if v < 0 else v % state_colors.size()
state = v
$label.self_modulate = state_colors[v]

View File

@@ -6,50 +6,50 @@ extends Panel
const PRIBORS_RES = \
{
'pribor_afsp_l': ['res://scenes/pribor-afsp/pribor-afsp.tscn', 'Контроль исправности прибора АФСП левый'],
'pribor_afsp_r': ['res://scenes/pribor-afsp/pribor-afsp.tscn', 'Контроль исправности прибора АФСП правый'],
'pribor_prd_k_1': ['res://scenes/pribor-prd-k/pribor-prd-k.tscn', 'Контроль исправности прибора ПРД-К 1'],
'pribor_prd_k_2': ['res://scenes/pribor-prd-k/pribor-prd-k.tscn', 'Контроль исправности прибора ПРД-К 2'],
'pribor_prd_k_3': ['res://scenes/pribor-prd-k/pribor-prd-k.tscn', 'Контроль исправности прибора ПРД-К 3'],
'pribor_prd_k_4': ['res://scenes/pribor-prd-k/pribor-prd-k.tscn', 'Контроль исправности прибора ПРД-К 4'],
'pribor_prd_n_1': ['res://scenes/pribor-prd-n/pribor-prd-n.tscn', 'Контроль исправности прибора ПРД-Н 1'],
'pribor_prd_n_2': ['res://scenes/pribor-prd-n/pribor-prd-n.tscn', 'Контроль исправности прибора ПРД-Н 2'],
'pribor_prd_n_3': ['res://scenes/pribor-prd-n/pribor-prd-n.tscn', 'Контроль исправности прибора ПРД-Н 3'],
'pribor_prd_n_4': ['res://scenes/pribor-prd-n/pribor-prd-n.tscn', 'Контроль исправности прибора ПРД-Н 4'],
'pribor_prd_v_1': ['res://scenes/pribor-prd-v/pribor-prd-v.tscn', 'Контроль исправности прибора ПРД-В 1'],
'pribor_prd_v_2': ['res://scenes/pribor-prd-v/pribor-prd-v.tscn', 'Контроль исправности прибора ПРД-В 2'],
'pribor_prd_v_3': ['res://scenes/pribor-prd-v/pribor-prd-v.tscn', 'Контроль исправности прибора ПРД-В 3'],
'pribor_prd_v_4': ['res://scenes/pribor-prd-v/pribor-prd-v.tscn', 'Контроль исправности прибора ПРД-В 4'],
'pribor_rtr': ['res://scenes/pribor-rtr/pribor-rtr.tscn', 'Контроль исправности прибора РТР'],
'pribor_uf': ['res://scenes/pribor-uf/pribor-uf.tscn', 'Контроль исправности прибора УФ'],
'pribor_uyep': ['res://scenes/pribor-uyep/pribor-uyep.tscn', 'Контроль исправности прибора УЭП'],
}
'pribor_afsp_l': ['res://scenes/pribor-afsp/pribor-afsp.tscn', 'Контроль исправности прибора АФСП левый'],
'pribor_afsp_r': ['res://scenes/pribor-afsp/pribor-afsp.tscn', 'Контроль исправности прибора АФСП правый'],
'pribor_prd_k_1': ['res://scenes/pribor-prd-k/pribor-prd-k.tscn', 'Контроль исправности прибора ПРД-К 1'],
'pribor_prd_k_2': ['res://scenes/pribor-prd-k/pribor-prd-k.tscn', 'Контроль исправности прибора ПРД-К 2'],
'pribor_prd_k_3': ['res://scenes/pribor-prd-k/pribor-prd-k.tscn', 'Контроль исправности прибора ПРД-К 3'],
'pribor_prd_k_4': ['res://scenes/pribor-prd-k/pribor-prd-k.tscn', 'Контроль исправности прибора ПРД-К 4'],
'pribor_prd_n_1': ['res://scenes/pribor-prd-n/pribor-prd-n.tscn', 'Контроль исправности прибора ПРД-Н 1'],
'pribor_prd_n_2': ['res://scenes/pribor-prd-n/pribor-prd-n.tscn', 'Контроль исправности прибора ПРД-Н 2'],
'pribor_prd_n_3': ['res://scenes/pribor-prd-n/pribor-prd-n.tscn', 'Контроль исправности прибора ПРД-Н 3'],
'pribor_prd_n_4': ['res://scenes/pribor-prd-n/pribor-prd-n.tscn', 'Контроль исправности прибора ПРД-Н 4'],
'pribor_prd_v_1': ['res://scenes/pribor-prd-v/pribor-prd-v.tscn', 'Контроль исправности прибора ПРД-В 1'],
'pribor_prd_v_2': ['res://scenes/pribor-prd-v/pribor-prd-v.tscn', 'Контроль исправности прибора ПРД-В 2'],
'pribor_prd_v_3': ['res://scenes/pribor-prd-v/pribor-prd-v.tscn', 'Контроль исправности прибора ПРД-В 3'],
'pribor_prd_v_4': ['res://scenes/pribor-prd-v/pribor-prd-v.tscn', 'Контроль исправности прибора ПРД-В 4'],
'pribor_rtr': ['res://scenes/pribor-rtr/pribor-rtr.tscn', 'Контроль исправности прибора РТР'],
'pribor_uf': ['res://scenes/pribor-uf/pribor-uf.tscn', 'Контроль исправности прибора УФ'],
'pribor_uyep': ['res://scenes/pribor-uyep/pribor-uyep.tscn', 'Контроль исправности прибора УЭП'],
}
func _ready():
var group = ButtonGroup.new()
for k in PRIBORS_RES:
var pribor_node = get_node(k)
assert(pribor_node)
var pribor_path = PRIBORS_RES[k][0]
var header_text = PRIBORS_RES[k][1]
pribor_node.set_button_group(group)
pribor_node.connect('button_down', Callable(self, 'on_pribor_press').bind(pribor_path, header_text))
log.info('Модуль контроля работы готов')
var group = ButtonGroup.new()
for k in PRIBORS_RES:
var pribor_node = get_node(k)
assert(pribor_node)
var pribor_path = PRIBORS_RES[k][0]
var header_text = PRIBORS_RES[k][1]
pribor_node.set_button_group(group)
pribor_node.connect('button_down', Callable(self, 'on_pribor_press').bind(pribor_path, header_text))
log.info('Модуль контроля работы готов')
func on_pribor_press(pribor_path, header_text):
clear_obj($mnemo_container)
var pribor_obj = load(pribor_path)
assert(pribor_obj)
var pribor = pribor_obj.instantiate()
assert(pribor)
pribor.set_header_text(header_text)
$mnemo_container.add_child(pribor)
clear_obj($mnemo_container)
var pribor_obj = load(pribor_path)
assert(pribor_obj)
var pribor = pribor_obj.instantiate()
assert(pribor)
pribor.set_header_text(header_text)
$mnemo_container.add_child(pribor)
func clear_obj(obj: Object):
var items = obj.get_children()
for item in items:
obj.remove_child(item)
item.queue_free()
var items = obj.get_children()
for item in items:
obj.remove_child(item)
item.queue_free()

View File

@@ -7,20 +7,20 @@ extends Control
"""
@export var fname: String:
set(v):
fname = v
get:
return fname
set(v):
fname = v
get:
return fname
@export var rname: String:
set(v):
rname = v
get:
return rname
set(v):
rname = v
get:
return rname
func set_header_text(header_text: String):
$lbl_header.text = header_text
$lbl_header.text = header_text
@export var nodes_mask: String = ''

View File

@@ -1,82 +1,18 @@
class_name setting extends Panel
var TableHeader = preload('res://table/header.tscn')
var TableLabel = preload('res://table/lable.tscn')
var TableTaggle = preload('res://table/taggle.tscn')
var TableEdit = preload('res://table/editl.tscn')
var TableEmpti = preload('res://table/empti.tscn')
## Вызывается, когда узел впервые попадает в дерево сцены.
func _ready() -> void:
setting_tab()
dop_set_tab()
set_tab()
$lbl_commit.text = ' \"%s\"' % commit.VCS_HEAD
$lbl_commit.text = 'Текущая версия ПО УАРЭП: \"%s\"' % commit.VCS_HEAD
if not OS.get_cmdline_args().has('++ограниченный'):
return
$Label2.text = 'Текущий режим доступа: ограниченный'
for node in $GridContainer.get_children().filter(func(b): return b is Button):
node.disabled = true
func _on_external_cu_btn_pressed() -> void:
var state = $GridContainer2/external_cu_btn.button_pressed
ProjectSettings.set_setting('application/config/external_cu', state)
if state:
signaller.emit_signal('interfer_off_all')
func setting_tab():
var yams: Array = [ HORIZONTAL_ALIGNMENT_CENTER, HORIZONTAL_ALIGNMENT_CENTER ]
$table.set_header([TableHeader, TableHeader])
$table.set_header_text(['Элемент', ' Показывать '])
for i in range(15):
$table.add_row([TableLabel, TableTaggle])
$table.set_row_text(0,([' Кнопка «ПД-М']))
$table.set_row_text(1,([' Кнопка «ПД-М']))
$table.set_row_text(2,([' Кнопка «ОЗП-1» ']))
$table.set_row_text(3,([' Кнопка «МЧМ-1» ']))
$table.set_row_text(4,([' Кнопка «ОРН-И1» ']))
$table.set_row_text(5,([' Кнопка «ОРН-М']))
$table.set_row_text(6,([' Кнопка «ЛЧМ-1» ']))
$table.set_row_text(7,([' Кнопка «ПД-М4С» ']))
$table.set_row_text(8,([' Кнопка «ПД-ЧМ» ']))
$table.set_row_text(9,([' Кнопка «СВ-И1» ']))
$table.set_row_text(10,([' Кнопка «К']))
$table.set_row_text(11,([' Кнопка «СВ-М']))
$table.set_row_text(12,([' Кнопка «ОРН» ']))
$table.set_row_text(13,([' Кнопка «JTIDS» ']))
$table.set_row_text(14,([' Кнопка «КУПД» ']))
$table.set_columns_min_size([150, 100])
$table.set_columns_alignments(yams)
func dop_set_tab():
var yams: Array = [ HORIZONTAL_ALIGNMENT_CENTER, HORIZONTAL_ALIGNMENT_CENTER ]
$table2.add_row([TableLabel, TableEmpti])
$table2.add_row([TableLabel, TableEmpti])
$table2.add_row([TableLabel, TableTaggle])
$table2.add_row([TableLabel, TableTaggle])
$table2.set_row_text(0, (['Текущая версия ПО УАРЭП:']))
$table2.set_row_text(1, (['Адрес сервера карт:']))
$table2.set_row_text(2, (['Внешнее целеуказание.']))
$table2.set_row_text(3, (['Кнопка «Подавление».\nОтключить отображение этой кнопки,\nчто бы тип помех применялся сразу.']))
$table2.set_columns_min_size([180, 50])
$table2.set_columns_alignments(yams)
func set_tab():
$table3.add_row([TableLabel, TableEmpti])
$table3.add_row([TableLabel, TableEmpti])
$table3.set_row_text(0, ([' Текущий режим доступа: полный ']))
$table3.set_row_text(1, ([' Управление элементами в режиме с ограниченным доступом ']))
$table3.set_columns_min_size([250, 5])

View File

@@ -1,7 +1,6 @@
[gd_scene load_steps=4 format=3 uid="uid://bnptm4rlp60dq"]
[gd_scene load_steps=3 format=3 uid="uid://bnptm4rlp60dq"]
[ext_resource type="Script" path="res://scenes/настройки/настройки.gd" id="1_2l3rd"]
[ext_resource type="Script" path="res://table/table.gd" id="2_ts42i"]
[sub_resource type="GDScript" id="GDScript_anjmx"]
script/source = "extends LineEdit
@@ -17,47 +16,295 @@ func _on_text_changed(new_text: String) -> void:
[node name="Настройки" type="Panel"]
script = ExtResource("1_2l3rd")
[node name="table" type="GridContainer" parent="."]
layout_mode = 2
offset_left = 32.0
offset_top = 96.0
offset_right = 241.0
offset_bottom = 535.0
[node name="GridContainer" type="GridContainer" parent="."]
layout_mode = 0
offset_left = 44.0
offset_top = 87.0
offset_right = 336.0
offset_bottom = 193.0
columns = 2
script = ExtResource("2_ts42i")
[node name="Label" type="Label" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
text = "Элемент"
vertical_alignment = 1
[node name="Label2" type="Label" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
text = "Показывать"
horizontal_alignment = 1
vertical_alignment = 1
[node name="Label4" type="Label" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
text = "Кнопка «ПД-M4»"
vertical_alignment = 1
[node name="CheckButton" type="CheckButton" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 6
size_flags_vertical = 6
button_pressed = true
[node name="Label5" type="Label" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
text = "Кнопка «ПД-M2»"
vertical_alignment = 1
[node name="CheckButton3" type="CheckButton" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 6
size_flags_vertical = 6
button_pressed = true
[node name="Label6" type="Label" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
text = "Кнопка «ОЗП-1»"
vertical_alignment = 1
[node name="CheckButton5" type="CheckButton" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 6
size_flags_vertical = 6
button_pressed = true
[node name="Label7" type="Label" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
text = "Кнопка «МЧМ-1»"
vertical_alignment = 1
[node name="CheckButton6" type="CheckButton" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 6
size_flags_vertical = 6
button_pressed = true
[node name="Label8" type="Label" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
text = "Кнопка «СРН-И1»"
vertical_alignment = 1
[node name="CheckButton7" type="CheckButton" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 6
size_flags_vertical = 6
button_pressed = true
[node name="Label9" type="Label" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
text = "Кнопка «СРН-М2»"
vertical_alignment = 1
[node name="CheckButton8" type="CheckButton" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 6
size_flags_vertical = 6
button_pressed = true
[node name="Label10" type="Label" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
text = "Кнопка «ЛЧМ-1»"
vertical_alignment = 1
[node name="CheckButton9" type="CheckButton" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 6
size_flags_vertical = 6
button_pressed = true
[node name="Label11" type="Label" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
text = "Кнопка «ПД-М4С»"
vertical_alignment = 1
[node name="CheckButton10" type="CheckButton" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 6
size_flags_vertical = 6
button_pressed = true
[node name="Label12" type="Label" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
text = "Кнопка «ПД-ЧМ»"
vertical_alignment = 1
[node name="CheckButton11" type="CheckButton" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 6
size_flags_vertical = 6
button_pressed = true
[node name="Label13" type="Label" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
text = "Кнопка «СВ-И1»"
vertical_alignment = 1
[node name="CheckButton12" type="CheckButton" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 6
size_flags_vertical = 6
button_pressed = true
[node name="Label14" type="Label" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
text = "Кнопка «К1»"
vertical_alignment = 1
[node name="CheckButton13" type="CheckButton" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 6
size_flags_vertical = 6
button_pressed = true
[node name="Label15" type="Label" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
text = "Кнопка «СВ-М2»"
vertical_alignment = 1
[node name="CheckButton14" type="CheckButton" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 6
size_flags_vertical = 6
button_pressed = true
[node name="Label16" type="Label" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
text = "Кнопка «СРН»"
vertical_alignment = 1
[node name="CheckButton15" type="CheckButton" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 6
size_flags_vertical = 6
button_pressed = true
[node name="Label17" type="Label" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
text = "Кнопка «JTIDS»"
vertical_alignment = 1
[node name="CheckButton16" type="CheckButton" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 6
size_flags_vertical = 6
button_pressed = true
[node name="Label18" type="Label" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
text = "Кнопка «КУПД»"
vertical_alignment = 1
[node name="CheckButton17" type="CheckButton" parent="GridContainer"]
layout_mode = 2
size_flags_horizontal = 6
size_flags_vertical = 6
button_pressed = true
[node name="Label1" type="Label" parent="."]
layout_mode = 0
offset_left = 16.0
offset_top = 60.0
offset_right = 434.0
offset_bottom = 82.0
text = "Управление элементами в режиме с ограниченным доступом"
[node name="Label2" type="Label" parent="."]
layout_mode = 0
offset_left = 20.0
offset_top = 20.0
offset_right = 445.0
offset_bottom = 50.0
text = "Текущий режим доступа: полный"
vertical_alignment = 1
[node name="edt_mapaddr" type="LineEdit" parent="."]
layout_mode = 2
offset_left = 560.0
offset_top = 144.0
offset_right = 896.0
offset_bottom = 171.0
theme_override_colors/font_color = Color(1, 0.596078, 0.4, 1)
layout_mode = 0
offset_left = 660.0
offset_top = 80.0
offset_right = 1032.0
offset_bottom = 110.0
script = SubResource("GDScript_anjmx")
[node name="Label" type="Label" parent="edt_mapaddr"]
layout_mode = 0
offset_left = -180.0
offset_right = -20.0
offset_bottom = 30.0
text = "Адрес сервера карт:"
vertical_alignment = 1
[node name="GridContainer2" type="GridContainer" parent="."]
layout_mode = 0
offset_left = 478.0
offset_top = 154.0
offset_right = 784.0
offset_bottom = 260.0
columns = 2
[node name="external_cu_lbl" type="Label" parent="GridContainer2"]
layout_mode = 2
text = "Внешнее целеуказание."
vertical_alignment = 1
[node name="external_cu_btn" type="CheckButton" parent="GridContainer2"]
layout_mode = 2
[node name="Label19" type="Label" parent="GridContainer2"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
text = "Кнопка «Подавление».
Отключить отображение этой кнопки,
что бы тип помех применялся сразу."
[node name="CheckButton18" type="CheckButton" parent="GridContainer2"]
layout_mode = 2
size_flags_horizontal = 6
size_flags_vertical = 6
disabled = true
[node name="lbl_commit" type="Label" parent="."]
layout_mode = 0
offset_left = 560.0
offset_top = 112.0
offset_right = 1120.0
offset_bottom = 142.0
theme_override_colors/font_color = Color(1, 0.596078, 0.4, 1)
offset_left = 500.0
offset_top = 20.0
offset_right = 1060.0
offset_bottom = 50.0
text = "Текущая версия ПО УАРЭП:"
vertical_alignment = 1
[node name="table2" type="GridContainer" parent="."]
layout_mode = 0
offset_left = 296.0
offset_top = 120.0
offset_right = 880.0
offset_bottom = 408.0
script = ExtResource("2_ts42i")
[node name="table3" type="GridContainer" parent="."]
layout_mode = 0
offset_left = 24.0
offset_top = 24.0
offset_right = 440.0
offset_bottom = 80.0
script = ExtResource("2_ts42i")
[connection signal="text_changed" from="edt_mapaddr" to="edt_mapaddr" method="_on_text_changed"]
[connection signal="pressed" from="GridContainer2/external_cu_btn" to="." method="_on_external_cu_btn_pressed"]

View File

@@ -7,10 +7,10 @@ func _ready(): _on_value_changed(3)
func _on_value_changed(val):
val = int(val)
for nm in VAL_TO_LABEL.values():
var lbl0 = get_node(nm)
lbl0.self_modulate.a = 0.45
var lbl1 = get_node(VAL_TO_LABEL[val])
lbl1.self_modulate.a = 1.0
signaller.emit_signal(VAL_TO_EVENT[val])
val = int(val)
for nm in VAL_TO_LABEL.values():
var lbl0 = get_node(nm)
lbl0.self_modulate.a = 0.45
var lbl1 = get_node(VAL_TO_LABEL[val])
lbl1.self_modulate.a = 1.0
signaller.emit_signal(VAL_TO_EVENT[val])

View File

@@ -37,59 +37,66 @@ signal drag_begin ## Вызывается в момент начала пе
signal drag_continue ## Вызывается во время продолжения перетаскивания.
signal drag_end ## Вызывается в момент завершения перетаскивания.
## Границы секторов блоков ФС по частоте в режиме РТО.
const fs_band_rto: Dictionary = {
0: [200.0, 250.0],
1: [250.0, 300.0],
2: [300.0, 350.0],
3: [350.0, 400.0],
4: [400.0, 450.0],
5: [450.0, 500.0],
6: [500.0, 550.0] }
0: [200.0, 250.0],
1: [250.0, 300.0],
2: [300.0, 350.0],
3: [350.0, 400.0],
4: [400.0, 450.0],
5: [450.0, 500.0],
6: [500.0, 550.0] }
## Границы секторов блоков ФС по частоте в режиме РЛС.
const fs_band_rls: Dictionary = {
0: [480.0, 490.0],
1: [490.0, 500.0],
2: [500.0, 510.0],
3: [510.0, 520.0],
4: [520.0, 530.0],
5: [530.0, 540.0],
6: [540.0, 550.0] }
0: [480.0, 490.0],
1: [490.0, 500.0],
2: [500.0, 510.0],
3: [510.0, 520.0],
4: [520.0, 530.0],
5: [530.0, 540.0],
6: [540.0, 550.0] }
## Границы секторов блоков ФС по пеленгу.
const fs_sectors: Dictionary = {
0: [0.0, 90.0],
3: [90.0, 180.0],
2: [180.0, 270.0],
1: [270.0, 360.0] }
0: [0.0, 90.0],
3: [90.0, 180.0],
2: [180.0, 270.0],
1: [270.0, 360.0] }
const fs_freq: Dictionary = {
0: [400.0, 1200.0],
1: [1200.0, 2000.0],
2: [2000.0, 2800.0],
3: [2800.0, 3600.0],
4: [3600.0, 4400.0],
5: [4400.0, 5200.0],
6: [5200.0, 6000.0] }
0: [400.0, 1200.0],
1: [1200.0, 2000.0],
2: [2000.0, 2800.0],
3: [2800.0, 3600.0],
4: [3600.0, 4400.0],
5: [4400.0, 5200.0],
6: [5200.0, 6000.0] }
var fs_band = fs_band_rto # Текущий режим блоков ФС по частоте
var fs_selected: Dictionary # Словарь выбранных секторов
var fs_active: Dictionary # Словарь секторов назначенных на подавление
## Размеры карты для разных режимов РТО и РЛС соответственно
const map_sizes = [
Rect2i(360, 360, 400, 400),
Rect2i(82, 82, 955, 955)]
var fs_band = fs_band_rto ## Текущий режим блоков ФС по частоте
var fs_selected: Dictionary ## Словарь выбранных секторов
var fs_active: Dictionary ## Словарь секторов назначенных на подавление
## Состояние перетаскивания.
enum DragFSM {
DRAG, ## В процессе.
IDLE, ## Не активно, ожидание.
OFF ## Выключено.
}
DRAG, ## В процессе.
IDLE, ## Не активно, ожидание.
OFF ## Выключено.
}
## Состояние перетаскивания.
enum BtnSel {
SECTOR, ## В процессе.
THREAT, ## Не активно, ожидание.
FS ## Выключено.
}
SECTOR, ## В процессе.
THREAT, ## Не активно, ожидание.
FS ## Выключено.
}
## Обработчик сигнала изменения списка целей.
func on_threats_resized(threats: Dictionary): $count_all_pad/count_all.text = '%02d' % threats.size()
@@ -99,394 +106,371 @@ func on_threats_resized(threats: Dictionary): $count_all_pad/count_all.text = '%
func on_threat_selected(th, ecm_btns): on_rto_threat_sel(th.id, ecm_btns)
## Встроенный обратный вызов.
func on_enter_tree() -> void: call_deferred('on_button_strobe_pressed')
## Обработчик нажатия на кнопку вид.
func on_button_view_toggled(toggled: bool):
signaller.emit_signal('map_mode_changed', int(toggled))
if toggled:
fs_band = fs_band_rls
else:
fs_band = fs_band_rto
signaller.emit_signal('map_mode_changed', int(toggled))
fs_band = fs_band_rls if toggled else fs_band_rto
var s = map_sizes[int(toggled)]
$tilemap.size = s.size
$tilemap.position = s.position
##
## Встроенный обратный вызов.
func _enter_tree() -> void:
var timer1 = Timer.new()
timer1.name = 'timer1'
add_child(timer1)
timer1.one_shot = true
timer1.connect('timeout', Callable(self, 'on_enter_tree'))
timer1.start(1.0)
var timer1 = Timer.new()
timer1.name = 'timer1'
add_child(timer1)
timer1.one_shot = true
timer1.connect('timeout', Callable(self, 'on_enter_tree'))
timer1.start(1.0)
## Встроенный обратный вызов.
func _exit_tree() -> void:
var timer1 = get_node('timer1')
remove_child(timer1)
timer1.queue_free()
var timer1 = get_node('timer1')
remove_child(timer1)
timer1.queue_free()
## Производит инициализацию.
func _ready():
min_zoom = ProjectSettings.get_setting('application/config/map_minimum_zoom', 5)
max_zoom = ProjectSettings.get_setting('application/config/map_maximum_zoom', 19)
drag_fsm = DragFSM.OFF
$btn_zoom.set_meta('zoom', min_zoom - 1)
var btns_select: Array = $btns_select.get_children()
btns_select.any(func(btn): btn.button_connect('pressed', Callable(self, 'on_button_select').bind(btn, btns_select)))
$btn_view.button_connect('toggled', Callable(self, 'on_button_view_toggled'))
var nodes: Array = get_tree().get_nodes_in_group('группа-режим-помехи')
nodes.any(func(btn): btn.button_connect('pressed', Callable(self, 'on_button_pressed').bind(btn, nodes)))
threats.connect('threat_new', Callable(self, 'on_threat_new'))
threats.connect('threat_lost', Callable(self, 'on_threat_lost'))
threats.connect('threats_resized', Callable(self, 'on_threats_resized'))
threats.connect('threat_update', Callable(self, 'on_threat_update'))
signaller.connect('zoom_complete', Callable(self, '_on_zoom_complete'))
signaller.connect('rto_threat_sel', Callable(self, 'on_rto_threat_sel').bind(nodes))
signaller.connect('bip_pressed', Callable(self, 'on_bip_pressed').bind(nodes))
signaller.connect('rto_threat_unsel', Callable(self, 'on_rto_threat_unsel').bind(nodes))
signaller.connect('threat_selected', Callable(self, 'on_threat_selected').bind(nodes))
signaller.connect('interfer_selected', Callable(self, 'on_interfer_selected').bind(nodes))
signaller.connect('interfer_new', Callable(self, 'on_interfer_new'))
signaller.connect('interfer_off_all', Callable(self, 'on_interfer_off_all'))
signaller.connect('fs_selected', Callable(self, 'on_fs_selected'))
interfer.connect('interfer_update', Callable(self, 'on_interfer_new'))
var items_list = []
recurse(get_tree().get_root(), items_list)
print_debug(items_list)
min_zoom = ProjectSettings.get_setting('application/config/map_minimum_zoom', 5)
max_zoom = ProjectSettings.get_setting('application/config/map_maximum_zoom', 19)
drag_fsm = DragFSM.OFF
var btns_select: Array = $btns_select.get_children()
btns_select.any(func(btn): btn.button_connect('pressed', Callable(self, 'on_button_select').bind(btn, btns_select)))
$btn_view.button_connect('toggled', Callable(self, 'on_button_view_toggled'))
var nodes: Array = get_tree().get_nodes_in_group('группа-режим-помехи')
nodes.any(func(btn): btn.button_connect('pressed', Callable(self, 'on_button_pressed').bind(btn, nodes)))
threats.connect('threat_new', Callable(self, 'on_threat_new'))
threats.connect('threat_lost', Callable(self, 'on_threat_lost'))
threats.connect('threats_resized', Callable(self, 'on_threats_resized'))
threats.connect('threat_update', Callable(self, 'on_threat_update'))
signaller.connect('rto_threat_sel', Callable(self, 'on_rto_threat_sel').bind(nodes))
signaller.connect('bip_pressed', Callable(self, 'on_bip_pressed').bind(nodes))
signaller.connect('rto_threat_unsel', Callable(self, 'on_rto_threat_unsel').bind(nodes))
signaller.connect('threat_selected', Callable(self, 'on_threat_selected').bind(nodes))
signaller.connect('interfer_selected', Callable(self, 'on_interfer_selected').bind(nodes))
signaller.connect('interfer_new', Callable(self, 'on_interfer_new'))
signaller.connect('interfer_off_all', Callable(self, 'on_interfer_off_all'))
signaller.connect('fs_selected', Callable(self, 'on_fs_selected'))
interfer.connect('interfer_update', Callable(self, 'on_interfer_new'))
func recurse(obj, items_list: Array):
var childrens = obj.get_children()
for ch in childrens:
items_list.append(ch)
recurse(ch, items_list)
func get_all_nodes(root):
var nodes = []
nodes.append(root)
for child in root.get_children():
nodes.append_array(get_all_nodes(child))
return nodes
func on_enter_tree() -> void:
call_deferred('_on_btn_scale_pressed')
call_deferred('on_button_strobe_pressed')
$tilemap.position = $canvas.get_strob_center()
var nodes = []
nodes.append(root)
for child in root.get_children():
nodes.append_array(get_all_nodes(child))
return nodes
## Производит отображение цели на диаграмму.
func map_threat_to_bip(th: threats.Threat, bip: Control):
bip.position = tools.pos_calc(
th.freq,
th.aoa,
radius_center,
radius_internal,
radius_outter,
freq_low,
freq_high) - bip.size / 2
bip.position = tools.pos_calc(
th.freq,
th.aoa,
radius_center,
radius_internal,
radius_outter,
freq_low,
freq_high) - bip.size / 2
## Обработчик сигнала обновления цели.
func on_threat_update(th):
var bip = get_node('%d' % th.id)
map_threat_to_bip(th, bip)
var bip = get_node('%d' % th.id)
bip.update()
map_threat_to_bip(th, bip)
## Обработчик выбора помехи для цели.
func on_interfer_new(ecm):
if interfer.thrs.has(ecm.ispp):
if interfer.thrs.has(ecm.ispp):
var th_id = interfer.thrs[ecm.ispp]
var bip = get_node('%d' % th_id)
if ecm.krp != interfer.INTERFER_OFF:
bip.set_track_visible(true)
if ecm.svk == 1:
bip.set_ecm_visible(true)
else: bip.set_ecm_visible(false)
else:
bip.set_ecm_visible(false)
bip.set_track_visible(false)
set_fs_color(ecm)
if interfer.thrs.has(ecm.ispp):
if interfer.thrs.has(ecm.ispp):
var th_id = interfer.thrs[ecm.ispp]
var bip = get_node('%d' % th_id)
if ecm.krp != interfer.INTERFER_OFF:
bip.set_track_visible(true)
if ecm.svk == 1:
bip.set_ecm_visible(true)
else: bip.set_ecm_visible(false)
else:
bip.set_ecm_visible(false)
bip.set_track_visible(false)
set_fs_color(ecm)
## Обработчик выключения всех помех.
func on_interfer_off_all():
var bips: Array = get_children().filter(func(item): return item.name.is_valid_int())
for bip in bips:
bip.set_ecm_visible(false)
bip.set_track_visible(false)
fs_active.clear()
update_fs_colors()
var bips: Array = get_children().filter(func(item): return item.name.is_valid_int())
for bip in bips:
bip.set_ecm_visible(false)
bip.set_track_visible(false)
fs_active.clear()
update_fs_colors()
## Обработчик сигнала обнаружения цели.
func on_threat_new(th) -> void:
var bip = Bip.instantiate()
bip.name = '%d' % th.id
map_threat_to_bip(th, bip)
add_child(bip)
var bip = Bip.instantiate()
bip.name = '%d' % th.id
map_threat_to_bip(th, bip)
add_child(bip)
## Обработчик сигнала потери цели.
func on_threat_lost(th) -> void:
var bip = get_node('%d' % th.id)
remove_child(bip)
bip.free()
var bip = get_node('%d' % th.id)
remove_child(bip)
bip.free()
## обработчик нажатия на цель на экране.
func on_bip_pressed(th_id, _ecm_btns):
if btn_select == BtnSel.THREAT:
signaller.emit_signal('rto_threat_sel', th_id)
if btn_select == BtnSel.THREAT:
signaller.emit_signal('rto_threat_sel', th_id)
## Обработчик выбора РТО цели.
func on_rto_threat_sel(th_id, ecm_btns):
var bips: Array = get_children().filter(func(item): return item.name.is_valid_int())
for item in bips:
if th_id == item.name.to_int():
item.set_sel_visible(true)
unsel_fs_all(false)
else:
item.set_sel_visible(false)
for btn in ecm_btns:
set_btns_disabled(false, btn)
var bips: Array = get_children().filter(func(item): return item.name.is_valid_int())
for item in bips:
if th_id == item.name.to_int():
item.set_sel_visible(true)
unsel_fs_all(false)
else:
item.set_sel_visible(false)
for btn in ecm_btns:
set_btns_disabled(false, btn)
## Обработчик снятия выбора РТО цели.
func on_rto_threat_unsel(th_id, _ecm_btns):
var bips: Array = get_children().filter(func(item): return item.name.is_valid_int())
for item in bips:
if th_id == item.name.to_int():
item.set_sel_visible(false)
break
set_ecm_btns_state(true)
var bips: Array = get_children().filter(func(item): return item.name.is_valid_int())
for item in bips:
if th_id == item.name.to_int():
item.set_sel_visible(false)
break
set_ecm_btns_state(true)
## Обработчик нажатия кнопки "Строб". Управляет отображением строба.
func on_button_strobe_pressed() -> void:
drag_fsm = DragFSM.IDLE if $btns_select/btn_strob.is_pressed() else DragFSM.OFF
$canvas.set_strob_visible(drag_fsm == DragFSM.IDLE)
drag_fsm = DragFSM.IDLE if $btns_select/btn_strob.is_pressed() else DragFSM.OFF
$canvas.set_strob_visible(drag_fsm == DragFSM.IDLE)
## Поведение радиокнопок в группе. Управляет состоянием кнопок выбора целей.
func on_button_select(btn_this, btn_others) -> void:
for btn_other in btn_others: btn_other.set_pressed(btn_this == btn_other)
btn_select = btn_this.get_meta('state')
on_button_strobe_pressed()
signaller.emit_signal('rto_threat_unsel_all')
unsel_fs_all(true)
set_ecm_btns_state(true)
for btn_other in btn_others: btn_other.set_pressed(btn_this == btn_other)
btn_select = btn_this.get_meta('state')
on_button_strobe_pressed()
signaller.emit_signal('rto_threat_unsel_all')
unsel_fs_all(true)
set_ecm_btns_state(true)
## Поведение радиокнопок в группе. Управляет состоянием кнопок в радиогруппе.
func on_button_pressed(btn_this, btn_others) -> void:
var fs_arr: Array = []
for btn_other in btn_others: btn_other.set_pressed(btn_this == btn_other)
for key in fs_selected.keys():
fs_arr.append(get_fs_param(key))
signaller.emit_signal('interfer_init', btn_this, fs_arr)
var fs_arr: Array = []
for btn_other in btn_others: btn_other.set_pressed(btn_this == btn_other)
for key in fs_selected.keys():
fs_arr.append(get_fs_param(key))
signaller.emit_signal('interfer_init', btn_this, fs_arr)
## Управляет состоянием кнопок в радиогруппе при выборе помехи.
func on_interfer_selected(ecm, btns) -> void:
var ecm_name: String
if ecm != null:
ecm_name = ecm.krp
else:
ecm_name = interfer.INTERFER_OFF
for btn in btns:
set_btns_disabled(false, btn)
btn.get_meta('rfi_name')
if ecm_name == btn.get_meta('rfi_name'):
btn.set_pressed(true)
else:
btn.set_pressed(false)
var ecm_name: String
if ecm != null:
ecm_name = ecm.krp
else:
ecm_name = interfer.INTERFER_OFF
for btn in btns:
set_btns_disabled(false, btn)
btn.get_meta('rfi_name')
if ecm_name == btn.get_meta('rfi_name'):
btn.set_pressed(true)
else:
btn.set_pressed(false)
## Возвращает ширину строба по направлению.
func get_strob_width(pos: Vector2, prev: float) -> float:
var strob_dir_width: float = prev + (drag_pos_begin.y - pos.y) * drag_scale_width
strob_dir_width = tools.clip_value(strob_dir_width, strob_min_width, strob_max_width)
return strob_dir_width
var strob_dir_width: float = prev + (drag_pos_begin.y - pos.y) * drag_scale_width
strob_dir_width = tools.clip_value(strob_dir_width, strob_min_width, strob_max_width)
return strob_dir_width
## Возвращает ширину строба по частоте.
func get_strob_band(pos: Vector2, prev: float) -> float:
var strob_dir_band: float = prev - (drag_pos_begin.y - pos.y) * drag_scale_band
strob_dir_band = tools.clip_value(strob_dir_band, strob_min_band, strob_max_band)
return strob_dir_band
var strob_dir_band: float = prev - (drag_pos_begin.y - pos.y) * drag_scale_band
strob_dir_band = tools.clip_value(strob_dir_band, strob_min_band, strob_max_band)
return strob_dir_band
## Устанавливает центр строба по частоте и направлению.
func set_strob(pos: Vector2) -> void:
var radp: Vector2 = tools.map_dec_to_rad(radius_center, pos)
if radp.y < $canvas.get_radius_outter():
$canvas.set_strob_dir(radp.x)
$canvas.set_strob_radius(radp.y)
var radp: Vector2 = tools.map_dec_to_rad(radius_center, pos)
if radp.y < $canvas.get_radius_outter():
$canvas.set_strob_dir(radp.x)
$canvas.set_strob_radius(radp.y)
## Обработка движения мыши для управления стробом. Вызывается во время движения мыши.
func _on_drag_continue(event) -> void:
if drag_button == MOUSE_BUTTON_RIGHT:
if shift_pressed:
var strob_width: float = get_strob_width(event.position, strob_width_begin)
$canvas.set_strob_width(strob_width)
else:
var strob_band: float = get_strob_band(event.position, strob_band_begin)
$canvas.set_strob_band(strob_band)
elif drag_button == MOUSE_BUTTON_LEFT:
set_strob(event.position)
if drag_button == MOUSE_BUTTON_RIGHT:
if shift_pressed:
var strob_width: float = get_strob_width(event.position, strob_width_begin)
$canvas.set_strob_width(strob_width)
else:
var strob_band: float = get_strob_band(event.position, strob_band_begin)
$canvas.set_strob_band(strob_band)
elif drag_button == MOUSE_BUTTON_LEFT:
set_strob(event.position)
## Обработка движения мыши для управления стробом. Вызывается при нажатии мыши.
func _on_drag_begin(event) -> void:
drag_pos_begin = event.position
if drag_button == MOUSE_BUTTON_LEFT:
set_strob(event.position)
elif drag_button == MOUSE_BUTTON_RIGHT:
strob_width_begin = $canvas.get_strob_width()
strob_band_begin = $canvas.get_strob_band()
drag_pos_begin = event.position
if drag_button == MOUSE_BUTTON_LEFT:
set_strob(event.position)
elif drag_button == MOUSE_BUTTON_RIGHT:
strob_width_begin = $canvas.get_strob_width()
strob_band_begin = $canvas.get_strob_band()
## Обработчик событий ввода. Вызывается godot.
func _input(event) -> void:
if not is_visible_in_tree(): return
if event is InputEventMouseMotion:
if drag_fsm == DragFSM.DRAG:
emit_signal('drag_continue', event)
elif event is InputEventMouseButton:
drag_button = event.button_index
if event.pressed:
if drag_fsm == DragFSM.IDLE:
drag_fsm = DragFSM.DRAG
emit_signal('drag_begin', event)
else:
if btn_select == BtnSel.FS:
sel_fs_sector(event)
drag_fsm = DragFSM.IDLE
emit_signal('drag_end', event)
if not is_visible_in_tree(): return
if event is InputEventMouseMotion:
if drag_fsm == DragFSM.DRAG:
emit_signal('drag_continue', event)
elif event is InputEventMouseButton:
drag_button = event.button_index
if event.pressed:
if drag_fsm == DragFSM.IDLE:
drag_fsm = DragFSM.DRAG
emit_signal('drag_begin', event)
else:
if btn_select == BtnSel.FS:
sel_fs_sector(event)
drag_fsm = DragFSM.IDLE
emit_signal('drag_end', event)
elif event is InputEventKey:
if event.physical_keycode == KEY_SHIFT:
shift_pressed = event.pressed
## Вызывается по нажатию кнопки "Детализация".
func _on_btn_scale_pressed() -> void:
var zoom = $btn_zoom.get_meta('zoom')
zoom = zoom + 1 if zoom < max_zoom else min_zoom
signaller.emit_signal('zoom_changed', zoom)
## Вызывается по завершению изменения детализации карты.
func _on_zoom_complete(zoom: int) -> void:
var prefix = $btn_zoom.get_meta('prefix')
$btn_zoom/lbl.text = '%s %d' % [prefix, zoom]
$btn_zoom.set_meta('zoom', zoom)
elif event is InputEventKey:
if event.physical_keycode == KEY_SHIFT:
shift_pressed = event.pressed
## Обработчик нажатия по сектору формирователя сигналов.
func sel_fs_sector(event):
if (event.button_index == MOUSE_BUTTON_LEFT) or (event.button_index == MOUSE_BUTTON_RIGHT):
var center: Vector2 = $tilemap.position
var center_offset: Vector2 = ProjectSettings.get_setting('application/interfer/center_offset', Vector2i(0, 25))
var angle: float = event.position.angle_to_point(center + center_offset) * 180/PI - 45.0
if angle < 0:
angle += 360
var distance: float = event.position.distance_to(center + center_offset)
var sector: int
var band: int
var has_band: bool = false
for sec in fs_sectors.values():
if angle >= sec[0] and angle <= sec[1]:
sector = fs_sectors.find_key(sec)
for diap in fs_band.values():
if distance >= diap[0] and distance <= diap[1]:
band = fs_band.find_key(diap)
has_band = true
if not has_band:
return
var fs_index = band * 4 + sector
if event.button_index == MOUSE_BUTTON_LEFT:
fs_selected[fs_index] = null
else:
fs_selected.erase(fs_index)
update_fs_colors()
signaller.emit_signal('rto_threat_unsel_all')
var btn_state = len(fs_selected) == 0
set_ecm_btns_state(btn_state)
if (event.button_index == MOUSE_BUTTON_LEFT) or (event.button_index == MOUSE_BUTTON_RIGHT):
var center: Vector2 = $tilemap.position
var center_offset: Vector2 = ProjectSettings.get_setting('application/interfer/center_offset', Vector2i(0, 25))
var angle: float = event.position.angle_to_point(center + center_offset) * 180/PI - 45.0
if angle < 0:
angle += 360
var distance: float = event.position.distance_to(center + center_offset)
var sector: int
var band: int
var has_band: bool = false
for sec in fs_sectors.values():
if angle >= sec[0] and angle <= sec[1]:
sector = fs_sectors.find_key(sec)
for diap in fs_band.values():
if distance >= diap[0] and distance <= diap[1]:
band = fs_band.find_key(diap)
has_band = true
if not has_band:
return
var fs_index = band * 4 + sector
if event.button_index == MOUSE_BUTTON_LEFT:
fs_selected[fs_index] = null
else:
fs_selected.erase(fs_index)
update_fs_colors()
signaller.emit_signal('rto_threat_unsel_all')
var btn_state = len(fs_selected) == 0
set_ecm_btns_state(btn_state)
func set_fs_color(ecm):
var fs_index = get_fs_index(ecm)
if ecm.krp != interfer.INTERFER_OFF:
fs_active[fs_index] = null
else:
fs_active.erase(fs_index)
update_fs_colors()
var fs_index = get_fs_index(ecm)
if ecm.krp != interfer.INTERFER_OFF:
fs_active[fs_index] = null
else:
fs_active.erase(fs_index)
update_fs_colors()
func update_fs_colors():
var fs_colors = $canvas.fs_default_colors.duplicate()
var color_ecm: Vector3 = ProjectSettings.get_setting('application/interfer/interfer_color', Vector3(0.58, 0.47, 0.51))
var color_select: Vector3 = ProjectSettings.get_setting('application/interfer/select_color', Vector3(1.0, 1.0, 1.0))
for key in fs_active.keys():
fs_colors[key] = color_ecm
for key in fs_selected.keys():
fs_colors[key] = color_select
$canvas.set_band_colors(fs_colors)
var fs_colors = $canvas.fs_default_colors.duplicate()
var color_ecm: Vector3 = ProjectSettings.get_setting('application/interfer/interfer_color', Vector3(0.58, 0.47, 0.51))
var color_select: Vector3 = ProjectSettings.get_setting('application/interfer/select_color', Vector3(1.0, 1.0, 1.0))
for key in fs_active.keys():
fs_colors[key] = color_ecm
for key in fs_selected.keys():
fs_colors[key] = color_select
$canvas.set_band_colors(fs_colors)
func unsel_fs_all(need_btns_off: bool):
fs_selected.clear()
update_fs_colors()
if need_btns_off:
set_ecm_btns_state(true)
fs_selected.clear()
update_fs_colors()
if need_btns_off:
set_ecm_btns_state(true)
func set_ecm_btns_state(value: bool):
var btns: Array = get_tree().get_nodes_in_group('группа-режим-помехи')
for btn in btns:
btn.set_pressed(false)
set_btns_disabled(value, btn)
var btns: Array = get_tree().get_nodes_in_group('группа-режим-помехи')
for btn in btns:
btn.set_pressed(false)
set_btns_disabled(value, btn)
func get_fs_param(index: int) -> Array:
var freq = (fs_freq[index / 4][0] + fs_freq[index / 4][1])/2
var sector = (fs_sectors[index % 4][0] + fs_sectors[index % 4][1])/2 - 45.0
if sector < 0:
sector += 360.0
var width = fs_freq[index / 4][1] - fs_freq[index / 4][0]
return [sector, freq, width, index]
var freq = (fs_freq[index / 4][0] + fs_freq[index / 4][1])/2
var sector = (fs_sectors[index % 4][0] + fs_sectors[index % 4][1])/2 - 45.0
if sector < 0:
sector += 360.0
var width = fs_freq[index / 4][1] - fs_freq[index / 4][0]
return [sector, freq, width, index]
func on_fs_selected(ecm):
var fs_index = get_fs_index(ecm)
fs_selected.clear()
fs_selected[fs_index] = null
update_fs_colors()
var fs_index = get_fs_index(ecm)
fs_selected.clear()
fs_selected[fs_index] = null
update_fs_colors()
func get_fs_index(ecm):
var sector: int
var band: int
var angle: float = ecm.kni + 45.0
if angle > 360.0:
angle -= 360.0
for sec in fs_sectors.values():
if float(angle) >= sec[0] and float(angle) <= sec[1]:
sector = fs_sectors.find_key(sec)
for diap in fs_freq.values():
if ecm.params.has('freq'):
if ecm.params['freq'] >= diap[0] and ecm.params['freq'] <= diap[1]:
band = fs_freq.find_key(diap)
var fs_index = band * 4 + sector
return fs_index
var sector: int
var band: int
var angle: float = ecm.kni + 45.0
if angle > 360.0:
angle -= 360.0
for sec in fs_sectors.values():
if float(angle) >= sec[0] and float(angle) <= sec[1]:
sector = fs_sectors.find_key(sec)
for diap in fs_freq.values():
if ecm.params.has('freq'):
if ecm.params['freq'] >= diap[0] and ecm.params['freq'] <= diap[1]:
band = fs_freq.find_key(diap)
var fs_index = band * 4 + sector
return fs_index
func set_btns_disabled(value: bool, btn):
var external_cu = ProjectSettings.get_setting('application/config/external_cu', false)
if external_cu:
btn.set_pressed(false)
btn.set_disabled(true)
else:
btn.set_disabled(value)
var external_cu = ProjectSettings.get_setting('application/config/external_cu', false)
if external_cu:
btn.set_pressed(false)
btn.set_disabled(true)
else:
btn.set_disabled(value)

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=15 format=3 uid="uid://b276iygic5itk"]
[gd_scene load_steps=12 format=3 uid="uid://b276iygic5itk"]
[ext_resource type="Script" path="res://scenes/работа/работа.gd" id="1_niok4"]
[ext_resource type="PackedScene" uid="uid://nl1vklubr5kr" path="res://scenes/bip/bip.tscn" id="2_br3s6"]
@@ -7,17 +7,10 @@
[ext_resource type="PackedScene" uid="uid://dymo732qc2doa" path="res://scenes/count-all/count-all-pad.tscn" id="6_e3re7"]
[ext_resource type="PackedScene" uid="uid://bwti82byphl68" path="res://scenes/count-danger/count_danger_pad.tscn" id="7_2arls"]
[ext_resource type="PackedScene" uid="uid://da7w3vkhadfwe" path="res://scenes/button-flat/button-flat.tscn" id="8_k0iv2"]
[ext_resource type="PackedScene" uid="uid://0uqi1ghf7sma" path="res://scenes/button-scale/btn-scale.tscn" id="9_oey3x"]
[ext_resource type="PackedScene" uid="uid://bbq6nklh36yij" path="res://scenes/tilemap/tilemap.tscn" id="10_gtwyg"]
[ext_resource type="PackedScene" uid="uid://ceknxxfu1loo5" path="res://scenes/tilemap/tilemap.tscn" id="10_gtwyg"]
[ext_resource type="PackedScene" uid="uid://b5kjdyxuwsot5" path="res://scenes/diagram/diagram.tscn" id="11_b6su4"]
[ext_resource type="Shader" path="res://shaders/tilemap.gdshader" id="11_ekapd"]
[ext_resource type="Script" path="res://scenes/работа/sld-mode.gd" id="12_gqdkl"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_4p7y1"]
resource_local_to_scene = true
shader = ExtResource("11_ekapd")
shader_parameter/radius = 0.39
[sub_resource type="ImageTexture" id="ImageTexture_cnmvt"]
[node name="Работа" type="Panel"]
@@ -41,7 +34,8 @@ metadata/_edit_vertical_guides_ = []
[node name="canvas" parent="." instance=ExtResource("11_b6su4")]
[node name="tilemap" parent="." instance=ExtResource("10_gtwyg")]
material = SubResource("ShaderMaterial_4p7y1")
self_modulate = Color(1, 1, 1, 0.415686)
position = Vector2(360, 360)
[node name="grd_side" parent="." instance=ExtResource("3_s2j0e")]
layout_mode = 0
@@ -49,7 +43,6 @@ offset_left = 1114.0
offset_top = 7.0
offset_right = 1594.0
offset_bottom = 1170.0
metadata/_edit_lock_ = true
[node name="grd_btns" parent="." instance=ExtResource("5_2jnbs")]
layout_mode = 0
@@ -108,17 +101,19 @@ offset_right = 111.0
offset_bottom = 953.0
size_flags_horizontal = 3
size_flags_vertical = 3
strips_rotation = 25.0
text = "Вид"
toggle_mode = true
[node name="btn_activate" parent="." instance=ExtResource("8_k0iv2")]
layout_mode = 2
offset_left = 954.0
offset_left = 906.0
offset_top = 1060.0
offset_right = 1106.0
offset_bottom = 1112.0
size_flags_horizontal = 3
size_flags_vertical = 3
strips_rotation = 15.0
text = "Подавление"
toggle_mode = true
@@ -149,14 +144,6 @@ layout_mode = 0
offset_right = 40.0
offset_bottom = 40.0
[node name="btn_zoom" parent="." instance=ExtResource("9_oey3x")]
layout_mode = 0
offset_left = 12.0
offset_top = 986.0
offset_right = 112.0
offset_bottom = 1006.0
metadata/zoom = 4
[node name="btns_select" type="GridContainer" parent="."]
layout_mode = 0
offset_left = 1018.0
@@ -169,6 +156,7 @@ layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
tooltip_text = "Кнопка для выбора сектора при помощи клика мышкой"
strips_rotation = 25.0
text = "Строб"
toggle_mode = true
metadata/state = 0
@@ -178,6 +166,7 @@ layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
tooltip_text = "Кнопка для выбора сектора при помощи клика мышкой"
strips_rotation = 25.0
text = "Цель"
toggle_mode = true
metadata/state = 1
@@ -187,6 +176,7 @@ layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
tooltip_text = "Кнопка для выбора сектора при помощи клика мышкой"
strips_rotation = 25.0
text = "Сектор"
toggle_mode = true
metadata/state = 2
@@ -265,5 +255,4 @@ vertical_alignment = 1
[connection signal="drag_begin" from="." to="." method="_on_drag_begin"]
[connection signal="drag_continue" from="." to="." method="_on_drag_continue"]
[connection signal="pressed" from="btn_zoom" to="." method="_on_btn_scale_pressed"]
[connection signal="value_changed" from="vslider" to="vslider" method="_on_value_changed"]

View File

@@ -29,6 +29,7 @@ offset_left = 1446.0
offset_top = 55.0
offset_right = 1583.0
offset_bottom = 85.0
strips_rotation = 10.0
text = "Прочитать"
[node name="btn_flash_write" parent="." instance=ExtResource("2_eju8r")]
@@ -37,6 +38,7 @@ offset_left = 1446.0
offset_top = 99.0
offset_right = 1583.0
offset_bottom = 129.0
strips_rotation = 10.0
text = "Записать"
[node name="btn_new" type="Button" parent="."]

View File

@@ -9,42 +9,42 @@ const BUFFER_SIZE = 2048 ## Размер приёмного буфера д
class CapsRpb:
signal line_changed(unit: CapsRpb) ## Сигнал состояние связи изменилось.
signal data_received(unit: CapsRpb) ## Сигнал данные приняты.
signal command_fail(unit: CapsRpb) ## Сигнал при не выполнении команды.
signal parse_error(unit: CapsRpb, msg: String) ## Сигнал ошибки разбора сообщения.
signal line_changed(unit: CapsRpb) ## Сигнал состояние связи изменилось.
signal data_received(unit: CapsRpb) ## Сигнал данные приняты.
signal command_fail(unit: CapsRpb) ## Сигнал при не выполнении команды.
signal parse_error(unit: CapsRpb, msg: String) ## Сигнал ошибки разбора сообщения.
static var json_conv: = JSON.new()
var online: = false ## Состояние связи с источником сообщений.
var name: String ## Имя экземпляра.
var json_dic: Dictionary ## Аппарат для разбора сообщений.
var tick: = 0 ## Тик время последнего обновления, мс.
var tx_data: = PackedByteArray() ## Пакет для отправки.
var tx_stack: Array ## Массив пакетов для отправки.
static var json_conv: = JSON.new()
var online: = false ## Состояние связи с источником сообщений.
var name: String ## Имя экземпляра.
var json_dic: Dictionary ## Аппарат для разбора сообщений.
var tick: = 0 ## Тик время последнего обновления, мс.
var tx_data: = PackedByteArray() ## Пакет для отправки.
var tx_stack: Array ## Массив пакетов для отправки.
func _to_string() -> String: return String('капсрпб: "%s" %s ' % [self.name, ['отключен', 'на связи'][int(online)]])
func _init(nm): self.name = nm
func _to_string() -> String: return String('капсрпб: "%s" %s ' % [self.name, ['отключен', 'на связи'][int(online)]])
func _init(nm): self.name = nm
func parse(data: PackedByteArray, now_tick: int):
tick = now_tick
if not online:
online = true
emit_signal('line_changed', self)
var utf8_data = data.get_string_from_utf8()
if Error.OK == json_conv.parse(utf8_data):
json_dic.merge(json_conv.get_data(), true)
emit_signal('data_received', self)
else:
var msg = json_conv.get_error_message()
emit_signal('parse_error', self, msg)
func parse(data: PackedByteArray, now_tick: int):
tick = now_tick
if not online:
online = true
emit_signal('line_changed', self)
var utf8_data = data.get_string_from_utf8()
if Error.OK == json_conv.parse(utf8_data):
json_dic.merge(json_conv.get_data(), true)
emit_signal('data_received', self)
else:
var msg = json_conv.get_error_message()
emit_signal('parse_error', self, msg)
func process(now_tick: int):
if online and ((now_tick - tick) > ONLINE_TIMEOUT):
online = false
emit_signal('line_changed', self)
if len(tx_stack):
tx_data = tx_stack[0]
tx_stack.remove_at(0)
return Error.OK
else:
return Error.ERR_UNAVAILABLE
func process(now_tick: int):
if online and ((now_tick - tick) > ONLINE_TIMEOUT):
online = false
emit_signal('line_changed', self)
if len(tx_stack):
tx_data = tx_stack[0]
tx_stack.remove_at(0)
return Error.OK
else:
return Error.ERR_UNAVAILABLE

View File

@@ -4,22 +4,22 @@ class_name Iterfer extends Node
## Режимы помехи.
const ITERFER_MODE = {
'off': ['Откл', ['freq', 'width'], 0],
'auto': ['АВТО', ['freq', 'width'], 1000],
'srni1': ['СРН-И1', ['freq', 'width'], 1],
'srnm2': ['СРН-М2', ['freq', 'width'], 2],
'srngps': ['СРН-GPS', ['freq', 'width'], 3],
'lchm1': ['ЛЧМ-1', ['freq', 'width'], 10],
'lchm2': ['ЛЧМ-2', ['freq', 'width'], 4],
'mchm1': ['МЧМ-1', ['freq', 'width'], 11],
'pdm2': ['ПД-М2', ['freq', 'width'], 12],
'pdm4': ['ПД-М4', ['freq', 'width'], 13],
'pdm4s': ['ПД-М4С', ['freq', 'width'], 14],
'pdchm': ['ПД-ЧМ', ['freq', 'width'], 15],
'svi1': ['СВ-И1', ['freq', 'width'], 20],
'svm2': ['СВ-М2', ['freq', 'width'], 21],
'k1': ['K1', ['freq', 'width', 'dur', 'period'], 50],
'ozp1': ['ОЗП-1', ['freq', 'width'], 51] }
'off': ['Откл', ['freq', 'width'], 0],
'auto': ['АВТО', ['freq', 'width'], 1000],
'srni1': ['СРН-И1', ['freq', 'width'], 1],
'srnm2': ['СРН-М2', ['freq', 'width'], 2],
'srngps': ['СРН-GPS', ['freq', 'width'], 3],
'lchm1': ['ЛЧМ-1', ['freq', 'width'], 10],
'lchm2': ['ЛЧМ-2', ['freq', 'width'], 4],
'mchm1': ['МЧМ-1', ['freq', 'width'], 11],
'pdm2': ['ПД-М2', ['freq', 'width'], 12],
'pdm4': ['ПД-М4', ['freq', 'width'], 13],
'pdm4s': ['ПД-М4С', ['freq', 'width'], 14],
'pdchm': ['ПД-ЧМ', ['freq', 'width'], 15],
'svi1': ['СВ-И1', ['freq', 'width'], 20],
'svm2': ['СВ-М2', ['freq', 'width'], 21],
'k1': ['K1', ['freq', 'width', 'dur', 'period'], 50],
'ozp1': ['ОЗП-1', ['freq', 'width'], 51] }
const FREQ_MAX = 6000
const FREQ_MIN = 400
@@ -38,52 +38,52 @@ var cu: Dictionary ## Словарь воздействий для 5
## Сеанс помехи.
class Interfer:
var ss: int = 0 ## Счётчик сообщений.
var ispp: int = 0 ## Индекс сеанса.
var svk: = 0 ## Состояние сеанса. Ответ КАПСРПБ. Возможные значения в [param ITERFER_STATE].
var krp: String = '' ## Тип помехи. Возможные значения в [param INTERFER_MODE].
var powp: float = 0.0 ## Мощность излучения выраженная в процентах.
var kni: = 0.0 ## Курсовой угол направления излучения.
var params: = {} ## Параметры помехи.
var get_params: Array[String] ## Запрос массива параметров (список запрошенных параметров).
var retss: int ## Значение счётчика сообщения в котором был произведён запрос массива параметров.
var tick_tx: int ## Тик время обновления цели, мс.
var source_interfer: String = '' ## Тип объекта по которой поставлена помеха.
var ss: int = 0 ## Счётчик сообщений.
var ispp: int = 0 ## Индекс сеанса.
var svk: = 0 ## Состояние сеанса. Ответ КАПСРПБ. Возможные значения в [param ITERFER_STATE].
var krp: String = '' ## Тип помехи. Возможные значения в [param INTERFER_MODE].
var powp: float = 0.0 ## Мощность излучения выраженная в процентах.
var kni: = 0.0 ## Курсовой угол направления излучения.
var params: = {} ## Параметры помехи.
var get_params: Array[String] ## Запрос массива параметров (список запрошенных параметров).
var retss: int ## Значение счётчика сообщения в котором был произведён запрос массива параметров.
var tick_tx: int ## Тик время обновления цели, мс.
var source_interfer: String = '' ## Тип объекта по которой поставлена помеха.
func _to_string() -> String:
return 'номер: %d название: %s частота: %0.1f МГц пеленг: %0.1f гр.' % [ispp, ITERFER_MODE[krp][0], params['freq'], kni]
func _to_string() -> String:
return 'номер: %d название: %s частота: %0.1f МГц пеленг: %0.1f гр.' % [ispp, ITERFER_MODE[krp][0], params['freq'], kni]
func get_msg6_data():
var data_to_json : Dictionary = {}
data_to_json['ts'] = 6
data_to_json['ss'] = ss
data_to_json['ispp'] = ispp
data_to_json['krp'] = krp
if krp != INTERFER_OFF:
data_to_json['kni'] = kni
data_to_json['powp'] = powp # Необходимо добавить управление мощностью
data_to_json['get'] = []
data_to_json['params'] = params
var json = JSON.stringify(data_to_json)
var pac = json.to_utf8_buffer()
return pac
func get_msg6_data():
var data_to_json : Dictionary = {}
data_to_json['ts'] = 6
data_to_json['ss'] = ss
data_to_json['ispp'] = ispp
data_to_json['krp'] = krp
if krp != INTERFER_OFF:
data_to_json['kni'] = kni
data_to_json['powp'] = powp # Необходимо добавить управление мощностью
data_to_json['get'] = []
data_to_json['params'] = params
var json = JSON.stringify(data_to_json)
var pac = json.to_utf8_buffer()
return pac
func set_from_dic(dic: Dictionary):
var names: = tools.get_members_names(self)
names.any(func(vn): if dic.has(vn): set(vn, dic[vn]); return false)
func set_from_dic(dic: Dictionary):
var names: = tools.get_members_names(self)
names.any(func(vn): if dic.has(vn): set(vn, dic[vn]); return false)
func get_value(value_key):
var param = self.get(value_key)
if param == null:
if params.has(value_key): param = params[value_key]
return param
func get_value(value_key):
var param = self.get(value_key)
if param == null:
if params.has(value_key): param = params[value_key]
return param
func set_value(value, value_key):
var param = self.get(value_key)
if param == null:
params[value_key] = value
else:
self.set(value_key, value)
func set_value(value, value_key):
var param = self.get(value_key)
if param == null:
params[value_key] = value
else:
self.set(value_key, value)
signal interfer_preparing(ecm: Interfer) ## Вызывается в момент запуска помехи.
@@ -94,172 +94,172 @@ signal interfer_resized(ecms: Dictionary) ## Вызывается пр
func _ready():
var ecms: Dictionary = {} ## Словарь помех.
for i in range(ID_INTERFERS_LEN): # Заполняет массив ID целей
id_array.append(i + 1)
var unit_key = repsettings.get_unit_key('уарэп-капсрпб-отп')
var unit = repnetwork.units[unit_key]
var unit_5p28_key = repsettings.get_unit_key('уарэп-5п28')
var unit_5p28 = repnetwork.units_tcp[unit_5p28_key]
var timer_check_state: = Timer.new()
add_child(timer_check_state)
var resend_timeout = ProjectSettings.get_setting('application/interfer/resend_timeout', 1000.0)
timer_check_state.connect('timeout', Callable(self, 'on_timer_check_state').bind(unit, ecms, resend_timeout))
timer_check_state.start(resend_timeout / 1000.0)
unit.connect('data_received', Callable(self, 'on_data_capsrpb_received').bind(ecms))
unit_5p28.connect('get_interfers', Callable(self, 'on_get_interfers').bind(unit_5p28, ecms))
signaller.connect('interfer_create', Callable(self, 'on_interfer_create').bind(unit, ecms))
signaller.connect('interfer_rcv', Callable(self, 'on_interfer_rcv').bind(unit_5p28, ecms))
signaller.connect('interfer_new', Callable(self, 'send_ecm').bind(unit))
signaller.connect('interfer_off_all', Callable(self, 'on_interfer_off_all').bind(unit, ecms))
var ecms: Dictionary = {} ## Словарь помех.
for i in range(ID_INTERFERS_LEN): # Заполняет массив ID целей
id_array.append(i + 1)
var unit_key = repsettings.get_unit_key('уарэп-капсрпб-отп')
var unit = repnetwork.units[unit_key]
var unit_5p28_key = repsettings.get_unit_key('уарэп-5п28')
var unit_5p28 = repnetwork.units_tcp[unit_5p28_key]
var timer_check_state: = Timer.new()
add_child(timer_check_state)
var resend_timeout = ProjectSettings.get_setting('application/interfer/resend_timeout', 1000.0)
timer_check_state.connect('timeout', Callable(self, 'on_timer_check_state').bind(unit, ecms, resend_timeout))
timer_check_state.start(resend_timeout / 1000.0)
unit.connect('data_received', Callable(self, 'on_data_capsrpb_received').bind(ecms))
unit_5p28.connect('get_interfers', Callable(self, 'on_get_interfers').bind(unit_5p28, ecms))
signaller.connect('interfer_create', Callable(self, 'on_interfer_create').bind(unit, ecms))
signaller.connect('interfer_rcv', Callable(self, 'on_interfer_rcv').bind(unit_5p28, ecms))
signaller.connect('interfer_new', Callable(self, 'send_ecm').bind(unit))
signaller.connect('interfer_off_all', Callable(self, 'on_interfer_off_all').bind(unit, ecms))
## Проверяет состояние отправки пакета и если не доставлен, повторяет отправку.
func on_timer_check_state(unit, ecms, resend_timeout):
var tick = Time.get_ticks_msec()
for ecm in ecms.values():
if ecm.svk == STATE_TSMT:
if (tick - ecm.tick_tx) >= resend_timeout:
send_ecm(ecm, unit)
ecm.tick_tx = tick
var tick = Time.get_ticks_msec()
for ecm in ecms.values():
if ecm.svk == STATE_TSMT:
if (tick - ecm.tick_tx) >= resend_timeout:
send_ecm(ecm, unit)
ecm.tick_tx = tick
## Производит приём состояния помех. [param unit] -> [param ecms].
func on_data_capsrpb_received(unit: capsrpb.CapsRpb, ecms: Dictionary):
if not unit.json_dic.has('ts'): return
if not (unit.json_dic['ts'] is float): return
if unit.json_dic['ts'] != 5: return
if not unit.json_dic.has('msvk'): return
if not (unit.json_dic['msvk'] is Array): return
var msvk = unit.json_dic['msvk']
var sz: = ecms.size()
for dic_msvk in msvk:
if not (dic_msvk is Dictionary): continue
if dic_msvk.has('ispp'):
var ispp = int(dic_msvk['ispp'])
if ecms.has(ispp):
var ecm = ecms[ispp]
ecm.set_from_dic(dic_msvk)
call_deferred('emit_signal', 'interfer_update', ecm)
else:
var ecm = Interfer.new()
ecm.set_from_dic(dic_msvk)
ecm.krp = INTERFER_OFF
send_ecm(ecm, unit)
if sz != ecms.size():
call_deferred('emit_signal', 'interfer_resized', ecms)
if not unit.json_dic.has('ts'): return
if not (unit.json_dic['ts'] is float): return
if unit.json_dic['ts'] != 5: return
if not unit.json_dic.has('msvk'): return
if not (unit.json_dic['msvk'] is Array): return
var msvk = unit.json_dic['msvk']
var sz: = ecms.size()
for dic_msvk in msvk:
if not (dic_msvk is Dictionary): continue
if dic_msvk.has('ispp'):
var ispp = int(dic_msvk['ispp'])
if ecms.has(ispp):
var ecm = ecms[ispp]
ecm.set_from_dic(dic_msvk)
call_deferred('emit_signal', 'interfer_update', ecm)
else:
var ecm = Interfer.new()
ecm.set_from_dic(dic_msvk)
ecm.krp = INTERFER_OFF
send_ecm(ecm, unit)
if sz != ecms.size():
call_deferred('emit_signal', 'interfer_resized', ecms)
## Вызыватся, когда пользователь выбрал помеху и выбраны цели.
func on_interfer_create(btn, sel_threats, fs_arr, _unit, ecms):
for item in sel_threats:
var param: Dictionary = {}
var kni: float = item.aoa
var id = item.id
var interfer_name: String = btn.get_meta('rfi_name')
param['freq'] = item.freq
param['width'] = item.width
interfer_create(interfer_name, ecms, param, kni, id, thrs, SOURCE_INTERFER[0])
for item in sel_threats:
var param: Dictionary = {}
var kni: float = item.aoa
var id = item.id
var interfer_name: String = btn.get_meta('rfi_name')
param['freq'] = item.freq
param['width'] = item.width
interfer_create(interfer_name, ecms, param, kni, id, thrs, SOURCE_INTERFER[0])
for item in fs_arr:
var param: Dictionary = {}
var kni: float = item[0]
var id = item[3]
var interfer_name: String = btn.get_meta('rfi_name')
param['freq'] = item[1]
param['width'] = item[2]
interfer_create(interfer_name, ecms, param, kni, id, fs, SOURCE_INTERFER[1])
for item in fs_arr:
var param: Dictionary = {}
var kni: float = item[0]
var id = item[3]
var interfer_name: String = btn.get_meta('rfi_name')
param['freq'] = item[1]
param['width'] = item[2]
interfer_create(interfer_name, ecms, param, kni, id, fs, SOURCE_INTERFER[1])
func on_interfer_rcv(interfer_params: Array, unit, ecms):
var err: bool = false
var mode = interfer_params[0]
var id = interfer_params[1]
var aoa = interfer_params[2]
if (aoa < 0) and (aoa > 360):
err = true
var param: Dictionary = {}
param['freq'] = interfer_params[4]
if (param['freq'] < FREQ_MIN) and (param['freq'] > FREQ_MAX):
err = true
param['width'] = interfer_params[5]
if (param['width'] < WIDTH_MIN) and (param['width'] > WIDTH_MAX):
err = true
var interfer_name: String
for key in ITERFER_MODE:
if ITERFER_MODE[key][2] == mode:
interfer_name = key
break
if interfer_name == '':
err = true
if not err:
interfer_create(interfer_name, ecms, param, aoa, id, cu, SOURCE_INTERFER[2])
unit.on_interfer_accept(err)
var err: bool = false
var mode = interfer_params[0]
var id = interfer_params[1]
var aoa = interfer_params[2]
if (aoa < 0) and (aoa > 360):
err = true
var param: Dictionary = {}
param['freq'] = interfer_params[4]
if (param['freq'] < FREQ_MIN) and (param['freq'] > FREQ_MAX):
err = true
param['width'] = interfer_params[5]
if (param['width'] < WIDTH_MIN) and (param['width'] > WIDTH_MAX):
err = true
var interfer_name: String
for key in ITERFER_MODE:
if ITERFER_MODE[key][2] == mode:
interfer_name = key
break
if interfer_name == '':
err = true
if not err:
interfer_create(interfer_name, ecms, param, aoa, id, cu, SOURCE_INTERFER[2])
unit.on_interfer_accept(err)
func interfer_create(interfer_name, ecms, param, kni, id, dict, source_interfer):
var ispp: int = 0
if interfer_name == 'k1':
param['dur'] = 1.0 # Необходимо добавить параметры управления
param['period'] = 1.0 # Необходимо добавить параметры управления
var key = dict.find_key(id)
if source_interfer != SOURCE_INTERFER[2]:
if key != null:
ispp = key
else:
if interfer_name == 'off':
return
ispp = pop_id()
if ispp == 0: return
else: ispp = id
dict[ispp] = id
msg_ss += 1
var ecm: Interfer
if ecms.has(ispp):
ecm = ecms[ispp]
else:
ecm = Interfer.new()
ecm.ispp = ispp
ecms[ispp] = ecm
ecm.svk = STATE_TSMT # Подготовка к передаче сообщения
ecm.ss = msg_ss
ecm.kni = kni
ecm.krp = interfer_name
ecm.params = param
ecm.powp = 100.0 # Необходимо добавить управление мощностью
ecm.source_interfer = source_interfer
signaller.emit_signal('interfer_new', ecm)
if interfer_name == 'off':
signaller.emit_signal('interfer_off', ecm, ecms, thrs)
push_id(ecm.ispp)
thrs.erase(ispp)
ecm.tick_tx = Time.get_ticks_msec()
var ispp: int = 0
if interfer_name == 'k1':
param['dur'] = 1.0 # Необходимо добавить параметры управления
param['period'] = 1.0 # Необходимо добавить параметры управления
var key = dict.find_key(id)
if source_interfer != SOURCE_INTERFER[2]:
if key != null:
ispp = key
else:
if interfer_name == 'off':
return
ispp = pop_id()
if ispp == 0: return
else: ispp = id
dict[ispp] = id
msg_ss += 1
var ecm: Interfer
if ecms.has(ispp):
ecm = ecms[ispp]
else:
ecm = Interfer.new()
ecm.ispp = ispp
ecms[ispp] = ecm
ecm.svk = STATE_TSMT # Подготовка к передаче сообщения
ecm.ss = msg_ss
ecm.kni = kni
ecm.krp = interfer_name
ecm.params = param
ecm.powp = 100.0 # Необходимо добавить управление мощностью
ecm.source_interfer = source_interfer
signaller.emit_signal('interfer_new', ecm)
if interfer_name == 'off':
signaller.emit_signal('interfer_off', ecm, ecms, thrs)
push_id(ecm.ispp)
thrs.erase(ispp)
ecm.tick_tx = Time.get_ticks_msec()
func send_ecm(ecm, unit):
var json_data = ecm.get_msg6_data()
unit.tx_stack.append(json_data)
var json_data = ecm.get_msg6_data()
unit.tx_stack.append(json_data)
func on_interfer_off_all(unit, ecms):
for ecm in ecms.values():
ecm.krp = INTERFER_OFF
push_id(ecm.ispp)
send_ecm(ecm, unit)
signaller.emit_signal('interfer_off',ecm, ecms, thrs)
for ecm in ecms.values():
ecm.krp = INTERFER_OFF
push_id(ecm.ispp)
send_ecm(ecm, unit)
signaller.emit_signal('interfer_off',ecm, ecms, thrs)
func pop_id():
var id = 0
if len(id_array):
id = id_array[0]
id_array.remove_at(0)
return id
var id = 0
if len(id_array):
id = id_array[0]
id_array.remove_at(0)
return id
func push_id(id : int):
id_array.append(id)
id_array.sort()
id_array.append(id)
id_array.sort()
## Отправляет словарь помех в юнит, для запаковки и отправки в 5П28.

View File

@@ -45,9 +45,8 @@ class Tile:
func _bound_index(index: int) -> int:
var mi: int = 2 ** z
if index < 0:
index *= -1
index %= mi
index = mi - index
index = -index - 1;
index = int(mi - 1) - (index % int(mi))
else:
index %= mi
return index

View File

@@ -4,128 +4,128 @@ const ONLINE_TIMEOUT = 5.0 ## Время ожидания пакета от я
class ModbusClient:
## Состояние автомата выполнения команды.
enum CmdState {
DONE, ## Команда выполнена.
FAIL, ## Команда не выполнена.
SEND, ## Буфер готов для отправки.
WAIT ## Ожидание выполнения.
}
## Состояние автомата выполнения команды.
enum CmdState {
DONE, ## Команда выполнена.
FAIL, ## Команда не выполнена.
SEND, ## Буфер готов для отправки.
WAIT ## Ожидание выполнения.
}
var name: String
var unit: = 1 ## Адрес устройства Modbus.
var cmd_state: = CmdState.DONE ## Код текущей команды.
var tx_data: PackedByteArray ## Данные для отправки.
var online: = false ## Флаг. Если установлен, устройство на связи. Активный уровень - [b]true[/b].
var tick_rx: = 0 ## Время приёма крайнего сообщения.
var tick_tx: = 0 ## Время отправки крайнего сообщения.
var name: String
var unit: = 1 ## Адрес устройства Modbus.
var cmd_state: = CmdState.DONE ## Код текущей команды.
var tx_data: PackedByteArray ## Данные для отправки.
var online: = false ## Флаг. Если установлен, устройство на связи. Активный уровень - [b]true[/b].
var tick_rx: = 0 ## Время приёма крайнего сообщения.
var tick_tx: = 0 ## Время отправки крайнего сообщения.
static var json_conv: = JSON.new()
static var json_conv: = JSON.new()
signal command_done() ## Вызывается при успешном выполнении команды.
signal command_fail() ## Вызывается при возникновении ошибки во время выполнения команды.
signal line_changed() ## Вызывается при смене состояния связи с ячейкой.
signal data_received() ## Вызывается при приёме сообщения, до его разбора.
signal parse_failed(msg: String) ## Вызывается при возникновении ошибки во время разбора пакета. Передаёт сообщение об ошибке.
signal command_done() ## Вызывается при успешном выполнении команды.
signal command_fail() ## Вызывается при возникновении ошибки во время выполнения команды.
signal line_changed() ## Вызывается при смене состояния связи с ячейкой.
signal data_received() ## Вызывается при приёме сообщения, до его разбора.
signal parse_failed(msg: String) ## Вызывается при возникновении ошибки во время разбора пакета. Передаёт сообщение об ошибке.
signal holding_received(addr: int, data: Array) ## Вызывается при получении сообщения c данными из регистров хранения.
signal input_received(addr: int, data: Array) ## Вызывается при получении сообщения c данными из регистров входов.
signal info_received(msg: String) ## Вызывается при получении информационного сообщения.
signal except_received(msg: String) ## Вызывается при получении сообщения об исключительной ситуации.
signal holding_received(addr: int, data: Array) ## Вызывается при получении сообщения c данными из регистров хранения.
signal input_received(addr: int, data: Array) ## Вызывается при получении сообщения c данными из регистров входов.
signal info_received(msg: String) ## Вызывается при получении информационного сообщения.
signal except_received(msg: String) ## Вызывается при получении сообщения об исключительной ситуации.
func _init(nm: String, u: int = 1):
unit = u
name = nm
func _init(nm: String, u: int = 1):
unit = u
name = nm
## Изменяет состояние. Вызывать периодически.[br]
## [param tick] - Текущщее время в мс.[br]
func process(tick: int):
if online and ((tick - tick_rx) > ONLINE_TIMEOUT):
online = false
emit_signal('line_changed')
if (cmd_state == CmdState.WAIT) or (cmd_state == CmdState.SEND):
cmd_state = CmdState.FAIL
emit_signal('command_fail')
var rc = Error.ERR_BUSY
if cmd_state == CmdState.SEND:
rc = Error.OK
tick_tx = tick
cmd_state = CmdState.WAIT
return rc
## Изменяет состояние. Вызывать периодически.[br]
## [param tick] - Текущщее время в мс.[br]
func process(tick: int):
if online and ((tick - tick_rx) > ONLINE_TIMEOUT):
online = false
emit_signal('line_changed')
if (cmd_state == CmdState.WAIT) or (cmd_state == CmdState.SEND):
cmd_state = CmdState.FAIL
emit_signal('command_fail')
var rc = Error.ERR_BUSY
if cmd_state == CmdState.SEND:
rc = Error.OK
tick_tx = tick
cmd_state = CmdState.WAIT
return rc
## Производит разбор принятых данных. Вызывать при приёме данных на сокете.[br]
## [param data] - Данные принятые на сокете.[br]
## [param tick] - Текущщее время в мс.[br]
func parse(data: PackedByteArray, tick: int) -> bool:
tick_rx = tick
if not online:
online = true
emit_signal('line_changed')
var utf8_data = data.get_string_from_utf8()
if Error.OK == json_conv.parse(utf8_data):
emit_signal('data_received')
var message: Dictionary = json_conv.get_data()
if message.has('code') and message.has('data'):
var rc: = true
var message_code = message['code']
var message_data = message['data']
match message_code:
'read_input':
var addr = message_data[0]
var regs = message_data.slice(1)
emit_signal('input_received', addr, regs)
'read_holding':
var addr = message_data[0]
var regs = message_data.slice(1)
emit_signal('holding_received', addr, regs)
'info':
var msg = message['data']
emit_signal('info_received', msg)
'exception':
var msg = message['data']
emit_signal('except_received', msg)
rc = false
_:
var msg: String = 'код команды \"%s\" - не распознан' % message_code
emit_signal('parse_failed', msg)
rc = false
if (cmd_state == CmdState.WAIT) and rc:
cmd_state = CmdState.DONE
emit_signal('command_done')
elif (cmd_state == CmdState.WAIT) and (not rc):
cmd_state = CmdState.FAIL
emit_signal('command_fail')
else:
var message = json_conv.get_error_message()
emit_signal('parse_failed', message)
return true
## Производит разбор принятых данных. Вызывать при приёме данных на сокете.[br]
## [param data] - Данные принятые на сокете.[br]
## [param tick] - Текущщее время в мс.[br]
func parse(data: PackedByteArray, tick: int) -> bool:
tick_rx = tick
if not online:
online = true
emit_signal('line_changed')
var utf8_data = data.get_string_from_utf8()
if Error.OK == json_conv.parse(utf8_data):
emit_signal('data_received')
var message: Dictionary = json_conv.get_data()
if message.has('code') and message.has('data'):
var rc: = true
var message_code = message['code']
var message_data = message['data']
match message_code:
'read_input':
var addr = message_data[0]
var regs = message_data.slice(1)
emit_signal('input_received', addr, regs)
'read_holding':
var addr = message_data[0]
var regs = message_data.slice(1)
emit_signal('holding_received', addr, regs)
'info':
var msg = message['data']
emit_signal('info_received', msg)
'exception':
var msg = message['data']
emit_signal('except_received', msg)
rc = false
_:
var msg: String = 'код команды \"%s\" - не распознан' % message_code
emit_signal('parse_failed', msg)
rc = false
if (cmd_state == CmdState.WAIT) and rc:
cmd_state = CmdState.DONE
emit_signal('command_done')
elif (cmd_state == CmdState.WAIT) and (not rc):
cmd_state = CmdState.FAIL
emit_signal('command_fail')
else:
var message = json_conv.get_error_message()
emit_signal('parse_failed', message)
return true
## Отправляет команду "записать в регистры".[br]
## [param addr] - Начальный адрес регистров.[br]
## [param count] - Данные для записи в регистры от начального адреса.[br]
func write(addr: int, data: Array) -> bool:
if cmd_state != CmdState.DONE: return false
var json_dict = {'code': 'write', 'data': [addr] + data}
tx_data = JSON.stringify(json_dict).to_utf8_buffer()
cmd_state = CmdState.SEND
return true
## Отправляет команду "записать в регистры".[br]
## [param addr] - Начальный адрес регистров.[br]
## [param count] - Данные для записи в регистры от начального адреса.[br]
func write(addr: int, data: Array) -> bool:
if cmd_state != CmdState.DONE: return false
var json_dict = {'code': 'write', 'data': [addr] + data}
tx_data = JSON.stringify(json_dict).to_utf8_buffer()
cmd_state = CmdState.SEND
return true
## Отправляет команду "читать регистры хранения".[br]
## [param addr] - Начальный адрес регистров.[br]
## [param count] - Количество регистров от начального адреса.[br]
func read_holdind(addr: int, count: int = 1) -> bool:
if cmd_state != CmdState.DONE: return false
var json_dict = {'code': 'read_holding', 'data': [addr, count]}
tx_data = JSON.stringify(json_dict).to_utf8_buffer()
cmd_state = CmdState.SEND
return true
## Отправляет команду "читать регистры хранения".[br]
## [param addr] - Начальный адрес регистров.[br]
## [param count] - Количество регистров от начального адреса.[br]
func read_holdind(addr: int, count: int = 1) -> bool:
if cmd_state != CmdState.DONE: return false
var json_dict = {'code': 'read_holding', 'data': [addr, count]}
tx_data = JSON.stringify(json_dict).to_utf8_buffer()
cmd_state = CmdState.SEND
return true
## Отправляет команду "читать регистры входов".[br]
## [param addr] - Начальный адрес регистров.[br]
## [param count] - Количество регистров от начального адреса.[br]
func read_input(addr: int, count: int = 1) -> bool:
if cmd_state != CmdState.DONE: return false
var json_dict = {'code': 'read_input', 'data': [addr, count]}
tx_data = JSON.stringify(json_dict).to_utf8_buffer()
cmd_state = CmdState.SEND
return true
## Отправляет команду "читать регистры входов".[br]
## [param addr] - Начальный адрес регистров.[br]
## [param count] - Количество регистров от начального адреса.[br]
func read_input(addr: int, count: int = 1) -> bool:
if cmd_state != CmdState.DONE: return false
var json_dict = {'code': 'read_input', 'data': [addr, count]}
tx_data = JSON.stringify(json_dict).to_utf8_buffer()
cmd_state = CmdState.SEND
return true

View File

@@ -69,8 +69,8 @@ func _ready() -> void:
sock_capsrpb = poll_sockets[2]
for unit_name in repsettings.UnitProfiles:
var setting_ = repsettings.UnitProfiles[unit_name]
var proto = setting_[0]
var settings = repsettings.UnitProfiles[unit_name]
var proto = settings[0]
if proto in PROTOCOL_TYPES:
var unit = PROTOCOL_TYPES[proto].new(unit_name)
var unit_key = repsettings.get_unit_key(unit_name)

View File

@@ -3,40 +3,40 @@ class_name rep_settings extends Node
## Параметры.
@export var UnitProfiles: = { \
# Название | Протокол | Адрес | Порт | Широковещательное | Привязывать к адресу | Комментарий
'уарэп-яу07-частный': ['yau07rx', '10.1.1.2', 50002, false, true, 'IP-адрес на котором принимают частные сообщения от ЯУ-07'],
'уарэп-яу07-общий': ['yau07rx', '10.1.1.255', 50000, true, true, 'IP-адрес на котором принимают широковещательные сообщения от ЯУ-07'],
'уарэп-капсрпб-пол': ['jsonrx', '127.0.0.1', 42023, false, true, 'IP-адрес на котором принимают сообщения от КАПС РПБ'],
'уарэп-капсрпб-отп': ['jsontx', '127.0.0.1', 42022, false, false, 'IP-адрес на который отправляют сообщения для КАПС РПБ'],
'уарэп-эмс': ['yau07tx', '10.1.1.27', 50027, false, false, 'IP-адрес ЯУ-07 прибора УФ'],
'уарэп-яу07-2в': ['yau07tx', '10.1.1.34', 50034, false, false, 'IP-адрес ЯУ-07 прибора ПРД-В'],
'уарэп-яу07-2н': ['yau07tx', '10.1.1.33', 50033, false, false, 'IP-адрес ЯУ-07 прибора ПРД-Н'],
'уарэп-яу07-2к': ['yau07tx', '10.1.1.35', 50035, false, false, 'IP-адрес ЯУ-07 прибора ПРД-К'],
'уарэп-яу07-4в': ['yau07tx', '10.1.1.66', 50066, false, false, 'IP-адрес ЯУ-07 прибора ПРД-В'],
'уарэп-яу07-4н': ['yau07tx', '10.1.1.65', 50065, false, false, 'IP-адрес ЯУ-07 прибора ПРД-Н'],
'уарэп-яу07-4к': ['yau07tx', '10.1.1.67', 50067, false, false, 'IP-адрес ЯУ-07 прибора ПРД-К'],
'уарэп-яу07-1в': ['yau07tx', '10.1.1.18', 50018, false, false, 'IP-адрес ЯУ-07 прибора ПРД-В'],
'уарэп-яу07-1н': ['yau07tx', '10.1.1.17', 50017, false, false, 'IP-адрес ЯУ-07 прибора ПРД-Н'],
'уарэп-яу07-1к': ['yau07tx', '10.1.1.19', 50019, false, false, 'IP-адрес ЯУ-07 прибора ПРД-К'],
'уарэп-яу07-3в': ['yau07tx', '10.1.1.50', 50050, false, false, 'IP-адрес ЯУ-07 прибора ПРД-В'],
'уарэп-яу07-3н': ['yau07tx', '10.1.1.49', 50049, false, false, 'IP-адрес ЯУ-07 прибора ПРД-Н'],
'уарэп-яу07-3к': ['yau07tx', '10.1.1.51', 50051, false, false, 'IP-адрес ЯУ-07 прибора ПРД-Н'],
# Название | Протокол | Адрес | Порт | Широковещательное | Привязывать к адресу | Комментарий
'уарэп-яу07-частный': ['yau07rx', '10.1.1.2', 50002, false, true, 'IP-адрес на котором принимают частные сообщения от ЯУ-07'],
'уарэп-яу07-общий': ['yau07rx', '10.1.1.255', 50000, true, true, 'IP-адрес на котором принимают широковещательные сообщения от ЯУ-07'],
'уарэп-капсрпб-пол': ['jsonrx', '127.0.0.1', 42023, false, true, 'IP-адрес на котором принимают сообщения от КАПС РПБ'],
'уарэп-капсрпб-отп': ['jsontx', '127.0.0.1', 42022, false, false, 'IP-адрес на который отправляют сообщения для КАПС РПБ'],
'уарэп-эмс': ['yau07tx', '10.1.1.27', 50027, false, false, 'IP-адрес ЯУ-07 прибора УФ'],
'уарэп-яу07-2в': ['yau07tx', '10.1.1.34', 50034, false, false, 'IP-адрес ЯУ-07 прибора ПРД-В'],
'уарэп-яу07-2н': ['yau07tx', '10.1.1.33', 50033, false, false, 'IP-адрес ЯУ-07 прибора ПРД-Н'],
'уарэп-яу07-2к': ['yau07tx', '10.1.1.35', 50035, false, false, 'IP-адрес ЯУ-07 прибора ПРД-К'],
'уарэп-яу07-4в': ['yau07tx', '10.1.1.66', 50066, false, false, 'IP-адрес ЯУ-07 прибора ПРД-В'],
'уарэп-яу07-4н': ['yau07tx', '10.1.1.65', 50065, false, false, 'IP-адрес ЯУ-07 прибора ПРД-Н'],
'уарэп-яу07-4к': ['yau07tx', '10.1.1.67', 50067, false, false, 'IP-адрес ЯУ-07 прибора ПРД-К'],
'уарэп-яу07-1в': ['yau07tx', '10.1.1.18', 50018, false, false, 'IP-адрес ЯУ-07 прибора ПРД-В'],
'уарэп-яу07-1н': ['yau07tx', '10.1.1.17', 50017, false, false, 'IP-адрес ЯУ-07 прибора ПРД-Н'],
'уарэп-яу07-1к': ['yau07tx', '10.1.1.19', 50019, false, false, 'IP-адрес ЯУ-07 прибора ПРД-К'],
'уарэп-яу07-3в': ['yau07tx', '10.1.1.50', 50050, false, false, 'IP-адрес ЯУ-07 прибора ПРД-В'],
'уарэп-яу07-3н': ['yau07tx', '10.1.1.49', 50049, false, false, 'IP-адрес ЯУ-07 прибора ПРД-Н'],
'уарэп-яу07-3к': ['yau07tx', '10.1.1.51', 50051, false, false, 'IP-адрес ЯУ-07 прибора ПРД-Н'],
# Название | Протокол | Адрес | Порт получения | Комментарий
'уарэп-5п28': ['5p28', '127.0.0.1', 50000, 'IP-адрес комплекса 5П-28'],
# Название | Протокол | Адрес | Порт получения | Комментарий
'уарэп-5п28': ['5p28', '127.0.0.1', 50000, 'IP-адрес комплекса 5П-28'],
# Название | Протокол | Адрес | Порт получения | Порт отправки | Комментарий
'уарэп-щ3': ['udp-json-mbcs', 'localhost', 48000, 44100, 'IP-адрес и порты через которые производят обмен сообщения с драйвером клиента Modbus Serial'],
# Название | Протокол | Адрес | Порт получения | Порт отправки | Комментарий
'уарэп-щ3': ['udp-json-mbcs', 'localhost', 48000, 44100, 'IP-адрес и порты через которые производят обмен сообщения с драйвером клиента Modbus Serial'],
# Название | Протокол | Имя порта | Скорость, бод | Чётность | Комментарий
'уарэп-спт25-1': ['uart', '/dev/tty2', 115200, false, 'Адрес прибора СПТ-25 (G1)'],
'уарэп-спт25-2': ['uart', '/dev/tty3', 115200, false, 'Адрес прибора СПТ-25 (G2)'],
# Название | Протокол | Имя порта | Скорость, бод | Чётность | Комментарий
'уарэп-спт25-1': ['uart', '/dev/tty2', 115200, false, 'Адрес прибора СПТ-25 (G1)'],
'уарэп-спт25-2': ['uart', '/dev/tty3', 115200, false, 'Адрес прибора СПТ-25 (G2)'],
}
## Параметры цели.
@export var ThreatParams = {
'time_lost': 5000 ## Время после последнего обновления в мс, через которое цель считается потерянной.
}
'time_lost': 5000 ## Время после последнего обновления в мс, через которое цель считается потерянной.
}
func _ready() -> void: log.info('Модуль настроек готов')
@@ -44,6 +44,6 @@ func _ready() -> void: log.info('Модуль настроек готов')
## Возвращает ключ устройства.
func get_unit_key(unit_name: String) -> Array:
assert(UnitProfiles.has(unit_name), 'ошибка: нет такого устройства \"%s\"' % unit_name)
var item = UnitProfiles[unit_name]
return [item[1], item[2]]
assert(UnitProfiles.has(unit_name), 'ошибка: нет такого устройства \"%s\"' % unit_name)
var item = UnitProfiles[unit_name]
return [item[1], item[2]]

View File

@@ -119,16 +119,6 @@ signal interfer_selected (ecm: interfer.Interfer)
signal fs_selected(ecm: interfer.Interfer)
## Вызывается при запросе изменения уровня детализации карты.[br]
## [param zoom] - Вновь установленный уровень детализации.
signal zoom_changed(zoom: int)
## Вызывается при завершении изменения уровня детализации карты.[br]
## [param zoom] - Вновь установленный уровень детализации.
signal zoom_complete(zoom: int)
## Вызывается при изменении координат карты.[br]
## [param lan], [param lat] - Вновь установленные координаты.
signal map_coords_changed(lan: float, lat: float)

View File

@@ -5,36 +5,36 @@ extends Node
## Цель
class Threat:
var alt: float ## Текущая высота, метры.
var aoa: float ## Курсовой угол на источник сигнала, градусы.
var atick: float ## Тик обнаружения.
var baud: float ## Модуляционная скорость.
var fflags: Dictionary ## Флаги достоверности полей.
var freq: float ## Частота несущей или центральная частота, МГц.
var freqm: String ## Режим смены частоты.
var id: int ## Идентификатор цели.
var lat: float ## Текущие координаты, широта в градусах.
var lon: float ## Текущие координаты, долгота в градусах.
var power: float ## Уровень принимаемого сигнала, дБм.
var proto: String ## Тип протокола источника сигнала.
var rkrp: String ## Рекомендуемый режим помехи.
var rparams: String ## Рекомендуемые параметры помехи.
var salt: float ## Координаты старта, высота от уровня моря, м.
var slat: float ## Координаты старта, широта в градусах.
var slon: float ## Координаты старта, долгота в градусах.
var tick: int ## Тик время приёма сигнала, мс.
var tick_rx: int ## Тик время обновления цели, мс.
var tmod: String ## Тип модуляции.
var width: float ## Ширина занимаемого диапазона частот, МГц.
var selected: bool ## Флаг, что цель выбрана.
var alt: float ## Текущая высота, метры.
var aoa: float ## Курсовой угол на источник сигнала, градусы.
var atick: float ## Тик обнаружения.
var baud: float ## Модуляционная скорость.
var fflags: Dictionary ## Флаги достоверности полей.
var freq: float ## Частота несущей или центральная частота, МГц.
var freqm: String ## Режим смены частоты.
var id: int ## Идентификатор цели.
var lat: float ## Текущие координаты, широта в градусах.
var lon: float ## Текущие координаты, долгота в градусах.
var power: float ## Уровень принимаемого сигнала, дБм.
var proto: String ## Тип протокола источника сигнала.
var rkrp: String ## Рекомендуемый режим помехи.
var rparams: String ## Рекомендуемые параметры помехи.
var salt: float ## Координаты старта, высота от уровня моря, м.
var slat: float ## Координаты старта, широта в градусах.
var slon: float ## Координаты старта, долгота в градусах.
var tick: int ## Тик время приёма сигнала, мс.
var tick_rx: int ## Тик время обновления цели, мс.
var tmod: String ## Тип модуляции.
var width: float ## Ширина занимаемого диапазона частот, МГц.
var selected: bool ## Флаг, что цель выбрана.
func _init(): clear_fflags()
func _to_string() -> String: return 'номер: %d пеленг: %0.1f частота: %0.1f МГц' % [id, aoa, freq]
func clear_fflags(): tools.get_members_names(self).any(func(vn): fflags[vn] = false)
func _init(): clear_fflags()
func _to_string() -> String: return 'номер: %d пеленг: %0.1f частота: %0.1f МГц' % [id, aoa, freq]
func clear_fflags(): tools.get_members_names(self).any(func(vn): fflags[vn] = false)
func set_from_dic(dic: Dictionary):
var items: = fflags.keys().filter(func(vn): return dic.has(vn))
items.any(func(vn): set(vn, dic[vn]); fflags[vn] = true; return false)
func set_from_dic(dic: Dictionary):
var items: = fflags.keys().filter(func(vn): return dic.has(vn))
items.any(func(vn): set(vn, dic[vn]); fflags[vn] = true; return false)
signal threat_new(th: Threat)
@@ -44,34 +44,34 @@ signal threats_resized(th: Dictionary)
func _ready() -> void:
log.info('Модуль обработки целей готов')
var threats = {} ## Список целей. Индекс - <номер цели>
var unit_key = repsettings.get_unit_key('уарэп-капсрпб-отп')
var unit = repnetwork.units[unit_key]
var unit_5p28_key = repsettings.get_unit_key('уарэп-5п28')
var unit_5p28 = repnetwork.units_tcp[unit_5p28_key]
var timer_check_lost: = Timer.new()
add_child(timer_check_lost)
unit.connect('data_received', Callable(self, 'on_data_capsrpb_received').bind(threats))
unit_5p28.connect('get_threats', Callable(self, 'on_get_threats').bind(unit_5p28, threats))
timer_check_lost.connect('timeout', Callable(self, 'on_timer_check_lost').bind(repsettings.ThreatParams.time_lost, threats))
timer_check_lost.start(repsettings.ThreatParams.time_lost / 1000.0)
signaller.connect('rto_threat_sel', Callable(self, 'on_rto_threat_sel').bind(threats))
signaller.connect('rto_threat_unsel', Callable(self, 'on_rto_threat_unsel').bind(threats))
signaller.connect('rto_threat_unsel_all', Callable(self, 'on_rto_threat_unsel_all').bind(threats))
signaller.connect('threat_selected', Callable(self, 'on_threat_selected').bind(threats))
signaller.connect('interfer_init', Callable(self, 'on_interfer_init').bind(threats))
log.info('Модуль обработки целей готов')
var threats = {} ## Список целей. Индекс - <номер цели>
var unit_key = repsettings.get_unit_key('уарэп-капсрпб-отп')
var unit = repnetwork.units[unit_key]
var unit_5p28_key = repsettings.get_unit_key('уарэп-5п28')
var unit_5p28 = repnetwork.units_tcp[unit_5p28_key]
var timer_check_lost: = Timer.new()
add_child(timer_check_lost)
unit.connect('data_received', Callable(self, 'on_data_capsrpb_received').bind(threats))
unit_5p28.connect('get_threats', Callable(self, 'on_get_threats').bind(unit_5p28, threats))
timer_check_lost.connect('timeout', Callable(self, 'on_timer_check_lost').bind(repsettings.ThreatParams.time_lost, threats))
timer_check_lost.start(repsettings.ThreatParams.time_lost / 1000.0)
signaller.connect('rto_threat_sel', Callable(self, 'on_rto_threat_sel').bind(threats))
signaller.connect('rto_threat_unsel', Callable(self, 'on_rto_threat_unsel').bind(threats))
signaller.connect('rto_threat_unsel_all', Callable(self, 'on_rto_threat_unsel_all').bind(threats))
signaller.connect('threat_selected', Callable(self, 'on_threat_selected').bind(threats))
signaller.connect('interfer_init', Callable(self, 'on_interfer_init').bind(threats))
## Периодически удаляет устаревшие цели.
func on_timer_check_lost(time_lost: int, threats: Dictionary):
var tick = Time.get_ticks_msec()
var sz: = threats.size()
for th in threats.values():
if (tick - th.tick_rx) >= time_lost:
threats.erase(th.id)
call_deferred('emit_signal', 'threat_lost', th)
if sz != threats.size():
call_deferred('emit_signal', 'threats_resized', threats)
var tick = Time.get_ticks_msec()
var sz: = threats.size()
for th in threats.values():
if (tick - th.tick_rx) >= time_lost:
threats.erase(th.id)
call_deferred('emit_signal', 'threat_lost', th)
if sz != threats.size():
call_deferred('emit_signal', 'threats_resized', threats)
## отправляет словарь целей в юнит, для запаковки и отправки в 5П28.
@@ -84,8 +84,8 @@ func on_rto_threat_unsel(th_id, threats): threats[th_id].selected = false
## Снимает выделение со всех целей.
func on_rto_threat_unsel_all(threats):
for th in threats.values():
signaller.emit_signal('rto_threat_unsel', th.id)
for th in threats.values():
signaller.emit_signal('rto_threat_unsel', th.id)
## Выбирает цель, если нажать по отметке цели.
@@ -94,44 +94,44 @@ func on_rto_threat_sel(th_id, threats): on_threat_selected(threats[th_id], threa
## Выбирает цель, если выбрать в таблице.
func on_threat_selected(th, threats):
for threat in threats.values():
threat.selected = false
th.selected = true
for threat in threats.values():
threat.selected = false
th.selected = true
## Формирует список выбранных целей для включения помехи.
func on_interfer_init(btn, fs_arr, threats):
var sel_threats: Array = []
for threat in threats.values():
if threat.selected:
sel_threats.append(threat)
if len(sel_threats) or (len(fs_arr)):
signaller.emit_signal('interfer_create', btn, sel_threats, fs_arr)
var sel_threats: Array = []
for threat in threats.values():
if threat.selected:
sel_threats.append(threat)
if len(sel_threats) or (len(fs_arr)):
signaller.emit_signal('interfer_create', btn, sel_threats, fs_arr)
## Производит приём целей.
func on_data_capsrpb_received(unit: capsrpb.CapsRpb, threats: Dictionary):
if not unit.json_dic.has('ts'): return
if unit.json_dic['ts'] != 5: return
if not unit.json_dic.has('mis'): return
if not unit.json_dic.has('ts'): return
if unit.json_dic['ts'] != 5: return
if not unit.json_dic.has('mis'): return
var mis = unit.json_dic['mis']
var sz: = threats.size()
for dic_th in mis:
if dic_th is Dictionary:
if dic_th.has('id'):
var id = int(dic_th['id'])
if threats.has(id):
var th = threats[id]
th.clear_fflags()
th.set_from_dic(dic_th)
th.tick_rx = unit.tick
call_deferred('emit_signal', 'threat_update', th)
else:
var th = Threat.new()
th.set_from_dic(dic_th)
threats[id] = th
th.tick_rx = unit.tick
call_deferred('emit_signal', 'threat_new', th)
if sz != threats.size():
call_deferred('emit_signal', 'threats_resized', threats)
var mis = unit.json_dic['mis']
var sz: = threats.size()
for dic_th in mis:
if dic_th is Dictionary:
if dic_th.has('id'):
var id = int(dic_th['id'])
if threats.has(id):
var th = threats[id]
th.clear_fflags()
th.set_from_dic(dic_th)
th.tick_rx = unit.tick
call_deferred('emit_signal', 'threat_update', th)
else:
var th = Threat.new()
th.set_from_dic(dic_th)
threats[id] = th
th.tick_rx = unit.tick
call_deferred('emit_signal', 'threat_new', th)
if sz != threats.size():
call_deferred('emit_signal', 'threats_resized', threats)

View File

@@ -30,14 +30,14 @@ func is_overlap(x1, x2, y1, y2) -> bool: return max(x1, y1) <= min(x2, y2)
func _check_index(obj, index) -> bool:
if obj is Dictionary:
if obj.has(index):
return true
elif obj is Array:
if index is int:
if (index >= 0) and (index < obj.size()):
return true
return false
if obj is Dictionary:
if obj.has(index):
return true
elif obj is Array:
if index is int:
if (index >= 0) and (index < obj.size()):
return true
return false
## Возвращает значение поля хранящегося по указанному пути внутри объекта.[br]
@@ -48,15 +48,15 @@ func _check_index(obj, index) -> bool:
## [param ret_val[1]] - устанвлено в [b]true[/b], если поле найдено, и установлено в [b]false[/b],
## если искомое поле не найдено.
func get_value_by_path(obj, paths: Array, ret_val: Array):
var index = paths.pop_at(0)
ret_val[1] = false
if paths.size():
if _check_index(obj, index):
get_value_by_path(obj[index], paths, ret_val)
else:
if _check_index(obj, index):
ret_val[0] = obj[index]
ret_val[1] = true
var index = paths.pop_at(0)
ret_val[1] = false
if paths.size():
if _check_index(obj, index):
get_value_by_path(obj[index], paths, ret_val)
else:
if _check_index(obj, index):
ret_val[0] = obj[index]
ret_val[1] = true
## Возвращает часть от входной строки ограниченную индексами символов.[br]
@@ -65,75 +65,75 @@ func get_value_by_path(obj, paths: Array, ret_val: Array):
## [param i0] - Индекс начала части строки.[br]
## [param i1] - Индекс конца части строки. Возвращает все символы до этого индекса.
func get_string_slice(s_in: String, s_out: Array, i0: int, i1: int):
for i in range(i0, i1):
if s_in[i] != '\'':
s_out[0] += s_in[i]
for i in range(i0, i1):
if s_in[i] != '\'':
s_out[0] += s_in[i]
## Производит преобразование из декартовой в полярную систему координат.
func pos_calc(freq: float, aoa: float, pos0: Vector2, r0: float, r1: float, f0: float, f1: float):
var r = ((freq - f0) / (f1 - f0)) * r1 + r0
var aoa_rad = radians(aoa) - PI / 2.0
return Vector2(cos(aoa_rad), sin(aoa_rad)) * r + pos0
var r = ((freq - f0) / (f1 - f0)) * r1 + r0
var aoa_rad = radians(aoa) - PI / 2.0
return Vector2(cos(aoa_rad), sin(aoa_rad)) * r + pos0
## Отсекает значение до заданного диапазона.
func clip_value(val, val_min, val_max):
if val < val_min: val = val_min
if val > val_max: val = val_max
return val
if val < val_min: val = val_min
if val > val_max: val = val_max
return val
## Возвращает массив с именами членов [param obj].
func get_members_names(obj: Object) -> Array:
var pl = obj.get_property_list()
var names: = Array()
for item in pl: names.append(item.name)
return names
var pl = obj.get_property_list()
var names: = Array()
for item in pl: names.append(item.name)
return names
## Проверяет, что заданный массив [param val] похож на ip-адрес.
func is_array_ipaddr(val: Array):
return (val.size() == 2) \
and (val[0] is String) \
and (val[1] is int) \
and (0 < val[1]) \
and (val[1] < 65536)
return (val.size() == 2) \
and (val[0] is String) \
and (val[1] is int) \
and (0 < val[1]) \
and (val[1] < 65536)
## Преобразует декартовые координаты в полярные.[br]
## [param pos0] - координаты центра.[br]
## [param pos] - координаты точки.[br]
func map_dec_to_rad(pos0: Vector2, pos: Vector2) -> Vector2:
var x1: float = pos.x - pos0.x
var y1: float = pos.y - pos0.y
var ang: float = (atan2(y1, x1) * 180.0) / PI
return Vector2(ang + 360.0 * float(ang < 0.0), hypot(x1, y1))
var x1: float = pos.x - pos0.x
var y1: float = pos.y - pos0.y
var ang: float = (atan2(y1, x1) * 180.0) / PI
return Vector2(ang + 360.0 * float(ang < 0.0), hypot(x1, y1))
## Переводит значение val из диапазона a0...a1 в диапазон b0...b1.
func map_range(val: float, a0: float, a1: float, b0: float, b1: float) -> float:
return (((val - a0) * (b1 - b0)) / (a1 - a0)) + b0
return (((val - a0) * (b1 - b0)) / (a1 - a0)) + b0
## Возвращает [b]true[/b], если точка внутри заданного полигона.[br]
## [param pos] - Проверяемая точка.[br]
## [param p] - Координаты вершин полигона.[br]
func is_point_in_polygon(pos: Vector2, p: PackedVector2Array) -> bool:
var sz: = p.size()
assert(sz)
var j: int = sz - 1
var odd_nodes: int = 0
for i in sz:
if ((p[i].y < pos.y and p[j].y >= pos.y or p[j].y < pos.y and p[i].y >= pos.y) and (p[i].x <= pos.x or p[j].x <= pos.x)):
odd_nodes ^= int((p[i].x + (pos.y - p[i].y) / (p[j].y - p[i].y) * (p[j].x - p[i].x) < pos.x))
j = i
return odd_nodes > 0
var sz: = p.size()
assert(sz)
var j: int = sz - 1
var odd_nodes: int = 0
for i in sz:
if ((p[i].y < pos.y and p[j].y >= pos.y or p[j].y < pos.y and p[i].y >= pos.y) and (p[i].x <= pos.x or p[j].x <= pos.x)):
odd_nodes ^= int((p[i].x + (pos.y - p[i].y) / (p[j].y - p[i].y) * (p[j].x - p[i].x) < pos.x))
j = i
return odd_nodes > 0
## Записывает слово [param val] в элемент байтовго массива [param buf].
## расположенный по индексу [param index] (младшее слово).[br]
## [i]Следует использовать встроенные в[/i] [b]PackedByteArray[/b] [i]методы вместо этой функции.[/i]
func set_wordi(buf: PackedByteArray, index: int, val: int) -> void:
buf[index] = val & 0xff
buf[index + 1] = (val >> 8) & 0xff
buf[index] = val & 0xff
buf[index + 1] = (val >> 8) & 0xff

View File

@@ -11,239 +11,239 @@ const BUFFER_SIZE = 2048 ## Размер буфера для приёма д
## Ячейка ЯУ-07.
class YaU07:
## Коды выполняемых команд.
enum CmdCode {
READ_ISA = 0, ## Код команды "читать порт ISA" ячейки ЯУ-07.
WRITE_ISA = 1, ## Код команды "записать порт ISA" ячейки ЯУ-07.
WRITE_BLOCK = 60, ## Код команды "записать блок данных ISA" внешней шины ячейки ЯУ-07.
READ_BLOCK = 61, ## Код команды "читать блок данных ISA" внешней шины ячейки ЯУ-07.
}
## Коды выполняемых команд.
enum CmdCode {
READ_ISA = 0, ## Код команды "читать порт ISA" ячейки ЯУ-07.
WRITE_ISA = 1, ## Код команды "записать порт ISA" ячейки ЯУ-07.
WRITE_BLOCK = 60, ## Код команды "записать блок данных ISA" внешней шины ячейки ЯУ-07.
READ_BLOCK = 61, ## Код команды "читать блок данных ISA" внешней шины ячейки ЯУ-07.
}
## Состояние автомата выполнения команды.
enum CmdState {
UNCK, ## Номер команды не ининциирован.
SEND, ## Буфер готов для отправки.
WAIT, ## Ожидание выполнения.
DONE, ## Команда выполнена.
FAIL ## Команда не выполнена.
}
## Состояние автомата выполнения команды.
enum CmdState {
UNCK, ## Номер команды не ининциирован.
SEND, ## Буфер готов для отправки.
WAIT, ## Ожидание выполнения.
DONE, ## Команда выполнена.
FAIL ## Команда не выполнена.
}
signal line_changed(unit: YaU07) ## Вызывается когда меняется состояние связи с ячейкой.
signal command_done(unit: YaU07) ## Вызывается после выполнения команды.
signal command_fail(unit: YaU07) ## Вызывается если возникла ошибка при выполнении команды и команда не выполнена.
signal parse_failed(unit: YaU07)
signal line_changed(unit: YaU07) ## Вызывается когда меняется состояние связи с ячейкой.
signal command_done(unit: YaU07) ## Вызывается после выполнения команды.
signal command_fail(unit: YaU07) ## Вызывается если возникла ошибка при выполнении команды и команда не выполнена.
signal parse_failed(unit: YaU07)
var name: = String()
var cmd_num_rx: = 0 ## Номер команды принятый.
var cmd_num_tx: = 0 ## Номер команды переданный.
var cmd_state = CmdState.UNCK ## Состояние выполнения команды.
var cmd_result: = 1 ## Результат выполнения команды. Устанавливается в ЯУ-07.
var cmd_data: = PackedByteArray() ## Данные команды. Устанавливается в ЯУ-07.
var cmd_retry = 0 ## Счётчик оставшихся попыток выполнить команду.
var tick = 0.0 ## Текущее тик-время в мс.
var cmd_tick_tx = 0.0 ## Время последней отправки команды.
var cmd_tick_rx = 0.0 ## Время последнего приёма команды.
var status: = PackedByteArray() ## Поле "состояние прибора".
var tx_data: = PackedByteArray() ## Буфер отправленный.
var tx_len = 0 ## Длина последнего отправленного пакета.
var isa_ports = {} ## Результат выполнения команды "читать порт".
var isa_block = {} ## Результат выполнения команды "читать массив из порта".
var online = false ## Состояние связи с ячейкой.
var name: = String()
var cmd_num_rx: = 0 ## Номер команды принятый.
var cmd_num_tx: = 0 ## Номер команды переданный.
var cmd_state = CmdState.UNCK ## Состояние выполнения команды.
var cmd_result: = 1 ## Результат выполнения команды. Устанавливается в ЯУ-07.
var cmd_data: = PackedByteArray() ## Данные команды. Устанавливается в ЯУ-07.
var cmd_retry = 0 ## Счётчик оставшихся попыток выполнить команду.
var tick = 0.0 ## Текущее тик-время в мс.
var cmd_tick_tx = 0.0 ## Время последней отправки команды.
var cmd_tick_rx = 0.0 ## Время последнего приёма команды.
var status: = PackedByteArray() ## Поле "состояние прибора".
var tx_data: = PackedByteArray() ## Буфер отправленный.
var tx_len = 0 ## Длина последнего отправленного пакета.
var isa_ports = {} ## Результат выполнения команды "читать порт".
var isa_block = {} ## Результат выполнения команды "читать массив из порта".
var online = false ## Состояние связи с ячейкой.
func _to_string() -> String: return String('яу07: %s %s' % [self.name, ['отключен', 'на связи'][int(online)]])
func _to_string() -> String: return String('яу07: %s %s' % [self.name, ['отключен', 'на связи'][int(online)]])
func _init(nm):
self.name = nm
tx_data.resize(BUFFER_SIZE)
tx_data.fill(0)
tick = 0.0
func _init(nm):
self.name = nm
tx_data.resize(BUFFER_SIZE)
tx_data.fill(0)
tick = 0.0
func process(delta: float):
tick += delta
if online and ((tick - cmd_tick_rx) > ONLINE_TIMEOUT):
online = false
emit_signal('line_changed', self)
func process(delta: float):
tick += delta
if online and ((tick - cmd_tick_rx) > ONLINE_TIMEOUT):
online = false
emit_signal('line_changed', self)
var rc = Error.ERR_BUSY
if cmd_state == CmdState.SEND:
rc = Error.OK
cmd_tick_tx = tick
cmd_state = CmdState.WAIT
cmd_retry = RETRY_COUNT
elif cmd_state == CmdState.WAIT:
if cmd_retry:
if (tick - cmd_tick_tx) > RESEND_TIMEOUT:
rc = Error.OK
cmd_tick_tx = tick
cmd_retry -= 1
else:
cmd_state = CmdState.FAIL
emit_signal('command_fail', self)
rc = Error.FAILED
return rc
var rc = Error.ERR_BUSY
if cmd_state == CmdState.SEND:
rc = Error.OK
cmd_tick_tx = tick
cmd_state = CmdState.WAIT
cmd_retry = RETRY_COUNT
elif cmd_state == CmdState.WAIT:
if cmd_retry:
if (tick - cmd_tick_tx) > RESEND_TIMEOUT:
rc = Error.OK
cmd_tick_tx = tick
cmd_retry -= 1
else:
cmd_state = CmdState.FAIL
emit_signal('command_fail', self)
rc = Error.FAILED
return rc
func send_isa(cmd_code: int, ports_data: Array) -> bool:
if (cmd_state == CmdState.WAIT) \
or (cmd_state == CmdState.SEND) \
or (cmd_state == CmdState.UNCK):
return false
if not ((cmd_code == CmdCode.READ_ISA) or (cmd_code == CmdCode.WRITE_ISA)):
return false
func send_isa(cmd_code: int, ports_data: Array) -> bool:
if (cmd_state == CmdState.WAIT) \
or (cmd_state == CmdState.SEND) \
or (cmd_state == CmdState.UNCK):
return false
if not ((cmd_code == CmdCode.READ_ISA) or (cmd_code == CmdCode.WRITE_ISA)):
return false
cmd_num_tx = (cmd_num_rx + 1) % 0xffff
cmd_num_tx = (cmd_num_rx + 1) % 0xffff
var ports_count = ports_data.size()
tx_len = ports_count * 2 + 8
tx_data.encode_u16(0, cmd_retry) # Исправление для сетевого стека "nm-callback" процессоров STM
tx_data.encode_u16(2, cmd_num_tx)
var ports_count = ports_data.size()
tx_len = ports_count * 2 + 8
tx_data.encode_u16(0, cmd_retry) # Исправление для сетевого стека "nm-callback" процессоров STM
tx_data.encode_u16(2, cmd_num_tx)
if cmd_code == CmdCode.WRITE_ISA:
ports_count /= 2
tx_data[6] = cmd_code
tx_data[7] = ports_count
if cmd_code == CmdCode.WRITE_ISA:
ports_count /= 2
tx_data[6] = cmd_code
tx_data[7] = ports_count
var i: = 0
for word in ports_data:
tx_data.encode_u16(8 + i, word)
i += 2
cmd_state = CmdState.SEND
return true
var i: = 0
for word in ports_data:
tx_data.encode_u16(8 + i, word)
i += 2
cmd_state = CmdState.SEND
return true
func send_block(cmd_code: int, block_len: int, port: Array) -> bool:
if (cmd_state == CmdState.WAIT) \
or (cmd_state == CmdState.SEND) \
or (cmd_state == CmdState.UNCK):
return false
if not ((cmd_code == CmdCode.READ_BLOCK) or (cmd_code == CmdCode.WRITE_BLOCK)):
return false
func send_block(cmd_code: int, block_len: int, port: Array) -> bool:
if (cmd_state == CmdState.WAIT) \
or (cmd_state == CmdState.SEND) \
or (cmd_state == CmdState.UNCK):
return false
if not ((cmd_code == CmdCode.READ_BLOCK) or (cmd_code == CmdCode.WRITE_BLOCK)):
return false
cmd_num_tx = (cmd_num_rx + 1) % 0xffff
cmd_num_tx = (cmd_num_rx + 1) % 0xffff
tx_len = 11
tx_data.encode_u16(0, cmd_retry) # Исправление для сетевого стека "nm-callback" процессоров STM
tx_data.encode_u16(2, cmd_num_tx)
tx_len = 11
tx_data.encode_u16(0, cmd_retry) # Исправление для сетевого стека "nm-callback" процессоров STM
tx_data.encode_u16(2, cmd_num_tx)
if cmd_code == CmdCode.WRITE_BLOCK:
block_len /= 2
tx_data[6] = cmd_code
tx_data.encode_u16(7, block_len)
if cmd_code == CmdCode.WRITE_BLOCK:
block_len /= 2
tx_data[6] = cmd_code
tx_data.encode_u16(7, block_len)
tx_data.encode_u16(9, int(port[0]))
cmd_state = CmdState.SEND
return true
## Возвращает код команды ЯУ-07.
## rx_data - Принятые данные.
## Возвращает - Код команды не найден, 0...255 - код команды.
func get_cmd_code(rx_data: PackedByteArray):
tx_data.encode_u16(9, int(port[0]))
cmd_state = CmdState.SEND
return true
## Возвращает код команды ЯУ-07.
## rx_data - Принятые данные.
## Возвращает - Код команды не найден, 0...255 - код команды.
func get_cmd_code(rx_data: PackedByteArray):
var rx_data_len = rx_data.size()
if rx_data_len < 7:
return null
var status_len = rx_data.decode_u16(6) # Длина массива байтового "состояние прибора"
if rx_data_len <= (status_len + 6): # Нет поля "ответ на команду", разбор пакета завершить
return null
var cmd_code_pos = status_len + 8 # Положение в приёмном буфере "кода команды"
if rx_data_len <= cmd_code_pos:
return null
return rx_data[cmd_code_pos] # "код команды"
var rx_data_len = rx_data.size()
if rx_data_len < 7:
return null
var status_len = rx_data.decode_u16(6) # Длина массива байтового "состояние прибора"
if rx_data_len <= (status_len + 6): # Нет поля "ответ на команду", разбор пакета завершить
return null
var cmd_code_pos = status_len + 8 # Положение в приёмном буфере "кода команды"
if rx_data_len <= cmd_code_pos:
return null
return rx_data[cmd_code_pos] # "код команды"
func parse_isa(rx_data: PackedByteArray, status_len) -> bool:
var ports_count_pos = status_len + 10 # Положение в приёмном буфере количества портов
if rx_data.size() < ports_count_pos:
return false
var ports_count = rx_data[ports_count_pos] # Количество портов
var counted_len = status_len + (ports_count * 4) + 11 # Вычисленная длина пакета
if rx_data.size() < counted_len:
return false
var addr_pos = status_len + 11 # Смещение положения адреса в приёмном буфере
var data_pos = status_len + 13 # Смещение положения данных в приёмном буфере
var i: = 0
while i < ports_count:
var shift: = i * 4
var isa_addr = rx_data.decode_u16(addr_pos + shift) # Адрес порта ISA яу-07
var isa_data = rx_data.decode_u16(data_pos + shift) # Данные порта ISA яу-07
isa_ports[isa_addr] = isa_data
i += 1
cmd_data = rx_data.slice(addr_pos, (addr_pos + ports_count * 4) - 1)
return i > 0
func parse_isa(rx_data: PackedByteArray, status_len) -> bool:
var ports_count_pos = status_len + 10 # Положение в приёмном буфере количества портов
if rx_data.size() < ports_count_pos:
return false
var ports_count = rx_data[ports_count_pos] # Количество портов
var counted_len = status_len + (ports_count * 4) + 11 # Вычисленная длина пакета
if rx_data.size() < counted_len:
return false
var addr_pos = status_len + 11 # Смещение положения адреса в приёмном буфере
var data_pos = status_len + 13 # Смещение положения данных в приёмном буфере
var i: = 0
while i < ports_count:
var shift: = i * 4
var isa_addr = rx_data.decode_u16(addr_pos + shift) # Адрес порта ISA яу-07
var isa_data = rx_data.decode_u16(data_pos + shift) # Данные порта ISA яу-07
isa_ports[isa_addr] = isa_data
i += 1
cmd_data = rx_data.slice(addr_pos, (addr_pos + ports_count * 4) - 1)
return i > 0
func parse_block(rx_data: PackedByteArray, status_len) -> bool:
var ports_count_pos = status_len + 7 # Положение в приёмном буфере количества портов
if rx_data.size() < ports_count_pos:
return false
var block_len = rx_data.decode_u16(ports_count_pos + 3)
var block_adr = rx_data.decode_u16(ports_count_pos + 5)
var block_data = []
for i in block_len:
var shift: int = i * 2
block_data.append(rx_data.decode_u16(ports_count_pos + 7 + shift))
isa_block[block_adr] = block_data
return true
func parse_block(rx_data: PackedByteArray, status_len) -> bool:
var ports_count_pos = status_len + 7 # Положение в приёмном буфере количества портов
if rx_data.size() < ports_count_pos:
return false
var block_len = rx_data.decode_u16(ports_count_pos + 3)
var block_adr = rx_data.decode_u16(ports_count_pos + 5)
var block_data = []
for i in block_len:
var shift: int = i * 2
block_data.append(rx_data.decode_u16(ports_count_pos + 7 + shift))
isa_block[block_adr] = block_data
return true
## Заполняет состояние прибора данными прочитанными из портов ЯУ-07.
## для команд "читать порты" или "записать порты".[br]
## [param rx_data] - Принятые данные.[br]
## Возвращает [b]true[/b] - Данные обработаны успешно.[br]
## Возвращает [b]false[/b] - Данные не обработаны из за ошибки.[br]
func parse(rx_data: PackedByteArray) -> bool:
## Заполняет состояние прибора данными прочитанными из портов ЯУ-07.
## для команд "читать порты" или "записать порты".[br]
## [param rx_data] - Принятые данные.[br]
## Возвращает [b]true[/b] - Данные обработаны успешно.[br]
## Возвращает [b]false[/b] - Данные не обработаны из за ошибки.[br]
func parse(rx_data: PackedByteArray) -> bool:
cmd_tick_rx = tick
cmd_tick_rx = tick
if not online:
online = true
emit_signal('line_changed', self)
if not online:
online = true
emit_signal('line_changed', self)
var rx_data_len = rx_data.size()
if rx_data_len < 10:
return false
var rx_data_len = rx_data.size()
if rx_data_len < 10:
return false
cmd_num_rx = rx_data.decode_u16(2)
if cmd_state == CmdState.UNCK:
cmd_state = CmdState.DONE
cmd_num_rx = rx_data.decode_u16(2)
if cmd_state == CmdState.UNCK:
cmd_state = CmdState.DONE
var status_len = rx_data.decode_u16(6) # Длина массива байтового "состояние прибора"
if rx_data_len < (status_len + 6):
return false
var i: = 0
var len_rx_data: = rx_data.size()
status.resize(status_len)
while i < status_len:
var byte_i = i + 8
if byte_i < len_rx_data:
status[i] = rx_data[byte_i] # Массив байтовый "состояние прибора"
i += 1
var status_len = rx_data.decode_u16(6) # Длина массива байтового "состояние прибора"
if rx_data_len < (status_len + 6):
return false
var i: = 0
var len_rx_data: = rx_data.size()
status.resize(status_len)
while i < status_len:
var byte_i = i + 8
if byte_i < len_rx_data:
status[i] = rx_data[byte_i] # Массив байтовый "состояние прибора"
i += 1
if rx_data_len == (status_len + 6):
return true # Нет поля "ответ на команду", разбор пакета завершить
if rx_data_len == (status_len + 6):
return true # Нет поля "ответ на команду", разбор пакета завершить
var cmd_code_pos = status_len + 8 # Положение в приёмном буфере "код команды"
if rx_data_len <= cmd_code_pos:
return false
var cmd_code_pos = status_len + 8 # Положение в приёмном буфере "код команды"
if rx_data_len <= cmd_code_pos:
return false
var cmd_status_pos = status_len + 9 # Положение в приёмном буфере "состояния выполнения команды"
if rx_data_len <= cmd_status_pos:
return false
cmd_result = rx_data[cmd_status_pos]
if cmd_result != 1: # Команда не выполнена
return false
var cmd_status_pos = status_len + 9 # Положение в приёмном буфере "состояния выполнения команды"
if rx_data_len <= cmd_status_pos:
return false
cmd_result = rx_data[cmd_status_pos]
if cmd_result != 1: # Команда не выполнена
return false
var cmd_code: CmdCode = rx_data[cmd_code_pos] as CmdCode # "код команды"
var rc = true
if (cmd_code == CmdCode.READ_ISA) or (cmd_code == CmdCode.WRITE_ISA):
rc = parse_isa(rx_data, status_len)
var cmd_code: CmdCode = rx_data[cmd_code_pos] as CmdCode # "код команды"
var rc = true
if (cmd_code == CmdCode.READ_ISA) or (cmd_code == CmdCode.WRITE_ISA):
rc = parse_isa(rx_data, status_len)
elif (cmd_code == CmdCode.READ_BLOCK) or (cmd_code == CmdCode.WRITE_BLOCK):
rc = parse_block(rx_data, status_len)
elif (cmd_code == CmdCode.READ_BLOCK) or (cmd_code == CmdCode.WRITE_BLOCK):
rc = parse_block(rx_data, status_len)
if rc:
if (cmd_state == CmdState.WAIT) and (cmd_num_rx == cmd_num_tx):
cmd_state = CmdState.DONE
emit_signal('command_done', self)
else:
emit_signal('parse_failed', self)
return true
if rc:
if (cmd_state == CmdState.WAIT) and (cmd_num_rx == cmd_num_tx):
cmd_state = CmdState.DONE
emit_signal('command_done', self)
else:
emit_signal('parse_failed', self)
return true

View File

@@ -2,36 +2,36 @@ class_name yems_dev extends Node
class InDev:
var id: int ## Идентификатор индекс устройства.
var dev_name: String ## Имя подключаемого комплекса.
var connector: String ## Номер разъёма в приборе.
var delay: float ## Задержка фронта импульса.
var offset: float ## Смещение фронта импульса.
var state: int ## Состояние входа.
var freq: int ## Предполагаемая частота на которой работает комплекс.
var ports_data: Dictionary ## Словарь состояния портов устройства.
var tbl_row_id: int ## Индекс устройства в таблице выходов.
var out_dots: Array ## Список выходных устройств в порядке, как в таблице и точки матрицы.
var connect_devs: Dictionary ## Список присоединённых устройств.
var board: String ## Плата в которой находится устройство.
var id: int ## Идентификатор индекс устройства.
var dev_name: String ## Имя подключаемого комплекса.
var connector: String ## Номер разъёма в приборе.
var delay: float ## Задержка фронта импульса.
var offset: float ## Смещение фронта импульса.
var state: int ## Состояние входа.
var freq: int ## Предполагаемая частота на которой работает комплекс.
var ports_data: Dictionary ## Словарь состояния портов устройства.
var tbl_row_id: int ## Индекс устройства в таблице выходов.
var out_dots: Array ## Список выходных устройств в порядке, как в таблице и точки матрицы.
var connect_devs: Dictionary ## Список присоединённых устройств.
var board: String ## Плата в которой находится устройство.
func _init(dev_id : int, params : Array, row_id : int):
id = dev_id
dev_name = String(params[0])
connector = String(params[1])
delay = float(params[2])
offset = float(params[3])
tbl_row_id = row_id
func _init(dev_id : int, params : Array, row_id : int):
id = dev_id
dev_name = String(params[0])
connector = String(params[1])
delay = float(params[2])
offset = float(params[3])
tbl_row_id = row_id
class OutDev extends InDev:
var mode: String ## Режим работы выхода.
var mode: String ## Режим работы выхода.
func _init(dev_id : int, params : Array, row_id : int):
id = dev_id
dev_name = String(params[0])
connector = String(params[1])
delay = float(params[2])
offset = float(params[3])
tbl_row_id = row_id
mode = 'Работа'
func _init(dev_id : int, params : Array, row_id : int):
id = dev_id
dev_name = String(params[0])
connector = String(params[1])
delay = float(params[2])
offset = float(params[3])
tbl_row_id = row_id
mode = 'Работа'

View File

@@ -3,17 +3,24 @@ shader_type canvas_item;
#include "res://shaders/tools.gdshaderinc"
/* Закрашенная окружность */
uniform vec4 color: source_color = vec4(1, 1, 1, 1);
uniform vec4 color_up: source_color = vec4(1, 1, 1, 1); /* Цвет вспышки при обновлении */
uniform uint tick_update = 0; /* Время последнего обновления, мс */
const float time_delta = 0.1; /* Время вспышки при обновлении, c */
uniform float tick_update = 0.0; /* Время последнего обновления, мс */
const float time_delta = 0.5; /* Время вспышки при обновлении, c */
const float ds = 0.01; /* Относительное расстояние на котором выполняется сглаживание краёв */
const vec2 center = vec2(0.125, 0.125); /* Центр должен быть в (0.5, 0.5), но это не так */
const float radius = 0.075 - ds * 2.0; /* Радиус должен быть 0.5, но это не так */
const float alpha_offset = 0.25; /* Изменние скорости альфа канала */
void fragment()
{
float time_now = TIME;
float alpha = 1.0;
if ((time_now - tick_update) < time_delta)
{
alpha = (time_delta - (time_now - tick_update)) * alpha_offset;
}
else{
alpha = 0.0;
}
vec4 color = vec4(0.972549, 0.12549, 0.411765, 1.0 - alpha);
COLOR = color * smooth_px(length(UV - center), radius, ds);
}