Skip to content

Commit 294212c

Browse files
authored
Merge pull request #372 from mtconnect/mqtt_last_will_and_client_id_fix
2 parents ac6a13e + f72b6c7 commit 294212c

File tree

4 files changed

+146
-17
lines changed

4 files changed

+146
-17
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
set(AGENT_VERSION_MAJOR 2)
33
set(AGENT_VERSION_MINOR 2)
44
set(AGENT_VERSION_PATCH 0)
5-
set(AGENT_VERSION_BUILD 15)
5+
set(AGENT_VERSION_BUILD 16)
66
set(AGENT_VERSION_RC "")
77

88
# This minimum version is to support Visual Studio 2019 and C++ feature checking and FetchContent

src/mtconnect/mqtt/mqtt_client_impl.hpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
#include <boost/uuid/name_generator_sha1.hpp>
2222

2323
#include <inttypes.h>
24+
#include <random>
25+
#include <chrono>
26+
2427
#include <mqtt/async_client.hpp>
2528
#include <mqtt/setup_log.hpp>
2629

@@ -92,17 +95,14 @@ namespace mtconnect {
9295
}
9396
else
9497
{
98+
using namespace boost::uuids;
9599
std::stringstream identity;
96-
identity << '_' << m_host << '_' << m_port;
97-
98-
boost::uuids::detail::sha1 sha1;
99-
sha1.process_bytes(identity.str().c_str(), identity.str().length());
100-
boost::uuids::detail::sha1::digest_type digest;
101-
sha1.get_digest(digest);
100+
const auto now = std::chrono::high_resolution_clock::now();
102101

103-
identity.str("");
104-
identity << std::hex << digest[0] << digest[1] << digest[2];
105-
m_identity = std::string("_") + (identity.str()).substr(0, 10);
102+
auto seed = now.time_since_epoch().count();
103+
std::mt19937_64 gen(seed);
104+
identity << "mtc_" << std::hex << gen();
105+
m_identity = identity.str();
106106
}
107107

108108
LOG(debug) << "Using ClientID " << m_identity;

src/mtconnect/pipeline/json_mapper.cpp

Lines changed: 91 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ namespace mtconnect::pipeline {
4747
DATA_SET,
4848
TIMESTAMP,
4949
ASSET,
50+
VALUE_ERROR,
5051

5152
VALUE,
5253
KEY,
@@ -174,6 +175,60 @@ namespace mtconnect::pipeline {
174175
Forward m_forward;
175176
std::list<pair<DataItemPtr, entity::Properties>> m_queue;
176177
};
178+
179+
/// @brief consume value in case of error
180+
struct ErrorHandler : rj::BaseReaderHandler<rj::UTF8<>, ErrorHandler>
181+
{
182+
ErrorHandler(int depth = 0) : m_depth(depth) {}
183+
184+
bool Default()
185+
{
186+
return true;
187+
}
188+
bool Key(const Ch *str, rj::SizeType length, bool copy)
189+
{
190+
return true;
191+
}
192+
bool StartObject()
193+
{
194+
m_depth++;
195+
return true;
196+
}
197+
bool EndObject(rj::SizeType memberCount)
198+
{
199+
m_depth--;
200+
return true;
201+
}
202+
bool StartArray()
203+
{
204+
m_depth++;
205+
return true;
206+
}
207+
bool EndArray(rj::SizeType elementCount)
208+
{
209+
m_depth--;
210+
return true;
211+
}
212+
213+
bool operator()(rj::Reader &reader, rj::StringStream &buff)
214+
{
215+
LOG(warning) << "Consuming value due to error";
216+
217+
if (!reader.IterativeParseNext<rj::kParseNanAndInfFlag>(buff, *this))
218+
return false;
219+
220+
while (m_depth > 0 && !reader.IterativeParseComplete())
221+
{
222+
// Read the key
223+
if (!reader.IterativeParseNext<rj::kParseNanAndInfFlag>(buff, *this))
224+
return false;
225+
}
226+
227+
return true;
228+
}
229+
230+
int m_depth {0};
231+
};
177232

178233
/// @brief SAX Parser handler for JSON Parsing
179234
struct DataSetHandler : rj::BaseReaderHandler<rj::UTF8<>, DataSetHandler>
@@ -410,6 +465,7 @@ namespace mtconnect::pipeline {
410465

411466
bool StartArray()
412467
{
468+
m_depth++;
413469
if (m_dataItem->isTimeSeries() || m_dataItem->isThreeSpace())
414470
{
415471
auto &value = m_props["VALUE"];
@@ -420,11 +476,13 @@ namespace mtconnect::pipeline {
420476
else
421477
{
422478
LOG(warning) << "Unexpected vector type for data item " << m_dataItem->getId();
423-
return false;
479+
m_expectation = Expectation::VALUE_ERROR;
480+
return true;
424481
}
425482
}
426483
bool EndArray(rj::SizeType elementCount)
427484
{
485+
m_depth--;
428486
if (m_expectation == Expectation::VECTOR)
429487
{
430488
m_vector = nullptr;
@@ -434,7 +492,8 @@ namespace mtconnect::pipeline {
434492
else
435493
{
436494
LOG(warning) << "Unexpected vector type for data item " << m_dataItem->getId();
437-
return false;
495+
m_expectation = Expectation::VALUE_ERROR;
496+
return true;
438497
}
439498
}
440499

@@ -449,7 +508,8 @@ namespace mtconnect::pipeline {
449508
if (f == e)
450509
{
451510
LOG(warning) << "Unexpected key: " << sv << " for condition " << m_dataItem->getId();
452-
return false;
511+
m_expectation = Expectation::VALUE_ERROR;
512+
return true;
453513
}
454514
}
455515
else
@@ -470,7 +530,8 @@ namespace mtconnect::pipeline {
470530
else
471531
{
472532
LOG(warning) << "Unexpected key " << sv << " for data item " << m_dataItem->getId();
473-
return false;
533+
m_expectation = Expectation::VALUE_ERROR;
534+
return true;
474535
}
475536
m_key = sv;
476537
}
@@ -480,13 +541,15 @@ namespace mtconnect::pipeline {
480541

481542
bool StartObject()
482543
{
544+
m_depth++;
483545
m_object = true;
484546
m_expectation = Expectation::KEY;
485547
return true;
486548
}
487549

488550
bool EndObject(rj::SizeType memberCount)
489551
{
552+
m_depth--;
490553
m_done = true;
491554
return true;
492555
}
@@ -509,7 +572,15 @@ namespace mtconnect::pipeline {
509572

510573
if (!m_done)
511574
{
512-
if (m_expectation == Expectation::DATA_SET)
575+
if (m_expectation == Expectation::VALUE_ERROR)
576+
{
577+
ErrorHandler handler(m_depth);
578+
if (!handler(reader, buff))
579+
return false;
580+
m_props.clear();
581+
return true;
582+
}
583+
else if (m_expectation == Expectation::DATA_SET)
513584
{
514585
auto &value = m_props["VALUE"];
515586
DataSet &set = value.emplace<DataSet>();
@@ -547,6 +618,7 @@ namespace mtconnect::pipeline {
547618
bool m_object {false};
548619
std::string m_key;
549620
Expectation m_expectation {Expectation::NONE};
621+
int m_depth{0};
550622
};
551623

552624
struct TimestampHandler : rj::BaseReaderHandler<rj::UTF8<>, TimestampHandler>
@@ -690,7 +762,8 @@ namespace mtconnect::pipeline {
690762
bool Default()
691763
{
692764
LOG(warning) << "Expecting a key";
693-
return false;
765+
m_expectation = Expectation::VALUE_ERROR;
766+
return true;
694767
}
695768

696769
bool Key(const Ch *str, rj::SizeType length, bool copy)
@@ -789,6 +862,16 @@ namespace mtconnect::pipeline {
789862
m_expectation = Expectation::KEY;
790863
break;
791864
}
865+
866+
case Expectation::VALUE_ERROR:
867+
{
868+
ErrorHandler handler;
869+
if (!handler(reader, buff))
870+
return false;
871+
872+
m_expectation = Expectation::KEY;
873+
break;
874+
}
792875

793876
case Expectation::KEY:
794877
case Expectation::NONE:
@@ -802,7 +885,8 @@ namespace mtconnect::pipeline {
802885
PropertiesHandler props(m_dataItem, m_props);
803886
if (!props(reader, buff))
804887
return false;
805-
m_context.send(m_dataItem, m_props);
888+
if (m_props.size() > 0)
889+
m_context.send(m_dataItem, m_props);
806890
}
807891
else
808892
{

test_package/json_mapping_test.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,5 +1005,50 @@ TEST_F(JsonMappingTest, should_parse_xml_asset)
10051005
ASSERT_EQ("CuttingToolArchetype", asset->getName());
10061006
}
10071007

1008+
/// @test if observation is incorrect, skip levels
1009+
TEST_F(JsonMappingTest, should_skip_erroneous_values)
1010+
{
1011+
auto dev = makeDevice("Device", {{"id", "device"s}, {"name", "device"s}, {"uuid", "device"s}});
1012+
makeDataItem("device", {{"id", "a"s},
1013+
{"type", "EXECUTION"s},
1014+
{"category", "EVENT"s}});
1015+
makeDataItem("device", {{"id", "b"s},
1016+
{"type", "CONTROLLER_MODE"s},
1017+
{"category", "EVENT"s}});
1018+
1019+
1020+
Properties props {{"VALUE", R"(
1021+
{
1022+
"timestamp": "2023-11-09T11:20:00Z",
1023+
"a": {
1024+
"r1": {
1025+
"k1": 123.45
1026+
},
1027+
"r2": {
1028+
"k2": "ABCDEF",
1029+
"k3": 6789
1030+
}
1031+
},
1032+
"b": "MANUAL"
1033+
})"s}};
1034+
1035+
auto jmsg = std::make_shared<JsonMessage>("JsonMessage", props);
1036+
jmsg->m_device = dev;
1037+
1038+
auto res = (*m_mapper)(std::move(jmsg));
1039+
ASSERT_TRUE(res);
1040+
1041+
auto value = res->getValue();
1042+
ASSERT_TRUE(std::holds_alternative<EntityList>(value));
1043+
auto list = get<EntityList>(value);
1044+
ASSERT_EQ(1, list.size());
1045+
1046+
auto obs = dynamic_pointer_cast<Observation>(list.front());
1047+
ASSERT_TRUE(obs);
1048+
ASSERT_EQ("ControllerMode", obs->getName());
1049+
ASSERT_EQ("b", obs->getDataItem()->getId());
1050+
ASSERT_EQ("MANUAL", obs->getValue<string>());
1051+
}
1052+
10081053
/// @test verify the json mapper can an asset in json
10091054
TEST_F(JsonMappingTest, should_parse_json_asset) { GTEST_SKIP(); }

0 commit comments

Comments
 (0)