32 Commits
Author SHA1 Message Date
7iwind b3578b0b53 Merge pull request 'develop' (#20) from develop into main
Release / release (push) Successful in 10s
Reviewed-on: #20
2026-07-26 22:41:12 +08:00
7iwind 6144b93e10 Merge pull request 'feat: 变更架构为广播,发送到所有绑定的端口' (#19) from feature/route into develop
Reviewed-on: #19
2026-07-26 22:40:15 +08:00
7iwind e8ee92110b feat: 变更架构为广播,发送到所有绑定的端口 2026-07-26 22:29:47 +08:00
7iwind 714281dbf1 Merge pull request 'develop' (#18) from develop into main
Release / release (push) Successful in 8s
Reviewed-on: #18
2026-07-26 15:07:38 +08:00
7iwind 9bce4b12cf Merge pull request 'fix: 优化打包后的目录结构,便于引入' (#17) from feature/route into develop
Reviewed-on: #17
2026-07-26 15:07:21 +08:00
7iwind e3c40c822b fix: 优化打包后的目录结构,便于引入 2026-07-26 15:06:36 +08:00
7iwind f6684bebc9 Merge pull request 'develop' (#16) from develop into main
Release / release (push) Failing after 18s
Reviewed-on: #16
2026-07-26 14:54:14 +08:00
7iwind 924c33fedf Merge pull request 'feature/route' (#15) from feature/route into develop
Reviewed-on: #15
2026-07-26 14:53:57 +08:00
7iwind 89c4e770d5 fix: 优化打包后的目录结构,便于引入 2026-07-26 14:53:34 +08:00
7iwind 1027d47e04 Merge branch 'develop' into feature/route 2026-07-26 14:47:20 +08:00
7iwind 323b6bd464 Merge pull request 'fix: 优化打包后的目录结构,便于引入' (#14) from feature/route into main
Release / release (push) Failing after 20s
Reviewed-on: #14
2026-07-26 14:45:05 +08:00
7iwind f5cb361ffe fix: 优化打包后的目录结构,便于引入 2026-07-26 14:44:35 +08:00
7iwind 29c49057b4 Merge pull request 'develop' (#12) from develop into main
Release / release (push) Successful in 19s
Reviewed-on: #12
2026-07-26 13:30:36 +08:00
7iwind 03039bd493 Merge pull request 'fix: 去掉并行构建,gitea不支持' (#11) from feature/route into develop
Reviewed-on: #11
2026-07-26 13:30:10 +08:00
7iwind 0d06f797f8 fix: 去掉并行构建,gitea不支持 2026-07-26 13:29:38 +08:00
7iwind 0e0047b946 Merge pull request 'Merge pull request 'feature/route' (#9) from feature/route into develop' (#10) from develop into main
Release / build (map[build_testing:OFF cmake_extra: name:linux-arm64 toolchain:-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-linux-arm64.cmake]) (push) Failing after 8s
Release / build (map[build_testing:OFF cmake_extra: name:linux-armhf toolchain:-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-linux-armhf.cmake]) (push) Failing after 7s
Release / build (map[build_testing:OFF cmake_extra:-DBUILD_EXAMPLES=OFF -DBUILD_SHARED=OFF name:baremetal-arm32 toolchain:-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-baremetal-arm32.cmake]) (push) Failing after 6s
Release / build (map[build_testing:OFF cmake_extra:-DBUILD_EXAMPLES=OFF name:windows-amd64 toolchain:-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-windows-amd64.cmake]) (push) Failing after 7s
Release / build (map[build_testing:ON cmake_extra: name:linux-amd64 toolchain:]) (push) Failing after 6s
Release / release (push) Has been skipped
Reviewed-on: #10
2026-07-26 13:26:14 +08:00
7iwind 0779e9171f Merge pull request 'feature/route' (#9) from feature/route into develop
Reviewed-on: #9
2026-07-26 13:25:54 +08:00
7iwind 18bc4eb08f Merge pull request 'fix: 修复 bash -e 下,for 循环中 [ -f ... ] && cp ... 里 [ 返回 1 时会触发 set -e 退出脚本。同时 test 需要实际开启 BUILD_TESTING。' (#8) from feature/route into main
Release / build (map[build_testing:OFF cmake_extra: name:linux-arm64 toolchain:-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-linux-arm64.cmake]) (push) Failing after 7s
Release / build (map[build_testing:OFF cmake_extra: name:linux-armhf toolchain:-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-linux-armhf.cmake]) (push) Failing after 6s
Release / build (map[build_testing:OFF cmake_extra:-DBUILD_EXAMPLES=OFF -DBUILD_SHARED=OFF name:baremetal-arm32 toolchain:-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-baremetal-arm32.cmake]) (push) Failing after 6s
Release / build (map[build_testing:OFF cmake_extra:-DBUILD_EXAMPLES=OFF name:windows-amd64 toolchain:-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-windows-amd64.cmake]) (push) Failing after 7s
Release / build (map[build_testing:ON cmake_extra: name:linux-amd64 toolchain:]) (push) Failing after 6s
Release / release (push) Has been skipped
Reviewed-on: #8
2026-07-26 13:23:55 +08:00
7iwind a9f0f995de Merge branch 'main' into feature/route 2026-07-26 13:23:50 +08:00
7iwind 62fd8c0685 fix: 修复 bash -e 下,for 循环中 [ -f ... ] && cp ... 里 [ 返回 1 时会触发 set -e 退出脚本。同时 test 需要实际开启 BUILD_TESTING。 2026-07-26 13:23:16 +08:00
7iwind f280a4d383 Merge pull request 'develop' (#7) from develop into main
Release / build (map[cmake_extra: name:linux-amd64 toolchain:]) (push) Failing after 6s
Release / build (map[cmake_extra: name:linux-arm64 toolchain:-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-linux-arm64.cmake]) (push) Failing after 6s
Release / build (map[cmake_extra: name:linux-armhf toolchain:-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-linux-armhf.cmake]) (push) Failing after 7s
Release / build (map[cmake_extra:-DBUILD_EXAMPLES=OFF -DBUILD_TESTING=OFF -DBUILD_SHARED=OFF name:baremetal-arm32 toolchain:-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-baremetal-arm32.cmake]) (push) Failing after 6s
Release / build (map[cmake_extra:-DBUILD_EXAMPLES=OFF -DBUILD_TESTING=OFF name:windows-amd64 toolchain:-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-windows-amd64.cmake]) (push) Failing after 6s
Release / release (push) Has been skipped
Reviewed-on: #7
2026-07-26 13:09:22 +08:00
7iwind 07a6397764 Merge branch 'main' into develop 2026-07-26 13:09:11 +08:00
7iwind b5d4963ca8 Merge pull request 'fix: 修正run_on标签名' (#6) from feature/route into develop
Reviewed-on: #6
2026-07-26 13:08:01 +08:00
7iwind e00bda534b fix: 修正run_on标签名 2026-07-26 13:07:14 +08:00
7iwind 4e43d7e3a4 Merge pull request 'develop' (#5) from develop into main
Release / build (map[cmake_extra: name:linux-arm64 toolchain:-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-linux-arm64.cmake]) (push) Has been cancelled
Release / build (map[cmake_extra: name:linux-armhf toolchain:-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-linux-armhf.cmake]) (push) Has been cancelled
Release / build (map[cmake_extra:-DBUILD_EXAMPLES=OFF -DBUILD_TESTING=OFF -DBUILD_SHARED=OFF name:baremetal-arm32 toolchain:-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-baremetal-arm32.cmake]) (push) Has been cancelled
Release / build (map[cmake_extra:-DBUILD_EXAMPLES=OFF -DBUILD_TESTING=OFF name:windows-amd64 toolchain:-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-windows-amd64.cmake]) (push) Has been cancelled
Release / release (push) Has been cancelled
Release / build (map[cmake_extra: name:linux-amd64 toolchain:]) (push) Has been cancelled
Reviewed-on: #5
2026-07-26 12:57:43 +08:00
7iwind 8dbda93e3f Merge pull request 'feature/route' (#4) from feature/route into develop
Reviewed-on: #4
2026-07-26 12:52:01 +08:00
7iwind 9b5b479560 fix: 修正run_on标签名 2026-07-26 12:50:49 +08:00
7iwind 2a4a9510b8 feat: 新增GITEA工作流,新建工具链文件 2026-07-26 12:47:46 +08:00
7iwind cfee4cb939 feat: 新增GITEA工作流,新建工具链文件 2026-07-26 12:47:36 +08:00
7iwind 48bd749251 Merge pull request 'develop' (#3) from develop into main
Reviewed-on: #3
2026-07-26 10:03:04 +08:00
7iwind 09809b0eb1 Merge pull request 'feature/route' (#2) from feature/route into develop
Reviewed-on: #2
2026-07-26 10:02:35 +08:00
7iwind 51e081118a feat: 拆分发送和接收功能声明,优化config配置方式 2026-07-26 10:01:30 +08:00
20 changed files with 1174 additions and 993 deletions
+87
View File
@@ -0,0 +1,87 @@
name: Release
on:
push:
branches: [main]
jobs:
release:
runs-on: ubuntu-builder
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Verify merge commit
run: |
PARENTS=$(git log --format="%P" -1 | wc -w)
[ "$PARENTS" -ge 2 ] || exit 78
- name: Read tag from HEAD
id: tag
run: |
TAG=$(git tag --points-at HEAD | grep '^v' | head -n1)
[ -n "$TAG" ] || exit 78
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
- name: Install cross-compilers
run: |
sudo apt-get update
sudo apt-get install -y \
gcc-arm-linux-gnueabihf \
gcc-aarch64-linux-gnu \
gcc-mingw-w64-x86-64 \
gcc-arm-none-eabi
- name: Build all targets
run: |
for entry in \
"linux-amd64|ON|" \
"linux-arm64|OFF|-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-linux-arm64.cmake" \
"linux-armhf|OFF|-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-linux-armhf.cmake" \
"windows-amd64|OFF|-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-windows-amd64.cmake -DBUILD_EXAMPLES=OFF" \
"baremetal-arm32|OFF|-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-baremetal-arm32.cmake -DBUILD_EXAMPLES=OFF -DBUILD_SHARED=OFF"
do
IFS='|' read -r name testing extra <<< "$entry"
echo ">>> Building $name"
cmake -S . -B "build-$name" -DBUILD_TESTING=$testing $extra
cmake --build "build-$name"
[ "$testing" = "ON" ] && ctest --test-dir "build-$name" --output-on-failure
pkg="datarouter-$name"
cmake --install "build-$name" --prefix "dist/$pkg"
done
- name: Create release & upload assets
run: |
TAG="${{ steps.tag.outputs.tag }}"
API="${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/releases"
TOKEN="${{ gitea.token }}"
# Check if release already exists
echo "=== Checking existing releases for $TAG ==="
EXISTING=$(curl -s -H "Authorization: token ${TOKEN}" "${API}" | \
python3 -c "import sys,json;rels=json.load(sys.stdin);print(next((r['id'] for r in rels if r['tag_name']=='${TAG}'),''))")
if [ -n "$EXISTING" ]; then
ID="$EXISTING"
echo "Release already exists (ID=$ID), updating it"
else
echo "=== Creating release $TAG ==="
RESP=$(curl -s -X POST \
-H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"tag_name\":\"${TAG}\",\"name\":\"DataRouter ${TAG}\"}" \
"$API")
ID=$(echo "$RESP" | python3 -c "import sys,json;d=json.loads(sys.stdin.read());print(d['id'])")
fi
for dir in dist/*/; do
name="$(basename "$dir").tar.gz"
tar czf "/tmp/$name" -C "$dir" .
echo "Uploading $name"
curl -s -X POST \
-H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/octet-stream" \
--data-binary @"/tmp/$name" \
"${API}/${ID}/assets?name=${name}"
done
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CidrCTestProjectState">
<ctestState>
<lineNumber testId="path:null test:datarouter" value="28" />
</ctestState>
</component>
</project>
+247
View File
@@ -1,6 +1,253 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="BackendCodeEditorSettings"> <component name="BackendCodeEditorSettings">
<option name="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=Component_0020Designer_0020generated_0020code/@EntryIndexedValue" value="Component Designer generated code" type="string" />
<option name="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=Designer_0020generated_0020code/@EntryIndexedValue" value="Designer generated code" type="string" />
<option name="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=Web_0020Form_0020Designer_0020generated_0020code/@EntryIndexedValue" value="Web Form Designer generated code" type="string" />
<option name="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=Windows_0020Form_0020Designer_0020generated_0020code/@EntryIndexedValue" value="Windows Form Designer generated code" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CDeclarationWithImplicitIntType/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CommentTypo/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConstevalIfIsAlwaysConstant/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppAbstractClassWithoutSpecifier/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppAbstractFinalClass/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppAbstractVirtualFunctionCallInCtor/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppAccessSpecifierWithNoDeclarations/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppAwaiterTypeIsNotClass/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBooleanIncrementExpression/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBoostFormatBadCode/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBoostFormatLegacyCode/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBoostFormatMixedArgs/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBoostFormatTooFewArgs/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBoostFormatTooManyArgs/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppCStyleCast/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppCVQualifierCanNotBeAppliedToReference/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassCanBeFinal/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassIsIncomplete/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassNeedsConstructorBecauseOfUninitializedMember/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppCompileTimeConstantCanBeReplacedWithBooleanConstant/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppConceptNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppConditionalExpressionCanBeSimplified/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppConstParameterInDeclaration/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppConstValueFunctionReturnType/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppCoroutineCallResolveError/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAArrayIndexOutOfBounds/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAConstantConditions/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAConstantFunctionResult/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAConstantParameter/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFADeletedPointer/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAEndlessLoop/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAEndlessLoop/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAInfiniteRecursion/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAInvalidatedMemory/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFALocalValueEscapesFunction/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFALocalValueEscapesScope/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFALoopConditionNotUpdated/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAMemoryLeak/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFANotInitializedField/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFANullDereference/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFATimeOver/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAUnreachableCode/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAUnreachableFunctionCall/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAUnreadVariable/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAUnusedValue/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeclarationHidesLocal/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeclarationHidesUncapturedLocal/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeclarationSpecifierWithoutDeclarators/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeclaratorDisambiguatedAsFunction/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeclaratorNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeclaratorUsedBeforeInitialization/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDefaultCaseNotHandledInSwitchStatement/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDefaultInitializationWithNoUserConstructor/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDefaultIsUsedAsIdentifier/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDefaultedSpecialMemberFunctionIsImplicitlyDeleted/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDefinitionsOrder/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeletingVoidPointer/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDependentTemplateWithoutTemplateKeyword/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDependentTypeWithoutTypenameKeyword/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeprecatedEntity/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeprecatedOverridenMethod/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeprecatedRegisterStorageClassSpecifier/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDereferenceOperatorLimitExceeded/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDiscardedPostfixOperatorResult/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDoxygenSyntaxError/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDoxygenUndocumentedParameter/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDoxygenUnresolvedReference/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEmptyDeclaration/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceCVQualifiersOrder/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceCVQualifiersPlacement/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceDoStatementBraces/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceForStatementBraces/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceFunctionDeclarationStyle/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceIfStatementBraces/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceNestedNamespacesStyle/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceOverridingDestructorStyle/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceOverridingFunctionStyle/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceTypeAliasCodeStyle/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceWhileStatementBraces/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEntityAssignedButNoRead/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEntityUsedOnlyInUnevaluatedContext/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnumeratorNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEqualOperandsInBinaryExpression/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEvaluationFailure/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppExplicitSpecializationInNonNamespaceScope/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppExpressionWithoutSideEffects/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppFinalFunctionInFinalClass/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppFinalNonOverridingVirtualFunction/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppForLoopCanBeReplacedWithWhile/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppForwardEnumDeclarationWithoutUnderlyingType/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppFunctionDoesntReturnValue/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppFunctionIsNotImplemented/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppFunctionResultShouldBeUsed/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppFunctionalStyleCast/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppHeaderHasBeenAlreadyIncluded/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppHiddenFunction/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppHidingFunction/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIdenticalOperandsInBinaryExpression/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIfCanBeReplacedByConstexprIf/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppImplicitDefaultConstructorNotAvailable/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIncompatiblePointerConversion/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIncompleteSwitchStatement/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppInconsistentNaming/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIntegralToPointerConversion/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppInvalidLineContinuation/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppJoinDeclarationAndAssignment/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLambdaCaptureNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLocalVariableMayBeConst/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLocalVariableMightNotBeInitialized/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLocalVariableWithNonTrivialDtorIsNeverUsed/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLongFloat/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMemberFunctionMayBeConst/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMemberFunctionMayBeStatic/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMemberInitializersOrder/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMismatchedClassTags/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMissingIncludeGuard/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMissingKeywordThrow/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppModulePartitionWithSeveralPartitionUnits/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMsExtAddressOfClassRValue/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMsExtBindingRValueToLvalueReference/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMsExtCopyElisionInCopyInitDeclarator/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMsExtDoubleUserConversionInCopyInit/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMsExtNotInitializedStaticConstLocalVar/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMsExtReinterpretCastFromNullptr/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMultiCharacterLiteral/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMultiCharacterWideLiteral/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMustBePublicVirtualToImplementInterface/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMutableSpecifierOnReferenceMember/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNoDiscardExpression/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNodiscardFunctionWithoutReturnValue/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNonExceptionSafeResourceAcquisition/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNonExplicitConversionOperator/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNonExplicitConvertingConstructor/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNonInlineFunctionDefinitionInHeaderFile/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNonInlineVariableDefinitionInHeaderFile/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNotAllPathsReturnValue/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppObjectMemberMightNotBeInitialized/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppOutParameterMustBeWritten/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterMayBeConst/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterMayBeConstPtrOrRef/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterNamesMismatch/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterNeverUsed/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPassValueParameterByConstReference/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPointerConversionDropsQualifiers/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPointerToIntegralConversion/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPolymorphicClassWithNonVirtualPublicDestructor/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPossiblyErroneousEmptyStatements/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPossiblyUninitializedMember/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPossiblyUnintendedObjectSlicing/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrecompiledHeaderIsNotIncluded/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrecompiledHeaderNotFound/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrintfBadFormat/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrintfExtraArg/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrintfMissedArg/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrintfRiskyFormat/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrivateSpecialMemberFunctionIsNotImplemented/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRangeBasedForIncompatibleReference/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedefinitionOfDefaultArgumentInOverrideFunction/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantAccessSpecifier/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantBaseClassAccessSpecifier/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantBaseClassInitializer/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantBooleanExpressionArgument/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantCastExpression/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantComplexityInComparison/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantConditionalExpression/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantConstSpecifier/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantControlFlowJump/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantDereferencingAndTakingAddress/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantElaboratedTypeSpecifier/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantElseKeyword/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantElseKeywordInsideCompoundStatement/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantEmptyDeclaration/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantEmptyStatement/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantExportKeyword/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantFwdClassOrEnumSpecifier/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantInlineSpecifier/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantLambdaParameterList/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantMemberInitializer/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantNamespaceDefinition/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantParentheses/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantQualifier/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantQualifierADL/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantStaticSpecifierOnMemberAllocationFunction/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantStaticSpecifierOnThreadLocalLocalVariable/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantTemplateArguments/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantTemplateKeyword/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantTypenameKeyword/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantVoidArgumentList/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantZeroInitializerInAggregateInitialization/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppReinterpretCastFromVoidPtr/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRemoveRedundantBraces/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppReplaceMemsetWithZeroInitialization/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppReplaceTieWithStructuredBinding/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppReturnNoValueInNonVoidFunction/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppSmartPointerVsMakeFunction/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppSomeObjectMembersMightNotBeInitialized/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppSpecialFunctionWithoutNoexceptSpecification/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticAssertFailure/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticDataMemberInUnnamedStruct/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticSpecifierOnAnonymousNamespaceMember/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStringLiteralToCharPointerConversion/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTabsAreDisallowed/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateArgumentsCanBeDeduced/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateParameterNeverUsed/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateParameterShadowing/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppThrowExpressionCanBeReplacedWithRethrow/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTooWideScope/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTooWideScopeInitStatement/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTypeAliasNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUninitializedDependentBaseClass/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUninitializedNonStaticDataMember/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnionMemberOfReferenceType/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnmatchedPragmaEndRegionDirective/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnmatchedPragmaRegionDirective/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnnamedNamespaceInHeaderFile/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnnecessaryWhitespace/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnsignedZeroComparison/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnusedIncludeDirective/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseAlgorithmWithCount/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseAssociativeContains/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseAuto/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseAutoForNumeric/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseElementsView/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseEraseAlgorithm/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseFamiliarTemplateSyntaxForGenericLambdas/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseRangeAlgorithm/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseStdSize/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseStructuredBinding/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseTypeTraitAlias/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUserDefinedLiteralSuffixDoesNotStartWithUnderscore/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUsingResultOfAssignmentAsCondition/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppVariableCanBeMadeConstexpr/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppVirtualFunctionCallInsideCtor/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppVirtualFunctionInFinalClass/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppVolatileParameterInDeclaration/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppWarningDirective/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppWrongIncludesOrder/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppWrongSlashesInIncludeDirective/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppZeroConstantCanBeReplacedWithNullptr/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppZeroValuedExpressionUsedAsNullPointer/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=IdentifierTypo/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=IfStdIsConstantEvaluatedCanBeReplaced/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=StdIsConstantEvaluatedWillAlwaysEvaluateToConstant/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=StringLiteralTypo/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
</component> </component>
</project> </project>
+65 -1
View File
@@ -3,17 +3,81 @@ project(DataRouter C)
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)
# ======== 静态库 ========
add_library(DataRouter STATIC datarouter.c) add_library(DataRouter STATIC datarouter.c)
target_include_directories(DataRouter PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(DataRouter PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:include>
)
# ======== 共享库(可选,通过 BUILD_SHARED 控制) ========
option(BUILD_SHARED "Build shared library" ON)
if(BUILD_SHARED)
add_library(DataRouter_shared SHARED datarouter.c)
target_include_directories(DataRouter_shared PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:include>
)
set_target_properties(DataRouter_shared PROPERTIES OUTPUT_NAME DataRouter)
endif()
# ======== 示例程序 ========
if(BUILD_EXAMPLES OR NOT DEFINED BUILD_EXAMPLES) if(BUILD_EXAMPLES OR NOT DEFINED BUILD_EXAMPLES)
add_executable(example example.c) add_executable(example example.c)
target_link_libraries(example DataRouter) target_link_libraries(example DataRouter)
endif() endif()
# ======== 测试 ========
if(BUILD_TESTING OR NOT DEFINED BUILD_TESTING) if(BUILD_TESTING OR NOT DEFINED BUILD_TESTING)
enable_testing() enable_testing()
add_executable(test_datarouter tests/test_datarouter.c) add_executable(test_datarouter tests/test_datarouter.c)
target_link_libraries(test_datarouter DataRouter) target_link_libraries(test_datarouter DataRouter)
add_test(NAME datarouter COMMAND test_datarouter) add_test(NAME datarouter COMMAND test_datarouter)
endif() endif()
# ======== 安装规则 ========
# 静态库 → lib/
install(TARGETS DataRouter
EXPORT DataRouterTargets
ARCHIVE DESTINATION lib
)
# 共享库 → lib/(可选)
if(TARGET DataRouter_shared)
install(TARGETS DataRouter_shared
EXPORT DataRouterTargets
ARCHIVE DESTINATION lib # .lib (Windows import lib)
LIBRARY DESTINATION lib # .so
RUNTIME DESTINATION lib # .dll
)
endif()
# 示例 → bin/
if(TARGET example)
install(TARGETS example RUNTIME DESTINATION bin)
endif()
# 头文件 → include/
install(FILES datarouter.h DESTINATION include)
# 文档 → share/
install(FILES datarouter_config_template.h LICENSE DESTINATION share)
if(EXISTS ${CMAKE_SOURCE_DIR}/README.md)
install(FILES README.md DESTINATION share)
endif()
# CMake 包配置文件 → cmake/
set(INCLUDE_INSTALL_DIR include)
set(LIB_INSTALL_DIR lib)
include(CMakePackageConfigHelpers)
configure_package_config_file(
cmake/DataRouterConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/DataRouterConfig.cmake
INSTALL_DESTINATION cmake
PATH_VARS INCLUDE_INSTALL_DIR LIB_INSTALL_DIR
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/DataRouterConfig.cmake DESTINATION cmake)
install(EXPORT DataRouterTargets FILE DataRouterTargets.cmake DESTINATION cmake)
+212 -282
View File
@@ -2,31 +2,35 @@
## 1. 设计目标 ## 1. 设计目标
轻量级 C 语言数据路由库。核心思想: 轻量级 C 语言数据路由库。发送与接收能力分开声明,端口绑定 `send_type` 和外设 handler 列表。调用 `datarouter_send_<id>(data, len)` 时自动遍历端口表,广播到所有 `send_type` 匹配的端口。
- **报文类型(Channel)**:编译期通过宏声明的数据类型(如 `sensor` / `status` / `control`) ### 核心设计
- **端口(Port)**:运行期创建的连接点,绑定一个报文类型 + 一个物理外设发送 handler
- **路由(Routing)**:将逻辑报文类型连接到实际的物理外设发送函数
``` ```
应用层 send_sensor(port, data) DATAROUTER_SEND_CAPABILITIES DATAROUTER_RECV_CAPABILITIES
│ DATAROUTER_SEND(sensor, ...) DATAROUTER_RECV(rtcm, ...)
▼ DATAROUTER_SEND(status, ...) DATAROUTER_RECV(command, ...)
库校验端口 │ │
│ ▼ ▼
▼ send_sensor(data, len) on_recv_rtcm(port, data, len)
调用注册的 handler(data) ← 用户实现:UART / TCP / CAN / 文件发送 │ (用户实现回调)
│ ▼
▼ 遍历端口表 → send_type 匹配?
物理外设 │
▼
for each handler(port, data) ─── UART / TCP / CAN
``` ```
**关键**:send 无 port_id 参数,自动路由到所有匹配端口。
### 设计原则 ### 设计原则
| 原则 | 说明 | | 原则 | 说明 |
|------|------| |------|------|
| 编译期确定类型 | 所有报文类型在 `datarouter_config.h` 中声明,库自动生成代码 | | 收发分离 | 发送/接收能力在配置中分开声明,按需组合 |
| 运行期绑定外设 | 端口创建后,用户注册具体的发送 handler(函数指针) | | 编译期确定能力 | 所有能力在 `datarouter_config.h` 中声明,库自动生成代码 |
| 运行期绑定外设 | 端口创建后,通过 `add_handler` 注册物理外设发送函数 |
| 自动广播 | `send_<id>(data, len)` 遍历所有端口,自动匹配 send_type |
| 不关心物理外设 | 库不依赖任何外设 API,只调用用户提供的 handler | | 不关心物理外设 | 库不依赖任何外设 API,只调用用户提供的 handler |
| 零动态内存 | 纯静态数组,无 malloc/free | | 零动态内存 | 纯静态数组,无 malloc/free |
@@ -41,23 +45,22 @@
left to right direction left to right direction
rectangle "用户代码" as User { rectangle "用户代码" as User {
component "应用逻辑\n调用 datarouter_send_<type>()" as App component "应用逻辑\n调 send_<id>(data, len)" as App
component "datarouter_config.h\n声明报文类型" as Cfg component "datarouter_config.h\n声明能力" as Cfg
component "外设 handler\nUART / TCP / CAN / 文件" as Handlers component "外设 handler\nUART / TCP / CAN" as Handlers
component "接收回调\n处理收到的报文" as Callbacks component "接收回调\non_recv_<id>()" as Callbacks
} }
database "DataRouter 库" as Lib { database "DataRouter 库" as Lib {
component "datarouter.h\n公共 API + 代码生成" as Hdr component "datarouter.h\n公共 API + X-macro" as Hdr
component "datarouter.c\n端口表 + handler 调用" as Impl component "datarouter.c\n端口表+遍历路由" as Impl
} }
App --> Hdr App --> Hdr
Cfg --> Hdr Cfg --> Hdr
Impl --> Cfg Impl --> Cfg
Handlers --> Impl : 注册为 send_handler Handlers --> Impl : add_handler
Callbacks --> Impl : 实现 on_recv_<type> Callbacks --> Impl : 实现 on_recv_<id>
@enduml @enduml
``` ```
@@ -65,138 +68,87 @@ Callbacks --> Impl : 实现 on_recv_<type>
| 文件 | 职责 | | 文件 | 职责 |
|------|------| |------|------|
| `datarouter_config.h` | 用户在此声明所有报文类型 | | `datarouter_config.h` | 声明 `SEND_CAPABILITIES` 和 `RECV_CAPABILITIES` |
| `datarouter.h` | 公共 API + X-macro 展开(enum + 发送函数声明 + 回调声明) | | `datarouter.h` | 公共 API + X-macro 展开 |
| `datarouter.c` | 发送函数实现 + handler 调用 + 端口管理 | | `datarouter.c` | 端口表 + send 遍历 + 弱符号回调 |
| `example.c` | 使用示例 | | `example.c` | 使用示例 |
| `tests/test_datarouter.c` | 31 个单元测试 | | `tests/test_datarouter.c` | 22 个单元测试 |
--- ---
## 3. 核心概念 ## 3. 收发分离设计
### 3.1 报文类型(Channel) ### 3.1 配置声明
```plantuml ```c
@startuml #define DATAROUTER_SEND_CAPABILITIES \
object "DATAROUTER_CHANNEL(sensor, \"Sensor data\")" as C1 DATAROUTER_SEND(sensor, "Sensor data report") \
object "DATAROUTER_CHANNEL(status, \"Status report\")" as C2 DATAROUTER_SEND(status, "Device status report")
object "DATAROUTER_CHANNEL(control, \"Control command\")" as C3
note right of C1 #define DATAROUTER_RECV_CAPABILITIES \
编译期展开产物: DATAROUTER_RECV(rtcm, "RTCM correction data") \
· DATAROUTER_CHANNEL_sensor = 0 DATAROUTER_RECV(command, "Remote control command")
· int datarouter_send_sensor(port, data, len)
· void datarouter_on_recv_sensor(port, data, len)
· channel_names[0] = "sensor"
· channel_descs[0] = "Sensor data"
end note
C1 --> C2
C2 --> C3
@enduml
``` ```
### 3.2 端口与 handler ### 3.2 展开产物
```plantuml
@startuml
skinparam objectBorderColor black
object "端口 A" as PA {
type = sensor
handler = uart_send
}
object "端口 B" as PB {
type = sensor
handler = tcp_send
}
object "端口 C" as PC {
type = status
handler = uart_send
}
object "UART发送函数" as UART
object "TCP发送函数" as TCP
PA --> UART : 注册
PB --> TCP : 注册
PC --> UART : 注册
note right of PA
同一报文类型 sensor
可路由到不同外设
end note
note right of PC
不同报文类型 status
可复用同一外设 UART
end note
@enduml
``` ```
DATAROUTER_SEND_CAPABILITIES 展开:
enum: sensor = 0, status = 1, SEND_CAP_COUNT = 2
发送函数: int datarouter_send_sensor(const void *data, size_t len);
int datarouter_send_status(const void *data, size_t len);
元数据: send_names[] = {"sensor", "status"}
send_descs[] = {"Sensor data report", "Device status report"}
DATAROUTER_RECV_CAPABILITIES 展开:
enum: rtcm = 0, command = 1, RECV_CAP_COUNT = 2
接收回调: void datarouter_on_recv_rtcm(uint32_t port, const void*, size_t);
void datarouter_on_recv_command(uint32_t port, const void*, size_t);
弱符号默认: 空实现,用户覆盖
元数据: recv_names[] = {"rtcm", "command"}
recv_descs[] = {"RTCM correction data", "Remote control command"}
```
### 3.3 端口示例
```
端口 A: send_type = sensor, handlers=[uart_send, tcp_send]
端口 B: send_type = sensor, handlers=[can_send]
端口 C: send_type = status, handlers=[uart_send]
```
调用 `send_sensor("temp", 4)` → A 和 B 同时触发。
--- ---
## 4. X-macro 代码生成 ## 4. X-macro 代码生成
### 4.1 展开流程
```plantuml ```plantuml
@startuml @startuml
== datarouter.h ==
== datarouter.h: 3 次展开 == -> datarouter_config.h: 获得两组宏列表
-> datarouter_config.h: #include 获得 DATAROUTER_CHANNELS datarouter.h -> "1": #define DATAROUTER_SEND(id,desc) DATAROUTER_SEND_##id,
note right: 发送 enum
datarouter.h -> "第 1 遍": #define DATAROUTER_CHANNEL(id,desc) DATAROUTER_CHANNEL_##id, datarouter.h -> "2": #define DATAROUTER_RECV(id,desc) DATAROUTER_RECV_##id,
note right: 生成 enum 通道枚举 note right: 接收 enum
datarouter.h -> "第 2 遍": #define DATAROUTER_CHANNEL(id,desc) int datarouter_send_##id(...); datarouter.h -> "3": #define DATAROUTER_SEND(id,desc) int send_##id(data, len);
note right: 生成发送触发函数声明 note right: 发送函数声明
datarouter.h -> "第 3 遍": #define DATAROUTER_CHANNEL(id,desc) void datarouter_on_recv_##id(...); datarouter.h -> "4": #define DATAROUTER_RECV(id,desc) void on_recv_##id(...);
note right: 生成接收回调声明 note right: 接收回调声明
== datarouter.c: 3 次展开 == == datarouter.c ==
datarouter.c -> "第 4 遍": #define DATAROUTER_CHANNEL(id,desc) DATAROUTER_WEAK void ...
note right: 弱符号回调默认空实现
datarouter.c -> "第 5 遍": #define DATAROUTER_CHANNEL(id,desc) int datarouter_send_##id(…) {…}
note right: 发送函数实现 → 调用handler
datarouter.c -> "第 6 遍": #define DATAROUTER_CHANNEL(id,desc) #id, → channel_names[]
datarouter.c -> "第 7 遍": #define DATAROUTER_CHANNEL(id,desc) desc, → channel_descs[]
datarouter.c -> "5": 弱符号回调默认实现
datarouter.c -> "6": 发送函数实现(遍历端口表)
datarouter.c -> "7~8": send_names[] / send_descs[] / recv_names[] / recv_descs[]
@enduml @enduml
``` ```
### 4.2 展开示例(3 个通道)
```
用户配置:
DATAROUTER_CHANNEL(sensor, "Sensor data")
DATAROUTER_CHANNEL(status, "Status report")
DATAROUTER_CHANNEL(control, "Control command")
展开产物:
enum: sensor = 0, status = 1, control = 2, CHANNEL_COUNT = 3
发送: int datarouter_send_sensor(uint32_t, const void*, size_t);
int datarouter_send_status(…);
int datarouter_send_control(…);
回调: void datarouter_on_recv_sensor(uint32_t, const void*, size_t);
void datarouter_on_recv_status(…);
void datarouter_on_recv_control(…);
元数据: names[] = {"sensor", "status", "control"}
descs[] = {"Sensor data", "Status report", "Control command"}
```
--- ---
## 5. 数据结构 ## 5. 数据结构
@@ -207,64 +159,56 @@ datarouter.c -> "第 7 遍": #define DATAROUTER_CHANNEL(id,desc) desc, → chann
@startuml @startuml
skinparam classAttributeIconSize 0 skinparam classAttributeIconSize 0
class "datarouter_channel (enum)" as Chan { enum "datarouter_send_cap" as S { SENSOR, STATUS, SEND_CAP_COUNT }
+ DATAROUTER_CHANNEL_sensor = 0 enum "datarouter_recv_cap" as R { RTCM, COMMAND, RECV_CAP_COUNT }
+ DATAROUTER_CHANNEL_status = 1
+ DATAROUTER_CHANNEL_control = 2 class "port_entry_t" as Entry {
+ DATAROUTER_CHANNEL_COUNT = 3 - int send_type
- datarouter_send_handler_t handlers[]
- int count
- uint8_t in_use
} }
class "datarouter_send_handler_t" as Handler { class "datarouter_send_handler_t" as Handler {
+ void (*)(uint32_t port_id, const void *data, size_t len) + void (*)(uint32_t port_id, const void *data, size_t len)
} }
class "端口表" as Table {
- port_entry_t ports[64]
- uint8_t initialized
}
class "port_entry_t" as Entry {
- int type
- datarouter_send_handler_t handler
- uint8_t in_use
}
class "发送函数" as Send { class "发送函数" as Send {
+ int send_sensor(port, data, len) + int send_sensor(data, len)
+ int send_status(port, data, len) + int send_status(data, len)
+ int send_control(port, data, len)
} }
class "接收回调" as Recv { class "接收回调" as Recv {
+ void on_recv_sensor(port, data, len) ← 用户实现 + void on_recv_rtcm(port, data, len) ← 用户实现
+ void on_recv_status(port, data, len) + void on_recv_command(port, data, len)
+ void on_recv_control(port, data, len)
} }
Entry --> Chan : type ∈ enum Entry --> S : send_type
Entry --> Handler : handler 函数指针 Entry --> Handler : handlers[]
Table --> Entry : ports[] Send --> Entry : 遍历端口表 → 匹配 send_type → 遍历 handlers
Send --> Table : 校验端口 + 调用 handler Recv --> Entry : 用户根据业务调对应回调
Recv --> Send : 被用户从外设收数据时调用
@enduml @enduml
``` ```
### 5.2 `port_entry_t` ### 5.2 `port_entry_t`
```c ```c
#define DATAROUTER_MAX_HANDLERS_PER_PORT 4
typedef struct { typedef struct {
int type; // 报文类型(DATAROUTER_CHANNEL_xxx / -1) int send_type;
datarouter_send_handler_t handler; // 外设发送函数(NULL 表示未注册) datarouter_send_handler_t handlers[DATAROUTER_MAX_HANDLERS_PER_PORT];
uint8_t in_use; // 1=已分配,0=空闲 int count;
uint8_t in_use;
} port_entry_t; } port_entry_t;
``` ```
### 5.3 端口表 ### 5.3 端口表
```c ```c
#define DATAROUTER_MAX_PORTS 64 // 可由 -D 覆盖 #define DATAROUTER_MAX_PORTS 64
static port_entry_t ports[64]; // 静态数组,无动态内存 static port_entry_t ports[DATAROUTER_MAX_PORTS];
static uint8_t initialized; // init/reset 标志 static uint8_t initialized;
``` ```
--- ---
@@ -274,54 +218,53 @@ static uint8_t initialized; // init/reset 标志
### 6.1 初始化 ### 6.1 初始化
```c ```c
void datarouter_init(void); // 幂等初始化 void datarouter_init(void); // 幂等初始化
void datarouter_reset(void); // 重置所有状态(测试隔离/热重启) void datarouter_reset(void); // 重置全部状态
``` ```
### 6.2 端口管理 ### 6.2 端口管理
```c ```c
datarouter_port_t datarouter_port_create(void); // 返回端口 ID 或 -1 datarouter_port_t datarouter_port_create(void);
void datarouter_port_delete(port); // 删除端口,ID 可复用 void datarouter_port_delete(datarouter_port_t port);
void datarouter_port_set_type(port, channel_id); // 指定报文类型
int datarouter_port_get_type(port); // 获取报文类型,-1 表示无 void datarouter_port_set_send_type(port, int cap_id);
void datarouter_port_set_handler(port, handler); // 注册外设发送 handler int datarouter_port_get_send_type(port);
datarouter_send_handler_t datarouter_port_get_handler(port); // 获取 handler
int datarouter_port_add_handler(port, handler);
int datarouter_port_remove_handler(port, handler);
void datarouter_port_clear_handlers(port);
int datarouter_port_handler_count(port);
``` ```
### 6.3 发送触发函数(每通道 1 个,X-macro 生成) ### 6.3 发送触发函数(每个 SEND 能力生成一个)
```c ```c
int datarouter_send_sensor(uint32_t port_id, const void *data, size_t len); int datarouter_send_sensor(const void *data, size_t len);
int datarouter_send_status(uint32_t port_id, const void *data, size_t len); int datarouter_send_status(const void *data, size_t len);
int datarouter_send_control(uint32_t port_id, const void *data, size_t len);
``` ```
返回值: 返回值:0 成功 / -1 未初始化 / -2 无端口匹配该 send_type
| 值 | 含义 |
|:--:|------|
| 0 | 成功,handler 已被调用 |
| -1 | 库未初始化 |
| -2 | 端口无效或已删除 |
| -3 | 端口类型与触发的报文类型不匹配 |
| -4 | 端口未注册外设发送 handler |
### 6.4 接收回调声明(每通道 1 个,用户实现) ### 6.4 接收回调(每个 RECV 能力生成一个声明)
```c ```c
void datarouter_on_recv_sensor(uint32_t port_id, const void *data, size_t len); void datarouter_on_recv_rtcm(uint32_t port_id, const void *data, size_t len);
void datarouter_on_recv_status(uint32_t port_id, const void *data, size_t len); void datarouter_on_recv_command(uint32_t port_id, const void *data, size_t len);
void datarouter_on_recv_control(uint32_t port_id, const void *data, size_t len);
``` ```
库提供弱符号默认空实现,用户定义同名函数即可覆盖。 库提供弱符号默认空实现。
### 6.5 元数据查询 ### 6.5 能力元数据
```c ```c
int datarouter_channel_count(void); int datarouter_send_cap_count(void);
const char *datarouter_channel_name(int channel_id); const char *datarouter_send_cap_name(int cap_id);
const char *datarouter_channel_desc(int channel_id); const char *datarouter_send_cap_desc(int cap_id);
int datarouter_recv_cap_count(void);
const char *datarouter_recv_cap_name(int cap_id);
const char *datarouter_recv_cap_desc(int cap_id);
``` ```
--- ---
@@ -334,38 +277,35 @@ const char *datarouter_channel_desc(int channel_id);
@startuml @startuml
actor "应用" as App actor "应用" as App
participant "datarouter_send_sensor()" as Send participant "datarouter_send_sensor()" as Send
participant "端口表" as Table participant "端口表 ports[]" as Table
participant "handler" as Handler participant "handlers[]" as HList
participant "外设" as Dev participant "外设" as Dev
App -> Send : send_sensor(port=2, data, len) App -> Send : send_sensor("temp:25.3", 9)
group 校验 group 初始化检查
Send -> Send : initialized? Send -> Send : initialized?
alt 未初始化 alt 否 → return -1
Send --> App : return -1
end
Send -> Table : ports[2].in_use?
alt 端口无效
Send --> App : return -2
end
Send -> Table : ports[2].type == sensor?
alt 类型不匹配
Send --> App : return -3
end
Send -> Table : ports[2].handler != NULL?
alt 无 handler
Send --> App : return -4
end end
end end
Send -> Handler : handler(2, data, len) group 遍历端口表
Handler -> Dev : UART_SendData(data, len) loop for p in 0..MAX_PORTS-1
Handler --> Send : void Send -> Table : ports[p].in_use && ports[p].send_type == SENSOR ?
Send --> App : return 0 alt 匹配
loop for h in 0..count-1
Send -> HList : handlers[h](p, data, len)
HList -> Dev : 实际外设写入
end
end
end
end
alt 至少调了一次 handler
Send --> App : return 0
else 无匹配端口
Send --> App : return -2
end
@enduml @enduml
``` ```
@@ -375,13 +315,10 @@ Send --> App : return 0
@startuml @startuml
actor "外设" as Dev actor "外设" as Dev
participant "用户分发代码" as Dispatch participant "用户分发代码" as Dispatch
participant "datarouter_on_recv_sensor()" as Callback participant "on_recv_rtcm()" as Callback
participant "用户处理逻辑" as Process
Dev -> Dispatch : 收到数据,识别为 sensor 类型 Dev -> Dispatch : 收到数据,识别为 rtcm
Dispatch -> Callback : on_recv_sensor(port_id, data, len) Dispatch -> Callback : on_recv_rtcm(port_id, data, len)
Callback -> Process : 解析数据、更新状态等
Process --> Callback : void
Callback --> Dispatch : void Callback --> Dispatch : void
@enduml @enduml
``` ```
@@ -390,53 +327,57 @@ Callback --> Dispatch : void
```plantuml ```plantuml
@startuml @startuml
state "空闲\nin_use=0" as Idle state "空闲" as Idle
state "已创建\nin_use=1\ntype=-1\nhandler=NULL" as Created state "已创建\nsend_type=-1\ncount=0" as Created
state "已配置\nin_use=1\ntype=sensor\nhandler=..." as Config state "已配置" as Config
[*] --> Idle : datarouter_reset() [*] --> Idle : reset / 启动
Idle --> Created : port_create()
Idle --> Created : datarouter_port_create() Created --> Config : set_send_type / add_handler
Config --> Config : 切换 send_type\n增删 handler
Created --> Config : set_type + set_handler Config --> Created : set_send_type(-1) / clear_handlers
Config --> Config : set_type / set_handler\n可任意切换 Created --> Idle : port_delete()
Config --> Created : set_type(-1) / set_handler(NULL)\n清除能力 Config --> Idle : port_delete()
Created --> Idle : datarouter_port_delete()
Config --> Idle : datarouter_port_delete()
Idle --> Created : 复用 ID Idle --> Created : 复用 ID
@enduml @enduml
``` ```
--- ---
## 8. 路由示例 ## 8. 路由场景
### 场景:传感器数据同时路由到 UART 和 TCP ### 场景 1:sensor 广播到 UART + TCP + CAN
``` ```c
应用 datarouter_port_t p1 = datarouter_port_create();
│ datarouter_port_set_send_type(p1, DATAROUTER_SEND_sensor);
├─ send_sensor(sensor_uart_port, data, len) datarouter_port_add_handler(p1, uart_send);
│ │ datarouter_port_add_handler(p1, tcp_send);
│ ▼
│ handler = uart_send → UART 外设 datarouter_port_t p2 = datarouter_port_create();
│ datarouter_port_set_send_type(p2, DATAROUTER_SEND_sensor);
└─ send_sensor(sensor_tcp_port, data, len) datarouter_port_add_handler(p2, can_send);
│
▼ datarouter_send_sensor("temp:25.3", 9);
handler = tcp_send → TCP 外设 // → uart_send(p1, "temp:25.3", 9)
// → tcp_send(p1, "temp:25.3", 9)
// → can_send(p2, "temp:25.3", 9)
``` ```
### 场景:运行时切换路由 ### 场景 2:仅接收
```c
void datarouter_on_recv_rtcm(uint32_t port_id, const void *data, size_t len) {
apply_rtcm_correction(data, len);
}
// 外设收到后:datarouter_on_recv_rtcm(port, data, len);
``` ```
端口 sensor_tcp 原 handler = tcp_send
datarouter_port_set_handler(sensor_tcp, file_send); ### 场景 3:运行时移除 handler
端口 sensor_tcp 现 handler = file_send → 改为写入文件 ```c
datarouter_port_remove_handler(p1, tcp_send);
// 下次 send_sensor 不再触发 tcp
``` ```
--- ---
@@ -446,10 +387,10 @@ datarouter_port_set_handler(sensor_tcp, file_send);
```plantuml ```plantuml
@startuml @startuml
package "libDataRouter.a" { package "libDataRouter.a" {
component "弱符号\nvoid on_recv_sensor(...){}" as Weak component "弱符号\nvoid on_recv_rtcm(...){}" as Weak
} }
package "用户代码" { package "用户代码" {
component "强符号\nvoid on_recv_sensor(...){...}" as Strong component "强符号\nvoid on_recv_rtcm(...){...}" as Strong
} }
package "最终链接产物" { package "最终链接产物" {
component "使用用户版本" as Result component "使用用户版本" as Result
@@ -459,44 +400,32 @@ Weak ..> Result : 后备
@enduml @enduml
``` ```
- 库为每个回调提供 `__attribute__((weak))` 空实现
- 用户定义同名函数自动覆盖
- 未定义的回调在调用时静默空转
--- ---
## 10. 错误码体系 ## 10. 错误码
| 返回值 | 含义 | | 返回值 | 含义 |
|:------:|------| |:------:|------|
| 0 | 成功 | | 0 | 成功,至少有一个 handler 被调用 |
| -1 | 库未初始化 | | -1 | 库未初始化(未调 init) |
| -2 | 端口无效(越界/未创建/已删除) | | -2 | 无端口匹配该 send_type(或匹配端口均无 handler) |
| -3 | 端口类型不匹配 |
| -4 | 端口未注册发送 handler |
--- ---
## 11. 配置扩展 ## 11. 配置扩展
### 增加报文类型
编辑 `datarouter_config.h`:
```c ```c
#define DATAROUTER_CHANNELS \ #define DATAROUTER_SEND_CAPABILITIES \
DATAROUTER_CHANNEL(sensor, "Sensor data") \ DATAROUTER_SEND(sensor, "Sensor data") \
DATAROUTER_CHANNEL(status, "Status report") \ DATAROUTER_SEND(debug, "Debug log") /* 新增 */
DATAROUTER_CHANNEL(control, "Control command") \
DATAROUTER_CHANNEL(debug, "Debug message") /* 新增 */ #define DATAROUTER_RECV_CAPABILITIES \
DATAROUTER_RECV(rtcm, "RTCM data") \
DATAROUTER_RECV(ota, "OTA firmware") /* 新增 */
``` ```
重新编译后自动生成:枚举、发送函数、回调声明、元数据数组。
### 修改最大端口数
```bash ```bash
cmake -DCMAKE_C_FLAGS="-DDATAROUTER_MAX_PORTS=128" ... cmake -DCMAKE_C_FLAGS="-DDATAROUTER_MAX_PORTS=128 -DDATAROUTER_MAX_HANDLERS_PER_PORT=8"
``` ```
--- ---
@@ -505,8 +434,9 @@ cmake -DCMAKE_C_FLAGS="-DDATAROUTER_MAX_PORTS=128" ...
| 约束 | 说明 | | 约束 | 说明 |
|------|------| |------|------|
| 报文类型 | 编译期固定,修改需重新编译 | | 能力声明 | 编译期固定,修改需重新编译 |
| 最大端口数 | 默认 64,可由 `-D` 覆盖 | | 最大端口数 | 默认 64,通过 `-D` 覆盖 |
| 每端口 handler 上限 | 默认 4,通过 `-D` 覆盖 |
| 线程安全 | 不支持(无锁),多线程需外部加锁 | | 线程安全 | 不支持(无锁),多线程需外部加锁 |
| 弱符号 | GCC/Clang 原生支持 | | 弱符号 | GCC/Clang 原生支持 |
| 内存分配 | 纯静态,无 malloc/free | | 内存分配 | 纯静态,无 malloc/free |
+82 -61
View File
@@ -1,34 +1,45 @@
# DataRouter # DataRouter
轻量级 C 语言数据路由库。编译期声明报文类型,运行期将端口绑定到物理外设发送函数,实现一对多广播路由。 轻量级 C 语言数据路由库。**发送能力和接收能力分开声明**,端口绑定 `send_type` 和外设 handler 列表。调用 `datarouter_send_<id>(data, len)` 时自动广播到所有匹配端口。
## 概念 ## 概念
| 术语 | 说明 | | 术语 | 说明 |
|------|------| |------|------|
| **通道 (Channel)** | 编译期声明的报文类型(如 `sensor` / `status` / `control`) | | **发送能力 (Send Capability)** | 编译期声明的可发送报文类型,生成 `datarouter_send_<id>(data, len)` |
| **端口 (Port)** | 运行期创建的连接点,绑定一个报文类型和 N 个外设发送 handler | | **接收能力 (Recv Capability)** | 编译期声明的可接收报文类型,生成 `datarouter_on_recv_<id>(port, data, len)` |
| **路由** | 将报文类型连接到物理外设发送函数的过程 | | **端口 (Port)** | 运行期创建的连接点,包含 `send_type` + handler 列表 |
| **Handler** | 用户实现的物理外设发送函数,一个端口可注册多个(默认上限 4) | | **Handler** | 用户实现的物理外设发送函数,一个端口可注册多个(默认上限 4) |
## 快速开始 ## 快速开始
### 1. 声明报文类型 ### 1. 声明能力
编辑 `datarouter_config.h`: 创建自己的 `datarouter_config.h`(可参考 `datarouter_config_template.h`):
```c ```c
#define DATAROUTER_CHANNELS \ #define DATAROUTER_SEND_CAPABILITIES \
DATAROUTER_CHANNEL(sensor, "Sensor data") \ DATAROUTER_SEND(sensor, "Sensor data report") \
DATAROUTER_CHANNEL(status, "Status report") \ DATAROUTER_SEND(status, "Device status report")
DATAROUTER_CHANNEL(control, "Control command")
#define DATAROUTER_RECV_CAPABILITIES \
DATAROUTER_RECV(rtcm, "RTCM correction data") \
DATAROUTER_RECV(command, "Remote control command")
``` ```
> 库自带默认配置。将你的 `datarouter_config.h` 放到项目目录,
> 编译时添加 `-I /path/to/your/config` 即可覆盖。无需修改库源码。
>
> ```cmake
> include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR})
> add_subdirectory(path/to/DataRouter)
> target_link_libraries(my_app DataRouter)
> ```
### 2. 实现外设发送 handler ### 2. 实现外设发送 handler
```c ```c
static void uart_send(uint32_t port_id, const void *data, size_t len) { static void uart_send(uint32_t port_id, const void *data, size_t len) {
/* 调用具体外设 API 发送数据 */
HAL_UART_Transmit(&huart1, data, len, 1000); HAL_UART_Transmit(&huart1, data, len, 1000);
} }
@@ -37,27 +48,29 @@ static void tcp_send(uint32_t port_id, const void *data, size_t len) {
} }
``` ```
### 3. 创建端口并注册 handler ### 3. 创建端口
```c ```c
datarouter_init(); datarouter_init();
datarouter_port_t p = datarouter_port_create(); /* —— 传感器上报:sensor 报文走 UART + TCP —— */
datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor); datarouter_port_t tx = datarouter_port_create();
datarouter_port_set_send_type(tx, DATAROUTER_SEND_sensor);
datarouter_port_add_handler(tx, uart_send);
datarouter_port_add_handler(tx, tcp_send);
/* 一个端口同时路由到两个外设 */ /* 不指定端口,库自动广播到所有 send_type=sensor 的端口 */
datarouter_port_add_handler(p, uart_send); datarouter_send_sensor("temperature:25.3", 16); // → UART + TCP
datarouter_port_add_handler(p, tcp_send);
/* 发送 → 同时走 UART 和 TCP */ /* —— 接收:实现回调,外设数据到达后自行调用 —— */
datarouter_send_sensor(p, "temperature:25.3", 16); datarouter_on_recv_rtcm(rx_port, data, len);
``` ```
### 4. 实现接收回调 ### 4. 实现接收回调
```c ```c
void datarouter_on_recv_sensor(uint32_t port_id, const void *data, size_t len) { void datarouter_on_recv_rtcm(uint32_t port_id, const void *data, size_t len) {
printf("received sensor data: %.*s\n", (int)len, (const char *)data); apply_rtcm_correction(data, len);
} }
``` ```
@@ -66,64 +79,71 @@ void datarouter_on_recv_sensor(uint32_t port_id, const void *data, size_t len) {
### 初始化 ### 初始化
```c ```c
void datarouter_init(void); // 初始化(幂等) void datarouter_init(void); // 幂等初始化
void datarouter_reset(void); // 重置全部状态 void datarouter_reset(void); // 重置全部状态
``` ```
### 端口管理 ### 端口管理
```c ```c
datarouter_port_t datarouter_port_create(void); // 创建端口 datarouter_port_t datarouter_port_create(void); // 创建端口
void datarouter_port_delete(datarouter_port_t port); // 删除端口 void datarouter_port_delete(datarouter_port_t port); // 删除
void datarouter_port_set_type(port, int channel_id); // 设置报文类型
int datarouter_port_get_type(port); // 获取报文类型 void datarouter_port_set_send_type(port, int cap_id); // 设置发送类型
int datarouter_port_add_handler(port, handler); // 注册外设 handler int datarouter_port_get_send_type(port); // -1 = 未设置
int datarouter_port_remove_handler(port, handler); // 移除 handler
void datarouter_port_clear_handlers(port); // 清除全部 handler int datarouter_port_add_handler(port, handler); // 注册外设 handler
int datarouter_port_handler_count(port); // handler 数量 int datarouter_port_remove_handler(port, handler); // 移除
void datarouter_port_clear_handlers(port); // 清除全部
int datarouter_port_handler_count(port); // 当前数量
``` ```
### 发送触发(每通道一个,X-macro 自动生成) ### 发送触发(每个 SEND 能力生成一个)
```c ```c
int datarouter_send_sensor(port, data, len); // 返回值:0 成功 / -1 未初始化 int datarouter_send_sensor(const void *data, size_t len); // 0 成功
int datarouter_send_status(port, data, len); // -2 端口无效 int datarouter_send_status(const void *data, size_t len); // -1 未初始化
int datarouter_send_control(port, data, len); // -3 类型不匹配 // -2 无匹配端口
// -4 无 handler
``` ```
### 接收回调(每通道一个,用户实现) 不指定端口,库遍历所有端口,`send_type` 匹配的逐一调用其全部 handler。
### 接收回调(每个 RECV 能力生成一个声明)
```c ```c
void datarouter_on_recv_sensor(uint32_t port_id, const void *data, size_t len); void datarouter_on_recv_rtcm(uint32_t port_id, const void *data, size_t len);
void datarouter_on_recv_status(uint32_t port_id, const void *data, size_t len); void datarouter_on_recv_command(uint32_t port_id, const void *data, size_t len);
void datarouter_on_recv_control(uint32_t port_id, const void *data, size_t len);
``` ```
库提供弱符号默认空实现,用户定义同名函数即可覆盖。 库提供弱符号默认空实现,用户定义同名函数覆盖。
### 通道元数据 ### 能力元数据(两组独立)
```c ```c
int datarouter_channel_count(void); int datarouter_send_cap_count(void);
const char *datarouter_channel_name(int channel_id); const char *datarouter_send_cap_name(int cap_id);
const char *datarouter_channel_desc(int channel_id); const char *datarouter_send_cap_desc(int cap_id);
int datarouter_recv_cap_count(void);
const char *datarouter_recv_cap_name(int cap_id);
const char *datarouter_recv_cap_desc(int cap_id);
``` ```
## 路由模型 ## 路由模型
``` ```
send_sensor(port, data, len) datarouter_send_sensor("temperature:25.3", 16)
│ │
├─→ handler[0] = uart_send → UART 外设 ├─→ port 0 (send_type=sensor) handlers[0]=uart_send → UART
├─→ handler[1] = tcp_send → TCP 外设 │ handlers[1]=tcp_send → TCP
└─→ handler[2] = can_send → CAN 外设 └─→ port 1 (send_type=sensor) handlers[0]=can_send → CAN
``` ```
- 一个端口可注册多个 handler(一对多广播) - 调用 `send_<id>(data, len)` 时,库遍历所有端口,自动匹配 `send_type`
- 每个端口可注册多个 handler(一对多广播)
- 同一 handler 可注册到多个端口(多路复用) - 同一 handler 可注册到多个端口(多路复用)
- 运行时通过 `add_handler` / `remove_handler` 动态改变路由 - 运行时通过 `add_handler` / `remove_handler` / `port_delete` 动态改变路由
- 清空 handler 后发送返回 -4 - 无任何端口匹配该 `send_type` 时返回 -2
## 编译 ## 编译
@@ -132,7 +152,7 @@ cmake -S . -B build
cmake --build build cmake --build build
``` ```
可选配置项: 可选配置:
```bash ```bash
cmake -DCMAKE_C_FLAGS="-DDATAROUTER_MAX_PORTS=128 -DDATAROUTER_MAX_HANDLERS_PER_PORT=8" cmake -DCMAKE_C_FLAGS="-DDATAROUTER_MAX_PORTS=128 -DDATAROUTER_MAX_HANDLERS_PER_PORT=8"
@@ -147,18 +167,19 @@ ctest --test-dir build
./build/test_datarouter ./build/test_datarouter
``` ```
当前 36 个测试用例,覆盖端口管理、类型、handler 增删、一对多广播、接收回调、错误边界。 当前 22 个测试用例,覆盖端口管理、send_type 与 handler 设置、自动广播路由、接收回调、能力元数据。
## 文件结构 ## 文件结构
``` ```
datarouter_config.h 报文类型配置(用户编辑) datarouter_config.h 发送 + 接收能力配置(用户可覆盖)
datarouter.h 公共 API + X-macro 展开 datarouter.h 公共 API + X-macro 展开
datarouter.c 实现(端口表 + handler 调用) datarouter.c 实现(端口表 + send 遍历)
example.c 使用示例 datarouter_config_template.h 外部配置模板
tests/test_datarouter.c 单元测试 example.c 使用示例
DESIGN.md 详细设计文档 tests/test_datarouter.c 单元测试(22 用例)
TEST_CASES.md 测试用例文档 DESIGN.md 详细设计文档(含 PlantUML 图)
TEST_CASES.md 测试用例文档
``` ```
## 依赖 ## 依赖
+35 -50
View File
@@ -8,7 +8,8 @@
| 编译器 | GCC 11.4.0 | | 编译器 | GCC 11.4.0 |
| 测试框架 | 自定义轻量框架(宏 + goto cleanup) | | 测试框架 | 自定义轻量框架(宏 + goto cleanup) |
| 构建系统 | CMake ≥ 3.10 | | 构建系统 | CMake ≥ 3.10 |
| 报文类型 | `sensor` / `status` / `control` 三个通道 | | 发送能力 | `sensor` / `status` |
| 接收能力 | `rtcm` / `command` |
## 运行方式 ## 运行方式
@@ -16,7 +17,6 @@
cmake -S . -B build cmake -S . -B build
cmake --build build cmake --build build
ctest --test-dir build ctest --test-dir build
# 或直接运行:
./build/test_datarouter ./build/test_datarouter
``` ```
@@ -26,78 +26,63 @@ ctest --test-dir build
| # | 测试名 | 描述 | 步骤 | 预期 | | # | 测试名 | 描述 | 步骤 | 预期 |
|---|--------|------|------|------| |---|--------|------|------|------|
| 1 | `error_uninit` | 未初始化时不能发送 | ① 不调 init 直接 `send_sensor(0, ...)` | 返回 -1 | | 1 | `error_uninit` | 未 init 时发送 | `send_sensor("d", 1)` | 返回 -1 |
### 2. 端口管理(7 个) ### 2. 端口管理(4 个)
| # | 测试名 | 描述 | 步骤 | 预期 | | # | 测试名 | 描述 | 步骤 | 预期 |
|---|--------|------|------|------| |---|--------|------|------|------|
| 3 | `port_create_basic` | 基本创建 | ① `datarouter_port_create()` | 返回 ≥ 0 | | 2 | `port_create_basic` | 基本创建 | `port_create()` | 返回 ≥ 0 |
| 4 | `port_create_multiple` | 多端口唯一 ID | ① 连续创建 3 个端口 | ID 互不相同 | | 3 | `port_create_max` | 满容量 64 | 循环创建到满再尝试 | 64 个成功,第 65 个 INVALID |
| 5 | `port_create_reuse` | 删除后 ID 复用 | ① 创建 A ② 删除 A ③ 再创建 | 新 ID == A ID | | 4 | `port_delete_invalid` | 删除无效 ID | `delete(-1)` + `delete(999)` | 不崩溃 |
| 6 | `port_delete_invalid` | 删除无效 ID | ① `delete(-1)` ② `delete(9999)` | 不崩溃 | | 5 | `port_default_state` | 默认状态 | 新建端口,查 send_type / handler count | `send_type=-1`, `count=0` |
| 7 | `port_delete_twice` | 重复删除 | ① 创建 ② 删除 ③ 再次删除 | 不崩溃 |
| 8 | `port_default_state` | 默认状态 | ① 创建端口 ② `get_type()` ③ `get_handler()` | type == -1, handler == NULL |
| 9 | `port_create_max` | 满容量边界 | ① 循环创建 64 个 ② 再创建一个 | 64 个成功,第 65 个返回 INVALID |
### 3. 类型与 handler 设置获取(7 个) ### 3. send_type 与 handler(3 个)
| # | 测试名 | 描述 | 步骤 | 预期 | | # | 测试名 | 描述 | 步骤 | 预期 |
|---|--------|------|------|------| |---|--------|------|------|------|
| 10 | `type_set_get` | 设置并获取类型 | ① `set_type(sensor)` ② `get_type()` | 返回 sensor | | 6 | `send_type_set_get` | 设置并清除 send_type | `set_send_type(sensor)` → `get` → `set(-1)` → `get` | sensor → -1 |
| 11 | `type_clear` | 清除类型 | ① `set_type(sensor)` ② `set_type(-1)` ③ `get_type()` | 返回 -1 | | 7 | `handler_add_remove` | 添加后移除 handler | add(h) count=1 → remove(h) count=0 | count 正确 |
| 12 | `type_switch` | 运行时切换类型 | ① `set_type(sensor)` ② `set_type(status)` ③ `get_type()` | 返回 status | | 8 | `handler_clear` | 清除全部 handler | add → clear | count == 0 |
| 13 | `type_from_invalid_port` | 无效端口查询类型 | ① `get_type(-1)` ② `get_type(999)` | 均返回 -1 |
| 14 | `handler_set_get` | 设置并获取 handler | ① `set_handler(f)` ② `get_handler()` | 返回 f |
| 15 | `handler_clear` | 清除 handler | ① `set_handler(f)` ② `set_handler(NULL)` ③ `get_handler()` | 返回 NULL |
| 16 | `handler_from_invalid_port` | 无效端口查询 handler | ① `get_handler(-1)` ② `get_handler(999)` | 均返回 NULL |
### 4. 通道元数据查询(4 个) ### 4. 发送 — 自动广播(11 个)
| # | 测试名 | 描述 | 步骤 | 预期 | | # | 测试名 | 描述 | 步骤 | 预期 |
|---|--------|------|------|------| |---|--------|------|------|------|
| 17 | `channel_count` | 通道计数 | ① `channel_count()` | 3 | | 9 | `send_no_match_port` | 无匹配端口 | 不创建任何端口,直接 `send_sensor` | 返回 -2 |
| 18 | `channel_names` | 通道名称 | ① 依次 `channel_name(0/1/2)` | "sensor" / "status" / "control" | | 10 | `send_one_port_one_handler` | 单端口单 handler | 创建端口中 send_type + add(h) → send | 返回 0;handler 1 次 |
| 19 | `channel_descs` | 通道描述 | ① 依次 `channel_desc(0/1/2)` | "Sensor data" / "Status report" / "Control command" | | 11 | `send_one_port_multi_handler` | 单端口双 handler | add(h) + add(h2) → send | handler 2 次 |
| 20 | `channel_invalid_id` | 无效 ID | ① `channel_name(-1/99)` ② `channel_desc(-1/99)` | 均返回 NULL | | 12 | `send_multi_ports_same_type` | 两端口同类型 | p1(sensor,h) + p2(sensor,h) → send | handler 2 次,各端口一次 |
| 13 | `send_multi_port_multi_handler` | 多端口多 handler | p1(h,h2) + p2(h3) → send | handler 3 次 |
| 14 | `send_type_filter` | 类型过滤 | p1(sensor,h) + p2(status,h) → `send_sensor` | 只触发 p1 |
| 15 | `send_after_remove_handler` | 移除 handler 后 | add(h) → send count=1 → remove(h) → send | 第二次返回 -2 |
| 16 | `send_after_delete_port` | 删除端口后 | 创建→设好→删除→send | 返回 -2 |
| 17 | `send_zero_length` | 零长度数据 | send("x", 0) | 返回 0;handler 被调 |
| 18 | `send_null_data` | NULL 数据 | send(NULL, 0) | 返回 0;handler 被调 |
| 19 | `send_multi_cap` | 多能力独立 | p1(sensor) + p2(status) → 分别 send_sensor + send_status | 各触发匹配的端口 |
### 5. 发送触发(9 个) ### 5. 接收回调(1 个)
| # | 测试名 | 描述 | 步骤 | 预期 | | # | 测试名 | 描述 | 步骤 | 预期 |
|---|--------|------|------|------| |---|--------|------|------|------|
| 21 | `send_sensor` | sensor 类型发送 | ① 创建端口 ② set_type(sensor) + set_handler(test) ③ `send_sensor(port, "hello", 5)` | 返回 0;handler 被调用,参数正确 | | 20 | `recv_callback` | 用户实现接收回调 | `on_recv_rtcm(0, "d", 1)` | 回调被触发 |
| 22 | `send_status` | status 类型发送 | 同上,用 status 通道 | 返回 0 |
| 23 | `send_control` | control 类型发送 | 同上,用 control 通道 | 返回 0 |
| 24 | `send_type_mismatch` | 类型不匹配 | ① set_type(sensor) ② `send_status(port, ...)` | 返回 -3;handler 不被调用 |
| 25 | `send_no_handler` | 未注册 handler | ① set_type(sensor) ② 不设 handler ③ `send_sensor(port, ...)` | 返回 -4 |
| 26 | `send_same_type_multiple_ports` | 同类型多端口 | ① 两端口均设 sensor + handler ② 依次发送 | 各调一次 handler |
| 27 | `send_switch_handler_runtime` | 运行时切换 handler | ① 注册 h1 发送 ② 改注册 h2 发送 | 第一次用 h1,第二次用 h2 |
| 28 | `send_zero_length` | 零长度数据 | ① 正常端口 ② `send_sensor(port, "x", 0)` | 返回 0;handler 被调用,len=0 |
| 29 | `send_null_data` | NULL 数据 | ① 正常端口 ② `send_sensor(port, NULL, 0)` | 返回 0;handler 被调用 |
### 6. 接收回调(1 个) ### 6. 能力元数据(2 个)
| # | 测试名 | 描述 | 步骤 | 预期 | | # | 测试名 | 描述 | 步骤 | 预期 |
|---|--------|------|------|------| |---|--------|------|------|------|
| 30 | `recv_callback_user_impl` | 用户实现接收回调 | ① `on_recv_sensor(port, data, len)` ② `on_recv_status(port, data, len)` | sensor 回调触发,status 不触发;再触发 status 回调,sensor 不变 | | 21 | `send_cap_meta` | 发送能力元数据 | count / name / desc / 边界 | SEND 两组正确 |
| 22 | `recv_cap_meta` | 接收能力元数据 | count / name / desc / 边界 | RECV 两组正确 |
### 7. 错误处理(2 个)
| # | 测试名 | 描述 | 步骤 | 预期 |
|---|--------|------|------|------|
| 30 | `error_invalid_port` | 无效端口 ID | ① `send_sensor(999, ...)` ② `send_sensor(-1, ...)` | 均返回 -2 |
| 31 | `error_deleted_port` | 已删除端口 | ① 创建设好类型 + handler ② 删除 ③ `send_sensor(原ID, ...)` | 返回 -2 |
## 覆盖矩阵 ## 覆盖矩阵
| 功能模块 | 用例数 | 主要验证点 | | 功能模块 | 用例数 | 主要验证点 |
|----------|:------:|------------| |----------|:------:|------------|
| 未初始化 | 1 | 未 init 调用 send → -1 | | 未初始化 | 1 | send → -1 |
| 端口生命周期 | 7 | 创建/多创建/ID复用/删除异常/满容量 | | 端口管理 | 4 | 创建/满容量/删除异常/默认状态 |
| 类型与 handler | 7 | set/get/clear/switch/无效端口 | | send_type & handler | 3 | 设置/清除/添加/移除/清空 |
| 通道元数据 | 4 | count/names/descs/boundary | | 发送自动广播 | 11 | 无匹配/单播/多 handler 单端口/多端口同类型/多端口多 handler/类型过滤/移除后/删除后/零长/NULL/多能力独立 |
| 发送触发 | 9 | 三通道/类型不匹配/无handler/多端口/切换handler/零长/NULL |
| 接收回调 | 1 | 用户实现覆盖弱符号 | | 接收回调 | 1 | 用户实现覆盖弱符号 |
| 错误处理 | 2 | 无效端口/已删除端口 | | 能力元数据 | 2 | SEND 与 RECV 两组独立查询 |
**总计:31 个测试用例** **总计:22 个测试用例**
BIN
View File
Binary file not shown.
Binary file not shown.
+16
View File
@@ -0,0 +1,16 @@
@PACKAGE_INIT@
set_and_check(DataRouter_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
set_and_check(DataRouter_LIBRARY_DIR "@PACKAGE_LIB_INSTALL_DIR@")
include("${CMAKE_CURRENT_LIST_DIR}/DataRouterTargets.cmake" OPTIONAL)
if(NOT TARGET DataRouter::DataRouter)
add_library(DataRouter::DataRouter STATIC IMPORTED)
set_target_properties(DataRouter::DataRouter PROPERTIES
IMPORTED_LOCATION "${DataRouter_LIBRARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}DataRouter${CMAKE_STATIC_LIBRARY_SUFFIX}"
INTERFACE_INCLUDE_DIRECTORIES "${DataRouter_INCLUDE_DIR}"
)
endif()
check_required_components(DataRouter)
+7
View File
@@ -0,0 +1,7 @@
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_C_COMPILER arm-none-eabi-gcc)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+6
View File
@@ -0,0 +1,6 @@
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+6
View File
@@ -0,0 +1,6 @@
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+6
View File
@@ -0,0 +1,6 @@
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR amd64)
set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+57 -66
View File
@@ -1,14 +1,9 @@
/** /**
* @file datarouter.c * @file datarouter.c
* @brief 数据路由库 —— 实现
*
* 端口表:固定大小数组,每个端口存储报文类型 + handler 列表。
* 发送触发函数逐一调用端口上注册的全部 handler,实现一对多广播。
*/ */
#include "datarouter.h" #include "datarouter.h"
#include <string.h> #include <string.h>
#include <stdio.h>
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
#define DATAROUTER_WEAK __attribute__((weak)) #define DATAROUTER_WEAK __attribute__((weak))
@@ -20,17 +15,8 @@
#define DATAROUTER_MAX_PORTS 64 #define DATAROUTER_MAX_PORTS 64
#endif #endif
/**
* @struct port_entry_t
* @brief 端口表条目
*
* @var type 报文类型(DATAROUTER_CHANNEL_xxx / -1)
* @var handlers 外设发送 handler 数组
* @var count 当前注册的 handler 数量
* @var in_use 1=已分配,0=空闲
*/
typedef struct { typedef struct {
int type; int send_type;
datarouter_send_handler_t handlers[DATAROUTER_MAX_HANDLERS_PER_PORT]; datarouter_send_handler_t handlers[DATAROUTER_MAX_HANDLERS_PER_PORT];
int count; int count;
uint8_t in_use; uint8_t in_use;
@@ -39,48 +25,62 @@ typedef struct {
static port_entry_t ports[DATAROUTER_MAX_PORTS]; static port_entry_t ports[DATAROUTER_MAX_PORTS];
static uint8_t initialized; static uint8_t initialized;
/* ---- 通道元数据(X-macro 展开) ---- */ /* ---- 元数据 ---- */
static const char *channel_names[] = { static const char *send_names[] = {
#define DATAROUTER_CHANNEL(id, desc) #id, #define DATAROUTER_SEND(id, desc) #id,
DATAROUTER_CHANNELS DATAROUTER_SEND_CAPABILITIES
#undef DATAROUTER_CHANNEL #undef DATAROUTER_SEND
}; };
static const char *channel_descs[] = { static const char *send_descs[] = {
#define DATAROUTER_CHANNEL(id, desc) desc, #define DATAROUTER_SEND(id, desc) desc,
DATAROUTER_CHANNELS DATAROUTER_SEND_CAPABILITIES
#undef DATAROUTER_CHANNEL #undef DATAROUTER_SEND
}; };
/* ---- 弱符号接收回调默认实现 ---- */ static const char *recv_names[] = {
#define DATAROUTER_RECV(id, desc) #id,
DATAROUTER_RECV_CAPABILITIES
#undef DATAROUTER_RECV
};
#define DATAROUTER_CHANNEL(id, desc) \ static const char *recv_descs[] = {
#define DATAROUTER_RECV(id, desc) desc,
DATAROUTER_RECV_CAPABILITIES
#undef DATAROUTER_RECV
};
/* ---- 弱符号接收回调 ---- */
#define DATAROUTER_RECV(id, desc) \
DATAROUTER_WEAK void datarouter_on_recv_##id( \ DATAROUTER_WEAK void datarouter_on_recv_##id( \
uint32_t port_id, const void *data, size_t len) \ uint32_t port_id, const void *data, size_t len) \
{ \ { (void)port_id; (void)data; (void)len; }
(void)port_id; (void)data; (void)len; \ DATAROUTER_RECV_CAPABILITIES
} #undef DATAROUTER_RECV
DATAROUTER_CHANNELS
#undef DATAROUTER_CHANNEL
/* ---- 发送触发函数实现 ---- */ /* ---- 发送触发函数 ---- */
#define DATAROUTER_CHANNEL(id, desc) \ #define DATAROUTER_SEND(id, desc) \
int datarouter_send_##id(uint32_t port_id, const void *data, size_t len) \ int datarouter_send_##id(const void *data, size_t len) \
{ \ { \
if (!initialized) return -1; \ if (!initialized) return -1; \
if (port_id >= DATAROUTER_MAX_PORTS || !ports[port_id].in_use) return -2; \ int called = 0; \
if (ports[port_id].type != DATAROUTER_CHANNEL_##id) return -3; \ for (uint32_t _p = 0; _p < DATAROUTER_MAX_PORTS; _p++) { \
if (ports[port_id].count == 0) return -4; \ if (!ports[_p].in_use) continue; \
for (int _i = 0; _i < ports[port_id].count; _i++) \ if (ports[_p].send_type != DATAROUTER_SEND_##id) continue; \
ports[port_id].handlers[_i](port_id, data, len); \ for (int _h = 0; _h < ports[_p].count; _h++) { \
return 0; \ ports[_p].handlers[_h](_p, data, len); \
called = 1; \
} \
} \
return called ? 0 : -2; \
} }
DATAROUTER_CHANNELS DATAROUTER_SEND_CAPABILITIES
#undef DATAROUTER_CHANNEL #undef DATAROUTER_SEND
/* ---- 端口管理 API ---- */ /* ---- 端口管理 ---- */
void datarouter_init(void) void datarouter_init(void)
{ {
@@ -100,9 +100,9 @@ datarouter_port_t datarouter_port_create(void)
if (!initialized) return DATAROUTER_INVALID_PORT; if (!initialized) return DATAROUTER_INVALID_PORT;
for (int i = 0; i < DATAROUTER_MAX_PORTS; i++) { for (int i = 0; i < DATAROUTER_MAX_PORTS; i++) {
if (!ports[i].in_use) { if (!ports[i].in_use) {
ports[i].in_use = 1; ports[i].in_use = 1;
ports[i].type = -1; ports[i].send_type = -1;
ports[i].count = 0; ports[i].count = 0;
return (datarouter_port_t)i; return (datarouter_port_t)i;
} }
} }
@@ -116,18 +116,18 @@ void datarouter_port_delete(datarouter_port_t port)
ports[port].in_use = 0; ports[port].in_use = 0;
} }
void datarouter_port_set_type(datarouter_port_t port, int channel_id) void datarouter_port_set_send_type(datarouter_port_t port, int cap_id)
{ {
if (!initialized) return; if (!initialized) return;
if (port < 0 || port >= DATAROUTER_MAX_PORTS || !ports[port].in_use) return; if (port < 0 || port >= DATAROUTER_MAX_PORTS || !ports[port].in_use) return;
ports[port].type = channel_id; ports[port].send_type = cap_id;
} }
int datarouter_port_get_type(datarouter_port_t port) int datarouter_port_get_send_type(datarouter_port_t port)
{ {
if (!initialized) return -1; if (!initialized) return -1;
if (port < 0 || port >= DATAROUTER_MAX_PORTS || !ports[port].in_use) return -1; if (port < 0 || port >= DATAROUTER_MAX_PORTS || !ports[port].in_use) return -1;
return ports[port].type; return ports[port].send_type;
} }
int datarouter_port_add_handler(datarouter_port_t port, datarouter_send_handler_t handler) int datarouter_port_add_handler(datarouter_port_t port, datarouter_send_handler_t handler)
@@ -168,21 +168,12 @@ int datarouter_port_handler_count(datarouter_port_t port)
return ports[port].count; return ports[port].count;
} }
/* ---- 通道元数据查询 ---- */ /* ---- 元数据查询 ---- */
int datarouter_channel_count(void) int datarouter_send_cap_count(void) { return DATAROUTER_SEND_CAP_COUNT; }
{ const char *datarouter_send_cap_name(int c) { return (c<0||c>=DATAROUTER_SEND_CAP_COUNT)?NULL:send_names[c]; }
return DATAROUTER_CHANNEL_COUNT; const char *datarouter_send_cap_desc(int c) { return (c<0||c>=DATAROUTER_SEND_CAP_COUNT)?NULL:send_descs[c]; }
}
const char *datarouter_channel_name(int channel_id) int datarouter_recv_cap_count(void) { return DATAROUTER_RECV_CAP_COUNT; }
{ const char *datarouter_recv_cap_name(int c) { return (c<0||c>=DATAROUTER_RECV_CAP_COUNT)?NULL:recv_names[c]; }
if (channel_id < 0 || channel_id >= DATAROUTER_CHANNEL_COUNT) return NULL; const char *datarouter_recv_cap_desc(int c) { return (c<0||c>=DATAROUTER_RECV_CAP_COUNT)?NULL:recv_descs[c]; }
return channel_names[channel_id];
}
const char *datarouter_channel_desc(int channel_id)
{
if (channel_id < 0 || channel_id >= DATAROUTER_CHANNEL_COUNT) return NULL;
return channel_descs[channel_id];
}
+56 -106
View File
@@ -4,30 +4,16 @@
* *
* ## 概念 * ## 概念
* *
* - **通道 (Channel)**:编译期声明的报文类型(如 sensor / status / control) * - **发送能力**:编译期声明的可发送报文类型,生成 datarouter_send_<id>(data, len)
* - **端口 (Port)**:运行期创建的连接点,绑定一个报文类型和多个外设发送 handler * - **接收能力**:编译期声明的可接收报文类型,生成 datarouter_on_recv_<id>(port, data, len)
* - **路由**:将报文类型关联到一个或多个物理外设发送函数 * - **端口**:运行期创建的连接点,包含 send_type + handler 列表
* - **多外设**:一个端口可注册多个 handler,发送时逐一调用全部 handler
* *
* ## 使用流程 * ## 路由
* *
* 1. 在 datarouter_config.h 中声明报文类型 * 调用 datarouter_send_<id>(data, len) 时,库遍历所有端口,
* 2. 调用 datarouter_init() 初始化 * 找到 send_type 匹配的端口,逐一调用其全部 handler。无需指定从哪个端口发送。
* 3. 通过 datarouter_port_create() 创建端口
* 4. 调用 datarouter_port_set_type() 指定端口处理的报文类型
* 5. 调用 datarouter_port_add_handler() 注册一个或多个外设发送函数
* 6. 调用 datarouter_send_<id>() 触发发送(库逐一调用所有注册的 handler)
* 7. 实现 datarouter_on_recv_<id>() 处理从外设收到的报文
* 8. datarouter_port_delete() 移除端口
* *
* ## 路由规则 * 接收侧由用户自行调用 datarouter_on_recv_<id>(port, data, len) 处理外设收到的数据。
*
* datarouter_send_<id>(port, data, len)
* → 校验端口类型
* → for each handler: handler(port, data, len)
* → return 0
*
* 接收:外设收到数据 → 用户识别报文类型 → 调 datarouter_on_recv_<id>(port, data, len)
*/ */
#ifndef DATAROUTER_H #ifndef DATAROUTER_H
@@ -36,74 +22,61 @@
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include "datarouter_config.h" #if defined(__has_include)
# if __has_include(<datarouter_config.h>)
# include <datarouter_config.h>
# else
# include "datarouter_config.h"
# endif
#else
# include "datarouter_config.h"
#endif
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
/** /* ========== 发送能力枚举 ========== */
* @enum datarouter_channel
* @brief 报文类型枚举 enum datarouter_send_cap {
* #define DATAROUTER_SEND(id, desc) DATAROUTER_SEND_##id,
* 由 DATAROUTER_CHANNELS 宏列表自动生成。 DATAROUTER_SEND_CAPABILITIES
*/ #undef DATAROUTER_SEND
enum datarouter_channel { DATAROUTER_SEND_CAP_COUNT
#define DATAROUTER_CHANNEL(id, desc) DATAROUTER_CHANNEL_##id,
DATAROUTER_CHANNELS
#undef DATAROUTER_CHANNEL
DATAROUTER_CHANNEL_COUNT
}; };
/** /* ========== 接收能力枚举 ========== */
* @brief 外设发送 handler 类型
* enum datarouter_recv_cap {
* @param[in] port_id 源端口 ID #define DATAROUTER_RECV(id, desc) DATAROUTER_RECV_##id,
* @param[in] data 数据缓冲区 DATAROUTER_RECV_CAPABILITIES
* @param[in] len 数据长度 #undef DATAROUTER_RECV
*/ DATAROUTER_RECV_CAP_COUNT
};
/* ========== 外设发送 handler ========== */
typedef void (*datarouter_send_handler_t)(uint32_t port_id, const void *data, size_t len); typedef void (*datarouter_send_handler_t)(uint32_t port_id, const void *data, size_t len);
#ifndef DATAROUTER_MAX_HANDLERS_PER_PORT #ifndef DATAROUTER_MAX_HANDLERS_PER_PORT
#define DATAROUTER_MAX_HANDLERS_PER_PORT 4 #define DATAROUTER_MAX_HANDLERS_PER_PORT 4
#endif #endif
/** /* ========== 发送触发函数 ========== */
* @defgroup send_functions 发送触发函数
*
* 每个报文类型生成一个发送触发函数。
* 内部校验端口后,逐一调用端口上注册的所有 handler。
* @{
*/
#define DATAROUTER_CHANNEL(id, desc) \ #define DATAROUTER_SEND(id, desc) \
int datarouter_send_##id(uint32_t port_id, const void *data, size_t len); int datarouter_send_##id(const void *data, size_t len);
DATAROUTER_CHANNELS DATAROUTER_SEND_CAPABILITIES
#undef DATAROUTER_CHANNEL #undef DATAROUTER_SEND
/** @} */ /* ========== 接收回调声明 ========== */
/** #define DATAROUTER_RECV(id, desc) \
* @defgroup recv_callbacks 接收回调声明
*
* 每个报文类型生成一个接收回调声明。用户实现。
* @{
*/
#define DATAROUTER_CHANNEL(id, desc) \
void datarouter_on_recv_##id(uint32_t port_id, const void *data, size_t len); void datarouter_on_recv_##id(uint32_t port_id, const void *data, size_t len);
DATAROUTER_CHANNELS DATAROUTER_RECV_CAPABILITIES
#undef DATAROUTER_CHANNEL #undef DATAROUTER_RECV
/** @} */ /* ========== 端口管理 API ========== */
/**
* @defgroup port_api 端口管理 API
*
* 每个端口可注册 N 个外设发送 handler(上限 DATAROUTER_MAX_HANDLERS_PER_PORT)。
* 发送时逐一调用全部 handler,实现一对多广播路由。
* @{
*/
typedef int32_t datarouter_port_t; typedef int32_t datarouter_port_t;
#define DATAROUTER_INVALID_PORT ((datarouter_port_t)-1) #define DATAROUTER_INVALID_PORT ((datarouter_port_t)-1)
@@ -114,46 +87,23 @@ void datarouter_reset(void);
datarouter_port_t datarouter_port_create(void); datarouter_port_t datarouter_port_create(void);
void datarouter_port_delete(datarouter_port_t port); void datarouter_port_delete(datarouter_port_t port);
void datarouter_port_set_type(datarouter_port_t port, int channel_id); void datarouter_port_set_send_type(datarouter_port_t port, int cap_id);
int datarouter_port_get_type(datarouter_port_t port); int datarouter_port_get_send_type(datarouter_port_t port);
/** int datarouter_port_add_handler(datarouter_port_t port, datarouter_send_handler_t handler);
* @brief 为端口添加一个外设发送 handler int datarouter_port_remove_handler(datarouter_port_t port, datarouter_send_handler_t handler);
* @param[in] port 端口 ID
* @param[in] handler 发送函数指针(不可为 NULL)
* @retval 0 成功
* @retval -1 端口无效
* @retval -2 handler 列表已满
* @retval -3 handler 已存在(不重复添加)
*/
int datarouter_port_add_handler(datarouter_port_t port, datarouter_send_handler_t handler);
/**
* @brief 从端口移除一个外设发送 handler
* @param[in] port 端口 ID
* @param[in] handler 要移除的函数指针
* @retval 0 成功
* @retval -1 端口无效
* @retval -2 handler 未找到
*/
int datarouter_port_remove_handler(datarouter_port_t port, datarouter_send_handler_t handler);
/** @brief 清除端口所有 handler */
void datarouter_port_clear_handlers(datarouter_port_t port); void datarouter_port_clear_handlers(datarouter_port_t port);
int datarouter_port_handler_count(datarouter_port_t port);
/** @brief 获取端口当前 handler 数量 @return handler 数量,-1 表示端口无效 */ /* ========== 能力元数据查询 ========== */
int datarouter_port_handler_count(datarouter_port_t port);
/** @} */ int datarouter_send_cap_count(void);
const char *datarouter_send_cap_name(int cap_id);
const char *datarouter_send_cap_desc(int cap_id);
/** int datarouter_recv_cap_count(void);
* @defgroup introspection 报文类型元数据查询 const char *datarouter_recv_cap_name(int cap_id);
* @{ const char *datarouter_recv_cap_desc(int cap_id);
*/
int datarouter_channel_count(void);
const char *datarouter_channel_name(int channel_id);
const char *datarouter_channel_desc(int channel_id);
/** @} */
#ifdef __cplusplus #ifdef __cplusplus
} }
+27 -21
View File
@@ -1,34 +1,40 @@
/** /**
* @file datarouter_config.h * @file datarouter_config.h
* @brief 数据报文类型配置 * @brief 数据路由默认配置
* *
* 用户在此文件中定义所有报文类型(通道)。 * 这是库内置的默认配置,提供示例能力声明。
* 每个报文类型通过 DATAROUTER_CHANNEL(id, desc) 宏声明, * 用户可自定义此文件以实现自己的报文类型:
* 库在编译期据此自动生成: *
* - 通道枚举值 DATAROUTER_CHANNEL_<id> * 1. 复制当前文件到你的项目目录
* - 发送触发函数 datarouter_send_<id>(port, data, len) * 2. 按需修改 DATAROUTER_SEND_CAPABILITIES / DATAROUTER_RECV_CAPABILITIES
* - 接收回调声明 datarouter_on_recv_<id>(port, data, len) * 3. 编译时添加 -I /path/to/your/config 覆盖库的默认配置
*
* 覆盖后库将使用你的版本,无需修改库源代码。
*/ */
#ifndef DATAROUTER_CONFIG_H #ifndef DATAROUTER_CONFIG_H
#define DATAROUTER_CONFIG_H #define DATAROUTER_CONFIG_H
/** /**
* @def DATAROUTER_CHANNELS * @def DATAROUTER_SEND_CAPABILITIES
* @brief 报文类型列表宏 * @brief 发送能力列表
* *
* 每行格式:DATAROUTER_CHANNEL(标识符, "描述文字") * 每行格式:DATAROUTER_SEND(标识符, "描述")
* 用户按需增删改。每个标识符对应一种报文类型。 * 每项生成:发送触发函数 datarouter_send_<id>(port, data, len)
*
* @code
* #define DATAROUTER_CHANNELS \
* DATAROUTER_CHANNEL(sensor, "Sensor data") \
* DATAROUTER_CHANNEL(control, "Control command")
* @endcode
*/ */
#define DATAROUTER_CHANNELS \ #define DATAROUTER_SEND_CAPABILITIES \
DATAROUTER_CHANNEL(sensor, "Sensor data") \ DATAROUTER_SEND(sensor, "Sensor data report") \
DATAROUTER_CHANNEL(status, "Status report") \ DATAROUTER_SEND(status, "Device status report")
DATAROUTER_CHANNEL(control, "Control command")
/**
* @def DATAROUTER_RECV_CAPABILITIES
* @brief 接收能力列表
*
* 每行格式:DATAROUTER_RECV(标识符, "描述")
* 每项生成:接收回调声明 datarouter_on_recv_<id>(port, data, len)
*/
#define DATAROUTER_RECV_CAPABILITIES \
DATAROUTER_RECV(rtcm, "RTCM correction data") \
DATAROUTER_RECV(command, "Remote control command")
#endif /* DATAROUTER_CONFIG_H */ #endif /* DATAROUTER_CONFIG_H */
+51
View File
@@ -0,0 +1,51 @@
/**
* @file datarouter_config_template.h
* @brief 数据路由配置模板 —— 复制此文件到项目目录并修改
*
* 使用方法:
* 1. 将此文件复制到你的项目目录,重命名为 datarouter_config.h
* 2. 修改下面的 DATAROUTER_SEND_CAPABILITIES 和 DATAROUTER_RECV_CAPABILITIES
* 3. 编译时添加 -I /path/to/your/project
* 4. 库将自动使用你的配置,无需修改库源代码
*
* 格式说明:
* 发送能力:DATAROUTER_SEND(标识符, "描述")
* 接收能力:DATAROUTER_RECV(标识符, "描述")
*
* 标识符只能包含字母、数字、下划线。
* 描述为字符串字面量。
*
* 示例配置:
* // 传感器设备:只需发送
* #define DATAROUTER_SEND_CAPABILITIES \
* DATAROUTER_SEND(sensor, "Sensor data report")
* #define DATAROUTER_RECV_CAPABILITIES
*
* // 遥控设备:只需接收
* #define DATAROUTER_SEND_CAPABILITIES
* #define DATAROUTER_RECV_CAPABILITIES \
* DATAROUTER_RECV(command, "Remote command")
*
* // 网关设备:同时收发
* #define DATAROUTER_SEND_CAPABILITIES \
* DATAROUTER_SEND(status, "Status report")
* #define DATAROUTER_RECV_CAPABILITIES \
* DATAROUTER_RECV(config, "Configuration update")
*/
#ifndef DATAROUTER_CONFIG_H
#define DATAROUTER_CONFIG_H
/* ========== 在此声明你的发送能力 ========== */
#define DATAROUTER_SEND_CAPABILITIES \
DATAROUTER_SEND(sensor, "Sensor data report") \
DATAROUTER_SEND(status, "Device status report")
/* ========== 在此声明你的接收能力 ========== */
#define DATAROUTER_RECV_CAPABILITIES \
DATAROUTER_RECV(rtcm, "RTCM correction data") \
DATAROUTER_RECV(command, "Remote control command")
#endif /* DATAROUTER_CONFIG_H */
+35 -67
View File
@@ -1,105 +1,73 @@
/** /**
* @file example.c * @file example.c
* @brief 数据路由库使用示例
* *
* 展示: * 调用 datarouter_send_sensor(data, len) 无需指定端口,
* 1. 编译期声明报文类型 → 自动生成发送触发函数和接收回调声明 * 库自动广播到所有 send_type=sensor 的端口。
* 2. 创建端口、设置报文类型
* 3. 为一个端口注册多个外设 handler(一对多广播路由)
* 4. 运行时增删 handler,切换路由
* 5. 实现接收回调处理外设收到的数据
*/ */
#include "datarouter.h" #include "datarouter.h"
#include <stdio.h> #include <stdio.h>
#include <string.h>
/* ---- 外设发送 handler 实现 ---- */
static void uart_send(uint32_t port_id, const void *data, size_t len) static void uart_send(uint32_t port_id, const void *data, size_t len)
{ {
printf("[UART] port %u: %.*s\n", port_id, (int)len, (const char *)data); printf("[UART port %u] %.*s\n", port_id, (int)len, (const char *)data);
} }
static void tcp_send(uint32_t port_id, const void *data, size_t len) static void tcp_send(uint32_t port_id, const void *data, size_t len)
{ {
printf("[TCP] port %u: %.*s\n", port_id, (int)len, (const char *)data); printf("[TCP port %u] %.*s\n", port_id, (int)len, (const char *)data);
}
static void file_send(uint32_t port_id, const void *data, size_t len)
{
printf("[FILE] port %u: %.*s\n", port_id, (int)len, (const char *)data);
} }
static void can_send(uint32_t port_id, const void *data, size_t len) static void can_send(uint32_t port_id, const void *data, size_t len)
{ {
printf("[CAN] port %u: %.*s\n", port_id, (int)len, (const char *)data); printf("[CAN port %u] %.*s\n", port_id, (int)len, (const char *)data);
} }
/* ---- 接收回调实现 ---- */ void datarouter_on_recv_command(uint32_t port_id, const void *data, size_t len)
void datarouter_on_recv_sensor(uint32_t port_id, const void *data, size_t len)
{ {
printf("[RX:sensor] port %u: %.*s\n", port_id, (int)len, (const char *)data); printf("[RX:cmd port %u] %.*s\n", port_id, (int)len, (const char *)data);
} }
void datarouter_on_recv_status(uint32_t port_id, const void *data, size_t len)
{
printf("[RX:status] port %u: %.*s\n", port_id, (int)len, (const char *)data);
}
/* ---- main ---- */
int main(void) int main(void)
{ {
datarouter_init(); datarouter_init();
printf("Channels:\n"); printf("Capabilities:\n");
for (int i = 0; i < datarouter_channel_count(); i++) for (int i = 0; i < datarouter_send_cap_count(); i++)
printf(" [%d] %s - %s\n", i, printf(" SEND [%d] %s - %s\n", i,
datarouter_channel_name(i), datarouter_send_cap_name(i), datarouter_send_cap_desc(i));
datarouter_channel_desc(i));
/* /*
* 创建一个 sensor 端口,注册多个 handler。 * 创建两个端口,都设 send_type=sensor,各绑不同外设。
* 一次 send_sensor() 调用同时路由到 UART + TCP + CAN。 * 调用 datarouter_send_sensor() 时两个端口同时触发。
*/ */
datarouter_port_t sensor_port = datarouter_port_create(); datarouter_port_t p1 = datarouter_port_create();
datarouter_port_set_type(sensor_port, DATAROUTER_CHANNEL_sensor); datarouter_port_set_send_type(p1, DATAROUTER_SEND_sensor);
datarouter_port_add_handler(sensor_port, uart_send); datarouter_port_add_handler(p1, uart_send);
datarouter_port_add_handler(sensor_port, tcp_send); datarouter_port_add_handler(p1, tcp_send);
datarouter_port_add_handler(sensor_port, can_send);
printf("\n--- send sensor (UART + TCP + CAN) ---\n"); datarouter_port_t p2 = datarouter_port_create();
datarouter_send_sensor(sensor_port, "temperature:25.3", 16); datarouter_port_set_send_type(p2, DATAROUTER_SEND_sensor);
datarouter_port_add_handler(p2, can_send);
/* /* 不指定端口,自动广播到所有 sensor 端口 */
* 运行时移除 TCP handler,只剩 UART + CAN。 printf("\n--- send_sensor (auto-route to ALL sensor ports) ---\n");
*/ datarouter_send_sensor("temperature:25.3", 16);
printf("\n--- remove TCP, keep UART + CAN ---\n");
datarouter_port_remove_handler(sensor_port, tcp_send);
datarouter_send_sensor(sensor_port, "temperature:26.1", 16);
/* /* 运行时移除一个 handler,下次发送不再触发它 */
* 运行时再添加 FILE handler。 printf("\n--- remove TCP handler from p1, resend ---\n");
*/ datarouter_port_remove_handler(p1, tcp_send);
printf("\n--- add FILE back, now UART + CAN + FILE ---\n"); datarouter_send_sensor("temperature:26.1", 16);
datarouter_port_add_handler(sensor_port, file_send);
datarouter_send_sensor(sensor_port, "temperature:27.0", 16);
/* /* 接收侧:用户自行调用回调 */
* 清除所有 handler,不再输出到任何外设。 printf("\n--- recv command ---\n");
*/ datarouter_on_recv_command(0, "reboot", 6);
printf("\n--- clear all handlers, nothing sent ---\n");
datarouter_port_clear_handlers(sensor_port);
int ret = datarouter_send_sensor(sensor_port, "lost", 4);
printf("send_sensor after clear returned %d\n", ret);
/* ---- 接收 ---- */ /* 删除一个端口,触发时不再走它 */
printf("\n--- simulate peripheral receive ---\n"); printf("\n--- delete p2 (CAN), resend ---\n");
datarouter_on_recv_sensor(0, "temperature:24.8", 16); datarouter_port_delete(p2);
datarouter_on_recv_status(1, "status:ERROR", 12); datarouter_send_sensor("temperature:27.0", 16);
datarouter_port_delete(sensor_port); datarouter_port_delete(p1);
return 0; return 0;
} }
+171 -339
View File
@@ -1,15 +1,5 @@
/** /**
* @file test_datarouter.c * @file test_datarouter.c
* @brief 数据路由库单元测试
*
* 覆盖:
* - 端口管理(创建/删除/复用/边界)
* - 端口类型与多 handler 增删查
* - 通道元数据查询
* - 一对多广播发送(多 handler 逐一调用)
* - 运行时增删 handler 改变路由
* - 接收回调
* - 错误处理
*/ */
#include "datarouter.h" #include "datarouter.h"
@@ -17,7 +7,7 @@
#include <string.h> #include <string.h>
/* ================================================================ /* ================================================================
* 测试框架 * 框架
* ================================================================ */ * ================================================================ */
static int _total_run, _total_pass, _total_fail; static int _total_run, _total_pass, _total_fail;
@@ -66,34 +56,19 @@ cleanup: \
* ================================================================ */ * ================================================================ */
static int _h_cnt; static int _h_cnt;
static int _h_total; static int _h_port[16];
static int _h_ids[16]; static int _h_pos;
static void _h(uint32_t p, const void *d, size_t l) static void _h(uint32_t p, const void *d, size_t l) { (void)d; (void)l; _h_cnt++; if (_h_pos<16) _h_port[_h_pos++]=p; }
{ static void _h2(uint32_t p, const void *d, size_t l) { (void)d; (void)l; _h_cnt++; if (_h_pos<16) _h_port[_h_pos++]=p; }
(void)d; (void)l; static void _h3(uint32_t p, const void *d, size_t l) { (void)d; (void)l; _h_cnt++; if (_h_pos<16) _h_port[_h_pos++]=p; }
if (_h_cnt < 16) _h_ids[_h_cnt] = (int)p; static void reset_h(void) { _h_cnt = 0; _h_pos = 0; }
_h_cnt++;
_h_total++;
}
static void _h_a(uint32_t p, const void *d, size_t l) { (void)p; (void)d; (void)l; _h_cnt++; }
static void _h_b(uint32_t p, const void *d, size_t l) { (void)p; (void)d; (void)l; _h_cnt++; }
static void _h_c(uint32_t p, const void *d, size_t l) { (void)p; (void)d; (void)l; _h_cnt++; }
static void _h_d(uint32_t p, const void *d, size_t l) { (void)p; (void)d; (void)l; _h_cnt++; }
static void reset_hstate(void)
{
_h_cnt = _h_total = 0;
memset(_h_ids, 0, sizeof(_h_ids));
}
/* ================================================================ /* ================================================================
* 1. 未初始化 * 1. 未初始化
* ================================================================ */ * ================================================================ */
TEST_START(error_uninit) TEST_START(error_uninit)
int ret = datarouter_send_sensor(0, "d", 1); CHECK(datarouter_send_sensor("d", 1) == -1, "expected -1");
CHECK(ret == -1, "expected -1, got %d", ret);
TEST_END() TEST_END()
/* ================================================================ /* ================================================================
@@ -101,341 +76,219 @@ TEST_END()
* ================================================================ */ * ================================================================ */
TEST_START(port_create_basic) TEST_START(port_create_basic)
datarouter_port_t p = FIXTURE_CREATE(&_fix); CHECK(FIXTURE_CREATE(&_fix) >= 0, NULL);
CHECK(p >= 0, "create returned %d", p);
TEST_END() TEST_END()
TEST_START(port_create_multiple) TEST_START(port_create_max)
datarouter_port_t p1 = FIXTURE_CREATE(&_fix); int c = 0;
datarouter_port_t p2 = FIXTURE_CREATE(&_fix); for (int i = 0; i < 64; i++) {
datarouter_port_t p3 = FIXTURE_CREATE(&_fix); datarouter_port_t p = datarouter_port_create();
CHECK(p1 >= 0 && p2 >= 0 && p3 >= 0, "some invalid"); if (p == DATAROUTER_INVALID_PORT) break;
CHECK(p1 != p2 && p2 != p3, "IDs should be unique"); FIXTURE_PUSH(&_fix, p); c++;
TEST_END() }
CHECK(c >= 64, "got %d", c);
TEST_START(port_create_reuse) CHECK(datarouter_port_create() == DATAROUTER_INVALID_PORT, NULL);
datarouter_port_t p1 = FIXTURE_CREATE(&_fix);
datarouter_port_delete(p1);
datarouter_port_t p2 = FIXTURE_CREATE(&_fix);
CHECK(p1 == p2, "expected reuse ID %d, got %d", p1, p2);
TEST_END() TEST_END()
TEST_START(port_delete_invalid) TEST_START(port_delete_invalid)
datarouter_port_delete(-1); datarouter_port_delete(-1); datarouter_port_delete(999);
datarouter_port_delete(9999);
TEST_END()
TEST_START(port_delete_twice)
datarouter_port_t p = FIXTURE_CREATE(&_fix);
datarouter_port_delete(p);
datarouter_port_delete(p);
_fix.count--;
TEST_END() TEST_END()
TEST_START(port_default_state) TEST_START(port_default_state)
datarouter_port_t p = FIXTURE_CREATE(&_fix); datarouter_port_t p = FIXTURE_CREATE(&_fix);
CHECK(datarouter_port_get_type(p) == -1, "type should be -1"); CHECK(datarouter_port_get_send_type(p) == -1, NULL);
CHECK(datarouter_port_handler_count(p) == 0, "handler count should be 0"); CHECK(datarouter_port_handler_count(p) == 0, NULL);
TEST_END()
TEST_START(port_create_max)
int created = 0;
for (int i = 0; i < 64; i++) {
datarouter_port_t p = datarouter_port_create();
if (p == DATAROUTER_INVALID_PORT) break;
FIXTURE_PUSH(&_fix, p);
created++;
}
CHECK(created >= 64, "expected 64, got %d", created);
CHECK(datarouter_port_create() == DATAROUTER_INVALID_PORT,
"expected INVALID after exhaustion");
TEST_END() TEST_END()
/* ================================================================ /* ================================================================
* 3. 类型 * 3. send_type 与 handler
* ================================================================ */ * ================================================================ */
TEST_START(type_set_get) TEST_START(send_type_set_get)
datarouter_port_t p = FIXTURE_CREATE(&_fix); datarouter_port_t p = FIXTURE_CREATE(&_fix);
datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor); datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor);
CHECK(datarouter_port_get_type(p) == DATAROUTER_CHANNEL_sensor, "type mismatch"); CHECK(datarouter_port_get_send_type(p) == DATAROUTER_SEND_sensor, NULL);
datarouter_port_set_send_type(p, -1);
CHECK(datarouter_port_get_send_type(p) == -1, NULL);
TEST_END() TEST_END()
TEST_START(type_clear) TEST_START(handler_add_remove)
datarouter_port_t p = FIXTURE_CREATE(&_fix); datarouter_port_t p = FIXTURE_CREATE(&_fix);
datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor); CHECK(datarouter_port_add_handler(p, _h) == 0, NULL);
datarouter_port_set_type(p, -1); CHECK(datarouter_port_handler_count(p) == 1, NULL);
CHECK(datarouter_port_get_type(p) == -1, "should be -1"); CHECK(datarouter_port_remove_handler(p, _h) == 0, NULL);
TEST_END() CHECK(datarouter_port_handler_count(p) == 0, NULL);
TEST_START(type_switch)
datarouter_port_t p = FIXTURE_CREATE(&_fix);
datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor);
datarouter_port_set_type(p, DATAROUTER_CHANNEL_status);
CHECK(datarouter_port_get_type(p) == DATAROUTER_CHANNEL_status, "should be status");
TEST_END()
TEST_START(type_from_invalid)
CHECK(datarouter_port_get_type(-1) == -1, NULL);
CHECK(datarouter_port_get_type(999) == -1, NULL);
TEST_END()
/* ================================================================
* 4. 多 handler 增删查
* ================================================================ */
TEST_START(handler_add_one)
datarouter_port_t p = FIXTURE_CREATE(&_fix);
CHECK(datarouter_port_add_handler(p, _h) == 0, "add failed");
CHECK(datarouter_port_handler_count(p) == 1, "count=%d", datarouter_port_handler_count(p));
TEST_END()
TEST_START(handler_add_multiple)
datarouter_port_t p = FIXTURE_CREATE(&_fix);
CHECK(datarouter_port_add_handler(p, _h_a) == 0, "add a");
CHECK(datarouter_port_add_handler(p, _h_b) == 0, "add b");
CHECK(datarouter_port_add_handler(p, _h_c) == 0, "add c");
CHECK(datarouter_port_handler_count(p) == 3, "count=%d", datarouter_port_handler_count(p));
TEST_END()
TEST_START(handler_add_duplicate)
datarouter_port_t p = FIXTURE_CREATE(&_fix);
CHECK(datarouter_port_add_handler(p, _h) == 0, "first add");
CHECK(datarouter_port_add_handler(p, _h) == -3, "duplicate should return -3");
TEST_END()
TEST_START(handler_add_max)
datarouter_port_t p = FIXTURE_CREATE(&_fix);
CHECK(datarouter_port_add_handler(p, _h_a) == 0, "add a");
CHECK(datarouter_port_add_handler(p, _h_b) == 0, "add b");
CHECK(datarouter_port_add_handler(p, _h_c) == 0, "add c");
CHECK(datarouter_port_add_handler(p, _h_d) == 0, "add d");
CHECK(datarouter_port_handler_count(p) == DATAROUTER_MAX_HANDLERS_PER_PORT,
"count=%d", datarouter_port_handler_count(p));
/* 第 5 个应返回 -2(已满) */
CHECK(datarouter_port_add_handler(p, _h) == -2,
"expected -2 (full), got %d", datarouter_port_add_handler(p, _h));
TEST_END()
TEST_START(handler_remove)
datarouter_port_t p = FIXTURE_CREATE(&_fix);
datarouter_port_add_handler(p, _h_a);
datarouter_port_add_handler(p, _h_b);
datarouter_port_add_handler(p, _h_c);
CHECK(datarouter_port_remove_handler(p, _h_b) == 0, "remove b");
CHECK(datarouter_port_handler_count(p) == 2, "count should be 2, got %d", datarouter_port_handler_count(p));
TEST_END()
TEST_START(handler_remove_not_found)
datarouter_port_t p = FIXTURE_CREATE(&_fix);
CHECK(datarouter_port_remove_handler(p, _h) == -2, "expected -2");
TEST_END() TEST_END()
TEST_START(handler_clear) TEST_START(handler_clear)
datarouter_port_t p = FIXTURE_CREATE(&_fix); datarouter_port_t p = FIXTURE_CREATE(&_fix);
datarouter_port_add_handler(p, _h_a); datarouter_port_add_handler(p, _h);
datarouter_port_add_handler(p, _h_b);
datarouter_port_clear_handlers(p); datarouter_port_clear_handlers(p);
CHECK(datarouter_port_handler_count(p) == 0, "count should be 0"); CHECK(datarouter_port_handler_count(p) == 0, NULL);
TEST_END()
TEST_START(handler_from_invalid)
CHECK(datarouter_port_handler_count(-1) == -1, NULL);
CHECK(datarouter_port_handler_count(999) == -1, NULL);
CHECK(datarouter_port_add_handler(-1, _h) == -1, NULL);
CHECK(datarouter_port_remove_handler(999, _h) == -1, NULL);
TEST_END() TEST_END()
/* ================================================================ /* ================================================================
* 5. 通道元数据 * 4. 发送 — 自动广播
* ================================================================ */ * ================================================================ */
TEST_START(channel_count) TEST_START(send_no_match_port)
CHECK(datarouter_channel_count() == 3, "expected 3"); /* 没有端口配置该 send_type */
CHECK(datarouter_send_sensor("d", 1) == -2, "expected -2");
TEST_END() TEST_END()
TEST_START(channel_names) TEST_START(send_one_port_one_handler)
CHECK(strcmp(datarouter_channel_name(0), "sensor") == 0, NULL);
CHECK(strcmp(datarouter_channel_name(1), "status") == 0, NULL);
CHECK(strcmp(datarouter_channel_name(2), "control") == 0, NULL);
TEST_END()
TEST_START(channel_descs)
CHECK(strcmp(datarouter_channel_desc(0), "Sensor data") == 0, NULL);
CHECK(strcmp(datarouter_channel_desc(1), "Status report") == 0, NULL);
CHECK(strcmp(datarouter_channel_desc(2), "Control command") == 0, NULL);
TEST_END()
TEST_START(channel_invalid_id)
CHECK(datarouter_channel_name(-1) == NULL, NULL);
CHECK(datarouter_channel_name(99) == NULL, NULL);
CHECK(datarouter_channel_desc(-1) == NULL, NULL);
CHECK(datarouter_channel_desc(99) == NULL, NULL);
TEST_END()
/* ================================================================
* 6. 发送 — 一对多广播
* ================================================================ */
TEST_START(send_one_handler)
datarouter_port_t p = FIXTURE_CREATE(&_fix); datarouter_port_t p = FIXTURE_CREATE(&_fix);
datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor); datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor);
datarouter_port_add_handler(p, _h); datarouter_port_add_handler(p, _h);
reset_h();
reset_hstate(); CHECK(datarouter_send_sensor("d", 1) == 0, NULL);
CHECK(datarouter_send_sensor(p, "d", 1) == 0, "send failed"); CHECK(_h_cnt == 1, "cnt=%d", _h_cnt);
CHECK(_h_cnt == 1, "handler called %d times", _h_cnt); CHECK(_h_port[0] == (int)p, "port=%d", _h_port[0]);
TEST_END() TEST_END()
TEST_START(send_multi_handlers) TEST_START(send_one_port_multi_handler)
datarouter_port_t p = FIXTURE_CREATE(&_fix); datarouter_port_t p = FIXTURE_CREATE(&_fix);
datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor); datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor);
datarouter_port_add_handler(p, _h_a);
datarouter_port_add_handler(p, _h_b);
datarouter_port_add_handler(p, _h_c);
reset_hstate();
CHECK(datarouter_send_sensor(p, "d", 1) == 0, "send failed");
CHECK(_h_cnt == 3, "expected 3 handler calls, got %d", _h_cnt);
TEST_END()
TEST_START(send_type_mismatch)
datarouter_port_t p = FIXTURE_CREATE(&_fix);
datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor);
datarouter_port_add_handler(p, _h); datarouter_port_add_handler(p, _h);
datarouter_port_add_handler(p, _h2);
reset_hstate(); reset_h();
CHECK(datarouter_send_status(p, "d", 1) == -3, "expected -3"); CHECK(datarouter_send_sensor("d", 1) == 0, NULL);
CHECK(_h_cnt == 0, "handler should not fire"); CHECK(_h_cnt == 2, "cnt=%d", _h_cnt);
TEST_END() TEST_END()
TEST_START(send_no_handler) TEST_START(send_multi_ports_same_type)
datarouter_port_t p = FIXTURE_CREATE(&_fix); /* 两个端口同类型,各一个 handler */
datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor);
reset_hstate();
CHECK(datarouter_send_sensor(p, "d", 1) == -4, "expected -4");
TEST_END()
TEST_START(send_three_channels)
/* 三个通道各一个端口 */
datarouter_port_t p1 = FIXTURE_CREATE(&_fix); datarouter_port_t p1 = FIXTURE_CREATE(&_fix);
datarouter_port_t p2 = FIXTURE_CREATE(&_fix); datarouter_port_t p2 = FIXTURE_CREATE(&_fix);
datarouter_port_t p3 = FIXTURE_CREATE(&_fix); datarouter_port_set_send_type(p1, DATAROUTER_SEND_sensor);
datarouter_port_set_type(p1, DATAROUTER_CHANNEL_sensor); datarouter_port_set_send_type(p2, DATAROUTER_SEND_sensor);
datarouter_port_set_type(p2, DATAROUTER_CHANNEL_status);
datarouter_port_set_type(p3, DATAROUTER_CHANNEL_control);
datarouter_port_add_handler(p1, _h); datarouter_port_add_handler(p1, _h);
datarouter_port_add_handler(p2, _h); datarouter_port_add_handler(p2, _h);
datarouter_port_add_handler(p3, _h); reset_h();
CHECK(datarouter_send_sensor("d", 1) == 0, NULL);
reset_hstate(); CHECK(_h_cnt == 2, "cnt=%d", _h_cnt);
CHECK(datarouter_send_sensor(p1, "a", 1) == 0, NULL); /* p1 和 p2 各触发一次 */
CHECK(datarouter_send_status(p2, "b", 1) == 0, NULL); CHECK((_h_port[0]==p1 && _h_port[1]==p2) ||
CHECK(datarouter_send_control(p3, "c", 1) == 0, NULL); (_h_port[0]==p2 && _h_port[1]==p1), "ports %d,%d", _h_port[0], _h_port[1]);
CHECK(_h_cnt == 3, "expected 3 total calls, got %d", _h_cnt);
TEST_END() TEST_END()
/* ================================================================ TEST_START(send_multi_port_multi_handler)
* 7. 发送 — 运行时增删 handler datarouter_port_t p1 = FIXTURE_CREATE(&_fix);
* ================================================================ */ datarouter_port_t p2 = FIXTURE_CREATE(&_fix);
datarouter_port_set_send_type(p1, DATAROUTER_SEND_sensor);
datarouter_port_set_send_type(p2, DATAROUTER_SEND_sensor);
datarouter_port_add_handler(p1, _h);
datarouter_port_add_handler(p1, _h2); /* p1 两个 */
datarouter_port_add_handler(p2, _h3); /* p2 一个 */
reset_h();
CHECK(datarouter_send_sensor("d", 1) == 0, NULL);
CHECK(_h_cnt == 3, "cnt=%d", _h_cnt);
TEST_END()
TEST_START(send_remove_handler) TEST_START(send_type_filter)
/* 两个端口不同类型,只触发匹配的 */
datarouter_port_t p1 = FIXTURE_CREATE(&_fix);
datarouter_port_t p2 = FIXTURE_CREATE(&_fix);
datarouter_port_set_send_type(p1, DATAROUTER_SEND_sensor);
datarouter_port_set_send_type(p2, DATAROUTER_SEND_status);
datarouter_port_add_handler(p1, _h);
datarouter_port_add_handler(p2, _h);
reset_h();
CHECK(datarouter_send_sensor("d", 1) == 0, NULL);
CHECK(_h_cnt == 1, "cnt=%d", _h_cnt);
CHECK(_h_port[0] == (int)p1, "only p1 should fire");
TEST_END()
TEST_START(send_after_remove_handler)
datarouter_port_t p = FIXTURE_CREATE(&_fix); datarouter_port_t p = FIXTURE_CREATE(&_fix);
datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor); datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor);
datarouter_port_add_handler(p, _h_a); datarouter_port_add_handler(p, _h);
datarouter_port_add_handler(p, _h_b); reset_h(); datarouter_send_sensor("d", 1); CHECK(_h_cnt == 1, NULL);
datarouter_port_remove_handler(p, _h);
reset_hstate(); CHECK(datarouter_send_sensor("d", 1) == -2, "expected -2 after remove");
datarouter_send_sensor(p, "d", 1);
CHECK(_h_cnt == 2, "2 handlers: count=%d", _h_cnt);
datarouter_port_remove_handler(p, _h_a);
reset_hstate();
datarouter_send_sensor(p, "d", 1);
CHECK(_h_cnt == 1, "after remove: count=%d", _h_cnt);
TEST_END() TEST_END()
TEST_START(send_clear_handlers) TEST_START(send_after_delete_port)
datarouter_port_t p = FIXTURE_CREATE(&_fix); datarouter_port_t p = FIXTURE_CREATE(&_fix);
datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor); datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor);
datarouter_port_add_handler(p, _h_a);
datarouter_port_add_handler(p, _h_b);
datarouter_port_clear_handlers(p);
CHECK(datarouter_send_sensor(p, "d", 1) == -4, "expected -4 after clear");
TEST_END()
/* ================================================================
* 8. 接收回调
* ================================================================ */
static int _rx_s;
static void rx_s(uint32_t p, const void *d, size_t l) { (void)p; (void)d; (void)l; _rx_s++; }
static int _rx_t;
static void rx_t(uint32_t p, const void *d, size_t l) { (void)p; (void)d; (void)l; _rx_t++; }
void datarouter_on_recv_sensor(uint32_t p, const void *d, size_t l) { rx_s(p, d, l); }
void datarouter_on_recv_status(uint32_t p, const void *d, size_t l) { rx_t(p, d, l); }
TEST_START(recv_callback)
_rx_s = _rx_t = 0;
datarouter_on_recv_sensor(0, "data", 4);
CHECK(_rx_s == 1, "sensor cb: %d", _rx_s);
CHECK(_rx_t == 0, "status cb: %d", _rx_t);
datarouter_on_recv_status(1, "data", 4);
CHECK(_rx_t == 1, "status cb: %d", _rx_t);
CHECK(_rx_s == 1, "sensor cb unchanged: %d", _rx_s);
TEST_END()
/* ================================================================
* 9. 错误处理
* ================================================================ */
TEST_START(error_invalid_port)
CHECK(datarouter_send_sensor(999, "d", 1) == -2, NULL);
CHECK(datarouter_send_sensor(-1, "d", 1) == -2, NULL);
TEST_END()
TEST_START(error_deleted_port)
datarouter_port_t p = FIXTURE_CREATE(&_fix);
datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor);
datarouter_port_add_handler(p, _h); datarouter_port_add_handler(p, _h);
datarouter_port_delete(p); datarouter_port_delete(p);
CHECK(datarouter_send_sensor(p, "d", 1) == -2, "expected -2"); CHECK(datarouter_send_sensor("d", 1) == -2, "expected -2");
_fix.count--; _fix.count--;
TEST_END() TEST_END()
/* ================================================================
* 10. 零长与 NULL
* ================================================================ */
TEST_START(send_zero_length) TEST_START(send_zero_length)
datarouter_port_t p = FIXTURE_CREATE(&_fix); datarouter_port_t p = FIXTURE_CREATE(&_fix);
datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor); datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor);
datarouter_port_add_handler(p, _h); datarouter_port_add_handler(p, _h);
reset_h();
reset_hstate(); CHECK(datarouter_send_sensor("x", 0) == 0, NULL);
CHECK(datarouter_send_sensor(p, "x", 0) == 0, NULL); CHECK(_h_cnt == 1, NULL);
CHECK(_h_cnt == 1, "handler called %d", _h_cnt);
TEST_END() TEST_END()
TEST_START(send_null_data) TEST_START(send_null_data)
datarouter_port_t p = FIXTURE_CREATE(&_fix); datarouter_port_t p = FIXTURE_CREATE(&_fix);
datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor); datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor);
datarouter_port_add_handler(p, _h); datarouter_port_add_handler(p, _h);
reset_h();
CHECK(datarouter_send_sensor(NULL, 0) == 0, NULL);
CHECK(_h_cnt == 1, NULL);
TEST_END()
reset_hstate(); TEST_START(send_multi_cap)
CHECK(datarouter_send_sensor(p, NULL, 0) == 0, NULL); /* 发送 status 不影响 sensor 端口 */
CHECK(_h_cnt == 1, "handler called %d", _h_cnt); datarouter_port_t p1 = FIXTURE_CREATE(&_fix);
datarouter_port_t p2 = FIXTURE_CREATE(&_fix);
datarouter_port_set_send_type(p1, DATAROUTER_SEND_sensor);
datarouter_port_set_send_type(p2, DATAROUTER_SEND_status);
datarouter_port_add_handler(p1, _h);
datarouter_port_add_handler(p2, _h);
reset_h();
CHECK(datarouter_send_sensor("d", 1) == 0, NULL);
CHECK(_h_cnt == 1, "sensor: cnt=%d", _h_cnt);
CHECK(datarouter_send_status("d", 1) == 0, NULL);
CHECK(_h_cnt == 2, "status: cnt=%d", _h_cnt);
TEST_END()
/* ================================================================
* 5. 接收回调
* ================================================================ */
static int _rx_r;
void datarouter_on_recv_rtcm(uint32_t p, const void *d, size_t l)
{ (void)p; (void)d; (void)l; _rx_r++; }
TEST_START(recv_callback)
_rx_r = 0;
datarouter_on_recv_rtcm(0, "d", 1);
CHECK(_rx_r == 1, "cnt=%d", _rx_r);
TEST_END()
/* ================================================================
* 6. 能力元数据
* ================================================================ */
TEST_START(send_cap_meta)
CHECK(datarouter_send_cap_count() == 2, NULL);
CHECK(strcmp(datarouter_send_cap_name(0), "sensor") == 0, NULL);
CHECK(strcmp(datarouter_send_cap_desc(0), "Sensor data report") == 0, NULL);
CHECK(datarouter_send_cap_name(-1) == NULL, NULL);
CHECK(datarouter_send_cap_name(99) == NULL, NULL);
TEST_END()
TEST_START(recv_cap_meta)
CHECK(datarouter_recv_cap_count() == 2, NULL);
CHECK(strcmp(datarouter_recv_cap_name(0), "rtcm") == 0, NULL);
CHECK(strcmp(datarouter_recv_cap_desc(0), "RTCM correction data") == 0, NULL);
CHECK(datarouter_recv_cap_name(-1) == NULL, NULL);
CHECK(datarouter_recv_cap_desc(99) == NULL, NULL);
TEST_END() TEST_END()
/* ================================================================ /* ================================================================
* 主函数 * 主函数
* ================================================================ */ * ================================================================ */
#define RUN(name) do { reset_hstate(); test_##name(); } while (0) #define RUN(name) do { reset_h(); _rx_r = 0; test_##name(); } while (0)
int main(void) int main(void)
{ {
@@ -448,64 +301,43 @@ int main(void)
printf("\n[port management]\n"); printf("\n[port management]\n");
RUN(port_create_basic); RUN(port_create_basic);
RUN(port_create_multiple);
RUN(port_create_reuse);
RUN(port_delete_invalid);
RUN(port_delete_twice);
RUN(port_default_state);
RUN(port_create_max); RUN(port_create_max);
RUN(port_delete_invalid);
RUN(port_default_state);
printf("\n"); printf("\n");
datarouter_reset(); datarouter_init(); datarouter_reset(); datarouter_init();
printf("[type set/get]\n"); printf("[send_type & handler]\n");
RUN(type_set_get); RUN(send_type_set_get);
RUN(type_clear); RUN(handler_add_remove);
RUN(type_switch);
RUN(type_from_invalid);
printf("\n");
printf("[multi-handler add/remove]\n");
RUN(handler_add_one);
RUN(handler_add_multiple);
RUN(handler_add_duplicate);
RUN(handler_add_max);
RUN(handler_remove);
RUN(handler_remove_not_found);
RUN(handler_clear); RUN(handler_clear);
RUN(handler_from_invalid);
printf("\n"); printf("\n");
printf("[channel introspection]\n"); printf("[send — auto broadcast]\n");
RUN(channel_count); RUN(send_no_match_port);
RUN(channel_names); RUN(send_one_port_one_handler);
RUN(channel_descs); RUN(send_one_port_multi_handler);
RUN(channel_invalid_id); RUN(send_multi_ports_same_type);
printf("\n"); RUN(send_multi_port_multi_handler);
RUN(send_type_filter);
printf("[send — one-to-many broadcast]\n"); RUN(send_after_remove_handler);
RUN(send_one_handler); RUN(send_after_delete_port);
RUN(send_multi_handlers);
RUN(send_type_mismatch);
RUN(send_no_handler);
RUN(send_three_channels);
RUN(send_remove_handler);
RUN(send_clear_handlers);
RUN(send_zero_length); RUN(send_zero_length);
RUN(send_null_data); RUN(send_null_data);
RUN(send_multi_cap);
printf("\n"); printf("\n");
printf("[receive callback]\n"); printf("[receive callback]\n");
RUN(recv_callback); RUN(recv_callback);
printf("\n"); printf("\n");
printf("[error handling]\n"); printf("[capability metadata]\n");
RUN(error_invalid_port); RUN(send_cap_meta);
RUN(error_deleted_port); RUN(recv_cap_meta);
printf("\n"); printf("\n");
printf("==============================\n"); printf("==============================\n");
printf(" total: %d | pass: %d | fail: %d\n\n", printf(" total: %d | pass: %d | fail: %d\n\n",
_total_run, _total_pass, _total_fail); _total_run, _total_pass, _total_fail);
return _total_fail > 0 ? 1 : 0; return _total_fail > 0 ? 1 : 0;
} }