cluster4npu/core/nodes/exact_nodes.py

416 lines
16 KiB
Python

"""
Exact node implementations matching the original UI.py properties.
This module provides node implementations that exactly match the original
properties and behavior from the monolithic UI.py file.
"""
try:
from NodeGraphQt import BaseNode
NODEGRAPH_AVAILABLE = True
except ImportError:
NODEGRAPH_AVAILABLE = False
# Create a mock base class with property support
class BaseNode:
def __init__(self):
self._properties = {}
def create_property(self, name, value):
self._properties[name] = value
def set_property(self, name, value):
self._properties[name] = value
def get_property(self, name):
return self._properties.get(name, None)
def add_input(self, *args, **kwargs):
pass
def add_output(self, *args, **kwargs):
pass
def set_color(self, *args, **kwargs):
pass
def name(self):
return getattr(self, 'NODE_NAME', 'Unknown Node')
class ExactInputNode(BaseNode):
"""Input data source node - exact match to original."""
__identifier__ = 'com.cluster.input_node.ExactInputNode'
NODE_NAME = 'Input Node'
def __init__(self):
super().__init__()
if NODEGRAPH_AVAILABLE:
# Setup node connections - exact match
self.add_output('output', color=(0, 255, 0))
self.set_color(83, 133, 204)
# Original properties - exact match
self.create_property('source_type', 'Camera')
self.create_property('device_id', 0)
self.create_property('source_path', '')
self.create_property('resolution', '1920x1080')
self.create_property('fps', 30)
# Original property options - exact match
self._property_options = {
'source_type': ['Camera', 'Microphone', 'File', 'RTSP Stream', 'HTTP Stream'],
'device_id': {'min': 0, 'max': 10},
'resolution': ['640x480', '1280x720', '1920x1080', '3840x2160', 'Custom'],
'fps': {'min': 1, 'max': 120},
'source_path': {'type': 'file_path', 'filter': 'Media files (*.mp4 *.avi *.mov *.mkv *.wav *.mp3)'}
}
# Create custom properties dictionary for UI compatibility
self._populate_custom_properties()
def _populate_custom_properties(self):
"""Populate the custom properties dictionary for UI compatibility."""
if not NODEGRAPH_AVAILABLE:
return
# Get all business properties defined in _property_options
business_props = list(self._property_options.keys())
# Create custom dictionary containing current property values
custom_dict = {}
for prop_name in business_props:
try:
# Skip 'custom' property to avoid infinite recursion
if prop_name != 'custom':
custom_dict[prop_name] = self.get_property(prop_name)
except:
# If property doesn't exist, skip it
pass
# Create the custom property that contains all business properties
self.create_property('custom', custom_dict)
def get_business_properties(self):
"""Get all business properties for serialization."""
properties = {}
for prop_name in self._property_options.keys():
try:
properties[prop_name] = self.get_property(prop_name)
except:
pass
return properties
def get_display_properties(self):
"""Return properties that should be displayed in the UI panel."""
# Customize which properties appear in the properties panel
# You can reorder, filter, or modify this list
return ['source_type', 'resolution', 'fps'] # Only show these 3 properties
class ExactModelNode(BaseNode):
"""Model node for ML inference - exact match to original."""
__identifier__ = 'com.cluster.model_node.ExactModelNode'
NODE_NAME = 'Model Node'
def __init__(self):
super().__init__()
# Setup node connections (NodeGraphQt specific)
if NODEGRAPH_AVAILABLE:
self.add_input('input', multi_input=False, color=(255, 140, 0))
self.add_output('output', color=(0, 255, 0))
self.set_color(65, 84, 102)
# Create properties (always, regardless of NodeGraphQt availability)
self.create_property('multi_series_mode', False)
# Multi-series properties
self.create_property('assets_folder', '')
self.create_property('enabled_series', ['520', '720'])
self.create_property('port_mapping', {})
# Single-series properties (original)
self.create_property('model_path', '')
self.create_property('scpu_fw_path', '')
self.create_property('ncpu_fw_path', '')
self.create_property('dongle_series', '520')
self.create_property('num_dongles', 1)
self.create_property('port_id', '')
self.create_property('upload_fw', True)
# Property options with multi-series support (always available)
self._property_options = {
# Multi-series properties
'multi_series_mode': {'type': 'bool', 'default': False, 'description': 'Enable multi-series dongle support'},
'assets_folder': {'type': 'file_path', 'filter': 'Directories', 'mode': 'directory'},
'enabled_series': ['520', '720', '630', '730', '540'],
'port_mapping': {'type': 'dict', 'description': 'Port ID to series mapping'},
# Single-series properties (original)
'dongle_series': ['520', '720', '1080', 'Custom'],
'num_dongles': {'min': 1, 'max': 16},
'model_path': {'type': 'file_path', 'filter': 'NEF Model files (*.nef)'},
'scpu_fw_path': {'type': 'file_path', 'filter': 'SCPU Firmware files (*.bin)'},
'ncpu_fw_path': {'type': 'file_path', 'filter': 'NCPU Firmware files (*.bin)'},
'port_id': {'placeholder': 'e.g., 8080 or auto'},
'upload_fw': {'type': 'bool', 'default': True, 'description': 'Upload firmware to dongle if needed'}
}
# Create custom properties dictionary for UI compatibility (NodeGraphQt specific)
if NODEGRAPH_AVAILABLE:
self._populate_custom_properties()
def _populate_custom_properties(self):
"""Populate the custom properties dictionary for UI compatibility."""
if not NODEGRAPH_AVAILABLE:
return
# Get all business properties defined in _property_options
business_props = list(self._property_options.keys())
# Create custom dictionary containing current property values
custom_dict = {}
for prop_name in business_props:
try:
# Skip 'custom' property to avoid infinite recursion
if prop_name != 'custom':
custom_dict[prop_name] = self.get_property(prop_name)
except:
# If property doesn't exist, skip it
pass
# Create the custom property that contains all business properties
self.create_property('custom', custom_dict)
def get_business_properties(self):
"""Get all business properties for serialization."""
properties = {}
for prop_name in self._property_options.keys():
try:
properties[prop_name] = self.get_property(prop_name)
except:
pass
return properties
def get_display_properties(self):
"""Return properties that should be displayed in the UI panel."""
# Check if multi-series mode is enabled
multi_series_enabled = False
try:
multi_series_enabled = self.get_property('multi_series_mode')
except:
pass
if multi_series_enabled:
# Multi-series mode properties
return ['multi_series_mode', 'assets_folder', 'enabled_series', 'port_mapping']
else:
# Single-series mode properties (original)
return ['multi_series_mode', 'model_path', 'scpu_fw_path', 'ncpu_fw_path', 'dongle_series', 'num_dongles', 'port_id', 'upload_fw']
class ExactPreprocessNode(BaseNode):
"""Preprocessing node - exact match to original."""
__identifier__ = 'com.cluster.preprocess_node.ExactPreprocessNode'
NODE_NAME = 'Preprocess Node'
def __init__(self):
super().__init__()
if NODEGRAPH_AVAILABLE:
# Setup node connections - exact match
self.add_input('input', multi_input=False, color=(255, 140, 0))
self.add_output('output', color=(0, 255, 0))
self.set_color(45, 126, 72)
# Original properties - exact match
self.create_property('resize_width', 640)
self.create_property('resize_height', 480)
self.create_property('normalize', True)
self.create_property('crop_enabled', False)
self.create_property('operations', 'resize,normalize')
# Original property options - exact match
self._property_options = {
'resize_width': {'min': 64, 'max': 4096},
'resize_height': {'min': 64, 'max': 4096},
'operations': {'placeholder': 'comma-separated: resize,normalize,crop'}
}
# Create custom properties dictionary for UI compatibility
self._populate_custom_properties()
def _populate_custom_properties(self):
"""Populate the custom properties dictionary for UI compatibility."""
if not NODEGRAPH_AVAILABLE:
return
# Get all business properties defined in _property_options
business_props = list(self._property_options.keys())
# Create custom dictionary containing current property values
custom_dict = {}
for prop_name in business_props:
try:
# Skip 'custom' property to avoid infinite recursion
if prop_name != 'custom':
custom_dict[prop_name] = self.get_property(prop_name)
except:
# If property doesn't exist, skip it
pass
# Create the custom property that contains all business properties
self.create_property('custom', custom_dict)
def get_business_properties(self):
"""Get all business properties for serialization."""
properties = {}
for prop_name in self._property_options.keys():
try:
properties[prop_name] = self.get_property(prop_name)
except:
pass
return properties
class ExactPostprocessNode(BaseNode):
"""Postprocessing node - exact match to original."""
__identifier__ = 'com.cluster.postprocess_node.ExactPostprocessNode'
NODE_NAME = 'Postprocess Node'
def __init__(self):
super().__init__()
if NODEGRAPH_AVAILABLE:
# Setup node connections - exact match
self.add_input('input', multi_input=False, color=(255, 140, 0))
self.add_output('output', color=(0, 255, 0))
self.set_color(153, 51, 51)
# Original properties - exact match
self.create_property('output_format', 'JSON')
self.create_property('confidence_threshold', 0.5)
self.create_property('nms_threshold', 0.4)
self.create_property('max_detections', 100)
# Original property options - exact match
self._property_options = {
'output_format': ['JSON', 'XML', 'CSV', 'Binary'],
'confidence_threshold': {'min': 0.0, 'max': 1.0, 'step': 0.1},
'nms_threshold': {'min': 0.0, 'max': 1.0, 'step': 0.1},
'max_detections': {'min': 1, 'max': 1000}
}
# Create custom properties dictionary for UI compatibility
self._populate_custom_properties()
def _populate_custom_properties(self):
"""Populate the custom properties dictionary for UI compatibility."""
if not NODEGRAPH_AVAILABLE:
return
# Get all business properties defined in _property_options
business_props = list(self._property_options.keys())
# Create custom dictionary containing current property values
custom_dict = {}
for prop_name in business_props:
try:
# Skip 'custom' property to avoid infinite recursion
if prop_name != 'custom':
custom_dict[prop_name] = self.get_property(prop_name)
except:
# If property doesn't exist, skip it
pass
# Create the custom property that contains all business properties
self.create_property('custom', custom_dict)
def get_business_properties(self):
"""Get all business properties for serialization."""
properties = {}
for prop_name in self._property_options.keys():
try:
properties[prop_name] = self.get_property(prop_name)
except:
pass
return properties
class ExactOutputNode(BaseNode):
"""Output data sink node - exact match to original."""
__identifier__ = 'com.cluster.output_node.ExactOutputNode'
NODE_NAME = 'Output Node'
def __init__(self):
super().__init__()
if NODEGRAPH_AVAILABLE:
# Setup node connections - exact match
self.add_input('input', multi_input=False, color=(255, 140, 0))
self.set_color(255, 140, 0)
# Original properties - exact match
self.create_property('output_type', 'File')
self.create_property('destination', '')
self.create_property('format', 'JSON')
self.create_property('save_interval', 1.0)
# Original property options - exact match
self._property_options = {
'output_type': ['File', 'API Endpoint', 'Database', 'Display', 'MQTT'],
'format': ['JSON', 'XML', 'CSV', 'Binary'],
'destination': {'type': 'file_path', 'filter': 'Output files (*.json *.xml *.csv *.txt)'},
'save_interval': {'min': 0.1, 'max': 60.0, 'step': 0.1}
}
# Create custom properties dictionary for UI compatibility
self._populate_custom_properties()
def _populate_custom_properties(self):
"""Populate the custom properties dictionary for UI compatibility."""
if not NODEGRAPH_AVAILABLE:
return
# Get all business properties defined in _property_options
business_props = list(self._property_options.keys())
# Create custom dictionary containing current property values
custom_dict = {}
for prop_name in business_props:
try:
# Skip 'custom' property to avoid infinite recursion
if prop_name != 'custom':
custom_dict[prop_name] = self.get_property(prop_name)
except:
# If property doesn't exist, skip it
pass
# Create the custom property that contains all business properties
self.create_property('custom', custom_dict)
def get_business_properties(self):
"""Get all business properties for serialization."""
properties = {}
for prop_name in self._property_options.keys():
try:
properties[prop_name] = self.get_property(prop_name)
except:
pass
return properties
# Export the exact nodes
EXACT_NODE_TYPES = {
'Input Node': ExactInputNode,
'Model Node': ExactModelNode,
'Preprocess Node': ExactPreprocessNode,
'Postprocess Node': ExactPostprocessNode,
'Output Node': ExactOutputNode
}