using Sandbox.ModAPI.Ingame; using System.Collections.Generic; namespace IngameScript { partial class Program { [Flags] public enum FailoverState { Standby = 0x1, Failover = 0x2, Active = 0x4, } public class FailoverManager { private Program _program; private IConsole _console; private string _id; private int _rank; private bool _active = false; private SortedDictionary _nodes = new SortedDictionary(); public FailoverManager(Program program, IConsole console, MyIni ini) { _program = program; _console = new PrefixedConsole(console, "FailoverManager"); _ini = ini; // Read our config bool configExists = _ini.TryParse(_program.Me.CustomData); if (!configExists) { _console.Print("No failover config, assuming we are single instance."); _active = true; return; } _id = _ini.Get("failover", "id").ToString(); _rank = _ini.Get("failover", "rank").ToInt32(-1); if (_id == "" || _rank == -1) { _console.Print("Failover config invalid. Assuming we are single instance."); _active = true; return; } List allPBs = new List(); _program.GridTerminalSystem.GetBlocksOfType(allPBs, blockFilter); foreach (IMyProgrammableBlock node in allPBs) { _ini.TryParse(node.CustomData); string foreignId = _ini.Get("failover", "id"); if (foreignId != _id) continue; int foreignRank = _ini.Get("failover", "rank"); if (foreignRank == -1) continue; _nodes[foreignRank] = node; } } // returns true if we should become the active node public FailoverState ActiveCheck() { // Once we're the active node, we will stay active for the life of the script. // TODO: test the assumption that the script restarts from scratch on disable/enable... // if not we may need additional logic. if (_active) return FailoverState.Active; foreach (KeyValuePair kvp in _nodes) { // If we have a higher priority than the nodes we've checked so far, // we must be the active node. if (_rank < kvp.Key) { _active = true; return FailoverState.Failover | FailoverState.Active; } // We've found an active node with higher priority than us. if (!kvp.Value.Closed && kvp.Value.Enabled) return FailoverState.Standby; } // We're the last node standing. Let's hope we don't go down. _active = true; return FailoverState.Failover | FailoverState.Active; } private bool blockFilter(IMyProgrammableBlock block) { return block.IsSameConstructAs(_program.Me) && MyIni.HasSection(block.CustomData, "failover"); } } } }