37 Commits
Author SHA1 Message Date
7iwind ccde2f73e9 Merge pull request 'fix: 修正缺失构建平台的错误' (#35) from feature/route into main
CI / host (push) Successful in 7s
CI / cortex-m (push) Successful in 7s
Release / release (push) Successful in 16s
Reviewed-on: #35
2026-07-27 00:30:21 +08:00
7iwind 3b44bd97f7 fix: 修正缺失构建平台的错误
CI / host (pull_request) Successful in 4s
CI / cortex-m (pull_request) Successful in 6s
2026-07-27 00:29:35 +08:00
7iwind c5fea61784 Merge pull request 'fix: 修正缺失构建平台的错误' (#34) from feature/route into main
CI / host (push) Successful in 18s
CI / cortex-m (push) Successful in 3s
Release / release (push) Successful in 9s
Reviewed-on: #34
2026-07-27 00:23:31 +08:00
7iwind 07c4aa316a fix: 修正缺失构建平台的错误
CI / host (pull_request) Successful in 4s
CI / cortex-m (pull_request) Successful in 3s
2026-07-27 00:20:33 +08:00
7iwind 5d1ef621db Merge pull request 'fix: 修正缺失构建平台的错误' (#33) from feature/route into main
CI / host (push) Successful in 2s
CI / cortex-m (push) Successful in 3s
Release / release (push) Successful in 9s
Reviewed-on: #33
2026-07-27 00:15:18 +08:00
7iwind 8fe1b36cdc fix: 修正缺失构建平台的错误
CI / host (pull_request) Successful in 3s
CI / cortex-m (pull_request) Successful in 4s
2026-07-27 00:15:00 +08:00
7iwind b717c5f163 Merge pull request 'fix: 修正缺失构建平台的错误' (#32) from feature/route into main
CI / host (push) Successful in 8s
CI / cortex-m (push) Successful in 6s
Release / release (push) Failing after 10s
Reviewed-on: #32
2026-07-27 00:13:06 +08:00
7iwind 5ddc63abdf fix: 修正缺失构建平台的错误
CI / host (pull_request) Successful in 10s
CI / cortex-m (pull_request) Successful in 10s
2026-07-27 00:12:49 +08:00
7iwind 4e61ef5655 Merge pull request 'v1.1.0:fix: 修正缺失构建平台的错误' (#31) from feature/route into main
CI / host (push) Successful in 2s
CI / cortex-m (push) Successful in 3s
Release / release (push) Failing after 4s
Reviewed-on: #31
2026-07-27 00:10:50 +08:00
7iwind 361158c343 fix: 修正缺失构建平台的错误
CI / host (pull_request) Successful in 3s
CI / cortex-m (pull_request) Successful in 4s
2026-07-27 00:09:45 +08:00
7iwind 5fd55496bf v1.1.0:Merge pull request 'fix: 修正CD错误' (#30) from feature/route into main
CI / host (push) Successful in 3s
CI / cortex-m (push) Successful in 4s
Release / release (push) Successful in 26s
v1.1.0:Reviewed-on: #30
2026-07-27 00:03:44 +08:00
7iwind f0d0c4fb1e fix: 修正CD错误
CI / host (pull_request) Successful in 3s
CI / cortex-m (pull_request) Successful in 4s
2026-07-27 00:02:47 +08:00
7iwind 26862bf05f Merge pull request 'fix: 修正CD错误' (#29) from feature/route into main
CI / host (push) Successful in 2s
CI / cortex-m (push) Successful in 4s
Release / release (push) Failing after 5s
Reviewed-on: #29
2026-07-26 23:56:11 +08:00
7iwind b90c98d3ad fix: 修正CD错误
CI / host (pull_request) Successful in 4s
CI / cortex-m (pull_request) Successful in 4s
2026-07-26 23:55:44 +08:00
7iwind 04770e5df2 Merge pull request 'develop' (#28) from develop into main
CI / host (push) Successful in 5s
CI / cortex-m (push) Successful in 7s
Release / release (push) Failing after 6s
Reviewed-on: #28
2026-07-26 23:51:28 +08:00
7iwind 5a92e2ccfa Merge pull request 'fix: 修正CD错误' (#27) from feature/route into develop
CI / host (push) Successful in 9s
CI / cortex-m (push) Successful in 7s
CI / host (pull_request) Successful in 4s
CI / cortex-m (pull_request) Successful in 5s
Reviewed-on: #27
2026-07-26 23:50:47 +08:00
7iwind b98236466c fix: 修正CD错误 2026-07-26 23:49:36 +08:00
7iwind badb4ebf61 v1.1.0:Merge pull request 'develop' (#26) from develop into main
CI / host (push) Successful in 5s
CI / cortex-m (push) Successful in 6s
Release / release (push) Failing after 6s
v1.1.0:Reviewed-on: #26
2026-07-26 23:47:02 +08:00
7iwind 5aba22d8e9 Merge pull request 'fix: 修正CD错误' (#25) from feature/route into develop
CI / host (push) Successful in 5s
CI / cortex-m (push) Successful in 10s
CI / host (pull_request) Successful in 7s
CI / cortex-m (pull_request) Successful in 9s
Reviewed-on: #25
2026-07-26 23:45:37 +08:00
7iwind 052df2fc21 fix: 修正CD错误 2026-07-26 23:45:10 +08:00
7iwind 8c670003b8 Merge pull request 'v1.1.0' (#24) from develop into main
CI / host (push) Successful in 20s
CI / cortex-m (push) Successful in 6s
Release / release (push) Failing after 9s
v1.0.0:Reviewed-on: #24
2026-07-26 23:42:21 +08:00
7iwind dee130197c Merge pull request 'fix: 修正CI错误' (#23) from feature/route into develop
CI / host (pull_request) Successful in 18s
CI / cortex-m (pull_request) Successful in 5s
CI / host (push) Successful in 5s
CI / cortex-m (push) Successful in 6s
Reviewed-on: #23
2026-07-26 23:38:29 +08:00
7iwind d0500151ab fix: 修正CI错误 2026-07-26 23:37:41 +08:00
7iwind 9258ced2d0 Merge pull request 'feat: 添加CM3/4/7的硬件与软件浮点单元构建矩阵,变更api接口为dr_*' (#21) from feature/route into develop
CI / host (push) Successful in 6s
CI / cortex-m (push) Failing after 6s
CI / host (pull_request) Successful in 8s
CI / cortex-m (pull_request) Failing after 9s
Reviewed-on: #21
2026-07-26 23:34:53 +08:00
7iwind 7ba20659b2 feat: 添加CM3/4/7的硬件与软件浮点单元构建矩阵,变更api接口为dr_* 2026-07-26 23:34:11 +08:00
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
20 changed files with 1359 additions and 844 deletions
+34
View File
@@ -0,0 +1,34 @@
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
host:
runs-on: ubuntu-builder
steps:
- uses: actions/checkout@v4
- name: Host build + tests
run: |
cmake -S . -B build-host \
-DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON
cmake --build build-host
ctest --test-dir build-host --output-on-failure
cortex-m:
runs-on: ubuntu-builder
steps:
- uses: actions/checkout@v4
- name: Install ARM GCC
run: |
sudo apt-get update
sudo apt-get install -y gcc-arm-none-eabi
- name: Cortex-M compile matrix (DR_COMPILE_CHECK)
run: |
cmake -S . -B build-matrix -DDR_COMPILE_CHECK=ON
cmake --build build-matrix --parallel $(nproc)
+91 -20
View File
@@ -11,16 +11,62 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Verify merge commit # ── 自动标签:从合并进来的 PR body 提取 v*.*.* ──
- name: Auto-tag from merged PR
id: autotag
run: | run: |
PARENTS=$(git log --format="%P" -1 | wc -w) # 已有标签 → 跳过
[ "$PARENTS" -ge 2 ] || exit 78 EXISTING=$(git tag --points-at HEAD | grep '^v' | head -n1)
if [ -n "$EXISTING" ]; then
echo "Tag $EXISTING already exists"
echo "tag=$EXISTING" >> "$GITHUB_OUTPUT"
exit 0
fi
# 非 merge commit → 跳过
PARENTS=$(git log --format="%P" -1 | wc -w)
if [ "$PARENTS" -lt 2 ]; then
echo "Not a merge commit, skip auto-tag"
exit 0
fi
# 从 merge commit message 提取 PR 号
COMMIT_MSG=$(git log --format="%B" -1)
PR_NUM=$(echo "$COMMIT_MSG" | grep -oP '#\K\d+' | head -1) || true
if [ -z "$PR_NUM" ]; then
echo "No PR number found in merge message"
exit 0
fi
echo "Merge commit for PR #$PR_NUM"
# 通过 Gitea API 获取 PR body,提取版本号
API="${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/pulls/${PR_NUM}"
TOKEN="${{ gitea.token }}"
PR_BODY=$(curl -s -H "Authorization: token ${TOKEN}" "${API}" | \
python3 -c "import sys,json,re;pr=json.load(sys.stdin);body=pr.get('body','') or '';m=re.search(r'v\d+\.\d+\.\d+',body);print(m.group() if m else '')")
if [ -z "$PR_BODY" ]; then
echo "No version pattern v*.*.* found in PR body"
exit 0
fi
VERSION="$PR_BODY"
echo "Found version: $VERSION"
# 创建本地标签(不推送远端)
# Gitea Create Release API 会自动在远端创建标签
git config user.email "ci@datarouter"
git config user.name "DataRouter CI"
git tag -a "$VERSION" -m "Release $VERSION" 2>/dev/null || true
echo "Created local tag: $VERSION"
echo "tag=$VERSION" >> "$GITHUB_OUTPUT"
# ── 读取标签(已有标签 / auto-tag 创建的标签)──
- name: Read tag from HEAD - name: Read tag from HEAD
id: tag id: tag
run: | run: |
TAG=$(git tag --points-at HEAD | grep '^v' | head -n1) TAG=$(git tag --points-at HEAD | grep '^v' | head -n1)
[ -n "$TAG" ] || exit 78 [ -n "$TAG" ] || { echo "No version tag on HEAD"; exit 78; }
echo "tag=$TAG" >> "$GITHUB_OUTPUT" echo "tag=$TAG" >> "$GITHUB_OUTPUT"
- name: Install cross-compilers - name: Install cross-compilers
@@ -39,7 +85,7 @@ jobs:
"linux-arm64|OFF|-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-linux-arm64.cmake" \ "linux-arm64|OFF|-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-linux-arm64.cmake" \
"linux-armhf|OFF|-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-linux-armhf.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" \ "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" "baremetal-arm32|OFF|-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-baremetal-arm32.cmake -DBUILD_EXAMPLES=OFF"
do do
IFS='|' read -r name testing extra <<< "$entry" IFS='|' read -r name testing extra <<< "$entry"
echo ">>> Building $name" echo ">>> Building $name"
@@ -48,35 +94,60 @@ jobs:
[ "$testing" = "ON" ] && ctest --test-dir "build-$name" --output-on-failure [ "$testing" = "ON" ] && ctest --test-dir "build-$name" --output-on-failure
pkg="datarouter-$name" pkg="datarouter-$name"
mkdir -p "dist/$pkg" cmake --install "build-$name" --prefix "dist/$pkg"
for f in datarouter.h datarouter_config_template.h LICENSE README.md; do
if [ -f "$f" ]; then cp "$f" "dist/$pkg/"; fi
done
for f in libDataRouter.a libDataRouter.so libDataRouter.dll libDataRouter.dll.a example example.exe; do
if [ -f "build-$name/$f" ]; then cp "build-$name/$f" "dist/$pkg/"; fi
done
done done
- name: Cortex-M build & package
run: |
for name in cm3 cm4-soft cm4-hard cm7-soft cm7-hard; do
case $name in
cm3) flags="-mcpu=cortex-m3 -mthumb -mfloat-abi=soft" ;;
cm4-soft) flags="-mcpu=cortex-m4 -mthumb -mfloat-abi=soft" ;;
cm4-hard) flags="-mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard" ;;
cm7-soft) flags="-mcpu=cortex-m7 -mthumb -mfloat-abi=soft" ;;
cm7-hard) flags="-mcpu=cortex-m7 -mthumb -mfpu=fpv5-d16 -mfloat-abi=hard" ;;
esac
echo ">>> Building $name"
cmake -S . -B "build-$name" \
-DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF \
-DCMAKE_C_COMPILER=arm-none-eabi-gcc \
-DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY \
-DCMAKE_C_FLAGS="$flags"
cmake --build "build-$name"
cmake --install "build-$name" --prefix "dist/datarouter-$name"
done
echo "--- Cortex-M package build: all variants passed ---"
- name: Create release & upload assets - name: Create release & upload assets
run: | run: |
TAG="${{ steps.tag.outputs.tag }}" TAG="${{ steps.tag.outputs.tag }}"
API="${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/releases" API="${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/releases"
TOKEN="${{ gitea.token }}"
echo "=== Creating release $TAG ===" # Check if release already exists
RESP=$(curl -s -X POST \ echo "=== Checking existing releases for $TAG ==="
-H "Authorization: token ${{ gitea.token }}" \ EXISTING=$(curl -s -H "Authorization: token ${TOKEN}" "${API}" | \
-H "Content-Type: application/json" \ python3 -c "import sys,json;rels=json.load(sys.stdin);print(next((r['id'] for r in rels if r['tag_name']=='${TAG}'),''))")
-d "{\"tag_name\":\"${TAG}\",\"name\":\"DataRouter ${TAG}\"}" \
"$API")
ID=$(echo "$RESP" | python3 -c "import sys,json;print(json.load(sys.stdin)['id'])") 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 for dir in dist/*/; do
name="$(basename "$dir").tar.gz" name="$(basename "$dir").tar.gz"
tar czf "/tmp/$name" -C "$dir" . tar czf "/tmp/$name" -C "$dir" .
echo "Uploading $name" echo "Uploading $name"
curl -s -X POST \ curl -s -X POST \
-H "Authorization: token ${{ gitea.token }}" \ -H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/octet-stream" \ -H "Content-Type: application/octet-stream" \
--data-binary @"/tmp/$name" \ --data-binary @"/tmp/$name" \
"${API}/${ID}/assets?name=${name}" "${API}/${ID}/assets?name=${name}"
+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>
+62
View File
@@ -0,0 +1,62 @@
# Changelog
## [Unreleased]
### ⚠ Breaking Changes
- **API 前缀重命名**:所有 `datarouter_*` / `DATAROUTER_*` 改为 `dr_*` / `DR_*`
- `datarouter_init()` → `dr_init()`
- `datarouter_port_create()` → `dr_port_create()`
- `datarouter_send_<id>()` → `dr_send_<id>()`
- `datarouter_on_recv_<id>()` → `dr_on_recv_<id>()`
- `DATAROUTER_SEND_sensor` → `DR_SEND_sensor`
- `datarouter_port_t` → `dr_port_t`
- 配置宏 `DATAROUTER_SEND(...)` / `DATAROUTER_RECV(...)` → `DR_SEND(...)` / `DR_RECV(...)`
- **发送函数不再接收 port_id**:`dr_send_<id>(data, len)` 自动广播到所有 send_type 匹配的端口
- **端口移除 `recv_type`**:端口只保留 `send_type` + handler 列表,接收侧由用户自行调用 `dr_on_recv_<id>()`
### 🚀 新功能
- **自动广播路由**:调用 `dr_send_<id>(data, len)` 时遍历所有端口,自动匹配 `send_type`,逐一调用 handler
- **外部配置覆盖**:`datarouter.h` 通过 `__has_include(<datarouter_config.h>)` 优先加载用户配置,找不到时回退到库默认配置
- **配置模板**:新增 `datarouter_config_template.h` 供用户快速自定义
- **CMake Presets**:新增 `CMakePresets.json`,覆盖 7 种构建配置
- **Cortex-M 编译矩阵**:`DR_COMPILE_CHECK=ON` 自动检测 `arm-none-eabi-gcc`,验证 CM3 / CM4(soft/hard) / CM7(soft/hard-sp/hard-dp) 6 个变体的编译兼容性
- **嵌入式源文件直用模式**:`DR_SOURCE_ONLY=ON` 只暴露源码路径,用户将 `datarouter.c` 加入自身 target 以继承全部编译标志,消除 VFP/Thumb ABI 不匹配
- **Gitea CI 工作流**:新增 `.gitea/workflows/ci.yml`,PR/推送时执行 host 测试 + Cortex-M 编译矩阵
- **Release 工作流更新**:`.gitea/workflows/release.yml` 新增 Cortex-M compile matrix 步骤
### ✨ 增强
- 支持一个端口注册多个 handler(默认上限 4),发送时逐一调用
- 运行时通过 `dr_port_remove_handler()` / `dr_port_clear_handlers()` 动态改变路由
- 发送能力和接收能力分开声明,按需组合
### 📚 文档
- 新增 `DESIGN.md`(含 8 幅 PlantUML 架构图)
- 新增 `TEST_CASES.md`(22 个测试用例文档)
- 新增 `CHANGELOG.md`
- `README.md` 全面更新,覆盖嵌入式构建、Preset 用法、API 参考
- 所有源文件添加中文 Doxygen 注释
### 🧪 测试
- 22 个单元测试,覆盖端口管理、send_type/handler 设置、自动广播路由、类型过滤、移除/删除后路由、接收回调、能力元数据
### 🐛 修复
- 修复 ARM 交叉编译时 VFP 参数不匹配问题(通过 `DR_SOURCE_ONLY` 模式)
- 修复 `#undef` 宏名称残留旧前缀
---
## [1.0.0] - 2024-01-01
### 🚀 新功能
- 初始版本发布
- X-macro 代码生成:编译期声明通道,自动生成枚举、发送函数、回调声明
- 动态端口管理:创建、删除、运行时切换能力
- 弱符号回调默认实现
- Gitea Release 工作流:自动化构建 linux-amd64 / linux-arm64 / linux-armhf / windows-amd64 / baremetal-arm32
+104 -12
View File
@@ -1,30 +1,122 @@
cmake_minimum_required(VERSION 3.10) cmake_minimum_required(VERSION 3.10)
project(DataRouter C) project(DataRouter VERSION 1.1.0 LANGUAGES C)
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)
# ======== 静态库 ======== # ── 选项 ──────────────────────────────────────────────────────
add_library(DataRouter STATIC datarouter.c)
target_include_directories(DataRouter PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
# ======== 共享库(可选,通过 BUILD_SHARED 控制) ======== option(DR_SOURCE_ONLY "Provide source/include vars instead of building lib" OFF)
option(BUILD_SHARED "Build shared library" ON) option(DR_COMPILE_CHECK "Build compile-compatibility tests for each Cortex-M variant" OFF)
if(BUILD_SHARED)
add_library(DataRouter_shared SHARED datarouter.c) # 源码路径变量:嵌入式项目直接 target_sources() 使用
target_include_directories(DataRouter_shared PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) set(DR_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/datarouter.c CACHE INTERNAL "")
set_target_properties(DataRouter_shared PROPERTIES OUTPUT_NAME DataRouter) set(DR_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL "")
# ── 编译兼容性矩阵 (仅在 host 编译器和 ARM GCC 均可用时生效) ──
if(DR_COMPILE_CHECK)
find_program(ARM_GCC arm-none-eabi-gcc)
if(ARM_GCC)
message(STATUS "ARM GCC found: ${ARM_GCC}")
set(VARIANTS cm3 cm4-soft cm4-hard cm7-soft cm7-hard)
set(FLAG_LIST
"-mcpu=cortex-m3 -mthumb -mfloat-abi=soft"
"-mcpu=cortex-m4 -mthumb -mfloat-abi=soft"
"-mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard"
"-mcpu=cortex-m7 -mthumb -mfloat-abi=soft"
"-mcpu=cortex-m7 -mthumb -mfpu=fpv5-d16 -mfloat-abi=hard"
)
list(LENGTH VARIANTS VN)
math(EXPR VN "${VN} - 1")
foreach(I RANGE ${VN})
list(GET VARIANTS ${I} V)
list(GET FLAG_LIST ${I} F)
set(TGT "check_${V}")
add_custom_target(${TGT}
COMMAND ${ARM_GCC} ${F}
-c ${CMAKE_CURRENT_SOURCE_DIR}/datarouter.c
-I${CMAKE_CURRENT_SOURCE_DIR}
-o ${CMAKE_CURRENT_BINARY_DIR}/${TGT}.o
COMMENT "Compile check: ${V} (${F})"
)
message(STATUS " compile check: ${V} (make ${TGT})")
endforeach()
else()
message(WARNING "arm-none-eabi-gcc not found; skipping ARM compile checks")
endif()
return()
endif() endif()
# ======== 示例程序 ======== # ── 源码直用模式(嵌入式项目) ──────────────────────────────
if(DR_SOURCE_ONLY)
return()
endif()
# ── 常规库构建 ────────────────────────────────────────────────
add_library(DataRouter STATIC datarouter.c)
target_include_directories(DataRouter PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
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()
# ── 安装规则(用于打包发布) ─────────────────────────────
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
set(DataRouter_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/DataRouter")
install(TARGETS DataRouter
EXPORT DataRouterTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
install(FILES
${CMAKE_CURRENT_SOURCE_DIR}/datarouter.h
${CMAKE_CURRENT_SOURCE_DIR}/datarouter_config.h
${CMAKE_CURRENT_SOURCE_DIR}/datarouter_config_template.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
# ── CMake 包配置文件(find_package(DataRouter) 用) ────
set(PACKAGE_INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR})
set(PACKAGE_LIB_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR})
configure_package_config_file(
${CMAKE_CURRENT_SOURCE_DIR}/cmake/DataRouterConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/DataRouterConfig.cmake
INSTALL_DESTINATION ${DataRouter_INSTALL_CMAKEDIR}
PATH_VARS PACKAGE_INCLUDE_INSTALL_DIR PACKAGE_LIB_INSTALL_DIR
)
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/DataRouterConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion
)
install(EXPORT DataRouterTargets
FILE DataRouterTargets.cmake
DESTINATION ${DataRouter_INSTALL_CMAKEDIR}
)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/DataRouterConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/DataRouterConfigVersion.cmake
DESTINATION ${DataRouter_INSTALL_CMAKEDIR}
)
+96
View File
@@ -0,0 +1,96 @@
{
"version": 3,
"configurePresets": [
{
"name": "default",
"displayName": "Host (native) build",
"description": "Build with host compiler for development, testing, and examples",
"binaryDir": "${sourceDir}/build/default",
"cacheVariables": {
"BUILD_TESTING": "ON",
"BUILD_EXAMPLES": "ON"
}
},
{
"name": "cm3",
"displayName": "Cortex-M3 (soft-float)",
"description": "ARM Cortex-M3, no FPU, software float ABI. Requires arm-none-eabi-gcc in PATH.",
"binaryDir": "${sourceDir}/build/cm3",
"cacheVariables": {
"CMAKE_C_FLAGS": "-mcpu=cortex-m3 -mthumb -mfloat-abi=soft -Wall -Werror",
"DR_SOURCE_ONLY": "ON",
"BUILD_TESTING": "OFF",
"BUILD_EXAMPLES": "OFF"
}
},
{
"name": "cm4-soft",
"displayName": "Cortex-M4 (soft-float)",
"description": "ARM Cortex-M4, FPU present but soft-float ABI. Requires arm-none-eabi-gcc.",
"binaryDir": "${sourceDir}/build/cm4-soft",
"cacheVariables": {
"CMAKE_C_FLAGS": "-mcpu=cortex-m4 -mthumb -mfloat-abi=soft -Wall -Werror",
"DR_SOURCE_ONLY": "ON",
"BUILD_TESTING": "OFF",
"BUILD_EXAMPLES": "OFF"
}
},
{
"name": "cm4-hard",
"displayName": "Cortex-M4F (hard-float, FPv4-SP-D16)",
"description": "ARM Cortex-M4 with single-precision FPU, hard-float ABI. Requires arm-none-eabi-gcc.",
"binaryDir": "${sourceDir}/build/cm4-hard",
"cacheVariables": {
"CMAKE_C_FLAGS": "-mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -Wall -Werror",
"DR_SOURCE_ONLY": "ON",
"BUILD_TESTING": "OFF",
"BUILD_EXAMPLES": "OFF"
}
},
{
"name": "cm7-soft",
"displayName": "Cortex-M7 (soft-float)",
"description": "ARM Cortex-M7, FPU present but soft-float ABI. Requires arm-none-eabi-gcc.",
"binaryDir": "${sourceDir}/build/cm7-soft",
"cacheVariables": {
"CMAKE_C_FLAGS": "-mcpu=cortex-m7 -mthumb -mfloat-abi=soft -Wall -Werror",
"DR_SOURCE_ONLY": "ON",
"BUILD_TESTING": "OFF",
"BUILD_EXAMPLES": "OFF"
}
},
{
"name": "cm7-hard-sp",
"displayName": "Cortex-M7F (hard-float, FPv5-SP-D16, single)",
"description": "ARM Cortex-M7 with single-precision FPU, hard-float ABI. Requires arm-none-eabi-gcc.",
"binaryDir": "${sourceDir}/build/cm7-hard-sp",
"cacheVariables": {
"CMAKE_C_FLAGS": "-mcpu=cortex-m7 -mthumb -mfpu=fpv5-sp-d16 -mfloat-abi=hard -Wall -Werror",
"DR_SOURCE_ONLY": "ON",
"BUILD_TESTING": "OFF",
"BUILD_EXAMPLES": "OFF"
}
},
{
"name": "cm7-hard-dp",
"displayName": "Cortex-M7F (hard-float, FPv5-D16, double)",
"description": "ARM Cortex-M7 with double-precision FPU, hard-float ABI. Requires arm-none-eabi-gcc.",
"binaryDir": "${sourceDir}/build/cm7-hard-dp",
"cacheVariables": {
"CMAKE_C_FLAGS": "-mcpu=cortex-m7 -mthumb -mfpu=fpv5-d16 -mfloat-abi=hard -Wall -Werror",
"DR_SOURCE_ONLY": "ON",
"BUILD_TESTING": "OFF",
"BUILD_EXAMPLES": "OFF"
}
}
],
"buildPresets": [
{ "name": "default", "configurePreset": "default" },
{ "name": "cm3", "configurePreset": "cm3" },
{ "name": "cm4-soft", "configurePreset": "cm4-soft" },
{ "name": "cm4-hard", "configurePreset": "cm4-hard" },
{ "name": "cm7-soft", "configurePreset": "cm7-soft" },
{ "name": "cm7-hard-sp", "configurePreset": "cm7-hard-sp" },
{ "name": "cm7-hard-dp", "configurePreset": "cm7-hard-dp" }
]
}
+152 -203
View File
@@ -2,37 +2,35 @@
## 1. 设计目标 ## 1. 设计目标
轻量级 C 语言数据路由库。发送与接收能力分开声明,每个端口独立设置发送类型和接收类型,支持多外设一对多广播路由。 轻量级 C 语言数据路由库。发送与接收能力分开声明,端口绑定 `send_type` 和外设 handler 列表。调用 `dr_send_<id>(data, len)` 时自动遍历端口表,广播到所有 `send_type` 匹配的端口。
### 核心设计 ### 核心设计
``` ```
DATAROUTER_SEND_CAPABILITIES DATAROUTER_RECV_CAPABILITIES DR_SEND_CAPABILITIES DR_RECV_CAPABILITIES
DATAROUTER_SEND(sensor, ...) DATAROUTER_RECV(rtcm, ...) DR_SEND(sensor, ...) DR_RECV(rtcm, ...)
DATAROUTER_SEND(status, ...) DATAROUTER_RECV(command, ...) DR_SEND(status, ...) DR_RECV(command, ...)
│ │ │ │
▼ ▼ ▼ ▼
send_sensor(port, data) on_recv_rtcm(port, data) send_sensor(data, len) on_recv_rtcm(port, data, len)
send_status(port, data) on_recv_command(port, data) │ (用户实现回调)
│ (用户实现回调)
▼ ▼
端口校验 → for each handler(port, data) 遍历端口表 → send_type 匹配?
│ │
┌────────┼────────┐ ▼
▼ ▼ ▼ for each handler(port, data) ─── UART / TCP / CAN
UART TCP CAN
``` ```
**关键区别**:sensor 只声明发送,不生成接收回调;rtcm 只声明接收,不生成发送函数。端口可独立设置 send_type 和 recv_type,互不干扰。 **关键**:send 无 port_id 参数,自动路由到所有匹配端口。
### 设计原则 ### 设计原则
| 原则 | 说明 | | 原则 | 说明 |
|------|------| |------|------|
| 收发分离 | 发送能力和接收能力在配置中分开声明,按需组合 | | 收发分离 | 发送/接收能力在配置中分开声明,按需组合 |
| 编译期确定能力 | 所有能力在 `datarouter_config.h` 中声明,库自动生成代码 | | 编译期确定能力 | 所有能力在 `dr_config.h` 中声明,库自动生成代码 |
| 运行期绑定外设 | 端口创建后,通过 `add_handler` 注册物理外设发送函数 | | 运行期绑定外设 | 端口创建后,通过 `add_handler` 注册物理外设发送函数 |
| 一对多广播 | 一个端口可注册多个 handler,发送时逐一调用 | | 自动广播 | `send_<id>(data, len)` 遍历所有端口,自动匹配 send_type |
| 不关心物理外设 | 库不依赖任何外设 API,只调用用户提供的 handler | | 不关心物理外设 | 库不依赖任何外设 API,只调用用户提供的 handler |
| 零动态内存 | 纯静态数组,无 malloc/free | | 零动态内存 | 纯静态数组,无 malloc/free |
@@ -47,23 +45,22 @@ DATAROUTER_SEND_CAPABILITIES DATAROUTER_RECV_CAPABILITIES
left to right direction left to right direction
rectangle "用户代码" as User { rectangle "用户代码" as User {
component "应用逻辑" as App component "应用逻辑\n调 send_<id>(data, len)" as App
component "datarouter_config.h\n声明发送+接收能力" as Cfg component "dr_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 + X-macro 展开" 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 : 注册 handler Handlers --> Impl : add_handler
Callbacks --> Impl : 实现 on_recv_<id> Callbacks --> Impl : 实现 on_recv_<id>
@enduml @enduml
``` ```
@@ -71,11 +68,11 @@ Callbacks --> Impl : 实现 on_recv_<id>
| 文件 | 职责 | | 文件 | 职责 |
|------|------| |------|------|
| `datarouter_config.h` | 分别声明 `SEND_CAPABILITIES` 和 `RECV_CAPABILITIES` | | `dr_config.h` | 声明 `SEND_CAPABILITIES` 和 `RECV_CAPABILITIES` |
| `datarouter.h` | 公共 API + X-macro 展开(两组枚举 + 声明) | | `datarouter.h` | 公共 API + X-macro 展开 |
| `datarouter.c` | 端口表 + handler 列表遍历 + 弱符号回调 | | `datarouter.c` | 端口表 + send 遍历 + 弱符号回调 |
| `example.c` | 收发分离示例 | | `example.c` | 使用示例 |
| `tests/test_datarouter.c` | 27 个单元测试 | | `tests/test_datarouter.c` | 22 个单元测试 |
--- ---
@@ -84,90 +81,71 @@ Callbacks --> Impl : 实现 on_recv_<id>
### 3.1 配置声明 ### 3.1 配置声明
```c ```c
// 发送能力 —— 只生成发送触发函数 #define DR_SEND_CAPABILITIES \
#define DATAROUTER_SEND_CAPABILITIES \ DR_SEND(sensor, "Sensor data report") \
DATAROUTER_SEND(sensor, "Sensor data report") \ DR_SEND(status, "Device status report")
DATAROUTER_SEND(status, "Device status report")
// 接收能力 —— 只生成接收回调声明 #define DR_RECV_CAPABILITIES \
#define DATAROUTER_RECV_CAPABILITIES \ DR_RECV(rtcm, "RTCM correction data") \
DATAROUTER_RECV(rtcm, "RTCM correction data") \ DR_RECV(command, "Remote control command")
DATAROUTER_RECV(command, "Remote control command")
``` ```
### 3.2 展开产物 ### 3.2 展开产物
``` ```
DATAROUTER_SEND_CAPABILITIES 展开: DR_SEND_CAPABILITIES 展开:
enum: DATAROUTER_SEND_sensor = 0, enum: sensor = 0, status = 1, SEND_CAP_COUNT = 2
DATAROUTER_SEND_status = 1, 发送函数: int dr_send_sensor(const void *data, size_t len);
DATAROUTER_SEND_CAP_COUNT = 2 int dr_send_status(const void *data, size_t len);
发送函数: int datarouter_send_sensor(port, data, len);
int datarouter_send_status(port, data, len);
元数据: send_names[] = {"sensor", "status"} 元数据: send_names[] = {"sensor", "status"}
send_descs[] = {"Sensor data report", "Device status report"} send_descs[] = {"Sensor data report", "Device status report"}
DATAROUTER_RECV_CAPABILITIES 展开: DR_RECV_CAPABILITIES 展开:
enum: DATAROUTER_RECV_rtcm = 0, enum: rtcm = 0, command = 1, RECV_CAP_COUNT = 2
DATAROUTER_RECV_command = 1, 接收回调: void dr_on_recv_rtcm(uint32_t port, const void*, size_t);
DATAROUTER_RECV_CAP_COUNT = 2 void dr_on_recv_command(uint32_t port, const void*, size_t);
接收回调: void datarouter_on_recv_rtcm(port, data, len); 弱符号默认: 空实现,用户覆盖
void datarouter_on_recv_command(port, data, len);
弱符号默认: 每个回调提供空的 weak 实现,用户覆盖
元数据: recv_names[] = {"rtcm", "command"} 元数据: recv_names[] = {"rtcm", "command"}
recv_descs[] = {"RTCM correction data", "Remote control command"} recv_descs[] = {"RTCM correction data", "Remote control command"}
``` ```
### 3.3 端口组合示例 ### 3.3 端口示例
``` ```
端口 A: send_type = sensor, recv_type = -1 → 只发送 端口 A: send_type = sensor, handlers=[uart_send, tcp_send]
端口 B: send_type = -1, recv_type = rtcm → 只接收 端口 B: send_type = sensor, handlers=[can_send]
端口 C: send_type = status, recv_type = command → 同时收发 端口 C: send_type = status, handlers=[uart_send]
端口 D: send_type = -1, recv_type = -1 → 无功能
``` ```
调用 `send_sensor("temp", 4)` → A 和 B 同时触发。
--- ---
## 4. X-macro 代码生成 ## 4. X-macro 代码生成
### 4.1 展开流程
```plantuml ```plantuml
@startuml @startuml
== datarouter.h:4 次展开 == == datarouter.h ==
-> datarouter_config.h: 获得两组宏列表 -> dr_config.h: 获得两组宏列表
datarouter.h -> "第 1 遍": datarouter.h -> "1": #define DR_SEND(id,desc) DR_SEND_##id,
#define DATAROUTER_SEND(id,desc) DATAROUTER_SEND_##id, note right: 发送 enum
DATAROUTER_SEND_CAPABILITIES → 发送 enum
datarouter.h -> "第 2 遍": datarouter.h -> "2": #define DR_RECV(id,desc) DR_RECV_##id,
#define DATAROUTER_RECV(id,desc) DATAROUTER_RECV_##id, note right: 接收 enum
DATAROUTER_RECV_CAPABILITIES → 接收 enum
datarouter.h -> "第 3 遍": datarouter.h -> "3": #define DR_SEND(id,desc) int send_##id(data, len);
#define DATAROUTER_SEND(id,desc) int datarouter_send_##id(...); note right: 发送函数声明
DATAROUTER_SEND_CAPABILITIES → 发送函数声明
datarouter.h -> "第 4 遍": datarouter.h -> "4": #define DR_RECV(id,desc) void on_recv_##id(...);
#define DATAROUTER_RECV(id,desc) void datarouter_on_recv_##id(...); note right: 接收回调声明
DATAROUTER_RECV_CAPABILITIES → 接收回调声明
== datarouter.c:4 次展开 == == datarouter.c ==
datarouter.c -> "第 5 遍":
弱符号回调默认实现(DATAROUTER_RECV 展开)
datarouter.c -> "第 6 遍":
发送函数实现 body(DATAROUTER_SEND 展开)
校验 → 遍历 handler 列表
datarouter.c -> "第 7 ~ 8 遍":
send_names[] / send_descs[]
recv_names[] / recv_descs[]
datarouter.c -> "5": 弱符号回调默认实现
datarouter.c -> "6": 发送函数实现(遍历端口表)
datarouter.c -> "7~8": send_names[] / send_descs[] / recv_names[] / recv_descs[]
@enduml @enduml
``` ```
@@ -181,33 +159,23 @@ datarouter.c -> "第 7 ~ 8 遍":
@startuml @startuml
skinparam classAttributeIconSize 0 skinparam classAttributeIconSize 0
enum "datarouter_send_cap" as S { enum "dr_send_cap" as S { SENSOR, STATUS, SEND_CAP_COUNT }
SENSOR enum "dr_recv_cap" as R { RTCM, COMMAND, RECV_CAP_COUNT }
STATUS
SEND_CAP_COUNT
}
enum "datarouter_recv_cap" as R {
RTCM
COMMAND
RECV_CAP_COUNT
}
class "port_entry_t" as Entry { class "port_entry_t" as Entry {
- int send_type - int send_type
- int recv_type - dr_send_handler_t handlers[]
- datarouter_send_handler_t handlers[]
- int count - int count
- uint8_t in_use - uint8_t in_use
} }
class "datarouter_send_handler_t" as Handler { class "dr_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 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)
} }
class "接收回调" as Recv { class "接收回调" as Recv {
@@ -215,10 +183,9 @@ class "接收回调" as Recv {
+ void on_recv_command(port, data, len) + void on_recv_command(port, data, len)
} }
Entry --> S : send_type ∈ S enum / -1 Entry --> S : send_type
Entry --> R : recv_type ∈ R enum / -1 Entry --> Handler : handlers[]
Entry --> Handler : handlers[] 数组 Send --> Entry : 遍历端口表 → 匹配 send_type → 遍历 handlers
Send --> Entry : 校验 send_type + 遍历 handlers
Recv --> Entry : 用户根据业务调对应回调 Recv --> Entry : 用户根据业务调对应回调
@enduml @enduml
``` ```
@@ -226,13 +193,12 @@ Recv --> Entry : 用户根据业务调对应回调
### 5.2 `port_entry_t` ### 5.2 `port_entry_t`
```c ```c
#define DATAROUTER_MAX_HANDLERS_PER_PORT 4 // 每端口最多 4 个外设 #define DR_MAX_HANDLERS_PER_PORT 4
typedef struct { typedef struct {
int send_type; // DATAROUTER_SEND_xxx / -1 int send_type;
int recv_type; // DATAROUTER_RECV_xxx / -1 dr_send_handler_t handlers[DR_MAX_HANDLERS_PER_PORT];
datarouter_send_handler_t handlers[DATAROUTER_MAX_HANDLERS_PER_PORT]; int count;
int count; // 当前 handler 数量
uint8_t in_use; uint8_t in_use;
} port_entry_t; } port_entry_t;
``` ```
@@ -240,8 +206,8 @@ typedef struct {
### 5.3 端口表 ### 5.3 端口表
```c ```c
#define DATAROUTER_MAX_PORTS 64 #define DR_MAX_PORTS 64
static port_entry_t ports[DATAROUTER_MAX_PORTS]; static port_entry_t ports[DR_MAX_PORTS];
static uint8_t initialized; static uint8_t initialized;
``` ```
@@ -252,57 +218,53 @@ static uint8_t initialized;
### 6.1 初始化 ### 6.1 初始化
```c ```c
void datarouter_init(void); // 幂等初始化 void dr_init(void); // 幂等初始化
void datarouter_reset(void); // 重置全部状态 void dr_reset(void); // 重置全部状态
``` ```
### 6.2 端口管理 ### 6.2 端口管理
```c ```c
datarouter_port_t datarouter_port_create(void); // 创建 dr_port_t dr_port_create(void);
void datarouter_port_delete(datarouter_port_t port); // 删除 void dr_port_delete(dr_port_t port);
void datarouter_port_set_send_type(port, int cap_id); // 设置发送类型 void dr_port_set_send_type(port, int cap_id);
int datarouter_port_get_send_type(port); // -1 表示未设置 int dr_port_get_send_type(port);
void datarouter_port_set_recv_type(port, int cap_id); // 设置接收类型
int datarouter_port_get_recv_type(port); // -1 表示未设置
int datarouter_port_add_handler(port, handler); // 注册外设 handler int dr_port_add_handler(port, handler);
int datarouter_port_remove_handler(port, handler); // 移除 int dr_port_remove_handler(port, handler);
void datarouter_port_clear_handlers(port); // 清除全部 void dr_port_clear_handlers(port);
int datarouter_port_handler_count(port); // 数量 int dr_port_handler_count(port);
``` ```
### 6.3 发送触发函数(每个 SEND 能力生成一个) ### 6.3 发送触发函数(每个 SEND 能力生成一个)
```c ```c
int datarouter_send_sensor(port, data, len); int dr_send_sensor(const void *data, size_t len);
int datarouter_send_status(port, data, len); int dr_send_status(const void *data, size_t len);
``` ```
返回值:0 成功 / -1 未初始化 / -2 端口无效 / -3 send_type 不匹配 / -4 无 handler 返回值:0 成功 / -1 未初始化 / -2 无端口匹配该 send_type
### 6.4 接收回调(每个 RECV 能力生成一个声明) ### 6.4 接收回调(每个 RECV 能力生成一个声明)
```c ```c
void datarouter_on_recv_rtcm(uint32_t port_id, const void *data, size_t len); void dr_on_recv_rtcm(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 dr_on_recv_command(uint32_t port_id, const void *data, size_t len);
``` ```
库提供弱符号默认空实现,用户定义同名函数覆盖。 库提供弱符号默认空实现。
### 6.5 能力元数据(两组独立) ### 6.5 能力元数据
```c ```c
// 发送能力 int dr_send_cap_count(void);
int datarouter_send_cap_count(void); const char *dr_send_cap_name(int cap_id);
const char *datarouter_send_cap_name(int cap_id); const char *dr_send_cap_desc(int cap_id);
const char *datarouter_send_cap_desc(int cap_id);
// 接收能力 int dr_recv_cap_count(void);
int datarouter_recv_cap_count(void); const char *dr_recv_cap_name(int cap_id);
const char *datarouter_recv_cap_name(int cap_id); const char *dr_recv_cap_desc(int cap_id);
const char *datarouter_recv_cap_desc(int cap_id);
``` ```
--- ---
@@ -314,34 +276,36 @@ const char *datarouter_recv_cap_desc(int cap_id);
```plantuml ```plantuml
@startuml @startuml
actor "应用" as App actor "应用" as App
participant "datarouter_send_sensor()" as Send participant "dr_send_sensor()" as Send
participant "端口表" as Table participant "端口表 ports[]" as Table
participant "handlers[]" as HList 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 否 → return -1 alt 否 → return -1
end end
Send -> Table : ports[2].in_use? end
alt 否 → return -2
end group 遍历端口表
Send -> Table : ports[2].send_type == SENSOR? loop for p in 0..MAX_PORTS-1
alt 否 → return -3 Send -> Table : ports[p].in_use && ports[p].send_type == SENSOR ?
end alt 匹配
Send -> Table : ports[2].count > 0? loop for h in 0..count-1
alt 否 → return -4 Send -> HList : handlers[h](p, data, len)
HList -> Dev : 实际外设写入
end
end
end end
end end
loop for each handler in ports[2].handlers[] alt 至少调了一次 handler
Send -> HList : handlers[i](port, data, len) Send --> App : return 0
HList -> Dev : 实际外设写入 else 无匹配端口
Send --> App : return -2
end end
Send --> App : return 0
@enduml @enduml
``` ```
@@ -353,7 +317,7 @@ actor "外设" as Dev
participant "用户分发代码" as Dispatch participant "用户分发代码" as Dispatch
participant "on_recv_rtcm()" as Callback participant "on_recv_rtcm()" as Callback
Dev -> Dispatch : 收到数据,识别为 rtcm 类型 Dev -> Dispatch : 收到数据,识别为 rtcm
Dispatch -> Callback : on_recv_rtcm(port_id, data, len) Dispatch -> Callback : on_recv_rtcm(port_id, data, len)
Callback --> Dispatch : void Callback --> Dispatch : void
@enduml @enduml
@@ -364,19 +328,14 @@ Callback --> Dispatch : void
```plantuml ```plantuml
@startuml @startuml
state "空闲" as Idle state "空闲" as Idle
state "已创建\nsend=-1 recv=-1" as Created state "已创建\nsend_type=-1\ncount=0" as Created
state "已配置" as Config state "已配置" as Config
[*] --> Idle : reset / 启动 [*] --> Idle : reset / 启动
Idle --> Created : port_create() Idle --> Created : port_create()
Created --> Config : set_send_type / add_handler
Created --> Config : set_send_type / set_recv_type / add_handler Config --> Config : 切换 send_type\n增删 handler
Config --> Created : set_send_type(-1) / clear_handlers
Config --> Config : 独立切换 send_type / recv_type\n增删 handler
Config --> Created : set_send_type(-1) / set_recv_type(-1)\nclear_handlers
Created --> Idle : port_delete() Created --> Idle : port_delete()
Config --> Idle : port_delete() Config --> Idle : port_delete()
Idle --> Created : 复用 ID Idle --> Created : 复用 ID
@@ -387,42 +346,38 @@ Idle --> Created : 复用 ID
## 8. 路由场景 ## 8. 路由场景
### 场景 1:传感器只发送(sensor → UART + TCP) ### 场景 1:sensor 广播到 UART + TCP + CAN
```c ```c
datarouter_port_t p = datarouter_port_create(); dr_port_t p1 = dr_port_create();
datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); dr_port_set_send_type(p1, DR_SEND_sensor);
datarouter_port_add_handler(p, uart_send); dr_port_add_handler(p1, uart_send);
datarouter_port_add_handler(p, tcp_send); dr_port_add_handler(p1, tcp_send);
datarouter_send_sensor(p, "temp:25.3", 9); dr_port_t p2 = dr_port_create();
// → uart_send(p, "temp:25.3", 9) dr_port_set_send_type(p2, DR_SEND_sensor);
// → tcp_send(p, "temp:25.3", 9) dr_port_add_handler(p2, can_send);
dr_send_sensor("temp:25.3", 9);
// → uart_send(p1, "temp:25.3", 9)
// → tcp_send(p1, "temp:25.3", 9)
// → can_send(p2, "temp:25.3", 9)
``` ```
### 场景 2:RTCM 只接收 ### 场景 2:仅接收
```c ```c
datarouter_port_t p = datarouter_port_create(); void dr_on_recv_rtcm(uint32_t port_id, const void *data, size_t len) {
datarouter_port_set_recv_type(p, DATAROUTER_RECV_rtcm); apply_rtcm_correction(data, len);
// 无 send_type,不注册 handler }
// 外设收到后:dr_on_recv_rtcm(port, data, len);
// 外设收到数据后由用户调回调:
datarouter_on_recv_rtcm(p, data, len);
``` ```
### 场景 3:双向端口(status 发送 + command 接收) ### 场景 3:运行时移除 handler
```c ```c
datarouter_port_t p = datarouter_port_create(); dr_port_remove_handler(p1, tcp_send);
datarouter_port_set_send_type(p, DATAROUTER_SEND_status); // 下次 send_sensor 不再触发 tcp
datarouter_port_set_recv_type(p, DATAROUTER_RECV_command);
datarouter_port_add_handler(p, uart_send);
// 发送
datarouter_send_status(p, "status:OK", 9);
// 接收
datarouter_on_recv_command(p, "reboot", 6);
``` ```
--- ---
@@ -451,32 +406,26 @@ Weak ..> Result : 后备
| 返回值 | 含义 | | 返回值 | 含义 |
|:------:|------| |:------:|------|
| 0 | 成功,所有 handler 被调用 | | 0 | 成功,至少有一个 handler 被调用 |
| -1 | 库未初始化(未调 init) | | -1 | 库未初始化(未调 init) |
| -2 | 端口无效(越界/未创建/已删除) | | -2 | 无端口匹配该 send_type(或匹配端口均无 handler) |
| -3 | 端口 send_type 与调用的发送函数不匹配 |
| -4 | 端口 handler 列表为空(count == 0) |
--- ---
## 11. 配置扩展 ## 11. 配置扩展
### 增加能力
```c ```c
#define DATAROUTER_SEND_CAPABILITIES \ #define DR_SEND_CAPABILITIES \
DATAROUTER_SEND(sensor, "Sensor data") \ DR_SEND(sensor, "Sensor data") \
DATAROUTER_SEND(debug, "Debug log") /* 新增发送能力 */ DR_SEND(debug, "Debug log") /* 新增 */
#define DATAROUTER_RECV_CAPABILITIES \ #define DR_RECV_CAPABILITIES \
DATAROUTER_RECV(rtcm, "RTCM data") \ DR_RECV(rtcm, "RTCM data") \
DATAROUTER_RECV(ota, "OTA firmware") /* 新增接收能力 */ DR_RECV(ota, "OTA firmware") /* 新增 */
``` ```
### 自定义限制
```bash ```bash
cmake -DCMAKE_C_FLAGS="-DDATAROUTER_MAX_PORTS=128 -DDATAROUTER_MAX_HANDLERS_PER_PORT=8" cmake -DCMAKE_C_FLAGS="-DDR_MAX_PORTS=128 -DDR_MAX_HANDLERS_PER_PORT=8"
``` ```
--- ---
+122 -81
View File
@@ -1,40 +1,37 @@
# DataRouter # DataRouter
轻量级 C 语言数据路由库。**发送能力和接收能力分开声明**,端口独立设置 send_type 和 recv_type,支持多外设一对多广播路由。 轻量级 C 语言数据路由库。**发送能力和接收能力分开声明**,端口绑定 `send_type` 和外设 handler 列表。调用 `dr_send_<id>(data, len)` 时自动广播到所有匹配端口。
## 概念 ## 概念
| 术语 | 说明 | | 术语 | 说明 |
|------|------| |------|------|
| **发送能力 (Send Capability)** | 编译期声明的可发送报文类型,生成 `datarouter_send_<id>()` | | **发送能力 (Send Capability)** | 编译期声明的可发送报文类型,生成 `dr_send_<id>(data, len)` |
| **接收能力 (Recv Capability)** | 编译期声明的可接收报文类型,生成 `datarouter_on_recv_<id>()` | | **接收能力 (Recv Capability)** | 编译期声明的可接收报文类型,生成 `dr_on_recv_<id>(port, data, len)` |
| **端口 (Port)** | 运行期连接点,独立设置 send_type + recv_type + handler 列表 | | **端口 (Port)** | 运行期创建的连接点,包含 `send_type` + handler 列表 |
| **Handler** | 用户实现的物理外设发送函数,一个端口可注册多个(默认上限 4) | | **Handler** | 用户实现的物理外设发送函数,一个端口可注册多个(默认上限 4) |
## 快速开始 ## 快速开始
### 1. 声明能力 ### 1. 声明能力
创建自己的 `datarouter_config.h`(可参考 `datarouter_config_template.h`): 创建自己的 `dr_config.h`(可参考 `dr_config_template.h`):
```c ```c
#define DATAROUTER_SEND_CAPABILITIES \ #define DR_SEND_CAPABILITIES \
DATAROUTER_SEND(sensor, "Sensor data report") \ DR_SEND(sensor, "Sensor data report") \
DATAROUTER_SEND(status, "Device status report") DR_SEND(status, "Device status report")
#define DATAROUTER_RECV_CAPABILITIES \ #define DR_RECV_CAPABILITIES \
DATAROUTER_RECV(rtcm, "RTCM correction data") \ DR_RECV(rtcm, "RTCM correction data") \
DATAROUTER_RECV(command, "Remote control command") DR_RECV(command, "Remote control command")
``` ```
> 库默认自带一份示例配置。将你的 `datarouter_config.h` 放到项目目录, > 库自带默认配置。将你的 `dr_config.h` 放到项目目录,
> 编译时添加 `-I /path/to/your/config` 即可覆盖。无需修改库源代码。 > 编译时添加 `-I /path/to/your/config` 即可覆盖。无需修改库源码。
> >
> **注意**:确保你的配置目录在包含路径中出现在库目录之前,否则编译 `datarouter.c`
> 时仍会使用库的默认配置。在 CMake 中可参考:
> ```cmake > ```cmake
> # 你的 CMakeLists.txt > include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR})
> include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}) # 全局优先
> add_subdirectory(path/to/DataRouter) > add_subdirectory(path/to/DataRouter)
> target_link_libraries(my_app DataRouter) > target_link_libraries(my_app DataRouter)
> ``` > ```
@@ -54,40 +51,27 @@ static void tcp_send(uint32_t port_id, const void *data, size_t len) {
### 3. 创建端口 ### 3. 创建端口
```c ```c
datarouter_init(); dr_init();
/* —— 只发送:传感器上报 —— */ /* —— 传感器上报:sensor 报文走 UART + TCP —— */
datarouter_port_t tx = datarouter_port_create(); dr_port_t tx = dr_port_create();
datarouter_port_set_send_type(tx, DATAROUTER_SEND_sensor); dr_port_set_send_type(tx, DR_SEND_sensor);
datarouter_port_add_handler(tx, uart_send); dr_port_add_handler(tx, uart_send);
datarouter_port_add_handler(tx, tcp_send); dr_port_add_handler(tx, tcp_send);
datarouter_send_sensor(tx, "temperature:25.3", 16); // → UART + TCP
/* —— 只接收:RTCM 修正 —— */ /* 不指定端口,库自动广播到所有 send_type=sensor 的端口 */
datarouter_port_t rx = datarouter_port_create(); dr_send_sensor("temperature:25.3", 16); // → UART + TCP
datarouter_port_set_recv_type(rx, DATAROUTER_RECV_rtcm);
// 外设收到数据后由用户调回调:
datarouter_on_recv_rtcm(rx, data, len);
/* —— 同时收发:状态上报 + 命令接收 —— */ /* —— 接收:实现回调,外设数据到达后自行调用 —— */
datarouter_port_t bi = datarouter_port_create(); dr_on_recv_rtcm(rx_port, data, len);
datarouter_port_set_send_type(bi, DATAROUTER_SEND_status);
datarouter_port_set_recv_type(bi, DATAROUTER_RECV_command);
datarouter_port_add_handler(bi, uart_send);
datarouter_send_status(bi, "status:OK", 9);
datarouter_on_recv_command(bi, "reboot", 6);
``` ```
### 4. 实现接收回调 ### 4. 实现接收回调
```c ```c
void datarouter_on_recv_rtcm(uint32_t port_id, const void *data, size_t len) { void dr_on_recv_rtcm(uint32_t port_id, const void *data, size_t len) {
apply_rtcm_correction(data, len); apply_rtcm_correction(data, len);
} }
void datarouter_on_recv_command(uint32_t port_id, const void *data, size_t len) {
execute_command(data, len);
}
``` ```
## API 一览 ## API 一览
@@ -95,41 +79,40 @@ void datarouter_on_recv_command(uint32_t port_id, const void *data, size_t len)
### 初始化 ### 初始化
```c ```c
void datarouter_init(void); // 幂等初始化 void dr_init(void); // 幂等初始化
void datarouter_reset(void); // 重置全部状态 void dr_reset(void); // 重置全部状态
``` ```
### 端口管理 ### 端口管理
```c ```c
datarouter_port_t datarouter_port_create(void); // 创建端口 dr_port_t dr_port_create(void); // 创建端口
void datarouter_port_delete(datarouter_port_t port); // 删除 void dr_port_delete(dr_port_t port); // 删除
void datarouter_port_set_send_type(port, int cap_id); // 设置发送类型 void dr_port_set_send_type(port, int cap_id); // 设置发送类型
int datarouter_port_get_send_type(port); // -1 = 未设置 int dr_port_get_send_type(port); // -1 = 未设置
void datarouter_port_set_recv_type(port, int cap_id); // 设置接收类型
int datarouter_port_get_recv_type(port); // -1 = 未设置
int datarouter_port_add_handler(port, handler); // 注册外设 handler int dr_port_add_handler(port, handler); // 注册外设 handler
int datarouter_port_remove_handler(port, handler); // 移除 int dr_port_remove_handler(port, handler); // 移除
void datarouter_port_clear_handlers(port); // 清除全部 void dr_port_clear_handlers(port); // 清除全部
int datarouter_port_handler_count(port); // 当前数量 int dr_port_handler_count(port); // 当前数量
``` ```
### 发送触发(每个 SEND 能力生成一个) ### 发送触发(每个 SEND 能力生成一个)
```c ```c
int datarouter_send_sensor(port, data, len); // 0 成功 / -1 未初始化 int dr_send_sensor(const void *data, size_t len); // 0 成功
int datarouter_send_status(port, data, len); // -2 端口无效 int dr_send_status(const void *data, size_t len); // -1 未初始化
// -3 send_type 不匹配 // -2 无匹配端口
// -4 无 handler
``` ```
不指定端口,库遍历所有端口,`send_type` 匹配的逐一调用其全部 handler。
### 接收回调(每个 RECV 能力生成一个声明) ### 接收回调(每个 RECV 能力生成一个声明)
```c ```c
void datarouter_on_recv_rtcm(uint32_t port_id, const void *data, size_t len); void dr_on_recv_rtcm(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 dr_on_recv_command(uint32_t port_id, const void *data, size_t len);
``` ```
库提供弱符号默认空实现,用户定义同名函数覆盖。 库提供弱符号默认空实现,用户定义同名函数覆盖。
@@ -137,29 +120,30 @@ void datarouter_on_recv_command(uint32_t port_id, const void *data, size_t len);
### 能力元数据(两组独立) ### 能力元数据(两组独立)
```c ```c
int datarouter_send_cap_count(void); int dr_send_cap_count(void);
const char *datarouter_send_cap_name(int cap_id); const char *dr_send_cap_name(int cap_id);
const char *datarouter_send_cap_desc(int cap_id); const char *dr_send_cap_desc(int cap_id);
int datarouter_recv_cap_count(void); int dr_recv_cap_count(void);
const char *datarouter_recv_cap_name(int cap_id); const char *dr_recv_cap_name(int cap_id);
const char *datarouter_recv_cap_desc(int cap_id); const char *dr_recv_cap_desc(int cap_id);
``` ```
## 路由模型 ## 路由模型
``` ```
send_sensor(port, data, len) dr_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
## 编译 ## 编译
@@ -171,9 +155,65 @@ cmake --build build
可选配置: 可选配置:
```bash ```bash
cmake -DCMAKE_C_FLAGS="-DDATAROUTER_MAX_PORTS=128 -DDATAROUTER_MAX_HANDLERS_PER_PORT=8" cmake -DCMAKE_C_FLAGS="-DDR_MAX_PORTS=128 -DDR_MAX_HANDLERS_PER_PORT=8"
``` ```
## 嵌入式 / Cortex-M 构建
库支持 `arm-none-eabi-gcc` 交叉编译,提供 **7 个 CMake preset** 覆盖常见 Cortex-M 目标:
```bash
# 列出所有 preset
cmake --list-presets
# Cortex-M3(软浮点)
cmake --preset cm3
cmake --build --preset cm3
# Cortex-M4F(硬件浮点,单精度)
cmake --preset cm4-hard
cmake --build --preset cm4-hard
# Cortex-M7F(硬件浮点,双精度)
cmake --preset cm7-hard-dp
cmake --build --preset cm7-hard-dp
```
> 使用 preset 编译需要 `arm-none-eabi-gcc` 在 PATH 中。
> 各 preset 均启用 `DR_SOURCE_ONLY` 模式——只提供源文件路径,
> 你需要将 `datarouter.c` 加入自己的 target 以继承项目编译标志:
```cmake
# 你的 CMakeLists.txt
set(DR_SOURCE_ONLY ON)
FetchContent_MakeAvailable(datarouter)
target_sources(my_app PRIVATE ${datarouter_SOURCE_DIR}/datarouter.c)
target_include_directories(my_app PRIVATE ${datarouter_SOURCE_DIR})
```
### 编译兼容性矩阵
启用 `DR_COMPILE_CHECK` 可自动检测 `arm-none-eabi-gcc` 并对 CM3/CM4/CM7
各变体执行编译验证:
```bash
cmake -B build-check -DDR_COMPILE_CHECK=ON
cmake --build build-check # 生成 check_cm3, check_cm4-hard, … 等 target
# 或单独检查某个变体:
make check_cm7-hard-dp
```
### 内置 CMake Preset 列表
| Preset | 目标 | FPU | ABI |
|--------|------|-----|-----|
| `cm3` | Cortex-M3 | 无 | soft |
| `cm4-soft` | Cortex-M4 | 无 | soft |
| `cm4-hard` | Cortex-M4F | FPv4-SP-D16 | hard |
| `cm7-soft` | Cortex-M7 | 无 | soft |
| `cm7-hard-sp` | Cortex-M7F | FPv5-SP-D16 | hard |
| `cm7-hard-dp` | Cortex-M7F | FPv5-D16 | hard |
## 测试 ## 测试
```bash ```bash
@@ -183,18 +223,19 @@ ctest --test-dir build
./build/test_datarouter ./build/test_datarouter
``` ```
当前 27 个测试用例,覆盖端口管理、send_type/recv_type 独立设置、handler 增删、一对多广播、接收回调、能力元数据、错误边界。 当前 22 个测试用例,覆盖端口管理、send_type 与 handler 设置、自动广播路由、接收回调、能力元数据。
## 文件结构 ## 文件结构
``` ```
datarouter_config.h 发送 + 接收能力配置(用户编辑) dr_config.h 发送 + 接收能力配置(用户可覆盖)
datarouter.h 公共 API + X-macro 展开 datarouter.h 公共 API + X-macro 展开
datarouter.c 实现(端口表 + handler 遍历) datarouter.c 实现(端口表 + send 遍历)
example.c 收发分离使用示例 dr_config_template.h 外部配置模板
tests/test_datarouter.c 单元测试(27 用例) example.c 使用示例
DESIGN.md 详细设计文档(含 PlantUML 图) tests/test_datarouter.c 单元测试(22 用例)
TEST_CASES.md 测试用例文档 DESIGN.md 详细设计文档(含 PlantUML 图)
TEST_CASES.md 测试用例文档
``` ```
## 依赖 ## 依赖
+27 -45
View File
@@ -17,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
``` ```
@@ -27,68 +26,53 @@ ctest --test-dir build
| # | 测试名 | 描述 | 步骤 | 预期 | | # | 测试名 | 描述 | 步骤 | 预期 |
|---|--------|------|------|------| |---|--------|------|------|------|
| 1 | `error_uninit` | 未 init 时发送 | 不调 init 直接 `send_sensor(0, ...)` | 返回 -1 | | 1 | `error_uninit` | 未 init 时发送 | `send_sensor("d", 1)` | 返回 -1 |
### 2. 端口管理(4 个) ### 2. 端口管理(4 个)
| # | 测试名 | 描述 | 步骤 | 预期 | | # | 测试名 | 描述 | 步骤 | 预期 |
|---|--------|------|------|------| |---|--------|------|------|------|
| 2 | `port_create_basic` | 基本创建 | `port_create()` | 返回 ≥ 0 | | 2 | `port_create_basic` | 基本创建 | `port_create()` | 返回 ≥ 0 |
| 3 | `port_create_max` | 满容量(64) | 循环创建到满再尝试 | 64 个成功,第 65 个 INVALID | | 3 | `port_create_max` | 满容量 64 | 循环创建到满再尝试 | 64 个成功,第 65 个 INVALID |
| 4 | `port_delete_invalid` | 删除无效 ID | `delete(-1)` + `delete(999)` | 不崩溃 | | 4 | `port_delete_invalid` | 删除无效 ID | `delete(-1)` + `delete(999)` | 不崩溃 |
| 5 | `port_default_state` | 默认状态 | 创建端口,查 send/recv/handler | send_type=-1, recv_type=-1, count=0 | | 5 | `port_default_state` | 默认状态 | 新建端口,查 send_type / handler count | `send_type=-1`, `count=0` |
### 3. send_type / recv_type 独立设置(6 个) ### 3. send_type 与 handler(3 个)
| # | 测试名 | 描述 | 步骤 | 预期 | | # | 测试名 | 描述 | 步骤 | 预期 |
|---|--------|------|------|------| |---|--------|------|------|------|
| 6 | `send_type_set_get` | 设置发送类型 | `set_send_type(sensor)` → `get_send_type()` | 返回 sensor | | 6 | `send_type_set_get` | 设置并清除 send_type | `set_send_type(sensor)` → `get` → `set(-1)` → `get` | sensor → -1 |
| 7 | `recv_type_set_get` | 设置接收类型 | `set_recv_type(rtcm)` → `get_recv_type()` | 返回 rtcm | | 7 | `handler_add_remove` | 添加后移除 handler | add(h) count=1 → remove(h) count=0 | count 正确 |
| 8 | `types_independent` | 收发互不干扰 | 同时 `set_send_type(sensor)` + `set_recv_type(command)` | get_send=sensor, get_recv=command | | 8 | `handler_clear` | 清除全部 handler | add → clear | count == 0 |
| 9 | `send_type_clear` | 清除发送类型 | `set_send_type(sensor)` → `set_send_type(-1)` | `get_send_type()` == -1 |
| 10 | `recv_type_clear` | 清除接收类型 | `set_recv_type(rtcm)` → `set_recv_type(-1)` | `get_recv_type()` == -1 |
| 11 | `types_from_invalid` | 无效端口查类型 | `get_send_type(-1/999)` / `get_recv_type(-1/999)` | 均返回 -1 |
### 4. handler 增删(2 个) ### 4. 发送 — 自动广播(11 个)
| # | 测试名 | 描述 | 步骤 | 预期 | | # | 测试名 | 描述 | 步骤 | 预期 |
|---|--------|------|------|------| |---|--------|------|------|------|
| 12 | `handler_add_remove` | 添加后移除 | ① add(a) add(b) count=2 ② remove(a) count=1 | count 正确 | | 9 | `send_no_match_port` | 无匹配端口 | 不创建任何端口,直接 `send_sensor` | 返回 -2 |
| 13 | `handler_clear` | 清除全部 | add 后 clear | count == 0 | | 10 | `send_one_port_one_handler` | 单端口单 handler | 创建端口中 send_type + add(h) → send | 返回 0;handler 1 次 |
| 11 | `send_one_port_multi_handler` | 单端口双 handler | add(h) + add(h2) → send | handler 2 次 |
| 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 个)
| # | 测试名 | 描述 | 步骤 | 预期 | | # | 测试名 | 描述 | 步骤 | 预期 |
|---|--------|------|------|------| |---|--------|------|------|------|
| 14 | `send_one_handler` | 单 handler 发送 | send_type + add_handler → send_sensor | 返回 0;handler 被调用 1 次 | | 20 | `recv_callback` | 用户实现接收回调 | `on_recv_rtcm(0, "d", 1)` | 回调被触发 |
| 15 | `send_multi_handlers` | 三个 handler 广播 | 注册 3 个 handler 后 send | 返回 0;handler 被调用 3 次 |
| 16 | `send_type_mismatch` | 发送类型不匹配 | set_send_type(sensor) 但调用 send_status | 返回 -3;handler 不被调用 |
| 17 | `send_no_handler` | 未注册 handler | 只 set_send_type 不 add_handler | 返回 -4 |
| 18 | `send_no_send_type` | 没有 send_type | 端口只有 recv_type,调 send_sensor | 返回 -3 |
| 19 | `send_remove_handler` | 移除后少广播 | 注册 2 handler → send count=2 → remove 1 → send count=1 | 数量正确 |
| 20 | `send_clear_handlers` | 清空后无法发送 | add → clear → send | 返回 -4 |
| 21 | `send_zero_length` | 零长度数据 | send(port, "x", 0) | 返回 0;handler 被调用 |
| 22 | `send_null_data` | NULL 数据 | send(port, NULL, 0) | 返回 0;handler 被调用 |
### 6. 接收回调(1 个) ### 6. 能力元数据(2 个)
| # | 测试名 | 描述 | 步骤 | 预期 | | # | 测试名 | 描述 | 步骤 | 预期 |
|---|--------|------|------|------| |---|--------|------|------|------|
| 23 | `recv_callbacks` | 用户实现接收回调 | ① `on_recv_rtcm(0, ...)` ② `on_recv_command(1, ...)` | 各自触发,互不干扰 | | 21 | `send_cap_meta` | 发送能力元数据 | count / name / desc / 边界 | SEND 两组正确 |
| 22 | `recv_cap_meta` | 接收能力元数据 | count / name / desc / 边界 | RECV 两组正确 |
### 7. 能力元数据(2 个)
| # | 测试名 | 描述 | 步骤 | 预期 |
|---|--------|------|------|------|
| 24 | `send_cap_meta` | 发送能力元数据 | 查 count / name / desc / 边界 | SEND 两组正确 |
| 25 | `recv_cap_meta` | 接收能力元数据 | 查 count / name / desc / 边界 | RECV 两组正确 |
### 8. 错误处理(2 个)
| # | 测试名 | 描述 | 步骤 | 预期 |
|---|--------|------|------|------|
| 26 | `error_invalid_port` | 无效端口 | `send_sensor(999/-1, ...)` | 均返回 -2 |
| 27 | `error_deleted_port` | 已删除端口 | 创建→设好→删除→send | 返回 -2 |
## 覆盖矩阵 ## 覆盖矩阵
@@ -96,11 +80,9 @@ ctest --test-dir build
|----------|:------:|------------| |----------|:------:|------------|
| 未初始化 | 1 | send → -1 | | 未初始化 | 1 | send → -1 |
| 端口管理 | 4 | 创建/满容量/删除异常/默认状态 | | 端口管理 | 4 | 创建/满容量/删除异常/默认状态 |
| send_type / recv_type | 6 | 设置/清除/独立/无效端口 | | send_type & handler | 3 | 设置/清除/添加/移除/清空 |
| handler 增删 | 2 | 添加/移除/清空 | | 发送自动广播 | 11 | 无匹配/单播/多 handler 单端口/多端口同类型/多端口多 handler/类型过滤/移除后/删除后/零长/NULL/多能力独立 |
| 发送触发 | 9 | 单播/多播/类型不匹配/无handler/无send_type/移除后/清空后/零长/NULL |
| 接收回调 | 1 | 用户实现覆盖弱符号 | | 接收回调 | 1 | 用户实现覆盖弱符号 |
| 能力元数据 | 2 | SEND 与 RECV 两组独立查询 | | 能力元数据 | 2 | SEND 与 RECV 两组独立查询 |
| 错误处理 | 2 | 无效端口/已删除端口 |
**总计:27 个测试用例** **总计:22 个测试用例**
+40
View File
@@ -0,0 +1,40 @@
####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() #######
####### Any changes to this file will be overwritten by the next CMake run ####
####### The input file was DataRouterConfig.cmake.in ########
get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../" ABSOLUTE)
macro(set_and_check _var _file)
set(${_var} "${_file}")
if(NOT EXISTS "${_file}")
message(FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist !")
endif()
endmacro()
macro(check_required_components _NAME)
foreach(comp ${${_NAME}_FIND_COMPONENTS})
if(NOT ${_NAME}_${comp}_FOUND)
if(${_NAME}_FIND_REQUIRED_${comp})
set(${_NAME}_FOUND FALSE)
endif()
endif()
endforeach()
endmacro()
####################################################################################
set_and_check(DataRouter_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")
set_and_check(DataRouter_LIBRARY_DIR "${PACKAGE_PREFIX_DIR}/lib")
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)
BIN
View File
Binary file not shown.
Binary file not shown.
+16
View File
@@ -0,0 +1,16 @@
@PACKAGE_INIT@
set(DataRouter_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
set(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)
+68 -111
View File
@@ -1,167 +1,150 @@
/** /**
* @file datarouter.c * @file datarouter.c
* @brief 数据路由库 —— 实现
*/ */
#include "datarouter.h" #include "datarouter.h"
#include <string.h> #include <string.h>
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
#define DATAROUTER_WEAK __attribute__((weak)) #define DR_WEAK __attribute__((weak))
#else #else
#define DATAROUTER_WEAK #define DR_WEAK
#endif #endif
#ifndef DATAROUTER_MAX_PORTS #ifndef DR_MAX_PORTS
#define DATAROUTER_MAX_PORTS 64 #define DR_MAX_PORTS 64
#endif #endif
typedef struct { typedef struct {
int send_type; int send_type;
int recv_type; dr_send_handler_t handlers[DR_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;
} port_entry_t; } port_entry_t;
static port_entry_t ports[DATAROUTER_MAX_PORTS]; static port_entry_t ports[DR_MAX_PORTS];
static uint8_t initialized; static uint8_t initialized;
/* ---- 发送能力元数据 ---- */ /* ---- 元数据 ---- */
static const char *send_names[] = { static const char *send_names[] = {
#define DATAROUTER_SEND(id, desc) #id, #define DR_SEND(id, desc) #id,
DATAROUTER_SEND_CAPABILITIES DR_SEND_CAPABILITIES
#undef DATAROUTER_SEND #undef DR_SEND
}; };
static const char *send_descs[] = { static const char *send_descs[] = {
#define DATAROUTER_SEND(id, desc) desc, #define DR_SEND(id, desc) desc,
DATAROUTER_SEND_CAPABILITIES DR_SEND_CAPABILITIES
#undef DATAROUTER_SEND #undef DR_SEND
}; };
/* ---- 接收能力元数据 ---- */
static const char *recv_names[] = { static const char *recv_names[] = {
#define DATAROUTER_RECV(id, desc) #id, #define DR_RECV(id, desc) #id,
DATAROUTER_RECV_CAPABILITIES DR_RECV_CAPABILITIES
#undef DATAROUTER_RECV #undef DR_RECV
}; };
static const char *recv_descs[] = { static const char *recv_descs[] = {
#define DATAROUTER_RECV(id, desc) desc, #define DR_RECV(id, desc) desc,
DATAROUTER_RECV_CAPABILITIES DR_RECV_CAPABILITIES
#undef DATAROUTER_RECV #undef DR_RECV
}; };
/* ---- 弱符号接收回调默认实现 ---- */ /* ---- 弱符号接收回调 ---- */
#define DATAROUTER_RECV(id, desc) \ #define DR_RECV(id, desc) \
DATAROUTER_WEAK void datarouter_on_recv_##id( \ DR_WEAK void dr_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; \ DR_RECV_CAPABILITIES
} #undef DR_RECV
DATAROUTER_RECV_CAPABILITIES
#undef DATAROUTER_RECV
/* ---- 发送触发函数实现 ---- */ /* ---- 发送触发函数 ---- */
#define DATAROUTER_SEND(id, desc) \ #define DR_SEND(id, desc) \
int datarouter_send_##id(uint32_t port_id, const void *data, size_t len) \ int dr_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].send_type != DATAROUTER_SEND_##id) return -3; \ for (uint32_t _p = 0; _p < DR_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 != DR_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_SEND_CAPABILITIES DR_SEND_CAPABILITIES
#undef DATAROUTER_SEND #undef DR_SEND
/* ---- 端口管理 ---- */ /* ---- 端口管理 ---- */
void datarouter_init(void) void dr_init(void)
{ {
if (initialized) return; if (initialized) return;
memset(ports, 0, sizeof(ports)); memset(ports, 0, sizeof(ports));
initialized = 1; initialized = 1;
} }
void datarouter_reset(void) void dr_reset(void)
{ {
initialized = 0; initialized = 0;
memset(ports, 0, sizeof(ports)); memset(ports, 0, sizeof(ports));
} }
datarouter_port_t datarouter_port_create(void) dr_port_t dr_port_create(void)
{ {
if (!initialized) return DATAROUTER_INVALID_PORT; if (!initialized) return DR_INVALID_PORT;
for (int i = 0; i < DATAROUTER_MAX_PORTS; i++) { for (int i = 0; i < DR_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].send_type = -1; ports[i].send_type = -1;
ports[i].recv_type = -1;
ports[i].count = 0; ports[i].count = 0;
return (datarouter_port_t)i; return (dr_port_t)i;
} }
} }
return DATAROUTER_INVALID_PORT; return DR_INVALID_PORT;
} }
void datarouter_port_delete(datarouter_port_t port) void dr_port_delete(dr_port_t port)
{ {
if (!initialized) return; if (!initialized) return;
if (port < 0 || port >= DATAROUTER_MAX_PORTS) return; if (port < 0 || port >= DR_MAX_PORTS) return;
ports[port].in_use = 0; ports[port].in_use = 0;
} }
void datarouter_port_set_send_type(datarouter_port_t port, int cap_id) void dr_port_set_send_type(dr_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 >= DR_MAX_PORTS || !ports[port].in_use) return;
ports[port].send_type = cap_id; ports[port].send_type = cap_id;
} }
int datarouter_port_get_send_type(datarouter_port_t port) int dr_port_get_send_type(dr_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 >= DR_MAX_PORTS || !ports[port].in_use) return -1;
return ports[port].send_type; return ports[port].send_type;
} }
void datarouter_port_set_recv_type(datarouter_port_t port, int cap_id) int dr_port_add_handler(dr_port_t port, dr_send_handler_t handler)
{
if (!initialized) return;
if (port < 0 || port >= DATAROUTER_MAX_PORTS || !ports[port].in_use) return;
ports[port].recv_type = cap_id;
}
int datarouter_port_get_recv_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 >= DR_MAX_PORTS || !ports[port].in_use) return -1;
return ports[port].recv_type; if (ports[port].count >= DR_MAX_HANDLERS_PER_PORT) return -2;
}
int datarouter_port_add_handler(datarouter_port_t port, datarouter_send_handler_t handler)
{
if (!initialized) return -1;
if (port < 0 || port >= DATAROUTER_MAX_PORTS || !ports[port].in_use) return -1;
if (ports[port].count >= DATAROUTER_MAX_HANDLERS_PER_PORT) return -2;
for (int i = 0; i < ports[port].count; i++) for (int i = 0; i < ports[port].count; i++)
if (ports[port].handlers[i] == handler) return -3; if (ports[port].handlers[i] == handler) return -3;
ports[port].handlers[ports[port].count++] = handler; ports[port].handlers[ports[port].count++] = handler;
return 0; return 0;
} }
int datarouter_port_remove_handler(datarouter_port_t port, datarouter_send_handler_t handler) int dr_port_remove_handler(dr_port_t port, dr_send_handler_t handler)
{ {
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 >= DR_MAX_PORTS || !ports[port].in_use) return -1;
for (int i = 0; i < ports[port].count; i++) { for (int i = 0; i < ports[port].count; i++) {
if (ports[port].handlers[i] == handler) { if (ports[port].handlers[i] == handler) {
ports[port].handlers[i] = ports[port].handlers[--ports[port].count]; ports[port].handlers[i] = ports[port].handlers[--ports[port].count];
@@ -171,52 +154,26 @@ int datarouter_port_remove_handler(datarouter_port_t port, datarouter_send_handl
return -2; return -2;
} }
void datarouter_port_clear_handlers(datarouter_port_t port) void dr_port_clear_handlers(dr_port_t port)
{ {
if (!initialized) return; if (!initialized) return;
if (port < 0 || port >= DATAROUTER_MAX_PORTS || !ports[port].in_use) return; if (port < 0 || port >= DR_MAX_PORTS || !ports[port].in_use) return;
ports[port].count = 0; ports[port].count = 0;
} }
int datarouter_port_handler_count(datarouter_port_t port) int dr_port_handler_count(dr_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 >= DR_MAX_PORTS || !ports[port].in_use) return -1;
return ports[port].count; return ports[port].count;
} }
/* ---- 元数据查询 ---- */ /* ---- 元数据查询 ---- */
int datarouter_send_cap_count(void) int dr_send_cap_count(void) { return DR_SEND_CAP_COUNT; }
{ const char *dr_send_cap_name(int c) { return (c<0||c>=DR_SEND_CAP_COUNT)?NULL:send_names[c]; }
return DATAROUTER_SEND_CAP_COUNT; const char *dr_send_cap_desc(int c) { return (c<0||c>=DR_SEND_CAP_COUNT)?NULL:send_descs[c]; }
}
const char *datarouter_send_cap_name(int cap_id) int dr_recv_cap_count(void) { return DR_RECV_CAP_COUNT; }
{ const char *dr_recv_cap_name(int c) { return (c<0||c>=DR_RECV_CAP_COUNT)?NULL:recv_names[c]; }
if (cap_id < 0 || cap_id >= DATAROUTER_SEND_CAP_COUNT) return NULL; const char *dr_recv_cap_desc(int c) { return (c<0||c>=DR_RECV_CAP_COUNT)?NULL:recv_descs[c]; }
return send_names[cap_id];
}
const char *datarouter_send_cap_desc(int cap_id)
{
if (cap_id < 0 || cap_id >= DATAROUTER_SEND_CAP_COUNT) return NULL;
return send_descs[cap_id];
}
int datarouter_recv_cap_count(void)
{
return DATAROUTER_RECV_CAP_COUNT;
}
const char *datarouter_recv_cap_name(int cap_id)
{
if (cap_id < 0 || cap_id >= DATAROUTER_RECV_CAP_COUNT) return NULL;
return recv_names[cap_id];
}
const char *datarouter_recv_cap_desc(int cap_id)
{
if (cap_id < 0 || cap_id >= DATAROUTER_RECV_CAP_COUNT) return NULL;
return recv_descs[cap_id];
}
+53 -67
View File
@@ -4,36 +4,24 @@
* *
* ## 概念 * ## 概念
* *
* - **发送能力 (Send Capability)**:编译期声明的可发送报文类型 * - **发送能力**:编译期声明的可发送报文类型,生成 dr_send_<id>(data, len)
* - **接收能力 (Recv Capability)**:编译期声明的可接收报文类型 * - **接收能力**:编译期声明的可接收报文类型,生成 dr_on_recv_<id>(port, data, len)
* - **端口 (Port)**:运行期连接点,独立设置 send_type + recv_type + 外设 handler 列表 * - **端口**:运行期创建的连接点,包含 send_type + handler 列表
* *
* ## 使用流程 * ## 路由
* *
* 1. 创建 datarouter_config.h 声明发送和接收能力(见 datarouter_config_template.h) * 调用 dr_send_<id>(data, len) 时,库遍历所有端口,
* 2. 编译时通过 -I 指向你的配置目录;若无,库使用内置默认配置 * 找到 send_type 匹配的端口,逐一调用其全部 handler。无需指定从哪个端口发送。
* 3. datarouter_init() 初始化 *
* 4. port_create() 创建端口 * 接收侧由用户自行调用 dr_on_recv_<id>(port, data, len) 处理外设收到的数据。
* 5. port_set_send_type() 设置发送类型 / port_set_recv_type() 设置接收类型
* 6. port_add_handler() 注册外设发送函数
* 7. 调 datarouter_send_<id>() 触发发送(库调用所有 handler)
* 8. 实现 datarouter_on_recv_<id>() 处理接收数据
*/ */
#ifndef DATAROUTER_H #ifndef DR_H
#define DATAROUTER_H #define DR_H
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
/*
* 配置加载策略:
* 1. 优先从 include path 搜索 <datarouter_config.h>(用户提供的版本)
* 2. 找不到则回退到库自带的 "datarouter_config.h"(默认配置)
*
* 用户只需将自己的 datarouter_config.h 放到项目目录,
* 编译时添加 -I /path/to/your/config 即可覆盖内置配置。
*/
#if defined(__has_include) #if defined(__has_include)
# if __has_include(<datarouter_config.h>) # if __has_include(<datarouter_config.h>)
# include <datarouter_config.h> # include <datarouter_config.h>
@@ -50,77 +38,75 @@ extern "C" {
/* ========== 发送能力枚举 ========== */ /* ========== 发送能力枚举 ========== */
enum datarouter_send_cap { enum dr_send_cap {
#define DATAROUTER_SEND(id, desc) DATAROUTER_SEND_##id, #define DR_SEND(id, desc) DR_SEND_##id,
DATAROUTER_SEND_CAPABILITIES DR_SEND_CAPABILITIES
#undef DATAROUTER_SEND #undef DR_SEND
DATAROUTER_SEND_CAP_COUNT DR_SEND_CAP_COUNT
}; };
/* ========== 接收能力枚举 ========== */ /* ========== 接收能力枚举 ========== */
enum datarouter_recv_cap { enum dr_recv_cap {
#define DATAROUTER_RECV(id, desc) DATAROUTER_RECV_##id, #define DR_RECV(id, desc) DR_RECV_##id,
DATAROUTER_RECV_CAPABILITIES DR_RECV_CAPABILITIES
#undef DATAROUTER_RECV #undef DR_RECV
DATAROUTER_RECV_CAP_COUNT DR_RECV_CAP_COUNT
}; };
/* ========== 外设发送 handler 类型 ========== */ /* ========== 外设发送 handler ========== */
typedef void (*datarouter_send_handler_t)(uint32_t port_id, const void *data, size_t len); typedef void (*dr_send_handler_t)(uint32_t port_id, const void *data, size_t len);
#ifndef DATAROUTER_MAX_HANDLERS_PER_PORT #ifndef DR_MAX_HANDLERS_PER_PORT
#define DATAROUTER_MAX_HANDLERS_PER_PORT 4 #define DR_MAX_HANDLERS_PER_PORT 4
#endif #endif
/* ========== 发送触发函数(每个发送能力生成一个) ========== */ /* ========== 发送触发函数 ========== */
#define DATAROUTER_SEND(id, desc) \ #define DR_SEND(id, desc) \
int datarouter_send_##id(uint32_t port_id, const void *data, size_t len); int dr_send_##id(const void *data, size_t len);
DATAROUTER_SEND_CAPABILITIES DR_SEND_CAPABILITIES
#undef DATAROUTER_SEND #undef DR_SEND
/* ========== 接收回调声明(每个接收能力生成一个,用户实现) ========== */ /* ========== 接收回调声明 ========== */
#define DATAROUTER_RECV(id, desc) \ #define DR_RECV(id, desc) \
void datarouter_on_recv_##id(uint32_t port_id, const void *data, size_t len); void dr_on_recv_##id(uint32_t port_id, const void *data, size_t len);
DATAROUTER_RECV_CAPABILITIES DR_RECV_CAPABILITIES
#undef DATAROUTER_RECV #undef DR_RECV
/* ========== 端口管理 API ========== */ /* ========== 端口管理 API ========== */
typedef int32_t datarouter_port_t; typedef int32_t dr_port_t;
#define DATAROUTER_INVALID_PORT ((datarouter_port_t)-1) #define DR_INVALID_PORT ((dr_port_t)-1)
void datarouter_init(void); void dr_init(void);
void datarouter_reset(void); void dr_reset(void);
datarouter_port_t datarouter_port_create(void); dr_port_t dr_port_create(void);
void datarouter_port_delete(datarouter_port_t port); void dr_port_delete(dr_port_t port);
void datarouter_port_set_send_type(datarouter_port_t port, int cap_id); void dr_port_set_send_type(dr_port_t port, int cap_id);
int datarouter_port_get_send_type(datarouter_port_t port); int dr_port_get_send_type(dr_port_t port);
void datarouter_port_set_recv_type(datarouter_port_t port, int cap_id);
int datarouter_port_get_recv_type(datarouter_port_t port);
int datarouter_port_add_handler(datarouter_port_t port, datarouter_send_handler_t handler); int dr_port_add_handler(dr_port_t port, dr_send_handler_t handler);
int datarouter_port_remove_handler(datarouter_port_t port, datarouter_send_handler_t handler); int dr_port_remove_handler(dr_port_t port, dr_send_handler_t handler);
void datarouter_port_clear_handlers(datarouter_port_t port); void dr_port_clear_handlers(dr_port_t port);
int datarouter_port_handler_count(datarouter_port_t port); int dr_port_handler_count(dr_port_t port);
/* ========== 能力元数据查询 ========== */ /* ========== 能力元数据查询 ========== */
int datarouter_send_cap_count(void); int dr_send_cap_count(void);
const char *datarouter_send_cap_name(int cap_id); const char *dr_send_cap_name(int cap_id);
const char *datarouter_send_cap_desc(int cap_id); const char *dr_send_cap_desc(int cap_id);
int datarouter_recv_cap_count(void); int dr_recv_cap_count(void);
const char *datarouter_recv_cap_name(int cap_id); const char *dr_recv_cap_name(int cap_id);
const char *datarouter_recv_cap_desc(int cap_id); const char *dr_recv_cap_desc(int cap_id);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
#endif /* DATAROUTER_H */ #endif /* DR_H */
+16 -16
View File
@@ -6,35 +6,35 @@
* 用户可自定义此文件以实现自己的报文类型: * 用户可自定义此文件以实现自己的报文类型:
* *
* 1. 复制当前文件到你的项目目录 * 1. 复制当前文件到你的项目目录
* 2. 按需修改 DATAROUTER_SEND_CAPABILITIES / DATAROUTER_RECV_CAPABILITIES * 2. 按需修改 DR_SEND_CAPABILITIES / DR_RECV_CAPABILITIES
* 3. 编译时添加 -I /path/to/your/config 覆盖库的默认配置 * 3. 编译时添加 -I /path/to/your/config 覆盖库的默认配置
* *
* 覆盖后库将使用你的版本,无需修改库源代码。 * 覆盖后库将使用你的版本,无需修改库源代码。
*/ */
#ifndef DATAROUTER_CONFIG_H #ifndef DR_CONFIG_H
#define DATAROUTER_CONFIG_H #define DR_CONFIG_H
/** /**
* @def DATAROUTER_SEND_CAPABILITIES * @def DR_SEND_CAPABILITIES
* @brief 发送能力列表 * @brief 发送能力列表
* *
* 每行格式:DATAROUTER_SEND(标识符, "描述") * 每行格式:DR_SEND(标识符, "描述")
* 每项生成:发送触发函数 datarouter_send_<id>(port, data, len) * 每项生成:发送触发函数 dr_send_<id>(port, data, len)
*/ */
#define DATAROUTER_SEND_CAPABILITIES \ #define DR_SEND_CAPABILITIES \
DATAROUTER_SEND(sensor, "Sensor data report") \ DR_SEND(sensor, "Sensor data report") \
DATAROUTER_SEND(status, "Device status report") DR_SEND(status, "Device status report")
/** /**
* @def DATAROUTER_RECV_CAPABILITIES * @def DR_RECV_CAPABILITIES
* @brief 接收能力列表 * @brief 接收能力列表
* *
* 每行格式:DATAROUTER_RECV(标识符, "描述") * 每行格式:DR_RECV(标识符, "描述")
* 每项生成:接收回调声明 datarouter_on_recv_<id>(port, data, len) * 每项生成:接收回调声明 dr_on_recv_<id>(port, data, len)
*/ */
#define DATAROUTER_RECV_CAPABILITIES \ #define DR_RECV_CAPABILITIES \
DATAROUTER_RECV(rtcm, "RTCM correction data") \ DR_RECV(rtcm, "RTCM correction data") \
DATAROUTER_RECV(command, "Remote control command") DR_RECV(command, "Remote control command")
#endif /* DATAROUTER_CONFIG_H */ #endif /* DR_CONFIG_H */
+22 -22
View File
@@ -4,48 +4,48 @@
* *
* 使用方法: * 使用方法:
* 1. 将此文件复制到你的项目目录,重命名为 datarouter_config.h * 1. 将此文件复制到你的项目目录,重命名为 datarouter_config.h
* 2. 修改下面的 DATAROUTER_SEND_CAPABILITIES 和 DATAROUTER_RECV_CAPABILITIES * 2. 修改下面的 DR_SEND_CAPABILITIES 和 DR_RECV_CAPABILITIES
* 3. 编译时添加 -I /path/to/your/project * 3. 编译时添加 -I /path/to/your/project
* 4. 库将自动使用你的配置,无需修改库源代码 * 4. 库将自动使用你的配置,无需修改库源代码
* *
* 格式说明: * 格式说明:
* 发送能力:DATAROUTER_SEND(标识符, "描述") * 发送能力:DR_SEND(标识符, "描述")
* 接收能力:DATAROUTER_RECV(标识符, "描述") * 接收能力:DR_RECV(标识符, "描述")
* *
* 标识符只能包含字母、数字、下划线。 * 标识符只能包含字母、数字、下划线。
* 描述为字符串字面量。 * 描述为字符串字面量。
* *
* 示例配置: * 示例配置:
* // 传感器设备:只需发送 * // 传感器设备:只需发送
* #define DATAROUTER_SEND_CAPABILITIES \ * #define DR_SEND_CAPABILITIES \
* DATAROUTER_SEND(sensor, "Sensor data report") * DR_SEND(sensor, "Sensor data report")
* #define DATAROUTER_RECV_CAPABILITIES * #define DR_RECV_CAPABILITIES
* *
* // 遥控设备:只需接收 * // 遥控设备:只需接收
* #define DATAROUTER_SEND_CAPABILITIES * #define DR_SEND_CAPABILITIES
* #define DATAROUTER_RECV_CAPABILITIES \ * #define DR_RECV_CAPABILITIES \
* DATAROUTER_RECV(command, "Remote command") * DR_RECV(command, "Remote command")
* *
* // 网关设备:同时收发 * // 网关设备:同时收发
* #define DATAROUTER_SEND_CAPABILITIES \ * #define DR_SEND_CAPABILITIES \
* DATAROUTER_SEND(status, "Status report") * DR_SEND(status, "Status report")
* #define DATAROUTER_RECV_CAPABILITIES \ * #define DR_RECV_CAPABILITIES \
* DATAROUTER_RECV(config, "Configuration update") * DR_RECV(config, "Configuration update")
*/ */
#ifndef DATAROUTER_CONFIG_H #ifndef DR_CONFIG_H
#define DATAROUTER_CONFIG_H #define DR_CONFIG_H
/* ========== 在此声明你的发送能力 ========== */ /* ========== 在此声明你的发送能力 ========== */
#define DATAROUTER_SEND_CAPABILITIES \ #define DR_SEND_CAPABILITIES \
DATAROUTER_SEND(sensor, "Sensor data report") \ DR_SEND(sensor, "Sensor data report") \
DATAROUTER_SEND(status, "Device status report") DR_SEND(status, "Device status report")
/* ========== 在此声明你的接收能力 ========== */ /* ========== 在此声明你的接收能力 ========== */
#define DATAROUTER_RECV_CAPABILITIES \ #define DR_RECV_CAPABILITIES \
DATAROUTER_RECV(rtcm, "RTCM correction data") \ DR_RECV(rtcm, "RTCM correction data") \
DATAROUTER_RECV(command, "Remote control command") DR_RECV(command, "Remote control command")
#endif /* DATAROUTER_CONFIG_H */ #endif /* DR_CONFIG_H */
+36 -56
View File
@@ -1,93 +1,73 @@
/** /**
* @file example.c * @file example.c
* @brief 数据路由库使用示例
* *
* 展示: * 调用 dr_send_sensor(data, len) 无需指定端口,
* - 发送能力和接收能力分开声明(sensor 只发送,rtcm 只接收) * 库自动广播到所有 send_type=sensor 的端口。
* - 端口独立设置 send_type 和 recv_type
* - 一个端口可同时拥有发送和接收能力
*/ */
#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 can_send(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("[RX:rtcm] 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 dr_on_recv_command(uint32_t port_id, const void *data, size_t len)
{ {
printf("[RX:cmd] 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);
} }
int main(void) int main(void)
{ {
datarouter_init(); dr_init();
/* 查询能力 */ printf("Capabilities:\n");
printf("Send capabilities: %d\n", datarouter_send_cap_count()); for (int i = 0; i < dr_send_cap_count(); i++)
for (int i = 0; i < datarouter_send_cap_count(); i++)
printf(" SEND [%d] %s - %s\n", i, printf(" SEND [%d] %s - %s\n", i,
datarouter_send_cap_name(i), datarouter_send_cap_desc(i)); dr_send_cap_name(i), dr_send_cap_desc(i));
printf("Recv capabilities: %d\n", datarouter_recv_cap_count());
for (int i = 0; i < datarouter_recv_cap_count(); i++)
printf(" RECV [%d] %s - %s\n", i,
datarouter_recv_cap_name(i), datarouter_recv_cap_desc(i));
/* /*
* 端口 A:只有发送能力 —— 上报传感器数据 * 创建两个端口,都设 send_type=sensor,各绑不同外设。
* 调用 dr_send_sensor() 时两个端口同时触发。
*/ */
datarouter_port_t tx_port = datarouter_port_create(); dr_port_t p1 = dr_port_create();
datarouter_port_set_send_type(tx_port, DATAROUTER_SEND_sensor); dr_port_set_send_type(p1, DR_SEND_sensor);
datarouter_port_add_handler(tx_port, uart_send); dr_port_add_handler(p1, uart_send);
datarouter_port_add_handler(tx_port, tcp_send); dr_port_add_handler(p1, tcp_send);
printf("\n--- send sensor (UART + TCP) ---\n"); dr_port_t p2 = dr_port_create();
datarouter_send_sensor(tx_port, "temperature:25.3", 16); dr_port_set_send_type(p2, DR_SEND_sensor);
/* 该端口没有 recv_type,不能接收任何报文 */ dr_port_add_handler(p2, can_send);
/* /* 不指定端口,自动广播到所有 sensor 端口 */
* 端口 B:只有接收能力 —— 接收 RTCM 修正 printf("\n--- send_sensor (auto-route to ALL sensor ports) ---\n");
*/ dr_send_sensor("temperature:25.3", 16);
datarouter_port_t rtcm_port = datarouter_port_create();
datarouter_port_set_recv_type(rtcm_port, DATAROUTER_RECV_rtcm);
/* 没有 send_type,也没有 handler —— 不能发送 */
printf("\n--- simulate receive RTCM ---\n"); /* 运行时移除一个 handler,下次发送不再触发它 */
datarouter_on_recv_rtcm(rtcm_port, "rtcm_data_v3.2", 14); printf("\n--- remove TCP handler from p1, resend ---\n");
dr_port_remove_handler(p1, tcp_send);
dr_send_sensor("temperature:26.1", 16);
/* /* 接收侧:用户自行调用回调 */
* 端口 C:同时有收发能力 —— 接收命令 + 上报状态 printf("\n--- recv command ---\n");
*/ dr_on_recv_command(0, "reboot", 6);
datarouter_port_t bi_port = datarouter_port_create();
datarouter_port_set_send_type(bi_port, DATAROUTER_SEND_status);
datarouter_port_set_recv_type(bi_port, DATAROUTER_RECV_command);
datarouter_port_add_handler(bi_port, uart_send);
printf("\n--- bidirectional port ---\n"); /* 删除一个端口,触发时不再走它 */
datarouter_send_status(bi_port, "status:OK", 9); printf("\n--- delete p2 (CAN), resend ---\n");
datarouter_on_recv_command(bi_port, "reboot", 6); dr_port_delete(p2);
dr_send_sensor("temperature:27.0", 16);
/* 清理 */ dr_port_delete(p1);
datarouter_port_delete(tx_port);
datarouter_port_delete(rtcm_port);
datarouter_port_delete(bi_port);
return 0; return 0;
} }
+165 -211
View File
@@ -1,15 +1,5 @@
/** /**
* @file test_datarouter.c * @file test_datarouter.c
* @brief 数据路由库单元测试
*
* 覆盖:
* - 端口管理
* - send_type / recv_type 独立设置
* - 多 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;
@@ -26,14 +16,14 @@ static const char *_cur_name;
#define FIXTURE_MAX 16 #define FIXTURE_MAX 16
typedef struct { typedef struct {
datarouter_port_t ports[FIXTURE_MAX]; dr_port_t ports[FIXTURE_MAX];
int count; int count;
} fixture_t; } fixture_t;
#define FIXTURE_INIT(f) memset((f), 0, sizeof(*(f))) #define FIXTURE_INIT(f) memset((f), 0, sizeof(*(f)))
#define FIXTURE_PUSH(f, p) do { if ((f)->count < FIXTURE_MAX) (f)->ports[(f)->count++] = (p); } while (0) #define FIXTURE_PUSH(f, p) do { if ((f)->count < FIXTURE_MAX) (f)->ports[(f)->count++] = (p); } while (0)
#define FIXTURE_CLEAN(f) do { for (int _i = 0; _i < (f)->count; _i++) datarouter_port_delete((f)->ports[_i]); } while (0) #define FIXTURE_CLEAN(f) do { for (int _i = 0; _i < (f)->count; _i++) dr_port_delete((f)->ports[_i]); } while (0)
#define FIXTURE_CREATE(f) ({ datarouter_port_t _p = datarouter_port_create(); FIXTURE_PUSH(f, _p); _p; }) #define FIXTURE_CREATE(f) ({ dr_port_t _p = dr_port_create(); FIXTURE_PUSH(f, _p); _p; })
#define TEST_START(name) \ #define TEST_START(name) \
static void test_##name(void) \ static void test_##name(void) \
@@ -66,19 +56,19 @@ cleanup: \
* ================================================================ */ * ================================================================ */
static int _h_cnt; static int _h_cnt;
static void _h(uint32_t p, const void *d, size_t l) { (void)p; (void)d; (void)l; _h_cnt++; } static int _h_port[16];
static void _h_a(uint32_t p, const void *d, size_t l) { (void)p; (void)d; (void)l; _h_cnt++; } static int _h_pos;
static void _h_b(uint32_t p, const void *d, size_t l) { (void)p; (void)d; (void)l; _h_cnt++; } 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 _h_c(uint32_t p, const void *d, size_t l) { (void)p; (void)d; (void)l; _h_cnt++; } 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; }
static void _h_d(uint32_t p, const void *d, size_t l) { (void)p; (void)d; (void)l; _h_cnt++; } 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; }
static void reset_h(void) { _h_cnt = 0; } static void reset_h(void) { _h_cnt = 0; _h_pos = 0; }
/* ================================================================ /* ================================================================
* 1. 未初始化 * 1. 未初始化
* ================================================================ */ * ================================================================ */
TEST_START(error_uninit) TEST_START(error_uninit)
CHECK(datarouter_send_sensor(0, "d", 1) == -1, "expected -1"); CHECK(dr_send_sensor("d", 1) == -1, "expected -1");
TEST_END() TEST_END()
/* ================================================================ /* ================================================================
@@ -86,244 +76,219 @@ TEST_END()
* ================================================================ */ * ================================================================ */
TEST_START(port_create_basic) TEST_START(port_create_basic)
CHECK(FIXTURE_CREATE(&_fix) >= 0, "create failed"); CHECK(FIXTURE_CREATE(&_fix) >= 0, NULL);
TEST_END() TEST_END()
TEST_START(port_create_max) TEST_START(port_create_max)
int c = 0; int c = 0;
for (int i = 0; i < 64; i++) { for (int i = 0; i < 64; i++) {
datarouter_port_t p = datarouter_port_create(); dr_port_t p = dr_port_create();
if (p == DATAROUTER_INVALID_PORT) break; if (p == DR_INVALID_PORT) break;
FIXTURE_PUSH(&_fix, p); c++; FIXTURE_PUSH(&_fix, p); c++;
} }
CHECK(c >= 64, "got %d", c); CHECK(c >= 64, "got %d", c);
CHECK(datarouter_port_create() == DATAROUTER_INVALID_PORT, NULL); CHECK(dr_port_create() == DR_INVALID_PORT, NULL);
TEST_END() TEST_END()
TEST_START(port_delete_invalid) TEST_START(port_delete_invalid)
datarouter_port_delete(-1); datarouter_port_delete(999); dr_port_delete(-1); dr_port_delete(999);
TEST_END() TEST_END()
TEST_START(port_default_state) TEST_START(port_default_state)
datarouter_port_t p = FIXTURE_CREATE(&_fix); dr_port_t p = FIXTURE_CREATE(&_fix);
CHECK(datarouter_port_get_send_type(p) == -1, "send_type should be -1"); CHECK(dr_port_get_send_type(p) == -1, NULL);
CHECK(datarouter_port_get_recv_type(p) == -1, "recv_type should be -1"); CHECK(dr_port_handler_count(p) == 0, NULL);
CHECK(datarouter_port_handler_count(p) == 0, "handler count 0");
TEST_END() TEST_END()
/* ================================================================ /* ================================================================
* 3. send_type / recv_type 独立设置 * 3. send_type 与 handler
* ================================================================ */ * ================================================================ */
TEST_START(send_type_set_get) TEST_START(send_type_set_get)
datarouter_port_t p = FIXTURE_CREATE(&_fix); dr_port_t p = FIXTURE_CREATE(&_fix);
datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); dr_port_set_send_type(p, DR_SEND_sensor);
CHECK(datarouter_port_get_send_type(p) == DATAROUTER_SEND_sensor, NULL); CHECK(dr_port_get_send_type(p) == DR_SEND_sensor, NULL);
dr_port_set_send_type(p, -1);
CHECK(dr_port_get_send_type(p) == -1, NULL);
TEST_END() TEST_END()
TEST_START(recv_type_set_get)
datarouter_port_t p = FIXTURE_CREATE(&_fix);
datarouter_port_set_recv_type(p, DATAROUTER_RECV_rtcm);
CHECK(datarouter_port_get_recv_type(p) == DATAROUTER_RECV_rtcm, NULL);
TEST_END()
TEST_START(types_independent)
/* 一个端口同时设置 send_type 和 recv_type,互不干扰 */
datarouter_port_t p = FIXTURE_CREATE(&_fix);
datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor);
datarouter_port_set_recv_type(p, DATAROUTER_RECV_command);
CHECK(datarouter_port_get_send_type(p) == DATAROUTER_SEND_sensor, NULL);
CHECK(datarouter_port_get_recv_type(p) == DATAROUTER_RECV_command, NULL);
TEST_END()
TEST_START(send_type_clear)
datarouter_port_t p = FIXTURE_CREATE(&_fix);
datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor);
datarouter_port_set_send_type(p, -1);
CHECK(datarouter_port_get_send_type(p) == -1, NULL);
TEST_END()
TEST_START(recv_type_clear)
datarouter_port_t p = FIXTURE_CREATE(&_fix);
datarouter_port_set_recv_type(p, DATAROUTER_RECV_rtcm);
datarouter_port_set_recv_type(p, -1);
CHECK(datarouter_port_get_recv_type(p) == -1, NULL);
TEST_END()
TEST_START(types_from_invalid)
CHECK(datarouter_port_get_send_type(-1) == -1, NULL);
CHECK(datarouter_port_get_recv_type(-1) == -1, NULL);
CHECK(datarouter_port_get_send_type(999) == -1, NULL);
CHECK(datarouter_port_get_recv_type(999) == -1, NULL);
TEST_END()
/* ================================================================
* 4. 多 handler
* ================================================================ */
TEST_START(handler_add_remove) TEST_START(handler_add_remove)
datarouter_port_t p = FIXTURE_CREATE(&_fix); dr_port_t p = FIXTURE_CREATE(&_fix);
CHECK(datarouter_port_add_handler(p, _h_a) == 0, NULL); CHECK(dr_port_add_handler(p, _h) == 0, NULL);
CHECK(datarouter_port_add_handler(p, _h_b) == 0, NULL); CHECK(dr_port_handler_count(p) == 1, NULL);
CHECK(datarouter_port_handler_count(p) == 2, NULL); CHECK(dr_port_remove_handler(p, _h) == 0, NULL);
CHECK(datarouter_port_remove_handler(p, _h_a) == 0, NULL); CHECK(dr_port_handler_count(p) == 0, NULL);
CHECK(datarouter_port_handler_count(p) == 1, NULL);
TEST_END() TEST_END()
TEST_START(handler_clear) TEST_START(handler_clear)
datarouter_port_t p = FIXTURE_CREATE(&_fix); dr_port_t p = FIXTURE_CREATE(&_fix);
datarouter_port_add_handler(p, _h); dr_port_add_handler(p, _h);
datarouter_port_clear_handlers(p); dr_port_clear_handlers(p);
CHECK(datarouter_port_handler_count(p) == 0, NULL); CHECK(dr_port_handler_count(p) == 0, NULL);
TEST_END() TEST_END()
/* ================================================================ /* ================================================================
* 5. 发送触发 * 4. 发送 — 自动广播
* ================================================================ */ * ================================================================ */
TEST_START(send_one_handler) TEST_START(send_no_match_port)
datarouter_port_t p = FIXTURE_CREATE(&_fix); /* 没有端口配置该 send_type */
datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); CHECK(dr_send_sensor("d", 1) == -2, "expected -2");
datarouter_port_add_handler(p, _h); TEST_END()
TEST_START(send_one_port_one_handler)
dr_port_t p = FIXTURE_CREATE(&_fix);
dr_port_set_send_type(p, DR_SEND_sensor);
dr_port_add_handler(p, _h);
reset_h(); reset_h();
CHECK(datarouter_send_sensor(p, "d", 1) == 0, NULL); CHECK(dr_send_sensor("d", 1) == 0, NULL);
CHECK(_h_cnt == 1, "handler called %d times", _h_cnt); CHECK(_h_cnt == 1, "cnt=%d", _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); dr_port_t p = FIXTURE_CREATE(&_fix);
datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); dr_port_set_send_type(p, DR_SEND_sensor);
datarouter_port_add_handler(p, _h_a); dr_port_add_handler(p, _h);
datarouter_port_add_handler(p, _h_b); dr_port_add_handler(p, _h2);
datarouter_port_add_handler(p, _h_c);
reset_h(); reset_h();
CHECK(datarouter_send_sensor(p, "d", 1) == 0, NULL); CHECK(dr_send_sensor("d", 1) == 0, NULL);
CHECK(_h_cnt == 3, "handler called %d times", _h_cnt); CHECK(_h_cnt == 2, "cnt=%d", _h_cnt);
TEST_END() TEST_END()
TEST_START(send_type_mismatch) TEST_START(send_multi_ports_same_type)
datarouter_port_t p = FIXTURE_CREATE(&_fix); /* 两个端口同类型,各一个 handler */
datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); dr_port_t p1 = FIXTURE_CREATE(&_fix);
datarouter_port_add_handler(p, _h); dr_port_t p2 = FIXTURE_CREATE(&_fix);
dr_port_set_send_type(p1, DR_SEND_sensor);
dr_port_set_send_type(p2, DR_SEND_sensor);
dr_port_add_handler(p1, _h);
dr_port_add_handler(p2, _h);
reset_h(); reset_h();
CHECK(datarouter_send_status(p, "d", 1) == -3, "expected -3"); CHECK(dr_send_sensor("d", 1) == 0, NULL);
CHECK(_h_cnt == 0, NULL); CHECK(_h_cnt == 2, "cnt=%d", _h_cnt);
/* p1 和 p2 各触发一次 */
CHECK((_h_port[0]==p1 && _h_port[1]==p2) ||
(_h_port[0]==p2 && _h_port[1]==p1), "ports %d,%d", _h_port[0], _h_port[1]);
TEST_END() TEST_END()
TEST_START(send_no_handler) TEST_START(send_multi_port_multi_handler)
datarouter_port_t p = FIXTURE_CREATE(&_fix); dr_port_t p1 = FIXTURE_CREATE(&_fix);
datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); dr_port_t p2 = FIXTURE_CREATE(&_fix);
CHECK(datarouter_send_sensor(p, "d", 1) == -4, "expected -4"); dr_port_set_send_type(p1, DR_SEND_sensor);
dr_port_set_send_type(p2, DR_SEND_sensor);
dr_port_add_handler(p1, _h);
dr_port_add_handler(p1, _h2); /* p1 两个 */
dr_port_add_handler(p2, _h3); /* p2 一个 */
reset_h();
CHECK(dr_send_sensor("d", 1) == 0, NULL);
CHECK(_h_cnt == 3, "cnt=%d", _h_cnt);
TEST_END() TEST_END()
TEST_START(send_no_send_type) TEST_START(send_type_filter)
/* 端口只有 recv_type,没有 send_type */ /* 两个端口不同类型,只触发匹配的 */
datarouter_port_t p = FIXTURE_CREATE(&_fix); dr_port_t p1 = FIXTURE_CREATE(&_fix);
datarouter_port_set_recv_type(p, DATAROUTER_RECV_rtcm); dr_port_t p2 = FIXTURE_CREATE(&_fix);
CHECK(datarouter_send_sensor(p, "d", 1) == -3, "expected -3 (send_type unset)"); dr_port_set_send_type(p1, DR_SEND_sensor);
dr_port_set_send_type(p2, DR_SEND_status);
dr_port_add_handler(p1, _h);
dr_port_add_handler(p2, _h);
reset_h();
CHECK(dr_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_END()
TEST_START(send_remove_handler) TEST_START(send_after_remove_handler)
datarouter_port_t p = FIXTURE_CREATE(&_fix); dr_port_t p = FIXTURE_CREATE(&_fix);
datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); dr_port_set_send_type(p, DR_SEND_sensor);
datarouter_port_add_handler(p, _h_a); dr_port_add_handler(p, _h);
datarouter_port_add_handler(p, _h_b); reset_h(); dr_send_sensor("d", 1); CHECK(_h_cnt == 1, NULL);
reset_h(); datarouter_send_sensor(p, "d", 1); CHECK(_h_cnt == 2, NULL); dr_port_remove_handler(p, _h);
datarouter_port_remove_handler(p, _h_a); CHECK(dr_send_sensor("d", 1) == -2, "expected -2 after remove");
reset_h(); datarouter_send_sensor(p, "d", 1); CHECK(_h_cnt == 1, NULL);
TEST_END() TEST_END()
TEST_START(send_clear_handlers) TEST_START(send_after_delete_port)
datarouter_port_t p = FIXTURE_CREATE(&_fix); dr_port_t p = FIXTURE_CREATE(&_fix);
datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); dr_port_set_send_type(p, DR_SEND_sensor);
datarouter_port_add_handler(p, _h); dr_port_add_handler(p, _h);
datarouter_port_clear_handlers(p); dr_port_delete(p);
CHECK(datarouter_send_sensor(p, "d", 1) == -4, "expected -4"); CHECK(dr_send_sensor("d", 1) == -2, "expected -2");
_fix.count--;
TEST_END() TEST_END()
TEST_START(send_zero_length) TEST_START(send_zero_length)
datarouter_port_t p = FIXTURE_CREATE(&_fix); dr_port_t p = FIXTURE_CREATE(&_fix);
datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); dr_port_set_send_type(p, DR_SEND_sensor);
datarouter_port_add_handler(p, _h); dr_port_add_handler(p, _h);
reset_h(); reset_h();
CHECK(datarouter_send_sensor(p, "x", 0) == 0, NULL); CHECK(dr_send_sensor("x", 0) == 0, NULL);
CHECK(_h_cnt == 1, NULL); CHECK(_h_cnt == 1, NULL);
TEST_END() TEST_END()
TEST_START(send_null_data) TEST_START(send_null_data)
datarouter_port_t p = FIXTURE_CREATE(&_fix); dr_port_t p = FIXTURE_CREATE(&_fix);
datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); dr_port_set_send_type(p, DR_SEND_sensor);
datarouter_port_add_handler(p, _h); dr_port_add_handler(p, _h);
reset_h(); reset_h();
CHECK(datarouter_send_sensor(p, NULL, 0) == 0, NULL); CHECK(dr_send_sensor(NULL, 0) == 0, NULL);
CHECK(_h_cnt == 1, NULL); CHECK(_h_cnt == 1, NULL);
TEST_END() TEST_END()
/* ================================================================ TEST_START(send_multi_cap)
* 6. 接收回调 /* 发送 status 不影响 sensor 端口 */
* ================================================================ */ dr_port_t p1 = FIXTURE_CREATE(&_fix);
dr_port_t p2 = FIXTURE_CREATE(&_fix);
static int _rx_r, _rx_c; dr_port_set_send_type(p1, DR_SEND_sensor);
void datarouter_on_recv_rtcm(uint32_t p, const void *d, size_t l) dr_port_set_send_type(p2, DR_SEND_status);
{ (void)p; (void)d; (void)l; _rx_r++; } dr_port_add_handler(p1, _h);
void datarouter_on_recv_command(uint32_t p, const void *d, size_t l) dr_port_add_handler(p2, _h);
{ (void)p; (void)d; (void)l; _rx_c++; } reset_h();
CHECK(dr_send_sensor("d", 1) == 0, NULL);
TEST_START(recv_callbacks) CHECK(_h_cnt == 1, "sensor: cnt=%d", _h_cnt);
_rx_r = _rx_c = 0; CHECK(dr_send_status("d", 1) == 0, NULL);
datarouter_on_recv_rtcm(0, "d", 1); CHECK(_h_cnt == 2, "status: cnt=%d", _h_cnt);
CHECK(_rx_r == 1 && _rx_c == 0, "rtcm=%d cmd=%d", _rx_r, _rx_c);
datarouter_on_recv_command(1, "d", 1);
CHECK(_rx_r == 1 && _rx_c == 1, "rtcm=%d cmd=%d", _rx_r, _rx_c);
TEST_END() TEST_END()
/* ================================================================ /* ================================================================
* 7. 能力元数据(两组独立) * 5. 接收回调
* ================================================================ */
static int _rx_r;
void dr_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;
dr_on_recv_rtcm(0, "d", 1);
CHECK(_rx_r == 1, "cnt=%d", _rx_r);
TEST_END()
/* ================================================================
* 6. 能力元数据
* ================================================================ */ * ================================================================ */
TEST_START(send_cap_meta) TEST_START(send_cap_meta)
CHECK(datarouter_send_cap_count() == 2, NULL); CHECK(dr_send_cap_count() == 2, NULL);
CHECK(strcmp(datarouter_send_cap_name(0), "sensor") == 0, NULL); CHECK(strcmp(dr_send_cap_name(0), "sensor") == 0, NULL);
CHECK(strcmp(datarouter_send_cap_desc(0), "Sensor data report") == 0, NULL); CHECK(strcmp(dr_send_cap_desc(0), "Sensor data report") == 0, NULL);
CHECK(strcmp(datarouter_send_cap_name(1), "status") == 0, NULL); CHECK(dr_send_cap_name(-1) == NULL, NULL);
CHECK(strcmp(datarouter_send_cap_desc(1), "Device status report") == 0, NULL); CHECK(dr_send_cap_name(99) == NULL, NULL);
CHECK(datarouter_send_cap_name(-1) == NULL, NULL);
CHECK(datarouter_send_cap_name(99) == NULL, NULL);
TEST_END() TEST_END()
TEST_START(recv_cap_meta) TEST_START(recv_cap_meta)
CHECK(datarouter_recv_cap_count() == 2, NULL); CHECK(dr_recv_cap_count() == 2, NULL);
CHECK(strcmp(datarouter_recv_cap_name(0), "rtcm") == 0, NULL); CHECK(strcmp(dr_recv_cap_name(0), "rtcm") == 0, NULL);
CHECK(strcmp(datarouter_recv_cap_desc(0), "RTCM correction data") == 0, NULL); CHECK(strcmp(dr_recv_cap_desc(0), "RTCM correction data") == 0, NULL);
CHECK(strcmp(datarouter_recv_cap_name(1), "command") == 0, NULL); CHECK(dr_recv_cap_name(-1) == NULL, NULL);
CHECK(strcmp(datarouter_recv_cap_desc(1), "Remote control command") == 0, NULL); CHECK(dr_recv_cap_desc(99) == NULL, NULL);
CHECK(datarouter_recv_cap_name(-1) == NULL, NULL);
CHECK(datarouter_recv_cap_desc(99) == NULL, NULL);
TEST_END()
/* ================================================================
* 8. 错误
* ================================================================ */
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_send_type(p, DATAROUTER_SEND_sensor);
datarouter_port_add_handler(p, _h);
datarouter_port_delete(p);
CHECK(datarouter_send_sensor(p, "d", 1) == -2, NULL);
_fix.count--;
TEST_END() TEST_END()
/* ================================================================ /* ================================================================
* 主函数 * 主函数
* ================================================================ */ * ================================================================ */
#define RUN(name) do { reset_h(); test_##name(); } while (0) #define RUN(name) do { reset_h(); _rx_r = 0; test_##name(); } while (0)
int main(void) int main(void)
{ {
@@ -332,7 +297,7 @@ int main(void)
printf("[uninitialized]\n"); printf("[uninitialized]\n");
RUN(error_uninit); RUN(error_uninit);
datarouter_init(); dr_init();
printf("\n[port management]\n"); printf("\n[port management]\n");
RUN(port_create_basic); RUN(port_create_basic);
@@ -341,35 +306,29 @@ int main(void)
RUN(port_default_state); RUN(port_default_state);
printf("\n"); printf("\n");
datarouter_reset(); datarouter_init(); dr_reset(); dr_init();
printf("[send_type / recv_type]\n"); printf("[send_type & handler]\n");
RUN(send_type_set_get); RUN(send_type_set_get);
RUN(recv_type_set_get);
RUN(types_independent);
RUN(send_type_clear);
RUN(recv_type_clear);
RUN(types_from_invalid);
printf("\n");
printf("[handler add/remove]\n");
RUN(handler_add_remove); RUN(handler_add_remove);
RUN(handler_clear); RUN(handler_clear);
printf("\n"); printf("\n");
printf("[send trigger]\n"); printf("[send — auto broadcast]\n");
RUN(send_one_handler); RUN(send_no_match_port);
RUN(send_multi_handlers); RUN(send_one_port_one_handler);
RUN(send_type_mismatch); RUN(send_one_port_multi_handler);
RUN(send_no_handler); RUN(send_multi_ports_same_type);
RUN(send_no_send_type); RUN(send_multi_port_multi_handler);
RUN(send_remove_handler); RUN(send_type_filter);
RUN(send_clear_handlers); RUN(send_after_remove_handler);
RUN(send_after_delete_port);
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_callbacks); RUN(recv_callback);
printf("\n"); printf("\n");
printf("[capability metadata]\n"); printf("[capability metadata]\n");
@@ -377,11 +336,6 @@ int main(void)
RUN(recv_cap_meta); RUN(recv_cap_meta);
printf("\n"); printf("\n");
printf("[error handling]\n");
RUN(error_invalid_port);
RUN(error_deleted_port);
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);