You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

155 lines
5.4 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# malis-cep (prototype)
Spring Boot prototype demonstrating a **config-driven CEP** pipeline:
- split configuration (common + env profile + bindings directory)
- routing by binding (source match -> parser -> detector -> lifecycle -> outputs)
- external detectors return **events only** (ALARM/CANCEL); state is managed in this service
- caller-managed lifecycle: transition-only publishing (dedup + debounce)
- pluggable outputs + DLQ envelope
It also contains **real transport implementations**:
- MQTT ingress via **Spring Integration MQTT (Paho)**
- MQTT egress via **Paho** with per-output `brokerRef` (see `MqttBrokerRegistry`)
- RabbitMQ egress (and optional ingress) via **Spring AMQP**
## EEBUS XML support (prototype)
This prototype includes minimal, namespace-tolerant DOM parsing/formatting for:
- full SPINE-like `<Datagram>` **measurement** payloads (`EEBUS_MEASUREMENT_DATAGRAM_XML`)
- full SPINE-like `<Datagram>` **alarm** payloads (`EEBUS_ALARM_DATAGRAM_XML`)
- formatting detected alarms into a full `<Datagram>` (`EEBUS_ALARM_DATAGRAM_XML`)
The XML samples used by unit tests are in `src/test/resources/eebus/`.
## Internal detector (expression rules)
This prototype now includes a fully **internal** detector that evaluates configurable expressions on top of a
protocol-agnostic input event model.
- detector type: `EXPRESSION_RULES`
- accepts parsed bodies:
- `EEBUS_MEASUREMENT_DATAGRAM_XML` (full `<Datagram>`)
- `MEASUREMENT_SET` (from `JSON_PORTS_BOOLEAN`, etc.)
- `EventBatch` (advanced: other converters can produce this directly)
Rules are boolean SpEL expressions over the in-memory per-device signal store.
Example `common.yml` snippet:
```yaml
cep:
detectors:
internalPorts:
type: EXPRESSION_RULES
config:
missingPolicy: FALSE
rules:
- id: any_port_open
severity: MAJOR
expression: "s['port.1.state'] == false || s['port.2.state'] == false || s['port.3.state'] == false"
emitCancel: true
```
Each rule emits `ALARM` when the expression evaluates to `true`, otherwise `CANCEL` (unless `emitCancel=false`).
State transitions are still managed by `AlarmLifecycleService`.
## Heartbeat (periodic state propagation)
By default, bindings publish only on state transitions (`ALARM` on open, `CANCEL` on close).
You can optionally enable a **per-binding heartbeat** that periodically re-emits the *current* state
(useful when downstream systems want a "still active" / "still inactive" signal).
Example in a binding YAML:
```yaml
cep:
bindings:
mqttEebusAlarm:
# ...
heartbeat:
enabled: true
periodMs: 60000
includeInactive: false # set true to also emit CANCEL heartbeats
initialDelayMs: 60000 # optional; if 0/absent it defaults to periodMs
```
Heartbeat messages keep the normal action (`ALARM` or `CANCEL`) and add `details.heartbeat=true`.
While a debounced CANCEL is pending, heartbeats continue as `ALARM` until the debounce maturity is reached.
## Config layout
The app uses Spring Boot's config import:
- `./config/cep/common.yml`
- `./config/cep/env/${spring.profiles.active}.yml`
- all binding files in `./config/cep/bindings/*.yml`
Bindings are merged into `cep.bindings` by `BindingFileLoader`.
## Run (dev)
```bash
mvn spring-boot:run -Dspring-boot.run.profiles=dev
```
### Local MQTT
The default dev profile expects MQTT at `tcp://localhost:1883`.
You can test with e.g. Mosquitto and publish:
```bash
# Scenario 3: raw ports JSON (8 boolean)
mosquitto_pub -h localhost -t "/sadfjsadf/site-a/heating" -m '{"port1":true,"port2":true,"port3":true,"port4":true,"port5":true,"port6":true,"port7":true,"port8":false}'
# Scenario 1/2: device publishes an alarm (JSON or full <Datagram> XML)
mosquitto_pub -h localhost -t "alarms/site-a/heating/device-1" -m '{"alarmCode":"X","action":"ALARM"}'
```
The produced alarms are published to:
- `malis/alarms/${site}/${department}` (JSON alarm message)
- `malis/alarms/xml/${site}/${department}` (full <Datagram> XML)
- DLQ to `malis/dlq` (on errors / no binding match)
### Local RabbitMQ (optional)
`./config/cep/common.yml` ships with a Rabbit output example (`rabbitMalisEebus`) and a Rabbit ingress example.
To enable Rabbit ingress, set in `common.yml` or a profile override:
```yaml
cep:
ingress:
rabbit:
enabled: true
```
Dev profile includes a default Rabbit broker URI:
- `amqp://guest:guest@localhost:5672/%2F`
## Test via HTTP (transport simulation)
If you don't want to run MQTT/Rabbit locally, you can inject a message directly:
```bash
curl -X POST "http://localhost:8080/api/test/ingress?inType=MQTT&path=/sadfjsadf/site-a/heating" \
-H "Content-Type: application/json" \
-d '{"port1":true,"port2":true,"port3":true,"port4":true,"port5":true,"port6":true,"port7":true,"port8":false}'
```
## Where to look in code
- `at.co.procon.malis.cep.binding.BindingResolver` resolves an ingress message to **exactly one** binding
- `at.co.procon.malis.cep.parser.*` parsers (raw payload -> canonical model)
- `at.co.procon.malis.cep.detector.*` detectors (canonical model -> events)
- `at.co.procon.malis.cep.lifecycle.AlarmLifecycleService` in-memory state (transition-only)
- `at.co.procon.malis.cep.pipeline.CepPipeline` orchestration + DLQ
- `at.co.procon.malis.cep.transport.mqtt.*` real MQTT ingress/egress
- `at.co.procon.malis.cep.transport.rabbit.*` Rabbit registry + optional ingress