Add architecture v2 with OCPI and GIREVE integration
This commit is contained in:
93
scripts/generate_architecture_diagram.py
Normal file
93
scripts/generate_architecture_diagram.py
Normal 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")
|
||||
146
scripts/generate_architecture_v2.py
Normal file
146
scripts/generate_architecture_v2.py
Normal 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")
|
||||
Reference in New Issue
Block a user