Esempio n. 1
0
 public static BackgroundWorker InitializeFanController2(ArduinoDriver.ArduinoDriver driver, Dispatcher MainDispatcher)
 {
     //Turns fan set 2 on, off, up and down
     //The idea here is to start the fan at half speed, see if and see it has any noticable effect on temperature -- if the temperature drops maintain the speed, if it rises increase the speed, if it drops lower the speed
     FanController2 = new BackgroundWorker();
     FanController2.WorkerSupportsCancellation = true;
     FanController2.DoWork += new DoWorkEventHandler((state, args) =>
     {
         int LastTemp  = Main.CondensorTemp;
         byte FanSpeed = 125;
         do
         {
             if (Main.Run != true || FanController2.CancellationPending == true)
             {
                 break;
             }
             try
             {
                 System.Threading.Thread.Sleep(10000);
                 if (Main.CondensorTemp > LastTemp)
                 {
                     FanSpeed = Convert.ToByte((int)FanSpeed + 5);
                     MainDispatcher.Invoke(new Action(() => { driver.Send(new AnalogWriteRequest(SystemProperties.FanController1, FanSpeed)); }));
                 }
                 else if (Main.CondensorTemp < LastTemp)
                 {
                     FanSpeed = Convert.ToByte((int)FanSpeed - 5);
                     MainDispatcher.Invoke(new Action(() => { driver.Send(new AnalogWriteRequest(SystemProperties.FanController1, FanSpeed)); }));
                 }
             }
             catch { MainDispatcher.Invoke(new Action(() => { driver = Periphrials.InitializeArduinoDriver(); })); }
         } while (true);
     });
     return(FanController2);
 }
Esempio n. 2
0
 public static BackgroundWorker InitializeFanController1(ArduinoDriver.ArduinoDriver driver, Dispatcher MainDispatcher)
 {
     //Turns fan set 1 on, off, up and down
     //The target temperature should be just under the distillation plateau temp so it doesnt slow down the distillation process but keeps the column cool enough that only the target substance can come over
     FanController1 = new BackgroundWorker();
     FanController1.WorkerSupportsCancellation = true;
     FanController1.DoWork += new DoWorkEventHandler((state, args) =>
     {
         int TargetTemp = Main.PlateauTemp - 1;
         byte FanSpeed  = 50;
         do
         {
             if (Main.Run != true || FanController1.CancellationPending == true)
             {
                 break;
             }
             try
             {
                 System.Threading.Thread.Sleep(10000);
                 if (Main.RefluxTemp > TargetTemp)
                 {
                     FanSpeed = Convert.ToByte((int)FanSpeed + 5);
                     MainDispatcher.Invoke(new Action(() => { driver.Send(new AnalogWriteRequest(SystemProperties.FanController1, FanSpeed)); }));
                 }
                 else if (Main.RefluxTemp < TargetTemp)
                 {
                     FanSpeed = Convert.ToByte((int)FanSpeed - 5);
                     MainDispatcher.Invoke(new Action(() => { driver.Send(new AnalogWriteRequest(SystemProperties.FanController1, FanSpeed)); }));
                 }
             }
             catch { MainDispatcher.Invoke(new Action(() => { driver = Periphrials.InitializeArduinoDriver(); })); }
         } while (true);
     });
     return(FanController1);
 }
Esempio n. 3
0
        public static BackgroundWorker InitializePressureWorker(ArduinoDriver.ArduinoDriver driver, Dispatcher MainDispatcher)
        {
            PressureWorker = new BackgroundWorker();
            PressureWorker.WorkerSupportsCancellation = true;
            PressureWorker.DoWork += new DoWorkEventHandler((state, args) =>
            {
                //Make sure the pump is off
                MainDispatcher.Invoke(new Action(() => { DriverFunctions.RelayOff(driver, SystemProperties.VacuumPump); }));
                Main.VacuumPumpOn = false;
                do
                {
                    try
                    {
                        if (Main.Run != true || PressureWorker.CancellationPending == true)
                        {
                            break;
                        }

                        System.Threading.Thread.Sleep(1000);
                        if (Convert.ToDouble(Main.Pressure) > SystemProperties.TargetPressure && Main.VacuumPumpOn == false)
                        {
                            //Turn the vacuum pump on
                            MainDispatcher.Invoke(new Action(() => { DriverFunctions.RelayOn(driver, SystemProperties.VacuumPump); }));
                            Main.VacuumPumpOn = true;

                            //Refresh the pressure has changed every second -- Note that the pressure is set in the still monitor background worker
                            do
                            {
                                System.Threading.Thread.Sleep(1000);
                            }while (Convert.ToDouble(Main.Pressure) > (SystemProperties.TargetPressure - SystemProperties.TgtPresHysteresisBuffer) && PressureWorker.CancellationPending == false);

                            //Once the pressure has reached its target turn the pump off
                            MainDispatcher.Invoke(new Action(() => { DriverFunctions.RelayOff(driver, SystemProperties.VacuumPump); }));
                            Main.VacuumPumpOn = false;
                        }
                    }
                    catch { MainDispatcher.Invoke(new Action(() => { driver = Periphrials.InitializeArduinoDriver(); })); }
                } while (true);
            });
            return(PressureWorker);
        }
Esempio n. 4
0
        private static BackgroundWorker FanController2;  //Turns the fan set for the condensor on, off, up and down depending on target temperature and distillation speed

        //Background worker to monitor all sensor valuess and switch states on the still and keep global variables and the UI updated
        //The idea here is to imtermittently check all variables and write to a local variable in memory to minimize commands sent to the arduino
        //This is also convienent as it minimizes the amount of long of code required to message the arduino in the control loop
        public static BackgroundWorker InitializeSystemMonitor(ArduinoDriver.ArduinoDriver driver, Dispatcher MainDispatcher)
        {
            SystemMonitor = new BackgroundWorker();
            SystemMonitor.WorkerSupportsCancellation = true;
            SystemMonitor.DoWork += new DoWorkEventHandler((state, args) =>

            {
                do
                {
                    if (Main.Run != true || driver == null) //The periphrials class returns null if no arduino is found on any of the com ports
                    {
                        break;
                    }
                    System.Threading.Thread.Sleep(1000);
                    //Check Temperature
                    bool success = false;
                    while (success == false)
                    {
                        try
                        {
                            MainDispatcher.Invoke(new Action(() =>
                            {
                                Main.ColumnTemp      = DriverFunctions.GetTemperature(driver, SystemProperties.SensorColumnTemp).ToString();
                                Main.RefluxTemp      = DriverFunctions.GetTemperature(driver, SystemProperties.SensorCoolantTemp1);
                                Main.CondensorTemp   = DriverFunctions.GetTemperature(driver, SystemProperties.SensorCoolantTemp2);
                                Main.ElementAmperage = DriverFunctions.GetAmperage(driver, SystemProperties.SensorColumnTemp);
                            }));
                            success = true;
                        }
                        catch
                        {
                            MainDispatcher.Invoke(new Action(() => {
                                driver.Dispose();
                                driver = Periphrials.InitializeArduinoDriver();
                            }));
                        }
                    }

                    //Check the low level switch -- a value of low means the lower still switch is open
                    MainDispatcher.Invoke(new Action(() => {
                        if (driver.Send(new DigitalReadRequest(SystemProperties.StillLowSwitch)).PinValue == DigitalValue.Low)
                        {
                            Main.StillEmpty = true;
                        }
                        else
                        {
                            Main.StillEmpty = false;
                        }
                    }));

                    //Check the high level switch -- a value of high means the upper still switch is closed
                    MainDispatcher.Invoke(new Action(() => {
                        if (driver.Send(new DigitalReadRequest(SystemProperties.StillHighSwitch)).PinValue == DigitalValue.High)
                        {
                            Main.StillFull = true;
                        }
                        else
                        {
                            Main.StillFull = false;
                        }
                    }));

                    //Check the recieving vessel low level switch -- a value of high means the upper rv switch is closed
                    MainDispatcher.Invoke(new Action(() => {
                        if (driver.Send(new DigitalReadRequest(SystemProperties.RVEmptySwitch)).PinValue == DigitalValue.High)
                        {
                            Main.RVEmpty = true;
                        }
                        else
                        {
                            Main.RVEmpty = false;
                        }
                    }));

                    //Check the recieving vessel high level switch -- a value of high means the upper rv switch is closed
                    MainDispatcher.Invoke(new Action(() => {
                        if (driver.Send(new DigitalReadRequest(SystemProperties.RVFullSwitch)).PinValue == DigitalValue.Low)
                        {
                            Main.RVFull = true;
                        }
                        else
                        {
                            Main.RVFull = false;
                        }
                    }));

                    //Check the pressure (1024 is the resolution of the ADC on the arduino, 45.1 is the approximate pressure range in PSI that the sensor is capable of reading, the -15 makes sure that STP = 0 PSI/kPa)
                    MainDispatcher.Invoke(new Action(() => { Main.Pressure = Math.Round((((Convert.ToDouble(driver.Send(new AnalogReadRequest(SystemProperties.SensorPressure)).PinValue.ToString()) / 1024) * 45.1) - 16.5) * ((SystemProperties.Units == "Metric") ? 6.895 : 1), 2).ToString(); }));

                    if (Main.Phase == -1)
                    {
                        Main.Phase = 0;
                    }
                } while (true);
            });
            return(SystemMonitor);
        }
Esempio n. 5
0
        public void StillLoop()
        {
            //Dispatcher to accept commands from the various background workers
            Dispatcher MainDispatcher = Dispatcher.CurrentDispatcher;

            //Instanciate the periphrial class and start up the arduino
            ArduinoDriver.ArduinoDriver driver = Periphrials.InitializeArduinoDriver();

            if (driver == null)
            {
                lblStatus.Text    = "No controller found";
                btnRescan.Visible = true;
            }
            else
            {
                btnRescan.Visible = false;
                lblStatus.Text    = "Starting";
            }

            //Declare the background workers
            SystemMonitor     = BackGroundWorkers.InitializeSystemMonitor(driver, MainDispatcher);
            PressureRegulator = BackGroundWorkers.InitializePressureWorker(driver, MainDispatcher);
            //StillController;
            FanController1 = BackGroundWorkers.InitializeFanController1(driver, MainDispatcher);
            FanController2 = BackGroundWorkers.InitializeFanController2(driver, MainDispatcher);

            //Datatable for statistics and calculating when to turn the element off
            DataTable StillStats = Statistics.InitializeTable();

            chartRun.DataSource = StillStats;


            StillController = new BackgroundWorker();
            StillController.WorkerSupportsCancellation = true;
            StillController.DoWork += new DoWorkEventHandler((state, args) =>

            {
                do
                {
                    try {
                        DateTime RunStart = DateTime.Now;
                        StillStats.Clear();
                        //Run unless a stop condition is hit
                        if (Run != true || driver == null)
                        {
                            break;
                        }

                        while (Phase == -1)//Wait for initial values to be collected before starting
                        {
                            System.Threading.Thread.Sleep(250);
                        }

                        //Check to see if the still is full, if not fill it. This ensures there is no product wasted if the previous batch was stopped half way
                        if (StillFull == false && Phase < 2)
                        {
                            {
                                //Open the inlet valve and turn the inlet pump on
                                MainDispatcher.Invoke(new Action(() => { DriverFunctions.RelayOn(driver, SystemProperties.StillFillValve); }));
                                StillValveOpen = true;
                                //Wait 5 seconds for the valve to open
                                System.Threading.Thread.Sleep(3000);
                                MainDispatcher.Invoke(new Action(() => { DriverFunctions.RelayOn(driver, SystemProperties.StillFluidPump); }));
                                MainDispatcher.Invoke(new Action(() => { lblStatus.Text = "Filling Still"; }));
                                StillPumpOn = true;


                                //Check once a second to see if the still is full now -- note that StillFull is updated by the monitor worker
                                while (StillFull == false)
                                {
                                    System.Threading.Thread.Sleep(1000);
                                }
                                //Close the valve and turn off the pump
                                MainDispatcher.Invoke(new Action(() => { DriverFunctions.RelayOff(driver, SystemProperties.StillFillValve); }));
                                StillValveOpen = false;
                                MainDispatcher.Invoke(new Action(() => { DriverFunctions.RelayOff(driver, SystemProperties.StillFluidPump); }));
                                MainDispatcher.Invoke(new Action(() => { lblStatus.Text = "Filling Complete"; }));

                                StillPumpOn = false;

                                //If this line is reached that means the still has liquid in it and is ready to start distilling
                                Phase = 1;
                            }
                        }

                        //Make sure the first loop was passed and the still didnt magically empty itself
                        if (Phase < 2 && StillFull == true)
                        {
                            //Turn on the element and vacuum pump
                            DriverFunctions.TurnOn(driver, SystemProperties.StillElement);
                            ElementOn = true;
                            PressureRegulator.RunWorkerAsync();
                            MainDispatcher.Invoke(new Action(() => { lblStatus.Text = "Heating"; }));



                            //Set up variables for calculating when to turn off the element
                            int CurrentTemp     = Convert.ToInt16(ColumnTemp);
                            int CurrentDelta    = 0;
                            int Counter         = 0;
                            PlateauTemp         = 0;
                            string StartTempRaw = null;
                            while (StartTempRaw == null)
                            {
                                try { StartTempRaw = driver.Send(new AnalogReadRequest(SystemProperties.SensorColumnTemp)).PinValue.ToString(); } catch { }
                            }
                            double StartTemp    = Convert.ToInt64((((Convert.ToDouble(StartTempRaw) * (5.0 / 1023.0)) - 1.25) / 0.005));
                            double Temp1        = 0.0;
                            double Temp2        = 0.0;
                            double AverageDelta = 0.0;
                            double TotalDelta   = 0.0;

                            DataRow row;

                            row                         = StillStats.NewRow();
                            row["Time"]                 = DateTime.Now;
                            row["Temperature"]          = CurrentTemp;
                            row["TemperatureDelta"]     = 0;
                            row["Pressure"]             = Convert.ToDecimal(Pressure);
                            row["Phase"]                = Phase;
                            row["Amperage"]             = ElementAmperage;
                            row["RefluxTemperature"]    = RefluxTemp;
                            row["CondensorTemperature"] = CondensorTemp;
                            StillStats.Rows.Add(row);

                            //Get the last written row for collecting temperature rise statistics
                            DataRow LastRow = StillStats.Rows[0];

                            //Get two rows from two points in time 2.5 minutes apart so an average temperature change can be obtained from the given time span
                            DataRow Delta1;
                            DataRow Delta2;

                            //Start both fan controllers to maintain the temperature of the coolant in Reflux column and the Condensor
                            //Note that these are started here because they are auto-regulating and will shut off by themselves when not necessary
                            //FanController1.RunWorkerAsync();
                            //FanController2.RunWorkerAsync();

                            //Keep the element on and keep collecting data every 10 seconds until the first plateau is reached then go to the next loop
                            //note thate the total delta is there incase it takes longer than 10 minutes to start seeing a temperature rise at the sensor
                            while ((StillEmpty == false && AverageDelta >= 0.02) || TotalDelta < 0.25)
                            {
                                //Change this back to 10 seconds
                                System.Threading.Thread.Sleep(250);
                                //Once the element has been on for 10 minutes start checking for the plateau
                                if (Counter < 60)
                                {
                                    Counter = Counter + 1;
                                }
                                else
                                {
                                    Delta1       = StillStats.Rows[StillStats.Rows.Count - 19];
                                    Delta2       = StillStats.Rows[StillStats.Rows.Count - 1];
                                    Temp1        = Delta1.Field <Int32>("Temperature");
                                    Temp2        = Delta2.Field <Int32>("Temperature");
                                    AverageDelta = Temp2 != 0 ? ((Temp2 - Temp1) / Temp2) : 0;
                                    if (Temp2 > Temp1)
                                    {
                                        TotalDelta = Temp2 != 0 ? ((Temp2 - StartTemp) / Temp2) : 0;
                                    }
                                }

                                CurrentTemp                 = Convert.ToInt32(ColumnTemp);
                                CurrentDelta                = CurrentTemp - LastRow.Field <Int32>("Temperature");
                                row                         = StillStats.NewRow();
                                row["Time"]                 = DateTime.Now;
                                row["Temperature"]          = CurrentTemp;
                                row["TemperatureDelta"]     = CurrentDelta;
                                row["Pressure"]             = Convert.ToDecimal(Pressure);
                                row["Phase"]                = Phase;
                                row["Amperage"]             = ElementAmperage;
                                row["RefluxTemperature"]    = RefluxTemp;
                                row["CondensorTemperature"] = CondensorTemp;
                                StillStats.Rows.Add(row);
                                LastRow = StillStats.Rows[StillStats.Rows.Count - 1];
                                MainDispatcher.Invoke(new Action(() => { chartRun.DataBind(); }));
                            }

                            //Prep variables related to the distillation phase and start the fan controller for the condensor
                            Phase        = 2;
                            AverageDelta = 0;
                            TotalDelta   = 0;
                            PlateauTemp  = LastRow.Field <Int32>("Temperature");
                            MainDispatcher.Invoke(new Action(() => { lblStatus.Text = "Distilling"; }));


                            //Once the first plateau is reached allowing for a 4 degree change at the most
                            //or end the batch if the saftey limit switch is triggered also reset the Delta counters so the next step is not skipped
                            while (StillEmpty == false && (Temp2 - PlateauTemp) < 5 && RVFull == false)
                            {
                                Delta1       = StillStats.Rows[StillStats.Rows.Count - 19];
                                Delta2       = StillStats.Rows[StillStats.Rows.Count - 1];
                                Temp1        = Delta1.Field <Int32>("Temperature");
                                Temp2        = Delta2.Field <Int32>("Temperature");
                                AverageDelta = Math.Abs(((Temp2 - Temp1) / Temp2));
                                System.Threading.Thread.Sleep(250); //Change this back to 10 seconds
                                CurrentTemp                 = Convert.ToInt32(ColumnTemp);
                                CurrentDelta                = CurrentTemp - LastRow.Field <Int32>("Temperature");
                                row                         = StillStats.NewRow();
                                row["Time"]                 = DateTime.Now;
                                row["Temperature"]          = CurrentTemp;
                                row["TemperatureDelta"]     = CurrentDelta;
                                row["Pressure"]             = Convert.ToDecimal(Pressure);
                                row["Phase"]                = Phase;
                                row["Amperage"]             = ElementAmperage;
                                row["RefluxTemperature"]    = RefluxTemp;
                                row["CondensorTemperature"] = CondensorTemp;
                                StillStats.Rows.Add(row);
                                LastRow = StillStats.Rows[StillStats.Rows.Count - 1];
                                MainDispatcher.Invoke(new Action(() => { chartRun.DataBind(); }));
                            }

                            //Batch complete!
                            MainDispatcher.Invoke(new Action(() => { lblStatus.Text = "Batch Complete, Saving Run Data"; }));
                            MainDispatcher.Invoke(new Action(() => { DriverFunctions.RelayOff(driver, SystemProperties.StillElement); }));
                            ElementOn = false;
                            Phase     = 3;
                        }
                        //If the run completed without issue then calculate the header info and write the data to a local sqldb
                        //Note that this must be done sequentially as the records must relate to a header record
                        //Once the table is succesfully written set the phase back to 0 and start another run
                        if (Phase == 3)
                        {
                            Statistics.CreateHeader(RunStart, DateTime.Now, true, SystemProperties.Units);
                            Statistics.SaveRun(StillStats, RunStart);

                            //Turn off the vacuum pump and fan controllers synchronously with the main thread
                            PressureRegulator.CancelAsync();
                            //FanController1.CancelAsync();
                            //FanController2.CancelAsync();
                            while (PressureRegulator.CancellationPending == true || FanController1.CancellationPending == true || FanController2.CancellationPending == true)
                            {
                                System.Threading.Thread.Sleep(100);
                            }


                            //Fill the system with air so it is at a neutral pressure before pumping any fluids -- note that the system will pull air from the drain valve
                            //since it eventually vents somewhere that is at atmospheric pressure
                            MainDispatcher.Invoke(new Action(() => { DriverFunctions.RelayOn(driver, SystemProperties.StillDrainValve); }));
                            MainDispatcher.Invoke(new Action(() => { lblStatus.Text = "Draining Still"; }));
                            while (StillEmpty == false || Convert.ToDouble(Pressure) <= -0.2)
                            {
                                System.Threading.Thread.Sleep(1500);
                            }
                            MainDispatcher.Invoke(new Action(() => { DriverFunctions.RelayOff(driver, SystemProperties.StillDrainValve); }));
                            System.Threading.Thread.Sleep(3000);


                            //Make sure that the switches are working then pump the Recieving vessels contents into a storage tank so the next run can begin
                            MainDispatcher.Invoke(new Action(() => { lblStatus.Text = "Draining Distillate"; }));
                            MainDispatcher.Invoke(new Action(() => { RVEmpty = (driver.Send(new DigitalReadRequest(SystemProperties.RVEmptySwitch)).PinValue == DigitalValue.Low) ? true : false; }));
                            MainDispatcher.Invoke(new Action(() => { DriverFunctions.RelayOn(driver, SystemProperties.RVDrainValve); }));
                            System.Threading.Thread.Sleep(3000); //3 second delay so the valve has time to open
                            MainDispatcher.Invoke(new Action(() => { DriverFunctions.RelayOn(driver, SystemProperties.RVFluidPump); }));
                            while (RVEmpty == false)
                            {
                                //MainDispatcher.Invoke(new Action(() => { RVEmpty = (driver.Send(new DigitalReadRequest(SystemProperties.RVEmptySwitch)).PinValue == DigitalValue.Low) ? true : false; }));
                                System.Threading.Thread.Sleep(1500);
                            }
                            //Turn off the pump and shut the valves and give them 3 seconds to close
                            MainDispatcher.Invoke(new Action(() => { DriverFunctions.RelayOff(driver, SystemProperties.RVFluidPump); }));
                            MainDispatcher.Invoke(new Action(() => { DriverFunctions.RelayOff(driver, SystemProperties.RVDrainValve); }));

                            Phase = 0;
                        }
                    }
                    //The arduino driver reference has a tendency to randomly throw null reference exceptions, for now I will handle it by just restarting the arduino
                    //the code is designed to pick up where it left off if it errors since the phase is still in memory
                    catch (NullReferenceException)
                    {
                        MainDispatcher.Invoke(new Action(() => { driver = Periphrials.InitializeArduinoDriver(); }));
                    }
                } while (true);
            });

            UIUpdater = new BackgroundWorker();
            UIUpdater.WorkerSupportsCancellation = true;
            UIUpdater.DoWork += new DoWorkEventHandler((state, args) =>
            {
                do
                {
                    System.Threading.Thread.Sleep(1000);
                    MainDispatcher.Invoke(new Action(() => { lblPressure.Text = Main.Pressure + ((SystemProperties.Units == "Metric") ? "kPa" : "PSI"); }));
                    MainDispatcher.Invoke(new Action(() => { lblTemp1.Text = Main.ColumnTemp + ((SystemProperties.Units == "Metric") ? "°C" : "°F"); }));
                }while (true);
            });

            //Start the workers and pause for 2 seconds to allow for initial values to be collected
            SystemMonitor.RunWorkerAsync();
            System.Threading.Thread.Sleep(2000);
            StillController.RunWorkerAsync();
            UIUpdater.RunWorkerAsync();
        }