Feature/bacnet secure connect hub (#818)
* Added BACnet Secure Connect datalink. * Added BACnet/SC hub application --------- Co-authored-by: Kirill Neznamov <kirill.neznamov@dsr-corporation.com> Co-authored-by: Mikhail Antropov <michail.antropov@dsr-corporation.com> Co-authored-by: Ondřej Hruška <ondra@ondrovo.com> Co-authored-by: Patrick Grimm <patrick@lunatiki.de>
This commit is contained in:
@@ -0,0 +1,81 @@
|
|||||||
|
name: BACNet/SC linux tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
job_bsc_tests_linux:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Create Build Environment
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get install -qq libconfig-dev
|
||||||
|
sudo apt-get install -qq libcap-dev
|
||||||
|
sudo apt-get install -qq libssl-dev
|
||||||
|
sudo apt-get install -qq libuv1-dev
|
||||||
|
git clone --branch v4.3-stable https://github.com/warmcat/libwebsockets.git
|
||||||
|
bash -c 'cd libwebsockets;mkdir build;cd build;cmake .. -DLWS_WITH_LIBUV=ON -DLWS_WITH_MINIMAL_EXAMPLES=0 -DLWS_MAX_SMP=32;make'
|
||||||
|
sudo bash -c 'cd libwebsockets;cd build;make install'
|
||||||
|
- name: Build and run bsc_event test
|
||||||
|
run: |
|
||||||
|
cd ./test/ports/linux/bsc_event
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
./test_bsc_event
|
||||||
|
- name: Build and run bvlc-sc test
|
||||||
|
run: |
|
||||||
|
cd ./test/bacnet/datalink/bvlc-sc
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
./test_bvlc-sc
|
||||||
|
- name: Build and run websockets test
|
||||||
|
run: |
|
||||||
|
cd ./test/bacnet/datalink/websockets
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
./test_websockets
|
||||||
|
- name: Build and run bsc-socket test
|
||||||
|
run: |
|
||||||
|
cd ./test/bacnet/datalink/bsc-socket
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
./test_bsc-socket
|
||||||
|
- name: Build and run hub-sc test
|
||||||
|
run: |
|
||||||
|
cd ./test/bacnet/datalink/hub-sc
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
./test_hub-sc
|
||||||
|
- name: Build and run bsc-node test
|
||||||
|
run: |
|
||||||
|
cd ./test/bacnet/datalink/bsc-node
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
./test_bsc-node
|
||||||
|
- name: Build and run bsc-datalink test
|
||||||
|
run: |
|
||||||
|
cd ./test/bacnet/datalink/bsc-datalink
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
./test_bsc-datalink
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
name: BACNet/SC macos/bsd tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
job_bsc_tests_macos:
|
||||||
|
runs-on: macOS-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Create Build Environment
|
||||||
|
run: |
|
||||||
|
brew install openssl
|
||||||
|
brew install libwebsockets
|
||||||
|
- name: Build and run bsc_event test
|
||||||
|
run: |
|
||||||
|
cd ./test/ports/bsd/bsc_event
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
./test_bsc_event
|
||||||
|
- name: Build and run bvlc-sc test
|
||||||
|
run: |
|
||||||
|
cd ./test/bacnet/datalink/bvlc-sc
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
./test_bvlc-sc
|
||||||
|
- name: Build and run websockets test
|
||||||
|
run: |
|
||||||
|
cd ./test/bacnet/datalink/websockets
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl@3.0
|
||||||
|
make
|
||||||
|
./test_websockets
|
||||||
|
- name: Build and run bsc-socket test
|
||||||
|
run: |
|
||||||
|
cd ./test/bacnet/datalink/bsc-socket
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
./test_bsc-socket
|
||||||
|
- name: Build and run hub-sc test
|
||||||
|
run: |
|
||||||
|
cd ./test/bacnet/datalink/hub-sc
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl@3.0
|
||||||
|
make
|
||||||
|
./test_hub-sc
|
||||||
|
- name: Build and run bsc-node test
|
||||||
|
run: |
|
||||||
|
cd ./test/bacnet/datalink/bsc-node
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl@3.0
|
||||||
|
make
|
||||||
|
./test_bsc-node
|
||||||
|
- name: Build and run bsc-datalink test
|
||||||
|
run: |
|
||||||
|
cd ./test/bacnet/datalink/bsc-datalink
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl@3.0
|
||||||
|
make
|
||||||
|
./test_bsc-datalink
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
name: BACNet/SC windows tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
job_bsc_tests_windows:
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Create Build Environment
|
||||||
|
run: |
|
||||||
|
rmdir 'C:\Program Files\OpenSSL' -Recurse -Force
|
||||||
|
rmdir 'C:\Strawberry' -Recurse -Force
|
||||||
|
copy c:\vcpkg\triplets\x64-windows.cmake c:\vcpkg\triplets\x64-windows-custom.cmake
|
||||||
|
"set(VCPKG_BUILD_TYPE release)`n" >> c:\vcpkg\triplets\x64-windows-custom.cmake
|
||||||
|
c:\vcpkg\vcpkg.exe install pthreads --triplet x64-windows-custom
|
||||||
|
c:\vcpkg\vcpkg.exe install libuv --triplet x64-windows-custom
|
||||||
|
c:\vcpkg\vcpkg.exe install openssl --triplet x64-windows-custom
|
||||||
|
c:\vcpkg\vcpkg.exe install libwebsockets --triplet x64-windows-custom
|
||||||
|
git clone --branch v4.3-stable https://github.com/warmcat/libwebsockets.git
|
||||||
|
cd libwebsockets
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DOPENSSL_ROOT_DIR=c:\vcpkg\installed\x64-windows-custom -DCMAKE_TOOLCHAIN_FILE=c:\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-custom -DCMAKE_INSTALL_PREFIX="./pkg" -DLWS_WITH_MINIMAL_EXAMPLES=0 -DLWS_MAX_SMP=32 -DLWS_WITH_LIBUV=1 -DLWS_HAVE_PTHREAD_H=1 -DLWS_EXT_PTHREAD_INCLUDE_DIR="C:\vcpkg\installed\x64-windows-custom\include" -DLWS_EXT_PTHREAD_LIBRARIES="C:\vcpkg\installed\x64-windows-custom\lib\pthreadVC3.lib"
|
||||||
|
cmake --build . --config RELEASE
|
||||||
|
cmake --install . --config RELEASE
|
||||||
|
Copy-Item -Path .\pkg\bin -Destination c:\vcpkg\installed\x64-windows-custom -Recurse -Force
|
||||||
|
Copy-Item -Path .\pkg\include -Destination c:\vcpkg\installed\x64-windows-custom -Recurse -Force
|
||||||
|
Copy-Item -Path .\pkg\lib -Destination c:\vcpkg\installed\x64-windows-custom -Recurse -Force
|
||||||
|
Copy-Item -Path .\pkg\share -Destination c:\vcpkg\installed\x64-windows-custom -Recurse -Force
|
||||||
|
cd ../../
|
||||||
|
- name: Setup MSBuild
|
||||||
|
uses: microsoft/setup-msbuild@v1.1
|
||||||
|
with:
|
||||||
|
msbuild-architecture: x64
|
||||||
|
- name: Build and run bsc_event test
|
||||||
|
run: |
|
||||||
|
cd ./test/ports/win32/bsc_event
|
||||||
|
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=c:\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-custom
|
||||||
|
msbuild build/test_bsc_event.sln /Property:Configuration=Release
|
||||||
|
./build/Release/test_bsc_event.exe
|
||||||
|
- name: Build and run bvlc-sc test
|
||||||
|
run: |
|
||||||
|
cd ./test/bacnet/datalink/bvlc-sc
|
||||||
|
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=c:\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-custom
|
||||||
|
msbuild build/test_bvlc-sc.sln /Property:Configuration=Release
|
||||||
|
./build/Release/test_bvlc-sc.exe
|
||||||
|
- name: Build and run websockets test
|
||||||
|
run: |
|
||||||
|
cd ./test/bacnet/datalink/websockets
|
||||||
|
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=c:\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-custom
|
||||||
|
msbuild build/test_websockets.sln /Property:Configuration=Release
|
||||||
|
./build/Release/test_websockets.exe
|
||||||
|
- name: Build and run bsc-socket test
|
||||||
|
run: |
|
||||||
|
cd ./test/bacnet/datalink/bsc-socket
|
||||||
|
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=c:\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-custom
|
||||||
|
msbuild build/test_bsc-socket.sln /Property:Configuration=Release
|
||||||
|
./build/Release/test_bsc-socket.exe
|
||||||
|
# (Link target) ->
|
||||||
|
# bacreal.obj : error LNK2019: unresolved external symbol big_endian referenced in function decode_double [D:\a\bacnet-stack-upstream\bacnet-stack-upstream\test\bacnet\datalink\hub-sc\build\test_hub-sc.vcxproj]
|
||||||
|
# - name: Build and run hub-sc test
|
||||||
|
# run: |
|
||||||
|
# cd ./test/bacnet/datalink/hub-sc
|
||||||
|
# cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=c:\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-custom
|
||||||
|
# msbuild build/test_hub-sc.sln /Property:Configuration=Release
|
||||||
|
# ./build/Release/test_hub-sc.exe
|
||||||
|
- name: Build and run bsc-node test
|
||||||
|
run: |
|
||||||
|
cd ./test/bacnet/datalink/bsc-node
|
||||||
|
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=c:\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-custom
|
||||||
|
msbuild build/test_bsc-node.sln /Property:Configuration=Release
|
||||||
|
./build/Release/test_bsc-node.exe
|
||||||
|
- name: Build and run bsc-datalink test
|
||||||
|
run: |
|
||||||
|
cd ./test/bacnet/datalink/bsc-datalink
|
||||||
|
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=c:\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-custom
|
||||||
|
msbuild build/test_bsc-datalink.sln /Property:Configuration=Release
|
||||||
|
./build/Release/test_bsc-datalink.exe
|
||||||
@@ -77,6 +77,26 @@ jobs:
|
|||||||
make clean
|
make clean
|
||||||
make LEGACY=true gateway
|
make LEGACY=true gateway
|
||||||
|
|
||||||
|
bacnet-sc-hub:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Create BACnet/SC Build Environment
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get install -qq libconfig-dev
|
||||||
|
sudo apt-get install -qq libcap-dev
|
||||||
|
sudo apt-get install -qq libssl-dev
|
||||||
|
sudo apt-get install -qq libuv1-dev
|
||||||
|
git clone --branch v4.3-stable https://github.com/warmcat/libwebsockets.git
|
||||||
|
bash -c 'cd libwebsockets;mkdir build;cd build;cmake .. -DLWS_WITH_LIBUV=ON -DLWS_WITH_MINIMAL_EXAMPLES=0 -DLWS_MAX_SMP=32;make'
|
||||||
|
sudo bash -c 'cd libwebsockets;cd build;make install'
|
||||||
|
- name: Build BACnet/SC Hub Demo
|
||||||
|
run: |
|
||||||
|
gcc --version
|
||||||
|
make clean
|
||||||
|
make sc-hub
|
||||||
|
|
||||||
router:
|
router:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
@@ -85,6 +105,12 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
sudo apt-get update -qq
|
sudo apt-get update -qq
|
||||||
sudo apt-get install -qq libconfig-dev
|
sudo apt-get install -qq libconfig-dev
|
||||||
|
sudo apt-get install -qq libcap-dev
|
||||||
|
sudo apt-get install -qq libssl-dev
|
||||||
|
sudo apt-get install -qq libuv1-dev
|
||||||
|
git clone --branch v4.3-stable https://github.com/warmcat/libwebsockets.git
|
||||||
|
bash -c 'cd libwebsockets;mkdir build;cd build;cmake .. -DLWS_WITH_LIBUV=ON -DLWS_WITH_MINIMAL_EXAMPLES=0 -DLWS_MAX_SMP=32;make'
|
||||||
|
sudo bash -c 'cd libwebsockets;cd build;make install'
|
||||||
- name: Build Router Demo
|
- name: Build Router Demo
|
||||||
run: |
|
run: |
|
||||||
gcc --version
|
gcc --version
|
||||||
|
|||||||
@@ -88,5 +88,11 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
sudo apt-get update -qq
|
sudo apt-get update -qq
|
||||||
sudo apt-get install -qq lcov
|
sudo apt-get install -qq lcov
|
||||||
|
sudo apt-get install -qq libssl-dev
|
||||||
|
sudo apt-get install -qq libcap-dev
|
||||||
|
sudo apt-get install -qq libuv1-dev
|
||||||
|
git clone --branch v4.3-stable https://github.com/warmcat/libwebsockets.git
|
||||||
|
bash -c 'cd libwebsockets;mkdir build;cd build;cmake .. -DLWS_WITH_LIBUV=ON -DLWS_WITH_MINIMAL_EXAMPLES=0 -DLWS_MAX_SMP=32;make'
|
||||||
|
sudo bash -c 'cd libwebsockets;cd build;make install'
|
||||||
- name: Run Unit Test with Code Coverage
|
- name: Run Unit Test with Code Coverage
|
||||||
run: make test
|
run: make test
|
||||||
|
|||||||
+69
-5
@@ -64,13 +64,19 @@ option(
|
|||||||
"compile with ipv6 datalink support"
|
"compile with ipv6 datalink support"
|
||||||
ON)
|
ON)
|
||||||
|
|
||||||
|
option(
|
||||||
|
BACDL_BSC
|
||||||
|
"compile with secure-connect support"
|
||||||
|
OFF)
|
||||||
|
|
||||||
if(NOT (BACDL_ETHERNET OR
|
if(NOT (BACDL_ETHERNET OR
|
||||||
BACDL_MSTP OR
|
BACDL_MSTP OR
|
||||||
BACDL_ARCNET OR
|
BACDL_ARCNET OR
|
||||||
BACDL_BIP OR
|
BACDL_BIP OR
|
||||||
BACDL_BIP6 OR
|
BACDL_BIP6 OR
|
||||||
|
BACDL_BSC OR
|
||||||
BACDL_CUSTOM))
|
BACDL_CUSTOM))
|
||||||
add_definitions(-DBACDL_NONE)
|
add_definitions(-DBACDL_NONE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
option(
|
option(
|
||||||
@@ -174,6 +180,9 @@ elseif(MSVC)
|
|||||||
|
|
||||||
# Might be slower if builded with /Qspectre
|
# Might be slower if builded with /Qspectre
|
||||||
add_compile_options(/wd5045)
|
add_compile_options(/wd5045)
|
||||||
|
|
||||||
|
# format string expected in argument 3 is not a string literal. secure_connect.c:1414
|
||||||
|
add_compile_options(/wd4774)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "AppleClang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "AppleClang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||||
@@ -186,6 +195,15 @@ endif()
|
|||||||
|
|
||||||
find_package(Threads)
|
find_package(Threads)
|
||||||
|
|
||||||
|
if(BACDL_BSC)
|
||||||
|
find_package(libwebsockets CONFIG REQUIRED)
|
||||||
|
find_package(PkgConfig)
|
||||||
|
pkg_check_modules(LIB_WEBSOCKETS REQUIRED libwebsockets)
|
||||||
|
#libwebsocket need C99 with variadic-macros
|
||||||
|
add_compile_options(-Wno-variadic-macros)
|
||||||
|
find_package(OpenSSL)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(UCI)
|
if(UCI)
|
||||||
FIND_PATH(uci_include_dir uci.h)
|
FIND_PATH(uci_include_dir uci.h)
|
||||||
FIND_LIBRARY(uci_lib NAMES uci)
|
FIND_LIBRARY(uci_lib NAMES uci)
|
||||||
@@ -257,12 +275,30 @@ add_library(${PROJECT_NAME}
|
|||||||
src/bacnet/weeklyschedule.h
|
src/bacnet/weeklyschedule.h
|
||||||
src/bacnet/special_event.c
|
src/bacnet/special_event.c
|
||||||
src/bacnet/special_event.h
|
src/bacnet/special_event.h
|
||||||
|
src/bacnet/secure_connect.c
|
||||||
|
src/bacnet/secure_connect.h
|
||||||
$<$<BOOL:${BACDL_BIP}>:src/bacnet/basic/bbmd/h_bbmd.c>
|
$<$<BOOL:${BACDL_BIP}>:src/bacnet/basic/bbmd/h_bbmd.c>
|
||||||
$<$<BOOL:${BACDL_BIP}>:src/bacnet/basic/bbmd/h_bbmd.h>
|
$<$<BOOL:${BACDL_BIP}>:src/bacnet/basic/bbmd/h_bbmd.h>
|
||||||
$<$<BOOL:${BACDL_BIP6}>:src/bacnet/basic/bbmd6/h_bbmd6.c>
|
$<$<BOOL:${BACDL_BIP6}>:src/bacnet/basic/bbmd6/h_bbmd6.c>
|
||||||
$<$<BOOL:${BACDL_BIP6}>:src/bacnet/basic/bbmd6/h_bbmd6.h>
|
$<$<BOOL:${BACDL_BIP6}>:src/bacnet/basic/bbmd6/h_bbmd6.h>
|
||||||
$<$<BOOL:${BACDL_BIP6}>:src/bacnet/basic/bbmd6/vmac.c>
|
$<$<BOOL:${BACDL_BIP6}>:src/bacnet/basic/bbmd6/vmac.c>
|
||||||
$<$<BOOL:${BACDL_BIP6}>:src/bacnet/basic/bbmd6/vmac.h>
|
$<$<BOOL:${BACDL_BIP6}>:src/bacnet/basic/bbmd6/vmac.h>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:src/bacnet/datalink/bsc/bvlc-sc.c>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:src/bacnet/datalink/bsc/bvlc-sc.h>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:src/bacnet/datalink/bsc/bsc-socket.c>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:src/bacnet/datalink/bsc/bsc-socket.h>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:src/bacnet/datalink/bsc/bsc-util.h>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:src/bacnet/datalink/bsc/bsc-util.c>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:src/bacnet/datalink/bsc/bsc-hub-connector.h>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:src/bacnet/datalink/bsc/bsc-hub-connector.c>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:src/bacnet/datalink/bsc/bsc-hub-function.h>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:src/bacnet/datalink/bsc/bsc-hub-function.c>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:src/bacnet/datalink/bsc/bsc-node-switch.h>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:src/bacnet/datalink/bsc/bsc-node-switch.c>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:src/bacnet/datalink/bsc/bsc-node.h>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:src/bacnet/datalink/bsc/bsc-node.c>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:src/bacnet/datalink/bsc/bsc-datalink.h>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:src/bacnet/datalink/bsc/bsc-datalink.c>
|
||||||
src/bacnet/basic/binding/address.c
|
src/bacnet/basic/binding/address.c
|
||||||
src/bacnet/basic/binding/address.h
|
src/bacnet/basic/binding/address.h
|
||||||
src/bacnet/basic/npdu/h_npdu.c
|
src/bacnet/basic/npdu/h_npdu.c
|
||||||
@@ -340,6 +376,8 @@ add_library(${PROJECT_NAME}
|
|||||||
src/bacnet/basic/object/nc.h
|
src/bacnet/basic/object/nc.h
|
||||||
src/bacnet/basic/object/netport.c
|
src/bacnet/basic/object/netport.c
|
||||||
src/bacnet/basic/object/netport.h
|
src/bacnet/basic/object/netport.h
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:src/bacnet/basic/object/sc_netport.c>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:src/bacnet/basic/object/sc_netport.h>
|
||||||
src/bacnet/basic/object/objects.c
|
src/bacnet/basic/object/objects.c
|
||||||
src/bacnet/basic/object/objects.h
|
src/bacnet/basic/object/objects.h
|
||||||
src/bacnet/basic/object/osv.c
|
src/bacnet/basic/object/osv.c
|
||||||
@@ -623,6 +661,7 @@ target_compile_definitions(
|
|||||||
PUBLIC
|
PUBLIC
|
||||||
BACNET_PROTOCOL_REVISION=${BACNET_PROTOCOL_REVISION}
|
BACNET_PROTOCOL_REVISION=${BACNET_PROTOCOL_REVISION}
|
||||||
$<$<BOOL:${BACDL_BIP}>:BACDL_BIP>
|
$<$<BOOL:${BACDL_BIP}>:BACDL_BIP>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:BACDL_BSC>
|
||||||
$<$<BOOL:${BACDL_BIP6}>:BACDL_BIP6>
|
$<$<BOOL:${BACDL_BIP6}>:BACDL_BIP6>
|
||||||
$<$<BOOL:${BACDL_ARCNET}>:BACDL_ARCNET>
|
$<$<BOOL:${BACDL_ARCNET}>:BACDL_ARCNET>
|
||||||
$<$<BOOL:${BACDL_MSTP}>:BACDL_MSTP>
|
$<$<BOOL:${BACDL_MSTP}>:BACDL_MSTP>
|
||||||
@@ -633,7 +672,12 @@ target_compile_definitions(
|
|||||||
$<$<NOT:$<BOOL:${BUILD_SHARED_LIBS}>>:BACNET_STACK_STATIC_DEFINE>
|
$<$<NOT:$<BOOL:${BUILD_SHARED_LIBS}>>:BACNET_STACK_STATIC_DEFINE>
|
||||||
PRIVATE
|
PRIVATE
|
||||||
PRINT_ENABLED=1)
|
PRINT_ENABLED=1)
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC Threads::Threads)
|
|
||||||
|
if(BACDL_BSC)
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC Threads::Threads ${LIB_WEBSOCKETS_LIBRARIES} )
|
||||||
|
else()
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC Threads::Threads)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_library(
|
add_library(
|
||||||
${PROJECT_NAME}::${PROJECT_NAME}
|
${PROJECT_NAME}::${PROJECT_NAME}
|
||||||
@@ -652,6 +696,7 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
|||||||
message(STATUS "BACNET: building for linux")
|
message(STATUS "BACNET: building for linux")
|
||||||
set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/linux)
|
set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/linux)
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC m)
|
target_link_libraries(${PROJECT_NAME} PUBLIC m)
|
||||||
|
add_compile_definitions(BACNET_PORT=linux)
|
||||||
|
|
||||||
target_sources(${PROJECT_NAME} PRIVATE
|
target_sources(${PROJECT_NAME} PRIVATE
|
||||||
ports/linux/bacport.h
|
ports/linux/bacport.h
|
||||||
@@ -663,11 +708,16 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
|||||||
$<$<BOOL:${BACDL_MSTP}>:ports/linux/rs485.h>
|
$<$<BOOL:${BACDL_MSTP}>:ports/linux/rs485.h>
|
||||||
$<$<BOOL:${BACDL_MSTP}>:ports/linux/dlmstp.c>
|
$<$<BOOL:${BACDL_MSTP}>:ports/linux/dlmstp.c>
|
||||||
$<$<BOOL:${BACDL_ETHERNET}>:ports/linux/ethernet.c>
|
$<$<BOOL:${BACDL_ETHERNET}>:ports/linux/ethernet.c>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:ports/linux/bsc-event.c>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:ports/linux/websocket-cli.c>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:ports/linux/websocket-srv.c>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:ports/linux/websocket-global.c>
|
||||||
ports/linux/mstimer-init.c)
|
ports/linux/mstimer-init.c)
|
||||||
|
|
||||||
elseif(WIN32)
|
elseif(WIN32)
|
||||||
message(STATUS "BACNET: building for win32")
|
message(STATUS "BACNET: building for win32")
|
||||||
set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/win32)
|
set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/win32)
|
||||||
|
add_compile_definitions(BACNET_PORT=win32)
|
||||||
|
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC wsock32)
|
target_link_libraries(${PROJECT_NAME} PUBLIC wsock32)
|
||||||
|
|
||||||
@@ -685,7 +735,11 @@ elseif(WIN32)
|
|||||||
# ports/win32/dlmstp-mm.c
|
# ports/win32/dlmstp-mm.c
|
||||||
ports/win32/mstimer-init.c
|
ports/win32/mstimer-init.c
|
||||||
$<$<BOOL:${BACDL_MSTP}>:ports/win32/rs485.c>
|
$<$<BOOL:${BACDL_MSTP}>:ports/win32/rs485.c>
|
||||||
$<$<BOOL:${BACDL_MSTP}>:ports/win32/rs485.h>)
|
$<$<BOOL:${BACDL_MSTP}>:ports/win32/rs485.h>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:ports/win32/bsc-event.c>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:ports/win32/websocket-cli.c>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:ports/win32/websocket-srv.c>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:ports/win32/websocket-global.c>)
|
||||||
|
|
||||||
if(BACDL_ETHERNET)
|
if(BACDL_ETHERNET)
|
||||||
include(ExternalProject)
|
include(ExternalProject)
|
||||||
@@ -729,6 +783,10 @@ elseif(APPLE)
|
|||||||
$<$<BOOL:${BACDL_MSTP}>:ports/bsd/dlmstp.c>
|
$<$<BOOL:${BACDL_MSTP}>:ports/bsd/dlmstp.c>
|
||||||
ports/bsd/datetime-init.c
|
ports/bsd/datetime-init.c
|
||||||
ports/bsd/mstimer-init.c
|
ports/bsd/mstimer-init.c
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:ports/bsd/bsc-event.c>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:ports/bsd/websocket-cli.c>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:ports/bsd/websocket-srv.c>
|
||||||
|
$<$<BOOL:${BACDL_BSC}>:ports/bsd/websocket-global.c>
|
||||||
ports/bsd/stdbool.h)
|
ports/bsd/stdbool.h)
|
||||||
|
|
||||||
if(BACDL_ETHERNET)
|
if(BACDL_ETHERNET)
|
||||||
@@ -766,7 +824,8 @@ endif()
|
|||||||
|
|
||||||
target_include_directories(${PROJECT_NAME} PUBLIC
|
target_include_directories(${PROJECT_NAME} PUBLIC
|
||||||
$<BUILD_INTERFACE:${BACNET_PORT_DIRECTORY_PATH}>
|
$<BUILD_INTERFACE:${BACNET_PORT_DIRECTORY_PATH}>
|
||||||
)
|
$<$<BOOL:${BACDL_BSC}>:${OPEN_SSL_DIR}/include>
|
||||||
|
)
|
||||||
|
|
||||||
#
|
#
|
||||||
# examples
|
# examples
|
||||||
@@ -874,7 +933,7 @@ if(BACNET_STACK_BUILD_APPS)
|
|||||||
)
|
)
|
||||||
endif(BACNET_BUILD_BACDISCOVER_APP)
|
endif(BACNET_BUILD_BACDISCOVER_APP)
|
||||||
|
|
||||||
if(BACDL_BIP)
|
if(BACDL_BIP AND (NOT BACDL_BSC))
|
||||||
add_executable(readbdt apps/readbdt/main.c)
|
add_executable(readbdt apps/readbdt/main.c)
|
||||||
target_link_libraries(readbdt PRIVATE ${PROJECT_NAME})
|
target_link_libraries(readbdt PRIVATE ${PROJECT_NAME})
|
||||||
|
|
||||||
@@ -1017,6 +1076,10 @@ if(BACNET_STACK_BUILD_APPS)
|
|||||||
|
|
||||||
add_executable(writepropm apps/writepropm/main.c)
|
add_executable(writepropm apps/writepropm/main.c)
|
||||||
target_link_libraries(writepropm PRIVATE ${PROJECT_NAME})
|
target_link_libraries(writepropm PRIVATE ${PROJECT_NAME})
|
||||||
|
if(BACDL_BSC)
|
||||||
|
add_executable(sc-hub apps/sc-hub/main.c)
|
||||||
|
target_link_libraries(sc-hub PRIVATE ${PROJECT_NAME})
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -1077,6 +1140,7 @@ message(STATUS "BACNET: BACNET_PROTOCOL_REVISION:.......\"${BACNET_PROTOCOL_REVI
|
|||||||
message(STATUS "BACNET: Selected datalinks:")
|
message(STATUS "BACNET: Selected datalinks:")
|
||||||
message(STATUS "BACNET: BACDL_BIP6:.....................\"${BACDL_BIP6}\"")
|
message(STATUS "BACNET: BACDL_BIP6:.....................\"${BACDL_BIP6}\"")
|
||||||
message(STATUS "BACNET: BACDL_BIP:......................\"${BACDL_BIP}\"")
|
message(STATUS "BACNET: BACDL_BIP:......................\"${BACDL_BIP}\"")
|
||||||
|
message(STATUS "BACNET: BACDL_BSC:......................\"${BACDL_BSC}\"")
|
||||||
message(STATUS "BACNET: BACDL_ARCNET:...................\"${BACDL_ARCNET}\"")
|
message(STATUS "BACNET: BACDL_ARCNET:...................\"${BACDL_ARCNET}\"")
|
||||||
message(STATUS "BACNET: BACDL_MSTP:.....................\"${BACDL_MSTP}\"")
|
message(STATUS "BACNET: BACDL_MSTP:.....................\"${BACDL_MSTP}\"")
|
||||||
message(STATUS "BACNET: BACDL_ETHERNET:.................\"${BACDL_ETHERNET}\"")
|
message(STATUS "BACNET: BACDL_ETHERNET:.................\"${BACDL_ETHERNET}\"")
|
||||||
|
|||||||
@@ -56,6 +56,12 @@ bip-client:
|
|||||||
ethernet:
|
ethernet:
|
||||||
$(MAKE) BACDL=ethernet -s -C apps all
|
$(MAKE) BACDL=ethernet -s -C apps all
|
||||||
|
|
||||||
|
# note: requires additional libraries to be installed
|
||||||
|
# see .github/workflows/gcc.yml
|
||||||
|
.PHONY: bsc
|
||||||
|
bsc:
|
||||||
|
$(MAKE) BACDL=bsc -s -C apps all
|
||||||
|
|
||||||
.PHONY: apps
|
.PHONY: apps
|
||||||
apps:
|
apps:
|
||||||
$(MAKE) -s -C apps all
|
$(MAKE) -s -C apps all
|
||||||
@@ -200,6 +206,10 @@ server-client:
|
|||||||
server-discover:
|
server-discover:
|
||||||
$(MAKE) LEGACY=true -s -C apps $@
|
$(MAKE) LEGACY=true -s -C apps $@
|
||||||
|
|
||||||
|
.PHONY: sc-hub
|
||||||
|
sc-hub:
|
||||||
|
$(MAKE) BACDL=bsc -s -C apps $@
|
||||||
|
|
||||||
.PHONY: mstpcap
|
.PHONY: mstpcap
|
||||||
mstpcap:
|
mstpcap:
|
||||||
$(MAKE) -s -C apps $@
|
$(MAKE) -s -C apps $@
|
||||||
@@ -456,6 +466,7 @@ clean: ports-clean
|
|||||||
$(MAKE) -s -C apps/router-ipv6 clean
|
$(MAKE) -s -C apps/router-ipv6 clean
|
||||||
$(MAKE) -s -C apps/router-mstp clean
|
$(MAKE) -s -C apps/router-mstp clean
|
||||||
$(MAKE) -s -C apps/gateway clean
|
$(MAKE) -s -C apps/gateway clean
|
||||||
|
$(MAKE) -s -C apps/sc-hub clean
|
||||||
$(MAKE) -s -C apps/fuzz-afl clean
|
$(MAKE) -s -C apps/fuzz-afl clean
|
||||||
$(MAKE) -s -C apps/fuzz-libfuzzer clean
|
$(MAKE) -s -C apps/fuzz-libfuzzer clean
|
||||||
$(MAKE) -s -C ports/lwip clean
|
$(MAKE) -s -C ports/lwip clean
|
||||||
|
|||||||
+16
-1
@@ -35,6 +35,10 @@ endif
|
|||||||
ifeq (${BACDL},bip6)
|
ifeq (${BACDL},bip6)
|
||||||
BACDL_DEFINE=-DBACDL_BIP6=1
|
BACDL_DEFINE=-DBACDL_BIP6=1
|
||||||
endif
|
endif
|
||||||
|
ifeq (${BACDL},bsc)
|
||||||
|
BACDL_DEFINE=-DBACDL_BSC=1
|
||||||
|
BACNET_DEFINE=-DBACFILE=1
|
||||||
|
endif
|
||||||
ifeq (${BACDL},none)
|
ifeq (${BACDL},none)
|
||||||
BACDL_DEFINE=-DBACDL_NONE=1
|
BACDL_DEFINE=-DBACDL_NONE=1
|
||||||
endif
|
endif
|
||||||
@@ -108,11 +112,19 @@ ifeq (${BACNET_PORT},linux)
|
|||||||
PFLAGS = -pthread
|
PFLAGS = -pthread
|
||||||
TARGET_EXT =
|
TARGET_EXT =
|
||||||
SYSTEM_LIB=-lc,-lgcc,-lrt,-lm
|
SYSTEM_LIB=-lc,-lgcc,-lrt,-lm
|
||||||
|
ifeq (${BACDL},bsc)
|
||||||
|
# note: install libwebsockets libssl libcrypto and lcap to build for BACnet/SC
|
||||||
|
SYSTEM_LIB += -lwebsockets -lssl -lcrypto -lcap
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
ifeq (${BACNET_PORT},bsd)
|
ifeq (${BACNET_PORT},bsd)
|
||||||
PFLAGS = -pthread
|
PFLAGS = -pthread
|
||||||
TARGET_EXT =
|
TARGET_EXT =
|
||||||
SYSTEM_LIB=-lc,-lm
|
SYSTEM_LIB=-lc,-lm
|
||||||
|
ifeq (${BACDL},bsc)
|
||||||
|
# note: install libwebsockets libssl and libcrypto to build for BACnet/SC
|
||||||
|
SYSTEM_LIB += -lwebsockets -lssl -lcrypto
|
||||||
|
endif
|
||||||
CSTANDARD = -std=c99
|
CSTANDARD = -std=c99
|
||||||
endif
|
endif
|
||||||
ifeq (${BACNET_PORT},win32)
|
ifeq (${BACNET_PORT},win32)
|
||||||
@@ -174,7 +186,6 @@ endif
|
|||||||
|
|
||||||
BACNET_DEFINES += -DPRINT_ENABLED=1
|
BACNET_DEFINES += -DPRINT_ENABLED=1
|
||||||
BACNET_DEFINES += -DBACAPP_ALL
|
BACNET_DEFINES += -DBACAPP_ALL
|
||||||
BACNET_DEFINES += -DBACFILE
|
|
||||||
BACNET_DEFINES += -DINTRINSIC_REPORTING
|
BACNET_DEFINES += -DINTRINSIC_REPORTING
|
||||||
BACNET_DEFINES += -DBACNET_TIME_MASTER
|
BACNET_DEFINES += -DBACNET_TIME_MASTER
|
||||||
BACNET_DEFINES += -DBACNET_PROPERTY_LISTS=1
|
BACNET_DEFINES += -DBACNET_PROPERTY_LISTS=1
|
||||||
@@ -462,3 +473,7 @@ writepropm: $(BACNET_LIB_TARGET)
|
|||||||
.PHONY: writegroup
|
.PHONY: writegroup
|
||||||
writegroup: $(BACNET_LIB_TARGET)
|
writegroup: $(BACNET_LIB_TARGET)
|
||||||
$(MAKE) -B -C $@
|
$(MAKE) -B -C $@
|
||||||
|
|
||||||
|
.PHONY: sc-hub
|
||||||
|
sc-hub: $(BACNET_LIB_TARGET)
|
||||||
|
$(MAKE) -B -C $@
|
||||||
|
|||||||
+26
-1
@@ -6,6 +6,7 @@ BACNET_LIB_DIR ?= $(realpath .)
|
|||||||
BACNET_LIB_TARGET ?= $(BACNET_LIB_DIR)/lib$(BACNET_LIB_NAME).a
|
BACNET_LIB_TARGET ?= $(BACNET_LIB_DIR)/lib$(BACNET_LIB_NAME).a
|
||||||
BACNET_SRC_DIR ?= $(realpath ../../src)
|
BACNET_SRC_DIR ?= $(realpath ../../src)
|
||||||
BACNET_PORT_DIR ?= $(realpath ../../ports/linux)
|
BACNET_PORT_DIR ?= $(realpath ../../ports/linux)
|
||||||
|
BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object
|
||||||
BACNET_DEFINES ?=
|
BACNET_DEFINES ?=
|
||||||
|
|
||||||
#build for release (default) or debug
|
#build for release (default) or debug
|
||||||
@@ -61,13 +62,34 @@ PORT_BIP6_SRC = \
|
|||||||
$(BACNET_SRC_DIR)/bacnet/basic/bbmd6/vmac.c \
|
$(BACNET_SRC_DIR)/bacnet/basic/bbmd6/vmac.c \
|
||||||
$(BACNET_SRC_DIR)/bacnet/datalink/bvlc6.c
|
$(BACNET_SRC_DIR)/bacnet/datalink/bvlc6.c
|
||||||
|
|
||||||
|
PORT_BSC_SRC = \
|
||||||
|
$(BACNET_PORT_DIR)/bip-init.c \
|
||||||
|
$(BACNET_PORT_DIR)/websocket-global.c \
|
||||||
|
$(BACNET_PORT_DIR)/websocket-srv.c \
|
||||||
|
$(BACNET_PORT_DIR)/websocket-cli.c \
|
||||||
|
$(BACNET_PORT_DIR)/bsc-event.c \
|
||||||
|
$(BACNET_SRC_DIR)/bacnet/datalink/bvlc.c \
|
||||||
|
$(BACNET_SRC_DIR)/bacnet/basic/bbmd/h_bbmd.c \
|
||||||
|
$(BACNET_SRC_DIR)/bacnet/datalink/bsc/bsc-datalink.c \
|
||||||
|
$(BACNET_SRC_DIR)/bacnet/datalink/bsc/bsc-hub-connector.c \
|
||||||
|
$(BACNET_SRC_DIR)/bacnet/datalink/bsc/bsc-hub-function.c \
|
||||||
|
$(BACNET_SRC_DIR)/bacnet/datalink/bsc/bsc-node-switch.c \
|
||||||
|
$(BACNET_SRC_DIR)/bacnet/datalink/bsc/bsc-node.c \
|
||||||
|
$(BACNET_SRC_DIR)/bacnet/datalink/bsc/bsc-socket.c \
|
||||||
|
$(BACNET_SRC_DIR)/bacnet/datalink/bsc/bsc-util.c \
|
||||||
|
$(BACNET_SRC_DIR)/bacnet/datalink/bsc/bvlc-sc.c \
|
||||||
|
$(BACNET_OBJECT_DIR)/sc_netport.c \
|
||||||
|
$(BACNET_OBJECT_DIR)/bacfile.c \
|
||||||
|
$(BACNET_SRC_DIR)/bacnet/secure_connect.c
|
||||||
|
|
||||||
PORT_ALL_SRC = \
|
PORT_ALL_SRC = \
|
||||||
$(BACNET_SRC_DIR)/bacnet/datalink/datalink.c \
|
$(BACNET_SRC_DIR)/bacnet/datalink/datalink.c \
|
||||||
$(PORT_ARCNET_SRC) \
|
$(PORT_ARCNET_SRC) \
|
||||||
$(PORT_MSTP_SRC) \
|
$(PORT_MSTP_SRC) \
|
||||||
$(PORT_ETHERNET_SRC) \
|
$(PORT_ETHERNET_SRC) \
|
||||||
$(PORT_BIP_SRC) \
|
$(PORT_BIP_SRC) \
|
||||||
$(PORT_BIP6_SRC)
|
$(PORT_BIP6_SRC) \
|
||||||
|
$(PORT_BSC_SRC)
|
||||||
|
|
||||||
PORT_NONE_SRC = \
|
PORT_NONE_SRC = \
|
||||||
$(BACNET_SRC_DIR)/bacnet/datalink/datalink.c
|
$(BACNET_SRC_DIR)/bacnet/datalink/datalink.c
|
||||||
@@ -90,6 +112,9 @@ endif
|
|||||||
ifeq (${BACDL_DEFINE},-DBACDL_NONE=1)
|
ifeq (${BACDL_DEFINE},-DBACDL_NONE=1)
|
||||||
BACNET_PORT_SRC = ${PORT_NONE_SRC}
|
BACNET_PORT_SRC = ${PORT_NONE_SRC}
|
||||||
endif
|
endif
|
||||||
|
ifeq (${BACDL_DEFINE},-DBACDL_BSC=1)
|
||||||
|
BACNET_PORT_SRC = ${PORT_BSC_SRC} ${APPS_ENVIRONMENT_SRC}
|
||||||
|
endif
|
||||||
ifeq (${BACDL_DEFINE},-DBACDL_ALL=1)
|
ifeq (${BACDL_DEFINE},-DBACDL_ALL=1)
|
||||||
BACNET_PORT_SRC = ${PORT_ALL_SRC}
|
BACNET_PORT_SRC = ${PORT_ALL_SRC}
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
#Makefile to build BACnet Application using GCC compiler
|
||||||
|
|
||||||
|
# Executable file name
|
||||||
|
TARGET = bacschub
|
||||||
|
# BACnet objects that are used with this app
|
||||||
|
BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object
|
||||||
|
SRC = main.c \
|
||||||
|
$(BACNET_OBJECT_DIR)/client/device-client.c \
|
||||||
|
$(BACNET_OBJECT_DIR)/netport.c \
|
||||||
|
$(BACNET_SRC_DIR)/bacnet/secure_connect.c \
|
||||||
|
$(BACNET_OBJECT_DIR)/sc_netport.c
|
||||||
|
|
||||||
|
CFLAGS += -DBACNET_SECURE_CONNECT_ROUTING_TABLE=1
|
||||||
|
CFLAGS += -DBSC_CONF_HUB_FUNCTIONS_NUM=1
|
||||||
|
CFLAGS += -DBSC_CONF_HUB_CONNECTORS_NUM=1
|
||||||
|
|
||||||
|
# TARGET_EXT is defined in apps/Makefile as .exe or nothing
|
||||||
|
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||||
|
|
||||||
|
OBJS += ${SRC:.c=.o}
|
||||||
|
|
||||||
|
all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN}
|
||||||
|
|
||||||
|
${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET}
|
||||||
|
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||||
|
size $@
|
||||||
|
cp $@ ../../bin
|
||||||
|
|
||||||
|
${BACNET_LIB_TARGET}:
|
||||||
|
( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) -s )
|
||||||
|
|
||||||
|
-.c.o:
|
||||||
|
${CC} -c ${CFLAGS} $*.c -o $@
|
||||||
|
|
||||||
|
.PHONY: depend
|
||||||
|
depend:
|
||||||
|
rm -f .depend
|
||||||
|
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map ${BACNET_LIB_TARGET}
|
||||||
|
|
||||||
|
.PHONY: include
|
||||||
|
include: .depend
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# BACnet/SC Hub
|
||||||
|
|
||||||
|
Test BACnet/SC using the following steps:
|
||||||
|
|
||||||
|
* Build apps for bsc datalink:
|
||||||
|
|
||||||
|
make bsc
|
||||||
|
|
||||||
|
* Run hub app:
|
||||||
|
|
||||||
|
cd bin
|
||||||
|
./bsc-server.sh
|
||||||
|
./bacschub 1 Hubster
|
||||||
|
|
||||||
|
* Run server app:
|
||||||
|
|
||||||
|
cd bin
|
||||||
|
./bsc-client.sh
|
||||||
|
./bacserv 123 Francine
|
||||||
|
|
||||||
|
* Run any client:
|
||||||
|
|
||||||
|
cd bin
|
||||||
|
./bsc-client.sh
|
||||||
|
./bacwi
|
||||||
|
./bacepics 1
|
||||||
|
./bacepics 123
|
||||||
@@ -0,0 +1,240 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Sample BACnet/SC hub.
|
||||||
|
* @author Mikhail Antropov <michail.antropov@dsr-corporation.com>
|
||||||
|
* @date December 2022
|
||||||
|
* @copyright SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "bacnet/config.h"
|
||||||
|
#include "bacnet/apdu.h"
|
||||||
|
#include "bacnet/iam.h"
|
||||||
|
#include "bacnet/version.h"
|
||||||
|
#include "bacnet/basic/services.h"
|
||||||
|
#include "bacnet/datalink/dlenv.h"
|
||||||
|
#include "bacnet/basic/sys/filename.h"
|
||||||
|
#include "bacnet/basic/sys/mstimer.h"
|
||||||
|
#include "bacnet/datalink/datalink.h"
|
||||||
|
/* include the device object */
|
||||||
|
#include "bacnet/basic/object/device.h"
|
||||||
|
#if defined(BAC_UCI)
|
||||||
|
#include "bacnet/basic/ucix/ucix.h"
|
||||||
|
#endif /* defined(BAC_UCI) */
|
||||||
|
#include "bacnet/datalink/bsc/bsc-datalink.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-event.h"
|
||||||
|
|
||||||
|
/* (Doxygen note: The next two lines pull all the following Javadoc
|
||||||
|
* into the SCServerDemo module.) */
|
||||||
|
/** @addtogroup SCServerDemo */
|
||||||
|
/*@{*/
|
||||||
|
|
||||||
|
#ifndef BACDL_BSC
|
||||||
|
#error "BACDL_BSC must be defined"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* current version of the BACnet stack */
|
||||||
|
static const char *BACnet_Version = BACNET_VERSION_TEXT;
|
||||||
|
|
||||||
|
/** Buffer used for receiving */
|
||||||
|
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
|
||||||
|
|
||||||
|
/** Initialize the handlers we will utilize.
|
||||||
|
* @see Device_Init, apdu_set_unconfirmed_handler, apdu_set_confirmed_handler
|
||||||
|
*/
|
||||||
|
static void Init_Service_Handlers(void)
|
||||||
|
{
|
||||||
|
Device_Init(NULL);
|
||||||
|
|
||||||
|
/* we need to handle who-is to support dynamic device binding */
|
||||||
|
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is);
|
||||||
|
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, handler_who_has);
|
||||||
|
/* set the handler for all the services we don't implement */
|
||||||
|
/* It is required to send the proper reject message... */
|
||||||
|
apdu_set_unrecognized_service_handler_handler(handler_unrecognized_service);
|
||||||
|
/* Set the handlers for any confirmed services that we support. */
|
||||||
|
/* We must implement read property - it's required! */
|
||||||
|
apdu_set_confirmed_handler(
|
||||||
|
SERVICE_CONFIRMED_READ_PROPERTY, handler_read_property);
|
||||||
|
apdu_set_confirmed_handler(
|
||||||
|
SERVICE_CONFIRMED_READ_PROP_MULTIPLE, handler_read_property_multiple);
|
||||||
|
apdu_set_confirmed_handler(
|
||||||
|
SERVICE_CONFIRMED_WRITE_PROPERTY, handler_write_property);
|
||||||
|
apdu_set_confirmed_handler(
|
||||||
|
SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE, handler_write_property_multiple);
|
||||||
|
apdu_set_confirmed_handler(
|
||||||
|
SERVICE_CONFIRMED_REINITIALIZE_DEVICE, handler_reinitialize_device);
|
||||||
|
apdu_set_unconfirmed_handler(
|
||||||
|
SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, handler_timesync_utc);
|
||||||
|
apdu_set_unconfirmed_handler(
|
||||||
|
SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, handler_timesync);
|
||||||
|
#if defined(BACNET_TIME_MASTER)
|
||||||
|
handler_timesync_init();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_usage(const char *filename)
|
||||||
|
{
|
||||||
|
printf("Usage: %s [device-instance [device-name]]\n", filename);
|
||||||
|
printf(" [--version][--help]\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_help(const char *filename)
|
||||||
|
{
|
||||||
|
printf(
|
||||||
|
"Simulate a BACnet/SC HUB device\n"
|
||||||
|
"device-instance: BACnet Device Object Instance number that you are\n"
|
||||||
|
"trying simulate.\n"
|
||||||
|
"device-name: The Device name in ASCII for this device.\n"
|
||||||
|
"\n");
|
||||||
|
printf("Other parameters are passing over environment variables:\n"
|
||||||
|
"- BACNET_SC_ISSUER_1_CERTIFICATE_FILE: Filename of CA certificate\n"
|
||||||
|
"- BACNET_SC_OPERATIONAL_CERTIFICATE_FILE: Filename of device "
|
||||||
|
"certificate\n"
|
||||||
|
"- BACNET_SC_OPERATIONAL_CERTIFICATE_PRIVATE_KEY_FILE: Filename of "
|
||||||
|
"device certificate key\n"
|
||||||
|
"- BACNET_SC_HUB_FUNCTION_BINDING: Local port or pair \"interface "
|
||||||
|
"name:port number\"\n"
|
||||||
|
"For additional information see file bin/bsc-server.sh\n");
|
||||||
|
printf(
|
||||||
|
"\nExample:\n"
|
||||||
|
"To simulate Device 111, use following command:\n"
|
||||||
|
"%s 111\n",
|
||||||
|
filename);
|
||||||
|
printf(
|
||||||
|
"To simulate Device 111 named NoFred, use following command:\n"
|
||||||
|
"%s 111 NoFred\n",
|
||||||
|
filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Main function of server demo.
|
||||||
|
*
|
||||||
|
* @see Device_Set_Object_Instance_Number, dlenv_init, Send_I_Am,
|
||||||
|
* datalink_receive, npdu_handler,
|
||||||
|
* dcc_timer_seconds, datalink_maintenance_timer,
|
||||||
|
* Load_Control_State_Machine_Handler, handler_cov_task,
|
||||||
|
* tsm_timer_milliseconds
|
||||||
|
*
|
||||||
|
* @param argc [in] Arg count.
|
||||||
|
* @param argv [in] Takes one argument: the Device Instance #.
|
||||||
|
* @return 0 on success.
|
||||||
|
*/
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
#if defined(BAC_UCI)
|
||||||
|
int uciId = 0;
|
||||||
|
struct uci_context *ctx;
|
||||||
|
#endif
|
||||||
|
#if defined(BACNET_TIME_MASTER)
|
||||||
|
BACNET_DATE_TIME bdatetime = { 0 };
|
||||||
|
#endif
|
||||||
|
BACNET_ADDRESS src = { 0 }; /* address where message came from */
|
||||||
|
int argi = 0;
|
||||||
|
const char *filename = NULL;
|
||||||
|
|
||||||
|
BACNET_CHARACTER_STRING DeviceName;
|
||||||
|
uint16_t pdu_len = 0;
|
||||||
|
unsigned delay_milliseconds = 1;
|
||||||
|
unsigned elapsed_seconds = 0;
|
||||||
|
struct mstimer datalink_timer = { 0 };
|
||||||
|
|
||||||
|
filename = filename_remove_path(argv[0]);
|
||||||
|
argi = 0;
|
||||||
|
if ((argc >= 2) && (strcmp(argv[1], "--help") == 0)) {
|
||||||
|
print_usage(filename);
|
||||||
|
print_help(filename);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ((argc >= 2) && (strcmp(argv[1], "--version") == 0)) {
|
||||||
|
printf("%s %s\n", filename, BACNET_VERSION_TEXT);
|
||||||
|
printf("Copyright (C) 2022 by Steve Karg and others.\n"
|
||||||
|
"This is free software; see the source for copying "
|
||||||
|
"conditions.\n"
|
||||||
|
"There is NO warranty; not even for MERCHANTABILITY or\n"
|
||||||
|
"FITNESS FOR A PARTICULAR PURPOSE.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(BAC_UCI)
|
||||||
|
ctx = ucix_init("bacnet_dev");
|
||||||
|
if (!ctx) {
|
||||||
|
fprintf(stderr, "Failed to load config file bacnet_dev\n");
|
||||||
|
}
|
||||||
|
uciId = ucix_get_option_int(ctx, "bacnet_dev", "0", "Id", 0);
|
||||||
|
if (uciId != 0) {
|
||||||
|
Device_Set_Object_Instance_Number(uciId);
|
||||||
|
} else {
|
||||||
|
#endif /* defined(BAC_UCI) */
|
||||||
|
/* allow the device ID to be set */
|
||||||
|
if (++argi < argc) {
|
||||||
|
Device_Set_Object_Instance_Number(strtol(argv[argi], NULL, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(BAC_UCI)
|
||||||
|
}
|
||||||
|
ucix_cleanup(ctx);
|
||||||
|
#endif /* defined(BAC_UCI) */
|
||||||
|
|
||||||
|
printf(
|
||||||
|
"BACnet SC Hub Demo\n"
|
||||||
|
"BACnet Stack Version %s\n"
|
||||||
|
"BACnet Device ID: %u\n"
|
||||||
|
"Max APDU: %d\n",
|
||||||
|
BACnet_Version, Device_Object_Instance_Number(), MAX_APDU);
|
||||||
|
/* load any static address bindings to show up
|
||||||
|
in our device bindings list */
|
||||||
|
address_init();
|
||||||
|
Init_Service_Handlers();
|
||||||
|
#if defined(BAC_UCI)
|
||||||
|
const char *uciname;
|
||||||
|
ctx = ucix_init("bacnet_dev");
|
||||||
|
if (!ctx) {
|
||||||
|
fprintf(stderr, "Failed to load config file bacnet_dev\n");
|
||||||
|
}
|
||||||
|
uciname = ucix_get_option(ctx, "bacnet_dev", "0", "Name");
|
||||||
|
if (uciname != 0) {
|
||||||
|
Device_Object_Name_ANSI_Init(uciname);
|
||||||
|
} else {
|
||||||
|
#endif /* defined(BAC_UCI) */
|
||||||
|
if (++argi < argc) {
|
||||||
|
Device_Object_Name_ANSI_Init(argv[argi]);
|
||||||
|
}
|
||||||
|
#if defined(BAC_UCI)
|
||||||
|
}
|
||||||
|
ucix_cleanup(ctx);
|
||||||
|
#endif /* defined(BAC_UCI) */
|
||||||
|
if (Device_Object_Name(Device_Object_Instance_Number(), &DeviceName)) {
|
||||||
|
printf("BACnet Device Name: %s\n", DeviceName.value);
|
||||||
|
}
|
||||||
|
dlenv_init();
|
||||||
|
atexit(datalink_cleanup);
|
||||||
|
mstimer_set(&datalink_timer, 1000);
|
||||||
|
/* loop forever */
|
||||||
|
for (;;) {
|
||||||
|
/* input */
|
||||||
|
pdu_len =
|
||||||
|
datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, delay_milliseconds);
|
||||||
|
/* process */
|
||||||
|
if (pdu_len) {
|
||||||
|
npdu_handler(&src, &Rx_Buf[0], pdu_len);
|
||||||
|
}
|
||||||
|
if (mstimer_expired(&datalink_timer)) {
|
||||||
|
elapsed_seconds = mstimer_interval(&datalink_timer) / 1000;
|
||||||
|
mstimer_reset(&datalink_timer);
|
||||||
|
datalink_maintenance_timer(elapsed_seconds);
|
||||||
|
#if defined(BACNET_TIME_MASTER)
|
||||||
|
Device_getCurrentDateTime(&bdatetime);
|
||||||
|
handler_timesync_task(&bdatetime);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @} */
|
||||||
|
|
||||||
|
/* End group SCServerDemo */
|
||||||
Executable
+39
@@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo "Example of parameters for BACnet/SC clients"
|
||||||
|
|
||||||
|
BACNET_SC_PRIMARY_HUB_URI="wss://127.0.0.1:50050"
|
||||||
|
export BACNET_SC_PRIMARY_HUB_URI
|
||||||
|
|
||||||
|
BACNET_SC_FAILOVER_HUB_URI="wss://127.0.0.1:5555"
|
||||||
|
export BACNET_SC_FAILOVER_HUB_URI
|
||||||
|
|
||||||
|
BACNET_SC_ISSUER_1_CERTIFICATE_FILE="certs/ca_cert.pem"
|
||||||
|
export BACNET_SC_ISSUER_1_CERTIFICATE_FILE
|
||||||
|
|
||||||
|
# Second CA certificate is not used yet
|
||||||
|
#BACNET_SC_ISSUER_2_CERTIFICATE_FILE="certs/ca_cert2.pem"
|
||||||
|
#export BACNET_SC_ISSUER_2_CERTIFICATE_FILE
|
||||||
|
|
||||||
|
BACNET_SC_OPERATIONAL_CERTIFICATE_FILE="certs/client_cert.pem"
|
||||||
|
export BACNET_SC_OPERATIONAL_CERTIFICATE_FILE
|
||||||
|
|
||||||
|
BACNET_SC_OPERATIONAL_CERTIFICATE_PRIVATE_KEY_FILE="certs/client_key.pem"
|
||||||
|
export BACNET_SC_OPERATIONAL_CERTIFICATE_PRIVATE_KEY_FILE
|
||||||
|
|
||||||
|
# Need if the device allows the direct connect.
|
||||||
|
# Possible values: or port number, like "5000" or pair interface name and
|
||||||
|
# port number, like "eth0:50050"
|
||||||
|
#BACNET_SC_DIRECT_CONNECT_BINDING="eth0:1234"
|
||||||
|
#export BACNET_SC_DIRECT_CONNECT_BINDING
|
||||||
|
|
||||||
|
# Set if the device supports the direct connect initiate
|
||||||
|
# Value: if allow then "1", "y", "Y", otherwise forbidden
|
||||||
|
#BACNET_SC_DIRECT_CONNECT_INITIATE="y"
|
||||||
|
#export BACNET_SC_DIRECT_CONNECT_INITIATE
|
||||||
|
|
||||||
|
# List of direct connect accept URLs separated by a space character
|
||||||
|
#BACNET_SC_DIRECT_CONNECT_ACCEPT_URLS="wss://192.0.0.1:40000 wss://192.0.0.2:6666"
|
||||||
|
#export BACNET_SC_DIRECT_CONNECT_ACCEPT_URLS
|
||||||
|
|
||||||
|
echo "Launching new shell using the BACnet/SC client environment..."
|
||||||
|
/bin/bash
|
||||||
Executable
+44
@@ -0,0 +1,44 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo "Example of parameters for BACnet/SC server"
|
||||||
|
|
||||||
|
BACNET_SC_PRIMARY_HUB_URI="wss://127.0.0.1:50050"
|
||||||
|
export BACNET_SC_PRIMARY_HUB_URI
|
||||||
|
BACNET_SC_FAILOVER_HUB_URI="wss://127.0.0.1:50050"
|
||||||
|
export BACNET_SC_FAILOVER_HUB_URI
|
||||||
|
|
||||||
|
BACNET_SC_ISSUER_1_CERTIFICATE_FILE="certs/ca_cert.pem"
|
||||||
|
export BACNET_SC_ISSUER_1_CERTIFICATE_FILE
|
||||||
|
|
||||||
|
# Second CA certificate is not used yet
|
||||||
|
#BACNET_SC_ISSUER_2_CERTIFICATE_FILE="certs/ca_cert2.pem"
|
||||||
|
#export BACNET_SC_ISSUER_2_CERTIFICATE_FILE
|
||||||
|
|
||||||
|
BACNET_SC_OPERATIONAL_CERTIFICATE_FILE="certs/server_cert.pem"
|
||||||
|
export BACNET_SC_OPERATIONAL_CERTIFICATE_FILE
|
||||||
|
|
||||||
|
BACNET_SC_OPERATIONAL_CERTIFICATE_PRIVATE_KEY_FILE="certs/server_key.pem"
|
||||||
|
export BACNET_SC_OPERATIONAL_CERTIFICATE_PRIVATE_KEY_FILE
|
||||||
|
|
||||||
|
# Need if the device is HUB.
|
||||||
|
# Possible values: or port number, like "9999" or pair interface name and
|
||||||
|
# port number, like "eth0:50050"
|
||||||
|
BACNET_SC_HUB_FUNCTION_BINDING="50050"
|
||||||
|
export BACNET_SC_HUB_FUNCTION_BINDING
|
||||||
|
|
||||||
|
# Need if the device allows the direct connect.
|
||||||
|
# Possible values: or port number, like "5000" or pair interface name and
|
||||||
|
# port number, like "eth0:50050"
|
||||||
|
#BACNET_SC_DIRECT_CONNECT_BINDING="eth0:1234"
|
||||||
|
#export BACNET_SC_DIRECT_CONNECT_BINDING
|
||||||
|
|
||||||
|
# Set if the device supports the direct connect initiate
|
||||||
|
# Value: if allow then "1", "y", "Y", otherwise forbidden
|
||||||
|
#BACNET_SC_DIRECT_CONNECT_INITIATE="y"
|
||||||
|
#export BACNET_SC_DIRECT_CONNECT_INITIATE
|
||||||
|
|
||||||
|
# List of direct connect accept URLs separated by a space character
|
||||||
|
#BACNET_SC_DIRECT_CONNECT_ACCEPT_URLS="wss://192.0.0.1:40000 wss://192.0.0.2:6666"
|
||||||
|
#export BACNET_SC_DIRECT_CONNECT_ACCEPT_URLS
|
||||||
|
|
||||||
|
echo "Launching new shell using the BACnet/SC server environment..."
|
||||||
|
/bin/bash
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDHDCCAgQCCQDCD53YZJJ7ljANBgkqhkiG9w0BAQsFADBPMRswGQYDVQQKExJs
|
||||||
|
aWJ3ZWJzb2NrZXRzLXRlc3QxEjAQBgNVBAcTCVhpYW9iaXRhbjEPMA0GA1UECBMG
|
||||||
|
VGFpcGVpMQswCQYDVQQGEwJUVzAgFw0yMjA3MDYxMTI0MjBaGA8yMDUwMDcxOTEx
|
||||||
|
MjQyMFowTzEbMBkGA1UEChMSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQHEwlY
|
||||||
|
aWFvYml0YW4xDzANBgNVBAgTBlRhaXBlaTELMAkGA1UEBhMCVFcwggEiMA0GCSqG
|
||||||
|
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDV/Oz5VsX3V4R8vaANyPIMaFsygMeWpKan
|
||||||
|
HMT4kfigUYUHUYeLkbPewZAycCfYsCdBJmsXCXCsfiELpuYnepRetrZ29wTj/b+m
|
||||||
|
2TjUb2G4Q67CKSHNlh6CQqFvEWnBX+8fYehQT3QwJXN9hVBuPxD+xA+bI6Z28W0k
|
||||||
|
A0ZL9FfWCT6fODxM7egkSSmKeDgTUsn5wd8R8kQhk4yI2KPUys+nYxzowjNOi8uh
|
||||||
|
DXzr+Fxg/ps68ViZjGFgXSFHw5ZIvRQTGI2h0T9qmHP0BVFybJu9s6XDYuoM8EJs
|
||||||
|
WSs+4vOacaTEVYAQF5HxALfCtaOePjSp+ffE+U0CxYF3NH93YKZxAgMBAAEwDQYJ
|
||||||
|
KoZIhvcNAQELBQADggEBAImJvqnpPdSuFOZT6vtH1pJCEvJ9bSx1CAv6F4FDowKw
|
||||||
|
CqKSlYEjrILdlB0921OT0vah3lUv/kGNLvXUqTiBakwfRG09aIEnShmyo0hceOh3
|
||||||
|
O1KKYvJ2jJGk6PlRxeSg7d5Mi7XgnAdeaxwhuvZZmaIzbhAWP8qgI06P2RBS5BJv
|
||||||
|
rDD3Dhw8N8GwBD1RY2yKryFQ+4U21Ersw/38cY8UAFTkg3rWr4DWx6tnIfdrN1II
|
||||||
|
pRqSxHQW4kZag1uZdFcSiaYT+ebLWVXAzbfJLOV8EFF3RDC/A3U3VcORDBDWDhmh
|
||||||
|
8r4T+6FggYL2ejMmfwbewzI+Hm4SidgOxLf1Fx1d2v4=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEogIBAAKCAQEA1fzs+VbF91eEfL2gDcjyDGhbMoDHlqSmpxzE+JH4oFGFB1GH
|
||||||
|
i5Gz3sGQMnAn2LAnQSZrFwlwrH4hC6bmJ3qUXra2dvcE4/2/ptk41G9huEOuwikh
|
||||||
|
zZYegkKhbxFpwV/vH2HoUE90MCVzfYVQbj8Q/sQPmyOmdvFtJANGS/RX1gk+nzg8
|
||||||
|
TO3oJEkping4E1LJ+cHfEfJEIZOMiNij1MrPp2Mc6MIzTovLoQ186/hcYP6bOvFY
|
||||||
|
mYxhYF0hR8OWSL0UExiNodE/aphz9AVRcmybvbOlw2LqDPBCbFkrPuLzmnGkxFWA
|
||||||
|
EBeR8QC3wrWjnj40qfn3xPlNAsWBdzR/d2CmcQIDAQABAoIBACXIxz8vpz0JYwdz
|
||||||
|
pDNK7BJsys2cF6Ht6M9RMSaC/9evDUJBjyGB1LTckM2XKDIGyKekVPx4WkDa9JLO
|
||||||
|
IY2PA2vF72oKK/7l61VWvcYk9KhIqy71fFaEz1ZI1aB6/qV6fwqXiyHDJczqj/31
|
||||||
|
8EHHOQUDMY4/OUF/V7ooKd13g5rzLAPGNtC7n+VkjA11x8Ol2WbH3dj1xOG/fPRW
|
||||||
|
rciWFayJr3qVlZ+By1fDCY81MsUVAeUW/T3k9BnKg6hqa3JG67NA6BrczNKSKGy7
|
||||||
|
ppVVAeaGDIs7bsCzRpEBCgNhRBZE/AyewMVnBJbKArnryii8/ZbVQUMLVH2ph4Fi
|
||||||
|
k+0VfVECgYEA/ZE95qKFmqQeO6mvB7kVkpVKSXGu/Y5kFcpLWOc5EVHL6FmfzbqB
|
||||||
|
Axoq9HhfLy9a7NzsaCw0f4VyZrsOasgB1CBZP602CGi8hbHajJMwJg7B9d8Rqbg5
|
||||||
|
vakEudpB1h9jm9NJ5acR7/vcdVpEgpn2nP84Rn/N6reuwRVa8/u+YecCgYEA2Ap6
|
||||||
|
O6xM22pNDd3p5yPe868fyf5V5/2Kov4vF8wfisaEcfb/WbJRgwc6/kUUk1lJnxnG
|
||||||
|
PK98Hmjid5Y39dzilXgtC66XVAu0OJ04lqilsM4BSCdklimphuFszJ3POt6U7hgB
|
||||||
|
1qsKMKautj3/8v2Swcq6FIx6l6Fo7q+sptVrWecCgYB+6c1TvJCmf/Jp5lomwWqc
|
||||||
|
vYA7Fl2Bp11M0r23t7OGiaxHlWQ4slqUVOqfBgiOM2ONHl5tHYMBOKez53gl1gmk
|
||||||
|
RLSm/GkI+HMzb3t11M3KnqRSDd5nVkAAP7KP20AMjhVrDuvzuBVwSl1jl1SnEayv
|
||||||
|
k8Z08s775gffuHKXn68AjQKBgFXsLNso1tR5PbYjKVD9ikGex+TdW6tNwmK6913e
|
||||||
|
mDjoZWqyErLI4fRb3tGcBefonghCBv7ByHqLum5Xd2A4foF172xy/sq1cPMHTKdW
|
||||||
|
4bck54ub5zx1y2/SniPvK6k9NzxOngSTuBTLZjcjB3XL9h9PEpzzlhpSXtp3UhJ0
|
||||||
|
VSLHAoGADNykRP2U84xpPm6w1i6rGHqP8ge+OoowiGGKHTEHrV9Syfl/cB9UMeyC
|
||||||
|
EIhF7Nqk5v+Mh+P1RIRq59fMA4rNzmaxek5rK6EPg1b/gxzEsLVEEBXIQ5WWnXjR
|
||||||
|
u+ZVI73hb6yVVpuoNhRCgYgEpHknGCEvbURKOhyDMNilIj1mqDM=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEGDCCAwCgAwIBAgIDEAAEMA0GCSqGSIb3DQEBCwUAME8xGzAZBgNVBAoTEmxp
|
||||||
|
YndlYnNvY2tldHMtdGVzdDESMBAGA1UEBxMJWGlhb2JpdGFuMQ8wDQYDVQQIEwZU
|
||||||
|
YWlwZWkxCzAJBgNVBAYTAlRXMCAXDTIyMDcwNjExMjUxOFoYDzIwNTAwNzE5MTEy
|
||||||
|
NTE4WjBMMQswCQYDVQQGEwJUVzEPMA0GA1UECBMGVGFpcGVpMRswGQYDVQQKExJs
|
||||||
|
aWJ3ZWJzb2NrZXRzLXRlc3QxDzANBgNVBAMTBmNsaWVudDCCAiIwDQYJKoZIhvcN
|
||||||
|
AQEBBQADggIPADCCAgoCggIBAK8EsjZ4X7w4nBiTgX1ECxhKSgtvzduU6Nin+us5
|
||||||
|
A295uCD8C3HMRlh1rnsYqZrwMBfl9uhRs+XyXavBYcJavp6o+D4Zf0PsFAZUPlzI
|
||||||
|
6iKcMs6WZuB5vjjgin7Ml3Ziy6RclZOK69hkOChUkF0F8mccWzRXYJwq75rZCAUG
|
||||||
|
75pwcj/sJ3s4VECi5JkD9ko+3kcy6Ml9qgCLghWG1gOg1kvTy1boKkry3kKs9bad
|
||||||
|
n2aYQXY3yJXgwbs2IAdGnPcgx9ZnnkDgjT2s0QHFlsLvqFVphARJDwyNaYbpw/d9
|
||||||
|
xtUXFnZkFK+5B3jzD8Oadwc/qo6mh/uqAk4WQqPmFPYUP+Ph8DDcvELMo2pn+9c+
|
||||||
|
YOAoXtuED33J9N1sv2UWrkoLEJ8s4a2c/ViefvMFKo+OEPBOqWES9IeMXiLBDr/i
|
||||||
|
MDW1WJvFtWx2W3VtUnjMKbRG+xj4FGjv8ic/4SqnK6SCe/yKGgEyvo/afNm90Itv
|
||||||
|
1E6aiAyIMsmQC1JOoVh/e0qDGqpIVI7A/Osz9tShxDeRuZF9+gMMn0hAJkb5R3Mx
|
||||||
|
o8X6kDwaszHHvRUsfTfkfW8P1QbHwnMxeSypu0aIxw+c8Dmzt+ls7bj2m26H89Jh
|
||||||
|
Ylcrhd3Dm/7ahlVfd5yKwAW8vw2wWyECVy7LLZ6cKZWW2mZGKj7zuxkVe9B/MDTu
|
||||||
|
Tn9zAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAMLpo+SXCF+5sM5hWbJm8wRdN6Ec
|
||||||
|
MHjT3ta+zytm3oyqYcS/bSCVt0j8eqrZT3scCwwiwSy2s7Zcet5umkFUHb6Jen0Q
|
||||||
|
lXKBoaSGTWBOZbkiU6O6m6RLMrMmbp2khNag6Yq9p3sfsYz8iE18z6C1vpuKc9QT
|
||||||
|
PQLQezlhwZZWRNwkCyNRBji9N7U/uOROI8dk6KHnMZR2iR4eCSB2gtkvhPeLUXwX
|
||||||
|
8Jym2QHX5CY7/YMfs4D0I8fpjSOeeRY3cnJv37LuQL5rJpuvIZxFJ3nVr7V+DTMr
|
||||||
|
NqNE4iWHMVXqcEbMS/YJj84icGe1pCU01eI/a34rySgUu+3SCUUaXZs/+QA=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIJKQIBAAKCAgEArwSyNnhfvDicGJOBfUQLGEpKC2/N25To2Kf66zkDb3m4IPwL
|
||||||
|
ccxGWHWuexipmvAwF+X26FGz5fJdq8Fhwlq+nqj4Phl/Q+wUBlQ+XMjqIpwyzpZm
|
||||||
|
4Hm+OOCKfsyXdmLLpFyVk4rr2GQ4KFSQXQXyZxxbNFdgnCrvmtkIBQbvmnByP+wn
|
||||||
|
ezhUQKLkmQP2Sj7eRzLoyX2qAIuCFYbWA6DWS9PLVugqSvLeQqz1tp2fZphBdjfI
|
||||||
|
leDBuzYgB0ac9yDH1meeQOCNPazRAcWWwu+oVWmEBEkPDI1phunD933G1RcWdmQU
|
||||||
|
r7kHePMPw5p3Bz+qjqaH+6oCThZCo+YU9hQ/4+HwMNy8Qsyjamf71z5g4Che24QP
|
||||||
|
fcn03Wy/ZRauSgsQnyzhrZz9WJ5+8wUqj44Q8E6pYRL0h4xeIsEOv+IwNbVYm8W1
|
||||||
|
bHZbdW1SeMwptEb7GPgUaO/yJz/hKqcrpIJ7/IoaATK+j9p82b3Qi2/UTpqIDIgy
|
||||||
|
yZALUk6hWH97SoMaqkhUjsD86zP21KHEN5G5kX36AwyfSEAmRvlHczGjxfqQPBqz
|
||||||
|
Mce9FSx9N+R9bw/VBsfCczF5LKm7RojHD5zwObO36WztuPabbofz0mFiVyuF3cOb
|
||||||
|
/tqGVV93nIrABby/DbBbIQJXLsstnpwplZbaZkYqPvO7GRV70H8wNO5Of3MCAwEA
|
||||||
|
AQKCAgBlx3Vb9+S0sLcWE7HaBxfsqEcnH323lIFUfVuOLzmwOji59dkx9Hl0N/uf
|
||||||
|
2lfHoqZVP4a208yqjKesmmkffWYdHmY+ttUryr5ab+NUUgLW3b8uJIjvQd09dlcU
|
||||||
|
JiZu0kVa7/yMMN22mZGO4p6RekPdcsAXUDj+mHl+s3fWdJIiXgIS6mKLZdaZQCFw
|
||||||
|
Wr1/r8/T1+dIRav3ZSnhGuiac44ryp8Viy4NJ+/ZFnxOFpv8Lc7j0Mz1XB9Nn8xA
|
||||||
|
bvAZRxbvuJJvaCa7FyT0wtNJydU61oHPfC0kP5h+vLMZ2isnAKYvc0QUbKXL0I36
|
||||||
|
Unml3YByNKYf1F5Cyu82ITdPY7dC99Uon/G5fRUVnamX4A+NbfGEhlJ2XXUQXTih
|
||||||
|
AwFuZHrTHAJX9dow4LQQuIhDPOYUNzZo4W04DDJ+buf4gzpNh19YReBDuzg3CTbY
|
||||||
|
K8REnXvMY4KD6ciMOraxJS1CImgSphVpP7TwOOlbuxj9v1EcTAx4Ih18qrql622V
|
||||||
|
4Ep4lb/JxWnXNHw/z2dpdMUkJJf3lmn+/4ZV+gNBtByqLRIPTG2l7O4UnR/2iuqx
|
||||||
|
0wKpY7Th4IfXjSVW6sOc+Jw7d5hsV/z7AUREEJ4WX6NNpBwQZN2VxH07Ixyvk3ZQ
|
||||||
|
XgVQXNhryNCIWpt/fWivuPCzwt2UtOrp/5WC57w1eeMsXchrAQKCAQEA5KHzL4vW
|
||||||
|
BXefo6/KhwIJA7y5TSgAbDhYpQi42Llt37NMnJcyKTVmyxpfYG8enp1FqzTVYtce
|
||||||
|
ZIw3e9T5HGNqUjpoxCp53KcDu8iGF6CQqJMFrk8GkVQU81qXuWoAP2yJfQSmBuhg
|
||||||
|
PdHKUK1hG1o62DH2gW3en7j3pxCp4Nhp9Ip/59g0LsPDBg6nYi8x5aDNoYmvEVEA
|
||||||
|
5FRz7u/wwgymGIPgWpYG3c5ks1nQAZGSa7dLFtDMEYOx476wBuHAVSWSApdUM8Vw
|
||||||
|
k/kcf/vao3Ozqe3os77CoFRwZMNaz/F/z5SwcGwBoe4QgiBo39KXR3ZcW3Hchvzz
|
||||||
|
F8T6m80xazgTjQKCAQEAw/fUbSxik3x8ZpYPqSJZob0XvKktg7R5DN2W/eLnF4cU
|
||||||
|
E3p+8Z38LWv7l6rrNGMANHjYDGSZ/gy+H1ou8irT+UdBllXKpqD59ZVUs+ovNszR
|
||||||
|
hP7wlLM0BgEDX3kn6xRAcZ+tkYSdWyco+ZKOHcfquT/rJ4K5BCeu31HoxtQlxJDQ
|
||||||
|
R6KjBN+ac7c3l2WRGz/pKj0G952qBCrXLq220TNqDKDDLPT2ZFAkeHwzPX2eeqSu
|
||||||
|
b4kHEIDzrfMUD5cmTsBPq0LYulguBYxZWsPcm+fWcSnw9s2L+EL27R99IgzCvdGI
|
||||||
|
qREu/ieZfym1ngeX3VpsPNT6wCUXLacpyJFmYWqe/wKCAQBDuISjzLO0It6ySVuf
|
||||||
|
6cZpyPjKFdMqOvZmy9KUvzgATseeilpdQmgUONePZNqY+NSuBZQ/FqD1/2Jf15GC
|
||||||
|
CyBvAsYNdNdruDyu3pJ5ZSH0DLheDSJQ4ari+i5+yRsRrkB7O2lCGoqHRSC8DN76
|
||||||
|
jxtm9kWhyL1saglQ/uqSowefW3bPYYbzYyDdJnIF7xzEe4DtHC8y3d95wlokqAWU
|
||||||
|
NNLt6AIIUu/tuziwtyWZkVjhdwVS2L3ZYjzKzKyvHScvEVXSVqimPRE/g0Yh0qPM
|
||||||
|
TnUlHEcVFqfnfZtc/VBkz4+gGNa6NgO1xyI0qQznXyVYEYMBwxQjF8GZhMsGYx0E
|
||||||
|
OTPxAoIBAQC6g1rL2Y2sv7OK09Ht8QKL/HPmHMJz8sWvaAt4coftjaOe8lx1zB6i
|
||||||
|
gH7BmGwpvPwJ9OXzzZ/++tPM9UFvPTHotFgJJHgu5VFh2pH2drIfHwte4GHLtU+T
|
||||||
|
U02Jhb9nbyvyuL4ynJxDkF7gQgs+7vJJBDvkq/c5rCc05zl8W5fioM2LEJDFZ6gR
|
||||||
|
8FRZpNJZtBdQGytRat7Q/pE1HSK9s4iAolWAqVkEmn5LqjH+mVvHcIuRHkL6AzFp
|
||||||
|
4su6Furf0wtAfevWV2zd3PmCeJ20SRCBPiDYVUOdsuZfQzQOv0Rt80p1VyR5wujF
|
||||||
|
7cWstZ9ljtUrhK1vSR3pVotfGHvvxdGzAoIBAQC6YpZdqM8zh2EA0uiNUOU/ZcmT
|
||||||
|
1QlKg+iXKfWZr+adPVPk1dT9yUx/nNwnfCbKcTEyVt7eAziiLsZw6eXOPK9U8zvC
|
||||||
|
JPUqggDZ3+kbk4jPDRAs0JtIkC0buHzkXF0rgA4bJzuUG0DEsXJXYqHoOm1OVJfs
|
||||||
|
xoLPthqUD0D2sCY7FbJXgNMa3vYb19kxeJ7v7piBrihLk5LWdAx2lSV0laFE9O8C
|
||||||
|
h6HaZeR9JIiBycZ9qHIXPPCyZWQOllCIVi1qSQkpQN4vYNhc/IE7MvQ1JBaOblJH
|
||||||
|
cscILC8aiJVFOSZnNjK/florxtTODD9IG3gHfxTR1sfaLv5zCQf9wOI5RI/x
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEGzCCAwOgAwIBAgIDEAADMA0GCSqGSIb3DQEBCwUAME8xGzAZBgNVBAoTEmxp
|
||||||
|
YndlYnNvY2tldHMtdGVzdDESMBAGA1UEBxMJWGlhb2JpdGFuMQ8wDQYDVQQIEwZU
|
||||||
|
YWlwZWkxCzAJBgNVBAYTAlRXMCAXDTIyMDcwNjExMjUwM1oYDzIwNTAwNzE5MTEy
|
||||||
|
NTAzWjBPMQswCQYDVQQGEwJUVzEPMA0GA1UECBMGVGFpcGVpMRswGQYDVQQKExJs
|
||||||
|
aWJ3ZWJzb2NrZXRzLXRlc3QxEjAQBgNVBAMTCWxvY2FsaG9zdDCCAiIwDQYJKoZI
|
||||||
|
hvcNAQEBBQADggIPADCCAgoCggIBAMtXqhiYhGjVLwVuayvMMIrjK7S4LbRHlNUD
|
||||||
|
tJzFo2AjuLumteF+KLs4VjYzxudaCQryvMal0d/828un3x4gWjYTOC55cQ9MDNd+
|
||||||
|
pmMEE4+d1unTfo9882jM8ejlNFTcDoTPhoN7wqfxO15FniTwTCW90bmz72t6zLOm
|
||||||
|
3CpZuudHSnXHqtgOz6nSSbTjO1QR3LN+DvN04bMXBfe/0NVE/QpeOOGPZSGCOtKv
|
||||||
|
DZ4OuyKhm2RorGJnLmXxDFHmBMp00dAcerBERLDkLRpdW48K2i5tBQvAcJZzlL8Q
|
||||||
|
+H2fguSbB22eUytBbbNngv6hyRy0bAIzaYHwapRIxrLTWndMiB7yOvdislU+1SoF
|
||||||
|
WAbfV5yuhwOnwTMSodR09OxgUauA2yrAL2Q7BM5GnYLpUo5q5FgAwksJl8wySDpY
|
||||||
|
cQr7FrzSjqc1o9Us7hEElkvNBZk1+VPTaP5xEYypcbg+u+YCzctJWnJKbCBz2es6
|
||||||
|
KkpfC4aWc8UILlUK/W4jRFsNG3UhA1vnIZwK86iqm3k+PEbahcExA4qB/LBhogvs
|
||||||
|
4z9b2MQ35tiIoQDj7R5xN8c5AywDyb8xGv2Ymk4jGtDkdqrbtIU42+HM1yOk8u8Z
|
||||||
|
EixLQzowmqUqreBChoAosc3FOROmTCDlY634/acIIP31dedHNYZ51RrukAdA62Db
|
||||||
|
GDwgwz6TAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAKVv/SCPOgwPAY0G47ycvUHg
|
||||||
|
k3DQgmbxVTFFWQyrP6XvKHOl3eZU2ReJxeTxCMUKcNlW4+eM2oCZ7W1vitaupnO0
|
||||||
|
zLW6GBFSVXU4T+YZciIHnxkqlH3XxckgzNVLyWDAAJoaw2c+X1E9BzYbIT+zMYjZ
|
||||||
|
1bYAavyQOoMPd/1z0dYmI9FrwxZ2OBnv+cXAri0/JMGTczX6fVgSEPGfHHco6SKJ
|
||||||
|
RZW8VI9gIORuWhy9vxUWACJo0OkJDuU7+WTa9eDfjF+bbh+/Is86frL2gv8y9iMU
|
||||||
|
t267uX9RRd0FpiKp+pKMHmPwG3pukxWd8vkOrgjJDF+TVHggL1JYXZ7EJ9yjPdw=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIJKAIBAAKCAgEAy1eqGJiEaNUvBW5rK8wwiuMrtLgttEeU1QO0nMWjYCO4u6a1
|
||||||
|
4X4ouzhWNjPG51oJCvK8xqXR3/zby6ffHiBaNhM4LnlxD0wM136mYwQTj53W6dN+
|
||||||
|
j3zzaMzx6OU0VNwOhM+Gg3vCp/E7XkWeJPBMJb3RubPva3rMs6bcKlm650dKdceq
|
||||||
|
2A7PqdJJtOM7VBHcs34O83ThsxcF97/Q1UT9Cl444Y9lIYI60q8Nng67IqGbZGis
|
||||||
|
YmcuZfEMUeYEynTR0Bx6sEREsOQtGl1bjwraLm0FC8BwlnOUvxD4fZ+C5JsHbZ5T
|
||||||
|
K0Fts2eC/qHJHLRsAjNpgfBqlEjGstNad0yIHvI692KyVT7VKgVYBt9XnK6HA6fB
|
||||||
|
MxKh1HT07GBRq4DbKsAvZDsEzkadgulSjmrkWADCSwmXzDJIOlhxCvsWvNKOpzWj
|
||||||
|
1SzuEQSWS80FmTX5U9No/nERjKlxuD675gLNy0lackpsIHPZ6zoqSl8LhpZzxQgu
|
||||||
|
VQr9biNEWw0bdSEDW+chnArzqKqbeT48RtqFwTEDioH8sGGiC+zjP1vYxDfm2Iih
|
||||||
|
AOPtHnE3xzkDLAPJvzEa/ZiaTiMa0OR2qtu0hTjb4czXI6Ty7xkSLEtDOjCapSqt
|
||||||
|
4EKGgCixzcU5E6ZMIOVjrfj9pwgg/fV150c1hnnVGu6QB0DrYNsYPCDDPpMCAwEA
|
||||||
|
AQKCAgEAti3Ie1jJ6JJ3GkqfQhIiu4xkL/mDGS24rlAvlPWNNilK7PSjp288VuBW
|
||||||
|
fSFOZyBHUcPAVclii2ckFQZF49vgduopP5oWLTpZlwglUjjovZcQYVXW4f90pMRZ
|
||||||
|
R+tyQ0UUzPCQ92P+NjDum7uLwFR6JKY4LESFkKnolFw+jMLvrR42G+fTrNc4PlHl
|
||||||
|
L2cWVG/caAPcqUpim+1Yr2rvUuQIQS3BkHTEioz+3ptve9h+2LwpWIBfUZpX+4VH
|
||||||
|
IGkQicK359hcI2hGkqUbs69IxJNYfpe2GZ1EQccz0SsHNqWew+oRcaiTowFvo3ht
|
||||||
|
6GxSnqEW07H0KTKYilnZi4XIW3H50QauahbwH0uVsd2U9o4l7T8ZMe4RPmf9wFn/
|
||||||
|
Ez4FthbnQOWBo1DxaBmx9Bfn5nC5I6Ryr6XMnnJJ+eAtSjXSAV7/63CrpcB+sBtF
|
||||||
|
izF3yEQ8rtWACqxjMUPC3Jq9l9398/jzCpZZLvz4NUjjtfNh3i6DP6IhAK13TV7I
|
||||||
|
4G40brZhmXSVKBnxG7D5iY6PbgVWHFDEjPDwoJktK1BPad3qWf94fmt5ykZd/teq
|
||||||
|
eRIEvUzJG5PFGwOzxVRHDN8QQmNJcCJuytw4DnsxoC83izfh+nEqEPPQiDFoTLyD
|
||||||
|
+4VRgFBb6airVcyXWdr20CdOGgXoBMrNBl+E62MYAe2qc67cQYECggEBAO92XNAF
|
||||||
|
RAwZydzYch5E5ejz9TZbHfef4zNBHU/mV1vh89d9KjSF/OiL3EnoYxPkHgUXyn9A
|
||||||
|
4C4u1rldZ6dwCVAbZJnT8UF2nLRFMXhaqyJEmT7jCYRy7erd7VL95gVhUOsJYwLm
|
||||||
|
tYfcik07QmDSPVuGhV2KeknzTa1NSTU8vwQF+wKHi864dmzkslYnQqbFoTVRZtNe
|
||||||
|
rLb6g6ncw6JWx9yeB07VdpJp6x9TYT+HGj3kb5acuInP+jg7GZzkrHJMnHLweFvb
|
||||||
|
NWZflPfiANmwM8wMR+kfP2vogGamKCieojVaTi8vgqqjx4UGPd596FNFeiZRvXNk
|
||||||
|
0dB0AK2Q/126SlECggEBANlitdxHttxh/WqwFK35bcOTXah6PapPdg/72PuOl3Wt
|
||||||
|
7Wn8At6FIGQ3XZOOPf9v5HfqPpSlNV93AXXaO6ITzwIEb8IG3iEOtTLw292KU8lJ
|
||||||
|
Ll8XphOkVUrPkzGG6dfVNp6I2hTpYJISwAU5HvKpc9oB0YKhVLfC+idakVC5fZz6
|
||||||
|
zFAo27FeoIEdHt92Ze6Q34NJgpVbgmT0+wtuurqoZ/zRtkx45wuqI3E+TDa1Suq3
|
||||||
|
4OQse2WcgjzSfdLJtTfyeL1p4VhGycxg+JE5H4ji3z53FxrMnA0mSElyEji88xTG
|
||||||
|
HCYGInJpZr+QzgPTzWf5R46k08GKsmVgj1P2z9Oe3aMCggEAHBkdZuNXSrwz7ZAQ
|
||||||
|
Q/D9sUn++fPTHl1KgZcgY2G52nQ28pAjRap7NKZEoP99sLXRt/NEtY3dQE4KsBF/
|
||||||
|
uivxS88LDOnLo0zRsm10EpVBA3JdMP3e/gWmWSrVUCmute7nWczuK0b7Aggkyk8r
|
||||||
|
yMSiony0ZXd8RfUgpjctYevQ1h4FiBRzmTwX2Us0idttfLgv5Fu63w564bWfgW0A
|
||||||
|
H6pjJUX9wYhWs3NuPWzBOn0V3TFSzq9xD+qxh6uXo8tJWNgYe3wwA0dDitGoXzXL
|
||||||
|
t79vNIA0Z+xcusK7XlAc4jdK/UutZOgEQxWYsck7tUG84xiB7sEko8euvvD9q2Aa
|
||||||
|
NttHIQKCAQBDw0ZnVR+GUTFaqWq41JDHr085rmk8hqqJzvrTZ56E+2OPbxRB4wO2
|
||||||
|
2Z9c2LEwx+NTEfzR/zHFAWa+IvoFyuDzgwiHVfa2HUyUhYoz4+O8UgiXcdq+MrK3
|
||||||
|
sD706FZKhlcRUh2Q+TK2NgwPVL/16qaHZZu2ea3b3nZ2AABeRoQGhBNeRW4+Ov4T
|
||||||
|
YU13YQPjOtvBpA+FmVHrQIZlCO5CMy6/G2wVKHb1JuMHcOD3CMpConEDD26jvQbI
|
||||||
|
COFZ7LCFsRq8CJ5LEz3ibLPvKPxQglAOu7bDDtbyLmXIJzvQ3e10I1Ew9SaVvY9e
|
||||||
|
1beyb7qO16DbsTulECQN/+yNwtkD4Xi7AoIBAAcsoOzuPm75JO2B3v2Ed+7GK1Sc
|
||||||
|
lvVNU3Cz0NfLPJVysVSHegaSZ83ZoNPhmTBGbrVEr1b3BUY0lkFIOo3mf02l7WHY
|
||||||
|
xMQ8Hc2anU8qdOXvoXbQ4/YYK1WBvKu/ClBSEcaO90bqyDs73miFW/C1TfxPXnkS
|
||||||
|
T+rOMmwRQgLFPl5FAbHOk1AXZE4FFGPuUbRtkOErdw4tZhSwfXt/FA6+2LnHy788
|
||||||
|
XOFbq0FKncnh2ja+d7eNlHwLKcSC4CFNFKQmlEhZZJHcen/B0mzrHl5vSlYy7P7s
|
||||||
|
c2R43TlEAMwr2J0L7Xe4+3E61OdCO0F7NF0v3f/4ZoJImLtNzMO1hiaQ4ig=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
@@ -1,2 +1,58 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Nothing to do
|
# Nothing to do
|
||||||
|
sudo apt-get update -qq
|
||||||
|
# install core development packages
|
||||||
|
sudo apt-get install -qq build-essential
|
||||||
|
sudo apt-get install -qq cmake
|
||||||
|
sudo apt-get install -qq lcov
|
||||||
|
sudo apt-get install -qq clang
|
||||||
|
sudo apt-get install -qq gcc
|
||||||
|
sudo apt-get install -qq libconfig-dev
|
||||||
|
sudo apt-get install -qq liblwip-dev
|
||||||
|
sudo apt-get install -qq mingw-w64
|
||||||
|
|
||||||
|
# install git and useful tools packages
|
||||||
|
sudo apt-get install -qq git
|
||||||
|
sudo apt-get install -qq gitk
|
||||||
|
sudo apt-get install -qq gitg
|
||||||
|
|
||||||
|
# install source code format packages
|
||||||
|
sudo apt-get install -qq tofrodos
|
||||||
|
sudo apt-get install -qq clang-format
|
||||||
|
sudo apt-get install -qq sloccount
|
||||||
|
sudo apt-get install -qq complexity
|
||||||
|
sudo apt-get install -qq pmccabe
|
||||||
|
|
||||||
|
# install ARM compiler and debugging tools
|
||||||
|
sudo apt-get install -qq gcc-arm-none-eabi
|
||||||
|
sudo apt-get install -qq binutils-arm-none-eabi
|
||||||
|
sudo apt-get install -qq picolibc-arm-none-eabi
|
||||||
|
sudo apt-get install -qq gdb-multiarch
|
||||||
|
|
||||||
|
# install AVR compiler and debugging tools
|
||||||
|
sudo apt-get install -qq gcc-avr
|
||||||
|
sudo apt-get install -qq avarice
|
||||||
|
sudo apt-get install -qq avr-libc
|
||||||
|
sudo apt-get install -qq avrdude
|
||||||
|
sudo apt-get install -qq gdb-avr
|
||||||
|
sudo apt-get install -qq avrdude-doc
|
||||||
|
|
||||||
|
# install static analysis tools
|
||||||
|
sudo apt-get install -qq flawfinder
|
||||||
|
sudo apt-get install -qq cppcheck
|
||||||
|
sudo apt-get install -qq splint
|
||||||
|
sudo apt-get install -qq clang-tools
|
||||||
|
sudo apt-get install -qq clang-tidy
|
||||||
|
sudo apt-get install -qq codespell
|
||||||
|
|
||||||
|
# install BACnet Secure Connect (BACnet/SC) dependencies
|
||||||
|
sudo apt-get install -qq libconfig-dev
|
||||||
|
sudo apt-get install -qq libcap-dev
|
||||||
|
sudo apt-get install -qq libssl-dev
|
||||||
|
sudo apt-get install -qq libuv1-dev
|
||||||
|
git clone --branch v4.3-stable https://github.com/warmcat/libwebsockets.git
|
||||||
|
bash -c 'cd libwebsockets;mkdir build;cd build;cmake .. -DLWS_WITH_LIBUV=ON -DLWS_WITH_MINIMAL_EXAMPLES=0 -DLWS_MAX_SMP=32;make'
|
||||||
|
sudo bash -c 'cd libwebsockets;cd build;make install'
|
||||||
|
|
||||||
|
# all complete!
|
||||||
|
echo "🚀 ALL GOOD TO GO"
|
||||||
|
|||||||
@@ -0,0 +1,183 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Implementation of mutex abstraction used in BACNet secure connect.
|
||||||
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||||
|
* @date August 2022
|
||||||
|
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||||
|
*/
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "bacnet/basic/sys/debug.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-event.h"
|
||||||
|
|
||||||
|
#define DEBUG_BSC_EVENT 0
|
||||||
|
|
||||||
|
#if DEBUG_BSC_EVENT == 1
|
||||||
|
#define DEBUG_PRINTF printf
|
||||||
|
#else
|
||||||
|
#undef DEBUG_ENABLED
|
||||||
|
#define DEBUG_PRINTF debug_printf_disabled
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct BSC_Event {
|
||||||
|
pthread_mutex_t mutex;
|
||||||
|
pthread_cond_t cond;
|
||||||
|
bool v;
|
||||||
|
size_t counter;
|
||||||
|
};
|
||||||
|
|
||||||
|
BSC_EVENT *bsc_event_init(void)
|
||||||
|
{
|
||||||
|
BSC_EVENT *ev;
|
||||||
|
pthread_mutexattr_t attr;
|
||||||
|
|
||||||
|
if (pthread_mutexattr_init(&attr) != 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) {
|
||||||
|
pthread_mutexattr_destroy(&attr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ev = malloc(sizeof(BSC_EVENT));
|
||||||
|
|
||||||
|
if (!ev) {
|
||||||
|
pthread_mutexattr_destroy(&attr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_mutex_init(&ev->mutex, &attr) != 0) {
|
||||||
|
pthread_mutexattr_destroy(&attr);
|
||||||
|
free(ev);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutexattr_destroy(&attr);
|
||||||
|
|
||||||
|
if (pthread_cond_init(&ev->cond, NULL) != 0) {
|
||||||
|
pthread_mutex_destroy(&ev->mutex);
|
||||||
|
free(ev);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ev->v = false;
|
||||||
|
ev->counter = 0;
|
||||||
|
return ev;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsc_event_deinit(BSC_EVENT *ev)
|
||||||
|
{
|
||||||
|
pthread_mutex_destroy(&ev->mutex);
|
||||||
|
pthread_cond_destroy(&ev->cond);
|
||||||
|
free(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsc_event_wait(BSC_EVENT *ev)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&ev->mutex);
|
||||||
|
DEBUG_PRINTF("bsc_event_wait() >>> ev = %p\n", ev);
|
||||||
|
DEBUG_PRINTF("bsc_event_wait() counter before %zu\n", ev->counter);
|
||||||
|
ev->counter++;
|
||||||
|
DEBUG_PRINTF("bsc_event_wait() counter %zu\n", ev->counter);
|
||||||
|
while (!ev->v) {
|
||||||
|
pthread_cond_wait(&ev->cond, &ev->mutex);
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF("bsc_event_wait() ev = %p\n", ev);
|
||||||
|
DEBUG_PRINTF("bsc_event_wait() before counter %zu\n", ev->counter);
|
||||||
|
ev->counter--;
|
||||||
|
DEBUG_PRINTF("bsc_event_wait() counter %zu\n", ev->counter);
|
||||||
|
if (!ev->counter) {
|
||||||
|
ev->v = false;
|
||||||
|
DEBUG_PRINTF("bsc_event_wait() reset ev\n");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DEBUG_PRINTF("bsc_event_wait() wake up other waiting threads\n");
|
||||||
|
pthread_cond_broadcast(&ev->cond);
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF("bsc_event_wait() <<< ev = %p\n", ev);
|
||||||
|
pthread_mutex_unlock(&ev->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bsc_event_timedwait(BSC_EVENT *ev, unsigned int ms_timeout)
|
||||||
|
{
|
||||||
|
struct timespec to;
|
||||||
|
int r = 0;
|
||||||
|
|
||||||
|
clock_gettime(CLOCK_REALTIME, &to);
|
||||||
|
to.tv_sec = to.tv_sec + ms_timeout / 1000;
|
||||||
|
to.tv_nsec = to.tv_nsec + (ms_timeout % 1000) * 1000000;
|
||||||
|
to.tv_sec += to.tv_nsec / 1000000000;
|
||||||
|
to.tv_nsec %= 1000000000;
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bsc_event_timedwait() >>> before lock ev = %p ev->v = %d\n",
|
||||||
|
ev, ev->v);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&ev->mutex);
|
||||||
|
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bsc_event_timedwait() >>> after lock ev = %p ev->v = %d\n", ev, ev->v);
|
||||||
|
|
||||||
|
ev->counter++;
|
||||||
|
DEBUG_PRINTF("bsc_event_timedwait() counter %zu\n", ev->counter);
|
||||||
|
|
||||||
|
while (!ev->v) {
|
||||||
|
r = pthread_cond_timedwait(&ev->cond, &ev->mutex, &to);
|
||||||
|
if (r != 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ev->v) {
|
||||||
|
if(r!=0) {
|
||||||
|
DEBUG_PRINTF("Fired!!! r = %d\n", r);
|
||||||
|
}
|
||||||
|
r = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ev->counter--;
|
||||||
|
DEBUG_PRINTF("bsc_event_timedwait() counter %zu\n", ev->counter);
|
||||||
|
|
||||||
|
if (!ev->counter) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bsc_event_timedwait() event is reset, err = %d\n", r);
|
||||||
|
ev->v = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DEBUG_PRINTF("bsc_event_timedwait() wake up other waiting threads\n");
|
||||||
|
pthread_cond_broadcast(&ev->cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bsc_event_timedwait() <<< ret = %d, ev = %p ev->v = %d r = %d\n",
|
||||||
|
r == 0, ev, ev->v, r);
|
||||||
|
pthread_mutex_unlock(&ev->mutex);
|
||||||
|
return r == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsc_event_signal(BSC_EVENT *ev)
|
||||||
|
{
|
||||||
|
DEBUG_PRINTF("bsc_event_signal() >>> ev = %p\n", ev);
|
||||||
|
pthread_mutex_lock(&ev->mutex);
|
||||||
|
DEBUG_PRINTF("bsc_event_signal() >>> ev = %p\n", ev);
|
||||||
|
ev->v = true;
|
||||||
|
pthread_cond_broadcast(&ev->cond);
|
||||||
|
DEBUG_PRINTF("bsc_event_signal() <<< ev = %p\n", ev);
|
||||||
|
pthread_mutex_unlock(&ev->mutex);
|
||||||
|
DEBUG_PRINTF("bsc_event_signal() <<< ev = %p\n", ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsc_wait(int seconds)
|
||||||
|
{
|
||||||
|
sleep(seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsc_wait_ms(int mseconds)
|
||||||
|
{
|
||||||
|
usleep(mseconds * 1000);
|
||||||
|
}
|
||||||
@@ -0,0 +1,762 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Implementation of websocket client interface.
|
||||||
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||||
|
* @date May 2022
|
||||||
|
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||||
|
*/
|
||||||
|
#include <libwebsockets.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include "bacnet/datalink/bsc/websocket.h"
|
||||||
|
#include "bacnet/basic/sys/debug.h"
|
||||||
|
#include "websocket-global.h"
|
||||||
|
|
||||||
|
#define DEBUG_WEBSOCKET_CLIENT 0
|
||||||
|
|
||||||
|
#if DEBUG_WEBSOCKET_CLIENT == 1
|
||||||
|
#define DEBUG_PRINTF debug_printf
|
||||||
|
#else
|
||||||
|
#undef DEBUG_ENABLED
|
||||||
|
#define DEBUG_PRINTF(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LWS_PROTOCOL_LIST_TERM
|
||||||
|
#define LWS_PROTOCOL_LIST_TERM \
|
||||||
|
{ \
|
||||||
|
NULL, NULL, 0, 0, 0, NULL, 0 \
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BSC_RX_BUFFER_LEN BSC_WEBSOCKET_RX_BUFFER_LEN
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BSC_WEBSOCKET_STATE_IDLE = 0,
|
||||||
|
BSC_WEBSOCKET_STATE_CONNECTING = 1,
|
||||||
|
BSC_WEBSOCKET_STATE_CONNECTED = 2,
|
||||||
|
BSC_WEBSOCKET_STATE_DISCONNECTING = 3
|
||||||
|
} BSC_WEBSOCKET_STATE;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
struct lws_context *ctx;
|
||||||
|
struct lws *ws;
|
||||||
|
BSC_WEBSOCKET_STATE state;
|
||||||
|
bool want_send_data;
|
||||||
|
bool can_send_data;
|
||||||
|
BSC_WEBSOCKET_CLI_DISPATCH dispatch_func;
|
||||||
|
void *user_param;
|
||||||
|
uint8_t *fragment_buffer;
|
||||||
|
size_t fragment_buffer_size;
|
||||||
|
size_t fragment_buffer_len;
|
||||||
|
char err_desc[BSC_WEBSOCKET_ERR_DESC_STR_MAX_LEN];
|
||||||
|
BACNET_ERROR_CODE err_code;
|
||||||
|
} BSC_WEBSOCKET_CONNECTION;
|
||||||
|
|
||||||
|
/* Some forward function declarations */
|
||||||
|
|
||||||
|
static int bws_cli_websocket_event(struct lws *wsi,
|
||||||
|
enum lws_callback_reasons reason,
|
||||||
|
void *user,
|
||||||
|
void *in,
|
||||||
|
size_t len);
|
||||||
|
|
||||||
|
static const char *bws_hub_protocol = BSC_WEBSOCKET_HUB_PROTOCOL_STR;
|
||||||
|
static const char *bws_direct_protocol = BSC_WEBSOCKET_DIRECT_PROTOCOL_STR;
|
||||||
|
|
||||||
|
static pthread_mutex_t bws_cli_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
/* Websockets protocol defined in BACnet/SC \S AB.7.1. */
|
||||||
|
|
||||||
|
static struct lws_protocols bws_cli_direct_protocol[] = {
|
||||||
|
{ BSC_WEBSOCKET_DIRECT_PROTOCOL_STR, bws_cli_websocket_event, 0, 0, 0, NULL,
|
||||||
|
0 },
|
||||||
|
LWS_PROTOCOL_LIST_TERM
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct lws_protocols bws_cli_hub_protocol[] = {
|
||||||
|
{ BSC_WEBSOCKET_HUB_PROTOCOL_STR, bws_cli_websocket_event, 0, 0, 0, NULL,
|
||||||
|
0 },
|
||||||
|
LWS_PROTOCOL_LIST_TERM
|
||||||
|
};
|
||||||
|
|
||||||
|
static lws_retry_bo_t bws_retry;
|
||||||
|
|
||||||
|
static BSC_WEBSOCKET_CONNECTION bws_cli_conn[BSC_CLIENT_WEBSOCKETS_MAX_NUM] = {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
static BSC_WEBSOCKET_HANDLE bws_cli_alloc_connection(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < BSC_CLIENT_WEBSOCKETS_MAX_NUM; i++) {
|
||||||
|
if (bws_cli_conn[i].state == BSC_WEBSOCKET_STATE_IDLE) {
|
||||||
|
memset(&bws_cli_conn[i], 0, sizeof(bws_cli_conn[i]));
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return BSC_WEBSOCKET_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bws_cli_free_connection(BSC_WEBSOCKET_HANDLE h)
|
||||||
|
{
|
||||||
|
if (h >= 0 && h < BSC_CLIENT_WEBSOCKETS_MAX_NUM) {
|
||||||
|
if (bws_cli_conn[h].state != BSC_WEBSOCKET_STATE_IDLE) {
|
||||||
|
if (bws_cli_conn[h].fragment_buffer) {
|
||||||
|
free(bws_cli_conn[h].fragment_buffer);
|
||||||
|
bws_cli_conn[h].fragment_buffer = NULL;
|
||||||
|
bws_cli_conn[h].fragment_buffer_len = 0;
|
||||||
|
bws_cli_conn[h].fragment_buffer_size = 0;
|
||||||
|
}
|
||||||
|
bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_IDLE;
|
||||||
|
bws_cli_conn[h].ws = NULL;
|
||||||
|
bws_cli_conn[h].ctx = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BSC_WEBSOCKET_HANDLE bws_cli_find_connnection(struct lws *ws)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < BSC_CLIENT_WEBSOCKETS_MAX_NUM; i++) {
|
||||||
|
if (bws_cli_conn[i].ws == ws &&
|
||||||
|
bws_cli_conn[i].state != BSC_WEBSOCKET_STATE_IDLE) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return BSC_WEBSOCKET_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bws_set_err_desc(BSC_WEBSOCKET_HANDLE h, char *err_desc)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
if (bws_cli_conn[h].err_code == ERROR_CODE_SUCCESS) {
|
||||||
|
len = strlen(err_desc) >= sizeof(bws_cli_conn[h].err_desc)
|
||||||
|
? sizeof(bws_cli_conn[h].err_desc) - 1
|
||||||
|
: strlen(err_desc);
|
||||||
|
|
||||||
|
memcpy(bws_cli_conn[h].err_desc, err_desc, len);
|
||||||
|
bws_cli_conn[h].err_desc[len] = 0;
|
||||||
|
|
||||||
|
if (strstr(err_desc, "tls:")) {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_TLS_ERROR;
|
||||||
|
} else {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bws_set_disconnect_reason(BSC_WEBSOCKET_HANDLE h, uint16_t err_code)
|
||||||
|
{
|
||||||
|
bws_cli_conn[h].err_desc[0] = 0;
|
||||||
|
switch (err_code) {
|
||||||
|
case LWS_CLOSE_STATUS_NORMAL: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_CLOSED_BY_PEER;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_GOINGAWAY: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_ENDPOINT_LEAVES;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_PROTOCOL_ERR: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_PROTOCOL_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_NOT_ACCEPTED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_NO_STATUS:
|
||||||
|
case LWS_CLOSE_STATUS_RESERVED: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_ABNORMAL_CLOSE: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_NOT_ACCEPTED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_INVALID_PAYLOAD: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_INCONSISTENT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_POLICY_VIOLATION: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_AGAINST_POLICY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_FRAME_TOO_LONG;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_EXTENSION_REQUIRED: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_EXTENSION_MISSING;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_UNEXPECTED_CONDITION: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_REQUEST_UNAVAILABLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bws_cli_websocket_event(struct lws *wsi,
|
||||||
|
enum lws_callback_reasons reason,
|
||||||
|
void *user,
|
||||||
|
void *in,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
BSC_WEBSOCKET_HANDLE h;
|
||||||
|
BSC_WEBSOCKET_CLI_DISPATCH dispatch_func;
|
||||||
|
void *user_param;
|
||||||
|
uint8_t err_code[2];
|
||||||
|
|
||||||
|
(void)user;
|
||||||
|
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() >>> reason = %d, user = %p, in = %p\n",
|
||||||
|
reason, user, in);
|
||||||
|
|
||||||
|
switch (reason) {
|
||||||
|
case LWS_CALLBACK_CLIENT_ESTABLISHED: {
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
h = bws_cli_find_connnection(wsi);
|
||||||
|
|
||||||
|
if (h == BSC_WEBSOCKET_INVALID_HANDLE) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() can not find websocket handle "
|
||||||
|
"for wsi %p\n",
|
||||||
|
wsi);
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = 0\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bws_cli_websocket_event() connection established\n");
|
||||||
|
bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_CONNECTED;
|
||||||
|
dispatch_func = bws_cli_conn[h].dispatch_func;
|
||||||
|
user_param = bws_cli_conn[h].user_param;
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
dispatch_func(
|
||||||
|
h, BSC_WEBSOCKET_CONNECTED, 0, NULL, NULL, 0, user_param);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CALLBACK_CLIENT_RECEIVE: {
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
h = bws_cli_find_connnection(wsi);
|
||||||
|
|
||||||
|
if (h == BSC_WEBSOCKET_INVALID_HANDLE) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() can not find websocket handle "
|
||||||
|
"for wsi %p\n",
|
||||||
|
wsi);
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = 0\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() received %d bytes of data\n", len);
|
||||||
|
if (!lws_frame_is_binary(wsi)) {
|
||||||
|
/* According AB.7.5.3 BACnet/SC BVLC Message Exchange,
|
||||||
|
if a received data frame is not binary,
|
||||||
|
the WebSocket connection shall be closed with a
|
||||||
|
status code of 1003 -WEBSOCKET_DATA_NOT_ACCEPTED.
|
||||||
|
*/
|
||||||
|
DEBUG_PRINTF("bws_cli_websocket_event() got non-binary frame, "
|
||||||
|
"close connection for socket %d\n",
|
||||||
|
h);
|
||||||
|
lws_close_reason(
|
||||||
|
wsi, LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE, NULL, 0);
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = -1\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (bws_cli_conn[h].state != BSC_WEBSOCKET_STATE_CONNECTED) {
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
} else {
|
||||||
|
if (!bws_cli_conn[h].fragment_buffer) {
|
||||||
|
DEBUG_PRINTF("bws_cli_websocket_event() alloc %d bytes for "
|
||||||
|
"socket %d\n",
|
||||||
|
len, h);
|
||||||
|
bws_cli_conn[h].fragment_buffer = malloc(BSC_RX_BUFFER_LEN);
|
||||||
|
if (!bws_cli_conn[h].fragment_buffer) {
|
||||||
|
lws_close_reason(
|
||||||
|
wsi, LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE, NULL, 0);
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = -1, "
|
||||||
|
"allocation of %d bytes failed\n",
|
||||||
|
len <= BSC_RX_BUFFER_LEN ? BSC_RX_BUFFER_LEN : len);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
bws_cli_conn[h].fragment_buffer_len = 0;
|
||||||
|
bws_cli_conn[h].fragment_buffer_size = BSC_RX_BUFFER_LEN;
|
||||||
|
}
|
||||||
|
if (bws_cli_conn[h].fragment_buffer_len + len >
|
||||||
|
bws_cli_conn[h].fragment_buffer_size) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() realloc buf of %d bytes"
|
||||||
|
"for socket %d to %d bytes\n",
|
||||||
|
bws_cli_conn[h].fragment_buffer_len, h,
|
||||||
|
bws_cli_conn[h].fragment_buffer_len + len);
|
||||||
|
bws_cli_conn[h].fragment_buffer =
|
||||||
|
realloc(bws_cli_conn[h].fragment_buffer,
|
||||||
|
bws_cli_conn[h].fragment_buffer_len + len);
|
||||||
|
if (!bws_cli_conn[h].fragment_buffer) {
|
||||||
|
lws_close_reason(
|
||||||
|
wsi, LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE, NULL, 0);
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = -1, "
|
||||||
|
"re-allocation of %d bytes failed\n",
|
||||||
|
bws_cli_conn[h].fragment_buffer_len + len);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
bws_cli_conn[h].fragment_buffer_size =
|
||||||
|
bws_cli_conn[h].fragment_buffer_len + len;
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF("bws_cli_websocket_event() got next %d bytes for "
|
||||||
|
"socket %d\n",
|
||||||
|
len, h);
|
||||||
|
memcpy(&bws_cli_conn[h].fragment_buffer[
|
||||||
|
bws_cli_conn[h].fragment_buffer_len],
|
||||||
|
in, len);
|
||||||
|
bws_cli_conn[h].fragment_buffer_len += len;
|
||||||
|
|
||||||
|
if (lws_is_final_fragment(wsi)) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() last fragment received\n");
|
||||||
|
dispatch_func = bws_cli_conn[h].dispatch_func;
|
||||||
|
user_param = bws_cli_conn[h].user_param;
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
dispatch_func(h, BSC_WEBSOCKET_RECEIVED, 0, NULL,
|
||||||
|
bws_cli_conn[h].fragment_buffer,
|
||||||
|
bws_cli_conn[h].fragment_buffer_len, user_param);
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
bws_cli_conn[h].fragment_buffer_len = 0;
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
} else {
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CALLBACK_CLIENT_WRITEABLE: {
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
h = bws_cli_find_connnection(wsi);
|
||||||
|
|
||||||
|
if (h == BSC_WEBSOCKET_INVALID_HANDLE) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() can not find websocket handle "
|
||||||
|
"for wsi %p\n",
|
||||||
|
wsi);
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = 0\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bws_cli_websocket_event() can write, state = %d\n",
|
||||||
|
bws_cli_conn[h].state);
|
||||||
|
DEBUG_PRINTF("bws_cli_websocket_event() ws = %d, cs = %d\n",
|
||||||
|
bws_cli_conn[h].want_send_data, bws_cli_conn[h].can_send_data);
|
||||||
|
if (bws_cli_conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED &&
|
||||||
|
bws_cli_conn[h].want_send_data) {
|
||||||
|
bws_cli_conn[h].can_send_data = true;
|
||||||
|
dispatch_func = bws_cli_conn[h].dispatch_func;
|
||||||
|
user_param = bws_cli_conn[h].user_param;
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
dispatch_func(
|
||||||
|
h, BSC_WEBSOCKET_SENDABLE, 0, NULL, NULL, 0, user_param);
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
bws_cli_conn[h].want_send_data = false;
|
||||||
|
bws_cli_conn[h].can_send_data = false;
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() was send , ws = %d, cs = %d\n",
|
||||||
|
bws_cli_conn[h].want_send_data,
|
||||||
|
bws_cli_conn[h].can_send_data);
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
/* wakeup worker to process internal state */
|
||||||
|
lws_cancel_service(bws_cli_conn[h].ctx);
|
||||||
|
} else {
|
||||||
|
bws_cli_conn[h].want_send_data = false;
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() no send, ws = %d, cs = %d\n",
|
||||||
|
bws_cli_conn[h].want_send_data,
|
||||||
|
bws_cli_conn[h].can_send_data);
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: {
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
h = bws_cli_find_connnection(wsi);
|
||||||
|
if (h != BSC_WEBSOCKET_INVALID_HANDLE && len >= 2) {
|
||||||
|
err_code[0] = ((uint8_t *)in)[1];
|
||||||
|
err_code[1] = ((uint8_t *)in)[0];
|
||||||
|
bws_set_disconnect_reason(h, *((uint16_t *)&err_code));
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CALLBACK_CLIENT_CLOSED:
|
||||||
|
case LWS_CALLBACK_CLOSED:
|
||||||
|
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: {
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
h = bws_cli_find_connnection(wsi);
|
||||||
|
if (h != BSC_WEBSOCKET_INVALID_HANDLE) {
|
||||||
|
bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING;
|
||||||
|
if (reason == LWS_CALLBACK_CLIENT_CONNECTION_ERROR && in) {
|
||||||
|
bws_set_err_desc(h, (char *)in);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
/* wakeup worker to process pending event */
|
||||||
|
lws_cancel_service(bws_cli_conn[h].ctx);
|
||||||
|
} else {
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = 0\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *bws_cli_worker(void *arg)
|
||||||
|
{
|
||||||
|
BSC_WEBSOCKET_CONNECTION *conn = (BSC_WEBSOCKET_CONNECTION *)arg;
|
||||||
|
BSC_WEBSOCKET_HANDLE h = conn - &bws_cli_conn[0];
|
||||||
|
BSC_WEBSOCKET_CLI_DISPATCH dispatch_func;
|
||||||
|
void *user_param;
|
||||||
|
char err_desc[BSC_WEBSOCKET_ERR_DESC_STR_MAX_LEN];
|
||||||
|
uint16_t err_code;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
DEBUG_PRINTF("bws_cli_worker() try mutex h = %d\n", h);
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_worker() mutex locked h = %d\n", h);
|
||||||
|
if (conn->state == BSC_WEBSOCKET_STATE_CONNECTED) {
|
||||||
|
if (conn->want_send_data) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_worker() process request for sending data\n");
|
||||||
|
lws_callback_on_writable(conn->ws);
|
||||||
|
}
|
||||||
|
} else if (conn->state == BSC_WEBSOCKET_STATE_DISCONNECTING) {
|
||||||
|
DEBUG_PRINTF("bws_cli_worker() process disconnecting event\n");
|
||||||
|
DEBUG_PRINTF("bws_cli_worker() destroy ctx %p\n", conn->ctx);
|
||||||
|
/* TRICKY: Libwebsockets API is not designed to be used from
|
||||||
|
multipe service threads, as a result lws_context_destroy()
|
||||||
|
is not thread safe. More over, on different platforms the
|
||||||
|
function behaves in different ways. Call of
|
||||||
|
lws_context_destroy() leads to several calls of
|
||||||
|
bws_cli_websocket_event() callback (LWS_CALLBACK_CLOSED,
|
||||||
|
etc..). But under some OS (MacOSx) that callback is
|
||||||
|
called from context of the bws_cli_worker() thread and
|
||||||
|
under some other OS (linux) the callback is called from
|
||||||
|
internal libwebsockets lib thread. That's why
|
||||||
|
bws_cli_mutex must be unlocked before
|
||||||
|
lws_context_destroy() call. To ensure that nobody calls
|
||||||
|
lws_context_destroy() from some parallel thread it is
|
||||||
|
protected by global websocket mutex.
|
||||||
|
*/
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
bsc_websocket_global_lock();
|
||||||
|
lws_context_destroy(conn->ctx);
|
||||||
|
bsc_websocket_global_unlock();
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
dispatch_func = conn->dispatch_func;
|
||||||
|
user_param = conn->user_param;
|
||||||
|
err_code = conn->err_code;
|
||||||
|
if (err_code != ERROR_CODE_SUCCESS) {
|
||||||
|
memcpy(err_desc, conn->err_desc, sizeof(err_desc));
|
||||||
|
}
|
||||||
|
bws_cli_free_connection(h);
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_worker() unlock mutex\n");
|
||||||
|
dispatch_func(h, BSC_WEBSOCKET_DISCONNECTED, err_code,
|
||||||
|
err_code != ERROR_CODE_SUCCESS ? err_desc : NULL, NULL, 0,
|
||||||
|
user_param);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF("bws_cli_worker() unlock mutex\n");
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_worker() going to block on lws_service() call\n");
|
||||||
|
lws_service(conn->ctx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
BSC_WEBSOCKET_RET bws_cli_connect(BSC_WEBSOCKET_PROTOCOL proto,
|
||||||
|
char *url,
|
||||||
|
uint8_t *ca_cert,
|
||||||
|
size_t ca_cert_size,
|
||||||
|
uint8_t *cert,
|
||||||
|
size_t cert_size,
|
||||||
|
uint8_t *key,
|
||||||
|
size_t key_size,
|
||||||
|
size_t timeout_s,
|
||||||
|
BSC_WEBSOCKET_CLI_DISPATCH dispatch_func,
|
||||||
|
void *dispatch_func_user_param,
|
||||||
|
BSC_WEBSOCKET_HANDLE *out_handle)
|
||||||
|
{
|
||||||
|
struct lws_context_creation_info info;
|
||||||
|
char tmp_url[BSC_WSURL_MAX_LEN];
|
||||||
|
const char *prot = NULL, *addr = NULL, *path = NULL;
|
||||||
|
int port = -1;
|
||||||
|
BSC_WEBSOCKET_HANDLE h;
|
||||||
|
struct lws_client_connect_info cinfo;
|
||||||
|
pthread_t thread_id;
|
||||||
|
size_t len;
|
||||||
|
pthread_attr_t attr;
|
||||||
|
int r;
|
||||||
|
DEBUG_PRINTF("bws_cli_connect() >>> proto = %d, url = %s\n", proto, url);
|
||||||
|
|
||||||
|
if (!ca_cert || !ca_cert_size || !cert || !cert_size || !key || !key_size ||
|
||||||
|
!url || !out_handle || !timeout_s || !dispatch_func) {
|
||||||
|
DEBUG_PRINTF("bws_cli_connect() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n");
|
||||||
|
return BSC_WEBSOCKET_BAD_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_handle = BSC_WEBSOCKET_INVALID_HANDLE;
|
||||||
|
|
||||||
|
if (proto != BSC_WEBSOCKET_HUB_PROTOCOL &&
|
||||||
|
proto != BSC_WEBSOCKET_DIRECT_PROTOCOL) {
|
||||||
|
DEBUG_PRINTF("bws_cli_connect() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n");
|
||||||
|
return BSC_WEBSOCKET_BAD_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&info, 0, sizeof(info));
|
||||||
|
memset(&cinfo, 0, sizeof(cinfo));
|
||||||
|
|
||||||
|
len = strlen(url) >= sizeof(tmp_url) ? sizeof(tmp_url) - 1 : strlen(url);
|
||||||
|
memcpy(tmp_url, url, len);
|
||||||
|
tmp_url[len] = 0;
|
||||||
|
|
||||||
|
bsc_websocket_init_log();
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
|
||||||
|
if (lws_parse_uri(tmp_url, &prot, &addr, &port, &path) != 0 ||
|
||||||
|
port == -1 || !prot || !addr || !path) {
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_connect() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n");
|
||||||
|
return BSC_WEBSOCKET_BAD_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(prot, "wss") != 0) {
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_connect() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n");
|
||||||
|
return BSC_WEBSOCKET_BAD_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
h = bws_cli_alloc_connection();
|
||||||
|
|
||||||
|
if (h == BSC_WEBSOCKET_INVALID_HANDLE) {
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_connect() <<< ret = BSC_WEBSOCKET_NO_RESOURCES\n");
|
||||||
|
return BSC_WEBSOCKET_NO_RESOURCES;
|
||||||
|
}
|
||||||
|
bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_CONNECTING;
|
||||||
|
bws_cli_conn[h].fragment_buffer = NULL;
|
||||||
|
bws_cli_conn[h].fragment_buffer_len = 0;
|
||||||
|
bws_cli_conn[h].fragment_buffer_size = 0;
|
||||||
|
bws_cli_conn[h].dispatch_func = dispatch_func;
|
||||||
|
bws_cli_conn[h].user_param = dispatch_func_user_param;
|
||||||
|
info.port = CONTEXT_PORT_NO_LISTEN;
|
||||||
|
if (proto == BSC_WEBSOCKET_HUB_PROTOCOL) {
|
||||||
|
info.protocols = bws_cli_hub_protocol;
|
||||||
|
} else {
|
||||||
|
info.protocols = bws_cli_direct_protocol;
|
||||||
|
}
|
||||||
|
info.gid = -1;
|
||||||
|
info.uid = -1;
|
||||||
|
info.client_ssl_cert_mem = cert;
|
||||||
|
info.client_ssl_cert_mem_len = cert_size;
|
||||||
|
info.client_ssl_ca_mem = ca_cert;
|
||||||
|
info.client_ssl_ca_mem_len = ca_cert_size;
|
||||||
|
info.client_ssl_key_mem = key;
|
||||||
|
info.client_ssl_key_mem_len = key_size;
|
||||||
|
info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
|
||||||
|
info.options |= LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND;
|
||||||
|
info.timeout_secs = timeout_s;
|
||||||
|
info.connect_timeout_secs = timeout_s;
|
||||||
|
|
||||||
|
/* TRICKY: check comments related to lws_context_destroy() call */
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
bsc_websocket_global_lock();
|
||||||
|
bws_cli_conn[h].ctx = lws_create_context(&info);
|
||||||
|
bsc_websocket_global_unlock();
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_connect() created ctx %p\n", bws_cli_conn[h].ctx);
|
||||||
|
|
||||||
|
if (!bws_cli_conn[h].ctx) {
|
||||||
|
bws_cli_free_connection(h);
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_connect() <<< ret = BSC_WEBSOCKET_NO_RESOURCES\n");
|
||||||
|
return BSC_WEBSOCKET_NO_RESOURCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
bws_cli_conn[h].ws = NULL;
|
||||||
|
cinfo.context = bws_cli_conn[h].ctx;
|
||||||
|
cinfo.address = addr;
|
||||||
|
cinfo.origin = cinfo.address;
|
||||||
|
cinfo.host = cinfo.address;
|
||||||
|
cinfo.port = port;
|
||||||
|
cinfo.path = path;
|
||||||
|
cinfo.pwsi = &bws_cli_conn[h].ws;
|
||||||
|
cinfo.alpn = "h2;http/1.1";
|
||||||
|
bws_retry.secs_since_valid_ping = 3;
|
||||||
|
bws_retry.secs_since_valid_hangup = 10;
|
||||||
|
cinfo.retry_and_idle_policy = &bws_retry;
|
||||||
|
cinfo.ssl_connection = LCCSCF_USE_SSL |
|
||||||
|
LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK | LCCSCF_ALLOW_SELFSIGNED;
|
||||||
|
|
||||||
|
if (proto == BSC_WEBSOCKET_HUB_PROTOCOL) {
|
||||||
|
cinfo.protocol = bws_hub_protocol;
|
||||||
|
} else {
|
||||||
|
cinfo.protocol = bws_direct_protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_SUCCESS;
|
||||||
|
*out_handle = h;
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
bsc_websocket_global_lock();
|
||||||
|
lws_client_connect_via_info(&cinfo);
|
||||||
|
bsc_websocket_global_unlock();
|
||||||
|
r = pthread_attr_init(&attr);
|
||||||
|
|
||||||
|
if(!r) {
|
||||||
|
r = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!r) {
|
||||||
|
r = pthread_create(&thread_id, &attr, &bws_cli_worker, &bws_cli_conn[h]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(r) {
|
||||||
|
/* TRICKY: Libwebsockets API is not designed to be used from
|
||||||
|
multipe service threads, as a result lws_context_destroy()
|
||||||
|
is not thread safe. More over, on different platforms the
|
||||||
|
function behaves in different ways. Call of
|
||||||
|
lws_context_destroy() leads to several calls of
|
||||||
|
bws_cli_websocket_event() callback (LWS_CALLBACK_CLOSED,
|
||||||
|
etc..). But under some OS (MacOSx) that callback is
|
||||||
|
called from context of the bws_cli_worker() thread and
|
||||||
|
under some other OS (linux) the callback is called from
|
||||||
|
internal libwebsockets lib thread. That's why
|
||||||
|
bws_cli_mutex must be unlocked before
|
||||||
|
lws_context_destroy() call. To ensure that nobody calls
|
||||||
|
lws_context_destroy() from some parallel thread it is
|
||||||
|
protected by global websocket mutex.
|
||||||
|
*/
|
||||||
|
bsc_websocket_global_lock();
|
||||||
|
lws_context_destroy(bws_cli_conn[h].ctx);
|
||||||
|
bsc_websocket_global_unlock();
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
bws_cli_free_connection(h);
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_connect() <<< ret = BSC_WEBSOCKET_NO_RESOURCES\n");
|
||||||
|
return BSC_WEBSOCKET_NO_RESOURCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_attr_destroy(&attr);
|
||||||
|
DEBUG_PRINTF("bws_cli_connect() <<< ret = %d\n", BSC_WEBSOCKET_SUCCESS);
|
||||||
|
return BSC_WEBSOCKET_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bws_cli_disconnect(BSC_WEBSOCKET_HANDLE h)
|
||||||
|
{
|
||||||
|
DEBUG_PRINTF("bws_cli_disconnect() >>> h = %d\n", h);
|
||||||
|
|
||||||
|
if (h >= 0 && h < BSC_CLIENT_WEBSOCKETS_MAX_NUM) {
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_disconnect() state = %d\n", bws_cli_conn[h].state);
|
||||||
|
if (bws_cli_conn[h].state == BSC_WEBSOCKET_STATE_CONNECTING ||
|
||||||
|
bws_cli_conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED) {
|
||||||
|
/* tell worker to process change of connection state */
|
||||||
|
bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING;
|
||||||
|
lws_cancel_service(bws_cli_conn[h].ctx);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bws_cli_disconnect() <<<\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void bws_cli_send(BSC_WEBSOCKET_HANDLE h)
|
||||||
|
{
|
||||||
|
DEBUG_PRINTF("bws_cli_send() >>> h = %d\n", h);
|
||||||
|
|
||||||
|
if (h >= 0 && h < BSC_CLIENT_WEBSOCKETS_MAX_NUM) {
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
|
||||||
|
if (bws_cli_conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED) {
|
||||||
|
/* tell worker to process send request */
|
||||||
|
bws_cli_conn[h].want_send_data = true;
|
||||||
|
DEBUG_PRINTF("bws_cli_send() cs = 1\n");
|
||||||
|
lws_cancel_service(bws_cli_conn[h].ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bws_cli_send() <<<\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
BSC_WEBSOCKET_RET bws_cli_dispatch_send(
|
||||||
|
BSC_WEBSOCKET_HANDLE h, uint8_t *payload, size_t payload_size)
|
||||||
|
{
|
||||||
|
int written;
|
||||||
|
BSC_WEBSOCKET_RET ret;
|
||||||
|
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_dispatch_send() >>> h = %d, payload = %p, payload_size = %d\n",
|
||||||
|
h, payload, payload_size);
|
||||||
|
|
||||||
|
if (h < 0 || h >= BSC_CLIENT_WEBSOCKETS_MAX_NUM) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_dispatch_send() <<< ret = BACNET_WEBSOCKET_BAD_PARAM\n");
|
||||||
|
return BSC_WEBSOCKET_BAD_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
|
||||||
|
if ((bws_cli_conn[h].state != BSC_WEBSOCKET_STATE_CONNECTED) ||
|
||||||
|
!bws_cli_conn[h].want_send_data || !bws_cli_conn[h].can_send_data) {
|
||||||
|
DEBUG_PRINTF("bws_cli_dispatch_send() state = %d, ws = %d, cs = %d\n",
|
||||||
|
bws_cli_conn[h].state, bws_cli_conn[h].want_send_data,
|
||||||
|
bws_cli_conn[h].can_send_data);
|
||||||
|
DEBUG_PRINTF("bws_cli_dispatch_send() <<< ret = "
|
||||||
|
"BSC_WEBSOCKET_INVALID_OPERATION\n");
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
return BSC_WEBSOCKET_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
written =
|
||||||
|
lws_write(bws_cli_conn[h].ws, payload, payload_size, LWS_WRITE_BINARY);
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bws_cli_dispatch_send() %d bytes is sent\n", written);
|
||||||
|
|
||||||
|
if (written < (int)payload_size) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_dispatch_send() websocket connection is broken(closed)\n");
|
||||||
|
/* tell worker to process change of connection state */
|
||||||
|
bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING;
|
||||||
|
lws_cancel_service(bws_cli_conn[h].ctx);
|
||||||
|
ret = BSC_WEBSOCKET_INVALID_OPERATION;
|
||||||
|
} else {
|
||||||
|
ret = BSC_WEBSOCKET_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_dispatch_send() <<< ret = %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Implementation of global websocket functions.
|
||||||
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||||
|
* @date May 2022
|
||||||
|
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||||
|
*/
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <libwebsockets.h>
|
||||||
|
#include "websocket-global.h"
|
||||||
|
#include "bacnet/basic/sys/debug.h"
|
||||||
|
|
||||||
|
static pthread_mutex_t websocket_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
|
||||||
|
static pthread_mutex_t websocket_dispatch_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
#if (BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED != 1)
|
||||||
|
|
||||||
|
void bsc_websocket_global_lock(void)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&websocket_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsc_websocket_global_unlock(void)
|
||||||
|
{
|
||||||
|
pthread_mutex_unlock(&websocket_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bws_dispatch_lock(void)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&websocket_dispatch_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bws_dispatch_unlock(void)
|
||||||
|
{
|
||||||
|
pthread_mutex_unlock(&websocket_dispatch_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
static int volatile websocket_mutex_cnt = 0;
|
||||||
|
static int volatile websocket_dispatch_mutex_cnt = 0;
|
||||||
|
|
||||||
|
void bsc_websocket_global_lock_dbg(char *f, int line)
|
||||||
|
{
|
||||||
|
printf("bsc_websocket_global_lock_dbg() >>> %s:%d lock_cnt %d tid = %ld\n", f, line, websocket_mutex_cnt, pthread_self());
|
||||||
|
websocket_mutex_cnt++;
|
||||||
|
fflush(stdout);
|
||||||
|
pthread_mutex_lock(&websocket_mutex);
|
||||||
|
printf("bsc_websocket_global_lock_dbg() <<< lock_cnt %d tid = %ld\n", websocket_mutex_cnt, pthread_self());
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsc_websocket_global_unlock_dbg(char *f, int line)
|
||||||
|
{
|
||||||
|
printf("bsc_websocket_global_unlock_dbg() >>> %s:%d lock_cnt %d tid = %ld\n", f, line, websocket_mutex_cnt, pthread_self());
|
||||||
|
websocket_mutex_cnt--;
|
||||||
|
fflush(stdout);
|
||||||
|
pthread_mutex_unlock(&websocket_mutex);
|
||||||
|
printf("bsc_websocket_global_unlock_dbg() <<< lock_cnt %d tid = %ld\n", websocket_mutex_cnt, pthread_self());
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bws_dispatch_lock_dbg(char *f, int line)
|
||||||
|
{
|
||||||
|
printf("bws_dispatch_lock_dbg() >>> %s:%d lock_cnt %d tid = %ld\n", f, line, websocket_dispatch_mutex_cnt, pthread_self());
|
||||||
|
websocket_dispatch_mutex_cnt++;
|
||||||
|
fflush(stdout);
|
||||||
|
pthread_mutex_lock(&websocket_dispatch_mutex);
|
||||||
|
printf("bws_dispatch_lock_dbg() <<< lock_cnt %d tid = %ld\n", websocket_dispatch_mutex_cnt, pthread_self());
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bws_dispatch_unlock_dbg(char *f, int line)
|
||||||
|
{
|
||||||
|
printf("bws_dispatch_unlock_dbg() >>> %s:%d lock_cnt %d tid = %ld\n", f, line, websocket_dispatch_mutex_cnt, pthread_self());
|
||||||
|
websocket_dispatch_mutex_cnt--;
|
||||||
|
fflush(stdout);
|
||||||
|
pthread_mutex_unlock(&websocket_dispatch_mutex);
|
||||||
|
printf("bws_dispatch_unlock_dbg() <<< lock_cnt %d tid = %ld\n", websocket_dispatch_mutex_cnt, pthread_self());
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static bool bsc_websocket_log_initialized = false;
|
||||||
|
|
||||||
|
void bsc_websocket_init_log(void)
|
||||||
|
{
|
||||||
|
bsc_websocket_global_lock();
|
||||||
|
if(!bsc_websocket_log_initialized) {
|
||||||
|
bsc_websocket_log_initialized = true;
|
||||||
|
#if DEBUG_LIBWEBSOCKETS_ENABLED == 1
|
||||||
|
printf("LWS_MAX_SMP = %d", LWS_MAX_SMP);
|
||||||
|
lws_set_log_level(LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_INFO | LLL_DEBUG |
|
||||||
|
LLL_PARSER | LLL_HEADER | LLL_EXT | LLL_CLIENT | LLL_LATENCY |
|
||||||
|
LLL_USER | LLL_THREAD,
|
||||||
|
NULL);
|
||||||
|
#else
|
||||||
|
lws_set_log_level(0, NULL);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
bsc_websocket_global_unlock();
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Global websocket functions
|
||||||
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||||
|
* @date May 2022
|
||||||
|
* @copyright SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
#ifndef __BSC_WEBSOCKET_MUTEX_INCLUDED__
|
||||||
|
#define __BSC_WEBSOCKET_MUTEX_INCLUDED__
|
||||||
|
|
||||||
|
#ifndef BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED
|
||||||
|
#define BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED != 1)
|
||||||
|
void bsc_websocket_global_lock(void);
|
||||||
|
void bsc_websocket_global_unlock(void);
|
||||||
|
#else
|
||||||
|
void bsc_websocket_global_lock_dbg(char *f, int line);
|
||||||
|
void bsc_websocket_global_unlock_dbg(char *f, int line);
|
||||||
|
#define bsc_websocket_global_lock() bsc_websocket_global_lock_dbg(__FILE__, __LINE__);
|
||||||
|
#define bsc_websocket_global_unlock() bsc_websocket_global_unlock_dbg(__FILE__, __LINE__);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void bsc_websocket_init_log(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,999 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Implementation of server websocket interface.
|
||||||
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||||
|
* @date June 2022
|
||||||
|
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||||
|
*/
|
||||||
|
#include <libwebsockets.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include "bacnet/datalink/bsc/websocket.h"
|
||||||
|
#include "bacnet/basic/sys/debug.h"
|
||||||
|
#include "websocket-global.h"
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#define DEBUG_WEBSOCKET_SERVER 0
|
||||||
|
|
||||||
|
#if DEBUG_WEBSOCKET_SERVER == 1
|
||||||
|
#define DEBUG_PRINTF debug_printf
|
||||||
|
#else
|
||||||
|
#undef DEBUG_ENABLED
|
||||||
|
#define DEBUG_PRINTF(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BSC_RX_BUFFER_LEN BSC_WEBSOCKET_RX_BUFFER_LEN
|
||||||
|
|
||||||
|
#ifndef LWS_PROTOCOL_LIST_TERM
|
||||||
|
#define LWS_PROTOCOL_LIST_TERM \
|
||||||
|
{ \
|
||||||
|
NULL, NULL, 0, 0, 0, NULL, 0 \
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BSC_WEBSOCKET_STATE_IDLE = 0,
|
||||||
|
BSC_WEBSOCKET_STATE_CONNECTED = 1,
|
||||||
|
BSC_WEBSOCKET_STATE_DISCONNECTING = 2
|
||||||
|
} BSC_WEBSOCKET_STATE;
|
||||||
|
|
||||||
|
/* Some forward function declarations */
|
||||||
|
|
||||||
|
static int bws_srv_websocket_event(struct lws *wsi,
|
||||||
|
enum lws_callback_reasons reason,
|
||||||
|
void *user,
|
||||||
|
void *in,
|
||||||
|
size_t len);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
struct lws_context *ctx;
|
||||||
|
struct lws *ws;
|
||||||
|
BSC_WEBSOCKET_STATE state;
|
||||||
|
bool want_send_data;
|
||||||
|
bool can_send_data;
|
||||||
|
uint8_t *fragment_buffer;
|
||||||
|
size_t fragment_buffer_size;
|
||||||
|
size_t fragment_buffer_len;
|
||||||
|
BACNET_ERROR_CODE err_code;
|
||||||
|
} BSC_WEBSOCKET_CONNECTION;
|
||||||
|
|
||||||
|
#if BSC_CONF_WEBSOCKET_SERVERS_NUM < 1
|
||||||
|
#error "BSC_CONF_WEBSOCKET_SERVERS_NUM must be >= 1"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static pthread_mutex_t bws_global_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
|
||||||
|
static pthread_mutex_t bws_srv_direct_mutex[BSC_CONF_WEBSOCKET_SERVERS_NUM];
|
||||||
|
static pthread_mutex_t bws_srv_hub_mutex[BSC_CONF_WEBSOCKET_SERVERS_NUM];
|
||||||
|
|
||||||
|
#if BSC_SERVER_HUB_WEBSOCKETS_MAX_NUM > 0
|
||||||
|
static BSC_WEBSOCKET_CONNECTION
|
||||||
|
bws_hub_conn[BSC_CONF_WEBSOCKET_SERVERS_NUM]
|
||||||
|
[BSC_SERVER_HUB_WEBSOCKETS_MAX_NUM] = { 0 };
|
||||||
|
#else
|
||||||
|
static BSC_WEBSOCKET_CONNECTION bws_hub_conn[1][1] = { 0 };
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if BSC_SERVER_DIRECT_WEBSOCKETS_MAX_NUM > 0
|
||||||
|
static BSC_WEBSOCKET_CONNECTION
|
||||||
|
bws_direct_conn[BSC_CONF_WEBSOCKET_SERVERS_NUM]
|
||||||
|
[BSC_SERVER_DIRECT_WEBSOCKETS_MAX_NUM] = { 0 };
|
||||||
|
#else
|
||||||
|
static BSC_WEBSOCKET_CONNECTION bws_direct_conn[1][1] = { 0 };
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct BACNetWebsocketServerContext {
|
||||||
|
bool used;
|
||||||
|
struct lws_context *wsctx;
|
||||||
|
BSC_WEBSOCKET_PROTOCOL proto;
|
||||||
|
BSC_WEBSOCKET_CONNECTION *conn;
|
||||||
|
pthread_mutex_t *mutex;
|
||||||
|
BSC_WEBSOCKET_SRV_DISPATCH dispatch_func;
|
||||||
|
void *user_param;
|
||||||
|
bool stop_worker;
|
||||||
|
} BSC_WEBSOCKET_CONTEXT;
|
||||||
|
|
||||||
|
static BSC_WEBSOCKET_CONTEXT bws_hub_ctx[BSC_CONF_WEBSOCKET_SERVERS_NUM] = {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
static BSC_WEBSOCKET_CONTEXT bws_direct_ctx[BSC_CONF_WEBSOCKET_SERVERS_NUM] = {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool bws_mutex_init(pthread_mutex_t *mutex)
|
||||||
|
{
|
||||||
|
pthread_mutexattr_t attr;
|
||||||
|
|
||||||
|
if (pthread_mutexattr_init(&attr) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) {
|
||||||
|
pthread_mutexattr_destroy(&attr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_mutex_init(mutex, &attr) != 0) {
|
||||||
|
pthread_mutexattr_destroy(&attr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutexattr_destroy(&attr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BSC_WEBSOCKET_CONTEXT *bws_alloc_server_ctx(BSC_WEBSOCKET_PROTOCOL proto)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
BSC_WEBSOCKET_CONTEXT *ctx = (proto == BSC_WEBSOCKET_HUB_PROTOCOL)
|
||||||
|
? &bws_hub_ctx[0]
|
||||||
|
: &bws_direct_ctx[0];
|
||||||
|
|
||||||
|
pthread_mutex_lock(&bws_global_mutex);
|
||||||
|
DEBUG_PRINTF("bws_alloc_server_ctx() >>> proto = %d\n", proto);
|
||||||
|
|
||||||
|
for (i = 0; i < BSC_CONF_WEBSOCKET_SERVERS_NUM; i++) {
|
||||||
|
if (!ctx[i].used) {
|
||||||
|
if (proto == BSC_WEBSOCKET_HUB_PROTOCOL) {
|
||||||
|
ctx[i].mutex = &bws_srv_hub_mutex[i];
|
||||||
|
ctx[i].conn = &bws_hub_conn[i][0];
|
||||||
|
} else {
|
||||||
|
ctx[i].mutex = &bws_srv_direct_mutex[i];
|
||||||
|
ctx[i].conn = &bws_direct_conn[i][0];
|
||||||
|
}
|
||||||
|
if (!bws_mutex_init(ctx[i].mutex)) {
|
||||||
|
DEBUG_PRINTF("bws_alloc_server_ctx() <<< ret = %p\n", &ctx[i]);
|
||||||
|
pthread_mutex_unlock(&bws_global_mutex);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ctx[i].used = true;
|
||||||
|
DEBUG_PRINTF("bws_alloc_server_ctx() <<< ret = %p\n", &ctx[i]);
|
||||||
|
pthread_mutex_unlock(&bws_global_mutex);
|
||||||
|
return &ctx[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF("bws_alloc_server_ctx() <<< ret = %p\n", &ctx[i]);
|
||||||
|
pthread_mutex_unlock(&bws_global_mutex);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bws_set_disconnect_reason(
|
||||||
|
BSC_WEBSOCKET_CONTEXT *ctx, BSC_WEBSOCKET_HANDLE h, uint16_t err_code)
|
||||||
|
{
|
||||||
|
switch (err_code) {
|
||||||
|
case LWS_CLOSE_STATUS_NORMAL: {
|
||||||
|
ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_CLOSED_BY_PEER;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_GOINGAWAY: {
|
||||||
|
ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_ENDPOINT_LEAVES;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_PROTOCOL_ERR: {
|
||||||
|
ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_PROTOCOL_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE: {
|
||||||
|
ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_NOT_ACCEPTED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_NO_STATUS:
|
||||||
|
case LWS_CLOSE_STATUS_RESERVED: {
|
||||||
|
ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_ABNORMAL_CLOSE: {
|
||||||
|
ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_NOT_ACCEPTED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_INVALID_PAYLOAD: {
|
||||||
|
ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_INCONSISTENT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_POLICY_VIOLATION: {
|
||||||
|
ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_AGAINST_POLICY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE: {
|
||||||
|
ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_FRAME_TOO_LONG;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_EXTENSION_REQUIRED: {
|
||||||
|
ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_EXTENSION_MISSING;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_UNEXPECTED_CONDITION: {
|
||||||
|
ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_REQUEST_UNAVAILABLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bws_free_server_ctx(BSC_WEBSOCKET_CONTEXT *ctx)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&bws_global_mutex);
|
||||||
|
DEBUG_PRINTF("bws_free_server_ctx() >>> ctx = %p\n", ctx);
|
||||||
|
ctx->used = false;
|
||||||
|
ctx->wsctx = NULL;
|
||||||
|
ctx->conn = NULL;
|
||||||
|
pthread_mutex_destroy(ctx->mutex);
|
||||||
|
ctx->mutex = NULL;
|
||||||
|
ctx->dispatch_func = NULL;
|
||||||
|
ctx->user_param = NULL;
|
||||||
|
DEBUG_PRINTF("bws_free_server_ctx() <<< \n");
|
||||||
|
pthread_mutex_unlock(&bws_global_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG_ENABLED == 1
|
||||||
|
static bool bws_validate_ctx_pointer(BSC_WEBSOCKET_CONTEXT *ctx)
|
||||||
|
{
|
||||||
|
bool is_validated = false;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&bws_global_mutex);
|
||||||
|
|
||||||
|
for (i = 0; i < BSC_CONF_WEBSOCKET_SERVERS_NUM; i++) {
|
||||||
|
if (ctx == &bws_hub_ctx[i]) {
|
||||||
|
is_validated = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_validated) {
|
||||||
|
for (i = 0; i < BSC_CONF_WEBSOCKET_SERVERS_NUM; i++) {
|
||||||
|
if (ctx == &bws_direct_ctx[i]) {
|
||||||
|
is_validated = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&bws_global_mutex);
|
||||||
|
return is_validated;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int bws_srv_get_max_sockets(BSC_WEBSOCKET_PROTOCOL proto)
|
||||||
|
{
|
||||||
|
int max = 0;
|
||||||
|
if (proto == BSC_WEBSOCKET_HUB_PROTOCOL) {
|
||||||
|
max = BSC_SERVER_HUB_WEBSOCKETS_MAX_NUM;
|
||||||
|
} else if (proto == BSC_WEBSOCKET_DIRECT_PROTOCOL) {
|
||||||
|
max = BSC_SERVER_DIRECT_WEBSOCKETS_MAX_NUM;
|
||||||
|
}
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BSC_WEBSOCKET_HANDLE bws_srv_alloc_connection(BSC_WEBSOCKET_CONTEXT *ctx)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bws_srv_alloc_connection() >>> ctx = %p\n", ctx);
|
||||||
|
|
||||||
|
for (i = 0; i < bws_srv_get_max_sockets(ctx->proto); i++) {
|
||||||
|
if (ctx->conn[i].state == BSC_WEBSOCKET_STATE_IDLE) {
|
||||||
|
memset(&ctx->conn[i], 0, sizeof(ctx->conn[i]));
|
||||||
|
DEBUG_PRINTF("bws_srv_alloc_connection() <<< ret = %d\n", i);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bws_srv_alloc_connection() <<< ret = "
|
||||||
|
"BSC_WEBSOCKET_INVALID_HANDLE\n");
|
||||||
|
return BSC_WEBSOCKET_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bws_srv_free_connection(
|
||||||
|
BSC_WEBSOCKET_CONTEXT *ctx, BSC_WEBSOCKET_HANDLE h)
|
||||||
|
{
|
||||||
|
DEBUG_PRINTF("bws_srv_free_connection() >>> ctx = %p, h = %d\n", ctx, h);
|
||||||
|
|
||||||
|
if (h >= 0 && h < bws_srv_get_max_sockets(ctx->proto)) {
|
||||||
|
if (ctx->conn[h].state != BSC_WEBSOCKET_STATE_IDLE) {
|
||||||
|
if (ctx->conn[h].fragment_buffer) {
|
||||||
|
free(ctx->conn[h].fragment_buffer);
|
||||||
|
ctx->conn[h].fragment_buffer = NULL;
|
||||||
|
ctx->conn[h].fragment_buffer_len = 0;
|
||||||
|
ctx->conn[h].fragment_buffer_size = 0;
|
||||||
|
}
|
||||||
|
ctx->conn[h].state = BSC_WEBSOCKET_STATE_IDLE;
|
||||||
|
ctx->conn[h].ws = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF("bws_srv_free_connection() <<<\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static BSC_WEBSOCKET_HANDLE bws_find_connnection(
|
||||||
|
BSC_WEBSOCKET_CONTEXT *ctx, struct lws *ws)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < bws_srv_get_max_sockets(ctx->proto); i++) {
|
||||||
|
if (ctx->conn[i].ws == ws &&
|
||||||
|
ctx->conn[i].state != BSC_WEBSOCKET_STATE_IDLE) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return BSC_WEBSOCKET_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bws_srv_websocket_event(struct lws *wsi,
|
||||||
|
enum lws_callback_reasons reason,
|
||||||
|
void *user,
|
||||||
|
void *in,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
BSC_WEBSOCKET_HANDLE h;
|
||||||
|
BSC_WEBSOCKET_CONTEXT *ctx =
|
||||||
|
(BSC_WEBSOCKET_CONTEXT *)lws_context_user(lws_get_context(wsi));
|
||||||
|
int ret = 0;
|
||||||
|
BSC_WEBSOCKET_SRV_DISPATCH dispatch_func;
|
||||||
|
void *user_param;
|
||||||
|
bool stop_worker;
|
||||||
|
uint8_t err_code[2];
|
||||||
|
uint16_t err;
|
||||||
|
(void)user;
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bws_srv_websocket_event() >>> ctx = %p, user_param = %p, "
|
||||||
|
"proto = %d, wsi = %p, "
|
||||||
|
"reason = %d, in = %p, len = %d\n",
|
||||||
|
ctx, ctx->user_param, ctx->proto, wsi, reason, in, len);
|
||||||
|
|
||||||
|
switch (reason) {
|
||||||
|
case LWS_CALLBACK_ESTABLISHED: {
|
||||||
|
pthread_mutex_lock(ctx->mutex);
|
||||||
|
DEBUG_PRINTF("bws_srv_websocket_event() established connection\n");
|
||||||
|
h = bws_srv_alloc_connection(ctx);
|
||||||
|
if (h == BSC_WEBSOCKET_INVALID_HANDLE) {
|
||||||
|
DEBUG_PRINTF("bws_srv_websocket_event() no free sockets, "
|
||||||
|
"dropping incoming connection\n");
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_srv_websocket_event() ctx %p proto %d set state of"
|
||||||
|
" socket %d to BACNET_WEBSOCKET_STATE_CONNECTING\n",
|
||||||
|
ctx, ctx->proto, h);
|
||||||
|
ctx->conn[h].ws = wsi;
|
||||||
|
ctx->conn[h].state = BSC_WEBSOCKET_STATE_CONNECTED;
|
||||||
|
ctx->conn[h].err_code = ERROR_CODE_SUCCESS;
|
||||||
|
dispatch_func = ctx->dispatch_func;
|
||||||
|
user_param = ctx->user_param;
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
dispatch_func((BSC_WEBSOCKET_SRV_HANDLE)ctx, h,
|
||||||
|
BSC_WEBSOCKET_CONNECTED, 0, NULL, NULL, 0, user_param);
|
||||||
|
/* wakeup worker to process pending event */
|
||||||
|
lws_cancel_service(ctx->wsctx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CALLBACK_CLOSED: {
|
||||||
|
DEBUG_PRINTF("bws_srv_websocket_event() closed connection\n");
|
||||||
|
pthread_mutex_lock(ctx->mutex);
|
||||||
|
h = bws_find_connnection(ctx, wsi);
|
||||||
|
if (h == BSC_WEBSOCKET_INVALID_HANDLE) {
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
} else {
|
||||||
|
DEBUG_PRINTF("bws_srv_websocket_event() ctx %p user_param = %p "
|
||||||
|
"proto %d state of "
|
||||||
|
"socket %d is %d\n",
|
||||||
|
ctx, ctx->user_param, ctx->proto, h, ctx->conn[h].state);
|
||||||
|
dispatch_func = ctx->dispatch_func;
|
||||||
|
user_param = ctx->user_param;
|
||||||
|
stop_worker = ctx->stop_worker;
|
||||||
|
err = ctx->conn[h].err_code;
|
||||||
|
bws_srv_free_connection(ctx, h);
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
if (!stop_worker) {
|
||||||
|
dispatch_func((BSC_WEBSOCKET_SRV_HANDLE)ctx, h,
|
||||||
|
BSC_WEBSOCKET_DISCONNECTED, err, NULL, NULL, 0,
|
||||||
|
user_param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: {
|
||||||
|
pthread_mutex_lock(ctx->mutex);
|
||||||
|
h = bws_find_connnection(ctx, wsi);
|
||||||
|
if (h != BSC_WEBSOCKET_INVALID_HANDLE && len >= 2) {
|
||||||
|
err_code[0] = ((uint8_t *)in)[1];
|
||||||
|
err_code[1] = ((uint8_t *)in)[0];
|
||||||
|
bws_set_disconnect_reason(ctx, h, *((uint16_t *)&err_code));
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CALLBACK_RECEIVE: {
|
||||||
|
pthread_mutex_lock(ctx->mutex);
|
||||||
|
h = bws_find_connnection(ctx, wsi);
|
||||||
|
if (h == BSC_WEBSOCKET_INVALID_HANDLE) {
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
} else {
|
||||||
|
DEBUG_PRINTF("bws_srv_websocket_event() ctx %p proto %d "
|
||||||
|
"received %d bytes of "
|
||||||
|
"data for websocket %d\n",
|
||||||
|
ctx, ctx->proto, len, h);
|
||||||
|
if (!lws_frame_is_binary(wsi)) {
|
||||||
|
/* According AB.7.5.3 BACnet/SC BVLC Message Exchange,
|
||||||
|
if a received data frame is not binary,
|
||||||
|
the WebSocket connection shall be closed with a
|
||||||
|
status code of 1003 -WEBSOCKET_DATA_NOT_ACCEPTED.
|
||||||
|
*/
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_srv_websocket_event() ctx %p proto %d got "
|
||||||
|
"non-binary frame, "
|
||||||
|
"close websocket %d\n",
|
||||||
|
ctx, ctx->proto, h);
|
||||||
|
lws_close_reason(
|
||||||
|
wsi, LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE, NULL, 0);
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
DEBUG_PRINTF("bws_srv_websocket_event() <<< ret = -1\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (ctx->conn[h].state != BSC_WEBSOCKET_STATE_CONNECTED) {
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
} else {
|
||||||
|
if (!ctx->conn[h].fragment_buffer) {
|
||||||
|
ctx->conn[h].fragment_buffer =
|
||||||
|
malloc(BSC_RX_BUFFER_LEN);
|
||||||
|
if (!ctx->conn[h].fragment_buffer) {
|
||||||
|
lws_close_reason(wsi,
|
||||||
|
LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE, NULL, 0);
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
DEBUG_PRINTF("bws_srv_websocket_event() <<< ret = "
|
||||||
|
"-1, allocation of %d bytes failed\n",
|
||||||
|
len <= BSC_RX_BUFFER_LEN ? BSC_RX_BUFFER_LEN
|
||||||
|
: len);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ctx->conn[h].fragment_buffer_len = 0;
|
||||||
|
ctx->conn[h].fragment_buffer_size = BSC_RX_BUFFER_LEN;
|
||||||
|
}
|
||||||
|
if (ctx->conn[h].fragment_buffer_len + len >
|
||||||
|
ctx->conn[h].fragment_buffer_size) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_srv_websocket_event() realloc buf of %d bytes"
|
||||||
|
"for socket %d to %d bytes\n",
|
||||||
|
ctx->conn[h].fragment_buffer_len, h,
|
||||||
|
ctx->conn[h].fragment_buffer_len + len);
|
||||||
|
|
||||||
|
ctx->conn[h].fragment_buffer =
|
||||||
|
realloc(ctx->conn[h].fragment_buffer,
|
||||||
|
ctx->conn[h].fragment_buffer_len + len);
|
||||||
|
if (!ctx->conn[h].fragment_buffer) {
|
||||||
|
lws_close_reason(wsi,
|
||||||
|
LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE, NULL, 0);
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_srv_websocket_event() <<< ret = -1, "
|
||||||
|
"re-allocation of %d bytes failed\n",
|
||||||
|
ctx->conn[h].fragment_buffer_len + len);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ctx->conn[h].fragment_buffer_size =
|
||||||
|
ctx->conn[h].fragment_buffer_len + len;
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_srv_websocket_event() got next %d bytes for "
|
||||||
|
"socket %d total_len %d\n",
|
||||||
|
len, h, ctx->conn[h].fragment_buffer_len);
|
||||||
|
memcpy(&ctx->conn[h].fragment_buffer[
|
||||||
|
ctx->conn[h].fragment_buffer_len],
|
||||||
|
in, len);
|
||||||
|
ctx->conn[h].fragment_buffer_len += len;
|
||||||
|
if (lws_is_final_fragment(wsi) && !ctx->stop_worker) {
|
||||||
|
dispatch_func = ctx->dispatch_func;
|
||||||
|
user_param = ctx->user_param;
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
dispatch_func((BSC_WEBSOCKET_SRV_HANDLE)ctx, h,
|
||||||
|
BSC_WEBSOCKET_RECEIVED, 0, NULL,
|
||||||
|
ctx->conn[h].fragment_buffer,
|
||||||
|
ctx->conn[h].fragment_buffer_len, user_param);
|
||||||
|
pthread_mutex_lock(ctx->mutex);
|
||||||
|
ctx->conn[h].fragment_buffer_len = 0;
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
} else {
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CALLBACK_SERVER_WRITEABLE: {
|
||||||
|
pthread_mutex_lock(ctx->mutex);
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_srv_websocket_event() ctx %p proto %d can write\n", ctx,
|
||||||
|
ctx->proto);
|
||||||
|
h = bws_find_connnection(ctx, wsi);
|
||||||
|
if (h == BSC_WEBSOCKET_INVALID_HANDLE) {
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
} else {
|
||||||
|
DEBUG_PRINTF("bws_srv_websocket_event() ctx %p proto %d socket "
|
||||||
|
"%d state = %d\n",
|
||||||
|
ctx, ctx->proto, h, ctx->conn[h].state);
|
||||||
|
|
||||||
|
if (ctx->conn[h].state == BSC_WEBSOCKET_STATE_DISCONNECTING) {
|
||||||
|
/* if -1 returned from this callback libwebsocket closes
|
||||||
|
+ websocket related to wsi */
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ctx->conn[h].state != BSC_WEBSOCKET_STATE_IDLE) &&
|
||||||
|
!ctx->stop_worker && ctx->conn[h].want_send_data) {
|
||||||
|
ctx->conn[h].can_send_data = true;
|
||||||
|
dispatch_func = ctx->dispatch_func;
|
||||||
|
user_param = ctx->user_param;
|
||||||
|
stop_worker = ctx->stop_worker;
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
if (!stop_worker) {
|
||||||
|
dispatch_func((BSC_WEBSOCKET_SRV_HANDLE)ctx, h,
|
||||||
|
BSC_WEBSOCKET_SENDABLE, 0, NULL, NULL, 0,
|
||||||
|
user_param);
|
||||||
|
}
|
||||||
|
pthread_mutex_lock(ctx->mutex);
|
||||||
|
ctx->conn[h].want_send_data = false;
|
||||||
|
ctx->conn[h].can_send_data = false;
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
/* wakeup worker to process internal state */
|
||||||
|
lws_cancel_service(ctx->wsctx);
|
||||||
|
} else {
|
||||||
|
ctx->conn[h].want_send_data = false;
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF("bws_srv_websocket_event() <<< ret = %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *bws_srv_worker(void *arg)
|
||||||
|
{
|
||||||
|
BSC_WEBSOCKET_CONTEXT *ctx = (BSC_WEBSOCKET_CONTEXT *)arg;
|
||||||
|
int i;
|
||||||
|
BSC_WEBSOCKET_SRV_DISPATCH dispatch_func;
|
||||||
|
void *user_param;
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bws_srv_worker() started for ctx %p proto %d user_param %p\n",
|
||||||
|
ctx, ctx->proto, ctx->user_param);
|
||||||
|
|
||||||
|
pthread_mutex_lock(ctx->mutex);
|
||||||
|
dispatch_func = ctx->dispatch_func;
|
||||||
|
user_param = ctx->user_param;
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
|
||||||
|
dispatch_func((BSC_WEBSOCKET_SRV_HANDLE)ctx, 0,
|
||||||
|
BSC_WEBSOCKET_SERVER_STARTED, 0, NULL, NULL, 0, user_param);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
DEBUG_PRINTF("bws_srv_worker() ctx %p proto %d blocked user_param %p\n",
|
||||||
|
ctx, ctx->proto, ctx->user_param);
|
||||||
|
pthread_mutex_lock(ctx->mutex);
|
||||||
|
|
||||||
|
if (ctx->stop_worker) {
|
||||||
|
DEBUG_PRINTF("bws_srv_worker() ctx %p user_param %p proto %d going "
|
||||||
|
"to stop\n",
|
||||||
|
ctx, ctx->user_param, ctx->proto);
|
||||||
|
DEBUG_PRINTF("bws_srv_worker() destroy wsctx %p, ctx = %p, "
|
||||||
|
"user_param = %p\n",
|
||||||
|
ctx->wsctx, ctx, ctx->user_param);
|
||||||
|
/* TRICKY: Libwebsockets API is not designed to be used from
|
||||||
|
multipe service threads, as a result lws_context_destroy()
|
||||||
|
is not thread safe. More over, on different platforms the
|
||||||
|
function behaves in different ways. Call of
|
||||||
|
lws_context_destroy() leads to several calls of
|
||||||
|
bws_srv_websocket_event() callback (LWS_CALLBACK_CLOSED,
|
||||||
|
etc..). But under some OS (MacOSx) that callback is
|
||||||
|
called from context of the bws_cli_worker() thread and
|
||||||
|
under some other OS (linux) the callback is called from
|
||||||
|
internal libwebsockets lib thread. That's why
|
||||||
|
bws_cli_mutex must be unlocked before
|
||||||
|
lws_context_destroy() call. To ensure that nobody calls
|
||||||
|
lws_context_destroy() from some parallel thread it is
|
||||||
|
protected by global websocket mutex.
|
||||||
|
*/
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
bsc_websocket_global_lock();
|
||||||
|
lws_context_destroy(ctx->wsctx);
|
||||||
|
bsc_websocket_global_unlock();
|
||||||
|
pthread_mutex_lock(ctx->mutex);
|
||||||
|
ctx->wsctx = NULL;
|
||||||
|
DEBUG_PRINTF("bws_srv_worker() set wsctx %p\n", ctx->wsctx);
|
||||||
|
ctx->stop_worker = false;
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_srv_worker() ctx %p proto %d emitting stop event\n", ctx,
|
||||||
|
ctx->proto);
|
||||||
|
dispatch_func = ctx->dispatch_func;
|
||||||
|
user_param = ctx->user_param;
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_srv_worker() ctx %p user_param = %p\n", ctx, user_param);
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
dispatch_func(ctx, 0, BSC_WEBSOCKET_SERVER_STOPPED, 0, NULL, NULL,
|
||||||
|
0, user_param);
|
||||||
|
bws_free_server_ctx(ctx);
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_srv_worker() ctx %p proto %d stopped\n", ctx, ctx->proto);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < bws_srv_get_max_sockets(ctx->proto); i++) {
|
||||||
|
DEBUG_PRINTF("bws_srv_worker() ctx %p user_param %p proto %d "
|
||||||
|
"socket %d(%p) state = %d\n",
|
||||||
|
ctx, ctx->user_param, ctx->proto, i, &ctx->conn[i],
|
||||||
|
ctx->conn[i].state);
|
||||||
|
if (ctx->conn[i].state == BSC_WEBSOCKET_STATE_CONNECTED) {
|
||||||
|
if (ctx->conn[i].want_send_data) {
|
||||||
|
DEBUG_PRINTF("bws_srv_worker() process request for sending "
|
||||||
|
"data on socket %d\n",
|
||||||
|
i);
|
||||||
|
lws_callback_on_writable(ctx->conn[i].ws);
|
||||||
|
}
|
||||||
|
} else if (ctx->conn[i].state ==
|
||||||
|
BSC_WEBSOCKET_STATE_DISCONNECTING) {
|
||||||
|
DEBUG_PRINTF("bws_srv_worker() process disconnecting event on "
|
||||||
|
"socket %d\n",
|
||||||
|
i);
|
||||||
|
lws_callback_on_writable(ctx->conn[i].ws);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_srv_worker() ctx %p proto %d unblocked\n", ctx, ctx->proto);
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_srv_worker() ctx %p user_param %p proto %d going to block on "
|
||||||
|
"lws_service() call\n",
|
||||||
|
ctx, ctx->user_param, ctx->proto);
|
||||||
|
lws_service(ctx->wsctx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
BSC_WEBSOCKET_RET bws_srv_start(BSC_WEBSOCKET_PROTOCOL proto,
|
||||||
|
int port,
|
||||||
|
char *iface,
|
||||||
|
uint8_t *ca_cert,
|
||||||
|
size_t ca_cert_size,
|
||||||
|
uint8_t *cert,
|
||||||
|
size_t cert_size,
|
||||||
|
uint8_t *key,
|
||||||
|
size_t key_size,
|
||||||
|
size_t timeout_s,
|
||||||
|
BSC_WEBSOCKET_SRV_DISPATCH dispatch_func,
|
||||||
|
void *dispatch_func_user_param,
|
||||||
|
BSC_WEBSOCKET_SRV_HANDLE *sh)
|
||||||
|
{
|
||||||
|
pthread_t thread_id;
|
||||||
|
struct lws_context_creation_info info = { 0 };
|
||||||
|
BSC_WEBSOCKET_CONTEXT *ctx;
|
||||||
|
pthread_attr_t attr;
|
||||||
|
int r;
|
||||||
|
struct lws_protocols protos[] = {
|
||||||
|
{ (proto == BSC_WEBSOCKET_HUB_PROTOCOL)
|
||||||
|
? BSC_WEBSOCKET_HUB_PROTOCOL_STR
|
||||||
|
: BSC_WEBSOCKET_DIRECT_PROTOCOL_STR,
|
||||||
|
bws_srv_websocket_event, 0, 0, 0, NULL, 0 },
|
||||||
|
LWS_PROTOCOL_LIST_TERM
|
||||||
|
};
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bws_srv_start() >>> proto = %d port = %d "
|
||||||
|
"dispatch_func_user_param = %p\n",
|
||||||
|
proto, port, dispatch_func_user_param);
|
||||||
|
|
||||||
|
if (proto != BSC_WEBSOCKET_HUB_PROTOCOL &&
|
||||||
|
proto != BSC_WEBSOCKET_DIRECT_PROTOCOL) {
|
||||||
|
DEBUG_PRINTF("bws_srv_start() <<< bad protocol, ret = "
|
||||||
|
"BSC_WEBSOCKET_BAD_PARAM\n");
|
||||||
|
return BSC_WEBSOCKET_BAD_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ca_cert || !ca_cert_size || !cert || !cert_size || !key || !key_size ||
|
||||||
|
!dispatch_func || !timeout_s || !sh) {
|
||||||
|
DEBUG_PRINTF("bws_srv_start() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n");
|
||||||
|
return BSC_WEBSOCKET_BAD_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (port < 0 || port > 65535) {
|
||||||
|
DEBUG_PRINTF("bws_srv_start() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n");
|
||||||
|
return BSC_WEBSOCKET_BAD_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = bws_alloc_server_ctx(proto);
|
||||||
|
|
||||||
|
if (!ctx) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_srv_start() <<< maximum amount of servers for "
|
||||||
|
"server proto %d is to small, ret = BSC_WEBSOCKET_NO_RESOURCES\n",
|
||||||
|
proto);
|
||||||
|
return BSC_WEBSOCKET_NO_RESOURCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
bsc_websocket_init_log();
|
||||||
|
|
||||||
|
pthread_mutex_lock(ctx->mutex);
|
||||||
|
info.port = port;
|
||||||
|
info.iface = iface;
|
||||||
|
info.protocols = protos;
|
||||||
|
info.gid = -1;
|
||||||
|
info.uid = -1;
|
||||||
|
info.server_ssl_cert_mem = cert;
|
||||||
|
info.server_ssl_cert_mem_len = cert_size;
|
||||||
|
info.server_ssl_ca_mem = ca_cert;
|
||||||
|
info.server_ssl_ca_mem_len = ca_cert_size;
|
||||||
|
info.server_ssl_private_key_mem = key;
|
||||||
|
info.server_ssl_private_key_mem_len = key_size;
|
||||||
|
info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
|
||||||
|
info.options |= LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND;
|
||||||
|
info.timeout_secs = timeout_s;
|
||||||
|
info.connect_timeout_secs = timeout_s;
|
||||||
|
info.user = ctx;
|
||||||
|
|
||||||
|
/* TRICKY: check comments related to lws_context_destroy() call */
|
||||||
|
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
bsc_websocket_global_lock();
|
||||||
|
ctx->wsctx = lws_create_context(&info);
|
||||||
|
bsc_websocket_global_unlock();
|
||||||
|
pthread_mutex_lock(ctx->mutex);
|
||||||
|
|
||||||
|
if (!ctx->wsctx) {
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
bws_free_server_ctx(ctx);
|
||||||
|
DEBUG_PRINTF("bws_srv_start() <<< ret = BSC_WEBSOCKET_NO_RESOURCES\n");
|
||||||
|
return BSC_WEBSOCKET_NO_RESOURCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->dispatch_func = dispatch_func;
|
||||||
|
ctx->user_param = dispatch_func_user_param;
|
||||||
|
ctx->proto = proto;
|
||||||
|
r = pthread_attr_init(&attr);
|
||||||
|
|
||||||
|
if(!r) {
|
||||||
|
r = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!r) {
|
||||||
|
r = pthread_create(&thread_id, &attr, &bws_srv_worker, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r) {
|
||||||
|
/* TRICKY: Libwebsockets API is not designed to be used from
|
||||||
|
multipe service threads, as a result lws_context_destroy()
|
||||||
|
is not thread safe. More over, on different platforms the
|
||||||
|
function behaves in different ways. Call of
|
||||||
|
lws_context_destroy() leads to several calls of
|
||||||
|
bws_srv_websocket_event() callback (LWS_CALLBACK_CLOSED,
|
||||||
|
etc..). But under some OS (MacOSx) that callback is
|
||||||
|
called from context of the bws_cli_worker() thread and
|
||||||
|
under some other OS (linux) the callback is called from
|
||||||
|
internal libwebsockets lib thread. That's why
|
||||||
|
bws_cli_mutex must be unlocked before
|
||||||
|
lws_context_destroy() call. To ensure that nobody calls
|
||||||
|
lws_context_destroy() from some parallel thread it is
|
||||||
|
protected by global websocket mutex.
|
||||||
|
*/
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
bsc_websocket_global_lock();
|
||||||
|
lws_context_destroy(ctx->wsctx);
|
||||||
|
bsc_websocket_global_unlock();
|
||||||
|
pthread_mutex_lock(ctx->mutex);
|
||||||
|
ctx->wsctx = NULL;
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
bws_free_server_ctx(ctx);
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_srv_start() <<< ret = BACNET_WEBSOCKET_NO_RESOURCES\n");
|
||||||
|
return BSC_WEBSOCKET_NO_RESOURCES;
|
||||||
|
}
|
||||||
|
pthread_attr_destroy(&attr);
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
*sh = (BSC_WEBSOCKET_SRV_HANDLE)ctx;
|
||||||
|
DEBUG_PRINTF("bws_srv_start() <<< ret = BSC_WEBSOCKET_SUCCESS\n");
|
||||||
|
return BSC_WEBSOCKET_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
BSC_WEBSOCKET_RET bws_srv_stop(BSC_WEBSOCKET_SRV_HANDLE sh)
|
||||||
|
{
|
||||||
|
BSC_WEBSOCKET_CONTEXT *ctx = (BSC_WEBSOCKET_CONTEXT *)sh;
|
||||||
|
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_srv_stop() >>> ctx = %p user_param = %p\n", ctx, ctx->user_param);
|
||||||
|
|
||||||
|
#if DEBUG_ENABLED == 1
|
||||||
|
if (!bws_validate_ctx_pointer(ctx)) {
|
||||||
|
DEBUG_PRINTF("bws_srv_stop() <<< bad websocket handle, ret = "
|
||||||
|
"BSC_WEBSOCKET_BAD_PARAM\n");
|
||||||
|
return BSC_WEBSOCKET_BAD_PARAM;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pthread_mutex_lock(ctx->mutex);
|
||||||
|
|
||||||
|
if (ctx->stop_worker) {
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_srv_stop() <<< ret = BSC_WEBSOCKET_INVALID_OPERATION\n");
|
||||||
|
return BSC_WEBSOCKET_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->stop_worker = true;
|
||||||
|
/* wake up libwebsockets runloop */
|
||||||
|
lws_cancel_service(ctx->wsctx);
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bws_srv_stop() <<< ret = BSC_WEBSOCKET_SUCCESS\n");
|
||||||
|
return BSC_WEBSOCKET_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bws_srv_disconnect(BSC_WEBSOCKET_SRV_HANDLE sh, BSC_WEBSOCKET_HANDLE h)
|
||||||
|
{
|
||||||
|
BSC_WEBSOCKET_CONTEXT *ctx = (BSC_WEBSOCKET_CONTEXT *)sh;
|
||||||
|
DEBUG_PRINTF("bws_srv_disconnect() >>> sh = %p h = %d\n", sh, h);
|
||||||
|
|
||||||
|
#if DEBUG_ENABLED == 1
|
||||||
|
if (!bws_validate_ctx_pointer(ctx)) {
|
||||||
|
DEBUG_PRINTF("bws_srv_disconnect() <<< bad websocket handle\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pthread_mutex_lock(ctx->mutex);
|
||||||
|
if (h >= 0 && h < bws_srv_get_max_sockets(ctx->proto) &&
|
||||||
|
!ctx->stop_worker) {
|
||||||
|
if (ctx->conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED) {
|
||||||
|
/* tell worker to process change of connection state */
|
||||||
|
ctx->conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING;
|
||||||
|
lws_cancel_service(ctx->wsctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
DEBUG_PRINTF("bws_srv_disconnect() <<<\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void bws_srv_send(BSC_WEBSOCKET_SRV_HANDLE sh, BSC_WEBSOCKET_HANDLE h)
|
||||||
|
{
|
||||||
|
BSC_WEBSOCKET_CONTEXT *ctx = (BSC_WEBSOCKET_CONTEXT *)sh;
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bws_srv_send() >>> ctx = %p h = %d\n", ctx, h);
|
||||||
|
|
||||||
|
#if DEBUG_ENABLED == 1
|
||||||
|
if (!bws_validate_ctx_pointer(ctx)) {
|
||||||
|
DEBUG_PRINTF("bws_srv_send() <<< bad websocket handle\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pthread_mutex_lock(ctx->mutex);
|
||||||
|
if (ctx->conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED) {
|
||||||
|
/* tell worker to process send request */
|
||||||
|
ctx->conn[h].want_send_data = true;
|
||||||
|
lws_cancel_service(ctx->wsctx);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
DEBUG_PRINTF("bws_srv_send() <<<\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
BSC_WEBSOCKET_RET bws_srv_dispatch_send(BSC_WEBSOCKET_SRV_HANDLE sh,
|
||||||
|
BSC_WEBSOCKET_HANDLE h,
|
||||||
|
uint8_t *payload,
|
||||||
|
size_t payload_size)
|
||||||
|
{
|
||||||
|
int written;
|
||||||
|
BSC_WEBSOCKET_RET ret;
|
||||||
|
BSC_WEBSOCKET_CONTEXT *ctx = (BSC_WEBSOCKET_CONTEXT *)sh;
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bws_srv_dispatch_send() >>> ctx = %p h = %d payload %p "
|
||||||
|
"payload_size %d\n",
|
||||||
|
ctx, h, payload, payload_size);
|
||||||
|
|
||||||
|
#if DEBUG_ENABLED == 1
|
||||||
|
if (!bws_validate_ctx_pointer(ctx)) {
|
||||||
|
DEBUG_PRINTF("bws_srv_dispatch_send() <<< bad websocket handle, ret = "
|
||||||
|
"BSC_WEBSOCKET_BAD_PARAM\n");
|
||||||
|
return BSC_WEBSOCKET_BAD_PARAM;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (h < 0 || h >= bws_srv_get_max_sockets(ctx->proto)) {
|
||||||
|
DEBUG_PRINTF("bws_srv_dispatch_send() <<< BSC_WEBSOCKET_BAD_PARAM\n");
|
||||||
|
return BSC_WEBSOCKET_BAD_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!payload || !payload_size) {
|
||||||
|
DEBUG_PRINTF("bws_srv_dispatch_send() <<< BSC_WEBSOCKET_BAD_PARAM\n");
|
||||||
|
return BSC_WEBSOCKET_BAD_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(ctx->mutex);
|
||||||
|
|
||||||
|
if (ctx->stop_worker) {
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
DEBUG_PRINTF("bws_srv_dispatch_send() <<< ret = "
|
||||||
|
"BSC_WEBSOCKET_INVALID_OPERATION\n");
|
||||||
|
return BSC_WEBSOCKET_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ctx->conn[h].state != BSC_WEBSOCKET_STATE_CONNECTED) ||
|
||||||
|
!ctx->conn[h].want_send_data || !ctx->conn[h].can_send_data) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_srv_dispatch_send() <<< ret = BACNET_WEBSOCKET_BAD_PARAM\n");
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
return BSC_WEBSOCKET_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
written =
|
||||||
|
lws_write(ctx->conn[h].ws, payload, payload_size, LWS_WRITE_BINARY);
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bws_srv_dispatch_send() %d bytes is sent\n", written);
|
||||||
|
|
||||||
|
if (written < (int)payload_size) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_srv_dispatch_send() websocket connection is broken(closed)\n");
|
||||||
|
/* tell worker to process change of connection state */
|
||||||
|
ctx->conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING;
|
||||||
|
lws_cancel_service(ctx->wsctx);
|
||||||
|
ret = BSC_WEBSOCKET_INVALID_OPERATION;
|
||||||
|
} else {
|
||||||
|
ret = BSC_WEBSOCKET_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
DEBUG_PRINTF("bws_srv_dispatch_send() <<< ret = %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bws_srv_get_peer_ip_addr(BSC_WEBSOCKET_SRV_HANDLE sh,
|
||||||
|
BSC_WEBSOCKET_HANDLE h,
|
||||||
|
uint8_t *ip_str,
|
||||||
|
size_t ip_str_len,
|
||||||
|
uint16_t *port)
|
||||||
|
{
|
||||||
|
BSC_WEBSOCKET_CONTEXT *ctx = (BSC_WEBSOCKET_CONTEXT *)sh;
|
||||||
|
int fd;
|
||||||
|
struct sockaddr_storage addr;
|
||||||
|
socklen_t len;
|
||||||
|
const char *ret = NULL;
|
||||||
|
|
||||||
|
if (!ctx || h < 0 || !ip_str || !ip_str_len || !port ||
|
||||||
|
h >= bws_srv_get_max_sockets(ctx->proto)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(ctx->mutex);
|
||||||
|
|
||||||
|
if (ctx->conn[h].state != BSC_WEBSOCKET_STATE_IDLE &&
|
||||||
|
ctx->conn[h].ws != NULL && !ctx->stop_worker) {
|
||||||
|
len = sizeof(addr);
|
||||||
|
fd = lws_get_socket_fd(ctx->conn[h].ws);
|
||||||
|
if (fd != -1) {
|
||||||
|
getpeername(fd, (struct sockaddr *)&addr, &len);
|
||||||
|
if (addr.ss_family == AF_INET) {
|
||||||
|
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
|
||||||
|
*port = ntohs(s->sin_port);
|
||||||
|
ret = inet_ntop(
|
||||||
|
AF_INET, &s->sin_addr, (char *)ip_str, ip_str_len);
|
||||||
|
} else {
|
||||||
|
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr;
|
||||||
|
*port = ntohs(s->sin6_port);
|
||||||
|
ret = inet_ntop(
|
||||||
|
AF_INET6, &s->sin6_addr, (char *)ip_str, ip_str_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(ctx->mutex);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Implementation of mutex abstraction used in BACNet secure connect.
|
||||||
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||||
|
* @date August 2022
|
||||||
|
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||||
|
*/
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "bacnet/basic/sys/debug.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-event.h"
|
||||||
|
|
||||||
|
#define DEBUG_BSC_EVENT 0
|
||||||
|
|
||||||
|
#if DEBUG_BSC_EVENT == 1
|
||||||
|
#define DEBUG_PRINTF printf
|
||||||
|
#else
|
||||||
|
#undef DEBUG_ENABLED
|
||||||
|
#define DEBUG_PRINTF debug_printf_disabled
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct BSC_Event {
|
||||||
|
pthread_mutex_t mutex;
|
||||||
|
pthread_cond_t cond;
|
||||||
|
bool v;
|
||||||
|
size_t counter;
|
||||||
|
};
|
||||||
|
|
||||||
|
BSC_EVENT *bsc_event_init(void)
|
||||||
|
{
|
||||||
|
BSC_EVENT *ev;
|
||||||
|
pthread_mutexattr_t attr;
|
||||||
|
|
||||||
|
if (pthread_mutexattr_init(&attr) != 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) {
|
||||||
|
pthread_mutexattr_destroy(&attr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ev = malloc(sizeof(BSC_EVENT));
|
||||||
|
|
||||||
|
if (!ev) {
|
||||||
|
pthread_mutexattr_destroy(&attr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_mutex_init(&ev->mutex, &attr) != 0) {
|
||||||
|
pthread_mutexattr_destroy(&attr);
|
||||||
|
free(ev);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutexattr_destroy(&attr);
|
||||||
|
|
||||||
|
if (pthread_cond_init(&ev->cond, NULL) != 0) {
|
||||||
|
pthread_mutex_destroy(&ev->mutex);
|
||||||
|
free(ev);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ev->v = false;
|
||||||
|
ev->counter = 0;
|
||||||
|
return ev;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsc_event_deinit(BSC_EVENT *ev)
|
||||||
|
{
|
||||||
|
pthread_mutex_destroy(&ev->mutex);
|
||||||
|
pthread_cond_destroy(&ev->cond);
|
||||||
|
free(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsc_event_wait(BSC_EVENT *ev)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&ev->mutex);
|
||||||
|
DEBUG_PRINTF("bsc_event_wait() >>> ev = %p\n", ev);
|
||||||
|
DEBUG_PRINTF("bsc_event_wait() counter before %zu\n", ev->counter);
|
||||||
|
ev->counter++;
|
||||||
|
DEBUG_PRINTF("bsc_event_wait() counter %zu\n", ev->counter);
|
||||||
|
while (!ev->v) {
|
||||||
|
pthread_cond_wait(&ev->cond, &ev->mutex);
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF("bsc_event_wait() ev = %p\n", ev);
|
||||||
|
DEBUG_PRINTF("bsc_event_wait() before counter %zu\n", ev->counter);
|
||||||
|
ev->counter--;
|
||||||
|
DEBUG_PRINTF("bsc_event_wait() counter %zu\n", ev->counter);
|
||||||
|
if (!ev->counter) {
|
||||||
|
ev->v = false;
|
||||||
|
DEBUG_PRINTF("bsc_event_wait() reset ev\n");
|
||||||
|
} else {
|
||||||
|
DEBUG_PRINTF("bsc_event_wait() wake up other waiting threads\n");
|
||||||
|
pthread_cond_broadcast(&ev->cond);
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF("bsc_event_wait() <<< ev = %p\n", ev);
|
||||||
|
pthread_mutex_unlock(&ev->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bsc_event_timedwait(BSC_EVENT *ev, unsigned int ms_timeout)
|
||||||
|
{
|
||||||
|
struct timespec to;
|
||||||
|
int r = 0;
|
||||||
|
|
||||||
|
clock_gettime(CLOCK_REALTIME, &to);
|
||||||
|
to.tv_sec = to.tv_sec + ms_timeout / 1000;
|
||||||
|
to.tv_nsec = to.tv_nsec + (ms_timeout % 1000) * 1000000;
|
||||||
|
to.tv_sec += to.tv_nsec / 1000000000;
|
||||||
|
to.tv_nsec %= 1000000000;
|
||||||
|
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bsc_event_timedwait() >>> before lock ev = %p ev->v = %d\n", ev,
|
||||||
|
ev->v);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&ev->mutex);
|
||||||
|
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bsc_event_timedwait() >>> after lock ev = %p ev->v = %d\n", ev, ev->v);
|
||||||
|
|
||||||
|
ev->counter++;
|
||||||
|
DEBUG_PRINTF("bsc_event_timedwait() counter %zu\n", ev->counter);
|
||||||
|
|
||||||
|
while (!ev->v) {
|
||||||
|
r = pthread_cond_timedwait(&ev->cond, &ev->mutex, &to);
|
||||||
|
if (r != 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev->v) {
|
||||||
|
if (r != 0) {
|
||||||
|
DEBUG_PRINTF("Fired!!! r = %d\n", r);
|
||||||
|
}
|
||||||
|
r = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ev->counter--;
|
||||||
|
DEBUG_PRINTF("bsc_event_timedwait() counter %zu\n", ev->counter);
|
||||||
|
|
||||||
|
if (!ev->counter) {
|
||||||
|
DEBUG_PRINTF("bsc_event_timedwait() event is reset, err = %d\n", r);
|
||||||
|
ev->v = false;
|
||||||
|
} else {
|
||||||
|
DEBUG_PRINTF("bsc_event_timedwait() wake up other waiting threads\n");
|
||||||
|
pthread_cond_broadcast(&ev->cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bsc_event_timedwait() <<< ret = %d, ev = %p ev->v = %d r = %d\n",
|
||||||
|
r == 0, ev, ev->v, r);
|
||||||
|
pthread_mutex_unlock(&ev->mutex);
|
||||||
|
return r == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsc_event_signal(BSC_EVENT *ev)
|
||||||
|
{
|
||||||
|
DEBUG_PRINTF("bsc_event_signal() >>> ev = %p\n", ev);
|
||||||
|
pthread_mutex_lock(&ev->mutex);
|
||||||
|
DEBUG_PRINTF("bsc_event_signal() >>> ev = %p\n", ev);
|
||||||
|
ev->v = true;
|
||||||
|
pthread_cond_broadcast(&ev->cond);
|
||||||
|
DEBUG_PRINTF("bsc_event_signal() <<< ev = %p\n", ev);
|
||||||
|
pthread_mutex_unlock(&ev->mutex);
|
||||||
|
DEBUG_PRINTF("bsc_event_signal() <<< ev = %p\n", ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsc_wait(int seconds)
|
||||||
|
{
|
||||||
|
sleep(seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsc_wait_ms(int mseconds)
|
||||||
|
{
|
||||||
|
usleep(mseconds * 1000);
|
||||||
|
}
|
||||||
@@ -0,0 +1,781 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Implementation of websocket client interface.
|
||||||
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||||
|
* @date May 2022
|
||||||
|
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||||
|
*/
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <libwebsockets.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include "bacnet/datalink/bsc/websocket.h"
|
||||||
|
#include "bacnet/basic/sys/debug.h"
|
||||||
|
#include "websocket-global.h"
|
||||||
|
|
||||||
|
#define DEBUG_WEBSOCKET_CLIENT 0
|
||||||
|
|
||||||
|
#if DEBUG_WEBSOCKET_CLIENT == 1
|
||||||
|
#define DEBUG_PRINTF debug_printf
|
||||||
|
#else
|
||||||
|
#undef DEBUG_ENABLED
|
||||||
|
#define DEBUG_PRINTF debug_printf_disabled
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LWS_PROTOCOL_LIST_TERM
|
||||||
|
#define LWS_PROTOCOL_LIST_TERM \
|
||||||
|
{ \
|
||||||
|
NULL, NULL, 0, 0, 0, NULL, 0 \
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BSC_RX_BUFFER_LEN BSC_WEBSOCKET_RX_BUFFER_LEN
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BSC_WEBSOCKET_STATE_IDLE = 0,
|
||||||
|
BSC_WEBSOCKET_STATE_CONNECTING = 1,
|
||||||
|
BSC_WEBSOCKET_STATE_CONNECTED = 2,
|
||||||
|
BSC_WEBSOCKET_STATE_DISCONNECTING = 3
|
||||||
|
} BSC_WEBSOCKET_STATE;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
struct lws_context *ctx;
|
||||||
|
struct lws *ws;
|
||||||
|
BSC_WEBSOCKET_STATE state;
|
||||||
|
bool want_send_data;
|
||||||
|
bool can_send_data;
|
||||||
|
BSC_WEBSOCKET_CLI_DISPATCH dispatch_func;
|
||||||
|
void *user_param;
|
||||||
|
uint8_t *fragment_buffer;
|
||||||
|
size_t fragment_buffer_size;
|
||||||
|
size_t fragment_buffer_len;
|
||||||
|
char err_desc[BSC_WEBSOCKET_ERR_DESC_STR_MAX_LEN];
|
||||||
|
BACNET_ERROR_CODE err_code;
|
||||||
|
} BSC_WEBSOCKET_CONNECTION;
|
||||||
|
|
||||||
|
/* Some forward function declarations */
|
||||||
|
|
||||||
|
static int bws_cli_websocket_event(
|
||||||
|
struct lws *wsi,
|
||||||
|
enum lws_callback_reasons reason,
|
||||||
|
void *user,
|
||||||
|
void *in,
|
||||||
|
size_t len);
|
||||||
|
|
||||||
|
static const char *bws_hub_protocol = BSC_WEBSOCKET_HUB_PROTOCOL_STR;
|
||||||
|
static const char *bws_direct_protocol = BSC_WEBSOCKET_DIRECT_PROTOCOL_STR;
|
||||||
|
|
||||||
|
static pthread_mutex_t bws_cli_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
|
||||||
|
|
||||||
|
/* Websockets protocol defined in BACnet/SC \S AB.7.1. */
|
||||||
|
|
||||||
|
static struct lws_protocols bws_cli_direct_protocol[] = {
|
||||||
|
{ BSC_WEBSOCKET_DIRECT_PROTOCOL_STR, bws_cli_websocket_event, 0, 0, 0, NULL,
|
||||||
|
0 },
|
||||||
|
LWS_PROTOCOL_LIST_TERM
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct lws_protocols bws_cli_hub_protocol[] = {
|
||||||
|
{ BSC_WEBSOCKET_HUB_PROTOCOL_STR, bws_cli_websocket_event, 0, 0, 0, NULL,
|
||||||
|
0 },
|
||||||
|
LWS_PROTOCOL_LIST_TERM
|
||||||
|
};
|
||||||
|
|
||||||
|
static lws_retry_bo_t bws_retry;
|
||||||
|
|
||||||
|
static BSC_WEBSOCKET_CONNECTION bws_cli_conn[BSC_CLIENT_WEBSOCKETS_MAX_NUM] = {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
static BSC_WEBSOCKET_HANDLE bws_cli_alloc_connection(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < BSC_CLIENT_WEBSOCKETS_MAX_NUM; i++) {
|
||||||
|
if (bws_cli_conn[i].state == BSC_WEBSOCKET_STATE_IDLE) {
|
||||||
|
memset(&bws_cli_conn[i], 0, sizeof(bws_cli_conn[i]));
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return BSC_WEBSOCKET_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bws_cli_free_connection(BSC_WEBSOCKET_HANDLE h)
|
||||||
|
{
|
||||||
|
if (h >= 0 && h < BSC_CLIENT_WEBSOCKETS_MAX_NUM) {
|
||||||
|
if (bws_cli_conn[h].state != BSC_WEBSOCKET_STATE_IDLE) {
|
||||||
|
if (bws_cli_conn[h].fragment_buffer) {
|
||||||
|
free(bws_cli_conn[h].fragment_buffer);
|
||||||
|
bws_cli_conn[h].fragment_buffer = NULL;
|
||||||
|
bws_cli_conn[h].fragment_buffer_len = 0;
|
||||||
|
bws_cli_conn[h].fragment_buffer_size = 0;
|
||||||
|
}
|
||||||
|
bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_IDLE;
|
||||||
|
bws_cli_conn[h].ws = NULL;
|
||||||
|
bws_cli_conn[h].ctx = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BSC_WEBSOCKET_HANDLE bws_cli_find_connnection(struct lws *ws)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < BSC_CLIENT_WEBSOCKETS_MAX_NUM; i++) {
|
||||||
|
if (bws_cli_conn[i].ws == ws &&
|
||||||
|
bws_cli_conn[i].state != BSC_WEBSOCKET_STATE_IDLE) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return BSC_WEBSOCKET_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bws_set_err_desc(BSC_WEBSOCKET_HANDLE h, char *err_desc)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
if (bws_cli_conn[h].err_code == ERROR_CODE_SUCCESS) {
|
||||||
|
len = strlen(err_desc) >= sizeof(bws_cli_conn[h].err_desc)
|
||||||
|
? sizeof(bws_cli_conn[h].err_desc) - 1
|
||||||
|
: strlen(err_desc);
|
||||||
|
|
||||||
|
memcpy(bws_cli_conn[h].err_desc, err_desc, len);
|
||||||
|
bws_cli_conn[h].err_desc[len] = 0;
|
||||||
|
|
||||||
|
if (strstr(err_desc, "tls:")) {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_TLS_ERROR;
|
||||||
|
} else {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bws_set_disconnect_reason(BSC_WEBSOCKET_HANDLE h, uint16_t err_code)
|
||||||
|
{
|
||||||
|
bws_cli_conn[h].err_desc[0] = 0;
|
||||||
|
switch (err_code) {
|
||||||
|
case LWS_CLOSE_STATUS_NORMAL: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_CLOSED_BY_PEER;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_GOINGAWAY: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_ENDPOINT_LEAVES;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_PROTOCOL_ERR: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_PROTOCOL_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_NOT_ACCEPTED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_NO_STATUS:
|
||||||
|
case LWS_CLOSE_STATUS_RESERVED: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_ABNORMAL_CLOSE: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_NOT_ACCEPTED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_INVALID_PAYLOAD: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_INCONSISTENT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_POLICY_VIOLATION: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_AGAINST_POLICY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_FRAME_TOO_LONG;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_EXTENSION_REQUIRED: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_EXTENSION_MISSING;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_UNEXPECTED_CONDITION: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_REQUEST_UNAVAILABLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bws_cli_websocket_event(
|
||||||
|
struct lws *wsi,
|
||||||
|
enum lws_callback_reasons reason,
|
||||||
|
void *user,
|
||||||
|
void *in,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
BSC_WEBSOCKET_HANDLE h;
|
||||||
|
BSC_WEBSOCKET_CLI_DISPATCH dispatch_func;
|
||||||
|
void *user_param;
|
||||||
|
uint8_t err_code[2];
|
||||||
|
|
||||||
|
(void)user;
|
||||||
|
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() >>> reason = %d, user = %p, in = %p\n",
|
||||||
|
reason, user, in);
|
||||||
|
|
||||||
|
switch (reason) {
|
||||||
|
case LWS_CALLBACK_CLIENT_ESTABLISHED: {
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
h = bws_cli_find_connnection(wsi);
|
||||||
|
|
||||||
|
if (h == BSC_WEBSOCKET_INVALID_HANDLE) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() can not find websocket handle "
|
||||||
|
"for wsi %p\n",
|
||||||
|
wsi);
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = 0\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bws_cli_websocket_event() connection established\n");
|
||||||
|
bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_CONNECTED;
|
||||||
|
dispatch_func = bws_cli_conn[h].dispatch_func;
|
||||||
|
user_param = bws_cli_conn[h].user_param;
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
dispatch_func(
|
||||||
|
h, BSC_WEBSOCKET_CONNECTED, 0, NULL, NULL, 0, user_param);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CALLBACK_CLIENT_RECEIVE: {
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
h = bws_cli_find_connnection(wsi);
|
||||||
|
|
||||||
|
if (h == BSC_WEBSOCKET_INVALID_HANDLE) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() can not find websocket handle "
|
||||||
|
"for wsi %p\n",
|
||||||
|
wsi);
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = 0\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() received %d bytes of data\n", len);
|
||||||
|
if (!lws_frame_is_binary(wsi)) {
|
||||||
|
/* According AB.7.5.3 BACnet/SC BVLC Message Exchange,
|
||||||
|
if a received data frame is not binary,
|
||||||
|
the WebSocket connection shall be closed with a
|
||||||
|
status code of 1003 -WEBSOCKET_DATA_NOT_ACCEPTED.
|
||||||
|
*/
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() got non-binary frame, "
|
||||||
|
"close connection for socket %d\n",
|
||||||
|
h);
|
||||||
|
lws_close_reason(
|
||||||
|
wsi, LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE, NULL, 0);
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = -1\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (bws_cli_conn[h].state != BSC_WEBSOCKET_STATE_CONNECTED) {
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
} else {
|
||||||
|
if (!bws_cli_conn[h].fragment_buffer) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() alloc %d bytes for "
|
||||||
|
"socket %d\n",
|
||||||
|
len, h);
|
||||||
|
bws_cli_conn[h].fragment_buffer = malloc(BSC_RX_BUFFER_LEN);
|
||||||
|
if (!bws_cli_conn[h].fragment_buffer) {
|
||||||
|
lws_close_reason(
|
||||||
|
wsi, LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE, NULL, 0);
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() <<< ret = -1, "
|
||||||
|
"allocation of %d bytes failed\n",
|
||||||
|
len <= BSC_RX_BUFFER_LEN ? BSC_RX_BUFFER_LEN : len);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
bws_cli_conn[h].fragment_buffer_len = 0;
|
||||||
|
bws_cli_conn[h].fragment_buffer_size = BSC_RX_BUFFER_LEN;
|
||||||
|
}
|
||||||
|
if (bws_cli_conn[h].fragment_buffer_len + len >
|
||||||
|
bws_cli_conn[h].fragment_buffer_size) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() realloc buf of %d bytes"
|
||||||
|
"for socket %d to %d bytes\n",
|
||||||
|
bws_cli_conn[h].fragment_buffer_len, h,
|
||||||
|
bws_cli_conn[h].fragment_buffer_len + len);
|
||||||
|
bws_cli_conn[h].fragment_buffer = realloc(
|
||||||
|
bws_cli_conn[h].fragment_buffer,
|
||||||
|
bws_cli_conn[h].fragment_buffer_len + len);
|
||||||
|
if (!bws_cli_conn[h].fragment_buffer) {
|
||||||
|
lws_close_reason(
|
||||||
|
wsi, LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE, NULL, 0);
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() <<< ret = -1, "
|
||||||
|
"re-allocation of %d bytes failed\n",
|
||||||
|
bws_cli_conn[h].fragment_buffer_len + len);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
bws_cli_conn[h].fragment_buffer_size =
|
||||||
|
bws_cli_conn[h].fragment_buffer_len + len;
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() got next %d bytes for "
|
||||||
|
"socket %d\n",
|
||||||
|
len, h);
|
||||||
|
memcpy(
|
||||||
|
&bws_cli_conn[h]
|
||||||
|
.fragment_buffer[bws_cli_conn[h].fragment_buffer_len],
|
||||||
|
in, len);
|
||||||
|
bws_cli_conn[h].fragment_buffer_len += len;
|
||||||
|
|
||||||
|
if (lws_is_final_fragment(wsi)) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() last fragment received\n");
|
||||||
|
dispatch_func = bws_cli_conn[h].dispatch_func;
|
||||||
|
user_param = bws_cli_conn[h].user_param;
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
dispatch_func(
|
||||||
|
h, BSC_WEBSOCKET_RECEIVED, 0, NULL,
|
||||||
|
bws_cli_conn[h].fragment_buffer,
|
||||||
|
bws_cli_conn[h].fragment_buffer_len, user_param);
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
bws_cli_conn[h].fragment_buffer_len = 0;
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
} else {
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CALLBACK_CLIENT_WRITEABLE: {
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
h = bws_cli_find_connnection(wsi);
|
||||||
|
|
||||||
|
if (h == BSC_WEBSOCKET_INVALID_HANDLE) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() can not find websocket handle "
|
||||||
|
"for wsi %p\n",
|
||||||
|
wsi);
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = 0\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() can write, state = %d\n",
|
||||||
|
bws_cli_conn[h].state);
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() ws = %d, cs = %d\n",
|
||||||
|
bws_cli_conn[h].want_send_data, bws_cli_conn[h].can_send_data);
|
||||||
|
if (bws_cli_conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED &&
|
||||||
|
bws_cli_conn[h].want_send_data) {
|
||||||
|
bws_cli_conn[h].can_send_data = true;
|
||||||
|
dispatch_func = bws_cli_conn[h].dispatch_func;
|
||||||
|
user_param = bws_cli_conn[h].user_param;
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
dispatch_func(
|
||||||
|
h, BSC_WEBSOCKET_SENDABLE, 0, NULL, NULL, 0, user_param);
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
bws_cli_conn[h].want_send_data = false;
|
||||||
|
bws_cli_conn[h].can_send_data = false;
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() was send , ws = %d, cs = %d\n",
|
||||||
|
bws_cli_conn[h].want_send_data,
|
||||||
|
bws_cli_conn[h].can_send_data);
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
/* wakeup worker to process internal state */
|
||||||
|
lws_cancel_service(bws_cli_conn[h].ctx);
|
||||||
|
} else {
|
||||||
|
bws_cli_conn[h].want_send_data = false;
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() no send, ws = %d, cs = %d\n",
|
||||||
|
bws_cli_conn[h].want_send_data,
|
||||||
|
bws_cli_conn[h].can_send_data);
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: {
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
h = bws_cli_find_connnection(wsi);
|
||||||
|
if (h != BSC_WEBSOCKET_INVALID_HANDLE && len >= 2) {
|
||||||
|
err_code[0] = ((uint8_t *)in)[1];
|
||||||
|
err_code[1] = ((uint8_t *)in)[0];
|
||||||
|
bws_set_disconnect_reason(h, *((uint16_t *)&err_code));
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CALLBACK_CLIENT_CLOSED:
|
||||||
|
case LWS_CALLBACK_CLOSED:
|
||||||
|
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: {
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
h = bws_cli_find_connnection(wsi);
|
||||||
|
if (h != BSC_WEBSOCKET_INVALID_HANDLE) {
|
||||||
|
bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING;
|
||||||
|
if (reason == LWS_CALLBACK_CLIENT_CONNECTION_ERROR && in) {
|
||||||
|
bws_set_err_desc(h, (char *)in);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
/* wakeup worker to process pending event */
|
||||||
|
lws_cancel_service(bws_cli_conn[h].ctx);
|
||||||
|
} else {
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = 0\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *bws_cli_worker(void *arg)
|
||||||
|
{
|
||||||
|
BSC_WEBSOCKET_CONNECTION *conn = (BSC_WEBSOCKET_CONNECTION *)arg;
|
||||||
|
BSC_WEBSOCKET_HANDLE h = conn - &bws_cli_conn[0];
|
||||||
|
BSC_WEBSOCKET_CLI_DISPATCH dispatch_func;
|
||||||
|
void *user_param;
|
||||||
|
char err_desc[BSC_WEBSOCKET_ERR_DESC_STR_MAX_LEN];
|
||||||
|
uint16_t err_code;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
DEBUG_PRINTF("bws_cli_worker() try mutex h = %d\n", h);
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_worker() mutex locked h = %d\n", h);
|
||||||
|
if (conn->state == BSC_WEBSOCKET_STATE_CONNECTED) {
|
||||||
|
if (conn->want_send_data) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_worker() process request for sending data\n");
|
||||||
|
lws_callback_on_writable(conn->ws);
|
||||||
|
}
|
||||||
|
} else if (conn->state == BSC_WEBSOCKET_STATE_DISCONNECTING) {
|
||||||
|
DEBUG_PRINTF("bws_cli_worker() process disconnecting event\n");
|
||||||
|
DEBUG_PRINTF("bws_cli_worker() destroy ctx %p\n", conn->ctx);
|
||||||
|
/* TRICKY: Libwebsockets API is not designed to be used from
|
||||||
|
multipe service threads, as a result
|
||||||
|
lws_context_destroy() is not thread safe.More over, on different
|
||||||
|
platforms the function behaves in different ways. Call of
|
||||||
|
lws_context_destroy() leads to several calls of
|
||||||
|
bws_cli_websocket_event() callback (LWS_CALLBACK_CLOSED,
|
||||||
|
etc..). But under some OS (MacOSx) that callback is
|
||||||
|
called from context of the bws_cli_worker() thread and
|
||||||
|
under some other OS (linux) the callback is called from
|
||||||
|
internal libwebsockets lib thread. That's why
|
||||||
|
bws_cli_mutex must be unlocked before
|
||||||
|
lws_context_destroy() call. To ensure that nobody calls
|
||||||
|
lws_context_destroy() from some parallel thread it is
|
||||||
|
protected by global websocket mutex.
|
||||||
|
*/
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
bsc_websocket_global_lock();
|
||||||
|
lws_context_destroy(conn->ctx);
|
||||||
|
bsc_websocket_global_unlock();
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
dispatch_func = conn->dispatch_func;
|
||||||
|
user_param = conn->user_param;
|
||||||
|
err_code = conn->err_code;
|
||||||
|
if (err_code != ERROR_CODE_SUCCESS) {
|
||||||
|
memcpy(err_desc, conn->err_desc, sizeof(err_desc));
|
||||||
|
}
|
||||||
|
bws_cli_free_connection(h);
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_worker() unlock mutex\n");
|
||||||
|
dispatch_func(
|
||||||
|
h, BSC_WEBSOCKET_DISCONNECTED, err_code,
|
||||||
|
err_code != ERROR_CODE_SUCCESS ? err_desc : NULL, NULL, 0,
|
||||||
|
user_param);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF("bws_cli_worker() unlock mutex\n");
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_worker() going to block on lws_service() call\n");
|
||||||
|
lws_service(conn->ctx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
BSC_WEBSOCKET_RET bws_cli_connect(
|
||||||
|
BSC_WEBSOCKET_PROTOCOL proto,
|
||||||
|
char *url,
|
||||||
|
uint8_t *ca_cert,
|
||||||
|
size_t ca_cert_size,
|
||||||
|
uint8_t *cert,
|
||||||
|
size_t cert_size,
|
||||||
|
uint8_t *key,
|
||||||
|
size_t key_size,
|
||||||
|
size_t timeout_s,
|
||||||
|
BSC_WEBSOCKET_CLI_DISPATCH dispatch_func,
|
||||||
|
void *dispatch_func_user_param,
|
||||||
|
BSC_WEBSOCKET_HANDLE *out_handle)
|
||||||
|
{
|
||||||
|
struct lws_context_creation_info info;
|
||||||
|
char tmp_url[BSC_WSURL_MAX_LEN];
|
||||||
|
const char *prot = NULL, *addr = NULL, *path = NULL;
|
||||||
|
int port = -1;
|
||||||
|
BSC_WEBSOCKET_HANDLE h;
|
||||||
|
struct lws_client_connect_info cinfo;
|
||||||
|
pthread_t thread_id;
|
||||||
|
size_t len;
|
||||||
|
pthread_attr_t attr;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bws_cli_connect() >>> proto = %d, url = %s\n", proto, url);
|
||||||
|
|
||||||
|
if (!ca_cert || !ca_cert_size || !cert || !cert_size || !key || !key_size ||
|
||||||
|
!url || !out_handle || !timeout_s || !dispatch_func) {
|
||||||
|
DEBUG_PRINTF("bws_cli_connect() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n");
|
||||||
|
return BSC_WEBSOCKET_BAD_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_handle = BSC_WEBSOCKET_INVALID_HANDLE;
|
||||||
|
|
||||||
|
if (proto != BSC_WEBSOCKET_HUB_PROTOCOL &&
|
||||||
|
proto != BSC_WEBSOCKET_DIRECT_PROTOCOL) {
|
||||||
|
DEBUG_PRINTF("bws_cli_connect() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n");
|
||||||
|
return BSC_WEBSOCKET_BAD_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&info, 0, sizeof(info));
|
||||||
|
memset(&cinfo, 0, sizeof(cinfo));
|
||||||
|
|
||||||
|
len = strlen(url) >= sizeof(tmp_url) ? sizeof(tmp_url) - 1 : strlen(url);
|
||||||
|
memcpy(tmp_url, url, len);
|
||||||
|
tmp_url[len] = 0;
|
||||||
|
|
||||||
|
bsc_websocket_init_log();
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
|
||||||
|
if (lws_parse_uri(tmp_url, &prot, &addr, &port, &path) != 0 || port == -1 ||
|
||||||
|
!prot || !addr || !path) {
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_connect() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n");
|
||||||
|
return BSC_WEBSOCKET_BAD_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(prot, "wss") != 0) {
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_connect() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n");
|
||||||
|
return BSC_WEBSOCKET_BAD_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
h = bws_cli_alloc_connection();
|
||||||
|
|
||||||
|
if (h == BSC_WEBSOCKET_INVALID_HANDLE) {
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_connect() <<< ret = BSC_WEBSOCKET_NO_RESOURCES\n");
|
||||||
|
return BSC_WEBSOCKET_NO_RESOURCES;
|
||||||
|
}
|
||||||
|
bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_CONNECTING;
|
||||||
|
bws_cli_conn[h].fragment_buffer = NULL;
|
||||||
|
bws_cli_conn[h].fragment_buffer_len = 0;
|
||||||
|
bws_cli_conn[h].fragment_buffer_size = 0;
|
||||||
|
bws_cli_conn[h].dispatch_func = dispatch_func;
|
||||||
|
bws_cli_conn[h].user_param = dispatch_func_user_param;
|
||||||
|
info.port = CONTEXT_PORT_NO_LISTEN;
|
||||||
|
if (proto == BSC_WEBSOCKET_HUB_PROTOCOL) {
|
||||||
|
info.protocols = bws_cli_hub_protocol;
|
||||||
|
} else {
|
||||||
|
info.protocols = bws_cli_direct_protocol;
|
||||||
|
}
|
||||||
|
info.gid = -1;
|
||||||
|
info.uid = -1;
|
||||||
|
info.client_ssl_cert_mem = cert;
|
||||||
|
info.client_ssl_cert_mem_len = cert_size;
|
||||||
|
info.client_ssl_ca_mem = ca_cert;
|
||||||
|
info.client_ssl_ca_mem_len = ca_cert_size;
|
||||||
|
info.client_ssl_key_mem = key;
|
||||||
|
info.client_ssl_key_mem_len = key_size;
|
||||||
|
info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
|
||||||
|
info.options |= LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND;
|
||||||
|
info.timeout_secs = timeout_s;
|
||||||
|
info.connect_timeout_secs = timeout_s;
|
||||||
|
|
||||||
|
/* TRICKY: check comments related to lws_context_destroy() call */
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
bsc_websocket_global_lock();
|
||||||
|
bws_cli_conn[h].ctx = lws_create_context(&info);
|
||||||
|
bsc_websocket_global_unlock();
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_connect() created ctx %p\n", bws_cli_conn[h].ctx);
|
||||||
|
|
||||||
|
if (!bws_cli_conn[h].ctx) {
|
||||||
|
bws_cli_free_connection(h);
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_connect() <<< ret = BSC_WEBSOCKET_NO_RESOURCES\n");
|
||||||
|
return BSC_WEBSOCKET_NO_RESOURCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
bws_cli_conn[h].ws = NULL;
|
||||||
|
cinfo.context = bws_cli_conn[h].ctx;
|
||||||
|
cinfo.address = addr;
|
||||||
|
cinfo.origin = cinfo.address;
|
||||||
|
cinfo.host = cinfo.address;
|
||||||
|
cinfo.port = port;
|
||||||
|
cinfo.path = path;
|
||||||
|
cinfo.pwsi = &bws_cli_conn[h].ws;
|
||||||
|
cinfo.alpn = "h2;http/1.1";
|
||||||
|
bws_retry.secs_since_valid_ping = 3;
|
||||||
|
bws_retry.secs_since_valid_hangup = 10;
|
||||||
|
cinfo.retry_and_idle_policy = &bws_retry;
|
||||||
|
cinfo.ssl_connection = LCCSCF_USE_SSL |
|
||||||
|
LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK | LCCSCF_ALLOW_SELFSIGNED;
|
||||||
|
|
||||||
|
if (proto == BSC_WEBSOCKET_HUB_PROTOCOL) {
|
||||||
|
cinfo.protocol = bws_hub_protocol;
|
||||||
|
} else {
|
||||||
|
cinfo.protocol = bws_direct_protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_SUCCESS;
|
||||||
|
*out_handle = h;
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
bsc_websocket_global_lock();
|
||||||
|
lws_client_connect_via_info(&cinfo);
|
||||||
|
bsc_websocket_global_unlock();
|
||||||
|
r = pthread_attr_init(&attr);
|
||||||
|
|
||||||
|
if (!r) {
|
||||||
|
r = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!r) {
|
||||||
|
r = pthread_create(
|
||||||
|
&thread_id, &attr, &bws_cli_worker, &bws_cli_conn[h]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r) {
|
||||||
|
/* TRICKY: Libwebsockets API is not designed to be used from
|
||||||
|
multipe service threads, as a result lws_context_destroy()
|
||||||
|
is not thread safe. More over, on different platforms the
|
||||||
|
function behaves in different ways. Call of
|
||||||
|
lws_context_destroy() leads to several calls of
|
||||||
|
bws_cli_websocket_event() callback (LWS_CALLBACK_CLOSED,
|
||||||
|
etc..). But under some OS (MacOSx) that callback is
|
||||||
|
called from context of the bws_cli_worker() thread and
|
||||||
|
under some other OS (linux) the callback is called from
|
||||||
|
internal libwebsockets lib thread. That's why
|
||||||
|
bws_cli_mutex must be unlocked before
|
||||||
|
lws_context_destroy() call. To ensure that nobody calls
|
||||||
|
lws_context_destroy() from some parallel thread it is
|
||||||
|
protected by global websocket mutex.
|
||||||
|
*/
|
||||||
|
/* pthread_mutex_unlock(&bws_cli_mutex); */
|
||||||
|
bsc_websocket_global_lock();
|
||||||
|
lws_context_destroy(bws_cli_conn[h].ctx);
|
||||||
|
bsc_websocket_global_unlock();
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
bws_cli_free_connection(h);
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_connect() <<< ret = BSC_WEBSOCKET_NO_RESOURCES\n");
|
||||||
|
return BSC_WEBSOCKET_NO_RESOURCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_attr_destroy(&attr);
|
||||||
|
DEBUG_PRINTF("bws_cli_connect() <<< ret = %d\n", BSC_WEBSOCKET_SUCCESS);
|
||||||
|
return BSC_WEBSOCKET_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bws_cli_disconnect(BSC_WEBSOCKET_HANDLE h)
|
||||||
|
{
|
||||||
|
DEBUG_PRINTF("bws_cli_disconnect() >>> h = %d\n", h);
|
||||||
|
|
||||||
|
if (h >= 0 && h < BSC_CLIENT_WEBSOCKETS_MAX_NUM) {
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_disconnect() state = %d\n", bws_cli_conn[h].state);
|
||||||
|
if (bws_cli_conn[h].state == BSC_WEBSOCKET_STATE_CONNECTING ||
|
||||||
|
bws_cli_conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED) {
|
||||||
|
/* tell worker to process change of connection state */
|
||||||
|
bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING;
|
||||||
|
lws_cancel_service(bws_cli_conn[h].ctx);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bws_cli_disconnect() <<<\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void bws_cli_send(BSC_WEBSOCKET_HANDLE h)
|
||||||
|
{
|
||||||
|
DEBUG_PRINTF("bws_cli_send() >>> h = %d\n", h);
|
||||||
|
|
||||||
|
if (h >= 0 && h < BSC_CLIENT_WEBSOCKETS_MAX_NUM) {
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
|
||||||
|
if (bws_cli_conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED) {
|
||||||
|
/* tell worker to process send request */
|
||||||
|
bws_cli_conn[h].want_send_data = true;
|
||||||
|
DEBUG_PRINTF("bws_cli_send() cs = 1\n");
|
||||||
|
lws_cancel_service(bws_cli_conn[h].ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bws_cli_send() <<<\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
BSC_WEBSOCKET_RET bws_cli_dispatch_send(
|
||||||
|
BSC_WEBSOCKET_HANDLE h, uint8_t *payload, size_t payload_size)
|
||||||
|
{
|
||||||
|
int written;
|
||||||
|
BSC_WEBSOCKET_RET ret;
|
||||||
|
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_dispatch_send() >>> h = %d, payload = %p, payload_size = %d\n",
|
||||||
|
h, payload, payload_size);
|
||||||
|
|
||||||
|
if (h < 0 || h >= BSC_CLIENT_WEBSOCKETS_MAX_NUM) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_dispatch_send() <<< ret = BACNET_WEBSOCKET_BAD_PARAM\n");
|
||||||
|
return BSC_WEBSOCKET_BAD_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&bws_cli_mutex);
|
||||||
|
|
||||||
|
if ((bws_cli_conn[h].state != BSC_WEBSOCKET_STATE_CONNECTED) ||
|
||||||
|
!bws_cli_conn[h].want_send_data || !bws_cli_conn[h].can_send_data) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_dispatch_send() state = %d, ws = %d, cs = %d\n",
|
||||||
|
bws_cli_conn[h].state, bws_cli_conn[h].want_send_data,
|
||||||
|
bws_cli_conn[h].can_send_data);
|
||||||
|
DEBUG_PRINTF("bws_cli_dispatch_send() <<< ret = "
|
||||||
|
"BSC_WEBSOCKET_INVALID_OPERATION\n");
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
return BSC_WEBSOCKET_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
written =
|
||||||
|
lws_write(bws_cli_conn[h].ws, payload, payload_size, LWS_WRITE_BINARY);
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bws_cli_dispatch_send() %d bytes is sent\n", written);
|
||||||
|
|
||||||
|
if (written < (int)payload_size) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_dispatch_send() websocket connection is broken(closed)\n");
|
||||||
|
/* tell worker to process change of connection state */
|
||||||
|
bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING;
|
||||||
|
lws_cancel_service(bws_cli_conn[h].ctx);
|
||||||
|
ret = BSC_WEBSOCKET_INVALID_OPERATION;
|
||||||
|
} else {
|
||||||
|
ret = BSC_WEBSOCKET_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_dispatch_send() <<< ret = %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Implementation of global websocket functions.
|
||||||
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||||
|
* @date May 2022
|
||||||
|
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||||
|
*/
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <libwebsockets.h>
|
||||||
|
#include "websocket-global.h"
|
||||||
|
#include "bacnet/basic/sys/debug.h"
|
||||||
|
|
||||||
|
#if LWS_MAX_SMP <= 1
|
||||||
|
#warning \
|
||||||
|
"Libwebsockets must be built with LWS_MAX_SMP > 1 (otherwise it does not support thread synchronization in correct way)"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static pthread_mutex_t websocket_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
|
||||||
|
static pthread_mutex_t websocket_dispatch_mutex =
|
||||||
|
PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
|
||||||
|
|
||||||
|
#if (BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED != 1)
|
||||||
|
|
||||||
|
void bsc_websocket_global_lock(void)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&websocket_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsc_websocket_global_unlock(void)
|
||||||
|
{
|
||||||
|
pthread_mutex_unlock(&websocket_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bws_dispatch_lock(void)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&websocket_dispatch_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bws_dispatch_unlock(void)
|
||||||
|
{
|
||||||
|
pthread_mutex_unlock(&websocket_dispatch_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
static volatile int websocket_mutex_cnt = 0;
|
||||||
|
static volatile int websocket_dispatch_mutex_cnt = 0;
|
||||||
|
|
||||||
|
void bsc_websocket_global_lock_dbg(char *f, int line)
|
||||||
|
{
|
||||||
|
printf(
|
||||||
|
"bsc_websocket_global_lock_dbg() >>> %s:%d lock_cnt %d tid = %ld\n", f,
|
||||||
|
line, websocket_mutex_cnt, pthread_self());
|
||||||
|
websocket_mutex_cnt++;
|
||||||
|
fflush(stdout);
|
||||||
|
pthread_mutex_lock(&websocket_mutex);
|
||||||
|
printf(
|
||||||
|
"bsc_websocket_global_lock_dbg() <<< lock_cnt %d tid = %ld\n",
|
||||||
|
websocket_mutex_cnt, pthread_self());
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsc_websocket_global_unlock_dbg(char *f, int line)
|
||||||
|
{
|
||||||
|
printf(
|
||||||
|
"bsc_websocket_global_unlock_dbg() >>> %s:%d lock_cnt %d tid = %ld\n",
|
||||||
|
f, line, websocket_mutex_cnt, pthread_self());
|
||||||
|
websocket_mutex_cnt--;
|
||||||
|
fflush(stdout);
|
||||||
|
pthread_mutex_unlock(&websocket_mutex);
|
||||||
|
printf(
|
||||||
|
"bsc_websocket_global_unlock_dbg() <<< lock_cnt %d tid = %ld\n",
|
||||||
|
websocket_mutex_cnt, pthread_self());
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bws_dispatch_lock_dbg(char *f, int line)
|
||||||
|
{
|
||||||
|
printf(
|
||||||
|
"bws_dispatch_lock_dbg() >>> %s:%d lock_cnt %d tid = %ld\n", f, line,
|
||||||
|
websocket_dispatch_mutex_cnt, pthread_self());
|
||||||
|
websocket_dispatch_mutex_cnt++;
|
||||||
|
fflush(stdout);
|
||||||
|
pthread_mutex_lock(&websocket_dispatch_mutex);
|
||||||
|
printf(
|
||||||
|
"bws_dispatch_lock_dbg() <<< lock_cnt %d tid = %ld\n",
|
||||||
|
websocket_dispatch_mutex_cnt, pthread_self());
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bws_dispatch_unlock_dbg(char *f, int line)
|
||||||
|
{
|
||||||
|
printf(
|
||||||
|
"bws_dispatch_unlock_dbg() >>> %s:%d lock_cnt %d tid = %ld\n", f, line,
|
||||||
|
websocket_dispatch_mutex_cnt, pthread_self());
|
||||||
|
websocket_dispatch_mutex_cnt--;
|
||||||
|
fflush(stdout);
|
||||||
|
pthread_mutex_unlock(&websocket_dispatch_mutex);
|
||||||
|
printf(
|
||||||
|
"bws_dispatch_unlock_dbg() <<< lock_cnt %d tid = %ld\n",
|
||||||
|
websocket_dispatch_mutex_cnt, pthread_self());
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static bool bsc_websocket_log_initialized = false;
|
||||||
|
|
||||||
|
void bsc_websocket_init_log(void)
|
||||||
|
{
|
||||||
|
bsc_websocket_global_lock();
|
||||||
|
if (!bsc_websocket_log_initialized) {
|
||||||
|
bsc_websocket_log_initialized = true;
|
||||||
|
#if DEBUG_LIBWEBSOCKETS_ENABLED == 1
|
||||||
|
printf("LWS_MAX_SMP = %d", LWS_MAX_SMP);
|
||||||
|
lws_set_log_level(
|
||||||
|
LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_INFO | LLL_DEBUG |
|
||||||
|
LLL_PARSER | LLL_HEADER | LLL_EXT | LLL_CLIENT | LLL_LATENCY |
|
||||||
|
LLL_USER | LLL_THREAD,
|
||||||
|
NULL);
|
||||||
|
#else
|
||||||
|
lws_set_log_level(0, NULL);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
bsc_websocket_global_unlock();
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Global websocket functions
|
||||||
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||||
|
* @date May 2022
|
||||||
|
* @copyright SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
#ifndef __BSC_WEBSOCKET_MUTEX_INCLUDED__
|
||||||
|
#define __BSC_WEBSOCKET_MUTEX_INCLUDED__
|
||||||
|
|
||||||
|
#ifndef BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED
|
||||||
|
#define BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED != 1)
|
||||||
|
void bsc_websocket_global_lock(void);
|
||||||
|
void bsc_websocket_global_unlock(void);
|
||||||
|
#else
|
||||||
|
void bsc_websocket_global_lock_dbg(char *f, int line);
|
||||||
|
void bsc_websocket_global_unlock_dbg(char *f, int line);
|
||||||
|
#define bsc_websocket_global_lock() \
|
||||||
|
bsc_websocket_global_lock_dbg(__FILE__, __LINE__);
|
||||||
|
#define bsc_websocket_global_unlock() \
|
||||||
|
bsc_websocket_global_unlock_dbg(__FILE__, __LINE__);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void bsc_websocket_init_log(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,145 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Implementation of mutex abstraction used in BACNet secure connect.
|
||||||
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||||
|
* @date August 2022
|
||||||
|
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||||
|
*/
|
||||||
|
#include <windows.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "bacnet/datalink/bsc/bsc-event.h"
|
||||||
|
|
||||||
|
#define DEBUG_BSC_EVENT 0
|
||||||
|
|
||||||
|
#if DEBUG_BSC_EVENT == 1
|
||||||
|
#define DEBUG_PRINTF printf
|
||||||
|
#else
|
||||||
|
#undef DEBUG_ENABLED
|
||||||
|
#define DEBUG_PRINTF(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct BSC_Event {
|
||||||
|
HANDLE mutex;
|
||||||
|
HANDLE event;
|
||||||
|
size_t counter;
|
||||||
|
};
|
||||||
|
|
||||||
|
BSC_EVENT *bsc_event_init(void)
|
||||||
|
{
|
||||||
|
BSC_EVENT *ret;
|
||||||
|
|
||||||
|
ret = (BSC_EVENT *)malloc(sizeof(BSC_EVENT));
|
||||||
|
if (!ret) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret->mutex = CreateMutex(
|
||||||
|
NULL, // default security attributes
|
||||||
|
FALSE, // initially not owned
|
||||||
|
NULL); // unnamed mutex
|
||||||
|
|
||||||
|
if (ret->mutex == NULL) {
|
||||||
|
free(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret->event = CreateEvent(
|
||||||
|
NULL, // default security attributes
|
||||||
|
TRUE, // manual-reset event
|
||||||
|
FALSE, // initial state is nonsignaled
|
||||||
|
NULL // unnamed event
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ret->event == NULL) {
|
||||||
|
CloseHandle(ret->mutex);
|
||||||
|
free(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret->counter = 0;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsc_event_deinit(BSC_EVENT *ev)
|
||||||
|
{
|
||||||
|
CloseHandle(ev->mutex);
|
||||||
|
CloseHandle(ev->event);
|
||||||
|
free(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsc_event_wait(BSC_EVENT *ev)
|
||||||
|
{
|
||||||
|
DEBUG_PRINTF("bsc_event_wait() >>> ev = %p\n", ev);
|
||||||
|
WaitForSingleObject(ev->mutex, INFINITE);
|
||||||
|
DEBUG_PRINTF("bsc_event_wait() counter before %zu\n", ev->counter);
|
||||||
|
ev->counter++;
|
||||||
|
ReleaseMutex(ev->mutex);
|
||||||
|
WaitForSingleObject(ev->event, INFINITE);
|
||||||
|
WaitForSingleObject(ev->mutex, INFINITE);
|
||||||
|
DEBUG_PRINTF("bsc_event_wait() before counter %zu\n", ev->counter);
|
||||||
|
ev->counter--;
|
||||||
|
DEBUG_PRINTF("bsc_event_wait() counter %zu\n", ev->counter);
|
||||||
|
if (!ev->counter) {
|
||||||
|
DEBUG_PRINTF("bsc_event_wait() reset event\n");
|
||||||
|
ReleaseMutex(ev->mutex);
|
||||||
|
ResetEvent(ev->event);
|
||||||
|
} else {
|
||||||
|
DEBUG_PRINTF("bsc_event_wait() set event\n");
|
||||||
|
ReleaseMutex(ev->mutex);
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF("bsc_event_wait() <<< ev = %p\n", ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bsc_event_timedwait(BSC_EVENT *ev, unsigned int ms_timeout)
|
||||||
|
{
|
||||||
|
bool timedout = false;
|
||||||
|
DWORD ret;
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bsc_event_timedwait() >>> ev = %p\n", ev);
|
||||||
|
WaitForSingleObject(ev->mutex, INFINITE);
|
||||||
|
DEBUG_PRINTF("bsc_event_timedwait() counter before %zu\n", ev->counter);
|
||||||
|
ev->counter++;
|
||||||
|
DEBUG_PRINTF("bsc_event_timedwait() counter %zu\n", ev->counter);
|
||||||
|
ReleaseMutex(ev->mutex);
|
||||||
|
|
||||||
|
ret = WaitForSingleObject(ev->event, ms_timeout);
|
||||||
|
WaitForSingleObject(ev->mutex, INFINITE);
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bsc_event_timedwait() before counter %zu\n", ev->counter);
|
||||||
|
ev->counter--;
|
||||||
|
DEBUG_PRINTF("bsc_event_timedwait() counter %zu\n", ev->counter);
|
||||||
|
if (!ev->counter) {
|
||||||
|
ReleaseMutex(ev->mutex);
|
||||||
|
if (ret == WAIT_OBJECT_0) {
|
||||||
|
ResetEvent(ev->event);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ReleaseMutex(ev->mutex);
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bsc_event_timedwait() <<< ret = %d\n",
|
||||||
|
ret == WAIT_OBJECT_0 ? true : false);
|
||||||
|
return ret == WAIT_OBJECT_0 ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsc_event_signal(BSC_EVENT *ev)
|
||||||
|
{
|
||||||
|
DEBUG_PRINTF("bsc_event_signal() >>> ev = %p\n", ev);
|
||||||
|
SetEvent(ev->event);
|
||||||
|
DEBUG_PRINTF("bsc_event_signal() <<< ev = %p\n", ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsc_wait(int seconds)
|
||||||
|
{
|
||||||
|
Sleep(seconds * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsc_wait_ms(int mseconds)
|
||||||
|
{
|
||||||
|
Sleep(mseconds);
|
||||||
|
}
|
||||||
@@ -0,0 +1,769 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Implementation of websocket client interface.
|
||||||
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||||
|
* @date May 2022
|
||||||
|
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||||
|
*/
|
||||||
|
#include <libwebsockets.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "bacnet/datalink/bsc/websocket.h"
|
||||||
|
#include "bacnet/basic/sys/debug.h"
|
||||||
|
#include "websocket-global.h"
|
||||||
|
|
||||||
|
#define DEBUG_WEBSOCKET_CLIENT 0
|
||||||
|
|
||||||
|
#if DEBUG_WEBSOCKET_CLIENT == 1
|
||||||
|
#define DEBUG_PRINTF debug_printf
|
||||||
|
#else
|
||||||
|
#undef DEBUG_ENABLED
|
||||||
|
#define DEBUG_PRINTF(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LWS_PROTOCOL_LIST_TERM
|
||||||
|
#define LWS_PROTOCOL_LIST_TERM \
|
||||||
|
{ \
|
||||||
|
NULL, NULL, 0, 0, 0, NULL, 0 \
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BSC_RX_BUFFER_LEN BSC_WEBSOCKET_RX_BUFFER_LEN
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BSC_WEBSOCKET_STATE_IDLE = 0,
|
||||||
|
BSC_WEBSOCKET_STATE_CONNECTING = 1,
|
||||||
|
BSC_WEBSOCKET_STATE_CONNECTED = 2,
|
||||||
|
BSC_WEBSOCKET_STATE_DISCONNECTING = 3
|
||||||
|
} BSC_WEBSOCKET_STATE;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
struct lws_context *ctx;
|
||||||
|
struct lws *ws;
|
||||||
|
BSC_WEBSOCKET_STATE state;
|
||||||
|
bool want_send_data;
|
||||||
|
bool can_send_data;
|
||||||
|
BSC_WEBSOCKET_CLI_DISPATCH dispatch_func;
|
||||||
|
void *user_param;
|
||||||
|
uint8_t *fragment_buffer;
|
||||||
|
size_t fragment_buffer_size;
|
||||||
|
size_t fragment_buffer_len;
|
||||||
|
char err_desc[BSC_WEBSOCKET_ERR_DESC_STR_MAX_LEN];
|
||||||
|
BACNET_ERROR_CODE err_code;
|
||||||
|
} BSC_WEBSOCKET_CONNECTION;
|
||||||
|
|
||||||
|
/* Some forward function declarations */
|
||||||
|
|
||||||
|
static int bws_cli_websocket_event(
|
||||||
|
struct lws *wsi,
|
||||||
|
enum lws_callback_reasons reason,
|
||||||
|
void *user,
|
||||||
|
void *in,
|
||||||
|
size_t len);
|
||||||
|
|
||||||
|
static const char *bws_hub_protocol = BSC_WEBSOCKET_HUB_PROTOCOL_STR;
|
||||||
|
static const char *bws_direct_protocol = BSC_WEBSOCKET_DIRECT_PROTOCOL_STR;
|
||||||
|
|
||||||
|
static HANDLE bws_cli_mutex = NULL;
|
||||||
|
|
||||||
|
/* Websockets protocol defined in BACnet/SC \S AB.7.1. */
|
||||||
|
|
||||||
|
static struct lws_protocols bws_cli_direct_protocol[] = {
|
||||||
|
{ BSC_WEBSOCKET_DIRECT_PROTOCOL_STR, bws_cli_websocket_event, 0, 0, 0, NULL,
|
||||||
|
0 },
|
||||||
|
LWS_PROTOCOL_LIST_TERM
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct lws_protocols bws_cli_hub_protocol[] = {
|
||||||
|
{ BSC_WEBSOCKET_HUB_PROTOCOL_STR, bws_cli_websocket_event, 0, 0, 0, NULL,
|
||||||
|
0 },
|
||||||
|
LWS_PROTOCOL_LIST_TERM
|
||||||
|
};
|
||||||
|
|
||||||
|
static lws_retry_bo_t bws_retry;
|
||||||
|
|
||||||
|
static BSC_WEBSOCKET_CONNECTION bws_cli_conn[BSC_CLIENT_WEBSOCKETS_MAX_NUM] = {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
static BSC_WEBSOCKET_HANDLE bws_cli_alloc_connection(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < BSC_CLIENT_WEBSOCKETS_MAX_NUM; i++) {
|
||||||
|
if (bws_cli_conn[i].state == BSC_WEBSOCKET_STATE_IDLE) {
|
||||||
|
memset(&bws_cli_conn[i], 0, sizeof(bws_cli_conn[i]));
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return BSC_WEBSOCKET_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bws_cli_free_connection(BSC_WEBSOCKET_HANDLE h)
|
||||||
|
{
|
||||||
|
if (h >= 0 && h < BSC_CLIENT_WEBSOCKETS_MAX_NUM) {
|
||||||
|
if (bws_cli_conn[h].state != BSC_WEBSOCKET_STATE_IDLE) {
|
||||||
|
if (bws_cli_conn[h].fragment_buffer) {
|
||||||
|
free(bws_cli_conn[h].fragment_buffer);
|
||||||
|
bws_cli_conn[h].fragment_buffer = NULL;
|
||||||
|
bws_cli_conn[h].fragment_buffer_len = 0;
|
||||||
|
bws_cli_conn[h].fragment_buffer_size = 0;
|
||||||
|
}
|
||||||
|
bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_IDLE;
|
||||||
|
bws_cli_conn[h].ws = NULL;
|
||||||
|
bws_cli_conn[h].ctx = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BSC_WEBSOCKET_HANDLE bws_cli_find_connnection(struct lws *ws)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < BSC_CLIENT_WEBSOCKETS_MAX_NUM; i++) {
|
||||||
|
if (bws_cli_conn[i].ws == ws &&
|
||||||
|
bws_cli_conn[i].state != BSC_WEBSOCKET_STATE_IDLE) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return BSC_WEBSOCKET_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bws_set_err_desc(BSC_WEBSOCKET_HANDLE h, char *err_desc)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
if (bws_cli_conn[h].err_code == ERROR_CODE_SUCCESS) {
|
||||||
|
len = strlen(err_desc) >= sizeof(bws_cli_conn[h].err_desc)
|
||||||
|
? sizeof(bws_cli_conn[h].err_desc) - 1
|
||||||
|
: strlen(err_desc);
|
||||||
|
|
||||||
|
memcpy(bws_cli_conn[h].err_desc, err_desc, len);
|
||||||
|
bws_cli_conn[h].err_desc[len] = 0;
|
||||||
|
|
||||||
|
if (strstr(err_desc, "tls:")) {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_TLS_ERROR;
|
||||||
|
} else {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bws_set_disconnect_reason(BSC_WEBSOCKET_HANDLE h, uint16_t err_code)
|
||||||
|
{
|
||||||
|
bws_cli_conn[h].err_desc[0] = 0;
|
||||||
|
switch (err_code) {
|
||||||
|
case LWS_CLOSE_STATUS_NORMAL: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_CLOSED_BY_PEER;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_GOINGAWAY: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_ENDPOINT_LEAVES;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_PROTOCOL_ERR: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_PROTOCOL_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_NOT_ACCEPTED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_NO_STATUS:
|
||||||
|
case LWS_CLOSE_STATUS_RESERVED: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_ABNORMAL_CLOSE: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_NOT_ACCEPTED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_INVALID_PAYLOAD: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_INCONSISTENT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_POLICY_VIOLATION: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_AGAINST_POLICY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_FRAME_TOO_LONG;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_EXTENSION_REQUIRED: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_EXTENSION_MISSING;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CLOSE_STATUS_UNEXPECTED_CONDITION: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_REQUEST_UNAVAILABLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bws_cli_websocket_event(
|
||||||
|
struct lws *wsi,
|
||||||
|
enum lws_callback_reasons reason,
|
||||||
|
void *user,
|
||||||
|
void *in,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
BSC_WEBSOCKET_HANDLE h;
|
||||||
|
BSC_WEBSOCKET_CLI_DISPATCH dispatch_func;
|
||||||
|
void *user_param;
|
||||||
|
uint8_t err_code[2];
|
||||||
|
|
||||||
|
(void)user;
|
||||||
|
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() >>> reason = %d, user = %p, in = %p\n",
|
||||||
|
reason, user, in);
|
||||||
|
|
||||||
|
switch (reason) {
|
||||||
|
case LWS_CALLBACK_CLIENT_ESTABLISHED: {
|
||||||
|
bsc_mutex_lock(&bws_cli_mutex);
|
||||||
|
h = bws_cli_find_connnection(wsi);
|
||||||
|
|
||||||
|
if (h == BSC_WEBSOCKET_INVALID_HANDLE) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() can not find websocket handle "
|
||||||
|
"for wsi %p\n",
|
||||||
|
wsi);
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = 0\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bws_cli_websocket_event() connection established\n");
|
||||||
|
bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_CONNECTED;
|
||||||
|
dispatch_func = bws_cli_conn[h].dispatch_func;
|
||||||
|
user_param = bws_cli_conn[h].user_param;
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
dispatch_func(
|
||||||
|
h, BSC_WEBSOCKET_CONNECTED, 0, NULL, NULL, 0, user_param);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CALLBACK_CLIENT_RECEIVE: {
|
||||||
|
bsc_mutex_lock(&bws_cli_mutex);
|
||||||
|
h = bws_cli_find_connnection(wsi);
|
||||||
|
|
||||||
|
if (h == BSC_WEBSOCKET_INVALID_HANDLE) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() can not find websocket handle "
|
||||||
|
"for wsi %p\n",
|
||||||
|
wsi);
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = 0\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() received %d bytes of data\n", len);
|
||||||
|
if (!lws_frame_is_binary(wsi)) {
|
||||||
|
/* According AB.7.5.3 BACnet/SC BVLC Message Exchange,
|
||||||
|
if a received data frame is not binary,
|
||||||
|
the WebSocket connection shall be closed with a
|
||||||
|
status code of 1003 -WEBSOCKET_DATA_NOT_ACCEPTED.
|
||||||
|
*/
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() got non-binary frame, "
|
||||||
|
"close connection for socket %d\n",
|
||||||
|
h);
|
||||||
|
lws_close_reason(
|
||||||
|
wsi, LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE, NULL, 0);
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = -1\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (bws_cli_conn[h].state != BSC_WEBSOCKET_STATE_CONNECTED) {
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
} else {
|
||||||
|
if (!bws_cli_conn[h].fragment_buffer) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() alloc %d bytes for "
|
||||||
|
"socket %d\n",
|
||||||
|
len, h);
|
||||||
|
bws_cli_conn[h].fragment_buffer = malloc(BSC_RX_BUFFER_LEN);
|
||||||
|
if (!bws_cli_conn[h].fragment_buffer) {
|
||||||
|
lws_close_reason(
|
||||||
|
wsi, LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE, NULL, 0);
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() <<< ret = -1, "
|
||||||
|
"allocation of %d bytes failed\n",
|
||||||
|
len <= BSC_RX_BUFFER_LEN ? BSC_RX_BUFFER_LEN : len);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
bws_cli_conn[h].fragment_buffer_len = 0;
|
||||||
|
bws_cli_conn[h].fragment_buffer_size = BSC_RX_BUFFER_LEN;
|
||||||
|
}
|
||||||
|
if (bws_cli_conn[h].fragment_buffer_len + len >
|
||||||
|
bws_cli_conn[h].fragment_buffer_size) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() realloc buf of %d bytes"
|
||||||
|
"for socket %d to %d bytes\n",
|
||||||
|
bws_cli_conn[h].fragment_buffer_len, h,
|
||||||
|
bws_cli_conn[h].fragment_buffer_len + len);
|
||||||
|
bws_cli_conn[h].fragment_buffer = realloc(
|
||||||
|
bws_cli_conn[h].fragment_buffer,
|
||||||
|
bws_cli_conn[h].fragment_buffer_len + len);
|
||||||
|
if (!bws_cli_conn[h].fragment_buffer) {
|
||||||
|
lws_close_reason(
|
||||||
|
wsi, LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE, NULL, 0);
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() <<< ret = -1, "
|
||||||
|
"re-allocation of %d bytes failed\n",
|
||||||
|
bws_cli_conn[h].fragment_buffer_len + len);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
bws_cli_conn[h].fragment_buffer_size =
|
||||||
|
bws_cli_conn[h].fragment_buffer_len + len;
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() got next %d bytes for "
|
||||||
|
"socket %d\n",
|
||||||
|
len, h);
|
||||||
|
memcpy(
|
||||||
|
&bws_cli_conn[h]
|
||||||
|
.fragment_buffer[bws_cli_conn[h].fragment_buffer_len],
|
||||||
|
in, len);
|
||||||
|
bws_cli_conn[h].fragment_buffer_len += len;
|
||||||
|
|
||||||
|
if (lws_is_final_fragment(wsi)) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() last fragment received\n");
|
||||||
|
dispatch_func = bws_cli_conn[h].dispatch_func;
|
||||||
|
user_param = bws_cli_conn[h].user_param;
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
dispatch_func(
|
||||||
|
h, BSC_WEBSOCKET_RECEIVED, 0, NULL,
|
||||||
|
&bws_cli_conn[h].fragment_buffer[0],
|
||||||
|
bws_cli_conn[h].fragment_buffer_len, user_param);
|
||||||
|
bsc_mutex_lock(&bws_cli_mutex);
|
||||||
|
bws_cli_conn[h].fragment_buffer_len = 0;
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
} else {
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CALLBACK_CLIENT_WRITEABLE: {
|
||||||
|
bsc_mutex_lock(&bws_cli_mutex);
|
||||||
|
h = bws_cli_find_connnection(wsi);
|
||||||
|
|
||||||
|
if (h == BSC_WEBSOCKET_INVALID_HANDLE) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() can not find websocket handle "
|
||||||
|
"for wsi %p\n",
|
||||||
|
wsi);
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = 0\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() can write, state = %d\n",
|
||||||
|
bws_cli_conn[h].state);
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() ws = %d, cs = %d\n",
|
||||||
|
bws_cli_conn[h].want_send_data, bws_cli_conn[h].can_send_data);
|
||||||
|
if (bws_cli_conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED &&
|
||||||
|
bws_cli_conn[h].want_send_data) {
|
||||||
|
bws_cli_conn[h].can_send_data = true;
|
||||||
|
dispatch_func = bws_cli_conn[h].dispatch_func;
|
||||||
|
user_param = bws_cli_conn[h].user_param;
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
dispatch_func(
|
||||||
|
h, BSC_WEBSOCKET_SENDABLE, 0, NULL, NULL, 0, user_param);
|
||||||
|
bsc_mutex_lock(&bws_cli_mutex);
|
||||||
|
bws_cli_conn[h].want_send_data = false;
|
||||||
|
bws_cli_conn[h].can_send_data = false;
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() was send , ws = %d, cs = %d\n",
|
||||||
|
bws_cli_conn[h].want_send_data,
|
||||||
|
bws_cli_conn[h].can_send_data);
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
/* wakeup worker to process internal state */
|
||||||
|
lws_cancel_service(bws_cli_conn[h].ctx);
|
||||||
|
} else {
|
||||||
|
bws_cli_conn[h].want_send_data = false;
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_websocket_event() no send, ws = %d, cs = %d\n",
|
||||||
|
bws_cli_conn[h].want_send_data,
|
||||||
|
bws_cli_conn[h].can_send_data);
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: {
|
||||||
|
bsc_mutex_lock(&bws_cli_mutex);
|
||||||
|
h = bws_cli_find_connnection(wsi);
|
||||||
|
if (h != BSC_WEBSOCKET_INVALID_HANDLE && len >= 2) {
|
||||||
|
err_code[0] = ((uint8_t *)in)[1];
|
||||||
|
err_code[1] = ((uint8_t *)in)[0];
|
||||||
|
bws_set_disconnect_reason(h, *((uint16_t *)&err_code));
|
||||||
|
}
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CALLBACK_CLIENT_CLOSED:
|
||||||
|
case LWS_CALLBACK_CLOSED:
|
||||||
|
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: {
|
||||||
|
bsc_mutex_lock(&bws_cli_mutex);
|
||||||
|
h = bws_cli_find_connnection(wsi);
|
||||||
|
if (h != BSC_WEBSOCKET_INVALID_HANDLE) {
|
||||||
|
bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING;
|
||||||
|
if (reason == LWS_CALLBACK_CLIENT_CONNECTION_ERROR && in) {
|
||||||
|
bws_set_err_desc(h, (char *)in);
|
||||||
|
}
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
/* wakeup worker to process pending event */
|
||||||
|
lws_cancel_service(bws_cli_conn[h].ctx);
|
||||||
|
} else {
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = 0\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DWORD WINAPI bws_cli_worker(LPVOID arg)
|
||||||
|
{
|
||||||
|
BSC_WEBSOCKET_CONNECTION *conn = (BSC_WEBSOCKET_CONNECTION *)arg;
|
||||||
|
BSC_WEBSOCKET_HANDLE h = (BSC_WEBSOCKET_HANDLE)(conn - &bws_cli_conn[0]);
|
||||||
|
BSC_WEBSOCKET_CLI_DISPATCH dispatch_func;
|
||||||
|
void *user_param;
|
||||||
|
char err_desc[BSC_WEBSOCKET_ERR_DESC_STR_MAX_LEN];
|
||||||
|
uint16_t err_code;
|
||||||
|
|
||||||
|
srand((unsigned)GetCurrentThreadId());
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
DEBUG_PRINTF("bws_cli_worker() try mutex h = %d\n", h);
|
||||||
|
bsc_mutex_lock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_worker() mutex locked h = %d\n", h);
|
||||||
|
if (conn->state == BSC_WEBSOCKET_STATE_CONNECTED) {
|
||||||
|
if (conn->want_send_data) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_worker() process request for sending data\n");
|
||||||
|
lws_callback_on_writable(conn->ws);
|
||||||
|
}
|
||||||
|
} else if (conn->state == BSC_WEBSOCKET_STATE_DISCONNECTING) {
|
||||||
|
DEBUG_PRINTF("bws_cli_worker() process disconnecting event\n");
|
||||||
|
DEBUG_PRINTF("bws_cli_worker() destroy ctx %p\n", conn->ctx);
|
||||||
|
/* TRICKY: Libwebsockets API is not designed to be used from
|
||||||
|
multipe service threads, as a result
|
||||||
|
lws_context_destroy() is not thread safe. More over, on different
|
||||||
|
platforms the function behaves in different ways. Call of
|
||||||
|
lws_context_destroy() leads to several calls of
|
||||||
|
bws_cli_websocket_event() callback (LWS_CALLBACK_CLOSED,
|
||||||
|
etc..). But under some OS (MacOSx) that callback is
|
||||||
|
called from context of the bws_cli_worker() thread and
|
||||||
|
under some other OS (linux) the callback is called from
|
||||||
|
internal libwebsockets lib thread. That's why
|
||||||
|
bws_cli_mutex must be unlocked before
|
||||||
|
lws_context_destroy() call. To ensure that nobody calls
|
||||||
|
lws_context_destroy() from some parallel thread it is
|
||||||
|
protected by global websocket mutex.
|
||||||
|
*/
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
bsc_websocket_global_lock();
|
||||||
|
lws_context_destroy(conn->ctx);
|
||||||
|
bsc_websocket_global_unlock();
|
||||||
|
bsc_mutex_lock(&bws_cli_mutex);
|
||||||
|
dispatch_func = conn->dispatch_func;
|
||||||
|
user_param = conn->user_param;
|
||||||
|
err_code = conn->err_code;
|
||||||
|
if (err_code != ERROR_CODE_SUCCESS) {
|
||||||
|
memcpy(err_desc, conn->err_desc, sizeof(err_desc));
|
||||||
|
}
|
||||||
|
bws_cli_free_connection(h);
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_worker() unlock mutex\n");
|
||||||
|
dispatch_func(
|
||||||
|
h, BSC_WEBSOCKET_DISCONNECTED, err_code,
|
||||||
|
err_code != ERROR_CODE_SUCCESS ? err_desc : NULL, NULL, 0,
|
||||||
|
user_param);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF("bws_cli_worker() unlock mutex\n");
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_worker() going to block on lws_service() call\n");
|
||||||
|
lws_service(conn->ctx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BSC_WEBSOCKET_RET bws_cli_connect(
|
||||||
|
BSC_WEBSOCKET_PROTOCOL proto,
|
||||||
|
char *url,
|
||||||
|
uint8_t *ca_cert,
|
||||||
|
size_t ca_cert_size,
|
||||||
|
uint8_t *cert,
|
||||||
|
size_t cert_size,
|
||||||
|
uint8_t *key,
|
||||||
|
size_t key_size,
|
||||||
|
size_t timeout_s,
|
||||||
|
BSC_WEBSOCKET_CLI_DISPATCH dispatch_func,
|
||||||
|
void *dispatch_func_user_param,
|
||||||
|
BSC_WEBSOCKET_HANDLE *out_handle)
|
||||||
|
{
|
||||||
|
struct lws_context_creation_info info;
|
||||||
|
char tmp_url[BSC_WSURL_MAX_LEN];
|
||||||
|
const char *prot = NULL, *addr = NULL, *path = NULL;
|
||||||
|
int port = -1;
|
||||||
|
BSC_WEBSOCKET_HANDLE h;
|
||||||
|
struct lws_client_connect_info cinfo;
|
||||||
|
HANDLE thread;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bws_cli_connect() >>> proto = %d, url = %s\n", proto, url);
|
||||||
|
|
||||||
|
if (!ca_cert || !ca_cert_size || !cert || !cert_size || !key || !key_size ||
|
||||||
|
!url || !out_handle || !timeout_s || !dispatch_func) {
|
||||||
|
DEBUG_PRINTF("bws_cli_connect() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n");
|
||||||
|
return BSC_WEBSOCKET_BAD_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_handle = BSC_WEBSOCKET_INVALID_HANDLE;
|
||||||
|
|
||||||
|
if (proto != BSC_WEBSOCKET_HUB_PROTOCOL &&
|
||||||
|
proto != BSC_WEBSOCKET_DIRECT_PROTOCOL) {
|
||||||
|
DEBUG_PRINTF("bws_cli_connect() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n");
|
||||||
|
return BSC_WEBSOCKET_BAD_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&info, 0, sizeof(info));
|
||||||
|
memset(&cinfo, 0, sizeof(cinfo));
|
||||||
|
|
||||||
|
len = strlen(url) >= sizeof(tmp_url) ? sizeof(tmp_url) - 1 : strlen(url);
|
||||||
|
memcpy(tmp_url, url, len);
|
||||||
|
tmp_url[len] = 0;
|
||||||
|
|
||||||
|
bsc_websocket_init_log();
|
||||||
|
bsc_mutex_lock(&bws_cli_mutex);
|
||||||
|
|
||||||
|
if (lws_parse_uri(tmp_url, &prot, &addr, &port, &path) != 0 || port == -1 ||
|
||||||
|
!prot || !addr || !path) {
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_connect() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n");
|
||||||
|
return BSC_WEBSOCKET_BAD_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(prot, "wss") != 0) {
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_connect() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n");
|
||||||
|
return BSC_WEBSOCKET_BAD_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
h = bws_cli_alloc_connection();
|
||||||
|
|
||||||
|
if (h == BSC_WEBSOCKET_INVALID_HANDLE) {
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_connect() <<< ret = BSC_WEBSOCKET_NO_RESOURCES\n");
|
||||||
|
return BSC_WEBSOCKET_NO_RESOURCES;
|
||||||
|
}
|
||||||
|
bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_CONNECTING;
|
||||||
|
bws_cli_conn[h].fragment_buffer = NULL;
|
||||||
|
bws_cli_conn[h].fragment_buffer_len = 0;
|
||||||
|
bws_cli_conn[h].fragment_buffer_size = 0;
|
||||||
|
bws_cli_conn[h].dispatch_func = dispatch_func;
|
||||||
|
bws_cli_conn[h].user_param = dispatch_func_user_param;
|
||||||
|
info.port = CONTEXT_PORT_NO_LISTEN;
|
||||||
|
if (proto == BSC_WEBSOCKET_HUB_PROTOCOL) {
|
||||||
|
info.protocols = bws_cli_hub_protocol;
|
||||||
|
} else {
|
||||||
|
info.protocols = bws_cli_direct_protocol;
|
||||||
|
}
|
||||||
|
info.gid = -1;
|
||||||
|
info.uid = -1;
|
||||||
|
info.client_ssl_cert_mem = cert;
|
||||||
|
info.client_ssl_cert_mem_len = (unsigned int)cert_size;
|
||||||
|
info.client_ssl_ca_mem = ca_cert;
|
||||||
|
info.client_ssl_ca_mem_len = (unsigned int)ca_cert_size;
|
||||||
|
info.client_ssl_key_mem = key;
|
||||||
|
info.client_ssl_key_mem_len = (unsigned int)key_size;
|
||||||
|
info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
|
||||||
|
info.options |= LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND;
|
||||||
|
info.timeout_secs = (unsigned int)timeout_s;
|
||||||
|
info.connect_timeout_secs = (unsigned int)timeout_s;
|
||||||
|
|
||||||
|
/* TRICKY: check comments related to lws_context_destroy() call */
|
||||||
|
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
bsc_websocket_global_lock();
|
||||||
|
bws_cli_conn[h].ctx = lws_create_context(&info);
|
||||||
|
bsc_websocket_global_unlock();
|
||||||
|
bsc_mutex_lock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_connect() created ctx %p\n", bws_cli_conn[h].ctx);
|
||||||
|
|
||||||
|
if (!bws_cli_conn[h].ctx) {
|
||||||
|
bws_cli_free_connection(h);
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_connect() <<< ret = BSC_WEBSOCKET_NO_RESOURCES\n");
|
||||||
|
return BSC_WEBSOCKET_NO_RESOURCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
bws_cli_conn[h].ws = NULL;
|
||||||
|
cinfo.context = bws_cli_conn[h].ctx;
|
||||||
|
cinfo.address = addr;
|
||||||
|
cinfo.origin = cinfo.address;
|
||||||
|
cinfo.host = cinfo.address;
|
||||||
|
cinfo.port = port;
|
||||||
|
cinfo.path = path;
|
||||||
|
cinfo.pwsi = &bws_cli_conn[h].ws;
|
||||||
|
cinfo.alpn = "h2;http/1.1";
|
||||||
|
bws_retry.secs_since_valid_ping = 3;
|
||||||
|
bws_retry.secs_since_valid_hangup = 10;
|
||||||
|
cinfo.retry_and_idle_policy = &bws_retry;
|
||||||
|
cinfo.ssl_connection = LCCSCF_USE_SSL |
|
||||||
|
LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK | LCCSCF_ALLOW_SELFSIGNED;
|
||||||
|
|
||||||
|
if (proto == BSC_WEBSOCKET_HUB_PROTOCOL) {
|
||||||
|
cinfo.protocol = bws_hub_protocol;
|
||||||
|
} else {
|
||||||
|
cinfo.protocol = bws_direct_protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
bws_cli_conn[h].err_code = ERROR_CODE_SUCCESS;
|
||||||
|
*out_handle = h;
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
bsc_websocket_global_lock();
|
||||||
|
lws_client_connect_via_info(&cinfo);
|
||||||
|
bsc_websocket_global_unlock();
|
||||||
|
|
||||||
|
thread = CreateThread(NULL, 0, &bws_cli_worker, &bws_cli_conn[h], 0, NULL);
|
||||||
|
|
||||||
|
if (thread == NULL) {
|
||||||
|
/* TRICKY: Libwebsockets API is not designed to be used from
|
||||||
|
multipe service threads, as a result lws_context_destroy()
|
||||||
|
is not thread safe. More over, on different platforms the
|
||||||
|
function behaves in different ways. Call of
|
||||||
|
lws_context_destroy() leads to several calls of
|
||||||
|
bws_cli_websocket_event() callback (LWS_CALLBACK_CLOSED,
|
||||||
|
etc..). But under some OS (MacOSx) that callback is
|
||||||
|
called from context of the bws_cli_worker() thread and
|
||||||
|
under some other OS (linux) the callback is called from
|
||||||
|
internal libwebsockets lib thread. That's why
|
||||||
|
bws_cli_mutex must be unlocked before
|
||||||
|
lws_context_destroy() call. To ensure that nobody calls
|
||||||
|
lws_context_destroy() from some parallel thread it is
|
||||||
|
protected by global websocket mutex.
|
||||||
|
*/
|
||||||
|
bsc_websocket_global_lock();
|
||||||
|
lws_context_destroy(bws_cli_conn[h].ctx);
|
||||||
|
bsc_websocket_global_unlock();
|
||||||
|
bsc_mutex_lock(&bws_cli_mutex);
|
||||||
|
bws_cli_free_connection(h);
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_connect() <<< ret = BSC_WEBSOCKET_NO_RESOURCES\n");
|
||||||
|
return BSC_WEBSOCKET_NO_RESOURCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bws_cli_connect() <<< ret = %d\n", BSC_WEBSOCKET_SUCCESS);
|
||||||
|
return BSC_WEBSOCKET_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bws_cli_disconnect(BSC_WEBSOCKET_HANDLE h)
|
||||||
|
{
|
||||||
|
DEBUG_PRINTF("bws_cli_disconnect() >>> h = %d\n", h);
|
||||||
|
|
||||||
|
if (h >= 0 && h < BSC_CLIENT_WEBSOCKETS_MAX_NUM) {
|
||||||
|
bsc_mutex_lock(&bws_cli_mutex);
|
||||||
|
|
||||||
|
if (bws_cli_conn[h].state == BSC_WEBSOCKET_STATE_CONNECTING ||
|
||||||
|
bws_cli_conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED) {
|
||||||
|
/* tell worker to process change of connection state */
|
||||||
|
bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING;
|
||||||
|
lws_cancel_service(bws_cli_conn[h].ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bws_cli_disconnect() <<<\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void bws_cli_send(BSC_WEBSOCKET_HANDLE h)
|
||||||
|
{
|
||||||
|
DEBUG_PRINTF("bws_cli_send() >>> h = %d\n", h);
|
||||||
|
|
||||||
|
if (h >= 0 && h < BSC_CLIENT_WEBSOCKETS_MAX_NUM) {
|
||||||
|
bsc_mutex_lock(&bws_cli_mutex);
|
||||||
|
|
||||||
|
if (bws_cli_conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED) {
|
||||||
|
/* tell worker to process send request */
|
||||||
|
bws_cli_conn[h].want_send_data = true;
|
||||||
|
DEBUG_PRINTF("bws_cli_send() cs = 1\n");
|
||||||
|
lws_cancel_service(bws_cli_conn[h].ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bws_cli_send() <<<\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
BSC_WEBSOCKET_RET bws_cli_dispatch_send(
|
||||||
|
BSC_WEBSOCKET_HANDLE h, uint8_t *payload, size_t payload_size)
|
||||||
|
{
|
||||||
|
int written;
|
||||||
|
BSC_WEBSOCKET_RET ret;
|
||||||
|
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_dispatch_send() >>> h = %d, payload = %p, payload_size = %d\n",
|
||||||
|
h, payload, payload_size);
|
||||||
|
|
||||||
|
if (h < 0 || h >= BSC_CLIENT_WEBSOCKETS_MAX_NUM) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_dispatch_send() <<< ret = BACNET_WEBSOCKET_BAD_PARAM\n");
|
||||||
|
return BSC_WEBSOCKET_BAD_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
bsc_mutex_lock(&bws_cli_mutex);
|
||||||
|
|
||||||
|
if ((bws_cli_conn[h].state != BSC_WEBSOCKET_STATE_CONNECTED) ||
|
||||||
|
!bws_cli_conn[h].want_send_data || !bws_cli_conn[h].can_send_data) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_dispatch_send() state = %d, ws = %d, cs = %d\n",
|
||||||
|
bws_cli_conn[h].state, bws_cli_conn[h].want_send_data,
|
||||||
|
bws_cli_conn[h].can_send_data);
|
||||||
|
DEBUG_PRINTF("bws_cli_dispatch_send() <<< ret = "
|
||||||
|
"BSC_WEBSOCKET_INVALID_OPERATION\n");
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
return BSC_WEBSOCKET_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
written =
|
||||||
|
lws_write(bws_cli_conn[h].ws, payload, payload_size, LWS_WRITE_BINARY);
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bws_cli_dispatch_send() %d bytes is sent\n", written);
|
||||||
|
|
||||||
|
if (written < (int)payload_size) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bws_cli_dispatch_send() websocket connection is broken(closed)\n");
|
||||||
|
/* tell worker to process change of connection state */
|
||||||
|
bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING;
|
||||||
|
lws_cancel_service(bws_cli_conn[h].ctx);
|
||||||
|
ret = BSC_WEBSOCKET_INVALID_OPERATION;
|
||||||
|
} else {
|
||||||
|
ret = BSC_WEBSOCKET_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
bsc_mutex_unlock(&bws_cli_mutex);
|
||||||
|
DEBUG_PRINTF("bws_cli_dispatch_send() <<< ret = %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Implementation of global websocket functions.
|
||||||
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||||
|
* @date May 2022
|
||||||
|
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||||
|
*/
|
||||||
|
#include <libwebsockets.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include "websocket-global.h"
|
||||||
|
|
||||||
|
static HANDLE websocket_mutex = NULL;
|
||||||
|
static HANDLE websocket_dispatch_mutex = NULL;
|
||||||
|
|
||||||
|
static void bsc_init_mutex(volatile HANDLE *m)
|
||||||
|
{
|
||||||
|
HANDLE p;
|
||||||
|
if (*m == NULL) {
|
||||||
|
p = CreateMutex(NULL, FALSE, NULL);
|
||||||
|
if (InterlockedCompareExchangePointer((PVOID *)m, (PVOID)p, NULL) !=
|
||||||
|
NULL) {
|
||||||
|
CloseHandle(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsc_mutex_lock(volatile HANDLE *m)
|
||||||
|
{
|
||||||
|
bsc_init_mutex(m);
|
||||||
|
WaitForSingleObject(*m, INFINITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsc_mutex_unlock(volatile HANDLE *m)
|
||||||
|
{
|
||||||
|
ReleaseMutex(*m);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsc_websocket_global_lock(void)
|
||||||
|
{
|
||||||
|
bsc_mutex_lock(&websocket_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsc_websocket_global_unlock(void)
|
||||||
|
{
|
||||||
|
bsc_mutex_unlock(&websocket_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bws_dispatch_lock(void)
|
||||||
|
{
|
||||||
|
bsc_mutex_lock(&websocket_dispatch_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bws_dispatch_unlock(void)
|
||||||
|
{
|
||||||
|
bsc_mutex_unlock(&websocket_dispatch_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL bsc_websocket_log_initialized = FALSE;
|
||||||
|
|
||||||
|
void bsc_websocket_init_log(void)
|
||||||
|
{
|
||||||
|
bsc_websocket_global_lock();
|
||||||
|
if (!bsc_websocket_log_initialized) {
|
||||||
|
bsc_websocket_log_initialized = TRUE;
|
||||||
|
#if DEBUG_LIBWEBSOCKETS_ENABLED == 1
|
||||||
|
printf("LWS_MAX_SMP = %d", LWS_MAX_SMP);
|
||||||
|
lws_set_log_level(
|
||||||
|
LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_INFO | LLL_DEBUG |
|
||||||
|
LLL_PARSER | LLL_HEADER | LLL_EXT | LLL_CLIENT | LLL_LATENCY |
|
||||||
|
LLL_USER | LLL_THREAD,
|
||||||
|
NULL);
|
||||||
|
#else
|
||||||
|
lws_set_log_level(0, NULL);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
bsc_websocket_global_unlock();
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Global websocket functions.
|
||||||
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||||
|
* @date May 2022
|
||||||
|
* @copyright SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
#ifndef __BSC_WEBSOCKET_MUTEX_INCLUDED__
|
||||||
|
#define __BSC_WEBSOCKET_MUTEX_INCLUDED__
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
void bsc_mutex_lock(volatile HANDLE *m);
|
||||||
|
void bsc_mutex_unlock(volatile HANDLE *m);
|
||||||
|
void bsc_websocket_global_lock(void);
|
||||||
|
void bsc_websocket_global_unlock(void);
|
||||||
|
void bsc_websocket_init_log(void);
|
||||||
|
#endif
|
||||||
File diff suppressed because it is too large
Load Diff
+80
-2
@@ -17,12 +17,13 @@
|
|||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
#include <wctype.h>
|
#include <wctype.h>
|
||||||
#endif
|
#endif
|
||||||
|
/* BACnet Stack defines - first */
|
||||||
|
#include "bacnet/bacdef.h"
|
||||||
|
/* BACnet Stack API */
|
||||||
#include "bacnet/access_rule.h"
|
#include "bacnet/access_rule.h"
|
||||||
#include "bacnet/bacenum.h"
|
|
||||||
#include "bacnet/bacdcode.h"
|
#include "bacnet/bacdcode.h"
|
||||||
#include "bacnet/bacint.h"
|
#include "bacnet/bacint.h"
|
||||||
#include "bacnet/bacreal.h"
|
#include "bacnet/bacreal.h"
|
||||||
#include "bacnet/bacdef.h"
|
|
||||||
#include "bacnet/bacapp.h"
|
#include "bacnet/bacapp.h"
|
||||||
#include "bacnet/bactext.h"
|
#include "bacnet/bactext.h"
|
||||||
#include "bacnet/datetime.h"
|
#include "bacnet/datetime.h"
|
||||||
@@ -30,6 +31,7 @@
|
|||||||
#include "bacnet/bacaction.h"
|
#include "bacnet/bacaction.h"
|
||||||
#include "bacnet/lighting.h"
|
#include "bacnet/lighting.h"
|
||||||
#include "bacnet/hostnport.h"
|
#include "bacnet/hostnport.h"
|
||||||
|
#include "bacnet/secure_connect.h"
|
||||||
#include "bacnet/weeklyschedule.h"
|
#include "bacnet/weeklyschedule.h"
|
||||||
#include "bacnet/calendar_entry.h"
|
#include "bacnet/calendar_entry.h"
|
||||||
#include "bacnet/special_event.h"
|
#include "bacnet/special_event.h"
|
||||||
@@ -518,6 +520,24 @@ int bacapp_encode_application_data(
|
|||||||
apdu_len = bacnet_channel_value_type_encode(
|
apdu_len = bacnet_channel_value_type_encode(
|
||||||
apdu, &value->type.Channel_Value);
|
apdu, &value->type.Channel_Value);
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
#if defined(BACAPP_SECURE_CONNECT)
|
||||||
|
case BACNET_APPLICATION_TAG_SC_FAILED_CONNECTION_REQUEST:
|
||||||
|
apdu_len = bacapp_encode_SCFailedConnectionRequest(
|
||||||
|
apdu, &value->type.SC_Failed_Req);
|
||||||
|
break;
|
||||||
|
case BACNET_APPLICATION_TAG_SC_HUB_FUNCTION_CONNECTION_STATUS:
|
||||||
|
apdu_len = bacapp_encode_SCHubFunctionConnection(
|
||||||
|
apdu, &value->type.SC_Hub_Function_Status);
|
||||||
|
break;
|
||||||
|
case BACNET_APPLICATION_TAG_SC_DIRECT_CONNECTION_STATUS:
|
||||||
|
apdu_len = bacapp_encode_SCDirectConnection(
|
||||||
|
apdu, &value->type.SC_Direct_Status);
|
||||||
|
break;
|
||||||
|
case BACNET_APPLICATION_TAG_SC_HUB_CONNECTION_STATUS:
|
||||||
|
apdu_len = bacapp_encode_SCHubConnection(
|
||||||
|
apdu, &value->type.SC_Hub_Status);
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -535,6 +555,7 @@ int bacapp_encode_application_data(
|
|||||||
* @param len_value_type Count of bytes of given tag
|
* @param len_value_type Count of bytes of given tag
|
||||||
* @param value Pointer to the application value structure,
|
* @param value Pointer to the application value structure,
|
||||||
* used to store the decoded value to.
|
* used to store the decoded value to.
|
||||||
|
* @note Decodes only the 13 primitive application data types!
|
||||||
*
|
*
|
||||||
* @return Number of octets consumed (could be zero).
|
* @return Number of octets consumed (could be zero).
|
||||||
* Parameter value->tag set to MAX_BACNET_APPLICATION_TAG when
|
* Parameter value->tag set to MAX_BACNET_APPLICATION_TAG when
|
||||||
@@ -936,6 +957,10 @@ int bacapp_encode_context_data_value(
|
|||||||
case BACNET_APPLICATION_TAG_SCALE:
|
case BACNET_APPLICATION_TAG_SCALE:
|
||||||
case BACNET_APPLICATION_TAG_SHED_LEVEL:
|
case BACNET_APPLICATION_TAG_SHED_LEVEL:
|
||||||
case BACNET_APPLICATION_TAG_ACCESS_RULE:
|
case BACNET_APPLICATION_TAG_ACCESS_RULE:
|
||||||
|
case BACNET_APPLICATION_TAG_SC_FAILED_CONNECTION_REQUEST:
|
||||||
|
case BACNET_APPLICATION_TAG_SC_HUB_FUNCTION_CONNECTION_STATUS:
|
||||||
|
case BACNET_APPLICATION_TAG_SC_DIRECT_CONNECTION_STATUS:
|
||||||
|
case BACNET_APPLICATION_TAG_SC_HUB_CONNECTION_STATUS:
|
||||||
/* complex data is enclosed in open/close tags */
|
/* complex data is enclosed in open/close tags */
|
||||||
len = encode_opening_tag(apdu, context_tag_number);
|
len = encode_opening_tag(apdu, context_tag_number);
|
||||||
apdu_len += len;
|
apdu_len += len;
|
||||||
@@ -1316,6 +1341,19 @@ int bacapp_known_property_tag(
|
|||||||
/* BACnetAccessRule */
|
/* BACnetAccessRule */
|
||||||
return BACNET_APPLICATION_TAG_ACCESS_RULE;
|
return BACNET_APPLICATION_TAG_ACCESS_RULE;
|
||||||
|
|
||||||
|
case PROP_SC_FAILED_CONNECTION_REQUESTS:
|
||||||
|
return BACNET_APPLICATION_TAG_SC_FAILED_CONNECTION_REQUEST;
|
||||||
|
|
||||||
|
case PROP_SC_HUB_FUNCTION_CONNECTION_STATUS:
|
||||||
|
return BACNET_APPLICATION_TAG_SC_HUB_FUNCTION_CONNECTION_STATUS;
|
||||||
|
|
||||||
|
case PROP_SC_DIRECT_CONNECT_CONNECTION_STATUS:
|
||||||
|
return BACNET_APPLICATION_TAG_SC_DIRECT_CONNECTION_STATUS;
|
||||||
|
|
||||||
|
case PROP_SC_PRIMARY_HUB_CONNECTION_STATUS:
|
||||||
|
case PROP_SC_FAILOVER_HUB_CONNECTION_STATUS:
|
||||||
|
return BACNET_APPLICATION_TAG_SC_HUB_CONNECTION_STATUS;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -1625,6 +1663,24 @@ int bacapp_decode_application_tag_value(
|
|||||||
apdu_len = bacnet_channel_value_decode(
|
apdu_len = bacnet_channel_value_decode(
|
||||||
apdu, apdu_size, &value->type.Channel_Value);
|
apdu, apdu_size, &value->type.Channel_Value);
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
#if defined(BACAPP_SECURE_CONNECT)
|
||||||
|
case BACNET_APPLICATION_TAG_SC_FAILED_CONNECTION_REQUEST:
|
||||||
|
apdu_len = bacapp_decode_SCFailedConnectionRequest(
|
||||||
|
apdu, apdu_size, &value->type.SC_Failed_Req);
|
||||||
|
break;
|
||||||
|
case BACNET_APPLICATION_TAG_SC_HUB_FUNCTION_CONNECTION_STATUS:
|
||||||
|
apdu_len = bacapp_decode_SCHubFunctionConnection(
|
||||||
|
apdu, apdu_size, &value->type.SC_Hub_Function_Status);
|
||||||
|
break;
|
||||||
|
case BACNET_APPLICATION_TAG_SC_DIRECT_CONNECTION_STATUS:
|
||||||
|
apdu_len = bacapp_decode_SCDirectConnection(
|
||||||
|
apdu, apdu_size, &value->type.SC_Direct_Status);
|
||||||
|
break;
|
||||||
|
case BACNET_APPLICATION_TAG_SC_HUB_CONNECTION_STATUS:
|
||||||
|
apdu_len = bacapp_decode_SCHubConnection(
|
||||||
|
apdu, apdu_size, &value->type.SC_Hub_Status);
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -3539,6 +3595,28 @@ int bacapp_snprintf_value(
|
|||||||
str, str_len, &value->type.Object_Property_Reference);
|
str, str_len, &value->type.Object_Property_Reference);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(BACAPP_SECURE_CONNECT)
|
||||||
|
case BACNET_APPLICATION_TAG_SC_FAILED_CONNECTION_REQUEST:
|
||||||
|
ret_val = bacapp_snprintf_SCFailedConnectionRequest(
|
||||||
|
str, str_len, &value->type.SC_Failed_Req);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BACNET_APPLICATION_TAG_SC_HUB_FUNCTION_CONNECTION_STATUS:
|
||||||
|
ret_val = bacapp_snprintf_SCHubFunctionConnection(
|
||||||
|
str, str_len, &value->type.SC_Hub_Function_Status);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BACNET_APPLICATION_TAG_SC_DIRECT_CONNECTION_STATUS:
|
||||||
|
ret_val = bacapp_snprintf_SCDirectConnection(
|
||||||
|
str, str_len, &value->type.SC_Direct_Status);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BACNET_APPLICATION_TAG_SC_HUB_CONNECTION_STATUS:
|
||||||
|
ret_val = bacapp_snprintf_SCHubConnection(
|
||||||
|
str, str_len, &value->type.SC_Hub_Status);
|
||||||
|
break;
|
||||||
|
|
||||||
|
#endif
|
||||||
#if defined(BACAPP_DESTINATION)
|
#if defined(BACAPP_DESTINATION)
|
||||||
case BACNET_APPLICATION_TAG_DESTINATION:
|
case BACNET_APPLICATION_TAG_DESTINATION:
|
||||||
ret_val = bacnet_destination_to_ascii(
|
ret_val = bacnet_destination_to_ascii(
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include "bacnet/calendar_entry.h"
|
#include "bacnet/calendar_entry.h"
|
||||||
#include "bacnet/special_event.h"
|
#include "bacnet/special_event.h"
|
||||||
#include "bacnet/channel_value.h"
|
#include "bacnet/channel_value.h"
|
||||||
|
#include "bacnet/secure_connect.h"
|
||||||
|
|
||||||
#ifndef BACAPP_PRINT_ENABLED
|
#ifndef BACAPP_PRINT_ENABLED
|
||||||
#if PRINT_ENABLED
|
#if PRINT_ENABLED
|
||||||
@@ -167,6 +168,12 @@ typedef struct BACnet_Application_Data_Value {
|
|||||||
#endif
|
#endif
|
||||||
#if defined(BACAPP_CHANNEL_VALUE)
|
#if defined(BACAPP_CHANNEL_VALUE)
|
||||||
BACNET_CHANNEL_VALUE Channel_Value;
|
BACNET_CHANNEL_VALUE Channel_Value;
|
||||||
|
#endif
|
||||||
|
#if defined(BACAPP_SECURE_CONNECT)
|
||||||
|
BACNET_SC_FAILED_CONNECTION_REQUEST SC_Failed_Req;
|
||||||
|
BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS SC_Hub_Function_Status;
|
||||||
|
BACNET_SC_DIRECT_CONNECTION_STATUS SC_Direct_Status;
|
||||||
|
BACNET_SC_HUB_CONNECTION_STATUS SC_Hub_Status;
|
||||||
#endif
|
#endif
|
||||||
} type;
|
} type;
|
||||||
/* simple linked list if needed */
|
/* simple linked list if needed */
|
||||||
|
|||||||
+3
-1
@@ -148,7 +148,9 @@ typedef uint32_t BACNET_ARRAY_INDEX;
|
|||||||
Ethernet = 6 bytes
|
Ethernet = 6 bytes
|
||||||
BACnet/IPv4 = 6 bytes
|
BACnet/IPv4 = 6 bytes
|
||||||
LonTalk = 7 bytes
|
LonTalk = 7 bytes
|
||||||
BACnet/IPv6 = 3 bytes (VMAC) */
|
BACnet/IPv6 = 3 bytes (VMAC)
|
||||||
|
BACnet/SC = 6 bytes (VMAC)
|
||||||
|
*/
|
||||||
#define MAX_MAC_LEN 7
|
#define MAX_MAC_LEN 7
|
||||||
|
|
||||||
struct BACnet_Device_Address {
|
struct BACnet_Device_Address {
|
||||||
|
|||||||
@@ -1599,6 +1599,14 @@ typedef enum {
|
|||||||
BACNET_APPLICATION_TAG_XY_COLOR,
|
BACNET_APPLICATION_TAG_XY_COLOR,
|
||||||
/* BACnetColorCommand */
|
/* BACnetColorCommand */
|
||||||
BACNET_APPLICATION_TAG_COLOR_COMMAND,
|
BACNET_APPLICATION_TAG_COLOR_COMMAND,
|
||||||
|
/* BACNET_SC_FAILED_CONNECTION_REQUEST */
|
||||||
|
BACNET_APPLICATION_TAG_SC_FAILED_CONNECTION_REQUEST,
|
||||||
|
/* BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS */
|
||||||
|
BACNET_APPLICATION_TAG_SC_HUB_FUNCTION_CONNECTION_STATUS,
|
||||||
|
/* BACNET_SC_DIRECT_CONNECTION_STATUS */
|
||||||
|
BACNET_APPLICATION_TAG_SC_DIRECT_CONNECTION_STATUS,
|
||||||
|
/* BACNET_SC_HUB_CONNECTION_STATUS */
|
||||||
|
BACNET_APPLICATION_TAG_SC_HUB_CONNECTION_STATUS,
|
||||||
/* BACnetBDTEntry */
|
/* BACnetBDTEntry */
|
||||||
BACNET_APPLICATION_TAG_BDT_ENTRY,
|
BACNET_APPLICATION_TAG_BDT_ENTRY,
|
||||||
/* BACnetFDTEntry */
|
/* BACnetFDTEntry */
|
||||||
@@ -2172,6 +2180,10 @@ typedef enum {
|
|||||||
compilers will allocate adequate sized datatype for enum
|
compilers will allocate adequate sized datatype for enum
|
||||||
which is used to store decoding */
|
which is used to store decoding */
|
||||||
ERROR_CODE_PROPRIETARY_FIRST = 256,
|
ERROR_CODE_PROPRIETARY_FIRST = 256,
|
||||||
|
/* some error codes for internal stack usage */
|
||||||
|
ERROR_CODE_ = 65535,
|
||||||
|
ERROR_CODE_DISCARD = 65534,
|
||||||
|
ERROR_CODE_DEFAULT = 65535,
|
||||||
ERROR_CODE_PROPRIETARY_LAST = 65535
|
ERROR_CODE_PROPRIETARY_LAST = 65535
|
||||||
} BACNET_ERROR_CODE;
|
} BACNET_ERROR_CODE;
|
||||||
|
|
||||||
@@ -2592,6 +2604,10 @@ typedef enum {
|
|||||||
PORT_TYPE_NON_BACNET = 8,
|
PORT_TYPE_NON_BACNET = 8,
|
||||||
PORT_TYPE_BIP6 = 9,
|
PORT_TYPE_BIP6 = 9,
|
||||||
PORT_TYPE_SERIAL = 10,
|
PORT_TYPE_SERIAL = 10,
|
||||||
|
/* For BACnet/SC network port implementations with
|
||||||
|
a Protocol_Revision 24 and higher, BACnet/SC network ports shall be
|
||||||
|
represented by a Network Port object at the BACNET_APPLICATION
|
||||||
|
protocol level with network type of SECURE_CONNECT. */
|
||||||
PORT_TYPE_BSC = 11,
|
PORT_TYPE_BSC = 11,
|
||||||
/* Enumerated values 0-63 are reserved for definition by ASHRAE.
|
/* Enumerated values 0-63 are reserved for definition by ASHRAE.
|
||||||
Enumerated values 64-255 may be used by others subject to the
|
Enumerated values 64-255 may be used by others subject to the
|
||||||
@@ -2993,4 +3009,20 @@ typedef enum BACnetAuditOperation {
|
|||||||
AUDIT_OPERATION_PROPRIETARY_MAX = 63
|
AUDIT_OPERATION_PROPRIETARY_MAX = 63
|
||||||
} BACNET_AUDIT_OPERATION;
|
} BACNET_AUDIT_OPERATION;
|
||||||
|
|
||||||
|
typedef enum BACnetSCHubConnectorState {
|
||||||
|
/* FIXME: prefix with typedef name as much as possible */
|
||||||
|
BACNET_SC_HUB_CONNECTOR_STATE_NO_HUB_CONNECTION = 0,
|
||||||
|
BACNET_SC_HUB_CONNECTOR_STATE_CONNECTED_TO_PRIMARY = 1,
|
||||||
|
BACNET_SC_HUB_CONNECTOR_STATE_CONNECTED_TO_FAILOVER = 2,
|
||||||
|
BACNET_SC_HUB_CONNECTOR_STATE_MAX = 3
|
||||||
|
} BACNET_SC_HUB_CONNECTOR_STATE;
|
||||||
|
|
||||||
|
typedef enum BACnetSCConnectionState {
|
||||||
|
BACNET_SC_CONNECTION_STATE_NOT_CONNECTED = 0,
|
||||||
|
BACNET_SC_CONNECTION_STATE_CONNECTED = 1,
|
||||||
|
BACNET_SC_CONNECTION_STATE_DISCONNECTED_WITH_ERRORS = 2,
|
||||||
|
BACNET_SC_CONNECTION_STATE_FAILED_TO_CONNECT = 3,
|
||||||
|
BACNET_SC_CONNECTION_STATE_MAX = 4
|
||||||
|
} BACNET_SC_CONNECTION_STATE;
|
||||||
|
|
||||||
#endif /* end of BACENUM_H */
|
#endif /* end of BACENUM_H */
|
||||||
|
|||||||
@@ -133,6 +133,28 @@ static object_functions_t Object_Table[] = {
|
|||||||
NULL /* Create */,
|
NULL /* Create */,
|
||||||
NULL /* Delete */,
|
NULL /* Delete */,
|
||||||
NULL /* Timer */ },
|
NULL /* Timer */ },
|
||||||
|
#endif
|
||||||
|
#if defined(BACFILE)
|
||||||
|
{ OBJECT_FILE,
|
||||||
|
bacfile_init,
|
||||||
|
bacfile_count,
|
||||||
|
bacfile_index_to_instance,
|
||||||
|
bacfile_valid_instance,
|
||||||
|
bacfile_object_name,
|
||||||
|
bacfile_read_property,
|
||||||
|
bacfile_write_property,
|
||||||
|
BACfile_Property_Lists,
|
||||||
|
NULL /* ReadRangeInfo */,
|
||||||
|
NULL /* Iterator */,
|
||||||
|
NULL /* Value_Lists */,
|
||||||
|
NULL /* COV */,
|
||||||
|
NULL /* COV Clear */,
|
||||||
|
NULL /* Intrinsic Reporting */,
|
||||||
|
NULL /* Add_List_Element */,
|
||||||
|
NULL /* Remove_List_Element */,
|
||||||
|
NULL /* Create */,
|
||||||
|
NULL /* Delete */,
|
||||||
|
NULL /* Timer */ },
|
||||||
#endif
|
#endif
|
||||||
{ MAX_BACNET_OBJECT_TYPE,
|
{ MAX_BACNET_OBJECT_TYPE,
|
||||||
NULL /* Init */,
|
NULL /* Init */,
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include "bacnet/basic/object/device.h"
|
#include "bacnet/basic/object/device.h"
|
||||||
/* me */
|
/* me */
|
||||||
#include "bacnet/basic/object/netport.h"
|
#include "bacnet/basic/object/netport.h"
|
||||||
|
#include <bacnet/basic/object/netport_internal.h>
|
||||||
|
|
||||||
#if defined(BACDL_BIP6)
|
#if defined(BACDL_BIP6)
|
||||||
#include "bacnet/datalink/bvlc6.h"
|
#include "bacnet/datalink/bvlc6.h"
|
||||||
@@ -92,6 +93,11 @@ struct mstp_port {
|
|||||||
uint8_t Max_Info_Frames;
|
uint8_t Max_Info_Frames;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct bsc_port {
|
||||||
|
uint8_t MAC_Address[6];
|
||||||
|
BACNET_SC_PARAMS Parameters;
|
||||||
|
};
|
||||||
|
|
||||||
struct object_data {
|
struct object_data {
|
||||||
uint32_t Instance_Number;
|
uint32_t Instance_Number;
|
||||||
const char *Object_Name;
|
const char *Object_Name;
|
||||||
@@ -111,11 +117,14 @@ struct object_data {
|
|||||||
struct bacnet_ipv6_port IPv6;
|
struct bacnet_ipv6_port IPv6;
|
||||||
struct ethernet_port Ethernet;
|
struct ethernet_port Ethernet;
|
||||||
struct mstp_port MSTP;
|
struct mstp_port MSTP;
|
||||||
|
struct bsc_port BSC;
|
||||||
} Network;
|
} Network;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifndef BACNET_NETWORK_PORTS_MAX
|
#ifndef BACNET_NETWORK_PORTS_MAX
|
||||||
#define BACNET_NETWORK_PORTS_MAX 1
|
#define BACNET_NETWORK_PORTS_MAX 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static struct object_data Object_List[BACNET_NETWORK_PORTS_MAX];
|
static struct object_data Object_List[BACNET_NETWORK_PORTS_MAX];
|
||||||
|
|
||||||
/* These three arrays are used by the ReadPropertyMultiple handler */
|
/* These three arrays are used by the ReadPropertyMultiple handler */
|
||||||
@@ -194,6 +203,53 @@ static const int BIP6_Port_Properties_Optional[] = {
|
|||||||
-1
|
-1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const int BSC_Port_Properties_Optional[] = {
|
||||||
|
PROP_NETWORK_NUMBER,
|
||||||
|
PROP_NETWORK_NUMBER_QUALITY,
|
||||||
|
PROP_APDU_LENGTH,
|
||||||
|
PROP_MAC_ADDRESS,
|
||||||
|
PROP_BACNET_IP_MODE,
|
||||||
|
PROP_IP_ADDRESS,
|
||||||
|
PROP_BACNET_IP_UDP_PORT,
|
||||||
|
PROP_IP_SUBNET_MASK,
|
||||||
|
PROP_IP_DEFAULT_GATEWAY,
|
||||||
|
PROP_IP_DNS_SERVER,
|
||||||
|
PROP_MAX_BVLC_LENGTH_ACCEPTED,
|
||||||
|
PROP_MAX_NPDU_LENGTH_ACCEPTED,
|
||||||
|
PROP_SC_PRIMARY_HUB_URI,
|
||||||
|
PROP_SC_FAILOVER_HUB_URI,
|
||||||
|
PROP_SC_MINIMUM_RECONNECT_TIME,
|
||||||
|
PROP_SC_MAXIMUM_RECONNECT_TIME,
|
||||||
|
PROP_SC_CONNECT_WAIT_TIMEOUT,
|
||||||
|
PROP_SC_DISCONNECT_WAIT_TIMEOUT,
|
||||||
|
PROP_SC_HEARTBEAT_TIMEOUT,
|
||||||
|
PROP_SC_HUB_CONNECTOR_STATE,
|
||||||
|
PROP_OPERATIONAL_CERTIFICATE_FILE,
|
||||||
|
PROP_ISSUER_CERTIFICATE_FILES,
|
||||||
|
PROP_CERTIFICATE_SIGNING_REQUEST_FILE,
|
||||||
|
/*SC optional*/
|
||||||
|
#ifdef BACNET_SECURE_CONNECT_ROUTING_TABLE
|
||||||
|
PROP_ROUTING_TABLE,
|
||||||
|
#endif /* BACNET_SECURE_CONNECT_ROUTING_TABLE */
|
||||||
|
#if BSC_CONF_HUB_FUNCTIONS_NUM != 0
|
||||||
|
PROP_SC_PRIMARY_HUB_CONNECTION_STATUS,
|
||||||
|
PROP_SC_FAILOVER_HUB_CONNECTION_STATUS,
|
||||||
|
PROP_SC_HUB_FUNCTION_ENABLE,
|
||||||
|
PROP_SC_HUB_FUNCTION_ACCEPT_URIS,
|
||||||
|
PROP_SC_HUB_FUNCTION_BINDING,
|
||||||
|
PROP_SC_HUB_FUNCTION_CONNECTION_STATUS,
|
||||||
|
#endif /* BSC_CONF_HUB_FUNCTIONS_NUM!=0 */
|
||||||
|
#if BSC_CONF_HUB_CONNECTORS_NUM != 0
|
||||||
|
PROP_SC_DIRECT_CONNECT_INITIATE_ENABLE,
|
||||||
|
PROP_SC_DIRECT_CONNECT_ACCEPT_ENABLE,
|
||||||
|
PROP_SC_DIRECT_CONNECT_ACCEPT_URIS,
|
||||||
|
PROP_SC_DIRECT_CONNECT_BINDING,
|
||||||
|
PROP_SC_DIRECT_CONNECT_CONNECTION_STATUS,
|
||||||
|
#endif /* BSC_CONF_HUB_CONNECTORS_NUM!=0 */
|
||||||
|
PROP_SC_FAILED_CONNECTION_REQUESTS,
|
||||||
|
-1
|
||||||
|
};
|
||||||
|
|
||||||
static const int Network_Port_Properties_Proprietary[] = { -1 };
|
static const int Network_Port_Properties_Proprietary[] = { -1 };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -229,6 +285,9 @@ void Network_Port_Property_List(
|
|||||||
case PORT_TYPE_BIP:
|
case PORT_TYPE_BIP:
|
||||||
*pOptional = BIP_Port_Properties_Optional;
|
*pOptional = BIP_Port_Properties_Optional;
|
||||||
break;
|
break;
|
||||||
|
case PORT_TYPE_BSC:
|
||||||
|
*pOptional = BSC_Port_Properties_Optional;
|
||||||
|
break;
|
||||||
case PORT_TYPE_BIP6:
|
case PORT_TYPE_BIP6:
|
||||||
*pOptional = BIP6_Port_Properties_Optional;
|
*pOptional = BIP6_Port_Properties_Optional;
|
||||||
break;
|
break;
|
||||||
@@ -590,6 +649,16 @@ uint8_t Network_Port_Type(uint32_t object_instance)
|
|||||||
index = Network_Port_Instance_To_Index(object_instance);
|
index = Network_Port_Instance_To_Index(object_instance);
|
||||||
if (index < BACNET_NETWORK_PORTS_MAX) {
|
if (index < BACNET_NETWORK_PORTS_MAX) {
|
||||||
port_type = Object_List[index].Network_Type;
|
port_type = Object_List[index].Network_Type;
|
||||||
|
#if (BACNET_PROTOCOL_REVISION >= 17) && (BACNET_PROTOCOL_REVISION <= 23)
|
||||||
|
/* For BACnet/SC network port implementations with
|
||||||
|
a protocol revision Protocol_Revision 17 and higher through 23,
|
||||||
|
BACnet/SC network ports shall be represented by a Network Port
|
||||||
|
object at the BACNET_APPLICATION protocol level with
|
||||||
|
a proprietary network type value. */
|
||||||
|
if (port_type == PORT_TYPE_BSC) {
|
||||||
|
port_type = PORT_TYPE_BSC_INTERIM;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return port_type;
|
return port_type;
|
||||||
@@ -612,7 +681,18 @@ bool Network_Port_Type_Set(uint32_t object_instance, uint8_t value)
|
|||||||
|
|
||||||
index = Network_Port_Instance_To_Index(object_instance);
|
index = Network_Port_Instance_To_Index(object_instance);
|
||||||
if (index < BACNET_NETWORK_PORTS_MAX) {
|
if (index < BACNET_NETWORK_PORTS_MAX) {
|
||||||
|
#if (BACNET_PROTOCOL_REVISION >= 17) && (BACNET_PROTOCOL_REVISION <= 23)
|
||||||
|
/* For BACnet/SC network port implementations with
|
||||||
|
a protocol revision Protocol_Revision 17 and higher through 23,
|
||||||
|
BACnet/SC network ports shall be represented by a Network Port
|
||||||
|
object at the BACNET_APPLICATION protocol level with
|
||||||
|
a proprietary network type value. */
|
||||||
|
if (value == PORT_TYPE_BSC_INTERIM) {
|
||||||
|
value = PORT_TYPE_BSC;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
Object_List[index].Network_Type = value;
|
Object_List[index].Network_Type = value;
|
||||||
|
|
||||||
status = true;
|
status = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -748,6 +828,10 @@ uint8_t Network_Port_MAC_Address_Value(
|
|||||||
mac = &Object_List[index].Network.IPv6.MAC_Address[0];
|
mac = &Object_List[index].Network.IPv6.MAC_Address[0];
|
||||||
mac_len = sizeof(Object_List[index].Network.IPv6.MAC_Address);
|
mac_len = sizeof(Object_List[index].Network.IPv6.MAC_Address);
|
||||||
break;
|
break;
|
||||||
|
case PORT_TYPE_BSC:
|
||||||
|
mac = &Object_List[index].Network.BSC.MAC_Address[0];
|
||||||
|
mac_len = sizeof(Object_List[index].Network.BSC.MAC_Address);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -824,6 +908,10 @@ bool Network_Port_MAC_Address_Set(
|
|||||||
mac_dest = &Object_List[index].Network.IPv6.MAC_Address[0];
|
mac_dest = &Object_List[index].Network.IPv6.MAC_Address[0];
|
||||||
mac_size = sizeof(Object_List[index].Network.IPv6.MAC_Address);
|
mac_size = sizeof(Object_List[index].Network.IPv6.MAC_Address);
|
||||||
break;
|
break;
|
||||||
|
case PORT_TYPE_BSC:
|
||||||
|
mac_dest = &Object_List[index].Network.BSC.MAC_Address[0];
|
||||||
|
mac_size = sizeof(Object_List[index].Network.BSC.MAC_Address);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -3540,6 +3628,178 @@ int Network_Port_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
|||||||
apdu_len =
|
apdu_len =
|
||||||
encode_application_character_string(&apdu[0], &char_string);
|
encode_application_character_string(&apdu[0], &char_string);
|
||||||
break;
|
break;
|
||||||
|
#ifdef BACDL_BSC
|
||||||
|
case PROP_MAX_BVLC_LENGTH_ACCEPTED:
|
||||||
|
apdu_len = encode_application_unsigned(
|
||||||
|
&apdu[0],
|
||||||
|
Network_Port_Max_BVLC_Length_Accepted(rpdata->object_instance));
|
||||||
|
break;
|
||||||
|
case PROP_MAX_NPDU_LENGTH_ACCEPTED:
|
||||||
|
apdu_len = encode_application_unsigned(
|
||||||
|
&apdu[0],
|
||||||
|
Network_Port_Max_NPDU_Length_Accepted(rpdata->object_instance));
|
||||||
|
break;
|
||||||
|
case PROP_SC_PRIMARY_HUB_URI:
|
||||||
|
Network_Port_SC_Primary_Hub_URI(
|
||||||
|
rpdata->object_instance, &char_string);
|
||||||
|
apdu_len =
|
||||||
|
encode_application_character_string(&apdu[0], &char_string);
|
||||||
|
break;
|
||||||
|
case PROP_SC_FAILOVER_HUB_URI:
|
||||||
|
Network_Port_SC_Failover_Hub_URI(
|
||||||
|
rpdata->object_instance, &char_string);
|
||||||
|
apdu_len =
|
||||||
|
encode_application_character_string(&apdu[0], &char_string);
|
||||||
|
break;
|
||||||
|
case PROP_SC_MINIMUM_RECONNECT_TIME:
|
||||||
|
apdu_len = encode_application_unsigned(
|
||||||
|
&apdu[0],
|
||||||
|
Network_Port_SC_Minimum_Reconnect_Time(
|
||||||
|
rpdata->object_instance));
|
||||||
|
break;
|
||||||
|
case PROP_SC_MAXIMUM_RECONNECT_TIME:
|
||||||
|
apdu_len = encode_application_unsigned(
|
||||||
|
&apdu[0],
|
||||||
|
Network_Port_SC_Maximum_Reconnect_Time(
|
||||||
|
rpdata->object_instance));
|
||||||
|
break;
|
||||||
|
case PROP_SC_CONNECT_WAIT_TIMEOUT:
|
||||||
|
apdu_len = encode_application_unsigned(
|
||||||
|
&apdu[0],
|
||||||
|
Network_Port_SC_Connect_Wait_Timeout(rpdata->object_instance));
|
||||||
|
break;
|
||||||
|
case PROP_SC_DISCONNECT_WAIT_TIMEOUT:
|
||||||
|
apdu_len = encode_application_unsigned(
|
||||||
|
&apdu[0],
|
||||||
|
Network_Port_SC_Disconnect_Wait_Timeout(
|
||||||
|
rpdata->object_instance));
|
||||||
|
break;
|
||||||
|
case PROP_SC_HEARTBEAT_TIMEOUT:
|
||||||
|
apdu_len = encode_application_unsigned(
|
||||||
|
&apdu[0],
|
||||||
|
Network_Port_SC_Heartbeat_Timeout(rpdata->object_instance));
|
||||||
|
break;
|
||||||
|
case PROP_SC_HUB_CONNECTOR_STATE:
|
||||||
|
apdu_len = encode_application_enumerated(
|
||||||
|
&apdu[0],
|
||||||
|
Network_Port_SC_Hub_Connector_State(rpdata->object_instance));
|
||||||
|
break;
|
||||||
|
case PROP_OPERATIONAL_CERTIFICATE_FILE:
|
||||||
|
apdu_len = encode_application_unsigned(
|
||||||
|
&apdu[0],
|
||||||
|
Network_Port_Operational_Certificate_File(
|
||||||
|
rpdata->object_instance));
|
||||||
|
break;
|
||||||
|
case PROP_ISSUER_CERTIFICATE_FILES:
|
||||||
|
apdu_len = bacnet_array_encode(
|
||||||
|
rpdata->object_instance, rpdata->array_index,
|
||||||
|
Network_Port_Issuer_Certificate_File_Encode,
|
||||||
|
BACNET_ISSUER_CERT_FILE_MAX, apdu, apdu_size);
|
||||||
|
if (apdu_len == BACNET_STATUS_ABORT) {
|
||||||
|
rpdata->error_code =
|
||||||
|
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||||
|
} else if (apdu_len == BACNET_STATUS_ERROR) {
|
||||||
|
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||||
|
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PROP_CERTIFICATE_SIGNING_REQUEST_FILE:
|
||||||
|
apdu_len = encode_application_unsigned(
|
||||||
|
&apdu[0],
|
||||||
|
Network_Port_Certificate_Signing_Request_File(
|
||||||
|
rpdata->object_instance));
|
||||||
|
break;
|
||||||
|
/* SC optionals */
|
||||||
|
#if BACNET_SECURE_CONNECT_ROUTING_TABLE
|
||||||
|
case PROP_ROUTING_TABLE:
|
||||||
|
apdu_len = Network_Port_Routing_Table_Encode(
|
||||||
|
rpdata->object_instance, apdu, apdu_size);
|
||||||
|
break;
|
||||||
|
#endif /* BACNET_SECURE_CONNECT_ROUTING_TABLE */
|
||||||
|
#if BSC_CONF_HUB_FUNCTIONS_NUM != 0
|
||||||
|
case PROP_SC_PRIMARY_HUB_CONNECTION_STATUS:
|
||||||
|
apdu_len = bacapp_encode_SCHubConnection(
|
||||||
|
&apdu[0],
|
||||||
|
Network_Port_SC_Primary_Hub_Connection_Status(
|
||||||
|
rpdata->object_instance));
|
||||||
|
break;
|
||||||
|
case PROP_SC_FAILOVER_HUB_CONNECTION_STATUS:
|
||||||
|
apdu_len = bacapp_encode_SCHubConnection(
|
||||||
|
&apdu[0],
|
||||||
|
Network_Port_SC_Failover_Hub_Connection_Status(
|
||||||
|
rpdata->object_instance));
|
||||||
|
break;
|
||||||
|
case PROP_SC_HUB_FUNCTION_ENABLE:
|
||||||
|
apdu_len = encode_application_boolean(
|
||||||
|
&apdu[0],
|
||||||
|
Network_Port_SC_Hub_Function_Enable(rpdata->object_instance));
|
||||||
|
break;
|
||||||
|
case PROP_SC_HUB_FUNCTION_ACCEPT_URIS:
|
||||||
|
apdu_len = bacnet_array_encode(
|
||||||
|
rpdata->object_instance, rpdata->array_index,
|
||||||
|
Network_Port_SC_Hub_Function_Accept_URI_Encode,
|
||||||
|
BACNET_SC_DIRECT_ACCEPT_URI_MAX, apdu, apdu_size);
|
||||||
|
if (apdu_len == BACNET_STATUS_ABORT) {
|
||||||
|
rpdata->error_code =
|
||||||
|
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||||
|
} else if (apdu_len == BACNET_STATUS_ERROR) {
|
||||||
|
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||||
|
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PROP_SC_HUB_FUNCTION_BINDING:
|
||||||
|
Network_Port_SC_Hub_Function_Binding(
|
||||||
|
rpdata->object_instance, &char_string);
|
||||||
|
apdu_len =
|
||||||
|
encode_application_character_string(&apdu[0], &char_string);
|
||||||
|
break;
|
||||||
|
case PROP_SC_HUB_FUNCTION_CONNECTION_STATUS:
|
||||||
|
apdu_len = Network_Port_SC_Hub_Function_Connection_Status_Encode(
|
||||||
|
rpdata->object_instance, apdu, apdu_size);
|
||||||
|
break;
|
||||||
|
#endif /* BSC_CONF_HUB_FUNCTIONS_NUM!=0 */
|
||||||
|
#if BSC_CONF_HUB_CONNECTORS_NUM != 0
|
||||||
|
case PROP_SC_DIRECT_CONNECT_INITIATE_ENABLE:
|
||||||
|
apdu_len = encode_application_boolean(
|
||||||
|
&apdu[0],
|
||||||
|
Network_Port_SC_Direct_Connect_Initiate_Enable(
|
||||||
|
rpdata->object_instance));
|
||||||
|
break;
|
||||||
|
case PROP_SC_DIRECT_CONNECT_ACCEPT_ENABLE:
|
||||||
|
apdu_len = encode_application_boolean(
|
||||||
|
&apdu[0],
|
||||||
|
Network_Port_SC_Direct_Connect_Accept_Enable(
|
||||||
|
rpdata->object_instance));
|
||||||
|
break;
|
||||||
|
case PROP_SC_DIRECT_CONNECT_ACCEPT_URIS:
|
||||||
|
apdu_len = bacnet_array_encode(
|
||||||
|
rpdata->object_instance, rpdata->array_index,
|
||||||
|
Network_Port_SC_Direct_Connect_Accept_URI_Encode,
|
||||||
|
BACNET_SC_DIRECT_ACCEPT_URI_MAX, apdu, apdu_size);
|
||||||
|
if (apdu_len == BACNET_STATUS_ABORT) {
|
||||||
|
rpdata->error_code =
|
||||||
|
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||||
|
} else if (apdu_len == BACNET_STATUS_ERROR) {
|
||||||
|
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||||
|
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PROP_SC_DIRECT_CONNECT_BINDING:
|
||||||
|
Network_Port_SC_Direct_Connect_Binding(
|
||||||
|
rpdata->object_instance, &char_string);
|
||||||
|
apdu_len =
|
||||||
|
encode_application_character_string(&apdu[0], &char_string);
|
||||||
|
break;
|
||||||
|
case PROP_SC_DIRECT_CONNECT_CONNECTION_STATUS:
|
||||||
|
apdu_len = Network_Port_SC_Direct_Connect_Connection_Status_Encode(
|
||||||
|
rpdata->object_instance, apdu, apdu_size);
|
||||||
|
break;
|
||||||
|
#endif /* BSC_CONF_HUB_CONNECTORS_NUM!=0 */
|
||||||
|
case PROP_SC_FAILED_CONNECTION_REQUESTS:
|
||||||
|
apdu_len = Network_Port_SC_Failed_Connection_Requests_Encode(
|
||||||
|
rpdata->object_instance, apdu, apdu_size);
|
||||||
|
break;
|
||||||
|
#endif /* BACDL_BSC */
|
||||||
default:
|
default:
|
||||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||||
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||||
@@ -3693,6 +3953,11 @@ bool Network_Port_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
|||||||
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!status && (wp_data->error_code == ERROR_CODE_OTHER)) {
|
||||||
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||||
|
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3715,7 +3980,7 @@ int Network_Port_Read_Range_BDT(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
|
|||||||
* ReadRange service handler for the BACnet/IP FDT.
|
* ReadRange service handler for the BACnet/IP FDT.
|
||||||
*
|
*
|
||||||
* @param apdu - place to encode the data
|
* @param apdu - place to encode the data
|
||||||
* @param apdu - BACNET_READ_RANGE_DATA data
|
* @param pRequest - BACNET_READ_RANGE_DATA data
|
||||||
*
|
*
|
||||||
* @return number of bytes encoded
|
* @return number of bytes encoded
|
||||||
*/
|
*/
|
||||||
@@ -3845,7 +4110,16 @@ void Network_Port_Changes_Discard(void)
|
|||||||
*/
|
*/
|
||||||
void Network_Port_Cleanup(void)
|
void Network_Port_Cleanup(void)
|
||||||
{
|
{
|
||||||
/* do something interesting */
|
#if defined(BACDL_BSC) && defined(BACNET_SECURE_CONNECT_ROUTING_TABLE)
|
||||||
|
unsigned index = 0;
|
||||||
|
for (index = 0; index < BACNET_NETWORK_PORTS_MAX; index++) {
|
||||||
|
BACNET_SC_PARAMS *sc = &Object_List[index].Network.BSC.Parameters;
|
||||||
|
if (sc->Routing_Table) {
|
||||||
|
Keylist_Delete(sc->Routing_Table);
|
||||||
|
sc->Routing_Table = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -3853,5 +4127,58 @@ void Network_Port_Cleanup(void)
|
|||||||
*/
|
*/
|
||||||
void Network_Port_Init(void)
|
void Network_Port_Init(void)
|
||||||
{
|
{
|
||||||
|
unsigned index = 0;
|
||||||
|
|
||||||
|
#ifdef BACDL_BSC
|
||||||
|
BACNET_SC_PARAMS *sc;
|
||||||
|
#endif /* BACDL_BSC */
|
||||||
|
|
||||||
/* do something interesting */
|
/* do something interesting */
|
||||||
|
|
||||||
|
for (index = 0; index < BACNET_NETWORK_PORTS_MAX; index++) {
|
||||||
|
memset(&Object_List[index], 0, sizeof(Object_List[index]));
|
||||||
|
#ifdef BACDL_BSC
|
||||||
|
Object_List[index].Network_Type = PORT_TYPE_BSC;
|
||||||
|
sc = &Object_List[index].Network.BSC.Parameters;
|
||||||
|
Object_List[index].Activate_Changes =
|
||||||
|
Network_Port_SC_Pending_Params_Apply;
|
||||||
|
Object_List[index].Discard_Changes =
|
||||||
|
Network_Port_SC_Pending_Params_Discard;
|
||||||
|
#ifdef BACNET_SECURE_CONNECT_ROUTING_TABLE
|
||||||
|
sc->Routing_Table = Keylist_Create();
|
||||||
|
#endif
|
||||||
|
sc->SC_Failed_Connection_Requests_Count = 0;
|
||||||
|
#if BSC_CONF_HUB_FUNCTIONS_NUM != 0
|
||||||
|
sc->SC_Hub_Function_Connection_Status_Count = 0;
|
||||||
|
#endif
|
||||||
|
#if BSC_CONF_HUB_CONNECTORS_NUM != 0
|
||||||
|
sc->SC_Direct_Connect_Connection_Status_Count = 0;
|
||||||
|
#endif
|
||||||
|
(void)sc;
|
||||||
|
#endif /* BACDL_BSC */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef BACDL_BSC
|
||||||
|
/**
|
||||||
|
* For a given object instance-number, gets SC parameters structure
|
||||||
|
*
|
||||||
|
* @param object_instance - object-instance number of the object
|
||||||
|
*
|
||||||
|
* @return SC params structure
|
||||||
|
*/
|
||||||
|
BACNET_SC_PARAMS *Network_Port_SC_Params(uint32_t object_instance)
|
||||||
|
{
|
||||||
|
BACNET_SC_PARAMS *param = NULL;
|
||||||
|
unsigned index = 0;
|
||||||
|
|
||||||
|
index = Network_Port_Instance_To_Index(object_instance);
|
||||||
|
if (index < BACNET_NETWORK_PORTS_MAX) {
|
||||||
|
if (Object_List[index].Network_Type == PORT_TYPE_BSC) {
|
||||||
|
param = &Object_List[index].Network.BSC.Parameters;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Helper Network port objects to implement secure connect attributes
|
||||||
|
* @author Mikhail Antropov <michail.antropov@dsr-corporation.com>
|
||||||
|
* @date December 2022
|
||||||
|
* @copyright SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
#ifndef BACNET_NETPORT_INTERNAL_H
|
||||||
|
#define BACNET_NETPORT_INTERNAL_H
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
/* BACnet Stack defines - first */
|
||||||
|
#include "bacnet/bacdef.h"
|
||||||
|
/* BACnet Stack API */
|
||||||
|
#include "bacnet/basic/sys/keylist.h"
|
||||||
|
#include "bacnet/apdu.h"
|
||||||
|
#include "bacnet/datetime.h"
|
||||||
|
#include "bacnet/hostnport.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-conf.h"
|
||||||
|
#include "bacnet/basic/object/sc_netport.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
/* Support
|
||||||
|
- SC_Hub_Function_Connection_Status
|
||||||
|
- SC_Direct_Connect_Connection_Status
|
||||||
|
- SC_FailedConnectionRequests
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct BACnetSCAttributes_T {
|
||||||
|
BACNET_UNSIGNED_INTEGER Max_BVLC_Length_Accepted;
|
||||||
|
BACNET_UNSIGNED_INTEGER Max_BVLC_Length_Accepted_dirty;
|
||||||
|
BACNET_UNSIGNED_INTEGER Max_NPDU_Length_Accepted;
|
||||||
|
BACNET_UNSIGNED_INTEGER Max_NPDU_Length_Accepted_dirty;
|
||||||
|
char SC_Primary_Hub_URI[BACNET_URI_LENGTH];
|
||||||
|
char SC_Primary_Hub_URI_dirty[BACNET_URI_LENGTH];
|
||||||
|
char SC_Failover_Hub_URI[BACNET_URI_LENGTH];
|
||||||
|
char SC_Failover_Hub_URI_dirty[BACNET_URI_LENGTH];
|
||||||
|
BACNET_UNSIGNED_INTEGER SC_Minimum_Reconnect_Time;
|
||||||
|
BACNET_UNSIGNED_INTEGER SC_Minimum_Reconnect_Time_dirty;
|
||||||
|
BACNET_UNSIGNED_INTEGER SC_Maximum_Reconnect_Time;
|
||||||
|
BACNET_UNSIGNED_INTEGER SC_Maximum_Reconnect_Time_dirty;
|
||||||
|
BACNET_UNSIGNED_INTEGER SC_Connect_Wait_Timeout;
|
||||||
|
BACNET_UNSIGNED_INTEGER SC_Connect_Wait_Timeout_dirty;
|
||||||
|
BACNET_UNSIGNED_INTEGER SC_Disconnect_Wait_Timeout;
|
||||||
|
BACNET_UNSIGNED_INTEGER SC_Disconnect_Wait_Timeout_dirty;
|
||||||
|
BACNET_UNSIGNED_INTEGER SC_Heartbeat_Timeout;
|
||||||
|
BACNET_UNSIGNED_INTEGER SC_Heartbeat_Timeout_dirty;
|
||||||
|
BACNET_SC_HUB_CONNECTOR_STATE SC_Hub_Connector_State;
|
||||||
|
uint32_t Operational_Certificate_File;
|
||||||
|
uint32_t Issuer_Certificate_Files[BACNET_ISSUER_CERT_FILE_MAX];
|
||||||
|
uint32_t Certificate_Signing_Request_File;
|
||||||
|
/* Optional params */
|
||||||
|
#ifdef BACNET_SECURE_CONNECT_ROUTING_TABLE
|
||||||
|
OS_Keylist Routing_Table;
|
||||||
|
#endif /* BACNET_SECURE_CONNECT_ROUTING_TABLE */
|
||||||
|
#if BSC_CONF_HUB_FUNCTIONS_NUM != 0
|
||||||
|
BACNET_SC_HUB_CONNECTION_STATUS SC_Primary_Hub_Connection_Status;
|
||||||
|
BACNET_SC_HUB_CONNECTION_STATUS SC_Failover_Hub_Connection_Status;
|
||||||
|
bool SC_Hub_Function_Enable;
|
||||||
|
bool SC_Hub_Function_Enable_dirty;
|
||||||
|
char SC_Hub_Function_Accept_URIs
|
||||||
|
[BACNET_SC_HUB_URI_MAX * (BACNET_URI_LENGTH + 1)];
|
||||||
|
char SC_Hub_Function_Accept_URIs_dirty
|
||||||
|
[BACNET_SC_HUB_URI_MAX * (BACNET_URI_LENGTH + 1)];
|
||||||
|
char SC_Hub_Function_Binding[BACNET_BINDING_STRING_LENGTH];
|
||||||
|
char SC_Hub_Function_Binding_dirty[BACNET_BINDING_STRING_LENGTH];
|
||||||
|
BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS
|
||||||
|
SC_Hub_Function_Connection_Status
|
||||||
|
[BSC_CONF_HUB_FUNCTION_CONNECTION_STATUS_MAX_NUM];
|
||||||
|
uint8_t SC_Hub_Function_Connection_Status_Count;
|
||||||
|
uint16_t Hub_Server_Port;
|
||||||
|
#endif /* BSC_CONF_HUB_FUNCTIONS_NUM!=0 */
|
||||||
|
#if BSC_CONF_HUB_CONNECTORS_NUM != 0
|
||||||
|
bool SC_Direct_Connect_Initiate_Enable;
|
||||||
|
bool SC_Direct_Connect_Initiate_Enable_dirty;
|
||||||
|
bool SC_Direct_Connect_Accept_Enable;
|
||||||
|
bool SC_Direct_Connect_Accept_Enable_dirty;
|
||||||
|
char SC_Direct_Connect_Accept_URIs
|
||||||
|
[BACNET_SC_DIRECT_ACCEPT_URI_MAX * (BACNET_URI_LENGTH + 1)];
|
||||||
|
char SC_Direct_Connect_Accept_URIs_dirty
|
||||||
|
[BACNET_SC_DIRECT_ACCEPT_URI_MAX * (BACNET_URI_LENGTH + 1)];
|
||||||
|
char SC_Direct_Connect_Binding[BACNET_BINDING_STRING_LENGTH];
|
||||||
|
char SC_Direct_Connect_Binding_dirty[BACNET_BINDING_STRING_LENGTH];
|
||||||
|
BACNET_SC_DIRECT_CONNECTION_STATUS SC_Direct_Connect_Connection_Status
|
||||||
|
[BSC_CONF_NODE_SWITCH_CONNECTION_STATUS_MAX_NUM];
|
||||||
|
uint8_t SC_Direct_Connect_Connection_Status_Count;
|
||||||
|
uint16_t Direct_Server_Port;
|
||||||
|
#endif /* BSC_CONF_HUB_CONNECTORS_NUM!=0 */
|
||||||
|
BACNET_SC_FAILED_CONNECTION_REQUEST SC_Failed_Connection_Requests
|
||||||
|
[BSC_CONF_FAILED_CONNECTION_STATUS_MAX_NUM];
|
||||||
|
uint8_t SC_Failed_Connection_Requests_Count;
|
||||||
|
uint32_t Certificate_Key_File;
|
||||||
|
BACNET_UUID Local_UUID;
|
||||||
|
} BACNET_SC_PARAMS;
|
||||||
|
|
||||||
|
BACNET_SC_PARAMS *Network_Port_SC_Params(uint32_t object_instance);
|
||||||
|
void Network_Port_SC_Pending_Params_Apply(uint32_t object_instance);
|
||||||
|
void Network_Port_SC_Pending_Params_Discard(uint32_t object_instance);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
#endif /* NETPORT_INTERNAL_H */
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,415 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Helper Network port objects to implement secure connect attributes
|
||||||
|
* @author Mikhail Antropov <michail.antropov@dsr-corporation.com>
|
||||||
|
* @date June 2022
|
||||||
|
* @copyright SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
#ifndef BACNET_SC_NETPORT_H
|
||||||
|
#define BACNET_SC_NETPORT_H
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
/* BACnet Stack defines - first */
|
||||||
|
#include "bacnet/bacdef.h"
|
||||||
|
/* BACnet Stack API */
|
||||||
|
#include "bacnet/basic/sys/keylist.h"
|
||||||
|
#include "bacnet/bacapp.h"
|
||||||
|
#include "bacnet/apdu.h"
|
||||||
|
#include "bacnet/secure_connect.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-conf.h"
|
||||||
|
#include "bacnet/datalink/bsc/bvlc-sc.h"
|
||||||
|
|
||||||
|
/* BACnet file instance numbers */
|
||||||
|
#ifndef BSC_ISSUER_CERTIFICATE_FILE_1_INSTANCE
|
||||||
|
#define BSC_ISSUER_CERTIFICATE_FILE_1_INSTANCE \
|
||||||
|
BSC_CONF_ISSUER_CERTIFICATE_FILE_1_INSTANCE
|
||||||
|
#endif
|
||||||
|
#ifndef BSC_ISSUER_CERTIFICATE_FILE_2_INSTANCE
|
||||||
|
#define BSC_ISSUER_CERTIFICATE_FILE_2_INSTANCE \
|
||||||
|
BSC_CONF_ISSUER_CERTIFICATE_FILE_2_INSTANCE
|
||||||
|
#endif
|
||||||
|
#ifndef BSC_OPERATIONAL_CERTIFICATE_FILE_INSTANCE
|
||||||
|
#define BSC_OPERATIONAL_CERTIFICATE_FILE_INSTANCE \
|
||||||
|
BSC_CONF_OPERATIONAL_CERTIFICATE_FILE_INSTANCE
|
||||||
|
#endif
|
||||||
|
#ifndef BSC_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE
|
||||||
|
#define BSC_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE \
|
||||||
|
BSC_CONF_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
#define BACNET_SC_HUB_URI_MAX 2
|
||||||
|
#ifndef BACNET_SC_DIRECT_ACCEPT_URI_MAX
|
||||||
|
#define BACNET_SC_DIRECT_ACCEPT_URI_MAX 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BACNET_ISSUER_CERT_FILE_MAX 2
|
||||||
|
#define BACNET_BINDING_STRING_LENGTH 80
|
||||||
|
|
||||||
|
#define BACNET_SC_BINDING_SEPARATOR ':'
|
||||||
|
|
||||||
|
/* */
|
||||||
|
/* getter / setter */
|
||||||
|
/* */
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BACNET_UNSIGNED_INTEGER
|
||||||
|
Network_Port_Max_BVLC_Length_Accepted(uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_Max_BVLC_Length_Accepted_Set(
|
||||||
|
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||||
|
bool Network_Port_Max_BVLC_Length_Accepted_Dirty_Set(
|
||||||
|
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BACNET_UNSIGNED_INTEGER
|
||||||
|
Network_Port_Max_NPDU_Length_Accepted(uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_Max_NPDU_Length_Accepted_Set(
|
||||||
|
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||||
|
bool Network_Port_Max_NPDU_Length_Accepted_Dirty_Set(
|
||||||
|
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Primary_Hub_URI(
|
||||||
|
uint32_t object_instance, BACNET_CHARACTER_STRING *str);
|
||||||
|
const char *Network_Port_SC_Primary_Hub_URI_char(uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Primary_Hub_URI_Set(
|
||||||
|
uint32_t object_instance, const char *str);
|
||||||
|
bool Network_Port_SC_Primary_Hub_URI_Dirty_Set(
|
||||||
|
uint32_t object_instance, const char *str);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Failover_Hub_URI(
|
||||||
|
uint32_t object_instance, BACNET_CHARACTER_STRING *str);
|
||||||
|
const char *Network_Port_SC_Failover_Hub_URI_char(uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Failover_Hub_URI_Set(
|
||||||
|
uint32_t object_instance, const char *str);
|
||||||
|
bool Network_Port_SC_Failover_Hub_URI_Dirty_Set(
|
||||||
|
uint32_t object_instance, const char *str);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BACNET_UNSIGNED_INTEGER
|
||||||
|
Network_Port_SC_Minimum_Reconnect_Time(uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Minimum_Reconnect_Time_Set(
|
||||||
|
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||||
|
bool Network_Port_SC_Minimum_Reconnect_Time_Dirty_Set(
|
||||||
|
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BACNET_UNSIGNED_INTEGER
|
||||||
|
Network_Port_SC_Maximum_Reconnect_Time(uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Maximum_Reconnect_Time_Set(
|
||||||
|
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||||
|
bool Network_Port_SC_Maximum_Reconnect_Time_Dirty_Set(
|
||||||
|
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BACNET_UNSIGNED_INTEGER
|
||||||
|
Network_Port_SC_Connect_Wait_Timeout(uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Connect_Wait_Timeout_Set(
|
||||||
|
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||||
|
bool Network_Port_SC_Connect_Wait_Timeout_Dirty_Set(
|
||||||
|
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BACNET_UNSIGNED_INTEGER
|
||||||
|
Network_Port_SC_Disconnect_Wait_Timeout(uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Disconnect_Wait_Timeout_Set(
|
||||||
|
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||||
|
bool Network_Port_SC_Disconnect_Wait_Timeout_Dirty_Set(
|
||||||
|
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BACNET_UNSIGNED_INTEGER
|
||||||
|
Network_Port_SC_Heartbeat_Timeout(uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Heartbeat_Timeout_Set(
|
||||||
|
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||||
|
bool Network_Port_SC_Heartbeat_Timeout_Dirty_Set(
|
||||||
|
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BACNET_SC_HUB_CONNECTOR_STATE
|
||||||
|
Network_Port_SC_Hub_Connector_State(uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Hub_Connector_State_Set(
|
||||||
|
uint32_t object_instance, BACNET_SC_HUB_CONNECTOR_STATE value);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
uint32_t Network_Port_Operational_Certificate_File(uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_Operational_Certificate_File_Set(
|
||||||
|
uint32_t object_instance, uint32_t value);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
uint32_t
|
||||||
|
Network_Port_Issuer_Certificate_File(uint32_t object_instance, uint8_t index);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_Issuer_Certificate_File_Set(
|
||||||
|
uint32_t object_instance, uint8_t index, uint32_t value);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int Network_Port_Issuer_Certificate_File_Encode(
|
||||||
|
uint32_t object_instance, BACNET_ARRAY_INDEX index, uint8_t *apdu);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
uint32_t
|
||||||
|
Network_Port_Certificate_Signing_Request_File(uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_Certificate_Signing_Request_File_Set(
|
||||||
|
uint32_t object_instance, uint32_t value);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BACNET_ROUTER_ENTRY *Network_Port_Routing_Table_Find(
|
||||||
|
uint32_t object_instance, uint16_t Network_Number);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BACNET_ROUTER_ENTRY *
|
||||||
|
Network_Port_Routing_Table_Get(uint32_t object_instance, uint8_t index);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_Routing_Table_Add(
|
||||||
|
uint32_t object_instance,
|
||||||
|
uint16_t network_number,
|
||||||
|
uint8_t *mac,
|
||||||
|
uint8_t mac_len,
|
||||||
|
uint8_t status,
|
||||||
|
uint8_t performance_index);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_Routing_Table_Delete(
|
||||||
|
uint32_t object_instance, uint16_t Network_Number);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_Routing_Table_Delete_All(uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
uint8_t Network_Port_Routing_Table_Count(uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int Network_Port_Routing_Table_Encode(
|
||||||
|
uint32_t object_instance, uint8_t *apdu, int max_apdu);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BACNET_SC_HUB_CONNECTION_STATUS *
|
||||||
|
Network_Port_SC_Primary_Hub_Connection_Status(uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Primary_Hub_Connection_Status_Set(
|
||||||
|
uint32_t object_instance,
|
||||||
|
BACNET_SC_CONNECTION_STATE state,
|
||||||
|
BACNET_DATE_TIME *connect_ts,
|
||||||
|
BACNET_DATE_TIME *disconnect_ts,
|
||||||
|
BACNET_ERROR_CODE error,
|
||||||
|
const char *error_details);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BACNET_SC_HUB_CONNECTION_STATUS *
|
||||||
|
Network_Port_SC_Failover_Hub_Connection_Status(uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Failover_Hub_Connection_Status_Set(
|
||||||
|
uint32_t object_instance,
|
||||||
|
BACNET_SC_CONNECTION_STATE state,
|
||||||
|
BACNET_DATE_TIME *connect_ts,
|
||||||
|
BACNET_DATE_TIME *disconnect_ts,
|
||||||
|
BACNET_ERROR_CODE error,
|
||||||
|
const char *error_details);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Hub_Function_Enable(uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Hub_Function_Enable_Set(
|
||||||
|
uint32_t object_instance, bool value);
|
||||||
|
bool Network_Port_SC_Hub_Function_Enable_Dirty_Set(
|
||||||
|
uint32_t object_instance, bool value);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Hub_Function_Accept_URI(
|
||||||
|
uint32_t object_instance, uint8_t index, BACNET_CHARACTER_STRING *str);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Hub_Function_Accept_URI_Set(
|
||||||
|
uint32_t object_instance, uint8_t index, const char *str);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int Network_Port_SC_Hub_Function_Accept_URI_Encode(
|
||||||
|
uint32_t object_instance, BACNET_ARRAY_INDEX array_index, uint8_t *apdu);
|
||||||
|
|
||||||
|
bool Network_Port_SC_Hub_Function_Accept_URI_Dirty_Set(
|
||||||
|
uint32_t object_instance, uint8_t index, const char *str);
|
||||||
|
const char *
|
||||||
|
Network_Port_SC_Hub_Function_Accept_URIs_char(uint32_t object_instance);
|
||||||
|
bool Network_Port_SC_Hub_Function_Accept_URIs_Set(
|
||||||
|
uint32_t object_instance, const char *str);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Internal representation of the SC_Hub_Function_Binding value is a
|
||||||
|
struct {
|
||||||
|
uint16 port;
|
||||||
|
char interface_name[BACNET_BINDING_STRING_LENGTH - sizeof(uint16)];
|
||||||
|
}
|
||||||
|
External representation is string e.q "interface_name:port_number".
|
||||||
|
|
||||||
|
The Network_Port_SC_Hub_Function_Binding() and
|
||||||
|
the Network_Port_SC_Hub_Function_Binding_Set() do transform from/to.
|
||||||
|
|
||||||
|
The Network_Port_SC_Hub_Function_Binding_Dirty sets value like
|
||||||
|
external representation.
|
||||||
|
*/
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Hub_Function_Binding(
|
||||||
|
uint32_t object_instance, BACNET_CHARACTER_STRING *str);
|
||||||
|
void Network_Port_SC_Hub_Function_Binding_get(
|
||||||
|
uint32_t object_instance, uint16_t *port, char **ifname);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Hub_Function_Binding_Set(
|
||||||
|
uint32_t object_instance, const char *str);
|
||||||
|
bool Network_Port_SC_Hub_Function_Binding_Dirty_Set(
|
||||||
|
uint32_t object_instance, const char *str);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *
|
||||||
|
Network_Port_SC_Hub_Function_Connection_Status_Get(
|
||||||
|
uint32_t object_instance, uint8_t index);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Hub_Function_Connection_Status_Add(
|
||||||
|
uint32_t object_instance,
|
||||||
|
BACNET_SC_CONNECTION_STATE state,
|
||||||
|
BACNET_DATE_TIME *connect_ts,
|
||||||
|
BACNET_DATE_TIME *disconnect_ts,
|
||||||
|
BACNET_HOST_N_PORT_DATA *peer_address,
|
||||||
|
uint8_t *peer_VMAC,
|
||||||
|
uint8_t *peer_UUID,
|
||||||
|
BACNET_ERROR_CODE error,
|
||||||
|
const char *error_details);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Hub_Function_Connection_Status_Delete_All(
|
||||||
|
uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int Network_Port_SC_Hub_Function_Connection_Status_Count(
|
||||||
|
uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int Network_Port_SC_Hub_Function_Connection_Status_Encode(
|
||||||
|
uint32_t object_instance, uint8_t *apdu, int max_apdu);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Direct_Connect_Initiate_Enable(uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Direct_Connect_Initiate_Enable_Set(
|
||||||
|
uint32_t object_instance, bool value);
|
||||||
|
bool Network_Port_SC_Direct_Connect_Initiate_Enable_Dirty_Set(
|
||||||
|
uint32_t object_instance, bool value);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Direct_Connect_Accept_Enable(uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Direct_Connect_Accept_Enable_Set(
|
||||||
|
uint32_t object_instance, bool value);
|
||||||
|
bool Network_Port_SC_Direct_Connect_Accept_Enable_Dirty_Set(
|
||||||
|
uint32_t object_instance, bool value);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Direct_Connect_Accept_URI(
|
||||||
|
uint32_t object_instance, uint8_t index, BACNET_CHARACTER_STRING *str);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Direct_Connect_Accept_URI_Set(
|
||||||
|
uint32_t object_instance, uint8_t index, const char *str);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int Network_Port_SC_Direct_Connect_Accept_URI_Encode(
|
||||||
|
uint32_t object_instance, BACNET_ARRAY_INDEX array_index, uint8_t *apdu);
|
||||||
|
|
||||||
|
bool Network_Port_SC_Direct_Connect_Accept_URI_Dirty_Set(
|
||||||
|
uint32_t object_instance, uint8_t index, const char *str);
|
||||||
|
char *Network_Port_SC_Direct_Connect_Accept_URIs_char(uint32_t object_instance);
|
||||||
|
bool Network_Port_SC_Direct_Connect_Accept_URIs_Set(
|
||||||
|
uint32_t object_instance, const char *str);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Internal representation of the SC_Direct_Connect_Binding value is a
|
||||||
|
struct {
|
||||||
|
uint16 port;
|
||||||
|
char interface_name[BACNET_BINDING_STRING_LENGTH - sizeof(uint16)];
|
||||||
|
}
|
||||||
|
External representation is string e.q "interface_name:port_number".
|
||||||
|
|
||||||
|
The Network_Port_SC_Direct_Connect_Binding() and
|
||||||
|
the Network_Port_SC_Direct_Connect_Binding_Set() do transform from/to.
|
||||||
|
|
||||||
|
The Network_Port_SC_Direct_Connect_Binding_Dirty sets value like
|
||||||
|
external representation.
|
||||||
|
*/
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Direct_Connect_Binding(
|
||||||
|
uint32_t object_instance, BACNET_CHARACTER_STRING *str);
|
||||||
|
void Network_Port_SC_Direct_Connect_Binding_get(
|
||||||
|
uint32_t object_instance, uint16_t *port, char **ifname);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Direct_Connect_Binding_Set(
|
||||||
|
uint32_t object_instance, const char *str);
|
||||||
|
bool Network_Port_SC_Direct_Connect_Binding_Dirty_Set(
|
||||||
|
uint32_t object_instance, const char *str);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BACNET_SC_DIRECT_CONNECTION_STATUS *
|
||||||
|
Network_Port_SC_Direct_Connect_Connection_Status_Get(
|
||||||
|
uint32_t object_instance, uint8_t index);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Direct_Connect_Connection_Status_Add(
|
||||||
|
uint32_t object_instance,
|
||||||
|
const char *uri,
|
||||||
|
BACNET_SC_CONNECTION_STATE state,
|
||||||
|
BACNET_DATE_TIME *connect_ts,
|
||||||
|
BACNET_DATE_TIME *disconnect_ts,
|
||||||
|
BACNET_HOST_N_PORT_DATA *peer_address,
|
||||||
|
uint8_t *peer_VMAC,
|
||||||
|
uint8_t *peer_UUID,
|
||||||
|
BACNET_ERROR_CODE error,
|
||||||
|
const char *error_details);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Direct_Connect_Connection_Status_Delete_All(
|
||||||
|
uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int Network_Port_SC_Direct_Connect_Connection_Status_Count(
|
||||||
|
uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int Network_Port_SC_Direct_Connect_Connection_Status_Encode(
|
||||||
|
uint32_t object_instance, uint8_t *apdu, int max_apdu);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BACNET_SC_FAILED_CONNECTION_REQUEST *
|
||||||
|
Network_Port_SC_Failed_Connection_Requests_Get(
|
||||||
|
uint32_t object_instance, uint8_t index);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Failed_Connection_Requests_Add(
|
||||||
|
uint32_t object_instance,
|
||||||
|
BACNET_DATE_TIME *ts,
|
||||||
|
BACNET_HOST_N_PORT_DATA *peer_address,
|
||||||
|
uint8_t *peer_VMAC,
|
||||||
|
uint8_t *peer_UUID,
|
||||||
|
BACNET_ERROR_CODE error,
|
||||||
|
const char *error_details);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Failed_Connection_Requests_Delete_All(
|
||||||
|
uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int Network_Port_SC_Failed_Connection_Requests_Count(uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int Network_Port_SC_Failed_Connection_Requests_Encode(
|
||||||
|
uint32_t object_instance, uint8_t *apdu, int max_apdu);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
uint32_t Network_Port_Certificate_Key_File(uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_Certificate_Key_File_Set(
|
||||||
|
uint32_t object_instance, uint32_t value);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
const BACNET_UUID *Network_Port_SC_Local_UUID(uint32_t object_instance);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool Network_Port_SC_Local_UUID_Set(
|
||||||
|
uint32_t object_instance, BACNET_UUID *value);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
#endif
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
#define BACDL_MSTP
|
#define BACDL_MSTP
|
||||||
#define BACDL_BIP
|
#define BACDL_BIP
|
||||||
#define BACDL_BIP6
|
#define BACDL_BIP6
|
||||||
|
#define BACDL_BSC
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(BACDL_ETHERNET)
|
#if defined(BACDL_ETHERNET)
|
||||||
@@ -65,6 +66,13 @@
|
|||||||
#define BACDL_SOME_DATALINK_ENABLED 1
|
#define BACDL_SOME_DATALINK_ENABLED 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(BACDL_BSC)
|
||||||
|
#if defined(BACDL_SOME_DATALINK_ENABLED)
|
||||||
|
#define BACDL_MULTIPLE 1
|
||||||
|
#endif
|
||||||
|
#define BACDL_SOME_DATALINK_ENABLED 1
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(BACDL_CUSTOM)
|
#if defined(BACDL_CUSTOM)
|
||||||
#if defined(BACDL_SOME_DATALINK_ENABLED)
|
#if defined(BACDL_SOME_DATALINK_ENABLED)
|
||||||
#define BACDL_MULTIPLE 1
|
#define BACDL_MULTIPLE 1
|
||||||
@@ -144,6 +152,27 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(BACDL_BSC)
|
||||||
|
#ifndef SC_NETPORT_BVLC_MAX
|
||||||
|
#define SC_NETPORT_BVLC_MAX 1500
|
||||||
|
#endif
|
||||||
|
#ifndef SC_NETPORT_NPDU_MAX
|
||||||
|
#define SC_NETPORT_NPDU_MAX 1500
|
||||||
|
#endif
|
||||||
|
#ifndef SC_NETPORT_CONNECT_TIMEOUT
|
||||||
|
#define SC_NETPORT_CONNECT_TIMEOUT 5
|
||||||
|
#endif
|
||||||
|
#ifndef SC_NETPORT_HEARTBEAT_TIMEOUT
|
||||||
|
#define SC_NETPORT_HEARTBEAT_TIMEOUT 60
|
||||||
|
#endif
|
||||||
|
#ifndef SC_NETPORT_DISCONNECT_TIMEOUT
|
||||||
|
#define SC_NETPORT_DISCONNECT_TIMEOUT 150
|
||||||
|
#endif
|
||||||
|
#ifndef SC_NETPORT_RECONNECT_TIME
|
||||||
|
#define SC_NETPORT_RECONNECT_TIME 2
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
/* for confirmed messages, this is the number of transactions */
|
/* for confirmed messages, this is the number of transactions */
|
||||||
/* that we hold in a queue waiting for timeout. */
|
/* that we hold in a queue waiting for timeout. */
|
||||||
/* Configure to zero if you don't want any confirmed messages */
|
/* Configure to zero if you don't want any confirmed messages */
|
||||||
@@ -205,6 +234,7 @@
|
|||||||
defined(BACAPP_SHED_LEVEL) || \
|
defined(BACAPP_SHED_LEVEL) || \
|
||||||
defined(BACAPP_ACCESS_RULE) || \
|
defined(BACAPP_ACCESS_RULE) || \
|
||||||
defined(BACAPP_CHANNEL_VALUE) || \
|
defined(BACAPP_CHANNEL_VALUE) || \
|
||||||
|
defined(BACAPP_SECURE_CONNECT) || \
|
||||||
defined(BACAPP_TYPES_EXTRA))
|
defined(BACAPP_TYPES_EXTRA))
|
||||||
#define BACAPP_ALL
|
#define BACAPP_ALL
|
||||||
#endif
|
#endif
|
||||||
@@ -253,6 +283,7 @@
|
|||||||
#define BACAPP_SHED_LEVEL
|
#define BACAPP_SHED_LEVEL
|
||||||
#define BACAPP_ACCESS_RULE
|
#define BACAPP_ACCESS_RULE
|
||||||
#define BACAPP_CHANNEL_VALUE
|
#define BACAPP_CHANNEL_VALUE
|
||||||
|
#define BACAPP_SECURE_CONNECT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* clang-format off */
|
/* clang-format off */
|
||||||
@@ -270,6 +301,7 @@
|
|||||||
defined(BACAPP_DEVICE_OBJECT_REFERENCE) || \
|
defined(BACAPP_DEVICE_OBJECT_REFERENCE) || \
|
||||||
defined(BACAPP_OBJECT_PROPERTY_REFERENCE) || \
|
defined(BACAPP_OBJECT_PROPERTY_REFERENCE) || \
|
||||||
defined(BACAPP_DESTINATION) || \
|
defined(BACAPP_DESTINATION) || \
|
||||||
|
defined(BACAPP_SECURE_CONNECT) || \
|
||||||
defined(BACAPP_BDT_ENTRY) || \
|
defined(BACAPP_BDT_ENTRY) || \
|
||||||
defined(BACAPP_FDT_ENTRY) || \
|
defined(BACAPP_FDT_ENTRY) || \
|
||||||
defined(BACAPP_ACTION_COMMAND) || \
|
defined(BACAPP_ACTION_COMMAND) || \
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
This readme explains how to enable support of BACNet Secure Connect datalink
|
||||||
|
(ANNEX AB – BACnet Secure Connect in standard), how to set up the building
|
||||||
|
environment and clarifies some important moments regarding implementation
|
||||||
|
of that functionality.
|
||||||
|
|
||||||
|
By default the support of that functionality is turned off, to enable it
|
||||||
|
you need to set option BACDL_BSC=ON in CMakeLists.txt if cmake build system
|
||||||
|
is used or set BACDL_BSC=1 define in a case if using makefiles.
|
||||||
|
|
||||||
|
BACNet/SC standard uses websockets as a transport layer, so Windows/Linux/BSD
|
||||||
|
implementations use libwebsocket library. Implementation for Zephyr uses
|
||||||
|
native websocket API for the client side and mongoose library for the
|
||||||
|
server side. Websocket layer which is built on the top of libwebsockets
|
||||||
|
uses 1 service thread per websocket server instance and 1 service thread
|
||||||
|
per 1 websocket client instance. As a result, libwebsocket must be built
|
||||||
|
with LWS_MAX_SMP > 1, otherwise rarely crashes may ocure in the application
|
||||||
|
which uses bacnet stack library. Recommended value for that define is 32.
|
||||||
|
You should note, that by default libwebsocket is built with LWS_MAX_SMP=1,
|
||||||
|
so a packet manager like vcpkg and apt can have that lib built with
|
||||||
|
LWS_MAX_SMP=1, which can lead to unstable work. It's better to build
|
||||||
|
libwebsockets from recent stable sources for your platform, corresponded
|
||||||
|
examples of how to do that you can find in .github/workflows/bsc-tests-platform.yml,
|
||||||
|
(for example bsc-tests-linux.yml).
|
||||||
|
|
||||||
|
In order to build bacnet stack library for linux, user needs to install
|
||||||
|
libconfig-dev, libcap-dev and libssl-dev to the system. Most easiest way to do
|
||||||
|
that is to use Advanced Packaging Tool (APT), check bsc-test-linux.yml.
|
||||||
|
|
||||||
|
For MacOSX build user must install brew packet manager (https://brew.sh),
|
||||||
|
then install openssl using brew then build libwebsocket, check
|
||||||
|
.github/workflows/bsc-tests-macosx.yml.
|
||||||
|
|
||||||
|
Windows build may be a challenge because libwebsocket depends on libpthreads
|
||||||
|
which not so easy to find and build on windows, refer to the libwebsocket
|
||||||
|
build instruction https://libwebsockets.org/lws-api-doc-master/html/md_README_8build.html.
|
||||||
|
Using of vcpkg packet manager can save a lot of time, but you must ensure that
|
||||||
|
installed libwebsocket library is compiled with LWS_MAX_SMP > 1.
|
||||||
|
A good example how to use vcpkg packet manager with custom build of
|
||||||
|
libwebsocket can be found in .github/workflows/bsc-tests-windows.yml.
|
||||||
|
|
||||||
|
One moment is still needed to be improved. The current implementation processes
|
||||||
|
dataflow between BACNET/SC nodes ignoring max_bvlc_size and max_npdu_size
|
||||||
|
params in connect request and connect accept packets as that is not
|
||||||
|
explicitly explained in standard. As a result if a node which has max_npdu_size
|
||||||
|
less than remote peer can drop received packet if it does not fit into it's internal
|
||||||
|
buffer which depends on BVLC_SC_NPDU_SIZE_CONF parameter. In opposite, a node
|
||||||
|
can send a PDU more than max_npdu_size which may also lead to the drop of PDU
|
||||||
|
on remote peer side.
|
||||||
|
|
||||||
|
The current implementation does not support Certificate_Signing_Request_File
|
||||||
|
property of BACNET/SC netport and properties Operational_Certificate_File
|
||||||
|
and Issuer_Certificate_Files related to certificates are readonly and can't
|
||||||
|
be changed remotely. So, management of device certificates is out of the scope
|
||||||
|
of current BACNet/SC implementation.
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Configuration file of BACNet/SC datalink.
|
||||||
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||||
|
* @date August 2022
|
||||||
|
* @copyright SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
#ifndef BACNET_DATALINK_BSC_CONF_H
|
||||||
|
#define BACNET_DATALINK_BSC_CONF_H
|
||||||
|
/* BACnet Stack defines - first */
|
||||||
|
#include "bacnet/bacdef.h"
|
||||||
|
/* BACnet Stack API */
|
||||||
|
#include "bvlc-sc.h"
|
||||||
|
|
||||||
|
#if !defined(BACDL_BSC)
|
||||||
|
#define BSC_CONF_TX_PRE 0
|
||||||
|
#else
|
||||||
|
#ifndef bsd
|
||||||
|
#define bsd 1
|
||||||
|
#endif
|
||||||
|
#ifndef linux
|
||||||
|
#define linux 2
|
||||||
|
#endif
|
||||||
|
#ifndef win32
|
||||||
|
#define win32 3
|
||||||
|
#endif
|
||||||
|
#if BACNET_PORT == bsd || BACNET_PORT == linux || BACNET_PORT == win32
|
||||||
|
#include <libwebsockets.h>
|
||||||
|
#define BSC_CONF_TX_PRE LWS_PRE
|
||||||
|
#else
|
||||||
|
#define BSC_CONF_TX_PRE 0
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BSC_CONF_HUB_CONNECTORS_NUM
|
||||||
|
#define BSC_CONF_HUB_CONNECTORS_NUM 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BSC_CONF_HUB_FUNCTIONS_NUM
|
||||||
|
#define BSC_CONF_HUB_FUNCTIONS_NUM 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BSC_CONF_NODE_SWITCHES_NUM
|
||||||
|
#define BSC_CONF_NODE_SWITCHES_NUM 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BSC_CONF_NODES_NUM
|
||||||
|
#define BSC_CONF_NODES_NUM 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BVLC_SC_NPDU_SIZE_CONF
|
||||||
|
/* 16 bytes is sum of all sizes of all static (non variable)
|
||||||
|
fields of header of BVLC message */
|
||||||
|
#define BVLC_SC_NPDU_SIZE_CONF ((MAX_PDU) + 16)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BSC_CONF_WEBSOCKET_RX_BUFFER_LEN
|
||||||
|
#define BSC_CONF_WEBSOCKET_RX_BUFFER_LEN BVLC_SC_NPDU_SIZE_CONF
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* THIS should not be changed, most of BACNet/SC devices must have */
|
||||||
|
/* hub connector, it uses 2 connections */
|
||||||
|
#ifndef BSC_CONF_HUB_CONNECTOR_CONNECTIONS_NUM
|
||||||
|
#define BSC_CONF_HUB_CONNECTOR_CONNECTIONS_NUM (BSC_CONF_HUB_CONNECTORS_NUM * 2)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BSC_CONF_HUB_FUNCTION_CONNECTIONS_NUM
|
||||||
|
#define BSC_CONF_HUB_FUNCTION_CONNECTIONS_NUM (BSC_CONF_HUB_FUNCTIONS_NUM * 10)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BSC_CONF_NODE_SWITCH_CONNECTIONS_NUM
|
||||||
|
#define BSC_CONF_NODE_SWITCH_CONNECTIONS_NUM 10
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Total amount of client(initiator) webosocket connections */
|
||||||
|
#ifndef BSC_CONF_CLIENT_CONNECTIONS_NUM
|
||||||
|
#define BSC_CONF_CLIENT_CONNECTIONS_NUM \
|
||||||
|
(BSC_CONF_HUB_CONNECTOR_CONNECTIONS_NUM + \
|
||||||
|
BSC_CONF_NODE_SWITCH_CONNECTIONS_NUM * BSC_CONF_NODE_SWITCHES_NUM)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BSC_CONF_SERVER_HUB_CONNECTIONS_MAX_NUM
|
||||||
|
#define BSC_CONF_SERVER_HUB_CONNECTIONS_MAX_NUM \
|
||||||
|
(BSC_CONF_HUB_FUNCTION_CONNECTIONS_NUM)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BSC_CONF_SERVER_DIRECT_CONNECTIONS_MAX_NUM
|
||||||
|
#define BSC_CONF_SERVER_DIRECT_CONNECTIONS_MAX_NUM \
|
||||||
|
(BSC_CONF_NODE_SWITCH_CONNECTIONS_NUM * BSC_CONF_NODE_SWITCHES_NUM)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BSC_CONF_SOCKET_TX_BUFFERED_PACKET_NUM 2
|
||||||
|
#define BSC_CONF_DATALINK_BUFFERED_PACKET_NUM 10
|
||||||
|
|
||||||
|
#define BSC_CONF_SOCK_RX_BUFFER_SIZE BVLC_SC_NPDU_SIZE_CONF
|
||||||
|
|
||||||
|
/* 2 bytes is a prefix containing BVLC message length.
|
||||||
|
BSC_CONF_TX_PRE - some reserved bytes before actual payload.
|
||||||
|
Some libs like libwebsocket requires some bytes to be reserved
|
||||||
|
before actual payload for sending, so BSC_CONF_TX_PRE is used for
|
||||||
|
that purpose (it allows to avoid copying of payload and
|
||||||
|
buffer reallocation)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define BSC_CONF_SOCK_TX_BUFFER_SIZE \
|
||||||
|
((BVLC_SC_NPDU_SIZE_CONF + 2 + BSC_CONF_TX_PRE) * \
|
||||||
|
BSC_CONF_SOCKET_TX_BUFFERED_PACKET_NUM)
|
||||||
|
|
||||||
|
/* datalink RX buffer size is always rounded to next power of two */
|
||||||
|
/* so if final buffer size is 1628 it will be rounded to 2048 */
|
||||||
|
|
||||||
|
#define BSC_CONF_DATALINK_RX_BUFFER_SIZE \
|
||||||
|
(BVLC_SC_NPDU_SIZE_CONF * BSC_CONF_DATALINK_BUFFERED_PACKET_NUM)
|
||||||
|
|
||||||
|
#ifndef BSC_CONF_WSURL_MAX_LEN
|
||||||
|
#define BSC_CONF_WSURL_MAX_LEN 128
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BSC_CONF_WEBSOCKET_ERR_DESC_STR_MAX_LEN
|
||||||
|
#define BSC_CONF_WEBSOCKET_ERR_DESC_STR_MAX_LEN 128
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BSC_CONF_NODE_MAX_URI_SIZE_IN_ADDRESS_RESOLUTION_ACK
|
||||||
|
#define BSC_CONF_NODE_MAX_URI_SIZE_IN_ADDRESS_RESOLUTION_ACK \
|
||||||
|
BSC_CONF_WSURL_MAX_LEN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BSC_CONF_NODE_MAX_URIS_NUM_IN_ADDRESS_RESOLUTION_ACK
|
||||||
|
#define BSC_CONF_NODE_MAX_URIS_NUM_IN_ADDRESS_RESOLUTION_ACK \
|
||||||
|
(BSC_CONF_SOCK_RX_BUFFER_SIZE / \
|
||||||
|
BSC_CONF_NODE_MAX_URI_SIZE_IN_ADDRESS_RESOLUTION_ACK - \
|
||||||
|
1)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BSC_CONF_OPERATIONAL_CERTIFICATE_FILE_INSTANCE
|
||||||
|
#define BSC_CONF_OPERATIONAL_CERTIFICATE_FILE_INSTANCE 5
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BSC_CONF_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE
|
||||||
|
#define BSC_CONF_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE 6
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BSC_CONF_ISSUER_CERTIFICATE_FILE_1_INSTANCE
|
||||||
|
#define BSC_CONF_ISSUER_CERTIFICATE_FILE_1_INSTANCE 7
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BSC_CONF_ISSUER_CERTIFICATE_FILE_2_INSTANCE
|
||||||
|
#define BSC_CONF_ISSUER_CERTIFICATE_FILE_2_INSTANCE 8
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BSC_CONF_ISSUER_CERTIFICATE_FILE_3_INSTANCE
|
||||||
|
#define BSC_CONF_ISSUER_CERTIFICATE_FILE_3_INSTANCE 9
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BSC_CONF_HUB_FUNCTION_CONNECTION_STATUS_MAX_NUM
|
||||||
|
#define BSC_CONF_HUB_FUNCTION_CONNECTION_STATUS_MAX_NUM \
|
||||||
|
BSC_CONF_HUB_FUNCTION_CONNECTIONS_NUM
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BSC_CONF_NODE_SWITCH_CONNECTION_STATUS_MAX_NUM
|
||||||
|
#define BSC_CONF_NODE_SWITCH_CONNECTION_STATUS_MAX_NUM \
|
||||||
|
BSC_CONF_NODE_SWITCH_CONNECTIONS_NUM
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BSC_CONF_FAILED_CONNECTION_STATUS_MAX_NUM
|
||||||
|
#define BSC_CONF_FAILED_CONNECTION_STATUS_MAX_NUM 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,653 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief BACnet secure connect hub function API.
|
||||||
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||||
|
* @date October 2022
|
||||||
|
* @copyright SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
#include "bacnet/basic/sys/debug.h"
|
||||||
|
#include <bacnet/basic/sys/fifo.h>
|
||||||
|
#include "bacnet/datalink/bsc/bsc-conf.h"
|
||||||
|
#include "bacnet/datalink/bsc/bvlc-sc.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-datalink.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-socket.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-util.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-event.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-hub-function.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-node.h"
|
||||||
|
#include "bacnet/bacdef.h"
|
||||||
|
#include "bacnet/npdu.h"
|
||||||
|
#include "bacnet/bacenum.h"
|
||||||
|
#include "bacnet/basic/object/netport.h"
|
||||||
|
#include "bacnet/basic/object/sc_netport.h"
|
||||||
|
#include "bacnet/basic/object/bacfile.h"
|
||||||
|
|
||||||
|
#define PRINTF debug_perror
|
||||||
|
#define DEBUG_BSC_DATALINK 0
|
||||||
|
#if DEBUG_BSC_DATALINK == 1
|
||||||
|
#define DEBUG_PRINTF debug_printf
|
||||||
|
#else
|
||||||
|
#define DEBUG_PRINTF debug_printf_disabled
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BSC_NEXT_POWER_OF_TWO1(v) \
|
||||||
|
((((unsigned int)v) - 1) | ((((unsigned int)v) - 1) >> 1))
|
||||||
|
#define BSC_NEXT_POWER_OF_TWO2(v) \
|
||||||
|
(BSC_NEXT_POWER_OF_TWO1(v) | BSC_NEXT_POWER_OF_TWO1(v) >> 2)
|
||||||
|
#define BSC_NEXT_POWER_OF_TWO3(v) \
|
||||||
|
(BSC_NEXT_POWER_OF_TWO2(v) | BSC_NEXT_POWER_OF_TWO2(v) >> 4)
|
||||||
|
#define BSC_NEXT_POWER_OF_TWO4(v) \
|
||||||
|
(BSC_NEXT_POWER_OF_TWO3(v) | BSC_NEXT_POWER_OF_TWO3(v) >> 8)
|
||||||
|
#define BSC_NEXT_POWER_OF_TWO(v) \
|
||||||
|
((BSC_NEXT_POWER_OF_TWO4(v) | BSC_NEXT_POWER_OF_TWO4(v) >> 16)) + 1
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BSC_DATALINK_STATE_IDLE = 0,
|
||||||
|
BSC_DATALINK_STATE_STARTING = 1,
|
||||||
|
BSC_DATALINK_STATE_STARTED = 2,
|
||||||
|
BSC_DATALINK_STATE_STOPPING = 3
|
||||||
|
} BSC_DATALINK_STATE;
|
||||||
|
|
||||||
|
static FIFO_BUFFER bsc_fifo = { 0 };
|
||||||
|
static uint8_t
|
||||||
|
bsc_fifo_buf[BSC_NEXT_POWER_OF_TWO(BSC_CONF_DATALINK_RX_BUFFER_SIZE)];
|
||||||
|
static BSC_NODE *bsc_node = NULL;
|
||||||
|
static BSC_NODE_CONF bsc_conf;
|
||||||
|
static BSC_DATALINK_STATE bsc_datalink_state = BSC_DATALINK_STATE_IDLE;
|
||||||
|
static BSC_EVENT *bsc_event = NULL;
|
||||||
|
static BSC_EVENT *bsc_data_event = NULL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief bsc_node_event() is a callback function which is called by
|
||||||
|
* BACnet/SC datalink when some event occurs.
|
||||||
|
* The function is used to notify the upper layer about the events.
|
||||||
|
* @param node - pointer to the BACnet/SC node
|
||||||
|
* @param ev - event type
|
||||||
|
* @param dest - pointer to the destination address
|
||||||
|
* @param pdu - pointer to the received PDU
|
||||||
|
* @param pdu_len - length of the received PDU
|
||||||
|
*/
|
||||||
|
static void bsc_node_event(
|
||||||
|
BSC_NODE *node,
|
||||||
|
BSC_NODE_EVENT ev,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *dest,
|
||||||
|
uint8_t *pdu,
|
||||||
|
size_t pdu_len)
|
||||||
|
{
|
||||||
|
uint16_t pdu16_len;
|
||||||
|
DEBUG_PRINTF("bsc_node_event() >>> ev = %d\n", ev);
|
||||||
|
bws_dispatch_lock();
|
||||||
|
(void)node;
|
||||||
|
(void)dest;
|
||||||
|
if (ev == BSC_NODE_EVENT_STARTED || ev == BSC_NODE_EVENT_STOPPED) {
|
||||||
|
if (bsc_datalink_state != BSC_DATALINK_STATE_IDLE) {
|
||||||
|
bsc_event_signal(bsc_event);
|
||||||
|
}
|
||||||
|
} else if (ev == BSC_NODE_EVENT_RECEIVED_NPDU) {
|
||||||
|
if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED) {
|
||||||
|
if (pdu_len <= USHRT_MAX &&
|
||||||
|
FIFO_Available(
|
||||||
|
&bsc_fifo, (unsigned)(pdu_len + sizeof(pdu16_len)))) {
|
||||||
|
pdu16_len = (uint16_t)pdu_len;
|
||||||
|
FIFO_Add(&bsc_fifo, (uint8_t *)&pdu16_len, sizeof(pdu16_len));
|
||||||
|
FIFO_Add(&bsc_fifo, pdu, pdu16_len);
|
||||||
|
bsc_event_signal(bsc_data_event);
|
||||||
|
}
|
||||||
|
#if DEBUG_ENABLED == 1
|
||||||
|
else {
|
||||||
|
PRINTF("pdu of size %d\n is dropped\n", pdu_len);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
DEBUG_PRINTF("bsc_node_event() <<<\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief bsc_deinit_resources() is a function which is used to
|
||||||
|
* deinitialize all resources allocated by BACnet/SC datalink.
|
||||||
|
*/
|
||||||
|
static void bsc_deinit_resources(void)
|
||||||
|
{
|
||||||
|
if (bsc_event) {
|
||||||
|
bsc_event_deinit(bsc_event);
|
||||||
|
bsc_event = NULL;
|
||||||
|
}
|
||||||
|
if (bsc_data_event) {
|
||||||
|
bsc_event_deinit(bsc_data_event);
|
||||||
|
bsc_data_event = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the BACnet/SC datalink layer
|
||||||
|
* @param ifname - name of the network interface
|
||||||
|
* @return true if the initialization was successful, otherwise false
|
||||||
|
*/
|
||||||
|
bool bsc_init(char *ifname)
|
||||||
|
{
|
||||||
|
BSC_SC_RET r;
|
||||||
|
bool ret = false;
|
||||||
|
DEBUG_PRINTF("bsc_init() >>>\n");
|
||||||
|
|
||||||
|
(void)ifname;
|
||||||
|
|
||||||
|
bws_dispatch_lock();
|
||||||
|
|
||||||
|
if (bsc_datalink_state != BSC_DATALINK_STATE_IDLE) {
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
PRINTF("bsc_init() <<< ret = %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bsc_event = bsc_event_init();
|
||||||
|
bsc_data_event = bsc_event_init();
|
||||||
|
|
||||||
|
if (!bsc_event || !bsc_data_event) {
|
||||||
|
bsc_deinit_resources();
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
PRINTF("bsc_init() <<< ret = %d\n", false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bsc_init() BACNET/SC datalink configured to use input fifo "
|
||||||
|
"of size %d\n",
|
||||||
|
sizeof(bsc_fifo_buf));
|
||||||
|
FIFO_Init(&bsc_fifo, bsc_fifo_buf, sizeof(bsc_fifo_buf));
|
||||||
|
|
||||||
|
ret = bsc_node_conf_fill_from_netport(&bsc_conf, &bsc_node_event);
|
||||||
|
|
||||||
|
if (!ret) {
|
||||||
|
bsc_deinit_resources();
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
PRINTF("bsc_init() <<< configuration of BACNET/SC datalink "
|
||||||
|
"failed, ret = false\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bsc_datalink_state = BSC_DATALINK_STATE_STARTING;
|
||||||
|
r = bsc_node_init(&bsc_conf, &bsc_node);
|
||||||
|
if (r == BSC_SC_SUCCESS) {
|
||||||
|
r = bsc_node_start(bsc_node);
|
||||||
|
if (r == BSC_SC_SUCCESS) {
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
bsc_event_wait(bsc_event);
|
||||||
|
bws_dispatch_lock();
|
||||||
|
bsc_datalink_state = BSC_DATALINK_STATE_STARTED;
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
DEBUG_PRINTF("bsc_init() <<< ret = %d\n", true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bsc_deinit_resources();
|
||||||
|
bsc_node_conf_cleanup(&bsc_conf);
|
||||||
|
bsc_datalink_state = BSC_DATALINK_STATE_IDLE;
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
PRINTF("bsc_init() <<< ret = %d\n", false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Blocking thread-safe bsc_cleanup() function
|
||||||
|
* de-initializes BACNet/SC datalink.
|
||||||
|
*/
|
||||||
|
void bsc_cleanup(void)
|
||||||
|
{
|
||||||
|
DEBUG_PRINTF("bsc_cleanup() >>>\n");
|
||||||
|
bws_dispatch_lock();
|
||||||
|
if (bsc_datalink_state != BSC_DATALINK_STATE_IDLE &&
|
||||||
|
bsc_datalink_state != BSC_DATALINK_STATE_STOPPING) {
|
||||||
|
if (bsc_datalink_state == BSC_DATALINK_STATE_STARTING) {
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
bsc_event_wait(bsc_event);
|
||||||
|
bws_dispatch_lock();
|
||||||
|
}
|
||||||
|
if (bsc_datalink_state != BSC_DATALINK_STATE_STOPPING) {
|
||||||
|
bsc_datalink_state = BSC_DATALINK_STATE_STOPPING;
|
||||||
|
bsc_event_signal(bsc_data_event);
|
||||||
|
bsc_node_stop(bsc_node);
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
bsc_event_wait(bsc_event);
|
||||||
|
bsc_event_wait(bsc_data_event);
|
||||||
|
bws_dispatch_lock();
|
||||||
|
bsc_deinit_resources();
|
||||||
|
(void)bsc_node_deinit(bsc_node);
|
||||||
|
bsc_node_conf_cleanup(&bsc_conf);
|
||||||
|
bsc_node = NULL;
|
||||||
|
bsc_datalink_state = BSC_DATALINK_STATE_IDLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
DEBUG_PRINTF("bsc_cleanup() <<<\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send a BACnet/SC PDU to a remote node
|
||||||
|
* @param dest - destination address
|
||||||
|
* @param npdu_data - network layer data
|
||||||
|
* @param pdu - PDU to send
|
||||||
|
* @param pdu_len - length of the PDU
|
||||||
|
* @return number of bytes sent on success, negative number on failure
|
||||||
|
*/
|
||||||
|
int bsc_send_pdu(
|
||||||
|
BACNET_ADDRESS *dest,
|
||||||
|
BACNET_NPDU_DATA *npdu_data,
|
||||||
|
uint8_t *pdu,
|
||||||
|
unsigned pdu_len)
|
||||||
|
{
|
||||||
|
BSC_SC_RET ret;
|
||||||
|
BACNET_SC_VMAC_ADDRESS dest_vmac;
|
||||||
|
int len = -1;
|
||||||
|
static uint8_t buf[BVLC_SC_NPDU_SIZE_CONF];
|
||||||
|
|
||||||
|
/* this datalink doesn't need to know the npdu data */
|
||||||
|
(void)npdu_data;
|
||||||
|
|
||||||
|
bws_dispatch_lock();
|
||||||
|
|
||||||
|
if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED) {
|
||||||
|
if (dest->net == BACNET_BROADCAST_NETWORK || dest->mac_len == 0) {
|
||||||
|
/* broadcast message */
|
||||||
|
memset(&dest_vmac.address[0], 0xFF, BVLC_SC_VMAC_SIZE);
|
||||||
|
} else if (dest->mac_len == BVLC_SC_VMAC_SIZE) {
|
||||||
|
/* unicast */
|
||||||
|
memcpy(&dest_vmac.address[0], &dest->mac[0], BVLC_SC_VMAC_SIZE);
|
||||||
|
} else {
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
PRINTF("bsc_send_pdu() <<< ret = -1, incorrect dest mac address\n");
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = (int)bvlc_sc_encode_encapsulated_npdu(
|
||||||
|
buf, sizeof(buf), bsc_get_next_message_id(), NULL, &dest_vmac, pdu,
|
||||||
|
pdu_len);
|
||||||
|
|
||||||
|
ret = bsc_node_send(bsc_node, buf, len);
|
||||||
|
len = pdu_len;
|
||||||
|
|
||||||
|
if (ret != BSC_SC_SUCCESS) {
|
||||||
|
len = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
DEBUG_PRINTF("bsc_send_pdu() <<< ret = %d\n", len);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove a BACnet/SC packet from the FIFO buffer
|
||||||
|
* @param packet_size - size of the packet to remove
|
||||||
|
*/
|
||||||
|
static void bsc_remove_packet(size_t packet_size)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < packet_size; i++) {
|
||||||
|
(void)FIFO_Get(&bsc_fifo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Blocking thread-safe bsc_receive() function
|
||||||
|
* receives NPDUs transferred over BACNet/SC
|
||||||
|
* from a node specified by it's virtual MAC address as
|
||||||
|
* defined in Clause AB.1.5.2.
|
||||||
|
* @param src - source VMAC address
|
||||||
|
* @param pdu - a buffer to hold the PDU portion of the received packet,
|
||||||
|
* after the BVLC portion has been stripped off.
|
||||||
|
* @param max_pdu - size of the pdu[] buffer
|
||||||
|
* @param timeout_ms - the number of milliseconds to wait for a packet
|
||||||
|
* @return the number of octets (remaining) in the PDU, or zero if no packet
|
||||||
|
*/
|
||||||
|
uint16_t bsc_receive(
|
||||||
|
BACNET_ADDRESS *src, uint8_t *pdu, uint16_t max_pdu, unsigned timeout_ms)
|
||||||
|
{
|
||||||
|
uint16_t pdu_len = 0;
|
||||||
|
uint16_t npdu16_len = 0;
|
||||||
|
BVLC_SC_DECODED_MESSAGE dm;
|
||||||
|
BACNET_ERROR_CODE error;
|
||||||
|
BACNET_ERROR_CLASS class;
|
||||||
|
const char *err_desc = NULL;
|
||||||
|
static uint8_t buf[BVLC_SC_NPDU_SIZE_CONF];
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bsc_receive() >>>\n");
|
||||||
|
|
||||||
|
bws_dispatch_lock();
|
||||||
|
|
||||||
|
if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED) {
|
||||||
|
if (FIFO_Count(&bsc_fifo) <= sizeof(npdu16_len)) {
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
bsc_event_timedwait(bsc_data_event, timeout_ms);
|
||||||
|
bws_dispatch_lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED &&
|
||||||
|
FIFO_Count(&bsc_fifo) > sizeof(npdu16_len)) {
|
||||||
|
DEBUG_PRINTF("bsc_receive() processing data...\n");
|
||||||
|
FIFO_Pull(&bsc_fifo, (uint8_t *)&npdu16_len, sizeof(npdu16_len));
|
||||||
|
|
||||||
|
if (sizeof(buf) < npdu16_len) {
|
||||||
|
PRINTF("bsc_receive() pdu of size %d is dropped\n", npdu16_len);
|
||||||
|
bsc_remove_packet(npdu16_len);
|
||||||
|
} else {
|
||||||
|
FIFO_Pull(&bsc_fifo, buf, npdu16_len);
|
||||||
|
if (!bvlc_sc_decode_message(
|
||||||
|
buf, npdu16_len, &dm, &error, &class, &err_desc)) {
|
||||||
|
PRINTF(
|
||||||
|
"bsc_receive() pdu of size %d is dropped because "
|
||||||
|
"of err = %d, class %d, desc = %s\n",
|
||||||
|
npdu16_len, error, class, err_desc);
|
||||||
|
bsc_remove_packet(npdu16_len);
|
||||||
|
} else {
|
||||||
|
if (dm.hdr.origin &&
|
||||||
|
max_pdu >= dm.payload.encapsulated_npdu.npdu_len) {
|
||||||
|
src->mac_len = BVLC_SC_VMAC_SIZE;
|
||||||
|
memcpy(
|
||||||
|
&src->mac[0], &dm.hdr.origin->address[0],
|
||||||
|
BVLC_SC_VMAC_SIZE);
|
||||||
|
memcpy(
|
||||||
|
pdu, dm.payload.encapsulated_npdu.npdu,
|
||||||
|
dm.payload.encapsulated_npdu.npdu_len);
|
||||||
|
pdu_len =
|
||||||
|
(uint16_t)dm.payload.encapsulated_npdu.npdu_len;
|
||||||
|
}
|
||||||
|
#if DEBUG_ENABLED == 1
|
||||||
|
else {
|
||||||
|
PRINTF(
|
||||||
|
"bsc_receive() pdu of size %d is dropped "
|
||||||
|
"because origin addr is absent or output "
|
||||||
|
"buf of size %d is to small\n",
|
||||||
|
npdu16_len, max_pdu);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF("bsc_receive() pdu_len = %d\n", pdu_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
DEBUG_PRINTF("bsc_receive() <<< ret = %d\n", pdu_len);
|
||||||
|
return pdu_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function can be used to retrieve broadcast
|
||||||
|
* VMAC address for BACNet/SC node.
|
||||||
|
* @param dest - value of broadcast VMAC address
|
||||||
|
*/
|
||||||
|
void bsc_get_broadcast_address(BACNET_ADDRESS *dest)
|
||||||
|
{
|
||||||
|
if (dest) {
|
||||||
|
dest->net = BACNET_BROADCAST_NETWORK;
|
||||||
|
dest->mac_len = BVLC_SC_VMAC_SIZE;
|
||||||
|
memset(&dest->mac[0], 0xFF, BVLC_SC_VMAC_SIZE);
|
||||||
|
/* no SADR */
|
||||||
|
dest->len = 0;
|
||||||
|
memset(dest->adr, 0, sizeof(dest->adr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function can be used to retrieve local
|
||||||
|
* VMAC address of initialized BACNet/SC datalink.
|
||||||
|
* @param my_address - value of local VMAC address
|
||||||
|
*/
|
||||||
|
void bsc_get_my_address(BACNET_ADDRESS *my_address)
|
||||||
|
{
|
||||||
|
if (my_address) {
|
||||||
|
memset(my_address, 0, sizeof(*my_address));
|
||||||
|
}
|
||||||
|
|
||||||
|
bws_dispatch_lock();
|
||||||
|
if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED) {
|
||||||
|
my_address->mac_len = BVLC_SC_VMAC_SIZE;
|
||||||
|
memcpy(
|
||||||
|
&my_address->mac[0], &bsc_conf.local_vmac.address[0],
|
||||||
|
BVLC_SC_VMAC_SIZE);
|
||||||
|
}
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Determine if the BACnet/SC direct connection is established
|
||||||
|
* with a remote BACnet/SC node.
|
||||||
|
* @param dest - BACnet/SC VMAC address of the remote node to check direct
|
||||||
|
* connection status
|
||||||
|
* @param urls - array representing the possible URIs of a remote node for
|
||||||
|
* acceptance of direct connections. Can contain 1 element
|
||||||
|
* @param urls_cnt - number of elements in the urls array
|
||||||
|
* @return true if the connection is established, otherwise false
|
||||||
|
*/
|
||||||
|
bool bsc_direct_connection_established(
|
||||||
|
BACNET_SC_VMAC_ADDRESS *dest, char **urls, size_t urls_cnt)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
bws_dispatch_lock();
|
||||||
|
if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED) {
|
||||||
|
ret = bsc_node_direct_connection_established(
|
||||||
|
bsc_node, dest, urls, urls_cnt);
|
||||||
|
}
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start the process of establishing a direct BACnet/SC connection
|
||||||
|
* to a node identified by either urls or dest parameter. The user should
|
||||||
|
* note that if the dest parameter is used, the local node tries to resolve
|
||||||
|
* it (e.g. to get URIs related to dest VMAC from all existing BACnet/SC
|
||||||
|
* nodes in the network). As a result, the process of establishing a BACnet/SC
|
||||||
|
* connection by dest may take an unpredictable amount of time depending on
|
||||||
|
* the current network configuration.
|
||||||
|
* @param dest - BACnet/SC VMAC address of the remote node to check direct
|
||||||
|
* connection status
|
||||||
|
* @param urls - array representing the possible URIs of a remote node for
|
||||||
|
* acceptance of direct connections. Can contain 1 element
|
||||||
|
* @param urls_cnt - number of elements in the urls array
|
||||||
|
* @return BSC_SC_SUCCESS if the process of establishing a BACnet/SC
|
||||||
|
* connection was started successfully, otherwise an error code
|
||||||
|
*/
|
||||||
|
BSC_SC_RET
|
||||||
|
bsc_connect_direct(BACNET_SC_VMAC_ADDRESS *dest, char **urls, size_t urls_cnt)
|
||||||
|
{
|
||||||
|
BSC_SC_RET ret = BSC_SC_INVALID_OPERATION;
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bsc_connect_direct() >>> dest = %p, urls = %p, urls_cnt = %d\n", dest,
|
||||||
|
urls, urls_cnt);
|
||||||
|
bws_dispatch_lock();
|
||||||
|
if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED) {
|
||||||
|
ret = bsc_node_connect_direct(bsc_node, dest, urls, urls_cnt);
|
||||||
|
}
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
DEBUG_PRINTF("bsc_connect_direct() ret = %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Disconnect a direct BACnet/SC connection with a remote node
|
||||||
|
* identified by its VMAC address.
|
||||||
|
* @param dest - BACnet/SC VMAC address of the remote node to disconnect
|
||||||
|
* the direct connection with
|
||||||
|
*/
|
||||||
|
void bsc_disconnect_direct(BACNET_SC_VMAC_ADDRESS *dest)
|
||||||
|
{
|
||||||
|
bws_dispatch_lock();
|
||||||
|
if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED) {
|
||||||
|
bsc_node_disconnect_direct(bsc_node, dest);
|
||||||
|
}
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Process the hub connector state
|
||||||
|
*/
|
||||||
|
static void bsc_update_hub_connector_state(void)
|
||||||
|
{
|
||||||
|
BACNET_SC_HUB_CONNECTOR_STATE state;
|
||||||
|
uint32_t instance;
|
||||||
|
|
||||||
|
instance = Network_Port_Index_To_Instance(0);
|
||||||
|
state = bsc_node_hub_connector_state(bsc_node);
|
||||||
|
Network_Port_SC_Hub_Connector_State_Set(instance, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Process the hub connector status
|
||||||
|
*/
|
||||||
|
static void bsc_update_hub_connector_status(void)
|
||||||
|
{
|
||||||
|
BACNET_SC_HUB_CONNECTION_STATUS *status;
|
||||||
|
uint32_t instance;
|
||||||
|
|
||||||
|
instance = Network_Port_Index_To_Instance(0);
|
||||||
|
status = bsc_node_hub_connector_status(bsc_node, true);
|
||||||
|
if (status) {
|
||||||
|
Network_Port_SC_Primary_Hub_Connection_Status_Set(
|
||||||
|
instance, status->State, &status->Connect_Timestamp,
|
||||||
|
&status->Disconnect_Timestamp, status->Error,
|
||||||
|
status->Error_Details[0] ? status->Error_Details : NULL);
|
||||||
|
}
|
||||||
|
status = bsc_node_hub_connector_status(bsc_node, false);
|
||||||
|
if (status) {
|
||||||
|
Network_Port_SC_Failover_Hub_Connection_Status_Set(
|
||||||
|
instance, status->State, &status->Connect_Timestamp,
|
||||||
|
&status->Disconnect_Timestamp, status->Error,
|
||||||
|
status->Error_Details[0] ? status->Error_Details : NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Process the hub function status
|
||||||
|
*/
|
||||||
|
static void bsc_update_hub_function_status(void)
|
||||||
|
{
|
||||||
|
BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *s;
|
||||||
|
size_t cnt;
|
||||||
|
int i;
|
||||||
|
uint32_t instance = Network_Port_Index_To_Instance(0);
|
||||||
|
BACNET_SC_VMAC_ADDRESS uninitialized = { 0 };
|
||||||
|
|
||||||
|
s = bsc_node_hub_function_status(bsc_node, &cnt);
|
||||||
|
if (s) {
|
||||||
|
Network_Port_SC_Hub_Function_Connection_Status_Delete_All(instance);
|
||||||
|
for (i = 0; i < cnt; i++) {
|
||||||
|
if (memcmp(
|
||||||
|
&uninitialized.address[0], &s[i].Peer_VMAC[0],
|
||||||
|
BVLC_SC_VMAC_SIZE) != 0) {
|
||||||
|
Network_Port_SC_Hub_Function_Connection_Status_Add(
|
||||||
|
instance, s[i].State, &s[i].Connect_Timestamp,
|
||||||
|
&s[i].Disconnect_Timestamp, &s[i].Peer_Address,
|
||||||
|
s[i].Peer_VMAC, s[i].Peer_UUID.uuid.uuid128, s[i].Error,
|
||||||
|
s[i].Error_Details[0] == 0 ? NULL : s[i].Error_Details);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add direct connection status to the network port
|
||||||
|
* @param s - direct connection status
|
||||||
|
* @param cnt - number of direct connection statuses
|
||||||
|
*/
|
||||||
|
static void bsc_add_direct_status_to_netport(
|
||||||
|
BACNET_SC_DIRECT_CONNECTION_STATUS *s, size_t cnt)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
uint32_t instance = Network_Port_Index_To_Instance(0);
|
||||||
|
BACNET_SC_VMAC_ADDRESS uninitialized = { 0 };
|
||||||
|
|
||||||
|
for (i = 0; i < cnt; i++) {
|
||||||
|
if (memcmp(
|
||||||
|
&uninitialized.address[0], &s[i].Peer_VMAC[0],
|
||||||
|
BVLC_SC_VMAC_SIZE) != 0) {
|
||||||
|
Network_Port_SC_Direct_Connect_Connection_Status_Add(
|
||||||
|
instance, s[i].URI[0] == 0 ? NULL : s[i].URI, s[i].State,
|
||||||
|
&s[i].Connect_Timestamp, &s[i].Disconnect_Timestamp,
|
||||||
|
&s[i].Peer_Address, s[i].Peer_VMAC, s[i].Peer_UUID.uuid.uuid128,
|
||||||
|
s[i].Error,
|
||||||
|
s[i].Error_Details[0] == 0 ? NULL : s[i].Error_Details);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Process the direct connection status
|
||||||
|
*/
|
||||||
|
static void bsc_update_direct_connection_status(void)
|
||||||
|
{
|
||||||
|
BACNET_SC_DIRECT_CONNECTION_STATUS *s = NULL;
|
||||||
|
size_t cnt = 0;
|
||||||
|
uint32_t instance = Network_Port_Index_To_Instance(0);
|
||||||
|
|
||||||
|
s = bsc_node_direct_connection_status(bsc_node, &cnt);
|
||||||
|
if (s) {
|
||||||
|
Network_Port_SC_Direct_Connect_Connection_Status_Delete_All(instance);
|
||||||
|
if (s && cnt) {
|
||||||
|
bsc_add_direct_status_to_netport(s, cnt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Process the failed requests
|
||||||
|
*/
|
||||||
|
static void bsc_update_failed_requests(void)
|
||||||
|
{
|
||||||
|
BACNET_SC_FAILED_CONNECTION_REQUEST *r;
|
||||||
|
size_t cnt;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
uint32_t instance = Network_Port_Index_To_Instance(0);
|
||||||
|
r = bsc_node_failed_requests_status(bsc_node, &cnt);
|
||||||
|
if (r) {
|
||||||
|
Network_Port_SC_Failed_Connection_Requests_Delete_All(instance);
|
||||||
|
for (i = 0; i < cnt; i++) {
|
||||||
|
if (r[i].Peer_Address.host[0] != 0) {
|
||||||
|
#if DEBUG_ENABLED == 1
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"failed request record %d, host %s, vmac %s, uuid "
|
||||||
|
"%s, error %d, details = %s\n",
|
||||||
|
i, r[i].Peer_Address.host,
|
||||||
|
bsc_vmac_to_string(
|
||||||
|
(BACNET_SC_VMAC_ADDRESS *)r[i].Peer_VMAC),
|
||||||
|
bsc_uuid_to_string(
|
||||||
|
(BACNET_SC_UUID *)r[i].Peer_UUID.uuid.uuid128),
|
||||||
|
r[i].Error, r[i].Error_Details);
|
||||||
|
#endif
|
||||||
|
Network_Port_SC_Failed_Connection_Requests_Add(
|
||||||
|
instance, &r[i].Timestamp, &r[i].Peer_Address,
|
||||||
|
r[i].Peer_VMAC, r[i].Peer_UUID.uuid.uuid128, r[i].Error,
|
||||||
|
r[i].Error_Details[0] != 0 ? r[i].Error_Details : NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update the network port properties
|
||||||
|
*/
|
||||||
|
static void bsc_update_netport_properties(void)
|
||||||
|
{
|
||||||
|
if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED) {
|
||||||
|
bsc_update_hub_connector_state();
|
||||||
|
bsc_update_hub_connector_status();
|
||||||
|
bsc_update_hub_function_status();
|
||||||
|
bsc_update_direct_connection_status();
|
||||||
|
bsc_update_failed_requests();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Manage the BACnet/SC datalink timer
|
||||||
|
* @param seconds - number of elapsed seconds
|
||||||
|
*/
|
||||||
|
void bsc_maintenance_timer(uint16_t seconds)
|
||||||
|
{
|
||||||
|
bws_dispatch_lock();
|
||||||
|
bsc_node_maintenance_timer(seconds);
|
||||||
|
bsc_update_netport_properties();
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
}
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief BACNet/SC datalink public interface API.
|
||||||
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||||
|
* @date October 2022
|
||||||
|
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||||
|
*/
|
||||||
|
#ifndef BACNET_DATALINK_BSC_DATALINK_H
|
||||||
|
#define BACNET_DATALINK_BSC_DATALINK_H
|
||||||
|
/* BACnet Stack defines - first */
|
||||||
|
#include "bacnet/bacdef.h"
|
||||||
|
/* BACnet Stack API */
|
||||||
|
#include "bacnet/npdu.h"
|
||||||
|
#include "bacnet/datalink/bsc/bvlc-sc.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-retcodes.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Blocking thread-safe bsc_init() function
|
||||||
|
* initializes BACNet/SC datalink in accordence with value of properties
|
||||||
|
* from BACNet/SC Network Port Object (sc_netport.h). That means that
|
||||||
|
* user must initialize and set corresponded properties required for
|
||||||
|
* configuration of BACNet/SC datalink before calling of that function.
|
||||||
|
* According to "Addendum cc" to ANSI/ASHRAE Standard 135-2020
|
||||||
|
* https://bacnet.org/wp-content/uploads/sites/4/2022/08/Add-135-2020cc.pdf
|
||||||
|
* most important properties are:
|
||||||
|
* SC_Primary_Hub_URI
|
||||||
|
* SC_Failover_Hub_URI
|
||||||
|
* SC_Maximum_Reconnect_Time
|
||||||
|
* SC_Connect_Wait_Timeout
|
||||||
|
* SC_Disconnect_Wait_Timeout
|
||||||
|
* SC_Heartbeat_Timeout
|
||||||
|
* Operational_Certificate_File
|
||||||
|
* Issuer_Certificate_Files
|
||||||
|
* SC_Hub_Function_Enable
|
||||||
|
* SC_Hub_Function_Binding
|
||||||
|
* SC_Direct_Connect_Initiate_Enable
|
||||||
|
* SC_Direct_Connect_Binding
|
||||||
|
* SC_Direct_Connect_Accept_Enable
|
||||||
|
* @param ifname - ignored and unused it was added for backward compatibility.
|
||||||
|
* @return true if datalink was initiated and started, otherwise returns false.
|
||||||
|
*/
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool bsc_init(char *ifname);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Blocking thread-safe bsc_cleanup() function
|
||||||
|
* de-initializes BACNet/SC datalink.
|
||||||
|
*/
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void bsc_cleanup(void);
|
||||||
|
/**
|
||||||
|
* @brief Function checks if all needed certificate file are present.
|
||||||
|
* @return true if all needed certificate file are present otherwise returns
|
||||||
|
* false.
|
||||||
|
*/
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool bsc_cert_files_check(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Blocking thread-safe bsc_send_pdu() function
|
||||||
|
* sends pdu over BACNet/SC to node specified by
|
||||||
|
* destination address param.
|
||||||
|
*
|
||||||
|
* @param dest [in] BACNet/SC node's virtual MAC address as
|
||||||
|
* defined in Clause AB.1.5.2.
|
||||||
|
* Can be broadcast.
|
||||||
|
* @param npdu_data [in] BACNet/SC datalink does not use that
|
||||||
|
* parameter. Added for backward
|
||||||
|
* compatibility.
|
||||||
|
* @param pdu [in] protocol data unit to be sent.
|
||||||
|
* @param pdu_len [in] - number of bytes to send.
|
||||||
|
* @return Number of bytes sent on success, negative number on failure.
|
||||||
|
*/
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int bsc_send_pdu(
|
||||||
|
BACNET_ADDRESS *dest,
|
||||||
|
BACNET_NPDU_DATA *npdu_data,
|
||||||
|
uint8_t *pdu,
|
||||||
|
unsigned pdu_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Blocking thread-safe bsc_receive() function
|
||||||
|
* receives NPDUs transferred over BACNet/SC
|
||||||
|
* from a node specified by it's virtual MAC address as
|
||||||
|
* defined in Clause AB.1.5.2.
|
||||||
|
*
|
||||||
|
* @param src [out] Source VMAC address
|
||||||
|
* @param pdu [out] A buffer to hold the PDU portion of the received packet,
|
||||||
|
* after the BVLC portion has been stripped off.
|
||||||
|
* @param max_pdu [in] Size of the pdu[] buffer.
|
||||||
|
* @param timeout [in] The number of milliseconds to wait for a packet.
|
||||||
|
* @return The number of octets (remaining) in the PDU, or zero on failure.
|
||||||
|
*/
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
uint16_t bsc_receive(
|
||||||
|
BACNET_ADDRESS *src, uint8_t *pdu, uint16_t max_pdu, unsigned timeout_ms);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function can be used to retrieve broadcast
|
||||||
|
* VMAC address for BACNet/SC node.
|
||||||
|
*
|
||||||
|
* @param addr [out] Value of broadcast VMAC address.
|
||||||
|
*/
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void bsc_get_broadcast_address(BACNET_ADDRESS *addr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function can be used to retrieve local
|
||||||
|
* VMAC address of initialized BACNet/SC datalink.
|
||||||
|
* If function called when datalink is not started,
|
||||||
|
* my_address filled by empty vmac address
|
||||||
|
* X'000000000000' as it defined in clause AB.1.5.2
|
||||||
|
*
|
||||||
|
* @param my_address [out] Value of local VMAC address.
|
||||||
|
*/
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void bsc_get_my_address(BACNET_ADDRESS *my_address);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function checks if BACNet/SC direct connection is
|
||||||
|
* established with remote BACNet/SC node.
|
||||||
|
* User can check the status of connection using either
|
||||||
|
* destination vmac or list of destination urls.
|
||||||
|
* @param dest BACNet/SC vmac of remote node to check direct
|
||||||
|
* connection status.
|
||||||
|
* @param urls this array represents the possible URIs of a
|
||||||
|
* remote node for acceptance of direct connections.
|
||||||
|
* Can contain 1 elem.
|
||||||
|
* @param urls_cnt - size of urls array.
|
||||||
|
* @return true if connection is established otherwise returns
|
||||||
|
* false.
|
||||||
|
*/
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool bsc_direct_connection_established(
|
||||||
|
BACNET_SC_VMAC_ADDRESS *dest, char **urls, size_t urls_cnt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function starts process of establishing of a
|
||||||
|
* direct BACNet/SC connection to node identified by
|
||||||
|
* either urls or dest parameter. User should note that
|
||||||
|
* if dest parameter is used, local node tries to resolve
|
||||||
|
* it (e.g.to get URIs related to dest vmac from all existent
|
||||||
|
* BACNet/SC nodes in network). As a result the process of
|
||||||
|
* establishing of a BACNet/SC connection by dest may
|
||||||
|
* take unpredictable amount of time depending on a current
|
||||||
|
* network configuration.
|
||||||
|
* @param dest BACNet/SC vmac of remote node to check direct
|
||||||
|
* connection status.
|
||||||
|
* @param urls this array represents the possible URIs of a
|
||||||
|
* remote node for acceptance of direct connections.
|
||||||
|
* Can contain 1 elem.
|
||||||
|
* @param urls_cnt - size of urls array.
|
||||||
|
*
|
||||||
|
* @return BSC_SC_SUCCESS if process of a establishing of a BACNet/SC
|
||||||
|
* connection was started successfully, otherwise returns
|
||||||
|
* any retcode from BSC_SC_RET enum.
|
||||||
|
*/
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BSC_SC_RET
|
||||||
|
bsc_connect_direct(BACNET_SC_VMAC_ADDRESS *dest, char **urls, size_t urls_cnt);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void bsc_disconnect_direct(BACNET_SC_VMAC_ADDRESS *dest);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void bsc_maintenance_timer(uint16_t seconds);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief crossplatform event abstraction used in BACnet secure connect.
|
||||||
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||||
|
* @date August 2022
|
||||||
|
* @copyright SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
#ifndef BACNET_DATALINK_BSC_EVENT_H
|
||||||
|
#define BACNET_DATALINK_BSC_EVENT_H
|
||||||
|
/* BACnet Stack defines - first */
|
||||||
|
#include "bacnet/bacdef.h"
|
||||||
|
|
||||||
|
struct BSC_Event;
|
||||||
|
typedef struct BSC_Event BSC_EVENT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief bsc_event_init() allocates and initializes auto-reset
|
||||||
|
* event object to non-signalled state. An event object
|
||||||
|
* can be set to signalled state by the call of
|
||||||
|
* bsc_event_signal(). When the state of the event object
|
||||||
|
* is signaled, it remains signaled until last thread is
|
||||||
|
* released which was blocked on bsc_event_wait() or
|
||||||
|
* bsc_event_timed_wait() calls. When writing the code user
|
||||||
|
* must always remember the following statements:
|
||||||
|
* 1. It is guaranteed that all currently waiting threads will be
|
||||||
|
* unblocked by the call of bsc_event_signal().
|
||||||
|
* 2. If user has called bsc_event_wait() or bsc_event_timedwait()
|
||||||
|
* from thread N after the call of bsc_event_signal() but
|
||||||
|
* when the state of the corresponded event object is still
|
||||||
|
* signalled, it is not guaranteed that thread N will be
|
||||||
|
* unblocked. Thread N may be stay blocked or may unblock
|
||||||
|
* depending on multitasking context of the OS.
|
||||||
|
*
|
||||||
|
* @return handle of event object if function succeeded, otherwise
|
||||||
|
* returns NULL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
BSC_EVENT *bsc_event_init(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief bsc_event_deinit() deinitialize auto-reset
|
||||||
|
* event object. If a user calls that function while
|
||||||
|
* some threads are waiting the specified event object ev
|
||||||
|
* the behaviour is undefined.
|
||||||
|
*
|
||||||
|
* @param ev - handle to previously initialized event object.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void bsc_event_deinit(BSC_EVENT *ev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief bsc_wait() suspends the execution of the current thread
|
||||||
|
* for the specified amount of seconds.
|
||||||
|
*
|
||||||
|
* @param seconds - time to sleep in seconds.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void bsc_wait(int seconds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief bsc_wait() suspends the execution of the current thread
|
||||||
|
* for the specified amount of seconds.
|
||||||
|
*
|
||||||
|
* @param mseconds - time to sleep in milliseconds.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void bsc_wait_ms(int mseconds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief bsc_event_wait() suspends the execution of the current thread
|
||||||
|
* until corresponded event object becomes signalled.
|
||||||
|
*
|
||||||
|
* @param ev - handle to previously initialized event object.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void bsc_event_wait(BSC_EVENT *ev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief bsc_event_timedwait() suspends the execution of the current thread
|
||||||
|
* until corresponded event object becomes signalled
|
||||||
|
* or ms_timeout becomes elapsed.
|
||||||
|
*
|
||||||
|
* @param ev - handle to previously initialized event object.
|
||||||
|
* @param ms_timeout - timeout in milliseconds
|
||||||
|
* @return true if the corresponded event was signalled.
|
||||||
|
* false if ms_timeout was elapsed but event ev is still in
|
||||||
|
* non signalled state.
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool bsc_event_timedwait(BSC_EVENT *ev, unsigned int ms_timeout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief bsc_event_signal() function sets state of corresponded event object
|
||||||
|
* to signalled state.
|
||||||
|
*
|
||||||
|
* @param ev - handle to previously initialized event object.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void bsc_event_signal(BSC_EVENT *ev);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,651 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief BACnet hub connector API.
|
||||||
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||||
|
* @date July 2022
|
||||||
|
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||||
|
*/
|
||||||
|
#include "bacnet/basic/sys/debug.h"
|
||||||
|
#include "bacnet/datalink/bsc/bvlc-sc.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-socket.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-util.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-hub-connector.h"
|
||||||
|
#include "bacnet/bacdef.h"
|
||||||
|
#include "bacnet/npdu.h"
|
||||||
|
#include "bacnet/bacenum.h"
|
||||||
|
#include "bacnet/basic/object/sc_netport.h"
|
||||||
|
#include "bacnet/bactext.h"
|
||||||
|
|
||||||
|
#define DEBUG_BSC_HUB_CONNECTOR 0
|
||||||
|
|
||||||
|
#if DEBUG_BSC_HUB_CONNECTOR == 1
|
||||||
|
#define DEBUG_PRINTF debug_printf
|
||||||
|
#else
|
||||||
|
#undef DEBUG_ENABLED
|
||||||
|
#define DEBUG_PRINTF debug_printf_disabled
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BSC_HUB_CONN_PRIMARY = 0,
|
||||||
|
BSC_HUB_CONN_FAILOVER = 1
|
||||||
|
} BSC_HUB_CONN_TYPE;
|
||||||
|
|
||||||
|
static void hub_connector_socket_event(
|
||||||
|
BSC_SOCKET *c,
|
||||||
|
BSC_SOCKET_EVENT ev,
|
||||||
|
BACNET_ERROR_CODE reason,
|
||||||
|
const char *reason_desc,
|
||||||
|
uint8_t *pdu,
|
||||||
|
size_t pdu_len,
|
||||||
|
BVLC_SC_DECODED_MESSAGE *decoded_pdu);
|
||||||
|
|
||||||
|
static void hub_connector_context_event(BSC_SOCKET_CTX *ctx, BSC_CTX_EVENT ev);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BSC_HUB_CONNECTOR_STATE_IDLE = 0,
|
||||||
|
BSC_HUB_CONNECTOR_STATE_CONNECTING_PRIMARY = 1,
|
||||||
|
BSC_HUB_CONNECTOR_STATE_CONNECTING_FAILOVER = 2,
|
||||||
|
BSC_HUB_CONNECTOR_STATE_CONNECTED_PRIMARY = 3,
|
||||||
|
BSC_HUB_CONNECTOR_STATE_CONNECTED_FAILOVER = 4,
|
||||||
|
BSC_HUB_CONNECTOR_STATE_WAIT_FOR_RECONNECT = 5,
|
||||||
|
BSC_HUB_CONNECTOR_STATE_WAIT_FOR_CTX_DEINIT = 6,
|
||||||
|
BSC_HUB_CONNECTOR_STATE_DUPLICATED_VMAC = 7
|
||||||
|
} BSC_HUB_CONNECTOR_STATE;
|
||||||
|
|
||||||
|
typedef struct BSC_Hub_Connector {
|
||||||
|
bool used;
|
||||||
|
BSC_SOCKET_CTX ctx;
|
||||||
|
BSC_CONTEXT_CFG cfg;
|
||||||
|
BSC_SOCKET sock[2];
|
||||||
|
BSC_HUB_CONNECTOR_STATE state;
|
||||||
|
unsigned int reconnect_timeout_s;
|
||||||
|
uint8_t primary_url[BSC_WSURL_MAX_LEN + 1];
|
||||||
|
uint8_t failover_url[BSC_WSURL_MAX_LEN + 1];
|
||||||
|
struct mstimer t;
|
||||||
|
BSC_HUB_CONNECTOR_EVENT_FUNC event_func;
|
||||||
|
void *user_arg;
|
||||||
|
BACNET_SC_HUB_CONNECTION_STATUS primary_status;
|
||||||
|
BACNET_SC_HUB_CONNECTION_STATUS failover_status;
|
||||||
|
} BSC_HUB_CONNECTOR;
|
||||||
|
|
||||||
|
#if BSC_CONF_HUB_CONNECTORS_NUM > 0
|
||||||
|
static BSC_HUB_CONNECTOR bsc_hub_connector[BSC_CONF_HUB_CONNECTORS_NUM] = { 0 };
|
||||||
|
#else
|
||||||
|
static BSC_HUB_CONNECTOR *bsc_hub_connector = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static BSC_SOCKET_CTX_FUNCS bsc_hub_connector_ctx_funcs = {
|
||||||
|
NULL, NULL, hub_connector_socket_event, hub_connector_context_event, NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the BACnet hub connector status
|
||||||
|
* @param s - pointer to the status structure
|
||||||
|
*/
|
||||||
|
static void hub_connector_reset_status(BACNET_SC_HUB_CONNECTION_STATUS *s)
|
||||||
|
{
|
||||||
|
/* set timestamps to unspecified values */
|
||||||
|
memset(&s->Connect_Timestamp, 0xFF, sizeof(s->Connect_Timestamp));
|
||||||
|
memset(&s->Disconnect_Timestamp, 0xFF, sizeof(s->Disconnect_Timestamp));
|
||||||
|
s->Error = ERROR_CODE_DEFAULT;
|
||||||
|
s->Error_Details[0] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Allocate a hub connector
|
||||||
|
* @return pointer to the allocated hub connector
|
||||||
|
*/
|
||||||
|
static BSC_HUB_CONNECTOR *hub_connector_alloc(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < BSC_CONF_HUB_CONNECTORS_NUM; i++) {
|
||||||
|
if (!bsc_hub_connector[i].used) {
|
||||||
|
memset(&bsc_hub_connector[i], 0, sizeof(bsc_hub_connector[i]));
|
||||||
|
bsc_hub_connector[i].used = true;
|
||||||
|
hub_connector_reset_status(&bsc_hub_connector[i].primary_status);
|
||||||
|
hub_connector_reset_status(&bsc_hub_connector[i].failover_status);
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"hub_connector_alloc() ret = %p\n", &bsc_hub_connector[i]);
|
||||||
|
return &bsc_hub_connector[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF("hub_connector_alloc() ret = %p\n", &bsc_hub_connector[i]);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Free a hub connector
|
||||||
|
* @param c - pointer to the hub connector
|
||||||
|
*/
|
||||||
|
static void hub_connector_free(BSC_HUB_CONNECTOR *c)
|
||||||
|
{
|
||||||
|
DEBUG_PRINTF("hub_connector_free() c = %p\n", c);
|
||||||
|
c->used = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update the BACnet hub connector status
|
||||||
|
* @param s - pointer to the status structure
|
||||||
|
* @param state - new state
|
||||||
|
* @param err - error code
|
||||||
|
* @param err_desc - error description
|
||||||
|
*/
|
||||||
|
static void hub_conector_update_status(
|
||||||
|
BACNET_SC_HUB_CONNECTION_STATUS *s,
|
||||||
|
BACNET_SC_CONNECTION_STATE state,
|
||||||
|
BACNET_ERROR_CODE err,
|
||||||
|
const char *err_desc)
|
||||||
|
{
|
||||||
|
s->State = state;
|
||||||
|
if (state == BACNET_SC_CONNECTION_STATE_NOT_CONNECTED ||
|
||||||
|
state == BACNET_SC_CONNECTION_STATE_DISCONNECTED_WITH_ERRORS) {
|
||||||
|
bsc_set_timestamp(&s->Disconnect_Timestamp);
|
||||||
|
} else if (
|
||||||
|
state == BACNET_SC_CONNECTION_STATE_CONNECTED ||
|
||||||
|
state == BACNET_SC_CONNECTION_STATE_FAILED_TO_CONNECT) {
|
||||||
|
bsc_set_timestamp(&s->Connect_Timestamp);
|
||||||
|
}
|
||||||
|
s->Error = err;
|
||||||
|
s->Error_Details[0] = 0;
|
||||||
|
if (err_desc) {
|
||||||
|
bsc_copy_str(&s->Error_Details[0], err_desc, sizeof(s->Error_Details));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Connect to a BACnet hub
|
||||||
|
* @param p - pointer to the hub connector
|
||||||
|
* @param type - connection type
|
||||||
|
*/
|
||||||
|
static void hub_connector_connect(BSC_HUB_CONNECTOR *p, BSC_HUB_CONN_TYPE type)
|
||||||
|
{
|
||||||
|
BSC_SC_RET ret;
|
||||||
|
char *url = (type == BSC_HUB_CONN_PRIMARY) ? (char *)p->primary_url
|
||||||
|
: (char *)p->failover_url;
|
||||||
|
|
||||||
|
p->state = (type == BSC_HUB_CONN_PRIMARY)
|
||||||
|
? BSC_HUB_CONNECTOR_STATE_CONNECTING_PRIMARY
|
||||||
|
: BSC_HUB_CONNECTOR_STATE_CONNECTING_FAILOVER;
|
||||||
|
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"hub_connector_connect() hub = %p connecting to url %s\n", p, url);
|
||||||
|
|
||||||
|
if (url[0] == 0) {
|
||||||
|
mstimer_set(&p->t, p->reconnect_timeout_s * 1000);
|
||||||
|
p->state = BSC_HUB_CONNECTOR_STATE_WAIT_FOR_RECONNECT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = bsc_connect(&p->ctx, &p->sock[type], url);
|
||||||
|
(void)ret;
|
||||||
|
|
||||||
|
#if DEBUG_ENABLED == 1
|
||||||
|
if (ret != BSC_SC_SUCCESS) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"hub_connector_connect() got error while "
|
||||||
|
"connecting to hub type %d, err = %d\n",
|
||||||
|
type, ret);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Process the hub connector state
|
||||||
|
* @param c - pointer to the hub connector
|
||||||
|
*/
|
||||||
|
static void hub_connector_process_state(BSC_HUB_CONNECTOR *c)
|
||||||
|
{
|
||||||
|
if (c->state == BSC_HUB_CONNECTOR_STATE_WAIT_FOR_RECONNECT) {
|
||||||
|
if (mstimer_expired(&c->t)) {
|
||||||
|
hub_connector_connect(c, BSC_HUB_CONN_PRIMARY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Hub connector maintenance timer
|
||||||
|
* @param seconds - number of elapsed seconds
|
||||||
|
*/
|
||||||
|
void bsc_hub_connector_maintenance_timer(uint16_t seconds)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
(void)seconds;
|
||||||
|
|
||||||
|
bws_dispatch_lock();
|
||||||
|
for (i = 0; i < BSC_CONF_HUB_CONNECTORS_NUM; i++) {
|
||||||
|
if (bsc_hub_connector[i].used) {
|
||||||
|
hub_connector_process_state(&bsc_hub_connector[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Hub connector socket event
|
||||||
|
* @param c - pointer to the socket
|
||||||
|
* @param ev - event
|
||||||
|
* @param disconnect_reason - disconnect reason
|
||||||
|
* @param disconnect_reason_desc - disconnect reason description
|
||||||
|
* @param pdu - pointer to the PDU
|
||||||
|
* @param pdu_len - PDU length
|
||||||
|
* @param decoded_pdu - decoded PDU
|
||||||
|
*/
|
||||||
|
static void hub_connector_socket_event(
|
||||||
|
BSC_SOCKET *c,
|
||||||
|
BSC_SOCKET_EVENT ev,
|
||||||
|
BACNET_ERROR_CODE disconnect_reason,
|
||||||
|
const char *disconnect_reason_desc,
|
||||||
|
uint8_t *pdu,
|
||||||
|
size_t pdu_len,
|
||||||
|
BVLC_SC_DECODED_MESSAGE *decoded_pdu)
|
||||||
|
{
|
||||||
|
BSC_HUB_CONNECTOR *hc;
|
||||||
|
BACNET_SC_CONNECTION_STATE st =
|
||||||
|
BACNET_SC_CONNECTION_STATE_DISCONNECTED_WITH_ERRORS;
|
||||||
|
|
||||||
|
bws_dispatch_lock();
|
||||||
|
hc = (BSC_HUB_CONNECTOR *)c->ctx->user_arg;
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"hub_connector_socket_event() >>> hub_connector = %p, socket "
|
||||||
|
"= %p, ev = %d, reason = %d, reason_desc = %p,"
|
||||||
|
"pdu = %p, pdu_len = %d\n",
|
||||||
|
hc, c, ev, disconnect_reason, disconnect_reason_desc, pdu, pdu_len);
|
||||||
|
DEBUG_PRINTF("hub_connector_socket_event() state = %d\n", hc->state);
|
||||||
|
if (ev == BSC_SOCKET_EVENT_CONNECTED) {
|
||||||
|
if (hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTING_PRIMARY) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"hub_connector_socket_event() hub_connector = %p "
|
||||||
|
"connected primary\n",
|
||||||
|
hc);
|
||||||
|
hc->state = BSC_HUB_CONNECTOR_STATE_CONNECTED_PRIMARY;
|
||||||
|
hub_conector_update_status(
|
||||||
|
&hc->primary_status, BACNET_SC_CONNECTION_STATE_CONNECTED,
|
||||||
|
ERROR_CODE_DEFAULT, NULL);
|
||||||
|
hc->event_func(
|
||||||
|
BSC_HUBC_EVENT_CONNECTED_PRIMARY, hc, hc->user_arg, NULL, 0,
|
||||||
|
NULL);
|
||||||
|
} else if (hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTING_FAILOVER) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"hub_connector_socket_event() hub_connector = %p "
|
||||||
|
"connected failover\n",
|
||||||
|
hc);
|
||||||
|
hc->state = BSC_HUB_CONNECTOR_STATE_CONNECTED_FAILOVER;
|
||||||
|
hub_conector_update_status(
|
||||||
|
&hc->failover_status, BACNET_SC_CONNECTION_STATE_CONNECTED,
|
||||||
|
ERROR_CODE_DEFAULT, NULL);
|
||||||
|
hc->event_func(
|
||||||
|
BSC_HUBC_EVENT_CONNECTED_FAILOVER, hc, hc->user_arg, NULL, 0,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
} else if (ev == BSC_SOCKET_EVENT_DISCONNECTED) {
|
||||||
|
if (disconnect_reason == ERROR_CODE_NODE_DUPLICATE_VMAC &&
|
||||||
|
hc->state != BSC_HUB_CONNECTOR_STATE_WAIT_FOR_CTX_DEINIT) {
|
||||||
|
DEBUG_PRINTF("hub_connector_socket_event() "
|
||||||
|
"got ERROR_CODE_NODE_DUPLICATE_VMAC error\n");
|
||||||
|
if (hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTING_PRIMARY) {
|
||||||
|
hub_conector_update_status(
|
||||||
|
&hc->primary_status,
|
||||||
|
BACNET_SC_CONNECTION_STATE_FAILED_TO_CONNECT,
|
||||||
|
disconnect_reason, disconnect_reason_desc);
|
||||||
|
} else if (
|
||||||
|
hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTING_FAILOVER) {
|
||||||
|
hub_conector_update_status(
|
||||||
|
&hc->failover_status,
|
||||||
|
BACNET_SC_CONNECTION_STATE_FAILED_TO_CONNECT,
|
||||||
|
disconnect_reason, disconnect_reason_desc);
|
||||||
|
}
|
||||||
|
hc->state = BSC_HUB_CONNECTOR_STATE_DUPLICATED_VMAC;
|
||||||
|
hc->event_func(
|
||||||
|
BSC_HUBC_EVENT_ERROR_DUPLICATED_VMAC, hc, hc->user_arg, NULL, 0,
|
||||||
|
NULL);
|
||||||
|
} else if (hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTING_PRIMARY) {
|
||||||
|
DEBUG_PRINTF("hub_connector_socket_event() try to connect to "
|
||||||
|
"failover hub\n");
|
||||||
|
hub_conector_update_status(
|
||||||
|
&hc->primary_status,
|
||||||
|
BACNET_SC_CONNECTION_STATE_FAILED_TO_CONNECT, disconnect_reason,
|
||||||
|
disconnect_reason_desc);
|
||||||
|
hub_connector_connect(hc, BSC_HUB_CONN_FAILOVER);
|
||||||
|
} else if (hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTING_FAILOVER) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"hub_connector_socket_event() wait for %d seconds\n",
|
||||||
|
hc->reconnect_timeout_s);
|
||||||
|
hub_conector_update_status(
|
||||||
|
&hc->failover_status,
|
||||||
|
BACNET_SC_CONNECTION_STATE_FAILED_TO_CONNECT, disconnect_reason,
|
||||||
|
disconnect_reason_desc);
|
||||||
|
hc->state = BSC_HUB_CONNECTOR_STATE_WAIT_FOR_RECONNECT;
|
||||||
|
mstimer_set(&hc->t, hc->reconnect_timeout_s * 1000);
|
||||||
|
} else if (
|
||||||
|
hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTED_PRIMARY ||
|
||||||
|
hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTED_FAILOVER) {
|
||||||
|
if (disconnect_reason == ERROR_CODE_WEBSOCKET_CLOSED_BY_PEER ||
|
||||||
|
disconnect_reason == ERROR_CODE_SUCCESS) {
|
||||||
|
st = BACNET_SC_CONNECTION_STATE_NOT_CONNECTED;
|
||||||
|
}
|
||||||
|
if (hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTED_PRIMARY) {
|
||||||
|
hub_conector_update_status(
|
||||||
|
&hc->primary_status, st, ERROR_CODE_DEFAULT, NULL);
|
||||||
|
} else {
|
||||||
|
hub_conector_update_status(
|
||||||
|
&hc->failover_status, st, ERROR_CODE_DEFAULT, NULL);
|
||||||
|
}
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"hub_connector_socket_event() try to connect to primary hub\n");
|
||||||
|
hub_connector_connect(hc, BSC_HUB_CONN_PRIMARY);
|
||||||
|
}
|
||||||
|
} else if (ev == BSC_SOCKET_EVENT_RECEIVED) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"hub_connector_socket_event() hub_connector = %p pdu of "
|
||||||
|
"%d len is received\n",
|
||||||
|
hc, pdu_len);
|
||||||
|
hc->event_func(
|
||||||
|
BSC_HUBC_EVENT_RECEIVED, hc, hc->user_arg, pdu, pdu_len,
|
||||||
|
decoded_pdu);
|
||||||
|
}
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
DEBUG_PRINTF("hub_connector_socket_event() <<<\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Hub connector context event
|
||||||
|
* @param ctx - pointer to the context
|
||||||
|
* @param ev - event
|
||||||
|
*/
|
||||||
|
static void hub_connector_context_event(BSC_SOCKET_CTX *ctx, BSC_CTX_EVENT ev)
|
||||||
|
{
|
||||||
|
BSC_HUB_CONNECTOR *c;
|
||||||
|
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"hub_connector_context_event() >>> ctx = %p, ev = %d\n", ctx, ev);
|
||||||
|
|
||||||
|
if (ev == BSC_CTX_DEINITIALIZED) {
|
||||||
|
bws_dispatch_lock();
|
||||||
|
c = (BSC_HUB_CONNECTOR *)ctx->user_arg;
|
||||||
|
if (c->state != BSC_HUB_CONNECTOR_STATE_IDLE) {
|
||||||
|
c->state = BSC_HUB_CONNECTOR_STATE_IDLE;
|
||||||
|
hub_connector_free(c);
|
||||||
|
c->event_func(
|
||||||
|
BSC_HUBC_EVENT_STOPPED, c, c->user_arg, NULL, 0, NULL);
|
||||||
|
}
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_PRINTF("hub_connector_context_event() <<<\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start a BACnet hub connector
|
||||||
|
* @param ca_cert_chain - pointer to the CA certificate chain
|
||||||
|
* @param ca_cert_chain_size - size of the CA certificate chain
|
||||||
|
* @param cert_chain - pointer to the certificate chain
|
||||||
|
* @param cert_chain_size - size of the certificate chain
|
||||||
|
* @param key - pointer to the private key
|
||||||
|
* @param key_size - size of the private key
|
||||||
|
* @param local_uuid - pointer to the local UUID
|
||||||
|
* @param local_vmac - pointer to the local VMAC address
|
||||||
|
* @param max_local_bvlc_len - maximum BVLC message size
|
||||||
|
* @param max_local_npdu_len - maximum NPDU message size
|
||||||
|
* @param connect_timeout_s - connection timeout in seconds
|
||||||
|
* @param heartbeat_timeout_s - heartbeat timeout in seconds
|
||||||
|
* @param disconnect_timeout_s - disconnect timeout in seconds
|
||||||
|
* @param primaryURL - primary hub URL
|
||||||
|
* @param failoverURL - failover hub URL
|
||||||
|
* @param reconnect_timeout_s - reconnect timeout in seconds
|
||||||
|
* @param event_func - event function
|
||||||
|
* @param user_arg - user argument
|
||||||
|
* @param h - pointer to the hub connector handle
|
||||||
|
* @return status
|
||||||
|
*/
|
||||||
|
BSC_SC_RET bsc_hub_connector_start(
|
||||||
|
uint8_t *ca_cert_chain,
|
||||||
|
size_t ca_cert_chain_size,
|
||||||
|
uint8_t *cert_chain,
|
||||||
|
size_t cert_chain_size,
|
||||||
|
uint8_t *key,
|
||||||
|
size_t key_size,
|
||||||
|
BACNET_SC_UUID *local_uuid,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *local_vmac,
|
||||||
|
uint16_t max_local_bvlc_len,
|
||||||
|
uint16_t max_local_npdu_len,
|
||||||
|
unsigned int connect_timeout_s,
|
||||||
|
unsigned int heartbeat_timeout_s,
|
||||||
|
unsigned int disconnect_timeout_s,
|
||||||
|
char *primaryURL,
|
||||||
|
char *failoverURL,
|
||||||
|
unsigned int reconnect_timeout_s,
|
||||||
|
BSC_HUB_CONNECTOR_EVENT_FUNC event_func,
|
||||||
|
void *user_arg,
|
||||||
|
BSC_HUB_CONNECTOR_HANDLE *h)
|
||||||
|
{
|
||||||
|
BSC_SC_RET ret = BSC_SC_SUCCESS;
|
||||||
|
BSC_HUB_CONNECTOR *c;
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bsc_hub_connector_start() >>>\n");
|
||||||
|
|
||||||
|
if (!ca_cert_chain || !ca_cert_chain_size || !cert_chain ||
|
||||||
|
!cert_chain_size || !key || !key_size || !local_uuid || !local_vmac ||
|
||||||
|
!max_local_npdu_len || !max_local_bvlc_len || !connect_timeout_s ||
|
||||||
|
!heartbeat_timeout_s || !disconnect_timeout_s || !primaryURL ||
|
||||||
|
!reconnect_timeout_s || !event_func || !h) {
|
||||||
|
DEBUG_PRINTF("bsc_hub_connector_start() <<< ret = BSC_SC_BAD_PARAM\n");
|
||||||
|
return BSC_SC_BAD_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(primaryURL) > BSC_WSURL_MAX_LEN ||
|
||||||
|
(failoverURL && (strlen(failoverURL) > BSC_WSURL_MAX_LEN))) {
|
||||||
|
DEBUG_PRINTF("bsc_hub_connector_start() <<< ret = BSC_SC_BAD_PARAM\n");
|
||||||
|
return BSC_SC_BAD_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
bws_dispatch_lock();
|
||||||
|
c = hub_connector_alloc();
|
||||||
|
if (!c) {
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bsc_hub_connector_start() <<< ret = BSC_SC_NO_RESOURCES\n");
|
||||||
|
return BSC_SC_NO_RESOURCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
c->reconnect_timeout_s = reconnect_timeout_s;
|
||||||
|
c->primary_url[0] = 0;
|
||||||
|
c->failover_url[0] = 0;
|
||||||
|
c->user_arg = user_arg;
|
||||||
|
strcpy((char *)c->primary_url, primaryURL);
|
||||||
|
|
||||||
|
if (failoverURL) {
|
||||||
|
strcpy((char *)c->failover_url, failoverURL);
|
||||||
|
}
|
||||||
|
|
||||||
|
c->event_func = event_func;
|
||||||
|
|
||||||
|
bsc_init_ctx_cfg(
|
||||||
|
BSC_SOCKET_CTX_INITIATOR, &c->cfg, BSC_WEBSOCKET_HUB_PROTOCOL, 0, NULL,
|
||||||
|
ca_cert_chain, ca_cert_chain_size, cert_chain, cert_chain_size, key,
|
||||||
|
key_size, local_uuid, local_vmac, max_local_bvlc_len,
|
||||||
|
max_local_npdu_len, connect_timeout_s, heartbeat_timeout_s,
|
||||||
|
disconnect_timeout_s);
|
||||||
|
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bsc_hub_connector_start() uuid = %s, vmac = %s\n",
|
||||||
|
bsc_uuid_to_string(&c->cfg.local_uuid),
|
||||||
|
bsc_vmac_to_string(&c->cfg.local_vmac));
|
||||||
|
|
||||||
|
ret = bsc_init_ctx(
|
||||||
|
&c->ctx, &c->cfg, &bsc_hub_connector_ctx_funcs, c->sock,
|
||||||
|
sizeof(c->sock) / sizeof(BSC_SOCKET), (void *)c);
|
||||||
|
|
||||||
|
if (ret == BSC_SC_SUCCESS) {
|
||||||
|
*h = (BSC_HUB_CONNECTOR_HANDLE)c;
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bsc_hub_connector_start() hub = %p connecting to url %s\n", c,
|
||||||
|
c->primary_url);
|
||||||
|
ret = bsc_connect(
|
||||||
|
&c->ctx, &c->sock[BSC_HUB_CONN_PRIMARY], (char *)c->primary_url);
|
||||||
|
if (ret == BSC_SC_SUCCESS) {
|
||||||
|
c->state = BSC_HUB_CONNECTOR_STATE_CONNECTING_PRIMARY;
|
||||||
|
} else {
|
||||||
|
if (c->failover_url[0] == 0) {
|
||||||
|
c->state = BSC_HUB_CONNECTOR_STATE_IDLE;
|
||||||
|
bsc_deinit_ctx(&c->ctx);
|
||||||
|
hub_connector_free(c);
|
||||||
|
*h = NULL;
|
||||||
|
} else {
|
||||||
|
c->state = BSC_HUB_CONNECTOR_STATE_CONNECTING_FAILOVER;
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bsc_hub_connector_start() hub = %p connecting to url %s\n",
|
||||||
|
c, c->primary_url);
|
||||||
|
ret = bsc_connect(
|
||||||
|
&c->ctx, &c->sock[BSC_HUB_CONN_FAILOVER],
|
||||||
|
(char *)c->failover_url);
|
||||||
|
if (ret != BSC_SC_SUCCESS) {
|
||||||
|
c->state = BSC_HUB_CONNECTOR_STATE_IDLE;
|
||||||
|
bsc_deinit_ctx(&c->ctx);
|
||||||
|
hub_connector_free(c);
|
||||||
|
*h = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
DEBUG_PRINTF("bsc_hub_connector_start() <<< ret = %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Stop a BACnet hub connector
|
||||||
|
* @param h - pointer to the hub connector
|
||||||
|
*/
|
||||||
|
void bsc_hub_connector_stop(BSC_HUB_CONNECTOR_HANDLE h)
|
||||||
|
{
|
||||||
|
BSC_HUB_CONNECTOR *c = (BSC_HUB_CONNECTOR *)h;
|
||||||
|
DEBUG_PRINTF("bsc_hub_connector_stop() >>> h = %p\n", h);
|
||||||
|
|
||||||
|
bws_dispatch_lock();
|
||||||
|
|
||||||
|
#if DEBUG_ENABLED == 1
|
||||||
|
if (c) {
|
||||||
|
DEBUG_PRINTF("bsc_hub_connector_stop() state = %d\n", c->state);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (c && c->state != BSC_HUB_CONNECTOR_STATE_WAIT_FOR_CTX_DEINIT &&
|
||||||
|
c->state != BSC_HUB_CONNECTOR_STATE_IDLE) {
|
||||||
|
c->state = BSC_HUB_CONNECTOR_STATE_WAIT_FOR_CTX_DEINIT;
|
||||||
|
bsc_deinit_ctx(&c->ctx);
|
||||||
|
}
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
DEBUG_PRINTF("bsc_hub_connector_stop() <<<\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send a BACnet hub connector PDU
|
||||||
|
* @param h - pointer to the hub connector
|
||||||
|
* @param pdu - pointer to the PDU
|
||||||
|
* @param pdu_len - PDU length
|
||||||
|
* @return status
|
||||||
|
*/
|
||||||
|
BSC_SC_RET
|
||||||
|
bsc_hub_connector_send(BSC_HUB_CONNECTOR_HANDLE h, uint8_t *pdu, size_t pdu_len)
|
||||||
|
{
|
||||||
|
BSC_SC_RET ret;
|
||||||
|
BSC_HUB_CONNECTOR *c = (BSC_HUB_CONNECTOR *)h;
|
||||||
|
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bsc_hub_connector_send() >>> h = %p, pdu = %p, pdu_len = %d\n", h,
|
||||||
|
pdu, pdu_len);
|
||||||
|
|
||||||
|
bws_dispatch_lock();
|
||||||
|
if (!c) {
|
||||||
|
DEBUG_PRINTF("bsc_hub_connector_send() <<< ret = BSC_SC_BAD_PARAM\n");
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
return BSC_SC_BAD_PARAM;
|
||||||
|
}
|
||||||
|
if (c->state == BSC_HUB_CONNECTOR_STATE_IDLE ||
|
||||||
|
(c->state != BSC_HUB_CONNECTOR_STATE_CONNECTED_PRIMARY &&
|
||||||
|
c->state != BSC_HUB_CONNECTOR_STATE_CONNECTED_FAILOVER)) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bsc_hub_connector_send() pdu is dropped, state of "
|
||||||
|
"hub_connector %p is %d\n",
|
||||||
|
c, c->state);
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bsc_hub_connector_send() <<< ret = BSC_SC_INVALID_OPERATION\n");
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
return BSC_SC_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
if (c->state == BSC_HUB_CONNECTOR_STATE_CONNECTED_PRIMARY) {
|
||||||
|
ret = bsc_send(&c->sock[BSC_HUB_CONN_PRIMARY], pdu, pdu_len);
|
||||||
|
} else {
|
||||||
|
ret = bsc_send(&c->sock[BSC_HUB_CONN_FAILOVER], pdu, pdu_len);
|
||||||
|
}
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
DEBUG_PRINTF("bsc_hub_connector_send() <<< ret = %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if the hub connector is stopped
|
||||||
|
* @param h - pointer to the hub connector
|
||||||
|
* @return status
|
||||||
|
*/
|
||||||
|
bool bsc_hub_connector_stopped(BSC_HUB_CONNECTOR_HANDLE h)
|
||||||
|
{
|
||||||
|
BSC_HUB_CONNECTOR *c = (BSC_HUB_CONNECTOR *)h;
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bsc_hub_connector_stopped() >>> h = %p\n", h);
|
||||||
|
bws_dispatch_lock();
|
||||||
|
if (c && c->state == BSC_HUB_CONNECTOR_STATE_IDLE) {
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
DEBUG_PRINTF("bsc_hub_connector_stopped() <<< ret = %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the hub connector status
|
||||||
|
* @param h - pointer to the hub connector
|
||||||
|
* @param primary - primary or failover status
|
||||||
|
* @return pointer to the status
|
||||||
|
*/
|
||||||
|
BACNET_SC_HUB_CONNECTION_STATUS *
|
||||||
|
bsc_hub_connector_status(BSC_HUB_CONNECTOR_HANDLE h, bool primary)
|
||||||
|
{
|
||||||
|
BSC_HUB_CONNECTOR *c = (BSC_HUB_CONNECTOR *)h;
|
||||||
|
BACNET_SC_HUB_CONNECTION_STATUS *ret = NULL;
|
||||||
|
bws_dispatch_lock();
|
||||||
|
if (c) {
|
||||||
|
if (primary) {
|
||||||
|
ret = &c->primary_status;
|
||||||
|
} else {
|
||||||
|
ret = &c->failover_status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the hub connector state
|
||||||
|
* @param h - pointer to the hub connector
|
||||||
|
* @return state
|
||||||
|
*/
|
||||||
|
BACNET_SC_HUB_CONNECTOR_STATE
|
||||||
|
bsc_hub_connector_state(BSC_HUB_CONNECTOR_HANDLE h)
|
||||||
|
{
|
||||||
|
BSC_HUB_CONNECTOR *c = (BSC_HUB_CONNECTOR *)h;
|
||||||
|
BACNET_SC_HUB_CONNECTOR_STATE ret =
|
||||||
|
BACNET_SC_HUB_CONNECTOR_STATE_NO_HUB_CONNECTION;
|
||||||
|
bws_dispatch_lock();
|
||||||
|
if (c) {
|
||||||
|
if (c->state == BSC_HUB_CONNECTOR_STATE_CONNECTED_PRIMARY) {
|
||||||
|
ret = BACNET_SC_HUB_CONNECTOR_STATE_CONNECTED_TO_PRIMARY;
|
||||||
|
} else if (c->state == BSC_HUB_CONNECTOR_STATE_CONNECTED_FAILOVER) {
|
||||||
|
ret = BACNET_SC_HUB_CONNECTOR_STATE_CONNECTED_TO_FAILOVER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief BACNet secure connect hub connector API.
|
||||||
|
* In general, user should not use that API directly,
|
||||||
|
* BACNet/SC datalink API should be used.
|
||||||
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||||
|
* @date July 2022
|
||||||
|
* @copyright SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
#ifndef BACNET_DATALINK_BSC_HUB_CONNECTOR_H
|
||||||
|
#define BACNET_DATALINK_BSC_HUB_CONNECTOR_H
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
/* BACnet Stack defines - first */
|
||||||
|
#include "bacnet/bacdef.h"
|
||||||
|
/* BACnet Stack API */
|
||||||
|
#include "bacnet/datalink/bsc/bsc-retcodes.h"
|
||||||
|
|
||||||
|
typedef void *BSC_HUB_CONNECTOR_HANDLE;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BSC_HUBC_EVENT_CONNECTED_PRIMARY = 1,
|
||||||
|
BSC_HUBC_EVENT_CONNECTED_FAILOVER = 2,
|
||||||
|
BSC_HUBC_EVENT_ERROR_DUPLICATED_VMAC = 3,
|
||||||
|
BSC_HUBC_EVENT_RECEIVED = 4,
|
||||||
|
BSC_HUBC_EVENT_STOPPED = 5
|
||||||
|
} BSC_HUB_CONNECTOR_EVENT;
|
||||||
|
|
||||||
|
typedef void (*BSC_HUB_CONNECTOR_EVENT_FUNC)(
|
||||||
|
BSC_HUB_CONNECTOR_EVENT ev,
|
||||||
|
BSC_HUB_CONNECTOR_HANDLE h,
|
||||||
|
void *user_arg,
|
||||||
|
uint8_t *pdu,
|
||||||
|
size_t pdu_len,
|
||||||
|
BVLC_SC_DECODED_MESSAGE *decoded_pdu);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BSC_SC_RET bsc_hub_connector_start(
|
||||||
|
uint8_t *ca_cert_chain,
|
||||||
|
size_t ca_cert_chain_size,
|
||||||
|
uint8_t *cert_chain,
|
||||||
|
size_t cert_chain_size,
|
||||||
|
uint8_t *key,
|
||||||
|
size_t key_size,
|
||||||
|
BACNET_SC_UUID *local_uuid,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *local_vmac,
|
||||||
|
uint16_t max_local_bvlc_len,
|
||||||
|
uint16_t max_local_npdu_len,
|
||||||
|
unsigned int connect_timeout_s,
|
||||||
|
unsigned int heartbeat_timeout_s,
|
||||||
|
unsigned int disconnect_timeout_s,
|
||||||
|
char *primaryURL,
|
||||||
|
char *failoverURL,
|
||||||
|
unsigned int reconnnect_timeout_s,
|
||||||
|
BSC_HUB_CONNECTOR_EVENT_FUNC event_func,
|
||||||
|
void *user_arg,
|
||||||
|
BSC_HUB_CONNECTOR_HANDLE *h);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void bsc_hub_connector_stop(BSC_HUB_CONNECTOR_HANDLE h);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BSC_SC_RET bsc_hub_connector_send(
|
||||||
|
BSC_HUB_CONNECTOR_HANDLE h, uint8_t *pdu, size_t pdu_len);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool bsc_hub_connector_stopped(BSC_HUB_CONNECTOR_HANDLE h);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BACNET_SC_HUB_CONNECTOR_STATE
|
||||||
|
bsc_hub_connector_state(BSC_HUB_CONNECTOR_HANDLE h);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BACNET_SC_HUB_CONNECTION_STATUS *
|
||||||
|
bsc_hub_connector_status(BSC_HUB_CONNECTOR_HANDLE h, bool primary);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void bsc_hub_connector_maintenance_timer(uint16_t seconds);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,535 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief BACNet hub function API.
|
||||||
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||||
|
* @date July 2022
|
||||||
|
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||||
|
*/
|
||||||
|
#include "bacnet/basic/sys/debug.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-conf.h"
|
||||||
|
#include "bacnet/datalink/bsc/bvlc-sc.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-socket.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-util.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-hub-function.h"
|
||||||
|
#include "bacnet/bacdef.h"
|
||||||
|
#include "bacnet/npdu.h"
|
||||||
|
#include "bacnet/bacenum.h"
|
||||||
|
|
||||||
|
#define DEBUG_BSC_HUB_FUNCTION 0
|
||||||
|
|
||||||
|
#if DEBUG_BSC_HUB_FUNCTION == 1
|
||||||
|
#define DEBUG_PRINTF debug_printf
|
||||||
|
#else
|
||||||
|
#undef DEBUG_ENABLED
|
||||||
|
#define DEBUG_PRINTF debug_printf_disabled
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static BSC_SOCKET *hub_function_find_connection_for_vmac(
|
||||||
|
BACNET_SC_VMAC_ADDRESS *vmac, void *user_arg);
|
||||||
|
|
||||||
|
static BSC_SOCKET *
|
||||||
|
hub_function_find_connection_for_uuid(BACNET_SC_UUID *uuid, void *user_arg);
|
||||||
|
|
||||||
|
static void hub_function_failed_request(
|
||||||
|
BSC_SOCKET_CTX *ctx,
|
||||||
|
BSC_SOCKET *c,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *vmac,
|
||||||
|
BACNET_SC_UUID *uuid,
|
||||||
|
BACNET_ERROR_CODE error,
|
||||||
|
const char *error_desc);
|
||||||
|
|
||||||
|
static void hub_function_socket_event(
|
||||||
|
BSC_SOCKET *c,
|
||||||
|
BSC_SOCKET_EVENT ev,
|
||||||
|
BACNET_ERROR_CODE reason,
|
||||||
|
const char *reason_desc,
|
||||||
|
uint8_t *pdu,
|
||||||
|
size_t pdu_len,
|
||||||
|
BVLC_SC_DECODED_MESSAGE *decoded_pdu);
|
||||||
|
|
||||||
|
static void hub_function_context_event(BSC_SOCKET_CTX *ctx, BSC_CTX_EVENT ev);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BSC_HUB_FUNCTION_STATE_IDLE = 0,
|
||||||
|
BSC_HUB_FUNCTION_STATE_STARTING = 1,
|
||||||
|
BSC_HUB_FUNCTION_STATE_STARTED = 2,
|
||||||
|
BSC_HUB_FUNCTION_STATE_STOPPING = 3
|
||||||
|
} BSC_HUB_FUNCTION_STATE;
|
||||||
|
|
||||||
|
typedef struct BSC_Hub_Connector {
|
||||||
|
bool used;
|
||||||
|
BSC_SOCKET_CTX ctx;
|
||||||
|
BSC_CONTEXT_CFG cfg;
|
||||||
|
BSC_SOCKET sock[BSC_CONF_HUB_FUNCTION_CONNECTIONS_NUM];
|
||||||
|
BSC_HUB_FUNCTION_STATE state;
|
||||||
|
BSC_HUB_EVENT_FUNC event_func;
|
||||||
|
void *user_arg;
|
||||||
|
} BSC_HUB_FUNCTION;
|
||||||
|
|
||||||
|
#if BSC_CONF_HUB_FUNCTIONS_NUM > 0
|
||||||
|
static BSC_HUB_FUNCTION bsc_hub_function[BSC_CONF_HUB_FUNCTIONS_NUM] = { 0 };
|
||||||
|
#else
|
||||||
|
static BSC_HUB_FUNCTION *bsc_hub_function = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static BSC_SOCKET_CTX_FUNCS bsc_hub_function_ctx_funcs = {
|
||||||
|
hub_function_find_connection_for_vmac,
|
||||||
|
hub_function_find_connection_for_uuid, hub_function_socket_event,
|
||||||
|
hub_function_context_event, hub_function_failed_request
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Allocate a hub function
|
||||||
|
* @return pointer to the hub function
|
||||||
|
*/
|
||||||
|
static BSC_HUB_FUNCTION *hub_function_alloc(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < BSC_CONF_HUB_FUNCTIONS_NUM; i++) {
|
||||||
|
if (!bsc_hub_function[i].used) {
|
||||||
|
bsc_hub_function[i].used = true;
|
||||||
|
return &bsc_hub_function[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Free a hub function
|
||||||
|
* @param p - pointer to the hub function
|
||||||
|
*/
|
||||||
|
static void hub_function_free(BSC_HUB_FUNCTION *p)
|
||||||
|
{
|
||||||
|
p->used = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief find a hub function connection for a specific VMAC address
|
||||||
|
* @param vmac - pointer to the VMAC address
|
||||||
|
* @param user_arg - pointer to the user argument
|
||||||
|
* @return pointer to the socket, or NULL if not found
|
||||||
|
*/
|
||||||
|
static BSC_SOCKET *hub_function_find_connection_for_vmac(
|
||||||
|
BACNET_SC_VMAC_ADDRESS *vmac, void *user_arg)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
BSC_HUB_FUNCTION *f;
|
||||||
|
|
||||||
|
bws_dispatch_lock();
|
||||||
|
f = (BSC_HUB_FUNCTION *)user_arg;
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"hubf = %p local_vmac = %s\n", f,
|
||||||
|
bsc_vmac_to_string(&f->cfg.local_vmac));
|
||||||
|
for (i = 0; i < sizeof(f->sock) / sizeof(BSC_SOCKET); i++) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"hubf = %p, sock %p, state = %d, vmac = %s\n", f, &f->sock[i],
|
||||||
|
f->sock[i].state, bsc_vmac_to_string(&f->sock[i].vmac));
|
||||||
|
if (f->sock[i].state != BSC_SOCK_STATE_IDLE &&
|
||||||
|
!memcmp(
|
||||||
|
&vmac->address[0], &f->sock[i].vmac.address[0],
|
||||||
|
sizeof(vmac->address))) {
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
return &f->sock[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief find a hub function connection for a specific UUID
|
||||||
|
* @param uuid - pointer to the UUID
|
||||||
|
* @param user_arg - pointer to the user argument
|
||||||
|
* @return pointer to the socket, or NULL if not found
|
||||||
|
*/
|
||||||
|
static BSC_SOCKET *
|
||||||
|
hub_function_find_connection_for_uuid(BACNET_SC_UUID *uuid, void *user_arg)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
BSC_HUB_FUNCTION *f;
|
||||||
|
|
||||||
|
bws_dispatch_lock();
|
||||||
|
f = (BSC_HUB_FUNCTION *)user_arg;
|
||||||
|
for (i = 0; i < sizeof(f->sock) / sizeof(BSC_SOCKET); i++) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"hubf = %p, sock %p, state = %d, uuid = %s\n", f, &f->sock[i],
|
||||||
|
f->sock[i].state, bsc_uuid_to_string(&f->sock[i].uuid));
|
||||||
|
if (f->sock[i].state != BSC_SOCK_STATE_IDLE &&
|
||||||
|
!memcmp(
|
||||||
|
&uuid->uuid[0], &f->sock[i].uuid.uuid[0], sizeof(uuid->uuid))) {
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
DEBUG_PRINTF("found socket\n");
|
||||||
|
return &f->sock[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief update the status of the hub function
|
||||||
|
* @param f - pointer to the hub function
|
||||||
|
* @param c - pointer to the socket
|
||||||
|
* @param ev - event
|
||||||
|
* @param disconnect_reason - disconnect reason
|
||||||
|
* @param disconnect_reason_desc - disconnect reason description
|
||||||
|
*/
|
||||||
|
static void hub_function_update_status(
|
||||||
|
BSC_HUB_FUNCTION *f,
|
||||||
|
BSC_SOCKET *c,
|
||||||
|
BSC_SOCKET_EVENT ev,
|
||||||
|
BACNET_ERROR_CODE disconnect_reason,
|
||||||
|
const char *disconnect_reason_desc)
|
||||||
|
{
|
||||||
|
BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *s;
|
||||||
|
|
||||||
|
if (f->user_arg) {
|
||||||
|
s = bsc_node_find_hub_status_for_vmac(f->user_arg, &c->vmac);
|
||||||
|
|
||||||
|
if (s) {
|
||||||
|
memcpy(s->Peer_VMAC, &c->vmac.address[0], BVLC_SC_VMAC_SIZE);
|
||||||
|
memcpy(
|
||||||
|
&s->Peer_UUID.uuid.uuid128[0], &c->uuid.uuid[0],
|
||||||
|
BVLC_SC_UUID_SIZE);
|
||||||
|
if (!bsc_socket_get_peer_addr(c, &s->Peer_Address)) {
|
||||||
|
memset(&s->Peer_Address, 0, sizeof(s->Peer_Address));
|
||||||
|
}
|
||||||
|
if (disconnect_reason_desc) {
|
||||||
|
bsc_copy_str(
|
||||||
|
s->Error_Details, disconnect_reason_desc,
|
||||||
|
sizeof(s->Error_Details));
|
||||||
|
} else {
|
||||||
|
s->Error_Details[0] = 0;
|
||||||
|
}
|
||||||
|
s->Error = ERROR_CODE_DEFAULT;
|
||||||
|
if (ev == BSC_SOCKET_EVENT_CONNECTED) {
|
||||||
|
s->State = BACNET_SC_CONNECTION_STATE_CONNECTED;
|
||||||
|
bsc_set_timestamp(&s->Connect_Timestamp);
|
||||||
|
memset(
|
||||||
|
&s->Disconnect_Timestamp, 0xff,
|
||||||
|
sizeof(s->Disconnect_Timestamp));
|
||||||
|
} else if (ev == BSC_SOCKET_EVENT_DISCONNECTED) {
|
||||||
|
bsc_set_timestamp(&s->Disconnect_Timestamp);
|
||||||
|
if (disconnect_reason == ERROR_CODE_WEBSOCKET_CLOSED_BY_PEER ||
|
||||||
|
disconnect_reason == ERROR_CODE_SUCCESS) {
|
||||||
|
s->State = BACNET_SC_CONNECTION_STATE_NOT_CONNECTED;
|
||||||
|
} else {
|
||||||
|
s->State =
|
||||||
|
BACNET_SC_CONNECTION_STATE_DISCONNECTED_WITH_ERRORS;
|
||||||
|
s->Error = disconnect_reason;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handle a hub function failed request
|
||||||
|
* @param ctx - pointer to the socket context
|
||||||
|
* @param c - pointer to the socket
|
||||||
|
* @param vmac - pointer to the VMAC address
|
||||||
|
* @param uuid - pointer to the UUID
|
||||||
|
* @param error - error code
|
||||||
|
* @param error_desc - pointer to the error description
|
||||||
|
*/
|
||||||
|
static void hub_function_failed_request(
|
||||||
|
BSC_SOCKET_CTX *ctx,
|
||||||
|
BSC_SOCKET *c,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *vmac,
|
||||||
|
BACNET_SC_UUID *uuid,
|
||||||
|
BACNET_ERROR_CODE error,
|
||||||
|
const char *error_desc)
|
||||||
|
{
|
||||||
|
BSC_HUB_FUNCTION *f;
|
||||||
|
BACNET_HOST_N_PORT_DATA peer;
|
||||||
|
(void)ctx;
|
||||||
|
|
||||||
|
bws_dispatch_lock();
|
||||||
|
f = (BSC_HUB_FUNCTION *)c->ctx->user_arg;
|
||||||
|
if (f->user_arg) {
|
||||||
|
if (bsc_socket_get_peer_addr(c, &peer)) {
|
||||||
|
bsc_node_store_failed_request_info(
|
||||||
|
(BSC_NODE *)f->user_arg, &peer, vmac, uuid, error, error_desc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handle a hub function socket event
|
||||||
|
* @param c - pointer to the socket
|
||||||
|
* @param ev - event
|
||||||
|
* @param reason - disconnect reason
|
||||||
|
* @param reason_desc - disconnect reason description
|
||||||
|
* @param pdu - pointer to the PDU
|
||||||
|
* @param pdu_len - PDU length
|
||||||
|
* @param decoded_pdu - decoded PDU
|
||||||
|
*/
|
||||||
|
static void hub_function_socket_event(
|
||||||
|
BSC_SOCKET *c,
|
||||||
|
BSC_SOCKET_EVENT ev,
|
||||||
|
BACNET_ERROR_CODE reason,
|
||||||
|
const char *reason_desc,
|
||||||
|
uint8_t *pdu,
|
||||||
|
size_t pdu_len,
|
||||||
|
BVLC_SC_DECODED_MESSAGE *decoded_pdu)
|
||||||
|
{
|
||||||
|
BSC_SOCKET *dst;
|
||||||
|
BSC_SC_RET ret;
|
||||||
|
int i;
|
||||||
|
uint8_t *p_pdu;
|
||||||
|
BSC_HUB_FUNCTION *f;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"hub_function_socket_event() >>> c = %p, ev = %d, reason = "
|
||||||
|
"%d, desc = %p, pdu = %p, pdu_len = %d, decoded_pdu = %p\n",
|
||||||
|
c, ev, reason, reason_desc, pdu, pdu_len, decoded_pdu);
|
||||||
|
|
||||||
|
bws_dispatch_lock();
|
||||||
|
f = (BSC_HUB_FUNCTION *)c->ctx->user_arg;
|
||||||
|
if (ev == BSC_SOCKET_EVENT_RECEIVED) {
|
||||||
|
/* double check that received message does not contain */
|
||||||
|
/* originating virtual address and contains dest vaddr */
|
||||||
|
/* although such kind of check is already in bsc-socket.c */
|
||||||
|
if (!decoded_pdu->hdr.origin && decoded_pdu->hdr.dest) {
|
||||||
|
if (bvlc_sc_is_vmac_broadcast(decoded_pdu->hdr.dest)) {
|
||||||
|
if (bsc_socket_get_global_buf_size() >= pdu_len) {
|
||||||
|
p_pdu = bsc_socket_get_global_buf();
|
||||||
|
len = pdu_len;
|
||||||
|
memcpy(p_pdu, pdu, len);
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(f->sock) / sizeof(BSC_SOCKET); i++) {
|
||||||
|
if (&f->sock[i] != c &&
|
||||||
|
f->sock[i].state == BSC_SOCK_STATE_CONNECTED) {
|
||||||
|
/* change origin address if presented or add origin
|
||||||
|
*/
|
||||||
|
/* address into pdu by extending of it's header */
|
||||||
|
len = (uint16_t)bvlc_sc_set_orig(
|
||||||
|
&p_pdu, len, &c->vmac);
|
||||||
|
ret = bsc_send(&f->sock[i], p_pdu, len);
|
||||||
|
(void)ret;
|
||||||
|
#if DEBUG_ENABLED == 1
|
||||||
|
if (ret != BSC_SC_SUCCESS) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"sending of reconstructed pdu failed, "
|
||||||
|
"err = %d\n",
|
||||||
|
ret);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if DEBUG_ENABLED == 1
|
||||||
|
else {
|
||||||
|
DEBUG_PRINTF("pdu with len = %d is dropped\n", pdu_len);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
dst = hub_function_find_connection_for_vmac(
|
||||||
|
decoded_pdu->hdr.dest, (void *)f);
|
||||||
|
if (!dst) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"can not find socket, hub dropped pdu of size "
|
||||||
|
"%d for dest vmac %s\n",
|
||||||
|
pdu_len, bsc_vmac_to_string(decoded_pdu->hdr.dest));
|
||||||
|
} else {
|
||||||
|
bvlc_sc_remove_dest_set_orig(pdu, pdu_len, &c->vmac);
|
||||||
|
ret = bsc_send(dst, pdu, pdu_len);
|
||||||
|
(void)ret;
|
||||||
|
#if DEBUG_ENABLED == 1
|
||||||
|
if (ret != BSC_SC_SUCCESS) {
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"sending of pdu of %d bytes failed, err = %d\n",
|
||||||
|
pdu_len, ret);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (ev == BSC_SOCKET_EVENT_DISCONNECTED) {
|
||||||
|
hub_function_update_status(f, c, ev, reason, reason_desc);
|
||||||
|
if (reason == ERROR_CODE_NODE_DUPLICATE_VMAC) {
|
||||||
|
f->event_func(
|
||||||
|
BSC_HUBF_EVENT_ERROR_DUPLICATED_VMAC,
|
||||||
|
(BSC_HUB_FUNCTION_HANDLE)f, f->user_arg);
|
||||||
|
}
|
||||||
|
} else if (ev == BSC_SOCKET_EVENT_CONNECTED) {
|
||||||
|
hub_function_update_status(f, c, ev, reason, reason_desc);
|
||||||
|
}
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
DEBUG_PRINTF("hub_function_socket_event() <<<\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handle a hub function context event
|
||||||
|
* @param ctx - pointer to the socket context
|
||||||
|
* @param ev - event
|
||||||
|
*/
|
||||||
|
static void hub_function_context_event(BSC_SOCKET_CTX *ctx, BSC_CTX_EVENT ev)
|
||||||
|
{
|
||||||
|
BSC_HUB_FUNCTION *f;
|
||||||
|
bws_dispatch_lock();
|
||||||
|
f = (BSC_HUB_FUNCTION *)ctx->user_arg;
|
||||||
|
if (ev == BSC_CTX_INITIALIZED) {
|
||||||
|
f->state = BSC_HUB_FUNCTION_STATE_STARTED;
|
||||||
|
f->event_func(
|
||||||
|
BSC_HUBF_EVENT_STARTED, (BSC_HUB_FUNCTION_HANDLE)f, f->user_arg);
|
||||||
|
} else if (ev == BSC_CTX_DEINITIALIZED) {
|
||||||
|
f->state = BSC_HUB_FUNCTION_STATE_IDLE;
|
||||||
|
hub_function_free(f);
|
||||||
|
f->event_func(
|
||||||
|
BSC_HUBF_EVENT_STOPPED, (BSC_HUB_FUNCTION_HANDLE)f, f->user_arg);
|
||||||
|
}
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start a BACnet hub function
|
||||||
|
* @param ca_cert_chain - pointer to the CA certificate chain
|
||||||
|
* @param ca_cert_chain_size - size of the CA certificate chain
|
||||||
|
* @param cert_chain - pointer to the certificate chain
|
||||||
|
* @param cert_chain_size - size of the certificate chain
|
||||||
|
* @param key - pointer to the private key
|
||||||
|
* @param key_size - size of the private key
|
||||||
|
* @param port - port number
|
||||||
|
* @param iface - pointer to the interface
|
||||||
|
* @param local_uuid - pointer to the local UUID
|
||||||
|
* @param local_vmac - pointer to the local VMAC address
|
||||||
|
* @param max_local_bvlc_len - maximum local BVLC length
|
||||||
|
* @param max_local_npdu_len - maximum local NPDU length
|
||||||
|
* @param connect_timeout_s - connection timeout in seconds
|
||||||
|
* @param heartbeat_timeout_s - heartbeat timeout in seconds
|
||||||
|
* @param disconnect_timeout_s - disconnect timeout in seconds
|
||||||
|
* @param event_func - pointer to the event function
|
||||||
|
* @param user_arg - pointer to the user argument
|
||||||
|
* @param h - pointer to the hub function handle
|
||||||
|
* @return BACnet/SC status
|
||||||
|
*/
|
||||||
|
BSC_SC_RET bsc_hub_function_start(
|
||||||
|
uint8_t *ca_cert_chain,
|
||||||
|
size_t ca_cert_chain_size,
|
||||||
|
uint8_t *cert_chain,
|
||||||
|
size_t cert_chain_size,
|
||||||
|
uint8_t *key,
|
||||||
|
size_t key_size,
|
||||||
|
int port,
|
||||||
|
char *iface,
|
||||||
|
BACNET_SC_UUID *local_uuid,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *local_vmac,
|
||||||
|
uint16_t max_local_bvlc_len,
|
||||||
|
uint16_t max_local_npdu_len,
|
||||||
|
unsigned int connect_timeout_s,
|
||||||
|
unsigned int heartbeat_timeout_s,
|
||||||
|
unsigned int disconnect_timeout_s,
|
||||||
|
BSC_HUB_EVENT_FUNC event_func,
|
||||||
|
void *user_arg,
|
||||||
|
BSC_HUB_FUNCTION_HANDLE *h)
|
||||||
|
{
|
||||||
|
BSC_SC_RET ret;
|
||||||
|
BSC_HUB_FUNCTION *f;
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bsc_hub_function_start() >>>\n");
|
||||||
|
|
||||||
|
if (!ca_cert_chain || !ca_cert_chain_size || !cert_chain ||
|
||||||
|
!cert_chain_size || !key || !key_size || !local_uuid || !local_vmac ||
|
||||||
|
!max_local_npdu_len || !max_local_bvlc_len || !connect_timeout_s ||
|
||||||
|
!heartbeat_timeout_s || !disconnect_timeout_s || !event_func || !h) {
|
||||||
|
DEBUG_PRINTF("bsc_hub_function_start() <<< ret = BSC_SC_BAD_PARAM\n");
|
||||||
|
return BSC_SC_BAD_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
*h = NULL;
|
||||||
|
|
||||||
|
bws_dispatch_lock();
|
||||||
|
f = hub_function_alloc();
|
||||||
|
|
||||||
|
if (!f) {
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
DEBUG_PRINTF(
|
||||||
|
"bsc_hub_function_start() <<< ret = BSC_SC_NO_RESOURCES\n");
|
||||||
|
return BSC_SC_NO_RESOURCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
f->user_arg = user_arg;
|
||||||
|
f->event_func = event_func;
|
||||||
|
|
||||||
|
bsc_init_ctx_cfg(
|
||||||
|
BSC_SOCKET_CTX_ACCEPTOR, &f->cfg, BSC_WEBSOCKET_HUB_PROTOCOL, port,
|
||||||
|
iface, ca_cert_chain, ca_cert_chain_size, cert_chain, cert_chain_size,
|
||||||
|
key, key_size, local_uuid, local_vmac, max_local_bvlc_len,
|
||||||
|
max_local_npdu_len, connect_timeout_s, heartbeat_timeout_s,
|
||||||
|
disconnect_timeout_s);
|
||||||
|
|
||||||
|
ret = bsc_init_ctx(
|
||||||
|
&f->ctx, &f->cfg, &bsc_hub_function_ctx_funcs, f->sock,
|
||||||
|
sizeof(f->sock) / sizeof(BSC_SOCKET), f);
|
||||||
|
|
||||||
|
if (ret == BSC_SC_SUCCESS) {
|
||||||
|
f->state = BSC_HUB_FUNCTION_STATE_STARTING;
|
||||||
|
*h = (BSC_HUB_FUNCTION_HANDLE)f;
|
||||||
|
} else {
|
||||||
|
hub_function_free(f);
|
||||||
|
}
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
DEBUG_PRINTF("bsc_hub_function_start() << ret = %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Stop a BACnet hub function
|
||||||
|
* @param h - pointer to the hub function handle
|
||||||
|
*/
|
||||||
|
void bsc_hub_function_stop(BSC_HUB_FUNCTION_HANDLE h)
|
||||||
|
{
|
||||||
|
BSC_HUB_FUNCTION *f = (BSC_HUB_FUNCTION *)h;
|
||||||
|
DEBUG_PRINTF("bsc_hub_function_stop() >>> h = %p\n", h);
|
||||||
|
bws_dispatch_lock();
|
||||||
|
if (f && f->state != BSC_HUB_FUNCTION_STATE_IDLE &&
|
||||||
|
f->state != BSC_HUB_FUNCTION_STATE_STOPPING) {
|
||||||
|
f->state = BSC_HUB_FUNCTION_STATE_STOPPING;
|
||||||
|
bsc_deinit_ctx(&f->ctx);
|
||||||
|
}
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
DEBUG_PRINTF("bsc_hub_function_stop() <<<\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if the hub function is stopped
|
||||||
|
* @param h - pointer to the hub function handle
|
||||||
|
* @return true if the hub function is stopped, false otherwise
|
||||||
|
*/
|
||||||
|
bool bsc_hub_function_stopped(BSC_HUB_FUNCTION_HANDLE h)
|
||||||
|
{
|
||||||
|
BSC_HUB_FUNCTION *f = (BSC_HUB_FUNCTION *)h;
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bsc_hub_function_stopped() >>> h = %p\n", h);
|
||||||
|
bws_dispatch_lock();
|
||||||
|
if (f && f->state == BSC_HUB_FUNCTION_STATE_IDLE) {
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
DEBUG_PRINTF("bsc_hub_function_stopped() <<< ret = %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if the hub function is started
|
||||||
|
* @param h - pointer to the hub function handle
|
||||||
|
* @return true if the hub function is started, false otherwise
|
||||||
|
*/
|
||||||
|
bool bsc_hub_function_started(BSC_HUB_FUNCTION_HANDLE h)
|
||||||
|
{
|
||||||
|
BSC_HUB_FUNCTION *f = (BSC_HUB_FUNCTION *)h;
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
DEBUG_PRINTF("bsc_hub_function_started() >>> h = %p\n", h);
|
||||||
|
bws_dispatch_lock();
|
||||||
|
if (f && f->state == BSC_HUB_FUNCTION_STATE_STARTED) {
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
bws_dispatch_unlock();
|
||||||
|
DEBUG_PRINTF("bsc_hub_function_started() <<< ret = %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief BACNet secure connect hub function API.
|
||||||
|
* In general, user should not use that API directly,
|
||||||
|
* BACNet/SC datalink API should be used.
|
||||||
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||||
|
* @date July 2022
|
||||||
|
* @copyright SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
#ifndef BACNET_DATALINK_BSC_HUB_FUNCTION_H
|
||||||
|
#define BACNET_DATALINK_BSC_HUB_FUNCTION_H
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
/* BACnet Stack defines - first */
|
||||||
|
#include "bacnet/bacdef.h"
|
||||||
|
/* BACnet Stack API */
|
||||||
|
#include "bacnet/datalink/bsc/bsc-retcodes.h"
|
||||||
|
#include "bacnet/basic/object/sc_netport.h"
|
||||||
|
|
||||||
|
typedef void *BSC_HUB_FUNCTION_HANDLE;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BSC_HUBF_EVENT_STARTED = 1,
|
||||||
|
BSC_HUBF_EVENT_STOPPED = 2,
|
||||||
|
BSC_HUBF_EVENT_ERROR_DUPLICATED_VMAC = 3
|
||||||
|
} BSC_HUB_FUNCTION_EVENT;
|
||||||
|
|
||||||
|
typedef void (*BSC_HUB_EVENT_FUNC)(
|
||||||
|
BSC_HUB_FUNCTION_EVENT ev, BSC_HUB_FUNCTION_HANDLE h, void *user_arg);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BSC_SC_RET bsc_hub_function_start(
|
||||||
|
uint8_t *ca_cert_chain,
|
||||||
|
size_t ca_cert_chain_size,
|
||||||
|
uint8_t *cert_chain,
|
||||||
|
size_t cert_chain_size,
|
||||||
|
uint8_t *key,
|
||||||
|
size_t key_size,
|
||||||
|
int port,
|
||||||
|
char *iface,
|
||||||
|
BACNET_SC_UUID *local_uuid,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *local_vmac,
|
||||||
|
uint16_t max_local_bvlc_len,
|
||||||
|
uint16_t max_local_npdu_len,
|
||||||
|
unsigned int connect_timeout_s,
|
||||||
|
unsigned int heartbeat_timeout_s,
|
||||||
|
unsigned int disconnect_timeout_s,
|
||||||
|
BSC_HUB_EVENT_FUNC event_func,
|
||||||
|
void *user_arg,
|
||||||
|
BSC_HUB_FUNCTION_HANDLE *h);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void bsc_hub_function_stop(BSC_HUB_FUNCTION_HANDLE h);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool bsc_hub_function_stopped(BSC_HUB_FUNCTION_HANDLE h);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool bsc_hub_function_started(BSC_HUB_FUNCTION_HANDLE h);
|
||||||
|
|
||||||
|
#endif
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,126 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief BACNet secure connect node switch function API.
|
||||||
|
* In general, user should not use that API directly,
|
||||||
|
* BACNet/SC datalink API should be used.
|
||||||
|
* @author Kirill Neznamov <kirill\.neznamov@dsr-corporation\.com>
|
||||||
|
* @date October 2022
|
||||||
|
* @copyright SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
#ifndef BACNET_DATALINK_BSC_NODE_SWITCH_H
|
||||||
|
#define BACNET_DATALINK_BSC_NODE_SWITCH_H
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
/* BACnet Stack defines - first */
|
||||||
|
#include "bacnet/bacdef.h"
|
||||||
|
/* BACnet Stack API */
|
||||||
|
#include "bacnet/datalink/bsc/bsc-retcodes.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-node.h"
|
||||||
|
|
||||||
|
typedef void *BSC_NODE_SWITCH_HANDLE;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BSC_NODE_SWITCH_EVENT_STARTED = 1,
|
||||||
|
BSC_NODE_SWITCH_EVENT_STOPPED = 2,
|
||||||
|
BSC_NODE_SWITCH_EVENT_RECEIVED = 3,
|
||||||
|
BSC_NODE_SWITCH_EVENT_DUPLICATED_VMAC = 4,
|
||||||
|
|
||||||
|
/* BSC_NODE_SWITCH_EVENT_CONNECTED event is emitted every */
|
||||||
|
/* time remote peer connects only if bsc_node_switch_connect() */
|
||||||
|
/* was called after start for corresponded mac or url/urls. */
|
||||||
|
/* Events indication are stopped only */
|
||||||
|
/* if node switch was stopped or bsc_node_switch_disconnect() */
|
||||||
|
/* was called. For a connection initiated from a remote peer that */
|
||||||
|
/* event won't be ever emitted. */
|
||||||
|
|
||||||
|
BSC_NODE_SWITCH_EVENT_CONNECTED = 5,
|
||||||
|
|
||||||
|
/* The BSC_NODE_SWITCH_EVENT_DISCONNECTED event is emitted */
|
||||||
|
/* every time remote peer disconnects only if */
|
||||||
|
/* bsc_node_switch_connect() was called after start. */
|
||||||
|
/* If user called bsc_node_switch_disconnect() or stopped */
|
||||||
|
/* node switch, after last event indication next event indications */
|
||||||
|
/* are stopped for corresponded peer with corresponded vmac. */
|
||||||
|
BSC_NODE_SWITCH_EVENT_DISCONNECTED = 6
|
||||||
|
|
||||||
|
} BSC_NODE_SWITCH_EVENT;
|
||||||
|
|
||||||
|
/* dest parameter is actual only for */
|
||||||
|
/* BSC_NODE_SWITCH_EVENT_CONNECTED and BSC_NODE_SWITCH_EVENT_DISCONNECTED */
|
||||||
|
/* events. It is the parameter which was specified in */
|
||||||
|
/* bsc_node_switch_connect() or bsc_node_switch_disconnect() calls. */
|
||||||
|
|
||||||
|
typedef void (*BSC_NODE_SWITCH_EVENT_FUNC)(
|
||||||
|
BSC_NODE_SWITCH_EVENT ev,
|
||||||
|
BSC_NODE_SWITCH_HANDLE h,
|
||||||
|
void *user_arg,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *dest,
|
||||||
|
uint8_t *pdu,
|
||||||
|
size_t pdu_len,
|
||||||
|
BVLC_SC_DECODED_MESSAGE *decoded_pdu);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BSC_SC_RET bsc_node_switch_start(
|
||||||
|
uint8_t *ca_cert_chain,
|
||||||
|
size_t ca_cert_chain_size,
|
||||||
|
uint8_t *cert_chain,
|
||||||
|
size_t cert_chain_size,
|
||||||
|
uint8_t *key,
|
||||||
|
size_t key_size,
|
||||||
|
int port,
|
||||||
|
char *iface,
|
||||||
|
BACNET_SC_UUID *local_uuid,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *local_vmac,
|
||||||
|
uint16_t max_local_bvlc_len,
|
||||||
|
uint16_t max_local_npdu_len,
|
||||||
|
uint16_t connect_timeout_s,
|
||||||
|
uint16_t heartbeat_timeout_s,
|
||||||
|
uint16_t disconnect_timeout_s,
|
||||||
|
uint16_t reconnnect_timeout_s,
|
||||||
|
uint16_t address_resolution_timeout_s,
|
||||||
|
bool direct_connect_accept_enable,
|
||||||
|
bool direct_connect_initiate_enable,
|
||||||
|
BSC_NODE_SWITCH_EVENT_FUNC event_func,
|
||||||
|
void *user_arg,
|
||||||
|
BSC_NODE_SWITCH_HANDLE *h);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void bsc_node_switch_stop(BSC_NODE_SWITCH_HANDLE h);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool bsc_node_switch_stopped(BSC_NODE_SWITCH_HANDLE h);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool bsc_node_switch_started(BSC_NODE_SWITCH_HANDLE h);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BSC_SC_RET bsc_node_switch_connect(
|
||||||
|
BSC_NODE_SWITCH_HANDLE h,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *dest,
|
||||||
|
char **urls,
|
||||||
|
size_t urls_cnt);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool bsc_node_switch_connected(
|
||||||
|
BSC_NODE_SWITCH_HANDLE h,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *dest,
|
||||||
|
char **urls,
|
||||||
|
size_t urls_cnt);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void bsc_node_switch_disconnect(
|
||||||
|
BSC_NODE_SWITCH_HANDLE h, BACNET_SC_VMAC_ADDRESS *dest);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void bsc_node_switch_process_address_resolution(
|
||||||
|
BSC_NODE_SWITCH_HANDLE h, BSC_ADDRESS_RESOLUTION *r);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BSC_SC_RET
|
||||||
|
bsc_node_switch_send(BSC_NODE_SWITCH_HANDLE h, uint8_t *pdu, size_t pdu_len);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void bsc_node_switch_maintenance_timer(uint16_t seconds);
|
||||||
|
|
||||||
|
#endif
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,162 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief BACNet secure connect node API.
|
||||||
|
* In general, user should not use that API directly,
|
||||||
|
* BACNet/SC datalink API should be used.
|
||||||
|
* @author Kirill Neznamov <kirill\.neznamov@dsr-corporation\.com>
|
||||||
|
* @date October 2022
|
||||||
|
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||||
|
*/
|
||||||
|
#ifndef BACNET_DATALINK_BSC_NODE_H
|
||||||
|
#define BACNET_DATALINK_BSC_NODE_H
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
/* BACnet Stack defines - first */
|
||||||
|
#include "bacnet/bacdef.h"
|
||||||
|
/* BACnet Stack API */
|
||||||
|
#include "bacnet/datalink/bsc/bsc-conf.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-retcodes.h"
|
||||||
|
#include "bacnet/datalink/bsc/bvlc-sc.h"
|
||||||
|
#include "bacnet/basic/sys/mstimer.h"
|
||||||
|
#include "bacnet/basic/object/sc_netport.h"
|
||||||
|
|
||||||
|
typedef struct BSC_Node BSC_NODE;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BSC_NODE_EVENT_STARTED = 1,
|
||||||
|
BSC_NODE_EVENT_STOPPED = 2,
|
||||||
|
BSC_NODE_EVENT_RESTARTED = 3,
|
||||||
|
BSC_NODE_EVENT_RECEIVED_NPDU = 4,
|
||||||
|
BSC_NODE_EVENT_RECEIVED_RESULT = 5,
|
||||||
|
BSC_NODE_EVENT_RECEIVED_ADVERTISIMENT = 6,
|
||||||
|
BSC_NODE_EVENT_DIRECT_CONNECTED = 7,
|
||||||
|
BSC_NODE_EVENT_DIRECT_DISCONNECTED = 8
|
||||||
|
} BSC_NODE_EVENT;
|
||||||
|
|
||||||
|
typedef void (*BSC_NODE_EVENT_FUNC)(
|
||||||
|
BSC_NODE *node,
|
||||||
|
BSC_NODE_EVENT ev,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *dest,
|
||||||
|
uint8_t *pdu,
|
||||||
|
size_t pdu_len);
|
||||||
|
typedef struct {
|
||||||
|
bool used;
|
||||||
|
BACNET_SC_VMAC_ADDRESS vmac;
|
||||||
|
uint8_t utf8_urls[BSC_CONF_NODE_MAX_URIS_NUM_IN_ADDRESS_RESOLUTION_ACK]
|
||||||
|
[BSC_CONF_NODE_MAX_URI_SIZE_IN_ADDRESS_RESOLUTION_ACK + 1];
|
||||||
|
size_t urls_num;
|
||||||
|
struct mstimer fresh_timer;
|
||||||
|
} BSC_ADDRESS_RESOLUTION;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t *ca_cert_chain;
|
||||||
|
size_t ca_cert_chain_size;
|
||||||
|
uint8_t *cert_chain;
|
||||||
|
size_t cert_chain_size;
|
||||||
|
uint8_t *key;
|
||||||
|
size_t key_size;
|
||||||
|
BACNET_SC_UUID *local_uuid;
|
||||||
|
BACNET_SC_VMAC_ADDRESS local_vmac;
|
||||||
|
uint16_t max_local_bvlc_len;
|
||||||
|
uint16_t max_local_npdu_len;
|
||||||
|
uint16_t connect_timeout_s;
|
||||||
|
uint16_t heartbeat_timeout_s;
|
||||||
|
uint16_t disconnect_timeout_s;
|
||||||
|
uint16_t reconnnect_timeout_s;
|
||||||
|
uint16_t address_resolution_timeout_s;
|
||||||
|
uint16_t address_resolution_freshness_timeout_s;
|
||||||
|
char *primaryURL;
|
||||||
|
char *failoverURL;
|
||||||
|
uint16_t hub_server_port;
|
||||||
|
uint16_t direct_server_port;
|
||||||
|
char *hub_iface;
|
||||||
|
char *direct_iface;
|
||||||
|
bool direct_connect_accept_enable;
|
||||||
|
bool direct_connect_initiate_enable;
|
||||||
|
bool hub_function_enabled;
|
||||||
|
char *direct_connection_accept_uris; /* URIs joined ' 'space */
|
||||||
|
size_t direct_connection_accept_uris_len;
|
||||||
|
BSC_NODE_EVENT_FUNC event_func;
|
||||||
|
} BSC_NODE_CONF;
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BSC_SC_RET bsc_node_init(BSC_NODE_CONF *conf, BSC_NODE **node);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BSC_SC_RET bsc_node_deinit(BSC_NODE *node);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BSC_SC_RET bsc_node_start(BSC_NODE *node);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void bsc_node_stop(BSC_NODE *node);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BSC_SC_RET bsc_node_send(BSC_NODE *node, uint8_t *pdu, size_t pdu_len);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BSC_ADDRESS_RESOLUTION *
|
||||||
|
bsc_node_get_address_resolution(void *node, BACNET_SC_VMAC_ADDRESS *vmac);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BSC_SC_RET
|
||||||
|
bsc_node_send_address_resolution(void *node, BACNET_SC_VMAC_ADDRESS *dest);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BSC_SC_RET
|
||||||
|
bsc_node_hub_connector_send(void *user_arg, uint8_t *pdu, size_t pdu_len);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BSC_SC_RET bsc_node_connect_direct(
|
||||||
|
BSC_NODE *node, BACNET_SC_VMAC_ADDRESS *dest, char **urls, size_t urls_cnt);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void bsc_node_disconnect_direct(BSC_NODE *node, BACNET_SC_VMAC_ADDRESS *dest);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BACNET_SC_HUB_CONNECTOR_STATE
|
||||||
|
bsc_node_hub_connector_state(BSC_NODE *node);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BACNET_SC_HUB_CONNECTION_STATUS *
|
||||||
|
bsc_node_hub_connector_status(BSC_NODE *node, bool primary);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool bsc_node_direct_connection_established(
|
||||||
|
BSC_NODE *node, BACNET_SC_VMAC_ADDRESS *dest, char **urls, size_t urls_cnt);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void bsc_node_maintenance_timer(uint16_t seconds);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *
|
||||||
|
bsc_node_hub_function_status(BSC_NODE *node, size_t *cnt);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BACNET_SC_DIRECT_CONNECTION_STATUS *
|
||||||
|
bsc_node_direct_connection_status(BSC_NODE *node, size_t *cnt);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void bsc_node_store_failed_request_info(
|
||||||
|
BSC_NODE *node,
|
||||||
|
BACNET_HOST_N_PORT_DATA *peer,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *vmac,
|
||||||
|
BACNET_SC_UUID *uuid,
|
||||||
|
BACNET_ERROR_CODE error,
|
||||||
|
const char *error_desc);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BACNET_SC_FAILED_CONNECTION_REQUEST *
|
||||||
|
bsc_node_failed_requests_status(BSC_NODE *node, size_t *cnt);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BACNET_SC_DIRECT_CONNECTION_STATUS *bsc_node_find_direct_status_for_vmac(
|
||||||
|
BSC_NODE *node, BACNET_SC_VMAC_ADDRESS *vmac);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *
|
||||||
|
bsc_node_find_hub_status_for_vmac(BSC_NODE *node, BACNET_SC_VMAC_ADDRESS *vmac);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief BACNet secure connect main include header.
|
||||||
|
* @author Kirill Neznamov <kirill\.neznamov@dsr-corporation\.com>
|
||||||
|
* @date August 2022
|
||||||
|
* @copyright SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
#ifndef BACNET_DATALINK_BSC_RETCODES_H
|
||||||
|
#define BACNET_DATALINK_BSC_RETCODES_H
|
||||||
|
/* BACnet Stack defines - first */
|
||||||
|
#include "bacnet/bacdef.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BSC_SC_SUCCESS = 0,
|
||||||
|
BSC_SC_NO_RESOURCES = 1,
|
||||||
|
BSC_SC_BAD_PARAM = 2,
|
||||||
|
BSC_SC_INVALID_OPERATION = 3
|
||||||
|
} BSC_SC_RET;
|
||||||
|
|
||||||
|
#endif
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,308 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief BACNet secure connect socket API.
|
||||||
|
* In general, user should not use that API directly,
|
||||||
|
* BACNet/SC datalink API should be used.
|
||||||
|
* @author Kirill Neznamov <kirill\.neznamov@dsr-corporation\.com>
|
||||||
|
* @date December 2022
|
||||||
|
* @copyright SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
#ifndef BACNET_DATALINK_BSC_SOCKET_H
|
||||||
|
#define BACNET_DATALINK_BSC_SOCKET_H
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
/* BACnet Stack defines - first */
|
||||||
|
#include "bacnet/bacdef.h"
|
||||||
|
/* BACnet Stack API */
|
||||||
|
#include "bacnet/npdu.h"
|
||||||
|
#include "bacnet/datalink/bsc/websocket.h"
|
||||||
|
#include "bacnet/datalink/bsc/bvlc-sc.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-retcodes.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-conf.h"
|
||||||
|
#include "bacnet/basic/sys/mstimer.h"
|
||||||
|
#include "bacnet/basic/object/sc_netport.h"
|
||||||
|
|
||||||
|
#define BSC_RX_BUFFER_SIZE BSC_CONF_SOCK_RX_BUFFER_SIZE
|
||||||
|
#define BSC_TX_BUFFER_SIZE BSC_CONF_SOCK_TX_BUFFER_SIZE
|
||||||
|
|
||||||
|
#define BSC_SOCKET_CTX_NUM \
|
||||||
|
(BSC_CONF_NODES_NUM * \
|
||||||
|
(BSC_CONF_HUB_CONNECTORS_NUM + 2 * BSC_CONF_NODE_SWITCHES_NUM + \
|
||||||
|
BSC_CONF_HUB_FUNCTIONS_NUM))
|
||||||
|
|
||||||
|
struct BSC_Socket;
|
||||||
|
typedef struct BSC_Socket BSC_SOCKET;
|
||||||
|
|
||||||
|
struct BSC_SocketContext;
|
||||||
|
typedef struct BSC_SocketContext BSC_SOCKET_CTX;
|
||||||
|
|
||||||
|
struct BSC_SocketContextFuncs;
|
||||||
|
typedef struct BSC_SocketContextFuncs BSC_SOCKET_CTX_FUNCS;
|
||||||
|
|
||||||
|
struct BSC_ContextCFG;
|
||||||
|
typedef struct BSC_ContextCFG BSC_CONTEXT_CFG;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BSC_SOCKET_CTX_INITIATOR = 1,
|
||||||
|
BSC_SOCKET_CTX_ACCEPTOR = 2
|
||||||
|
} BSC_SOCKET_CTX_TYPE;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BSC_SOCKET_EVENT_CONNECTED = 0,
|
||||||
|
BSC_SOCKET_EVENT_DISCONNECTED = 1,
|
||||||
|
BSC_SOCKET_EVENT_RECEIVED = 2
|
||||||
|
} BSC_SOCKET_EVENT;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BSC_CTX_INITIALIZED = 0,
|
||||||
|
BSC_CTX_DEINITIALIZED = 1
|
||||||
|
} BSC_CTX_EVENT;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BSC_CTX_STATE_IDLE = 0,
|
||||||
|
BSC_CTX_STATE_INITIALIZING = 1,
|
||||||
|
BSC_CTX_STATE_INITIALIZED = 2,
|
||||||
|
BSC_CTX_STATE_DEINITIALIZING = 3
|
||||||
|
} BSC_CTX_STATE;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BSC_SOCK_STATE_IDLE = 0,
|
||||||
|
BSC_SOCK_STATE_AWAITING_WEBSOCKET = 1,
|
||||||
|
BSC_SOCK_STATE_AWAITING_REQUEST = 2,
|
||||||
|
BSC_SOCK_STATE_AWAITING_ACCEPT = 3,
|
||||||
|
BSC_SOCK_STATE_CONNECTED = 4,
|
||||||
|
BSC_SOCK_STATE_DISCONNECTING = 5,
|
||||||
|
BSC_SOCK_STATE_ERROR = 6,
|
||||||
|
BSC_SOCK_STATE_ERROR_FLUSH_TX = 7
|
||||||
|
} BSC_SOCKET_STATE;
|
||||||
|
|
||||||
|
struct BSC_Socket {
|
||||||
|
BSC_SOCKET_CTX *ctx;
|
||||||
|
BSC_WEBSOCKET_HANDLE wh;
|
||||||
|
BSC_SOCKET_STATE state;
|
||||||
|
BACNET_ERROR_CODE reason;
|
||||||
|
struct mstimer t;
|
||||||
|
struct mstimer heartbeat;
|
||||||
|
BACNET_SC_VMAC_ADDRESS vmac; /* VMAC address of the requesting node. */
|
||||||
|
BACNET_SC_UUID uuid;
|
||||||
|
|
||||||
|
/* Regarding max_bvlc_len and max_npdu_len: */
|
||||||
|
/* These are the datalink limits and are passed up the stack to let */
|
||||||
|
/* the application layer know one of the several numbers that go into
|
||||||
|
* computing */
|
||||||
|
/* how big an NPDU/APDU can be. */
|
||||||
|
|
||||||
|
uint16_t max_bvlc_len; /* remote peer max bvlc len */
|
||||||
|
uint16_t max_npdu_len; /* remote peer max npdu len */
|
||||||
|
|
||||||
|
uint16_t expected_connect_accept_message_id;
|
||||||
|
uint16_t expected_disconnect_message_id;
|
||||||
|
uint16_t expected_heartbeat_message_id;
|
||||||
|
|
||||||
|
uint8_t tx_buf[BSC_TX_BUFFER_SIZE];
|
||||||
|
size_t tx_buf_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BSC_ContextCFG {
|
||||||
|
BSC_SOCKET_CTX_TYPE type;
|
||||||
|
BSC_WEBSOCKET_PROTOCOL proto;
|
||||||
|
uint16_t port;
|
||||||
|
char *iface;
|
||||||
|
uint8_t *ca_cert_chain;
|
||||||
|
size_t ca_cert_chain_size;
|
||||||
|
uint8_t *cert_chain;
|
||||||
|
size_t cert_chain_size;
|
||||||
|
uint8_t *priv_key;
|
||||||
|
size_t priv_key_size;
|
||||||
|
BACNET_SC_VMAC_ADDRESS local_vmac;
|
||||||
|
BACNET_SC_UUID local_uuid;
|
||||||
|
uint16_t max_bvlc_len; /* local peer max bvlc len */
|
||||||
|
uint16_t max_ndpu_len; /* local peer max npdu len */
|
||||||
|
|
||||||
|
/* According AB.6.2 BACnet/SC Connection Establishment and Termination */
|
||||||
|
/* recommended default value for establishing of connection 10 seconds */
|
||||||
|
uint16_t connect_timeout_s;
|
||||||
|
uint16_t disconnect_timeout_s;
|
||||||
|
|
||||||
|
/* According 12.56.Y10 SC_Heartbeat_Timeout */
|
||||||
|
/* (http://www.bacnet.org/Addenda/Add-135-2020cc.pdf) the recommended
|
||||||
|
* default */
|
||||||
|
/* value is 300 seconds. */
|
||||||
|
uint16_t heartbeat_timeout_s;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BSC_SocketContextFuncs {
|
||||||
|
BSC_SOCKET *(*find_connection_for_vmac)(
|
||||||
|
BACNET_SC_VMAC_ADDRESS *vmac, void *user_arg);
|
||||||
|
BSC_SOCKET *(*find_connection_for_uuid)(
|
||||||
|
BACNET_SC_UUID *uuid, void *user_arg);
|
||||||
|
/* We always reserve BSC_PRE bytes before BVLC message header */
|
||||||
|
/* to avoid copying of packet payload during manipulation with */
|
||||||
|
/* origin and dest addresses (e.g. adding them to received PDU) */
|
||||||
|
/* That's why pdu pointer has always reserved BSC_PRE bytes behind */
|
||||||
|
/* The params disconnect_reason and disconnect_reason_desc are meanfull */
|
||||||
|
/* only for disconnect events, e.g. when ev == BSC_SOCKET_EVENT_DISCONNECTED
|
||||||
|
*/
|
||||||
|
|
||||||
|
void (*socket_event)(
|
||||||
|
BSC_SOCKET *c,
|
||||||
|
BSC_SOCKET_EVENT ev,
|
||||||
|
BACNET_ERROR_CODE disconnect_reason,
|
||||||
|
const char *disconnect_reason_desc,
|
||||||
|
uint8_t *pdu,
|
||||||
|
size_t pdu_len,
|
||||||
|
BVLC_SC_DECODED_MESSAGE *decoded_pdu);
|
||||||
|
|
||||||
|
void (*context_event)(BSC_SOCKET_CTX *ctx, BSC_CTX_EVENT ev);
|
||||||
|
void (*failed_request)(
|
||||||
|
BSC_SOCKET_CTX *ctx,
|
||||||
|
BSC_SOCKET *c,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *vmac,
|
||||||
|
BACNET_SC_UUID *uuid,
|
||||||
|
BACNET_ERROR_CODE error,
|
||||||
|
const char *error_desc);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BSC_SocketContext {
|
||||||
|
BSC_CTX_STATE state;
|
||||||
|
BSC_WEBSOCKET_SRV_HANDLE sh;
|
||||||
|
BSC_SOCKET *sock;
|
||||||
|
size_t sock_num;
|
||||||
|
BSC_SOCKET_CTX_FUNCS *funcs;
|
||||||
|
BSC_CONTEXT_CFG *cfg;
|
||||||
|
bool deinit_in_progress;
|
||||||
|
void *user_arg;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* max_local_bvlc_len - The maximum BVLC message size int bytes that can be */
|
||||||
|
/* received and processed by BSC/SC datalink. */
|
||||||
|
/* max_local_ndpu_len - The maximum NPDU message size in bytes hat can be */
|
||||||
|
/* handled by BSC/SC datalink. */
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void bsc_init_ctx_cfg(
|
||||||
|
BSC_SOCKET_CTX_TYPE type,
|
||||||
|
BSC_CONTEXT_CFG *cfg,
|
||||||
|
BSC_WEBSOCKET_PROTOCOL proto,
|
||||||
|
uint16_t port,
|
||||||
|
char *iface,
|
||||||
|
uint8_t *ca_cert_chain,
|
||||||
|
size_t ca_cert_chain_size,
|
||||||
|
uint8_t *cert_chain,
|
||||||
|
size_t cert_chain_size,
|
||||||
|
uint8_t *key,
|
||||||
|
size_t key_size,
|
||||||
|
BACNET_SC_UUID *local_uuid,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *local_vmac,
|
||||||
|
uint16_t max_local_bvlc_len,
|
||||||
|
uint16_t max_local_ndpu_len,
|
||||||
|
unsigned int connect_timeout_s,
|
||||||
|
unsigned int heartbeat_timeout_s,
|
||||||
|
unsigned int disconnect_timeout_s);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BSC_SC_RET bsc_init_ctx(
|
||||||
|
BSC_SOCKET_CTX *ctx,
|
||||||
|
BSC_CONTEXT_CFG *cfg,
|
||||||
|
BSC_SOCKET_CTX_FUNCS *funcs,
|
||||||
|
BSC_SOCKET *sockets,
|
||||||
|
size_t sockets_num,
|
||||||
|
void *user_arg);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void bsc_deinit_ctx(BSC_SOCKET_CTX *ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief bsc_connect() function starts connect operation for a
|
||||||
|
* specified BACNet socket. The function call be called only
|
||||||
|
* for initiator context otherwise BSC_SC_INVALID_OPERATION
|
||||||
|
* error is returned. As a result if bsc_connect() was
|
||||||
|
* succeeded for given param c, that leads to emitting of
|
||||||
|
* BSC_SOCKET_EVENT_CONNECTED or BSC_SOCKET_EVENT_DISCONNECTED
|
||||||
|
* events depending on the result of connect operation.
|
||||||
|
* If connect operation is failed, BSC_SOCKET_EVENT_DISCONNECTED
|
||||||
|
* event is emitted and user can determine the reason why it
|
||||||
|
* happened using disconnect_reason and disconnect_reason_desc
|
||||||
|
* parameters in callback function.
|
||||||
|
* If connect operation succeeded, BSC_SOCKET_EVENT_CONNECTED
|
||||||
|
* event is emitted.
|
||||||
|
*
|
||||||
|
* @param ctx - socket context.
|
||||||
|
* @param c - BACNet socket descriptor .
|
||||||
|
* @param url - url to connect to. For example: wss://legrand.com:8080.
|
||||||
|
*
|
||||||
|
* @return error code from BSC_SC_RET enum.
|
||||||
|
* The following error codes can be returned:
|
||||||
|
* BSC_SC_BAD_PARAM - In a case if some input parameter is
|
||||||
|
* incorrect.
|
||||||
|
* BSC_SC_INVALID_OPERATION - if socket is not in opened state,
|
||||||
|
or disconnect operation is in progress using
|
||||||
|
bsc_disconnect() or bsc_deinit_ctx().
|
||||||
|
* BSC_SC_SUCCESS - operation has succeeded.
|
||||||
|
* BSC_SC_NO_RESOURCES - there are not resources (memory, etc.. )
|
||||||
|
* to send data
|
||||||
|
*/
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BSC_SC_RET bsc_connect(BSC_SOCKET_CTX *ctx, BSC_SOCKET *c, char *url);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void bsc_disconnect(BSC_SOCKET *c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief bsc_send() function schedules transmitting of pdu to
|
||||||
|
* another BACNet socket. The function may be used only
|
||||||
|
* when the socket is in a connected state
|
||||||
|
* otherwise BSC_SC_INVALID_OPERATION error is returned.
|
||||||
|
*
|
||||||
|
* @param c - BACNet socket descriptor initialized by bsc_accept() or
|
||||||
|
* bsc_connect() calls.
|
||||||
|
* @param pdu - pointer to a data to send.
|
||||||
|
* @param pdu_len - size in bytes of data to send.
|
||||||
|
*
|
||||||
|
* @return error code from BSC_SC_RET enum.
|
||||||
|
* The following error codes can be returned:
|
||||||
|
* BSC_SC_BAD_PARAM - In a case if some input parameter is
|
||||||
|
* incorrect.
|
||||||
|
* BSC_SC_INVALID_OPERATION - if socket is not in opened state,
|
||||||
|
or disconnect operation is in progress using
|
||||||
|
bsc_disconnect() or bsc_deinit_ctx().
|
||||||
|
* BSC_SC_SUCCESS - operation has succeeded.
|
||||||
|
* BSC_SC_NO_RESOURCES - there are not resources (memory, etc.. )
|
||||||
|
* to send data
|
||||||
|
*/
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
BSC_SC_RET bsc_send(BSC_SOCKET *c, uint8_t *pdu, size_t pdu_len);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
uint16_t bsc_get_next_message_id(void);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void bsc_socket_maintenance_timer(uint16_t seconds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief bsc_socket_get_peer_addr() function gets information
|
||||||
|
* about remote peer address only for socket with acceptor cotext.
|
||||||
|
*
|
||||||
|
* @param c - BACNet socket descriptor initialized by bsc_accept() call.
|
||||||
|
* @param data - pointer to a struct holding address information.
|
||||||
|
* @param pdu_len - size in bytes of data to send.
|
||||||
|
*
|
||||||
|
* @return false if socket is not acceptor or bad parameter was passed or
|
||||||
|
* if getting of address information failed.
|
||||||
|
* true if function succeeds.
|
||||||
|
*/
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool bsc_socket_get_peer_addr(BSC_SOCKET *c, BACNET_HOST_N_PORT_DATA *data);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
uint8_t *bsc_socket_get_global_buf(void);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
size_t bsc_socket_get_global_buf_size(void);
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,343 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief module for common function for BACnet/SC implementation
|
||||||
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||||
|
* @date Jule 2022
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||||
|
*/
|
||||||
|
#include "bacnet/datalink/bsc/bsc-util.h"
|
||||||
|
#include "bacnet/basic/object/bacfile.h"
|
||||||
|
#include "bacnet/basic/object/netport.h"
|
||||||
|
#include "bacnet/basic/object/sc_netport.h"
|
||||||
|
#include "bacnet/basic/object/bacfile.h"
|
||||||
|
#include "bacnet/basic/sys/debug.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define PRINTF debug_aprintf
|
||||||
|
#define PRINTF_ERR debug_perror
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Map websocket return code to BACnet/SC return code
|
||||||
|
* @param ret - websocket return code
|
||||||
|
* @return BACnet/SC return code
|
||||||
|
*/
|
||||||
|
BSC_SC_RET bsc_map_websocket_retcode(BSC_WEBSOCKET_RET ret)
|
||||||
|
{
|
||||||
|
switch (ret) {
|
||||||
|
case BSC_WEBSOCKET_SUCCESS:
|
||||||
|
return BSC_SC_SUCCESS;
|
||||||
|
case BSC_WEBSOCKET_NO_RESOURCES:
|
||||||
|
return BSC_SC_NO_RESOURCES;
|
||||||
|
case BSC_WEBSOCKET_BAD_PARAM:
|
||||||
|
return BSC_SC_BAD_PARAM;
|
||||||
|
case BSC_WEBSOCKET_INVALID_OPERATION:
|
||||||
|
default:
|
||||||
|
return BSC_SC_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copy BACnet Secure Connect VMAC address
|
||||||
|
* @param dst - destination VMAC address
|
||||||
|
* @param src - source VMAC address
|
||||||
|
*/
|
||||||
|
void bsc_copy_vmac(BACNET_SC_VMAC_ADDRESS *dst, BACNET_SC_VMAC_ADDRESS *src)
|
||||||
|
{
|
||||||
|
memcpy(dst->address, src->address, sizeof(src->address));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copy the BACnet Secure Connect UUID
|
||||||
|
* @param dst - destination UUID
|
||||||
|
* @param src - source UUID
|
||||||
|
*/
|
||||||
|
void bsc_copy_uuid(BACNET_SC_UUID *dst, BACNET_SC_UUID *src)
|
||||||
|
{
|
||||||
|
memcpy(dst->uuid, src->uuid, sizeof(src->uuid));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert BACnet Secure Connect VMAC address to string
|
||||||
|
* @param vmac - VMAC address
|
||||||
|
* @return string representation of VMAC address
|
||||||
|
*/
|
||||||
|
char *bsc_vmac_to_string(BACNET_SC_VMAC_ADDRESS *vmac)
|
||||||
|
{
|
||||||
|
static char buf[128];
|
||||||
|
sprintf(
|
||||||
|
buf, "%02x%02x%02x%02x%02x%02x", vmac->address[0], vmac->address[1],
|
||||||
|
vmac->address[2], vmac->address[3], vmac->address[4], vmac->address[5]);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert BACnet Secure Connect UUID to string
|
||||||
|
* @param uuid - UUID
|
||||||
|
* @return string representation of UUID
|
||||||
|
*/
|
||||||
|
char *bsc_uuid_to_string(BACNET_SC_UUID *uuid)
|
||||||
|
{
|
||||||
|
static char buf[128];
|
||||||
|
sprintf(
|
||||||
|
buf,
|
||||||
|
"%02x%02x%02x%02x-%02x%02x%02x%02x-%02x%02x%02x%02x-%02x%02x%02x%02x",
|
||||||
|
uuid->uuid[0], uuid->uuid[1], uuid->uuid[2], uuid->uuid[3],
|
||||||
|
uuid->uuid[4], uuid->uuid[5], uuid->uuid[6], uuid->uuid[7],
|
||||||
|
uuid->uuid[8], uuid->uuid[9], uuid->uuid[10], uuid->uuid[11],
|
||||||
|
uuid->uuid[12], uuid->uuid[13], uuid->uuid[14], uuid->uuid[15]);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Generate random BACnet Secure Connect VMAC address
|
||||||
|
* @param p - pointer to the VMAC address
|
||||||
|
*/
|
||||||
|
void bsc_generate_random_vmac(BACNET_SC_VMAC_ADDRESS *p)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < BVLC_SC_VMAC_SIZE; i++) {
|
||||||
|
p->address[i] = rand() % 255;
|
||||||
|
if (i == 0) {
|
||||||
|
/* According H.7.3 EUI-48 and Random-48 VMAC Address:
|
||||||
|
The Random-48 VMAC is a 6-octet VMAC address in which the least
|
||||||
|
significant 4 bits (Bit 3 to Bit 0) in the first octet shall be
|
||||||
|
B'0010' (X'2'), and all other 44 bits are randomly selected to be
|
||||||
|
0 or 1. */
|
||||||
|
p->address[i] = (p->address[i] & 0xF0) | 0x02;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug_printf_hex(
|
||||||
|
0, p->address, BVLC_SC_VMAC_SIZE, "bsc_generate_random_vmac");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Generate random BACnet Secure Connect UUID
|
||||||
|
* @param p - pointer to the UUID
|
||||||
|
*/
|
||||||
|
void bsc_generate_random_uuid(BACNET_SC_UUID *p)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < BVLC_SC_UUID_SIZE; i++) {
|
||||||
|
p->uuid[i] = rand() % 255;
|
||||||
|
}
|
||||||
|
debug_printf_hex(0, p->uuid, BVLC_SC_UUID_SIZE, "bsc_generate_random_uuid");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* bsc_node_load_cert_bacfile loads one credentional file from bacfile object
|
||||||
|
* Note: the function adds null-terminated byte to loaded file
|
||||||
|
* (certificate, certificate key).
|
||||||
|
* The MbedTLS PEM parser requires data to be null-terminated.
|
||||||
|
*/
|
||||||
|
#ifdef CONFIG_MBEDTLS
|
||||||
|
#define ZERO_BYTE 1
|
||||||
|
#else
|
||||||
|
#define ZERO_BYTE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load certificate from BACnet file object
|
||||||
|
* @param file_instance - file instance
|
||||||
|
* @param pbuf - pointer to the buffer
|
||||||
|
* @param psize - pointer to the size
|
||||||
|
* @return true if successful, else false
|
||||||
|
*/
|
||||||
|
static bool bsc_node_load_cert_bacfile(
|
||||||
|
uint32_t file_instance, uint8_t **pbuf, size_t *psize)
|
||||||
|
{
|
||||||
|
uint32_t file_length;
|
||||||
|
uint8_t *buf;
|
||||||
|
|
||||||
|
*psize = bacfile_file_size(file_instance) + ZERO_BYTE;
|
||||||
|
if (*psize == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = calloc(1, *psize);
|
||||||
|
if (buf == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_length =
|
||||||
|
bacfile_read(file_instance, buf, (uint32_t)(*psize - ZERO_BYTE));
|
||||||
|
#ifdef CONFIG_MBEDTLS
|
||||||
|
buf[*psize - 1] = 0;
|
||||||
|
#endif
|
||||||
|
if (file_length == 0) {
|
||||||
|
PRINTF_ERR("Can't read %s file\n", bacfile_pathname(file_instance));
|
||||||
|
free(buf);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*pbuf = buf;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fill BACnet/SC node configuration from network port object
|
||||||
|
* @param bsc_conf - pointer to the BACnet/SC node configuration
|
||||||
|
* @param event_func - event function
|
||||||
|
* @return true if successful, else false
|
||||||
|
*/
|
||||||
|
bool bsc_node_conf_fill_from_netport(
|
||||||
|
BSC_NODE_CONF *bsc_conf, BSC_NODE_EVENT_FUNC event_func)
|
||||||
|
{
|
||||||
|
uint32_t instance;
|
||||||
|
uint32_t file_instance;
|
||||||
|
|
||||||
|
instance = Network_Port_Index_To_Instance(0);
|
||||||
|
bsc_conf->ca_cert_chain = NULL;
|
||||||
|
bsc_conf->cert_chain = NULL;
|
||||||
|
bsc_conf->key = NULL;
|
||||||
|
|
||||||
|
file_instance = Network_Port_Issuer_Certificate_File(instance, 0);
|
||||||
|
if (!bsc_node_load_cert_bacfile(
|
||||||
|
file_instance, &bsc_conf->ca_cert_chain,
|
||||||
|
&bsc_conf->ca_cert_chain_size)) {
|
||||||
|
bsc_node_conf_cleanup(bsc_conf);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_instance = Network_Port_Operational_Certificate_File(instance);
|
||||||
|
if (!bsc_node_load_cert_bacfile(
|
||||||
|
file_instance, &bsc_conf->cert_chain, &bsc_conf->cert_chain_size)) {
|
||||||
|
bsc_node_conf_cleanup(bsc_conf);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_instance = Network_Port_Certificate_Key_File(instance);
|
||||||
|
bsc_conf->key_size = bacfile_file_size(file_instance) + 1;
|
||||||
|
if (!bsc_node_load_cert_bacfile(
|
||||||
|
file_instance, &bsc_conf->key, &bsc_conf->key_size)) {
|
||||||
|
bsc_node_conf_cleanup(bsc_conf);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bsc_conf->local_uuid =
|
||||||
|
(BACNET_SC_UUID *)Network_Port_SC_Local_UUID(instance);
|
||||||
|
Network_Port_MAC_Address_Value(
|
||||||
|
instance, bsc_conf->local_vmac.address,
|
||||||
|
sizeof(bsc_conf->local_vmac.address));
|
||||||
|
bsc_conf->max_local_bvlc_len =
|
||||||
|
(uint16_t)Network_Port_Max_BVLC_Length_Accepted(instance);
|
||||||
|
bsc_conf->max_local_npdu_len =
|
||||||
|
(uint16_t)Network_Port_Max_NPDU_Length_Accepted(instance);
|
||||||
|
bsc_conf->connect_timeout_s =
|
||||||
|
(uint16_t)Network_Port_SC_Connect_Wait_Timeout(instance);
|
||||||
|
bsc_conf->heartbeat_timeout_s =
|
||||||
|
(uint16_t)Network_Port_SC_Heartbeat_Timeout(instance);
|
||||||
|
bsc_conf->disconnect_timeout_s =
|
||||||
|
(uint16_t)Network_Port_SC_Disconnect_Wait_Timeout(instance);
|
||||||
|
bsc_conf->reconnnect_timeout_s =
|
||||||
|
(uint16_t)Network_Port_SC_Maximum_Reconnect_Time(instance);
|
||||||
|
bsc_conf->address_resolution_timeout_s = bsc_conf->connect_timeout_s;
|
||||||
|
bsc_conf->address_resolution_freshness_timeout_s =
|
||||||
|
bsc_conf->connect_timeout_s;
|
||||||
|
bsc_conf->primaryURL =
|
||||||
|
(char *)Network_Port_SC_Primary_Hub_URI_char(instance);
|
||||||
|
bsc_conf->failoverURL =
|
||||||
|
(char *)Network_Port_SC_Failover_Hub_URI_char(instance);
|
||||||
|
#if BSC_CONF_HUB_CONNECTORS_NUM != 0
|
||||||
|
bsc_conf->direct_connect_initiate_enable =
|
||||||
|
Network_Port_SC_Direct_Connect_Initiate_Enable(instance);
|
||||||
|
bsc_conf->direct_connect_accept_enable =
|
||||||
|
Network_Port_SC_Direct_Connect_Accept_Enable(instance);
|
||||||
|
Network_Port_SC_Direct_Connect_Binding_get(
|
||||||
|
instance, &bsc_conf->direct_server_port, &bsc_conf->direct_iface);
|
||||||
|
#endif
|
||||||
|
#if BSC_CONF_HUB_FUNCTIONS_NUM != 0
|
||||||
|
Network_Port_SC_Hub_Function_Binding_get(
|
||||||
|
instance, &bsc_conf->hub_server_port, &bsc_conf->hub_iface);
|
||||||
|
bsc_conf->hub_function_enabled =
|
||||||
|
Network_Port_SC_Hub_Function_Enable(instance);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if BSC_CONF_HUB_CONNECTORS_NUM != 0
|
||||||
|
bsc_conf->direct_connection_accept_uris =
|
||||||
|
Network_Port_SC_Direct_Connect_Accept_URIs_char(instance);
|
||||||
|
bsc_conf->direct_connection_accept_uris_len =
|
||||||
|
strlen(bsc_conf->direct_connection_accept_uris);
|
||||||
|
#endif
|
||||||
|
bsc_conf->event_func = event_func;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Cleanup BACnet/SC node configuration
|
||||||
|
* @param bsc_conf - pointer to the BACnet/SC node configuration
|
||||||
|
*/
|
||||||
|
void bsc_node_conf_cleanup(BSC_NODE_CONF *bsc_conf)
|
||||||
|
{
|
||||||
|
bsc_conf->ca_cert_chain_size = 0;
|
||||||
|
if (bsc_conf->ca_cert_chain) {
|
||||||
|
free(bsc_conf->ca_cert_chain);
|
||||||
|
}
|
||||||
|
bsc_conf->cert_chain_size = 0;
|
||||||
|
if (bsc_conf->cert_chain) {
|
||||||
|
free(bsc_conf->cert_chain);
|
||||||
|
}
|
||||||
|
bsc_conf->key_size = 0;
|
||||||
|
if (bsc_conf->key) {
|
||||||
|
free(bsc_conf->key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copy string
|
||||||
|
* @param dst - destination string
|
||||||
|
* @param src - source string
|
||||||
|
* @param dst_len - destination string length
|
||||||
|
*/
|
||||||
|
void bsc_copy_str(char *dst, const char *src, size_t dst_len)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
if (dst_len > 0) {
|
||||||
|
len = strlen(src) >= dst_len ? dst_len - 1 : strlen(src);
|
||||||
|
memcpy(dst, src, len);
|
||||||
|
dst[len] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set timestamp
|
||||||
|
* @param timestamp - pointer to the timestamp
|
||||||
|
*/
|
||||||
|
void bsc_set_timestamp(BACNET_DATE_TIME *timestamp)
|
||||||
|
{
|
||||||
|
int16_t utc_offset_minutes;
|
||||||
|
bool dst_active;
|
||||||
|
datetime_local(
|
||||||
|
×tamp->date, ×tamp->time, &utc_offset_minutes, &dst_active);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if BACnet/SC certificate files exist
|
||||||
|
* @return true if all files exist, else false
|
||||||
|
*/
|
||||||
|
bool bsc_cert_files_check(void)
|
||||||
|
{
|
||||||
|
uint32_t instance;
|
||||||
|
uint32_t file_instance;
|
||||||
|
|
||||||
|
instance = Network_Port_Index_To_Instance(0);
|
||||||
|
|
||||||
|
file_instance = Network_Port_Issuer_Certificate_File(instance, 0);
|
||||||
|
if (bacfile_file_size(file_instance) == 0) {
|
||||||
|
PRINTF_ERR("CA certificate file not exist\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_instance = Network_Port_Operational_Certificate_File(instance);
|
||||||
|
if (bacfile_file_size(file_instance) == 0) {
|
||||||
|
PRINTF_ERR("Certificate file not exist\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_instance = Network_Port_Certificate_Key_File(instance);
|
||||||
|
if (bacfile_file_size(file_instance) == 0) {
|
||||||
|
PRINTF_ERR("Certificate key file not exist\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief module for common function for BACnet/SC implementation
|
||||||
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||||
|
* @date Jule 2022
|
||||||
|
* @copyright SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
#ifndef BACNET_DATALINK_BSC_UTIL_H
|
||||||
|
#define BACNET_DATALINK_BSC_UTIL_H
|
||||||
|
#include <stdbool.h>
|
||||||
|
/* BACnet Stack defines - first */
|
||||||
|
#include "bacnet/bacdef.h"
|
||||||
|
/* BACnet Stack API */
|
||||||
|
#include "bacnet/basic/sys/mstimer.h"
|
||||||
|
#include "bacnet/basic/sys/debug.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-node.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-retcodes.h"
|
||||||
|
#include "bacnet/datalink/bsc/bvlc-sc.h"
|
||||||
|
#include "bacnet/datalink/bsc/websocket.h"
|
||||||
|
#include "bacnet/datetime.h"
|
||||||
|
|
||||||
|
BSC_SC_RET bsc_map_websocket_retcode(BSC_WEBSOCKET_RET ret);
|
||||||
|
|
||||||
|
void bsc_copy_vmac(BACNET_SC_VMAC_ADDRESS *dst, BACNET_SC_VMAC_ADDRESS *src);
|
||||||
|
void bsc_copy_uuid(BACNET_SC_UUID *dst, BACNET_SC_UUID *src);
|
||||||
|
char *bsc_vmac_to_string(BACNET_SC_VMAC_ADDRESS *vmac);
|
||||||
|
char *bsc_uuid_to_string(BACNET_SC_UUID *uuid);
|
||||||
|
void bsc_generate_random_vmac(BACNET_SC_VMAC_ADDRESS *p);
|
||||||
|
void bsc_generate_random_uuid(BACNET_SC_UUID *p);
|
||||||
|
bool bsc_node_conf_fill_from_netport(
|
||||||
|
BSC_NODE_CONF *bsc_conf, BSC_NODE_EVENT_FUNC event_func);
|
||||||
|
void bsc_node_conf_cleanup(BSC_NODE_CONF *bsc_conf);
|
||||||
|
void bsc_copy_str(char *dst, const char *src, size_t dst_len);
|
||||||
|
void bsc_set_timestamp(BACNET_DATE_TIME *timestamp);
|
||||||
|
|
||||||
|
#endif
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,425 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief API for encoding/decoding of BACnet/SC BVLC messages
|
||||||
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||||
|
* @date May 2022
|
||||||
|
* @copyright SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
#ifndef BACNET_DATALINK_BSC_BVLC_SC_H
|
||||||
|
#define BACNET_DATALINK_BSC_BVLC_SC_H
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
/* BACnet Stack defines - first */
|
||||||
|
#include "bacnet/bacdef.h"
|
||||||
|
/* BACnet Stack API */
|
||||||
|
#include "bacnet/npdu.h"
|
||||||
|
|
||||||
|
#ifndef BVLC_SC_NPDU_SIZE_CONF
|
||||||
|
#define BVLC_SC_NPDU_SIZE 1440
|
||||||
|
#else
|
||||||
|
#define BVLC_SC_NPDU_SIZE BVLC_SC_NPDU_SIZE_CONF
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BVLC_SC_NPDU_MAX_SIZE \
|
||||||
|
61327 /* Table 6-1. NPDU Lengths of BACnet Data Link Layers */
|
||||||
|
#define BVLC_SC_VMAC_SIZE 6
|
||||||
|
#define BVLC_SC_UUID_SIZE 16
|
||||||
|
#define BSC_PRE (2 * BVLC_SC_VMAC_SIZE)
|
||||||
|
|
||||||
|
#if !defined(USER_DEFINED_BVLC_SC_HEADER_OPTION_MAX)
|
||||||
|
#define BVLC_SC_HEADER_OPTION_MAX \
|
||||||
|
4 /* though BACNet standard does not limit number of option headers \
|
||||||
|
the implementation defines max value */
|
||||||
|
#else
|
||||||
|
#define BVLC_SC_HEADER_OPTION_MAX USER_DEFINED_BVLC_SC_HEADER_OPTION_MAX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if BVLC_SC_NPDU_SIZE > BVLC_SC_NPDU_MAX_SIZE
|
||||||
|
#error \
|
||||||
|
"Maximum NPDU Length on BACNet/SC Data Link must be <= BVLC_SC_NPDU_MAX_SIZE"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BACnet/SC BVLC Messages (functions) (AB.2 BACnet/SC Virtual Link Layer
|
||||||
|
* Messages)
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef enum BVLC_SC_Message_Type {
|
||||||
|
BVLC_SC_RESULT = 0x00,
|
||||||
|
BVLC_SC_ENCAPSULATED_NPDU = 0x01,
|
||||||
|
BVLC_SC_ADDRESS_RESOLUTION = 0x02,
|
||||||
|
BVLC_SC_ADDRESS_RESOLUTION_ACK = 0x03,
|
||||||
|
BVLC_SC_ADVERTISIMENT = 0x04,
|
||||||
|
BVLC_SC_ADVERTISIMENT_SOLICITATION = 0x05,
|
||||||
|
BVLC_SC_CONNECT_REQUEST = 0x06,
|
||||||
|
BVLC_SC_CONNECT_ACCEPT = 0x07,
|
||||||
|
BVLC_SC_DISCONNECT_REQUEST = 0x08,
|
||||||
|
BVLC_SC_DISCONNECT_ACK = 0x09,
|
||||||
|
BVLC_SC_HEARTBEAT_REQUEST = 0x0a,
|
||||||
|
BVLC_SC_HEARTBEAT_ACK = 0x0b,
|
||||||
|
BVLC_SC_PROPRIETARY_MESSAGE = 0x0c
|
||||||
|
} BVLC_SC_MESSAGE_TYPE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AB.2.2 Control Flags
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define BVLC_SC_CONTROL_DATA_OPTIONS (1 << 0)
|
||||||
|
#define BVLC_SC_CONTROL_DEST_OPTIONS (1 << 1)
|
||||||
|
#define BVLC_SC_CONTROL_DEST_VADDR (1 << 2)
|
||||||
|
#define BVLC_SC_CONTROL_ORIG_VADDR (1 << 3)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AB.2.3 Header Options
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define BVLC_SC_HEADER_DATA (1 << 5)
|
||||||
|
#define BVLC_SC_HEADER_MUST_UNDERSTAND (1 << 6)
|
||||||
|
#define BVLC_SC_HEADER_MORE (1 << 7)
|
||||||
|
#define BVLC_SC_HEADER_OPTION_TYPE_MASK (0x1F)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BACnet SC VMAC Address
|
||||||
|
*
|
||||||
|
* B.1.5.2 VMAC Addressing of Nodes
|
||||||
|
* For the BVLC message exchange, BACnet/SC nodes are identified by their
|
||||||
|
* 6-octet virtual MAC address as defined in Clause H.7.3.
|
||||||
|
* For broadcast BVLC messages that need to reach all nodes of the BACnet/SC
|
||||||
|
* network, the destination VMAC address shall be the non-EUI-48 value
|
||||||
|
* X'FFFFFFFFFFFF', referred to as the Local Broadcast VMAC address.
|
||||||
|
* The reserved EUI-48 value X'000000000000' is not used by this data link
|
||||||
|
* and therefore can be used internally to indicate that a VMAC is unknown
|
||||||
|
* or uninitialized.
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct BACnet_SC_VMAC_Address {
|
||||||
|
uint8_t address[BVLC_SC_VMAC_SIZE];
|
||||||
|
} BACNET_SC_VMAC_ADDRESS;
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BACnet SC UUID
|
||||||
|
* AB.1.5.3 Device UUID
|
||||||
|
* Every BACnet device that supports one or more BACnet/SC network ports shall
|
||||||
|
* have a Universally Unique ID (UUID) as defined in RFC 4122. This UUID
|
||||||
|
* identifies the device regardless of its current VMAC address or device
|
||||||
|
* instance number and is referred to as the device UUID.
|
||||||
|
* This device UUID shall be generated before first deployment of the device in
|
||||||
|
* an installation, shall be persistently stored across device restarts, and
|
||||||
|
* shall not change over the entire lifetime of a device.
|
||||||
|
* If a device is replaced in an installation, the new device is not required
|
||||||
|
* to reuse the UUID of the replaced device. For BACnet/SC, it is assumed
|
||||||
|
* that existing connections to the device being replaced are all terminated
|
||||||
|
* before the new device comes into operation.
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct BACnet_SC_Uuid {
|
||||||
|
uint8_t uuid[BVLC_SC_UUID_SIZE];
|
||||||
|
} BACNET_SC_UUID;
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AB.2.3.1 Secure Path Header Option
|
||||||
|
*/
|
||||||
|
typedef enum BVLC_SC_Option_Type {
|
||||||
|
BVLC_SC_OPTION_TYPE_SECURE_PATH = 1,
|
||||||
|
BVLC_SC_OPTION_TYPE_PROPRIETARY = 31
|
||||||
|
} BVLC_SC_OPTION_TYPE;
|
||||||
|
|
||||||
|
typedef enum BVLC_SC_Direct_Connection_Support {
|
||||||
|
BVLC_SC_DIRECT_CONNECTION_ACCEPT_UNSUPPORTED = 0,
|
||||||
|
BVLC_SC_DIRECT_CONNECTION_ACCEPT_SUPPORTED = 1,
|
||||||
|
BVLC_SC_DIRECT_CONNECTION_SUPPORT_MAX = 1
|
||||||
|
} BVLC_SC_DIRECT_CONNECTION_SUPPORT;
|
||||||
|
|
||||||
|
typedef struct BVLC_SC_Decoded_Hdr {
|
||||||
|
uint8_t bvlc_function;
|
||||||
|
uint16_t message_id;
|
||||||
|
BACNET_SC_VMAC_ADDRESS *origin;
|
||||||
|
BACNET_SC_VMAC_ADDRESS *dest;
|
||||||
|
uint8_t *dest_options; /* pointer to packed dest options list in message */
|
||||||
|
size_t dest_options_len;
|
||||||
|
size_t dest_options_num; /* number of filled items in dest_options */
|
||||||
|
uint8_t *data_options; /* pointer to packed data options list in message */
|
||||||
|
size_t data_options_len;
|
||||||
|
size_t data_options_num; /* number of filled items in data_options */
|
||||||
|
uint8_t *payload; /* packed payload, points to data in message */
|
||||||
|
size_t payload_len;
|
||||||
|
} BVLC_SC_DECODED_HDR;
|
||||||
|
|
||||||
|
typedef struct BVLC_SC_Decoded_Result {
|
||||||
|
uint8_t bvlc_function;
|
||||||
|
uint8_t result;
|
||||||
|
uint8_t error_header_marker;
|
||||||
|
uint16_t error_class;
|
||||||
|
uint16_t error_code;
|
||||||
|
uint8_t *utf8_details_string; /* NOTE!: this is utf 8 string without
|
||||||
|
trailing zero */
|
||||||
|
size_t utf8_details_string_len;
|
||||||
|
} BVLC_SC_DECODED_RESULT;
|
||||||
|
|
||||||
|
typedef struct BVLC_SC_Decoded_Address_Resolution_Ack {
|
||||||
|
uint8_t *utf8_websocket_uri_string; /* NOTE!: this is utf 8 string without
|
||||||
|
trailing zero */
|
||||||
|
size_t utf8_websocket_uri_string_len;
|
||||||
|
} BVLC_SC_DECODED_ADDRESS_RESOLUTION_ACK;
|
||||||
|
|
||||||
|
typedef struct BVLC_SC_Decoded_Ecapsulated_NPDU {
|
||||||
|
uint8_t *npdu;
|
||||||
|
size_t npdu_len;
|
||||||
|
} BVLC_SC_DECODED_ENCAPSULATED_NPDU;
|
||||||
|
|
||||||
|
typedef struct BVLC_SC_Decoded_Advertisiment {
|
||||||
|
BACNET_SC_HUB_CONNECTOR_STATE hub_status;
|
||||||
|
BVLC_SC_DIRECT_CONNECTION_SUPPORT support;
|
||||||
|
uint16_t max_bvlc_len;
|
||||||
|
uint16_t max_npdu_len;
|
||||||
|
} BVLC_SC_DECODED_ADVERTISIMENT;
|
||||||
|
|
||||||
|
typedef struct BVLC_SC_Decoded_Connect_Request {
|
||||||
|
BACNET_SC_VMAC_ADDRESS *vmac;
|
||||||
|
BACNET_SC_UUID *uuid;
|
||||||
|
uint16_t max_bvlc_len;
|
||||||
|
uint16_t max_npdu_len;
|
||||||
|
} BVLC_SC_DECODED_CONNECT_REQUEST;
|
||||||
|
|
||||||
|
typedef struct BVLC_SC_Decoded_Connect_Accept {
|
||||||
|
BACNET_SC_VMAC_ADDRESS *vmac;
|
||||||
|
BACNET_SC_UUID *uuid;
|
||||||
|
uint16_t max_bvlc_len;
|
||||||
|
uint16_t max_npdu_len;
|
||||||
|
} BVLC_SC_DECODED_CONNECT_ACCEPT;
|
||||||
|
|
||||||
|
typedef struct BVLC_SC_Decoded_Proprietary {
|
||||||
|
uint16_t vendor_id;
|
||||||
|
uint8_t function;
|
||||||
|
uint8_t *data;
|
||||||
|
size_t data_len;
|
||||||
|
} BVLC_SC_DECODED_PROPRIETARY;
|
||||||
|
|
||||||
|
typedef union BVLC_SC_Decoded_Data {
|
||||||
|
BVLC_SC_DECODED_RESULT result;
|
||||||
|
BVLC_SC_DECODED_ENCAPSULATED_NPDU encapsulated_npdu;
|
||||||
|
BVLC_SC_DECODED_ADDRESS_RESOLUTION_ACK address_resolution_ack;
|
||||||
|
BVLC_SC_DECODED_ADVERTISIMENT advertisiment;
|
||||||
|
BVLC_SC_DECODED_CONNECT_REQUEST connect_request;
|
||||||
|
BVLC_SC_DECODED_CONNECT_ACCEPT connect_accept;
|
||||||
|
BVLC_SC_DECODED_PROPRIETARY proprietary;
|
||||||
|
} BVLC_SC_DECODED_DATA;
|
||||||
|
|
||||||
|
typedef struct BVLC_SC_Decoded_Hdr_Proprietary_Option {
|
||||||
|
uint16_t vendor_id;
|
||||||
|
uint8_t option_type;
|
||||||
|
uint8_t *data;
|
||||||
|
size_t data_len;
|
||||||
|
} BVLC_SC_DECODED_HDR_PROPRIETARY_OPTION;
|
||||||
|
|
||||||
|
typedef union BVLC_SC_Decoded_Specific_Option_Data {
|
||||||
|
BVLC_SC_DECODED_HDR_PROPRIETARY_OPTION proprietary;
|
||||||
|
} BVLC_SC_DECODED_SPECIFIC_OPTION_DATA;
|
||||||
|
|
||||||
|
typedef struct BVLC_SC_Decoded_Hdr_Option {
|
||||||
|
uint8_t packed_header_marker;
|
||||||
|
BVLC_SC_OPTION_TYPE type;
|
||||||
|
bool must_understand;
|
||||||
|
BVLC_SC_DECODED_SPECIFIC_OPTION_DATA specific;
|
||||||
|
} BVLC_SC_DECODED_HDR_OPTION;
|
||||||
|
|
||||||
|
typedef struct BVLC_SC_Decoded_Message {
|
||||||
|
BVLC_SC_DECODED_HDR hdr;
|
||||||
|
BVLC_SC_DECODED_HDR_OPTION data_options[BVLC_SC_HEADER_OPTION_MAX];
|
||||||
|
BVLC_SC_DECODED_HDR_OPTION dest_options[BVLC_SC_HEADER_OPTION_MAX];
|
||||||
|
BVLC_SC_DECODED_DATA payload;
|
||||||
|
} BVLC_SC_DECODED_MESSAGE;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
size_t bvlc_sc_add_option_to_destination_options(
|
||||||
|
uint8_t *out_pdu,
|
||||||
|
size_t out_pdu_size,
|
||||||
|
uint8_t *pdu,
|
||||||
|
size_t pdu_size,
|
||||||
|
uint8_t *sc_option,
|
||||||
|
size_t sc_option_len);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
size_t bvlc_sc_add_option_to_data_options(
|
||||||
|
uint8_t *out_pdu,
|
||||||
|
size_t out_pdu_size,
|
||||||
|
uint8_t *pdu,
|
||||||
|
size_t pdu_size,
|
||||||
|
uint8_t *sc_option,
|
||||||
|
size_t sc_option_len);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
size_t bvlc_sc_encode_proprietary_option(
|
||||||
|
uint8_t *pdu,
|
||||||
|
size_t pdu_size,
|
||||||
|
bool must_understand,
|
||||||
|
uint16_t vendor_id,
|
||||||
|
uint8_t proprietary_option_type,
|
||||||
|
uint8_t *proprietary_data,
|
||||||
|
size_t proprietary_data_len);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
size_t bvlc_sc_encode_secure_path_option(
|
||||||
|
uint8_t *pdu, size_t pdu_size, bool must_understand);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
size_t bvlc_sc_encode_result(
|
||||||
|
uint8_t *pdu,
|
||||||
|
size_t pdu_len,
|
||||||
|
uint16_t message_id,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *origin,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *dest,
|
||||||
|
uint8_t bvlc_function,
|
||||||
|
uint8_t result_code,
|
||||||
|
uint8_t *error_header_marker,
|
||||||
|
uint16_t *error_class,
|
||||||
|
uint16_t *error_code,
|
||||||
|
uint8_t *utf8_details_string);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
size_t bvlc_sc_encode_encapsulated_npdu(
|
||||||
|
uint8_t *pdu,
|
||||||
|
size_t pdu_len,
|
||||||
|
uint16_t message_id,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *origin,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *dest,
|
||||||
|
uint8_t *npdu,
|
||||||
|
size_t npdu_size);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
size_t bvlc_sc_encode_address_resolution(
|
||||||
|
uint8_t *pdu,
|
||||||
|
size_t pdu_len,
|
||||||
|
uint16_t message_id,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *origin,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *dest);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
size_t bvlc_sc_encode_address_resolution_ack(
|
||||||
|
uint8_t *pdu,
|
||||||
|
size_t pdu_len,
|
||||||
|
uint16_t message_id,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *origin,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *dest,
|
||||||
|
uint8_t *web_socket_uris,
|
||||||
|
size_t web_socket_uris_len);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
size_t bvlc_sc_encode_advertisiment(
|
||||||
|
uint8_t *pdu,
|
||||||
|
size_t pdu_len,
|
||||||
|
uint16_t message_id,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *origin,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *dest,
|
||||||
|
BACNET_SC_HUB_CONNECTOR_STATE hub_status,
|
||||||
|
BVLC_SC_DIRECT_CONNECTION_SUPPORT support,
|
||||||
|
uint16_t max_bvlc_len,
|
||||||
|
uint16_t max_npdu_size);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
size_t bvlc_sc_encode_advertisiment_solicitation(
|
||||||
|
uint8_t *pdu,
|
||||||
|
size_t pdu_len,
|
||||||
|
uint16_t message_id,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *origin,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *dest);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
size_t bvlc_sc_encode_connect_request(
|
||||||
|
uint8_t *pdu,
|
||||||
|
size_t pdu_len,
|
||||||
|
uint16_t message_id,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *local_vmac,
|
||||||
|
BACNET_SC_UUID *local_uuid,
|
||||||
|
uint16_t max_bvlc_len,
|
||||||
|
uint16_t max_npdu_size);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
size_t bvlc_sc_encode_connect_accept(
|
||||||
|
uint8_t *pdu,
|
||||||
|
size_t pdu_len,
|
||||||
|
uint16_t message_id,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *local_vmac,
|
||||||
|
BACNET_SC_UUID *local_uuid,
|
||||||
|
uint16_t max_bvlc_len,
|
||||||
|
uint16_t max_npdu_len);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
size_t bvlc_sc_encode_disconnect_request(
|
||||||
|
uint8_t *pdu, size_t pdu_len, uint16_t message_id);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
size_t bvlc_sc_encode_disconnect_ack(
|
||||||
|
uint8_t *pdu, size_t pdu_len, uint16_t message_id);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
size_t bvlc_sc_encode_heartbeat_request(
|
||||||
|
uint8_t *out_buf, size_t out_buf_len, uint16_t message_id);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
size_t bvlc_sc_encode_heartbeat_ack(
|
||||||
|
uint8_t *out_buf, size_t out_buf_len, uint16_t message_id);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
size_t bvlc_sc_encode_proprietary_message(
|
||||||
|
uint8_t *pdu,
|
||||||
|
size_t pdu_len,
|
||||||
|
uint16_t message_id,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *origin,
|
||||||
|
BACNET_SC_VMAC_ADDRESS *dest,
|
||||||
|
uint16_t vendor_id,
|
||||||
|
uint8_t proprietary_function,
|
||||||
|
uint8_t *proprietary_data,
|
||||||
|
size_t proprietary_data_len);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool bvlc_sc_decode_message(
|
||||||
|
uint8_t *buf,
|
||||||
|
size_t buf_len,
|
||||||
|
BVLC_SC_DECODED_MESSAGE *message,
|
||||||
|
BACNET_ERROR_CODE *error,
|
||||||
|
BACNET_ERROR_CLASS *class,
|
||||||
|
const char **err_desc);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void bvlc_sc_remove_dest_set_orig(
|
||||||
|
uint8_t *pdu, size_t pdu_len, BACNET_SC_VMAC_ADDRESS *orig);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
size_t
|
||||||
|
bvlc_sc_set_orig(uint8_t **ppdu, size_t pdu_len, BACNET_SC_VMAC_ADDRESS *orig);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool bvlc_sc_is_vmac_broadcast(BACNET_SC_VMAC_ADDRESS *vmac);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool bvlc_sc_need_send_bvlc_result(BVLC_SC_DECODED_MESSAGE *dm);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool bvlc_sc_pdu_has_dest_broadcast(uint8_t *pdu, size_t pdu_len);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool bvlc_sc_pdu_has_no_dest(uint8_t *pdu, size_t pdu_len);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool bvlc_sc_pdu_get_dest(
|
||||||
|
uint8_t *pdu, size_t pdu_len, BACNET_SC_VMAC_ADDRESS *vmac);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
size_t bvlc_sc_remove_orig_and_dest(uint8_t **ppdu, size_t pdu_len);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,450 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Client/Server thread-safe websocket interface API.
|
||||||
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||||
|
* @date May 2022
|
||||||
|
* @copyright SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
#ifndef BACNET_DATALINK_BSC_WEBSOCKET_H
|
||||||
|
#define BACNET_DATALINK_BSC_WEBSOCKET_H
|
||||||
|
/* BACnet Stack defines - first */
|
||||||
|
#include "bacnet/bacdef.h"
|
||||||
|
/* BACnet Stack API */
|
||||||
|
#include "bacnet/datalink/bsc/bsc-conf.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of sockets that can be opened on client's side.
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
#ifndef BSC_CONF_CLIENT_CONNECTIONS_NUM
|
||||||
|
#define BSC_CLIENT_WEBSOCKETS_MAX_NUM 4
|
||||||
|
#else
|
||||||
|
#define BSC_CLIENT_WEBSOCKETS_MAX_NUM BSC_CONF_CLIENT_CONNECTIONS_NUM
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of server instances. Value 10 means that
|
||||||
|
* 10 hub servers and 10 direct servers can be started.
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BSC_CONF_WEBSOCKET_SERVERS_NUM
|
||||||
|
#define BSC_CONF_WEBSOCKET_SERVERS_NUM 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of sockets supported for hub websocket server
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
#ifndef BSC_CONF_SERVER_HUB_CONNECTIONS_MAX_NUM
|
||||||
|
#define BSC_SERVER_HUB_WEBSOCKETS_MAX_NUM 4
|
||||||
|
#else
|
||||||
|
#define BSC_SERVER_HUB_WEBSOCKETS_MAX_NUM \
|
||||||
|
BSC_CONF_SERVER_HUB_CONNECTIONS_MAX_NUM
|
||||||
|
#endif
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initial size of websocket buffer used on receive. It can be increased
|
||||||
|
* dynamically depending on received packet size.
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BSC_CONF_WEBSOCKET_RX_BUFFER_LEN
|
||||||
|
#define BSC_WEBSOCKET_RX_BUFFER_LEN 512
|
||||||
|
#else
|
||||||
|
#define BSC_WEBSOCKET_RX_BUFFER_LEN BSC_CONF_WEBSOCKET_RX_BUFFER_LEN
|
||||||
|
#endif
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of sockets supported for direct websocket server
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
#ifndef BSC_CONF_SERVER_DIRECT_CONNECTIONS_MAX_NUM
|
||||||
|
#define BSC_SERVER_DIRECT_WEBSOCKETS_MAX_NUM 4
|
||||||
|
#else
|
||||||
|
#define BSC_SERVER_DIRECT_WEBSOCKETS_MAX_NUM \
|
||||||
|
BSC_CONF_SERVER_DIRECT_CONNECTIONS_MAX_NUM
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BSC_CONF_WEBSOCKET_ERR_DESC_STR_MAX_LEN
|
||||||
|
#define BSC_WEBSOCKET_ERR_DESC_STR_MAX_LEN 128
|
||||||
|
#else
|
||||||
|
#define BSC_WEBSOCKET_ERR_DESC_STR_MAX_LEN \
|
||||||
|
BSC_CONF_WEBSOCKET_ERR_DESC_STR_MAX_LEN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BSC_WSURL_MAX_LEN BSC_CONF_WSURL_MAX_LEN
|
||||||
|
|
||||||
|
typedef int BSC_WEBSOCKET_HANDLE;
|
||||||
|
typedef void *BSC_WEBSOCKET_SRV_HANDLE;
|
||||||
|
#define BSC_WEBSOCKET_INVALID_HANDLE (-1)
|
||||||
|
|
||||||
|
/* Websockets protocol defined in BACnet/SC \S AB.7.1. */
|
||||||
|
#define BSC_WEBSOCKET_HUB_PROTOCOL_STR "hub.bsc.bacnet.org"
|
||||||
|
#define BSC_WEBSOCKET_DIRECT_PROTOCOL_STR "dc.bsc.bacnet.org"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BSC_WEBSOCKET_HUB_PROTOCOL = 0,
|
||||||
|
BSC_WEBSOCKET_DIRECT_PROTOCOL = 1,
|
||||||
|
BSC_WEBSOCKET_PROTOCOLS_AMOUNT = 2 /* must be always last */
|
||||||
|
} BSC_WEBSOCKET_PROTOCOL;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BSC_WEBSOCKET_SUCCESS = 0,
|
||||||
|
BSC_WEBSOCKET_NO_RESOURCES = 1,
|
||||||
|
BSC_WEBSOCKET_BAD_PARAM = 2,
|
||||||
|
BSC_WEBSOCKET_INVALID_OPERATION = 3
|
||||||
|
} BSC_WEBSOCKET_RET;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BSC_WEBSOCKET_CONNECTED = 0,
|
||||||
|
BSC_WEBSOCKET_DISCONNECTED = 1,
|
||||||
|
BSC_WEBSOCKET_RECEIVED = 2,
|
||||||
|
BSC_WEBSOCKET_SENDABLE = 3,
|
||||||
|
BSC_WEBSOCKET_SERVER_STARTED = 4,
|
||||||
|
BSC_WEBSOCKET_SERVER_STOPPED = 5
|
||||||
|
} BSC_WEBSOCKET_EVENT;
|
||||||
|
|
||||||
|
/*
|
||||||
|
values of ws_reason and ws_reason_desc parameters are actual
|
||||||
|
only for BSC_WEBSOCKET_DISCONNECTED event.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef void (*BSC_WEBSOCKET_CLI_DISPATCH)(
|
||||||
|
BSC_WEBSOCKET_HANDLE h,
|
||||||
|
BSC_WEBSOCKET_EVENT ev,
|
||||||
|
BACNET_ERROR_CODE ws_reason,
|
||||||
|
char *ws_reason_desc,
|
||||||
|
uint8_t *buf,
|
||||||
|
size_t bufsize,
|
||||||
|
void *dispatch_func_user_param);
|
||||||
|
|
||||||
|
typedef void (*BSC_WEBSOCKET_SRV_DISPATCH)(
|
||||||
|
BSC_WEBSOCKET_SRV_HANDLE sh,
|
||||||
|
BSC_WEBSOCKET_HANDLE h,
|
||||||
|
BSC_WEBSOCKET_EVENT ev,
|
||||||
|
BACNET_ERROR_CODE ws_reason,
|
||||||
|
char *ws_reason_desc,
|
||||||
|
uint8_t *buf,
|
||||||
|
size_t bufsize,
|
||||||
|
void *dispatch_func_user_param);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Asynchronous bws_cli_сonnect() function starts establishing
|
||||||
|
* of a new connection to a websocket server specified by url parameter.
|
||||||
|
* Result of completion of operation is call of dispatch_func() with
|
||||||
|
* BSC_WEBSOCKET_CONNECTED in a case if connection established successfully or
|
||||||
|
* BSC_WEBSOCKET_DISCONNECTED if connection attempt failed.
|
||||||
|
*
|
||||||
|
* @param type - type of BACNet/SC connection, check
|
||||||
|
* BSC_WEBSOCKET_CONNECTION_TYPE enum. According BACNet standard
|
||||||
|
* different type of connections require different websocket protocols.
|
||||||
|
* @param url - BACNet/SC server URL. For example: wss://legrand.com:8080.
|
||||||
|
* @param ca_cert - pointer to certificate authority (CA) cert in PEM or DER
|
||||||
|
* format.
|
||||||
|
* @param ca_cert_size - size in bytes of CA cert.
|
||||||
|
* @param cert - pointer to client certificate in PEM or DER format.
|
||||||
|
* @param cert_size - size in bytes of client certificate.
|
||||||
|
* @param key - pointer to client private key in PEM or DER format.
|
||||||
|
* @param key_size - size of private key in bytes of of client certificate.
|
||||||
|
* @param timeout_s - timeout for socket operations (connect, etc..) in seconds.
|
||||||
|
* Must be > 0.
|
||||||
|
* @param dispatch_func - pointer to dispatch callback function to handle
|
||||||
|
* events from websocket specified by *out_handle.
|
||||||
|
* @param dispatch_func_user_param - parameter which is passed into
|
||||||
|
dispatch_func call.
|
||||||
|
* @param out_handle - pointer to a websocket handle.
|
||||||
|
*
|
||||||
|
* @return error code from BSC_WEBSOCKET_RET enum.
|
||||||
|
* The following error codes can be returned:
|
||||||
|
* BSC_WEBSOCKET_BAD_PARAM - In a case if some input parameter is
|
||||||
|
* incorrect.
|
||||||
|
* BSC_WEBSOCKET_NO_RESOURCES - if a user has already opened
|
||||||
|
* more sockets than the limit defined by BSC_CLIENT_WEBSOCKETS_MAX_NUM,
|
||||||
|
* or if some mem allocation has failed or some allocation of system
|
||||||
|
* resources like mutex, thread, etc .., failed.
|
||||||
|
* BSC_WEBSOCKET_SUCCESS - connect operation was successfully started.
|
||||||
|
*/
|
||||||
|
|
||||||
|
BSC_WEBSOCKET_RET bws_cli_connect(
|
||||||
|
BSC_WEBSOCKET_PROTOCOL proto,
|
||||||
|
char *url,
|
||||||
|
uint8_t *ca_cert,
|
||||||
|
size_t ca_cert_size,
|
||||||
|
uint8_t *cert,
|
||||||
|
size_t cert_size,
|
||||||
|
uint8_t *key,
|
||||||
|
size_t key_size,
|
||||||
|
size_t timeout_s,
|
||||||
|
BSC_WEBSOCKET_CLI_DISPATCH dispatch_func,
|
||||||
|
void *dispatch_func_user_param,
|
||||||
|
BSC_WEBSOCKET_HANDLE *out_handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Asynchronous bws_cli_disconnnect() function starts process of
|
||||||
|
* disconnection for specified websocket handle. When the process completes,
|
||||||
|
* dispatch_func() with event BSC_WEBSOCKET_DISCONNECTED is called.
|
||||||
|
* connection to some websocket server.
|
||||||
|
*
|
||||||
|
* @param h - websocket handle.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
void bws_cli_disconnect(BSC_WEBSOCKET_HANDLE h);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Non-blocked bws_cli_send() function signals to the websocket
|
||||||
|
* specified by websocket handle h that some data is needed to be sent.
|
||||||
|
* When websocket becomes sendable, dispatch_func() is called with
|
||||||
|
* event BSC_WEBSOCKET_SENDABLE and data can be sent from dispatch_func()
|
||||||
|
* call using bws_cli_dispatch_send() call.
|
||||||
|
*
|
||||||
|
* @param h - websocket handle.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
void bws_cli_send(BSC_WEBSOCKET_HANDLE h);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief bws_cli_dispatch_send() function sends data to a websocket server
|
||||||
|
* in a case if websocket handle is sendable (e.g. ready to send data).
|
||||||
|
* In as case if data was not sent for some reasons thic could result
|
||||||
|
* dispatch_func() cal with event BSC_WEBSOCKET_DISCONNECTED
|
||||||
|
* @param h - websocket handle.
|
||||||
|
* @param payload - pointer to a data to send. It is assumed that
|
||||||
|
* BSC_CONF_TX_PRE bytes are available before payload.
|
||||||
|
* So for example you need this kind of code to use
|
||||||
|
* bws_srv_dispatch_send with a 128-byte payload:
|
||||||
|
*
|
||||||
|
* char buf[BSC_CONF_TX_PRE + 128];
|
||||||
|
*
|
||||||
|
* // fill your part of the buffer... for example here
|
||||||
|
* // it's all zeros
|
||||||
|
*
|
||||||
|
* memset(&buf[BSC_CONF_TX_PRE], 0, 128);
|
||||||
|
* bws_cli_dispatch_send(sh, h, &buf[BSC_CONF_TX_PRE], 128);
|
||||||
|
*
|
||||||
|
* @param payload_size - size in bytes of data to send.
|
||||||
|
*
|
||||||
|
* @return error code from BSC_WEBSOCKET_RET enum.
|
||||||
|
* The following error codes can be returned:
|
||||||
|
* BSC_WEBSOCKET_BAD_PARAM - In a case if some input parameter is
|
||||||
|
* incorrect.
|
||||||
|
* BSC_WEBSOCKET_NO_RESOURCES - if some mem allocation has failed o
|
||||||
|
* some allocation of system resources like mutex, thread,
|
||||||
|
* etc .., has failed.
|
||||||
|
* BSC_WEBSOCKET_INVALID_OPERATION - if the function was called not from
|
||||||
|
* dispatch_func() callback context or websocket is not in connected
|
||||||
|
* state.
|
||||||
|
* BSC_WEBSOCKET_SUCCESS - data is sent successfully.
|
||||||
|
*/
|
||||||
|
|
||||||
|
BSC_WEBSOCKET_RET bws_cli_dispatch_send(
|
||||||
|
BSC_WEBSOCKET_HANDLE h, uint8_t *payload, size_t payload_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Asynchronous bws_srv_start() function triggers process of
|
||||||
|
* starting of a websocket server on a specified port for specified
|
||||||
|
* BACNet websocket protocol. At present time peer can have only 2
|
||||||
|
* instances of server: onerelates to BSC_WEBSOCKET_HUB_PROTOCOL and
|
||||||
|
* the other to BSC_WEBSOCKET_HUB_PROTOCOL. When process completes,
|
||||||
|
* dispatch_func() is called with BSC_WEBSOCKET_SERVER_STARTED
|
||||||
|
* event.
|
||||||
|
*
|
||||||
|
* @param proto - type of BACNet websocket protocol defined in
|
||||||
|
* BSC_WEBSOCKET_PROTOCOL enum.
|
||||||
|
* @param port - port number.
|
||||||
|
* @param iface - name of interface to bind to. If the parameter is NULL
|
||||||
|
* that means that websocket server binds to all interfaces.
|
||||||
|
* @param ca_cert - pointer to certificate authority (CA) cert in PEM or DER
|
||||||
|
* format.
|
||||||
|
* @param ca_cert_size - size in bytes of CA cert.
|
||||||
|
* @param cert - pointer to server certificate in PEM or DER format.
|
||||||
|
* @param cert_size - size in bytes of server certificate.
|
||||||
|
* @param key - pointer to server private key in PEM or DER format.
|
||||||
|
* @param key_size - size of private key in bytes of of client certificate.
|
||||||
|
* @param timeout_s - timeout for socket operations (connect, etc..) in seconds.
|
||||||
|
* Must be > 0.
|
||||||
|
* @param dispatch_func - pointer to dispatch callback function to handle
|
||||||
|
* events from a websocket which is corresponded to
|
||||||
|
* server specified by proto param.
|
||||||
|
* @param dispatch_func_user_param - parameter which is passed into
|
||||||
|
* dispatch_func call.
|
||||||
|
* @param sh - pointer to receive websocket server handle
|
||||||
|
*
|
||||||
|
* @return error code from BSC_WEBSOCKET_RET enum.
|
||||||
|
* The following error codes can be returned:
|
||||||
|
* BSC_WEBSOCKET_BAD_PARAM - In a case if some input parameter is
|
||||||
|
* incorrect.
|
||||||
|
* BSC_WEBSOCKET_NO_RESOURCES - if a user has already opened
|
||||||
|
* more sockets than the limit defined to corresponded protocol
|
||||||
|
* (BSC_SERVER_HUB_WEBSOCKETS_MAX_NUM or
|
||||||
|
* BSC_CLIENT_WEBSOCKETS_MAX_NUM), or if some mem allocation
|
||||||
|
* has failed or some allocation of system resources like
|
||||||
|
* mutex, thread, condition variable etc .., failed.
|
||||||
|
* BSC_WEBSOCKET_SUCCESS - the start operation is in progress.
|
||||||
|
* BSC_WEBSOCKET_INVALID_OPERATION - operation is not started because
|
||||||
|
* server in a process of shutdown of server is already started.
|
||||||
|
*/
|
||||||
|
|
||||||
|
BSC_WEBSOCKET_RET bws_srv_start(
|
||||||
|
BSC_WEBSOCKET_PROTOCOL proto,
|
||||||
|
int port,
|
||||||
|
char *iface,
|
||||||
|
uint8_t *ca_cert,
|
||||||
|
size_t ca_cert_size,
|
||||||
|
uint8_t *cert,
|
||||||
|
size_t cert_size,
|
||||||
|
uint8_t *key,
|
||||||
|
size_t key_size,
|
||||||
|
size_t timeout_s,
|
||||||
|
BSC_WEBSOCKET_SRV_DISPATCH dispatch_func,
|
||||||
|
void *dispatch_func_user_param,
|
||||||
|
BSC_WEBSOCKET_SRV_HANDLE *sh);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Asynchronous bws_srv_stop() function starts process of a shutdowns
|
||||||
|
* of a websocket server specified by proto param.
|
||||||
|
* opened websocket connections are closed.
|
||||||
|
*
|
||||||
|
* @param sh - websocket server handle
|
||||||
|
*
|
||||||
|
* @return error code from BSC_WEBSOCKET_RET enum.
|
||||||
|
* The following error codes can be returned:
|
||||||
|
* BSC_WEBSOCKET_SUCCESS - the operation is started or server was
|
||||||
|
* already stopped before. BSC_WEBSOCKET_INVALID_OPERATION - if server was not
|
||||||
|
* started or server shutdown is already in progress.
|
||||||
|
*/
|
||||||
|
|
||||||
|
BSC_WEBSOCKET_RET bws_srv_stop(BSC_WEBSOCKET_SRV_HANDLE sh);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Asynchronous bws_srv_disconnnect() function starts process of
|
||||||
|
* disconnection for specified websocket handle h for specified server type
|
||||||
|
* by proto parameter. When the process completes, dispatch_func() with event
|
||||||
|
* BSC_WEBSOCKET_DISCONNECTED is called.
|
||||||
|
*
|
||||||
|
* @param sh - websocket server handle.
|
||||||
|
* @param h - websocket handle.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
void bws_srv_disconnect(BSC_WEBSOCKET_SRV_HANDLE sh, BSC_WEBSOCKET_HANDLE h);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Asynchronous bws_srv_send() function signals to a websocket
|
||||||
|
* specified by handle h for specified server type by proto param that
|
||||||
|
* some data is needed to be sent.
|
||||||
|
*
|
||||||
|
* When websocket becomes sendable, dispatch_func() is called with
|
||||||
|
* event BSC_WEBSOCKET_SENDABLE and data can be sent from dispatch_func()
|
||||||
|
* call using bws_srv_dispatch_send() call.
|
||||||
|
*
|
||||||
|
* @param sh - websocket server handle.
|
||||||
|
* @param h - websocket handle.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
void bws_srv_send(BSC_WEBSOCKET_SRV_HANDLE sh, BSC_WEBSOCKET_HANDLE h);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief bws_srv_dispatch_send() function sends data to a websocket server
|
||||||
|
* in a case if websocket handle is sendable (e.g. ready to send data).
|
||||||
|
* In as case if data was not sent for some reasons thic could result
|
||||||
|
* dispatch_func() call with event BSC_WEBSOCKET_DISCONNECTED
|
||||||
|
*
|
||||||
|
* @param sh - websocket server handle.
|
||||||
|
* @param h - websocket handle.
|
||||||
|
* @param payload - pointer to a data to send. It is assumed that
|
||||||
|
* BSC_CONF_TX_PRE bytes are available before payload.
|
||||||
|
* So for example you need this kind of code to use
|
||||||
|
* bws_srv_dispatch_send with a 128-byte payload:
|
||||||
|
*
|
||||||
|
* char buf[BSC_CONF_TX_PRE + 128];
|
||||||
|
*
|
||||||
|
* // fill your part of the buffer... for example here
|
||||||
|
* // it's all zeros
|
||||||
|
*
|
||||||
|
* memset(&buf[BSC_CONF_TX_PRE], 0, 128);
|
||||||
|
* bws_srv_dispatch_send(sh, h, &buf[BSC_CONF_TX_PRE], 128);
|
||||||
|
*
|
||||||
|
* @param payload_size - size in bytes of data to send.
|
||||||
|
*
|
||||||
|
* @return error code from BSC_WEBSOCKET_RET enum.
|
||||||
|
* The following error codes can be returned:
|
||||||
|
* BSC_WEBSOCKET_BAD_PARAM - In a case if some input parameter is
|
||||||
|
* incorrect.
|
||||||
|
* BSC_WEBSOCKET_NO_RESOURCES - if some mem allocation has failed o
|
||||||
|
* some allocation of system resources like mutex, thread,
|
||||||
|
* etc .., has failed.
|
||||||
|
* BSC_WEBSOCKET_INVALID_OPERATION - if the function was called not from
|
||||||
|
* dispatch_func() callback context or websocket is not in connected
|
||||||
|
* state or server in a process of a shutdown or server is not started.
|
||||||
|
* BSC_WEBSOCKET_SUCCESS - data is sent successfully.
|
||||||
|
*/
|
||||||
|
|
||||||
|
BSC_WEBSOCKET_RET bws_srv_dispatch_send(
|
||||||
|
BSC_WEBSOCKET_SRV_HANDLE sh,
|
||||||
|
BSC_WEBSOCKET_HANDLE h,
|
||||||
|
uint8_t *payload,
|
||||||
|
size_t payload_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief bws_dispatch_lock() function acquires internal websocket
|
||||||
|
* global mutex. As a result dispatch_func() callback for all
|
||||||
|
* websocket server and client instances won't be called until user
|
||||||
|
* calls bws_dispatch_unlock().
|
||||||
|
*/
|
||||||
|
#ifndef BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED
|
||||||
|
#define BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED != 1)
|
||||||
|
void bws_dispatch_lock(void);
|
||||||
|
#else
|
||||||
|
extern void bws_dispatch_lock_dbg(char *f, int line);
|
||||||
|
#define bws_dispatch_lock() bws_dispatch_lock_dbg(__FILE__, __LINE__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief bws_dispatch_unlock() function releases internal websocket
|
||||||
|
* global mutex.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if (BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED != 1)
|
||||||
|
void bws_dispatch_unlock(void);
|
||||||
|
#else
|
||||||
|
extern void bws_dispatch_unlock_dbg(char *f, int line);
|
||||||
|
#define bws_dispatch_unlock() bws_dispatch_unlock_dbg(__FILE__, __LINE__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief bws_srv_get_peer_ip_addr() gets ipv4 or ipv6 address as ANSI string
|
||||||
|
* and port of remote peer.
|
||||||
|
*
|
||||||
|
* @param sh - websocket server handle.
|
||||||
|
* @param h - websocket handle.
|
||||||
|
* @param ip_str - buffer to store null terminated string of ip address.
|
||||||
|
* @param ip_str_len - size of ip_str buffer
|
||||||
|
* @param port- pointer to store port of a remote node.
|
||||||
|
*
|
||||||
|
* @return true if function succeeded otherwise returns false
|
||||||
|
* if peer's address information can't be retrieved from
|
||||||
|
* underlying websocket library.
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool bws_srv_get_peer_ip_addr(
|
||||||
|
BSC_WEBSOCKET_SRV_HANDLE sh,
|
||||||
|
BSC_WEBSOCKET_HANDLE h,
|
||||||
|
uint8_t *ip_str,
|
||||||
|
size_t ip_str_len,
|
||||||
|
uint16_t *port);
|
||||||
|
#endif
|
||||||
@@ -29,6 +29,9 @@
|
|||||||
#if defined(BACDL_MSTP)
|
#if defined(BACDL_MSTP)
|
||||||
#include "bacnet/datalink/dlmstp.h"
|
#include "bacnet/datalink/dlmstp.h"
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(BACDL_BSC)
|
||||||
|
#include "bacnet/datalink/bsc/bsc-datalink.h"
|
||||||
|
#endif
|
||||||
#ifdef HAVE_STRINGS_H
|
#ifdef HAVE_STRINGS_H
|
||||||
#include <strings.h> /* for strcasecmp() */
|
#include <strings.h> /* for strcasecmp() */
|
||||||
#endif
|
#endif
|
||||||
@@ -39,7 +42,8 @@ static enum {
|
|||||||
DATALINK_ETHERNET,
|
DATALINK_ETHERNET,
|
||||||
DATALINK_BIP,
|
DATALINK_BIP,
|
||||||
DATALINK_BIP6,
|
DATALINK_BIP6,
|
||||||
DATALINK_MSTP
|
DATALINK_MSTP,
|
||||||
|
DATALINK_BSC
|
||||||
} Datalink_Transport;
|
} Datalink_Transport;
|
||||||
|
|
||||||
void datalink_set(char *datalink_string)
|
void datalink_set(char *datalink_string)
|
||||||
@@ -72,6 +76,11 @@ void datalink_set(char *datalink_string)
|
|||||||
Datalink_Transport = DATALINK_MSTP;
|
Datalink_Transport = DATALINK_MSTP;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(BACDL_BSC)
|
||||||
|
else if (strcasecmp("bsc", datalink_string) == 0) {
|
||||||
|
Datalink_Transport = DATALINK_BSC;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool datalink_init(char *ifname)
|
bool datalink_init(char *ifname)
|
||||||
@@ -106,6 +115,11 @@ bool datalink_init(char *ifname)
|
|||||||
case DATALINK_MSTP:
|
case DATALINK_MSTP:
|
||||||
status = dlmstp_init(ifname);
|
status = dlmstp_init(ifname);
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
#if defined(BACDL_BSC)
|
||||||
|
case DATALINK_BSC:
|
||||||
|
status = bsc_init(ifname);
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -150,6 +164,11 @@ int datalink_send_pdu(
|
|||||||
case DATALINK_MSTP:
|
case DATALINK_MSTP:
|
||||||
bytes = dlmstp_send_pdu(dest, npdu_data, pdu, pdu_len);
|
bytes = dlmstp_send_pdu(dest, npdu_data, pdu, pdu_len);
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
#if defined(BACDL_BSC)
|
||||||
|
case DATALINK_BSC:
|
||||||
|
bytes = bsc_send_pdu(dest, npdu_data, pdu, pdu_len);
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -190,6 +209,11 @@ uint16_t datalink_receive(
|
|||||||
case DATALINK_MSTP:
|
case DATALINK_MSTP:
|
||||||
bytes = dlmstp_receive(src, pdu, max_pdu, timeout);
|
bytes = dlmstp_receive(src, pdu, max_pdu, timeout);
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
#if defined(BACDL_BSC)
|
||||||
|
case DATALINK_BSC:
|
||||||
|
bytes = bsc_receive(src, pdu, max_pdu, timeout);
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -227,6 +251,11 @@ void datalink_cleanup(void)
|
|||||||
case DATALINK_MSTP:
|
case DATALINK_MSTP:
|
||||||
dlmstp_cleanup();
|
dlmstp_cleanup();
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
#if defined(BACDL_BSC)
|
||||||
|
case DATALINK_BSC:
|
||||||
|
bsc_cleanup();
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -262,6 +291,11 @@ void datalink_get_broadcast_address(BACNET_ADDRESS *dest)
|
|||||||
case DATALINK_MSTP:
|
case DATALINK_MSTP:
|
||||||
dlmstp_get_broadcast_address(dest);
|
dlmstp_get_broadcast_address(dest);
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
#if defined(BACDL_BSC)
|
||||||
|
case DATALINK_BSC:
|
||||||
|
bsc_get_broadcast_address(dest);
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -297,6 +331,11 @@ void datalink_get_my_address(BACNET_ADDRESS *my_address)
|
|||||||
case DATALINK_MSTP:
|
case DATALINK_MSTP:
|
||||||
dlmstp_get_my_address(my_address);
|
dlmstp_get_my_address(my_address);
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
#if defined(BACDL_BSC)
|
||||||
|
case DATALINK_BSC:
|
||||||
|
bsc_get_my_address(my_address);
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -333,6 +372,11 @@ void datalink_set_interface(char *ifname)
|
|||||||
case DATALINK_MSTP:
|
case DATALINK_MSTP:
|
||||||
(void)ifname;
|
(void)ifname;
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
#if defined(BACDL_BSC)
|
||||||
|
case DATALINK_BSC:
|
||||||
|
(void)ifname;
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -365,6 +409,11 @@ void datalink_maintenance_timer(uint16_t seconds)
|
|||||||
#if defined(BACDL_MSTP)
|
#if defined(BACDL_MSTP)
|
||||||
case DATALINK_MSTP:
|
case DATALINK_MSTP:
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
#if defined(BACDL_BSC)
|
||||||
|
case DATALINK_BSC:
|
||||||
|
bsc_maintenance_timer(seconds);
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -32,6 +32,11 @@
|
|||||||
#include "bacnet/basic/bbmd6/h_bbmd6.h"
|
#include "bacnet/basic/bbmd6/h_bbmd6.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(BACDL_BSC)
|
||||||
|
#include "bacnet/datalink/bsc/bsc-conf.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-datalink.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(BACDL_ETHERNET) && !defined(BACDL_MULTIPLE)
|
#if defined(BACDL_ETHERNET) && !defined(BACDL_MULTIPLE)
|
||||||
#define MAX_MPDU ETHERNET_MPDU_MAX
|
#define MAX_MPDU ETHERNET_MPDU_MAX
|
||||||
|
|
||||||
@@ -99,6 +104,17 @@ void routed_get_my_address(BACNET_ADDRESS *my_address);
|
|||||||
#define datalink_get_my_address bip6_get_my_address
|
#define datalink_get_my_address bip6_get_my_address
|
||||||
#define datalink_maintenance_timer(s) bvlc6_maintenance_timer(s)
|
#define datalink_maintenance_timer(s) bvlc6_maintenance_timer(s)
|
||||||
|
|
||||||
|
#elif defined(BACDL_BSC) && !defined(BACDL_MULTIPLE)
|
||||||
|
#define MAX_MPDU BVLC_SC_NPDU_SIZE_CONF
|
||||||
|
|
||||||
|
#define datalink_init bsc_init
|
||||||
|
#define datalink_send_pdu bsc_send_pdu
|
||||||
|
#define datalink_receive bsc_receive
|
||||||
|
#define datalink_cleanup bsc_cleanup
|
||||||
|
#define datalink_get_broadcast_address bsc_get_broadcast_address
|
||||||
|
#define datalink_get_my_address bsc_get_my_address
|
||||||
|
#define datalink_maintenance_timer(s) bsc_maintenance_timer(s)
|
||||||
|
|
||||||
#elif !defined(BACDL_TEST) /* Multiple, none or custom datalink */
|
#elif !defined(BACDL_TEST) /* Multiple, none or custom datalink */
|
||||||
#include "bacnet/npdu.h"
|
#include "bacnet/npdu.h"
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,14 @@
|
|||||||
#if (BACNET_PROTOCOL_REVISION >= 17)
|
#if (BACNET_PROTOCOL_REVISION >= 17)
|
||||||
#include "bacnet/basic/object/netport.h"
|
#include "bacnet/basic/object/netport.h"
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(BACDL_BSC)
|
||||||
|
#include "bacnet/basic/object/bacfile.h"
|
||||||
|
#include "bacnet/basic/object/sc_netport.h"
|
||||||
|
#include "bacnet/datalink/bsc/bvlc-sc.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-util.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-datalink.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-event.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
/** @file dlenv.c Initialize the DataLink configuration. */
|
/** @file dlenv.c Initialize the DataLink configuration. */
|
||||||
/* timer used to renew Foreign Device Registration */
|
/* timer used to renew Foreign Device Registration */
|
||||||
@@ -459,6 +467,179 @@ void dlenv_network_port_init(void)
|
|||||||
since they are already set */
|
since they are already set */
|
||||||
Network_Port_Changes_Pending_Set(instance, false);
|
Network_Port_Changes_Pending_Set(instance, false);
|
||||||
}
|
}
|
||||||
|
#elif defined(BACDL_BSC)
|
||||||
|
/**
|
||||||
|
* @brief Datalink network port object settings
|
||||||
|
* @param primary_hub_uri
|
||||||
|
* @param failover_hub_uri
|
||||||
|
* @param filename_ca_1_cert
|
||||||
|
* @param filename_ca_2_cert
|
||||||
|
* @param filename_cert
|
||||||
|
* @param filename_key
|
||||||
|
* @param direct_connect_port
|
||||||
|
* @param hub_function_port
|
||||||
|
* @param direct_connect_initiate
|
||||||
|
* @param direct_connect_accept_urls
|
||||||
|
*/
|
||||||
|
static void bacnet_secure_connect_network_port_init(
|
||||||
|
char *primary_hub_uri,
|
||||||
|
char *failover_hub_uri,
|
||||||
|
char *filename_ca_1_cert,
|
||||||
|
char *filename_ca_2_cert,
|
||||||
|
char *filename_cert,
|
||||||
|
char *filename_key,
|
||||||
|
char *direct_binding,
|
||||||
|
char *hub_binding,
|
||||||
|
char *direct_connect_initiate,
|
||||||
|
char *direct_connect_accept_urls)
|
||||||
|
{
|
||||||
|
const uint32_t instance = 1;
|
||||||
|
BACNET_SC_UUID uuid = { 0 };
|
||||||
|
BACNET_SC_VMAC_ADDRESS vmac = { 0 };
|
||||||
|
long seed;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
seed = (long)&instance;
|
||||||
|
srand((int)seed);
|
||||||
|
Network_Port_Object_Instance_Number_Set(0, instance);
|
||||||
|
Network_Port_Name_Set(instance, "BACnet/BSC Port");
|
||||||
|
Network_Port_Type_Set(instance, PORT_TYPE_BSC);
|
||||||
|
|
||||||
|
bsc_generate_random_uuid(&uuid);
|
||||||
|
Network_Port_SC_Local_UUID_Set(instance, (BACNET_UUID *)&uuid);
|
||||||
|
bsc_generate_random_vmac(&vmac);
|
||||||
|
Network_Port_MAC_Address_Set(instance, vmac.address, sizeof(vmac));
|
||||||
|
|
||||||
|
/* common NP data */
|
||||||
|
Network_Port_Reliability_Set(instance, RELIABILITY_NO_FAULT_DETECTED);
|
||||||
|
Network_Port_Out_Of_Service_Set(instance, false);
|
||||||
|
Network_Port_Quality_Set(instance, PORT_QUALITY_UNKNOWN);
|
||||||
|
Network_Port_APDU_Length_Set(instance, MAX_APDU);
|
||||||
|
Network_Port_Network_Number_Set(instance, 0);
|
||||||
|
|
||||||
|
/* SC parameters */
|
||||||
|
Network_Port_Max_BVLC_Length_Accepted_Set(instance, SC_NETPORT_BVLC_MAX);
|
||||||
|
Network_Port_Max_NPDU_Length_Accepted_Set(instance, SC_NETPORT_NPDU_MAX);
|
||||||
|
Network_Port_SC_Connect_Wait_Timeout_Set(
|
||||||
|
instance, SC_NETPORT_CONNECT_TIMEOUT);
|
||||||
|
Network_Port_SC_Heartbeat_Timeout_Set(
|
||||||
|
instance, SC_NETPORT_HEARTBEAT_TIMEOUT);
|
||||||
|
Network_Port_SC_Disconnect_Wait_Timeout_Set(
|
||||||
|
instance, SC_NETPORT_DISCONNECT_TIMEOUT);
|
||||||
|
Network_Port_SC_Maximum_Reconnect_Time_Set(
|
||||||
|
instance, SC_NETPORT_RECONNECT_TIME);
|
||||||
|
|
||||||
|
if (filename_ca_1_cert == NULL) {
|
||||||
|
fprintf(stderr, "BACNET_SC_ISSUER_1_CERTIFICATE_FILE must be set\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bacfile_create(BSC_ISSUER_CERTIFICATE_FILE_1_INSTANCE);
|
||||||
|
bacfile_pathname_set(
|
||||||
|
BSC_ISSUER_CERTIFICATE_FILE_1_INSTANCE, filename_ca_1_cert);
|
||||||
|
Network_Port_Issuer_Certificate_File_Set(
|
||||||
|
instance, 0, BSC_ISSUER_CERTIFICATE_FILE_1_INSTANCE);
|
||||||
|
|
||||||
|
if (filename_ca_2_cert) {
|
||||||
|
bacfile_create(BSC_ISSUER_CERTIFICATE_FILE_2_INSTANCE);
|
||||||
|
bacfile_pathname_set(
|
||||||
|
BSC_ISSUER_CERTIFICATE_FILE_2_INSTANCE, filename_ca_2_cert);
|
||||||
|
Network_Port_Issuer_Certificate_File_Set(
|
||||||
|
instance, 1, BSC_ISSUER_CERTIFICATE_FILE_2_INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filename_cert == NULL) {
|
||||||
|
fprintf(stderr, "BACNET_SC_OPERATIONAL_CERTIFICATE_FILE must be set\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bacfile_create(BSC_OPERATIONAL_CERTIFICATE_FILE_INSTANCE);
|
||||||
|
bacfile_pathname_set(
|
||||||
|
BSC_OPERATIONAL_CERTIFICATE_FILE_INSTANCE, filename_cert);
|
||||||
|
Network_Port_Operational_Certificate_File_Set(
|
||||||
|
instance, BSC_OPERATIONAL_CERTIFICATE_FILE_INSTANCE);
|
||||||
|
|
||||||
|
if (filename_key == NULL) {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"BACNET_SC_OPERATIONAL_CERTIFICATE_PRIVATE_KEY_FILE must be set\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bacfile_create(BSC_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE);
|
||||||
|
bacfile_pathname_set(
|
||||||
|
BSC_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE, filename_key);
|
||||||
|
Network_Port_Certificate_Key_File_Set(
|
||||||
|
instance, BSC_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE);
|
||||||
|
|
||||||
|
if ((primary_hub_uri == NULL) && (failover_hub_uri == NULL) &&
|
||||||
|
(direct_binding == NULL) && (hub_binding == NULL)) {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"At least must be set:\n"
|
||||||
|
"BACNET_SC_HUB_FUNCTION_BINDING for HUB or\n"
|
||||||
|
"BACNET_SC_PRIMARY_HUB_URI and BACNET_SC_FAILOVER_HUB_URI for node "
|
||||||
|
"or\n"
|
||||||
|
"BACNET_SC_DIRECT_CONNECT_BINDING for direct connect.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Network_Port_SC_Primary_Hub_URI_Set(instance, primary_hub_uri);
|
||||||
|
Network_Port_SC_Failover_Hub_URI_Set(instance, failover_hub_uri);
|
||||||
|
|
||||||
|
Network_Port_SC_Direct_Connect_Binding_Set(instance, direct_binding);
|
||||||
|
Network_Port_SC_Direct_Connect_Accept_Enable_Set(
|
||||||
|
instance, direct_binding != NULL);
|
||||||
|
|
||||||
|
c = direct_connect_initiate ? direct_connect_initiate[0] : '0';
|
||||||
|
if ((c != '0') && (c != 'n') && (c != 'N')) {
|
||||||
|
Network_Port_SC_Direct_Connect_Initiate_Enable_Set(instance, true);
|
||||||
|
} else {
|
||||||
|
Network_Port_SC_Direct_Connect_Initiate_Enable_Set(instance, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Network_Port_SC_Direct_Connect_Accept_URIs_Set(
|
||||||
|
instance, direct_connect_accept_urls);
|
||||||
|
|
||||||
|
/* HUB parameters */
|
||||||
|
Network_Port_SC_Hub_Function_Binding_Set(instance, hub_binding);
|
||||||
|
Network_Port_SC_Hub_Function_Enable_Set(instance, hub_binding != NULL);
|
||||||
|
|
||||||
|
/* last thing - clear pending changes - we don't want to set these
|
||||||
|
since they are already set */
|
||||||
|
Network_Port_Changes_Pending_Set(instance, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool dlenv_hub_connection_status_check(void)
|
||||||
|
{
|
||||||
|
uint32_t instance = Network_Port_Index_To_Instance(0);
|
||||||
|
BACNET_SC_HUB_CONNECTION_STATUS *status;
|
||||||
|
|
||||||
|
status = Network_Port_SC_Primary_Hub_Connection_Status(instance);
|
||||||
|
if (status && status->State == BACNET_SC_CONNECTION_STATE_CONNECTED) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = Network_Port_SC_Failover_Hub_Connection_Status(instance);
|
||||||
|
if (status && status->State == BACNET_SC_CONNECTION_STATE_CONNECTED) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Datalink network port object settings for BACnet/SC
|
||||||
|
*/
|
||||||
|
void dlenv_network_port_init(void)
|
||||||
|
{
|
||||||
|
/* if a user has configured BACnet/SC port with primary hub URI, */
|
||||||
|
/* wait for a establishin of a connection to BACnet/SC hub at first */
|
||||||
|
/* to reduce possibility of packet losses. */
|
||||||
|
if (Network_Port_SC_Primary_Hub_URI_char(1)) {
|
||||||
|
while (!dlenv_hub_connection_status_check()) {
|
||||||
|
bsc_wait(1);
|
||||||
|
bsc_maintenance_timer(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
/**
|
/**
|
||||||
* Datalink network port object settings
|
* Datalink network port object settings
|
||||||
@@ -548,6 +729,24 @@ void dlenv_maintenance_timer(uint16_t elapsed_seconds)
|
|||||||
* - BACNET_BIP6_PORT - UDP/IP port number (0..65534) used for BACnet/IPv6
|
* - BACNET_BIP6_PORT - UDP/IP port number (0..65534) used for BACnet/IPv6
|
||||||
* communications. Default is 47808 (0xBAC0).
|
* communications. Default is 47808 (0xBAC0).
|
||||||
* - BACNET_BIP6_BROADCAST - FF05::BAC0 or FF02::BAC0 or ...
|
* - BACNET_BIP6_BROADCAST - FF05::BAC0 or FF02::BAC0 or ...
|
||||||
|
* - BACDL_BSC: (BACnet Secure Connect)
|
||||||
|
* - BACNET_SC_PRIMARY_HUB_URI
|
||||||
|
* - BACNET_SC_FAILOVER_HUB_URI
|
||||||
|
* - BACNET_SC_ISSUER_1_CERTIFICATE_FILE
|
||||||
|
* - BACNET_SC_ISSUER_2_CERTIFICATE_FILE
|
||||||
|
* - BACNET_SC_OPERATIONAL_CERTIFICATE_FILE
|
||||||
|
* - BACNET_SC_OPERATIONAL_CERTIFICATE_PRIVATE_KEY_FILE
|
||||||
|
* - BACNET_SC_DIRECT_CONNECT_BINDING - pair: interface name (optional) and
|
||||||
|
* TCP/IP port number (0..65534), like "50000" (port only) or
|
||||||
|
* "eth0:50000"(both)
|
||||||
|
* - BACNET_SC_HUB_FUNCTION_BINDING - pair: interface name (optional) and
|
||||||
|
* TCP/IP port number (0..65534), like "50000" (port only) or
|
||||||
|
* "eth0:50000"(both)
|
||||||
|
* - BACNET_SC_DIRECT_CONNECT_INITIATE - if true equal "1", "y", "Y",
|
||||||
|
* otherwise false
|
||||||
|
* - BACNET_SC_DIRECT_CONNECT_ACCEPT_URLS - list of direct connect accept URLs
|
||||||
|
* separated by a space character, e.g.
|
||||||
|
* "wss://192.0.0.1:40000 wss://192.0.0.2:6666"
|
||||||
*/
|
*/
|
||||||
void dlenv_init(void)
|
void dlenv_init(void)
|
||||||
{
|
{
|
||||||
@@ -574,6 +773,8 @@ void dlenv_init(void)
|
|||||||
datalink_set("ethernet");
|
datalink_set("ethernet");
|
||||||
#elif defined(BACDL_ARCNET)
|
#elif defined(BACDL_ARCNET)
|
||||||
datalink_set("arcnet");
|
datalink_set("arcnet");
|
||||||
|
#elif defined(BACDL_BSC)
|
||||||
|
datalink_set("bsc");
|
||||||
#else
|
#else
|
||||||
datalink_set("none");
|
datalink_set("none");
|
||||||
#endif
|
#endif
|
||||||
@@ -665,6 +866,35 @@ void dlenv_init(void)
|
|||||||
} else {
|
} else {
|
||||||
dlmstp_set_mac_address(127);
|
dlmstp_set_mac_address(127);
|
||||||
}
|
}
|
||||||
|
#elif defined(BACDL_BSC)
|
||||||
|
char *primary_hub_uri;
|
||||||
|
char *failover_hub_uri;
|
||||||
|
char *filename_ca_1_cert;
|
||||||
|
char *filename_ca_2_cert;
|
||||||
|
char *filename_cert;
|
||||||
|
char *filename_key;
|
||||||
|
char *direct_binding;
|
||||||
|
char *hub_binding;
|
||||||
|
char *direct_connect_initiate;
|
||||||
|
char *direct_connect_accept_urls;
|
||||||
|
|
||||||
|
primary_hub_uri = getenv("BACNET_SC_PRIMARY_HUB_URI");
|
||||||
|
failover_hub_uri = getenv("BACNET_SC_FAILOVER_HUB_URI");
|
||||||
|
filename_ca_1_cert = getenv("BACNET_SC_ISSUER_1_CERTIFICATE_FILE");
|
||||||
|
filename_ca_2_cert = getenv("BACNET_SC_ISSUER_2_CERTIFICATE_FILE");
|
||||||
|
filename_cert = getenv("BACNET_SC_OPERATIONAL_CERTIFICATE_FILE");
|
||||||
|
filename_key = getenv("BACNET_SC_OPERATIONAL_CERTIFICATE_PRIVATE_KEY_FILE");
|
||||||
|
direct_binding = getenv("BACNET_SC_DIRECT_CONNECT_BINDING");
|
||||||
|
hub_binding = getenv("BACNET_SC_HUB_FUNCTION_BINDING");
|
||||||
|
direct_connect_initiate = getenv("BACNET_SC_DIRECT_CONNECT_INITIATE");
|
||||||
|
direct_connect_accept_urls = getenv("BACNET_SC_DIRECT_CONNECT_ACCEPT_URLS");
|
||||||
|
bacnet_secure_connect_network_port_init(
|
||||||
|
primary_hub_uri, failover_hub_uri, filename_ca_1_cert,
|
||||||
|
filename_ca_2_cert, filename_cert, filename_key, direct_binding,
|
||||||
|
hub_binding, direct_connect_initiate, direct_connect_accept_urls);
|
||||||
|
if (!bsc_cert_files_check()) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
pEnv = getenv("BACNET_APDU_TIMEOUT");
|
pEnv = getenv("BACNET_APDU_TIMEOUT");
|
||||||
if (pEnv) {
|
if (pEnv) {
|
||||||
|
|||||||
@@ -391,6 +391,8 @@ static const int Properties_BACnetARRAY[] = {
|
|||||||
PROP_WEEKLY_SCHEDULE,
|
PROP_WEEKLY_SCHEDULE,
|
||||||
PROP_EXCEPTION_SCHEDULE,
|
PROP_EXCEPTION_SCHEDULE,
|
||||||
PROP_TAGS,
|
PROP_TAGS,
|
||||||
|
PROP_ISSUER_CERTIFICATE_FILES,
|
||||||
|
PROP_SC_HUB_FUNCTION_ACCEPT_URIS,
|
||||||
-1
|
-1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,175 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief BACnet Secure Connect encode and decode functions.
|
||||||
|
* @author Mikhail Antropov <michail.antropov@dsr-corporation.com>
|
||||||
|
* @date 2023
|
||||||
|
* @copyright SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
#ifndef BACNET_SECURE_CONNECT_H
|
||||||
|
#define BACNET_SECURE_CONNECT_H
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
/* BACnet Stack defines - first */
|
||||||
|
#include "bacnet/bacdef.h"
|
||||||
|
/* BACnet Stack API */
|
||||||
|
#include "bacnet/datetime.h"
|
||||||
|
#include "bacnet/datalink/bsc/bsc-conf.h"
|
||||||
|
#include "bacnet/datalink/bsc/bvlc-sc.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
/* Support
|
||||||
|
- SC_Hub_Function_Connection_Status
|
||||||
|
- SC_Direct_Connect_Connection_Status
|
||||||
|
- SC_FailedConnectionRequests
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct BACnetUUID_T_ {
|
||||||
|
union {
|
||||||
|
struct guid {
|
||||||
|
uint32_t time_low;
|
||||||
|
uint16_t time_mid;
|
||||||
|
uint16_t time_hi_and_version;
|
||||||
|
uint8_t clock_seq_and_node[8];
|
||||||
|
} guid;
|
||||||
|
uint8_t uuid128[16];
|
||||||
|
} uuid;
|
||||||
|
} BACNET_UUID;
|
||||||
|
|
||||||
|
#define BACNET_ERROR_STRING_LENGTH BSC_CONF_WEBSOCKET_ERR_DESC_STR_MAX_LEN
|
||||||
|
#define BACNET_URI_LENGTH BSC_CONF_WSURL_MAX_LEN
|
||||||
|
#define BACNET_PEER_VMAC_LENGTH BVLC_SC_VMAC_SIZE
|
||||||
|
|
||||||
|
enum BACNET_HOST_N_PORT_TYPE {
|
||||||
|
BACNET_HOST_N_PORT_IP = 1,
|
||||||
|
BACNET_HOST_N_PORT_HOST = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct BACnetHostNPort_data_T {
|
||||||
|
uint8_t type;
|
||||||
|
char host[BACNET_URI_LENGTH];
|
||||||
|
uint16_t port;
|
||||||
|
} BACNET_HOST_N_PORT_DATA;
|
||||||
|
|
||||||
|
typedef struct BACnetSCHubConnectionStatus_T {
|
||||||
|
BACNET_SC_CONNECTION_STATE State; /*index = 0 */
|
||||||
|
BACNET_DATE_TIME Connect_Timestamp; /*index = 1 */
|
||||||
|
BACNET_DATE_TIME Disconnect_Timestamp; /*index = 2 */
|
||||||
|
/* optionals - use ERROR_CODE_DEFAULT for default value */
|
||||||
|
BACNET_ERROR_CODE Error; /* index = 3 */
|
||||||
|
char Error_Details[BACNET_ERROR_STRING_LENGTH]; /* index = 4 */
|
||||||
|
} BACNET_SC_HUB_CONNECTION_STATUS;
|
||||||
|
|
||||||
|
typedef struct BACnetSCHubFunctionConnectionStatus_T {
|
||||||
|
BACNET_SC_CONNECTION_STATE State;
|
||||||
|
BACNET_DATE_TIME Connect_Timestamp;
|
||||||
|
BACNET_DATE_TIME Disconnect_Timestamp;
|
||||||
|
BACNET_HOST_N_PORT_DATA Peer_Address;
|
||||||
|
uint8_t Peer_VMAC[BACNET_PEER_VMAC_LENGTH];
|
||||||
|
BACNET_UUID Peer_UUID;
|
||||||
|
BACNET_ERROR_CODE Error;
|
||||||
|
char Error_Details[BACNET_ERROR_STRING_LENGTH];
|
||||||
|
} BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS;
|
||||||
|
|
||||||
|
typedef struct BACnetSCFailedConnectionRequest_T {
|
||||||
|
BACNET_DATE_TIME Timestamp;
|
||||||
|
BACNET_HOST_N_PORT_DATA Peer_Address;
|
||||||
|
uint8_t Peer_VMAC[BACNET_PEER_VMAC_LENGTH];
|
||||||
|
BACNET_UUID Peer_UUID;
|
||||||
|
BACNET_ERROR_CODE Error;
|
||||||
|
char Error_Details[BACNET_ERROR_STRING_LENGTH];
|
||||||
|
} BACNET_SC_FAILED_CONNECTION_REQUEST;
|
||||||
|
|
||||||
|
typedef enum BACnetRouterStatus {
|
||||||
|
BACNET_ROUTER_STATUS_AVAILABLE = 0,
|
||||||
|
BACNET_ROUTER_STATUS_BUSY = 1,
|
||||||
|
BACNET_ROUTER_STATUS_DISCONNECTED = 2,
|
||||||
|
BACNET_ROUTER_STATUS_MAX = 2
|
||||||
|
} BACNET_ROUTER_STATUS;
|
||||||
|
|
||||||
|
typedef struct BACnetRouterEntry_T {
|
||||||
|
uint16_t Network_Number;
|
||||||
|
uint8_t Mac_Address[6];
|
||||||
|
BACNET_ROUTER_STATUS Status;
|
||||||
|
uint8_t Performance_Index;
|
||||||
|
} BACNET_ROUTER_ENTRY;
|
||||||
|
|
||||||
|
typedef struct BACnetSCDirectConnectionStatus_T {
|
||||||
|
char URI[BACNET_URI_LENGTH];
|
||||||
|
BACNET_SC_CONNECTION_STATE State;
|
||||||
|
BACNET_DATE_TIME Connect_Timestamp;
|
||||||
|
BACNET_DATE_TIME Disconnect_Timestamp;
|
||||||
|
BACNET_HOST_N_PORT_DATA Peer_Address;
|
||||||
|
uint8_t Peer_VMAC[BACNET_PEER_VMAC_LENGTH];
|
||||||
|
BACNET_UUID Peer_UUID;
|
||||||
|
BACNET_ERROR_CODE Error;
|
||||||
|
char Error_Details[BACNET_ERROR_STRING_LENGTH];
|
||||||
|
} BACNET_SC_DIRECT_CONNECTION_STATUS;
|
||||||
|
|
||||||
|
/* */
|
||||||
|
/* Encode / decode */
|
||||||
|
/* */
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int bacapp_encode_SCHubConnection(
|
||||||
|
uint8_t *apdu, const BACNET_SC_HUB_CONNECTION_STATUS *value);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int bacapp_decode_SCHubConnection(
|
||||||
|
const uint8_t *apdu,
|
||||||
|
size_t apdu_size,
|
||||||
|
BACNET_SC_HUB_CONNECTION_STATUS *value);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int bacapp_encode_SCHubFunctionConnection(
|
||||||
|
uint8_t *apdu, const BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *value);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int bacapp_decode_SCHubFunctionConnection(
|
||||||
|
const uint8_t *apdu,
|
||||||
|
size_t apdu_size,
|
||||||
|
BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *value);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int bacapp_encode_SCFailedConnectionRequest(
|
||||||
|
uint8_t *apdu, const BACNET_SC_FAILED_CONNECTION_REQUEST *value);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int bacapp_decode_SCFailedConnectionRequest(
|
||||||
|
const uint8_t *apdu,
|
||||||
|
size_t apdu_size,
|
||||||
|
BACNET_SC_FAILED_CONNECTION_REQUEST *value);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int bacapp_encode_RouterEntry(uint8_t *apdu, const BACNET_ROUTER_ENTRY *value);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int bacapp_decode_RouterEntry(
|
||||||
|
const uint8_t *apdu, size_t apdu_size, BACNET_ROUTER_ENTRY *value);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int bacapp_encode_SCDirectConnection(
|
||||||
|
uint8_t *apdu, const BACNET_SC_DIRECT_CONNECTION_STATUS *value);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int bacapp_decode_SCDirectConnection(
|
||||||
|
const uint8_t *apdu,
|
||||||
|
size_t apdu_size,
|
||||||
|
BACNET_SC_DIRECT_CONNECTION_STATUS *value);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int bacapp_snprintf_SCFailedConnectionRequest(
|
||||||
|
char *str, size_t str_len, const BACNET_SC_FAILED_CONNECTION_REQUEST *req);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int bacapp_snprintf_SCHubFunctionConnection(
|
||||||
|
char *str,
|
||||||
|
size_t str_len,
|
||||||
|
const BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *st);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int bacapp_snprintf_SCDirectConnection(
|
||||||
|
char *str, size_t str_len, const BACNET_SC_DIRECT_CONNECTION_STATUS *st);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int bacapp_snprintf_SCHubConnection(
|
||||||
|
char *str, size_t str_len, const BACNET_SC_HUB_CONNECTION_STATUS *st);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
#endif
|
||||||
@@ -109,6 +109,7 @@ list(APPEND testdirs
|
|||||||
bacnet/reject
|
bacnet/reject
|
||||||
bacnet/rp
|
bacnet/rp
|
||||||
bacnet/rpm
|
bacnet/rpm
|
||||||
|
bacnet/secure_connect
|
||||||
bacnet/specialevent
|
bacnet/specialevent
|
||||||
bacnet/timestamp
|
bacnet/timestamp
|
||||||
bacnet/timesync
|
bacnet/timesync
|
||||||
@@ -187,6 +188,30 @@ list(APPEND testdirs
|
|||||||
bacnet/datalink/mstp
|
bacnet/datalink/mstp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ports tests
|
||||||
|
if(ZEPHYR_BASE)
|
||||||
|
message(FATAL_ERROR "ZEPHYR_BASE env variable defined. Use zephyr/CMakeLists.txt for Zephyr build")
|
||||||
|
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
|
message(STATUS "Added ports specific tests for linux")
|
||||||
|
|
||||||
|
list(APPEND testdirs
|
||||||
|
ports/linux/bsc_event
|
||||||
|
)
|
||||||
|
|
||||||
|
elseif(WIN32)
|
||||||
|
message(STATUS "Added ports specific tests for win32")
|
||||||
|
|
||||||
|
list(APPEND testdirs
|
||||||
|
ports/win32/bsc_event
|
||||||
|
)
|
||||||
|
|
||||||
|
elseif(APPLE)
|
||||||
|
message(STATUS "Added ports specific tests for APPLE")
|
||||||
|
list(APPEND testdirs
|
||||||
|
ports/bsd/bsc_event
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
enable_testing()
|
enable_testing()
|
||||||
foreach(testdir IN ITEMS ${testdirs})
|
foreach(testdir IN ITEMS ${testdirs})
|
||||||
get_filename_component(basename ${testdir} NAME)
|
get_filename_component(basename ${testdir} NAME)
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/calendar_entry.c
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
${SRC_DIR}/bacnet/special_event.c
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
${SRC_DIR}/bacnet/channel_value.c
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
# Test and test library files
|
# Test and test library files
|
||||||
./src/main.c
|
./src/main.c
|
||||||
${ZTST_DIR}/ztest_mock.c
|
${ZTST_DIR}/ztest_mock.c
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/calendar_entry.c
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
${SRC_DIR}/bacnet/special_event.c
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
${SRC_DIR}/bacnet/channel_value.c
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
# Test and test library files
|
# Test and test library files
|
||||||
./src/main.c
|
./src/main.c
|
||||||
${ZTST_DIR}/ztest_mock.c
|
${ZTST_DIR}/ztest_mock.c
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/calendar_entry.c
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
${SRC_DIR}/bacnet/special_event.c
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
${SRC_DIR}/bacnet/channel_value.c
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
# Test and test library files
|
# Test and test library files
|
||||||
./src/main.c
|
./src/main.c
|
||||||
${ZTST_DIR}/ztest_mock.c
|
${ZTST_DIR}/ztest_mock.c
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/calendar_entry.c
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
${SRC_DIR}/bacnet/special_event.c
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
${SRC_DIR}/bacnet/channel_value.c
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
# Test and test library files
|
# Test and test library files
|
||||||
./src/main.c
|
./src/main.c
|
||||||
${ZTST_DIR}/ztest_mock.c
|
${ZTST_DIR}/ztest_mock.c
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/calendar_entry.c
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
${SRC_DIR}/bacnet/special_event.c
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
${SRC_DIR}/bacnet/channel_value.c
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
# Test and test library files
|
# Test and test library files
|
||||||
./src/main.c
|
./src/main.c
|
||||||
${ZTST_DIR}/ztest_mock.c
|
${ZTST_DIR}/ztest_mock.c
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/calendar_entry.c
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
${SRC_DIR}/bacnet/special_event.c
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
${SRC_DIR}/bacnet/channel_value.c
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
# Test and test library files
|
# Test and test library files
|
||||||
./src/main.c
|
./src/main.c
|
||||||
${ZTST_DIR}/ztest_mock.c
|
${ZTST_DIR}/ztest_mock.c
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/calendar_entry.c
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
${SRC_DIR}/bacnet/special_event.c
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
${SRC_DIR}/bacnet/channel_value.c
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
# Test and test library files
|
# Test and test library files
|
||||||
./src/main.c
|
./src/main.c
|
||||||
${ZTST_DIR}/ztest_mock.c
|
${ZTST_DIR}/ztest_mock.c
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/calendar_entry.c
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
${SRC_DIR}/bacnet/special_event.c
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
${SRC_DIR}/bacnet/channel_value.c
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
# Test and test library files
|
# Test and test library files
|
||||||
./src/main.c
|
./src/main.c
|
||||||
${ZTST_DIR}/ztest_mock.c
|
${ZTST_DIR}/ztest_mock.c
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/calendar_entry.c
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
${SRC_DIR}/bacnet/special_event.c
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
${SRC_DIR}/bacnet/channel_value.c
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
# Test and test library files
|
# Test and test library files
|
||||||
./src/main.c
|
./src/main.c
|
||||||
${ZTST_DIR}/ztest_mock.c
|
${ZTST_DIR}/ztest_mock.c
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/calendar_entry.c
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
${SRC_DIR}/bacnet/special_event.c
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
${SRC_DIR}/bacnet/channel_value.c
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
# Test and test library files
|
# Test and test library files
|
||||||
./src/main.c
|
./src/main.c
|
||||||
${TST_DIR}/bacnet/basic/object/test/property_test.c
|
${TST_DIR}/bacnet/basic/object/test/property_test.c
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/calendar_entry.c
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
${SRC_DIR}/bacnet/special_event.c
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
${SRC_DIR}/bacnet/channel_value.c
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
# Test and test library files
|
# Test and test library files
|
||||||
./src/main.c
|
./src/main.c
|
||||||
${ZTST_DIR}/ztest_mock.c
|
${ZTST_DIR}/ztest_mock.c
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/calendar_entry.c
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
${SRC_DIR}/bacnet/special_event.c
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
${SRC_DIR}/bacnet/channel_value.c
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
# Test and test library files
|
# Test and test library files
|
||||||
./src/main.c
|
./src/main.c
|
||||||
${ZTST_DIR}/ztest_mock.c
|
${ZTST_DIR}/ztest_mock.c
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/calendar_entry.c
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
${SRC_DIR}/bacnet/special_event.c
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
${SRC_DIR}/bacnet/channel_value.c
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
||||||
${SRC_DIR}/bacnet/basic/sys/days.c
|
${SRC_DIR}/bacnet/basic/sys/days.c
|
||||||
${SRC_DIR}/bacnet/basic/sys/debug.c
|
${SRC_DIR}/bacnet/basic/sys/debug.c
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/calendar_entry.c
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
${SRC_DIR}/bacnet/special_event.c
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
${SRC_DIR}/bacnet/channel_value.c
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
||||||
${SRC_DIR}/bacnet/basic/sys/days.c
|
${SRC_DIR}/bacnet/basic/sys/days.c
|
||||||
${SRC_DIR}/bacnet/basic/sys/keylist.c
|
${SRC_DIR}/bacnet/basic/sys/keylist.c
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/calendar_entry.c
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
${SRC_DIR}/bacnet/special_event.c
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
${SRC_DIR}/bacnet/channel_value.c
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
||||||
${SRC_DIR}/bacnet/basic/sys/days.c
|
${SRC_DIR}/bacnet/basic/sys/days.c
|
||||||
${SRC_DIR}/bacnet/basic/sys/debug.c
|
${SRC_DIR}/bacnet/basic/sys/debug.c
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/calendar_entry.c
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
${SRC_DIR}/bacnet/special_event.c
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
${SRC_DIR}/bacnet/channel_value.c
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
# Test and test library files
|
# Test and test library files
|
||||||
./src/main.c
|
./src/main.c
|
||||||
${TST_DIR}/bacnet/basic/object/test/apdu_mock.c
|
${TST_DIR}/bacnet/basic/object/test/apdu_mock.c
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ static void test_BACnet_File_Object(void)
|
|||||||
required_property++;
|
required_property++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bacfile_cleanup();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/calendar_entry.c
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
${SRC_DIR}/bacnet/special_event.c
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
${SRC_DIR}/bacnet/channel_value.c
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
||||||
${SRC_DIR}/bacnet/basic/sys/days.c
|
${SRC_DIR}/bacnet/basic/sys/days.c
|
||||||
${SRC_DIR}/bacnet/basic/sys/debug.c
|
${SRC_DIR}/bacnet/basic/sys/debug.c
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/calendar_entry.c
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
${SRC_DIR}/bacnet/special_event.c
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
${SRC_DIR}/bacnet/channel_value.c
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
# Test and test library files
|
# Test and test library files
|
||||||
./src/main.c
|
./src/main.c
|
||||||
${TST_DIR}/bacnet/basic/object/test/property_test.c
|
${TST_DIR}/bacnet/basic/object/test/property_test.c
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/calendar_entry.c
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
${SRC_DIR}/bacnet/special_event.c
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
${SRC_DIR}/bacnet/channel_value.c
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
# Test and test library files
|
# Test and test library files
|
||||||
./src/main.c
|
./src/main.c
|
||||||
${TST_DIR}/bacnet/basic/object/test/device_mock.c
|
${TST_DIR}/bacnet/basic/object/test/device_mock.c
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/calendar_entry.c
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
${SRC_DIR}/bacnet/special_event.c
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
${SRC_DIR}/bacnet/channel_value.c
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
||||||
${SRC_DIR}/bacnet/basic/sys/days.c
|
${SRC_DIR}/bacnet/basic/sys/days.c
|
||||||
${SRC_DIR}/bacnet/basic/sys/debug.c
|
${SRC_DIR}/bacnet/basic/sys/debug.c
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/calendar_entry.c
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
${SRC_DIR}/bacnet/special_event.c
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
${SRC_DIR}/bacnet/channel_value.c
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
||||||
${SRC_DIR}/bacnet/basic/sys/days.c
|
${SRC_DIR}/bacnet/basic/sys/days.c
|
||||||
${SRC_DIR}/bacnet/basic/sys/debug.c
|
${SRC_DIR}/bacnet/basic/sys/debug.c
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/calendar_entry.c
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
${SRC_DIR}/bacnet/special_event.c
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
${SRC_DIR}/bacnet/channel_value.c
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
${SRC_DIR}/bacnet/bactimevalue.c
|
${SRC_DIR}/bacnet/bactimevalue.c
|
||||||
${SRC_DIR}/bacnet/dailyschedule.c
|
${SRC_DIR}/bacnet/dailyschedule.c
|
||||||
# Test and test library files
|
# Test and test library files
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/calendar_entry.c
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
${SRC_DIR}/bacnet/special_event.c
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
${SRC_DIR}/bacnet/channel_value.c
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
# Test and test library files
|
# Test and test library files
|
||||||
./src/main.c
|
./src/main.c
|
||||||
${TST_DIR}/bacnet/basic/object/test/property_test.c
|
${TST_DIR}/bacnet/basic/object/test/property_test.c
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/calendar_entry.c
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
${SRC_DIR}/bacnet/special_event.c
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
${SRC_DIR}/bacnet/channel_value.c
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
# Test and test library files
|
# Test and test library files
|
||||||
./src/main.c
|
./src/main.c
|
||||||
${TST_DIR}/bacnet/basic/object/test/device_mock.c
|
${TST_DIR}/bacnet/basic/object/test/device_mock.c
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/calendar_entry.c
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
${SRC_DIR}/bacnet/special_event.c
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
${SRC_DIR}/bacnet/channel_value.c
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
# Test and test library files
|
# Test and test library files
|
||||||
./src/main.c
|
./src/main.c
|
||||||
${TST_DIR}/bacnet/basic/object/test/device_mock.c
|
${TST_DIR}/bacnet/basic/object/test/device_mock.c
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user