Showing the changes to Source/Libraries/TimeSeriesPlatformLibrary (26 of 28 files). Show all
Unified · Split
| @@ -45,9 +45,13 @@ const decimal_t Decimal::DotNetMaxValue = decimal_t("792281625142643375935439503 · 44 unmodified lines | |||
| 45 | 45 | ||
| 46 | const decimal_t Decimal::DotNetMinValue = decimal_t("-79228162514264337593543950335"); | 46 | const decimal_t Decimal::DotNetMinValue = decimal_t("-79228162514264337593543950335"); |
| 47 | 47 | ||
| 48 | const datetime_t DateTime::MaxValue(max_date_time); | ||
| 49 | |||
| 50 | const datetime_t DateTime::MinValue(min_date_time); | ||
| 51 | |||
| 48 | const string Empty::String {}; | 52 | const string Empty::String {}; |
| 49 | 53 | ||
| 50 | const DateTime Empty::DateTime {}; | 54 | const datetime_t Empty::DateTime {}; |
| 51 | 55 | ||
| 52 | const Guid Empty::Guid = NilGuidGen(); | 56 | const Guid Empty::Guid = NilGuidGen(); |
| 53 | 57 | ||
| @@ -280,7 +284,7 @@ string GSF::PadRight(const string& value, uint32_t count, char padChar) · 226 unmodified lines | |||
| 280 | return value; | 284 | return value; |
| 281 | } | 285 | } |
| 282 | 286 | ||
| 283 | DateTime GSF::DateAdd(const DateTime& value, int32_t addValue, TimeInterval interval) | 287 | datetime_t GSF::DateAdd(const datetime_t& value, int32_t addValue, TimeInterval interval) |
| 284 | { | 288 | { |
| 285 | switch (interval) | 289 | switch (interval) |
| 286 | { | 290 | { |
| @@ -307,7 +311,7 @@ DateTime GSF::DateAdd(const DateTime& value, int32_t addValue, TimeInterval inte · 20 unmodified lines | |||
| 307 | } | 311 | } |
| 308 | } | 312 | } |
| 309 | 313 | ||
| 310 | int32_t GSF::DateDiff(const DateTime& startTime, const DateTime& endTime, TimeInterval interval) | 314 | int32_t GSF::DateDiff(const datetime_t& startTime, const datetime_t& endTime, TimeInterval interval) |
| 311 | { | 315 | { |
| 312 | if (interval < TimeInterval::Hour) | 316 | if (interval < TimeInterval::Hour) |
| 313 | { | 317 | { |
| @@ -345,7 +349,7 @@ int32_t GSF::DateDiff(const DateTime& startTime, const DateTime& endTime, TimeIn · 31 unmodified lines | |||
| 345 | } | 349 | } |
| 346 | } | 350 | } |
| 347 | 351 | ||
| 348 | int32_t GSF::DatePart(const DateTime& value, TimeInterval interval) | 352 | int32_t GSF::DatePart(const datetime_t& value, TimeInterval interval) |
| 349 | { | 353 | { |
| 350 | static float64_t tickInterval = pow(10.0, time_duration::num_fractional_digits()); | 354 | static float64_t tickInterval = pow(10.0, time_duration::num_fractional_digits()); |
| 351 | 355 | ||
| @@ -376,12 +380,12 @@ int32_t GSF::DatePart(const DateTime& value, TimeInterval interval) · 24 unmodified lines | |||
| 376 | } | 380 | } |
| 377 | } | 381 | } |
| 378 | 382 | ||
| 379 | DateTime GSF::Now() | 383 | datetime_t GSF::Now() |
| 380 | { | 384 | { |
| 381 | return DateTime { microsec_clock::local_time() }; | 385 | return datetime_t { microsec_clock::local_time() }; |
| 382 | } | 386 | } |
| 383 | 387 | ||
| 384 | DateTime GSF::UtcNow() | 388 | datetime_t GSF::UtcNow() |
| 385 | { | 389 | { |
| 386 | return DateTime { microsec_clock::universal_time() }; | 390 | return datetime_t { microsec_clock::universal_time() }; |
| 387 | } | 391 | } |
| @@ -48,6 +48,9 @@ namespace GSF · 47 unmodified lines | |||
| 48 | typedef double_t float64_t; | 48 | typedef double_t float64_t; |
| 49 | typedef boost::multiprecision::cpp_dec_float_100 decimal_t; | 49 | typedef boost::multiprecision::cpp_dec_float_100 decimal_t; |
| 50 | 50 | ||
| 51 | // DateTime type | ||
| 52 | typedef boost::posix_time::ptime datetime_t; | ||
| 53 | |||
| 51 | struct Int8 | 54 | struct Int8 |
| 52 | { | 55 | { |
| 53 | static const int8_t MaxValue = static_cast<int8_t>(127); | 56 | static const int8_t MaxValue = static_cast<int8_t>(127); |
| @@ -105,16 +108,26 @@ namespace GSF · 51 unmodified lines | |||
| 105 | static const decimal_t DotNetMinValue; | 108 | static const decimal_t DotNetMinValue; |
| 106 | }; | 109 | }; |
| 107 | 110 | ||
| 111 | struct DateTime | ||
| 112 | { | ||
| 113 | static const datetime_t MaxValue; | ||
| 114 | static const datetime_t MinValue; | ||
| 115 | }; | ||
| 116 | |||
| 108 | struct Ticks | 117 | struct Ticks |
| 109 | { | 118 | { |
| 119 | static const int64_t MaxValue = 3155378975999999999L; // 12/31/1999 11:59:59.999 | ||
| 120 | static const int64_t MinValue = 0L; // 01/01/0001 00:00:00.000 | ||
| 121 | |||
| 122 | static const int64_t UnixBaseOffset = 621355968000000000L; // 01/01/1970 00:00:00.000 | ||
| 123 | static const int64_t PTimeBaseOffset = 441481536000000000L; // 01/01/1400 00:00:00.000 | ||
| 124 | |||
| 110 | static const int64_t PerSecond = 10000000L; | 125 | static const int64_t PerSecond = 10000000L; |
| 111 | static const int64_t PerMillisecond = Ticks::PerSecond / 1000; | 126 | static const int64_t PerMillisecond = Ticks::PerSecond / 1000; |
| 112 | static const int64_t PerMicrosecond = Ticks::PerSecond / 1000000; | 127 | static const int64_t PerMicrosecond = Ticks::PerSecond / 1000000; |
| 113 | static const int64_t PerMinute = 60L * Ticks::PerSecond; | 128 | static const int64_t PerMinute = 60L * Ticks::PerSecond; |
| 114 | static const int64_t PerHour = 60L * Ticks::PerMinute; | 129 | static const int64_t PerHour = 60L * Ticks::PerMinute; |
| 115 | static const int64_t PerDay = 24L * Ticks::PerHour; | 130 | static const int64_t PerDay = 24L * Ticks::PerHour; |
| 116 | static const int64_t UnixBaseOffset = 621355968000000000L; // 01/01/1970 00:00:00 | ||
| 117 | static const int64_t PtimeBaseOffset = 441481536000000000L; // 01/01/1400 00:00:00 | ||
| 118 | }; | 131 | }; |
| 119 | 132 | ||
| 120 | template<class T> | 133 | template<class T> |
| @@ -167,7 +180,6 @@ namespace GSF · 46 unmodified lines | |||
| 167 | 180 | ||
| 168 | typedef boost::any Object; | 181 | typedef boost::any Object; |
| 169 | typedef boost::uuids::uuid Guid; | 182 | typedef boost::uuids::uuid Guid; |
| 170 | typedef boost::posix_time::ptime DateTime; | ||
| 171 | typedef boost::posix_time::time_duration TimeSpan; | 183 | typedef boost::posix_time::time_duration TimeSpan; |
| 172 | typedef boost::system::error_code ErrorCode; | 184 | typedef boost::system::error_code ErrorCode; |
| 173 | typedef boost::system::system_error SystemError; | 185 | typedef boost::system::system_error SystemError; |
| @@ -192,7 +204,7 @@ namespace GSF · 18 unmodified lines | |||
| 192 | struct Empty | 204 | struct Empty |
| 193 | { | 205 | { |
| 194 | static const std::string String; | 206 | static const std::string String; |
| 195 | static const GSF::DateTime DateTime; | 207 | static const GSF::datetime_t DateTime; |
| 196 | static const GSF::Guid Guid; | 208 | static const GSF::Guid Guid; |
| 197 | static const GSF::Object Object; | 209 | static const GSF::Object Object; |
| 198 | static const GSF::IPAddress IPAddress; | 210 | static const GSF::IPAddress IPAddress; |
| @@ -394,11 +406,11 @@ namespace GSF · 195 unmodified lines | |||
| 394 | Millisecond | 406 | Millisecond |
| 395 | }; | 407 | }; |
| 396 | 408 | ||
| 397 | DateTime DateAdd(const DateTime& value, int32_t addValue, TimeInterval interval); | 409 | datetime_t DateAdd(const datetime_t& value, int32_t addValue, TimeInterval interval); |
| 398 | int32_t DateDiff(const DateTime& startTime, const DateTime& endTime, TimeInterval interval); | 410 | int32_t DateDiff(const datetime_t& startTime, const datetime_t& endTime, TimeInterval interval); |
| 399 | int32_t DatePart(const DateTime& value, TimeInterval interval); | 411 | int32_t DatePart(const datetime_t& value, TimeInterval interval); |
| 400 | DateTime Now(); | 412 | datetime_t Now(); |
| 401 | DateTime UtcNow(); | 413 | datetime_t UtcNow(); |
| 402 | } | 414 | } |
| 403 | 415 | ||
| 404 | // Setup standard hash code for Guid | 416 | // Setup standard hash code for Guid |
| @@ -40,7 +40,7 @@ using namespace boost::posix_time; · 39 unmodified lines | |||
| 40 | using namespace boost::gregorian; | 40 | using namespace boost::gregorian; |
| 41 | using namespace GSF; | 41 | using namespace GSF; |
| 42 | 42 | ||
| 43 | const DateTime DateTimeEpoch(date(1400, 1, 1), time_duration(0, 0, 0)); | 43 | const datetime_t DateTimeEpoch(date(1400, 1, 1), time_duration(0, 0, 0)); |
| 44 | const auto DateTimeTicksPerSecond = time_duration::ticks_per_second(); | 44 | const auto DateTimeTicksPerSecond = time_duration::ticks_per_second(); |
| 45 | 45 | ||
| 46 | string PreparseTimestamp(const string& timestamp, time_duration& utcOffset) | 46 | string PreparseTimestamp(const string& timestamp, time_duration& utcOffset) |
| @@ -161,24 +161,24 @@ void GSF::ToUnixTime(const int64_t ticks, time_t& unixSOC, uint16_t& millisecond · 114 unmodified lines | |||
| 161 | milliseconds = static_cast<uint16_t>(ticks / 10000 % 1000); | 161 | milliseconds = static_cast<uint16_t>(ticks / 10000 % 1000); |
| 162 | } | 162 | } |
| 163 | 163 | ||
| 164 | DateTime GSF::FromUnixTime(time_t unixSOC, uint16_t milliseconds) | 164 | datetime_t GSF::FromUnixTime(time_t unixSOC, uint16_t milliseconds) |
| 165 | { | 165 | { |
| 166 | return from_time_t(unixSOC) + boost::posix_time::milliseconds(milliseconds); | 166 | return from_time_t(unixSOC) + boost::posix_time::milliseconds(milliseconds); |
| 167 | } | 167 | } |
| 168 | 168 | ||
| 169 | DateTime GSF::FromTicks(const int64_t ticks) | 169 | datetime_t GSF::FromTicks(const int64_t ticks) |
| 170 | { | 170 | { |
| 171 | static float64_t tickInterval = float64_t(Ticks::PerSecond); | 171 | static float64_t tickInterval = float64_t(Ticks::PerSecond); |
| 172 | const DateTime time = from_time_t((ticks - Ticks::UnixBaseOffset) / Ticks::PerSecond); | 172 | const datetime_t time = from_time_t((ticks - Ticks::UnixBaseOffset) / Ticks::PerSecond); |
| 173 | const int64_t pticks = int64_t(ticks % Ticks::PerSecond / tickInterval * DateTimeTicksPerSecond); | 173 | const int64_t pticks = int64_t(ticks % Ticks::PerSecond / tickInterval * DateTimeTicksPerSecond); |
| 174 | return time + time_duration(0, 0, 0, pticks % DateTimeTicksPerSecond); | 174 | return time + time_duration(0, 0, 0, pticks % DateTimeTicksPerSecond); |
| 175 | } | 175 | } |
| 176 | 176 | ||
| 177 | int64_t GSF::ToTicks(const DateTime& time) | 177 | int64_t GSF::ToTicks(const datetime_t& time) |
| 178 | { | 178 | { |
| 179 | static float64_t tickInterval = pow(10.0, time_duration::num_fractional_digits()); | 179 | static float64_t tickInterval = pow(10.0, time_duration::num_fractional_digits()); |
| 180 | const time_duration offset = time - DateTimeEpoch; | 180 | const time_duration offset = time - DateTimeEpoch; |
| 181 | return Ticks::PtimeBaseOffset + offset.total_seconds() * Ticks::PerSecond + | 181 | return Ticks::PTimeBaseOffset + offset.total_seconds() * Ticks::PerSecond + |
| 182 | int64_t(offset.fractional_seconds() / tickInterval * Ticks::PerSecond); | 182 | int64_t(offset.fractional_seconds() / tickInterval * Ticks::PerSecond); |
| 183 | } | 183 | } |
| 184 | 184 | ||
| @@ -240,9 +240,9 @@ uint32_t GSF::TicksToString(char* ptr, uint32_t maxsize, string format, int64_t · 55 unmodified lines | |||
| 240 | return strftime(ptr, maxsize, formatStream.str().data(), &timeinfo); | 240 | return strftime(ptr, maxsize, formatStream.str().data(), &timeinfo); |
| 241 | } | 241 | } |
| 242 | 242 | ||
| 243 | DateTime GSF::LocalFromUtc(const DateTime& timestamp) | 243 | datetime_t GSF::LocalFromUtc(const datetime_t& timestamp) |
| 244 | { | 244 | { |
| 245 | return boost::date_time::c_local_adjustor<DateTime>::utc_to_local(timestamp); | 245 | return boost::date_time::c_local_adjustor<datetime_t>::utc_to_local(timestamp); |
| 246 | } | 246 | } |
| 247 | 247 | ||
| 248 | std::string GSF::ToString(const Guid& value) | 248 | std::string GSF::ToString(const Guid& value) |
| @@ -250,7 +250,7 @@ std::string GSF::ToString(const Guid& value) · 1 unmodified line | |||
| 250 | return boost::uuids::to_string(value); | 250 | return boost::uuids::to_string(value); |
| 251 | } | 251 | } |
| 252 | 252 | ||
| 253 | std::string GSF::ToString(const DateTime& value, const char* format) | 253 | std::string GSF::ToString(const datetime_t& value, const char* format) |
| 254 | { | 254 | { |
| 255 | stringstream stream; | 255 | stringstream stream; |
| 256 | time_facet* facet = new time_facet(format); | 256 | time_facet* facet = new time_facet(format); |
| @@ -309,7 +309,35 @@ bool GSF::ParseBoolean(const string& value) · 52 unmodified lines | |||
| 309 | return false; | 309 | return false; |
| 310 | } | 310 | } |
| 311 | 311 | ||
| 312 | bool GSF::TryParseDouble(const string& value, float64_t& result) | 312 | bool GSF::TryParseInt32(const std::string& value, int32_t& result, const int32_t defaultValue) |
| 313 | { | ||
| 314 | try | ||
| 315 | { | ||
| 316 | result = stoi(value); | ||
| 317 | return true; | ||
| 318 | } | ||
| 319 | catch (...) | ||
| 320 | { | ||
| 321 | result = defaultValue; | ||
| 322 | return false; | ||
| 323 | } | ||
| 324 | } | ||
| 325 | |||
| 326 | bool GSF::TryParseInt64(const std::string& value, int64_t& result, const int64_t defaultValue) | ||
| 327 | { | ||
| 328 | try | ||
| 329 | { | ||
| 330 | result = stoll(value); | ||
| 331 | return true; | ||
| 332 | } | ||
| 333 | catch (...) | ||
| 334 | { | ||
| 335 | result = defaultValue; | ||
| 336 | return false; | ||
| 337 | } | ||
| 338 | } | ||
| 339 | |||
| 340 | bool GSF::TryParseDouble(const string& value, float64_t& result, const float64_t defaultValue) | ||
| 313 | { | 341 | { |
| 314 | try | 342 | try |
| 315 | { | 343 | { |
| @@ -318,6 +346,7 @@ bool GSF::TryParseDouble(const string& value, float64_t& result) · 2 unmodified lines | |||
| 318 | } | 346 | } |
| 319 | catch (...) | 347 | catch (...) |
| 320 | { | 348 | { |
| 349 | result = defaultValue; | ||
| 321 | return false; | 350 | return false; |
| 322 | } | 351 | } |
| 323 | } | 352 | } |
| @@ -428,8 +457,8 @@ const char* GSF::Coalesce(const char* data, const char* nonEmptyValue) · 104 unmodified lines | |||
| 428 | return data; | 457 | return data; |
| 429 | } | 458 | } |
| 430 | 459 | ||
| 431 | // Parse a timestamp string, e.g.: 2018-03-14T19:23:11.665-04:00 | 460 | // Attempt to parse a timestamp string, e.g.: 2018-03-14T19:23:11.665-04:00 |
| 432 | bool GSF::TryParseTimestamp(const char* time, DateTime& timestamp, bool parseAsUTC) | 461 | bool GSF::TryParseTimestamp(const char* time, datetime_t& timestamp, const datetime_t& defaultValue, bool parseAsUTC) |
| 433 | { | 462 | { |
| 434 | static const locale formats[] = { | 463 | static const locale formats[] = { |
| 435 | locale(locale::classic(), new time_input_facet("%Y-%m-%d %H:%M:%S%F")), | 464 | locale(locale::classic(), new time_input_facet("%Y-%m-%d %H:%M:%S%F")), |
| @@ -438,10 +467,12 @@ bool GSF::TryParseTimestamp(const char* time, DateTime& timestamp, bool parseAsU · 2 unmodified lines | |||
| 438 | 467 | ||
| 439 | static const int32_t formatsCount = sizeof(formats) / sizeof(formats[0]); | 468 | static const int32_t formatsCount = sizeof(formats) / sizeof(formats[0]); |
| 440 | 469 | ||
| 470 | time_duration utcOffset{}; | ||
| 471 | const string cleanTimestamp = PreparseTimestamp(time, utcOffset); | ||
| 472 | |||
| 441 | for (int32_t i = 0; i < formatsCount; i++) | 473 | for (int32_t i = 0; i < formatsCount; i++) |
| 442 | { | 474 | { |
| 443 | time_duration utcOffset{}; | 475 | istringstream stream(cleanTimestamp); |
| 444 | istringstream stream(PreparseTimestamp(time, utcOffset)); | ||
| 445 | 476 | ||
| 446 | stream.imbue(formats[i]); | 477 | stream.imbue(formats[i]); |
| 447 | stream >> timestamp; | 478 | stream >> timestamp; |
| @@ -455,14 +486,15 @@ bool GSF::TryParseTimestamp(const char* time, DateTime& timestamp, bool parseAsU · 7 unmodified lines | |||
| 455 | } | 486 | } |
| 456 | } | 487 | } |
| 457 | 488 | ||
| 489 | timestamp = defaultValue; | ||
| 458 | return false; | 490 | return false; |
| 459 | } | 491 | } |
| 460 | 492 | ||
| 461 | DateTime GSF::ParseTimestamp(const char* time, bool parseAsUTC) | 493 | datetime_t GSF::ParseTimestamp(const char* time, bool parseAsUTC) |
| 462 | { | 494 | { |
| 463 | DateTime timestamp; | 495 | datetime_t timestamp; |
| 464 | 496 | ||
| 465 | if (TryParseTimestamp(time, timestamp, parseAsUTC)) | 497 | if (TryParseTimestamp(time, timestamp, DateTime::MinValue, parseAsUTC)) |
| 466 | return timestamp; | 498 | return timestamp; |
| 467 | 499 | ||
| 468 | throw runtime_error("Failed to parse timestamp \"" + string(time) + "\""); | 500 | throw runtime_error("Failed to parse timestamp \"" + string(time) + "\""); |
| @@ -32,18 +32,18 @@ namespace GSF · 31 unmodified lines | |||
| 32 | void ToUnixTime(int64_t ticks, time_t& unixSOC, uint16_t& milliseconds); | 32 | void ToUnixTime(int64_t ticks, time_t& unixSOC, uint16_t& milliseconds); |
| 33 | 33 | ||
| 34 | // Convert Unix second of century and milliseconds to DateTime | 34 | // Convert Unix second of century and milliseconds to DateTime |
| 35 | DateTime FromUnixTime(time_t unixSOC, uint16_t milliseconds); | 35 | datetime_t FromUnixTime(time_t unixSOC, uint16_t milliseconds); |
| 36 | 36 | ||
| 37 | // Converts a timestamp, in Ticks, to DateTime | 37 | // Converts a timestamp, in Ticks, to DateTime |
| 38 | DateTime FromTicks(int64_t ticks); | 38 | datetime_t FromTicks(int64_t ticks); |
| 39 | 39 | ||
| 40 | // Converts a DateTime to Ticks | 40 | // Converts a DateTime to Ticks |
| 41 | int64_t ToTicks(const DateTime& time); | 41 | int64_t ToTicks(const datetime_t& time); |
| 42 | 42 | ||
| 43 | // Thin wrapper around strftime to provide formats for milliseconds (%f) and full-resolution ticks (%t) | 43 | // Thin wrapper around strftime to provide formats for milliseconds (%f) and full-resolution ticks (%t) |
| 44 | uint32_t TicksToString(char* ptr, uint32_t maxsize, std::string format, int64_t ticks); | 44 | uint32_t TicksToString(char* ptr, uint32_t maxsize, std::string format, int64_t ticks); |
| 45 | 45 | ||
| 46 | DateTime LocalFromUtc(const DateTime& timestamp); | 46 | datetime_t LocalFromUtc(const datetime_t& timestamp); |
| 47 | 47 | ||
| 48 | // Converts an object to a string | 48 | // Converts an object to a string |
| 49 | template<class T> | 49 | template<class T> |
| @@ -56,7 +56,7 @@ namespace GSF · 6 unmodified lines | |||
| 56 | 56 | ||
| 57 | std::string ToString(const Guid& value); | 57 | std::string ToString(const Guid& value); |
| 58 | 58 | ||
| 59 | std::string ToString(const DateTime& value, const char* format = "%Y-%m-%d %H:%M:%S%F"); | 59 | std::string ToString(const datetime_t& value, const char* format = "%Y-%m-%d %H:%M:%S%F"); |
| 60 | 60 | ||
| 61 | std::string ToString(const TimeSpan& value); | 61 | std::string ToString(const TimeSpan& value); |
| 62 | 62 | ||
| @@ -74,7 +74,11 @@ namespace GSF · 11 unmodified lines | |||
| 74 | 74 | ||
| 75 | bool ParseBoolean(const std::string& value); | 75 | bool ParseBoolean(const std::string& value); |
| 76 | 76 | ||
| 77 | bool TryParseDouble(const std::string& value, float64_t& result); | 77 | bool TryParseInt32(const std::string& value, int32_t& result, int32_t defaultValue = 0); |
| 78 | |||
| 79 | bool TryParseInt64(const std::string& value, int64_t& result, int64_t defaultValue = 0L); | ||
| 80 | |||
| 81 | bool TryParseDouble(const std::string& value, float64_t& result, float64_t defaultValue = 0.0); | ||
| 78 | 82 | ||
| 79 | // Encodes a character value into an escaped RegEx value | 83 | // Encodes a character value into an escaped RegEx value |
| 80 | std::string RegExEncode(char value); | 84 | std::string RegExEncode(char value); |
| @@ -88,10 +92,10 @@ namespace GSF · 7 unmodified lines | |||
| 88 | const char* Coalesce(const char* data, const char* nonEmptyValue); | 92 | const char* Coalesce(const char* data, const char* nonEmptyValue); |
| 89 | 93 | ||
| 90 | // Attempts to parse an time string in several common formats | 94 | // Attempts to parse an time string in several common formats |
| 91 | bool TryParseTimestamp(const char* time, DateTime& timestamp, bool parseAsUTC = true); | 95 | bool TryParseTimestamp(const char* time, datetime_t& timestamp, const datetime_t& defaultValue = DateTime::MinValue, bool parseAsUTC = true); |
| 92 | 96 | ||
| 93 | // Converts a string to a date-time that may be in several common formats | 97 | // Converts a string to a date-time that may be in several common formats |
| 94 | DateTime ParseTimestamp(const char* time, bool parseAsUTC = true); | 98 | datetime_t ParseTimestamp(const char* time, bool parseAsUTC = true); |
| 95 | 99 | ||
| 96 | // Parses a string of key/value pairs into a case-insensitive string dictionary | 100 | // Parses a string of key/value pairs into a case-insensitive string dictionary |
| 97 | StringMap<std::string> ParseKeyValuePairs(const std::string& value, char parameterDelimiter = ';', char keyValueDelimiter = '=', char startValueDelimiter = '{', char endValueDelimiter = '}'); | 101 | StringMap<std::string> ParseKeyValuePairs(const std::string& value, char parameterDelimiter = ';', char keyValueDelimiter = '=', char startValueDelimiter = '{', char endValueDelimiter = '}'); |
| @@ -324,7 +324,7 @@ namespace GSF · 323 unmodified lines | |||
| 324 | return {}; | 324 | return {}; |
| 325 | } | 325 | } |
| 326 | 326 | ||
| 327 | inline std::string ToString(const Nullable<DateTime>& value, const char* fmt = "%Y-%m-%d %H:%M:%S%F") | 327 | inline std::string ToString(const Nullable<datetime_t>& value, const char* fmt = "%Y-%m-%d %H:%M:%S%F") |
| 328 | { | 328 | { |
| 329 | if (value.HasValue()) | 329 | if (value.HasValue()) |
| 330 | return ToString(value.GetValueOrDefault(), fmt); | 330 | return ToString(value.GetValueOrDefault(), fmt); |
| @@ -394,7 +394,7 @@ Object DataRow::GetComputedValue(const DataColumnPtr& column, DataType targetTyp · 393 unmodified lines | |||
| 394 | } | 394 | } |
| 395 | case ExpressionValueType::DateTime: | 395 | case ExpressionValueType::DateTime: |
| 396 | { | 396 | { |
| 397 | const DateTime result = sourceValue->ValueAsDateTime(); | 397 | const datetime_t result = sourceValue->ValueAsDateTime(); |
| 398 | const time_t value = to_time_t(result); | 398 | const time_t value = to_time_t(result); |
| 399 | 399 | ||
| 400 | switch (targetType) | 400 | switch (targetType) |
| @@ -690,22 +690,22 @@ void DataRow::SetBooleanValue(const string& columnName, const Nullable<bool>& va · 289 unmodified lines | |||
| 690 | SetBooleanValue(GetColumnIndex(columnName), value); | 690 | SetBooleanValue(GetColumnIndex(columnName), value); |
| 691 | } | 691 | } |
| 692 | 692 | ||
| 693 | Nullable<DateTime> DataRow::ValueAsDateTime(const int32_t columnIndex) | 693 | Nullable<datetime_t> DataRow::ValueAsDateTime(const int32_t columnIndex) |
| 694 | { | 694 | { |
| 695 | return GetValue<DateTime>(columnIndex, DataType::DateTime); | 695 | return GetValue<datetime_t>(columnIndex, DataType::DateTime); |
| 696 | } | 696 | } |
| 697 | 697 | ||
| 698 | Nullable<DateTime> DataRow::ValueAsDateTime(const string& columnName) | 698 | Nullable<datetime_t> DataRow::ValueAsDateTime(const string& columnName) |
| 699 | { | 699 | { |
| 700 | return ValueAsDateTime(GetColumnIndex(columnName)); | 700 | return ValueAsDateTime(GetColumnIndex(columnName)); |
| 701 | } | 701 | } |
| 702 | 702 | ||
| 703 | void DataRow::SetDateTimeValue(const int32_t columnIndex, const Nullable<DateTime>& value) | 703 | void DataRow::SetDateTimeValue(const int32_t columnIndex, const Nullable<datetime_t>& value) |
| 704 | { | 704 | { |
| 705 | SetValue<DateTime>(columnIndex, value, DataType::DateTime); | 705 | SetValue<datetime_t>(columnIndex, value, DataType::DateTime); |
| 706 | } | 706 | } |
| 707 | 707 | ||
| 708 | void DataRow::SetDateTimeValue(const string& columnName, const Nullable<DateTime>& value) | 708 | void DataRow::SetDateTimeValue(const string& columnName, const Nullable<datetime_t>& value) |
| 709 | { | 709 | { |
| 710 | SetDateTimeValue(GetColumnIndex(columnName), value); | 710 | SetDateTimeValue(GetColumnIndex(columnName), value); |
| 711 | } | 711 | } |
| @@ -87,10 +87,10 @@ namespace Data · 86 unmodified lines | |||
| 87 | void SetBooleanValue(int32_t columnIndex, const GSF::Nullable<bool>& value); | 87 | void SetBooleanValue(int32_t columnIndex, const GSF::Nullable<bool>& value); |
| 88 | void SetBooleanValue(const std::string& columnName, const GSF::Nullable<bool>& value); | 88 | void SetBooleanValue(const std::string& columnName, const GSF::Nullable<bool>& value); |
| 89 | 89 | ||
| 90 | GSF::Nullable<GSF::DateTime> ValueAsDateTime(int32_t columnIndex); | 90 | GSF::Nullable<GSF::datetime_t> ValueAsDateTime(int32_t columnIndex); |
| 91 | GSF::Nullable<GSF::DateTime> ValueAsDateTime(const std::string& columnName); | 91 | GSF::Nullable<GSF::datetime_t> ValueAsDateTime(const std::string& columnName); |
| 92 | void SetDateTimeValue(int32_t columnIndex, const GSF::Nullable<GSF::DateTime>& value); | 92 | void SetDateTimeValue(int32_t columnIndex, const GSF::Nullable<GSF::datetime_t>& value); |
| 93 | void SetDateTimeValue(const std::string& columnName, const GSF::Nullable<GSF::DateTime>& value); | 93 | void SetDateTimeValue(const std::string& columnName, const GSF::Nullable<GSF::datetime_t>& value); |
| 94 | 94 | ||
| 95 | GSF::Nullable<GSF::float32_t> ValueAsSingle(int32_t columnIndex); | 95 | GSF::Nullable<GSF::float32_t> ValueAsSingle(int32_t columnIndex); |
| 96 | GSF::Nullable<GSF::float32_t> ValueAsSingle(const std::string& columnName); | 96 | GSF::Nullable<GSF::float32_t> ValueAsSingle(const std::string& columnName); |
| @@ -387,24 +387,24 @@ Nullable<Guid> ValueExpression::ValueAsNullableGuid() const · 386 unmodified lines | |||
| 387 | return Cast<Guid>(Value); | 387 | return Cast<Guid>(Value); |
| 388 | } | 388 | } |
| 389 | 389 | ||
| 390 | DateTime ValueExpression::ValueAsDateTime() const | 390 | datetime_t ValueExpression::ValueAsDateTime() const |
| 391 | { | 391 | { |
| 392 | ValidateValueType(ExpressionValueType::DateTime); | 392 | ValidateValueType(ExpressionValueType::DateTime); |
| 393 | 393 | ||
| 394 | if (ValueIsNullable) | 394 | if (ValueIsNullable) |
| 395 | return Cast<Nullable<DateTime>>(Value).GetValueOrDefault(); | 395 | return Cast<Nullable<datetime_t>>(Value).GetValueOrDefault(); |
| 396 | 396 | ||
| 397 | return Cast<DateTime>(Value); | 397 | return Cast<datetime_t>(Value); |
| 398 | } | 398 | } |
| 399 | 399 | ||
| 400 | Nullable<DateTime> ValueExpression::ValueAsNullableDateTime() const | 400 | Nullable<datetime_t> ValueExpression::ValueAsNullableDateTime() const |
| 401 | { | 401 | { |
| 402 | ValidateValueType(ExpressionValueType::DateTime); | 402 | ValidateValueType(ExpressionValueType::DateTime); |
| 403 | 403 | ||
| 404 | if (ValueIsNullable) | 404 | if (ValueIsNullable) |
| 405 | return Cast<Nullable<DateTime>>(Value); | 405 | return Cast<Nullable<datetime_t>>(Value); |
| 406 | 406 | ||
| 407 | return Cast<DateTime>(Value); | 407 | return Cast<datetime_t>(Value); |
| 408 | } | 408 | } |
| 409 | 409 | ||
| 410 | UnaryExpression::UnaryExpression(const ExpressionUnaryType unaryType, ExpressionPtr value) : | 410 | UnaryExpression::UnaryExpression(const ExpressionUnaryType unaryType, ExpressionPtr value) : |
| @@ -1626,7 +1626,7 @@ ValueExpressionPtr ExpressionTree::IsDate(const ValueExpressionPtr& testValue) c · 1215 unmodified lines | |||
| 1626 | if (testValue->ValueType == ExpressionValueType::DateTime) | 1626 | if (testValue->ValueType == ExpressionValueType::DateTime) |
| 1627 | return ExpressionTree::True; | 1627 | return ExpressionTree::True; |
| 1628 | 1628 | ||
| 1629 | DateTime timestamp; | 1629 | datetime_t timestamp; |
| 1630 | 1630 | ||
| 1631 | if (testValue->ValueType == ExpressionValueType::String && TryParseTimestamp(testValue->ValueAsString().c_str(), timestamp)) | 1631 | if (testValue->ValueType == ExpressionValueType::String && TryParseTimestamp(testValue->ValueAsString().c_str(), timestamp)) |
| 1632 | return ExpressionTree::True; | 1632 | return ExpressionTree::True; |
| @@ -3119,7 +3119,7 @@ ValueExpressionPtr ExpressionTree::Convert(const ValueExpressionPtr& sourceValue · 1486 unmodified lines | |||
| 3119 | } | 3119 | } |
| 3120 | case ExpressionValueType::DateTime: | 3120 | case ExpressionValueType::DateTime: |
| 3121 | { | 3121 | { |
| 3122 | const DateTime result = sourceValue->ValueAsDateTime(); | 3122 | const datetime_t result = sourceValue->ValueAsDateTime(); |
| 3123 | const time_t value = to_time_t(result); | 3123 | const time_t value = to_time_t(result); |
| 3124 | 3124 | ||
| 3125 | switch (targetValueType) | 3125 | switch (targetValueType) |
| @@ -3236,7 +3236,7 @@ ValueExpressionPtr ExpressionTree::NullValue(const ExpressionValueType targetVal · 110 unmodified lines | |||
| 3236 | case ExpressionValueType::Guid: | 3236 | case ExpressionValueType::Guid: |
| 3237 | return NewSharedPtr<ValueExpression>(ExpressionValueType::Guid, Nullable<Guid>(nullptr), true); | 3237 | return NewSharedPtr<ValueExpression>(ExpressionValueType::Guid, Nullable<Guid>(nullptr), true); |
| 3238 | case ExpressionValueType::DateTime: | 3238 | case ExpressionValueType::DateTime: |
| 3239 | return NewSharedPtr<ValueExpression>(ExpressionValueType::DateTime, Nullable<DateTime>(nullptr), true); | 3239 | return NewSharedPtr<ValueExpression>(ExpressionValueType::DateTime, Nullable<datetime_t>(nullptr), true); |
| 3240 | default: | 3240 | default: |
| 3241 | return NewSharedPtr<ValueExpression>(ExpressionValueType::Undefined, nullptr); | 3241 | return NewSharedPtr<ValueExpression>(ExpressionValueType::Undefined, nullptr); |
| 3242 | } | 3242 | } |
| @@ -127,8 +127,8 @@ namespace FilterExpressions · 126 unmodified lines | |||
| 127 | GSF::Guid ValueAsGuid() const; | 127 | GSF::Guid ValueAsGuid() const; |
| 128 | Nullable<GSF::Guid> ValueAsNullableGuid() const; | 128 | Nullable<GSF::Guid> ValueAsNullableGuid() const; |
| 129 | 129 | ||
| 130 | GSF::DateTime ValueAsDateTime() const; | 130 | GSF::datetime_t ValueAsDateTime() const; |
| 131 | Nullable<GSF::DateTime> ValueAsNullableDateTime() const; | 131 | Nullable<GSF::datetime_t> ValueAsNullableDateTime() const; |
| 132 | }; | 132 | }; |
| 133 | 133 | ||
| 134 | typedef GSF::SharedPtr<ValueExpression> ValueExpressionPtr; | 134 | typedef GSF::SharedPtr<ValueExpression> ValueExpressionPtr; |
| @@ -59,7 +59,7 @@ static GSF::Guid ParseGuidLiteral(string guidLiteral) · 58 unmodified lines | |||
| 59 | return ParseGuid(guidLiteral.c_str()); | 59 | return ParseGuid(guidLiteral.c_str()); |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | static DateTime ParseDateTimeLiteral(string time) | 62 | static datetime_t ParseDateTimeLiteral(string time) |
| 63 | { | 63 | { |
| 64 | // Remove any surrounding '#' symbols from date/time, ANTLR grammar already | 64 | // Remove any surrounding '#' symbols from date/time, ANTLR grammar already |
| 65 | // ensures date/time starting with '#' symbol will also end with one | 65 | // ensures date/time starting with '#' symbol will also end with one |
| @@ -33,21 +33,23 @@ using namespace GSF::TimeSeries::Transport; · 32 unmodified lines | |||
| 33 | using namespace GSF::FilterExpressions; | 33 | using namespace GSF::FilterExpressions; |
| 34 | 34 | ||
| 35 | DataPublisherPtr Publisher; | 35 | DataPublisherPtr Publisher; |
| 36 | TimerPtr PublishTimer; | 36 | TimerPtr PublishTimer, CompletionTimer; |
| 37 | vector<DeviceMetadataPtr> DevicesToPublish; | 37 | vector<DeviceMetadataPtr> DevicesToPublish; |
| 38 | vector<MeasurementMetadataPtr> MeasurementsToPublish; | 38 | vector<MeasurementMetadataPtr> MeasurementsToPublish; |
| 39 | vector<PhasorMetadataPtr> PhasorsToPublish; | 39 | vector<PhasorMetadataPtr> PhasorsToPublish; |
| 40 | 40 | ||
| 41 | bool RunPublisher(uint16_t port); | 41 | bool RunPublisher(uint16_t port); |
| 42 | void DisplayClientConnected(DataPublisher* source, const Guid& subscriberID, const string& connectionID); | 42 | void DisplayClientConnected(DataPublisher* source, const SubscriberConnectionPtr& connection); |
| 43 | void DisplayClientDisconnected(DataPublisher* source, const Guid& subscriberID, const string& connectionID); | 43 | void DisplayClientDisconnected(DataPublisher* source, const SubscriberConnectionPtr& connection); |
| 44 | void DisplayStatusMessage(DataPublisher* source, const string& message); | 44 | void DisplayStatusMessage(DataPublisher* source, const string& message); |
| 45 | void DisplayErrorMessage(DataPublisher* source, const string& message); | 45 | void DisplayErrorMessage(DataPublisher* source, const string& message); |
| 46 | void HandleTemporalSubscriptionRequested(DataPublisher* source, const SubscriberConnectionPtr& connection); | ||
| 47 | void HandleTemporalProcessingIntervalChangeRequested(DataPublisher* source, const SubscriberConnectionPtr& connection); | ||
| 46 | 48 | ||
| 47 | void LoadMetadataToPublish(vector<DeviceMetadataPtr>& deviceMetadata, vector<MeasurementMetadataPtr>& measurementMetadata, vector<PhasorMetadataPtr>& phasorMetadata) | 49 | void LoadMetadataToPublish(vector<DeviceMetadataPtr>& deviceMetadata, vector<MeasurementMetadataPtr>& measurementMetadata, vector<PhasorMetadataPtr>& phasorMetadata) |
| 48 | { | 50 | { |
| 49 | DeviceMetadataPtr device1Metadata = NewSharedPtr<DeviceMetadata>(); | 51 | DeviceMetadataPtr device1Metadata = NewSharedPtr<DeviceMetadata>(); |
| 50 | const DateTime timestamp = UtcNow(); | 52 | const datetime_t timestamp = UtcNow(); |
| 51 | 53 | ||
| 52 | // Add a device | 54 | // Add a device |
| 53 | device1Metadata->Name = "Test PMU"; | 55 | device1Metadata->Name = "Test PMU"; |
| @@ -210,6 +212,11 @@ bool RunPublisher(uint16_t port) · 156 unmodified lines | |||
| 210 | Publisher->RegisterClientDisconnectedCallback(&DisplayClientDisconnected); | 212 | Publisher->RegisterClientDisconnectedCallback(&DisplayClientDisconnected); |
| 211 | Publisher->RegisterStatusMessageCallback(&DisplayStatusMessage); | 213 | Publisher->RegisterStatusMessageCallback(&DisplayStatusMessage); |
| 212 | Publisher->RegisterErrorMessageCallback(&DisplayErrorMessage); | 214 | Publisher->RegisterErrorMessageCallback(&DisplayErrorMessage); |
| 215 | Publisher->RegisterTemporalSubscriptionRequestedCallback(&HandleTemporalSubscriptionRequested); | ||
| 216 | Publisher->RegisterTemporalProcessingIntervalChangeRequestedCallback(&HandleTemporalProcessingIntervalChangeRequested); | ||
| 217 | |||
| 218 | // Enable temporal subscription support | ||
| 219 | Publisher->SetSupportsTemporalSubscriptions(true); | ||
| 213 | 220 | ||
| 214 | // Load metadata to be used for publication | 221 | // Load metadata to be used for publication |
| 215 | LoadMetadataToPublish(DevicesToPublish, MeasurementsToPublish, PhasorsToPublish); | 222 | LoadMetadataToPublish(DevicesToPublish, MeasurementsToPublish, PhasorsToPublish); |
| @@ -285,18 +292,18 @@ bool RunPublisher(uint16_t port) · 69 unmodified lines | |||
| 285 | return running; | 292 | return running; |
| 286 | } | 293 | } |
| 287 | 294 | ||
| 288 | void DisplayClientConnected(DataPublisher* source, const Guid& subscriberID, const string& connectionID) | 295 | void DisplayClientConnected(DataPublisher* source, const SubscriberConnectionPtr& connection) |
| 289 | { | 296 | { |
| 290 | cout << ">> New Client Connected:" << endl; | 297 | cout << ">> New Client Connected:" << endl; |
| 291 | cout << " Subscriber ID: " << ToString(subscriberID) << endl; | 298 | cout << " Subscriber ID: " << ToString(connection->GetSubscriberID()) << endl; |
| 292 | cout << " Connection ID: " << connectionID << endl << endl; | 299 | cout << " Connection ID: " << ToString(connection->GetConnectionID()) << endl << endl; |
| 293 | } | 300 | } |
| 294 | 301 | ||
| 295 | void DisplayClientDisconnected(DataPublisher* source, const Guid& subscriberID, const string& connectionID) | 302 | void DisplayClientDisconnected(DataPublisher* source, const SubscriberConnectionPtr& connection) |
| 296 | { | 303 | { |
| 297 | cout << ">> Client Disconnected:" << endl; | 304 | cout << ">> Client Disconnected:" << endl; |
| 298 | cout << " Subscriber ID: " << ToString(subscriberID) << endl; | 305 | cout << " Subscriber ID: " << ToString(connection->GetSubscriberID()) << endl; |
| 299 | cout << " Connection ID: " << connectionID << endl << endl; | 306 | cout << " Connection ID: " << ToString(connection->GetConnectionID()) << endl << endl; |
| 300 | } | 307 | } |
| 301 | 308 | ||
| 302 | // Callback which is called to display status messages from the subscriber. | 309 | // Callback which is called to display status messages from the subscriber. |
| @@ -309,4 +316,21 @@ void DisplayStatusMessage(DataPublisher* source, const string& message) · 6 unmodified lines | |||
| 309 | void DisplayErrorMessage(DataPublisher* source, const string& message) | 316 | void DisplayErrorMessage(DataPublisher* source, const string& message) |
| 310 | { | 317 | { |
| 311 | cerr << message << endl << endl; | 318 | cerr << message << endl << endl; |
| 312 | } | 319 | } |
| 320 | |||
| 321 | void HandleTemporalSubscriptionRequested(DataPublisher* source, const SubscriberConnectionPtr& connection) | ||
| 322 | { | ||
| 323 | cout << "Client \"" << connection->GetConnectionID() << "\" with subscriber ID " << ToString(connection->GetSubscriberID()) << " has requested a temporal subscription starting at " << ToString(connection->GetStartTimeConstraint()) << endl << endl; | ||
| 324 | |||
| 325 | CompletionTimer = NewSharedPtr<Timer>(1000, [connection](Timer*, void*) | ||
| 326 | { | ||
| 327 | connection->CompleteTemporalSubscription(); | ||
| 328 | }); | ||
| 329 | |||
| 330 | CompletionTimer->Start(); | ||
| 331 | } | ||
| 332 | |||
| 333 | void HandleTemporalProcessingIntervalChangeRequested(DataPublisher* source, const SubscriberConnectionPtr& connection) | ||
| 334 | { | ||
| 335 | cout << "Client \"" << connection->GetConnectionID() << "\" with subscriber ID " << ToString(connection->GetSubscriberID()) << " has requested to change its temporal processing interval to " << ToString(connection->GetProcessingInterval()) << "ms" << endl << endl; | ||
| 336 | } | ||
| @@ -1177,42 +1177,42 @@ int main(int argc, char* argv[]) · 1176 unmodified lines | |||
| 1177 | valueExpression = FilterExpressionParser::Evaluate(dataRow, "DateAdd('2019-02-04', 2, 'Month')"); | 1177 | valueExpression = FilterExpressionParser::Evaluate(dataRow, "DateAdd('2019-02-04', 2, 'Month')"); |
| 1178 | 1178 | ||
| 1179 | assert(valueExpression->ValueType == ExpressionValueType::DateTime); | 1179 | assert(valueExpression->ValueType == ExpressionValueType::DateTime); |
| 1180 | assert(valueExpression->ValueAsDateTime() == DateTime(date(2019, 4, 4))); | 1180 | assert(valueExpression->ValueAsDateTime() == datetime_t(date(2019, 4, 4))); |
| 1181 | cout << "Test " << ++test << " succeeded..." << endl; | 1181 | cout << "Test " << ++test << " succeeded..." << endl; |
| 1182 | 1182 | ||
| 1183 | // Test 119 | 1183 | // Test 119 |
| 1184 | valueExpression = FilterExpressionParser::Evaluate(dataRow, "DateAdd(#1/31/2019#, 1, 'Day')"); | 1184 | valueExpression = FilterExpressionParser::Evaluate(dataRow, "DateAdd(#1/31/2019#, 1, 'Day')"); |
| 1185 | 1185 | ||
| 1186 | assert(valueExpression->ValueType == ExpressionValueType::DateTime); | 1186 | assert(valueExpression->ValueType == ExpressionValueType::DateTime); |
| 1187 | assert(valueExpression->ValueAsDateTime() == DateTime(date(2019, 2, 1))); | 1187 | assert(valueExpression->ValueAsDateTime() == datetime_t(date(2019, 2, 1))); |
| 1188 | cout << "Test " << ++test << " succeeded..." << endl; | 1188 | cout << "Test " << ++test << " succeeded..." << endl; |
| 1189 | 1189 | ||
| 1190 | // Test 120 | 1190 | // Test 120 |
| 1191 | valueExpression = FilterExpressionParser::Evaluate(dataRow, "DateAdd(#2019-01-31#, 2, 'Week')"); | 1191 | valueExpression = FilterExpressionParser::Evaluate(dataRow, "DateAdd(#2019-01-31#, 2, 'Week')"); |
| 1192 | 1192 | ||
| 1193 | assert(valueExpression->ValueType == ExpressionValueType::DateTime); | 1193 | assert(valueExpression->ValueType == ExpressionValueType::DateTime); |
| 1194 | assert(valueExpression->ValueAsDateTime() == DateTime(date(2019, 2, 14))); | 1194 | assert(valueExpression->ValueAsDateTime() == datetime_t(date(2019, 2, 14))); |
| 1195 | cout << "Test " << ++test << " succeeded..." << endl; | 1195 | cout << "Test " << ++test << " succeeded..." << endl; |
| 1196 | 1196 | ||
| 1197 | // Test 121 | 1197 | // Test 121 |
| 1198 | valueExpression = FilterExpressionParser::Evaluate(dataRow, "DateAdd(#2019-01-31#, 25, 'Hour')"); | 1198 | valueExpression = FilterExpressionParser::Evaluate(dataRow, "DateAdd(#2019-01-31#, 25, 'Hour')"); |
| 1199 | 1199 | ||
| 1200 | assert(valueExpression->ValueType == ExpressionValueType::DateTime); | 1200 | assert(valueExpression->ValueType == ExpressionValueType::DateTime); |
| 1201 | assert(valueExpression->ValueAsDateTime() == DateTime(date(2019, 2, 1), time_duration(1, 0, 0))); | 1201 | assert(valueExpression->ValueAsDateTime() == datetime_t(date(2019, 2, 1), time_duration(1, 0, 0))); |
| 1202 | cout << "Test " << ++test << " succeeded..." << endl; | 1202 | cout << "Test " << ++test << " succeeded..." << endl; |
| 1203 | 1203 | ||
| 1204 | // Test 122 | 1204 | // Test 122 |
| 1205 | valueExpression = FilterExpressionParser::Evaluate(dataRow, "DateAdd(#2018-12-31 23:58#, 3, 'Minute')"); | 1205 | valueExpression = FilterExpressionParser::Evaluate(dataRow, "DateAdd(#2018-12-31 23:58#, 3, 'Minute')"); |
| 1206 | 1206 | ||
| 1207 | assert(valueExpression->ValueType == ExpressionValueType::DateTime); | 1207 | assert(valueExpression->ValueType == ExpressionValueType::DateTime); |
| 1208 | assert(valueExpression->ValueAsDateTime() == DateTime(date(2019, 1, 1), time_duration(0, 1, 0))); | 1208 | assert(valueExpression->ValueAsDateTime() == datetime_t(date(2019, 1, 1), time_duration(0, 1, 0))); |
| 1209 | cout << "Test " << ++test << " succeeded..." << endl; | 1209 | cout << "Test " << ++test << " succeeded..." << endl; |
| 1210 | 1210 | ||
| 1211 | // Test 123 | 1211 | // Test 123 |
| 1212 | valueExpression = FilterExpressionParser::Evaluate(dataRow, "DateAdd('2019-01-1 00:59', 61, 'Second')"); | 1212 | valueExpression = FilterExpressionParser::Evaluate(dataRow, "DateAdd('2019-01-1 00:59', 61, 'Second')"); |
| 1213 | 1213 | ||
| 1214 | assert(valueExpression->ValueType == ExpressionValueType::DateTime); | 1214 | assert(valueExpression->ValueType == ExpressionValueType::DateTime); |
| 1215 | assert(valueExpression->ValueAsDateTime() == DateTime(date(2019, 1, 1), time_duration(1, 0, 1))); | 1215 | assert(valueExpression->ValueAsDateTime() == datetime_t(date(2019, 1, 1), time_duration(1, 0, 1))); |
| 1216 | cout << "Test " << ++test << " succeeded..." << endl; | 1216 | cout << "Test " << ++test << " succeeded..." << endl; |
| 1217 | 1217 | ||
| 1218 | const float64_t baseFraction = pow(10.0, time_duration::num_fractional_digits()); | 1218 | const float64_t baseFraction = pow(10.0, time_duration::num_fractional_digits()); |
| @@ -1222,14 +1222,14 @@ int main(int argc, char* argv[]) · 3 unmodified lines | |||
| 1222 | valueExpression = FilterExpressionParser::Evaluate(dataRow, "DateAdd('2019-01-1 00:00:59.999', 2, 'Millisecond')"); | 1222 | valueExpression = FilterExpressionParser::Evaluate(dataRow, "DateAdd('2019-01-1 00:00:59.999', 2, 'Millisecond')"); |
| 1223 | 1223 | ||
| 1224 | assert(valueExpression->ValueType == ExpressionValueType::DateTime); | 1224 | assert(valueExpression->ValueType == ExpressionValueType::DateTime); |
| 1225 | assert(valueExpression->ValueAsDateTime() == DateTime(date(2019, 1, 1), time_duration(0, 1, 0, fracSecond(1)))); | 1225 | assert(valueExpression->ValueAsDateTime() == datetime_t(date(2019, 1, 1), time_duration(0, 1, 0, fracSecond(1)))); |
| 1226 | cout << "Test " << ++test << " succeeded..." << endl; | 1226 | cout << "Test " << ++test << " succeeded..." << endl; |
| 1227 | 1227 | ||
| 1228 | // Test 125 | 1228 | // Test 125 |
| 1229 | valueExpression = FilterExpressionParser::Evaluate(dataRow, "DateAdd(#1/1/2019 0:0:1.029#, -FramesPerSecond, 'Millisecond')"); | 1229 | valueExpression = FilterExpressionParser::Evaluate(dataRow, "DateAdd(#1/1/2019 0:0:1.029#, -FramesPerSecond, 'Millisecond')"); |
| 1230 | 1230 | ||
| 1231 | assert(valueExpression->ValueType == ExpressionValueType::DateTime); | 1231 | assert(valueExpression->ValueType == ExpressionValueType::DateTime); |
| 1232 | assert(valueExpression->ValueAsDateTime() == DateTime(date(2019, 1, 1), time_duration(0, 0, 0, fracSecond(999)))); | 1232 | assert(valueExpression->ValueAsDateTime() == datetime_t(date(2019, 1, 1), time_duration(0, 0, 0, fracSecond(999)))); |
| 1233 | cout << "Test " << ++test << " succeeded..." << endl; | 1233 | cout << "Test " << ++test << " succeeded..." << endl; |
| 1234 | 1234 | ||
| 1235 | // Test 126 | 1235 | // Test 126 |
| @@ -1429,7 +1429,7 @@ int main(int argc, char* argv[]) · 193 unmodified lines | |||
| 1429 | cout << "Test " << ++test << " succeeded..." << endl; | 1429 | cout << "Test " << ++test << " succeeded..." << endl; |
| 1430 | 1430 | ||
| 1431 | // Test 150 | 1431 | // Test 150 |
| 1432 | const DateTime time = ParseTimestamp("2019-02-10T15:55:25.090477Z"); | 1432 | const datetime_t time = ParseTimestamp("2019-02-10T15:55:25.090477Z"); |
| 1433 | const int64_t ticks = ToTicks(time); | 1433 | const int64_t ticks = ToTicks(time); |
| 1434 | 1434 | ||
| 1435 | assert(ticks == 636854109250904770L); | 1435 | assert(ticks == 636854109250904770L); |
| @@ -36,8 +36,8 @@ TimerPtr PublishTimer; · 35 unmodified lines | |||
| 36 | vector<MeasurementMetadataPtr> MeasurementsToPublish; | 36 | vector<MeasurementMetadataPtr> MeasurementsToPublish; |
| 37 | 37 | ||
| 38 | bool RunPublisher(uint16_t port); | 38 | bool RunPublisher(uint16_t port); |
| 39 | void DisplayClientConnected(DataPublisher* source, const Guid& subscriberID, const string& connectionID); | 39 | void DisplayClientConnected(DataPublisher* source, const SubscriberConnectionPtr& connection); |
| 40 | void DisplayClientDisconnected(DataPublisher* source, const Guid& subscriberID, const string& connectionID); | 40 | void DisplayClientDisconnected(DataPublisher* source, const SubscriberConnectionPtr& connection); |
| 41 | void DisplayStatusMessage(DataPublisher* source, const string& message); | 41 | void DisplayStatusMessage(DataPublisher* source, const string& message); |
| 42 | void DisplayErrorMessage(DataPublisher* source, const string& message); | 42 | void DisplayErrorMessage(DataPublisher* source, const string& message); |
| 43 | 43 | ||
| @@ -168,18 +168,18 @@ bool RunPublisher(uint16_t port) · 124 unmodified lines | |||
| 168 | return running; | 168 | return running; |
| 169 | } | 169 | } |
| 170 | 170 | ||
| 171 | void DisplayClientConnected(DataPublisher* source, const Guid& subscriberID, const string& connectionID) | 171 | void DisplayClientConnected(DataPublisher* source, const SubscriberConnectionPtr& connection) |
| 172 | { | 172 | { |
| 173 | cout << ">> New Client Connected:" << endl; | 173 | cout << ">> New Client Connected:" << endl; |
| 174 | cout << " Subscriber ID: " << ToString(subscriberID) << endl; | 174 | cout << " Subscriber ID: " << ToString(connection->GetSubscriberID()) << endl; |
| 175 | cout << " Connection ID: " << connectionID << endl << endl; | 175 | cout << " Connection ID: " << ToString(connection->GetConnectionID()) << endl << endl; |
| 176 | } | 176 | } |
| 177 | 177 | ||
| 178 | void DisplayClientDisconnected(DataPublisher* source, const Guid& subscriberID, const string& connectionID) | 178 | void DisplayClientDisconnected(DataPublisher* source, const SubscriberConnectionPtr& connection) |
| 179 | { | 179 | { |
| 180 | cout << ">> Client Disconnected:" << endl; | 180 | cout << ">> Client Disconnected:" << endl; |
| 181 | cout << " Subscriber ID: " << ToString(subscriberID) << endl; | 181 | cout << " Subscriber ID: " << ToString(connection->GetSubscriberID()) << endl; |
| 182 | cout << " Connection ID: " << connectionID << endl << endl; | 182 | cout << " Connection ID: " << ToString(connection->GetConnectionID()) << endl << endl; |
| 183 | } | 183 | } |
| 184 | 184 | ||
| 185 | // Callback which is called to display status messages from the subscriber. | 185 | // Callback which is called to display status messages from the subscriber. |
| @@ -192,4 +192,4 @@ void DisplayStatusMessage(DataPublisher* source, const string& message) · 6 unmodified lines | |||
| 192 | void DisplayErrorMessage(DataPublisher* source, const string& message) | 192 | void DisplayErrorMessage(DataPublisher* source, const string& message) |
| 193 | { | 193 | { |
| 194 | cerr << message << endl << endl; | 194 | cerr << message << endl << endl; |
| 195 | } | 195 | } |
| @@ -104,7 +104,7 @@ void SubscriberHandler::DataStartTime(time_t unixSOC, uint16_t milliseconds) · 103 unmodified lines | |||
| 104 | // TODO: This reports timestamp, time_t format, of very first received measurement (if useful) | 104 | // TODO: This reports timestamp, time_t format, of very first received measurement (if useful) |
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | void SubscriberHandler::DataStartTime(DateTime startTime) | 107 | void SubscriberHandler::DataStartTime(datetime_t startTime) |
| 108 | { | 108 | { |
| 109 | // TODO: This reports timestamp, ptime format, of very first received measurement (if useful) | 109 | // TODO: This reports timestamp, ptime format, of very first received measurement (if useful) |
| 110 | } | 110 | } |
| @@ -41,7 +41,7 @@ protected: · 40 unmodified lines | |||
| 41 | void StatusMessage(const std::string& message) override; | 41 | void StatusMessage(const std::string& message) override; |
| 42 | void ErrorMessage(const std::string& message) override; | 42 | void ErrorMessage(const std::string& message) override; |
| 43 | void DataStartTime(time_t unixSOC, uint16_t milliseconds) override; | 43 | void DataStartTime(time_t unixSOC, uint16_t milliseconds) override; |
| 44 | void DataStartTime(GSF::DateTime startTime) override; | 44 | void DataStartTime(GSF::datetime_t startTime) override; |
| 45 | void ReceivedMetadata(const std::vector<uint8_t>& payload) override; | 45 | void ReceivedMetadata(const std::vector<uint8_t>& payload) override; |
| 46 | void ReceivedNewMeasurements(const std::vector<GSF::TimeSeries::MeasurementPtr>& measurements) override; | 46 | void ReceivedNewMeasurements(const std::vector<GSF::TimeSeries::MeasurementPtr>& measurements) override; |
| 47 | void ParsedMetadata() override; | 47 | void ParsedMetadata() override; |
| @@ -36,24 +36,13 @@ using namespace GSF::FilterExpressions; · 35 unmodified lines | |||
| 36 | using namespace GSF::TimeSeries; | 36 | using namespace GSF::TimeSeries; |
| 37 | using namespace GSF::TimeSeries::Transport; | 37 | using namespace GSF::TimeSeries::Transport; |
| 38 | 38 | ||
| 39 | struct SubscriberConnectionInfo | ||
| 40 | { | ||
| 41 | const GSF::Guid SubscriberID; | ||
| 42 | const string ConnectionID; | ||
| 43 | |||
| 44 | SubscriberConnectionInfo(const GSF::Guid& subscriberID, string connectionID) : | ||
| 45 | SubscriberID(subscriberID), | ||
| 46 | ConnectionID(std::move(connectionID)) | ||
| 47 | { | ||
| 48 | } | ||
| 49 | }; | ||
| 50 | |||
| 51 | DataPublisher::DataPublisher(const TcpEndPoint& endpoint) : | 39 | DataPublisher::DataPublisher(const TcpEndPoint& endpoint) : |
| 52 | m_nodeID(NewGuid()), | 40 | m_nodeID(NewGuid()), |
| 53 | m_securityMode(SecurityMode::None), | 41 | m_securityMode(SecurityMode::None), |
| 54 | m_allowMetadataRefresh(true), | 42 | m_allowMetadataRefresh(true), |
| 55 | m_allowNaNValueFilter(true), | 43 | m_allowNaNValueFilter(true), |
| 56 | m_forceNaNValueFilter(false), | 44 | m_forceNaNValueFilter(false), |
| 45 | m_supportsTemporalSubscriptions(false), | ||
| 57 | m_cipherKeyRotationPeriod(60000), | 46 | m_cipherKeyRotationPeriod(60000), |
| 58 | m_userData(nullptr), | 47 | m_userData(nullptr), |
| 59 | m_disposing(false), | 48 | m_disposing(false), |
| @@ -116,19 +105,21 @@ void DataPublisher::AcceptConnection(const SubscriberConnectionPtr& connection, · 56 unmodified lines | |||
| 116 | m_subscriberConnectionsLock.unlock(); | 105 | m_subscriberConnectionsLock.unlock(); |
| 117 | 106 | ||
| 118 | connection->Start(); | 107 | connection->Start(); |
| 119 | DispatchClientConnected(connection->GetSubscriberID(), connection->GetConnectionID()); | 108 | DispatchClientConnected(connection); |
| 120 | } | 109 | } |
| 121 | 110 | ||
| 122 | StartAccept(); | 111 | StartAccept(); |
| 123 | } | 112 | } |
| 124 | 113 | ||
| 114 | void DataPublisher::ConnectionTerminated(const SubscriberConnectionPtr& connection) | ||
| 115 | { | ||
| 116 | DispatchClientDisconnected(connection); | ||
| 117 | } | ||
| 118 | |||
| 125 | void DataPublisher::RemoveConnection(const SubscriberConnectionPtr& connection) | 119 | void DataPublisher::RemoveConnection(const SubscriberConnectionPtr& connection) |
| 126 | { | 120 | { |
| 127 | m_subscriberConnectionsLock.lock(); | 121 | m_subscriberConnectionsLock.lock(); |
| 128 | 122 | m_subscriberConnections.erase(connection); | |
| 129 | if (m_subscriberConnections.erase(connection)) | ||
| 130 | DispatchClientDisconnected(connection->GetSubscriberID(), connection->GetConnectionID()); | ||
| 131 | |||
| 132 | m_subscriberConnectionsLock.unlock(); | 123 | m_subscriberConnectionsLock.unlock(); |
| 133 | } | 124 | } |
| 134 | 125 | ||
| @@ -169,16 +160,28 @@ void DataPublisher::DispatchErrorMessage(const string& message) · 34 unmodified lines | |||
| 169 | Dispatch(&ErrorMessageDispatcher, reinterpret_cast<const uint8_t*>(message.c_str()), 0, messageSize); | 160 | Dispatch(&ErrorMessageDispatcher, reinterpret_cast<const uint8_t*>(message.c_str()), 0, messageSize); |
| 170 | } | 161 | } |
| 171 | 162 | ||
| 172 | void DataPublisher::DispatchClientConnected(const GSF::Guid& subscriberID, const string& connectionID) | 163 | void DataPublisher::DispatchClientConnected(const SubscriberConnectionPtr& connection) |
| 164 | { | ||
| 165 | SubscriberConnection* connectionReference = &*connection; | ||
| 166 | Dispatch(&ClientConnectedDispatcher, reinterpret_cast<uint8_t*>(&connectionReference), 0, sizeof(SubscriberConnection**)); | ||
| 167 | } | ||
| 168 | |||
| 169 | void DataPublisher::DispatchClientDisconnected(const SubscriberConnectionPtr& connection) | ||
| 170 | { | ||
| 171 | SubscriberConnection* connectionReference = &*connection; | ||
| 172 | Dispatch(&ClientDisconnectedDispatcher, reinterpret_cast<uint8_t*>(&connectionReference), 0, sizeof(SubscriberConnection**)); | ||
| 173 | } | ||
| 174 | |||
| 175 | void DataPublisher::DispatchTemporalSubscriptionRequested(const SubscriberConnectionPtr& connection) | ||
| 173 | { | 176 | { |
| 174 | SubscriberConnectionInfo* data = new SubscriberConnectionInfo(subscriberID, connectionID); | 177 | SubscriberConnection* connectionReference = &*connection; |
| 175 | Dispatch(&ClientConnectedDispatcher, reinterpret_cast<uint8_t*>(&data), 0, sizeof(SubscriberConnectionInfo**)); | 178 | Dispatch(&TemporalSubscriptionRequestedDispatcher, reinterpret_cast<uint8_t*>(&connectionReference), 0, sizeof(SubscriberConnection**)); |
| 176 | } | 179 | } |
| 177 | 180 | ||
| 178 | void DataPublisher::DispatchClientDisconnected(const GSF::Guid& subscriberID, const std::string& connectionID) | 181 | void DataPublisher::DispatchTemporalProcessingIntervalChangeRequested(const SubscriberConnectionPtr& connection) |
| 179 | { | 182 | { |
| 180 | SubscriberConnectionInfo* data = new SubscriberConnectionInfo(subscriberID, connectionID); | 183 | SubscriberConnection* connectionReference = &*connection; |
| 181 | Dispatch(&ClientDisconnectedDispatcher, reinterpret_cast<uint8_t*>(&data), 0, sizeof(SubscriberConnectionInfo**)); | 184 | Dispatch(&TemporalProcessingIntervalChangeRequestedDispatcher, reinterpret_cast<uint8_t*>(&connectionReference), 0, sizeof(SubscriberConnection**)); |
| 182 | } | 185 | } |
| 183 | 186 | ||
| 184 | // Dispatcher function for status messages. Decodes the message and provides it to the user via the status message callback. | 187 | // Dispatcher function for status messages. Decodes the message and provides it to the user via the status message callback. |
| @@ -207,32 +210,56 @@ void DataPublisher::ErrorMessageDispatcher(DataPublisher* source, const vector<u · 22 unmodified lines | |||
| 207 | 210 | ||
| 208 | void DataPublisher::ClientConnectedDispatcher(DataPublisher* source, const vector<uint8_t>& buffer) | 211 | void DataPublisher::ClientConnectedDispatcher(DataPublisher* source, const vector<uint8_t>& buffer) |
| 209 | { | 212 | { |
| 210 | SubscriberConnectionInfo* data = *reinterpret_cast<SubscriberConnectionInfo**>(const_cast<uint8_t*>(&buffer[0])); | 213 | SubscriberConnection* connection = *reinterpret_cast<SubscriberConnection**>(const_cast<uint8_t*>(&buffer[0])); |
| 211 | 214 | ||
| 212 | if (source != nullptr) | 215 | if (source != nullptr) |
| 213 | { | 216 | { |
| 214 | const SubscriberConnectionCallback clientConnectedCallback = source->m_clientConnectedCallback; | 217 | const SubscriberConnectionCallback clientConnectedCallback = source->m_clientConnectedCallback; |
| 215 | 218 | ||
| 216 | if (clientConnectedCallback != nullptr) | 219 | if (clientConnectedCallback != nullptr) |
| 217 | clientConnectedCallback(source, data->SubscriberID, data->ConnectionID); | 220 | clientConnectedCallback(source, connection->GetReference()); |
| 218 | } | 221 | } |
| 219 | |||
| 220 | delete data; | ||
| 221 | } | 222 | } |
| 222 | 223 | ||
| 223 | void DataPublisher::ClientDisconnectedDispatcher(DataPublisher* source, const std::vector<uint8_t>& buffer) | 224 | void DataPublisher::ClientDisconnectedDispatcher(DataPublisher* source, const std::vector<uint8_t>& buffer) |
| 224 | { | 225 | { |
| 225 | SubscriberConnectionInfo* data = *reinterpret_cast<SubscriberConnectionInfo**>(const_cast<uint8_t*>(&buffer[0])); | 226 | SubscriberConnection* connection = *reinterpret_cast<SubscriberConnection**>(const_cast<uint8_t*>(&buffer[0])); |
| 226 | 227 | ||
| 227 | if (source != nullptr) | 228 | if (source != nullptr) |
| 228 | { | 229 | { |
| 229 | const SubscriberConnectionCallback clientDisconnectedCallback = source->m_clientDisconnectedCallback; | 230 | const SubscriberConnectionCallback clientDisconnectedCallback = source->m_clientDisconnectedCallback; |
| 230 | 231 | ||
| 231 | if (clientDisconnectedCallback != nullptr) | 232 | if (clientDisconnectedCallback != nullptr) |
| 232 | clientDisconnectedCallback(source, data->SubscriberID, data->ConnectionID); | 233 | clientDisconnectedCallback(source, connection->GetReference()); |
| 234 | |||
| 235 | source->RemoveConnection(connection->GetReference()); | ||
| 236 | } | ||
| 237 | } | ||
| 238 | |||
| 239 | void DataPublisher::TemporalSubscriptionRequestedDispatcher(DataPublisher* source, const std::vector<uint8_t>& buffer) | ||
| 240 | { | ||
| 241 | SubscriberConnection* connection = *reinterpret_cast<SubscriberConnection**>(const_cast<uint8_t*>(&buffer[0])); | ||
| 242 | |||
| 243 | if (source != nullptr) | ||
| 244 | { | ||
| 245 | const SubscriberConnectionCallback temporalSubscriptionRequestedCallback = source->m_temporalSubscriptionRequestedCallback; | ||
| 246 | |||
| 247 | if (temporalSubscriptionRequestedCallback != nullptr) | ||
| 248 | temporalSubscriptionRequestedCallback(source, connection->GetReference()); | ||
| 233 | } | 249 | } |
| 250 | } | ||
| 251 | |||
| 252 | void DataPublisher::TemporalProcessingIntervalChangeRequestedDispatcher(DataPublisher* source, const std::vector<uint8_t>& buffer) | ||
| 253 | { | ||
| 254 | SubscriberConnection* connection = *reinterpret_cast<SubscriberConnection**>(const_cast<uint8_t*>(&buffer[0])); | ||
| 255 | |||
| 256 | if (source != nullptr) | ||
| 257 | { | ||
| 258 | const SubscriberConnectionCallback temporalProcessingIntervalChangeRequestedCallback = source->m_temporalProcessingIntervalChangeRequestedCallback; | ||
| 234 | 259 | ||
| 235 | delete data; | 260 | if (temporalProcessingIntervalChangeRequestedCallback != nullptr) |
| 261 | temporalProcessingIntervalChangeRequestedCallback(source, connection->GetReference()); | ||
| 262 | } | ||
| 236 | } | 263 | } |
| 237 | 264 | ||
| 238 | int32_t DataPublisher::GetColumnIndex(const GSF::Data::DataTablePtr& table, const std::string& columnName) | 265 | int32_t DataPublisher::GetColumnIndex(const GSF::Data::DataTablePtr& table, const std::string& columnName) |
| @@ -683,14 +710,27 @@ vector<MeasurementMetadataPtr> DataPublisher::FilterMetadata(const string& filte · 444 unmodified lines | |||
| 683 | 710 | ||
| 684 | void DataPublisher::PublishMeasurements(const vector<Measurement>& measurements) | 711 | void DataPublisher::PublishMeasurements(const vector<Measurement>& measurements) |
| 685 | { | 712 | { |
| 686 | for (const auto& connection : m_subscriberConnections) | 713 | vector<MeasurementPtr> measurementPtrs; |
| 687 | connection->PublishMeasurements(measurements); | 714 | |
| 715 | measurementPtrs.reserve(measurements.size()); | ||
| 716 | |||
| 717 | for (const auto& measurement : measurements) | ||
| 718 | measurementPtrs.push_back(ToPtr(measurement)); | ||
| 719 | |||
| 720 | PublishMeasurements(measurementPtrs); | ||
| 688 | } | 721 | } |
| 689 | 722 | ||
| 690 | void DataPublisher::PublishMeasurements(const vector<MeasurementPtr>& measurements) | 723 | void DataPublisher::PublishMeasurements(const vector<MeasurementPtr>& measurements) |
| 691 | { | 724 | { |
| 725 | m_subscriberConnectionsLock.lock(); | ||
| 726 | |||
| 692 | for (const auto& connection : m_subscriberConnections) | 727 | for (const auto& connection : m_subscriberConnections) |
| 693 | connection->PublishMeasurements(measurements); | 728 | { |
| 729 | if (connection->GetIsSubscribed() && !connection->GetIsTemporalSubscription()) | ||
| 730 | connection->PublishMeasurements(measurements); | ||
| 731 | } | ||
| 732 | |||
| 733 | m_subscriberConnectionsLock.unlock(); | ||
| 694 | } | 734 | } |
| 695 | 735 | ||
| 696 | const GSF::Guid& DataPublisher::GetNodeID() const | 736 | const GSF::Guid& DataPublisher::GetNodeID() const |
| @@ -743,6 +783,16 @@ void DataPublisher::SetNaNValueFilterForced(bool forced) · 46 unmodified lines | |||
| 743 | m_forceNaNValueFilter = forced; | 783 | m_forceNaNValueFilter = forced; |
| 744 | } | 784 | } |
| 745 | 785 | ||
| 786 | bool DataPublisher::GetSupportsTemporalSubscriptions() const | ||
| 787 | { | ||
| 788 | return m_supportsTemporalSubscriptions; | ||
| 789 | } | ||
| 790 | |||
| 791 | void DataPublisher::SetSupportsTemporalSubscriptions(bool value) | ||
| 792 | { | ||
| 793 | m_supportsTemporalSubscriptions = value; | ||
| 794 | } | ||
| 795 | |||
| 746 | uint32_t DataPublisher::GetCipherKeyRotationPeriod() const | 796 | uint32_t DataPublisher::GetCipherKeyRotationPeriod() const |
| 747 | { | 797 | { |
| 748 | return m_cipherKeyRotationPeriod; | 798 | return m_cipherKeyRotationPeriod; |
| @@ -823,4 +873,14 @@ void DataPublisher::RegisterClientConnectedCallback(const SubscriberConnectionCa · 74 unmodified lines | |||
| 823 | void DataPublisher::RegisterClientDisconnectedCallback(const SubscriberConnectionCallback& clientDisconnectedCallback) | 873 | void DataPublisher::RegisterClientDisconnectedCallback(const SubscriberConnectionCallback& clientDisconnectedCallback) |
| 824 | { | 874 | { |
| 825 | m_clientDisconnectedCallback = clientDisconnectedCallback; | 875 | m_clientDisconnectedCallback = clientDisconnectedCallback; |
| 826 | } | 876 | } |
| 877 | |||
| 878 | void DataPublisher::RegisterTemporalSubscriptionRequestedCallback(const SubscriberConnectionCallback& temporalSubscriptionRequestedCallback) | ||
| 879 | { | ||
| 880 | m_temporalSubscriptionRequestedCallback = temporalSubscriptionRequestedCallback; | ||
| 881 | } | ||
| 882 | |||
| 883 | void DataPublisher::RegisterTemporalProcessingIntervalChangeRequestedCallback(const SubscriberConnectionCallback& temporalProcessingIntervalChangeRequestedCallback) | ||
| 884 | { | ||
| 885 | m_temporalProcessingIntervalChangeRequestedCallback = temporalProcessingIntervalChangeRequestedCallback; | ||
| 886 | } | ||
| @@ -44,12 +44,13 @@ namespace Transport · 43 unmodified lines | |||
| 44 | { | 44 | { |
| 45 | class DataPublisher : public EnableSharedThisPtr<DataPublisher> // NOLINT | 45 | class DataPublisher : public EnableSharedThisPtr<DataPublisher> // NOLINT |
| 46 | { | 46 | { |
| 47 | private: | 47 | public: |
| 48 | // Function pointer types | 48 | // Function pointer types |
| 49 | typedef std::function<void(DataPublisher*, const std::vector<uint8_t>&)> DispatcherFunction; | 49 | typedef std::function<void(DataPublisher*, const std::vector<uint8_t>&)> DispatcherFunction; |
| 50 | typedef std::function<void(DataPublisher*, const std::string&)> MessageCallback; | 50 | typedef std::function<void(DataPublisher*, const std::string&)> MessageCallback; |
| 51 | typedef std::function<void(DataPublisher*, const GSF::Guid&, const std::string&)> SubscriberConnectionCallback; | 51 | typedef std::function<void(DataPublisher*, const SubscriberConnectionPtr&)> SubscriberConnectionCallback; |
| 52 | 52 | ||
| 53 | private: | ||
| 53 | // Structure used to dispatch | 54 | // Structure used to dispatch |
| 54 | // callbacks on the callback thread. | 55 | // callbacks on the callback thread. |
| 55 | struct CallbackDispatcher | 56 | struct CallbackDispatcher |
| @@ -70,6 +71,7 @@ namespace Transport · 14 unmodified lines | |||
| 70 | bool m_allowMetadataRefresh; | 71 | bool m_allowMetadataRefresh; |
| 71 | bool m_allowNaNValueFilter; | 72 | bool m_allowNaNValueFilter; |
| 72 | bool m_forceNaNValueFilter; | 73 | bool m_forceNaNValueFilter; |
| 74 | bool m_supportsTemporalSubscriptions; | ||
| 73 | uint32_t m_cipherKeyRotationPeriod; | 75 | uint32_t m_cipherKeyRotationPeriod; |
| 74 | void* m_userData; | 76 | void* m_userData; |
| 75 | bool m_disposing; | 77 | bool m_disposing; |
| @@ -93,6 +95,7 @@ namespace Transport · 17 unmodified lines | |||
| 93 | // Command channel handlers | 95 | // Command channel handlers |
| 94 | void StartAccept(); | 96 | void StartAccept(); |
| 95 | void AcceptConnection(const SubscriberConnectionPtr& connection, const ErrorCode& error); | 97 | void AcceptConnection(const SubscriberConnectionPtr& connection, const ErrorCode& error); |
| 98 | void ConnectionTerminated(const SubscriberConnectionPtr& connection); | ||
| 96 | void RemoveConnection(const SubscriberConnectionPtr& connection); | 99 | void RemoveConnection(const SubscriberConnectionPtr& connection); |
| 97 | 100 | ||
| 98 | // Callbacks | 101 | // Callbacks |
| @@ -100,19 +103,25 @@ namespace Transport · 1 unmodified line | |||
| 100 | MessageCallback m_errorMessageCallback; | 103 | MessageCallback m_errorMessageCallback; |
| 101 | SubscriberConnectionCallback m_clientConnectedCallback; | 104 | SubscriberConnectionCallback m_clientConnectedCallback; |
| 102 | SubscriberConnectionCallback m_clientDisconnectedCallback; | 105 | SubscriberConnectionCallback m_clientDisconnectedCallback; |
| 106 | SubscriberConnectionCallback m_temporalSubscriptionRequestedCallback; | ||
| 107 | SubscriberConnectionCallback m_temporalProcessingIntervalChangeRequestedCallback; | ||
| 103 | 108 | ||
| 104 | // Dispatchers | 109 | // Dispatchers |
| 105 | void Dispatch(const DispatcherFunction& function); | 110 | void Dispatch(const DispatcherFunction& function); |
| 106 | void Dispatch(const DispatcherFunction& function, const uint8_t* data, uint32_t offset, uint32_t length); | 111 | void Dispatch(const DispatcherFunction& function, const uint8_t* data, uint32_t offset, uint32_t length); |
| 107 | void DispatchStatusMessage(const std::string& message); | 112 | void DispatchStatusMessage(const std::string& message); |
| 108 | void DispatchErrorMessage(const std::string& message); | 113 | void DispatchErrorMessage(const std::string& message); |
| 109 | void DispatchClientConnected(const GSF::Guid& subscriberID, const std::string& connectionID); | 114 | void DispatchClientConnected(const SubscriberConnectionPtr& connection); |
| 110 | void DispatchClientDisconnected(const GSF::Guid& subscriberID, const std::string& connectionID); | 115 | void DispatchClientDisconnected(const SubscriberConnectionPtr& connection); |
| 116 | void DispatchTemporalSubscriptionRequested(const SubscriberConnectionPtr& connection); | ||
| 117 | void DispatchTemporalProcessingIntervalChangeRequested(const SubscriberConnectionPtr& connection); | ||
| 111 | 118 | ||
| 112 | static void StatusMessageDispatcher(DataPublisher* source, const std::vector<uint8_t>& buffer); | 119 | static void StatusMessageDispatcher(DataPublisher* source, const std::vector<uint8_t>& buffer); |
| 113 | static void ErrorMessageDispatcher(DataPublisher* source, const std::vector<uint8_t>& buffer); | 120 | static void ErrorMessageDispatcher(DataPublisher* source, const std::vector<uint8_t>& buffer); |
| 114 | static void ClientConnectedDispatcher(DataPublisher* source, const std::vector<uint8_t>& buffer); | 121 | static void ClientConnectedDispatcher(DataPublisher* source, const std::vector<uint8_t>& buffer); |
| 115 | static void ClientDisconnectedDispatcher(DataPublisher* source, const std::vector<uint8_t>& buffer); | 122 | static void ClientDisconnectedDispatcher(DataPublisher* source, const std::vector<uint8_t>& buffer); |
| 123 | static void TemporalSubscriptionRequestedDispatcher(DataPublisher* source, const std::vector<uint8_t>& buffer); | ||
| 124 | static void TemporalProcessingIntervalChangeRequestedDispatcher(DataPublisher* source, const std::vector<uint8_t>& buffer); | ||
| 116 | static int32_t GetColumnIndex(const GSF::Data::DataTablePtr& table, const std::string& columnName); | 125 | static int32_t GetColumnIndex(const GSF::Data::DataTablePtr& table, const std::string& columnName); |
| 117 | public: | 126 | public: |
| 118 | // Creates a new instance of the data publisher. | 127 | // Creates a new instance of the data publisher. |
| @@ -123,14 +132,22 @@ namespace Transport · 4 unmodified lines | |||
| 123 | // tied up by the publisher. | 132 | // tied up by the publisher. |
| 124 | ~DataPublisher(); | 133 | ~DataPublisher(); |
| 125 | 134 | ||
| 126 | // Define metadata from existing metadata tables | 135 | // Defines metadata from existing metadata records |
| 127 | void DefineMetadata(const std::vector<DeviceMetadataPtr>& deviceMetadata, const std::vector<MeasurementMetadataPtr>& measurementMetadata, const std::vector<PhasorMetadataPtr>& phasorMetadata, int32_t versionNumber = 0); | 136 | void DefineMetadata(const std::vector<DeviceMetadataPtr>& deviceMetadata, const std::vector<MeasurementMetadataPtr>& measurementMetadata, const std::vector<PhasorMetadataPtr>& phasorMetadata, int32_t versionNumber = 0); |
| 128 | 137 | ||
| 129 | // Define metadata from an existing dataset | 138 | // Defines metadata from an existing dataset |
| 130 | void DefineMetadata(const GSF::Data::DataSetPtr& metadata); | 139 | void DefineMetadata(const GSF::Data::DataSetPtr& metadata); |
| 131 | 140 | ||
| 141 | // Gets primary metadata. This dataset contains all the normalized metadata tables that define | ||
| 142 | // the available detail about the data points that can be subscribed to by clients. | ||
| 132 | const GSF::Data::DataSetPtr& GetMetadata() const; | 143 | const GSF::Data::DataSetPtr& GetMetadata() const; |
| 144 | |||
| 145 | // Gets filtering metadata. This dataset, derived from primary metadata, contains a flattened | ||
| 146 | // table used to subscribe to a filtered set of points with an expression, e.g.: | ||
| 147 | // FILTER ActiveMeasurements WHERE SignalType LIKE '%PHA' | ||
| 133 | const GSF::Data::DataSetPtr& GetFilteringMetadata() const; | 148 | const GSF::Data::DataSetPtr& GetFilteringMetadata() const; |
| 149 | |||
| 150 | // Filters primary MeasurementDetail metadata returning values as measurement metadata records | ||
| 134 | std::vector<MeasurementMetadataPtr> FilterMetadata(const std::string& filterExpression) const; | 151 | std::vector<MeasurementMetadataPtr> FilterMetadata(const std::string& filterExpression) const; |
| 135 | 152 | ||
| 136 | void PublishMeasurements(const std::vector<Measurement>& measurements); | 153 | void PublishMeasurements(const std::vector<Measurement>& measurements); |
| @@ -154,6 +171,9 @@ namespace Transport · 17 unmodified lines | |||
| 154 | bool IsNaNValueFilterForced() const; | 171 | bool IsNaNValueFilterForced() const; |
| 155 | void SetNaNValueFilterForced(bool forced); | 172 | void SetNaNValueFilterForced(bool forced); |
| 156 | 173 | ||
| 174 | bool GetSupportsTemporalSubscriptions() const; | ||
| 175 | void SetSupportsTemporalSubscriptions(bool value); | ||
| 176 | |||
| 157 | uint32_t GetCipherKeyRotationPeriod() const; | 177 | uint32_t GetCipherKeyRotationPeriod() const; |
| 158 | void SetCipherKeyRotationPeriod(uint32_t period); | 178 | void SetCipherKeyRotationPeriod(uint32_t period); |
| 159 | 179 | ||
| @@ -169,17 +189,18 @@ namespace Transport · 9 unmodified lines | |||
| 169 | // Callback registration | 189 | // Callback registration |
| 170 | // | 190 | // |
| 171 | // Callback functions are defined with the following signatures: | 191 | // Callback functions are defined with the following signatures: |
| 172 | // void ProcessStatusMessage(DataPublisher*, const string& message) | 192 | // void ProcessStatusMessage(DataPublisher* source, const string& message) |
| 173 | // void ProcessErrorMessage(DataPublisher*, const string& message) | 193 | // void ProcessErrorMessage(DataPublisher* source, const string& message) |
| 174 | // void ProcessClientConnected(DataPublisher*, const GSF::Guid& subscriberID, const string& connectionID); | 194 | // void ProcessClientConnected(DataPublisher* source, const SubscriberConnectionPtr& connection); |
| 175 | // void ProcessClientDisconnected(DataPublisher*, const GSF::Guid& subscriberID, const string& connectionID); | 195 | // void ProcessClientDisconnected(DataPublisher* source, const SubscriberConnectionPtr& connection); |
| 196 | // void ProcessTemporalSubscriptionRequested(DataPublisher* source, const SubscriberConnectionPtr& connection); | ||
| 197 | // void ProcessTemporalProcessingIntervalChangeRequested(DataPublisher* source, const SubscriberConnectionPtr& connection); | ||
| 176 | void RegisterStatusMessageCallback(const MessageCallback& statusMessageCallback); | 198 | void RegisterStatusMessageCallback(const MessageCallback& statusMessageCallback); |
| 177 | void RegisterErrorMessageCallback(const MessageCallback& errorMessageCallback); | 199 | void RegisterErrorMessageCallback(const MessageCallback& errorMessageCallback); |
| 178 | void RegisterClientConnectedCallback(const SubscriberConnectionCallback& clientConnectedCallback); | 200 | void RegisterClientConnectedCallback(const SubscriberConnectionCallback& clientConnectedCallback); |
| 179 | void RegisterClientDisconnectedCallback(const SubscriberConnectionCallback& clientDisconnectedCallback); | 201 | void RegisterClientDisconnectedCallback(const SubscriberConnectionCallback& clientDisconnectedCallback); |
| 180 | 202 | void RegisterTemporalSubscriptionRequestedCallback(const SubscriberConnectionCallback& temporalSubscriptionRequestedCallback); | |
| 181 | //static std::string DecodeClientString(const SubscriberConnectionPtr& connection, const uint8_t* data, uint32_t offset, uint32_t length); | 203 | void RegisterTemporalProcessingIntervalChangeRequestedCallback(const SubscriberConnectionCallback& temporalProcessingIntervalChangeRequestedCallback); |
| 182 | //static std::vector<uint8_t> EncodeClientString(const SubscriberConnectionPtr& connection, const std::string& value); | ||
| 183 | 204 | ||
| 184 | friend class SubscriberConnection; | 205 | friend class SubscriberConnection; |
| 185 | }; | 206 | }; |
| @@ -134,7 +134,7 @@ namespace Transport · 133 unmodified lines | |||
| 134 | 134 | ||
| 135 | class DataSubscriber // NOLINT | 135 | class DataSubscriber // NOLINT |
| 136 | { | 136 | { |
| 137 | private: | 137 | public: |
| 138 | // Function pointer types | 138 | // Function pointer types |
| 139 | typedef std::function<void(DataSubscriber*, const std::vector<uint8_t>&)> DispatcherFunction; | 139 | typedef std::function<void(DataSubscriber*, const std::vector<uint8_t>&)> DispatcherFunction; |
| 140 | typedef std::function<void(DataSubscriber*, const std::string&)> MessageCallback; | 140 | typedef std::function<void(DataSubscriber*, const std::string&)> MessageCallback; |
| @@ -144,6 +144,7 @@ namespace Transport · 3 unmodified lines | |||
| 144 | typedef std::function<void(DataSubscriber*)> ConfigurationChangedCallback; | 144 | typedef std::function<void(DataSubscriber*)> ConfigurationChangedCallback; |
| 145 | typedef std::function<void(DataSubscriber*)> ConnectionTerminatedCallback; | 145 | typedef std::function<void(DataSubscriber*)> ConnectionTerminatedCallback; |
| 146 | 146 | ||
| 147 | private: | ||
| 147 | // Structure used to dispatch | 148 | // Structure used to dispatch |
| 148 | // callbacks on the callback thread. | 149 | // callbacks on the callback thread. |
| 149 | struct CallbackDispatcher | 150 | struct CallbackDispatcher |
| @@ -263,14 +264,14 @@ namespace Transport · 113 unmodified lines | |||
| 263 | // Callback registration | 264 | // Callback registration |
| 264 | // | 265 | // |
| 265 | // Callback functions are defined with the following signatures: | 266 | // Callback functions are defined with the following signatures: |
| 266 | // void ProcessStatusMessage(DataSubscriber*, const string& message) | 267 | // void ProcessStatusMessage(DataSubscriber* source, const string& message) |
| 267 | // void ProcessErrorMessage(DataSubscriber*, const string& message) | 268 | // void ProcessErrorMessage(DataSubscriber* source, const string& message) |
| 268 | // void ProcessDataStartTime(DataSubscriber*, int64_t startTime) | 269 | // void ProcessDataStartTime(DataSubscriber* source, int64_t startTime) |
| 269 | // void ProcessMetadata(DataSubscriber*, const vector<uint8_t>& metadata) | 270 | // void ProcessMetadata(DataSubscriber* source, const vector<uint8_t>& metadata) |
| 270 | // void ProcessNewMeasurements(DataSubscriber*, const vector<MeasurementPtr>& newMeasurements) | 271 | // void ProcessNewMeasurements(DataSubscriber* source, const vector<MeasurementPtr>& newMeasurements) |
| 271 | // void ProcessProcessingComplete(DataSubscriber*, const string& message) | 272 | // void ProcessProcessingComplete(DataSubscriber* source, const string& message) |
| 272 | // void ProcessConfigurationChanged(DataSubscriber*) | 273 | // void ProcessConfigurationChanged(DataSubscriber* source) |
| 273 | // void ProcessConnectionTerminated(DataSubscriber*) | 274 | // void ProcessConnectionTerminated(DataSubscriber* source) |
| 274 | // | 275 | // |
| 275 | // Metadata is provided to the user as zlib-compressed XML, | 276 | // Metadata is provided to the user as zlib-compressed XML, |
| 276 | // and must be decompressed and interpreted before it can be used. | 277 | // and must be decompressed and interpreted before it can be used. |
| @@ -57,16 +57,28 @@ void PublisherInstance::HandleErrorMessage(DataPublisher* source, const string& · 56 unmodified lines | |||
| 57 | instance->ErrorMessage(message); | 57 | instance->ErrorMessage(message); |
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | void PublisherInstance::HandleClientConnected(DataPublisher* source, const GSF::Guid& subscriberID, const std::string& connectionID) | 60 | void PublisherInstance::HandleClientConnected(DataPublisher* source, const SubscriberConnectionPtr& connection) |
| 61 | { | 61 | { |
| 62 | PublisherInstance* instance = static_cast<PublisherInstance*>(source->GetUserData()); | 62 | PublisherInstance* instance = static_cast<PublisherInstance*>(source->GetUserData()); |
| 63 | instance->ClientConnected(subscriberID, connectionID); | 63 | instance->ClientConnected(connection); |
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | void PublisherInstance::HandleClientDisconnected(DataPublisher* source, const GSF::Guid& subscriberID, const std::string& connectionID) | 66 | void PublisherInstance::HandleClientDisconnected(DataPublisher* source, const SubscriberConnectionPtr& connection) |
| 67 | { | 67 | { |
| 68 | PublisherInstance* instance = static_cast<PublisherInstance*>(source->GetUserData()); | 68 | PublisherInstance* instance = static_cast<PublisherInstance*>(source->GetUserData()); |
| 69 | instance->ClientDisconnected(subscriberID, connectionID); | 69 | instance->ClientDisconnected(connection); |
| 70 | } | ||
| 71 | |||
| 72 | void PublisherInstance::HandleTemporalSubscriptionRequested(DataPublisher* source, const SubscriberConnectionPtr& connection) | ||
| 73 | { | ||
| 74 | PublisherInstance* instance = static_cast<PublisherInstance*>(source->GetUserData()); | ||
| 75 | instance->TemporalSubscriptionRequested(connection); | ||
| 76 | } | ||
| 77 | |||
| 78 | void PublisherInstance::HandleTemporalProcessingIntervalChangeRequested(DataPublisher* source, const SubscriberConnectionPtr& connection) | ||
| 79 | { | ||
| 80 | PublisherInstance* instance = static_cast<PublisherInstance*>(source->GetUserData()); | ||
| 81 | instance->TemporalProcessingIntervalChangeRequested(connection); | ||
| 70 | } | 82 | } |
| 71 | 83 | ||
| 72 | void PublisherInstance::StatusMessage(const string& message) | 84 | void PublisherInstance::StatusMessage(const string& message) |
| @@ -79,14 +91,24 @@ void PublisherInstance::ErrorMessage(const string& message) · 6 unmodified lines | |||
| 79 | cerr << message << endl << endl; | 91 | cerr << message << endl << endl; |
| 80 | } | 92 | } |
| 81 | 93 | ||
| 82 | void PublisherInstance::ClientConnected(const GSF::Guid& subscriberID, const string& connectionID) | 94 | void PublisherInstance::ClientConnected(const SubscriberConnectionPtr& connection) |
| 95 | { | ||
| 96 | cout << "Client \"" << connection->GetConnectionID() << "\" with subscriber ID " << ToString(connection->GetSubscriberID()) << " connected..." << endl << endl; | ||
| 97 | } | ||
| 98 | |||
| 99 | void PublisherInstance::ClientDisconnected(const SubscriberConnectionPtr& connection) | ||
| 100 | { | ||
| 101 | cout << "Client \"" << connection->GetConnectionID() << "\" with subscriber ID " << ToString(connection->GetSubscriberID()) << " disconnected..." << endl << endl; | ||
| 102 | } | ||
| 103 | |||
| 104 | void PublisherInstance::TemporalSubscriptionRequested(const SubscriberConnectionPtr& connection) | ||
| 83 | { | 105 | { |
| 84 | cout << "Client \"" << connectionID << "\" with subscriber ID " << ToString(subscriberID) << " connected..." << endl << endl; | 106 | cout << "Client \"" << connection->GetConnectionID() << "\" with subscriber ID " << ToString(connection->GetSubscriberID()) << " has requested a temporal subscription starting at " << ToString(connection->GetStartTimeConstraint()) << endl << endl; |
| 85 | } | 107 | } |
| 86 | 108 | ||
| 87 | void PublisherInstance::ClientDisconnected(const GSF::Guid& subscriberID, const std::string& connectionID) | 109 | void PublisherInstance::TemporalProcessingIntervalChangeRequested(const SubscriberConnectionPtr& connection) |
| 88 | { | 110 | { |
| 89 | cout << "Client \"" << connectionID << "\" with subscriber ID " << ToString(subscriberID) << " disconnected..." << endl << endl; | 111 | cout << "Client \"" << connection->GetConnectionID() << "\" with subscriber ID " << ToString(connection->GetSubscriberID()) << " has requested to change its temporal processing interval to " << ToString(connection->GetProcessingInterval()) << "ms" << endl << endl; |
| 90 | } | 112 | } |
| 91 | 113 | ||
| 92 | void PublisherInstance::Initialize() | 114 | void PublisherInstance::Initialize() |
| @@ -96,6 +118,8 @@ void PublisherInstance::Initialize() · 3 unmodified lines | |||
| 96 | m_publisher.RegisterErrorMessageCallback(&HandleErrorMessage); | 118 | m_publisher.RegisterErrorMessageCallback(&HandleErrorMessage); |
| 97 | m_publisher.RegisterClientConnectedCallback(&HandleClientConnected); | 119 | m_publisher.RegisterClientConnectedCallback(&HandleClientConnected); |
| 98 | m_publisher.RegisterClientDisconnectedCallback(&HandleClientDisconnected); | 120 | m_publisher.RegisterClientDisconnectedCallback(&HandleClientDisconnected); |
| 121 | m_publisher.RegisterTemporalSubscriptionRequestedCallback(&HandleTemporalSubscriptionRequested); | ||
| 122 | m_publisher.RegisterTemporalProcessingIntervalChangeRequestedCallback(&HandleTemporalProcessingIntervalChangeRequested); | ||
| 99 | 123 | ||
| 100 | m_initialized = true; | 124 | m_initialized = true; |
| 101 | } | 125 | } |
| @@ -43,14 +43,18 @@ namespace Transport · 42 unmodified lines | |||
| 43 | // Internal subscription event handlers | 43 | // Internal subscription event handlers |
| 44 | static void HandleStatusMessage(DataPublisher* source, const std::string& message); | 44 | static void HandleStatusMessage(DataPublisher* source, const std::string& message); |
| 45 | static void HandleErrorMessage(DataPublisher* source, const std::string& message); | 45 | static void HandleErrorMessage(DataPublisher* source, const std::string& message); |
| 46 | static void HandleClientConnected(DataPublisher* source, const Guid& subscriberID, const std::string& connectionID); | 46 | static void HandleClientConnected(DataPublisher* source, const SubscriberConnectionPtr& connection); |
| 47 | static void HandleClientDisconnected(DataPublisher* source, const Guid& subscriberID, const std::string& connectionID); | 47 | static void HandleClientDisconnected(DataPublisher* source, const SubscriberConnectionPtr& connection); |
| 48 | static void HandleTemporalSubscriptionRequested(DataPublisher* source, const SubscriberConnectionPtr& connection); | ||
| 49 | static void HandleTemporalProcessingIntervalChangeRequested(DataPublisher* source, const SubscriberConnectionPtr& connection); | ||
| 48 | 50 | ||
| 49 | protected: | 51 | protected: |
| 50 | virtual void StatusMessage(const std::string& message); // Defaults output to cout | 52 | virtual void StatusMessage(const std::string& message); // Defaults output to cout |
| 51 | virtual void ErrorMessage(const std::string& message); // Defaults output to cerr | 53 | virtual void ErrorMessage(const std::string& message); // Defaults output to cerr |
| 52 | virtual void ClientConnected(const Guid& subscriberID, const std::string& connectionID); | 54 | virtual void ClientConnected(const SubscriberConnectionPtr& connection); |
| 53 | virtual void ClientDisconnected(const Guid& subscriberID, const std::string& connectionID); | 55 | virtual void ClientDisconnected(const SubscriberConnectionPtr& connection); |
| 56 | virtual void TemporalSubscriptionRequested(const SubscriberConnectionPtr& connection); | ||
| 57 | virtual void TemporalProcessingIntervalChangeRequested(const SubscriberConnectionPtr& connection); | ||
| 54 | 58 | ||
| 55 | public: | 59 | public: |
| 56 | PublisherInstance(uint16_t port, bool ipV6); | 60 | PublisherInstance(uint16_t port, bool ipV6); |
| @@ -48,6 +48,9 @@ SubscriberConnection::SubscriberConnection(DataPublisherPtr parent, IOContext& c · 47 unmodified lines | |||
| 48 | m_subscriberID(NewGuid()), | 48 | m_subscriberID(NewGuid()), |
| 49 | m_operationalModes(OperationalModes::NoFlags), | 49 | m_operationalModes(OperationalModes::NoFlags), |
| 50 | m_encoding(OperationalEncoding::UTF8), | 50 | m_encoding(OperationalEncoding::UTF8), |
| 51 | m_startTimeConstraint(DateTime::MaxValue), | ||
| 52 | m_stopTimeConstraint(DateTime::MaxValue), | ||
| 53 | m_processingInterval(-1), | ||
| 51 | m_usePayloadCompression(false), | 54 | m_usePayloadCompression(false), |
| 52 | m_useCompactMeasurementFormat(true), | 55 | m_useCompactMeasurementFormat(true), |
| 53 | m_includeTime(true), | 56 | m_includeTime(true), |
| @@ -80,6 +83,11 @@ const DataPublisherPtr& SubscriberConnection::GetParent() const · 26 unmodified lines | |||
| 80 | return m_parent; | 83 | return m_parent; |
| 81 | } | 84 | } |
| 82 | 85 | ||
| 86 | SubscriberConnectionPtr SubscriberConnection::GetReference() | ||
| 87 | { | ||
| 88 | return shared_from_this(); | ||
| 89 | } | ||
| 90 | |||
| 83 | TcpSocket& SubscriberConnection::CommandChannelSocket() | 91 | TcpSocket& SubscriberConnection::CommandChannelSocket() |
| 84 | { | 92 | { |
| 85 | return m_commandChannelSocket; | 93 | return m_commandChannelSocket; |
| @@ -126,6 +134,43 @@ uint32_t SubscriberConnection::GetEncoding() const · 40 unmodified lines | |||
| 126 | return m_encoding; | 134 | return m_encoding; |
| 127 | } | 135 | } |
| 128 | 136 | ||
| 137 | bool SubscriberConnection::GetIsTemporalSubscription() const | ||
| 138 | { | ||
| 139 | return m_startTimeConstraint < DateTime::MaxValue; | ||
| 140 | } | ||
| 141 | |||
| 142 | const GSF::datetime_t& SubscriberConnection::GetStartTimeConstraint() const | ||
| 143 | { | ||
| 144 | return m_startTimeConstraint; | ||
| 145 | } | ||
| 146 | |||
| 147 | void SubscriberConnection::SetStartTimeConstraint(const GSF::datetime_t& value) | ||
| 148 | { | ||
| 149 | m_startTimeConstraint = value; | ||
| 150 | } | ||
| 151 | |||
| 152 | const GSF::datetime_t& SubscriberConnection::GetStopTimeConstraint() const | ||
| 153 | { | ||
| 154 | return m_stopTimeConstraint; | ||
| 155 | } | ||
| 156 | |||
| 157 | void SubscriberConnection::SetStopTimeConstraint(const GSF::datetime_t& value) | ||
| 158 | { | ||
| 159 | m_stopTimeConstraint = value; | ||
| 160 | } | ||
| 161 | |||
| 162 | int32_t SubscriberConnection::GetProcessingInterval() const | ||
| 163 | { | ||
| 164 | return m_processingInterval; | ||
| 165 | } | ||
| 166 | |||
| 167 | void SubscriberConnection::SetProcessingInterval(int32_t value) | ||
| 168 | { | ||
| 169 | m_processingInterval = value; | ||
| 170 | m_parent->DispatchTemporalProcessingIntervalChangeRequested(shared_from_this()); | ||
| 171 | m_parent->DispatchStatusMessage(m_connectionID + " was assigned a new processing interval of " + ToString(value) + "."); | ||
| 172 | } | ||
| 173 | |||
| 129 | bool SubscriberConnection::GetUsePayloadCompression() const | 174 | bool SubscriberConnection::GetUsePayloadCompression() const |
| 130 | { | 175 | { |
| 131 | return m_usePayloadCompression; | 176 | return m_usePayloadCompression; |
| @@ -313,50 +358,7 @@ void SubscriberConnection::Stop() · 181 unmodified lines | |||
| 313 | m_pingTimer.Stop(); | 358 | m_pingTimer.Stop(); |
| 314 | m_commandChannelSocket.shutdown(socket_base::shutdown_both); | 359 | m_commandChannelSocket.shutdown(socket_base::shutdown_both); |
| 315 | m_commandChannelSocket.cancel(); | 360 | m_commandChannelSocket.cancel(); |
| 316 | m_parent->RemoveConnection(shared_from_this()); | 361 | m_parent->ConnectionTerminated(shared_from_this()); |
| 317 | } | ||
| 318 | |||
| 319 | void SubscriberConnection::PublishMeasurements(const vector<Measurement>& measurements) | ||
| 320 | { | ||
| 321 | if (measurements.empty() || !m_isSubscribed) | ||
| 322 | return; | ||
| 323 | |||
| 324 | if (!m_startTimeSent) | ||
| 325 | m_startTimeSent = SendDataStartTime(measurements[0].Timestamp); | ||
| 326 | |||
| 327 | // TODO: Consider queuing measurements for processing | ||
| 328 | |||
| 329 | CompactMeasurement serializer(m_signalIndexCache, m_baseTimeOffsets, m_includeTime, m_useCompactMeasurementFormat); | ||
| 330 | vector<uint8_t> packet, buffer; | ||
| 331 | int32_t count = 0; | ||
| 332 | |||
| 333 | packet.reserve(MaxPacketSize); | ||
| 334 | buffer.reserve(16); | ||
| 335 | |||
| 336 | for (size_t i = 0; i < measurements.size(); i++) | ||
| 337 | { | ||
| 338 | const Measurement& measurement = measurements[i]; | ||
| 339 | const uint16_t runtimeID = m_signalIndexCache->GetSignalIndex(measurement.SignalID); | ||
| 340 | |||
| 341 | if (runtimeID == UInt16::MaxValue) | ||
| 342 | continue; | ||
| 343 | |||
| 344 | const uint32_t length = serializer.SerializeMeasurement(measurement, buffer, runtimeID); | ||
| 345 | |||
| 346 | if (packet.size() + length > MaxPacketSize) | ||
| 347 | { | ||
| 348 | PublishDataPacket(packet, count); | ||
| 349 | packet.clear(); | ||
| 350 | count = 0; | ||
| 351 | } | ||
| 352 | |||
| 353 | WriteBytes(packet, buffer); | ||
| 354 | buffer.clear(); | ||
| 355 | count++; | ||
| 356 | } | ||
| 357 | |||
| 358 | if (count > 0) | ||
| 359 | PublishDataPacket(packet, count); | ||
| 360 | } | 362 | } |
| 361 | 363 | ||
| 362 | void SubscriberConnection::PublishMeasurements(const vector<MeasurementPtr>& measurements) | 364 | void SubscriberConnection::PublishMeasurements(const vector<MeasurementPtr>& measurements) |
| @@ -402,6 +404,11 @@ void SubscriberConnection::PublishMeasurements(const vector<MeasurementPtr>& mea · 39 unmodified lines | |||
| 402 | PublishDataPacket(packet, count); | 404 | PublishDataPacket(packet, count); |
| 403 | } | 405 | } |
| 404 | 406 | ||
| 407 | void SubscriberConnection::CompleteTemporalSubscription() | ||
| 408 | { | ||
| 409 | SendResponse(ServerResponse::ProcessingComplete, ServerCommand::Subscribe, ToString(m_parent->GetNodeID())); | ||
| 410 | } | ||
| 411 | |||
| 405 | void SubscriberConnection::HandleSubscribe(uint8_t* data, uint32_t length) | 412 | void SubscriberConnection::HandleSubscribe(uint8_t* data, uint32_t length) |
| 406 | { | 413 | { |
| 407 | try | 414 | try |
| @@ -441,6 +448,24 @@ void SubscriberConnection::HandleSubscribe(uint8_t* data, uint32_t length) · 33 unmodified lines | |||
| 441 | if (TryGetValue(settings, "requestNaNValueFilter", setting)) | 448 | if (TryGetValue(settings, "requestNaNValueFilter", setting)) |
| 442 | SetIsNaNFiltered(ParseBoolean(setting)); | 449 | SetIsNaNFiltered(ParseBoolean(setting)); |
| 443 | 450 | ||
| 451 | if (TryGetValue(settings, "startTimeConstraint", setting)) | ||
| 452 | TryParseTimestamp(setting.c_str(), m_startTimeConstraint, DateTime::MaxValue); | ||
| 453 | |||
| 454 | if (TryGetValue(settings, "startTimeConstraint", setting)) | ||
| 455 | TryParseTimestamp(setting.c_str(), m_startTimeConstraint, DateTime::MaxValue); | ||
| 456 | |||
| 457 | if (TryGetValue(settings, "processingInterval", setting) && !setting.empty()) | ||
| 458 | TryParseInt32(setting, m_processingInterval, -1); | ||
| 459 | |||
| 460 | if (GetIsTemporalSubscription()) | ||
| 461 | { | ||
| 462 | if (!m_parent->GetSupportsTemporalSubscriptions()) | ||
| 463 | throw PublisherException("Publisher does not temporal subscriptions"); | ||
| 464 | |||
| 465 | if (m_startTimeConstraint > m_stopTimeConstraint) | ||
| 466 | throw PublisherException("Specified stop time of requested temporal subscription precedes start time"); | ||
| 467 | } | ||
| 468 | |||
| 444 | SetUsePayloadCompression(usePayloadCompression); | 469 | SetUsePayloadCompression(usePayloadCompression); |
| 445 | SetUseCompactMeasurementFormat(useCompactMeasurementFormat); | 470 | SetUseCompactMeasurementFormat(useCompactMeasurementFormat); |
| 446 | 471 | ||
| @@ -518,36 +543,48 @@ void SubscriberConnection::HandleSubscribe(uint8_t* data, uint32_t length) · 71 unmodified lines | |||
| 518 | SetIsSubscribed(true); | 543 | SetIsSubscribed(true); |
| 519 | SendResponse(ServerResponse::Succeeded, ServerCommand::Subscribe, message); | 544 | SendResponse(ServerResponse::Succeeded, ServerCommand::Subscribe, message); |
| 520 | m_parent->DispatchStatusMessage(message); | 545 | m_parent->DispatchStatusMessage(message); |
| 546 | |||
| 547 | if (GetIsTemporalSubscription()) | ||
| 548 | m_parent->DispatchTemporalSubscriptionRequested(shared_from_this()); | ||
| 521 | } | 549 | } |
| 522 | else | 550 | else |
| 523 | { | 551 | { |
| 524 | const string message = byteLength > 0 ? | 552 | HandleSubscribeFailure(byteLength > 0 ? |
| 525 | "Not enough buffer was provided to parse client data subscription." : | 553 | "Not enough buffer was provided to parse client data subscription." : |
| 526 | "Cannot initialize client data subscription without a connection string."; | 554 | "Cannot initialize client data subscription without a connection string."); |
| 527 | |||
| 528 | SendResponse(ServerResponse::Failed, ServerCommand::Subscribe, message); | ||
| 529 | m_parent->DispatchErrorMessage(message); | ||
| 530 | } | 555 | } |
| 531 | } | 556 | } |
| 532 | } | 557 | } |
| 533 | else | 558 | else |
| 534 | { | 559 | { |
| 535 | const string message = "Not enough buffer was provided to parse client data subscription."; | 560 | HandleSubscribeFailure("Not enough buffer was provided to parse client data subscription."); |
| 536 | SendResponse(ServerResponse::Failed, ServerCommand::Subscribe, message); | ||
| 537 | m_parent->DispatchErrorMessage(message); | ||
| 538 | } | 561 | } |
| 539 | } | 562 | } |
| 540 | catch (const std::exception& ex) | 563 | catch (const PublisherException& ex) |
| 541 | { | 564 | { |
| 542 | const string message = "Failed to process client data subscription due to exception: " + string(ex.what()); | 565 | HandleSubscribeFailure("Failed to process client data subscription due to exception: " + string(ex.what())); |
| 543 | SendResponse(ServerResponse::Failed, ServerCommand::Subscribe, message); | 566 | } |
| 544 | m_parent->DispatchErrorMessage(message); | 567 | catch (...) |
| 568 | { | ||
| 569 | HandleSubscribeFailure("Failed to process client data subscription due to exception: " + boost::current_exception_diagnostic_information(true)); | ||
| 545 | } | 570 | } |
| 546 | } | 571 | } |
| 547 | 572 | ||
| 573 | void SubscriberConnection::HandleSubscribeFailure(const std::string& message) | ||
| 574 | { | ||
| 575 | SendResponse(ServerResponse::Failed, ServerCommand::Subscribe, message); | ||
| 576 | m_parent->DispatchErrorMessage(message); | ||
| 577 | |||
| 578 | if (GetIsTemporalSubscription()) | ||
| 579 | CompleteTemporalSubscription(); | ||
| 580 | } | ||
| 581 | |||
| 548 | void SubscriberConnection::HandleUnsubscribe() | 582 | void SubscriberConnection::HandleUnsubscribe() |
| 549 | { | 583 | { |
| 550 | SetIsSubscribed(false); | 584 | SetIsSubscribed(false); |
| 585 | |||
| 586 | if (GetIsTemporalSubscription()) | ||
| 587 | CompleteTemporalSubscription(); | ||
| 551 | } | 588 | } |
| 552 | 589 | ||
| 553 | void SubscriberConnection::HandleMetadataRefresh(uint8_t* data, uint32_t length) | 590 | void SubscriberConnection::HandleMetadataRefresh(uint8_t* data, uint32_t length) |
| @@ -559,7 +596,7 @@ void SubscriberConnection::HandleMetadataRefresh(uint8_t* data, uint32_t length) · 5 unmodified lines | |||
| 559 | m_parent->DispatchStatusMessage("Received meta-data refresh request from " + GetConnectionID() + ", preparing response..."); | 596 | m_parent->DispatchStatusMessage("Received meta-data refresh request from " + GetConnectionID() + ", preparing response..."); |
| 560 | 597 | ||
| 561 | StringMap<ExpressionTreePtr> filterExpressions; | 598 | StringMap<ExpressionTreePtr> filterExpressions; |
| 562 | const DateTime startTime = UtcNow(); //-V821 | 599 | const datetime_t startTime = UtcNow(); //-V821 |
| 563 | 600 | ||
| 564 | try | 601 | try |
| 565 | { | 602 | { |
| @@ -622,8 +659,22 @@ void SubscriberConnection::HandleRotateCipherKeys() · 56 unmodified lines | |||
| 622 | { | 659 | { |
| 623 | } | 660 | } |
| 624 | 661 | ||
| 625 | void SubscriberConnection::HandleUpdateProcessingInterval(uint8_t* data, uint32_t length) | 662 | void SubscriberConnection::HandleUpdateProcessingInterval(const uint8_t* data, uint32_t length) |
| 626 | { | 663 | { |
| 664 | // Make sure there is enough buffer for new processing interval value | ||
| 665 | if (length >= 4) | ||
| 666 | { | ||
| 667 | // Next 4 bytes are an integer representing the new processing interval | ||
| 668 | const int32_t processingInterval = EndianConverter::Default.ConvertBigEndian(*reinterpret_cast<const int32_t*>(data)); | ||
| 669 | SetProcessingInterval(processingInterval); | ||
| 670 | SendResponse(ServerResponse::Succeeded, ServerCommand::UpdateProcessingInterval, "New processing interval of " + ToString(processingInterval) + " assigned."); | ||
| 671 | } | ||
| 672 | else | ||
| 673 | { | ||
| 674 | const string message = "Not enough buffer was provided to update client processing interval."; | ||
| 675 | SendResponse(ServerResponse::Failed, ServerCommand::UpdateProcessingInterval, message); | ||
| 676 | m_parent->DispatchErrorMessage(message); | ||
| 677 | } | ||
| 627 | } | 678 | } |
| 628 | 679 | ||
| 629 | void SubscriberConnection::HandleDefineOperationalModes(uint8_t* data, uint32_t length) | 680 | void SubscriberConnection::HandleDefineOperationalModes(uint8_t* data, uint32_t length) |
| @@ -1056,6 +1107,11 @@ void SubscriberConnection::WriteHandler(const ErrorCode& error, uint32_t bytesTr · 426 unmodified lines | |||
| 1056 | } | 1107 | } |
| 1057 | } | 1108 | } |
| 1058 | 1109 | ||
| 1110 | bool SubscriberConnection::SendResponse(uint8_t responseCode, uint8_t commandCode) | ||
| 1111 | { | ||
| 1112 | return SendResponse(responseCode, commandCode, vector<uint8_t>(0)); | ||
| 1113 | } | ||
| 1114 | |||
| 1059 | bool SubscriberConnection::SendResponse(uint8_t responseCode, uint8_t commandCode, const string& message) | 1115 | bool SubscriberConnection::SendResponse(uint8_t responseCode, uint8_t commandCode, const string& message) |
| 1060 | { | 1116 | { |
| 1061 | return SendResponse(responseCode, commandCode, EncodeString(message)); | 1117 | return SendResponse(responseCode, commandCode, EncodeString(message)); |
| @@ -37,6 +37,9 @@ namespace Transport · 36 unmodified lines | |||
| 37 | class DataPublisher; | 37 | class DataPublisher; |
| 38 | typedef GSF::SharedPtr<DataPublisher> DataPublisherPtr; | 38 | typedef GSF::SharedPtr<DataPublisher> DataPublisherPtr; |
| 39 | 39 | ||
| 40 | class SubscriberConnection; | ||
| 41 | typedef GSF::SharedPtr<SubscriberConnection> SubscriberConnectionPtr; | ||
| 42 | |||
| 40 | // Represents a subscriber connection to a data publisher | 43 | // Represents a subscriber connection to a data publisher |
| 41 | class SubscriberConnection : public GSF::EnableSharedThisPtr<SubscriberConnection> // NOLINT | 44 | class SubscriberConnection : public GSF::EnableSharedThisPtr<SubscriberConnection> // NOLINT |
| 42 | { | 45 | { |
| @@ -49,6 +52,9 @@ namespace Transport · 6 unmodified lines | |||
| 49 | std::string m_subscriptionInfo; | 52 | std::string m_subscriptionInfo; |
| 50 | uint32_t m_operationalModes; | 53 | uint32_t m_operationalModes; |
| 51 | uint32_t m_encoding; | 54 | uint32_t m_encoding; |
| 55 | GSF::datetime_t m_startTimeConstraint; | ||
| 56 | GSF::datetime_t m_stopTimeConstraint; | ||
| 57 | int32_t m_processingInterval; | ||
| 52 | bool m_usePayloadCompression; | 58 | bool m_usePayloadCompression; |
| 53 | bool m_useCompactMeasurementFormat; | 59 | bool m_useCompactMeasurementFormat; |
| 54 | bool m_includeTime; | 60 | bool m_includeTime; |
| @@ -79,17 +85,18 @@ namespace Transport · 24 unmodified lines | |||
| 79 | SignalIndexCachePtr m_signalIndexCache; | 85 | SignalIndexCachePtr m_signalIndexCache; |
| 80 | int32_t m_timeIndex; | 86 | int32_t m_timeIndex; |
| 81 | int64_t m_baseTimeOffsets[2]; | 87 | int64_t m_baseTimeOffsets[2]; |
| 82 | DateTime m_lastPublishTime; | 88 | datetime_t m_lastPublishTime; |
| 83 | //TSSCMeasurementParser m_tsscMeasurementParser; | 89 | //TSSCMeasurementParser m_tsscMeasurementParser; |
| 84 | //bool m_tsscResetRequested; | 90 | //bool m_tsscResetRequested; |
| 85 | //uint16_t m_tsscSequenceNumber; | 91 | //uint16_t m_tsscSequenceNumber; |
| 86 | 92 | ||
| 87 | // Server request handlers | 93 | // Server request handlers |
| 88 | void HandleSubscribe(uint8_t* data, uint32_t length); | 94 | void HandleSubscribe(uint8_t* data, uint32_t length); |
| 95 | void HandleSubscribeFailure(const std::string& message); | ||
| 89 | void HandleUnsubscribe(); | 96 | void HandleUnsubscribe(); |
| 90 | void HandleMetadataRefresh(uint8_t* data, uint32_t length); | 97 | void HandleMetadataRefresh(uint8_t* data, uint32_t length); |
| 91 | void HandleRotateCipherKeys(); | 98 | void HandleRotateCipherKeys(); |
| 92 | void HandleUpdateProcessingInterval(uint8_t* data, uint32_t length); | 99 | void HandleUpdateProcessingInterval(const uint8_t* data, uint32_t length); |
| 93 | void HandleDefineOperationalModes(uint8_t* data, uint32_t length); | 100 | void HandleDefineOperationalModes(uint8_t* data, uint32_t length); |
| 94 | void HandleConfirmNotification(uint8_t* data, uint32_t length); | 101 | void HandleConfirmNotification(uint8_t* data, uint32_t length); |
| 95 | void HandleConfirmBufferBlock(uint8_t* data, uint32_t length); | 102 | void HandleConfirmBufferBlock(uint8_t* data, uint32_t length); |
| @@ -112,43 +119,78 @@ namespace Transport · 16 unmodified lines | |||
| 112 | ~SubscriberConnection(); | 119 | ~SubscriberConnection(); |
| 113 | 120 | ||
| 114 | const DataPublisherPtr& GetParent() const; | 121 | const DataPublisherPtr& GetParent() const; |
| 122 | SubscriberConnectionPtr GetReference(); | ||
| 115 | 123 | ||
| 116 | GSF::TcpSocket& CommandChannelSocket(); | 124 | GSF::TcpSocket& CommandChannelSocket(); |
| 117 | 125 | ||
| 118 | // Gets or sets subscriber identification used when subscriber is known and pre-established | 126 | // Gets or sets subscriber UUID used when subscriber is known and pre-established |
| 119 | const GSF::Guid& GetSubscriberID() const; | 127 | const GSF::Guid& GetSubscriberID() const; |
| 120 | void SetSubscriberID(const GSF::Guid& id); | 128 | void SetSubscriberID(const GSF::Guid& id); |
| 121 | 129 | ||
| 130 | // Gets subscriber connection identification, e.g., remote IP/port, for display and logging references | ||
| 122 | const std::string& GetConnectionID() const; | 131 | const std::string& GetConnectionID() const; |
| 132 | |||
| 133 | // Gets subscriber remote IP address | ||
| 123 | const GSF::IPAddress& GetIPAddress() const; | 134 | const GSF::IPAddress& GetIPAddress() const; |
| 135 | |||
| 136 | // Gets subscriber communications port | ||
| 124 | const std::string& GetHostName() const; | 137 | const std::string& GetHostName() const; |
| 125 | 138 | ||
| 139 | // Gets or sets established subscriber operational modes | ||
| 126 | uint32_t GetOperationalModes() const; | 140 | uint32_t GetOperationalModes() const; |
| 127 | void SetOperationalModes(uint32_t value); | 141 | void SetOperationalModes(uint32_t value); |
| 128 | 142 | ||
| 143 | // Gets established subscriber string encoding | ||
| 129 | uint32_t GetEncoding() const; | 144 | uint32_t GetEncoding() const; |
| 130 | 145 | ||
| 146 | // Gets flags that determines if this subscription is temporal based | ||
| 147 | bool GetIsTemporalSubscription() const; | ||
| 148 | |||
| 149 | // Gets or sets the start time temporal processing constraint | ||
| 150 | const GSF::datetime_t& GetStartTimeConstraint() const; | ||
| 151 | void SetStartTimeConstraint(const GSF::datetime_t& value); | ||
| 152 | |||
| 153 | // Gets or sets the stop time temporal processing constraint | ||
| 154 | const GSF::datetime_t& GetStopTimeConstraint() const; | ||
| 155 | void SetStopTimeConstraint(const GSF::datetime_t& value); | ||
| 156 | |||
| 157 | // Gets or sets the desired temporal processing interval, in milliseconds | ||
| 158 | // With the exception of the values of -1 and 0, this value specifies the desired processing interval for data, i.e., | ||
| 159 | // basically a delay, or timer interval, over which to process data. A value of -1 means to use the default processing | ||
| 160 | // interval while a value of 0 means to process data as fast as possible. | ||
| 161 | int32_t GetProcessingInterval() const; | ||
| 162 | void SetProcessingInterval(int32_t value); | ||
| 163 | |||
| 164 | // Gets or sets flag that determines if payload compression should be enabled in data packets | ||
| 131 | bool GetUsePayloadCompression() const; | 165 | bool GetUsePayloadCompression() const; |
| 132 | void SetUsePayloadCompression(bool value); | 166 | void SetUsePayloadCompression(bool value); |
| 133 | 167 | ||
| 168 | // Gets or sets flag that determines if the compact measurement format should be used in data packets | ||
| 134 | bool GetUseCompactMeasurementFormat() const; | 169 | bool GetUseCompactMeasurementFormat() const; |
| 135 | void SetUseCompactMeasurementFormat(bool value); | 170 | void SetUseCompactMeasurementFormat(bool value); |
| 136 | 171 | ||
| 172 | // Gets or sets flag that determines if time should be included in data packets when the compact measurement format used | ||
| 137 | bool GetIncludeTime() const; | 173 | bool GetIncludeTime() const; |
| 138 | void SetIncludeTime(bool value); | 174 | void SetIncludeTime(bool value); |
| 139 | 175 | ||
| 176 | // Gets or sets flag that determines if time should be restricted to millisecond resolution in data packets when the | ||
| 177 | // compact measurement format used; otherwise, full resolution time will be used | ||
| 140 | bool GetUseMillisecondResolution() const; | 178 | bool GetUseMillisecondResolution() const; |
| 141 | void SetUseMillisecondResolution(bool value); | 179 | void SetUseMillisecondResolution(bool value); |
| 142 | 180 | ||
| 181 | // Gets or sets flag that determines if NaN values should be excluded from data packets | ||
| 143 | bool GetIsNaNFiltered() const; | 182 | bool GetIsNaNFiltered() const; |
| 144 | void SetIsNaNFiltered(bool value); | 183 | void SetIsNaNFiltered(bool value); |
| 145 | 184 | ||
| 185 | // Gets or sets flag that determines if subscriber connection is currently subscribed | ||
| 146 | bool GetIsSubscribed() const; | 186 | bool GetIsSubscribed() const; |
| 147 | void SetIsSubscribed(bool value); | 187 | void SetIsSubscribed(bool value); |
| 148 | 188 | ||
| 189 | // Gets or sets subscription details about subscriber | ||
| 149 | const std::string& GetSubscriptionInfo() const; | 190 | const std::string& GetSubscriptionInfo() const; |
| 150 | void SetSubscriptionInfo(const std::string& value); | 191 | void SetSubscriptionInfo(const std::string& value); |
| 151 | 192 | ||
| 193 | // Gets or sets signal index cache for subscriber representing run-time mappings for subscribed points | ||
| 152 | const SignalIndexCachePtr& GetSignalIndexCache() const; | 194 | const SignalIndexCachePtr& GetSignalIndexCache() const; |
| 153 | void SetSignalIndexCache(SignalIndexCachePtr signalIndexCache); | 195 | void SetSignalIndexCache(SignalIndexCachePtr signalIndexCache); |
| 154 | 196 | ||
| @@ -164,15 +206,16 @@ namespace Transport · 9 unmodified lines | |||
| 164 | void Start(); | 206 | void Start(); |
| 165 | void Stop(); | 207 | void Stop(); |
| 166 | 208 | ||
| 167 | void PublishMeasurements(const std::vector<Measurement>& measurements); | ||
| 168 | void PublishMeasurements(const std::vector<MeasurementPtr>& measurements); | 209 | void PublishMeasurements(const std::vector<MeasurementPtr>& measurements); |
| 210 | void CompleteTemporalSubscription(); | ||
| 169 | 211 | ||
| 170 | void CommandChannelSendAsync(uint8_t* data, uint32_t offset, uint32_t length); | 212 | void CommandChannelSendAsync(uint8_t* data, uint32_t offset, uint32_t length); |
| 171 | void DataChannelSendAsync(uint8_t* data, uint32_t offset, uint32_t length); | 213 | void DataChannelSendAsync(uint8_t* data, uint32_t offset, uint32_t length); |
| 172 | void WriteHandler(const ErrorCode& error, uint32_t bytesTransferred); | 214 | void WriteHandler(const ErrorCode& error, uint32_t bytesTransferred); |
| 173 | 215 | ||
| 216 | bool SendResponse(uint8_t responseCode, uint8_t commandCode); | ||
| 174 | bool SendResponse(uint8_t responseCode, uint8_t commandCode, const std::string& message); | 217 | bool SendResponse(uint8_t responseCode, uint8_t commandCode, const std::string& message); |
| 175 | bool SendResponse(uint8_t responseCode, uint8_t commandCode, const std::vector<uint8_t>& data = {}); | 218 | bool SendResponse(uint8_t responseCode, uint8_t commandCode, const std::vector<uint8_t>& data); |
| 176 | 219 | ||
| 177 | std::string DecodeString(const uint8_t* data, uint32_t offset, uint32_t length) const; | 220 | std::string DecodeString(const uint8_t* data, uint32_t offset, uint32_t length) const; |
| 178 | std::vector<uint8_t> EncodeString(const std::string& value) const; | 221 | std::vector<uint8_t> EncodeString(const std::string& value) const; |
| @@ -562,7 +562,7 @@ void SubscriberInstance::DataStartTime(time_t unixSOC, uint16_t milliseconds) · 561 unmodified lines | |||
| 562 | { | 562 | { |
| 563 | } | 563 | } |
| 564 | 564 | ||
| 565 | void SubscriberInstance::DataStartTime(DateTime startTime) | 565 | void SubscriberInstance::DataStartTime(datetime_t startTime) |
| 566 | { | 566 | { |
| 567 | } | 567 | } |
| 568 | 568 | ||
| @@ -876,7 +876,7 @@ void SubscriberInstance::ConstructConfigurationFrames(const StringMap<DeviceMeta · 307 unmodified lines | |||
| 876 | phasorReference->Phasor->Type = "?"; | 876 | phasorReference->Phasor->Type = "?"; |
| 877 | phasorReference->Phasor->Phase = "+"; | 877 | phasorReference->Phasor->Phase = "+"; |
| 878 | phasorReference->Phasor->SourceIndex = i; | 878 | phasorReference->Phasor->SourceIndex = i; |
| 879 | phasorReference->Phasor->UpdatedOn = DateTime(); | 879 | phasorReference->Phasor->UpdatedOn = datetime_t(); |
| 880 | 880 | ||
| 881 | phasorReference->Angle = nullptr; | 881 | phasorReference->Angle = nullptr; |
| 882 | phasorReference->Magnitude = nullptr; | 882 | phasorReference->Magnitude = nullptr; |
| @@ -912,7 +912,7 @@ void SubscriberInstance::ConstructConfigurationFrames(const StringMap<DeviceMeta · 29 unmodified lines | |||
| 912 | measurement->Reference.Kind = SignalKind::Analog; | 912 | measurement->Reference.Kind = SignalKind::Analog; |
| 913 | measurement->PhasorSourceIndex = 0U; | 913 | measurement->PhasorSourceIndex = 0U; |
| 914 | measurement->Description = ""; | 914 | measurement->Description = ""; |
| 915 | measurement->UpdatedOn = DateTime(); | 915 | measurement->UpdatedOn = datetime_t(); |
| 916 | 916 | ||
| 917 | configurationFrame->Analogs.push_back(measurement); | 917 | configurationFrame->Analogs.push_back(measurement); |
| 918 | } | 918 | } |
| @@ -946,7 +946,7 @@ void SubscriberInstance::ConstructConfigurationFrames(const StringMap<DeviceMeta · 27 unmodified lines | |||
| 946 | measurement->Reference.Kind = SignalKind::Digital; | 946 | measurement->Reference.Kind = SignalKind::Digital; |
| 947 | measurement->PhasorSourceIndex = 0U; | 947 | measurement->PhasorSourceIndex = 0U; |
| 948 | measurement->Description = ""; | 948 | measurement->Description = ""; |
| 949 | measurement->UpdatedOn = DateTime(); | 949 | measurement->UpdatedOn = datetime_t(); |
| 950 | 950 | ||
| 951 | configurationFrame->Digitals.push_back(measurement); | 951 | configurationFrame->Digitals.push_back(measurement); |
| 952 | } | 952 | } |
| @@ -79,7 +79,7 @@ namespace Transport · 78 unmodified lines | |||
| 79 | virtual void StatusMessage(const std::string& message); // Defaults output to cout | 79 | virtual void StatusMessage(const std::string& message); // Defaults output to cout |
| 80 | virtual void ErrorMessage(const std::string& message); // Defaults output to cerr | 80 | virtual void ErrorMessage(const std::string& message); // Defaults output to cerr |
| 81 | virtual void DataStartTime(time_t unixSOC, uint16_t milliseconds); | 81 | virtual void DataStartTime(time_t unixSOC, uint16_t milliseconds); |
| 82 | virtual void DataStartTime(DateTime startTime); | 82 | virtual void DataStartTime(datetime_t startTime); |
| 83 | virtual void ReceivedMetadata(const std::vector<uint8_t>& payload); | 83 | virtual void ReceivedMetadata(const std::vector<uint8_t>& payload); |
| 84 | virtual void ParsedMetadata(); | 84 | virtual void ParsedMetadata(); |
| 85 | virtual void ReceivedNewMeasurements(const std::vector<MeasurementPtr>& measurements); | 85 | virtual void ReceivedNewMeasurements(const std::vector<MeasurementPtr>& measurements); |
| @@ -64,7 +64,7 @@ float64_t Measurement::AdjustedValue() const · 63 unmodified lines | |||
| 64 | return Value * Multiplier + Adder; | 64 | return Value * Multiplier + Adder; |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | DateTime Measurement::GetDateTime() const | 67 | datetime_t Measurement::GetDateTime() const |
| 68 | { | 68 | { |
| 69 | return FromTicks(Timestamp); | 69 | return FromTicks(Timestamp); |
| 70 | } | 70 | } |
| @@ -74,6 +74,23 @@ void Measurement::GetUnixTime(time_t& unixSOC, uint16_t& milliseconds) const · 3 unmodified lines | |||
| 74 | ToUnixTime(Timestamp, unixSOC, milliseconds); | 74 | ToUnixTime(Timestamp, unixSOC, milliseconds); |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | MeasurementPtr TimeSeries::ToPtr(const Measurement& source) | ||
| 78 | { | ||
| 79 | MeasurementPtr destination = NewSharedPtr<Measurement>(); | ||
| 80 | |||
| 81 | destination->ID = source.ID; | ||
| 82 | destination->Source = source.Source; | ||
| 83 | destination->SignalID = source.SignalID; | ||
| 84 | destination->Tag = source.Tag; | ||
| 85 | destination->Value = source.Value; | ||
| 86 | destination->Adder = source.Adder; | ||
| 87 | destination->Multiplier = source.Multiplier; | ||
| 88 | destination->Timestamp = source.Timestamp; | ||
| 89 | destination->Flags = source.Flags; | ||
| 90 | |||
| 91 | return destination; | ||
| 92 | } | ||
| 93 | |||
| 77 | SignalReference::SignalReference() : | 94 | SignalReference::SignalReference() : |
| 78 | SignalID(Empty::Guid), | 95 | SignalID(Empty::Guid), |
| 79 | Index(0), | 96 | Index(0), |
| @@ -97,13 +97,14 @@ namespace TimeSeries · 96 unmodified lines | |||
| 97 | float64_t AdjustedValue() const; | 97 | float64_t AdjustedValue() const; |
| 98 | 98 | ||
| 99 | // Gets Timestamp as DateTime | 99 | // Gets Timestamp as DateTime |
| 100 | DateTime GetDateTime() const; | 100 | datetime_t GetDateTime() const; |
| 101 | 101 | ||
| 102 | // Gets Timestamp in Unix second of century and milliseconds | 102 | // Gets Timestamp in Unix second of century and milliseconds |
| 103 | void GetUnixTime(time_t& unixSOC, uint16_t& milliseconds) const; | 103 | void GetUnixTime(time_t& unixSOC, uint16_t& milliseconds) const; |
| 104 | }; | 104 | }; |
| 105 | 105 | ||
| 106 | typedef SharedPtr<Measurement> MeasurementPtr; | 106 | typedef SharedPtr<Measurement> MeasurementPtr; |
| 107 | MeasurementPtr ToPtr(const Measurement& source); | ||
| 107 | 108 | ||
| 108 | enum SignalKind : int16_t | 109 | enum SignalKind : int16_t |
| 109 | { | 110 | { |
| @@ -162,7 +163,7 @@ namespace TimeSeries · 52 unmodified lines | |||
| 162 | SignalReference Reference; // Parsed signal reference structure | 163 | SignalReference Reference; // Parsed signal reference structure |
| 163 | uint16_t PhasorSourceIndex; // Measurement phasor index, if measurement represents a "Phasor" | 164 | uint16_t PhasorSourceIndex; // Measurement phasor index, if measurement represents a "Phasor" |
| 164 | std::string Description; // Detailed measurement description (free-form) | 165 | std::string Description; // Detailed measurement description (free-form) |
| 165 | DateTime UpdatedOn; // Time of last meta-data update | 166 | datetime_t UpdatedOn; // Time of last meta-data update |
| 166 | }; | 167 | }; |
| 167 | 168 | ||
| 168 | typedef SharedPtr<MeasurementMetadata> MeasurementMetadataPtr; | 169 | typedef SharedPtr<MeasurementMetadata> MeasurementMetadataPtr; |
| @@ -174,7 +175,7 @@ namespace TimeSeries · 5 unmodified lines | |||
| 174 | std::string Type; // Phasor type, i.e., "V" for voltage or "I" for current | 175 | std::string Type; // Phasor type, i.e., "V" for voltage or "I" for current |
| 175 | std::string Phase; // Phasor phase, one of, "+", "-", "0", "A", "B" or "C" | 176 | std::string Phase; // Phasor phase, one of, "+", "-", "0", "A", "B" or "C" |
| 176 | uint16_t SourceIndex; // Phasor ordered index, uses 1-based indexing (key to MeasurementMetadata.PhasorSourceIndex) | 177 | uint16_t SourceIndex; // Phasor ordered index, uses 1-based indexing (key to MeasurementMetadata.PhasorSourceIndex) |
| 177 | DateTime UpdatedOn; // Time of last meta-data update | 178 | datetime_t UpdatedOn; // Time of last meta-data update |
| 178 | }; | 179 | }; |
| 179 | 180 | ||
| 180 | typedef SharedPtr<PhasorMetadata> PhasorMetadataPtr; | 181 | typedef SharedPtr<PhasorMetadata> PhasorMetadataPtr; |
| @@ -202,7 +203,7 @@ namespace TimeSeries · 21 unmodified lines | |||
| 202 | std::string VendorDeviceName; // Original vendor device name, e.g., PMU brand (if useful / provided) | 203 | std::string VendorDeviceName; // Original vendor device name, e.g., PMU brand (if useful / provided) |
| 203 | float64_t Longitude; // Device longitude (if reported) | 204 | float64_t Longitude; // Device longitude (if reported) |
| 204 | float64_t Latitude; // Device latitude (if reported) | 205 | float64_t Latitude; // Device latitude (if reported) |
| 205 | DateTime UpdatedOn; // Time of last meta-data update | 206 | datetime_t UpdatedOn; // Time of last meta-data update |
| 206 | 207 | ||
| 207 | // Associated measurement and phasor meta-data | 208 | // Associated measurement and phasor meta-data |
| 208 | std::vector<MeasurementMetadataPtr> Measurements; // DataPublisher does not need to assign | 209 | std::vector<MeasurementMetadataPtr> Measurements; // DataPublisher does not need to assign |