Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
A
Android_Catering_service
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
姜天宇
Android_Catering_service
Commits
5bc4a8c6
Commit
5bc4a8c6
authored
Apr 23, 2025
by
姜天宇
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 特征表增加字段记录图片地址,数据库版本升级1=>2; 增加本地导入导出功能
parent
f43bd978
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
26 changed files
with
1244 additions
and
85 deletions
+1244
-85
build.gradle
app/build.gradle
+5
-5
BaseMvvmFragment.java
...n/java/com/wmdigit/common/base/mvvm/BaseMvvmFragment.java
+20
-1
BaseViewModel.java
...main/java/com/wmdigit/common/base/mvvm/BaseViewModel.java
+20
-0
ZipUtils.java
common/src/main/java/com/wmdigit/common/utils/ZipUtils.java
+276
-0
strings.xml
common/src/main/res/values/strings.xml
+1
-0
HnswRepository.java
core/src/main/java/com/wmdigit/core/hnsw/HnswRepository.java
+82
-34
build.gradle
data-local/build.gradle
+1
-1
LocalDataModule.java
...local/src/main/java/com/wmdigit/data/LocalDataModule.java
+1
-1
AppDatabase.java
.../src/main/java/com/wmdigit/data/database/AppDatabase.java
+54
-2
ExportDatabase.java
...c/main/java/com/wmdigit/data/database/ExportDatabase.java
+98
-0
ExportFeaturesDao.java
...java/com/wmdigit/data/database/dao/ExportFeaturesDao.java
+41
-0
FeaturesDao.java
.../main/java/com/wmdigit/data/database/dao/FeaturesDao.java
+25
-0
ExportFeaturesPO.java
...va/com/wmdigit/data/database/entity/ExportFeaturesPO.java
+75
-0
FeaturesPO.java
...ain/java/com/wmdigit/data/database/entity/FeaturesPO.java
+14
-3
FeaturesMapper.java
...java/com/wmdigit/data/database/mapper/FeaturesMapper.java
+53
-0
ExportFeaturesRepository.java
...it/data/database/repository/ExportFeaturesRepository.java
+194
-0
FeaturesRepository.java
.../wmdigit/data/database/repository/FeaturesRepository.java
+33
-2
SetMenuAndIngredientRepository.java
...a/database/repository/SetMenuAndIngredientRepository.java
+2
-0
DiskRepository.java
...java/com/wmdigit/data/disk/repository/DiskRepository.java
+105
-6
history.txt
history.txt
+8
-6
SettingActivity.java
...ng/src/main/java/com/wmdigit/setting/SettingActivity.java
+0
-16
DataManagerFragment.java
...ava/com/wmdigit/setting/fragment/DataManagerFragment.java
+35
-0
DataLearningViewModel.java
.../com/wmdigit/setting/viewmodel/DataLearningViewModel.java
+15
-5
DataManagerViewModel.java
...a/com/wmdigit/setting/viewmodel/DataManagerViewModel.java
+73
-2
strings.xml
module-setting/src/main/res/values/strings.xml
+12
-0
CateringInterfaceImpl.java
.../java/com/wmdigit/service/aidl/CateringInterfaceImpl.java
+1
-1
No files found.
app/build.gradle
View file @
5bc4a8c6
...
...
@@ -9,10 +9,10 @@ android {
defaultConfig
{
applicationId
"com.wmdigit.cateringdetect"
minSdk
2
4
minSdk
2
6
targetSdk
33
versionCode
100020
0
versionName
"v1.0.2"
versionCode
100020
1
versionName
"v1.0.2
.1
"
ndk
{
abiFilters
'armeabi-v7a'
...
...
@@ -59,8 +59,8 @@ android {
//如果合并不能解决问题就选择其中一个
merge
'META-INF/proguard/androidx-annotations.pro'
merge
'META-INF/proguard/coroutines.pro'
merge
'lib/arm64-v8a/libc++_shared.so'
merge
'lib/armeabi-v7a/libc++_shared.so'
//
merge 'lib/arm64-v8a/libc++_shared.so'
//
merge 'lib/armeabi-v7a/libc++_shared.so'
pickFirst
'lib/x86/libc++_shared.so'
pickFirst
'lib/x86_64/libc++_shared.so'
pickFirst
'lib/arm64-v8a/libc++_shared.so'
...
...
common/src/main/java/com/wmdigit/common/base/mvvm/BaseMvvmFragment.java
View file @
5bc4a8c6
package
com
.
wmdigit
.
common
.
base
.
mvvm
;
import
android.os.Bundle
;
import
android.text.TextUtils
;
import
android.view.LayoutInflater
;
import
android.view.View
;
import
android.view.ViewGroup
;
...
...
@@ -63,10 +64,28 @@ public abstract class BaseMvvmFragment<VM extends BaseViewModel, DB extends View
protected
abstract
int
getLayoutId
();
/**
* 注册观察对象
* 初始化观察者
*
* 本方法用于设置视图模型中数据的观察者,以更新UI组件
* 它观察视图模型的toast消息和加载进度文本,并相应地更新UI
*/
protected
void
initObserve
(){
// 观察toast消息,当消息变化时,调用Toaster的show方法显示toast
mViewModel
.
toastMessage
.
observe
(
requireActivity
(),
Toaster:
:
show
);
// 观察加载进度文本,根据文本的变化来显示或隐藏进度对话框
mViewModel
.
loadingProgressText
.
observe
(
requireActivity
(),
text
->
{
if
(
TextUtils
.
isEmpty
(
text
)){
// 当加载进度文本为空时,重置进度对话框的标题并隐藏对话框
mProgressDialog
.
setTitle
(
""
);
mProgressDialog
.
dismiss
();
}
else
{
// 当加载进度文本不为空时,更新进度对话框的标题并显示对话框
mProgressDialog
.
setTitle
(
text
);
mProgressDialog
.
show
();
}
});
}
/**
...
...
common/src/main/java/com/wmdigit/common/base/mvvm/BaseViewModel.java
View file @
5bc4a8c6
...
...
@@ -25,6 +25,8 @@ public class BaseViewModel extends AndroidViewModel implements DefaultLifecycleO
*/
public
SingleLiveEvent
<
String
>
toastMessage
=
new
SingleLiveEvent
<>();
public
SingleLiveEvent
<
String
>
loadingProgressText
=
new
SingleLiveEvent
<>();
public
BaseViewModel
(
@NonNull
Application
application
)
{
super
(
application
);
}
...
...
@@ -75,4 +77,22 @@ public class BaseViewModel extends AndroidViewModel implements DefaultLifecycleO
public
void
showToast
(
String
message
){
toastMessage
.
postValue
(
message
);
}
/**
* 显示加载中的进度信息
*
* @param text 进度信息文本,用于显示在加载进度框中
*/
public
void
showLoadingProgress
(
String
text
){
loadingProgressText
.
postValue
(
text
);
}
/**
* 关闭加载中的进度信息
*
* 通过发送空字符串作为进度信息来实现关闭加载进度框的效果
*/
public
void
closeLoadingProgress
(){
loadingProgressText
.
postValue
(
""
);
}
}
common/src/main/java/com/wmdigit/common/utils/ZipUtils.java
0 → 100644
View file @
5bc4a8c6
This diff is collapsed.
Click to expand it.
common/src/main/res/values/strings.xml
View file @
5bc4a8c6
...
...
@@ -78,6 +78,7 @@
<string
name=
"unknown_product"
>
未知商品
</string>
<string
name=
"save_success"
>
保存成功
</string>
<string
name=
"save_failed"
>
保存失败
</string>
<string
name=
"title_download_remote_tool"
>
下载远程工具
</string>
<string
name=
"confirm_download_remote_tool"
>
请确认是否下载远程工具
</string>
...
...
core/src/main/java/com/wmdigit/core/hnsw/HnswRepository.java
View file @
5bc4a8c6
...
...
@@ -23,6 +23,7 @@ import io.reactivex.schedulers.Schedulers;
*/
public
class
HnswRepository
{
// 单例模式实现,保证全局只有一个实例
private
static
HnswRepository
instance
;
public
static
HnswRepository
getInstance
(){
if
(
instance
==
null
){
...
...
@@ -34,17 +35,17 @@ public class HnswRepository {
}
return
instance
;
}
/**
* 索引算法
*/
// 核心索引算法实例
private
Hnsw
hnsw
;
// 用于管理订阅,以便于统一取消
private
CompositeDisposable
compositeDisposable
;
/**
* 记录初始化完成情况
*/
// 记录初始化完成情况
private
boolean
initComplete
=
false
;
// 初始化监听器
private
OnHnswInitListener
listener
;
// 默认特征维度
private
int
dimension
=
128
;
public
HnswRepository
()
{
...
...
@@ -53,32 +54,61 @@ public class HnswRepository {
}
/**
* 初始化
* 初始化索引库
* @param dimension 特征维度
*/
public
void
init
(
int
dimension
){
this
.
dimension
=
dimension
;
initComplete
=
false
;
hnsw
.
initHnsw
(
dimension
);
Disposable
disposable
=
Observable
.
create
(
emitter
->
{
// 遍历特征库
queryAndWriteFeaturesIntoHnsw
();
emitter
.
onNext
(
1
);
})
.
subscribeOn
(
Schedulers
.
io
())
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
o
->
{
initComplete
=
true
;
if
(
listener
!=
null
){
listener
.
onInitSuccess
();
}
});
compositeDisposable
.
add
(
disposable
);
compositeDisposable
.
add
(
startHnswDataInitialization
());
}
/**
* 启动Hnsw数据初始化
* 该方法使用RxJava在IO线程中初始化Hnsw数据,并在主线程中处理初始化完成后的逻辑
*
* @return Disposable 用于订阅的Disposable对象,可通过它来取消订阅
*/
private
Disposable
startHnswDataInitialization
(){
return
initializeHnswData
()
.
subscribeOn
(
Schedulers
.
io
())
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
o
->
{
// 如果存在监听器,则调用监听器的初始化成功方法
if
(
listener
!=
null
){
listener
.
onInitSuccess
();
}
});
}
/**
* 根据特征检索
* @param feature
* @return
* 初始化Hnsw数据结构
*
* 本方法使用Observable模式异步执行Hnsw数据结构的初始化过程包括删除所有现有数据、重新初始化数据结构,
* 以及将特征数据写入新的数据结构中这个方法被设计为异步执行,以便在不阻塞主线程的情况下完成可能耗时的初始化操作
*
* @return Observable<Boolean> 返回一个Observable对象,用于订阅初始化完成的事件
*/
public
Observable
<
Boolean
>
initializeHnswData
(){
return
Observable
.
create
(
emitter
->
{
// 标记初始化未完成
initComplete
=
false
;
// 清空Hnsw图中的所有数据,为重新初始化做准备
hnsw
.
deleteAll
();
// 初始化Hnsw图的数据结构,dimension表示数据的维度
hnsw
.
initHnsw
(
dimension
);
// 遍历特征库,将特征数据写入Hnsw图中
queryAndWriteFeaturesIntoHnsw
();
// 标记初始化完成
initComplete
=
true
;
// 发送信号,表示初始化过程中的一个步骤已完成
emitter
.
onNext
(
true
);
});
}
/**
* 根据特征检索商品
* @param feature 查询用的特征数组
* @return 返回匹配的商品信息,如果没有找到则返回null
*/
public
ProductsPO
retrieveByFeature
(
float
[]
feature
){
if
(!
initComplete
){
...
...
@@ -95,9 +125,9 @@ public class HnswRepository {
/**
* 检索是否存在相同特征
* @param productCode
* @param feature
* @return
* @param productCode
商品代码
* @param feature
特征数组
* @return
如果存在相同特征返回true,否则返回false
*/
public
boolean
retrieveWhetherExistSameFeature
(
String
productCode
,
float
[]
feature
){
if
(!
initComplete
){
...
...
@@ -116,9 +146,9 @@ public class HnswRepository {
/**
* 插入索引,并删除超出表上限的特征
* @param id
* @param code
* @param feature
* @param id
特征ID
* @param code
商品代码
* @param feature
特征数组
*/
public
void
writeFeatureIntoHnsw
(
long
id
,
String
code
,
float
[]
feature
){
long
deleteId
=
hnsw
.
writeFeaturesIntoHnsw
(
id
,
code
,
feature
);
...
...
@@ -128,8 +158,7 @@ public class HnswRepository {
}
/**
* 查询并写入特征
* @param dimension
* 查询并写入特征到索引库
*/
private
void
queryAndWriteFeaturesIntoHnsw
(){
int
page
=
1
;
...
...
@@ -156,6 +185,9 @@ public class HnswRepository {
}
}
/**
* 删除所有样本,并重新初始化索引库
*/
public
void
deleteAllSample
(){
if
(!
initComplete
){
XLog
.
i
(
"索引库未完成初始化"
);
...
...
@@ -166,6 +198,10 @@ public class HnswRepository {
hnsw
.
initHnsw
(
dimension
);
}
/**
* 设置初始化监听器
* @param listener 初始化监听器实例
*/
public
void
setListener
(
OnHnswInitListener
listener
)
{
this
.
listener
=
listener
;
if
(
initComplete
&&
listener
!=
null
){
...
...
@@ -173,16 +209,28 @@ public class HnswRepository {
}
}
/**
* 检查是否初始化完成
* @return 如果初始化完成返回true,否则返回false
*/
public
boolean
isInitComplete
()
{
return
initComplete
;
}
/**
* 清理资源,包括取消所有订阅和设置监听器为null
*/
public
void
close
(){
compositeDisposable
.
clear
();
listener
=
null
;
}
/**
* 初始化监听器接口,用于通知初始化成功
*/
public
static
interface
OnHnswInitListener
{
void
onInitSuccess
();
}
}
data-local/build.gradle
View file @
5bc4a8c6
...
...
@@ -7,7 +7,7 @@ android {
compileSdk
33
defaultConfig
{
minSdk
2
4
minSdk
2
6
testInstrumentationRunner
"androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles
"consumer-rules.pro"
...
...
data-local/src/main/java/com/wmdigit/data/LocalDataModule.java
View file @
5bc4a8c6
...
...
@@ -21,7 +21,7 @@ public class LocalDataModule {
// 初始化mmkv
MMKV
.
initialize
(
context
);
// 清空本地识别记录图片
DiskRepository
.
getInstance
().
clea
r
IdentifyRecords
();
DiskRepository
.
getInstance
().
clea
n
IdentifyRecords
();
}
}
...
...
data-local/src/main/java/com/wmdigit/data/database/AppDatabase.java
View file @
5bc4a8c6
package
com
.
wmdigit
.
data
.
database
;
import
android.content.Context
;
import
android.os.Build
;
import
android.os.Environment
;
import
androidx.annotation.NonNull
;
import
androidx.room.Database
;
...
...
@@ -8,8 +10,12 @@ import androidx.room.DatabaseConfiguration;
import
androidx.room.InvalidationTracker
;
import
androidx.room.Room
;
import
androidx.room.RoomDatabase
;
import
androidx.room.migration.Migration
;
import
androidx.sqlite.db.SupportSQLiteDatabase
;
import
androidx.sqlite.db.SupportSQLiteOpenHelper
;
import
com.elvishew.xlog.XLog
;
import
com.wmdigit.common.utils.FileUtils
;
import
com.wmdigit.data.database.dao.FeaturesDao
;
import
com.wmdigit.data.database.dao.IngredientDao
;
import
com.wmdigit.data.database.dao.ProductsDao
;
...
...
@@ -21,11 +27,14 @@ import com.wmdigit.data.database.entity.ProductsPO;
import
com.wmdigit.data.database.entity.SetMenu
;
import
com.wmdigit.data.database.entity.SetMenuAndIngredient
;
import
java.io.File
;
import
java.nio.file.Files
;
/**
* @author dizi
*/
@Database
(
entities
=
{
ProductsPO
.
class
,
FeaturesPO
.
class
},
version
=
1
,
version
=
2
,
exportSchema
=
false
)
public
abstract
class
AppDatabase
extends
RoomDatabase
{
/**
...
...
@@ -35,15 +44,18 @@ public abstract class AppDatabase extends RoomDatabase {
private
static
volatile
AppDatabase
instance
;
private
static
Context
mContext
;
public
static
AppDatabase
getInstance
(
Context
context
)
{
if
(
instance
==
null
){
synchronized
(
AppDatabase
.
class
){
if
(
instance
==
null
){
mContext
=
context
;
instance
=
Room
.
databaseBuilder
(
context
.
getApplicationContext
(),
AppDatabase
.
class
,
DATABASE_NAME
)
.
addMigrations
(
MIGRATION_1_2
)
.
fallbackToDestructiveMigrationOnDowngrade
()
.
allowMainThreadQueries
()
.
setJournalMode
(
JournalMode
.
TRUNCATE
)
// .createFromAsset("CateringDatabase.db")
.
build
();
}
}
...
...
@@ -101,4 +113,44 @@ public abstract class AppDatabase extends RoomDatabase {
protected
SupportSQLiteOpenHelper
createOpenHelper
(
@NonNull
DatabaseConfiguration
databaseConfiguration
)
{
return
null
;
}
/**
* 复制数据库文件到外部路径
* @return 0-成功 -1-源文件不存在 -2-其他异常
*/
public
int
copyDatabaseFileToExternalPath
(){
File
directory
=
Environment
.
getExternalStorageDirectory
();
String
folderPath
=
directory
.
getAbsolutePath
()
+
File
.
separator
+
"database"
;
File
folder
=
new
File
(
folderPath
);
// 创建文件夹
if
(!
folder
.
exists
()){
folder
.
mkdirs
();
}
// 指定源文件
File
srcFile
=
mContext
.
getDatabasePath
(
DATABASE_NAME
);
if
(!
srcFile
.
exists
()){
return
-
1
;
}
// 指定目标文件
File
dstFile
=
new
File
(
folderPath
+
File
.
separator
+
DATABASE_NAME
);
try
{
Files
.
copy
(
srcFile
.
toPath
(),
dstFile
.
toPath
());
}
catch
(
Exception
e
){
XLog
.
e
(
e
);
return
-
2
;
}
return
0
;
}
/**
* 1=>2升级脚本,Features表增加imgPath图片路径字段
*/
private
static
final
Migration
MIGRATION_1_2
=
new
Migration
(
1
,
2
){
@Override
public
void
migrate
(
@NonNull
SupportSQLiteDatabase
database
)
{
database
.
execSQL
(
"ALTER TABLE `Features` ADD COLUMN `imgPath` TEXT"
);
}
};
}
data-local/src/main/java/com/wmdigit/data/database/ExportDatabase.java
0 → 100644
View file @
5bc4a8c6
package
com
.
wmdigit
.
data
.
database
;
import
android.content.Context
;
import
android.os.Environment
;
import
androidx.annotation.NonNull
;
import
androidx.room.Database
;
import
androidx.room.DatabaseConfiguration
;
import
androidx.room.InvalidationTracker
;
import
androidx.room.Room
;
import
androidx.room.RoomDatabase
;
import
androidx.sqlite.db.SupportSQLiteOpenHelper
;
import
com.elvishew.xlog.XLog
;
import
com.wmdigit.data.database.dao.ExportFeaturesDao
;
import
com.wmdigit.data.database.entity.ExportFeaturesPO
;
import
java.io.File
;
/**
* 导出用数据库
*
* @author dizi
*/
@Database
(
entities
=
{
ExportFeaturesPO
.
class
},
version
=
1
,
exportSchema
=
false
)
public
abstract
class
ExportDatabase
extends
RoomDatabase
{
private
final
static
String
DATABASE_NAME
=
"ExportCateringDatabase.db"
;
private
static
final
String
EXPORT_PATH
=
Environment
.
getExternalStorageDirectory
().
getAbsolutePath
()
+
File
.
separator
+
"database"
+
File
.
separator
+
DATABASE_NAME
;
private
static
volatile
ExportDatabase
instance
;
public
static
ExportDatabase
getInstance
(
Context
context
)
{
if
(
instance
==
null
){
synchronized
(
ExportDatabase
.
class
){
if
(
instance
==
null
){
instance
=
Room
.
databaseBuilder
(
context
.
getApplicationContext
(),
ExportDatabase
.
class
,
EXPORT_PATH
)
.
fallbackToDestructiveMigrationOnDowngrade
()
.
allowMainThreadQueries
()
.
setJournalMode
(
JournalMode
.
TRUNCATE
)
.
build
();
}
}
}
return
instance
;
}
public
static
ExportDatabase
getInstance
()
{
return
instance
;
}
/**
* 关闭数据库
*/
public
void
closeDatabase
(){
if
(
instance
!=
null
&&
instance
.
isOpen
()){
XLog
.
i
(
"ExportDatabase关闭"
);
instance
.
getOpenHelper
().
close
();
instance
.
close
();
}
instance
=
null
;
}
@Override
public
void
clearAllTables
()
{
}
@NonNull
@Override
protected
InvalidationTracker
createInvalidationTracker
()
{
return
null
;
}
@NonNull
@Override
protected
SupportSQLiteOpenHelper
createOpenHelper
(
@NonNull
DatabaseConfiguration
databaseConfiguration
)
{
return
null
;
}
/**
* 获取导出学习数据表的DAO
* @return
*/
public
abstract
ExportFeaturesDao
getExportFeaturesDao
();
/**
* 获取导出路径
* @return
*/
public
String
getExportPath
()
{
return
EXPORT_PATH
;
}
}
data-local/src/main/java/com/wmdigit/data/database/dao/ExportFeaturesDao.java
0 → 100644
View file @
5bc4a8c6
package
com
.
wmdigit
.
data
.
database
.
dao
;
import
androidx.room.Dao
;
import
androidx.room.Insert
;
import
androidx.room.OnConflictStrategy
;
import
androidx.room.Query
;
import
com.wmdigit.data.database.entity.ExportFeaturesPO
;
import
java.util.List
;
/**
* 导出学习数据表的DAO
* @author dizi
*/
@Dao
public
interface
ExportFeaturesDao
{
/**
* 删除全部数据
*/
@Query
(
"Delete from ExportFeatures"
)
void
deleteAll
();
/**
* 插入全部数据
* @param list
*/
@Insert
(
onConflict
=
OnConflictStrategy
.
REPLACE
)
void
insert
(
List
<
ExportFeaturesPO
>
list
);
/**
* 分页查询
* @param offset
* @param limit
* @return
*/
@Query
(
"Select * from ExportFeatures limit :limit offset :offset "
)
List
<
ExportFeaturesPO
>
queryByPage
(
int
offset
,
int
limit
);
}
data-local/src/main/java/com/wmdigit/data/database/dao/FeaturesDao.java
View file @
5bc4a8c6
...
...
@@ -2,6 +2,7 @@ package com.wmdigit.data.database.dao;
import
androidx.room.Dao
;
import
androidx.room.Insert
;
import
androidx.room.OnConflictStrategy
;
import
androidx.room.Query
;
import
com.wmdigit.data.database.entity.FeaturesPO
;
...
...
@@ -24,6 +25,13 @@ public interface FeaturesDao {
@Query
(
"SELECT * FROM Features LIMIT :offset, :pageSize"
)
List
<
FeaturesPO
>
queryByPage
(
int
offset
,
int
pageSize
);
/**
* 查询所有的图片路径
* @return
*/
@Query
(
"Select imgPath From Features"
)
List
<
String
>
queryAllImgPath
();
/**
* 根据ID删除
* @param id
...
...
@@ -38,6 +46,12 @@ public interface FeaturesDao {
@Insert
long
insert
(
FeaturesPO
featuresPO
);
/**
* 批量插入数据
* @param list
*/
@Insert
(
onConflict
=
OnConflictStrategy
.
REPLACE
)
void
insert
(
List
<
FeaturesPO
>
list
);
/**
* 删除所有特性数据
...
...
@@ -51,4 +65,15 @@ public interface FeaturesDao {
@Query
(
"DELETE FROM Features"
)
int
deleteAll
();
/**
* 查询Features表中的记录数
*
* 此方法通过执行SQL查询来统计Features表中的所有记录数
* 使用了注解@Query来定义查询语句,这种方式使得查询逻辑更加清晰和集中
*
* @return Features表中的记录总数
*/
@Query
(
"SELECT COUNT(*) FROM Features"
)
int
getCount
();
}
data-local/src/main/java/com/wmdigit/data/database/entity/ExportFeaturesPO.java
0 → 100644
View file @
5bc4a8c6
package
com
.
wmdigit
.
data
.
database
.
entity
;
import
androidx.room.Entity
;
import
androidx.room.Ignore
;
import
androidx.room.Index
;
import
androidx.room.PrimaryKey
;
/**
* 导出向量表
*/
@Entity
(
tableName
=
"ExportFeatures"
)
public
class
ExportFeaturesPO
{
@PrimaryKey
(
autoGenerate
=
true
)
private
long
id
;
/**
* 商品CODE
*/
private
String
productCode
;
/**
* 特征
*/
private
String
feature
;
/**
* 图片路径
*/
private
String
imgPath
;
public
ExportFeaturesPO
()
{
}
@Ignore
public
ExportFeaturesPO
(
long
id
,
String
productCode
,
String
feature
,
String
imgPath
)
{
this
.
id
=
id
;
this
.
productCode
=
productCode
;
this
.
feature
=
feature
;
this
.
imgPath
=
imgPath
;
}
public
long
getId
()
{
return
id
;
}
public
void
setId
(
long
id
)
{
this
.
id
=
id
;
}
public
String
getProductCode
()
{
return
productCode
;
}
public
void
setProductCode
(
String
productCode
)
{
this
.
productCode
=
productCode
;
}
public
String
getFeature
()
{
return
feature
;
}
public
String
getImgPath
()
{
return
imgPath
;
}
public
void
setImgPath
(
String
imgPath
)
{
this
.
imgPath
=
imgPath
;
}
public
void
setFeature
(
String
feature
)
{
this
.
feature
=
feature
;
}
}
data-local/src/main/java/com/wmdigit/data/database/entity/FeaturesPO.java
View file @
5bc4a8c6
...
...
@@ -14,25 +14,28 @@ public class FeaturesPO {
@PrimaryKey
(
autoGenerate
=
true
)
private
long
id
;
/**
* 商品CODE
*/
private
String
productCode
;
/**
* 特征
*/
private
String
feature
;
/**
* 文件路径
*/
private
String
imgPath
;
public
FeaturesPO
()
{
}
@Ignore
public
FeaturesPO
(
long
id
,
String
productCode
,
String
feature
)
{
public
FeaturesPO
(
long
id
,
String
productCode
,
String
feature
,
String
imgPath
)
{
this
.
id
=
id
;
this
.
productCode
=
productCode
;
this
.
feature
=
feature
;
this
.
imgPath
=
imgPath
;
}
public
long
getId
()
{
...
...
@@ -58,4 +61,12 @@ public class FeaturesPO {
public
void
setFeature
(
String
feature
)
{
this
.
feature
=
feature
;
}
public
String
getImgPath
()
{
return
imgPath
;
}
public
void
setImgPath
(
String
imgPath
)
{
this
.
imgPath
=
imgPath
;
}
}
data-local/src/main/java/com/wmdigit/data/database/mapper/FeaturesMapper.java
0 → 100644
View file @
5bc4a8c6
package
com
.
wmdigit
.
data
.
database
.
mapper
;
import
com.wmdigit.data.database.entity.ExportFeaturesPO
;
import
com.wmdigit.data.database.entity.FeaturesPO
;
import
org.mapstruct.Mapper
;
import
org.mapstruct.factory.Mappers
;
import
java.util.List
;
/**
* FeaturesMapper接口用于定义特征信息的映射操作
* 它主要负责在不同的数据表示形式之间进行转换,特别是与特征导出相关的持久化对象(PO)
*/
@Mapper
public
interface
FeaturesMapper
{
/**
* INSTANCE是一个静态常量,用于获取FeaturesMapper的实例
* 通过Mappers.getMapper方法初始化,便于在需要的地方直接使用,减少重复代码
*/
FeaturesMapper
INSTANCE
=
Mappers
.
getMapper
(
FeaturesMapper
.
class
);
/**
* 将ExportFeaturesPO对象列表转换为另一个ExportFeaturesPO对象列表
* 这个方法的主要作用是进行数据的重新组织或格式转换,以便于特征导出功能的实现
*
* @param poList 一个包含多个FeaturesPO对象的列表,代表待转换的原始数据
* @return 返回一个新的ExportFeaturesPO列表,代表转换后的数据
*/
List
<
ExportFeaturesPO
>
toExportFeaturesPOList
(
List
<
FeaturesPO
>
poList
);
/**
* 将单个ExportFeaturesPO对象转换为另一个ExportFeaturesPO对象
* 这个方法用于对单个数据对象进行转换或映射,同样是为了适应特征导出功能的需求
*
* @param po 一个FeaturesPO对象,代表待转换的原始数据
* @return 返回一个新的ExportFeaturesPO对象,代表转换后的数据
*/
ExportFeaturesPO
toExportFeaturesPO
(
FeaturesPO
po
);
/**
* 将ExportFeaturesPO列表转换为FeaturesPO列表
* 此方法用于批量转换对象,提高处理效率
*
* @param list ExportFeaturesPO对象的列表,代表待转换的特征数据
* @return 转换后的FeaturesPO对象列表
*/
List
<
FeaturesPO
>
toFeaturesPOList
(
List
<
ExportFeaturesPO
>
list
);
}
data-local/src/main/java/com/wmdigit/data/database/repository/ExportFeaturesRepository.java
0 → 100644
View file @
5bc4a8c6
package
com
.
wmdigit
.
data
.
database
.
repository
;
import
com.wmdigit.data.LocalDataModule
;
import
com.wmdigit.data.database.ExportDatabase
;
import
com.wmdigit.data.database.dao.ExportFeaturesDao
;
import
com.wmdigit.data.database.entity.ExportFeaturesPO
;
import
com.wmdigit.data.database.entity.FeaturesPO
;
import
com.wmdigit.data.database.mapper.FeaturesMapper
;
import
com.wmdigit.data.disk.repository.DiskRepository
;
import
java.io.File
;
import
java.util.List
;
import
io.reactivex.Observable
;
import
io.reactivex.ObservableSource
;
import
io.reactivex.android.schedulers.AndroidSchedulers
;
import
io.reactivex.disposables.Disposable
;
import
io.reactivex.functions.Consumer
;
import
io.reactivex.functions.Function
;
import
io.reactivex.schedulers.Schedulers
;
/**
* @author dizi
*/
public
class
ExportFeaturesRepository
{
private
static
volatile
ExportFeaturesRepository
instance
;
public
static
ExportFeaturesRepository
getInstance
()
{
if
(
instance
==
null
){
synchronized
(
ExportFeaturesRepository
.
class
){
if
(
instance
==
null
){
instance
=
new
ExportFeaturesRepository
();
}
}
}
return
instance
;
}
private
ExportFeaturesDao
getExportFeaturesDao
(){
return
ExportDatabase
.
getInstance
(
LocalDataModule
.
getAppContext
()).
getExportFeaturesDao
();
}
/**
* 删除全部数据
*/
public
void
deleteAll
(){
getExportFeaturesDao
().
deleteAll
();
}
/**
* 插入列表
* @param list
*/
public
void
insert
(
List
<
ExportFeaturesPO
>
list
){
getExportFeaturesDao
().
insert
(
list
);
}
/**
* 分页查询
* @param page 页码,从1开始计数
* @param pageSize
* @return
*/
public
List
<
ExportFeaturesPO
>
queryByPage
(
int
page
,
int
pageSize
){
int
offset
=
pageSize
*
(
page
-
1
);
return
getExportFeaturesDao
().
queryByPage
(
offset
,
pageSize
);
}
/**
* 异步导出数据库信息和图片
* 该方法将导出操作放在异步线程中执行,以避免阻塞主线程
*
* @param onNext 消费者接口,用于处理导出成功后的操作
* @param onError 消费者接口,用于处理导出过程中发生的错误
* @return Disposable对象,用于管理订阅的生命周期
*/
public
Disposable
asyncExportLearningData
(
Consumer
<
Object
>
onNext
,
Consumer
<
Object
>
onError
){
return
Observable
.
create
(
emitter
->
{
// 删除所有已导出的数据,确保开始新一轮的导出
deleteAll
();
// 导出数据库中的数据
exportDatabase
();
// 导出图片信息
exportImages
();
// 通知观察者导出完成
emitter
.
onNext
(
true
);
})
.
subscribeOn
(
Schedulers
.
io
())
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
onNext
,
onError
);
}
/**
* 导出数据库中的数据
* 该方法分页查询数据库中的数据,并将其转换后插入到导出数据库中
*/
private
void
exportDatabase
(){
int
page
=
1
;
int
pageSize
=
1000
;
while
(
true
){
// 分页查询数据库中的数据
List
<
FeaturesPO
>
list
=
FeaturesRepository
.
getInstance
().
getFeaturesByPage
(
page
,
pageSize
);
// 将查询结果转换为导出对象列表
List
<
ExportFeaturesPO
>
exportList
=
FeaturesMapper
.
INSTANCE
.
toExportFeaturesPOList
(
list
);
// 将转换后的数据插入到导出数据库中
insert
(
exportList
);
// 如果查询结果小于页面大小,说明所有数据已经查询完毕,退出循环
if
(
list
.
size
()
<
pageSize
){
break
;
}
// 增加页码,继续下一页的查询
page
++;
}
// 导出完成后,关闭数据库连接
// ExportDatabase.getInstance().closeDatabase();
}
/**
* 导出图片信息
* 该方法将图片信息打包压缩,以便于导出
*/
private
void
exportImages
(){
// 调用磁盘仓库实例的压缩方法,对识别记录进行压缩
DiskRepository
.
getInstance
().
zipIdentifyRecords
();
}
/**
* 判断导出文件是否存在
* @return
*/
public
boolean
isExistExportFile
()
{
File
file
=
new
File
(
ExportDatabase
.
getInstance
(
LocalDataModule
.
getAppContext
()).
getExportPath
());
return
file
.
exists
();
}
/**
* 异步导入学习数据
* 该方法用于在异步环境下导入数据库和图片资源,并进行相应的处理
*
* @param function 用于处理数据的函数
* @param onNext 处理数据成功时的回调
* @param onError 处理数据失败时的回调
* @return Disposable对象,用于管理订阅的生命周期
*/
public
Disposable
asyncImportLearningData
(
Function
<
Object
,
ObservableSource
<?>>
function
,
Consumer
<
Object
>
onNext
,
Consumer
<
Object
>
onError
){
return
Observable
.
create
(
emitter
->
{
// 导入数据库
importDatabase
();
// 解压图片
importImages
();
// 通知观察者数据已准备好
emitter
.
onNext
(
true
);
}).
flatMap
(
function
)
.
subscribeOn
(
Schedulers
.
io
())
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
onNext
,
onError
);
}
/**
* 导入数据库方法
* 该方法用于分页查询数据,将查询到的数据转换格式后批量插入数据库
* 直到查询到的数据量小于设定的页面大小,表明已导入所有数据
*/
private
void
importDatabase
(){
// 初始化页码为第1页
int
page
=
1
;
// 设置每页的大小为1000条记录
int
pageSize
=
1000
;
// 无限循环,直到查询到的数据量小于页面大小,表明已导入所有数据
while
(
true
){
// 根据页码和页面大小查询数据
List
<
ExportFeaturesPO
>
list
=
queryByPage
(
page
,
pageSize
);
// 将查询到的数据转换格式,以便插入数据库
List
<
FeaturesPO
>
importList
=
FeaturesMapper
.
INSTANCE
.
toFeaturesPOList
(
list
);
// 批量插入数据到数据库
FeaturesRepository
.
getInstance
().
insert
(
importList
);
// 如果查询到的数据量小于页面大小,表明已导入所有数据,退出循环
if
(
list
.
size
()
<
pageSize
){
break
;
}
// 页码递增,准备查询下一页数据
page
++;
}
// 导入完成后,关闭数据库连接
// ExportDatabase.getInstance().closeDatabase();
}
/**
* 导入图片
*/
private
void
importImages
(){
DiskRepository
.
getInstance
().
unzipIdentifyRecords
();
}
}
data-local/src/main/java/com/wmdigit/data/database/repository/FeaturesRepository.java
View file @
5bc4a8c6
...
...
@@ -5,6 +5,7 @@ import android.text.TextUtils;
import
com.elvishew.xlog.XLog
;
import
com.wmdigit.data.database.AppDatabase
;
import
com.wmdigit.data.database.dao.FeaturesDao
;
import
com.wmdigit.data.database.entity.ExportFeaturesPO
;
import
com.wmdigit.data.database.entity.FeaturesPO
;
import
java.util.Arrays
;
...
...
@@ -43,12 +44,21 @@ public class FeaturesRepository {
return
getFeaturesDao
().
queryByPage
(
offset
,
pageSize
);
}
/**
* 获取所有图片路径
* @return
*/
public
List
<
String
>
getAllImgPath
(){
return
getFeaturesDao
().
queryAllImgPath
();
}
/**
* 插入
* @param productCode
* @param feature
* @param imgPath
*/
public
long
insert
(
String
productCode
,
float
[]
feature
){
public
long
insert
(
String
productCode
,
float
[]
feature
,
String
imgPath
){
if
(
TextUtils
.
isEmpty
(
productCode
)
||
feature
==
null
){
return
-
1
;
}
...
...
@@ -58,9 +68,18 @@ public class FeaturesRepository {
FeaturesPO
featuresPO
=
new
FeaturesPO
();
featuresPO
.
setFeature
(
featureStr
);
featuresPO
.
setProductCode
(
productCode
);
featuresPO
.
setImgPath
(
imgPath
);
return
getFeaturesDao
().
insert
(
featuresPO
);
}
/**
* 批量插入
* @param list
*/
public
void
insert
(
List
<
FeaturesPO
>
list
){
getFeaturesDao
().
insert
(
list
);
}
/**
* 根据ID删除
* @param id
...
...
@@ -79,5 +98,17 @@ public class FeaturesRepository {
public
void
deleteAll
(){
int
deleteCount
=
getFeaturesDao
().
deleteAll
();
XLog
.
i
(
"删除了%s条数据"
,
deleteCount
);
};
}
/**
* 获取特征的数量
*
* 此方法通过调用特征数据访问对象(FeaturesDao)的 getCount 方法来获取特征的数量
* 使用这种封装方式,可以使代码结构更清晰,便于维护和测试
*
* @return 特征的数量
*/
public
int
getCount
(){
return
getFeaturesDao
().
getCount
();
}
}
data-local/src/main/java/com/wmdigit/data/database/repository/SetMenuAndIngredientRepository.java
View file @
5bc4a8c6
...
...
@@ -14,9 +14,11 @@ import java.util.concurrent.atomic.AtomicInteger;
import
java.util.stream.Collectors
;
/**
* 不再使用
* 套餐、原料相关的Repository
* @author dizi
*/
@Deprecated
public
class
SetMenuAndIngredientRepository
{
private
static
volatile
SetMenuAndIngredientRepository
instance
;
...
...
data-local/src/main/java/com/wmdigit/data/disk/repository/DiskRepository.java
View file @
5bc4a8c6
...
...
@@ -2,16 +2,30 @@ package com.wmdigit.data.disk.repository;
import
android.content.Context
;
import
android.graphics.Bitmap
;
import
android.os.Environment
;
import
android.text.TextUtils
;
import
com.elvishew.xlog.XLog
;
import
com.wmdigit.common.utils.BitmapUtils
;
import
com.wmdigit.common.utils.ZipUtils
;
import
com.wmdigit.data.LocalDataModule
;
import
com.wmdigit.data.database.entity.ProductsPO
;
import
com.wmdigit.data.database.repository.FeaturesRepository
;
import
java.io.File
;
import
java.io.IOException
;
import
java.nio.file.Files
;
import
java.nio.file.Path
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.HashSet
;
import
java.util.List
;
import
java.util.Objects
;
import
java.util.Set
;
import
java.util.UUID
;
import
java.util.concurrent.ConcurrentSkipListSet
;
import
java.util.concurrent.atomic.AtomicInteger
;
import
java.util.stream.Stream
;
/**
* @author dizi
...
...
@@ -45,17 +59,20 @@ public class DiskRepository {
* @param bitmap
* @param rectArray
* @param products
* @return
返回原图的
路径
* @return
String[] 0-大图路径 1、2、3...n-切图
路径
*/
public
String
saveIdentifyRecordToDisk
(
Bitmap
bitmap
,
int
[][]
rectArray
,
List
<
ProductsPO
>
products
){
public
String
[]
saveIdentifyRecordToDisk
(
Bitmap
bitmap
,
int
[][]
rectArray
,
List
<
ProductsPO
>
products
){
List
<
String
>
results
=
new
ArrayList
<>();
Context
context
=
LocalDataModule
.
getAppContext
();
// 识别记录根目录
String
identifyRecordsRootPath
=
context
.
getExternalFilesDir
(
FOLDER_NAME_IDENTIFY_RECORDS
).
getAbsolutePath
();
String
imageFilename
=
UUID
.
randomUUID
().
toString
()
+
".jpg"
;
// 保存图片
BitmapUtils
.
saveBitmap
(
bitmap
,
identifyRecordsRootPath
+
"/"
+
imageFilename
);
// 记录原始图片位置
results
.
add
(
identifyRecordsRootPath
+
"/"
+
imageFilename
);
if
(
rectArray
==
null
||
products
==
null
){
return
identifyRecordsRootPath
+
"/"
+
imageFilename
;
return
results
.
toArray
(
new
String
[
1
])
;
}
// 商品的学习记录根目录
String
productLearningRecordsRootPath
=
context
.
getExternalFilesDir
(
FOLDER_NAME_PRODUCT_LEARNING_RECORDS
).
getAbsolutePath
();
...
...
@@ -66,17 +83,19 @@ public class DiskRepository {
}
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"
);
String
path
=
productLearningRecordsRootPath
+
"/"
+
products
.
get
(
i
).
getProductCode
()
+
"/"
+
imageFilename
.
replace
(
".jpg"
,
""
)
+
"_"
+
i
+
".jpg"
;
BitmapUtils
.
saveBitmap
(
bitmapTemp
,
path
);
results
.
add
(
path
);
bitmapTemp
.
recycle
();
}
return
identifyRecordsRootPath
+
"/"
+
imageFilename
;
return
results
.
toArray
(
new
String
[
results
.
size
()])
;
}
/**
* 删除识别记录文件夹图片
*/
public
void
clea
r
IdentifyRecords
(){
public
void
clea
n
IdentifyRecords
(){
Context
context
=
LocalDataModule
.
getAppContext
();
String
identifyRecordsRootPath
=
context
.
getExternalFilesDir
(
FOLDER_NAME_IDENTIFY_RECORDS
).
getAbsolutePath
();
File
folder
=
new
File
(
identifyRecordsRootPath
);
...
...
@@ -88,4 +107,84 @@ public class DiskRepository {
}
}
/* public void cleanOrphanedImages(){
String folderPath = LocalDataModule.getAppContext().getExternalFilesDir(FOLDER_NAME_PRODUCT_LEARNING_RECORDS).getAbsolutePath();
File rootImageDir = new File(folderPath);
if (!rootImageDir.exists()){
return;
}
long startTime = System.currentTimeMillis();
Set<String> dbImgPaths = new ConcurrentSkipListSet<>(FeaturesRepository.getInstance().getAllImgPath());
try (Stream<Path> pathStream = Files.walk(rootImageDir.toPath())){
pathStream
.parallel()
.filter(Files::isRegularFile)
.forEach(filePath -> {
String absPath = filePath.toString();
if (!dbImgPaths.contains(absPath)) {
try {
Files.delete(filePath);
} catch (IOException e) {
XLog.e(e);
}
}
});
}
catch (Exception e){
XLog.e(e);
}
}*/
/**
* 将产品的学习记录打包成zip文件
* 此方法首先获取应用上下文,然后确定源文件夹路径和目标zip文件路径
* 如果目标zip文件已存在,则将其删除,以确保生成的是最新记录的zip文件
* 最后,使用ZipUtils工具类将源文件夹压缩成zip文件
*/
public
void
zipIdentifyRecords
(){
// 获取应用上下文
Context
context
=
LocalDataModule
.
getAppContext
();
// 商品的学习记录根目录
String
sourcePath
=
context
.
getExternalFilesDir
(
FOLDER_NAME_PRODUCT_LEARNING_RECORDS
).
getAbsolutePath
();
// 目标zip文件路径
String
targetPath
=
Environment
.
getExternalStorageDirectory
().
getAbsolutePath
()
+
File
.
separator
+
"database"
+
File
.
separator
+
FOLDER_NAME_PRODUCT_LEARNING_RECORDS
+
".zip"
;
// 创建目标zip文件对象
File
targetFile
=
new
File
(
targetPath
);
// 如果目标zip文件已存在,则将其删除
if
(
targetFile
.
exists
()){
targetFile
.
delete
();
}
// 压缩文件
ZipUtils
.
zipFile
(
sourcePath
,
targetPath
);
}
/**
* 解压识别记录文件
* 该方法用于解压存储在外部存储中的识别记录zip文件到指定目录
* 主要目的是为了恢复或导入识别记录数据
*/
public
void
unzipIdentifyRecords
(){
// 获取应用上下文
Context
context
=
LocalDataModule
.
getAppContext
();
// 构造源文件路径,指向外部存储中的识别记录zip文件
String
sourcePath
=
Environment
.
getExternalStorageDirectory
().
getAbsolutePath
()
+
File
.
separator
+
"database"
+
File
.
separator
+
FOLDER_NAME_PRODUCT_LEARNING_RECORDS
+
".zip"
;
// 创建源文件对象
File
zipFile
=
new
File
(
sourcePath
);
// 检查源文件是否存在,如果不存在则记录错误日志并返回
if
(!
zipFile
.
exists
()){
XLog
.
e
(
"图片压缩包不存在"
);
return
;
}
// 构造目标路径,指向应用外部文件目录中的识别记录文件夹
String
targetPath
=
context
.
getExternalFilesDir
(
FOLDER_NAME_PRODUCT_LEARNING_RECORDS
).
getAbsolutePath
();
File
targetFile
=
new
File
(
targetPath
);
try
{
// 调用ZipUtils工具类的unzip方法解压文件
ZipUtils
.
unzip
(
sourcePath
,
targetFile
.
getParent
());
}
catch
(
Exception
e
)
{
// 捕获并记录解压过程中可能发生的异常
XLog
.
e
(
e
);
}
}
}
history.txt
View file @
5bc4a8c6
...
...
@@ -10,12 +10,14 @@ v1.0.2 2024/08/06 1.增加系统信息页
7.增加AIDL服务
8.集成标框、菜品识别、餐盘识别算法
9.集成索引库算法(索引库版本较老,可能存在最后一条索引删除不掉的BUG)
10.
todo
菜品算法更新128模型
10.菜品算法更新128模型
11.增加数据上传
12.增加【清空商品数据】、【SN解绑】、【下载远程工具】、【检查更新】功能
13.增加摄像头断线重连
14.todo 增加学习记录管理模块
15.todo 替换LOGO
16.todo 增加过期数据清理
17.todo 增加轮询job
18.摄像头改为UvcCamera
\ No newline at end of file
14.摄像头改为UvcCamera
v1.0.2.1 2025/04/22 1.特征表增加字段记录图片地址,数据库版本升级1=>2
2.增加本地导入导出功能
todo 增加学习记录管理模块
todo 替换LOGO
todo 增加轮询job
\ No newline at end of file
module-setting/src/main/java/com/wmdigit/setting/SettingActivity.java
View file @
5bc4a8c6
...
...
@@ -84,21 +84,6 @@ public class SettingActivity extends BaseMvvmNaviDrawerActivity<SettingViewModel
protected
void
onStart
()
{
super
.
onStart
();
// 开相机
/*if(!CameraxController.getInstance(this).isOpen()){
isCameraNeedRelease = true;
// 将相机生命周期和页面绑定,开启相机
if (CameraLocalRepository.getInstance().getCamera().getVid() == 0){
CameraxController.getInstance()
.setLifecycleOwner(this)
.setFrameInterval(2)
.bindCamera();
}
else{
CameraxController.getInstance()
.setLifecycleOwner(this)
.setFrameInterval(2);
}
}*/
if
(!
USBCameraHelper
.
getInstance
(
this
).
checkCameraOpened
()){
isCameraNeedRelease
=
true
;
USBCameraHelper
.
getInstance
(
this
).
open
();
...
...
@@ -111,7 +96,6 @@ public class SettingActivity extends BaseMvvmNaviDrawerActivity<SettingViewModel
// 关相机
if
(
isCameraNeedRelease
){
isCameraNeedRelease
=
false
;
// CameraxController.getInstance().destroy();
USBCameraHelper
.
getInstance
(
this
).
destroy
();
}
}
...
...
module-setting/src/main/java/com/wmdigit/setting/fragment/DataManagerFragment.java
View file @
5bc4a8c6
...
...
@@ -41,6 +41,16 @@ public class DataManagerFragment extends BaseMvvmFragment<DataManagerViewModel,
protected
void
initListener
()
{
adapter
.
setOnItemClickListener
(
position
->
{
switch
(
position
)
{
case
0
:
// 导出学习数据
exportLearningData
();
break
;
case
1
:
// 导入学习数据
importLearningData
();
break
;
case
2
:
// 清空学习数据
clearLearningData
();
...
...
@@ -67,6 +77,31 @@ public class DataManagerFragment extends BaseMvvmFragment<DataManagerViewModel,
});
}
/**
* 导入学习数据
*/
private
void
importLearningData
()
{
mSecondConfirmWindow
.
setTitle
(
getString
(
com
.
wmdigit
.
common
.
R
.
string
.
prompt
))
.
setContent
(
getString
(
R
.
string
.
confirm_import_learning_data
))
.
setClickListener
(()
->
{
mViewModel
.
importLearningData
();
})
.
showWindow
();
}
/**
* 导出学习数据
*/
private
void
exportLearningData
()
{
mSecondConfirmWindow
.
setTitle
(
getString
(
com
.
wmdigit
.
common
.
R
.
string
.
prompt
))
.
setContent
(
getString
(
R
.
string
.
confirm_export_learning_data
))
.
setClickListener
(()
->
{
mViewModel
.
exportLearningData
();
})
.
showWindow
();
}
/**
* 下载远程工具
*
...
...
module-setting/src/main/java/com/wmdigit/setting/viewmodel/DataLearningViewModel.java
View file @
5bc4a8c6
...
...
@@ -400,15 +400,19 @@ public class DataLearningViewModel extends BaseViewModel {
*/
public
void
saveDetectResult
(){
synchronized
(
detectResultLock
){
if
(
detectResult
==
null
||
detectResult
.
getProducts
()
==
null
){
if
(
detectResult
==
null
||
detectResult
.
getProducts
()
==
null
||
detectResult
.
getProducts
().
size
()
==
0
){
return
;
}
String
[]
productNames
=
new
String
[
detectResult
.
getProducts
().
size
()];
String
[]
productCodes
=
new
String
[
detectResult
.
getProducts
().
size
()];
// 保存本地,并获取保存的路径
String
[]
imagePaths
=
DiskRepository
.
getInstance
().
saveIdentifyRecordToDisk
(
detectResult
.
getBitmap
(),
detectResult
.
getRectArray
(),
detectResult
.
getProducts
());
// 计数,标识保存成功数量
int
learnSuccessCount
=
0
;
for
(
int
i
=
0
;
i
<
detectResult
.
getProducts
().
size
();
i
++){
productNames
[
i
]
=
""
;
productCodes
[
i
]
=
""
;
if
(
TextUtils
.
isEmpty
(
detectResult
.
getProducts
().
get
(
i
).
getProductCode
())){
if
(
detectResult
.
getProducts
().
get
(
i
)
==
null
||
TextUtils
.
isEmpty
(
detectResult
.
getProducts
().
get
(
i
).
getProductCode
())){
continue
;
}
if
(
detectResult
.
getFeatures
()[
i
]
==
null
){
...
...
@@ -420,20 +424,26 @@ public class DataLearningViewModel extends BaseViewModel {
continue
;
}
// 插入特征数据库
long
id
=
FeaturesRepository
.
getInstance
().
insert
(
detectResult
.
getProducts
().
get
(
i
).
getProductCode
(),
detectResult
.
getFeatures
()[
i
]);
long
id
=
FeaturesRepository
.
getInstance
().
insert
(
detectResult
.
getProducts
().
get
(
i
).
getProductCode
(),
detectResult
.
getFeatures
()[
i
]
,
imagePaths
[
i
+
1
]
);
// 插入索引库
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
();
learnSuccessCount
++;
}
String
imagePath
=
DiskRepository
.
getInstance
().
saveIdentifyRecordToDisk
(
detectResult
.
getBitmap
(),
detectResult
.
getRectArray
(),
detectResult
.
getProducts
());
if
(
learnSuccessCount
==
0
){
// 学习失败
toastMessage
.
postValue
(
getApplication
().
getString
(
com
.
wmdigit
.
common
.
R
.
string
.
save_failed
));
return
;
}
// 学习成功
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
));
compositeDisposable
.
add
(
IdentifyRecordRepository
.
getInstance
().
uploadIdentifyRecord
(
json
,
imagePath
s
[
0
]
));
}
}
...
...
module-setting/src/main/java/com/wmdigit/setting/viewmodel/DataManagerViewModel.java
View file @
5bc4a8c6
...
...
@@ -4,9 +4,11 @@ import android.app.Application;
import
androidx.annotation.NonNull
;
import
com.elvishew.xlog.XLog
;
import
com.wmdigit.common.base.mvvm.BaseViewModel
;
import
com.wmdigit.common.base.mvvm.SingleLiveEvent
;
import
com.wmdigit.core.hnsw.HnswRepository
;
import
com.wmdigit.data.database.repository.ExportFeaturesRepository
;
import
com.wmdigit.data.database.repository.FeaturesRepository
;
import
com.wmdigit.data.database.repository.ProductsRepository
;
import
com.wmdigit.network.repository.UserRemoteRepository
;
...
...
@@ -34,14 +36,83 @@ public class DataManagerViewModel extends BaseViewModel {
private
void
getData
(){
httpToast
=
UserRemoteRepository
.
getInstance
().
mHttpToast
;
// 初始化功能键列表
funcButtons
.
add
(
new
FuncButton
(
getApplication
().
getString
(
R
.
string
.
module_setting_upload_database_to_server
),
R
.
drawable
.
ic_upload_learning_data
));
funcButtons
.
add
(
new
FuncButton
(
getApplication
().
getString
(
R
.
string
.
module_setting_download_database_from_server
),
R
.
drawable
.
ic_download_learning_data
));
funcButtons
.
add
(
new
FuncButton
(
getApplication
().
getString
(
R
.
string
.
export_learning_data
),
R
.
drawable
.
ic_upload_learning_data
));
funcButtons
.
add
(
new
FuncButton
(
getApplication
().
getString
(
R
.
string
.
import_learning_data
),
R
.
drawable
.
ic_download_learning_data
));
funcButtons
.
add
(
new
FuncButton
(
getApplication
().
getString
(
R
.
string
.
module_setting_clear_learning_data
),
R
.
drawable
.
ic_clear_data_red
));
funcButtons
.
add
(
new
FuncButton
(
getApplication
().
getString
(
R
.
string
.
module_setting_clear_products
),
R
.
drawable
.
ic_clean_learning_data
));
funcButtons
.
add
(
new
FuncButton
(
getApplication
().
getString
(
R
.
string
.
module_setting_unbind
),
R
.
drawable
.
ic_unbind
));
funcButtons
.
add
(
new
FuncButton
(
getApplication
().
getString
(
R
.
string
.
module_setting_download_remote_tool
),
R
.
drawable
.
ic_download_remote
));
}
/**
* 导出学习数据
*
* 此方法首先检查是否有数据可供导出如果数据库中没有数据,则显示提示消息并返回
* 如果有数据,将显示加载进度文本,并开始异步导出数据库
* 导出成功后,将更新UI以显示成功消息,否则显示失败消息
*/
public
void
exportLearningData
(){
// 判断是否有数据
if
(
FeaturesRepository
.
getInstance
().
getCount
()
==
0
){
toastMessage
.
postValue
(
getApplication
().
getString
(
R
.
string
.
export_learning_data_no_data
));
return
;
}
// 准备导出数据,显示加载进度文本
loadingProgressText
.
postValue
(
getApplication
().
getString
(
R
.
string
.
exporting_learning_data
));
// 异步导出数据库,根据结果更新UI
compositeDisposable
.
add
(
ExportFeaturesRepository
.
getInstance
().
asyncExportLearningData
(
success
->{
// 导出成功后,隐藏加载进度文本并显示成功消息
loadingProgressText
.
postValue
(
""
);
toastMessage
.
postValue
(
getApplication
().
getString
(
R
.
string
.
export_success
));
},
error
->{
XLog
.
e
(
error
);
// 导出失败后,隐藏加载进度文本并显示失败消息
loadingProgressText
.
postValue
(
""
);
toastMessage
.
postValue
(
getApplication
().
getString
(
R
.
string
.
export_failed
));
})
);
}
/**
* 导入学习数据
* 此方法检查本地是否存在可导入的数据库文件,如果存在,则开始导入学习数据
* 在导入数据期间,会显示加载进度文本,并在成功或失败时更新UI
*/
public
void
importLearningData
(){
// 判断本地是否存在可导入得数据库文件
if
(!
ExportFeaturesRepository
.
getInstance
().
isExistExportFile
()){
// 如果不存在,显示提示信息并返回
toastMessage
.
postValue
(
getApplication
().
getString
(
R
.
string
.
import_database_file_not_exits
));
return
;
}
// 存在可导入的文件时,显示导入学习数据的加载进度文本
loadingProgressText
.
postValue
(
getApplication
().
getString
(
R
.
string
.
importing_learning_data
));
// 异步导入学习数据
compositeDisposable
.
add
(
ExportFeaturesRepository
.
getInstance
().
asyncImportLearningData
(
// 导入后的工作,初始化HNWS数据结构
obj
->
HnswRepository
.
getInstance
().
initializeHnswData
(),
// 导入成功时的处理
success
->{
// 隐藏加载进度文本
loadingProgressText
.
postValue
(
""
);
// 显示导入成功的提示信息
toastMessage
.
postValue
(
getApplication
().
getString
(
R
.
string
.
import_success
));
},
// 导入失败时的处理
error
->{
// 记录错误日志
XLog
.
e
(
error
);
// 隐藏加载进度文本
loadingProgressText
.
postValue
(
""
);
// 显示导入失败的提示信息
toastMessage
.
postValue
(
getApplication
().
getString
(
R
.
string
.
import_failed
));
})
);
}
/**
* 清除学习数据
*
...
...
module-setting/src/main/res/values/strings.xml
View file @
5bc4a8c6
...
...
@@ -48,4 +48,16 @@
<string
name=
"packaging_remote_tool"
>
正在安装远程工具
</string>
<string
name=
"download_fail"
>
下载失败
</string>
<string
name=
"download_retry"
>
下载成功
</string>
<string
name=
"export_learning_data"
>
导出学习数据
</string>
<string
name=
"import_learning_data"
>
导入学习数据
</string>
<string
name=
"confirm_export_learning_data"
>
请确认是否导出学习数据?
</string>
<string
name=
"confirm_import_learning_data"
>
请确认是否导入学习数据?
</string>
<string
name=
"exporting_learning_data"
>
正在导出学习数据,请勿中断操作
</string>
<string
name=
"importing_learning_data"
>
正在导入学习数据,请勿中断操作
</string>
<string
name=
"export_learning_data_no_data"
>
本地没有学习数据,导出终止
</string>
<string
name=
"export_success"
>
学习数据导出成功
</string>
<string
name=
"export_failed"
>
学习数据导出失败,请联系运维人员
</string>
<string
name=
"import_database_file_not_exits"
>
待导入的数据库文件不存在
</string>
<string
name=
"import_success"
>
导入成功
</string>
<string
name=
"import_failed"
>
导入失败
</string>
</resources>
\ No newline at end of file
service/src/main/java/com/wmdigit/service/aidl/CateringInterfaceImpl.java
View file @
5bc4a8c6
...
...
@@ -407,7 +407,7 @@ public class CateringInterfaceImpl extends ICateringInterface.Stub{
String
json
=
new
Gson
().
toJson
(
identifyRecordDTO
);
Bitmap
bitmapTemp
=
ParcelHelper
.
copy
(
bitmap
);
Disposable
disposable
=
Observable
.
create
(
emitter
->
{
String
filePath
=
DiskRepository
.
getInstance
().
saveIdentifyRecordToDisk
(
bitmapTemp
,
null
,
null
);
String
filePath
=
DiskRepository
.
getInstance
().
saveIdentifyRecordToDisk
(
bitmapTemp
,
null
,
null
)
[
0
]
;
String
url
=
OSSManager
.
getInstance
().
syncUploadFileToOSS
(
new
File
(
filePath
));
emitter
.
onNext
(
url
);
}).
flatMap
(
url
->{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment