24 Commits
Author SHA1 Message Date
Kyle Schwarz 6926ca8199 CI: Update Linux images 2025-08-25 11:29:46 -04:00
Kyle Schwarz 29dc7b345f Driver: Switch to libredxx
- no more libFTDI
- no more libusb on Linux and macOS
- no more FTDI repack
- no more binary libs
- faster D2XX on Windows (no longer uses COM)
2025-08-25 11:24:03 -04:00
Kyle Schwarz 17285389e3 Tests: Switch to FetchContent 2025-08-21 20:19:22 -04:00
Bryant JonesandKyle Schwarz 328563b7e6 Docs: Add TC10 .py example 2025-08-12 12:36:16 -04:00
Kyle Schwarz 6b60804174 Device: Add com keepalive 2025-08-06 17:17:36 -04:00
Jonathan SchwartzandKyle Schwarz c5bce795c6 Device: Add MessageFilter to enableMessagePolling() 2025-07-29 13:15:18 -04:00
Yasser YassineandKyle Schwarz 1e5e714f5b Device: Add disk formatting 2025-07-22 20:02:06 +00:00
Kyle Schwarz d70defbf8a All: Address compiler warnings 2025-07-17 15:47:23 -04:00
Thomas StoddardandKyle Schwarz 10efacf91e Communication: EthernetMessage: Switch to TX_WRAP 2025-07-17 01:49:31 +00:00
Kyle Schwarz 6d985ca873 Communication: Fix neotc10sleepstatus_t 2025-07-16 11:54:27 -04:00
Nicholas Zamora 19df557b4a DeviceAppVersion: Add operator== 2025-07-09 20:48:22 +00:00
Kyle Schwarz 7afa41bf2a API: C: Add TC10 2025-07-03 09:30:29 -04:00
Kyle Schwarz c056e8dc2e Driver: Servd: Toggle closing when done 2025-07-02 14:28:59 -04:00
Yasser YassineandKyle Schwarz d5ae1cdb03 Device: RED2: Update settings 2025-07-02 18:10:51 +00:00
Kyle Schwarz 5795791eac Driver: Servd: Fix Address creation 2025-06-19 16:48:15 -04:00
Ben KleinhekselandKyle Schwarz c91db6355c Device: Add setValueLiveData()
* Adds the ability to set a CoreMini signal value via the live data interface
* Adds definition for the Manual Trigger and DAQ Enable signals
2025-06-18 21:51:32 +00:00
Kyle Schwarz f37b88d616 Communication: Fix ScriptStatus sizes 2025-06-12 11:17:59 -04:00
Yasser YassineandKyle Schwarz 33dea748f7 Device: Add supportsCoreminiScript() 2025-06-06 19:29:42 +00:00
Kyle Schwarz 245073f9d5 1.0.0 2025-06-05 21:10:54 -04:00
Kyle Schwarz c4e858d346 Driver: Prefer Npcap
Prefer Npcap over WinPcap and TCP
2025-06-04 22:14:09 -04:00
Keith NashandKyle Schwarz b3d47f2ae5 Communication: Add LogDataMessage 2025-05-16 19:36:27 +00:00
Kyle Schwarz b94ade1ef6 FIRE3: Add Ethernet Activation Lines 2025-05-15 11:11:26 -04:00
Bryant Jones d9ea5e085e Device: Gigastar2: Update networks 2025-05-09 11:51:26 -04:00
Bryant JonesandKyle Schwarz 5125520e76 Device: Gigastar2: Update networks 2025-05-09 15:30:20 +00:00
416 changed files with 1899 additions and 129451 deletions
+1
View File
@@ -15,3 +15,4 @@ third-party/concurrentqueue/tests
examples/csharp/bin
examples/csharp/obj
test/system
.env
+64 -88
View File
@@ -67,7 +67,7 @@ unit_test windows/x86:
script:
- apt update -y
- apt upgrade -y
- apt install -y g++ ninja-build cmake libusb-1.0-0-dev libpcap-dev git
- apt install -y g++ ninja-build cmake libpcap-dev git
- sh ci/build-posix.sh
artifacts:
when: always
@@ -82,7 +82,7 @@ unit_test windows/x86:
script:
- apt update -y
- apt upgrade -y
- apt install -y libusb-1.0-0-dev libpcap-dev
- apt install -y libpcap-dev
- build/libicsneo-unit-tests
tags:
- linux-build
@@ -93,7 +93,7 @@ unit_test windows/x86:
script:
- apt update -y
- apt upgrade -y
- apt install -y clang lld ninja-build cmake libusb-1.0-0-dev libpcap-dev git
- apt install -y clang lld ninja-build cmake libpcap-dev git
- CC=clang CXX=clang++ LDFLAGS=-fuse-ld=lld sh ci/build-posix.sh
artifacts:
when: always
@@ -108,36 +108,12 @@ unit_test windows/x86:
script:
- apt update -y
- apt upgrade -y
- apt install -y libusb-1.0-0-dev libpcap-dev
- apt install -y libpcap-dev
- build/libicsneo-unit-tests
tags:
- linux-build
timeout: 5m
build linux/ubuntu/2004/amd64/gcc:
<<: *build_linux_ubuntu_gcc
image: ubuntu:20.04
unit_test linux/ubuntu/2004/amd64/gcc:
<<: *test_linux_ubuntu_gcc
image: ubuntu:20.04
dependencies:
- build linux/ubuntu/2004/amd64/gcc
needs:
- build linux/ubuntu/2004/amd64/gcc
build linux/ubuntu/2004/amd64/clang:
<<: *build_linux_ubuntu_clang
image: ubuntu:20.04
unit_test linux/ubuntu/2004/amd64/clang:
<<: *test_linux_ubuntu_clang
image: ubuntu:20.04
dependencies:
- build linux/ubuntu/2004/amd64/clang
needs:
- build linux/ubuntu/2004/amd64/clang
build linux/ubuntu/2204/amd64/gcc:
<<: *build_linux_ubuntu_gcc
image: ubuntu:22.04
@@ -162,6 +138,30 @@ unit_test linux/ubuntu/2204/amd64/clang:
needs:
- build linux/ubuntu/2204/amd64/clang
build linux/ubuntu/2404/amd64/gcc:
<<: *build_linux_ubuntu_gcc
image: ubuntu:24.04
unit_test linux/ubuntu/2404/amd64/gcc:
<<: *test_linux_ubuntu_gcc
image: ubuntu:24.04
dependencies:
- build linux/ubuntu/2404/amd64/gcc
needs:
- build linux/ubuntu/2404/amd64/gcc
build linux/ubuntu/2404/amd64/clang:
<<: *build_linux_ubuntu_clang
image: ubuntu:24.04
unit_test linux/ubuntu/2404/amd64/clang:
<<: *test_linux_ubuntu_clang
image: ubuntu:24.04
dependencies:
- build linux/ubuntu/2404/amd64/clang
needs:
- build linux/ubuntu/2404/amd64/clang
#-------------------------------------------------------------------------------
# Fedora
#-------------------------------------------------------------------------------
@@ -175,7 +175,7 @@ unit_test linux/ubuntu/2204/amd64/clang:
- echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf
- echo fastestmirror=True >>/etc/dnf/dnf.conf
- dnf upgrade -y
- dnf install -y g++ libpcap-devel cmake ninja-build libusb1-devel git
- dnf install -y g++ libpcap-devel cmake ninja-build git
- sh ci/build-posix.sh
artifacts:
when: always
@@ -194,7 +194,7 @@ unit_test linux/ubuntu/2204/amd64/clang:
- echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf
- echo fastestmirror=True >>/etc/dnf/dnf.conf
- dnf upgrade -y
- dnf install -y libpcap-devel libusb1-devel
- dnf install -y libpcap-devel
- build/libicsneo-unit-tests
tags:
- linux-build
@@ -209,7 +209,7 @@ unit_test linux/ubuntu/2204/amd64/clang:
- echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf
- echo fastestmirror=True >>/etc/dnf/dnf.conf
- dnf upgrade -y
- dnf install -y clang lld libpcap-devel cmake ninja-build libusb1-devel git
- dnf install -y clang lld libpcap-devel cmake ninja-build git
- CC=clang CXX=clang++ LDFLAGS=-fuse-ld=lld sh ci/build-posix.sh
artifacts:
when: always
@@ -228,60 +228,12 @@ unit_test linux/ubuntu/2204/amd64/clang:
- echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf
- echo fastestmirror=True >>/etc/dnf/dnf.conf
- dnf upgrade -y
- dnf install -y libpcap-devel libusb1-devel
- dnf install -y libpcap-devel
- build/libicsneo-unit-tests
tags:
- linux-build
timeout: 5m
build linux/fedora/39/amd64/gcc:
<<: *build_linux_fedora_gcc
image: fedora:39
unit_test linux/fedora/39/amd64/gcc:
<<: *test_linux_fedora_gcc
image: fedora:39
dependencies:
- build linux/fedora/39/amd64/gcc
needs:
- build linux/fedora/39/amd64/gcc
build linux/fedora/39/amd64/clang:
<<: *build_linux_fedora_clang
image: fedora:39
unit_test linux/fedora/39/amd64/clang:
<<: *test_linux_fedora_clang
image: fedora:39
dependencies:
- build linux/fedora/39/amd64/clang
needs:
- build linux/fedora/39/amd64/clang
build linux/fedora/40/amd64/gcc:
<<: *build_linux_fedora_gcc
image: fedora:40
unit_test linux/fedora/40/amd64/gcc:
<<: *test_linux_fedora_gcc
image: fedora:40
dependencies:
- build linux/fedora/40/amd64/gcc
needs:
- build linux/fedora/40/amd64/gcc
build linux/fedora/40/amd64/clang:
<<: *build_linux_fedora_clang
image: fedora:40
unit_test linux/fedora/40/amd64/clang:
<<: *test_linux_fedora_clang
image: fedora:40
dependencies:
- build linux/fedora/40/amd64/clang
needs:
- build linux/fedora/40/amd64/clang
build linux/fedora/41/amd64/gcc:
<<: *build_linux_fedora_gcc
image: fedora:41
@@ -306,6 +258,30 @@ unit_test linux/fedora/41/amd64/clang:
needs:
- build linux/fedora/41/amd64/clang
build linux/fedora/42/amd64/gcc:
<<: *build_linux_fedora_gcc
image: fedora:42
unit_test linux/fedora/42/amd64/gcc:
<<: *test_linux_fedora_gcc
image: fedora:42
dependencies:
- build linux/fedora/42/amd64/gcc
needs:
- build linux/fedora/42/amd64/gcc
build linux/fedora/42/amd64/clang:
<<: *build_linux_fedora_clang
image: fedora:42
unit_test linux/fedora/42/amd64/clang:
<<: *test_linux_fedora_clang
image: fedora:42
dependencies:
- build linux/fedora/42/amd64/clang
needs:
- build linux/fedora/42/amd64/clang
#-------------------------------------------------------------------------------
# Python Module
#-------------------------------------------------------------------------------
@@ -314,19 +290,19 @@ build python/linux/amd64:
stage: build
tags:
- linux-build
image: python:3.12
image: python:3.13
services:
- name: docker:dind
entrypoint: ["env", "-u", "DOCKER_HOST"]
command: ["dockerd-entrypoint.sh"]
variables:
CIBW_BEFORE_ALL: yum install -y flex && sh ci/bootstrap-libpcap.sh && sh ci/bootstrap-libusb.sh
CIBW_BEFORE_ALL: yum install -y flex && sh ci/bootstrap-libpcap.sh
CIBW_BUILD: "*manylinux*" # no musl
CIBW_ARCHS: x86_64
DOCKER_HOST: unix:///var/run/docker.sock
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: ""
CIBW_ENVIRONMENT: CMAKE_PREFIX_PATH=/project/libpcap/install:/project/libusb/install
CIBW_ENVIRONMENT: CMAKE_PREFIX_PATH=/project/libpcap/install
script:
- curl -sSL https://get.docker.com/ | sh
- sh ci/build-wheel-posix.sh
@@ -339,10 +315,10 @@ build python/linux/arm64:
tags:
- arm64-linux-build
variables:
CIBW_BEFORE_ALL: yum install -y flex && sh ci/bootstrap-libpcap.sh && sh ci/bootstrap-libusb.sh
CIBW_BEFORE_ALL: yum install -y flex && sh ci/bootstrap-libpcap.sh
CIBW_BUILD: "*manylinux*" # no musl
CIBW_ARCHS: aarch64
CIBW_ENVIRONMENT: CMAKE_PREFIX_PATH=/project/libpcap/install:/project/libusb/install
CIBW_ENVIRONMENT: CMAKE_PREFIX_PATH=/project/libpcap/install
script:
- sh ci/build-wheel-posix.sh
artifacts:
@@ -354,9 +330,9 @@ build python/macos:
tags:
- macos-arm64
variables:
CIBW_BEFORE_ALL: sh ci/bootstrap-libpcap.sh && sh ci/bootstrap-libusb.sh
CIBW_BEFORE_ALL: sh ci/bootstrap-libpcap.sh
CIBW_ARCHS: arm64
CIBW_ENVIRONMENT: CMAKE_PREFIX_PATH=$CI_PROJECT_DIR/libpcap/install:$CI_PROJECT_DIR/libusb/install
CIBW_ENVIRONMENT: CMAKE_PREFIX_PATH=$CI_PROJECT_DIR/libpcap/install
MACOSX_DEPLOYMENT_TARGET: 10.14
script:
- sh ci/build-wheel-posix.sh
@@ -384,7 +360,7 @@ deploy python/pypi:
TWINE_PASSWORD: $PYPI_TOKEN
tags:
- linux-build
image: python:3.12
image: python:3.13
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
script:
+27 -93
View File
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.16)
project(libicsneo VERSION 0.3.0)
project(libicsneo VERSION 1.0.0)
cmake_policy(SET CMP0074 NEW)
if(POLICY CMP0135)
@@ -22,9 +22,8 @@ set(LIBICSNEO_NPCAP_INCLUDE_DIR "" CACHE STRING "Npcap include directory; set to
option(LIBICSNEO_ENABLE_FIRMIO "Enable communication between Linux and CoreMini within the same device" OFF)
option(LIBICSNEO_ENABLE_RAW_ETHERNET "Enable devices which communicate over raw ethernet" ON)
option(LIBICSNEO_ENABLE_CDCACM "Enable devices which communicate over USB CDC ACM" ON)
option(LIBICSNEO_ENABLE_FTDI "Enable devices which communicate over USB FTDI2XX" ON)
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_DXX "Enable devices which communicate over D2XX/D3XX via libredxx" ON)
option(LIBICSNEO_ENABLE_BINDINGS_PYTHON "Enable Python library" OFF)
@@ -109,7 +108,6 @@ if(LIBICSNEO_BUILD_DOCS)
endif()
if(WIN32)
add_definitions(-DWIN32_LEAN_AND_MEAN -DNOMINMAX -D_CRT_SECURE_NO_WARNINGS)
set(PLATFORM_SRC
platform/windows/strings.cpp
platform/windows/registry.cpp
@@ -122,9 +120,9 @@ if(WIN32)
)
endif()
if(LIBICSNEO_ENABLE_CDCACM OR LIBICSNEO_ENABLE_FTDI)
if(LIBICSNEO_ENABLE_CDCACM)
list(APPEND PLATFORM_SRC
platform/windows/vcp.cpp
platform/windows/cdcacm.cpp
)
endif()
else() # Darwin or Linux
@@ -142,12 +140,6 @@ else() # Darwin or Linux
)
endif()
if(LIBICSNEO_ENABLE_FTDI)
list(APPEND PLATFORM_SRC
platform/posix/ftdi.cpp
)
endif()
if(LIBICSNEO_ENABLE_CDCACM)
list(APPEND PLATFORM_SRC
platform/posix/cdcacm.cpp
@@ -171,51 +163,9 @@ else() # Darwin or Linux
endif()
endif()
if(LIBICSNEO_ENABLE_FTD3XX)
if(NOT FTD3XX_ROOT) # allow system override
include(FetchContent)
if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8)
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/24.34.0/libftd3xx-1.3.0.10-win-x64.zip")
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=459e635496ab47d6069c9d3515fdd6d82cba3d95e7ae34f794d66ffdf336e9d1")
elseif(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 4)
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/24.34.0/libftd3xx-1.3.0.10-win-i686.zip")
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=ce4259ae11772d6ede7d217172156fa392f329b29d9455131f4126a2fb89dad1")
elseif(APPLE AND CMAKE_SIZEOF_VOID_P EQUAL 8)
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/24.34.0/libftd3xx-1.0.16-macos-universal2.zip")
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=0904ac5eda8e1dc4b5aac3714383bcc7792b42dfeb585dce6cbfb8b67b8c0c51")
elseif(UNIX)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64")
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/24.34.0/libftd3xx-1.0.16-linux-x64.zip")
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=cf66bf299fc722f050cdd3c36998a670f1df69f7c0df18afa73707277067114b")
endif()
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm.*|aarch64")
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/24.34.0/libftd3xx-1.0.16-linux-aarch64.zip")
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=66341b5112b9841e959e81400b51711be96fec91894477c5cbfc29b10a0c00a6")
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/24.34.0/libftd3xx-1.0.16-linux-armhf.zip")
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=cec1f959b48a11eb6b829ed43c81b6ba1c0bcf3e797bafcc84a6376e5ffc3c47")
endif()
endif()
endif()
if(NOT LIBICSNEO_FTD3XX_URL)
message(FATAL_ERROR "Unsupported platform for FTD3XX driver")
endif()
FetchContent_Declare(
ftdi3xx
URL ${LIBICSNEO_FTD3XX_URL}
URL_HASH ${LIBICSNEO_FTD3XX_URL_HASH}
)
FetchContent_GetProperties(ftdi3xx)
if(NOT ftdi3xx_POPULATED)
FetchContent_Populate(ftdi3xx)
endif()
set(FTD3XX_ROOT "${ftdi3xx_SOURCE_DIR}")
endif()
find_package(FTD3XX REQUIRED)
if(LIBICSNEO_ENABLE_DXX)
list(APPEND PLATFORM_SRC
platform/ftd3xx.cpp
platform/dxx.cpp
)
endif()
@@ -246,6 +196,7 @@ set(SRC_FILES
communication/message/ethphymessage.cpp
communication/message/linmessage.cpp
communication/message/livedatamessage.cpp
communication/message/logdatamessage.cpp
communication/message/tc10statusmessage.cpp
communication/message/gptpstatusmessage.cpp
communication/message/ethernetstatusmessage.cpp
@@ -291,6 +242,7 @@ set(SRC_FILES
disk/plasiondiskreaddriver.cpp
disk/extextractordiskreaddriver.cpp
disk/fat.cpp
disk/diskdetails.cpp
disk/vsa/vsa.cpp
disk/vsa/vsa02.cpp
disk/vsa/vsa03.cpp
@@ -382,12 +334,9 @@ endif()
if(LIBICSNEO_ENABLE_CDCACM)
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_CDCACM)
endif()
if(LIBICSNEO_ENABLE_FTDI)
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_FTDI)
endif()
if(LIBICSNEO_ENABLE_FTD3XX)
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_FTD3XX)
target_link_libraries(icsneocpp PRIVATE FTD3XX::FTD3XX)
if(LIBICSNEO_ENABLE_DXX)
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_DXX)
target_link_libraries(icsneocpp PRIVATE libredxx::libredxx)
endif()
if(LIBICSNEO_ENABLE_TCP)
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_TCP)
@@ -401,25 +350,15 @@ add_subdirectory(third-party/fatfs)
set_property(TARGET fatfs PROPERTY POSITION_INDEPENDENT_CODE ON)
target_link_libraries(icsneocpp PRIVATE fatfs)
# libftdi
if(LIBICSNEO_ENABLE_FTDI)
if(NOT WIN32)
target_include_directories(icsneocpp PUBLIC third-party/libftdi/src)
set(LIBFTDI_DOCUMENTATION OFF CACHE INTERNAL "")
set(LIBFTDI_BUILD_TESTS OFF CACHE INTERNAL "")
set(LIBFTDI_INSTALL OFF CACHE INTERNAL "")
set(LIBFTDI_PYTHON_BINDINGS OFF CACHE INTERNAL "")
set(LIBFTDI_LINK_PYTHON_LIBRARY OFF CACHE INTERNAL "")
set(FTDIPP OFF CACHE INTERNAL "")
set(FTDI_EEPROM OFF CACHE INTERNAL "")
add_subdirectory(third-party/libftdi)
target_include_directories(icsneocpp PRIVATE ${LIBUSB_INCLUDE_DIR})
set_property(TARGET ftdi1-static PROPERTY POSITION_INDEPENDENT_CODE ON)
target_link_libraries(icsneocpp PUBLIC ftdi1-static)
target_link_libraries(icsneocpp PUBLIC ${CMAKE_THREAD_LIBS_INIT})
endif(NOT WIN32)
endif(LIBICSNEO_ENABLE_FTDI)
# dxx
if(LIBICSNEO_ENABLE_DXX)
include(FetchContent)
FetchContent_Declare(libredxx
GIT_REPOSITORY https://github.com/Zeranoe/libredxx.git
GIT_TAG 2b7932fe7f2fc006ef269c7a72595f3940178a10
)
FetchContent_MakeAvailable(libredxx)
endif()
# pcap
if(LIBICSNEO_ENABLE_RAW_ETHERNET)
@@ -429,7 +368,6 @@ if(LIBICSNEO_ENABLE_RAW_ETHERNET)
add_definitions(-DWPCAP -DHAVE_REMOTE)
else()
target_include_directories(icsneocpp PUBLIC AFTER ${LIBICSNEO_NPCAP_INCLUDE_DIR})
add_definitions(-DNPCAP)
endif()
else()
find_package(PCAP REQUIRED)
@@ -506,17 +444,12 @@ add_subdirectory(bindings)
# googletest
if(LIBICSNEO_BUILD_UNIT_TESTS)
if(WIN32)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
endif()
if (NOT TARGET gtest)
add_subdirectory(third-party/googletest-master)
endif()
if (CMAKE_VERSION VERSION_LESS 2.8.11)
include_directories("${gtest_SOURCE_DIR}/include")
endif()
include(FetchContent)
FetchContent_Declare(googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG 6986c2b575f77135401a4e1c65a7a42f20e18fef
)
FetchContent_MakeAvailable(googletest)
add_executable(libicsneo-unit-tests
test/unit/main.cpp
@@ -532,6 +465,7 @@ if(LIBICSNEO_BUILD_UNIT_TESTS)
test/unit/ringbuffertest.cpp
test/unit/apperrordecodertest.cpp
test/unit/windowsstrings.cpp
test/unit/periodictest.cpp
)
target_link_libraries(libicsneo-unit-tests gtest gtest_main)
+30
View File
@@ -757,3 +757,33 @@ bool icsneo_isOnlineSupported(const neodevice_t* device) {
return device->device->isOnlineSupported();
}
bool icsneo_requestTC10Wake(const neodevice_t* device, neonetid_t netid) {
if(!icsneo_isValidNeoDevice(device))
return false;
return device->device->requestTC10Wake((Network::NetID)netid);
}
bool icsneo_requestTC10Sleep(const neodevice_t* device, neonetid_t netid) {
if(!icsneo_isValidNeoDevice(device))
return false;
return device->device->requestTC10Sleep((Network::NetID)netid);
}
bool icsneo_getTC10Status(const neodevice_t* device, neonetid_t netid, neotc10status_t* status) {
if(!icsneo_isValidNeoDevice(device))
return false;
const auto statusMsg = device->device->getTC10Status((Network::NetID)netid);
if(!statusMsg)
return false;
status->wakeStatus = (neotc10wakestatus_t)statusMsg->wakeStatus;
status->sleepStatus = (neotc10sleepstatus_t)statusMsg->sleepStatus;
return true;
}
+39 -106
View File
@@ -28,7 +28,7 @@ std::string APIEvent::describe() const noexcept {
ss << *device; // Makes use of device.describe()
else
ss << "API";
Severity severity = getSeverity();
if(severity == Severity::EventInfo) {
ss << " Info: ";
@@ -74,6 +74,9 @@ static constexpr const char* TIMEOUT = "The timeout was reached.";
static constexpr const char* WIVI_NOT_SUPPORTED = "Wireless neoVI functions are not supported on this device.";
static constexpr const char* RESTRICTED_ENTRY_FLAG = "Attempted to set a restricted flag in a Root Directory entry.";
static constexpr const char* NOT_SUPPORTED = "The requested feature is not supported.";
static constexpr const char* FIXED_POINT_OVERFLOW = "Value is too large to convert to fixed point.";
static constexpr const char* FIXED_POINT_PRECISION = "Value is too small for fixed point precision.";
static constexpr const char* SYSCALL_ERROR = "Error returned from syscall, check errno/GetLastError().";
// Device Errors
static constexpr const char* POLLING_MESSAGE_OVERFLOW = "Too many messages have been recieved for the polling message buffer, some have been lost!";
@@ -114,7 +117,7 @@ static constexpr const char* ATOMIC_OPERATION_RETRIED = "An operation failed to
static constexpr const char* ATOMIC_OPERATION_COMPLETED_NONATOMICALLY = "An ideally-atomic operation was completed nonatomically.";
static constexpr const char* WIVI_STACK_REFRESH_FAILED = "The Wireless neoVI stack encountered a communication error.";
static constexpr const char* WIVI_UPLOAD_STACK_OVERFLOW = "The Wireless neoVI upload stack has encountered an overflow condition.";
static constexpr const char* A2B_MESSAGE_INCOMPLETE_FRAME = "At least one of the frames of the A2B message does not contain samples for each channel and stream.";
static constexpr const char* A2B_MESSAGE_INCOMPLETE_FRAME = "At least one of the frames of the A2B message does not contain samples for each channel and stream.";
static constexpr const char* COREMINI_UPLOAD_VERSION_MISMATCH = "The version of the coremini engine on the device and the script uploaded are not the same.";
static constexpr const char* DISK_NOT_CONNECTED = "The program tried to access a disk that is not connected.";
static constexpr const char* UNEXPECTED_RESPONSE = "Received an unexpected or invalid response from the device.";
@@ -122,6 +125,10 @@ static constexpr const char* LIN_SETTINGS_NOT_AVAILABLE = "LIN settings are not
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.";
static constexpr const char* SETTING_NOT_AVAILABLE = "Requested a setting that is not available on this device";
static constexpr const char* DISK_FORMAT_NOT_SUPPORTED = "Disk formatting is not supported on this device.";
static constexpr const char* DISK_FORMAT_INVALID_COUNT = "Disk format config disk count is mismatched with device disk count.";
// Transport Errors
static constexpr const char* FAILED_TO_READ = "A read operation failed.";
@@ -140,41 +147,6 @@ static constexpr const char* ERROR_SETTING_SOCKET_OPTION = "A call to setsockopt
static constexpr const char* GETIFADDRS_ERROR = "A call to getifaddrs() failed.";
static constexpr const char* SEND_TO_ERROR = "A call to sendto() failed.";
// FTD3XX
static constexpr const char* FT_OK = "FTD3XX success.";
static constexpr const char* FT_INVALID_HANDLE = "Invalid FTD3XX handle.";
static constexpr const char* FT_DEVICE_NOT_FOUND = "FTD3XX device not found.";
static constexpr const char* FT_DEVICE_NOT_OPENED = "FTD3XX device not opened.";
static constexpr const char* FT_IO_ERROR = "FTD3XX IO error.";
static constexpr const char* FT_INSUFFICIENT_RESOURCES = "Insufficient resources for FTD3XX.";
static constexpr const char* FT_INVALID_PARAMETER = "Invalid FTD3XX parameter.";
static constexpr const char* FT_INVALID_BAUD_RATE = "Invalid FTD3XX baud rate.";
static constexpr const char* FT_DEVICE_NOT_OPENED_FOR_ERASE = "FTD3XX device not opened for erase.";
static constexpr const char* FT_DEVICE_NOT_OPENED_FOR_WRITE = "FTD3XX not opened for write.";
static constexpr const char* FT_FAILED_TO_WRITE_DEVICE = "FTD3XX failed to write device.";
static constexpr const char* FT_EEPROM_READ_FAILED = "FTD3XX EEPROM read failed.";
static constexpr const char* FT_EEPROM_WRITE_FAILED = "FTD3XX EEPROM write failed.";
static constexpr const char* FT_EEPROM_ERASE_FAILED = "FTD3XX EEPROM erase failed.";
static constexpr const char* FT_EEPROM_NOT_PRESENT = "FTD3XX EEPROM not present.";
static constexpr const char* FT_EEPROM_NOT_PROGRAMMED = "FTD3XX EEPROM not programmed.";
static constexpr const char* FT_INVALID_ARGS = "Invalid FTD3XX arguments.";
static constexpr const char* FT_NOT_SUPPORTED = "FTD3XX not supported.";
static constexpr const char* FT_NO_MORE_ITEMS = "No more FTD3XX items.";
static constexpr const char* FT_TIMEOUT = "FTD3XX timeout.";
static constexpr const char* FT_OPERATION_ABORTED = "FTD3XX operation aborted.";
static constexpr const char* FT_RESERVED_PIPE = "Reserved FTD3XX pipe.";
static constexpr const char* FT_INVALID_CONTROL_REQUEST_DIRECTION = "Invalid FTD3XX control request direction.";
static constexpr const char* FT_INVALID_CONTROL_REQUEST_TYPE = "Invalid FTD3XX control request type.";
static constexpr const char* FT_IO_PENDING = "FTD3XX IO pending.";
static constexpr const char* FT_IO_INCOMPLETE = "FTD3XX IO incomplete.";
static constexpr const char* FT_HANDLE_EOF = "Handle FTD3XX EOF.";
static constexpr const char* FT_BUSY = "FTD3XX busy.";
static constexpr const char* FT_NO_SYSTEM_RESOURCES = "No FTD3XX system resources.";
static constexpr const char* FT_DEVICE_LIST_NOT_READY = "FTD3XX device list not ready.";
static constexpr const char* FT_DEVICE_NOT_CONNECTED = "FTD3XX device not connected.";
static constexpr const char* FT_INCORRECT_DEVICE_PATH = "Incorrect FTD3XX device path.";
static constexpr const char* FT_OTHER_ERROR = "Other FTD3XX error.";
// VSA
static constexpr const char* VSA_BUFFER_CORRUPTED = "VSA data in record buffer is corrupted.";
static constexpr const char* VSA_TIMESTAMP_NOT_FOUND = "Unable to find a VSA record with a valid timestamp.";
@@ -197,6 +169,13 @@ static constexpr const char* SERVD_POLL_ERROR = "Error polling on Servd socket";
static constexpr const char* SERVD_NODATA_ERROR = "No data received from Servd";
static constexpr const char* SERVD_JOIN_MULTICAST_ERROR = "Error joining Servd multicast group";
// DXX
static constexpr const char* DXX_ERROR_SYS = "System error, check errno/GetLastError()";
static constexpr const char* DXX_ERROR_INT = "DXX interrupt called";
static constexpr const char* DXX_ERROR_OVERFLOW = "Overflow in DXX";
static constexpr const char* DXX_ERROR_IO = "I/O failure in DXX";
static constexpr const char* DXX_ERROR_ARG = "Invalid arg passed to DXX";
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.";
@@ -240,6 +219,12 @@ const char* APIEvent::DescriptionForType(Type type) {
return RESTRICTED_ENTRY_FLAG;
case Type::NotSupported:
return NOT_SUPPORTED;
case Type::FixedPointOverflow:
return FIXED_POINT_OVERFLOW;
case Type::FixedPointPrecision:
return FIXED_POINT_PRECISION;
case Type::SyscallError:
return SYSCALL_ERROR;
// Device Errors
case Type::PollingMessageOverflow:
@@ -365,74 +350,10 @@ const char* APIEvent::DescriptionForType(Type type) {
return SEND_TO_ERROR;
case Type::GPTPNotSupported:
return GPTP_NOT_SUPPORTED;
// FTD3XX
case Type::FTOK:
return FT_OK;
case Type::FTInvalidHandle:
return FT_INVALID_HANDLE;
case Type::FTDeviceNotFound:
return FT_DEVICE_NOT_FOUND;
case Type::FTDeviceNotOpened:
return FT_DEVICE_NOT_OPENED;
case Type::FTIOError:
return FT_IO_ERROR;
case Type::FTInsufficientResources:
return FT_INSUFFICIENT_RESOURCES;
case Type::FTInvalidParameter:
return FT_INVALID_PARAMETER;
case Type::FTInvalidBaudRate:
return FT_INVALID_BAUD_RATE;
case Type::FTDeviceNotOpenedForErase:
return FT_DEVICE_NOT_OPENED_FOR_ERASE;
case Type::FTDeviceNotOpenedForWrite:
return FT_DEVICE_NOT_OPENED_FOR_WRITE;
case Type::FTFailedToWriteDevice:
return FT_FAILED_TO_WRITE_DEVICE;
case Type::FTEEPROMReadFailed:
return FT_EEPROM_READ_FAILED;
case Type::FTEEPROMWriteFailed:
return FT_EEPROM_WRITE_FAILED;
case Type::FTEEPROMEraseFailed:
return FT_EEPROM_ERASE_FAILED;
case Type::FTEEPROMNotPresent:
return FT_EEPROM_NOT_PRESENT;
case Type::FTEEPROMNotProgrammed:
return FT_EEPROM_NOT_PROGRAMMED;
case Type::FTInvalidArgs:
return FT_INVALID_ARGS;
case Type::FTNotSupported:
return FT_NOT_SUPPORTED;
case Type::FTNoMoreItems:
return FT_NO_MORE_ITEMS;
case Type::FTTimeout:
return FT_TIMEOUT;
case Type::FTOperationAborted:
return FT_OPERATION_ABORTED;
case Type::FTReservedPipe:
return FT_RESERVED_PIPE;
case Type::FTInvalidControlRequestDirection:
return FT_INVALID_CONTROL_REQUEST_DIRECTION;
case Type::FTInvalidControlRequestType:
return FT_INVALID_CONTROL_REQUEST_TYPE;
case Type::FTIOPending:
return FT_IO_PENDING;
case Type::FTIOIncomplete:
return FT_IO_INCOMPLETE;
case Type::FTHandleEOF:
return FT_HANDLE_EOF;
case Type::FTBusy:
return FT_BUSY;
case Type::FTNoSystemResources:
return FT_NO_SYSTEM_RESOURCES;
case Type::FTDeviceListNotReady:
return FT_DEVICE_LIST_NOT_READY;
case Type::FTDeviceNotConnected:
return FT_DEVICE_NOT_CONNECTED;
case Type::FTIncorrectDevicePath:
return FT_INCORRECT_DEVICE_PATH;
case Type::FTOtherError:
return FT_OTHER_ERROR;
case Type::DiskFormatNotSupported:
return DISK_FORMAT_NOT_SUPPORTED;
case Type::DiskFormatInvalidCount:
return DISK_FORMAT_INVALID_COUNT;
// VSA
case Type::VSABufferCorrupted:
@@ -474,6 +395,18 @@ const char* APIEvent::DescriptionForType(Type type) {
case Type::ServdJoinMulticastError:
return SERVD_JOIN_MULTICAST_ERROR;
// DXX
case Type::DXXErrorSys:
return DXX_ERROR_SYS;
case Type::DXXErrorInt:
return DXX_ERROR_INT;
case Type::DXXErrorOverflow:
return DXX_ERROR_OVERFLOW;
case Type::DXXErrorIO:
return DXX_ERROR_IO;
case Type::DXXErrorArg:
return DXX_ERROR_ARG;
// Other Errors
case Type::TooManyEvents:
return TOO_MANY_EVENTS;
@@ -487,7 +420,7 @@ const char* APIEvent::DescriptionForType(Type type) {
bool EventFilter::match(const APIEvent& event) const noexcept {
if(type != APIEvent::Type::Any && type != event.getType())
return false;
if(matchOnDevicePtr && !event.isForDevice(device))
return false;
@@ -1,5 +1,7 @@
//FILE: icsneo40DLLAPI.H
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
#include "icsneo/icsnVC40.h"
+27 -37
View File
@@ -28,6 +28,9 @@ void init_event(pybind11::module_& m) {
.value("WiVINotSupported", APIEvent::Type::WiVINotSupported)
.value("RestrictedEntryFlag", APIEvent::Type::RestrictedEntryFlag)
.value("NotSupported", APIEvent::Type::NotSupported)
.value("FixedPointOverflow", APIEvent::Type::FixedPointOverflow)
.value("FixedPointPrecision", APIEvent::Type::FixedPointPrecision)
.value("SyscallError", APIEvent::Type::SyscallError)
.value("PollingMessageOverflow", APIEvent::Type::PollingMessageOverflow)
.value("NoSerialNumber", APIEvent::Type::NoSerialNumber)
.value("IncorrectSerialNumber", APIEvent::Type::IncorrectSerialNumber)
@@ -84,6 +87,10 @@ void init_event(pybind11::module_& m) {
.value("LINSettingsNotAvailable", APIEvent::Type::LINSettingsNotAvailable)
.value("ModeNotFound", APIEvent::Type::ModeNotFound)
.value("AppErrorParsingFailed", APIEvent::Type::AppErrorParsingFailed)
.value("GPTPNotSupported", APIEvent::Type::GPTPNotSupported)
.value("SettingNotAvaiableDevice", APIEvent::Type::SettingNotAvaiableDevice)
.value("DiskFormatNotSupported", APIEvent::Type::DiskFormatNotSupported)
.value("DiskFormatInvalidCount", APIEvent::Type::DiskFormatInvalidCount)
.value("FailedToRead", APIEvent::Type::FailedToRead)
.value("FailedToWrite", APIEvent::Type::FailedToWrite)
.value("DriverFailedToOpen", APIEvent::Type::DriverFailedToOpen)
@@ -100,39 +107,6 @@ void init_event(pybind11::module_& m) {
.value("GetIfAddrsError", APIEvent::Type::GetIfAddrsError)
.value("SendToError", APIEvent::Type::SendToError)
.value("MDIOMessageExceedsMaxLength", APIEvent::Type::MDIOMessageExceedsMaxLength)
.value("FTOK", APIEvent::Type::FTOK)
.value("FTInvalidHandle", APIEvent::Type::FTInvalidHandle)
.value("FTDeviceNotFound", APIEvent::Type::FTDeviceNotFound)
.value("FTDeviceNotOpened", APIEvent::Type::FTDeviceNotOpened)
.value("FTIOError", APIEvent::Type::FTIOError)
.value("FTInsufficientResources", APIEvent::Type::FTInsufficientResources)
.value("FTInvalidParameter", APIEvent::Type::FTInvalidParameter)
.value("FTInvalidBaudRate", APIEvent::Type::FTInvalidBaudRate)
.value("FTDeviceNotOpenedForErase", APIEvent::Type::FTDeviceNotOpenedForErase)
.value("FTDeviceNotOpenedForWrite", APIEvent::Type::FTDeviceNotOpenedForWrite)
.value("FTFailedToWriteDevice", APIEvent::Type::FTFailedToWriteDevice)
.value("FTEEPROMReadFailed", APIEvent::Type::FTEEPROMReadFailed)
.value("FTEEPROMWriteFailed", APIEvent::Type::FTEEPROMWriteFailed)
.value("FTEEPROMEraseFailed", APIEvent::Type::FTEEPROMEraseFailed)
.value("FTEEPROMNotPresent", APIEvent::Type::FTEEPROMNotPresent)
.value("FTEEPROMNotProgrammed", APIEvent::Type::FTEEPROMNotProgrammed)
.value("FTInvalidArgs", APIEvent::Type::FTInvalidArgs)
.value("FTNotSupported", APIEvent::Type::FTNotSupported)
.value("FTNoMoreItems", APIEvent::Type::FTNoMoreItems)
.value("FTTimeout", APIEvent::Type::FTTimeout)
.value("FTOperationAborted", APIEvent::Type::FTOperationAborted)
.value("FTReservedPipe", APIEvent::Type::FTReservedPipe)
.value("FTInvalidControlRequestDirection", APIEvent::Type::FTInvalidControlRequestDirection)
.value("FTInvalidControlRequestType", APIEvent::Type::FTInvalidControlRequestType)
.value("FTIOPending", APIEvent::Type::FTIOPending)
.value("FTIOIncomplete", APIEvent::Type::FTIOIncomplete)
.value("FTHandleEOF", APIEvent::Type::FTHandleEOF)
.value("FTBusy", APIEvent::Type::FTBusy)
.value("FTNoSystemResources", APIEvent::Type::FTNoSystemResources)
.value("FTDeviceListNotReady", APIEvent::Type::FTDeviceListNotReady)
.value("FTDeviceNotConnected", APIEvent::Type::FTDeviceNotConnected)
.value("FTIncorrectDevicePath", APIEvent::Type::FTIncorrectDevicePath)
.value("FTOtherError", APIEvent::Type::FTOtherError)
.value("VSABufferCorrupted", APIEvent::Type::VSABufferCorrupted)
.value("VSATimestampNotFound", APIEvent::Type::VSATimestampNotFound)
.value("VSABufferFormatError", APIEvent::Type::VSABufferFormatError)
@@ -140,16 +114,32 @@ void init_event(pybind11::module_& m) {
.value("VSAByteParseFailure", APIEvent::Type::VSAByteParseFailure)
.value("VSAExtendedMessageError", APIEvent::Type::VSAExtendedMessageError)
.value("VSAOtherError", APIEvent::Type::VSAOtherError)
.value("ServdBindError", APIEvent::Type::ServdBindError)
.value("ServdNonblockError", APIEvent::Type::ServdNonblockError)
.value("ServdTransceiveError", APIEvent::Type::ServdTransceiveError)
.value("ServdOutdatedError", APIEvent::Type::ServdOutdatedError)
.value("ServdInvalidResponseError", APIEvent::Type::ServdInvalidResponseError)
.value("ServdLockError", APIEvent::Type::ServdLockError)
.value("ServdSendError", APIEvent::Type::ServdSendError)
.value("ServdRecvError", APIEvent::Type::ServdRecvError)
.value("ServdPollError", APIEvent::Type::ServdPollError)
.value("ServdNoDataError", APIEvent::Type::ServdNoDataError)
.value("ServdJoinMulticastError", APIEvent::Type::ServdJoinMulticastError)
.value("DXXErrorSys", APIEvent::Type::DXXErrorSys)
.value("DXXErrorInt", APIEvent::Type::DXXErrorInt)
.value("DXXErrorOverflow", APIEvent::Type::DXXErrorOverflow)
.value("DXXErrorIO", APIEvent::Type::DXXErrorIO)
.value("DXXErrorArg", APIEvent::Type::DXXErrorArg)
.value("NoErrorFound", APIEvent::Type::NoErrorFound)
.value("TooManyEvents", APIEvent::Type::TooManyEvents)
.value("Unknown", APIEvent::Type::Unknown);
pybind11::enum_<APIEvent::Severity>(apiEvent, "Severity")
pybind11::enum_<APIEvent::Severity>(apiEvent, "Severity")
.value("Any", APIEvent::Severity::Any)
.value("EventInfo", APIEvent::Severity::EventInfo)
.value("EventWarning", APIEvent::Severity::EventWarning)
.value("Error", APIEvent::Severity::Error);
apiEvent
.def("get_type", &APIEvent::getType)
.def("get_severity", &APIEvent::getSeverity)
@@ -166,5 +156,5 @@ void init_event(pybind11::module_& m) {
.def_readwrite("serial", &EventFilter::serial);
}
} // namespace icsneo
} // namespace icsneo
@@ -123,6 +123,7 @@ void init_network(pybind11::module_& m) {
.value("I2C_03", Network::NetID::I2C_03)
.value("I2C_04", Network::NetID::I2C_04)
.value("ETHERNET_02", Network::NetID::ETHERNET_02)
.value("ETHERNET_TX_WRAP", Network::NetID::ETHERNET_TX_WRAP)
.value("A2B_01", Network::NetID::A2B_01)
.value("A2B_02", Network::NetID::A2B_02)
.value("ETHERNET_03", Network::NetID::ETHERNET_03)
+1 -1
View File
@@ -17,7 +17,7 @@ void init_device(pybind11::module_& m) {
.def("close", &Device::close, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("describe", &Device::describe)
.def("disable_message_polling", &Device::disableMessagePolling, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("enable_message_polling", &Device::enableMessagePolling, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("enable_message_polling", &Device::enableMessagePolling, pybind11::arg("filter") = std::nullopt, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_current_message_count", &Device::getCurrentMessageCount)
.def("get_digital_io", &Device::getDigitalIO, pybind11::arg("type"), pybind11::arg("number"), 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>())
-21
View File
@@ -1,21 +0,0 @@
#!/bin/sh
VERSION="1.0.27"
ROOT="$PWD/libusb"
SOURCE="$ROOT/source"
BUILD="$ROOT/build"
INSTALL="$ROOT/install"
mkdir -p "$ROOT"
cd "$ROOT" || exit 1
curl -LO "https://github.com/libusb/libusb/releases/download/v$VERSION/libusb-$VERSION.tar.bz2" || exit 1
tar -xf "libusb-$VERSION.tar.bz2" || exit 1
mv "libusb-$VERSION" "$SOURCE" || exit 1
mkdir "$BUILD" || exit 1
cd "$BUILD" || exit 1
"$SOURCE/configure" --prefix="$INSTALL" --disable-shared --disable-udev --disable-eventfd --disable-timerfd --with-pic || exit 1
make || exit 1
make install || exit 1
-22
View File
@@ -1,22 +0,0 @@
find_path(FTD3XX_INCLUDE_DIR
NAMES ftd3xx.h FTD3XX.h
)
find_library(FTD3XX_LIBRARY
NAMES libftd3xx.a libftd3xx-static.a FTD3XX.lib
PATH_SUFFIXES x64/Static
)
mark_as_advanced(FTD3XX_FOUND FTD3XX_INCLUDE_DIR FTD3XX_LIBRARY)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(FTD3XX
REQUIRED_VARS FTD3XX_INCLUDE_DIR FTD3XX_LIBRARY
)
if(FTD3XX_FOUND AND NOT TARGET D3XX::D3XX)
add_library(FTD3XX::FTD3XX INTERFACE IMPORTED)
set_target_properties(FTD3XX::FTD3XX PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${FTD3XX_INCLUDE_DIR}"
INTERFACE_LINK_LIBRARIES "${FTD3XX_LIBRARY}"
)
endif()
+21
View File
@@ -16,6 +16,7 @@
#include "icsneo/communication/message/mdiomessage.h"
#include "icsneo/communication/message/extendeddatamessage.h"
#include "icsneo/communication/message/livedatamessage.h"
#include "icsneo/communication/message/logdatamessage.h"
#include "icsneo/communication/message/diskdatamessage.h"
#include "icsneo/communication/message/hardwareinfo.h"
#include "icsneo/communication/message/tc10statusmessage.h"
@@ -323,6 +324,17 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
result = GPTPStatus::DecodeToMessage(packet->data, report);
return true;
}
case ExtendedCommand::GetDiskDetails:
case ExtendedCommand::DiskFormatProgress: {
std::vector<uint8_t> responseBody(
packet->data.begin() + sizeof(ExtendedResponseMessage::ResponseHeader),
packet->data.end()
);
auto response = std::make_shared<ExtendedResponseMessage>(resp.command);
response->data = std::move(responseBody);
result = response;
return true;
}
default:
// No defined handler, treat this as a RawMessage
break;
@@ -491,7 +503,16 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
result = std::make_shared<DiskDataMessage>(std::move(packet->data));
return true;
}
case Network::NetID::Data_To_Host: {
result = LogDataMessage::DecodeToMessage(packet->data);
if(!result) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::EventWarning);
return false;
}
return true;
}
default:
break;
}
break;
+13 -4
View File
@@ -35,15 +35,25 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
auto ethmsg = std::dynamic_pointer_cast<EthernetMessage>(message);
if(!ethmsg) {
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
return false; // The message was not a properly formed EthernetMessage
return false;
}
shortFormat = false; // Ensure long-format RED header is added
netid = static_cast<uint16_t>(Network::NetID::ETHERNET_TX_WRAP);
buffer = &result;
if(!HardwareEthernetPacket::EncodeFromMessage(*ethmsg, result, report))
if(!HardwareEthernetPacket::EncodeFromMessage(*ethmsg, result, report)) {
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
return false;
}
if(result.empty()) {
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
return false;
}
break;
} // End of Network::Type::Ethernet
}
case Network::Type::CAN:
case Network::Type::SWCAN:
case Network::Type::LSFTCAN: {
@@ -230,7 +240,6 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
(uint8_t)(netid >> 8)
});
}
result = packetizer.packetWrap(*buffer, shortFormat);
return true;
}
+57
View File
@@ -1,4 +1,5 @@
#include "icsneo/communication/livedata.h"
#include <cmath>
namespace icsneo {
namespace LiveDataUtil {
@@ -18,5 +19,61 @@ double liveDataValueToDouble(const LiveDataValue& val) {
return val.value * liveDataFixedPointToDouble;
}
bool liveDataDoubleToValue(const double& dFloat, LiveDataValue& value) {
union {
struct
{
uint32_t ValueFractionPart;
int32_t ValueInt32;
} parts;
int64_t ValueLarge;
} CminiFixedPt;
constexpr double CM_FIXED_POINT_TO_DOUBLEVALUE = (1.0 / (double)(1ULL << 32)); // 2^-32
constexpr double CM_DOUBLEVALUE_TO_FIXED_POINT = ((double)(1ULL << 32)); // 2^32
// Use const for limits (C++98 compatible)
const double INT32_MAX_DOUBLE =
static_cast<double>(std::numeric_limits<int32_t>::max()) + (1.0 - std::numeric_limits<double>::epsilon());
const double INT32_MIN_DOUBLE = static_cast<double>(std::numeric_limits<int32_t>::min());
const double MIN_FIXED_POINT_DOUBLE = (double)(1ull * CM_FIXED_POINT_TO_DOUBLEVALUE);
// This needs to be assigned separately, otherwise, for dFloat >= 2^31,
// long double dBigFloat = dFloat * CM_DOUBLEVALUE_TO_FIXED_POINT overflows
// long long (value is >= 2^63) and so the assignment ValueLarge = dBigFloat is undefined
int32_t intPart; //creating temp variable due to static analysis warning about writing and reading to different union members
if(dFloat < 0.0)
intPart = (int32_t)std::floor(dFloat);
else
intPart = (int32_t)dFloat;
//using temp varialbes to avoid static analysis warning about read/write to different union members
double frac = dFloat - (double)(intPart);
uint32_t fracPart = (uint32_t)std::floor((frac * CM_DOUBLEVALUE_TO_FIXED_POINT) + 0.5);
//write temp vars back into the union
CminiFixedPt.parts.ValueInt32 = intPart;
CminiFixedPt.parts.ValueFractionPart = fracPart;
value.value = CminiFixedPt.ValueLarge;
if(dFloat == (double)0.0)
return true;
//check if double can be stored as 32.32
// 0x1 0000 0000 0000 0000 * CM_FIXED_POINT_TO_DOUBLEVALUE = 0x1 0000 0000
if(dFloat > INT32_MAX_DOUBLE || dFloat < INT32_MIN_DOUBLE) {
EventManager::GetInstance().add(APIEvent::Type::FixedPointOverflow, APIEvent::Severity::Error);
return false;
}
// Use absolute value for minimum fixed point check
double absFloat = (dFloat < 0.0) ? -dFloat : dFloat;
if(absFloat < MIN_FIXED_POINT_DOUBLE) {
EventManager::GetInstance().add(APIEvent::Type::FixedPointPrecision, APIEvent::Severity::Error);
return false;
}
return true;
}
} // namespace LiveDataUtil
} // namespace icsneo
+10
View File
@@ -12,4 +12,14 @@ void LiveDataCommandMessage::appendSignalArg(LiveDataValueType valueType) {
arg->valueType = valueType;
}
void LiveDataSetValueMessage::appendSetValue(LiveDataValueType valueType, const LiveDataValue& value) {
auto& arg = args.emplace_back(std::make_shared<LiveDataArgument>());
arg->objectType = LiveDataObjectType::MISC;
arg->objectIndex = 0u;
arg->signalIndex = 0u;
arg->valueType = valueType;
values.push_back(std::make_shared<LiveDataValue>(value));
}
} // namespace icsneo
+12
View File
@@ -0,0 +1,12 @@
#include "icsneo/communication/message/logdatamessage.h"
#include <iostream>
using namespace icsneo;
std::shared_ptr<LogDataMessage> LogDataMessage::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
if(bytestream.size() % 2 != 0)
return nullptr;
const auto* begin = (char16_t*)bytestream.data();
const auto* end = begin + (bytestream.size() / sizeof(char16_t));
return std::make_shared<LogDataMessage>(std::wstring(begin,end));
}
+72 -39
View File
@@ -1,5 +1,5 @@
#include "icsneo/communication/packet/ethernetpacket.h"
#include <cstring> // memcpy
#include <algorithm> // for std::copy
#include <iostream>
using namespace icsneo;
@@ -7,40 +7,31 @@ using namespace icsneo;
std::shared_ptr<EthernetMessage> HardwareEthernetPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream, const device_eventhandler_t& report) {
const HardwareEthernetPacket* packet = (const HardwareEthernetPacket*)((const void*)bytestream.data());
const uint16_t* rawWords = (const uint16_t*)bytestream.data();
// Make sure we have enough to read the packet length first
if(bytestream.size() < sizeof(HardwareEthernetPacket))
return nullptr;
// packet->Length will also encompass the two uint16_t's at the end of the struct, make sure that at least they are here
if(packet->Length < 4)
return nullptr;
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;
// Check for oversized packets, noting that some devices will send an extra byte to have an even number of bytes
if(bytestreamActualSize > bytestreamExpectedSize + 1)
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::EventWarning);
auto messagePtr = std::make_shared<EthernetMessage>();
EthernetMessage& message = *messagePtr;
message.transmitted = packet->eid.TXMSG;
if(message.transmitted)
message.description = packet->stats;
message.preemptionEnabled = packet->header.PREEMPTION_ENABLED;
if(message.preemptionEnabled)
message.preemptionFlags = (uint8_t)((rawWords[0] & 0x03F8) >> 4);
message.frameTooShort = packet->header.RUNT_FRAME;
if(message.frameTooShort)
message.error = true;
// This timestamp is raw off the device (in timestampResolution increments)
// Decoder will fix as it has information about the timestampResolution increments
message.timestamp = packet->timestamp.TS;
@@ -48,7 +39,7 @@ std::shared_ptr<EthernetMessage> HardwareEthernetPacket::DecodeToMessage(const s
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);
@@ -59,41 +50,83 @@ std::shared_ptr<EthernetMessage> HardwareEthernetPacket::DecodeToMessage(const s
bool HardwareEthernetPacket::EncodeFromMessage(const EthernetMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t&) {
const size_t unpaddedSize = message.data.size();
size_t paddedSize = unpaddedSize;
uint16_t description = message.description;
if(!message.noPadding && unpaddedSize < 60)
paddedSize = 60; // Pad out short messages
size_t sizeWithHeader = paddedSize + 4; // DescriptionID and Padded Count
if(unpaddedSize == 0)
return false;
// Description ID Most Significant bit is used to identify preemption frames
uint16_t description = message.description;
if(description & 0x8000)
return false;
if(message.preemptionEnabled) {
sizeWithHeader++; // Make space for the preemption flags
const bool preempt = message.preemptionEnabled;
// full header including parent
const size_t headerByteCount = preempt ? 15 : 14;
// local header for netID, description, and flags
const size_t localHeader = preempt ? 10 : 9;
// allocate space for fcs override
const size_t fcsSize = message.fcs ? 4 : 0;
if(preempt)
description |= 0x8000;
size_t paddedSize = unpaddedSize;
if(!message.noPadding && unpaddedSize < 60)
paddedSize = 60;
// size of full payload including optional fcs
size_t payloadSize = paddedSize + fcsSize;
// totalBufferSize is local header + ethernet payload and option fcs
const size_t totalBufferSize = localHeader + paddedSize + fcsSize;
bytestream.clear();
bytestream.reserve(totalBufferSize);
// Header size field (little endian)
bytestream.push_back(static_cast<uint8_t>(payloadSize & 0xFF));
bytestream.push_back(static_cast<uint8_t>((payloadSize >> 8) & 0xFF));
// Description (big endian)
bytestream.push_back(static_cast<uint8_t>(description >> 8));
bytestream.push_back(static_cast<uint8_t>(description));
bytestream.push_back(0x00);
bytestream.push_back(static_cast<uint8_t>(headerByteCount));
// Network ID (little endian)
uint16_t realID = static_cast<uint16_t>(message.network.getNetID());
bytestream.push_back(static_cast<uint8_t>(realID & 0xFF));
bytestream.push_back(static_cast<uint8_t>((realID >> 8) & 0xFF));
// Flags
constexpr uint8_t FLAG_PADDING = 0x01;
constexpr uint8_t FLAG_FCS = 0x04;
constexpr uint8_t FLAG_PREEMPTION = 0x08;
uint8_t flags = 0x00;
if(!message.noPadding) flags |= FLAG_PADDING;
if(message.fcs) flags |= FLAG_FCS;
if(message.preemptionEnabled) flags |= FLAG_PREEMPTION;
bytestream.push_back(flags);
if(preempt)
bytestream.push_back(static_cast<uint8_t>(message.preemptionFlags));
bytestream.insert(bytestream.end(), message.data.begin(), message.data.end());
// Only zero-fill when we want padding
if(!message.noPadding && unpaddedSize < 60) {
size_t paddingNeeded = 60 - unpaddedSize;
bytestream.insert(bytestream.end(), paddingNeeded, 0); // Zero-fill for padding
}
bytestream.reserve(sizeWithHeader + 8); // Also reserve space for the bytes we'll use later on
bytestream.resize(sizeWithHeader);
size_t index = 0;
// Padded size, little endian
bytestream[index++] = uint8_t(paddedSize);
bytestream[index++] = uint8_t(paddedSize >> 8);
// Description ID, big endian
bytestream[index++] = uint8_t(description >> 8);
bytestream[index++] = uint8_t(description);
// The header is one byte larger if preemption is enabled, shifting the data
if(message.preemptionEnabled)
bytestream[index++] = message.preemptionFlags;
// We only copy in the unpadded size, the rest will be 0
memcpy(bytestream.data() + index, message.data.data(), unpaddedSize);
if(message.fcs) {
uint32_t fcs = message.fcs.value();
const uint8_t* fcsBytes = reinterpret_cast<const uint8_t*>(&fcs);
bytestream.insert(bytestream.end(), fcsBytes, fcsBytes + sizeof(fcs));
}
return true;
}
}
+27
View File
@@ -99,6 +99,33 @@ bool HardwareLiveDataPacket::EncodeFromMessage(LiveDataMessage& message, std::ve
clearMsg->cmd = static_cast<uint32_t>(message.cmd);
break;
}
case LiveDataCommand::SET_VALUE: {
auto setValMsg = reinterpret_cast<LiveDataSetValueMessage*>(&message);
const auto numArgs = setValMsg->args.size();
if(numArgs) {
payloadSize = static_cast<uint16_t>(sizeof(LiveDataSetValue) + (sizeof(LiveDataSetValueEntry) * (numArgs-1)));
bytestream.resize((payloadSize + sizeof(ExtendedCommandHeader)),0);
LiveDataSetValue* out = reinterpret_cast<LiveDataSetValue*>(bytestream.data() + sizeof(ExtendedCommandHeader));
out->version = icsneo::LiveDataUtil::LiveDataVersion;
out->cmd = static_cast<uint32_t>(setValMsg->cmd);
if(!setValMsg->handle)
setValMsg->handle = LiveDataUtil::getNewHandle();
out->handle = setValMsg->handle;
out->numSetValues = (uint32_t)numArgs;
for(size_t i = 0; i < numArgs; ++i) {
out->values[i].arg.objectType = setValMsg->args[i]->objectType;
out->values[i].arg.objectIndex = setValMsg->args[i]->objectIndex;
out->values[i].arg.signalIndex = setValMsg->args[i]->signalIndex;
out->values[i].arg.valueType = setValMsg->args[i]->valueType;
out->values[i].value.value = setValMsg->values[i]->value;
out->values[i].value.header.length = sizeof(LiveDataValue::value);
}
} else {
report(APIEvent::Type::LiveDataInvalidArgument, APIEvent::Severity::Error);
return false;
}
break;
}
default: {
report(APIEvent::Type::LiveDataInvalidCommand, APIEvent::Severity::Error);
return false;
+211 -7
View File
@@ -5,6 +5,7 @@
#include "icsneo/device/device.h"
#include "icsneo/device/extensions/deviceextension.h"
#include "icsneo/disk/fat.h"
#include "icsneo/communication/message/filter/extendedresponsefilter.h"
#ifdef _MSC_VER
#pragma warning(disable : 4996) // STL time functions
@@ -104,15 +105,21 @@ std::string Device::describe() const {
return ss.str();
}
bool Device::enableMessagePolling() {
bool Device::enableMessagePolling(std::optional<MessageFilter> filter) {
if(isMessagePollingEnabled()) {// We are already polling
report(APIEvent::Type::DeviceCurrentlyPolling, APIEvent::Severity::Error);
return false;
}
messagePollingCallbackID = com->addMessageCallback(std::make_shared<MessageCallback>([this](std::shared_ptr<Message> message) {
if(!filter.has_value()) {
// If no filter is provided, use a default that includes all messages
filter.emplace(MessageFilter());
filter->includeInternalInAny = true;
}
auto callback = std::make_shared<MessageCallback>(*filter, [this](std::shared_ptr<Message> message) {
pollingContainer.enqueue(message);
enforcePollingMessageLimit();
}));
});
messagePollingCallbackID = com->addMessageCallback(callback);
return true;
}
@@ -277,7 +284,7 @@ bool Device::open(OpenFlags flags, OpenStatusHandler handler) {
std::condition_variable heartbeatCV;
std::mutex receivedMessageMutex;
bool receivedMessage = false;
auto messageReceivedCallbackID = com->addMessageCallback(std::make_shared<MessageCallback>(filter, [&](std::shared_ptr<Message> message) {
auto messageReceivedCallbackID = com->addMessageCallback(std::make_shared<MessageCallback>(filter, [&](std::shared_ptr<Message>) {
{
std::scoped_lock<std::mutex> lk(receivedMessageMutex);
receivedMessage = true;
@@ -405,8 +412,17 @@ bool Device::close() {
return com->close();
}
bool Device::enableLogData() {
return com->sendCommand(Command::EnableLogData, true);
}
bool Device::disableLogData() {
return com->sendCommand(Command::EnableLogData, false);
}
bool Device::goOnline() {
if(!enableNetworkCommunication(true))
static constexpr uint32_t onlineTimeoutMs = 5000;
if(!enableNetworkCommunication(true, onlineTimeoutMs))
return false;
auto startTime = std::chrono::system_clock::now();
@@ -435,13 +451,19 @@ bool Device::goOnline() {
return false;
}
// (re)start the keeponline
keeponline = std::make_unique<Periodic>([this] { return enableNetworkCommunication(true, onlineTimeoutMs); }, std::chrono::milliseconds(onlineTimeoutMs / 4));
online = true;
forEachExtension([](const std::shared_ptr<DeviceExtension>& ext) { ext->onGoOnline(); return true; });
return true;
}
bool Device::goOffline() {
keeponline.reset();
forEachExtension([](const std::shared_ptr<DeviceExtension>& ext) { ext->onGoOffline(); return true; });
if(isDisconnected()) {
@@ -2227,6 +2249,61 @@ bool Device::clearAllLiveData() {
return true;
}
bool Device::setValueLiveData(std::shared_ptr<LiveDataSetValueMessage> message) {
if(!supportsLiveData()) {
report(APIEvent::Type::LiveDataNotSupported, APIEvent::Severity::Error);
return false;
}
if(!isOpen()) {
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
return false;
}
if((message->args.size() != message->values.size()) || message->args.empty()) {
report(APIEvent::Type::LiveDataInvalidArgument, APIEvent::Severity::Error);
return false;
}
std::vector<uint8_t> bytes;
if(!com->encoder->encode(*com->packetizer, bytes, message)) {
report(APIEvent::Type::LiveDataEncoderError, APIEvent::Severity::Error);
return false;
}
std::shared_ptr<Message> response = com->waitForMessageSync(
[this, &bytes](){ return com->sendPacket(bytes); },
std::make_shared<MessageFilter>(Message::Type::LiveData));
if(response) {
auto statusMsg = std::dynamic_pointer_cast<LiveDataStatusMessage>(response);
if(statusMsg && statusMsg->requestedCommand == message->cmd) {
switch(statusMsg->status) {
case LiveDataStatus::SUCCESS:
return true;
case LiveDataStatus::ERR_DUPLICATE:
case LiveDataStatus::ERR_HANDLE:
{
report(APIEvent::Type::LiveDataInvalidHandle, APIEvent::Severity::Error);
return false;
}
case LiveDataStatus::ERR_FULL:
{
report(APIEvent::Type::LiveDataMaxSignalsReached, APIEvent::Severity::Error);
return false;
}
case LiveDataStatus::ERR_UNKNOWN_COMMAND:
{
report(APIEvent::Type::LiveDataCommandFailed, APIEvent::Severity::Error);
return false;
}
default:
break;
}
}
}
report(APIEvent::Type::LiveDataNoDeviceResponse, APIEvent::Severity::Error);
return false;
}
bool Device::readVSA(const VSAExtractionSettings& extractionSettings) {
if(isOnline()) {
goOffline();
@@ -3412,15 +3489,142 @@ bool Device::writeMACsecConfig(const MACsecMessage& message, uint16_t binaryInde
return writeBinaryFile(raw, binaryIndex);
}
bool Device::enableNetworkCommunication(bool enable) {
bool Device::enableNetworkCommunication(bool enable, uint32_t timeout) {
bool sendMsg = false;
if(!com->driver->enableCommunication(enable, sendMsg)) {
return false;
}
if(sendMsg) {
if(!com->sendCommand(Command::EnableNetworkCommunication, enable)) {
const uint8_t* i = (uint8_t*)&timeout;
if(!com->sendCommand(Command::EnableNetworkCommunication, {enable, 0, 0, 0, i[0], i[1], i[2], i[3]})) {
return false;
}
}
return true;
}
bool Device::formatDisk(const DiskDetails& config, const DiskFormatProgress& handler, std::chrono::milliseconds interval) {
#pragma pack(push, 2)
struct DiskFormatProgressResponse {
uint16_t state;
uint64_t sectorsRemaining;
};
#pragma pack(pop)
auto diskCount = getDiskCount();
if(!diskCount) {
report(APIEvent::Type::DiskFormatNotSupported, APIEvent::Severity::Error);
return false;
}
if(config.disks.size() != diskCount) {
report(APIEvent::Type::DiskFormatInvalidCount, APIEvent::Severity::Error);
return false;
}
std::vector<uint8_t> payload = DiskDetails::Encode(config);
if(!com->sendCommand(ExtendedCommand::DiskFormatStart, payload)) {
return false;
}
uint64_t sectorsFormatted = 0;
uint64_t sectorsTotal = 0;
uint16_t lastState = 1;
do {
std::shared_ptr<Message> response = com->waitForMessageSync(
[this](){
return com->sendCommand(ExtendedCommand::DiskFormatProgress, {});
},
std::make_shared<ExtendedResponseFilter>(ExtendedCommand::DiskFormatProgress),
std::chrono::milliseconds(200)
);
if(!response) {
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error);
return false;
}
auto extResponse = std::dynamic_pointer_cast<ExtendedResponseMessage>(response);
if(!extResponse) {
// Should never happen
return false;
}
if(extResponse->data.size() < sizeof(DiskFormatProgressResponse)) {
report(APIEvent::Type::BufferInsufficient, APIEvent::Severity::Error);
return false;
}
auto* progress = reinterpret_cast<DiskFormatProgressResponse*>(extResponse->data.data());
lastState = progress->state;
if(sectorsTotal == 0) {
sectorsTotal = progress->sectorsRemaining;
} else {
sectorsFormatted = sectorsTotal - progress->sectorsRemaining;
if(handler) {
auto directive = handler(sectorsFormatted, sectorsTotal);
if(directive == DiskFormatDirective::Stop) {
return com->sendCommand(ExtendedCommand::DiskFormatCancel);
}
}
}
std::this_thread::sleep_for(interval);
} while(lastState);
return true;
}
bool Device::forceDiskConfigUpdate(const DiskDetails& config) {
auto diskCount = getDiskCount();
if(!diskCount) {
report(APIEvent::Type::DiskFormatNotSupported, APIEvent::Severity::Error);
return false;
}
if(config.disks.size() != diskCount) {
report(APIEvent::Type::DiskFormatInvalidCount, APIEvent::Severity::Error);
return false;
}
return com->sendCommand(ExtendedCommand::DiskFormatUpdate, DiskDetails::Encode(config));
}
std::shared_ptr<DiskDetails> Device::getDiskDetails(std::chrono::milliseconds timeout) {
if(!supportsDiskFormatting()) {
report(APIEvent::Type::DiskFormatNotSupported, APIEvent::Severity::Error);
return nullptr;
}
if(!isOpen()) {
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
return nullptr;
}
std::shared_ptr<Message> response = com->waitForMessageSync(
[this](){
return com->sendCommand(ExtendedCommand::GetDiskDetails, {});
},
std::make_shared<ExtendedResponseFilter>(ExtendedCommand::GetDiskDetails),
timeout
);
if(!response) {
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error);
return nullptr;
}
auto extResponse = std::dynamic_pointer_cast<ExtendedResponseMessage>(response);
if(!extResponse) {
// Should never happen
return nullptr;
}
return DiskDetails::Decode(extResponse->data, getDiskCount(), report);
}
+4 -12
View File
@@ -16,12 +16,8 @@
#include "icsneo/platform/cdcacm.h"
#endif
#ifdef ICSNEO_ENABLE_FTDI
#include "icsneo/platform/ftdi.h"
#endif
#ifdef ICSNEO_ENABLE_FTD3XX
#include "icsneo/platform/ftd3xx.h"
#ifdef ICSNEO_ENABLE_DXX
#include "icsneo/platform/dxx.h"
#endif
#ifdef ICSNEO_ENABLE_TCP
@@ -83,12 +79,8 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
CDCACM::Find(newDriverFoundDevices);
#endif
#ifdef ICSNEO_ENABLE_FTDI
FTDI::Find(newDriverFoundDevices);
#endif
#ifdef ICSNEO_ENABLE_FTD3XX
FTD3XX::Find(newDriverFoundDevices);
#ifdef ICSNEO_ENABLE_DXX
DXX::Find(newDriverFoundDevices);
#endif
}
+81
View File
@@ -0,0 +1,81 @@
#include <icsneo/disk/diskdetails.h>
#include <icsneo/device/idevicesettings.h>
using namespace icsneo;
#pragma pack(push, 2)
struct DISK_STATUS {
uint16_t status;
uint64_t sectors;
uint32_t bytesPerSector;
};
struct DISK_DETAILS {
DISK_SETTINGS settings;
uint16_t options;
DISK_STATUS status[12];
};
#pragma pack(pop)
static constexpr uint8_t DISK_FORMAT_FAT32 = 0x01u;
static constexpr uint8_t DISK_STATUS_PRESENT = 0x01u;
static constexpr uint8_t DISK_STATUS_INITIALIZED = 0x02u;
static constexpr uint16_t DISK_DETAILS_FULL_FORMAT = 0x01u;
std::vector<uint8_t> DiskDetails::Encode(const DiskDetails& config) {
std::vector<uint8_t> result(sizeof(DISK_DETAILS), 0);
DISK_DETAILS* details = reinterpret_cast<DISK_DETAILS*>(result.data());
details->settings.disk_layout = static_cast<uint8_t>(config.layout);
details->settings.disk_format = DISK_FORMAT_FAT32; // Always FAT32
details->options = config.fullFormat ? DISK_DETAILS_FULL_FORMAT : 0;
uint32_t& enables = details->settings.disk_enables;
enables = 0u;
for(size_t i = 0; i < config.disks.size(); i++) {
auto& disk = config.disks[i];
details->status[i].sectors = disk.sectors;
details->status[i].bytesPerSector = disk.bytesPerSector;
uint16_t& status = details->status[i].status;
status = 0u;
if(disk.initialized) {
status |= DISK_STATUS_INITIALIZED;
}
if(disk.present) {
status |= DISK_STATUS_PRESENT;
}
if(disk.formatted) {
enables |= (1u << i);
}
}
return result;
}
std::shared_ptr<DiskDetails> DiskDetails::Decode(const std::vector<uint8_t>& bytes, size_t diskCount, device_eventhandler_t report) {
if(bytes.size() < sizeof(DISK_DETAILS)) {
report(APIEvent::Type::BufferInsufficient, APIEvent::Severity::Error);
return nullptr;
}
auto result = std::make_shared<DiskDetails>();
const DISK_DETAILS* details = reinterpret_cast<const DISK_DETAILS*>(bytes.data());
result->layout = static_cast<DiskLayout>(details->settings.disk_layout);
result->fullFormat = details->options & DISK_DETAILS_FULL_FORMAT;
result->disks.reserve(diskCount);
for(size_t i = 0; i < diskCount; i++) {
auto& disk = details->status[i];
result->disks.emplace_back();
auto& info = result->disks.back();
info.present = static_cast<bool>(disk.status & DISK_STATUS_PRESENT);
info.initialized = static_cast<bool>(disk.status & DISK_STATUS_INITIALIZED);
info.formatted = static_cast<bool>(details->settings.disk_enables & (1u << i));
info.sectors = disk.sectors;
info.bytesPerSector = disk.bytesPerSector;
}
return result;
}
+1 -1
View File
@@ -32,7 +32,7 @@ std::optional<uint64_t> ExtExtractorDiskReadDriver::readLogicalDiskAligned(Commu
return ret;
}
std::optional<uint64_t> ExtExtractorDiskReadDriver::attemptReadLogicalDiskAligned(Communication& com, device_eventhandler_t report,
std::optional<uint64_t> ExtExtractorDiskReadDriver::attemptReadLogicalDiskAligned(Communication& com, device_eventhandler_t,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, MemoryType) {
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
+1 -1
View File
@@ -58,7 +58,7 @@ std::optional<uint64_t> NeoMemoryDiskDriver::readLogicalDiskAligned(Communicatio
return SectorSize;
}
std::optional<uint64_t> NeoMemoryDiskDriver::writeLogicalDiskAligned(Communication& com, device_eventhandler_t report,
std::optional<uint64_t> NeoMemoryDiskDriver::writeLogicalDiskAligned(Communication& com, device_eventhandler_t,
uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
static std::shared_ptr<MessageFilter> NeoMemoryDone = std::make_shared<MessageFilter>(Network::NetID::NeoMemoryWriteDone);
+1 -1
View File
@@ -6,7 +6,7 @@
using namespace icsneo;
using namespace icsneo::Disk;
std::optional<uint64_t> PlasionDiskReadDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
std::optional<uint64_t> PlasionDiskReadDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, MemoryType) {
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
+1 -1
View File
@@ -7,7 +7,7 @@ VSA08::VSA08(uint8_t* const recordBytes)
{
setType(VSA::Type::AA08);
troubleSramCount.insert(troubleSramCount.end(), recordBytes + 2, recordBytes + 6);
troubleSectors.insert(troubleSectors.end(), reinterpret_cast<uint32_t*>(recordBytes + 6), reinterpret_cast<uint32_t*>(recordBytes + 20));
troubleSectors.insert(troubleSectors.end(), reinterpret_cast<uint32_t*>(recordBytes + 6), reinterpret_cast<uint32_t*>(recordBytes + 22));
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 22) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
doChecksum(recordBytes);
+1 -1
View File
@@ -8,7 +8,7 @@ Dependencies
The minimum requirements to build libicsneo are:
- CMake version 3.12 or newer
- A C++17 compiler
- libusb and libpcap on Linux and macOS
- libpcap on Linux and macOS
Building library & examples
+2 -35
View File
@@ -92,42 +92,9 @@ Monitor Ethernet Status
TC10
====
.. code-block:: python
:download:`Download example <../../examples/python/tc10/tc10.py>`
import icsneopy
import time
devices: list[icsneopy.Device] = icsneopy.find_all_devices()
device: icsneopy.Device = devices[0]
print(f"using {device} for TC10")
device.open()
netid = icsneopy.Network.NetID.AE_01
if device.supports_tc10():
# initial
status = device.get_tc10_status(netid)
print(f"initial status: wake: {status.wakeStatus}, sleep: {status.sleepStatus}")
time.sleep(1)
# sleep
device.request_tc10_sleep(netid)
print("waiting 1s for sleep to occur")
time.sleep(1)
status = device.get_tc10_status(netid)
print(f"post sleep status: wake: {status.wakeStatus}, sleep: {status.sleepStatus}")
# wake
device.request_tc10_wake(netid)
print("waiting 1s for wake to occur")
time.sleep(1)
status = device.get_tc10_status(netid)
print(f"post wake status: wake: {status.wakeStatus}, sleep: {status.sleepStatus}")
else:
print(f"{device} does not support TC10")
.. literalinclude:: ../../examples/python/tc10/tc10.py
DoIP Ethernet Activation
========================
+1 -1
View File
@@ -52,7 +52,7 @@ Although the example program will build without successfully completing the step
First, we are going to build the icsneoc library into a .so file that we can later use in order to access the library functions.
1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libusb-1.0-0-dev libpcap0.8-dev`
1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libpcap0.8-dev`
2. Change directories to `libicsneo-examples/third-party/libicsneo` and create a build directory by running `mkdir -p build`
3. Enter the build directory with `cd build`
4. Run `cmake ..` to generate your Makefile.
+1 -1
View File
@@ -52,7 +52,7 @@ Although the example program will build without successfully completing the step
First, we are going to build the icsneoc library into a .so file that we can later use in order to access the library functions.
1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libusb-1.0-0-dev libpcap0.8-dev`
1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libpcap0.8-dev`
2. Change directories to `libicsneo-examples/third-party/libicsneo` and create a build directory by running `mkdir -p build`
3. Enter the build directory with `cd build`
4. Run `cmake ..` to generate your Makefile.
+1 -1
View File
@@ -52,7 +52,7 @@ Although the example program will build without successfully completing the step
First, we are going to build the icsneoc library into a .so file that we can later use in order to access the library functions.
1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libusb-1.0-0-dev libpcap0.8-dev`
1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libpcap0.8-dev`
2. Change directories to `libicsneo-examples/third-party/libicsneo` and create a build directory by running `mkdir -p build`
3. Enter the build directory with `cd build`
4. Run `cmake ..` to generate your Makefile.
+1 -1
View File
@@ -32,7 +32,7 @@ If you haven't done this, `third-party/libicsneo` will be empty and you won't be
### Ubuntu 18.04 LTS
1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libusb-1.0-0-dev libpcap0.8-dev`
1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libpcap0.8-dev`
2. Change directories to your `libicsneo-examples/libicsneocpp-example` folder and create a build directory by running `mkdir -p build`
3. Enter the build directory with `cd build`
4. Run `cmake ..` to generate your Makefile.
@@ -35,6 +35,8 @@ int main() {
msg->appendSignalArg(icsneo::LiveDataValueType::GPS_LATITUDE);
msg->appendSignalArg(icsneo::LiveDataValueType::GPS_LONGITUDE);
msg->appendSignalArg(icsneo::LiveDataValueType::GPS_ACCURACY);
msg->appendSignalArg(icsneo::LiveDataValueType::DAQ_ENABLE);
msg->appendSignalArg(icsneo::LiveDataValueType::MANUAL_TRIGGER);
msg->cmd = icsneo::LiveDataCommand::SUBSCRIBE;
msg->handle = icsneo::LiveDataUtil::getNewHandle();
msg->updatePeriod = std::chrono::milliseconds(100);
@@ -77,6 +79,26 @@ int main() {
}));
// Run handler for three seconds to observe the signal data
std::this_thread::sleep_for(std::chrono::seconds(3));
double val = 0;
for (unsigned int i = 0; i < 10; ++i) {
// Set the values of signals we're watching so we can see them change live
auto setValMsg = std::make_shared<icsneo::LiveDataSetValueMessage>();
setValMsg->cmd = icsneo::LiveDataCommand::SET_VALUE;
setValMsg->handle = msg->handle;
// Convert the value format
icsneo::LiveDataValue ldValueDAQEnable;
icsneo::LiveDataValue ldValueManTrig;
if (!icsneo::LiveDataUtil::liveDataDoubleToValue(val / 3, ldValueDAQEnable) ||
!icsneo::LiveDataUtil::liveDataDoubleToValue(val, ldValueManTrig)) {
break;
}
setValMsg->appendSetValue(icsneo::LiveDataValueType::DAQ_ENABLE, ldValueDAQEnable);
setValMsg->appendSetValue(icsneo::LiveDataValueType::MANUAL_TRIGGER, ldValueManTrig);
device->setValueLiveData(setValMsg);
++val;
// Run handler for three seconds to observe the signal data
std::this_thread::sleep_for(std::chrono::seconds(3));
}
// Unsubscribe from the GPS signals and run handler for one more second
// Unsubscription only requires a valid in-use handle, in this case from our previous subscription
ret = device->unsubscribeLiveData(msg->handle);
+1 -1
View File
@@ -32,7 +32,7 @@ If you haven't done this, `third-party/libicsneo` will be empty and you won't be
### Ubuntu 18.04 LTS
1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libusb-1.0-0-dev libpcap0.8-dev`
1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libpcap0.8-dev`
2. Change directories to your `libicsneo-examples/libicsneocpp-example` folder and create a build directory by running `mkdir -p build`
3. Enter the build directory with `cd build`
4. Run `cmake ..` to generate your Makefile.
+85
View File
@@ -0,0 +1,85 @@
import icsneopy
import argparse
def main():
parser = get_parser()
args = parser.parse_args()
run_test(args)
def find_device(serial: str) -> icsneopy.Device:
devices = icsneopy.find_all_devices()
for d in devices:
if d.get_serial() == serial:
print(f"opening device {serial}")
return d
return None
def run_test(args):
# find the device
d = find_device(args.serial)
if d is None:
print(f"error: unable to find device {args.serial}")
exit(1)
# open the device
if not d.open():
print(f"error: unable to open device {args.serial}")
exit(1)
# check if TC10 is supported
if not d.supports_tc10():
print(f"error: device does not support TC10 {args.serial}")
exit(1)
# send the request on all networks
for n in args.networks:
net = getattr(icsneopy.Network.NetID, n)
if args.send_wake:
print(f"requesting TC10 wake on network {net}")
if not d.request_tc10_wake(net):
print(f"error: unable to send TC10 wake on device {args.serial}")
exit(1)
elif args.send_sleep:
print(f"requesting TC10 sleep on network {net}")
if not d.request_tc10_sleep(net):
print(f"error: unable to send TC10 sleep on device {args.serial}")
exit(1)
# close the device
print(f"closing device {args.serial}")
d.close()
def get_parser():
parser = argparse.ArgumentParser(description="TC10 wake request")
parser.add_argument(
"serial",
help="The serial number of the device",
)
parser.add_argument(
"--networks",
nargs="+",
help="List of icsneopy networks to use. Multiple networks accepted, e.g. '--networks ETHERNET_01 AE_01'",
required=True,
),
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
"--send-wake",
help="Trigger TC10 wake on the selected networks",
action="store_true",
default=False,
)
group.add_argument(
"--send-sleep",
help="Trigger TC10 sleep on the selected networks",
action="store_true",
default=False,
)
return parser
if __name__ == "__main__":
main()
+12 -35
View File
@@ -51,6 +51,9 @@ public:
WiVINotSupported = 0x1015,
RestrictedEntryFlag = 0x1016,
NotSupported = 0x1017,
FixedPointOverflow = 0x1018,
FixedPointPrecision = 0x1019,
SyscallError = 0x1020, // check errno/GetLastError() for details
// Device Events
PollingMessageOverflow = 0x2000,
@@ -111,6 +114,8 @@ public:
AppErrorParsingFailed = 0x2055,
GPTPNotSupported = 0x2056,
SettingNotAvaiableDevice = 0x2057,
DiskFormatNotSupported = 0x2058,
DiskFormatInvalidCount = 0x2059,
// Transport Events
FailedToRead = 0x3000,
@@ -130,41 +135,6 @@ public:
SendToError = 0x3109,
MDIOMessageExceedsMaxLength = 0x3110,
// FTD3XX
FTOK = 0x4000, // placeholder
FTInvalidHandle = FTOK + 1,
FTDeviceNotFound = FTOK + 2,
FTDeviceNotOpened = FTOK + 3,
FTIOError = FTOK + 4,
FTInsufficientResources = FTOK + 5,
FTInvalidParameter = FTOK + 6,
FTInvalidBaudRate = FTOK + 7,
FTDeviceNotOpenedForErase = FTOK + 8,
FTDeviceNotOpenedForWrite = FTOK + 9,
FTFailedToWriteDevice = FTOK + 10,
FTEEPROMReadFailed = FTOK + 11,
FTEEPROMWriteFailed = FTOK + 12,
FTEEPROMEraseFailed = FTOK + 13,
FTEEPROMNotPresent = FTOK + 14,
FTEEPROMNotProgrammed = FTOK + 15,
FTInvalidArgs = FTOK + 16,
FTNotSupported = FTOK + 17,
FTNoMoreItems = FTOK + 18,
FTTimeout = FTOK + 19,
FTOperationAborted = FTOK + 20,
FTReservedPipe = FTOK + 21,
FTInvalidControlRequestDirection = FTOK + 22,
FTInvalidControlRequestType = FTOK + 23,
FTIOPending = FTOK + 24,
FTIOIncomplete = FTOK + 25,
FTHandleEOF = FTOK + 26,
FTBusy = FTOK + 27,
FTNoSystemResources = FTOK + 28,
FTDeviceListNotReady = FTOK + 29,
FTDeviceNotConnected = FTOK + 30,
FTIncorrectDevicePath = FTOK + 31,
FTOtherError = FTOK + 32,
// VSA
VSABufferCorrupted = 0x5000,
VSATimestampNotFound = VSABufferCorrupted + 1,
@@ -187,6 +157,13 @@ public:
ServdNoDataError = ServdBindError + 9,
ServdJoinMulticastError = ServdBindError + 10,
// DXX
DXXErrorSys = 0x6100,
DXXErrorInt = 0x6101,
DXXErrorOverflow = 0x6102,
DXXErrorIO = 0x6103,
DXXErrorArg = 0x6104,
NoErrorFound = 0xFFFFFFFD,
TooManyEvents = 0xFFFFFFFE,
Unknown = 0xFFFFFFFF
+1 -1
View File
@@ -20,7 +20,7 @@ public:
EventCallback(std::shared_ptr<EventFilter> f, fn_eventCallback cb) : callback(cb), filter(f) {}
EventCallback(EventFilter f, fn_eventCallback cb) : callback(cb), filter(std::make_shared<EventFilter>(f)) {}
virtual bool callIfMatch(const std::shared_ptr<APIEvent>& event) const {
bool callIfMatch(const std::shared_ptr<APIEvent>& event) const {
bool ret = filter->match(*event);
if(ret)
callback(event);
+53
View File
@@ -0,0 +1,53 @@
#ifndef __PERIODIC_H__
#define __PERIODIC_H__
#ifdef __cplusplus
#include <condition_variable>
#include <mutex>
#include <functional>
#include <chrono>
#include <thread>
namespace icsneo {
class Periodic {
public:
using Callback = std::function<bool(void)>;
Periodic(Callback&& callback, const std::chrono::milliseconds& period) :
thread(&Periodic::loop, this, std::move(callback), period)
{}
~Periodic() {
{
std::scoped_lock lk(mutex);
stop = true;
}
cv.notify_all();
thread.join();
}
private:
void loop(Callback&& callback, const std::chrono::milliseconds& period) {
while (true) {
{
std::unique_lock lk(mutex);
cv.wait_for(lk, period, [&]{ return stop; });
if(stop) {
break;
}
}
if (!callback()) {
break;
}
}
}
bool stop = false;
std::condition_variable cv;
std::mutex mutex;
std::thread thread;
};
} // icsneo
#endif // __cplusplus
#endif // __PERIODIC_H__
+1
View File
@@ -38,6 +38,7 @@ enum class Command : uint8_t {
FlexRayControl = 0xF3,
CoreMiniPreload = 0xF4, // Previously known as RED_CMD_COREMINI_PRELOAD
PHYControlRegisters = 0xEF,
EnableLogData = 0xF6,
};
enum class ExtendedCommand : uint16_t {
+20
View File
@@ -19,6 +19,7 @@ enum class LiveDataCommand : uint32_t {
UNSUBSCRIBE,
RESPONSE,
CLEAR_ALL,
SET_VALUE,
};
enum class LiveDataStatus : uint32_t {
@@ -41,10 +42,13 @@ enum class LiveDataValueType : uint32_t {
GPS_SPEED,
GPS_VALID,
GPS_ENABLE = 62,
MANUAL_TRIGGER = 108,
TIME_SINCE_MSG = 111,
GPS_ACCURACY = 120,
GPS_BEARING = 121,
GPS_TIME = 122,
GPS_TIME_VALID = 123,
DAQ_ENABLE = 124,
};
inline std::ostream& operator<<(std::ostream& os, const LiveDataCommand cmd) {
@@ -54,6 +58,7 @@ inline std::ostream& operator<<(std::ostream& os, const LiveDataCommand cmd) {
case LiveDataCommand::UNSUBSCRIBE: return os << "Unsubscribe";
case LiveDataCommand::RESPONSE: return os << "Response";
case LiveDataCommand::CLEAR_ALL: return os << "Clear All";
case LiveDataCommand::SET_VALUE: return os << "Set Value";
}
return os;
}
@@ -81,6 +86,9 @@ inline std::ostream& operator<<(std::ostream& os, const LiveDataValueType cmd) {
case LiveDataValueType::GPS_BEARING: return os << "GPS Bearing";
case LiveDataValueType::GPS_TIME: return os << "GPS Time";
case LiveDataValueType::GPS_TIME_VALID: return os << "GPS Time Valid";
case LiveDataValueType::DAQ_ENABLE: return os << "DAQ Enable";
case LiveDataValueType::MANUAL_TRIGGER: return os << "Manual Trigger";
case LiveDataValueType::TIME_SINCE_MSG: return os << "Time Since Msg";
}
return os;
}
@@ -127,6 +135,17 @@ struct LiveDataSubscribe : public LiveDataHeader {
LiveDataArgument args[1];
};
struct LiveDataSetValueEntry
{
LiveDataArgument arg;
LiveDataValue value;
};
struct LiveDataSetValue : public LiveDataHeader {
uint32_t numSetValues;
LiveDataSetValueEntry values[1];
};
struct ExtResponseHeader {
ExtendedCommand command;
uint16_t length;
@@ -138,6 +157,7 @@ namespace LiveDataUtil
LiveDataHandle getNewHandle();
double liveDataValueToDouble(const LiveDataValue& val);
bool liveDataDoubleToValue(const double& dFloat, LiveDataValue& value);
static constexpr uint32_t LiveDataVersion = 1;
} // namespace LiveDataUtil
@@ -10,11 +10,12 @@ namespace icsneo {
class ExtendedResponseMessage : public Message {
public:
ExtendedResponseMessage(ExtendedCommand cmd, ExtendedResponse resp)
ExtendedResponseMessage(ExtendedCommand cmd, ExtendedResponse resp = ExtendedResponse::OK)
: Message(Message::Type::ExtendedResponse), command(cmd), response(resp) {}
const ExtendedCommand command;
const ExtendedResponse response;
std::vector<uint8_t> data;
#pragma pack(push, 2)
struct ResponseHeader {
@@ -15,20 +15,36 @@ namespace icsneo {
class ExtendedResponseFilter : public MessageFilter {
public:
ExtendedResponseFilter(icsneo::ExtendedResponse resp) : MessageFilter(Message::Type::ExtendedResponse), response(resp) {}
ExtendedResponseFilter(icsneo::ExtendedCommand cmd) : MessageFilter(Message::Type::ExtendedResponse), command(cmd), response(std::nullopt) {}
ExtendedResponseFilter(icsneo::ExtendedResponse resp) : MessageFilter(Message::Type::ExtendedResponse), command(std::nullopt), response(resp) {}
ExtendedResponseFilter(icsneo::ExtendedCommand cmd, icsneo::ExtendedResponse resp)
: MessageFilter(Message::Type::ExtendedResponse), command(cmd), response(resp) {}
bool match(const std::shared_ptr<Message>& message) const override {
if(!MessageFilter::match(message)) {
return false;
}
const auto respMsg = std::static_pointer_cast<ExtendedResponseMessage>(message);
return respMsg && matchResponse(respMsg->response);
return respMsg && matchResponse(respMsg->command, respMsg->response);
}
private:
icsneo::ExtendedResponse response;
bool matchResponse(icsneo::ExtendedResponse resp) const {
return response == resp;
std::optional<icsneo::ExtendedCommand> command = std::nullopt;
std::optional<icsneo::ExtendedResponse> response = std::nullopt;
bool matchResponse(icsneo::ExtendedCommand cmd, icsneo::ExtendedResponse resp) const {
if(command) {
if(*command != cmd) {
return false;
}
}
if(response) {
if(*response != resp) {
return false;
}
}
return true;
}
};
@@ -38,6 +38,14 @@ public:
LiveDataStatus status;
};
class LiveDataSetValueMessage : public LiveDataMessage {
public:
LiveDataSetValueMessage() {}
std::vector<std::shared_ptr<LiveDataArgument>> args;
std::vector<std::shared_ptr<LiveDataValue>> values;
void appendSetValue(LiveDataValueType valueType, const LiveDataValue& value);
};
} // namespace icsneo
#endif // __cplusplus
@@ -0,0 +1,26 @@
#ifndef __LOGDATAMESSAGE_H_
#define __LOGDATAMESSAGE_H_
#ifdef __cplusplus
#include "icsneo/communication/message/message.h"
#include <cstdint>
#include <memory>
namespace icsneo {
class LogDataMessage : public RawMessage {
public:
static std::shared_ptr<LogDataMessage> DecodeToMessage(const std::vector<uint8_t>& bytestream);
LogDataMessage(std::wstring logDataString) :
RawMessage(Message::Type::LogData, Network::NetID::Data_To_Host), logMessage(logDataString) {}
std::wstring logMessage;
};
}
#endif // __cplusplus
#endif
@@ -44,6 +44,7 @@ public:
AppError = 0x8012,
GPTPStatus = 0x8013,
EthernetStatus = 0x8014,
LogData = 0x8015,
};
Message(Type t) : type(t) {}
@@ -17,11 +17,11 @@ public:
bool isCoreminiRunning = false;
uint32_t sectorOverflows = 0;
uint32_t numRemainingSectorBuffers = 0;
int32_t lastSector = 0;
int32_t readBinSize = 0;
int32_t minSector = 0;
int32_t maxSector = 0;
int32_t currentSector = 0;
uint32_t lastSector = 0;
uint32_t readBinSize = 0;
uint32_t minSector = 0;
uint32_t maxSector = 0;
uint32_t currentSector = 0;
uint64_t coreminiCreateTime = 0;
uint16_t fileChecksum = 0;
uint16_t coreminiVersion = 0;
@@ -4,24 +4,13 @@
#ifdef __cplusplus
#include "icsneo/communication/message/message.h"
#include "icsneo/communication/tc10.h"
#include <memory>
namespace icsneo
{
enum class TC10WakeStatus : uint8_t {
NoWakeReceived,
WakeReceived,
};
enum class TC10SleepStatus : uint8_t {
NoSleepReceived,
SleepReceived,
SleepFailed,
SleepAborted,
};
class TC10StatusMessage : public Message {
public:
static std::shared_ptr<TC10StatusMessage> DecodeToMessage(const std::vector<uint8_t>& bytestream);
+5
View File
@@ -143,6 +143,7 @@ public:
I2C_03 = 518, // previously I2C3
I2C_04 = 519, // previously I2C4
ETHERNET_02 = 520, // previously Ethernet2
ETHERNET_TX_WRAP = 521,
A2B_01 = 522, // previously A2B1
A2B_02 = 523, // previously A2B2
ETHERNET_03 = 524, // previously Ethernet3
@@ -583,6 +584,7 @@ public:
case NetID::RED_GET_RTC:
case NetID::DiskData:
case NetID::RED_App_Error:
case NetID::Data_To_Host:
return Type::Internal;
case NetID::Invalid:
case NetID::Any:
@@ -590,6 +592,7 @@ public:
case NetID::ETHERNET_01:
case NetID::ETHERNET_DAQ:
case NetID::ETHERNET_02:
case NetID::ETHERNET_TX_WRAP:
case NetID::ETHERNET_03:
case NetID::AE_01:
case NetID::AE_02:
@@ -886,6 +889,8 @@ public:
return "I2C 04";
case NetID::ETHERNET_02:
return "Ethernet 02";
case NetID::ETHERNET_TX_WRAP:
return "Ethernet TX Wrap";
case NetID::A2B_01:
return "A2B 01";
case NetID::A2B_02:
@@ -7,6 +7,9 @@
#include "icsneo/api/eventmanager.h"
#include <cstdint>
#include <memory>
#include <iostream>
#include <iomanip>
#include <sstream>
namespace icsneo {
@@ -18,30 +21,59 @@ struct HardwareEthernetPacket {
static std::shared_ptr<EthernetMessage> DecodeToMessage(const std::vector<uint8_t>& bytestream, const device_eventhandler_t& report);
static bool EncodeFromMessage(const EthernetMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report);
// Word 0 - Header flags (offset 0)
struct {
icscm_bitfield FCS_AVAIL : 1;
icscm_bitfield RUNT_FRAME : 1;
icscm_bitfield DISABLE_PADDING : 1;
icscm_bitfield ENABLE_PADDING : 1;
icscm_bitfield PREEMPTION_ENABLED : 1;
icscm_bitfield MPACKET_TYPE : 4;
icscm_bitfield MPACKET_FRAG_CNT : 2;
icscm_bitfield : 6;
icscm_bitfield UPDATE_CHECKSUMS : 1;
icscm_bitfield FCS_OVERRIDE : 1;
icscm_bitfield CRC_ERROR : 1;
icscm_bitfield FCS_VERIFIED : 1;
icscm_bitfield DISABLE_TX_RECEIPT : 1;
icscm_bitfield T1S_ETHERNET : 1;
} header;
// Word 1 - Extended ID flags
struct {
icscm_bitfield txlen : 12;
icscm_bitfield TXMSG : 1;
icscm_bitfield : 3;
icscm_bitfield TXAborted : 1;
icscm_bitfield T1S_SYMBOL : 1;
icscm_bitfield T1S_BURST : 1;
} eid;
icscm_bitfield reserved;
unsigned char data[8];
// Word 2 - T1S Status flags
struct {
icscm_bitfield TXCollision : 1;
icscm_bitfield T1SWake : 1;
icscm_bitfield : 14;
} t1s_status;
// Word 3 - T1S Node Information
struct {
icscm_bitfield T1S_BURST_COUNT : 8;
icscm_bitfield T1S_NODE_ID : 8;
uint8_t RESERVED[6];
} t1s_node;
// Words 4-7 - Reserved/Padding
uint16_t stats;
// Words 8-11 - Timestamp (offset 14-22)
struct {
uint64_t TS : 60;
uint64_t : 3; // Reserved for future status bits
uint64_t : 3;
uint64_t IsExtended : 1;
} timestamp;
uint16_t NetworkID;
uint16_t Length;
};
#pragma pack(pop)
@@ -50,4 +82,4 @@ struct HardwareEthernetPacket {
#endif // __cplusplus
#endif
#endif // __ETHERNETPACKET_H__
@@ -47,11 +47,11 @@ struct ScriptStatus
CoreMiniStatus status;
uint32_t sectorOverflows;
uint32_t numRemainingSectorBuffers;
int32_t lastSector;
int32_t readBinSize;
int32_t minSector;
int32_t maxSector;
int32_t currentSector;
uint32_t lastSector;
uint32_t readBinSize;
uint32_t minSector;
uint32_t maxSector;
uint32_t currentSector;
uint32_t coreminiCreateTimeMsb;
uint32_t coreminiCreateTimeLsb;
uint16_t zero2;
+47
View File
@@ -0,0 +1,47 @@
#ifndef __ICSNEO_TC10_H_
#define __ICSNEO_TC10_H_
#ifdef __cplusplus
#include <cstdint>
namespace icsneo {
enum class TC10WakeStatus : uint8_t {
NoWakeReceived,
WakeReceived,
};
enum class TC10SleepStatus : uint8_t {
NoSleepReceived,
SleepReceived,
SleepFailed,
SleepAborted,
};
}
#endif // __cplusplus
#ifdef __ICSNEOC_H_
typedef enum _neotc10wakestatus_t {
ICSNEO_TC10_NO_WAKE_RECEIVED = 0,
ICSNEO_TC10_WAKE_RECEIVED = 1,
} neotc10wakestatus_t;
typedef enum _neotc10sleepstatus_t {
ICSNEO_TC10_NO_SLEEP_RECEIVED = 0,
ICSNEO_TC10_SLEEP_RECEIVED = 1,
ICSNEO_TC10_SLEEP_FAILED = 2,
ICSNEO_TC10_SLEEP_ABORTED = 3,
} neotc10sleepstatus_t;
typedef struct _neotc10status_t {
neotc10wakestatus_t wakeStatus;
neotc10sleepstatus_t sleepStatus;
} neotc10status_t;
#endif
#endif // __ICSNEO_TC10_H_
+30 -2
View File
@@ -16,6 +16,7 @@
#include <chrono>
#include "icsneo/api/eventmanager.h"
#include "icsneo/api/lifetime.h"
#include "icsneo/api/periodic.h"
#include "icsneo/device/neodevice.h"
#include "icsneo/device/idevicesettings.h"
#include "icsneo/device/nullsettings.h"
@@ -26,6 +27,7 @@
#include "icsneo/disk/diskreaddriver.h"
#include "icsneo/disk/diskwritedriver.h"
#include "icsneo/disk/nulldiskdriver.h"
#include "icsneo/disk/diskdetails.h"
#include "icsneo/communication/communication.h"
#include "icsneo/communication/packetizer.h"
#include "icsneo/communication/encoder.h"
@@ -151,6 +153,8 @@ public:
virtual bool isDisconnected() const { return com->isDisconnected(); }
virtual bool goOnline();
virtual bool goOffline();
virtual bool enableLogData();
virtual bool disableLogData();
enum class PreloadReturn : uint8_t
{
@@ -179,6 +183,10 @@ public:
return std::nullopt;
}
bool supportsCoreminiScript() const {
return (getCoreminiStartAddressFlash()) || (getCoreminiStartAddressSD());
}
std::optional<MemoryAddress> getCoreminiStartAddress(Disk::MemoryType memType) const {
switch(memType) {
case Disk::MemoryType::Flash:
@@ -199,7 +207,7 @@ public:
// Message polling related functions
bool enableMessagePolling();
bool enableMessagePolling(std::optional<MessageFilter> filter = std::nullopt);
bool disableMessagePolling();
bool isMessagePollingEnabled() { return messagePollingCallbackID != 0; };
std::pair<std::vector<std::shared_ptr<Message>>, bool> getMessages();
@@ -630,6 +638,23 @@ public:
bool subscribeLiveData(std::shared_ptr<LiveDataCommandMessage> message);
bool unsubscribeLiveData(const LiveDataHandle& handle);
bool clearAllLiveData();
bool setValueLiveData(std::shared_ptr<LiveDataSetValueMessage> message);
enum class DiskFormatDirective : uint8_t {
Continue,
Stop
};
using DiskFormatProgress = std::function<DiskFormatDirective(uint64_t sectorsFormatted, uint64_t sectorsTotal)>;
bool formatDisk(
const DiskDetails& config,
const DiskFormatProgress& handler = {},
std::chrono::milliseconds interval = std::chrono::milliseconds(500) /** Send updates at an interval of 500ms */
);
bool forceDiskConfigUpdate(const DiskDetails& config); // Forces a disk layout and enables change without formatting
std::shared_ptr<DiskDetails> getDiskDetails(std::chrono::milliseconds timeout = std::chrono::milliseconds(100));
virtual size_t getDiskCount() const { return 0; }
bool supportsDiskFormatting() const { return getDiskCount() != 0; }
// VSA Read functions
@@ -1013,7 +1038,10 @@ private:
*/
std::optional<uint64_t> getVSADiskSize();
bool enableNetworkCommunication(bool enable);
bool enableNetworkCommunication(bool enable, uint32_t timeout = 0);
// Keeponline (keepalive for online)
std::unique_ptr<Periodic> keeponline;
};
}
+5
View File
@@ -13,7 +13,12 @@ struct DeviceAppVersion {
uint8_t major = 0;
uint8_t minor = 0;
DeviceAppVersion() = default;
DeviceAppVersion(const uint8_t major, const uint8_t minor)
: major(major), minor(minor) {}
bool operator!=(const DeviceAppVersion& rhs) const { return major != rhs.major || minor != rhs.minor; }
bool operator==(const DeviceAppVersion& rhs) const { return major == rhs.major && minor == rhs.minor; }
};
} // namespace icsneo
+34 -2
View File
@@ -668,6 +668,37 @@ typedef struct
} Fire3LinuxSettings;
#define FIRE3LINUXSETTINGS_SIZE 8
static_assert(sizeof(Fire3LinuxSettings) == FIRE3LINUXSETTINGS_SIZE, "Fire3LinuxSettings is the wrong size!");
/* Define number of CMP streams per device*/
#define CMP_STREAMS_FIRE3 (10)
#define CMP_STREAMS_FIRE3FR (10)
#define CMP_STREAMS_RED2 (10)
#define CMP_STREAMS_A2B (3)
#define CMP_STREAMS_GIGASTAR (10)
/* CMP Network Enables */
typedef struct
{
uint8_t bStreamEnabled : 1;
uint8_t EthModule : 2;
uint8_t bControlEnabled : 1;
uint8_t spare : 4;
uint8_t streamId;
uint8_t dstMac[6];
uint64_t network_enables_1;
uint64_t network_enables_2;
} CMP_NETWORK_DATA;
/* Global CMP Data */
typedef struct
{
uint8_t cmp_enabled : 1;
uint8_t sparebits : 7;
uint8_t spare;
uint16_t cmp_device_id;
} CMP_GLOBAL_DATA;
#pragma pack(pop)
#ifdef __cplusplus
@@ -690,7 +721,7 @@ public:
IDeviceSettings(std::shared_ptr<Communication> com, size_t size) : com(com), report(com->report), structSize(size) {}
virtual ~IDeviceSettings() {}
bool ok() { return !disabled && settingsLoaded; }
bool ok() const { return !disabled && settingsLoaded; }
virtual bool refresh(bool ignoreChecksum = false); // Get from device
@@ -893,7 +924,8 @@ public:
template<typename T> T getStructure() const { return *getStructurePointer<T>(); }
template<typename T> bool applyStructure(const T& newStructure);
const size_t& getSize() const { return structSize; }
size_t getSize() const { return structSize; }
size_t getSerialSize() const { return settings.size(); }
// if settings are disabled for this device. always false unless constructed null
bool disabled = false;
@@ -70,6 +70,10 @@ protected:
}
bool supportsGPTP() const override { return true; }
size_t getDiskCount() const override {
return 2;
}
};
@@ -11,7 +11,7 @@ namespace icsneo {
class NeoVIFIRE : public Device {
public:
// USB PID is 0x0701, standard driver is FTDI
// USB PID is 0x0701, standard driver is DXX
ICSNEO_FINDABLE_DEVICE_BY_PID(NeoVIFIRE, DeviceType::FIRE, 0x0701);
static const std::vector<Network>& GetSupportedNetworks() {
@@ -12,7 +12,7 @@ namespace icsneo {
class NeoVIFIRE2 : public Device {
public:
// Serial numbers start with CY
// USB PID is 0x1000, standard driver is FTDI
// USB PID is 0x1000, standard driver is DXX
// Ethernet MAC allocation is 0x04, standard driver is Raw
ICSNEO_FINDABLE_DEVICE(NeoVIFIRE2, DeviceType::FIRE2, "CY");
@@ -141,6 +141,10 @@ protected:
bool supportsEraseMemory() const override {
return true;
}
size_t getDiskCount() const override {
return 1;
}
};
}
@@ -49,6 +49,7 @@ public:
};
return supportedNetworks;
}
size_t getEthernetActivationLineCount() const override { return 2; }
protected:
NeoVIFIRE3(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) {
initialize<NeoVIFIRE3Settings, Disk::ExtExtractorDiskReadDriver, Disk::NeoMemoryDiskDriver>(makeDriver);
@@ -90,6 +91,10 @@ protected:
return true;
}
size_t getDiskCount() const override {
return 2;
}
};
}
@@ -75,6 +75,10 @@ protected:
bool supportsEraseMemory() const override {
return true;
}
size_t getDiskCount() const override {
return 2;
}
};
}
@@ -79,7 +79,15 @@ typedef struct {
uint16_t digitalIoThresholdEnable;
uint16_t misc_io_initial_ddr;
uint16_t misc_io_initial_latch;
ETHERNET_SETTINGS2 ethernet2;
ETHERNET_SETTINGS2 ethernet2_1;
ETHERNET_SETTINGS ethernet_2;
ETHERNET_SETTINGS2 ethernet2_2;
Fire3LinuxSettings os_settings;
RAD_GPTP_SETTINGS gPTP;
uint16_t iso_tester_pullup_enable;
CMP_GLOBAL_DATA cmp_global_data;
CMP_NETWORK_DATA cmp_stream_data[CMP_STREAMS_RED2];
uint32_t networkTimeSync;
} neovired2_settings_t;
typedef struct {
@@ -12,7 +12,7 @@ namespace icsneo {
class NeoVIION : public Plasion {
public:
// USB PID is 0x0901, standard driver is FTDI
// USB PID is 0x0901, standard driver is DXX
ICSNEO_FINDABLE_DEVICE_BY_PID(NeoVIION, DeviceType::ION, 0x0901);
private:
@@ -10,7 +10,7 @@ namespace icsneo {
class NeoVIPLASMA : public Plasion {
public:
// USB PID is 0x0801, standard driver is FTDI
// USB PID is 0x0801, standard driver is DXX
ICSNEO_FINDABLE_DEVICE_BY_PID(NeoVIPLASMA, DeviceType::PLASMA, 0x0801);
private:
+1 -1
View File
@@ -15,7 +15,7 @@ namespace icsneo {
class RADA2B : public Device {
public:
// Serial numbers start with AB
// USB PID is 0x0006, standard driver is FTDI
// USB PID is 0x0006, standard driver is DXX
// Ethernet MAC allocation is 0x18, standard driver is Raw
ICSNEO_FINDABLE_DEVICE(RADA2B, DeviceType::RAD_A2B, "AB");
@@ -12,7 +12,7 @@ class RADComet : public RADCometBase {
public:
// Serial numbers start with RC
// USB PID is 0x1207, standard driver is FTDI3
// USB PID is 0x1207, standard driver is DXX
// Ethernet MAC allocation is 0x1D, standard driver is Raw
ICSNEO_FINDABLE_DEVICE_BY_SERIAL_RANGE(RADComet, DeviceType::RADComet, "RC0000", "RC0299");
@@ -12,7 +12,7 @@ class RADComet2 : public RADCometBase {
public:
// Serial numbers start with RC, Comet2 starts at RC0300
// USB PID is 0x1207, standard driver is FTDI3
// USB PID is 0x1207, standard driver is DXX
// Ethernet MAC allocation is 0x1D, standard driver is Raw
ICSNEO_FINDABLE_DEVICE_BY_SERIAL_RANGE(RADComet2, DeviceType::RADComet, "RC0300", "RCZZZZ");
@@ -12,7 +12,7 @@ class RADComet3 : public Device {
public:
// Serial numbers start with C3
// USB PID is 0x1208, standard driver is FTDI3
// USB PID is 0x1208, standard driver is DXX
// Ethernet MAC allocation is 0x20, standard driver is Raw
ICSNEO_FINDABLE_DEVICE(RADComet3, DeviceType::RADComet3, "C3");
@@ -54,6 +54,10 @@ protected:
bool supportsEraseMemory() const override {
return true;
}
size_t getDiskCount() const override {
return 1;
}
};
}
@@ -107,6 +107,10 @@ protected:
std::optional<MemoryAddress> getCoreminiStartAddressSD() const override {
return 0;
}
size_t getDiskCount() const override {
return 1;
}
};
}
@@ -14,7 +14,7 @@ namespace icsneo {
class RADGigastar : public Device {
public:
// Serial numbers start with GS
// USB PID is 0x1204, standard driver is FTDI3
// USB PID is 0x1204, standard driver is DXX
// Ethernet MAC allocation is 0x0F, standard driver is Raw
ICSNEO_FINDABLE_DEVICE(RADGigastar, DeviceType::RADGigastar, "GS");
@@ -120,6 +120,10 @@ protected:
std::optional<MemoryAddress> getCoreminiStartAddressSD() const override {
return 0;
}
size_t getDiskCount() const override {
return 1;
}
};
}
@@ -16,7 +16,7 @@ namespace icsneo
{
public:
// Serial numbers start with GT
// USB PID is 0x1210, standard driver is FTDI3
// USB PID is 0x1210, standard driver is DXX
// Ethernet MAC allocation is 0x22, standard driver is Raw
ICSNEO_FINDABLE_DEVICE(RADGigastar2, DeviceType::RADGigastar2, "GT");
@@ -52,6 +52,12 @@ namespace icsneo
Network::NetID::LIN_08,
Network::NetID::LIN_09,
Network::NetID::LIN_10,
Network::NetID::LIN_11,
Network::NetID::LIN_12,
Network::NetID::LIN_13,
Network::NetID::LIN_14,
Network::NetID::LIN_15,
Network::NetID::LIN_16,
Network::NetID::I2C_01,
Network::NetID::I2C_02,
@@ -131,6 +137,11 @@ namespace icsneo
{
return 0;
}
size_t getDiskCount() const override
{
return 1;
}
};
}
@@ -127,12 +127,18 @@ namespace icsneo
ETHERNET_SETTINGS2 ethT1s8;
ETHERNET10T1S_SETTINGS t1s8;
ETHERNET10T1S_SETTINGS_EXT t1s8Ext;
LIN_SETTINGS lin11;
LIN_SETTINGS lin12;
LIN_SETTINGS lin13;
LIN_SETTINGS lin14;
LIN_SETTINGS lin15;
LIN_SETTINGS lin16;
} radgigastar2_settings_t;
#pragma pack(pop)
#ifdef __cplusplus
static_assert(sizeof(radgigastar2_settings_t) == 2024, "RADGigastar2 settings size mismatch");
static_assert(sizeof(radgigastar2_settings_t) == 2084, "RADGigastar2 settings size mismatch");
#include <iostream>
@@ -215,6 +221,18 @@ namespace icsneo
return &(cfg->lin9);
case Network::NetID::LIN_10:
return &(cfg->lin10);
case Network::NetID::LIN_11:
return &(cfg->lin11);
case Network::NetID::LIN_12:
return &(cfg->lin12);
case Network::NetID::LIN_13:
return &(cfg->lin13);
case Network::NetID::LIN_14:
return &(cfg->lin14);
case Network::NetID::LIN_15:
return &(cfg->lin15);
case Network::NetID::LIN_16:
return &(cfg->lin16);
default:
return nullptr;
}
+1 -1
View File
@@ -14,7 +14,7 @@ namespace icsneo {
class RADMars : public Device {
public:
// Serial numbers start with GL (previously, RAD-Gigalog)
// USB PID is 0x1203, standard driver is FTDI3
// USB PID is 0x1203, standard driver is DXX
// Ethernet MAC allocation is 0x0A, standard driver is Raw
ICSNEO_FINDABLE_DEVICE(RADMars, DeviceType::RADMars, "GL");
@@ -11,7 +11,7 @@ namespace icsneo {
class RADMoon2 : public RADMoon2Base {
public:
// Serial numbers start with RM
// USB PID is 0x1202, standard driver is FTDI3
// USB PID is 0x1202, standard driver is DXX
ICSNEO_FINDABLE_DEVICE(RADMoon2, DeviceType::RADMoon2, "RM");
uint8_t getPhyAddrOrPort() const override { return 6; };
@@ -12,7 +12,7 @@ class RADMoonT1S : public Device {
public:
// Serial numbers start with MS
// USB PID is 0x1209, standard driver is FTDI3
// USB PID is 0x1209, standard driver is DXX
// Ethernet MAC allocation is 0x21, standard driver is Raw
ICSNEO_FINDABLE_DEVICE(RADMoonT1S, DeviceType::RADMoonT1S, "MS");
@@ -12,7 +12,7 @@ namespace icsneo {
class RADStar2 : public Device {
public:
// Serial numbers start with RS
// USB PID is 0x0005, standard driver is FTDI
// USB PID is 0x0005, standard driver is DXX
// Ethernet MAC allocation is 0x05, standard driver is Raw
ICSNEO_FINDABLE_DEVICE(RADStar2, DeviceType::RADStar2, "RS");
@@ -12,7 +12,7 @@ namespace icsneo {
class RADSupermoon : public Device {
public:
// Serial numbers start with SM
// USB PID is 0x1201, standard driver is FTDI3
// USB PID is 0x1201, standard driver is DXX
ICSNEO_FINDABLE_DEVICE(RADSupermoon, DeviceType::RADSupermoon, "SM");
enum class SKU {
@@ -11,7 +11,7 @@ namespace icsneo {
class ValueCAN3 : public Device {
public:
// USB PID is 0x0601, standard driver is FTDI
// USB PID is 0x0601, standard driver is DXX
ICSNEO_FINDABLE_DEVICE_BY_PID(ValueCAN3, DeviceType::VCAN3, 0x0601);
static const std::vector<Network>& GetSupportedNetworks() {
+44
View File
@@ -0,0 +1,44 @@
#ifndef __DISKDETAILS_H__
#define __DISKDETAILS_H__
#ifdef __cplusplus
#include <cstdint>
#include <vector>
#include <icsneo/api/eventmanager.h>
namespace icsneo {
enum class DiskLayout : uint8_t {
Spanned = 0,
RAID0 = 1
};
struct DiskInfo {
bool present; // Disk is connected
bool initialized; // Disk is initialized
bool formatted; // Disk is formatted
uint64_t sectors;
uint32_t bytesPerSector;
uint64_t size() const {
return sectors * bytesPerSector;
}
};
struct DiskDetails {
DiskLayout layout;
bool fullFormat;
std::vector<DiskInfo> disks;
static std::vector<uint8_t> Encode(const DiskDetails& config);
static std::shared_ptr<DiskDetails> Decode(const std::vector<uint8_t>& bytes, size_t diskCount, device_eventhandler_t report);
};
} // namespace icsneo
#endif // __cplusplus
#endif
+41
View File
@@ -9,6 +9,7 @@
#include "icsneo/platform/dynamiclib.h" // Dynamic library loading and exporting
#include "icsneo/communication/network.h" // Network type and netID defines
#include "icsneo/communication/io.h" // IO enum defines
#include "icsneo/communication/tc10.h"
#include "icsneo/api/version.h" // For version info
#include "icsneo/api/event.h" // For event and error info
@@ -845,6 +846,34 @@ extern bool DLLExport icsneo_setRTC(const neodevice_t* device, uint64_t input);
*/
extern bool DLLExport icsneo_isOnlineSupported(const neodevice_t* device);
/**
* \brief Request TC10 wake
* \param[in] device A pointer to the neodevice_t structure specifying the device to operate on.
* \param[in] netid The interface to request the TC10 wake on.
* \returns True if the device successfully sent the TC10 wake request.
*/
extern bool DLLExport icsneo_requestTC10Wake(const neodevice_t* device, neonetid_t netid);
/**
* \brief Request TC10 sleep
* \param[in] device A pointer to the neodevice_t structure specifying the device to operate on.
* \param[in] netid The interface to request the TC10 sleep on.
* \returns True if the device successfully sent the TC10 sleep request.
*/
extern bool DLLExport icsneo_requestTC10Sleep(const neodevice_t* device, neonetid_t netid);
/**
* \brief Query TC10 status
* \param[in] device A pointer to the neodevice_t structure specifying the device to operate on.
* \param[in] netid The interface to request the TC10 status for.
* \param[out] status The TC10 status for the given interface
* \returns True if the device successfully retreieved the TC10 status.
*/
extern bool DLLExport icsneo_getTC10Status(const neodevice_t* device, neonetid_t netid, neotc10status_t* status);
#ifdef __cplusplus
} // extern "C"
#endif
@@ -1028,6 +1057,15 @@ fn_icsneo_setRTC icsneo_setRTC;
typedef bool(*fn_icsneo_isOnlineSupported)(const neodevice_t* device);
fn_icsneo_isOnlineSupported icsneo_isOnlineSupported;
typedef bool(*fn_icsneo_requestTC10Wake)(const neodevice_t* device, neonetid_t netid);
fn_icsneo_requestTC10Wake icsneo_requestTC10Wake;
typedef bool(*fn_icsneo_requestTC10Sleep)(const neodevice_t* device, neonetid_t netid);
fn_icsneo_requestTC10Sleep icsneo_requestTC10Sleep;
typedef bool(*fn_icsneo_getTC10Status)(const neodevice_t* device, neonetid_t netid, neotc10status_t* status);
fn_icsneo_getTC10Status icsneo_getTC10Status;
#define ICSNEO_IMPORT(func) func = (fn_##func)icsneo_dynamicLibraryGetFunction(icsneo_libraryHandle, #func)
#define ICSNEO_IMPORTASSERT(func) if((ICSNEO_IMPORT(func)) == NULL) return 3
void* icsneo_libraryHandle = NULL;
@@ -1101,6 +1139,9 @@ int icsneo_init() {
ICSNEO_IMPORTASSERT(icsneo_getRTC);
ICSNEO_IMPORTASSERT(icsneo_setRTC);
ICSNEO_IMPORTASSERT(icsneo_isOnlineSupported);
ICSNEO_IMPORTASSERT(icsneo_requestTC10Wake);
ICSNEO_IMPORTASSERT(icsneo_requestTC10Sleep);
ICSNEO_IMPORTASSERT(icsneo_getTC10Status);
icsneo_initialized = true;
return 0;
+1
View File
@@ -20,6 +20,7 @@
#include "icsneo/communication/message/a2bmessage.h"
#include "icsneo/communication/message/linmessage.h"
#include "icsneo/communication/message/mdiomessage.h"
#include "icsneo/communication/message/logdatamessage.h"
#include "icsneo/communication/message/callback/streamoutput/a2bwavoutput.h"
+39
View File
@@ -0,0 +1,39 @@
#ifndef __DXX_H_
#define __DXX_H_
#ifdef __cplusplus
#include "icsneo/communication/driver.h"
#include "icsneo/device/founddevice.h"
#include "libredxx/libredxx.h"
namespace icsneo {
class DXX : public Driver {
public:
static void Find(std::vector<FoundDevice>& found);
DXX(const device_eventhandler_t& err, neodevice_t& forDevice, uint16_t pid, libredxx_device_type type);
bool open() override;
bool isOpen() override;
bool close() override;
private:
void read();
void write();
neodevice_t neodevice;
uint16_t pid;
libredxx_device_type type;
libredxx_opened_device* device = nullptr;
std::thread readThread;
std::thread writeThread;
};
}
#endif // __cplusplus
#endif // __DXX_H_
-35
View File
@@ -1,35 +0,0 @@
#ifndef __FTD3XX_H_
#define __FTD3XX_H_
#ifdef __cplusplus
#include <optional>
#include "icsneo/communication/driver.h"
#include "icsneo/device/founddevice.h"
namespace icsneo {
class FTD3XX : public Driver {
public:
static void Find(std::vector<FoundDevice>& foundDevices);
FTD3XX(const device_eventhandler_t& err, neodevice_t& forDevice);
~FTD3XX() override { if(isOpen()) close(); }
bool open() override;
bool isOpen() override;
bool close() override;
bool isEthernet() const override { return false; }
private:
neodevice_t& device;
std::optional<void*> handle;
std::thread readThread, writeThread;
void readTask();
void writeTask();
};
}
#endif // __cplusplus
#endif
-14
View File
@@ -1,14 +0,0 @@
#ifndef __FTDI_H_
#define __FTDI_H_
#define INTREPID_USB_VENDOR_ID (0x093c)
#if defined _WIN32
#include "icsneo/platform/windows/ftdi.h"
#elif defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
#include "icsneo/platform/posix/ftdi.h"
#else
#warning "This platform is not supported by the FTDI driver"
#endif
#endif
-74
View File
@@ -1,74 +0,0 @@
#ifndef __FTDI_POSIX_H_
#define __FTDI_POSIX_H_
#ifdef __cplusplus
#include <vector>
#include <memory>
#include <string>
#include <ftdi.h>
#include "icsneo/device/neodevice.h"
#include "icsneo/communication/driver.h"
#include "icsneo/third-party/concurrentqueue/blockingconcurrentqueue.h"
#include "icsneo/api/eventmanager.h"
namespace icsneo {
class FTDI : public Driver {
public:
static void Find(std::vector<FoundDevice>& found);
FTDI(const device_eventhandler_t& err, neodevice_t& forDevice);
~FTDI() { if(isOpen()) close(); }
bool open();
bool close();
bool isOpen() { return ftdi.isOpen(); }
private:
class FTDIContext {
public:
FTDIContext() : context(ftdi_new()) {}
~FTDIContext() {
if(context)
ftdi_free(context); // calls ftdi_deinit and ftdi_close if required
context = nullptr;
}
// A PID of 0 disables filtering by PID
std::pair<int, std::vector< std::pair<std::string, uint16_t> > > findDevices(int pid = 0);
int openDevice(int pid, const char* serial);
bool closeDevice();
bool isOpen() const { return deviceOpen; }
int flush() { return ftdi_usb_purge_buffers(context); }
int reset() { return ftdi_usb_reset(context); }
int read(uint8_t* data, size_t size) { return ftdi_read_data(context, data, (int)size); }
int write(const uint8_t* data, size_t size) { return ftdi_write_data(context, data, (int)size); }
int setBaudrate(int baudrate) { return ftdi_set_baudrate(context, baudrate); }
int setLatencyTimer(uint8_t latency) { return ftdi_set_latency_timer(context, latency); }
bool setReadTimeout(int timeout) { if(context == nullptr) return false; context->usb_read_timeout = timeout; return true; }
bool setWriteTimeout(int timeout) { if(context == nullptr) return false; context->usb_write_timeout = timeout; return true; }
private:
struct ftdi_context* context;
bool deviceOpen = false;
};
FTDIContext ftdi;
static std::vector<std::string> handles;
static bool ErrorIsDisconnection(int errorCode);
std::thread readThread, writeThread;
void readTask();
void writeTask();
bool openable; // Set to false in the constructor if the object has not been found in searchResultDevices
neodevice_t& device;
};
}
#endif // __cplusplus
#endif
+2
View File
@@ -4,6 +4,8 @@
#ifdef __cplusplus
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
+24 -4
View File
@@ -3,14 +3,34 @@
#ifdef __cplusplus
#include "icsneo/platform/windows/vcp.h"
#include "icsneo/communication/driver.h"
#include "icsneo/device/founddevice.h"
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
#include <string>
namespace icsneo {
class CDCACM : public VCP {
class CDCACM : public Driver {
public:
CDCACM(const device_eventhandler_t& err, neodevice_t& forDevice) : VCP(err, forDevice) {}
static void Find(std::vector<FoundDevice>& found) { return VCP::Find(found, { L"usbser" }); }
CDCACM(const device_eventhandler_t& err, const std::wstring& path);
static void Find(std::vector<FoundDevice>& found);
bool open() override;
bool isOpen() override;
bool close() override;
private:
void read();
void write();
std::wstring path;
HANDLE handle = INVALID_HANDLE_VALUE;
std::thread readThread;
std::thread writeThread;
OVERLAPPED readOverlapped = {};
OVERLAPPED writeOverlapped = {};
};
}
@@ -1,6 +1,8 @@
#ifndef __DYNAMICLIB_WINDOWS_H_
#define __DYNAMICLIB_WINDOWS_H_
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
#ifndef ICSNEOC_BUILD_STATIC
-20
View File
@@ -1,20 +0,0 @@
#ifndef __FTDI_WINDOWS_H_
#define __FTDI_WINDOWS_H_
#ifdef __cplusplus
#include "icsneo/platform/windows/vcp.h"
namespace icsneo {
class FTDI : public VCP {
public:
FTDI(const device_eventhandler_t& err, neodevice_t& forDevice) : VCP(err, forDevice) {}
static void Find(std::vector<FoundDevice>& found) { return VCP::Find(found, { L"serenum" /*, L"ftdibus" */ }); }
};
}
#endif // __cplusplus
#endif
@@ -3,6 +3,8 @@
#ifdef __cplusplus
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
#include <pcap.h>
#include <memory>
@@ -50,7 +52,7 @@ public:
bool ok() const;
private:
PCAPDLL();
HINSTANCE dll;
HINSTANCE dll = nullptr;
void closeDLL();
};
}
-49
View File
@@ -1,49 +0,0 @@
#ifndef __VCP_WINDOWS_H_
#define __VCP_WINDOWS_H_
#ifdef __cplusplus
#include <vector>
#include <string>
#include <thread>
#include <atomic>
#include <chrono>
#include "icsneo/device/neodevice.h"
#include "icsneo/communication/driver.h"
#include "icsneo/api/eventmanager.h"
namespace icsneo {
// Virtual COM Port Communication
class VCP : public Driver {
public:
static void Find(std::vector<FoundDevice>& found, std::vector<std::wstring> driverName);
static bool IsHandleValid(neodevice_handle_t handle);
typedef void(*fn_boolCallback)(bool success);
VCP(const device_eventhandler_t& err, neodevice_t& forDevice);
virtual ~VCP();
bool open() { return open(false); }
void openAsync(fn_boolCallback callback);
bool close();
bool isOpen();
private:
bool open(bool fromAsync);
bool opening = false;
neodevice_t& device;
struct Detail;
std::shared_ptr<Detail> detail;
std::vector<std::shared_ptr<std::thread>> threads;
std::thread readThread, writeThread;
void readTask();
void writeTask();
};
}
#endif // __cplusplus
#endif
+194
View File
@@ -0,0 +1,194 @@
#include "icsneo/platform/dxx.h"
#define ICS_USB_VID 0x093C
using namespace icsneo;
static APIEvent::Type eventError(libredxx_status status) {
switch (status) {
case LIBREDXX_STATUS_ERROR_SYS: return APIEvent::Type::DXXErrorSys;
case LIBREDXX_STATUS_ERROR_INTERRUPTED: return APIEvent::Type::DXXErrorSys;
case LIBREDXX_STATUS_ERROR_OVERFLOW: return APIEvent::Type::DXXErrorSys;
case LIBREDXX_STATUS_ERROR_IO: return APIEvent::Type::DXXErrorSys;
case LIBREDXX_STATUS_ERROR_INVALID_ARGUMENT: return APIEvent::Type::DXXErrorSys;
default: return APIEvent::Type::Unknown;
}
}
void DXX::Find(std::vector<FoundDevice>& found) {
libredxx_status status;
static libredxx_find_filter filters[] = {
{ LIBREDXX_DEVICE_TYPE_D2XX, { ICS_USB_VID, 0x0005 } }, // RAD-Star 2
{ LIBREDXX_DEVICE_TYPE_D2XX, { ICS_USB_VID, 0x0006 } }, // RAD-A2B Rev A
{ LIBREDXX_DEVICE_TYPE_D2XX, { ICS_USB_VID, 0x1000 } }, // neoVI FIRE2
{ LIBREDXX_DEVICE_TYPE_D3XX, { ICS_USB_VID, 0x1201 } }, // RAD-SuperMoon
{ LIBREDXX_DEVICE_TYPE_D3XX, { ICS_USB_VID, 0x1202 } }, // RAD-Moon2
{ LIBREDXX_DEVICE_TYPE_D3XX, { ICS_USB_VID, 0x1203 } }, // RAD-Gigalog
{ LIBREDXX_DEVICE_TYPE_D3XX, { ICS_USB_VID, 0x1204 } }, // RAD-Gigastar
{ LIBREDXX_DEVICE_TYPE_D3XX, { ICS_USB_VID, 0x1206 } }, // RAD-A2B Rev B
{ LIBREDXX_DEVICE_TYPE_D3XX, { ICS_USB_VID, 0x1207 } }, // RAD-Comet
{ LIBREDXX_DEVICE_TYPE_D3XX, { ICS_USB_VID, 0x1208 } }, // RAD-Comet3
{ LIBREDXX_DEVICE_TYPE_D3XX, { ICS_USB_VID, 0x1209 } }, // RAD-MoonT1S
{ LIBREDXX_DEVICE_TYPE_D3XX, { ICS_USB_VID, 0x1210 } }, // RAD-Gigastar 2
};
static size_t filterCount = sizeof(filters) / sizeof(filters[0]);
libredxx_found_device** foundDevices = nullptr;
size_t foundDevicesCount;
status = libredxx_find_devices(filters, filterCount, &foundDevices, &foundDevicesCount);
if(status != LIBREDXX_STATUS_SUCCESS) {
EventManager::GetInstance().add(eventError(status), APIEvent::Severity::Error);
return;
}
if(foundDevicesCount == 0) {
return;
}
for(size_t i = 0; i < foundDevicesCount; ++i) {
libredxx_found_device* foundDevice = foundDevices[i];
libredxx_serial serial = {};
status = libredxx_get_serial(foundDevice, &serial);
if(status != LIBREDXX_STATUS_SUCCESS) {
EventManager::GetInstance().add(eventError(status), APIEvent::Severity::Error);
continue;
}
libredxx_device_id id;
status = libredxx_get_device_id(foundDevice, &id);
if(status != LIBREDXX_STATUS_SUCCESS) {
EventManager::GetInstance().add(eventError(status), APIEvent::Severity::Error);
continue;
}
libredxx_device_type type;
status = libredxx_get_device_type(foundDevice, &type);
if(status != LIBREDXX_STATUS_SUCCESS) {
EventManager::GetInstance().add(eventError(status), APIEvent::Severity::Error);
continue;
}
auto& device = found.emplace_back();
std::copy(serial.serial, serial.serial + sizeof(device.serial), device.serial);
device.makeDriver = [id, type](device_eventhandler_t err, neodevice_t& forDevice) {
return std::make_unique<DXX>(err, forDevice, id.pid, type);
};
}
libredxx_free_found(foundDevices);
}
DXX::DXX(const device_eventhandler_t& err, neodevice_t& forDevice, uint16_t pid, libredxx_device_type type) :
Driver(err), neodevice(forDevice), pid(pid), type(type) {
}
bool DXX::open() {
libredxx_status status;
libredxx_find_filter filters[] = {
{ (libredxx_device_type)type, { ICS_USB_VID, pid } }
};
libredxx_found_device** foundDevices = nullptr;
size_t foundDevicesCount;
status = libredxx_find_devices(filters, 1, &foundDevices, &foundDevicesCount);
if(status != LIBREDXX_STATUS_SUCCESS) {
EventManager::GetInstance().add(eventError(status), APIEvent::Severity::Error);
return false;
}
if(foundDevicesCount == 0) {
EventManager::GetInstance().add(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
return false;
}
libredxx_found_device* foundDevice = nullptr;
for(size_t i = 0; i < foundDevicesCount; ++i) {
libredxx_serial serial = {};
status = libredxx_get_serial(foundDevices[i], &serial);
if(status != LIBREDXX_STATUS_SUCCESS) {
EventManager::GetInstance().add(eventError(status), APIEvent::Severity::EventWarning);
continue;
}
if(strcmp(serial.serial, neodevice.serial) == 0) {
foundDevice = foundDevices[i];
break;
}
}
if(foundDevice == nullptr) {
EventManager::GetInstance().add(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
libredxx_free_found(foundDevices);
return false;
}
status = libredxx_open_device(foundDevice, &device);
if(status != LIBREDXX_STATUS_SUCCESS) {
EventManager::GetInstance().add(eventError(status), APIEvent::Severity::Error);
libredxx_free_found(foundDevices);
return false;
}
libredxx_free_found(foundDevices);
setIsDisconnected(false);
readThread = std::thread(&DXX::read, this);
writeThread = std::thread(&DXX::write, this);
return true;
}
bool DXX::isOpen() {
return device != nullptr;
}
bool DXX::close() {
setIsClosing(true);
libredxx_close_device(device); // unblock read thread & close
writeQueue.enqueue(WriteOperation{}); // unblock write thread
readThread.join();
writeThread.join();
device = nullptr;
setIsClosing(false);
return true;
}
void DXX::read() {
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
std::vector<uint8_t> buffer(ICSNEO_DRIVER_RINGBUFFER_SIZE);
while(!isDisconnected() && !isClosing()) {
size_t received = buffer.size();
const auto status = libredxx_read(device, buffer.data(), &received);
if(isDisconnected() || isClosing()) {
return;
}
if(status != LIBREDXX_STATUS_SUCCESS) {
EventManager::GetInstance().add(eventError(status), APIEvent::Severity::Error);
setIsDisconnected(true);
return;
}
while(!isDisconnected() && !isClosing()) {
if(pushRx(buffer.data(), received))
break;
}
}
}
void DXX::write() {
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
WriteOperation writeOp;
while(!isDisconnected() && !isClosing()) {
writeQueue.wait_dequeue(writeOp);
if(isDisconnected() || isClosing()) {
return;
}
for(size_t totalWritten = 0; totalWritten < writeOp.bytes.size();) {
size_t size = writeOp.bytes.size() - totalWritten;
const auto status = libredxx_write(device, &writeOp.bytes[totalWritten], &size);
if(isDisconnected() || isClosing()) {
return;
}
if(status != LIBREDXX_STATUS_SUCCESS) {
EventManager::GetInstance().add(eventError(status), APIEvent::Severity::Error);
setIsDisconnected(true);
return;
}
totalWritten += size;
}
}
}
-182
View File
@@ -1,182 +0,0 @@
#include <vector>
#include "icsneo/api/eventmanager.h"
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4091)
#endif
#define FTD3XX_STATIC
#include <ftd3xx.h>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "icsneo/platform/ftd3xx.h"
static constexpr auto READ_PIPE_ID = 0x82;
static constexpr auto WRITE_PIPE_ID = 0x02;
using namespace icsneo;
static void addEvent(FT_STATUS status, APIEvent::Severity severity) {
const auto internalEvent = static_cast<uint32_t>(APIEvent::Type::FTOK) + status;
EventManager::GetInstance().add(APIEvent((APIEvent::Type)internalEvent, severity));
}
void FTD3XX::Find(std::vector<FoundDevice>& found) {
DWORD count;
if(const auto ret = FT_CreateDeviceInfoList(&count); ret != FT_OK) {
addEvent(ret, APIEvent::Severity::EventWarning);
return;
}
if(count == 0) {
return;
}
std::vector<FT_DEVICE_LIST_INFO_NODE> devices(count);
if(const auto ret = FT_GetDeviceInfoList(devices.data(), &count); ret != FT_OK) {
addEvent(ret, APIEvent::Severity::EventWarning);
return;
}
for(const auto& dev : devices) {
FoundDevice foundDevice = {};
std::copy(dev.SerialNumber, dev.SerialNumber + sizeof(foundDevice.serial), foundDevice.serial);
foundDevice.makeDriver = [](const device_eventhandler_t& eh, neodevice_t& forDevice) {
return std::unique_ptr<Driver>(new FTD3XX(eh, forDevice));
};
found.push_back(std::move(foundDevice));
}
}
FTD3XX::FTD3XX(const device_eventhandler_t& err, neodevice_t& forDevice) : Driver(err), device(forDevice) {
}
bool FTD3XX::open() {
if(isOpen()) {
report(APIEvent::Type::DeviceCurrentlyOpen, APIEvent::Severity::Error);
return false;
}
void* tmpHandle;
if(const auto ret = FT_Create(device.serial, FT_OPEN_BY_SERIAL_NUMBER, &tmpHandle); ret != FT_OK) {
addEvent(ret, APIEvent::Severity::Error);
return false;
}
handle.emplace(tmpHandle);
setIsClosing(false);
setIsDisconnected(false);
readThread = std::thread(&FTD3XX::readTask, this);
writeThread = std::thread(&FTD3XX::writeTask, this);
return true;
}
bool FTD3XX::isOpen() {
return handle.has_value();
}
bool FTD3XX::close() {
if(!isOpen() && !isDisconnected()) {
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
return false;
}
setIsClosing(true);
// unblock the read thread
FT_AbortPipe(*handle, READ_PIPE_ID);
if(readThread.joinable())
readThread.join();
if(writeThread.joinable())
writeThread.join();
clearBuffers();
if(const auto ret = FT_Close(*handle); ret != FT_OK) {
addEvent(ret, APIEvent::Severity::EventWarning);
}
handle.reset();
setIsClosing(false);
return true;
}
void FTD3XX::readTask() {
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
std::vector<uint8_t> buffer(2 * 1024 * 1024);
FT_SetStreamPipe(*handle, false, false, READ_PIPE_ID, (ULONG)buffer.size());
// disable timeouts, we will interupt the read thread with AbortPipe
FT_SetPipeTimeout(*handle, READ_PIPE_ID, 0);
OVERLAPPED overlapped = {};
FT_InitializeOverlapped(*handle, &overlapped);
FT_STATUS status;
ULONG received = 0;
while(!isClosing() && !isDisconnected()) {
received = 0;
#ifdef _WIN32
status = FT_ReadPipe(*handle, READ_PIPE_ID, buffer.data(), (ULONG)buffer.size(), &received, &overlapped);
#else
status = FT_ReadPipeAsync(*handle, 0, buffer.data(), buffer.size(), &received, &overlapped);
#endif
if(FT_FAILED(status)) {
if(status != FT_IO_PENDING) {
addEvent(status, APIEvent::Severity::Error);
setIsDisconnected(true);
break;
}
status = FT_GetOverlappedResult(*handle, &overlapped, &received, true);
if(FT_FAILED(status)) {
addEvent(status, APIEvent::Severity::Error);
setIsDisconnected(true);
break;
}
if(received > 0) {
pushRx(buffer.data(), received);
}
}
}
FT_ReleaseOverlapped(*handle, &overlapped);
}
void FTD3XX::writeTask() {
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
FT_SetPipeTimeout(*handle, WRITE_PIPE_ID, 0);
WriteOperation writeOp;
ULONG sent;
FT_STATUS status;
while(!isClosing() && !isDisconnected()) {
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100)))
continue;
const auto size = static_cast<ULONG>(writeOp.bytes.size());
sent = 0;
#ifdef _WIN32
status = FT_WritePipe(*handle, WRITE_PIPE_ID, writeOp.bytes.data(), size, &sent, nullptr);
#else
status = FT_WritePipe(*handle, WRITE_PIPE_ID, writeOp.bytes.data(), size, &sent, 100);
#endif
if(FT_FAILED(status)) {
addEvent(status, APIEvent::Severity::Error);
setIsDisconnected(true);
break;
}
if(sent != size) {
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
setIsDisconnected(true);
break;
}
}
}
-242
View File
@@ -1,242 +0,0 @@
#include "icsneo/platform/ftdi.h"
#include "icsneo/device/founddevice.h"
#include <iostream>
#include <stdio.h>
#include <cstring>
#include <memory>
#include <utility>
#include <cctype>
#include <algorithm>
#include <libusb.h>
using namespace icsneo;
std::vector<std::string> FTDI::handles;
void FTDI::Find(std::vector<FoundDevice>& found) {
constexpr size_t deviceSerialBufferLength = sizeof(device.serial);
static FTDIContext context;
const auto result = context.findDevices();
if(result.first < 0)
return; // TODO Flag an error for the client application, there was an issue with FTDI
for(const auto& [serial, pid] : result.second) {
FoundDevice d;
strncpy(d.serial, serial.c_str(), deviceSerialBufferLength - 1);
d.serial[deviceSerialBufferLength - 1] = '\0'; // strncpy does not write a null terminator if serial is too long
for(size_t i = 0; i < deviceSerialBufferLength - 1; i++)
d.serial[i] = toupper(serial[i]);
std::string devHandle = serial;
auto it = std::find(handles.begin(), handles.end(), devHandle);
size_t foundHandle = SIZE_MAX;
if(it != handles.end()) {
foundHandle = it - handles.begin();
} else {
foundHandle = handles.size();
handles.push_back(devHandle);
}
d.handle = foundHandle;
d.productId = pid;
d.makeDriver = [](const device_eventhandler_t& report, neodevice_t& device) {
return std::unique_ptr<Driver>(new FTDI(report, device));
};
found.push_back(d);
}
}
FTDI::FTDI(const device_eventhandler_t& err, neodevice_t& forDevice) : Driver(err), device(forDevice) {
openable = strlen(forDevice.serial) > 0 && device.handle >= 0 && device.handle < (neodevice_handle_t)handles.size();
}
bool FTDI::open() {
if(isOpen()) {
report(APIEvent::Type::DeviceCurrentlyOpen, APIEvent::Severity::Error);
return false;
}
if(!openable) {
report(APIEvent::Type::InvalidNeoDevice, APIEvent::Severity::Error);
return false;
}
// At this point the handle has been checked to be within the bounds of the handles array
auto& handle = handles[device.handle];
const int openError = ftdi.openDevice(0, handle.c_str());
if(openError == -5) { // Unable to claim device
report(APIEvent::Type::DeviceInUse, APIEvent::Severity::Error);
return false;
} else if(openError != 0) {
report(APIEvent::Type::DriverFailedToOpen, APIEvent::Severity::Error);
return false;
}
ftdi.setReadTimeout(100);
ftdi.setWriteTimeout(1000);
ftdi.reset();
ftdi.setBaudrate(500000);
ftdi.setLatencyTimer(1);
ftdi.flush();
// Create threads
setIsClosing(false);
readThread = std::thread(&FTDI::readTask, this);
writeThread = std::thread(&FTDI::writeTask, this);
return true;
}
bool FTDI::close() {
if(!isOpen() && !isDisconnected()) {
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
return false;
}
setIsClosing(true);
if(readThread.joinable())
readThread.join();
if(writeThread.joinable())
writeThread.join();
bool ret = true;
if(!isDisconnected()) {
ret = ftdi.closeDevice();
if(!ret)
report(APIEvent::Type::DriverFailedToClose, APIEvent::Severity::Error);
}
clearBuffers();
setIsClosing(false);
setIsDisconnected(false);
return ret;
}
std::pair<int, std::vector< std::pair<std::string, uint16_t> > > FTDI::FTDIContext::findDevices(int pid) {
std::pair<int, std::vector< std::pair<std::string, uint16_t> > > ret;
if(context == nullptr) {
ret.first = -1;
return ret;
}
struct ftdi_device_list* devlist = nullptr;
ret.first = ftdi_usb_find_all(context, &devlist, INTREPID_USB_VENDOR_ID, pid);
if(ret.first < 1) {
// Didn't find anything, maybe got an error
if(devlist != nullptr)
ftdi_list_free(&devlist);
return ret;
}
if(devlist == nullptr) {
ret.first = -4;
return ret;
}
for(struct ftdi_device_list* curdev = devlist; curdev != nullptr; curdev = curdev->next) {
struct libusb_device_descriptor descriptor = {};
// Check against bDeviceClass here as it will be 0 for FTDI devices
// It will be 2 for CDC ACM devices, which we don't want to handle here
if(libusb_get_device_descriptor(curdev->dev, &descriptor) != 0 || descriptor.bDeviceClass != 0)
continue;
char serial[16] = {};
if(ftdi_usb_get_strings(context, curdev->dev, nullptr, 0, nullptr, 0, serial, sizeof(serial)) < 0)
continue;
const auto len = strnlen(serial, sizeof(serial));
if(len > 4 && len < 10)
ret.second.emplace_back(serial, descriptor.idProduct);
}
ret.first = static_cast<int>(ret.second.size());
ftdi_list_free(&devlist);
return ret;
}
int FTDI::FTDIContext::openDevice(int pid, const char* serial) {
if(context == nullptr)
return 1;
if(serial == nullptr)
return 2;
if(serial[0] == '\0')
return 3;
if(deviceOpen)
return 4;
int ret = ftdi_usb_open_desc(context, INTREPID_USB_VENDOR_ID, pid, nullptr, serial);
if(ret == 0 /* all ok */)
deviceOpen = true;
return ret;
}
bool FTDI::FTDIContext::closeDevice() {
if(context == nullptr)
return false;
if(!deviceOpen)
return true;
int ret = ftdi_usb_close(context);
if(ret != 0)
return false;
deviceOpen = false;
return true;
}
bool FTDI::ErrorIsDisconnection(int errorCode) {
return errorCode == LIBUSB_ERROR_NO_DEVICE ||
errorCode == LIBUSB_ERROR_PIPE ||
errorCode == LIBUSB_ERROR_IO;
}
void FTDI::readTask() {
constexpr size_t READ_BUFFER_SIZE = 8;
uint8_t readbuf[READ_BUFFER_SIZE];
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
while(!isClosing() && !isDisconnected()) {
auto readBytes = ftdi.read(readbuf, READ_BUFFER_SIZE);
if(readBytes < 0) {
if(ErrorIsDisconnection(readBytes)) {
if(!isDisconnected()) {
setIsDisconnected(true);
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
}
} else
report(APIEvent::Type::FailedToRead, APIEvent::Severity::EventWarning);
} else
pushRx(readbuf, readBytes);
}
}
void FTDI::writeTask() {
WriteOperation writeOp;
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
while(!isClosing() && !isDisconnected()) {
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100)))
continue;
size_t offset = 0;
while(offset < writeOp.bytes.size()) {
auto writeBytes = ftdi.write(writeOp.bytes.data() + offset, (int)writeOp.bytes.size() - offset);
if(writeBytes < 0) {
if(ErrorIsDisconnection(writeBytes)) {
if(!isDisconnected()) {
setIsDisconnected(true);
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
}
break;
} else
report(APIEvent::Type::FailedToWrite, APIEvent::Severity::EventWarning);
} else
offset += writeBytes;
}
}
}
+20 -7
View File
@@ -1,5 +1,9 @@
#include "icsneo/platform/servd.h"
#include <string_view>
#include <cstdlib>
using namespace icsneo;
#define SERVD_VERSION 1
@@ -8,17 +12,25 @@ static const Address SERVD_ADDRESS = Address("127.0.0.1", 26741);
static const std::string SERVD_VERSION_STR = std::to_string(SERVD_VERSION);
bool Servd::Enabled() {
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4996)
#endif
char* enabled = std::getenv("LIBICSNEO_USE_SERVD");
#ifdef _MSC_VER
#pragma warning(pop)
#endif
return enabled ? enabled[0] == '1' : false;
}
std::vector<std::string_view> split(const std::string_view& str, char delim = ' ')
{
std::vector<std::string_view> ret;
std::vector<std::string> split(const std::string_view& str, char delim = ' ') {
if(str.empty())
return {};
std::vector<std::string> ret;
size_t tail = 0;
size_t head = 0;
while (head < str.size()) {
if (str[head] == delim) {
while(head < str.size()) {
if(str[head] == delim) {
ret.emplace_back(&str[tail], head - tail);
tail = head + 1;
}
@@ -114,8 +126,8 @@ bool Servd::open() {
return false;
}
aliveThread = std::thread(&Servd::alive, this);
readThread = std::thread(&Servd::read, this, Address{tokens[2].data(), (uint16_t)std::stol(tokens[3].data())});
writeThread = std::thread(&Servd::write, this, Address{tokens[0].data(), (uint16_t)std::stol(tokens[1].data())});
readThread = std::thread(&Servd::read, this, Address{tokens[2].c_str(), (uint16_t)std::stol(tokens[3].c_str())});
writeThread = std::thread(&Servd::write, this, Address{tokens[0].c_str(), (uint16_t)std::stol(tokens[1].c_str())});
opened = true;
return true;
}
@@ -140,6 +152,7 @@ bool Servd::close() {
messageSocket.sendto(request.data(), request.size(), SERVD_ADDRESS);
}
opened = false;
setIsClosing(false);
return true;
}
+233
View File
@@ -0,0 +1,233 @@
#include "icsneo/platform/windows/cdcacm.h"
#include <setupapi.h>
#include <initguid.h>
#include <usbiodef.h>
#include <devpkey.h>
using namespace icsneo;
CDCACM::CDCACM(const device_eventhandler_t& err, const std::wstring& path) : Driver(err), path(path) {
}
bool CDCACM::open() {
handle = CreateFileW(path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr);
if(handle == INVALID_HANDLE_VALUE) {
EventManager::GetInstance().add(APIEvent::Type::SyscallError, APIEvent::Severity::Error);
return false;
}
COMMTIMEOUTS timeouts;
timeouts.ReadIntervalTimeout = MAXDWORD;
timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
timeouts.ReadTotalTimeoutConstant = MAXDWORD - 1;
timeouts.WriteTotalTimeoutMultiplier = 0;
timeouts.WriteTotalTimeoutConstant = 0;
if(!SetCommTimeouts(handle, &timeouts)) {
EventManager::GetInstance().add(APIEvent::Type::SyscallError, APIEvent::Severity::Error);
CloseHandle(handle);
handle = INVALID_HANDLE_VALUE;
return false;
}
DCB comstate;
if(!GetCommState(handle, &comstate)) {
EventManager::GetInstance().add(APIEvent::Type::SyscallError, APIEvent::Severity::Error);
CloseHandle(handle);
handle = INVALID_HANDLE_VALUE;
return false;
}
comstate.BaudRate = 115200;
comstate.ByteSize = 8;
comstate.fRtsControl = RTS_CONTROL_DISABLE;
if(!SetCommState(handle, &comstate)) {
EventManager::GetInstance().add(APIEvent::Type::SyscallError, APIEvent::Severity::Error);
CloseHandle(handle);
handle = INVALID_HANDLE_VALUE;
return false;
}
PurgeComm(handle, PURGE_RXCLEAR);
readOverlapped.hEvent = CreateEventA(nullptr, false, false, nullptr);
writeOverlapped.hEvent = CreateEventA(nullptr, false, false, nullptr);
setIsDisconnected(false);
readThread = std::thread(&CDCACM::read, this);
writeThread = std::thread(&CDCACM::write, this);
return true;
}
bool CDCACM::isOpen() {
return handle != INVALID_HANDLE_VALUE;
}
bool CDCACM::close() {
setIsClosing(true);
SetEvent(readOverlapped.hEvent); // unblock read thread
SetEvent(writeOverlapped.hEvent); // unblock write thread if waiting on COM write
writeQueue.enqueue(WriteOperation{}); // unblock write thread if waiting on write queue pop
readThread.join();
writeThread.join();
CloseHandle(readOverlapped.hEvent);
CloseHandle(writeOverlapped.hEvent);
CloseHandle(handle);
handle = INVALID_HANDLE_VALUE;
setIsClosing(false);
return true;
}
void CDCACM::read() {
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
std::vector<uint8_t> buffer(ICSNEO_DRIVER_RINGBUFFER_SIZE);
while(!isDisconnected() && !isClosing()) {
if(!ReadFile(handle, buffer.data(), (DWORD)buffer.size(), nullptr, &readOverlapped)) {
if(GetLastError() != ERROR_IO_PENDING) {
EventManager::GetInstance().add(APIEvent::Type::SyscallError, APIEvent::Severity::Error);
setIsDisconnected(true);
return;
}
}
DWORD read = 0;
if(!GetOverlappedResult(handle, &readOverlapped, &read, true)) {
EventManager::GetInstance().add(APIEvent::Type::SyscallError, APIEvent::Severity::Error);
setIsDisconnected(true);
return;
}
if(read == 0) {
continue;
}
while(!isDisconnected() && !isClosing()) {
if(pushRx(buffer.data(), read))
break;
}
}
}
void CDCACM::write() {
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
WriteOperation writeOp;
while(!isDisconnected() && !isClosing()) {
writeQueue.wait_dequeue(writeOp);
if(isDisconnected() || isClosing()) {
return;
}
if(!WriteFile(handle, writeOp.bytes.data(), (DWORD)writeOp.bytes.size(), nullptr, &writeOverlapped)) {
if(GetLastError() != ERROR_IO_PENDING) {
EventManager::GetInstance().add(APIEvent::Type::SyscallError, APIEvent::Severity::Error);
setIsDisconnected(true);
return;
}
}
DWORD written;
if(!GetOverlappedResult(handle, &writeOverlapped, &written, true)) {
EventManager::GetInstance().add(APIEvent::Type::SyscallError, APIEvent::Severity::Error);
setIsDisconnected(true);
return;
}
if(written != writeOp.bytes.size()) {
EventManager::GetInstance().add(APIEvent::Type::FailedToWrite, APIEvent::Severity::Error);
setIsDisconnected(true);
return;
}
}
}
class DeviceInfo {
public:
DeviceInfo() {
mDeviceInfo = SetupDiGetClassDevsW(&GUID_DEVINTERFACE_USB_DEVICE, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
}
~DeviceInfo() {
SetupDiDestroyDeviceInfoList(mDeviceInfo);
}
operator HDEVINFO() const {
return mDeviceInfo;
}
operator bool() const {
return mDeviceInfo != INVALID_HANDLE_VALUE;
}
private:
HDEVINFO mDeviceInfo;
};
class DeviceInfoData {
public:
DeviceInfoData() {
mDeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
}
operator SP_DEVINFO_DATA*() {
return &mDeviceInfoData;
}
private:
SP_DEVINFO_DATA mDeviceInfoData;
};
static constexpr size_t WSTRING_ELEMENT_SIZE = sizeof(std::wstring::value_type);
void CDCACM::Find(std::vector<FoundDevice>& found) {
DeviceInfoData deviceInfoData;
const std::wstring intrepidUSB(L"USB\\VID_093C");
DeviceInfo deviceInfoSet;
if(!deviceInfoSet) {
EventManager::GetInstance().add(APIEvent::Type::SyscallError, APIEvent::Severity::Error);
return;
}
for(DWORD i = 0; SetupDiEnumDeviceInfo(deviceInfoSet, i, deviceInfoData); ++i) {
DWORD DataT;
DWORD buffersize = 0;
std::wstring wclass;
while(!SetupDiGetDevicePropertyW(deviceInfoSet, deviceInfoData, &DEVPKEY_Device_Class, &DataT, reinterpret_cast<PBYTE>(wclass.data()), static_cast<DWORD>((wclass.size() + 1) * WSTRING_ELEMENT_SIZE), &buffersize, 0)) {
wclass.resize((buffersize - 1) / WSTRING_ELEMENT_SIZE);
}
if(wclass != L"Ports") {
continue;
}
// TODO: is this a bug in Windows? why is this returned size different/wrong? It's like it's not a wstring at all
std::wstring deviceInstanceId;
while(!SetupDiGetDeviceInstanceIdW(deviceInfoSet, deviceInfoData, deviceInstanceId.data(), static_cast<DWORD>(deviceInstanceId.size() + 1), &buffersize)) {
deviceInstanceId.resize(buffersize - 1);
}
if(deviceInstanceId.find(intrepidUSB) != 0) {
continue;
}
std::wstring wserial;
while(!SetupDiGetDevicePropertyW(deviceInfoSet, deviceInfoData, &DEVPKEY_Device_BusReportedDeviceDesc, &DataT, reinterpret_cast<PBYTE>(wserial.data()), static_cast<DWORD>((wserial.size() + 1) * WSTRING_ELEMENT_SIZE), &buffersize, 0)) {
wserial.resize((buffersize - 1) / WSTRING_ELEMENT_SIZE);
}
FoundDevice device;
if(WideCharToMultiByte(CP_ACP, 0, wserial.c_str(), (int)wserial.size(), device.serial, sizeof(device.serial), NULL, NULL) == 0) {
EventManager::GetInstance().add(APIEvent::Type::SyscallError, APIEvent::Severity::Error);
continue;
}
std::wstring wport;
while(!SetupDiGetCustomDevicePropertyW(deviceInfoSet, deviceInfoData, L"PortName", 0, &DataT, reinterpret_cast<PBYTE>(wport.data()), static_cast<DWORD>((wport.size() + 1) * WSTRING_ELEMENT_SIZE), &buffersize)) {
wport.resize((buffersize - 1) / WSTRING_ELEMENT_SIZE);
}
const std::wstring path(L"\\\\.\\" + wport);
device.makeDriver = [path](device_eventhandler_t err, neodevice_t&) {
return std::make_unique<CDCACM>(err, path);
};
found.emplace_back(std::move(device));
}
}
+8 -14
View File
@@ -28,21 +28,15 @@ bool PCAPDLL::ok() const
PCAPDLL::PCAPDLL()
{
#ifdef NPCAP // Use -DLIBICSNEO_NPCAP_INCLUDE_DIR when configuring, point towards the npcap includes
DLL_DIRECTORY_COOKIE cookie = 0;
TCHAR dllPath[512] = { 0 };
int len = GetSystemDirectory(dllPath, 480); // be safe
if (len) {
_tcscat_s(dllPath, 512, TEXT("\\Npcap"));
cookie = AddDllDirectory(dllPath);
TCHAR dir[512] = {0};
if(GetSystemDirectory(dir, 480)) {
_tcscat_s(dir, 512, TEXT("\\Npcap"));
if(SetDllDirectory(dir)) {
// will search for Npcap first, then fall back to WinPcap
dll = LoadLibrary(TEXT("wpcap.dll"));
SetDllDirectory(nullptr); // reset
}
}
dll = LoadLibraryEx(TEXT("wpcap.dll"), nullptr, LOAD_LIBRARY_SEARCH_USER_DIRS);
if (cookie)
RemoveDllDirectory(cookie);
#else // Otherwise we'll use WinPCAP, or npcap in compatibility mode
dll = LoadLibrary(TEXT("wpcap.dll"));
#endif
if(dll == NULL) {
closeDLL();
+2
View File
@@ -1,3 +1,5 @@
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
#include <winsock2.h>
+2
View File
@@ -1,6 +1,8 @@
#include "icsneo/platform/windows/registry.h"
#include "icsneo/platform/windows/strings.h"
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
#include <codecvt>
#include <vector>
+3
View File
@@ -1,4 +1,7 @@
#include <string>
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#include <icsneo/platform/windows/strings.h>
-471
View File
@@ -1,471 +0,0 @@
#include "icsneo/platform/windows/ftdi.h"
#include "icsneo/platform/windows/strings.h"
#include "icsneo/platform/ftdi.h"
#include "icsneo/platform/registry.h"
#include "icsneo/device/founddevice.h"
#include <windows.h>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <cwctype>
#include <algorithm>
#include <codecvt>
#include <cctype>
#include <limits>
#include <stdio.h>
using namespace icsneo;
static const std::wstring DRIVER_SERVICES_REG_KEY = L"SYSTEM\\CurrentControlSet\\services\\";
static const std::wstring ALL_ENUM_REG_KEY = L"SYSTEM\\CurrentControlSet\\Enum\\";
static constexpr unsigned int RETRY_TIMES = 5;
static constexpr unsigned int RETRY_DELAY = 50;
struct VCP::Detail {
Detail() {
overlappedRead.hEvent = INVALID_HANDLE_VALUE;
overlappedWrite.hEvent = INVALID_HANDLE_VALUE;
overlappedWait.hEvent = INVALID_HANDLE_VALUE;
}
HANDLE handle = INVALID_HANDLE_VALUE;
OVERLAPPED overlappedRead = {};
OVERLAPPED overlappedWrite = {};
OVERLAPPED overlappedWait = {};
};
void VCP::Find(std::vector<FoundDevice>& found, std::vector<std::wstring> driverNames) {
for(auto& driverName : driverNames) {
std::wstringstream regss;
regss << DRIVER_SERVICES_REG_KEY << driverName << L"\\Enum\\";
std::wstring driverEnumRegKey = regss.str();
uint32_t deviceCount = 0;
if(!Registry::Get(driverEnumRegKey, L"Count", deviceCount))
continue;
for(uint32_t i = 0; i < deviceCount; i++) {
FoundDevice device;
device.makeDriver = [](const device_eventhandler_t& reportFn, neodevice_t& device) {
return std::unique_ptr<Driver>(new VCP(reportFn, device));
};
// First we want to look at what devices FTDI is enumerating (inside driverEnumRegKey)
// The entry for a ValueCAN 3 with SN 138635 looks like "FTDIBUS\VID_093C+PID_0601+138635A\0000"
// The entry for a ValueCAN 4 with SN V20227 looks like "USB\VID_093C&PID_1101\V20227"
std::wstringstream ss;
ss << i;
std::wstring entry;
if(!Registry::Get(driverEnumRegKey, ss.str(), entry))
continue;
std::transform(entry.begin(), entry.end(), entry.begin(), std::towupper);
std::wstringstream vss;
vss << "VID_" << std::setfill(L'0') << std::setw(4) << std::uppercase << std::hex << INTREPID_USB_VENDOR_ID; // Intrepid Vendor ID
if(entry.find(vss.str()) == std::wstring::npos)
continue;
auto pidpos = entry.find(L"PID_");
if(pidpos == std::wstring::npos)
continue;
// We will later use this and startchar to parse the PID
// Okay, this is a device we want
// Get the serial number
auto startchar = entry.find(L"+", pidpos + 1);
if(startchar == std::wstring::npos)
startchar = entry.find(L"\\", pidpos + 1);
bool conversionError = false;
int sn = 0;
try {
sn = std::stoi(entry.substr(startchar + 1));
}
catch(...) {
conversionError = true;
}
std::wstringstream oss;
if(!sn || conversionError)
oss << entry.substr(startchar + 1, 6); // This is a device with characters in the serial number
else
oss << sn;
device.productId = uint16_t(std::wcstol(entry.c_str() + pidpos + 4, nullptr, 16));
if(!device.productId)
continue;
std::string serial = convertWideString(oss.str());
// The serial number should not have a path slash in it. If it does, that means we don't have the real serial.
if(serial.find_first_of('\\') != std::string::npos) {
// The serial number was not in the first serenum key where we expected it.
// We can try to match the ContainerID with the one in ALL_ENUM\USB and get a serial that way
std::wstringstream uess;
uess << ALL_ENUM_REG_KEY << L"\\USB\\" << vss.str() << L"&PID_" << std::setfill(L'0') << std::setw(4)
<< std::uppercase << std::hex << device.productId << L'\\';
std::wstringstream ciss;
ciss << ALL_ENUM_REG_KEY << entry;
std::wstring containerIDFromEntry, containerIDFromEnum;
if(!Registry::Get(ciss.str(), L"ContainerID", containerIDFromEntry))
continue; // We did not get a container ID. This can happen on Windows XP and before.
if(containerIDFromEntry.empty())
continue; // The container ID was empty?
std::vector<std::wstring> subkeys;
if(!Registry::EnumerateSubkeys(uess.str(), subkeys))
continue; // VID/PID combo was not present at all.
if(subkeys.empty())
continue; // No devices for VID/PID.
std::wstring correctSerial;
for(auto& subkey : subkeys) {
std::wstringstream skss;
skss << uess.str() << L'\\' << subkey;
if(!Registry::Get(skss.str(), L"ContainerID", containerIDFromEnum))
continue;
if(containerIDFromEntry != containerIDFromEnum)
continue;
correctSerial = subkey;
break;
}
if(correctSerial.empty())
continue; // Didn't find the device within the subkeys of the enumeration
sn = 0;
conversionError = false;
try {
sn = std::stoi(correctSerial);
}
catch(...) {
conversionError = true;
}
if(!sn || conversionError) {
// This is a device with characters in the serial number
if(correctSerial.size() != 6)
continue;
serial = convertWideString(correctSerial);
}
else {
std::wstringstream soss;
soss << sn;
serial = convertWideString(soss.str());
}
if(serial.find_first_of('\\') != std::string::npos)
continue;
}
for(char& c : serial)
c = static_cast<char>(toupper(c));
strcpy_s(device.serial, sizeof(device.serial), serial.c_str());
// Serial number is saved, we want the COM port number now
// This will be stored under ALL_ENUM_REG_KEY\entry\Device Parameters\PortName (entry from the FTDI_ENUM)
std::wstringstream dpss;
dpss << ALL_ENUM_REG_KEY << entry << L"\\Device Parameters";
std::wstring port;
Registry::Get(dpss.str(), L"PortName", port); // TODO If error do something else (Plasma maybe?)
std::transform(port.begin(), port.end(), port.begin(), std::towupper);
auto compos = port.find(L"COM");
device.handle = 0;
if(compos != std::wstring::npos) {
try {
device.handle = std::stoi(port.substr(compos + 3));
}
catch(...) {} // In case of this, or any other error, handle has already been initialized to 0
}
bool alreadyFound = false;
FoundDevice* shouldReplace = nullptr;
for(auto& foundDev : found) {
if((foundDev.handle == device.handle || foundDev.handle == 0 || device.handle == 0) && serial == foundDev.serial) {
alreadyFound = true;
if(foundDev.handle == 0)
shouldReplace = &foundDev;
break;
}
}
if(!alreadyFound)
found.push_back(device);
else if(shouldReplace != nullptr)
*shouldReplace = device;
}
}
}
VCP::VCP(const device_eventhandler_t& err, neodevice_t& forDevice) : Driver(err), device(forDevice) {
detail = std::make_shared<Detail>();
}
VCP::~VCP() {
if(isOpen())
close();
}
bool VCP::IsHandleValid(neodevice_handle_t handle) {
if(handle < 1)
return false;
if(handle > 256) // Windows default max COM port is COM256
return false; // TODO Enumerate subkeys of HKLM\HARDWARE\DEVICEMAP\SERIALCOMM as a user might have more serial ports somehow
return true;
}
bool VCP::open(bool fromAsync) {
if(isOpen() || (!fromAsync && opening)) {
report(APIEvent::Type::DeviceCurrentlyOpen, APIEvent::Severity::Error);
return false;
}
if(!IsHandleValid(device.handle)) {
report(APIEvent::Type::DriverFailedToOpen, APIEvent::Severity::Error);
return false;
}
opening = true;
std::wstringstream comss;
comss << L"\\\\.\\COM" << device.handle;
// We're going to attempt to open 5 (RETRY_TIMES) times in a row
for(int i = 0; !isOpen() && i < RETRY_TIMES; i++) {
detail->handle = CreateFileW(comss.str().c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr,
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr);
if(GetLastError() == ERROR_SUCCESS)
break; // We have the file handle
std::this_thread::sleep_for(std::chrono::milliseconds(RETRY_DELAY));
}
opening = false;
if(!isOpen()) {
report(APIEvent::Type::DriverFailedToOpen, APIEvent::Severity::Error);
return false;
}
// Set the timeouts
COMMTIMEOUTS timeouts;
if(!GetCommTimeouts(detail->handle, &timeouts)) {
close();
report(APIEvent::Type::DriverFailedToOpen, APIEvent::Severity::Error);
return false;
}
// See https://docs.microsoft.com/en-us/windows/desktop/api/winbase/ns-winbase-_commtimeouts#remarks
timeouts.ReadIntervalTimeout = MAXDWORD;
timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
timeouts.ReadTotalTimeoutConstant = 100;
timeouts.WriteTotalTimeoutConstant = 10000;
timeouts.WriteTotalTimeoutMultiplier = 0;
if(!SetCommTimeouts(detail->handle, &timeouts)) {
close();
report(APIEvent::Type::DriverFailedToOpen, APIEvent::Severity::Error);
return false;
}
// Set the COM state
DCB comstate;
if(!GetCommState(detail->handle, &comstate)) {
close();
report(APIEvent::Type::DriverFailedToOpen, APIEvent::Severity::Error);
return false;
}
comstate.BaudRate = 115200;
comstate.ByteSize = 8;
comstate.Parity = NOPARITY;
comstate.StopBits = 0;
comstate.fDtrControl = DTR_CONTROL_ENABLE;
comstate.fRtsControl = RTS_CONTROL_ENABLE;
if(!SetCommState(detail->handle, &comstate)) {
close();
report(APIEvent::Type::DriverFailedToOpen, APIEvent::Severity::Error);
return false;
}
PurgeComm(detail->handle, PURGE_RXCLEAR);
// Set up events so that overlapped IO can work with them
detail->overlappedRead.hEvent = CreateEvent(nullptr, false, false, nullptr);
detail->overlappedWrite.hEvent = CreateEvent(nullptr, false, false, nullptr);
detail->overlappedWait.hEvent = CreateEvent(nullptr, true, false, nullptr);
if (detail->overlappedRead.hEvent == nullptr || detail->overlappedWrite.hEvent == nullptr || detail->overlappedWait.hEvent == nullptr) {
close();
report(APIEvent::Type::DriverFailedToOpen, APIEvent::Severity::Error);
return false;
}
// Set up event so that we will satisfy overlappedWait when a character comes in
if(!SetCommMask(detail->handle, EV_RXCHAR)) {
close();
report(APIEvent::Type::DriverFailedToOpen, APIEvent::Severity::Error);
return false;
}
// TODO Set up some sort of shared memory, save which COM port we have open so we don't try to open it again
// Create threads
readThread = std::thread(&VCP::readTask, this);
writeThread = std::thread(&VCP::writeTask, this);
return true;
}
void VCP::openAsync(fn_boolCallback callback) {
threads.push_back(std::make_shared<std::thread>([&]() {
callback(open(true));
}));
}
bool VCP::close() {
if(!isOpen()) {
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
return false;
}
setIsClosing(true); // Signal the threads that we are closing
for(auto& t : threads)
t->join(); // Wait for the threads to close
readThread.join();
writeThread.join();
setIsClosing(false);
if(!CloseHandle(detail->handle)) {
report(APIEvent::Type::DriverFailedToClose, APIEvent::Severity::Error);
return false;
}
detail->handle = INVALID_HANDLE_VALUE;
bool ret = true; // If one of the events fails closing, we probably still want to try and close the others
if(detail->overlappedRead.hEvent != INVALID_HANDLE_VALUE) {
if(!CloseHandle(detail->overlappedRead.hEvent))
ret = false;
detail->overlappedRead.hEvent = INVALID_HANDLE_VALUE;
}
if(detail->overlappedWrite.hEvent != INVALID_HANDLE_VALUE) {
if(!CloseHandle(detail->overlappedWrite.hEvent))
ret = false;
detail->overlappedWrite.hEvent = INVALID_HANDLE_VALUE;
}
if(detail->overlappedWait.hEvent != INVALID_HANDLE_VALUE) {
if(!CloseHandle(detail->overlappedWait.hEvent))
ret = false;
detail->overlappedWait.hEvent = INVALID_HANDLE_VALUE;
}
clearBuffers();
if(!ret)
report(APIEvent::Type::DriverFailedToClose, APIEvent::Severity::Error);
// TODO Set up some sort of shared memory, free which COM port we had open so we can try to open it again
return ret;
}
bool VCP::isOpen() {
return detail->handle != INVALID_HANDLE_VALUE;
}
void VCP::readTask() {
constexpr size_t READ_BUFFER_SIZE = 10240;
uint8_t readbuf[READ_BUFFER_SIZE];
IOTaskState state = LAUNCH;
DWORD bytesRead = 0;
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
while(!isClosing() && !isDisconnected()) {
switch(state) {
case LAUNCH: {
COMSTAT comStatus;
unsigned long errorCodes;
ClearCommError(detail->handle, &errorCodes, &comStatus);
bytesRead = 0;
if(ReadFile(detail->handle, readbuf, READ_BUFFER_SIZE, nullptr, &detail->overlappedRead)) {
if(GetOverlappedResult(detail->handle, &detail->overlappedRead, &bytesRead, FALSE)) {
if(bytesRead)
pushRx(readbuf, bytesRead);
}
continue;
}
auto lastError = GetLastError();
if(lastError == ERROR_IO_PENDING)
state = WAIT;
else if(lastError != ERROR_SUCCESS) {
if(lastError == ERROR_ACCESS_DENIED) {
if(!isDisconnected()) {
setIsDisconnected(true);
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
}
} else
report(APIEvent::Type::FailedToRead, APIEvent::Severity::Error);
}
}
break;
case WAIT: {
auto ret = WaitForSingleObject(detail->overlappedRead.hEvent, 100);
if(ret == WAIT_OBJECT_0) {
if(GetOverlappedResult(detail->handle, &detail->overlappedRead, &bytesRead, FALSE)) {
pushRx(readbuf, bytesRead);
state = LAUNCH;
} else
report(APIEvent::Type::FailedToRead, APIEvent::Severity::Error);
}
if(ret == WAIT_ABANDONED || ret == WAIT_FAILED) {
state = LAUNCH;
report(APIEvent::Type::FailedToRead, APIEvent::Severity::Error);
}
}
}
}
}
void VCP::writeTask() {
IOTaskState state = LAUNCH;
VCP::WriteOperation writeOp;
DWORD bytesWritten = 0;
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
while(!isClosing() && !isDisconnected()) {
switch(state) {
case LAUNCH: {
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100)))
continue;
bytesWritten = 0;
if(WriteFile(detail->handle, writeOp.bytes.data(), (DWORD)writeOp.bytes.size(), nullptr, &detail->overlappedWrite))
continue;
auto winerr = GetLastError();
if(winerr == ERROR_IO_PENDING) {
state = WAIT;
}
else if(winerr == ERROR_ACCESS_DENIED) {
if(!isDisconnected()) {
setIsDisconnected(true);
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
}
} else
report(APIEvent::Type::FailedToWrite, APIEvent::Severity::Error);
}
break;
case WAIT: {
auto ret = WaitForSingleObject(detail->overlappedWrite.hEvent, 50);
if(ret == WAIT_OBJECT_0) {
if(!GetOverlappedResult(detail->handle, &detail->overlappedWrite, &bytesWritten, FALSE))
report(APIEvent::Type::FailedToWrite, APIEvent::Severity::Error);
state = LAUNCH;
}
if(ret == WAIT_ABANDONED) {
report(APIEvent::Type::FailedToWrite, APIEvent::Severity::Error);
state = LAUNCH;
}
}
}
}
}
-1
View File
@@ -46,7 +46,6 @@ local_scheme = "no-local-version"
[tool.scikit-build.cmake.define]
LIBICSNEO_ENABLE_BINDINGS_PYTHON = true
LIBICSNEO_ENABLE_TCP = true
CMAKE_MSVC_RUNTIME_LIBRARY = "MultiThreaded"
[tool.cibuildwheel]

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