commit c02885dd51d360febd5d6326965bb32c9823dd4f
Author: ZhangJinLong <19357383190@163.com>
Date: Fri Aug 29 10:58:21 2025 +0800
first
diff --git a/ (2).gitignore b/ (2).gitignore
new file mode 100644
index 0000000..9b398b0
--- /dev/null
+++ b/ (2).gitignore
@@ -0,0 +1,12 @@
+*.o
+*.d
+*.crf
+*.axf
+*.elf
+*.map
+*.lst
+*.bak
+*.lnp
+*.dep
+*.uvgui.*
+*.uvoptx
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..aa724b7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/AndroidProjectSystem.xml b/.idea/AndroidProjectSystem.xml
new file mode 100644
index 0000000..4a53bee
--- /dev/null
+++ b/.idea/AndroidProjectSystem.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..b86273d
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
new file mode 100644
index 0000000..b268ef3
--- /dev/null
+++ b/.idea/deploymentTargetSelector.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000..639c779
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/migrations.xml b/.idea/migrations.xml
new file mode 100644
index 0000000..f8051a6
--- /dev/null
+++ b/.idea/migrations.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..74dd639
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
new file mode 100644
index 0000000..16660f1
--- /dev/null
+++ b/.idea/runConfigurations.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.kotlin/errors/errors-1756261657029.log b/.kotlin/errors/errors-1756261657029.log
new file mode 100644
index 0000000..1219b50
--- /dev/null
+++ b/.kotlin/errors/errors-1756261657029.log
@@ -0,0 +1,4 @@
+kotlin version: 2.0.21
+error message: The daemon has terminated unexpectedly on startup attempt #1 with error code: 0. The daemon process output:
+ 1. Kotlin compile daemon is ready
+
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
new file mode 100644
index 0000000..b2584ef
--- /dev/null
+++ b/app/build.gradle.kts
@@ -0,0 +1,61 @@
+plugins {
+ alias(libs.plugins.android.application)
+ alias(libs.plugins.kotlin.android)
+}
+
+android {
+ namespace = "com.example.cmake_project_test"
+ compileSdk = 36
+
+ defaultConfig {
+ applicationId = "com.example.cmake_project_test"
+ minSdk = 24
+ targetSdk = 36
+ versionCode = 1
+ versionName = "1.0"
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ externalNativeBuild {
+ cmake {
+ cppFlags += "-std=c++17"
+ }
+ }
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ }
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+ kotlinOptions {
+ jvmTarget = "11"
+ }
+ externalNativeBuild {
+ cmake {
+ path = file("src/main/cpp/CMakeLists.txt")
+ version = "3.22.1"
+ }
+ }
+ buildFeatures {
+ viewBinding = true
+ }
+}
+
+dependencies {
+
+ implementation(libs.androidx.core.ktx)
+ implementation(libs.androidx.appcompat)
+ implementation(libs.material)
+ implementation(libs.androidx.constraintlayout)
+ testImplementation(libs.junit)
+ androidTestImplementation(libs.androidx.junit)
+ androidTestImplementation(libs.androidx.espresso.core)
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/app/src/androidTest/java/com/example/cmake_project_test/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/example/cmake_project_test/ExampleInstrumentedTest.kt
new file mode 100644
index 0000000..85c230a
--- /dev/null
+++ b/app/src/androidTest/java/com/example/cmake_project_test/ExampleInstrumentedTest.kt
@@ -0,0 +1,24 @@
+package com.example.cmake_project_test
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("com.example.cmake_project_test", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..7878023
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/assets/data1.dat b/app/src/main/assets/data1.dat
new file mode 100644
index 0000000..ba66912
Binary files /dev/null and b/app/src/main/assets/data1.dat differ
diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt
new file mode 100644
index 0000000..0989916
--- /dev/null
+++ b/app/src/main/cpp/CMakeLists.txt
@@ -0,0 +1,53 @@
+# For more information about using CMake with Android Studio, read the
+# documentation: https://d.android.com/studio/projects/add-native-code.html.
+# For more examples on how to use CMake, see https://github.com/android/ndk-samples.
+
+# Sets the minimum CMake version required for this project.
+cmake_minimum_required(VERSION 3.22.1)
+
+# Declares the project name. The project name can be accessed via ${ PROJECT_NAME},
+# Since this is the top level CMakeLists.txt, the project name is also accessible
+# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level
+# build script scope).
+project("cmake_project_test")
+
+# Creates and names a library, sets it as either STATIC
+# or SHARED, and provides the relative paths to its source code.
+# You can define multiple libraries, and CMake builds them for you.
+# Gradle automatically packages shared libraries with your APK.
+#
+# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define
+# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}
+# is preferred for the same purpose.
+#
+# In order to load a library into your app from Java/Kotlin, you must call
+# System.loadLibrary() and pass the name of the library defined here;
+# For GameActivity/NativeActivity derived applications, the same library name must be
+# used in the AndroidManifest.xml file.
+
+# Core static library (no JNI, pure C++)
+add_library(core_math STATIC
+ src/add.cpp)
+
+target_include_directories(core_math PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/include)
+
+# Data parser static library
+add_library(data_parser STATIC
+ src/data_praser.cpp)
+
+target_include_directories(data_parser PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/include)
+
+# JNI shared library: expose only JNI bridge, link against core
+add_library(${CMAKE_PROJECT_NAME} SHARED
+ jni/jni_bridge.cpp)
+
+# Specifies libraries CMake should link to your target library. You
+# can link libraries from various origins, such as libraries defined in this
+# build script, prebuilt third-party libraries, or Android system libraries.
+target_link_libraries(${CMAKE_PROJECT_NAME}
+ core_math
+ data_parser
+ android
+ log)
\ No newline at end of file
diff --git a/app/src/main/cpp/include/cpp/add.h b/app/src/main/cpp/include/cpp/add.h
new file mode 100644
index 0000000..1dfed32
--- /dev/null
+++ b/app/src/main/cpp/include/cpp/add.h
@@ -0,0 +1,11 @@
+#pragma once
+
+namespace core {
+namespace math {
+
+int add(int a, int b);
+
+} // namespace math
+} // namespace core
+
+
diff --git a/app/src/main/cpp/include/cpp/data_praser.h b/app/src/main/cpp/include/cpp/data_praser.h
new file mode 100644
index 0000000..549912e
--- /dev/null
+++ b/app/src/main/cpp/include/cpp/data_praser.h
@@ -0,0 +1,151 @@
+#ifndef DATA_PRASER_H
+#define DATA_PRASER_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+// 设备类型枚举
+enum class DataType {
+ EEG, // 脑电
+ ECG_2LEAD, // 胸腹心电/肌电
+ ECG_12LEAD, // 12导联心电
+ PPG, // 血氧
+ RESPIRATION, // 呼吸/姿态
+ SNORE, // 鼾声
+ STETHOSCOPE, // 数字听诊
+ MIT_BIH // 添加MIT-BIH心律失常数据集类型
+};
+
+// 导联脱落状态
+struct LeadOffStatus {
+ uint8_t status[2]; // 导联状态字节
+};
+
+// 统一传感器数据结构
+struct SensorData {
+ DataType data_type;
+ uint16_t packet_sn; // 数据包序列号
+ LeadOffStatus lead_status; // 导联状态
+ int64_t timestamp; // 系统时间戳(微秒)
+ float sqi; //信号质量
+
+ // 通道数据
+ std::variant<
+ std::vector, // 单通道数据
+ std::vector> // 多通道数据
+ > channel_data;
+ std::vector raw_data; // 原始二进制数据
+ // 附加数据
+ struct {
+ uint16_t movement; // 运动强度 (呼吸设备)
+ uint8_t posture; // 姿态 (呼吸设备)
+ uint8_t ambient_light; // 环境光 (呼吸设备)
+ uint8_t hr; // 心率 (血氧)
+ uint8_t spo2; // 血氧饱和度 (血氧)
+ float temperature; // 温度 (修正为浮点类型)
+ uint8_t gpio_state; // GPIO状态 (12导联心电)
+ } additional;
+};
+
+// 前向声明
+struct SensorData;
+struct FileManager;
+enum class DataType;
+
+// 辅助函数:从字节数组读取小端整数
+template
+T read_le(const uint8_t* data);
+
+// 脑电设备解析 (0x4230)
+SensorData parse_eeg(const uint8_t* data);
+
+// 心电设备解析 (0x4211)
+SensorData parse_ecg_emg(const uint8_t* data);
+
+// 血氧设备解析(0x4302)
+SensorData parse_ppg(const uint8_t* data);
+
+// 12导联心电解析 (0x4402)
+SensorData parse_12lead_ecg(const uint8_t* data);
+
+// MIT-BIH 212格式解析器
+SensorData parse_mit_bih_212(const uint8_t* data, size_t size);
+
+// 数字听诊解析 (0x1102)
+SensorData parse_stethoscope(const uint8_t* data);
+
+// 鼾声解析 (0x4212)
+SensorData parse_snore(const uint8_t* data);
+
+// 呼吸/姿态/环境光解析(0x4213)
+SensorData parse_respiration(const uint8_t* data);
+
+// 统一解析入口函数 - 支持多个包头和数据包格式
+std::vector parse_device_data(const std::vector& file_data);
+
+// 工具函数:将数值转换为十六进制字符串
+std::string to_hex_string(uint16_t value);
+
+// JNI辅助函数:将C++的SensorData转换为Java对象
+jobject convertSensorDataToJava(JNIEnv* env, const SensorData& sensorData);
+
+// JNI辅助函数:将Java的byte数组转换为C++的vector
+std::vector convertJavaByteArrayToVector(JNIEnv* env, jbyteArray byteArray);
+
+// JNI辅助函数:将C++的vector转换为Java的List
+jobject convertSensorDataVectorToJavaList(JNIEnv* env, const std::vector& sensorDataVector);
+
+
+class StreamParser {
+ public:
+ StreamParser();
+ ~StreamParser();
+
+ // 添加新接收到的数据
+ void appendData(const uint8_t* data, size_t length);
+
+ // 检查是否有完整的数据包可用
+ bool hasCompletePacket() const;
+
+ // 获取下一个完整的数据包
+ SensorData getNextPacket();
+
+ // 获取所有可用数据包
+ std::vector getAllPackets();
+
+ // 设置数据包处理回调函数
+ void setPacketCallback(std::function callback);
+
+ // 清空缓冲区
+ void clear();
+
+ // 新增:状态查询方法
+ size_t getBufferSize() const;
+ size_t getTotalPackets() const;
+ size_t getPendingDataSize() const;
+ bool isBufferEmpty() const;
+ size_t getQueuedPacketCount() const;
+
+ private:
+ std::vector buffer_;
+ std::queue complete_packets_;
+ static const size_t PACKET_SIZE = 238; // 固定数据包大小
+ std::function packet_callback_;
+
+ // 新增:成员变量
+ size_t buffer_size_; // 累计接收的数据大小
+ size_t total_packets_; // 累计解析的数据包数量
+
+ // 内部解析方法
+ void parseBuffer();
+
+ // 新增:数据包验证方法
+ bool isValidPacket(const uint8_t* data);
+ };
+
+
+#endif // DATA_PRASER_H
diff --git a/app/src/main/cpp/jni/jni_bridge.cpp b/app/src/main/cpp/jni/jni_bridge.cpp
new file mode 100644
index 0000000..8890820
--- /dev/null
+++ b/app/src/main/cpp/jni/jni_bridge.cpp
@@ -0,0 +1,146 @@
+#include
+#include
+#include
+#include
+#include
+#include "../include/cpp/add.h"
+#include "../include/cpp/data_praser.h"
+
+using core::math::add;
+
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_example_cmake_1project_1test_MainActivity_stringFromJNI(
+ JNIEnv* env,
+ jobject /* this */) {
+ std::string hello = "Hello from C++ (multi-file)";
+ return env->NewStringUTF(hello.c_str());
+}
+
+extern "C" JNIEXPORT jint JNICALL
+Java_com_example_cmake_1project_1test_MainActivity_addFromJNI(
+ JNIEnv* env,
+ jobject /* this */,
+ jint a,
+ jint b) {
+ (void)env;
+ return static_cast(add(static_cast(a), static_cast(b)));
+}
+
+extern "C" JNIEXPORT jobject JNICALL
+Java_com_example_cmake_1project_1test_MainActivity_parseDeviceDataFromJNI(
+ JNIEnv* env,
+ jobject /* this */,
+ jbyteArray fileData) {
+
+ try {
+ // 将Java的byte数组转换为C++的vector
+ std::vector cppFileData = convertJavaByteArrayToVector(env, fileData);
+
+ if (cppFileData.empty()) {
+ __android_log_print(ANDROID_LOG_WARN, "DataParser", "Empty file data received");
+ return nullptr;
+ }
+
+ // 调用C++的解析函数
+ std::vector result = parse_device_data(cppFileData);
+
+ // 将结果转换为Java对象
+ jobject javaResult = convertSensorDataVectorToJavaList(env, result);
+
+ if (javaResult == nullptr) {
+ __android_log_print(ANDROID_LOG_ERROR, "DataParser", "Failed to convert result to Java object");
+ } else {
+ __android_log_print(ANDROID_LOG_INFO, "DataParser", "Successfully parsed %zu data packets", result.size());
+ }
+
+ return javaResult;
+
+ } catch (const std::exception& e) {
+ __android_log_print(ANDROID_LOG_ERROR, "DataParser", "Exception during parsing: %s", e.what());
+ return nullptr;
+ } catch (...) {
+ __android_log_print(ANDROID_LOG_ERROR, "DataParser", "Unknown exception during parsing");
+ return nullptr;
+ }
+}
+
+
+// ================= 流式解析 JNI 接口 =================
+
+// 创建解析器实例,返回 native 句柄
+extern "C" JNIEXPORT jlong JNICALL
+Java_com_example_cmake_1project_1test_MainActivity_createStreamParser(
+ JNIEnv* env,
+ jobject /* this */) {
+ (void)env;
+ try {
+ auto* parser = new StreamParser();
+ return reinterpret_cast(parser);
+ } catch (...) {
+ return 0;
+ }
+}
+
+// 释放解析器实例
+extern "C" JNIEXPORT void JNICALL
+Java_com_example_cmake_1project_1test_MainActivity_destroyStreamParser(
+ JNIEnv* env,
+ jobject /* this */,
+ jlong handle) {
+ (void)env;
+ auto* parser = reinterpret_cast(handle);
+ delete parser;
+}
+
+// 追加一段字节流数据
+extern "C" JNIEXPORT void JNICALL
+Java_com_example_cmake_1project_1test_MainActivity_streamParserAppend(
+ JNIEnv* env,
+ jobject /* this */,
+ jlong handle,
+ jbyteArray chunk) {
+ auto* parser = reinterpret_cast(handle);
+ if (!parser || !chunk) return;
+
+ jsize len = env->GetArrayLength(chunk);
+ if (len <= 0) return;
+ std::vector buf(static_cast(len));
+ env->GetByteArrayRegion(chunk, 0, len, reinterpret_cast(buf.data()));
+ parser->appendData(buf.data(), buf.size());
+}
+
+// 拉取已完成的所有数据包,转换为 Java List
+extern "C" JNIEXPORT jobject JNICALL
+Java_com_example_cmake_1project_1test_MainActivity_streamParserDrainPackets(
+ JNIEnv* env,
+ jobject /* this */,
+ jlong handle) {
+ auto* parser = reinterpret_cast(handle);
+ if (!parser) return nullptr;
+ std::vector packets = parser->getAllPackets();
+ return convertSensorDataVectorToJavaList(env, packets);
+}
+
+// 便捷方法:从整块数据按固定块大小模拟流式解析,返回解析到的所有包
+extern "C" JNIEXPORT jobject JNICALL
+Java_com_example_cmake_1project_1test_MainActivity_parseStreamFromBytes(
+ JNIEnv* env,
+ jobject /* this */,
+ jbyteArray data,
+ jint chunkSize) {
+ if (data == nullptr || chunkSize <= 0) return nullptr;
+
+ std::vector src = convertJavaByteArrayToVector(env, data);
+ auto* parser = new StreamParser();
+ size_t offset = 0;
+ while (offset < src.size()) {
+ size_t n = std::min(static_cast(chunkSize), src.size() - offset);
+ parser->appendData(src.data() + offset, n);
+ offset += n;
+ }
+ std::vector packets = parser->getAllPackets();
+ delete parser;
+ return convertSensorDataVectorToJavaList(env, packets);
+}
+
+
diff --git a/app/src/main/cpp/src/add.cpp b/app/src/main/cpp/src/add.cpp
new file mode 100644
index 0000000..770ce17
--- /dev/null
+++ b/app/src/main/cpp/src/add.cpp
@@ -0,0 +1,11 @@
+#include "../include/cpp/add.h"
+
+namespace core {
+namespace math {
+
+int add(int a, int b) {
+ return a + b;
+}
+
+} // namespace math
+} // namespace core
diff --git a/app/src/main/cpp/src/data_praser.cpp b/app/src/main/cpp/src/data_praser.cpp
new file mode 100644
index 0000000..f6e92ee
--- /dev/null
+++ b/app/src/main/cpp/src/data_praser.cpp
@@ -0,0 +1,919 @@
+#include "../include/cpp/data_praser.h"
+#include // 添加 memcpy 头文件
+#include // 添加 stringstream 头文件
+#include // 添加 iomanip 头文件
+#include
+#include
+#include