Simulatore dell'impianto
This commit is contained in:
891
pvsim/plant.py
Normal file
891
pvsim/plant.py
Normal file
@@ -0,0 +1,891 @@
|
|||||||
|
```python
|
||||||
|
"""
|
||||||
|
plant.py
|
||||||
|
|
||||||
|
Modello gerarchico completo dell'impianto fotovoltaico.
|
||||||
|
|
||||||
|
Gerarchia:
|
||||||
|
|
||||||
|
PVPlant
|
||||||
|
|
|
||||||
|
+-- Inverter
|
||||||
|
| |
|
||||||
|
| +-- CombinerBox
|
||||||
|
| |
|
||||||
|
| +-- PVPanel
|
||||||
|
| +-- PVPanel
|
||||||
|
| +-- ...
|
||||||
|
|
|
||||||
|
+-- Inverter
|
||||||
|
|
|
||||||
|
+-- CombinerBox
|
||||||
|
|
|
||||||
|
+-- PVPanel
|
||||||
|
|
||||||
|
Il modulo costruisce automaticamente l'intero impianto
|
||||||
|
a partire dal file di configurazione plant.json.
|
||||||
|
|
||||||
|
Responsabilità:
|
||||||
|
|
||||||
|
- creazione della gerarchia;
|
||||||
|
- gestione degli inverter;
|
||||||
|
- gestione delle Combiner Box;
|
||||||
|
- creazione automatica dei pannelli;
|
||||||
|
- aggiornamento dell'intero impianto;
|
||||||
|
- aggregazione della potenza;
|
||||||
|
- aggregazione dell'energia;
|
||||||
|
- calcolo della disponibilità;
|
||||||
|
- diagnostica della struttura.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Dict, List, Any
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from .panel import PVPanel
|
||||||
|
from .combiner import CombinerBox
|
||||||
|
from .inverter import Inverter
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PVPlant:
|
||||||
|
"""
|
||||||
|
Rappresenta l'intero impianto fotovoltaico.
|
||||||
|
"""
|
||||||
|
|
||||||
|
plant_id: str
|
||||||
|
|
||||||
|
inverters: List[Inverter] = field(
|
||||||
|
default_factory=list
|
||||||
|
)
|
||||||
|
|
||||||
|
total_energy_Wh: float = 0.0
|
||||||
|
|
||||||
|
total_dc_energy_Wh: float = 0.0
|
||||||
|
|
||||||
|
total_ac_energy_Wh: float = 0.0
|
||||||
|
|
||||||
|
# Numero di timestep elaborati
|
||||||
|
timestep_count: int = 0
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Costruzione da configurazione
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_config(
|
||||||
|
cls,
|
||||||
|
config: Dict[str, Any]
|
||||||
|
) -> "PVPlant":
|
||||||
|
"""
|
||||||
|
Crea un impianto completo leggendo la configurazione.
|
||||||
|
|
||||||
|
La configurazione deve avere la struttura:
|
||||||
|
|
||||||
|
layout
|
||||||
|
|
|
||||||
|
+-- inverters
|
||||||
|
|
|
||||||
|
+-- combiners
|
||||||
|
|
|
||||||
|
+-- strings
|
||||||
|
+-- panels_per_string
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
PVPlant
|
||||||
|
Impianto completamente costruito.
|
||||||
|
"""
|
||||||
|
|
||||||
|
plant_name = (
|
||||||
|
config[
|
||||||
|
"plant"
|
||||||
|
][
|
||||||
|
"name"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
plant = cls(
|
||||||
|
plant_id=plant_name
|
||||||
|
)
|
||||||
|
|
||||||
|
module_config = (
|
||||||
|
config[
|
||||||
|
"module"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
layout_config = (
|
||||||
|
config[
|
||||||
|
"layout"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
# --------------------------------------------------------------
|
||||||
|
# Creazione inverter
|
||||||
|
# --------------------------------------------------------------
|
||||||
|
|
||||||
|
for inverter_config in (
|
||||||
|
layout_config[
|
||||||
|
"inverters"
|
||||||
|
]
|
||||||
|
):
|
||||||
|
|
||||||
|
inverter = (
|
||||||
|
cls._create_inverter(
|
||||||
|
inverter_config,
|
||||||
|
module_config
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
plant.inverters.append(
|
||||||
|
inverter
|
||||||
|
)
|
||||||
|
|
||||||
|
return plant
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_json(
|
||||||
|
cls,
|
||||||
|
config_path: str | Path
|
||||||
|
) -> "PVPlant":
|
||||||
|
"""
|
||||||
|
Carica la configurazione da un file JSON
|
||||||
|
e costruisce l'impianto.
|
||||||
|
"""
|
||||||
|
|
||||||
|
config_path = Path(
|
||||||
|
config_path
|
||||||
|
)
|
||||||
|
|
||||||
|
if not config_path.exists():
|
||||||
|
|
||||||
|
raise FileNotFoundError(
|
||||||
|
f"Configurazione non trovata: "
|
||||||
|
f"{config_path}"
|
||||||
|
)
|
||||||
|
|
||||||
|
with open(
|
||||||
|
config_path,
|
||||||
|
"r",
|
||||||
|
encoding="utf-8"
|
||||||
|
) as file:
|
||||||
|
|
||||||
|
config = json.load(
|
||||||
|
file
|
||||||
|
)
|
||||||
|
|
||||||
|
return cls.from_config(
|
||||||
|
config
|
||||||
|
)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Creazione inverter
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _create_inverter(
|
||||||
|
inverter_config: Dict[str, Any],
|
||||||
|
module_config: Dict[str, Any]
|
||||||
|
) -> Inverter:
|
||||||
|
"""
|
||||||
|
Crea un inverter e tutte le relative Combiner Box.
|
||||||
|
"""
|
||||||
|
|
||||||
|
inverter = Inverter(
|
||||||
|
|
||||||
|
inverter_id=
|
||||||
|
inverter_config[
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
|
||||||
|
nominal_power_kW=
|
||||||
|
inverter_config[
|
||||||
|
"nominal_power_kW"
|
||||||
|
],
|
||||||
|
|
||||||
|
nominal_efficiency=
|
||||||
|
inverter_config[
|
||||||
|
"efficiency"
|
||||||
|
][
|
||||||
|
"nominal"
|
||||||
|
],
|
||||||
|
|
||||||
|
night_consumption_W=
|
||||||
|
inverter_config[
|
||||||
|
"efficiency"
|
||||||
|
][
|
||||||
|
"night_consumption_W"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
# --------------------------------------------------------------
|
||||||
|
# Creazione Combiner Box
|
||||||
|
# --------------------------------------------------------------
|
||||||
|
|
||||||
|
for combiner_config in (
|
||||||
|
inverter_config[
|
||||||
|
"combiners"
|
||||||
|
]
|
||||||
|
):
|
||||||
|
|
||||||
|
combiner = (
|
||||||
|
PVPlant._create_combiner(
|
||||||
|
combiner_config,
|
||||||
|
module_config
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
inverter.add_combiner(
|
||||||
|
combiner
|
||||||
|
)
|
||||||
|
|
||||||
|
return inverter
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Creazione Combiner Box
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _create_combiner(
|
||||||
|
combiner_config: Dict[str, Any],
|
||||||
|
module_config: Dict[str, Any]
|
||||||
|
) -> CombinerBox:
|
||||||
|
"""
|
||||||
|
Crea una Combiner Box e tutti i relativi pannelli.
|
||||||
|
|
||||||
|
La configurazione definisce:
|
||||||
|
|
||||||
|
strings = numero di stringhe
|
||||||
|
|
||||||
|
panels_per_string =
|
||||||
|
pannelli per ogni stringa
|
||||||
|
|
||||||
|
In questa versione la struttura delle stringhe
|
||||||
|
viene rappresentata logicamente creando tutti i pannelli
|
||||||
|
nella Combiner Box.
|
||||||
|
|
||||||
|
L'ID del pannello contiene:
|
||||||
|
|
||||||
|
Combiner ID
|
||||||
|
String ID
|
||||||
|
Panel ID
|
||||||
|
"""
|
||||||
|
|
||||||
|
combiner = CombinerBox(
|
||||||
|
|
||||||
|
combiner_id=
|
||||||
|
combiner_config[
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
strings = (
|
||||||
|
combiner_config[
|
||||||
|
"strings"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
panels_per_string = (
|
||||||
|
combiner_config[
|
||||||
|
"panels_per_string"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
# --------------------------------------------------------------
|
||||||
|
# Parametri pannello
|
||||||
|
# --------------------------------------------------------------
|
||||||
|
|
||||||
|
nominal_power = (
|
||||||
|
module_config[
|
||||||
|
"nominal_power_W"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
electrical = (
|
||||||
|
module_config[
|
||||||
|
"electrical"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
physical = (
|
||||||
|
module_config[
|
||||||
|
"physical"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
degradation = (
|
||||||
|
module_config[
|
||||||
|
"degradation"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
# --------------------------------------------------------------
|
||||||
|
# Creazione pannelli
|
||||||
|
# --------------------------------------------------------------
|
||||||
|
|
||||||
|
for string_number in range(
|
||||||
|
1,
|
||||||
|
strings + 1
|
||||||
|
):
|
||||||
|
|
||||||
|
for panel_number in range(
|
||||||
|
1,
|
||||||
|
panels_per_string + 1
|
||||||
|
):
|
||||||
|
|
||||||
|
panel_id = (
|
||||||
|
f"{combiner_config['id']}"
|
||||||
|
f"_STR_{string_number:02d}"
|
||||||
|
f"_PANEL_{panel_number:02d}"
|
||||||
|
)
|
||||||
|
|
||||||
|
panel = PVPanel(
|
||||||
|
|
||||||
|
panel_id=
|
||||||
|
panel_id,
|
||||||
|
|
||||||
|
nominal_power=
|
||||||
|
nominal_power,
|
||||||
|
|
||||||
|
area=
|
||||||
|
physical[
|
||||||
|
"area_m2"
|
||||||
|
],
|
||||||
|
|
||||||
|
vmp=
|
||||||
|
electrical[
|
||||||
|
"vmp_V"
|
||||||
|
],
|
||||||
|
|
||||||
|
imp=
|
||||||
|
electrical[
|
||||||
|
"imp_A"
|
||||||
|
],
|
||||||
|
|
||||||
|
voc=
|
||||||
|
electrical[
|
||||||
|
"voc_V"
|
||||||
|
],
|
||||||
|
|
||||||
|
isc=
|
||||||
|
electrical[
|
||||||
|
"isc_A"
|
||||||
|
],
|
||||||
|
|
||||||
|
temp_coeff=
|
||||||
|
physical[
|
||||||
|
"temperature_coefficient_power"
|
||||||
|
],
|
||||||
|
|
||||||
|
nominal_temp=
|
||||||
|
physical[
|
||||||
|
"nominal_temperature_C"
|
||||||
|
],
|
||||||
|
|
||||||
|
annual_degradation=
|
||||||
|
degradation[
|
||||||
|
"annual_rate"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
combiner.add_panel(
|
||||||
|
panel
|
||||||
|
)
|
||||||
|
|
||||||
|
return combiner
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Gestione inverter
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def add_inverter(
|
||||||
|
self,
|
||||||
|
inverter: Inverter
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Aggiunge un inverter all'impianto.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.inverters.append(
|
||||||
|
inverter
|
||||||
|
)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def get_inverter_count(
|
||||||
|
self
|
||||||
|
) -> int:
|
||||||
|
"""
|
||||||
|
Restituisce il numero di inverter.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return len(
|
||||||
|
self.inverters
|
||||||
|
)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def get_active_inverter_count(
|
||||||
|
self
|
||||||
|
) -> int:
|
||||||
|
"""
|
||||||
|
Restituisce il numero di inverter attivi.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return sum(
|
||||||
|
|
||||||
|
1
|
||||||
|
|
||||||
|
for inverter
|
||||||
|
in self.inverters
|
||||||
|
|
||||||
|
if inverter.enabled
|
||||||
|
)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Conteggi gerarchici
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def get_combiner_count(
|
||||||
|
self
|
||||||
|
) -> int:
|
||||||
|
"""
|
||||||
|
Restituisce il numero totale di Combiner Box.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return sum(
|
||||||
|
|
||||||
|
len(
|
||||||
|
inverter.combiners
|
||||||
|
)
|
||||||
|
|
||||||
|
for inverter
|
||||||
|
in self.inverters
|
||||||
|
)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def get_panel_count(
|
||||||
|
self
|
||||||
|
) -> int:
|
||||||
|
"""
|
||||||
|
Restituisce il numero totale di pannelli.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return sum(
|
||||||
|
|
||||||
|
len(
|
||||||
|
combiner.panels
|
||||||
|
)
|
||||||
|
|
||||||
|
for inverter
|
||||||
|
in self.inverters
|
||||||
|
|
||||||
|
for combiner
|
||||||
|
in inverter.combiners
|
||||||
|
)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def get_active_panel_count(
|
||||||
|
self
|
||||||
|
) -> int:
|
||||||
|
"""
|
||||||
|
Restituisce il numero di pannelli attivi.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return sum(
|
||||||
|
|
||||||
|
1
|
||||||
|
|
||||||
|
for inverter
|
||||||
|
in self.inverters
|
||||||
|
|
||||||
|
for combiner
|
||||||
|
in inverter.combiners
|
||||||
|
|
||||||
|
for panel
|
||||||
|
in combiner.panels
|
||||||
|
|
||||||
|
if panel.enabled
|
||||||
|
)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Aggiornamento impianto
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def update(
|
||||||
|
self,
|
||||||
|
timestamp: datetime,
|
||||||
|
irradiance: float,
|
||||||
|
ambient_temperature: float,
|
||||||
|
years_from_start: float,
|
||||||
|
timestep_minutes: float
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Esegue un timestep sull'intero impianto.
|
||||||
|
|
||||||
|
Flusso:
|
||||||
|
|
||||||
|
Plant
|
||||||
|
|
|
||||||
|
+-- Inverter
|
||||||
|
|
|
||||||
|
+-- Combiner
|
||||||
|
|
|
||||||
|
+-- Panel
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Dict
|
||||||
|
Dati aggregati dell'impianto.
|
||||||
|
"""
|
||||||
|
|
||||||
|
inverter_data = []
|
||||||
|
|
||||||
|
# --------------------------------------------------------------
|
||||||
|
# Aggiornamento inverter
|
||||||
|
# --------------------------------------------------------------
|
||||||
|
|
||||||
|
for inverter in self.inverters:
|
||||||
|
|
||||||
|
data = inverter.update(
|
||||||
|
|
||||||
|
timestamp=
|
||||||
|
timestamp,
|
||||||
|
|
||||||
|
irradiance=
|
||||||
|
irradiance,
|
||||||
|
|
||||||
|
ambient_temperature=
|
||||||
|
ambient_temperature,
|
||||||
|
|
||||||
|
years_from_start=
|
||||||
|
years_from_start,
|
||||||
|
|
||||||
|
timestep_minutes=
|
||||||
|
timestep_minutes
|
||||||
|
)
|
||||||
|
|
||||||
|
inverter_data.append(
|
||||||
|
data
|
||||||
|
)
|
||||||
|
|
||||||
|
# --------------------------------------------------------------
|
||||||
|
# Aggregazione potenza
|
||||||
|
# --------------------------------------------------------------
|
||||||
|
|
||||||
|
total_dc_power = sum(
|
||||||
|
|
||||||
|
data[
|
||||||
|
"dc_power_W"
|
||||||
|
]
|
||||||
|
|
||||||
|
for data
|
||||||
|
in inverter_data
|
||||||
|
)
|
||||||
|
|
||||||
|
total_ac_power = sum(
|
||||||
|
|
||||||
|
data[
|
||||||
|
"ac_power_W"
|
||||||
|
]
|
||||||
|
|
||||||
|
for data
|
||||||
|
in inverter_data
|
||||||
|
)
|
||||||
|
|
||||||
|
# --------------------------------------------------------------
|
||||||
|
# Aggregazione energia
|
||||||
|
# --------------------------------------------------------------
|
||||||
|
|
||||||
|
dc_energy = (
|
||||||
|
|
||||||
|
total_dc_power
|
||||||
|
|
||||||
|
* timestep_minutes
|
||||||
|
|
||||||
|
/ 60.0
|
||||||
|
)
|
||||||
|
|
||||||
|
ac_energy = (
|
||||||
|
|
||||||
|
total_ac_power
|
||||||
|
|
||||||
|
* timestep_minutes
|
||||||
|
|
||||||
|
/ 60.0
|
||||||
|
)
|
||||||
|
|
||||||
|
self.total_dc_energy_Wh += (
|
||||||
|
dc_energy
|
||||||
|
)
|
||||||
|
|
||||||
|
self.total_ac_energy_Wh += (
|
||||||
|
ac_energy
|
||||||
|
)
|
||||||
|
|
||||||
|
self.total_energy_Wh += (
|
||||||
|
ac_energy
|
||||||
|
)
|
||||||
|
|
||||||
|
self.timestep_count += 1
|
||||||
|
|
||||||
|
# --------------------------------------------------------------
|
||||||
|
# Disponibilità
|
||||||
|
# --------------------------------------------------------------
|
||||||
|
|
||||||
|
total_inverters = (
|
||||||
|
self.get_inverter_count()
|
||||||
|
)
|
||||||
|
|
||||||
|
active_inverters = (
|
||||||
|
self.get_active_inverter_count()
|
||||||
|
)
|
||||||
|
|
||||||
|
if total_inverters > 0:
|
||||||
|
|
||||||
|
availability = (
|
||||||
|
|
||||||
|
active_inverters
|
||||||
|
|
||||||
|
/ total_inverters
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
availability = 0.0
|
||||||
|
|
||||||
|
# --------------------------------------------------------------
|
||||||
|
# Output
|
||||||
|
# --------------------------------------------------------------
|
||||||
|
|
||||||
|
return {
|
||||||
|
|
||||||
|
"timestamp":
|
||||||
|
timestamp,
|
||||||
|
|
||||||
|
"plant_id":
|
||||||
|
self.plant_id,
|
||||||
|
|
||||||
|
"dc_power_W":
|
||||||
|
total_dc_power,
|
||||||
|
|
||||||
|
"ac_power_W":
|
||||||
|
total_ac_power,
|
||||||
|
|
||||||
|
"dc_energy_Wh":
|
||||||
|
dc_energy,
|
||||||
|
|
||||||
|
"ac_energy_Wh":
|
||||||
|
ac_energy,
|
||||||
|
|
||||||
|
"total_energy_Wh":
|
||||||
|
self.total_energy_Wh,
|
||||||
|
|
||||||
|
"dc_total_energy_Wh":
|
||||||
|
self.total_dc_energy_Wh,
|
||||||
|
|
||||||
|
"ac_total_energy_Wh":
|
||||||
|
self.total_ac_energy_Wh,
|
||||||
|
|
||||||
|
"inverters":
|
||||||
|
total_inverters,
|
||||||
|
|
||||||
|
"active_inverters":
|
||||||
|
active_inverters,
|
||||||
|
|
||||||
|
"combiners":
|
||||||
|
self.get_combiner_count(),
|
||||||
|
|
||||||
|
"panels":
|
||||||
|
self.get_panel_count(),
|
||||||
|
|
||||||
|
"active_panels":
|
||||||
|
self.get_active_panel_count(),
|
||||||
|
|
||||||
|
"availability":
|
||||||
|
availability,
|
||||||
|
|
||||||
|
"enabled":
|
||||||
|
True
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Struttura impianto
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def describe(
|
||||||
|
self
|
||||||
|
) -> str:
|
||||||
|
"""
|
||||||
|
Restituisce una descrizione testuale
|
||||||
|
della gerarchia dell'impianto.
|
||||||
|
"""
|
||||||
|
|
||||||
|
lines = []
|
||||||
|
|
||||||
|
lines.append(
|
||||||
|
f"PV Plant: "
|
||||||
|
f"{self.plant_id}"
|
||||||
|
)
|
||||||
|
|
||||||
|
lines.append(
|
||||||
|
f" Inverters: "
|
||||||
|
f"{self.get_inverter_count()}"
|
||||||
|
)
|
||||||
|
|
||||||
|
lines.append(
|
||||||
|
f" Combiners: "
|
||||||
|
f"{self.get_combiner_count()}"
|
||||||
|
)
|
||||||
|
|
||||||
|
lines.append(
|
||||||
|
f" Panels: "
|
||||||
|
f"{self.get_panel_count()}"
|
||||||
|
)
|
||||||
|
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
for inverter in self.inverters:
|
||||||
|
|
||||||
|
lines.append(
|
||||||
|
|
||||||
|
f" ├── "
|
||||||
|
f"{inverter.inverter_id} "
|
||||||
|
f"({inverter.nominal_power_kW} kW)"
|
||||||
|
)
|
||||||
|
|
||||||
|
for combiner in (
|
||||||
|
inverter.combiners
|
||||||
|
):
|
||||||
|
|
||||||
|
lines.append(
|
||||||
|
|
||||||
|
f" │ ├── "
|
||||||
|
f"{combiner.combiner_id} "
|
||||||
|
f"({len(combiner.panels)} panels)"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Raggruppamento logico delle stringhe
|
||||||
|
panel_count = (
|
||||||
|
len(
|
||||||
|
combiner.panels
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
lines.append(
|
||||||
|
|
||||||
|
f" │ │ └── "
|
||||||
|
f"{panel_count} panels"
|
||||||
|
)
|
||||||
|
|
||||||
|
return "\n".join(
|
||||||
|
lines
|
||||||
|
)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Ricerca componenti
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def find_inverter(
|
||||||
|
self,
|
||||||
|
inverter_id: str
|
||||||
|
) -> Inverter | None:
|
||||||
|
"""
|
||||||
|
Cerca un inverter tramite ID.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for inverter in self.inverters:
|
||||||
|
|
||||||
|
if (
|
||||||
|
inverter.inverter_id
|
||||||
|
== inverter_id
|
||||||
|
):
|
||||||
|
|
||||||
|
return inverter
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def find_combiner(
|
||||||
|
self,
|
||||||
|
combiner_id: str
|
||||||
|
) -> CombinerBox | None:
|
||||||
|
"""
|
||||||
|
Cerca una Combiner Box tramite ID.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for inverter in self.inverters:
|
||||||
|
|
||||||
|
for combiner in (
|
||||||
|
inverter.combiners
|
||||||
|
):
|
||||||
|
|
||||||
|
if (
|
||||||
|
combiner.combiner_id
|
||||||
|
== combiner_id
|
||||||
|
):
|
||||||
|
|
||||||
|
return combiner
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def find_panel(
|
||||||
|
self,
|
||||||
|
panel_id: str
|
||||||
|
) -> PVPanel | None:
|
||||||
|
"""
|
||||||
|
Cerca un pannello tramite ID.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for inverter in self.inverters:
|
||||||
|
|
||||||
|
for combiner in (
|
||||||
|
inverter.combiners
|
||||||
|
):
|
||||||
|
|
||||||
|
for panel in (
|
||||||
|
combiner.panels
|
||||||
|
):
|
||||||
|
|
||||||
|
if (
|
||||||
|
panel.panel_id
|
||||||
|
== panel_id
|
||||||
|
):
|
||||||
|
|
||||||
|
return panel
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def __repr__(
|
||||||
|
self
|
||||||
|
) -> str:
|
||||||
|
"""
|
||||||
|
Rappresentazione leggibile dell'impianto.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return (
|
||||||
|
|
||||||
|
f"PVPlant("
|
||||||
|
f"id={self.plant_id}, "
|
||||||
|
f"inverters="
|
||||||
|
f"{self.get_inverter_count()}, "
|
||||||
|
f"combiners="
|
||||||
|
f"{self.get_combiner_count()}, "
|
||||||
|
f"panels="
|
||||||
|
f"{self.get_panel_count()})"
|
||||||
|
)
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user