refactor(redundancy): move ServiceLevelCalculator to Core.Cluster (shared, Runtime-reachable)

This commit is contained in:
Joseph Doherty
2026-06-15 12:45:17 -04:00
parent 7605f4d8fd
commit ff0f62db38
2 changed files with 2 additions and 2 deletions
@@ -1,43 +0,0 @@
using Akka.Cluster;
namespace ZB.MOM.WW.OtOpcUa.ControlPlane.Redundancy;
public readonly record struct NodeHealthInputs(
MemberStatus MemberState,
bool DbReachable,
bool OpcUaProbeOk,
bool Stale,
bool IsDriverRoleLeader);
/// <summary>
/// Pure ServiceLevel computation per design §6. Output range 0255, where higher = "more
/// authoritative." The OPC UA SDK exposes this as the node's <c>ServiceLevel</c> Variable and
/// redundant clients use it to pick which server to subscribe to.
///
/// Tiering:
/// - Member not Up/Joining: 0 (cluster cannot trust this node).
/// - DB reachable + OPC UA probe ok + not stale: 240 (full service).
/// - Stale config (DB reachable or not, OPC UA probe state ignored): 100 or 200 depending on DB.
/// - +10 bonus when this node holds the role-leader lease for the "driver" role.
/// </summary>
public static class ServiceLevelCalculator
{
/// <summary>Computes the service level (0-255) based on node health inputs.</summary>
/// <param name="h">The node health inputs including cluster state, database reachability, and probe status.</param>
/// <returns>A service level byte value (0-255) indicating node authority, where higher values indicate more authoritative nodes.</returns>
public static byte Compute(NodeHealthInputs h)
{
if (h.MemberState is not (MemberStatus.Up or MemberStatus.Joining))
return 0;
var basis = (h.DbReachable, h.OpcUaProbeOk, h.Stale) switch
{
(true, true, false) => 240,
(true, _, true) => 200,
(false, _, true) => 100,
_ => 0,
};
return (byte)Math.Clamp(basis + (h.IsDriverRoleLeader ? 10 : 0), 0, 255);
}
}