94 lines
5.0 KiB
Python
94 lines
5.0 KiB
Python
#!/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")
|