#!/usr/bin/env python3 """ Cariflex - Automatic Scheduling Service Creates battery/EV schedules based on OpenADR price signals and flexibility events. """ import json, logging, os, sys from datetime import datetime, timezone, timedelta # Add FM path sys.path.insert(0, '/app') logging.basicConfig(level=logging.INFO) logger = logging.getLogger("cariflex-scheduling") # FM imports from flexmeasures.data.models.planning.storage import StorageScheduler from flexmeasures.data.services.scheduling import make_schedule from flexmeasures.data.models.generic_asset import GenericAsset from flexmeasures.data.models.sensor import Sensor from flexmeasures.data import db # Scheduling config SCHEDULING_CONFIG = { "battery_sensors": list(range(51, 61)), # Bat_01 to Bat_10 "ev_sensors": list(range(61, 71)), # EV_01 to EV_10 "price_sensor": 84, # consumption_price (OpenADR) "load_control_sensor": 86, # load_control_signal (OpenADR) "demand_response_sensor": 87, # demand_response_signal (OpenADR) "planning_horizon_hours": 24, "resolution_minutes": 15, } def get_sensor_data(sensor_id, start, end): """Get sensor data from FM database.""" sensor = db.session.query(Sensor).filter(Sensor.id == sensor_id).first() if not sensor: return None beliefs = db.session.query( TimedBelief.event_start, TimedBelief.event_value ).filter( TimedBelief.sensor_id == sensor_id, TimedBelief.event_start >= start, TimedBelief.event_start <= end ).order_by(TimedBelief.event_start).all() return beliefs def create_battery_schedule(sensor_id, start_time, duration_hours=24): """Create a schedule for a battery based on price signals.""" from flexmeasures.data.models.sensor import Sensor from flexmeasures.data.models.generic_asset import GenericAsset sensor = db.session.query(Sensor).filter(Sensor.id == sensor_id).first() if not sensor: logger.warning(f"Sensor {sensor_id} not found") return None asset = db.session.query(GenericAsset).filter(GenericAsset.id == sensor.generic_asset_id).first() if not asset: logger.warning(f"Asset for sensor {sensor_id} not found") return None logger.info(f"Creating schedule for {asset.name} (sensor {sensor_id})") # Get price data end_time = start_time + timedelta(hours=duration_hours) price_sensor = db.session.query(Sensor).filter(Sensor.id == SCHEDULING_CONFIG["price_sensor"]).first() if not price_sensor: logger.warning("Price sensor not found") return None # Create schedule using FM's built-in scheduler try: schedule = make_schedule( sensor=sensor, start=start_time, end=end_time, resolution=timedelta(minutes=SCHEDULING_CONFIG["resolution_minutes"]), flex_model={ "soc-min": "0.1 MWh", "soc-max": "1 MWh", "power-capacity": "0.05 MW", "charging-efficiency": "0.95", "discharging-efficiency": "0.95", } ) logger.info(f"Schedule created for {asset.name}: {len(schedule) if schedule else 0} points") return schedule except Exception as e: logger.error(f"Scheduling error for {asset.name}: {e}") return None def run_scheduling(): """Run scheduling for all batteries and EVs.""" logger.info("Starting automatic scheduling...") now = datetime.now(timezone.utc) start_time = now.replace(minute=0, second=0, microsecond=0) # Schedule batteries for sensor_id in SCHEDULING_CONFIG["battery_sensors"]: try: create_battery_schedule(sensor_id, start_time) except Exception as e: logger.error(f"Error scheduling battery {sensor_id}: {e}") # Schedule EVs for sensor_id in SCHEDULING_CONFIG["ev_sensors"]: try: create_battery_schedule(sensor_id, start_time) except Exception as e: logger.error(f"Error scheduling EV {sensor_id}: {e}") logger.info("Scheduling complete") if __name__ == "__main__": # Run scheduling every 15 minutes import time while True: try: run_scheduling() except Exception as e: logger.error(f"Scheduling error: {e}") time.sleep(900) # 15 minutes