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 }