コード例 #1
0
 private static void StopTimer(ControlledTimer timer)
 {
     if (timer != null)
     {
         timer.Stop();
     }
 }
コード例 #2
0
        public async Task SetShotButtonAsync(bool value)
        {
            await Task.Delay(1);

            using (await this.Lock.AcquireAsync())
            {
                this.ShotButton = value;

                if (this.ShotButton)
                {
                    // should never turn on the make shots button when there is no water
                    if (this.WaterLevel <= 0)
                    {
                        Specification.Assert(false, "Please do not turn on shot maker if there is no water");
                    }
                }

                if (value && this.ShotTimer == null)
                {
                    // start monitoring the coffee level.
                    this.ShotTimer = new ControlledTimer("ShotTimer", TimeSpan.FromSeconds(1), this.MonitorShot);
                }
                else if (!value && this.ShotTimer != null)
                {
                    StopTimer(this.ShotTimer);
                    this.ShotTimer = null;
                }
            }
        }
コード例 #3
0
        private async Task OnGrinderButtonChanged(bool value)
        {
            using (await this.Lock.AcquireAsync())
            {
                this.GrinderButton = value;
                if (this.GrinderButton)
                {
                    // should never turn on the grinder when there is no coffee to grind
                    if (this.HopperLevel <= 0)
                    {
                        Specification.Assert(false, "Please do not turn on grinder if there are no beans in the hopper");
                    }
                }

                if (value && this.CoffeeLevelTimer == null)
                {
                    // start monitoring the coffee level.
                    this.CoffeeLevelTimer = new ControlledTimer("CoffeeLevelTimer", TimeSpan.FromSeconds(0.1), this.MonitorGrinder);
                }
                else if (!value && this.CoffeeLevelTimer != null)
                {
                    StopTimer(this.CoffeeLevelTimer);
                    this.CoffeeLevelTimer = null;
                }
            }
        }
コード例 #4
0
        private void MonitorShot()
        {
            Task.Run(async() =>
            {
                // one second of running water completes the shot.
                using (await this.Lock.AcquireAsync())
                {
                    this.WaterLevel -= 1;
                    // turn off the water.
                    this.ShotButton = false;
                    this.ShotTimer  = null;
                }

                // event callbacks should not be inside the lock otherwise we could get deadlocks.
                if (this.WaterLevel > 0)
                {
                    if (this.ShotComplete != null)
                    {
                        this.ShotComplete(this, true);
                    }
                }
                else
                {
                    if (this.WaterEmpty != null)
                    {
                        this.WaterEmpty(this, true);
                    }
                }
            });
        }
コード例 #5
0
        private void MonitorWaterTemperature()
        {
            double temp = this.WaterTemperature;

            if (this.WaterHeaterButton)
            {
                // Note: when running in production mode we run forever, and it is fun
                // to watch the water heat up and cool down.   But in test mode this creates
                // too many async events to explore which makes the test slow.  So in test
                // mode we short circuit this process and jump straight to the boundry conditions.
                if (!this.RunSlowly && temp < 99)
                {
                    temp = 99;
                }

                // every time interval the temperature increases by 10 degrees up to 100 degrees
                if (temp < 100)
                {
                    temp = (int)temp + 10;
                    this.WaterTemperature = temp;
                    if (this.WaterTemperatureChanged != null)
                    {
                        this.WaterTemperatureChanged(this, this.WaterTemperature);
                    }
                }
                else
                {
                    if (this.WaterHot != null)
                    {
                        this.WaterHot(this, true);
                    }
                }
            }
            else
            {
                // then it is cooling down to room temperature, more slowly.
                if (temp > 70)
                {
                    temp -= 0.1;
                    this.WaterTemperature = temp;
                }
            }

            // start another callback.
            this.WaterHeaterTimer = new ControlledTimer("WaterHeaterTimer", TimeSpan.FromSeconds(0.1), this.MonitorWaterTemperature);
        }
コード例 #6
0
        public MockSensors(bool runSlowly)
        {
            this.Lock            = AsyncLock.Create();
            this.RunSlowly       = runSlowly;
            this.RandomGenerator = Generator.Create();

            // The use of randomness here makes this mock a more interesting test as it will
            // make sure the coffee machine handles these values correctly.
            this.WaterLevel             = this.RandomGenerator.NextInteger(100);
            this.HopperLevel            = this.RandomGenerator.NextInteger(100);
            this.WaterHeaterButton      = false;
            this.WaterTemperature       = this.RandomGenerator.NextInteger(50) + 30;
            this.GrinderButton          = false;
            this.PortaFilterCoffeeLevel = 0;
            this.ShotButton             = false;
            this.DoorOpen         = this.RandomGenerator.NextBoolean(5);
            this.WaterHeaterTimer = new ControlledTimer("WaterHeaterTimer", TimeSpan.FromSeconds(0.1), this.MonitorWaterTemperature);
        }
コード例 #7
0
        internal void OnStopTest()
        {
            if (!this.IsInitialized)
            {
                // not ready!
                return;
            }

            if (this.HaltTimer != null)
            {
                this.HaltTimer.Stop();
                this.HaltTimer = null;
            }

            // Halt the CoffeeMachine.  HaltEvent is async and we must ensure the
            // CoffeeMachine is really halted before we create a new one because MockSensors
            // will get confused if two CoffeeMachines are running at the same time.
            // So we've implemented a terminate handshake here.  We send event to the CoffeeMachine
            // to terminate, and it sends back a HaltedEvent when it really has been halted.
            this.Log.WriteLine("forcing termination of CoffeeMachine.");
            Task.Run(this.CoffeeMachine.TerminateAsync);
        }
コード例 #8
0
        public async Task SetPowerSwitchAsync(bool value)
        {
            await Task.Delay(1);

            // NOTE: you should not use C# locks that interact with Tasks (like Task.Run) because
            // it can result in deadlocks, instead use the Coyote AsyncLock as follows.
            using (await this.Lock.AcquireAsync())
            {
                this.PowerOn = value;
                if (!this.PowerOn)
                {
                    // master power override then also turns everything else off for safety!
                    this.WaterHeaterButton = false;
                    this.GrinderButton     = false;
                    this.ShotButton        = false;

                    StopTimer(this.CoffeeLevelTimer);
                    this.CoffeeLevelTimer = null;

                    StopTimer(this.ShotTimer);
                    this.ShotTimer = null;
                }
            }
        }
コード例 #9
0
        public async Task RunTest()
        {
            bool halted = true;

            while (this.RunForever || this.Iterations <= 1)
            {
                this.Log.WriteLine("#################################################################");

                // Create a new CoffeeMachine instance
                string error = null;
                if (halted)
                {
                    this.Log.WriteLine("starting new CoffeeMachine iteration {0}.", this.Iterations);
                    this.IsInitialized = false;
                    this.CoffeeMachine = new CoffeeMachine();
                    halted             = false;
                    this.IsInitialized = await this.CoffeeMachine.InitializeAsync(this.Sensors);

                    if (!this.IsInitialized)
                    {
                        error = "init failed";
                    }
                }

                if (error == null)
                {
                    // Setup a timer to randomly kill the coffee machine.   When the timer fires
                    // we will restart the coffee machine and this is testing that the machine can
                    // recover gracefully when that happens.
                    this.HaltTimer = new ControlledTimer("HaltTimer", TimeSpan.FromSeconds(this.RandomGenerator.NextInteger(7) + 1), new Action(this.OnStopTest));

                    // Request a coffee!
                    var shots = this.RandomGenerator.NextInteger(3) + 1;
                    error = await this.CoffeeMachine.MakeCoffeeAsync(shots);
                }

                if (string.Compare(error, "<halted>", StringComparison.OrdinalIgnoreCase) == 0)
                {
                    // then OnStopTest did it's thing, so it is time to create new coffee machine.
                    this.Log.WriteWarning("CoffeeMachine is halted.");
                    halted = true;
                }
                else if (!string.IsNullOrEmpty(error))
                {
                    this.Log.WriteWarning("CoffeeMachine reported an error.");
                    this.RunForever = false; // no point trying to make more coffee.
                    this.Iterations = 10;
                }
                else
                {
                    // in this case we let the same CoffeeMachine continue on then.
                    this.Log.WriteLine("CoffeeMachine completed the job.");
                }

                this.Iterations++;
            }

            // Shutdown the sensors because test is now complete.
            this.Log.WriteLine("Test is complete, press ENTER to continue...");
            await this.Sensors.TerminateAsync();
        }
コード例 #10
0
        private void MonitorGrinder()
        {
            // Every time interval the portafilter fills 10%.
            // When it's full the grinder turns off automatically, unless the hopper is empty in which case
            // grinding does nothing!

            Task.Run(async() =>
            {
                bool changed        = false;
                bool notifyEmpty    = false;
                bool turnOffGrinder = false;

                using (await this.Lock.AcquireAsync())
                {
                    double hopperLevel = this.HopperLevel;
                    if (hopperLevel > 0)
                    {
                        double level = this.PortaFilterCoffeeLevel;

                        // Note: when running in production mode we run in real time, and it is fun
                        // to watch the portafilter filling up.   But in test mode this creates
                        // too many async events to explore which makes the test slow.  So in test
                        // mode we short circuit this process and jump straight to the boundry conditions.
                        if (!this.RunSlowly && level < 99)
                        {
                            hopperLevel -= 98 - (int)level;
                            this.Log.WriteLine("### HopperLevel: RunSlowly = {0}, level = {1}", this.RunSlowly, hopperLevel);
                            level = 99;
                        }

                        if (level < 100)
                        {
                            level += 10;
                            this.PortaFilterCoffeeLevel = level;
                            changed = true;
                            if (level >= 100)
                            {
                                turnOffGrinder = true;
                            }
                        }

                        // and the hopper level drops by 0.1 percent
                        hopperLevel -= 1;

                        this.HopperLevel = hopperLevel;
                    }

                    if (this.HopperLevel <= 0)
                    {
                        hopperLevel = 0;
                        notifyEmpty = true;

                        StopTimer(this.CoffeeLevelTimer);
                        this.CoffeeLevelTimer = null;
                    }
                }

                if (turnOffGrinder)
                {
                    // turning off the grinder is automatic
                    await this.OnGrinderButtonChanged(false);
                }

                // event callbacks should not be inside the lock otherwise we could get deadlocks.
                if (notifyEmpty && this.HopperEmpty != null)
                {
                    this.HopperEmpty(this, true);
                }

                if (changed && this.PortaFilterCoffeeLevelChanged != null)
                {
                    this.PortaFilterCoffeeLevelChanged(this, this.PortaFilterCoffeeLevel);
                }

                if (this.HopperLevel <= 0 && this.HopperEmpty != null)
                {
                    this.HopperEmpty(this, true);
                }

                // start another callback.
                this.CoffeeLevelTimer = new ControlledTimer("WaterHeaterTimer", TimeSpan.FromSeconds(0.1), this.MonitorGrinder);
            });
        }