From 8482351f7fb9fac131dcda5efc006f3ae9eef511 Mon Sep 17 00:00:00 2001 From: Will Sobel Date: Tue, 2 Jul 2024 16:16:07 -0400 Subject: [PATCH 1/7] Fixed regex for URL parsing. --- CMakeLists.txt | 2 +- src/mtconnect/configuration/agent_config.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d89564f6..443214b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ set(AGENT_VERSION_MAJOR 2) set(AGENT_VERSION_MINOR 3) set(AGENT_VERSION_PATCH 0) -set(AGENT_VERSION_BUILD 15) +set(AGENT_VERSION_BUILD 16) set(AGENT_VERSION_RC "") # This minimum version is to support Visual Studio 2019 and C++ feature checking and FetchContent diff --git a/src/mtconnect/configuration/agent_config.cpp b/src/mtconnect/configuration/agent_config.cpp index ae8eec60..99dff267 100644 --- a/src/mtconnect/configuration/agent_config.cpp +++ b/src/mtconnect/configuration/agent_config.cpp @@ -909,7 +909,7 @@ namespace mtconnect::configuration { string host, protocol, path; auto url = *GetOption(options, configuration::Url); - boost::regex pat("^([^:]+)://([^:/]+)(:[0-9]+)?(/.+)?"); + boost::regex pat("^([^:]+)://([^:/]+)(:[0-9]+)?/?(.+)?"); boost::match_results match; if (boost::regex_match(url, match, pat)) { @@ -979,7 +979,7 @@ namespace mtconnect::configuration { device = getDefaultDevice(); if (device) { - deviceName = *device->getComponentName(); + deviceName = *device->getUuid(); adapterOptions[configuration::Device] = deviceName; LOG(info) << "Assigning default device " << deviceName << " to adapter"; } From 16a50e781cda649350d6a483444ab1c5be50d60a Mon Sep 17 00:00:00 2001 From: Will Sobel Date: Tue, 2 Jul 2024 16:39:29 -0400 Subject: [PATCH 2/7] Added full observation delivery to agent adapter pipeline --- .../source/adapter/agent_adapter/agent_adapter.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/mtconnect/source/adapter/agent_adapter/agent_adapter.cpp b/src/mtconnect/source/adapter/agent_adapter/agent_adapter.cpp index a38db70a..40bf8d4f 100644 --- a/src/mtconnect/source/adapter/agent_adapter/agent_adapter.cpp +++ b/src/mtconnect/source/adapter/agent_adapter/agent_adapter.cpp @@ -51,12 +51,11 @@ namespace mtconnect::source::adapter::agent_adapter { TransformPtr next = bind(make_shared(m_context, m_feedback, m_device, m_uuid)); - std::optional obsMetrics; - obsMetrics = m_identity + "_observation_update_rate"; - next->bind(make_shared(m_context, obsMetrics)); + + buildObservationDelivery(next); buildDeviceDelivery(next); buildAssetDelivery(next); - + applySplices(); } From 1bdc5d63296f0122039acc05c47e83a36e8917f5 Mon Sep 17 00:00:00 2001 From: Will Sobel Date: Wed, 3 Jul 2024 10:28:43 -0400 Subject: [PATCH 3/7] Generalized URL parsing in the agent using the URL parser from the agent adapter. --- agent_lib/CMakeLists.txt | 2 - src/mtconnect/agent.cpp | 9 +- src/mtconnect/agent.hpp | 9 +- src/mtconnect/asset/asset.hpp | 4 +- src/mtconnect/configuration/agent_config.cpp | 34 ++-- src/mtconnect/configuration/async_context.hpp | 4 +- .../sink/mqtt_sink/mqtt2_service.cpp | 29 +-- .../sink/mqtt_sink/mqtt2_service.hpp | 4 +- .../adapter/agent_adapter/agent_adapter.cpp | 5 +- .../adapter/agent_adapter/agent_adapter.hpp | 4 +- .../adapter/agent_adapter/http_session.hpp | 2 +- .../adapter/agent_adapter/https_session.hpp | 3 +- .../source/adapter/agent_adapter/session.hpp | 10 +- .../adapter/agent_adapter/session_impl.hpp | 4 +- .../adapter/agent_adapter/url_parser.cpp | 162 ----------------- .../adapter/agent_adapter/url_parser.hpp | 167 ------------------ .../source/adapter/mqtt/mqtt_adapter.cpp | 3 +- .../source/adapter/mqtt/mqtt_adapter.hpp | 2 +- src/mtconnect/utilities.cpp | 142 +++++++++++++++ src/mtconnect/utilities.hpp | 146 ++++++++++++++- test_package/agent_adapter_test.cpp | 1 - test_package/agent_test.cpp | 4 +- test_package/config_test.cpp | 2 +- test_package/url_parser_test.cpp | 51 +++++- 24 files changed, 398 insertions(+), 405 deletions(-) delete mode 100644 src/mtconnect/source/adapter/agent_adapter/url_parser.cpp delete mode 100644 src/mtconnect/source/adapter/agent_adapter/url_parser.hpp diff --git a/agent_lib/CMakeLists.txt b/agent_lib/CMakeLists.txt index ac5763e4..45460856 100644 --- a/agent_lib/CMakeLists.txt +++ b/agent_lib/CMakeLists.txt @@ -214,7 +214,6 @@ set(AGENT_SOURCES "${SOURCE_DIR}/source/adapter/agent_adapter/https_session.hpp" "${SOURCE_DIR}/source/adapter/agent_adapter/session.hpp" "${SOURCE_DIR}/source/adapter/agent_adapter/session_impl.hpp" - "${SOURCE_DIR}/source/adapter/agent_adapter/url_parser.hpp" "${SOURCE_DIR}/source/adapter/mqtt/mqtt_adapter.hpp" "${SOURCE_DIR}/source/adapter/shdr/connector.hpp" "${SOURCE_DIR}/source/adapter/shdr/shdr_adapter.hpp" @@ -233,7 +232,6 @@ set(AGENT_SOURCES "${SOURCE_DIR}/source/loopback_source.cpp" "${SOURCE_DIR}/source/source.cpp" "${SOURCE_DIR}/source/adapter/agent_adapter/agent_adapter.cpp" - "${SOURCE_DIR}/source/adapter/agent_adapter/url_parser.cpp" # src/sink HEADER_FILE_ONLY diff --git a/src/mtconnect/agent.cpp b/src/mtconnect/agent.cpp index 51e82d21..870b8cb8 100644 --- a/src/mtconnect/agent.cpp +++ b/src/mtconnect/agent.cpp @@ -438,8 +438,7 @@ namespace mtconnect { } } - void Agent::loadDevices(list devices, const optional source, - bool force) + void Agent::loadDevices(list devices, const optional source, bool force) { if (!force && !IsOptionSet(m_options, config::EnableSourceDeviceModels)) { @@ -460,7 +459,7 @@ namespace mtconnect { { oldUuid = *oldDev->getUuid(); } - + auto uuid = *device->getUuid(); auto name = *device->getComponentName(); @@ -475,7 +474,7 @@ namespace mtconnect { s->setOptions({{config::Device, uuid}}); } } - + for (auto src : m_sources) { auto adapter = std::dynamic_pointer_cast(src); @@ -514,7 +513,7 @@ namespace mtconnect { cerr << f.what() << endl; } }; - + // Gets around a race condition in the loading of adapaters and setting of // UUID. if (m_context.isRunning() && !m_context.isPauased()) diff --git a/src/mtconnect/agent.hpp b/src/mtconnect/agent.hpp index 0f526a81..c827d7cf 100644 --- a/src/mtconnect/agent.hpp +++ b/src/mtconnect/agent.hpp @@ -279,8 +279,7 @@ namespace mtconnect { /// @param[in] deviceXml the device xml as a string /// @param[in] source the source loading the device void loadDevices(std::list device, - const std::optional source = std::nullopt, - bool force = false); + const std::optional source = std::nullopt, bool force = false); /// @brief receive and parse a single device from a source /// @param[in] deviceXml the device xml as a string @@ -591,8 +590,10 @@ namespace mtconnect { void deliverConnectStatus(entity::EntityPtr, const StringList &devices, bool autoAvailable) override; void deliverCommand(entity::EntityPtr) override; - void deliverDevice(DevicePtr device) override { m_agent->loadDevices({device}, std::nullopt, - true); } + void deliverDevice(DevicePtr device) override + { + m_agent->loadDevices({device}, std::nullopt, true); + } void deliverDevices(std::list devices) override { m_agent->loadDevices(devices); } void sourceFailed(const std::string &identity) override { m_agent->sourceFailed(identity); } diff --git a/src/mtconnect/asset/asset.hpp b/src/mtconnect/asset/asset.hpp index 0fe001a2..aea57917 100644 --- a/src/mtconnect/asset/asset.hpp +++ b/src/mtconnect/asset/asset.hpp @@ -160,9 +160,9 @@ namespace mtconnect { /// Override to skip the `hash`, `timestamp`, and `removed` properties. /// /// @param[in,out] sha1 The boost sha1 accumulator - void hash(boost::uuids::detail::sha1 &sha1) const override + void hash(::boost::uuids::detail::sha1 &sha1) const override { - static const boost::unordered_set skip {"hash", "timestamp", "removed"}; + static const ::boost::unordered_set skip {"hash", "timestamp", "removed"}; entity::Entity::hash(sha1, skip); } diff --git a/src/mtconnect/configuration/agent_config.cpp b/src/mtconnect/configuration/agent_config.cpp index 99dff267..c87e5b85 100644 --- a/src/mtconnect/configuration/agent_config.cpp +++ b/src/mtconnect/configuration/agent_config.cpp @@ -906,31 +906,21 @@ namespace mtconnect::configuration { void parseUrl(ConfigOptions &options) { - string host, protocol, path; + using namespace mtconnect::url; auto url = *GetOption(options, configuration::Url); - boost::regex pat("^([^:]+)://([^:/]+)(:[0-9]+)?/?(.+)?"); - boost::match_results match; - if (boost::regex_match(url, match, pat)) + auto parsed = Url::parse(url); + options[configuration::Protocol] = parsed.m_protocol; + options[configuration::Host] = parsed.getHost(); + if (parsed.m_port) + options[configuration::Port] = parsed.getPort(); + if (parsed.m_path != "/") { - if (match[1].matched) - options[configuration::Protocol] = string(match[1].first, match[1].second); - if (match[2].matched) - options[configuration::Host] = string(match[2].first, match[2].second); - if (match[3].matched) - { - try - { - options[configuration::Port] = - boost::lexical_cast(string(match[3].first + 1, match[3].second).c_str()); - } - catch (boost::bad_lexical_cast &e) - { - LOG(error) << "Cannot intrepret the port for " << match[3] << ": " << e.what(); - } - } - if (match[4].matched) - options[configuration::Topics] = StringList {string(match[4].first, match[4].second)}; + StringList list; + string topics = parsed.m_path.substr(1, string::npos); + boost::split(list, topics, boost::is_any_of(":"), + boost::token_compress_on); + options[configuration::Topics] = list; } } diff --git a/src/mtconnect/configuration/async_context.hpp b/src/mtconnect/configuration/async_context.hpp index e84501c1..81a31254 100644 --- a/src/mtconnect/configuration/async_context.hpp +++ b/src/mtconnect/configuration/async_context.hpp @@ -39,11 +39,11 @@ namespace mtconnect::configuration { /// @brief removes the copy constructor AsyncContext(const AsyncContext &) = delete; ~AsyncContext() {} - + /// @brief is the context running /// @returns running status auto isRunning() { return m_running; } - + /// @brief return the paused state /// @returns the paused state auto isPauased() { return m_paused; } diff --git a/src/mtconnect/sink/mqtt_sink/mqtt2_service.cpp b/src/mtconnect/sink/mqtt_sink/mqtt2_service.cpp index 74284e45..dfbd5743 100644 --- a/src/mtconnect/sink/mqtt_sink/mqtt2_service.cpp +++ b/src/mtconnect/sink/mqtt_sink/mqtt2_service.cpp @@ -116,11 +116,11 @@ namespace mtconnect { { m_options[configuration::MqttHost] = m_options[configuration::Host]; } - + auto retain = GetOption(m_options, configuration::MqttRetain); if (retain) m_retain = *retain; - + auto qoso = GetOption(m_options, configuration::MqttQOS); if (qoso) @@ -133,7 +133,7 @@ namespace mtconnect { else if (qos == "exactly_once") m_qos = MqttClient::QOS::exactly_once; else - LOG(warning) << "Invalid QOS for MQTT Client: " << qos + LOG(warning) << "Invalid QOS for MQTT Client: " << qos << ", must be at_most_once, at_least_once, or exactly_once"; } } @@ -269,16 +269,19 @@ namespace mtconnect { m_sinkContract->getCircularBuffer().getBufferSize(), end, firstSeq, lastSeq, *observations, false); - m_client->asyncPublish(topic, doc, [sampler, topic](std::error_code ec) { - if (!ec) - { - sampler->handlerCompleted(); - } - else - { - LOG(warning) << "Async publish failed for " << topic << ": " << ec.message(); - } - }, m_retain, m_qos); + m_client->asyncPublish( + topic, doc, + [sampler, topic](std::error_code ec) { + if (!ec) + { + sampler->handlerCompleted(); + } + else + { + LOG(warning) << "Async publish failed for " << topic << ": " << ec.message(); + } + }, + m_retain, m_qos); return end; } diff --git a/src/mtconnect/sink/mqtt_sink/mqtt2_service.hpp b/src/mtconnect/sink/mqtt_sink/mqtt2_service.hpp index 62a38710..c3c73027 100644 --- a/src/mtconnect/sink/mqtt_sink/mqtt2_service.hpp +++ b/src/mtconnect/sink/mqtt_sink/mqtt2_service.hpp @@ -112,7 +112,7 @@ namespace mtconnect { /// @brief Mqtt Client is Connected or not /// @return `true` when the client was connected bool isConnected() { return m_client && m_client->isConnected(); } - + /// @name Retain and QOS flags ///@{ auto getRetain() { return m_retain; } @@ -205,7 +205,7 @@ namespace mtconnect { std::map m_filters; std::map> m_samplers; - + bool m_retain {true}; MqttClient::QOS m_qos {MqttClient::QOS::at_least_once}; }; diff --git a/src/mtconnect/source/adapter/agent_adapter/agent_adapter.cpp b/src/mtconnect/source/adapter/agent_adapter/agent_adapter.cpp index 40bf8d4f..b54ae9d7 100644 --- a/src/mtconnect/source/adapter/agent_adapter/agent_adapter.cpp +++ b/src/mtconnect/source/adapter/agent_adapter/agent_adapter.cpp @@ -39,6 +39,7 @@ using namespace std; using namespace mtconnect; using namespace mtconnect::pipeline; +using namespace mtconnect::url; namespace mtconnect::source::adapter::agent_adapter { void AgentAdapterPipeline::build(const ConfigOptions &options) @@ -51,11 +52,11 @@ namespace mtconnect::source::adapter::agent_adapter { TransformPtr next = bind(make_shared(m_context, m_feedback, m_device, m_uuid)); - + buildObservationDelivery(next); buildDeviceDelivery(next); buildAssetDelivery(next); - + applySplices(); } diff --git a/src/mtconnect/source/adapter/agent_adapter/agent_adapter.hpp b/src/mtconnect/source/adapter/agent_adapter/agent_adapter.hpp index e949654a..fb97f9e0 100644 --- a/src/mtconnect/source/adapter/agent_adapter/agent_adapter.hpp +++ b/src/mtconnect/source/adapter/agent_adapter/agent_adapter.hpp @@ -21,8 +21,8 @@ #include "mtconnect/pipeline/mtconnect_xml_transform.hpp" #include "mtconnect/source/adapter/adapter.hpp" #include "mtconnect/source/adapter/adapter_pipeline.hpp" +#include "mtconnect/utilities.hpp" #include "session.hpp" -#include "url_parser.hpp" namespace boost::asio::ssl { class context; @@ -127,7 +127,7 @@ namespace mtconnect::source::adapter::agent_adapter { protected: pipeline::XmlTransformFeedback m_feedback; AgentAdapterPipeline m_pipeline; - Url m_url; + url::Url m_url; int m_count = 1000; std::chrono::milliseconds m_heartbeat; bool m_reconnecting = false; diff --git a/src/mtconnect/source/adapter/agent_adapter/http_session.hpp b/src/mtconnect/source/adapter/agent_adapter/http_session.hpp index 1003a2cd..9f97d8cb 100644 --- a/src/mtconnect/source/adapter/agent_adapter/http_session.hpp +++ b/src/mtconnect/source/adapter/agent_adapter/http_session.hpp @@ -33,7 +33,7 @@ namespace mtconnect::source::adapter::agent_adapter { /// @brief Create a session to connect to the remote agent /// @param ioc the asio strand to run in /// @param url URL to connect to - HttpSession(boost::asio::io_context::strand &ioc, const Url &url) + HttpSession(boost::asio::io_context::strand &ioc, const url::Url &url) : super(ioc, url), m_stream(ioc.context()) {} diff --git a/src/mtconnect/source/adapter/agent_adapter/https_session.hpp b/src/mtconnect/source/adapter/agent_adapter/https_session.hpp index 0b5b0ad3..ba60d7dd 100644 --- a/src/mtconnect/source/adapter/agent_adapter/https_session.hpp +++ b/src/mtconnect/source/adapter/agent_adapter/https_session.hpp @@ -36,7 +36,8 @@ namespace mtconnect::source::adapter::agent_adapter { /// @param ex the strand to run in /// @param url the url to connect to /// @param ctx the TLS context - explicit HttpsSession(boost::asio::io_context::strand &ex, const Url &url, ssl::context &ctx) + explicit HttpsSession(boost::asio::io_context::strand &ex, const url::Url &url, + ssl::context &ctx) : super(ex, url), m_stream(ex.context(), ctx) {} ~HttpsSession() diff --git a/src/mtconnect/source/adapter/agent_adapter/session.hpp b/src/mtconnect/source/adapter/agent_adapter/session.hpp index 89e14682..55e3699a 100644 --- a/src/mtconnect/source/adapter/agent_adapter/session.hpp +++ b/src/mtconnect/source/adapter/agent_adapter/session.hpp @@ -24,7 +24,6 @@ #include "mtconnect/config.hpp" #include "mtconnect/entity/entity.hpp" -#include "url_parser.hpp" namespace mtconnect::source::adapter { struct Handler; @@ -53,7 +52,7 @@ namespace mtconnect::source::adapter::agent_adapter { /// @param stream `true` if HTTP x-multipart-replace streaming is desired /// @param next Function to determine what to do on successful read Request(const std::optional &device, const std::string &operation, - const UrlQuery &query, bool stream, Next next) + const url::UrlQuery &query, bool stream, Next next) : m_sourceDevice(device), m_operation(operation), m_query(query), @@ -65,7 +64,7 @@ namespace mtconnect::source::adapter::agent_adapter { std::optional m_sourceDevice; ///< optional source device std::string m_operation; ///< The REST operation (probe, current, sample, asset) - UrlQuery m_query; ///< URL Query parameters + url::UrlQuery m_query; ///< URL Query parameters bool m_stream; ///< `true` if using HTTP long pull Next m_next; ///< function to call on successful read int32_t m_agentVersion = 0; ///< agent version if required > 0 for asset requests @@ -73,7 +72,10 @@ namespace mtconnect::source::adapter::agent_adapter { /// @brief Given a url, get a formatted target for a given operation /// @param url The base url /// @return a string with a new URL path and query (for the GET) - auto getTarget(const Url &url) { return url.getTarget(m_sourceDevice, m_operation, m_query); } + auto getTarget(const url::Url &url) + { + return url.getTarget(m_sourceDevice, m_operation, m_query); + } }; virtual ~Session() {} diff --git a/src/mtconnect/source/adapter/agent_adapter/session_impl.hpp b/src/mtconnect/source/adapter/agent_adapter/session_impl.hpp index 43ff13e5..3e9c5e7d 100644 --- a/src/mtconnect/source/adapter/agent_adapter/session_impl.hpp +++ b/src/mtconnect/source/adapter/agent_adapter/session_impl.hpp @@ -56,7 +56,7 @@ namespace mtconnect::source::adapter::agent_adapter { // Objects are constructed with a strand to // ensure that handlers do not execute concurrently. - SessionImpl(boost::asio::io_context::strand &strand, const Url &url) + SessionImpl(boost::asio::io_context::strand &strand, const url::Url &url) : m_resolver(strand.context()), m_strand(strand), m_url(url), m_chunk(1 * 1024 * 1024) {} @@ -595,7 +595,7 @@ namespace mtconnect::source::adapter::agent_adapter { std::optional> m_chunkParser; std::optional> m_textParser; asio::io_context::strand m_strand; - Url m_url; + url::Url m_url; std::function m_chunkHandler; diff --git a/src/mtconnect/source/adapter/agent_adapter/url_parser.cpp b/src/mtconnect/source/adapter/agent_adapter/url_parser.cpp deleted file mode 100644 index b6c3bb17..00000000 --- a/src/mtconnect/source/adapter/agent_adapter/url_parser.cpp +++ /dev/null @@ -1,162 +0,0 @@ -// -// Copyright Copyright 2009-2022, 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 "url_parser.hpp" - -#include -#include -#include - -namespace qi = boost::spirit::qi; - -/// @brief User credentials struct for intermediate parsing -struct UserCred -{ - std::string m_username; - std::string m_password; -}; - -/// @brief make the struct available to spirit -BOOST_FUSION_ADAPT_STRUCT(UserCred, (std::string, m_username)(std::string, m_password)) - -/// @brief make the struct available to spirit -BOOST_FUSION_ADAPT_STRUCT(mtconnect::source::adapter::agent_adapter::UrlQueryPair, - (std::string, first)(std::string, second)) - -/// @brief make the struct available to spirit -BOOST_FUSION_ADAPT_STRUCT( - mtconnect::source::adapter::agent_adapter::Url, - (std::string, m_protocol)(mtconnect::source::adapter::agent_adapter::Url::Host, - m_host)(std::optional, m_username)( - std::optional, m_password)(std::optional, m_port)(std::string, m_path)( - mtconnect::source::adapter::agent_adapter::UrlQuery, m_query)(std::string, m_fragment)) - -namespace mtconnect::source::adapter::agent_adapter { - - /// @brief Convert from four uns8s to an ipv4 address - /// @param n1 first octet - /// @param n2 second octet - /// @param n3 third octet - /// @param n4 fourth octet - /// @return a boost ipv4 address - static boost::asio::ip::address_v4 from_four_number(unsigned char n1, unsigned char n2, - unsigned char n3, unsigned char n4) - { - boost::asio::ip::address_v4::bytes_type bt; - - bt[0] = n1; - bt[1] = n2; - bt[2] = n3; - bt[3] = n4; - - return boost::asio::ip::address_v4(bt); - } - - /// @brief convert a string to an ipv6 address - /// @param str the string (as a char vector) - /// @return a boost ipv6 address - static boost::asio::ip::address_v6 from_v6_string(std::vector str) - { - return boost::asio::ip::address_v6::from_string(str.data()); - } - - BOOST_PHOENIX_ADAPT_FUNCTION(boost::asio::ip::address_v4, v4_from_4number, from_four_number, 4) - BOOST_PHOENIX_ADAPT_FUNCTION(boost::asio::ip::address_v6, v6_from_string, from_v6_string, 1) - - /// @brief The Uri parser spirit qi grammar - /// @tparam Iterator - template - struct UriGrammar : qi::grammar - { - UriGrammar() : UriGrammar::base_type(url) - { - using namespace boost::phoenix; - using boost::phoenix::ref; - - url = schema[at_c<0>(qi::_val) = qi::_1] >> "://" >> - -(username[at_c<2>(qi::_val) = qi::_1] >> - -(':' >> password[at_c<3>(qi::_val) = qi::_1]) >> - qi::lit('@')[boost::phoenix::ref(has_user_name) = true]) >> - host[at_c<1>(qi::_val) = qi::_1] >> - -(qi::lit(':') >> qi::int_[at_c<4>(qi::_val) = qi::_1]) >> - -(path[at_c<5>(qi::_val) = qi::_1] >> -('?' >> query[at_c<6>(qi::_val) = qi::_1])) >> - -('#' >> fragment[at_c<7>(qi::_val) = qi::_1]); - - host = ip_host | domain_host; - - domain_host = qi::lexeme[+(qi::char_("a-zA-Z0-9.\\-"))]; - ip_host = ('[' >> ipv6_host >> ']') | ipv4_host; - - ipv6_host = (+qi::char_("0123456789abcdefABCDEF:."))[qi::_val = v6_from_string(qi::_1)]; - - ipv4_host = (qi::int_ >> '.' >> qi::int_ >> '.' >> qi::int_ >> '.' >> - qi::int_)[qi::_val = v4_from_4number(qi::_1, qi::_2, qi::_3, qi::_4)]; - - username = qi::lexeme[+(qi::char_ - ':' - '@' - '/')]; - password = qi::lexeme[+(qi::char_ - '@')]; - - schema = qi::lexeme[+(qi::char_ - ':' - '/')]; - - path = qi::lexeme[+(qi::char_ - '?' - '#')]; - - query = pair >> *((qi::lit(';') | '&') >> pair); - pair = key >> -('=' >> value); - key = qi::lexeme[+(qi::char_ - '=' - '#')]; - value = qi::lexeme[*(qi::char_ - '&' - '#')]; - - fragment = qi::lexeme[+(qi::char_)]; - }; - - qi::rule url; - qi::rule schema, path; - - qi::rule()> host; - - qi::rule domain_host; - qi::rule ip_host; - - qi::rule ipv4_host; - qi::rule ipv6_host; - ; - - qi::rule username, password; - - qi::rule query; - qi::rule pair; - qi::rule key, value; - - qi::rule fragment; - - bool has_user_name = false; - }; - - Url Url::parse(const std::string_view& url) - { - Url ast; - UriGrammar grammar; - - auto first = url.begin(); - - [[maybe_unused]] bool r = boost::spirit::qi::parse(first, url.end(), grammar, ast); - if (!grammar.has_user_name) - { - ast.m_username.reset(); - ast.m_password.reset(); - } - return ast; - } -} // namespace mtconnect::source::adapter::agent_adapter diff --git a/src/mtconnect/source/adapter/agent_adapter/url_parser.hpp b/src/mtconnect/source/adapter/agent_adapter/url_parser.hpp deleted file mode 100644 index b14fd0da..00000000 --- a/src/mtconnect/source/adapter/agent_adapter/url_parser.hpp +++ /dev/null @@ -1,167 +0,0 @@ -// -// Copyright Copyright 2009-2022, 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 -#include -#include -#include -#include - -#include "mtconnect/config.hpp" -#include "mtconnect/utilities.hpp" - -namespace mtconnect::source::adapter::agent_adapter { - using UrlQueryPair = std::pair; - - /// @brief A map of URL query parameters that can format as a string - struct AGENT_LIB_API UrlQuery : public std::map - { - using std::map::map; - - /// @brief join the parameters as `=&=&...` - /// @return - std::string join() const - { - std::stringstream ss; - bool has_pre = false; - - for (const auto& kv : *this) - { - if (has_pre) - ss << '&'; - - ss << kv.first << '=' << kv.second; - has_pre = true; - } - - return ss.str(); - } - - /// @brief Merge twos sets over-writing existing pairs set with `query` and adding new pairs - /// @param query query to merge - void merge(UrlQuery query) - { - for (const auto& kv : query) - { - insert_or_assign(kv.first, kv.second); - } - } - }; - - /// @brief URL struct to parse and format URLs - struct AGENT_LIB_API Url - { - /// @brief Variant for the Host that is either a host name or an ip address - using Host = std::variant; - - std::string m_protocol; ///< either `http` or `https` - - Host m_host; ///< the host component - std::optional m_username; ///< optional username - std::optional m_password; ///< optional password - std::optional m_port; ///< The optional port number (defaults to 5000) - std::string m_path = "/"; ///< The path component - UrlQuery m_query; ///< Query parameters - std::string m_fragment; ///< The component after a `#` - - /// @brief Visitor to format the Host as a string - struct HostVisitor - { - std::string operator()(std::string v) const { return v; } - - std::string operator()(boost::asio::ip::address v) const { return v.to_string(); } - }; - - /// @brief Get the host as a string - /// @return the host - std::string getHost() const { return std::visit(HostVisitor(), m_host); } - /// @brief Get the port as a string - /// @return the port - std::string getService() const { return boost::lexical_cast(getPort()); } - - /// @brief Get the path and the query portion of the URL - /// @return the path and query - std::string getTarget() const - { - if (m_query.size()) - return m_path + '?' + m_query.join(); - else - return m_path; - } - - /// @brief Format a target using the existing host and port to make a request - /// @param device an optional device - /// @param operation the operation (probe,sample,current, or asset) - /// @param query query parameters - /// @return A string with the target for a GET reuest - std::string getTarget(const std::optional& device, const std::string& operation, - const UrlQuery& query) const - { - UrlQuery uq {m_query}; - if (!query.empty()) - uq.merge(query); - - std::stringstream path; - path << m_path; - if (m_path[m_path.size() - 1] != '/') - path << '/'; - if (device) - path << *device << '/'; - if (!operation.empty()) - path << operation; - if (uq.size() > 0) - path << '?' << uq.join(); - - return path.str(); - } - - int getPort() const - { - if (m_port) - return *m_port; - else if (m_protocol == "https") - return 443; - else if (m_protocol == "http") - return 80; - else - return 0; - } - - /// @brief Format the URL as text - /// @param device optional device to add to the URL - /// @return formatted URL - std::string getUrlText(const std::optional& device) - { - std::stringstream url; - url << m_protocol << "://" << getHost() << ':' << getPort() << getTarget(); - if (device) - url << *device; - return url.str(); - } - - /// @brief parse a string to a Url - /// @return parsed URL - static Url parse(const std::string_view& url); - }; - -} // namespace mtconnect::source::adapter::agent_adapter diff --git a/src/mtconnect/source/adapter/mqtt/mqtt_adapter.cpp b/src/mtconnect/source/adapter/mqtt/mqtt_adapter.cpp index ec83f997..d2a9eb7b 100644 --- a/src/mtconnect/source/adapter/mqtt/mqtt_adapter.cpp +++ b/src/mtconnect/source/adapter/mqtt/mqtt_adapter.cpp @@ -64,6 +64,7 @@ namespace mtconnect { {configuration::Manufacturer, string()}, {configuration::Station, string()}, {configuration::Url, string()}, + {configuration::Topics, StringList()}, {configuration::MqttCaCert, string()}, {configuration::MqttPrivateKey, string()}, {configuration::MqttCert, string()}, @@ -173,7 +174,7 @@ namespace mtconnect { } options[configuration::Topics] = list; } - else + else if (!HasOption(options, configuration::Topics)) { LOG(error) << "MQTT Adapter requires at least one topic to subscribe to. Provide 'Topics = " "' or Topics block"; diff --git a/src/mtconnect/source/adapter/mqtt/mqtt_adapter.hpp b/src/mtconnect/source/adapter/mqtt/mqtt_adapter.hpp index e5158739..991a2b3c 100644 --- a/src/mtconnect/source/adapter/mqtt/mqtt_adapter.hpp +++ b/src/mtconnect/source/adapter/mqtt/mqtt_adapter.hpp @@ -69,7 +69,7 @@ namespace mtconnect::source::adapter::mqtt_adapter { unsigned int getPort() const override; ///@} - + /// @name Source interface ///@{ bool start() override; diff --git a/src/mtconnect/utilities.cpp b/src/mtconnect/utilities.cpp index 898daf40..0a6c8eba 100644 --- a/src/mtconnect/utilities.cpp +++ b/src/mtconnect/utilities.cpp @@ -18,6 +18,9 @@ #include "mtconnect/utilities.hpp" #include +#include +#include +#include #include #include @@ -50,6 +53,29 @@ using namespace std; using namespace std::chrono; +namespace qi = boost::spirit::qi; + +/// @brief User credentials struct for intermediate parsing +struct UserCred +{ + std::string m_username; + std::string m_password; +}; + +/// @brief make the struct available to spirit +BOOST_FUSION_ADAPT_STRUCT(UserCred, (std::string, m_username)(std::string, m_password)) + +/// @brief make the struct available to spirit +BOOST_FUSION_ADAPT_STRUCT(mtconnect::url::UrlQueryPair, (std::string, first)(std::string, second)) + +/// @brief make the struct available to spirit +BOOST_FUSION_ADAPT_STRUCT(mtconnect::url::Url, + (std::string, m_protocol)(mtconnect::url::Url::Host, + m_host)(std::optional, m_username)( + std::optional, m_password)(std::optional, m_port)( + std::string, m_path)(mtconnect::url::UrlQuery, m_query)(std::string, + m_fragment)) + namespace mtconnect { AGENT_LIB_API void mt_localtime(const time_t *time, struct tm *buf) { localtime_r(time, buf); } @@ -158,4 +184,120 @@ namespace mtconnect { return address; } + + namespace url { + /// @brief Convert from four uns8s to an ipv4 address + /// @param n1 first octet + /// @param n2 second octet + /// @param n3 third octet + /// @param n4 fourth octet + /// @return a boost ipv4 address + static boost::asio::ip::address_v4 from_four_number(unsigned char n1, unsigned char n2, + unsigned char n3, unsigned char n4) + { + boost::asio::ip::address_v4::bytes_type bt; + + bt[0] = n1; + bt[1] = n2; + bt[2] = n3; + bt[3] = n4; + + return boost::asio::ip::address_v4(bt); + } + + /// @brief convert a string to an ipv6 address + /// @param str the string (as a char vector) + /// @return a boost ipv6 address + static boost::asio::ip::address_v6 from_v6_string(std::vector str) + { + return boost::asio::ip::address_v6::from_string(str.data()); + } + + BOOST_PHOENIX_ADAPT_FUNCTION(boost::asio::ip::address_v4, v4_from_4number, from_four_number, 4) + BOOST_PHOENIX_ADAPT_FUNCTION(boost::asio::ip::address_v6, v6_from_string, from_v6_string, 1) + + /// @brief The Uri parser spirit qi grammar + /// @tparam Iterator + template + struct UriGrammar : qi::grammar + { + UriGrammar() : UriGrammar::base_type(url) + { + using namespace boost::phoenix; + using boost::phoenix::ref; + + url = schema[at_c<0>(qi::_val) = qi::_1] >> "://" >> + -(username[at_c<2>(qi::_val) = qi::_1] >> + -(':' >> password[at_c<3>(qi::_val) = qi::_1]) >> + qi::lit('@')[boost::phoenix::ref(has_user_name) = true]) >> + host[at_c<1>(qi::_val) = qi::_1] >> + -(qi::lit(':') >> qi::int_[at_c<4>(qi::_val) = qi::_1]) >> + -(path[at_c<5>(qi::_val) = qi::_1] >> -('?' >> query[at_c<6>(qi::_val) = qi::_1])) >> + -('#' >> fragment[at_c<7>(qi::_val) = qi::_1]); + + host = ip_host | domain_host; + + domain_host = qi::lexeme[+(qi::char_("a-zA-Z0-9.\\-"))]; + ip_host = ('[' >> ipv6_host >> ']') | ipv4_host; + + ipv6_host = (+qi::char_("0123456789abcdefABCDEF:."))[qi::_val = v6_from_string(qi::_1)]; + + ipv4_host = (qi::int_ >> '.' >> qi::int_ >> '.' >> qi::int_ >> '.' >> + qi::int_)[qi::_val = v4_from_4number(qi::_1, qi::_2, qi::_3, qi::_4)]; + + username = qi::lexeme[+(qi::char_ - ':' - '@' - '/')]; + password = qi::lexeme[+(qi::char_ - '@')]; + + schema = qi::lexeme[+(qi::char_ - ':' - '/')]; + + path = qi::lexeme[+(qi::char_ - '?')]; + + query = pair >> *((qi::lit(';') | '&') >> pair); + pair = key >> -('=' >> value); + key = qi::lexeme[+(qi::char_ - '=' - '#')]; + value = qi::lexeme[*(qi::char_ - '&' - '#')]; + + fragment = qi::lexeme[+(qi::char_)]; + }; + + qi::rule url; + qi::rule schema, path; + + qi::rule()> host; + + qi::rule domain_host; + qi::rule ip_host; + + qi::rule ipv4_host; + qi::rule ipv6_host; + ; + + qi::rule username, password; + + qi::rule query; + qi::rule pair; + qi::rule key, value; + + qi::rule fragment; + + bool has_user_name = false; + }; + + Url Url::parse(const std::string_view &url) + { + Url ast; + UriGrammar grammar; + + auto first = url.begin(); + + [[maybe_unused]] bool r = boost::spirit::qi::parse(first, url.end(), grammar, ast); + if (!grammar.has_user_name) + { + ast.m_username.reset(); + ast.m_password.reset(); + } + return ast; + } + } // namespace url + } // namespace mtconnect diff --git a/src/mtconnect/utilities.hpp b/src/mtconnect/utilities.hpp index 279208d6..adf59697 100644 --- a/src/mtconnect/utilities.hpp +++ b/src/mtconnect/utilities.hpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -31,7 +32,13 @@ #include #include #include +#include #include +#include +#include +#include +#include +#include #include "mtconnect/config.hpp" #include "mtconnect/logging.hpp" @@ -811,11 +818,11 @@ namespace mtconnect { /// @param[in] sha the sha1 namespace to use as context /// @param[in] id the id to use transform /// @returns Returns the first 16 characters of the base 64 encoded sha1 - inline std::string makeUniqueId(const boost::uuids::detail::sha1 &sha, const std::string &id) + inline std::string makeUniqueId(const ::boost::uuids::detail::sha1 &sha, const std::string &id) { using namespace std; - boost::uuids::detail::sha1 sha1(sha); + ::boost::uuids::detail::sha1 sha1(sha); constexpr string_view startc("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"); constexpr auto isIDStartChar = [](unsigned char c) -> bool { return isalpha(c) || c == '_'; }; @@ -846,4 +853,139 @@ namespace mtconnect { return s; } + + namespace url { + using UrlQueryPair = std::pair; + + /// @brief A map of URL query parameters that can format as a string + struct AGENT_LIB_API UrlQuery : public std::map + { + using std::map::map; + + /// @brief join the parameters as `=&=&...` + /// @return + std::string join() const + { + std::stringstream ss; + bool has_pre = false; + + for (const auto &kv : *this) + { + if (has_pre) + ss << '&'; + + ss << kv.first << '=' << kv.second; + has_pre = true; + } + + return ss.str(); + } + + /// @brief Merge twos sets over-writing existing pairs set with `query` and adding new pairs + /// @param query query to merge + void merge(UrlQuery query) + { + for (const auto &kv : query) + { + insert_or_assign(kv.first, kv.second); + } + } + }; + + /// @brief URL struct to parse and format URLs + struct AGENT_LIB_API Url + { + /// @brief Variant for the Host that is either a host name or an ip address + using Host = std::variant; + + std::string m_protocol; ///< either `http` or `https` + + Host m_host; ///< the host component + std::optional m_username; ///< optional username + std::optional m_password; ///< optional password + std::optional m_port; ///< The optional port number (defaults to 5000) + std::string m_path = "/"; ///< The path component + UrlQuery m_query; ///< Query parameters + std::string m_fragment; ///< The component after a `#` + + /// @brief Visitor to format the Host as a string + struct HostVisitor + { + std::string operator()(std::string v) const { return v; } + + std::string operator()(boost::asio::ip::address v) const { return v.to_string(); } + }; + + /// @brief Get the host as a string + /// @return the host + std::string getHost() const { return std::visit(HostVisitor(), m_host); } + /// @brief Get the port as a string + /// @return the port + std::string getService() const { return boost::lexical_cast(getPort()); } + + /// @brief Get the path and the query portion of the URL + /// @return the path and query + std::string getTarget() const + { + if (m_query.size()) + return m_path + '?' + m_query.join(); + else + return m_path; + } + + /// @brief Format a target using the existing host and port to make a request + /// @param device an optional device + /// @param operation the operation (probe,sample,current, or asset) + /// @param query query parameters + /// @return A string with the target for a GET reuest + std::string getTarget(const std::optional &device, const std::string &operation, + const UrlQuery &query) const + { + UrlQuery uq {m_query}; + if (!query.empty()) + uq.merge(query); + + std::stringstream path; + path << m_path; + if (m_path[m_path.size() - 1] != '/') + path << '/'; + if (device) + path << *device << '/'; + if (!operation.empty()) + path << operation; + if (uq.size() > 0) + path << '?' << uq.join(); + + return path.str(); + } + + int getPort() const + { + if (m_port) + return *m_port; + else if (m_protocol == "https") + return 443; + else if (m_protocol == "http") + return 80; + else + return 0; + } + + /// @brief Format the URL as text + /// @param device optional device to add to the URL + /// @return formatted URL + std::string getUrlText(const std::optional &device) + { + std::stringstream url; + url << m_protocol << "://" << getHost() << ':' << getPort() << getTarget(); + if (device) + url << *device; + return url.str(); + } + + /// @brief parse a string to a Url + /// @return parsed URL + static Url parse(const std::string_view &url); + }; + } // namespace url } // namespace mtconnect diff --git a/test_package/agent_adapter_test.cpp b/test_package/agent_adapter_test.cpp index 29554b2f..81dc48b3 100644 --- a/test_package/agent_adapter_test.cpp +++ b/test_package/agent_adapter_test.cpp @@ -34,7 +34,6 @@ #include "mtconnect/printer//xml_printer.hpp" #include "mtconnect/source/adapter/adapter.hpp" #include "mtconnect/source/adapter/agent_adapter/agent_adapter.hpp" -#include "mtconnect/source/adapter/agent_adapter/url_parser.hpp" #include "test_utilities.hpp" // Registers the fixture into the 'registry' diff --git a/test_package/agent_test.cpp b/test_package/agent_test.cpp index 675e2d16..12ab33ea 100644 --- a/test_package/agent_test.cpp +++ b/test_package/agent_test.cpp @@ -1663,7 +1663,7 @@ TEST_F(AgentTest, adapter_should_receive_commands) m_agentTestHelper->m_adapter->parseBuffer("* uuid: MK-1234\n"); m_agentTestHelper->m_ioContext.run_for(2000ms); - + m_agentTestHelper->m_adapter->parseBuffer("* manufacturer: Big Tool\n"); m_agentTestHelper->m_adapter->parseBuffer("* serialNumber: XXXX-1234\n"); m_agentTestHelper->m_adapter->parseBuffer("* station: YYYY\n"); @@ -1741,7 +1741,7 @@ TEST_F(AgentTest, adapter_should_receive_device_commands) ASSERT_EQ(string(*device2->getUuid()), device); m_agentTestHelper->m_adapter->parseBuffer("* uuid: new-uuid\n"); - + device2 = agent->getDeviceByName("Device2"); ASSERT_TRUE(device2); diff --git a/test_package/config_test.cpp b/test_package/config_test.cpp index 7742ddb4..d9b0d4b9 100644 --- a/test_package/config_test.cpp +++ b/test_package/config_test.cpp @@ -2276,7 +2276,7 @@ Adapters { } } )DOC"); - + m_config->setDebug(true); m_config->loadConfig(config); diff --git a/test_package/url_parser_test.cpp b/test_package/url_parser_test.cpp index 51afc286..f36ebd02 100644 --- a/test_package/url_parser_test.cpp +++ b/test_package/url_parser_test.cpp @@ -19,15 +19,13 @@ #include // Keep this comment to keep gtest.h above. (clang-format off/on is not working here!) -#include "mtconnect/source/adapter/agent_adapter/url_parser.hpp" +#include "mtconnect/utilities.hpp" using namespace std; using namespace mtconnect; -using namespace mtconnect::source::adapter; +using namespace mtconnect::url; using namespace std::literals; -using namespace mtconnect::source::adapter::agent_adapter; - // main int main(int argc, char *argv[]) { @@ -176,3 +174,48 @@ TEST(UrlParserTest, should_get_target_with_query) EXPECT_EQ("/Device?one=1&two=2", url.getTarget()); } + +/// Adapter URL tests + +TEST(UrlParserTest, should_parse_simple_url) +{ + Url url = Url::parse("mqtt://10.100.1.55"); + + EXPECT_EQ("mqtt", url.m_protocol); + EXPECT_EQ("10.100.1.55", url.getHost()); + EXPECT_FALSE(url.m_port); + EXPECT_EQ("/", url.m_path); +} + +TEST(UrlParserTest, should_parse_simple_url_with_port) +{ + Url url = Url::parse("mqtt://10.100.1.55:1885"); + + EXPECT_EQ("mqtt", url.m_protocol); + EXPECT_EQ("10.100.1.55", url.getHost()); + EXPECT_TRUE(url.m_port); + EXPECT_EQ(1885, url.getPort()); + EXPECT_EQ("/", url.m_path); +} + +TEST(UrlParserTest, should_parse_simple_url_with_port_with_trailing_slash) +{ + Url url = Url::parse("mqtt://10.100.1.55:1885/"); + + EXPECT_EQ("mqtt", url.m_protocol); + EXPECT_EQ("10.100.1.55", url.getHost()); + EXPECT_TRUE(url.m_port); + EXPECT_EQ(1885, url.getPort()); + EXPECT_EQ("/", url.m_path); +} + +TEST(UrlParserTest, should_parse_simple_url_with_port_with_topic_list) +{ + Url url = Url::parse("mqtt://10.100.1.55:1885/injest/#,machine1/data"); + + EXPECT_EQ("mqtt", url.m_protocol); + EXPECT_EQ("10.100.1.55", url.getHost()); + EXPECT_TRUE(url.m_port); + EXPECT_EQ(1885, url.getPort()); + EXPECT_EQ("/injest/#,machine1/data", url.m_path); +} From aee8c6b70d5e620a1396b4dfdb7ef9121ddc495e Mon Sep 17 00:00:00 2001 From: Will Sobel Date: Wed, 3 Jul 2024 16:25:37 -0400 Subject: [PATCH 4/7] Updated conan workflow to use 2.4.1 for Windows builds --- .github/workflows/build.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c14e8f22..6e438b65 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -75,6 +75,8 @@ jobs: - name: Install Conan uses: turtlebrowser/get-conan@v1.2 + with: + version: 2.4.1 - name: Initialize VS Dev Env uses: seanmiddleditch/gha-setup-vsdevenv@master @@ -96,7 +98,7 @@ jobs: - name: Build and Test C++ Agent run: | set CTEST_OUTPUT_ON_FAILURE=TRUE - conan create . --build=missing -pr conan/profiles/${{ matrix.profile }} -o with_docs=False -o cpack=True -o cpack_destination=${{ env.ZIP_DIR }} -o shared=${{ matrix.shared }} + conan create . --build=missing -pr conan/profiles/${{ matrix.profile }} -o with_docs=False -o "&:cpack=True" -o "&:cpack_destination=${{ env.ZIP_DIR }}" -o "*:shared=${{ matrix.shared }}" - name: Release uses: softprops/action-gh-release@v1 @@ -149,7 +151,7 @@ jobs: shell: bash run: | export CTEST_OUTPUT_ON_FAILURE=TRUE - conan create . --build=missing -pr conan/profiles/gcc -o shared=${{ matrix.shared }} -o with_docs=False -o cpack=True -o cpack_name=dist -o cpack_destination=${{ github.workspace }} + conan create . --build=missing -pr conan/profiles/gcc -o '*:shared=${{ matrix.shared }}' -o '&:with_docs=False' -o '&:cpack=True' -o '&:cpack_name=dist' -o '&:cpack_destination=${{ github.workspace }}' - name: Cleanse package version run: | @@ -225,5 +227,5 @@ jobs: shell: bash run: | export CTEST_OUTPUT_ON_FAILURE=TRUE - conan create . --build=missing -pr conan/profiles/macos -o shared=${{ matrix.shared }} -o with_docs=False + conan create . --build=missing -pr conan/profiles/macos -o '*:shared=${{ matrix.shared }}' -o '&:with_docs=False' From 5cc7888d2a97320bd32e8125351f23a86c842375 Mon Sep 17 00:00:00 2001 From: Will Sobel Date: Wed, 3 Jul 2024 16:36:19 -0400 Subject: [PATCH 5/7] fixed windows porting issues --- .github/workflows/build.yml | 4 +--- src/mtconnect/configuration/service.cpp | 5 ++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6e438b65..6c4db29f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -75,8 +75,6 @@ jobs: - name: Install Conan uses: turtlebrowser/get-conan@v1.2 - with: - version: 2.4.1 - name: Initialize VS Dev Env uses: seanmiddleditch/gha-setup-vsdevenv@master @@ -98,7 +96,7 @@ jobs: - name: Build and Test C++ Agent run: | set CTEST_OUTPUT_ON_FAILURE=TRUE - conan create . --build=missing -pr conan/profiles/${{ matrix.profile }} -o with_docs=False -o "&:cpack=True" -o "&:cpack_destination=${{ env.ZIP_DIR }}" -o "*:shared=${{ matrix.shared }}" + conan create . --build=missing -pr conan/profiles/${{ matrix.profile }} -o "&:with_docs=False" -o "&:cpack=True" -o "&:cpack_destination=${{ env.ZIP_DIR }}" -o "*:shared=${{ matrix.shared }}" - name: Release uses: softprops/action-gh-release@v1 diff --git a/src/mtconnect/configuration/service.cpp b/src/mtconnect/configuration/service.cpp index 5840952c..07dfe8db 100644 --- a/src/mtconnect/configuration/service.cpp +++ b/src/mtconnect/configuration/service.cpp @@ -16,9 +16,8 @@ // /* -- this file needs to be first for the servics to build correctly */ -#ifdef _WINDOWS -#include "windows.h" -#endif +#include "mtconnect/utilities.hpp" + /* keep this file first */ #include From a0ce1544347aa6c97ded5f3d43bb70dd4b77cbd4 Mon Sep 17 00:00:00 2001 From: Will Sobel Date: Wed, 3 Jul 2024 16:47:49 -0400 Subject: [PATCH 6/7] reverted build.yml --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6c4db29f..c14e8f22 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -96,7 +96,7 @@ jobs: - name: Build and Test C++ Agent run: | set CTEST_OUTPUT_ON_FAILURE=TRUE - conan create . --build=missing -pr conan/profiles/${{ matrix.profile }} -o "&:with_docs=False" -o "&:cpack=True" -o "&:cpack_destination=${{ env.ZIP_DIR }}" -o "*:shared=${{ matrix.shared }}" + conan create . --build=missing -pr conan/profiles/${{ matrix.profile }} -o with_docs=False -o cpack=True -o cpack_destination=${{ env.ZIP_DIR }} -o shared=${{ matrix.shared }} - name: Release uses: softprops/action-gh-release@v1 @@ -149,7 +149,7 @@ jobs: shell: bash run: | export CTEST_OUTPUT_ON_FAILURE=TRUE - conan create . --build=missing -pr conan/profiles/gcc -o '*:shared=${{ matrix.shared }}' -o '&:with_docs=False' -o '&:cpack=True' -o '&:cpack_name=dist' -o '&:cpack_destination=${{ github.workspace }}' + conan create . --build=missing -pr conan/profiles/gcc -o shared=${{ matrix.shared }} -o with_docs=False -o cpack=True -o cpack_name=dist -o cpack_destination=${{ github.workspace }} - name: Cleanse package version run: | @@ -225,5 +225,5 @@ jobs: shell: bash run: | export CTEST_OUTPUT_ON_FAILURE=TRUE - conan create . --build=missing -pr conan/profiles/macos -o '*:shared=${{ matrix.shared }}' -o '&:with_docs=False' + conan create . --build=missing -pr conan/profiles/macos -o shared=${{ matrix.shared }} -o with_docs=False From 6e8765767121eeeb1856f602ca1d504f360b2860 Mon Sep 17 00:00:00 2001 From: Will Sobel Date: Wed, 3 Jul 2024 16:54:00 -0400 Subject: [PATCH 7/7] modified workflow to make shared package local option. --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c14e8f22..af4e8e07 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -96,7 +96,7 @@ jobs: - name: Build and Test C++ Agent run: | set CTEST_OUTPUT_ON_FAILURE=TRUE - conan create . --build=missing -pr conan/profiles/${{ matrix.profile }} -o with_docs=False -o cpack=True -o cpack_destination=${{ env.ZIP_DIR }} -o shared=${{ matrix.shared }} + conan create . --build=missing -pr conan/profiles/${{ matrix.profile }} -o "&:with_docs=False" -o "&:cpack=True" -o "&:cpack_destination=${{ env.ZIP_DIR }}" -o "&:shared=${{ matrix.shared }}" - name: Release uses: softprops/action-gh-release@v1 @@ -149,7 +149,7 @@ jobs: shell: bash run: | export CTEST_OUTPUT_ON_FAILURE=TRUE - conan create . --build=missing -pr conan/profiles/gcc -o shared=${{ matrix.shared }} -o with_docs=False -o cpack=True -o cpack_name=dist -o cpack_destination=${{ github.workspace }} + conan create . --build=missing -pr conan/profiles/gcc -o '&:shared=${{ matrix.shared }}' -o '&:with_docs=False' -o '&:cpack=True' -o '&:cpack_name=dist' -o '&:cpack_destination=${{ github.workspace }}' - name: Cleanse package version run: | @@ -225,5 +225,5 @@ jobs: shell: bash run: | export CTEST_OUTPUT_ON_FAILURE=TRUE - conan create . --build=missing -pr conan/profiles/macos -o shared=${{ matrix.shared }} -o with_docs=False + conan create . --build=missing -pr conan/profiles/macos -o '&:shared=${{ matrix.shared }}' -o '&:with_docs=False'