diff --git a/src/components/ble/weather/WeatherData.h b/src/components/ble/weather/WeatherData.h index f8f0e6c543..7c2e6803e3 100644 --- a/src/components/ble/weather/WeatherData.h +++ b/src/components/ble/weather/WeatherData.h @@ -198,6 +198,10 @@ namespace Pinetime { * If there's a newer event of the same type then it overrides this one, even if it hasn't expired */ uint32_t expires; + /** + * Unique ID for this event + */ + uint16_t eventID; /** * What type of weather-related event */ diff --git a/src/components/ble/weather/WeatherService.cpp b/src/components/ble/weather/WeatherService.cpp index 8922ebd940..a2448bd444 100644 --- a/src/components/ble/weather/WeatherService.cpp +++ b/src/components/ble/weather/WeatherService.cpp @@ -22,119 +22,142 @@ #include "libs/QCBOR/inc/qcbor/qcbor.h" namespace { - std::unique_ptr - CreateAirQualityEvent(uint64_t timestamp, uint32_t expires, const std::string& polluter, uint32_t amount) { - auto airquality = std::make_unique(); - airquality->timestamp = timestamp; - airquality->eventType = Pinetime::Controllers::WeatherData::eventtype::AirQuality; - airquality->expires = expires; - airquality->polluter = polluter; - airquality->amount = amount; - return airquality; + void CreateAirQualityEvent(std::unique_ptr& event, + uint64_t timestamp, + uint32_t expires, + uint16_t id, + const std::string& polluter, + uint32_t amount) { + event->timestamp = timestamp; + event->eventType = Pinetime::Controllers::WeatherData::eventtype::AirQuality; + event->expires = expires; + event->eventID = id; + event->polluter = polluter; + event->amount = amount; } - std::unique_ptr - CreateObscurationEvent(uint64_t timestamp, uint32_t expires, uint16_t amount) { - auto obscuration = std::make_unique(); - obscuration->timestamp = timestamp; - obscuration->eventType = Pinetime::Controllers::WeatherData::eventtype::Obscuration; - obscuration->expires = expires; - obscuration->amount = amount; - return obscuration; + void CreateObscurationEvent(std::unique_ptr& event, + uint64_t timestamp, + uint32_t expires, + uint16_t id, + uint16_t amount) { + event->timestamp = timestamp; + event->eventType = Pinetime::Controllers::WeatherData::eventtype::Obscuration; + event->expires = expires; + event->eventID = id; + event->amount = amount; } - std::unique_ptr - CreatePrecipitationEvent(uint64_t timestamp, - uint32_t expires, - Pinetime::Controllers::WeatherData::precipitationtype type, - uint8_t amount) { - auto precipitation = std::make_unique(); - precipitation->timestamp = timestamp; - precipitation->eventType = Pinetime::Controllers::WeatherData::eventtype::Precipitation; - precipitation->expires = expires; - precipitation->type = type; - precipitation->amount = amount; - return precipitation; + void CreatePrecipitationEvent(std::unique_ptr& event, + uint64_t timestamp, + uint32_t expires, + uint16_t id, + Pinetime::Controllers::WeatherData::precipitationtype type, + uint8_t amount) { + event->timestamp = timestamp; + event->eventType = Pinetime::Controllers::WeatherData::eventtype::Precipitation; + event->expires = expires; + event->eventID = id; + event->type = type; + event->amount = amount; } - std::unique_ptr - CreateWindEvent(uint64_t timestamp, uint32_t expires, uint8_t speedMin, uint8_t speedMax, uint8_t directionMin, uint8_t directionMax) { - auto wind = std::make_unique(); - wind->timestamp = timestamp; - wind->eventType = Pinetime::Controllers::WeatherData::eventtype::Wind; - wind->expires = expires; - wind->speedMin = speedMin; - wind->speedMax = speedMax; - wind->directionMin = directionMin; - wind->directionMax = directionMax; - return wind; + void CreateWindEvent(std::unique_ptr& event, + uint64_t timestamp, + uint32_t expires, + uint16_t id, + uint8_t speedMin, + uint8_t speedMax, + uint8_t directionMin, + uint8_t directionMax) { + event->timestamp = timestamp; + event->eventType = Pinetime::Controllers::WeatherData::eventtype::Wind; + event->expires = expires; + event->eventID = id; + event->speedMin = speedMin; + event->speedMax = speedMax; + event->directionMin = directionMin; + event->directionMax = directionMax; } - std::unique_ptr - CreateTemperatureEvent(uint64_t timestamp, uint32_t expires, int16_t temperatureValue, uint16_t dewPoint) { - auto temperature = std::make_unique(); - temperature->timestamp = timestamp; - temperature->eventType = Pinetime::Controllers::WeatherData::eventtype::Temperature; - temperature->expires = expires; - temperature->temperature = temperatureValue; - temperature->dewPoint = dewPoint; - return temperature; + void CreateTemperatureEvent(std::unique_ptr& event, + uint64_t timestamp, + uint32_t expires, + uint16_t id, + int16_t temperatureValue, + uint16_t dewPoint) { + event->timestamp = timestamp; + event->eventType = Pinetime::Controllers::WeatherData::eventtype::Temperature; + event->expires = expires; + event->eventID = id; + event->temperature = temperatureValue; + event->dewPoint = dewPoint; } - std::unique_ptr - CreateSpecialEvent(uint64_t timestamp, uint32_t expires, Pinetime::Controllers::WeatherData::specialtype type) { - auto special = std::make_unique(); - special->timestamp = timestamp; - special->eventType = Pinetime::Controllers::WeatherData::eventtype::Special; - special->expires = expires; - special->type = type; - return special; + void CreateSpecialEvent(std::unique_ptr& event, + uint64_t timestamp, + uint32_t expires, + uint16_t id, + Pinetime::Controllers::WeatherData::specialtype type) { + event->timestamp = timestamp; + event->eventType = Pinetime::Controllers::WeatherData::eventtype::Special; + event->expires = expires; + event->eventID = id; + event->type = type; } - std::unique_ptr - CreatePressureEvent(uint64_t timestamp, uint32_t expires, uint16_t pressureValue) { - auto pressure = std::make_unique(); - pressure->timestamp = timestamp; - pressure->eventType = Pinetime::Controllers::WeatherData::eventtype::Pressure; - pressure->expires = expires; - pressure->pressure = pressureValue; - return pressure; + void CreatePressureEvent(std::unique_ptr& event, + uint64_t timestamp, + uint32_t expires, + uint16_t id, + uint16_t pressureValue) { + event->timestamp = timestamp; + event->eventType = Pinetime::Controllers::WeatherData::eventtype::Pressure; + event->expires = expires; + event->eventID = id; + event->pressure = pressureValue; } - std::unique_ptr CreateLocationEvent(uint64_t timestamp, - uint32_t expires, - const std::string& locationValue, - int16_t altitude, - int32_t latitude, - int32_t longitude) { - auto location = std::make_unique(); - location->timestamp = timestamp; - location->eventType = Pinetime::Controllers::WeatherData::eventtype::Location; - location->expires = expires; - location->location = locationValue; - location->altitude = altitude; - location->latitude = latitude; - location->longitude = longitude; - return location; + void CreateLocationEvent(std::unique_ptr& event, + uint64_t timestamp, + uint32_t expires, + uint16_t id, + const std::string& locationValue, + int16_t altitude, + int32_t latitude, + int32_t longitude) { + event->timestamp = timestamp; + event->eventType = Pinetime::Controllers::WeatherData::eventtype::Location; + event->expires = expires; + event->eventID = id; + event->location = locationValue; + event->altitude = altitude; + event->latitude = latitude; + event->longitude = longitude; } - std::unique_ptr CreateCloudsEvent(uint64_t timestamp, uint32_t expires, uint8_t amount) { - auto clouds = std::make_unique(); - clouds->timestamp = timestamp; - clouds->eventType = Pinetime::Controllers::WeatherData::eventtype::Clouds; - clouds->expires = expires; - clouds->amount = amount; - return clouds; + void CreateCloudsEvent(std::unique_ptr& event, + uint64_t timestamp, + uint32_t expires, + uint16_t id, + uint8_t amount) { + event->timestamp = timestamp; + event->eventType = Pinetime::Controllers::WeatherData::eventtype::Clouds; + event->expires = expires; + event->eventID = id; + event->amount = amount; } - std::unique_ptr - CreateHumidityEvent(uint64_t timestamp, uint32_t expires, uint8_t humidityValue) { - auto humidity = std::make_unique(); - humidity->timestamp = timestamp; - humidity->eventType = Pinetime::Controllers::WeatherData::eventtype::Humidity; - humidity->expires = expires; - humidity->humidity = humidityValue; - return humidity; + void CreateHumidityEvent(std::unique_ptr& event, + uint64_t timestamp, + uint32_t expires, + uint16_t id, + uint8_t humidityValue) { + event->timestamp = timestamp; + event->eventType = Pinetime::Controllers::WeatherData::eventtype::Humidity; + event->expires = expires; + event->eventID = id; + event->humidity = humidityValue; } template @@ -268,6 +291,7 @@ namespace Pinetime { WeatherService::WeatherService(const DateTime& dateTimeController) : dateTimeController(dateTimeController) { nullHeader = &nullTimelineheader; nullTimelineheader->timestamp = 0; + nullTimelineheader->expires = 0; } void WeatherService::Init() { @@ -281,13 +305,14 @@ namespace Pinetime { int WeatherService::OnCommand(struct ble_gatt_access_ctxt* ctxt) { if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { - const uint8_t packetLen = OS_MBUF_PKTLEN(ctxt->om); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + const uint16_t packetLen = OS_MBUF_PKTLEN(ctxt->om); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) if (packetLen <= 0) { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } + // Decode QCBORDecodeContext decodeContext; - UsefulBufC encodedCbor = {ctxt->om->om_data, OS_MBUF_PKTLEN(ctxt->om)}; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + UsefulBufC encodedCbor = {ctxt->om->om_data, packetLen}; QCBORDecode_Init(&decodeContext, encodedCbor, QCBOR_DECODE_MODE_NORMAL); // KINDLY provide us a fixed-length map @@ -319,8 +344,21 @@ namespace Pinetime { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } + auto optID = Get(&decodeContext, "EventID"); + if (!optID) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + TidyTimeline(); + std::unique_ptr& existingEvent = GetEventByID(*optEventType, *optID); + bool useExisting = IsEventStillValid(existingEvent, currentTimestamp); + if (!useExisting && GetTimelineLength() >= maxNbElements) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + switch (*optEventType) { case WeatherData::eventtype::AirQuality: { auto optPolluter = Get(&decodeContext, "Polluter"); @@ -335,7 +373,18 @@ namespace Pinetime { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } - auto airQuality = CreateAirQualityEvent(*optTimestamp, *optExpires, *optPolluter, *optAmount); + if (useExisting) { + CreateAirQualityEvent(reinterpret_cast&>(existingEvent), + *optTimestamp, + *optExpires, + *optID, + *optPolluter, + *optAmount); + break; + } + + auto airQuality = std::make_unique(); + CreateAirQualityEvent(airQuality, *optTimestamp, *optExpires, *optID, *optPolluter, *optAmount); if (!AddEventToTimeline(std::move(airQuality))) { CleanUpQcbor(&decodeContext); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; @@ -355,7 +404,17 @@ namespace Pinetime { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } - auto obscuration = CreateObscurationEvent(*optTimestamp, *optExpires, *optAmount); + if (useExisting) { + CreateObscurationEvent(reinterpret_cast&>(existingEvent), + *optTimestamp, + *optExpires, + *optID, + *optAmount); + break; + } + + auto obscuration = std::make_unique(); + CreateObscurationEvent(obscuration, *optTimestamp, *optExpires, *optID, *optAmount); if (!AddEventToTimeline(std::move(obscuration))) { CleanUpQcbor(&decodeContext); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; @@ -375,7 +434,18 @@ namespace Pinetime { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } - auto precipitation = CreatePrecipitationEvent(*optTimestamp, *optExpires, *optType, *optAmount); + if (useExisting) { + CreatePrecipitationEvent(reinterpret_cast&>(existingEvent), + *optTimestamp, + *optExpires, + *optID, + *optType, + *optAmount); + break; + } + + auto precipitation = std::make_unique(); + CreatePrecipitationEvent(precipitation, *optTimestamp, *optExpires, *optID, *optType, *optAmount); if (!AddEventToTimeline(std::move(precipitation))) { CleanUpQcbor(&decodeContext); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; @@ -407,7 +477,20 @@ namespace Pinetime { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } - auto wind = CreateWindEvent(*optTimestamp, *optExpires, *optMin, *optMax, *optDMin, *optDMax); + if (useExisting) { + CreateWindEvent(reinterpret_cast&>(existingEvent), + *optTimestamp, + *optExpires, + *optID, + *optMin, + *optMax, + *optDMin, + *optDMax); + break; + } + + auto wind = std::make_unique(); + CreateWindEvent(wind, *optTimestamp, *optExpires, *optID, *optMin, *optMax, *optDMin, *optDMax); if (!AddEventToTimeline(std::move(wind))) { CleanUpQcbor(&decodeContext); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; @@ -427,7 +510,18 @@ namespace Pinetime { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } - auto temperature = CreateTemperatureEvent(*optTimestamp, *optExpires, *optTemperature, *optDewPoint); + if (useExisting) { + CreateTemperatureEvent(reinterpret_cast&>(existingEvent), + *optTimestamp, + *optExpires, + *optID, + *optTemperature, + *optDewPoint); + break; + } + + auto temperature = std::make_unique(); + CreateTemperatureEvent(temperature, *optTimestamp, *optExpires, *optID, *optTemperature, *optDewPoint); if (!AddEventToTimeline(std::move(temperature))) { CleanUpQcbor(&decodeContext); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; @@ -441,7 +535,17 @@ namespace Pinetime { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } - auto special = CreateSpecialEvent(*optTimestamp, *optExpires, *optType); + if (useExisting) { + CreateSpecialEvent(reinterpret_cast&>(existingEvent), + *optTimestamp, + *optExpires, + *optID, + *optType); + break; + } + + auto special = std::make_unique(); + CreateSpecialEvent(special, *optTimestamp, *optExpires, *optID, *optType); if (!AddEventToTimeline(std::move(special))) { CleanUpQcbor(&decodeContext); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; @@ -455,7 +559,17 @@ namespace Pinetime { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } - auto pressure = CreatePressureEvent(*optTimestamp, *optExpires, *optPressure); + if (useExisting) { + CreatePressureEvent(reinterpret_cast&>(existingEvent), + *optTimestamp, + *optExpires, + *optID, + *optPressure); + break; + } + + auto pressure = std::make_unique(); + CreatePressureEvent(pressure, *optTimestamp, *optExpires, *optID, *optPressure); if (!AddEventToTimeline(std::move(pressure))) { CleanUpQcbor(&decodeContext); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; @@ -487,7 +601,20 @@ namespace Pinetime { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } - auto location = CreateLocationEvent(*optTimestamp, *optExpires, *optLocation, *optAltitude, *optLatitude, *optLongitude); + if (useExisting) { + CreateLocationEvent(reinterpret_cast&>(existingEvent), + *optTimestamp, + *optExpires, + *optID, + *optLocation, + *optAltitude, + *optLatitude, + *optLongitude); + break; + } + + auto location = std::make_unique(); + CreateLocationEvent(location, *optTimestamp, *optExpires, *optID, *optLocation, *optAltitude, *optLatitude, *optLongitude); if (!AddEventToTimeline(std::move(location))) { CleanUpQcbor(&decodeContext); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; @@ -501,7 +628,17 @@ namespace Pinetime { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } - auto clouds = CreateCloudsEvent(*optTimestamp, *optExpires, *optAmount); + if (useExisting) { + CreateCloudsEvent(reinterpret_cast&>(existingEvent), + *optTimestamp, + *optExpires, + *optID, + *optAmount); + break; + } + + auto clouds = std::make_unique(); + CreateCloudsEvent(clouds, *optTimestamp, *optExpires, *optID, *optAmount); if (!AddEventToTimeline(std::move(clouds))) { CleanUpQcbor(&decodeContext); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; @@ -515,7 +652,17 @@ namespace Pinetime { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } - auto humidity = CreateHumidityEvent(*optTimestamp, *optExpires, *optType); + if (useExisting) { + CreateHumidityEvent(reinterpret_cast&>(existingEvent), + *optTimestamp, + *optExpires, + *optID, + *optType); + break; + } + + auto humidity = std::make_unique(); + CreateHumidityEvent(humidity, *optTimestamp, *optExpires, *optID, *optType); if (!AddEventToTimeline(std::move(humidity))) { CleanUpQcbor(&decodeContext); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; @@ -583,6 +730,17 @@ namespace Pinetime { return *best; } + std::unique_ptr& WeatherService::GetEventByID(WeatherData::eventtype eventType, uint16_t id) { + uint64_t currentTimestamp = GetCurrentUnixTimestamp(); + for (auto&& header : this->timeline) { + if (header->eventType == eventType && IsEventStillValid(header, currentTimestamp) && header->eventID == id) { + return header; + } + } + + return *this->nullHeader; + } + std::unique_ptr& WeatherService::GetCurrentClouds() { return reinterpret_cast&>(GetCurrentEvent(WeatherData::eventtype::Clouds)); } diff --git a/src/components/ble/weather/WeatherService.h b/src/components/ble/weather/WeatherService.h index c5c530d1e7..6a98409757 100644 --- a/src/components/ble/weather/WeatherService.h +++ b/src/components/ble/weather/WeatherService.h @@ -150,6 +150,8 @@ namespace Pinetime { std::unique_ptr& GetCurrentEvent(WeatherData::eventtype eventType); + std::unique_ptr& GetEventByID(WeatherData::eventtype eventType, uint16_t ID); + /** * Returns current UNIX timestamp */