From d6cc458ea239c0224c4e7d0692273cdc54f5111f Mon Sep 17 00:00:00 2001 From: JF Date: Sat, 23 Dec 2023 21:35:14 +0100 Subject: [PATCH] Integration of Simple Weather Service (#135) * Integration of Simple Weather Service Replace the old WeatherService by the new Simple Weather Service.= * Update InfiniTime to after SimpleWeatherService got merged --------- Co-authored-by: Reinhold Gschweicher --- CMakeLists.txt | 4 +- InfiniTime | 2 +- .../ble/AlertNotificationService.cpp | 8 +- sim/components/ble/NimbleController.cpp | 2 +- sim/components/ble/NimbleController.h | 6 +- sim/components/ble/SimpleWeatherService.cpp | 110 ++++ sim/components/ble/SimpleWeatherService.h | 125 ++++ sim/components/ble/weather/WeatherService.cpp | 603 ------------------ sim/components/ble/weather/WeatherService.h | 172 ----- 9 files changed, 246 insertions(+), 786 deletions(-) create mode 100644 sim/components/ble/SimpleWeatherService.cpp create mode 100644 sim/components/ble/SimpleWeatherService.h delete mode 100644 sim/components/ble/weather/WeatherService.cpp delete mode 100644 sim/components/ble/weather/WeatherService.h diff --git a/CMakeLists.txt b/CMakeLists.txt index cef0e02..f6286ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,8 +124,8 @@ target_sources(infinisim PUBLIC sim/components/ble/NavigationService.cpp sim/components/ble/NimbleController.h sim/components/ble/NimbleController.cpp - sim/components/ble/weather/WeatherService.h - sim/components/ble/weather/WeatherService.cpp + sim/components/ble/SimpleWeatherService.h + sim/components/ble/SimpleWeatherService.cpp sim/components/brightness/BrightnessController.h sim/components/brightness/BrightnessController.cpp sim/components/firmwarevalidator/FirmwareValidator.h diff --git a/InfiniTime b/InfiniTime index e6b96c2..ca7d8a6 160000 --- a/InfiniTime +++ b/InfiniTime @@ -1 +1 @@ -Subproject commit e6b96c286376454b0aeab3c1444f4d106d755b94 +Subproject commit ca7d8a668d37d3377aeb38d122a9eccafdd6822d diff --git a/sim/components/ble/AlertNotificationService.cpp b/sim/components/ble/AlertNotificationService.cpp index 0ce00b0..5fee5d2 100644 --- a/sim/components/ble/AlertNotificationService.cpp +++ b/sim/components/ble/AlertNotificationService.cpp @@ -11,10 +11,10 @@ using namespace Pinetime::Controllers; //constexpr ble_uuid16_t AlertNotificationService::ansCharUuid; //constexpr ble_uuid128_t AlertNotificationService::notificationEventUuid; -int AlertNotificationCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) { - auto anService = static_cast(arg); - return anService->OnAlert(conn_handle, attr_handle, ctxt); -} +//int AlertNotificationCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) { +//auto anService = static_cast(arg); +// return anService->OnAlert(conn_handle, attr_handle, ctxt); +//} void AlertNotificationService::Init() { // int res; diff --git a/sim/components/ble/NimbleController.cpp b/sim/components/ble/NimbleController.cpp index 3684d25..388b8a1 100644 --- a/sim/components/ble/NimbleController.cpp +++ b/sim/components/ble/NimbleController.cpp @@ -44,7 +44,7 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask, // alertNotificationClient {systemTask, notificationManager}, // currentTimeService {dateTimeController}, musicService {systemTask}, - weatherService {systemTask, dateTimeController}, + weatherService {dateTimeController}, navService {systemTask} { // batteryInformationService {batteryController}, // immediateAlertService {systemTask, notificationManager}, diff --git a/sim/components/ble/NimbleController.h b/sim/components/ble/NimbleController.h index ce7e8d7..c212820 100644 --- a/sim/components/ble/NimbleController.h +++ b/sim/components/ble/NimbleController.h @@ -20,7 +20,7 @@ #include "components/ble/NavigationService.h" //#include "components/ble/ServiceDiscovery.h" //#include "components/ble/MotionService.h" -#include "components/ble/weather/WeatherService.h" +#include "components/ble/SimpleWeatherService.h" #include "components/fs/FS.h" //#include "components/ble/FSService.h" @@ -78,7 +78,7 @@ namespace Pinetime { Pinetime::Controllers::AlertNotificationService& alertService() { return anService; }; - Pinetime::Controllers::WeatherService& weather() { + Pinetime::Controllers::SimpleWeatherService& weather() { return weatherService; }; @@ -111,7 +111,7 @@ namespace Pinetime { // AlertNotificationClient alertNotificationClient; // CurrentTimeService currentTimeService; MusicService musicService; - WeatherService weatherService; + SimpleWeatherService weatherService; NavigationService navService; // BatteryInformationService batteryInformationService; // ImmediateAlertService immediateAlertService; diff --git a/sim/components/ble/SimpleWeatherService.cpp b/sim/components/ble/SimpleWeatherService.cpp new file mode 100644 index 0000000..01fc792 --- /dev/null +++ b/sim/components/ble/SimpleWeatherService.cpp @@ -0,0 +1,110 @@ +#include "components/ble/SimpleWeatherService.h" + +#include +#include +#include +#include + +using namespace Pinetime::Controllers; + +namespace { +enum class MessageType : uint8_t { CurrentWeather, Forecast, Unknown }; + +uint64_t ToUInt64(const uint8_t* data) { + return data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24) + (static_cast(data[4]) << 32) + + (static_cast(data[5]) << 40) + (static_cast(data[6]) << 48) + (static_cast(data[7]) << 56); +} + +int16_t ToInt16(const uint8_t* data) { + return data[0] + (data[1] << 8); +} + +SimpleWeatherService::CurrentWeather CreateCurrentWeather(const uint8_t* dataBuffer) { + SimpleWeatherService::Location cityName; + std::memcpy(cityName.data(), &dataBuffer[16], 32); + cityName[32] = '\0'; + return SimpleWeatherService::CurrentWeather(ToUInt64(&dataBuffer[2]), + ToInt16(&dataBuffer[10]), + ToInt16(&dataBuffer[12]), + ToInt16(&dataBuffer[14]), + SimpleWeatherService::Icons {dataBuffer[16 + 32]}, + std::move(cityName)); +} + +SimpleWeatherService::Forecast CreateForecast(const uint8_t* dataBuffer) { + auto timestamp = static_cast(ToUInt64(&dataBuffer[2])); + + std::array days; + const uint8_t nbDaysInBuffer = dataBuffer[10]; + const uint8_t nbDays = std::min(SimpleWeatherService::MaxNbForecastDays, nbDaysInBuffer); + for (int i = 0; i < nbDays; i++) { + days[i] = SimpleWeatherService::Forecast::Day {ToInt16(&dataBuffer[11 + (i * 5)]), + ToInt16(&dataBuffer[13 + (i * 5)]), + SimpleWeatherService::Icons {dataBuffer[15 + (i * 5)]}}; + } + return SimpleWeatherService::Forecast {timestamp, nbDays, days}; +} + +MessageType GetMessageType(const uint8_t* data) { + auto messageType = static_cast(*data); + if (messageType > MessageType::Unknown) { + return MessageType::Unknown; + } + return messageType; +} + +uint8_t GetVersion(const uint8_t* dataBuffer) { + return dataBuffer[1]; +} +} + +int WeatherCallback(uint16_t /*connHandle*/, uint16_t /*attrHandle*/, struct ble_gatt_access_ctxt* ctxt, void* arg) { + return static_cast(arg)->OnCommand(ctxt); +} + +SimpleWeatherService::SimpleWeatherService(const DateTime& dateTimeController) : dateTimeController(dateTimeController) { +} + +void SimpleWeatherService::Init() { + //ble_gatts_count_cfg(serviceDefinition); + //ble_gatts_add_svcs(serviceDefinition); +} + +int SimpleWeatherService::OnCommand(struct ble_gatt_access_ctxt* ctxt) { + + return 0; +} + +std::optional SimpleWeatherService::Current() const { + if (currentWeather) { + auto currentTime = dateTimeController.UTCDateTime().time_since_epoch(); + auto weatherTpSecond = std::chrono::seconds {currentWeather->timestamp}; + auto weatherTp = std::chrono::duration_cast(weatherTpSecond); + auto delta = currentTime - weatherTp; + + if (delta < std::chrono::hours {24}) { + return currentWeather; + } + } + return {}; +} + +std::optional SimpleWeatherService::GetForecast() const { + if (forecast) { + auto currentTime = dateTimeController.UTCDateTime().time_since_epoch(); + auto weatherTpSecond = std::chrono::seconds {forecast->timestamp}; + auto weatherTp = std::chrono::duration_cast(weatherTpSecond); + auto delta = currentTime - weatherTp; + + if (delta < std::chrono::hours {24}) { + return this->forecast; + } + } + return {}; +} + +bool SimpleWeatherService::CurrentWeather::operator==(const SimpleWeatherService::CurrentWeather& other) const { + return this->iconId == other.iconId && this->temperature == other.temperature && this->timestamp == other.timestamp && + this->maxTemperature == other.maxTemperature && this->minTemperature == other.maxTemperature && + std::strcmp(this->location.data(), other.location.data()) == 0; +} diff --git a/sim/components/ble/SimpleWeatherService.h b/sim/components/ble/SimpleWeatherService.h new file mode 100644 index 0000000..be0d141 --- /dev/null +++ b/sim/components/ble/SimpleWeatherService.h @@ -0,0 +1,125 @@ +#pragma once + +#include +#include +#include +#include + +//#define min // workaround: nimble's min/max macros conflict with libstdc++ +//#define max +//#include +//#include +#include +#include +#include +//#undef max +//#undef min + +#include "components/datetime/DateTimeController.h" + +int WeatherCallback(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt, void* arg); + +namespace Pinetime { +namespace Controllers { + +class SimpleWeatherService { +public: + explicit SimpleWeatherService(const DateTime& dateTimeController); + + void Init(); + + int OnCommand(struct ble_gatt_access_ctxt* ctxt); + + static constexpr uint8_t MaxNbForecastDays = 5; + + enum class Icons : uint8_t { + Sun = 0, // ClearSky + CloudsSun = 1, // FewClouds + Clouds = 2, // Scattered clouds + BrokenClouds = 3, + CloudShowerHeavy = 4, // shower rain + CloudSunRain = 5, // rain + Thunderstorm = 6, + Snow = 7, + Smog = 8, // Mist + Unknown = 255 + }; + + using Location = std::array; // 32 char + \0 (end of string) + + struct CurrentWeather { + CurrentWeather(uint64_t timestamp, + int16_t temperature, + int16_t minTemperature, + int16_t maxTemperature, + Icons iconId, + Location&& location) + : timestamp {timestamp}, + temperature {temperature}, + minTemperature {minTemperature}, + maxTemperature {maxTemperature}, + iconId {iconId}, + location {std::move(location)} { + } + + uint64_t timestamp; + int16_t temperature; + int16_t minTemperature; + int16_t maxTemperature; + Icons iconId; + Location location; + + bool operator==(const CurrentWeather& other) const; + }; + + struct Forecast { + uint64_t timestamp; + uint8_t nbDays; + + struct Day { + int16_t minTemperature; + int16_t maxTemperature; + Icons iconId; + }; + + std::array days; + }; + + std::optional Current() const; + std::optional GetForecast() const; + +private: + // 00050000-78fc-48fe-8e23-433b3a1942d0 + //static constexpr ble_uuid128_t BaseUuid() { + // return CharUuid(0x00, 0x00); + //} + + // 0005yyxx-78fc-48fe-8e23-433b3a1942d0 + //static constexpr ble_uuid128_t CharUuid(uint8_t x, uint8_t y) { + // return ble_uuid128_t {.u = {.type = BLE_UUID_TYPE_128}, + // .value = {0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, y, x, 0x05, 0x00}}; + //} + + //ble_uuid128_t weatherUuid {BaseUuid()}; + + //ble_uuid128_t weatherDataCharUuid {CharUuid(0x00, 0x01)}; + + //const struct ble_gatt_chr_def characteristicDefinition[2] = {{.uuid = &weatherDataCharUuid.u, + // .access_cb = WeatherCallback, + // .arg = this, + // .flags = BLE_GATT_CHR_F_WRITE, + // .val_handle = &eventHandle}, + // {0}}; + //const struct ble_gatt_svc_def serviceDefinition[2] = { + // {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = &weatherUuid.u, .characteristics = characteristicDefinition}, + // {0}}; + + uint16_t eventHandle {}; + + const Pinetime::Controllers::DateTime& dateTimeController; + + std::optional currentWeather; + std::optional forecast; +}; +} +} diff --git a/sim/components/ble/weather/WeatherService.cpp b/sim/components/ble/weather/WeatherService.cpp deleted file mode 100644 index 88ff41a..0000000 --- a/sim/components/ble/weather/WeatherService.cpp +++ /dev/null @@ -1,603 +0,0 @@ -/* Copyright (C) 2021 Avamander - - This file is part of InfiniTime. - - InfiniTime is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - InfiniTime is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ -//#include -#include "components/ble/weather/WeatherService.h" -//#include "libs/QCBOR/inc/qcbor/qcbor.h" -#include "systemtask/SystemTask.h" -#include - -using namespace Pinetime::Controllers; - -int WeatherCallback(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt, void* arg) { - return static_cast(arg)->OnCommand(connHandle, attrHandle, ctxt); -} - - WeatherService::WeatherService(System::SystemTask& system, DateTime& dateTimeController) - : system(system), dateTimeController(dateTimeController) { - nullHeader = &nullTimelineheader; - nullTimelineheader->timestamp = 0; - } - - void WeatherService::Init() { -// uint8_t res = 0; -// res = ble_gatts_count_cfg(serviceDefinition); -// ASSERT(res == 0); -// -// res = ble_gatts_add_svcs(serviceDefinition); -// ASSERT(res == 0); - } - - int WeatherService::OnCommand(uint16_t connHandle, uint16_t attrHandle, 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) - // 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) - - // QCBORDecode_Init(&decodeContext, encodedCbor, QCBOR_DECODE_MODE_NORMAL); - // // KINDLY provide us a fixed-length map - // QCBORDecode_EnterMap(&decodeContext, nullptr); - // // Always encodes to the smallest number of bytes based on the value - // int64_t tmpTimestamp = 0; - // QCBORDecode_GetInt64InMapSZ(&decodeContext, "Timestamp", &tmpTimestamp); - // if (QCBORDecode_GetError(&decodeContext) != QCBOR_SUCCESS) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // int64_t tmpExpires = 0; - // QCBORDecode_GetInt64InMapSZ(&decodeContext, "Expires", &tmpExpires); - // if (QCBORDecode_GetError(&decodeContext) != QCBOR_SUCCESS || tmpExpires < 0 || tmpExpires > 4294967295) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // int64_t tmpEventType = 0; - // QCBORDecode_GetInt64InMapSZ(&decodeContext, "EventType", &tmpEventType); - // if (QCBORDecode_GetError(&decodeContext) != QCBOR_SUCCESS || tmpEventType < 0 || - // tmpEventType >= static_cast(WeatherData::eventtype::Length)) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - - // switch (static_cast(tmpEventType)) { - // case WeatherData::eventtype::AirQuality: { - // std::unique_ptr airquality = std::make_unique(); - // airquality->timestamp = tmpTimestamp; - // airquality->eventType = static_cast(tmpEventType); - // airquality->expires = tmpExpires; - - // UsefulBufC stringBuf; // TODO: Everything ok with lifecycle here? - // QCBORDecode_GetTextStringInMapSZ(&decodeContext, "Polluter", &stringBuf); - // if (UsefulBuf_IsNULLOrEmptyC(stringBuf) != 0) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // airquality->polluter = std::string(static_cast(stringBuf.ptr), stringBuf.len); - - // int64_t tmpAmount = 0; - // QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount); - // if (tmpAmount < 0 || tmpAmount > 4294967295) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // airquality->amount = tmpAmount; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) - - // if (!AddEventToTimeline(std::move(airquality))) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // break; - // } - // case WeatherData::eventtype::Obscuration: { - // std::unique_ptr obscuration = std::make_unique(); - // obscuration->timestamp = tmpTimestamp; - // obscuration->eventType = static_cast(tmpEventType); - // obscuration->expires = tmpExpires; - - // int64_t tmpType = 0; - // QCBORDecode_GetInt64InMapSZ(&decodeContext, "Type", &tmpType); - // if (tmpType < 0 || tmpType >= static_cast(WeatherData::obscurationtype::Length)) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // obscuration->type = static_cast(tmpType); - - // int64_t tmpAmount = 0; - // QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount); - // if (tmpAmount < 0 || tmpAmount > 65535) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // obscuration->amount = tmpAmount; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) - - // if (!AddEventToTimeline(std::move(obscuration))) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // break; - // } - // case WeatherData::eventtype::Precipitation: { - // std::unique_ptr precipitation = std::make_unique(); - // precipitation->timestamp = tmpTimestamp; - // precipitation->eventType = static_cast(tmpEventType); - // precipitation->expires = tmpExpires; - - // int64_t tmpType = 0; - // QCBORDecode_GetInt64InMapSZ(&decodeContext, "Type", &tmpType); - // if (tmpType < 0 || tmpType >= static_cast(WeatherData::precipitationtype::Length)) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // precipitation->type = static_cast(tmpType); - - // int64_t tmpAmount = 0; - // QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount); - // if (tmpAmount < 0 || tmpAmount > 255) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // precipitation->amount = tmpAmount; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) - - // if (!AddEventToTimeline(std::move(precipitation))) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // break; - // } - // case WeatherData::eventtype::Wind: { - // std::unique_ptr wind = std::make_unique(); - // wind->timestamp = tmpTimestamp; - // wind->eventType = static_cast(tmpEventType); - // wind->expires = tmpExpires; - - // int64_t tmpMin = 0; - // QCBORDecode_GetInt64InMapSZ(&decodeContext, "SpeedMin", &tmpMin); - // if (tmpMin < 0 || tmpMin > 255) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // wind->speedMin = tmpMin; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) - - // int64_t tmpMax = 0; - // QCBORDecode_GetInt64InMapSZ(&decodeContext, "SpeedMin", &tmpMax); - // if (tmpMax < 0 || tmpMax > 255) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // wind->speedMax = tmpMax; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) - - // int64_t tmpDMin = 0; - // QCBORDecode_GetInt64InMapSZ(&decodeContext, "DirectionMin", &tmpDMin); - // if (tmpDMin < 0 || tmpDMin > 255) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // wind->directionMin = tmpDMin; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) - - // int64_t tmpDMax = 0; - // QCBORDecode_GetInt64InMapSZ(&decodeContext, "DirectionMax", &tmpDMax); - // if (tmpDMax < 0 || tmpDMax > 255) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // wind->directionMax = tmpDMax; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) - - // if (!AddEventToTimeline(std::move(wind))) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // break; - // } - // case WeatherData::eventtype::Temperature: { - // std::unique_ptr temperature = std::make_unique(); - // temperature->timestamp = tmpTimestamp; - // temperature->eventType = static_cast(tmpEventType); - // temperature->expires = tmpExpires; - - // int64_t tmpTemperature = 0; - // QCBORDecode_GetInt64InMapSZ(&decodeContext, "Temperature", &tmpTemperature); - // if (tmpTemperature < -32768 || tmpTemperature > 32767) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // temperature->temperature = - // static_cast(tmpTemperature); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) - - // int64_t tmpDewPoint = 0; - // QCBORDecode_GetInt64InMapSZ(&decodeContext, "DewPoint", &tmpDewPoint); - // if (tmpDewPoint < -32768 || tmpDewPoint > 32767) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // temperature->dewPoint = - // static_cast(tmpDewPoint); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) - - // if (!AddEventToTimeline(std::move(temperature))) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // break; - // } - // case WeatherData::eventtype::Special: { - // std::unique_ptr special = std::make_unique(); - // special->timestamp = tmpTimestamp; - // special->eventType = static_cast(tmpEventType); - // special->expires = tmpExpires; - - // int64_t tmpType = 0; - // QCBORDecode_GetInt64InMapSZ(&decodeContext, "Type", &tmpType); - // if (tmpType < 0 || tmpType >= static_cast(WeatherData::specialtype::Length)) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // special->type = static_cast(tmpType); - - // if (!AddEventToTimeline(std::move(special))) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // break; - // } - // case WeatherData::eventtype::Pressure: { - // std::unique_ptr pressure = std::make_unique(); - // pressure->timestamp = tmpTimestamp; - // pressure->eventType = static_cast(tmpEventType); - // pressure->expires = tmpExpires; - - // int64_t tmpPressure = 0; - // QCBORDecode_GetInt64InMapSZ(&decodeContext, "Pressure", &tmpPressure); - // if (tmpPressure < 0 || tmpPressure >= 65535) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // pressure->pressure = tmpPressure; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) - - // if (!AddEventToTimeline(std::move(pressure))) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // break; - // } - // case WeatherData::eventtype::Location: { - // std::unique_ptr location = std::make_unique(); - // location->timestamp = tmpTimestamp; - // location->eventType = static_cast(tmpEventType); - // location->expires = tmpExpires; - - // UsefulBufC stringBuf; // TODO: Everything ok with lifecycle here? - // QCBORDecode_GetTextStringInMapSZ(&decodeContext, "Location", &stringBuf); - // if (UsefulBuf_IsNULLOrEmptyC(stringBuf) != 0) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // location->location = std::string(static_cast(stringBuf.ptr), stringBuf.len); - - // int64_t tmpAltitude = 0; - // QCBORDecode_GetInt64InMapSZ(&decodeContext, "Altitude", &tmpAltitude); - // if (tmpAltitude < -32768 || tmpAltitude >= 32767) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // location->altitude = static_cast(tmpAltitude); - - // int64_t tmpLatitude = 0; - // QCBORDecode_GetInt64InMapSZ(&decodeContext, "Latitude", &tmpLatitude); - // if (tmpLatitude < -2147483648 || tmpLatitude >= 2147483647) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // location->latitude = static_cast(tmpLatitude); - - // int64_t tmpLongitude = 0; - // QCBORDecode_GetInt64InMapSZ(&decodeContext, "Longitude", &tmpLongitude); - // if (tmpLongitude < -2147483648 || tmpLongitude >= 2147483647) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // location->latitude = static_cast(tmpLongitude); - - // if (!AddEventToTimeline(std::move(location))) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // break; - // } - // case WeatherData::eventtype::Clouds: { - // std::unique_ptr clouds = std::make_unique(); - // clouds->timestamp = tmpTimestamp; - // clouds->eventType = static_cast(tmpEventType); - // clouds->expires = tmpExpires; - - // int64_t tmpAmount = 0; - // QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount); - // if (tmpAmount < 0 || tmpAmount > 255) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // clouds->amount = static_cast(tmpAmount); - - // if (!AddEventToTimeline(std::move(clouds))) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // break; - // } - // case WeatherData::eventtype::Humidity: { - // std::unique_ptr humidity = std::make_unique(); - // humidity->timestamp = tmpTimestamp; - // humidity->eventType = static_cast(tmpEventType); - // humidity->expires = tmpExpires; - - // int64_t tmpType = 0; - // QCBORDecode_GetInt64InMapSZ(&decodeContext, "Humidity", &tmpType); - // if (tmpType < 0 || tmpType >= 255) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // humidity->humidity = static_cast(tmpType); - - // if (!AddEventToTimeline(std::move(humidity))) { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // break; - // } - // default: { - // CleanUpQcbor(&decodeContext); - // return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - // } - // } - - // QCBORDecode_ExitMap(&decodeContext); - // GetTimelineLength(); - // TidyTimeline(); - - // if (QCBORDecode_Finish(&decodeContext) != QCBOR_SUCCESS) { - // return BLE_ATT_ERR_INSUFFICIENT_RES; - // } - // } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { - // // Encode - // uint8_t buffer[64]; - // QCBOREncodeContext encodeContext; - // /* TODO: This is very much still a test endpoint - // * it needs a characteristic UUID check - // * and actual implementations that show - // * what actually has to be read. - // * WARN: Consider commands not part of the API for now! - // */ - // QCBOREncode_Init(&encodeContext, UsefulBuf_FROM_BYTE_ARRAY(buffer)); - // QCBOREncode_OpenMap(&encodeContext); - // QCBOREncode_AddTextToMap(&encodeContext, "test", UsefulBuf_FROM_SZ_LITERAL("test")); - // QCBOREncode_AddInt64ToMap(&encodeContext, "test", 1ul); - // QCBOREncode_CloseMap(&encodeContext); - - // UsefulBufC encodedEvent; - // auto uErr = QCBOREncode_Finish(&encodeContext, &encodedEvent); - // if (uErr != 0) { - // return BLE_ATT_ERR_INSUFFICIENT_RES; - // } - // auto res = os_mbuf_append(ctxt->om, &buffer, sizeof(buffer)); - // if (res == 0) { - // return BLE_ATT_ERR_INSUFFICIENT_RES; - // } - - // return 0; - // } - return 0; - } - - std::unique_ptr& WeatherService::GetCurrentClouds() { - uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Clouds && IsEventStillValid(header, currentTimestamp)) { - return reinterpret_cast&>(header); - } - } - - return reinterpret_cast&>(*this->nullHeader); - } - - std::unique_ptr& WeatherService::GetCurrentObscuration() { - uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Obscuration && IsEventStillValid(header, currentTimestamp)) { - return reinterpret_cast&>(header); - } - } - - return reinterpret_cast&>(*this->nullHeader); - } - - std::unique_ptr& WeatherService::GetCurrentPrecipitation() { - uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Precipitation && IsEventStillValid(header, currentTimestamp)) { - return reinterpret_cast&>(header); - } - } - - return reinterpret_cast&>(*this->nullHeader); - } - - std::unique_ptr& WeatherService::GetCurrentWind() { - uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Wind && IsEventStillValid(header, currentTimestamp)) { - return reinterpret_cast&>(header); - } - } - - return reinterpret_cast&>(*this->nullHeader); - } - - std::unique_ptr& WeatherService::GetCurrentTemperature() { - uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Temperature && IsEventStillValid(header, currentTimestamp)) { - return reinterpret_cast&>(header); - } - } - - return reinterpret_cast&>(*this->nullHeader); - } - - std::unique_ptr& WeatherService::GetCurrentHumidity() { - uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Humidity && IsEventStillValid(header, currentTimestamp)) { - return reinterpret_cast&>(header); - } - } - - return reinterpret_cast&>(*this->nullHeader); - } - - std::unique_ptr& WeatherService::GetCurrentPressure() { - uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Pressure && IsEventStillValid(header, currentTimestamp)) { - return reinterpret_cast&>(header); - } - } - - return reinterpret_cast&>(*this->nullHeader); - } - - std::unique_ptr& WeatherService::GetCurrentLocation() { - uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Location && IsEventStillValid(header, currentTimestamp)) { - return reinterpret_cast&>(header); - } - } - - return reinterpret_cast&>(*this->nullHeader); - } - - std::unique_ptr& WeatherService::GetCurrentQuality() { - uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::AirQuality && IsEventStillValid(header, currentTimestamp)) { - return reinterpret_cast&>(header); - } - } - - return reinterpret_cast&>(*this->nullHeader); - } - - size_t WeatherService::GetTimelineLength() const { - return timeline.size(); - } - - bool WeatherService::AddEventToTimeline(std::unique_ptr event) { - if (timeline.size() == timeline.max_size()) { - return false; - } - - timeline.push_back(std::move(event)); - return true; - } - - bool WeatherService::HasTimelineEventOfType(const WeatherData::eventtype type) const { - uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : timeline) { - if (header->eventType == type && IsEventStillValid(header, currentTimestamp)) { - return true; - } - } - return false; - } - - void WeatherService::TidyTimeline() { - uint64_t timeCurrent = GetCurrentUnixTimestamp(); - timeline.erase(std::remove_if(std::begin(timeline), - std::end(timeline), - [&](std::unique_ptr const& header) { - return !IsEventStillValid(header, timeCurrent); - }), - std::end(timeline)); - - std::sort(std::begin(timeline), std::end(timeline), CompareTimelineEvents); - } - - bool WeatherService::CompareTimelineEvents(const std::unique_ptr& first, - const std::unique_ptr& second) { - return first->timestamp > second->timestamp; - } - - bool WeatherService::IsEventStillValid(const std::unique_ptr& uniquePtr, const uint64_t timestamp) { - // Not getting timestamp in isEventStillValid for more speed - return uniquePtr->timestamp + uniquePtr->expires >= timestamp; - } - - uint64_t WeatherService::GetCurrentUnixTimestamp() const { - return std::chrono::duration_cast(dateTimeController.CurrentDateTime().time_since_epoch()).count(); - } - - int16_t WeatherService::GetTodayMinTemp() const { - uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - uint64_t currentDayEnd = currentTimestamp - ((24 - dateTimeController.Hours()) * 60 * 60) - - ((60 - dateTimeController.Minutes()) * 60) - (60 - dateTimeController.Seconds()); - int16_t result = -32768; - for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Temperature && IsEventStillValid(header, currentTimestamp) && - header->timestamp < currentDayEnd && - reinterpret_cast&>(header)->temperature != -32768) { - int16_t temperature = reinterpret_cast&>(header)->temperature; - if (result == -32768) { - result = temperature; - } else if (result > temperature) { - result = temperature; - } else { - // The temperature in this item is higher than the lowest we've found - } - } - } - - return result; - } - - int16_t WeatherService::GetTodayMaxTemp() const { - uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - uint64_t currentDayEnd = currentTimestamp - ((24 - dateTimeController.Hours()) * 60 * 60) - - ((60 - dateTimeController.Minutes()) * 60) - (60 - dateTimeController.Seconds()); - int16_t result = -32768; - for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Temperature && IsEventStillValid(header, currentTimestamp) && - header->timestamp < currentDayEnd && - reinterpret_cast&>(header)->temperature != -32768) { - int16_t temperature = reinterpret_cast&>(header)->temperature; - if (result == -32768) { - result = temperature; - } else if (result < temperature) { - result = temperature; - } else { - // The temperature in this item is lower than the highest we've found - } - } - } - - return result; - } - -// void WeatherService::CleanUpQcbor(QCBORDecodeContext* decodeContext) { -// QCBORDecode_ExitMap(decodeContext); -// QCBORDecode_Finish(decodeContext); -// } diff --git a/sim/components/ble/weather/WeatherService.h b/sim/components/ble/weather/WeatherService.h deleted file mode 100644 index ea7560a..0000000 --- a/sim/components/ble/weather/WeatherService.h +++ /dev/null @@ -1,172 +0,0 @@ -/* Copyright (C) 2021 Avamander - - This file is part of InfiniTime. - - InfiniTime is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - InfiniTime is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ -#pragma once - -#include -#include -#include -#include - -//#define min // workaround: nimble's min/max macros conflict with libstdc++ -//#define max -//#include -//#include -//#undef max -//#undef min - -#include "components/ble/weather/WeatherData.h" -//#include "libs/QCBOR/inc/qcbor/qcbor.h" -#include "components/datetime/DateTimeController.h" - -//int WeatherCallback(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt, void* arg); - -namespace Pinetime { - namespace System { - class SystemTask; - } - namespace Controllers { - - class WeatherService { - public: - explicit WeatherService(System::SystemTask& system, DateTime& dateTimeController); - - void Init(); - - int OnCommand(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt); - - /* - * Helper functions for quick access to currently valid data - */ - std::unique_ptr& GetCurrentLocation(); - std::unique_ptr& GetCurrentClouds(); - std::unique_ptr& GetCurrentObscuration(); - std::unique_ptr& GetCurrentPrecipitation(); - std::unique_ptr& GetCurrentWind(); - std::unique_ptr& GetCurrentTemperature(); - std::unique_ptr& GetCurrentHumidity(); - std::unique_ptr& GetCurrentPressure(); - std::unique_ptr& GetCurrentQuality(); - - /** - * Searches for the current day's maximum temperature - * @return -32768 if there's no data, degrees Celsius times 100 otherwise - */ - int16_t GetTodayMaxTemp() const; - /** - * Searches for the current day's minimum temperature - * @return -32768 if there's no data, degrees Celsius times 100 otherwise - */ - int16_t GetTodayMinTemp() const; - - /* - * Management functions - */ - /** - * Adds an event to the timeline - * @return - */ - bool AddEventToTimeline(std::unique_ptr event); - /** - * Gets the current timeline length - */ - size_t GetTimelineLength() const; - /** - * Checks if an event of a certain type exists in the timeline - */ - bool HasTimelineEventOfType(WeatherData::eventtype type) const; - - private: - // 00040000-78fc-48fe-8e23-433b3a1942d0 -// static constexpr ble_uuid128_t BaseUuid() { -// return CharUuid(0x00, 0x00); -// } - -// // 0004yyxx-78fc-48fe-8e23-433b3a1942d0 -// static constexpr ble_uuid128_t CharUuid(uint8_t x, uint8_t y) { -// return ble_uuid128_t {.u = {.type = BLE_UUID_TYPE_128}, -// .value = {0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, y, x, 0x04, 0x00}}; -// } - -// ble_uuid128_t weatherUuid {BaseUuid()}; - - /** - * Just write timeline data here. - * - * See {@link WeatherData.h} for more information. - */ -// ble_uuid128_t weatherDataCharUuid {CharUuid(0x00, 0x01)}; - /** - * This doesn't take timeline data, provides some control over it. - * - * NOTE: Currently not supported. Companion app implementer feedback required. - * There's very little point in solidifying an API before we know the needs. - */ -// ble_uuid128_t weatherControlCharUuid {CharUuid(0x00, 0x02)}; - -// const struct ble_gatt_chr_def characteristicDefinition[3] = { -// {.uuid = &weatherDataCharUuid.u, -// .access_cb = WeatherCallback, -// .arg = this, -// .flags = BLE_GATT_CHR_F_WRITE, -// .val_handle = &eventHandle}, -// {.uuid = &weatherControlCharUuid.u, .access_cb = WeatherCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ}, -// {nullptr}}; -// const struct ble_gatt_svc_def serviceDefinition[2] = { -// {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = &weatherUuid.u, .characteristics = characteristicDefinition}, {0}}; - - uint16_t eventHandle {}; - - Pinetime::System::SystemTask& system; - Pinetime::Controllers::DateTime& dateTimeController; - - std::vector> timeline; - std::unique_ptr nullTimelineheader = std::make_unique(); - std::unique_ptr* nullHeader; - - /** - * Cleans up the timeline of expired events - */ - void TidyTimeline(); - - /** - * Compares two timeline events - */ - static bool CompareTimelineEvents(const std::unique_ptr& first, - const std::unique_ptr& second); - - /** - * Returns current UNIX timestamp - */ - uint64_t GetCurrentUnixTimestamp() const; - - /** - * Checks if the event hasn't gone past and expired - * - * @param header timeline event to check - * @param currentTimestamp what's the time right now - * @return if the event is valid - */ - static bool IsEventStillValid(const std::unique_ptr& uniquePtr, const uint64_t timestamp); - - /** - * This is a helper function that closes a QCBOR map and decoding context cleanly - */ -// void CleanUpQcbor(QCBORDecodeContext* decodeContext); - }; - } -}