Exemple #1
0
        public ATOStatus UpdateDeviceATOStatus(ATOStatus atoStatus)
        {
            //Get last ATO
            var uncompletedATOs = _aquariumDao.GetATOHistory(atoStatus.DeviceId, new PaginationSliver
            {
                Descending = true,
                Count      = 10 //doesn't matter, just not all of them
            }).Where(a => !a.Completed).OrderBy(a => a.UpdatedAt);

            uncompletedATOs.ToList().ForEach(ato =>
            {
                if (ato.Id == atoStatus.Id)
                {
                    return;
                }
                ato.UpdatedAt = DateTime.Now.ToUniversalTime();
                ato.EndReason = "Error"; //todo
                ato.Completed = true;
                _aquariumDao.UpdateATOStatus(ato);
            });
            if (!atoStatus.PumpRunning && atoStatus.Id == null)
            {
                return(atoStatus);
            }
            return(_aquariumDao.UpdateATOStatus(atoStatus));
        }
        public IActionResult RecieveATOStatus([FromBody] ATOStatus atoStatus)
        {
            try
            {
                _logger.LogInformation($"POST /v1/DeviceInteraction/ATO called");
                var userId   = _accountService.GetCurrentUserId();
                var id       = _accountService.GetCurrentAquariumId();
                var aquarium = _aquariumService.GetAquariumById(id);
                if (aquarium.Device == null)
                {
                    return(BadRequest("This aquarium does not have a device"));
                }
                if (!_accountService.CanModify(userId, aquarium))
                {
                    return(BadRequest("You do not own this aquarium"));
                }
                atoStatus.DeviceId = aquarium.Device.Id;

                var s = _aquariumService.UpdateDeviceATOStatus(atoStatus);

                if (s.Completed)
                {
                    _logger.LogInformation($"ATO was completed for aquarium id: {id}");
                }

                return(new OkObjectResult(s));
            }
            catch (Exception ex)
            {
                _logger.LogError($"POST /v1/DeviceInteraction/ATO: { ex.Message } Details: { ex.ToString() }");
                _logger.LogError(ex.StackTrace);
                return(BadRequest(ex.Message));
            }
        }
        public void Setup()
        {
            var device = _aquariumAuthService.GetAquarium().Device;

            //Register associated device tasks
            RegisterDeviceTasks();

            //Check pins and sensors
            var pumpRelaySensor   = GetPumpRelayPin();
            var floatSwitchSensor = GetFloatSensorPin();

            if (pumpRelaySensor == null || floatSwitchSensor == null)
            {
                throw new Exception($"Invalid ATO sensors (Pump: {pumpRelaySensor} Sensor: {floatSwitchSensor})");
            }
            floatSwitchSensor.OnSensorTriggered = OnFloatSwitchTriggered;

            var task = device.ScheduleAssignments.Select(assignment =>
                                                         assignment.Schedule.Tasks.Where(t => t.TaskId == ScheduleTaskTypes.StartATO).FirstOrDefault()
                                                         ).FirstOrDefault();

            DateTime?nextRunTime = null;

            if (task != null)
            {
                nextRunTime = task.StartTime.ToUniversalTime();
            }

            _logger.LogInformation($"ATO successfully set up (Pump Relay Pin: {pumpRelaySensor.Pin} Float Sensor Pin: {floatSwitchSensor.Pin})");
            Status = new ATOStatus()
            {
                PumpRelaySensor = pumpRelaySensor,
                FloatSensor     = floatSwitchSensor,
                Enabled         = true,
                DeviceId        = device.Id,
                UpdatedAt       = new DateTime(),
                NextRunTime     = nextRunTime
            };
        }
        public async Task <ATOStatus> DispatchATOStatus(ATOStatus status)
        {
            var path = $"/v1/DeviceInteraction/ATO";

            _logger.LogInformation("Dispatching ATO status...");
            _logger.LogInformation($" - Pump running: {status.PumpRunning}");
            _logger.LogInformation($" - Max Run Time: {status.MaxRuntime}");
            using (HttpClient client = GetHttpClient())
            {
                var httpContent = new StringContent(JsonConvert.SerializeObject(status), Encoding.UTF8, "application/json");
                var result      = await client.PostAsync(path, httpContent);

                if (!result.IsSuccessStatusCode)
                {
                    throw new Exception($"Could not dispatch ATO status. Response: {result.StatusCode}: {result.ReasonPhrase} Destination: {client.BaseAddress + path}");
                }
                _logger.LogInformation($"ATO status successfully dispatched to service.");
                var res = await result.Content.ReadAsStringAsync();

                var response = JsonConvert.DeserializeObject <ATOStatus>(res);
                return(response);
            }
        }
        public void BeginAutoTopOff(AutoTopOffRequest atoRequest)
        {
            if (Status.PumpRunning)
            {
                throw new Exception($"ATO is currently running...");
            }
            if (CancelToken != null && !CancelToken.IsCancellationRequested)
            {
                _logger.LogInformation("Canceled by new ATO process"); //todo remove all these cancel logs when we solve the issue of random ato cancels
                CancelToken.Cancel();
            }
            var pumpRelaySensor   = GetPumpRelayPin();
            var floatSwitchSensor = GetFloatSensorPin();

            if (pumpRelaySensor == null || floatSwitchSensor == null)
            {
                throw new Exception($"Invalid ATO pins specified (Pump: {pumpRelaySensor} Sensor: {floatSwitchSensor})");
            }

            if (atoRequest.Runtime > 60)
            {
                throw new Exception($"ATO max runtime is larger than maximum allowed (Runtime: {atoRequest.Runtime} Maximum: 60)");
            }

            var currentSensorValue = _gpioService.GetPinValue(floatSwitchSensor);

            if (currentSensorValue == GpioPinValue.Low)
            {
                throw new Exception($"ATO sensor is currently reading maximum water level");
            }



            _logger.LogInformation("[ATOService] Beginning ATO...");
            var maxPumpRuntime = atoRequest.Runtime * 1000 * 60;

            _gpioService.SetPinValue(pumpRelaySensor, PinValue.High);

            //Next run time
            var device = _aquariumAuthService.GetAquarium().Device;
            var task   = device.ScheduleAssignments.Select(assignment =>
                                                           assignment.Schedule.Tasks.Where(t => t.TaskId == Models.ScheduleTaskTypes.StartATO).FirstOrDefault()
                                                           ).FirstOrDefault();
            DateTime?nextRunTime = null;

            if (task != null)
            {
                nextRunTime = task.StartTime.ToUniversalTime();
            }

            var startTime = DateTime.Now.ToUniversalTime();

            Status = new ATOStatus
            {
                StartTime        = startTime,
                EstimatedEndTime = startTime.AddMilliseconds(maxPumpRuntime),
                UpdatedAt        = startTime,
                MaxRuntime       = atoRequest.Runtime,
                RunIndefinitely  = atoRequest.RunIndefinitely,
                PumpRunning      = true,
                Enabled          = true,
                PumpRelaySensor  = pumpRelaySensor,
                FloatSensor      = floatSwitchSensor,
                DeviceId         = device.Id,
                NextRunTime      = nextRunTime,
                FloatSensorValue = currentSensorValue
            };
            DispatchStatus().Wait(); //.ConfigureAwait(false);

            //Apply a max drain time
            CancelToken = new CancellationTokenSource();
            CancellationToken ct = CancelToken.Token;


            var statusId = Status.Id;

            Task.Run(() =>
            {
                var t       = TimeSpan.FromMilliseconds(maxPumpRuntime);
                string time = string.Format("{0:D2}h:{1:D2}m", t.Hours, t.Minutes);
                _logger.LogInformation($"[ATOService] Running (Max run time: {time})");
                Thread.Sleep(maxPumpRuntime);
                //ct.ThrowIfCancellationRequested();
                if (ct.IsCancellationRequested)
                {
                    if (statusId != Status.Id)
                    {
                        _logger.LogError("Attempted to cancel water change. The Status IDs of the water changes do not match!");
                        //so this definitely happens occasionally
                        return;
                    }
                    StopAutoTopOff(AutoTopOffStopReason.Canceled);
                    //_gpioService.SetPinValue(pumpRelaySensor.Pin, PinValue.Low); //maybe enable this in case we run into pin issues
                    return;
                }

                if (Status.PumpRunning)
                {
                    _logger.LogInformation($"[ATOService] Reached maximum run time of {Status.MaxRuntime} minutes.");
                    StopAutoTopOff(AutoTopOffStopReason.MaximumRuntimeReached);
                }
            }).ConfigureAwait(false); //Fire and forget
        }