#!/usr/bin/env python3 """ Cariflex Simulator - Publishes simulated EV charging data to Redis. Simulates 40 assets: 10 PV, 10 Battery, 10 EV Charger, 10 EV V2G """ import redis import json import time import random import math from datetime import datetime, timezone # Redis connection r = redis.Redis(host='flexmeasures-redis', port=6379, db=0, decode_responses=True) # Asset configurations ASSETS = { # PV panels (production) "pv_{:02d}": {"type": "pv", "unit": "kW", "min": 0, "max": 5, "base": 2.5}, # Batteries (storage) "bat_{:02d}": {"type": "battery", "unit": "kWh", "min": 10, "max": 100, "base": 50}, # EV Chargers (consumption) "chg_{:02d}": {"type": "ev_charger", "unit": "kW", "min": 0, "max": 22, "base": 11}, # EVs (V2G - bidirectional) "ev_{:02d}": {"type": "ev_v2g", "unit": "kW", "min": -11, "max": 11, "base": 0}, } def generate_value(asset_config, hour): """Generate a realistic value based on asset type and time of day.""" cfg = asset_config base = cfg["base"] if cfg["type"] == "pv": # Solar production: peaks at noon solar_factor = max(0, math.sin((hour - 6) * math.pi / 12)) if 6 <= hour <= 18 else 0 noise = random.gauss(0, 0.5) value = base * solar_factor * 2 + noise elif cfg["type"] == "battery": # SOC: slowly varies throughout the day variation = 20 * math.sin(hour * math.pi / 12) noise = random.gauss(0, 3) value = base + variation + noise elif cfg["type"] == "ev_charger": # Charging: more active during day and evening if 8 <= hour <= 22: factor = random.uniform(0.3, 1.0) else: factor = random.uniform(0, 0.2) noise = random.gauss(0, 1) value = cfg["max"] * factor + noise elif cfg["type"] == "ev_v2g": # V2G: charges at night, discharges during peak if 0 <= hour <= 6: factor = random.uniform(0.3, 0.8) # charging elif 17 <= hour <= 21: factor = random.uniform(-0.6, -0.2) # discharging else: factor = random.uniform(-0.1, 0.1) noise = random.gauss(0, 0.5) value = cfg["max"] * factor + noise else: value = base + random.gauss(0, 1) return round(max(cfg["min"], min(cfg["max"], value)), 2) def main(): print("🚗 Cariflex Simulator - Publishing to Redis") print(f" Assets: 40 (10 PV, 10 Bat, 10 Chg, 10 EV)") print(f" Redis: flexmeasures-redis:6379/0") print() # Test Redis connection try: r.ping() print("✅ Redis connected") except redis.ConnectionError: print("❌ Redis connection failed") return # Publish loop iteration = 0 while True: now = datetime.now(timezone.utc) hour = now.hour timestamp = now.isoformat() # Publish each asset's data for template, cfg in ASSETS.items(): for i in range(1, 11): asset_id = template.format(i) value = generate_value(cfg, hour) # Create data packet data = { "asset_id": asset_id, "type": cfg["type"], "value": value, "unit": cfg["unit"], "timestamp": timestamp, "iteration": iteration } # Publish to Redis (list per asset) key = f"cariflex:asset:{asset_id}" r.lpush(key, json.dumps(data)) r.ltrim(key, 0, 99) # Keep last 100 values r.expire(key, 3600) # 1h TTL # Also publish to a pub/sub channel r.publish("cariflex:data", json.dumps(data)) # Publish aggregate data aggregate = { "timestamp": timestamp, "total_pv_kw": sum(generate_value({"type": "pv", "base": 2.5, "min": 0, "max": 5}, hour) for _ in range(10)), "total_battery_soc": sum(generate_value({"type": "battery", "base": 50, "min": 10, "max": 100}, hour) for _ in range(10)) / 10, "total_charger_kw": sum(generate_value({"type": "ev_charger", "base": 11, "min": 0, "max": 22}, hour) for _ in range(10)), "total_ev_v2g_kw": sum(generate_value({"type": "ev_v2g", "base": 0, "min": -11, "max": 11}, hour) for _ in range(10)), "flexibility_available_kw": 0 # Will be calculated } aggregate["flexibility_available_kw"] = round( abs(aggregate["total_ev_v2g_kw"]) + abs(aggregate["total_charger_kw"] * 0.3) + # 30% of charger can be modulated abs(aggregate["total_battery_soc"] * 0.5), # 50% of battery capacity 2 ) r.set("cariflex:aggregate", json.dumps(aggregate), ex=300) iteration += 1 if iteration % 10 == 0: print(f" 📊 Iteration {iteration}: published {40} assets to Redis") time.sleep(10) # Publish every 10 seconds if __name__ == "__main__": main()