Skeleton of the receiving logic

This commit is contained in:
Avamander 2021-06-16 23:31:17 +03:00
parent eb27813c18
commit 6e16584816
3 changed files with 145 additions and 76 deletions

View file

@ -39,7 +39,7 @@ namespace Pinetime {
None = 0, None = 0,
/** Water particles suspended in the air; low visibility; does not fall */ /** Water particles suspended in the air; low visibility; does not fall */
Fog = 1, Fog = 1,
/** Extremely small, dry particles in the air; invisible to the eye; opalescent */ /** Tiny, dry particles in the air; invisible to the eye; opalescent */
Haze = 2, Haze = 2,
/** Small fire-created particles suspended in the air */ /** Small fire-created particles suspended in the air */
Smoke = 3, Smoke = 3,
@ -51,6 +51,7 @@ namespace Pinetime {
Sand = 6, Sand = 6,
/** Water particles suspended in the air; low-ish visibility; temperature is near dewpoint */ /** Water particles suspended in the air; low-ish visibility; temperature is near dewpoint */
Mist = 7, Mist = 7,
Length
}; };
/** /**
@ -82,7 +83,8 @@ namespace Pinetime {
/** Frozen drizzle; very small snow crystals */ /** Frozen drizzle; very small snow crystals */
SnowGrains = 8, SnowGrains = 8,
/** Needles; columns or plates of ice. Sometimes described as "diamond dust". In very cold regions */ /** Needles; columns or plates of ice. Sometimes described as "diamond dust". In very cold regions */
IceCrystals = 9 IceCrystals = 9,
Length
}; };
/** /**
@ -99,6 +101,7 @@ namespace Pinetime {
Fire = 3, Fire = 3,
/** Thunder and/or lightning */ /** Thunder and/or lightning */
Thunder = 4, Thunder = 4,
Length
}; };
/** /**
@ -111,7 +114,8 @@ namespace Pinetime {
/** This wipes the entire timeline */ /** This wipes the entire timeline */
DelTimeline = 1, DelTimeline = 1,
/** There's a currently valid timeline event with the given type */ /** There's a currently valid timeline event with the given type */
HasValidEvent = 3 HasValidEvent = 3,
Length
}; };
/** /**
@ -137,19 +141,20 @@ namespace Pinetime {
Location = 7, Location = 7,
/** @see cloud */ /** @see cloud */
Clouds = 8, Clouds = 8,
Length
}; };
/** /**
* Valid event query * Valid event query
*/ */
class valideventquery { class ValidEventQuery {
public: public:
static constexpr controlcodes code = controlcodes::HasValidEvent; static constexpr controlcodes code = controlcodes::HasValidEvent;
eventtype eventType; eventtype eventType;
}; };
/** The header used for further parsing */ /** The header used for further parsing */
class timelineheader { class TimelineHeader {
public: public:
/** UNIX timestamp */ /** UNIX timestamp */
uint64_t timestamp; uint64_t timestamp;
@ -168,23 +173,23 @@ namespace Pinetime {
}; };
/** Specifies how cloudiness is stored */ /** Specifies how cloudiness is stored */
class clouds : public timelineheader { class Clouds : public TimelineHeader {
public: public:
/** Cloud coverage in percentage, 0-100% */ /** Cloud coverage in percentage, 0-100% */
uint8_t amount; uint8_t amount;
}; };
/** Specifies how obscuration is stored */ /** Specifies how obscuration is stored */
class obscuration : public timelineheader { class Obscuration : public TimelineHeader {
public: public:
/** Type */ /** Type */
obscurationtype type; obscurationtype type;
/** Visibility distance in meters */ /** Visibility distance in meters */
uint8_t amount; uint16_t amount;
}; };
/** Specifies how precipitation is stored */ /** Specifies how precipitation is stored */
class precipitation : public timelineheader { class Precipitation : public TimelineHeader {
public: public:
/** Type */ /** Type */
precipitationtype type; precipitationtype type;
@ -201,7 +206,7 @@ namespace Pinetime {
* As direction can fluctuate wildly and some watchfaces might wish to display it nicely, * As direction can fluctuate wildly and some watchfaces might wish to display it nicely,
* we're following the aerospace industry weather report option of specifying a range. * we're following the aerospace industry weather report option of specifying a range.
*/ */
class wind : public timelineheader { class Wind : public TimelineHeader {
public: public:
/** Meters per second */ /** Meters per second */
uint8_t speedMin; uint8_t speedMin;
@ -221,7 +226,7 @@ namespace Pinetime {
* *
* We don't do floats, microdegrees are not useful. Make sure to multiply. * We don't do floats, microdegrees are not useful. Make sure to multiply.
*/ */
class temperature : public timelineheader { class Temperature : public TimelineHeader {
public: public:
/** Temperature °C but multiplied by 100 (e.g. -12.50°C becomes -1250) */ /** Temperature °C but multiplied by 100 (e.g. -12.50°C becomes -1250) */
int16_t temperature; int16_t temperature;
@ -240,7 +245,7 @@ namespace Pinetime {
* or daylight calculations, should those be required. * or daylight calculations, should those be required.
* *
*/ */
class location : public timelineheader { class Location : public TimelineHeader {
public: public:
/** Location name */ /** Location name */
std::string location; std::string location;
@ -255,7 +260,7 @@ namespace Pinetime {
/** /**
* How humidity is stored * How humidity is stored
*/ */
class humidity : public timelineheader { class Humidity : public TimelineHeader {
public: public:
/** Relative humidity, 0-100% */ /** Relative humidity, 0-100% */
uint8_t humidity; uint8_t humidity;
@ -264,7 +269,7 @@ namespace Pinetime {
/** /**
* How air pressure is stored * How air pressure is stored
*/ */
class pressure : public timelineheader { class Pressure : public TimelineHeader {
public: public:
/** Air pressure in hectopascals (hPa) */ /** Air pressure in hectopascals (hPa) */
int16_t pressure; int16_t pressure;
@ -273,7 +278,7 @@ namespace Pinetime {
/** /**
* How special events are stored * How special events are stored
*/ */
class special : public timelineheader { class Special : public TimelineHeader {
public: public:
/** Special event's type */ /** Special event's type */
specialtype type; specialtype type;
@ -288,7 +293,7 @@ namespace Pinetime {
* *
* If this needs further enforced standardization, pull requests are welcome * If this needs further enforced standardization, pull requests are welcome
*/ */
class airquality : public timelineheader { class AirQuality : public TimelineHeader {
public: public:
/** /**
* The name of the pollution * The name of the pollution

View file

@ -40,58 +40,125 @@ namespace Pinetime {
} }
int WeatherService::OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) { int WeatherService::OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
// TODO: Detect control messages
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
getCurrentPressure();
tidyTimeline();
getTimelineLength();
const auto packetLen = OS_MBUF_PKTLEN(ctxt->om); const auto packetLen = OS_MBUF_PKTLEN(ctxt->om);
if (packetLen <= 0) { if (packetLen <= 0) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
} }
// Decode // Decode
QCBORDecodeContext decodeContext; QCBORDecodeContext decodeContext;
UsefulBufC EncodedCBOR; UsefulBufC encodedCbor;
// TODO: Check uninit fine // TODO: Check, uninit fine?
QCBORDecode_Init(&decodeContext, EncodedCBOR, QCBOR_DECODE_MODE_NORMAL);
QCBORDecode_Init(&decodeContext, encodedCbor, QCBOR_DECODE_MODE_NORMAL);
QCBORDecode_EnterMap(&decodeContext, nullptr); QCBORDecode_EnterMap(&decodeContext, nullptr);
WeatherData::timelineheader timelineHeader {};
// Always encodes to the smallest number of bytes based on the value // Always encodes to the smallest number of bytes based on the value
QCBORDecode_GetInt64InMapSZ(&decodeContext, "Timestamp", reinterpret_cast<int64_t*>(&(timelineHeader.timestamp))); int64_t tmpVersion = 0;
QCBORDecode_GetInt64InMapSZ(&decodeContext, "Expires", reinterpret_cast<int64_t*>(&(timelineHeader.expires))); QCBORDecode_GetInt64InMapSZ(&decodeContext, "Version", &tmpVersion);
QCBORDecode_GetInt64InMapSZ(&decodeContext, "EventType", reinterpret_cast<int64_t*>(&(timelineHeader.eventType))); if (tmpVersion != 1) {
switch (timelineHeader.eventType) { // TODO: Return better error?
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
int64_t tmpTimestamp = 0;
QCBORDecode_GetInt64InMapSZ(&decodeContext, "Timestamp", &tmpTimestamp);
int64_t tmpExpires = 0;
QCBORDecode_GetInt64InMapSZ(&decodeContext, "Expires", &tmpExpires);
if (tmpExpires > 4294967295) {
// TODO: Return better error?
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
int64_t tmpEventType = 0;
QCBORDecode_GetInt64InMapSZ(&decodeContext, "EventType", &tmpEventType);
if (tmpEventType > static_cast<int64_t>(WeatherData::eventtype::Length)) {
// TODO: Return better error?
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
switch (static_cast<WeatherData::eventtype>(tmpEventType)) {
// TODO: Populate // TODO: Populate
case WeatherData::eventtype::AirQuality: { case WeatherData::eventtype::AirQuality: {
std::unique_ptr<WeatherData::AirQuality> airquality = std::make_unique<WeatherData::AirQuality>();
airquality->timestamp = tmpTimestamp;
airquality->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
airquality->expires = tmpExpires;
timeline.push_back(std::move(airquality));
break; break;
} }
case WeatherData::eventtype::Obscuration: { case WeatherData::eventtype::Obscuration: {
std::unique_ptr<WeatherData::Obscuration> obscuration = std::make_unique<WeatherData::Obscuration>();
obscuration->timestamp = tmpTimestamp;
obscuration->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
obscuration->expires = tmpExpires;
timeline.push_back(std::move(obscuration));
break; break;
} }
case WeatherData::eventtype::Precipitation: { case WeatherData::eventtype::Precipitation: {
std::unique_ptr<WeatherData::Precipitation> precipitation = std::make_unique<WeatherData::Precipitation>();
precipitation->timestamp = tmpTimestamp;
precipitation->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
precipitation->expires = tmpExpires;
timeline.push_back(std::move(precipitation));
break; break;
} }
case WeatherData::eventtype::Wind: { case WeatherData::eventtype::Wind: {
std::unique_ptr<WeatherData::Wind> wind = std::make_unique<WeatherData::Wind>();
wind->timestamp = tmpTimestamp;
wind->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
wind->expires = tmpExpires;
timeline.push_back(std::move(wind));
break; break;
} }
case WeatherData::eventtype::Temperature: { case WeatherData::eventtype::Temperature: {
std::unique_ptr<WeatherData::Temperature> temperature = std::make_unique<WeatherData::Temperature>();
temperature->timestamp = tmpTimestamp;
temperature->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
temperature->expires = tmpExpires;
timeline.push_back(std::move(temperature));
break; break;
} }
case WeatherData::eventtype::Special: { case WeatherData::eventtype::Special: {
std::unique_ptr<WeatherData::Special> special = std::make_unique<WeatherData::Special>();
special->timestamp = tmpTimestamp;
special->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
special->expires = tmpExpires;
timeline.push_back(std::move(special));
break; break;
} }
case WeatherData::eventtype::Pressure: { case WeatherData::eventtype::Pressure: {
std::unique_ptr<WeatherData::Pressure> pressure = std::make_unique<WeatherData::Pressure>();
pressure->timestamp = tmpTimestamp;
pressure->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
pressure->expires = tmpExpires;
timeline.push_back(std::move(pressure));
break; break;
} }
case WeatherData::eventtype::Location: { case WeatherData::eventtype::Location: {
std::unique_ptr<WeatherData::Location> location = std::make_unique<WeatherData::Location>();
location->timestamp = tmpTimestamp;
location->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
location->expires = tmpExpires;
timeline.push_back(std::move(location));
break; break;
} }
case WeatherData::eventtype::Clouds: { case WeatherData::eventtype::Clouds: {
std::unique_ptr<WeatherData::Clouds> clouds = std::make_unique<WeatherData::Clouds>();
clouds->timestamp = tmpTimestamp;
clouds->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
clouds->expires = tmpExpires;
timeline.push_back(std::move(clouds));
break; break;
} }
default: { default: {
break; break;
} }
} }
getCurrentPressure();
tidyTimeline();
getTimelineLength();
QCBORDecode_ExitMap(&decodeContext); QCBORDecode_ExitMap(&decodeContext);
auto uErr = QCBORDecode_Finish(&decodeContext); auto uErr = QCBORDecode_Finish(&decodeContext);
@ -99,8 +166,6 @@ namespace Pinetime {
return BLE_ATT_ERR_INSUFFICIENT_RES; return BLE_ATT_ERR_INSUFFICIENT_RES;
} }
} else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
// TODO: Detect control messages
// Encode // Encode
uint8_t buffer[64]; uint8_t buffer[64];
QCBOREncodeContext encodeContext; QCBOREncodeContext encodeContext;
@ -125,46 +190,46 @@ namespace Pinetime {
return 0; return 0;
} }
WeatherData::location WeatherService::getCurrentLocation() const { WeatherData::Location WeatherService::getCurrentLocation() const {
return WeatherData::location(); return WeatherData::Location();
} }
WeatherData::clouds WeatherService::getCurrentClouds() const { WeatherData::Clouds WeatherService::getCurrentClouds() const {
return WeatherData::clouds(); return WeatherData::Clouds();
} }
WeatherData::obscuration WeatherService::getCurrentObscuration() const { WeatherData::Obscuration WeatherService::getCurrentObscuration() const {
return WeatherData::obscuration(); return WeatherData::Obscuration();
} }
WeatherData::precipitation WeatherService::getCurrentPrecipitation() const { WeatherData::Precipitation WeatherService::getCurrentPrecipitation() const {
return WeatherData::precipitation(); return WeatherData::Precipitation();
} }
WeatherData::wind WeatherService::getCurrentWind() const { WeatherData::Wind WeatherService::getCurrentWind() const {
return WeatherData::wind(); return WeatherData::Wind();
} }
WeatherData::temperature WeatherService::getCurrentTemperature() const { WeatherData::Temperature WeatherService::getCurrentTemperature() const {
return WeatherData::temperature(); return WeatherData::Temperature();
} }
WeatherData::humidity WeatherService::getCurrentHumidity() const { WeatherData::Humidity WeatherService::getCurrentHumidity() const {
return WeatherData::humidity(); return WeatherData::Humidity();
} }
WeatherData::pressure WeatherService::getCurrentPressure() const { WeatherData::Pressure WeatherService::getCurrentPressure() const {
uint64_t currentTimestamp = getCurrentUNIXTimestamp(); uint64_t currentTimestamp = getCurrentUNIXTimestamp();
for (auto&& header : timeline) { for (auto&& header : timeline) {
if (header->eventType == WeatherData::eventtype::Pressure && header->timestamp + header->expires <= currentTimestamp) { if (header->eventType == WeatherData::eventtype::Pressure && header->timestamp + header->expires <= currentTimestamp) {
return WeatherData::pressure(); return WeatherData::Pressure();
} }
} }
return WeatherData::pressure(); return WeatherData::Pressure();
} }
WeatherData::airquality WeatherService::getCurrentQuality() const { WeatherData::AirQuality WeatherService::getCurrentQuality() const {
return WeatherData::airquality(); return WeatherData::AirQuality();
} }
size_t WeatherService::getTimelineLength() const { size_t WeatherService::getTimelineLength() const {
return timeline.size(); return timeline.size();
} }
bool WeatherService::addEventToTimeline(std::unique_ptr<WeatherData::timelineheader> event) { bool WeatherService::addEventToTimeline(std::unique_ptr<WeatherData::TimelineHeader> event) {
if (timeline.size() == timeline.max_size()) { if (timeline.size() == timeline.max_size()) {
return false; return false;
} }
@ -188,7 +253,7 @@ namespace Pinetime {
uint64_t timeCurrent = 0; uint64_t timeCurrent = 0;
timeline.erase(std::remove_if(std::begin(timeline), timeline.erase(std::remove_if(std::begin(timeline),
std::end(timeline), std::end(timeline),
[&](std::unique_ptr<WeatherData::timelineheader> const& header) { [&](std::unique_ptr<WeatherData::TimelineHeader> const& header) {
return header->timestamp + header->expires > timeCurrent; return header->timestamp + header->expires > timeCurrent;
}), }),
std::end(timeline)); std::end(timeline));
@ -196,8 +261,8 @@ namespace Pinetime {
std::sort(std::begin(timeline), std::end(timeline), compareTimelineEvents); std::sort(std::begin(timeline), std::end(timeline), compareTimelineEvents);
} }
bool WeatherService::compareTimelineEvents(const std::unique_ptr<WeatherData::timelineheader>& first, bool WeatherService::compareTimelineEvents(const std::unique_ptr<WeatherData::TimelineHeader>& first,
const std::unique_ptr<WeatherData::timelineheader>& second) { const std::unique_ptr<WeatherData::TimelineHeader>& second) {
return first->timestamp > second->timestamp; return first->timestamp > second->timestamp;
} }

View file

@ -57,15 +57,15 @@ namespace Pinetime {
/* /*
* Helper functions for quick access to currently valid data * Helper functions for quick access to currently valid data
*/ */
WeatherData::location getCurrentLocation() const; WeatherData::Location getCurrentLocation() const;
WeatherData::clouds getCurrentClouds() const; WeatherData::Clouds getCurrentClouds() const;
WeatherData::obscuration getCurrentObscuration() const; WeatherData::Obscuration getCurrentObscuration() const;
WeatherData::precipitation getCurrentPrecipitation() const; WeatherData::Precipitation getCurrentPrecipitation() const;
WeatherData::wind getCurrentWind() const; WeatherData::Wind getCurrentWind() const;
WeatherData::temperature getCurrentTemperature() const; WeatherData::Temperature getCurrentTemperature() const;
WeatherData::humidity getCurrentHumidity() const; WeatherData::Humidity getCurrentHumidity() const;
WeatherData::pressure getCurrentPressure() const; WeatherData::Pressure getCurrentPressure() const;
WeatherData::airquality getCurrentQuality() const; WeatherData::AirQuality getCurrentQuality() const;
/* /*
* Management functions * Management functions
@ -74,7 +74,7 @@ namespace Pinetime {
* Adds an event to the timeline * Adds an event to the timeline
* @return * @return
*/ */
bool addEventToTimeline(std::unique_ptr<WeatherData::timelineheader> event); bool addEventToTimeline(std::unique_ptr<WeatherData::TimelineHeader> event);
/** /**
* Gets the current timeline length * Gets the current timeline length
*/ */
@ -86,37 +86,36 @@ namespace Pinetime {
bool hasTimelineEventOfType(WeatherData::eventtype type) const; bool hasTimelineEventOfType(WeatherData::eventtype type) const;
private: private:
ble_uuid128_t msUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = WEATHER_SERVICE_UUID_BASE}; ble_uuid128_t weatherUUID {.u = {.type = BLE_UUID_TYPE_128}, .value = WEATHER_SERVICE_UUID_BASE};
/** /**
* Just write timeline data here * Just write timeline data here
*/ */
ble_uuid128_t wDataCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = WEATHER_SERVICE_CHAR_UUID(0x00, 0x01)}; ble_uuid128_t weatherDataCharUUID {.u = {.type = BLE_UUID_TYPE_128}, .value = WEATHER_SERVICE_CHAR_UUID(0x00, 0x01)};
/** /**
* This doesn't take timeline data * This doesn't take timeline data,
* but provides some control over it * provides some control over it
*/ */
ble_uuid128_t wControlCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = WEATHER_SERVICE_CHAR_UUID(0x00, 0x02)}; ble_uuid128_t weatherControlCharUUID {.u = {.type = BLE_UUID_TYPE_128}, .value = WEATHER_SERVICE_CHAR_UUID(0x00, 0x02)};
const struct ble_gatt_chr_def characteristicDefinition[2] = {{.uuid = reinterpret_cast<ble_uuid_t*>(&wDataCharUuid), const struct ble_gatt_chr_def characteristicDefinition[2] = {{.uuid = &weatherDataCharUUID.u,
.access_cb = WeatherCallback, .access_cb = WeatherCallback,
.arg = this, .arg = this,
.flags = BLE_GATT_CHR_F_NOTIFY, .flags = BLE_GATT_CHR_F_NOTIFY,
.val_handle = &eventHandle}, .val_handle = &eventHandle},
{.uuid = reinterpret_cast<ble_uuid_t*>(&wControlCharUuid), {.uuid = &weatherControlCharUUID.u,
.access_cb = WeatherCallback, .access_cb = WeatherCallback,
.arg = this, .arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ}}; .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ}};
const struct ble_gatt_svc_def serviceDefinition[2] = { const struct ble_gatt_svc_def serviceDefinition[2] = {
{.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = reinterpret_cast<ble_uuid_t*>(&msUuid), .characteristics = characteristicDefinition}, {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = &weatherUUID.u, .characteristics = characteristicDefinition}, {0}};
{0}};
uint16_t eventHandle {}; uint16_t eventHandle {};
Pinetime::System::SystemTask& system; Pinetime::System::SystemTask& system;
Pinetime::Controllers::DateTime& dateTimeController; Pinetime::Controllers::DateTime& dateTimeController;
std::vector<std::unique_ptr<WeatherData::timelineheader>> timeline; std::vector<std::unique_ptr<WeatherData::TimelineHeader>> timeline;
/** /**
* Cleans up the timeline of expired events * Cleans up the timeline of expired events
@ -127,11 +126,11 @@ namespace Pinetime {
/** /**
* Compares two timeline events * Compares two timeline events
*/ */
static bool compareTimelineEvents(const std::unique_ptr<WeatherData::timelineheader>& first, static bool compareTimelineEvents(const std::unique_ptr<WeatherData::TimelineHeader>& first,
const std::unique_ptr<WeatherData::timelineheader>& second); const std::unique_ptr<WeatherData::TimelineHeader>& second);
/** /**
* * Returns current UNIX timestamp
*/ */
uint64_t getCurrentUNIXTimestamp() const; uint64_t getCurrentUNIXTimestamp() const;
}; };