This MVP release provides a complete full-stack solution for managing action mappings in Geutebruck's GeViScope and GeViSoft video surveillance systems. ## Features ### Flutter Web Application (Port 8081) - Modern, responsive UI for managing action mappings - Action picker dialog with full parameter configuration - Support for both GSC (GeViScope) and G-Core server actions - Consistent UI for input and output actions with edit/delete capabilities - Real-time action mapping creation, editing, and deletion - Server categorization (GSC: prefix for GeViScope, G-Core: prefix for G-Core servers) ### FastAPI REST Backend (Port 8000) - RESTful API for action mapping CRUD operations - Action template service with comprehensive action catalog (247 actions) - Server management (G-Core and GeViScope servers) - Configuration tree reading and writing - JWT authentication with role-based access control - PostgreSQL database integration ### C# SDK Bridge (gRPC, Port 50051) - Native integration with GeViSoft SDK (GeViProcAPINET_4_0.dll) - Action mapping creation with correct binary format - Support for GSC and G-Core action types - Proper Camera parameter inclusion in action strings (fixes CrossSwitch bug) - Action ID lookup table with server-specific action IDs - Configuration reading/writing via SetupClient ## Bug Fixes - **CrossSwitch Bug**: GSC and G-Core actions now correctly display camera/PTZ head parameters in GeViSet - Action strings now include Camera parameter: `@ PanLeft (Comment: "", Camera: 101028)` - Proper filter flags and VideoInput=0 for action mappings - Correct action ID assignment (4198 for GSC, 9294 for G-Core PanLeft) ## Technical Stack - **Frontend**: Flutter Web, Dart, Dio HTTP client - **Backend**: Python FastAPI, PostgreSQL, Redis - **SDK Bridge**: C# .NET 8.0, gRPC, GeViSoft SDK - **Authentication**: JWT tokens - **Configuration**: GeViSoft .set files (binary format) ## Credentials - GeViSoft/GeViScope: username=sysadmin, password=masterkey - Default admin: username=admin, password=admin123 ## Deployment All services run on localhost: - Flutter Web: http://localhost:8081 - FastAPI: http://localhost:8000 - SDK Bridge gRPC: localhost:50051 - GeViServer: localhost (default port) Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
202 lines
5.8 KiB
Python
202 lines
5.8 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Test and example script for GeViSet parser
|
|
|
|
This script demonstrates how to:
|
|
1. Parse a .set configuration file
|
|
2. Extract specific settings
|
|
3. Modify configuration
|
|
4. Generate a new .set file
|
|
"""
|
|
|
|
import sys
|
|
import json
|
|
from pathlib import Path
|
|
from geviset_parser import GeViSetParser, GeViSetGenerator
|
|
|
|
|
|
def test_parse(set_file_path):
|
|
"""Test parsing a .set file"""
|
|
print("=" * 80)
|
|
print("TEST 1: Parsing .set file")
|
|
print("=" * 80)
|
|
print()
|
|
|
|
parser = GeViSetParser()
|
|
config = parser.parse_file(set_file_path)
|
|
|
|
print("\nParsing Results:")
|
|
print(f" Top-level sections: {len(config)}")
|
|
print(f" Section names:")
|
|
for section_name in config.keys():
|
|
print(f" - {section_name}")
|
|
|
|
return config
|
|
|
|
|
|
def test_extract_users(config):
|
|
"""Test extracting user information"""
|
|
print("\n" + "=" * 80)
|
|
print("TEST 2: Extracting User Information")
|
|
print("=" * 80)
|
|
print()
|
|
|
|
if 'Users' not in config:
|
|
print(" No Users section found")
|
|
return
|
|
|
|
users_section = config['Users']
|
|
print(f" Users section metadata: {users_section.get('_metadata', {})}")
|
|
print(f"\n Users found:")
|
|
|
|
for key, value in users_section.items():
|
|
if key == '_metadata':
|
|
continue
|
|
|
|
if isinstance(value, dict):
|
|
print(f"\n User: {key}")
|
|
for prop_name, prop_value in value.items():
|
|
if prop_name == '_metadata':
|
|
print(f" Metadata: {prop_value}")
|
|
elif prop_name == 'Password':
|
|
# Truncate password hash for display
|
|
print(f" {prop_name}: {str(prop_value)[:16]}...")
|
|
else:
|
|
print(f" {prop_name}: {prop_value}")
|
|
|
|
|
|
def test_extract_network_settings(config):
|
|
"""Test extracting network settings"""
|
|
print("\n" + "=" * 80)
|
|
print("TEST 3: Extracting Network Settings")
|
|
print("=" * 80)
|
|
print()
|
|
|
|
# Look for GETAS section (network/TCP settings)
|
|
if 'GeViSoft Parameters' in config:
|
|
params = config['GeViSoft Parameters']
|
|
|
|
if 'GETAS' in params:
|
|
getas = params['GETAS']
|
|
print(" GETAS Configuration:")
|
|
|
|
for key, value in getas.items():
|
|
if key == '_metadata':
|
|
continue
|
|
print(f" {key}: {value}")
|
|
else:
|
|
print(" No GETAS section found")
|
|
else:
|
|
print(" No GeViSoft Parameters section found")
|
|
|
|
|
|
def test_modify_and_save(config, output_path):
|
|
"""Test modifying configuration and saving"""
|
|
print("\n" + "=" * 80)
|
|
print("TEST 4: Modifying and Saving Configuration")
|
|
print("=" * 80)
|
|
print()
|
|
|
|
# Create a copy to avoid modifying original
|
|
import copy
|
|
modified_config = copy.deepcopy(config)
|
|
|
|
# Example modification: Change GETAS TCP port
|
|
if 'GeViSoft Parameters' in modified_config:
|
|
if 'GETAS' in modified_config['GeViSoft Parameters']:
|
|
old_port = modified_config['GeViSoft Parameters']['GETAS'].get('TCPPort', 'N/A')
|
|
modified_config['GeViSoft Parameters']['GETAS']['TCPPort'] = 9999
|
|
print(f" Changed GETAS TCPPort: {old_port} -> 9999")
|
|
|
|
# Save modified configuration
|
|
generator = GeViSetGenerator()
|
|
generator.generate_file(modified_config, output_path)
|
|
|
|
print(f"\n Modified configuration saved to: {output_path}")
|
|
|
|
# Verify by re-parsing
|
|
print("\n Verifying modified file...")
|
|
parser2 = GeViSetParser()
|
|
verified_config = parser2.parse_file(output_path)
|
|
|
|
if 'GeViSoft Parameters' in verified_config:
|
|
if 'GETAS' in verified_config['GeViSoft Parameters']:
|
|
new_port = verified_config['GeViSoft Parameters']['GETAS'].get('TCPPort', 'N/A')
|
|
print(f" Verified TCPPort in new file: {new_port}")
|
|
|
|
if new_port == 9999:
|
|
print(" ✓ Modification successful!")
|
|
else:
|
|
print(" ✗ Modification failed!")
|
|
else:
|
|
print(" ✗ GETAS section missing in regenerated file")
|
|
else:
|
|
print(" ✗ GeViSoft Parameters missing in regenerated file")
|
|
|
|
|
|
def test_json_export(config, json_path):
|
|
"""Test exporting to JSON"""
|
|
print("\n" + "=" * 80)
|
|
print("TEST 5: Exporting to JSON")
|
|
print("=" * 80)
|
|
print()
|
|
|
|
# Convert to JSON
|
|
with open(json_path, 'w', encoding='utf-8') as f:
|
|
json.dump(config, f, indent=2, ensure_ascii=False)
|
|
|
|
print(f" Configuration exported to: {json_path}")
|
|
|
|
# Show file size
|
|
json_size = Path(json_path).stat().st_size
|
|
print(f" JSON file size: {json_size:,} bytes")
|
|
|
|
|
|
def main():
|
|
"""Main test function"""
|
|
if len(sys.argv) < 2:
|
|
print("Usage: python3 test_geviset_parser.py <path_to_config.set>")
|
|
print()
|
|
print("Example: python3 test_geviset_parser.py GeViSoft.set")
|
|
sys.exit(1)
|
|
|
|
set_file = Path(sys.argv[1])
|
|
|
|
if not set_file.exists():
|
|
print(f"Error: File not found: {set_file}")
|
|
sys.exit(1)
|
|
|
|
print(f"\nTesting GeViSet Parser with file: {set_file}")
|
|
print(f"File size: {set_file.stat().st_size:,} bytes")
|
|
print()
|
|
|
|
# Test 1: Parse
|
|
config = test_parse(set_file)
|
|
|
|
# Test 2: Extract users
|
|
test_extract_users(config)
|
|
|
|
# Test 3: Extract network settings
|
|
test_extract_network_settings(config)
|
|
|
|
# Test 4: Modify and save
|
|
output_file = set_file.parent / f"{set_file.stem}_modified{set_file.suffix}"
|
|
test_modify_and_save(config, output_file)
|
|
|
|
# Test 5: Export to JSON
|
|
json_file = set_file.parent / f"{set_file.stem}.json"
|
|
test_json_export(config, json_file)
|
|
|
|
print("\n" + "=" * 80)
|
|
print("ALL TESTS COMPLETE")
|
|
print("=" * 80)
|
|
print()
|
|
print("Generated files:")
|
|
print(f" - {output_file} (modified .set file)")
|
|
print(f" - {json_file} (JSON export)")
|
|
print()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|