init
This commit is contained in:
29
.gitignore
vendored
Normal file
29
.gitignore
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
migrate_working_dir/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
#.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||||
|
/pubspec.lock
|
||||||
|
**/doc/api/
|
||||||
|
.dart_tool/
|
||||||
|
build/
|
||||||
30
.metadata
Normal file
30
.metadata
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# This file tracks properties of this Flutter project.
|
||||||
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
|
#
|
||||||
|
# This file should be version controlled and should not be manually edited.
|
||||||
|
|
||||||
|
version:
|
||||||
|
revision: "761747bfc538b5af34aa0d3fac380f1bc331ec49"
|
||||||
|
channel: "stable"
|
||||||
|
|
||||||
|
project_type: plugin
|
||||||
|
|
||||||
|
# Tracks metadata for the flutter migrate command
|
||||||
|
migration:
|
||||||
|
platforms:
|
||||||
|
- platform: root
|
||||||
|
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||||
|
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||||
|
- platform: android
|
||||||
|
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||||
|
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||||
|
|
||||||
|
# User provided section
|
||||||
|
|
||||||
|
# List of Local paths (relative to this file) that should be
|
||||||
|
# ignored by the migrate tool.
|
||||||
|
#
|
||||||
|
# Files that are not part of the templates will be ignored by default.
|
||||||
|
unmanaged_files:
|
||||||
|
- 'lib/main.dart'
|
||||||
|
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||||
3
CHANGELOG.md
Normal file
3
CHANGELOG.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
## 0.0.1
|
||||||
|
|
||||||
|
* TODO: Describe initial release.
|
||||||
15
README.md
Normal file
15
README.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# terra
|
||||||
|
|
||||||
|
A new Flutter project.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
This project is a starting point for a Flutter
|
||||||
|
[plug-in package](https://flutter.dev/developing-packages/),
|
||||||
|
a specialized package that includes platform-specific implementation code for
|
||||||
|
Android and/or iOS.
|
||||||
|
|
||||||
|
For help getting started with Flutter development, view the
|
||||||
|
[online documentation](https://flutter.dev/docs), which offers tutorials,
|
||||||
|
samples, guidance on mobile development, and a full API reference.
|
||||||
|
|
||||||
4
analysis_options.yaml
Normal file
4
analysis_options.yaml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
|
# Additional information about this file can be found at
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
||||||
9
android/.gitignore
vendored
Normal file
9
android/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea/workspace.xml
|
||||||
|
/.idea/libraries
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
||||||
|
.cxx
|
||||||
60
android/build.gradle
Normal file
60
android/build.gradle
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
group = "com.example.terra"
|
||||||
|
version = "1.0"
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath("com.android.tools.build:gradle:7.3.0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.allprojects {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
flatDir{
|
||||||
|
dirs project(':terra').file('libs')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: "com.android.library"
|
||||||
|
|
||||||
|
android {
|
||||||
|
if (project.android.hasProperty("namespace")) {
|
||||||
|
namespace = "com.example.terra"
|
||||||
|
}
|
||||||
|
|
||||||
|
compileSdk = 34
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk = 29
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
testImplementation("junit:junit:4.13.2")
|
||||||
|
testImplementation("org.mockito:mockito-core:5.0.0")
|
||||||
|
|
||||||
|
implementation(name: 'terra',ext: 'aar')
|
||||||
|
implementation(name: 'RamanMatch',ext: 'aar')
|
||||||
|
}
|
||||||
|
|
||||||
|
testOptions {
|
||||||
|
unitTests.all {
|
||||||
|
testLogging {
|
||||||
|
events "passed", "skipped", "failed", "standardOut", "standardError"
|
||||||
|
outputs.upToDateWhen {false}
|
||||||
|
showStandardStreams = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
android/libs/RamanMatch.aar
Normal file
BIN
android/libs/RamanMatch.aar
Normal file
Binary file not shown.
BIN
android/libs/terra.aar
Normal file
BIN
android/libs/terra.aar
Normal file
Binary file not shown.
1
android/settings.gradle
Normal file
1
android/settings.gradle
Normal file
@@ -0,0 +1 @@
|
|||||||
|
rootProject.name = 'terra'
|
||||||
3
android/src/main/AndroidManifest.xml
Normal file
3
android/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.example.terra">
|
||||||
|
</manifest>
|
||||||
974
android/src/main/java/com/example/terra/TerraPlugin.java
Normal file
974
android/src/main/java/com/example/terra/TerraPlugin.java
Normal file
@@ -0,0 +1,974 @@
|
|||||||
|
package com.example.terra;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import io.flutter.embedding.engine.plugins.FlutterPlugin;
|
||||||
|
import io.flutter.plugin.common.MethodCall;
|
||||||
|
import io.flutter.plugin.common.MethodChannel;
|
||||||
|
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
||||||
|
import io.flutter.plugin.common.MethodChannel.Result;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.optics.terra.Device;
|
||||||
|
import com.optics.terra.DeviceWrapper;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.example.terra.utils.ArrayUtils;
|
||||||
|
import com.ramanmatch.match.RamanMatch;
|
||||||
|
import com.ramanmatch.match.MatchResult;
|
||||||
|
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TerraPlugin - 拉曼光谱仪 Flutter 通信插件
|
||||||
|
*
|
||||||
|
* 提供 Flutter 与拉曼光谱仪硬件设备之间的 MethodChannel 通信桥梁,
|
||||||
|
* 支持设备管理、参数配置、光谱采集、数据处理和算法匹配等功能。
|
||||||
|
*/
|
||||||
|
public class TerraPlugin implements FlutterPlugin, MethodCallHandler {
|
||||||
|
|
||||||
|
private static final String TAG = "TerraPlugin";
|
||||||
|
private static final String CHANNEL_NAME = "terra";
|
||||||
|
|
||||||
|
/** 默认激光功率 */
|
||||||
|
private static final int DEFAULT_LASER_POWER = 500;
|
||||||
|
/** 默认积分时间 (ms) */
|
||||||
|
private static final int DEFAULT_INTEGRATION_TIME = 500;
|
||||||
|
/** 消荧光因子,数字越小削得越狠 */
|
||||||
|
private static final int FLUORESCENCE_REMOVAL_FACTOR = 15;
|
||||||
|
/** 光谱饱和阈值 */
|
||||||
|
private static final double SATURATION_THRESHOLD = 65535.0;
|
||||||
|
/** 波数起始截取值 */
|
||||||
|
private static final double WAVE_NUMBER_START = 200.0;
|
||||||
|
|
||||||
|
/** 设备 SN 码与算法激活码、激光系数的映射表 */
|
||||||
|
private static final Map<String, String[]> DEVICE_CONFIG_MAP;
|
||||||
|
|
||||||
|
//SN,算法激活码,激光系数
|
||||||
|
static {
|
||||||
|
Map<String, String[]> map = new HashMap<>();
|
||||||
|
map.put("B7BP340105", new String[]{"2bb7b4a2206a90bea48fe999cace47ca", "0080080016002500"});
|
||||||
|
map.put("B7BP340101", new String[]{"10a1cf036a325b3e747dccefcf57ce84", "0081075016002300"});
|
||||||
|
map.put("B7BP230208", new String[]{"8bec16f120a49cd9987cd7f4dab60510", "0100100030004800"});
|
||||||
|
map.put("B7BP230209", new String[]{"0eb603282ef9d0f73a47a548c9cb8c9e", "0020023006801400"});
|
||||||
|
DEVICE_CONFIG_MAP = Collections.unmodifiableMap(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodChannel channel;
|
||||||
|
private Context context;
|
||||||
|
private Device device;
|
||||||
|
|
||||||
|
/** 200 波数对应的像素位置索引 */
|
||||||
|
private int startPxIndexForWaveNumber = 0;
|
||||||
|
/** 当前积分时间 (ms) */
|
||||||
|
private int integrationTime = DEFAULT_INTEGRATION_TIME;
|
||||||
|
/** 波数数组 */
|
||||||
|
private double[] waveNumber = null;
|
||||||
|
|
||||||
|
/** USB 操作单线程执行器,确保所有调用串行化 */
|
||||||
|
private volatile ExecutorService usbExecutor = Executors.newSingleThreadExecutor();
|
||||||
|
|
||||||
|
/** 普通 USB 调用超时(5 秒) */
|
||||||
|
private static final int USB_CALL_TIMEOUT_MS = 5000;
|
||||||
|
/** 光谱采集超时(60 秒,支持深度模式长时间采集) */
|
||||||
|
private static final int SPECTRUM_TIMEOUT_MS = 60000;
|
||||||
|
|
||||||
|
// ==================== 生命周期方法 ====================
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
|
||||||
|
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), CHANNEL_NAME);
|
||||||
|
channel.setMethodCallHandler(this);
|
||||||
|
context = flutterPluginBinding.getApplicationContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
|
||||||
|
channel.setMethodCallHandler(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Flutter MethodChannel 方法调用分发 */
|
||||||
|
@Override
|
||||||
|
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
|
||||||
|
switch (call.method) {
|
||||||
|
// --- 平台信息 ---
|
||||||
|
case "getPlatformVersion":
|
||||||
|
result.success("Android " + android.os.Build.VERSION.RELEASE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// --- 设备管理 ---
|
||||||
|
case "openAllDevices":
|
||||||
|
handleOpenAllDevices(result);
|
||||||
|
break;
|
||||||
|
case "closeAllDevices":
|
||||||
|
handleCloseAllDevices(result);
|
||||||
|
break;
|
||||||
|
case "close":
|
||||||
|
handleCloseDevice(result);
|
||||||
|
break;
|
||||||
|
case "getDeviceSn":
|
||||||
|
handleGetDeviceSn(result);
|
||||||
|
break;
|
||||||
|
case "getIndex":
|
||||||
|
handleGetIndex(result);
|
||||||
|
break;
|
||||||
|
case "isConnect":
|
||||||
|
handleIsConnect(result);
|
||||||
|
break;
|
||||||
|
case "openDebug":
|
||||||
|
handleOpenDebug();
|
||||||
|
result.success(true);
|
||||||
|
break;
|
||||||
|
case "checkUsbPermission":
|
||||||
|
handleCheckUsbPermission(result);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// --- CCD 制冷控制 ---
|
||||||
|
case "setCCDTECOn":
|
||||||
|
safeBooleanCall(result, () -> device.setCCDTECOn());
|
||||||
|
break;
|
||||||
|
case "setCCDTECOff":
|
||||||
|
safeBooleanCall(result, () -> device.setCCDTECOff());
|
||||||
|
break;
|
||||||
|
case "setCCDTECTemperature":
|
||||||
|
safeBooleanCall(result, () -> device.setCCDTECTemperature(getIntArg(call, "temperature")));
|
||||||
|
break;
|
||||||
|
case "getCCDTECState":
|
||||||
|
result.success(device.getCCDTECState());
|
||||||
|
break;
|
||||||
|
|
||||||
|
// --- 参数配置 ---
|
||||||
|
case "setIntegrationTime":
|
||||||
|
handleSetIntegrationTime(call, result);
|
||||||
|
break;
|
||||||
|
case "setAverageTimes":
|
||||||
|
handleSetAverageTimes(call, result);
|
||||||
|
break;
|
||||||
|
case "setTriggerMode":
|
||||||
|
handleSetTriggerMode(call, result);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// --- 光谱数据获取 ---
|
||||||
|
case "getSpectrum":
|
||||||
|
result.success(device.getSpectrum());
|
||||||
|
break;
|
||||||
|
case "getResetSpectrum":
|
||||||
|
result.success(device.getResetSpectrum());
|
||||||
|
break;
|
||||||
|
case "getWavelength":
|
||||||
|
result.success(device.getWavelength());
|
||||||
|
break;
|
||||||
|
case "getWaveNumber":
|
||||||
|
result.success(device.getWaveNumber());
|
||||||
|
break;
|
||||||
|
case "getXvalue":
|
||||||
|
result.success(ArrayUtils.InterpolX);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// --- 脉冲参数控制 ---
|
||||||
|
case "setLampDelayTime":
|
||||||
|
safeBooleanCall(result, () -> device.setLampDelayTime(getDoubleArg(call, "delayTime")));
|
||||||
|
break;
|
||||||
|
case "setLampWidth":
|
||||||
|
safeBooleanCall(result, () -> device.setLampWidth(getDoubleArg(call, "width")));
|
||||||
|
break;
|
||||||
|
case "setLampInterval":
|
||||||
|
safeBooleanCall(result, () -> device.setLampInterval(getDoubleArg(call, "interval")));
|
||||||
|
break;
|
||||||
|
case "getLampDelayTime":
|
||||||
|
safeDoubleCall(result, () -> device.getLampDelayTime());
|
||||||
|
break;
|
||||||
|
case "getLampWidth":
|
||||||
|
safeDoubleCall(result, () -> device.getLampWidth());
|
||||||
|
break;
|
||||||
|
case "getLampInterval":
|
||||||
|
safeDoubleCall(result, () -> device.getLampInterval());
|
||||||
|
break;
|
||||||
|
|
||||||
|
// --- 激发波长与激光控制 ---
|
||||||
|
case "setExcitedWaveLength":
|
||||||
|
safeBooleanCall(result, () -> device.setExcitedWaveLength(getIntArg(call, "excitedWaveLength")));
|
||||||
|
break;
|
||||||
|
case "setLaserPower":
|
||||||
|
int power = getIntArg(call, "power");
|
||||||
|
Log.i(TAG, "setLaserPower 请求: power=" + power);
|
||||||
|
if (power < 0 || power > 2000) {
|
||||||
|
Log.e(TAG, "setLaserPower 参数超出范围 (0-2000): " + power + ",拒绝执行");
|
||||||
|
result.success(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
safeBooleanCall(result, "setLaserPower", () -> device.setLaserPower(power));
|
||||||
|
break;
|
||||||
|
case "setLaserOn":
|
||||||
|
Log.i(TAG, "收到 setLaserOn 请求");
|
||||||
|
safeBooleanCall(result, () -> {
|
||||||
|
boolean is_tec_open = device.isTECOpen();
|
||||||
|
Log.i(TAG, "TEC 是否打开:" + is_tec_open);
|
||||||
|
if (!is_tec_open) {
|
||||||
|
boolean set_tec_res = device.setTECOn();
|
||||||
|
Log.i(TAG, "TEC 打开结果:" + set_tec_res);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean res = device.setLaserOn();
|
||||||
|
Log.i(TAG, "device.setLaserOn() 返回:" + res);
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "setLaserOff":
|
||||||
|
Log.i(TAG, "收到 setLaserOff 请求");
|
||||||
|
safeBooleanCall(result, () -> {
|
||||||
|
boolean res = device.setLaserOff();
|
||||||
|
Log.i(TAG, "device.setLaserOff() 返回:" + res);
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "setLaserPowerOn":
|
||||||
|
safeBooleanCall(result, () -> device.setLaserPowerOn());
|
||||||
|
break;
|
||||||
|
case "setLaserPowerOff":
|
||||||
|
safeBooleanCall(result, () -> device.setLaserPowerOff());
|
||||||
|
break;
|
||||||
|
|
||||||
|
// --- 激光校正系数 ---
|
||||||
|
case "getLaserPowerCorrectCoefficient":
|
||||||
|
handleGetLaserPowerCorrectCoefficient(result);
|
||||||
|
break;
|
||||||
|
case "setLaserPowerCorrectCoefficient":
|
||||||
|
handleSetLaserPowerCorrectCoefficient(call, result);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// --- 硬件开关控制 ---
|
||||||
|
case "setTECOn":
|
||||||
|
safeBooleanCall(result, () -> device.setTECOn());
|
||||||
|
break;
|
||||||
|
case "setTECOff":
|
||||||
|
safeBooleanCall(result, () -> device.setTECOff());
|
||||||
|
break;
|
||||||
|
case "setMainBoardOn":
|
||||||
|
safeBooleanCall(result, () -> device.setMainBoardOn());
|
||||||
|
break;
|
||||||
|
case "setMainBoardOff":
|
||||||
|
safeBooleanCall(result, () -> device.setMainBoardOff());
|
||||||
|
break;
|
||||||
|
case "isMainBoardOpen":
|
||||||
|
safeBooleanCall(result, () -> device.isMainBoardOpen());
|
||||||
|
break;
|
||||||
|
case "isTECOpen":
|
||||||
|
safeBooleanCall(result, () -> device.isTECOpen());
|
||||||
|
break;
|
||||||
|
|
||||||
|
// --- 光谱数据处理 ---
|
||||||
|
case "waveletSmooth":
|
||||||
|
handleWaveletSmooth(call, result);
|
||||||
|
break;
|
||||||
|
case "removeFluorescence":
|
||||||
|
handleRemoveFluorescence(call, result);
|
||||||
|
break;
|
||||||
|
case "lineInterpolation":
|
||||||
|
handleLineInterpolation(call, result);
|
||||||
|
break;
|
||||||
|
case "getRamanSpectrum":
|
||||||
|
handleGetRamanSpectrum(result);
|
||||||
|
break;
|
||||||
|
case "findPeaks":
|
||||||
|
handleFindPeaks(call, result);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// --- 算法匹配与校准 ---
|
||||||
|
case "ramanMatchRegister":
|
||||||
|
handleRamanMatchRegister(result);
|
||||||
|
break;
|
||||||
|
case "ramanMatchCalcSimilarity":
|
||||||
|
handleRamanMatchCalcSimilarity(call, result);
|
||||||
|
break;
|
||||||
|
case "calibration":
|
||||||
|
handleCalibration(call, result);
|
||||||
|
break;
|
||||||
|
case "getYJCorrectCoefficient":
|
||||||
|
result.success(device.getYJCorrectCoefficient());
|
||||||
|
break;
|
||||||
|
case "setYJCorrectCoefficient":
|
||||||
|
handleSetYJCorrectCoefficient(call, result);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// --- 其他校准信息查询 ---
|
||||||
|
case "getYAxisCorrectCoefficient":
|
||||||
|
result.success(device.getYAxisCorrectCoefficient());
|
||||||
|
break;
|
||||||
|
case "getCorrectForDetectorNonlinear":
|
||||||
|
result.success(device.getCorrectForDetectorNonlinear());
|
||||||
|
break;
|
||||||
|
case "getWavelengthCalibrationCoefficients":
|
||||||
|
result.success(device.getWavelengthCalibrationCoefficients());
|
||||||
|
break;
|
||||||
|
case "getFpgaVersion":
|
||||||
|
result.success(device.getFpgaVersion());
|
||||||
|
break;
|
||||||
|
case "getSlit":
|
||||||
|
result.success(device.getSlit());
|
||||||
|
break;
|
||||||
|
case "getBadPoints":
|
||||||
|
result.success(device.getBadPoints());
|
||||||
|
break;
|
||||||
|
case "setLampEnable":
|
||||||
|
safeBooleanCall(result, () -> device.setLampEnable((Boolean) ((Map<String, Object>) call.arguments()).get("enable")));
|
||||||
|
break;
|
||||||
|
|
||||||
|
// --- MD5 加密 ---
|
||||||
|
case "bytesToMD5":
|
||||||
|
String sn = (String) ((Map<String, Object>) call.arguments()).get("sn");
|
||||||
|
result.success(computeMD5(sn));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
result.notImplemented();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 设备管理处理 ====================
|
||||||
|
|
||||||
|
/** 打开所有已连接的设备,并初始化第一个设备的默认参数 */
|
||||||
|
private void handleOpenAllDevices(Result result) {
|
||||||
|
try {
|
||||||
|
List<Device> deviceList = DeviceWrapper.openAllDevices(context);
|
||||||
|
device = deviceList.get(0);
|
||||||
|
device.setCallBack(state -> {
|
||||||
|
if (state) {
|
||||||
|
initDeviceDefaults();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
result.success(deviceList.size());
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.success(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 初始化设备默认参数(激光功率、积分时间、波数起始索引、激光校正系数) */
|
||||||
|
private void initDeviceDefaults() {
|
||||||
|
device.setLaserPower(DEFAULT_LASER_POWER);
|
||||||
|
device.setIntegrationTime(integrationTime);
|
||||||
|
waveNumber = device.getWaveNumber();
|
||||||
|
startPxIndexForWaveNumber = findWaveNumberStartIndex(waveNumber);
|
||||||
|
|
||||||
|
// 设置激光校正系数
|
||||||
|
String sn = device.getSerialNumber();
|
||||||
|
String laserCoefficient = getLaserPowerCoefficient(sn);
|
||||||
|
Log.i(TAG, "初始化设备参数:SN=" + sn + ", 激光校正系数:" + laserCoefficient);
|
||||||
|
|
||||||
|
if (laserCoefficient != null && !laserCoefficient.isEmpty() && laserCoefficient.length() == 16) {
|
||||||
|
try {
|
||||||
|
device.setLaserPowerCorrectCoefficient(laserCoefficient);
|
||||||
|
Log.i(TAG, "激光校正系数设置成功:" + laserCoefficient);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "设置激光校正系数失败:" + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "激光校正系数为空或格式错误,跳过设置");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找波数数组中 >= 200 波数的起始索引位置
|
||||||
|
*
|
||||||
|
* @param waveNumber 波数数组
|
||||||
|
* @return 起始索引(目标位置的前一个),未找到返回 0
|
||||||
|
*/
|
||||||
|
private int findWaveNumberStartIndex(double[] waveNumber) {
|
||||||
|
for (int i = 0; i < waveNumber.length; i++) {
|
||||||
|
if (waveNumber[i] >= WAVE_NUMBER_START) {
|
||||||
|
return i - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 关闭所有设备并释放引用 */
|
||||||
|
private void handleCloseAllDevices(Result result) {
|
||||||
|
try {
|
||||||
|
DeviceWrapper.closeAllDevices();
|
||||||
|
device = null;
|
||||||
|
result.success(true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.success(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取设备序列号 */
|
||||||
|
private void handleGetDeviceSn(Result result) {
|
||||||
|
try {
|
||||||
|
result.success(device.getSerialNumber());
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.success("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 检查设备是否已连接 */
|
||||||
|
private void handleIsConnect(Result result) {
|
||||||
|
try {
|
||||||
|
result.success(device.isConnect());
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.success(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 关闭单个设备 */
|
||||||
|
private void handleCloseDevice(Result result) {
|
||||||
|
try {
|
||||||
|
if (device != null) {
|
||||||
|
device.close();
|
||||||
|
device = null;
|
||||||
|
}
|
||||||
|
result.success(true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.success(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取设备索引 */
|
||||||
|
private void handleGetIndex(Result result) {
|
||||||
|
try {
|
||||||
|
result.success(device != null ? device.getIndex() : -1);
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.success(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 开启 SDK 调试日志 */
|
||||||
|
private void handleOpenDebug() {
|
||||||
|
DeviceWrapper.openDebug();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 检查 USB 权限(SDK 的 PermissionCallBack 接口在 AAR 中不可用,此处占位) */
|
||||||
|
private void handleCheckUsbPermission(Result result) {
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("deviceIndex", 0);
|
||||||
|
response.put("state", device != null && device.isConnect());
|
||||||
|
result.success(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 参数配置处理 ====================
|
||||||
|
|
||||||
|
/** 设置积分时间 */
|
||||||
|
private void handleSetIntegrationTime(MethodCall call, Result result) {
|
||||||
|
result.success(device.setIntegrationTime(getDoubleArg(call, "integrationTime")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 设置平均次数(设备方法无返回值,固定返回 true) */
|
||||||
|
private void handleSetAverageTimes(MethodCall call, Result result) {
|
||||||
|
device.setAverageTimes(getIntArg(call, "averageTimes"));
|
||||||
|
result.success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 设置触发模式 */
|
||||||
|
private void handleSetTriggerMode(MethodCall call, Result result) {
|
||||||
|
result.success(device.setTriggerMode(getIntArg(call, "triggerMode")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 激光校正系数处理 ====================
|
||||||
|
|
||||||
|
/** 获取激光校正系数 */
|
||||||
|
private void handleGetLaserPowerCorrectCoefficient(Result result) {
|
||||||
|
try {
|
||||||
|
double[] coefficient = device.getLaserPowerCorrectCoefficient();
|
||||||
|
result.success(coefficient);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "获取激光校正系数失败:" + e.getMessage(), e);
|
||||||
|
result.success(new double[]{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置激光校正系数
|
||||||
|
* @param call 调用参数,包含 calibrationCoefficient 字符串
|
||||||
|
* @param result 回调结果
|
||||||
|
*/
|
||||||
|
private void handleSetLaserPowerCorrectCoefficient(MethodCall call, Result result) {
|
||||||
|
try {
|
||||||
|
String calibrationCoefficient = (String) ((Map<String, Object>) call.arguments()).get("calibrationCoefficient");
|
||||||
|
if (calibrationCoefficient == null || calibrationCoefficient.length() != 16) {
|
||||||
|
Log.e(TAG, "激光校正系数格式错误,必须为 16 位字符串");
|
||||||
|
result.success(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
device.setLaserPowerCorrectCoefficient(calibrationCoefficient);
|
||||||
|
result.success(true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "设置激光校正系数失败:" + e.getMessage(), e);
|
||||||
|
result.success(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 光谱数据处理 ====================
|
||||||
|
|
||||||
|
/** 小波平滑处理 */
|
||||||
|
private void handleWaveletSmooth(MethodCall call, Result result) {
|
||||||
|
try {
|
||||||
|
double[] spectrum = getDoubleArrayArg(call, "spectrum");
|
||||||
|
result.success(device.waveletSmooth(spectrum));
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.success(new double[]{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 消荧光处理 */
|
||||||
|
private void handleRemoveFluorescence(MethodCall call, Result result) {
|
||||||
|
try {
|
||||||
|
Map<String, Object> arguments = call.arguments();
|
||||||
|
double[] spectrum = getDoubleArrayArg(call, "spectrum");
|
||||||
|
int side = FLUORESCENCE_REMOVAL_FACTOR;
|
||||||
|
if (arguments != null && arguments.containsKey("side")) {
|
||||||
|
side = ((Number) arguments.get("side")).intValue();
|
||||||
|
}
|
||||||
|
result.success(device.removeFluorescence(spectrum, side));
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.success(new double[]{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 线性插值处理 */
|
||||||
|
private void handleLineInterpolation(MethodCall call, Result result) {
|
||||||
|
try {
|
||||||
|
double[] xDataRaw = getDoubleArrayArg(call, "xDataRaw");
|
||||||
|
double[] yDataRaw = getDoubleArrayArg(call, "yDataRaw");
|
||||||
|
double[] xData = getDoubleArrayArg(call, "xData");
|
||||||
|
result.success(device.lineInterpolation(xDataRaw, yDataRaw, xData));
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.success(new double[]{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取经过完整处理流程的拉曼光谱
|
||||||
|
* 在独立线程中执行,带 30 秒超时保护
|
||||||
|
*/
|
||||||
|
private void handleGetRamanSpectrum(Result result) {
|
||||||
|
runWithTimeout(result, "getRamanSpectrum", this::acquireAndProcessSpectrum, SPECTRUM_TIMEOUT_MS, new double[]{});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 寻峰处理 */
|
||||||
|
private void handleFindPeaks(MethodCall call, Result result) {
|
||||||
|
Map<String, Object> arguments = call.arguments();
|
||||||
|
double[] spectrum = getDoubleArrayArg(call, "spectrum");
|
||||||
|
int minIndicesBetweenPeaks = (int) arguments.get("minIndicesBetweenPeaks");
|
||||||
|
double baseline = (double) arguments.get("baseline");
|
||||||
|
result.success(device.findPeaks(spectrum, minIndicesBetweenPeaks, baseline));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 算法匹配与校准处理 ====================
|
||||||
|
|
||||||
|
/** 注册检测算法(根据设备 SN 码获取激活码) */
|
||||||
|
private void handleRamanMatchRegister(Result result) {
|
||||||
|
String sn = device.getSerialNumber();
|
||||||
|
String activationCode = getActivationCode(sn);
|
||||||
|
Log.i(TAG, "算法注册:sn=" + sn + ", code=" + activationCode);
|
||||||
|
result.success(RamanMatch.register(device, activationCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 光谱相似度比对 */
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void handleRamanMatchCalcSimilarity(MethodCall call, Result result) {
|
||||||
|
Map<String, ArrayList<Double>> arguments = call.arguments();
|
||||||
|
double[] spectrum = arrayListToDoubleArray(arguments.get("spectrum"));
|
||||||
|
double[] libSpectrum = arrayListToDoubleArray(arguments.get("libSpectrumList"));
|
||||||
|
MatchResult matchResult = RamanMatch.getInstance().calcSimilarity(spectrum, libSpectrum);
|
||||||
|
result.success(matchResult.getSimilarity());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 光谱校准 */
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void handleCalibration(MethodCall call, Result result) {
|
||||||
|
try {
|
||||||
|
Map<String, Object> arguments = call.arguments();
|
||||||
|
double[] spectrum = (double[]) arguments.get("spectrum");
|
||||||
|
double[] yjCoeff = arrayListToDoubleArray((ArrayList<Double>) arguments.get("YJCoeff"));
|
||||||
|
double[] calibrationResult = RamanMatch.getInstance().calibration(spectrum, yjCoeff);
|
||||||
|
if (calibrationResult == null) {
|
||||||
|
Log.e(TAG, "校准失败:RamanMatch.calibration 返回 null");
|
||||||
|
result.success(new double[]{});
|
||||||
|
} else {
|
||||||
|
result.success(calibrationResult);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "校准异常:" + e.getMessage(), e);
|
||||||
|
result.success(new double[]{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 设置乙腈定标系数 */
|
||||||
|
private void handleSetYJCorrectCoefficient(MethodCall call, Result result) {
|
||||||
|
try {
|
||||||
|
Map<String, Object> arguments = call.arguments();
|
||||||
|
Object coffesObj = arguments.get("coffes");
|
||||||
|
double[] coffes;
|
||||||
|
if (coffesObj instanceof double[]) {
|
||||||
|
// 直接是 double 数组
|
||||||
|
coffes = (double[]) coffesObj;
|
||||||
|
} else if (coffesObj instanceof ArrayList) {
|
||||||
|
// ArrayList 转换为 double 数组
|
||||||
|
coffes = arrayListToDoubleArray((ArrayList<Double>) coffesObj);
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "coffes 参数类型错误:" + coffesObj.getClass().getName());
|
||||||
|
result.success(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
result.success(device.setYJCorrectCoefficient(coffes));
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "设置乙腈定标系数失败:" + e.getMessage(), e);
|
||||||
|
result.success(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 光谱采集核心流程 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行完整的拉曼光谱采集与处理流程
|
||||||
|
* 流程:关激光 -> 采集背景 -> 开激光 -> 等待稳定 -> 采集样本 -> 关激光 -> 饱和检测 -> 扣除背景 -> 插值 -> Y 轴校正 -> 消荧光 -> 平滑 -> 取整
|
||||||
|
*
|
||||||
|
* @return 处理完成的光谱数据(取整后),饱和时返回空数组
|
||||||
|
*/
|
||||||
|
private double[] acquireAndProcessSpectrum() {
|
||||||
|
// 1. 确保激光关闭后采集背景光谱
|
||||||
|
device.setLaserOff();
|
||||||
|
double[] bgSpectrum = device.getResetSpectrum();
|
||||||
|
|
||||||
|
// 2. 开激光
|
||||||
|
boolean laserOnRes = device.setLaserOn();
|
||||||
|
Log.i(TAG, "开激光结果:" + laserOnRes);
|
||||||
|
|
||||||
|
// 3. 等待激光功率稳定(2 秒)
|
||||||
|
try {
|
||||||
|
Thread.sleep(2000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
Log.w(TAG, "激光稳定等待被中断");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 采集样本光谱
|
||||||
|
double[] sampleSpectrum = device.getResetSpectrum();
|
||||||
|
|
||||||
|
// 5. 关激光
|
||||||
|
device.setLaserOff();
|
||||||
|
|
||||||
|
// 6. 饱和检测
|
||||||
|
if (isSpectrumSaturated(sampleSpectrum)) {
|
||||||
|
Log.w(TAG, "光谱饱和,数据无效");
|
||||||
|
return new double[]{};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 扣除背景
|
||||||
|
double[] spectrum = subtractBackground(sampleSpectrum, bgSpectrum);
|
||||||
|
|
||||||
|
// 8. 插值到波数 200~3200
|
||||||
|
spectrum = device.lineInterpolation(waveNumber, spectrum, ArrayUtils.InterpolX);
|
||||||
|
|
||||||
|
// 9. Y 轴校正
|
||||||
|
spectrum = device.yAxisCorrect(waveNumber, spectrum);
|
||||||
|
|
||||||
|
// 10. 消荧光
|
||||||
|
spectrum = device.removeFluorescence(spectrum, FLUORESCENCE_REMOVAL_FACTOR);
|
||||||
|
|
||||||
|
// 11. 平滑
|
||||||
|
spectrum = device.waveletSmooth(spectrum);
|
||||||
|
|
||||||
|
// 12. 取整
|
||||||
|
roundInPlace(spectrum);
|
||||||
|
|
||||||
|
return spectrum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扣除背景光谱(样本 - 背景)
|
||||||
|
*
|
||||||
|
* @param sample 样本光谱
|
||||||
|
* @param background 背景光谱
|
||||||
|
* @return 扣除背景后的光谱数据
|
||||||
|
*/
|
||||||
|
private double[] subtractBackground(double[] sample, double[] background) {
|
||||||
|
int length = Math.min(background.length, sample.length);
|
||||||
|
double[] result = new double[length];
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
result[i] = sample[i] - background[i];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 对数组每个元素四舍五入取整(原地修改) */
|
||||||
|
private void roundInPlace(double[] array) {
|
||||||
|
for (int i = 0; i < array.length; i++) {
|
||||||
|
array[i] = Math.round(array[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断光谱是否饱和(200 波数之后是否存在>=65535 的值)
|
||||||
|
*
|
||||||
|
* @param spectrumRaw 原始光谱数据
|
||||||
|
* @return true 表示已饱和
|
||||||
|
*/
|
||||||
|
private boolean isSpectrumSaturated(double[] spectrumRaw) {
|
||||||
|
if (startPxIndexForWaveNumber <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = startPxIndexForWaveNumber; i < spectrumRaw.length; i++) {
|
||||||
|
if (spectrumRaw[i] >= SATURATION_THRESHOLD) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 安全调用包装(带超时保护) ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 超时后放弃当前 executor 并重建
|
||||||
|
* 因为 bulkTransfer 是 JNI native 调用,Thread.interrupt() 无法中断它,
|
||||||
|
* 被阻塞的线程会永远持有 synchronized 锁。
|
||||||
|
* 唯一办法是抛弃 executor 创建新的,让后续操作在新线程中执行。
|
||||||
|
*/
|
||||||
|
private synchronized void abandonExecutor(String reason) {
|
||||||
|
Log.w(TAG, reason + ":放弃当前 executor 并重建");
|
||||||
|
try {
|
||||||
|
usbExecutor.shutdownNow();
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
usbExecutor = Executors.newSingleThreadExecutor();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 带超时保护的设备操作包装器
|
||||||
|
*
|
||||||
|
* @param result Flutter 回调
|
||||||
|
* @param action 设备操作
|
||||||
|
*/
|
||||||
|
private void safeBooleanCall(Result result, BooleanAction action) {
|
||||||
|
safeBooleanCall(result, "", action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 带超时保护的设备操作包装器(带操作名称日志)
|
||||||
|
*
|
||||||
|
* @param result Flutter 回调
|
||||||
|
* @param actionName 操作名称(用于日志)
|
||||||
|
* @param action 设备操作
|
||||||
|
*/
|
||||||
|
private void safeBooleanCall(Result result, String actionName, BooleanAction action) {
|
||||||
|
final ExecutorService currentExecutor = usbExecutor;
|
||||||
|
Future<Boolean> future = currentExecutor.submit(() -> {
|
||||||
|
boolean success = action.execute();
|
||||||
|
if (!actionName.isEmpty()) {
|
||||||
|
Log.i(TAG, actionName + " 执行结果:" + success);
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
boolean success = future.get(USB_CALL_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||||
|
result.success(success);
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
abandonExecutor(actionName + " 超时");
|
||||||
|
result.success(false);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, (actionName.isEmpty() ? "safeBooleanCall" : actionName) + " 执行失败:" + e.getMessage(), e);
|
||||||
|
result.success(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用 double 结果的安全调用包装,异常时返回空数组
|
||||||
|
*
|
||||||
|
* @param result Flutter 回调
|
||||||
|
* @param action 设备操作
|
||||||
|
*/
|
||||||
|
private void safeDoubleCall(Result result, DoubleAction action) {
|
||||||
|
try {
|
||||||
|
result.success(action.execute());
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.success(new double[]{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 带超时保护的 double 数组结果调用包装器
|
||||||
|
*
|
||||||
|
* @param result Flutter 回调
|
||||||
|
* @param actionName 操作名称(用于日志)
|
||||||
|
* @param action 设备操作
|
||||||
|
*/
|
||||||
|
private void safeDoubleArrayCall(Result result, String actionName, DoubleArrayAction action) {
|
||||||
|
final ExecutorService currentExecutor = usbExecutor;
|
||||||
|
Future<double[]> future = currentExecutor.submit(() -> {
|
||||||
|
double[] arr = action.execute();
|
||||||
|
if (arr == null) return new double[]{};
|
||||||
|
return arr;
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
result.success(future.get(USB_CALL_TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
abandonExecutor(actionName + " 超时");
|
||||||
|
result.success(new double[]{});
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, actionName + " 执行失败:" + e.getMessage(), e);
|
||||||
|
result.success(new double[]{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行一个带超时的操作(任意返回类型)
|
||||||
|
* 超时后放弃 executor 并重建,确保后续调用不会被阻塞线程影响
|
||||||
|
*/
|
||||||
|
private <T> void runWithTimeout(Result result, String actionName, Callable<T> action, int timeoutMs, T fallback) {
|
||||||
|
final ExecutorService currentExecutor = usbExecutor;
|
||||||
|
Future<T> future = currentExecutor.submit(action);
|
||||||
|
|
||||||
|
try {
|
||||||
|
T value = future.get(timeoutMs, TimeUnit.MILLISECONDS);
|
||||||
|
result.success(value);
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
abandonExecutor(actionName + " 超时");
|
||||||
|
result.success(fallback);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, actionName + " 执行失败:" + e.getMessage(), e);
|
||||||
|
result.success(fallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 返回 boolean 的设备操作接口 */
|
||||||
|
@FunctionalInterface
|
||||||
|
private interface BooleanAction {
|
||||||
|
boolean execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 返回 double 的设备操作接口 */
|
||||||
|
@FunctionalInterface
|
||||||
|
private interface DoubleAction {
|
||||||
|
double execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 返回 double[] 的设备操作接口 */
|
||||||
|
@FunctionalInterface
|
||||||
|
private interface DoubleArrayAction {
|
||||||
|
double[] execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 参数提取工具方法 ====================
|
||||||
|
|
||||||
|
/** 从 MethodCall 参数中提取 double 值 */
|
||||||
|
private double getDoubleArg(MethodCall call, String key) {
|
||||||
|
Map<String, Object> arguments = call.arguments();
|
||||||
|
return ((Number) arguments.get(key)).doubleValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 从 MethodCall 参数中提取 int 值 */
|
||||||
|
private int getIntArg(MethodCall call, String key) {
|
||||||
|
Map<String, Object> arguments = call.arguments();
|
||||||
|
return ((Number) arguments.get(key)).intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 MethodCall 参数中提取 double 数组,兼容 ArrayList 和原生数组
|
||||||
|
*
|
||||||
|
* @param call MethodCall 对象
|
||||||
|
* @param key 参数名
|
||||||
|
* @return double 数组
|
||||||
|
*/
|
||||||
|
private double[] getDoubleArrayArg(MethodCall call, String key) {
|
||||||
|
Map<String, Object> arguments = call.arguments();
|
||||||
|
return toDoubleArray(arguments.get(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 类型转换工具方法 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 Object 转换为 double 数组,兼容 ArrayList 和原生数组两种类型
|
||||||
|
*
|
||||||
|
* @param obj 待转换对象
|
||||||
|
* @return double 数组
|
||||||
|
* @throws IllegalArgumentException 类型不支持或包含非 Number 元素时抛出
|
||||||
|
*/
|
||||||
|
private double[] toDoubleArray(Object obj) {
|
||||||
|
if (obj instanceof ArrayList) {
|
||||||
|
ArrayList<?> list = (ArrayList<?>) obj;
|
||||||
|
double[] result = new double[list.size()];
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
result[i] = ((Number) list.get(i)).doubleValue();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} else if (obj.getClass().isArray()) {
|
||||||
|
int length = java.lang.reflect.Array.getLength(obj);
|
||||||
|
double[] result = new double[length];
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
result[i] = ((Number) java.lang.reflect.Array.get(obj, i)).doubleValue();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Provided object is neither an ArrayList nor an array");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 将 ArrayList<Double>转换为 double 原生数组 */
|
||||||
|
private double[] arrayListToDoubleArray(ArrayList<Double> list) {
|
||||||
|
double[] result = new double[list.size()];
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
result[i] = list.get(i);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 加密与激活码 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算字符串的 MD5 哈希值
|
||||||
|
*
|
||||||
|
* @param input 输入字符串
|
||||||
|
* @return MD5 十六进制字符串,异常时返回 null
|
||||||
|
*/
|
||||||
|
public static String computeMD5(String input) {
|
||||||
|
try {
|
||||||
|
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||||
|
byte[] digest = md.digest(input.getBytes());
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (byte b : digest) {
|
||||||
|
sb.append(String.format("%02x", b & 0xFF));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据设备 SN 码获取算法激活码
|
||||||
|
*
|
||||||
|
* @param sn 设备序列号
|
||||||
|
* @return 对应的激活码,未注册的 SN 返回空字符串
|
||||||
|
*/
|
||||||
|
public String getActivationCode(String sn) {
|
||||||
|
String[] config = DEVICE_CONFIG_MAP.getOrDefault(sn, new String[]{"", ""});
|
||||||
|
return config[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据设备 SN 码获取激光校正系数
|
||||||
|
*
|
||||||
|
* @param sn 设备序列号
|
||||||
|
* @return 对应的激光校正系数(16 位字符串),未注册的 SN 返回空字符串
|
||||||
|
*/
|
||||||
|
public String getLaserPowerCoefficient(String sn) {
|
||||||
|
String[] config = DEVICE_CONFIG_MAP.getOrDefault(sn, new String[]{"", ""});
|
||||||
|
return config[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
217
android/src/main/java/com/example/terra/utils/ArrayUtils.java
Normal file
217
android/src/main/java/com/example/terra/utils/ArrayUtils.java
Normal file
File diff suppressed because one or more lines are too long
29
android/src/test/java/com/example/terra/TerraPluginTest.java
Normal file
29
android/src/test/java/com/example/terra/TerraPluginTest.java
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package com.example.terra;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import io.flutter.plugin.common.MethodCall;
|
||||||
|
import io.flutter.plugin.common.MethodChannel;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This demonstrates a simple unit test of the Java portion of this plugin's implementation.
|
||||||
|
*
|
||||||
|
* Once you have built the plugin's example app, you can run these tests from the command
|
||||||
|
* line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or
|
||||||
|
* you can run them directly from IDEs that support JUnit such as Android Studio.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class TerraPluginTest {
|
||||||
|
@Test
|
||||||
|
public void onMethodCall_getPlatformVersion_returnsExpectedValue() {
|
||||||
|
TerraPlugin plugin = new TerraPlugin();
|
||||||
|
|
||||||
|
final MethodCall call = new MethodCall("getPlatformVersion", null);
|
||||||
|
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
|
||||||
|
plugin.onMethodCall(call, mockResult);
|
||||||
|
|
||||||
|
verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE);
|
||||||
|
}
|
||||||
|
}
|
||||||
593
docs/sdk_apis.md
Normal file
593
docs/sdk_apis.md
Normal file
@@ -0,0 +1,593 @@
|
|||||||
|
# Terra SDK 接口文档
|
||||||
|
|
||||||
|
|
||||||
|
## Terra SDK
|
||||||
|
|
||||||
|
Terra 是设备的名称,包含了光谱仪、激光器、拉曼相关接口。
|
||||||
|
|
||||||
|
## 接口列表
|
||||||
|
|
||||||
|
| 类名称 | 描述 |
|
||||||
|
|--------|------|
|
||||||
|
| DeviceWrapper | 设备管理器 |
|
||||||
|
| Device | 设备 |
|
||||||
|
|
||||||
|
## 调用示例
|
||||||
|
|
||||||
|
**step1: 获取 USB 设备**
|
||||||
|
```java
|
||||||
|
List<Device> allDevices = DeviceWrapper.openAllDevices(mContext);
|
||||||
|
```
|
||||||
|
|
||||||
|
**step2: 设备初始化**,初始化完成后通过回调方式通知客户端
|
||||||
|
```java
|
||||||
|
device.setCallBack(new Device.CallBack() {
|
||||||
|
@Override
|
||||||
|
public void onDeviceStatue(boolean state) {
|
||||||
|
Log.d(TAG, "state = " + state );
|
||||||
|
String sn = "";
|
||||||
|
if(state){
|
||||||
|
sn = device.getSerialNumber();
|
||||||
|
}
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**step3: 调用设备**
|
||||||
|
```java
|
||||||
|
double[] spectrum = device.getSpectrum();
|
||||||
|
```
|
||||||
|
|
||||||
|
## DeviceWrapper
|
||||||
|
|
||||||
|
获取或关闭所有设备
|
||||||
|
|
||||||
|
### Methods
|
||||||
|
|
||||||
|
#### openAllDevices
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 打开所有设备
|
||||||
|
*
|
||||||
|
* @param context Application context
|
||||||
|
* @return 挂在的所有设备
|
||||||
|
*/
|
||||||
|
public static List<Device> openAllDevices(Context context);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### closeAllDevices
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 关闭所有设备
|
||||||
|
*/
|
||||||
|
public static void closeAllDevices();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### openDebug
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 开启日志输出
|
||||||
|
*/
|
||||||
|
public static void openDebug();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### checkUsbPermission
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 检测是否有 USB 权限
|
||||||
|
*/
|
||||||
|
public static void checkUsbPermission(Context context, PermissionCallBack permissionCallBack) {
|
||||||
|
UsbConnect.checkUsbPermission(context,permissionCallBack);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### PermissionCallBack
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* USB 权限状态回调
|
||||||
|
*/
|
||||||
|
public interface PermissionCallBack{
|
||||||
|
/**
|
||||||
|
* USB 权限状态
|
||||||
|
* @param deviceIndex 设备索引
|
||||||
|
* @param state true:有权限
|
||||||
|
*/
|
||||||
|
public void onPermissionStatue(int deviceIndex, boolean state);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
## Device
|
||||||
|
|
||||||
|
Device 是封装了设备提供的接口
|
||||||
|
|
||||||
|
### Methods
|
||||||
|
|
||||||
|
`CallBack` `setCallBack` `close` `getIndex` `isConnect` `getSerialNumber` `setIntegrationTime` `setAverageTimes` `setTriggerMode` `getSpectrum` `getResetSpectrum` `getWavelength` `getWaveNumber` `setCCDTECOn` `setCCDTECOff` `setCCDTECTemperature` `setLampEnable` `setLampDelayTime` `setLampWidth` `setLampInterval` `getLampDelayTime` `getLampWidth` `getLampInterval` `setExcitedWaveLength` `setLaserPower` `setLaserOn` `setLaserOff` `setLaserPowerOn` `setLaserPowerOff` `setTECOn` `setTECOff` `setMainBoardOn` `setMainBoardOff` `isMainBoardOpen` `isTECOpen` `waveletSmooth` `removeFluorescence` `lineInterpolation` `findPeaks` `getSlit` `getBadPoints` `getYJCorrectCoefficient` `getYAxisCorrectCoefficient` `getCorrectForDetectorNonlinear` `getWavelengthCalibrationCoefficients` `getLaserPowerCorrectCoefficient` `getFpgaVersion` `setYJCorrectCoefficient` `setLaserPowerCorrectCoefficient`
|
||||||
|
|
||||||
|
#### CallBack
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 设备状态回调接口
|
||||||
|
*/
|
||||||
|
public interface CallBack{
|
||||||
|
/**
|
||||||
|
* 设备状态回调
|
||||||
|
* @param state 设备状态
|
||||||
|
* true:连接成功
|
||||||
|
* false:连接失败
|
||||||
|
*/
|
||||||
|
void onDeviceStatue(boolean state);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### setCallBack
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 设置回调函数
|
||||||
|
* @param callBack 回调
|
||||||
|
*/
|
||||||
|
public void setCallBack(CallBack callBack);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### close
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 关闭设备
|
||||||
|
*/
|
||||||
|
public void close();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### getIndex
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 设备编号
|
||||||
|
* @return 编号
|
||||||
|
*/
|
||||||
|
public int getIndex();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### isConnect
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 设备是否连接成功
|
||||||
|
*/
|
||||||
|
public boolean isConnect();
|
||||||
|
```
|
||||||
|
#### getSerialNumber
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 获取设备序列号
|
||||||
|
* @return 设备序列号
|
||||||
|
*/
|
||||||
|
public String getSerialNumber();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### setIntegrationTime
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 设置设备积分时间
|
||||||
|
* @param integrationTime 积分时间
|
||||||
|
* @return 是否设置成功
|
||||||
|
*/
|
||||||
|
public boolean setIntegrationTime(double integrationTime);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### setAverageTimes
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 设置平均次数
|
||||||
|
* @param averageTimes 平均次数
|
||||||
|
*/
|
||||||
|
public void setAverageTimes(int averageTimes);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### setTriggerMode
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 设置触发模式
|
||||||
|
* @param triggerMode 触发模式
|
||||||
|
* 0: Free Running
|
||||||
|
* 1: Hardware Level
|
||||||
|
* 2: Synchronous
|
||||||
|
* 3: Hardware Edge
|
||||||
|
* 4: Edge And Free
|
||||||
|
* @return 是否设置成功
|
||||||
|
*/
|
||||||
|
public boolean setTriggerMode(int triggerMode);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### getSpectrum
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 获取光谱(读取当前光谱,非平均过后的光谱)
|
||||||
|
* @return 光谱数据
|
||||||
|
*/
|
||||||
|
public double[] getSpectrum();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### getResetSpectrum
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 获取光谱(读取当前光谱,受用户积分时间)
|
||||||
|
* @return 光谱数据
|
||||||
|
*/
|
||||||
|
public double[] getResetSpectrum();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### getWavelength
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 获取波长
|
||||||
|
* @return 波长数据
|
||||||
|
*/
|
||||||
|
public double[] getWavelength();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### getWaveNumber
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 获取波数
|
||||||
|
* @return 波数数据
|
||||||
|
*/
|
||||||
|
public double[] getWaveNumber();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### setCCDTECOn
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 开 CCD 制冷
|
||||||
|
* @return 是否关闭成功
|
||||||
|
*/
|
||||||
|
public boolean setCCDTECOn();
|
||||||
|
```
|
||||||
|
#### setCCDTECOff
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 关 CCD 制冷
|
||||||
|
* @return 是否关闭成功
|
||||||
|
*/
|
||||||
|
public boolean setCCDTECOff();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### setCCDTECTemperature
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 设置制冷的目标温度 (-20~40)
|
||||||
|
* @return 是否设置成功
|
||||||
|
*/
|
||||||
|
public boolean setCCDTECTemperature(int temperature);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### getCCDTECState
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 获取探测器制冷状态,返回 16 个字节:
|
||||||
|
* 第一个字节是 TEC 当前温度符号位(0:零上,1:零下),单位
|
||||||
|
* 第二个字节是 TEC 当前温度整数位
|
||||||
|
* 第三个字节是 TEC 当前温度小数位
|
||||||
|
* 第四个字节是 PID 参数 P 高八位
|
||||||
|
* 第五个字节是 PID 参数 P 低八位
|
||||||
|
* 第六个字节是 PID 参数 I
|
||||||
|
* 第七个字节是 PID 参数 D
|
||||||
|
* 第八个字节是 TEC 电流符号位(0:正电流,1:负电流),单位 mA
|
||||||
|
* 第九个字节是 TEC 电流高八位
|
||||||
|
* 第十个字节是 TEC 电流低八位
|
||||||
|
* 第十一个字节是报警是否位(0 表示没有,1 表示报警,2 表示 TEC 没有)
|
||||||
|
* 第十二个字节 TEC 目标温度符号位(0:零上,1:零下),单位
|
||||||
|
* 第十三个字节 TEC 目标温度整数位
|
||||||
|
* 第十四个字节 LD 电流高八位(目前是 PD 管放大器的电压)
|
||||||
|
* 第十五个字节 LD 电流低八位
|
||||||
|
* 第十六个字节上电状态(0 表示没上电,1 表示上电)
|
||||||
|
* @return 当前状态信息
|
||||||
|
*/
|
||||||
|
public byte[] getCCDTECState();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### setLampEnable
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* Lamp 控制
|
||||||
|
* @param enable 控制 true:开,false
|
||||||
|
* @return 是否设置成功
|
||||||
|
*/
|
||||||
|
public boolean setLampEnable(boolean enable);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### setLampDelayTime
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 设置灯泡的延迟时间(微秒)
|
||||||
|
* @param delayTime 延迟时间
|
||||||
|
* @return 是否设置成功
|
||||||
|
*/
|
||||||
|
public boolean setLampDelayTime(double delayTime);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### setLampWidth
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 设置灯泡脉宽(微秒)
|
||||||
|
* @param width 脉宽
|
||||||
|
* @return 是否设置成功
|
||||||
|
*/
|
||||||
|
public boolean setLampWidth(double width);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### setLampInterval
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 设置灯泡的周期(微秒)
|
||||||
|
* @param interval 周期
|
||||||
|
* @return 是否设置成功
|
||||||
|
*/
|
||||||
|
public boolean setLampInterval(double interval);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### getLampDelayTime
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 获取延迟时间(微秒)
|
||||||
|
* @return 延迟时间
|
||||||
|
*/
|
||||||
|
public double getLampDelayTime();
|
||||||
|
```
|
||||||
|
#### getLampWidth
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 获取灯泡脉宽(微秒)
|
||||||
|
* @return 灯泡脉宽
|
||||||
|
*/
|
||||||
|
public double getLampWidth();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### getLampInterval
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 获取灯泡的周期(微秒)
|
||||||
|
* @return 灯泡周期
|
||||||
|
*/
|
||||||
|
public double getLampInterval();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### setExcitedWaveLength
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 设置激发波长
|
||||||
|
* @param excitedWaveLength 激发波长
|
||||||
|
* @return 是否设置成功
|
||||||
|
*/
|
||||||
|
public boolean setExcitedWaveLength(int excitedWaveLength);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### setLaserPower
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 设置激光功率
|
||||||
|
* @param power 激光功率
|
||||||
|
* @return 是否设置成功
|
||||||
|
*/
|
||||||
|
public boolean setLaserPower(int power);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### setLaserOn
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 开激光
|
||||||
|
* @return 是否设置成功
|
||||||
|
*/
|
||||||
|
public boolean setLaserOn();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### setLaserOff
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 关激光
|
||||||
|
* @return 是否设置成功
|
||||||
|
*/
|
||||||
|
public boolean setLaserOff();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### setLaserPowerOn
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 开激光器电源
|
||||||
|
* @return 是否设置成功
|
||||||
|
*/
|
||||||
|
public boolean setLaserPowerOn();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### setLaserPowerOff
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 关激光器电源
|
||||||
|
* @return 是否设置成功
|
||||||
|
*/
|
||||||
|
public boolean setLaserPowerOff();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### setTECOn
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 开 TEC
|
||||||
|
* @return 是否设置成功
|
||||||
|
*/
|
||||||
|
public boolean setTECOn();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### setTECOff
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 关 TEC
|
||||||
|
* @return 是否设置成功
|
||||||
|
*/
|
||||||
|
public boolean setTECOff();
|
||||||
|
```
|
||||||
|
#### setMainBoardOn
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 开主板
|
||||||
|
* @return 是否设置成功
|
||||||
|
*/
|
||||||
|
public double setMainBoardOn();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### setMainBoardOff
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 关主板
|
||||||
|
* @return 是否设置成功
|
||||||
|
*/
|
||||||
|
public double setMainBoardOff();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### isMainBoardOpen
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 主板是否开成功,在最后一次操作时判断
|
||||||
|
* @return 是否开成功
|
||||||
|
*/
|
||||||
|
public boolean isMainBoardOpen();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### isTECOpen
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 获取 TEC 状态,true:已开
|
||||||
|
* @return TEC 状态
|
||||||
|
*/
|
||||||
|
public boolean isTECOpen();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### waveletSmooth
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 小波平滑
|
||||||
|
* @param spectrum 光谱
|
||||||
|
* @return 平滑后的光谱
|
||||||
|
*/
|
||||||
|
public double[] waveletSmooth(double[] spectrum);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### removeFluorescence
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 去荧光
|
||||||
|
* @param spectrum 光谱
|
||||||
|
* @return 去荧光后的光谱
|
||||||
|
*/
|
||||||
|
public double[] removeFluorescence(double[] spectrum, int side);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### lineInterpolation
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 线性插值
|
||||||
|
* @param xDataRaw 原 X 轴数据
|
||||||
|
* @param yDataRaw 原 Y 轴数据
|
||||||
|
* @param xData 插值后的 X 轴数据
|
||||||
|
* @return yData 插值后的 Y 轴数据
|
||||||
|
*/
|
||||||
|
public double[] lineInterpolation(double[] xDataRaw, double[] yDataRaw, double[] xData);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### findPeaks
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 寻峰
|
||||||
|
* @param spectrum 光谱
|
||||||
|
* @param minIndicesBetweenPeaks 最小峰间距(值为 2,实际可能需要 5)
|
||||||
|
* @param baseline 基线
|
||||||
|
* @return 返回找到的峰的位置
|
||||||
|
*/
|
||||||
|
public static int[] findPeaks(double[] spectrum, int minIndicesBetweenPeaks, double baseline);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### getSlit
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 获取狭缝
|
||||||
|
* @return 狭缝大小
|
||||||
|
*/
|
||||||
|
public String getSlit();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### getBadPoints
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 获取坏点
|
||||||
|
* @return 坏点
|
||||||
|
*/
|
||||||
|
public int[] getBadPoints();
|
||||||
|
```
|
||||||
|
#### getYJCorrectCoefficient
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 获取波长校准系数
|
||||||
|
* @return 波长校准系数
|
||||||
|
*/
|
||||||
|
public double[] getYJCorrectCoefficient();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### getYAxisCorrectCoefficient
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 获取 Y 轴校准系数
|
||||||
|
* @return Y 轴系数
|
||||||
|
*/
|
||||||
|
public double[] getYAxisCorrectCoefficient();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### getCorrectForDetectorNonlinear
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 获取探测器非线性校正系数
|
||||||
|
* @return 探测器非线性校正系数
|
||||||
|
*/
|
||||||
|
public double[] getCorrectForDetectorNonlinear();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### getWavelengthCalibrationCoefficients
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 获取波长校准系数
|
||||||
|
* @return 波长校准系数
|
||||||
|
*/
|
||||||
|
public double[] getWavelengthCalibrationCoefficients();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### getLaserPowerCorrectCoefficient
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 获取功率校准系数
|
||||||
|
* @return 功率校准系数
|
||||||
|
*/
|
||||||
|
public double[] getLaserPowerCorrectCoefficient();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### getFpgaVersion
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 获取 FPGA 版本
|
||||||
|
* @return FPGA 版本
|
||||||
|
*/
|
||||||
|
public double[] getFpgaVersion();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### setYJCorrectCoefficient
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 设置波长校准系数
|
||||||
|
* @param coffes 波长校准系数
|
||||||
|
* @return 是否设置成功
|
||||||
|
*/
|
||||||
|
public boolean setYJCorrectCoefficient(double[] coffes);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### setLaserPowerCorrectCoefficient
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 设置功率校准系数
|
||||||
|
* @param calibrationCoefficient 功率校准系数(格式:16 位字符串,4 个数据,每个数据 4 位,如 0070030008001400)
|
||||||
|
*/
|
||||||
|
public void setLaserPowerCorrectCoefficient(String calibrationCoefficient);
|
||||||
|
```
|
||||||
43
example/.gitignore
vendored
Normal file
43
example/.gitignore
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
migrate_working_dir/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
#.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
**/doc/api/
|
||||||
|
**/ios/Flutter/.last_build_id
|
||||||
|
.dart_tool/
|
||||||
|
.flutter-plugins
|
||||||
|
.flutter-plugins-dependencies
|
||||||
|
.pub-cache/
|
||||||
|
.pub/
|
||||||
|
/build/
|
||||||
|
|
||||||
|
# Symbolication related
|
||||||
|
app.*.symbols
|
||||||
|
|
||||||
|
# Obfuscation related
|
||||||
|
app.*.map.json
|
||||||
|
|
||||||
|
# Android Studio will place build artifacts here
|
||||||
|
/android/app/debug
|
||||||
|
/android/app/profile
|
||||||
|
/android/app/release
|
||||||
16
example/README.md
Normal file
16
example/README.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# terra_example
|
||||||
|
|
||||||
|
Demonstrates how to use the terra plugin.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
This project is a starting point for a Flutter application.
|
||||||
|
|
||||||
|
A few resources to get you started if this is your first Flutter project:
|
||||||
|
|
||||||
|
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
||||||
|
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
|
||||||
|
|
||||||
|
For help getting started with Flutter development, view the
|
||||||
|
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
||||||
|
samples, guidance on mobile development, and a full API reference.
|
||||||
28
example/analysis_options.yaml
Normal file
28
example/analysis_options.yaml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# This file configures the analyzer, which statically analyzes Dart code to
|
||||||
|
# check for errors, warnings, and lints.
|
||||||
|
#
|
||||||
|
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||||
|
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||||
|
# invoked from the command line by running `flutter analyze`.
|
||||||
|
|
||||||
|
# The following line activates a set of recommended lints for Flutter apps,
|
||||||
|
# packages, and plugins designed to encourage good coding practices.
|
||||||
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
|
linter:
|
||||||
|
# The lint rules applied to this project can be customized in the
|
||||||
|
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||||
|
# included above or to enable additional rules. A list of all available lints
|
||||||
|
# and their documentation is published at https://dart.dev/lints.
|
||||||
|
#
|
||||||
|
# Instead of disabling a lint rule for the entire project in the
|
||||||
|
# section below, it can also be suppressed for a single line of code
|
||||||
|
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||||
|
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||||
|
# producing the lint.
|
||||||
|
rules:
|
||||||
|
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||||
|
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||||
|
|
||||||
|
# Additional information about this file can be found at
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
||||||
13
example/android/.gitignore
vendored
Normal file
13
example/android/.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
gradle-wrapper.jar
|
||||||
|
/.gradle
|
||||||
|
/captures/
|
||||||
|
/gradlew
|
||||||
|
/gradlew.bat
|
||||||
|
/local.properties
|
||||||
|
GeneratedPluginRegistrant.java
|
||||||
|
|
||||||
|
# Remember to never publicly share your keystore.
|
||||||
|
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
|
||||||
|
key.properties
|
||||||
|
**/*.keystore
|
||||||
|
**/*.jks
|
||||||
58
example/android/app/build.gradle
Normal file
58
example/android/app/build.gradle
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
plugins {
|
||||||
|
id "com.android.application"
|
||||||
|
id "kotlin-android"
|
||||||
|
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
|
||||||
|
id "dev.flutter.flutter-gradle-plugin"
|
||||||
|
}
|
||||||
|
|
||||||
|
def localProperties = new Properties()
|
||||||
|
def localPropertiesFile = rootProject.file("local.properties")
|
||||||
|
if (localPropertiesFile.exists()) {
|
||||||
|
localPropertiesFile.withReader("UTF-8") { reader ->
|
||||||
|
localProperties.load(reader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutterVersionCode = localProperties.getProperty("flutter.versionCode")
|
||||||
|
if (flutterVersionCode == null) {
|
||||||
|
flutterVersionCode = "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutterVersionName = localProperties.getProperty("flutter.versionName")
|
||||||
|
if (flutterVersionName == null) {
|
||||||
|
flutterVersionName = "1.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "com.example.terra_example"
|
||||||
|
compileSdk = flutter.compileSdkVersion
|
||||||
|
ndkVersion = flutter.ndkVersion
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
|
applicationId = "com.example.terra_example"
|
||||||
|
// You can update the following values to match your application needs.
|
||||||
|
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||||
|
minSdk = 29
|
||||||
|
targetSdk = flutter.targetSdkVersion
|
||||||
|
versionCode = flutterVersionCode.toInteger()
|
||||||
|
versionName = flutterVersionName
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
// TODO: Add your own signing config for the release build.
|
||||||
|
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||||
|
signingConfig = signingConfigs.debug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flutter {
|
||||||
|
source = "../.."
|
||||||
|
}
|
||||||
7
example/android/app/src/debug/AndroidManifest.xml
Normal file
7
example/android/app/src/debug/AndroidManifest.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
|
the Flutter tool needs it to communicate with the running application
|
||||||
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
-->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
</manifest>
|
||||||
45
example/android/app/src/main/AndroidManifest.xml
Normal file
45
example/android/app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<application
|
||||||
|
android:label="terra_example"
|
||||||
|
android:name="${applicationName}"
|
||||||
|
android:icon="@mipmap/ic_launcher">
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:launchMode="singleTop"
|
||||||
|
android:taskAffinity=""
|
||||||
|
android:theme="@style/LaunchTheme"
|
||||||
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
|
android:windowSoftInputMode="adjustResize">
|
||||||
|
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||||
|
the Android process has started. This theme is visible to the user
|
||||||
|
while the Flutter UI initializes. After that, this theme continues
|
||||||
|
to determine the Window background behind the Flutter UI. -->
|
||||||
|
<meta-data
|
||||||
|
android:name="io.flutter.embedding.android.NormalTheme"
|
||||||
|
android:resource="@style/NormalTheme"
|
||||||
|
/>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<!-- Don't delete the meta-data below.
|
||||||
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
|
<meta-data
|
||||||
|
android:name="flutterEmbedding"
|
||||||
|
android:value="2" />
|
||||||
|
</application>
|
||||||
|
<!-- Required to query activities that can process text, see:
|
||||||
|
https://developer.android.com/training/package-visibility and
|
||||||
|
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
|
||||||
|
|
||||||
|
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
|
||||||
|
<queries>
|
||||||
|
<intent>
|
||||||
|
<action android:name="android.intent.action.PROCESS_TEXT"/>
|
||||||
|
<data android:mimeType="text/plain"/>
|
||||||
|
</intent>
|
||||||
|
</queries>
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package com.example.terra_example;
|
||||||
|
|
||||||
|
import io.flutter.embedding.android.FlutterActivity;
|
||||||
|
|
||||||
|
public class MainActivity extends FlutterActivity {
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Modify this file to customize your launch splash screen -->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="?android:colorBackground" />
|
||||||
|
|
||||||
|
<!-- You can insert your own image assets here -->
|
||||||
|
<!-- <item>
|
||||||
|
<bitmap
|
||||||
|
android:gravity="center"
|
||||||
|
android:src="@mipmap/launch_image" />
|
||||||
|
</item> -->
|
||||||
|
</layer-list>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Modify this file to customize your launch splash screen -->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@android:color/white" />
|
||||||
|
|
||||||
|
<!-- You can insert your own image assets here -->
|
||||||
|
<!-- <item>
|
||||||
|
<bitmap
|
||||||
|
android:gravity="center"
|
||||||
|
android:src="@mipmap/launch_image" />
|
||||||
|
</item> -->
|
||||||
|
</layer-list>
|
||||||
BIN
example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
BIN
example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 544 B |
BIN
example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
BIN
example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 442 B |
BIN
example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
BIN
example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 721 B |
BIN
example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
BIN
example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
BIN
example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
BIN
example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
18
example/android/app/src/main/res/values-night/styles.xml
Normal file
18
example/android/app/src/main/res/values-night/styles.xml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||||
|
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
|
the Flutter engine draws its first frame -->
|
||||||
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
|
</style>
|
||||||
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
|
This theme determines the color of the Android Window while your
|
||||||
|
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||||
|
running.
|
||||||
|
|
||||||
|
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||||
|
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
18
example/android/app/src/main/res/values/styles.xml
Normal file
18
example/android/app/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||||
|
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
|
the Flutter engine draws its first frame -->
|
||||||
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
|
</style>
|
||||||
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
|
This theme determines the color of the Android Window while your
|
||||||
|
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||||
|
running.
|
||||||
|
|
||||||
|
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||||
|
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
7
example/android/app/src/profile/AndroidManifest.xml
Normal file
7
example/android/app/src/profile/AndroidManifest.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
|
the Flutter tool needs it to communicate with the running application
|
||||||
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
-->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
</manifest>
|
||||||
18
example/android/build.gradle
Normal file
18
example/android/build.gradle
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.buildDir = "../build"
|
||||||
|
subprojects {
|
||||||
|
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
||||||
|
}
|
||||||
|
subprojects {
|
||||||
|
project.evaluationDependsOn(":app")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("clean", Delete) {
|
||||||
|
delete rootProject.buildDir
|
||||||
|
}
|
||||||
3
example/android/gradle.properties
Normal file
3
example/android/gradle.properties
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError
|
||||||
|
android.useAndroidX=true
|
||||||
|
android.enableJetifier=true
|
||||||
5
example/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
example/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
|
||||||
25
example/android/settings.gradle
Normal file
25
example/android/settings.gradle
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
pluginManagement {
|
||||||
|
def flutterSdkPath = {
|
||||||
|
def properties = new Properties()
|
||||||
|
file("local.properties").withInputStream { properties.load(it) }
|
||||||
|
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||||
|
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||||
|
return flutterSdkPath
|
||||||
|
}()
|
||||||
|
|
||||||
|
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||||
|
id "com.android.application" version "7.3.0" apply false
|
||||||
|
id "org.jetbrains.kotlin.android" version "1.7.10" apply false
|
||||||
|
}
|
||||||
|
|
||||||
|
include ":app"
|
||||||
25
example/integration_test/plugin_integration_test.dart
Normal file
25
example/integration_test/plugin_integration_test.dart
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// This is a basic Flutter integration test.
|
||||||
|
//
|
||||||
|
// Since integration tests run in a full Flutter application, they can interact
|
||||||
|
// with the host side of a plugin implementation, unlike Dart unit tests.
|
||||||
|
//
|
||||||
|
// For more information about Flutter integration tests, please see
|
||||||
|
// https://docs.flutter.dev/cookbook/testing/integration/introduction
|
||||||
|
|
||||||
|
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:integration_test/integration_test.dart';
|
||||||
|
|
||||||
|
import 'package:terra/terra.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
testWidgets('getPlatformVersion test', (WidgetTester tester) async {
|
||||||
|
final Terra plugin = Terra();
|
||||||
|
final String? version = await plugin.getPlatformVersion();
|
||||||
|
// The version string depends on the host platform running the test, so
|
||||||
|
// just assert that some non-empty string is returned.
|
||||||
|
expect(version?.isNotEmpty, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
130
example/lib/main.dart
Normal file
130
example/lib/main.dart
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:terra/terra.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
runApp(const MyApp());
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyApp extends StatefulWidget {
|
||||||
|
const MyApp({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MyApp> createState() => _MyAppState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MyAppState extends State<MyApp> {
|
||||||
|
String _platformVersion = 'Unknown';
|
||||||
|
final _terraPlugin = Terra();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
initPlatformState();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Platform messages are asynchronous, so we initialize in an async method.
|
||||||
|
Future<void> initPlatformState() async {
|
||||||
|
String platformVersion;
|
||||||
|
// Platform messages may fail, so we use a try/catch PlatformException.
|
||||||
|
// We also handle the message potentially returning null.
|
||||||
|
try {
|
||||||
|
platformVersion =
|
||||||
|
await _terraPlugin.getPlatformVersion() ?? 'Unknown platform version';
|
||||||
|
} on PlatformException {
|
||||||
|
platformVersion = 'Failed to get platform version.';
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the widget was removed from the tree while the asynchronous platform
|
||||||
|
// message was in flight, we want to discard the reply rather than calling
|
||||||
|
// setState to update our non-existent appearance.
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_platformVersion = platformVersion;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Plugin example app'),
|
||||||
|
),
|
||||||
|
body: Center(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Text('Running on: $_platformVersion\n'),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
var deviceCount = await Terra.openAllDevices();
|
||||||
|
print("设备数量:$deviceCount");
|
||||||
|
},
|
||||||
|
child: const Text("连接设备")
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
var closeResult = await Terra.closeAllDevices();
|
||||||
|
print("关闭设备结果:$closeResult");
|
||||||
|
},
|
||||||
|
child: const Text("关闭所有设备")
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
var sn = await Terra.getDeviceSn();
|
||||||
|
print("设备SN:$sn");
|
||||||
|
},
|
||||||
|
child: const Text("获取设备SN")
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
var res = await Terra.setIntegrationTime(10.00);
|
||||||
|
print("设置积分时间结果:$res");
|
||||||
|
},
|
||||||
|
child: const Text("设置积分时间")
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
var res = await Terra.setAverageTimes(3);
|
||||||
|
print("设置平均次数结果:$res");
|
||||||
|
},
|
||||||
|
child: const Text("设置平均次数")
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
var res = await Terra.setTriggerMode(0);
|
||||||
|
print("设置触发模式:$res");
|
||||||
|
},
|
||||||
|
child: const Text("设置触发模式")
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
var res = await Terra.isConnect();
|
||||||
|
print("是否连接:$res");
|
||||||
|
},
|
||||||
|
child: const Text("是否连接")
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
var spectrum = await Terra.getRamanSpectrum();
|
||||||
|
print("光谱:$spectrum");
|
||||||
|
},
|
||||||
|
child: const Text("获取光谱(处理后的光谱)")
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
var registerRes = await Terra.ramanMatchRegister();
|
||||||
|
print("注册结果:$registerRes");
|
||||||
|
},
|
||||||
|
child: const Text("算法注册")
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
283
example/pubspec.lock
Normal file
283
example/pubspec.lock
Normal file
@@ -0,0 +1,283 @@
|
|||||||
|
# Generated by pub
|
||||||
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
|
packages:
|
||||||
|
async:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: async
|
||||||
|
sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.13.1"
|
||||||
|
boolean_selector:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: boolean_selector
|
||||||
|
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
|
characters:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: characters
|
||||||
|
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.1"
|
||||||
|
clock:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: clock
|
||||||
|
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.2"
|
||||||
|
collection:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: collection
|
||||||
|
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "1.19.1"
|
||||||
|
cupertino_icons:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: cupertino_icons
|
||||||
|
sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.9"
|
||||||
|
fake_async:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fake_async
|
||||||
|
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.3"
|
||||||
|
file:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file
|
||||||
|
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.1"
|
||||||
|
flutter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
flutter_driver:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
flutter_lints:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: flutter_lints
|
||||||
|
sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.2"
|
||||||
|
flutter_test:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
fuchsia_remote_debug_protocol:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
integration_test:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
leak_tracker:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: leak_tracker
|
||||||
|
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "11.0.2"
|
||||||
|
leak_tracker_flutter_testing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: leak_tracker_flutter_testing
|
||||||
|
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.10"
|
||||||
|
leak_tracker_testing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: leak_tracker_testing
|
||||||
|
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.2"
|
||||||
|
lints:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: lints
|
||||||
|
sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0"
|
||||||
|
matcher:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: matcher
|
||||||
|
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "0.12.19"
|
||||||
|
material_color_utilities:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: material_color_utilities
|
||||||
|
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "0.13.0"
|
||||||
|
meta:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: meta
|
||||||
|
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "1.17.0"
|
||||||
|
path:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path
|
||||||
|
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "1.9.1"
|
||||||
|
platform:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: platform
|
||||||
|
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.6"
|
||||||
|
plugin_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: plugin_platform_interface
|
||||||
|
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.8"
|
||||||
|
process:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: process
|
||||||
|
sha256: c6248e4526673988586e8c00bb22a49210c258dc91df5227d5da9748ecf79744
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.5"
|
||||||
|
sky_engine:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
source_span:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_span
|
||||||
|
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "1.10.2"
|
||||||
|
stack_trace:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stack_trace
|
||||||
|
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "1.12.1"
|
||||||
|
stream_channel:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stream_channel
|
||||||
|
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.4"
|
||||||
|
string_scanner:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: string_scanner
|
||||||
|
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.1"
|
||||||
|
sync_http:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sync_http
|
||||||
|
sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.1"
|
||||||
|
term_glyph:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: term_glyph
|
||||||
|
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.2"
|
||||||
|
terra:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: ".."
|
||||||
|
relative: true
|
||||||
|
source: path
|
||||||
|
version: "0.0.1"
|
||||||
|
test_api:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: test_api
|
||||||
|
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.10"
|
||||||
|
vector_math:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vector_math
|
||||||
|
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.0"
|
||||||
|
vm_service:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vm_service
|
||||||
|
sha256: "046d3928e16fa4dc46e8350415661755ab759d9fc97fc21b5ab295f71e4f0499"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "15.1.0"
|
||||||
|
webdriver:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: webdriver
|
||||||
|
sha256: "2f3a14ca026957870cfd9c635b83507e0e51d8091568e90129fbf805aba7cade"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.0"
|
||||||
|
sdks:
|
||||||
|
dart: ">=3.9.0 <4.0.0"
|
||||||
|
flutter: ">=3.18.0-18.0.pre.54"
|
||||||
85
example/pubspec.yaml
Normal file
85
example/pubspec.yaml
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
name: terra_example
|
||||||
|
description: "Demonstrates how to use the terra plugin."
|
||||||
|
# The following line prevents the package from being accidentally published to
|
||||||
|
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||||
|
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: '>=3.4.3 <4.0.0'
|
||||||
|
|
||||||
|
# Dependencies specify other packages that your package needs in order to work.
|
||||||
|
# To automatically upgrade your package dependencies to the latest versions
|
||||||
|
# consider running `flutter pub upgrade --major-versions`. Alternatively,
|
||||||
|
# dependencies can be manually updated by changing the version numbers below to
|
||||||
|
# the latest version available on pub.dev. To see which dependencies have newer
|
||||||
|
# versions available, run `flutter pub outdated`.
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
|
terra:
|
||||||
|
# When depending on this package from a real application you should use:
|
||||||
|
# terra: ^x.y.z
|
||||||
|
# See https://dart.dev/tools/pub/dependencies#version-constraints
|
||||||
|
# The example app is bundled with the plugin so we use a path dependency on
|
||||||
|
# the parent directory to use the current plugin's version.
|
||||||
|
path: ../
|
||||||
|
|
||||||
|
# The following adds the Cupertino Icons font to your application.
|
||||||
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
|
cupertino_icons: ^1.0.6
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
integration_test:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
|
# The "flutter_lints" package below contains a set of recommended lints to
|
||||||
|
# encourage good coding practices. The lint set provided by the package is
|
||||||
|
# activated in the `analysis_options.yaml` file located at the root of your
|
||||||
|
# package. See that file for information about deactivating specific lint
|
||||||
|
# rules and activating additional ones.
|
||||||
|
flutter_lints: ^3.0.0
|
||||||
|
|
||||||
|
# For information on the generic Dart part of this file, see the
|
||||||
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
||||||
|
# The following section is specific to Flutter packages.
|
||||||
|
flutter:
|
||||||
|
|
||||||
|
# The following line ensures that the Material Icons font is
|
||||||
|
# included with your application, so that you can use the icons in
|
||||||
|
# the material Icons class.
|
||||||
|
uses-material-design: true
|
||||||
|
|
||||||
|
# To add assets to your application, add an assets section, like this:
|
||||||
|
# assets:
|
||||||
|
# - images/a_dot_burr.jpeg
|
||||||
|
# - images/a_dot_ham.jpeg
|
||||||
|
|
||||||
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
|
# https://flutter.dev/assets-and-images/#resolution-aware
|
||||||
|
|
||||||
|
# For details regarding adding assets from package dependencies, see
|
||||||
|
# https://flutter.dev/assets-and-images/#from-packages
|
||||||
|
|
||||||
|
# To add custom fonts to your application, add a fonts section here,
|
||||||
|
# in this "flutter" section. Each entry in this list should have a
|
||||||
|
# "family" key with the font family name, and a "fonts" key with a
|
||||||
|
# list giving the asset and other descriptors for the font. For
|
||||||
|
# example:
|
||||||
|
# fonts:
|
||||||
|
# - family: Schyler
|
||||||
|
# fonts:
|
||||||
|
# - asset: fonts/Schyler-Regular.ttf
|
||||||
|
# - asset: fonts/Schyler-Italic.ttf
|
||||||
|
# style: italic
|
||||||
|
# - family: Trajan Pro
|
||||||
|
# fonts:
|
||||||
|
# - asset: fonts/TrajanPro.ttf
|
||||||
|
# - asset: fonts/TrajanPro_Bold.ttf
|
||||||
|
# weight: 700
|
||||||
|
#
|
||||||
|
# For details regarding fonts from package dependencies,
|
||||||
|
# see https://flutter.dev/custom-fonts/#from-packages
|
||||||
27
example/test/widget_test.dart
Normal file
27
example/test/widget_test.dart
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// This is a basic Flutter widget test.
|
||||||
|
//
|
||||||
|
// To perform an interaction with a widget in your test, use the WidgetTester
|
||||||
|
// utility in the flutter_test package. For example, you can send tap and scroll
|
||||||
|
// gestures. You can also use WidgetTester to find child widgets in the widget
|
||||||
|
// tree, read text, and verify that the values of widget properties are correct.
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
import 'package:terra_example/main.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets('Verify Platform version', (WidgetTester tester) async {
|
||||||
|
// Build our app and trigger a frame.
|
||||||
|
await tester.pumpWidget(const MyApp());
|
||||||
|
|
||||||
|
// Verify that platform version is retrieved.
|
||||||
|
expect(
|
||||||
|
find.byWidgetPredicate(
|
||||||
|
(Widget widget) => widget is Text &&
|
||||||
|
widget.data!.startsWith('Running on:'),
|
||||||
|
),
|
||||||
|
findsOneWidget,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
390
lib/terra.dart
Normal file
390
lib/terra.dart
Normal file
@@ -0,0 +1,390 @@
|
|||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
import 'terra_platform_interface.dart';
|
||||||
|
|
||||||
|
class Terra {
|
||||||
|
Future<String?> getPlatformVersion() {
|
||||||
|
return TerraPlatform.instance.getPlatformVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MethodChannel _channel = MethodChannel('terra');
|
||||||
|
|
||||||
|
// ==================== 设备管理 ====================
|
||||||
|
|
||||||
|
/// 打开所有设备
|
||||||
|
static Future<int> openAllDevices() async {
|
||||||
|
var deviceCount = await _channel.invokeMethod("openAllDevices");
|
||||||
|
return deviceCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 关闭所有设备
|
||||||
|
static Future<bool> closeAllDevices() async {
|
||||||
|
return await _channel.invokeMethod("closeAllDevices");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 关闭当前设备
|
||||||
|
static Future<bool> closeDevice() async {
|
||||||
|
return await _channel.invokeMethod("close");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取设备SN
|
||||||
|
static Future<String> getDeviceSn() async {
|
||||||
|
return await _channel.invokeMethod("getDeviceSn");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取设备索引
|
||||||
|
static Future<int> getDeviceIndex() async {
|
||||||
|
return await _channel.invokeMethod("getIndex");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设备是否连接成功
|
||||||
|
static Future<bool> isConnect() async {
|
||||||
|
return await _channel.invokeMethod("isConnect");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 开启 SDK 调试日志
|
||||||
|
static Future<bool> openDebug() async {
|
||||||
|
return await _channel.invokeMethod("openDebug");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 检查 USB 权限
|
||||||
|
static Future<Map<String, dynamic>> checkUsbPermission() async {
|
||||||
|
final result = await _channel.invokeMethod("checkUsbPermission");
|
||||||
|
return Map<String, dynamic>.from(result as Map);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 参数配置 ====================
|
||||||
|
|
||||||
|
/// 设置积分时间
|
||||||
|
static Future<bool> setIntegrationTime(double integrationTime) async {
|
||||||
|
Map<String, dynamic> args = {"integrationTime": integrationTime};
|
||||||
|
return await _channel.invokeMethod("setIntegrationTime", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置平均次数
|
||||||
|
static Future<bool> setAverageTimes(int averageTimes) async {
|
||||||
|
Map<String, dynamic> args = {"averageTimes": averageTimes};
|
||||||
|
return await _channel.invokeMethod("setAverageTimes", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置触发模式
|
||||||
|
/// 0: Free Running
|
||||||
|
/// 1: Hardware Level
|
||||||
|
/// 2: Synchronous
|
||||||
|
/// 3: Hardware Edge
|
||||||
|
/// 4: Edge And Free
|
||||||
|
static Future<bool> setTriggerMode(int triggerMode) async {
|
||||||
|
Map<String, dynamic> args = {"triggerMode": triggerMode};
|
||||||
|
return await _channel.invokeMethod("setTriggerMode", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 光谱数据获取 ====================
|
||||||
|
|
||||||
|
/// 获取光谱(获取当前光谱,可能拿到缓存光谱)
|
||||||
|
static Future<List<double>> getSpectrum() async {
|
||||||
|
return await _channel.invokeMethod("getSpectrum");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取光谱(获取光谱前会重新设置积分时间)
|
||||||
|
static Future<List<double>> getResetSpectrum() async {
|
||||||
|
return await _channel.invokeMethod("getResetSpectrum");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取波长
|
||||||
|
static Future<List<double>> getWavelength() async {
|
||||||
|
return await _channel.invokeMethod("getWavelength");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取波数
|
||||||
|
static Future<List<double>> getWaveNumber() async {
|
||||||
|
return await _channel.invokeMethod("getWaveNumber");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取处理完的光谱
|
||||||
|
static Future<List<double>> getRamanSpectrum() async {
|
||||||
|
List<double> spectrum = await _channel.invokeMethod("getRamanSpectrum");
|
||||||
|
return spectrum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取插值 X 轴数据(200~3200,步长 2)
|
||||||
|
static Future<List<double>> getXvalue() async {
|
||||||
|
return await _channel.invokeMethod("getXvalue");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== CCD 制冷控制 ====================
|
||||||
|
|
||||||
|
/// 开 CCD 制冷
|
||||||
|
static Future<bool> setCCDTECOn() async {
|
||||||
|
return await _channel.invokeMethod("setCCDTECOn");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 关 CCD 制冷
|
||||||
|
static Future<bool> setCCDTECOff() async {
|
||||||
|
return await _channel.invokeMethod("setCCDTECOff");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置 CCD 制冷目标温度(-20 ~ 40)
|
||||||
|
static Future<bool> setCCDTECTemperature(int temperature) async {
|
||||||
|
Map<String, dynamic> args = {"temperature": temperature};
|
||||||
|
return await _channel.invokeMethod("setCCDTECTemperature", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取 CCD 制冷状态(返回 16 字节状态数组)
|
||||||
|
static Future<List<int>> getCCDTECState() async {
|
||||||
|
final result = await _channel.invokeMethod("getCCDTECState");
|
||||||
|
return List<int>.from(result as List);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 灯泡/脉冲控制 ====================
|
||||||
|
|
||||||
|
/// 设置灯泡延迟时间(微秒)
|
||||||
|
static Future<bool> setLampDelayTime(double delayTime) async {
|
||||||
|
return await _channel.invokeMethod("setLampDelayTime", {"delayTime": delayTime});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置灯泡脉宽(微秒)
|
||||||
|
static Future<bool> setLampWidth(double width) async {
|
||||||
|
return await _channel.invokeMethod("setLampWidth", {"width": width});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置灯泡周期(微秒)
|
||||||
|
static Future<bool> setLampInterval(double interval) async {
|
||||||
|
return await _channel.invokeMethod("setLampInterval", {"interval": interval});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取灯泡延迟时间(微秒)
|
||||||
|
static Future<List<double>> getLampDelayTime() async {
|
||||||
|
final result = await _channel.invokeMethod("getLampDelayTime");
|
||||||
|
return List<double>.from(result as List);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取灯泡脉宽(微秒)
|
||||||
|
static Future<List<double>> getLampWidth() async {
|
||||||
|
final result = await _channel.invokeMethod("getLampWidth");
|
||||||
|
return List<double>.from(result as List);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取灯泡周期(微秒)
|
||||||
|
static Future<List<double>> getLampInterval() async {
|
||||||
|
final result = await _channel.invokeMethod("getLampInterval");
|
||||||
|
return List<double>.from(result as List);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 开关灯泡控制
|
||||||
|
static Future<bool> setLampEnable(bool enable) async {
|
||||||
|
return await _channel.invokeMethod("setLampEnable", {"enable": enable});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 激光控制 ====================
|
||||||
|
|
||||||
|
/// 设置激发波长
|
||||||
|
static Future<bool> setExcitedWaveLength(int excitedWaveLength) async {
|
||||||
|
return await _channel.invokeMethod("setExcitedWaveLength", {"excitedWaveLength": excitedWaveLength});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置激光功率(范围 0-2000)
|
||||||
|
static Future<bool> setLaserPower(double power) async {
|
||||||
|
if (power < 0 || power > 2000) {
|
||||||
|
print('[Terra] setLaserPower 参数超出范围 (0-2000): $power,拒绝执行');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Map<String, dynamic> args = {"power": power};
|
||||||
|
print('[Terra] setLaserPower 发送: power=$power');
|
||||||
|
return await _channel.invokeMethod("setLaserPower", args)
|
||||||
|
.timeout(const Duration(seconds: 5), onTimeout: () {
|
||||||
|
print('[Terra] setLaserPower 超时,设备可能未正确响应');
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 开激光
|
||||||
|
static Future<bool> setLaserOn() async {
|
||||||
|
return await _channel.invokeMethod("setLaserOn");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 关激光
|
||||||
|
static Future<bool> setLaserOff() async {
|
||||||
|
return await _channel.invokeMethod("setLaserOff");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 开激光器电源
|
||||||
|
static Future<bool> setLaserPowerOn() async {
|
||||||
|
return await _channel.invokeMethod("setLaserPowerOn");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 关激光器电源
|
||||||
|
static Future<bool> setLaserPowerOff() async {
|
||||||
|
return await _channel.invokeMethod("setLaserPowerOff");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 硬件开关 ====================
|
||||||
|
|
||||||
|
/// 开 TEC
|
||||||
|
static Future<bool> setTECOn() async {
|
||||||
|
return await _channel.invokeMethod("setTECOn");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 关 TEC
|
||||||
|
static Future<bool> setTECOff() async {
|
||||||
|
return await _channel.invokeMethod("setTECOff");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 开主板
|
||||||
|
static Future<bool> setMainBoardOn() async {
|
||||||
|
return await _channel.invokeMethod("setMainBoardOn");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 关主板
|
||||||
|
static Future<bool> setMainBoardOff() async {
|
||||||
|
return await _channel.invokeMethod("setMainBoardOff");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 主板是否已打开
|
||||||
|
static Future<bool> isMainBoardOpen() async {
|
||||||
|
return await _channel.invokeMethod("isMainBoardOpen");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TEC 是否已打开
|
||||||
|
static Future<bool> isTECOpen() async {
|
||||||
|
return await _channel.invokeMethod("isTECOpen");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 光谱数据处理 ====================
|
||||||
|
|
||||||
|
/// 小波平滑
|
||||||
|
static Future<List<double>> waveletSmooth(List<double> spectrum) async {
|
||||||
|
return await _channel.invokeMethod("waveletSmooth", {"spectrum": spectrum});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 消荧光(side 为消荧光因子,默认 15)
|
||||||
|
static Future<List<double>> removeFluorescence(List<double> spectrum, {int side = 15}) async {
|
||||||
|
return await _channel.invokeMethod("removeFluorescence", {"spectrum": spectrum, "side": side});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 线性插值
|
||||||
|
static Future<List<double>> lineInterpolation(
|
||||||
|
List<double> xDataRaw,
|
||||||
|
List<double> yDataRaw,
|
||||||
|
List<double> xData,
|
||||||
|
) async {
|
||||||
|
return await _channel.invokeMethod("lineInterpolation", {
|
||||||
|
"xDataRaw": xDataRaw,
|
||||||
|
"yDataRaw": yDataRaw,
|
||||||
|
"xData": xData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 寻峰
|
||||||
|
static Future<List<int>> findPeaks(
|
||||||
|
List<double> spectrum,
|
||||||
|
int minIndicesBetweenPeaks,
|
||||||
|
double baseline,
|
||||||
|
) async {
|
||||||
|
Map<String, dynamic> args = {
|
||||||
|
"spectrum": spectrum,
|
||||||
|
"minIndicesBetweenPeaks": minIndicesBetweenPeaks,
|
||||||
|
"baseline": baseline,
|
||||||
|
};
|
||||||
|
return await _channel.invokeMethod("findPeaks", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 校准系数 ====================
|
||||||
|
|
||||||
|
/// 获取激光校正系数
|
||||||
|
static Future<List<double>> getLaserPowerCorrectCoefficient() async {
|
||||||
|
return await _channel.invokeMethod("getLaserPowerCorrectCoefficient");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置激光校正系数
|
||||||
|
/// calibrationCoefficient 格式:16 位字符串,4 个数据,每个数据 4 位
|
||||||
|
/// 如 "0070030008001400"
|
||||||
|
static Future<bool> setLaserPowerCorrectCoefficient(String calibrationCoefficient) async {
|
||||||
|
Map<String, String> args = {"calibrationCoefficient": calibrationCoefficient};
|
||||||
|
return await _channel.invokeMethod("setLaserPowerCorrectCoefficient", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取乙腈校准数据
|
||||||
|
static Future<List<double>> getYJCorrectCoefficient() async {
|
||||||
|
return await _channel.invokeMethod("getYJCorrectCoefficient");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置乙腈定标系数
|
||||||
|
static Future<bool> setYJCorrectCoefficient(List<double> coffes) async {
|
||||||
|
Map<String, List<double>> args = {"coffes": coffes};
|
||||||
|
return await _channel.invokeMethod("setYJCorrectCoefficient", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取 Y 轴校准系数
|
||||||
|
static Future<List<double>> getYAxisCorrectCoefficient() async {
|
||||||
|
final result = await _channel.invokeMethod("getYAxisCorrectCoefficient");
|
||||||
|
return List<double>.from(result as List);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取探测器非线性校正系数
|
||||||
|
static Future<List<double>> getCorrectForDetectorNonlinear() async {
|
||||||
|
final result = await _channel.invokeMethod("getCorrectForDetectorNonlinear");
|
||||||
|
return List<double>.from(result as List);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取波长校准系数
|
||||||
|
static Future<List<double>> getWavelengthCalibrationCoefficients() async {
|
||||||
|
final result = await _channel.invokeMethod("getWavelengthCalibrationCoefficients");
|
||||||
|
return List<double>.from(result as List);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取 FPGA 版本
|
||||||
|
static Future<List<double>> getFpgaVersion() async {
|
||||||
|
final result = await _channel.invokeMethod("getFpgaVersion");
|
||||||
|
return List<double>.from(result as List);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取狭缝大小
|
||||||
|
static Future<String> getSlit() async {
|
||||||
|
return await _channel.invokeMethod("getSlit");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取坏点
|
||||||
|
static Future<List<int>> getBadPoints() async {
|
||||||
|
final result = await _channel.invokeMethod("getBadPoints");
|
||||||
|
return List<int>.from(result as List);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 算法匹配与校准 ====================
|
||||||
|
|
||||||
|
/// 算法注册
|
||||||
|
static Future<bool> ramanMatchRegister() async {
|
||||||
|
return await _channel.invokeMethod("ramanMatchRegister");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 光谱匹配,返回相似度
|
||||||
|
static Future<double> ramanMatchCalcSimilarity(
|
||||||
|
List<double> spectrum,
|
||||||
|
List<double> libSpectrumList,
|
||||||
|
) async {
|
||||||
|
Map<String, List<double>> args = {
|
||||||
|
"spectrum": spectrum,
|
||||||
|
"libSpectrumList": libSpectrumList,
|
||||||
|
};
|
||||||
|
return await _channel.invokeMethod("ramanMatchCalcSimilarity", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 校准
|
||||||
|
static Future<List<double>> calibration(
|
||||||
|
List<double> spectrum,
|
||||||
|
List<double> YJCoeff,
|
||||||
|
) async {
|
||||||
|
Map<String, List<double>> args = {
|
||||||
|
"spectrum": spectrum,
|
||||||
|
"YJCoeff": YJCoeff,
|
||||||
|
};
|
||||||
|
return await _channel.invokeMethod("calibration", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 其他 ====================
|
||||||
|
|
||||||
|
/// SN 码 MD5 加密
|
||||||
|
static Future<String> bytesToMD5(String sn) async {
|
||||||
|
Map<String, String> args = {"sn": sn};
|
||||||
|
return await _channel.invokeMethod("bytesToMD5", args);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
lib/terra_method_channel.dart
Normal file
17
lib/terra_method_channel.dart
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
import 'terra_platform_interface.dart';
|
||||||
|
|
||||||
|
/// An implementation of [TerraPlatform] that uses method channels.
|
||||||
|
class MethodChannelTerra extends TerraPlatform {
|
||||||
|
/// The method channel used to interact with the native platform.
|
||||||
|
@visibleForTesting
|
||||||
|
final methodChannel = const MethodChannel('terra');
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String?> getPlatformVersion() async {
|
||||||
|
final version = await methodChannel.invokeMethod<String>('getPlatformVersion');
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
}
|
||||||
29
lib/terra_platform_interface.dart
Normal file
29
lib/terra_platform_interface.dart
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||||
|
|
||||||
|
import 'terra_method_channel.dart';
|
||||||
|
|
||||||
|
abstract class TerraPlatform extends PlatformInterface {
|
||||||
|
/// Constructs a TerraPlatform.
|
||||||
|
TerraPlatform() : super(token: _token);
|
||||||
|
|
||||||
|
static final Object _token = Object();
|
||||||
|
|
||||||
|
static TerraPlatform _instance = MethodChannelTerra();
|
||||||
|
|
||||||
|
/// The default instance of [TerraPlatform] to use.
|
||||||
|
///
|
||||||
|
/// Defaults to [MethodChannelTerra].
|
||||||
|
static TerraPlatform get instance => _instance;
|
||||||
|
|
||||||
|
/// Platform-specific implementations should set this with their own
|
||||||
|
/// platform-specific class that extends [TerraPlatform] when
|
||||||
|
/// they register themselves.
|
||||||
|
static set instance(TerraPlatform instance) {
|
||||||
|
PlatformInterface.verifyToken(instance, _token);
|
||||||
|
_instance = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String?> getPlatformVersion() {
|
||||||
|
throw UnimplementedError('platformVersion() has not been implemented.');
|
||||||
|
}
|
||||||
|
}
|
||||||
70
pubspec.yaml
Normal file
70
pubspec.yaml
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
name: terra
|
||||||
|
description: "A new Flutter project."
|
||||||
|
version: 0.0.1
|
||||||
|
homepage:
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: '>=3.4.3 <4.0.0'
|
||||||
|
flutter: '>=3.3.0'
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
plugin_platform_interface: ^2.0.2
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_lints: ^3.0.0
|
||||||
|
|
||||||
|
# For information on the generic Dart part of this file, see the
|
||||||
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
||||||
|
# The following section is specific to Flutter packages.
|
||||||
|
flutter:
|
||||||
|
# This section identifies this Flutter project as a plugin project.
|
||||||
|
# The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.)
|
||||||
|
# which should be registered in the plugin registry. This is required for
|
||||||
|
# using method channels.
|
||||||
|
# The Android 'package' specifies package in which the registered class is.
|
||||||
|
# This is required for using method channels on Android.
|
||||||
|
# The 'ffiPlugin' specifies that native code should be built and bundled.
|
||||||
|
# This is required for using `dart:ffi`.
|
||||||
|
# All these are used by the tooling to maintain consistency when
|
||||||
|
# adding or updating assets for this project.
|
||||||
|
plugin:
|
||||||
|
platforms:
|
||||||
|
android:
|
||||||
|
package: com.example.terra
|
||||||
|
pluginClass: TerraPlugin
|
||||||
|
|
||||||
|
# To add assets to your plugin package, add an assets section, like this:
|
||||||
|
# assets:
|
||||||
|
# - images/a_dot_burr.jpeg
|
||||||
|
# - images/a_dot_ham.jpeg
|
||||||
|
#
|
||||||
|
# For details regarding assets in packages, see
|
||||||
|
# https://flutter.dev/assets-and-images/#from-packages
|
||||||
|
#
|
||||||
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
|
# https://flutter.dev/assets-and-images/#resolution-aware
|
||||||
|
|
||||||
|
# To add custom fonts to your plugin package, add a fonts section here,
|
||||||
|
# in this "flutter" section. Each entry in this list should have a
|
||||||
|
# "family" key with the font family name, and a "fonts" key with a
|
||||||
|
# list giving the asset and other descriptors for the font. For
|
||||||
|
# example:
|
||||||
|
# fonts:
|
||||||
|
# - family: Schyler
|
||||||
|
# fonts:
|
||||||
|
# - asset: fonts/Schyler-Regular.ttf
|
||||||
|
# - asset: fonts/Schyler-Italic.ttf
|
||||||
|
# style: italic
|
||||||
|
# - family: Trajan Pro
|
||||||
|
# fonts:
|
||||||
|
# - asset: fonts/TrajanPro.ttf
|
||||||
|
# - asset: fonts/TrajanPro_Bold.ttf
|
||||||
|
# weight: 700
|
||||||
|
#
|
||||||
|
# For details regarding fonts in packages, see
|
||||||
|
# https://flutter.dev/custom-fonts/#from-packages
|
||||||
27
test/terra_method_channel_test.dart
Normal file
27
test/terra_method_channel_test.dart
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:terra/terra_method_channel.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
MethodChannelTerra platform = MethodChannelTerra();
|
||||||
|
const MethodChannel channel = MethodChannel('terra');
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
|
||||||
|
channel,
|
||||||
|
(MethodCall methodCall) async {
|
||||||
|
return '42';
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() {
|
||||||
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getPlatformVersion', () async {
|
||||||
|
expect(await platform.getPlatformVersion(), '42');
|
||||||
|
});
|
||||||
|
}
|
||||||
29
test/terra_test.dart
Normal file
29
test/terra_test.dart
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:terra/terra.dart';
|
||||||
|
import 'package:terra/terra_platform_interface.dart';
|
||||||
|
import 'package:terra/terra_method_channel.dart';
|
||||||
|
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||||
|
|
||||||
|
class MockTerraPlatform
|
||||||
|
with MockPlatformInterfaceMixin
|
||||||
|
implements TerraPlatform {
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String?> getPlatformVersion() => Future.value('42');
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
final TerraPlatform initialPlatform = TerraPlatform.instance;
|
||||||
|
|
||||||
|
test('$MethodChannelTerra is the default instance', () {
|
||||||
|
expect(initialPlatform, isInstanceOf<MethodChannelTerra>());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getPlatformVersion', () async {
|
||||||
|
Terra terraPlugin = Terra();
|
||||||
|
MockTerraPlatform fakePlatform = MockTerraPlatform();
|
||||||
|
TerraPlatform.instance = fakePlatform;
|
||||||
|
|
||||||
|
expect(await terraPlugin.getPlatformVersion(), '42');
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user