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>
165 lines
5.7 KiB
Python
165 lines
5.7 KiB
Python
"""
|
|
Parse all action documentation HTML files to extract complete action catalog
|
|
"""
|
|
import re
|
|
import json
|
|
from pathlib import Path
|
|
from bs4 import BeautifulSoup
|
|
|
|
# Base path for action documentation
|
|
ACTION_DOCS_PATH = Path(r"C:\DEV\COPILOT\SOURCES\CODEX\GeViScope\GeViScopeSDK_HTML5\Content\ActionDoku")
|
|
|
|
# Category mapping from filename to display name
|
|
CATEGORY_MAPPING = {
|
|
"ATMActions.htm": "ATM",
|
|
"AudioActions.htm": "Audio",
|
|
"BackupActions.htm": "Backup",
|
|
"CameraControlActions.htm": "Camera Control",
|
|
"CashManagementActions.htm": "Cash Management",
|
|
"DeviceActions.htm": "Device",
|
|
"DigitalContactsActions.htm": "Digital Contacts",
|
|
"LogisticActions.htm": "Logistics",
|
|
"LPSActions.htm": "License Plate System",
|
|
"POSActions.htm": "Point of Sale",
|
|
"RemoteExportActions.htm": "Remote Export",
|
|
"SkiDataActions.htm": "Ski Data",
|
|
"SystemActions.htm": "System",
|
|
"VideoActions.htm": "Video",
|
|
"ViewerActions.htm": "Viewer",
|
|
"ViewerNotificationActions.htm": "Viewer Notification",
|
|
"Lenelactions.htm": "Lenel Access Control"
|
|
}
|
|
|
|
def extract_action_name(action_name_str):
|
|
"""
|
|
Extract action name and parameters from string like:
|
|
'CustomAction(Int, String)' -> ('CustomAction', ['Int', 'String'])
|
|
"""
|
|
match = re.match(r'([A-Za-z0-9_]+)\((.*?)\)', action_name_str)
|
|
if match:
|
|
name = match.group(1)
|
|
params_str = match.group(2)
|
|
if params_str.strip():
|
|
params = [p.strip() for p in params_str.split(',')]
|
|
else:
|
|
params = []
|
|
return name, params
|
|
return action_name_str, []
|
|
|
|
def parse_action_html(html_file, category):
|
|
"""Parse a single HTML file and extract all actions"""
|
|
actions = {}
|
|
|
|
try:
|
|
with open(html_file, 'r', encoding='utf-8') as f:
|
|
soup = BeautifulSoup(f.read(), 'html.parser')
|
|
|
|
# Find all h2 headers (each action starts with h2)
|
|
for h2 in soup.find_all('h2'):
|
|
# Get the next p tag which should contain "Action name:"
|
|
action_name_p = h2.find_next('p')
|
|
if not action_name_p:
|
|
continue
|
|
|
|
action_name_text = action_name_p.get_text()
|
|
if not action_name_text.startswith('Action name:'):
|
|
continue
|
|
|
|
# Extract action name
|
|
action_full_name = action_name_text.replace('Action name:', '').strip()
|
|
action_name, param_types = extract_action_name(action_full_name)
|
|
|
|
# Get action category (logical, notification, command)
|
|
category_p = action_name_p.find_next('p')
|
|
action_category = "unknown"
|
|
if category_p and 'Action category:' in category_p.get_text():
|
|
action_category = category_p.get_text().replace('Action category:', '').strip()
|
|
|
|
# Get description
|
|
desc_p = category_p.find_next('p') if category_p else h2.find_next('p', class_=None)
|
|
description = desc_p.get_text().strip() if desc_p else ""
|
|
|
|
# Find parameter table
|
|
param_table = None
|
|
current = h2.find_next_sibling()
|
|
while current and current.name != 'h2':
|
|
if current.name == 'table':
|
|
param_table = current
|
|
break
|
|
current = current.find_next_sibling()
|
|
|
|
# Extract parameters from table
|
|
parameters = []
|
|
if param_table:
|
|
rows = param_table.find_all('tr')
|
|
for row in rows[1:]: # Skip header row
|
|
cols = row.find_all('td')
|
|
if len(cols) >= 2:
|
|
param_name = cols[0].get_text().strip()
|
|
param_type = cols[1].get_text().strip()
|
|
if param_name and param_type:
|
|
parameters.append(param_name)
|
|
|
|
# Store action info
|
|
actions[action_name] = {
|
|
"parameters": parameters,
|
|
"description": description,
|
|
"category": category,
|
|
"action_category": action_category,
|
|
"required_caption": True, # Default, can be refined
|
|
"supports_delay": True # Default, can be refined
|
|
}
|
|
|
|
print(f" Found: {action_name} ({len(parameters)} params)")
|
|
|
|
except Exception as e:
|
|
print(f"Error parsing {html_file}: {e}")
|
|
|
|
return actions
|
|
|
|
def main():
|
|
"""Parse all action HTML files and create comprehensive action catalog"""
|
|
all_actions = {}
|
|
|
|
print("Parsing action documentation files...")
|
|
print("=" * 60)
|
|
|
|
for filename, category_name in CATEGORY_MAPPING.items():
|
|
html_file = ACTION_DOCS_PATH / filename
|
|
|
|
if not html_file.exists():
|
|
print(f"⚠ Skipping {filename} (not found)")
|
|
continue
|
|
|
|
print(f"\n{category_name} ({filename})")
|
|
print("-" * 60)
|
|
|
|
actions = parse_action_html(html_file, category_name)
|
|
all_actions.update(actions)
|
|
|
|
print(f" Total: {len(actions)} actions")
|
|
|
|
print("\n" + "=" * 60)
|
|
print(f"TOTAL ACTIONS FOUND: {len(all_actions)}")
|
|
print("=" * 60)
|
|
|
|
# Save to JSON
|
|
output_file = Path(r"C:\DEV\COPILOT\all_actions_catalog.json")
|
|
with open(output_file, 'w', encoding='utf-8') as f:
|
|
json.dump(all_actions, f, indent=2, ensure_ascii=False)
|
|
|
|
print(f"\nSaved to: {output_file}")
|
|
|
|
# Print category breakdown
|
|
print("\nActions by category:")
|
|
category_counts = {}
|
|
for action_name, action_info in all_actions.items():
|
|
cat = action_info['category']
|
|
category_counts[cat] = category_counts.get(cat, 0) + 1
|
|
|
|
for cat, count in sorted(category_counts.items(), key=lambda x: x[1], reverse=True):
|
|
print(f" {cat}: {count} actions")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|