Initial commit: COPILOT D6 Flutter keyboard controller
Flutter web app replacing legacy WPF CCTV surveillance keyboard controller. Includes wall overview, section view with monitor grid, camera input, PTZ control, alarm/lock/sequence BLoCs, and legacy-matching UI styling. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
179
copilot_keyboard/lib/config/app_config.dart
Normal file
179
copilot_keyboard/lib/config/app_config.dart
Normal file
@@ -0,0 +1,179 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:logger/logger.dart';
|
||||
|
||||
import '../domain/entities/function_button_config.dart';
|
||||
import '../domain/entities/server_config.dart';
|
||||
|
||||
/// Application configuration loaded from servers.json
|
||||
class AppConfig {
|
||||
final List<ServerConfig> servers;
|
||||
final String coordinatorUrl;
|
||||
final String keyboardId;
|
||||
final FunctionButtonConfig functionButtons;
|
||||
final int alarmSyncIntervalSeconds;
|
||||
final int connectionRetrySeconds;
|
||||
final int commandTimeoutSeconds;
|
||||
|
||||
const AppConfig({
|
||||
required this.servers,
|
||||
this.coordinatorUrl = 'http://localhost:8090',
|
||||
this.keyboardId = 'keyboard-1',
|
||||
this.functionButtons = const FunctionButtonConfig(),
|
||||
this.alarmSyncIntervalSeconds = 30,
|
||||
this.connectionRetrySeconds = 5,
|
||||
this.commandTimeoutSeconds = 10,
|
||||
});
|
||||
|
||||
/// Load configuration from file or assets
|
||||
static Future<AppConfig> load({String? configPath}) async {
|
||||
final logger = Logger();
|
||||
Map<String, dynamic> configJson;
|
||||
|
||||
// Try loading from file path first
|
||||
if (configPath != null) {
|
||||
try {
|
||||
final file = File(configPath);
|
||||
if (await file.exists()) {
|
||||
final contents = await file.readAsString();
|
||||
configJson = jsonDecode(contents) as Map<String, dynamic>;
|
||||
logger.i('Loaded config from: $configPath');
|
||||
return _parseConfig(configJson);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.w('Failed to load config from file: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Try common file locations
|
||||
final commonPaths = [
|
||||
'servers.json',
|
||||
'../servers.json',
|
||||
'config/servers.json',
|
||||
r'C:\DEV\COPILOT_D6\servers.json',
|
||||
];
|
||||
|
||||
for (final path in commonPaths) {
|
||||
try {
|
||||
final file = File(path);
|
||||
if (await file.exists()) {
|
||||
final contents = await file.readAsString();
|
||||
configJson = jsonDecode(contents) as Map<String, dynamic>;
|
||||
logger.i('Loaded config from: $path');
|
||||
return _parseConfig(configJson);
|
||||
}
|
||||
} catch (e) {
|
||||
// Continue to next path
|
||||
}
|
||||
}
|
||||
|
||||
// Try loading from assets
|
||||
try {
|
||||
final contents = await rootBundle.loadString('assets/config/servers.json');
|
||||
configJson = jsonDecode(contents) as Map<String, dynamic>;
|
||||
logger.i('Loaded config from assets');
|
||||
return _parseConfig(configJson);
|
||||
} catch (e) {
|
||||
logger.w('Failed to load config from assets: $e');
|
||||
}
|
||||
|
||||
// Return default empty config
|
||||
logger.w('No config file found, using defaults');
|
||||
return const AppConfig(servers: []);
|
||||
}
|
||||
|
||||
static AppConfig _parseConfig(Map<String, dynamic> json) {
|
||||
final serversJson = json['servers'] as List<dynamic>? ?? [];
|
||||
final servers = serversJson
|
||||
.map((s) => ServerConfig.fromJson(s as Map<String, dynamic>))
|
||||
.where((s) => s.enabled)
|
||||
.toList();
|
||||
|
||||
final settings = json['settings'] as Map<String, dynamic>? ?? {};
|
||||
|
||||
// Parse function button config
|
||||
final fbJson = json['functionButtons'] as Map<String, dynamic>?;
|
||||
final functionButtons = fbJson != null
|
||||
? FunctionButtonConfig.fromJson(fbJson)
|
||||
: const FunctionButtonConfig();
|
||||
|
||||
return AppConfig(
|
||||
servers: servers,
|
||||
coordinatorUrl: settings['coordinatorUrl'] as String? ?? 'http://localhost:8090',
|
||||
keyboardId: settings['keyboardId'] as String? ?? 'keyboard-1',
|
||||
functionButtons: functionButtons,
|
||||
alarmSyncIntervalSeconds: settings['alarmSyncIntervalSeconds'] as int? ?? 30,
|
||||
connectionRetrySeconds: settings['connectionRetrySeconds'] as int? ?? 5,
|
||||
commandTimeoutSeconds: settings['commandTimeoutSeconds'] as int? ?? 10,
|
||||
);
|
||||
}
|
||||
|
||||
/// Get servers by type
|
||||
List<ServerConfig> getServersByType(ServerType type) {
|
||||
return servers.where((s) => s.type == type).toList();
|
||||
}
|
||||
|
||||
/// Get server that owns a camera ID
|
||||
ServerConfig? getServerForCamera(int cameraId) {
|
||||
for (final server in servers) {
|
||||
if (server.ownsCamera(cameraId)) {
|
||||
return server;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Get server that owns a monitor ID
|
||||
ServerConfig? getServerForMonitor(int monitorId) {
|
||||
for (final server in servers) {
|
||||
if (server.ownsMonitor(monitorId)) {
|
||||
return server;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Get total camera count across all servers
|
||||
int get totalCameras {
|
||||
int count = 0;
|
||||
for (final server in servers) {
|
||||
count += (server.cameraRangeEnd - server.cameraRangeStart + 1);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/// Get total monitor count across all servers
|
||||
int get totalMonitors {
|
||||
int count = 0;
|
||||
for (final server in servers) {
|
||||
count += (server.monitorRangeEnd - server.monitorRangeStart + 1);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/// Get all camera IDs
|
||||
List<int> get allCameraIds {
|
||||
final ids = <int>[];
|
||||
for (final server in servers) {
|
||||
for (int i = server.cameraRangeStart; i <= server.cameraRangeEnd; i++) {
|
||||
ids.add(i);
|
||||
}
|
||||
}
|
||||
ids.sort();
|
||||
return ids;
|
||||
}
|
||||
|
||||
/// Get all monitor IDs
|
||||
List<int> get allMonitorIds {
|
||||
final ids = <int>[];
|
||||
for (final server in servers) {
|
||||
for (int i = server.monitorRangeStart; i <= server.monitorRangeEnd; i++) {
|
||||
ids.add(i);
|
||||
}
|
||||
}
|
||||
ids.sort();
|
||||
return ids;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user