using InnovEnergy.App.SodiStoreMax.Ess; using InnovEnergy.App.SodiStoreMax.SaliMaxRelays; using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc; using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc; using static InnovEnergy.Lib.Devices.Trumpf.SystemControl.DataTypes.GridType; namespace InnovEnergy.App.SodiStoreMax.System; public static class Controller { private static Int32 GetSystemState(this StatusRecord r) { var relays = r.Relays; if (relays is null) return 101; // Message = "Panic: relay device is not available!", var acDcs = r.AcDc; if (acDcs.NotAvailable()) return 102; var k4 = acDcs.AllGridTied() ? 0 : acDcs.AllIsland() ? 1 : 4; var k5 = acDcs.AllDisabled() ? 0 : acDcs.AllEnabled() ? 1 : 4; if (k4 == 4 || k5 == 4) return 103; //Message = "Panic: ACDCs have unequal grid types or power stage", var nInverters = r.AcDc.Devices.Count; var k1 = relays.K1GridBusIsConnectedToGrid ? 1 : 0; var k2 = relays.K2IslandBusIsConnectedToGridBus ? 1 : 0; var k3 = relays.K3InverterIsConnectedToIslandBus.Take(nInverters).Any(c => c) ? 1 : 0; // states as defined in states excel sheet return 1 * k1 + 2 * k2 + 4 * k3 + 8 * k4 + 16 * k5; } public static Boolean ControlSystemState(this StatusRecord s) { s.StateMachine.State = s.GetSystemState(); return s.StateMachine.State switch { 0 => State0(s), 1 => State1(s), 2 => State2(s), 3 => State3(s), 4 => State4(s), 5 => State5(s), 6 => State6(s), 7 => State7(s), 8 => State8(s), 9 => State9(s), 10 => State10(s), 11 => State11(s), 12 => State12(s), 13 => State13(s), 14 => State14(s), 15 => State15(s), 16 => State16(s), 17 => State17(s), 18 => State18(s), 19 => State19(s), 20 => State20(s), 21 => State21(s), 22 => State22(s), 23 => State23(s), 24 => State24(s), 25 => State25(s), 26 => State26(s), 27 => State27(s), 28 => State28(s), 29 => State29(s), 30 => State30(s), 31 => State31(s), 101 => State101(s), 102 => State102(s), 103 => State103(s), _ => UnknownState(s) }; } private static Boolean NotAvailable(this AcDcDevicesRecord acDcs) { return acDcs.SystemControl == null || acDcs.Devices.Count == 0; } private static Boolean State0(StatusRecord s) { s.StateMachine.Message = "Ac/Dc are off. Switching to Island Mode."; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableIslandMode(); s.Relays.DisconnectIslandBusFromGrid(); return false; // => 8 } private static Boolean State1(StatusRecord s) { s.StateMachine.Message = "Grid Tied mode active, closing k2"; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableGridTieMode(); s.Relays.ConnectIslandBusToGrid(); return false; // => 3 } private static Boolean State2(StatusRecord s) { s.StateMachine.Message = ""; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableGridTieMode(); s.Relays.DisconnectIslandBusFromGrid(); return false; // => 0 } private static Boolean State3(StatusRecord s) { s.StateMachine.Message = "K2 closed, Turning on Ac/Dc"; s.DcDc.Enable(); s.AcDc.Enable(); s.AcDc.EnableGridTieMode(); s.Relays.ConnectIslandBusToGrid(); return false; // => 19 } private static Boolean State4(StatusRecord s) { s.StateMachine.Message = "K2 is open, waiting K3 to open"; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableGridTieMode(); s.Relays.DisconnectIslandBusFromGrid(); return true; // => 0 } private static Boolean State5(StatusRecord s) { s.StateMachine.Message = ""; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableGridTieMode(); s.Relays.DisconnectIslandBusFromGrid(); return false; // => ? } private static Boolean State6(StatusRecord s) { s.StateMachine.Message = "Inverters are off, opening K2"; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableGridTieMode(); s.Relays.DisconnectIslandBusFromGrid(); return true; // => 4 } private static Boolean State7(StatusRecord s) { s.StateMachine.Message = ""; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableGridTieMode(); s.Relays.ConnectIslandBusToGrid(); return false; // => ? } private static Boolean State8(StatusRecord s) { s.StateMachine.Message = "Ac/Dc are off and in Island Mode."; s.DcDc.Enable(); s.AcDc.Enable(); s.AcDc.EnableIslandMode(); s.Relays.DisconnectIslandBusFromGrid(); return true; // => 24 } private static Boolean State9(StatusRecord s) { s.StateMachine.Message = "Ac/Dc are disconnected from Island Bus. Switching to GridTie Mode."; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableGridTieMode(); s.Relays.DisconnectIslandBusFromGrid(); return false; // => 1 } private static Boolean State10(StatusRecord s) { s.StateMachine.Message = ""; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableIslandMode(); s.Relays.DisconnectIslandBusFromGrid(); return false; // => 8 } private static Boolean State11(StatusRecord s) { s.StateMachine.Message = ""; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableIslandMode(); s.Relays.DisconnectIslandBusFromGrid(); return false; // => 9 } private static Boolean State12(StatusRecord s) { s.StateMachine.Message = ""; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableIslandMode(); s.Relays.DisconnectIslandBusFromGrid(); return false; // => 8 } private static Boolean State13(StatusRecord s) { s.StateMachine.Message = "Ac/Dc are off. Waiting for them to disconnect from Island Bus."; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableIslandMode(); s.Relays.DisconnectIslandBusFromGrid(); return true; // => 9 } private static Boolean State14(StatusRecord s) { s.StateMachine.Message = ""; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableIslandMode(); s.Relays.DisconnectIslandBusFromGrid(); return false; // => 12 } private static Boolean State15(StatusRecord s) { s.StateMachine.Message = ""; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableIslandMode(); s.Relays.DisconnectIslandBusFromGrid(); return false; // => 13 } private static Boolean State16(StatusRecord s) { s.StateMachine.Message = ""; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableGridTieMode(); s.Relays.DisconnectIslandBusFromGrid(); return false; // => 0 } private static Boolean State17(StatusRecord s) { s.StateMachine.Message = ""; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableGridTieMode(); s.Relays.DisconnectIslandBusFromGrid(); return false; // => 1 } private static Boolean State18(StatusRecord s) { s.StateMachine.Message = ""; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableGridTieMode(); s.Relays.DisconnectIslandBusFromGrid(); return false; // => 0 } private static Boolean State19(StatusRecord s) { s.StateMachine.Message = "Waiting for Ac/Dc to connect to Island Bus"; s.DcDc.Enable(); s.AcDc.Enable(); s.AcDc.EnableGridTieMode(); s.Relays.ConnectIslandBusToGrid(); return true; // => 23 } private static Boolean State20(StatusRecord s) { s.StateMachine.Message = ""; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableGridTieMode(); s.Relays.DisconnectIslandBusFromGrid(); return false; // => 4 } private static Boolean State21(StatusRecord s) { s.StateMachine.Message = ""; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableGridTieMode(); s.Relays.DisconnectIslandBusFromGrid(); return false; // => 5 } private static Boolean State22(StatusRecord s) { s.StateMachine.Message = "K1 opened, switching inverters off"; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableGridTieMode(); s.Relays.ConnectIslandBusToGrid(); return true; // => 6 } private static Boolean State23(StatusRecord s) { s.StateMachine.Message = "ESS"; s.DcDc.Enable(); s.AcDc.Enable(); s.AcDc.EnableGridTieMode(); s.Relays.ConnectIslandBusToGrid(); return true; // => 22 } private static Boolean State24(StatusRecord s) { s.StateMachine.Message = "Inverter are on waiting for k3 to close"; s.DcDc.Enable(); s.AcDc.Enable(); s.AcDc.EnableIslandMode(); s.Relays.DisconnectIslandBusFromGrid(); return false; // => 28 } private static Boolean State25(StatusRecord s) { s.StateMachine.Message = ""; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableIslandMode(); s.Relays.DisconnectIslandBusFromGrid(); return false; // => 9 } private static Boolean State26(StatusRecord s) { s.StateMachine.Message = ""; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableIslandMode(); s.Relays.DisconnectIslandBusFromGrid(); return false; // => 10 } private static Boolean State27(StatusRecord s) { s.StateMachine.Message = "K2 open and enable island mode"; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableIslandMode(); s.Relays.DisconnectIslandBusFromGrid(); return false; // => 9 } private static Boolean State28(StatusRecord s) { s.StateMachine.Message = "Island Mode"; s.DcDc.Enable(); s.AcDc.Enable(); s.AcDc.EnableIslandMode(); s.Relays.DisconnectIslandBusFromGrid(); return false; // => 29 } private static Boolean State29(StatusRecord s) { s.StateMachine.Message = "K1 closed, Switching off Inverters and moving to grid tie"; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableIslandMode(); s.Relays.DisconnectIslandBusFromGrid(); return false; // => 13 } private static Boolean State30(StatusRecord s) { s.StateMachine.Message = ""; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableIslandMode(); s.Relays.DisconnectIslandBusFromGrid(); return false; // => 14 } private static Boolean State31(StatusRecord s) { s.StateMachine.Message = ""; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableIslandMode(); s.Relays.DisconnectIslandBusFromGrid(); return false; // => 13 } private static Boolean State101(StatusRecord s) { s.StateMachine.Message = "Relay device is not available"; return s.EnableSafeDefaults(); } private static Boolean State102(StatusRecord s) { s.StateMachine.Message = "ACDCs not available"; return s.EnableSafeDefaults(); } private static Boolean State103(StatusRecord s) { s.StateMachine.Message = "Panic: ACDCs have unequal grid types or PowerStage"; s.AcDc.Enable(); return false; } // private static Boolean State104(StatusRecord s) // { // s.StateMachine.Message = "Panic: DCDCs not available"; // return s.EnableSafeDefaults(); // } private static Boolean UnknownState(StatusRecord s) { // "Unknown System State" return s.EnableSafeDefaults(); } private static Boolean AllDisabled(this AcDcDevicesRecord acDcs) { return acDcs.Devices.All(d => !d.Control.PowerStageEnable); } private static Boolean AllEnabled(this AcDcDevicesRecord acDcs) { return acDcs.Devices.All(d => d.Control.PowerStageEnable); } private static Boolean AllTheSame(this AcDcDevicesRecord acDcs) { return acDcs.Devices .Select(d => d.Control.PowerStageEnable) .Distinct() .Count() == 1; } private static Boolean AllGridTied(this AcDcDevicesRecord acDcs) { return acDcs.Devices.All(d => d.Status.ActiveGridType is GridTied380V60Hz) || acDcs.Devices.All(d => d.Status.ActiveGridType is GridTied400V50Hz) || acDcs.Devices.All(d => d.Status.ActiveGridType is GridTied480V60Hz); } private static Boolean AllIsland(this AcDcDevicesRecord acDcs) { return acDcs.Devices.All(d => d.Status.ActiveGridType is Island400V50Hz) || acDcs.Devices.All(d => d.Status.ActiveGridType is Island480V60Hz); } private static void ForAll(this IEnumerable ts, Action action) { foreach (var t in ts) action(t); } private static void Disable(this AcDcDevicesRecord acDc) { acDc.Devices .Select(d => d.Control) .ForAll(c => c.PowerStageEnable = false); } //this is must be deleted private static void Disable(this DcDcDevicesRecord dcDc) { // For Test purpose, The transition from island mode to grid tier and vis versa may not need to disable Dc/Dc. // This will keep the Dc link powered. // dcDc.Devices // .Select(d => d.Control) // .ForAll(c => c.PowerStageEnable = false); } private static void Enable(this AcDcDevicesRecord acDc) { acDc.Devices .Select(d => d.Control) .ForAll(c => c.PowerStageEnable = true); } private static void Enable(this DcDcDevicesRecord dcDc) { dcDc.Devices .Select(d => d.Control) .ForAll(c => c.PowerStageEnable = true); } private static void EnableGridTieMode(this AcDcDevicesRecord acDc) { acDc.Devices .Select(d => d.Control) .ForAll(c => c.Ac.GridType = GridTied400V50Hz); // TODO: config grid type } private static void EnableIslandMode(this AcDcDevicesRecord acDc) { acDc.Devices .Select(d => d.Control) .ForAll(c => c.Ac.GridType = Island400V50Hz); // TODO: config grid type } private static void DisconnectIslandBusFromGrid(this IRelaysRecord? relays) { if (relays is not null) relays.K2ConnectIslandBusToGridBus = false; } private static void ConnectIslandBusToGrid(this IRelaysRecord? relays) { if (relays is not null) relays.K2ConnectIslandBusToGridBus = true; } public static Boolean EnableSafeDefaults(this StatusRecord s) { // After some tests, the safe state is switch off inverter and keep the last state of K2, Dc/Dc and Grid type to avoid conflict. // s.DcDc.Disable(); s.AcDc.Disable(); // Maybe comment this to avoid opening/closing K3 // s.AcDc.EnableGridTieMode(); // s.Relays.DisconnectIslandBusFromGrid(); return false; } public static DcDcDevicesRecord ResetAlarms(this DcDcDevicesRecord dcDcStatus) { var sc = dcDcStatus.SystemControl; if (sc is not null) sc.ResetAlarmsAndWarnings = sc.Alarms.Any(); foreach (var d in dcDcStatus.Devices) d.Control.ResetAlarmsAndWarnings = d.Status.Alarms.Any() || d.Status.Warnings.Any(); return dcDcStatus; } public static AcDcDevicesRecord ResetAlarms(this AcDcDevicesRecord acDcStatus) { var sc = acDcStatus.SystemControl; if (sc is not null) sc.ResetAlarmsAndWarnings = sc.Alarms.Any() || sc.Warnings.Any(); foreach (var d in acDcStatus.Devices) d.Control.ResetAlarmsAndWarnings = d.Status.Alarms.Any() || d.Status.Warnings.Any(); return acDcStatus; } }