Compare commits

...

17 Commits

Author SHA1 Message Date
David Rebbe 6dd4456f9a API: Added icsneoc2.
Signed-off-by: David Rebbe <drebbe@intrepidcs.com>
2025-02-04 13:48:43 -05:00
Jonathan Schwartz c5ba2d8d32 Communication: Add missing CAN error types 2025-02-04 15:15:44 +00:00
Kyle Schwarz a22791c9e0 CI: Move Windows jobs 2025-02-03 17:16:30 -05:00
Kyle Schwarz dc2a364afb Bindings: Python: Fix API docs since adding stubs 2025-01-29 14:53:18 -05:00
Kyle Schwarz 114b664d78 All: Welcome to 2025 2025-01-15 16:46:07 -05:00
Kyle Schwarz 2acf248583 Bindings: Python: Add stubs 2025-01-09 00:14:46 -05:00
Kyle Schwarz f18aa00322 Communication: Add EthernetStatusMessage 2025-01-06 14:12:19 -05:00
Jonathan Schwartz 87baa97c3f Device: Fix ComponentVersion retrieval 2025-01-06 19:06:11 +00:00
Jonathan Schwartz 8dcbbe0d72 NeoVIConnect: Add ComponentVersions support 2025-01-06 14:51:42 +00:00
Kyle Schwarz b624d06ca0 Driver: FTD3XX: Fix close() 2024-12-30 14:14:23 -05:00
Kyle Schwarz c249df8756 Bindings: Python: Add CANErrorCountMessage 2024-12-23 10:28:19 -05:00
Kyle Schwarz b2161211c5 Bindings: Python: Add Message::Type MessageFilter 2024-12-20 18:28:41 -05:00
Kyle Schwarz 0ee8a990a7 Bindings: Python: Fix get_gptp_status default arg 2024-12-20 18:28:22 -05:00
Yasser Yassine dc0f16b1d2 Communication: Add GPTPStatus 2024-12-20 23:22:25 +00:00
Kyle Schwarz c4ce803d62 LINMessage: Fix timestamp scaling 2024-12-19 15:54:27 -05:00
Kyle Schwarz 34cacf4cf2 EthernetMessage: Fix TX receipts
Fixes
- HardwareEthernetPacket packing
- EthernetMessage::fcs
2024-12-13 12:55:57 -05:00
Kyle Schwarz 4157558e84 Bindings: Python: Drop GIL for Device calls
Avoids an ABBA deadlock with the GIL and messageCallbacksLock
2024-12-12 11:12:55 -05:00
118 changed files with 7638 additions and 490 deletions

View File

@ -21,7 +21,7 @@ build windows/x64:
- build
expire_in: 3 days
tags:
- icsneo-windows
- libicsneo-win-x64
unit_test windows/x64:
stage: unit_test
@ -32,7 +32,7 @@ unit_test windows/x64:
needs:
- build windows/x64
tags:
- icsneo-windows
- libicsneo-win-x64
timeout: 5m
build windows/x86:
@ -45,7 +45,7 @@ build windows/x86:
- build
expire_in: 3 days
tags:
- icsneo-windows
- libicsneo-win-x64
unit_test windows/x86:
stage: unit_test
@ -56,7 +56,7 @@ unit_test windows/x86:
needs:
- build windows/x86
tags:
- icsneo-windows
- libicsneo-win-x64
timeout: 5m
#-------------------------------------------------------------------------------

View File

@ -12,6 +12,8 @@ option(LIBICSNEO_BUILD_DOCS "Build documentation. Don't use in Visual Studio." O
option(LIBICSNEO_BUILD_EXAMPLES "Build examples." ON)
option(LIBICSNEO_BUILD_ICSNEOC "Build dynamic C library" ON)
option(LIBICSNEO_BUILD_ICSNEOC_STATIC "Build static C library" ON)
option(LIBICSNEO_BUILD_ICSNEOC2 "Build dynamic C2 library" ON)
option(LIBICSNEO_BUILD_ICSNEOC2_STATIC "Build static C2 library" ON)
option(LIBICSNEO_BUILD_ICSNEOLEGACY "Build icsnVC40 compatibility library" ON)
option(LIBICSNEO_BUILD_ICSNEOLEGACY_STATIC "Build static icsnVC40 compatibility library" ON)
set(LIBICSNEO_NPCAP_INCLUDE_DIR "" CACHE STRING "Npcap include directory; set to build with Npcap")
@ -28,6 +30,7 @@ option(LIBICSNEO_ENABLE_TCP "Enable devices which communicate over TCP" OFF)
option(LIBICSNEO_ENABLE_FTD3XX "Enable devices which communicate over USB FTD3XX" ON)
option(LIBICSNEO_ENABLE_BINDINGS_PYTHON "Enable Python library" OFF)
option(LIBICSNEO_ENABLE_BINDINGS_RUST "Enable Rust library" OFF)
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 17)
@ -246,6 +249,8 @@ set(SRC_FILES
communication/message/linmessage.cpp
communication/message/livedatamessage.cpp
communication/message/tc10statusmessage.cpp
communication/message/gptpstatusmessage.cpp
communication/message/ethernetstatusmessage.cpp
communication/packet/flexraypacket.cpp
communication/packet/canpacket.cpp
communication/packet/a2bpacket.cpp
@ -332,6 +337,7 @@ endif()
configure_file(api/icsneocpp/buildinfo.h.template ${CMAKE_CURRENT_BINARY_DIR}/generated/buildinfo.h)
configure_file(api/icsneoc/version.rc.template ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc/version.rc)
configure_file(api/icsneoc2/version.rc.template ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc2/version.rc)
foreach(EXTINC ${LIBICSNEO_EXTENSION_INCLUDES})
message("Including " ${EXTINC})
@ -459,6 +465,42 @@ if(LIBICSNEO_BUILD_ICSNEOC_STATIC)
target_compile_definitions(icsneoc-static PUBLIC ICSNEOC_BUILD_STATIC)
endif()
if(LIBICSNEO_BUILD_ICSNEOC2)
add_library(icsneoc2 SHARED api/icsneoc2/icsneoc2.cpp ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc2/version.rc)
target_include_directories(icsneoc2
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
)
target_link_libraries(icsneoc2 PRIVATE icsneocpp)
target_compile_definitions(icsneoc2
PRIVATE
ICSNEO_EXPORTS _CRT_SECURE_NO_WARNINGS
PUBLIC
ICSNEO_IMPORTS _CRT_SECURE_NO_WARNINGS
INTERFACE
ICSNEO_IMPORTS _CRT_SECURE_NO_WARNINGS
)
target_compile_features(icsneoc2 PRIVATE cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums)
target_compile_definitions(icsneoc2 PUBLIC ICSNEOC2_BUILD_DYNAMIC)
endif()
if(LIBICSNEO_BUILD_ICSNEOC2_STATIC)
add_library(icsneoc2-static STATIC api/icsneoc2/icsneoc2.cpp)
target_include_directories(icsneoc2-static
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
)
target_link_libraries(icsneoc2-static PUBLIC icsneocpp)
target_compile_features(icsneoc2-static PUBLIC cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums)
target_compile_definitions(icsneoc2-static PUBLIC ICSNEOC2_BUILD_STATIC)
endif()
if(LIBICSNEO_BUILD_ICSNEOLEGACY)
add_library(icsneolegacy SHARED
api/icsneolegacy/icsneolegacy.cpp
@ -525,10 +567,12 @@ if(LIBICSNEO_BUILD_UNIT_TESTS)
test/unit/livedataencoderdecodertest.cpp
test/unit/ringbuffertest.cpp
test/unit/apperrordecodertest.cpp
test/unit/icsneoc2.cpp
)
target_link_libraries(libicsneo-unit-tests gtest gtest_main)
target_link_libraries(libicsneo-unit-tests icsneocpp)
target_link_libraries(libicsneo-unit-tests icsneoc2-static)
target_include_directories(libicsneo-unit-tests PUBLIC ${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})

30
LICENSE
View File

@ -1,13 +1,29 @@
Copyright 2018-2024 Intrepid Control Systems, Inc.
Copyright (c) 2018-2025 Intrepid Control Systems, Inc.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
4. It is forbidden to use this library or derivatives to interface with vehicle networking hardware not produced by Intrepid Control Systems, Inc.
4. It is forbidden to use this library or derivatives to interface with vehicle
networking hardware not produced by Intrepid Control Systems, Inc.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,54 @@
#include "icsneo/icsneo.h"
#include "icsneo/icsneocpp.h"
#include "icsneo/device/devicefinder.h"
#include <string>
#include <algorithm>
using namespace icsneo;
struct icsneoOpenOptions_t {
/** Open the device in online mode on open. */
bool goOnlineOnOpen = true;
/** Enable message polling on open. */
bool enablePollingOnOpen = true;
/** Synchronize the device RTC on open.
* Useful for devices that don't have an RTC battery like ValueCAN4 */
bool syncRTCOnOpen = true;
};
typedef struct icsneo_device_t {
// This is the actual device handle, when its not open it will be NULL.
std::shared_ptr<Device> device;
icsneoOpenOptions_t openOptions;
} *icsneo_device_ptr_t;
icsneo_result_t icsneo_find(icsneo_device_ptr_t* devices, uint32_t* devices_count, void* reserved) {
static std::deque<icsneo_device_t> found_icsneo_devices;
if (!devices || !devices_count) {
return icsneo_error_invalid_parameter;
}
// Find all devices and get the minimum size to process
auto found_devices = DeviceFinder::FindAll();
auto min_size = std::minmax(found_devices.size(), static_cast<size_t>(*devices_count)).first;
// lets resize the array to the minimum size
for (auto i = 0; i < min_size; i++) {
const auto* dev = devices[i];
dev = new icsneo_device_t {};
dev->device = found_devices[i];
}
return icsneo_error_success;
}
icsneo_result_t icsneo_open(icsneo_device_ptr_t* device) {
if (!device) {
return icsneo_error_invalid_parameter;
}
return icsneo_error_success;
}

View File

@ -0,0 +1,51 @@
#define VER_FILEVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@
#define VER_FILEVERSION_STR "v@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@@BUILD_METADATA_PLUS@ @BUILD_GIT_INFO@"
#define VER_PRODUCTVERSION VER_FILEVERSION
#define VER_PRODUCTVERSION_STR VER_FILEVERSION_STR
#ifndef DEBUG
#define VER_DEBUG 0
#else
#define VER_DEBUG VS_FF_DEBUG
#endif
#include <windows.h>
VS_VERSION_INFO VERSIONINFO
FILEVERSION VER_FILEVERSION
PRODUCTVERSION VER_PRODUCTVERSION
FILEFLAGSMASK (VS_FF_DEBUG)
FILEFLAGS (VER_DEBUG)
FILEOS VOS__WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
VALUE "CompanyName", "Intrepid Control Systems, Inc."
VALUE "FileDescription", "Intrepid Control Systems Open Device Communication C API"
VALUE "FileVersion", VER_FILEVERSION_STR
VALUE "InternalName", "icsneo.dll"
VALUE "LegalCopyright", "Intrepid Control Systems, Inc. (C) 2018-2024"
VALUE "OriginalFilename", "icsneo.dll"
VALUE "ProductName", "libicsneo"
VALUE "ProductVersion", VER_PRODUCTVERSION_STR
END
END
BLOCK "VarFileInfo"
BEGIN
/* The following line should only be modified for localized versions. */
/* It consists of any number of WORD,WORD pairs, with each pair */
/* describing a language,codepage combination supported by the file. */
/* */
/* For example, a file might have values "0x409,1252" indicating that it */
/* supports English language (0x409) in the Windows ANSI codepage (1252). */
VALUE "Translation", 0x409, 1252
END
END

View File

@ -462,7 +462,7 @@ bool icsneo_transmit(const neodevice_t* device, const neomessage_t* message) {
if(!icsneo_isValidNeoDevice(device))
return false;
if(auto frame = std::dynamic_pointer_cast<icsneo::Frame>(CreateMessageFromNeoMessage(message)))
if(auto frame = std::dynamic_pointer_cast<icsneo::BusMessage>(CreateMessageFromNeoMessage(message)))
return device->device->transmit(frame);
return false;
@ -738,7 +738,7 @@ int icsneo_getDeviceStatus(const neodevice_t* device, void* status, size_t* size
if(!msg) // Did not receive a message
return false;
auto rawMessage = std::static_pointer_cast<RawMessage>(msg);
auto rawMessage = std::static_pointer_cast<InternalMessage>(msg);
if(!rawMessage || (rawMessage->network.getNetID() != Network::NetID::DeviceStatus))
return false;

View File

@ -29,7 +29,7 @@ BEGIN
VALUE "FileDescription", "Intrepid Control Systems Open Device Communication C API"
VALUE "FileVersion", VER_FILEVERSION_STR
VALUE "InternalName", "icsneoc.dll"
VALUE "LegalCopyright", "Intrepid Control Systems, Inc. (C) 2018-2024"
VALUE "LegalCopyright", "Intrepid Control Systems, Inc. (C) 2018-2025"
VALUE "OriginalFilename", "icsneoc.dll"
VALUE "ProductName", "libicsneo"
VALUE "ProductVersion", VER_PRODUCTVERSION_STR

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,51 @@
#define VER_FILEVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@
#define VER_FILEVERSION_STR "v@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@@BUILD_METADATA_PLUS@ @BUILD_GIT_INFO@"
#define VER_PRODUCTVERSION VER_FILEVERSION
#define VER_PRODUCTVERSION_STR VER_FILEVERSION_STR
#ifndef DEBUG
#define VER_DEBUG 0
#else
#define VER_DEBUG VS_FF_DEBUG
#endif
#include <windows.h>
VS_VERSION_INFO VERSIONINFO
FILEVERSION VER_FILEVERSION
PRODUCTVERSION VER_PRODUCTVERSION
FILEFLAGSMASK (VS_FF_DEBUG)
FILEFLAGS (VER_DEBUG)
FILEOS VOS__WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
VALUE "CompanyName", "Intrepid Control Systems, Inc."
VALUE "FileDescription", "Intrepid Control Systems Open Device Communication C API"
VALUE "FileVersion", VER_FILEVERSION_STR
VALUE "InternalName", "icsneo.dll"
VALUE "LegalCopyright", "Intrepid Control Systems, Inc. (C) 2018-2024"
VALUE "OriginalFilename", "icsneo.dll"
VALUE "ProductName", "libicsneo"
VALUE "ProductVersion", VER_PRODUCTVERSION_STR
END
END
BLOCK "VarFileInfo"
BEGIN
/* The following line should only be modified for localized versions. */
/* It consists of any number of WORD,WORD pairs, with each pair */
/* describing a language,codepage combination supported by the file. */
/* */
/* For example, a file might have values "0x409,1252" indicating that it */
/* supports English language (0x409) in the Windows ANSI codepage (1252). */
VALUE "Translation", 0x409, 1252
END
END

View File

@ -120,6 +120,7 @@ static constexpr const char* DISK_NOT_CONNECTED = "The program tried to access a
static constexpr const char* UNEXPECTED_RESPONSE = "Received an unexpected or invalid response from the device.";
static constexpr const char* LIN_SETTINGS_NOT_AVAILABLE = "LIN settings are not available for this device.";
static constexpr const char* MODE_NOT_FOUND = "The mode was not found.";
static constexpr const char* GPTP_NOT_SUPPORTED = "GPTP clock synchronization is not supported on this device.";
// Transport Errors
static constexpr const char* FAILED_TO_READ = "A read operation failed.";
@ -185,6 +186,7 @@ static constexpr const char* VSA_OTHER_ERROR = "Unknown error in VSA read API.";
static constexpr const char* TOO_MANY_EVENTS = "Too many events have occurred. The list has been truncated.";
static constexpr const char* UNKNOWN = "An unknown internal error occurred.";
static constexpr const char* INVALID = "An invalid internal error occurred.";
const char* APIEvent::DescriptionForType(Type type) {
switch(type) {
// API Errors
@ -345,6 +347,8 @@ const char* APIEvent::DescriptionForType(Type type) {
return GETIFADDRS_ERROR;
case Type::SendToError:
return SEND_TO_ERROR;
case Type::GPTPNotSupported:
return GPTP_NOT_SUPPORTED;
// FTD3XX
case Type::FTOK:

View File

@ -1,3 +1,18 @@
if(LIBICSNEO_ENABLE_BINDINGS_PYTHON)
add_subdirectory(python)
endif()
if(LIBICSNEO_ENABLE_BINDINGS_RUST)
include(FetchContent)
FetchContent_Declare(
Corrosion
GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git
GIT_TAG v0.5 # Optionally specify a commit hash, version tag or branch here
)
FetchContent_MakeAvailable(Corrosion)
corrosion_import_crate(MANIFEST_PATH ${CMAKE_SOURCE_DIR}/bindings/rust/icsneoc2rs/Cargo.toml)
else()
message(STATUS "Not building rust bindings")
endif()

View File

@ -12,10 +12,13 @@ pybind11_add_module(icsneopy
icsneopy/communication/network.cpp
icsneopy/communication/message/message.cpp
icsneopy/communication/message/canmessage.cpp
icsneopy/communication/message/canerrormessage.cpp
icsneopy/communication/message/ethernetmessage.cpp
icsneopy/communication/message/linmessage.cpp
icsneopy/communication/message/tc10statusmessage.cpp
icsneopy/communication/message/mdiomessage.cpp
icsneopy/communication/message/gptpstatusmessage.cpp
icsneopy/communication/message/ethernetstatusmessage.cpp
icsneopy/communication/message/callback/messagecallback.cpp
icsneopy/communication/message/filter/messagefilter.cpp
icsneopy/device/device.cpp
@ -23,4 +26,9 @@ pybind11_add_module(icsneopy
)
target_link_libraries(icsneopy PRIVATE icsneocpp)
install(TARGETS icsneopy LIBRARY DESTINATION .)
install(TARGETS icsneopy LIBRARY DESTINATION icsneopy)
find_program(STUBGEN stubgen REQUIRED)
add_custom_command(TARGET icsneopy POST_BUILD COMMAND ${STUBGEN} -m icsneopy -o .)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/icsneopy.pyi __init__.py py.typed DESTINATION icsneopy)

View File

@ -0,0 +1 @@
from .icsneopy import *

View File

@ -0,0 +1,38 @@
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/functional.h>
#include "icsneo/communication/message/canerrormessage.h"
namespace icsneo {
void init_errorcodes(pybind11::module_& m) {
pybind11::enum_<CANErrorCode>(m, "CANErrorCode")
.value("NoError", CANErrorCode::NoError)
.value("StuffError", CANErrorCode::StuffError)
.value("FormError", CANErrorCode::FormError)
.value("AckError", CANErrorCode::AckError)
.value("Bit1Error", CANErrorCode::Bit1Error)
.value("Bit0Error", CANErrorCode::Bit0Error)
.value("CRCError", CANErrorCode::CRCError)
.value("NoChange", CANErrorCode::NoChange);
}
void init_canerrormessage(pybind11::module_& m) {
init_errorcodes(m);
pybind11::class_<CANErrorMessage, std::shared_ptr<CANErrorMessage>, Message>(m, "CANErrorMessage")
.def_readonly("network", &CANErrorMessage::network)
.def_readonly("transmitErrorCount", &CANErrorMessage::transmitErrorCount)
.def_readonly("receiveErrorCount", &CANErrorMessage::receiveErrorCount)
.def_readonly("busOff", &CANErrorMessage::busOff)
.def_readonly("errorPassive", &CANErrorMessage::errorPassive)
.def_readonly("errorWarn", &CANErrorMessage::errorWarn)
.def_readonly("dataErrorCode", &CANErrorMessage::dataErrorCode)
.def_readonly("errorCode", &CANErrorMessage::errorCode);
m.attr("CANErrorCountMessage") = m.attr("CANErrorMessage");
}
} // namespace icsneo

View File

@ -7,7 +7,7 @@
namespace icsneo {
void init_canmessage(pybind11::module_& m) {
pybind11::class_<CANMessage, std::shared_ptr<CANMessage>, Frame>(m, "CANMessage")
pybind11::class_<CANMessage, std::shared_ptr<CANMessage>, BusMessage>(m, "CANMessage")
.def(pybind11::init())
.def_readwrite("arbid", &CANMessage::arbid)
.def_readwrite("dlcOnWire", &CANMessage::dlcOnWire)

View File

@ -11,11 +11,11 @@ void init_ethernetmessage(pybind11::module_& m) {
.def("to_string", &MACAddress::toString)
.def("__repr__", &MACAddress::toString);
pybind11::class_<EthernetMessage, std::shared_ptr<EthernetMessage>, Frame>(m, "EthernetMessage")
pybind11::class_<EthernetMessage, std::shared_ptr<EthernetMessage>, BusMessage>(m, "EthernetMessage")
.def(pybind11::init())
.def_readwrite("preemptionEnabled", &EthernetMessage::preemptionEnabled)
.def_readwrite("preemptionFlags", &EthernetMessage::preemptionFlags)
.def_readwrite("fcsAvailable", &EthernetMessage::fcsAvailable)
.def_readwrite("fcs", &EthernetMessage::fcs)
.def_readwrite("frameTooShort", &EthernetMessage::frameTooShort)
.def_readwrite("noPadding", &EthernetMessage::noPadding)
.def("get_destination_mac", &EthernetMessage::getDestinationMAC, pybind11::return_value_policy::reference)

View File

@ -0,0 +1,35 @@
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/functional.h>
#include "icsneo/communication/message/ethernetstatusmessage.h"
namespace icsneo {
void init_ethernetstatusmessage(pybind11::module_& m) {
pybind11::class_<EthernetStatusMessage, std::shared_ptr<EthernetStatusMessage>, Message> ethernetStatusMessage(m, "EthernetStatusMessage");
pybind11::enum_<EthernetStatusMessage::LinkSpeed>(ethernetStatusMessage, "LinkSpeed")
.value("LinkSpeedAuto", EthernetStatusMessage::LinkSpeed::LinkSpeedAuto)
.value("LinkSpeed10", EthernetStatusMessage::LinkSpeed::LinkSpeed10)
.value("LinkSpeed100", EthernetStatusMessage::LinkSpeed::LinkSpeed100)
.value("LinkSpeed1000", EthernetStatusMessage::LinkSpeed::LinkSpeed1000)
.value("LinkSpeed2500", EthernetStatusMessage::LinkSpeed::LinkSpeed2500)
.value("LinkSpeed5000", EthernetStatusMessage::LinkSpeed::LinkSpeed5000)
.value("LinkSpeed10000", EthernetStatusMessage::LinkSpeed::LinkSpeed10000);
pybind11::enum_<EthernetStatusMessage::LinkMode>(ethernetStatusMessage, "LinkMode")
.value("LinkModeAuto", EthernetStatusMessage::LinkMode::LinkModeAuto)
.value("LinkModeMaster", EthernetStatusMessage::LinkMode::LinkModeMaster)
.value("LinkModeSlave", EthernetStatusMessage::LinkMode::LinkModeSlave)
.value("LinkModeInvalid", EthernetStatusMessage::LinkMode::LinkModeInvalid);
ethernetStatusMessage
.def_readonly("network", &EthernetStatusMessage::network)
.def_readonly("state", &EthernetStatusMessage::state)
.def_readonly("speed", &EthernetStatusMessage::speed)
.def_readonly("duplex", &EthernetStatusMessage::duplex)
.def_readonly("mode", &EthernetStatusMessage::mode);
}
} // namespace icsneo

View File

@ -9,6 +9,7 @@ namespace icsneo {
void init_messagefilter(pybind11::module_& m) {
pybind11::class_<MessageFilter, std::shared_ptr<MessageFilter>>(m, "MessageFilter")
.def(pybind11::init())
.def(pybind11::init<Message::Type>())
.def(pybind11::init<Network::NetID>());
}

View File

@ -0,0 +1,79 @@
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/functional.h>
#include "icsneo/communication/message/gptpstatusmessage.h"
namespace icsneo {
void init_gptpstatusmessage(pybind11::module_& m) {
pybind11::class_<GPTPStatus, std::shared_ptr<GPTPStatus>, Message> gptpStatus(m, "GPTPStatus");
pybind11::class_<GPTPStatus::Timestamp>(gptpStatus, "Timestamp")
.def_readonly("seconds", &GPTPStatus::Timestamp::seconds)
.def_readonly("nanoseconds", &GPTPStatus::Timestamp::nanoseconds)
.def("to_seconds", &GPTPStatus::Timestamp::toSeconds, pybind11::call_guard<pybind11::gil_scoped_release>());
pybind11::class_<GPTPStatus::ScaledNanoSeconds>(gptpStatus, "ScaledNanoSeconds")
.def_readonly("nanosecondsMSB", &GPTPStatus::ScaledNanoSeconds::nanosecondsMSB)
.def_readonly("nanosecondsLSB", &GPTPStatus::ScaledNanoSeconds::nanosecondsLSB)
.def_readonly("fractionalNanoseconds", &GPTPStatus::ScaledNanoSeconds::fractionalNanoseconds);
pybind11::class_<GPTPStatus::PortID>(gptpStatus, "PortID")
.def_readonly("clockIdentity", &GPTPStatus::PortID::clockIdentity)
.def_readonly("portNumber", &GPTPStatus::PortID::portNumber);
pybind11::class_<GPTPStatus::ClockQuality>(gptpStatus, "ClockQuality")
.def_readonly("clockClass", &GPTPStatus::ClockQuality::clockClass)
.def_readonly("clockAccuracy", &GPTPStatus::ClockQuality::clockAccuracy)
.def_readonly("offsetScaledLogVariance", &GPTPStatus::ClockQuality::offsetScaledLogVariance);
pybind11::class_<GPTPStatus::SystemID>(gptpStatus, "SystemID")
.def_readonly("priority1", &GPTPStatus::SystemID::priority1)
.def_readonly("clockQuality", &GPTPStatus::SystemID::clockQuality)
.def_readonly("priority2", &GPTPStatus::SystemID::priority2)
.def_readonly("clockID", &GPTPStatus::SystemID::clockID);
pybind11::class_<GPTPStatus::PriorityVector>(gptpStatus, "PriorityVector")
.def_readonly("sysID", &GPTPStatus::PriorityVector::sysID)
.def_readonly("stepsRemoved", &GPTPStatus::PriorityVector::stepsRemoved)
.def_readonly("portID", &GPTPStatus::PriorityVector::portID)
.def_readonly("portNumber", &GPTPStatus::PriorityVector::portNumber);
pybind11::class_<GPTPStatus::ParentDS>(gptpStatus, "ParentDS")
.def_readonly("parentPortIdentity", &GPTPStatus::ParentDS::parentPortIdentity)
.def_readonly("cumulativeRateRatio", &GPTPStatus::ParentDS::cumulativeRateRatio)
.def_readonly("grandmasterIdentity", &GPTPStatus::ParentDS::grandmasterIdentity)
.def_readonly("gmClockQualityClockClass", &GPTPStatus::ParentDS::gmClockQualityClockClass)
.def_readonly("gmClockQualityClockAccuracy", &GPTPStatus::ParentDS::gmClockQualityClockAccuracy)
.def_readonly("gmClockQualityOffsetScaledLogVariance", &GPTPStatus::ParentDS::gmClockQualityOffsetScaledLogVariance)
.def_readonly("gmPriority1", &GPTPStatus::ParentDS::gmPriority1)
.def_readonly("gmPriority2", &GPTPStatus::ParentDS::gmPriority2);
pybind11::class_<GPTPStatus::CurrentDS>(gptpStatus, "CurrentDS")
.def_readonly("stepsRemoved", &GPTPStatus::CurrentDS::stepsRemoved)
.def_readonly("offsetFromMaster", &GPTPStatus::CurrentDS::offsetFromMaster)
.def_readonly("lastgmPhaseChange", &GPTPStatus::CurrentDS::lastgmPhaseChange)
.def_readonly("lastgmFreqChange", &GPTPStatus::CurrentDS::lastgmFreqChange)
.def_readonly("gmTimeBaseIndicator", &GPTPStatus::CurrentDS::gmTimeBaseIndicator)
.def_readonly("gmChangeCount", &GPTPStatus::CurrentDS::gmChangeCount)
.def_readonly("timeOfLastgmChangeEvent", &GPTPStatus::CurrentDS::timeOfLastgmChangeEvent)
.def_readonly("timeOfLastgmPhaseChangeEvent", &GPTPStatus::CurrentDS::timeOfLastgmPhaseChangeEvent)
.def_readonly("timeOfLastgmFreqChangeEvent", &GPTPStatus::CurrentDS::timeOfLastgmFreqChangeEvent);
gptpStatus.def_readonly("currentTime", &GPTPStatus::currentTime)
.def_readonly("gmPriority", &GPTPStatus::gmPriority)
.def_readonly("msOffsetNs", &GPTPStatus::msOffsetNs)
.def_readonly("isSync", &GPTPStatus::isSync)
.def_readonly("linkStatus", &GPTPStatus::linkStatus)
.def_readonly("linkDelayNS", &GPTPStatus::linkDelayNS)
.def_readonly("selectedRole", &GPTPStatus::selectedRole)
.def_readonly("asCapable", &GPTPStatus::asCapable)
.def_readonly("isSyntonized", &GPTPStatus::isSyntonized)
.def_readonly("lastRXSyncTS", &GPTPStatus::lastRXSyncTS)
.def_readonly("currentDS", &GPTPStatus::currentDS)
.def_readonly("parentDS", &GPTPStatus::parentDS);
}
} // namespace icsneo

View File

@ -30,7 +30,7 @@ void init_linmessage(pybind11::module_& m) {
.def_readwrite("BusRecovered", &LINStatusFlags::BusRecovered)
.def_readwrite("BreakOnly", &LINStatusFlags::BreakOnly);
pybind11::class_<LINMessage, std::shared_ptr<LINMessage>, Frame> linMessage(m, "LINMessage");
pybind11::class_<LINMessage, std::shared_ptr<LINMessage>, BusMessage> linMessage(m, "LINMessage");
pybind11::enum_<LINMessage::Type>(linMessage, "Type")
.value("NOT_SET", LINMessage::Type::NOT_SET)

View File

@ -7,7 +7,7 @@
namespace icsneo {
void init_mdiomessage(pybind11::module_& m) {
pybind11::class_<MDIOMessage, std::shared_ptr<MDIOMessage>, Frame> mdioMessage(m, "MDIOMessage");
pybind11::class_<MDIOMessage, std::shared_ptr<MDIOMessage>, BusMessage> mdioMessage(m, "MDIOMessage");
pybind11::enum_<MDIOMessage::Clause>(mdioMessage, "Clause")
.value("Clause45", MDIOMessage::Clause::Clause45)
.value("Clause22", MDIOMessage::Clause::Clause22);

View File

@ -9,12 +9,13 @@ namespace icsneo {
void init_message(pybind11::module_& m) {
pybind11::class_<Message, std::shared_ptr<Message>> message(m, "Message");
pybind11::enum_<Message::Type>(message, "Type")
.value("Frame", Message::Type::Frame)
.value("BusMessage", Message::Type::BusMessage)
.value("CANErrorCount", Message::Type::CANErrorCount)
.value("CANError", Message::Type::CANError)
.value("LINHeaderOnly", Message::Type::LINHeaderOnly)
.value("LINBreak", Message::Type::LINBreak)
.value("Invalid", Message::Type::Invalid)
.value("RawMessage", Message::Type::RawMessage)
.value("InternalMessage", Message::Type::InternalMessage)
.value("ReadSettings", Message::Type::ReadSettings)
.value("ResetStatus", Message::Type::ResetStatus)
.value("DeviceVersion", Message::Type::DeviceVersion)
@ -31,20 +32,22 @@ void init_message(pybind11::module_& m) {
.value("LiveData", Message::Type::LiveData)
.value("HardwareInfo", Message::Type::HardwareInfo)
.value("TC10Status", Message::Type::TC10Status)
.value("AppError", Message::Type::AppError);
.value("AppError", Message::Type::AppError)
.value("GPTPStatus", Message::Type::GPTPStatus)
.value("EthernetStatus", Message::Type::EthernetStatus);
message.def(pybind11::init<Message::Type>());
message.def_readonly("type", &Message::type);
message.def_readwrite("timestamp", &Message::timestamp);
pybind11::class_<RawMessage, std::shared_ptr<RawMessage>, Message>(m, "RawMessage")
.def_readwrite("network", &RawMessage::network)
.def_readwrite("data", &RawMessage::data);
pybind11::class_<InternalMessage, std::shared_ptr<InternalMessage>, Message>(m, "InternalMessage")
.def_readwrite("network", &InternalMessage::network)
.def_readwrite("data", &InternalMessage::data);
pybind11::class_<Frame, std::shared_ptr<Frame>, RawMessage>(m, "Frame")
.def_readwrite("description", &Frame::description)
.def_readwrite("transmitted", &Frame::transmitted)
.def_readwrite("error", &Frame::error);
pybind11::class_<BusMessage, std::shared_ptr<BusMessage>, InternalMessage>(m, "BusMessage")
.def_readwrite("description", &BusMessage::description)
.def_readwrite("transmitted", &BusMessage::transmitted)
.def_readwrite("error", &BusMessage::error);
}
} // namespace icsneo

View File

@ -13,31 +13,33 @@ void init_device(pybind11::module_& m) {
.def("get_serial", &Device::getSerial)
.def("get_serial_number", &Device::getSerialNumber)
.def("get_product_name", &Device::getProductName)
.def("open", [](Device& device) { return device.open(); })
.def("close", &Device::close)
.def("open", [](Device& device) { return device.open(); }, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("close", &Device::close, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("is_open", &Device::isOpen)
.def("go_online", &Device::goOnline)
.def("go_offline", &Device::goOffline)
.def("is_online", &Device::isOnline).def("enable_message_polling", &Device::enableMessagePolling)
.def("disable_message_polling", &Device::disableMessagePolling)
.def("go_online", &Device::goOnline, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("go_offline", &Device::goOffline, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("is_online", &Device::isOnline)
.def("enable_message_polling", &Device::enableMessagePolling, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("disable_message_polling", &Device::disableMessagePolling, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("is_message_polling_enabled", &Device::isMessagePollingEnabled)
.def("get_messages", [](Device& device) { return device.getMessages(); })
.def("get_messages", [](Device& device) { return device.getMessages(); }, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_current_message_count", &Device::getCurrentMessageCount)
.def("get_polling_message_limit", &Device::getPollingMessageLimit)
.def("set_polling_message_limit", &Device::setPollingMessageLimit)
.def("add_message_callback", &Device::addMessageCallback)
.def("remove_message_callback", &Device::removeMessageCallback)
.def("transmit", pybind11::overload_cast<std::shared_ptr<Frame>>(&Device::transmit))
.def("add_message_callback", &Device::addMessageCallback, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("remove_message_callback", &Device::removeMessageCallback, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("transmit", pybind11::overload_cast<std::shared_ptr<BusMessage>>(&Device::transmit), pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_supported_rx_networks", &Device::getSupportedRXNetworks, pybind11::return_value_policy::reference)
.def("get_supported_tx_networks", &Device::getSupportedTXNetworks, pybind11::return_value_policy::reference)
.def("get_rtc", &Device::getRTC)
.def("set_rtc", &Device::setRTC)
.def("get_rtc", &Device::getRTC, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_rtc", &Device::setRTC, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("describe", &Device::describe)
.def("is_online_supported", &Device::isOnlineSupported)
.def("supports_tc10", &Device::supportsTC10)
.def("request_tc10_wake", &Device::requestTC10Wake)
.def("request_tc10_sleep", &Device::requestTC10Sleep)
.def("get_tc10_status", &Device::getTC10Status)
.def("request_tc10_wake", &Device::requestTC10Wake, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("request_tc10_sleep", &Device::requestTC10Sleep, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_tc10_status", &Device::getTC10Status, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_gptp_status", &Device::getGPTPStatus, pybind11::arg("timeout") = std::chrono::milliseconds(100), pybind11::call_guard<pybind11::gil_scoped_release>())
.def("__repr__", &Device::describe);
}

View File

@ -48,14 +48,10 @@ void init_devicetype(pybind11::module_& m) {
.value("OBD2_PRO", DeviceType::Enum::OBD2_PRO)
.value("ECUChip_UART", DeviceType::Enum::ECUChip_UART)
.value("PLASMA", DeviceType::Enum::PLASMA)
.value("DONT_REUSE0", DeviceType::Enum::DONT_REUSE0)
.value("NEOAnalog", DeviceType::Enum::NEOAnalog)
.value("CT_OBD", DeviceType::Enum::CT_OBD)
.value("DONT_REUSE1", DeviceType::Enum::DONT_REUSE1)
.value("DONT_REUSE2", DeviceType::Enum::DONT_REUSE2)
.value("ION", DeviceType::Enum::ION)
.value("RADStar", DeviceType::Enum::RADStar)
.value("DONT_REUSE3", DeviceType::Enum::DONT_REUSE3)
.value("VCAN4_4", DeviceType::Enum::VCAN4_4)
.value("VCAN4_2", DeviceType::Enum::VCAN4_2)
.value("CMProbe", DeviceType::Enum::CMProbe)

View File

@ -13,16 +13,21 @@ void init_network(pybind11::module_&);
void init_devicetype(pybind11::module_&);
void init_message(pybind11::module_&);
void init_canmessage(pybind11::module_&);
void init_canerrormessage(pybind11::module_&);
void init_ethernetmessage(pybind11::module_&);
void init_linmessage(pybind11::module_&);
void init_tc10statusmessage(pybind11::module_&);
void init_gptpstatusmessage(pybind11::module_&);
void init_mdiomessage(pybind11::module_&);
void init_ethernetstatusmessage(pybind11::module_&);
void init_device(pybind11::module_&);
void init_messagefilter(pybind11::module_&);
void init_messagecallback(pybind11::module_&);
void init_version(pybind11::module_&);
PYBIND11_MODULE(icsneopy, m) {
pybind11::options options;
options.disable_enum_members_docstring();
m.doc() = "libicsneo Python module";
init_event(m);
@ -33,10 +38,13 @@ PYBIND11_MODULE(icsneopy, m) {
init_network(m);
init_message(m);
init_canmessage(m);
init_canerrormessage(m);
init_ethernetmessage(m);
init_linmessage(m);
init_tc10statusmessage(m);
init_gptpstatusmessage(m);
init_mdiomessage(m);
init_ethernetstatusmessage(m);
init_messagefilter(m);
init_messagecallback(m);
init_device(m);

View File

View File

@ -0,0 +1,17 @@
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
# RustRover
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

View File

@ -0,0 +1,293 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "bindgen"
version = "0.71.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3"
dependencies = [
"bitflags",
"cexpr",
"clang-sys",
"itertools",
"log",
"prettyplease",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn",
]
[[package]]
name = "bitflags"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clang-sys"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
name = "either"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "glob"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
[[package]]
name = "icsneoc2rs"
version = "0.1.0"
dependencies = [
"bindgen",
"scopeguard",
]
[[package]]
name = "itertools"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]]
name = "libc"
version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]]
name = "libloading"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [
"cfg-if",
"windows-targets",
]
[[package]]
name = "log"
version = "0.4.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "prettyplease"
version = "0.2.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "rustc-hash"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "syn"
version = "2.0.96"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

View File

@ -0,0 +1,22 @@
[package]
name = "icsneoc2rs"
version = "0.1.0"
edition = "2021"
[lib]
name = "icsneoc2rs"
crate-type = ["rlib", "staticlib", "cdylib"]
path = "src/lib.rs"
[[example]]
name = "simple"
path = "examples/simple.rs"
[dev-dependencies]
scopeguard = "1.2.0"
[features]
static = []
[build-dependencies]
bindgen = "0.71.1"

View File

@ -0,0 +1,40 @@
use std::env;
use std::path::PathBuf;
pub fn main() {
// Tell cargo to look for shared libraries in the specified directory
println!("cargo:rustc-link-search=../../../build");
// Tell cargo to tell rustc to link the system
// shared library.
println!("cargo:rustc-link-lib=icsneoc2");
// The bindgen::Builder is the main entry point
// to bindgen, and lets you build up options for
// the resulting bindings.
let bindings = bindgen::Builder::default()
// The input header we would like to generate
// bindings for.
.header("../../../include/icsneo/icsneoc2.h")
.clang_arg("-I../../../include")
// Tell cargo to invalidate the built crate whenever any of the
// included header files changed.
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.derive_default(true)
.derive_debug(true)
.derive_partialeq(true)
.derive_copy(true)
.default_enum_style(bindgen::EnumVariation::Rust { non_exhaustive: true })
.default_alias_style(bindgen::AliasVariation::TypeAlias)
.generate_cstr(true)
// Finish the builder and generate the bindings.
.generate()
// Unwrap the Result and panic on failure.
.expect("Unable to generate bindings");
// Write the bindings to the $OUT_DIR/bindings.rs file.
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("bindings.rs"))
.expect("Couldn't write bindings!");
}

View File

@ -0,0 +1,333 @@
use icsneoc2rs::ffi::_icsneoc2_msg_bus_type_t::*;
use icsneoc2rs::ffi::_icsneoc2_msg_type_t::*;
use icsneoc2rs::ffi::_icsneoc2_netid_t;
use icsneoc2rs::ffi::_icsneoc2_netid_t::*;
use icsneoc2rs::ffi::_icsneoc2_open_options_t::*;
use icsneoc2rs::ffi::icsneoc2_message_t;
use icsneoc2rs::ffi::icsneoc2_msg_bus_type_t;
use icsneoc2rs::ffi::icsneoc2_msg_type_t;
use icsneoc2rs::ffi::icsneoc2_netid_t;
use icsneoc2rs::Device;
use icsneoc2rs::Error;
use icsneoc2rs::Result as ICSNeoResult;
use scopeguard::defer;
fn main() -> ICSNeoResult<()> {
print!("Finding devices... ");
let devices = Device::find_all(255)?;
println!(
"OK, {} device{} found...",
devices.len(),
if devices.len() > 1 { "s" } else { "" }
);
for device in devices {
device.device_is_valid()?;
// Get description of the device
let description = match device.device_description_get() {
Ok(description) => description,
Err(err) => {
println!("Failed to get description of device: {err}");
continue;
}
};
println!("{} @ Handle {:?}", description, device.handle);
// Get/Set open options
let mut open_options = match device.device_open_options_get() {
Ok(value) => value,
Err(err) => {
println!("Failed to get open options of device: {err}");
continue;
}
};
// Disable Syncing RTC and going online
open_options &= !(icsneoc2_open_options_sync_rtc as u32);
open_options &= !(icsneoc2_open_options_go_online as u32);
match device.device_open_options_set(open_options) {
Ok(value) => value,
Err(err) => {
print_error(&device, &description, err);
continue;
}
};
println!("\tDevice open options: {open_options:#02x}");
// Open the device
print!("\tOpening device... ");
match device.device_open() {
Ok(value) => value,
Err(err) => {
print_error(&device, &description, err);
continue;
}
};
println!("OK");
defer! {
print_device_events(&device, &description).expect("Critical: Failed to print device events");
}
// Get timestamp resolution of the device
print!("\tGetting timestamp resolution... ");
let timestamp_resolution = match device.device_timestamp_resolution_get() {
Ok(value) => value,
Err(err) => {
print_error(&device, &description, err);
continue;
}
};
println!("{timestamp_resolution}ns");
// Get baudrates for HSCAN
print!("\tGetting HSCAN baudrate... ");
let baudrate = match device.device_baudrate_get(icsneoc2_netid_hscan) {
Ok(value) => value,
Err(err) => {
print_error(&device, &description, err);
continue;
}
};
println!("{baudrate}mbit/s");
// Get FD baudrates for HSCAN
print!("\tGetting FD HSCAN baudrate... ");
let fd_baudrate = match device.device_canfd_baudrate_get(icsneoc2_netid_hscan) {
Ok(value) => value,
Err(err) => {
print_error(&device, &description, err);
continue;
}
};
println!("{fd_baudrate}mbit/s");
// Set baudrates for HSCAN
// saveToDevice: If this is set to true, the baudrate will be saved on the device
// and will persist through a power cycle
print!("\tSetting HSCAN baudrate... ");
let save_to_device = false;
match device.device_baudrate_set(icsneoc2_netid_hscan, baudrate, save_to_device) {
Ok(value) => value,
Err(err) => {
print_error(&device, &description, err);
continue;
}
};
println!("OK");
// Set FD baudrates for HSCAN
print!("\tSetting FD HSCAN baudrate... ");
match device.device_canfd_baudrate_set(icsneoc2_netid_hscan, fd_baudrate, save_to_device) {
Ok(value) => value,
Err(err) => {
print_error(&device, &description, err);
continue;
}
};
println!("OK");
// Get RTC
print!("\tGetting RTC... ");
let current_rtc = match device.device_rtc_get() {
Ok(value) => value,
Err(err) => {
print_error(&device, &description, err);
continue;
}
};
println!("{current_rtc}");
// Set RTC
print!("\tSetting RTC... ");
match device.device_rtc_set(current_rtc) {
Ok(value) => value,
Err(err) => {
print_error(&device, &description, err);
continue;
}
};
println!("OK");
// Get RTC
print!("\tGetting RTC... ");
let rtc = match device.device_rtc_get() {
Ok(value) => value,
Err(err) => {
print_error(&device, &description, err);
continue;
}
};
println!("{rtc}");
// Go online, start acking traffic
print!("\tGoing online... ");
match device.device_go_online(true) {
Ok(value) => value,
Err(err) => {
print_error(&device, &description, err);
continue;
}
};
// Redundant check to show how to check if the device is online, if the previous
// icsneoc2_device_go_online call was successful we can assume we are online already
let is_online = match device.device_is_online() {
Ok(value) => value,
Err(err) => {
print_error(&device, &description, err);
continue;
}
};
println!("{}", if is_online { "Online" } else { "Offline" });
// Transmit CAN messages
if transmit_can_messages(&device).is_err() {
continue;
}
println!("\tWaiting 1 second for messages...");
std::thread::sleep(std::time::Duration::from_secs(1));
// Get the messages
let messages = match device.device_messages_get(20000, 3000) {
Ok(messages) => messages,
Err(err) => {
print_error(&device, &description, err);
continue;
}
};
// Process the messages
if process_messages(&device, messages).is_err() {
continue;
}
// Close the device
print!("\tClosing device... ");
match device.device_close() {
Ok(value) => value,
Err(err) => {
print_error(&device, &description, err);
continue;
}
};
println!("OK");
}
Ok(())
}
fn print_error(device: &Device, device_description: &String, err: Error) {
println!("Failed: {err}");
print_device_events(device, device_description)
.expect("Critical: Failed to print device events");
}
fn transmit_can_messages(device: &Device) -> ICSNeoResult<()> {
const MESSAGE_COUNT: usize = 100;
println!("\tTransmitting {MESSAGE_COUNT} messages...");
for i in 0..MESSAGE_COUNT {
let message = match device.message_can_create(1) {
Ok(messages) => messages[0],
Err(err) => {
println!("Failed to create CAN message #{i}: {err}");
continue;
}
};
device.message_netid_set(message, icsneoc2_netid_hscan)?;
device.message_can_arbid_set(message, 0x10)?;
device.message_can_canfd_set(message, true)?;
device.message_can_extended_set(message, true)?;
device.message_can_baudrate_switch_set(message, true)?;
device.message_data_set(
message,
vec![
(i >> 56 & 0xFF) as u8,
(i >> 48 & 0xFF) as u8,
(i >> 40 & 0xFF) as u8,
(i >> 32 & 0xFF) as u8,
(i >> 24 & 0xFF) as u8,
(i >> 16 & 0xFF) as u8,
(i >> 8 & 0xFF) as u8,
(i & 0xFF) as u8,
],
)?;
device.message_can_dlc_set(message, -1)?;
device.device_messages_transmit(vec![message])?;
}
Ok(())
}
fn process_messages(device: &Device, messages: Vec<*mut icsneoc2_message_t>) -> ICSNeoResult<()> {
let mut tx_count: u32 = 0;
for (i, message) in messages.iter().enumerate() {
// Get the message type
let msg_type = device.message_type_get(*message)?;
// Get the message type name
let msg_type_name = device.message_type_name_get(msg_type)?;
// Check if the message is a bus message, ignore otherwise
if msg_type != icsneoc2_msg_type_bus as icsneoc2_msg_type_t {
println!("\tIgnoring message type : {msg_type} ({msg_type_name})");
continue;
}
// Get the message bus type
let msg_bus_type = device.message_bus_type_get(*message)?;
// Get the message type name
let msg_bus_type_name = device.bus_type_name_get(msg_bus_type)?;
// Check if message is a transmit message
if device.message_is_transmit(*message)? {
tx_count += 1;
continue;
}
println!("\t{i} Message type: {msg_type} bus type: {msg_bus_type_name} ({msg_bus_type})\n");
// Check if the message is a CAN message, ignore otherwise
if msg_bus_type == icsneoc2_msg_bus_type_can as icsneoc2_msg_bus_type_t {
let netid = device.message_netid_get(*message)?;
let netid_name = device.netid_name_get(netid)?;
let arbid = device.message_can_arbid_get(*message)?;
let dlc = device.message_can_dlc_get(*message)?;
let is_remote = device.message_can_is_remote(*message)?;
let is_canfd = device.message_can_is_canfd(*message)?;
let is_extended = device.message_can_is_extended(*message)?;
let data = device.message_data_get(*message, 64)?;
// Finally lets print the RX message
println!("\t NetID: {netid_name} ({:#02x})\tArbID: {arbid:#02x}\t DLC: {dlc}\t Remote: {is_remote}\t \
CANFD: {is_canfd}\t Extended: {is_extended}\t Data length: {}\n", netid as icsneoc2_netid_t,data.len());
println!("\t Data: {data:#02x?}\n");
} else {
println!("\tIgnoring bus message type: {msg_bus_type} ({msg_bus_type_name})\n");
}
}
println!(
"\tReceived {} messages total, {} were TX messages",
messages.len(),
tx_count
);
Ok(())
}
fn print_device_events(device: &Device, device_description: &String) -> ICSNeoResult<()> {
let events = match device.device_events_get(1024) {
Ok(events) => events,
Err(err) => {
println!("Failed to get device events: {err}");
return Err(err);
}
};
for (i, event) in events.iter().enumerate() {
let description = match device.event_description_get(*event) {
Ok(description) => description,
Err(err) => {
println!("Failed to get event description: {err}");
continue;
}
};
println!("\t{device_description}: Event {i}: {description}");
}
// Get global events
let global_events = match device.events_get(1024) {
Ok(events) => events,
Err(err) => {
println!("Failed to get global events: {err}");
return Err(err);
}
};
for (i, event) in global_events.iter().enumerate() {
let description = match device.event_description_get(*event) {
Ok(description) => description,
Err(err) => {
println!("Failed to get event description: {err}");
continue;
}
};
println!("\t{device_description}: Global Event {i}: {description}");
}
println!(
"\t{device_description}: Received {} events and {} global events\n",
events.len(),
global_events.len()
);
Ok(())
}

View File

@ -0,0 +1,5 @@
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

View File

@ -0,0 +1,902 @@
pub mod ffi;
use ffi::_icsneoc2_error_t::{icsneoc2_error_invalid_device, icsneoc2_error_success};
use ffi::_icsneoc2_open_options_t::*;
use ffi::*;
use std::ffi::CString;
use std::fmt::Display;
#[derive(Debug, Clone)]
pub enum Error {
/// icsneoc2 API error
APIError(icsneoc2_error_t, String),
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::APIError(error_code, error_string) => write!(f, "API Error: \"{}\" ({})", error_string, error_code),
}
}
}
pub type Result<T> = core::result::Result<T, Error>;
#[derive(Debug, Clone)]
pub struct Device {
/// Handle to the device.
pub handle: *mut icsneoc2_device_t,
}
impl Drop for Device {
fn drop(&mut self) {
// Make sure the device is closed before we go out of scope.
self.device_close().expect("Failed to close device!");
}
}
impl Device {
pub fn find_all(device_count: u32) -> Result<Vec<Self>> {
// Find all the devices
const MAX_DEVICE_COUNT: u32 = 255;
let mut devices: [*mut icsneoc2_device_t; MAX_DEVICE_COUNT as usize] =
[std::ptr::null_mut(); 255];
let mut devices_count: u32 = if device_count > MAX_DEVICE_COUNT {
MAX_DEVICE_COUNT
} else {
device_count
};
let res = unsafe {
icsneoc2_device_find_all(
devices.as_mut_ptr(),
&mut devices_count as *mut u32,
std::ptr::null_mut(),
)
};
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
// Return the results
let found_devices: Vec<Self> = devices[..devices_count as usize]
.iter()
.map(|d| Self { handle: *d })
.collect();
Ok(found_devices)
}
pub fn bus_type_name_get(&self, bus_type: icsneoc2_msg_bus_type_t) -> Result<String> {
// Get the string
let mut str: Vec<u8> = vec![0; 255];
let mut str_length: u32 = 255;
let res = unsafe {
icsneoc2_bus_type_name_get(bus_type, str.as_mut_ptr() as *mut i8, &mut str_length)
};
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
// Convert the vec to an String
str.resize(str_length as usize, 0);
let str_string = {
CString::new(str)
.expect("CString::new failed")
.into_string()
.expect("CString::new::into_string")
};
Ok(str_string)
}
pub fn device_baudrate_get(&self, netid: _icsneoc2_netid_t) -> Result<u64> {
let mut value: u64 = 0;
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_device_baudrate_get(self.handle, netid as icsneoc2_netid_t, &mut value) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(value)
}
pub fn device_baudrate_set(
&self,
netid: _icsneoc2_netid_t,
value: u64,
save: bool,
) -> Result<()> {
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_device_baudrate_set(self.handle, netid as icsneoc2_netid_t, value, save) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(())
}
pub fn device_canfd_baudrate_get(&self, netid: _icsneoc2_netid_t) -> Result<u64> {
let mut value: u64 = 0;
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_device_canfd_baudrate_get(self.handle, netid as icsneoc2_netid_t, &mut value) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(value)
}
pub fn device_canfd_baudrate_set(
&self,
netid: _icsneoc2_netid_t,
value: u64,
save: bool,
) -> Result<()> {
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_device_canfd_baudrate_set(self.handle, netid as icsneoc2_netid_t, value, save) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(())
}
pub fn device_close(&self) -> Result<()> {
let res: icsneoc2_error_t = unsafe { ffi::icsneoc2_device_close(self.handle) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(())
}
pub fn device_description_get(&self) -> Result<String> {
// Get the error code
let mut description: Vec<u8> = vec![0; 255];
let mut description_length: u32 = 255;
let res = unsafe {
icsneoc2_device_description_get(
self.handle,
description.as_mut_ptr() as *mut i8,
&mut description_length,
)
};
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
// Convert the vec to an String
description.resize(description_length as usize, 0);
let description_str = {
CString::new(description)
.expect("CString::new failed")
.into_string()
.expect("CString::new::into_string")
};
Ok(description_str)
}
pub fn device_events_get(&self, event_count: u32) -> Result<Vec<*mut icsneoc2_event_t>> {
let mut events: Vec<*mut icsneoc2_event_t> =
vec![std::ptr::null_mut(); event_count as usize];
let mut event_count: u32 = event_count;
let res: icsneoc2_error_t = unsafe {
ffi::icsneoc2_device_events_get(self.handle, events.as_mut_ptr(), &mut event_count)
};
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
events.resize(event_count as usize, std::ptr::null_mut());
Ok(events)
}
pub fn device_go_online(&self, go_online: bool) -> Result<()> {
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_device_go_online(self.handle, go_online) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(())
}
pub fn device_is_online(&self) -> Result<bool> {
let mut is_online: bool = false;
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_device_is_online(self.handle, &mut is_online) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(is_online)
}
pub fn device_is_online_supported(&self) -> Result<bool> {
let mut is_online_supported: bool = false;
let res: icsneoc2_error_t = unsafe {
ffi::icsneoc2_device_is_online_supported(self.handle, &mut is_online_supported)
};
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(is_online_supported)
}
pub fn device_is_valid(&self) -> Result<bool> {
let res: icsneoc2_error_t = unsafe { icsneoc2_device_is_valid(self.handle) };
match res {
res if res == icsneoc2_error_success as icsneoc2_error_t => Ok(true),
res if res == icsneoc2_error_invalid_device as icsneoc2_error_t => Ok(false),
_ => Err(Self::error_code_get(res)),
}
}
pub fn device_is_open(&self) -> Result<bool> {
let mut is_open: bool = false;
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_device_is_open(self.handle, &mut is_open) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(is_open)
}
pub fn device_is_disconnected(&self) -> Result<bool> {
let mut is_disconnected: bool = false;
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_device_is_disconnected(self.handle, &mut is_disconnected) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(is_disconnected)
}
pub fn device_load_default_settings(&self, save: bool) -> Result<()> {
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_device_load_default_settings(self.handle, save) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(())
}
pub fn device_message_count_get(&self) -> Result<u32> {
let mut value: u32 = 0;
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_device_message_count_get(self.handle, &mut value) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(value)
}
pub fn device_message_polling_get(&self) -> Result<bool> {
let mut value: bool = false;
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_device_message_polling_get(self.handle, &mut value) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(value)
}
pub fn device_message_polling_limit_get(&self) -> Result<u32> {
let mut value: u32 = 0;
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_device_message_polling_limit_get(self.handle, &mut value) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(value)
}
pub fn device_message_polling_set(&self, enable: bool) -> Result<()> {
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_device_message_polling_set(self.handle, enable) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(())
}
pub fn device_message_polling_set_limit(&self, value: u32) -> Result<()> {
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_device_message_polling_set_limit(self.handle, value) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(())
}
pub fn device_messages_get(
&self,
message_count: u32,
timeout_ms: u32,
) -> Result<Vec<*mut icsneoc2_message_t>> {
let mut messages: Vec<*mut icsneoc2_message_t> =
vec![std::ptr::null_mut(); message_count as usize];
let mut message_count: u32 = message_count;
let res: icsneoc2_error_t = unsafe {
ffi::icsneoc2_device_messages_get(
self.handle,
messages.as_mut_ptr(),
&mut message_count,
timeout_ms,
)
};
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
messages.resize(message_count as usize, std::ptr::null_mut());
Ok(messages)
}
pub fn device_messages_transmit(&self, messages: Vec<*mut icsneoc2_message_t>) -> Result<u32> {
let mut messages_count: u32 = messages.len() as u32;
let mut messages = messages;
let res: icsneoc2_error_t = unsafe {
ffi::icsneoc2_device_messages_transmit(
self.handle,
messages.as_mut_ptr(),
&mut messages_count,
)
};
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(messages_count)
}
pub fn device_open(&self) -> Result<()> {
let res: icsneoc2_error_t = unsafe { ffi::icsneoc2_device_open(self.handle) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(())
}
pub fn device_open_options_get(&self) -> Result<ffi::icsneoc2_open_options_t> {
let mut open_options: ffi::icsneoc2_open_options_t =
icsneoc2_open_options_none as ffi::icsneoc2_open_options_t;
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_device_open_options_get(self.handle, &mut open_options) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(open_options)
}
pub fn device_open_options_set(
&self,
open_options: ffi::icsneoc2_open_options_t,
) -> Result<()> {
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_device_open_options_set(self.handle, open_options) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(())
}
pub fn device_rtc_get(&self) -> Result<i64> {
let mut value: i64 = 0;
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_device_rtc_get(self.handle, &mut value) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(value)
}
pub fn device_rtc_set(&self, value: i64) -> Result<()> {
let res: icsneoc2_error_t = unsafe { ffi::icsneoc2_device_rtc_set(self.handle, value) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(())
}
pub fn device_serial_get(&self) -> Result<String> {
// Get the string
let mut str: Vec<u8> = vec![0; 255];
let mut str_length: u32 = 255;
let res = unsafe {
icsneoc2_device_serial_get(self.handle, str.as_mut_ptr() as *mut i8, &mut str_length)
};
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
// Convert the vec to an String
str.resize(str_length as usize, 0);
let str_string = {
CString::new(str)
.expect("CString::new failed")
.into_string()
.expect("CString::new::into_string")
};
Ok(str_string)
}
pub fn device_supports_tc10(&self) -> Result<bool> {
let mut value: bool = false;
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_device_supports_tc10(self.handle, &mut value) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(value)
}
pub fn device_timestamp_resolution_get(&self) -> Result<u32> {
let mut timestamp_resolution: u32 = 0;
let res: icsneoc2_error_t = unsafe {
ffi::icsneoc2_device_timestamp_resolution_get(self.handle, &mut timestamp_resolution)
};
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(timestamp_resolution)
}
pub fn device_type_from_type(&self, device_type: icsneoc2_devicetype_t) -> Result<String> {
// Get the string
let mut str: Vec<u8> = vec![0; 255];
let mut str_length: u32 = 255;
let res = unsafe {
icsneoc2_device_type_name_get(
device_type,
str.as_mut_ptr() as *mut i8,
&mut str_length,
)
};
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
// Convert the vec to an String
str.resize(str_length as usize, 0);
let str_string = {
CString::new(str)
.expect("CString::new failed")
.into_string()
.expect("CString::new::into_string")
};
Ok(str_string)
}
pub fn device_type_get(&self) -> Result<icsneoc2_devicetype_t> {
let mut value: icsneoc2_devicetype_t = 0;
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_device_type_get(self.handle, &mut value) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(value)
}
pub fn error_code_get(error_code: icsneoc2_error_t) -> Error {
// Get the error code
let mut error_description: Vec<u8> = vec![0; 255];
let mut error_description_length: u32 = 255;
let res = unsafe {
icsneoc2_error_code_get(
error_code,
error_description.as_mut_ptr() as *mut i8,
&mut error_description_length,
)
};
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Error::APIError(res, "icsneoc2_error_code_get() failed.".to_string());
}
// Convert the vec to an String
error_description.resize(error_description_length as usize, 0);
let error_str = CString::new(error_description)
.expect("CString::new failed")
.into_string()
.expect("CString::new::into_string");
Error::APIError(error_code, error_str)
}
pub fn event_description_get(&self, event: *mut icsneoc2_event_t) -> Result<String> {
// Get the string
let mut str: Vec<u8> = vec![0; 255];
let mut str_length: u32 = 255;
let res = unsafe {
icsneoc2_event_description_get(event, str.as_mut_ptr() as *mut i8, &mut str_length)
};
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
// Convert the vec to an String
str.resize(str_length as usize, 0);
let str_string = {
CString::new(str)
.expect("CString::new failed")
.into_string()
.expect("CString::new::into_string")
};
Ok(str_string)
}
pub fn events_get(&self, event_count: u32) -> Result<Vec<*mut icsneoc2_event_t>> {
let mut events: Vec<*mut icsneoc2_event_t> =
vec![std::ptr::null_mut(); event_count as usize];
let mut event_count: u32 = event_count;
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_events_get(events.as_mut_ptr(), &mut event_count) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
events.resize(event_count as usize, std::ptr::null_mut());
Ok(events)
}
pub fn message_bus_type_get(
&self,
message: *mut icsneoc2_message_t,
) -> Result<icsneoc2_msg_bus_type_t> {
let mut value: icsneoc2_msg_bus_type_t = 0;
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_message_bus_type_get(self.handle, message, &mut value) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(value)
}
pub fn message_can_arbid_get(&self, message: *mut icsneoc2_message_t) -> Result<u32> {
let mut value: u32 = 0;
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_message_can_arbid_get(self.handle, message, &mut value) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(value)
}
pub fn message_can_arbid_set(
&self,
message: *mut icsneoc2_message_t,
value: u32,
) -> Result<()> {
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_message_can_arbid_set(self.handle, message, value) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(())
}
pub fn message_can_baudrate_switch_get(
&self,
message: *mut icsneoc2_message_t,
) -> Result<bool> {
let mut value: bool = false;
let res: icsneoc2_error_t = unsafe {
ffi::icsneoc2_message_can_baudrate_switch_get(self.handle, message, &mut value)
};
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(value)
}
pub fn message_can_baudrate_switch_set(
&self,
message: *mut icsneoc2_message_t,
value: bool,
) -> Result<()> {
let res: icsneoc2_error_t = unsafe {
ffi::icsneoc2_message_can_baudrate_switch_set(self.handle, message, value)
};
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(())
}
pub fn message_can_canfd_set(
&self,
message: *mut icsneoc2_message_t,
value: bool,
) -> Result<()> {
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_message_can_canfd_set(self.handle, message, value) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(())
}
pub fn message_can_create(&self, message_count: u32) -> Result<Vec<*mut icsneoc2_message_t>> {
let mut messages: Vec<*mut icsneoc2_message_t> =
vec![std::ptr::null_mut(); message_count as usize];
let res: icsneoc2_error_t = unsafe {
ffi::icsneoc2_message_can_create(self.handle, messages.as_mut_ptr(), message_count)
};
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(messages)
}
pub fn message_can_dlc_get(&self, message: *mut icsneoc2_message_t) -> Result<i32> {
let mut value: i32 = 0;
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_message_can_dlc_get(self.handle, message, &mut value) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(value)
}
pub fn message_can_dlc_set(&self, message: *mut icsneoc2_message_t, value: i32) -> Result<()> {
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_message_can_dlc_set(self.handle, message, value) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(())
}
pub fn message_can_error_state_indicator_get(
&self,
message: *mut icsneoc2_message_t,
) -> Result<bool> {
let mut value: bool = false;
let res: icsneoc2_error_t = unsafe {
ffi::icsneoc2_message_can_error_state_indicator_get(self.handle, message, &mut value)
};
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(value)
}
pub fn message_can_extended_set(
&self,
message: *mut icsneoc2_message_t,
value: bool,
) -> Result<()> {
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_message_can_extended_set(self.handle, message, value) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(())
}
pub fn message_can_free(&self, message: *mut icsneoc2_message_t) -> Result<()> {
let res: icsneoc2_error_t = unsafe { ffi::icsneoc2_message_can_free(self.handle, message) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(())
}
pub fn message_can_is_canfd(&self, message: *mut icsneoc2_message_t) -> Result<bool> {
let mut value: bool = false;
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_message_can_is_canfd(self.handle, message, &mut value) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(value)
}
pub fn message_can_is_extended(&self, message: *mut icsneoc2_message_t) -> Result<bool> {
let mut value: bool = false;
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_message_can_is_extended(self.handle, message, &mut value) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(value)
}
pub fn message_can_is_remote(&self, message: *mut icsneoc2_message_t) -> Result<bool> {
let mut value: bool = false;
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_message_can_is_remote(self.handle, message, &mut value) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(value)
}
pub fn message_data_get(
&self,
message: *mut icsneoc2_message_t,
data_length: u32,
) -> Result<Vec<u8>> {
let mut data: Vec<u8> = vec![0; data_length as usize];
let mut data_length = data_length;
let res: icsneoc2_error_t = unsafe {
ffi::icsneoc2_message_data_get(
self.handle,
message,
data.as_mut_ptr(),
&mut data_length,
)
};
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
data.resize(data_length as usize, 0);
Ok(data)
}
pub fn message_data_set(&self, message: *mut icsneoc2_message_t, data: Vec<u8>) -> Result<()> {
let mut data = data;
let res: icsneoc2_error_t = unsafe {
ffi::icsneoc2_message_data_set(
self.handle,
message,
data.as_mut_ptr(),
data.len() as u32,
)
};
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(())
}
pub fn message_is_transmit(&self, message: *mut icsneoc2_message_t) -> Result<bool> {
let mut value: bool = false;
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_message_is_transmit(self.handle, message, &mut value) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(value)
}
pub fn message_is_valid(&self, message: *mut icsneoc2_message_t) -> Result<bool> {
let mut value: bool = false;
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_message_is_valid(self.handle, message, &mut value) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(value)
}
pub fn message_netid_get(&self, message: *mut icsneoc2_message_t) -> Result<_icsneoc2_netid_t> {
let mut value: icsneoc2_netid_t = 0;
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_message_netid_get(self.handle, message, &mut value) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
// TODO: This is gross, there is probably a better way to do this.
// TryFrom impl would be a lot of work to implement because there are so many values.
// We are relying on the function call to always return a valid value.
let value: _icsneoc2_netid_t = unsafe { std::mem::transmute(value as u32) };
Ok(value)
}
pub fn message_netid_set(
&self,
message: *mut icsneoc2_message_t,
value: _icsneoc2_netid_t,
) -> Result<()> {
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_message_netid_set(self.handle, message, value as icsneoc2_netid_t) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(())
}
pub fn message_type_get(
&self,
message: *mut icsneoc2_message_t,
) -> Result<icsneoc2_msg_type_t> {
let mut value: icsneoc2_msg_type_t = 0;
let res: icsneoc2_error_t =
unsafe { ffi::icsneoc2_message_type_get(self.handle, message, &mut value) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
Ok(value)
}
pub fn message_type_name_get(&self, msg_type: icsneoc2_msg_type_t) -> Result<String> {
// Get the string
let mut str: Vec<u8> = vec![0; 255];
let mut str_length: u32 = 255;
let res = unsafe {
icsneoc2_message_type_name_get(msg_type, str.as_mut_ptr() as *mut i8, &mut str_length)
};
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
// Convert the vec to an String
str.resize(str_length as usize, 0);
let str_string = {
CString::new(str)
.expect("CString::new failed")
.into_string()
.expect("CString::new::into_string")
};
Ok(str_string)
}
pub fn netid_name_get(&self, netid: _icsneoc2_netid_t) -> Result<String> {
// Get the string
let mut str: Vec<u8> = vec![0; 255];
let mut str_length: u32 = 255;
let res =
unsafe { icsneoc2_netid_name_get(netid as icsneoc2_netid_t, str.as_mut_ptr() as *mut i8, &mut str_length) };
// Check the error code
if res != icsneoc2_error_success as icsneoc2_error_t {
return Err(Self::error_code_get(res));
}
// Convert the vec to an String
str.resize(str_length as usize, 0);
let str_string = {
CString::new(str)
.expect("CString::new failed")
.into_string()
.expect("CString::new::into_string")
};
Ok(str_string)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_device_find_all() {
let devices = Device::find_all(0).unwrap();
assert_eq!(devices.len(), 0);
}
}

View File

@ -1,2 +1,2 @@
call "%VCVARS32%"
call "%VCVARS32_2022%"
call "ci\build-windows.bat"

View File

@ -1,2 +1,2 @@
call "%VCVARS64%"
call "%VCVARS64_2022%"
call "ci\build-windows.bat"

View File

@ -3,7 +3,7 @@
#include "icsneo/communication/message/serialnumbermessage.h"
#include "icsneo/communication/message/resetstatusmessage.h"
#include "icsneo/communication/message/readsettingsmessage.h"
#include "icsneo/communication/message/canerrorcountmessage.h"
#include "icsneo/communication/message/canerrormessage.h"
#include "icsneo/communication/message/neoreadmemorysdmessage.h"
#include "icsneo/communication/message/flashmemorymessage.h"
#include "icsneo/communication/message/extendedresponsemessage.h"
@ -19,7 +19,9 @@
#include "icsneo/communication/message/diskdatamessage.h"
#include "icsneo/communication/message/hardwareinfo.h"
#include "icsneo/communication/message/tc10statusmessage.h"
#include "icsneo/communication/message/gptpstatusmessage.h"
#include "icsneo/communication/message/apperrormessage.h"
#include "icsneo/communication/message/ethernetstatusmessage.h"
#include "icsneo/communication/command.h"
#include "icsneo/device/device.h"
#include "icsneo/communication/packet/canpacket.h"
@ -87,13 +89,13 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
result->timestamp *= timestampResolution;
switch(result->type) {
case Message::Type::Frame: {
case Message::Type::BusMessage: {
CANMessage& can = *static_cast<CANMessage*>(result.get());
can.network = packet->network;
break;
}
case Message::Type::CANErrorCount: {
CANErrorCountMessage& can = *static_cast<CANErrorCountMessage*>(result.get());
CANErrorMessage& can = *static_cast<CANErrorMessage*>(result.get());
can.network = packet->network;
break;
}
@ -178,6 +180,7 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
LINMessage& msg = *static_cast<LINMessage*>(result.get());
msg.network = packet->network;
msg.timestamp *= timestampResolution;
return true;
}
case Network::Type::MDIO: {
@ -230,32 +233,37 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
// They come in as CAN but we will handle them in the device rather than
// passing them onto the user.
if(packet->data.size() < 24) {
auto rawmsg = std::make_shared<RawMessage>(Network::NetID::Device);
auto rawmsg = std::make_shared<InternalMessage>(Network::NetID::Device);
result = rawmsg;
rawmsg->data = packet->data;
return true;
}
result = HardwareCANPacket::DecodeToMessage(packet->data);
if(!result) {
const auto can = std::dynamic_pointer_cast<CANMessage>(HardwareCANPacket::DecodeToMessage(packet->data));
if(!can) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return false; // A nullptr was returned, the packet was malformed
return false;
}
if(can->arbid == 0x162) {
result = EthernetStatusMessage::DecodeToMessage(can->data);
if(!result) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return false;
}
} else {
// TODO: move more handleNeoVIMessage handling here, the Decoder layer will parse the message and the Device layer can cache the values
can->network = packet->network;
result = can;
}
// Timestamps are in (resolution) ns increments since 1/1/2007 GMT 00:00:00.0000
// The resolution depends on the device
auto* raw = dynamic_cast<RawMessage*>(result.get());
if(raw == nullptr) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return false; // A nullptr was returned, the packet was malformed
}
raw->timestamp *= timestampResolution;
raw->network = packet->network;
result->timestamp *= timestampResolution;
return true;
}
case Network::NetID::DeviceStatus: {
// Just pass along the data, the device needs to handle this itself
result = std::make_shared<RawMessage>(packet->network, packet->data);
result = std::make_shared<InternalMessage>(packet->network, packet->data);
return true;
}
case Network::NetID::RED_INT_MEMORYREAD: {
@ -284,11 +292,11 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
}
case Network::NetID::ExtendedCommand: {
if(packet->data.size() < sizeof(ExtendedResponseMessage::PackedGenericResponse))
if(packet->data.size() < sizeof(ExtendedResponseMessage::ResponseHeader))
break; // Handle as a raw message, might not be a generic response
const auto& resp = *reinterpret_cast<ExtendedResponseMessage::PackedGenericResponse*>(packet->data.data());
switch(resp.header.command) {
const auto& resp = *reinterpret_cast<ExtendedResponseMessage::ResponseHeader*>(packet->data.data());
switch(resp.command) {
case ExtendedCommand::GetComponentVersions:
result = ComponentVersionPacket::DecodeToMessage(packet->data);
return true;
@ -298,17 +306,25 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
case ExtendedCommand::GenericBinaryInfo:
result = GenericBinaryStatusPacket::DecodeToMessage(packet->data);
return true;
case ExtendedCommand::GenericReturn:
result = std::make_shared<ExtendedResponseMessage>(resp.command, resp.returnCode);
case ExtendedCommand::GenericReturn: {
if(packet->data.size() < sizeof(ExtendedResponseMessage::PackedGenericResponse))
break;
const auto& packedResp = *reinterpret_cast<ExtendedResponseMessage::PackedGenericResponse*>(packet->data.data());
result = std::make_shared<ExtendedResponseMessage>(packedResp.command, packedResp.returnCode);
return true;
}
case ExtendedCommand::LiveData:
result = HardwareLiveDataPacket::DecodeToMessage(packet->data, report);
return true;
case ExtendedCommand::GetTC10Status:
result = TC10StatusMessage::DecodeToMessage(packet->data);
return true;
case ExtendedCommand::GetGPTPStatus: {
result = GPTPStatus::DecodeToMessage(packet->data, report);
return true;
}
default:
// No defined handler, treat this as a RawMessage
// No defined handler, treat this as a InternalMessage
break;
}
break;
@ -483,6 +499,6 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
}
// For the moment other types of messages will automatically be decoded as raw messages
result = std::make_shared<RawMessage>(packet->network, packet->data);
result = std::make_shared<InternalMessage>(packet->network, packet->data);
return true;
}

View File

@ -23,8 +23,8 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
result.clear();
switch(message->type) {
case Message::Type::Frame: {
auto frame = std::dynamic_pointer_cast<Frame>(message);
case Message::Type::BusMessage: {
auto frame = std::dynamic_pointer_cast<BusMessage>(message);
// Frame uses frame->data as the buffer unless directed otherwise
buffer = &frame->data;
@ -130,8 +130,8 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
break;
}
case Message::Type::RawMessage: {
auto raw = std::dynamic_pointer_cast<RawMessage>(message);
case Message::Type::InternalMessage: {
auto raw = std::dynamic_pointer_cast<InternalMessage>(message);
// Raw message uses raw->data as the buffer unless directed otherwise
buffer = &raw->data;
@ -243,7 +243,7 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
* In this case, command 0x06 is SetLEDState.
* This old command type is not really used anywhere else.
*/
auto canmsg = std::make_shared<RawMessage>(Network::NetID::Device);
auto canmsg = std::make_shared<InternalMessage>(Network::NetID::Device);
msg = canmsg;
if(arguments.empty()) {
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);

View File

@ -102,11 +102,11 @@ bool A2BWAVOutput::callIfMatch(const std::shared_ptr<Message>& message) const {
return false;
}
if(message->type != Message::Type::Frame) {
if(message->type != Message::Type::BusMessage) {
return false;
}
const auto& frameMsg = std::dynamic_pointer_cast<Frame>(message);
const auto& frameMsg = std::dynamic_pointer_cast<BusMessage>(message);
if(!frameMsg) {
return false;

View File

@ -0,0 +1,57 @@
#include "icsneo/communication/message/ethernetstatusmessage.h"
using namespace icsneo;
#pragma pack(push, 1)
enum LinkSpeed {
ethSpeed10,
ethSpeed100,
ethSpeed1000,
ethSpeedAutoNeg,
ethSpeed2500,
ethSpeed5000,
ethSpeed10000,
};
enum LinkMode {
OPETH_LINK_AUTO,
OPETH_LINK_MASTER,
OPETH_LINK_SLAVE,
OPETH_LINK_INVALID = 255,
};
struct Packet {
uint8_t state;
uint8_t speed;
uint8_t duplex;
uint16_t network;
uint8_t mode;
};
#pragma pack(pop)
std::shared_ptr<Message> EthernetStatusMessage::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
if(bytestream.size() < sizeof(Packet)) {
return nullptr;
}
Packet* packet = (Packet*)bytestream.data();
LinkSpeed speed;
switch(packet->speed) {
case ethSpeed10: speed = EthernetStatusMessage::LinkSpeed::LinkSpeed10; break;
case ethSpeed100: speed = EthernetStatusMessage::LinkSpeed::LinkSpeed100; break;
case ethSpeed1000: speed = EthernetStatusMessage::LinkSpeed::LinkSpeed1000; break;
case ethSpeedAutoNeg: speed = EthernetStatusMessage::LinkSpeed::LinkSpeedAuto; break;
case ethSpeed2500: speed = EthernetStatusMessage::LinkSpeed::LinkSpeed2500; break;
case ethSpeed5000: speed = EthernetStatusMessage::LinkSpeed::LinkSpeed5000; break;
case ethSpeed10000: speed = EthernetStatusMessage::LinkSpeed::LinkSpeed10000; break;
default: return nullptr;
}
LinkMode mode;
switch(packet->mode) {
case OPETH_LINK_INVALID: mode = EthernetStatusMessage::LinkMode::LinkModeInvalid; break;
case OPETH_LINK_AUTO: mode = EthernetStatusMessage::LinkMode::LinkModeAuto; break;
case OPETH_LINK_MASTER: mode = EthernetStatusMessage::LinkMode::LinkModeMaster; break;
case OPETH_LINK_SLAVE: mode = EthernetStatusMessage::LinkMode::LinkModeSlave; break;
default: return nullptr;
}
return std::make_shared<EthernetStatusMessage>(packet->network, packet->state, speed, packet->duplex, mode);
}

View File

@ -0,0 +1,219 @@
#include <icsneo/communication/message/gptpstatusmessage.h>
#include <icsneo/communication/message/extendedresponsemessage.h>
#include <cstring>
namespace icsneo {
typedef double float64;
typedef int64_t time_interval;
typedef uint64_t _clock_identity;
#pragma pack(push, 1)
struct port_identity {
_clock_identity clock_identity;
uint16_t port_number;
};
struct _scaled_ns {
int16_t nanoseconds_msb;
int64_t nanoseconds_lsb;
int16_t fractional_nanoseconds;
};
struct _clock_quality {
uint8_t clock_class;
uint8_t clock_accuracy;
uint16_t offset_scaled_log_variance;
};
struct system_identity {
uint8_t priority_1;
struct _clock_quality clock_quality;
uint8_t priority_2;
_clock_identity clock_identity;
};
struct _timestamp {
uint16_t seconds_msb;
uint32_t seconds_lsb;
uint32_t nanoseconds;
};
struct priority_vector {
struct system_identity sysid;
uint16_t steps_removed;
struct port_identity portid;
uint16_t port_number;
};
// IEEE 802.1AS-2020 14.3
// This is a read-only data structure
struct _current_ds {
uint16_t steps_removed;
time_interval offset_from_master;
struct _scaled_ns last_gm_phase_change;
float64 last_gm_freq_change;
uint16_t gm_time_base_indicator;
uint32_t gm_change_count;
uint32_t time_of_last_gm_change_event;
uint32_t time_of_last_gm_phase_change_event;
uint32_t time_of_last_gm_freq_change_event;
};
// IEEE 802.1AS-2020 14.4
// This is a read-only data structure
struct _parent_ds {
struct port_identity parent_port_identity;
int32_t cumulative_rate_ratio;
_clock_identity grandmaster_identity;
uint8_t gm_clock_quality_clock_class;
uint8_t gm_clock_quality_clock_accuracy;
uint16_t gm_clock_quality_offset_scaled_log_variance;
uint8_t gm_priority1;
uint8_t gm_priority2;
};
struct _GPTPStatus
{
struct _timestamp current_time;
struct priority_vector gm_priority;
int64_t ms_offset_ns;
uint8_t is_sync;
uint8_t link_status;
int64_t link_delay_ns;
uint8_t selected_role;
uint8_t as_capable;
uint8_t is_syntonized;
struct _timestamp last_rx_sync_ts; // t2 in IEEE 1588-2019 Figure-16
struct _current_ds current_ds;
struct _parent_ds parent_ds;
};
#pragma pack(pop)
static void SetField(GPTPStatus::Timestamp& output, const _timestamp& input) {
output.seconds = ((uint64_t)(input.seconds_msb) << 32) | ((uint64_t)input.seconds_lsb);
output.nanoseconds = input.nanoseconds;
}
static void SetField(GPTPStatus::PortID& output, const port_identity& input) {
output.clockIdentity = input.clock_identity;
output.portNumber = input.port_number;
}
static void SetField(GPTPStatus::ClockQuality& output, const _clock_quality& input) {
output.clockClass = input.clock_class;
output.clockAccuracy = input.clock_accuracy;
output.offsetScaledLogVariance = input.offset_scaled_log_variance;
}
static void SetField(GPTPStatus::SystemID& output, const system_identity& input) {
output.priority1 = input.priority_1;
SetField(output.clockQuality, input.clock_quality);
output.priority2 = input.priority_2;
output.clockID = input.clock_identity;
}
static void SetField(GPTPStatus::ScaledNanoSeconds& output, const _scaled_ns& input) {
output.nanosecondsMSB = input.nanoseconds_msb;
output.nanosecondsLSB = input.nanoseconds_lsb;
output.fractionalNanoseconds = input.fractional_nanoseconds;
}
static void SetField(GPTPStatus::PriorityVector& output, const priority_vector& input) {
SetField(output.sysID, input.sysid);
output.stepsRemoved = input.steps_removed;
SetField(output.portID, input.portid);
output.portNumber = input.port_number;
}
static void SetField(GPTPStatus::CurrentDS& output, const _current_ds& input) {
output.stepsRemoved = input.steps_removed;
output.offsetFromMaster = input.offset_from_master;
SetField(output.lastgmPhaseChange, input.last_gm_phase_change);
output.lastgmFreqChange = input.last_gm_freq_change;
output.gmTimeBaseIndicator = input.gm_time_base_indicator;
output.gmChangeCount = input.gm_change_count;
output.timeOfLastgmChangeEvent = input.time_of_last_gm_change_event;
output.timeOfLastgmPhaseChangeEvent = input.time_of_last_gm_phase_change_event;
output.timeOfLastgmFreqChangeEvent = input.time_of_last_gm_freq_change_event;
}
static void SetField(GPTPStatus::ParentDS& output, const _parent_ds& input) {
SetField(output.parentPortIdentity, input.parent_port_identity);
output.cumulativeRateRatio = input.cumulative_rate_ratio;
output.grandmasterIdentity = input.grandmaster_identity;
output.gmClockQualityClockClass = input.gm_clock_quality_clock_class;
output.gmClockQualityClockAccuracy = input.gm_clock_quality_clock_accuracy;
output.gmClockQualityOffsetScaledLogVariance = input.gm_clock_quality_offset_scaled_log_variance;
output.gmPriority1 = input.gm_priority1;
output.gmPriority2 = input.gm_priority2;
}
[[maybe_unused]] static void SetField(uint8_t& output, const uint8_t& input) {
output = input;
}
[[maybe_unused]] static void SetField(uint16_t& output, const uint16_t& input) {
output = input;
}
[[maybe_unused]] static void SetField(uint32_t& output, const uint32_t& input) {
output = input;
}
[[maybe_unused]] static void SetField(uint64_t& output, const uint64_t& input) {
output = input;
}
[[maybe_unused]] static void SetField(int8_t& output, const int8_t& input) {
output = input;
}
[[maybe_unused]] static void SetField(int16_t& output, const int16_t& input) {
output = input;
}
[[maybe_unused]] static void SetField(int32_t& output, const int32_t& input) {
output = input;
}
[[maybe_unused]] static void SetField(int64_t& output, const int64_t& input) {
output = input;
}
std::shared_ptr<GPTPStatus> GPTPStatus::DecodeToMessage(std::vector<uint8_t>& bytes, const device_eventhandler_t&) {
// The following does not lead to overflow since we only call this function if it has at least ResponseHeader length bytes
std::shared_ptr<GPTPStatus> res = std::make_shared<GPTPStatus>();
auto* header = reinterpret_cast<ExtendedResponseMessage::ResponseHeader*>(bytes.data());
uint16_t length = header->length;
_GPTPStatus* input = reinterpret_cast<_GPTPStatus*>(bytes.data() + sizeof(ExtendedResponseMessage::ResponseHeader));
#define CheckLengthAndSet(output, input) if(length >= sizeof(decltype(input))) { \
SetField(output, input); \
length -= sizeof(decltype(input)); \
} else {\
memset(&output, 0, sizeof(decltype(output))); \
length = 0; \
res->shortFormat = true; \
}
CheckLengthAndSet(res->currentTime, input->current_time);
CheckLengthAndSet(res->gmPriority, input->gm_priority);
CheckLengthAndSet(res->msOffsetNs, input->ms_offset_ns);
CheckLengthAndSet(res->isSync, input->is_sync);
CheckLengthAndSet(res->linkStatus, input->link_status);
CheckLengthAndSet(res->linkDelayNS, input->link_delay_ns);
CheckLengthAndSet(res->selectedRole, input->selected_role);
CheckLengthAndSet(res->asCapable, input->as_capable);
CheckLengthAndSet(res->isSyntonized, input->is_syntonized);
CheckLengthAndSet(res->lastRXSyncTS, input->last_rx_sync_ts);
CheckLengthAndSet(res->currentDS, input->current_ds);
CheckLengthAndSet(res->parentDS, input->parent_ds);
return res;
}
}

View File

@ -1,7 +1,7 @@
#include "icsneo/communication/message/neomessage.h"
#include "icsneo/communication/message/canmessage.h"
#include "icsneo/communication/message/ethernetmessage.h"
#include "icsneo/communication/message/canerrorcountmessage.h"
#include "icsneo/communication/message/canerrormessage.h"
#include "icsneo/communication/message/linmessage.h"
using namespace icsneo;
@ -14,9 +14,9 @@ neomessage_t icsneo::CreateNeoMessage(const std::shared_ptr<Message> message) {
neomsg.timestamp = message->timestamp;
switch (message->type)
{
case Message::Type::Frame: {
case Message::Type::BusMessage: {
neomessage_frame_t& frame = *(neomessage_frame_t*)&neomsg;
auto framemsg = std::static_pointer_cast<Frame>(message);
auto framemsg = std::static_pointer_cast<BusMessage>(message);
const auto netType = framemsg->network.getType();
frame.netid = (neonetid_t)framemsg->network.getNetID();
@ -51,7 +51,7 @@ neomessage_t icsneo::CreateNeoMessage(const std::shared_ptr<Message> message) {
eth.status.incompleteFrame = ethmsg->frameTooShort;
// TODO Fill in extra status bits
//eth.status.xyz = ethmsg->preemptionEnabled;
//eth.status.xyz = ethmsg->fcsAvailable;
//eth.status.xyz = ethmsg->fcs;
//eth.status.xyz = ethmsg->noPadding;
break;
}
@ -104,7 +104,7 @@ neomessage_t icsneo::CreateNeoMessage(const std::shared_ptr<Message> message) {
}
case Message::Type::CANErrorCount: {
neomessage_can_error_t& canerror = *(neomessage_can_error_t*)&neomsg;
auto canerrormsg = std::static_pointer_cast<CANErrorCountMessage>(message);
auto canerrormsg = std::static_pointer_cast<CANErrorMessage>(message);
canerror.transmitErrorCount = canerrormsg->transmitErrorCount;
canerror.receiveErrorCount = canerrormsg->receiveErrorCount;
canerror.status.canBusOff = canerrormsg->busOff;
@ -120,7 +120,7 @@ neomessage_t icsneo::CreateNeoMessage(const std::shared_ptr<Message> message) {
std::shared_ptr<Message> icsneo::CreateMessageFromNeoMessage(const neomessage_t* neomessage) {
switch((Message::Type)neomessage->messageType) {
case Message::Type::Frame: {
case Message::Type::BusMessage: {
const Network network = ((neomessage_frame_t*)neomessage)->netid;
switch(network.getType()) {
case Network::Type::CAN:

View File

@ -1,9 +1,9 @@
#include "icsneo/communication/packet/canpacket.h"
#include "icsneo/communication/message/canerrorcountmessage.h"
#include "icsneo/communication/message/canerrormessage.h"
using namespace icsneo;
static std::optional<uint8_t> CAN_DLCToLength(uint8_t length, bool fd) {
std::optional<uint8_t> icsneo::CAN_DLCToLength(uint8_t length, bool fd) {
if (length <= 8)
return length;
@ -29,7 +29,7 @@ static std::optional<uint8_t> CAN_DLCToLength(uint8_t length, bool fd) {
return std::nullopt;
}
static std::optional<uint8_t> CAN_LengthToDLC(size_t dataLength, bool fd)
std::optional<uint8_t> icsneo::CAN_LengthToDLC(size_t dataLength, bool fd)
{
if (dataLength <= 8)
return uint8_t(dataLength);
@ -53,16 +53,18 @@ static std::optional<uint8_t> CAN_LengthToDLC(size_t dataLength, bool fd)
return std::nullopt;
}
std::shared_ptr<Message> HardwareCANPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
const HardwareCANPacket* data = (const HardwareCANPacket*)bytestream.data();
if(data->dlc.RB1) { // Change counts reporting
const bool busOff = data->data[0] & 0b00100000;
auto msg = std::make_shared<CANErrorCountMessage>(data->data[2], data->data[1], busOff);
const HardwareCANErrorPacket* errPacket = (const HardwareCANErrorPacket*)bytestream.data();
if(errPacket->ERROR_INDICATOR) {
auto msg = std::make_shared<CANErrorMessage>();
msg->receiveErrorCount = errPacket->REC;
msg->transmitErrorCount = errPacket->TEC;
msg->errorWarn = HardwareCANErrorPacket::GetErrorWarn(errPacket->flags);
msg->errorPassive = HardwareCANErrorPacket::GetErrorPassive(errPacket->flags);
msg->busOff = HardwareCANErrorPacket::GetBusOff(errPacket->flags);
msg->errorCode = (CANErrorCode)errPacket->error_code;
msg->dataErrorCode = (CANErrorCode)errPacket->brs_data_error_code;
// This timestamp is raw off the device (in timestampResolution increments)
// Decoder will fix as it has information about the timestampResolution increments
msg->timestamp = data->timestamp.TS;

View File

@ -16,8 +16,8 @@ std::shared_ptr<EthernetMessage> HardwareEthernetPacket::DecodeToMessage(const s
if(packet->Length < 4)
return nullptr;
const size_t ethernetFrameSize = packet->Length - (sizeof(uint16_t) * 2);
const size_t bytestreamExpectedSize = sizeof(HardwareEthernetPacket) + ethernetFrameSize;
const size_t fcsSize = packet->header.FCS_AVAIL ? 4 : 0;
const size_t bytestreamExpectedSize = sizeof(HardwareEthernetPacket) + packet->Length;
const size_t bytestreamActualSize = bytestream.size();
if(bytestreamActualSize < bytestreamExpectedSize)
return nullptr;
@ -36,8 +36,6 @@ std::shared_ptr<EthernetMessage> HardwareEthernetPacket::DecodeToMessage(const s
message.preemptionEnabled = packet->header.PREEMPTION_ENABLED;
if(message.preemptionEnabled)
message.preemptionFlags = (uint8_t)((rawWords[0] & 0x03F8) >> 4);
message.fcsAvailable = packet->header.FCS_AVAIL;
message.frameTooShort = packet->header.RUNT_FRAME;
if(message.frameTooShort)
@ -47,11 +45,14 @@ std::shared_ptr<EthernetMessage> HardwareEthernetPacket::DecodeToMessage(const s
// Decoder will fix as it has information about the timestampResolution increments
message.timestamp = packet->timestamp.TS;
// Network ID is also not set, this will be fixed in the Decoder as well
const std::vector<uint8_t>::const_iterator databegin = bytestream.begin() + (sizeof(HardwareEthernetPacket) - (sizeof(uint16_t) * 2));
const std::vector<uint8_t>::const_iterator dataend = databegin + ethernetFrameSize;
const std::vector<uint8_t>::const_iterator databegin = bytestream.begin() + sizeof(HardwareEthernetPacket);
const std::vector<uint8_t>::const_iterator dataend = databegin + packet->Length - fcsSize;
message.data.insert(message.data.begin(), databegin, dataend);
if(fcsSize) {
uint32_t& fcs = message.fcs.emplace();
std::copy(dataend, dataend + fcsSize, (uint8_t*)&fcs);
}
return messagePtr;
}

View File

@ -218,8 +218,7 @@ bool Device::open(OpenFlags flags, OpenStatusHandler handler) {
if(block) // Extensions say no
return false;
// Get component versions *after* the extension "onDeviceOpen" hooks (e.g. device reflashes)
// Get component versions again *after* the extension "onDeviceOpen" hooks (e.g. device reflashes)
if(supportsComponentVersions()) {
if(auto compVersions = com->getComponentVersionsSync())
componentVersions = std::move(*compVersions);
@ -348,6 +347,15 @@ APIEvent::Type Device::attemptToBeginCommunication() {
else
versions = std::move(*maybeVersions);
// Get component versions before the extension "onDeviceOpen" hooks so that we can properly check verisons
if(supportsComponentVersions()) {
if(auto compVersions = com->getComponentVersionsSync())
componentVersions = std::move(*compVersions);
else
return getCommunicationNotEstablishedError();
}
return APIEvent::Type::NoErrorFound;
}
@ -472,7 +480,7 @@ int8_t Device::prepareScriptLoad() {
return false;
}
const auto resp = std::static_pointer_cast<RawMessage>(generic);
const auto resp = std::static_pointer_cast<InternalMessage>(generic);
retVal = (int8_t)resp->data[0];
}
@ -735,7 +743,7 @@ std::optional<CoreminiHeader> Device::readCoreminiHeader(Disk::MemoryType memTyp
return ret;
}
bool Device::transmit(std::shared_ptr<Frame> frame) {
bool Device::transmit(std::shared_ptr<BusMessage> frame) {
if(!isOpen()) {
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
return false;
@ -768,7 +776,7 @@ bool Device::transmit(std::shared_ptr<Frame> frame) {
return com->sendPacket(packet);
}
bool Device::transmit(std::vector<std::shared_ptr<Frame>> frames) {
bool Device::transmit(std::vector<std::shared_ptr<BusMessage>> frames) {
for(auto& frame : frames) {
if(!transmit(frame))
return false;
@ -806,12 +814,7 @@ std::shared_ptr<HardwareInfo> Device::getHardwareInfo(std::chrono::milliseconds
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
return nullptr;
}
if(!isOnline()) {
report(APIEvent::Type::DeviceCurrentlyOffline, APIEvent::Severity::Error);
return nullptr;
}
auto filter = std::make_shared<MessageFilter>(Message::Type::HardwareInfo);
auto response = com->waitForMessageSync([this]() {
@ -1718,18 +1721,9 @@ void Device::handleInternalMessage(std::shared_ptr<Message> message) {
case Message::Type::ResetStatus:
latestResetStatus = std::static_pointer_cast<ResetStatusMessage>(message);
break;
case Message::Type::RawMessage: {
auto rawMessage = std::static_pointer_cast<RawMessage>(message);
case Message::Type::InternalMessage: {
auto rawMessage = std::static_pointer_cast<InternalMessage>(message);
switch(rawMessage->network.getNetID()) {
case Network::NetID::Device: {
// Device is not guaranteed to be a CANMessage, it might be a RawMessage
// if it couldn't be decoded to a CANMessage. We only care about the
// CANMessage decoding right now.
auto canmsg = std::dynamic_pointer_cast<CANMessage>(message);
if(canmsg)
handleNeoVIMessage(std::move(canmsg));
break;
}
case Network::NetID::DeviceStatus:
// Device Status format is unique per device, so the devices need to decode it themselves
handleDeviceStatus(rawMessage);
@ -1739,6 +1733,15 @@ void Device::handleInternalMessage(std::shared_ptr<Message> message) {
}
break;
}
case Message::Type::BusMessage: {
// Device is not guaranteed to be a CANMessage, it might be a InternalMessage
// if it couldn't be decoded to a CANMessage. We only care about the
// CANMessage decoding right now.
auto canmsg = std::dynamic_pointer_cast<CANMessage>(message);
if(canmsg)
handleNeoVIMessage(std::move(canmsg));
break;
}
default: break;
}
forEachExtension([&](const std::shared_ptr<DeviceExtension>& ext) {
@ -1895,7 +1898,7 @@ std::optional<std::chrono::time_point<std::chrono::system_clock>> Device::getRTC
if(!generic) // Did not receive a message
return std::nullopt;
auto rawMes = std::dynamic_pointer_cast<RawMessage>(generic);
auto rawMes = std::dynamic_pointer_cast<InternalMessage>(generic);
if(!rawMes)
return std::nullopt;
@ -3335,3 +3338,34 @@ std::optional<TC10StatusMessage> Device::getTC10Status(Network::NetID network) {
return *typed;
}
std::optional<GPTPStatus> Device::getGPTPStatus(std::chrono::milliseconds timeout) {
if(!supportsGPTP()) {
report(APIEvent::Type::GPTPNotSupported, APIEvent::Severity::Error);
return std::nullopt;
}
if(!isOpen()) {
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
return std::nullopt;
}
std::shared_ptr<Message> response = com->waitForMessageSync(
[this](){
return com->sendCommand(ExtendedCommand::GetGPTPStatus, {});
},
std::make_shared<MessageFilter>(Message::Type::GPTPStatus),
timeout
);
if(!response) {
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error);
return std::nullopt;
}
auto retMsg = std::static_pointer_cast<GPTPStatus>(response);
if(!retMsg) {
return std::nullopt;
}
return *retMsg;
}

View File

@ -45,7 +45,7 @@ void FlexRay::Extension::handleMessage(const std::shared_ptr<Message>& message)
}
}
bool FlexRay::Extension::transmitHook(const std::shared_ptr<Frame>& frame, bool& success) {
bool FlexRay::Extension::transmitHook(const std::shared_ptr<BusMessage>& frame, bool& success) {
if(!frame || frame->network.getType() != Network::Type::FlexRay)
return true; // Don't hook non-FlexRay messages

View File

@ -12,7 +12,7 @@ subprocess.call('cd ..; doxygen docs/icsneoc/Doxyfile', shell=True)
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = 'libicsneo'
copyright = '2024, Intrepid Control Systems, Inc.'
copyright = '2024-2025, Intrepid Control Systems, Inc.'
author = 'Intrepid Control Systems, Inc.'
# -- General configuration ---------------------------------------------------

View File

@ -6,4 +6,5 @@ Python API
:members:
:undoc-members:
:show-inheritance:
:imported-members:
:special-members: __init__

View File

@ -50,3 +50,41 @@ Receive CAN frames on HSCAN
# rx for 10s
time.sleep(10)
Monitor Ethernet Status
=======================
.. code-block:: python
import icsneopy
import time
def main():
devices = icsneopy.find_all_devices()
if len(devices) == 0:
print("error: no devices found")
return False
device = devices[0]
print(f"info: monitoring Ethernet status on {device}")
def on_message(message):
print(f"info: network: {message.network}, state: {message.state}, speed: {message.speed}, duplex: {message.duplex}, mode: {message.mode}")
filter = icsneopy.MessageFilter(icsneopy.Message.Type.EthernetStatus)
callback = icsneopy.MessageCallback(on_message, filter)
device.add_message_callback(callback)
if not device.open():
print("error: unable to open device")
return False
if not device.go_online():
print("error: unable to go online")
return False
while True:
time.sleep(1)
main()

View File

@ -1,6 +1,7 @@
option(LIBICSNEO_BUILD_C_INTERACTIVE_EXAMPLE "Build the command-line interactive C example." ON)
option(LIBICSNEO_BUILD_C_SIMPLE_EXAMPLE "Build the command-line simple C example." ON)
option(LIBICSNEO_BUILD_C_LEGACY_EXAMPLE "Build the command-line simple C example." ON)
option(LIBICSNEO_BUILD_C2_SIMPLE_EXAMPLE "Build the command-line simple C example." ON)
option(LIBICSNEO_BUILD_CPP_SIMPLE_EXAMPLE "Build the simple C++ example." ON)
option(LIBICSNEO_BUILD_CPP_INTERACTIVE_EXAMPLE "Build the command-line interactive C++ example." ON)
option(LIBICSNEO_BUILD_CPP_A2B_EXAMPLE "Build the A2B example." ON)
@ -27,6 +28,10 @@ if(LIBICSNEO_BUILD_C_LEGACY_EXAMPLE)
add_subdirectory(c/legacy)
endif()
if(LIBICSNEO_BUILD_C2_SIMPLE_EXAMPLE)
add_subdirectory(c2/simple)
endif()
if(LIBICSNEO_BUILD_CPP_SIMPLE_EXAMPLE)
add_subdirectory(cpp/simple)
endif()

View File

@ -0,0 +1,2 @@
add_executable(libicsneo-simple-example src/main.c)
target_link_libraries(libicsneo-simple-example icsneoc2)

View File

@ -0,0 +1,454 @@
#include <icsneo/icsneoc2.h>
#include <stdio.h>
#include <time.h>
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#else
#include <unistd.h>
#endif
/**
* @brief Sleeps for a specified number of milliseconds.
*
* Sleeps for a specified number of milliseconds using Sleep() on Windows and sleep() on *nix.
*
* @param ms The number of milliseconds to sleep.
*/
void sleep_ms(uint32_t ms) {
#if defined(_WIN32) || defined(_WIN64)
Sleep(ms);
#else
sleep(ms / 1000);
#endif
}
/**
* @brief Prints an error message with the given string and error code.
*
* If the error code is not icsneoc2_error_success, prints the error string for the given error code
* and returns the error code.
*
* @param message The message to print.
* @param error The error code to print.
* @return error as int
*/
int print_error_code(const char* message, icsneoc2_error_t error) {
char error_str[256] = {0};
uint32_t error_length = 256;
icsneoc2_error_t res = icsneoc2_error_code_get(error, error_str, &error_length);
if (res != icsneoc2_error_success) {
printf("%s: Failed to get string for error code %d with error code %d\n", message, error, res);
return res;
}
printf("%s: \"%s\" (%u)\n", message, error_str, error);
return (int)error;
}
/**
* @brief Processes a list of messages from a device.
*
* This function iterates over a given array of messages received from a specified device.
* For each message in the array, it retrieves and prints the message type and bus type.
* If an error occurs while retrieving these details, an error message is printed.
*
* @param device A pointer to the icsneoc2_device_t structure representing the device.
* @param messages An array of pointers to icsneoc2_message_t structures containing the messages to process.
* @param messages_count The number of messages in the messages array.
*
* @return An icsneoc2_error_t value indicating success or failure of the message processing.
*/
int process_messages(icsneoc2_device_t* device, icsneoc2_message_t** messages, uint32_t messages_count);
/**
* @brief Prints device and global events for a given device.
*
* This function retrieves and prints all current events associated with the specified device,
* as well as any global events not tied to a specific device. For each event, it retrieves
* and prints a description. If retrieving events or their descriptions fails, an error
* message is printed. The function also prints a summary of the count of device-specific
* and global events processed.
*
* @param device A pointer to the icsneoc2_device_t structure representing the device to get events from.
* @param device_description A description of the device used in the output.
*/
void print_device_events(icsneoc2_device_t* device, const char* device_description);
/**
* @brief Transmits a series of CAN messages from a device.
*
* This function creates and transmits 100 CAN messages with incrementing payload data.
* Each message is configured with specific attributes such as network ID, arbitration
* ID, CANFD status, extended status, and baudrate switch. After successfully transmitting
* each message, it is freed from memory.
*
* @param device A pointer to the icsneoc2_device_t structure representing the device to transmit messages from.
*
* @return An icsneoc2_error_t value indicating success or failure of the message transmission process.
*/
int transmit_can_messages(icsneoc2_device_t* device);
/**
* @brief Get the RTC (Real time clock) of a device and print it.
*
* @param[in] device The device to get the RTC of.
* @param[in] description A description of the device for printing purpose.
*
* @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise.
*/
icsneoc2_error_t get_and_print_rtc(icsneoc2_device_t* device, const char* description);
int main(int argc, char* argv[]) {
(void)argc;
(void)argv;
icsneoc2_device_t* devices[255] = {0};
uint32_t devices_count = 255;
printf("Finding devices...\n");
icsneoc2_error_t res = icsneoc2_device_find_all(devices, &devices_count, NULL);
if (res != icsneoc2_error_success) {
return print_error_code("\tFailed to find devices", res);
};
printf("OK, %u device%s found\n", devices_count, devices_count == 1 ? "" : "s");
// List off the devices
for (uint32_t i = 0; i < devices_count; i++) {
icsneoc2_device_t* device = devices[i];
// Get description of the device
const char description[255] = {0};
uint32_t description_length = 255;
res = icsneoc2_device_description_get(device, description, &description_length);
if (res != icsneoc2_error_success) {
print_device_events(device, description);
return print_error_code("\tFailed to get device description", res);
};
printf("%.*s @ Handle %p\n", description_length, description, device);
// Get/Set open options
icsneoc2_open_options_t options = icsneoc2_open_options_none;
res = icsneoc2_device_open_options_get(device, &options);
if (res != icsneoc2_error_success) {
print_device_events(device, description);
return print_error_code("\tFailed to get open options", res);
}
// Disable Syncing RTC and going online
options &= ~icsneoc2_open_options_sync_rtc;
options &= ~icsneoc2_open_options_go_online;
printf("\tDevice open options: 0x%x\n", options);
res = icsneoc2_device_open_options_set(device, options);
if (res != icsneoc2_error_success) {
print_device_events(device, description);
return print_error_code("\tFailed to set open options", res);
}
// Open the device
printf("\tOpening device: %s...\n", description);
res = icsneoc2_device_open(device);
if (res != icsneoc2_error_success) {
print_device_events(device, description);
return print_error_code("\tFailed to open device", res);
};
// Get timestamp resolution of the device
printf("\tGetting timestamp resolution... ");
uint32_t timestamp_resolution = 0;
res = icsneoc2_device_timestamp_resolution_get(device, &timestamp_resolution);
if (res != icsneoc2_error_success) {
print_device_events(device, description);
return print_error_code("\tFailed to get timestamp resolution", res);
}
printf("%uns\n", timestamp_resolution);
// Get baudrates for HSCAN
printf("\tGetting HSCAN Baudrate... ");
uint64_t baudrate = 0;
res = icsneoc2_device_baudrate_get(device, icsneoc2_netid_hscan, &baudrate);
if (res != icsneoc2_error_success) {
print_device_events(device, description);
return print_error_code("\tFailed to get baudrate", res);
};
printf("%llumbit/s\n", baudrate);
// Get FDbaudrates for HSCAN
printf("\tGetting FD HSCAN Baudrate... ");
uint64_t fd_baudrate = 0;
res = icsneoc2_device_canfd_baudrate_get(device, icsneoc2_netid_hscan, &fd_baudrate);
if (res != icsneoc2_error_success) {
print_device_events(device, description);
return print_error_code("\tFailed to get FD baudrate", res);
};
printf("%llumbit/s\n", fd_baudrate);
// Set baudrates for HSCAN
// save_to_device: If this is set to true, the baudrate will be saved on the device
// and will persist through a power cycle
bool save_to_device = false;
printf("\tSetting HSCAN Baudrate... ");
res = icsneoc2_device_baudrate_set(device, icsneoc2_netid_hscan, baudrate, save_to_device);
if (res != icsneoc2_error_success) {
print_device_events(device, description);
return print_error_code("\tFailed to set baudrate", res);
};
printf("Ok\n");
// Set FDbaudrates for HSCAN
printf("\tSetting FD HSCAN Baudrate... ");
res = icsneoc2_device_canfd_baudrate_set(device, icsneoc2_netid_hscan, fd_baudrate, save_to_device);
if (res != icsneoc2_error_success) {
print_device_events(device, description);
return print_error_code("\tFailed to set FD baudrate", res);
};
printf("Ok\n");
// Get RTC
printf("\tGetting RTC... ");
res = get_and_print_rtc(device, description);
if (res != icsneoc2_error_success) {
print_device_events(device, description);
return print_error_code("\tFailed to get RTC", res);
}
// Set RTC
printf("\tSetting RTC to current time... ");
time_t current_time = time(NULL);
res = icsneoc2_device_rtc_set(device, current_time);
if (res != icsneoc2_error_success) {
print_device_events(device, description);
return print_error_code("\tFailed to set RTC", res);
}
printf("Ok\n");
// Get RTC
printf("\tGetting RTC... ");
res = get_and_print_rtc(device, description);
if (res != icsneoc2_error_success) {
print_device_events(device, description);
return print_error_code("\tFailed to get RTC", res);
}
// Go online, start acking traffic
printf("\tGoing online... ");
res = icsneoc2_device_go_online(device, true);
if (res != icsneoc2_error_success) {
print_device_events(device, description);
return print_error_code("\tFailed to go online", res);
}
// Redundant check to show how to check if the device is online, if the previous
// icsneoc2_device_go_online call was successful we can assume we are online already
bool is_online = false;
res = icsneoc2_device_is_online(device, &is_online);
if (res != icsneoc2_error_success) {
print_device_events(device, description);
return print_error_code("\tFailed to check if online", res);
}
printf("%s\n", is_online ? "Online" : "Offline");
// Transmit CAN messages
res = transmit_can_messages(device);
if (res != icsneoc2_error_success) {
print_device_events(device, description);
return print_error_code("\tFailed to transmit CAN messages", res);
}
// Wait for the bus to collect some messages, requires an active bus to get messages
printf("\tWaiting 1 second for messages...\n");
sleep_ms(1000);
// Get the messages
icsneoc2_message_t* messages[20000] = {0};
uint32_t message_count = 20000;
printf("\tGetting messages from device with timeout of 3000ms on %s...\n", description);
res = icsneoc2_device_messages_get(device, messages, &message_count, 3000);
if (res != icsneoc2_error_success) {
print_device_events(device, description);
return print_error_code("\tFailed to get messages from device", res);
};
// Process the messages
res = process_messages(device, messages, message_count);
if (res != icsneoc2_error_success) {
print_device_events(device, description);
return print_error_code("\tFailed to process messages", res);
}
// Finally, close the device.
printf("\tClosing device: %s...\n", description);
res = icsneoc2_device_close(device);
if (res != icsneoc2_error_success) {
print_device_events(device, description);
return print_error_code("\tFailed to close device", res);
};
// Print device events
print_device_events(device, description);
}
printf("\n");
return 0;
}
icsneoc2_error_t get_and_print_rtc(icsneoc2_device_t* device, const char* description) {
time_t unix_epoch = 0;
icsneoc2_error_t res = icsneoc2_device_rtc_get(device, &unix_epoch);
if (res != icsneoc2_error_success) {
return res;
}
char rtc_time[32] = {0};
strftime(rtc_time, sizeof(rtc_time), "%Y-%m-%d %H:%M:%S", localtime(&unix_epoch));
printf("RTC: %lld %s\n", unix_epoch, rtc_time);
return icsneoc2_error_success;
}
void print_device_events(icsneoc2_device_t* device, const char* device_description) {
// Get device events
icsneoc2_event_t* events[1024] = {0};
uint32_t events_count = 1024;
icsneoc2_error_t res = icsneoc2_device_events_get(device, events, &events_count);
if (res != icsneoc2_error_success) {
(void)print_error_code("\tFailed to get device events", res);
return;
}
// Loop over each event and describe it.
for (uint32_t i = 0; i < events_count; i++) {
const char event_description[255] = {0};
uint32_t event_description_length = 255;
res = icsneoc2_event_description_get(events[i], event_description, &event_description_length);
if (res != icsneoc2_error_success) {
print_error_code("\tFailed to get event description", res);
continue;
}
printf("\t%s: Event %u: %s\n", device_description, i, event_description);
}
// Get global events
icsneoc2_event_t* global_events[1024] = {0};
uint32_t global_events_count = 1024;
res = icsneoc2_events_get(global_events, &global_events_count);
if (res != icsneoc2_error_success) {
(void)print_error_code("\tFailed to get global events", res);
return;
}
// Loop over each event and describe it.
for (uint32_t i = 0; i < global_events_count; i++) {
const char event_description[255] = {0};
uint32_t event_description_length = 255;
res = icsneoc2_event_description_get(global_events[i], event_description, &event_description_length);
if (res != icsneoc2_error_success) {
print_error_code("\tFailed to get global event description", res);
continue;
}
printf("\t%s: Global Event %u: %s\n", device_description, i, event_description);
}
printf("\t%s: Received %u events and %u global events\n", device_description, events_count, global_events_count);
}
int process_messages(icsneoc2_device_t* device, icsneoc2_message_t** messages, uint32_t messages_count) {
// Print the type and bus type of each message
uint32_t tx_count = 0;
for (uint32_t i = 0; i < messages_count; i++) {
icsneoc2_message_t* message = messages[i];
// Get the message type
icsneoc2_msg_type_t msg_type = 0;
icsneoc2_error_t res = icsneoc2_message_type_get(device, message, &msg_type);
if (res != icsneoc2_error_success) {
return print_error_code("\tFailed to get message type", res);
}
// Get the message type name
char msg_type_name[128] = {0};
uint32_t msg_type_name_length = 128;
res = icsneoc2_message_type_name_get(msg_type, msg_type_name, &msg_type_name_length);
if (res != icsneoc2_error_success) {
return print_error_code("\tFailed to get message type name", res);
}
// Check if the message is a bus message, ignore otherwise
if (msg_type != icsneoc2_msg_type_bus) {
printf("Ignoring message type: %u (%s)\n", msg_type, msg_type_name);
continue;
}
icsneoc2_msg_bus_type_t bus_type = 0;
res = icsneoc2_message_bus_type_get(device, message, &bus_type);
if (res != icsneoc2_error_success) {
return print_error_code("\tFailed to get message bus type", res);
}
const char bus_name[128] = {0};
uint32_t bus_name_length = 128;
res = icsneoc2_bus_type_name_get(bus_type, bus_name, &bus_name_length);
if (res != icsneoc2_error_success) {
return print_error_code("\tFailed to get message bus type name", res);
}
bool is_tx = false;
res = icsneoc2_message_is_transmit(device, message, &is_tx);
if (res != icsneoc2_error_success) {
return print_error_code("\tFailed to get message is transmit", res);
}
if (is_tx) {
tx_count++;
continue;
}
printf("\t%d) Message type: %u bus type: %s (%u)\n", i, msg_type, bus_name, bus_type);
if (bus_type == icsneoc2_msg_bus_type_can) {
uint32_t arbid = 0;
int32_t dlc = 0;
icsneoc2_netid_t netid = 0;
bool is_remote = false;
bool is_canfd = false;
bool is_extended = false;
uint8_t data[64] = {0};
uint32_t data_length = 64;
const char netid_name[128] = {0};
uint32_t netid_name_length = 128;
uint32_t result = icsneoc2_message_netid_get(device, message, &netid);
result += icsneoc2_netid_name_get(netid, netid_name, &netid_name_length);
result += icsneoc2_message_can_arbid_get(device, message, &arbid);
result += icsneoc2_message_can_dlc_get(device, message, &dlc);
result += icsneoc2_message_can_is_remote(device, message, &is_remote);
result += icsneoc2_message_can_is_canfd(device, message, &is_canfd);
result += icsneoc2_message_can_is_extended(device, message, &is_extended);
result += icsneoc2_message_data_get(device, message, data, &data_length);
if (result != icsneoc2_error_success) {
printf("\tFailed get get CAN parameters (error: %u) for index %u\n", result, i);
continue;
}
printf("\t NetID: %s (0x%x)\tArbID: 0x%x\t DLC: %u\t Remote: %d\t CANFD: %d\t Extended: %d\t Data length: %u\n", netid_name, netid, arbid, dlc, is_remote, is_canfd, is_extended, data_length);
printf("\t Data: [");
for (uint32_t x = 0; x < data_length; x++) {
printf(" 0x%x", data[x]);
}
printf(" ]\n");
}
}
printf("\tReceived %u messages total, %u were TX messages\n", messages_count, tx_count);
return icsneoc2_error_success;
}
int transmit_can_messages(icsneoc2_device_t* device) {
uint64_t counter = 0;
const uint32_t msg_count = 100;
printf("\tTransmitting %d messages...\n", msg_count);
for (uint32_t i = 0; i < msg_count; i++) {
// Create the message
icsneoc2_message_t* message = NULL;
uint32_t message_count = 1;
icsneoc2_error_t res = icsneoc2_message_can_create(device, &message, message_count);
if (res != icsneoc2_error_success) {
return print_error_code("\tFailed to create messages", res);
}
// Set the message attributes
res = icsneoc2_message_netid_set(device, message, icsneoc2_netid_hscan);
res += icsneoc2_message_can_arbid_set(device, message, 0x10);
res += icsneoc2_message_can_canfd_set(device, message, true);
res += icsneoc2_message_can_extended_set(device, message, true);
res += icsneoc2_message_can_baudrate_switch_set(device, message, true);
// Create the payload
uint8_t data[8] = {0};
data[0] = (uint8_t)(counter >> 56);
data[1] = (uint8_t)(counter >> 48);
data[2] = (uint8_t)(counter >> 40);
data[3] = (uint8_t)(counter >> 32);
data[4] = (uint8_t)(counter >> 24);
data[5] = (uint8_t)(counter >> 16);
data[6] = (uint8_t)(counter >> 8);
data[7] = (uint8_t)(counter >> 0);
res += icsneoc2_message_data_set(device, message, data, sizeof(data));
res += icsneoc2_message_can_dlc_set(device, message, -1);
if (res != icsneoc2_error_success) {
return print_error_code("\tFailed to modify message", res);
}
res = icsneoc2_device_messages_transmit(device, &message, &message_count);
res += icsneoc2_message_can_free(device, message);
if (res != icsneoc2_error_success) {
return print_error_code("\tFailed to transmit messages", res);
}
counter++;
}
return icsneoc2_error_success;
}

View File

@ -259,8 +259,8 @@ void example4(const std::shared_ptr<icsneo::Device>& rada2b) {
auto handler = rada2b->addMessageCallback(std::make_shared<icsneo::MessageCallback>(
[] (std::shared_ptr<icsneo::Message> newMsg) {
if(newMsg->type == icsneo::Message::Type::Frame) {
const auto& frame = std::dynamic_pointer_cast<icsneo::Frame>(newMsg);
if(newMsg->type == icsneo::Message::Type::BusMessage) {
const auto& frame = std::dynamic_pointer_cast<icsneo::BusMessage>(newMsg);
if(frame && frame->network.getNetID() == icsneo::Network::NetID::I2C2) {
const auto& i2cMessage = std::dynamic_pointer_cast<icsneo::I2CMessage>(frame);

View File

@ -184,9 +184,9 @@ std::shared_ptr<icsneo::Device> selectDevice(const std::vector<std::shared_ptr<i
void printMessage(const std::shared_ptr<icsneo::Message>& message) {
switch(message->type) {
case icsneo::Message::Type::Frame: {
case icsneo::Message::Type::BusMessage: {
// A message of type Frame is guaranteed to be a Frame, so we can static cast safely
auto frame = std::static_pointer_cast<icsneo::Frame>(message);
auto frame = std::static_pointer_cast<icsneo::BusMessage>(message);
switch(frame->network.getType()) {
case icsneo::Network::Type::CAN: {
// A message of type CAN is guaranteed to be a CANMessage, so we can static cast safely
@ -264,10 +264,10 @@ void printMessage(const std::shared_ptr<icsneo::Message>& message) {
break;
}
break;
} // end of icsneo::Message::Type::Frame
} // end of icsneo::Message::Type::BusMessage
case icsneo::Message::Type::CANErrorCount: {
// A message of type CANErrorCount is guaranteed to be a CANErrorCount, so we can static cast safely
auto cec = std::static_pointer_cast<icsneo::CANErrorCountMessage>(message);
auto cec = std::static_pointer_cast<icsneo::CANErrorMessage>(message);
std::cout << "\t\t" << cec->network << " error counts changed, REC=" << cec->receiveErrorCount
<< " TEC=" << cec->transmitErrorCount << " (" << (cec->busOff ? "" : "Not ") << "Bus Off)" << std::endl;
break;

View File

@ -96,8 +96,8 @@ int main() {
std::cout << "OK" << std::endl << std::endl;
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> message) {
if(icsneo::Message::Type::Frame == message->type) {
auto frame = std::static_pointer_cast<icsneo::Frame>(message);
if(icsneo::Message::Type::BusMessage == message->type) {
auto frame = std::static_pointer_cast<icsneo::BusMessage>(message);
if(icsneo::Network::Type::LIN == frame->network.getType()) {
auto msg = std::static_pointer_cast<icsneo::LINMessage>(message);
std::cout << msg->network << " RX frame | ID: 0x" << std::hex << static_cast<int>(msg->ID) << " | ";

View File

@ -92,8 +92,8 @@ int main()
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> message)
{
if(icsneo::Message::Type::Frame == message->type) {
auto frame = std::static_pointer_cast<icsneo::Frame>(message);
if(icsneo::Message::Type::BusMessage == message->type) {
auto frame = std::static_pointer_cast<icsneo::BusMessage>(message);
if(icsneo::Network::Type::MDIO == frame->network.getType()) {
auto msg = std::static_pointer_cast<icsneo::MDIOMessage>(message);
std::cout << msg->network << " " << ((msg->isTXMsg)? "TX" : "RX") << " frame\n";

View File

@ -169,9 +169,9 @@ int main() {
// MessageCallbacks are powerful, and can filter on things like ArbID for you. See the documentation
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([](std::shared_ptr<icsneo::Message> message) {
switch(message->type) {
case icsneo::Message::Type::Frame: {
// A message of type Frame is guaranteed to be a Frame, so we can static cast safely
auto frame = std::static_pointer_cast<icsneo::Frame>(message);
case icsneo::Message::Type::BusMessage: {
// A message of type BusMessage is guaranteed to be a BusMessage, so we can static cast safely
auto frame = std::static_pointer_cast<icsneo::BusMessage>(message);
switch(frame->network.getType()) {
case icsneo::Network::Type::CAN: {
// A message of type CAN is guaranteed to be a CANMessage, so we can static cast safely
@ -202,7 +202,7 @@ int main() {
case icsneo::Network::Type::Ethernet: {
auto ethMessage = std::static_pointer_cast<icsneo::EthernetMessage>(message);
std::cout << "\t\t" << ethMessage->network << " Frame - " << std::dec
std::cout << "\t\t" << ethMessage->network << " BusMessage - " << std::dec
<< ethMessage->data.size() << " bytes on wire\n";
std::cout << "\t\t Timestamped:\t"<< ethMessage->timestamp << " ns since 1/1/2007\n";
@ -249,10 +249,10 @@ int main() {
break;
}
break;
} // end of icsneo::Message::Type::Frame
} // end of icsneo::Message::Type::BusMessage
case icsneo::Message::Type::CANErrorCount: {
// A message of type CANErrorCount is guaranteed to be a CANErrorCount, so we can static cast safely
auto cec = std::static_pointer_cast<icsneo::CANErrorCountMessage>(message);
auto cec = std::static_pointer_cast<icsneo::CANErrorMessage>(message);
// Print the error counts
std::cout << "\t\t" << cec->network << " error counts changed, REC=" << std::to_string(cec->receiveErrorCount)

View File

@ -16,13 +16,13 @@ void onEvent(std::shared_ptr<icsneo::APIEvent> event) {
std::cout << event->describe() << std::endl;
}
std::vector<std::shared_ptr<icsneo::Frame>> constructRandomFrames(size_t frameCount, MessageType frameType) {
std::vector<std::shared_ptr<icsneo::BusMessage>> constructRandomFrames(size_t frameCount, MessageType frameType) {
static constexpr size_t ClassicCANSize = 8;
static constexpr size_t CANFDSize = 64;
static constexpr size_t ShortEthSize = 500;
static constexpr size_t LongEthSize = 1500;
std::vector<std::shared_ptr<icsneo::Frame>> frames;
std::vector<std::shared_ptr<icsneo::BusMessage>> frames;
std::random_device randDev;
std::mt19937 randEngine(randDev());
std::uniform_int_distribution randByteDist(0,255);
@ -166,10 +166,10 @@ int main(int argc, char* argv[]) {
uint64_t canFrameCount = 0;
uint64_t ethFrameCount = 0;
rxDevice->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> msg) {
if(msg->type != icsneo::Message::Type::Frame) {
if(msg->type != icsneo::Message::Type::BusMessage) {
return;
}
const auto frame = std::static_pointer_cast<icsneo::Frame>(msg);
const auto frame = std::static_pointer_cast<icsneo::BusMessage>(msg);
if(frame->network.getType() == icsneo::Network::Type::CAN) {
++canFrameCount;
} else if(frame->network.getType() == icsneo::Network::Type::Ethernet) {
@ -200,7 +200,7 @@ int main(int argc, char* argv[]) {
const uint8_t NumFrameTypes = 4;
const size_t FrameCountPerType = 2500;
std::vector<std::shared_ptr<icsneo::Frame>> frames;
std::vector<std::shared_ptr<icsneo::BusMessage>> frames;
for(uint8_t i = 0; i < NumFrameTypes; i++) {
std::cout << "info: transmitting " << FrameCountPerType << " random " << MessageTypeLabels[i] << " frames" << std::endl;
auto tempFrames = constructRandomFrames(FrameCountPerType, static_cast<MessageType>(i));
@ -216,10 +216,10 @@ int main(int argc, char* argv[]) {
size_t currentMessage = 0;
rxDevice->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> msg) {
if(msg->type != icsneo::Message::Type::Frame) {
if(msg->type != icsneo::Message::Type::BusMessage) {
return;
}
auto frame = std::static_pointer_cast<icsneo::Frame>(msg);
auto frame = std::static_pointer_cast<icsneo::BusMessage>(msg);
if(frames[currentMessage]->data == frame->data) {
currentMessage++;
}

View File

@ -0,0 +1,19 @@
libicsneoc2 simple Go example
====
This is a mirror of the icsneoc2 C simple example, written in Go.
Windows
====
- Install [msys64](https://www.msys2.org/) with gcc (`pacman -S mingw-w64-ucrt-x86_64-gcc`)
- Setup environment variables:
- add `C:\msys64\ucrt64\bin` to `PATH`
- Powershell: `$env:PATH += ";C:\msys64\ucrt64\bin"`
- `gcc --version` should return a version now
- enable cgo: `CGO_ENABLED = 1`
- Powershell: `$env:CGO_ENABLED=1`
- Set compiler to gcc
- Powershell: `$env:CC="gcc"`
- `icsneoc2.dll` should be in path (or inside this directory)
- `go run simple`

View File

@ -0,0 +1,3 @@
module simple
go 1.23.4

View File

@ -0,0 +1,354 @@
package main
// #cgo CFLAGS: -I../../../include
// #cgo LDFLAGS: -L../../../build -licsneoc2
// #include "icsneo/icsneoc2.h"
// #include "stdint.h"
import "C"
import (
"fmt"
"time"
"unsafe"
)
func main() {
// Find devices connected to host.
devices := [255]*C.icsneoc2_device_t{nil}
devicesCount := 255
print("Finding devices... ")
if res := C.icsneoc2_device_find_all(&devices[0], (*C.uint)(unsafe.Pointer(&devicesCount)), nil); res != C.icsneoc2_error_success {
printError(res)
return
}
fmt.Printf("OK, %d device(s) found\n", devicesCount)
// List off the devices
for _, device := range devices[:devicesCount] {
// Get description of the device
description := make([]byte, 255)
descriptionLength := 255
if res := C.icsneoc2_device_description_get(device, (*C.char)(unsafe.Pointer(&description[0])), (*C.uint)(unsafe.Pointer(&descriptionLength))); res != C.icsneoc2_error_success {
printError(res)
continue
}
fmt.Printf("%s @ Handle %p\n", description, device)
// Get/Set open options
options := C.icsneoc2_open_options_none
if res := C.icsneoc2_device_open_options_get(device, (*C.icsneoc2_open_options_t)(unsafe.Pointer(&options))); res != C.icsneoc2_error_success {
printError(res)
continue
}
options &= ^C.icsneoc2_open_options_sync_rtc
options &= ^C.icsneoc2_open_options_go_online
fmt.Printf("\tDevice open options: 0x%X\n", options)
if res := C.icsneoc2_device_open_options_set(device, (C.icsneoc2_open_options_t)(options)); res != C.icsneoc2_error_success {
printError(res)
continue
}
// Open the device
fmt.Printf("\tOpening device: %s...\n", description)
if res := C.icsneoc2_device_open(device); res != C.icsneoc2_error_success {
printError(res)
continue
}
defer func() {
if !printDeviceEvents(device, string(description)) {
println("\tFailed to print events...")
}
fmt.Printf("\tClosing device: %s...\n", description)
if res := C.icsneoc2_device_close(device); res != C.icsneoc2_error_success {
printError(res)
return
}
}()
// Get timestamp resolution of the device
fmt.Printf("\tGetting timestamp resolution... ")
var timestampResolution C.uint = 0
if res := C.icsneoc2_device_timestamp_resolution_get(device, &timestampResolution); res != C.icsneoc2_error_success {
printError(res)
return
}
fmt.Printf("%dns\n", timestampResolution)
// Get baudrates for HSCAN
fmt.Printf("\tGetting HSCAN Baudrate... ")
var baudrate uint64 = 0
if res := C.icsneoc2_device_baudrate_get(device, (C.icsneoc2_netid_t)(C.icsneoc2_netid_hscan), (*C.uint64_t)(unsafe.Pointer(&baudrate))); res != C.icsneoc2_error_success {
printError(res)
return
}
fmt.Printf("%dmbit/s\n", baudrate)
// Get FD baudrates for HSCAN
fmt.Printf("\tGetting FD HSCAN Baudrate... ")
var fdBaudrate uint64 = 0
if res := C.icsneoc2_device_canfd_baudrate_get(device, (C.icsneoc2_netid_t)(C.icsneoc2_netid_hscan), (*C.uint64_t)(unsafe.Pointer(&fdBaudrate))); res != C.icsneoc2_error_success {
printError(res)
return
}
fmt.Printf("%dmbit/s\n", fdBaudrate)
// Set baudrates for HSCAN
// saveToDevice: If this is set to true, the baudrate will be saved on the device
// and will persist through a power cycle
var saveToDevice C.bool = false
fmt.Printf("\tSetting HSCAN Baudrate... ")
if res := C.icsneoc2_device_baudrate_set(device, (C.icsneoc2_netid_t)(C.icsneoc2_netid_hscan), (C.uint64_t)(baudrate), saveToDevice); res != C.icsneoc2_error_success {
printError(res)
return
}
fmt.Printf("OK\n")
// Set FD baudrates for HSCAN
fmt.Printf("\tSetting FD HSCAN Baudrate... ")
if res := C.icsneoc2_device_canfd_baudrate_set(device, (C.icsneoc2_netid_t)(C.icsneoc2_netid_hscan), (C.uint64_t)(fdBaudrate), saveToDevice); res != C.icsneoc2_error_success {
printError(res)
return
}
fmt.Printf("OK\n")
// Get RTC
fmt.Printf("\tGetting RTC... ")
var unix_epoch C.int64_t = 0
if res := C.icsneoc2_device_rtc_get(device, (*C.int64_t)(unsafe.Pointer(&unix_epoch))); res != C.icsneoc2_error_success {
printError(res)
return
}
currentRTC := time.Unix(int64(unix_epoch), 0)
fmt.Printf("%d %s\n", currentRTC.Unix(), currentRTC)
// Set RTC
fmt.Printf("\tSetting RTC... ")
unix_epoch = (C.int64_t)(time.Now().Unix())
if res := C.icsneoc2_device_rtc_set(device, unix_epoch); res != C.icsneoc2_error_success {
printError(res)
return
}
fmt.Printf("OK\n")
// Get RTC
fmt.Printf("\tGetting RTC... ")
if res := C.icsneoc2_device_rtc_get(device, (*C.int64_t)(unsafe.Pointer(&unix_epoch))); res != C.icsneoc2_error_success {
printError(res)
return
}
currentRTC = time.Unix(int64(unix_epoch), 0)
fmt.Printf("%d %s\n", currentRTC.Unix(), currentRTC)
// Go online, start acking traffic
fmt.Printf("\tGoing online... ")
if res := C.icsneoc2_device_go_online(device, true); res != C.icsneoc2_error_success {
printError(res)
return
}
// Redundant check to show how to check if the device is online, if the previous
// icsneoc2_device_go_online call was successful we can assume we are online already
var isOnline C.bool = false
if res := C.icsneoc2_device_is_online(device, &isOnline); res != C.icsneoc2_error_success {
printError(res)
return
}
if isOnline {
println("Online")
} else {
println("Offline")
}
// Transmit CAN messages
if !transmitCANMessages(device) {
return
}
// Wait for the bus to collect some messages, requires an active bus to get messages
println("\tWaiting 1 second for messages...")
time.Sleep(1 * time.Second)
// Get the messages
messages := [20000]*C.icsneoc2_message_t{nil}
var messagesCount C.uint32_t = 20000
if res := C.icsneoc2_device_messages_get(device, &messages[0], &messagesCount, 3000); res != C.icsneoc2_error_success {
printError(res)
return
}
// Process the messages
if !processMessages(device, messages[0:messagesCount]) {
return
}
}
}
func printError(err C.icsneoc2_error_t) C.icsneoc2_error_t {
buffer := make([]byte, 255)
bufferLength := 255
res := C.icsneoc2_error_code_get(err, (*C.char)(unsafe.Pointer(&buffer[0])), (*C.uint)(unsafe.Pointer(&bufferLength)))
if res != C.icsneoc2_error_success {
println("\ticsneoc2_get_error_code failed, original error:", err)
return res
}
println("\tError:", string(buffer[:bufferLength]))
return res
}
func printDeviceEvents(device *C.icsneoc2_device_t, deviceDescription string) bool {
// Get device events
events := [1024]*C.icsneoc2_event_t{nil}
var eventsCount C.uint32_t = 1024
if res := C.icsneoc2_device_events_get(device, &events[0], &eventsCount); res != C.icsneoc2_error_success {
printError(res)
return false
}
for i, event := range events[:eventsCount] {
eventDescription := make([]byte, 255)
var eventDescriptionLength C.uint32_t = 255
if res := C.icsneoc2_event_description_get(event, (*C.char)(unsafe.Pointer(&eventDescription[0])), &eventDescriptionLength); res != C.icsneoc2_error_success {
printError(res)
continue
}
fmt.Printf("\t%s: Event %d: %s\n", deviceDescription, i, eventDescription)
}
// Get global events
globalEvents := [1024]*C.icsneoc2_event_t{nil}
var globalEventsCount C.uint32_t = 1024
if res := C.icsneoc2_events_get(&globalEvents[0], &globalEventsCount); res != C.icsneoc2_error_success {
printError(res)
return false
}
for i, event := range globalEvents[:globalEventsCount] {
globalEventsDescription := make([]byte, 255)
var globalEventsDescriptionLength C.uint32_t = 255
if res := C.icsneoc2_event_description_get(event, (*C.char)(unsafe.Pointer(&globalEventsDescription[0])), &globalEventsDescriptionLength); res != C.icsneoc2_error_success {
printError(res)
continue
}
fmt.Printf("\t%s: Global Event %d: %s\n", deviceDescription, i, globalEventsDescription)
}
fmt.Printf("\t%s: Received %d events and %d global events\n", deviceDescription, eventsCount, globalEventsCount)
return true
}
func transmitCANMessages(device *C.icsneoc2_device_t) bool {
var counter uint32 = 0
const msgCount int = 100
fmt.Printf("\tTransmitting %d messages...\n", msgCount)
for range msgCount {
// Create the message
var message *C.icsneoc2_message_t = nil
if res := C.icsneoc2_message_can_create(device, &message, 1); res != C.icsneoc2_error_success {
printError(res)
return false
}
defer func() {
if res := C.icsneoc2_message_can_free(device, message); res != C.icsneoc2_error_success {
printError(res)
}
}()
// Set the message attributes
res := C.icsneoc2_message_netid_set(device, message, C.icsneoc2_netid_hscan)
res += C.icsneoc2_message_can_arbid_set(device, message, 0x10)
res += C.icsneoc2_message_can_canfd_set(device, message, true)
res += C.icsneoc2_message_can_extended_set(device, message, true)
res += C.icsneoc2_message_can_baudrate_switch_set(device, message, true)
// Create the payload
data := [...]C.uint8_t{
(C.uint8_t)(counter >> 56),
(C.uint8_t)(counter >> 48),
(C.uint8_t)(counter >> 40),
(C.uint8_t)(counter >> 32),
(C.uint8_t)(counter >> 24),
(C.uint8_t)(counter >> 16),
(C.uint8_t)(counter >> 8),
(C.uint8_t)(counter >> 0),
}
res += C.icsneoc2_message_data_set(device, message, &data[0], (C.uint32_t)(len(data)))
res += C.icsneoc2_message_can_dlc_set(device, message, -1)
if res != C.icsneoc2_error_success {
fmt.Printf("\tFailed to modify message: %d\n", res)
return false
}
var messageCount C.uint32_t = 1
if res := C.icsneoc2_device_messages_transmit(device, &message, &messageCount); res != C.icsneoc2_error_success {
printError(res)
return false
}
counter += 1
}
return true
}
func processMessages(device *C.icsneoc2_device_t, messages []*C.icsneoc2_message_t) bool {
txCount := 0
for i, message := range messages {
// Get the message type
var msgType C.icsneoc2_msg_type_t = 0
if res := C.icsneoc2_message_type_get(device, message, &msgType); res != C.icsneoc2_error_success {
printError(res)
return false
}
// Get the message type name
msgTypeName := make([]byte, 128)
var msgTypeNameLength C.uint32_t = 128
if res := C.icsneoc2_message_type_name_get(msgType, (*C.char)(unsafe.Pointer(&msgTypeName[0])), &msgTypeNameLength); res != C.icsneoc2_error_success {
printError(res)
return false
}
// Check if the message is a bus message, ignore otherwise
if msgType != C.icsneoc2_msg_type_bus {
fmt.Print("\tIgnoring message type: %d (%s)\n", msgType, msgTypeName)
continue
}
// Get the message bus type
var msgBusType C.icsneoc2_msg_bus_type_t = 0
if res := C.icsneoc2_message_bus_type_get(device, message, &msgBusType); res != C.icsneoc2_error_success {
printError(res)
return false
}
// Get the bus message type name
msgBusTypeName := make([]byte, 128)
var msgBusTypeNameLength C.uint32_t = 128
if res := C.icsneoc2_bus_type_name_get(msgBusType, (*C.char)(unsafe.Pointer(&msgBusTypeName[0])), &msgBusTypeNameLength); res != C.icsneoc2_error_success {
printError(res)
return false
}
// Check if the message is a transmit message
var isTransmit C.bool = false
if res := C.icsneoc2_message_is_transmit(device, message, &isTransmit); res != C.icsneoc2_error_success {
printError(res)
return false
}
if isTransmit {
txCount += 1
continue
}
fmt.Printf("\t%d) Message type: %d bus type: %s (%d)\n", i, msgType, msgBusTypeName, msgBusType)
if msgBusType == C.icsneoc2_msg_bus_type_can {
var arbid C.uint32_t = 0
var dlc C.int32_t = 0
var netid C.icsneoc2_netid_t = 0
var isRemote C.bool = false
var isCanfd C.bool = false
var isExtended C.bool = false
data := make([]byte, 64)
var dataLength C.uint32_t = 64
netidName := make([]byte, 128)
var netidNameLength C.uint32_t = 128
var res C.icsneoc2_error_t = C.icsneoc2_error_success
res = C.icsneoc2_message_netid_get(device, message, &netid)
res += C.icsneoc2_netid_name_get(netid, (*C.char)(unsafe.Pointer(&netidName)), &netidNameLength)
res += C.icsneoc2_message_can_arbid_get(device, message, &arbid)
res += C.icsneoc2_message_can_dlc_get(device, message, &dlc)
res += C.icsneoc2_message_can_is_remote(device, message, &isRemote)
res += C.icsneoc2_message_can_is_canfd(device, message, &isCanfd)
res += C.icsneoc2_message_can_is_extended(device, message, &isExtended)
res += C.icsneoc2_message_data_get(device, message, (*C.uint8_t)(unsafe.Pointer(&data[0])), &dataLength)
// We really should check the error message for all of these since we can't tell the exact error if something
// bad happens but for an example this should be okay.
if res != C.icsneoc2_error_success {
fmt.Printf("\tFailed to get CAN parameters (error: %d) for index %d\n", res, i)
continue
}
// Finally lets print the RX message
fmt.Printf("\t NetID: %s (0x%X)\tArbID: 0x%X\t DLC: %d\t Remote: %t\t CANFD: %t\t Extended: %t\t Data length: %d\n", netidName, netid, arbid, dlc, isRemote, isCanfd, isExtended, dataLength)
fmt.Printf("\t Data: [")
for _, d := range data[:dataLength] {
fmt.Printf(" 0x%X", d)
}
println(" ]")
continue
} else {
fmt.Printf("\tIgnoring bus message type: %d (%s)\n", msgBusType, msgBusTypeName)
continue
}
}
fmt.Printf("\tReceived %d messages total, %d were TX messages\n", len(messages), txCount)
return true
}

22
examples/zig/simple/.gitignore vendored 100644
View File

@ -0,0 +1,22 @@
# This file is for zig-specific build artifacts.
# If you have OS-specific or editor-specific files to ignore,
# such as *.swp or .DS_Store, put those in your global
# ~/.gitignore and put this in your ~/.gitconfig:
#
# [core]
# excludesfile = ~/.gitignore
#
# Cheers!
# -andrewrk
.zig-cache/
zig-out/
/release/
/debug/
/build/
/build-*/
/docgen_tmp/
# Although this was renamed to .zig-cache, let's leave it here for a few
# releases to make it less annoying to work with multiple branches.
zig-cache/

View File

@ -0,0 +1,72 @@
const std = @import("std");
// Although this function looks imperative, note that its job is to
// declaratively construct a build graph that will be executed by an external
// runner.
pub fn build(b: *std.Build) void {
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
// for restricting supported target set are available.
const target = b.standardTargetOptions(.{ .default_target = .{ .abi = .msvc } });
// Standard optimization options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
// set a preferred release mode, allowing the user to decide how to optimize.
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "simple",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
exe.linkLibC();
// Add support for icsneoc2
exe.addIncludePath(b.path("../../../include"));
exe.addLibraryPath(b.path("../../../build"));
exe.linkSystemLibrary("icsneoc2");
// This declares intent for the executable to be installed into the
// standard location when the user invokes the "install" step (the default
// step when running `zig build`).
b.installArtifact(exe);
// This *creates* a Run step in the build graph, to be executed when another
// step is evaluated that depends on it. The next line below will establish
// such a dependency.
const run_cmd = b.addRunArtifact(exe);
// By making the run step depend on the install step, it will be run from the
// installation directory rather than directly from within the cache directory.
// This is not necessary, however, if the application depends on other installed
// files, this ensures they will be present and in the expected location.
run_cmd.step.dependOn(b.getInstallStep());
// This allows the user to pass arguments to the application in the build
// command itself, like this: `zig build run -- arg1 arg2 etc`
if (b.args) |args| {
run_cmd.addArgs(args);
}
// This creates a build step. It will be visible in the `zig build --help` menu,
// and can be selected like this: `zig build run`
// This will evaluate the `run` step rather than the default, which is "install".
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
const exe_unit_tests = b.addTest(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
// Similar to creating the run step earlier, this exposes a `test` step to
// the `zig build --help` menu, providing a way for the user to request
// running the unit tests.
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&run_exe_unit_tests.step);
}

View File

@ -0,0 +1,72 @@
.{
// This is the default name used by packages depending on this one. For
// example, when a user runs `zig fetch --save <url>`, this field is used
// as the key in the `dependencies` table. Although the user can choose a
// different name, most users will stick with this provided value.
//
// It is redundant to include "zig" in this name because it is already
// within the Zig package namespace.
.name = "simple",
// This is a [Semantic Version](https://semver.org/).
// In a future version of Zig it will be used for package deduplication.
.version = "0.0.0",
// This field is optional.
// This is currently advisory only; Zig does not yet do anything
// with this value.
//.minimum_zig_version = "0.11.0",
// This field is optional.
// Each dependency must either provide a `url` and `hash`, or a `path`.
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
// Once all dependencies are fetched, `zig build` no longer requires
// internet connectivity.
.dependencies = .{
// See `zig fetch --save <url>` for a command-line interface for adding dependencies.
//.example = .{
// // When updating this field to a new URL, be sure to delete the corresponding
// // `hash`, otherwise you are communicating that you expect to find the old hash at
// // the new URL.
// .url = "https://example.com/foo.tar.gz",
//
// // This is computed from the file contents of the directory of files that is
// // obtained after fetching `url` and applying the inclusion rules given by
// // `paths`.
// //
// // This field is the source of truth; packages do not come from a `url`; they
// // come from a `hash`. `url` is just one of many possible mirrors for how to
// // obtain a package matching this `hash`.
// //
// // Uses the [multihash](https://multiformats.io/multihash/) format.
// .hash = "...",
//
// // When this is provided, the package is found in a directory relative to the
// // build root. In this case the package's hash is irrelevant and therefore not
// // computed. This field and `url` are mutually exclusive.
// .path = "foo",
//
// // When this is set to `true`, a package is declared to be lazily
// // fetched. This makes the dependency only get fetched if it is
// // actually used.
// .lazy = false,
//},
},
// Specifies the set of files and directories that are included in this package.
// Only files and directories listed here are included in the `hash` that
// is computed for this package. Only files listed here will remain on disk
// when using the zig package manager. As a rule of thumb, one should list
// files required for compilation plus any license(s).
// Paths are relative to the build root. Use the empty string (`""`) to refer to
// the build root itself.
// A directory listed here means that all files within, recursively, are included.
.paths = .{
"build.zig",
"build.zig.zon",
"src",
// For example...
//"LICENSE",
//"README.md",
},
}

View File

@ -0,0 +1,421 @@
const std = @import("std");
const print = std.debug.print;
const ics = @cImport({
@cInclude("icsneo/icsneoc2.h");
@cInclude("icsneo/icsneoc2types.h");
});
const c = @cImport({
@cInclude("time.h");
});
pub fn main() !void {
// Find devices connected to host.
const MAX_DEVICE_COUNT: u32 = 255;
var device_buffer: [MAX_DEVICE_COUNT]?*ics.icsneoc2_device_t = undefined;
var devices_count: u32 = MAX_DEVICE_COUNT;
print("Finding devices... ", .{});
if (!check_error(
ics.icsneoc2_device_find_all(&device_buffer, &devices_count, null),
"Failed to find device",
)) {
return;
}
print("OK, {d} device{s} found\n", .{ devices_count, if (devices_count == 1) "" else "s" });
// Lets just take a slice of the entire device buffer
const devices = device_buffer[0..devices_count];
// List off the devices
for (devices) |device| {
// Get description of the device
var description: [255]u8 = [_:0]u8{0} ** 255;
var description_length: u32 = 255;
if (!check_error(
ics.icsneoc2_device_description_get(device, &description, &description_length),
"\tFailed to get device description",
)) {
return;
}
print("{s}. {*}\n", .{ description, device.? });
// Get/Set open options
var options: ics.icsneoc2_open_options_t = ics.icsneoc2_open_options_none;
if (!check_error(
ics.icsneoc2_device_open_options_get(device, &options),
"\tFailed to get device open options",
)) {
return;
}
// Disable Syncing RTC and going online
options &= ~@as(ics.icsneoc2_open_options_t, ics.icsneoc2_open_options_sync_rtc);
options &= ~@as(ics.icsneoc2_open_options_t, ics.icsneoc2_open_options_go_online);
print("\tDevice open options: 0x{X}\n", .{options});
if (!check_error(
ics.icsneoc2_device_open_options_set(device, options),
"\tFailed to set device open options",
)) {
return;
}
// Open the device
print("\tOpening device: {s}...\n", .{description});
if (!check_error(
ics.icsneoc2_device_open(device),
"\tFailed to open device",
)) {
return;
}
defer {
// Finally, close the device.
if (!print_device_events(device)) {
print("\tFailed to print events...\n", .{});
}
print("\tClosing device: {s}... ", .{description});
if (check_error(
ics.icsneoc2_device_close(device),
"\tFailed to close device",
)) {
print("OK\n", .{});
}
}
// Get timestamp resolution of the device
print("\tGetting timestamp resolution... ", .{});
var timestamp_resolution: u32 = 0;
if (!check_error(
ics.icsneoc2_device_timestamp_resolution_get(device, &timestamp_resolution),
"\tFailed to get timestamp resolution",
)) {
return;
}
print("{d}ns\n", .{timestamp_resolution});
// Get baudrates for HSCAN
print("\tGetting HSCAN baudrate... ", .{});
var baudrate: u64 = 0;
if (!check_error(
ics.icsneoc2_device_baudrate_get(device, ics.icsneoc2_netid_hscan, &baudrate),
"\tFailed to get baudrate",
)) {
return;
}
print("{d}mbit/s\n", .{baudrate});
// Get FDbaudrates for HSCAN
print("\tGetting FD HSCAN baudrate... ", .{});
var fd_baudrate: u64 = 0;
if (!check_error(
ics.icsneoc2_device_canfd_baudrate_get(device, ics.icsneoc2_netid_hscan, &fd_baudrate),
"\tFailed to get FD baudrate",
)) {
return;
}
print("{d}mbit/s\n", .{fd_baudrate});
// Set baudrates for HSCAN
// save_to_device: If this is set to true, the baudrate will be saved on the device
// and will persist through a power cycle
print("\tSetting HSCAN Baudrate... ", .{});
const save_to_device: bool = false;
if (!check_error(
ics.icsneoc2_device_baudrate_set(device, ics.icsneoc2_netid_hscan, baudrate, save_to_device),
"\tFailed to set baudrate",
)) {
return;
}
print("OK\n", .{});
// Set FDbaudrates for HSCAN
print("\tSetting FD HSCAN Baudrate... ", .{});
if (!check_error(
ics.icsneoc2_device_canfd_baudrate_set(device, ics.icsneoc2_netid_hscan, baudrate, save_to_device),
"\tFailed to set FD baudrate",
)) {
return;
}
print("OK\n", .{});
// Get RTC
print("\tGetting RTC... ", .{});
var unix_epoch: c.time_t = 0;
if (!check_error(
ics.icsneoc2_device_rtc_get(device, &unix_epoch),
"\tFailed to get RTC",
)) {
return;
}
print_rtc(unix_epoch);
// Set RTC
print("\tSetting RTC to current time... ", .{});
const current_time: i64 = c.time(0);
if (!check_error(
ics.icsneoc2_device_rtc_set(device, current_time),
"\tFailed to set RTC",
)) {
return;
}
print("OK\n", .{});
// Get RTC
print("\tGetting RTC... ", .{});
if (!check_error(
ics.icsneoc2_device_rtc_get(device, &unix_epoch),
"\tFailed to get RTC",
)) {
return;
}
print_rtc(unix_epoch);
// Go online, start acking traffic
print("\tGoing online... ", .{});
if (!check_error(
ics.icsneoc2_device_go_online(device, true),
"\tFailed to go online",
)) {
return;
}
// Redundant check to show how to check if the device is online, if the previous
// icsneoc2_device_go_online call was successful we can assume we are online already
var is_online: bool = false;
if (!check_error(
ics.icsneoc2_device_is_online(device, &is_online),
"\tFailed to check if online",
)) {
return;
}
print("{s}\n", .{if (is_online) "Online" else "Offline"});
// Transmit CAN messages
if (!transmit_can_messages(device)) {
return;
}
// Wait for the bus to collect some messages, requires an active bus to get messages
print("\tWaiting 1 second for messages...\n", .{});
std.time.sleep(std.time.ns_per_s);
// Get the messages
var messages: [20000]?*ics.icsneoc2_message_t = [_]?*ics.icsneoc2_message_t{null} ** 20000;
var messages_count: u32 = 20000;
const res = ics.icsneoc2_device_messages_get(device, &messages, &messages_count, 3000);
if (!check_error(
res,
"\tFailed to get messages on device",
)) {
return;
}
// Process the messages
if (!process_messages(device, messages[0..messages_count])) {
return;
}
}
}
pub fn check_error(error_code: ics.icsneoc2_error_t, error_msg: []const u8) bool {
if (error_code == ics.icsneoc2_error_success) {
return true;
}
var error_str: [256]u8 = [_:0]u8{0} ** 256;
var error_length: u32 = 256;
const res: ics.icsneoc2_error_t = ics.icsneoc2_error_code_get(
error_code,
&error_str,
&error_length,
);
if (res != ics.icsneoc2_error_success) {
print(
"{s}: Failed to get string for error code {d} with error code {d}\n",
.{ error_msg, error_code, res },
);
return false;
}
print(
"{s}: \"{s}\" ({d})\n",
.{ error_msg, error_str, error_code },
);
return error_code == ics.icsneoc2_error_success;
}
pub fn print_rtc(unix_epoch: c.time_t) void {
var rtc_time: [32]u8 = [_:0]u8{0} ** 32;
_ = c.strftime(&rtc_time, rtc_time.len, "%Y-%m-%d %H:%M:%S", c.localtime(&unix_epoch));
print("{d} {s}\n", .{ unix_epoch, rtc_time });
}
pub fn transmit_can_messages(device: ?*ics.icsneoc2_device_t) bool {
const msg_count: usize = 100;
print("\tTransmitting {} messages...\n", .{msg_count});
for (0..msg_count) |counter| {
// Create the message
var message: ?*ics.icsneoc2_message_t = null;
var message_count: u32 = 1;
if (!check_error(
ics.icsneoc2_message_can_create(device, &message, message_count),
"\tFailed to create CAN message",
)) {
return false;
}
defer {
_ = check_error(
ics.icsneoc2_message_can_free(device, message),
"\tFailed to free CAN message",
);
}
// Set the message attributes
var res: ics.icsneoc2_error_t = ics.icsneoc2_message_netid_set(device, message, ics.icsneoc2_netid_hscan);
res += ics.icsneoc2_message_can_arbid_set(device, message, 0x10);
res += ics.icsneoc2_message_can_canfd_set(device, message, true);
res += ics.icsneoc2_message_can_extended_set(device, message, true);
res += ics.icsneoc2_message_can_baudrate_switch_set(device, message, true);
// Create the payload
var data: [8]u8 = .{
@intCast(counter >> 56),
@intCast(counter >> 48),
@intCast(counter >> 40),
@intCast(counter >> 32),
@intCast(counter >> 24),
@intCast(counter >> 16),
@intCast(counter >> 8),
@intCast(counter >> 0),
};
res += ics.icsneoc2_message_data_set(device, message, &data, data.len);
res += ics.icsneoc2_message_can_dlc_set(device, message, -1);
if (!check_error(res, "\tFailed to set CAN Message attributes!")) {
return false;
}
if (!check_error(
ics.icsneoc2_device_messages_transmit(device, &message, &message_count),
"\tFailed to transmit message",
)) {
return false;
}
}
return true;
}
pub fn process_messages(device: ?*ics.icsneoc2_device_t, messages: []const ?*ics.icsneoc2_message_t) bool {
var tx_count: usize = 0;
for (messages, 0..) |message, i| {
// Get the message type
var msg_type: ics.icsneoc2_msg_type_t = 0;
if (!check_error(
ics.icsneoc2_message_type_get(device, message.?, &msg_type),
"\tFailed to get message type",
)) {
return false;
}
// Get the message type name
var msg_type_name: [128]u8 = [_:0]u8{0} ** 128;
var msg_type_name_length: u32 = 128;
if (!check_error(
ics.icsneoc2_message_type_name_get(msg_type, &msg_type_name, &msg_type_name_length),
"\tFailed to get message type name",
)) {
return false;
}
// Check if the message is a bus message, ignore otherwise
if (msg_type != ics.icsneoc2_msg_type_bus) {
print("\tIgnoring message type: {d} ({s})\n", .{ msg_type, msg_type_name });
continue;
}
// Get the message bus type
var msg_bus_type: ics.icsneoc2_msg_bus_type_t = 0;
if (!check_error(
ics.icsneoc2_message_bus_type_get(device, message, &msg_bus_type),
"\tFailed to get message bus type",
)) {
return false;
}
// Get the message type name
var msg_bus_type_name: [128]u8 = [_:0]u8{0} ** 128;
var msg_bus_type_name_length: u32 = 128;
if (!check_error(
ics.icsneoc2_bus_type_name_get(msg_bus_type, &msg_bus_type_name, &msg_bus_type_name_length),
"\tFailed to get message bus type name",
)) {
return false;
}
// Check if message is a transmit message
var is_tx: bool = false;
if (!check_error(
ics.icsneoc2_message_is_transmit(device, message, &is_tx),
"\tFailed to get message is transmit",
)) {
return false;
}
if (is_tx) {
tx_count += 1;
continue;
}
print("\t{d} Message type: {d} bus type: {s} ({d})\n", .{ i, msg_type, msg_bus_type_name, msg_bus_type });
// Check if the message is a CAN message, ignore otherwise
if (msg_bus_type == ics.icsneoc2_msg_bus_type_can) {
var arbid: u32 = 0;
var dlc: i32 = 0;
var netid: ics.icsneoc2_netid_t = 0;
var is_remote: bool = false;
var is_canfd: bool = false;
var is_extended: bool = false;
var data: [64]u8 = [_]u8{0} ** 64;
var data_length: u32 = 64;
var netid_name: [128]u8 = [_:0]u8{0} ** 128;
var netid_name_length: u32 = 128;
var res: ics.icsneoc2_error_t = ics.icsneoc2_error_success;
res = ics.icsneoc2_message_netid_get(device, message, &netid);
res += ics.icsneoc2_netid_name_get(netid, &netid_name, &netid_name_length);
res += ics.icsneoc2_message_can_arbid_get(device, message, &arbid);
res += ics.icsneoc2_message_can_dlc_get(device, message, &dlc);
res += ics.icsneoc2_message_can_is_remote(device, message, &is_remote);
res += ics.icsneoc2_message_can_is_canfd(device, message, &is_canfd);
res += ics.icsneoc2_message_can_is_extended(device, message, &is_extended);
res += ics.icsneoc2_message_data_get(device, message, &data, &data_length);
// We really should check the error message for all of these since we can't tell the exact error if something
// bad happens but for an example this should be okay.
if (res != ics.icsneoc2_error_success) {
print("\tFailed to get CAN parameters (error: {d}) for index {d}\n", .{ res, i });
continue;
}
// Finally lets print the RX message
print("\t NetID: {s} (0x{X})\tArbID: 0x{X}\t DLC: {d}\t Remote: {}\t CANFD: {}\t Extended: {}\t Data length: {d}\n", .{ netid_name, netid, arbid, dlc, is_remote, is_canfd, is_extended, data_length });
print("\t Data: {any}\n", .{data[0..data_length]});
} else {
print("\tIgnoring bus message type: {d} ({s})\n", .{ msg_bus_type, msg_bus_type_name });
continue;
}
}
print("\tReceived {d} messages total, {d} were TX messages\n", .{ messages.len, tx_count });
return true;
}
pub fn print_device_events(device: ?*ics.icsneoc2_device_t) bool {
// Get device events
var events: [1024]?*ics.icsneoc2_event_t = [_]?*ics.icsneoc2_event_t{null} ** 1024;
var events_count: u32 = 1024;
if (!check_error(
ics.icsneoc2_device_events_get(device, &events, &events_count),
"\tFailed to get device events",
)) {
return false;
}
for (events[0..events_count], 0..) |event, i| {
var event_description: [256]u8 = [_:0]u8{0} ** 256;
var event_description_length: u32 = 256;
if (!check_error(
ics.icsneoc2_event_description_get(event, &event_description, &event_description_length),
"\tFailed to get event description",
)) {
continue;
}
print("\tEvent {d}: {s}\n", .{ i, event_description });
}
// Get global events
var global_events: [1024]?*ics.icsneoc2_event_t = [_]?*ics.icsneoc2_event_t{null} ** 1024;
var global_events_count: u32 = 1024;
if (!check_error(
ics.icsneoc2_events_get(&global_events, &global_events_count),
"\tFailed to get device global events",
)) {
return false;
}
for (global_events[0..global_events_count], 0..) |event, i| {
var event_description: [256]u8 = [_:0]u8{0} ** 256;
var event_description_length: u32 = 256;
if (!check_error(
ics.icsneoc2_event_description_get(event, &event_description, &event_description_length),
"\tFailed to get event description",
)) {
continue;
}
print("\tGlobal event {d}: {s}\n", .{ i, event_description });
}
print("\tReceived {d} events and {d} global events\n", .{ events_count, global_events_count });
return true;
}

View File

@ -109,6 +109,7 @@ public:
LINSettingsNotAvailable = 0x2053,
ModeNotFound = 0x2054,
AppErrorParsingFailed = 0x2055,
GPTPNotSupported = 0x2056,
// Transport Events
FailedToRead = 0x3000,

View File

@ -51,6 +51,7 @@ enum class ExtendedCommand : uint16_t {
StartDHCPServer = 0x0016,
StopDHCPServer = 0x0017,
GetSupportedFeatures = 0x0018,
GetGPTPStatus = 0x0019,
GetComponentVersions = 0x001A,
Reboot = 0x001C,
SetRootFSEntryFlags = 0x0027,

View File

@ -20,8 +20,10 @@ enum class PCMType : uint8_t {
using ChannelMap = std::unordered_map<uint8_t, uint8_t>;
class A2BMessage : public Frame {
class A2BMessage : public BusMessage {
public:
const BusMessage::Type getBusType() const final { return BusMessage::Type::A2B; }
static constexpr size_t maxAudioBufferSize = 2048;
enum class TDMMode : uint8_t {

View File

@ -64,9 +64,9 @@ enum class AppErrorType : uint16_t {
AppNoError = 255
};
class AppErrorMessage : public RawMessage {
class AppErrorMessage : public InternalMessage {
public:
AppErrorMessage() : RawMessage(Message::Type::AppError, Network::NetID::RED_App_Error) {}
AppErrorMessage() : InternalMessage(Message::Type::AppError, Network::NetID::RED_App_Error) {}
uint16_t errorType;
Network::NetID errorNetID;
uint32_t timestamp10us;

View File

@ -1,25 +0,0 @@
#ifndef __CANERRORCOUNTMESSAGE_H_
#define __CANERRORCOUNTMESSAGE_H_
#ifdef __cplusplus
#include "icsneo/communication/message/message.h"
namespace icsneo {
class CANErrorCountMessage : public Message {
public:
CANErrorCountMessage(uint8_t tec, uint8_t rec, bool busOffFlag)
: Message(Message::Type::CANErrorCount), transmitErrorCount(tec), receiveErrorCount(rec), busOff(busOffFlag){}
Network network;
uint8_t transmitErrorCount;
uint8_t receiveErrorCount;
bool busOff;
};
}
#endif // __cplusplus
#endif

View File

@ -0,0 +1,40 @@
#ifndef __CANERRORMESSAGE_H_
#define __CANERRORMESSAGE_H_
#ifdef __cplusplus
#include "icsneo/communication/message/message.h"
namespace icsneo {
enum class CANErrorCode : uint8_t
{
NoError = 0,
StuffError = 1,
FormError = 2,
AckError = 3,
Bit1Error = 4,
Bit0Error = 5,
CRCError = 6,
NoChange = 7
};
class CANErrorMessage : public Message {
public:
CANErrorMessage() : Message(Type::CANError) {}
Network network;
uint8_t transmitErrorCount;
uint8_t receiveErrorCount;
bool busOff;
bool errorPassive;
bool errorWarn;
CANErrorCode dataErrorCode;
CANErrorCode errorCode;
};
using CANErrorCountMessage = CANErrorMessage;
}
#endif // __cplusplus
#endif

View File

@ -7,8 +7,10 @@
namespace icsneo {
class CANMessage : public Frame {
class CANMessage : public BusMessage {
public:
const BusMessage::Type getBusType() const final { return BusMessage::Type::CAN; }
uint32_t arbid;
uint8_t dlcOnWire;
bool isRemote = false; // Not allowed if CAN FD

View File

@ -7,9 +7,9 @@
namespace icsneo {
class DiskDataMessage : public RawMessage {
class DiskDataMessage : public InternalMessage {
public:
DiskDataMessage(std::vector<uint8_t>&& d) : RawMessage(Network::NetID::DiskData) {
DiskDataMessage(std::vector<uint8_t>&& d) : InternalMessage(Network::NetID::DiskData) {
data = std::move(d);
}
};

View File

@ -31,11 +31,13 @@ struct MACAddress {
}
};
class EthernetMessage : public Frame {
class EthernetMessage : public BusMessage {
public:
const BusMessage::Type getBusType() const final { return BusMessage::Type::Ethernet; }
bool preemptionEnabled = false;
uint8_t preemptionFlags = 0;
bool fcsAvailable = false;
std::optional<uint32_t> fcs;
bool frameTooShort = false;
bool noPadding = false;

View File

@ -0,0 +1,43 @@
#ifndef __ETHERNETSTATUSMESSAGE_H__
#define __ETHERNETSTATUSMESSAGE_H__
#ifdef __cplusplus
#include "icsneo/communication/message/message.h"
#include <memory>
namespace icsneo {
class EthernetStatusMessage : public Message {
public:
enum class LinkSpeed {
LinkSpeedAuto,
LinkSpeed10,
LinkSpeed100,
LinkSpeed1000,
LinkSpeed2500,
LinkSpeed5000,
LinkSpeed10000,
};
enum class LinkMode {
LinkModeAuto,
LinkModeMaster,
LinkModeSlave,
LinkModeInvalid,
};
EthernetStatusMessage(Network net, bool state, LinkSpeed speed, bool duplex, LinkMode mode) : Message(Type::EthernetStatus),
network(net), state(state), speed(speed), duplex(duplex), mode(mode) {}
Network network;
bool state;
LinkSpeed speed;
bool duplex;
LinkMode mode;
static std::shared_ptr<Message> DecodeToMessage(const std::vector<uint8_t>& bytestream);
};
}; // namespace icsneo
#endif // __cplusplus
#endif // __ETHERNETSTATUSMESSAGE_H__

View File

@ -9,7 +9,7 @@
namespace icsneo {
class ExtendedDataMessage : public Frame {
class ExtendedDataMessage : public InternalMessage {
public:
#pragma pack(push, 2)
struct ExtendedDataHeader {

View File

@ -13,8 +13,8 @@ namespace icsneo {
class CANMessageFilter : public MessageFilter {
public:
CANMessageFilter() : MessageFilter(Network::Type::CAN), arbid(INVALID_ARBID) { messageType = Message::Type::Frame; }
CANMessageFilter(uint32_t arbid) : MessageFilter(Network::Type::CAN), arbid(arbid) { messageType = Message::Type::Frame; }
CANMessageFilter() : MessageFilter(Network::Type::CAN), arbid(INVALID_ARBID) { messageType = Message::Type::BusMessage; }
CANMessageFilter(uint32_t arbid) : MessageFilter(Network::Type::CAN), arbid(arbid) { messageType = Message::Type::BusMessage; }
bool match(const std::shared_ptr<Message>& message) const {
if(!MessageFilter::match(message))

View File

@ -27,9 +27,9 @@ public:
if(!matchMessageType(message->type))
return false;
if(message->type == Message::Type::Frame || message->type == Message::Type::Main51 ||
message->type == Message::Type::RawMessage || message->type == Message::Type::ReadSettings) {
const auto frame = std::static_pointer_cast<RawMessage>(message);
if(message->type == Message::Type::BusMessage || message->type == Message::Type::Main51 ||
message->type == Message::Type::InternalMessage || message->type == Message::Type::ReadSettings) {
const auto frame = std::static_pointer_cast<InternalMessage>(message);
if(!matchNetworkType(frame->network.getType()))
return false;
if(!matchNetID(frame->network.getNetID()))

View File

@ -7,9 +7,9 @@
namespace icsneo {
class FlashMemoryMessage : public RawMessage {
class FlashMemoryMessage : public InternalMessage {
public:
FlashMemoryMessage() : RawMessage(Message::Type::RawMessage, Network::NetID::RED_INT_MEMORYREAD) {}
FlashMemoryMessage() : InternalMessage(Message::Type::InternalMessage, Network::NetID::RED_INT_MEMORYREAD) {}
uint16_t startAddress = 0;
};

View File

@ -10,8 +10,10 @@
namespace icsneo {
class FlexRayMessage : public Frame {
class FlexRayMessage : public BusMessage {
public:
const BusMessage::Type getBusType() const final { return BusMessage::Type::FlexRay; }
uint16_t slotid = 0;
double tsslen = 0;
double framelen = 0;

View File

@ -0,0 +1,103 @@
#ifndef __GPTPSTATUSMESSAGE_H_
#define __GPTPSTATUSMESSAGE_H_
#ifdef __cplusplus
#include "icsneo/communication/message/message.h"
#include "icsneo/communication/command.h"
#include "icsneo/api/eventmanager.h"
namespace icsneo {
class GPTPStatus : public Message {
public:
typedef uint64_t TimeInterval;
typedef uint64_t ClockID;
struct Timestamp {
uint64_t seconds;
uint32_t nanoseconds;
double toSeconds() const {
static constexpr double billion = 1e9;
return (double)seconds + ((double)nanoseconds) / billion;
}
};
struct ScaledNanoSeconds {
int16_t nanosecondsMSB; // The most significant bits
int64_t nanosecondsLSB; // The least significant bits
int16_t fractionalNanoseconds; // Fractional part
};
struct PortID {
ClockID clockIdentity;
uint16_t portNumber;
};
struct ClockQuality {
uint8_t clockClass;
uint8_t clockAccuracy;
uint16_t offsetScaledLogVariance;
};
struct SystemID {
uint8_t priority1;
ClockQuality clockQuality;
uint8_t priority2;
ClockID clockID;
};
struct PriorityVector {
SystemID sysID;
uint16_t stepsRemoved;
PortID portID;
uint16_t portNumber;
};
struct ParentDS {
PortID parentPortIdentity;
int32_t cumulativeRateRatio;
ClockID grandmasterIdentity;
uint8_t gmClockQualityClockClass;
uint8_t gmClockQualityClockAccuracy;
uint16_t gmClockQualityOffsetScaledLogVariance;
uint8_t gmPriority1;
uint8_t gmPriority2;
};
struct CurrentDS {
uint16_t stepsRemoved;
TimeInterval offsetFromMaster;
ScaledNanoSeconds lastgmPhaseChange;
double lastgmFreqChange;
uint16_t gmTimeBaseIndicator;
uint32_t gmChangeCount;
uint32_t timeOfLastgmChangeEvent;
uint32_t timeOfLastgmPhaseChangeEvent;
uint32_t timeOfLastgmFreqChangeEvent;
};
GPTPStatus() : Message(Message::Type::GPTPStatus) {}
static std::shared_ptr<GPTPStatus> DecodeToMessage(std::vector<uint8_t>& bytes, const device_eventhandler_t& report);
Timestamp currentTime;
PriorityVector gmPriority;
int64_t msOffsetNs;
uint8_t isSync;
uint8_t linkStatus;
int64_t linkDelayNS;
uint8_t selectedRole;
uint8_t asCapable;
uint8_t isSyntonized;
Timestamp lastRXSyncTS; // t2 in IEEE 1588-2019 Figure-16
CurrentDS currentDS;
ParentDS parentDS;
bool shortFormat = false; // Set to true if the above variables weren't set (some firmware versions do not contain all the above variables)
};
}
#endif
#endif

View File

@ -8,8 +8,10 @@
namespace icsneo {
class I2CMessage : public Frame {
class I2CMessage : public BusMessage {
public:
const BusMessage::Type getBusType() const final { return BusMessage::Type::I2C; }
enum class DeviceMode : uint8_t {
Target = 0,
Controller = 1

View File

@ -8,8 +8,10 @@
namespace icsneo {
class ISO9141Message : public Frame {
class ISO9141Message : public BusMessage {
public:
const BusMessage::Type getBusType() const final { return BusMessage::Type::ISO9141; }
std::array<uint8_t, 3> header;
bool isInit = false;
bool isBreak = false;

View File

@ -34,8 +34,10 @@ struct LINStatusFlags {
bool BreakOnly = false;
};
class LINMessage : public Frame {
class LINMessage : public BusMessage {
public:
const BusMessage::Type getBusType() const final { return BusMessage::Type::LIN; }
enum class Type : uint8_t {
NOT_SET = 0,
LIN_COMMANDER_MSG,

View File

@ -8,9 +8,9 @@
#include "icsneo/communication/livedata.h"
namespace icsneo {
class LiveDataMessage : public RawMessage {
class LiveDataMessage : public InternalMessage {
public:
LiveDataMessage() : RawMessage(Message::Type::LiveData, Network::NetID::ExtendedCommand) {}
LiveDataMessage() : InternalMessage(Message::Type::LiveData, Network::NetID::ExtendedCommand) {}
LiveDataHandle handle;
LiveDataCommand cmd;
};

View File

@ -8,9 +8,9 @@
namespace icsneo {
class Main51Message : public RawMessage {
class Main51Message : public InternalMessage {
public:
Main51Message() : RawMessage(Message::Type::Main51, Network::NetID::Main51) {}
Main51Message() : InternalMessage(Message::Type::Main51, Network::NetID::Main51) {}
Command command = Command(0);
bool forceShortFormat = false; // Necessary for EnableNetworkCom and EnableNetworkComEx
};

View File

@ -7,8 +7,10 @@
namespace icsneo {
class MDIOMessage : public Frame {
class MDIOMessage : public BusMessage {
public:
const BusMessage::Type getBusType() const final { return BusMessage::Type::MDIO; }
enum class Clause : uint8_t {
Clause45 = 0,
Clause22 = 1

View File

@ -7,23 +7,82 @@ typedef uint16_t neomessagetype_t;
#ifdef __cplusplus
#include "icsneo/communication/network.h"
#include "icsneo/icsneoc2types.h"
#include <vector>
#include <sstream>
#include <string>
namespace icsneo {
class Message {
/**
* @brief Type of message class
*
* @see AbstractMessage::getMsgType()
*/
typedef enum class MessageType : icsneoc2_msg_type_t {
Device = icsneoc2_msg_type_device,
Internal = icsneoc2_msg_type_internal,
Bus = icsneoc2_msg_type_bus,
MaxSize = icsneoc2_msg_type_maxsize,
} MessageType;
/**
* @brief Pure virtual abstract class for all messages. Inherit from Message over this class.
*
* @see Message
*
*/
class AbstractMessage {
public:
virtual const MessageType getMsgType() const = 0;
};
/**
* @brief Base Message class representing Device messages.
*/
class Message : public AbstractMessage {
public:
virtual const MessageType getMsgType() const { return MessageType::Device; }
/**
* @brief Get the string representation of the message type
*
* @return String representation of the message type
*
* @see AbstractMessage::getMsgType()
*/
static std::string getMsgTypeName(MessageType msgType) {
switch (msgType) {
case MessageType::Device:
return "Device";
case MessageType::Internal:
return "Internal";
case MessageType::Bus:
return "Bus";
// Don't default here so we can rely on the compiler to warn us about missing cases
};
std::stringstream ss;
ss << "Unknown (" << static_cast<int>(msgType) << ")";
return ss.str();
}
enum class Type : neomessagetype_t {
Frame = 0,
BusMessage = 0,
// Deprecated: Will be removed in the future.
Frame = BusMessage,
CANErrorCount = 0x100,
CANError = 0x100,
LINHeaderOnly = 0x200,
LINBreak = 0x201,
// Past 0x8000 are all for internal use only
Invalid = 0x8000,
RawMessage = 0x8001,
InternalMessage = 0x8001,
// Deprecated: Will be removed in the future.
RawMessage = InternalMessage,
ReadSettings = 0x8002,
ResetStatus = 0x8003,
DeviceVersion = 0x8004,
@ -41,6 +100,8 @@ public:
HardwareInfo = 0x8010,
TC10Status = 0x8011,
AppError = 0x8012,
GPTPStatus = 0x8013,
EthernetStatus = 0x8014,
};
Message(Type t) : type(t) {}
@ -49,26 +110,69 @@ public:
uint64_t timestamp = 0;
};
class RawMessage : public Message {
/**
* @brief Internal Message class representing Device messages that shouldn't be exposed to public APIs.
*/
class InternalMessage : public Message {
public:
RawMessage(Message::Type type = Message::Type::RawMessage) : Message(type) {}
RawMessage(Message::Type type, Network net) : Message(type), network(net) {}
RawMessage(Network net) : Message(Message::Type::RawMessage), network(net) {}
RawMessage(Network net, std::vector<uint8_t> d) : Message(Message::Type::RawMessage), network(net), data(d) {}
InternalMessage(Message::Type type = Message::Type::InternalMessage) : Message(type) {}
InternalMessage(Message::Type type, Network net) : Message(type), network(net) {}
InternalMessage(Network net) : Message(Message::Type::InternalMessage), network(net) {}
InternalMessage(Network net, std::vector<uint8_t> d) : Message(Message::Type::InternalMessage), network(net), data(d) {}
virtual const MessageType getMsgType() const { return MessageType::Internal; }
Network network;
std::vector<uint8_t> data;
};
class Frame : public RawMessage {
/**
* @brief Bus Message class representing Device messages representing Bus networks like CAN, LIN, Ethernet, etc.
*/
class BusMessage : public InternalMessage {
public:
Frame() : RawMessage(Message::Type::Frame) {}
BusMessage() : InternalMessage(Message::Type::BusMessage) {}
/** @brief
* Bus message types, useful for filtering out or identifying Bus Messages.
*/
typedef enum class Type : icsneoc2_msg_bus_type_t {
Invalid = icsneoc2_msg_bus_type_invalid,
Internal = icsneoc2_msg_bus_type_internal,
CAN = icsneoc2_msg_bus_type_can,
LIN = icsneoc2_msg_bus_type_lin,
FlexRay = icsneoc2_msg_bus_type_flexray,
MOST = icsneoc2_msg_bus_type_most,
Ethernet = icsneoc2_msg_bus_type_ethernet,
LSFTCAN = icsneoc2_msg_bus_type_lsftcan,
SWCAN = icsneoc2_msg_bus_type_swcan,
ISO9141 = icsneoc2_msg_bus_type_iso9141,
I2C = icsneoc2_msg_bus_type_i2c,
A2B = icsneoc2_msg_bus_type_a2b,
SPI = icsneoc2_msg_bus_type_spi,
MDIO = icsneoc2_msg_bus_type_mdio,
ANY = icsneoc2_msg_bus_type_any,
OTHER = icsneoc2_msg_bus_type_other
} Type;
const MessageType getMsgType() const final { return MessageType::Bus; }
virtual const BusMessage::Type getBusType() const = 0;
// Description ID of the message. This is used for filtering / tracking in firmware and driver.
// This is equivalent to icsSpyMessage::DescriptionID
uint16_t description = 0;
// weather the message was originally transmitted on the bus. This is equivalent to
// SPY_STATUS_TX_MSG bit field in icsSpyMessage::StatusBitField
bool transmitted = false;
bool error = false;
};
/** @brief Backwards compatibility, RawMessage was renamed to better reflect what it actually does. */
typedef InternalMessage RawMessage;
/** @brief Backwards compatibility, Frame was renamed to better reflect what it actually does. */
typedef BusMessage Frame;
}
#endif // __cplusplus

View File

@ -7,9 +7,9 @@
namespace icsneo {
class NeoReadMemorySDMessage : public RawMessage {
class NeoReadMemorySDMessage : public InternalMessage {
public:
NeoReadMemorySDMessage() : RawMessage(Message::Type::RawMessage, Network::NetID::NeoMemorySDRead) {}
NeoReadMemorySDMessage() : InternalMessage(Message::Type::InternalMessage, Network::NetID::NeoMemorySDRead) {}
uint32_t startAddress = 0;
};

View File

@ -8,9 +8,9 @@
namespace icsneo {
class ReadSettingsMessage : public RawMessage {
class ReadSettingsMessage : public InternalMessage {
public:
ReadSettingsMessage() : RawMessage(Message::Type::ReadSettings, Network::NetID::ReadSettings) {}
ReadSettingsMessage() : InternalMessage(Message::Type::ReadSettings, Network::NetID::ReadSettings) {}
enum class Response : uint8_t {
OK = 0,

View File

@ -12,6 +12,8 @@ typedef uint8_t neonettype_t;
#include <optional>
#include <tuple>
#include <icsneo/icsneoc2types.h>
namespace icsneo {
class Network {
@ -23,187 +25,187 @@ class Network {
static constexpr uint16_t OFFSET_PLASMA_SLAVE3_RANGE2 = 12800;
public:
enum class NetID : neonetid_t {
Device = 0,
HSCAN = 1,
MSCAN = 2,
SWCAN = 3,
LSFTCAN = 4,
FordSCP = 5,
J1708 = 6,
Aux = 7,
J1850VPW = 8,
ISO9141 = 9,
DiskData = 10,
Main51 = 11,
RED = 12,
SCI = 13,
ISO9141_2 = 14,
ISO14230 = 15,
LIN = 16,
OP_Ethernet1 = 17,
OP_Ethernet2 = 18,
OP_Ethernet3 = 19,
enum class NetID : icsneoc2_netid_t {
Device = icsneoc2_netid_device,
HSCAN = icsneoc2_netid_hscan,
MSCAN = icsneoc2_netid_mscan,
SWCAN = icsneoc2_netid_swcan,
LSFTCAN = icsneoc2_netid_lsftcan,
FordSCP = icsneoc2_netid_fordscp,
J1708 = icsneoc2_netid_j1708,
Aux = icsneoc2_netid_aux,
J1850VPW = icsneoc2_netid_j1850vpw,
ISO9141 = icsneoc2_netid_iso9141,
DiskData = icsneoc2_netid_disk_data,
Main51 = icsneoc2_netid_main51,
RED = icsneoc2_netid_red,
SCI = icsneoc2_netid_sci,
ISO9141_2 = icsneoc2_netid_iso9141_2,
ISO14230 = icsneoc2_netid_iso14230,
LIN = icsneoc2_netid_lin,
OP_Ethernet1 = icsneoc2_netid_op_ethernet1,
OP_Ethernet2 = icsneoc2_netid_op_ethernet2,
OP_Ethernet3 = icsneoc2_netid_op_ethernet3,
// START Device Command Returns
// When we send a command, the device returns on one of these, depending on command
RED_EXT_MEMORYREAD = 20,
RED_INT_MEMORYREAD = 21,
RED_DFLASH_READ = 22,
NeoMemorySDRead = 23, // Response from NeoMemory (MemoryTypeSD)
CAN_ERRBITS = 24,
NeoMemoryWriteDone = 25,
RED_WAVE_CAN1_LOGICAL = 26,
RED_WAVE_CAN2_LOGICAL = 27,
RED_WAVE_LIN1_LOGICAL = 28,
RED_WAVE_LIN2_LOGICAL = 29,
RED_WAVE_LIN1_ANALOG = 30,
RED_WAVE_LIN2_ANALOG = 31,
RED_WAVE_MISC_ANALOG = 32,
RED_WAVE_MISCDIO2_LOGICAL = 33,
RED_NETWORK_COM_ENABLE_EX = 34,
RED_NEOVI_NETWORK = 35,
RED_READ_BAUD_SETTINGS = 36,
RED_OLDFORMAT = 37,
RED_SCOPE_CAPTURE = 38,
RED_HARDWARE_EXCEP = 39,
RED_GET_RTC = 40,
RED_EXT_MEMORYREAD = icsneoc2_netid_red_ext_memoryread,
RED_INT_MEMORYREAD = icsneoc2_netid_red_int_memoryread,
RED_DFLASH_READ = icsneoc2_netid_red_dflash_read,
NeoMemorySDRead = icsneoc2_netid_neo_memory_sdread, // Response from NeoMemory (MemoryTypeSD)
CAN_ERRBITS = icsneoc2_netid_can_errbits,
NeoMemoryWriteDone = icsneoc2_netid_neo_memory_write_done,
RED_WAVE_CAN1_LOGICAL = icsneoc2_netid_red_wave_can1_logical,
RED_WAVE_CAN2_LOGICAL = icsneoc2_netid_red_wave_can2_logical,
RED_WAVE_LIN1_LOGICAL = icsneoc2_netid_red_wave_lin1_logical,
RED_WAVE_LIN2_LOGICAL = icsneoc2_netid_red_wave_lin2_logical,
RED_WAVE_LIN1_ANALOG = icsneoc2_netid_red_wave_lin1_analog,
RED_WAVE_LIN2_ANALOG = icsneoc2_netid_red_wave_lin2_analog,
RED_WAVE_MISC_ANALOG = icsneoc2_netid_red_wave_misc_analog,
RED_WAVE_MISCDIO2_LOGICAL = icsneoc2_netid_red_wave_miscdio2_logical,
RED_NETWORK_COM_ENABLE_EX = icsneoc2_netid_red_network_com_enable_ex,
RED_NEOVI_NETWORK = icsneoc2_netid_red_neovi_network,
RED_READ_BAUD_SETTINGS = icsneoc2_netid_red_read_baud_settings,
RED_OLDFORMAT = icsneoc2_netid_red_oldformat,
RED_SCOPE_CAPTURE = icsneoc2_netid_red_scope_capture,
RED_HARDWARE_EXCEP = icsneoc2_netid_red_hardware_excep,
RED_GET_RTC = icsneoc2_netid_red_get_rtc,
// END Device Command Returns
ISO9141_3 = 41,
HSCAN2 = 42,
HSCAN3 = 44,
OP_Ethernet4 = 45,
OP_Ethernet5 = 46,
ISO9141_4 = 47,
LIN2 = 48,
LIN3 = 49,
LIN4 = 50,
ISO9141_3 = icsneoc2_netid_iso9141_3,
HSCAN2 = icsneoc2_netid_hscan2,
HSCAN3 = icsneoc2_netid_hscan3,
OP_Ethernet4 = icsneoc2_netid_op_ethernet4,
OP_Ethernet5 = icsneoc2_netid_op_ethernet5,
ISO9141_4 = icsneoc2_netid_iso9141_4,
LIN2 = icsneoc2_netid_lin2,
LIN3 = icsneoc2_netid_lin3,
LIN4 = icsneoc2_netid_lin4,
// MOST = 51, Old and unused
RED_App_Error = 52,
CGI = 53,
Reset_Status = 54,
FB_Status = 55,
App_Signal_Status = 56,
Read_Datalink_Cm_Tx_Msg = 57,
Read_Datalink_Cm_Rx_Msg = 58,
Logging_Overflow = 59,
ReadSettings = 60,
HSCAN4 = 61,
HSCAN5 = 62,
RS232 = 63,
UART = 64,
UART2 = 65,
UART3 = 66,
UART4 = 67,
SWCAN2 = 68,
Ethernet_DAQ = 69,
Data_To_Host = 70,
TextAPI_To_Host = 71,
SPI1 = 72,
OP_Ethernet6 = 73,
Red_VBat = 74,
OP_Ethernet7 = 75,
OP_Ethernet8 = 76,
OP_Ethernet9 = 77,
OP_Ethernet10 = 78,
OP_Ethernet11 = 79,
FlexRay1a = 80,
FlexRay1b = 81,
FlexRay2a = 82,
FlexRay2b = 83,
LIN5 = 84,
FlexRay = 85,
FlexRay2 = 86,
OP_Ethernet12 = 87,
I2C = 88,
MOST25 = 90,
MOST50 = 91,
MOST150 = 92,
Ethernet = 93,
GMFSA = 94,
TCP = 95,
HSCAN6 = 96,
HSCAN7 = 97,
LIN6 = 98,
LSFTCAN2 = 99,
LogicalDiskInfo = 187,
WiVICommand = 221,
ScriptStatus = 224,
EthPHYControl = 239,
ExtendedCommand = 240,
ExtendedData = 242,
FlexRayControl = 243,
CoreMiniPreLoad = 244,
HW_COM_Latency_Test = 512,
DeviceStatus = 513,
UDP = 514,
ForwardedMessage = 516,
I2C2 = 517,
I2C3 = 518,
I2C4 = 519,
Ethernet2 = 520,
A2B1 = 522,
A2B2 = 523,
Ethernet3 = 524,
WBMS = 532,
DWCAN9 = 534,
DWCAN10 = 535,
DWCAN11 = 536,
DWCAN12 = 537,
DWCAN13 = 538,
DWCAN14 = 539,
DWCAN15 = 540,
DWCAN16 = 541,
LIN7 = 542,
LIN8 = 543,
SPI2 = 544,
MDIO1 = 545,
MDIO2 = 546,
MDIO3 = 547,
MDIO4 = 548,
MDIO5 = 549,
MDIO6 = 550,
MDIO7 = 551,
MDIO8 = 552,
OP_Ethernet13 = 553,
OP_Ethernet14 = 554,
OP_Ethernet15 = 555,
OP_Ethernet16 = 556,
SPI3 = 557,
SPI4 = 558,
SPI5 = 559,
SPI6 = 560,
SPI7 = 561,
SPI8 = 562,
LIN9 = 563,
LIN10 = 564,
LIN11 = 565,
LIN12 = 566,
LIN13 = 567,
LIN14 = 568,
LIN15 = 569,
LIN16 = 570,
Any = 0xfffe, // Never actually set as type, but used as flag for filtering
Invalid = 0xffff
RED_App_Error = icsneoc2_netid_red_app_error,
CGI = icsneoc2_netid_cgi,
Reset_Status = icsneoc2_netid_reset_status,
FB_Status = icsneoc2_netid_fb_status,
App_Signal_Status = icsneoc2_netid_app_signal_status,
Read_Datalink_Cm_Tx_Msg = icsneoc2_netid_read_datalink_cm_tx_msg,
Read_Datalink_Cm_Rx_Msg = icsneoc2_netid_read_datalink_cm_rx_msg,
Logging_Overflow = icsneoc2_netid_logging_overflow,
ReadSettings = icsneoc2_netid_read_settings,
HSCAN4 = icsneoc2_netid_hscan4,
HSCAN5 = icsneoc2_netid_hscan5,
RS232 = icsneoc2_netid_rs232,
UART = icsneoc2_netid_uart,
UART2 = icsneoc2_netid_uart2,
UART3 = icsneoc2_netid_uart3,
UART4 = icsneoc2_netid_uart4,
SWCAN2 = icsneoc2_netid_swcan2,
Ethernet_DAQ = icsneoc2_netid_ethernet_daq,
Data_To_Host = icsneoc2_netid_data_to_host,
TextAPI_To_Host = icsneoc2_netid_textapi_to_host,
SPI1 = icsneoc2_netid_spi1,
OP_Ethernet6 = icsneoc2_netid_op_ethernet6,
Red_VBat = icsneoc2_netid_red_vbat,
OP_Ethernet7 = icsneoc2_netid_op_ethernet7,
OP_Ethernet8 = icsneoc2_netid_op_ethernet8,
OP_Ethernet9 = icsneoc2_netid_op_ethernet9,
OP_Ethernet10 = icsneoc2_netid_op_ethernet10,
OP_Ethernet11 = icsneoc2_netid_op_ethernet11,
FlexRay1a = icsneoc2_netid_flexray1a,
FlexRay1b = icsneoc2_netid_flexray1b,
FlexRay2a = icsneoc2_netid_flexray2a,
FlexRay2b = icsneoc2_netid_flexray2b,
LIN5 = icsneoc2_netid_lin5,
FlexRay = icsneoc2_netid_flexray,
FlexRay2 = icsneoc2_netid_flexray2,
OP_Ethernet12 = icsneoc2_netid_op_ethernet12,
I2C = icsneoc2_netid_i2c,
MOST25 = icsneoc2_netid_most25,
MOST50 = icsneoc2_netid_most50,
MOST150 = icsneoc2_netid_most150,
Ethernet = icsneoc2_netid_ethernet,
GMFSA = icsneoc2_netid_gmfsa,
TCP = icsneoc2_netid_tcp,
HSCAN6 = icsneoc2_netid_hscan6,
HSCAN7 = icsneoc2_netid_hscan7,
LIN6 = icsneoc2_netid_lin6,
LSFTCAN2 = icsneoc2_netid_lsftcan2,
LogicalDiskInfo = icsneoc2_netid_logical_disk_info,
WiVICommand = icsneoc2_netid_wivi_command,
ScriptStatus = icsneoc2_netid_script_status,
EthPHYControl = icsneoc2_netid_eth_phy_control,
ExtendedCommand = icsneoc2_netid_extended_command,
ExtendedData = icsneoc2_netid_extended_data,
FlexRayControl = icsneoc2_netid_flexray_control,
CoreMiniPreLoad = icsneoc2_netid_coremini_preload,
HW_COM_Latency_Test = icsneoc2_netid_hw_com_latency_test,
DeviceStatus = icsneoc2_netid_device_status,
UDP = icsneoc2_netid_udp,
ForwardedMessage = icsneoc2_netid_forwarded_message,
I2C2 = icsneoc2_netid_i2c2,
I2C3 = icsneoc2_netid_i2c3,
I2C4 = icsneoc2_netid_i2c4,
Ethernet2 = icsneoc2_netid_ethernet2,
A2B1 = icsneoc2_netid_a2b1,
A2B2 = icsneoc2_netid_a2b2,
Ethernet3 = icsneoc2_netid_ethernet3,
WBMS = icsneoc2_netid_wbms,
DWCAN9 = icsneoc2_netid_dwcan9,
DWCAN10 = icsneoc2_netid_dwcan10,
DWCAN11 = icsneoc2_netid_dwcan11,
DWCAN12 = icsneoc2_netid_dwcan12,
DWCAN13 = icsneoc2_netid_dwcan13,
DWCAN14 = icsneoc2_netid_dwcan14,
DWCAN15 = icsneoc2_netid_dwcan15,
DWCAN16 = icsneoc2_netid_dwcan16,
LIN7 = icsneoc2_netid_lin7,
LIN8 = icsneoc2_netid_lin8,
SPI2 = icsneoc2_netid_spi2,
MDIO1 = icsneoc2_netid_mdio1,
MDIO2 = icsneoc2_netid_mdio2,
MDIO3 = icsneoc2_netid_mdio3,
MDIO4 = icsneoc2_netid_mdio4,
MDIO5 = icsneoc2_netid_mdio5,
MDIO6 = icsneoc2_netid_mdio6,
MDIO7 = icsneoc2_netid_mdio7,
MDIO8 = icsneoc2_netid_mdio8,
OP_Ethernet13 = icsneoc2_netid_op_ethernet13,
OP_Ethernet14 = icsneoc2_netid_op_ethernet14,
OP_Ethernet15 = icsneoc2_netid_op_ethernet15,
OP_Ethernet16 = icsneoc2_netid_op_ethernet16,
SPI3 = icsneoc2_netid_spi3,
SPI4 = icsneoc2_netid_spi4,
SPI5 = icsneoc2_netid_spi5,
SPI6 = icsneoc2_netid_spi6,
SPI7 = icsneoc2_netid_spi7,
SPI8 = icsneoc2_netid_spi8,
LIN9 = icsneoc2_netid_lin9,
LIN10 = icsneoc2_netid_lin10,
LIN11 = icsneoc2_netid_lin11,
LIN12 = icsneoc2_netid_lin12,
LIN13 = icsneoc2_netid_lin13,
LIN14 = icsneoc2_netid_lin14,
LIN15 = icsneoc2_netid_lin15,
LIN16 = icsneoc2_netid_lin16,
Any = icsneoc2_netid_any, // Never actually set as type, but used as flag for filtering
Invalid = icsneoc2_netid_invalid
};
enum class Type : neonettype_t {
Invalid = 0,
Internal = 1, // Used for statuses that don't actually need to be transferred to the client application
CAN = 2,
LIN = 3,
FlexRay = 4,
MOST = 5,
Ethernet = 6,
LSFTCAN = 7,
SWCAN = 8,
ISO9141 = 9,
I2C = 10,
A2B = 11,
SPI = 12,
MDIO = 13,
Any = 0xFE, // Never actually set as type, but used as flag for filtering
Other = 0xFF
enum class Type : icsneoc2_msg_bus_type_t {
Invalid = icsneoc2_msg_bus_type_invalid,
Internal = icsneoc2_msg_bus_type_internal, // Used for statuses that don't actually need to be transferred to the client application
CAN = icsneoc2_msg_bus_type_can,
LIN = icsneoc2_msg_bus_type_lin,
FlexRay = icsneoc2_msg_bus_type_flexray,
MOST = icsneoc2_msg_bus_type_most,
Ethernet = icsneoc2_msg_bus_type_ethernet,
LSFTCAN = icsneoc2_msg_bus_type_lsftcan,
SWCAN = icsneoc2_msg_bus_type_swcan,
ISO9141 = icsneoc2_msg_bus_type_iso9141,
I2C = icsneoc2_msg_bus_type_i2c,
A2B = icsneoc2_msg_bus_type_a2b,
SPI = icsneoc2_msg_bus_type_spi,
MDIO = icsneoc2_msg_bus_type_mdio,
Any = icsneoc2_msg_bus_type_any, // Never actually set as type, but used as flag for filtering
Other = icsneoc2_msg_bus_type_other
};
enum class CoreMini : uint8_t {
HSCAN = 0,

View File

@ -13,6 +13,10 @@ namespace icsneo {
typedef uint16_t icscm_bitfield;
std::optional<uint8_t> CAN_DLCToLength(uint8_t length, bool fd);
std::optional<uint8_t> CAN_LengthToDLC(size_t dataLength, bool fd);
#pragma pack(push,2)
struct HardwareCANPacket {
static std::shared_ptr<Message> DecodeToMessage(const std::vector<uint8_t>& bytestream);
static bool EncodeFromMessage(const CANMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report);
@ -50,9 +54,31 @@ struct HardwareCANPacket {
uint64_t IsExtended : 1;
} timestamp;
};
struct HardwareCANErrorPacket {
uint8_t error_code;
uint8_t brs_data_error_code;
uint16_t reserved;
uint16_t DLC : 4;
uint16_t : 4;
uint16_t ERROR_INDICATOR : 1;
uint16_t : 7;
uint8_t flags;
uint8_t REC;
uint8_t TEC;
static bool GetErrorWarn(uint8_t flags) { return flags & 0b0000'0001; }
static bool GetErrorPassive(uint8_t flags) { return flags & 0b0000'1000; }
static bool GetBusOff(uint8_t flags) { return flags & 0b0010'0000; }
};
#pragma pack(pop)
}
#endif // __cplusplus
#endif
#endif

View File

@ -10,6 +10,8 @@
namespace icsneo {
#pragma pack(push, 2)
typedef uint16_t icscm_bitfield;
struct HardwareEthernetPacket {
@ -42,6 +44,8 @@ struct HardwareEthernetPacket {
uint16_t Length;
};
#pragma pack(pop)
}
#endif // __cplusplus

View File

@ -50,6 +50,7 @@
#include "icsneo/disk/vsa/vsa.h"
#include "icsneo/disk/vsa/vsaparser.h"
#include "icsneo/communication/message/versionmessage.h"
#include "icsneo/communication/message/gptpstatusmessage.h"
#define ICSNEO_FINDABLE_DEVICE_BASE(className, type) \
@ -212,8 +213,8 @@ public:
int addMessageCallback(const std::shared_ptr<MessageCallback>& cb) { return com->addMessageCallback(cb); }
bool removeMessageCallback(int id) { return com->removeMessageCallback(id); }
bool transmit(std::shared_ptr<Frame> frame);
bool transmit(std::vector<std::shared_ptr<Frame>> frames);
bool transmit(std::shared_ptr<BusMessage> frame);
bool transmit(std::vector<std::shared_ptr<BusMessage>> frames);
void setWriteBlocks(bool blocks);
@ -727,12 +728,15 @@ public:
virtual bool supportsComponentVersions() const { return false; }
virtual bool supportsTC10() const { return false; }
virtual bool supportsGPTP() const { return false; }
bool requestTC10Wake(Network::NetID network);
bool requestTC10Sleep(Network::NetID network);
std::optional<TC10StatusMessage> getTC10Status(Network::NetID network);
std::optional<GPTPStatus> getGPTPStatus(std::chrono::milliseconds timeout = std::chrono::milliseconds(100));
protected:
bool online = false;
@ -835,7 +839,7 @@ protected:
void handleInternalMessage(std::shared_ptr<Message> message);
virtual void handleDeviceStatus(const std::shared_ptr<RawMessage>&) {}
virtual void handleDeviceStatus(const std::shared_ptr<InternalMessage>&) {}
neodevice_t& getWritableNeoDevice() { return data; }

View File

@ -14,7 +14,9 @@ typedef uint32_t devicetype_t;
#include <ostream>
#include <cstdint>
typedef uint32_t devicetype_t;
#include <icsneo/icsneoc2types.h>
typedef icsneoc2_devicetype_t devicetype_t;
namespace icsneo {
@ -22,65 +24,65 @@ class DeviceType {
public:
// This enum used to be a bitfield, but has since become an enum as we have more than 32 devices
// Adding something? Make sure you update the type string and C-compatible defines below!
enum Enum : devicetype_t {
Unknown = (0x00000000),
BLUE = (0x00000001),
ECU_AVB = (0x00000002),
RADSupermoon = (0x00000003),
DW_VCAN = (0x00000004),
RADMoon2 = (0x00000005),
RADMars = (0x00000006),
VCAN4_1 = (0x00000007),
FIRE = (0x00000008),
RADPluto = (0x00000009),
VCAN4_2EL = (0x0000000a),
RADIO_CANHUB = (0x0000000b),
NEOECU12 = (0x0000000c),
OBD2_LCBADGE = (0x0000000d),
RADMoonDuo = (0x0000000e),
FIRE3 = (0x0000000f),
VCAN3 = (0x00000010),
RADJupiter = (0x00000011),
VCAN4_IND = (0x00000012),
RADGigastar = (0x00000013),
RED2 = (0x00000014),
EtherBADGE = (0x00000016),
RAD_A2B = (0x00000017),
RADEpsilon = (0x00000018),
RADGalaxy2 = (0x00000021),
RADMoon3 = (0x00000023),
RADComet = (0x00000024),
FIRE3_FlexRay = (0x00000025),
Connect = (0x00000026),
RADComet3 = (0x00000027),
RADMoonT1S = (0x00000028),
RADGigastar2 = (0x00000029),
RED = (0x00000040),
ECU = (0x00000080),
IEVB = (0x00000100),
Pendant = (0x00000200),
OBD2_PRO = (0x00000400),
ECUChip_UART = (0x00000800),
PLASMA = (0x00001000),
DONT_REUSE0 = (0x00002000), // Previously FIRE_VNET
NEOAnalog = (0x00004000),
CT_OBD = (0x00008000),
DONT_REUSE1 = (0x00010000), // Previously PLASMA_1_12
DONT_REUSE2 = (0x00020000), // Previously PLASMA_1_13
ION = (0x00040000),
RADStar = (0x00080000),
DONT_REUSE3 = (0x00100000), // Previously ION3
VCAN4_4 = (0x00200000),
VCAN4_2 = (0x00400000),
CMProbe = (0x00800000),
EEVB = (0x01000000),
VCANrf = (0x02000000),
FIRE2 = (0x04000000),
Flex = (0x08000000),
RADGalaxy = (0x10000000),
RADStar2 = (0x20000000),
VividCAN = (0x40000000),
OBD2_SIM = (0x80000000)
enum Enum : icsneoc2_devicetype_t {
Unknown = icsneoc2_devicetype_unknown,
BLUE = icsneoc2_devicetype_blue,
ECU_AVB = icsneoc2_devicetype_ecu_avb,
RADSupermoon = icsneoc2_devicetype_rad_supermoon,
DW_VCAN = icsneoc2_devicetype_dw_vcan,
RADMoon2 = icsneoc2_devicetype_rad_moon2,
RADMars = icsneoc2_devicetype_rad_mars,
VCAN4_1 = icsneoc2_devicetype_vcan41,
FIRE = icsneoc2_devicetype_fire,
RADPluto = icsneoc2_devicetype_rad_pluto,
VCAN4_2EL = icsneoc2_devicetype_vcan42_el,
RADIO_CANHUB = icsneoc2_devicetype_radio_canhub,
NEOECU12 = icsneoc2_devicetype_neo_ecu12,
OBD2_LCBADGE = icsneoc2_devicetype_obd2_lc_badge,
RADMoonDuo = icsneoc2_devicetype_rad_moon_duo,
FIRE3 = icsneoc2_devicetype_fire3,
VCAN3 = icsneoc2_devicetype_vcan3,
RADJupiter = icsneoc2_devicetype_rad_jupiter,
VCAN4_IND = icsneoc2_devicetype_vcan4_industrial,
RADGigastar = icsneoc2_devicetype_rad_gigastar,
RED2 = icsneoc2_devicetype_red2,
EtherBADGE = icsneoc2_devicetype_etherbadge,
RAD_A2B = icsneoc2_devicetype_rad_a2b,
RADEpsilon = icsneoc2_devicetype_rad_epsilon,
RADGalaxy2 = icsneoc2_devicetype_rad_galaxy2,
RADMoon3 = icsneoc2_devicetype_rad_moon3,
RADComet = icsneoc2_devicetype_rad_comet,
FIRE3_FlexRay = icsneoc2_devicetype_fire3_flexray,
Connect = icsneoc2_devicetype_connect,
RADComet3 = icsneoc2_devicetype_rad_comet3,
RADMoonT1S = icsneoc2_devicetype_rad_moon_t1s,
RADGigastar2 = icsneoc2_devicetype_rad_gigastar2,
RED = icsneoc2_devicetype_red,
ECU = icsneoc2_devicetype_ecu,
IEVB = icsneoc2_devicetype_ievb,
Pendant = icsneoc2_devicetype_pendant,
OBD2_PRO = icsneoc2_devicetype_obd2_pro,
ECUChip_UART = icsneoc2_devicetype_ecuchip_uart,
PLASMA = icsneoc2_devicetype_plasma,
//DONT_REUSE0 = , // Previously FIRE_VNET
NEOAnalog = icsneoc2_devicetype_neo_analog,
CT_OBD = icsneoc2_devicetype_ct_obd,
//DONT_REUSE1 = (0x00010000), // Previously PLASMA_1_12
//DONT_REUSE2 = (0x00020000), // Previously PLASMA_1_13
ION = icsneoc2_devicetype_ion,
RADStar = icsneoc2_devicetype_rad_star,
//DONT_REUSE3 = (0x00100000), // Previously ION3
VCAN4_4 = icsneoc2_devicetype_vcan44,
VCAN4_2 = icsneoc2_devicetype_vcan42,
CMProbe = icsneoc2_devicetype_cm_probe,
EEVB = icsneoc2_devicetype_eevb,
VCANrf = icsneoc2_devicetype_vcan_rf,
FIRE2 = icsneoc2_devicetype_fire2,
Flex = icsneoc2_devicetype_flex,
RADGalaxy = icsneoc2_devicetype_rad_galaxy,
RADStar2 = icsneoc2_devicetype_rad_star2,
VividCAN = icsneoc2_devicetype_vividcan,
OBD2_SIM = icsneoc2_devicetype_obd2_sim
};
/**
@ -90,9 +92,10 @@ public:
* as the product name may change based on device-specific factors, such as serial
* number.
*/
static const char* GetGenericProductName(DeviceType::Enum type) {
template<typename T>
static std::string GetGenericProductName(T deviceType) {
// Adding something? Make sure you update DEVICE_TYPE_LONGEST_NAME at the top!
switch(type) {
switch(static_cast<icsneoc2_devicetype_t>(deviceType)) {
case Unknown:
return "Unknown";
case BLUE:
@ -201,12 +204,7 @@ public:
return "neoVI Connect";
case RADGigastar2:
return "RAD-Gigastar 2";
case DONT_REUSE0:
case DONT_REUSE1:
case DONT_REUSE2:
case DONT_REUSE3:
// Intentionally don't use default so that the compiler throws a warning when something is added
return "Unknown neoVI";
// Intentionally don't use default so that the compiler throws a warning when something is added
}
return "Unknown neoVI";
}
@ -214,8 +212,8 @@ public:
DeviceType() { value = DeviceType::Enum::Unknown; }
DeviceType(devicetype_t netid) { value = (DeviceType::Enum)netid; }
DeviceType(DeviceType::Enum netid) { value = netid; }
DeviceType::Enum getDeviceType() const { return value; }
std::string getGenericProductName() const { return GetGenericProductName(getDeviceType()); }
icsneoc2_devicetype_t getDeviceType() const { return value; }
std::string getGenericProductName() const { return DeviceType::GetGenericProductName(getDeviceType()); }
operator devicetype_t() const { return getDeviceType(); }
private:

View File

@ -33,7 +33,7 @@ public:
virtual void handleMessage(const std::shared_ptr<Message>&) {}
// Return true to continue transmitting, success should be written to if false is returned
virtual bool transmitHook(const std::shared_ptr<Frame>& frame, bool& success) { (void)frame; (void)success; return true; }
virtual bool transmitHook(const std::shared_ptr<BusMessage>& frame, bool& success) { (void)frame; (void)success; return true; }
protected:
Device& device;

View File

@ -25,7 +25,7 @@ public:
void onGoOffline() override;
void handleMessage(const std::shared_ptr<Message>& message) override;
bool transmitHook(const std::shared_ptr<Frame>& frame, bool& success) override;
bool transmitHook(const std::shared_ptr<BusMessage>& frame, bool& success) override;
std::shared_ptr<Controller> getController(uint8_t index) const {
if(index >= controllers.size())

View File

@ -444,20 +444,21 @@ typedef struct LOGGER_SETTINGS_t
#define RAD_GPTP_NUM_PORTS 1 // 1 because only supported as gPTP endpoint
typedef struct RAD_GPTP_SETTINGS_t
{
uint32_t neighborPropDelayThresh;//ns
uint32_t sys_phc_sync_interval;//ns
int8_t logPDelayReqInterval;// log2ms
int8_t logSyncInterval;// log2ms
int8_t logAnnounceInterval;// log2ms
uint32_t neighborPropDelayThresh; //ns
uint32_t sys_phc_sync_interval; //ns
int8_t logPDelayReqInterval; // log2ms
int8_t logSyncInterval; // log2ms
int8_t logAnnounceInterval; // log2ms
uint8_t profile;
uint8_t priority1;
uint8_t clockclass;
uint8_t clockaccuracy;
uint8_t priority2;
uint16_t offset_scaled_log_variance;
uint8_t gPTPportRole[RAD_GPTP_NUM_PORTS];
uint8_t portEnable[RAD_GPTP_NUM_PORTS];
uint8_t rsvd[16];
uint8_t gptpPortRole;
uint8_t gptpEnabledPort;
uint8_t enableClockSyntonization;
uint8_t rsvd[15];
} RAD_GPTP_SETTINGS;//36 Bytes with RAD_GPTP_NUM_PORTS = 1
#define RAD_GPTP_SETTINGS_SIZE 36

View File

@ -68,6 +68,10 @@ protected:
std::optional<MemoryAddress> getCoreminiStartAddressSD() const override {
return 0;
}
bool supportsComponentVersions() const override { return true; }
bool supportsGPTP() const override { return true; }
};
}

View File

@ -121,7 +121,7 @@ protected:
// The supported TX networks are the same as the supported RX networks for this device
void setupSupportedTXNetworks(std::vector<Network>& txNetworks) override { setupSupportedRXNetworks(txNetworks); }
void handleDeviceStatus(const std::shared_ptr<RawMessage>& message) override {
void handleDeviceStatus(const std::shared_ptr<InternalMessage>& message) override {
if(message->data.size() < sizeof(neovifire2_status_t))
return;
std::lock_guard<std::mutex> lk(ioMutex);

View File

@ -79,6 +79,8 @@ protected:
bool supportsLiveData() const override { return true; }
bool supportsGPTP() const override { return true; }
std::optional<MemoryAddress> getCoreminiStartAddressFlash() const override {
return 33*1024*1024;
}
@ -90,6 +92,7 @@ protected:
bool supportsEraseMemory() const override {
return true;
}
};
}

Some files were not shown because too many files have changed in this diff Show More