Add architecture v2 with OCPI and GIREVE integration

This commit is contained in:
Eric F
2026-06-08 17:55:55 -04:00
parent 91506a6634
commit 11a51faffb
6 changed files with 907 additions and 0 deletions

View File

@@ -0,0 +1,93 @@
#!/usr/bin/env python3
"""Generate Cariflex architecture diagrams as PDF."""
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch, FancyArrowPatch
import matplotlib.patches as patches
fig, ax = plt.subplots(1, 1, figsize=(20, 14))
ax.set_xlim(0, 20)
ax.set_ylim(0, 14)
ax.axis('off')
fig.patch.set_facecolor('#f5f5f5')
def draw_box(ax, x, y, w, h, text, color='#2196F3', textcolor='white', fontsize=9, alpha=1.0):
box = FancyBboxPatch((x, y), w, h, boxstyle="round,pad=0.1",
facecolor=color, edgecolor='white', linewidth=2, alpha=alpha)
ax.add_patch(box)
ax.text(x + w/2, y + h/2, text, ha='center', va='center',
fontsize=fontsize, color=textcolor, fontweight='bold', wrap=True)
def draw_arrow(ax, x1, y1, x2, y2, color='#666', label=''):
ax.annotate('', xy=(x2, y2), xytext=(x1, y1),
arrowprops=dict(arrowstyle='->', color=color, lw=1.5))
if label:
mx, my = (x1+x2)/2, (y1+y2)/2
ax.text(mx+0.1, my+0.1, label, fontsize=7, color=color)
# Title
ax.text(10, 13.5, 'CARIFLEX EMS - Architecture d\'Intégration',
ha='center', fontsize=16, fontweight='bold', color='#1565C0')
# External Layer
ax.text(0.5, 12.5, 'EXTERNAL', fontsize=10, fontweight='bold', color='#666')
draw_box(ax, 0.5, 11.5, 4, 0.8, 'EPEX SPOT\nDay-ahead Prices', '#FF9800')
draw_box(ax, 5, 11.5, 4, 0.8, 'ENTSO-E\nCO2 / Prices', '#FF9800')
draw_box(ax, 9.5, 11.5, 4, 0.8, 'Weather API\nSolcast/Météo', '#FF9800')
draw_box(ax, 14, 11.5, 5, 0.8, 'DSO/TSO\nOpenADR / S2', '#FF9800')
# Arrows to FM
draw_arrow(ax, 2.5, 11.5, 5, 10.5, '#666', 'Prices')
draw_arrow(ax, 7, 11.5, 7, 10.5, '#666', 'CO2')
draw_arrow(ax, 11.5, 11.5, 9, 10.5, '#666', 'Irradiance')
draw_arrow(ax, 16.5, 11.5, 15, 10.5, '#666', 'Flex Requests')
# FlexMeasures EMS (central)
draw_box(ax, 3, 8.5, 14, 1.8, '', '#2196F3', alpha=0.2)
ax.text(10, 9.8, 'FLEXMEASURES EMS', ha='center', fontsize=12, fontweight='bold', color='#1565C0')
draw_box(ax, 3.5, 8.7, 3, 0.8, 'INGESTION\n• Sensors • Assets • Beliefs', '#2196F3', fontsize=7)
draw_box(ax, 7, 8.7, 3, 0.8, 'FORECASTING\n• PV • Load • Prices', '#2196F3', fontsize=7)
draw_box(ax, 10.5, 8.7, 3, 0.8, 'SCHEDULING\n• Batteries • EVs • Grid', '#2196F3', fontsize=7)
draw_box(ax, 14, 8.7, 2.5, 0.8, 'REPORTING\n• Assets • Schedules', '#2196F3', fontsize=7)
# Device Layer
ax.text(0.5, 7.8, 'INTEGRATION', fontsize=10, fontweight='bold', color='#666')
draw_box(ax, 0.5, 6.5, 5, 1, 'CITRINEOS (CSMS)\n• OCPP 2.0.1 • Charge Points • Transactions', '#4CAF50', fontsize=8)
draw_box(ax, 6, 6.5, 5, 1, 'EVEREST (Middleware)\n• ISO 15118 • Smart Charging • Power Mgmt', '#FF9800', fontsize=8)
draw_box(ax, 11.5, 6.5, 5, 1, 'OPENLEADR (VEN)\n• OpenADR 2.0b • DSO Signals • Flexibility', '#9C27B0', fontsize=8)
# Arrows to devices
draw_arrow(ax, 3, 6.5, 3, 5.5, '#4CAF50', 'OCPP')
draw_arrow(ax, 8.5, 6.5, 8.5, 5.5, '#FF9800', 'ISO 15118')
draw_arrow(ax, 14, 6.5, 14, 5.5, '#9C27B0', 'Commands')
# Devices
ax.text(0.5, 5.8, 'DEVICES', fontsize=10, fontweight='bold', color='#666')
draw_box(ax, 0.5, 4.5, 3.5, 1, '10 PV Panels\n5kWc each\nModbus TCP', '#FFC107', fontsize=8, textcolor='#333')
draw_box(ax, 4.5, 4.5, 3.5, 1, '10 Batteries\n100kWh each\nModbus TCP', '#FFC107', fontsize=8, textcolor='#333')
draw_box(ax, 8.5, 4.5, 3.5, 1, '10 EV Chargers\n22kW each\nOCPP 2.0.1', '#FFC107', fontsize=8, textcolor='#333')
draw_box(ax, 12.5, 4.5, 4, 1, '10 EVs\n75kWh V2G\nISO 15118', '#FFC107', fontsize=8, textcolor='#333')
# Market Layer
ax.text(0.5, 3.8, 'MARKET (R&D)', fontsize=10, fontweight='bold', color='#666')
draw_box(ax, 0.5, 2.5, 5.5, 1, 'GRID SINGULARITY\ngsy-e (88 stars)\n• P2P Trading • Market Clearing', '#00BCD4', fontsize=8)
draw_box(ax, 6.5, 2.5, 5.5, 1, 'OPLEM\npip install oplem\n• Local Market • Prosumer Opt.', '#00BCD4', fontsize=8)
draw_box(ax, 12.5, 2.5, 5.5, 1, 'HAMLET\nconda hamlet\n• Multi-agent • Simulation', '#00BCD4', fontsize=8)
# Visualization Layer
ax.text(0.5, 1.8, 'VISUALIZATION', fontsize=10, fontweight='bold', color='#666')
draw_box(ax, 0.5, 0.5, 4, 1, 'GRAFANA\n• 4 Timeseries • 1 Table\nPostgreSQL DS', '#E91E63', fontsize=8)
draw_box(ax, 5, 0.5, 4, 1, 'METABASE\n• SQL Analytics\n• Reporting', '#E91E63', fontsize=8)
draw_box(ax, 9.5, 0.5, 5, 1, 'CARIFLEX UI\n(FM Frontend)\n• Asset Map • Schedules', '#E91E63', fontsize=8)
# Legend
ax.text(15, 1, 'Cariflex - Caribbean Flexibility Platform',
ha='center', fontsize=8, fontweight='bold', color='#1565C0',
bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
plt.tight_layout()
plt.savefig('/home/eric/cariflex/docs/architecture_cariflex.pdf', dpi=150, bbox_inches='tight')
plt.savefig('/home/eric/cariflex/docs/architecture_cariflex.png', dpi=150, bbox_inches='tight')
print("✅ Architecture diagrams generated")
print(" - PDF: /home/eric/cariflex/docs/architecture_cariflex.pdf")
print(" - PNG: /home/eric/cariflex/docs/architecture_cariflex.png")

View File

@@ -0,0 +1,146 @@
#!/usr/bin/env python3
"""Generate Cariflex architecture diagrams v2 (with OCPI and GIREVE) as PDF."""
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch
import numpy as np
fig, ax = plt.subplots(1, 1, figsize=(22, 16))
ax.set_xlim(0, 22)
ax.set_ylim(0, 16)
ax.axis('off')
fig.patch.set_facecolor('#f8f9fa')
def draw_box(ax, x, y, w, h, text, color='#2196F3', textcolor='white', fontsize=8, alpha=1.0, border='white'):
box = FancyBboxPatch((x, y), w, h, boxstyle="round,pad=0.15",
facecolor=color, edgecolor=border, linewidth=1.5, alpha=alpha)
ax.add_patch(box)
ax.text(x + w/2, y + h/2, text, ha='center', va='center',
fontsize=fontsize, color=textcolor, fontweight='bold', wrap=True,
multialignment='center')
def draw_arrow(ax, x1, y1, x2, y2, color='#666', label='', lw=1.2):
ax.annotate('', xy=(x2, y2), xytext=(x1, y1),
arrowprops=dict(arrowstyle='->', color=color, lw=lw))
if label:
mx, my = (x1+x2)/2, (y1+y2)/2
ax.text(mx+0.1, my+0.1, label, fontsize=6, color=color, style='italic')
# Title
ax.text(11, 15.5, 'CARIFLEX EMS - Architecture d\'Intégration v2',
ha='center', fontsize=18, fontweight='bold', color='#1565C0')
ax.text(11, 15.1, 'avec OCPI et GIREVE',
ha='center', fontsize=12, color='#666')
# ═══════════════════════════════════════════════════════════════════
# EXTERNAL LAYER
# ═══════════════════════════════════════════════════════════════════
ax.text(0.3, 14.3, 'EXTERNAL', fontsize=9, fontweight='bold', color='#888', style='italic')
draw_box(ax, 0.3, 13.3, 4.5, 0.8, 'EPEX SPOT\nDay-ahead / Localflex', '#FF9800', fontsize=8)
draw_box(ax, 5.3, 13.3, 4.5, 0.8, 'ENTSO-E\nPrices / CO2', '#FF9800', fontsize=8)
draw_box(ax, 10.3, 13.3, 4.5, 0.8, 'Weather API\nSolcast / Météo', '#FF9800', fontsize=8)
draw_box(ax, 15.3, 13.3, 6, 0.8, 'DSO / TSO\nOpenADR / S2', '#FF9800', fontsize=8)
# Arrows to interop/integration
draw_arrow(ax, 2.5, 13.3, 3, 12.5, '#FF9800', 'prices')
draw_arrow(ax, 7.5, 13.3, 8, 12.5, '#FF9800', 'CO2')
draw_arrow(ax, 12.5, 13.3, 10, 12.5, '#FF9800', 'weather')
draw_arrow(ax, 18.3, 13.3, 17, 12.5, '#FF9800', 'flex')
# ═══════════════════════════════════════════════════════════════════
# INTEROPERABILITY LAYER (NEW)
# ═══════════════════════════════════════════════════════════════════
ax.text(0.3, 12.7, 'INTEROPERABILITY', fontsize=9, fontweight='bold', color='#888', style='italic')
draw_box(ax, 0.3, 11.5, 9, 1, 'OCPI Gateway (eMSP/CPO)\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n• Tarifs • Sessions • CDRs • Roaming (in/out) • Commands', '#E91E63', fontsize=7.5)
draw_box(ax, 10, 11.5, 9, 1, 'GIREVE Connector\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n• Session data • Roaming hub • Billing • PNIRE • CDRs', '#795548', fontsize=7.5)
# Arrows from external
draw_arrow(ax, 5.3, 11.5, 5.3, 10.8, '#E91E63', 'roaming')
draw_arrow(ax, 14.5, 11.5, 12, 10.8, '#795548', 'sessions')
# ═══════════════════════════════════════════════════════════════════
# INTEGRATION LAYER
# ═══════════════════════════════════════════════════════════════════
ax.text(0.3, 11.0, 'INTEGRATION', fontsize=9, fontweight='bold', color='#888', style='italic')
# FlexMeasures EMS (central)
draw_box(ax, 2, 9.2, 16, 1.5, '', '#2196F3', alpha=0.15)
ax.text(10, 10.4, 'FLEXMEASURES EMS', ha='center', fontsize=11, fontweight='bold', color='#1565C0')
draw_box(ax, 2.3, 9.4, 3.5, 0.9, 'INGESTION\nSensors / Assets\nBeliefs / OCPI CDRs', '#2196F3', fontsize=7)
draw_box(ax, 6.2, 9.4, 3.5, 0.9, 'FORECASTING\nPV / Load\nPrices / CO2', '#2196F3', fontsize=7)
draw_box(ax, 10.1, 9.4, 3.5, 0.9, 'SCHEDULING\nBatteries / EVs\nGrid services', '#2196F3', fontsize=7)
draw_box(ax, 14, 9.4, 3.7, 0.9, 'REPORTING\nAssets / Sched\nForecasts / CDRs', '#2196F3', fontsize=7)
# Sub-components
draw_box(ax, 0.3, 8.2, 5, 0.8, 'CITRINEOS (CSMS)\nOCPP 2.0.1 • Charge Points • Transactions', '#4CAF50', fontsize=7.5)
draw_box(ax, 5.8, 8.2, 5, 0.8, 'EVEREST (Middleware)\nISO 15118 • Smart Charging • V2G', '#FF9800', fontsize=7.5)
draw_box(ax, 11.3, 8.2, 5, 0.8, 'OPENLEADR (VEN)\nOpenADR 2.0b • DSO • Flexibility', '#9C27B0', fontsize=7.5)
# Arrows to FM
draw_arrow(ax, 2.8, 8.2, 4, 9.2, '#4CAF50', 'REST')
draw_arrow(ax, 8.3, 8.2, 8, 9.2, '#FF9800', 'OCPP')
draw_arrow(ax, 13.8, 8.2, 12, 9.2, '#9C27B0', 'REST')
# ═══════════════════════════════════════════════════════════════════
# DEVICES LAYER
# ═══════════════════════════════════════════════════════════════════
ax.text(0.3, 7.5, 'DEVICES', fontsize=9, fontweight='bold', color='#888', style='italic')
draw_box(ax, 0.3, 6.3, 4.5, 1, '10 PV Panels\n5kWc each\nModbus TCP → FM', '#FFC107', textcolor='#333', fontsize=8)
draw_box(ax, 5.3, 6.3, 4.5, 1, '10 Batteries\n100kWh / 50kW\nModbus TCP → FM', '#FFC107', textcolor='#333', fontsize=8)
draw_box(ax, 10.3, 6.3, 4.5, 1, '10 EV Chargers\n22kW OCPP 2.0.1\n→ CitrineOS', '#FFC107', textcolor='#333', fontsize=8)
draw_box(ax, 15.3, 6.3, 5.5, 1, '10 EVs\n75kWh V2G\nISO 15118 → EVerest', '#FFC107', textcolor='#333', fontsize=8)
# ═══════════════════════════════════════════════════════════════════
# MARKET LAYER (R&D)
# ═══════════════════════════════════════════════════════════════════
ax.text(0.3, 5.6, 'MARKET (R&D)', fontsize=9, fontweight='bold', color='#888', style='italic')
draw_box(ax, 0.3, 4.4, 6.5, 1, 'GRID SINGULARITY\ngsy-e • P2P Trading • Market Clearing\nPrice discovery → FM schedules', '#00BCD4', fontsize=7.5)
draw_box(ax, 7.3, 4.4, 6.5, 1, 'OPLEM\nLocal Market • Prosumer Opt.\nBattery/EV agents → FM', '#00BCD4', fontsize=7.5)
draw_box(ax, 14.3, 4.4, 6.5, 1, 'HAMLET\nMulti-agent Simulation\nStrategy validation → FM', '#00BCD4', fontsize=7.5)
# ═══════════════════════════════════════════════════════════════════
# VISUALIZATION LAYER
# ═══════════════════════════════════════════════════════════════════
ax.text(0.3, 3.7, 'VISUALIZATION', fontsize=9, fontweight='bold', color='#888', style='italic')
draw_box(ax, 0.3, 2.5, 6, 1, 'GRAFANA (Port 3001)\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n4 Timeseries + 1 Table\nPostgreSQL-FlexMeasures DS', '#E91E63', fontsize=7.5)
draw_box(ax, 6.8, 2.5, 6, 1, 'METABASE\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nSQL Analytics • Reporting\nBilling • Data exploration', '#E91E63', fontsize=7.5)
draw_box(ax, 13.3, 2.5, 7, 1, 'CARIFLEX UI (Port 5000)\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nAsset Map • Schedules\nForecasts • OCPI Sessions', '#E91E63', fontsize=7.5)
# ═══════════════════════════════════════════════════════════════════
# NETWORK LEGEND
# ═══════════════════════════════════════════════════════════════════
ax.text(0.3, 1.8, 'PROTOCOLS:', fontsize=8, fontweight='bold', color='#666')
protocols = [
('OCPP 2.0.1', '#4CAF50'),
('ISO 15118', '#FF9800'),
('OCPI 2.2', '#E91E63'),
('OpenADR', '#9C27B0'),
('Modbus', '#FFC107'),
('REST API', '#2196F3'),
('GIREVE', '#795548'),
]
for i, (name, color) in enumerate(protocols):
x = 0.3 + i * 2.8
rect = plt.Rectangle((x, 1.1), 0.3, 0.4, facecolor=color, edgecolor='white')
ax.add_patch(rect)
ax.text(x + 0.5, 1.3, name, fontsize=7, color='#333')
# Footer
ax.text(11, 0.5, 'Cariflex - Caribbean Flexibility Platform - 2026',
ha='center', fontsize=10, fontweight='bold', color='#1565C0',
bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
plt.tight_layout()
plt.savefig('/home/eric/cariflex/docs/architecture_cariflex_v2.pdf', dpi=150, bbox_inches='tight')
plt.savefig('/home/eric/cariflex/docs/architecture_cariflex_v2.png', dpi=150, bbox_inches='tight')
print("✅ Architecture v2 diagrams generated")
print(" - PDF: /home/eric/cariflex/docs/architecture_cariflex_v2.pdf")
print(" - PNG: /home/eric/cariflex/docs/architecture_cariflex_v2.png")