Initial commit: Phase 1 & Phase 2 infrastructure complete
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
"""
|
||||
Main signature matching engine
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import List, Optional, Dict, Any
|
||||
from loguru import logger
|
||||
|
||||
from ..parser.metadata import SignalMetadata
|
||||
|
||||
|
||||
@dataclass
|
||||
class MatchResult:
|
||||
"""Result of a signature match"""
|
||||
device_id: int
|
||||
device_name: str
|
||||
manufacturer: str
|
||||
confidence: float # 0.0 to 1.0
|
||||
match_method: str # 'exact', 'partial', 'pattern', 'timing'
|
||||
match_details: Dict[str, Any]
|
||||
|
||||
def to_dict(self) -> Dict:
|
||||
"""Convert to dictionary"""
|
||||
return {
|
||||
'device_id': self.device_id,
|
||||
'device_name': self.device_name,
|
||||
'manufacturer': self.manufacturer,
|
||||
'confidence': self.confidence,
|
||||
'match_method': self.match_method,
|
||||
'match_details': self.match_details
|
||||
}
|
||||
|
||||
|
||||
class SignatureMatcher:
|
||||
"""
|
||||
Main signature matching engine
|
||||
|
||||
Coordinates multiple matching strategies to identify devices
|
||||
from RF signal metadata
|
||||
"""
|
||||
|
||||
def __init__(self, database):
|
||||
"""
|
||||
Initialize matcher with database connection
|
||||
|
||||
Args:
|
||||
database: Database connection for signature queries
|
||||
"""
|
||||
self.db = database
|
||||
self.strategies = []
|
||||
|
||||
def add_strategy(self, strategy):
|
||||
"""Add a matching strategy"""
|
||||
self.strategies.append(strategy)
|
||||
|
||||
def match(self, metadata: SignalMetadata, max_results: int = 10) -> List[MatchResult]:
|
||||
"""
|
||||
Match signal metadata against signature database
|
||||
|
||||
Args:
|
||||
metadata: Parsed signal metadata
|
||||
max_results: Maximum number of results to return
|
||||
|
||||
Returns:
|
||||
List of MatchResult objects sorted by confidence
|
||||
"""
|
||||
all_matches = []
|
||||
|
||||
# Run all matching strategies
|
||||
for strategy in self.strategies:
|
||||
try:
|
||||
matches = strategy.match(metadata, self.db)
|
||||
all_matches.extend(matches)
|
||||
except Exception as e:
|
||||
logger.error(f"Strategy {strategy.__class__.__name__} failed: {e}")
|
||||
|
||||
# Deduplicate and sort
|
||||
unique_matches = self._deduplicate_matches(all_matches)
|
||||
sorted_matches = sorted(unique_matches, key=lambda x: x.confidence, reverse=True)
|
||||
|
||||
return sorted_matches[:max_results]
|
||||
|
||||
def _deduplicate_matches(self, matches: List[MatchResult]) -> List[MatchResult]:
|
||||
"""
|
||||
Deduplicate matches, keeping highest confidence for each device
|
||||
|
||||
Args:
|
||||
matches: List of match results
|
||||
|
||||
Returns:
|
||||
Deduplicated list
|
||||
"""
|
||||
device_map = {}
|
||||
|
||||
for match in matches:
|
||||
if match.device_id not in device_map:
|
||||
device_map[match.device_id] = match
|
||||
else:
|
||||
# Keep higher confidence match
|
||||
if match.confidence > device_map[match.device_id].confidence:
|
||||
device_map[match.device_id] = match
|
||||
|
||||
return list(device_map.values())
|
||||
|
||||
|
||||
class MatchStrategy:
|
||||
"""Base class for matching strategies"""
|
||||
|
||||
def match(self, metadata: SignalMetadata, db) -> List[MatchResult]:
|
||||
"""
|
||||
Match metadata against database
|
||||
|
||||
Args:
|
||||
metadata: Signal metadata
|
||||
db: Database connection
|
||||
|
||||
Returns:
|
||||
List of MatchResult objects
|
||||
"""
|
||||
raise NotImplementedError
|
||||
Reference in New Issue
Block a user