From ca7f783a8d22cddc7211ab8353db26e031e261e7 Mon Sep 17 00:00:00 2001 From: Will Sobel Date: Mon, 14 Nov 2022 23:33:59 +0100 Subject: [PATCH 1/6] Get schema version from config file and if not provided from device.xml. Detech changes in schema version from device.xml and restart agent if version do not match. --- CMakeLists.txt | 2 +- src/agent.cpp | 75 +++++-- src/agent.hpp | 7 +- src/configuration/agent_config.cpp | 47 +++-- src/parser/xml_parser.cpp | 40 ++-- src/parser/xml_parser.hpp | 6 +- src/printer/json_printer.cpp | 20 +- src/printer/json_printer.hpp | 3 +- src/printer/printer.hpp | 14 ++ src/printer/xml_printer.cpp | 18 +- src/printer/xml_printer.hpp | 6 +- src/sink/rest_sink/rest_service.cpp | 8 +- src/sink/rest_sink/rest_service.hpp | 2 +- src/version.h.in | 2 + test/config_test.cpp | 296 +++++++++++++++------------- 15 files changed, 333 insertions(+), 213 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 43dde6f84..030ede85a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ set(AGENT_VERSION_MAJOR 2) set(AGENT_VERSION_MINOR 0) set(AGENT_VERSION_PATCH 0) set(AGENT_VERSION_BUILD 12) -set(AGENT_VERSION_RC "_RC24") +set(AGENT_VERSION_RC "_RC25") # This minimum version is to support Visual Studio 2017 and C++ feature checking and FetchContent cmake_minimum_required(VERSION 3.16 FATAL_ERROR) diff --git a/src/agent.cpp b/src/agent.cpp index 34ad66ca6..70f421a47 100644 --- a/src/agent.cpp +++ b/src/agent.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -76,9 +77,8 @@ namespace mtconnect { m_context(context), m_strand(m_context), m_xmlParser(make_unique()), - m_version( - GetOption(options, config::SchemaVersion) - .value_or(to_string(AGENT_VERSION_MAJOR) + "." + to_string(AGENT_VERSION_MINOR))), + m_schemaVersion( + GetOption(options, config::SchemaVersion)), m_deviceXmlPath(deviceXmlPath), m_circularBuffer(GetOption(options, config::BufferSize).value_or(17), GetOption(options, config::CheckpointFrequency).value_or(1000)), @@ -102,8 +102,19 @@ namespace mtconnect { uint32_t(GetOption(options, mtconnect::configuration::JsonVersion).value_or(2)); // Create the Printers - m_printers["xml"] = make_unique(m_version, m_pretty); - m_printers["json"] = make_unique(jsonVersion, m_version, m_pretty); + m_printers["xml"] = make_unique(m_pretty); + m_printers["json"] = make_unique(jsonVersion, m_pretty); + + if (m_schemaVersion) + { + int major, minor; + char c; + stringstream vstr(*m_schemaVersion); + vstr >> major >> c >> minor; + m_version = major * 100 + minor; + for (auto &[k, pr] : m_printers) + pr->setSchemaVersion(*m_schemaVersion); + } } void Agent::initialize(pipeline::PipelineContextPtr context) @@ -114,17 +125,25 @@ namespace mtconnect { m_loopback = std::make_shared("AgentSource", m_strand, context, m_options); + loadXMLDeviceFile(m_deviceXmlPath); + if (!m_schemaVersion) + { + m_schemaVersion.emplace(to_string(AGENT_VERSION_MAJOR) + "." + to_string(AGENT_VERSION_MINOR)); + } + for (auto &[k, pr] : m_printers) + pr->setSchemaVersion(*m_schemaVersion); + int major, minor; char c; - stringstream vstr(m_version); + stringstream vstr(*m_schemaVersion); vstr >> major >> c >> minor; + m_version = major * 100 + minor; auto disableAgentDevice = GetOption(m_options, config::DisableAgentDevice); - if (!(disableAgentDevice && *disableAgentDevice) && (major > 1 || (major == 1 && minor >= 7))) + if (!(disableAgentDevice && *disableAgentDevice) && m_version >= 107) { createAgentDevice(); } - loadXMLDeviceFile(m_deviceXmlPath); loadCachedProbe(); m_initialized = true; @@ -277,13 +296,22 @@ namespace mtconnect { } } - void Agent::reloadDevices(const std::string &deviceFile) + bool Agent::reloadDevices(const std::string &deviceFile) { try { // Load the configuration for the Agent auto devices = m_xmlParser->parseFile( deviceFile, dynamic_cast(m_printers["xml"].get())); + + if (m_xmlParser->getSchemaVersion() && + m_xmlParser->getVersion() != m_version) + { + LOG(info) << "Got version: " << *(m_xmlParser->getSchemaVersion()); + LOG(warning) << "Schema version does not match agent schema version, restarting the agent"; + return false; + } + // Fir the DeviceAdded event for each device bool changed = false; @@ -293,6 +321,8 @@ namespace mtconnect { } if (changed) loadCachedProbe(); + + return true; } catch (runtime_error &e) { @@ -430,7 +460,7 @@ namespace mtconnect { if (!fs::exists(backup)) fs::rename(file, backup); - printer::XmlPrinter printer(m_version, true); + printer::XmlPrinter printer(true); std::list list; copy_if(m_deviceIndex.begin(), m_deviceIndex.end(), back_inserter(list), @@ -576,7 +606,7 @@ namespace mtconnect { // Create the Agent Device ErrorList errors; - Properties ps {{"uuid", uuid}, {"id", id}, {"name", "Agent"s}, {"mtconnectVersion", m_version}}; + Properties ps {{"uuid", uuid}, {"id", id}, {"name", "Agent"s}, {"mtconnectVersion", *m_schemaVersion}}; m_agentDevice = dynamic_pointer_cast(AgentDevice::getFactory()->make("Agent", ps, errors)); if (!errors.empty()) @@ -602,6 +632,16 @@ namespace mtconnect { auto devices = m_xmlParser->parseFile( configXmlPath, dynamic_cast(m_printers["xml"].get())); + if (!m_schemaVersion && m_xmlParser->getSchemaVersion()) + { + m_schemaVersion = m_xmlParser->getSchemaVersion(); + m_version = m_xmlParser->getVersion(); + } + else if (!m_schemaVersion && !m_xmlParser->getSchemaVersion()) + { + m_version = AGENT_VERSION_MAJOR * 100 + AGENT_VERSION_MINOR; + } + // Fir the DeviceAdded event for each device for (auto device : devices) addDevice(device); @@ -627,7 +667,6 @@ namespace mtconnect { NAMED_SCOPE("Agent::verifyDevice"); auto xmlPrinter = dynamic_cast(m_printers["xml"].get()); - const auto &schemaVersion = xmlPrinter->getSchemaVersion(); // Add the devices to the device map and create availability and // asset changed events if they don't exist @@ -644,11 +683,7 @@ namespace mtconnect { device->addDataItem(di, errors); } - int major, minor; - char c; - stringstream ss(schemaVersion); - ss >> major >> c >> minor; - if (!device->getAssetChanged() && (major > 1 || (major == 1 && minor >= 2))) + if (!device->getAssetChanged() && m_version >= 102) { entity::ErrorList errors; // Create asset change data item and add it to the device. @@ -659,14 +694,14 @@ namespace mtconnect { device->addDataItem(di, errors); } - if (device->getAssetChanged() && (major > 1 || (major == 1 && minor >= 5))) + if (device->getAssetChanged() && m_version >= 105) { auto di = device->getAssetChanged(); if (!di->isDiscrete()) di->makeDiscrete(); } - if (!device->getAssetRemoved() && (major > 1 || (major == 1 && minor >= 3))) + if (!device->getAssetRemoved() && m_version >= 103) { // Create asset removed data item and add it to the device. entity::ErrorList errors; @@ -677,7 +712,7 @@ namespace mtconnect { device->addDataItem(di, errors); } - if (!device->getAssetCount() && (major >= 2)) + if (!device->getAssetCount() && m_version >= 200) { entity::ErrorList errors; auto di = DataItem::make({{"type", "ASSET_COUNT"s}, diff --git a/src/agent.hpp b/src/agent.hpp index 7e69b95ca..e56030c0b 100644 --- a/src/agent.hpp +++ b/src/agent.hpp @@ -129,6 +129,8 @@ namespace mtconnect { const auto &getSources() const { return m_sources; } const auto &getSinks() const { return m_sinks; } + const auto &getSchemaVersion() const { return m_schemaVersion; } + // Get device from device map DevicePtr getDeviceByName(const std::string &name); DevicePtr getDeviceByName(const std::string &name) const; @@ -157,7 +159,7 @@ namespace mtconnect { // Add the a device from a configuration file void addDevice(DevicePtr device); void deviceChanged(DevicePtr device, const std::string &oldUuid, const std::string &oldName); - void reloadDevices(const std::string &deviceFile); + bool reloadDevices(const std::string &deviceFile); // Message when adapter has connected and disconnected void connecting(const std::string &adapter); @@ -296,7 +298,8 @@ namespace mtconnect { std::unordered_map m_dataItemMap; // Xml Config - std::string m_version; + std::optional m_schemaVersion; + int m_version { 0 }; std::string m_deviceXmlPath; bool m_versionDeviceXml = false; diff --git a/src/configuration/agent_config.cpp b/src/configuration/agent_config.cpp index e8fb7ccef..8cea8fe81 100644 --- a/src/configuration/agent_config.cpp +++ b/src/configuration/agent_config.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #ifdef __APPLE__ #include @@ -85,6 +86,7 @@ using namespace std; namespace fs = std::filesystem; +namespace bfs = boost::filesystem; namespace pt = boost::property_tree; namespace logr = boost::log; namespace dll = boost::dll; @@ -276,18 +278,34 @@ namespace mtconnect::configuration { LOG(warning) << "Detected change in configuration files. Will reload when youngest file is at least " << m_monitorDelay.count() << " seconds old"; + + if (devTime != *m_deviceTime) + { + auto t = bfs::last_write_time(m_devicesFile); + LOG(warning) << "Dected change in Devices file: " << m_devicesFile; + LOG(warning) << "... File changed at: " << put_time(localtime(&t), "%F %T"); + } + + if (cfgTime != *m_configTime) + { + auto t = bfs::last_write_time(m_devicesFile); + LOG(warning) << "Dected change in Config file: " << m_configFile; + LOG(warning) << "... File changed at: " << put_time(localtime(&t), "%F %T"); + } auto delta = min(now - cfgTime, now - devTime); if (delta < m_monitorDelay) { - LOG(warning) << "Changed, waiting " << int32_t((m_monitorDelay - delta).count()); + LOG(warning) << "... Waiting " << int32_t((m_monitorDelay - delta).count()) << " seconds"; + scheduleMonitorTimer(); } else { if (cfgTime != *m_configTime) { LOG(warning) - << "Monitor thread has detected change in configuration files, restarting agent."; + << "Monitor thread has detected change in configuration files."; + LOG(warning) << ".... Restarting agent: " << m_configFile; m_agent->stop(); @@ -311,21 +329,23 @@ namespace mtconnect::configuration { } }, true); - - return; } - - // Handle device changed by delivering the device file to the agent - if (devTime != *m_deviceTime) + else if (devTime != *m_deviceTime) { + // Handle device changed by delivering the device file to the agent + LOG(warning) << "Monitor thread has detected change in devices files."; + LOG(warning) << "... Reloading Devices File: " << m_devicesFile; + m_context->pause([this](AsyncContext &context) { - m_agent->reloadDevices(m_devicesFile); - m_deviceTime.reset(); + if (!m_agent->reloadDevices(m_devicesFile)) + m_configTime.emplace(m_configTime->min()); + else + m_deviceTime.reset(); + scheduleMonitorTimer(); }); } } - scheduleMonitorTimer(); return; } @@ -624,8 +644,7 @@ namespace mtconnect::configuration { {configuration::MaxCachedFileSize, "20k"s}, {configuration::MinCompressFileSize, "100k"s}, {configuration::ServiceName, "MTConnect Agent"s}, - {configuration::SchemaVersion, - to_string(AGENT_VERSION_MAJOR) + "."s + to_string(AGENT_VERSION_MINOR)}, + {configuration::SchemaVersion, ""s}, {configuration::LogStreams, false}, {configuration::ShdrVersion, 1}, {configuration::WorkerThreads, 1}, @@ -685,13 +704,12 @@ namespace mtconnect::configuration { } // Check for schema version - m_version = get(options[configuration::SchemaVersion]); auto port = get(options[configuration::Port]); LOG(info) << "Starting agent on port " << int(port); // Make the Agent m_agent = make_unique(getAsyncContext(), m_devicesFile, options); - + // Make the PipelineContext m_pipelineContext = std::make_shared(); m_pipelineContext->m_contract = m_agent->makePipelineContract(); @@ -699,6 +717,7 @@ namespace mtconnect::configuration { loadSinks(config, options); m_agent->initialize(m_pipelineContext); + m_version = *m_agent->getSchemaVersion(); DevicePtr device; if (get(options[configuration::PreserveUUID])) diff --git a/src/parser/xml_parser.cpp b/src/parser/xml_parser.cpp index c4c93c38a..6b3fcda2e 100644 --- a/src/parser/xml_parser.cpp +++ b/src/parser/xml_parser.cpp @@ -17,6 +17,15 @@ #include "xml_parser.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + #include #include @@ -87,6 +96,9 @@ namespace mtconnect::parser { std::list XmlParser::parseFile(const std::string &filePath, XmlPrinter *aPrinter) { + using namespace boost::adaptors; + using namespace boost::range; + std::unique_lock lock(m_mutex); if (m_doc) @@ -118,20 +130,22 @@ namespace mtconnect::parser { THROW_IF_XML2_ERROR(xmlXPathRegisterNs(xpathCtx, BAD_CAST "m", root->ns->href)); // Get schema version from Devices.xml - if (aPrinter->getSchemaVersion().empty()) + string ns((const char *)root->ns->href); + size_t colon = string::npos; + if (ns.find_first_of("urn:mtconnect.org:MTConnectDevices") == 0 && + (colon = ns.find_last_of(':')) != string::npos) { - string ns((const char *)root->ns->href); - - if (!ns.find_first_of("urn:mtconnect.org:MTConnectDevices")) - { - auto last = ns.find_last_of(':'); - - if (last != string::npos) - { - string version = ns.substr(last + 1); - aPrinter->setSchemaVersion(version); - } - } + auto version = ns.substr(colon + 1); + LOG(info) << "MTConnect Schema Version of file: " << filePath << " = " << version; + m_schemaVersion.emplace(version); + + int major, minor; + char c; + stringstream vstr(*m_schemaVersion); + vstr >> major >> c >> minor; + m_version = major * 100 + minor; + + LOG(debug) << "Numeric schema version: " << m_version; } } diff --git a/src/parser/xml_parser.hpp b/src/parser/xml_parser.hpp index f5d80a468..e96bff087 100644 --- a/src/parser/xml_parser.hpp +++ b/src/parser/xml_parser.hpp @@ -52,10 +52,14 @@ namespace mtconnect::parser { // Get std::list of data items in path void getDataItems(FilterSet &filterSet, const std::string &path, xmlNodePtr node = nullptr); - + const auto &getSchemaVersion() const { return m_schemaVersion; } + auto getVersion() const { return m_version; } + protected: // LibXML XML Doc xmlDocPtr m_doc = nullptr; + std::optional m_schemaVersion; + int m_version { 0 }; mutable std::shared_mutex m_mutex; }; } // namespace mtconnect::parser diff --git a/src/printer/json_printer.cpp b/src/printer/json_printer.cpp index 58a91a7f7..3c805993d 100644 --- a/src/printer/json_printer.cpp +++ b/src/printer/json_printer.cpp @@ -40,8 +40,8 @@ namespace mtconnect::printer { using namespace observation; using namespace device_model; - JsonPrinter::JsonPrinter(uint32_t jsonVersion, const string version, bool pretty) - : Printer(pretty), m_schemaVersion(version), m_jsonVersion(jsonVersion) + JsonPrinter::JsonPrinter(uint32_t jsonVersion, bool pretty) + : Printer(pretty), m_jsonVersion(jsonVersion) { NAMED_SCOPE("JsonPrinter::JsonPrinter"); char appVersion[32] = {0}; @@ -125,6 +125,8 @@ namespace mtconnect::printer { std::string JsonPrinter::printErrors(const uint64_t instanceId, const unsigned int bufferSize, const uint64_t nextSeq, const ProtoErrorList &list) const { + defaultSchemaVersion(); + json errors = json::array(); for (auto &e : list) { @@ -137,7 +139,7 @@ namespace mtconnect::printer { json doc = json::object({{"MTConnectError", {{"jsonVersion", m_jsonVersion}, {"Header", header(m_version, hostname(), instanceId, bufferSize, - m_schemaVersion, m_modelChangeTime)}, + *m_schemaVersion, m_modelChangeTime)}, {"Errors", errors}}}}); return print(doc, m_pretty); @@ -163,6 +165,8 @@ namespace mtconnect::printer { const std::list &devices, const std::map *count) const { + defaultSchemaVersion(); + entity::JsonPrinter printer(m_jsonVersion); json devicesDoc; @@ -190,7 +194,7 @@ namespace mtconnect::printer { {{"jsonVersion", m_jsonVersion}, {"Header", probeAssetHeader(m_version, hostname(), instanceId, bufferSize, assetBufferSize, assetCount, - m_schemaVersion, m_modelChangeTime)}, + *m_schemaVersion, m_modelChangeTime)}, {"Devices", devicesDoc}}}}); return print(doc, m_pretty); @@ -376,6 +380,8 @@ namespace mtconnect::printer { const uint64_t nextSeq, const uint64_t firstSeq, const uint64_t lastSeq, ObservationList &observations) const { + defaultSchemaVersion(); + json streams; if (observations.size() > 0) @@ -424,7 +430,7 @@ namespace mtconnect::printer { {{"MTConnectStreams", {{"jsonVersion", m_jsonVersion}, {"Header", streamHeader(m_version, hostname(), instanceId, bufferSize, nextSeq, firstSeq, - lastSeq, m_schemaVersion, m_modelChangeTime)}, + lastSeq, *m_schemaVersion, m_modelChangeTime)}, {"Streams", streams}}}}); return print(doc, m_pretty); @@ -434,6 +440,8 @@ namespace mtconnect::printer { const unsigned int assetCount, const asset::AssetList &asset) const { + defaultSchemaVersion(); + entity::JsonPrinter printer(m_jsonVersion); json assetDoc; if (m_jsonVersion == 1) @@ -459,7 +467,7 @@ namespace mtconnect::printer { {{"MTConnectAssets", {{"jsonVersion", m_jsonVersion}, {"Header", probeAssetHeader(m_version, hostname(), instanceId, 0, bufferSize, assetCount, - m_schemaVersion, m_modelChangeTime)}, + *m_schemaVersion, m_modelChangeTime)}, {"Assets", assetDoc}}}}); return print(doc, m_pretty); diff --git a/src/printer/json_printer.hpp b/src/printer/json_printer.hpp index 65064d783..759d9cbe4 100644 --- a/src/printer/json_printer.hpp +++ b/src/printer/json_printer.hpp @@ -25,7 +25,7 @@ namespace mtconnect::printer { class JsonPrinter : public Printer { public: - JsonPrinter(uint32_t jsonVersion, const std::string version = "", bool pretty = false); + JsonPrinter(uint32_t jsonVersion, bool pretty = false); ~JsonPrinter() override = default; std::string printErrors(const uint64_t instanceId, const unsigned int bufferSize, @@ -48,7 +48,6 @@ namespace mtconnect::printer { protected: const std::string &hostname() const; - std::string m_schemaVersion; std::string m_version; std::string m_hostname; uint32_t m_jsonVersion; diff --git a/src/printer/printer.hpp b/src/printer/printer.hpp index 41ca7e9d8..6a9b87c5e 100644 --- a/src/printer/printer.hpp +++ b/src/printer/printer.hpp @@ -25,6 +25,7 @@ #include "asset/asset.hpp" #include "observation/observation.hpp" #include "utilities.hpp" +#include "version.h" namespace mtconnect { namespace device_model { @@ -72,10 +73,23 @@ namespace mtconnect { void setModelChangeTime(const std::string &t) { m_modelChangeTime = t; } const std::string &getModelChangeTime() { return m_modelChangeTime; } + + void setSchemaVersion(const std::string &s) { m_schemaVersion = s; } + const auto &getSchemaVersion() const { return m_schemaVersion; } + + void defaultSchemaVersion() const { + if (!m_schemaVersion) + { + std::string ver = std::to_string(AGENT_VERSION_MAJOR) + "." + + std::to_string(AGENT_VERSION_MINOR); + const_cast(this)->m_schemaVersion.emplace(ver); + } + } protected: bool m_pretty; std::string m_modelChangeTime; + std::optional m_schemaVersion; }; } // namespace printer } // namespace mtconnect diff --git a/src/printer/xml_printer.cpp b/src/printer/xml_printer.cpp index 76c111cd3..070383f7f 100644 --- a/src/printer/xml_printer.cpp +++ b/src/printer/xml_printer.cpp @@ -100,12 +100,10 @@ namespace mtconnect::printer { xmlBufferPtr m_buf; }; - XmlPrinter::XmlPrinter(const string version, bool pretty) - : Printer(pretty), m_schemaVersion(version) + XmlPrinter::XmlPrinter(bool pretty) + : Printer(pretty) { NAMED_SCOPE("xml.printer"); - if (m_schemaVersion.empty()) - m_schemaVersion = to_string(AGENT_VERSION_MAJOR) + "." + to_string(AGENT_VERSION_MINOR); } void XmlPrinter::addDevicesNamespace(const std::string &urn, const std::string &location, @@ -189,10 +187,6 @@ namespace mtconnect::printer { void XmlPrinter::clearStreamsNamespaces() { m_streamsNamespaces.clear(); } - void XmlPrinter::setSchemaVersion(const std::string &version) { m_schemaVersion = version; } - - const std::string &XmlPrinter::getSchemaVersion() { return m_schemaVersion; } - string XmlPrinter::getStreamsUrn(const std::string &prefix) { auto ns = m_streamsNamespaces.find(prefix); @@ -588,7 +582,7 @@ namespace mtconnect::printer { } string rootName = "MTConnect" + xmlType; - string xmlns = "urn:mtconnect.org:" + rootName + ":" + m_schemaVersion; + string xmlns = "urn:mtconnect.org:" + rootName + ":" + *m_schemaVersion; string location; openElement(writer, rootName.c_str()); @@ -623,13 +617,15 @@ namespace mtconnect::printer { mtcLocation = xmlns + " " + ns.second.mSchemaLocation; } } + + // Write the schema location if (location.empty() && !mtcLocation.empty()) location = mtcLocation; else if (location.empty()) location = xmlns + " http://schemas.mtconnect.org/schemas/" + rootName + "_" + - m_schemaVersion + ".xsd"; + *m_schemaVersion + ".xsd"; addAttribute(writer, "xsi:schemaLocation", location); @@ -656,7 +652,7 @@ namespace mtconnect::printer { int major, minor; char c; - stringstream v(m_schemaVersion); + stringstream v(*m_schemaVersion); v >> major >> c >> minor; if (major > 1 || (major == 1 && minor >= 7)) diff --git a/src/printer/xml_printer.hpp b/src/printer/xml_printer.hpp index 9e9b92fe5..4f6a21eda 100644 --- a/src/printer/xml_printer.hpp +++ b/src/printer/xml_printer.hpp @@ -38,7 +38,7 @@ namespace mtconnect { class XmlPrinter : public Printer { public: - XmlPrinter(const std::string version = "", bool pretty = false); + XmlPrinter(bool pretty = false); ~XmlPrinter() override = default; std::string printErrors(const uint64_t instanceId, const unsigned int bufferSize, @@ -67,9 +67,6 @@ namespace mtconnect { void addAssetsNamespace(const std::string &urn, const std::string &location, const std::string &prefix); - void setSchemaVersion(const std::string &version); - const std::string &getSchemaVersion(); - void setDevicesStyle(const std::string &style); void setStreamStyle(const std::string &style); void setAssetsStyle(const std::string &style); @@ -130,7 +127,6 @@ namespace mtconnect { std::unordered_set m_errorNsSet; std::unordered_set m_assetNsSet; - std::string m_schemaVersion; std::string m_streamsStyle; std::string m_devicesStyle; std::string m_errorStyle; diff --git a/src/sink/rest_sink/rest_service.cpp b/src/sink/rest_sink/rest_service.cpp index e35a47add..e72c3a49e 100644 --- a/src/sink/rest_sink/rest_service.cpp +++ b/src/sink/rest_sink/rest_service.cpp @@ -44,7 +44,7 @@ namespace mtconnect { : Sink("RestService", move(contract)), m_context(context), m_strand(context), - m_version(GetOption(options, config::SchemaVersion).value_or("x.y")), + m_schemaVersion(GetOption(options, config::SchemaVersion).value_or("x.y")), m_options(options), m_logStreamData(GetOption(options, config::LogStreams).value_or(false)) { @@ -166,7 +166,7 @@ namespace mtconnect { auto path = block.second.get_optional("Path"); if (path && !location.empty()) { - auto xns = m_fileCache.registerFile(location, *path, m_version); + auto xns = m_fileCache.registerFile(location, *path, m_schemaVersion); if (!xns) { LOG(debug) << "Cannot register " << urn << " at " << location << " and path " @@ -194,7 +194,7 @@ namespace mtconnect { } else { - auto namespaces = m_fileCache.registerFiles(*location, *path, m_version); + auto namespaces = m_fileCache.registerFiles(*location, *path, m_schemaVersion); for (auto &ns : namespaces) { if (ns.first.find(::config::Devices) != string::npos) @@ -272,7 +272,7 @@ namespace mtconnect { auto path = style->get_optional("Path"); if (path) { - m_fileCache.registerFile(*location, *path, m_version); + m_fileCache.registerFile(*location, *path, m_schemaVersion); } } } diff --git a/src/sink/rest_sink/rest_service.hpp b/src/sink/rest_sink/rest_service.hpp index cad456ae6..8f894a4f2 100644 --- a/src/sink/rest_sink/rest_service.hpp +++ b/src/sink/rest_sink/rest_service.hpp @@ -208,7 +208,7 @@ namespace mtconnect { boost::asio::io_context::strand m_strand; - std::string m_version; + std::string m_schemaVersion; ConfigOptions m_options; diff --git a/src/version.h.in b/src/version.h.in index 3ba170113..cd55bdbc1 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,3 +1,5 @@ +#pragma once + #include // the configured options and settings for MTConnect Agent diff --git a/test/config_test.cpp b/test/config_test.cpp index c883dee53..c8694b06c 100644 --- a/test/config_test.cpp +++ b/test/config_test.cpp @@ -19,6 +19,8 @@ #include // Keep this comment to keep gtest.h above. (clang-format off/on is not working here!) +#include + #include #include #include @@ -41,6 +43,7 @@ using namespace mtconnect; using namespace mtconnect::configuration; namespace fs = std::filesystem; using namespace std::chrono_literals; +using namespace boost::algorithm; namespace { class ConfigTest : public testing::Test @@ -51,6 +54,9 @@ namespace { m_config = std::make_unique(); m_config->setDebug(true); m_cwd = std::filesystem::current_path(); + + chdir(TEST_BIN_ROOT_DIR); + m_config->updateWorkingDirectory(); } void TearDown() override @@ -58,6 +64,49 @@ namespace { m_config.reset(); chdir(m_cwd.string().c_str()); } + + fs::path createTempDirectory(const string &ext) + { + fs::path root { fs::path(TEST_BIN_ROOT_DIR) / ("config_test_" + ext) }; + if (!fs::exists(root)) + fs::create_directory(root); + chdir(root.string().c_str()); + m_config->updateWorkingDirectory(); + //m_config->setDebug(false); + + return root; + } + + fs::path copyFile(const std::string &src, + fs::path target, + chrono::seconds delta) + { + fs::path file { fs::path(PROJECT_ROOT_DIR) / "samples" / src }; + + fs::copy_file(file, target, fs::copy_options::overwrite_existing); + auto t = fs::last_write_time(target); + if (delta.count() != 0) + fs::last_write_time(target, t - delta); + + return target; + } + + void replaceTextInFile(fs::path file, const std::string &from, + const std::string &to) + { + ifstream is {file.string(), ios::binary | ios::ate}; + auto size = is.tellg(); + string str(size, '\0'); // construct string to stream size + is.seekg(0); + is.read(&str[0], size); + is.close(); + + replace_all(str, from, to); + + ofstream os(file.string()); + os << str; + os.close(); + } std::unique_ptr m_config; std::filesystem::path m_cwd; @@ -65,19 +114,16 @@ namespace { TEST_F(ConfigTest, BlankConfig) { - chdir(TEST_BIN_ROOT_DIR); - m_config->updateWorkingDirectory(); m_config->loadConfig(""); const auto agent = m_config->getAgent(); ASSERT_TRUE(agent); - ASSERT_EQ(size_t(2), agent->getDevices().size()); + ASSERT_EQ(size_t(1), agent->getDevices().size()); + ASSERT_EQ("1.1", *agent->getSchemaVersion()); } TEST_F(ConfigTest, BufferSize) { - chdir(TEST_BIN_ROOT_DIR); - m_config->updateWorkingDirectory(); m_config->loadConfig("BufferSize = 4\n"); const auto agent = m_config->getAgent(); @@ -231,8 +277,6 @@ namespace { TEST_F(ConfigTest, Namespaces) { - chdir(TEST_BIN_ROOT_DIR); - m_config->updateWorkingDirectory(); string streams( "StreamsNamespaces {\n" "x {\n" @@ -353,8 +397,6 @@ namespace { TEST_F(ConfigTest, SpecifyMTCNamespace) { - chdir(TEST_BIN_ROOT_DIR); - m_config->updateWorkingDirectory(); string streams( "StreamsNamespaces {\n" "m {\n" @@ -379,8 +421,6 @@ namespace { TEST_F(ConfigTest, SetSchemaVersion) { - chdir(TEST_BIN_ROOT_DIR); - m_config->updateWorkingDirectory(); string streams("SchemaVersion = 1.4\n"); m_config->loadConfig(streams); @@ -441,9 +481,6 @@ namespace { TEST_F(ConfigTest, check_http_headers) { - chdir(TEST_BIN_ROOT_DIR); - m_config->updateWorkingDirectory(); - string str( "HttpHeaders {\n" " Access-Control-Allow-Origin = *\n" @@ -471,9 +508,6 @@ namespace { TEST_F(ConfigTest, dynamic_load_sinks_bad) { - chdir(TEST_BIN_ROOT_DIR); - m_config->updateWorkingDirectory(); - string str(R"( Plugins { TestBADService { @@ -495,10 +529,6 @@ Sinks { TEST_F(ConfigTest, dynamic_load_sinks_simple) { - chdir(TEST_BIN_ROOT_DIR); - - m_config->updateWorkingDirectory(); - string str(R"( Sinks { sink_plugin_test { @@ -517,10 +547,6 @@ Sinks { TEST_F(ConfigTest, dynamic_load_sinks_with_plugin_block) { - chdir(TEST_BIN_ROOT_DIR); - - m_config->updateWorkingDirectory(); - string str(R"( Plugins { sink_plugin_test { @@ -543,10 +569,6 @@ Sinks { TEST_F(ConfigTest, dynamic_load_sinks_assigned_name) { - chdir(TEST_BIN_ROOT_DIR); - - m_config->updateWorkingDirectory(); - string str(R"( Sinks { sink_plugin_test:Sink1 { @@ -567,10 +589,6 @@ Sinks { TEST_F(ConfigTest, dynamic_load_sinks_assigned_name_tag) { - chdir(TEST_BIN_ROOT_DIR); - - m_config->updateWorkingDirectory(); - string str(R"( Sinks { sink_plugin_test { @@ -593,9 +611,6 @@ Sinks { // TEST_F(ConfigTest, dynamic_load_adapter_bad) { - chdir(TEST_BIN_ROOT_DIR); - m_config->updateWorkingDirectory(); - string str(R"( Adapters { BadAdapter:Test { @@ -615,9 +630,6 @@ Adapters { TEST_F(ConfigTest, dynamic_load_adapter_simple) { - chdir(TEST_BIN_ROOT_DIR); - m_config->updateWorkingDirectory(); - string str(R"( Adapters { adapter_plugin_test:Test { @@ -637,9 +649,6 @@ Adapters { TEST_F(ConfigTest, dynamic_load_adapter_with_plugin_block) { - chdir(TEST_BIN_ROOT_DIR); - m_config->updateWorkingDirectory(); - string str(R"( Plugins { adapter_plugin_test { @@ -664,9 +673,6 @@ Adapters { TEST_F(ConfigTest, max_cache_size_in_no_units) { - chdir(TEST_BIN_ROOT_DIR); - m_config->updateWorkingDirectory(); - string str(R"( MaxCachedFileSize = 2000 )"); @@ -685,9 +691,6 @@ MaxCachedFileSize = 2000 TEST_F(ConfigTest, max_cache_size_in_kb) { - chdir(TEST_BIN_ROOT_DIR); - m_config->updateWorkingDirectory(); - string str(R"( MaxCachedFileSize = 2k )"); @@ -706,9 +709,6 @@ MaxCachedFileSize = 2k TEST_F(ConfigTest, max_cache_size_in_Kb_in_uppercase) { - chdir(TEST_BIN_ROOT_DIR); - m_config->updateWorkingDirectory(); - string str(R"( MaxCachedFileSize = 2K )"); @@ -727,9 +727,6 @@ MaxCachedFileSize = 2K TEST_F(ConfigTest, max_cache_size_in_mb) { - chdir(TEST_BIN_ROOT_DIR); - m_config->updateWorkingDirectory(); - string str(R"( MaxCachedFileSize = 2m )"); @@ -748,9 +745,6 @@ MaxCachedFileSize = 2m TEST_F(ConfigTest, max_cache_size_in_gb) { - chdir(TEST_BIN_ROOT_DIR); - m_config->updateWorkingDirectory(); - string str(R"( MaxCachedFileSize = 2g )"); @@ -772,8 +766,6 @@ MaxCachedFileSize = 2g TEST_F(ConfigTest, log_output_should_set_archive_file_pattern) { - chdir(TEST_BIN_ROOT_DIR); - m_config->updateWorkingDirectory(); m_config->setDebug(false); string str(R"( @@ -794,8 +786,6 @@ logger_config { TEST_F(ConfigTest, log_output_should_configure_file_name) { - chdir(TEST_BIN_ROOT_DIR); - m_config->updateWorkingDirectory(); m_config->setDebug(false); string str(R"( @@ -816,8 +806,6 @@ logger_config { TEST_F(ConfigTest, log_should_configure_file_name) { - chdir(TEST_BIN_ROOT_DIR); - m_config->updateWorkingDirectory(); m_config->setDebug(false); string str(R"( @@ -839,8 +827,6 @@ logger_config { TEST_F(ConfigTest, log_should_specify_relative_directory) { - chdir(TEST_BIN_ROOT_DIR); - m_config->updateWorkingDirectory(); m_config->setDebug(false); string str(R"( @@ -864,8 +850,6 @@ logger_config { TEST_F(ConfigTest, log_should_specify_relative_directory_with_active_in_parent) { - chdir(TEST_BIN_ROOT_DIR); - m_config->updateWorkingDirectory(); m_config->setDebug(false); string str(R"( @@ -889,8 +873,6 @@ logger_config { TEST_F(ConfigTest, log_should_specify_max_file_and_rotation_size) { - chdir(TEST_BIN_ROOT_DIR); - m_config->updateWorkingDirectory(); m_config->setDebug(false); using namespace boost::log::trivial; @@ -913,8 +895,6 @@ logger_config { TEST_F(ConfigTest, log_should_configure_logging_level) { - chdir(TEST_BIN_ROOT_DIR); - m_config->updateWorkingDirectory(); m_config->setDebug(false); using namespace boost::log::trivial; @@ -974,16 +954,13 @@ logger_config { m_config->setLoggingLevel("FATAL"); EXPECT_EQ(severity_level::fatal, m_config->getLogLevel()); } + + TEST_F(ConfigTest, should_reload_device_xml_file) { - fs::path root(fs::path(TEST_BIN_ROOT_DIR) / "config_test_dir_1"); - if (!fs::exists(root)) - fs::create_directory(root); - chdir(root.string().c_str()); - m_config->updateWorkingDirectory(); - m_config->setDebug(false); - + auto root { createTempDirectory("1") }; + fs::path devices(root / "Devices.xml"); fs::path config {root / "agent.cfg"}; { @@ -996,11 +973,8 @@ Port = 0 )DOC"; cfg << "Devices = " << devices << endl; } - - fs::copy_file(fs::path(PROJECT_ROOT_DIR) / "samples" / "min_config.xml", devices, - fs::copy_options::overwrite_existing); - auto t = fs::last_write_time(devices); - fs::last_write_time(devices, t - 60min); + + copyFile("min_config.xml", devices, 60min); boost::program_options::variables_map options; boost::program_options::variable_value value(boost::optional(config.string()), false); @@ -1035,20 +1009,7 @@ Port = 0 di.reset(); // Modify devices - ifstream is {devices.string(), ios::binary | ios::ate}; - auto size = is.tellg(); - string str(size, '\0'); // construct string to stream size - is.seekg(0); - is.read(&str[0], size); - - auto pos = str.find("SPINDLE_SPEED"); - EXPECT_NE(string::npos, pos); - - str.replace(pos, 13, "ROTARY_VELOCITY"); - is.close(); - - ofstream os(devices.string()); - os << str; + replaceTextInFile(devices, "SPINDLE_SPEED", "ROTARY_VELOCITY"); } }); @@ -1077,12 +1038,7 @@ Port = 0 TEST_F(ConfigTest, should_reload_device_xml_and_skip_unchanged_devices) { - fs::path root(fs::path(TEST_BIN_ROOT_DIR) / "config_test_dir_2"); - if (!fs::exists(root)) - fs::create_directory(root); - chdir(root.string().c_str()); - m_config->updateWorkingDirectory(); - m_config->setDebug(false); + fs::path root { createTempDirectory("2") }; fs::path devices(root / "Devices.xml"); fs::path config {root / "agent.cfg"}; @@ -1096,11 +1052,8 @@ Port = 0 )DOC"; cfg << "Devices = " << devices << endl; } - - fs::copy_file(fs::path(PROJECT_ROOT_DIR) / "samples" / "min_config.xml", devices, - fs::copy_options::overwrite_existing); - auto t = fs::last_write_time(devices); - fs::last_write_time(devices, t - 1min); + + copyFile("min_config.xml", devices, 1min); boost::program_options::variables_map options; boost::program_options::variable_value value(boost::optional(config.string()), false); @@ -1151,12 +1104,7 @@ Port = 0 TEST_F(ConfigTest, should_restart_agent_when_config_file_changes) { - fs::path root(fs::path(TEST_BIN_ROOT_DIR) / "config_test_dir_3"); - if (!fs::exists(root)) - fs::create_directory(root); - chdir(root.string().c_str()); - m_config->updateWorkingDirectory(); - m_config->setDebug(false); + fs::path root { createTempDirectory("3") }; auto &context = m_config->getAsyncContext(); fs::path devices(root / "Devices.xml"); @@ -1171,9 +1119,8 @@ Port = 0 )DOC"; cfg << "Devices = " << devices << endl; } - - fs::copy_file(fs::path(PROJECT_ROOT_DIR) / "samples" / "min_config.xml", devices, - fs::copy_options::overwrite_existing); + + copyFile("min_config.xml", devices, 0s); boost::program_options::variables_map options; boost::program_options::variable_value value(boost::optional(config.string()), false); @@ -1232,12 +1179,7 @@ Port = 0 TEST_F(ConfigTest, should_reload_device_xml_and_add_new_devices) { - fs::path root(fs::path(TEST_BIN_ROOT_DIR) / "config_test_dir_4"); - if (!fs::exists(root)) - fs::create_directory(root); - chdir(root.string().c_str()); - m_config->updateWorkingDirectory(); - m_config->setDebug(false); + fs::path root { createTempDirectory("4") }; fs::path devices(root / "Devices.xml"); fs::path config {root / "agent.cfg"}; @@ -1251,11 +1193,8 @@ Port = 0 )DOC"; cfg << "Devices = " << devices << endl; } - - fs::copy_file(fs::path(PROJECT_ROOT_DIR) / "samples" / "min_config.xml", devices, - fs::copy_options::overwrite_existing); - auto t = fs::last_write_time(devices); - fs::last_write_time(devices, t - 1min); + + copyFile("min_config.xml", devices, 1min); boost::program_options::variables_map options; boost::program_options::variable_value value(boost::optional(config.string()), false); @@ -1327,8 +1266,6 @@ Port = 0 TEST_F(ConfigTest, should_disable_agent_device) { - chdir(TEST_BIN_ROOT_DIR); - m_config->updateWorkingDirectory(); string streams("SchemaVersion = 2.0\nDisableAgentDevice = true\n"); m_config->loadConfig(streams); @@ -1344,8 +1281,6 @@ Port = 0 TEST_F(ConfigTest, should_default_not_disable_agent_device) { - chdir(TEST_BIN_ROOT_DIR); - m_config->updateWorkingDirectory(); string streams("SchemaVersion = 2.0\n"); m_config->loadConfig(streams); @@ -1355,8 +1290,103 @@ Port = 0 auto devices = agent->getDevices(); ASSERT_EQ(2, devices.size()); - auto device = devices.front(); + auto device = devices.back(); ASSERT_EQ("Agent", device->getName()); } + + TEST_F(ConfigTest, should_update_schema_version_when_device_file_updates) + { + auto root { createTempDirectory("5") }; + + fs::path devices(root / "Devices.xml"); + fs::path config {root / "agent.cfg"}; + { + ofstream cfg(config.string()); + cfg << R"DOC( +MonitorConfigFiles = true +MonitorInterval = 1 +MinimumConfigReloadAge = 1 +Port = 0 +)DOC"; + cfg << "Devices = " << devices << endl; + } + + copyFile("min_config.xml", devices, 10min); + replaceTextInFile(devices, "2.0", "1.2"); + + boost::program_options::variables_map options; + boost::program_options::variable_value value(boost::optional(config.string()), false); + options.insert(make_pair("config-file"s, value)); + + m_config->initialize(options); + auto agent = m_config->getAgent(); + auto &context = m_config->getAsyncContext(); + auto sink = agent->findSink("RestService"); + auto rest = dynamic_pointer_cast(sink); + ASSERT_TRUE(rest); + + auto instance = rest->instanceId(); + sink.reset(); + rest.reset(); + + const auto &printer = agent->getPrinter("xml"); + ASSERT_NE(nullptr, printer); + ASSERT_EQ("1.2", *printer->getSchemaVersion()); + + boost::asio::steady_timer timer1(context.getContext()); + timer1.expires_from_now(1s); + timer1.async_wait([this, &devices, agent](boost::system::error_code ec) { + if (ec) + { + m_config->stop(); + } + else + { + auto di = agent->getDataItemById("c1"); + EXPECT_TRUE(di); + EXPECT_EQ("SPINDLE_SPEED", di->getType()); + di.reset(); + + // Modify devices + replaceTextInFile(devices, "SPINDLE_SPEED", "ROTARY_VELOCITY"); + replaceTextInFile(devices, "1.2", "1.3"); + } + }); + + auto th = thread([this, agent, instance, &context]() { + this_thread::sleep_for(5s); + + boost::asio::steady_timer timer1(context.getContext()); + timer1.expires_from_now(1s); + timer1.async_wait([this, agent, instance](boost::system::error_code ec) { + if (!ec) + { + auto agent2 = m_config->getAgent(); + const auto sink = agent2->findSink("RestService"); + EXPECT_TRUE(sink); + const auto rest = dynamic_pointer_cast(sink); + EXPECT_TRUE(rest); + + EXPECT_NE(agent, agent2); + EXPECT_NE(instance, rest->instanceId()); + + auto dataItem = agent2->getDataItemById("c1"); + EXPECT_TRUE(dataItem); + EXPECT_EQ("ROTARY_VELOCITY", dataItem->getType()); + + const auto &printer = agent2->getPrinter("xml"); + EXPECT_NE(nullptr, printer); + ASSERT_EQ("1.3", *printer->getSchemaVersion()); + + } + }); + + m_config->stop(); + }); + + m_config->start(); + th.join(); + } + } // namespace From 0876c13509201599ebd10ac086d8a1af4e35fd4b Mon Sep 17 00:00:00 2001 From: Will Sobel Date: Tue, 15 Nov 2022 16:52:35 +0100 Subject: [PATCH 2/6] All tests now pass --- src/agent.cpp | 18 +++++++++++------- src/agent.hpp | 2 +- test/agent_device_test.cpp | 4 +++- test/config_test.cpp | 2 +- test/json_printer_asset_test.cpp | 4 ++-- test/json_printer_error_test.cpp | 2 +- test/json_printer_probe_test.cpp | 4 ++-- test/json_printer_stream_test.cpp | 6 +++--- test/mqtt_isolated_test.cpp | 2 +- test/mqtt_sink_test.cpp | 2 +- test/xml_printer_test.cpp | 10 ++++++---- 11 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/agent.cpp b/src/agent.cpp index 70f421a47..3925dcc81 100644 --- a/src/agent.cpp +++ b/src/agent.cpp @@ -125,7 +125,7 @@ namespace mtconnect { m_loopback = std::make_shared("AgentSource", m_strand, context, m_options); - loadXMLDeviceFile(m_deviceXmlPath); + auto devices = loadXMLDeviceFile(m_deviceXmlPath); if (!m_schemaVersion) { m_schemaVersion.emplace(to_string(AGENT_VERSION_MAJOR) + "." + to_string(AGENT_VERSION_MINOR)); @@ -144,8 +144,12 @@ namespace mtconnect { { createAgentDevice(); } - loadCachedProbe(); + + // For the DeviceAdded event for each device + for (auto device : devices) + addDevice(device); + loadCachedProbe(); m_initialized = true; } @@ -622,7 +626,7 @@ namespace mtconnect { // Device management and Initialization // ---------------------------------------------- - void Agent::loadXMLDeviceFile(const std::string &configXmlPath) + std::list Agent::loadXMLDeviceFile(const std::string &configXmlPath) { NAMED_SCOPE("Agent::loadXMLDeviceFile"); @@ -641,10 +645,8 @@ namespace mtconnect { { m_version = AGENT_VERSION_MAJOR * 100 + AGENT_VERSION_MINOR; } - - // Fir the DeviceAdded event for each device - for (auto device : devices) - addDevice(device); + + return devices; } catch (runtime_error &e) { @@ -660,6 +662,8 @@ namespace mtconnect { cerr << f.what() << endl; throw f; } + + return {}; } void Agent::verifyDevice(DevicePtr device) diff --git a/src/agent.hpp b/src/agent.hpp index e56030c0b..0ed7795ce 100644 --- a/src/agent.hpp +++ b/src/agent.hpp @@ -223,7 +223,7 @@ namespace mtconnect { // Initialization methods void createAgentDevice(); - void loadXMLDeviceFile(const std::string &config); + std::list loadXMLDeviceFile(const std::string &config); void verifyDevice(DevicePtr device); void initializeDataItems(DevicePtr device, std::optional> skip = std::nullopt); diff --git a/test/agent_device_test.cpp b/test/agent_device_test.cpp index fb8f51f1c..f30c21c3d 100644 --- a/test/agent_device_test.cpp +++ b/test/agent_device_test.cpp @@ -144,7 +144,9 @@ TEST_F(AgentDeviceTest, VerifyRequiredDataItems) TEST_F(AgentDeviceTest, DeviceAddedItemsInBuffer) { auto agent = m_agentTestHelper->getAgent(); - auto &uuid = agent->getDevices().back()->getUuid(); + auto device = agent->findDeviceByUUIDorName("000"); + ASSERT_TRUE(device); + auto uuid = *device->getUuid(); ASSERT_EQ("000", uuid); auto found = false; diff --git a/test/config_test.cpp b/test/config_test.cpp index c8694b06c..5e4cd877a 100644 --- a/test/config_test.cpp +++ b/test/config_test.cpp @@ -1290,7 +1290,7 @@ Port = 0 auto devices = agent->getDevices(); ASSERT_EQ(2, devices.size()); - auto device = devices.back(); + auto device = devices.front(); ASSERT_EQ("Agent", device->getName()); } diff --git a/test/json_printer_asset_test.cpp b/test/json_printer_asset_test.cpp index 4cb230118..bc25da63d 100644 --- a/test/json_printer_asset_test.cpp +++ b/test/json_printer_asset_test.cpp @@ -57,7 +57,7 @@ class JsonPrinterAssetTest : public testing::Test FileArchetypeAsset::registerAsset(); FileAsset::registerAsset(); - m_printer = std::make_unique(1, "1.5", true); + m_printer = std::make_unique(1, true); m_parser = std::make_unique(); } @@ -257,7 +257,7 @@ TEST_F(JsonPrinterAssetTest, CuttingToolArchitype) TEST_F(JsonPrinterAssetTest, json_printer_version_2_with_multiple_assets) { - m_printer = std::make_unique(2, "1.5", true); + m_printer = std::make_unique(2, true); AssetList assetList; diff --git a/test/json_printer_error_test.cpp b/test/json_printer_error_test.cpp index 9432b1827..2f063d749 100644 --- a/test/json_printer_error_test.cpp +++ b/test/json_printer_error_test.cpp @@ -42,7 +42,7 @@ using json = nlohmann::json; class JsonPrinterErrorTest : public testing::Test { protected: - void SetUp() override { m_printer = std::make_unique(1, "1.5", true); } + void SetUp() override { m_printer = std::make_unique(1, true); } std::unique_ptr m_printer; }; diff --git a/test/json_printer_probe_test.cpp b/test/json_printer_probe_test.cpp index f53871fdc..40324b859 100644 --- a/test/json_printer_probe_test.cpp +++ b/test/json_printer_probe_test.cpp @@ -50,7 +50,7 @@ class JsonPrinterProbeTest : public testing::Test void SetUp() override { m_xmlPrinter = std::make_unique("1.5"); - m_printer = std::make_unique(1, "1.5", true); + m_printer = std::make_unique(1, true); m_agentTestHelper = make_unique(); m_agentTestHelper->createAgent("/samples/SimpleDevlce.xml", 8, 4, "1.5", 25); @@ -368,7 +368,7 @@ TEST_F(JsonPrinterProbeTest, PrintDataItemRelationships) TEST_F(JsonPrinterProbeTest, version_2_with_multiple_devices) { - m_printer = std::make_unique(2, "1.5", true); + m_printer = std::make_unique(2, true); m_agentTestHelper->createAgent("/samples/two_devices.xml", 8, 4, "1.5", 25); m_devices = m_agentTestHelper->m_agent->getDevices(); diff --git a/test/json_printer_stream_test.cpp b/test/json_printer_stream_test.cpp index b1724d7f8..48cdbc498 100644 --- a/test/json_printer_stream_test.cpp +++ b/test/json_printer_stream_test.cpp @@ -51,7 +51,7 @@ class JsonPrinterStreamTest : public testing::Test void SetUp() override { m_xmlPrinter = std::make_unique("1.5"); - m_printer = std::make_unique(1, "1.5", true); + m_printer = std::make_unique(1, true); m_config = std::make_unique(); m_devices = m_config->parseFile(PROJECT_ROOT_DIR "/samples/SimpleDevlce.xml", m_xmlPrinter.get()); @@ -208,7 +208,7 @@ TEST_F(JsonPrinterStreamTest, ComponentStreamTwoComponents) TEST_F(JsonPrinterStreamTest, two_components_version_2) { - m_printer = std::make_unique(2, "1.5", true); + m_printer = std::make_unique(2, true); Checkpoint checkpoint; addObservationToCheckpoint(checkpoint, "Xpos", 10254804, 100_value); @@ -306,7 +306,7 @@ TEST_F(JsonPrinterStreamTest, SampleAndEventDataItem) TEST_F(JsonPrinterStreamTest, samples_and_events_version_2) { - m_printer = std::make_unique(2, "1.5", true); + m_printer = std::make_unique(2, true); ObservationList list; Timestamp now = chrono::system_clock::now(); diff --git a/test/mqtt_isolated_test.cpp b/test/mqtt_isolated_test.cpp index f6b1fefd4..63d626c64 100644 --- a/test/mqtt_isolated_test.cpp +++ b/test/mqtt_isolated_test.cpp @@ -45,7 +45,7 @@ class MqttIsolatedUnitTest : public testing::Test void SetUp() override { m_agentTestHelper = make_unique(); - m_jsonPrinter = std::make_unique(2, "1.5", true); + m_jsonPrinter = std::make_unique(2, true); } void TearDown() override diff --git a/test/mqtt_sink_test.cpp b/test/mqtt_sink_test.cpp index 5de6cb48f..0444daf0f 100644 --- a/test/mqtt_sink_test.cpp +++ b/test/mqtt_sink_test.cpp @@ -48,7 +48,7 @@ class MqttSinkTest : public testing::Test void SetUp() override { m_agentTestHelper = make_unique(); - m_jsonPrinter = std::make_unique(2, "1.5", true); + m_jsonPrinter = std::make_unique(2, true); } void TearDown() override diff --git a/test/xml_printer_test.cpp b/test/xml_printer_test.cpp index 51a62e78c..946a53725 100644 --- a/test/xml_printer_test.cpp +++ b/test/xml_printer_test.cpp @@ -48,8 +48,8 @@ class XmlPrinterTest : public testing::Test void SetUp() override { m_config = new XmlParser(); - m_printer = new printer::XmlPrinter("1.7", true); - m_printer->setSchemaVersion(""); + m_printer = new printer::XmlPrinter(true); + m_printer->setSchemaVersion("1.2"); m_devices = m_config->parseFile(PROJECT_ROOT_DIR "/samples/test_config.xml", m_printer); } @@ -239,7 +239,8 @@ TEST_F(XmlPrinterTest, ChangeDevicesNamespace) { // Devices m_printer->clearDevicesNamespaces(); - + + { PARSE_XML(m_printer->printProbe(123, 9999, 1024, 10, 1, m_devices)); ASSERT_XML_PATH_EQUAL(doc, "/m:MTConnectDevices@schemaLocation", @@ -766,7 +767,8 @@ TEST_F(XmlPrinterTest, LegacyReferences) TEST_F(XmlPrinterTest, CheckDeviceChangeTime) { - m_printer = new XmlPrinter("1.7", true); + m_printer = new XmlPrinter(true); + m_printer->setSchemaVersion("1.7"); m_devices = m_config->parseFile(PROJECT_ROOT_DIR "/samples/test_config.xml", m_printer); m_printer->setModelChangeTime(getCurrentTime(GMT_UV_SEC)); ASSERT_FALSE(m_printer->getModelChangeTime().empty()); From ff97545285da4fa53411600db5626455e07f76d5 Mon Sep 17 00:00:00 2001 From: Will Sobel Date: Tue, 15 Nov 2022 17:08:53 +0100 Subject: [PATCH 3/6] Removed dup test exection --- test/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 11b70845b..e1e296d79 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -120,7 +120,6 @@ macro(add_agent_test AGENT_TEST_NAME ADD_TEST_HELPER SUB_FOLDER) target_compile_definitions(${AGENT_TEST_NAME}_test PRIVATE "PROJECT_ROOT_DIR=\"${CMAKE_SOURCE_DIR}\"" "TEST_BIN_ROOT_DIR=\"$/../Resources\"") - add_test(NAME ${AGENT_TEST_NAME}_test COMMAND ${AGENT_TEST_NAME}_test) target_compile_features(${AGENT_TEST_NAME}_test PUBLIC ${CXX_COMPILE_FEATURES}) # Organize into folders From 467a9819321b335be96a2e2684e900d3dd64c524 Mon Sep 17 00:00:00 2001 From: Will Sobel Date: Tue, 15 Nov 2022 17:57:20 +0100 Subject: [PATCH 4/6] Cleaned up use of schema version --- src/agent.cpp | 35 ++++++++++++----------------------- src/agent.hpp | 1 - src/parser/xml_parser.cpp | 10 +--------- src/parser/xml_parser.hpp | 2 -- src/utilities.hpp | 24 ++++++++++++++++++++++++ 5 files changed, 37 insertions(+), 35 deletions(-) diff --git a/src/agent.cpp b/src/agent.cpp index 3925dcc81..6997a9b5a 100644 --- a/src/agent.cpp +++ b/src/agent.cpp @@ -107,11 +107,6 @@ namespace mtconnect { if (m_schemaVersion) { - int major, minor; - char c; - stringstream vstr(*m_schemaVersion); - vstr >> major >> c >> minor; - m_version = major * 100 + minor; for (auto &[k, pr] : m_printers) pr->setSchemaVersion(*m_schemaVersion); } @@ -128,19 +123,15 @@ namespace mtconnect { auto devices = loadXMLDeviceFile(m_deviceXmlPath); if (!m_schemaVersion) { - m_schemaVersion.emplace(to_string(AGENT_VERSION_MAJOR) + "." + to_string(AGENT_VERSION_MINOR)); + m_schemaVersion.emplace(StrDefaultSchemaVersion()); } + + auto version = IntSchemaVersion(*m_schemaVersion); for (auto &[k, pr] : m_printers) pr->setSchemaVersion(*m_schemaVersion); - - int major, minor; - char c; - stringstream vstr(*m_schemaVersion); - vstr >> major >> c >> minor; - m_version = major * 100 + minor; auto disableAgentDevice = GetOption(m_options, config::DisableAgentDevice); - if (!(disableAgentDevice && *disableAgentDevice) && m_version >= 107) + if (!(disableAgentDevice && *disableAgentDevice) && version >= SCHEMA_VERSION(1, 7)) { createAgentDevice(); } @@ -309,13 +300,12 @@ namespace mtconnect { deviceFile, dynamic_cast(m_printers["xml"].get())); if (m_xmlParser->getSchemaVersion() && - m_xmlParser->getVersion() != m_version) + IntSchemaVersion(*m_xmlParser->getSchemaVersion()) != IntSchemaVersion(*m_schemaVersion)) { LOG(info) << "Got version: " << *(m_xmlParser->getSchemaVersion()); LOG(warning) << "Schema version does not match agent schema version, restarting the agent"; return false; } - // Fir the DeviceAdded event for each device bool changed = false; @@ -639,11 +629,10 @@ namespace mtconnect { if (!m_schemaVersion && m_xmlParser->getSchemaVersion()) { m_schemaVersion = m_xmlParser->getSchemaVersion(); - m_version = m_xmlParser->getVersion(); } else if (!m_schemaVersion && !m_xmlParser->getSchemaVersion()) { - m_version = AGENT_VERSION_MAJOR * 100 + AGENT_VERSION_MINOR; + m_schemaVersion = StrDefaultSchemaVersion(); } return devices; @@ -669,8 +658,8 @@ namespace mtconnect { void Agent::verifyDevice(DevicePtr device) { NAMED_SCOPE("Agent::verifyDevice"); - - auto xmlPrinter = dynamic_cast(m_printers["xml"].get()); + + auto version = IntSchemaVersion(*m_schemaVersion); // Add the devices to the device map and create availability and // asset changed events if they don't exist @@ -687,7 +676,7 @@ namespace mtconnect { device->addDataItem(di, errors); } - if (!device->getAssetChanged() && m_version >= 102) + if (!device->getAssetChanged() && version >= SCHEMA_VERSION(1, 2)) { entity::ErrorList errors; // Create asset change data item and add it to the device. @@ -698,14 +687,14 @@ namespace mtconnect { device->addDataItem(di, errors); } - if (device->getAssetChanged() && m_version >= 105) + if (device->getAssetChanged() && version >= SCHEMA_VERSION(1, 5)) { auto di = device->getAssetChanged(); if (!di->isDiscrete()) di->makeDiscrete(); } - if (!device->getAssetRemoved() && m_version >= 103) + if (!device->getAssetRemoved() && version >= SCHEMA_VERSION(1, 3)) { // Create asset removed data item and add it to the device. entity::ErrorList errors; @@ -716,7 +705,7 @@ namespace mtconnect { device->addDataItem(di, errors); } - if (!device->getAssetCount() && m_version >= 200) + if (!device->getAssetCount() && version >= SCHEMA_VERSION(2, 0)) { entity::ErrorList errors; auto di = DataItem::make({{"type", "ASSET_COUNT"s}, diff --git a/src/agent.hpp b/src/agent.hpp index 0ed7795ce..75b9dd6b6 100644 --- a/src/agent.hpp +++ b/src/agent.hpp @@ -299,7 +299,6 @@ namespace mtconnect { // Xml Config std::optional m_schemaVersion; - int m_version { 0 }; std::string m_deviceXmlPath; bool m_versionDeviceXml = false; diff --git a/src/parser/xml_parser.cpp b/src/parser/xml_parser.cpp index 6b3fcda2e..3fe52222d 100644 --- a/src/parser/xml_parser.cpp +++ b/src/parser/xml_parser.cpp @@ -137,15 +137,7 @@ namespace mtconnect::parser { { auto version = ns.substr(colon + 1); LOG(info) << "MTConnect Schema Version of file: " << filePath << " = " << version; - m_schemaVersion.emplace(version); - - int major, minor; - char c; - stringstream vstr(*m_schemaVersion); - vstr >> major >> c >> minor; - m_version = major * 100 + minor; - - LOG(debug) << "Numeric schema version: " << m_version; + m_schemaVersion.emplace(version); } } diff --git a/src/parser/xml_parser.hpp b/src/parser/xml_parser.hpp index e96bff087..23871c2b2 100644 --- a/src/parser/xml_parser.hpp +++ b/src/parser/xml_parser.hpp @@ -53,13 +53,11 @@ namespace mtconnect::parser { // Get std::list of data items in path void getDataItems(FilterSet &filterSet, const std::string &path, xmlNodePtr node = nullptr); const auto &getSchemaVersion() const { return m_schemaVersion; } - auto getVersion() const { return m_version; } protected: // LibXML XML Doc xmlDocPtr m_doc = nullptr; std::optional m_schemaVersion; - int m_version { 0 }; mutable std::shared_mutex m_mutex; }; } // namespace mtconnect::parser diff --git a/src/utilities.hpp b/src/utilities.hpp index 995219223..23d7b580d 100644 --- a/src/utilities.hpp +++ b/src/utilities.hpp @@ -74,6 +74,8 @@ typedef unsigned __int64 uint64_t; #include #include +#include + //####### CONSTANTS ####### // Port number to put server on @@ -574,4 +576,26 @@ namespace mtconnect { return camel; } + +#define SCHEMA_VERSION(major, minor) (major * 100 + minor) + + inline std::string StrDefaultSchemaVersion() + { + return std::to_string(AGENT_VERSION_MAJOR) + "." + std::to_string(AGENT_VERSION_MINOR); + } + + inline constexpr int32_t IntDefaultSchemaVersion() + { + return SCHEMA_VERSION(AGENT_VERSION_MAJOR, AGENT_VERSION_MINOR); + } + + inline int32_t IntSchemaVersion(const std::string &s) + { + int major, minor; + char c; + std::stringstream vstr(s); + vstr >> major >> c >> minor; + return SCHEMA_VERSION(major, minor); + + } } // namespace mtconnect From 6281d38a6c08af598b3c986694c7a9d172976831 Mon Sep 17 00:00:00 2001 From: Will Sobel Date: Tue, 15 Nov 2022 18:40:40 +0100 Subject: [PATCH 5/6] Sped up reload of device file when schema version changes --- src/configuration/agent_config.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/configuration/agent_config.cpp b/src/configuration/agent_config.cpp index 8cea8fe81..769ad376a 100644 --- a/src/configuration/agent_config.cpp +++ b/src/configuration/agent_config.cpp @@ -338,10 +338,21 @@ namespace mtconnect::configuration { m_context->pause([this](AsyncContext &context) { if (!m_agent->reloadDevices(m_devicesFile)) + { m_configTime.emplace(m_configTime->min()); + using namespace chrono; + using namespace chrono_literals; + + using boost::placeholders::_1; + + m_monitorTimer.expires_from_now(100ms); + m_monitorTimer.async_wait(boost::bind(&AgentConfiguration::monitorFiles, this, _1)); + } else + { m_deviceTime.reset(); - scheduleMonitorTimer(); + scheduleMonitorTimer(); + } }); } } From e25bc67f507ce5cd6d1d82ab2d6e9e92260420c4 Mon Sep 17 00:00:00 2001 From: Will Sobel Date: Tue, 15 Nov 2022 19:09:04 +0100 Subject: [PATCH 6/6] Fixed remaining warnings --- src/mqtt/mqtt_server_impl.hpp | 2 +- src/pipeline/mtconnect_xml_transform.hpp | 2 +- src/pipeline/response_document.cpp | 2 +- src/pipeline/response_document.hpp | 2 +- test/mqtt_sink_test.cpp | 5 ++--- test/test_utilities.hpp | 2 +- 6 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/mqtt/mqtt_server_impl.hpp b/src/mqtt/mqtt_server_impl.hpp index b1f5a3ac4..ca957c85b 100644 --- a/src/mqtt/mqtt_server_impl.hpp +++ b/src/mqtt/mqtt_server_impl.hpp @@ -114,7 +114,7 @@ namespace mtconnect { // It makes sure wp.lock() never return nullptr in the handlers below // including close_handler and error_handler. ep.start_session(std::make_tuple(std::move(spep), std::move(g))); - ep.set_connect_handler([this, &server, wp](MQTT_NS::buffer client_id, + ep.set_connect_handler([this, wp](MQTT_NS::buffer client_id, MQTT_NS::optional username, MQTT_NS::optional password, MQTT_NS::optional, diff --git a/src/pipeline/mtconnect_xml_transform.hpp b/src/pipeline/mtconnect_xml_transform.hpp index 726981804..a8aa4172c 100644 --- a/src/pipeline/mtconnect_xml_transform.hpp +++ b/src/pipeline/mtconnect_xml_transform.hpp @@ -28,7 +28,7 @@ namespace mtconnect::pipeline { struct XmlTransformFeedback { - int m_instanceId = 0; + uint64_t m_instanceId = 0; SequenceNumber_t m_next = 0; entity::EntityList m_assetEvents; ResponseDocument::Errors m_errors; diff --git a/src/pipeline/response_document.cpp b/src/pipeline/response_document.cpp index 18feb5733..3fb7ca56b 100644 --- a/src/pipeline/response_document.cpp +++ b/src/pipeline/response_document.cpp @@ -439,7 +439,7 @@ namespace mtconnect::pipeline { // xmlInitParser(); // xmlXPathInit(); unique_ptr> doc( - xmlReadMemory(content.data(), content.length(), "incoming.xml", nullptr, + xmlReadMemory(content.data(), static_cast(content.length()), "incoming.xml", nullptr, XML_PARSE_NOBLANKS), [](xmlDocPtr d) { xmlFreeDoc(d); }); xmlNodePtr root = xmlDocGetRootElement(doc.get()); diff --git a/src/pipeline/response_document.hpp b/src/pipeline/response_document.hpp index 0a5396a5f..f97b26572 100644 --- a/src/pipeline/response_document.hpp +++ b/src/pipeline/response_document.hpp @@ -40,7 +40,7 @@ namespace mtconnect::pipeline { // Parsed data SequenceNumber_t m_next; - int m_instanceId; + uint64_t m_instanceId; entity::EntityList m_entities; entity::EntityList m_assetEvents; Errors m_errors; diff --git a/test/mqtt_sink_test.cpp b/test/mqtt_sink_test.cpp index 0444daf0f..c3b5d7362 100644 --- a/test/mqtt_sink_test.cpp +++ b/test/mqtt_sink_test.cpp @@ -248,7 +248,7 @@ TEST_F(MqttSinkTest, mqtt_sink_should_publish_Streams) auto handler = make_unique(); bool foundLineDataItem = false; - handler->m_receive = [&foundLineDataItem, &parser](std::shared_ptr client, + handler->m_receive = [&foundLineDataItem](std::shared_ptr client, const std::string &topic, const std::string &payload) { EXPECT_EQ("MTConnect/Observation/000/Controller[Controller]/Path/Line[line]", topic); @@ -296,7 +296,7 @@ TEST_F(MqttSinkTest, mqtt_sink_should_publish_Asset) auto handler = make_unique(); bool gotControllerDataItem = false; - handler->m_receive = [&gotControllerDataItem, &parser](std::shared_ptr, + handler->m_receive = [&gotControllerDataItem](std::shared_ptr, const std::string &topic, const std::string &payload) { EXPECT_EQ("MTConnect/Asset/0001", topic); @@ -319,7 +319,6 @@ TEST_F(MqttSinkTest, mqtt_sink_should_publish_Asset) createAgent(); auto service = m_agentTestHelper->getMqttService(); ASSERT_TRUE(waitFor(1s, [&service]() { return service->isConnected(); })); - auto time = chrono::system_clock::now(); m_agentTestHelper->m_adapter->processData( "2021-02-01T12:00:00Z|@ASSET@|@1|Part|TEST 1"); diff --git a/test/test_utilities.hpp b/test/test_utilities.hpp index 920b48dac..b6dbdb298 100644 --- a/test/test_utilities.hpp +++ b/test/test_utilities.hpp @@ -46,7 +46,7 @@ void xpathTest(xmlDocPtr doc, const char *xpath, const char *expected, const std #define PARSE_XML(expr) \ string result = expr; \ - auto doc = xmlParseMemory(result.c_str(), result.length()); \ + auto doc = xmlParseMemory(result.c_str(), static_cast(result.length())); \ ASSERT_TRUE(doc); /// Asserts that two XML strings are equivalent.