Add guarded SRNE settings write infrastructure - #21
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 914cad00c1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ry continuous rating Review follow-ups on the guarded settings-write path (Aaron offline): - SafeWriteHandler: queueIfChanged runs on the Edge cycle thread while onExecute/verify fire on the Modbus bridge worker thread; the mutable state/target had no synchronization, so transitions were not guaranteed visible across threads. Make the four methods synchronized. - Current-limit settings (stopChargeCurrent, acChargeCurrentLimit, maxChargeCurrentLimit) were clamped to 120A. The SR-SE10B (205Ah LFP) continuous max charge/discharge is 100A; 120A is only the 3-second peak (datasheet SRNE_SE series V1.5). Cap at 100A via a named constant so a configured continuous limit can never exceed the battery rating. Signed-off-by: arindahills <293051436+arindahills@users.noreply.github.com>
arindahills
left a comment
There was a problem hiding this comment.
Reviewed the guarded settings-write infrastructure. Solid, safety-first design: gate off by default, controlEnabled && machineState.isVerified() required before any write, write-only-if-different, clamp-to-bounds, post-write readback verification, and no auto-retry after a FAILED. Well tested.
Aaron is offline, so rather than leave blocking comments I've pushed two fixes directly to this branch. Details below, with what's fixed vs. what I'd leave for the OpenEMS#53 follow-ups.
Pushed to this branch
1. SafeWriteHandler thread-safety. queueIfChanged runs on the Edge cycle thread (run()), while onExecute and verify fire on the Modbus bridge worker thread (task execute callback + read-back channel callback). state/target were plain fields with no synchronization, so a transition on one thread wasn't guaranteed visible to the other (a real cross-thread visibility bug in a safety component). Made the four public methods synchronized so transitions are atomic and visible. Behaviour unchanged; existing tests still pass.
2. Current-limit ceiling 120A → 100A (battery safety). stopChargeCurrent, acChargeCurrentLimit, and maxChargeCurrentLimit clamped to 0..120. Per the SR-SE10B (205Ah LFP) datasheet (SRNE_SE series V1.5): continuous max charge/discharge is 100A; 120A is only the 3-second peak. A continuous limit setting must cap at the continuous rating, otherwise an operator could configure a 120A continuous limit above what the battery allows. Replaced the magic 120 with a named, datasheet-cited constant MAX_BATTERY_CURRENT_A = 100. (SoC registers stay 0..100, correct.)
Verified, no change needed
Read-back freshness. I flagged that verify() might act on a pre-write reading, but on closer look the Modbus bridge runs tasks serially, so the read-back that fires after onExecute physically post-dates the write — it's fresh. No change; the guarantee holds.
Suggested follow-ups for OpenEMS#53 (not blocking this slice)
AWAITING_READBACKhas no timeout. If the LOW-priority read-back never delivers a fresh value, a handler stays awaiting forever (write happened, never confirmed). Not dangerous (no repeat write) and it's within OpenEMS#53's "stale/fault/transition gates" — just flagging it's currently unbounded.VERIFIEDis terminal. A handler never writes again for the life of the component instance, so continuous drift-correction only happens if a config change re-activates the component (which it does — no@Modified, so a config update triggers deactivate/activate → fresh handlers). Acceptable for settings (vs. control set-points); worth a comment noting the re-activation dependency.- Silent clamp. When a configured target is clamped, nothing signals it (operator sets 150, gets 100, sees
VERIFIED). A debug log / status channel would help once control is enabled at site. aggregateWriteStateranks byenum.ordinal()whileStateis anOptionsEnumwith distinctgetValue()s — reordering the enum would silently change aggregation. Rank on explicit precedence.
Net: safe to merge as disabled infrastructure once CI is green. The two blockers before controlEnabled is ever turned on at site (thread-safety and the 100A ceiling) are handled in this branch.
What changed
Story boundary
This is the first implementation slice of settings story #53. Remaining OpenEMS#53 work includes rollback context, stale/fault/transition gates and tests, exact FC16 integration coverage, auditing, and validated schedule-block support.
The PR intentionally excludes ambiguous mode/source-priority writes and all DFxx commands. Guarded inverter ON/OFF and sleep/run commands are tracked separately in #57; immediate source transfer is OpenEMS#56; managed power control is OpenEMS#55.
Why
This establishes safe settings-write infrastructure while keeping control disabled unless an operator explicitly enables it.
Validation
./gradlew :io.openems.edge.ess.srne:check