diff --git a/src/c/microstrain/connections/serial/serial_port.c b/src/c/microstrain/connections/serial/serial_port.c index 9393d6f2..be117b81 100644 --- a/src/c/microstrain/connections/serial/serial_port.c +++ b/src/c/microstrain/connections/serial/serial_port.c @@ -255,6 +255,64 @@ bool serial_port_open(serial_port *port, const char *port_str, int baudrate) return true; } +bool serial_port_set_baudrate(serial_port* port, int baudrate) +{ + if(!serial_port_is_open(port)) + return false; + +#ifdef WIN32 + DCB dcb; + + if(GetCommState(port->handle, &dcb) == 0) + { + MICROSTRAIN_LOG_ERROR("GetCommState() failed with error code %d\n", GetLastError()); + return false; + } + + dcb.BaudRate = baudrate; + + if(SetCommState(port->handle, &dcb) == 0) + { + MICROSTRAIN_LOG_ERROR("SetCommState() failed with error code %d\n", GetLastError()); + return false; + } + +#elif defined __APPLE__ + + speed_t speed = baudrate; + if (ioctl(port->handle, IOSSIOSPEED, &speed) < 0) + { + MICROSTRAIN_LOG_ERROR("Unable to set baud rate (%d): %s\n", errno, strerror(errno)); + return false; + } + +#else // Linux + + // Get existing settings + struct termios serial_port_settings; + if (tcgetattr(port->handle, &serial_port_settings) < 0) + { + MICROSTRAIN_LOG_ERROR("Unable to get serial port settings (%d): %s\n", errno, strerror(errno)); + return false; + } + + if (cfsetispeed(&serial_port_settings, baud_rate_to_speed(baudrate)) < 0 || cfsetospeed(&serial_port_settings, baud_rate_to_speed(baudrate)) < 0) + { + MICROSTRAIN_LOG_ERROR("Unable to set baud rate (%d): %s\n", errno, strerror(errno)); + return false; + } + + // Persist the settings + if(tcsetattr(port->handle, TCSANOW, &serial_port_settings) < 0) + { + MICROSTRAIN_LOG_ERROR("Unable to save serial port settings (%d): %s\n", errno, strerror(errno)); + return false; + } +#endif + + return true; +} + bool serial_port_close(serial_port *port) { if(!serial_port_is_open(port)) diff --git a/src/c/microstrain/connections/serial/serial_port.h b/src/c/microstrain/connections/serial/serial_port.h index 4c5a810f..13daba8b 100644 --- a/src/c/microstrain/connections/serial/serial_port.h +++ b/src/c/microstrain/connections/serial/serial_port.h @@ -50,6 +50,7 @@ typedef struct serial_port void serial_port_init(serial_port *port); bool serial_port_open(serial_port *port, const char *port_str, int baudrate); +bool serial_port_set_baudrate(serial_port *port, int baudrate); bool serial_port_close(serial_port *port); bool serial_port_write(serial_port *port, const void *buffer, size_t num_bytes, size_t *bytes_written); bool serial_port_read(serial_port *port, void *buffer, size_t num_bytes, int wait_time, size_t *bytes_read); diff --git a/src/c/mip/mip_device_models.c b/src/c/mip/mip_device_models.c index 5bd4d47b..f7807ad9 100644 --- a/src/c/mip/mip_device_models.c +++ b/src/c/mip/mip_device_models.c @@ -12,6 +12,14 @@ namespace C { extern "C" { #endif// __cplusplus +//////////////////////////////////////////////////////////////////////////////// +///@brief Parses a string into a model number. +/// +///@param model_or_serial Device model or serial number. +/// +///@returns The model number. Note that it may not be listed in the enum (e.g. +/// if it's a new model released after this version of source code). +/// mip_model_number get_model_from_string(const char* model_or_serial) { unsigned int start_index = 0; @@ -44,6 +52,13 @@ mip_model_number get_model_from_string(const char* model_or_serial) return (mip_model_number)atoi(model_or_serial + start_index); } +//////////////////////////////////////////////////////////////////////////////// +///@brief Converts a model number to the product's model name. +/// +///@param model +/// +///@returns A string containing the (approximate) model name. +/// const char* get_model_name_from_number(mip_model_number model) { switch (model) diff --git a/src/c/mip/mip_device_models.h b/src/c/mip/mip_device_models.h index 5f9769e2..d3d8a7ab 100644 --- a/src/c/mip/mip_device_models.h +++ b/src/c/mip/mip_device_models.h @@ -9,26 +9,29 @@ extern "C" { enum mip_model_number { MODEL_UNKNOWN = 0, + // Gen 3 MODEL_3DM_DH3 = 6219,// 3DM-DH3 - MODEL_3DM_GX3_15 = 6227,// 3DM-GX3-15 MODEL_3DM_GX3_25 = 6223,// 3DM-GX3-25 MODEL_3DM_GX3_35 = 6225,// 3DM-GX3-35 + MODEL_3DM_GX3_15 = 6227,// 3DM-GX3-15 MODEL_3DM_GX3_45 = 6228,// 3DM-GX3-45 + // Gen 4 MODEL_3DM_RQ1_45_LT = 6232,// 3DM-RQ1-45-LT MODEL_3DM_GX4_15 = 6233,// 3DM-GX4-15 MODEL_3DM_GX4_25 = 6234,// 3DM-GX4-25 MODEL_3DM_GX4_45 = 6236,// 3DM-GX4-45 MODEL_3DM_RQ1_45_ST = 6239,// 3DM-RQ1-45-ST - MODEL_3DM_GX5_10 = 6255,// 3DM-GX5-10 - MODEL_3DM_GX5_15 = 6254,// 3DM-GX5-15 - MODEL_3DM_GX5_25 = 6253,// 3DM-GX5-25 - MODEL_3DM_GX5_35 = 6252,// 3DM-GX5-35 + MODEL_3DM_GQ4_45 = 6250,// 3DM-GQ4-45 + // Gen 5 MODEL_3DM_GX5_45 = 6251,// 3DM-GX5-45 - MODEL_3DM_CV5_10 = 6259,// 3DM-CV5-10 - MODEL_3DM_CV5_15 = 6258,// 3DM-CV5-15 - MODEL_3DM_CV5_25 = 6257,// 3DM-CV5-25 + MODEL_3DM_GX5_35 = 6252,// 3DM-GX5-35 + MODEL_3DM_GX5_25 = 6253,// 3DM-GX5-25 + MODEL_3DM_GX5_15 = 6254,// 3DM-GX5-15 + MODEL_3DM_GX5_10 = 6255,// 3DM-GX5-10 MODEL_3DM_CV5_45 = 6256,// 3DM-CV5-45 - MODEL_3DM_GQ4_45 = 6250,// 3DM-GQ4-45 + MODEL_3DM_CV5_25 = 6257,// 3DM-CV5-25 + MODEL_3DM_CV5_15 = 6258,// 3DM-CV5-15 + MODEL_3DM_CV5_10 = 6259,// 3DM-CV5-10 MODEL_3DM_CX5_45 = 6271,// 3DM-CX5-45 MODEL_3DM_CX5_35 = 6272,// 3DM-CX5-35 MODEL_3DM_CX5_25 = 6273,// 3DM-CX5-25 @@ -37,6 +40,7 @@ enum mip_model_number MODEL_3DM_CL5_10 = 6279,// 3DM-CL5-10 MODEL_3DM_CL5_15 = 6280,// 3DM-CL5-15 MODEL_3DM_CL5_25 = 6281,// 3DM-CL5-25 + // Gen 7 MODEL_3DM_GQ7 = 6284,// 3DM-GQ7 MODEL_3DM_RTK = 6285,// 3DM-RTK MODEL_3DM_CV7_AHRS = 6286,// 3DM-CV7-AHRS @@ -53,7 +57,6 @@ typedef enum mip_model_number mip_model_number; mip_model_number get_model_from_string(const char* model_or_serial); const char* get_model_name_from_number(mip_model_number model); - #ifdef __cplusplus } // extern "C" } // namespace C diff --git a/src/cpp/microstrain/connections/serial/serial_connection.cpp b/src/cpp/microstrain/connections/serial/serial_connection.cpp index 54e7e1ed..e8e623e1 100644 --- a/src/cpp/microstrain/connections/serial/serial_connection.cpp +++ b/src/cpp/microstrain/connections/serial/serial_connection.cpp @@ -52,6 +52,17 @@ bool SerialConnection::disconnect() return serial_port_close(&mPort); } +///@brief Change the baudrate +bool SerialConnection::setBaudrate(uint32_t baud) +{ + bool ok = serial_port_set_baudrate(&mPort, baud); + + if(ok) + mBaudrate = baud; + + return ok; +} + ///@copydoc microstrain::Connection::recvFromDevice diff --git a/src/cpp/microstrain/connections/serial/serial_connection.hpp b/src/cpp/microstrain/connections/serial/serial_connection.hpp index cc1af1df..7dbb0ef2 100644 --- a/src/cpp/microstrain/connections/serial/serial_connection.hpp +++ b/src/cpp/microstrain/connections/serial/serial_connection.hpp @@ -40,6 +40,10 @@ class SerialConnection : public microstrain::Connection baudrate = mBaudrate; }; + uint32_t baudrate() const { return mBaudrate; } + + bool setBaudrate(uint32_t baud); + private: serial_port mPort; std::string mPortName; diff --git a/src/cpp/microstrain/connections/tcp/tcp_connection.cpp b/src/cpp/microstrain/connections/tcp/tcp_connection.cpp index 80a2b78b..652cada6 100644 --- a/src/cpp/microstrain/connections/tcp/tcp_connection.cpp +++ b/src/cpp/microstrain/connections/tcp/tcp_connection.cpp @@ -14,9 +14,9 @@ namespace connections /// ///@param hostname Host name or IP address to connect to ///@param port Port on hostName to connect to -TcpConnection::TcpConnection(const std::string& hostname, uint16_t port) +TcpConnection::TcpConnection(const std::string &hostname, uint16_t port) { - mHostname = hostname; + mHostname = std::move(hostname); mPort = port; mType = TYPE; diff --git a/src/cpp/microstrain/connections/tcp/tcp_connection.hpp b/src/cpp/microstrain/connections/tcp/tcp_connection.hpp index 60efb400..f8b4b299 100644 --- a/src/cpp/microstrain/connections/tcp/tcp_connection.hpp +++ b/src/cpp/microstrain/connections/tcp/tcp_connection.hpp @@ -23,6 +23,7 @@ class TcpConnection : public microstrain::Connection public: static constexpr auto TYPE = "TCP"; + TcpConnection() = default; TcpConnection(const std::string& hostname, uint16_t port); ~TcpConnection(); diff --git a/src/cpp/mip/extras/CMakeLists.txt b/src/cpp/mip/extras/CMakeLists.txt index 679c89ac..234e8788 100644 --- a/src/cpp/mip/extras/CMakeLists.txt +++ b/src/cpp/mip/extras/CMakeLists.txt @@ -6,6 +6,9 @@ add_library(${MIP_EXTRAS_LIBRARY} "${CMAKE_CURRENT_LIST_DIR}/descriptor_id.hpp" "${CMAKE_CURRENT_LIST_DIR}/firmware_version.cpp" "${CMAKE_CURRENT_LIST_DIR}/firmware_version.hpp" + "${CMAKE_CURRENT_LIST_DIR}/mip_extras_all.hpp" + "${CMAKE_CURRENT_LIST_DIR}/platform_connection.cpp" + "${CMAKE_CURRENT_LIST_DIR}/platform_connection.hpp" ) target_compile_features(${MIP_EXTRAS_LIBRARY} PUBLIC cxx_std_11) diff --git a/src/cpp/mip/extras/composite_result.hpp b/src/cpp/mip/extras/composite_result.hpp index 26028725..5da19350 100644 --- a/src/cpp/mip/extras/composite_result.hpp +++ b/src/cpp/mip/extras/composite_result.hpp @@ -26,6 +26,7 @@ namespace mip operator bool() const { return result; } bool operator!() const { return !result; } + operator CmdResult() const { return result; } bool operator==(mip::C::mip_cmd_result value) const { return result == value; } bool operator!=(mip::C::mip_cmd_result value) const { return result != value; } diff --git a/src/cpp/mip/extras/firmware_version.cpp b/src/cpp/mip/extras/firmware_version.cpp index f59ef8e3..87c2bac6 100644 --- a/src/cpp/mip/extras/firmware_version.cpp +++ b/src/cpp/mip/extras/firmware_version.cpp @@ -24,7 +24,8 @@ void FirmwareVersion::toString(char* buffer, size_t buffer_size) const ///@brief Reads a standard-format string (X.Y.ZZ\0 or XYZZ\0). /// ///@param str Input string. Can be unterminated if length is specified. -///@param length Length of input string. Assumed to be NULL-terminated if -1. +///@param length Limits reading this many chars from str. Can be larger if str +/// is terminated. Use -1 if unknown and str is terminated. /// ///@return True if a valid version was parsed. /// @@ -35,7 +36,7 @@ bool FirmwareVersion::fromString(const char* str, size_t length) unsigned int digit = 0; for(unsigned int i=0; i +#if __cpp_impl_three_way_comparison +#include +#endif #if __cpp_lib_string_view >= 201606L #include #endif @@ -49,11 +52,21 @@ class FirmwareVersion uint8_t patch() const { return m_version % 100; } bool operator==(FirmwareVersion other) const { return m_version == other.m_version; } - bool operator!=(FirmwareVersion other) const { return m_version != other.m_version; } - bool operator<=(FirmwareVersion other) const { return m_version <= other.m_version; } - bool operator>=(FirmwareVersion other) const { return m_version >= other.m_version; } - bool operator< (FirmwareVersion other) const { return m_version < other.m_version; } - bool operator> (FirmwareVersion other) const { return m_version > other.m_version; } + bool operator!=(FirmwareVersion other) const { return !(*this == other); } +#ifndef __cpp_impl_three_way_comparison + bool operator<=(FirmwareVersion other) const { return !isNull() && major() == other.major() && m_version <= other.m_version; } + bool operator>=(FirmwareVersion other) const { return !isNull() && major() == other.major() && m_version >= other.m_version; } + bool operator< (FirmwareVersion other) const { return !isNull() && major() == other.major() && m_version < other.m_version; } + bool operator> (FirmwareVersion other) const { return !isNull() && major() == other.major() && m_version > other.m_version; } +#else // __cpp_impl_three_way_comparison + auto operator<=>(FirmwareVersion other) const + { + if(major() != other.major() || isNull()) + return std::partial_ordering::unordered; + else + return std::partial_ordering(m_version <=> other.m_version); + } +#endif // __cpp_impl_three_way_comparison void toString(char* buffer, size_t buffer_size) const; bool fromString(const char* str, size_t length=-1); diff --git a/src/cpp/mip/extras/platform_connection.cpp b/src/cpp/mip/extras/platform_connection.cpp new file mode 100644 index 00000000..7806ceb5 --- /dev/null +++ b/src/cpp/mip/extras/platform_connection.cpp @@ -0,0 +1,82 @@ +#pragma once + +#include "mip/extras/platform_connection.hpp" + +#if MIP_USE_SERIAL +#include "serial_connection.hpp" +#endif + +#if MIP_USE_TCP +#include "tcp_connection.hpp" +#endif + +#include + +#include + + +namespace mip +{ +namespace platform +{ + //////////////////////////////////////////////////////////////////////////////// + ///@brief Creates a connection object given the interface name and a parameter. + /// + ///@note This only creates the connection object; it does not open it. Call + /// `connection->connect()` to open it. + /// + ///@param interfaceName + /// This is the interface name - COM* (windows) or /dev/tty* (Linux) for + /// a serial port or anything else for a TCP socket. + /// + ///@param parameter + /// For serial ports, this is the baud rate. + /// For TCP sockets, this is the port number. + /// + std::unique_ptr createConnectionFromInterfaceName(std::string interface_name, uint32_t parameter) + { +#ifdef MIP_USE_SERIAL + // Todo: Detect USB connections (interface_name.find("ttyACM0") or similar) + if(isSerialInterfaceName(interface_name)) + return std::make_unique(std::move(interface_name), parameter); +#endif + +#ifdef MIP_USE_TCP + if(isNetworkInterfaceName(interface_name)) + return std::make_unique(std::move(interface_name), parameter); +#endif + + return nullptr; + } + + //////////////////////////////////////////////////////////////////////////////// + ///@brief Determines if the name corresponds to a serial port device. + /// + ///@param interface_name + /// + ///@returns True if the interface is likely a serial port. False otherwise. + /// + bool isSerialInterfaceName(std::string_view interface_name) + { +#ifdef WIN32 + return interface_name.find("COM", 0) != std::string::npos; // todo: make case insensitive +#else + return interface_name.rfind("/dev/", 0) == 0; +#endif + } + + //////////////////////////////////////////////////////////////////////////////// + ///@brief Determines if the name corresponds to a URL or IP address. + /// + ///@param interface_name + /// + ///@returns True if the interface could be a URL or IP address. False otherwise. + /// + bool isNetworkInterfaceName(std::string_view interface_name) + { + return interface_name == "localhost" || interface_name.find('.') != std::string::npos; + } + + +} // namespace platform +} // namespace mip diff --git a/src/cpp/mip/extras/platform_connection.hpp b/src/cpp/mip/extras/platform_connection.hpp new file mode 100644 index 00000000..d8f2b11b --- /dev/null +++ b/src/cpp/mip/extras/platform_connection.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "mip/mip_interface.hpp" + +#include +#include + +// tsets + +namespace mip +{ + namespace platform + { + std::unique_ptr createConnectionFromInterfaceName(std::string interface_name, uint32_t parameter); + + bool isNetworkInterfaceName(std::string_view interface_name); + bool isSerialInterfaceName(std::string_view interface_name); + + } // namespace platform +} // namespace mip diff --git a/src/cpp/mip/mip_descriptors.hpp b/src/cpp/mip/mip_descriptors.hpp index d0928209..21ca5401 100644 --- a/src/cpp/mip/mip_descriptors.hpp +++ b/src/cpp/mip/mip_descriptors.hpp @@ -103,6 +103,7 @@ struct TypedResult : public CmdResult // Same constructor as CmdResult. using CmdResult::CmdResult; + TypedResult(CmdResult existing) : CmdResult(existing) {} ///@brief The command descriptor. /// diff --git a/src/cpp/mip/mip_interface.hpp b/src/cpp/mip/mip_interface.hpp index 5b4f3de1..280d9f4e 100644 --- a/src/cpp/mip/mip_interface.hpp +++ b/src/cpp/mip/mip_interface.hpp @@ -26,7 +26,6 @@ class Interface; void connect_interface(mip::Interface& dev, microstrain::Connection& conn); - using DispatchHandler = C::mip_dispatch_handler; struct Dispatcher : public C::mip_dispatcher