//TODO register this with an event in messenger class /// <summary>Loads device and sensor info from the DB.</summary> /// <remarks>Don't make publish or call directly! always push onto the task!</remarks> /// <returns>True if it loaded something, false otherwise.</returns> private void LoadData() { LoggingService.LogInfo("DatapointsSaver refreshing cache", Windows.Foundation.Diagnostics.LoggingLevel.Information); using (var db = new MainDbContext()) { _dbDevices = db.Devices.Include(dv => dv.Sensors).Include(dv => dv.Relays).AsNoTracking().ToList(); var sensorsHistory = db.SensorsHistory.Where(sh => sh.TimeStamp > Today).ToList(); // we will edit this _sensorTypes = db.SensorTypes.Include(st => st.Param).Include(st => st.Place).AsNoTracking().ToList(); //Add missing sensors and relays foreach (var sensor in _dbDevices.SelectMany(dv => dv.Sensors)) { if (_sensorBuffer.Any(sb => sb.Sensor.ID == sensor.ID) == false) { _sensorBuffer.Add(new SensorBuffer(sensor)); } } //TODO merge the datapoints! foreach (var sHistory in sensorsHistory) { sHistory.DeserialiseData(); var mIndex = _sensorBuffer.FindIndex(sb => sb.Sensor.ID == sHistory.SensorID); if (_sensorBuffer[mIndex].DataDay == null || _sensorBuffer[mIndex].DataDay.TimeStamp < sHistory.TimeStamp) { _sensorBuffer[mIndex] = new SensorBuffer(_sensorBuffer[mIndex].Sensor, sHistory); } else if (_sensorBuffer[mIndex].DataDay.Data != null && sHistory.Data != null) { var sHistMerged = SensorHistory.Merge(_sensorBuffer[mIndex].DataDay, sHistory); _sensorBuffer[mIndex] = new SensorBuffer(_sensorBuffer[mIndex].Sensor, sHistMerged); } } } }
public int AddSmokeSensorHistory(SmokeSensor sensor) { if (sensor == null) { throw new Exception("Sensore object cannot be null"); } sensorHistory = new SensorHistory { HouseId = sensor.HouseId, SensorId = sensor.SensorId, Type = sensor.Type, Name = sensor.Name, Smoke = sensor.Smoke, IsOn = sensor.IsOn }; try { _databaseContext.SensorHistories.Add(sensorHistory); _databaseContext.SaveChanges(); } catch (Exception exp) { Console.WriteLine(exp.Message); } return(sensorHistory.SensorHistoryId); }
public JsonResult UpdateSensor([FromBody] SensorUpdate s) { var sensor = _ctx.Find("{\"SensorId\" : \"" + s.SensorId + "\"}").FirstOrDefault(); sensor.SensorValue = s.Value; sensor.SensorBattery = s.Battery; var historyItem = new SensorHistory() { SensorId = sensor.SensorId, SensorValue = sensor.SensorValue, SensorBattery = sensor.SensorBattery, Timestamp = DateTime.Now }; _ctx.ReplaceOne("{\"SensorId\" : \"" + s.SensorId + "\"}", sensor); _history.InsertOne(historyItem); return(new JsonResult(sensor)); }
public int AddTemperatureSensorHistory(TemperatureSensor sensor) { if (sensor == null) { throw new Exception("Sensore object cannot be null"); } sensorHistory = new SensorHistory { HouseId = sensor.HouseId, SensorId = sensor.SensorId, Type = sensor.Type, Name = sensor.Name, Temperature = sensor.Temperature, IsOn = sensor.IsOn }; _databaseContext.SensorHistories.Add(sensorHistory); _databaseContext.SaveChanges(); return(sensorHistory.SensorHistoryId); }
public async Task <HttpResponseMessage> PostSensorsHistory(List <SensorHistory> shRecievedList) { if (!ModelState.IsValid) { return(Request.CreateResponse(HttpStatusCode.BadRequest, ModelState)); } List <Guid> sensorIDs = shRecievedList.Select(chR => chR.SensorID).ToList(); //Get all relevant sensors, they must both exist and belong to this user! List <Sensor> userSensors = await _db.Sensors.Where(rel => sensorIDs.Contains(rel.ID) && rel.Device.Location.PersonId == _UserID) .ToListAsync(); //If one of the submitted items reffers to a sensor that doesn't exist/belong to user, return error foreach (SensorHistory sHistory in shRecievedList) { if (userSensors.Any(rel => rel.ID == sHistory.SensorID) == false) { return(Request.CreateResponse(HttpStatusCode.BadRequest, new ErrorResponse <SensorHistory>("One of the SensorIDs does not exist", sHistory))); } } //Get all the control histories that are being edited List <DateTimeOffset> timestamps = shRecievedList.Select(sensHist => sensHist.TimeStamp).ToList(); List <SensorHistory> sensHistDbRawList = await _db.SensorHistories.Where(sensHist => timestamps.Contains(sensHist.TimeStamp) && sensorIDs.Contains(sensHist.SensorID)).ToListAsync(); List <Location> userLocations = await _db.Location.Where(loc => loc.PersonId == _UserID).ToListAsync(); foreach (var sensHistRecieved in shRecievedList) { SensorHistory SensHistoryDB = sensHistDbRawList.FirstOrDefault(rHist => rHist.SensorID == sensHistRecieved.SensorID && rHist.TimeStamp == sensHistRecieved.TimeStamp); if (SensHistoryDB == null) //create new { if (false == userLocations.Any(loc => loc.ID == sensHistRecieved.LocationID)) { return(Request.CreateResponse(HttpStatusCode.BadRequest, new ErrorResponse <SensorHistory>("Referenced location doesn't exist", sensHistRecieved))); } sensHistRecieved.SerialiseData(); sensHistRecieved.UploadedAt = DateTimeOffset.Now; _db.Entry(sensHistRecieved).State = EntityState.Added; } else { if (SensHistoryDB.LocationID != sensHistRecieved.LocationID) { return(Request.CreateResponse(HttpStatusCode.Forbidden, new ErrorResponse <SensorHistory>("You are not allowed to change location of SensorHistory", sensHistRecieved))); } //Check if changed were made by comparing raw bytes data sensHistRecieved.SerialiseData(); if (Compare(sensHistRecieved.RawData, SensHistoryDB.RawData) == false) { SensHistoryDB.DeserialiseData(); SensorHistory chMerged = SensorHistory.Merge(SensHistoryDB, sensHistRecieved); SensHistoryDB.Data = chMerged.Data; SensHistoryDB.SerialiseData(); SensHistoryDB.UploadedAt = DateTimeOffset.Now; } } } await _db.SaveChangesAsync(); return(Request.CreateResponse(HttpStatusCode.OK)); }
public SensorBuffer(Sensor assignSensor, SensorHistory inDataDay = null) { Sensor = assignSensor; FreshBuffer = new List <SensorDatapoint>(); DataDay = inDataDay; }
//Closes sensor Histories that are no longer usefull private void SaveBufferedReadings(object sender, object e) { LoggingService.LogInfo($"Saved Buffered Readings: {DateTimeOffset.Now.DateTime} Datapointsaver", Windows.Foundation.Diagnostics.LoggingLevel.Information); _localTask = _localTask.ContinueWith(previous => { //if (Settings.Instance.LastSuccessfulGeneralDbGet > DateTimeOffset.Now - TimeSpan.FromMinutes(5)) //{ LoggingService.LogInfo("Datasaver started, did not bother detecting a recent update.", Windows.Foundation.Diagnostics.LoggingLevel.Verbose); using (var db = new MainDbContext()) { for (var i = 0; i < _sensorBuffer.Count; i++) { var sbuffer = _sensorBuffer[i]; SensorDatapoint sensorDatapoint = null; if (sbuffer.FreshBuffer.Count > 0) { var startTime = sbuffer.FreshBuffer[0].TimeStamp; var endTime = sbuffer.FreshBuffer.Last().TimeStamp; var duration = (endTime - startTime).Subtract(sbuffer.FreshBuffer[0].Duration); var cumulativeDuration = TimeSpan.Zero; double cumulativeValue = 0; for (var b = 0; b < sbuffer.FreshBuffer.Count; b++) { cumulativeDuration += sbuffer.FreshBuffer[b].Duration; cumulativeValue += sbuffer.FreshBuffer[b].Value; } var sensorType = _sensorTypes.First(st => st.ID == sbuffer.Sensor.SensorTypeID); var value = cumulativeValue / sbuffer.FreshBuffer.Count; if (sensorType.ParamID == 5) // Level { sensorDatapoint = new SensorDatapoint(Math.Round(value), endTime, duration); } else if (sensorType.ParamID == 9) //water flow { sensorDatapoint = new SensorDatapoint(value, endTime, cumulativeDuration); } else { sensorDatapoint = new SensorDatapoint(value, endTime, duration); } sbuffer.FreshBuffer.RemoveRange(0, sbuffer.FreshBuffer.Count); } //only if new data is present if (sensorDatapoint != null) { //check if corresponding dataDay is too old or none exists at all if (sbuffer.DataDay?.TimeStamp < sensorDatapoint.TimeStamp || sbuffer.DataDay == null) { var dataDay = new SensorHistory { LocationID = sbuffer.Sensor.Device.LocationID, SensorID = sbuffer.Sensor.ID, Sensor = sbuffer.Sensor, TimeStamp = Tomorrow, Data = new List <SensorDatapoint>() }; _sensorBuffer[i] = new SensorBuffer(sbuffer.Sensor, dataDay); //Only uses this entity, and does not follow the references to stick related references in the DB db.Entry(dataDay).State = EntityState.Added; } else { //this will not attach related entities, which is good db.Entry(sbuffer.DataDay).State = EntityState.Unchanged; } _sensorBuffer[i].DataDay.Data.Add(sensorDatapoint); _sensorBuffer[i].DataDay.SerialiseData(); } } //for loop ends //Once we are done here, mark changes to the db db.SaveChanges(); LoggingService.LogInfo("Saved Sensor Data", Windows.Foundation.Diagnostics.LoggingLevel.Verbose); } //} //else //{ // Debug.WriteLine("Skipped datasaver due to lack of recent update."); //} }); }
/// <summary>Updates sensor history from server.</summary> /// <returns>Errors, null on succes. Some data may have been successfully saved alongside errors.</returns> private static async Task <string> GetRequestSensorHistoryAsync(MainDbContext db) { var devices = db.Devices.AsNoTracking().Include(d => d.Sensors).ToList(); var table = nameof(db.SensorsHistory); var errors = ""; // Download all the data for each device in turn. foreach (var device in devices) { // Find the newest received item so dowloading can resume after it. // There will be items for multiple sensors with the same time, it diesn't matter which one is used. // It will be updated after each item is added, much cheaper than re-querying. var mostRecentDayDownloaded = db.SensorsHistory.AsNoTracking() .Include(sh => sh.Sensor) .Where(sh => sh.Sensor.DeviceID == device.ID) // Never uploaded days may be much newer than items that have not yet been downloaded. .Where(sh => sh.UploadedAt != default(DateTimeOffset)) //Ignore never uploaded days. .OrderByDescending(sh => sh.TimeStamp).FirstOrDefault(); //Don't use MaxBy, it wont EF query. DateTimeOffset mostRecentDownloadedTimestamp; if (null == mostRecentDayDownloaded) { // Data has never been downloaded for this device, so start downloading for time 0. mostRecentDownloadedTimestamp = default(DateTimeOffset); } else { //The timestamp is always the end of the day so look inside to see what time the data actually ends. // Its pulled out of the database as raw so deserialise to access the data. mostRecentDayDownloaded.DeserialiseData(); if (mostRecentDayDownloaded.Data.Any()) { mostRecentDownloadedTimestamp = mostRecentDayDownloaded.Data.Max(entry => entry.TimeStamp); } else { Log.ShouldNeverHappen( $"Found a history with no entries: {device.Name}, {mostRecentDayDownloaded.TimeStamp}"); // This is a broken situation, but it should be fine if we continue from the start of the day. mostRecentDownloadedTimestamp = mostRecentDayDownloaded.TimeStamp - TimeSpan.FromDays(1); } } // This loop will keep requesting data untill the server gives no more. bool itemsReceived; var added = new List <SensorHistory>(); do { var cred = Creds.FromUserIdAndToken(Settings.Instance.CredUserId, Settings.Instance.CredToken); // Although any time far in the past should work, using 0 makes intent clear in debugging. var unixTimeSeconds = mostRecentDownloadedTimestamp == default(DateTimeOffset) ? 0 : mostRecentDownloadedTimestamp.ToUnixTimeSeconds(); List <SensorHistory> histories; try { // Download with get request. var raw = await GetRequestTableThrowOnErrorAsync($"{table}/{device.ID}/{unixTimeSeconds}/{MaxDaysDl}", cred).ConfigureAwait(false); // Deserialise download to POCO object. histories = await DeserializeTableThrowOnErrrorAsync <SensorHistory>(table, raw).ConfigureAwait(false); } catch (Exception ex) { // Something went wrong, try moving onto next device. var message = $"GetRequest or deserialise failed for {device.Name}: {ex}"; errors += message + Environment.NewLine; Debug.WriteLine(message); break; } Debug.WriteLine($"{histories.Count} dl for {device.Name}"); if (histories.Any()) { var lastDayOfThisDownloadSet = DateTimeOffset.MinValue; foreach (var hist in histories) { Debug.WriteLine( $"[{mostRecentDownloadedTimestamp}]{hist.TimeStamp}#{hist.UploadedAt}#{device.Name}#{hist.SensorID}"); // See if this is a new object. var existing = db.SensorsHistory.AsNoTracking() .FirstOrDefault( sh => sh.SensorID == hist.SensorID && sh.TimeStamp.Date == hist.TimeStamp.Date); // Make sure that its not an object tracked from a previous loop. // This can happen because the end of the previous day will always get sliced with no data. // That end slice isn't perfect but is makes sure all the data was taken. if (existing == null) { existing = added.FirstOrDefault( sh => sh.SensorID == hist.SensorID && sh.TimeStamp.Date == hist.TimeStamp.Date); if (existing != null) { // Detach the existing object because it will be merged and replaced. var entityEntry = db.Entry(existing); entityEntry.State = EntityState.Detached; added.Remove(existing); } } if (existing == null) { // The json deserialiser deserialises json to the .Data property. // The data has to be serialised into raw-data-blobs before saving to the databse. hist.SerialiseData(); added.Add(db.Add(hist).Entity); } else { // The data is pulled from the database as serialised raw data blobs. existing.DeserialiseData(); // The merged object merges using the deserialised Data property. var merged = SensorHistory.Merge(existing, hist); // Data has to be serialised again into raw data blobs for the database. merged.SerialiseData(); added.Add(db.Update(merged).Entity); } // This day was just downloaded so it must be completed if (hist.TimeStamp > lastDayOfThisDownloadSet) { lastDayOfThisDownloadSet = hist.TimeStamp; } } // The GET request allways gets days completed to the end (start may be missing but not the end) mostRecentDownloadedTimestamp = lastDayOfThisDownloadSet; itemsReceived = true; } else { itemsReceived = false; } } while (itemsReceived); // Save the data for this device. await Task.Run(() => db.SaveChanges()).ConfigureAwait(false); foreach (var entity in added) { var entry = db.Entry(entity); if (entry.State != EntityState.Detached) { entry.State = EntityState.Detached; } } // Move onto next device. } return(string.IsNullOrWhiteSpace(errors) ? null : errors); }