Compare commits

...
2 Commits
Author SHA1 Message Date
BBIT-Kai a42d6ceec6 优化日志逻辑;修改除湿机自动重发指令的时间间隔; 2026-07-24 18:17:06 +08:00
BBIT-Kai 5612c478bd 完善日志内容 2026-07-24 10:24:31 +08:00
46 changed files with 927 additions and 343 deletions
+2 -2
View File
@@ -10,8 +10,8 @@ android {
applicationId "com.example.iot_controlhost"
minSdkVersion 25
targetSdkVersion 30
versionCode 160
versionName "3.5.0.160"
versionCode 163
versionName "3.5.0.163"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
ndk {
moduleName "mcu"
@@ -82,10 +82,12 @@ public class AutoControllerAdapter extends BaseRecyclerAdapter<Controller, ItemC
if (result.isOperateResult()) {
changeState(binding, device);
Toasty.success(mContext, mContext.getString(R.string.operate_success)).show();
UserLog.operate("手动:" + (result.isState() ? "开启" : "关闭") + device.getName());
UserLog.operate("本机屏幕", "自动页面手动操作成功:"
+ (result.isState() ? "开启" : "关闭") + device.getName());
} else {
Toasty.error(mContext, mContext.getString(R.string.operate_fail)).show();
UserLog.operateError("手动操作失败:" + (result.isState() ? "开启" : "关闭") + device.getName());
UserLog.operateError("本机屏幕", "自动页面手动操作失败:"
+ (result.isState() ? "开启" : "关闭") + device.getName());
}
DialogUtil.hideLoadingDialog();
}
@@ -6,6 +6,7 @@ import com.example.iot_controlhost.base.BaseRecyclerAdapter;
import com.example.iot_controlhost.databinding.ItemLogBinding;
import com.example.iot_controlhost.model.Log;
import com.example.iot_controlhost.utils.global.RoomSetting;
import com.example.iot_controlhost.utils.log.UserLog;
import java.util.List;
@@ -27,7 +28,7 @@ public class LogAdapter extends BaseRecyclerAdapter<Log, ItemLogBinding> {
data.getLevel() == android.util.Log.ERROR ? R.color.red : RoomSetting.isDarkTheme() ? R.color.white : R.color.black);
binding.tvMessage.setTextColor(targetColorId);
binding.tvDate.setTextColor(targetColorId);
binding.tvMessage.setText(data.getMessage());
binding.tvMessage.setText("[" + UserLog.getCategoryName(data.getTag()) + "] " + data.getMessage());
binding.tvDate.setText(TimeUtils.date2String(data.getDatetime()));
}
}
@@ -65,10 +65,12 @@ public class ManualControllerAdapter extends BaseRecyclerAdapter<Controller, Ite
if (result.isOperateResult()) {
changeState(binding, device);
Toasty.success(mContext, mContext.getString(R.string.operate_success)).show();
UserLog.operate("手动:" + (result.isState() ? "开启" : "关闭") + device.getName());
UserLog.operate("本机屏幕", "设备操作成功:"
+ (result.isState() ? "开启" : "关闭") + device.getName());
} else {
Toasty.error(mContext, mContext.getString(R.string.operate_fail)).show();
UserLog.operateError("手动操作失败:" + (result.isState() ? "开启" : "关闭") + device.getName());
UserLog.operateError("本机屏幕", "设备操作失败:"
+ (result.isState() ? "开启" : "关闭") + device.getName());
}
DialogUtil.hideLoadingDialog();
}
@@ -17,6 +17,7 @@ import com.example.iot_controlhost.utils.global.AutoModelSet;
import com.example.iot_controlhost.utils.global.RoomSetting;
import com.example.iot_controlhost.utils.global.RxTag;
import com.example.iot_controlhost.utils.log.UserLog;
import com.example.iot_controlhost.utils.log.MyLog;
import com.example.iot_controlhost.utils.network.Url;
import com.example.iot_controlhost.utils.network.XHttpManager;
import com.example.iot_controlhost.utils.network.XHttpShorthand;
@@ -58,6 +59,8 @@ public class StageAdapter extends BaseRecyclerAdapter<StageList.Data, ItemBtnBin
public void success(WorkStage model) {
String info = WorkNewDialog.getInfo(model, false);
TempTHVDBManager.INSTANCE.setStage(content.getName(), info);
UserLog.task("切换作业阶段:" + content.getName());
MyLog.app("作业阶段切换成功:" + content.getName() + ",已应用阶段控制参数");
tvStageContent.setText(info);
if (RoomSetting.getMode() != 1) {
// 如果不是自动模式,则切换为自动模式
@@ -71,7 +74,8 @@ public class StageAdapter extends BaseRecyclerAdapter<StageList.Data, ItemBtnBin
@Override
public void error(Throwable e) {
tvStageContent.setText("获取失败");
UserLog.taskError("作业列表获取失败" + e.getMessage());
UserLog.taskError("作业阶段切换失败" + content.getName());
MyLog.networkError("作业阶段切换失败:" + content.getName() + ",原因=" + e.getMessage());
}
@Override
@@ -82,4 +86,4 @@ public class StageAdapter extends BaseRecyclerAdapter<StageList.Data, ItemBtnBin
});
}
}
}
@@ -9,6 +9,7 @@ import com.example.iot_controlhost.databinding.ItemUvTaskBinding;
import com.example.iot_controlhost.model.MyTask;
import com.example.iot_controlhost.ui.dialog.ConfirmDialog;
import com.example.iot_controlhost.utils.timer.UVTaskUtil;
import com.example.iot_controlhost.utils.log.UserLog;
import java.util.List;
@@ -37,6 +38,9 @@ public class UvTaskAdapter extends BaseRecyclerAdapter<MyTask, ItemUvTaskBinding
UVTaskUtil.removeUvTask(data);
dataList.remove(data);
notifyDataSetChanged();
UserLog.operate("本机屏幕", String.format("删除消毒计划成功:%02d:%02d→%02d:%02d",
data.getStartTimeHour(), data.getStartTimeMinute(),
data.getEndTimeHour(), data.getEndTimeMinute()));
}).show());
}
@@ -11,6 +11,7 @@ import com.example.iot_controlhost.utils.DialogUtil;
import com.example.iot_controlhost.utils.global.RoomSetting;
import com.example.iot_controlhost.utils.global.RxTag;
import com.example.iot_controlhost.utils.log.UserLog;
import com.example.iot_controlhost.utils.log.MyLog;
import com.example.iot_controlhost.utils.network.Url;
import com.example.iot_controlhost.utils.network.XHttpManager;
import com.example.iot_controlhost.utils.network.XHttpShorthand;
@@ -65,7 +66,8 @@ public class WorkAdapter extends BaseRecyclerAdapter<WorkList.Data, ItemBtnBindi
@Override
public void error(Throwable e) {
tvWorkContent.setText("获取失败");
UserLog.taskError("作业列表获取失败" + e.getMessage());
UserLog.taskError("作业内容获取失败");
MyLog.networkError("作业内容获取失败:" + e.getMessage());
}
@Override
@@ -7,7 +7,7 @@ import com.example.iot_controlhost.utils.global.Variable;
import com.example.iot_controlhost.utils.log.MyLog;
/**
* @Description 除湿机,核心控制地方
* @Description 除湿机,核心控制地方 物理位置
* @Author DuanKaiji
* @CreateTime 2023年12月11日 09:35:18
*/
@@ -23,7 +23,7 @@ public class Dehumidifier extends Relay {
}
public boolean setPowerSupplyForOld(boolean state) {
// 设置物理开关的方法
// 设置继电器 物理开关的方法
return super.setPowerSupply(state);
}
@@ -36,8 +36,8 @@ public class Dehumidifier extends Relay {
if (!this.powerSupply) {
MyLog.controller("除湿机电源当前为关闭状态,现开启除湿机物理开关");
if (setPowerSupplyForOld(true)) {
MyLog.controller("除湿机电源打开成功,等待" + Variable.WAIT_START_INFRARED_FOR_DEHUMIDIFIER / 1000 + "s后使用红外遥控器打开除湿机");
try {
MyLog.controller("除湿机电源打开成功,等待" + Variable.WAIT_START_INFRARED_FOR_DEHUMIDIFIER / 1000 + "s后使用红外遥控器打开除湿机");
Thread.sleep(Variable.WAIT_START_INFRARED_FOR_DEHUMIDIFIER);
} catch (InterruptedException e) {
e.printStackTrace();
@@ -53,6 +53,7 @@ public class Dehumidifier extends Relay {
result = executeInstructions(str + getCRC(str), "开电源");
// 设置开机成功与否
this.powerSupply = result;
// 设置红外状态
RoomController.dehumidifierInfrared.setPowerSupply(result);
} else {
//关机
@@ -62,7 +63,7 @@ public class Dehumidifier extends Relay {
str = str + crc;
result = executeInstructions(str, "关电源");
if (result) {
//关机成功
//关机成功 设置红外状态
RoomController.dehumidifierInfrared.setPowerSupply(false);
this.powerSupply = false;
}
@@ -14,19 +14,20 @@ import com.xuexiang.rxutil2.rxbus.RxBusUtils;
public class FrpStartReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context mContext, Intent intent) {
if (intent.getAction().equals("receiver_frp_version")) {
String action = intent == null ? null : intent.getAction();
if ("receiver_frp_version".equals(action)) {
int frpVersion = intent.getIntExtra("version", 0);
MyLog.frp("收到FRP版本:" + frpVersion);
MMKVUtil.put(HardwareSetting.FRP_VERSION, frpVersion);
} else if (intent.getAction().equals("receiver_start_frp")) {
} else if ("receiver_start_frp".equals(action)) {
if (intent.getBooleanExtra("start_frp", false)) {
MyLog.frp("收到启动FRP指令");
MyUtil.relaunchFrp();
}
} else if (intent.getAction().equals("receiver_frp_info")) {
} else if ("receiver_frp_info".equals(action)) {
String info = intent.getStringExtra("info");
MyLog.frp(info);
RxBusUtils.get().post(RxTag.UPDATE_MAIN_MSG, info);
}
}
}
}
@@ -5,6 +5,7 @@ import android.content.Context;
import android.content.Intent;
import com.example.iot_controlhost.utils.timer.ThemeTaskUtil;
import com.example.iot_controlhost.utils.database.LogDBManager;
import com.example.iot_controlhost.utils.log.MyLog;
import com.example.iot_controlhost.utils.timer.TimerTaskUtil;
import com.example.iot_controlhost.utils.timer.UVTaskUtil;
@@ -12,7 +13,7 @@ import com.example.iot_controlhost.utils.timer.UVTaskUtil;
/**
* 每日计划
* 适用于每天都需要重新设置的计划重设
* 每天0启动
* 每天凌晨00:30启动
*/
public class TimerReceiver extends BroadcastReceiver {
/**
@@ -21,11 +22,13 @@ public class TimerReceiver extends BroadcastReceiver {
public static final String DAILY_TIMER = "DAILY_TIMER";
public void onReceive(Context context, Intent intent) {
MyLog.auto("0时刷新定时任务:更新每日计划——自动切换亮暗色调");
MyLog.auto("凌晨定时维护:更新每日计划——自动切换亮暗色调");
ThemeTaskUtil.resetTask();
MyLog.auto("0时刷新定时任务:更新每日计划——消毒灯自动任务");
MyLog.auto("凌晨定时维护:更新每日计划——消毒灯自动任务");
UVTaskUtil.restartTask();
MyLog.auto("凌晨定时维护:清理十五天前的日志");
LogDBManager.clearExpiredLogsAsync();
// 初始化每日定时任务
TimerTaskUtil.start();
}
}
}
@@ -121,15 +121,17 @@ public class MainActivity extends BaseActivity<ActivityIncubationBinding, MainAc
case 5 ->//显示详细信息
startActivity(new Intent(mContext, InfoActivity.class));
case 7 ->//切换为手动模式
new ConfirmDialog(mContext, getString(R.string.warning), getString(R.string.ask_turn_manual), () -> presenter.initMode(0)).show();
new ConfirmDialog(mContext, getString(R.string.warning), getString(R.string.ask_turn_manual),
() -> presenter.initMode(0, MainActivityPresenter.MODE_SOURCE_LOCAL)).show();
case 8 ->//切换为自动模式
new ConfirmDialog(mContext, getString(R.string.warning), getString(R.string.ask_turn_auto), () -> presenter.initMode(1)).show();
new ConfirmDialog(mContext, getString(R.string.warning), getString(R.string.ask_turn_auto),
() -> presenter.initMode(1, MainActivityPresenter.MODE_SOURCE_LOCAL)).show();
case 9 ->//切换为智能模式
new ConfirmDialog(mContext, getString(R.string.warning), getString(R.string.ask_turn_intelligence), () -> {
if (!SilkwormDBManager.isStarting()) {
new ConfirmDialog(mContext, getString(R.string.error), "当前未在" + (RoomSetting.getType() == 4 ? "催青" : "共育") + ",无法进入智能模式", null).show();
} else {
presenter.initMode(2);
presenter.initMode(2, MainActivityPresenter.MODE_SOURCE_LOCAL);
}
}).show();
case 10 ->//刷新共育状态
@@ -180,7 +182,7 @@ public class MainActivity extends BaseActivity<ActivityIncubationBinding, MainAc
@Override
public void doInUIThread(CulInfo culInfo) {
if (needSwitchMode) {
presenter.initMode(culInfo.isStarting ? 1 : 0);
presenter.initMode(culInfo.isStarting ? 1 : 0, MainActivityPresenter.MODE_SOURCE_WORKFLOW);
}
fillDataInText(binding.tvAge, culInfo.age);
fillDataInText(binding.tvVariety, culInfo.variety);
@@ -42,6 +42,11 @@ import java.util.ArrayList;
public class MainActivityPresenter extends BasePresenter {
public static final String MODE_SOURCE_LOCAL = "本机屏幕";
public static final String MODE_SOURCE_REMOTE = "远程控制";
public static final String MODE_SOURCE_WORKFLOW = "作业流程";
public static final String MODE_SOURCE_SYSTEM = "系统联动";
private MutableLiveData<Integer> noticeLiveData = new SingleLiveEvent<>();
private MutableLiveData<WorkList> workListMutableLiveData = new SingleLiveEvent<>();
private MutableLiveData<Boolean> workNewListMutableLiveData = new SingleLiveEvent<>();
@@ -97,15 +102,21 @@ public class MainActivityPresenter extends BasePresenter {
roomName.setValue(RoomSetting.getRoomName());
if (!RoomSetting.isRoomEnable()) {
//刷新切换为手动模式
initMode(0);
initMode(0, MODE_SOURCE_REMOTE + "-区域禁用联动");
}
}
case 3 -> //刷新切换为自动模式
initMode(1);
initMode(1, MODE_SOURCE_WORKFLOW);
case 5 -> //刷新切换为手动模式
initMode(0);
initMode(0, MODE_SOURCE_WORKFLOW);
case 6 -> //刷新切换为智能模式
initMode(2);
initMode(2, MODE_SOURCE_SYSTEM);
case 13 -> //远程切换为自动模式
initMode(1, MODE_SOURCE_REMOTE);
case 15 -> //远程切换为手动模式
initMode(0, MODE_SOURCE_REMOTE);
case 16 -> //远程切换为智能模式
initMode(2, MODE_SOURCE_REMOTE);
case 7 -> //刷新共育信息 并刷新模式
noticeLiveData.setValue(10);
}
@@ -126,33 +137,81 @@ public class MainActivityPresenter extends BasePresenter {
* @param targetMode 新模式
*/
public void initMode(int targetMode) {
String modeName;
if (targetMode == 0) {
modeName = "手动模式";
} else if (targetMode == 1) {
modeName = "自动模式";
} else {
modeName = "智能模式";
initMode(targetMode, MODE_SOURCE_SYSTEM);
}
public void initMode(int targetMode, String source) {
int previousMode = RoomSetting.getMode();
String previousModeName = getModeName(previousMode);
String modeName = getModeName(targetMode);
if (previousMode == targetMode) {
MyLog.app("模式切换未执行:来源=" + source + ",当前已是" + modeName);
return;
}
showLoadingDialog("正在切换为" + modeName + "");
MyQueue.start(MyQueue.TYPE_CONTROLLER, new QueueIOUITask<>() {
MyQueue.start(MyQueue.TYPE_CONTROLLER, new QueueIOUITask<ModeChangeResult>() {
@Override
public Object doInQueueThread() {
ModelsAdapter.getModeByPosition(RoomSetting.getMode()).stopMode();
MMKVUtil.put(RoomSetting.MODE, targetMode);
UserLog.operate("切换为:" + modeName);
return null;
public ModeChangeResult doInQueueThread() {
try {
ModelsAdapter.getModeByPosition(previousMode).stopMode();
MMKVUtil.put(RoomSetting.MODE, targetMode);
return ModeChangeResult.success();
} catch (Exception e) {
return ModeChangeResult.failure(e.getMessage());
}
}
@Override
public void doInUIThread(Object o) {
changeMode.setValue(targetMode);
ModelsAdapter.getModeByPosition(targetMode).initMode();
public void doInUIThread(ModeChangeResult result) {
if (result.success) {
try {
changeMode.setValue(targetMode);
ModelsAdapter.getModeByPosition(targetMode).initMode();
UserLog.operate(source, "模式切换成功:" + previousModeName + "" + modeName);
} catch (Exception e) {
UserLog.operateError(source, "模式切换失败:" + previousModeName + "" + modeName);
MyLog.appError("模式切换后初始化失败:来源=" + source + "" + previousModeName
+ "" + modeName + ",原因=" + e.getMessage());
errorTips("模式初始化失败,请检查设备状态");
}
} else {
UserLog.operateError(source, "模式切换失败:" + previousModeName + "" + modeName);
MyLog.appError("模式切换失败:来源=" + source + "" + previousModeName + ""
+ modeName + ",原因=" + result.message);
errorTips("模式切换失败,请稍后重试");
}
hideLoadingDialog();
}
});
}
private String getModeName(int mode) {
return switch (mode) {
case 0 -> "手动模式";
case 1 -> "自动模式";
case 2 -> "智能模式";
default -> "未知模式(" + mode + ")";
};
}
private static class ModeChangeResult {
private final boolean success;
private final String message;
private ModeChangeResult(boolean success, String message) {
this.success = success;
this.message = message;
}
private static ModeChangeResult success() {
return new ModeChangeResult(true, null);
}
private static ModeChangeResult failure(String message) {
return new ModeChangeResult(false, message == null ? "未知异常" : message);
}
}
public MutableLiveData<Integer> getNoticeLiveData() {
return noticeLiveData;
@@ -240,7 +299,8 @@ public class MainActivityPresenter extends BasePresenter {
@Override
public void error(Throwable e) {
errorTips("作业列表获取失败,请检查网络");
UserLog.taskError("作业列表获取失败" + e.getMessage());
UserLog.taskError("作业列表获取失败");
MyLog.networkError("作业列表获取失败:" + e.getMessage());
}
@Override
@@ -275,4 +335,4 @@ public class MainActivityPresenter extends BasePresenter {
});
}
}
}
@@ -51,6 +51,8 @@ import com.example.iot_controlhost.utils.global.RoomController;
import com.example.iot_controlhost.utils.global.RoomSensor;
import com.example.iot_controlhost.utils.global.RoomSetting;
import com.example.iot_controlhost.utils.global.SpinnerList;
import com.example.iot_controlhost.utils.log.UserLog;
import com.example.iot_controlhost.utils.log.MyLog;
import java.util.ArrayList;
import java.util.Collections;
@@ -171,8 +173,10 @@ public class SetActivity extends BaseActivity<ActivitySetBinding, SetActivityPre
binding.tvTitle.setText(getText(R.string.set) + " > " + getText(R.string.set_app));
setList.clear();
setList.add(new Set(getString(R.string.set_password), "******", view ->
new PasswordDialog(mContext, getString(R.string.set_password), null, ti ->
MMKVUtil.put(RoomSetting.SET_PASSWORD, ti.getText().toString())).show()));
new PasswordDialog(mContext, getString(R.string.set_password), null, ti -> {
MMKVUtil.put(RoomSetting.SET_PASSWORD, ti.getText().toString());
UserLog.operate("本机屏幕", "系统设置密码修改成功(密码内容未记录)");
}).show()));
setList.add(new Set(getString(R.string.set_password_main_lock), "******", view -> new PasswordDialog(mContext, getString(R.string.set_password_main_lock),
null, ti -> {
if (ti.getText().toString().length() != 6) {
@@ -180,6 +184,7 @@ public class SetActivity extends BaseActivity<ActivitySetBinding, SetActivityPre
return;
}
MMKVUtil.put(RoomSetting.MAIN_ACTIVITY_LOCK, ti.getText().toString());
UserLog.operate("本机屏幕", "锁屏密码修改成功(密码内容未记录)");
}).show()));
setList.add(new Set("导入配置", view -> new SpinnerDialog(mContext, "导入配置", "请选择配置来源",
new String[]{"预设", "基地", "会东", "旧版本", "兴文石海", "主干继电器-从机模式"},
@@ -257,7 +262,9 @@ public class SetActivity extends BaseActivity<ActivitySetBinding, SetActivityPre
if (!Device.isIntegratedController()) {
//装配式控制器
setList.add(new Set(getString(R.string.enable_relay), RoomController.relay.isEnable(), (compoundButton, b) -> {
boolean oldValue = RoomController.relay.isEnable();
RoomController.relay.setEnable(b);
logSettingChanged("继电器启用状态", oldValue, b);
setRelay();
}));
setList.add(new Set(getString(R.string.set_base), RoomController.relay.toString(), view -> new SetDeviceBaseDialog(mContext, getString(R.string.set_relay), RoomController.relay.getModels(),
@@ -294,7 +301,9 @@ public class SetActivity extends BaseActivity<ActivitySetBinding, SetActivityPre
binding.tvTitle.setText(getText(R.string.set) + " > 除湿机配置 > 485除湿机");
setList.clear();
setList.add(new Set("启用485除湿机", dehumidifier485.isEnable(), (compoundButton, b) -> {
boolean oldValue = dehumidifier485.isEnable();
dehumidifier485.setEnable(b);
logSettingChanged("485除湿机启用状态", oldValue, b);
setDehumidifier485();
}));
setList.add(new Set("基础配置", dehumidifier485.toString(), view ->
@@ -314,7 +323,9 @@ public class SetActivity extends BaseActivity<ActivitySetBinding, SetActivityPre
binding.tvTitle.setText(getText(R.string.set) + " > 除湿机配置");
setList.clear();
setList.add(new Set(getString(R.string.enable_air_condition_infrared), deHumidityInfrared.isEnable(), (compoundButton, b) -> {
boolean oldValue = deHumidityInfrared.isEnable();
deHumidityInfrared.setEnable(b);
logSettingChanged("红外除湿机启用状态", oldValue, b);
setDeHumidityInfrared();
}));
setList.add(new Set(getString(R.string.set_base_infrared), deHumidityInfrared.toString(), view ->
@@ -345,18 +356,24 @@ public class SetActivity extends BaseActivity<ActivitySetBinding, SetActivityPre
new ConfirmDialog(mContext, "提示", "设置失败,等待时间不可少于3秒", null).show();
return;
}
long oldSeconds = RoomSetting.getTimeOfAirStopRelay();
MMKVUtil.put(RoomSetting.TIME_OF_AIR_STOP_RELAY, seconds);
logSettingChanged("空调关闭物理电源等待时间", oldSeconds + "", seconds + "");
setAirCondition();
} catch (Exception e) {
new ConfirmDialog(mContext, "提示", "设置失败,等待时间必须为整数", null).show();
}
}, true).show()));
setList.add(new Set("手动/自动控制时:先开物理开关,再切换温度", RoomSetting.isAirStartFirst(), (compoundButton, b) -> {
boolean oldValue = RoomSetting.isAirStartFirst();
MMKVUtil.put(RoomSetting.CONTROL_AIR_START_FIRST, b);
logSettingChanged("空调先开物理开关", oldValue, b);
setAirCondition();
}));
setList.add(new Set("自动控制时:循环发送控制指令", RoomSetting.isAirRecycleControl(), (compoundButton, b) -> {
boolean oldValue = RoomSetting.isAirRecycleControl();
MMKVUtil.put(RoomSetting.AIR_RECYCLE_CONTROL, b);
logSettingChanged("空调循环发送控制指令", oldValue, b);
setAirCondition();
}));
setAdapter.setDataList(setList);
@@ -370,7 +387,9 @@ public class SetActivity extends BaseActivity<ActivitySetBinding, SetActivityPre
binding.tvTitle.setText(getText(R.string.set) + " > " + getText(R.string.set_air_condition) + " > " + airConditionInfrared.getName());
setList.clear();
setList.add(new Set(getString(R.string.enable_air_condition_infrared), airConditionInfrared.isEnable(), (compoundButton, b) -> {
boolean oldValue = airConditionInfrared.isEnable();
airConditionInfrared.setEnable(b);
logSettingChanged(airConditionInfrared.getName() + "启用状态", oldValue, b);
setAirConditionDetail(airId, airConditionInfrared);
}));
setList.add(new Set(getString(R.string.set_base_infrared), airConditionInfrared.toString(), view ->
@@ -420,17 +439,23 @@ public class SetActivity extends BaseActivity<ActivitySetBinding, SetActivityPre
binding.tvTitle.setText(getText(R.string.set) + " > " + RoomSetting.getFloorName() + "配置");
setList.clear();
setList.add(new Set("开启" + RoomSetting.getFloorName(), RoomController.floor.isEnable(), (compoundButton, b) -> {
boolean oldValue = RoomController.floor.isEnable();
RoomController.floor.setEnable(b);
logSettingChanged(RoomSetting.getFloorName() + "启用状态", oldValue, b);
setFloor();
}));
setList.add(new Set("地暖别名", RoomSetting.getFloorName(), view ->
new SpinnerDialog(mContext, "地暖别名", "请选择显示名称", new String[]{"地暖", "升温板"}, str -> {
String oldValue = RoomSetting.getFloorName();
MMKVUtil.put(RoomSetting.FLOOR_NAME, str);
logSettingChanged("地暖显示名称", oldValue, str);
setFloor();
}).show()));
setList.add(new Set(getString(R.string.max_gear), RoomController.floor.getMaxGear() + "", v1 ->
new SpinnerDialog(mContext, getString(R.string.max_gear), "请选择" + RoomSetting.getFloorName() + "最大档位", new String[]{"1", "2", "3", "4", "5"}, selection -> {
int oldValue = RoomController.floor.getMaxGear();
RoomController.floor.setMaxGear(Integer.parseInt(selection));
logSettingChanged(RoomSetting.getFloorName() + "最大档位", oldValue, selection);
setFloor();
successTips("最大档位已设置为:" + Integer.parseInt(selection) + "");
}).show()));
@@ -556,6 +581,7 @@ public class SetActivity extends BaseActivity<ActivitySetBinding, SetActivityPre
binding.tvTitle.setText(getText(R.string.set) + " > " + getText(R.string.set_advanced));
setList.clear();
setList.add(new Set("恢复出厂设置", view -> new ConfirmDialog(mContext, getString(R.string.error), "确定恢复出厂设置吗?\n设备将在初始化后重启", () -> {
UserLog.operate("本机屏幕", "已确认恢复出厂设置,设备即将重置并重启");
MMKVUtil.clear();
//清空数据后需要恢复主机码 否则无法上传最新配置
MMKVUtil.put(HardwareSetting.HOST_ID, Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID));
@@ -581,4 +607,9 @@ public class SetActivity extends BaseActivity<ActivitySetBinding, SetActivityPre
setAdapter.setDataList(setList);
}
private void logSettingChanged(String name, Object oldValue, Object newValue) {
UserLog.operate("本机屏幕", "修改设备设置:" + name);
MyLog.app("设备设置修改:" + name + "=" + oldValue + "" + newValue);
}
}
@@ -25,7 +25,6 @@ import com.example.iot_controlhost.utils.database.dynamicSensor.IndoorAmmoniaSen
import com.example.iot_controlhost.utils.database.dynamicSensor.IndoorCO2SensorUtil;
import com.example.iot_controlhost.utils.database.dynamicSensor.IndoorFloorTSensorUtil;
import com.example.iot_controlhost.utils.database.dynamicSensor.IndoorTHSensorUtil;
import com.example.iot_controlhost.utils.database.LogDBManager;
import com.example.iot_controlhost.utils.global.AutoModelSet;
import com.example.iot_controlhost.utils.global.HardwareSetting;
import com.example.iot_controlhost.utils.global.RoomController;
@@ -35,7 +34,6 @@ import com.example.iot_controlhost.utils.global.RxTag;
import com.example.iot_controlhost.utils.global.SpinnerList;
import com.example.iot_controlhost.utils.global.Variable;
import com.example.iot_controlhost.utils.log.MyLog;
import com.example.iot_controlhost.utils.log.UserLog;
import com.example.iot_controlhost.utils.network.Url;
import com.example.iot_controlhost.utils.network.XHttpManager;
import com.example.iot_controlhost.utils.network.XHttpShorthand;
@@ -76,9 +74,7 @@ public class SplashActivityPresenter extends BasePresenter {
public Void doInIOThread(Object o) {
// 检查向后兼容性
checkOldVersion();
processLiveData.postValue(new ProcessInfo(5, "正在清除过期日志..."));
//清除1个月前的日志
LogDBManager.clearLogsOlderThanOneMonthsSync();
processLiveData.postValue(new ProcessInfo(5, "正在检查本地配置..."));
// 记录是否长时间未开机,即未在共育
Date lastRecordDate = EnergyDBManager.queryLastRecordDate();
if (lastRecordDate == null ||
@@ -264,7 +260,7 @@ public class SplashActivityPresenter extends BasePresenter {
public void onGranted(@NonNull List<String> permissions, boolean allGranted) {
if (allGranted) {
processLiveData.postValue(new ProcessInfo(100, "系统启动成功"));
UserLog.operate("系统启动成功");
MyLog.app("系统启动成功");
} else {
MyLog.appError("获取部分权限成功,但部分权限未正常授予");
XXPermissions.startPermissionActivity(getApplication(), permissions);
@@ -108,14 +108,14 @@ public class AirDialog extends BaseDialog<DialogAirConditionBinding> {
public void doInUIThread(Boolean result) {
if (result) {
successTips(air.getState());
UserLog.operate("手动:操作空调" + air.getState());
UserLog.operate("本机屏幕", "空调操作成功:" + air.getState());
} else {
String errorTips = "手动:操作空调失败";
if (RoomSetting.isAirConditionForceClose()) {
errorTips += ",在消毒期间无法开启空调";
}
errorTips(errorTips);
UserLog.operateError(errorTips);
UserLog.operateError("本机屏幕", errorTips);
}
refreshAir();
DialogUtil.hideLoadingDialog();
@@ -19,6 +19,7 @@ import com.example.iot_controlhost.utils.MyQueue;
import com.example.iot_controlhost.utils.global.RoomSetting;
import com.example.iot_controlhost.utils.global.RxTag;
import com.example.iot_controlhost.utils.log.UserLog;
import com.example.iot_controlhost.utils.log.MyLog;
import com.xuexiang.rxutil2.rxbus.RxBusUtils;
import android_serialport_api.SerialPortUtil;
@@ -124,10 +125,11 @@ public class Dehumidifier485Dialog extends BaseDialog<DialogDehumidifier485Bindi
public void doInUIThread(OperateResult result) {
if (result.success) {
successTips(dehumidifier.getState());
UserLog.operate("手动:操作485除湿机 " + dehumidifier.getState());
UserLog.operate("本机屏幕", "485除湿机操作成功:" + dehumidifier.getState());
} else {
errorTips(result.message);
UserLog.operateError("手动:操作485除湿机失败" + result.message);
UserLog.operateError("本机屏幕", "485除湿机操作失败");
MyLog.controllerError("485除湿机操作失败:" + result.message);
}
refreshDehumidifier();
DialogUtil.hideLoadingDialog();
@@ -94,13 +94,13 @@ public class FloorDialog extends BaseDialog<DialogFloorBinding> {
if (aBoolean) {
preGear = floor.getGear();
successTips(floor.getState());
UserLog.operate("手动:切换地暖档位" + floor.getGear() + "");
UserLog.operate("本机屏幕", "地暖档位切换成功" + floor.getGear() + "");
} else {
binding.rs.setValues(preGear);
if (gear > floor.getMaxGear()) {
errorTips("目标档位大于设定最大档位,切换失败");
} else {
UserLog.operateError("手动:切换地暖档位" + gear + "失败");
UserLog.operateError("本机屏幕", "地暖档位切换失败" + gear + "");
errorTips("切换档位失败");
}
}
@@ -13,83 +13,122 @@ import com.example.iot_controlhost.model.Log;
import com.example.iot_controlhost.ui.view.CustomSpinnerAdapter;
import com.example.iot_controlhost.utils.DialogUtil;
import com.example.iot_controlhost.utils.database.LogDBManager;
import com.example.iot_controlhost.utils.log.MyLog;
import com.example.iot_controlhost.utils.log.UserLog;
import com.xuexiang.rxutil2.rxjava.RxJavaUtils;
import com.xuexiang.rxutil2.rxjava.task.RxAsyncTask;
import java.util.Collections;
import java.util.List;
/**
* 体质弹窗
* 日志管理弹窗
*/
public class LogDialog extends BaseDialog<DialogLogBinding> {
/**
* 当前主界面日志所在页数
*/
private int logPage = 1;
List<Log> logs;
LogAdapter adapter;
private final LogAdapter adapter;
private String currentFilter = UserLog.FILTER_ALL;
private Log pageCursor;
private boolean loading;
private int queryGeneration;
public LogDialog(Context mContext, List<Log> logs) {
super(mContext);
this.logs = logs;
adapter = new LogAdapter(logs, ItemLogBinding.class);
//初始化界面控件
binding.rvLog.setLayoutManager(new LinearLayoutManager(mContext));
public LogDialog(Context context, List<Log> initialLogs) {
super(context);
List<Log> safeLogs = initialLogs == null ? Collections.emptyList() : initialLogs;
adapter = new LogAdapter(safeLogs, ItemLogBinding.class);
pageCursor = getLastLog(safeLogs);
binding.rvLog.setLayoutManager(new LinearLayoutManager(context));
binding.rvLog.setAdapter(adapter);
binding.acInput.setHint(mContext.getString(R.string.select_log_tag));
binding.acInput.setAdapter(new CustomSpinnerAdapter(mContext, MyLog.getUserQueryTag().toArray(new String[0])));
binding.refreshLayout.setOnRefreshListener(refreshlayout -> RxJavaUtils.executeAsyncTask(new RxAsyncTask<Object, List<Log>>(null) {
@Override
public List<Log> doInIOThread(Object o) {
logPage = 1;
return LogDBManager.queryList(binding.acInput.getText().toString(), logPage);
}
binding.acInput.setHint(context.getString(R.string.select_log_tag));
binding.acInput.setAdapter(new CustomSpinnerAdapter(
context, UserLog.getUserQueryFilters().toArray(new String[0])));
binding.acInput.setText(UserLog.FILTER_ALL, false);
@Override
public void doInUIThread(List<Log> logs) {
adapter.setDataList(logs);
if (logs.size() == 0) {
refreshlayout.finishLoadMoreWithNoMoreData();
} else {
refreshlayout.finishLoadMore();
}
refreshlayout.finishRefresh();
}
}));
binding.refreshLayout.setOnLoadMoreListener(refreshlayout -> RxJavaUtils.executeAsyncTask(new RxAsyncTask<Object, List<Log>>(null) {
@Override
public List<Log> doInIOThread(Object o) {
return LogDBManager.queryList(binding.acInput.getText().toString(), ++logPage);
}
binding.refreshLayout.setOnRefreshListener(refreshLayout -> refreshLogs(false));
binding.refreshLayout.setOnLoadMoreListener(refreshLayout -> loadNextPage());
binding.btnQuery.setOnClickListener(view -> refreshLogs(true));
}
@Override
public void doInUIThread(List<Log> logs) {
adapter.addDataList(logs);
if (logs.size() == 0) {
refreshlayout.finishLoadMoreWithNoMoreData();
} else {
refreshlayout.finishLoadMore();
}
}
}));
binding.btnQuery.setOnClickListener(view -> {
logPage = 1;
private void refreshLogs(boolean showLoading) {
if (loading) {
binding.refreshLayout.finishRefresh();
return;
}
loading = true;
int generation = ++queryGeneration;
String requestedFilter = getSelectedFilter();
binding.refreshLayout.resetNoMoreData();
if (showLoading) {
DialogUtil.showLoadingDialog(mContext, "正在读取日志...");
RxJavaUtils.executeAsyncTask(new RxAsyncTask<Object, List<Log>>(null) {
@Override
public List<Log> doInIOThread(Object o) {
return LogDBManager.queryList(binding.acInput.getText().toString(), logPage);
}
}
RxJavaUtils.executeAsyncTask(new RxAsyncTask<Object, List<Log>>(null) {
@Override
public List<Log> doInIOThread(Object o) {
return LogDBManager.queryLatest(requestedFilter);
}
@Override
public void doInUIThread(List<Log> logs) {
@Override
public void doInUIThread(List<Log> logs) {
if (generation == queryGeneration) {
currentFilter = requestedFilter;
pageCursor = getLastLog(logs);
adapter.setDataList(logs);
updateLoadMoreState(logs);
}
loading = false;
binding.refreshLayout.finishRefresh();
if (showLoading) {
DialogUtil.hideLoadingDialog();
}
});
}
});
}
private void loadNextPage() {
if (loading || pageCursor == null) {
binding.refreshLayout.finishLoadMoreWithNoMoreData();
return;
}
loading = true;
int generation = queryGeneration;
Log requestedCursor = pageCursor;
String requestedFilter = currentFilter;
RxJavaUtils.executeAsyncTask(new RxAsyncTask<Object, List<Log>>(null) {
@Override
public List<Log> doInIOThread(Object o) {
return LogDBManager.queryNext(requestedFilter, requestedCursor);
}
@Override
public void doInUIThread(List<Log> logs) {
if (generation == queryGeneration) {
if (!logs.isEmpty()) {
adapter.addDataList(logs);
pageCursor = getLastLog(logs);
}
updateLoadMoreState(logs);
} else {
binding.refreshLayout.finishLoadMore();
}
loading = false;
}
});
}
private void updateLoadMoreState(List<Log> logs) {
if (LogDBManager.isFullPage(logs)) {
binding.refreshLayout.finishLoadMore();
} else {
binding.refreshLayout.finishLoadMoreWithNoMoreData();
}
}
private String getSelectedFilter() {
String filter = binding.acInput.getText().toString().trim();
return filter.isEmpty() ? UserLog.FILTER_ALL : filter;
}
private Log getLastLog(List<Log> logs) {
return logs == null || logs.isEmpty() ? null : logs.get(logs.size() - 1);
}
}
@@ -18,6 +18,8 @@ import com.example.iot_controlhost.utils.global.AutoModelSet;
import com.example.iot_controlhost.utils.global.RxTag;
import com.example.iot_controlhost.utils.global.SpinnerList;
import com.example.iot_controlhost.utils.global.Variable;
import com.example.iot_controlhost.utils.log.UserLog;
import com.example.iot_controlhost.utils.log.MyLog;
import com.xuexiang.rxutil2.rxbus.RxBusUtils;
import java.util.List;
@@ -131,7 +133,7 @@ public class SetAutoParamDialog extends BaseDialog<DialogSetAutoBinding> {
binding.btnMinus1.setOnClickListener(getAutoSetAddAndMinusOnClickListener(binding.tiSet1, setType, false));
}
binding.btnYes.setOnClickListener(view -> {
double min = Double.parseDouble(binding.tiSet3.getText().toString());
double min = setType <= 2 ? Double.parseDouble(binding.tiSet3.getText().toString()) : 0;
double max = Double.parseDouble(binding.tiSet2.getText().toString());
double target = Double.parseDouble(binding.tiSet1.getText().toString());
boolean needRefresh = false;
@@ -146,10 +148,20 @@ public class SetAutoParamDialog extends BaseDialog<DialogSetAutoBinding> {
return;
}
//保存自动模式-温度设置
double oldTarget = AutoModelSet.getTargetTemperature();
double oldMax = AutoModelSet.getMaxTemperature();
double oldMin = AutoModelSet.getMinTemperature();
boolean oldEnabled = AutoModelSet.isAutoTemperature();
boolean enabled = binding.ivAuto.getDrawable().getConstantState()
.equals(context.getDrawable(R.mipmap.on).getConstantState());
MMKVUtil.put(AutoModelSet.TARGET_TEMPERATURE, target);
MMKVUtil.put(AutoModelSet.MAX_TEMPERATURE, max);
MMKVUtil.put(AutoModelSet.MIN_TEMPERATURE, min);
MMKVUtil.put(AutoModelSet.AUTO_TEMPERATURE, binding.ivAuto.getDrawable().getConstantState().equals(context.getDrawable(R.mipmap.on).getConstantState()));
MMKVUtil.put(AutoModelSet.AUTO_TEMPERATURE, enabled);
UserLog.operate("本机屏幕", "修改自动温度参数");
MyLog.app("自动温度参数修改:启用=" + oldEnabled + "" + enabled
+ ",目标=" + oldTarget + "" + target + "℃,上限=" + oldMax + "" + max
+ "℃,下限=" + oldMin + "" + min + "");
dismiss();
needRefresh = true;
break;
@@ -163,10 +175,20 @@ public class SetAutoParamDialog extends BaseDialog<DialogSetAutoBinding> {
return;
}
//保存自动模式-湿度设置
double humidityOldTarget = AutoModelSet.getTargetHumidity();
double humidityOldMin = AutoModelSet.getMinHumidity();
double humidityOldMax = AutoModelSet.getMaxHumidity();
boolean humidityOldEnabled = AutoModelSet.isAutoHumidity();
boolean humidityEnabled = binding.ivAuto.getDrawable().getConstantState()
.equals(context.getDrawable(R.mipmap.on).getConstantState());
MMKVUtil.put(AutoModelSet.TARGET_HUMIDITY, target);
MMKVUtil.put(AutoModelSet.MIN_HUMIDITY, min);
MMKVUtil.put(AutoModelSet.MAX_HUMIDITY, max);
MMKVUtil.put(AutoModelSet.AUTO_HUMIDITY, binding.ivAuto.getDrawable().getConstantState().equals(context.getDrawable(R.mipmap.on).getConstantState()));
MMKVUtil.put(AutoModelSet.AUTO_HUMIDITY, humidityEnabled);
UserLog.operate("本机屏幕", "修改自动湿度参数");
MyLog.app("自动湿度参数修改:启用=" + humidityOldEnabled + "" + humidityEnabled
+ ",目标=" + humidityOldTarget + "" + target + "% ,上限=" + humidityOldMax + "" + max
+ "% ,下限=" + humidityOldMin + "" + min + "%");
dismiss();
needRefresh = true;
}
@@ -178,10 +200,23 @@ public class SetAutoParamDialog extends BaseDialog<DialogSetAutoBinding> {
&& checkInput(context, binding.tvSet3, binding.tiSet3, 500, 3000)) {
//保存自动模式-换气设置
try {
MMKVUtil.put(AutoModelSet.CYCLE_START, Integer.parseInt(binding.tiSet1.getText().toString()));
MMKVUtil.put(AutoModelSet.CYCLE_STOP, Integer.parseInt(binding.tiSet2.getText().toString()));
MMKVUtil.put(AutoModelSet.MAX_DENSITY_CO2, Integer.parseInt(binding.tiSet3.getText().toString()));
MMKVUtil.put(AutoModelSet.AUTO_VENTILATOR, binding.ivAuto.getDrawable().getConstantState().equals(context.getDrawable(R.mipmap.on).getConstantState()));
int start = Integer.parseInt(binding.tiSet1.getText().toString());
int stop = Integer.parseInt(binding.tiSet2.getText().toString());
int maxCo2 = Integer.parseInt(binding.tiSet3.getText().toString());
int oldStart = AutoModelSet.getCycleStart();
int oldStop = AutoModelSet.getCycleStop();
int oldMaxCo2 = AutoModelSet.getMaxDensityCO2();
boolean ventilatorOldEnabled = AutoModelSet.isAutoVentilator();
boolean ventilatorEnabled = binding.ivAuto.getDrawable().getConstantState()
.equals(context.getDrawable(R.mipmap.on).getConstantState());
MMKVUtil.put(AutoModelSet.CYCLE_START, start);
MMKVUtil.put(AutoModelSet.CYCLE_STOP, stop);
MMKVUtil.put(AutoModelSet.MAX_DENSITY_CO2, maxCo2);
MMKVUtil.put(AutoModelSet.AUTO_VENTILATOR, ventilatorEnabled);
UserLog.operate("本机屏幕", "修改自动换气参数");
MyLog.app("自动换气参数修改:启用=" + ventilatorOldEnabled + "" + ventilatorEnabled
+ ",开启=" + oldStart + "" + start + "分钟,关闭=" + oldStop + ""
+ stop + "分钟,CO₂上限=" + oldMaxCo2 + "" + maxCo2 + "ppm");
dismiss();
needRefresh = true;
} catch (NumberFormatException e) {
@@ -206,10 +241,15 @@ public class SetAutoParamDialog extends BaseDialog<DialogSetAutoBinding> {
}
}
AITHMode mode = MyAIModeUtil.getMyAiMode().get(age);
double oldTemperature = mode.getTargetTemp();
double oldHumidity = mode.getTargetHumi();
//修改当前龄期的湿度
mode.setTargetTemp(temperature);
mode.setTargetHumi(humidity);
MyAIModeUtil.update(mode);
UserLog.operate("本机屏幕", "修改智能模式当前龄期温湿度参数");
MyLog.app("智能模式当前龄期参数修改:温度=" + oldTemperature + "" + temperature
+ "℃,湿度=" + oldHumidity + "" + humidity + "%");
RxBusUtils.get().post(RxTag.AI_INFO, 0);
dismiss();
needRefresh = true;
@@ -232,9 +272,14 @@ public class SetAutoParamDialog extends BaseDialog<DialogSetAutoBinding> {
}
}
AITHMode mode = MyAIModeUtil.getMyAiMode().get(age);
int oldStart = mode.getTargetVentOpen();
int oldStop = mode.getTargetVentClose();
mode.setTargetVentOpen(start);
mode.setTargetVentClose(stop);
MyAIModeUtil.update(mode);
UserLog.operate("本机屏幕", "修改智能模式当前龄期换气参数");
MyLog.app("智能模式当前龄期换气参数修改:开启=" + oldStart + "" + start
+ "分钟,关闭=" + oldStop + "" + stop + "分钟");
RxBusUtils.get().post(RxTag.AI_INFO, 0);
dismiss();
needRefresh = true;
@@ -26,6 +26,8 @@ import com.example.iot_controlhost.utils.global.RoomSetting;
import com.example.iot_controlhost.utils.global.RxTag;
import com.example.iot_controlhost.utils.global.SpinnerList;
import com.example.iot_controlhost.utils.global.Variable;
import com.example.iot_controlhost.utils.log.UserLog;
import com.example.iot_controlhost.utils.log.MyLog;
import com.example.iot_controlhost.utils.timer.ThemeTaskUtil;
import com.example.iot_controlhost.utils.timer.UVTaskUtil;
import com.xuexiang.rxutil2.rxbus.RxBusUtils;
@@ -77,7 +79,9 @@ public class SetBaseDialog extends BaseDialog<DialogSetBinding> {
new ConfirmDialog(mContext, mContext.getString(R.string.tip), "时间不可小于5s,请重新输入", null).show();
return;
}
long oldTime = RoomSetting.getMainActivityOperateLockTimeOut();
MMKVUtil.put(RoomSetting.MAIN_ACTIVITY_OPERATE_LOCK_TIMEOUT, time);
logSettingChanged("自动锁屏等待时间", oldTime + "", time + "");
setMain();
} catch (Exception e) {
new ConfirmDialog(mContext, mContext.getString(R.string.tip), "输入格式有误,设置失败", null).show();
@@ -85,12 +89,16 @@ public class SetBaseDialog extends BaseDialog<DialogSetBinding> {
}, true).show()));
setList.add(new Set(mContext.getString(R.string.notice_type), RoomSetting.getNoticeType(), view ->
new SpinnerDialog(mContext, mContext.getString(R.string.notice_type), "请选择通知类型", RoomSetting.NOTICE_TYPE_SPINNER, sel -> {
String oldValue = RoomSetting.getNoticeType();
MMKVUtil.put(RoomSetting.NOTICE_TYPE, sel);
logSettingChanged("通知类型", oldValue, sel);
setMain();
}).show()));
setList.add(new Set(mContext.getString(R.string.light_night),
RoomSetting.getAutoLightNight() ? "根据日出日落时间自动设置" : RoomSetting.isDarkTheme() ? "总是暗色调" : "总是亮色调",
v2 -> new SpinnerDialog(mContext, mContext.getString(R.string.light_night), "请选择色调调节方式", new String[]{"总是亮色调", "总是暗色调", "根据日出日落时间自动设置"}, sel -> {
String oldValue = RoomSetting.getAutoLightNight() ? "根据日出日落时间自动设置"
: RoomSetting.isDarkTheme() ? "总是暗色调" : "总是亮色调";
boolean isDark;
if ("根据日出日落时间自动设置".equals(sel)) {
MMKVUtil.put(RoomSetting.AUTO_LIGHT_NIGHT, true);
@@ -101,6 +109,7 @@ public class SetBaseDialog extends BaseDialog<DialogSetBinding> {
}
dismiss();
this.dismiss();
logSettingChanged("界面色调", oldValue, sel);
MyUtil.switchDarkMode(isDark);
}).show()));
setList.add(new Set(getString(R.string.set_password_main_lock), "******", view -> new PasswordDialog(mContext, "请先输入原锁屏密码",
@@ -116,11 +125,14 @@ public class SetBaseDialog extends BaseDialog<DialogSetBinding> {
return;
}
MMKVUtil.put(RoomSetting.MAIN_ACTIVITY_LOCK, ti2.getText().toString());
UserLog.operate("本机屏幕", "锁屏密码修改成功(密码内容未记录)");
Toasty.success(mContext, "锁屏新密码设置成功").show();
}).show();
}).show()));
setList.add(new Set("消毒作业期间自动关闭空调", RoomSetting.isAirConditionForceCloseEnable(), (compoundButton, b) -> {
boolean oldValue = RoomSetting.isAirConditionForceCloseEnable();
MMKVUtil.put(RoomSetting.IS_AIR_CONDITION_FORCE_CLOSE_ENABLE, b);
logSettingChanged("消毒作业期间自动关闭空调", oldValue, b);
setMain();
}));
setList.add(new Set(mContext.getString(R.string.log), view -> showLogDialog()));
@@ -135,16 +147,21 @@ public class SetBaseDialog extends BaseDialog<DialogSetBinding> {
setList.add(new Set("返回上一级", view -> setMain()));
setList.add(new Set("升温设备", AutoModelSet.getAutoTemperatureHeat(), view ->
new SpinnerDialog(mContext, mContext.getString(R.string.temperature_control_heat), "请选择升温设备", SpinnerList.TEMPERATURE_HEAT, selection -> {
String oldValue = AutoModelSet.getAutoTemperatureHeat();
MMKVUtil.put(AutoModelSet.AUTO_TEMPERATURE_HEAT, selection);
logSettingChanged("自动升温设备", oldValue, selection);
updateTemperatureControl();
}).show()));
setList.add(new Set("降温设备", AutoModelSet.getAutoTemperatureCool(), view ->
new SpinnerDialog(mContext, mContext.getString(R.string.temperature_control_heat), "请选择降温设备", SpinnerList.TEMPERATURE_COOL, selection -> {
String oldValue = AutoModelSet.getAutoTemperatureCool();
MMKVUtil.put(AutoModelSet.AUTO_TEMPERATURE_COOL, selection);
logSettingChanged("自动降温设备", oldValue, selection);
updateTemperatureControl();
}).show()));
setList.add(new Set("温度控制模式", AutoModelSet.getAutoTemperatureMode(), view ->
new SpinnerDialog(mContext, mContext.getString(R.string.temperature_control_mode), "请选择温控模式", SpinnerList.TEMPERATURE_MODE, selection -> {
String oldMode = AutoModelSet.getAutoTemperatureMode();
double target = AutoModelSet.getTargetTemperature();
double min = AutoModelSet.getMinTemperature();
double max = AutoModelSet.getMaxTemperature();
@@ -159,6 +176,7 @@ public class SetBaseDialog extends BaseDialog<DialogSetBinding> {
heatMode = SpinnerList.TEMPERATURE_HEAT[2];
} else {
MMKVUtil.put(AutoModelSet.AUTO_TEMPERATURE_MODE, selection);
logSettingChanged("温度控制模式", oldMode, selection);
updateTemperatureControl();
return;
}
@@ -168,11 +186,15 @@ public class SetBaseDialog extends BaseDialog<DialogSetBinding> {
MMKVUtil.put(AutoModelSet.AUTO_TEMPERATURE_HEAT, heatMode);
MMKVUtil.put(AutoModelSet.MIN_TEMPERATURE, target - bias);
MMKVUtil.put(AutoModelSet.MAX_TEMPERATURE, target + bias);
UserLog.operate("本机屏幕", "修改设置:温度控制模式");
MyLog.app("温度控制模式修改:" + oldMode + "" + selection
+ ",自动调整目标上下限为" + (target - bias) + "~" + (target + bias) + "");
updateTemperatureControl();
}).show();
} else {
MMKVUtil.put(AutoModelSet.AUTO_TEMPERATURE_MODE, selection);
MMKVUtil.put(AutoModelSet.AUTO_TEMPERATURE_HEAT, heatMode);
logSettingChanged("温度控制模式", oldMode, selection);
updateTemperatureControl();
}
}).show()
@@ -185,24 +207,32 @@ public class SetBaseDialog extends BaseDialog<DialogSetBinding> {
}));
setList.add(new Set("匀风扇控制-启动时机", AutoModelSet.getAutoEvenMode(), view ->
new SpinnerDialog(mContext, "匀风扇控制", "请选择匀风扇启动时机", SpinnerList.AUTO_EVEN_MODES, selection -> {
String oldValue = AutoModelSet.getAutoEvenMode();
MMKVUtil.put(AutoModelSet.AUTO_EVEN_MODE, selection);
logSettingChanged("匀风扇启动时机", oldValue, selection);
RxBusUtils.get().post(RxTag.UPDATE_AUTO, 3);
setAutoParam();
}).show()));
setList.add(new Set("匀风扇控制-在加湿期间不运行", AutoModelSet.isAutoEvenModeDuringHumidify()?"" : "", view ->
new SpinnerDialog(mContext, "匀风扇在加湿期间不运行", "请选择", SpinnerList.YES_OR_NO, selection -> {
boolean oldValue = AutoModelSet.isAutoEvenModeDuringHumidify();
MMKVUtil.put(AutoModelSet.AUTO_EVEN_MODE_DURING_HUMIDIFY, selection.equals(""));
logSettingChanged("匀风扇加湿期间停运", oldValue, selection.equals(""));
RxBusUtils.get().post(RxTag.UPDATE_AUTO, 3);
setAutoParam();
}).show()));
setList.add(new Set("换气扇控制", AutoModelSet.getVentilatorMode(), view ->
new SpinnerDialog(mContext, "换气扇控制", "请选择换气扇控制方法", SpinnerList.VENTILATION_MODES, selection -> {
String oldValue = AutoModelSet.getVentilatorMode();
MMKVUtil.put(AutoModelSet.VENTILATOR_MODE, selection);
logSettingChanged("换气扇控制方式", oldValue, selection);
setAutoParam();
}).show()));
setList.add(new Set("新风控制", AutoModelSet.getAirExchangeMode(), view ->
new SpinnerDialog(mContext, "新风控制", "请选择新风控制方法", SpinnerList.AIR_EXCHANGE_MODES, selection -> {
String oldValue = AutoModelSet.getAirExchangeMode();
MMKVUtil.put(AutoModelSet.AIR_EXCHANGE_MODE, selection);
logSettingChanged("新风控制方式", oldValue, selection);
setAutoParam();
}).show()));
setList.add(new Set("地暖最大温度限制", AutoModelSet.getAutoFlorMaxTemp() + "", view ->
@@ -213,7 +243,9 @@ public class SetBaseDialog extends BaseDialog<DialogSetBinding> {
new ConfirmDialog(mContext, mContext.getString(R.string.tip), "地暖温度区间需为35~45℃,请重新设置", null).show();
return;
}
double oldTemp = AutoModelSet.getAutoFlorMaxTemp();
MMKVUtil.put(AutoModelSet.AUTO_FLOOR_MAX_TEMP, temp);
logSettingChanged("地暖最大温度限制", oldTemp + "", temp + "");
setAutoParam();
} catch (Exception e) {
new ConfirmDialog(mContext, mContext.getString(R.string.tip), "输入格式有误,设置失败", null).show();
@@ -250,7 +282,7 @@ public class SetBaseDialog extends BaseDialog<DialogSetBinding> {
RxJavaUtils.executeAsyncTask(new RxAsyncTask<Object, List<Log>>(null) {
@Override
public List<Log> doInIOThread(Object o) {
return LogDBManager.queryList(null, 1);
return LogDBManager.queryLatest(UserLog.FILTER_ALL);
}
@Override
@@ -260,4 +292,9 @@ public class SetBaseDialog extends BaseDialog<DialogSetBinding> {
}
});
}
private void logSettingChanged(String name, Object oldValue, Object newValue) {
UserLog.operate("本机屏幕", "修改设置:" + name);
MyLog.app("设置修改:" + name + "=" + oldValue + "" + newValue);
}
}
@@ -9,6 +9,8 @@ import com.example.iot_controlhost.base.BaseDialog;
import com.example.iot_controlhost.databinding.DialogDeviceCustomSetBinding;
import com.example.iot_controlhost.ui.activity.SetActivity;
import com.example.iot_controlhost.ui.view.CustomSpinnerAdapter;
import com.example.iot_controlhost.utils.log.UserLog;
import com.example.iot_controlhost.utils.log.MyLog;
import es.dmoral.toasty.Toasty;
@@ -50,6 +52,9 @@ public class SetDeviceBaseDialog extends BaseDialog<DialogDeviceCustomSetBinding
return;
}
listener.btnSet(binding.acInput1.getText().toString(), binding.acInput2.getText().toString());
UserLog.operate("本机屏幕", "修改设备配置:" + title);
MyLog.app(title + "修改:" + ac1Hint + "=" + binding.acInput1.getText()
+ "" + ac2Hint + "=" + binding.acInput2.getText());
dismiss();
});
}
@@ -11,6 +11,8 @@ import com.example.iot_controlhost.R;
import com.example.iot_controlhost.base.BaseDialog;
import com.example.iot_controlhost.databinding.DialogDeviceCustomSetBinding;
import com.example.iot_controlhost.utils.global.RoomController;
import com.example.iot_controlhost.utils.log.UserLog;
import com.example.iot_controlhost.utils.log.MyLog;
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
@@ -44,7 +46,10 @@ public class SetPWMDialog extends BaseDialog<DialogDeviceCustomSetBinding> {
Toasty.error(mContext, "PWM大于当前地暖型号支持的最大PWM").show();
return;
}
int oldPwm = RoomController.floor.getPwm(gear).getPwm();
RoomController.floor.getPwm(gear).setPwm(pwm);
UserLog.operate("本机屏幕", "修改地暖PWM" + gear + "");
MyLog.app("地暖PWM修改:档位=" + gear + "PWM=" + oldPwm + "" + pwm);
} catch (Exception e) {
Toasty.error(mContext, "输入格式有误,请检查").show();
return;
@@ -13,6 +13,8 @@ import com.example.iot_controlhost.model.controller.relay.Relay;
import com.example.iot_controlhost.ui.view.CustomSpinnerAdapter;
import com.example.iot_controlhost.utils.MyUtil;
import com.example.iot_controlhost.utils.global.RoomController;
import com.example.iot_controlhost.utils.log.UserLog;
import com.example.iot_controlhost.utils.log.MyLog;
import java.util.Arrays;
import java.util.HashSet;
@@ -64,7 +66,8 @@ public class SetPortDialog extends BaseDialog<DialogDeviceCustomSetBinding> {
binding.btnYes.setText(mContext.getText(R.string.set));
binding.btnYes.setOnClickListener(view -> {
//移除之前设备的端口设置
Relay relay = RoomController.getRelay(MyUtil.getPortToDeviceName(port));
String oldDeviceName = MyUtil.getPortToDeviceName(port);
Relay relay = RoomController.getRelay(oldDeviceName);
if (relay != null) {
relay.removePort(port);
} else {
@@ -82,6 +85,9 @@ public class SetPortDialog extends BaseDialog<DialogDeviceCustomSetBinding> {
}
//确定后关闭弹窗
RoomController.relay.setPowerSupply(false, port);
UserLog.operate("本机屏幕", "修改继电器端口配置:端口" + port);
MyLog.app("继电器端口配置修改:端口=" + port + ",设备=" + oldDeviceName
+ "" + (deviceName.isEmpty() ? "未配置" : deviceName));
dismiss();
afterSetRelay.run();
});
@@ -13,6 +13,8 @@ import com.example.iot_controlhost.model.thread.QueueIOUITask;
import com.example.iot_controlhost.ui.view.CustomSpinnerAdapter;
import com.example.iot_controlhost.utils.MyQueue;
import com.example.iot_controlhost.utils.global.SpinnerList;
import com.example.iot_controlhost.utils.log.UserLog;
import com.example.iot_controlhost.utils.log.MyLog;
import android_serialport_api.SerialPortUtil;
@@ -87,6 +89,9 @@ public class SetSensorDialog extends BaseDialog<DialogSetSensorBinding> {
sensor.setEnable(enable);
//刷新页面
listener.run(sensor);
UserLog.operate("本机屏幕", "修改传感器配置:" + sensor.getName());
MyLog.app("传感器配置修改:名称=" + sensor.getName() + ",启用=" + sensor.isEnable()
+ ",型号=" + sensor.getModel() + ",地址=" + sensor.getAddress());
dismiss();
});
}
@@ -95,4 +100,4 @@ public class SetSensorDialog extends BaseDialog<DialogSetSensorBinding> {
void run(Sensor sensor);
}
}
}
@@ -25,6 +25,8 @@ import com.example.iot_controlhost.utils.global.RxTag;
import com.example.iot_controlhost.utils.network.Url;
import com.example.iot_controlhost.utils.network.XHttpManager;
import com.example.iot_controlhost.utils.network.XHttpShorthand;
import com.example.iot_controlhost.utils.log.UserLog;
import com.example.iot_controlhost.utils.log.MyLog;
import com.xuexiang.rxutil2.rxbus.RxBusUtils;
import org.xutils.http.RequestParams;
@@ -143,6 +145,10 @@ public class StartCultivateDialog extends BaseDialog<DialogStartCoeducationBindi
public void success(CommonResponse model) {
//记录此次蚕期
SilkwormDBManager.INSTANCE.init(new SilkwormNew(model.getData().toString(), season, startTime, null, number, variety));
String cultivateType = RoomSetting.getType() == 4 ? "催青" : "共育";
UserLog.task("开始" + cultivateType + "登记:批次=" + season);
MyLog.app("开始" + cultivateType + "登记成功:批次=" + season + ",品种=" + variety
+ ",数量=" + number + ",起始龄期=" + age + "");
//开始共育后切换为自动模式
dismiss();
RxBusUtils.get().post(RxTag.UPDATE_MAIN, 7);
@@ -151,11 +157,17 @@ public class StartCultivateDialog extends BaseDialog<DialogStartCoeducationBindi
@Override
public void onSuccessError(CommonResponse data) {
String cultivateType = RoomSetting.getType() == 4 ? "催青" : "共育";
UserLog.taskError("开始" + cultivateType + "登记失败");
MyLog.networkError("开始" + cultivateType + "登记失败:" + data.getMessage());
errorTips("服务器-开始"+(RoomSetting.getType() == 4 ? "催青" : "共育")+"失败:" + data.getMessage());
}
@Override
public void error(Throwable e) {
String cultivateType = RoomSetting.getType() == 4 ? "催青" : "共育";
UserLog.taskError("开始" + cultivateType + "登记失败");
MyLog.networkError("开始" + cultivateType + "登记网络请求失败:" + e.getMessage());
errorTips("开始"+(RoomSetting.getType() == 4 ? "催青" : "共育")+"网络请求失败");
}
@@ -16,6 +16,8 @@ import com.example.iot_controlhost.utils.MMKVUtil;
import com.example.iot_controlhost.utils.MyUtil;
import com.example.iot_controlhost.utils.global.AutoModelSet;
import com.example.iot_controlhost.utils.global.RoomSetting;
import com.example.iot_controlhost.utils.log.UserLog;
import com.example.iot_controlhost.utils.log.MyLog;
/**
* @Description 文本输入弹窗
@@ -37,7 +39,12 @@ public class THBiasDialog extends BaseDialog<DialogBiasThBinding> {
binding.btnYes.setOnClickListener(view -> {
InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
MMKVUtil.put(isTemp ? RoomSetting.BIAS_TEMP : RoomSetting.BIAS_HUMIDITY, Double.parseDouble(binding.tiSet1.getText().toString()));
double oldValue = isTemp ? RoomSetting.getBiasTemp() : RoomSetting.getBiasHumidity();
double newValue = Double.parseDouble(binding.tiSet1.getText().toString());
MMKVUtil.put(isTemp ? RoomSetting.BIAS_TEMP : RoomSetting.BIAS_HUMIDITY, newValue);
String typeName = isTemp ? "温度" : "湿度";
UserLog.operate("本机屏幕", "修改" + typeName + "校准偏差");
MyLog.app(typeName + "校准偏差修改:" + oldValue + "" + newValue);
dismiss();
});
}
@@ -14,6 +14,7 @@ import com.example.iot_controlhost.utils.MMKVUtil;
import com.example.iot_controlhost.utils.MyUtil;
import com.example.iot_controlhost.utils.global.AutoModelSet;
import com.example.iot_controlhost.utils.global.RxTag;
import com.example.iot_controlhost.utils.log.UserLog;
import com.xuexiang.rxutil2.rxbus.RxBusUtils;
import java.util.List;
@@ -34,7 +35,9 @@ public class UvTaskDialog extends BaseDialog<DialogUvtaskBinding> {
MyUtil.changeIcon(context, binding.ivAuto, AutoModelSet.isAutoDisinfect(), binding.llAutoSetParam,binding.llAutoSetParamNone);
binding.ivAuto.setOnClickListener(v -> {
//修改自动模式-消毒灯计划
MMKVUtil.put(AutoModelSet.AUTO_DISINFECT, !AutoModelSet.isAutoDisinfect());
boolean oldValue = AutoModelSet.isAutoDisinfect();
MMKVUtil.put(AutoModelSet.AUTO_DISINFECT, !oldValue);
UserLog.operate("本机屏幕", (!oldValue ? "启用" : "停用") + "自动消毒计划");
MyUtil.changeIcon(context, binding.ivAuto, AutoModelSet.isAutoDisinfect(), binding.llAutoSetParam,binding.llAutoSetParamNone);
RxBusUtils.get().post(RxTag.UPDATE_AUTO, 3);
});
@@ -18,6 +18,7 @@ import com.example.iot_controlhost.utils.MyUtil;
import com.example.iot_controlhost.utils.control.disinfect.DisinfectController;
import com.example.iot_controlhost.utils.global.RoomController;
import com.example.iot_controlhost.utils.timer.UVTaskUtil;
import com.example.iot_controlhost.utils.log.UserLog;
import java.util.Date;
@@ -62,6 +63,7 @@ public class UvTaskInsertDialog extends BaseDialog<DialogUvtaskInsertBinding> {
DisinfectController.isBetween(task.getStartTimeHour(), task.getStartTimeMinute(), task.getEndTimeHour(), task.getEndTimeMinute()));
MyQueue.getInstance(MyQueue.TYPE_CONTROLLER).addTask(new QueueIOTask(() -> MyUtil.autoControlOperateThird(RoomController.uvLight, target)));
adapter.setDataList(UVTaskUtil.getUvTask());
UserLog.operate("本机屏幕", "新增消毒计划成功:" + startTimeStr + "" + endTimeStr);
dismiss();
}
});
@@ -31,6 +31,7 @@ import com.example.iot_controlhost.utils.global.RoomController;
import com.example.iot_controlhost.utils.global.RoomSetting;
import com.example.iot_controlhost.utils.global.RxTag;
import com.example.iot_controlhost.utils.log.UserLog;
import com.example.iot_controlhost.utils.log.MyLog;
import com.example.iot_controlhost.utils.network.Url;
import com.example.iot_controlhost.utils.network.XHttpManager;
import com.example.iot_controlhost.utils.network.XHttpShorthand;
@@ -281,6 +282,7 @@ public class WorkDialog extends BaseDialog<DialogWorkBinding> {
DialogUtil.hideLoadingDialog();
//记录此次蚕期
SilkwormDBManager.updateCurDate();
UserLog.task("结束" + (RoomSetting.getType() == 4 ? "催青" : "共育") + "登记成功");
//停止共育后刷新共育状态
RxBusUtils.get().post(RxTag.UPDATE_MAIN, 7);
// MyUtil.relaunchApp();
@@ -288,6 +290,9 @@ public class WorkDialog extends BaseDialog<DialogWorkBinding> {
@Override
public void error(Throwable e) {
String cultivateType = RoomSetting.getType() == 4 ? "催青" : "共育";
UserLog.taskError("结束" + cultivateType + "登记失败");
MyLog.networkError("结束" + cultivateType + "登记失败:" + e.getMessage());
errorTips("服务器-结束失败");
}
@@ -43,6 +43,7 @@ import com.example.iot_controlhost.utils.global.RoomController;
import com.example.iot_controlhost.utils.global.RoomSetting;
import com.example.iot_controlhost.utils.global.RxTag;
import com.example.iot_controlhost.utils.log.UserLog;
import com.example.iot_controlhost.utils.log.MyLog;
import com.example.iot_controlhost.utils.network.Url;
import com.example.iot_controlhost.utils.network.XHttpManager;
import com.example.iot_controlhost.utils.network.XHttpShorthand;
@@ -90,14 +91,16 @@ public class WorkNewDialog extends BaseDialog<DialogNewWorkBinding> {
});
binding.btnStopStage.setOnClickListener(v -> {
// 结束阶段
String stoppedStage = binding.tvStageCurrent.getText().toString();
binding.tvStageCurrent.setText("");
binding.tvStageContent.setText("");
if (RoomSetting.getMode() != 0) {
// 进入手动模式
RxBusUtils.get().post(RxTag.UPDATE_MAIN, 3);
RxBusUtils.get().post(RxTag.UPDATE_MAIN, 5);
}
RxBusUtils.get().post(RxTag.UPDATE_MAIN, 5);
TempTHVDBManager.INSTANCE.setStage(null, null);
UserLog.task("结束作业阶段:" + (stoppedStage.isEmpty() ? "未命名阶段" : stoppedStage));
MyLog.app("结束作业阶段,控制模式切换为手动模式:" + stoppedStage);
});
binding.btnStopWork.setOnClickListener(v -> {
// 结束作业
@@ -177,7 +180,7 @@ public class WorkNewDialog extends BaseDialog<DialogNewWorkBinding> {
@Override
public void error(Throwable e) {
errorTips("作业列表获取失败,请检查网络");
UserLog.taskError("作业列表获取失败" + e.getMessage());
MyLog.networkError("作业列表获取失败" + e.getMessage());
}
@Override
@@ -200,7 +203,7 @@ public class WorkNewDialog extends BaseDialog<DialogNewWorkBinding> {
@Override
public void error(Throwable e) {
errorTips("阶段列表获取失败,请检查网络");
UserLog.taskError("阶段列表获取失败" + e.getMessage());
MyLog.networkError("阶段列表获取失败" + e.getMessage());
}
@Override
@@ -338,6 +341,7 @@ public class WorkNewDialog extends BaseDialog<DialogNewWorkBinding> {
DialogUtil.hideLoadingDialog();
//记录此次蚕期
SilkwormDBManager.updateCurDate();
UserLog.task("结束" + (RoomSetting.getType() == 4 ? "催青" : "共育") + "登记成功");
//停止共育后刷新共育状态
RxBusUtils.get().post(RxTag.UPDATE_MAIN, 7);
// MyUtil.relaunchApp();
@@ -345,6 +349,9 @@ public class WorkNewDialog extends BaseDialog<DialogNewWorkBinding> {
@Override
public void error(Throwable e) {
String cultivateType = RoomSetting.getType() == 4 ? "催青" : "共育";
UserLog.taskError("结束" + cultivateType + "登记失败");
MyLog.networkError("结束" + cultivateType + "登记失败:" + e.getMessage());
errorTips("服务器-结束失败");
}
@@ -24,6 +24,8 @@ import com.example.iot_controlhost.utils.database.SilkwormDBManager;
import com.example.iot_controlhost.utils.global.AIModelSet;
import com.example.iot_controlhost.utils.global.RoomController;
import com.example.iot_controlhost.utils.global.RoomSetting;
import com.example.iot_controlhost.utils.log.UserLog;
import com.example.iot_controlhost.utils.log.MyLog;
import com.example.iot_controlhost.utils.timer.UVTaskUtil;
import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.Legend;
@@ -147,6 +149,7 @@ public class IntelligenceFragment extends BaseFragment<FragmentModelIntelligence
@Override
public Object doInQueueThread() {
stopMode();
presenter.confirmPauseCultivate();
return null;
}
@@ -158,12 +161,18 @@ public class IntelligenceFragment extends BaseFragment<FragmentModelIntelligence
}).show();
case 2 -> //退回半天
new ConfirmDialog(mContext, getString(R.string.warning), "确定将"+name+"计划返回至12小时前吗", () -> {
int oldBias = AIModelSet.getAiBiasTime();
MMKVUtil.put(AIModelSet.AI_BIAS_TIME, AIModelSet.getAiBiasTime() - 12);
UserLog.operate("本机屏幕", name + "计划后移12小时");
MyLog.app(name + "计划偏移量修改:" + oldBias + "" + AIModelSet.getAiBiasTime() + "小时");
refreshInfo();
}).show();
case 3 -> //提前半天
new ConfirmDialog(mContext, getString(R.string.warning), "确定将"+name+"计划提前到12小时后吗", () -> {
int oldBias = AIModelSet.getAiBiasTime();
MMKVUtil.put(AIModelSet.AI_BIAS_TIME, AIModelSet.getAiBiasTime() + 12);
UserLog.operate("本机屏幕", name + "计划前移12小时");
MyLog.app(name + "计划偏移量修改:" + oldBias + "" + AIModelSet.getAiBiasTime() + "小时");
refreshInfo();
}).show();
case 4 -> {//消毒
@@ -359,4 +368,4 @@ public class IntelligenceFragment extends BaseFragment<FragmentModelIntelligence
return set;
}
}
}
@@ -22,6 +22,8 @@ import com.example.iot_controlhost.utils.database.MyAIModeUtil;
import com.example.iot_controlhost.utils.global.AIModelSet;
import com.example.iot_controlhost.utils.global.RoomSetting;
import com.example.iot_controlhost.utils.global.RxTag;
import com.example.iot_controlhost.utils.log.UserLog;
import com.example.iot_controlhost.utils.log.MyLog;
import com.example.iot_controlhost.utils.network.Url;
import com.example.iot_controlhost.utils.network.XHttpManager;
import com.example.iot_controlhost.utils.network.XHttpShorthand;
@@ -138,10 +140,17 @@ public class IntelligenceFragmentPresenter extends BasePresenter {
noticeLiveData.setValue(1);
} else {
//要进行
MMKVUtil.put(AIModelSet.AI_PROCESSING, true);
UserLog.operate("本机屏幕", "智能模式恢复运行");
startAICultivate();
noticeLiveData.setValue(8);
}
MMKVUtil.put(AIModelSet.AI_PROCESSING, !AIModelSet.isAiProcessing());
noticeLiveData.setValue(8);
}
public void confirmPauseCultivate() {
MMKVUtil.put(AIModelSet.AI_PROCESSING, false);
UserLog.operate("本机屏幕", "智能模式暂停成功,所有设备已关闭");
noticeLiveData.postValue(8);
}
public void notice(int type) {
@@ -164,12 +173,15 @@ public class IntelligenceFragmentPresenter extends BasePresenter {
AITHModeDBManager.insert(modes);
MyAIModeUtil.initMyAiMode(AITHModeDBManager.getAllSilkworms());
MMKVUtil.put(AIModelSet.AI_BIAS_TIME, 0);
UserLog.operate("本机屏幕", "智能模式模板刷新成功:" + data.getProgramName());
notice(7);
}
@Override
public void error(Throwable e) {
e.printStackTrace();
UserLog.operateError("本机屏幕", "智能模式模板刷新失败");
MyLog.networkError("智能模式模板刷新失败:" + e.getMessage());
errorTips("模板获取失败,错误信息:\n" + e.getMessage());
}
@@ -23,7 +23,7 @@ import com.example.iot_controlhost.utils.global.RoomController;
import com.example.iot_controlhost.utils.global.RoomSensor;
import com.example.iot_controlhost.utils.global.RoomSetting;
import com.example.iot_controlhost.utils.global.Variable;
import com.example.iot_controlhost.utils.log.UserLog;
import com.example.iot_controlhost.utils.log.MyLog;
import java.util.List;
@@ -81,13 +81,13 @@ public class ManualFragment extends BaseFragment<FragmentModelManualBinding, Man
@Override
public void initMode() {
UserLog.operate("进入手动模式");
MyLog.app("模式生命周期:进入手动模式");
refreshManualView();
}
@Override
public void stopMode() {
UserLog.operate("退出手动模式关闭所有设备");
MyLog.app("模式生命周期:退出手动模式关闭所有设备");
RoomController.closeAllControllers();
}
@@ -240,7 +240,11 @@ public class MyUtil {
* 获取当前时间
*/
public static String getDateTime() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
return getDateTime(new Date());
}
public static String getDateTime(Date date) {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
}
/**
@@ -271,7 +275,7 @@ public class MyUtil {
* 关闭APP
*/
public static void exitApp() {
UserLog.system("关闭APP");
UserLog.system("本机屏幕", "关闭软件");
MyUtil.stopAppControl("正在关闭软件中", () -> AppUtils.exitApp());
}
@@ -279,7 +283,11 @@ public class MyUtil {
* 重启APP
*/
public static void relaunchApp() {
UserLog.system("重启APP");
relaunchApp("本机屏幕");
}
public static void relaunchApp(String source) {
UserLog.system(source, "重启软件");
MyUtil.stopAppControl("正在重启软件中", () -> {
// AppUtils.relaunchApp(); // 这个方法重启不干净
ProcessPhoenix.triggerRebirth(MyApp.getAppContext());
@@ -290,7 +298,11 @@ public class MyUtil {
* 关闭设备
*/
public static void shutDown() {
UserLog.system("关闭设备");
shutDown("本机屏幕");
}
public static void shutDown(String source) {
UserLog.system(source, "关闭设备");
//先关闭所有设备
MyUtil.stopAppControl("正在关闭设备中", () -> MyAPIContext.getInstance().shutDown());
}
@@ -299,7 +311,11 @@ public class MyUtil {
* 重启设备
*/
public static void reboot() {
UserLog.system("重启设备");
reboot("本机屏幕");
}
public static void reboot(String source) {
UserLog.system(source, "重启设备");
MyUtil.stopAppControl("正在重启设备中", () -> MyAPIContext.getInstance().reBoot());
}
@@ -362,7 +378,6 @@ public class MyUtil {
if (controller.isPowerSupply() != targetState) {
// 只有在状态不一致时方才输出日志
MyLog.auto((needAutoLog ? "自动:" : "") + (targetState ? "开启" : "关闭") + controller.getName());
UserLog.operate((needAutoLog ? "自动:" : "") + (targetState ? "开启" : "关闭") + controller.getName());
}
while (count < maxCount) {
count++; // 将计数器递增放在循环开始处
@@ -373,8 +388,8 @@ public class MyUtil {
}
}
if (!success) {
MyLog.auto((needAutoLog ? "自动" : "") + "自动操作(连续" + maxCount + "次)失败:" + (targetState ? "开启" : "关闭") + controller.getName());
UserLog.operateError((needAutoLog ? "自动" : "") + "操作(连续" + maxCount + "次)失败:" + (targetState ? "开启" : "关闭") + controller.getName());
MyLog.autoError((needAutoLog ? "自动" : "系统联动") + "操作(连续" + maxCount + "次)失败:"
+ (targetState ? "开启" : "关闭") + controller.getName());
}
//自动操作完设备需要更新UI
RxBusUtils.get().post(RxTag.UPDATE_AUTO, 2);
@@ -451,17 +466,38 @@ public class MyUtil {
}
public static void relaunchFrp(){
Context mContext = ActivityUtils.getTopActivity();
Intent intent = new Intent();
intent.setAction("receiver_control");
intent.putExtra("control", -1);
mContext.sendBroadcast(intent);
// 等待2sFRP关闭然后重启
public static void relaunchFrp() {
Context context = ActivityUtils.getTopActivity();
if (context == null) {
context = MyApp.getAppContext();
}
if (context == null) {
MyLog.frpError("启动FRP失败:无法获取应用上下文");
return;
}
Context launchContext = context;
try {
Intent stopIntent = new Intent("receiver_control");
stopIntent.putExtra("control", -1);
launchContext.sendBroadcast(stopIntent);
} catch (Exception e) {
MyLog.frpError("发送FRP停止指令失败:" + e.getMessage());
}
// 等待2s让FRP释放资源然后重新启动
RxJavaUtils.delay(2, aLong -> {
Intent intent2 = mContext.getPackageManager().getLaunchIntentForPackage("com.bbitcn.bbit_frp2");
intent2.putExtra("host_id", HardwareSetting.getHostId());
mContext.startActivity(intent2);
try {
Intent launchIntent = launchContext.getPackageManager()
.getLaunchIntentForPackage("com.bbitcn.bbit_frp2");
if (launchIntent == null) {
MyLog.frpError("启动FRP失败:未找到远程协助应用");
return;
}
launchIntent.putExtra("host_id", HardwareSetting.getHostId());
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
launchContext.startActivity(launchIntent);
} catch (Exception e) {
MyLog.frpError("启动FRP失败:" + e.getMessage());
}
});
}
}
@@ -77,7 +77,7 @@ public class RemoteControl {
//切换模式
int newMode = OldSet.oldModeToNewMode(value);
if (RoomSetting.getMode() != newMode) {
int targetSignal = newMode == 0 ? 5 : newMode == 1 ? 3 : 6;
int targetSignal = newMode == 0 ? 15 : newMode == 1 ? 13 : 16;
RxBusUtils.get().post(RxTag.UPDATE_MAIN, targetSignal);
MyLog.remote("远程设置" + MyUtil.getModeName(newMode) + "模式");
}
@@ -7,7 +7,6 @@ import com.example.iot_controlhost.utils.MyUtil;
import com.example.iot_controlhost.utils.control.Control;
import com.example.iot_controlhost.utils.global.RoomController;
import com.example.iot_controlhost.utils.log.MyLog;
import com.example.iot_controlhost.utils.log.UserLog;
import com.example.iot_controlhost.utils.timer.UVTaskUtil;
import java.util.Calendar;
@@ -72,7 +71,6 @@ public class DisinfectController extends Control {
@Override
protected void stopMethod() {
MyLog.auto("停止自动控制消毒————————————————————————————————————————————————————");
UserLog.operate("自动:停止自动控制消毒");
for (MyTask task : UVTaskUtil.getUvTask()) {
UVTaskUtil.stopTask(task);
}
@@ -12,7 +12,6 @@ import com.example.iot_controlhost.utils.global.RxTag;
import com.example.iot_controlhost.utils.global.SpinnerList;
import com.example.iot_controlhost.utils.global.Variable;
import com.example.iot_controlhost.utils.log.MyLog;
import com.example.iot_controlhost.utils.log.UserLog;
import com.xuexiang.rxutil2.rxjava.DisposablePool;
import com.xuexiang.rxutil2.rxjava.RxJavaUtils;
@@ -77,6 +76,5 @@ public class EvenController extends Control {
MyUtil.autoControlOperateThird(RoomController.evenFan);
}));
MyLog.auto("停止自动控制匀风————————————————————————————————————————————————————");
UserLog.operate("自动:停止自动控制匀风");
}
}
@@ -15,7 +15,6 @@ import com.example.iot_controlhost.utils.global.RxTag;
import com.example.iot_controlhost.utils.global.SpinnerList;
import com.example.iot_controlhost.utils.global.Variable;
import com.example.iot_controlhost.utils.log.MyLog;
import com.example.iot_controlhost.utils.log.UserLog;
import com.xuexiang.rxutil2.rxjava.DisposablePool;
import com.xuexiang.rxutil2.rxjava.RxJavaUtils;
@@ -150,7 +149,6 @@ public class HumidityController extends Control {
}
}));
MyLog.auto("停止自动控制湿度————————————————————————————————————————————————————");
UserLog.operate("自动:停止自动控制湿度");
}
}
@@ -17,7 +17,6 @@ import com.example.iot_controlhost.utils.global.RxTag;
import com.example.iot_controlhost.utils.global.SpinnerList;
import com.example.iot_controlhost.utils.global.Variable;
import com.example.iot_controlhost.utils.log.MyLog;
import com.example.iot_controlhost.utils.log.UserLog;
/**
* @Description 自动模式-控制逻辑-温度 策略模式+工厂方法
@@ -78,10 +77,6 @@ public class TemperatureController extends Control {
"\n-算法" + AutoModelSet.getAutoTemperatureMode() +
"\n-加热方式" + AutoModelSet.getAutoTemperatureHeat() +
"\n-降温方式" + AutoModelSet.getAutoTemperatureCool());
UserLog.operate("手动:更新温控参数为:" +
"\n-算法" + AutoModelSet.getAutoTemperatureMode() +
"\n-加热方式:" + AutoModelSet.getAutoTemperatureHeat() +
"\n-降温方式:" + AutoModelSet.getAutoTemperatureCool());
start();
strategy = getStrategy();
start = startMethod();
@@ -121,7 +116,6 @@ public class TemperatureController extends Control {
* 停止自动控制
*/
protected void stopMethod() {
UserLog.operate("自动:停止自动控制温度,关闭空调,关闭地暖");
MyLog.auto("停止自动控制温度————————————————————————————————————————————————————");
MyQueue.getInstance(MyQueue.TYPE_CONTROLLER).addTask(new QueueIOTask(() -> {
MyUtil.autoControlOperateThird(RoomController.airConditionInfrared);
@@ -9,7 +9,6 @@ import com.example.iot_controlhost.utils.global.RoomSetting;
import com.example.iot_controlhost.utils.global.RxTag;
import com.example.iot_controlhost.utils.global.Variable;
import com.example.iot_controlhost.utils.log.MyLog;
import com.example.iot_controlhost.utils.log.UserLog;
import com.xuexiang.rxutil2.rxbus.RxBusUtils;
/**
@@ -153,13 +152,13 @@ public abstract class ControlStrategy {
if (!tipsTempStr.equals(tips.toString())) {
//防止重复发送相同的提示日志
tipsTempStr = tips.toString();
UserLog.operate("自动:" + tips);
MyLog.auto("自动温控" + tips);
}
} else {
if (mode != -1 && temp != -1 && RoomSetting.isAirConditionForceClose()) {
tips.append(",在消毒期间无法开启空调");
}
UserLog.operateError("自动操作失败:" + tips);
MyLog.autoError("自动温控操作失败:" + tips);
}
}
}
@@ -1,142 +1,303 @@
package com.example.iot_controlhost.utils.database;
import com.blankj.utilcode.util.StringUtils;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import com.example.iot_controlhost.MyApp;
import com.example.iot_controlhost.model.Log;
import com.example.iot_controlhost.model.LogDao;
import com.example.iot_controlhost.utils.log.MyLog;
import com.example.iot_controlhost.utils.log.UserLog;
import com.xuexiang.rxutil2.rxjava.RxJavaUtils;
import com.xuexiang.rxutil2.rxjava.task.RxIOTask;
import org.greenrobot.greendao.query.QueryBuilder;
import org.greenrobot.greendao.query.WhereCondition;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
public class LogDBManager {
/**
* 日志数据库访问入口
*
* <p>所有日志数据库操作都在同一个线程中串行执行避免高频日志写入时创建大量线程
* 同时保证用户日志在上传服务器前已经完成本地落库</p>
*/
public final class LogDBManager {
private static ExecutorService executor = Executors.newCachedThreadPool();
private static final String LOGCAT_TAG = "LogDBManager";
private static final int PAGE_SIZE = 20;
private static final int RETENTION_DAYS = 15;
private static final int CLEANUP_BATCH_SIZE = 500;
private static final long CLEANUP_BATCH_DELAY_MS = 30L;
private static final long SYNC_WAIT_SECONDS = 10L;
/**
* 增加
*/
public static void insertLog(Log log) {
executor.execute(() -> logDao.insert(log));
private static volatile Thread databaseThread;
private static final AtomicBoolean CLEANUP_RUNNING = new AtomicBoolean(false);
private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor(runnable -> {
Thread thread = new Thread(() -> {
databaseThread = Thread.currentThread();
runnable.run();
}, "log-database");
thread.setPriority(Thread.NORM_PRIORITY - 1);
return thread;
});
private static final LogDao LOG_DAO = MyApp.getDaoSession().getLogDao();
private LogDBManager() {
}
/**
* 每页的长度
* 异步增加普通诊断日志
*/
private static int pageSize = 20;
/**
* 日志操作类
*/
private static LogDao logDao = MyApp.getDaoSession().getLogDao();
public static void insertLog(com.example.iot_controlhost.model.Log log) {
EXECUTOR.execute(() -> insertInternal(log));
}
/**
* 删除
* 异步落库并回调结果回调在日志数据库线程执行不允许执行耗时或UI操作
*/
public static void insertLog(com.example.iot_controlhost.model.Log log, InsertCallback callback) {
EXECUTOR.execute(() -> {
boolean saved = insertInternal(log);
if (callback != null) {
callback.onComplete(saved);
}
});
}
private static boolean insertInternal(com.example.iot_controlhost.model.Log log) {
try {
LOG_DAO.insert(log);
return true;
} catch (Exception e) {
Log.e(LOGCAT_TAG, "日志写入数据库失败", e);
return false;
}
}
/**
* 查询最新一页日志
*/
public static List<com.example.iot_controlhost.model.Log> queryLatest(String filter) {
return queryPage(filter, null);
}
/**
* 使用最后一条日志作为游标查询下一页避免 OFFSET 在并发插入时出现重复或漏项
*/
public static List<com.example.iot_controlhost.model.Log> queryNext(String filter,
com.example.iot_controlhost.model.Log cursor) {
if (cursor == null) {
return Collections.emptyList();
}
return queryPage(filter, cursor);
}
public static boolean isFullPage(List<?> logs) {
return logs != null && logs.size() == PAGE_SIZE;
}
private static List<com.example.iot_controlhost.model.Log> queryPage(
String filter, com.example.iot_controlhost.model.Log cursor) {
return executeSync(() -> {
QueryBuilder<com.example.iot_controlhost.model.Log> qb = LOG_DAO.queryBuilder();
List<String> tags = UserLog.getStorageTagsForFilter(filter);
qb.where(LogDao.Properties.Tag.in(tags));
if (cursor != null && cursor.getId() != null) {
qb.where(LogDao.Properties.Id.lt(cursor.getId()));
}
return qb.orderDesc(LogDao.Properties.Id)
.limit(PAGE_SIZE)
.list();
}, Collections.emptyList());
}
/**
* 兼容旧调用新界面应使用游标分页
*/
public static List<com.example.iot_controlhost.model.Log> queryList(String filter, int currentPage) {
return executeSync(() -> {
return LOG_DAO.queryBuilder()
.where(LogDao.Properties.Tag.in(UserLog.getStorageTagsForFilter(filter)))
.orderDesc(LogDao.Properties.Id)
.offset(Math.max(0, currentPage - 1) * PAGE_SIZE)
.limit(PAGE_SIZE)
.list();
}, Collections.emptyList());
}
public static List<com.example.iot_controlhost.model.Log> queryDateRange(Date startDate, Date endDate) {
return executeSync(() -> {
return LOG_DAO.queryBuilder()
.where(LogDao.Properties.Datetime.between(startDate, endDate))
.orderDesc(LogDao.Properties.Id)
.list();
}, Collections.emptyList());
}
/**
* 导出最近七天系统和网络诊断日志
*/
public static List<com.example.iot_controlhost.model.Log> queryAllList() {
return executeSync(() -> {
Calendar calendar = Calendar.getInstance();
Date currentDate = calendar.getTime();
calendar.add(Calendar.DAY_OF_YEAR, -7);
Date oneWeekAgo = calendar.getTime();
return LOG_DAO.queryBuilder()
.where(LogDao.Properties.Datetime.between(oneWeekAgo, currentDate))
.where(LogDao.Properties.Tag.in(
MyLog.TAG_APP,
MyLog.TAG_APP + MyLog.TAG_ERROR,
MyLog.TAG_NETWORK,
MyLog.TAG_NETWORK + MyLog.TAG_ERROR))
.orderDesc(LogDao.Properties.Id)
.list();
}, Collections.emptyList());
}
public static List<com.example.iot_controlhost.model.Log> queryWorkLogs(int currentPage) {
return queryList(UserLog.FILTER_ALL, currentPage);
}
public static void removeLog(Long id) {
logDao.deleteByKey(id);
executeSync(() -> {
LOG_DAO.deleteByKey(id);
return null;
}, null);
}
public static void removeAll() {
logDao.deleteAll();
executeSync(() -> {
LOG_DAO.deleteAll();
return null;
}, null);
}
public static List<Log> queryDateRange(Date startDate, Date endDate) {
QueryBuilder<Log> qb = logDao.queryBuilder();
// 创建日期区间查询条件
WhereCondition dateCondition = LogDao.Properties.Datetime.between(startDate, endDate);
QueryBuilder<Log> dateRangeQueryBuilder = qb.where(dateCondition);
return dateRangeQueryBuilder.list();
public static void update(com.example.iot_controlhost.model.Log log) {
executeSync(() -> {
LOG_DAO.update(log);
return null;
}, null);
}
/**
* 返回某类型的所有日志
*
* @param tagType MyLog类下的TAG常量 为空时返回全部
* 每日后台分批清理十五天前的日志每批之间主动让出数据库线程
* 避免巨量历史数据形成大事务或长时间独占SQLite连接
*/
public static List<Log> queryList(String tagType, int currentPage) {
QueryBuilder<Log> qb = logDao.queryBuilder();
if (!StringUtils.isEmpty(tagType)) {
qb.where(LogDao.Properties.Tag.like("%" + tagType + "%"));
}else{
qb.whereOr(
LogDao.Properties.Tag.like("%" + UserLog.TAG_USER_OPERATE + "%"),
LogDao.Properties.Tag.like("%" + UserLog.TAG_USER_SENSOR + "%"),
LogDao.Properties.Tag.like("%" + UserLog.TAG_USER_TASK + "%"),
LogDao.Properties.Tag.like("%" + UserLog.TAG_USER_SYSTEM + "%")
);
public static void clearExpiredLogsAsync() {
if (!CLEANUP_RUNNING.compareAndSet(false, true)) {
Log.i(LOGCAT_TAG, "日志清理任务已在执行,本次跳过");
return;
}
return qb.offset((currentPage - 1) * pageSize)
.limit(pageSize)
.orderDesc(LogDao.Properties.Datetime)
.list();
}
/**
* 返回某类型的所有日志
*/
public static List<Log> queryAllList() {
QueryBuilder<Log> qb = logDao.queryBuilder();
Calendar calendar = Calendar.getInstance();
Date currentDate = calendar.getTime();
calendar.add(Calendar.DAY_OF_YEAR, -7);
Date oneWeekAgo = calendar.getTime();
return qb.orderDesc(LogDao.Properties.Datetime)
.where(LogDao.Properties.Datetime.between(oneWeekAgo, currentDate))
.whereOr(
LogDao.Properties.Tag.like("%" + MyLog.TAG_APP + "%"),
LogDao.Properties.Tag.like("%" + MyLog.TAG_NETWORK + "%")
)
.list();
calendar.add(Calendar.DAY_OF_YEAR, -RETENTION_DAYS);
CleanupState state = new CleanupState(calendar.getTimeInMillis(), System.currentTimeMillis());
EXECUTOR.execute(() -> clearNextBatch(state));
}
public static List<Log> queryWorkLogs(int currentPage) {
return logDao.queryBuilder()
.orderDesc(LogDao.Properties.Datetime)
.whereOr(
LogDao.Properties.Tag.like("%" + UserLog.TAG_USER_OPERATE + "%"),
LogDao.Properties.Tag.like("%" + UserLog.TAG_USER_SENSOR + "%"),
LogDao.Properties.Tag.like("%" + UserLog.TAG_USER_TASK + "%"),
LogDao.Properties.Tag.like("%" + UserLog.TAG_USER_SYSTEM + "%")
)
.offset((currentPage - 1) * pageSize)
.limit(pageSize)
.list();
}
/**
*
*/
public static void update(Log log) {
logDao.update(log);
}
/**
* 删除1个月前的日志
*/
public static void clearLogsOlderThanOneMonthsSync() {
private static void clearNextBatch(CleanupState state) {
try {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MONTH, -1);
Date oneMonthAgo = calendar.getTime();
logDao.queryBuilder()
.where(LogDao.Properties.Datetime.lt(oneMonthAgo))
.buildDelete()
.executeDeleteWithoutDetachingEntities();
SQLiteDatabase database = getRawDatabase();
Batch batch = findExpiredBatch(database, state.lastId, state.cutoffMillis);
if (batch.count == 0) {
LOG_DAO.detachAll();
CLEANUP_RUNNING.set(false);
long duration = System.currentTimeMillis() - state.startMillis;
MyLog.app("日志维护完成:保留" + RETENTION_DAYS + "天,分批清理"
+ state.deletedCount + "条,耗时" + duration + "ms");
return;
}
database.execSQL(
"DELETE FROM LOG WHERE _id > ? AND _id <= ? AND DATETIME < ?",
new Object[]{state.lastId, batch.lastId, state.cutoffMillis});
state.lastId = batch.lastId;
state.deletedCount += batch.count;
EXECUTOR.schedule(() -> clearNextBatch(state), CLEANUP_BATCH_DELAY_MS, TimeUnit.MILLISECONDS);
} catch (Exception e) {
e.printStackTrace();
CLEANUP_RUNNING.set(false);
Log.e(LOGCAT_TAG, "分批清理过期日志失败", e);
}
}
private static Batch findExpiredBatch(SQLiteDatabase database, long lastId, long cutoffMillis) {
long batchLastId = lastId;
int count = 0;
String sql = "SELECT _id FROM LOG WHERE _id > ? AND DATETIME < ? ORDER BY _id LIMIT "
+ CLEANUP_BATCH_SIZE;
try (Cursor cursor = database.rawQuery(sql,
new String[]{String.valueOf(lastId), String.valueOf(cutoffMillis)})) {
while (cursor.moveToNext()) {
batchLastId = cursor.getLong(0);
count++;
}
}
return new Batch(batchLastId, count);
}
private static SQLiteDatabase getRawDatabase() {
Object rawDatabase = LOG_DAO.getDatabase().getRawDatabase();
if (!(rawDatabase instanceof SQLiteDatabase)) {
throw new IllegalStateException("日志数据库类型不受支持:" + rawDatabase);
}
return (SQLiteDatabase) rawDatabase;
}
private static <T> T executeSync(Callable<T> task, T fallback) {
if (Thread.currentThread() == databaseThread) {
try {
return task.call();
} catch (Exception e) {
Log.e(LOGCAT_TAG, "日志数据库操作失败", e);
return fallback;
}
}
try {
return EXECUTOR.submit(task).get(SYNC_WAIT_SECONDS, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
Log.e(LOGCAT_TAG, "等待日志数据库操作时被中断", e);
} catch (ExecutionException e) {
Log.e(LOGCAT_TAG, "日志数据库操作失败", e.getCause());
} catch (TimeoutException e) {
Log.e(LOGCAT_TAG, "等待日志数据库操作超时", e);
}
return fallback;
}
public interface InsertCallback {
void onComplete(boolean saved);
}
private static final class CleanupState {
private final long cutoffMillis;
private final long startMillis;
private long lastId;
private long deletedCount;
private CleanupState(long cutoffMillis, long startMillis) {
this.cutoffMillis = cutoffMillis;
this.startMillis = startMillis;
}
}
private static final class Batch {
private final long lastId;
private final int count;
private Batch(long lastId, int count) {
this.lastId = lastId;
this.count = count;
}
}
}
@@ -298,13 +298,13 @@ public class RoomController {
// 检查除湿机功率是否与除湿机物理电源状态匹配时刻监测当前除湿机功率状态使之与软件开关匹配
// 防止出现软件界面中开启除湿器但实际上除湿器未开启功率不足100W的情况
if (RoomController.dehumidifier.isAvailable() || RoomController.dehumidifier485Relay.isAvailable()) {
PollingTask.getInstance().startPollingTaskOnIOThread(RxTag.TAG_DEHUMIDIFIER_STATUS, 60, () -> {
PollingTask.getInstance().startPollingTaskOnIOThread(RxTag.TAG_DEHUMIDIFIER_STATUS, 60 * 5, () -> {
if (RoomController.dehumidifier.isAvailable() && RoomController.dehumidifier.isPowerSupply() && RoomController.dehumidifier.getPower() < 100) {
MyLog.controllerError("除湿机电源已开启但功率不足100W,现尝试再次开启除湿机物理电源");
MyQueue.getInstance(MyQueue.TYPE_CONTROLLER).addTask(new QueueIOTask(() -> RoomController.dehumidifier.setPowerSupplyForOld(true)));
MyLog.controllerError("除湿机电源已开启但功率不足100W,现尝试再次开启除湿机红外电源");//红外开关是一样的指令 所以这里等于是重新发送
MyQueue.getInstance(MyQueue.TYPE_CONTROLLER).addTask(new QueueIOTask(() -> RoomController.dehumidifier.setPowerSupply(true)));
} else if (RoomController.dehumidifier.isAvailable() && !RoomController.dehumidifier.isPowerSupply() && RoomController.dehumidifier.getPower() > 100) {
MyLog.controllerError("除湿机电源已关闭但功率大于100W,现尝试再次关闭除湿机物理电源");
MyQueue.getInstance(MyQueue.TYPE_CONTROLLER).addTask(new QueueIOTask(() -> RoomController.dehumidifier.setPowerSupplyForOld(false)));
MyLog.controllerError("除湿机电源已关闭但功率大于100W,现尝试再次关闭除湿机红外电源");
MyQueue.getInstance(MyQueue.TYPE_CONTROLLER).addTask(new QueueIOTask(() -> RoomController.dehumidifier.setPowerSupply(false)));
}
if (RoomController.dehumidifier485Relay.isAvailable()
&& RoomController.dehumidifier485.isPowerSupply()
@@ -330,7 +330,7 @@ public class RoomController {
doNotNeedDelayCloseAirCondition = true;
// MyLog.test("空调后台线程:不采取操作");
}
// 红外除湿机
if (dehumidifier.isAvailable()
&& !dehumidifierInfrared.isPowerSupply()
&& doNotNeedDelayCloseDehumidifier) {
@@ -4,7 +4,6 @@ package com.example.iot_controlhost.utils.log;
import android.content.Context;
import android.content.Intent;
import com.example.iot_controlhost.utils.network.TopicClass;
import com.jakewharton.processphoenix.ProcessPhoenix;
import com.xuexiang.rxutil2.rxjava.RxJavaUtils;
@@ -36,11 +35,12 @@ public class CrashHandlerUtil implements Thread.UncaughtExceptionHandler {
public void uncaughtException(Thread thread, Throwable ex) {
ex.printStackTrace();
MyLog.appError("软件已崩溃,重启应用:" + getFormattedException(ex));
TopicClass.uploadLog(4, 3, "已崩溃,系统将自动重启:" + getFormattedException(ex));
// 先启动重启保护任何日志或数据库异常都不能阻断恢复流程
new Thread(() -> {
try { Thread.sleep(500); } catch (Exception ignored) { }
ProcessPhoenix.triggerRebirth(mContext);
}).start();
UserLog.systemErrorImmediately("程序运行异常,系统将自动重启");
}
@@ -69,4 +69,4 @@ public class CrashHandlerUtil implements Thread.UncaughtExceptionHandler {
}
}
}
@@ -8,8 +8,6 @@ import static com.example.iot_controlhost.utils.log.UserLog.TAG_USER_TASK;
import android.util.Log;
import com.example.iot_controlhost.utils.database.LogDBManager;
import com.xuexiang.rxutil2.rxjava.RxJavaUtils;
import com.xuexiang.rxutil2.rxjava.task.RxIOTask;
import java.util.ArrayList;
import java.util.Collections;
@@ -152,19 +150,13 @@ public class MyLog extends Timber.Tree {
@Override
protected void log(int priority, String tag, String message, Throwable t) {
// 不处理UserLog日志
if (tag.contains(TAG_USER_OPERATE) || tag.contains(TAG_USER_SENSOR) || tag.contains(TAG_USER_TASK) || tag.contains(TAG_USER_SYSTEM)) {
if (tag == null || tag.contains(TAG_USER_OPERATE) || tag.contains(TAG_USER_SENSOR)
|| tag.contains(TAG_USER_TASK) || tag.contains(TAG_USER_SYSTEM)) {
return;
}
RxJavaUtils.doInIOThread(new RxIOTask<>(null) {
@Override
public Void doInIOThread(Object o) {
//输出日志到控制台
Log.println(priority, tag, message);
//保存日志到数据库
LogDBManager.insertLog(new com.example.iot_controlhost.model.Log(tag, priority, new Date(), message));
return null;
}
});
// 控制台立即输出数据库写入由日志单线程异步处理避免重复切换IO线程
Log.println(priority, tag, message);
LogDBManager.insertLog(new com.example.iot_controlhost.model.Log(tag, priority, new Date(), message));
}
}
@@ -3,13 +3,10 @@ package com.example.iot_controlhost.utils.log;
import android.util.Log;
import com.example.iot_controlhost.utils.database.LogDBManager;
import com.example.iot_controlhost.utils.global.RxTag;
import com.example.iot_controlhost.utils.network.TopicClass;
import com.xuexiang.rxutil2.rxbus.RxBusUtils;
import com.xuexiang.rxutil2.rxjava.RxJavaUtils;
import com.xuexiang.rxutil2.rxjava.task.RxIOTask;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
@@ -20,6 +17,12 @@ import timber.log.Timber;
* 日志用户查看用
*/
public class UserLog extends Timber.Tree {
public static final String FILTER_ALL = "全部上报日志";
public static final String FILTER_OPERATE = "操作日志";
public static final String FILTER_SENSOR = "传感器日志";
public static final String FILTER_TASK = "作业日志";
public static final String FILTER_SYSTEM = "系统日志";
/**
* 用户-操作类
*/
@@ -43,14 +46,69 @@ public class UserLog extends Timber.Tree {
return tags;
}
public static List<String> getUserQueryFilters() {
return Arrays.asList(FILTER_ALL, FILTER_OPERATE, FILTER_SENSOR, FILTER_TASK, FILTER_SYSTEM);
}
/**
* 将界面筛选项转换为实际存储标签成功和错误级别都包含在同一分类中
*/
public static List<String> getStorageTagsForFilter(String filter) {
if (FILTER_OPERATE.equals(filter) || TAG_USER_OPERATE.equals(filter)) {
return tagsWithError(TAG_USER_OPERATE);
}
if (FILTER_SENSOR.equals(filter) || TAG_USER_SENSOR.equals(filter)) {
return tagsWithError(TAG_USER_SENSOR);
}
if (FILTER_TASK.equals(filter) || TAG_USER_TASK.equals(filter)) {
return tagsWithError(TAG_USER_TASK);
}
if (FILTER_SYSTEM.equals(filter) || TAG_USER_SYSTEM.equals(filter)) {
return tagsWithError(TAG_USER_SYSTEM);
}
List<String> tags = new ArrayList<>();
for (String userTag : getUserLogTags()) {
tags.addAll(tagsWithError(userTag));
}
return tags;
}
private static List<String> tagsWithError(String tag) {
return Arrays.asList(tag, tag + MyLog.TAG_ERROR);
}
public static String getCategoryName(String tag) {
if (tag == null) {
return "未知";
}
if (tag.contains(TAG_USER_OPERATE)) {
return "操作";
}
if (tag.contains(TAG_USER_SENSOR)) {
return "传感器";
}
if (tag.contains(TAG_USER_TASK)) {
return "作业";
}
return "系统";
}
public static void operate(String msg) {
Timber.tag(TAG_USER_OPERATE).i(msg);
}
public static void operate(String source, String msg) {
operate(formatSource(source, msg));
}
public static void operateError(String msg) {
Timber.tag(TAG_USER_OPERATE + MyLog.TAG_ERROR).e(msg);
}
public static void operateError(String source, String msg) {
operateError(formatSource(source, msg));
}
public static void task(String msg) {
Timber.tag(TAG_USER_TASK).i(msg);
}
@@ -71,46 +129,70 @@ public class UserLog extends Timber.Tree {
Timber.tag(TAG_USER_SYSTEM).i(msg);
}
public static void system(String source, String msg) {
system(formatSource(source, msg));
}
public static void systemError(String msg) {
Timber.tag(TAG_USER_SYSTEM + MyLog.TAG_ERROR).e(msg);
}
/**
* 崩溃场景使用非阻塞入口防止数据库繁忙时阻断进程重启
*/
public static void systemErrorImmediately(String msg) {
persistAndUpload(TAG_USER_SYSTEM + MyLog.TAG_ERROR, Log.ERROR, msg, new Date());
}
private static String formatSource(String source, String msg) {
return "[来源=" + source + "] " + msg;
}
@Override
protected void log(int priority, String tag, String message, Throwable t) {
//只有特定UserLog日志才能处理
if (!tag.contains(TAG_USER_OPERATE) && !tag.contains(TAG_USER_SENSOR) && !tag.contains(TAG_USER_TASK) && !tag.contains(TAG_USER_SYSTEM)) {
if (tag == null || !isUserLogTag(tag)) {
return;
}
RxJavaUtils.doInIOThread(new RxIOTask<>(null) {
@Override
public Void doInIOThread(Object o) {
//发送日志到服务器
int tagInt = 0;
if (tag.contains(TAG_USER_OPERATE)) {
tagInt = 1;
} else if (tag.contains(TAG_USER_SENSOR)) {
tagInt = 2;
} else if (tag.contains(TAG_USER_TASK)) {
tagInt = 3;
} else if (tag.contains(TAG_USER_SYSTEM)) {
tagInt = 4;
}
int newPriority;
//1调试 2信息 3警告
if (tag.contains(MyLog.TAG_ERROR)) {
newPriority = 3;
} else {
newPriority = 2;
}
//保存日志到数据库
LogDBManager.insertLog(new com.example.iot_controlhost.model.Log(tag, priority, new Date(), message));
//输出日志到控制台
Log.println(priority, tag, message);
//上传日志
TopicClass.uploadLog(tagInt, newPriority, message);
return null;
persistAndUpload(tag, priority, message, new Date());
}
private static boolean isUserLogTag(String tag) {
return tag.contains(TAG_USER_OPERATE)
|| tag.contains(TAG_USER_SENSOR)
|| tag.contains(TAG_USER_TASK)
|| tag.contains(TAG_USER_SYSTEM);
}
private static void persistAndUpload(String tag, int priority, String message, Date datetime) {
com.example.iot_controlhost.model.Log entry =
new com.example.iot_controlhost.model.Log(tag, priority, datetime, message);
Log.println(priority, tag, message);
LogDBManager.insertLog(entry, saved -> {
if (!saved) {
Log.e(TAG_USER_SYSTEM, "用户日志本地落库失败,已取消上传以避免两端数据不一致");
return;
}
TopicClass.uploadLog(toServerTag(tag), toServerPriority(tag), message, datetime);
});
}
private static int toServerTag(String tag) {
if (tag.contains(TAG_USER_OPERATE)) {
return 1;
}
if (tag.contains(TAG_USER_SENSOR)) {
return 2;
}
if (tag.contains(TAG_USER_TASK)) {
return 3;
}
return 4;
}
private static int toServerPriority(String tag) {
// 服务端1调试2信息3警告
return tag.contains(MyLog.TAG_ERROR) ? 3 : 2;
}
}
@@ -33,6 +33,7 @@ import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
@@ -132,35 +133,41 @@ public class TopicClass {
*/
public static void hanldeSerianetMessage(String msg) {
String tip = null;
boolean actionLogged = false;
switch (msg) {
case "重启" -> {
tip = "远程:重启设备";
MyUtil.reboot();
tip = "重启设备";
MyUtil.reboot("远程控制");
actionLogged = true;
}
case "关机" -> {
tip = "远程:关闭设备";
MyUtil.shutDown();
tip = "关闭设备";
MyUtil.shutDown("远程控制");
actionLogged = true;
}
case "重启软件" -> {
tip = "远程:重启软件";
MyUtil.relaunchApp();
tip = "重启软件";
MyUtil.relaunchApp("远程控制");
actionLogged = true;
}
case "更新" -> {
tip = "远程:强制更新";
tip = "强制更新软件";
AppUpdateUtil.updateAPP(ActivityUtils.getTopActivity(), true, true);
}
case "下载frp" -> {
tip = "远程:更新远程控制软件";
tip = "更新远程控制软件";
FrpUpdateUtil.check(ActivityUtils.getTopActivity(), true);
}
case "检测更新" -> {
tip = "远程:检测更新";
tip = "检测软件更新";
AppUpdateUtil.updateAPP(ActivityUtils.getTopActivity(), false, false);
}
}
if (!StringUtils.isEmpty(tip)) {
TopicClass.receiveSuccess(msg);
UserLog.operate(tip);
if (!actionLogged) {
UserLog.operate("远程控制", tip);
}
}
}
@@ -197,7 +204,10 @@ public class TopicClass {
MyQueue.getInstance(MyQueue.TYPE_CONTROLLER).addTask(new QueueIOTask(() -> {
boolean operateSuccess = RemoteControl.remoteControl(controlCommand, gear, value);
if (operateSuccess) {
UserLog.operate("远程:" + getCommandNameById(controlCommand, value));
String commandName = getCommandNameById(controlCommand, value);
if (controlCommand != 8) {
UserLog.operate("远程控制", "设备操作成功:" + commandName);
}
//设置完远程控制后 上传最新当前状态
if (controlCommand == 8) {
// 如果是切换模式为优化速度直接上传最新的模式不等转换完成
@@ -206,7 +216,9 @@ public class TopicClass {
uploadDeviceState(true);//设置完远程控制后 上传最新当前状态
}
} else {
UserLog.operateError("远程操作失败:" + getCommandNameById(controlCommand, value));
String commandName = getCommandNameById(controlCommand, value);
UserLog.operateError("远程控制", controlCommand == 8 ? "模式切换失败" : "设备操作失败:" + commandName);
MyLog.remoteError("远程操作失败:" + commandName + ",指令=" + controlCommand + ",值=" + value);
}
//更新手动模式下设备UI
RxBusUtils.get().post(RxTag.UPDATE_MANUAL, 0);
@@ -293,13 +305,17 @@ public class TopicClass {
* 上传日志到服务器
*/
public static void uploadLog(int tag, int priority, String message) {
uploadLog(tag, priority, message, new Date());
}
public static void uploadLog(int tag, int priority, String message, Date datetime) {
JSONObject json = new JSONObject();
try {
json.put("areacode", RoomSetting.getAreaCode());
json.put("tage", tag);
json.put("msglevel", priority);
json.put("msg", message);
json.put("datetime", MyUtil.getDateTime());
json.put("datetime", MyUtil.getDateTime(datetime));
MQTTService.publish(DeviceRunLogsPost(), json.toString());
} catch (JSONException e) {
e.printStackTrace();
@@ -588,7 +604,7 @@ public class TopicClass {
//2025年5月7日取消以下逻辑
// //切换为自动模式
// RxBusUtils.get().post(RxTag.UPDATE_MAIN, 3);
UserLog.operate("远程:已更新自动模式参数");
UserLog.operate("远程控制", "自动模式参数更新成功");
if (RoomSetting.getMode() == 1) {
// 如果是自动模式则刷新自动模式参数
RxBusUtils.get().post(RxTag.UPDATE_AUTO, 3);