diff --git a/agent_lib/CMakeLists.txt b/agent_lib/CMakeLists.txt index dc8316917..0943f4de6 100644 --- a/agent_lib/CMakeLists.txt +++ b/agent_lib/CMakeLists.txt @@ -25,6 +25,9 @@ set(AGENT_SOURCES "${SOURCE_DIR}/asset/raw_material.hpp" "${SOURCE_DIR}/asset/qif_document.hpp" "${SOURCE_DIR}/asset/component_configuration_parameters.hpp" + "${SOURCE_DIR}/asset/physical_asset.hpp" + "${SOURCE_DIR}/asset/fixture.hpp" + "${SOURCE_DIR}/asset/pallet.hpp" # src/asset SOURCE_FILES_ONLY @@ -34,6 +37,9 @@ set(AGENT_SOURCES "${SOURCE_DIR}/asset/raw_material.cpp" "${SOURCE_DIR}/asset/qif_document.cpp" "${SOURCE_DIR}/asset/component_configuration_parameters.cpp" + "${SOURCE_DIR}/asset/physical_asset.cpp" + "${SOURCE_DIR}/asset/fixture.cpp" + "${SOURCE_DIR}/asset/pallet.cpp" # src/buffer HEADER_FILES_ONLY diff --git a/src/mtconnect/agent.cpp b/src/mtconnect/agent.cpp index 2cdc88cdf..49182279c 100644 --- a/src/mtconnect/agent.cpp +++ b/src/mtconnect/agent.cpp @@ -46,6 +46,8 @@ #include "mtconnect/asset/component_configuration_parameters.hpp" #include "mtconnect/asset/cutting_tool.hpp" #include "mtconnect/asset/file_asset.hpp" +#include "mtconnect/asset/fixture.hpp" +#include "mtconnect/asset/pallet.hpp" #include "mtconnect/asset/qif_document.hpp" #include "mtconnect/asset/raw_material.hpp" #include "mtconnect/configuration/config_options.hpp" @@ -98,6 +100,8 @@ namespace mtconnect { RawMaterial::registerAsset(); QIFDocumentWrapper::registerAsset(); ComponentConfigurationParameters::registerAsset(); + Pallet::registerAsset(); + Fixture::registerAsset(); m_assetStorage = make_unique( GetOption(options, mtconnect::configuration::MaxAssets).value_or(1024)); @@ -242,7 +246,7 @@ namespace mtconnect { // Start all the sources for (auto source : m_sources) source->start(); - + m_afterStartHooks.exec(*this); } catch (std::runtime_error &e) @@ -478,11 +482,11 @@ namespace mtconnect { changed = receiveDevice(device, true) || changed; if (changed) { - if (source) - { - auto s = findSource(*source); - if (s) + if (source) { + auto s = findSource(*source); + if (s) + { s->setOptions({{config::Device, uuid}}); } } @@ -1018,7 +1022,7 @@ namespace mtconnect { if (m_agentDevice) { auto d = m_agentDevice->getDeviceDataItem("device_removed"); - if (d) + if (d) m_loopback->receive(d, oldUuid); } } @@ -1465,12 +1469,12 @@ namespace mtconnect { { try { - double fact_value = stod(factor); - double off_value = stod(offset); + double fact_value = stod(factor); + double off_value = stod(offset); - device_model::data_item::UnitConversion conv(fact_value, off_value); - di->setConverter(conv); - } + device_model::data_item::UnitConversion conv(fact_value, off_value); + di->setConverter(conv); + } catch (std::exception e) { LOG(error) << "Cannot convert factor " << factor << " or " << offset diff --git a/src/mtconnect/agent.hpp b/src/mtconnect/agent.hpp index de9f1a85d..3556d02b4 100644 --- a/src/mtconnect/agent.hpp +++ b/src/mtconnect/agent.hpp @@ -112,7 +112,7 @@ namespace mtconnect { /// @brief Hooks to run when before the agent starts all the soures and sinks /// @return configuration::HookManager& auto &beforeStartHooks() { return m_beforeStartHooks; } - + /// @brief Hooks to run when after the agent starts all the soures and sinks /// @return configuration::HookManager& auto &afterStartHooks() { return m_afterStartHooks; } @@ -663,7 +663,7 @@ namespace mtconnect { } buffer::CircularBuffer &getCircularBuffer() override { return m_agent->getCircularBuffer(); } - + configuration::HookManager &getHooks(HookType type) override { using namespace sink; @@ -672,7 +672,7 @@ namespace mtconnect { case BEFORE_START: return m_agent->beforeStartHooks(); break; - + case AFTER_START: return m_agent->afterStartHooks(); break; @@ -680,33 +680,32 @@ namespace mtconnect { case BEFORE_STOP: return m_agent->beforeStopHooks(); break; - + case BEFORE_DEVICE_XML_UPDATE: return m_agent->beforeDeviceXmlUpdateHooks(); break; - + case AFTER_DEVICE_XML_UPDATE: return m_agent->afterDeviceXmlUpdateHooks(); break; - + case BEFORE_INITIALIZE: return m_agent->beforeInitializeHooks(); break; - + case AFTER_INITIALIZE: return m_agent->afterInitializeHooks(); break; } - + LOG(error) << "getHooks: Bad hook manager type given to sink contract"; throw std::runtime_error("getHooks: Bad hook manager type"); - + // Never gets here. static configuration::HookManager NullHooks; return NullHooks; } - protected: Agent *m_agent; }; diff --git a/src/mtconnect/asset/cutting_tool.cpp b/src/mtconnect/asset/cutting_tool.cpp index 43d5303af..245f0b2a6 100644 --- a/src/mtconnect/asset/cutting_tool.cpp +++ b/src/mtconnect/asset/cutting_tool.cpp @@ -53,18 +53,7 @@ namespace mtconnect { Requirement("nominal", ValueType::DOUBLE, false), Requirement("VALUE", ValueType::DOUBLE, false)})); - static auto measurement = make_shared(Requirements( - {Requirement("significantDigits", ValueType::INTEGER, false), - Requirement("units", false), Requirement("nativeUnits", false), - Requirement("code", false), Requirement("maximum", ValueType::DOUBLE, false), - Requirement("minimum", ValueType::DOUBLE, false), - Requirement("nominal", ValueType::DOUBLE, false), - Requirement("VALUE", ValueType::DOUBLE, false)})); - - static auto measurements = make_shared(Requirements({Requirement( - "Measurement", ValueType::ENTITY, measurement, 1, Requirement::Infinite)})); - measurements->registerMatchers(); - measurements->registerFactory(regex(".+"), measurement); + static auto measurements = PhysicalAsset::getMeasurementsFactory()->deepCopy(); static auto ext = make_shared(); ext->registerFactory(regex(".+"), ext); @@ -83,7 +72,6 @@ namespace mtconnect { item->registerFactory(regex(".+"), ext); item->setAny(true); - measurements->registerMatchers(); item->setOrder({"Description", "CutterStatus", "Locus", "ItemLife", "ProgramToolGroup", "Measurements"}); diff --git a/src/mtconnect/asset/cutting_tool.hpp b/src/mtconnect/asset/cutting_tool.hpp index 7eec093e6..969a01270 100644 --- a/src/mtconnect/asset/cutting_tool.hpp +++ b/src/mtconnect/asset/cutting_tool.hpp @@ -22,6 +22,7 @@ #include #include "asset.hpp" +#include "mtconnect/asset/physical_asset.hpp" #include "mtconnect/config.hpp" #include "mtconnect/utilities.hpp" diff --git a/src/mtconnect/asset/fixture.cpp b/src/mtconnect/asset/fixture.cpp new file mode 100644 index 000000000..16bf509a8 --- /dev/null +++ b/src/mtconnect/asset/fixture.cpp @@ -0,0 +1,54 @@ +// +// Copyright Copyright 2009-2024, AMT – The Association For Manufacturing Technology (“AMT”) +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "fixture.hpp" + +using namespace std; + +namespace mtconnect::asset { + using namespace entity; + + FactoryPtr Fixture::getFactory() + { + static FactoryPtr factory; + if (!factory) + { + factory = make_shared(*PhysicalAsset::getFactory()); + factory->addRequirements(Requirements {{"FixtureId", ValueType::STRING, false}, + {"FixtureNumber", ValueType::INTEGER, false}, + {"ClampingMethod", ValueType::STRING, false}, + {"MountingMethod", ValueType::STRING, false}}); + + factory->setOrder({"ManufactureDate", "CalibrationDate", "InspectionDate", + "NextInspectionDate", "Measurements", "FixtureId", "FixtureNumber", + "ClampingMethod", "MountingMethod"}); + } + + return factory; + } + + void Fixture::registerAsset() + { + static bool once {true}; + if (once) + { + Asset::registerAssetType("Fixture", getFactory()); + once = false; + } + } + +} // namespace mtconnect::asset diff --git a/src/mtconnect/asset/fixture.hpp b/src/mtconnect/asset/fixture.hpp new file mode 100644 index 000000000..5823f9432 --- /dev/null +++ b/src/mtconnect/asset/fixture.hpp @@ -0,0 +1,37 @@ +// +// Copyright Copyright 2009-2024, AMT – The Association For Manufacturing Technology (“AMT”) +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#pragma once + +#include +#include +#include + +#include "mtconnect/entity/entity.hpp" +#include "mtconnect/entity/factory.hpp" +#include "mtconnect/utilities.hpp" +#include "physical_asset.hpp" + +namespace mtconnect::asset { + /// @brief abstract Physical Asset + class AGENT_LIB_API Fixture : public PhysicalAsset + { + public: + static entity::FactoryPtr getFactory(); + static void registerAsset(); + }; +} // namespace mtconnect::asset diff --git a/src/mtconnect/asset/pallet.cpp b/src/mtconnect/asset/pallet.cpp new file mode 100644 index 000000000..1f3023620 --- /dev/null +++ b/src/mtconnect/asset/pallet.cpp @@ -0,0 +1,55 @@ +// +// Copyright Copyright 2009-2024, AMT – The Association For Manufacturing Technology (“AMT”) +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "pallet.hpp" + +using namespace std; + +namespace mtconnect::asset { + using namespace entity; + + FactoryPtr Pallet::getFactory() + { + static FactoryPtr factory; + if (!factory) + { + factory = make_shared(*PhysicalAsset::getFactory()); + factory->addRequirements(Requirements {{"Type", ValueType::STRING, false}, + {"PalletId", ValueType::STRING, false}, + {"PalletNumber", ValueType::INTEGER, false}, + {"ClampingMethod", ValueType::STRING, false}, + {"MountingMethod", ValueType::STRING, false}}); + + factory->setOrder({"ManufactureDate", "CalibrationDate", "InspectionDate", + "NextInspectionDate", "Measurements", "Type", "PalletId", "PalletNumber", + "ClampingMethod", "MountingMethod"}); + } + + return factory; + } + + void Pallet::registerAsset() + { + static bool once {true}; + if (once) + { + Asset::registerAssetType("Pallet", getFactory()); + once = false; + } + } + +} // namespace mtconnect::asset diff --git a/src/mtconnect/asset/pallet.hpp b/src/mtconnect/asset/pallet.hpp new file mode 100644 index 000000000..77894aaf4 --- /dev/null +++ b/src/mtconnect/asset/pallet.hpp @@ -0,0 +1,37 @@ +// +// Copyright Copyright 2009-2024, AMT – The Association For Manufacturing Technology (“AMT”) +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#pragma once + +#include +#include +#include + +#include "mtconnect/entity/entity.hpp" +#include "mtconnect/entity/factory.hpp" +#include "mtconnect/utilities.hpp" +#include "physical_asset.hpp" + +namespace mtconnect::asset { + /// @brief abstract Physical Asset + class AGENT_LIB_API Pallet : public PhysicalAsset + { + public: + static entity::FactoryPtr getFactory(); + static void registerAsset(); + }; +} // namespace mtconnect::asset diff --git a/src/mtconnect/asset/physical_asset.cpp b/src/mtconnect/asset/physical_asset.cpp new file mode 100644 index 000000000..c5e413d0b --- /dev/null +++ b/src/mtconnect/asset/physical_asset.cpp @@ -0,0 +1,82 @@ +// +// Copyright Copyright 2009-2024, AMT – The Association For Manufacturing Technology (“AMT”) +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "mtconnect/asset/physical_asset.hpp" + +using namespace std; + +namespace mtconnect::asset { + using namespace entity; + + FactoryPtr PhysicalAsset::getMeasurementsFactory() + { + static FactoryPtr measurements; + + if (!measurements) + { + static auto measurement = make_shared(Requirements( + {Requirement("significantDigits", ValueType::INTEGER, false), Requirement("units", false), + Requirement("nativeUnits", false), Requirement("code", false), + Requirement("maximum", ValueType::DOUBLE, false), + Requirement("minimum", ValueType::DOUBLE, false), + Requirement("nominal", ValueType::DOUBLE, false), + Requirement("VALUE", ValueType::DOUBLE, false)})); + + measurements = make_shared(Requirements( + {Requirement("Measurement", ValueType::ENTITY, measurement, 1, Requirement::Infinite)})); + measurements->registerMatchers(); + measurements->registerFactory(regex(".+"), measurement); + } + return measurements; + } + + FactoryPtr PhysicalAsset::getFactory() + { + static FactoryPtr factory; + if (!factory) + { + static auto measurements = getMeasurementsFactory()->deepCopy(); + + factory = make_shared(*Asset::getFactory()); + factory->addRequirements( + Requirements {{"ManufactureDate", ValueType::TIMESTAMP, false}, + {"CalibrationDate", ValueType::TIMESTAMP, false}, + {"InspectionDate", ValueType::TIMESTAMP, false}, + {"NextInspectionDate", ValueType::TIMESTAMP, false}, + {"Measurements", ValueType::ENTITY_LIST, measurements, false}}); + + auto meas = measurements->factoryFor("Measurement"); + meas->getRequirement("VALUE")->makeRequired(); + + factory->setOrder({"ManufactureDate", "CalibrationDate", "InspectionDate", + "NextInspectionDate", "Measurements"}); + } + + return factory; + } + + void PhysicalAsset::registerAsset() + { + static bool once {true}; + if (once) + { + Asset::registerAssetType("PhysicalAsset", getFactory()); + once = false; + } + } + +} // namespace mtconnect::asset diff --git a/src/mtconnect/asset/physical_asset.hpp b/src/mtconnect/asset/physical_asset.hpp new file mode 100644 index 000000000..216bc73ca --- /dev/null +++ b/src/mtconnect/asset/physical_asset.hpp @@ -0,0 +1,39 @@ +// +// Copyright Copyright 2009-2024, AMT – The Association For Manufacturing Technology (“AMT”) +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#pragma once + +#include +#include +#include + +#include "asset.hpp" +#include "mtconnect/config.hpp" +#include "mtconnect/entity/entity.hpp" +#include "mtconnect/entity/factory.hpp" +#include "mtconnect/utilities.hpp" + +namespace mtconnect::asset { + /// @brief abstract Physical Asset + class AGENT_LIB_API PhysicalAsset : public Asset + { + public: + static entity::FactoryPtr getFactory(); + static void registerAsset(); + static entity::FactoryPtr getMeasurementsFactory(); + }; +} // namespace mtconnect::asset diff --git a/src/mtconnect/configuration/agent_config.cpp b/src/mtconnect/configuration/agent_config.cpp index 8d9aae894..19b576fd1 100644 --- a/src/mtconnect/configuration/agent_config.cpp +++ b/src/mtconnect/configuration/agent_config.cpp @@ -430,7 +430,7 @@ namespace mtconnect::configuration { void AgentConfiguration::setLoggingLevel(const logr::trivial::severity_level level) { - for ( auto &[channelName, logChannel] : m_logChannels) + for (auto &[channelName, logChannel] : m_logChannels) logChannel.m_logLevel = level; } @@ -483,7 +483,7 @@ namespace mtconnect::configuration { logr::add_common_attributes(); core->add_global_attribute("Scope", logr::attributes::named_scope()); core->add_global_attribute(logr::aux::default_attribute_names::thread_id(), - logr::attributes::current_thread_id()); + logr::attributes::current_thread_id()); core->add_global_attribute("Timestamp", logr::attributes::utc_clock()); m_logger = &::boost::log::trivial::logger::get(); @@ -498,7 +498,9 @@ namespace mtconnect::configuration { configureLoggerChannel("agent", config, formatter); } - void AgentConfiguration::configureLoggerChannel(const std::string &channelName, const ptree &config, std::optional> formatter) + void AgentConfiguration::configureLoggerChannel( + const std::string &channelName, const ptree &config, + std::optional> formatter) { using namespace logr::trivial; namespace expr = logr::expressions; @@ -510,10 +512,10 @@ namespace mtconnect::configuration { if (!formatter) { - formatter = - expr::stream << expr::format_date_time("Timestamp", - "%Y-%m-%dT%H:%M:%S.%fZ ") - << std::setw(7) << std::left << boost::log::trivial::severity << " " << expr::message; + formatter = expr::stream << expr::format_date_time( + "Timestamp", "%Y-%m-%dT%H:%M:%S.%fZ ") + << std::setw(7) << std::left << boost::log::trivial::severity << " " + << expr::message; } ptree empty; @@ -553,11 +555,13 @@ namespace mtconnect::configuration { logChannel.m_logLevel = level; logChannel.m_logFileName = output.value_or("debug"); - sink->locked_backend()->add_stream(boost::shared_ptr(out, boost::null_deleter())); + sink->locked_backend()->add_stream( + boost::shared_ptr(out, boost::null_deleter())); sink->locked_backend()->auto_flush(true); sink->set_formatter(formatter.value()); - sink->set_filter(expr::attr("Channel") == logChannel.m_channelName && severity >= logChannel.m_logLevel); + sink->set_filter(expr::attr("Channel") == logChannel.m_channelName && + severity >= logChannel.m_logLevel); logr::core::get()->add_sink(sink); return; @@ -615,8 +619,7 @@ namespace mtconnect::configuration { logArchivePattern = fs::path(archive_pattern); if (!logArchivePattern.has_filename()) { - logArchivePattern = - logArchivePattern / archiveFileName(get(options["file_name"])); + logArchivePattern = logArchivePattern / archiveFileName(get(options["file_name"])); } if (logArchivePattern.is_relative()) @@ -648,16 +651,16 @@ namespace mtconnect::configuration { if (rotationLogInterval > 0) { - sink->locked_backend()->set_time_based_rotation( - logr::sinks::file::rotation_at_time_interval( - boost::posix_time::hours(rotationLogInterval))); + sink->locked_backend()->set_time_based_rotation(logr::sinks::file::rotation_at_time_interval( + boost::posix_time::hours(rotationLogInterval))); } // Upon restart, scan the target directory for files matching the file_name pattern sink->locked_backend()->scan_for_files(); sink->set_formatter(formatter.value()); - sink->set_filter(expr::attr("Channel") == logChannel.m_channelName && severity >= logChannel.m_logLevel); + sink->set_filter(expr::attr("Channel") == logChannel.m_channelName && + severity >= logChannel.m_logLevel); // Formatter for the logger logr::core::get()->add_sink(sink); @@ -949,7 +952,7 @@ namespace mtconnect::configuration { auto parsed = Url::parse(url); options[configuration::Protocol] = parsed.m_protocol; - + auto host = parsed.getHost(); if (host.empty()) { @@ -957,15 +960,14 @@ namespace mtconnect::configuration { exit(1); } options[configuration::Host] = host; - + if (parsed.m_port) options[configuration::Port] = parsed.getPort(); if (parsed.m_path != "/") { StringList list; string topics = parsed.m_path.substr(1, string::npos); - boost::split(list, topics, boost::is_any_of(":"), - boost::token_compress_on); + boost::split(list, topics, boost::is_any_of(":"), boost::token_compress_on); options[configuration::Topics] = list; } } diff --git a/src/mtconnect/configuration/agent_config.hpp b/src/mtconnect/configuration/agent_config.hpp index 071391472..eb0b51414 100644 --- a/src/mtconnect/configuration/agent_config.hpp +++ b/src/mtconnect/configuration/agent_config.hpp @@ -113,7 +113,9 @@ namespace mtconnect { /// @param channelName the log channel name /// @param config the configuration node /// @param formatter optional custom message format - void configureLoggerChannel(const std::string &channelName, const ptree &config, std::optional> formatter = std::nullopt); + void configureLoggerChannel( + const std::string &channelName, const ptree &config, + std::optional> formatter = std::nullopt); /// @brief Configure the agent logger with the config node from the config file /// @param config the configuration node @@ -155,22 +157,40 @@ namespace mtconnect { ///@{ /// @brief gets the boost log sink /// @return boost log sink - const auto &getLoggerSink(const std::string &channelName = "agent") { return m_logChannels[channelName].m_logSink; } + const auto &getLoggerSink(const std::string &channelName = "agent") + { + return m_logChannels[channelName].m_logSink; + } /// @brief gets the log directory /// @return log directory - const auto &getLogDirectory(const std::string &channelName = "agent") { return m_logChannels[channelName].m_logDirectory; } + const auto &getLogDirectory(const std::string &channelName = "agent") + { + return m_logChannels[channelName].m_logDirectory; + } /// @brief get the logging file name /// @return log file name - const auto &getLogFileName(const std::string &channelName = "agent") { return m_logChannels[channelName].m_logFileName; } + const auto &getLogFileName(const std::string &channelName = "agent") + { + return m_logChannels[channelName].m_logFileName; + } /// @brief for log rolling, get the log archive pattern /// @return log archive pattern - const auto &getLogArchivePattern(const std::string &channelName = "agent") { return m_logChannels[channelName].m_logArchivePattern; } + const auto &getLogArchivePattern(const std::string &channelName = "agent") + { + return m_logChannels[channelName].m_logArchivePattern; + } /// @brief Get the maximum size of all the log files /// @return the maximum size of all log files - auto getMaxLogFileSize(const std::string &channelName = "agent") { return m_logChannels[channelName].m_maxLogFileSize; } + auto getMaxLogFileSize(const std::string &channelName = "agent") + { + return m_logChannels[channelName].m_maxLogFileSize; + } /// @brief the maximum size of a log file when it triggers rolling over /// @return the maxumum site of a log file - auto getLogRotationSize(const std::string &channelName = "agent") { return m_logChannels[channelName].m_logRotationSize; } + auto getLogRotationSize(const std::string &channelName = "agent") + { + return m_logChannels[channelName].m_logRotationSize; + } /// @brief How often to roll over the log file /// /// One of: @@ -179,10 +199,16 @@ namespace mtconnect { /// - `NEVER` /// /// @return the log file interval - auto getRotationLogInterval(const std::string &channelName = "agent") { return m_logChannels[channelName].m_rotationLogInterval; } + auto getRotationLogInterval(const std::string &channelName = "agent") + { + return m_logChannels[channelName].m_rotationLogInterval; + } /// @brief Get the current log level /// @return log level - auto getLogLevel(const std::string &channelName = "agent") { return m_logChannels[channelName].m_logLevel; } + auto getLogLevel(const std::string &channelName = "agent") + { + return m_logChannels[channelName].m_logLevel; + } /// @brief set the logging level /// @param[in] level the new logging level @@ -311,7 +337,8 @@ namespace mtconnect { protected: using text_sink = boost::log::sinks::synchronous_sink; - using console_sink = boost::log::sinks::synchronous_sink; + using console_sink = + boost::log::sinks::synchronous_sink; struct LogChannel { diff --git a/src/mtconnect/entity/xml_parser.cpp b/src/mtconnect/entity/xml_parser.cpp index 9c7a37f78..26d4b4cde 100644 --- a/src/mtconnect/entity/xml_parser.cpp +++ b/src/mtconnect/entity/xml_parser.cpp @@ -258,7 +258,7 @@ namespace mtconnect::entity { if (root != nullptr) entity = parseXmlNode(factory, root, errors, parseNamespaces); else - errors.emplace_back(new EntityError("Cannot parse asset")); + errors.emplace_back(new EntityError("Cannot parse document")); } catch (EntityError e) diff --git a/src/mtconnect/logging.hpp b/src/mtconnect/logging.hpp index c0e1f9f73..7f7ac3714 100644 --- a/src/mtconnect/logging.hpp +++ b/src/mtconnect/logging.hpp @@ -21,21 +21,23 @@ #pragma once #include -#include -#include #include +#include +#include #include "mtconnect/config.hpp" -typedef boost::log::sources::severity_channel_logger_mt -< - boost::log::trivial::severity_level, // the type of the severity level - std::string // the type of the channel name -> channel_logger_mt; +typedef boost::log::sources::severity_channel_logger_mt< + boost::log::trivial::severity_level, // the type of the severity level + std::string // the type of the channel name + > + channel_logger_mt; -#define CHANNEL_LOGGER_INIT(logger, channelName) \ -BOOST_LOG_INLINE_GLOBAL_LOGGER_INIT(logger, channel_logger_mt) \ -{ return channel_logger_mt(boost::log::keywords::channel = channelName); } +#define CHANNEL_LOGGER_INIT(logger, channelName) \ + BOOST_LOG_INLINE_GLOBAL_LOGGER_INIT(logger, channel_logger_mt) \ + { \ + return channel_logger_mt(boost::log::keywords::channel = channelName); \ + } CHANNEL_LOGGER_INIT(agent_logger, "agent") @@ -45,4 +47,3 @@ CHANNEL_LOGGER_INIT(agent_logger, "agent") #define NAMED_SCOPE BOOST_LOG_NAMED_SCOPE #define LOG_LEVEL(lvl) ::boost::log::trivial::lvl - diff --git a/src/mtconnect/mqtt/mqtt_client_impl.hpp b/src/mtconnect/mqtt/mqtt_client_impl.hpp index 7b7155e50..850387b89 100644 --- a/src/mtconnect/mqtt/mqtt_client_impl.hpp +++ b/src/mtconnect/mqtt/mqtt_client_impl.hpp @@ -476,7 +476,7 @@ namespace mtconnect { { return static_pointer_cast(shared_from_this()); } - + /// @brief Get the Mqtt TCP Client /// @return pointer to the Mqtt TCP Client auto &getClient() @@ -558,7 +558,7 @@ namespace mtconnect { { return static_pointer_cast(shared_from_this()); } - + /// @brief Get the Mqtt TLS WebSocket Client /// @return pointer to the Mqtt TLS WebSocket Client auto &getClient() @@ -597,7 +597,7 @@ namespace mtconnect { { return static_pointer_cast(shared_from_this()); } - + /// @brief Get the Mqtt TLS WebSocket Client /// @return pointer to the Mqtt TLS WebSocket Client auto &getClient() diff --git a/src/mtconnect/sink/mqtt_sink/mqtt_service.cpp b/src/mtconnect/sink/mqtt_sink/mqtt_service.cpp index 72fbfd6e4..4d8fa5261 100644 --- a/src/mtconnect/sink/mqtt_sink/mqtt_service.cpp +++ b/src/mtconnect/sink/mqtt_sink/mqtt_service.cpp @@ -289,8 +289,8 @@ namespace mtconnect { m_currentTimer.expires_after(m_currentInterval); m_currentTimer.async_wait(boost::asio::bind_executor( m_strand, boost::bind(&MqttService::publishCurrent, this, _1))); - - return seq; + + return seq; } bool MqttService::publish(observation::ObservationPtr &observation) diff --git a/src/mtconnect/sink/rest_sink/rest_service.cpp b/src/mtconnect/sink/rest_sink/rest_service.cpp index 86a859f3c..f4b9bea7a 100644 --- a/src/mtconnect/sink/rest_sink/rest_service.cpp +++ b/src/mtconnect/sink/rest_sink/rest_service.cpp @@ -550,13 +550,13 @@ namespace mtconnect { auto format = request->parameter("format"); auto pretty = request->parameter("pretty").value_or(false); auto printer = getPrinter(request->m_accepts, format); - + list ids; stringstream str(*asset); string id; while (getline(str, id, ';')) ids.emplace_back(id); - + respond(session, assetIdsRequest(printer, ids, pretty, request->m_requestId), request->m_requestId); } diff --git a/src/mtconnect/sink/sink.hpp b/src/mtconnect/sink/sink.hpp index 4678cde16..416564933 100644 --- a/src/mtconnect/sink/sink.hpp +++ b/src/mtconnect/sink/sink.hpp @@ -28,10 +28,10 @@ #include "mtconnect/asset/asset_storage.hpp" #include "mtconnect/buffer/circular_buffer.hpp" #include "mtconnect/config.hpp" +#include "mtconnect/configuration/hook_manager.hpp" #include "mtconnect/device_model/device.hpp" #include "mtconnect/observation/observation.hpp" #include "mtconnect/printer//printer.hpp" -#include "mtconnect/configuration/hook_manager.hpp" namespace mtconnect { namespace printer { @@ -55,7 +55,8 @@ namespace mtconnect { class AGENT_LIB_API SinkContract { public: - enum HookType { + enum HookType + { BEFORE_STOP, BEFORE_START, AFTER_START, @@ -64,7 +65,7 @@ namespace mtconnect { BEFORE_INITIALIZE, AFTER_INITIALIZE }; - + virtual ~SinkContract() = default; /// @brief get the printer for a mime type. Current options: `xml` or `json`. /// @param[in] aType a string for the type @@ -114,7 +115,7 @@ namespace mtconnect { /// @brief Get a pointer to the asset storage /// @return a pointer to the asset storage. virtual const asset::AssetStorage *getAssetStorage() = 0; - + /// @brief Get a reference to the hook manager for the agent. /// @param[in] type the type manager to retrieve /// @return a reference to the hook manager diff --git a/src/mtconnect/validation/observation_validations.hpp b/src/mtconnect/validation/observation_validations.hpp index 0fbb4b078..1c19ba4fa 100644 --- a/src/mtconnect/validation/observation_validations.hpp +++ b/src/mtconnect/validation/observation_validations.hpp @@ -1,32 +1,77 @@ Validation ControlledVocabularies { {"ActiveAxes", {}}, - {"ActuatorState", {{"ACTIVE", { SCHEMA_VERSION(1, 2), 0} }, {"INACTIVE", { SCHEMA_VERSION(1, 2), 0} }}}, + {"ActuatorState", + {{"ACTIVE", {SCHEMA_VERSION(1, 2), 0}}, {"INACTIVE", {SCHEMA_VERSION(1, 2), 0}}}}, {"Alarm", {}}, {"AssetChanged", {}}, {"AssetRemoved", {}}, - {"Availability", {{"AVAILABLE", { SCHEMA_VERSION(1, 1), 0} }, {"UNAVAILABLE", { SCHEMA_VERSION(1, 1), 0} }}}, - {"AxisCoupling", {{"MASTER", { SCHEMA_VERSION(1, 1), 0} }, {"SLAVE", { SCHEMA_VERSION(1, 1), 0} }, {"SYNCHRONOUS", { SCHEMA_VERSION(1, 1), 0} }, {"TANDEM", { SCHEMA_VERSION(1, 1), 0} }}}, + {"Availability", + {{"AVAILABLE", {SCHEMA_VERSION(1, 1), 0}}, {"UNAVAILABLE", {SCHEMA_VERSION(1, 1), 0}}}}, + {"AxisCoupling", + {{"MASTER", {SCHEMA_VERSION(1, 1), 0}}, + {"SLAVE", {SCHEMA_VERSION(1, 1), 0}}, + {"SYNCHRONOUS", {SCHEMA_VERSION(1, 1), 0}}, + {"TANDEM", {SCHEMA_VERSION(1, 1), 0}}}}, {"AxisFeedrateOverride", {}}, - {"AxisInterlock", {{"ACTIVE", { SCHEMA_VERSION(1, 3), 0} }, {"INACTIVE", { SCHEMA_VERSION(1, 3), 0} }}}, - {"AxisState", {{"HOME", { SCHEMA_VERSION(1, 3), 0} }, {"PARKED", { SCHEMA_VERSION(1, 3), 0} }, {"STOPPED", { SCHEMA_VERSION(1, 3), 0} }, {"TRAVEL", { SCHEMA_VERSION(1, 3), 0} }}}, + {"AxisInterlock", + {{"ACTIVE", {SCHEMA_VERSION(1, 3), 0}}, {"INACTIVE", {SCHEMA_VERSION(1, 3), 0}}}}, + {"AxisState", + {{"HOME", {SCHEMA_VERSION(1, 3), 0}}, + {"PARKED", {SCHEMA_VERSION(1, 3), 0}}, + {"STOPPED", {SCHEMA_VERSION(1, 3), 0}}, + {"TRAVEL", {SCHEMA_VERSION(1, 3), 0}}}}, {"Block", {}}, {"BlockCount", {}}, - {"ChuckInterlock", {{"ACTIVE", { SCHEMA_VERSION(1, 3), 0} }, {"INACTIVE", { SCHEMA_VERSION(1, 3), 0} }}}, - {"ChuckState", {{"CLOSED", { SCHEMA_VERSION(1, 3), 0} }, {"OPEN", { SCHEMA_VERSION(1, 3), 0} }, {"UNLATCHED", { SCHEMA_VERSION(1, 3), 0} }}}, + {"ChuckInterlock", + {{"ACTIVE", {SCHEMA_VERSION(1, 3), 0}}, {"INACTIVE", {SCHEMA_VERSION(1, 3), 0}}}}, + {"ChuckState", + {{"CLOSED", {SCHEMA_VERSION(1, 3), 0}}, + {"OPEN", {SCHEMA_VERSION(1, 3), 0}}, + {"UNLATCHED", {SCHEMA_VERSION(1, 3), 0}}}}, {"Code", {}}, {"CompositionState", {}}, - {"ControllerMode", {{"AUTOMATIC", { SCHEMA_VERSION(1, 0), 0} }, {"MANUAL", { SCHEMA_VERSION(1, 0), 0} }, {"MANUAL_DATA_INPUT", { SCHEMA_VERSION(1, 0), 0} }, {"SEMI_AUTOMATIC", { SCHEMA_VERSION(1, 1), 0} }, {"FEED_HOLD", { SCHEMA_VERSION(1, 2), SCHEMA_VERSION(1, 3)} }, {"EDIT", { SCHEMA_VERSION(1, 3), 0} }}}, - {"ControllerModeOverride", {{"OFF", { SCHEMA_VERSION(1, 4), 0} }, {"ON", { SCHEMA_VERSION(1, 4), 0} }}}, + {"ControllerMode", + {{"AUTOMATIC", {SCHEMA_VERSION(1, 0), 0}}, + {"MANUAL", {SCHEMA_VERSION(1, 0), 0}}, + {"MANUAL_DATA_INPUT", {SCHEMA_VERSION(1, 0), 0}}, + {"SEMI_AUTOMATIC", {SCHEMA_VERSION(1, 1), 0}}, + {"FEED_HOLD", {SCHEMA_VERSION(1, 2), SCHEMA_VERSION(1, 3)}}, + {"EDIT", {SCHEMA_VERSION(1, 3), 0}}}}, + {"ControllerModeOverride", + {{"OFF", {SCHEMA_VERSION(1, 4), 0}}, {"ON", {SCHEMA_VERSION(1, 4), 0}}}}, {"CoupledAxes", {}}, {"DateCode", {}}, {"DeviceUuid", {}}, - {"Direction", {{"CLOCKWISE", { SCHEMA_VERSION(1, 0), SCHEMA_VERSION(1, 4)} }, {"COUNTER_CLOCKWISE", { SCHEMA_VERSION(1, 0), SCHEMA_VERSION(1, 4)} }, {"NEGATIVE", { SCHEMA_VERSION(1, 2), SCHEMA_VERSION(1, 4)} }, {"POSITIVE", { SCHEMA_VERSION(1, 2), SCHEMA_VERSION(1, 4)} }}}, - {"DoorState", {{"CLOSED", { SCHEMA_VERSION(1, 1), 0} }, {"OPEN", { SCHEMA_VERSION(1, 1), 0} }, {"UNLATCHED", { SCHEMA_VERSION(1, 2), 0} }}}, - {"EmergencyStop", {{"ARMED", { SCHEMA_VERSION(1, 1), 0} }, {"TRIGGERED", { SCHEMA_VERSION(1, 1), 0} }}}, - {"EndOfBar", {{"NO", { SCHEMA_VERSION(1, 3), 0} }, {"YES", { SCHEMA_VERSION(1, 3), 0} }}}, - {"EquipmentMode", {{"OFF", { SCHEMA_VERSION(1, 4), 0} }, {"ON", { SCHEMA_VERSION(1, 4), 0} }}}, - {"Execution", {{"ACTIVE", { SCHEMA_VERSION(1, 0), 0} }, {"INTERRUPTED", { SCHEMA_VERSION(1, 0), 0} }, {"READY", { SCHEMA_VERSION(1, 0), 0} }, {"STOPPED", { SCHEMA_VERSION(1, 0), 0} }, {"FEED_HOLD", { SCHEMA_VERSION(1, 3), 0} }, {"PROGRAM_COMPLETED", { SCHEMA_VERSION(1, 3), 0} }, {"PROGRAM_OPTIONAL_STOP", { SCHEMA_VERSION(1, 3), SCHEMA_VERSION(1, 4)} }, {"PROGRAM_STOPPED", { SCHEMA_VERSION(1, 3), 0} }, {"OPTIONAL_STOP", { SCHEMA_VERSION(1, 4), 0} }, {"WAIT", { SCHEMA_VERSION(1, 5), 0} }}}, - {"FunctionalMode", {{"MAINTENANCE", { SCHEMA_VERSION(1, 3), 0} }, {"PROCESS_DEVELOPMENT", { SCHEMA_VERSION(1, 3), 0} }, {"PRODUCTION", { SCHEMA_VERSION(1, 3), 0} }, {"SETUP", { SCHEMA_VERSION(1, 3), 0} }, {"TEARDOWN", { SCHEMA_VERSION(1, 3), 0} }}}, + {"Direction", + {{"CLOCKWISE", {SCHEMA_VERSION(1, 0), SCHEMA_VERSION(1, 4)}}, + {"COUNTER_CLOCKWISE", {SCHEMA_VERSION(1, 0), SCHEMA_VERSION(1, 4)}}, + {"NEGATIVE", {SCHEMA_VERSION(1, 2), SCHEMA_VERSION(1, 4)}}, + {"POSITIVE", {SCHEMA_VERSION(1, 2), SCHEMA_VERSION(1, 4)}}}}, + {"DoorState", + {{"CLOSED", {SCHEMA_VERSION(1, 1), 0}}, + {"OPEN", {SCHEMA_VERSION(1, 1), 0}}, + {"UNLATCHED", {SCHEMA_VERSION(1, 2), 0}}}}, + {"EmergencyStop", + {{"ARMED", {SCHEMA_VERSION(1, 1), 0}}, {"TRIGGERED", {SCHEMA_VERSION(1, 1), 0}}}}, + {"EndOfBar", {{"NO", {SCHEMA_VERSION(1, 3), 0}}, {"YES", {SCHEMA_VERSION(1, 3), 0}}}}, + {"EquipmentMode", {{"OFF", {SCHEMA_VERSION(1, 4), 0}}, {"ON", {SCHEMA_VERSION(1, 4), 0}}}}, + {"Execution", + {{"ACTIVE", {SCHEMA_VERSION(1, 0), 0}}, + {"INTERRUPTED", {SCHEMA_VERSION(1, 0), 0}}, + {"READY", {SCHEMA_VERSION(1, 0), 0}}, + {"STOPPED", {SCHEMA_VERSION(1, 0), 0}}, + {"FEED_HOLD", {SCHEMA_VERSION(1, 3), 0}}, + {"PROGRAM_COMPLETED", {SCHEMA_VERSION(1, 3), 0}}, + {"PROGRAM_OPTIONAL_STOP", {SCHEMA_VERSION(1, 3), SCHEMA_VERSION(1, 4)}}, + {"PROGRAM_STOPPED", {SCHEMA_VERSION(1, 3), 0}}, + {"OPTIONAL_STOP", {SCHEMA_VERSION(1, 4), 0}}, + {"WAIT", {SCHEMA_VERSION(1, 5), 0}}}}, + {"FunctionalMode", + {{"MAINTENANCE", {SCHEMA_VERSION(1, 3), 0}}, + {"PROCESS_DEVELOPMENT", {SCHEMA_VERSION(1, 3), 0}}, + {"PRODUCTION", {SCHEMA_VERSION(1, 3), 0}}, + {"SETUP", {SCHEMA_VERSION(1, 3), 0}}, + {"TEARDOWN", {SCHEMA_VERSION(1, 3), 0}}}}, {"Hardness", {}}, {"Line", {}}, {"LineLabel", {}}, @@ -37,26 +82,41 @@ Validation ControlledVocabularies { {"OperatorId", {}}, {"PalletId", {}}, {"PartCount", {}}, - {"PartDetect", {{"NOT_PRESENT", { SCHEMA_VERSION(1, 5), 0} }, {"PRESENT", { SCHEMA_VERSION(1, 5), 0} }}}, + {"PartDetect", + {{"NOT_PRESENT", {SCHEMA_VERSION(1, 5), 0}}, {"PRESENT", {SCHEMA_VERSION(1, 5), 0}}}}, {"PartId", {}}, {"PartNumber", {}}, {"PathFeedrateOverride", {}}, - {"PathMode", {{"INDEPENDENT", { SCHEMA_VERSION(1, 1), 0} }, {"MIRROR", { SCHEMA_VERSION(1, 1), 0} }, {"SYNCHRONOUS", { SCHEMA_VERSION(1, 1), 0} }, {"MASTER", { SCHEMA_VERSION(1, 2), 0} }}}, - {"PowerState", {{"OFF", { SCHEMA_VERSION(1, 1), 0} }, {"ON", { SCHEMA_VERSION(1, 1), 0} }}}, - {"PowerStatus", {{"OFF", { SCHEMA_VERSION(1, 0), SCHEMA_VERSION(1, 1)} }, {"ON", { SCHEMA_VERSION(1, 0), SCHEMA_VERSION(1, 1)} }}}, + {"PathMode", + {{"INDEPENDENT", {SCHEMA_VERSION(1, 1), 0}}, + {"MIRROR", {SCHEMA_VERSION(1, 1), 0}}, + {"SYNCHRONOUS", {SCHEMA_VERSION(1, 1), 0}}, + {"MASTER", {SCHEMA_VERSION(1, 2), 0}}}}, + {"PowerState", {{"OFF", {SCHEMA_VERSION(1, 1), 0}}, {"ON", {SCHEMA_VERSION(1, 1), 0}}}}, + {"PowerStatus", + {{"OFF", {SCHEMA_VERSION(1, 0), SCHEMA_VERSION(1, 1)}}, + {"ON", {SCHEMA_VERSION(1, 0), SCHEMA_VERSION(1, 1)}}}}, {"ProcessTime", {}}, {"Program", {}}, {"ProgramComment", {}}, - {"ProgramEdit", {{"ACTIVE", { SCHEMA_VERSION(1, 3), 0} }, {"NOT_READY", { SCHEMA_VERSION(1, 3), 0} }, {"READY", { SCHEMA_VERSION(1, 3), 0} }}}, + {"ProgramEdit", + {{"ACTIVE", {SCHEMA_VERSION(1, 3), 0}}, + {"NOT_READY", {SCHEMA_VERSION(1, 3), 0}}, + {"READY", {SCHEMA_VERSION(1, 3), 0}}}}, {"ProgramEditName", {}}, {"ProgramHeader", {}}, {"ProgramLocation", {}}, - {"ProgramLocationType", {{"EXTERNAL", { SCHEMA_VERSION(1, 5), 0} }, {"LOCAL", { SCHEMA_VERSION(1, 5), 0} }}}, + {"ProgramLocationType", + {{"EXTERNAL", {SCHEMA_VERSION(1, 5), 0}}, {"LOCAL", {SCHEMA_VERSION(1, 5), 0}}}}, {"ProgramNestLevel", {}}, - {"RotaryMode", {{"CONTOUR", { SCHEMA_VERSION(1, 1), 0} }, {"INDEX", { SCHEMA_VERSION(1, 1), 0} }, {"SPINDLE", { SCHEMA_VERSION(1, 1), 0} }}}, + {"RotaryMode", + {{"CONTOUR", {SCHEMA_VERSION(1, 1), 0}}, + {"INDEX", {SCHEMA_VERSION(1, 1), 0}}, + {"SPINDLE", {SCHEMA_VERSION(1, 1), 0}}}}, {"RotaryVelocityOverride", {}}, {"SerialNumber", {}}, - {"SpindleInterlock", {{"ACTIVE", { SCHEMA_VERSION(1, 3), 0} }, {"INACTIVE", { SCHEMA_VERSION(1, 3), 0} }}}, + {"SpindleInterlock", + {{"ACTIVE", {SCHEMA_VERSION(1, 3), 0}}, {"INACTIVE", {SCHEMA_VERSION(1, 3), 0}}}}, {"ToolAssetId", {}}, {"ToolGroup", {}}, {"ToolId", {}}, @@ -64,7 +124,18 @@ Validation ControlledVocabularies { {"ToolOffset", {}}, {"User", {}}, {"Variable", {}}, - {"WaitState", {{"MATERIAL_LOAD", { SCHEMA_VERSION(1, 5), 0} }, {"MATERIAL_UNLOAD", { SCHEMA_VERSION(1, 5), 0} }, {"PART_LOAD", { SCHEMA_VERSION(1, 5), 0} }, {"PART_UNLOAD", { SCHEMA_VERSION(1, 5), 0} }, {"PAUSING", { SCHEMA_VERSION(1, 5), 0} }, {"POWERING_DOWN", { SCHEMA_VERSION(1, 5), 0} }, {"POWERING_UP", { SCHEMA_VERSION(1, 5), 0} }, {"RESUMING", { SCHEMA_VERSION(1, 5), 0} }, {"SECONDARY_PROCESS", { SCHEMA_VERSION(1, 5), 0} }, {"TOOL_LOAD", { SCHEMA_VERSION(1, 5), 0} }, {"TOOL_UNLOAD", { SCHEMA_VERSION(1, 5), 0} }}}, + {"WaitState", + {{"MATERIAL_LOAD", {SCHEMA_VERSION(1, 5), 0}}, + {"MATERIAL_UNLOAD", {SCHEMA_VERSION(1, 5), 0}}, + {"PART_LOAD", {SCHEMA_VERSION(1, 5), 0}}, + {"PART_UNLOAD", {SCHEMA_VERSION(1, 5), 0}}, + {"PAUSING", {SCHEMA_VERSION(1, 5), 0}}, + {"POWERING_DOWN", {SCHEMA_VERSION(1, 5), 0}}, + {"POWERING_UP", {SCHEMA_VERSION(1, 5), 0}}, + {"RESUMING", {SCHEMA_VERSION(1, 5), 0}}, + {"SECONDARY_PROCESS", {SCHEMA_VERSION(1, 5), 0}}, + {"TOOL_LOAD", {SCHEMA_VERSION(1, 5), 0}}, + {"TOOL_UNLOAD", {SCHEMA_VERSION(1, 5), 0}}}}, {"Wire", {}}, {"WorkholdingId", {}}, {"WorkOffset", {}}, @@ -77,7 +148,7 @@ Validation ControlledVocabularies { {"Rotation", {}}, {"Translation", {}}, {"ProcessKindId", {}}, - {"PartStatus", {{"FAIL", { SCHEMA_VERSION(1, 7), 0} }, {"PASS", { SCHEMA_VERSION(1, 7), 0} }}}, + {"PartStatus", {{"FAIL", {SCHEMA_VERSION(1, 7), 0}}, {"PASS", {SCHEMA_VERSION(1, 7), 0}}}}, {"AlarmLimit", {}}, {"ProcessAggregateId", {}}, {"PartKindId", {}}, @@ -85,7 +156,10 @@ Validation ControlledVocabularies { {"DeviceRemoved", {}}, {"DeviceChanged", {}}, {"SpecificationLimit", {}}, - {"ConnectionStatus", {{"CLOSED", { SCHEMA_VERSION(1, 7), 0} }, {"ESTABLISHED", { SCHEMA_VERSION(1, 7), 0} }, {"LISTEN", { SCHEMA_VERSION(1, 7), 0} }}}, + {"ConnectionStatus", + {{"CLOSED", {SCHEMA_VERSION(1, 7), 0}}, + {"ESTABLISHED", {SCHEMA_VERSION(1, 7), 0}}, + {"LISTEN", {SCHEMA_VERSION(1, 7), 0}}}}, {"AdapterSoftwareVersion", {}}, {"SensorAttachment", {}}, {"ControlLimit", {}}, @@ -98,22 +172,52 @@ Validation ControlledVocabularies { {"DeactivationCount", {}}, {"TransferCount", {}}, {"LoadCount", {}}, - {"PartProcessingState", {{"IN_PROCESS", { SCHEMA_VERSION(1, 8), 0} }, {"IN_TRANSIT", { SCHEMA_VERSION(1, 8), 0} }, {"NEEDS_PROCESSING", { SCHEMA_VERSION(1, 8), 0} }, {"PROCESSING_ENDED", { SCHEMA_VERSION(1, 8), 0} }, {"PROCESSING_ENDED_ABORTED", { SCHEMA_VERSION(1, 8), 0} }, {"PROCESSING_ENDED_COMPLETE", { SCHEMA_VERSION(1, 8), 0} }, {"PROCESSING_ENDED_LOST", { SCHEMA_VERSION(1, 8), 0} }, {"PROCESSING_ENDED_REJECTED", { SCHEMA_VERSION(1, 8), 0} }, {"PROCESSING_ENDED_SKIPPED", { SCHEMA_VERSION(1, 8), 0} }, {"PROCESSING_ENDED_STOPPED", { SCHEMA_VERSION(1, 8), 0} }, {"TRANSIT_COMPLETE", { SCHEMA_VERSION(1, 8), 0} }, {"WAITING_FOR_TRANSIT", { SCHEMA_VERSION(1, 8), 0} }}}, - {"ProcessState", {{"ABORTED", { SCHEMA_VERSION(1, 8), 0} }, {"ACTIVE", { SCHEMA_VERSION(1, 8), 0} }, {"COMPLETE", { SCHEMA_VERSION(1, 8), 0} }, {"INITIALIZING", { SCHEMA_VERSION(1, 8), 0} }, {"INTERRUPTED", { SCHEMA_VERSION(1, 8), 0} }, {"READY", { SCHEMA_VERSION(1, 8), 0} }}}, - {"ValveState", {{"CLOSED", { SCHEMA_VERSION(1, 8), 0} }, {"CLOSING", { SCHEMA_VERSION(1, 8), 0} }, {"OPEN", { SCHEMA_VERSION(1, 8), 0} }, {"OPENING", { SCHEMA_VERSION(1, 8), 0} }}}, - {"LockState", {{"LOCKED", { SCHEMA_VERSION(1, 8), 0} }, {"UNLOCKED", { SCHEMA_VERSION(1, 8), 0} }}}, + {"PartProcessingState", + {{"IN_PROCESS", {SCHEMA_VERSION(1, 8), 0}}, + {"IN_TRANSIT", {SCHEMA_VERSION(1, 8), 0}}, + {"NEEDS_PROCESSING", {SCHEMA_VERSION(1, 8), 0}}, + {"PROCESSING_ENDED", {SCHEMA_VERSION(1, 8), 0}}, + {"PROCESSING_ENDED_ABORTED", {SCHEMA_VERSION(1, 8), 0}}, + {"PROCESSING_ENDED_COMPLETE", {SCHEMA_VERSION(1, 8), 0}}, + {"PROCESSING_ENDED_LOST", {SCHEMA_VERSION(1, 8), 0}}, + {"PROCESSING_ENDED_REJECTED", {SCHEMA_VERSION(1, 8), 0}}, + {"PROCESSING_ENDED_SKIPPED", {SCHEMA_VERSION(1, 8), 0}}, + {"PROCESSING_ENDED_STOPPED", {SCHEMA_VERSION(1, 8), 0}}, + {"TRANSIT_COMPLETE", {SCHEMA_VERSION(1, 8), 0}}, + {"WAITING_FOR_TRANSIT", {SCHEMA_VERSION(1, 8), 0}}}}, + {"ProcessState", + {{"ABORTED", {SCHEMA_VERSION(1, 8), 0}}, + {"ACTIVE", {SCHEMA_VERSION(1, 8), 0}}, + {"COMPLETE", {SCHEMA_VERSION(1, 8), 0}}, + {"INITIALIZING", {SCHEMA_VERSION(1, 8), 0}}, + {"INTERRUPTED", {SCHEMA_VERSION(1, 8), 0}}, + {"READY", {SCHEMA_VERSION(1, 8), 0}}}}, + {"ValveState", + {{"CLOSED", {SCHEMA_VERSION(1, 8), 0}}, + {"CLOSING", {SCHEMA_VERSION(1, 8), 0}}, + {"OPEN", {SCHEMA_VERSION(1, 8), 0}}, + {"OPENING", {SCHEMA_VERSION(1, 8), 0}}}}, + {"LockState", {{"LOCKED", {SCHEMA_VERSION(1, 8), 0}}, {"UNLOCKED", {SCHEMA_VERSION(1, 8), 0}}}}, {"UnloadCount", {}}, {"CycleCount", {}}, - {"OperatingMode", {{"AUTOMATIC", { SCHEMA_VERSION(2, 0), 0} }, {"MANUAL", { SCHEMA_VERSION(2, 0), 0} }, {"SEMI_AUTOMATIC", { SCHEMA_VERSION(2, 0), 0} }}}, + {"OperatingMode", + {{"AUTOMATIC", {SCHEMA_VERSION(2, 0), 0}}, + {"MANUAL", {SCHEMA_VERSION(2, 0), 0}}, + {"SEMI_AUTOMATIC", {SCHEMA_VERSION(2, 0), 0}}}}, {"AssetCount", {}}, {"MaintenanceList", {}}, {"FixtureId", {}}, - {"PartCountType", {{"BATCH", { SCHEMA_VERSION(2, 0), 0} }, {"EACH", { SCHEMA_VERSION(2, 0), 0} }}}, + {"PartCountType", {{"BATCH", {SCHEMA_VERSION(2, 0), 0}}, {"EACH", {SCHEMA_VERSION(2, 0), 0}}}}, {"ClockTime", {}}, {"NetworkPort", {}}, {"HostName", {}}, - {"LeakDetect", {{"DETECTED", { SCHEMA_VERSION(2, 1), 0} }, {"NOT_DETECTED", { SCHEMA_VERSION(2, 1), 0} }}}, - {"BatteryState", {{"CHARGED", { SCHEMA_VERSION(2, 1), 0} }, {"CHARGING", { SCHEMA_VERSION(2, 1), 0} }, {"DISCHARGED", { SCHEMA_VERSION(2, 1), 0} }, {"DISCHARGING", { SCHEMA_VERSION(2, 1), 0} }}}, + {"LeakDetect", + {{"DETECTED", {SCHEMA_VERSION(2, 1), 0}}, {"NOT_DETECTED", {SCHEMA_VERSION(2, 1), 0}}}}, + {"BatteryState", + {{"CHARGED", {SCHEMA_VERSION(2, 1), 0}}, + {"CHARGING", {SCHEMA_VERSION(2, 1), 0}}, + {"DISCHARGED", {SCHEMA_VERSION(2, 1), 0}}, + {"DISCHARGING", {SCHEMA_VERSION(2, 1), 0}}}}, {"FeaturePersisitentId", {}}, {"SensorState", {}}, {"ComponentData", {}}, @@ -124,8 +228,17 @@ Validation ControlledVocabularies { {"MeasurementType", {}}, {"MeasurementValue", {}}, {"MeasurementUnits", {}}, - {"CharacteristicStatus", {{"BASIC_OR_THEORETIC_EXACT_DIMENSION", { SCHEMA_VERSION(2, 2), 0} }, {"FAIL", { SCHEMA_VERSION(2, 2), 0} }, {"INDETERMINATE", { SCHEMA_VERSION(2, 2), 0} }, {"NOT_ANALYZED", { SCHEMA_VERSION(2, 2), 0} }, {"PASS", { SCHEMA_VERSION(2, 2), 0} }, {"REWORK", { SCHEMA_VERSION(2, 2), 0} }, {"SYSTEM_ERROR", { SCHEMA_VERSION(2, 2), 0} }, {"UNDEFINED", { SCHEMA_VERSION(2, 2), 0} }}}, - {"UncertaintyType", {{"COMBINED", { SCHEMA_VERSION(2, 2), 0} }, {"MEAN", { SCHEMA_VERSION(2, 2), 0} }}}, + {"CharacteristicStatus", + {{"BASIC_OR_THEORETIC_EXACT_DIMENSION", {SCHEMA_VERSION(2, 2), 0}}, + {"FAIL", {SCHEMA_VERSION(2, 2), 0}}, + {"INDETERMINATE", {SCHEMA_VERSION(2, 2), 0}}, + {"NOT_ANALYZED", {SCHEMA_VERSION(2, 2), 0}}, + {"PASS", {SCHEMA_VERSION(2, 2), 0}}, + {"REWORK", {SCHEMA_VERSION(2, 2), 0}}, + {"SYSTEM_ERROR", {SCHEMA_VERSION(2, 2), 0}}, + {"UNDEFINED", {SCHEMA_VERSION(2, 2), 0}}}}, + {"UncertaintyType", + {{"COMBINED", {SCHEMA_VERSION(2, 2), 0}}, {"MEAN", {SCHEMA_VERSION(2, 2), 0}}}}, {"Uncertainty", {}}, {"AlarmLimits", {}}, {"ControlLimits", {}}, @@ -135,5 +248,4 @@ Validation ControlledVocabularies { {"ActivePowerSource", {}}, {"LocationNarrative", {}}, {"Thickness", {}}, - {"LocationSpatialGeographic", {}} -}; \ No newline at end of file + {"LocationSpatialGeographic", {}}}; \ No newline at end of file diff --git a/src/mtconnect/validation/observations.hpp b/src/mtconnect/validation/observations.hpp index c6df2c858..324f9ddd5 100644 --- a/src/mtconnect/validation/observations.hpp +++ b/src/mtconnect/validation/observations.hpp @@ -31,7 +31,9 @@ namespace mtconnect { namespace observations { /// @brief Validation type for observations - using Validation = std::unordered_map>>; + using Validation = + std::unordered_map>>; /// @brief Global Validations for Event Observation's Controlled Vocabularies /// diff --git a/test_package/CMakeLists.txt b/test_package/CMakeLists.txt index 05427a32d..c4f0a1a0a 100644 --- a/test_package/CMakeLists.txt +++ b/test_package/CMakeLists.txt @@ -218,6 +218,9 @@ add_agent_test(qif_document TRUE asset) add_agent_test(asset_buffer TRUE asset) add_agent_test(component_parameters TRUE asset) add_agent_test(asset_hash TRUE asset) +add_agent_test(physical_asset FALSE asset) +add_agent_test(pallet FALSE asset) +add_agent_test(fixture FALSE asset) add_agent_test(agent_device TRUE device_model) add_agent_test(component FALSE device_model) diff --git a/test_package/data_item_mapping_test.cpp b/test_package/data_item_mapping_test.cpp index 7c69b4637..a17523e1b 100644 --- a/test_package/data_item_mapping_test.cpp +++ b/test_package/data_item_mapping_test.cpp @@ -368,21 +368,21 @@ TEST_F(DataItemMappingTest, should_map_an_event_data_set) TEST_F(DataItemMappingTest, should_map_a_sample_data_set) { auto di = makeDataItem({{"id", "a"s}, - {"type", "SOMETHING"s}, - {"category", "SAMPLE"s}, - {"representation", "DATA_SET"s}}); - + {"type", "SOMETHING"s}, + {"category", "SAMPLE"s}, + {"representation", "DATA_SET"s}}); + auto ts = makeTimestamped({"a", "a=1 b=2 c={3}"}); auto observations = (*m_mapper)(ts); auto oblist = observations->getValue(); ASSERT_EQ(1, oblist.size()); - + auto set = dynamic_pointer_cast(oblist.front()); ASSERT_TRUE(set); ASSERT_EQ("SomethingDataSet", set->getName()); - + ASSERT_EQ(di, set->getDataItem()); - + auto &ds = set->getValue(); ASSERT_EQ(3, ds.size()); ASSERT_EQ(1, get(ds.find("a"_E)->m_value)); @@ -390,7 +390,6 @@ TEST_F(DataItemMappingTest, should_map_a_sample_data_set) ASSERT_EQ("3", get(ds.find("c"_E)->m_value)); } - TEST_F(DataItemMappingTest, should_map_an_event_table) { auto di = makeDataItem( @@ -427,39 +426,40 @@ TEST_F(DataItemMappingTest, should_map_an_event_table) TEST_F(DataItemMappingTest, should_map_an_sample_table) { - auto di = makeDataItem( - {{"id", "a"s}, {"type", "SOMETHING"s}, {"category", "SAMPLE"s}, {"representation", "TABLE"s}}); - + auto di = makeDataItem({{"id", "a"s}, + {"type", "SOMETHING"s}, + {"category", "SAMPLE"s}, + {"representation", "TABLE"s}}); + auto ts = makeTimestamped({"a", "a={c=1 n=3.0} b={d=2 e=3} c={x=abc y=def}"}); auto observations = (*m_mapper)(ts); auto oblist = observations->getValue(); ASSERT_EQ(1, oblist.size()); - + auto set = dynamic_pointer_cast(oblist.front()); ASSERT_TRUE(set); - + ASSERT_EQ(di, set->getDataItem()); ASSERT_EQ("SomethingTable", set->getName()); - + auto &ds = set->getValue(); ASSERT_EQ(3, ds.size()); auto a = get(ds.find("a"_E)->m_value); ASSERT_EQ(2, a.size()); ASSERT_EQ(1, get(a.find("c"_E)->m_value)); ASSERT_EQ(3.0, get(a.find("n"_E)->m_value)); - + auto b = get(ds.find("b"_E)->m_value); ASSERT_EQ(2, a.size()); ASSERT_EQ(2, get(b.find("d"_E)->m_value)); ASSERT_EQ(3, get(b.find("e"_E)->m_value)); - + auto c = get(ds.find("c"_E)->m_value); ASSERT_EQ(2, c.size()); ASSERT_EQ("abc", get(c.find("x"_E)->m_value)); ASSERT_EQ("def", get(c.find("y"_E)->m_value)); } - TEST_F(DataItemMappingTest, should_handle_data_set_reset_trigger) { makeDataItem({{"id", "a"s}, diff --git a/test_package/fixture_test.cpp b/test_package/fixture_test.cpp new file mode 100644 index 000000000..36cc02e8f --- /dev/null +++ b/test_package/fixture_test.cpp @@ -0,0 +1,251 @@ +// +// Copyright Copyright 2009-2024, AMT – The Association For Manufacturing Technology (“AMT”) +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Ensure that gtest is the first header otherwise Windows raises an error +#include +// Keep this comment to keep gtest.h above. (clang-format off/on is not working here!) + +#include +#include +#include +#include +#include +#include +#include + +#include "json_helper.hpp" +#include "mtconnect/agent.hpp" +#include "mtconnect/asset/asset.hpp" +#include "mtconnect/asset/fixture.hpp" +#include "mtconnect/entity/entity.hpp" +#include "mtconnect/entity/json_printer.hpp" +#include "mtconnect/entity/xml_parser.hpp" +#include "mtconnect/entity/xml_printer.hpp" +#include "mtconnect/printer//xml_printer.hpp" +#include "mtconnect/printer//xml_printer_helper.hpp" +#include "mtconnect/source/adapter/adapter.hpp" + +using json = nlohmann::json; +using namespace std; +using namespace mtconnect; +using namespace mtconnect::entity; +using namespace mtconnect::source::adapter; +using namespace mtconnect::asset; +using namespace mtconnect::printer; + +// main +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + +class FixtureTest : public testing::Test +{ +protected: + void SetUp() override + { + Fixture::registerAsset(); + m_writer = make_unique(true); + } + + void TearDown() override { m_writer.reset(); } + + std::unique_ptr m_writer; +}; + +TEST_F(FixtureTest, minimal_fixture_definition) +{ + using namespace date; + const auto doc = R"DOC( + + 2022-05-20 + 2022-05-21 + 2022-05-22 + 2022-05-23 + + 5.1 + 1.27 + + XXXYYY + 12345 + CLAMP + MOUNT + +)DOC"; + + ErrorList errors; + entity::XmlParser parser; + + auto entity = parser.parse(Asset::getRoot(), doc, errors); + ASSERT_EQ(0, errors.size()); + + auto asset = dynamic_cast(entity.get()); + ASSERT_NE(nullptr, asset); + + ASSERT_EQ("7ae770f0-c11e-013a-c34c-4e7f553bbb76", asset->getAssetId()); + + ASSERT_FALSE(asset->getTimestamp()); + ASSERT_FALSE(asset->getDeviceUuid()); + + auto date = asset->get("ManufactureDate"); + auto ymd = year_month_day(floor(date)); + ASSERT_EQ(2022, int(ymd.year())); + ASSERT_EQ(5, unsigned(ymd.month())); + ASSERT_EQ(20, unsigned(ymd.day())); + + date = asset->get("CalibrationDate"); + ymd = year_month_day(floor(date)); + ASSERT_EQ(2022, int(ymd.year())); + ASSERT_EQ(5, unsigned(ymd.month())); + ASSERT_EQ(21, unsigned(ymd.day())); + + date = asset->get("InspectionDate"); + ymd = year_month_day(floor(date)); + ASSERT_EQ(2022, int(ymd.year())); + ASSERT_EQ(5, unsigned(ymd.month())); + ASSERT_EQ(22, unsigned(ymd.day())); + + date = asset->get("NextInspectionDate"); + ymd = year_month_day(floor(date)); + ASSERT_EQ(2022, int(ymd.year())); + ASSERT_EQ(5, unsigned(ymd.month())); + ASSERT_EQ(23, unsigned(ymd.day())); + + ASSERT_EQ("XXXYYY", asset->get("FixtureId")); + ASSERT_EQ(12345, asset->get("FixtureNumber")); + ASSERT_EQ("CLAMP", asset->get("ClampingMethod")); + ASSERT_EQ("MOUNT", asset->get("MountingMethod")); + + auto meas = asset->getList("Measurements"); + ASSERT_TRUE(meas); + ASSERT_EQ(2, meas->size()); + + auto it = meas->begin(); + ASSERT_EQ("Length", (*it)->getName()); + ASSERT_EQ("MILLIMETER", get((*it)->getProperty("units"))); + ASSERT_EQ(5.0, get((*it)->getProperty("nominal"))); + ASSERT_EQ(4.95, get((*it)->getProperty("minimum"))); + ASSERT_EQ(5.2, get((*it)->getProperty("maximum"))); + ASSERT_EQ(5.1, get((*it)->getProperty("VALUE"))); + + it++; + ASSERT_EQ("Diameter", (*it)->getName()); + ASSERT_EQ("MILLIMETER", get((*it)->getProperty("units"))); + ASSERT_EQ(1.25, get((*it)->getProperty("nominal"))); + ASSERT_EQ(0.95, get((*it)->getProperty("minimum"))); + ASSERT_EQ(1.4, get((*it)->getProperty("maximum"))); + ASSERT_EQ(1.27, get((*it)->getProperty("VALUE"))); +} + +TEST_F(FixtureTest, should_round_trip_xml) +{ + using namespace date; + const auto doc = + R"DOC( + 2022-05-20T00:00:00Z + 2022-05-21T00:00:00Z + 2022-05-22T00:00:00Z + 2022-05-23T00:00:00Z + + 5.1 + 1.27 + + XXXYYY + 12345 + CLAMP + MOUNT + +)DOC"; + + ErrorList errors; + entity::XmlParser parser; + + auto entity = parser.parse(Asset::getRoot(), doc, errors); + ASSERT_EQ(0, errors.size()); + + entity::XmlPrinter printer; + printer.print(*m_writer, entity, {"x"}); + string content = m_writer->getContent(); + + ASSERT_EQ(doc, content); +} + +TEST_F(FixtureTest, should_generate_json) +{ + using namespace date; + const auto doc = + R"DOC( + 2022-05-20T00:00:00Z + 2022-05-21T00:00:00Z + 2022-05-22T00:00:00Z + 2022-05-23T00:00:00Z + + 5.1 + 1.27 + + XXXYYY + 12345 + CLAMP + MOUNT + +)DOC"; + + ErrorList errors; + entity::XmlParser parser; + + auto entity = parser.parse(Asset::getRoot(), doc, errors); + ASSERT_EQ(0, errors.size()); + + entity::JsonEntityPrinter jsonPrinter(2, true); + auto json = jsonPrinter.print(entity); + + ASSERT_EQ(R"({ + "Fixture": { + "CalibrationDate": "2022-05-21T00:00:00Z", + "ClampingMethod": "CLAMP", + "FixtureId": "XXXYYY", + "FixtureNumber": 12345, + "InspectionDate": "2022-05-22T00:00:00Z", + "ManufactureDate": "2022-05-20T00:00:00Z", + "Measurements": { + "Diameter": [ + { + "value": 1.27, + "maximum": 1.4, + "minimum": 0.95, + "nominal": 1.25, + "units": "MILLIMETER" + } + ], + "Length": [ + { + "value": 5.1, + "maximum": 5.2, + "minimum": 4.95, + "nominal": 5.0, + "units": "MILLIMETER" + } + ] + }, + "MountingMethod": "MOUNT", + "NextInspectionDate": "2022-05-23T00:00:00Z", + "assetId": "7ae770f0-c11e-013a-c34c-4e7f553bbb76" + } +})", + json); +} diff --git a/test_package/json_mapping_test.cpp b/test_package/json_mapping_test.cpp index 58398a123..72a0ee72e 100644 --- a/test_package/json_mapping_test.cpp +++ b/test_package/json_mapping_test.cpp @@ -106,7 +106,7 @@ class JsonMappingTest : public testing::Test cerr << "Errors occurred during make data item" << endl; for (auto &e : errors) { - cerr << " " << e->getEntity() << ": " << e->what() << endl; + cerr << " " << e->getEntity() << ": " << e->what() << endl; } return nullptr; } @@ -1063,7 +1063,7 @@ TEST_F(JsonMappingTest, should_skip_erroneous_array) auto dev = makeDevice("Device", {{"id", "device"s}, {"name", "device"s}, {"uuid", "device"s}}); makeDataItem("device", {{"id", "a"s}, {"type", "EXECUTION"s}, {"category", "EVENT"s}}); makeDataItem("device", {{"id", "b"s}, {"type", "CONTROLLER_MODE"s}, {"category", "EVENT"s}}); - + Properties props {{"VALUE", R"( { "timestamp": "2023-11-09T11:20:00Z", @@ -1079,18 +1079,18 @@ TEST_F(JsonMappingTest, should_skip_erroneous_array) }, "b": "MANUAL" })"s}}; - + auto jmsg = std::make_shared("JsonMessage", props); jmsg->m_device = dev; - + auto res = (*m_mapper)(std::move(jmsg)); ASSERT_TRUE(res); - + auto value = res->getValue(); ASSERT_TRUE(std::holds_alternative(value)); auto list = get(value); ASSERT_EQ(1, list.size()); - + auto obs = dynamic_pointer_cast(list.front()); ASSERT_TRUE(obs); ASSERT_EQ("ControllerMode", obs->getName()); @@ -1104,7 +1104,7 @@ TEST_F(JsonMappingTest, should_skip_erroneous_table) auto dev = makeDevice("Device", {{"id", "device"s}, {"name", "device"s}, {"uuid", "device"s}}); makeDataItem("device", {{"id", "a"s}, {"type", "EXECUTION"s}, {"category", "EVENT"s}}); makeDataItem("device", {{"id", "b"s}, {"type", "CONTROLLER_MODE"s}, {"category", "EVENT"s}}); - + Properties props {{"VALUE", R"( { "timestamp": "2023-11-09T11:20:00Z", @@ -1128,18 +1128,18 @@ TEST_F(JsonMappingTest, should_skip_erroneous_table) }, "b": "MANUAL" })"s}}; - + auto jmsg = std::make_shared("JsonMessage", props); jmsg->m_device = dev; - + auto res = (*m_mapper)(std::move(jmsg)); ASSERT_TRUE(res); - + auto value = res->getValue(); ASSERT_TRUE(std::holds_alternative(value)); auto list = get(value); ASSERT_EQ(1, list.size()); - + auto obs = dynamic_pointer_cast(list.front()); ASSERT_TRUE(obs); ASSERT_EQ("ControllerMode", obs->getName()); diff --git a/test_package/mqtt_sink_test.cpp b/test_package/mqtt_sink_test.cpp index 54d2a27dc..0da157089 100644 --- a/test_package/mqtt_sink_test.cpp +++ b/test_package/mqtt_sink_test.cpp @@ -249,21 +249,21 @@ TEST_F(MqttSinkTest, mqtt_sink_should_publish_Sample) auto handler = make_unique(); bool gotSample = false; bool first = true; - handler->m_receive = [&gotSample, &first](std::shared_ptr client, const std::string &topic, - const std::string &payload) { + handler->m_receive = [&gotSample, &first](std::shared_ptr client, + const std::string &topic, const std::string &payload) { if (first) { first = false; } else { - EXPECT_EQ("MTConnect/Sample/000", topic); + EXPECT_EQ("MTConnect/Sample/000", topic); - auto jdoc = json::parse(payload); - auto streams = jdoc.at("/MTConnectStreams/Streams/0/DeviceStream"_json_pointer); - EXPECT_EQ(string("LinuxCNC"), streams.at("/name"_json_pointer).get()); + auto jdoc = json::parse(payload); + auto streams = jdoc.at("/MTConnectStreams/Streams/0/DeviceStream"_json_pointer); + EXPECT_EQ(string("LinuxCNC"), streams.at("/name"_json_pointer).get()); - gotSample = true; + gotSample = true; } }; diff --git a/test_package/observation_validation_test.cpp b/test_package/observation_validation_test.cpp index 1fe9c7fab..f2e014848 100644 --- a/test_package/observation_validation_test.cpp +++ b/test_package/observation_validation_test.cpp @@ -224,16 +224,15 @@ TEST_F(ObservationValidationTest, should_be_invalid_if_entry_has_not_been_introd { ErrorList errors; m_dataItem = - DataItem::make({{"id", "exec"s}, {"category", "EVENT"s}, {"type", "EXECUTION"s}}, errors); - + DataItem::make({{"id", "exec"s}, {"category", "EVENT"s}, {"type", "EXECUTION"s}}, errors); + auto contract = static_cast(m_context->m_contract.get()); contract->m_schemaVersion = SCHEMA_VERSION(1, 4); - + auto event = Observation::make(m_dataItem, {{"VALUE", "WAIT"s}}, m_time, errors); - + auto evt = (*m_validator)(std::move(event)); auto quality = evt->get("quality"); ASSERT_EQ("INVALID", quality); ASSERT_FALSE(evt->hasProperty("deprecated")); } - diff --git a/test_package/pallet_test.cpp b/test_package/pallet_test.cpp new file mode 100644 index 000000000..a8eb5f5ce --- /dev/null +++ b/test_package/pallet_test.cpp @@ -0,0 +1,256 @@ +// +// Copyright Copyright 2009-2024, AMT – The Association For Manufacturing Technology (“AMT”) +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Ensure that gtest is the first header otherwise Windows raises an error +#include +// Keep this comment to keep gtest.h above. (clang-format off/on is not working here!) + +#include +#include +#include +#include +#include +#include +#include + +#include "json_helper.hpp" +#include "mtconnect/agent.hpp" +#include "mtconnect/asset/asset.hpp" +#include "mtconnect/asset/pallet.hpp" +#include "mtconnect/entity/entity.hpp" +#include "mtconnect/entity/json_printer.hpp" +#include "mtconnect/entity/xml_parser.hpp" +#include "mtconnect/entity/xml_printer.hpp" +#include "mtconnect/printer//xml_printer.hpp" +#include "mtconnect/printer//xml_printer_helper.hpp" +#include "mtconnect/source/adapter/adapter.hpp" + +using json = nlohmann::json; +using namespace std; +using namespace mtconnect; +using namespace mtconnect::entity; +using namespace mtconnect::source::adapter; +using namespace mtconnect::asset; +using namespace mtconnect::printer; + +// main +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + +class PalletTest : public testing::Test +{ +protected: + void SetUp() override + { + Pallet::registerAsset(); + m_writer = make_unique(true); + } + + void TearDown() override { m_writer.reset(); } + + std::unique_ptr m_writer; +}; + +TEST_F(PalletTest, minimal_pallet_definition) +{ + using namespace date; + const auto doc = R"DOC( + + 2022-05-20 + 2022-05-21 + 2022-05-22 + 2022-05-23 + + 5.1 + 1.27 + + MACHINE + XXXYYY + 12345 + CLAMP + MOUNT + +)DOC"; + + ErrorList errors; + entity::XmlParser parser; + + auto entity = parser.parse(Asset::getRoot(), doc, errors); + ASSERT_EQ(0, errors.size()); + + auto asset = dynamic_cast(entity.get()); + ASSERT_NE(nullptr, asset); + + ASSERT_EQ("7ae770f0-c11e-013a-c34c-4e7f553bbb76", asset->getAssetId()); + + ASSERT_FALSE(asset->getTimestamp()); + ASSERT_FALSE(asset->getDeviceUuid()); + + auto date = asset->get("ManufactureDate"); + auto ymd = year_month_day(floor(date)); + ASSERT_EQ(2022, int(ymd.year())); + ASSERT_EQ(5, unsigned(ymd.month())); + ASSERT_EQ(20, unsigned(ymd.day())); + + date = asset->get("CalibrationDate"); + ymd = year_month_day(floor(date)); + ASSERT_EQ(2022, int(ymd.year())); + ASSERT_EQ(5, unsigned(ymd.month())); + ASSERT_EQ(21, unsigned(ymd.day())); + + date = asset->get("InspectionDate"); + ymd = year_month_day(floor(date)); + ASSERT_EQ(2022, int(ymd.year())); + ASSERT_EQ(5, unsigned(ymd.month())); + ASSERT_EQ(22, unsigned(ymd.day())); + + date = asset->get("NextInspectionDate"); + ymd = year_month_day(floor(date)); + ASSERT_EQ(2022, int(ymd.year())); + ASSERT_EQ(5, unsigned(ymd.month())); + ASSERT_EQ(23, unsigned(ymd.day())); + + ASSERT_EQ("MACHINE", asset->get("Type")); + ASSERT_EQ("XXXYYY", asset->get("PalletId")); + ASSERT_EQ(12345, asset->get("PalletNumber")); + ASSERT_EQ("CLAMP", asset->get("ClampingMethod")); + ASSERT_EQ("MOUNT", asset->get("MountingMethod")); + + auto meas = asset->getList("Measurements"); + ASSERT_TRUE(meas); + ASSERT_EQ(2, meas->size()); + + auto it = meas->begin(); + ASSERT_EQ("Length", (*it)->getName()); + ASSERT_EQ("MILLIMETER", get((*it)->getProperty("units"))); + ASSERT_EQ(5.0, get((*it)->getProperty("nominal"))); + ASSERT_EQ(4.95, get((*it)->getProperty("minimum"))); + ASSERT_EQ(5.2, get((*it)->getProperty("maximum"))); + ASSERT_EQ(5.1, get((*it)->getProperty("VALUE"))); + + it++; + ASSERT_EQ("Diameter", (*it)->getName()); + ASSERT_EQ("MILLIMETER", get((*it)->getProperty("units"))); + ASSERT_EQ(1.25, get((*it)->getProperty("nominal"))); + ASSERT_EQ(0.95, get((*it)->getProperty("minimum"))); + ASSERT_EQ(1.4, get((*it)->getProperty("maximum"))); + ASSERT_EQ(1.27, get((*it)->getProperty("VALUE"))); +} + +TEST_F(PalletTest, should_round_trip_xml) +{ + using namespace date; + const auto doc = + R"DOC( + 2022-05-20T00:00:00Z + 2022-05-21T00:00:00Z + 2022-05-22T00:00:00Z + 2022-05-23T00:00:00Z + + 5.1 + 1.27 + + MACHINE + XXXYYY + 12345 + CLAMP + MOUNT + +)DOC"; + + ErrorList errors; + entity::XmlParser parser; + + auto entity = parser.parse(Asset::getRoot(), doc, errors); + ASSERT_EQ(0, errors.size()); + + entity::XmlPrinter printer; + printer.print(*m_writer, entity, {"x"}); + string content = m_writer->getContent(); + + ASSERT_EQ(doc, content); +} + +TEST_F(PalletTest, should_generate_json) +{ + using namespace date; + const auto doc = + R"DOC( + 2022-05-20T00:00:00Z + 2022-05-21T00:00:00Z + 2022-05-22T00:00:00Z + 2022-05-23T00:00:00Z + + 5.1 + 1.27 + + MACHINE + XXXYYY + 12345 + CLAMP + MOUNT + +)DOC"; + + ErrorList errors; + entity::XmlParser parser; + + auto entity = parser.parse(Asset::getRoot(), doc, errors); + ASSERT_EQ(0, errors.size()); + + entity::JsonEntityPrinter jsonPrinter(2, true); + auto json = jsonPrinter.print(entity); + + ASSERT_EQ(R"({ + "Pallet": { + "CalibrationDate": "2022-05-21T00:00:00Z", + "ClampingMethod": "CLAMP", + "InspectionDate": "2022-05-22T00:00:00Z", + "ManufactureDate": "2022-05-20T00:00:00Z", + "Measurements": { + "Diameter": [ + { + "value": 1.27, + "maximum": 1.4, + "minimum": 0.95, + "nominal": 1.25, + "units": "MILLIMETER" + } + ], + "Length": [ + { + "value": 5.1, + "maximum": 5.2, + "minimum": 4.95, + "nominal": 5.0, + "units": "MILLIMETER" + } + ] + }, + "MountingMethod": "MOUNT", + "NextInspectionDate": "2022-05-23T00:00:00Z", + "PalletId": "XXXYYY", + "PalletNumber": 12345, + "Type": "MACHINE", + "assetId": "7ae770f0-c11e-013a-c34c-4e7f553bbb76" + } +})", + json); +} diff --git a/test_package/physical_asset_test.cpp b/test_package/physical_asset_test.cpp new file mode 100644 index 000000000..f451f03a3 --- /dev/null +++ b/test_package/physical_asset_test.cpp @@ -0,0 +1,231 @@ +// +// Copyright Copyright 2009-2024, AMT – The Association For Manufacturing Technology (“AMT”) +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Ensure that gtest is the first header otherwise Windows raises an error +#include +// Keep this comment to keep gtest.h above. (clang-format off/on is not working here!) + +#include +#include +#include +#include +#include +#include +#include + +#include "agent_test_helper.hpp" +#include "json_helper.hpp" +#include "mtconnect/agent.hpp" +#include "mtconnect/asset/asset.hpp" +#include "mtconnect/asset/physical_asset.hpp" +#include "mtconnect/entity/entity.hpp" +#include "mtconnect/entity/json_printer.hpp" +#include "mtconnect/entity/xml_parser.hpp" +#include "mtconnect/entity/xml_printer.hpp" +#include "mtconnect/printer//xml_printer.hpp" +#include "mtconnect/printer//xml_printer_helper.hpp" +#include "mtconnect/source/adapter/adapter.hpp" + +using json = nlohmann::json; +using namespace std; +using namespace mtconnect; +using namespace mtconnect::entity; +using namespace mtconnect::source::adapter; +using namespace mtconnect::asset; +using namespace mtconnect::printer; + +// main +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + +class PhysicalAssetTest : public testing::Test +{ +protected: + void SetUp() override + { + PhysicalAsset::registerAsset(); + m_writer = make_unique(true); + } + + void TearDown() override { m_writer.reset(); } + + std::unique_ptr m_writer; +}; + +TEST_F(PhysicalAssetTest, minimal_physical_asset_definition) +{ + using namespace date; + const auto doc = R"DOC( + + 2022-05-20 + 2022-05-21 + 2022-05-22 + 2022-05-23 + + 5.1 + 1.27 + + +)DOC"; + + ErrorList errors; + entity::XmlParser parser; + + auto entity = parser.parse(Asset::getRoot(), doc, errors); + ASSERT_EQ(0, errors.size()); + + auto asset = dynamic_cast(entity.get()); + ASSERT_NE(nullptr, asset); + + ASSERT_EQ("7ae770f0-c11e-013a-c34c-4e7f553bbb76", asset->getAssetId()); + + ASSERT_FALSE(asset->getTimestamp()); + ASSERT_FALSE(asset->getDeviceUuid()); + + auto date = asset->get("ManufactureDate"); + auto ymd = year_month_day(floor(date)); + ASSERT_EQ(2022, int(ymd.year())); + ASSERT_EQ(5, unsigned(ymd.month())); + ASSERT_EQ(20, unsigned(ymd.day())); + + date = asset->get("CalibrationDate"); + ymd = year_month_day(floor(date)); + ASSERT_EQ(2022, int(ymd.year())); + ASSERT_EQ(5, unsigned(ymd.month())); + ASSERT_EQ(21, unsigned(ymd.day())); + + date = asset->get("InspectionDate"); + ymd = year_month_day(floor(date)); + ASSERT_EQ(2022, int(ymd.year())); + ASSERT_EQ(5, unsigned(ymd.month())); + ASSERT_EQ(22, unsigned(ymd.day())); + + date = asset->get("NextInspectionDate"); + ymd = year_month_day(floor(date)); + ASSERT_EQ(2022, int(ymd.year())); + ASSERT_EQ(5, unsigned(ymd.month())); + ASSERT_EQ(23, unsigned(ymd.day())); + + auto meas = asset->getList("Measurements"); + ASSERT_TRUE(meas); + ASSERT_EQ(2, meas->size()); + + auto it = meas->begin(); + ASSERT_EQ("Length", (*it)->getName()); + ASSERT_EQ("MILLIMETER", get((*it)->getProperty("units"))); + ASSERT_EQ(5.0, get((*it)->getProperty("nominal"))); + ASSERT_EQ(4.95, get((*it)->getProperty("minimum"))); + ASSERT_EQ(5.2, get((*it)->getProperty("maximum"))); + ASSERT_EQ(5.1, get((*it)->getProperty("VALUE"))); + + it++; + ASSERT_EQ("Diameter", (*it)->getName()); + ASSERT_EQ("MILLIMETER", get((*it)->getProperty("units"))); + ASSERT_EQ(1.25, get((*it)->getProperty("nominal"))); + ASSERT_EQ(0.95, get((*it)->getProperty("minimum"))); + ASSERT_EQ(1.4, get((*it)->getProperty("maximum"))); + ASSERT_EQ(1.27, get((*it)->getProperty("VALUE"))); +} + +TEST_F(PhysicalAssetTest, should_round_trip_xml) +{ + using namespace date; + const auto doc = + R"DOC( + 2022-05-20T00:00:00Z + 2022-05-21T00:00:00Z + 2022-05-22T00:00:00Z + 2022-05-23T00:00:00Z + + 5.1 + 1.27 + + +)DOC"; + + ErrorList errors; + entity::XmlParser parser; + + auto entity = parser.parse(Asset::getRoot(), doc, errors); + ASSERT_EQ(0, errors.size()); + + entity::XmlPrinter printer; + printer.print(*m_writer, entity, {"x"}); + string content = m_writer->getContent(); + + ASSERT_EQ(doc, content); +} + +TEST_F(PhysicalAssetTest, should_generate_json) +{ + using namespace date; + const auto doc = + R"DOC( + 2022-05-20T00:00:00Z + 2022-05-21T00:00:00Z + 2022-05-22T00:00:00Z + 2022-05-23T00:00:00Z + + 5.1 + 1.27 + + +)DOC"; + + ErrorList errors; + entity::XmlParser parser; + + auto entity = parser.parse(Asset::getRoot(), doc, errors); + ASSERT_EQ(0, errors.size()); + + entity::JsonEntityPrinter jsonPrinter(2, true); + auto json = jsonPrinter.print(entity); + + ASSERT_EQ(R"({ + "PhysicalAsset": { + "CalibrationDate": "2022-05-21T00:00:00Z", + "InspectionDate": "2022-05-22T00:00:00Z", + "ManufactureDate": "2022-05-20T00:00:00Z", + "Measurements": { + "Diameter": [ + { + "value": 1.27, + "maximum": 1.4, + "minimum": 0.95, + "nominal": 1.25, + "units": "MILLIMETER" + } + ], + "Length": [ + { + "value": 5.1, + "maximum": 5.2, + "minimum": 4.95, + "nominal": 5.0, + "units": "MILLIMETER" + } + ] + }, + "NextInspectionDate": "2022-05-23T00:00:00Z", + "assetId": "7ae770f0-c11e-013a-c34c-4e7f553bbb76" + } +})", + json); +}