open-energymesh-offline-fir.../src/mesh.js

140 lines
3.6 KiB
JavaScript

// Minimal Open-EnergyMesh scaffold (offline-first MVP)
// Provides a tiny in-memory data model: Device, Inverter, Meter, DER, PriceQuote, Forecast
// and a lightweight EnergyMesh orchestrator with a simple energy-flow calculation.
class Device {
constructor(id, name, type) {
this.id = id;
this.name = name;
this.type = type; // e.g., 'inverter', 'meter', 'storage', 'der'
}
}
class Inverter {
constructor(id, deviceId, capacityW, outputW) {
this.id = id;
this.deviceId = deviceId;
this.capacityW = capacityW;
this.outputW = typeof outputW === 'number' ? outputW : 0; // current output in Watts
}
}
class Meter {
constructor(id, deviceId, consumptionW) {
this.id = id;
this.deviceId = deviceId;
this.consumptionW = typeof consumptionW === 'number' ? consumptionW : 0; // current consumption in Watts
}
}
class DER {
constructor(id, type, capacityW) {
this.id = id;
this.type = type; // 'inverter' | 'storage'
this.capacityW = capacityW;
}
}
class PriceQuote {
constructor(timestamp, pricePerKWh) {
this.timestamp = timestamp; // epoch ms
this.pricePerKWh = pricePerKWh; // number
}
}
class Forecast {
constructor(horizonHours, value) {
this.horizonHours = horizonHours;
this.value = value; // simple numeric forecast placeholder
}
}
class EnergyMesh {
constructor() {
this.devices = new Map(); // id -> Device
this.inverters = new Map(); // id -> Inverter
this.meters = new Map(); // id -> Meter
this.ders = new Map(); // id -> DER
this.quotes = [];
this.forecasts = [];
}
// Device helpers
addDevice(device) {
if (!(device instanceof Device)) {
throw new Error('addDevice expects a Device instance');
}
this.devices.set(device.id, device);
}
addInverter(inverter) {
if (!(inverter instanceof Inverter)) {
throw new Error('addInverter expects an Inverter instance');
}
this.inverters.set(inverter.id, inverter);
// implicitly ensure a device exists for this inverter
if (!this.devices.has(inverter.deviceId)) {
this.addDevice(new Device(inverter.deviceId, `Inverter-${inverter.deviceId}`, 'inverter'));
}
}
addMeter(meter) {
if (!(meter instanceof Meter)) {
throw new Error('addMeter expects a Meter instance');
}
this.meters.set(meter.id, meter);
if (!this.devices.has(meter.deviceId)) {
this.addDevice(new Device(meter.deviceId, `Meter-${meter.deviceId}`, 'meter'));
}
}
addDER(der) {
if (!(der instanceof DER)) {
throw new Error('addDER expects a DER instance');
}
this.ders.set(der.id, der);
}
addPriceQuote(quote) {
if (!(quote instanceof PriceQuote)) {
throw new Error('addPriceQuote expects a PriceQuote');
}
this.quotes.push(quote);
}
addForecast(forecast) {
if (!(forecast instanceof Forecast)) {
throw new Error('addForecast expects a Forecast');
}
this.forecasts.push(forecast);
}
// Simple energy-flow calculation: total generation minus total consumption
// Assumes inverter.outputW is generation and meter.consumptionW is load
computeFlow() {
let generation = 0;
for (const inv of this.inverters.values()) {
generation += typeof inv.outputW === 'number' ? inv.outputW : 0;
}
let consumption = 0;
for (const m of this.meters.values()) {
consumption += typeof m.consumptionW === 'number' ? m.consumptionW : 0;
}
return {
generationW: generation,
consumptionW: consumption,
netFlowW: generation - consumption
};
}
}
module.exports = {
EnergyMesh,
Device,
Inverter,
Meter,
DER,
PriceQuote,
Forecast
};