|
|
@@ -0,0 +1,105 @@
|
|
|
+import asyncio
|
|
|
+import logging
|
|
|
+from typing import Dict, List
|
|
|
+from datetime import datetime
|
|
|
+from models.battery import Battery
|
|
|
+from models.device import Device
|
|
|
+from services.i2c_service import I2CService
|
|
|
+from services.http_service import HTTPService
|
|
|
+from utils.health_calculator import calculate_health
|
|
|
+
|
|
|
+logger = logging.getLogger(__name__)
|
|
|
+
|
|
|
+class MeasurementController:
|
|
|
+ """Controls the measurement process for multiple devices and slots."""
|
|
|
+
|
|
|
+ def __init__(self, config: dict, i2c_service: I2CService, http_service: HTTPService):
|
|
|
+ self.config = config
|
|
|
+ self.i2c_service = i2c_service
|
|
|
+ self.http_service = http_service
|
|
|
+ self.active_measurements: Dict[str, Dict[int, asyncio.Task]] = {}
|
|
|
+
|
|
|
+ async def start_measurement(self, device_id: str, slot: int, cell_id: str):
|
|
|
+ """Start measurement cycle for a specific slot."""
|
|
|
+ if device_id not in self.active_measurements:
|
|
|
+ self.active_measurements[device_id] = {}
|
|
|
+
|
|
|
+ # Cancel existing measurement if any
|
|
|
+ if slot in self.active_measurements[device_id]:
|
|
|
+ self.active_measurements[device_id][slot].cancel()
|
|
|
+
|
|
|
+ # Create new measurement task
|
|
|
+ task = asyncio.create_task(
|
|
|
+ self._measure_cycle(device_id, slot, cell_id)
|
|
|
+ )
|
|
|
+ self.active_measurements[device_id][slot] = task
|
|
|
+
|
|
|
+ async def _measure_cycle(self, device_id: str, slot: int, cell_id: str):
|
|
|
+ """Execute measurement cycles for a battery."""
|
|
|
+ try:
|
|
|
+ # Get battery info from HTTP service
|
|
|
+ battery_info = await self.http_service.get_cell_info(cell_id)
|
|
|
+
|
|
|
+ measurements = []
|
|
|
+ cycles = self.config['measurement']['cycles']
|
|
|
+ sample_rate = self.config['measurement']['sample_rate_hz']
|
|
|
+
|
|
|
+ for cycle in range(cycles):
|
|
|
+ logger.info(f"Starting cycle {cycle+1}/{cycles} for cell {cell_id}")
|
|
|
+ cycle_measurements = []
|
|
|
+
|
|
|
+ while True:
|
|
|
+ # Read measurements
|
|
|
+ voltage = await self.i2c_service.read_voltage(device_id, slot)
|
|
|
+ current = await self.i2c_service.read_current(device_id, slot)
|
|
|
+ temp = await self.i2c_service.read_temperature(device_id, slot)
|
|
|
+
|
|
|
+ # Check safety limits
|
|
|
+ if not self._check_safety_limits(voltage, temp):
|
|
|
+ raise Exception("Safety limits exceeded")
|
|
|
+
|
|
|
+ cycle_measurements.append({
|
|
|
+ 'timestamp': datetime.now().isoformat(),
|
|
|
+ 'voltage': voltage,
|
|
|
+ 'current': current,
|
|
|
+ 'temperature': temp
|
|
|
+ })
|
|
|
+
|
|
|
+ # Check if cycle complete
|
|
|
+ if self._is_cycle_complete(cycle_measurements):
|
|
|
+ break
|
|
|
+
|
|
|
+ await asyncio.sleep(1/sample_rate)
|
|
|
+
|
|
|
+ measurements.extend(cycle_measurements)
|
|
|
+
|
|
|
+ # Rest period between cycles
|
|
|
+ await asyncio.sleep(self.config['measurement']['rest_time_minutes'] * 60)
|
|
|
+
|
|
|
+ # Calculate health
|
|
|
+ health = calculate_health([m['current'] for m in measurements])
|
|
|
+
|
|
|
+ logger.info(f"Measurement complete for cell {cell_id}. Health: {health}%")
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"Error during measurement: {str(e)}")
|
|
|
+ finally:
|
|
|
+ # Cleanup
|
|
|
+ if device_id in self.active_measurements and \
|
|
|
+ slot in self.active_measurements[device_id]:
|
|
|
+ del self.active_measurements[device_id][slot]
|
|
|
+
|
|
|
+ def _check_safety_limits(self, voltage: float, temperature: float) -> bool:
|
|
|
+ """Check if measurements are within safety limits."""
|
|
|
+ return (self.config['measurement']['min_voltage'] <= voltage <=
|
|
|
+ self.config['measurement']['max_voltage'] and
|
|
|
+ temperature <= self.config['measurement']['max_temperature_c'])
|
|
|
+
|
|
|
+ def _is_cycle_complete(self, measurements: List[dict]) -> bool:
|
|
|
+ """Determine if a measurement cycle is complete."""
|
|
|
+ if not measurements:
|
|
|
+ return False
|
|
|
+
|
|
|
+ # Add your cycle completion logic here
|
|
|
+ # Example: Check if voltage has reached upper limit and then lower limit
|
|
|
+ return len(measurements) > 100 # Simplified example
|