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; } }*/