diff --git a/core/functions/Multidongle.py b/core/functions/Multidongle.py index 5ae609b..4642342 100644 --- a/core/functions/Multidongle.py +++ b/core/functions/Multidongle.py @@ -33,7 +33,7 @@ class InferenceResult: class DongleSeriesSpec: """Dongle series specifications with GOPS capacity for load balancing""" - KL520_GOPS = 3 + KL520_GOPS = 2 KL720_GOPS = 28 SERIES_SPECS = { @@ -41,7 +41,7 @@ class DongleSeriesSpec: "KL720": {"product_id": 0x720, "gops": KL720_GOPS}, "KL630": {"product_id": 0x630, "gops": 400}, "KL730": {"product_id": 0x730, "gops": 1600}, - "KL540": {"product_id": 0x540, "gops": 800} + # "KL540": {"product_id": 0x540, "gops": 800} } @@ -115,7 +115,7 @@ class MultiDongle: "0x720": "KL720", "0x630": "KL630", "0x730": "KL730", - "0x540": "KL540", + # "0x540": "KL540", } @staticmethod @@ -208,8 +208,8 @@ class MultiDongle: return 'KL630' elif chip == kp.ModelNefDescriptor.KP_CHIP_KL730: return 'KL730' - elif chip == kp.ModelNefDescriptor.KP_CHIP_KL540: - return 'KL540' + # elif chip == kp.ModelNefDescriptor.KP_CHIP_KL540: + # return 'KL540' # Final fallback return 'Unknown' @@ -468,24 +468,47 @@ class MultiDongle: def _select_optimal_series(self) -> Optional[str]: """ - Select optimal series based on current load and GOPS capacity - Returns the series name with the best load/capacity ratio + Select optimal series based on current load and GOPS capacity with performance bias + Returns the series name with the best load/capacity ratio, favoring high-performance dongles """ if not self.multi_series_mode or not self.series_groups: return None - best_ratio = float('inf') + best_score = float('inf') selected_series = None + # Get series GOPS values for performance bias + series_gops = {} + for series_name in self.series_groups.keys(): + # Extract GOPS from DongleSeriesSpec + for spec_name, spec_info in DongleSeriesSpec.SERIES_SPECS.items(): + if spec_name == series_name: + series_gops[series_name] = spec_info["gops"] + break + for series_name in self.series_groups.keys(): current_load = self.current_loads.get(series_name, 0) weight = self.gops_weights.get(series_name, 0) + gops = series_gops.get(series_name, 1) + if weight <= 0: + continue + # Calculate load ratio (lower is better) - load_ratio = current_load / weight if weight > 0 else float('inf') + load_ratio = current_load / weight - if load_ratio < best_ratio: - best_ratio = load_ratio + # Add performance bias: penalize low-GOPS devices more heavily + # This encourages using high-performance dongles even if they have slightly higher load + if gops < 10: # Low-performance threshold (like KL520 with 2 GOPS) + performance_penalty = 2.0 # 2x penalty for slow devices + else: + performance_penalty = 1.0 + + # Combined score considers both load and performance + combined_score = load_ratio * performance_penalty + + if combined_score < best_score: + best_score = combined_score selected_series = series_name return selected_series @@ -1111,11 +1134,20 @@ class MultiDongle: """Result ordering thread: ensures results are output in sequence order""" print("Result ordering worker started") + # Track when we started waiting for each sequence + sequence_wait_times = {} + MAX_WAIT_TIME = 2.0 # Maximum wait time for slow sequences (seconds) + while not self._stop_event.is_set(): + current_time = time.time() + # Check if next expected result is available if self.next_output_sequence in self.pending_results: result = self.pending_results.pop(self.next_output_sequence) self._ordered_output_queue.put(result) + + # Remove from wait tracking + sequence_wait_times.pop(self.next_output_sequence, None) self.next_output_sequence += 1 # Clean up old pending results to prevent memory bloat @@ -1125,7 +1157,29 @@ class MultiDongle: if seq_id < self.next_output_sequence: self.pending_results.pop(seq_id, None) else: - time.sleep(0.001) # Small delay to prevent busy waiting + # Track how long we've been waiting for this sequence + if self.next_output_sequence not in sequence_wait_times: + sequence_wait_times[self.next_output_sequence] = current_time + + # Check if we've been waiting too long + wait_time = current_time - sequence_wait_times[self.next_output_sequence] + if wait_time > MAX_WAIT_TIME: + print(f"Warning: Skipping sequence {self.next_output_sequence} after {wait_time:.2f}s timeout") + + # Create a timeout result + timeout_result = { + 'sequence_id': self.next_output_sequence, + 'result': {'error': 'timeout', 'probability': 0.0, 'result_string': 'Timeout'}, + 'dongle_series': 'timeout', + 'timestamp': current_time + } + self._ordered_output_queue.put(timeout_result) + + # Remove from wait tracking and advance sequence + sequence_wait_times.pop(self.next_output_sequence, None) + self.next_output_sequence += 1 + else: + time.sleep(0.001) # Small delay to prevent busy waiting print("Result ordering worker stopped") @@ -1250,7 +1304,7 @@ class MultiDongle: 'kl720': 'KL720', 'kl630': 'KL630', 'kl730': 'KL730', - 'kl540': 'KL540', + # 'kl540': 'KL540', } if isinstance(chip_id, str): diff --git a/core/nodes/exact_nodes.py b/core/nodes/exact_nodes.py index 084545a..4c4fc99 100644 --- a/core/nodes/exact_nodes.py +++ b/core/nodes/exact_nodes.py @@ -127,7 +127,7 @@ class ExactModelNode(BaseNode): self.create_property('kl720_port_ids', '') self.create_property('kl630_port_ids', '') self.create_property('kl730_port_ids', '') - self.create_property('kl540_port_ids', '') + # self.create_property('kl540_port_ids', '') self.create_property('max_queue_size', 100) self.create_property('result_buffer_size', 1000) @@ -137,7 +137,7 @@ class ExactModelNode(BaseNode): # Original property options - exact match self._property_options = { - 'dongle_series': ['520', '720', '1080', 'Custom'], + 'dongle_series': ['520', '720'], '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)'}, @@ -155,7 +155,7 @@ class ExactModelNode(BaseNode): 'kl720_port_ids': {'placeholder': 'e.g., 30,34 (comma-separated port IDs for KL720)', 'description': 'Port IDs for KL720 dongles'}, 'kl630_port_ids': {'placeholder': 'e.g., 36,38 (comma-separated port IDs for KL630)', 'description': 'Port IDs for KL630 dongles'}, 'kl730_port_ids': {'placeholder': 'e.g., 40,42 (comma-separated port IDs for KL730)', 'description': 'Port IDs for KL730 dongles'}, - 'kl540_port_ids': {'placeholder': 'e.g., 44,46 (comma-separated port IDs for KL540)', 'description': 'Port IDs for KL540 dongles'}, + # 'kl540_port_ids': {'placeholder': 'e.g., 44,46 (comma-separated port IDs for KL540)', 'description': 'Port IDs for KL540 dongles'}, 'max_queue_size': {'min': 1, 'max': 1000, 'default': 100}, 'result_buffer_size': {'min': 100, 'max': 10000, 'default': 1000}, @@ -471,7 +471,7 @@ class ExactModelNode(BaseNode): return False # Check for at least one series subfolder - expected_series = ['KL520', 'KL720', 'KL630', 'KL730', 'KL540'] + expected_series = ['KL520', 'KL720', 'KL630', 'KL730'] firmware_series = [d for d in os.listdir(firmware_path) if os.path.isdir(os.path.join(firmware_path, d)) and d in expected_series]