240 lines
9.5 KiB
GDScript
240 lines
9.5 KiB
GDScript
class_name PRDConfigManager
|
||
extends RefCounted
|
||
|
||
## Менеджер конфигурации для системы контроля ПРД.
|
||
## Загружает и управляет настройками из JSON-конфигурации.
|
||
|
||
#region CONSTANTS
|
||
const CONFIG_PATH = 'user://config/setting-prd-control.json'
|
||
const DEFAULT_CONFIG_PATH = 'res://config/setting-prd-control.json'
|
||
const CONFIG_VERSION = '1.0.0' # Версия конфигурации - увеличивать при изменениях
|
||
#endregion
|
||
|
||
#region PRIVATE_VARIABLES
|
||
var _config_data: Dictionary ## Все данные конфигурации
|
||
var _device_constants: Dictionary ## Константы устройств
|
||
var _base_configs: Dictionary ## Базовые конфигурации
|
||
var _device_configs: Dictionary ## Конфигурации устройств
|
||
var _data_indices: Dictionary ## Индексы данных
|
||
var _fs_keys: Dictionary ## Ключи ФС (функциональных систем)
|
||
var _temperature_constants: Dictionary ## Константы температуры
|
||
var _ems_input_bits_config: Dictionary ## Конфигурация битов ввода ЭМС
|
||
var _fs_rules: Array ## Правила ФС
|
||
|
||
var _is_loaded: bool = false ## Флаг успешной загрузки
|
||
var _current_version: String ## Текущая версия загруженной конфигурации
|
||
#endregion
|
||
|
||
#region INITIALIZATION
|
||
func _init() -> void:
|
||
'''Инициализация менеджера конфигурации'''
|
||
_load_configuration()
|
||
#endregion
|
||
|
||
#region PUBLIC_API
|
||
## Проверяет, успешно ли загружена конфигурация
|
||
func is_loaded() -> bool:
|
||
return _is_loaded
|
||
|
||
## Возвращает текущую версию загруженной конфигурации
|
||
func get_config_version() -> String:
|
||
return _current_version
|
||
|
||
## Проверяет, актуальна ли конфигурация
|
||
func is_config_up_to_date() -> bool:
|
||
return _current_version == CONFIG_VERSION
|
||
|
||
## Возвращает все данные конфигурации
|
||
func get_config_data() -> Dictionary:
|
||
return _config_data.duplicate(true)
|
||
|
||
## Возвращает константы устройств
|
||
func get_device_constants() -> Dictionary:
|
||
return _device_constants.duplicate(true)
|
||
|
||
## Возвращает базовые конфигурации
|
||
func get_base_configs() -> Dictionary:
|
||
return _base_configs.duplicate(true)
|
||
|
||
## Возвращает конфигурации устройств
|
||
func get_device_configs() -> Dictionary:
|
||
return _device_configs.duplicate(true)
|
||
|
||
## Возвращает индексы данных
|
||
func get_data_indices() -> Dictionary:
|
||
return _data_indices.duplicate(true)
|
||
|
||
## Возвращает ключи ФС
|
||
func get_fs_keys() -> Dictionary:
|
||
return _fs_keys.duplicate(true)
|
||
|
||
## Возвращает константы температуры
|
||
func get_temperature_constants() -> Dictionary:
|
||
return _temperature_constants.duplicate(true)
|
||
|
||
## Возвращает конфигурацию битов ввода ЭМС
|
||
func get_ems_input_bits_config() -> Dictionary:
|
||
return _ems_input_bits_config.duplicate(true)
|
||
|
||
## Возвращает правила ФС
|
||
func get_fs_rules() -> Array:
|
||
return _fs_rules.duplicate(true)
|
||
|
||
## Возвращает индекс данных по ключу
|
||
func get_data_index(key: String) -> int:
|
||
return _data_indices.get(key, -1)
|
||
#endregion
|
||
|
||
#region PRIVATE_METHODS
|
||
## Загружает конфигурацию из JSON файла
|
||
func _load_configuration() -> void:
|
||
if not _ensure_config_exists():
|
||
_is_loaded = false
|
||
return
|
||
|
||
var config_file = FileAccess.open(CONFIG_PATH, FileAccess.READ)
|
||
if config_file:
|
||
var json_text = config_file.get_as_text()
|
||
config_file.close()
|
||
|
||
var json = JSON.new()
|
||
var parse_result = json.parse(json_text)
|
||
if parse_result == OK:
|
||
_config_data = json.data
|
||
_current_version = _config_data.get('config_version', '0.0.0')
|
||
_parse_config_data()
|
||
_is_loaded = true
|
||
else:
|
||
push_error('Ошибка разбора JSON конфигурации: ' + json.get_error_message())
|
||
_is_loaded = false
|
||
else:
|
||
push_error('Ошибка загрузки файла конфигурации: ' + CONFIG_PATH)
|
||
_is_loaded = false
|
||
|
||
|
||
## Парсит данные конфигурации по секциям
|
||
func _parse_config_data() -> void:
|
||
_device_constants = _convert_numbers(_config_data.get('device_constants', {}))
|
||
_base_configs = _convert_numbers(_config_data.get('base_configs', {}))
|
||
_device_configs = _convert_numbers(_config_data.get('device_configs', {}))
|
||
_data_indices = _convert_numbers(_config_data.get('data_indices', {}))
|
||
_fs_keys = _convert_numbers(_config_data.get('fs_keys', {}))
|
||
_temperature_constants = _convert_numbers(_config_data.get('temperature_constants', {}))
|
||
_ems_input_bits_config = _convert_numbers(_config_data.get('ems_input_bits_config', {}))
|
||
_fs_rules = _convert_numbers(_config_data.get('fs_rules', []))
|
||
|
||
|
||
## Конвертирует числовые значения в правильные типы (float -> int если возможно)
|
||
static func _convert_numbers(value):
|
||
if value is float:
|
||
# Конвертируем float в int если нет дробной части
|
||
if value == int(value):
|
||
return int(value)
|
||
return value
|
||
elif value is Dictionary:
|
||
var result = {}
|
||
for key in value:
|
||
result[key] = _convert_numbers(value[key])
|
||
return result
|
||
elif value is Array:
|
||
var result = []
|
||
for item in value:
|
||
result.append(_convert_numbers(item))
|
||
return result
|
||
else:
|
||
return value
|
||
|
||
|
||
## Обеспечивает существование файла конфигурации
|
||
## Создает папку и копирует дефолтный файл если нужно
|
||
func _ensure_config_exists() -> bool:
|
||
# Создаем папку конфигурации если не существует
|
||
DirAccess.make_dir_recursive_absolute('user://config')
|
||
|
||
# Проверяем, нужно ли обновить конфигурацию
|
||
if _should_update_config():
|
||
return _update_config()
|
||
|
||
# Если файл уже существует - все ок
|
||
if FileAccess.file_exists(CONFIG_PATH):
|
||
return true
|
||
|
||
# Если дефолтного файла нет - ошибка
|
||
if not FileAccess.file_exists(DEFAULT_CONFIG_PATH):
|
||
return false
|
||
|
||
# Копируем дефолтный конфиг в user директорию
|
||
return _copy_default_config()
|
||
|
||
|
||
## Проверяет, нужно ли обновить конфигурацию
|
||
func _should_update_config() -> bool:
|
||
# Если файла конфигурации нет - нужно создать
|
||
if not FileAccess.file_exists(CONFIG_PATH):
|
||
return true
|
||
|
||
# Проверяем версию существующей конфигурации
|
||
var config_file = FileAccess.open(CONFIG_PATH, FileAccess.READ)
|
||
if config_file:
|
||
var json_text = config_file.get_as_text()
|
||
config_file.close()
|
||
|
||
var json = JSON.new()
|
||
if json.parse(json_text) == OK:
|
||
var existing_version = json.data.get('config_version', '0.0.0')
|
||
# Сравниваем версии
|
||
return _compare_versions(existing_version, CONFIG_VERSION) < 0
|
||
else:
|
||
# Если ошибка парсинга - нужно обновить
|
||
return true
|
||
|
||
return false
|
||
|
||
|
||
## Сравнивает две версии (semantic versioning)
|
||
## Возвращает -1 если version1 < version2, 0 если равны, 1 если version1 > version2
|
||
static func _compare_versions(version1: String, version2: String) -> int:
|
||
var v1_parts = version1.split('.')
|
||
var v2_parts = version2.split('.')
|
||
|
||
for i in range(max(v1_parts.size(), v2_parts.size())):
|
||
var v1_part = int(v1_parts[i]) if i < v1_parts.size() else 0
|
||
var v2_part = int(v2_parts[i]) if i < v2_parts.size() else 0
|
||
|
||
if v1_part < v2_part:
|
||
return -1
|
||
elif v1_part > v2_part:
|
||
return 1
|
||
|
||
return 0
|
||
|
||
|
||
## Обновляет конфигурацию до актуальной версии
|
||
func _update_config() -> bool:
|
||
# Создаем резервную копию старой конфигурации
|
||
if FileAccess.file_exists(CONFIG_PATH):
|
||
var backup_path = CONFIG_PATH + '.backup_v' + _current_version
|
||
DirAccess.copy_absolute(CONFIG_PATH, backup_path)
|
||
|
||
# Копируем актуальную конфигурацию
|
||
return _copy_default_config()
|
||
|
||
|
||
## Копирует дефолтную конфигурацию в user директорию
|
||
func _copy_default_config() -> bool:
|
||
if not FileAccess.file_exists(DEFAULT_CONFIG_PATH):
|
||
push_error('Файл конфигурации не найден: ' + DEFAULT_CONFIG_PATH)
|
||
return false
|
||
|
||
var src := FileAccess.open(DEFAULT_CONFIG_PATH, FileAccess.READ)
|
||
var dst := FileAccess.open(CONFIG_PATH, FileAccess.WRITE)
|
||
if src == null or dst == null:
|
||
push_error('Ошибка копирования конфигурации')
|
||
return false
|
||
|
||
dst.store_string(src.get_as_text())
|
||
src.close()
|
||
dst.close()
|
||
return true
|
||
#endregion
|