diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c46afc5..e926ab5b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -178,6 +178,8 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") src/cpp/transport/udp/UDPv6AgentWindows.cpp src/cpp/transport/tcp/TCPv4AgentWindows.cpp src/cpp/transport/tcp/TCPv6AgentWindows.cpp + src/cpp/transport/serial/SerialAgentWindows.cpp + src/cpp/transport/serial/TermiosAgentWindows.cpp $<$:src/cpp/transport/discovery/DiscoveryServerWindows.cpp> ) endif() diff --git a/include/uxr/agent/transport/serial/SerialAgentWindows.hpp b/include/uxr/agent/transport/serial/SerialAgentWindows.hpp new file mode 100644 index 00000000..86b4133c --- /dev/null +++ b/include/uxr/agent/transport/serial/SerialAgentWindows.hpp @@ -0,0 +1,79 @@ +// Copyright 2017-present Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// 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. + +#ifndef UXR_AGENT_TRANSPORT_SERIAL_SERIALAGENTWINDOWS_HPP_ +#define UXR_AGENT_TRANSPORT_SERIAL_SERIALAGENTWINDOWS_HPP_ + +#include +#include +#include + +#include +#include +#include + +namespace eprosima { +namespace uxr { + +class SerialAgent : public Server +{ +public: + SerialAgent( + uint8_t addr, + Middleware::Kind middleware_kind); + +#ifdef UAGENT_DISCOVERY_PROFILE + bool has_discovery() final { return false; } +#endif + +#ifdef UAGENT_P2P_PROFILE + bool has_p2p() final { return false; } +#endif + +private: + virtual bool init() = 0; + + virtual bool fini() = 0; + + bool recv_message( + InputPacket& input_packet, + int timeout, + TransportRc& transport_rc) final; + + bool send_message( + OutputPacket output_packet, + TransportRc& transport_rc) final; + + ssize_t write_data( + uint8_t* buf, + size_t len, + TransportRc& transport_rc); + + ssize_t read_data( + uint8_t* buf, + size_t len, + int timeout, + TransportRc& transport_rc); + +protected: + const uint8_t addr_; + HANDLE serial_handle; + uint8_t buffer_[SERVER_BUFFER_SIZE]; + FramingIO framing_io_; +}; + +} // namespace uxr +} // namespace eprosima + +#endif // UXR_AGENT_TRANSPORT_SERIAL_SERIALAGENTWINDOWS_HPP_ diff --git a/include/uxr/agent/transport/serial/TermiosAgentWindows.hpp b/include/uxr/agent/transport/serial/TermiosAgentWindows.hpp new file mode 100644 index 00000000..369c0f95 --- /dev/null +++ b/include/uxr/agent/transport/serial/TermiosAgentWindows.hpp @@ -0,0 +1,49 @@ +// Copyright 2017-present Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// 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. + +#ifndef UXR_AGENT_TRANSPORT_SERIAL_TERMIOSAGENTWINDOWS_HPP_ +#define UXR_AGENT_TRANSPORT_SERIAL_TERMIOSAGENTWINDOWS_HPP_ + +#include +namespace eprosima { +namespace uxr { + +class TermiosAgent : public SerialAgent +{ +public: + TermiosAgent( + char const * dev, + DCB const & termios_attrs, + uint8_t addr, + Middleware::Kind middleware_kind); + + ~TermiosAgent(); + + HANDLE getfd() { return serial_handle; }; + +private: + bool init() final; + bool fini() final; + bool handle_error( + TransportRc transport_rc) final; + +private: + const std::string dev_; + const DCB termios_attrs_; +}; + +} // namespace uxr +} // namespace eprosima + +#endif // UXR_AGENT_TRANSPORT_SERIAL_TERMIOSAGENTWINDOWS_HPP_ \ No newline at end of file diff --git a/include/uxr/agent/transport/serial/baud_rate_table_windows.h b/include/uxr/agent/transport/serial/baud_rate_table_windows.h new file mode 100644 index 00000000..fed34f51 --- /dev/null +++ b/include/uxr/agent/transport/serial/baud_rate_table_windows.h @@ -0,0 +1,86 @@ +// Copyright 2019 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// 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. + +#ifndef UXR_AGENT_TRANSPORT_SERIAL_BAUD_RATE_TABLE_H +#define UXR_AGENT_TRANSPORT_SERIAL_BAUD_RATE_TABLE_H + +#include +#include +#include + +inline +DWORD getBaudRate(const char* baudrate_str) +{ + DWORD rv; + if (0 == strcmp(baudrate_str, "0")) + { + rv = 0; + } + else if (0 == strcmp(baudrate_str, "110")) + { + rv = CBR_110; + } + else if (0 == strcmp(baudrate_str, "300")) { + rv = CBR_300; + } + else if (0 == strcmp(baudrate_str, "600")) { + rv = CBR_600; + } + else if (0 == strcmp(baudrate_str, "1200")) { + rv = CBR_1200; + } + else if (0 == strcmp(baudrate_str, "2400")) { + rv = CBR_2400; + } + else if (0 == strcmp(baudrate_str, "4800")) { + rv = CBR_4800; + } + else if (0 == strcmp(baudrate_str, "9600")) { + rv = CBR_9600; + } + else if (0 == strcmp(baudrate_str, "14400")) { + rv = CBR_14400; + } + else if (0 == strcmp(baudrate_str, "19200")) { + rv = CBR_19200; + } + else if (0 == strcmp(baudrate_str, "38400")) { + rv = CBR_38400; + } + else if (0 == strcmp(baudrate_str, "56000")) { + rv = CBR_56000; + } + else if (0 == strcmp(baudrate_str, "57600")) { + rv = CBR_57600; + } + else if (0 == strcmp(baudrate_str, "115200")) { + rv = CBR_115200; + } + else if (0 == strcmp(baudrate_str, "128000")) { + rv = CBR_128000; + } + else if (0 == strcmp(baudrate_str, "256000")) { + rv = CBR_256000; + } + else + { + DWORD custom_baud_rate = (DWORD)atoi(baudrate_str); + printf("Warning: Custom baud rate set to: %d\n", custom_baud_rate); + rv = custom_baud_rate; + } + return rv; +} + +#endif // UXR_AGENT_TRANSPORT_SERIAL_BAUD_RATE_TABLE_H + diff --git a/include/uxr/agent/utils/ArgumentParser.hpp b/include/uxr/agent/utils/ArgumentParser.hpp index fc6d7795..29e618d1 100644 --- a/include/uxr/agent/utils/ArgumentParser.hpp +++ b/include/uxr/agent/utils/ArgumentParser.hpp @@ -29,6 +29,8 @@ #include #include #include +#include +#include #else #include #include @@ -64,11 +66,11 @@ enum class TransportKind UDP6, TCP4, TCP6, + SERIAL, #ifndef _WIN32 #ifdef UAGENT_SOCKETCAN_PROFILE CAN, #endif // UAGENT_SOCKETCAN_PROFILE - SERIAL, MULTISERIAL, PSEUDOTERMINAL, #endif // _WIN32 @@ -659,7 +661,6 @@ class IPvXArgs Argument port_; }; -#ifndef _WIN32 /************************************************************************************************* * Specific arguments for pseudoterminal transports *************************************************************************************************/ @@ -782,6 +783,8 @@ class SerialArgs : public PseudoTerminalArgs Argument file_; }; + +#ifndef _WIN32 /************************************************************************************************* * Specific arguments for multi serial termios transports *************************************************************************************************/ @@ -943,11 +946,11 @@ class ArgumentParser , argv_(argv) , common_args_() , ip_args_() + , serial_args_() #ifndef _WIN32 #ifdef UAGENT_SOCKETCAN_PROFILE , can_args_() #endif // UAGENT_SOCKETCAN_PROFILE - , serial_args_() , multiserial_args_() , pseudoterminal_args_() #endif // _WIN32 @@ -979,6 +982,11 @@ class ArgumentParser result &= ip_args_.parse(argc_, argv_); break; } + case TransportKind::SERIAL: + { + result &= serial_args_.parse(argc_, argv_); + break; + } #ifndef _WIN32 #ifdef UAGENT_SOCKETCAN_PROFILE case TransportKind::CAN: @@ -987,11 +995,6 @@ class ArgumentParser break; } #endif // UAGENT_SOCKETCAN_PROFILE - case TransportKind::SERIAL: - { - result &= serial_args_.parse(argc_, argv_); - break; - } case TransportKind::MULTISERIAL: { result &= multiserial_args_.parse(argc_, argv_); @@ -1075,6 +1078,29 @@ class ArgumentParser attr.c_ospeed = baudrate; #endif + return attr; + } +#else + DCB init_termios(const char* baudrate_str) { + DCB attr = {}; + + /* Setting baudrate. */ + attr.Parity = NOPARITY; + attr.StopBits = ONESTOPBIT; + attr.ByteSize = 8; + attr.BaudRate = getBaudRate(baudrate_str); + + attr.fBinary = FALSE; + attr.fParity = FALSE; + attr.fOutxCtsFlow = FALSE; + attr.fOutxDsrFlow = FALSE; + attr.fDtrControl = DTR_CONTROL_ENABLE; + attr.fRtsControl = RTS_CONTROL_ENABLE; + attr.fInX = FALSE; + attr.fOutX = FALSE; + attr.fDsrSensitivity = FALSE; + attr.fErrorChar = FALSE; + return attr; } #endif // _WIN32 @@ -1107,11 +1133,11 @@ class ArgumentParser char** argv_; CommonArgs common_args_; IPvXArgs ip_args_; + SerialArgs serial_args_; #ifndef _WIN32 #ifdef UAGENT_SOCKETCAN_PROFILE CanArgs can_args_; #endif // UAGENT_SOCKETCAN_PROFILE - SerialArgs serial_args_; MultiSerialArgs multiserial_args_; PseudoTerminalArgs pseudoterminal_args_; #endif // _WIN32 @@ -1119,13 +1145,20 @@ class ArgumentParser std::unique_ptr agent_server_; }; +template<> inline bool ArgumentParser::launch_agent() { #ifndef _WIN32 -template<> inline bool ArgumentParser::launch_agent() -{ - struct termios attr = init_termios(serial_args_.baud_rate().c_str()); - + termios +#else + DCB +#endif + attr = init_termios(serial_args_.baud_rate().c_str()); + agent_server_.reset(new TermiosAgent( - serial_args_.dev().c_str(), O_RDWR | O_NOCTTY, attr, 0, utils::get_mw_kind(common_args_.middleware()))); +#ifndef _WIN32 + serial_args_.dev().c_str(), O_RDWR | O_NOCTTY, attr, 0, utils::get_mw_kind(common_args_.middleware()))); +#else + serial_args_.dev().c_str(), attr, 0, utils::get_mw_kind(common_args_.middleware()))); +#endif if (agent_server_->start()) { @@ -1140,6 +1173,8 @@ template<> inline bool ArgumentParser::launch_agent() return false; } + +#ifndef _WIN32 template<> inline bool ArgumentParser::launch_agent() { struct termios attr = init_termios(multiserial_args_.baud_rate().c_str()); diff --git a/src/cpp/AgentInstance.cpp b/src/cpp/AgentInstance.cpp index df551dca..e6891847 100644 --- a/src/cpp/AgentInstance.cpp +++ b/src/cpp/AgentInstance.cpp @@ -64,6 +64,11 @@ bool AgentInstance::create( agent_thread_ = std::move(agent::create_agent_thread(argc, argv, exit_signal, valid_transport)); break; } + case agent::TransportKind::SERIAL: + { + agent_thread_ = std::move(agent::create_agent_thread(argc, argv, exit_signal, valid_transport)); + break; + } #ifndef _WIN32 #ifdef UAGENT_SOCKETCAN_PROFILE case agent::TransportKind::CAN: @@ -72,11 +77,6 @@ bool AgentInstance::create( break; } #endif // UAGENT_SOCKETCAN_PROFILE - case agent::TransportKind::SERIAL: - { - agent_thread_ = std::move(agent::create_agent_thread(argc, argv, exit_signal, valid_transport)); - break; - } case agent::TransportKind::MULTISERIAL: { agent_thread_ = std::move(agent::create_agent_thread(argc, argv, exit_signal, valid_transport)); diff --git a/src/cpp/transport/serial/SerialAgentWindows.cpp b/src/cpp/transport/serial/SerialAgentWindows.cpp new file mode 100644 index 00000000..2cdd4059 --- /dev/null +++ b/src/cpp/transport/serial/SerialAgentWindows.cpp @@ -0,0 +1,133 @@ +// Copyright 2018 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// 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 +#include +#include + +namespace eprosima { +namespace uxr { + +SerialAgent::SerialAgent( + uint8_t addr, + Middleware::Kind middleware_kind) + : Server{middleware_kind} + , addr_{addr} + , serial_handle{ INVALID_HANDLE_VALUE } + , buffer_{0} + , framing_io_( + addr, + std::bind(&SerialAgent::write_data, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), + std::bind(&SerialAgent::read_data, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)) +{} + +ssize_t SerialAgent::write_data( + uint8_t* buf, + size_t len, + TransportRc& transport_rc) +{ + size_t rv = 0; + DWORD bytes_written = 0; + + if (WriteFile(serial_handle, buf, static_cast(len), &bytes_written, nullptr)) + { + rv = size_t(bytes_written); + } + else + { + transport_rc = TransportRc::server_error; + } + return rv; +} + +ssize_t SerialAgent::read_data( + uint8_t* buf, + size_t len, + int timeout, + TransportRc& transport_rc) +{ + DWORD bytes_read = 0; + + if (!ReadFile(serial_handle, buf, static_cast(len), &bytes_read, nullptr)) { + transport_rc = TransportRc::server_error; + } + return bytes_read; +} + +bool SerialAgent::recv_message( + InputPacket& input_packet, + int timeout, + TransportRc& transport_rc) +{ + bool rv = false; + uint8_t remote_addr = 0x00; + ssize_t bytes_read = 0; + + do + { + bytes_read = framing_io_.read_framed_msg( + buffer_, SERVER_BUFFER_SIZE, remote_addr, timeout, transport_rc); + } + while ((0 == bytes_read) && (0 < timeout)); + + if (0 < bytes_read) + { + input_packet.message.reset(new InputMessage(buffer_, static_cast(bytes_read))); + input_packet.source = SerialEndPoint(remote_addr); + rv = true; + + uint32_t raw_client_key; + if (Server::get_client_key(input_packet.source, raw_client_key)) + { + UXR_AGENT_LOG_MESSAGE( + UXR_DECORATE_YELLOW("[==>> SER <<==]"), + raw_client_key, + input_packet.message->get_buf(), + input_packet.message->get_len()); + } + } + return rv; +} + +bool SerialAgent::send_message( + OutputPacket output_packet, + TransportRc& transport_rc) +{ + bool rv = false; + ssize_t bytes_written = + framing_io_.write_framed_msg( + output_packet.message->get_buf(), + output_packet.message->get_len(), + output_packet.destination.get_addr(), + transport_rc); + if ((0 < bytes_written) && ( + static_cast(bytes_written) == output_packet.message->get_len())) + { + rv = true; + + uint32_t raw_client_key; + if (Server::get_client_key(output_packet.destination, raw_client_key)) + { + UXR_AGENT_LOG_MESSAGE( + UXR_DECORATE_YELLOW("[** <> **]"), + raw_client_key, + output_packet.message->get_buf(), + output_packet.message->get_len()); + } + } + return rv; +} + +} // namespace uxr +} // namespace eprosima diff --git a/src/cpp/transport/serial/TermiosAgentWindows.cpp b/src/cpp/transport/serial/TermiosAgentWindows.cpp new file mode 100644 index 00000000..3ee170e9 --- /dev/null +++ b/src/cpp/transport/serial/TermiosAgentWindows.cpp @@ -0,0 +1,199 @@ +// Copyright 2017-present Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// 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 + +#include + +namespace eprosima { +namespace uxr { + +TermiosAgent::TermiosAgent( + char const* dev, + DCB const& termios_attrs, + uint8_t addr, + Middleware::Kind middleware_kind) + : SerialAgent(addr, middleware_kind) + , dev_{dev} + , termios_attrs_{termios_attrs} +{ +} + +TermiosAgent::~TermiosAgent() +{ + try + { + stop(); + } + catch (std::exception& e) + { + UXR_AGENT_LOG_CRITICAL( + UXR_DECORATE_RED("error stopping server"), + "exception: {}", + e.what()); + } +} + +bool TermiosAgent::init() +{ + bool rv = false; + /* + + // Check if serial port exist + std::chrono::steady_clock::time_point begin; + int serial_exist = 0; + int error_count = 0; + + do + { + if (serial_exist != 0) + { + std::this_thread::sleep_for((std::chrono::milliseconds) 10); + + if (EACCES == errno || EBUSY == errno) + { + // Increase error count + error_count++; + + if (error_count > 10) + { + // Resource busy or superuser privileges required + break; + } + } + else if (std::chrono::duration_cast(std::chrono::steady_clock::now() - begin).count()) + { + begin = std::chrono::steady_clock::now(); + UXR_AGENT_LOG_INFO( + UXR_DECORATE_YELLOW("Serial port not found."), + "device: {}, error {}, waiting for connection...", + dev_, errno); + } + } + + serial_exist = access(dev_.c_str(), W_OK ); + } + while (serial_exist != 0); + */ + + serial_handle = CreateFile(dev_.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr); + if (INVALID_HANDLE_VALUE != serial_handle) + { + DCB new_attrs = {}; + new_attrs.DCBlength = sizeof(DCB); + + if (GetCommState(serial_handle, &new_attrs)) { + new_attrs.BaudRate = termios_attrs_.BaudRate; + new_attrs.Parity = termios_attrs_.Parity; + new_attrs.StopBits = termios_attrs_.StopBits; + new_attrs.ByteSize = termios_attrs_.ByteSize; + + new_attrs.fBinary = termios_attrs_.fBinary; + new_attrs.fParity = termios_attrs_.fParity; + new_attrs.fOutxCtsFlow = termios_attrs_.fOutxCtsFlow; + new_attrs.fOutxDsrFlow = termios_attrs_.fOutxDsrFlow; + new_attrs.fDtrControl = termios_attrs_.fDtrControl; + new_attrs.fRtsControl = termios_attrs_.fRtsControl; + new_attrs.fInX = termios_attrs_.fInX; + new_attrs.fOutX = termios_attrs_.fOutX; + new_attrs.fDsrSensitivity = termios_attrs_.fDsrSensitivity; + new_attrs.fErrorChar = termios_attrs_.fErrorChar; + + + if (SetCommState(serial_handle, &new_attrs)) + { + COMMTIMEOUTS comm_timeouts{}; + comm_timeouts.ReadIntervalTimeout = MAXDWORD; + comm_timeouts.ReadTotalTimeoutMultiplier = 0; + comm_timeouts.ReadTotalTimeoutConstant = 0;; + comm_timeouts.WriteTotalTimeoutConstant = 0, + comm_timeouts.WriteTotalTimeoutMultiplier = 0; + if (!SetCommTimeouts(serial_handle, &comm_timeouts)) { + UXR_AGENT_LOG_ERROR( + UXR_DECORATE_RED("set SetCommTimeouts error"), + "errno: {}", + errno); + } + else { + rv = true; + + UXR_AGENT_LOG_INFO( + UXR_DECORATE_GREEN("running..."), + "handle: {}", + serial_handle); + } + } + else + { + UXR_AGENT_LOG_ERROR( + UXR_DECORATE_RED("set termios attributes error"), + "errno: {}", + errno); + } + } + else + { + UXR_AGENT_LOG_ERROR( + UXR_DECORATE_RED("get termios attributes error"), + "errno: {}", + errno); + } + } + else + { + UXR_AGENT_LOG_ERROR( + UXR_DECORATE_RED("open device error"), + "device: {}, errno: {}{}", + dev_, errno, + (EACCES == errno) ? ". Please re-run with superuser privileges." : ""); + } + return rv; +} + +bool TermiosAgent::fini() +{ + if (INVALID_HANDLE_VALUE == serial_handle) + { + return true; + } + + bool rv = false; + if (CloseHandle(serial_handle)) + { + UXR_AGENT_LOG_INFO( + UXR_DECORATE_GREEN("server stopped"), + "handle: {}", + serial_handle); + rv = true; + } + else + { + UXR_AGENT_LOG_ERROR( + UXR_DECORATE_RED("close server error"), + "handle: {}, errno: {}", + serial_handle, errno); + } + + serial_handle = INVALID_HANDLE_VALUE; + return rv; +} + +bool TermiosAgent::handle_error( + TransportRc /*transport_rc*/) +{ + return fini() && init(); +} + +} // namespace uxr +} // namespace eprosima diff --git a/src/cpp/utils/ArgumentParser.cpp b/src/cpp/utils/ArgumentParser.cpp index a34505ce..b22411a2 100644 --- a/src/cpp/utils/ArgumentParser.cpp +++ b/src/cpp/utils/ArgumentParser.cpp @@ -31,9 +31,9 @@ bool eprosima::uxr::agent::parser::utils::usage( executable_name_str = executable_name_str.substr(pos + 1); } std::stringstream ss; - ss << "Usage: '" << executable_name_str << " <>'" << std::endl; if (no_help) @@ -53,11 +53,11 @@ eprosima::uxr::agent::TransportKind eprosima::uxr::agent::parser::utils::check_t {"udp6", eprosima::uxr::agent::TransportKind::UDP6}, {"tcp4", eprosima::uxr::agent::TransportKind::TCP4}, {"tcp6", eprosima::uxr::agent::TransportKind::TCP6}, + {"serial", eprosima::uxr::agent::TransportKind::SERIAL}, #ifndef _WIN32 #ifdef UAGENT_SOCKETCAN_PROFILE {"canfd", eprosima::uxr::agent::TransportKind::CAN}, #endif // UAGENT_SOCKETCAN_PROFILE - {"serial", eprosima::uxr::agent::TransportKind::SERIAL}, {"multiserial", eprosima::uxr::agent::TransportKind::MULTISERIAL}, {"pseudoterminal", eprosima::uxr::agent::TransportKind::PSEUDOTERMINAL}, #endif // _WIN32