private static void AllOff(MCUBoard board, List <IOconfOut230Vac> ports)
 {
     try
     {
         board.SafeWriteLine("off", CancellationToken.None);
         foreach (var port in ports)
         {
             if (port.HasOnSafeState)
             {
                 board.SafeWriteLine($"p{port.PortNumber} on", CancellationToken.None);
             }
         }
     }
     catch (Exception ex)
     {
         CALog.LogErrorAndConsoleLn(LogID.A, $"Error detected while attempting to set ports to default positions for board {board.ToShortDescription()}", ex);
     }
 }
 private static async Task DoPortActions(NewVectorReceivedArgs vector, MCUBoard board, IOconfOut230Vac port, SwitchboardAction[] lastActions, CancellationToken token)
 {
     if (token.IsCancellationRequested)
     {
         return;
     }
     try
     {
         var action = SwitchboardAction.FromVectorSamples(vector, port.Name);
         if (action.Equals(lastActions[port.PortNumber - 1]))
         {
             return; // no action changes has been requested since the last action taken on the heater.
         }
         var onSeconds = action.GetRemainingOnSeconds(vector.GetVectorTime());
         if (onSeconds <= 0)
         {
             await board.SafeWriteLine($"p{port.PortNumber} off", token);
         }
         else if (onSeconds == int.MaxValue)
         {
             await board.SafeWriteLine($"p{port.PortNumber} on", token);
         }
         else
         {
             await board.SafeWriteLine($"p{port.PortNumber} on {onSeconds}", token);
         }
         lastActions[port.PortNumber - 1] = action;
     }
     catch (TimeoutException)
     {
         // we don't want logging at this level as the caller handles this in a way that reduces the amount of noise for failures that last many vectors.
         throw;
     }
     catch (Exception)
     {
         CALog.LogErrorAndConsoleLn(LogID.A, $"Failed executing port action for board {board.BoxName} port {port.Name} - {port.Name}");
         throw; // this will log extra info and avoid extra board actions on this cycle
     }
 }