setInOutWRteRvrtTms() existed but was never called. InOutWRte_RvrtTms is the delay after which the inverter drops the setpoint and resumes its native behaviour. Left at 0, a setpoint stays active indefinitely, HEMS stopped, network lost or process crashed included - the battery stays where the last command put it. Add a per-thing revertTime parameter, 0..28800 s, defaulting to 0 so the generic vendor keeps its current behaviour, and route every setpoint through SunSpecStorageLogic::planSetpoint(). The plan orders the writes - ChaGriSet, StorCtl_Mod, then the rates - and appends the revert timer last, so the timer never starts counting against a setpoint that has not been written yet. Writes are issued sequentially rather than in parallel: the order is part of the contract with the hardware.
117 lines
3.9 KiB
C++
117 lines
3.9 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
*
|
|
* Copyright (C) 2013 - 2024, nymea GmbH
|
|
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
|
|
*
|
|
* This file is part of nymea.
|
|
*
|
|
* nymea is free software: you can redistribute it and/or modify it under the
|
|
* terms of the GNU General Public License as published by the Free Software
|
|
* Foundation, either version 3 of the License, or (at your option) any later
|
|
* version.
|
|
*
|
|
* nymea is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* nymea. If not, see <https://www.gnu.org/licenses/>.
|
|
*
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
#ifndef SUNSPECSTORAGELOGIC_H
|
|
#define SUNSPECSTORAGELOGIC_H
|
|
|
|
#include <QVector>
|
|
|
|
#include <models/sunspecstoragemodel.h>
|
|
|
|
/* Decision logic of the storage (model 124) handling, kept free of the
|
|
* integration framework so it can be exercised without a running nymea.
|
|
*
|
|
* Two things live here:
|
|
*
|
|
* - readStates() maps a storage block update onto the state values the plugin
|
|
* publishes. Having it in one place is what makes it possible to check that
|
|
* a state is fed from the very register the matching action writes to;
|
|
* - planSetpoint() turns a set of setpoints into an ordered sequence of
|
|
* register writes. Order matters on real hardware, and the revert timer
|
|
* must close every sequence.
|
|
*/
|
|
namespace SunSpecStorageLogic {
|
|
|
|
/* The registers of model 124 this plugin ever writes. */
|
|
enum Register {
|
|
RegisterChaGriSet,
|
|
RegisterStorCtlMod,
|
|
RegisterInWRte,
|
|
RegisterOutWRte,
|
|
RegisterRvrtTms
|
|
};
|
|
|
|
struct Write {
|
|
Register reg;
|
|
double value;
|
|
};
|
|
|
|
struct SetpointPlan {
|
|
/* Scale factors are not necessarily static: they may change with firmware
|
|
* and at runtime. Whenever a scaled quantity is written, its scale factor
|
|
* should be read again immediately before the write rather than reused
|
|
* from an earlier poll. */
|
|
bool rereadScaleFactor = false;
|
|
|
|
/* Ordered: ChaGriSet, StorCtl_Mod, InWRte/OutWRte, RvrtTms last. */
|
|
QVector<Write> writes;
|
|
|
|
/* Registers to read back and compare after the sequence. Several inverters
|
|
* accept a write and then ignore it without raising a Modbus exception, so
|
|
* an unverified setpoint is an unapplied setpoint. */
|
|
QVector<Register> verifyReadback;
|
|
};
|
|
|
|
struct StorageStates {
|
|
/* Percent of WChaMax, from InWRte / OutWRte - the very registers the
|
|
* chargingRate / dischargingRate actions write to. */
|
|
double chargingRate = 0;
|
|
double dischargingRate = 0;
|
|
|
|
/* Percent of WChaMax per second, from WChaGra / WDisChaGra. A different
|
|
* quantity from the two above, and read only on several inverters. */
|
|
double chargeGradient = 0;
|
|
double dischargeGradient = 0;
|
|
|
|
/* Both flags are reconciled from StorCtl_Mod. Reconciling only one lets the
|
|
* other drift silently as soon as the inverter changes it on its own. */
|
|
bool enableCharging = false;
|
|
bool enableDischarging = false;
|
|
|
|
bool gridCharging = false;
|
|
|
|
int batteryLevel = 0;
|
|
bool batteryCritical = false;
|
|
};
|
|
|
|
/* Value range of the InWRte / OutWRte setpoints of model 124. */
|
|
const double MinimumRatePercent = -100.0;
|
|
const double MaximumRatePercent = 100.0;
|
|
|
|
/* Range of InOutWRte_RvrtTms. */
|
|
const int MaximumRevertTimeSeconds = 28800;
|
|
|
|
StorageStates readStates(SunSpecStorageModel *storage);
|
|
|
|
/* True for the registers carried by InOutWRte_SF, i.e. those whose write should
|
|
* be preceded by a fresh read of that scale factor. */
|
|
bool isScaledRate(Register reg);
|
|
|
|
QString registerName(Register reg);
|
|
|
|
SetpointPlan planSetpoint(const QVector<Write> &setpoints, quint16 revertTime);
|
|
|
|
}
|
|
|
|
#endif // SUNSPECSTORAGELOGIC_H
|