Add Machine state Controller
This commit is contained in:
parent
da9d2b1f40
commit
cbd4801568
|
|
@ -0,0 +1,376 @@
|
|||
using InnovEnergy.App.KacoCommunication.ESS;
|
||||
using InnovEnergy.Lib.Devices.Kaco92L3.DataType;
|
||||
#pragma warning disable CS8602 // Dereference of a possibly null reference.
|
||||
|
||||
namespace InnovEnergy.App.KacoCommunication.System;
|
||||
|
||||
public static class KacoCurrentStateController
|
||||
{
|
||||
// Call every 2 seconds
|
||||
public static Boolean ControlSystemState(this StatusRecord s)
|
||||
{
|
||||
var cs = s.InverterRecord.CurrentState; // 64201.CurrentState (1..12)
|
||||
s.StateMachine.State = (int)cs;
|
||||
|
||||
return cs switch
|
||||
{
|
||||
CurrentState.Off => State_Off(s),
|
||||
CurrentState.Sleeping => State_Sleeping(s),
|
||||
CurrentState.Starting => State_Starting(s),
|
||||
CurrentState.Mppt => State_Mppt(s),
|
||||
CurrentState.Throttled => State_Throttled(s),
|
||||
CurrentState.ShuttingDown => State_ShuttingDown(s),
|
||||
CurrentState.Fault => State_Fault(s),
|
||||
CurrentState.Standby => State_Standby(s),
|
||||
CurrentState.Precharge => State_Precharge(s),
|
||||
CurrentState.GridPreConnected=> State_GridPreConnected(s),
|
||||
CurrentState.GridConnected => State_GridConnected(s),
|
||||
CurrentState.NoErrorPending => State_NoErrorPending(s),
|
||||
_ => UnknownState(s)
|
||||
};
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Global rule: only allow power writes in 11 or 5
|
||||
// ─────────────────────────────────────────────
|
||||
private static void EnforcePowerRules(StatusRecord s)
|
||||
{
|
||||
var cs = s.InverterRecord.CurrentState;
|
||||
if (cs is not (CurrentState.GridConnected or CurrentState.Throttled))
|
||||
{
|
||||
// must be 0 outside (11) or (5)
|
||||
s.InverterRecord.ActivePowerSetPercent = 0f;
|
||||
//s.InverterRecord.ReactivePowerSetPercent = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// State handlers (based purely on CurrentState)
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
private static bool State_Off(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = "OFF: write limits (once) and request connect (11).";
|
||||
EnforcePowerRules(s);
|
||||
|
||||
// Write limits (ignore details)
|
||||
// WriteLimits();
|
||||
|
||||
// Always aim for running
|
||||
s.InverterRecord.RequestedState = ReuqestedState.GridConnected;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool State_Sleeping(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = "SLEEPING: write limits (once) and request connect (11).";
|
||||
EnforcePowerRules(s);
|
||||
|
||||
// s.InverterRecord.WriteLimits();
|
||||
s.InverterRecord.RequestedState = ReuqestedState.GridConnected;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool State_Standby(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = "STANDBY: write limits (once) and request connect (11).";
|
||||
EnforcePowerRules(s);
|
||||
|
||||
// s.InverterRecord.WriteLimits();
|
||||
s.InverterRecord.RequestedState = ReuqestedState.GridConnected;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool State_Mppt(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = "MPPT: keep requesting connect (11).";
|
||||
EnforcePowerRules(s);
|
||||
|
||||
s.InverterRecord.RequestedState = ReuqestedState.GridConnected;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool State_Starting(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = "STARTING: keep requesting connect (11), wait for 10/11/5.";
|
||||
EnforcePowerRules(s);
|
||||
|
||||
s.InverterRecord.RequestedState = ReuqestedState.GridConnected;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool State_Precharge(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = "PRECHARGE: keep requesting connect (11), wait.";
|
||||
EnforcePowerRules(s);
|
||||
|
||||
s.InverterRecord.RequestedState = ReuqestedState.GridConnected;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool State_GridPreConnected(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = "GRID_PRE_CONNECTED: keep requesting connect (11), wait for 11/5.";
|
||||
EnforcePowerRules(s);
|
||||
|
||||
s.InverterRecord.RequestedState = ReuqestedState.GridConnected;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool State_GridConnected(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = "GRID_CONNECTED: running. Power writes allowed.";
|
||||
|
||||
// Keep request latched
|
||||
s.InverterRecord.RequestedState = ReuqestedState.GridConnected;
|
||||
|
||||
// Here you may write power setpoints (your own targets)
|
||||
// Example:
|
||||
// s.InverterRecord.ControlMode = ControlModeEnum.RpcRemote;
|
||||
s.InverterRecord.ActivePowerSetPercent = s.Config.ActivePowerPercent;
|
||||
// s.InverterRecord.ReactivePowerSetPercent = s.Targets.ReactivePowerPercent;
|
||||
|
||||
return true; // end goal reached
|
||||
}
|
||||
|
||||
private static bool State_Throttled(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = "THROTTLED: still running. Power writes allowed.";
|
||||
s.InverterRecord.RequestedState = ReuqestedState.GridConnected;
|
||||
|
||||
// Power writes allowed here too
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool State_ShuttingDown(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = "SHUTTING_DOWN: keep requesting connect (11); will reconnect after reaching 8/1.";
|
||||
EnforcePowerRules(s);
|
||||
|
||||
s.InverterRecord.RequestedState = ReuqestedState.GridConnected;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool State_Fault(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = "FAULT: power=0 and acknowledge with RequestedState=1 (OFF).";
|
||||
EnforcePowerRules(s);
|
||||
|
||||
// Per doc: acknowledge uses RequestedState=1
|
||||
s.InverterRecord.RequestedState = ReuqestedState.Off;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool State_NoErrorPending(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = "NO_ERROR_PENDING: acknowledge with RequestedState=1 then controller will request 11 next cycles.";
|
||||
EnforcePowerRules(s);
|
||||
|
||||
// Per doc Step 8: set RequestedState to 1 to acknowledge
|
||||
s.InverterRecord.RequestedState = ReuqestedState.Off;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool UnknownState(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = $"UNKNOWN CurrentState={s.InverterRecord.CurrentState}. For safety, power=0 and request 11.";
|
||||
EnforcePowerRules(s);
|
||||
|
||||
s.InverterRecord.RequestedState = ReuqestedState.GridConnected;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
public static class Controller
|
||||
{
|
||||
private static UInt16 GetSystemState(this StatusRecord r)
|
||||
{
|
||||
if (r.InverterRecord != null)
|
||||
{
|
||||
return (UInt16)r.InverterRecord.CurrentState;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (UInt16)StateMachine.Default.State;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static Boolean ControlSystemState(this StatusRecord s)
|
||||
{
|
||||
s.StateMachine.State = s.GetSystemState();
|
||||
|
||||
var cs = s.InverterRecord?.CurrentState; // 64201.CurrentState (1..12)
|
||||
s.StateMachine.State = (UInt16)cs;
|
||||
|
||||
return s.StateMachine.State switch
|
||||
{
|
||||
1 => State_Off(s),
|
||||
2 => State_Sleeping(s),
|
||||
3 => State_Starting(s),
|
||||
4 => State_Mppt(s),
|
||||
5 => State_Throttled(s),
|
||||
6 => State_ShuttingDown(s),
|
||||
7 => State_Fault(s),
|
||||
8 => State_Standby(s),
|
||||
9 => State_Precharge(s),
|
||||
10 => State_GridPreConnected(s),
|
||||
11 => State_GridConnected(s),
|
||||
12 => State_NoErrorPending(s),
|
||||
_ => UnknownState(s)
|
||||
};
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Global rule: only allow power writes in 11 or 5
|
||||
// ─────────────────────────────────────────────
|
||||
private static void EnforcePowerRules(StatusRecord s)
|
||||
{
|
||||
var cs = s.InverterRecord?.CurrentState;
|
||||
|
||||
// must be 0 outside (11) or (5)
|
||||
s.InverterRecord.ActivePowerSetPercent = 0f;
|
||||
s.InverterRecord.ReactivePowerSetPercent = 0f; }
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// State handlers (based purely on CurrentState)
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
private static Boolean State_Off(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = "OFF: write limits (once) and request connect (11).";
|
||||
EnforcePowerRules(s);
|
||||
|
||||
// Write limits (ignore details)
|
||||
s.InverterRecord.WriteLimits();
|
||||
|
||||
// Always aim for running
|
||||
s.InverterRecord.RequestedState = ReuqestedState.GridConnected;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private static bool State_Sleeping(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = "SLEEPING: write limits (once) and request connect (11).";
|
||||
EnforcePowerRules(s);
|
||||
|
||||
s.InverterRecord.WriteLimits();
|
||||
s.InverterRecord.RequestedState = ReuqestedState.GridConnected;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool State_Standby(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = "STANDBY: write limits (once) and request connect (11).";
|
||||
EnforcePowerRules(s);
|
||||
|
||||
s.InverterRecord.WriteLimits();
|
||||
s.InverterRecord.RequestedState = ReuqestedState.GridConnected;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool State_Mppt(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = "MPPT: keep requesting connect (11).";
|
||||
EnforcePowerRules(s);
|
||||
|
||||
s.InverterRecord.RequestedState = ReuqestedState.GridConnected;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool State_Starting(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = "STARTING: keep requesting connect (11), wait for 10/11/5.";
|
||||
EnforcePowerRules(s);
|
||||
|
||||
s.InverterRecord.RequestedState = ReuqestedState.GridConnected;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool State_Precharge(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = "PRECHARGE: keep requesting connect (11), wait.";
|
||||
EnforcePowerRules(s);
|
||||
|
||||
s.InverterRecord.RequestedState = ReuqestedState.GridConnected;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool State_GridPreConnected(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = "GRID_PRE_CONNECTED: keep requesting connect (11), wait for 11/5.";
|
||||
EnforcePowerRules(s);
|
||||
|
||||
s.InverterRecord.RequestedState = ReuqestedState.GridConnected;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool State_GridConnected(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = "GRID_CONNECTED: running. Power writes allowed.";
|
||||
|
||||
// Keep request latched
|
||||
s.InverterRecord.RequestedState = ReuqestedState.GridConnected;
|
||||
|
||||
// Here you may write power setpoints (your own targets)
|
||||
// Example:
|
||||
// s.InverterRecord.ControlMode = ControlModeEnum.RpcRemote;
|
||||
// s.InverterRecord.ActivePowerSetPercent = s.Targets.ActivePowerPercent;
|
||||
// s.InverterRecord.ReactivePowerSetPercent = s.Targets.ReactivePowerPercent;
|
||||
|
||||
return true; // end goal reached
|
||||
}
|
||||
|
||||
private static bool State_Throttled(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = "THROTTLED: still running. Power writes allowed.";
|
||||
s.InverterRecord.RequestedState = ReuqestedState.GridConnected;
|
||||
|
||||
// Power writes allowed here too
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool State_ShuttingDown(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = "SHUTTING_DOWN: keep requesting connect (11); will reconnect after reaching 8/1.";
|
||||
EnforcePowerRules(s);
|
||||
|
||||
s.InverterRecord.RequestedState = ReuqestedState.GridConnected;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool State_Fault(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = "FAULT: power=0 and acknowledge with RequestedState=1 (OFF).";
|
||||
EnforcePowerRules(s);
|
||||
|
||||
// Per doc: acknowledge uses RequestedState=1
|
||||
s.InverterRecord.RequestedState = ReuqestedState.Off;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool State_NoErrorPending(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = "NO_ERROR_PENDING: acknowledge with RequestedState=1 then controller will request 11 next cycles.";
|
||||
EnforcePowerRules(s);
|
||||
|
||||
// Per doc Step 8: set RequestedState to 1 to acknowledge
|
||||
s.InverterRecord.RequestedState = ReuqestedState.Off;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool UnknownState(StatusRecord s)
|
||||
{
|
||||
s.StateMachine.Message = $"UNKNOWN CurrentState={s.InverterRecord.CurrentState}. For safety, power=0 and request 11.";
|
||||
EnforcePowerRules(s);
|
||||
|
||||
s.InverterRecord.RequestedState = ReuqestedState.GridConnected;
|
||||
return false;
|
||||
}
|
||||
|
||||
}*/
|
||||
Loading…
Reference in New Issue