├── .clang-format
├── .github
└── workflows
│ ├── cmake.yml
│ ├── cpack.yml
│ └── gtest.yml
├── .gitignore
├── .gitmodules
├── CMakeLists.txt
├── Doxyfile
├── FUNDING.yml
├── LICENSE
├── README.md
├── cmake
├── cpack.cmake
└── libsockcanppConfig.cmake
├── include
├── CMakeLists.txt
├── CanDriver.hpp
├── CanId.hpp
├── CanMessage.hpp
└── exceptions
│ ├── CanCloseException.hpp
│ ├── CanException.hpp
│ ├── CanInitException.hpp
│ └── InvalidSocketException.hpp
├── libsockcanpp.pc.in
├── src
├── CMakeLists.txt
└── CanDriver.cpp
├── test
├── CMakeLists.txt
├── app
│ ├── CMakeLists.txt
│ └── src
│ │ └── Main.cpp
└── unit
│ ├── .gitignore
│ ├── CMakeLists.txt
│ └── src
│ ├── CanId_Tests.cpp
│ └── main.cpp
└── toolchains
├── arm64-toolchain.cmake
├── armhf-toolchain.cmake
├── i686-toolchain.cmake
├── ppc-toolchain.cmake
├── ppc64-toolchain.cmake
└── x86_64-toolchain.cmake
/.clang-format:
--------------------------------------------------------------------------------
1 | AccessModifierOffset: 0
2 | AlignConsecutiveAssignments: true
3 | AllowShortBlocksOnASingleLine: true
4 | AllowShortFunctionsOnASingleLine: All
5 | AllowShortLambdasOnASingleLine: All
6 | AllowShortLoopsOnASingleLine: true
7 | BasedOnStyle: Google
8 | BreakBeforeBraces: Custom
9 | BraceWrapping:
10 | #AfterCaseLabel: Never
11 | AfterClass: false
12 | AfterControlStatement: false
13 | AfterEnum: false
14 | AfterFunction: false
15 | AfterNamespace: false
16 | AfterStruct: false
17 | AfterUnion: false
18 | AfterExternBlock: false
19 | BeforeCatch: false
20 | BeforeElse: false
21 | IndentBraces: false
22 | SplitEmptyRecord: false
23 | SplitEmptyNamespace: false
24 | BreakBeforeBinaryOperators: None
25 | BreakBeforeBraces: Attach
26 | BreakBeforeTernaryOperators: false
27 | BreakConstructorInitializers: AfterColon
28 | BreakInheritanceList: AfterColon
29 | ColumnLimit: 160
30 | CompactNamespaces: false
31 | ConstructorInitializerAllOnOneLineOrOnePerLine: false
32 | ConstructorInitializerIndentWidth: 4
33 | Cpp11BracedListStyle: false
34 | FixNamespaceComments: true
35 | IncludeBlocks: Regroup
36 | IndentCaseLabels: true
37 | #IndentGotoLabels: false # Clang 10
38 | IndentPPDirectives: BeforeHash
39 | IndentWidth: 4
40 | Language: Cpp
41 | MaxEmptyLinesToKeep: 1
42 | NamespaceIndentation: All
43 | PointerAlignment: Left
44 | SortIncludes: true
45 | SortUsingDeclarations: true
46 | SpaceAfterCStyleCast: false
47 | SpaceAfterLogicalNot: false
48 | SpaceAfterTemplateKeyword: false
49 | SpaceBeforeAssignmentOperators: true
50 | SpaceBeforeCpp11BracedList: true
51 | SpaceBeforeCtorInitializerColon: false
52 | SpaceBeforeInheritanceColon: false
53 | SpaceBeforeParens: ControlStatements
54 | SpaceBeforeRangeBasedForLoopColon: true
55 | #SpaceInEmptyBlock: true # Clang 10
56 | SpaceInEmptyParentheses: false
57 | SpacesBeforeTrailingComments: 1
58 | SpacesInAngles: false
59 | SpacesInCStyleCastParentheses: false
60 | SpacesInContainerLiterals: true
61 | SpacesInParentheses: false
62 | SpacesInSquareBrackets: false
63 | Standard: Auto
64 | TabWidth: 4
65 | UseTab: Never
--------------------------------------------------------------------------------
/.github/workflows/cmake.yml:
--------------------------------------------------------------------------------
1 | name: Build Debug
2 |
3 | on:
4 | push:
5 | branches: [ "master" ]
6 | pull_request:
7 | branches: [ "master" ]
8 |
9 | env:
10 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
11 | BUILD_TYPE: Debug
12 |
13 | jobs:
14 | build:
15 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
16 | # You can convert this to a matrix build if you need cross-platform coverage.
17 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
18 | runs-on: ubuntu-latest
19 |
20 | steps:
21 | - name: Install system dependencies
22 | if: runner.os == 'Linux'
23 | run: |
24 | sudo apt update
25 | sudo apt-get update
26 | sudo apt-get -y install can-utils libsocketcan-dev
27 | while read -r cmd
28 | do
29 | eval sudo $cmd
30 | done < <(Rscript -e 'writeLines(remotes::system_requirements("ubuntu", "20.04"))')
31 |
32 | - uses: actions/checkout@v3
33 |
34 | - name: Configure CMake
35 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
36 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
37 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
38 |
39 | - name: Build
40 | # Build your program with the given configuration
41 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
42 |
43 |
--------------------------------------------------------------------------------
/.github/workflows/cpack.yml:
--------------------------------------------------------------------------------
1 | name: Build and Pack
2 |
3 | on:
4 | push:
5 | branches: [ "master" ]
6 | pull_request:
7 | branches: [ "master" ]
8 |
9 | env:
10 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
11 | BUILD_TYPE: Release
12 |
13 | jobs:
14 | build:
15 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
16 | # You can convert this to a matrix build if you need cross-platform coverage.
17 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
18 | runs-on: ubuntu-latest
19 |
20 | steps:
21 | - name: Install system dependencies
22 | if: runner.os == 'Linux'
23 | run: |
24 | sudo apt update
25 | sudo apt-get update
26 | sudo apt-get -y install can-utils libsocketcan-dev
27 | while read -r cmd
28 | do
29 | eval sudo $cmd
30 | done < <(Rscript -e 'writeLines(remotes::system_requirements("ubuntu", "20.04"))')
31 |
32 | - uses: actions/checkout@v3
33 |
34 | - name: Configure CMake
35 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
36 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
37 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
38 |
39 | - name: Build
40 | # Build your program with the given configuration
41 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
42 |
43 | - name: Pack
44 | working-directory: ${{github.workspace}}/build
45 | # Execute tests defined by the CMake configuration.
46 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
47 | run: cpack .
48 |
49 |
--------------------------------------------------------------------------------
/.github/workflows/gtest.yml:
--------------------------------------------------------------------------------
1 | name: Build and Test
2 |
3 | on:
4 | push:
5 | branches: [ "master" ]
6 | pull_request:
7 | branches: [ "master" ]
8 |
9 | env:
10 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
11 | BUILD_TYPE: Debug
12 |
13 | jobs:
14 | build:
15 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
16 | # You can convert this to a matrix build if you need cross-platform coverage.
17 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
18 | runs-on: ubuntu-latest
19 |
20 | steps:
21 | - name: Install system dependencies
22 | if: runner.os == 'Linux'
23 | run: |
24 | sudo apt update
25 | sudo apt -y install can-utils libsocketcan-dev googletest
26 | while read -r cmd
27 | do
28 | eval sudo $cmd
29 | done < <(Rscript -e 'writeLines(remotes::system_requirements("ubuntu", "20.04"))')
30 |
31 | - uses: actions/checkout@v3
32 | - uses: MarkusJx/googletest-installer@v1.1
33 |
34 | - name: Configure CMake
35 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
36 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
37 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILD_TESTS=ON
38 |
39 | - name: Build
40 | # Build your program with the given configuration
41 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
42 |
43 | - name: Test
44 | working-directory: ${{github.workspace}}/build
45 | # Execute tests defined by the CMake configuration.
46 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
47 | run: ctest -C ${{env.BUILD_TYPE}}
48 |
49 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #################################################
2 | ### .GITIGNORE FOR SOCKETCANPP ###
3 | #################################################
4 |
5 | ###
6 | # Build directories
7 | ###
8 | build*/
9 |
10 | ###
11 | # Documentation (Doxygen)
12 | ###
13 | html/
14 |
15 | ###
16 | # cpack packages
17 | ###
18 | packages/
19 |
20 | ###
21 | # .vscode
22 | ###
23 | .vscode/
24 |
25 | ###
26 | # Binaries
27 | ###
28 | *.a
29 | *.so
30 | *.lib
31 | *.bin
32 | *.exe
33 | *.dll
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "doxygen-awesome-css"]
2 | path = doxygen-awesome-css
3 | url = https://github.com/jothepro/doxygen-awesome-css.git
4 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.23)
2 |
3 | project(sockcanpp LANGUAGES CXX VERSION 1.5.0)
4 |
5 | option(BUILD_SHARED_LIBS "Build shared libraries (.so) instead of static ones (.a)" ON)
6 | option(BUILD_TESTS "Build the tests" OFF)
7 |
8 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
9 |
10 | option(sockcanpp_CONCEPT_SUPPORT "Allow higher level language support" ON)
11 |
12 | if (sockcanpp_CONCEPT_SUPPORT)
13 | message(STATUS "Enabling support for higher level language features")
14 | set(CMAKE_CXX_STANDARD 20)
15 | else()
16 | set(CMAKE_CXX_STANDARD 11)
17 | endif()
18 |
19 | add_compile_options(
20 | -Wall
21 | -Werror
22 | -Wpedantic
23 | -Wno-unknown-pragmas
24 | )
25 |
26 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g")
27 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -s")
28 |
29 | include(GNUInstallDirs)
30 |
31 | if (BUILD_SHARED_LIBS STREQUAL "ON")
32 | add_library(${PROJECT_NAME})
33 | set_target_properties(${PROJECT_NAME} PROPERTIES SOVERSION ${PROJECT_VERSION})
34 | set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "")
35 | else()
36 | add_library(${PROJECT_NAME} STATIC)
37 | endif()
38 |
39 | ###
40 | # If BUILD_TESTS is set to ON, a static test library with the name of the project suffixed with "_test" will be created
41 | ###
42 | if(BUILD_TESTS STREQUAL "ON")
43 | add_library(${PROJECT_NAME}_test STATIC)
44 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/test/)
45 | endif()
46 |
47 | add_subdirectory(include)
48 | add_subdirectory(src)
49 |
50 | install(TARGETS ${PROJECT_NAME}
51 | EXPORT ${PROJECT_NAME}Targets
52 | FILE_SET HEADERS
53 | )
54 |
55 | install(EXPORT ${PROJECT_NAME}Targets
56 | FILE ${PROJECT_NAME}Targets.cmake
57 | NAMESPACE ${PROJECT_NAME}::
58 | DESTINATION lib/cmake/${PROJECT_NAME}
59 | )
60 |
61 | include(CMakePackageConfigHelpers)
62 | write_basic_package_version_file(
63 | "lib${PROJECT_NAME}ConfigVersion.cmake"
64 | VERSION ${${PROJECT_NAME}_VERSION}
65 | COMPATIBILITY AnyNewerVersion
66 | )
67 |
68 | install(FILES "cmake/lib${PROJECT_NAME}Config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/lib${PROJECT_NAME}ConfigVersion.cmake"
69 | DESTINATION lib/cmake/${PROJECT_NAME})
70 |
71 |
72 | set(prefix ${CMAKE_INSTALL_PREFIX})
73 | set(exec_prefix ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR})
74 | set(includedir ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR})
75 | set(libdir ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR})
76 |
77 | configure_file(libsockcanpp.pc.in ${CMAKE_BINARY_DIR}/libsockcanpp.pc @ONLY)
78 | # Install pkg-config files
79 | install(FILES ${CMAKE_BINARY_DIR}/libsockcanpp.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
80 |
81 | include(cmake/cpack.cmake)
82 |
83 | ###
84 | # Docs target
85 | ###
86 | add_custom_target("libsockcan_docs" COMMENT "Create Doxygen documentation")
87 | add_custom_command(
88 | TARGET "libsockcan_docs"
89 | POST_BUILD
90 | COMMENT "Generate Doxygen documentation for publication or reading"
91 | COMMAND doxygen ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile
92 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
93 | )
94 |
--------------------------------------------------------------------------------
/Doxyfile:
--------------------------------------------------------------------------------
1 | # Doxyfile 1.9.1
2 |
3 | #---------------------------------------------------------------------------
4 | # Project related configuration options
5 | #---------------------------------------------------------------------------
6 | DOXYFILE_ENCODING = UTF-8
7 | PROJECT_NAME = "Libsockcanpp"
8 | PROJECT_NUMBER =
9 | PROJECT_BRIEF = "A complete C++ wrapper around socketcan."
10 | PROJECT_LOGO =
11 | OUTPUT_DIRECTORY =
12 | CREATE_SUBDIRS = YES
13 | ALLOW_UNICODE_NAMES = YES
14 | OUTPUT_LANGUAGE = English
15 | OUTPUT_TEXT_DIRECTION = None
16 | BRIEF_MEMBER_DESC = YES
17 | REPEAT_BRIEF = YES
18 | ABBREVIATE_BRIEF = "The $name class" \
19 | "The $name widget" \
20 | "The $name file" \
21 | is \
22 | provides \
23 | specifies \
24 | contains \
25 | represents \
26 | a \
27 | an \
28 | the
29 | ALWAYS_DETAILED_SEC = NO
30 | INLINE_INHERITED_MEMB = NO
31 | FULL_PATH_NAMES = YES
32 | STRIP_FROM_PATH =
33 | STRIP_FROM_INC_PATH =
34 | SHORT_NAMES = NO
35 | JAVADOC_AUTOBRIEF = NO
36 | JAVADOC_BANNER = NO
37 | QT_AUTOBRIEF = NO
38 | MULTILINE_CPP_IS_BRIEF = NO
39 | PYTHON_DOCSTRING = YES
40 | INHERIT_DOCS = YES
41 | SEPARATE_MEMBER_PAGES = NO
42 | TAB_SIZE = 4
43 | ALIASES =
44 | OPTIMIZE_OUTPUT_FOR_C = YES
45 | OPTIMIZE_OUTPUT_JAVA = NO
46 | OPTIMIZE_FOR_FORTRAN = NO
47 | OPTIMIZE_OUTPUT_VHDL = NO
48 | OPTIMIZE_OUTPUT_SLICE = NO
49 | EXTENSION_MAPPING =
50 | MARKDOWN_SUPPORT = YES
51 | TOC_INCLUDE_HEADINGS = 5
52 | AUTOLINK_SUPPORT = YES
53 | BUILTIN_STL_SUPPORT = NO
54 | CPP_CLI_SUPPORT = YES
55 | SIP_SUPPORT = NO
56 | IDL_PROPERTY_SUPPORT = YES
57 | DISTRIBUTE_GROUP_DOC = NO
58 | GROUP_NESTED_COMPOUNDS = NO
59 | SUBGROUPING = YES
60 | INLINE_GROUPED_CLASSES = NO
61 | INLINE_SIMPLE_STRUCTS = NO
62 | TYPEDEF_HIDES_STRUCT = NO
63 | LOOKUP_CACHE_SIZE = 0
64 | NUM_PROC_THREADS = 1
65 | #---------------------------------------------------------------------------
66 | # Build related configuration options
67 | #---------------------------------------------------------------------------
68 | EXTRACT_ALL = YES
69 | EXTRACT_PRIVATE = YES
70 | EXTRACT_PRIV_VIRTUAL = YES
71 | EXTRACT_PACKAGE = YES
72 | EXTRACT_STATIC = YES
73 | EXTRACT_LOCAL_CLASSES = YES
74 | EXTRACT_LOCAL_METHODS = YES
75 | EXTRACT_ANON_NSPACES = YES
76 | RESOLVE_UNNAMED_PARAMS = YES
77 | HIDE_UNDOC_MEMBERS = NO
78 | HIDE_UNDOC_CLASSES = NO
79 | HIDE_FRIEND_COMPOUNDS = NO
80 | HIDE_IN_BODY_DOCS = NO
81 | INTERNAL_DOCS = NO
82 | CASE_SENSE_NAMES = YES
83 | HIDE_SCOPE_NAMES = NO
84 | HIDE_COMPOUND_REFERENCE= NO
85 | SHOW_INCLUDE_FILES = YES
86 | SHOW_GROUPED_MEMB_INC = NO
87 | FORCE_LOCAL_INCLUDES = NO
88 | INLINE_INFO = YES
89 | SORT_MEMBER_DOCS = YES
90 | SORT_BRIEF_DOCS = NO
91 | SORT_MEMBERS_CTORS_1ST = NO
92 | SORT_GROUP_NAMES = NO
93 | SORT_BY_SCOPE_NAME = NO
94 | STRICT_PROTO_MATCHING = NO
95 | GENERATE_TODOLIST = YES
96 | GENERATE_TESTLIST = YES
97 | GENERATE_BUGLIST = YES
98 | GENERATE_DEPRECATEDLIST= YES
99 | ENABLED_SECTIONS =
100 | MAX_INITIALIZER_LINES = 30
101 | SHOW_USED_FILES = YES
102 | SHOW_FILES = YES
103 | SHOW_NAMESPACES = YES
104 | FILE_VERSION_FILTER =
105 | LAYOUT_FILE =
106 | CITE_BIB_FILES =
107 | #---------------------------------------------------------------------------
108 | # Configuration options related to warning and progress messages
109 | #---------------------------------------------------------------------------
110 | QUIET = NO
111 | WARNINGS = YES
112 | WARN_IF_UNDOCUMENTED = YES
113 | WARN_IF_DOC_ERROR = YES
114 | WARN_NO_PARAMDOC = NO
115 | WARN_AS_ERROR = NO
116 | WARN_FORMAT = "$file:$line: $text"
117 | WARN_LOGFILE =
118 | #---------------------------------------------------------------------------
119 | # Configuration options related to the input files
120 | #---------------------------------------------------------------------------
121 | INPUT = README.md include/ include/exceptions/ src/ test/
122 | INPUT_ENCODING = UTF-8
123 | FILE_PATTERNS = *.c \
124 | *.cc \
125 | *.cxx \
126 | *.cpp \
127 | *.c++ \
128 | *.java \
129 | *.ii \
130 | *.ixx \
131 | *.ipp \
132 | *.i++ \
133 | *.inl \
134 | *.idl \
135 | *.ddl \
136 | *.odl \
137 | *.h \
138 | *.hh \
139 | *.hxx \
140 | *.hpp \
141 | *.h++ \
142 | *.cs \
143 | *.d \
144 | *.php \
145 | *.php4 \
146 | *.php5 \
147 | *.phtml \
148 | *.inc \
149 | *.m \
150 | *.markdown \
151 | *.md \
152 | *.mm \
153 | *.dox \
154 | *.py \
155 | *.pyw \
156 | *.f90 \
157 | *.f95 \
158 | *.f03 \
159 | *.f08 \
160 | *.f18 \
161 | *.f \
162 | *.for \
163 | *.vhd \
164 | *.vhdl \
165 | *.ucf \
166 | *.qsf \
167 | *.ice
168 | RECURSIVE = NO
169 | EXCLUDE =
170 | EXCLUDE_SYMLINKS = NO
171 | EXCLUDE_PATTERNS =
172 | EXCLUDE_SYMBOLS =
173 | EXAMPLE_PATH =
174 | EXAMPLE_PATTERNS = *
175 | EXAMPLE_RECURSIVE = NO
176 | IMAGE_PATH =
177 | INPUT_FILTER =
178 | FILTER_PATTERNS =
179 | FILTER_SOURCE_FILES = NO
180 | FILTER_SOURCE_PATTERNS =
181 | USE_MDFILE_AS_MAINPAGE = README.md
182 | #---------------------------------------------------------------------------
183 | # Configuration options related to source browsing
184 | #---------------------------------------------------------------------------
185 | SOURCE_BROWSER = YES
186 | INLINE_SOURCES = YES
187 | STRIP_CODE_COMMENTS = YES
188 | REFERENCED_BY_RELATION = YES
189 | REFERENCES_RELATION = YES
190 | REFERENCES_LINK_SOURCE = YES
191 | SOURCE_TOOLTIPS = YES
192 | USE_HTAGS = NO
193 | VERBATIM_HEADERS = YES
194 | CLANG_ASSISTED_PARSING = YES
195 | CLANG_ADD_INC_PATHS = YES
196 | CLANG_OPTIONS =
197 | CLANG_DATABASE_PATH =
198 | #---------------------------------------------------------------------------
199 | # Configuration options related to the alphabetical class index
200 | #---------------------------------------------------------------------------
201 | ALPHABETICAL_INDEX = YES
202 | IGNORE_PREFIX =
203 | #---------------------------------------------------------------------------
204 | # Configuration options related to the HTML output
205 | #---------------------------------------------------------------------------
206 | GENERATE_HTML = YES
207 | HTML_OUTPUT = html
208 | HTML_FILE_EXTENSION = .html
209 | HTML_HEADER =
210 | HTML_FOOTER =
211 | HTML_STYLESHEET =
212 | HTML_EXTRA_STYLESHEET = doxygen-awesome-css/doxygen-awesome.css
213 | HTML_EXTRA_FILES =
214 | HTML_COLORSTYLE_HUE = 220
215 | HTML_COLORSTYLE_SAT = 100
216 | HTML_COLORSTYLE_GAMMA = 80
217 | HTML_TIMESTAMP = NO
218 | HTML_DYNAMIC_MENUS = YES
219 | HTML_DYNAMIC_SECTIONS = NO
220 | HTML_INDEX_NUM_ENTRIES = 100
221 | GENERATE_DOCSET = NO
222 | DOCSET_FEEDNAME = "Doxygen generated docs"
223 | DOCSET_BUNDLE_ID = eu.simonc.EndlesshReportGen
224 | DOCSET_PUBLISHER_ID = eu.simonc.EndlesshReportGen
225 | DOCSET_PUBLISHER_NAME = Simon Cahill
226 | GENERATE_HTMLHELP = NO
227 | CHM_FILE =
228 | HHC_LOCATION =
229 | GENERATE_CHI = NO
230 | CHM_INDEX_ENCODING =
231 | BINARY_TOC = NO
232 | TOC_EXPAND = NO
233 | GENERATE_QHP = NO
234 | QCH_FILE =
235 | QHP_NAMESPACE = org.doxygen.Project
236 | QHP_VIRTUAL_FOLDER = doc
237 | QHP_CUST_FILTER_NAME =
238 | QHP_CUST_FILTER_ATTRS =
239 | QHP_SECT_FILTER_ATTRS =
240 | QHG_LOCATION =
241 | GENERATE_ECLIPSEHELP = NO
242 | ECLIPSE_DOC_ID = org.doxygen.Project
243 | DISABLE_INDEX = NO
244 | GENERATE_TREEVIEW = NO
245 | ENUM_VALUES_PER_LINE = 4
246 | TREEVIEW_WIDTH = 250
247 | EXT_LINKS_IN_WINDOW = NO
248 | HTML_FORMULA_FORMAT = png
249 | FORMULA_FONTSIZE = 10
250 | FORMULA_TRANSPARENT = YES
251 | FORMULA_MACROFILE =
252 | USE_MATHJAX = NO
253 | MATHJAX_FORMAT = HTML-CSS
254 | MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2
255 | MATHJAX_EXTENSIONS =
256 | MATHJAX_CODEFILE =
257 | SEARCHENGINE = YES
258 | SERVER_BASED_SEARCH = NO
259 | EXTERNAL_SEARCH = NO
260 | SEARCHENGINE_URL =
261 | SEARCHDATA_FILE = searchdata.xml
262 | EXTERNAL_SEARCH_ID =
263 | EXTRA_SEARCH_MAPPINGS =
264 | #---------------------------------------------------------------------------
265 | # Configuration options related to the LaTeX output
266 | #---------------------------------------------------------------------------
267 | GENERATE_LATEX = NO
268 | LATEX_OUTPUT = latex
269 | LATEX_CMD_NAME =
270 | MAKEINDEX_CMD_NAME = makeindex
271 | LATEX_MAKEINDEX_CMD = makeindex
272 | COMPACT_LATEX = NO
273 | PAPER_TYPE = a4
274 | EXTRA_PACKAGES =
275 | LATEX_HEADER =
276 | LATEX_FOOTER =
277 | LATEX_EXTRA_STYLESHEET =
278 | LATEX_EXTRA_FILES =
279 | PDF_HYPERLINKS = YES
280 | USE_PDFLATEX = YES
281 | LATEX_BATCHMODE = NO
282 | LATEX_HIDE_INDICES = NO
283 | LATEX_SOURCE_CODE = NO
284 | LATEX_BIB_STYLE = plain
285 | LATEX_TIMESTAMP = NO
286 | LATEX_EMOJI_DIRECTORY =
287 | #---------------------------------------------------------------------------
288 | # Configuration options related to the RTF output
289 | #---------------------------------------------------------------------------
290 | GENERATE_RTF = NO
291 | RTF_OUTPUT = rtf
292 | COMPACT_RTF = NO
293 | RTF_HYPERLINKS = NO
294 | RTF_STYLESHEET_FILE =
295 | RTF_EXTENSIONS_FILE =
296 | RTF_SOURCE_CODE = NO
297 | #---------------------------------------------------------------------------
298 | # Configuration options related to the man page output
299 | #---------------------------------------------------------------------------
300 | GENERATE_MAN = NO
301 | MAN_OUTPUT = man
302 | MAN_EXTENSION = .3
303 | MAN_SUBDIR =
304 | MAN_LINKS = NO
305 | #---------------------------------------------------------------------------
306 | # Configuration options related to the XML output
307 | #---------------------------------------------------------------------------
308 | GENERATE_XML = NO
309 | XML_OUTPUT = xml
310 | XML_PROGRAMLISTING = YES
311 | XML_NS_MEMB_FILE_SCOPE = NO
312 | #---------------------------------------------------------------------------
313 | # Configuration options related to the DOCBOOK output
314 | #---------------------------------------------------------------------------
315 | GENERATE_DOCBOOK = NO
316 | DOCBOOK_OUTPUT = docbook
317 | DOCBOOK_PROGRAMLISTING = NO
318 | #---------------------------------------------------------------------------
319 | # Configuration options for the AutoGen Definitions output
320 | #---------------------------------------------------------------------------
321 | GENERATE_AUTOGEN_DEF = NO
322 | #---------------------------------------------------------------------------
323 | # Configuration options related to the Perl module output
324 | #---------------------------------------------------------------------------
325 | GENERATE_PERLMOD = NO
326 | PERLMOD_LATEX = NO
327 | PERLMOD_PRETTY = YES
328 | PERLMOD_MAKEVAR_PREFIX =
329 | #---------------------------------------------------------------------------
330 | # Configuration options related to the preprocessor
331 | #---------------------------------------------------------------------------
332 | ENABLE_PREPROCESSING = YES
333 | MACRO_EXPANSION = NO
334 | EXPAND_ONLY_PREDEF = NO
335 | SEARCH_INCLUDES = YES
336 | INCLUDE_PATH =
337 | INCLUDE_FILE_PATTERNS =
338 | PREDEFINED =
339 | EXPAND_AS_DEFINED =
340 | SKIP_FUNCTION_MACROS = YES
341 | #---------------------------------------------------------------------------
342 | # Configuration options related to external references
343 | #---------------------------------------------------------------------------
344 | TAGFILES =
345 | GENERATE_TAGFILE =
346 | ALLEXTERNALS = NO
347 | EXTERNAL_GROUPS = YES
348 | EXTERNAL_PAGES = YES
349 | #---------------------------------------------------------------------------
350 | # Configuration options related to the dot tool
351 | #---------------------------------------------------------------------------
352 | CLASS_DIAGRAMS = YES
353 | DIA_PATH =
354 | HIDE_UNDOC_RELATIONS = YES
355 | HAVE_DOT = YES
356 | DOT_NUM_THREADS = 0
357 | DOT_FONTNAME = Helvetica
358 | DOT_FONTSIZE = 10
359 | DOT_FONTPATH =
360 | CLASS_GRAPH = YES
361 | COLLABORATION_GRAPH = YES
362 | GROUP_GRAPHS = YES
363 | UML_LOOK = NO
364 | UML_LIMIT_NUM_FIELDS = 10
365 | DOT_UML_DETAILS = NO
366 | DOT_WRAP_THRESHOLD = 17
367 | TEMPLATE_RELATIONS = NO
368 | INCLUDE_GRAPH = YES
369 | INCLUDED_BY_GRAPH = YES
370 | CALL_GRAPH = NO
371 | CALLER_GRAPH = NO
372 | GRAPHICAL_HIERARCHY = YES
373 | DIRECTORY_GRAPH = YES
374 | DOT_IMAGE_FORMAT = png
375 | INTERACTIVE_SVG = NO
376 | DOT_PATH =
377 | DOTFILE_DIRS =
378 | MSCFILE_DIRS =
379 | DIAFILE_DIRS =
380 | PLANTUML_JAR_PATH =
381 | PLANTUML_CFG_FILE =
382 | PLANTUML_INCLUDE_PATH =
383 | DOT_GRAPH_MAX_NODES = 50
384 | MAX_DOT_GRAPH_DEPTH = 0
385 | DOT_TRANSPARENT = NO
386 | DOT_MULTI_TARGETS = NO
387 | GENERATE_LEGEND = YES
388 | DOT_CLEANUP = YES
389 |
--------------------------------------------------------------------------------
/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: SimonCahill
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # libsockcanpp
2 |
3 | Welcome to the documentation for libsockcanpp!
4 | libsockcanpp is a socketcan wrapper library for C++, aimed to be easy to use without any fuss.
5 |
6 | [](https://github.com/SimonCahill/libsockcanpp/actions/workflows/cmake.yml)
7 |
8 | # Useful Links
9 |
10 | - GitHub repo: [https://github.com/SimonCahill/libsockcanpp](https://github.com/SimonCahill/libsockcanpp)
11 |
12 | # Getting Started
13 |
14 | ## Building
15 |
16 | > [!NOTE]
17 | > **C++11** SUPPORT:
18 | > This library supports modern C++ features, such as concepts in certain places.
19 | > If your project cannot support C++20 features, you can force the C++ standard back by setting
20 | > `-Dsockcanpp_CONCEPT_SUPPORT=OFF` in the command-line or `set(sockcanpp_CONCEPT_SUPPORT OFF CACHE BOOL "Force C++ standard back to 11")`
21 | > in your CMakeLists.txt.
22 |
23 | libsockcanpp was designed with use in CMake projects, but it can also easily be integrated into existing Makefile projects, as long as cmake is present on the build system.
24 |
25 | 1) clone the repository: `git clone https://github.com/SimonCahill/libsockcanpp.git`
26 | 2) create build directory: `mkdir build && cd build`
27 | 3) generate build files: `cmake .. -DCMAKE_TOOLCHAIN_FILE=../toolchains/desired-toolchain.cmake`
28 | 4) building library: `make -j`
29 |
30 | ## Incorporating into Cmake projects:
31 |
32 | 1) clone the repository: `git clone https://github.com/SimonCahill/libsockcanpp.git`
33 | 2) add the following to CMakeLists.txt
34 | ```cmake
35 | if (NOT TARGET sockcanpp)
36 | # IF you need C++11 support:
37 | # set(sockcanpp_CONCEPT_SUPPORT OFF CACHE BOOL "Force C++ standard back to 11")
38 |
39 | add_subdirectory(/path/to/libsockcanpprepo ${CMAKE_CURRENT_BUILD_DIR}/libsockcanpp)
40 | endif()
41 |
42 | # ...
43 |
44 | target_link_libraries(
45 | # ...
46 | sockcanpp
47 | )
48 | ```
49 | 3) generate and build
50 | 4) ??? profit
51 |
52 | ## Using the CAN driver
53 |
54 | libsockcanpp provides a simple interface to socketcan, which is represented by the CanDriver class.
55 | `CanDriver` handles the setup of the socket; **it does not however setup the CAN interface!**
56 |
57 | ### Instantiating CanDriver
58 |
59 | The example below describes how to instantiate a new instance of CanDriver.
60 | You can create as many instances as required, the resources are `free`'d once the instance has gone out of scope or been deleted.
61 |
62 | The following parameters are required for correct instantiation:
63 |
64 | 1) CAN interface: [v]can[0-?]
65 | 2) CAN protocol: see [linux/can.h](https://github.com/linux-can/can-utils/blob/master/include/linux/can.h) for options
66 | 3) (optional) CAN sender ID (arbitration ID)
67 |
68 | The following exceptions may be thrown if something went wrong during initialisation:
69 |
70 | - @see CanInitException
71 | - if socketcan failed to initlialise
72 | - if ioctl failed
73 | - if socket binding failed
74 |
75 | CanDriver is fully thread-safe and can be used in multi-threaded applications.
76 |
77 | ```cpp
78 | #include
79 |
80 | using sockcanpp::CanDriver;
81 |
82 | int main() {
83 | CanDriver canDriver("can0", CAN_RAW[, 0xbad]);
84 |
85 | return 0;
86 | }
87 | ```
88 |
89 | ### Using CAN IDs
90 |
91 | libsockcanpp provides a first-class datatype, @see CanId, which acts as an integer which can be either 11 or 29 bits in size.
92 | The @see CanId type is used through libsockcanpp to provide a semi fool-proof method of using CAN arbitration IDs without the pitfalls of using traditional 32-bit integers.
93 |
94 | CanId supports the following operations:
95 | - bitwise AND/OR
96 | - casting to: [u]int16, [u]int32
97 | - basic comparison:
98 | - equal to
99 | - not equal to
100 | - greater than (or equal to)
101 | - less than (or equal to)
102 | - arithmetic:
103 | - addition
104 | - subtraction
105 |
106 | ### Sending CAN frames
107 |
108 | Sending CAN frames with sockcanpp is as easy as you could imagine.
109 |
110 | 1) instantiate a @see CanDriver object
111 | 2) create a @see CanMessage
112 | 3) send message
113 |
114 | ```cpp
115 | #include
116 |
117 | using sockcanpp::CanDriver;
118 | using sockcanpp::CanId;
119 | using sockcanpp::CanMessage;
120 |
121 | void sendCanFrameExample() {
122 | CanDriver canDriver("can0", CAN_RAW[, 0xd00d]);
123 |
124 | CanMessage messageToSend(0 /*send with default ID*/, "8 bytes!" /* the data */);
125 |
126 | auto sentByteCount = canDriver.sendMessage(messageToSend[, false /* don't force extended ID */]);
127 |
128 | printf("Sent %d bytes via CAN!\n", sentByteCount);
129 | }
130 |
131 | void sendMultipleFramesExample() {
132 | CanDriver canDriver("can1", CAN_RAW[, 0 /* no default ID */]);
133 |
134 | queue messageQueue = {
135 | CanMessage(0x269, "somedata"),
136 | Canmessage(0x1e9, "moredata")
137 | };
138 |
139 | auto sentByteCount = canDriver.sendMessageQueue(messageQueue[, milliseconds(20) /* delay between frames */[, false /* don't force extended ID */]]);
140 |
141 | printf("Sent %d bytes via CAN!\n", sentByteCount);
142 |
143 | }
144 | ```
145 |
146 | ### Receiving messages via CAN
147 |
148 | Receiving CAN messages is almost as simple as sending them! Firstly, check if there are any messages in the buffer, then pull them out; either one-by-one, or all at once!
149 |
150 | ```cpp
151 | #include
152 |
153 | using sockcanpp::CanDriver;
154 | using sockcanpp::CanId;
155 | using sockcanpp::CanMessage;
156 |
157 | void receiveCanFramesExample() {
158 | CanDriver canDriver("can2", CAN_RAW[, 0 /* no default ID */]);
159 |
160 | if (canDriver.waitForMessages([milliseconds(3000) /* timeout */])) {
161 | // read a single message
162 | CanMessage receivedMessage = canDriver.readMessage();
163 |
164 | // read all available messages
165 | queue receivedMessages = canDriver.readQueuedMessages();
166 |
167 | // handle CAN frames
168 | }
169 | }
170 | ```
171 |
--------------------------------------------------------------------------------
/cmake/cpack.cmake:
--------------------------------------------------------------------------------
1 | # these are cache variables, so they could be overwritten with -D,
2 | set(CPACK_PACKAGE_NAME lib${PROJECT_NAME}
3 | CACHE STRING "The resulting package name"
4 | )
5 |
6 | # which is useful in case of packing only selected components instead of the whole thing
7 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A socketcan wrapper library for C++"
8 | CACHE STRING "Package description for the package metadata"
9 | )
10 |
11 | set(CPACK_VERBATIM_VARIABLES YES)
12 |
13 | set(CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME})
14 | SET(CPACK_OUTPUT_FILE_PREFIX "${CMAKE_SOURCE_DIR}/packages")
15 |
16 | # https://unix.stackexchange.com/a/11552/254512
17 | #set(CPACK_PACKAGING_INSTALL_PREFIX "/opt/some")#/${CMAKE_PROJECT_VERSION}")
18 |
19 | set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
20 | set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
21 | set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
22 |
23 | set(CPACK_PACKAGE_CONTACT "contact@simonc.eu")
24 | set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Simon Cahill")
25 |
26 | set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
27 | set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
28 |
29 | # package name for deb. If set, then instead of some-application-0.9.2-Linux.deb
30 | # you'll get some-application_0.9.2_amd64.deb (note the underscores too)
31 | set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT)
32 |
33 | # that is if you want every group to have its own package,
34 | # although the same will happen if this is not set (so it defaults to ONE_PER_GROUP)
35 | # and CPACK_DEB_COMPONENT_INSTALL is set to YES
36 | set(CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE)#ONE_PER_GROUP)
37 |
38 | # without this you won't be able to pack only specified component
39 | set(CPACK_DEB_COMPONENT_INSTALL YES)
40 |
41 | set(CPACK_GENERATOR TGZ DEB TZ STGZ RPM ZIP)
42 |
43 | include(CPack)
--------------------------------------------------------------------------------
/cmake/libsockcanppConfig.cmake:
--------------------------------------------------------------------------------
1 | # include(CMakeFindDependencyMacro)
2 | # find_dependency(xxx 2.0)
3 | include(${CMAKE_CURRENT_LIST_DIR}/libsockcanppTargets.cmake)
--------------------------------------------------------------------------------
/include/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.23)
2 |
3 | target_sources(${PROJECT_NAME}
4 | PUBLIC FILE_SET HEADERS
5 | BASE_DIRS ${CMAKE_CURRENT_LIST_DIR}
6 | FILES
7 | CanDriver.hpp
8 | CanId.hpp
9 | CanMessage.hpp
10 | exceptions/CanCloseException.hpp
11 | exceptions/CanException.hpp
12 | exceptions/CanInitException.hpp
13 | exceptions/InvalidSocketException.hpp
14 | )
15 |
16 | if (TARGET sockcanpp_test)
17 | target_sources(sockcanpp_test
18 | PUBLIC FILE_SET HEADERS
19 | BASE_DIRS ${CMAKE_CURRENT_LIST_DIR}
20 | FILES
21 | CanDriver.hpp
22 | CanId.hpp
23 | CanMessage.hpp
24 | exceptions/CanCloseException.hpp
25 | exceptions/CanException.hpp
26 | exceptions/CanInitException.hpp
27 | exceptions/InvalidSocketException.hpp
28 | )
29 | endif()
30 |
--------------------------------------------------------------------------------
/include/CanDriver.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @file CanDriver.hpp
3 | * @author Simon Cahill (contact@simonc.eu)
4 | * @brief Contains the declarations for the SocketCAN wrapper in C++.
5 | * @version 0.1
6 | * @date 2020-07-01
7 | *
8 | * @copyright Copyright (c) 2020-2025 Simon Cahill
9 | *
10 | * Licensed under the Apache License, Version 2.0 (the "License");
11 | * you may not use this file except in compliance with the License.
12 | * You may obtain a copy of the License at
13 | *
14 | * http://www.apache.org/licenses/LICENSE-2.0
15 | *
16 | * Unless required by applicable law or agreed to in writing, software
17 | * distributed under the License is distributed on an "AS IS" BASIS,
18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 | * See the License for the specific language governing permissions and
20 | * limitations under the License.
21 | */
22 |
23 | #ifndef LIBSOCKCANPP_INCLUDE_CANDRIVER_HPP
24 | #define LIBSOCKCANPP_INCLUDE_CANDRIVER_HPP
25 |
26 | //////////////////////////////
27 | // SYSTEM INCLUDES //
28 | //////////////////////////////
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 |
35 | //////////////////////////////
36 | // LOCAL INCLUDES //
37 | //////////////////////////////
38 | #include "CanId.hpp"
39 | #include "CanMessage.hpp"
40 |
41 | /**
42 | * @brief Main library namespace.
43 | *
44 | * This namespace contains the library's main code.
45 | */
46 | namespace sockcanpp {
47 |
48 | using namespace std::chrono_literals;
49 |
50 | using std::chrono::microseconds;
51 | using std::chrono::milliseconds;
52 | using std::chrono::nanoseconds;
53 | using std::mutex;
54 | using std::string;
55 | using std::queue;
56 | using std::unordered_map;
57 |
58 | using filtermap_t = unordered_map;
59 |
60 | /**
61 | * @brief CanDriver class; handles communication via CAN.
62 | *
63 | * This class provides the means of easily communicating with other devices via CAN in C++.
64 | *
65 | * @remarks
66 | * This class may be inherited by other applications and modified to suit your needs.
67 | */
68 | class CanDriver {
69 | public: // +++ Static +++
70 | static constexpr int32_t CAN_MAX_DATA_LENGTH = 8; //!< The maximum amount of bytes allowed in a single CAN frame
71 | static constexpr int32_t CAN_SOCK_RAW = CAN_RAW; //!< The raw CAN protocol
72 | static constexpr int32_t CAN_SOCK_SEVEN = 7; //!< A separate CAN protocol, used by certain embedded device OEMs.
73 |
74 | public: // +++ Constructor / Destructor +++
75 | CanDriver(const string& canInterface, const int32_t canProtocol, const CanId defaultSenderId = 0); //!< Constructor
76 | CanDriver(const string& canInterface, const int32_t canProtocol, const int32_t filterMask, const CanId defaultSenderId = 0);
77 | CanDriver(const string& canInterface, const int32_t canProtocol, const filtermap_t& filters, const CanId defaultSenderId = 0);
78 | CanDriver() = default;
79 | virtual ~CanDriver() { uninitialiseSocketCan(); } //!< Destructor
80 |
81 | public: // +++ Getter / Setter +++
82 | CanDriver& setDefaultSenderId(const CanId& id) { this->_defaultSenderId = id; return *this; } //!< Sets the default sender ID
83 |
84 | CanId getDefaultSenderId() const { return this->_defaultSenderId; } //!< Gets the default sender ID
85 |
86 | filtermap_t getFilterMask() const { return this->_canFilterMask; } //!< Gets the filter mask used by this instance
87 |
88 | int32_t getMessageQueueSize() const { return this->_queueSize; } //!< Gets the amount of CAN messages found after last calling waitForMessages()
89 | int32_t getSocketFd() const { return this->_socketFd; } //!< The socket file descriptor used by this instance.
90 |
91 | string getCanInterface() const { return this->_canInterface; } //!< The CAN interface used by this instance.
92 |
93 | public: // +++ I/O +++
94 | virtual bool waitForMessages(microseconds timeout = 3000us); //!< Waits for CAN messages to appear
95 | virtual bool waitForMessages(milliseconds timeout = 3000ms); //!< Waits for CAN messages to appear
96 | virtual bool waitForMessages(nanoseconds timeout = 3000ns); //!< Waits for CAN messages to appear
97 |
98 | virtual CanMessage readMessage(); //!< Attempts to read a single message from the bus
99 |
100 | virtual ssize_t sendMessage(const CanMessage& message, bool forceExtended = false); //!< Attempts to send a single CAN message
101 | virtual ssize_t sendMessageQueue(queue& messages, microseconds delay = 20us, bool forceExtended = false); //!< Attempts to send a queue of messages
102 | virtual ssize_t sendMessageQueue(queue& messages, milliseconds delay = 20ms, bool forceExtended = false); //!< Attempts to send a queue of messages
103 | virtual ssize_t sendMessageQueue(queue& messages, nanoseconds delay = 20ns, bool forceExtended = false); //!< Attempts to send a queue of messages
104 |
105 | virtual queue readQueuedMessages(); //!< Attempts to read all queued messages from the bus
106 |
107 | public: // +++ Socket Management +++
108 | virtual void allowCanFdFrames(const bool enabled = true) const; //!< Sets the CAN FD frame option for the interface
109 | #ifdef CANXL_XLF
110 | virtual void allowCanXlFrames(const bool enabled = true) const; //!< Sets the CAN XL frame option for the interface
111 | #endif // CANXL_XLF
112 | virtual void joinCanFilters() const; //!< Configures the socket to join the CAN filters
113 | virtual void setCanFilterMask(const int32_t mask, const CanId& filterId); //!< Attempts to set a new CAN filter mask to the interface
114 | virtual void setCanFilters(const filtermap_t& filters); //!< Sets the CAN filters for the interface
115 | virtual void setErrorFilter(const bool enabled = true) const; //!< Sets the error filter for the interface
116 | virtual void setReceiveOwnMessages(const bool enabled = true) const; //!< Sets the receive own messages option for the interface
117 |
118 | protected: // +++ Socket Management +++
119 | virtual void initialiseSocketCan(); //!< Initialises socketcan
120 | virtual void uninitialiseSocketCan(); //!< Uninitialises socketcan
121 |
122 | private: // +++ Member Functions +++
123 | virtual CanMessage readMessageLock(bool const lock = true); //!< readMessage deadlock guard
124 |
125 | private: // +++ Variables +++
126 |
127 | CanId _defaultSenderId; //!< The ID to send messages with if no other ID was set.
128 |
129 | filtermap_t _canFilterMask; //!< The bit mask used to filter CAN messages
130 |
131 | int32_t _canProtocol; //!< The protocol used when communicating via CAN
132 | int32_t _socketFd{-1}; //!< The CAN socket file descriptor
133 | int32_t _queueSize{0}; ///!< The size of the message queue read by waitForMessages()
134 | bool _canReadQueueSize{true}; ///!< Is the queue size available
135 |
136 | //!< Mutex for thread-safety.
137 | mutex _lock{};
138 | mutex _lockSend{};
139 |
140 | string _canInterface; //!< The CAN interface used for communication (e.g. can0, can1, ...)
141 |
142 | };
143 |
144 | /**
145 | * @brief Formats a std string object.
146 | *
147 | * @remarks Yoinked from https://github.com/Beatsleigher/liblogpp :)
148 | *
149 | * @tparam Args The formatting argument types.
150 | * @param format The format string.
151 | * @param args The format arguments (strings must be converted to C-style strings!)
152 | *
153 | * @return string The formatted string.
154 | */
155 | template
156 | string formatString(const string& format, Args... args) {
157 | using std::unique_ptr;
158 | auto stringSize = snprintf(nullptr, 0, format.c_str(), args...) + 1; // +1 for \0
159 | unique_ptr buffer(new char[stringSize]);
160 |
161 | snprintf(buffer.get(), stringSize, format.c_str(), args...);
162 |
163 | return string(buffer.get(), buffer.get() + stringSize - 1); // std::string handles termination for us.
164 | }
165 |
166 | }
167 |
168 | #endif // LIBSOCKCANPP_INCLUDE_CANDRIVER_HPP
169 |
--------------------------------------------------------------------------------
/include/CanId.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @file CanId.hpp
3 | * @author Simon Cahill (contact@simonc.eu)
4 | * @brief Contains the implementation of a value-type representing a CAN identifier.
5 | * @version 0.1
6 | * @date 2020-07-01
7 | *
8 | * @copyright Copyright (c) 2020-2025 Simon Cahill
9 | *
10 | * Licensed under the Apache License, Version 2.0 (the "License");
11 | * you may not use this file except in compliance with the License.
12 | * You may obtain a copy of the License at
13 | *
14 | * http://www.apache.org/licenses/LICENSE-2.0
15 | *
16 | * Unless required by applicable law or agreed to in writing, software
17 | * distributed under the License is distributed on an "AS IS" BASIS,
18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 | * See the License for the specific language governing permissions and
20 | * limitations under the License.
21 | */
22 |
23 | #ifndef LIBSOCKPP_INCLUDE_CANID_HPP
24 | #define LIBSOCKPP_INCLUDE_CANID_HPP
25 |
26 | //////////////////////////////
27 | // SYSTEM INCLUDES //
28 | //////////////////////////////
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 |
36 | #if __cpp_concepts >= 201907
37 | template
38 | concept Stringable = requires(Str s) { { s.data() + s.size() } -> std::convertible_to; };
39 |
40 | template
41 | concept CChar = requires(CharArr c) { std::is_same_v; };
42 |
43 | template
44 | concept Integral = requires(Int i) { std::is_integral_v; };
45 |
46 | template
47 | concept ConvertibleToCanId = Stringable || Integral || CChar;
48 | #endif
49 |
50 | namespace sockcanpp {
51 |
52 | using std::bitset;
53 | using std::error_code;
54 | using std::exception;
55 | using std::generic_category;
56 | using std::system_error;
57 |
58 | /**
59 | * @brief Represents a CAN ID in a simple and easy-to-use manner.
60 | */
61 | struct CanId {
62 | public: // +++ Constructors +++
63 | constexpr CanId() = default;
64 | constexpr CanId(const canid_t id): m_identifier(id) { }
65 | constexpr CanId(const int32_t id): m_identifier(id) { }
66 |
67 | #if __cpp_concepts >= 201907
68 | template
69 | CanId(const T& id) { m_identifier = std::stoul(id.data(), nullptr, 16); }
70 | #endif // __cpp_concepts >= 201907
71 |
72 | CanId(const char* id) { m_identifier = std::stoul(id, nullptr, 16); }
73 |
74 | public: // +++ Operators +++
75 | constexpr canid_t operator *() const { return m_identifier; } //!< Returns the raw CAN ID value.
76 |
77 | #pragma region "Conversions"
78 | constexpr operator int16_t() const { return static_cast(m_identifier) & CAN_ERR_MASK; }
79 | constexpr operator uint16_t() const { return static_cast(m_identifier) & CAN_ERR_MASK; }
80 | constexpr operator int32_t() const { return m_identifier & CAN_ERR_MASK; }
81 | constexpr operator canid_t() const { return m_identifier & CAN_ERR_MASK; }
82 | #pragma endregion
83 |
84 | #pragma region "Bitwise Operators"
85 | template
86 | constexpr CanId operator &(const T x) const { return m_identifier & x; } //!< Performs a bitwise AND operation on this ID and another.
87 | constexpr CanId operator &(const CanId& x) const { return m_identifier & x.m_identifier; } //!< Performs a bitwise AND operation on this ID and another.
88 |
89 | template
90 | constexpr CanId operator |(const T x) const { return m_identifier | x; } //!< Performs a bitwise OR operation on this ID and a 16-bit integer.
91 | constexpr CanId operator |(const CanId& x) const { return m_identifier | x.m_identifier; } //!< Performs a bitwise OR operation on this ID and another.
92 |
93 | template
94 | constexpr CanId operator ^(const T x) const { return m_identifier ^ x; } //!< Performs a bitwise XOR operation on this ID and a 16-bit integer.
95 | constexpr CanId operator ^(const CanId& x) const { return m_identifier ^ x.m_identifier; } //!< Performs a bitwise XOR operation on this ID and another.
96 |
97 | constexpr CanId operator ~() const { return ~m_identifier; } //!< Performs a bitwise NOT operation on this ID.
98 |
99 | template
100 | constexpr CanId operator <<(const T x) const { return m_identifier << x; } //!< Shifts this ID to the left by a 16-bit integer.
101 | constexpr CanId operator <<(const CanId& x) const { return m_identifier << x.m_identifier; } //!< Shifts this ID to the left by another.
102 |
103 | template
104 | constexpr CanId operator >>(const T x) const { return m_identifier >> x; } //!< Shifts this ID to the right by a 16-bit integer.
105 | constexpr CanId operator >>(const CanId& x) const { return m_identifier >> x.m_identifier; } //!< Shifts this ID to the right by another.
106 |
107 | template
108 | CanId operator <<=(const T x) { return m_identifier <<= x; } //!< Shifts this ID to the left by a 16-bit integer.
109 | CanId operator <<=(const CanId& x) { return m_identifier <<= x.m_identifier; } //!< Shifts this ID to the left by another.
110 |
111 | template
112 | CanId operator >>=(const T x) { return m_identifier >>= x; } //!< Shifts this ID to the right by a 16-bit integer.
113 | CanId operator >>=(const CanId& x) { return m_identifier >>= x.m_identifier; } //!< Shifts this ID to the right by another.
114 |
115 | #pragma endregion
116 |
117 | #pragma region "Comparison Operators"
118 | constexpr bool operator ==(const CanId& x) const { return m_identifier == x.m_identifier; } //!< Compares this ID to another.
119 |
120 | template
121 | constexpr bool operator ==(const T x) const { return m_identifier == static_cast(x); } //!< Compares this ID to another.
122 |
123 | constexpr bool operator !=(const CanId& x) const { return m_identifier != x.m_identifier; } //!< Compares this ID to another.
124 |
125 | template
126 | constexpr bool operator !=(const T x) const { return m_identifier != static_cast(x); } //!< Compares this ID to another.
127 |
128 | template
129 | constexpr bool operator <(T x) const { return static_cast(x) < m_identifier; } //!< Compares this ID to another.
130 |
131 | template
132 | constexpr bool operator >(T x) const { return static_cast(x) > m_identifier; } //!< Compares this ID to a 32-bit integer.
133 |
134 | template
135 | constexpr bool operator <=(const T x) const { return x.m_identifier <= m_identifier; } //!< Compares this ID to another.
136 |
137 | template
138 | constexpr bool operator >=(const T x) const { return x.m_identifier >= m_identifier; } //!< Compares this ID to another.
139 | #pragma endregion
140 |
141 | #pragma region "Assignment Operators"
142 | template
143 | constexpr CanId operator =(const T val) { return CanId(val); } //!< Assigns a new integer to this CanID
144 |
145 | #if __cpp_concepts >= 201907
146 | template
147 | CanId operator =(const T& val) {
148 | return operator=(std::stoul(val.data(), nullptr, 16));
149 | }
150 | #endif // __cpp_concepts >= 201907
151 |
152 | constexpr CanId operator =(const int64_t val) { return operator =((canid_t)val); } //!< Assigns a 64-bit integer to this ID.
153 |
154 | template
155 | constexpr CanId operator |=(const T x) { return m_identifier |= x; } //!< Performs a bitwise OR operation on this ID and another.
156 |
157 | template
158 | constexpr CanId operator &=(const T x) { return m_identifier &= x; } //!< Performs a bitwise AND operation on this ID and another.
159 |
160 | template
161 | constexpr CanId operator ^=(const T x) { return m_identifier ^= x; } //!< Performs a bitwise XOR operation on this ID and another.
162 | #pragma endregion
163 |
164 | #pragma region "Arithmetic Operators"
165 | template
166 | constexpr CanId operator +(const T x) const { return m_identifier + x; }
167 |
168 | template
169 | constexpr CanId operator +=(const T x) { return m_identifier += x; }
170 |
171 | template
172 | constexpr CanId operator -=(const T x) { return m_identifier -= x; }
173 |
174 | template
175 | constexpr CanId operator -(const T x) const { return m_identifier - x; }
176 |
177 | template
178 | constexpr CanId operator *(const T x) const { return m_identifier * x; }
179 |
180 | template
181 | constexpr CanId operator *= (const T x) { return m_identifier *= x; }
182 |
183 | template
184 | constexpr CanId operator /(const T x) const { return m_identifier / x; }
185 |
186 | template
187 | constexpr CanId operator /=(const T x) { return m_identifier /= x; }
188 |
189 | template
190 | constexpr CanId operator %(const T x) const { return m_identifier % x; }
191 |
192 | template
193 | constexpr CanId operator %=(const T x) { return m_identifier %= x; }
194 | #pragma endregion
195 |
196 | public: // +++ Validity Checks +++
197 | /**
198 | * @brief Indicates whether or not a given integer is a valid CAN identifier.
199 | *
200 | * @param value The integer to check.
201 | *
202 | * @return true If value is a valid CAN identifier.
203 | * @return false Otherwise.
204 | */
205 | template
206 | static constexpr bool isValidIdentifier(T value) {
207 | return static_cast(value) <= CAN_EFF_MASK;
208 | }
209 |
210 | /**
211 | * @brief Indicates whether or not a given integer contains the error frame flag or not.
212 | *
213 | * @param value The integer to check.
214 | *
215 | * @return true If value has the error frame flag (bit) set to 1.
216 | * @return false Otherwise.
217 | */
218 | template
219 | static constexpr bool isErrorFrame(T value) {
220 | return static_cast(value) & CAN_ERR_FLAG;
221 | }
222 |
223 | /**
224 | * @brief Indicates whether the received frame is a remote transmission request.
225 | *
226 | * @param value The integer to check.
227 | *
228 | * @return true If the frame is a remote transmission request.
229 | * @return false Otherwise.
230 | */
231 | template
232 | static constexpr bool isRemoteTransmissionRequest(T value) {
233 | return static_cast(value) & CAN_RTR_FLAG;
234 | }
235 |
236 | /**
237 | * @brief Indicates whether or not a given integer is an extended frame ID.
238 | *
239 | * @param value The integer to check.
240 | *
241 | * @return true If the frame is in the extended format.
242 | * @return false Otherwise.
243 | */
244 | template
245 | static constexpr bool isExtendedFrame(T value) {
246 | return static_cast(value) & CAN_EFF_FLAG;
247 | }
248 |
249 | public: // +++ Getters +++
250 | constexpr bool hasErrorFrameFlag() const { return isErrorFrame(m_identifier); } //!< Indicates whether or not this ID is an error frame.
251 | constexpr bool hasRtrFrameFlag() const { return isRemoteTransmissionRequest(m_identifier); } //!< Indicates whether or not this ID is a remote transmission request.
252 | constexpr bool isStandardFrameId() const { return !isExtendedFrame(m_identifier); } //!< Indicates whether or not this ID is a standard frame ID.
253 | constexpr bool isExtendedFrameId() const { return isExtendedFrame(m_identifier); } //!< Indicates whether or not this ID is an extended frame ID.
254 |
255 | public: // +++ Equality Checks +++
256 | constexpr bool equals(const CanId& otherId) const { return m_identifier == otherId.m_identifier; } //!< Compares this ID to another.
257 |
258 | private: // +++ Variables +++
259 | uint32_t m_identifier = 0;
260 | };
261 |
262 | /**
263 | * @brief Implements a hash function for the CanId type.
264 | */
265 | struct CanIdHasher {
266 | public:
267 | size_t operator()(const CanId& id) const { return std::hash()(*id); }
268 | };
269 |
270 | }
271 |
272 | #endif // LIBSOCKPP_INCLUDE_CANID_HPP
273 |
--------------------------------------------------------------------------------
/include/CanMessage.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @file CanMessage.hpp
3 | * @author Simon Cahill (contact@simonc.eu)
4 | * @brief Contains the implementation of a CAN message representation in C++.
5 | * @version 0.1
6 | * @date 2020-07-01
7 | *
8 | * @copyright Copyright (c) 2020-2025 Simon Cahill
9 | *
10 | * Licensed under the Apache License, Version 2.0 (the "License");
11 | * you may not use this file except in compliance with the License.
12 | * You may obtain a copy of the License at
13 | *
14 | * http://www.apache.org/licenses/LICENSE-2.0
15 | *
16 | * Unless required by applicable law or agreed to in writing, software
17 | * distributed under the License is distributed on an "AS IS" BASIS,
18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 | * See the License for the specific language governing permissions and
20 | * limitations under the License.
21 | */
22 |
23 | #ifndef LIBSOCKCANPP_INCLUDE_CANMESSAGE_HPP
24 | #define LIBSOCKCANPP_INCLUDE_CANMESSAGE_HPP
25 |
26 | //////////////////////////////
27 | // SYSTEM INCLUDES //
28 | //////////////////////////////
29 | #include
30 |
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 |
37 | //////////////////////////////
38 | // LOCAL INCLUDES //
39 | //////////////////////////////
40 | #include "CanId.hpp"
41 |
42 | namespace sockcanpp {
43 |
44 | using std::error_code;
45 | using std::generic_category;
46 | using std::memcpy;
47 | using std::string;
48 | using std::system_error;
49 |
50 | /**
51 | * @brief Represents a CAN message that was received.
52 | */
53 | class CanMessage {
54 | public: // +++ Constructor / Destructor +++
55 | CanMessage(const struct can_frame frame):
56 | _canIdentifier(frame.can_id), _frameData((const char*)frame.data, frame.can_dlc), _rawFrame(frame) { }
57 |
58 | CanMessage(const CanId canId, const string& frameData): _canIdentifier(canId), _frameData(frameData) {
59 | if (frameData.size() > CAN_MAX_DLEN) {
60 | throw system_error(error_code(0xbadd1c, generic_category()), "Payload too big!");
61 | }
62 |
63 | struct can_frame rawFrame{};
64 | rawFrame.can_id = canId;
65 | std::copy(frameData.begin(), frameData.end(), rawFrame.data);
66 | rawFrame.can_dlc = frameData.size();
67 |
68 | _rawFrame = rawFrame;
69 | }
70 |
71 | virtual ~CanMessage() {}
72 |
73 | public: // +++ Getters +++
74 | const CanId getCanId() const { return this->_canIdentifier; }
75 | const string getFrameData() const { return this->_frameData; }
76 | const can_frame getRawFrame() const { return this->_rawFrame; }
77 |
78 | private:
79 | CanId _canIdentifier;
80 |
81 | string _frameData;
82 |
83 | struct can_frame _rawFrame;
84 | };
85 |
86 | }
87 |
88 | #endif // LIBSOCKCANPP_INCLUDE_CANMESSAGE_HPP
89 |
--------------------------------------------------------------------------------
/include/exceptions/CanCloseException.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @file CanCloseException.hpp
3 | * @author Simon Cahill (contact@simonc.eu)
4 | * @brief Contains the implementation of an exception that may be thrown when an error occurs while closing a CAN socket.
5 | * @version 0.1
6 | * @date 2020-07-02
7 | *
8 | * @copyright Copyright (c) 2020 Simon Cahill
9 | *
10 | * Copyright 2020 Simon Cahill
11 | *
12 | * Licensed under the Apache License, Version 2.0 (the "License");
13 | * you may not use this file except in compliance with the License.
14 | * You may obtain a copy of the License at
15 | *
16 | * http://www.apache.org/licenses/LICENSE-2.0
17 | *
18 | * Unless required by applicable law or agreed to in writing, software
19 | * distributed under the License is distributed on an "AS IS" BASIS,
20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 | * See the License for the specific language governing permissions and
22 | * limitations under the License.
23 | */
24 |
25 | #ifndef LIBSOCKCANPP_INCLUDE_EXCEPTIONS_CANCLOSEEXCEPTION_HPP
26 | #define LIBSOCKCANPP_INCLUDE_EXCEPTIONS_CANCLOSEEXCEPTION_HPP
27 |
28 | #include
29 | #include
30 |
31 | namespace sockcanpp { namespace exceptions {
32 |
33 | using std::exception;
34 | using std::string;
35 |
36 | /**
37 | * @brief An exception that may be thrown when an error occurs while closing a CAN socket.
38 | */
39 | class CanCloseException: public exception {
40 | public: // +++ Constructor / Destructor +++
41 | CanCloseException(string message): _message(message) {}
42 | ~CanCloseException() {}
43 |
44 | public: // +++ Overrides +++
45 | const char* what() const noexcept { return _message.c_str(); }
46 |
47 | private:
48 | string _message;
49 | };
50 |
51 | } /* exceptions */ } /* sockcanpp */
52 |
53 | #endif // LIBSOCKCANPP_INCLUDE_EXCEPTIONS_CANCLOSEEXCEPTION_HPP
54 |
--------------------------------------------------------------------------------
/include/exceptions/CanException.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @file CanException.hpp
3 | * @author Simon Cahill (contact@simonc.eu)
4 | * @brief Contains the implementation of a general-purpose exception which may be thrown when an error occurs when performing IO on a CAN socket.
5 | * @version 0.1
6 | * @date 2020-07-02
7 | *
8 | * @copyright Copyright (c) 2020 Simon Cahill
9 | *
10 | * Copyright 2020 Simon Cahill
11 | *
12 | * Licensed under the Apache License, Version 2.0 (the "License");
13 | * you may not use this file except in compliance with the License.
14 | * You may obtain a copy of the License at
15 | *
16 | * http://www.apache.org/licenses/LICENSE-2.0
17 | *
18 | * Unless required by applicable law or agreed to in writing, software
19 | * distributed under the License is distributed on an "AS IS" BASIS,
20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 | * See the License for the specific language governing permissions and
22 | * limitations under the License.
23 | */
24 |
25 | #ifndef LIBSOCKCANPP_INCLUDE_EXCEPTIONS_CANEXCEPTION_HPP
26 | #define LIBSOCKCANPP_INCLUDE_EXCEPTIONS_CANEXCEPTION_HPP
27 |
28 | #include
29 | #include
30 |
31 | namespace sockcanpp { namespace exceptions {
32 |
33 | using std::exception;
34 | using std::string;
35 |
36 | /**
37 | * @brief An exception that may be thrown when an error occurs while closing a CAN socket.
38 | */
39 | class CanException: public exception {
40 | public: // +++ Constructor / Destructor +++
41 | CanException(const string& message, int32_t socket): m_socket(socket), m_message(message) {}
42 | ~CanException() {}
43 |
44 | public: // +++ Overrides +++
45 | const char* what() const noexcept { return m_message.c_str(); }
46 |
47 | public: // +++ Getter +++
48 | const int32_t getSocket() const { return m_socket; }
49 |
50 | private:
51 | int32_t m_socket;
52 |
53 | string m_message;
54 | };
55 |
56 | } /* exceptions */ } /* sockcanpp */
57 |
58 | #endif // LIBSOCKCANPP_INCLUDE_EXCEPTIONS_CANEXCEPTION_HPP
59 |
--------------------------------------------------------------------------------
/include/exceptions/CanInitException.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @file CanInitException.hpp
3 | * @author Simon Cahill (contact@simonc.eu)
4 | * @brief Contains the implementation of an exception that is thrown when a CAN socket couldn't be inintialised.
5 | * @version 0.1
6 | * @date 2020-07-02
7 | *
8 | * @copyright Copyright (c) 2020 Simon Cahill
9 | *
10 | * Copyright 2020 Simon Cahill
11 | *
12 | * Licensed under the Apache License, Version 2.0 (the "License");
13 | * you may not use this file except in compliance with the License.
14 | * You may obtain a copy of the License at
15 | *
16 | * http://www.apache.org/licenses/LICENSE-2.0
17 | *
18 | * Unless required by applicable law or agreed to in writing, software
19 | * distributed under the License is distributed on an "AS IS" BASIS,
20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 | * See the License for the specific language governing permissions and
22 | * limitations under the License.
23 | */
24 |
25 | #ifndef LIBSOCKCANPP_INCLUDE_EXCEPTIONS_CANINITEXCEPTION_HPP
26 | #define LIBSOCKCANPP_INCLUDE_EXCEPTIONS_CANINITEXCEPTION_HPP
27 |
28 | #include
29 | #include
30 |
31 | namespace sockcanpp { namespace exceptions {
32 |
33 | using std::exception;
34 | using std::string;
35 |
36 | /**
37 | * @brief An exception that may be thrown when an error occurred while initialising a CAN socket.
38 | */
39 | class CanInitException: public exception {
40 | public: // +++ Constructor / Destructor +++
41 | CanInitException(string message): _message(message) { }
42 | virtual ~CanInitException() { }
43 |
44 | public: // +++ Override +++
45 | const char* what() const noexcept { return _message.c_str(); }
46 |
47 | private:
48 | string _message;
49 | };
50 |
51 | } /* exceptions */ } /* sockcanpp */
52 |
53 | #endif // LIBSOCKCANPP_INCLUDE_EXCEPTIONS_CANINITEXCEPTION_HPP
54 |
--------------------------------------------------------------------------------
/include/exceptions/InvalidSocketException.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @file InvalidSocketException.hpp
3 | * @author Simon Cahill (contact@simonc.eu)
4 | * @brief Contains the implementation of an exception that may be thrown when an invalid CAN socket is detected.
5 | * @version 0.1
6 | * @date 2020-07-02
7 | *
8 | * @copyright Copyright (c) 2020 Simon Cahill
9 | *
10 | * Copyright 2020 Simon Cahill
11 | *
12 | * Licensed under the Apache License, Version 2.0 (the "License");
13 | * you may not use this file except in compliance with the License.
14 | * You may obtain a copy of the License at
15 | *
16 | * http://www.apache.org/licenses/LICENSE-2.0
17 | *
18 | * Unless required by applicable law or agreed to in writing, software
19 | * distributed under the License is distributed on an "AS IS" BASIS,
20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 | * See the License for the specific language governing permissions and
22 | * limitations under the License.
23 | */
24 |
25 | #ifndef LIBSOCKCANPP_INCLUDE_EXCEPTIONS_INVALIDSOCKETEXCEPTION_HPP
26 | #define LIBSOCKCANPP_INCLUDE_EXCEPTIONS_INVALIDSOCKETEXCEPTION_HPP
27 |
28 | #include
29 | #include
30 |
31 | namespace sockcanpp { namespace exceptions {
32 |
33 | using std::exception;
34 | using std::string;
35 |
36 | /**
37 | * @brief An exception that may be thrown when an error occurs while closing a CAN socket.
38 | */
39 | class InvalidSocketException: public exception {
40 | public: // +++ Constructor / Destructor +++
41 | InvalidSocketException(const string& message, int32_t socket): m_socket(socket), m_message(message) {}
42 | ~InvalidSocketException() {}
43 |
44 | public: // +++ Overrides +++
45 | const char* what() const noexcept { return m_message.c_str(); }
46 |
47 | public: // +++ Getter +++
48 | const int32_t getSocket() const { return m_socket; }
49 |
50 | private:
51 | int32_t m_socket;
52 |
53 | string m_message;
54 | };
55 |
56 | } /* exceptions */ } /* sockcanpp */
57 |
58 | #endif // LIBSOCKCANPP_INCLUDE_EXCEPTIONS_INVALIDSOCKETEXCEPTION_HPP
59 |
--------------------------------------------------------------------------------
/libsockcanpp.pc.in:
--------------------------------------------------------------------------------
1 | prefix=@prefix@
2 | exec_prefix=@exec_prefix@
3 | libdir=@libdir@
4 | includedir=@includedir@
5 |
6 | Name: @PROJECT_NAME@
7 | Description: A C++ user-space CAN bus driver for Linux (using socketCAN).
8 | Version: @PROJECT_VERSION@
9 | Libs: -L${libdir} -l@PROJECT_NAME@
10 | Cflags: -I${includedir}
11 |
--------------------------------------------------------------------------------
/src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | target_sources(${PROJECT_NAME}
2 | PRIVATE
3 | ${CMAKE_CURRENT_LIST_DIR}/CanDriver.cpp
4 | )
5 |
6 | if (TARGET sockcanpp_test)
7 | target_sources(sockcanpp_test
8 | PRIVATE
9 | ${CMAKE_CURRENT_LIST_DIR}/CanDriver.cpp
10 | )
11 | endif()
12 |
13 | add_compile_options(
14 | -Wno-unknown-pragmas
15 | )
16 |
--------------------------------------------------------------------------------
/src/CanDriver.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @file CanDriver.cpp
3 | * @author Simon Cahill (contact@simonc.eu)
4 | * @brief
5 | * @version 0.1
6 | * @date 2020-07-01
7 | *
8 | * @copyright Copyright (c) 2020-2025 Simon Cahill
9 | *
10 | * Licensed under the Apache License, Version 2.0 (the "License");
11 | * you may not use this file except in compliance with the License.
12 | * You may obtain a copy of the License at
13 | *
14 | * http://www.apache.org/licenses/LICENSE-2.0
15 | *
16 | * Unless required by applicable law or agreed to in writing, software
17 | * distributed under the License is distributed on an "AS IS" BASIS,
18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 | * See the License for the specific language governing permissions and
20 | * limitations under the License.
21 | */
22 |
23 | //////////////////////////////
24 | // SYSTEM INCLUDES //
25 | //////////////////////////////
26 |
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 |
36 | #include
37 | #include
38 | #include
39 | #include
40 | #include
41 | #include
42 | #include
43 | #include
44 |
45 | //////////////////////////////
46 | // LOCAL INCLUDES //
47 | //////////////////////////////
48 | #include "CanDriver.hpp"
49 | #include "CanId.hpp"
50 | #include "CanMessage.hpp"
51 | #include "exceptions/CanCloseException.hpp"
52 | #include "exceptions/CanException.hpp"
53 | #include "exceptions/CanInitException.hpp"
54 | #include "exceptions/InvalidSocketException.hpp"
55 |
56 | namespace sockcanpp {
57 |
58 | using exceptions::CanCloseException;
59 | using exceptions::CanException;
60 | using exceptions::CanInitException;
61 | using exceptions::InvalidSocketException;
62 |
63 | using std::mutex;
64 | using std::queue;
65 | using std::string;
66 | using std::strncpy;
67 | using std::unique_lock;
68 | using std::unique_ptr;
69 | using std::chrono::milliseconds;
70 | using std::this_thread::sleep_for;
71 | using std::vector;
72 |
73 | //////////////////////////////////////
74 | // PUBLIC IMPLEMENTATION //
75 | //////////////////////////////////////
76 |
77 |
78 | #pragma region "Object Construction"
79 | CanDriver::CanDriver(const string& canInterface, int32_t canProtocol, const CanId defaultSenderId):
80 | CanDriver(canInterface, canProtocol, 0 /* match all */, defaultSenderId) { }
81 |
82 | CanDriver::CanDriver(const string& canInterface, const int32_t canProtocol, const int32_t filterMask, const CanId defaultSenderId):
83 | CanDriver(canInterface, canProtocol, filtermap_t{{0, filterMask}}, defaultSenderId) { }
84 |
85 | CanDriver::CanDriver(const string& canInterface, const int32_t canProtocol, const filtermap_t& filters, const CanId defaultSenderId):
86 | _defaultSenderId(defaultSenderId), _canFilterMask(filters), _canProtocol(canProtocol), _canInterface(canInterface) {
87 | initialiseSocketCan();
88 | }
89 | #pragma endregion
90 |
91 | #pragma region "I / O"
92 | /**
93 | * @brief Blocks until one or more CAN messages appear on the bus, or until the timeout runs out.
94 | *
95 | * @param timeout The time (in µsec) to wait before timing out.
96 | *
97 | * @return true If messages are available on the bus.
98 | * @return false Otherwise.
99 | */
100 | bool CanDriver::waitForMessages(microseconds timeout/* = microseconds(3000)*/) {
101 | if (_socketFd < 0) { throw InvalidSocketException("Invalid socket!", _socketFd); }
102 |
103 | unique_lock locky(_lock);
104 |
105 | fd_set readFileDescriptors;
106 | timeval waitTime{0, static_cast(timeout.count())};
107 |
108 | FD_ZERO(&readFileDescriptors);
109 | FD_SET(_socketFd, &readFileDescriptors);
110 | const auto fdsAvailable = select(_socketFd + 1, &readFileDescriptors, nullptr, nullptr, &waitTime);
111 |
112 | int32_t bytesAvailable{0};
113 | const auto retCode = ioctl(_socketFd, FIONREAD, &bytesAvailable);
114 | if (retCode == 0) {
115 | _queueSize = static_cast(std::ceil(bytesAvailable / sizeof(can_frame)));
116 | } else {
117 | _queueSize = 0;
118 | // vcan interfaces don't support FIONREAD. So fall back to
119 | // using alternate implementation in readQueuedMessages().
120 | _canReadQueueSize = false;
121 | }
122 |
123 | return fdsAvailable > 0;
124 | }
125 |
126 | /**
127 | * @brief Blocks until one or more CAN messages appear on the bus, or until the timeout runs out.
128 | *
129 | * @param timeout The time (in millis) to wait before timing out.
130 | *
131 | * @return true If messages are available on the bus.
132 | * @return false Otherwise.
133 | */
134 | bool CanDriver::waitForMessages(milliseconds timeout) { return waitForMessages(std::chrono::duration_cast(timeout)); }
135 |
136 | /**
137 | * @brief Blocks until one or more CAN messages appear on the bus, or until the timeout runs out.
138 | *
139 | * @param timeout The time (in nanoseconds) to wait before timing out.
140 | *
141 | * @return true If messages are available on the bus.
142 | * @return false Otherwise.
143 | */
144 | bool CanDriver::waitForMessages(nanoseconds timeout/* = nanoseconds(3000)*/) { return waitForMessages(std::chrono::duration_cast(timeout)); }
145 |
146 | /**
147 | * @brief Attempts to read a message from the associated CAN bus.
148 | *
149 | * @return CanMessage The message read from the bus.
150 | */
151 | CanMessage CanDriver::readMessage() { return readMessageLock(); }
152 |
153 | /**
154 | * @brief readMessage deadlock guard, attempts to read a message from the associated CAN bus.
155 | *
156 | * @return CanMessage The message read from the bus.
157 | */
158 | CanMessage CanDriver::readMessageLock(bool const lock) {
159 | unique_ptr> locky{nullptr};
160 |
161 | if (lock) { locky = unique_ptr>{new unique_lock{_lock}}; }
162 |
163 | if (0 > _socketFd) { throw InvalidSocketException("Invalid socket!", _socketFd); }
164 |
165 | ssize_t readBytes{0};
166 | can_frame canFrame{};
167 |
168 | readBytes = read(_socketFd, &canFrame, sizeof(can_frame));
169 |
170 | if (0 > readBytes) { throw CanException(formatString("FAILED to read from CAN! Error: %d => %s", errno, strerror(errno)), _socketFd); }
171 |
172 | return CanMessage{canFrame};
173 | }
174 |
175 | /**
176 | * @brief Attempts to send a CAN message on the associated bus.
177 | *
178 | * @param message The message to be sent.
179 | * @param forceExtended Whether or not to force use of an extended ID.
180 | *
181 | * @return ssize_t The amount of bytes sent on the bus.
182 | */
183 | ssize_t CanDriver::sendMessage(const CanMessage& message, bool forceExtended) {
184 | if (_socketFd < 0) { throw InvalidSocketException("Invalid socket!", _socketFd); }
185 |
186 | unique_lock locky(_lockSend);
187 |
188 | ssize_t bytesWritten = 0;
189 |
190 | if (message.getFrameData().size() > CAN_MAX_DATA_LENGTH) {
191 | throw CanException(formatString("INVALID data length! Message must be smaller than %d bytes!", CAN_MAX_DATA_LENGTH), _socketFd);
192 | }
193 |
194 | auto canFrame = message.getRawFrame();
195 |
196 | if (forceExtended || ((uint32_t)message.getCanId() > CAN_SFF_MASK)) { canFrame.can_id |= CAN_EFF_FLAG; }
197 |
198 | bytesWritten = write(_socketFd, (const void*)&canFrame, sizeof(canFrame));
199 |
200 | if (bytesWritten == -1) { throw CanException(formatString("FAILED to write data to socket! Error: %d => %s", errno, strerror(errno)), _socketFd); }
201 |
202 | return bytesWritten;
203 | }
204 |
205 | /**
206 | * @brief Attempts to send a queue of messages on the associated CAN bus.
207 | *
208 | * @param messages A queue containing the messages to be sent.
209 | * @param delay If greater than 0, will delay the sending of the next message.
210 | * @param forceExtended Whether or not to force use of an extended ID.
211 | *
212 | * @return int32_t The total amount of bytes sent.
213 | */
214 | ssize_t CanDriver::sendMessageQueue(queue& messages, microseconds delay, bool forceExtended) { return sendMessageQueue(messages, std::chrono::duration_cast(delay), forceExtended); }
215 |
216 | /**
217 | * @brief Attempts to send a queue of messages on the associated CAN bus.
218 | *
219 | * @param messages A queue containing the messages to be sent.
220 | * @param delay If greater than 0, will delay the sending of the next message.
221 | * @param forceExtended Whether or not to force use of an extended ID.
222 | *
223 | * @return int32_t The total amount of bytes sent.
224 | */
225 | ssize_t CanDriver::sendMessageQueue(queue& messages, milliseconds delay, bool forceExtended) { return sendMessageQueue(messages, std::chrono::duration_cast(delay), forceExtended); }
226 |
227 | /**
228 | * @brief Attempts to send a queue of messages on the associated CAN bus.
229 | *
230 | * @param messages A queue containing the messages to be sent.
231 | * @param delay If greater than 0, will delay the sending of the next message.
232 | * @param forceExtended Whether or not to force use of an extended ID.
233 | *
234 | * @return int32_t The total amount of bytes sent.
235 | */
236 | ssize_t CanDriver::sendMessageQueue(queue& messages, nanoseconds delay, bool forceExtended) {
237 | if (_socketFd < 0) { throw InvalidSocketException("Invalid socket!", _socketFd); }
238 |
239 | ssize_t totalBytesWritten = 0;
240 |
241 | while (!messages.empty()) {
242 | totalBytesWritten += sendMessage(messages.front(), forceExtended);
243 | messages.pop();
244 |
245 | if (delay.count() > 0) {
246 | sleep_for(delay);
247 | }
248 | }
249 |
250 | return totalBytesWritten;
251 | }
252 |
253 | /**
254 | * @brief Attempts to read all messages stored in the buffer for the associated CAN bus.
255 | *
256 | * @return queue A queue containing the messages read from the bus buffer.
257 | */
258 | queue CanDriver::readQueuedMessages() {
259 | if (_socketFd < 0) { throw InvalidSocketException("Invalid socket!", _socketFd); }
260 |
261 | unique_lock locky{_lock};
262 | queue messages{};
263 |
264 | if (_canReadQueueSize) {
265 | for (int32_t i = _queueSize; 0 < i; --i) {
266 | messages.emplace(readMessageLock(false));
267 | }
268 | } else {
269 | // If the interface doesn't support FIONREAD, fall back
270 | // to reading until data exhausted.
271 | bool more{true};
272 |
273 | do {
274 | ssize_t readBytes;
275 | can_frame canFrame{};
276 | readBytes = read(_socketFd, &canFrame, sizeof(can_frame));
277 | if (readBytes >= 0) {
278 | messages.emplace(canFrame);
279 | } else if (errno == EAGAIN) {
280 | more = false;
281 | } else {
282 | throw CanException(formatString("FAILED to read from CAN! Error: %d => %s", errno, strerror(errno)), _socketFd);
283 | }
284 | } while (more);
285 | }
286 |
287 | return messages;
288 | }
289 |
290 | /**
291 | * @brief Sets the CAN FD frame option for the interface.
292 | *
293 | * This option allows the current driver instance to receive CAN FD frames.
294 | *
295 | * @param enabled Whether or not to enable the CAN FD frame option.
296 | */
297 | void CanDriver::allowCanFdFrames(const bool enabled/* = true*/) const {
298 | if (_socketFd < 0) { throw InvalidSocketException("Invalid socket!", _socketFd); }
299 |
300 | int32_t canFdFrames = enabled ? 1 : 0;
301 |
302 | if (setsockopt(_socketFd, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &canFdFrames, sizeof(canFdFrames)) == -1) {
303 | throw CanInitException(formatString("FAILED to set CAN FD frames on socket %d! Error: %d => %s", _socketFd, errno, strerror(errno)));
304 | }
305 | }
306 |
307 | #ifdef CANXL_XLF
308 | /**
309 | * @brief Sets the CAN XL option for the interface.
310 | *
311 | * This option allows the current driver instance to send/receive CAN XL frames.
312 | *
313 | * @param enabled Whether or not to enable the CAN XL option.
314 | */
315 | void CanDriver::allowCanXlFrames(const bool enabled/* = true*/) const {
316 | if (_socketFd < 0) { throw InvalidSocketException("Invalid socket!", _socketFd); }
317 |
318 | int32_t canXlFrames = enabled ? 1 : 0;
319 |
320 |
321 | if (setsockopt(_socketFd, SOL_CAN_RAW, CAN_RAW_XL_FRAMES, &canXlFrames, sizeof(canXlFrames)) == -1) {
322 | throw CanInitException(formatString("FAILED to set CAN XL frames on socket %d! Error: %d => %s", _socketFd, errno, strerror(errno)));
323 | }
324 | }
325 | #endif // CANXL_XLF
326 |
327 | /**
328 | * @brief Configures the socket to join the CAN filters.
329 | * This is especially required, when using inverted CAN filters.
330 | *
331 | * Source: https://stackoverflow.com/a/57680496/2921426
332 | */
333 | void CanDriver::joinCanFilters() const {
334 | if (_socketFd < 0) { throw InvalidSocketException("Invalid socket!", _socketFd); }
335 |
336 | int32_t joinFilters = 1;
337 |
338 | if (setsockopt(_socketFd, SOL_CAN_RAW, CAN_RAW_JOIN_FILTERS, &joinFilters, sizeof(joinFilters)) == -1) {
339 | throw CanInitException(formatString("FAILED to join CAN filters on socket %d! Error: %d => %s", _socketFd, errno, strerror(errno)));
340 | }
341 | }
342 |
343 | /**
344 | * @brief Attempts to set the filter mask for the associated CAN bus.
345 | *
346 | * @param mask The bit mask to apply.
347 | * @param filterId The ID to filter on.
348 | */
349 | void CanDriver::setCanFilterMask(const int32_t mask, const CanId& filterId) { setCanFilters({{filterId, static_cast(mask)}}); }
350 |
351 | /**
352 | * @brief Sets multiple CAN filters for the associated CAN bus.
353 | *
354 | * @param filters A map containing the filters to apply.
355 | */
356 | void CanDriver::setCanFilters(const filtermap_t& filters) {
357 | if (_socketFd < 0) { throw InvalidSocketException("Invalid socket!", _socketFd); }
358 |
359 | unique_lock locky(_lock);
360 | vector canFilters{};
361 |
362 | // Structured bindings only available with C++17
363 | #if __cplusplus >= 201703L
364 | for (const auto [id, filter] : filters) {
365 | canFilters.push_back({*id, filter});
366 | }
367 | #else
368 | for (const auto& filterPair : filters) {
369 | canFilters.push_back({*filterPair.first, filterPair.second});
370 | }
371 | #endif
372 |
373 | if (setsockopt(_socketFd, SOL_CAN_RAW, CAN_RAW_FILTER, canFilters.data(), canFilters.size() * sizeof(can_filter)) == -1) {
374 | throw CanInitException(formatString("FAILED to set CAN filters on socket %d! Error: %d => %s", _socketFd, errno, strerror(errno)));
375 | }
376 | }
377 |
378 | /**
379 | * @brief Sets the error filter for the associated CAN bus.
380 | *
381 | * @param enabled Whether or not to enable the error filter.
382 | */
383 | void CanDriver::setErrorFilter(const bool enabled/* = true*/) const {
384 | if (_socketFd < 0) { throw InvalidSocketException("Invalid socket!", _socketFd); }
385 |
386 | int32_t errorFilter = enabled ? 1 : 0;
387 |
388 | if (setsockopt(_socketFd, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, &errorFilter, sizeof(errorFilter)) == -1) {
389 | throw CanInitException(formatString("FAILED to set CAN error filter on socket %d! Error: %d => %s", _socketFd, errno, strerror(errno)));
390 | }
391 | }
392 |
393 | /**
394 | * @brief Sets the receive own messages option for the associated CAN bus.
395 | *
396 | * This option allows the socket to receive its own messages.
397 | *
398 | * @param enabled Whether or not to enable the receive own messages option.
399 | */
400 | void CanDriver::setReceiveOwnMessages(const bool enabled) const {
401 | if (_socketFd < 0) { throw InvalidSocketException("Invalid socket!", _socketFd); }
402 |
403 | int32_t receiveOwnMessages = enabled ? 1 : 0;
404 |
405 | if (setsockopt(_socketFd, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &receiveOwnMessages, sizeof(receiveOwnMessages)) == -1) {
406 | throw CanInitException(formatString("FAILED to set CAN error filter on socket %d! Error: %d => %s", _socketFd, errno, strerror(errno)));
407 | }
408 | }
409 | #pragma endregion
410 |
411 | //////////////////////////////////////
412 | // PROTECTED IMPLEMENTATION //
413 | //////////////////////////////////////
414 |
415 | #pragma region Socket Management
416 |
417 | /**
418 | * @brief Initialises the underlying CAN socket.
419 | */
420 | void CanDriver::initialiseSocketCan() {
421 | struct sockaddr_can address{};
422 | struct ifreq ifaceRequest{};
423 | int64_t fdOptions{0};
424 | int32_t tmpReturn{0};
425 |
426 | _socketFd = socket(PF_CAN, SOCK_RAW, _canProtocol);
427 |
428 | if (_socketFd == -1) {
429 | throw CanInitException(formatString("FAILED to initialise socketcan! Error: %d => %s", errno, strerror(errno)));
430 | }
431 |
432 | std::copy(_canInterface.begin(), _canInterface.end(), ifaceRequest.ifr_name);
433 |
434 | if ((tmpReturn = ioctl(_socketFd, SIOCGIFINDEX, &ifaceRequest)) == -1) {
435 | throw CanInitException(formatString("FAILED to perform IO control operation on socket %s! Error: %d => %s", _canInterface.c_str(), errno,
436 | strerror(errno)));
437 | }
438 |
439 | fdOptions = fcntl(_socketFd, F_GETFL);
440 | fdOptions |= O_NONBLOCK;
441 | tmpReturn = fcntl(_socketFd, F_SETFL, fdOptions);
442 |
443 | address.can_family = AF_CAN;
444 | address.can_ifindex = ifaceRequest.ifr_ifindex;
445 |
446 | setCanFilters(_canFilterMask);
447 |
448 | if ((tmpReturn = bind(_socketFd, (struct sockaddr*)&address, sizeof(address))) == -1) {
449 | throw CanInitException(formatString("FAILED to bind to socket CAN! Error: %d => %s", errno, strerror(errno)));
450 | }
451 | }
452 |
453 | /**
454 | * @brief Closes the underlying CAN socket.
455 | */
456 | void CanDriver::uninitialiseSocketCan() {
457 | unique_lock locky(_lock);
458 |
459 | if (_socketFd <= 0) { throw CanCloseException("Cannot close invalid socket!"); }
460 |
461 | if (close(_socketFd) == -1) { throw CanCloseException(formatString("FAILED to close CAN socket! Error: %d => %s", errno, strerror(errno))); }
462 |
463 | _socketFd = -1;
464 | }
465 | #pragma endregion
466 |
467 | } // namespace sockcanpp
468 |
--------------------------------------------------------------------------------
/test/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.12)
2 |
3 | add_subdirectory(app)
4 | add_subdirectory(unit)
--------------------------------------------------------------------------------
/test/app/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.14)
2 |
3 | project(testlibsockcanpp LANGUAGES CXX VERSION 1.0.0)
4 | set(TARGET_NAME testsockcanpp.bin)
5 |
6 | set(CMAKE_CXX_STANDARD 14)
7 |
8 | include_directories(
9 | include/
10 | )
11 |
12 | if (NOT TARGET sockcanpp)
13 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_BINARY_DIR}/libsockcanpp)
14 | endif()
15 |
16 | file(GLOB_RECURSE FILES ${CMAKE_CURRENT_SOURCE_DIR} src/*.cpp)
17 |
18 | add_executable(${TARGET_NAME} ${FILES})
19 |
20 | target_link_libraries(
21 | # Binary
22 | ${TARGET_NAME}
23 |
24 | # Libs
25 | sockcanpp_test
26 | -lm
27 |
28 | -static
29 | -static-libstdc++
30 | -static-libgcc
31 | )
--------------------------------------------------------------------------------
/test/app/src/Main.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @file Main.cpp
3 | * @author Simon Cahill (contact@simonc.eu)
4 | * @brief Contains the implementation of a test application using this library.
5 | * @version 0.1
6 | * @date 2020-07-02
7 | *
8 | * @copyright Copyright (c) 2020 Simon Cahill
9 | *
10 | * Copyright 2020 Simon Cahill
11 | *
12 | * Licensed under the Apache License, Version 2.0 (the "License");
13 | * you may not use this file except in compliance with the License.
14 | * You may obtain a copy of the License at
15 | *
16 | * http://www.apache.org/licenses/LICENSE-2.0
17 | *
18 | * Unless required by applicable law or agreed to in writing, software
19 | * distributed under the License is distributed on an "AS IS" BASIS,
20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 | * See the License for the specific language governing permissions and
22 | * limitations under the License.
23 | */
24 |
25 | #include
26 | #include
27 |
28 | #include
29 | #include
30 | #include
31 | #include
32 |
33 | using namespace std::chrono_literals;
34 |
35 | using sockcanpp::CanDriver;
36 | using sockcanpp::CanId;
37 | using sockcanpp::exceptions::CanException;
38 | using sockcanpp::exceptions::CanInitException;
39 | using sockcanpp::exceptions::InvalidSocketException;
40 | using sockcanpp::CanMessage;
41 |
42 | using std::cerr;
43 | using std::cout;
44 | using std::endl;
45 | using std::string;
46 |
47 | void printHelp(string);
48 |
49 | int main(int32_t argCount, char** argValues) {
50 | int32_t desiredCanSocket = 0;
51 | string canInterface;
52 |
53 | if (argCount > 2) {
54 | for (int32_t i = 1; i < argCount; i++) {
55 | string arg{argValues[i]};
56 | if (arg == "--help" || arg == "-h") {
57 | printHelp(argValues[0]);
58 | return 0;
59 | } else if (arg == "-protocol") {
60 | desiredCanSocket = atoi(argValues[i + 1]);
61 | i += 1;
62 | continue;
63 | } else if (arg == "-iface") {
64 | canInterface = (argValues[i + 1]);
65 | i += 1;
66 | continue;
67 | }
68 | }
69 | }
70 |
71 | if (desiredCanSocket <= 0)
72 | desiredCanSocket = CanDriver::CAN_SOCK_RAW;
73 | if (canInterface == "")
74 | canInterface = "can0";
75 |
76 | CanDriver* canDriver{nullptr};
77 | try {
78 | canDriver = new CanDriver(canInterface, desiredCanSocket);
79 | } catch (CanInitException& ex) {
80 | cerr << "An error occurred while initialising CanDriver: " << ex.what() << endl;
81 | if (canDriver) { delete canDriver; }
82 | return -1;
83 | }
84 |
85 | while (true) {
86 | printf("Writing test message:\n");
87 | try { canDriver->sendMessage(CanMessage(0x555, "abcdefg8")); }
88 | catch (CanException& ex) { cerr << "Failed to send test message! " << ex.what() << endl; }
89 | catch (InvalidSocketException& ex) { cerr << "Failed to send test message! " << ex.what() << endl; }
90 |
91 | printf("Reading messages\n");
92 | if (!canDriver->waitForMessages(3000ns)) continue;
93 |
94 | cout << "Reading queue..." << endl;
95 | auto canMessages = canDriver->readQueuedMessages();
96 | while (!canMessages.empty()) {
97 | auto msg = canMessages.front();
98 | canMessages.pop();
99 |
100 | cout << "CAN ID: " << (int32_t)msg.getCanId() << endl
101 | << "CAN data: ";
102 | for (auto byte : msg.getFrameData())
103 | cout << std::hex << byte << " ";
104 | cout << endl;
105 | }
106 | }
107 | }
108 |
109 | void printHelp(string appname) {
110 | cout << appname << endl << endl
111 | << "-h\t\tPrints this menu" << endl
112 | << "--help\t\tPrints this menu" << endl
113 | << "-protocol " << endl
114 | << "-iface " << endl;
115 | }
116 |
--------------------------------------------------------------------------------
/test/unit/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 |
--------------------------------------------------------------------------------
/test/unit/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.10)
2 |
3 | project(libsockcanpp_unittests LANGUAGES CXX VERSION 0.1)
4 | set(CMAKE_CXX_STANDARD 20)
5 | set(CMAKE_CXX_STANDARD_REQUIRED ON)
6 |
7 | find_package(GTest REQUIRED)
8 |
9 | include_directories(
10 | # none as of now
11 | )
12 |
13 | if (NOT TARGET sockcanpp)
14 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../ ${CMAKE_CURRENT_BINARY_DIR}/libsockcanpp)
15 | endif()
16 |
17 | file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
18 |
19 | add_compile_options(
20 | -Wall
21 | -Wextra
22 | -Wpedantic
23 | -Werror
24 | -Wno-unknown-pragmas
25 | )
26 |
27 | add_executable(${PROJECT_NAME} ${SOURCES})
28 |
29 | target_link_libraries(
30 | ${PROJECT_NAME}
31 |
32 | sockcanpp_test
33 | ${GTEST_LIBRARIES}
34 | pthread
35 | )
36 |
37 | gtest_discover_tests(${PROJECT_NAME})
--------------------------------------------------------------------------------
/test/unit/src/CanId_Tests.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @file CanId_Tests.cpp
3 | * @author Simon Cahill (contact@simonc.eu)
4 | * @brief Contains all the unit tests for the CanId structure.
5 | * @version 0.1
6 | * @date 2024-05-29
7 | *
8 | * @copyright Copyright (c) 2024 Simon Cahill and Contributors.
9 | */
10 |
11 | #include
12 |
13 | #include
14 |
15 | using sockcanpp::CanId;
16 |
17 | using std::string;
18 |
19 | TEST(CanIdTests, CanId_invalidId_ExpectFalse) {
20 | ASSERT_FALSE(CanId::isValidIdentifier(-1));
21 | }
22 |
23 | TEST(CanIdTests, CanId_validId_ExpectTrue_StandardFrameId) {
24 | ASSERT_TRUE(CanId::isValidIdentifier(0x123));
25 | }
26 |
27 | TEST(CanIdTests, CanId_validId_ExpectTrue_ExtendedFrameId) {
28 | ASSERT_TRUE(CanId::isValidIdentifier(0x123456));
29 | }
30 |
31 | TEST(CanIdTests, CanId_validId_ExpectTrue_StandardFrameId_ExplicitCast) {
32 | CanId id(0x123);
33 | ASSERT_TRUE(CanId::isValidIdentifier((int32_t)id));
34 | }
35 |
36 | TEST(CanIdTests, CanId_validId_ExpectTrue_ExtendedFrameId_ExplicitCast) {
37 | CanId id(0x123456);
38 | ASSERT_TRUE(CanId::isValidIdentifier((int32_t)id));
39 | }
40 |
41 | TEST(CanIdTests, CanId_validId_ExpectTrue_StandardFrameId_ImplicitCast) {
42 | CanId id(0x123);
43 | ASSERT_TRUE(CanId::isValidIdentifier(id));
44 | }
45 |
46 | TEST(CanIdTests, CanId_validId_ExpectTrue_ExtendedFrameId_ImplicitCast) {
47 | CanId id(0x123456);
48 | ASSERT_TRUE(CanId::isValidIdentifier(id));
49 | }
50 |
51 | TEST(CanIdTests, CanId_validId_ExpectTrue_StandardFrameId_ExplicitCastToInt16) {
52 | CanId id(0x123);
53 | ASSERT_TRUE(CanId::isValidIdentifier((int16_t)id));
54 | }
55 |
56 | TEST(CanIdTests, CanId_validId_ExpectTrue_StandardFrameId_ExplicitCastToUint16) {
57 | CanId id(0x123);
58 | ASSERT_TRUE(CanId::isValidIdentifier((uint16_t)id));
59 | }
60 |
61 | TEST(CanIdTests, CanId_validId_ExpectTrue_StandardFrameId_ExplicitCastToInt32) {
62 | CanId id(0x123);
63 | ASSERT_TRUE(CanId::isValidIdentifier((int32_t)id));
64 | }
65 |
66 | TEST(CanIdTests, CanId_validId_ExpectTrue_StandardFrameId_ImplicitCastToInt16) {
67 | CanId id(0x123);
68 | ASSERT_TRUE(CanId::isValidIdentifier(id));
69 | }
70 |
71 | TEST(CanIdTests, CanId_validId_ExpectTrue_StandardFrameId_ImplicitCastToUint16) {
72 | CanId id(0x123);
73 | ASSERT_TRUE(CanId::isValidIdentifier(id));
74 | }
75 |
76 | TEST(CanIdTests, CanId_validId_ExpectTrue_StandardFrameId_ImplicitCastToInt32) {
77 | CanId id(0x123);
78 | ASSERT_TRUE(CanId::isValidIdentifier(id));
79 | }
80 |
81 | TEST(CanIdTests, CanId_validId_ExpectTrue_ExtendedFrameId_ExplicitCastToInt16) {
82 | CanId id(0x123456);
83 | ASSERT_TRUE(CanId::isValidIdentifier((int16_t)id));
84 | }
85 |
86 | TEST(CanIdTests, CanId_validId_ExpectTrue_ExtendedFrameId_ExplicitCastToUint16) {
87 | CanId id(0x123456);
88 | ASSERT_TRUE(CanId::isValidIdentifier((uint16_t)id));
89 | }
90 |
91 | TEST(CanIdTests, CanId_validId_ExpectTrue_ExtendedFrameId_ExplicitCastToInt32) {
92 | CanId id(0x123456);
93 | ASSERT_TRUE(CanId::isValidIdentifier((int32_t)id));
94 | }
95 |
96 | TEST(CanIdTests, CanId_validId_ExpectTrue_ExtendedFrameId_ImplicitCastToInt16) {
97 | CanId id(0x123456);
98 | ASSERT_TRUE(CanId::isValidIdentifier(id));
99 | }
100 |
101 | TEST(CanIdTests, CanId_validId_ExpectTrue_ExtendedFrameId_ImplicitCastToUint16) {
102 | CanId id(0x123456);
103 | ASSERT_TRUE(CanId::isValidIdentifier(id));
104 | }
105 |
106 | TEST(CanIdTests, CanId_validId_ExpectTrue_ExtendedFrameId_ImplicitCastToInt32) {
107 | CanId id(0x123456);
108 | ASSERT_TRUE(CanId::isValidIdentifier(id));
109 | }
110 |
111 | TEST(CanIdTests, CanId_validId_ExpectTrue_StandardFrameId_ExplicitCastToUint32) {
112 | CanId id(0x123);
113 | ASSERT_TRUE(CanId::isValidIdentifier((uint32_t)id));
114 | }
115 |
116 | TEST(CanIdTests, CanId_validId_ExpectTrue_ExtendedFrameId_ExplicitCastToUint32) {
117 | CanId id(0x123456);
118 | ASSERT_TRUE(CanId::isValidIdentifier((uint32_t)id));
119 | }
120 |
121 | TEST(CanIdTests, CanId_validId_ExpectTrue_StandardFrameId_ImplicitCastToUint32) {
122 | CanId id(0x123);
123 | ASSERT_TRUE(CanId::isValidIdentifier(id));
124 | }
125 |
126 | TEST(CanIdTests, CanId_validId_ExpectTrue_ExtendedFrameId_ImplicitCastToUint32) {
127 | CanId id(0x123456);
128 | ASSERT_TRUE(CanId::isValidIdentifier(id));
129 | }
130 |
131 | TEST(CanIdTests, CanId_isErrorFrame_ExpectTrue) {
132 | auto id = 0xe0000abc;
133 | ASSERT_TRUE(CanId::isErrorFrame(id));
134 | }
135 |
136 | TEST(CanIdTests, CanId_isErrorFrame_ExpectFalse) {
137 | auto id = 0x123;
138 | ASSERT_FALSE(CanId::isErrorFrame(id));
139 | }
140 |
141 | TEST(CanIdTests, CanId_isErrorFrame_ExpectTrue_ExplicitCast) {
142 | auto id = 0xe0000abc;
143 | ASSERT_TRUE(CanId::isErrorFrame((int32_t)id));
144 | }
145 |
146 | TEST(CanIdTests, CanId_isErrorFrame_ExpectFalse_ExplicitCast) {
147 | auto id = 0x123;
148 | ASSERT_FALSE(CanId::isErrorFrame((int32_t)id));
149 | }
150 |
151 | TEST(CanIdTests, CanId_isExtendedFrame_ExpectTrue) {
152 | auto id = 0xe0000abc;
153 | ASSERT_TRUE(CanId::isExtendedFrame(id));
154 | }
155 |
156 | TEST(CanIdTests, CanId_isExtendedFrame_ExpectFalse) {
157 | auto id = 0x123;
158 | ASSERT_FALSE(CanId::isExtendedFrame(id));
159 | }
160 |
161 | TEST(CanIdTests, CanId_isRtr_ExpectTrue) {
162 | auto id = 0x40000000;
163 | ASSERT_TRUE(CanId::isRemoteTransmissionRequest(id));
164 | }
165 |
166 | TEST(CanIdTests, CanId_isRtr_ExpectFalse) {
167 | auto id = 0x123;
168 | ASSERT_FALSE(CanId::isRemoteTransmissionRequest(id));
169 | }
170 |
171 | // Test constexpr
172 | TEST(CanIdTests, CanId_isErrorFrame_ExpectTrue_Constexpr) {
173 | constexpr auto id = 0xe0000abc;
174 | ASSERT_TRUE(CanId::isErrorFrame(id));
175 | }
176 |
177 | TEST(CanIdTests, CanId_isErrorFrame_ExpectFalse_Constexpr) {
178 | constexpr auto id = 0x123;
179 | ASSERT_FALSE(CanId::isErrorFrame(id));
180 | }
181 |
182 | // Test implicit operators
183 | // Implicit operators strip out control bits
184 |
185 | TEST(CanIdTests, CanId_ImplicitOperatorInt32_ExpectTrue) {
186 | CanId id(0x123);
187 | ASSERT_EQ((int32_t)id, 0x123);
188 | }
189 |
190 | TEST(CanIdTests, CanId_ImplicitOperatorInt16_ExpectTrue) {
191 | CanId id(0x123);
192 | ASSERT_EQ((int16_t)id, 0x123);
193 | }
194 |
195 | TEST(CanIdTests, CanId_ImplicitOperatorUint16_ExpectTrue) {
196 | CanId id(0x123);
197 | ASSERT_EQ((uint16_t)id, 0x123);
198 | }
199 |
200 | TEST(CanIdTests, CanId_ImplicitOperatorUint32_ExpectTrue) {
201 | CanId id(0x123);
202 | ASSERT_EQ((uint32_t)id, 0x123);
203 | }
204 |
205 | // Test implicit operators with control bits set; these should be stripped.
206 | TEST(CanIdTests, CanId_ImplicitOperatorInt32_ExpectTrue_ControlBits) {
207 | CanId id(0x12345678);
208 | ASSERT_EQ((int32_t)id, 0x12345678);
209 | }
210 |
211 | TEST(CanIdTests, CanId_ImplicitOperatorInt16_ExpectTrue_ControlBits) {
212 | CanId id(0x12345678);
213 | ASSERT_EQ((int16_t)id, 0x5678);
214 | }
215 |
216 | TEST(CanIdTests, CanId_ImplicitOperatorUint16_ExpectTrue_ControlBits) {
217 | CanId id(0x12345678);
218 | ASSERT_EQ((uint16_t)id, 0x5678);
219 | }
220 |
221 | TEST(CanIdTests, CanId_ImplicitOperatorUint32_ExpectTrue_ControlBits) {
222 | CanId id(0x12345678);
223 | ASSERT_EQ((uint32_t)id, 0x12345678);
224 | }
225 |
226 | // Test arithmetic operators
227 | TEST(CanIdTests, CanId_ArithmeticOperatorPlus_ExpectTrue) {
228 | CanId id(0x123);
229 | ASSERT_EQ(id + 0x123, 0x246);
230 | }
231 |
232 | TEST(CanIdTests, CanId_ArithmeticOperatorMinus_ExpectTrue) {
233 | CanId id(0x123);
234 | ASSERT_EQ(id - 0x123, 0);
235 | }
236 |
237 | TEST(CanIdTests, CanId_ArithmeticOperatorPlusEquals_ExpectTrue) {
238 | CanId id(0x123);
239 | id += 0x123;
240 | ASSERT_EQ(id, 0x246);
241 | }
242 |
243 | TEST(CanIdTests, CanId_ArithmeticOperatorMinusEquals_ExpectTrue) {
244 | CanId id(0x123);
245 | id -= 0x123;
246 | ASSERT_EQ(id, 0);
247 | }
248 |
249 | TEST(CanIdTests, CanId_ArithmeticOperatorStar_ExpectTrue) {
250 | CanId id(0x123);
251 | ASSERT_EQ(id * 2, 0x246);
252 | }
253 |
254 | TEST(CanIdTests, CanId_ArithmeticOperatorSlash_ExpectTrue) {
255 | CanId id(0x246);
256 | ASSERT_EQ(id / 2, 0x123);
257 | }
258 |
259 | TEST(CanIdTests, CanId_ArithmeticOperatorStarEquals_ExpectTrue) {
260 | CanId id(0x123);
261 | id *= 2;
262 | ASSERT_EQ(id, 0x246);
263 | }
264 |
265 | TEST(CanIdTests, CanId_ArithmeticOperatorSlashEquals_ExpectTrue) {
266 | CanId id(0x246);
267 | id /= 2;
268 | ASSERT_EQ(id, 0x123);
269 | }
270 |
271 | TEST(CanIdTests, CanId_ArithmeticOperatorModulo_ExpectTrue) {
272 | CanId id(0x123);
273 | ASSERT_EQ(id % 2, 1);
274 | }
275 |
276 | TEST(CanIdTests, CanId_ArithmeticOperatorModuloEquals_ExpectTrue) {
277 | CanId id(0x123);
278 | id %= 2;
279 | ASSERT_EQ(id, 1);
280 | }
281 |
282 | #if __cpp_concepts >= 201907
283 |
284 | TEST(CanIdTests, CanId_TestStringToCanIdConversion_ExpectTrue) {
285 | string id{"0x123"};
286 |
287 | EXPECT_NO_THROW(CanId canId(id));
288 | EXPECT_NO_THROW(CanId canId{id});
289 | EXPECT_NO_THROW(CanId canId = id; (void)canId;);
290 | EXPECT_NO_THROW(CanId canId{"0x123"});
291 |
292 | CanId canId(id);
293 | ASSERT_EQ(canId, 0x123);
294 | }
295 |
296 | TEST(CanIdTests, CanId_TestStringToCanIdConversion_ExpectTrue_ExplicitCast) {
297 | string id{"0x123"};
298 |
299 | EXPECT_NO_THROW(CanId canId(static_cast(id.c_str())));
300 | EXPECT_NO_THROW(CanId canId = static_cast(id.c_str()); (void)canId;);
301 | EXPECT_NO_THROW(CanId canId = static_cast(id.c_str()); (void)canId;);
302 |
303 | CanId canId(static_cast(id.c_str()));
304 | ASSERT_EQ(canId, 0x123);
305 | }
306 |
307 | TEST(CanIdTests, CanId_TestStringToCanIdConversion_ExpectTrue_ImplicitCast) {
308 | string id{"0x123"};
309 |
310 | EXPECT_NO_THROW(CanId canId = id; (void)canId;);
311 | EXPECT_NO_THROW(CanId canId = id; (void)canId;);
312 |
313 | CanId canId = id;
314 | ASSERT_EQ(canId, 0x123);
315 | }
316 |
317 | TEST(CanIdTests, CanId_TestStringToCanIdConversion_ExpectError) {
318 | string id{"hello_world"};
319 |
320 | EXPECT_THROW(CanId canId(id), std::invalid_argument);
321 | EXPECT_THROW(CanId canId = id; (void)canId;, std::invalid_argument);
322 | EXPECT_THROW(CanId canId = id; (void)canId;, std::invalid_argument);
323 | }
324 |
325 | #endif // __cpp_concepts >= 201907
--------------------------------------------------------------------------------
/test/unit/src/main.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @file main.cpp
3 | * @author Simon Cahill (contact@simonc.eu)
4 | * @brief Contains the main entry point for the unit tests.
5 | * @version 0.1
6 | * @date 2024-05-29
7 | *
8 | * @copyright Copyright (c) 2024 Simon Cahill and Contributors.
9 | */
10 |
11 | #include
12 |
13 | int main(int argc, char **argv) {
14 | ::testing::InitGoogleTest(&argc, argv);
15 | return RUN_ALL_TESTS();
16 | }
--------------------------------------------------------------------------------
/toolchains/arm64-toolchain.cmake:
--------------------------------------------------------------------------------
1 | set(CMAKE_SYSTEM_NAME Linux)
2 | set(CMAKE_SYSTEM_PROCESSOR arm)
3 |
4 | ###
5 | # Set binary paths
6 | ###
7 | set(tools /usr/bin)
8 | set(cross aarch64-linux-gnu)
9 |
10 | set(CMAKE_C_COMPILER ${tools}/${cross}-gcc)
11 | set(CMAKE_CXX_COMPILER ${tools}/${cross}-g++)
12 | set(CMAKE_LD ${tools}/${cross}-ld)
13 | set(CMAKE_STRIP ${tools}/${cross}-strip)
14 | set(CMAKE_AR ${tools}/${cross}-ar)
15 |
16 | ###
17 | # Set source and lib paths
18 | ###
19 |
--------------------------------------------------------------------------------
/toolchains/armhf-toolchain.cmake:
--------------------------------------------------------------------------------
1 | set(CMAKE_SYSTEM_NAME Linux)
2 | set(CMAKE_SYSTEM_PROCESSOR arm)
3 |
4 | ###
5 | # Set binary paths
6 | ###
7 | set(tools /usr/bin)
8 | set(cross arm-linux-gnueabihf)
9 |
10 | set(CMAKE_C_COMPILER ${tools}/${cross}-gcc)
11 | set(CMAKE_CXX_COMPILER ${tools}/${cross}-g++)
12 | set(CMAKE_LD ${tools}/${cross}-ld)
13 | set(CMAKE_STRIP ${tools}/${cross}-strip)
14 | set(CMAKE_AR ${tools}/${cross}-ar)
15 |
16 | ###
17 | # Set source and lib paths
18 | ###
19 |
20 | #include_directories(SYSTEM )
21 |
--------------------------------------------------------------------------------
/toolchains/i686-toolchain.cmake:
--------------------------------------------------------------------------------
1 | set(CMAKE_SYSTEM_NAME Linux)
2 | set(CMAKE_SYSTEM_PROCESSOR i686)
3 |
4 | ###
5 | # Set binary paths
6 | ###
7 | set(tools /usr/bin)
8 | set(cross i686-linux-gnu)
9 |
10 | set(CMAKE_C_COMPILER ${tools}/${cross}-gcc)
11 | set(CMAKE_CXX_COMPILER ${tools}/${cross}-g++)
12 | set(CMAKE_LD ${tools}/${cross}-ld)
13 | set(CMAKE_STRIP ${tools}/${cross}-strip)
14 | set(CMAKE_AR ${tools}/${cross}-ar)
15 |
16 | ###
17 | # Set source and lib paths
18 | ###
19 |
20 | #include_directories(SYSTEM )
21 |
--------------------------------------------------------------------------------
/toolchains/ppc-toolchain.cmake:
--------------------------------------------------------------------------------
1 | set(CMAKE_SYSTEM_NAME Linux)
2 | set(CMAKE_SYSTEM_PROCESSOR powerpc)
3 |
4 | ###
5 | # Set binary paths
6 | ###
7 | set(tools /usr/bin)
8 | set(cross powerpc-linux-gnu)
9 |
10 | set(CMAKE_C_COMPILER ${tools}/${cross}-gcc)
11 | set(CMAKE_CXX_COMPILER ${tools}/${cross}-g++)
12 | set(CMAKE_LD ${tools}/${cross}-ld)
13 | set(CMAKE_STRIP ${tools}/${cross}-strip)
14 | set(CMAKE_AR ${tools}/${cross}-ar)
15 |
16 | add_definitions(
17 | -DPOWERPC
18 | )
19 |
20 | ###
21 | # Set source and lib paths
22 | ###
23 |
24 | #include_directories(SYSTEM )
25 |
--------------------------------------------------------------------------------
/toolchains/ppc64-toolchain.cmake:
--------------------------------------------------------------------------------
1 | set(CMAKE_SYSTEM_NAME Linux)
2 | set(CMAKE_SYSTEM_PROCESSOR powerpc64)
3 |
4 | ###
5 | # Set binary paths
6 | ###
7 | set(tools /usr/bin)
8 | set(cross arm-linux-gnueabihf)
9 |
10 | set(CMAKE_C_COMPILER ${tools}/${cross}-gcc)
11 | set(CMAKE_CXX_COMPILER ${tools}/${cross}-g++)
12 | set(CMAKE_LD ${tools}/${cross}-ld)
13 | set(CMAKE_STRIP ${tools}/${cross}-strip)
14 | set(CMAKE_AR ${tools}/${cross}-ar)
15 |
16 | ###
17 | # Set source and lib paths
18 | ###
19 |
20 | #include_directories(SYSTEM )
21 |
--------------------------------------------------------------------------------
/toolchains/x86_64-toolchain.cmake:
--------------------------------------------------------------------------------
1 | set(CMAKE_SYSTEM_NAME Linux)
2 | set(CMAKE_SYSTEM_PROCESSOR x86_64)
3 |
4 | ###
5 | # Set binary paths
6 | ###
7 | set(tools /usr/bin)
8 | set(cross x86_64-linux-gnu)
9 |
10 | set(CMAKE_C_COMPILER ${tools}/${cross}-gcc)
11 | set(CMAKE_CXX_COMPILER ${tools}/${cross}-g++)
12 | set(CMAKE_LD ${tools}/${cross}-ld)
13 | set(CMAKE_STRIP ${tools}/${cross}-strip)
14 | set(CMAKE_AR ${tools}/${cross}-ar)
15 |
16 | ###
17 | # Set source and lib paths
18 | ###
19 |
20 | #include_directories(SYSTEM )
21 |
--------------------------------------------------------------------------------