Files
geutebruck/compare_set_files.py
Administrator 14893e62a5 feat: Geutebruck GeViScope/GeViSoft Action Mapping System - MVP
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>
2025-12-31 18:10:54 +01:00

187 lines
5.7 KiB
Python

#!/usr/bin/env python3
"""Compare two .set files to find differences when adding a mapping"""
import sys
from pathlib import Path
def compare_binary_files(file1_path, file2_path):
"""Compare two binary files and show differences"""
# Read both files
with open(file1_path, 'rb') as f1:
data1 = f1.read()
with open(file2_path, 'rb') as f2:
data2 = f2.read()
print(f"File 1 (original): {len(data1):,} bytes")
print(f"File 2 (added): {len(data2):,} bytes")
print(f"Size difference: {len(data2) - len(data1):,} bytes")
print("=" * 80)
# Find first difference
min_len = min(len(data1), len(data2))
first_diff = None
for i in range(min_len):
if data1[i] != data2[i]:
first_diff = i
break
if first_diff is not None:
print(f"\nFirst difference at offset: {first_diff} (0x{first_diff:X})")
# Show context around first difference
start = max(0, first_diff - 50)
end = min(min_len, first_diff + 200)
print(f"\nOriginal file bytes [{start}:{end}]:")
print_hex_dump(data1[start:end], start)
print(f"\nAdded mapping file bytes [{start}:{end}]:")
print_hex_dump(data2[start:end], start)
else:
print("\nFiles are identical up to the shorter length")
if len(data1) != len(data2):
print(f"Extra data at end of {'file1' if len(data1) > len(data2) else 'file2'}")
# Find all difference regions
print("\n" + "=" * 80)
print("FINDING ALL DIFFERENCE REGIONS:")
print("=" * 80)
diff_regions = []
in_diff = False
diff_start = None
for i in range(min_len):
if data1[i] != data2[i]:
if not in_diff:
diff_start = i
in_diff = True
else:
if in_diff:
diff_regions.append((diff_start, i))
in_diff = False
if in_diff:
diff_regions.append((diff_start, min_len))
print(f"\nFound {len(diff_regions)} different regions:")
for idx, (start, end) in enumerate(diff_regions[:10], 1): # Show first 10
length = end - start
print(f"\nRegion {idx}: offset {start} (0x{start:X}), length {length} bytes")
# Show bytes
region_start = max(0, start - 20)
region_end = min(min_len, end + 20)
print(f" Original [{region_start}:{region_end}]:")
print_hex_dump(data1[region_start:region_end], region_start, highlight_start=start, highlight_end=end)
print(f" Added [{region_start}:{region_end}]:")
print_hex_dump(data2[region_start:region_end], region_start, highlight_start=start, highlight_end=end)
# Count "ules" markers in each file
print("\n" + "=" * 80)
print("ACTION MAPPING MARKERS:")
print("=" * 80)
ules_count1 = count_markers(data1, b'ules')
ules_count2 = count_markers(data2, b'ules')
print(f"'ules' markers in original: {ules_count1}")
print(f"'ules' markers in added: {ules_count2}")
print(f"Difference: {ules_count2 - ules_count1}")
# Find positions of ules markers
print("\nPositions of 'ules' markers:")
print("Original file:")
find_and_print_markers(data1, b'ules')
print("\nAdded mapping file:")
find_and_print_markers(data2, b'ules')
def print_hex_dump(data, offset=0, highlight_start=None, highlight_end=None):
"""Print hex dump of data with optional highlighting"""
for i in range(0, len(data), 16):
chunk = data[i:i+16]
addr = offset + i
# Hex part
hex_parts = []
for j, byte in enumerate(chunk):
byte_offset = addr + j
if highlight_start and highlight_end and highlight_start <= byte_offset < highlight_end:
hex_parts.append(f"\033[91m{byte:02X}\033[0m") # Red highlight
else:
hex_parts.append(f"{byte:02X}")
hex_str = " ".join(hex_parts)
# ASCII part
ascii_parts = []
for j, byte in enumerate(chunk):
byte_offset = addr + j
if 32 <= byte <= 126:
char = chr(byte)
else:
char = "."
if highlight_start and highlight_end and highlight_start <= byte_offset < highlight_end:
ascii_parts.append(f"\033[91m{char}\033[0m")
else:
ascii_parts.append(char)
ascii_str = "".join(ascii_parts)
print(f" {addr:06X}: {hex_str:<48} {ascii_str}")
def count_markers(data, marker):
"""Count occurrences of marker in data"""
count = 0
pos = 0
while True:
pos = data.find(marker, pos)
if pos == -1:
break
count += 1
pos += len(marker)
return count
def find_and_print_markers(data, marker):
"""Find and print all marker positions"""
positions = []
pos = 0
while True:
pos = data.find(marker, pos)
if pos == -1:
break
positions.append(pos)
pos += len(marker)
for i, pos in enumerate(positions[:20], 1): # Show first 20
# Check if it's preceded by 0x05 (marker byte)
if pos > 0 and data[pos-1] == 0x05:
print(f" {i:2d}. Offset {pos:6d} (0x{pos:05X}) - marker type 0x05")
else:
print(f" {i:2d}. Offset {pos:6d} (0x{pos:05X})")
if len(positions) > 20:
print(f" ... and {len(positions) - 20} more")
if __name__ == '__main__':
original = 'TestMKS_original.set'
added = 'TestMKS_added_mapping.set'
if not Path(original).exists():
print(f"Error: {original} not found")
sys.exit(1)
if not Path(added).exists():
print(f"Error: {added} not found")
sys.exit(1)
compare_binary_files(original, added)