Skip to content

Commit c86accb

Browse files
author
BESS Solutions
committed
feat(v0.7.0): DRL Sim + Federated Learning + VPP OpenADR + Helm
Gymnasium BESS Environment (src/simulation/): - BESSPhysicsModel: SOC dynamics, Rainflow degradation, RC thermal model - BESSEnv: 8-feature obs, continuous action, 96-step episodes (24h x 15min) reward = arbitrage_revenue - degradation_cost - thermal_penalty - Compatible with Ray RLlib, SB3, any Gym-compliant RL framework VPP Publisher (src/interfaces/vpp_publisher.py): - VPPPublisher: aggregates flex capacity from N edge sites - OpenADR 3.0 EiEvent JSON payload generation - Prometheus: bess_vpp_flex_capacity_kw + bess_vpp_events_published_total Federated Learning Client (src/interfaces/fl_client.py): - BESSAIFlowerClient(NumPyClient): get_parameters/fit/evaluate - Privacy principle: raw telemetry stays on-device, only weight deltas sent - Rolling buffer (1000 cycles), local training simulation - is_available property for graceful fallback when flwr not installed Prometheus Metrics: +4 (15 total now) - bess_vpp_flex_capacity_kw, bess_vpp_events_published_total - bess_fl_rounds_total, bess_fl_train_loss DRL Training Script (scripts/train_drl_policy.py): - Ray RLlib PPO config: gamma=0.99, lambda=0.95, 4096 batch - Exports policy to ONNX for edge deployment via ONNXDispatcher Helm Chart (infrastructure/helm/bessai-edge/): - Chart.yaml v0.7.0, values.yaml (image, autoscaling, security, probes) - deployment.yaml: securityContext, Prometheus annotations, ONNX volume - resources.yaml: Service, HPA (CPU+memory), ServiceAccount, ConfigMap - _helpers.tpl: standard name/label/fullname helpers Dependencies: gymnasium>=0.29.0 Tests: 108/108 in 8.47s (+35 new tests)
1 parent ec2c037 commit c86accb

16 files changed

Lines changed: 1691 additions & 18 deletions

File tree

CHANGELOG.md

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,34 @@
77
88
---
99

10-
## 🤖 AGENT HANDOFF — Estado actual del proyecto (2026-02-19T15:09 -03:00)
10+
## 🤖 AGENT HANDOFF — Estado actual del proyecto (2026-02-19T15:36 -03:00)
1111

1212
### Contexto del sistema
1313
**BESSAI Edge Gateway** (`open-bess-edge`) es el componente de borde de un sistema de gestión de baterías industriales (BESS). Adquiere telemetría via **Modbus TCP** desde inversores Huawei SUN2000, valida seguridad, y publica a **GCP Pub/Sub** con observabilidad via **OpenTelemetry** y **Prometheus**.
1414

15-
### Estado del código — ✅ v0.6.0, COMPLETO Y VALIDADO
15+
### Estado del código — ✅ v0.7.0, COMPLETO Y VALIDADO
1616

1717
| Archivo | Estado | Notas |
1818
|---|---|---|
19-
| `src/core/config.py` | ✅ Producción | `INVERTER_IP` acepta IPs y hostnames. Nuevo: `HEALTH_PORT=8000` |
19+
| `src/core/config.py` | ✅ Producción | `INVERTER_IP` acepta IPs y hostnames; `HEALTH_PORT=8000` |
2020
| `src/core/safety.py` | ✅ Producción | check_safety + watchdog_loop async |
2121
| `src/core/main.py` | ✅ Producción | Integrado con HealthServer + Prometheus metrics |
2222
| `src/drivers/modbus_driver.py` | ✅ Producción | pymodbus 3.12, struct-based encode/decode |
23-
| `src/interfaces/health.py` | ✅ Producción | Servidor HTTP /health (JSON) + /metrics (Prometheus) vía aiohttp |
24-
| `src/interfaces/metrics.py` |**AMPLIADO** | +4 métricas AI: IDS_ALERTS, IDS_SCORE, ONNX_MS, ONNX_CMDS |
25-
| `src/interfaces/ai_ids.py` |**NUEVO** | AI-IDS: IsolationForest + z-score ensemble, score 0-1, alertas Prometheus |
26-
| `src/interfaces/onnx_dispatcher.py` |**NUEVO** | ONNX Runtime offline dispatcher, fallback gracioso si no hay modelo |
27-
| `src/interfaces/pubsub_publisher.py` | ✅ Producción | Async context manager, GCP Pub/Sub, JSON envelope |
28-
| `src/interfaces/otel_setup.py` | ✅ Producción | TracerProvider + MeterProvider |
29-
| `models/dispatch_policy.onnx` |**NUEVO** | Modelo dummy (SOC×0.8). Reemplazar con export de Ray RLlib. |
30-
| `scripts/generate_dummy_onnx.py` |**NUEVO** | Genera el modelo dummy + smoke test integrado |
31-
| `infrastructure/docker/docker-compose.yml` | ✅ Producción | Perfil `monitoring` (Prometheus+Grafana), port 8000 |
32-
| `infrastructure/prometheus/prometheus.yml` | ✅ Producción | Scrape config: gateway:8000 + otel-collector:8888 |
23+
| `src/interfaces/health.py` | ✅ Producción | /health (JSON) + /metrics (Prometheus) vía aiohttp |
24+
| `src/interfaces/metrics.py` |**AMPLIADO** | 15 métricas (+ VPP_FLEX, VPP_EVENTS, FL_ROUNDS, FL_LOSS) |
25+
| `src/interfaces/ai_ids.py` | ✅ Producción | AI-IDS: IsolationForest + z-score ensemble, score 0-1 |
26+
| `src/interfaces/onnx_dispatcher.py` | ✅ Producción | ONNX Runtime offline dispatcher, fallback gracioso |
27+
| `src/interfaces/vpp_publisher.py` |**NUEVO** | VPP OpenADR 3.0: agrega flex, publica EiEvent JSON |
28+
| `src/interfaces/fl_client.py` |**NUEVO** | Flower FL client: datos no salen del edge |
29+
| `src/simulation/bess_env.py` |**NUEVO** | Gymnasium BESS env: obs(8), action cont., 96 steps/ep |
30+
| `src/simulation/bess_model.py` |**NUEVO** | Física BESS: SOC, degradación Rainflow, térmica RC |
31+
| `models/dispatch_policy.onnx` | ✅ Producción | Modelo dummy (soc×0.8). Reemplazar con Ray RLlib export. |
32+
| `scripts/generate_dummy_onnx.py` | ✅ Producción | Genera el modelo dummy + smoke test |
33+
| `scripts/train_drl_policy.py` |**NUEVO** | Ray RLlib PPO training + ONNX export |
34+
| `infrastructure/helm/bessai-edge/` |**NUEVO** | Helm chart completo: deploy, service, HPA, ConfigMap, SA |
3335
| `infrastructure/terraform/` | ✅ Producción | apply ejecutado — 18 recursos en GCP |
3436

35-
**Suite de tests: 73/73 ✅ en 11.89s — Python 3.14 · pytest-asyncio 1.3.0**
37+
**Suite de tests: 108/108 ✅ en 8.47s — Python 3.14 · gymnasium 0.29**
3638

3739
### 🐳 Stack Docker — OPERATIVO
3840

@@ -66,10 +68,11 @@ docker compose -f infrastructure/docker/docker-compose.yml --profile simulator -
6668

6769
**Todos los bloqueadores resueltos.** El pipeline completo está operativo.
6870

69-
**Próxima prioridad — BESSAI v0.7.0 (Edge AI Fase 2):**
70-
- DRL Training: Ray RLlib (PPO/SAC) + Gymnasium + pandapower simulator
71-
- Federated Learning: Flower (flwr) — solo gradientes salen del edge
72-
- Ver roadmap: `docs/bessai_v2_roadmap.md` — Fase 2 aún en progreso
71+
**Próxima prioridad — BESSAI v0.8.0 (Edge AI Fase 3):**
72+
- DRL Training real: Ray RLlib en servidor, export a ONNX, despliegue en edge
73+
- Federated Orchestration: servidor FL con Flower, FedAvg con N>=3 sitios
74+
- VPP real: conectar a broker OpenADR 3.0
75+
- Ver roadmap: `docs/bessai_v2_roadmap.md` — FASE 3 50% completada
7376

7477
### 📂 Estructura de archivos clave
7578
```
@@ -117,6 +120,28 @@ Format: [Semantic Versioning](https://semver.org/) · [Conventional Commits](htt
117120

118121
---
119122

123+
## [v0.7.0] — 2026-02-19
124+
125+
### Added
126+
- `src/simulation/bess_env.py``BESSEnv` (Gymnasium): obs(8), action continuo [-50,50], 96 steps/ep
127+
- `src/simulation/bess_model.py``BESSPhysicsModel`: SOC, degradación Rainflow approx, térmica RC
128+
- `src/interfaces/vpp_publisher.py``VPPPublisher` + `OpenADREvent` (OpenADR 3.0 JSON)
129+
- `src/interfaces/fl_client.py``BESSAIFlowerClient` (Flower NumPyClient): datos en edge, solo pesos salen
130+
- `scripts/train_drl_policy.py` — entrenamiento PPO con Ray RLlib + export ONNX
131+
- `infrastructure/helm/bessai-edge/` — Helm chart completo: Chart.yaml, values.yaml, deployment, HPA, ConfigMap, SA
132+
- 4 nuevas métricas Prometheus: `bess_vpp_flex_capacity_kw`, `bess_vpp_events_published_total`, `bess_fl_rounds_total`, `bess_fl_train_loss`
133+
- 35 nuevos tests: `test_bess_env.py` (15) + `test_vpp_publisher.py` (11) + `test_fl_client.py` (8) + 1 fix
134+
135+
### Dependencies
136+
- Agregado `gymnasium>=0.29.0` a requirements.txt
137+
138+
### Tests
139+
```
140+
108 / 108 passed in 8.47s (+35 tests vs v0.6.0: 73/73)
141+
```
142+
143+
---
144+
120145
## [v0.6.0] — 2026-02-19
121146

122147
### Added
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
apiVersion: v2
2+
name: bessai-edge
3+
description: BESSAI Edge Gateway — Kubernetes Helm chart
4+
5+
# Chart type: application (not library)
6+
type: application
7+
8+
# Chart version (follows SemVer — bump on chart changes)
9+
version: 0.7.0
10+
11+
# Application version deployed by this chart
12+
appVersion: "0.7.0"
13+
14+
home: https://github.com/bess-solutions/open-bess-edge
15+
sources:
16+
- https://github.com/bess-solutions/open-bess-edge
17+
18+
maintainers:
19+
- name: TCI-GECOMP
20+
email: ops@bess-solutions.cl
21+
22+
keywords:
23+
- bess
24+
- iot
25+
- energy
26+
- modbus
27+
- prometheus
28+
- edge-ai
29+
30+
annotations:
31+
category: IoT/Edge
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{{/*
2+
Expand the name of the chart.
3+
*/}}
4+
{{- define "bessai-edge.name" -}}
5+
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
6+
{{- end }}
7+
8+
{{/*
9+
Create a default fully qualified app name.
10+
*/}}
11+
{{- define "bessai-edge.fullname" -}}
12+
{{- if .Values.fullnameOverride }}
13+
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
14+
{{- else }}
15+
{{- $name := default .Chart.Name .Values.nameOverride }}
16+
{{- if contains $name .Release.Name }}
17+
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
18+
{{- else }}
19+
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
20+
{{- end }}
21+
{{- end }}
22+
{{- end }}
23+
24+
{{/*
25+
Create chart labels.
26+
*/}}
27+
{{- define "bessai-edge.labels" -}}
28+
helm.sh/chart: {{ include "bessai-edge.chart" . }}
29+
{{ include "bessai-edge.selectorLabels" . }}
30+
{{- if .Chart.AppVersion }}
31+
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
32+
{{- end }}
33+
app.kubernetes.io/managed-by: {{ .Release.Service }}
34+
{{- end }}
35+
36+
{{- define "bessai-edge.selectorLabels" -}}
37+
app.kubernetes.io/name: {{ include "bessai-edge.name" . }}
38+
app.kubernetes.io/instance: {{ .Release.Name }}
39+
{{- end }}
40+
41+
{{- define "bessai-edge.chart" -}}
42+
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
43+
{{- end }}
44+
45+
{{/*
46+
ServiceAccount name.
47+
*/}}
48+
{{- define "bessai-edge.serviceAccountName" -}}
49+
{{- if .Values.serviceAccount.create }}
50+
{{- default (include "bessai-edge.fullname" .) .Values.serviceAccount.name }}
51+
{{- else }}
52+
{{- default "default" .Values.serviceAccount.name }}
53+
{{- end }}
54+
{{- end }}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: {{ include "bessai-edge.fullname" . }}
5+
labels:
6+
{{- include "bessai-edge.labels" . | nindent 4 }}
7+
spec:
8+
{{- if not .Values.autoscaling.enabled }}
9+
replicas: {{ .Values.replicaCount }}
10+
{{- end }}
11+
selector:
12+
matchLabels:
13+
{{- include "bessai-edge.selectorLabels" . | nindent 6 }}
14+
template:
15+
metadata:
16+
labels:
17+
{{- include "bessai-edge.selectorLabels" . | nindent 8 }}
18+
annotations:
19+
prometheus.io/scrape: "true"
20+
prometheus.io/path: "/metrics"
21+
prometheus.io/port: "{{ .Values.service.port }}"
22+
spec:
23+
{{- with .Values.imagePullSecrets }}
24+
imagePullSecrets:
25+
{{- toYaml . | nindent 8 }}
26+
{{- end }}
27+
serviceAccountName: {{ include "bessai-edge.serviceAccountName" . }}
28+
securityContext:
29+
{{- toYaml .Values.podSecurityContext | nindent 8 }}
30+
containers:
31+
- name: {{ .Chart.Name }}
32+
securityContext:
33+
{{- toYaml .Values.securityContext | nindent 12 }}
34+
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
35+
imagePullPolicy: {{ .Values.image.pullPolicy }}
36+
ports:
37+
- name: http
38+
containerPort: {{ .Values.service.port }}
39+
protocol: TCP
40+
envFrom:
41+
- configMapRef:
42+
name: {{ include "bessai-edge.fullname" . }}-config
43+
{{- if .Values.onnxModel.enabled }}
44+
volumeMounts:
45+
- name: onnx-model
46+
mountPath: {{ .Values.onnxModel.mountPath }}
47+
readOnly: true
48+
- name: tmp
49+
mountPath: /tmp
50+
{{- end }}
51+
livenessProbe:
52+
{{- toYaml .Values.livenessProbe | nindent 12 }}
53+
readinessProbe:
54+
{{- toYaml .Values.readinessProbe | nindent 12 }}
55+
resources:
56+
{{- toYaml .Values.resources | nindent 12 }}
57+
{{- if .Values.onnxModel.enabled }}
58+
volumes:
59+
- name: onnx-model
60+
{{- if eq .Values.onnxModel.source "pvc" }}
61+
persistentVolumeClaim:
62+
claimName: {{ .Values.onnxModel.pvcName | default (printf "%s-models" (include "bessai-edge.fullname" .)) }}
63+
{{- else }}
64+
emptyDir: {}
65+
{{- end }}
66+
- name: tmp
67+
emptyDir: {}
68+
{{- end }}
69+
{{- with .Values.nodeSelector }}
70+
nodeSelector:
71+
{{- toYaml . | nindent 8 }}
72+
{{- end }}
73+
{{- with .Values.affinity }}
74+
affinity:
75+
{{- toYaml . | nindent 8 }}
76+
{{- end }}
77+
{{- with .Values.tolerations }}
78+
tolerations:
79+
{{- toYaml . | nindent 8 }}
80+
{{- end }}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: {{ include "bessai-edge.fullname" . }}-config
5+
labels:
6+
{{- include "bessai-edge.labels" . | nindent 4 }}
7+
data:
8+
SITE_ID: {{ .Values.config.siteId | quote }}
9+
INVERTER_IP: {{ .Values.config.inverterIp | quote }}
10+
INVERTER_PORT: {{ .Values.config.inverterPort | quote }}
11+
HEALTH_PORT: {{ .Values.config.healthPort | quote }}
12+
LOG_LEVEL: {{ .Values.config.logLevel | quote }}
13+
GCP_PROJECT_ID: {{ .Values.config.gcpProjectId | quote }}
14+
PUBSUB_TOPIC: {{ .Values.config.pubsubTopic | quote }}
15+
WATCHDOG_TIMEOUT_S: {{ .Values.config.watchdogTimeoutS | quote }}
16+
---
17+
apiVersion: v1
18+
kind: Service
19+
metadata:
20+
name: {{ include "bessai-edge.fullname" . }}
21+
labels:
22+
{{- include "bessai-edge.labels" . | nindent 4 }}
23+
spec:
24+
type: {{ .Values.service.type }}
25+
ports:
26+
- port: {{ .Values.service.port }}
27+
targetPort: http
28+
protocol: TCP
29+
name: http
30+
selector:
31+
{{- include "bessai-edge.selectorLabels" . | nindent 4 }}
32+
---
33+
{{- if .Values.autoscaling.enabled }}
34+
apiVersion: autoscaling/v2
35+
kind: HorizontalPodAutoscaler
36+
metadata:
37+
name: {{ include "bessai-edge.fullname" . }}
38+
labels:
39+
{{- include "bessai-edge.labels" . | nindent 4 }}
40+
spec:
41+
scaleTargetRef:
42+
apiVersion: apps/v1
43+
kind: Deployment
44+
name: {{ include "bessai-edge.fullname" . }}
45+
minReplicas: {{ .Values.autoscaling.minReplicas }}
46+
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
47+
metrics:
48+
- type: Resource
49+
resource:
50+
name: cpu
51+
target:
52+
type: Utilization
53+
averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
54+
- type: Resource
55+
resource:
56+
name: memory
57+
target:
58+
type: Utilization
59+
averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
60+
{{- end }}
61+
---
62+
{{- if .Values.serviceAccount.create }}
63+
apiVersion: v1
64+
kind: ServiceAccount
65+
metadata:
66+
name: {{ include "bessai-edge.serviceAccountName" . }}
67+
labels:
68+
{{- include "bessai-edge.labels" . | nindent 4 }}
69+
{{- with .Values.serviceAccount.annotations }}
70+
annotations:
71+
{{- toYaml . | nindent 4 }}
72+
{{- end }}
73+
{{- end }}

0 commit comments

Comments
 (0)