protected virtual void Dry(CancellationTokenPair pair) { byte unitsToRemove; { using var lls = LaundryFlags.SpinLock(TimeSpan.FromSeconds(2)); unitsToRemove = lls.LoadedLaundryItem != LaundryItems.InvalidItem ? lls.LoadedLaundryItem.Dampness : throw new StateLogicErrorException( "It should not be possible to lack laundry during the wash cycle."); } byte unitsToDeduct = unitsToRemove; TimeSpan timeRequired = TimeToDecrementDampnessPerUnit * unitsToRemove; double randomFactor = RandomNumberSource.Next(1, 11) / 100.0; //introduce randomness factor of 10%, either direction Debug.Assert(randomFactor >= 0.0 && randomFactor <= 0.11); TimeSpan randomTimeToAddOrSub = timeRequired * randomFactor; Debug.Assert(randomTimeToAddOrSub <= timeRequired); bool negate = RandomNumberSource.Next(1, 3) == 1; randomTimeToAddOrSub = negate ? -randomTimeToAddOrSub : +randomTimeToAddOrSub; timeRequired += randomTimeToAddOrSub; Stopwatch sw = null; try { sw = HighPrecisionTimer; sw.Restart(); SimulateWait(pair, timeRequired); } catch (OperationCanceledException) { if (sw == null) { string log = "For some reason the stopwatch is null."; TerminationHelper.TerminateApplication(log); return; } TimeSpan elapsed = sw.Elapsed; double percentage = elapsed >= timeRequired ? 1.0 : elapsed / timeRequired; unitsToDeduct = Convert.ToByte(Math.Floor(unitsToRemove * percentage)); throw; } finally { sw?.Reset(); byte newSetting = (byte)(unitsToRemove - unitsToDeduct); Debug.Assert(newSetting <= unitsToRemove); using var lls = LaundryFlags.SpinLock(TimeSpan.FromSeconds(2)); lls.SetDampFactor(newSetting); Debug.Assert(lls.LoadedLaundryItem.Dampness <= unitsToRemove); } }
protected virtual void Soak(CancellationTokenPair token) { Stopwatch sw = null; try { Debug.WriteLine("Beginning Cleanse Soak"); byte oldDampness; byte newDampness; sw = HighPrecisionTimer; sw.Reset(); sw.Start(); { using LockedLaundryStatus lls = LaundryFlags.SpinLock(token.IndividualToken, TimeSpan.FromSeconds(2)); var res = lls.SoakLaundry() ?? throw new StateLogicErrorException( "It is supposed to be impossible to start the machine without laundry in it."); oldDampness = res.OldDampness; newDampness = res.NewDampness; } Debug.Assert(newDampness >= oldDampness); int dampnessUnitsIncrease = newDampness - oldDampness; TimeSpan totalTimeRequired = TimeToIncrementDampnessPerUnit * dampnessUnitsIncrease; TimeSpan timeRemaining = totalTimeRequired - sw.Elapsed; if (timeRemaining > TimeSpan.Zero) { SimulateWait(token, timeRemaining, "Beginning soak wait", "Ending soak wait"); } } catch (StateLogicErrorException ex) { Console.Error.WriteLineAsync(ex.ToString()); Environment.Exit(-1); } catch (TimeoutException) { Console.Error.WriteAsync( $"Unable to obtain lock in {nameof(Soak)} method of {nameof(WashTask)} task."); throw; } finally { sw?.Reset(); Debug.WriteLine("Ending soak."); } }
protected virtual void Cleanse(CancellationTokenPair token) { byte unitsToRemove; { using var lls = LaundryFlags.SpinLock(TimeSpan.FromSeconds(2)); unitsToRemove = lls.LoadedLaundryItem != LaundryItems.InvalidItem ? lls.LoadedLaundryItem.SoiledFactor : throw new StateLogicErrorException( "It should not be possible to lack laundry during the wash cycle."); } byte unitsToDeduct = unitsToRemove; TimeSpan timeRequired = TimeToRemoveOneUnitOfSoil * unitsToRemove; Stopwatch sw = null; try { sw = HighPrecisionTimer; sw.Restart(); if (timeRequired > TimeSpan.Zero) { SimulateWait(token, timeRequired, "Beginning cleanse wait."); } } catch (IndividualOperationCancelledException) { if (sw == null) { string log = "For some reason the stopwatch is null."; TerminationHelper.TerminateApplication(log); return; } TimeSpan elapsed = sw.Elapsed; double percentage = elapsed >= timeRequired ? 1.0 : elapsed / timeRequired; unitsToDeduct = Convert.ToByte(Math.Floor(unitsToRemove * percentage)); throw; } finally { sw?.Reset(); byte newSetting = (byte)(unitsToRemove - unitsToDeduct); Debug.Assert(newSetting <= unitsToRemove); using var lls = LaundryFlags.SpinLock(TimeSpan.FromSeconds(2)); lls.SetSoilFactor(newSetting); Debug.Assert(lls.LoadedLaundryItem.SoiledFactor <= unitsToRemove); } }
protected override TaskResult ExecuteTask(CancellationTokenPair pair) { bool needToClearError; using (var lsfLck = LaundryFlags.Lock(TimeSpan.FromSeconds(2))) { pair.ThrowIfCancellationRequested(); needToClearError = lsfLck.ExecuteQuery((in LaundryStatusFlags lsf) => lsf.ErrorRegistrationStatus != ErrorRegistrationStatus.NilStatus); } if (needToClearError) { using (var lsfLck = LaundryFlags.Lock(TimeSpan.FromSeconds(2))) { lsfLck.ExecuteAction((ref LaundryStatusFlags lsf) => { bool processing = lsf.ProcessError(); if (!processing) { throw new StateLogicErrorException( "Bad state ... error not being handled in correct sequence."); } }); } ClearError(pair); pair.ThrowIfCancellationRequested(); using (var lsfLck = LaundryFlags.Lock(TimeSpan.FromSeconds(2))) { lsfLck.ExecuteAction((ref LaundryStatusFlags lsf) => { bool cleared = lsf.ClearError(); if (cleared) { lsf.ResetError(); } else { throw new StateLogicErrorException("Ut oh, we couln't fix the error for some reason."); } }); } pair.ThrowIfCancellationRequested(); } TimeSpan simulatedTurnOnCycleTime; try { using var rgen = RandomNumberSource.RGenVault.SpinLock(); simulatedTurnOnCycleTime = TimeSpan.FromSeconds(rgen.Value.Next(1, 4)); } catch (TimeoutException ex) { Console.Error.WriteLineAsync($"Error getting lock on the rgen vault ... exception: [{ex}]"); simulatedTurnOnCycleTime = TimeSpan.FromSeconds(2); } SimulateWait(in pair, simulatedTurnOnCycleTime); TaskResult ret; using var lck = TaskResult.SpinLock(TimeSpan.FromSeconds(2)); ret = lck.Value = lck.Value.WithTerminationTaskResultType(TaskResultCode.SuccessResult); return(ret); }