-
Notifications
You must be signed in to change notification settings - Fork 562
Open
Description
[Bug] EssPower circular dependency causes "Coefficient was not found" error in multiple ESS implementations
Summary
The fix for the circular dependency issue in PR #3113 was only applied to Edge2EdgeWebsocketEssImpl. However, many other ESS implementations have the same problematic @Reference pattern for the Power service, causing the same "Coefficient was not found" error.
Error Message
WARN [ems.edge.ess.core.power.Solver] Power-Solver: Solve failed: Coefficient for [ess0,ALL,ACTIVE] was not found. Ess-Power is not (yet) fully initialized.
Root Cause Analysis
1. Circular Dependency (Fixed in #3113 for Edge2Edge only)
The dependency chain ess -> _power -> ess causes issues during OSGi component activation:
- ESS component tries to activate
- ESS has
@Reference(cardinality = MANDATORY)toPower Power(EssPowerImpl) needs ESS to register viaaddEss()to create coefficients- Race condition: ESS may be referenced before its coefficients are initialized
The fix in #3113:
// Before (problematic):
@Reference
private Power power;
// After (fixed):
@Reference(cardinality = ReferenceCardinality.OPTIONAL,
policyOption = ReferencePolicyOption.GREEDY,
policy = ReferencePolicy.DYNAMIC)
private volatile Power power;2. Thread Safety Issue in Data.java (Unfixed)
There's also an underlying race condition in Data.java:
// EssPowerImpl.java - addEss() is synchronized
protected synchronized void addEss(ManagedSymmetricEss ess) {
this.data.addEss(ess); // adds to esss list, then updates coefficients
}
// Data.java - getConstraintsForAllInverters() is NOT synchronized
public List<Constraint> getConstraintsForAllInverters() throws OpenemsException {
return this.getConstraintsWithoutDisabledInverters(Collections.emptyList());
}Race condition:
Thread A (OSGi): Thread B (Cycle Scheduler):
───────────────── ───────────────────────────
addEss(ess0) - acquires lock
esss.add(ess0) ✓
updateInverters() starts...
Solver.solve() - NO LOCK!
getConstraintsForAllInverters()
iterates esss → sees ess0
coefficients.of("ess0"...)
→ FAILS! (not initialized yet)
coefficients.initialize() ✓
releases lock
Affected Components
Affected ESS Implementations
| Module | File |
|---|---|
| io.openems.edge.ess.generic | EssGenericManagedSymmetricImpl.java |
| io.openems.edge.ess.generic | EssGenericOffGridImpl.java |
| io.openems.edge.ess.cluster | EssClusterImpl.java |
| io.openems.edge.goodwe | GoodWeEssImpl.java |
| io.openems.edge.goodwe | GoodWeBatteryInverterImpl.java |
| io.openems.edge.edge2edge | Edge2EdgeEssImpl.java |
| io.openems.edge.edge2edge | Edge2EdgeGenericReadComponentImpl.java |
| io.openems.edge.fenecon.mini | FeneconMiniEssImpl.java |
| io.openems.edge.fenecon.pro | FeneconProEssImpl.java |
| io.openems.edge.ess.fenecon.commercial40 | EssFeneconCommercial40Impl.java |
| io.openems.edge.sma | EssSmaSunnyIslandImpl.java |
| io.openems.edge.kaco.blueplanet.hybrid10 | KacoBlueplanetHybrid10EssImpl.java |
| io.openems.edge.kostal | KostalManagedEssImpl.java |
| io.openems.edge.simulator | SimulatorEssSinglePhaseReactingImpl.java |
| io.openems.edge.simulator | SimulatorEssSymmetricReactingImpl.java |
| io.openems.edge.simulator | SimulatorEssAsymmetricReactingImpl.java |
Note: Other ESS implementations in third-party modules may also be affected.
Steps to Reproduce
- Configure multiple ESS components (e.g., ess0, ess1, ess2, ess3)
- Start OpenEMS Edge
- Observe logs during startup
- Error appears:
Coefficient for [essX,ALL,ACTIVE] was not found
The error may:
- Appear briefly during startup and then stop
- Persist for a long time if component activation order is unfavorable
- Reappear when ESS components reconnect (e.g., Modbus disconnect/reconnect)
Proposed Fix
Fix 1: Apply OPTIONAL/DYNAMIC pattern to all ESS implementations
For each affected ESS implementation:
// Change from:
@Reference
private Power power;
// or
@Reference(cardinality = ReferenceCardinality.MANDATORY, policyOption = ReferencePolicyOption.GREEDY)
private Power power;
// To:
@Reference(cardinality = ReferenceCardinality.OPTIONAL,
policyOption = ReferencePolicyOption.GREEDY,
policy = ReferencePolicy.DYNAMIC)
private volatile Power power;And update getPower():
@Override
public Power getPower() {
final var power = this.power;
if (power == null) {
throw new OpenemsRuntimeException("Ess.Power is not yet available");
}
return power;
}Fix 2: Add synchronization to Data.java (additional fix)
// Data.java - make constraint retrieval synchronized
public synchronized List<Constraint> getConstraintsWithoutDisabledInverters(
Collection<Inverter> disabledInverters) throws OpenemsException {
// existing code
}Environment
- OpenEMS Version: 2025.6.0+ (post PR FEMS Backports 2025-05-01 #3113)
- Java Version: 21
- OS: Linux (ARM64)
Related
- PR FEMS Backports 2025-05-01 #3113 - FEMS Backports 2025-05-01 (partial fix for Edge2EdgeWebsocketEss only)
- Community discussion: https://community.openems.io/t/how-can-i-prevent-the-error-coefficient-ess0-all-active-was-not-found-ess-power-is-not-yet-fully-initialized-from-occurring/10352
Checklist
- Apply fix to all ESS implementations listed above
- Consider adding synchronization to
Data.getConstraintsWithoutDisabledInverters() - Add unit tests for race condition scenarios
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels