Commit 43c3bc9e authored by 姜天宇's avatar 姜天宇

feat(v1.0.2): 增加识别记录上传

parent 4a984f16
package com.wmdigit.common.model;
import java.io.Serializable;
import java.util.List;
/**
* 识别结果DTO
* @author dizi
*/
public class IdentifyRecordDTO implements Serializable {
private int[][] rectArray;
private String[] productNames;
private String[] productCodes;
public IdentifyRecordDTO() {
}
public IdentifyRecordDTO(int[][] rectArray, String[] productNames, String[] productCodes) {
this.rectArray = rectArray;
this.productNames = productNames;
this.productCodes = productCodes;
}
public int[][] getRectArray() {
return rectArray;
}
public void setRectArray(int[][] rectArray) {
this.rectArray = rectArray;
}
public String[] getProductNames() {
return productNames;
}
public void setProductNames(String[] productNames) {
this.productNames = productNames;
}
public String[] getProductCodes() {
return productCodes;
}
public void setProductCodes(String[] productCodes) {
this.productCodes = productCodes;
}
}
......@@ -149,6 +149,7 @@ public class BitmapUtils {
public static void saveBitmap(Bitmap bitmap, String path) {
try {
File file = new File(path);
file.getParentFile().mkdirs();
FileOutputStream fos = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.flush();
......
......@@ -1165,6 +1165,9 @@ public class CropView extends AppCompatImageView {
if (presetCropCoordinate == null){
return;
}
if (mImageRectF == null){
return;
}
int l = presetCropCoordinate.left;
int t = presetCropCoordinate.top;
int w = presetCropCoordinate.right - l;
......
......@@ -56,7 +56,7 @@ set_target_properties(
${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libret.so
)
# 分类、特征提取库
# 菜品分类、特征提取库
add_library(clsretri SHARED IMPORTED)
set_target_properties(
clsretri
......
......@@ -28,7 +28,7 @@ Java_com_wmdigit_core_catering_dish_DishDetection_process(JNIEnv *env, jobject t
LOGD("准备推理菜品");
// 推理
int ret = DETFEA_Process(input, &output, handle);
LOGD("推理结果:%d", ret);
LOGD("推理结果:%x", ret);
if (ret != 0){
mat.release();
mat_bgr.release();
......@@ -40,7 +40,7 @@ Java_com_wmdigit_core_catering_dish_DishDetection_process(JNIEnv *env, jobject t
int cols = 4;
// 统计符合阈值的框数量
for (size_t i = 0; i < output.output_list.size(); ++i) {
if (output.output_list[i].prob >= 0.7){
if (output.output_list[i].prob >= 0.8){
rows ++;
}
}
......@@ -59,7 +59,7 @@ Java_com_wmdigit_core_catering_dish_DishDetection_process(JNIEnv *env, jobject t
// 遍历识别结果
for (size_t i = 0; i < output.output_list.size(); ++i) {
// 根据阈值,筛选出有效的框
if (output.output_list[i].prob >= 0.75){
if (output.output_list[i].prob >= 0.8){
// 记录框的左上、右下点坐标
rect_array[i][0] = (int)output.output_list[i].x1;
rect_array[i][1] = (int)output.output_list[i].y1;
......
......@@ -5,9 +5,10 @@
*/
extern "C"
JNIEXPORT jint JNICALL
Java_com_wmdigit_core_hnsw_Hnsw_init(JNIEnv *env, jobject thiz) {
Java_com_wmdigit_core_hnsw_Hnsw_init(JNIEnv *env, jobject thiz, jint dimension) {
std::vector<std::pair<long, std::string>>().swap(vector);
idx = new libhnsw::Index(FEATURES_DIMENSION, FEATURES_TOTAL, mode);
idx = new libhnsw::Index(dimension, FEATURES_TOTAL, mode);
output_dimension = dimension;
LOGI("索引库初始化完成");
return 0;
}
......@@ -96,7 +97,7 @@ Java_com_wmdigit_core_hnsw_Hnsw_retrieveMostSimilarResult(JNIEnv *env, jobject t
libhnsw::RequireSample requireSample;
idx->Selectsample(requireSample, label);
// 计算相似度
float similarity = calculateSimilar(array, requireSample.data, FEATURES_DIMENSION);
float similarity = calculateSimilar(array, requireSample.data, output_dimension);
if (similarity >= threshold){
// 相似度大于设定阈值,在vector中检索出对应的code
for (auto it = vector.begin(); it != vector.end(); it++) {
......
......@@ -16,8 +16,6 @@
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG, __VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,LOG_TAG, __VA_ARGS__)
// 特征维度
#define FEATURES_DIMENSION 160
// 特征总数
#define FEATURES_TOTAL 50000
// 索引
......@@ -33,6 +31,8 @@ libhnsw::SPACE_TYPE st = libhnsw::L2_DISTANCE;
// second->商品code
std::vector<std::pair<long, std::string>> vector;
int output_dimension = 128;
/**
* 计算相似度
* @param v1
......
......@@ -36,7 +36,7 @@ public class CoreModule {
// 初始化视频流
VideoPipeRepository.getInstance().initVideoPipe();
// 初始化索引库
HnswRepository.getInstance().init();
HnswRepository.getInstance().init(TargetDetectionRepository.getInstance().getDimension());
}
public static Context getAppContext() {
......
......@@ -33,4 +33,9 @@ public interface TargetDetection {
*/
int getAiMode();
/**
* 获取输出维度
* @return
*/
int getDimension();
}
......@@ -95,4 +95,12 @@ public class TargetDetectionRepository {
public void close(){
targetDetection.close();
}
/**
* 获取向量维度
* @return
*/
public int getDimension(){
return targetDetection.getDimension();
}
}
......@@ -16,6 +16,11 @@ public class DishDetection implements TargetDetection {
System.loadLibrary("catering_dish_detection");
}
/**
* 输出维度
*/
private final int OUTPUT_DIMENSION = 160;
/**
* JNI初始化目标检测算法
* @return
......@@ -76,4 +81,9 @@ public class DishDetection implements TargetDetection {
return TargetDetectionRepository.AI_MODE_DISH_DETECTION;
}
@Override
public int getDimension() {
return OUTPUT_DIMENSION;
}
}
......@@ -16,6 +16,11 @@ public class PlateDetection implements TargetDetection {
System.loadLibrary("catering_plate_detection");
}
/**
* 算法输出维度
*/
private final int OUTPUT_DIMENSION = 160;
/**
* JNI初始化目标检测算法
* @return
......@@ -80,4 +85,9 @@ public class PlateDetection implements TargetDetection {
public int getAiMode() {
return TargetDetectionRepository.AI_MODE_PLATE_DETECTION;
}
@Override
public int getDimension() {
return OUTPUT_DIMENSION;
}
}
......@@ -14,9 +14,10 @@ public class Hnsw {
/**
* 初始化方法
* @param dimension
* @return
*/
private native int init();
private native int init(int dimension);
/**
* 写入索引
......@@ -45,9 +46,9 @@ public class Hnsw {
*/
private final Object syncLock = new Object();
public void initHnsw(){
public void initHnsw(int dimension){
synchronized (syncLock){
int ret = init();
int ret = init(dimension);
XLog.i("hnsw初始化结果:" + ret);
}
}
......
......@@ -3,6 +3,7 @@ package com.wmdigit.core.hnsw;
import android.text.TextUtils;
import com.elvishew.xlog.XLog;
import com.wmdigit.core.catering.TargetDetectionRepository;
import com.wmdigit.data.database.entity.FeaturesPO;
import com.wmdigit.data.database.entity.ProductsPO;
import com.wmdigit.data.database.repository.FeaturesRepository;
......@@ -44,6 +45,8 @@ public class HnswRepository {
private boolean initComplete = false;
private OnHnswInitListener listener;
private int dimension = 128;
public HnswRepository() {
hnsw = new Hnsw();
compositeDisposable = new CompositeDisposable();
......@@ -52,9 +55,10 @@ public class HnswRepository {
/**
* 初始化
*/
public void init(){
public void init(int dimension){
this.dimension = dimension;
initComplete = false;
hnsw.initHnsw();
hnsw.initHnsw(dimension);
Disposable disposable = Observable.create(emitter -> {
// 遍历特征库
queryAndWriteFeaturesIntoHnsw();
......@@ -125,6 +129,7 @@ public class HnswRepository {
/**
* 查询并写入特征
* @param dimension
*/
private void queryAndWriteFeaturesIntoHnsw(){
int page = 1;
......@@ -133,10 +138,10 @@ public class HnswRepository {
List<FeaturesPO> list = FeaturesRepository.getInstance().getFeaturesByPage(page, pageSize);
for (FeaturesPO featuresPO : list){
String[] featureStrArray = featuresPO.getFeature().split(",");
if (featureStrArray.length != 160){
if (featureStrArray.length != dimension){
continue;
}
float[] feature = new float[160];
float[] feature = new float[dimension];
for (int i = 0; i < feature.length; i++){
feature[i] = Float.parseFloat(featureStrArray[i].trim().replace("[", "").replace("]", ""));
}
......@@ -158,7 +163,7 @@ public class HnswRepository {
}
close();
hnsw.deleteAll();
hnsw.initHnsw();
hnsw.initHnsw(dimension);
}
public void setListener(OnHnswInitListener listener) {
......
package com.wmdigit.data.disk.repository;
import android.content.Context;
import android.graphics.Bitmap;
import android.text.TextUtils;
import com.wmdigit.common.utils.BitmapUtils;
import com.wmdigit.data.LocalDataModule;
import com.wmdigit.data.database.entity.ProductsPO;
import java.io.File;
import java.util.List;
import java.util.UUID;
public class DiskRepository {
private static DiskRepository instance;
public static DiskRepository getInstance(){
if (instance == null){
synchronized (DiskRepository.class){
if (instance == null){
instance = new DiskRepository();
}
}
}
return instance;
}
private DiskRepository(){}
/**
* 保存识别记录到磁盘
* @param bitmap
* @param rectArray
* @param products
* @return 返回原图的路径
*/
public String saveIdentifyRecordToDisk(Bitmap bitmap, int[][] rectArray, List<ProductsPO> products){
Context context = LocalDataModule.getAppContext();
// 识别记录根目录
String identifyRecordsRootPath = context.getExternalFilesDir("IdentifyRecords").getAbsolutePath();
// 商品的学习记录根目录
String productLearningRecordsRootPath = context.getExternalFilesDir("ProductLearningRecords").getAbsolutePath();
String imageFilename = UUID.randomUUID().toString() + ".jpg";
// 保存图片
BitmapUtils.saveBitmap(bitmap, identifyRecordsRootPath + "/" + imageFilename);
// 根据框的坐标,拆分原图
for (int i = 0; i < rectArray.length; i++) {
if (products.get(i) == null || TextUtils.isEmpty(products.get(i).getProductCode())){
continue;
}
Bitmap bitmapTemp = Bitmap.createBitmap(bitmap, rectArray[i][0], rectArray[i][1], rectArray[i][2] - rectArray[i][0], rectArray[i][3] - rectArray[i][1]);
// 保存裁出来的图片
BitmapUtils.saveBitmap(bitmapTemp, productLearningRecordsRootPath + "/" + products.get(i).getProductCode() + "/" + imageFilename.replace(".jpg", "") + "_" + i + ".jpg");
bitmapTemp.recycle();
}
return identifyRecordsRootPath + "/" + imageFilename;
}
}
......@@ -107,4 +107,12 @@ public class UserLocalRepository {
return result;
}
/**
* 获取PosId
* @return
*/
public String getPosId(){
return MMKV.defaultMMKV().getString(MmkvCons.MMKV_KEY_DEVICE_ID, "");
}
}
package com.wmdigit.network.bean.request;
import java.io.Serializable;
/**
* 餐饮识别记录
* @author dizi
*/
public class CateringIdentifyRecord implements Serializable {
/**
* POS机ID,该值为后台PosMachine的主键
*/
private String posCode;
/**
* 框图URL
*/
private String frameImageUrl;
/**
* 原图URL
*/
private String imageUrl;
/**
* 裁剪坐标JSON
*/
private String smallImageSite;
/**
* 识别时间
* yyyy-MM-dd HH:mm:ss
*/
private String identifyTime;
public CateringIdentifyRecord() {
}
public CateringIdentifyRecord(String posCode, String frameImageUrl, String imageUrl, String smallImageSite, String identifyTime) {
this.posCode = posCode;
this.frameImageUrl = frameImageUrl;
this.imageUrl = imageUrl;
this.smallImageSite = smallImageSite;
this.identifyTime = identifyTime;
}
public String getPosCode() {
return posCode;
}
public void setPosCode(String posCode) {
this.posCode = posCode;
}
public String getFrameImageUrl() {
return frameImageUrl;
}
public void setFrameImageUrl(String frameImageUrl) {
this.frameImageUrl = frameImageUrl;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public String getSmallImageSite() {
return smallImageSite;
}
public void setSmallImageSite(String smallImageSite) {
this.smallImageSite = smallImageSite;
}
public String getIdentifyTime() {
return identifyTime;
}
public void setIdentifyTime(String identifyTime) {
this.identifyTime = identifyTime;
}
}
package com.wmdigit.network.repository;
import com.elvishew.xlog.XLog;
import com.wmdigit.common.utils.DateUtils;
import com.wmdigit.data.mmkv.repository.UserLocalRepository;
import com.wmdigit.network.bean.request.CateringIdentifyRecord;
import com.wmdigit.network.bean.response.BasePosResponse;
import com.wmdigit.network.factory.ServiceFactory;
import com.wmdigit.network.oss.OSSManager;
import com.wmdigit.network.utils.RxHelper;
import java.io.File;
import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
public class IdentifyRecordRepository {
private static IdentifyRecordRepository instance;
public static IdentifyRecordRepository getInstance(){
if (instance == null){
synchronized (IdentifyRecordRepository.class){
if (instance == null){
instance = new IdentifyRecordRepository();
}
}
}
return instance;
}
private IdentifyRecordRepository(){
}
/**
* 上传识别记录
* 此方法用于上传用户的识别记录到服务器,包括图片的同步上传和识别记录的异步处理
*
* @param json 识别记录的JSON字符串,包含识别相关信息
* @param imagePath 待上传图片的路径
* @return 返回一个Disposable对象,用于取消订阅
*/
public Disposable uploadIdentifyRecord(String json, String imagePath){
return Observable.create(emitter -> {
// 同步上传图片到对象存储服务
String url = OSSManager.getInstance().syncUploadFileToOSS(new File(imagePath));
// 向观察者发射上传后的图片URL
emitter.onNext(url);
}).flatMap(url ->{
// 创建一个餐饮识别记录对象
CateringIdentifyRecord record = new CateringIdentifyRecord();
// 设置POS机编号
record.setPosCode(UserLocalRepository.getInstance().getPosId());
// 设置识别时间
record.setIdentifyTime(DateUtils.getTodayTime());
// 设置图片URL
record.setImageUrl((String) url);
// 设置帧图片URL
record.setFrameImageUrl((String) url);
// 设置小图片站点信息
record.setSmallImageSite(json);
// 异步上传餐饮识别记录到服务器
return ServiceFactory.getServiceFactory().getPosService()
.uploadCateringIdentifyRecord(record)
.compose(RxHelper.handlePosResult());
}).subscribe(aLong -> {
// 日志记录上传成功
XLog.i("上传成功");
}, throwable -> {
XLog.e(throwable.toString());
});
}
}
package com.wmdigit.network.service;
import com.wmdigit.network.bean.request.CateringIdentifyRecord;
import com.wmdigit.network.bean.request.ProductIdentifyRecord;
import com.wmdigit.network.bean.request.QueryCommandParam;
import com.wmdigit.network.bean.request.QueryLatestAppVersionParam;
......@@ -94,4 +95,12 @@ public interface PosService {
@GET("newretail/api/sys/app/version/getLatestWithType")
Observable<BasePosResponse<AppVersionDTO>> getLatestRemoteToolWithType(@Query("type") String type);
/**
* 上传餐饮识别记录
* @param record
* @return
*/
@POST("newretail/api/search/cateringIdentifyRecord/saveCateringIdentifyRecord")
Observable<BasePosResponse<Long>> uploadCateringIdentifyRecord(@Body CateringIdentifyRecord record);
}
......@@ -9,4 +9,6 @@ v1.0.2 2024/08/06 1.增加系统信息页
6.增加学习页
7.增加AIDL服务
8.集成标框、菜品识别、餐盘识别算法
9.集成索引库算法(索引库版本较老,可能存在最后一条索引删除不掉的BUG)
\ No newline at end of file
9.集成索引库算法(索引库版本较老,可能存在最后一条索引删除不掉的BUG)
10.todo 菜品算法更新128模型
11.增加数据上传
\ No newline at end of file
......@@ -8,10 +8,12 @@ import androidx.annotation.NonNull;
import androidx.databinding.ObservableField;
import androidx.lifecycle.MutableLiveData;
import com.google.gson.Gson;
import com.wmdigit.camera.listener.OnImageAnalyzeListener;
import com.wmdigit.common.base.mvvm.BaseViewModel;
import com.wmdigit.common.base.mvvm.SingleLiveEvent;
import com.wmdigit.common.model.CropValueDTO;
import com.wmdigit.common.model.IdentifyRecordDTO;
import com.wmdigit.common.model.ProductsVO;
import com.wmdigit.common.utils.ParcelHelper;
import com.wmdigit.core.catering.TargetDetectionRepository;
......@@ -23,7 +25,10 @@ import com.wmdigit.data.database.entity.ProductsPO;
import com.wmdigit.data.database.mapper.ProductsMapper;
import com.wmdigit.data.database.repository.FeaturesRepository;
import com.wmdigit.data.database.repository.ProductsRepository;
import com.wmdigit.data.disk.repository.DiskRepository;
import com.wmdigit.data.mmkv.repository.CropLocalRepository;
import com.wmdigit.network.oss.OSSManager;
import com.wmdigit.network.repository.IdentifyRecordRepository;
import java.util.ArrayList;
import java.util.List;
......@@ -32,6 +37,10 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
/**
* 学习菜品的ViewModel
* @author dizi
......@@ -395,7 +404,11 @@ public class DataLearningViewModel extends BaseViewModel {
if (detectResult == null || detectResult.getProducts() == null){
return;
}
String[] productNames = new String[detectResult.getProducts().size()];
String[] productCodes = new String[detectResult.getProducts().size()];
for (int i = 0; i < detectResult.getProducts().size(); i++){
productNames[i] = "";
productCodes[i] = "";
if (TextUtils.isEmpty(detectResult.getProducts().get(i).getProductCode())){
continue;
}
......@@ -413,11 +426,19 @@ public class DataLearningViewModel extends BaseViewModel {
if (id != -1) {
HnswRepository.getInstance().writeFeatureIntoHnsw(id, detectResult.getProducts().get(i).getProductCode(), detectResult.getFeatures()[i]);
}
productNames[i] = detectResult.getProducts().get(i).getProductName();
productCodes[i] = detectResult.getProducts().get(i).getProductCode();
}
String imagePath = DiskRepository.getInstance().saveIdentifyRecordToDisk(detectResult.getBitmap(), detectResult.getRectArray(), detectResult.getProducts());
toastMessage.postValue(getApplication().getString(com.wmdigit.common.R.string.save_success));
// 异步上传后台
IdentifyRecordDTO identifyRecordDTO = new IdentifyRecordDTO(detectResult.getRectArray(), productNames, productCodes);
String json = new Gson().toJson(identifyRecordDTO);
compositeDisposable.add(IdentifyRecordRepository.getInstance().uploadIdentifyRecord(json, imagePath));
}
}
public OnImageAnalyzeListener getOnImageAnalyzeListener() {
return onImageAnalyzeListener;
}
......
# 元芒餐饮服务(WmCateringService)
## 安卓SDK接口说明文档
![image-20210604131103047](http://ai-wmdigit.oss-cn-shanghai.aliyuncs.com/ai-wmdigit/1296269352764444674/20210607/1401716255961911298/image.png)
##### Version Release v1.0.2
##### 更新时间: 2024.09.24
#### 上海元芒数字科技有限公司
<div style="page-break-after: always;"></div>
[TOC]
<div style="page-break-after: always;"></div>
# Android端SDK接口文档
# 一、 文档说明
## 1.1 版本说明
| SDK版本号 | 发布日期 | 更新内容 |
| --------- | ---------- | ---------------------------------------- |
| 1.0.0 | 2024.07.18 | 1.餐饮识别效果演示版本 |
| 1.0.1 | 2024.08.01 | 1.算法模块集成<br>2.相机模块集成 |
| 1.0.2 | 2024.09.24 | 1.发版版本 |
## 1.2 文档概述
此文档用于说明元芒数字餐饮服务(WmCateringService)Android系统接
口开发使用,集成相关软件包,以实现相关功能的接口说明文档。
## 1.3 术语定义以及说明
1. 术语说明
+ WmCateringService:元芒餐饮服务
2. 本文中所有传输内容均使用UTF- 8编码;
3. 本文SDK适用于Android 11及以上,主板要求RK3568(带有NPU模块),使用JAVA 1.8 版本;
4. 本文中的服务程序支持armeabi-v7a、arm64-v8a架构。
<div style="page-break-after: always;"></div>
# 二、 调用流程
## 2.1 集成方法说明
1. 把WmCateringService_v1.0.2_sdk.jar放在工程libs目录下;
2. module的build.gradle中添加依赖:
```
Implementation files("libs/WmCateringService_v1.0.2_sdk.jar")
```
3. AndroidManifest.xml中增加权限:
```
<uses-permissionandroid:name="android.permission.QUERY_ALL_PACKAGES"/>
```
4. 若程序开启代码混淆,在防混淆文件中增加:
```
-keep public class com.wmdigit.** {*;}
```
**接口调用流程描述 **
1. 推荐在主页Activity的onCreate()中调用bindService接口,绑定本地餐饮服务(需要预装服务程序);
2. 绑定成功后,调用init接口,对SDK进行初始化;
3. 初始化成功后,调用registerDetectionListener接口,注册识别结果的监听回调;
4. 同时,可以在Activity的onStart()和onStop()中,分别调用registerDetectionListener接口和unregisterDetectionListener接口,保证页面不可见时,不会因触发识别而占用设备性能。这两个接口具体的调用时机,可根据实际业务场景做调整;
5. 初次使用时,请调用openSettingPage接口或直接在系统桌面打开餐饮服务程序,进入设置页面,在设置页面中注册激活码和设定检测区域
6. 关于商品学习,请先调用importProducts接口,导入客户端的商品资料,然后打开餐饮服务设置页,进入数据学习页面
<div style="page-break-after: always;"></div>
# 三、 接口说明
## 主流程接口
## 3.1 bindService(绑定服务)
- 使用场景: 此接口用于绑定WmLPService本地服务,推荐在主页Activity的onCreate()中调用
- 函数名: bindService
- 函数原型
```java
public static void bindService(Context context, IOnServiceConListener listener);
```
- 请求参数
| 请求参数 | 必填 | 类型 | 描述 |
| -------- | ---- | --------------------- | ---------------- |
| context | 是 | Context | Context上下文 |
| listener | 是 | IOnServiceConListener | 本地服务连接回调 |
- 返回说明
- 请求用例
```java
WmSdk.getInstance().bindService(mContext, new IOnServiceConListener() {
      @Override
      public void onConnected(ComponentName componentName, IBinder iBinder) {
      }
      @Override
      public void onDisconnected(ComponentName componentName) {
      }
});
```
## 3.2 init(初始化服务SDK)
- 使用场景:此接口用于初始化,请在服务绑定成功后调用一次
- 函数名:init
- 函数原型
```java
public static void init();
```
- 请求参数
- 返回说明
- 请求用例
```java
WmSdk.getInstance().init();
```
## 3.3 openSettingPage(打开设置页)
- 使用场景: 此接口用于打开设置页面
- 函数名: openSettingPage
- 函数原型
```java
public static void openSettingPage();
```
- 请求参数
- 返回说明
- 请求用例
```java
WmSdk.getInstance().openSettingPage();
```
## 3.4 autoDetect(商品识别)
- 使用场景: 当扫码枪收到条码时,调用此接口
- 函数名: autoDetect
- 函数原型
```java
public void autoDetect(String barcode, String plu, String sessionId);
```
- 请求参数
| 请求参数 | 必填 | 类型 | 描述 |
| --------- | ---- | ------ | ------------------------------ |
| barcode | 是 | String | 扫码枪接收到的条码 |
| plu | 否 | String | 生鲜plu(仅生鲜类商品需要传plu) |
| sessionId | 否 | String | 区分相同条码的商品 |
- 返回说明
- 请求用例
```java
// 接收到的条码
String scanCode = "223057500988"; 
// 购物车中的行号,当相同barcode的商品第一次正确,第二次发生错扫,这时行号就可以区分是哪一个商品发生了错扫
String sessionId = UUID.randomUUID().toString();
// 不是生鲜码
if(){
WmSdk.getInstance().autoDetect(scanCode, "",  sessionId);
}else { 
// 是生鲜码
String plu = "30575"; 
// 从条码中解析出对应的PLU
WmSdk.getInstance().autoDetect(scanCode, plu,  sessionId);
}
```
## 3.5 start(开启防损检测)
- 使用场景:用于开启检测,推荐在购物车页面的onResume()中调用
- 函数名:start
- 函数原型
```java
public int start(IOnAlertEventCallback callback);
```
- 请求参数
| 请求参数 | 必填 | 类型 | 描述 |
| -------- | ---- | --------------------- | ------------------------------------------------------------ |
| callback | 是 | IOnAlertEventCallback | 当发生漏扫、错扫时,通过该回调函数进行通知,通知信息存储在回调参数AlertEvent中,AlertEvent属性:<br>bitmap : 识别图片,这里如果抓图异常,可能为空 <br/>barcode : 条码<br/>plu : 生鲜码<br/>sessionId : 行号<br/>eventType : 识别类型,0错扫,1漏扫<br/>feature : 特征值 |
- 返回说明
| 返回值类型 | 说明 |
|-------|------------|
| 0 | 成功 |
| 2001 | 缺少密钥文件1 |
| 2002 | 缺少密钥文件2 |
| 2003 | 密钥文件1校验失败 |
| 2004 | 密钥文件2校验失败 |
| 2005 | 未注册 |
| 2007 | 未标定动作检测区域 |
| 2008 | 未标定物品识别区域 |
| 3001 | 摄像头USB未连接 |
| 3002 | 摄像头尚未初始化完成 |
| 3003 | 摄像头工作停止 |
- 请求用例
```java
private final IOnAlertEventCallback callback= new IOnAlertEventCallback.Stub() {
        @Override
        public void onAlertEventCallback(AlertEvent event) throws RemoteException{
switch (alertEventBean.getScanType()){
case 1:
System.out.println("漏扫回调");
break;
case 0:
System.out.println("错扫回调");
break;
default:
break;
}
        }
};
int ret = WmSdk.getInstance().start(callback);
        String message = "";
        switch (ret){
            case 0: // 成功
                message = "检测开启成功";
                break;
            case 2001:
            case 2002:
                message = "缺少密钥文件,请先注册激活";
                break;
            case 2003:
            case 2004:
                message = "密钥文件校验失败";
                break;
case 2005:
                message = "未注册,请先注册激活";
                break;
            case 2007:                
             message = "未设置动作检测区域";
                break;
            case 2008:
                message = "未设置物品识别区域";
                break;
case 3001:
message = "摄像头USB断开连接";
break;
case 3002:
message = "摄像头工作停止";
break;
            default:
                message = "初始化失败" + ret;
                break;
}
```
## 3.6 pause(暂停防损检测)
- 使用场景: 用于暂停检测,推荐在购物车页面的onPause()中
- 函数名:pause
- 函数原型
```Java
public void pause();
```
- 请求参数
- 返回说明
- 请求用例
```java
WmSdk.getInstance().pause();
```
## 3.7 unbindService(解绑服务)
- 使用场景: 用于解绑服务,推荐在主页Activity的onStop()中调用
- 函数名:unbindService
- 函数原型
```Java
public void unbindService();
```
- 请求参数
- 返回说明
- 请求用例
```java
WmSdk.getInstance().unbindService();
```
## 其他接口
## 4.1 feedback(保存识别结果)
- 使用场景: 用于保存识别记录
- 函数名:feedback
- 函数原型
```Java
public void feedback(DetectResult result);
```
- 请求参数
| 请求参数 | 必填 | 类型 | 描述 |
| -------- | ---- | ------------ | ------------------------------------------------------------ |
| saveBean | 是 | DetectResult | 保存识别结果DetectResult的属性:<br>productId : 商品标识<br>feature : 特征值 |
- 返回说明
- 请求用例
```java
WmSdk.getInstance().feedback(result);
```
## 4.2 setAdminPassword(设置管理员密码)
- 使用场景: 用于设置管理员密码
- 函数名:setAdminPassword
- 函数原型
```Java
public void setAdminPassword(String password);
```
- 请求参数
| 请求参数 | 必填 | 类型 | 描述 |
| -------- | ---- | ------ | ---- |
| password | 是 | String | 密码 |
- 返回说明
- 请求用例
```java
WmSdk.getInstance().setAdminPassword(password);
```
<div style="page-break-after: always;"></div>
# 附录 1 :商品识别返回错误码
| **错误码** | **描述** | **解决方案** |
|---------|---------------------------------|-------------|
| 0 | 成功 | |
| 1001 | 初始化失败 | |
| 1002 | 模型初始化失败 | |
| 2001 | 缺少密钥文件1 | 重新注册激活 |
| 2002 | 缺少密钥文件2 | 重新注册激活 |
| 2003 | 密钥文件1校验失败 | 重新注册激活 |
| 2004 | 密钥文件2校验失败 | 重新注册激活 |
| 2005 | 未注册 | 重新注册激活 |
| 2007 | 未标定动作检测区域 | 设置页动作检测区域 |
| 2008 | 未标定物品识别区域 | 设置页图像识别区域 |
| 3001 | 摄像头USB断连 | 检查摄像头USB口连接 |
| 3002 | 摄像头尚未初始化完成 | 等待摄像头初始化 |
| 3003 | 摄像头停止工作 | 等待相机恢复 |
| 65006 | 此POS的MAC地址绑定过其他设备,请联系管理员确认POS编号 | |
| 65014 | SnCode未找到 | |
| 65017 | 该POS的MAC地址与服务器记录的地址不同,无法解绑 | |
| 65019 | SnCode并未绑定,无需解绑 | |
| 65024 | 该SN码不属于该租户 | |
| 65028 | 该SN码已被其他设备绑定 | |
# 附录 2 :用例说明
## 一、 识别服务程序使用说明
1. 在机器上安装WmLPService-v1.3.13-release.apk服务程序
2. 初次安装后请在桌面找到元芒防损服务程序,点击运行,会弹出权限申请界
面,请同意所需权限
3. 初次使用请先打开设置页面,完成激活码注册和检测区域标定
## 二、 注意事项
1. 相关接口使用请参考演示DEMO工程中的代码
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment