mirror of
https://github.com/intrepidcs/libicsneo.git
synced 2026-09-20 16:08:37 +02:00
Compare commits
16
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6926ca8199 | ||
|
|
29dc7b345f | ||
|
|
17285389e3 | ||
|
|
328563b7e6 | ||
|
|
6b60804174 | ||
|
|
c5bce795c6 | ||
|
|
1e5e714f5b | ||
|
|
d70defbf8a | ||
|
|
10efacf91e | ||
|
|
6d985ca873 | ||
|
|
19df557b4a | ||
|
|
7afa41bf2a | ||
|
|
c056e8dc2e | ||
|
|
d5ae1cdb03 | ||
|
|
5795791eac | ||
|
|
c91db6355c |
@@ -15,3 +15,4 @@ third-party/concurrentqueue/tests
|
||||
examples/csharp/bin
|
||||
examples/csharp/obj
|
||||
test/system
|
||||
.env
|
||||
|
||||
+64
-88
@@ -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:
|
||||
|
||||
+25
-91
@@ -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()
|
||||
|
||||
@@ -292,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
|
||||
@@ -383,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)
|
||||
@@ -402,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)
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+36
-103
@@ -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!";
|
||||
@@ -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;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
//FILE: icsneo40DLLAPI.H
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
#include "icsneo/icsnVC40.h"
|
||||
|
||||
|
||||
@@ -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,6 +114,22 @@ 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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>())
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
@@ -324,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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
+203
-7
@@ -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;
|
||||
@@ -414,7 +421,8 @@ bool Device::disableLogData() {
|
||||
}
|
||||
|
||||
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();
|
||||
@@ -443,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()) {
|
||||
@@ -2235,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();
|
||||
@@ -3420,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
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
========================
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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__
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -591,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:
|
||||
@@ -887,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__
|
||||
|
||||
@@ -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_
|
||||
@@ -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"
|
||||
@@ -205,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();
|
||||
@@ -636,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
|
||||
|
||||
@@ -1019,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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -71,6 +71,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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -91,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:
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -137,6 +137,11 @@ namespace icsneo
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t getDiskCount() const override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -4,6 +4,8 @@
|
||||
#ifdef __cplusplus
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#include <string>
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
#include <Windows.h>
|
||||
|
||||
#include <icsneo/platform/windows/strings.h>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
#include "icsneo/api/periodic.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <condition_variable>
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
// no wait, make sure stop works
|
||||
TEST(PeriodicTest, StartStop)
|
||||
{
|
||||
const auto start = std::chrono::steady_clock::now();
|
||||
Periodic p([] { return true; }, std::chrono::milliseconds(1000));
|
||||
const auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start);
|
||||
EXPECT_LT(delta.count(), 100); // hopefully enough
|
||||
}
|
||||
|
||||
// time single cycle
|
||||
TEST(PeriodicTest, OneCycle)
|
||||
{
|
||||
std::condition_variable cv;
|
||||
std::mutex mutex;
|
||||
uint8_t cycles = 0;
|
||||
const auto start = std::chrono::steady_clock::now();
|
||||
{
|
||||
Periodic p([&] {
|
||||
{
|
||||
std::scoped_lock lk(mutex);
|
||||
++cycles;
|
||||
}
|
||||
cv.notify_one();
|
||||
return true;
|
||||
}, std::chrono::seconds(1));
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
cv.wait_for(lk, std::chrono::seconds(2), [&]{ return cycles > 0; });
|
||||
}
|
||||
const auto delta = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - start);
|
||||
EXPECT_EQ(delta.count(), 1);
|
||||
EXPECT_EQ(cycles, 1);
|
||||
}
|
||||
|
||||
TEST(PeriodicTest, TenCycles)
|
||||
{
|
||||
std::condition_variable cv;
|
||||
std::mutex mutex;
|
||||
uint8_t cycles = 0;
|
||||
const auto start = std::chrono::steady_clock::now();
|
||||
{
|
||||
Periodic p([&] {
|
||||
{
|
||||
std::scoped_lock lk(mutex);
|
||||
++cycles;
|
||||
}
|
||||
cv.notify_one();
|
||||
return true;
|
||||
}, std::chrono::milliseconds(100));
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
cv.wait_for(lk, std::chrono::seconds(2), [&]{ return cycles >= 10; });
|
||||
}
|
||||
const auto delta = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - start);
|
||||
EXPECT_EQ(delta.count(), 1);
|
||||
EXPECT_EQ(cycles, 10);
|
||||
}
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
# Run manually to reformat a file:
|
||||
# clang-format -i --style=file <file>
|
||||
Language: Cpp
|
||||
BasedOnStyle: Google
|
||||
@@ -1,84 +0,0 @@
|
||||
# Ignore CI build directory
|
||||
build/
|
||||
xcuserdata
|
||||
cmake-build-debug/
|
||||
.idea/
|
||||
bazel-bin
|
||||
bazel-genfiles
|
||||
bazel-googletest
|
||||
bazel-out
|
||||
bazel-testlogs
|
||||
# python
|
||||
*.pyc
|
||||
|
||||
# Visual Studio files
|
||||
.vs
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.VC.opendb
|
||||
*.suo
|
||||
*.user
|
||||
_ReSharper.Caches/
|
||||
Win32-Debug/
|
||||
Win32-Release/
|
||||
x64-Debug/
|
||||
x64-Release/
|
||||
|
||||
# Ignore autoconf / automake files
|
||||
Makefile.in
|
||||
aclocal.m4
|
||||
configure
|
||||
build-aux/
|
||||
autom4te.cache/
|
||||
googletest/m4/libtool.m4
|
||||
googletest/m4/ltoptions.m4
|
||||
googletest/m4/ltsugar.m4
|
||||
googletest/m4/ltversion.m4
|
||||
googletest/m4/lt~obsolete.m4
|
||||
googlemock/m4
|
||||
|
||||
# Ignore generated directories.
|
||||
googlemock/fused-src/
|
||||
googletest/fused-src/
|
||||
|
||||
# macOS files
|
||||
.DS_Store
|
||||
googletest/.DS_Store
|
||||
googletest/xcode/.DS_Store
|
||||
|
||||
# Ignore cmake generated directories and files.
|
||||
CMakeFiles
|
||||
CTestTestfile.cmake
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
googlemock/CMakeFiles
|
||||
googlemock/CTestTestfile.cmake
|
||||
googlemock/Makefile
|
||||
googlemock/cmake_install.cmake
|
||||
googlemock/gtest
|
||||
/bin
|
||||
/googlemock/gmock.dir
|
||||
/googlemock/gmock_main.dir
|
||||
/googlemock/RUN_TESTS.vcxproj.filters
|
||||
/googlemock/RUN_TESTS.vcxproj
|
||||
/googlemock/INSTALL.vcxproj.filters
|
||||
/googlemock/INSTALL.vcxproj
|
||||
/googlemock/gmock_main.vcxproj.filters
|
||||
/googlemock/gmock_main.vcxproj
|
||||
/googlemock/gmock.vcxproj.filters
|
||||
/googlemock/gmock.vcxproj
|
||||
/googlemock/gmock.sln
|
||||
/googlemock/ALL_BUILD.vcxproj.filters
|
||||
/googlemock/ALL_BUILD.vcxproj
|
||||
/lib
|
||||
/Win32
|
||||
/ZERO_CHECK.vcxproj.filters
|
||||
/ZERO_CHECK.vcxproj
|
||||
/RUN_TESTS.vcxproj.filters
|
||||
/RUN_TESTS.vcxproj
|
||||
/INSTALL.vcxproj.filters
|
||||
/INSTALL.vcxproj
|
||||
/googletest-distribution.sln
|
||||
/CMakeCache.txt
|
||||
/ALL_BUILD.vcxproj.filters
|
||||
/ALL_BUILD.vcxproj
|
||||
-77
@@ -1,77 +0,0 @@
|
||||
# Build matrix / environment variable are explained on:
|
||||
# https://docs.travis-ci.com/user/customizing-the-build/
|
||||
# This file can be validated on:
|
||||
# http://lint.travis-ci.org/
|
||||
|
||||
sudo: false
|
||||
language: cpp
|
||||
|
||||
# Define the matrix explicitly, manually expanding the combinations of (os, compiler, env).
|
||||
# It is more tedious, but grants us far more flexibility.
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
sudo: required
|
||||
before_install: chmod -R +x ./ci/*platformio.sh
|
||||
install: ./ci/install-platformio.sh
|
||||
script: ./ci/build-platformio.sh
|
||||
- os: linux
|
||||
dist: xenial
|
||||
compiler: gcc
|
||||
sudo : true
|
||||
install: ./ci/install-linux.sh && ./ci/log-config.sh
|
||||
script: ./ci/build-linux-bazel.sh
|
||||
- os: linux
|
||||
dist: xenial
|
||||
compiler: clang
|
||||
sudo : true
|
||||
install: ./ci/install-linux.sh && ./ci/log-config.sh
|
||||
script: ./ci/build-linux-bazel.sh
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
env: BUILD_TYPE=Debug VERBOSE=1 CXX_FLAGS=-std=c++11
|
||||
- os: linux
|
||||
compiler: clang
|
||||
env: BUILD_TYPE=Release VERBOSE=1 CXX_FLAGS=-std=c++11 -Wgnu-zero-variadic-macro-arguments
|
||||
- os: linux
|
||||
compiler: clang
|
||||
env: BUILD_TYPE=Release VERBOSE=1 CXX_FLAGS=-std=c++11 NO_EXCEPTION=ON NO_RTTI=ON COMPILER_IS_GNUCXX=ON
|
||||
- os: osx
|
||||
compiler: gcc
|
||||
env: BUILD_TYPE=Release VERBOSE=1 CXX_FLAGS=-std=c++11 HOMEBREW_LOGS=~/homebrew-logs HOMEBREW_TEMP=~/homebrew-temp
|
||||
- os: osx
|
||||
compiler: clang
|
||||
env: BUILD_TYPE=Release VERBOSE=1 CXX_FLAGS=-std=c++11 HOMEBREW_LOGS=~/homebrew-logs HOMEBREW_TEMP=~/homebrew-temp
|
||||
|
||||
# These are the install and build (script) phases for the most common entries in the matrix. They could be included
|
||||
# in each entry in the matrix, but that is just repetitive.
|
||||
install:
|
||||
- ./ci/install-${TRAVIS_OS_NAME}.sh
|
||||
- . ./ci/env-${TRAVIS_OS_NAME}.sh
|
||||
- ./ci/log-config.sh
|
||||
|
||||
script: ./ci/travis.sh
|
||||
|
||||
# For sudo=false builds this section installs the necessary dependencies.
|
||||
addons:
|
||||
apt:
|
||||
# List of whitelisted in travis packages for ubuntu-precise can be found here:
|
||||
# https://github.com/travis-ci/apt-package-whitelist/blob/master/ubuntu-precise
|
||||
# List of whitelisted in travis apt-sources:
|
||||
# https://github.com/travis-ci/apt-source-whitelist/blob/master/ubuntu.json
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-precise-3.9
|
||||
packages:
|
||||
- g++-4.9
|
||||
- clang-3.9
|
||||
update: true
|
||||
homebrew:
|
||||
packages:
|
||||
- ccache
|
||||
- gcc@4.9
|
||||
- llvm@3.9
|
||||
update: true
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
-190
@@ -1,190 +0,0 @@
|
||||
# Copyright 2017 Google Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
# Bazel Build for Google C++ Testing Framework(Google Test)
|
||||
|
||||
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
exports_files(["LICENSE"])
|
||||
|
||||
config_setting(
|
||||
name = "windows",
|
||||
constraint_values = ["@platforms//os:windows"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "msvc_compiler",
|
||||
flag_values = {
|
||||
"@bazel_tools//tools/cpp:compiler": "msvc-cl",
|
||||
},
|
||||
visibility = [":__subpackages__"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "has_absl",
|
||||
values = {"define": "absl=1"},
|
||||
)
|
||||
|
||||
# Library that defines the FRIEND_TEST macro.
|
||||
cc_library(
|
||||
name = "gtest_prod",
|
||||
hdrs = ["googletest/include/gtest/gtest_prod.h"],
|
||||
includes = ["googletest/include"],
|
||||
)
|
||||
|
||||
# Google Test including Google Mock
|
||||
cc_library(
|
||||
name = "gtest",
|
||||
srcs = glob(
|
||||
include = [
|
||||
"googletest/src/*.cc",
|
||||
"googletest/src/*.h",
|
||||
"googletest/include/gtest/**/*.h",
|
||||
"googlemock/src/*.cc",
|
||||
"googlemock/include/gmock/**/*.h",
|
||||
],
|
||||
exclude = [
|
||||
"googletest/src/gtest-all.cc",
|
||||
"googletest/src/gtest_main.cc",
|
||||
"googlemock/src/gmock-all.cc",
|
||||
"googlemock/src/gmock_main.cc",
|
||||
],
|
||||
),
|
||||
hdrs = glob([
|
||||
"googletest/include/gtest/*.h",
|
||||
"googlemock/include/gmock/*.h",
|
||||
]),
|
||||
copts = select({
|
||||
":windows": [],
|
||||
"//conditions:default": ["-pthread"],
|
||||
}),
|
||||
defines = select({
|
||||
":has_absl": ["GTEST_HAS_ABSL=1"],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
features = select({
|
||||
":windows": ["windows_export_all_symbols"],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
includes = [
|
||||
"googlemock",
|
||||
"googlemock/include",
|
||||
"googletest",
|
||||
"googletest/include",
|
||||
],
|
||||
linkopts = select({
|
||||
":windows": [],
|
||||
"//conditions:default": ["-pthread"],
|
||||
}),
|
||||
deps = select({
|
||||
":has_absl": [
|
||||
"@com_google_absl//absl/debugging:failure_signal_handler",
|
||||
"@com_google_absl//absl/debugging:stacktrace",
|
||||
"@com_google_absl//absl/debugging:symbolize",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_absl//absl/types:any",
|
||||
"@com_google_absl//absl/types:optional",
|
||||
"@com_google_absl//absl/types:variant",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "gtest_main",
|
||||
srcs = ["googlemock/src/gmock_main.cc"],
|
||||
features = select({
|
||||
":windows": ["windows_export_all_symbols"],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
deps = [":gtest"],
|
||||
)
|
||||
|
||||
# The following rules build samples of how to use gTest.
|
||||
cc_library(
|
||||
name = "gtest_sample_lib",
|
||||
srcs = [
|
||||
"googletest/samples/sample1.cc",
|
||||
"googletest/samples/sample2.cc",
|
||||
"googletest/samples/sample4.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"googletest/samples/prime_tables.h",
|
||||
"googletest/samples/sample1.h",
|
||||
"googletest/samples/sample2.h",
|
||||
"googletest/samples/sample3-inl.h",
|
||||
"googletest/samples/sample4.h",
|
||||
],
|
||||
features = select({
|
||||
":windows": ["windows_export_all_symbols"],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "gtest_samples",
|
||||
size = "small",
|
||||
# All Samples except:
|
||||
# sample9 (main)
|
||||
# sample10 (main and takes a command line option and needs to be separate)
|
||||
srcs = [
|
||||
"googletest/samples/sample1_unittest.cc",
|
||||
"googletest/samples/sample2_unittest.cc",
|
||||
"googletest/samples/sample3_unittest.cc",
|
||||
"googletest/samples/sample4_unittest.cc",
|
||||
"googletest/samples/sample5_unittest.cc",
|
||||
"googletest/samples/sample6_unittest.cc",
|
||||
"googletest/samples/sample7_unittest.cc",
|
||||
"googletest/samples/sample8_unittest.cc",
|
||||
],
|
||||
linkstatic = 0,
|
||||
deps = [
|
||||
"gtest_sample_lib",
|
||||
":gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "sample9_unittest",
|
||||
size = "small",
|
||||
srcs = ["googletest/samples/sample9_unittest.cc"],
|
||||
deps = [":gtest"],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "sample10_unittest",
|
||||
size = "small",
|
||||
srcs = ["googletest/samples/sample10_unittest.cc"],
|
||||
deps = [":gtest"],
|
||||
)
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
# Note: CMake support is community-based. The maintainers do not use CMake
|
||||
# internally.
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
|
||||
if (POLICY CMP0048)
|
||||
cmake_policy(SET CMP0048 NEW)
|
||||
endif (POLICY CMP0048)
|
||||
|
||||
project(googletest-distribution)
|
||||
set(GOOGLETEST_VERSION 1.11.0)
|
||||
|
||||
if (CMAKE_VERSION VERSION_GREATER "3.0.2")
|
||||
if(NOT CYGWIN AND NOT MSYS AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL QNX)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
enable_testing()
|
||||
|
||||
include(CMakeDependentOption)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
#Note that googlemock target already builds googletest
|
||||
option(BUILD_GMOCK "Builds the googlemock subproject" ON)
|
||||
option(INSTALL_GTEST "Enable installation of googletest. (Projects embedding googletest may want to turn this OFF.)" ON)
|
||||
|
||||
if(BUILD_GMOCK)
|
||||
add_subdirectory( googlemock )
|
||||
else()
|
||||
add_subdirectory( googletest )
|
||||
endif()
|
||||
-130
@@ -1,130 +0,0 @@
|
||||
# How to become a contributor and submit your own code
|
||||
|
||||
## Contributor License Agreements
|
||||
|
||||
We'd love to accept your patches! Before we can take them, we have to jump a
|
||||
couple of legal hurdles.
|
||||
|
||||
Please fill out either the individual or corporate Contributor License Agreement
|
||||
(CLA).
|
||||
|
||||
* If you are an individual writing original source code and you're sure you
|
||||
own the intellectual property, then you'll need to sign an
|
||||
[individual CLA](https://developers.google.com/open-source/cla/individual).
|
||||
* If you work for a company that wants to allow you to contribute your work,
|
||||
then you'll need to sign a
|
||||
[corporate CLA](https://developers.google.com/open-source/cla/corporate).
|
||||
|
||||
Follow either of the two links above to access the appropriate CLA and
|
||||
instructions for how to sign and return it. Once we receive it, we'll be able to
|
||||
accept your pull requests.
|
||||
|
||||
## Are you a Googler?
|
||||
|
||||
If you are a Googler, please make an attempt to submit an internal change rather
|
||||
than a GitHub Pull Request. If you are not able to submit an internal change a
|
||||
PR is acceptable as an alternative.
|
||||
|
||||
## Contributing A Patch
|
||||
|
||||
1. Submit an issue describing your proposed change to the
|
||||
[issue tracker](https://github.com/google/googletest/issues).
|
||||
2. Please don't mix more than one logical change per submittal, because it
|
||||
makes the history hard to follow. If you want to make a change that doesn't
|
||||
have a corresponding issue in the issue tracker, please create one.
|
||||
3. Also, coordinate with team members that are listed on the issue in question.
|
||||
This ensures that work isn't being duplicated and communicating your plan
|
||||
early also generally leads to better patches.
|
||||
4. If your proposed change is accepted, and you haven't already done so, sign a
|
||||
Contributor License Agreement (see details above).
|
||||
5. Fork the desired repo, develop and test your code changes.
|
||||
6. Ensure that your code adheres to the existing style in the sample to which
|
||||
you are contributing.
|
||||
7. Ensure that your code has an appropriate set of unit tests which all pass.
|
||||
8. Submit a pull request.
|
||||
|
||||
## The Google Test and Google Mock Communities
|
||||
|
||||
The Google Test community exists primarily through the
|
||||
[discussion group](http://groups.google.com/group/googletestframework) and the
|
||||
GitHub repository. Likewise, the Google Mock community exists primarily through
|
||||
their own [discussion group](http://groups.google.com/group/googlemock). You are
|
||||
definitely encouraged to contribute to the discussion and you can also help us
|
||||
to keep the effectiveness of the group high by following and promoting the
|
||||
guidelines listed here.
|
||||
|
||||
### Please Be Friendly
|
||||
|
||||
Showing courtesy and respect to others is a vital part of the Google culture,
|
||||
and we strongly encourage everyone participating in Google Test development to
|
||||
join us in accepting nothing less. Of course, being courteous is not the same as
|
||||
failing to constructively disagree with each other, but it does mean that we
|
||||
should be respectful of each other when enumerating the 42 technical reasons
|
||||
that a particular proposal may not be the best choice. There's never a reason to
|
||||
be antagonistic or dismissive toward anyone who is sincerely trying to
|
||||
contribute to a discussion.
|
||||
|
||||
Sure, C++ testing is serious business and all that, but it's also a lot of fun.
|
||||
Let's keep it that way. Let's strive to be one of the friendliest communities in
|
||||
all of open source.
|
||||
|
||||
As always, discuss Google Test in the official GoogleTest discussion group. You
|
||||
don't have to actually submit code in order to sign up. Your participation
|
||||
itself is a valuable contribution.
|
||||
|
||||
## Style
|
||||
|
||||
To keep the source consistent, readable, diffable and easy to merge, we use a
|
||||
fairly rigid coding style, as defined by the
|
||||
[google-styleguide](https://github.com/google/styleguide) project. All patches
|
||||
will be expected to conform to the style outlined
|
||||
[here](https://google.github.io/styleguide/cppguide.html). Use
|
||||
[.clang-format](https://github.com/google/googletest/blob/master/.clang-format)
|
||||
to check your formatting.
|
||||
|
||||
## Requirements for Contributors
|
||||
|
||||
If you plan to contribute a patch, you need to build Google Test, Google Mock,
|
||||
and their own tests from a git checkout, which has further requirements:
|
||||
|
||||
* [Python](https://www.python.org/) v2.3 or newer (for running some of the
|
||||
tests and re-generating certain source files from templates)
|
||||
* [CMake](https://cmake.org/) v2.8.12 or newer
|
||||
|
||||
## Developing Google Test and Google Mock
|
||||
|
||||
This section discusses how to make your own changes to the Google Test project.
|
||||
|
||||
### Testing Google Test and Google Mock Themselves
|
||||
|
||||
To make sure your changes work as intended and don't break existing
|
||||
functionality, you'll want to compile and run Google Test and GoogleMock's own
|
||||
tests. For that you can use CMake:
|
||||
|
||||
mkdir mybuild
|
||||
cd mybuild
|
||||
cmake -Dgtest_build_tests=ON -Dgmock_build_tests=ON ${GTEST_REPO_DIR}
|
||||
|
||||
To choose between building only Google Test or Google Mock, you may modify your
|
||||
cmake command to be one of each
|
||||
|
||||
cmake -Dgtest_build_tests=ON ${GTEST_DIR} # sets up Google Test tests
|
||||
cmake -Dgmock_build_tests=ON ${GMOCK_DIR} # sets up Google Mock tests
|
||||
|
||||
Make sure you have Python installed, as some of Google Test's tests are written
|
||||
in Python. If the cmake command complains about not being able to find Python
|
||||
(`Could NOT find PythonInterp (missing: PYTHON_EXECUTABLE)`), try telling it
|
||||
explicitly where your Python executable can be found:
|
||||
|
||||
cmake -DPYTHON_EXECUTABLE=path/to/python ...
|
||||
|
||||
Next, you can build Google Test and / or Google Mock and all desired tests. On
|
||||
\*nix, this is usually done by
|
||||
|
||||
make
|
||||
|
||||
To run the tests, do
|
||||
|
||||
make test
|
||||
|
||||
All tests should pass.
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
# This file contains a list of people who've made non-trivial
|
||||
# contribution to the Google C++ Testing Framework project. People
|
||||
# who commit code to the project are encouraged to add their names
|
||||
# here. Please keep the list sorted by first names.
|
||||
|
||||
Ajay Joshi <jaj@google.com>
|
||||
Balázs Dán <balazs.dan@gmail.com>
|
||||
Benoit Sigoure <tsuna@google.com>
|
||||
Bharat Mediratta <bharat@menalto.com>
|
||||
Bogdan Piloca <boo@google.com>
|
||||
Chandler Carruth <chandlerc@google.com>
|
||||
Chris Prince <cprince@google.com>
|
||||
Chris Taylor <taylorc@google.com>
|
||||
Dan Egnor <egnor@google.com>
|
||||
Dave MacLachlan <dmaclach@gmail.com>
|
||||
David Anderson <danderson@google.com>
|
||||
Dean Sturtevant
|
||||
Eric Roman <eroman@chromium.org>
|
||||
Gene Volovich <gv@cite.com>
|
||||
Hady Zalek <hady.zalek@gmail.com>
|
||||
Hal Burch <gmock@hburch.com>
|
||||
Jeffrey Yasskin <jyasskin@google.com>
|
||||
Jim Keller <jimkeller@google.com>
|
||||
Joe Walnes <joe@truemesh.com>
|
||||
Jon Wray <jwray@google.com>
|
||||
Jói Sigurðsson <joi@google.com>
|
||||
Keir Mierle <mierle@gmail.com>
|
||||
Keith Ray <keith.ray@gmail.com>
|
||||
Kenton Varda <kenton@google.com>
|
||||
Kostya Serebryany <kcc@google.com>
|
||||
Krystian Kuzniarek <krystian.kuzniarek@gmail.com>
|
||||
Lev Makhlis
|
||||
Manuel Klimek <klimek@google.com>
|
||||
Mario Tanev <radix@google.com>
|
||||
Mark Paskin
|
||||
Markus Heule <markus.heule@gmail.com>
|
||||
Matthew Simmons <simmonmt@acm.org>
|
||||
Mika Raento <mikie@iki.fi>
|
||||
Mike Bland <mbland@google.com>
|
||||
Miklós Fazekas <mfazekas@szemafor.com>
|
||||
Neal Norwitz <nnorwitz@gmail.com>
|
||||
Nermin Ozkiranartli <nermin@google.com>
|
||||
Owen Carlsen <ocarlsen@google.com>
|
||||
Paneendra Ba <paneendra@google.com>
|
||||
Pasi Valminen <pasi.valminen@gmail.com>
|
||||
Patrick Hanna <phanna@google.com>
|
||||
Patrick Riley <pfr@google.com>
|
||||
Paul Menage <menage@google.com>
|
||||
Peter Kaminski <piotrk@google.com>
|
||||
Piotr Kaminski <piotrk@google.com>
|
||||
Preston Jackson <preston.a.jackson@gmail.com>
|
||||
Rainer Klaffenboeck <rainer.klaffenboeck@dynatrace.com>
|
||||
Russ Cox <rsc@google.com>
|
||||
Russ Rufer <russ@pentad.com>
|
||||
Sean Mcafee <eefacm@gmail.com>
|
||||
Sigurður Ásgeirsson <siggi@google.com>
|
||||
Sverre Sundsdal <sundsdal@gmail.com>
|
||||
Takeshi Yoshino <tyoshino@google.com>
|
||||
Tracy Bialik <tracy@pentad.com>
|
||||
Vadim Berman <vadimb@google.com>
|
||||
Vlad Losev <vladl@google.com>
|
||||
Wolfgang Klier <wklier@google.com>
|
||||
Zhanyong Wan <wan@google.com>
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
Copyright 2008, Google Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-140
@@ -1,140 +0,0 @@
|
||||
# GoogleTest
|
||||
|
||||
### Announcements
|
||||
|
||||
#### Live at Head
|
||||
|
||||
GoogleTest now follows the
|
||||
[Abseil Live at Head philosophy](https://abseil.io/about/philosophy#upgrade-support).
|
||||
We recommend using the latest commit in the `master` branch in your projects.
|
||||
|
||||
#### Documentation Updates
|
||||
|
||||
Our documentation is now live on GitHub Pages at
|
||||
https://google.github.io/googletest/. We recommend browsing the documentation on
|
||||
GitHub Pages rather than directly in the repository.
|
||||
|
||||
#### Release 1.10.x
|
||||
|
||||
[Release 1.10.x](https://github.com/google/googletest/releases/tag/release-1.10.0)
|
||||
is now available.
|
||||
|
||||
#### Coming Soon
|
||||
|
||||
* We are planning to take a dependency on
|
||||
[Abseil](https://github.com/abseil/abseil-cpp).
|
||||
* More documentation improvements are planned.
|
||||
|
||||
## Welcome to **GoogleTest**, Google's C++ test framework!
|
||||
|
||||
This repository is a merger of the formerly separate GoogleTest and GoogleMock
|
||||
projects. These were so closely related that it makes sense to maintain and
|
||||
release them together.
|
||||
|
||||
### Getting Started
|
||||
|
||||
See the [GoogleTest User's Guide](https://google.github.io/googletest/) for
|
||||
documentation. We recommend starting with the
|
||||
[GoogleTest Primer](https://google.github.io/googletest/primer.html).
|
||||
|
||||
More information about building GoogleTest can be found at
|
||||
[googletest/README.md](googletest/README.md).
|
||||
|
||||
## Features
|
||||
|
||||
* An [xUnit](https://en.wikipedia.org/wiki/XUnit) test framework.
|
||||
* Test discovery.
|
||||
* A rich set of assertions.
|
||||
* User-defined assertions.
|
||||
* Death tests.
|
||||
* Fatal and non-fatal failures.
|
||||
* Value-parameterized tests.
|
||||
* Type-parameterized tests.
|
||||
* Various options for running the tests.
|
||||
* XML test report generation.
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
GoogleTest requires a codebase and compiler compliant with the C++11 standard or
|
||||
newer.
|
||||
|
||||
The GoogleTest code is officially supported on the following platforms.
|
||||
Operating systems or tools not listed below are community-supported. For
|
||||
community-supported platforms, patches that do not complicate the code may be
|
||||
considered.
|
||||
|
||||
If you notice any problems on your platform, please file an issue on the
|
||||
[GoogleTest GitHub Issue Tracker](https://github.com/google/googletest/issues).
|
||||
Pull requests containing fixes are welcome!
|
||||
|
||||
### Operating Systems
|
||||
|
||||
* Linux
|
||||
* macOS
|
||||
* Windows
|
||||
|
||||
### Compilers
|
||||
|
||||
* gcc 5.0+
|
||||
* clang 5.0+
|
||||
* MSVC 2015+
|
||||
|
||||
**macOS users:** Xcode 9.3+ provides clang 5.0+.
|
||||
|
||||
### Build Systems
|
||||
|
||||
* [Bazel](https://bazel.build/)
|
||||
* [CMake](https://cmake.org/)
|
||||
|
||||
**Note:** Bazel is the build system used by the team internally and in tests.
|
||||
CMake is supported on a best-effort basis and by the community.
|
||||
|
||||
## Who Is Using GoogleTest?
|
||||
|
||||
In addition to many internal projects at Google, GoogleTest is also used by the
|
||||
following notable projects:
|
||||
|
||||
* The [Chromium projects](http://www.chromium.org/) (behind the Chrome browser
|
||||
and Chrome OS).
|
||||
* The [LLVM](http://llvm.org/) compiler.
|
||||
* [Protocol Buffers](https://github.com/google/protobuf), Google's data
|
||||
interchange format.
|
||||
* The [OpenCV](http://opencv.org/) computer vision library.
|
||||
|
||||
## Related Open Source Projects
|
||||
|
||||
[GTest Runner](https://github.com/nholthaus/gtest-runner) is a Qt5 based
|
||||
automated test-runner and Graphical User Interface with powerful features for
|
||||
Windows and Linux platforms.
|
||||
|
||||
[GoogleTest UI](https://github.com/ospector/gtest-gbar) is a test runner that
|
||||
runs your test binary, allows you to track its progress via a progress bar, and
|
||||
displays a list of test failures. Clicking on one shows failure text. Google
|
||||
Test UI is written in C#.
|
||||
|
||||
[GTest TAP Listener](https://github.com/kinow/gtest-tap-listener) is an event
|
||||
listener for GoogleTest that implements the
|
||||
[TAP protocol](https://en.wikipedia.org/wiki/Test_Anything_Protocol) for test
|
||||
result output. If your test runner understands TAP, you may find it useful.
|
||||
|
||||
[gtest-parallel](https://github.com/google/gtest-parallel) is a test runner that
|
||||
runs tests from your binary in parallel to provide significant speed-up.
|
||||
|
||||
[GoogleTest Adapter](https://marketplace.visualstudio.com/items?itemName=DavidSchuldenfrei.gtest-adapter)
|
||||
is a VS Code extension allowing to view GoogleTest in a tree view, and run/debug
|
||||
your tests.
|
||||
|
||||
[C++ TestMate](https://github.com/matepek/vscode-catch2-test-adapter) is a VS
|
||||
Code extension allowing to view GoogleTest in a tree view, and run/debug your
|
||||
tests.
|
||||
|
||||
[Cornichon](https://pypi.org/project/cornichon/) is a small Gherkin DSL parser
|
||||
that generates stub code for GoogleTest.
|
||||
|
||||
## Contributing Changes
|
||||
|
||||
Please read
|
||||
[`CONTRIBUTING.md`](https://github.com/google/googletest/blob/master/CONTRIBUTING.md)
|
||||
for details on how to contribute to this project.
|
||||
|
||||
Happy testing!
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user