History

TimeSeriesPlatformLibrary: Added temporal support options to DataPublisher interfaces.

J. Ritchie Carroll committed 7 years ago

Commit 5c5b4061 parent 04505f228 files · +603 −300Browse files

Showing the changes to Source/Libraries (26 of 28 files). Show all

Unified · Split

26 files
  1. Source/Libraries/TimeSeriesPlatformLibrary/Common/CommonTypes.cpp +12 −8
  2. Source/Libraries/TimeSeriesPlatformLibrary/Common/CommonTypes.h +21 −9
  3. Source/Libraries/TimeSeriesPlatformLibrary/Common/Convert.cpp +49 −17
  4. Source/Libraries/TimeSeriesPlatformLibrary/Common/Convert.h +12 −8
  5. Source/Libraries/TimeSeriesPlatformLibrary/Common/Nullable.h +1 −1
  6. Source/Libraries/TimeSeriesPlatformLibrary/Data/DataRow.cpp +7 −7
  7. Source/Libraries/TimeSeriesPlatformLibrary/Data/DataRow.h +4 −4
  8. Source/Libraries/TimeSeriesPlatformLibrary/FilterExpressions/ExpressionTree.cpp +9 −9
  9. Source/Libraries/TimeSeriesPlatformLibrary/FilterExpressions/ExpressionTree.h +2 −2
  10. Source/Libraries/TimeSeriesPlatformLibrary/FilterExpressions/FilterExpressionParser.cpp +1 −1
  11. Source/Libraries/TimeSeriesPlatformLibrary/Samples/AdvancedPublish.cpp +35 −11
  12. Source/Libraries/TimeSeriesPlatformLibrary/Samples/FilterExpressionTests.cpp +9 −9
  13. Source/Libraries/TimeSeriesPlatformLibrary/Samples/SimplePublish.cpp +9 −9
  14. Source/Libraries/TimeSeriesPlatformLibrary/Samples/SubscriberHandler.cpp +1 −1
  15. Source/Libraries/TimeSeriesPlatformLibrary/Samples/SubscriberHandler.h +1 −1
  16. Source/Libraries/TimeSeriesPlatformLibrary/Transport/DataPublisher.cpp +94 −34
  17. Source/Libraries/TimeSeriesPlatformLibrary/Transport/DataPublisher.h +34 −13
  18. Source/Libraries/TimeSeriesPlatformLibrary/Transport/DataSubscriber.h +10 −9
  19. Source/Libraries/TimeSeriesPlatformLibrary/Transport/PublisherInstance.cpp +32 −8
  20. Source/Libraries/TimeSeriesPlatformLibrary/Transport/PublisherInstance.h +8 −4
  21. Source/Libraries/TimeSeriesPlatformLibrary/Transport/SubscriberConnection.cpp +114 −58
  22. Source/Libraries/TimeSeriesPlatformLibrary/Transport/SubscriberConnection.h +48 −5
  23. Source/Libraries/TimeSeriesPlatformLibrary/Transport/SubscriberInstance.cpp +4 −4
  24. Source/Libraries/TimeSeriesPlatformLibrary/Transport/SubscriberInstance.h +1 −1
  25. Source/Libraries/TimeSeriesPlatformLibrary/Transport/TransportTypes.cpp +18 −1
  26. Source/Libraries/TimeSeriesPlatformLibrary/Transport/TransportTypes.h +5 −4
Source/Libraries/TimeSeriesPlatformLibrary/Common/CommonTypes.cpp+12−8
@@ -45,9 +45,13 @@ const decimal_t Decimal::DotNetMaxValue = decimal_t("792281625142643375935439503 · 44 unmodified lines
4545
4646 const decimal_t Decimal::DotNetMinValue = decimal_t("-79228162514264337593543950335");
4747
48+const datetime_t DateTime::MaxValue(max_date_time);
49+
50+const datetime_t DateTime::MinValue(min_date_time);
51+
4852 const string Empty::String {};
4953
50−const DateTime Empty::DateTime {};
54+const datetime_t Empty::DateTime {};
5155
5256 const Guid Empty::Guid = NilGuidGen();
5357
@@ -280,7 +284,7 @@ string GSF::PadRight(const string& value, uint32_t count, char padChar) · 226 unmodified lines
280284 return value;
281285 }
282286
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)
284288 {
285289 switch (interval)
286290 {
@@ -307,7 +311,7 @@ DateTime GSF::DateAdd(const DateTime& value, int32_t addValue, TimeInterval inte · 20 unmodified lines
307311 }
308312 }
309313
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)
311315 {
312316 if (interval < TimeInterval::Hour)
313317 {
@@ -345,7 +349,7 @@ int32_t GSF::DateDiff(const DateTime& startTime, const DateTime& endTime, TimeIn · 31 unmodified lines
345349 }
346350 }
347351
348−int32_t GSF::DatePart(const DateTime& value, TimeInterval interval)
352+int32_t GSF::DatePart(const datetime_t& value, TimeInterval interval)
349353 {
350354 static float64_t tickInterval = pow(10.0, time_duration::num_fractional_digits());
351355
@@ -376,12 +380,12 @@ int32_t GSF::DatePart(const DateTime& value, TimeInterval interval) · 24 unmodified lines
376380 }
377381 }
378382
379−DateTime GSF::Now()
383+datetime_t GSF::Now()
380384 {
381− return DateTime { microsec_clock::local_time() };
385+ return datetime_t { microsec_clock::local_time() };
382386 }
383387
384−DateTime GSF::UtcNow()
388+datetime_t GSF::UtcNow()
385389 {
386− return DateTime { microsec_clock::universal_time() };
390+ return datetime_t { microsec_clock::universal_time() };
387391 }
Source/Libraries/TimeSeriesPlatformLibrary/Common/CommonTypes.h+21−9
@@ -48,6 +48,9 @@ namespace GSF · 47 unmodified lines
4848 typedef double_t float64_t;
4949 typedef boost::multiprecision::cpp_dec_float_100 decimal_t;
5050
51+ // DateTime type
52+ typedef boost::posix_time::ptime datetime_t;
53+
5154 struct Int8
5255 {
5356 static const int8_t MaxValue = static_cast<int8_t>(127);
@@ -105,16 +108,26 @@ namespace GSF · 51 unmodified lines
105108 static const decimal_t DotNetMinValue;
106109 };
107110
111+ struct DateTime
112+ {
113+ static const datetime_t MaxValue;
114+ static const datetime_t MinValue;
115+ };
116+
108117 struct Ticks
109118 {
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+
110125 static const int64_t PerSecond = 10000000L;
111126 static const int64_t PerMillisecond = Ticks::PerSecond / 1000;
112127 static const int64_t PerMicrosecond = Ticks::PerSecond / 1000000;
113128 static const int64_t PerMinute = 60L * Ticks::PerSecond;
114129 static const int64_t PerHour = 60L * Ticks::PerMinute;
115130 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
118131 };
119132
120133 template<class T>
@@ -167,7 +180,6 @@ namespace GSF · 46 unmodified lines
167180
168181 typedef boost::any Object;
169182 typedef boost::uuids::uuid Guid;
170− typedef boost::posix_time::ptime DateTime;
171183 typedef boost::posix_time::time_duration TimeSpan;
172184 typedef boost::system::error_code ErrorCode;
173185 typedef boost::system::system_error SystemError;
@@ -192,7 +204,7 @@ namespace GSF · 18 unmodified lines
192204 struct Empty
193205 {
194206 static const std::string String;
195− static const GSF::DateTime DateTime;
207+ static const GSF::datetime_t DateTime;
196208 static const GSF::Guid Guid;
197209 static const GSF::Object Object;
198210 static const GSF::IPAddress IPAddress;
@@ -394,11 +406,11 @@ namespace GSF · 195 unmodified lines
394406 Millisecond
395407 };
396408
397− DateTime DateAdd(const DateTime& value, int32_t addValue, TimeInterval interval);
398− int32_t DateDiff(const DateTime& startTime, const DateTime& endTime, TimeInterval interval);
399− int32_t DatePart(const DateTime& value, TimeInterval interval);
400− DateTime Now();
401− DateTime UtcNow();
409+ datetime_t DateAdd(const datetime_t& value, int32_t addValue, TimeInterval interval);
410+ int32_t DateDiff(const datetime_t& startTime, const datetime_t& endTime, TimeInterval interval);
411+ int32_t DatePart(const datetime_t& value, TimeInterval interval);
412+ datetime_t Now();
413+ datetime_t UtcNow();
402414 }
403415
404416 // Setup standard hash code for Guid
Source/Libraries/TimeSeriesPlatformLibrary/Common/Convert.cpp+49−17
@@ -40,7 +40,7 @@ using namespace boost::posix_time; · 39 unmodified lines
4040 using namespace boost::gregorian;
4141 using namespace GSF;
4242
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));
4444 const auto DateTimeTicksPerSecond = time_duration::ticks_per_second();
4545
4646 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
161161 milliseconds = static_cast<uint16_t>(ticks / 10000 % 1000);
162162 }
163163
164−DateTime GSF::FromUnixTime(time_t unixSOC, uint16_t milliseconds)
164+datetime_t GSF::FromUnixTime(time_t unixSOC, uint16_t milliseconds)
165165 {
166166 return from_time_t(unixSOC) + boost::posix_time::milliseconds(milliseconds);
167167 }
168168
169−DateTime GSF::FromTicks(const int64_t ticks)
169+datetime_t GSF::FromTicks(const int64_t ticks)
170170 {
171171 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);
173173 const int64_t pticks = int64_t(ticks % Ticks::PerSecond / tickInterval * DateTimeTicksPerSecond);
174174 return time + time_duration(0, 0, 0, pticks % DateTimeTicksPerSecond);
175175 }
176176
177−int64_t GSF::ToTicks(const DateTime& time)
177+int64_t GSF::ToTicks(const datetime_t& time)
178178 {
179179 static float64_t tickInterval = pow(10.0, time_duration::num_fractional_digits());
180180 const time_duration offset = time - DateTimeEpoch;
181− return Ticks::PtimeBaseOffset + offset.total_seconds() * Ticks::PerSecond +
181+ return Ticks::PTimeBaseOffset + offset.total_seconds() * Ticks::PerSecond +
182182 int64_t(offset.fractional_seconds() / tickInterval * Ticks::PerSecond);
183183 }
184184
@@ -240,9 +240,9 @@ uint32_t GSF::TicksToString(char* ptr, uint32_t maxsize, string format, int64_t · 55 unmodified lines
240240 return strftime(ptr, maxsize, formatStream.str().data(), &timeinfo);
241241 }
242242
243−DateTime GSF::LocalFromUtc(const DateTime& timestamp)
243+datetime_t GSF::LocalFromUtc(const datetime_t& timestamp)
244244 {
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);
246246 }
247247
248248 std::string GSF::ToString(const Guid& value)
@@ -250,7 +250,7 @@ std::string GSF::ToString(const Guid& value) · 1 unmodified line
250250 return boost::uuids::to_string(value);
251251 }
252252
253−std::string GSF::ToString(const DateTime& value, const char* format)
253+std::string GSF::ToString(const datetime_t& value, const char* format)
254254 {
255255 stringstream stream;
256256 time_facet* facet = new time_facet(format);
@@ -309,7 +309,35 @@ bool GSF::ParseBoolean(const string& value) · 52 unmodified lines
309309 return false;
310310 }
311311
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)
313341 {
314342 try
315343 {
@@ -318,6 +346,7 @@ bool GSF::TryParseDouble(const string& value, float64_t& result) · 2 unmodified lines
318346 }
319347 catch (...)
320348 {
349+ result = defaultValue;
321350 return false;
322351 }
323352 }
@@ -428,8 +457,8 @@ const char* GSF::Coalesce(const char* data, const char* nonEmptyValue) · 104 unmodified lines
428457 return data;
429458 }
430459
431−// 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)
460+// Attempt to parse a timestamp string, e.g.: 2018-03-14T19:23:11.665-04:00
461+bool GSF::TryParseTimestamp(const char* time, datetime_t& timestamp, const datetime_t& defaultValue, bool parseAsUTC)
433462 {
434463 static const locale formats[] = {
435464 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
438467
439468 static const int32_t formatsCount = sizeof(formats) / sizeof(formats[0]);
440469
470+ time_duration utcOffset{};
471+ const string cleanTimestamp = PreparseTimestamp(time, utcOffset);
472+
441473 for (int32_t i = 0; i < formatsCount; i++)
442474 {
443− time_duration utcOffset{};
444− istringstream stream(PreparseTimestamp(time, utcOffset));
475+ istringstream stream(cleanTimestamp);
445476
446477 stream.imbue(formats[i]);
447478 stream >> timestamp;
@@ -455,14 +486,15 @@ bool GSF::TryParseTimestamp(const char* time, DateTime& timestamp, bool parseAsU · 7 unmodified lines
455486 }
456487 }
457488
489+ timestamp = defaultValue;
458490 return false;
459491 }
460492
461−DateTime GSF::ParseTimestamp(const char* time, bool parseAsUTC)
493+datetime_t GSF::ParseTimestamp(const char* time, bool parseAsUTC)
462494 {
463− DateTime timestamp;
495+ datetime_t timestamp;
464496
465− if (TryParseTimestamp(time, timestamp, parseAsUTC))
497+ if (TryParseTimestamp(time, timestamp, DateTime::MinValue, parseAsUTC))
466498 return timestamp;
467499
468500 throw runtime_error("Failed to parse timestamp \"" + string(time) + "\"");
Source/Libraries/TimeSeriesPlatformLibrary/Common/Convert.h+12−8
@@ -32,18 +32,18 @@ namespace GSF · 31 unmodified lines
3232 void ToUnixTime(int64_t ticks, time_t& unixSOC, uint16_t& milliseconds);
3333
3434 // 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);
3636
3737 // Converts a timestamp, in Ticks, to DateTime
38− DateTime FromTicks(int64_t ticks);
38+ datetime_t FromTicks(int64_t ticks);
3939
4040 // Converts a DateTime to Ticks
41− int64_t ToTicks(const DateTime& time);
41+ int64_t ToTicks(const datetime_t& time);
4242
4343 // Thin wrapper around strftime to provide formats for milliseconds (%f) and full-resolution ticks (%t)
4444 uint32_t TicksToString(char* ptr, uint32_t maxsize, std::string format, int64_t ticks);
4545
46− DateTime LocalFromUtc(const DateTime& timestamp);
46+ datetime_t LocalFromUtc(const datetime_t& timestamp);
4747
4848 // Converts an object to a string
4949 template<class T>
@@ -56,7 +56,7 @@ namespace GSF · 6 unmodified lines
5656
5757 std::string ToString(const Guid& value);
5858
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");
6060
6161 std::string ToString(const TimeSpan& value);
6262
@@ -74,7 +74,11 @@ namespace GSF · 11 unmodified lines
7474
7575 bool ParseBoolean(const std::string& value);
7676
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);
7882
7983 // Encodes a character value into an escaped RegEx value
8084 std::string RegExEncode(char value);
@@ -88,10 +92,10 @@ namespace GSF · 7 unmodified lines
8892 const char* Coalesce(const char* data, const char* nonEmptyValue);
8993
9094 // 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);
9296
9397 // 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);
9599
96100 // Parses a string of key/value pairs into a case-insensitive string dictionary
97101 StringMap<std::string> ParseKeyValuePairs(const std::string& value, char parameterDelimiter = ';', char keyValueDelimiter = '=', char startValueDelimiter = '{', char endValueDelimiter = '}');
Source/Libraries/TimeSeriesPlatformLibrary/Common/Nullable.h+1−1
@@ -324,7 +324,7 @@ namespace GSF · 323 unmodified lines
324324 return {};
325325 }
326326
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")
328328 {
329329 if (value.HasValue())
330330 return ToString(value.GetValueOrDefault(), fmt);
Source/Libraries/TimeSeriesPlatformLibrary/Data/DataRow.cpp+7−7
@@ -394,7 +394,7 @@ Object DataRow::GetComputedValue(const DataColumnPtr& column, DataType targetTyp · 393 unmodified lines
394394 }
395395 case ExpressionValueType::DateTime:
396396 {
397− const DateTime result = sourceValue->ValueAsDateTime();
397+ const datetime_t result = sourceValue->ValueAsDateTime();
398398 const time_t value = to_time_t(result);
399399
400400 switch (targetType)
@@ -690,22 +690,22 @@ void DataRow::SetBooleanValue(const string& columnName, const Nullable<bool>& va · 289 unmodified lines
690690 SetBooleanValue(GetColumnIndex(columnName), value);
691691 }
692692
693−Nullable<DateTime> DataRow::ValueAsDateTime(const int32_t columnIndex)
693+Nullable<datetime_t> DataRow::ValueAsDateTime(const int32_t columnIndex)
694694 {
695− return GetValue<DateTime>(columnIndex, DataType::DateTime);
695+ return GetValue<datetime_t>(columnIndex, DataType::DateTime);
696696 }
697697
698−Nullable<DateTime> DataRow::ValueAsDateTime(const string& columnName)
698+Nullable<datetime_t> DataRow::ValueAsDateTime(const string& columnName)
699699 {
700700 return ValueAsDateTime(GetColumnIndex(columnName));
701701 }
702702
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)
704704 {
705− SetValue<DateTime>(columnIndex, value, DataType::DateTime);
705+ SetValue<datetime_t>(columnIndex, value, DataType::DateTime);
706706 }
707707
708−void DataRow::SetDateTimeValue(const string& columnName, const Nullable<DateTime>& value)
708+void DataRow::SetDateTimeValue(const string& columnName, const Nullable<datetime_t>& value)
709709 {
710710 SetDateTimeValue(GetColumnIndex(columnName), value);
711711 }
Source/Libraries/TimeSeriesPlatformLibrary/Data/DataRow.h+4−4
@@ -87,10 +87,10 @@ namespace Data · 86 unmodified lines
8787 void SetBooleanValue(int32_t columnIndex, const GSF::Nullable<bool>& value);
8888 void SetBooleanValue(const std::string& columnName, const GSF::Nullable<bool>& value);
8989
90− GSF::Nullable<GSF::DateTime> ValueAsDateTime(int32_t columnIndex);
91− GSF::Nullable<GSF::DateTime> ValueAsDateTime(const std::string& columnName);
92− void SetDateTimeValue(int32_t columnIndex, const GSF::Nullable<GSF::DateTime>& value);
93− void SetDateTimeValue(const std::string& columnName, const GSF::Nullable<GSF::DateTime>& value);
90+ GSF::Nullable<GSF::datetime_t> ValueAsDateTime(int32_t columnIndex);
91+ GSF::Nullable<GSF::datetime_t> ValueAsDateTime(const std::string& columnName);
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_t>& value);
9494
9595 GSF::Nullable<GSF::float32_t> ValueAsSingle(int32_t columnIndex);
9696 GSF::Nullable<GSF::float32_t> ValueAsSingle(const std::string& columnName);
Source/Libraries/TimeSeriesPlatformLibrary/FilterExpressions/ExpressionTree.cpp+9−9
@@ -387,24 +387,24 @@ Nullable<Guid> ValueExpression::ValueAsNullableGuid() const · 386 unmodified lines
387387 return Cast<Guid>(Value);
388388 }
389389
390−DateTime ValueExpression::ValueAsDateTime() const
390+datetime_t ValueExpression::ValueAsDateTime() const
391391 {
392392 ValidateValueType(ExpressionValueType::DateTime);
393393
394394 if (ValueIsNullable)
395− return Cast<Nullable<DateTime>>(Value).GetValueOrDefault();
395+ return Cast<Nullable<datetime_t>>(Value).GetValueOrDefault();
396396
397− return Cast<DateTime>(Value);
397+ return Cast<datetime_t>(Value);
398398 }
399399
400−Nullable<DateTime> ValueExpression::ValueAsNullableDateTime() const
400+Nullable<datetime_t> ValueExpression::ValueAsNullableDateTime() const
401401 {
402402 ValidateValueType(ExpressionValueType::DateTime);
403403
404404 if (ValueIsNullable)
405− return Cast<Nullable<DateTime>>(Value);
405+ return Cast<Nullable<datetime_t>>(Value);
406406
407− return Cast<DateTime>(Value);
407+ return Cast<datetime_t>(Value);
408408 }
409409
410410 UnaryExpression::UnaryExpression(const ExpressionUnaryType unaryType, ExpressionPtr value) :
@@ -1626,7 +1626,7 @@ ValueExpressionPtr ExpressionTree::IsDate(const ValueExpressionPtr& testValue) c · 1215 unmodified lines
16261626 if (testValue->ValueType == ExpressionValueType::DateTime)
16271627 return ExpressionTree::True;
16281628
1629− DateTime timestamp;
1629+ datetime_t timestamp;
16301630
16311631 if (testValue->ValueType == ExpressionValueType::String && TryParseTimestamp(testValue->ValueAsString().c_str(), timestamp))
16321632 return ExpressionTree::True;
@@ -3119,7 +3119,7 @@ ValueExpressionPtr ExpressionTree::Convert(const ValueExpressionPtr& sourceValue · 1486 unmodified lines
31193119 }
31203120 case ExpressionValueType::DateTime:
31213121 {
3122− const DateTime result = sourceValue->ValueAsDateTime();
3122+ const datetime_t result = sourceValue->ValueAsDateTime();
31233123 const time_t value = to_time_t(result);
31243124
31253125 switch (targetValueType)
@@ -3236,7 +3236,7 @@ ValueExpressionPtr ExpressionTree::NullValue(const ExpressionValueType targetVal · 110 unmodified lines
32363236 case ExpressionValueType::Guid:
32373237 return NewSharedPtr<ValueExpression>(ExpressionValueType::Guid, Nullable<Guid>(nullptr), true);
32383238 case ExpressionValueType::DateTime:
3239− return NewSharedPtr<ValueExpression>(ExpressionValueType::DateTime, Nullable<DateTime>(nullptr), true);
3239+ return NewSharedPtr<ValueExpression>(ExpressionValueType::DateTime, Nullable<datetime_t>(nullptr), true);
32403240 default:
32413241 return NewSharedPtr<ValueExpression>(ExpressionValueType::Undefined, nullptr);
32423242 }
Source/Libraries/TimeSeriesPlatformLibrary/FilterExpressions/ExpressionTree.h+2−2
@@ -127,8 +127,8 @@ namespace FilterExpressions · 126 unmodified lines
127127 GSF::Guid ValueAsGuid() const;
128128 Nullable<GSF::Guid> ValueAsNullableGuid() const;
129129
130− GSF::DateTime ValueAsDateTime() const;
131− Nullable<GSF::DateTime> ValueAsNullableDateTime() const;
130+ GSF::datetime_t ValueAsDateTime() const;
131+ Nullable<GSF::datetime_t> ValueAsNullableDateTime() const;
132132 };
133133
134134 typedef GSF::SharedPtr<ValueExpression> ValueExpressionPtr;
Source/Libraries/TimeSeriesPlatformLibrary/FilterExpressions/FilterExpressionParser.cpp+1−1
@@ -59,7 +59,7 @@ static GSF::Guid ParseGuidLiteral(string guidLiteral) · 58 unmodified lines
5959 return ParseGuid(guidLiteral.c_str());
6060 }
6161
62−static DateTime ParseDateTimeLiteral(string time)
62+static datetime_t ParseDateTimeLiteral(string time)
6363 {
6464 // Remove any surrounding '#' symbols from date/time, ANTLR grammar already
6565 // ensures date/time starting with '#' symbol will also end with one
Source/Libraries/TimeSeriesPlatformLibrary/Samples/AdvancedPublish.cpp+35−11
@@ -33,21 +33,23 @@ using namespace GSF::TimeSeries::Transport; · 32 unmodified lines
3333 using namespace GSF::FilterExpressions;
3434
3535 DataPublisherPtr Publisher;
36−TimerPtr PublishTimer;
36+TimerPtr PublishTimer, CompletionTimer;
3737 vector<DeviceMetadataPtr> DevicesToPublish;
3838 vector<MeasurementMetadataPtr> MeasurementsToPublish;
3939 vector<PhasorMetadataPtr> PhasorsToPublish;
4040
4141 bool RunPublisher(uint16_t port);
42−void DisplayClientConnected(DataPublisher* source, const Guid& subscriberID, const string& connectionID);
43−void DisplayClientDisconnected(DataPublisher* source, const Guid& subscriberID, const string& connectionID);
42+void DisplayClientConnected(DataPublisher* source, const SubscriberConnectionPtr& connection);
43+void DisplayClientDisconnected(DataPublisher* source, const SubscriberConnectionPtr& connection);
4444 void DisplayStatusMessage(DataPublisher* source, const string& message);
4545 void DisplayErrorMessage(DataPublisher* source, const string& message);
46+void HandleTemporalSubscriptionRequested(DataPublisher* source, const SubscriberConnectionPtr& connection);
47+void HandleTemporalProcessingIntervalChangeRequested(DataPublisher* source, const SubscriberConnectionPtr& connection);
4648
4749 void LoadMetadataToPublish(vector<DeviceMetadataPtr>& deviceMetadata, vector<MeasurementMetadataPtr>& measurementMetadata, vector<PhasorMetadataPtr>& phasorMetadata)
4850 {
4951 DeviceMetadataPtr device1Metadata = NewSharedPtr<DeviceMetadata>();
50− const DateTime timestamp = UtcNow();
52+ const datetime_t timestamp = UtcNow();
5153
5254 // Add a device
5355 device1Metadata->Name = "Test PMU";
@@ -210,6 +212,11 @@ bool RunPublisher(uint16_t port) · 156 unmodified lines
210212 Publisher->RegisterClientDisconnectedCallback(&DisplayClientDisconnected);
211213 Publisher->RegisterStatusMessageCallback(&DisplayStatusMessage);
212214 Publisher->RegisterErrorMessageCallback(&DisplayErrorMessage);
215+ Publisher->RegisterTemporalSubscriptionRequestedCallback(&HandleTemporalSubscriptionRequested);
216+ Publisher->RegisterTemporalProcessingIntervalChangeRequestedCallback(&HandleTemporalProcessingIntervalChangeRequested);
217+
218+ // Enable temporal subscription support
219+ Publisher->SetSupportsTemporalSubscriptions(true);
213220
214221 // Load metadata to be used for publication
215222 LoadMetadataToPublish(DevicesToPublish, MeasurementsToPublish, PhasorsToPublish);
@@ -285,18 +292,18 @@ bool RunPublisher(uint16_t port) · 69 unmodified lines
285292 return running;
286293 }
287294
288−void DisplayClientConnected(DataPublisher* source, const Guid& subscriberID, const string& connectionID)
295+void DisplayClientConnected(DataPublisher* source, const SubscriberConnectionPtr& connection)
289296 {
290297 cout << ">> New Client Connected:" << endl;
291− cout << " Subscriber ID: " << ToString(subscriberID) << endl;
292− cout << " Connection ID: " << connectionID << endl << endl;
298+ cout << " Subscriber ID: " << ToString(connection->GetSubscriberID()) << endl;
299+ cout << " Connection ID: " << ToString(connection->GetConnectionID()) << endl << endl;
293300 }
294301
295−void DisplayClientDisconnected(DataPublisher* source, const Guid& subscriberID, const string& connectionID)
302+void DisplayClientDisconnected(DataPublisher* source, const SubscriberConnectionPtr& connection)
296303 {
297304 cout << ">> Client Disconnected:" << endl;
298− cout << " Subscriber ID: " << ToString(subscriberID) << endl;
299− cout << " Connection ID: " << connectionID << endl << endl;
305+ cout << " Subscriber ID: " << ToString(connection->GetSubscriberID()) << endl;
306+ cout << " Connection ID: " << ToString(connection->GetConnectionID()) << endl << endl;
300307 }
301308
302309 // 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
309316 void DisplayErrorMessage(DataPublisher* source, const string& message)
310317 {
311318 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+}
Source/Libraries/TimeSeriesPlatformLibrary/Samples/FilterExpressionTests.cpp+9−9
@@ -1177,42 +1177,42 @@ int main(int argc, char* argv[]) · 1176 unmodified lines
11771177 valueExpression = FilterExpressionParser::Evaluate(dataRow, "DateAdd('2019-02-04', 2, 'Month')");
11781178
11791179 assert(valueExpression->ValueType == ExpressionValueType::DateTime);
1180− assert(valueExpression->ValueAsDateTime() == DateTime(date(2019, 4, 4)));
1180+ assert(valueExpression->ValueAsDateTime() == datetime_t(date(2019, 4, 4)));
11811181 cout << "Test " << ++test << " succeeded..." << endl;
11821182
11831183 // Test 119
11841184 valueExpression = FilterExpressionParser::Evaluate(dataRow, "DateAdd(#1/31/2019#, 1, 'Day')");
11851185
11861186 assert(valueExpression->ValueType == ExpressionValueType::DateTime);
1187− assert(valueExpression->ValueAsDateTime() == DateTime(date(2019, 2, 1)));
1187+ assert(valueExpression->ValueAsDateTime() == datetime_t(date(2019, 2, 1)));
11881188 cout << "Test " << ++test << " succeeded..." << endl;
11891189
11901190 // Test 120
11911191 valueExpression = FilterExpressionParser::Evaluate(dataRow, "DateAdd(#2019-01-31#, 2, 'Week')");
11921192
11931193 assert(valueExpression->ValueType == ExpressionValueType::DateTime);
1194− assert(valueExpression->ValueAsDateTime() == DateTime(date(2019, 2, 14)));
1194+ assert(valueExpression->ValueAsDateTime() == datetime_t(date(2019, 2, 14)));
11951195 cout << "Test " << ++test << " succeeded..." << endl;
11961196
11971197 // Test 121
11981198 valueExpression = FilterExpressionParser::Evaluate(dataRow, "DateAdd(#2019-01-31#, 25, 'Hour')");
11991199
12001200 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)));
12021202 cout << "Test " << ++test << " succeeded..." << endl;
12031203
12041204 // Test 122
12051205 valueExpression = FilterExpressionParser::Evaluate(dataRow, "DateAdd(#2018-12-31 23:58#, 3, 'Minute')");
12061206
12071207 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)));
12091209 cout << "Test " << ++test << " succeeded..." << endl;
12101210
12111211 // Test 123
12121212 valueExpression = FilterExpressionParser::Evaluate(dataRow, "DateAdd('2019-01-1 00:59', 61, 'Second')");
12131213
12141214 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)));
12161216 cout << "Test " << ++test << " succeeded..." << endl;
12171217
12181218 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
12221222 valueExpression = FilterExpressionParser::Evaluate(dataRow, "DateAdd('2019-01-1 00:00:59.999', 2, 'Millisecond')");
12231223
12241224 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))));
12261226 cout << "Test " << ++test << " succeeded..." << endl;
12271227
12281228 // Test 125
12291229 valueExpression = FilterExpressionParser::Evaluate(dataRow, "DateAdd(#1/1/2019 0:0:1.029#, -FramesPerSecond, 'Millisecond')");
12301230
12311231 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))));
12331233 cout << "Test " << ++test << " succeeded..." << endl;
12341234
12351235 // Test 126
@@ -1429,7 +1429,7 @@ int main(int argc, char* argv[]) · 193 unmodified lines
14291429 cout << "Test " << ++test << " succeeded..." << endl;
14301430
14311431 // 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");
14331433 const int64_t ticks = ToTicks(time);
14341434
14351435 assert(ticks == 636854109250904770L);
Source/Libraries/TimeSeriesPlatformLibrary/Samples/SimplePublish.cpp+9−9
@@ -36,8 +36,8 @@ TimerPtr PublishTimer; · 35 unmodified lines
3636 vector<MeasurementMetadataPtr> MeasurementsToPublish;
3737
3838 bool RunPublisher(uint16_t port);
39−void DisplayClientConnected(DataPublisher* source, const Guid& subscriberID, const string& connectionID);
40−void DisplayClientDisconnected(DataPublisher* source, const Guid& subscriberID, const string& connectionID);
39+void DisplayClientConnected(DataPublisher* source, const SubscriberConnectionPtr& connection);
40+void DisplayClientDisconnected(DataPublisher* source, const SubscriberConnectionPtr& connection);
4141 void DisplayStatusMessage(DataPublisher* source, const string& message);
4242 void DisplayErrorMessage(DataPublisher* source, const string& message);
4343
@@ -168,18 +168,18 @@ bool RunPublisher(uint16_t port) · 124 unmodified lines
168168 return running;
169169 }
170170
171−void DisplayClientConnected(DataPublisher* source, const Guid& subscriberID, const string& connectionID)
171+void DisplayClientConnected(DataPublisher* source, const SubscriberConnectionPtr& connection)
172172 {
173173 cout << ">> New Client Connected:" << endl;
174− cout << " Subscriber ID: " << ToString(subscriberID) << endl;
175− cout << " Connection ID: " << connectionID << endl << endl;
174+ cout << " Subscriber ID: " << ToString(connection->GetSubscriberID()) << endl;
175+ cout << " Connection ID: " << ToString(connection->GetConnectionID()) << endl << endl;
176176 }
177177
178−void DisplayClientDisconnected(DataPublisher* source, const Guid& subscriberID, const string& connectionID)
178+void DisplayClientDisconnected(DataPublisher* source, const SubscriberConnectionPtr& connection)
179179 {
180180 cout << ">> Client Disconnected:" << endl;
181− cout << " Subscriber ID: " << ToString(subscriberID) << endl;
182− cout << " Connection ID: " << connectionID << endl << endl;
181+ cout << " Subscriber ID: " << ToString(connection->GetSubscriberID()) << endl;
182+ cout << " Connection ID: " << ToString(connection->GetConnectionID()) << endl << endl;
183183 }
184184
185185 // 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
192192 void DisplayErrorMessage(DataPublisher* source, const string& message)
193193 {
194194 cerr << message << endl << endl;
195−}
195+}
Source/Libraries/TimeSeriesPlatformLibrary/Samples/SubscriberHandler.cpp+1−1
@@ -104,7 +104,7 @@ void SubscriberHandler::DataStartTime(time_t unixSOC, uint16_t milliseconds) · 103 unmodified lines
104104 // TODO: This reports timestamp, time_t format, of very first received measurement (if useful)
105105 }
106106
107−void SubscriberHandler::DataStartTime(DateTime startTime)
107+void SubscriberHandler::DataStartTime(datetime_t startTime)
108108 {
109109 // TODO: This reports timestamp, ptime format, of very first received measurement (if useful)
110110 }
Source/Libraries/TimeSeriesPlatformLibrary/Samples/SubscriberHandler.h+1−1
@@ -41,7 +41,7 @@ protected: · 40 unmodified lines
4141 void StatusMessage(const std::string& message) override;
4242 void ErrorMessage(const std::string& message) override;
4343 void DataStartTime(time_t unixSOC, uint16_t milliseconds) override;
44− void DataStartTime(GSF::DateTime startTime) override;
44+ void DataStartTime(GSF::datetime_t startTime) override;
4545 void ReceivedMetadata(const std::vector<uint8_t>& payload) override;
4646 void ReceivedNewMeasurements(const std::vector<GSF::TimeSeries::MeasurementPtr>& measurements) override;
4747 void ParsedMetadata() override;
Source/Libraries/TimeSeriesPlatformLibrary/Transport/DataPublisher.cpp+94−34
@@ -36,24 +36,13 @@ using namespace GSF::FilterExpressions; · 35 unmodified lines
3636 using namespace GSF::TimeSeries;
3737 using namespace GSF::TimeSeries::Transport;
3838
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−
5139 DataPublisher::DataPublisher(const TcpEndPoint& endpoint) :
5240 m_nodeID(NewGuid()),
5341 m_securityMode(SecurityMode::None),
5442 m_allowMetadataRefresh(true),
5543 m_allowNaNValueFilter(true),
5644 m_forceNaNValueFilter(false),
45+ m_supportsTemporalSubscriptions(false),
5746 m_cipherKeyRotationPeriod(60000),
5847 m_userData(nullptr),
5948 m_disposing(false),
@@ -116,19 +105,21 @@ void DataPublisher::AcceptConnection(const SubscriberConnectionPtr& connection, · 56 unmodified lines
116105 m_subscriberConnectionsLock.unlock();
117106
118107 connection->Start();
119− DispatchClientConnected(connection->GetSubscriberID(), connection->GetConnectionID());
108+ DispatchClientConnected(connection);
120109 }
121110
122111 StartAccept();
123112 }
124113
114+void DataPublisher::ConnectionTerminated(const SubscriberConnectionPtr& connection)
115+{
116+ DispatchClientDisconnected(connection);
117+}
118+
125119 void DataPublisher::RemoveConnection(const SubscriberConnectionPtr& connection)
126120 {
127121 m_subscriberConnectionsLock.lock();
128−
129− if (m_subscriberConnections.erase(connection))
130− DispatchClientDisconnected(connection->GetSubscriberID(), connection->GetConnectionID());
131−
122+ m_subscriberConnections.erase(connection);
132123 m_subscriberConnectionsLock.unlock();
133124 }
134125
@@ -169,16 +160,28 @@ void DataPublisher::DispatchErrorMessage(const string& message) · 34 unmodified lines
169160 Dispatch(&ErrorMessageDispatcher, reinterpret_cast<const uint8_t*>(message.c_str()), 0, messageSize);
170161 }
171162
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)
173176 {
174− SubscriberConnectionInfo* data = new SubscriberConnectionInfo(subscriberID, connectionID);
175− Dispatch(&ClientConnectedDispatcher, reinterpret_cast<uint8_t*>(&data), 0, sizeof(SubscriberConnectionInfo**));
177+ SubscriberConnection* connectionReference = &*connection;
178+ Dispatch(&TemporalSubscriptionRequestedDispatcher, reinterpret_cast<uint8_t*>(&connectionReference), 0, sizeof(SubscriberConnection**));
176179 }
177180
178−void DataPublisher::DispatchClientDisconnected(const GSF::Guid& subscriberID, const std::string& connectionID)
181+void DataPublisher::DispatchTemporalProcessingIntervalChangeRequested(const SubscriberConnectionPtr& connection)
179182 {
180− SubscriberConnectionInfo* data = new SubscriberConnectionInfo(subscriberID, connectionID);
181− Dispatch(&ClientDisconnectedDispatcher, reinterpret_cast<uint8_t*>(&data), 0, sizeof(SubscriberConnectionInfo**));
183+ SubscriberConnection* connectionReference = &*connection;
184+ Dispatch(&TemporalProcessingIntervalChangeRequestedDispatcher, reinterpret_cast<uint8_t*>(&connectionReference), 0, sizeof(SubscriberConnection**));
182185 }
183186
184187 // 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
207210
208211 void DataPublisher::ClientConnectedDispatcher(DataPublisher* source, const vector<uint8_t>& buffer)
209212 {
210− SubscriberConnectionInfo* data = *reinterpret_cast<SubscriberConnectionInfo**>(const_cast<uint8_t*>(&buffer[0]));
213+ SubscriberConnection* connection = *reinterpret_cast<SubscriberConnection**>(const_cast<uint8_t*>(&buffer[0]));
211214
212215 if (source != nullptr)
213216 {
214217 const SubscriberConnectionCallback clientConnectedCallback = source->m_clientConnectedCallback;
215218
216219 if (clientConnectedCallback != nullptr)
217− clientConnectedCallback(source, data->SubscriberID, data->ConnectionID);
220+ clientConnectedCallback(source, connection->GetReference());
218221 }
219−
220− delete data;
221222 }
222223
223224 void DataPublisher::ClientDisconnectedDispatcher(DataPublisher* source, const std::vector<uint8_t>& buffer)
224225 {
225− SubscriberConnectionInfo* data = *reinterpret_cast<SubscriberConnectionInfo**>(const_cast<uint8_t*>(&buffer[0]));
226+ SubscriberConnection* connection = *reinterpret_cast<SubscriberConnection**>(const_cast<uint8_t*>(&buffer[0]));
226227
227228 if (source != nullptr)
228229 {
229230 const SubscriberConnectionCallback clientDisconnectedCallback = source->m_clientDisconnectedCallback;
230231
231232 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());
233249 }
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;
234259
235− delete data;
260+ if (temporalProcessingIntervalChangeRequestedCallback != nullptr)
261+ temporalProcessingIntervalChangeRequestedCallback(source, connection->GetReference());
262+ }
236263 }
237264
238265 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
683710
684711 void DataPublisher::PublishMeasurements(const vector<Measurement>& measurements)
685712 {
686− for (const auto& connection : m_subscriberConnections)
687− connection->PublishMeasurements(measurements);
713+ vector<MeasurementPtr> measurementPtrs;
714+
715+ measurementPtrs.reserve(measurements.size());
716+
717+ for (const auto& measurement : measurements)
718+ measurementPtrs.push_back(ToPtr(measurement));
719+
720+ PublishMeasurements(measurementPtrs);
688721 }
689722
690723 void DataPublisher::PublishMeasurements(const vector<MeasurementPtr>& measurements)
691724 {
725+ m_subscriberConnectionsLock.lock();
726+
692727 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();
694734 }
695735
696736 const GSF::Guid& DataPublisher::GetNodeID() const
@@ -743,6 +783,16 @@ void DataPublisher::SetNaNValueFilterForced(bool forced) · 46 unmodified lines
743783 m_forceNaNValueFilter = forced;
744784 }
745785
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+
746796 uint32_t DataPublisher::GetCipherKeyRotationPeriod() const
747797 {
748798 return m_cipherKeyRotationPeriod;
@@ -823,4 +873,14 @@ void DataPublisher::RegisterClientConnectedCallback(const SubscriberConnectionCa · 74 unmodified lines
823873 void DataPublisher::RegisterClientDisconnectedCallback(const SubscriberConnectionCallback& clientDisconnectedCallback)
824874 {
825875 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+}
Source/Libraries/TimeSeriesPlatformLibrary/Transport/DataPublisher.h+34−13
@@ -44,12 +44,13 @@ namespace Transport · 43 unmodified lines
4444 {
4545 class DataPublisher : public EnableSharedThisPtr<DataPublisher> // NOLINT
4646 {
47− private:
47+ public:
4848 // Function pointer types
4949 typedef std::function<void(DataPublisher*, const std::vector<uint8_t>&)> DispatcherFunction;
5050 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;
5252
53+ private:
5354 // Structure used to dispatch
5455 // callbacks on the callback thread.
5556 struct CallbackDispatcher
@@ -70,6 +71,7 @@ namespace Transport · 14 unmodified lines
7071 bool m_allowMetadataRefresh;
7172 bool m_allowNaNValueFilter;
7273 bool m_forceNaNValueFilter;
74+ bool m_supportsTemporalSubscriptions;
7375 uint32_t m_cipherKeyRotationPeriod;
7476 void* m_userData;
7577 bool m_disposing;
@@ -93,6 +95,7 @@ namespace Transport · 17 unmodified lines
9395 // Command channel handlers
9496 void StartAccept();
9597 void AcceptConnection(const SubscriberConnectionPtr& connection, const ErrorCode& error);
98+ void ConnectionTerminated(const SubscriberConnectionPtr& connection);
9699 void RemoveConnection(const SubscriberConnectionPtr& connection);
97100
98101 // Callbacks
@@ -100,19 +103,25 @@ namespace Transport · 1 unmodified line
100103 MessageCallback m_errorMessageCallback;
101104 SubscriberConnectionCallback m_clientConnectedCallback;
102105 SubscriberConnectionCallback m_clientDisconnectedCallback;
106+ SubscriberConnectionCallback m_temporalSubscriptionRequestedCallback;
107+ SubscriberConnectionCallback m_temporalProcessingIntervalChangeRequestedCallback;
103108
104109 // Dispatchers
105110 void Dispatch(const DispatcherFunction& function);
106111 void Dispatch(const DispatcherFunction& function, const uint8_t* data, uint32_t offset, uint32_t length);
107112 void DispatchStatusMessage(const std::string& message);
108113 void DispatchErrorMessage(const std::string& message);
109− void DispatchClientConnected(const GSF::Guid& subscriberID, const std::string& connectionID);
110− void DispatchClientDisconnected(const GSF::Guid& subscriberID, const std::string& connectionID);
114+ void DispatchClientConnected(const SubscriberConnectionPtr& connection);
115+ void DispatchClientDisconnected(const SubscriberConnectionPtr& connection);
116+ void DispatchTemporalSubscriptionRequested(const SubscriberConnectionPtr& connection);
117+ void DispatchTemporalProcessingIntervalChangeRequested(const SubscriberConnectionPtr& connection);
111118
112119 static void StatusMessageDispatcher(DataPublisher* source, const std::vector<uint8_t>& buffer);
113120 static void ErrorMessageDispatcher(DataPublisher* source, const std::vector<uint8_t>& buffer);
114121 static void ClientConnectedDispatcher(DataPublisher* source, const std::vector<uint8_t>& buffer);
115122 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);
116125 static int32_t GetColumnIndex(const GSF::Data::DataTablePtr& table, const std::string& columnName);
117126 public:
118127 // Creates a new instance of the data publisher.
@@ -123,14 +132,22 @@ namespace Transport · 4 unmodified lines
123132 // tied up by the publisher.
124133 ~DataPublisher();
125134
126− // Define metadata from existing metadata tables
135+ // Defines metadata from existing metadata records
127136 void DefineMetadata(const std::vector<DeviceMetadataPtr>& deviceMetadata, const std::vector<MeasurementMetadataPtr>& measurementMetadata, const std::vector<PhasorMetadataPtr>& phasorMetadata, int32_t versionNumber = 0);
128137
129− // Define metadata from an existing dataset
138+ // Defines metadata from an existing dataset
130139 void DefineMetadata(const GSF::Data::DataSetPtr& metadata);
131140
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.
132143 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'
133148 const GSF::Data::DataSetPtr& GetFilteringMetadata() const;
149+
150+ // Filters primary MeasurementDetail metadata returning values as measurement metadata records
134151 std::vector<MeasurementMetadataPtr> FilterMetadata(const std::string& filterExpression) const;
135152
136153 void PublishMeasurements(const std::vector<Measurement>& measurements);
@@ -154,6 +171,9 @@ namespace Transport · 17 unmodified lines
154171 bool IsNaNValueFilterForced() const;
155172 void SetNaNValueFilterForced(bool forced);
156173
174+ bool GetSupportsTemporalSubscriptions() const;
175+ void SetSupportsTemporalSubscriptions(bool value);
176+
157177 uint32_t GetCipherKeyRotationPeriod() const;
158178 void SetCipherKeyRotationPeriod(uint32_t period);
159179
@@ -169,17 +189,18 @@ namespace Transport · 9 unmodified lines
169189 // Callback registration
170190 //
171191 // Callback functions are defined with the following signatures:
172− // void ProcessStatusMessage(DataPublisher*, const string& message)
173− // void ProcessErrorMessage(DataPublisher*, const string& message)
174− // void ProcessClientConnected(DataPublisher*, const GSF::Guid& subscriberID, const string& connectionID);
175− // void ProcessClientDisconnected(DataPublisher*, const GSF::Guid& subscriberID, const string& connectionID);
192+ // void ProcessStatusMessage(DataPublisher* source, const string& message)
193+ // void ProcessErrorMessage(DataPublisher* source, const string& message)
194+ // void ProcessClientConnected(DataPublisher* source, const SubscriberConnectionPtr& connection);
195+ // void ProcessClientDisconnected(DataPublisher* source, const SubscriberConnectionPtr& connection);
196+ // void ProcessTemporalSubscriptionRequested(DataPublisher* source, const SubscriberConnectionPtr& connection);
197+ // void ProcessTemporalProcessingIntervalChangeRequested(DataPublisher* source, const SubscriberConnectionPtr& connection);
176198 void RegisterStatusMessageCallback(const MessageCallback& statusMessageCallback);
177199 void RegisterErrorMessageCallback(const MessageCallback& errorMessageCallback);
178200 void RegisterClientConnectedCallback(const SubscriberConnectionCallback& clientConnectedCallback);
179201 void RegisterClientDisconnectedCallback(const SubscriberConnectionCallback& clientDisconnectedCallback);
180−
181− //static std::string DecodeClientString(const SubscriberConnectionPtr& connection, const uint8_t* data, uint32_t offset, uint32_t length);
182− //static std::vector<uint8_t> EncodeClientString(const SubscriberConnectionPtr& connection, const std::string& value);
202+ void RegisterTemporalSubscriptionRequestedCallback(const SubscriberConnectionCallback& temporalSubscriptionRequestedCallback);
203+ void RegisterTemporalProcessingIntervalChangeRequestedCallback(const SubscriberConnectionCallback& temporalProcessingIntervalChangeRequestedCallback);
183204
184205 friend class SubscriberConnection;
185206 };
Source/Libraries/TimeSeriesPlatformLibrary/Transport/DataSubscriber.h+10−9
@@ -134,7 +134,7 @@ namespace Transport · 133 unmodified lines
134134
135135 class DataSubscriber // NOLINT
136136 {
137− private:
137+ public:
138138 // Function pointer types
139139 typedef std::function<void(DataSubscriber*, const std::vector<uint8_t>&)> DispatcherFunction;
140140 typedef std::function<void(DataSubscriber*, const std::string&)> MessageCallback;
@@ -144,6 +144,7 @@ namespace Transport · 3 unmodified lines
144144 typedef std::function<void(DataSubscriber*)> ConfigurationChangedCallback;
145145 typedef std::function<void(DataSubscriber*)> ConnectionTerminatedCallback;
146146
147+ private:
147148 // Structure used to dispatch
148149 // callbacks on the callback thread.
149150 struct CallbackDispatcher
@@ -263,14 +264,14 @@ namespace Transport · 113 unmodified lines
263264 // Callback registration
264265 //
265266 // Callback functions are defined with the following signatures:
266− // void ProcessStatusMessage(DataSubscriber*, const string& message)
267− // void ProcessErrorMessage(DataSubscriber*, const string& message)
268− // void ProcessDataStartTime(DataSubscriber*, int64_t startTime)
269− // void ProcessMetadata(DataSubscriber*, const vector<uint8_t>& metadata)
270− // void ProcessNewMeasurements(DataSubscriber*, const vector<MeasurementPtr>& newMeasurements)
271− // void ProcessProcessingComplete(DataSubscriber*, const string& message)
272− // void ProcessConfigurationChanged(DataSubscriber*)
273− // void ProcessConnectionTerminated(DataSubscriber*)
267+ // void ProcessStatusMessage(DataSubscriber* source, const string& message)
268+ // void ProcessErrorMessage(DataSubscriber* source, const string& message)
269+ // void ProcessDataStartTime(DataSubscriber* source, int64_t startTime)
270+ // void ProcessMetadata(DataSubscriber* source, const vector<uint8_t>& metadata)
271+ // void ProcessNewMeasurements(DataSubscriber* source, const vector<MeasurementPtr>& newMeasurements)
272+ // void ProcessProcessingComplete(DataSubscriber* source, const string& message)
273+ // void ProcessConfigurationChanged(DataSubscriber* source)
274+ // void ProcessConnectionTerminated(DataSubscriber* source)
274275 //
275276 // Metadata is provided to the user as zlib-compressed XML,
276277 // and must be decompressed and interpreted before it can be used.
Source/Libraries/TimeSeriesPlatformLibrary/Transport/PublisherInstance.cpp+32−8
@@ -57,16 +57,28 @@ void PublisherInstance::HandleErrorMessage(DataPublisher* source, const string& · 56 unmodified lines
5757 instance->ErrorMessage(message);
5858 }
5959
60−void PublisherInstance::HandleClientConnected(DataPublisher* source, const GSF::Guid& subscriberID, const std::string& connectionID)
60+void PublisherInstance::HandleClientConnected(DataPublisher* source, const SubscriberConnectionPtr& connection)
6161 {
6262 PublisherInstance* instance = static_cast<PublisherInstance*>(source->GetUserData());
63− instance->ClientConnected(subscriberID, connectionID);
63+ instance->ClientConnected(connection);
6464 }
6565
66−void PublisherInstance::HandleClientDisconnected(DataPublisher* source, const GSF::Guid& subscriberID, const std::string& connectionID)
66+void PublisherInstance::HandleClientDisconnected(DataPublisher* source, const SubscriberConnectionPtr& connection)
6767 {
6868 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);
7082 }
7183
7284 void PublisherInstance::StatusMessage(const string& message)
@@ -79,14 +91,24 @@ void PublisherInstance::ErrorMessage(const string& message) · 6 unmodified lines
7991 cerr << message << endl << endl;
8092 }
8193
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)
83105 {
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;
85107 }
86108
87−void PublisherInstance::ClientDisconnected(const GSF::Guid& subscriberID, const std::string& connectionID)
109+void PublisherInstance::TemporalProcessingIntervalChangeRequested(const SubscriberConnectionPtr& connection)
88110 {
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;
90112 }
91113
92114 void PublisherInstance::Initialize()
@@ -96,6 +118,8 @@ void PublisherInstance::Initialize() · 3 unmodified lines
96118 m_publisher.RegisterErrorMessageCallback(&HandleErrorMessage);
97119 m_publisher.RegisterClientConnectedCallback(&HandleClientConnected);
98120 m_publisher.RegisterClientDisconnectedCallback(&HandleClientDisconnected);
121+ m_publisher.RegisterTemporalSubscriptionRequestedCallback(&HandleTemporalSubscriptionRequested);
122+ m_publisher.RegisterTemporalProcessingIntervalChangeRequestedCallback(&HandleTemporalProcessingIntervalChangeRequested);
99123
100124 m_initialized = true;
101125 }
Source/Libraries/TimeSeriesPlatformLibrary/Transport/PublisherInstance.h+8−4
@@ -43,14 +43,18 @@ namespace Transport · 42 unmodified lines
4343 // Internal subscription event handlers
4444 static void HandleStatusMessage(DataPublisher* source, const std::string& message);
4545 static void HandleErrorMessage(DataPublisher* source, const std::string& message);
46− static void HandleClientConnected(DataPublisher* source, const Guid& subscriberID, const std::string& connectionID);
47− static void HandleClientDisconnected(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 SubscriberConnectionPtr& connection);
48+ static void HandleTemporalSubscriptionRequested(DataPublisher* source, const SubscriberConnectionPtr& connection);
49+ static void HandleTemporalProcessingIntervalChangeRequested(DataPublisher* source, const SubscriberConnectionPtr& connection);
4850
4951 protected:
5052 virtual void StatusMessage(const std::string& message); // Defaults output to cout
5153 virtual void ErrorMessage(const std::string& message); // Defaults output to cerr
52− virtual void ClientConnected(const Guid& subscriberID, const std::string& connectionID);
53− virtual void ClientDisconnected(const Guid& subscriberID, const std::string& connectionID);
54+ virtual void ClientConnected(const SubscriberConnectionPtr& connection);
55+ virtual void ClientDisconnected(const SubscriberConnectionPtr& connection);
56+ virtual void TemporalSubscriptionRequested(const SubscriberConnectionPtr& connection);
57+ virtual void TemporalProcessingIntervalChangeRequested(const SubscriberConnectionPtr& connection);
5458
5559 public:
5660 PublisherInstance(uint16_t port, bool ipV6);
Source/Libraries/TimeSeriesPlatformLibrary/Transport/SubscriberConnection.cpp+114−58
@@ -48,6 +48,9 @@ SubscriberConnection::SubscriberConnection(DataPublisherPtr parent, IOContext& c · 47 unmodified lines
4848 m_subscriberID(NewGuid()),
4949 m_operationalModes(OperationalModes::NoFlags),
5050 m_encoding(OperationalEncoding::UTF8),
51+ m_startTimeConstraint(DateTime::MaxValue),
52+ m_stopTimeConstraint(DateTime::MaxValue),
53+ m_processingInterval(-1),
5154 m_usePayloadCompression(false),
5255 m_useCompactMeasurementFormat(true),
5356 m_includeTime(true),
@@ -80,6 +83,11 @@ const DataPublisherPtr& SubscriberConnection::GetParent() const · 26 unmodified lines
8083 return m_parent;
8184 }
8285
86+SubscriberConnectionPtr SubscriberConnection::GetReference()
87+{
88+ return shared_from_this();
89+}
90+
8391 TcpSocket& SubscriberConnection::CommandChannelSocket()
8492 {
8593 return m_commandChannelSocket;
@@ -126,6 +134,43 @@ uint32_t SubscriberConnection::GetEncoding() const · 40 unmodified lines
126134 return m_encoding;
127135 }
128136
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+
129174 bool SubscriberConnection::GetUsePayloadCompression() const
130175 {
131176 return m_usePayloadCompression;
@@ -313,50 +358,7 @@ void SubscriberConnection::Stop() · 181 unmodified lines
313358 m_pingTimer.Stop();
314359 m_commandChannelSocket.shutdown(socket_base::shutdown_both);
315360 m_commandChannelSocket.cancel();
316− m_parent->RemoveConnection(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);
361+ m_parent->ConnectionTerminated(shared_from_this());
360362 }
361363
362364 void SubscriberConnection::PublishMeasurements(const vector<MeasurementPtr>& measurements)
@@ -402,6 +404,11 @@ void SubscriberConnection::PublishMeasurements(const vector<MeasurementPtr>& mea · 39 unmodified lines
402404 PublishDataPacket(packet, count);
403405 }
404406
407+void SubscriberConnection::CompleteTemporalSubscription()
408+{
409+ SendResponse(ServerResponse::ProcessingComplete, ServerCommand::Subscribe, ToString(m_parent->GetNodeID()));
410+}
411+
405412 void SubscriberConnection::HandleSubscribe(uint8_t* data, uint32_t length)
406413 {
407414 try
@@ -441,6 +448,24 @@ void SubscriberConnection::HandleSubscribe(uint8_t* data, uint32_t length) · 33 unmodified lines
441448 if (TryGetValue(settings, "requestNaNValueFilter", setting))
442449 SetIsNaNFiltered(ParseBoolean(setting));
443450
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+
444469 SetUsePayloadCompression(usePayloadCompression);
445470 SetUseCompactMeasurementFormat(useCompactMeasurementFormat);
446471
@@ -518,36 +543,48 @@ void SubscriberConnection::HandleSubscribe(uint8_t* data, uint32_t length) · 71 unmodified lines
518543 SetIsSubscribed(true);
519544 SendResponse(ServerResponse::Succeeded, ServerCommand::Subscribe, message);
520545 m_parent->DispatchStatusMessage(message);
546+
547+ if (GetIsTemporalSubscription())
548+ m_parent->DispatchTemporalSubscriptionRequested(shared_from_this());
521549 }
522550 else
523551 {
524− const string message = byteLength > 0 ?
552+ HandleSubscribeFailure(byteLength > 0 ?
525553 "Not enough buffer was provided to parse client data subscription." :
526− "Cannot initialize client data subscription without a connection string.";
527−
528− SendResponse(ServerResponse::Failed, ServerCommand::Subscribe, message);
529− m_parent->DispatchErrorMessage(message);
554+ "Cannot initialize client data subscription without a connection string.");
530555 }
531556 }
532557 }
533558 else
534559 {
535− const string message = "Not enough buffer was provided to parse client data subscription.";
536− SendResponse(ServerResponse::Failed, ServerCommand::Subscribe, message);
537− m_parent->DispatchErrorMessage(message);
560+ HandleSubscribeFailure("Not enough buffer was provided to parse client data subscription.");
538561 }
539562 }
540− catch (const std::exception& ex)
563+ catch (const PublisherException& ex)
541564 {
542− const string message = "Failed to process client data subscription due to exception: " + string(ex.what());
543− SendResponse(ServerResponse::Failed, ServerCommand::Subscribe, message);
544− m_parent->DispatchErrorMessage(message);
565+ HandleSubscribeFailure("Failed to process client data subscription due to exception: " + string(ex.what()));
566+ }
567+ catch (...)
568+ {
569+ HandleSubscribeFailure("Failed to process client data subscription due to exception: " + boost::current_exception_diagnostic_information(true));
545570 }
546571 }
547572
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+
548582 void SubscriberConnection::HandleUnsubscribe()
549583 {
550584 SetIsSubscribed(false);
585+
586+ if (GetIsTemporalSubscription())
587+ CompleteTemporalSubscription();
551588 }
552589
553590 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
559596 m_parent->DispatchStatusMessage("Received meta-data refresh request from " + GetConnectionID() + ", preparing response...");
560597
561598 StringMap<ExpressionTreePtr> filterExpressions;
562− const DateTime startTime = UtcNow(); //-V821
599+ const datetime_t startTime = UtcNow(); //-V821
563600
564601 try
565602 {
@@ -622,8 +659,22 @@ void SubscriberConnection::HandleRotateCipherKeys() · 56 unmodified lines
622659 {
623660 }
624661
625−void SubscriberConnection::HandleUpdateProcessingInterval(uint8_t* data, uint32_t length)
662+void SubscriberConnection::HandleUpdateProcessingInterval(const uint8_t* data, uint32_t length)
626663 {
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+ }
627678 }
628679
629680 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
10561107 }
10571108 }
10581109
1110+bool SubscriberConnection::SendResponse(uint8_t responseCode, uint8_t commandCode)
1111+{
1112+ return SendResponse(responseCode, commandCode, vector<uint8_t>(0));
1113+}
1114+
10591115 bool SubscriberConnection::SendResponse(uint8_t responseCode, uint8_t commandCode, const string& message)
10601116 {
10611117 return SendResponse(responseCode, commandCode, EncodeString(message));
Source/Libraries/TimeSeriesPlatformLibrary/Transport/SubscriberConnection.h+48−5
@@ -37,6 +37,9 @@ namespace Transport · 36 unmodified lines
3737 class DataPublisher;
3838 typedef GSF::SharedPtr<DataPublisher> DataPublisherPtr;
3939
40+ class SubscriberConnection;
41+ typedef GSF::SharedPtr<SubscriberConnection> SubscriberConnectionPtr;
42+
4043 // Represents a subscriber connection to a data publisher
4144 class SubscriberConnection : public GSF::EnableSharedThisPtr<SubscriberConnection> // NOLINT
4245 {
@@ -49,6 +52,9 @@ namespace Transport · 6 unmodified lines
4952 std::string m_subscriptionInfo;
5053 uint32_t m_operationalModes;
5154 uint32_t m_encoding;
55+ GSF::datetime_t m_startTimeConstraint;
56+ GSF::datetime_t m_stopTimeConstraint;
57+ int32_t m_processingInterval;
5258 bool m_usePayloadCompression;
5359 bool m_useCompactMeasurementFormat;
5460 bool m_includeTime;
@@ -79,17 +85,18 @@ namespace Transport · 24 unmodified lines
7985 SignalIndexCachePtr m_signalIndexCache;
8086 int32_t m_timeIndex;
8187 int64_t m_baseTimeOffsets[2];
82− DateTime m_lastPublishTime;
88+ datetime_t m_lastPublishTime;
8389 //TSSCMeasurementParser m_tsscMeasurementParser;
8490 //bool m_tsscResetRequested;
8591 //uint16_t m_tsscSequenceNumber;
8692
8793 // Server request handlers
8894 void HandleSubscribe(uint8_t* data, uint32_t length);
95+ void HandleSubscribeFailure(const std::string& message);
8996 void HandleUnsubscribe();
9097 void HandleMetadataRefresh(uint8_t* data, uint32_t length);
9198 void HandleRotateCipherKeys();
92− void HandleUpdateProcessingInterval(uint8_t* data, uint32_t length);
99+ void HandleUpdateProcessingInterval(const uint8_t* data, uint32_t length);
93100 void HandleDefineOperationalModes(uint8_t* data, uint32_t length);
94101 void HandleConfirmNotification(uint8_t* data, uint32_t length);
95102 void HandleConfirmBufferBlock(uint8_t* data, uint32_t length);
@@ -112,43 +119,78 @@ namespace Transport · 16 unmodified lines
112119 ~SubscriberConnection();
113120
114121 const DataPublisherPtr& GetParent() const;
122+ SubscriberConnectionPtr GetReference();
115123
116124 GSF::TcpSocket& CommandChannelSocket();
117125
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
119127 const GSF::Guid& GetSubscriberID() const;
120128 void SetSubscriberID(const GSF::Guid& id);
121129
130+ // Gets subscriber connection identification, e.g., remote IP/port, for display and logging references
122131 const std::string& GetConnectionID() const;
132+
133+ // Gets subscriber remote IP address
123134 const GSF::IPAddress& GetIPAddress() const;
135+
136+ // Gets subscriber communications port
124137 const std::string& GetHostName() const;
125138
139+ // Gets or sets established subscriber operational modes
126140 uint32_t GetOperationalModes() const;
127141 void SetOperationalModes(uint32_t value);
128142
143+ // Gets established subscriber string encoding
129144 uint32_t GetEncoding() const;
130145
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
131165 bool GetUsePayloadCompression() const;
132166 void SetUsePayloadCompression(bool value);
133167
168+ // Gets or sets flag that determines if the compact measurement format should be used in data packets
134169 bool GetUseCompactMeasurementFormat() const;
135170 void SetUseCompactMeasurementFormat(bool value);
136171
172+ // Gets or sets flag that determines if time should be included in data packets when the compact measurement format used
137173 bool GetIncludeTime() const;
138174 void SetIncludeTime(bool value);
139175
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
140178 bool GetUseMillisecondResolution() const;
141179 void SetUseMillisecondResolution(bool value);
142180
181+ // Gets or sets flag that determines if NaN values should be excluded from data packets
143182 bool GetIsNaNFiltered() const;
144183 void SetIsNaNFiltered(bool value);
145184
185+ // Gets or sets flag that determines if subscriber connection is currently subscribed
146186 bool GetIsSubscribed() const;
147187 void SetIsSubscribed(bool value);
148188
189+ // Gets or sets subscription details about subscriber
149190 const std::string& GetSubscriptionInfo() const;
150191 void SetSubscriptionInfo(const std::string& value);
151192
193+ // Gets or sets signal index cache for subscriber representing run-time mappings for subscribed points
152194 const SignalIndexCachePtr& GetSignalIndexCache() const;
153195 void SetSignalIndexCache(SignalIndexCachePtr signalIndexCache);
154196
@@ -164,15 +206,16 @@ namespace Transport · 9 unmodified lines
164206 void Start();
165207 void Stop();
166208
167− void PublishMeasurements(const std::vector<Measurement>& measurements);
168209 void PublishMeasurements(const std::vector<MeasurementPtr>& measurements);
210+ void CompleteTemporalSubscription();
169211
170212 void CommandChannelSendAsync(uint8_t* data, uint32_t offset, uint32_t length);
171213 void DataChannelSendAsync(uint8_t* data, uint32_t offset, uint32_t length);
172214 void WriteHandler(const ErrorCode& error, uint32_t bytesTransferred);
173215
216+ bool SendResponse(uint8_t responseCode, uint8_t commandCode);
174217 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);
176219
177220 std::string DecodeString(const uint8_t* data, uint32_t offset, uint32_t length) const;
178221 std::vector<uint8_t> EncodeString(const std::string& value) const;
Source/Libraries/TimeSeriesPlatformLibrary/Transport/SubscriberInstance.cpp+4−4
@@ -562,7 +562,7 @@ void SubscriberInstance::DataStartTime(time_t unixSOC, uint16_t milliseconds) · 561 unmodified lines
562562 {
563563 }
564564
565−void SubscriberInstance::DataStartTime(DateTime startTime)
565+void SubscriberInstance::DataStartTime(datetime_t startTime)
566566 {
567567 }
568568
@@ -876,7 +876,7 @@ void SubscriberInstance::ConstructConfigurationFrames(const StringMap<DeviceMeta · 307 unmodified lines
876876 phasorReference->Phasor->Type = "?";
877877 phasorReference->Phasor->Phase = "+";
878878 phasorReference->Phasor->SourceIndex = i;
879− phasorReference->Phasor->UpdatedOn = DateTime();
879+ phasorReference->Phasor->UpdatedOn = datetime_t();
880880
881881 phasorReference->Angle = nullptr;
882882 phasorReference->Magnitude = nullptr;
@@ -912,7 +912,7 @@ void SubscriberInstance::ConstructConfigurationFrames(const StringMap<DeviceMeta · 29 unmodified lines
912912 measurement->Reference.Kind = SignalKind::Analog;
913913 measurement->PhasorSourceIndex = 0U;
914914 measurement->Description = "";
915− measurement->UpdatedOn = DateTime();
915+ measurement->UpdatedOn = datetime_t();
916916
917917 configurationFrame->Analogs.push_back(measurement);
918918 }
@@ -946,7 +946,7 @@ void SubscriberInstance::ConstructConfigurationFrames(const StringMap<DeviceMeta · 27 unmodified lines
946946 measurement->Reference.Kind = SignalKind::Digital;
947947 measurement->PhasorSourceIndex = 0U;
948948 measurement->Description = "";
949− measurement->UpdatedOn = DateTime();
949+ measurement->UpdatedOn = datetime_t();
950950
951951 configurationFrame->Digitals.push_back(measurement);
952952 }
Source/Libraries/TimeSeriesPlatformLibrary/Transport/SubscriberInstance.h+1−1
@@ -79,7 +79,7 @@ namespace Transport · 78 unmodified lines
7979 virtual void StatusMessage(const std::string& message); // Defaults output to cout
8080 virtual void ErrorMessage(const std::string& message); // Defaults output to cerr
8181 virtual void DataStartTime(time_t unixSOC, uint16_t milliseconds);
82− virtual void DataStartTime(DateTime startTime);
82+ virtual void DataStartTime(datetime_t startTime);
8383 virtual void ReceivedMetadata(const std::vector<uint8_t>& payload);
8484 virtual void ParsedMetadata();
8585 virtual void ReceivedNewMeasurements(const std::vector<MeasurementPtr>& measurements);
Source/Libraries/TimeSeriesPlatformLibrary/Transport/TransportTypes.cpp+18−1
@@ -64,7 +64,7 @@ float64_t Measurement::AdjustedValue() const · 63 unmodified lines
6464 return Value * Multiplier + Adder;
6565 }
6666
67−DateTime Measurement::GetDateTime() const
67+datetime_t Measurement::GetDateTime() const
6868 {
6969 return FromTicks(Timestamp);
7070 }
@@ -74,6 +74,23 @@ void Measurement::GetUnixTime(time_t& unixSOC, uint16_t& milliseconds) const · 3 unmodified lines
7474 ToUnixTime(Timestamp, unixSOC, milliseconds);
7575 }
7676
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+
7794 SignalReference::SignalReference() :
7895 SignalID(Empty::Guid),
7996 Index(0),
Source/Libraries/TimeSeriesPlatformLibrary/Transport/TransportTypes.h+5−4
@@ -97,13 +97,14 @@ namespace TimeSeries · 96 unmodified lines
9797 float64_t AdjustedValue() const;
9898
9999 // Gets Timestamp as DateTime
100− DateTime GetDateTime() const;
100+ datetime_t GetDateTime() const;
101101
102102 // Gets Timestamp in Unix second of century and milliseconds
103103 void GetUnixTime(time_t& unixSOC, uint16_t& milliseconds) const;
104104 };
105105
106106 typedef SharedPtr<Measurement> MeasurementPtr;
107+ MeasurementPtr ToPtr(const Measurement& source);
107108
108109 enum SignalKind : int16_t
109110 {
@@ -162,7 +163,7 @@ namespace TimeSeries · 52 unmodified lines
162163 SignalReference Reference; // Parsed signal reference structure
163164 uint16_t PhasorSourceIndex; // Measurement phasor index, if measurement represents a "Phasor"
164165 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
166167 };
167168
168169 typedef SharedPtr<MeasurementMetadata> MeasurementMetadataPtr;
@@ -174,7 +175,7 @@ namespace TimeSeries · 5 unmodified lines
174175 std::string Type; // Phasor type, i.e., "V" for voltage or "I" for current
175176 std::string Phase; // Phasor phase, one of, "+", "-", "0", "A", "B" or "C"
176177 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
178179 };
179180
180181 typedef SharedPtr<PhasorMetadata> PhasorMetadataPtr;
@@ -202,7 +203,7 @@ namespace TimeSeries · 21 unmodified lines
202203 std::string VendorDeviceName; // Original vendor device name, e.g., PMU brand (if useful / provided)
203204 float64_t Longitude; // Device longitude (if reported)
204205 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
206207
207208 // Associated measurement and phasor meta-data
208209 std::vector<MeasurementMetadataPtr> Measurements; // DataPublisher does not need to assign

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t