├── .clang-format ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.cn.md ├── README.md ├── cmake └── utils.cmake ├── debian ├── changelog ├── control ├── copyright ├── rules └── source │ └── format ├── doc ├── API │ ├── cn │ │ ├── API.cn.md │ │ ├── ControllerLog.cn.md │ │ ├── Dashboard.cn.md │ │ ├── EliteDriver.cn.md │ │ ├── Log.cn.md │ │ ├── PrimaryPort.cn.md │ │ ├── RTSI.cn.md │ │ ├── RemoteUpgrade.cn.md │ │ └── VersionInfo.cn.md │ └── en │ │ ├── API.en.md │ │ ├── ControllerLog.en.md │ │ ├── Dashboard.en.md │ │ ├── EliteDriver.en.md │ │ ├── Log.en.md │ │ ├── PrimaryPort.en.md │ │ ├── RTSI.en.md │ │ ├── RemoteUpgrade.en.md │ │ └── VersionInfo.en.md ├── Architecture │ ├── Arch.cn.md │ ├── Arch.en.md │ ├── ControlScript-trajectory_socket.drawio.png │ ├── ControlScript.drawio.png │ ├── ExternalControl.drawio.png │ └── SDKDataFlow.drawio.png ├── BuildGuide │ ├── BuildGuide.cn.md │ └── BuildGuide.en.md ├── CMakeLists.txt ├── MessageFormat │ ├── MessageFormat.cn.md │ └── MessageFormat.en.md └── UserGuide │ ├── cn │ ├── Dashboard.cn.md │ ├── EliteDriver.cn.md │ ├── Log.cn.md │ ├── PrimaryPort.cn.md │ ├── RTSI.cn.md │ └── UserGuide.cn.md │ └── en │ ├── Dashboard.en.md │ ├── EliteDriver.en.md │ ├── Log.en.md │ ├── PrimaryPort.en.md │ ├── RTSI.en.md │ └── UserGuide.en.md ├── elite-cs-series-sdkConfig.cmake.in ├── example ├── CMakeLists.txt ├── dashboard_example.cpp ├── primary_example.cpp ├── resource │ ├── input_recipe.txt │ └── output_recipe.txt ├── rtsi_example.cpp ├── servo_example.cpp ├── speedj_example.cpp ├── speedl_example.cpp └── trajectory_example.cpp ├── include ├── Common │ ├── SshUtils.hpp │ ├── TcpServer.hpp │ └── Utils.hpp ├── Control │ ├── ControlCommon.hpp │ ├── ReverseInterface.hpp │ ├── ScriptCommandInterface.hpp │ ├── ScriptSender.hpp │ └── TrajectoryInterface.hpp ├── Dashboard │ └── DashboardClient.hpp ├── Elite │ ├── ControlMode.hpp │ ├── ControllerLog.hpp │ ├── DataType.hpp │ ├── DefaultLogHandler.hpp │ ├── EliteDriver.hpp │ ├── Log.hpp │ ├── Logger.hpp │ ├── RemoteUpgrade.hpp │ └── VersionInfo.hpp ├── EliteException.hpp ├── EliteOptions.hpp.in ├── Primary │ ├── PrimaryPackage.hpp │ ├── PrimaryPort.hpp │ ├── PrimaryPortInterface.hpp │ └── RobotConfPackage.hpp └── Rtsi │ ├── RtsiClient.hpp │ ├── RtsiClientInterface.hpp │ ├── RtsiIOInterface.hpp │ ├── RtsiRecipe.hpp │ └── RtsiRecipeInternal.hpp ├── source ├── Common │ ├── EliteException.cpp │ ├── SshUtils.cpp │ ├── TcpServer.cpp │ └── Utils.cpp ├── Control │ ├── ReverseInterface.cpp │ ├── ScriptCommandInterface.cpp │ ├── ScriptSender.cpp │ └── TrajectoryInterface.cpp ├── Dashboard │ └── DashboardClient.cpp ├── Elite │ ├── ControllerLog.cpp │ ├── EliteDriver.cpp │ ├── Log.cpp │ ├── Logger.cpp │ ├── RemoteUpgrade.cpp │ └── VersionInfo.cpp ├── Primary │ ├── PrimaryPort.cpp │ ├── PrimaryPortInterface.cpp │ └── RobotConfPackage.cpp ├── Rtsi │ ├── RtsiClient.cpp │ ├── RtsiClientInterface.cpp │ ├── RtsiIOInterface.cpp │ └── RtsiRecipeInternal.cpp └── resources │ └── external_control.script └── test ├── CMakeLists.txt ├── ControllerLogTest.cpp ├── DashboardClientTest.cpp ├── PrimaryPortTest.cpp ├── RemoteUpgradeTest.cpp ├── ReverseInterfaceTest.cpp ├── RtsiIOTest.cpp ├── ScriptCommnadInterfaceTest.cpp ├── ScriptSenderTest.cpp ├── SshUtilsTest.cpp ├── TcpServerTest.cpp └── TrajectoryInterfaceTest.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Google 4 | AccessModifierOffset: -1 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlinesLeft: true 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: All 15 | AllowShortIfStatementsOnASingleLine: true 16 | AllowShortLoopsOnASingleLine: true 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: true 20 | AlwaysBreakTemplateDeclarations: true 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | BraceWrapping: 24 | AfterClass: false 25 | AfterControlStatement: false 26 | AfterEnum: false 27 | AfterFunction: false 28 | AfterNamespace: false 29 | AfterObjCDeclaration: false 30 | AfterStruct: false 31 | AfterUnion: false 32 | BeforeCatch: false 33 | BeforeElse: false 34 | IndentBraces: false 35 | BreakBeforeBinaryOperators: None 36 | BreakBeforeBraces: Attach 37 | BreakBeforeTernaryOperators: true 38 | BreakConstructorInitializersBeforeComma: false 39 | ColumnLimit: 132 40 | CommentPragmas: '^ IWYU pragma:' 41 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 42 | ConstructorInitializerIndentWidth: 4 43 | ContinuationIndentWidth: 4 44 | Cpp11BracedListStyle: true 45 | DerivePointerAlignment: true 46 | DisableFormat: false 47 | ExperimentalAutoDetectBinPacking: false 48 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 49 | IncludeCategories: 50 | - Regex: '^<.*\.h>' 51 | Priority: 1 52 | - Regex: '^<.*' 53 | Priority: 2 54 | - Regex: '.*' 55 | Priority: 3 56 | IndentCaseLabels: true 57 | IndentWidth: 4 58 | IndentWrappedFunctionNames: false 59 | KeepEmptyLinesAtTheStartOfBlocks: false 60 | MacroBlockBegin: '' 61 | MacroBlockEnd: '' 62 | MaxEmptyLinesToKeep: 1 63 | NamespaceIndentation: None 64 | ObjCBlockIndentWidth: 2 65 | ObjCSpaceAfterProperty: false 66 | ObjCSpaceBeforeProtocolList: false 67 | PenaltyBreakBeforeFirstCallParameter: 1 68 | PenaltyBreakComment: 300 69 | PenaltyBreakFirstLessLess: 120 70 | PenaltyBreakString: 1000 71 | PenaltyExcessCharacter: 1000000 72 | PenaltyReturnTypeOnItsOwnLine: 200 73 | PointerAlignment: Left 74 | ReflowComments: true 75 | SortIncludes: true 76 | SpaceAfterCStyleCast: false 77 | SpaceBeforeAssignmentOperators: true 78 | SpaceBeforeParens: ControlStatements 79 | SpaceInEmptyParentheses: false 80 | SpacesBeforeTrailingComments: 2 81 | SpacesInAngles: false 82 | SpacesInContainerLiterals: true 83 | SpacesInCStyleCastParentheses: false 84 | SpacesInParentheses: false 85 | SpacesInSquareBrackets: false 86 | Standard: Auto 87 | TabWidth: 8 88 | UseTab: Never 89 | ... 90 | 91 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .vscode/ 3 | boost/ 4 | .vs/ 5 | out/ 6 | CMakeSettings.json 7 | debian/.debhelper 8 | debian/files 9 | debian/debhelper-build-stamp 10 | debian/tmp 11 | debian/elite-cs-series-sdk/ 12 | debian/*.log 13 | debian/*.substvars -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2024] [ELITE ROBOTS] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.cn.md: -------------------------------------------------------------------------------- 1 | [English](./README.md) 2 | # Elite Robots CS SDK 3 | 4 | Elite CS 系列机器人的SDK。 5 | 6 | ## Requirements 7 | * **CS Controller* (机器人的控制软件) version >=**2.14.x**(for CS-Series). 如果机器人的控制软件版本低于此,建议升级之。 8 | 9 | ## Build & Install 10 | 如果你的操作系统是 Ubuntu20.04、Ubuntu22.04、Ubuntu24.04,那么可以用下面的指令直接安装elite-cs-series-sdk: 11 | ```bash 12 | sudo add-apt-repository ppa:elite-robots/cs-robot-series-sdk 13 | sudo apt update 14 | sudo apt install elite-cs-series-sdk 15 | ``` 16 | 17 | 如果需要编译安装,参考[编译向导](./doc/BuildGuide/BuildGuide.cn.md) 18 | 19 | ## User guide 20 | [中文向导](./doc/UserGuide/cn/UserGuide.cn.md) 21 | 22 | ## Architecture 23 | [代码架构](./doc/Architecture/Arch.cn.md) 24 | 25 | ## API 说明 26 | [API](./doc/API/cn/API.cn.md) 27 | 28 | ## Compatible Operating Systems 29 | 在以下系统平台上测试过: 30 | 31 | * Ubuntu 22.04 (Jammy Jellyfish) 32 | * Ubuntu 16.04 (Xenial Xerus) 33 | * Windowns 11 34 | 35 | ## Compiler 36 | 目前在以下编译器中通过编译 37 | 38 | * gcc 11.4.0 39 | * gcc 5.5.0 40 | * msvc 19.40.33808 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [中文](./README.cn.md) 2 | # Elite Robots CS SDK 3 | 4 | The SDK for Elite CS series robots. 5 | 6 | ## Requirements 7 | * **CS Controller** (the control software for robots) version >= **2.13.x** (for CS-Series). If the version of the robot's control software is lower than this, it is recommended to upgrade it. 8 | * The socket in the SDK uses **boost::asio**. Therefore, the **boost** library needs to be installed. 9 | * This SDK requires a compiler that supports C++17 or C++14. Note that if the C++14 standard is used, **boost::variant** will be used. 10 | * cmake version >= 3.22.1 11 | 12 | ## Build & Install 13 | If your system is Ubuntu20.04, Ubuntu22.04 or Ubuntu24.04, you can run the following command to install elite-cs-series-sdk: 14 | ```bash 15 | sudo add-apt-repository ppa:elite-robots/cs-robot-series-sdk 16 | sudo apt update 17 | sudo apt install elite-cs-series-sdk 18 | ``` 19 | 20 | If compilation and installation are required, please refer to the [Compilation Guide](./doc/BuildGuide/BuildGuide.en.md). 21 | 22 | ## User guide 23 | [English guide](./doc/UserGuide/en/UserGuide.en.md) 24 | 25 | ## Architecture 26 | [Code architecture](./doc/Architecture/Arch.en.md) 27 | 28 | ## API document 29 | [API](./doc/API/en/API.en.md) 30 | 31 | ## Compatible Operating Systems 32 | Tested on the following system platforms: 33 | 34 | * Ubuntu 22.04 (Jammy Jellyfish) 35 | * Ubuntu 16.04 (Xenial Xerus) 36 | * Windows 11 37 | 38 | ## Compiler 39 | Currently compiled with the following compilers: 40 | 41 | * gcc 11.4.0 42 | * gcc 5.5.0 43 | * msvc 19.40.33808 -------------------------------------------------------------------------------- /cmake/utils.cmake: -------------------------------------------------------------------------------- 1 | # Define the source file relative path macro 2 | function(define_rel_file_macro targetname) 3 | # Get current target all source files 4 | get_target_property(source_files "${targetname}" SOURCES) 5 | foreach(sourcefile ${source_files}) 6 | # Gets the compilation parameters of the current source file 7 | get_property(defs SOURCE "${sourcefile}" 8 | PROPERTY COMPILE_DEFINITIONS) 9 | # Gets the absolute path to the current file 10 | get_filename_component(filepath "${sourcefile}" ABSOLUTE) 11 | # Replace the project path in the absolute path with empty to get the relative path of the source file with respect to the project path 12 | string(REPLACE ${PROJECT_SOURCE_DIR}/ "" relpath ${filepath}) 13 | # Add the compile parameter we want to add (__REL_FILE__ definition) to the original compile parameter 14 | list(APPEND defs "__REL_FILE__=\"${relpath}\"") 15 | # Reset the compilation parameters of the source file 16 | set_property( 17 | SOURCE "${sourcefile}" 18 | PROPERTY COMPILE_DEFINITIONS ${defs} 19 | ) 20 | endforeach() 21 | endfunction() -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | elite-cs-series-sdk (1.1.0-focal-1) focal; urgency=medium 2 | * Version 1.1.0 release on focal. 3 | 4 | -- Elite Robots Fri, 20 Dec 2024 14:29:14 +0800 5 | 6 | elite-cs-series-sdk (1.1.0-2) noble; urgency=medium 7 | * Version 1.1.0 release on noble. 8 | 9 | -- Elite Robots Fri, 20 Dec 2024 14:29:14 +0800 10 | 11 | elite-cs-series-sdk (1.1.0-1) jammy; urgency=medium 12 | * Version 1.1.0 release on jammy. 13 | 14 | -- Elite Robots Fri, 20 Dec 2024 14:29:14 +0800 15 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: elite-cs-series-sdk 2 | Section: libs 3 | Priority: optional 4 | Maintainer: Elite Robots 5 | Build-Depends: 6 | debhelper-compat (= 9), 7 | cmake(>= 3.16), 8 | libboost-all-dev(>=1.58) 9 | Standards-Version: 4.6.0 10 | Homepage: https://www.eliterobots.com/ 11 | Vcs-Browser: https://github.com/Elite-Robots/Elite_Robots_CS_SDK 12 | Vcs-Git: https://github.com/Elite-Robots/Elite_Robots_CS_SDK.git 13 | Rules-Requires-Root: no 14 | 15 | Package: elite-cs-series-sdk 16 | Architecture: amd64 17 | Depends: 18 | ${shlibs:Depends}, 19 | ${misc:Depends} 20 | Description: Elite Robots CS Series SDK. 21 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2024] [ELITE ROBOTS] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # See debhelper(7) (uncomment to enable) 3 | # output every command that modifies files on the build system. 4 | export DH_VERBOSE = 1 5 | 6 | 7 | # see FEATURE AREAS in dpkg-buildflags(1) 8 | export DEB_BUILD_MAINT_OPTIONS = hardening=+all 9 | 10 | # see ENVIRONMENT in dpkg-buildflags(1) 11 | # package maintainers to append CFLAGS 12 | #export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic 13 | # package maintainers to append LDFLAGS 14 | #export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed 15 | 16 | 17 | %: 18 | dh $@ --buildsystem=cmake --builddirectory=build 19 | 20 | 21 | # dh_make generated override targets 22 | # This is example for Cmake (See https://bugs.debian.org/641051 ) 23 | override_dh_auto_configure: 24 | dh_auto_configure -- \ 25 | -DCMAKE_CXX_STANDARD=17 26 | 27 | override_dh_clean: 28 | dh_clean 29 | 30 | override_dh_shlibdeps: 31 | LD_LIBRARY_PATH=$(DESTDIR)/usr/lib/$(DEB_TARGET_MULTIARCH):$$LD_LIBRARY_PATH dh_shlibdeps -l$(DESTDIR)/usr/lib/$(DEB_TARGET_MULTIARCH) -a -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /doc/API/cn/API.cn.md: -------------------------------------------------------------------------------- 1 | # ELITE_ROBOT_CS_SDK API 参考 2 | 3 | 本文档旨在说明SDK中的API功能。 4 | 5 | 适用SDK版本:***v1.2.0*** 6 | 7 | - [EliteDriver](./EliteDriver.cn.md) 8 | 9 | - [PrimaryPort](./PrimaryPort.cn.md) 10 | 11 | - [RTSI](./RTSI.cn.md) 12 | 13 | - [Dashboard](./Dashboard.cn.md) 14 | 15 | - [版本信息](./VersionInfo.cn.md) 16 | 17 | - [Log](./Log.cn.md) 18 | 19 | - [远程升级](./RemoteUpgrade.cn.md) 20 | 21 | - [控制器日志](./ControllerLog.cn.md) -------------------------------------------------------------------------------- /doc/API/cn/ControllerLog.cn.md: -------------------------------------------------------------------------------- 1 | # ControllerLog 类 2 | 3 | ## 简介 4 | 5 | ControllerLog 类提供了从机器人控制器下载系统日志的功能。 6 | 7 | ## 头文件 8 | ```cpp 9 | #include 10 | ``` 11 | 12 | ## 接口说明 13 | 14 | ### 下载系统日志 15 | ```cpp 16 | static bool downloadSystemLog(const std::string& robot_ip, 17 | const std::string& password, 18 | const std::string& path, 19 | std::function progress_cb) 20 | ``` 21 | - ***功能*** 22 | 23 | 从机器人控制器下载系统日志到本地路径 24 | 25 | - ***参数*** 26 | 27 | - `robot_ip` : 机器人IP地址。 28 | - `password` : 机器人SSH密码。 29 | - `path` : 日志文件保存路径。 30 | - `progress_cb` : 下载进度回调函数。 31 | 32 | - ***回调函数参数*** 33 | 34 | - `f_z`:文件总大小(字节)。 35 | - `r_z`:已下载大小(字节)。 36 | - `err`:错误信息(无错误时为nullptr)。 37 | 38 | - ***返回值*** 39 | 40 | - `true`: 下载成功 41 | - `false`: 下载失败 42 | 43 | - ***注意事项*** 44 | 45 | 1. 在Linux系统下,如果未安装`libssh`,需要确保运行SDK的计算机具有`scp`、`ssh`和`sshpass`命令可用 46 | 2. 在Windows系统下,如果未安装libssh,则此接口不可用 47 | -------------------------------------------------------------------------------- /doc/API/cn/Log.cn.md: -------------------------------------------------------------------------------- 1 | # Log 模块 2 | 3 | ## 简介 4 | 5 | Log模块提供了Elite_Robots_CS_SDK中的日志功能相关设置,包括日志级别定义、日志处理器接口和日志输出功能。 6 | 7 | ## 头文件 8 | ```cpp 9 | #include 10 | ``` 11 | 12 | ## 日志级别枚举 13 | 14 | ```cpp 15 | enum class LogLevel { 16 | ELI_DEBUG, 17 | ELI_INFO, 18 | ELI_WARN, 19 | ELI_ERROR, 20 | ELI_FATAL, 21 | ELI_NONE 22 | }; 23 | ``` 24 | - ***描述*** 25 | 26 | 定义了日志的级别,从低到高依次为: 27 | - `ELI_DEBUG`: 调试信息 28 | - `ELI_INFO`: 普通信息 29 | - `ELI_WARN`: 警告信息 30 | - `ELI_ERROR`: 错误信息 31 | - `ELI_FATAL`: 严重错误信息 32 | - `ELI_NONE`: 不输出任何日志 33 | 34 | ## 日志宏定义 35 | 36 | ### 调试日志 37 | ```cpp 38 | #define ELITE_LOG_DEBUG(...) 39 | ``` 40 | - ***功能*** 41 | 42 | 输出调试级别日志 43 | 44 | ### 信息日志 45 | ```cpp 46 | #define ELITE_LOG_INFO(...) 47 | ``` 48 | - ***功能*** 49 | 50 | 输出信息级别日志 51 | 52 | ### 警告日志 53 | ```cpp 54 | #define ELITE_LOG_WARN(...) 55 | ``` 56 | - ***功能*** 57 | 58 | 输出警告级别日志 59 | 60 | ### 错误日志 61 | ```cpp 62 | #define ELITE_LOG_ERROR(...) 63 | ``` 64 | - ***功能*** 65 | 66 | 输出错误级别日志 67 | 68 | ### 严重错误日志 69 | ```cpp 70 | #define ELITE_LOG_FATAL(...) 71 | ``` 72 | - ***功能*** 73 | 74 | 输出严重错误级别日志 75 | 76 | ## LogHandler 类 77 | 78 | ### 简介 79 | ```cpp 80 | class LogHandler 81 | ``` 82 | - ***描述*** 83 | 84 | 日志处理器抽象基类,可通过继承此类并实现`log()`方法来自定义日志处理方式。 85 | 86 | ### 构造函数 87 | ```cpp 88 | LogHandler() = default; 89 | ``` 90 | - ***功能*** 91 | 92 | 默认构造函数 93 | 94 | ### 析构函数 95 | ```cpp 96 | virtual ~LogHandler() = default; 97 | ``` 98 | - ***功能*** 99 | 100 | 虚析构函数 101 | 102 | ### 日志处理函数 103 | ```cpp 104 | virtual void log(const char* file, int line, LogLevel loglevel, const char* log) = 0; 105 | ``` 106 | - ***功能*** 107 | 108 | 纯虚函数,用于处理日志消息 109 | 110 | - ***参数*** 111 | - `file`: 日志来源文件 112 | - `line`: 日志来源行号 113 | - `loglevel`: 日志级别 114 | - `log`: 日志消息内容 115 | 116 | ## 全局函数 117 | 118 | ### 注册日志处理器 119 | ```cpp 120 | void registerLogHandler(std::unique_ptr hanlder); 121 | ``` 122 | - ***功能*** 123 | 124 | 注册自定义的日志处理器 125 | 126 | - ***参数*** 127 | - `hanlder`: 指向新日志处理器对象的unique_ptr 128 | 129 | ### 注销日志处理器 130 | ```cpp 131 | void unregisterLogHandler(); 132 | ``` 133 | - ***功能*** 134 | 135 | 注销当前日志处理器,将启用默认日志处理器 136 | 137 | ### 设置日志级别 138 | ```cpp 139 | void setLogLevel(LogLevel level); 140 | ``` 141 | - ***功能*** 142 | 143 | 设置日志级别,低于此级别的日志将不会被输出 144 | 145 | - ***参数*** 146 | - `level`: 要设置的日志级别 147 | 148 | ### 日志输出函数 149 | ```cpp 150 | void log(const char* file, int line, LogLevel level, const char* fmt, ...); 151 | ``` 152 | - ***功能*** 153 | 154 | 内部使用的日志输出函数,建议使用宏而非直接调用此函数 155 | 156 | - ***参数*** 157 | - `file`: 日志来源文件 158 | - `line`: 日志来源行号 159 | - `level`: 日志级别 160 | - `fmt`: 格式化字符串 161 | - `...`: 可变参数,用于格式化字符串 162 | 163 | ## 使用示例 164 | 165 | ```cpp 166 | // 自定义日志处理器 167 | class MyLogHandler : public ELITE::LogHandler { 168 | public: 169 | void log(const char* file, int line, ELITE::LogLevel loglevel, const char* log) override { 170 | // 自定义日志处理逻辑 171 | } 172 | }; 173 | 174 | // 注册自定义日志处理器 175 | ELITE::registerLogHandler(std::make_unique()); 176 | 177 | // 设置日志级别 178 | ELITE::setLogLevel(ELITE::LogLevel::ELI_INFO); 179 | 180 | // 使用日志宏 181 | ELITE_LOG_INFO("This is an info message"); 182 | ELITE_LOG_ERROR("Error code: %d", 404); 183 | ``` -------------------------------------------------------------------------------- /doc/API/cn/PrimaryPort.cn.md: -------------------------------------------------------------------------------- 1 | # Primary Port 2 | 3 | ## 简介 4 | 5 | SDK中提供了与机器人30001端口连接、发送脚本的接口,以及解析30001端口数据的框架。SDK中只有部分数据包的解析,如果要解析其他数据包,需要手动编写解析代码。 6 | 7 | # PrimaryPortInterface 类 8 | 9 | ## 简介 10 | 11 | 此接口提供了与机器人primary port接口通讯的方法。 12 | 13 | ## PrimaryPortInterface 头文件 14 | ```cpp 15 | #include 16 | ``` 17 | 18 | ## PrimaryPortInterface 类的构造函数 19 | 20 | ### ***构造函数*** 21 | ```cpp 22 | PrimaryPortInterface::PrimaryPortInterface() 23 | ``` 24 | - ***功能*** 25 | 26 | 初始化数据。注意,此构造函数不会连接机器人。 27 | 28 | --- 29 | 30 | ## 通讯 31 | 32 | ### ***连接*** 33 | ```cpp 34 | bool connect(const std::string& ip, int port = PRIMARY_PORT) 35 | ``` 36 | 37 | - ***功能*** 38 | 39 | 连接到机器人的30001端口(默认)。 40 | 41 | - ***参数*** 42 | - ip:机器人端口 43 | 44 | - timeout_ms:设置机器人读取下一条指令的超时时间,小于等于0时会无限等待。 45 | 46 | - ***返回值***:成功返回 true,失败返回 false。 47 | 48 | - ***注意*** 49 | 1. 如果重复调用此函数而没有调用disconnect,那么会断开原来的连接。 50 | 2. 重复调用此函数的频率最好不要超过2HZ。 51 | 52 | --- 53 | 54 | ### ***断开连接*** 55 | ```cpp 56 | void disconnect() 57 | ``` 58 | - ***功能*** 59 | 60 | 断开与机器人的连接 61 | 62 | - ***注意*** 63 | 64 | 建议在调用此函数之后进行500ms左右延迟再调用connect。 65 | 66 | --- 67 | 68 | ### ***发送脚本*** 69 | ```cpp 70 | bool sendScript(const std::string& script) 71 | ``` 72 | - ***功能*** 73 | 74 | 向机器人发送可执行脚本 75 | 76 | - ***参数*** 77 | - script:待发送的脚本。 78 | 79 | - ***返回值***:发送成功返回 true,失败返回 false。 80 | 81 | --- 82 | 83 | ### 获取数据包 84 | ```cpp 85 | bool getPackage(std::shared_ptr pkg, int timeout_ms) 86 | ``` 87 | - ***功能*** 88 | 89 | 获取机器人数据包并解析 90 | 91 | - ***参数*** 92 | - pkg:待获取的数据包 93 | 94 | - timeout_ms:等待超时时间。 95 | 96 | - ***返回值***:获取成功返回 true,失败返回 false。 97 | 98 | --- 99 | 100 | # PrimaryPackage 类 101 | 102 | ## 简介 103 | 104 | 此类主要用于被继承,以此来获取Primary端口数据。 105 | 106 | SDK中没有提供全部的数据包解析,如果需要其他的数据包,可以参考`RobotConfPackage.hpp`中`KinematicsInfo`的实现方式来编写自己需要的数据包解析。报文的格式参考Elite官方文档“CS_用户手册_机器人状态报文.xlsx”。 107 | 108 | ## PrimaryPackage 头文件 109 | ```cpp 110 | #include 111 | ``` 112 | 113 | ## PrimaryPackage 类的构造函数 114 | ### ***构造函数*** 115 | ```cpp 116 | PrimaryPackage::PrimaryPackage(int type) 117 | ``` 118 | - ***功能*** 119 | 120 | 初始化数据 121 | 122 | - ***参数*** 123 | - type:数据包类型(参考Elite官方文档:CS_用户手册_机器人状态报文.xlsx) 124 | 125 | --- 126 | 127 | ## 纯虚函数 128 | 129 | ### 解析报文 130 | ```cpp 131 | void parser(int len, const std::vector::const_iterator& iter) 132 | ``` 133 | - ***功能*** 134 | 135 | 由子类来完成具体实现,解析Primary端口机器人状态报文的子报文。当子类实例作为参数传入`PrimaryPortInterface::getPackage()`中会被调用。 136 | 137 | - ***参数*** 138 | - len:子报文的长度。 139 | 140 | - iter:子报文在整个报文中的位置。 141 | 142 | --- 143 | 144 | ## 其余 145 | 146 | ### ***获取报文类型*** 147 | ```cpp 148 | int getType() 149 | ``` 150 | - ***功能*** 151 | 152 | 获取数据包的类型。 153 | 154 | - ***返回值***:数据包类型。 155 | 156 | --- 157 | 158 | ## SDK内部使用 159 | ### 等待更新 160 | ```cpp 161 | bool waitUpdate(int timeout_ms) 162 | ``` 163 | - ***功能*** 164 | 165 | 等待数据包数据更新。在getPackage()函数中调用。 166 | 167 | - ***参数*** 168 | - timeout_ms:超时时间 169 | 170 | - ***返回值***:未超时返回 true,超时返回 false。 171 | 172 | --- 173 | 174 | # KinematicsInfo 类 175 | 176 | ## 简介 177 | 178 | 机器人配置数据中运动学的数据包解析。PrimaryPackage 是此接口的父类,除了 PrimaryPackage 类中的方法以外,此接口主要内容是机器人的DH参数。 179 | 180 | ## KinematicsInfo 头文件 181 | 182 | ```cpp 183 | #include 184 | ``` 185 | 186 | ## DH参数 187 | 188 | - `vector6d_t dh_a_` 189 | 190 | - `vector6d_t dh_d_` 191 | 192 | - `vector6d_t dh_alpha_` 193 | -------------------------------------------------------------------------------- /doc/API/cn/RemoteUpgrade.cn.md: -------------------------------------------------------------------------------- 1 | # RemoteUpgrade 模块 2 | 3 | ## 简介 4 | 5 | RemoteUpgrade模块提供了机器人控制软件的远程升级功能。 6 | 7 | ## 头文件 8 | ```cpp 9 | #include 10 | ``` 11 | 12 | ## 升级控制软件 13 | 14 | ```cpp 15 | bool upgradeControlSoftware(std::string ip, std::string file, std::string password) 16 | ``` 17 | - ***功能*** 18 | 19 | 升级机器人控制软件 20 | 21 | - ***参数*** 22 | 23 | - `ip`: 机器人IP地址 24 | - `file`: 升级文件路径 25 | - `password`: 机器人控制器SSH密码 26 | 27 | - ***返回值*** 28 | 29 | - `true`: 升级成功 30 | - `false`: 升级失败 31 | 32 | - ***注意事项*** 33 | 34 | 1. 在Linux系统下,如果未安装`libssh`,需要确保运行SDK的计算机具有`scp`、`ssh`和`sshpass`命令可用 35 | 2. 在Windows系统下,如果未安装libssh,则此接口不可用 -------------------------------------------------------------------------------- /doc/API/cn/VersionInfo.cn.md: -------------------------------------------------------------------------------- 1 | # VersionInfo 类 2 | 3 | ## 简介 4 | 5 | VersionInfo类用于包含和管理版本信息,提供了版本号的存储、比较和转换功能。 6 | 7 | ## 头文件 8 | ```cpp 9 | #include 10 | ``` 11 | 12 | ## 接口 13 | 14 | ### 构造函数 15 | ```cpp 16 | constexpr VersionInfo(int ma, int mi, int bug, int bui) 17 | ``` 18 | - ***功能*** 19 | 20 | 使用指定的版本号组件创建VersionInfo对象 21 | 22 | - ***参数*** 23 | 24 | - ma:主版本号 25 | - mi:次版本号 26 | - bug:补丁号 27 | - bui:构建号 28 | 29 | --- 30 | 31 | ### 字符串构造函数 32 | ```cpp 33 | explicit VersionInfo(const std::string& version) 34 | ``` 35 | - ***功能*** 36 | 37 | 从版本字符串创建VersionInfo对象 38 | 39 | - ***参数*** 40 | 41 | - version:格式为"major.minor.bugfix.build"的版本字符串 42 | 43 | --- 44 | 45 | ### 默认构造函数 46 | ```cpp 47 | VersionInfo() 48 | ``` 49 | - ***功能*** 50 | 51 | 创建默认的VersionInfo对象(所有版本号组件初始化为0) 52 | 53 | --- 54 | 55 | ### 转换为字符串 56 | ```cpp 57 | std::string toString() const 58 | ``` 59 | - ***功能*** 60 | 61 | 将版本信息转换为字符串 62 | 63 | - ***返回值***:格式为"major.minor.bugfix.build"的字符串 64 | 65 | --- 66 | 67 | ### 从字符串解析 68 | ```cpp 69 | static VersionInfo fromString(const std::string& str) 70 | ``` 71 | - ***功能*** 72 | 73 | 从字符串解析版本信息 74 | 75 | - ***参数*** 76 | 77 | - str:要解析的版本字符串 78 | 79 | - ***返回值***:解析得到的VersionInfo对象 80 | 81 | --- 82 | 83 | ## 运算符重载 84 | 85 | ### 赋值运算符 86 | ```cpp 87 | VersionInfo& operator=(const VersionInfo&) 88 | ``` 89 | - ***功能*** 90 | 91 | 版本信息赋值操作 92 | 93 | --- 94 | 95 | ### 相等比较运算符 96 | ```cpp 97 | bool operator==(const VersionInfo& v) const 98 | ``` 99 | - ***功能*** 100 | 101 | 比较两个版本是否相等 102 | 103 | - ***参数*** 104 | 105 | - v:要比较的版本 106 | 107 | - ***返回值***:相等返回true,否则返回false 108 | 109 | --- 110 | 111 | ### 不等比较运算符 112 | ```cpp 113 | bool operator!=(const VersionInfo& v) const 114 | ``` 115 | - ***功能*** 116 | 117 | 比较两个版本是否不等 118 | 119 | - ***参数*** 120 | 121 | - v:要比较的版本 122 | 123 | - ***返回值***:不等返回true,否则返回false 124 | 125 | --- 126 | 127 | ### 大于比较运算符 128 | ```cpp 129 | bool operator>(const VersionInfo& v) const 130 | ``` 131 | - ***功能*** 132 | 133 | 比较当前版本是否大于指定版本 134 | 135 | - ***参数*** 136 | 137 | - v:要比较的版本 138 | 139 | - ***返回值***:大于返回true,否则返回false 140 | 141 | --- 142 | 143 | ### 大于等于比较运算符 144 | ```cpp 145 | bool operator>=(const VersionInfo& v) const 146 | ``` 147 | - ***功能*** 148 | 149 | 比较当前版本是否大于等于指定版本 150 | 151 | - ***参数*** 152 | 153 | - v:要比较的版本 154 | 155 | - ***返回值***:大于等于返回true,否则返回false 156 | 157 | --- 158 | 159 | ### 小于比较运算符 160 | ```cpp 161 | bool operator<(const VersionInfo& v) const 162 | ``` 163 | - ***功能*** 164 | 165 | 比较当前版本是否小于指定版本 166 | 167 | - ***参数*** 168 | 169 | - v:要比较的版本 170 | 171 | - ***返回值***:小于返回true,否则返回false 172 | 173 | --- 174 | 175 | ### 小于等于比较运算符 176 | ```cpp 177 | bool operator<=(const VersionInfo& v) const 178 | ``` 179 | - ***功能*** 180 | 181 | 比较当前版本是否小于等于指定版本 182 | 183 | - ***参数*** 184 | 185 | - v:要比较的版本 186 | 187 | - ***返回值***:小于等于返回true,否则返回false 188 | 189 | --- 190 | 191 | ### 常量表达式相等比较 192 | ```cpp 193 | constexpr bool operator==(VersionInfo& v) const 194 | ``` 195 | - ***功能*** 196 | 197 | 常量表达式版本的相等比较 198 | 199 | - ***参数*** 200 | 201 | - v:要比较的版本 202 | 203 | - ***返回值***:相等返回true,否则返回false 204 | 205 | --- 206 | 207 | ### 常量表达式不等比较 208 | ```cpp 209 | constexpr bool operator!=(VersionInfo& v) const 210 | ``` 211 | - ***功能*** 212 | 213 | 常量表达式版本的不等比较 214 | 215 | - ***参数*** 216 | 217 | - v:要比较的版本 218 | 219 | - ***返回值***:不等返回true,否则返回false 220 | 221 | --- 222 | 223 | ### 常量表达式大于比较 224 | ```cpp 225 | constexpr bool operator>(VersionInfo& v) const 226 | ``` 227 | - ***功能*** 228 | 229 | 常量表达式版本的大于比较 230 | 231 | - ***参数*** 232 | 233 | - v:要比较的版本 234 | 235 | - ***返回值***:大于返回true,否则返回false 236 | 237 | --- 238 | 239 | ### 常量表达式大于等于比较 240 | ```cpp 241 | constexpr bool operator>=(VersionInfo& v) const 242 | ``` 243 | - ***功能*** 244 | 245 | 常量表达式版本的大于等于比较 246 | 247 | - ***参数*** 248 | 249 | - v:要比较的版本 250 | 251 | - ***返回值***:大于等于返回true,否则返回false 252 | 253 | --- 254 | 255 | ### 常量表达式小于比较 256 | ```cpp 257 | constexpr bool operator<(VersionInfo& v) const 258 | ``` 259 | - ***功能*** 260 | 261 | 常量表达式版本的小于比较 262 | 263 | - ***参数*** 264 | 265 | - v:要比较的版本 266 | 267 | - ***返回值***:小于返回true,否则返回false 268 | 269 | --- 270 | 271 | ### 常量表达式小于等于比较 272 | ```cpp 273 | constexpr bool operator<=(VersionInfo& v) const 274 | ``` 275 | - ***功能*** 276 | 277 | 常量表达式版本的小于等于比较 278 | 279 | - ***参数*** 280 | 281 | - v:要比较的版本 282 | 283 | - ***返回值***:小于等于返回true,否则返回false 284 | 285 | --- 286 | 287 | ## 成员变量 288 | 289 | ### 主版本号 290 | ```cpp 291 | uint32_t major = 0 292 | ``` 293 | 294 | ### 次版本号 295 | ```cpp 296 | uint32_t minor = 0 297 | ``` 298 | 299 | ### 补丁号 300 | ```cpp 301 | uint32_t bugfix = 0 302 | ``` 303 | 304 | ### 构建号 305 | ```cpp 306 | uint32_t build = 0 307 | ``` 308 | 309 | ## 全局常量 310 | 311 | ### SDK版本信息 312 | ```cpp 313 | constexpr VersionInfo SDK_VERSION_INFO(ELITE_SDK_VERSION_MAJOR, ELITE_SDK_VERSION_MINOR, ELITE_SDK_VERSION_BUGFIX, 0) 314 | ``` 315 | - ***功能*** 316 | 317 | 表示SDK版本信息的常量 318 | 319 | --- 320 | -------------------------------------------------------------------------------- /doc/API/en/API.en.md: -------------------------------------------------------------------------------- 1 | # ELITE_ROBOT_CS_SDK API Reference 2 | 3 | This document is intended to explain the API features in the SDK. 4 | 5 | Applicable SDK version: ***v1.2.0*** 6 | 7 | - [EliteDriver](./EliteDriver.en.md) 8 | 9 | - [PrimaryPort](./PrimaryPort.en.md) 10 | 11 | - [RTSI](./RTSI.en.md) 12 | 13 | - [Dashboard](./Dashboard.en.md) 14 | 15 | - [Version info](./VersionInfo.cn.md) 16 | 17 | - [Log](./Log.en.md) 18 | 19 | - [Remote upgrade](./RemoteUpgrade.en.md) 20 | 21 | - [Controller log](./ControllerLog.en.md) -------------------------------------------------------------------------------- /doc/API/en/ControllerLog.en.md: -------------------------------------------------------------------------------- 1 | # ControllerLog Class 2 | 3 | ## Introduction 4 | The ControllerLog class provides the function of downloading system logs from the robot controller. 5 | 6 | ## Header File 7 | ```cpp 8 | #include 9 | ``` 10 | 11 | ## Interface Description 12 | 13 | ### Download System Log 14 | ```cpp 15 | static bool downloadSystemLog(const std::string& robot_ip, 16 | const std::string& password, 17 | const std::string& path, 18 | std::function progress_cb) 19 | ``` 20 | - ***Function*** 21 | Downloads the system log from the robot controller to the local path. 22 | - ***Parameters*** 23 | - `robot_ip`: The IP address of the robot. 24 | - `password`: The SSH password of the robot. 25 | - `path`: The saving path of the log file. 26 | - `progress_cb`: The callback function for the download progress. 27 | - ***Parameters of the Callback Function*** 28 | - `f_z`: The total size of the file (in bytes). 29 | - `r_z`: The size that has been downloaded (in bytes). 30 | - `err`: The error information (nullptr when there is no error). 31 | - ***Return Value*** 32 | - `true`: The download is successful. 33 | - `false`: The download fails. 34 | - ***Notes*** 35 | 1. Under the Linux system, if `libssh` is not installed, it is necessary to ensure that the computer running the SDK has the `scp`, `ssh`, and `sshpass` commands available. 36 | 2. Under the Windows system, if `libssh` is not installed, this interface is not available. -------------------------------------------------------------------------------- /doc/API/en/Log.en.md: -------------------------------------------------------------------------------- 1 | # Log Module 2 | 3 | ## Introduction 4 | The Log module provides settings related to the logging functionality in the Elite_Robots_CS_SDK, including log level definitions, log handler interfaces, and log output functions. 5 | 6 | ## Header File 7 | ```cpp 8 | #include 9 | ``` 10 | 11 | ## Log Level Enumeration 12 | 13 | ```cpp 14 | enum class LogLevel { 15 | ELI_DEBUG, 16 | ELI_INFO, 17 | ELI_WARN, 18 | ELI_ERROR, 19 | ELI_FATAL, 20 | ELI_NONE 21 | }; 22 | ``` 23 | - ***Description*** 24 | Defines the log levels, from lowest to highest: 25 | - `ELI_DEBUG`: Debug information 26 | - `ELI_INFO`: General information 27 | - `ELI_WARN`: Warning information 28 | - `ELI_ERROR`: Error information 29 | - `ELI_FATAL`: Severe error information 30 | - `ELI_NONE`: Do not output any logs 31 | 32 | ## Log Macro Definitions 33 | 34 | ### Debug Log 35 | ```cpp 36 | #define ELITE_LOG_DEBUG(...) 37 | ``` 38 | - ***Function*** 39 | Outputs debug-level logs. 40 | 41 | ### Information Log 42 | ```cpp 43 | #define ELITE_LOG_INFO(...) 44 | ``` 45 | - ***Function*** 46 | Outputs information-level logs. 47 | 48 | ### Warning Log 49 | ```cpp 50 | #define ELITE_LOG_WARN(...) 51 | ``` 52 | - ***Function*** 53 | Outputs warning-level logs. 54 | 55 | ### Error Log 56 | ```cpp 57 | #define ELITE_LOG_ERROR(...) 58 | ``` 59 | - ***Function*** 60 | Outputs error-level logs. 61 | 62 | ### Severe Error Log 63 | ```cpp 64 | #define ELITE_LOG_FATAL(...) 65 | ``` 66 | - ***Function*** 67 | Outputs severe error-level logs. 68 | 69 | ## LogHandler Class 70 | 71 | ### Introduction 72 | ```cpp 73 | class LogHandler 74 | ``` 75 | - ***Description*** 76 | An abstract base class for log handlers. You can inherit from this class and implement the `log()` method to customize the log handling method. 77 | 78 | ### Constructor 79 | ```cpp 80 | LogHandler() = default; 81 | ``` 82 | - ***Function*** 83 | Default constructor. 84 | 85 | ### Destructor 86 | ```cpp 87 | virtual ~LogHandler() = default; 88 | ``` 89 | - ***Function*** 90 | Virtual destructor. 91 | 92 | ### Log Handling Function 93 | ```cpp 94 | virtual void log(const char* file, int line, LogLevel loglevel, const char* log) = 0; 95 | ``` 96 | - ***Function*** 97 | A pure virtual function used to handle log messages. 98 | - ***Parameters*** 99 | - `file`: The file where the log originated. 100 | - `line`: The line number where the log originated. 101 | - `loglevel`: The log level. 102 | - `log`: The content of the log message. 103 | 104 | ## Global Functions 105 | 106 | ### Register Log Handler 107 | ```cpp 108 | void registerLogHandler(std::unique_ptr hanlder); 109 | ``` 110 | - ***Function*** 111 | Registers a custom log handler. 112 | - ***Parameters*** 113 | - `hanlder`: A unique_ptr pointing to the new log handler object. 114 | 115 | ### Unregister Log Handler 116 | ```cpp 117 | void unregisterLogHandler(); 118 | ``` 119 | - ***Function*** 120 | Unregisters the current log handler and enables the default log handler. 121 | 122 | ### Set Log Level 123 | ```cpp 124 | void setLogLevel(LogLevel level); 125 | ``` 126 | - ***Function*** 127 | Sets the log level. Logs below this level will not be output. 128 | - ***Parameters*** 129 | - `level`: The log level to be set. 130 | 131 | ### Log Output Function 132 | ```cpp 133 | void log(const char* file, int line, LogLevel level, const char* fmt, ...); 134 | ``` 135 | - ***Function*** 136 | An internally used log output function. It is recommended to use macros instead of calling this function directly. 137 | - ***Parameters*** 138 | - `file`: The file where the log originated. 139 | - `line`: The line number where the log originated. 140 | - `level`: The log level. 141 | - `fmt`: The format string. 142 | - `...`: Variable arguments for the format string. 143 | 144 | ## Usage Example 145 | 146 | ```cpp 147 | // Custom log handler 148 | class MyLogHandler : public ELITE::LogHandler { 149 | public: 150 | void log(const char* file, int line, ELITE::LogLevel loglevel, const char* log) override { 151 | // Custom log handling logic 152 | } 153 | }; 154 | 155 | // Register the custom log handler 156 | ELITE::registerLogHandler(std::make_unique()); 157 | 158 | // Set the log level 159 | ELITE::setLogLevel(ELITE::LogLevel::ELI_INFO); 160 | 161 | // Use log macros 162 | ELITE_LOG_INFO("This is an info message"); 163 | ELITE_LOG_ERROR("Error code: %d", 404); 164 | ``` -------------------------------------------------------------------------------- /doc/API/en/PrimaryPort.en.md: -------------------------------------------------------------------------------- 1 | # Primary Port 2 | 3 | ## Introduction 4 | The SDK provides interfaces for connecting to the robot's port 30001, sending scripts, and a framework for parsing the data from port 30001. Only partial data packets are parsed in the SDK. If you want to parse other data packets, you need to write the parsing code manually. 5 | 6 | # PrimaryPortInterface Class 7 | 8 | ## Introduction 9 | This interface provides methods for communicating with the robot's primary port interface. 10 | 11 | ## Header File of PrimaryPortInterface 12 | ```cpp 13 | #include 14 | ``` 15 | 16 | ## Constructor of the PrimaryPortInterface Class 17 | 18 | ### ***Constructor*** 19 | ```cpp 20 | PrimaryPortInterface::PrimaryPortInterface() 21 | ``` 22 | - ***Function*** 23 | Initializes the data. Note that this constructor does not connect to the robot. 24 | 25 | --- 26 | 27 | ## Communication 28 | 29 | ### ***Connection*** 30 | ```cpp 31 | bool connect(const std::string& ip, int port = PRIMARY_PORT) 32 | ``` 33 | - ***Function*** 34 | Connects to the robot's port 30001 (by default). 35 | - ***Parameters*** 36 | - ip: The IP address of the robot. 37 | - timeout_ms: Sets the timeout for the robot to read the next instruction. If it is less than or equal to 0, it will wait indefinitely. 38 | - ***Return Value***: Returns true if successful, and false if failed. 39 | 40 | - ***Note*** 41 | 1. Warning: Repeated calls to this function without intermediate disconnect() will force-close the active connection. 42 | 2. Usage constraint: Call rate must be ≤ 2Hz (once per 500ms minimum interval). 43 | 44 | --- 45 | 46 | ### ***Disconnection*** 47 | ```cpp 48 | void disconnect() 49 | ``` 50 | - ***Function*** 51 | Disconnects from the robot. 52 | 53 | - ***Note*** 54 | 55 | Suggest adding ~500ms delay between this function and connect calls. 56 | 57 | --- 58 | 59 | ### ***Send Script*** 60 | ```cpp 61 | bool sendScript(const std::string& script) 62 | ``` 63 | - ***Function*** 64 | Sends an executable script to the robot. 65 | - ***Parameters*** 66 | - script: The script to be sent. 67 | - ***Return Value***: Returns true if the sending is successful, and false if failed. 68 | 69 | --- 70 | 71 | ### Get Data Packet 72 | ```cpp 73 | bool getPackage(std::shared_ptr pkg, int timeout_ms) 74 | ``` 75 | - ***Function*** 76 | Retrieves and parses the robot's data packet. 77 | - ***Parameters*** 78 | - pkg: The data packet to be retrieved. 79 | - timeout_ms: The waiting timeout. 80 | - ***Return Value***: Returns true if the retrieval is successful, and false if failed. 81 | 82 | --- 83 | 84 | # PrimaryPackage Class 85 | 86 | ## Introduction 87 | This class is mainly used to be inherited to obtain the data from the Primary port. 88 | The SDK does not provide parsing for all data packets. If you need other data packets, you can refer to the implementation of `KinematicsInfo` in `RobotConfPackage.hpp` to write the data packet parsing code you need. Refer to the Elite official document "CS_User Manual_Robot Status Message.xlsx" for the message format. 89 | 90 | ## Header File of PrimaryPackage 91 | ```cpp 92 | #include 93 | ``` 94 | 95 | ## Constructor of the PrimaryPackage Class 96 | ### ***Constructor*** 97 | ```cpp 98 | PrimaryPackage::PrimaryPackage(int type) 99 | ``` 100 | - ***Function*** 101 | Initializes the data. 102 | - ***Parameters*** 103 | - type: The type of the data packet (refer to the Elite official document: CS_User Manual_Robot Status Message.xlsx). 104 | 105 | --- 106 | 107 | ## Pure Virtual Function 108 | 109 | ### Parse Message 110 | ```cpp 111 | void parser(int len, const std::vector::const_iterator& iter) 112 | ``` 113 | - ***Function*** 114 | The specific implementation is to be completed by subclasses. It parses the sub-message of the robot status message from the Primary port. When an instance of a subclass is passed as a parameter to `PrimaryPortInterface::getPackage()`, this function will be called. 115 | - ***Parameters*** 116 | - len: The length of the sub-message. 117 | - iter: The position of the sub-message in the whole message. 118 | 119 | --- 120 | 121 | ## Others 122 | 123 | ### ***Get Message Type*** 124 | ```cpp 125 | int getType() 126 | ``` 127 | - ***Function*** 128 | Gets the type of the data packet. 129 | - ***Return Value***: The type of the data packet. 130 | 131 | --- 132 | 133 | ## Internal Use in the SDK 134 | ### Wait for Update 135 | ```cpp 136 | bool waitUpdate(int timeout_ms) 137 | ``` 138 | - ***Function*** 139 | Waits for the data packet to be updated. It is called in the `getPackage()` function. 140 | - ***Parameters*** 141 | - timeout_ms: The timeout. 142 | - ***Return Value***: Returns true if it does not time out, and false if it times out. 143 | 144 | --- 145 | 146 | # KinematicsInfo Class 147 | 148 | ## Introduction 149 | This is for parsing the kinematics data packet in the robot configuration data. `PrimaryPackage` is the parent class of this interface. In addition to the methods in the `PrimaryPackage` class, the main content of this interface is the robot's DH parameters. 150 | 151 | ## Header File of KinematicsInfo 152 | ```cpp 153 | #include 154 | ``` 155 | 156 | ## DH Parameters 157 | 158 | - `vector6d_t dh_a_` 159 | 160 | - `vector6d_t dh_d_` 161 | 162 | - `vector6d_t dh_alpha_` -------------------------------------------------------------------------------- /doc/API/en/RemoteUpgrade.en.md: -------------------------------------------------------------------------------- 1 | # RemoteUpgrade Module 2 | 3 | ## Introduction 4 | The RemoteUpgrade module provides the function of remote upgrading for the robot control software. 5 | 6 | ## Header File 7 | ```cpp 8 | #include 9 | ``` 10 | 11 | ## Upgrade the Control Software 12 | 13 | ```cpp 14 | bool upgradeControlSoftware(std::string ip, std::string file, std::string password) 15 | ``` 16 | - ***Function*** 17 | Upgrades the robot control software. 18 | - ***Parameters*** 19 | - `ip`: The IP address of the robot. 20 | - `file`: The path of the upgrade file. 21 | - `password`: The SSH password of the robot controller. 22 | - ***Return Value*** 23 | - `true`: The upgrade is successful. 24 | - `false`: The upgrade fails. 25 | - ***Notes*** 26 | 1. Under the Linux system, if `libssh` is not installed, it is necessary to ensure that the computer running the SDK has the `scp`, `ssh`, and `sshpass` commands available. 27 | 2. Under the Windows system, if `libssh` is not installed, this interface is not available. -------------------------------------------------------------------------------- /doc/Architecture/Arch.cn.md: -------------------------------------------------------------------------------- 1 | # SDK 的架构 2 | 3 | ## 1. 架构 4 | 下图粗略地展示了SDK与机器人之间的数据流关系,也是SDK的架构: 5 | ![SDKDataFlow](./SDKDataFlow.drawio.png) 6 | 7 | 下面是关于架构图的一些说明: 8 | - Script File:指 `external_control.script` 脚本位于,会被`EliDriver`读取。 9 | - EliDriver:指 `EliDriver` 类。 10 | - Robot:指机器人本体。 11 | - RTSI Client:指SDK中RTSI模块。 12 | - DashboardClient:指SDK中Dashboard模块。 13 | 14 | 接下来会详细说明 EliDriver 的工作原理,其余的模块直接与机器人的功能对应,可以通过[User Guide](../UserGuide/cn/UserGuide.cn.md)来了解使用,其背后的工作原理并不难理解,这里就不多赘述。 15 | 16 | ## 2. EliDriver 17 | 18 | EliDriver 是一个控制机器人运动、设置机器人配置的类,本文主要阐述其工作原理。在开始说明之前,要先了解一下`ExternalControl`插件的工作流程。 19 | 20 | ### 2.1 ExternalControl 插件 21 | 简单来说`ExternalControl`是:“请求脚本--->接收与处理脚本--->运行脚本”的流程,在开始运行任务后,插件会向外部控制器发送`request\n`字符串来请求脚本,外部控制器在接收到请求后会向插件发送脚本,脚本的格式如下: 22 | ```python 23 | # HEADER_BEGIN 24 | ... 25 | # HEADER_END 26 | 27 | # NODE_CONTROL_LOOP_BEGINS 28 | ... 29 | # NODE_CONTROL_LOOP_ENDS 30 | 31 | ``` 32 | 插件依据以上格式将脚本分割为“header”和“control loop”两个部分,这两个部分的内容分别是“声明”和“执行”,例如: 33 | ```python 34 | # HEADER_BEGIN 35 | # 声明了printHello()函数 36 | def printHello(): 37 | textmsg("Hello") 38 | # HEADER_END 39 | 40 | # NODE_CONTROL_LOOP_BEGINS 41 | # 执行printHello() 42 | printHello() 43 | # NODE_CONTROL_LOOP_ENDS 44 | 45 | ``` 46 | 可以猜测到,外部控制器需要创建一个TCP的Server,端口就对应着插件配置的`Custom port`,并且在接收到`request\n`字符串时需要回复一个符合格式要求的脚本。下图简述了整个工作流: 47 | ![ExternalControl](./ExternalControl.drawio.png) 48 | 49 | ### 2.2 EliDriver工作原理 50 | 51 | #### 2.2.1 ScriptSender 52 | 53 | > `ScriptSender`是一个类,它在构造时创建了一个TCP服务器,当收到`request\n`时就回复脚本。主要代码:`source/Control/ScriptSender.cpp` 54 | 55 | 既然`ExternalControl`插件是接收脚本、运行脚本的流程,那么`EliDriver`在初始化时会读取脚本——“external_control.script”,并且按照规则写入参数。在收到请求脚本后,会调用 `ScriptSender` 来发送脚本给插件。 56 | 57 | 58 | #### 2.2.2 external_control.script 59 | 60 | > 脚本位置:`source/resources/external_control.script` 61 | 62 | `external_control.script`脚本的基本逻辑是:使用socket接口,连接`EliDriver`外部控制器,通过socket来读取指令和数据并执行。 63 | 64 | 我们直接来看脚本中`NODE_CONTROL_LOOP_BEGINS`后面的内容,脚本最开始调用了`socket_open()`创建了几个socket,后文会结合SDK中的代码说明每个socket的作用,这里先主要看`reverse_socket`。这个socket主要是接收外部控制器的运动指令和数据,接着往后看能看到一个`while`循环,这里面就是机器人的控制循环,通过`reverse_socket`接收到的报文来获取指令和点位或者速度等信息。以下是此循环的流程: 65 | ![reverse_socket](./ControlScript.drawio.png) 66 | 67 | > 这里可以看到`{{SERVER_IP_REPLACE}}`类似格式的字符串,在脚本执行时时会被替换为机器人的IP、端口等参数,这些参数都是`EliDriver`构造时传入的。 68 | 69 | 在执行“Receive command and data from reverse_socket”步骤的右边,有一个“If time out”的判断,在首次执行时,time out值为0,也就是无限等待(可参考Elite CS 脚本手册“socket_read_binary_integer()”指令的内容)。当接收到指令后,time out值会被更新,更新的值包含在指令中。也就是在执行完指令之后,再次接收`reverse_socket`的指令和数据时会依据上一次接收到的time out值来等待。 70 | 接收到指令与数据之后会判断是否是“停止机器人”的指令,如果是,将会执行“stopj()”指令。 71 | 72 | #### 2.2.3 ReverseInterface 73 | 74 | > `ReverseInterface`是一个类,它在构造时创建了一个TCP服务器,用于向机器人的`reverse_socket`socket发送指令和数据。主要代码:`source/Control/ReverseInterface.cpp` 75 | 76 | 我们现在来看`EliDriver`中的`writeServoj()`、`writeSpeedl()`、`writeSpeedj()`、`writeIdle()`接口,对于这几个接口来说,都是都是通过`ReverseInterface`向机器人的`reverse_socket`发送“指令+点位或速度”,脚本在接收到指令后,会启动一个线程来执行运动,而主线程会保持接收`reverse_socket`的数据来更新运动数据或者模式。 77 | 78 | 79 | #### 2.2.4 TrajectoryInterface 80 | 81 | > `TrajectoryInterface`是一个类,它在构造时创建了一个TCP服务器,用于向机器人的`trajectory_socket`socket发送指令和数据。主要代码:`source/Control/TrajectoryInterface.cpp`。 82 | 83 | 这个接口需要与`ReverseInterface`一起看,下面是`TrajectoryInterface`的工作流程图: 84 | ![trajectory_socket](./ControlScript-trajectory_socket.drawio.png) 85 | 86 | `EliDriver`中有以下几个接口`writeTrajectoryPoint()`、`writeTrajectoryControlAction()`、`setTrajectoryResultCallback()`。 87 | 88 | 脚本中先是通过`reverse_socket`接收到“Trajectory”的指令,如果是start指令(对应`writeTrajectoryControlAction(TrajectoryControlAction::START, ...)`),就会启动一个线程,此线程的`trajectory_socket`负责接收点位并且运动到点位(对应`writeTrajectoryPoint(...)`)。与此同时的,主线程会保持接收`reverse_socket`的指令和数据,因此在机器人运动带点位之前需要发送“Trajectory”的空操作,来保证`reverse_socket`不会超时。当运动结束时,如果使用了`setTrajectoryResultCallback()`设置回调,就会调用此回调函数,并告知结果。 89 | 90 | 91 | #### 2.2.5 ScriptCommandInterface 92 | 93 | > `ScriptCommandInterface`是一个类,它在构造时创建了一个TCP服务器,用于向机器人的`script_command_socket`socket发送指令和数据。主要代码:`source/Control/ScriptCommandInterface.cpp`。 94 | 95 | `external_control.script`脚本在打开socket连接的时候,打开了一个名为“script_command_socket”的socket连接,并且在初始化`script_command_thread_handle`变量时,直接创建了一个线程,这个线程会持续接收来自“script_command_socket”的指令与数据,并且执行相应的动作,例如设置负载、设置工具电压等。这个接口主要用于机器人的配置。 96 | 97 | -------------------------------------------------------------------------------- /doc/Architecture/ControlScript-trajectory_socket.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Elite-Robots/Elite_Robots_CS_SDK/656af3ddfcdb8ac586287ffa27d4d48d8e8b5978/doc/Architecture/ControlScript-trajectory_socket.drawio.png -------------------------------------------------------------------------------- /doc/Architecture/ControlScript.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Elite-Robots/Elite_Robots_CS_SDK/656af3ddfcdb8ac586287ffa27d4d48d8e8b5978/doc/Architecture/ControlScript.drawio.png -------------------------------------------------------------------------------- /doc/Architecture/ExternalControl.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Elite-Robots/Elite_Robots_CS_SDK/656af3ddfcdb8ac586287ffa27d4d48d8e8b5978/doc/Architecture/ExternalControl.drawio.png -------------------------------------------------------------------------------- /doc/Architecture/SDKDataFlow.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Elite-Robots/Elite_Robots_CS_SDK/656af3ddfcdb8ac586287ffa27d4d48d8e8b5978/doc/Architecture/SDKDataFlow.drawio.png -------------------------------------------------------------------------------- /doc/BuildGuide/BuildGuide.cn.md: -------------------------------------------------------------------------------- 1 | # 编译向导 2 | 3 | ## Requirements 4 | * SDK中的socket使用了 **boost::asio**。 因此需要安装 **boost** 库。 5 | * 此SDK需要支持 C++17 或 C++14 的编译器。注意,如果是C++14的标准,会使用到`boost::variant`。 6 | * SDK提供了通过ssh下载文件的接口,建议安装 libssh。如果不安装的话,则需要确保能运行ssh、scp、sshpass指令。 7 | * cmake版本 >=3.22.1 8 | 9 | ## 依赖安装 10 | 11 | ### Ubuntu 12 | 13 | - 基础依赖 14 | ```bash 15 | sudo apt update 16 | 17 | sudo apt install libboost-all-dev 18 | 19 | sudo apt install libssh-dev # 可选,建议安装 20 | 21 | # sudo apt install sshpass #如果没安装 libssh-dev 则需要安装此指令 22 | ``` 23 | 24 | - 测例依赖(可选) 25 | ```bash 26 | sudo apt update 27 | 28 | sudo apt install libgtest-dev 29 | ``` 30 | 31 | - 文档编译依赖(可选) 32 | ```bash 33 | sudo apt-get install doxygen 34 | sudo apt-get install doxygen-gui 35 | ``` 36 | 37 | ### Windows(vistual studio) 38 | 使用vcpkg在Windows中安装依赖,首先需要下载vcpkg,创建一个文件夹保存vcpkg,注意此文件路径在后续的编译中会用到: 39 | ```shell 40 | git clone https://github.com/microsoft/vcpkg.git 41 | 42 | .\bootstrap-vcpkg.bat 43 | 44 | ``` 45 | 46 | - 基础依赖 47 | ```shell 48 | .\vcpkg install boost 49 | 50 | .\vcpkg install libssh 51 | 52 | vcpkg integrate install 53 | ``` 54 | 55 | - 测例依赖 56 | ```bash 57 | vcpkg install gtest 58 | 59 | vcpkg integrate install 60 | ``` 61 | 62 | ## 编译 63 | 64 | ### cmake 配置选项 65 | 通过 -D 参数可设置本项目编译选项,语法格式为: 66 | ```bash 67 | cmake -D<变量名>=<值> [其他参数] 68 | ``` 69 | 70 | 例如,需要使用C++14标准编译,则使用下面指令: 71 | ```bash 72 | cmake -DCMAKE_CXX_STANDARD=14 .. 73 | ``` 74 | 75 | 除了cmake通用的配置选项以外,本项目中还有下面的选项: 76 | - ELITE_COMPILE_EXAMPLES 77 | - 值:BOOL 78 | - 说明:如果为TRUE,则会编译example目录下的代码,否则不会编译。 79 | - ELITE_COMPILE_TESTS 80 | - 值:BOOL 81 | - 说明:如果为TRUE,则会编译test目录下的代码,否则不会编译。 82 | - ELITE_COMPILE_DOC 83 | - 值:BOOL 84 | - 说明:如果为TRUE,则会使用doxygen生成文档。 85 | - 注:Windows中此选项暂不可用。 86 | 87 | ### Ubuntu 编译安装 88 | ```shell 89 | cd 90 | 91 | mkdir build && cd build 92 | 93 | cmake .. 94 | 95 | make -j 96 | 97 | # 安装 98 | sudo make install 99 | 100 | sudo ldconfig 101 | ``` 102 | 103 | 104 | ### Windows 编译 105 | ```shell 106 | cd 107 | 108 | mkdir build && cd build 109 | 110 | # 注意需要替换vcpkg的路径 111 | cmake -DCMAKE_TOOLCHAIN_FILE=\scripts\buildsystems\vcpkg.cmake .. 112 | 113 | cmake --build ./ 114 | ``` 115 | 编译完成后会得到`libelite-cs-series-sdk_static.lib` 和 `libelite-cs-series-sdk.dll` 两个库文件、包括了头文件的`include`文件夹。 -------------------------------------------------------------------------------- /doc/BuildGuide/BuildGuide.en.md: -------------------------------------------------------------------------------- 1 | # Compilation Guide 2 | 3 | ## Requirements 4 | * The socket in the SDK uses **boost::asio**. Therefore, the **boost** library needs to be installed. 5 | * This SDK requires a compiler that supports C++17 or C++14. Note that if the C++14 standard is used, `boost::variant` will be utilized. 6 | * The SDK provides an interface for downloading files via ssh. It is recommended to install libssh. If not installed, you need to ensure that the ssh, scp, and sshpass commands can be run. 7 | * The cmake version should be >= 3.22.1. 8 | 9 | ## Dependency Installation 10 | 11 | ### Ubuntu 12 | 13 | - Basic Dependencies 14 | ```bash 15 | sudo apt update 16 | 17 | sudo apt install libboost-all-dev 18 | 19 | sudo apt install libssh-dev # Optional, recommended installation 20 | 21 | # sudo apt install sshpass # Install this command if libssh-dev is not installed 22 | ``` 23 | 24 | - Test Case Dependencies (Optional) 25 | ```bash 26 | sudo apt update 27 | 28 | sudo apt install libgtest-dev 29 | ``` 30 | 31 | - Documentation Compilation Dependencies (Optional) 32 | ```bash 33 | sudo apt-get install doxygen 34 | sudo apt-get install doxygen-gui 35 | ``` 36 | 37 | ### Windows (Visual Studio) 38 | Use vcpkg to install dependencies on Windows. First, you need to download vcpkg and create a folder to save it. Note that the path of this file will be used in subsequent compilations: 39 | ```shell 40 | git clone https://github.com/microsoft/vcpkg.git 41 | 42 | .\bootstrap-vcpkg.bat 43 | 44 | ``` 45 | 46 | - Basic Dependencies 47 | ```shell 48 | .\vcpkg install boost 49 | 50 | .\vcpkg install libssh 51 | 52 | vcpkg integrate install 53 | ``` 54 | 55 | - Test Case Dependencies (Optional) 56 | ```bash 57 | vcpkg install gtest 58 | 59 | vcpkg integrate install 60 | ``` 61 | 62 | ## Compilation 63 | 64 | ### CMake Configuration Options 65 | You can set the compilation options for this project using the -D parameter. The syntax is as follows: 66 | ```bash 67 | cmake -D= [other parameters] 68 | ``` 69 | 70 | For example, if you need to compile using the C++14 standard, use the following command: 71 | ```bash 72 | cmake -DCMAKE_CXX_STANDARD=14 .. 73 | ``` 74 | 75 | In addition to the common CMake configuration options, this project also has the following options: 76 | - ELITE_COMPILE_EXAMPLES 77 | - Value: BOOL 78 | - Description: If set to TRUE, the code in the example directory will be compiled; otherwise, it will not be compiled. 79 | - ELITE_COMPILE_TESTS 80 | - Value: BOOL 81 | - Description: If set to TRUE, the code in the test directory will be compiled; otherwise, it will not be compiled. 82 | - ELITE_COMPILE_DOC 83 | - Value: BOOL 84 | - Description: If set to TRUE, documentation will be generated using doxygen. 85 | - Note: This option is currently unavailable on Windows. 86 | 87 | ### Ubuntu Compilation and Installation 88 | ```shell 89 | cd 90 | 91 | mkdir build && cd build 92 | 93 | cmake .. 94 | 95 | make -j 96 | 97 | # Installation 98 | sudo make install 99 | 100 | sudo ldconfig 101 | ``` 102 | 103 | ### Windows Compilation 104 | ```shell 105 | cd 106 | 107 | mkdir build && cd build 108 | 109 | # Note that you need to replace the path to vcpkg 110 | cmake -DCMAKE_TOOLCHAIN_FILE=\scripts\buildsystems\vcpkg.cmake .. 111 | 112 | cmake --build ./ 113 | ``` 114 | After compilation, you will get two library files, `libelite-cs-series-sdk_static.lib` and `libelite-cs-series-sdk.dll`, and an `include` folder containing header files. -------------------------------------------------------------------------------- /doc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(Doxygen REQUIRED) 2 | 3 | if (DOXYGEN_FOUND) 4 | # Doxygen configuration 5 | set(DOXYGEN_PROJECT_NAME Elite_Robots_CS_SDK) 6 | set(DOXYGEN_OPTIMIZE_OUTPUT_FOR_C YES) 7 | set(DOXYGEN_TYPEDEF_HIDES_STRUCT YES) 8 | set(DOXYGEN_EXTRACT_ALL YES) 9 | set(DOXYGEN_EXTRACT_STATIC YES) 10 | set(DOXYGEN_STRIP_CODE_COMMENTS NO) 11 | set(DOXYGEN_MACRO_EXPANSION YES) 12 | set(DOXYGEN_EXPAND_ONLY_PREDEF YES) 13 | set(DOXYGEN_PREDEFINED ELITE_EXPORT) 14 | set(DOXYGEN_EXPAND_AS_DEFINED ELITE_EXPORT) 15 | set(DOXYGEN_COLLABORATION_GRAPH NO) 16 | set(DOXYGEN_INCLUDE_GRAPH NO) 17 | set(DOXYGEN_INCLUDED_BY_GRAPH NO) 18 | set(DOXYGEN_RECURSIVE NO) 19 | set(DOXYGEN_USE_MDFILE_AS_MAINPAGE README.md) 20 | set(DOXYGEN_GENERATE_XML YES) 21 | set(DOXYGEN_WARN_AS_ERROR FAIL_ON_WARNINGS) # Doxygen 1.9+ 22 | set(DOXYGEN_ALIASES 23 | [[req="\xrefitem req \"Requirement\" \"Requirements\" "]] 24 | ) 25 | set(DOXYGEN_VERBATIM_VARS DOXYGEN_ALIASES) 26 | set(DOXYGEN_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/docs) 27 | 28 | doxygen_add_docs( 29 | doxygen_doc 30 | ${PROJECT_BINARY_DIR}/include/Elite/ 31 | ${CMAKE_SOURCE_DIR} 32 | ${CMAKE_SOURCE_DIR}/doc/UserGuide/cn/ 33 | ${CMAKE_SOURCE_DIR}/doc/UserGuide/en/ 34 | ${CMAKE_SOURCE_DIR}/doc/Architecture/ 35 | COMMENT "Generating documentation with Doxygen" 36 | ) 37 | 38 | add_custom_target( 39 | doxygen_doc_target ALL 40 | COMMAND ${DOXYGEN_EXECUTABLE} ${PROJECT_BINARY_DIR}/doc/Doxyfile.doxygen_doc 41 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 42 | COMMENT "Generating API documentation with Doxygen" 43 | VERBATIM 44 | ) 45 | endif() 46 | -------------------------------------------------------------------------------- /doc/MessageFormat/MessageFormat.cn.md: -------------------------------------------------------------------------------- 1 | # 报文格式 2 | 3 | 此文档描述了报文格式,每个报文的长度都是固定的。 4 | 5 | ## 1. ReverseInterface 6 | 每个数据的大小为4个字节。 7 | 8 | ### 1.1 发送给控制脚本的报文 9 | 报文长度:8 个int数据。 10 | 11 | | 名称 | 描述 | 12 | | --- | --- | 13 | | timeout | 超时(毫秒) | 14 | | 数据1 | 关节角度、速度等数据*1(放大 1000000 倍传输) | 15 | | 数据2 | 关节角度、速度等数据(放大 1000000 倍传输) | 16 | | 数据3 | 关节角度、速度等数据(放大 1000000 倍传输) | 17 | | 数据4 | 关节角度、速度等数据(放大 1000000 倍传输) | 18 | | 数据5 | 关节角度、速度等数据(放大 1000000 倍传输) | 19 | | 数据6 | 关节角度、速度等数据(放大 1000000 倍传输) | 20 | | 模式 | *2控制模式(决定了是movej还是servoj等运动方式) | 21 | 22 | > *1:因为脚本在读取的时候,读取的是整数,而关节角等数据都是浮点数,因此需要放大 1000000 倍传输,脚本中读取的时候,会除以 1000000 。 23 | > *2:对应代码`ControlMode.hpp`中的`ControlMode`枚举。 24 | 25 | 对于`writeTrajectoryControlAction()`接口报文格式则如下: 26 | | 名称 | 描述 | 27 | | --- | --- | 28 | | timeout | 超时(毫秒) | 29 | | 动作 | 开始、取消、空操作 | 30 | | 点位的数量 | 点位的数量 | 31 | | 保留 | 保留 | 32 | | 保留 | 保留 | 33 | | 保留 | 保留 | 34 | | 保留 | 保留 | 35 | | 模式 | 值为`ControlMode::MODE_TRAJECTORY` | 36 | 37 | ## 2. ScriptCommandInterface 38 | 39 | ### 2.1 发送给控制脚本的报文 40 | 报文长度:26 个int数据。 41 | 42 | | 名称 | 描述 | 43 | | --- | --- | 44 | | command | *1指令 | 45 | | 指令数据1~25 | *2指令需要的数据 | 46 | 47 | > *1:对应代码`ScriptCommandInterface.hpp`中的`ScriptCommandInterface::Cmd`枚举。 48 | > *2:依据指令的不同,有用的数据个数也不同,没用到的数据可以设置为0。 49 | 50 | ## 3. TrajectoryInterface 51 | 52 | ### 3.1 发送给控制脚本的报文 53 | 报文长度:21 个int数据。 54 | 55 | | 名称 | 描述 | 56 | | --- | --- | 57 | | x或关节1 | 目标点的x坐标值,或关节1的角度值*1(放大 1000000 倍传输) | 58 | | y或关节2 | 目标点的y坐标值,或关节2的角度值(放大 1000000 倍传输) | 59 | | z或关节3 | 目标点的z坐标值,或关节3的角度值(放大 1000000 倍传输) | 60 | | rx或关节4 | 目标点的rx坐标值,或关节4的角度值(放大 1000000 倍传输) | 61 | | ry或关节5 | 目标点的ry坐标值,或关节5的角度值(放大 1000000 倍传输) | 62 | | rz或关节6 | 目标点的rz坐标值,或关节6的角度值(放大 1000000 倍传输) | 63 | | 7~18 | 保留 | 64 | | 时间 | 到达目标点的时间 | 65 | | 交融半径 | (放大 1000000 倍传输) | 66 | | 轨迹类型 | *1轨迹类型 | 67 | 68 | > *1:因为脚本在读取的时候,读取的是整数,而关节角等数据都是浮点数,因此需要放大 1000000 倍传输,脚本中读取的时候,会除以 1000000 。 69 | > *2:对应代码`TrajectoryInterface.hpp`中的`TrajectoryMotionType`枚举。 70 | 71 | ### 3.2 控制脚本返回的报文 72 | 报文长度:1 个int数据。 73 | 74 | | 名称 | 描述 | 75 | | --- | --- | 76 | | 结果 | *1运行结果 | 77 | > *2:对应代码`DataType.hpp`中的`TrajectoryMotionResult`枚举。 78 | 79 | -------------------------------------------------------------------------------- /doc/MessageFormat/MessageFormat.en.md: -------------------------------------------------------------------------------- 1 | # Message Format 2 | 3 | This document describes the message format, and the length of each message is fixed. 4 | 5 | ## 1. ReverseInterface 6 | The size of each piece of data is 4 bytes. 7 | 8 | ### 1.1 Messages Sent to the Control Script 9 | Message length: 8 int data. 10 | 11 | | Name | Description | 12 | | ---- | ---- | 13 | | timeout | Timeout (in milliseconds) | 14 | | Data 1 | Data such as joint angles and velocities*1 (transmitted after being amplified by 1,000,000 times) | 15 | | Data 2 | Data such as joint angles and velocities (transmitted after being amplified by 1,000,000 times) | 16 | | Data 3 | Data such as joint angles and velocities (transmitted after being amplified by 1,000,000 times) | 17 | | Data 4 | Data such as joint angles and velocities (transmitted after being amplified by 1,000,000 times) | 18 | | Data 5 | Data such as joint angles and velocities (transmitted after being amplified by 1,000,000 times) | 19 | | Data 6 | Data such as joint angles and velocities (transmitted after being amplified by 1,000,000 times) | 20 | | Mode | *2Control mode (determines the motion method such as movej or servoj) | 21 | 22 | > *1: Since the script reads integers, while data like joint angles are floating-point numbers, they need to be amplified by 1,000,000 times for transmission. When the script reads these data, it will divide them by 1,000,000. 23 | > *2: Corresponds to the `ControlMode` enumeration in the code `ControlMode.hpp`. 24 | 25 | For the `writeTrajectoryControlAction()` interface, the message format is as follows: 26 | 27 | | Name | Description | 28 | | ---- | ---- | 29 | | timeout | Timeout (in milliseconds) | 30 | | Action | Start, cancel or noop | 31 | | Number of Points | The number of points | 32 | | Reserved | Reserved | 33 | | Reserved | Reserved | 34 | | Reserved | Reserved | 35 | | Reserved | Reserved | 36 | | Mode | The value is `ControlMode::MODE_TRAJECTORY` | 37 | 38 | 39 | 40 | ## 2. ScriptCommandInterface 41 | 42 | ### 2.1 Messages Sent to the Control Script 43 | Message length: 26 int data. 44 | 45 | | Name | Description | 46 | | ---- | ---- | 47 | | command | *1Instruction | 48 | | Instruction Data 1 - 25 | *2Data required by the instruction | 49 | 50 | > *1: Corresponds to the `ScriptCommandInterface::Cmd` enumeration in the code `ScriptCommandInterface.hpp`. 51 | > *2: Depending on the instruction, the number of useful data varies. Unused data can be set to 0. 52 | 53 | ## 3. TrajectoryInterface 54 | 55 | ### 3.1 Messages Sent to the Control Script 56 | Message length: 21 int data. 57 | 58 | | Name | Description | 59 | | ---- | ---- | 60 | | x or Joint 1 | The x coordinate value of the target point, or the angle value of joint 1*1 (transmitted after being amplified by 1,000,000 times) | 61 | | y or Joint 2 | The y coordinate value of the target point, or the angle value of joint 2 (transmitted after being amplified by 1,000,000 times) | 62 | | z or Joint 3 | The z coordinate value of the target point, or the angle value of joint 3 (transmitted after being amplified by 1,000,000 times) | 63 | | rx or Joint 4 | The rx coordinate value of the target point, or the angle value of joint 4 (transmitted after being amplified by 1,000,000 times) | 64 | | ry or Joint 5 | The ry coordinate value of the target point, or the angle value of joint 5 (transmitted after being amplified by 1,000,000 times) | 65 | | rz or Joint 6 | The rz coordinate value of the target point, or the angle value of joint 6 (transmitted after being amplified by 1,000,000 times) | 66 | | 7 - 18 | Reserved | 67 | | Time | The time to reach the target point | 68 | | Blend Radius | (transmitted after being amplified by 1,000,000 times) | 69 | | Trajectory Type | *1Trajectory type | 70 | 71 | > *1: Since the script reads integers, while data like joint angles are floating-point numbers, they need to be amplified by 1,000,000 times for transmission. When the script reads these data, it will divide them by 1,000,000. 72 | > *2: Corresponds to the `TrajectoryMotionType` enumeration in the code `TrajectoryInterface.hpp`. 73 | 74 | ### 3.2 Messages Returned by the Control Script 75 | Message length: 1 int data. 76 | 77 | | Name | Description | 78 | | ---- | ---- | 79 | | Result | *1Operation result | 80 | > *2: Corresponds to the `TrajectoryMotionResult` enumeration in the code `DataType.hpp`. -------------------------------------------------------------------------------- /doc/UserGuide/cn/Dashboard.cn.md: -------------------------------------------------------------------------------- 1 | # Dashboard 2 | 3 | ## Calss 4 | - DashboardClient 5 | 6 | ## Background 7 | Dashbaord Shell是与机器人交互的一种方式,通过TCP连接机器人的29999端口,可实现机器人上下电、释放抱闸、加载查询任务等操作。SDK中提供了`DashboardClient`类,里面包含了大部分的dashboard接口。 8 | 9 | ## Prerequisites 10 | - 确保正确安装了SDK。 11 | 12 | ## Usage 13 | 14 | 创建一个代码文件: 15 | ```bash 16 | touch dashboard_example.cpp 17 | ``` 18 | 19 | 复制下面代码,使用你习惯的文本编辑器,粘贴到`dashboard_example.cpp`中: 20 | 21 | ```cpp 22 | #include 23 | 24 | #include 25 | 26 | using namespace ELITE; 27 | 28 | 29 | const std::string DEFAULT_ROBOT_IP = "192.168.51.244"; 30 | 31 | int main(int argc, char* argv[]) { 32 | std::string robot_ip = DEFAULT_ROBOT_IP; 33 | if (argc > 1) { 34 | robot_ip = std::string(argv[1]); 35 | } 36 | 37 | std::unique_ptr my_dashboard; 38 | my_dashboard.reset(new DashboardClient()); 39 | 40 | if (!my_dashboard->connect(robot_ip)) { 41 | std::cout << "Could not connect to robot" << std::endl; 42 | return 1; 43 | } else { 44 | std::cout << "Connect to robot" << std::endl; 45 | } 46 | 47 | if (!my_dashboard->echo()) { 48 | std::cout << "Echo not right response" << std::endl; 49 | return 1; 50 | } else { 51 | std::cout << "Echo right response" << std::endl; 52 | } 53 | 54 | my_dashboard->disconnect(); 55 | 56 | return 0; 57 | } 58 | ``` 59 | 60 | 使用gcc编译器,编译`dashboard_example.cpp`: 61 | ```bash 62 | g++ dashboard_example.cpp -o dashboard_example -lelite-cs-series-sdk 63 | ``` 64 | 65 | 运行编译出来的二进制程序: 66 | ```bash 67 | ./dashboard_example 68 | ``` 69 | 70 | 如果一切正常应当输出: 71 | ``` 72 | Connect to robot 73 | Echo right response 74 | ``` 75 | 76 | ## Examine the code 77 | 78 | 代码最开始的部分,包含了C++标准的头文件。标准头文件之后是`Elite/DashboardClient.hpp`,包含了此头文件之后便可以使用dashboard的接口。 79 | ```cpp 80 | #include 81 | 82 | #include 83 | 84 | using namespace ELITE; 85 | ``` 86 | 87 | main函数是程序开始的地方,main函数中最开始的部分的功能是:如果运行`dashboard_example`时,后面有跟机器人的IP参数,则使用此IP,否则使用默认IP。 88 | 89 | ```cpp 90 | int main(int argc, char* argv[]) { 91 | std::string robot_ip = DEFAULT_ROBOT_IP; 92 | if (argc > 1) { 93 | robot_ip = std::string(argv[1]); 94 | } 95 | ``` 96 | 97 | 紧接着创建了一个智能指针,并在内存中创建了一个`DashboardClient`的实例: 98 | ```cpp 99 | std::unique_ptr my_dashboard; 100 | my_dashboard.reset(new DashboardClient()); 101 | ``` 102 | 103 | 通过`->`符号可以调用`DashboardClient`的方法,调用`connect()`方法连接到机器人,调用`echo()`方法运行dashboard shell的`echo`指令,如果机器人正确相应了,此方法会返回`true`,依据返回值进行输出,最后调用`disconnect()`方法来断开与dashboard的连接。 104 | 105 | ```cpp 106 | if (!my_dashboard->connect(robot_ip)) { 107 | std::cout << "Could not connect to robot" << std::endl; 108 | return 1; 109 | } else { 110 | std::cout << "Connect to robot" << std::endl; 111 | } 112 | 113 | if (!my_dashboard->echo()) { 114 | std::cout << "Echo not right response" << std::endl; 115 | return 1; 116 | } else { 117 | std::cout << "Echo right response" << std::endl; 118 | } 119 | 120 | my_dashboard->disconnect(); 121 | ``` 122 | -------------------------------------------------------------------------------- /doc/UserGuide/cn/Log.cn.md: -------------------------------------------------------------------------------- 1 | # Log 2 | 3 | ## Background 4 | 5 | SDK拥有日志功能,并且支持用户自定义日志记录方式和等级。 6 | 7 | ## Prerequisites 8 | - 确保正确安装了SDK。 9 | 10 | ## Usage 11 | 12 | ### Change logging level 13 | 14 | 决定需要在程序中打印的日志等级,默认情况下,只有INFO或更高等级的日志才会被打印。调用`setLogLevel()`函数便可设置日志等级。 15 | 16 | ```cpp 17 | #include 18 | 19 | int main() { 20 | ELITE::setLogLevel(ELITE::LogLevel::DEBUG); 21 | 22 | ELITE_LOG_DEBUG("Log level set debug"); 23 | 24 | return 0; 25 | } 26 | ``` 27 | 28 | ### Create new log handle 29 | 30 | SDK中提供了默认的打印到终端的日志记录方式,如果需要,可以自定义日志的输出方式,下面的代码或许会给你灵感: 31 | 32 | ```cpp 33 | #include 34 | #include 35 | #include 36 | 37 | class NewLogHandler : public ELITE::LogHandler 38 | { 39 | private: 40 | 41 | public: 42 | NewLogHandler() = default; 43 | ~NewLogHandler() = default; 44 | 45 | void log(const char* file, int line, ELITE::LogLevel level, const char* log) { 46 | switch (level) { 47 | case ELITE::LogLevel::DEBUG: 48 | std::cout << "[DEBUG] " << file << ":" << line << ": " << log << std::endl; 49 | break; 50 | case ELITE::LogLevel::INFO: 51 | std::cout << "[INFO] " << file << ":" << line << ": " << log << std::endl; 52 | break; 53 | case ELITE::LogLevel::WARN: 54 | std::cout << "[WARN] " << file << ":" << line << ": " << log << std::endl; 55 | break; 56 | case ELITE::LogLevel::ERROR: 57 | std::cout << "[ERROR] " << file << ":" << line << ": " << log << std::endl; 58 | break; 59 | case ELITE::LogLevel::FATAL: 60 | std::cout << "[FATAL] " << file << ":" << line << ": " << log << std::endl; 61 | break; 62 | case ELITE::LogLevel::NONE: 63 | std::cout << "[NONE] " << file << ":" << line << ": " << log << std::endl; 64 | break; 65 | default: 66 | break; 67 | } 68 | } 69 | 70 | }; 71 | 72 | int main() { 73 | std::unique_ptr log_handle(new NewLogHandler); 74 | ELITE::registerLogHandler(std::move(log_handle)); 75 | 76 | ELITE_LOG_INFO("Register new log handler"); 77 | 78 | return 0; 79 | } 80 | ``` -------------------------------------------------------------------------------- /doc/UserGuide/cn/UserGuide.cn.md: -------------------------------------------------------------------------------- 1 | # Elite Robots CS SDK user guide 2 | 3 | 本使用向导旨在说明SDK中各个功能的使用,可以按需点击下面的章节进行查看。此向导的演示主要是在Ubuntu22.04上。 4 | 5 | - [Dashboard](./Dashboard.cn.md) 6 | - [RTSI](./RTSI.cn.md) 7 | - [PrimaryPort](./PrimaryPort.cn.md) 8 | - [EliteDriver](./EliteDriver.cn.md) 9 | - [Log](./Log.cn.md) -------------------------------------------------------------------------------- /doc/UserGuide/en/Dashboard.en.md: -------------------------------------------------------------------------------- 1 | # Dashboard 2 | 3 | ## Class 4 | - DashboardClient 5 | 6 | ## Background 7 | The Dashboard Shell is a way to interact with the robot. By connecting to port 29999 of the robot through TCP, operations such as powering on and off the robot, releasing the brake, and loading and querying tasks can be achieved. The SDK provides the `DashboardClient` class, which contains most of the dashboard interfaces. 8 | 9 | ## Prerequisites 10 | - Ensure that the SDK is installed correctly. 11 | 12 | ## Usage 13 | 14 | Create a code file: 15 | ```bash 16 | touch dashboard_example.cpp 17 | ``` 18 | 19 | Copy the following code and paste it into `dashboard_example.cpp` using your preferred text editor: 20 | 21 | ```cpp 22 | #include 23 | 24 | #include 25 | 26 | using namespace ELITE; 27 | 28 | 29 | const std::string DEFAULT_ROBOT_IP = "192.168.51.244"; 30 | 31 | int main(int argc, char* argv[]) { 32 | std::string robot_ip = DEFAULT_ROBOT_IP; 33 | if (argc > 1) { 34 | robot_ip = std::string(argv[1]); 35 | } 36 | 37 | std::unique_ptr my_dashboard; 38 | my_dashboard.reset(new DashboardClient()); 39 | 40 | if (!my_dashboard->connect(robot_ip)) { 41 | std::cout << "Could not connect to robot" << std::endl; 42 | return 1; 43 | } else { 44 | std::cout << "Connect to robot" << std::endl; 45 | } 46 | 47 | if (!my_dashboard->echo()) { 48 | std::cout << "Echo not right response" << std::endl; 49 | return 1; 50 | } else { 51 | std::cout << "Echo right response" << std::endl; 52 | } 53 | 54 | my_dashboard->disconnect(); 55 | 56 | return 0; 57 | } 58 | ``` 59 | 60 | Compile `dashboard_example.cpp` using the gcc compiler: 61 | ```bash 62 | g++ dashboard_example.cpp -o dashboard_example -lelite-cs-series-sdk 63 | ``` 64 | 65 | Run the compiled binary program: 66 | ```bash 67 | ./dashboard_example 68 | ``` 69 | 70 | If everything is normal, the following should be output: 71 | ``` 72 | Connect to robot 73 | Echo right response 74 | ``` 75 | 76 | ## Examine the code 77 | 78 | At the beginning of the code are the header files of the C++ standard. After the standard header files is `Elite/DashboardClient.hpp`. After including this header file, the dashboard interfaces can be used. 79 | ```cpp 80 | #include 81 | 82 | #include 83 | 84 | using namespace ELITE; 85 | ``` 86 | 87 | The main function is where the program starts. The function of the beginning part of the main function is: if there is a robot IP parameter after running `dashboard_example`, use this IP; otherwise, use the default IP. 88 | 89 | ```cpp 90 | int main(int argc, char* argv[]) { 91 | std::string robot_ip = DEFAULT_ROBOT_IP; 92 | if (argc > 1) { 93 | robot_ip = std::string(argv[1]); 94 | } 95 | ``` 96 | 97 | Immediately after, a smart pointer is created and an instance of `DashboardClient` is created in memory: 98 | ```cpp 99 | std::unique_ptr my_dashboard; 100 | my_dashboard.reset(new DashboardClient()); 101 | ``` 102 | 103 | Methods of `DashboardClient` can be called through the `->` symbol. Call the `connect()` method to connect to the robot, and call the `echo()` method to run the `echo` command of the dashboard shell. If the robot responds correctly, this method will return `true`. Output according to the return value. Finally, call the `disconnect()` method to disconnect from the dashboard. 104 | 105 | ```cpp 106 | if (!my_dashboard->connect(robot_ip)) { 107 | std::cout << "Could not connect to robot" << std::endl; 108 | return 1; 109 | } else { 110 | std::cout << "Connect to robot" << std::endl; 111 | } 112 | 113 | if (!my_dashboard->echo()) { 114 | std::cout << "Echo not right response" << std::endl; 115 | return 1; 116 | } else { 117 | std::cout << "Echo right response" << std::endl; 118 | } 119 | 120 | my_dashboard->disconnect(); 121 | ``` -------------------------------------------------------------------------------- /doc/UserGuide/en/Log.en.md: -------------------------------------------------------------------------------- 1 | # Log 2 | 3 | ## Background 4 | 5 | The SDK has a logging function and supports users to customize the logging method and level. 6 | 7 | ## Prerequisites 8 | - Ensure that the SDK is installed correctly. 9 | 10 | ## Usage 11 | 12 | ### Change logging level 13 | 14 | Decide the log level that needs to be printed in the program. By default, only logs of INFO level or higher will be printed. Calling the `setLogLevel()` function can set the log level. 15 | 16 | ```cpp 17 | #include 18 | 19 | int main() { 20 | ELITE::setLogLevel(ELITE::LogLevel::DEBUG); 21 | 22 | ELITE_LOG_DEBUG("Log level set debug"); 23 | 24 | return 0; 25 | } 26 | ``` 27 | 28 | ### Create new log handle 29 | 30 | The SDK provides a default log recording method that prints to the terminal. If needed, you can customize the log output method. The following code may give you inspiration: 31 | 32 | ```cpp 33 | #include 34 | #include 35 | #include 36 | 37 | class NewLogHandler : public ELITE::LogHandler 38 | { 39 | private: 40 | 41 | public: 42 | NewLogHandler() = default; 43 | ~NewLogHandler() = default; 44 | 45 | void log(const char* file, int line, ELITE::LogLevel level, const char* log) { 46 | switch (level) { 47 | case ELITE::LogLevel::DEBUG: 48 | std::cout << "[DEBUG] " << file << ":" << line << ": " << log << std::endl; 49 | break; 50 | case ELITE::LogLevel::INFO: 51 | std::cout << "[INFO] " << file << ":" << line << ": " << log << std::endl; 52 | break; 53 | case ELITE::LogLevel::WARN: 54 | std::cout << "[WARN] " << file << ":" << line << ": " << log << std::endl; 55 | break; 56 | case ELITE::LogLevel::ERROR: 57 | std::cout << "[ERROR] " << file << ":" << line << ": " << log << std::endl; 58 | break; 59 | case ELITE::LogLevel::FATAL: 60 | std::cout << "[FATAL] " << file << ":" << line << ": " << log << std::endl; 61 | break; 62 | case ELITE::LogLevel::NONE: 63 | std::cout << "[NONE] " << file << ":" << line << ": " << log << std::endl; 64 | break; 65 | default: 66 | break; 67 | } 68 | } 69 | 70 | }; 71 | 72 | int main() { 73 | std::unique_ptr log_handle(new NewLogHandler); 74 | ELITE::registerLogHandler(std::move(log_handle)); 75 | 76 | ELITE_LOG_INFO("Register new log handler"); 77 | 78 | return 0; 79 | } 80 | ``` -------------------------------------------------------------------------------- /doc/UserGuide/en/UserGuide.en.md: -------------------------------------------------------------------------------- 1 | # Elite Robots CS SDK user guide 2 | 3 | This user guide is designed to explain the usage of various functions in the SDK. You can click on the following sections as needed for viewing. The demonstrations in this guide are mainly on Ubuntu 22.04. 4 | 5 | - [Dashboard](./Dashboard.en.md) 6 | - [RTSI](./RTSI.en.md) 7 | - [PrimaryPort](./PrimaryPort.en.md) 8 | - [EliteDriver](./EliteDriver.en.md) 9 | - [Log](./Log.en.md) -------------------------------------------------------------------------------- /elite-cs-series-sdkConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include("${CMAKE_CURRENT_LIST_DIR}/elite-cs-series-sdkTargets.cmake") 4 | -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | configure_file(../source/resources/external_control.script ${PROJECT_BINARY_DIR}/example/ COPYONLY) 2 | configure_file(resource/input_recipe.txt ${PROJECT_BINARY_DIR}/example/ COPYONLY) 3 | configure_file(resource/output_recipe.txt ${PROJECT_BINARY_DIR}/example/ COPYONLY) 4 | 5 | file(GLOB SOURCES *.cpp) 6 | 7 | # On Windows systems, when using CMake in the main directory, 8 | # the ELITE_EXPORT_LIBRARY macro definition must be removed to avoid compilation errors. 9 | if(CMAKE_SYSTEM_NAME STREQUAL "Windows") 10 | remove_definitions(-DELITE_EXPORT_LIBRARY) 11 | endif() 12 | 13 | foreach(SOURCE ${SOURCES}) 14 | get_filename_component(ELITE_SDK_EXAMPLE_NAME ${SOURCE} NAME_WE) 15 | add_executable(${ELITE_SDK_EXAMPLE_NAME} ${SOURCE}) 16 | target_link_libraries( 17 | ${ELITE_SDK_EXAMPLE_NAME} 18 | elite-cs-series-sdk::shared 19 | ${SYSTEM_LIB} 20 | ) 21 | target_link_directories( 22 | ${ELITE_SDK_EXAMPLE_NAME} 23 | PRIVATE ${CMAKE_BINARY_DIR} 24 | ) 25 | endforeach() 26 | 27 | 28 | -------------------------------------------------------------------------------- /example/dashboard_example.cpp: -------------------------------------------------------------------------------- 1 | #include "Elite/DashboardClient.hpp" 2 | #include "Elite/DataType.hpp" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace ELITE; 9 | 10 | // In a real-world example it would be better to get those values from command line parameters / a 11 | // better configuration system such as Boost.Program_options 12 | const std::string DEFAULT_ROBOT_IP = "192.168.51.244"; 13 | 14 | int main(int argc, char* argv[]) { 15 | // Parse the ip arguments if given 16 | std::string robot_ip = DEFAULT_ROBOT_IP; 17 | if (argc > 1) { 18 | robot_ip = std::string(argv[1]); 19 | } 20 | 21 | // Making the robot ready for the program by: 22 | // Connect the the robot Dashboard 23 | std::unique_ptr my_dashboard; 24 | my_dashboard.reset(new DashboardClient()); 25 | if (!my_dashboard->connect(robot_ip)) { 26 | std::cout << "Could not connect to robot" << std::endl; 27 | return 1; 28 | } else { 29 | std::cout << "Connect to robot" << std::endl; 30 | } 31 | 32 | if (!my_dashboard->echo()) { 33 | std::cout << "Echo not right response" << std::endl; 34 | return 1; 35 | } else { 36 | std::cout << "Echo right response" << std::endl; 37 | } 38 | 39 | 40 | if (!my_dashboard->powerOff()) { 41 | std::cout << "Could not send power off" << std::endl; 42 | return 1; 43 | } else { 44 | std::cout << "Power off" << std::endl; 45 | } 46 | 47 | my_dashboard->closeSafetyDialog(); 48 | 49 | // Power it on 50 | if (!my_dashboard->powerOn()) { 51 | std::cout << "Could not send Power on command" << std::endl; 52 | return 1; 53 | } else { 54 | std::cout << "Power on" << std::endl; 55 | } 56 | 57 | // Release the brakes 58 | if (!my_dashboard->brakeRelease()) { 59 | std::cout << "Could not send BrakeRelease command" << std::endl; 60 | return 1; 61 | } else { 62 | std::cout << "Brake release" << std::endl; 63 | } 64 | 65 | // Load existing task 66 | const std::string task_file_name_to_be_loaded("test.task"); 67 | if (!my_dashboard->loadTask(task_file_name_to_be_loaded)) { 68 | std::cout << "Could not load " << task_file_name_to_be_loaded.c_str() << std::endl; 69 | return 1; 70 | } 71 | std::string task = my_dashboard->getTaskPath(); 72 | if (task != task_file_name_to_be_loaded) { 73 | std::cout << "Not load right task" << std::endl; 74 | return 1; 75 | } else { 76 | std::cout << "Load task" << std::endl; 77 | } 78 | 79 | if (my_dashboard->getTaskStatus() != TaskStatus::STOPPED) { 80 | std::cout << "Task not stopped" << std::endl; 81 | return 1; 82 | } else { 83 | std::cout << "Task stopped" << std::endl; 84 | } 85 | 86 | if (!my_dashboard->playProgram()) { 87 | std::cout << "Could not play task" << std::endl; 88 | return 1; 89 | } else { 90 | std::cout << "Play task" << std::endl; 91 | } 92 | 93 | if (my_dashboard->getTaskStatus() != TaskStatus::PLAYING) { 94 | std::cout << "Task not running" << std::endl; 95 | return 1; 96 | } else { 97 | std::cout << "Task running" << std::endl; 98 | } 99 | 100 | if (!my_dashboard->pauseProgram()) { 101 | std::cout << "Could not pause task" << std::endl; 102 | return 1; 103 | } else { 104 | std::cout << "Pause task" << std::endl; 105 | } 106 | 107 | if (my_dashboard->getTaskStatus() != TaskStatus::PAUSED) { 108 | std::cout << "Task not pause" << std::endl; 109 | return 1; 110 | } else { 111 | std::cout << "Task pause" << std::endl; 112 | } 113 | 114 | if (!my_dashboard->stopProgram()) { 115 | std::cout << "Could not stop task" << std::endl; 116 | return 1; 117 | } else { 118 | std::cout << "Stop task" << std::endl; 119 | } 120 | 121 | if (my_dashboard->getTaskStatus() != TaskStatus::STOPPED) { 122 | std::cout << "Task not stop" << std::endl; 123 | return 1; 124 | } else { 125 | std::cout << "Task stopped" << std::endl; 126 | } 127 | 128 | 129 | if (!my_dashboard->isTaskSaved()) { 130 | std::cout << "Task save status not right" << std::endl; 131 | return 1; 132 | } else { 133 | std::cout << "Task saved" << std::endl; 134 | } 135 | 136 | my_dashboard->disconnect(); 137 | 138 | return 0; 139 | } -------------------------------------------------------------------------------- /example/primary_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std::chrono; 10 | 11 | // In a real-world example it would be better to get those values from command line parameters / a 12 | // better configuration system such as Boost.Program_options 13 | const std::string DEFAULT_ROBOT_IP = "192.168.51.244"; 14 | 15 | int main(int argc, const char **argv) { 16 | // Parse the ip arguments if given 17 | std::string robot_ip = DEFAULT_ROBOT_IP; 18 | if (argc > 1) { 19 | robot_ip = std::string(argv[1]); 20 | } 21 | 22 | auto primary = std::make_unique(); 23 | 24 | auto kin = std::make_shared(); 25 | 26 | primary->connect(robot_ip, 30001); 27 | 28 | primary->getPackage(kin, 200); 29 | 30 | std::cout << "DH parameter a: "; 31 | for (auto i : kin->dh_a_) { 32 | std::cout << i << "\t"; 33 | } 34 | std::cout << std::endl; 35 | 36 | std::cout << "DH parameter d: "; 37 | for (auto i : kin->dh_d_) { 38 | std::cout << i << "\t"; 39 | } 40 | std::cout << std::endl; 41 | 42 | std::cout << "DH parameter alpha: "; 43 | for (auto i : kin->dh_alpha_) { 44 | std::cout << i << "\t"; 45 | } 46 | std::cout << std::endl; 47 | 48 | std::string script = "def hello():\n\ttextmsg(\"hello world\")\nend\n"; 49 | 50 | primary->sendScript(script); 51 | 52 | std::this_thread::sleep_for(1s); 53 | 54 | primary->disconnect(); 55 | 56 | return 0; 57 | } 58 | 59 | 60 | -------------------------------------------------------------------------------- /example/resource/input_recipe.txt: -------------------------------------------------------------------------------- 1 | speed_slider_mask 2 | speed_slider_fraction 3 | standard_digital_output_mask 4 | standard_digital_output 5 | configurable_digital_output_mask 6 | configurable_digital_output 7 | tool_digital_output_mask 8 | tool_digital_output 9 | standard_analog_output_mask 10 | standard_analog_output_type 11 | standard_analog_output_0 12 | standard_analog_output_1 13 | external_force_torque 14 | input_bit_registers0_to_31 15 | input_bit_registers32_to_63 -------------------------------------------------------------------------------- /example/resource/output_recipe.txt: -------------------------------------------------------------------------------- 1 | timestamp 2 | payload_mass 3 | payload_cog 4 | script_control_line 5 | target_joint_positions 6 | target_joint_speeds 7 | actual_joint_torques 8 | actual_joint_positions 9 | actual_joint_speeds 10 | actual_joint_current 11 | actual_TCP_pose 12 | actual_TCP_speed 13 | actual_TCP_force 14 | target_TCP_pose 15 | target_TCP_speed 16 | actual_digital_input_bits 17 | actual_digital_output_bits 18 | joint_temperatures 19 | robot_mode 20 | joint_mode 21 | safety_status 22 | speed_scaling 23 | target_speed_fraction 24 | actual_robot_voltage 25 | actual_robot_current 26 | runtime_state 27 | elbow_position 28 | robot_status_bits 29 | safety_status_bits 30 | analog_io_types 31 | standard_analog_input0 32 | standard_analog_input1 33 | standard_analog_output0 34 | standard_analog_output1 35 | io_current 36 | tool_mode 37 | tool_analog_input_types 38 | tool_analog_output_types 39 | tool_analog_input 40 | tool_analog_output 41 | tool_output_voltage 42 | tool_output_current 43 | tool_temperature 44 | tool_digital_mode 45 | tool_digital0_mode 46 | tool_digital1_mode 47 | tool_digital2_mode 48 | tool_digital3_mode 49 | output_bit_registers0_to_31 50 | output_bit_registers32_to_63 51 | input_bit_registers0_to_31 52 | input_bit_registers32_to_63 -------------------------------------------------------------------------------- /example/rtsi_example.cpp: -------------------------------------------------------------------------------- 1 | #include "Elite/RtsiIOInterface.hpp" 2 | #include 3 | #include 4 | 5 | using namespace ELITE; 6 | 7 | // In a real-world example it would be better to get those values from command line parameters / a 8 | // better configuration system such as Boost.Program_options 9 | const std::string DEFAULT_ROBOT_IP = "192.168.51.244"; 10 | 11 | int main(int argc, char* argv[]) { 12 | // Parse the ip arguments if given 13 | std::string robot_ip = DEFAULT_ROBOT_IP; 14 | if (argc > 1) { 15 | robot_ip = std::string(argv[1]); 16 | } 17 | 18 | std::unique_ptr io_interface = std::make_unique("output_recipe.txt", "input_recipe.txt", 250); 19 | 20 | if(!io_interface->connect(robot_ip)) { 21 | std::cout << "Couldn't connect RTSI server" << std::endl; 22 | return 1; 23 | } 24 | 25 | std::cout << "Controller is: " << io_interface->getControllerVersion().toString() << std::endl; 26 | 27 | if ((io_interface->getDigitalOutputBits() & 0x00000001)) { 28 | auto start_set_false = std::chrono::high_resolution_clock::now(); 29 | io_interface->setStandardDigital(0, false); 30 | while (io_interface->getDigitalOutputBits() | 0x00000000) { 31 | ; 32 | } 33 | auto finish_set_false = std::chrono::high_resolution_clock::now(); 34 | std::chrono::duration elapsed_set_false = finish_set_false - start_set_false; 35 | std::cout << "Setting low level cost time: " << elapsed_set_false.count() << std::endl; 36 | } 37 | 38 | auto start_set_true = std::chrono::high_resolution_clock::now(); 39 | io_interface->setStandardDigital(0, true); 40 | 41 | while (!(io_interface->getDigitalOutputBits() & 0x00000001)) { 42 | ; 43 | } 44 | auto finish_set_true = std::chrono::high_resolution_clock::now(); 45 | 46 | std::chrono::duration elapsed_set_true = finish_set_true - start_set_true; 47 | std::cout << "Setting high level cost time: " << elapsed_set_true.count() << std::endl; 48 | 49 | io_interface->disconnect(); 50 | 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /example/servo_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace ELITE; 12 | using namespace std::chrono; 13 | 14 | static std::unique_ptr s_driver; 15 | static std::unique_ptr s_rtsi_client; 16 | static std::unique_ptr s_dashboard; 17 | 18 | // 定义一个合适的 epsilon 值,这个值可以根据具体情况调整 19 | const double EPSILON = 1e-4; 20 | 21 | bool areAlmostEqual(double a, double b) { 22 | return std::fabs(a - b) < EPSILON; 23 | } 24 | 25 | 26 | bool areAlmostEqual(const vector6d_t& a, const vector6d_t& b) { 27 | for (size_t i = 0; i < a.size(); i++) { 28 | if (!areAlmostEqual(a[i], b[i])) { 29 | return false; 30 | } 31 | } 32 | return true; 33 | } 34 | 35 | template 36 | void printArray(T& l) { 37 | for (auto& i : l) { 38 | std::cout << i << " "; 39 | } 40 | std::cout << std::endl; 41 | } 42 | 43 | void waitRobotArrive(std::vector> recipe_list, const ELITE::vector6d_t& target) { 44 | vector6d_t acutal_joint; 45 | while (true) { 46 | if(s_rtsi_client->receiveData(recipe_list) > 0) { 47 | if(!recipe_list[0]->getValue("actual_joint_positions", acutal_joint)) { 48 | std::cout << "Recipe has wrong" << std::endl; 49 | continue; 50 | } 51 | if (areAlmostEqual(acutal_joint, target)) { 52 | break; 53 | } else { 54 | if(!s_driver->writeServoj(target, 100)) { 55 | return; 56 | } 57 | } 58 | } else { 59 | std::cout << "Couldn't receive data" << std::endl; 60 | } 61 | } 62 | } 63 | 64 | int main(int argc, char** argv) { 65 | if (argc < 3) { 66 | std::cout << "Must provide robot ip or local ip. Command like: ./servo_example 192.168.1.250 192.168.1.251" << std::endl; 67 | return 1; 68 | } 69 | s_driver = std::make_unique(argv[1], argv[2], "external_control.script"); 70 | s_rtsi_client = std::make_unique(); 71 | s_dashboard = std::make_unique(); 72 | 73 | if (!s_dashboard->connect(argv[1])) { 74 | return 1; 75 | } 76 | std::cout << "Dashboard connected" << std::endl; 77 | 78 | s_rtsi_client->connect(argv[1]); 79 | std::cout << "RTSI connected" << std::endl; 80 | 81 | if (!s_rtsi_client->negotiateProtocolVersion()) { 82 | return 1; 83 | } 84 | 85 | std::cout << "Controller version is " << s_rtsi_client->getControllerVersion().toString() << std::endl; 86 | 87 | auto recipe = s_rtsi_client->setupOutputRecipe({"actual_joint_positions"}, 125); 88 | if (!recipe) { 89 | return 1; 90 | } 91 | 92 | if(!s_dashboard->powerOn()) { 93 | return 1; 94 | } 95 | std::cout << "Robot power on" << std::endl; 96 | 97 | if (!s_dashboard->brakeRelease()) { 98 | return 1; 99 | } 100 | std::cout << "Robot brake released" << std::endl; 101 | 102 | if (!s_dashboard->playProgram()) { 103 | return 1; 104 | } 105 | std::cout << "Program run" << std::endl; 106 | 107 | while (!s_driver->isRobotConnected()) { 108 | std::this_thread::sleep_for(10ms); 109 | } 110 | 111 | // wait robot 112 | std::this_thread::sleep_for(1s); 113 | 114 | if (!s_rtsi_client->start()) { 115 | return 1; 116 | } 117 | std::vector> recipe_list{recipe}; 118 | vector6d_t acutal_joint; 119 | 120 | if(s_rtsi_client->receiveData(recipe_list) <= 0) { 121 | std::cout << "RTSI recipe receive none" << std::endl; 122 | return 1; 123 | } 124 | 125 | if(!recipe_list[0]->getValue("actual_joint_positions", acutal_joint)) { 126 | std::cout << "Recipe has wrong" << std::endl; 127 | return 1; 128 | } 129 | 130 | if(!s_rtsi_client->pause()) { 131 | return 1; 132 | } 133 | 134 | // Make target points 135 | std::vector target_positions_1; 136 | vector6d_t target_joint = acutal_joint; 137 | 138 | if (acutal_joint[5] <= 3) { 139 | for (double target = acutal_joint[5]; target < 3; target += 0.001) { 140 | target_joint[5] = target; 141 | target_positions_1.push_back(target_joint); 142 | } 143 | } else { 144 | for (double target = acutal_joint[5]; target > 3; target -= 0.001) { 145 | target_joint[5] = target; 146 | target_positions_1.push_back(target_joint); 147 | } 148 | } 149 | 150 | std::vector target_positions_2; 151 | for (double target = (*(target_positions_1.end() - 1))[5]; target > -3; target -= 0.002) { 152 | target_joint[5] = target; 153 | target_positions_2.push_back(target_joint); 154 | } 155 | 156 | for (auto& target : target_positions_1) { 157 | if(!s_driver->writeServoj(target, 100)) { 158 | return 1; 159 | } 160 | std::this_thread::sleep_for(4ms); 161 | } 162 | 163 | if (!s_rtsi_client->start()) { 164 | return 1; 165 | } 166 | 167 | waitRobotArrive(recipe_list, *(target_positions_1.end() - 1)); 168 | 169 | if(!s_rtsi_client->pause()) { 170 | return 1; 171 | } 172 | 173 | for (auto& target : target_positions_2) { 174 | if(!s_driver->writeServoj(target, 100)) { 175 | return 1; 176 | } 177 | std::this_thread::sleep_for(4ms); 178 | } 179 | 180 | if (!s_rtsi_client->start()) { 181 | return 1; 182 | } 183 | 184 | waitRobotArrive(recipe_list, *(target_positions_2.end() - 1)); 185 | 186 | s_driver->stopControl(); 187 | 188 | return 0; 189 | } 190 | -------------------------------------------------------------------------------- /example/speedj_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | using namespace ELITE; 10 | 11 | static std::unique_ptr s_driver; 12 | static std::unique_ptr s_dashboard; 13 | 14 | int main(int argc, char** argv) { 15 | if (argc < 3) { 16 | std::cout << "Must provide robot ip or local ip. Command like: ./speedj_example 192.168.1.250 192.168.1.251" << std::endl; 17 | return 1; 18 | } 19 | s_driver = std::make_unique(argv[1], argv[2], "external_control.script"); 20 | s_dashboard = std::make_unique(); 21 | 22 | if (!s_dashboard->connect(argv[1])) { 23 | return 1; 24 | } 25 | std::cout << "Dashboard connected" << std::endl; 26 | 27 | if(!s_dashboard->powerOn()) { 28 | return 1; 29 | } 30 | std::cout << "Robot power on" << std::endl; 31 | 32 | if (!s_dashboard->brakeRelease()) { 33 | return 1; 34 | } 35 | std::cout << "Robot brake released" << std::endl; 36 | 37 | if (!s_dashboard->playProgram()) { 38 | return 1; 39 | } 40 | std::cout << "Program run" << std::endl; 41 | 42 | while (!s_driver->isRobotConnected()) { 43 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 44 | } 45 | 46 | vector6d_t speedl_vector{0, 0, 0, 0, 0, -0.02}; 47 | while (true) { 48 | speedl_vector = {0, 0, 0, 0, 0, -0.02}; 49 | s_driver->writeSpeedj(speedl_vector, 0); 50 | 51 | std::this_thread::sleep_for(std::chrono::seconds(3)); 52 | 53 | speedl_vector = {0, 0, 0, 0, 0, 0.02}; 54 | s_driver->writeSpeedj(speedl_vector, 0); 55 | std::this_thread::sleep_for(std::chrono::seconds(3)); 56 | } 57 | 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /example/speedl_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | using namespace ELITE; 10 | 11 | static std::unique_ptr s_driver; 12 | static std::unique_ptr s_dashboard; 13 | 14 | int main(int argc, char** argv) { 15 | if (argc < 3) { 16 | std::cout << "Must provide robot ip or local ip. Command like: ./speedl_example 192.168.1.250 192.168.1.251" << std::endl; 17 | return 1; 18 | } 19 | s_driver = std::make_unique(argv[1], argv[2], "external_control.script"); 20 | s_dashboard = std::make_unique(); 21 | 22 | if (!s_dashboard->connect(argv[1])) { 23 | return 1; 24 | } 25 | std::cout << "Dashboard connected" << std::endl; 26 | 27 | if(!s_dashboard->powerOn()) { 28 | return 1; 29 | } 30 | std::cout << "Robot power on" << std::endl; 31 | 32 | if (!s_dashboard->brakeRelease()) { 33 | return 1; 34 | } 35 | std::cout << "Robot brake released" << std::endl; 36 | 37 | if (!s_dashboard->playProgram()) { 38 | return 1; 39 | } 40 | std::cout << "Program run" << std::endl; 41 | 42 | while (!s_driver->isRobotConnected()) { 43 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 44 | } 45 | 46 | vector6d_t speedl_vector{0, 0, -0.02, 0, 0, 0}; 47 | while (true) { 48 | speedl_vector = {0, 0, -0.02, 0, 0, 0}; 49 | s_driver->writeSpeedl(speedl_vector, 0); 50 | 51 | std::this_thread::sleep_for(std::chrono::seconds(3)); 52 | 53 | speedl_vector = {0, 0, 0.02, 0, 0, 0}; 54 | s_driver->writeSpeedl(speedl_vector, 0); 55 | std::this_thread::sleep_for(std::chrono::seconds(3)); 56 | } 57 | 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /example/trajectory_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace ELITE; 11 | 12 | static std::unique_ptr s_driver; 13 | static std::unique_ptr s_rtsi_client; 14 | static std::unique_ptr s_dashboard; 15 | 16 | int main(int argc, const char** argv) { 17 | if (argc < 3) { 18 | std::cout << "Must provide robot ip or local ip. Command like: ./trajectory_example 192.168.1.250 192.168.1.251" << std::endl; 19 | return 1; 20 | } 21 | s_driver = std::make_unique(argv[1], argv[2], "external_control.script"); 22 | s_rtsi_client = std::make_unique("output_recipe.txt", "input_recipe.txt", 250); 23 | s_dashboard = std::make_unique(); 24 | 25 | if (!s_dashboard->connect(argv[1])) { 26 | return 1; 27 | } 28 | std::cout << "Dashboard connected" << std::endl; 29 | 30 | s_rtsi_client->connect(argv[1]); 31 | std::cout << "RTSI connected" << std::endl; 32 | 33 | bool is_move_finish = false; 34 | s_driver->setTrajectoryResultCallback([&](TrajectoryMotionResult result){ 35 | if (result == TrajectoryMotionResult::SUCCESS) { 36 | is_move_finish = true; 37 | } 38 | }); 39 | 40 | if(!s_dashboard->powerOn()) { 41 | return 1; 42 | } 43 | std::cout << "Robot power on" << std::endl; 44 | 45 | if (!s_dashboard->brakeRelease()) { 46 | return 1; 47 | } 48 | std::cout << "Robot brake released" << std::endl; 49 | 50 | if (!s_dashboard->playProgram()) { 51 | return 1; 52 | } 53 | std::cout << "Program run" << std::endl; 54 | 55 | while (!s_driver->isRobotConnected()) { 56 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 57 | } 58 | 59 | vector6d_t actual_joints = s_rtsi_client->getActualJointPositions(); 60 | 61 | s_driver->writeTrajectoryControlAction(ELITE::TrajectoryControlAction::START, 1, 200); 62 | 63 | actual_joints[3] = -1.57; 64 | s_driver->writeTrajectoryPoint(actual_joints, 3, 0, false); 65 | 66 | while (!is_move_finish) { 67 | s_driver->writeTrajectoryControlAction(ELITE::TrajectoryControlAction::NOOP, 0, 200); 68 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 69 | } 70 | std::cout << "Joints moved to target" << std::endl; 71 | 72 | vector6d_t actual_pose = s_rtsi_client->getAcutalTCPPose(); 73 | 74 | is_move_finish = false; 75 | 76 | s_driver->writeTrajectoryControlAction(ELITE::TrajectoryControlAction::START, 3, 200); 77 | 78 | actual_pose[2] -= 0.2; 79 | s_driver->writeTrajectoryPoint(actual_pose, 3, 0.05, true); 80 | 81 | actual_pose[1] -= 0.2; 82 | s_driver->writeTrajectoryPoint(actual_pose, 3, 0.05, true); 83 | 84 | actual_pose[1] += 0.2; 85 | actual_pose[2] += 0.2; 86 | s_driver->writeTrajectoryPoint(actual_pose, 3, 0.05, true); 87 | 88 | while (!is_move_finish) { 89 | s_driver->writeTrajectoryControlAction(ELITE::TrajectoryControlAction::NOOP, 0, 200); 90 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 91 | } 92 | std::cout << "Line moved to target" << std::endl; 93 | 94 | s_driver->writeTrajectoryControlAction(ELITE::TrajectoryControlAction::CANCEL, 1, 200); 95 | s_driver->stopControl(); 96 | return 0; 97 | } -------------------------------------------------------------------------------- /include/Common/SshUtils.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __ELITE__SSH_UTILS_HPP__ 2 | #define __ELITE__SSH_UTILS_HPP__ 3 | 4 | #include 5 | #include 6 | 7 | namespace ELITE { 8 | 9 | namespace SSH_UTILS { 10 | /** 11 | * @brief Log in to the server via SSH, execute commands, and return the output 12 | * of the commands. 13 | * 14 | * @param host SSH server IP 15 | * @param user user name 16 | * @param password user password 17 | * @param cmd Want execute commands 18 | * @return std::string The result of the command. 19 | */ 20 | std::string executeCommand(const std::string &host, const std::string &user, 21 | const std::string &password, const std::string &cmd); 22 | 23 | /** 24 | * @brief Download files via SCP. 25 | * 26 | * @param server SSH server IP 27 | * @param user User name 28 | * @param password User password 29 | * @param remote_path Remote file path 30 | * @param local_path Save path (the file name needs to be included). 31 | * @param progress_cb Download progress callback function. 32 | * f_z: File size. 33 | * r_z: Downloaded size. 34 | * err: Error information (nullptr when there is no error) 35 | * @return true sucess 36 | * @return false fail 37 | */ 38 | bool downloadFile( 39 | const std::string &server, const std::string &user, 40 | const std::string &password, const std::string &remote_path, 41 | const std::string &local_path, 42 | std::function progress_cb); 43 | 44 | /** 45 | * @brief Download file via SCP 46 | * 47 | * @param server SSH server IP 48 | * @param user User name 49 | * @param password User password 50 | * @param remote_path Remote file path (the file name needs to be included). 51 | * @param local_path Save path 52 | * @param progress_cb Download progress callback function. 53 | * f_z: File size. 54 | * w_z: Uploaded size. 55 | * err: Error information (nullptr when there is no error) 56 | * @return true sucess 57 | * @return false fail 58 | */ 59 | bool uploadFile(const std::string& server, const std::string& user, 60 | const std::string& password, const std::string& remote_path, 61 | const std::string& local_path, 62 | std::function progress_cb); 63 | 64 | } // namespace SSH_UTILS 65 | 66 | } // namespace ELITE 67 | 68 | #endif -------------------------------------------------------------------------------- /include/Common/TcpServer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __TCP_SERVER_HPP__ 2 | #define __TCP_SERVER_HPP__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace ELITE 10 | { 11 | 12 | class TcpServer { 13 | private: 14 | boost::asio::io_context io_context_; 15 | bool loop_keep_alive_; 16 | int port_; 17 | std::unique_ptr server_thread_; 18 | std::function)> new_connect_function_; 19 | 20 | /** 21 | * @brief TCP server loop. Run boost library async interface. 22 | * 23 | */ 24 | void serverLoop(); 25 | 26 | /** 27 | * @brief Accept new connection is connected 28 | * 29 | * @param acceptor 30 | */ 31 | void doAccept(boost::asio::ip::tcp::acceptor &acceptor); 32 | 33 | public: 34 | TcpServer() = delete; 35 | 36 | /** 37 | * @brief Construct a new Tcp Server object 38 | * 39 | * @param port Server port 40 | */ 41 | TcpServer(int port); 42 | 43 | /** 44 | * @brief Destroy the Tcp Server object. Will join the serverLoop() finish. 45 | * 46 | */ 47 | ~TcpServer(); 48 | 49 | /** 50 | * @brief Set callback function when new connection in. 51 | * 52 | * @param func 53 | */ 54 | void setConnectCallback(std::function)> func) { 55 | new_connect_function_ = func; 56 | } 57 | 58 | /** 59 | * @brief Close the client socket and reset the pointer. 60 | * 61 | * @param client client pointer. 62 | */ 63 | void releaseClient(std::shared_ptr client); 64 | 65 | }; 66 | 67 | 68 | 69 | } // namespace ELITE 70 | 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /include/Control/ControlCommon.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __CONTROL_COMMON_HPP__ 2 | #define __CONTROL_COMMON_HPP__ 3 | 4 | 5 | namespace ELITE 6 | { 7 | 8 | namespace CONTROL 9 | { 10 | 11 | static const int POS_ZOOM_RATIO = 1000000; 12 | static const int COMMON_ZOOM_RATIO = 1000000; 13 | static const int TIME_ZOOM_RATIO = 1000; 14 | 15 | } // namespace CONTROL 16 | 17 | } // namespace ELITE 18 | 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /include/Control/ReverseInterface.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __REVERSE_INTERFACE_HPP__ 2 | #define __REVERSE_INTERFACE_HPP__ 3 | 4 | #include "TcpServer.hpp" 5 | #include "ControlMode.hpp" 6 | #include "DataType.hpp" 7 | 8 | #include 9 | #include 10 | 11 | namespace ELITE 12 | { 13 | 14 | /** 15 | * @brief The ReverseInterface class handles communication to the robot. 16 | * It starts a server and waits for the robot to connect via its EliteRobot program. 17 | * 18 | * 19 | */ 20 | class ReverseInterface 21 | { 22 | private: 23 | int port_; 24 | std::unique_ptr server_; 25 | std::shared_ptr client_; 26 | std::mutex client_mutex_; 27 | 28 | /** 29 | * @brief Not real read data. Check connection state. 30 | * 31 | */ 32 | void asyncRead(); 33 | 34 | /** 35 | * @brief Write buffer to socket. 36 | * 37 | * @return int data 38 | */ 39 | int write(int32_t buffer[], int size); 40 | 41 | public: 42 | static const int REVERSE_DATA_SIZE = 8; 43 | 44 | ReverseInterface() = delete; 45 | 46 | /** 47 | * @brief Construct a new Reverse Interface object include TcpServer 48 | * 49 | * @param port Port the Server is started on 50 | */ 51 | ReverseInterface(int port); 52 | 53 | /** 54 | * @brief Destroy the Reverse Interface object. Will disconnect robot connection. 55 | * 56 | */ 57 | ~ReverseInterface(); 58 | 59 | /** 60 | * @brief Writes needed information to the robot to be read by the EliteRobot program. 61 | * 62 | * @param pos 63 | * @param mode 64 | * @param timeout_ms 65 | * @return true 66 | * @return false 67 | */ 68 | bool writeJointCommand(const vector6d_t& pos, ControlMode mode, int timeout_ms); 69 | bool writeJointCommand(const vector6d_t* pos, ControlMode mode, int timeout_ms); 70 | 71 | /** 72 | * @brief Writes needed information to the robot to be read by the EliteRobot program. 73 | * 74 | * @param action Trajectory action assigned to this command. See documentation of TrajectoryControlAction for details on possible values. 75 | * @param point_number The number of points of the trajectory to be executed 76 | * @param timeout_ms The read timeout configuration for the reverse socket running in the external control script on the robot. 77 | * @return true success 78 | * @return false fail 79 | */ 80 | bool writeTrajectoryControlAction(TrajectoryControlAction action, const int point_number, int timeout_ms); 81 | 82 | /** 83 | * @brief Finish external control script. 84 | * 85 | * @return true success 86 | * @return false fail 87 | */ 88 | bool stopControl(); 89 | 90 | /** 91 | * @brief Is robot connect to server. 92 | * 93 | * @return true connected 94 | * @return false don't 95 | */ 96 | bool isRobotConnect(); 97 | 98 | }; 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | } // namespace ELITE 107 | 108 | 109 | 110 | #endif -------------------------------------------------------------------------------- /include/Control/ScriptCommandInterface.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __SCRIPT_COMMAND_INTERFACE_HPP__ 2 | #define __SCRIPT_COMMAND_INTERFACE_HPP__ 3 | 4 | #include "TcpServer.hpp" 5 | #include "DataType.hpp" 6 | 7 | #include 8 | #include 9 | 10 | namespace ELITE 11 | { 12 | 13 | class ScriptCommandInterface 14 | { 15 | private: 16 | enum class Cmd{ 17 | ZERO_FTSENSOR = 0, 18 | SET_PAYLOAD = 1, 19 | SET_TOOL_VOLTAGE = 2, 20 | START_FORCE_MODE = 3, 21 | END_FORCE_MODE = 4, 22 | }; 23 | 24 | std::unique_ptr server_; 25 | std::shared_ptr client_; 26 | std::mutex client_mutex_; 27 | 28 | /** 29 | * @brief Send socket data to client 30 | * 31 | * @param buffer data buffer 32 | * @param size buffer size 33 | * @return int 34 | */ 35 | int write(int32_t buffer[], int size); 36 | 37 | /** 38 | * @brief Not real read data. Check connection state. 39 | * 40 | */ 41 | void asyncRead(); 42 | 43 | public: 44 | static constexpr int SCRIPT_COMMAND_DATA_SIZE = 26; 45 | 46 | ScriptCommandInterface() = delete; 47 | 48 | /** 49 | * @brief Construct a new Script Command Interface object 50 | * 51 | * @param port Server port 52 | */ 53 | ScriptCommandInterface(int port); 54 | 55 | ~ScriptCommandInterface(); 56 | 57 | /** 58 | * @brief zero the force/torque applied to the TCP measured by the sensor(tare sensor). 59 | * 60 | * @return true 61 | * @return false 62 | */ 63 | bool zeroFTSensor(); 64 | 65 | /** 66 | * @brief This command is used to set the mass, 67 | * center of gravity and moment of inertia of the robot payload 68 | * 69 | * @param mass The mass of the payload 70 | * @param cog The coordinates of the center of gravity of the payload (relative to the flange frame). 71 | * @return true success 72 | * @return false fail 73 | */ 74 | bool setPayload(double mass, const vector3d_t& cog); 75 | 76 | /** 77 | * @brief Set the tool voltage 78 | * 79 | * @param vol Tool voltage 80 | * @return true success 81 | * @return false fail 82 | */ 83 | bool setToolVoltage(const ToolVoltage& vol); 84 | 85 | /** 86 | * @brief This command is used to enable force control mode and the robot will be controlled in the force control mode. 87 | * 88 | * @param reference_frame A pose vector that defines the force reference frame relative to the base frame. 89 | * The format is [X,Y,Z,Rx,Ry,Rz], where X, Y, and Z represent position with the unit of m, Rx, Ry, and RZ 90 | * represent pose with the unit of rad which is defined by standard Euler angles. 91 | * @param selection_vector a 6-dimensional vector consisting of 0 and 1 that defines the compliant axis in the force frame. 92 | * 1 represents the axis is compliant and 0 represents the axis is non compliant. 93 | * @param wrench The force/torque applied to the environment by the robot. 94 | * The robot moves/rotates along the compliant axis to adjust its pose to achieve the target force/torque. 95 | * The format is [Fx,Fy,Fz,Mx,My,Mz], where Fx, Fy, and Fz represent the force applied along the 96 | * compliant axis with the unit of N, Mx, My, and Mz represent the torque applied about the 97 | * compliant axis with the unit of Nm. This value is invalid for the non-compliant axis. Due to the 98 | * safety restrictions of joints, the actual applied force/torque is lower than the set one. In the 99 | * separate thread, the command get_tcp_force may be used to read the actual force/torque applied to the environment. 100 | * @param mode The parameter for force control mode 101 | * @param limits The parameter for the speed limit. The format is [Vx,Vy,Vz,ωx,ωy,ωz], 102 | * where Vx, Vy, and Vz represent the maximum speed for TCP along 103 | * the compliant axis with the unit of m/s, ωx, ωy, and ωz represent the maximum speed for TCP 104 | * about the compliant axis with the unit of rad/s. This parameter is invalid for the non-compliant 105 | * axis whose trajectory will be as set before. 106 | * @return true success 107 | * @return false fail 108 | */ 109 | bool startForceMode(const vector6d_t& task_frame, const vector6int32_t& selection_vector, 110 | const vector6d_t& wrench, const ForceMode& mode, const vector6d_t& limits); 111 | 112 | /** 113 | * @brief This command is used to disable the force control mode. It also will be performed when the procedure ends. 114 | * 115 | * @return true success 116 | * @return false fail 117 | */ 118 | bool endForceMode(); 119 | 120 | /** 121 | * @brief Is robot connect to server. 122 | * 123 | * @return true connected 124 | * @return false don't 125 | */ 126 | bool isRobotConnect(); 127 | 128 | }; 129 | 130 | 131 | 132 | 133 | 134 | } // namespace ELITE 135 | 136 | 137 | #endif -------------------------------------------------------------------------------- /include/Control/ScriptSender.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __SCRIPT_SENDER_HPP__ 2 | #define __SCRIPT_SENDER_HPP__ 3 | 4 | #include "TcpServer.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | namespace ELITE 11 | { 12 | 13 | class ScriptSender { 14 | private: 15 | const std::string PROGRAM_REQUEST_ = std::string("request_program"); 16 | std::unique_ptr server_; 17 | const std::string& program_; 18 | std::shared_ptr client_; 19 | boost::asio::streambuf recv_request_buffer_; 20 | 21 | void responseRequest(); 22 | 23 | public: 24 | ScriptSender(int port, const std::string& program); 25 | ~ScriptSender(); 26 | }; 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | } // namespace ELITE 36 | 37 | 38 | 39 | 40 | 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /include/Control/TrajectoryInterface.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __TRAJECTORY_INTERFACE_HPP__ 2 | #define __TRAJECTORY_INTERFACE_HPP__ 3 | 4 | #include "TcpServer.hpp" 5 | #include "DataType.hpp" 6 | #include 7 | #include 8 | 9 | namespace ELITE 10 | { 11 | 12 | enum class TrajectoryMotionType : int 13 | { 14 | JOINT = 0, // movej 15 | CARTESIAN = 1, // movel 16 | SPLINE = 2 // spline 17 | }; 18 | 19 | class TrajectoryInterface 20 | { 21 | public: 22 | static const int TRAJECTORY_MESSAGE_LEN = 21; 23 | 24 | TrajectoryInterface() = delete; 25 | 26 | /** 27 | * @brief Construct a new Trajectory Interface object include TcpServer 28 | * 29 | * @param port Port the Server is started on 30 | */ 31 | TrajectoryInterface(int port); 32 | 33 | ~TrajectoryInterface(); 34 | 35 | /** 36 | * @brief Register a callback for the robot-based trajectory execution completion. 37 | * 38 | * One mode of robot control is to forward a complete trajectory to the robot for execution. 39 | * When the execution is done, the callback function registered here will be triggered. 40 | * 41 | * @param cb Callback function that will be triggered in the event of finishing 42 | */ 43 | void setMotionResultCallback(std::function cb) { 44 | motion_result_func_ = cb; 45 | } 46 | 47 | /** 48 | * @brief Writes a trajectory point onto the dedicated socket. 49 | * 50 | * @param positions Desired joint or cartesian positions 51 | * @param time Time for the robot to reach this point 52 | * @param blend_radius The radius to be used for blending between control points 53 | * @param cartesian True, if the point sent is cartesian, false if joint-based 54 | * @return true 55 | * @return false 56 | */ 57 | bool writeTrajectoryPoint(const vector6d_t& positions, float time, float blend_radius, bool cartesian); 58 | 59 | /** 60 | * @brief Is robot connect to server. 61 | * 62 | * @return true connected 63 | * @return false don't 64 | */ 65 | bool isRobotConnect(); 66 | 67 | private: 68 | std::unique_ptr server_; 69 | std::shared_ptr client_; 70 | std::function motion_result_func_; 71 | std::mutex client_mutex_; 72 | TrajectoryMotionResult motion_result_; 73 | 74 | int write(int32_t buffer[], int size); 75 | void receiveResult(); 76 | }; 77 | 78 | 79 | 80 | 81 | } // namespace ELITE 82 | 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /include/Elite/ControlMode.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __CONTROL_MODE_HPP__ 2 | #define __CONTROL_MODE_HPP__ 3 | 4 | 5 | namespace ELITE 6 | { 7 | 8 | 9 | enum class ControlMode : int { 10 | MODE_STOPPED = -2, // When this is set, the program is expected to stop and exit. 11 | MODE_UNINITIALIZED = -1, // Startup default until another mode is sent to the script. 12 | MODE_IDLE = 0, // Set when no controller is currently active controlling the robot. 13 | MODE_SERVOJ = 1, // Set when servoj control is active. 14 | MODE_SPEEDJ = 2, // Set when speedj control is active. 15 | MODE_TRAJECTORY = 3, // Set when trajectory forwarding is active. 16 | MODE_SPEEDL = 4, // Set when cartesian velocity control is active. 17 | MODE_POSE = 5, // Set when cartesian pose control is active.(Not use now, coming soon) 18 | MODE_FREEDRIVE = 6, // Set when freedrive mode is active.(Not use now, coming soon) 19 | MODE_TOOL_IN_CONTACT = 7 // Set tool in contact.(Not use now, coming soon) 20 | }; 21 | 22 | 23 | } // namespace ELITE 24 | 25 | 26 | 27 | 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /include/Elite/ControllerLog.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ___ELITE_CONTROLLER_LOG_HPP__ 2 | #define ___ELITE_CONTROLLER_LOG_HPP__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace ELITE { 9 | class ControllerLog { 10 | private: 11 | public: 12 | /** 13 | * @brief Download system log from robot 14 | * 15 | * @param robot_ip Robot ip address 16 | * @param password Robot ssh password 17 | * @param path Save path 18 | * @param progress_cb Download progress callback function. 19 | * f_z: File size. 20 | * r_z: Downloaded size. 21 | * err: Error information (nullptr when there is no error) 22 | * @return true success 23 | * @return false fail 24 | * 1. On Linux, if `libssh` is not installed, you need to ensure that the computer running the SDK has the `scp`, `ssh`, 25 | * and `sshpass` commands available. 26 | * 2. In Windows, if libssh is not installed, then this interface will not be available. 27 | */ 28 | ELITE_EXPORT static bool downloadSystemLog(const std::string &robot_ip, const std::string &password, const std::string &path, 29 | std::function progress_cb); 30 | ControllerLog() {} 31 | ~ControllerLog() {} 32 | }; 33 | 34 | } // namespace ELITE 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /include/Elite/DataType.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file DataType.hpp 3 | * @author yanxiaojia 4 | * @brief The data type of sdk 5 | * @date 2024-08-21 6 | * 7 | * @copyright Copyright (c) 2024 8 | * 9 | */ 10 | #ifndef __DATA_TYPE_HPP__ 11 | #define __DATA_TYPE_HPP__ 12 | 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #if (ELITE_SDK_COMPILE_STANDARD >= 17) 19 | #include 20 | #elif (ELITE_SDK_COMPILE_STANDARD == 14) 21 | #include 22 | #endif 23 | 24 | namespace ELITE { 25 | 26 | enum class RobotMode : int32_t { 27 | UNKNOWN = -2, 28 | NO_CONTROLLER = -1, 29 | DISCONNECTED = 0, 30 | CONFIRM_SAFETY = 1, 31 | BOOTING = 2, 32 | POWER_OFF = 3, 33 | POWER_ON = 4, 34 | IDLE = 5, 35 | BACKDRIVE = 6, 36 | RUNNING = 7, 37 | UPDATING_FIRMWARE = 8, 38 | WAITING_CALIBRATION = 9, 39 | }; 40 | 41 | enum class JointMode : int32_t { 42 | MODE_RESET = 235, 43 | MODE_SHUTTING_DOWN = 236, 44 | MODE_BACKDRIVE = 238, 45 | MODE_POWER_OFF = 239, 46 | MODE_READY_FOR_POWEROFF = 240, 47 | MODE_NOT_RESPONDING = 245, 48 | MODE_MOTOR_INITIALISATION = 246, 49 | MODE_BOOTING = 247, 50 | MODE_BOOTLOADER = 249, 51 | MODE_VIOLATION = 251, 52 | MODE_FAULT = 252, 53 | MODE_RUNNING = 253, 54 | MODE_IDLE = 255 55 | }; 56 | 57 | enum class SafetyMode : int32_t { 58 | UNKNOWN = -2, 59 | NORMAL = 1, 60 | REDUCED = 2, 61 | PROTECTIVE_STOP = 3, 62 | RECOVERY = 4, 63 | SAFEGUARD_STOP = 5, 64 | SYSTEM_EMERGENCY_STOP = 6, 65 | ROBOT_EMERGENCY_STOP = 7, 66 | VIOLATION = 8, 67 | FAULT = 9, 68 | VALIDATE_JOINT_ID = 10, 69 | UNDEFINED_SAFETY_MODE = 11, 70 | AUTOMATIC_MODE_SAFEGUARD_STOP = 12, 71 | SYSTEM_THREE_POSITION_ENABLING_STOP = 13, 72 | TP_THREE_POSITION_ENABLING_STOP = 14, 73 | }; 74 | 75 | enum class ToolMode : uint32_t { 76 | MODE_RESET = 235, 77 | MODE_SHUTTING_DOWN = 236, 78 | MODE_POWER_OFF = 239, 79 | MODE_NOT_RESPONDING = 245, 80 | MODE_BOOTING = 247, 81 | MODE_BOOTLOADER = 249, 82 | MODE_FAULT = 252, 83 | MODE_RUNNING = 253, 84 | MODE_IDLE = 255 85 | }; 86 | 87 | enum class ToolDigitalMode : uint8_t { 88 | /// All available 89 | SINGLE_NEEDLE, 90 | /// 0 and 1 are both available 91 | DOUBLE_NEEDLE_1, 92 | /// 2 and 3 are both available 93 | DOUBLE_NEEDLE_2, 94 | /// All not available 95 | TRIPLE_NEEDLE 96 | }; 97 | 98 | enum class ToolDigitalOutputMode : uint8_t { 99 | /// push/pull mode 100 | PUSH_PULL_MODE = 0, 101 | /// PNP 102 | SOURCING_PNP_MODE = 1, 103 | /// NPN 104 | SINKING_NPN_MODE = 2 105 | }; 106 | 107 | enum class TaskStatus { UNKNOWN, PLAYING, PAUSED, STOPPED }; 108 | 109 | enum class TrajectoryMotionResult : int { 110 | /// Successful execution 111 | SUCCESS = 0, 112 | /// Canceled by user 113 | CANCELED = 1, 114 | /// Aborted due to error during execution 115 | FAILURE = 2 116 | }; 117 | 118 | enum class TrajectoryControlAction : int { 119 | /// Represents command to cancel currently active trajectory. 120 | CANCEL = -1, 121 | /// Represents no new control command. 122 | NOOP = 0, 123 | /// Represents command to start a new trajectory. 124 | START = 1, 125 | }; 126 | 127 | enum class ToolVoltage : int { 128 | OFF = 0, // 0V 129 | V_12 = 12, // 12V 130 | V_24 = 24 // 24V 131 | }; 132 | 133 | enum class ForceMode : int { 134 | /// The force frame is the force reference frame. 135 | FIX, 136 | /// The Y-axis in the force frame points from the TCP 137 | /// origin of the robot to the origin of the force reference frame. 138 | POINT, 139 | /// The X-axis in the force frame is the projection of the TCP motion direction 140 | /// vector in the X-Y plane belonging to the force reference frame. 141 | MOTION, 142 | /// The force frame is the TCP frame. 143 | TCP, 144 | }; 145 | 146 | using vector3d_t = std::array; 147 | using vector6d_t = std::array; 148 | using vector6int32_t = std::array; 149 | using vector6uint32_t = std::array; 150 | #if (ELITE_SDK_COMPILE_STANDARD >= 17) 151 | using RtsiTypeVariant = std::variant; 153 | #elif (ELITE_SDK_COMPILE_STANDARD == 14) 154 | using RtsiTypeVariant = boost::variant; 156 | #endif 157 | 158 | } // namespace ELITE 159 | 160 | #endif -------------------------------------------------------------------------------- /include/Elite/DefaultLogHandler.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __ELITE__DEFATULT_LOG_HPP__ 2 | #define __ELITE__DEFATULT_LOG_HPP__ 3 | 4 | #include "Log.hpp" 5 | #include 6 | 7 | namespace ELITE 8 | { 9 | 10 | class DefaultLogHandler : public LogHandler 11 | { 12 | private: 13 | 14 | public: 15 | DefaultLogHandler() = default; 16 | ~DefaultLogHandler() = default; 17 | 18 | void log(const char* file, int line, LogLevel level, const char* log) { 19 | switch (level) { 20 | case LogLevel::ELI_DEBUG: 21 | std::cout << "[DEBUG] " << file << ":" << line << ": " << log << std::endl; 22 | break; 23 | case LogLevel::ELI_INFO: 24 | std::cout << "[INFO] " << file << ":" << line << ": " << log << std::endl; 25 | break; 26 | case LogLevel::ELI_WARN: 27 | std::cout << "[WARN] " << file << ":" << line << ": " << log << std::endl; 28 | break; 29 | case LogLevel::ELI_ERROR: 30 | std::cout << "[ERROR] " << file << ":" << line << ": " << log << std::endl; 31 | break; 32 | case LogLevel::ELI_FATAL: 33 | std::cout << "[FATAL] " << file << ":" << line << ": " << log << std::endl; 34 | break; 35 | case LogLevel::ELI_NONE: 36 | std::cout << "[NONE] " << file << ":" << line << ": " << log << std::endl; 37 | break; 38 | default: 39 | break; 40 | } 41 | } 42 | 43 | }; 44 | 45 | 46 | } // namespace ELITE 47 | 48 | 49 | 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /include/Elite/Log.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Log.hpp 3 | * @author yanxiaojia 4 | * @brief Settings related to the logging function in Elite_Robots_CS_SDK. 5 | * @date 2024-08-21 6 | * 7 | * @copyright Copyright (c) 2024 8 | * 9 | */ 10 | #ifndef __ELITE__LOG_HPP__ 11 | #define __ELITE__LOG_HPP__ 12 | 13 | #include 14 | #include 15 | 16 | #ifndef __REL_FILE__ 17 | #define __REL_FILE__ __FILE__ 18 | #endif 19 | 20 | #define ELITE_LOG_DEBUG(...) ELITE::log(__REL_FILE__, __LINE__, ELITE::LogLevel::ELI_DEBUG, __VA_ARGS__) 21 | #define ELITE_LOG_WARN(...) ELITE::log(__REL_FILE__, __LINE__, ELITE::LogLevel::ELI_WARN, __VA_ARGS__) 22 | #define ELITE_LOG_INFO(...) ELITE::log(__REL_FILE__, __LINE__, ELITE::LogLevel::ELI_INFO, __VA_ARGS__) 23 | #define ELITE_LOG_ERROR(...) ELITE::log(__REL_FILE__, __LINE__, ELITE::LogLevel::ELI_ERROR, __VA_ARGS__) 24 | #define ELITE_LOG_FATAL(...) ELITE::log(__REL_FILE__, __LINE__, ELITE::LogLevel::ELI_FATAL, __VA_ARGS__) 25 | 26 | namespace ELITE 27 | { 28 | 29 | /** 30 | * @brief The log level 31 | * 32 | */ 33 | enum class LogLevel { 34 | ELI_DEBUG, 35 | ELI_INFO, 36 | ELI_WARN, 37 | ELI_ERROR, 38 | ELI_FATAL, 39 | ELI_NONE 40 | }; 41 | 42 | /** 43 | * @brief If you want to change the way you log, 44 | * you can inherit from this class and register the instance through the registerLogHandler() function 45 | * 46 | */ 47 | class LogHandler { 48 | private: 49 | public: 50 | LogHandler() = default; 51 | virtual ~LogHandler() = default; 52 | 53 | /** 54 | * @brief Function to log a message 55 | * 56 | * @param file The log message comes from this file 57 | * @param line The log message comes from this line 58 | * @param loglevel The log level 59 | * @param log Log message 60 | */ 61 | ELITE_EXPORT virtual void log(const char* file, int line, LogLevel loglevel, const char* log) = 0; 62 | 63 | }; 64 | 65 | /** 66 | * @brief Register a new LogHandler object for processing log messages. 67 | * 68 | * @param hanlder Pointer to the new object 69 | */ 70 | ELITE_EXPORT void registerLogHandler(std::unique_ptr hanlder); 71 | 72 | /** 73 | * @brief Unregister current log handler, this will enable default log handler. 74 | * 75 | */ 76 | ELITE_EXPORT void unregisterLogHandler(); 77 | 78 | /** 79 | * @brief Log a message, this is used internally by the macros to unpack the log message. 80 | * Should use the macros instead of this function directly. 81 | * 82 | * @param file The log message comes from this file 83 | * @param line The log message comes from this line 84 | * @param level Level of the log message 85 | * @param fmt Format string 86 | */ 87 | ELITE_EXPORT void log(const char* file, int line, LogLevel level, const char* fmt, ...); 88 | 89 | /** 90 | * @brief Set log level this will disable messages with lower log level. 91 | * 92 | * @param level Desired log level 93 | */ 94 | ELITE_EXPORT void setLogLevel(LogLevel level); 95 | 96 | } // namespace ELITE 97 | 98 | 99 | #endif -------------------------------------------------------------------------------- /include/Elite/Logger.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __ELITE__LOGGER_HPP__ 2 | #define __ELITE__LOGGER_HPP__ 3 | 4 | #include "Log.hpp" 5 | #include "DefaultLogHandler.hpp" 6 | 7 | 8 | namespace ELITE 9 | { 10 | 11 | class Logger { 12 | private: 13 | LogLevel level_; 14 | std::unique_ptr handler_; 15 | 16 | public: 17 | Logger() { 18 | level_ = LogLevel::ELI_INFO; 19 | handler_.reset(new DefaultLogHandler); 20 | }; 21 | 22 | ~Logger() = default; 23 | 24 | void setLevel(LogLevel level) { 25 | level_ = level; 26 | } 27 | 28 | void registerHandler(std::unique_ptr& handler) { 29 | handler_ = std::move(handler); 30 | } 31 | 32 | void unregisterHandler() { 33 | handler_.reset(new DefaultLogHandler); 34 | } 35 | 36 | void log(const char* file, int line, LogLevel level, const char* log) { 37 | if (!handler_) { 38 | handler_.reset(new DefaultLogHandler()); 39 | } 40 | handler_->log(file, line, level, log); 41 | } 42 | 43 | LogLevel getLogLevel() { 44 | return level_; 45 | } 46 | 47 | }; 48 | 49 | Logger& getLogger(); 50 | 51 | 52 | } // namespace ELITE 53 | 54 | 55 | 56 | 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /include/Elite/RemoteUpgrade.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __ELITE__REMOTE_UPGRADE_HPP__ 2 | #define __ELITE__REMOTE_UPGRADE_HPP__ 3 | 4 | #include 5 | #include 6 | 7 | namespace ELITE { 8 | 9 | namespace UPGRADE { 10 | /** 11 | * @brief Upgrade the robot control software 12 | * 13 | * @param ip Robot ip 14 | * @param file Upgrade file 15 | * @param password Robot controller ssh password 16 | * @return true success 17 | * @return false fail 18 | * @note 19 | * 1. On Linux, if `libssh` is not installed, you need to ensure that the computer running the SDK has the `scp`, `ssh`, and 20 | * `sshpass` commands available. 21 | * 2. In Windows, if libssh is not installed, then this interface will not be available. 22 | */ 23 | ELITE_EXPORT bool upgradeControlSoftware(std::string ip, std::string file, std::string password); 24 | 25 | } // namespace UPGRADE 26 | } // namespace ELITE 27 | 28 | #endif -------------------------------------------------------------------------------- /include/Elite/VersionInfo.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file VersionInfo.hpp 3 | * @author yanxiaojia 4 | * @brief Containing a version information 5 | * @date 2024-08-21 6 | * 7 | * @copyright Copyright (c) 2024 8 | * 9 | */ 10 | #ifndef __VERSION_INFO_HPP__ 11 | #define __VERSION_INFO_HPP__ 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | namespace ELITE 19 | { 20 | 21 | // In some compilers, there may be macro definitions for major and minor. 22 | #ifdef major 23 | #undef major 24 | #endif 25 | #ifdef minor 26 | #undef minor 27 | #endif 28 | 29 | /** 30 | * @brief Struct containing a Elite version information 31 | * 32 | */ 33 | class VersionInfo { 34 | public: 35 | /** 36 | * @brief Construct a constant for version information. 37 | * 38 | * @param ma major 39 | * @param mi minor 40 | * @param bug bugfix 41 | * @param bui build 42 | */ 43 | constexpr VersionInfo(int ma, int mi, int bug, int bui) : 44 | major(ma), minor(mi), bugfix(bug), build(bui) { }; 45 | 46 | /** 47 | * @brief Construct a new Version Info object 48 | * 49 | * @param version The format string of version infomation 50 | */ 51 | ELITE_EXPORT explicit VersionInfo(const std::string& version); 52 | 53 | VersionInfo() = default; 54 | 55 | ~VersionInfo() = default; 56 | 57 | /** 58 | * @brief Convert version information to a string. 59 | * 60 | * @return std::string The string of version: major.minor.bugfix.build 61 | */ 62 | ELITE_EXPORT std::string toString() const; 63 | 64 | /** 65 | * @brief Parses a version string into a VersionInformation object 66 | * 67 | * @param str Version string 68 | * @return VersionInfo Version information 69 | */ 70 | ELITE_EXPORT static VersionInfo fromString(const std::string& str); 71 | 72 | ELITE_EXPORT VersionInfo& operator=(const VersionInfo&); 73 | ELITE_EXPORT bool operator==(const VersionInfo& v) const; 74 | ELITE_EXPORT bool operator!=(const VersionInfo& v) const; 75 | ELITE_EXPORT bool operator>(const VersionInfo& v) const; 76 | ELITE_EXPORT bool operator>=(const VersionInfo& v) const; 77 | ELITE_EXPORT bool operator<(const VersionInfo& v) const; 78 | ELITE_EXPORT bool operator<=(const VersionInfo& v) const; 79 | 80 | ELITE_EXPORT constexpr bool operator==(VersionInfo& v) const; 81 | ELITE_EXPORT constexpr bool operator!=(VersionInfo& v) const; 82 | ELITE_EXPORT constexpr bool operator>(VersionInfo& v) const; 83 | ELITE_EXPORT constexpr bool operator>=(VersionInfo& v) const; 84 | ELITE_EXPORT constexpr bool operator<(VersionInfo& v) const; 85 | ELITE_EXPORT constexpr bool operator<=(VersionInfo& v) const; 86 | 87 | uint32_t major = 0; 88 | uint32_t minor = 0; 89 | uint32_t bugfix = 0; 90 | uint32_t build = 0; 91 | }; 92 | 93 | /** 94 | * @brief Get the SDK version info 95 | */ 96 | constexpr VersionInfo SDK_VERSION_INFO(ELITE_SDK_VERSION_MAJOR, ELITE_SDK_VERSION_MINOR, ELITE_SDK_VERSION_BUGFIX, 0); 97 | 98 | } // namespace ELITE 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /include/EliteException.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file EliteException.hpp 3 | * @author yanxiaojia 4 | * @brief Exception of the sdk 5 | * @date 2024-08-21 6 | * 7 | * @copyright Copyright (c) 2024 8 | * 9 | */ 10 | #ifndef __ELITE_EXCEPTION_HPP__ 11 | #define __ELITE_EXCEPTION_HPP__ 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | namespace ELITE { 18 | 19 | /** 20 | * @brief Exception of the sdk 21 | * 22 | */ 23 | class EliteException : virtual public std::runtime_error { 24 | public: 25 | /** 26 | * @brief The exception code 27 | * 28 | */ 29 | enum class Code { 30 | /// success 31 | SUCCESS, 32 | /// socket operation cancelled. 33 | SOCKET_OPT_CANCEL, 34 | /// connect fail 35 | SOCKET_CONNECT_FAIL, 36 | /// socket communicate error 37 | SOCKET_FAIL, 38 | /// RTSI receive unknown data type, 39 | /// maybe "NOT_FOUND" or "IN_USE" 40 | RTSI_UNKNOW_VARIABLE_TYPE, 41 | /// RTSI recipe parser fail 42 | RTSI_RECIPE_PARSER_FAIL, 43 | /// illegal parameter 44 | ILLEGAL_PARAM, 45 | /// dashboard did not receive the expected response 46 | DASHBOARD_NOT_EXPECT_RECIVE, 47 | /// open file fail 48 | FILE_OPEN_FAIL, 49 | }; 50 | 51 | EliteException() = delete; 52 | 53 | /** 54 | * @brief Construct a new Elite Exception object 55 | * 56 | * @param code The exception code 57 | * @param addition addition message 58 | */ 59 | explicit EliteException(Code code, const std::string& addition) 60 | : std::runtime_error(std::string(exceptionCodeToString(code)) + ": " + addition), 61 | exception_code_(code) { } 62 | 63 | /** 64 | * @brief Construct a new Elite Exception object 65 | * 66 | * @param code The exception code 67 | */ 68 | explicit EliteException(Code code) : std::runtime_error(exceptionCodeToString(code)), exception_code_(code) { } 69 | 70 | virtual ~EliteException() { } 71 | 72 | bool operator==(Code code) const { 73 | return exception_code_ == code; 74 | } 75 | 76 | operator bool() const { 77 | return exception_code_ != Code::SUCCESS; 78 | } 79 | 80 | const char* exceptionCodeToString(const Code& ec); 81 | 82 | private: 83 | std::string exceptionStringDeal(const std::string& what) { 84 | std::string exception_string = what; 85 | exceptionStringReplace(&exception_string, "\n", "\\n"); 86 | exceptionStringReplace(&exception_string, "\r", "\\r"); 87 | return exception_string; 88 | } 89 | void exceptionStringReplace(std::string* exception_string, const char* target, const char* replace) { 90 | size_t found = exception_string->find(target); 91 | while (found != std::string::npos) { 92 | exception_string->replace(found, 1, replace); 93 | found = exception_string->find(target, found); 94 | } 95 | } 96 | 97 | Code exception_code_; 98 | }; 99 | 100 | } // namespace ELITE 101 | 102 | #endif 103 | -------------------------------------------------------------------------------- /include/EliteOptions.hpp.in: -------------------------------------------------------------------------------- 1 | #ifndef __ELITE_OPTIONS_HPP__ 2 | #define __ELITE_OPTIONS_HPP__ 3 | 4 | #if defined(_WIN32) || defined(_WIN64) 5 | #ifdef ELITE_EXPORT_LIBRARY 6 | #define ELITE_EXPORT __declspec(dllexport) 7 | #else 8 | #define ELITE_EXPORT __declspec(dllimport) 9 | #endif 10 | #elif defined(__linux) || defined(linux) || defined(__linux__) 11 | #define ELITE_EXPORT __attribute__((visibility("default"))) 12 | #endif 13 | 14 | #define ELITE_SDK_COMPILE_STANDARD @ELITE_SDK_COMPILE_STANDARD@ 15 | 16 | #define ELITE_SDK_VERSION "@elite-cs-series-sdk_VERSION@" 17 | #define ELITE_SDK_VERSION_MAJOR (@elite-cs-series-sdk_VERSION_MAJOR@) 18 | #define ELITE_SDK_VERSION_MINOR (@elite-cs-series-sdk_VERSION_MINOR@) 19 | #define ELITE_SDK_VERSION_BUGFIX (@elite-cs-series-sdk_VERSION_PATCH@) 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /include/Primary/PrimaryPackage.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file PrimaryPackage.hpp 3 | * @author yanxiaojia 4 | * @brief About primary port package 5 | * @date 2024-08-21 6 | * 7 | * @copyright Copyright (c) 2024 8 | * 9 | */ 10 | #ifndef __ELITE__PRIMARY_PACKAGE_HPP__ 11 | #define __ELITE__PRIMARY_PACKAGE_HPP__ 12 | 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | namespace ELITE 22 | { 23 | 24 | /** 25 | * @brief Inherit this class to obtain the data of the primary port. 26 | * 27 | */ 28 | class PrimaryPackage { 29 | private: 30 | int type_; 31 | std::mutex cv_mutex_; 32 | std::condition_variable cv_; 33 | public: 34 | PrimaryPackage() = delete; 35 | 36 | /** 37 | * @brief Construct a new Primary Package object 38 | * 39 | * @param type The sub-package type 40 | */ 41 | explicit PrimaryPackage(int type) : type_(type) { } 42 | virtual ~PrimaryPackage() = default; 43 | 44 | /** 45 | * @brief Parser sub-package. Internal use. 46 | * 47 | * @param len The len of sub-package 48 | * @param iter Position of the sub-package in the entire package 49 | */ 50 | ELITE_EXPORT virtual void parser(int len, const std::vector::const_iterator& iter) = 0; 51 | 52 | /** 53 | * @brief Waiting for packet data update. Invoked inside the getPackage() function. 54 | * 55 | * @param timeout_ms 56 | * @return true 57 | * @return false 58 | */ 59 | bool waitUpdate(int timeout_ms) { 60 | std::unique_lock lock(cv_mutex_); 61 | std::cv_status sta = cv_.wait_for(lock, std::chrono::milliseconds(timeout_ms)); 62 | return sta == std::cv_status::no_timeout; 63 | } 64 | 65 | /** 66 | * @brief Notify the data has been updated. 67 | * This function is called when the data of this package is updated by a background thread. 68 | * 69 | */ 70 | void notifyUpated() { 71 | std::unique_lock lock(cv_mutex_); 72 | cv_.notify_all(); 73 | } 74 | 75 | /** 76 | * @brief Get the sub-package type 77 | * 78 | * @return int Sub-package type 79 | */ 80 | int getType() { return type_; } 81 | }; 82 | 83 | 84 | } // namespace ELITE 85 | 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /include/Primary/PrimaryPort.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __ELITE__PRIMARY_PORT_HPP__ 2 | #define __ELITE__PRIMARY_PORT_HPP__ 3 | 4 | #include "PrimaryPackage.hpp" 5 | #include "DataType.hpp" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace ELITE 17 | { 18 | 19 | class PrimaryPort 20 | { 21 | private: 22 | // The primary port package head length 23 | static constexpr int HEAD_LENGTH = 5; 24 | // The type of 'RobotState' package 25 | static constexpr int ROBOT_STATE_MSG_TYPE = 16; 26 | 27 | std::mutex socket_mutex_; 28 | boost::asio::io_context io_context_; 29 | std::unique_ptr socket_ptr_; 30 | 31 | // The buffer of package head 32 | std::vector message_head_; 33 | // The buffer of package body 34 | std::vector message_body_; 35 | 36 | // When getPackage() be called, insert a sub-package data to get 37 | std::unordered_map> parser_sub_msg_; 38 | std::unique_ptr socket_async_thread_; 39 | std::mutex mutex_; 40 | bool socket_async_thread_alive_; 41 | 42 | /** 43 | * @brief The background thread. 44 | * Receive and parser package. 45 | */ 46 | void socketAsyncLoop(); 47 | 48 | /** 49 | * @brief Receive and parser package. 50 | * 51 | */ 52 | bool parserMessage(); 53 | 54 | /** 55 | * @brief Receive and parser package body. 56 | * Only parser 'RobotState' package 57 | * @param type 58 | * @param len 59 | */ 60 | bool parserMessageBody(int type, int package_len); 61 | 62 | public: 63 | PrimaryPort(); 64 | ~PrimaryPort(); 65 | 66 | /** 67 | * @brief Connect to robot primary port. 68 | * And spawn a background thread for message receiving and parsing. 69 | * @param ip The robot ip 70 | * @param port The port(30001 or 30002) 71 | * @return true success 72 | * @return false fail 73 | * @note 74 | * 1. Warning: Repeated calls to this function without intermediate disconnect() will force-close the active connection. 75 | * 2. Usage constraint: Call rate must be ≤ 2Hz (once per 500ms minimum interval). 76 | */ 77 | bool connect(const std::string& ip, int port); 78 | 79 | /** 80 | * @brief Disconnect socket. 81 | * And wait for the background thread to finish. 82 | * @note After calling this function, a delay of around 500ms should be added prior to calling connect(). 83 | */ 84 | void disconnect(); 85 | 86 | /** 87 | * @brief Sends a custom script program to the robot. 88 | * 89 | * @param script Script code that shall be executed by the robot. 90 | * @return true success 91 | * @return false fail 92 | */ 93 | bool sendScript(const std::string& script); 94 | 95 | /** 96 | * @brief Get primary sub-package data. 97 | * 98 | * @param pkg Primary sub-package. 99 | * @param timeout_ms Wait time 100 | * @return true success 101 | * @return false fail 102 | */ 103 | bool getPackage(std::shared_ptr pkg, int timeout_ms); 104 | 105 | }; 106 | 107 | } // namespace ELITE 108 | 109 | #endif -------------------------------------------------------------------------------- /include/Primary/PrimaryPortInterface.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file PrimaryPortInterface.hpp 3 | * @author yanxiaojia 4 | * @brief Robot primary port interface 5 | * @date 2024-08-21 6 | * 7 | * @copyright Copyright (c) 2024 8 | * 9 | */ 10 | #ifndef __ELITE__PRIMARY_PORT_INTERFACE_HPP__ 11 | #define __ELITE__PRIMARY_PORT_INTERFACE_HPP__ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace ELITE 19 | { 20 | 21 | /** 22 | * @brief Robot primary port interface 23 | * 24 | */ 25 | class PrimaryPortInterface 26 | { 27 | private: 28 | class Impl; 29 | std::unique_ptr impl_; 30 | public: 31 | static constexpr int PRIMARY_PORT = 30001; 32 | 33 | ELITE_EXPORT PrimaryPortInterface(); 34 | ELITE_EXPORT ~PrimaryPortInterface(); 35 | 36 | /** 37 | * @brief Connect to robot primary port. 38 | * And spawn a background thread for message receiving and parsing. 39 | * @param ip The robot ip 40 | * @param port The port(30001) 41 | * @return true success 42 | * @return false fail 43 | */ 44 | ELITE_EXPORT bool connect(const std::string& ip, int port = PRIMARY_PORT); 45 | 46 | /** 47 | * @brief Disconnect socket. 48 | * And wait for the background thread to finish. 49 | */ 50 | ELITE_EXPORT void disconnect(); 51 | 52 | /** 53 | * @brief Sends a custom script program to the robot. 54 | * 55 | * @param script Script code that shall be executed by the robot. 56 | * @return true success 57 | * @return false fail 58 | */ 59 | ELITE_EXPORT bool sendScript(const std::string& script); 60 | 61 | /** 62 | * @brief Get primary sub-package data. 63 | * 64 | * @param pkg Primary sub-package. 65 | * @param timeout_ms Wait time 66 | * @return true success 67 | * @return false fail 68 | */ 69 | ELITE_EXPORT bool getPackage(std::shared_ptr pkg, int timeout_ms); 70 | 71 | }; 72 | 73 | } // namespace ELITE 74 | 75 | 76 | #endif -------------------------------------------------------------------------------- /include/Primary/RobotConfPackage.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file RobotConfPackage.hpp 3 | * @author yanxiaojia 4 | * @brief The RobotConfig message in the robot's primary port 5 | * @date 2024-08-21 6 | * 7 | * @copyright Copyright (c) 2024 8 | * 9 | */ 10 | #ifndef __ELITE__ROBOT_CONFIGURE_PACKAGE_HPP__ 11 | #define __ELITE__ROBOT_CONFIGURE_PACKAGE_HPP__ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace ELITE 19 | { 20 | 21 | /** 22 | * @brief The RobotConfig message in the robot's primary port 23 | * 24 | */ 25 | class RobotConfPackage : public PrimaryPackage { 26 | private: 27 | /// The Robot configure sub-package type 28 | static constexpr int ROBOT_CONFIG_PKG_TYPE = 6; 29 | public: 30 | /** 31 | * @brief Construct a new Robot Conf Package object 32 | * 33 | */ 34 | ELITE_EXPORT RobotConfPackage() : PrimaryPackage(ROBOT_CONFIG_PKG_TYPE) { } 35 | ELITE_EXPORT virtual ~RobotConfPackage() = default; 36 | }; 37 | 38 | /** 39 | * @brief The robot kinematics infomation in RobotConfig message 40 | * 41 | */ 42 | class KinematicsInfo : public RobotConfPackage { 43 | private: 44 | // In version 2.11.0 of robot controller, the DH parameters are located at a specific offset relative to the sub-header of the message. 45 | static constexpr int DH_PARAM_OFFSET = sizeof(uint32_t) + sizeof(uint8_t) + sizeof(double) * 2 * 6 + sizeof(double) * 2 * 6 + sizeof(double) * 5; 46 | public: 47 | ELITE_EXPORT KinematicsInfo() = default; 48 | ELITE_EXPORT ~KinematicsInfo() = default; 49 | 50 | vector6d_t dh_a_; 51 | vector6d_t dh_d_; 52 | vector6d_t dh_alpha_; 53 | 54 | /** 55 | * @brief Parser message from robot. Internal use. 56 | * 57 | * @param len The len of sub-package 58 | * @param iter Position of the sub-package in the entire package 59 | */ 60 | ELITE_EXPORT void parser(int len, const std::vector::const_iterator& iter); 61 | }; 62 | 63 | 64 | } // namespace ELITE 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /include/Rtsi/RtsiClient.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __RTSICLIENT_HPP__ 2 | #define __RTSICLIENT_HPP__ 3 | 4 | #include "RtsiRecipe.hpp" 5 | #include "VersionInfo.hpp" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | namespace ELITE { 16 | 17 | /** 18 | * @brief 19 | * Rtsi client 20 | * 21 | */ 22 | class RtsiClient 23 | { 24 | public: 25 | static constexpr uint16_t DEFAULT_PROTOCOL_VERSION = 1; 26 | 27 | RtsiClient() = default; 28 | virtual ~RtsiClient() = default; 29 | 30 | /** 31 | * @brief Connect to robot RTSI server 32 | * 33 | * @param ip The robot IP 34 | * @param port RTSI port 35 | */ 36 | void connect(const std::string& ip, int port = 30004); 37 | 38 | /** 39 | * @brief Disconnect 40 | * 41 | */ 42 | void disconnect(); 43 | 44 | /** 45 | * @brief Verify the protocol version. 46 | * 47 | * @param version The version of RTSI 48 | * @return bool true if successful 49 | */ 50 | bool negotiateProtocolVersion(uint16_t version = DEFAULT_PROTOCOL_VERSION); 51 | 52 | /** 53 | * @brief Get the Controller Version object 54 | * 55 | * @return std::tuple 56 | * A tuple type where the data is, in order, major version, minor version, bugfix, and build. 57 | */ 58 | VersionInfo getControllerVersion(); 59 | 60 | /** 61 | * @brief Subscribe to output variables. 62 | * 63 | * @param recipe The list of recipe. The variable names are explained in the document. 64 | * @param frequency Setup output frenqucy 65 | * @return RtsiRecipeSharedPtr The data recipe 66 | */ 67 | RtsiRecipeSharedPtr setupOutputRecipe(const std::vector& recipe_list, double frequency = 250); 68 | 69 | /** 70 | * @brief Subscribe to input variables. 71 | * 72 | * @param recipe The list of recipe. The variable names are explained in the document. 73 | * @return RtsiRecipeSharedPtr The data recipe 74 | */ 75 | RtsiRecipeSharedPtr setupInputRecipe(const std::vector& recipe); 76 | 77 | /** 78 | * @brief Send start signal to server 79 | * 80 | * @return true Start successfully 81 | * @return false Start fail 82 | */ 83 | bool start(); 84 | 85 | /** 86 | * @brief Send pause signal to server 87 | * 88 | * @return true Pause successfully 89 | * @return false Pause fail 90 | */ 91 | bool pause(); 92 | 93 | /** 94 | * @brief Send an recipe to controller 95 | * 96 | * @param recipe The recipe sent to the controller. 97 | */ 98 | void send(RtsiRecipeSharedPtr& recipe); 99 | 100 | /** 101 | * @brief Receive RTSI output recipes data 102 | * 103 | * @param recipes The recipe you want to receive. Note that only one recipe will be received. 104 | * @param read_newest If want to parser the newest message 105 | * @return int The ID of recipe which is received. If -1, not match recipe 106 | */ 107 | int receiveData(std::vector& recipes, bool read_newest = false); 108 | 109 | /** 110 | * @brief Receive RTSI output recipe data 111 | * 112 | * @param recipe The recipe you want to receive. 113 | * @param read_newest If want to parser the newest message 114 | * @return true success 115 | * @return false false 116 | */ 117 | bool receiveData(RtsiRecipeSharedPtr recipe, bool read_newest = false); 118 | 119 | /** 120 | * @brief Get connection state 121 | * 122 | * @return true connected 123 | * @return false disconnect 124 | */ 125 | bool isConnected(); 126 | 127 | /** 128 | * @brief Is start to sync robot data 129 | * 130 | * @return true started 131 | * @return false not started 132 | */ 133 | bool isStarted(); 134 | 135 | /** 136 | * @brief This function is used to determine have bytes that may be read without blocking. 137 | * 138 | * @return true has bytes 139 | * @return false don't has 140 | */ 141 | bool isReadAvailable(); 142 | 143 | private: 144 | enum class PackageType : uint8_t; 145 | 146 | boost::asio::io_context io_context_; 147 | std::unique_ptr socket_ptr_; 148 | std::unique_ptr resolver_ptr_; 149 | 150 | enum ConnectionState { 151 | DISCONNECTED, 152 | CONNECTED, 153 | STARTED, 154 | STOPED 155 | }; 156 | ConnectionState connection_state; 157 | 158 | /** 159 | * @brief Rtsi package type 160 | * 161 | */ 162 | enum class PackageType : uint8_t { 163 | REQUEST_PROTOCOL_VERSION = 86, // ascii V 164 | GET_ELITE_CONTROL_VERSION = 118, // ascii v 165 | TEXT_MESSAGE = 77, // ascii M 166 | DATA_PACKAGE = 85, // ascii U 167 | CONTROL_PACKAGE_SETUP_OUTPUTS = 79, // ascii O 168 | CONTROL_PACKAGE_SETUP_INPUTS = 73, // ascii I 169 | CONTROL_PACKAGE_START = 83, // ascii S 170 | CONTROL_PACKAGE_PAUSE = 80 // ascii P 171 | }; 172 | 173 | /** 174 | * @brief Send an package to RTSI server 175 | * 176 | * @param cmd Send package type 177 | * @param payload Package payload 178 | */ 179 | void sendAll(const PackageType& cmd, const std::vector& payload = std::vector()); 180 | 181 | /** 182 | * @brief Receive socket bytes from RTSI server 183 | * 184 | * @param buff Data buffer 185 | * @param size Size of buffer 186 | * @param offset Offset of buffer 187 | * @param timeout_ms Timeout(ms) 188 | * @return int The number of bytes recieved 189 | */ 190 | int receiveSocket(std::vector& buff, int size, int offset, unsigned timeout_ms = 1000); 191 | 192 | /** 193 | * @brief Loop receive util target package come 194 | * 195 | * @param target_type Target package type 196 | * @param parser_func When receive target type, will call the parser function 197 | * @param read_newest If want to parser the newest message 198 | */ 199 | void receive(const PackageType& target_type, 200 | std::function&)> parser_func, 201 | bool read_newest = false); 202 | 203 | /** 204 | * @brief Close socket connection 205 | * 206 | */ 207 | void socketDisconnect(); 208 | }; 209 | 210 | } 211 | 212 | #endif 213 | -------------------------------------------------------------------------------- /include/Rtsi/RtsiClientInterface.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file RtsiClientInterface.hpp 3 | * @author yanxiaojia 4 | * @brief The RTSI client interface 5 | * @date 2024-08-21 6 | * 7 | * @copyright Copyright (c) 2024 8 | * 9 | */ 10 | #ifndef __RTSI_CLIENT_INTERFACE_HPP__ 11 | #define __RTSI_CLIENT_INTERFACE_HPP__ 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | 20 | namespace ELITE { 21 | 22 | /** 23 | * @brief The RTSI client raw interface. 24 | * 25 | */ 26 | class RtsiClientInterface 27 | { 28 | private: 29 | class Impl; 30 | std::unique_ptr impl_; 31 | public: 32 | static constexpr uint16_t DEFAULT_PROTOCOL_VERSION = 1; 33 | 34 | ELITE_EXPORT RtsiClientInterface(); 35 | ELITE_EXPORT virtual ~RtsiClientInterface(); 36 | 37 | /** 38 | * @brief Connect to robot RTSI server 39 | * 40 | * @param ip The robot IP 41 | * @param port RTSI port 42 | */ 43 | ELITE_EXPORT virtual void connect(const std::string& ip, int port = 30004); 44 | 45 | /** 46 | * @brief Disconnect 47 | * 48 | */ 49 | ELITE_EXPORT virtual void disconnect(); 50 | 51 | /** 52 | * @brief Verify the protocol version. 53 | * 54 | * @param version The version of RTSI 55 | * @return bool true if successful 56 | */ 57 | ELITE_EXPORT bool negotiateProtocolVersion(uint16_t version = DEFAULT_PROTOCOL_VERSION); 58 | 59 | /** 60 | * @brief Get the Controller Version object 61 | * 62 | * @return std::tuple 63 | * A tuple type where the data is, in order, major version, minor version, bugfix, and build. 64 | */ 65 | ELITE_EXPORT virtual VersionInfo getControllerVersion(); 66 | 67 | /** 68 | * @brief Subscribe to output variables. 69 | * 70 | * @param recipe_list The list of recipe. The variable names are explained in the document. 71 | * @param frequency Setup output frenqucy 72 | * @return RtsiRecipeSharedPtr The data recipe 73 | */ 74 | ELITE_EXPORT RtsiRecipeSharedPtr setupOutputRecipe(const std::vector& recipe_list, double frequency = 250); 75 | 76 | /** 77 | * @brief Subscribe to input variables. 78 | * 79 | * @param recipe The list of recipe. The variable names are explained in the document. 80 | * @return RtsiRecipeSharedPtr The data recipe 81 | */ 82 | ELITE_EXPORT RtsiRecipeSharedPtr setupInputRecipe(const std::vector& recipe); 83 | 84 | /** 85 | * @brief Send start signal to server 86 | * 87 | * @return true Start successfully 88 | * @return false Start fail 89 | */ 90 | ELITE_EXPORT bool start(); 91 | 92 | /** 93 | * @brief Send pause signal to server 94 | * 95 | * @return true Pause successfully 96 | * @return false Pause fail 97 | */ 98 | ELITE_EXPORT bool pause(); 99 | 100 | /** 101 | * @brief Send an recipe to controller 102 | * 103 | * @param recipe The recipe sent to the controller. 104 | */ 105 | ELITE_EXPORT void send(RtsiRecipeSharedPtr& recipe); 106 | 107 | /** 108 | * @brief Receive RTSI output recipes data 109 | * 110 | * @param recipes The recipe you want to receive. Note that only one recipe will be received. 111 | * @param read_newest If want to parser the newest message 112 | * @return int The ID of recipe which is received. If -1, not match recipe 113 | */ 114 | ELITE_EXPORT int receiveData(std::vector& recipes, bool read_newest = false); 115 | 116 | /** 117 | * @brief Receive RTSI output recipe data 118 | * 119 | * @param recipe The recipe you want to receive. 120 | * @param read_newest If want to parser the newest message 121 | * @return true success 122 | * @return false false 123 | */ 124 | ELITE_EXPORT bool receiveData(RtsiRecipeSharedPtr recipe, bool read_newest = false); 125 | 126 | /** 127 | * @brief Get connection state 128 | * 129 | * @return true connected 130 | * @return false disconnect 131 | */ 132 | ELITE_EXPORT bool isConnected(); 133 | 134 | /** 135 | * @brief Is start to sync robot data 136 | * 137 | * @return true started 138 | * @return false not started 139 | */ 140 | ELITE_EXPORT bool isStarted(); 141 | 142 | /** 143 | * @brief This function is used to determine have bytes that may be read without blocking. 144 | * 145 | * @return true has bytes 146 | * @return false don't has 147 | */ 148 | ELITE_EXPORT bool isReadAvailable(); 149 | 150 | }; 151 | 152 | } 153 | 154 | 155 | 156 | 157 | #endif 158 | -------------------------------------------------------------------------------- /include/Rtsi/RtsiRecipeInternal.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __RTSI_RECIPE_INTERNAL_HPP__ 2 | #define __RTSI_RECIPE_INTERNAL_HPP__ 3 | 4 | #include "RtsiRecipe.hpp" 5 | #include 6 | #include 7 | 8 | namespace ELITE 9 | { 10 | 11 | /** 12 | * @brief 13 | * Rtsi recipe, used internal 14 | * 15 | */ 16 | class RtsiRecipeInternal : public RtsiRecipe 17 | { 18 | private: 19 | 20 | public: 21 | /** 22 | * @brief Create new object 23 | * 24 | */ 25 | RtsiRecipeInternal() = delete; 26 | explicit RtsiRecipeInternal(const std::vector& list); 27 | virtual ~RtsiRecipeInternal() = default; 28 | 29 | /** 30 | * @brief 31 | * Parser package RTSI ack of type list and recipe ID 32 | * When setup input or output recipe, after send the variable name list, RTSI server will ack the type of variables list and recipe ID 33 | * 34 | * @param package_len The package len 35 | * @param package The bytes buffer of package 36 | */ 37 | void parserTypePackage(int package_len, const std::vector& package); 38 | 39 | /** 40 | * @brief 41 | * Parser package of Data. 42 | * When RTSI client send start signal, RTSI server will periodically send data package. 43 | * This function can parser the data package 44 | * 45 | * @param package_len The package len 46 | * @param package The bytes buffer of package 47 | * @return true success 48 | * @return false fail 49 | */ 50 | bool parserDataPackage(int package_len, const std::vector& package); 51 | 52 | /** 53 | * @brief Pack the data in recipe to bytes 54 | * 55 | * @return std::vector The RTSI data package 56 | */ 57 | std::vector packToBytes(); 58 | 59 | }; 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | } // namespace ELITE 68 | 69 | 70 | 71 | 72 | 73 | #endif -------------------------------------------------------------------------------- /source/Common/EliteException.cpp: -------------------------------------------------------------------------------- 1 | #include "EliteException.hpp" 2 | 3 | using namespace ELITE; 4 | 5 | 6 | const char* EliteException::exceptionCodeToString(const Code& ec) { 7 | switch (ec) { 8 | case Code::SUCCESS: 9 | return "success"; 10 | case Code::SOCKET_OPT_CANCEL: 11 | return "socket option cancel"; 12 | case Code::SOCKET_CONNECT_FAIL: 13 | return "socket connect fail"; 14 | case Code::SOCKET_FAIL: 15 | return "socket disconnected"; 16 | case Code::RTSI_UNKNOW_VARIABLE_TYPE: 17 | return "rtsi unknow variable type"; 18 | case Code::RTSI_RECIPE_PARSER_FAIL: 19 | return "rtsi recipe parser fail"; 20 | case Code::ILLEGAL_PARAM: 21 | return "parametric is illegal"; 22 | case Code::DASHBOARD_NOT_EXPECT_RECIVE: 23 | return "dashboard not expect recive"; 24 | default: 25 | return "unknow code"; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /source/Common/TcpServer.cpp: -------------------------------------------------------------------------------- 1 | #include "TcpServer.hpp" 2 | #include "EliteException.hpp" 3 | #include "Log.hpp" 4 | #include 5 | 6 | using namespace ELITE; 7 | 8 | TcpServer::TcpServer(int port) : port_(port) { 9 | loop_keep_alive_ = true; 10 | server_thread_ = std::make_unique([&]() { 11 | serverLoop(); 12 | }); 13 | } 14 | 15 | TcpServer::~TcpServer() { 16 | loop_keep_alive_ = false; 17 | io_context_.stop(); 18 | if (server_thread_->joinable()) { 19 | server_thread_->join(); 20 | } 21 | 22 | } 23 | 24 | void TcpServer::doAccept(boost::asio::ip::tcp::acceptor &acceptor) { 25 | acceptor.async_accept([&](boost::system::error_code ec, boost::asio::ip::tcp::socket client_socket) { 26 | std::shared_ptr client_socket_ptr = std::make_shared(std::move(client_socket)); 27 | client_socket_ptr->set_option(boost::asio::ip::tcp::no_delay(true)); 28 | if (new_connect_function_) { 29 | new_connect_function_(client_socket_ptr); 30 | } 31 | doAccept(acceptor); 32 | }); 33 | } 34 | 35 | 36 | void TcpServer::serverLoop() { 37 | boost::asio::ip::tcp::acceptor acceptor(io_context_, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port_)); 38 | acceptor.listen(1); 39 | if (!acceptor.is_open()) { 40 | throw EliteException(EliteException::Code::SOCKET_FAIL); 41 | } 42 | 43 | while (loop_keep_alive_) { 44 | try { 45 | doAccept(acceptor); 46 | 47 | if(io_context_.stopped()) { 48 | io_context_.restart(); 49 | } 50 | io_context_.run(); 51 | } catch(const boost::system::system_error &error) { 52 | ELITE_LOG_INFO("TCP server %d has error: %s", port_, error.what()); 53 | continue; 54 | } 55 | } 56 | } 57 | 58 | void TcpServer::releaseClient(std::shared_ptr client) { 59 | boost::asio::post(io_context_, [client]() { 60 | client->shutdown(boost::asio::ip::tcp::socket::shutdown_both); 61 | client->close(); 62 | }); 63 | } -------------------------------------------------------------------------------- /source/Common/Utils.cpp: -------------------------------------------------------------------------------- 1 | #include "Utils.hpp" 2 | #include 3 | #include 4 | 5 | using namespace ELITE::UTILS; 6 | 7 | std::vector StringUtils::splitString(const std::string& input, const std::string& delimiter) { 8 | std::vector tokens; 9 | size_t start = 0; 10 | size_t end = input.find(delimiter); 11 | 12 | while (end != std::string::npos) { 13 | tokens.push_back(input.substr(start, end - start)); 14 | start = end + delimiter.length(); 15 | end = input.find(delimiter, start); 16 | } 17 | 18 | tokens.push_back(input.substr(start, std::string::npos)); 19 | 20 | return tokens; 21 | } -------------------------------------------------------------------------------- /source/Control/ReverseInterface.cpp: -------------------------------------------------------------------------------- 1 | #include "ReverseInterface.hpp" 2 | #include "ControlCommon.hpp" 3 | #include "EliteException.hpp" 4 | #include "Log.hpp" 5 | 6 | using namespace ELITE; 7 | 8 | ReverseInterface::ReverseInterface(int port) : port_(port) { 9 | server_ = std::make_unique(port); 10 | server_->setConnectCallback([&](std::shared_ptr client) { 11 | { 12 | std::lock_guard lock(client_mutex_); 13 | if (client_ && client_->is_open()) { 14 | ELITE_LOG_INFO("Reverse interface has new connection, previous connection will be dropped."); 15 | server_->releaseClient(client_); 16 | } 17 | ELITE_LOG_INFO("Reverse interface accept new connection."); 18 | client_ = client; 19 | } 20 | asyncRead(); 21 | }); 22 | } 23 | 24 | ReverseInterface::~ReverseInterface() { 25 | 26 | } 27 | 28 | void ReverseInterface::asyncRead() { 29 | std::lock_guard lock(client_mutex_); 30 | if (!client_) { 31 | return; 32 | } 33 | if (!client_->is_open()) { 34 | client_.reset(); 35 | return; 36 | } 37 | std::shared_ptr no_use; 38 | no_use.reset(new int); 39 | client_->async_read_some(boost::asio::buffer(no_use.get(), sizeof(int)), [&, no_use](boost::system::error_code ec, std::size_t len){ 40 | if (len <= 0 || ec) { 41 | ELITE_LOG_INFO("Connection to reverse interface dropped: %s", boost::system::system_error(ec).what()); 42 | server_->releaseClient(client_); 43 | return; 44 | } else { 45 | asyncRead(); 46 | } 47 | }); 48 | } 49 | 50 | int ReverseInterface::write(int32_t buffer[], int size) { 51 | try { 52 | return client_->write_some(boost::asio::buffer(buffer, size)); 53 | } catch(const boost::system::system_error &error) { 54 | server_->releaseClient(client_); 55 | return -1; 56 | } 57 | } 58 | 59 | bool ReverseInterface::writeJointCommand(const vector6d_t& pos, ControlMode mode, int timeout) { 60 | return writeJointCommand(&pos, mode, timeout); 61 | } 62 | 63 | bool ReverseInterface::writeJointCommand(const vector6d_t* pos, ControlMode mode, int timeout) { 64 | std::lock_guard lock(client_mutex_); 65 | if (!client_) { 66 | return false; 67 | } 68 | int32_t data[REVERSE_DATA_SIZE] = {0}; 69 | data[0] = htonl(timeout); 70 | data[REVERSE_DATA_SIZE - 1] = htonl((int)mode); 71 | if (pos) { 72 | for (size_t i = 0; i < 6; i++) { 73 | data[i + 1] = htonl(static_cast(round((*pos)[i] * CONTROL::POS_ZOOM_RATIO))); 74 | } 75 | } 76 | 77 | return write(data, sizeof(data)) > 0; 78 | } 79 | 80 | bool ReverseInterface::writeTrajectoryControlAction(TrajectoryControlAction action, int point_number, int timeout) { 81 | std::lock_guard lock(client_mutex_); 82 | if (!client_) { 83 | return false; 84 | } 85 | int32_t data[REVERSE_DATA_SIZE] = {0}; 86 | data[0] = htonl(timeout); 87 | data[1] = htonl((int)action); 88 | data[2] = htonl(point_number); 89 | data[REVERSE_DATA_SIZE - 1] = htonl((int)ControlMode::MODE_TRAJECTORY); 90 | return write(data, sizeof(data)) > 0; 91 | } 92 | 93 | bool ReverseInterface::stopControl() { 94 | std::lock_guard lock(client_mutex_); 95 | if (!client_) { 96 | return false; 97 | } 98 | int32_t data[REVERSE_DATA_SIZE]; 99 | data[0] = 0; 100 | data[REVERSE_DATA_SIZE - 1] = htonl((int)ControlMode::MODE_STOPPED); 101 | 102 | return write(data, sizeof(data)) > 0; 103 | } 104 | 105 | bool ReverseInterface::isRobotConnect() { 106 | std::lock_guard lock(client_mutex_); 107 | if (client_) { 108 | return client_->is_open(); 109 | } else { 110 | return false; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /source/Control/ScriptCommandInterface.cpp: -------------------------------------------------------------------------------- 1 | #include "ScriptCommandInterface.hpp" 2 | #include "ControlCommon.hpp" 3 | #include "Log.hpp" 4 | 5 | namespace ELITE 6 | { 7 | 8 | ScriptCommandInterface::ScriptCommandInterface(int port) { 9 | server_.reset(new TcpServer(port)); 10 | 11 | server_->setConnectCallback([&](std::shared_ptr client){ 12 | { 13 | std::lock_guard lock(client_mutex_); 14 | if (client_ && client_->is_open()) { 15 | ELITE_LOG_INFO("Script command has new connection, previous connection will be dropped."); 16 | server_->releaseClient(client_); 17 | } 18 | ELITE_LOG_INFO("Script command accept new connection."); 19 | client_ = client; 20 | } 21 | asyncRead(); 22 | }); 23 | } 24 | 25 | ScriptCommandInterface::~ScriptCommandInterface() { 26 | 27 | } 28 | 29 | void ScriptCommandInterface::asyncRead() { 30 | std::lock_guard lock(client_mutex_); 31 | if (!client_) { 32 | return; 33 | } 34 | if (!client_->is_open()) { 35 | client_.reset(); 36 | return; 37 | } 38 | std::shared_ptr no_use; 39 | no_use.reset(new int); 40 | client_->async_read_some(boost::asio::buffer(no_use.get(), sizeof(int)), [&, no_use](boost::system::error_code ec, std::size_t len){ 41 | if (len <= 0 || ec) { 42 | ELITE_LOG_INFO("Connection to script command interface dropped: %s", boost::system::system_error(ec).what()); 43 | server_->releaseClient(client_); 44 | return; 45 | } else { 46 | asyncRead(); 47 | } 48 | }); 49 | } 50 | 51 | bool ScriptCommandInterface::zeroFTSensor() { 52 | std::lock_guard lock(client_mutex_); 53 | if (!client_) { 54 | return false; 55 | } 56 | int32_t buffer[SCRIPT_COMMAND_DATA_SIZE] = {0}; 57 | buffer[0] = htonl(static_cast(Cmd::ZERO_FTSENSOR)); 58 | return write(buffer, sizeof(buffer)) > 0; 59 | } 60 | 61 | bool ScriptCommandInterface::setPayload(double mass, const vector3d_t& cog) { 62 | std::lock_guard lock(client_mutex_); 63 | if (!client_) { 64 | return false; 65 | } 66 | int32_t buffer[SCRIPT_COMMAND_DATA_SIZE] = {0}; 67 | buffer[0] = htonl(static_cast(Cmd::SET_PAYLOAD)); 68 | buffer[1] = htonl(static_cast((mass * CONTROL::COMMON_ZOOM_RATIO))); 69 | buffer[2] = htonl(static_cast((cog[0] * CONTROL::COMMON_ZOOM_RATIO))); 70 | buffer[3] = htonl(static_cast((cog[1] * CONTROL::COMMON_ZOOM_RATIO))); 71 | buffer[4] = htonl(static_cast((cog[2] * CONTROL::COMMON_ZOOM_RATIO))); 72 | return write(buffer, sizeof(buffer)) > 0; 73 | } 74 | 75 | bool ScriptCommandInterface::setToolVoltage(const ToolVoltage& vol) { 76 | std::lock_guard lock(client_mutex_); 77 | if (!client_) { 78 | return false; 79 | } 80 | int32_t buffer[SCRIPT_COMMAND_DATA_SIZE] = {0}; 81 | buffer[0] = htonl(static_cast(Cmd::SET_TOOL_VOLTAGE)); 82 | buffer[1] = htonl(static_cast(vol) * CONTROL::COMMON_ZOOM_RATIO); 83 | return write(buffer, sizeof(buffer)) > 0; 84 | } 85 | 86 | bool ScriptCommandInterface::startForceMode(const vector6d_t& task_frame, 87 | const vector6int32_t& selection_vector, 88 | const vector6d_t& wrench, 89 | const ForceMode& mode, 90 | const vector6d_t& limits) { 91 | 92 | std::lock_guard lock(client_mutex_); 93 | if (!client_) { 94 | return false; 95 | } 96 | int32_t buffer[SCRIPT_COMMAND_DATA_SIZE] = {0}; 97 | buffer[0] = htonl(static_cast(Cmd::START_FORCE_MODE)); 98 | int32_t* bp = &buffer[1]; 99 | for (auto& tf : task_frame) { 100 | *bp = htonl(static_cast((tf * CONTROL::COMMON_ZOOM_RATIO))); 101 | bp++; 102 | } 103 | for (auto& sv : selection_vector) { 104 | *bp = htonl(sv); 105 | bp++; 106 | } 107 | for (auto& wr : wrench) { 108 | *bp = htonl(static_cast((wr * CONTROL::COMMON_ZOOM_RATIO))); 109 | bp++; 110 | } 111 | *bp = htonl(static_cast(mode)); 112 | bp++; 113 | for (auto& li : limits) { 114 | *bp = htonl(static_cast((li * CONTROL::COMMON_ZOOM_RATIO))); 115 | bp++; 116 | } 117 | return write(buffer, sizeof(buffer)) > 0; 118 | } 119 | 120 | bool ScriptCommandInterface::endForceMode() { 121 | std::lock_guard lock(client_mutex_); 122 | if (!client_) { 123 | return false; 124 | } 125 | int32_t buffer[SCRIPT_COMMAND_DATA_SIZE] = {0}; 126 | buffer[0] = htonl(static_cast(Cmd::END_FORCE_MODE)); 127 | return write(buffer, sizeof(buffer)) > 0; 128 | } 129 | 130 | int ScriptCommandInterface::write(int32_t buffer[], int size) { 131 | try { 132 | return client_->write_some(boost::asio::buffer(buffer, size)); 133 | } catch(const boost::system::system_error &error) { 134 | server_->releaseClient(client_); 135 | return -1; 136 | } 137 | } 138 | 139 | bool ScriptCommandInterface::isRobotConnect() { 140 | std::lock_guard lock(client_mutex_); 141 | if (client_) { 142 | return client_->is_open(); 143 | } else { 144 | return false; 145 | } 146 | } 147 | 148 | } // namespace ELITE -------------------------------------------------------------------------------- /source/Control/ScriptSender.cpp: -------------------------------------------------------------------------------- 1 | #include "ScriptSender.hpp" 2 | #include "ControlCommon.hpp" 3 | #include "EliteException.hpp" 4 | #include "Log.hpp" 5 | #include 6 | 7 | using namespace ELITE; 8 | 9 | 10 | 11 | ScriptSender::ScriptSender(int port, const std::string& program) : program_(program) { 12 | server_.reset(new TcpServer(port)); 13 | server_->setConnectCallback([&](std::shared_ptr client){ 14 | if (client_ && client->is_open()) { 15 | ELITE_LOG_INFO("Script sender has new connection, previous connection will be dropped."); 16 | server_->releaseClient(client_); 17 | } 18 | client_ = client; 19 | ELITE_LOG_INFO("Script sender accept new connection."); 20 | responseRequest(); 21 | }); 22 | } 23 | 24 | 25 | ScriptSender::~ScriptSender() { 26 | 27 | } 28 | 29 | 30 | void ScriptSender::responseRequest() { 31 | if (!client_) { 32 | return; 33 | } 34 | boost::asio::async_read_until( 35 | *client_, 36 | recv_request_buffer_, 37 | '\n', 38 | [&](boost::system::error_code ec, std::size_t len) { 39 | if (ec || len <= 0) { 40 | if (client_->is_open()) { 41 | ELITE_LOG_INFO("Connection to script sender interface dropped: %s", boost::system::system_error(ec).what()); 42 | server_->releaseClient(client_); 43 | } 44 | return; 45 | } 46 | ELITE_LOG_INFO("Robot request external control script."); 47 | std::string request; 48 | std::istream response_stream(&recv_request_buffer_); 49 | std::getline(response_stream, request); 50 | if (request == PROGRAM_REQUEST_) { 51 | boost::system::error_code wec; 52 | client_->write_some(boost::asio::buffer(program_), wec); 53 | if (wec) { 54 | ELITE_LOG_ERROR("Script sender send script fail: %s", boost::system::system_error(wec).what()); 55 | return; 56 | } 57 | } 58 | responseRequest(); 59 | } 60 | ); 61 | } 62 | -------------------------------------------------------------------------------- /source/Control/TrajectoryInterface.cpp: -------------------------------------------------------------------------------- 1 | #include "TrajectoryInterface.hpp" 2 | #include "ControlCommon.hpp" 3 | #include "EliteException.hpp" 4 | #include "Log.hpp" 5 | #include 6 | 7 | using namespace ELITE; 8 | 9 | 10 | 11 | TrajectoryInterface::TrajectoryInterface(int port) { 12 | server_.reset(new TcpServer(port)); 13 | 14 | server_->setConnectCallback([&](std::shared_ptr client){ 15 | { 16 | std::lock_guard lock(client_mutex_); 17 | if (client_ && client_->is_open()) { 18 | ELITE_LOG_INFO("Trajectory interface has new connection, previous connection will be dropped."); 19 | server_->releaseClient(client_); 20 | } 21 | ELITE_LOG_INFO("Trajectory interface accept new connection."); 22 | client_ = client; 23 | } 24 | receiveResult(); 25 | }); 26 | } 27 | 28 | 29 | TrajectoryInterface::~TrajectoryInterface() { 30 | 31 | } 32 | 33 | 34 | void TrajectoryInterface::receiveResult() { 35 | std::lock_guard lock(client_mutex_); 36 | if (!client_) { 37 | return; 38 | } 39 | if (!client_->is_open()) { 40 | client_.reset(); 41 | return; 42 | } 43 | client_->async_read_some(boost::asio::buffer(&motion_result_, sizeof(motion_result_)), [&](boost::system::error_code ec, std::size_t len){ 44 | if (len <= 0 || ec) { 45 | ELITE_LOG_INFO("Connection to trajectory interface dropped: %s", boost::system::system_error(ec).what()); 46 | server_->releaseClient(client_); 47 | return; 48 | } 49 | 50 | motion_result_ = (TrajectoryMotionResult)htonl((int)motion_result_); 51 | if (motion_result_func_) { 52 | motion_result_func_(motion_result_); 53 | } 54 | receiveResult(); 55 | }); 56 | } 57 | 58 | 59 | bool TrajectoryInterface::writeTrajectoryPoint( const vector6d_t& positions, 60 | float time, 61 | float blend_radius, 62 | bool cartesian) { 63 | std::lock_guard lock(client_mutex_); 64 | if (!client_) { 65 | return false; 66 | } 67 | int32_t buffer[TRAJECTORY_MESSAGE_LEN] = {0}; 68 | for (size_t i = 0; i < 6; i++) { 69 | buffer[i] = htonl(round(positions[i] * CONTROL::POS_ZOOM_RATIO)); 70 | } 71 | buffer[18] = htonl(round(time * CONTROL::TIME_ZOOM_RATIO)); 72 | buffer[19] = htonl(round(blend_radius * CONTROL::POS_ZOOM_RATIO)); 73 | if (cartesian) { 74 | buffer[20] = htonl((int)TrajectoryMotionType::CARTESIAN); 75 | } else { 76 | buffer[20] = htonl((int)TrajectoryMotionType::JOINT); 77 | } 78 | 79 | return write(buffer, sizeof(buffer)) > 0; 80 | } 81 | 82 | 83 | int TrajectoryInterface::write(int32_t buffer[], int size) { 84 | try { 85 | return client_->write_some(boost::asio::buffer(buffer, size)); 86 | } catch(const boost::system::system_error &error) { 87 | server_->releaseClient(client_); 88 | return -1; 89 | } 90 | } 91 | 92 | bool TrajectoryInterface::isRobotConnect() { 93 | std::lock_guard lock(client_mutex_); 94 | if (client_) { 95 | return client_->is_open(); 96 | } else { 97 | return false; 98 | } 99 | } -------------------------------------------------------------------------------- /source/Elite/ControllerLog.cpp: -------------------------------------------------------------------------------- 1 | #include "Elite/ControllerLog.hpp" 2 | #include "Elite/Log.hpp" 3 | #include "Common/SshUtils.hpp" 4 | 5 | #include 6 | #include 7 | 8 | namespace ELITE { 9 | bool ControllerLog::downloadSystemLog(const std::string &robot_ip, 10 | const std::string &password, 11 | const std::string &path, 12 | std::function progress_cb) { 13 | 14 | std::string command = "bash -lc 'printenv RT_ROBOT_DATA_PATH'"; 15 | std::string remote_path = SSH_UTILS::executeCommand(robot_ip, "root", password, command); 16 | // Erase '\n' 17 | remote_path.erase(std::remove(remote_path.begin(), remote_path.end(), '\n'), remote_path.end()); 18 | remote_path += "log/log_history.csv"; 19 | ELITE_LOG_DEBUG("Remote path: %s", remote_path.c_str()); 20 | return SSH_UTILS::downloadFile(robot_ip, "root", password, remote_path, path, progress_cb); 21 | } 22 | 23 | } // namespace ELITE -------------------------------------------------------------------------------- /source/Elite/Log.cpp: -------------------------------------------------------------------------------- 1 | #include "Log.hpp" 2 | #include "Logger.hpp" 3 | #include 4 | 5 | 6 | namespace ELITE{ 7 | 8 | 9 | void registerLogHandler(std::unique_ptr hanlder) { 10 | getLogger().registerHandler(hanlder); 11 | } 12 | 13 | void unregisterLogHandler() { 14 | getLogger().unregisterHandler(); 15 | } 16 | 17 | void setLogLevel(LogLevel level) { 18 | getLogger().setLevel(level); 19 | } 20 | 21 | void log(const char* file, int line, LogLevel level, const char* fmt, ...) { 22 | if (level >= getLogger().getLogLevel()) { 23 | size_t buffer_size = 4096; 24 | std::unique_ptr buffer; 25 | buffer.reset(new char[buffer_size]); 26 | 27 | va_list args; 28 | va_start(args, fmt); 29 | va_list args_copy; 30 | va_copy(args_copy, args); 31 | 32 | size_t characters = 1 + std::vsnprintf(buffer.get(), buffer_size, fmt, args); 33 | 34 | if (characters >= buffer_size) { 35 | buffer_size = characters + 1; 36 | buffer.reset(new char[buffer_size]); 37 | std::vsnprintf(buffer.get(), buffer_size, fmt, args_copy); 38 | } 39 | 40 | va_end(args); 41 | va_end(args_copy); 42 | 43 | getLogger().log(file, line, level, buffer.get()); 44 | } 45 | } 46 | 47 | 48 | } -------------------------------------------------------------------------------- /source/Elite/Logger.cpp: -------------------------------------------------------------------------------- 1 | #include "Logger.hpp" 2 | 3 | namespace ELITE{ 4 | 5 | 6 | static Logger s_logger; 7 | 8 | Logger& getLogger() { 9 | return s_logger; 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /source/Elite/RemoteUpgrade.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "Common/SshUtils.hpp" 6 | #include "Elite/Logger.hpp" 7 | 8 | using namespace ELITE::SSH_UTILS; 9 | 10 | namespace ELITE 11 | { 12 | 13 | namespace UPGRADE 14 | { 15 | 16 | bool upgradeControlSoftware(std::string ip, std::string file, std::string password) { 17 | auto upload_error_cb = [&](int f_z, int r_z, const char* err) { 18 | if (err) { 19 | ELITE_LOG_ERROR("Upload update file fail %d/%d. Reason: %s ", r_z, f_z, err); 20 | } 21 | }; 22 | // Upload update package 23 | if (!uploadFile(ip, "root", password, "/tmp/CS_UPDATE.eup", file, upload_error_cb)) { 24 | return false; 25 | } 26 | 27 | // Add executable permissions to the upgrade package. 28 | std::string cmd = "chmod +x /tmp/CS_UPDATE.eup"; 29 | std::string cmd_out = executeCommand(ip, "root", password, cmd); 30 | ELITE_LOG_DEBUG("Execute cmd: %s\n Output:%s", cmd.c_str(), cmd_out.c_str()); 31 | 32 | // Execute the upgrade package in the bash environment. 33 | cmd = "bash -lc '/tmp/CS_UPDATE.eup --app'"; 34 | cmd_out = executeCommand(ip, "root", password, cmd); 35 | ELITE_LOG_DEBUG("Execute cmd: %s\n Output:%s", cmd.c_str(), cmd_out.c_str()); 36 | return true; 37 | } 38 | 39 | } // namespace UPGRADE 40 | 41 | 42 | } // namespace ELITE 43 | 44 | 45 | -------------------------------------------------------------------------------- /source/Elite/VersionInfo.cpp: -------------------------------------------------------------------------------- 1 | #include "VersionInfo.hpp" 2 | #include "Utils.hpp" 3 | #include "EliteException.hpp" 4 | 5 | using namespace ELITE; 6 | 7 | VersionInfo::VersionInfo(const std::string& version) { 8 | fromString(version); 9 | } 10 | 11 | std::string VersionInfo::toString() const { 12 | return std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(bugfix) + "." + std::to_string(build); 13 | } 14 | 15 | VersionInfo VersionInfo::fromString(const std::string& str) { 16 | auto components = UTILS::StringUtils::splitString(str, "."); 17 | VersionInfo version; 18 | if (components.size() >= 2) { 19 | version.major = std::stoi(components[0]); 20 | version.minor = std::stoi(components[1]); 21 | if (components.size() ==3 ) { 22 | version.bugfix = std::stoi(components[2]); 23 | } else if (components.size() == 4) { 24 | version.bugfix = std::stoi(components[2]); 25 | version.build = std::stoi(components[3]); 26 | } 27 | } else { 28 | throw EliteException(EliteException::Code::ILLEGAL_PARAM, 29 | "Given string '" + str + "' does not conform a version string format."); 30 | } 31 | return version; 32 | } 33 | 34 | VersionInfo& VersionInfo::operator=(const VersionInfo& input) { 35 | major = input.major; 36 | minor = input.minor; 37 | bugfix = input.bugfix; 38 | build = input.build; 39 | return *this; 40 | } 41 | 42 | bool VersionInfo::operator==(const VersionInfo& v) const { 43 | if (major == v.major && minor == v.minor && bugfix == v.bugfix && build == v.build) { 44 | return true; 45 | } 46 | return false; 47 | } 48 | 49 | bool VersionInfo::operator!=(const VersionInfo& v) const { 50 | return !(*this == v); 51 | } 52 | 53 | bool VersionInfo::operator>(const VersionInfo& v) const { 54 | if (major > v.major && minor > v.minor) 55 | { 56 | return true; 57 | } 58 | return false; 59 | } 60 | 61 | 62 | bool VersionInfo::operator>=(const VersionInfo& v) const { 63 | return (*this == v) || (*this > v); 64 | } 65 | 66 | 67 | bool VersionInfo::operator<(const VersionInfo& v) const { 68 | return !(*this >= v); 69 | } 70 | 71 | 72 | bool VersionInfo::operator<=(const VersionInfo& v) const { 73 | return (*this == v) || (*this < v); 74 | } 75 | 76 | constexpr bool VersionInfo::operator==(VersionInfo& v) const { 77 | if (major == v.major && minor == v.minor && bugfix == v.bugfix && build == v.build) { 78 | return true; 79 | } 80 | return false; 81 | } 82 | 83 | constexpr bool VersionInfo::operator!=(VersionInfo& v) const { 84 | return !(*this == v); 85 | } 86 | 87 | constexpr bool VersionInfo::operator>(VersionInfo& v) const { 88 | if (major > v.major && minor > v.minor) 89 | { 90 | return true; 91 | } 92 | return false; 93 | } 94 | 95 | constexpr bool VersionInfo::operator>=(VersionInfo& v) const { 96 | return (*this == v) || (*this > v); 97 | } 98 | 99 | constexpr bool VersionInfo::operator<(VersionInfo& v) const { 100 | return !(*this >= v); 101 | } 102 | 103 | constexpr bool VersionInfo::operator<=(VersionInfo& v) const { 104 | return (*this == v) || (*this < v); 105 | } -------------------------------------------------------------------------------- /source/Primary/PrimaryPort.cpp: -------------------------------------------------------------------------------- 1 | #include "PrimaryPort.hpp" 2 | #include "EliteException.hpp" 3 | #include "Utils.hpp" 4 | #include "Log.hpp" 5 | 6 | using namespace std::chrono; 7 | 8 | namespace ELITE 9 | { 10 | using namespace std::chrono; 11 | 12 | PrimaryPort::PrimaryPort() { 13 | message_head_.resize(HEAD_LENGTH); 14 | } 15 | 16 | PrimaryPort::~PrimaryPort() { 17 | disconnect(); 18 | } 19 | 20 | 21 | bool PrimaryPort::connect(const std::string& ip, int port) { 22 | try { 23 | std::lock_guard lock(socket_mutex_); 24 | socket_ptr_.reset(new boost::asio::ip::tcp::socket(io_context_)); 25 | socket_ptr_->open(boost::asio::ip::tcp::v4()); 26 | socket_ptr_->set_option(boost::asio::ip::tcp::no_delay(true)); 27 | socket_ptr_->set_option(boost::asio::socket_base::reuse_address(true)); 28 | socket_ptr_->set_option(boost::asio::socket_base::keep_alive(false)); 29 | #if defined(__linux) || defined(linux) || defined(__linux__) 30 | socket_ptr_->set_option(boost::asio::detail::socket_option::boolean(true)); 31 | #endif 32 | boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::make_address(ip), port); 33 | boost::system::error_code connect_ec; 34 | socket_ptr_->async_connect(endpoint, [&](const boost::system::error_code& ec){ 35 | connect_ec = ec; 36 | }); 37 | if (io_context_.stopped()) { 38 | io_context_.restart(); 39 | } 40 | io_context_.run_for(std::chrono::steady_clock::duration(500ms)); 41 | if (connect_ec) { 42 | ELITE_LOG_ERROR("Connect to robot primary port fail: %s", boost::system::system_error(connect_ec).what()); 43 | return false; 44 | } 45 | } catch(const boost::system::system_error &error) { 46 | throw EliteException(EliteException::Code::SOCKET_CONNECT_FAIL, error.what()); 47 | return false; 48 | } 49 | if (!socket_async_thread_) { 50 | // Start async thread 51 | socket_async_thread_alive_ = true; 52 | socket_async_thread_.reset(new std::thread([&](){ 53 | socketAsyncLoop(); 54 | })); 55 | } 56 | return true; 57 | } 58 | 59 | void PrimaryPort::disconnect() { 60 | // Close socket and set thread flag 61 | { 62 | std::lock_guard lock(socket_mutex_); 63 | socket_async_thread_alive_ = false; 64 | socket_ptr_.reset(); 65 | } 66 | if (socket_async_thread_ && socket_async_thread_->joinable()) { 67 | socket_async_thread_->join(); 68 | } 69 | socket_async_thread_.reset(); 70 | } 71 | 72 | bool PrimaryPort::sendScript(const std::string& script) { 73 | std::lock_guard lock(socket_mutex_); 74 | if (!socket_ptr_) { 75 | ELITE_LOG_ERROR("Don't connect to robot primary port"); 76 | return false; 77 | } 78 | auto script_with_newline = std::make_shared(script + "\n"); 79 | boost::system::error_code ec; 80 | socket_ptr_->write_some(boost::asio::buffer(*script_with_newline), ec); 81 | if (ec) { 82 | ELITE_LOG_ERROR("Send script to robot fail : ", boost::system::system_error(ec).what()); 83 | return false; 84 | } else { 85 | return true; 86 | } 87 | } 88 | 89 | bool PrimaryPort::getPackage(std::shared_ptr pkg, int timeout_ms) { 90 | { 91 | std::lock_guard lock(mutex_); 92 | parser_sub_msg_.insert({pkg->getType(), pkg}); 93 | } 94 | 95 | return pkg->waitUpdate(timeout_ms); 96 | } 97 | 98 | bool PrimaryPort::parserMessage() { 99 | std::lock_guard lock(socket_mutex_); 100 | if (!socket_ptr_ || !socket_ptr_->is_open()) { 101 | ELITE_LOG_WARN("Don't connect to robot primary port"); 102 | return false; 103 | } 104 | if (socket_ptr_->available() <= HEAD_LENGTH) { 105 | return true; 106 | } 107 | // Receive package head and parser it 108 | boost::system::error_code ec; 109 | int head_len = boost::asio::read(*socket_ptr_, boost::asio::buffer(message_head_, HEAD_LENGTH), ec); 110 | if (ec) { 111 | ELITE_LOG_ERROR("Primary port receive package head had expection: %s", boost::system::system_error(ec).what()); 112 | return false; 113 | } 114 | uint32_t package_len = 0; 115 | UTILS::EndianUtils::unpack(message_head_.begin(), package_len); 116 | if (package_len <= HEAD_LENGTH) { 117 | ELITE_LOG_ERROR("Primary port package len error: %d", package_len); 118 | return false; 119 | } 120 | 121 | return parserMessageBody(message_head_[4], package_len); 122 | } 123 | 124 | bool PrimaryPort::parserMessageBody(int type, int package_len) { 125 | boost::system::error_code ec; 126 | int body_len = package_len - HEAD_LENGTH; 127 | message_body_.resize(body_len); 128 | // Receive package body 129 | boost::asio::read(*socket_ptr_, boost::asio::buffer(message_body_, body_len), ec); 130 | if (ec) { 131 | ELITE_LOG_ERROR("Primary port receive package body had expection: %s", boost::system::system_error(ec).what()); 132 | return false; 133 | } 134 | // If RobotState message parser others don't do anything. 135 | if (type == ROBOT_STATE_MSG_TYPE) { 136 | uint32_t sub_len = 0; 137 | for (auto iter = message_body_.begin(); iter < message_body_.end(); iter += sub_len) { 138 | UTILS::EndianUtils::unpack(iter, sub_len); 139 | int sub_type = *(iter + 4); 140 | 141 | std::lock_guard lock(mutex_); 142 | auto psm = parser_sub_msg_.find(sub_type); 143 | if (psm != parser_sub_msg_.end()) { 144 | psm->second->parser(sub_len, iter); 145 | psm->second->notifyUpated(); 146 | parser_sub_msg_.erase(sub_type); 147 | } 148 | } 149 | } 150 | return true; 151 | } 152 | 153 | void PrimaryPort::socketAsyncLoop() { 154 | while (socket_async_thread_alive_) { 155 | try { 156 | if (!parserMessage()) { 157 | socket_async_thread_alive_ = false; 158 | } 159 | std::this_thread::sleep_for(10ms); 160 | } catch(const std::exception& e) { 161 | socket_async_thread_alive_ = false; 162 | } 163 | } 164 | } 165 | 166 | 167 | } -------------------------------------------------------------------------------- /source/Primary/PrimaryPortInterface.cpp: -------------------------------------------------------------------------------- 1 | #include "PrimaryPort.hpp" 2 | #include "PrimaryPortInterface.hpp" 3 | 4 | namespace ELITE 5 | { 6 | 7 | class PrimaryPortInterface::Impl { 8 | public: 9 | PrimaryPort primary_; 10 | }; 11 | 12 | 13 | PrimaryPortInterface::PrimaryPortInterface() { 14 | impl_ = std::make_unique(); 15 | } 16 | 17 | PrimaryPortInterface::~PrimaryPortInterface() { 18 | 19 | } 20 | 21 | bool PrimaryPortInterface::connect(const std::string& ip, int port) { 22 | return impl_->primary_.connect(ip, port); 23 | } 24 | 25 | void PrimaryPortInterface::disconnect() { 26 | impl_->primary_.disconnect(); 27 | } 28 | 29 | bool PrimaryPortInterface::sendScript(const std::string& script) { 30 | return impl_->primary_.sendScript(script); 31 | } 32 | 33 | bool PrimaryPortInterface::getPackage(std::shared_ptr pkg, int timeout_ms) { 34 | return impl_->primary_.getPackage(pkg, timeout_ms); 35 | } 36 | 37 | 38 | 39 | } // namespace ELITE 40 | 41 | -------------------------------------------------------------------------------- /source/Primary/RobotConfPackage.cpp: -------------------------------------------------------------------------------- 1 | #include "RobotConfPackage.hpp" 2 | #include "Utils.hpp" 3 | 4 | /** In controller version 2.11.0, the sub package is: 5 | uint32_t configuration_sub_len 6 | uint8_t configuration_sub_type 7 | foreach joint: 8 | double limit_min_joint_x 9 | double limit_max_joint_x 10 | end 11 | 12 | foreach joint: 13 | double max_velocity_joint_x 14 | double max_acc_joint_x 15 | end 16 | 17 | double default_velocity_joint 18 | double default_acc_joint 19 | double default_tool_velocity 20 | double default_tool_acc 21 | double eq_radius 22 | 23 | foreach joint: 24 | double dh_a_joint_x 25 | end 26 | 27 | foreach joint: 28 | double dh_d_joint_d 29 | end 30 | 31 | foreach joint: 32 | double dh_alpha_joint_x 33 | end 34 | 35 | foreach joint: 36 | double reserver 37 | end 38 | 39 | uint32_t board_version 40 | uint32_t control_box_type 41 | uint32_t robot_type 42 | uint32_t robot_struct 43 | */ 44 | 45 | namespace ELITE 46 | { 47 | 48 | 49 | void KinematicsInfo::parser(int len, const std::vector::const_iterator& iter) { 50 | int offset = DH_PARAM_OFFSET; 51 | for (size_t i = 0; i < 6; i++) { 52 | UTILS::EndianUtils::unpack(iter + offset, dh_a_[i]); 53 | offset += sizeof(double); 54 | } 55 | for (size_t i = 0; i < 6; i++) { 56 | UTILS::EndianUtils::unpack(iter + offset, dh_d_[i]); 57 | offset += sizeof(double); 58 | } 59 | for (size_t i = 0; i < 6; i++) { 60 | UTILS::EndianUtils::unpack(iter + offset, dh_alpha_[i]); 61 | offset += sizeof(double); 62 | } 63 | } 64 | 65 | 66 | 67 | } // namespace ELITE 68 | 69 | -------------------------------------------------------------------------------- /source/Rtsi/RtsiClientInterface.cpp: -------------------------------------------------------------------------------- 1 | #include "RtsiClientInterface.hpp" 2 | #include "RtsiClient.hpp" 3 | using namespace ELITE; 4 | 5 | class RtsiClientInterface::Impl { 6 | public: 7 | RtsiClient client_; 8 | }; 9 | 10 | RtsiClientInterface::RtsiClientInterface() { 11 | impl_ = std::make_unique(); 12 | } 13 | 14 | RtsiClientInterface::~RtsiClientInterface() = default; 15 | 16 | 17 | void RtsiClientInterface::connect(const std::string& ip, int port) { 18 | impl_->client_.connect(ip, port); 19 | } 20 | 21 | void RtsiClientInterface::disconnect() { 22 | impl_->client_.disconnect(); 23 | } 24 | 25 | 26 | bool RtsiClientInterface::negotiateProtocolVersion(uint16_t version) { 27 | return impl_->client_.negotiateProtocolVersion(version); 28 | } 29 | 30 | 31 | VersionInfo RtsiClientInterface::getControllerVersion() { 32 | return impl_->client_.getControllerVersion(); 33 | } 34 | 35 | 36 | RtsiRecipeSharedPtr RtsiClientInterface::setupOutputRecipe(const std::vector& recipe_list, double frequency) { 37 | return impl_->client_.setupOutputRecipe(recipe_list, frequency); 38 | } 39 | 40 | 41 | RtsiRecipeSharedPtr RtsiClientInterface::setupInputRecipe(const std::vector& recipe) { 42 | return impl_->client_.setupInputRecipe(recipe); 43 | } 44 | 45 | 46 | bool RtsiClientInterface::start() { 47 | return impl_->client_.start(); 48 | } 49 | 50 | 51 | bool RtsiClientInterface::pause() { 52 | return impl_->client_.pause(); 53 | } 54 | 55 | 56 | void RtsiClientInterface::send(RtsiRecipeSharedPtr& recipe) { 57 | impl_->client_.send(recipe); 58 | } 59 | 60 | 61 | int RtsiClientInterface::receiveData(std::vector& recipes, bool read_newest) { 62 | return impl_->client_.receiveData(recipes, read_newest); 63 | } 64 | 65 | bool RtsiClientInterface::receiveData(RtsiRecipeSharedPtr recipe, bool read_newest) { 66 | return impl_->client_.receiveData(recipe, read_newest); 67 | } 68 | 69 | bool RtsiClientInterface::isConnected() { 70 | return impl_->client_.isConnected(); 71 | } 72 | 73 | 74 | bool RtsiClientInterface::isStarted() { 75 | return impl_->client_.isStarted(); 76 | } 77 | 78 | 79 | bool RtsiClientInterface::isReadAvailable() { 80 | return impl_->client_.isReadAvailable(); 81 | } 82 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(GTest CONFIG REQUIRED) 2 | 3 | enable_testing() 4 | 5 | configure_file(${PROJECT_SOURCE_DIR}/example/resource/input_recipe.txt ${PROJECT_BINARY_DIR}/test/ COPYONLY) 6 | configure_file(${PROJECT_SOURCE_DIR}/example/resource/output_recipe.txt ${PROJECT_BINARY_DIR}/test/ COPYONLY) 7 | 8 | file(GLOB SOURCES *.cpp) 9 | 10 | foreach(SOURCE ${SOURCES}) 11 | get_filename_component(ELITE_SDK_TEST_NAME ${SOURCE} NAME_WE) 12 | add_executable(${ELITE_SDK_TEST_NAME} ${SOURCE}) 13 | target_include_directories( 14 | ${ELITE_SDK_TEST_NAME} 15 | PUBLIC 16 | ${PROJECT_SOURCE_DIR}/include/ 17 | ${PROJECT_SOURCE_DIR}/include/Common 18 | ${PROJECT_SOURCE_DIR}/include/Elite 19 | ${PROJECT_SOURCE_DIR}/include/Control 20 | ) 21 | target_link_libraries( 22 | ${ELITE_SDK_TEST_NAME} 23 | elite-cs-series-sdk::static 24 | GTest::gtest 25 | ${SYSTEM_LIB} 26 | ) 27 | target_link_directories( 28 | ${ELITE_SDK_TEST_NAME} 29 | PRIVATE ${CMAKE_BINARY_DIR} 30 | ) 31 | endforeach() 32 | -------------------------------------------------------------------------------- /test/ControllerLogTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "Elite/ControllerLog.hpp" 8 | #include "Elite/Log.hpp" 9 | 10 | using namespace ELITE; 11 | 12 | static std::string s_robot_ip; 13 | static std::string s_robot_ssh_pw; 14 | 15 | TEST(controllerLogTest, download_system_log) { 16 | const char* download_err = nullptr; 17 | bool result = ELITE::ControllerLog::downloadSystemLog( 18 | s_robot_ip, 19 | s_robot_ssh_pw, 20 | "./log_history.csv", 21 | [](int f_z, int r_z, const char* err) { 22 | std::cout << "\rDownloaded: " << r_z << "/" << f_z << " bytes" << std::flush; 23 | } 24 | ); 25 | EXPECT_TRUE(result); 26 | EXPECT_TRUE(download_err == nullptr); 27 | } 28 | 29 | 30 | int main(int argc, char** argv) { 31 | if (argc < 3 || argv[1] == nullptr || argv[2] == nullptr ) { 32 | std::cout << "cmd format:\n ControllerLogTest " << std::endl; 33 | return 1; 34 | } 35 | ELITE::setLogLevel(ELITE::LogLevel::ELI_DEBUG); 36 | s_robot_ip = argv[1]; 37 | s_robot_ssh_pw = argv[2]; 38 | 39 | testing::InitGoogleTest(&argc, argv); 40 | return RUN_ALL_TESTS(); 41 | } 42 | -------------------------------------------------------------------------------- /test/DashboardClientTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "Dashboard/DashboardClient.hpp" 5 | 6 | using namespace ELITE; 7 | 8 | static std::string s_robot_ip = "192.168.128.138"; 9 | 10 | class DashboardClientTest : public ::testing::Test { 11 | protected: 12 | void SetUp() { 13 | dashboard_client_.reset(new DashboardClient()); 14 | } 15 | 16 | void TearDown() { 17 | dashboard_client_.reset(); 18 | } 19 | 20 | std::unique_ptr dashboard_client_; 21 | }; 22 | 23 | TEST_F(DashboardClientTest, connect) { 24 | EXPECT_TRUE(dashboard_client_->connect(s_robot_ip)); 25 | dashboard_client_->closeSafetyDialog(); 26 | } 27 | 28 | TEST_F(DashboardClientTest, run_program) { 29 | EXPECT_TRUE(dashboard_client_->connect(s_robot_ip)); 30 | EXPECT_TRUE(dashboard_client_->loadTask("wait_program.task")); 31 | EXPECT_TRUE(dashboard_client_->powerOff()); 32 | std::this_thread::sleep_for(std::chrono::seconds(5)); 33 | EXPECT_TRUE(dashboard_client_->powerOn()); 34 | EXPECT_TRUE(dashboard_client_->brakeRelease()); 35 | EXPECT_TRUE(dashboard_client_->playProgram()); 36 | EXPECT_TRUE(dashboard_client_->pauseProgram()); 37 | EXPECT_TRUE(dashboard_client_->playProgram()); 38 | EXPECT_TRUE(dashboard_client_->stopProgram()); 39 | EXPECT_TRUE(dashboard_client_->powerOff()); 40 | } 41 | 42 | TEST_F(DashboardClientTest, load_configuration) { 43 | EXPECT_TRUE(dashboard_client_->connect(s_robot_ip)); 44 | EXPECT_TRUE(dashboard_client_->loadConfiguration("default.configuration")); 45 | } 46 | 47 | TEST_F(DashboardClientTest, log_and_getters) 48 | { 49 | std::string msg; 50 | EXPECT_TRUE(dashboard_client_->connect(s_robot_ip)); 51 | EXPECT_TRUE(dashboard_client_->log("Testing Log:")); 52 | msg = dashboard_client_->version(); 53 | EXPECT_TRUE(!msg.empty()); 54 | EXPECT_TRUE(dashboard_client_->log("Version: " + msg)); 55 | RobotMode mode = dashboard_client_->robotMode(); 56 | EXPECT_TRUE(mode != RobotMode::UNKNOWN); 57 | EXPECT_TRUE(dashboard_client_->log("Robot mode: " + std::to_string((int)mode))); 58 | msg = dashboard_client_->getTaskPath(); 59 | EXPECT_TRUE(!msg.empty()); 60 | EXPECT_TRUE(dashboard_client_->log("Loaded program: " + msg)); 61 | EXPECT_TRUE(dashboard_client_->stopProgram()); 62 | TaskStatus status = dashboard_client_->getTaskStatus(); 63 | EXPECT_TRUE(status == TaskStatus::STOPPED); 64 | EXPECT_TRUE(dashboard_client_->log("Program state: " + std::to_string((int)status))); 65 | } 66 | 67 | int main(int argc, char** argv) { 68 | if(argc >= 2) { 69 | s_robot_ip = argv[1]; 70 | } 71 | testing::InitGoogleTest(&argc, argv); 72 | return RUN_ALL_TESTS(); 73 | } -------------------------------------------------------------------------------- /test/PrimaryPortTest.cpp: -------------------------------------------------------------------------------- 1 | #include "Primary/PrimaryPort.hpp" 2 | #include "Primary/RobotConfPackage.hpp" 3 | #include "Elite/Log.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace ELITE; 11 | using namespace std::chrono; 12 | 13 | static std::string s_robot_ip = "192.168.51.127"; 14 | 15 | TEST(PrimaryPortTest, multiple_connect) { 16 | std::unique_ptr primary = std::make_unique(); 17 | 18 | for (size_t i = 0; i < 100; i++) { 19 | EXPECT_TRUE(primary->connect(s_robot_ip, 30001)); 20 | std::shared_ptr ki = std::make_shared(); 21 | EXPECT_TRUE(primary->getPackage(ki, 200)); 22 | } 23 | 24 | primary->disconnect(); 25 | std::this_thread::sleep_for(500ms); 26 | } 27 | 28 | 29 | TEST(PrimaryPortTest, connect_disconnect) { 30 | std::unique_ptr primary = std::make_unique(); 31 | 32 | for (size_t i = 0; i < 10; i++) { 33 | EXPECT_TRUE(primary->connect(s_robot_ip, 30001)); 34 | std::shared_ptr ki = std::make_shared(); 35 | EXPECT_TRUE(primary->getPackage(ki, 200)); 36 | primary->disconnect(); 37 | std::this_thread::sleep_for(500ms); 38 | } 39 | } 40 | 41 | TEST(PrimaryPortTest, get_package) { 42 | std::unique_ptr primary = std::make_unique(); 43 | 44 | EXPECT_TRUE(primary->connect(s_robot_ip, 30001)); 45 | 46 | std::shared_ptr template_ki = std::make_shared(); 47 | EXPECT_TRUE(primary->getPackage(template_ki, 100)); 48 | 49 | std::shared_ptr ki = std::make_shared(); 50 | for (size_t i = 0; i < 100; i++) { 51 | EXPECT_TRUE(primary->getPackage(ki, 200)); 52 | for (size_t i = 0; i < 6; i++) { 53 | EXPECT_EQ(template_ki->dh_a_[i], ki->dh_a_[i]); 54 | EXPECT_EQ(template_ki->dh_d_[i], ki->dh_d_[i]); 55 | EXPECT_EQ(template_ki->dh_alpha_[i], ki->dh_alpha_[i]); 56 | } 57 | } 58 | primary->disconnect(); 59 | } 60 | 61 | 62 | int main(int argc, char** argv) { 63 | setLogLevel(LogLevel::ELI_DEBUG); 64 | if(argc >= 2) { 65 | s_robot_ip = argv[1]; 66 | } 67 | testing::InitGoogleTest(&argc, argv); 68 | return RUN_ALL_TESTS(); 69 | } 70 | -------------------------------------------------------------------------------- /test/RemoteUpgradeTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "Elite/RemoteUpgrade.hpp" 7 | #include "Elite/Logger.hpp" 8 | 9 | using namespace ELITE; 10 | 11 | static std::string s_robot_ip; 12 | static std::string s_upgrade_file; 13 | static std::string s_robot_ssh_pw; 14 | 15 | TEST(remoteUpgradeTest, upgrade_control_software) { 16 | EXPECT_TRUE(ELITE::UPGRADE::upgradeControlSoftware(s_robot_ip, s_upgrade_file, s_robot_ssh_pw)); 17 | } 18 | 19 | 20 | int main(int argc, char** argv) { 21 | if (argc < 4 || argv[1] == nullptr || argv[2] == nullptr || argv[3] == nullptr) { 22 | std::cout << "cmd format:\n RemoteUpgradeTest " << std::endl; 23 | return 1; 24 | } 25 | ELITE::setLogLevel(ELITE::LogLevel::ELI_DEBUG); 26 | s_robot_ip = argv[1]; 27 | s_upgrade_file = argv[2]; 28 | s_robot_ssh_pw = argv[3]; 29 | 30 | testing::InitGoogleTest(&argc, argv); 31 | return RUN_ALL_TESTS(); 32 | } 33 | -------------------------------------------------------------------------------- /test/ReverseInterfaceTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "ReverseInterface.hpp" 7 | #include "ControlCommon.hpp" 8 | 9 | #define REVERSE_INTERFACE_TEST_PORT 50002 10 | 11 | using namespace ELITE; 12 | using namespace std::chrono; 13 | 14 | class TcpClient 15 | { 16 | public: 17 | boost::asio::io_context io_context; 18 | std::unique_ptr socket_ptr; 19 | std::unique_ptr resolver_ptr; 20 | TcpClient() = default; 21 | 22 | TcpClient(const std::string& ip, int port) { 23 | connect(ip, port); 24 | } 25 | 26 | ~TcpClient() = default; 27 | 28 | void connect(const std::string& ip, int port) { 29 | try { 30 | socket_ptr.reset(new boost::asio::ip::tcp::socket(io_context)); 31 | resolver_ptr.reset(new boost::asio::ip::tcp::resolver(io_context)); 32 | socket_ptr->open(boost::asio::ip::tcp::v4()); 33 | boost::asio::ip::tcp::no_delay no_delay_option(true); 34 | socket_ptr->set_option(no_delay_option); 35 | boost::asio::socket_base::reuse_address sol_reuse_option(true); 36 | socket_ptr->set_option(sol_reuse_option); 37 | #if defined(__linux) || defined(linux) || defined(__linux__) 38 | boost::asio::detail::socket_option::boolean quickack(true); 39 | socket_ptr->set_option(quickack); 40 | #endif 41 | boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::make_address(ip), port); 42 | socket_ptr->async_connect(endpoint, [&](const boost::system::error_code& error) { 43 | if (error) { 44 | throw boost::system::system_error(error); 45 | } 46 | }); 47 | io_context.run(); 48 | 49 | } catch(const boost::system::system_error &error) { 50 | throw error; 51 | } 52 | } 53 | 54 | }; 55 | 56 | TEST(REVERSE_INTERFACE, trajectory_control_action) { 57 | std::unique_ptr reverse_ins = std::make_unique(REVERSE_INTERFACE_TEST_PORT); 58 | std::unique_ptr client = std::make_unique(); 59 | 60 | EXPECT_NO_THROW(client->connect("127.0.0.1", REVERSE_INTERFACE_TEST_PORT)); 61 | 62 | std::this_thread::sleep_for(100ms); 63 | 64 | reverse_ins->writeTrajectoryControlAction(TrajectoryControlAction::START, 10, 100); 65 | 66 | int32_t buffer[ReverseInterface::REVERSE_DATA_SIZE]; 67 | int recv_num = client->socket_ptr->read_some(boost::asio::buffer(buffer, sizeof(buffer))); 68 | 69 | // check size 70 | EXPECT_EQ(recv_num, sizeof(buffer)); 71 | // timeout 72 | EXPECT_EQ(::htonl(buffer[0]), 100); 73 | // data 1 74 | EXPECT_EQ(::htonl(buffer[1]), (int)TrajectoryControlAction::START); 75 | // data 1 76 | EXPECT_EQ(::htonl(buffer[2]), 10); 77 | // control mode 78 | EXPECT_EQ(::htonl(buffer[7]), (int)ControlMode::MODE_TRAJECTORY); 79 | 80 | client->socket_ptr->close(); 81 | 82 | } 83 | 84 | TEST(REVERSE_INTERFACE, joint_idle_command) { 85 | std::unique_ptr reverse_ins = std::make_unique(REVERSE_INTERFACE_TEST_PORT); 86 | std::unique_ptr client = std::make_unique(); 87 | 88 | EXPECT_NO_THROW(client->connect("127.0.0.1", REVERSE_INTERFACE_TEST_PORT)); 89 | 90 | std::this_thread::sleep_for(100ms); 91 | 92 | reverse_ins->writeJointCommand({1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f}, ControlMode::MODE_IDLE, 100); 93 | 94 | int32_t buffer[ReverseInterface::REVERSE_DATA_SIZE]; 95 | int recv_num = client->socket_ptr->read_some(boost::asio::buffer(buffer, sizeof(buffer))); 96 | 97 | client->socket_ptr->close(); 98 | 99 | // check size 100 | EXPECT_EQ(recv_num, sizeof(buffer)); 101 | // timeout 102 | EXPECT_EQ(::htonl(buffer[0]), 100); 103 | // data 1 104 | EXPECT_EQ(::htonl(buffer[1]), 1 * CONTROL::POS_ZOOM_RATIO); 105 | // data 1 106 | EXPECT_EQ(::htonl(buffer[2]), 2 * CONTROL::POS_ZOOM_RATIO); 107 | // data 1 108 | EXPECT_EQ(::htonl(buffer[3]), 3 * CONTROL::POS_ZOOM_RATIO); 109 | // data 1 110 | EXPECT_EQ(::htonl(buffer[4]), 4 * CONTROL::POS_ZOOM_RATIO); 111 | // data 1 112 | EXPECT_EQ(::htonl(buffer[5]), 5 * CONTROL::POS_ZOOM_RATIO); 113 | // data 1 114 | EXPECT_EQ(::htonl(buffer[6]), 6 * CONTROL::POS_ZOOM_RATIO); 115 | // control mode 116 | EXPECT_EQ(::htonl(buffer[7]), (int)ControlMode::MODE_IDLE); 117 | 118 | } 119 | 120 | TEST(REVERSE_INTERFACE, joint_command_send_nullptr) { 121 | std::unique_ptr reverse_ins = std::make_unique(REVERSE_INTERFACE_TEST_PORT); 122 | std::unique_ptr client = std::make_unique(); 123 | 124 | EXPECT_NO_THROW(client->connect("127.0.0.1", REVERSE_INTERFACE_TEST_PORT)); 125 | 126 | std::this_thread::sleep_for(100ms); 127 | 128 | EXPECT_FALSE(reverse_ins->writeJointCommand(nullptr, ControlMode::MODE_IDLE, 100)); 129 | 130 | } 131 | 132 | 133 | TEST(REVERSE_INTERFACE, disconnect) { 134 | std::unique_ptr reverse_ins; 135 | 136 | reverse_ins.reset(new ReverseInterface(REVERSE_INTERFACE_TEST_PORT)); 137 | 138 | std::unique_ptr client; 139 | client.reset(new TcpClient()); 140 | EXPECT_NO_THROW(client->connect("127.0.0.1", REVERSE_INTERFACE_TEST_PORT)); 141 | 142 | std::this_thread::sleep_for(50ms); 143 | 144 | EXPECT_TRUE(reverse_ins->isRobotConnect()); 145 | 146 | client.reset(); 147 | 148 | std::this_thread::sleep_for(50ms); 149 | 150 | EXPECT_FALSE(reverse_ins->isRobotConnect()); 151 | 152 | } 153 | 154 | int main(int argc, char** argv) { 155 | testing::InitGoogleTest(&argc, argv); 156 | return RUN_ALL_TESTS(); 157 | } 158 | -------------------------------------------------------------------------------- /test/RtsiIOTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "Elite/RtsiIOInterface.hpp" 7 | 8 | using namespace ELITE; 9 | 10 | static std::string s_robot_ip; 11 | 12 | 13 | TEST(RtsiIOTest, rw_io) { 14 | EXPECT_FALSE(s_robot_ip.empty()); 15 | 16 | RtsiIOInterface io_interface("output_recipe.txt", "input_recipe.txt", 250); 17 | EXPECT_TRUE(io_interface.connect(s_robot_ip)); 18 | 19 | EXPECT_TRUE(io_interface.setStandardDigital(0, false)); 20 | std::this_thread::sleep_for(std::chrono::milliseconds(16)); 21 | uint32_t io_bits = io_interface.getDigitalOutputBits(); 22 | EXPECT_FALSE(io_bits & 0X01); 23 | 24 | EXPECT_TRUE(io_interface.setStandardDigital(0, true)); 25 | std::this_thread::sleep_for(std::chrono::milliseconds(16)); 26 | io_bits = io_interface.getDigitalOutputBits(); 27 | EXPECT_TRUE(io_bits & 0X01); 28 | } 29 | 30 | 31 | TEST(RtsiMultipleConnect, connect) { 32 | EXPECT_FALSE(s_robot_ip.empty()); 33 | 34 | RtsiIOInterface io_interface("output_recipe.txt", "input_recipe.txt", 250); 35 | EXPECT_TRUE(io_interface.connect(s_robot_ip)); 36 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 37 | EXPECT_TRUE(io_interface.connect(s_robot_ip)); 38 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 39 | EXPECT_TRUE(io_interface.connect(s_robot_ip)); 40 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 41 | 42 | std::thread connect_thread([&](){ 43 | EXPECT_TRUE(io_interface.connect(s_robot_ip)); 44 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 45 | EXPECT_TRUE(io_interface.connect(s_robot_ip)); 46 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 47 | EXPECT_TRUE(io_interface.connect(s_robot_ip)); 48 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 49 | }); 50 | 51 | std::thread get_thread([&](){ 52 | for (size_t i = 0; i < 10000; i++) { 53 | io_interface.getActualJointCurrent(); 54 | } 55 | }); 56 | 57 | connect_thread.join(); 58 | get_thread.join(); 59 | 60 | io_interface.disconnect(); 61 | } 62 | 63 | 64 | int main(int argc, char** argv) { 65 | if (argc < 2 || argv[1] == nullptr) { 66 | return 1; 67 | } 68 | s_robot_ip = argv[1]; 69 | 70 | testing::InitGoogleTest(&argc, argv); 71 | return RUN_ALL_TESTS(); 72 | } 73 | -------------------------------------------------------------------------------- /test/ScriptSenderTest.cpp: -------------------------------------------------------------------------------- 1 | #include "Control/ScriptSender.hpp" 2 | #include "EliteException.hpp" 3 | #include 4 | #include 5 | 6 | using namespace ELITE; 7 | 8 | #define PROGRAM_REQUEST ("request_program\n") 9 | #define TEST_PORT 60002 10 | 11 | class TcpClient { 12 | public: 13 | boost::asio::io_context io_context; 14 | std::unique_ptr socket_ptr; 15 | std::unique_ptr resolver_ptr; 16 | TcpClient(const std::string& ip, int port) { 17 | try { 18 | socket_ptr.reset(new boost::asio::ip::tcp::socket(io_context)); 19 | resolver_ptr.reset(new boost::asio::ip::tcp::resolver(io_context)); 20 | socket_ptr->open(boost::asio::ip::tcp::v4()); 21 | boost::asio::socket_base::reuse_address sol_reuse_option(true); 22 | socket_ptr->set_option(sol_reuse_option); 23 | boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::make_address(ip), port); 24 | socket_ptr->async_connect(endpoint, [&](const boost::system::error_code& error) { 25 | if (error) { 26 | throw EliteException(EliteException::Code::SOCKET_CONNECT_FAIL); 27 | } 28 | }); 29 | io_context.run(); 30 | 31 | } catch(const boost::system::system_error &error) { 32 | throw error; 33 | } 34 | } 35 | 36 | ~TcpClient() = default; 37 | }; 38 | 39 | 40 | class ScriptSenderTest : public ::testing::Test { 41 | protected: 42 | void SetUp() { 43 | script_sender_.reset(new ScriptSender(TEST_PORT, program_)); 44 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 45 | tcp_client_.reset(new TcpClient("127.0.0.1", TEST_PORT)); 46 | } 47 | 48 | void TearDown() { 49 | tcp_client_.reset(); 50 | script_sender_.reset(); 51 | } 52 | 53 | std::unique_ptr tcp_client_; 54 | std::unique_ptr script_sender_; 55 | std::string program_ = "print(\"success recv\")"; 56 | }; 57 | 58 | TEST_F(ScriptSenderTest, response_request) { 59 | 60 | std::unique_ptr client_recv_program; 61 | client_recv_program.reset(new char[program_.length()]); 62 | 63 | tcp_client_->socket_ptr->non_blocking(false); 64 | 65 | EXPECT_TRUE(tcp_client_->socket_ptr->write_some(boost::asio::buffer(PROGRAM_REQUEST, sizeof(PROGRAM_REQUEST) - 1)) == (sizeof(PROGRAM_REQUEST) - 1)); 66 | 67 | tcp_client_->socket_ptr->receive(boost::asio::buffer(client_recv_program.get(), program_.length())); 68 | 69 | ASSERT_EQ(client_recv_program.get(), program_); 70 | 71 | } 72 | 73 | TEST_F(ScriptSenderTest, mulit_clients) { 74 | 75 | std::unique_ptr client_recv_program; 76 | client_recv_program.reset(new char[program_.length()]); 77 | 78 | std::unique_ptr tcp_client_another = std::make_unique("127.0.0.1", TEST_PORT); 79 | 80 | tcp_client_another->socket_ptr->non_blocking(false); 81 | 82 | EXPECT_TRUE(tcp_client_another->socket_ptr->write_some(boost::asio::buffer(PROGRAM_REQUEST, sizeof(PROGRAM_REQUEST) - 1)) == (sizeof(PROGRAM_REQUEST) - 1)); 83 | 84 | tcp_client_another->socket_ptr->receive(boost::asio::buffer(client_recv_program.get(), program_.length())); 85 | 86 | ASSERT_EQ(client_recv_program.get(), program_); 87 | 88 | } 89 | 90 | 91 | int main(int argc, char** argv) { 92 | testing::InitGoogleTest(&argc, argv); 93 | return RUN_ALL_TESTS(); 94 | } -------------------------------------------------------------------------------- /test/SshUtilsTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "Common/SshUtils.hpp" 8 | #include "Elite/Logger.hpp" 9 | 10 | using namespace ELITE; 11 | 12 | static std::string s_user; 13 | static std::string s_ip; 14 | static std::string s_ssh_pw; 15 | 16 | TEST(sshUtilsTest, ssh_utils_test) { 17 | std::string cmd_output = 18 | SSH_UTILS::executeCommand( 19 | s_ip, 20 | s_user, 21 | s_ssh_pw, 22 | "printenv PATH"); 23 | EXPECT_NE(cmd_output.find("/usr/bin"), cmd_output.npos); 24 | } 25 | 26 | TEST(sshUtilsTest, ssh_file_test) { 27 | std::ofstream o_file("./sdk_test.txt", std::ios::out | std::ios::trunc); 28 | o_file << "abcd" << std::endl; 29 | o_file.close(); 30 | 31 | EXPECT_TRUE(SSH_UTILS::uploadFile(s_ip, s_user, s_ssh_pw, "/tmp/sdk_test.txt", "./sdk_test.txt", nullptr)); 32 | EXPECT_TRUE(SSH_UTILS::downloadFile(s_ip, s_user, s_ssh_pw, "/tmp/sdk_test.txt", "./sdk_test_dl.txt", nullptr)); 33 | 34 | std::ifstream i_file("./sdk_test_dl.txt"); 35 | std::string text; 36 | i_file >> text; 37 | EXPECT_EQ(text, "abcd"); 38 | } 39 | 40 | int main(int argc, char** argv) { 41 | if (argc < 4 || argv[1] == nullptr || argv[2] == nullptr || argv[3] == nullptr) { 42 | std::cout << "cmd format:\n SshUtilsTest " << std::endl; 43 | return 1; 44 | } 45 | ELITE::setLogLevel(ELITE::LogLevel::ELI_DEBUG); 46 | s_user = argv[1]; 47 | s_ip = argv[2]; 48 | s_ssh_pw = argv[3]; 49 | 50 | testing::InitGoogleTest(&argc, argv); 51 | return RUN_ALL_TESTS(); 52 | } 53 | -------------------------------------------------------------------------------- /test/TrajectoryInterfaceTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "TrajectoryInterface.hpp" 7 | #include "ControlCommon.hpp" 8 | 9 | using namespace ELITE; 10 | using namespace std::chrono; 11 | 12 | #define SCRIPT_COMMAND_INTERFACE_TEST_PORT 50004 13 | #define TRAJECTORY_INTERFACE_TEST_PORT 50003 14 | 15 | 16 | class TcpClient 17 | { 18 | public: 19 | boost::asio::io_context io_context; 20 | std::unique_ptr socket_ptr; 21 | std::unique_ptr resolver_ptr; 22 | TcpClient() = default; 23 | 24 | TcpClient(const std::string& ip, int port) { 25 | connect(ip, port); 26 | } 27 | 28 | ~TcpClient() = default; 29 | 30 | void connect(const std::string& ip, int port) { 31 | try { 32 | socket_ptr.reset(new boost::asio::ip::tcp::socket(io_context)); 33 | resolver_ptr.reset(new boost::asio::ip::tcp::resolver(io_context)); 34 | socket_ptr->open(boost::asio::ip::tcp::v4()); 35 | boost::asio::ip::tcp::no_delay no_delay_option(true); 36 | socket_ptr->set_option(no_delay_option); 37 | boost::asio::socket_base::reuse_address sol_reuse_option(true); 38 | socket_ptr->set_option(sol_reuse_option); 39 | #if defined(__linux) || defined(linux) || defined(__linux__) 40 | boost::asio::detail::socket_option::boolean quickack(true); 41 | socket_ptr->set_option(quickack); 42 | #endif 43 | boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::make_address(ip), port); 44 | socket_ptr->async_connect(endpoint, [&](const boost::system::error_code& error) { 45 | if (error) { 46 | throw boost::system::system_error(error); 47 | } 48 | }); 49 | io_context.run(); 50 | 51 | } catch(const boost::system::system_error &error) { 52 | throw error; 53 | } 54 | } 55 | 56 | }; 57 | 58 | TEST(TRAJECTORY_INTERFACE, write_point) { 59 | std::unique_ptr trajectory_ins = std::make_unique(TRAJECTORY_INTERFACE_TEST_PORT); 60 | std::unique_ptr client = std::make_unique(); 61 | 62 | EXPECT_NO_THROW(client->connect("127.0.0.1", TRAJECTORY_INTERFACE_TEST_PORT)); 63 | 64 | std::this_thread::sleep_for(50ms); 65 | 66 | TrajectoryMotionResult motion_result = TrajectoryMotionResult::FAILURE; 67 | trajectory_ins->setMotionResultCallback([&](TrajectoryMotionResult result) { 68 | motion_result = result; 69 | }); 70 | 71 | trajectory_ins->writeTrajectoryPoint({1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f}, 100, 1.230, true); 72 | 73 | int32_t buffer[TrajectoryInterface::TRAJECTORY_MESSAGE_LEN]; 74 | int recv_len = client->socket_ptr->read_some(boost::asio::buffer(buffer, sizeof(buffer))); 75 | 76 | EXPECT_EQ(recv_len, sizeof(buffer)); 77 | 78 | EXPECT_EQ(::htonl(buffer[0]), 1 * CONTROL::POS_ZOOM_RATIO); 79 | EXPECT_EQ(::htonl(buffer[1]), 2 * CONTROL::POS_ZOOM_RATIO); 80 | EXPECT_EQ(::htonl(buffer[2]), 3 * CONTROL::POS_ZOOM_RATIO); 81 | EXPECT_EQ(::htonl(buffer[3]), 4 * CONTROL::POS_ZOOM_RATIO); 82 | EXPECT_EQ(::htonl(buffer[4]), 5 * CONTROL::POS_ZOOM_RATIO); 83 | EXPECT_EQ(::htonl(buffer[5]), 6 * CONTROL::POS_ZOOM_RATIO); 84 | 85 | // time 86 | EXPECT_EQ(::htonl(buffer[18]), 100 * CONTROL::TIME_ZOOM_RATIO); 87 | // blend 88 | EXPECT_EQ(::htonl(buffer[19]), 1.230 * CONTROL::POS_ZOOM_RATIO); 89 | // mode 90 | EXPECT_EQ(::htonl(buffer[20]), (int)TrajectoryMotionType::CARTESIAN); 91 | 92 | int send_result = (int)TrajectoryMotionResult::SUCCESS; 93 | client->socket_ptr->send(boost::asio::buffer(&send_result, sizeof(int))); 94 | 95 | std::this_thread::sleep_for(50ms); 96 | 97 | EXPECT_EQ(motion_result, (TrajectoryMotionResult)send_result); 98 | 99 | } 100 | 101 | TEST(TRAJECTORY_INTERFACE, disconnect) { 102 | std::unique_ptr trajectory_ins; 103 | 104 | trajectory_ins.reset(new TrajectoryInterface(TRAJECTORY_INTERFACE_TEST_PORT)); 105 | 106 | std::unique_ptr client; 107 | client.reset(new TcpClient()); 108 | EXPECT_NO_THROW(client->connect("127.0.0.1", TRAJECTORY_INTERFACE_TEST_PORT)); 109 | 110 | std::this_thread::sleep_for(50ms); 111 | 112 | EXPECT_TRUE(trajectory_ins->isRobotConnect()); 113 | 114 | client.reset(); 115 | 116 | std::this_thread::sleep_for(50ms); 117 | 118 | EXPECT_FALSE(trajectory_ins->isRobotConnect()); 119 | 120 | } 121 | 122 | int main(int argc, char** argv) { 123 | testing::InitGoogleTest(&argc, argv); 124 | return RUN_ALL_TESTS(); 125 | } 126 | --------------------------------------------------------------------------------