优化日志、自动控制逻辑

This commit is contained in:
BBIT-Kai
2026-08-10 10:39:01 +08:00
parent b5fa7e1841
commit 4de70ff57a
29 changed files with 668 additions and 168 deletions
+1
View File
@@ -5,3 +5,4 @@ app/build/
build/ build/
.kotlin/ .kotlin/
*.pdf *.pdf
.gradle-home/
@@ -27,6 +27,7 @@ import com.scwang.smart.refresh.footer.ClassicsFooter;
import com.scwang.smart.refresh.header.ClassicsHeader; import com.scwang.smart.refresh.header.ClassicsHeader;
import com.scwang.smart.refresh.layout.SmartRefreshLayout; import com.scwang.smart.refresh.layout.SmartRefreshLayout;
import org.xutils.BuildConfig;
import org.xutils.x; import org.xutils.x;
import java.io.File; import java.io.File;
@@ -53,6 +54,11 @@ public class MyApp extends Application {
// 初始化日志库 // 初始化日志库
Timber.plant(new MyLog()); Timber.plant(new MyLog());
Timber.plant(new UserLog()); Timber.plant(new UserLog());
UserLog.system("软件生命周期", "软件启动,版本=" + BuildConfig.VERSION_NAME);
MyLog.app("软件生命周期:进程启动,版本=" + BuildConfig.VERSION_NAME
+ "versionCode=" + BuildConfig.VERSION_CODE
+ "Android=" + android.os.Build.VERSION.RELEASE
+ "SDK=" + android.os.Build.VERSION.SDK_INT);
if (OldSet.isFirstTime()) { if (OldSet.isFirstTime()) {
OldSet.importOldSet(getApplicationContext()); OldSet.importOldSet(getApplicationContext());
} }
@@ -6,7 +6,6 @@ import com.example.iot_controlhost.base.BaseRecyclerAdapter;
import com.example.iot_controlhost.databinding.ItemLogBinding; import com.example.iot_controlhost.databinding.ItemLogBinding;
import com.example.iot_controlhost.model.Log; import com.example.iot_controlhost.model.Log;
import com.example.iot_controlhost.utils.global.RoomSetting; import com.example.iot_controlhost.utils.global.RoomSetting;
import com.example.iot_controlhost.utils.log.UserLog;
import java.util.List; import java.util.List;
@@ -28,7 +27,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); data.getLevel() == android.util.Log.ERROR ? R.color.red : RoomSetting.isDarkTheme() ? R.color.white : R.color.black);
binding.tvMessage.setTextColor(targetColorId); binding.tvMessage.setTextColor(targetColorId);
binding.tvDate.setTextColor(targetColorId); binding.tvDate.setTextColor(targetColorId);
binding.tvMessage.setText("[" + UserLog.getCategoryName(data.getTag()) + "] " + data.getMessage()); binding.tvMessage.setText("[" + data.getTag() + "] " + data.getMessage());
binding.tvDate.setText(TimeUtils.date2String(data.getDatetime())); binding.tvDate.setText(TimeUtils.date2String(data.getDatetime()));
} }
} }
@@ -11,6 +11,7 @@ import androidx.annotation.NonNull;
import androidx.databinding.ViewDataBinding; import androidx.databinding.ViewDataBinding;
import com.example.iot_controlhost.R; import com.example.iot_controlhost.R;
import com.example.iot_controlhost.ui.activity.MainActivity;
import com.example.iot_controlhost.utils.log.MyLog; import com.example.iot_controlhost.utils.log.MyLog;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@@ -27,11 +28,31 @@ import es.dmoral.toasty.Toasty;
public abstract class BaseDialog<Binding extends ViewDataBinding> extends Dialog { public abstract class BaseDialog<Binding extends ViewDataBinding> extends Dialog {
protected Binding binding; protected Binding binding;
protected Context mContext; protected Context mContext;
private boolean lockTimerNotified;
public BaseDialog(@NonNull Context context) { public BaseDialog(@NonNull Context context) {
this(context, false); this(context, false);
} }
@Override
public void show() {
super.show();
if (!lockTimerNotified && mContext instanceof MainActivity) {
lockTimerNotified = true;
((MainActivity) mContext).onDialogShown();
}
}
@Override
public void dismiss() {
boolean needNotify = lockTimerNotified;
super.dismiss();
if (needNotify && mContext instanceof MainActivity) {
lockTimerNotified = false;
((MainActivity) mContext).onDialogDismissed();
}
}
/** /**
* 构造函数 * 构造函数
* *
@@ -28,19 +28,25 @@ public class Fan extends Relay {
public boolean setPowerSupply(boolean target) { public boolean setPowerSupply(boolean target) {
boolean result = false; boolean result = false;
if (target) { if (target) {
if (SpinnerList.VENTILATION_MODES[0].equals(AutoModelSet.getVentilatorMode()) if ((SpinnerList.VENTILATION_MODES[0].equals(AutoModelSet.getVentilatorMode())
|| SpinnerList.VENTILATION_MODES[1].equals(AutoModelSet.getVentilatorMode())) { || SpinnerList.VENTILATION_MODES[1].equals(AutoModelSet.getVentilatorMode()))
&& fans[0].isAvailable()) {
result = MyUtil.autoControlOperateThird(fans[0], true,false); result = MyUtil.autoControlOperateThird(fans[0], true,false);
} }
if (SpinnerList.VENTILATION_MODES[0].equals(AutoModelSet.getVentilatorMode()) if ((SpinnerList.VENTILATION_MODES[0].equals(AutoModelSet.getVentilatorMode())
|| SpinnerList.VENTILATION_MODES[2].equals(AutoModelSet.getVentilatorMode())) { || SpinnerList.VENTILATION_MODES[2].equals(AutoModelSet.getVentilatorMode()))
&& fans[1].isAvailable()) {
result = MyUtil.autoControlOperateThird(fans[1], true,false) || result; result = MyUtil.autoControlOperateThird(fans[1], true,false) || result;
} }
} else { } else {
//防止出现“仅开进气扇-开启-设置为仅开排气扇-关闭”此时进气扇无法关闭的情况 所以每次都得全部关闭(各种换气扇) //防止出现“仅开进气扇-开启-设置为仅开排气扇-关闭”此时进气扇无法关闭的情况 所以每次都得全部关闭(各种换气扇)
if (fans[0].isAvailable()) {
result = MyUtil.autoControlOperateThird(fans[0], false,false); result = MyUtil.autoControlOperateThird(fans[0], false,false);
}
if (fans[1].isAvailable()) {
result = MyUtil.autoControlOperateThird(fans[1], false,false) || result; result = MyUtil.autoControlOperateThird(fans[1], false,false) || result;
} }
}
return result; return result;
} }
@@ -1,6 +1,7 @@
package com.example.iot_controlhost.model.thread; package com.example.iot_controlhost.model.thread;
import com.example.iot_controlhost.base.BaseQueue; import com.example.iot_controlhost.base.BaseQueue;
import com.example.iot_controlhost.utils.log.MyLog;
/** /**
* 任务队列-IO任务 * 任务队列-IO任务
@@ -22,7 +23,12 @@ public class QueueIOTask extends BaseQueue {
@Override @Override
public void run() { public void run() {
try {
task.run(); task.run();
} catch (Throwable throwable) {
MyLog.controllerError("控制队列任务异常:异常=" + throwable.getClass().getSimpleName()
+ ",原因=" + throwable.getMessage());
}
} }
} }
@@ -2,6 +2,7 @@ package com.example.iot_controlhost.model.thread;
import com.example.iot_controlhost.base.BaseQueue; import com.example.iot_controlhost.base.BaseQueue;
import com.example.iot_controlhost.base.IRxIOTask; import com.example.iot_controlhost.base.IRxIOTask;
import com.example.iot_controlhost.utils.log.MyLog;
import com.xuexiang.rxutil2.rxjava.RxJavaUtils; import com.xuexiang.rxutil2.rxjava.RxJavaUtils;
import com.xuexiang.rxutil2.rxjava.impl.IRxUITask; import com.xuexiang.rxutil2.rxjava.impl.IRxUITask;
import com.xuexiang.rxutil2.rxjava.task.RxUITask; import com.xuexiang.rxutil2.rxjava.task.RxUITask;
@@ -19,12 +20,31 @@ public abstract class QueueIOUITask<T> extends BaseQueue implements IRxIOTask<T>
RxJavaUtils.doInUIThread(new RxUITask<T>(outData) { RxJavaUtils.doInUIThread(new RxUITask<T>(outData) {
@Override @Override
public void doInUIThread(T o) { public void doInUIThread(T o) {
try {
QueueIOUITask.this.doInUIThread(o); QueueIOUITask.this.doInUIThread(o);
} catch (Throwable throwable) {
MyLog.appError("队列UI回调异常:异常=" + throwable.getClass().getSimpleName()
+ ",原因=" + throwable.getMessage());
QueueIOUITask.this.onError(throwable);
}
}
});
} catch (Throwable throwable) {
MyLog.controllerError("队列任务执行异常:异常=" + throwable.getClass().getSimpleName()
+ ",原因=" + throwable.getMessage());
RxJavaUtils.doInUIThread(new RxUITask<Object>(null) {
@Override
public void doInUIThread(Object o) {
QueueIOUITask.this.onError(throwable);
} }
}); });
} catch (Exception e) {
e.printStackTrace();
} }
} }
/**
* IO任务或UI回调失败时执行,默认只记录日志;需要清理界面的任务可重写。
*/
public void onError(Throwable throwable) {
}
} }
@@ -160,6 +160,7 @@ public class MQTTService extends Service {
MyLog.network("服务器连接成功"); MyLog.network("服务器连接成功");
HardwareSetting.setRegistered(); HardwareSetting.setRegistered();
setMQTTConnect(true); setMQTTConnect(true);
TopicClass.flushPendingLogs();
try { try {
// 订阅myTopic话题 // 订阅myTopic话题
client.subscribe(TopicClass.arealistGet(), 1); client.subscribe(TopicClass.arealistGet(), 1);
@@ -2,6 +2,8 @@ package com.example.iot_controlhost.ui.activity;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.os.Handler;
import android.os.Looper;
import android.view.View; import android.view.View;
import com.blankj.utilcode.util.StringUtils; import com.blankj.utilcode.util.StringUtils;
@@ -18,6 +20,7 @@ import com.example.iot_controlhost.utils.MyQueue;
import com.example.iot_controlhost.utils.global.RoomController; import com.example.iot_controlhost.utils.global.RoomController;
import com.example.iot_controlhost.utils.global.RoomSensor; import com.example.iot_controlhost.utils.global.RoomSensor;
import com.example.iot_controlhost.utils.global.SpinnerList; import com.example.iot_controlhost.utils.global.SpinnerList;
import com.example.iot_controlhost.utils.log.MyLog;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -32,6 +35,10 @@ import es.dmoral.toasty.Toasty;
*/ */
public class DebugActivity extends BaseActivity<ActivityDebugBinding, DebugActivityPresenter> { public class DebugActivity extends BaseActivity<ActivityDebugBinding, DebugActivityPresenter> {
private static final long TEST_TIMEOUT_MS = 10_000L;
private final Handler mainHandler = new Handler(Looper.getMainLooper());
private int testRequestGeneration;
/** /**
* 当前设备 * 当前设备
*/ */
@@ -176,22 +183,64 @@ public class DebugActivity extends BaseActivity<ActivityDebugBinding, DebugActiv
Toasty.error(mContext, "请先选择设备当前地址").show(); Toasty.error(mContext, "请先选择设备当前地址").show();
return; return;
} }
final SetAddress testDevice = setAddress;
final int requestGeneration = ++testRequestGeneration;
final long startTime = System.currentTimeMillis();
binding.btnReadAndParse.setEnabled(false);
showLoadingDialog("正在执行测试指令"); showLoadingDialog("正在执行测试指令");
Runnable timeoutTask = () -> {
if (requestGeneration != testRequestGeneration) {
return;
}
testRequestGeneration++;
binding.tvParse.setText("测试超时,请检查串口、设备地址或队列状态");
binding.btnReadAndParse.setEnabled(true);
hideLoadingDialog();
MyLog.test("设备调试超时:设备=" + sensorType + ",型号=" + sensorModel
+ ",地址=" + sensorAddress + ",等待=" + TEST_TIMEOUT_MS + "ms");
};
mainHandler.postDelayed(timeoutTask, TEST_TIMEOUT_MS);
MyLog.test("设备调试开始:设备=" + sensorType + ",型号=" + sensorModel + ",地址=" + sensorAddress);
MyQueue.start(MyQueue.TYPE_SENSOR, new QueueIOUITask<Boolean>() { MyQueue.start(MyQueue.TYPE_SENSOR, new QueueIOUITask<Boolean>() {
@Override @Override
public Boolean doInQueueThread() throws Exception { public Boolean doInQueueThread() throws Exception {
try { try {
return setAddress.test(sensorModel, sensorAddress); return testDevice.test(sensorModel, sensorAddress);
} catch (Exception e) { } catch (Throwable throwable) {
MyLog.test("设备调试执行异常:设备=" + sensorType + ",型号=" + sensorModel
+ ",地址=" + sensorAddress + ",异常="
+ throwable.getClass().getSimpleName() + ",原因=" + throwable.getMessage());
return false; return false;
} }
} }
@Override @Override
public void doInUIThread(Boolean aBoolean) { public void doInUIThread(Boolean aBoolean) {
if (requestGeneration != testRequestGeneration) {
return;
}
mainHandler.removeCallbacks(timeoutTask);
try {
binding.tvSend.setText(SerialPortUtil.T); binding.tvSend.setText(SerialPortUtil.T);
binding.tvReceive.setText(SerialPortUtil.R); binding.tvReceive.setText(SerialPortUtil.R);
binding.tvParse.setText(aBoolean ? toSingleLine(setAddress.getTestState()) : "数据采集失败"); binding.tvParse.setText(aBoolean ? toSingleLine(testDevice.getTestState()) : "数据采集失败");
MyLog.test("设备调试完成:设备=" + sensorType + ",型号=" + sensorModel
+ ",地址=" + sensorAddress + ",结果=" + (aBoolean ? "成功" : "失败")
+ ",耗时=" + (System.currentTimeMillis() - startTime) + "ms");
} finally {
binding.btnReadAndParse.setEnabled(true);
hideLoadingDialog();
}
}
@Override
public void onError(Throwable throwable) {
if (requestGeneration != testRequestGeneration) {
return;
}
mainHandler.removeCallbacks(timeoutTask);
binding.tvParse.setText("测试执行异常:" + throwable.getClass().getSimpleName());
binding.btnReadAndParse.setEnabled(true);
hideLoadingDialog(); hideLoadingDialog();
} }
}); });
@@ -266,4 +315,11 @@ public class DebugActivity extends BaseActivity<ActivityDebugBinding, DebugActiv
.replaceAll("\\s+", " "); .replaceAll("\\s+", " ");
} }
@Override
protected void onDestroy() {
testRequestGeneration++;
mainHandler.removeCallbacksAndMessages(null);
super.onDestroy();
}
} }
@@ -2,6 +2,8 @@ package com.example.iot_controlhost.ui.activity;
import android.content.Intent; import android.content.Intent;
import android.graphics.Color; import android.graphics.Color;
import android.os.Handler;
import android.os.Looper;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
@@ -42,9 +44,6 @@ import com.xuexiang.rxutil2.rxjava.task.RxAsyncTask;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
@@ -56,6 +55,10 @@ public class MainActivity extends BaseActivity<ActivityIncubationBinding, MainAc
AppBarViewModel appBarViewModel; AppBarViewModel appBarViewModel;
WorkDialog workDialog; WorkDialog workDialog;
private PasswordDialog lockPasswordDialog; private PasswordDialog lockPasswordDialog;
private final Handler lockHandler = new Handler(Looper.getMainLooper());
private final Runnable lockRunnable = this::lock;
private int showingDialogCount;
private boolean mainPageResumed;
@Override @Override
public void initView() { public void initView() {
@@ -280,21 +283,21 @@ public class MainActivity extends BaseActivity<ActivityIncubationBinding, MainAc
* 刷新锁屏时间 * 刷新锁屏时间
*/ */
private void refreshLock() { private void refreshLock() {
// MyLog.app("刷新主屏幕锁时间" + RoomSetting.getMainActivityOperateLockTimeOut()); stopLockTimer();
if (mainLockExecutorService != null) { if (!mainPageResumed || showingDialogCount > 0 || binding.mainLock.getVisibility() == View.VISIBLE) {
mainLockExecutorService.shutdownNow(); return;
} }
mainLockExecutorService = Executors.newSingleThreadScheduledExecutor(); lockHandler.postDelayed(lockRunnable, RoomSetting.getMainActivityOperateLockTimeOut() * 1000L);
mainLockExecutorService.schedule(() -> lock(), RoomSetting.getMainActivityOperateLockTimeOut(), TimeUnit.SECONDS);
} }
private void lock() { private void lock() {
if (!mainPageResumed || showingDialogCount > 0 || binding.mainLock.getVisibility() == View.VISIBLE) {
return;
}
MyLog.app("屏幕已锁"); MyLog.app("屏幕已锁");
mainLockExecutorService.shutdownNow(); stopLockTimer();
runOnUiThread(() -> {
binding.mainLock.setVisibility(View.VISIBLE); binding.mainLock.setVisibility(View.VISIBLE);
binding.mainKey.setImageDrawable(getDrawable(R.mipmap.lock)); binding.mainKey.setImageDrawable(getDrawable(R.mipmap.lock));
});
} }
private void showLockPasswordDialog() { private void showLockPasswordDialog() {
@@ -310,15 +313,50 @@ public class MainActivity extends BaseActivity<ActivityIncubationBinding, MainAc
lockPasswordDialog.show(); lockPasswordDialog.show();
} }
private ScheduledExecutorService mainLockExecutorService;
@Override @Override
public boolean onTouchEvent(MotionEvent event) { public boolean dispatchTouchEvent(MotionEvent event) {
int action = event.getAction(); if (event.getAction() == MotionEvent.ACTION_DOWN
if (action == MotionEvent.ACTION_DOWN) { && binding.mainLock.getVisibility() != View.VISIBLE) {
refreshLock(); refreshLock();
} }
return super.onTouchEvent(event); return super.dispatchTouchEvent(event);
}
public void onDialogShown() {
showingDialogCount++;
stopLockTimer();
}
public void onDialogDismissed() {
showingDialogCount = Math.max(0, showingDialogCount - 1);
if (showingDialogCount == 0) {
refreshLock();
}
}
private void stopLockTimer() {
lockHandler.removeCallbacks(lockRunnable);
}
@Override
protected void onResume() {
super.onResume();
mainPageResumed = true;
refreshLock();
}
@Override
protected void onPause() {
mainPageResumed = false;
stopLockTimer();
super.onPause();
}
@Override
protected void onDestroy() {
stopLockTimer();
lockHandler.removeCallbacksAndMessages(null);
super.onDestroy();
} }
@Override @Override
@@ -236,14 +236,17 @@ public class SetActivity extends BaseActivity<ActivitySetBinding, SetActivityPre
// //非集成控制器则需要设置传感器串口 // //非集成控制器则需要设置传感器串口
setList.add(new Set(getString(R.string.serial_port_sensor), HardwareSetting.getSensorSerialCom() + "\t:\t" + HardwareSetting.getSensorSerialComBaud(), view -> setList.add(new Set(getString(R.string.serial_port_sensor), HardwareSetting.getSensorSerialCom() + "\t:\t" + HardwareSetting.getSensorSerialComBaud(), view ->
new SetDeviceBaseDialog(mContext, getString(R.string.serial_port_sensor), SpinnerList.SERIAL_ADDRESS, getString(R.string.serial_port_sensor), SpinnerList.BAUD, getString(R.string.baud), (serial, baud) -> { new SetDeviceBaseDialog(mContext, getString(R.string.serial_port_sensor),
SpinnerList.SERIAL_ADDRESS, getString(R.string.serial_port_sensor), HardwareSetting.getSensorSerialCom(),
SpinnerList.BAUD, getString(R.string.baud), HardwareSetting.getSensorSerialComBaud(), (serial, baud) -> {
HardwareSetting.setSensorSerialCom(serial); HardwareSetting.setSensorSerialCom(serial);
HardwareSetting.setSensorSerialComBaud(baud); HardwareSetting.setSensorSerialComBaud(baud);
setController(); setController();
}).show())); }).show()));
setList.add(new Set(getString(R.string.serial_port_controller), HardwareSetting.getControllerSerialCom() + "\t:\t" + HardwareSetting.getControllerSerialBaud(), view -> setList.add(new Set(getString(R.string.serial_port_controller), HardwareSetting.getControllerSerialCom() + "\t:\t" + HardwareSetting.getControllerSerialBaud(), view ->
new SetDeviceBaseDialog(mContext, getString(R.string.serial_port_controller), SpinnerList.SERIAL_ADDRESS, getString(R.string.serial_port_sensor), new SetDeviceBaseDialog(mContext, getString(R.string.serial_port_controller),
SpinnerList.BAUD, getString(R.string.baud), (serial, baud) -> { SpinnerList.SERIAL_ADDRESS, getString(R.string.serial_port_sensor), HardwareSetting.getControllerSerialCom(),
SpinnerList.BAUD, getString(R.string.baud), HardwareSetting.getControllerSerialBaud(), (serial, baud) -> {
HardwareSetting.setControllerSerialCom(serial); HardwareSetting.setControllerSerialCom(serial);
HardwareSetting.setControllerSerialBaud(baud); HardwareSetting.setControllerSerialBaud(baud);
setController(); setController();
@@ -268,7 +271,7 @@ public class SetActivity extends BaseActivity<ActivitySetBinding, SetActivityPre
setRelay(); 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(), setList.add(new Set(getString(R.string.set_base), RoomController.relay.toString(), view -> new SetDeviceBaseDialog(mContext, getString(R.string.set_relay), RoomController.relay.getModels(),
getString(R.string.mode), SpinnerList.ADDRESS, getString(R.string.address), (model, address) -> { getString(R.string.mode), RoomController.relay.getModel(), SpinnerList.ADDRESS, getString(R.string.address), RoomController.relay.getAddress(), (model, address) -> {
RoomController.relay.setModel(model); RoomController.relay.setModel(model);
RoomController.relay.setAddress(address); RoomController.relay.setAddress(address);
setRelay(); setRelay();
@@ -308,7 +311,8 @@ public class SetActivity extends BaseActivity<ActivitySetBinding, SetActivityPre
})); }));
setList.add(new Set("基础配置", dehumidifier485.toString(), view -> setList.add(new Set("基础配置", dehumidifier485.toString(), view ->
new SetDeviceBaseDialog(mContext, "485除湿机基础配置", dehumidifier485.getModels(), new SetDeviceBaseDialog(mContext, "485除湿机基础配置", dehumidifier485.getModels(),
getString(R.string.select_model), Dehumidifier485.ADDRESS_LIST, getString(R.string.select_address), (model, address) -> { getString(R.string.select_model), dehumidifier485.getModel(), Dehumidifier485.ADDRESS_LIST,
getString(R.string.select_address), dehumidifier485.getAddress(), (model, address) -> {
dehumidifier485.setModel(model); dehumidifier485.setModel(model);
dehumidifier485.setAddress(address); dehumidifier485.setAddress(address);
setDehumidifier485(); setDehumidifier485();
@@ -330,7 +334,8 @@ public class SetActivity extends BaseActivity<ActivitySetBinding, SetActivityPre
})); }));
setList.add(new Set(getString(R.string.set_base_infrared), deHumidityInfrared.toString(), view -> setList.add(new Set(getString(R.string.set_base_infrared), deHumidityInfrared.toString(), view ->
new SetDeviceBaseDialog(mContext, getString(R.string.set_base_infrared), deHumidityInfrared.getModels(), new SetDeviceBaseDialog(mContext, getString(R.string.set_base_infrared), deHumidityInfrared.getModels(),
getString(R.string.select_model), SpinnerList.ADDRESS, getString(R.string.select_address), (model, address) -> { getString(R.string.select_model), deHumidityInfrared.getModel(), SpinnerList.ADDRESS,
getString(R.string.select_address), deHumidityInfrared.getAddress(), (model, address) -> {
deHumidityInfrared.setModel(model); deHumidityInfrared.setModel(model);
deHumidityInfrared.setAddress(address); deHumidityInfrared.setAddress(address);
setDeHumidityInfrared(); setDeHumidityInfrared();
@@ -394,7 +399,8 @@ public class SetActivity extends BaseActivity<ActivitySetBinding, SetActivityPre
})); }));
setList.add(new Set(getString(R.string.set_base_infrared), airConditionInfrared.toString(), view -> setList.add(new Set(getString(R.string.set_base_infrared), airConditionInfrared.toString(), view ->
new SetDeviceBaseDialog(mContext, getString(R.string.set_base_infrared), airConditionInfrared.getModels(), new SetDeviceBaseDialog(mContext, getString(R.string.set_base_infrared), airConditionInfrared.getModels(),
getString(R.string.select_model), SpinnerList.ADDRESS, getString(R.string.select_address), (model, address) -> { getString(R.string.select_model), airConditionInfrared.getModel(), SpinnerList.ADDRESS,
getString(R.string.select_address), airConditionInfrared.getAddress(), (model, address) -> {
airConditionInfrared.setModel(model); airConditionInfrared.setModel(model);
airConditionInfrared.setAddress(address); airConditionInfrared.setAddress(address);
setAirConditionDetail(airId, airConditionInfrared); setAirConditionDetail(airId, airConditionInfrared);
@@ -465,7 +471,8 @@ public class SetActivity extends BaseActivity<ActivitySetBinding, SetActivityPre
//需要配置地暖型号与地址 //需要配置地暖型号与地址
setList.add(new Set(getString(R.string.set_base), RoomController.floor.toString(), view -> setList.add(new Set(getString(R.string.set_base), RoomController.floor.toString(), view ->
new SetDeviceBaseDialog(mContext, "地暖" + RoomSetting.getFloorName(), RoomController.floor.getModels(), new SetDeviceBaseDialog(mContext, "地暖" + RoomSetting.getFloorName(), RoomController.floor.getModels(),
getString(R.string.mode), SpinnerList.ADDRESS, getString(R.string.address), (model, address) -> { getString(R.string.mode), RoomController.floor.getModel(), SpinnerList.ADDRESS,
getString(R.string.address), RoomController.floor.getAddress(), (model, address) -> {
RoomController.floor.setModel(model); RoomController.floor.setModel(model);
RoomController.floor.setAddress(address); RoomController.floor.setAddress(address);
setFloor(); setFloor();
@@ -608,7 +615,7 @@ public class SetActivity extends BaseActivity<ActivitySetBinding, SetActivityPre
} }
private void logSettingChanged(String name, Object oldValue, Object newValue) { private void logSettingChanged(String name, Object oldValue, Object newValue) {
UserLog.operate("本机屏幕", "修改设备设置:" + name); UserLog.operate("本机屏幕", "设备设置变化" + name + "=" + oldValue + "" + newValue);
MyLog.app("设备设置修改:" + name + "=" + oldValue + "" + newValue); MyLog.app("设备设置修改:" + name + "=" + oldValue + "" + newValue);
} }
@@ -158,7 +158,9 @@ public class SetAutoParamDialog extends BaseDialog<DialogSetAutoBinding> {
MMKVUtil.put(AutoModelSet.MAX_TEMPERATURE, max); MMKVUtil.put(AutoModelSet.MAX_TEMPERATURE, max);
MMKVUtil.put(AutoModelSet.MIN_TEMPERATURE, min); MMKVUtil.put(AutoModelSet.MIN_TEMPERATURE, min);
MMKVUtil.put(AutoModelSet.AUTO_TEMPERATURE, enabled); MMKVUtil.put(AutoModelSet.AUTO_TEMPERATURE, enabled);
UserLog.operate("本机屏幕", "修改自动温度参数"); UserLog.operate("本机屏幕", "自动温度参数变化:启用=" + oldEnabled + "" + enabled
+ ",目标=" + oldTarget + "" + target + "℃,上限=" + oldMax + "" + max
+ "℃,下限=" + oldMin + "" + min + "");
MyLog.app("自动温度参数修改:启用=" + oldEnabled + "" + enabled MyLog.app("自动温度参数修改:启用=" + oldEnabled + "" + enabled
+ ",目标=" + oldTarget + "" + target + "℃,上限=" + oldMax + "" + max + ",目标=" + oldTarget + "" + target + "℃,上限=" + oldMax + "" + max
+ "℃,下限=" + oldMin + "" + min + ""); + "℃,下限=" + oldMin + "" + min + "");
@@ -185,7 +187,9 @@ public class SetAutoParamDialog extends BaseDialog<DialogSetAutoBinding> {
MMKVUtil.put(AutoModelSet.MIN_HUMIDITY, min); MMKVUtil.put(AutoModelSet.MIN_HUMIDITY, min);
MMKVUtil.put(AutoModelSet.MAX_HUMIDITY, max); MMKVUtil.put(AutoModelSet.MAX_HUMIDITY, max);
MMKVUtil.put(AutoModelSet.AUTO_HUMIDITY, humidityEnabled); MMKVUtil.put(AutoModelSet.AUTO_HUMIDITY, humidityEnabled);
UserLog.operate("本机屏幕", "修改自动湿度参数"); UserLog.operate("本机屏幕", "自动湿度参数变化:启用=" + humidityOldEnabled + "" + humidityEnabled
+ ",目标=" + humidityOldTarget + "" + target + "% ,上限=" + humidityOldMax
+ "" + max + "% ,下限=" + humidityOldMin + "" + min + "%");
MyLog.app("自动湿度参数修改:启用=" + humidityOldEnabled + "" + humidityEnabled MyLog.app("自动湿度参数修改:启用=" + humidityOldEnabled + "" + humidityEnabled
+ ",目标=" + humidityOldTarget + "" + target + "% ,上限=" + humidityOldMax + "" + max + ",目标=" + humidityOldTarget + "" + target + "% ,上限=" + humidityOldMax + "" + max
+ "% ,下限=" + humidityOldMin + "" + min + "%"); + "% ,下限=" + humidityOldMin + "" + min + "%");
@@ -213,7 +217,9 @@ public class SetAutoParamDialog extends BaseDialog<DialogSetAutoBinding> {
MMKVUtil.put(AutoModelSet.CYCLE_STOP, stop); MMKVUtil.put(AutoModelSet.CYCLE_STOP, stop);
MMKVUtil.put(AutoModelSet.MAX_DENSITY_CO2, maxCo2); MMKVUtil.put(AutoModelSet.MAX_DENSITY_CO2, maxCo2);
MMKVUtil.put(AutoModelSet.AUTO_VENTILATOR, ventilatorEnabled); MMKVUtil.put(AutoModelSet.AUTO_VENTILATOR, ventilatorEnabled);
UserLog.operate("本机屏幕", "修改自动换气参数"); UserLog.operate("本机屏幕", "自动换气参数变化:启用=" + ventilatorOldEnabled + ""
+ ventilatorEnabled + ",开启=" + oldStart + "" + start + "分钟,关闭="
+ oldStop + "" + stop + "分钟,CO₂上限=" + oldMaxCo2 + "" + maxCo2 + "ppm");
MyLog.app("自动换气参数修改:启用=" + ventilatorOldEnabled + "" + ventilatorEnabled MyLog.app("自动换气参数修改:启用=" + ventilatorOldEnabled + "" + ventilatorEnabled
+ ",开启=" + oldStart + "" + start + "分钟,关闭=" + oldStop + "" + ",开启=" + oldStart + "" + start + "分钟,关闭=" + oldStop + ""
+ stop + "分钟,CO₂上限=" + oldMaxCo2 + "" + maxCo2 + "ppm"); + stop + "分钟,CO₂上限=" + oldMaxCo2 + "" + maxCo2 + "ppm");
@@ -247,7 +253,8 @@ public class SetAutoParamDialog extends BaseDialog<DialogSetAutoBinding> {
mode.setTargetTemp(temperature); mode.setTargetTemp(temperature);
mode.setTargetHumi(humidity); mode.setTargetHumi(humidity);
MyAIModeUtil.update(mode); MyAIModeUtil.update(mode);
UserLog.operate("本机屏幕", "修改智能模式当前龄期温湿度参数"); UserLog.operate("本机屏幕", "智能模式当前龄期参数变化:温度=" + oldTemperature + ""
+ temperature + "℃,湿度=" + oldHumidity + "" + humidity + "%");
MyLog.app("智能模式当前龄期参数修改:温度=" + oldTemperature + "" + temperature MyLog.app("智能模式当前龄期参数修改:温度=" + oldTemperature + "" + temperature
+ "℃,湿度=" + oldHumidity + "" + humidity + "%"); + "℃,湿度=" + oldHumidity + "" + humidity + "%");
RxBusUtils.get().post(RxTag.AI_INFO, 0); RxBusUtils.get().post(RxTag.AI_INFO, 0);
@@ -277,7 +284,8 @@ public class SetAutoParamDialog extends BaseDialog<DialogSetAutoBinding> {
mode.setTargetVentOpen(start); mode.setTargetVentOpen(start);
mode.setTargetVentClose(stop); mode.setTargetVentClose(stop);
MyAIModeUtil.update(mode); MyAIModeUtil.update(mode);
UserLog.operate("本机屏幕", "修改智能模式当前龄期换气参数"); UserLog.operate("本机屏幕", "智能模式当前龄期换气参数变化:开启=" + oldStart + ""
+ start + "分钟,关闭=" + oldStop + "" + stop + "分钟");
MyLog.app("智能模式当前龄期换气参数修改:开启=" + oldStart + "" + start MyLog.app("智能模式当前龄期换气参数修改:开启=" + oldStart + "" + start
+ "分钟,关闭=" + oldStop + "" + stop + "分钟"); + "分钟,关闭=" + oldStop + "" + stop + "分钟");
RxBusUtils.get().post(RxTag.AI_INFO, 0); RxBusUtils.get().post(RxTag.AI_INFO, 0);
@@ -186,7 +186,9 @@ public class SetBaseDialog extends BaseDialog<DialogSetBinding> {
MMKVUtil.put(AutoModelSet.AUTO_TEMPERATURE_HEAT, heatMode); MMKVUtil.put(AutoModelSet.AUTO_TEMPERATURE_HEAT, heatMode);
MMKVUtil.put(AutoModelSet.MIN_TEMPERATURE, target - bias); MMKVUtil.put(AutoModelSet.MIN_TEMPERATURE, target - bias);
MMKVUtil.put(AutoModelSet.MAX_TEMPERATURE, target + bias); MMKVUtil.put(AutoModelSet.MAX_TEMPERATURE, target + bias);
UserLog.operate("本机屏幕", "修改设置:温度控制模式"); UserLog.operate("本机屏幕", "设置变化:温度控制模式=" + oldMode + "" + selection
+ ",温度下限=" + min + "" + (target - bias) + "℃,温度上限="
+ max + "" + (target + bias) + "");
MyLog.app("温度控制模式修改:" + oldMode + "" + selection MyLog.app("温度控制模式修改:" + oldMode + "" + selection
+ ",自动调整目标上下限为" + (target - bias) + "~" + (target + bias) + ""); + ",自动调整目标上下限为" + (target - bias) + "~" + (target + bias) + "");
updateTemperatureControl(); updateTemperatureControl();
@@ -294,7 +296,7 @@ public class SetBaseDialog extends BaseDialog<DialogSetBinding> {
} }
private void logSettingChanged(String name, Object oldValue, Object newValue) { private void logSettingChanged(String name, Object oldValue, Object newValue) {
UserLog.operate("本机屏幕", "修改设置:" + name); UserLog.operate("本机屏幕", "设置变化" + name + "=" + oldValue + "" + newValue);
MyLog.app("设置修改:" + name + "=" + oldValue + "" + newValue); MyLog.app("设置修改:" + name + "=" + oldValue + "" + newValue);
} }
} }
@@ -32,7 +32,10 @@ public class SetDeviceBaseDialog extends BaseDialog<DialogDeviceCustomSetBinding
* @param ac2Hint 下拉框2的提示 * @param ac2Hint 下拉框2的提示
* @param listener 点击确定事件 * @param listener 点击确定事件
*/ */
public SetDeviceBaseDialog(@NonNull Context context, String title, String[] ac1List, String ac1Hint, String[] ac2List, String ac2Hint, OnCommonSetListener listener) { public SetDeviceBaseDialog(@NonNull Context context, String title,
String[] ac1List, String ac1Hint, String oldValue1,
String[] ac2List, String ac2Hint, String oldValue2,
OnCommonSetListener listener) {
super(context,true); super(context,true);
binding.tilSpinner1.setVisibility(View.VISIBLE); binding.tilSpinner1.setVisibility(View.VISIBLE);
binding.tilSpinner2.setVisibility(View.VISIBLE); binding.tilSpinner2.setVisibility(View.VISIBLE);
@@ -51,10 +54,13 @@ public class SetDeviceBaseDialog extends BaseDialog<DialogDeviceCustomSetBinding
Toasty.info(context, ac2Hint).show(); Toasty.info(context, ac2Hint).show();
return; return;
} }
listener.btnSet(binding.acInput1.getText().toString(), binding.acInput2.getText().toString()); String newValue1 = binding.acInput1.getText().toString();
UserLog.operate("本机屏幕", "修改设备配置:" + title); String newValue2 = binding.acInput2.getText().toString();
MyLog.app(title + "修改:" + ac1Hint + "=" + binding.acInput1.getText() listener.btnSet(newValue1, newValue2);
+ "" + ac2Hint + "=" + binding.acInput2.getText()); String change = title + "配置变化:" + ac1Hint + "=" + oldValue1 + "" + newValue1
+ "" + ac2Hint + "=" + oldValue2 + "" + newValue2;
UserLog.operate("本机屏幕", change);
MyLog.app(change);
dismiss(); dismiss();
}); });
} }
@@ -48,7 +48,7 @@ public class SetPWMDialog extends BaseDialog<DialogDeviceCustomSetBinding> {
} }
int oldPwm = RoomController.floor.getPwm(gear).getPwm(); int oldPwm = RoomController.floor.getPwm(gear).getPwm();
RoomController.floor.getPwm(gear).setPwm(pwm); RoomController.floor.getPwm(gear).setPwm(pwm);
UserLog.operate("本机屏幕", "修改地暖PWM" + gear + ""); UserLog.operate("本机屏幕", "地暖PWM变化" + gear + "=" + oldPwm + "" + pwm);
MyLog.app("地暖PWM修改:档位=" + gear + "PWM=" + oldPwm + "" + pwm); MyLog.app("地暖PWM修改:档位=" + gear + "PWM=" + oldPwm + "" + pwm);
} catch (Exception e) { } catch (Exception e) {
Toasty.error(mContext, "输入格式有误,请检查").show(); Toasty.error(mContext, "输入格式有误,请检查").show();
@@ -85,7 +85,8 @@ public class SetPortDialog extends BaseDialog<DialogDeviceCustomSetBinding> {
} }
//确定后关闭弹窗 //确定后关闭弹窗
RoomController.relay.setPowerSupply(false, port); RoomController.relay.setPowerSupply(false, port);
UserLog.operate("本机屏幕", "修改继电器端口配置:端口" + port); UserLog.operate("本机屏幕", "继电器端口配置变化:端口" + port + "=" + oldDeviceName
+ "" + (deviceName.isEmpty() ? "未配置" : deviceName));
MyLog.app("继电器端口配置修改:端口=" + port + ",设备=" + oldDeviceName MyLog.app("继电器端口配置修改:端口=" + port + ",设备=" + oldDeviceName
+ "" + (deviceName.isEmpty() ? "未配置" : deviceName)); + "" + (deviceName.isEmpty() ? "未配置" : deviceName));
dismiss(); dismiss();
@@ -70,6 +70,9 @@ public class SetSensorDialog extends BaseDialog<DialogSetSensorBinding> {
}); });
binding.btnYes.setOnClickListener(button -> { binding.btnYes.setOnClickListener(button -> {
//设置事件 //设置事件
boolean oldEnable = sensor.isEnable();
String oldModel = sensor.getModel();
String oldAddress = sensor.getAddress();
boolean enable = binding.switcher.isChecked(); boolean enable = binding.switcher.isChecked();
if (enable) { if (enable) {
String address = binding.acAddress.getText().toString(); String address = binding.acAddress.getText().toString();
@@ -89,9 +92,11 @@ public class SetSensorDialog extends BaseDialog<DialogSetSensorBinding> {
sensor.setEnable(enable); sensor.setEnable(enable);
//刷新页面 //刷新页面
listener.run(sensor); listener.run(sensor);
UserLog.operate("本机屏幕", "修改传感器配置" + sensor.getName()); String change = "传感器配置变化:名称=" + sensor.getName() + ",启用=" + oldEnable + ""
MyLog.app("传感器配置修改:名称=" + sensor.getName() + ",启用=" + sensor.isEnable() + sensor.isEnable() + ",型号=" + oldModel + "" + sensor.getModel()
+ "型号=" + sensor.getModel() + ",地址=" + sensor.getAddress()); + "地址=" + oldAddress + "" + sensor.getAddress();
UserLog.operate("本机屏幕", change);
MyLog.app(change);
dismiss(); dismiss();
}); });
} }
@@ -43,7 +43,7 @@ public class THBiasDialog extends BaseDialog<DialogBiasThBinding> {
double newValue = Double.parseDouble(binding.tiSet1.getText().toString()); double newValue = Double.parseDouble(binding.tiSet1.getText().toString());
MMKVUtil.put(isTemp ? RoomSetting.BIAS_TEMP : RoomSetting.BIAS_HUMIDITY, newValue); MMKVUtil.put(isTemp ? RoomSetting.BIAS_TEMP : RoomSetting.BIAS_HUMIDITY, newValue);
String typeName = isTemp ? "温度" : "湿度"; String typeName = isTemp ? "温度" : "湿度";
UserLog.operate("本机屏幕", "修改" + typeName + "校准偏差"); UserLog.operate("本机屏幕", typeName + "校准偏差变化:" + oldValue + "" + newValue);
MyLog.app(typeName + "校准偏差修改:" + oldValue + "" + newValue); MyLog.app(typeName + "校准偏差修改:" + oldValue + "" + newValue);
dismiss(); dismiss();
}); });
@@ -9,6 +9,7 @@ import android.view.WindowManager;
import android.widget.TextView; import android.widget.TextView;
import com.example.iot_controlhost.R; import com.example.iot_controlhost.R;
import com.example.iot_controlhost.ui.activity.MainActivity;
import com.google.android.material.progressindicator.LinearProgressIndicator; import com.google.android.material.progressindicator.LinearProgressIndicator;
import com.xuexiang.rxutil2.rxjava.RxJavaUtils; import com.xuexiang.rxutil2.rxjava.RxJavaUtils;
import com.xuexiang.rxutil2.rxjava.task.RxUITask; import com.xuexiang.rxutil2.rxjava.task.RxUITask;
@@ -17,6 +18,7 @@ public class DialogUtil {
private static Dialog progressDialog; private static Dialog progressDialog;
private static TextView textViewInfo; private static TextView textViewInfo;
private static LinearProgressIndicator progressIndicator; private static LinearProgressIndicator progressIndicator;
private static Context progressContext;
/** /**
* 显示加载中弹窗,支持中途改变提示文字 * 显示加载中弹窗,支持中途改变提示文字
@@ -26,10 +28,10 @@ public class DialogUtil {
*/ */
public static void showLoadingDialog(Context context, String info) { public static void showLoadingDialog(Context context, String info) {
if (progressDialog != null) { if (progressDialog != null) {
progressDialog.dismiss(); // 如果之前有Dialog显示,则先关闭 hideLoadingDialog(); // 如果之前有Dialog显示,则先关闭并同步锁屏计数
progressDialog = null; // 释放旧Dialog
} }
progressDialog = new Dialog(context, R.style.MyDialogTheme); progressDialog = new Dialog(context, R.style.MyDialogTheme);
progressContext = context;
progressDialog.setContentView(R.layout.dialog_loading); progressDialog.setContentView(R.layout.dialog_loading);
progressDialog.setCancelable(false); progressDialog.setCancelable(false);
textViewInfo = progressDialog.findViewById(R.id.tv_loading); textViewInfo = progressDialog.findViewById(R.id.tv_loading);
@@ -44,6 +46,7 @@ public class DialogUtil {
layoutParams.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN; layoutParams.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN;
progressDialog.getWindow().setAttributes(layoutParams); progressDialog.getWindow().setAttributes(layoutParams);
progressDialog.show(); progressDialog.show();
notifyDialogShown(context);
} }
/** /**
@@ -69,6 +72,9 @@ public class DialogUtil {
public static void hideLoadingDialog() { public static void hideLoadingDialog() {
if (progressDialog != null) { if (progressDialog != null) {
progressDialog.dismiss(); progressDialog.dismiss();
notifyDialogDismissed(progressContext);
progressDialog = null;
progressContext = null;
} }
} }
@@ -79,7 +85,11 @@ public class DialogUtil {
* @param info 提示信息 * @param info 提示信息
*/ */
public static void showProgressIndicatorDialog(Context context, String info) { public static void showProgressIndicatorDialog(Context context, String info) {
if (progressDialog != null) {
hideLoadingDialog();
}
progressDialog = new Dialog(context, R.style.MyDialogTheme); progressDialog = new Dialog(context, R.style.MyDialogTheme);
progressContext = context;
progressDialog.setContentView(R.layout.dialog_process_indicator); progressDialog.setContentView(R.layout.dialog_process_indicator);
progressDialog.setCancelable(false); progressDialog.setCancelable(false);
textViewInfo = progressDialog.findViewById(R.id.tv_loading); textViewInfo = progressDialog.findViewById(R.id.tv_loading);
@@ -97,6 +107,7 @@ public class DialogUtil {
} }
textViewInfo.setText(info); textViewInfo.setText(info);
progressDialog.show(); progressDialog.show();
notifyDialogShown(context);
} }
/** /**
@@ -118,6 +129,21 @@ public class DialogUtil {
public static void hideProgressIndicatorDialog() { public static void hideProgressIndicatorDialog() {
if (progressDialog != null) { if (progressDialog != null) {
progressDialog.dismiss(); progressDialog.dismiss();
notifyDialogDismissed(progressContext);
progressDialog = null;
progressContext = null;
}
}
private static void notifyDialogShown(Context context) {
if (context instanceof MainActivity) {
((MainActivity) context).onDialogShown();
}
}
private static void notifyDialogDismissed(Context context) {
if (context instanceof MainActivity) {
((MainActivity) context).onDialogDismissed();
} }
} }
@@ -276,6 +276,7 @@ public class MyUtil {
*/ */
public static void exitApp() { public static void exitApp() {
UserLog.system("本机屏幕", "关闭软件"); UserLog.system("本机屏幕", "关闭软件");
MyLog.app("软件生命周期:收到本机关闭软件指令,开始停止自动控制和设备输出");
MyUtil.stopAppControl("正在关闭软件中", () -> AppUtils.exitApp()); MyUtil.stopAppControl("正在关闭软件中", () -> AppUtils.exitApp());
} }
@@ -288,6 +289,7 @@ public class MyUtil {
public static void relaunchApp(String source) { public static void relaunchApp(String source) {
UserLog.system(source, "重启软件"); UserLog.system(source, "重启软件");
MyLog.app("软件生命周期:收到重启软件指令,来源=" + source + ",开始停止自动控制和设备输出");
MyUtil.stopAppControl("正在重启软件中", () -> { MyUtil.stopAppControl("正在重启软件中", () -> {
// AppUtils.relaunchApp(); // 这个方法重启不干净 // AppUtils.relaunchApp(); // 这个方法重启不干净
ProcessPhoenix.triggerRebirth(MyApp.getAppContext()); ProcessPhoenix.triggerRebirth(MyApp.getAppContext());
@@ -303,6 +305,7 @@ public class MyUtil {
public static void shutDown(String source) { public static void shutDown(String source) {
UserLog.system(source, "关闭设备"); UserLog.system(source, "关闭设备");
MyLog.app("主机生命周期:收到关闭设备指令,来源=" + source + ",开始停止自动控制和设备输出");
//先关闭所有设备 //先关闭所有设备
MyUtil.stopAppControl("正在关闭设备中", () -> MyAPIContext.getInstance().shutDown()); MyUtil.stopAppControl("正在关闭设备中", () -> MyAPIContext.getInstance().shutDown());
} }
@@ -316,6 +319,7 @@ public class MyUtil {
public static void reboot(String source) { public static void reboot(String source) {
UserLog.system(source, "重启设备"); UserLog.system(source, "重启设备");
MyLog.app("主机生命周期:收到重启设备指令,来源=" + source + ",开始停止自动控制和设备输出");
MyUtil.stopAppControl("正在重启设备中", () -> MyAPIContext.getInstance().reBoot()); MyUtil.stopAppControl("正在重启设备中", () -> MyAPIContext.getInstance().reBoot());
} }
@@ -370,8 +374,14 @@ public class MyUtil {
*/ */
public static boolean autoControlOperateThird(Controller controller, boolean targetState, boolean needAutoLog) { public static boolean autoControlOperateThird(Controller controller, boolean targetState, boolean needAutoLog) {
if (!controller.isAvailable()) { if (!controller.isAvailable()) {
// 聚合设备(例如换气扇总控)会自行跳过未安装的子设备,避免每轮自动控制重复报错。
if (needAutoLog) {
MyLog.autoError("自动控制跳过:设备=" + controller.getName() + ",目标="
+ (targetState ? "开启" : "关闭") + ",原因=设备不可用");
}
return false; return false;
} }
boolean previousState = controller.isPowerSupply();
int maxCount = 3; int maxCount = 3;
int count = 0; int count = 0;
boolean success = false; boolean success = false;
@@ -390,6 +400,16 @@ public class MyUtil {
if (!success) { if (!success) {
MyLog.autoError((needAutoLog ? "自动" : "系统联动") + "操作(连续" + maxCount + "次)失败:" MyLog.autoError((needAutoLog ? "自动" : "系统联动") + "操作(连续" + maxCount + "次)失败:"
+ (targetState ? "开启" : "关闭") + controller.getName()); + (targetState ? "开启" : "关闭") + controller.getName());
if (needAutoLog) {
UserLog.systemError("自动控制", "设备操作失败:" + controller.getName() + ",状态="
+ (previousState ? "开启" : "关闭") + "" + (targetState ? "开启" : "关闭"));
}
} else if (needAutoLog && previousState != targetState) {
UserLog.system("自动控制", "设备状态变化:" + controller.getName() + ""
+ (previousState ? "开启" : "关闭") + "" + (targetState ? "开启" : "关闭"));
MyLog.auto("自动控制状态确认:设备=" + controller.getName() + ",原状态="
+ (previousState ? "开启" : "关闭") + ",目标状态="
+ (targetState ? "开启" : "关闭") + ",尝试次数=" + count + ",结果=成功");
} }
//自动操作完设备需要更新UI //自动操作完设备需要更新UI
RxBusUtils.get().post(RxTag.UPDATE_AUTO, 2); RxBusUtils.get().post(RxTag.UPDATE_AUTO, 2);
@@ -5,36 +5,39 @@ import android.os.Looper;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.example.iot_controlhost.utils.log.MyLog; import com.example.iot_controlhost.utils.log.MyLog;
import com.example.iot_controlhost.utils.log.UserLog;
import com.xuexiang.rxutil2.rxjava.impl.IRxUITask; import com.xuexiang.rxutil2.rxjava.impl.IRxUITask;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class PollingTask { public class PollingTask {
private static Map<String, PollingTask> instances = new HashMap<>(); private static final Map<String, PollingTask> instances = new ConcurrentHashMap<>();
private final String instanceId;
private ScheduledExecutorService scheduler; private ScheduledExecutorService scheduler;
private Map<String, ScheduledFuture<?>> taskMap; private final Map<String, ScheduledFuture<?>> taskMap = new ConcurrentHashMap<>();
private final Map<String, Long> lastUserErrorTime = new ConcurrentHashMap<>();
private volatile boolean stopped;
// 私有化构造函数,确保外部不能直接实例化 // 私有化构造函数,确保外部不能直接实例化
private PollingTask() { private PollingTask(String instanceId) {
this.instanceId = instanceId;
createNewScheduler(); createNewScheduler();
} }
// 创建新的 ScheduledExecutorService 实例 // 创建新的 ScheduledExecutorService 实例
private void createNewScheduler() { private synchronized void createNewScheduler() {
if (scheduler != null && !scheduler.isShutdown()) { if (scheduler != null && !scheduler.isShutdown()) {
scheduler.shutdown(); scheduler.shutdownNow();
} }
scheduler = Executors.newScheduledThreadPool(3); scheduler = Executors.newScheduledThreadPool(3);
taskMap = new HashMap<>(); taskMap.clear();
stopped = false;
} }
/** /**
@@ -48,7 +51,7 @@ public class PollingTask {
// 获取基于ID的单例实例 // 获取基于ID的单例实例
public static synchronized PollingTask getInstance(String id) { public static synchronized PollingTask getInstance(String id) {
if (!instances.containsKey(id)) { if (!instances.containsKey(id)) {
instances.put(id, new PollingTask()); instances.put(id, new PollingTask(id));
} }
return instances.get(id); return instances.get(id);
} }
@@ -59,13 +62,22 @@ public class PollingTask {
* @param delaySeconds 延迟时间(以秒为单位) * @param delaySeconds 延迟时间(以秒为单位)
* @param task 需要执行的任务 * @param task 需要执行的任务
*/ */
public void startDelayedTask(String taskId, long delaySeconds, @NonNull final Runnable task) { public synchronized void startDelayedTask(String taskId, long delaySeconds, @NonNull final Runnable task) {
stopPollingTask(taskId); // 如果已有相同ID的任务,先停止 stopPollingTask(taskId); // 如果已有相同ID的任务,先停止
if (stopped) {
ScheduledFuture<?> future = scheduler.schedule(() -> { MyLog.auto("调度器已停止,忽略延迟任务:控制器=" + instanceId + ",任务=" + taskId);
task.run(); // 执行任务 return;
}, delaySeconds, TimeUnit.SECONDS); }
ensureScheduler();
try {
ScheduledFuture<?> future = scheduler.schedule(
safeRunnable(taskId, task), Math.max(0, delaySeconds), TimeUnit.SECONDS);
taskMap.put(taskId, future); // 保存任务的ScheduledFuture taskMap.put(taskId, future); // 保存任务的ScheduledFuture
MyLog.auto("调度任务已创建:控制器=" + instanceId + ",任务=" + taskId
+ ",延迟=" + Math.max(0, delaySeconds) + "");
} catch (RejectedExecutionException e) {
recordTaskError(taskId, "调度延迟任务被拒绝", e);
}
} }
/** /**
@@ -120,17 +132,72 @@ public class PollingTask {
* @param intervalSeconds 轮询间隔时间(以秒为单位) * @param intervalSeconds 轮询间隔时间(以秒为单位)
* @param task 需要执行的任务 * @param task 需要执行的任务
*/ */
private void startPollingTask(String taskId, long intervalSeconds, @NonNull final Runnable task) { private synchronized void startPollingTask(String taskId, long intervalSeconds, @NonNull final Runnable task) {
stopPollingTask(taskId); // 如果已有相同ID的任务,先停止 stopPollingTask(taskId); // 如果已有相同ID的任务,先停止
if (stopped) {
ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(task, 0, intervalSeconds, TimeUnit.SECONDS); MyLog.auto("调度器已停止,忽略轮询任务:控制器=" + instanceId + ",任务=" + taskId);
return;
}
ensureScheduler();
long safeInterval = Math.max(1, intervalSeconds);
try {
ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(
safeRunnable(taskId, task), 0, safeInterval, TimeUnit.SECONDS);
taskMap.put(taskId, future); // 保存任务的ScheduledFuture taskMap.put(taskId, future); // 保存任务的ScheduledFuture
MyLog.auto("轮询任务已创建:控制器=" + instanceId + ",任务=" + taskId
+ ",间隔=" + safeInterval + "");
} catch (RejectedExecutionException e) {
recordTaskError(taskId, "调度轮询任务被拒绝", e);
}
}
private Runnable safeRunnable(String taskId, Runnable task) {
return () -> {
try {
task.run();
} catch (Throwable throwable) {
recordTaskError(taskId, "任务执行异常,调度器将继续运行", throwable);
}
};
}
private void recordTaskError(String taskId, String action, Throwable throwable) {
String detail = action + ":控制器=" + instanceId + ",任务=" + taskId
+ ",异常=" + throwable.getClass().getSimpleName() + ",原因=" + throwable.getMessage();
MyLog.autoError(detail);
long now = System.currentTimeMillis();
long lastTime = lastUserErrorTime.getOrDefault(taskId, 0L);
if (now - lastTime >= TimeUnit.MINUTES.toMillis(5)) {
lastUserErrorTime.put(taskId, now);
UserLog.systemError("任务调度", action + "" + instanceId + "/" + taskId);
}
}
private void ensureScheduler() {
if (scheduler == null || scheduler.isShutdown() || scheduler.isTerminated()) {
MyLog.auto("自动任务调度器已失效,正在重建:控制器=" + instanceId);
createNewScheduler();
}
}
public boolean isAvailable() {
return !stopped && scheduler != null && !scheduler.isShutdown() && !scheduler.isTerminated();
}
public boolean hasActiveTasks() {
if (!isAvailable()) {
return false;
}
taskMap.entrySet().removeIf(entry -> entry.getValue() == null
|| entry.getValue().isCancelled() || entry.getValue().isDone());
return !taskMap.isEmpty();
} }
// 停止所有轮询任务 // 停止所有轮询任务
public void stopAllPollingTasks() { public synchronized void stopAllPollingTasks() {
stopped = true;
for (String taskId : taskMap.keySet()) { for (String taskId : taskMap.keySet()) {
MyLog.test("关闭轮询任务" + taskId); MyLog.auto("停止调度任务:控制器=" + instanceId + ",任务=" + taskId);
} }
if (scheduler != null && !scheduler.isShutdown()) { if (scheduler != null && !scheduler.isShutdown()) {
// scheduler.shutdown(); // 停止所有任务 // scheduler.shutdown(); // 停止所有任务
@@ -144,14 +211,14 @@ public class PollingTask {
taskMap.clear(); // 清空任务映射 taskMap.clear(); // 清空任务映射
} }
// 从实例映射中移除当前实例 // 从实例映射中移除当前实例
instances.values().remove(this); instances.remove(instanceId, this);
} }
/** /**
* 停止轮询任务 * 停止轮询任务
* *
* @param taskId 任务ID * @param taskId 任务ID
*/ */
public void stopPollingTask(String taskId) { public synchronized void stopPollingTask(String taskId) {
ScheduledFuture<?> future = taskMap.get(taskId); ScheduledFuture<?> future = taskMap.get(taskId);
if (future != null) { if (future != null) {
future.cancel(true); // 取消任务,中断正在执行的任务 future.cancel(true); // 取消任务,中断正在执行的任务
@@ -14,19 +14,33 @@ public abstract class Control {
/** /**
* 是否在开始中 * 是否在开始中
*/ */
protected boolean start = false; protected volatile boolean start = false;
protected PollingTask pollingTask ; protected PollingTask pollingTask ;
/** /**
* 开始控制 * 开始控制
*/ */
public void start() { public synchronized void start() {
if (start) { if (start && pollingTask != null && pollingTask.hasActiveTasks()) {
return; return;
} }
if (start) {
MyLog.autoError("检测到自动控制任务已无有效调度,尝试自恢复:" + getClass().getSimpleName());
}
pollingTask = PollingTask.getInstance(getClass().getSimpleName()); pollingTask = PollingTask.getInstance(getClass().getSimpleName());
try {
start = startMethod(); start = startMethod();
if (start) {
MyLog.auto("自动控制启动成功:" + getClass().getSimpleName());
} else {
MyLog.autoError("自动控制未启动:" + getClass().getSimpleName() + ",设备不可用或配置不满足");
}
} catch (Throwable throwable) {
start = false;
MyLog.autoError("自动控制启动异常:" + getClass().getSimpleName() + ",异常="
+ throwable.getClass().getSimpleName() + ",原因=" + throwable.getMessage());
}
} }
/** /**
@@ -39,16 +53,28 @@ public abstract class Control {
/** /**
* 停止控制 * 停止控制
*/ */
public void stop() { public synchronized void stop() {
if(pollingTask != null){ if(pollingTask != null){
pollingTask.stopAllPollingTasks(); pollingTask.stopAllPollingTasks();
} }
if (!start) { if (!start) {
return; return;
} }
try {
stopMethod(); stopMethod();
RxBusUtils.get().post(RxTag.UPDATE_AUTO, 2); RxBusUtils.get().post(RxTag.UPDATE_AUTO, 2);
MyLog.auto("自动控制停止成功:" + getClass().getSimpleName());
} catch (Throwable throwable) {
MyLog.autoError("自动控制停止异常:" + getClass().getSimpleName() + ",异常="
+ throwable.getClass().getSimpleName() + ",原因=" + throwable.getMessage());
} finally {
start = false; start = false;
pollingTask = null;
}
}
public boolean isRunning() {
return start && pollingTask != null && pollingTask.hasActiveTasks();
} }
/** /**
@@ -15,8 +15,7 @@ import com.example.iot_controlhost.utils.global.RxTag;
import com.example.iot_controlhost.utils.global.SpinnerList; import com.example.iot_controlhost.utils.global.SpinnerList;
import com.example.iot_controlhost.utils.global.Variable; import com.example.iot_controlhost.utils.global.Variable;
import com.example.iot_controlhost.utils.log.MyLog; import com.example.iot_controlhost.utils.log.MyLog;
import com.xuexiang.rxutil2.rxjava.DisposablePool; import com.example.iot_controlhost.utils.log.UserLog;
import com.xuexiang.rxutil2.rxjava.RxJavaUtils;
/** /**
* @Description 自动模式-控制逻辑-湿度 * @Description 自动模式-控制逻辑-湿度
@@ -29,6 +28,9 @@ public class HumidityController extends Control {
*/ */
private static boolean needOpenToTarget = false; private static boolean needOpenToTarget = false;
private static boolean needDehumidifyToTarget = false; private static boolean needDehumidifyToTarget = false;
private Boolean lastHumidifierTarget;
private Boolean lastDehumidifierTarget;
private boolean invalidHumidityLogged;
/** /**
* 目标湿度 * 目标湿度
*/ */
@@ -53,7 +55,23 @@ public class HumidityController extends Control {
return false; return false;
} }
pollingTask.startPollingTaskOnIOThread(RxTag.AUTO_HUMIDITY, Variable.AUTO_CHECK_TIME, () -> { pollingTask.startPollingTaskOnIOThread(RxTag.AUTO_HUMIDITY, Variable.AUTO_CHECK_TIME, () -> {
double cur = RoomSensor.getAverageIndoorHumidity(); // 自动控制必须使用未叠加校准偏差的真实湿度;校准值仅用于界面显示和上报。
double cur = RoomSensor.getBeforeCalibratedIndoorHumidity();
double calibrated = RoomSensor.getAverageIndoorHumidity();
if (cur <= 0) {
MyLog.autoError("湿度自动控制跳过:没有有效真实湿度,校准湿度=" + calibrated + "%");
if (!invalidHumidityLogged) {
UserLog.systemError("自动控制", "湿度数据无效,已停止加湿以避免持续运行");
invalidHumidityLogged = true;
}
needOpenToTarget = false;
if (RoomController.humidifier.isAvailable() && RoomController.humidifier.isPowerSupply()) {
MyQueue.getInstance(MyQueue.TYPE_CONTROLLER).addTask(new QueueIOTask(() ->
MyUtil.autoControlOperateThird(RoomController.humidifier, false)));
}
return;
}
invalidHumidityLogged = false;
double min = 0; double min = 0;
double max = 0; double max = 0;
if (RoomSetting.getMode() == 1) { if (RoomSetting.getMode() == 1) {
@@ -85,10 +103,10 @@ public class HumidityController extends Control {
needDehumidifyToTarget = false; needDehumidifyToTarget = false;
needDehumidify = false; needDehumidify = false;
} }
// 真实湿度已接近 100%,禁止自动除湿 // 真实湿度已接近100%时,即使负校准偏差导致显示值较低,也绝不继续加湿。
if (RoomSensor.getBeforeCalibratedIndoorHumidity() > 99) { if (cur >= 99) {
needDehumidify = false; needHumidify = false;
needDehumidifyToTarget = false; needOpenToTarget = false;
} }
// === 互斥处理 === // === 互斥处理 ===
@@ -99,6 +117,18 @@ public class HumidityController extends Control {
} }
boolean humidifierState = needHumidify; boolean humidifierState = needHumidify;
boolean dehumidifierState = needDehumidify; boolean dehumidifierState = needDehumidify;
if (lastHumidifierTarget == null || lastHumidifierTarget != humidifierState) {
MyLog.auto("湿度控制决策变化:加湿器=" + formatState(lastHumidifierTarget) + ""
+ formatState(humidifierState) + ",真实湿度=" + cur + "%"
+ ",校准湿度=" + calibrated + "% ,目标=" + target + "% ,区间=" + min + "~" + max + "%");
lastHumidifierTarget = humidifierState;
}
if (lastDehumidifierTarget == null || lastDehumidifierTarget != dehumidifierState) {
MyLog.auto("湿度控制决策变化:除湿机=" + formatState(lastDehumidifierTarget) + ""
+ formatState(dehumidifierState) + ",真实湿度=" + cur + "%"
+ ",校准湿度=" + calibrated + "% ,目标=" + target + "% ,区间=" + min + "~" + max + "%");
lastDehumidifierTarget = dehumidifierState;
}
Controller autoDehumidifier = RoomController.getAutoDehumidifier(); Controller autoDehumidifier = RoomController.getAutoDehumidifier();
int dehumidifierTargetHumidity = (int) Math.round(target); int dehumidifierTargetHumidity = (int) Math.round(target);
boolean needRefreshDehumidifierSetting = autoDehumidifier instanceof Dehumidifier485 boolean needRefreshDehumidifierSetting = autoDehumidifier instanceof Dehumidifier485
@@ -109,16 +139,20 @@ public class HumidityController extends Control {
|| autoDehumidifier.isPowerSupply() == dehumidifierState && !needRefreshDehumidifierSetting; || autoDehumidifier.isPowerSupply() == dehumidifierState && !needRefreshDehumidifierSetting;
if (humidifierKeep && dehumidifierKeep) { if (humidifierKeep && dehumidifierKeep) {
MyLog.auto("湿度-控制维持当前状态"); // 状态维持时不重复输出,避免淹没真正的状态变化和异常日志。
} else { } else {
// 加湿器控制 // 加湿器控制
if (RoomController.humidifier.isAvailable()) { if (RoomController.humidifier.isAvailable()) {
MyLog.auto("自动:" + (humidifierState ? "开启" : "关闭") + "加湿器--");
MyQueue.getInstance(MyQueue.TYPE_CONTROLLER).addTask(new QueueIOTask(() -> { MyQueue.getInstance(MyQueue.TYPE_CONTROLLER).addTask(new QueueIOTask(() -> {
MyUtil.autoControlOperateThird(RoomController.humidifier, humidifierState); boolean humidifierResult = MyUtil.autoControlOperateThird(RoomController.humidifier, humidifierState);
MyLog.auto("湿度控制执行结果:设备=加湿器,目标=" + formatState(humidifierState)
+ ",结果=" + (humidifierResult ? "成功" : "失败") + ",真实湿度=" + cur
+ "% ,校准湿度=" + calibrated + "%");
String airExchangeMode = AutoModelSet.getAirExchangeMode(); String airExchangeMode = AutoModelSet.getAirExchangeMode();
if (RoomController.airExchange.isAvailable() && airExchangeMode.equals(SpinnerList.AIR_EXCHANGE_MODES[2])) { if (RoomController.airExchange.isAvailable() && airExchangeMode.equals(SpinnerList.AIR_EXCHANGE_MODES[2])) {
MyUtil.autoControlOperateThird(RoomController.airExchange, humidifierState); boolean airExchangeResult = MyUtil.autoControlOperateThird(RoomController.airExchange, humidifierState);
MyLog.auto("湿度联动执行结果:设备=新风,目标=" + formatState(humidifierState)
+ ",结果=" + (airExchangeResult ? "成功" : "失败"));
} }
})); }));
} }
@@ -143,6 +177,9 @@ public class HumidityController extends Control {
protected void stopMethod() { protected void stopMethod() {
needOpenToTarget = false; needOpenToTarget = false;
needDehumidifyToTarget = false; needDehumidifyToTarget = false;
lastHumidifierTarget = null;
lastDehumidifierTarget = null;
invalidHumidityLogged = false;
MyQueue.getInstance(MyQueue.TYPE_CONTROLLER).addTask(new QueueIOTask(() -> { MyQueue.getInstance(MyQueue.TYPE_CONTROLLER).addTask(new QueueIOTask(() -> {
MyUtil.autoControlOperateThird(RoomController.humidifier); MyUtil.autoControlOperateThird(RoomController.humidifier);
Controller autoDehumidifier = RoomController.getAutoDehumidifier(); Controller autoDehumidifier = RoomController.getAutoDehumidifier();
@@ -157,4 +194,11 @@ public class HumidityController extends Control {
MyLog.auto("停止自动控制湿度————————————————————————————————————————————————————"); MyLog.auto("停止自动控制湿度————————————————————————————————————————————————————");
} }
private String formatState(Boolean state) {
if (state == null) {
return "未初始化";
}
return state ? "开启" : "关闭";
}
} }
@@ -9,6 +9,7 @@ import com.example.iot_controlhost.utils.global.RoomSetting;
import com.example.iot_controlhost.utils.global.RxTag; import com.example.iot_controlhost.utils.global.RxTag;
import com.example.iot_controlhost.utils.global.Variable; import com.example.iot_controlhost.utils.global.Variable;
import com.example.iot_controlhost.utils.log.MyLog; import com.example.iot_controlhost.utils.log.MyLog;
import com.example.iot_controlhost.utils.log.UserLog;
import com.xuexiang.rxutil2.rxbus.RxBusUtils; import com.xuexiang.rxutil2.rxbus.RxBusUtils;
/** /**
@@ -153,12 +154,14 @@ public abstract class ControlStrategy {
//防止重复发送相同的提示日志 //防止重复发送相同的提示日志
tipsTempStr = tips.toString(); tipsTempStr = tips.toString();
MyLog.auto("自动温控:" + tips); MyLog.auto("自动温控:" + tips);
UserLog.system("自动控制", "温度控制状态变化:" + tips.toString().trim());
} }
} else { } else {
if (mode != -1 && temp != -1 && RoomSetting.isAirConditionForceClose()) { if (mode != -1 && temp != -1 && RoomSetting.isAirConditionForceClose()) {
tips.append(",在消毒期间无法开启空调"); tips.append(",在消毒期间无法开启空调");
} }
MyLog.autoError("自动温控操作失败:" + tips); MyLog.autoError("自动温控操作失败:" + tips);
UserLog.systemError("自动控制", "温度控制操作失败:" + tips.toString().trim());
} }
} }
} }
@@ -47,7 +47,12 @@ public class VentilatorController extends Control {
protected boolean startMethod() { protected boolean startMethod() {
//每次开启时获取最新参数 //每次开启时获取最新参数
refreshRule(); refreshRule();
MyLog.auto("自动:首先关闭换气扇"); if (!RoomController.fan.isAvailable()) {
MyLog.autoError("换气自动控制无法启动:换气扇不可用");
return false;
}
MyLog.auto("换气自动控制初始化:先关闭换气扇,开启时长=" + targetOpen
+ "分钟,关闭时长=" + targetClose + "分钟");
stopFan(0); stopFan(0);
return true; return true;
} }
@@ -63,25 +68,35 @@ public class VentilatorController extends Control {
} }
private void startFan(int minutes) { private void startFan(int minutes) {
MyLog.auto("自动:" + minutes + "分钟后开启换气扇"); MyLog.auto("换气调度:计划" + minutes + "分钟后开启换气扇");
pollingTask.startDelayedTask(RxTag.VENTILATOR_AUTO_START_FUN, minutes * 60, () -> { pollingTask.startDelayedTask(RxTag.VENTILATOR_AUTO_START_FUN, minutes * 60, () -> {
try {
refreshRule(); refreshRule();
if (targetOpen == 0 && targetClose == 0) { if (targetOpen == 0 && targetClose == 0) {
//如果开始停止时间均为0,则3分钟后再次检测配置项 //如果开始停止时间均为0,则3分钟后再次检测配置项
MyLog.auto("开始停止时间均为03分钟后再次检测配置"); MyLog.auto("换气控制暂停:开启和关闭时长均为03分钟后重新读取配置");
startFan(3); startFan(3);
return; return;
} }
// 开启换气扇 // 开启换气扇
MyQueue.getInstance(MyQueue.TYPE_CONTROLLER).addTask(new QueueIOTask(() -> { MyQueue.getInstance(MyQueue.TYPE_CONTROLLER).addTask(new QueueIOTask(() -> {
String airExchangeMode = AutoModelSet.getAirExchangeMode(); String airExchangeMode = AutoModelSet.getAirExchangeMode();
RoomController.fan.setPowerSupply(true); boolean fanResult = MyUtil.autoControlOperateThird(RoomController.fan, true);
MyLog.auto("换气控制执行结果:设备=换气扇,目标=开启,结果="
+ (fanResult ? "成功" : "失败") + ",计划运行=" + targetOpen + "分钟");
if (RoomController.airExchange.isAvailable() && airExchangeMode.equals(SpinnerList.AIR_EXCHANGE_MODES[1])) { if (RoomController.airExchange.isAvailable() && airExchangeMode.equals(SpinnerList.AIR_EXCHANGE_MODES[1])) {
MyUtil.autoControlOperateThird(RoomController.airExchange, true); boolean airExchangeResult = MyUtil.autoControlOperateThird(RoomController.airExchange, true);
MyLog.auto("换气联动执行结果:设备=新风,目标=开启,结果="
+ (airExchangeResult ? "成功" : "失败"));
} }
})); }));
MyLog.auto("自动:" + targetOpen + "分钟后关闭换气扇"); MyLog.auto("换气调度:计划" + targetOpen + "分钟后关闭换气扇");
stopFan(targetOpen); stopFan(targetOpen);
} catch (Throwable throwable) {
MyLog.autoError("换气扇开启阶段异常:异常=" + throwable.getClass().getSimpleName()
+ ",原因=" + throwable.getMessage() + "1分钟后尝试恢复调度");
startFan(1);
}
}); });
} }
@@ -93,14 +108,25 @@ public class VentilatorController extends Control {
private void stopFan(int minutes) { private void stopFan(int minutes) {
// 开启换气扇,持续targetOpen分钟 // 开启换气扇,持续targetOpen分钟
pollingTask.startDelayedTask(RxTag.VENTILATOR_AUTO_STOP_FUN, minutes * 60, () -> { pollingTask.startDelayedTask(RxTag.VENTILATOR_AUTO_STOP_FUN, minutes * 60, () -> {
try {
refreshRule();
// 关闭换气扇 // 关闭换气扇
MyQueue.getInstance(MyQueue.TYPE_CONTROLLER).addTask(new QueueIOTask(() -> { MyQueue.getInstance(MyQueue.TYPE_CONTROLLER).addTask(new QueueIOTask(() -> {
if (RoomController.airExchange.isAvailable() && AutoModelSet.getAirExchangeMode().equals(SpinnerList.AIR_EXCHANGE_MODES[1])) { if (RoomController.airExchange.isAvailable() && AutoModelSet.getAirExchangeMode().equals(SpinnerList.AIR_EXCHANGE_MODES[1])) {
MyUtil.autoControlOperateThird(RoomController.airExchange, false); boolean airExchangeResult = MyUtil.autoControlOperateThird(RoomController.airExchange, false);
MyLog.auto("换气联动执行结果:设备=新风,目标=关闭,结果="
+ (airExchangeResult ? "成功" : "失败"));
} }
RoomController.fan.setPowerSupply(false); boolean fanResult = MyUtil.autoControlOperateThird(RoomController.fan, false);
MyLog.auto("换气控制执行结果:设备=换气扇,目标=关闭,结果="
+ (fanResult ? "成功" : "失败") + ",计划停运=" + targetClose + "分钟");
})); }));
startFan(targetClose); startFan(targetClose);
} catch (Throwable throwable) {
MyLog.autoError("换气扇关闭阶段异常:异常=" + throwable.getClass().getSimpleName()
+ ",原因=" + throwable.getMessage() + "1分钟后尝试恢复调度");
startFan(1);
}
}); });
} }
@@ -199,14 +199,14 @@ public class RoomSensor {
} }
/** /**
* 获取室内平均湿度 * 获取未叠加校准偏差的室内平均真实湿度,供自动控制和安全保护使用。
*/ */
public static double getBeforeCalibratedIndoorHumidity() { public static double getBeforeCalibratedIndoorHumidity() {
double sumOfHumidities = 0.0; double sumOfHumidities = 0.0;
int numberOfEnabledSensors = 0; int numberOfEnabledSensors = 0;
for (THSensor sensor : dynamicTHSensorList) { for (THSensor sensor : dynamicTHSensorList) {
if (sensor.humidity == 0 || sensor.humidity == 100) { if (sensor.humidity <= 0 || sensor.humidity > 100) {
continue; continue;
} }
sumOfHumidities += sensor.humidity; sumOfHumidities += sensor.humidity;
@@ -214,7 +214,7 @@ public class RoomSensor {
} }
for (GasSensor sensor : dynamicCO2SensorList) { for (GasSensor sensor : dynamicCO2SensorList) {
if (sensor.getHumidity() == 0 || sensor.getHumidity() == 100) { if (sensor.getHumidity() <= 0 || sensor.getHumidity() > 100) {
continue; continue;
} }
sumOfHumidities += sensor.getHumidity(); sumOfHumidities += sensor.getHumidity();
@@ -222,7 +222,7 @@ public class RoomSensor {
} }
for (GasSensor sensor : dynamicAmmoniaSensorList) { for (GasSensor sensor : dynamicAmmoniaSensorList) {
if (sensor.getHumidity() == 0 || sensor.getHumidity() == 100) { if (sensor.getHumidity() <= 0 || sensor.getHumidity() > 100) {
continue; continue;
} }
sumOfHumidities += sensor.getHumidity(); sumOfHumidities += sensor.getHumidity();
@@ -235,8 +235,11 @@ public class RoomSensor {
return sumOfHumidities / numberOfEnabledSensors; return sumOfHumidities / numberOfEnabledSensors;
} }
/**
* 获取叠加校准偏差后的室内平均湿度,供界面显示和上报使用。
*/
public static double getAverageIndoorHumidity() { public static double getAverageIndoorHumidity() {
double averageHumidity = getAverageIndoorHumidity(); double averageHumidity = getBeforeCalibratedIndoorHumidity();
if (averageHumidity == 0.0) { if (averageHumidity == 0.0) {
return 0.0; return 0.0;
@@ -66,8 +66,21 @@ public class MyLog extends Timber.Tree {
public static final String TAG_FRP = "远程协助"; public static final String TAG_FRP = "远程协助";
public static List<String> getUserQueryTag() { public static List<String> getUserQueryTag() {
return getSystemLogTags();
}
/**
* 系统诊断日志的全部落库标签,包含普通和错误级别。
*/
public static List<String> getSystemLogTags() {
List<String> tags = new ArrayList<>(); List<String> tags = new ArrayList<>();
Collections.addAll(tags, TAG_APP, TAG_REMOTE, TAG_CONTROLLER, TAG_WARNING,TAG_SENSOR, TAG_NETWORK, TAG_AUTO); List<String> baseTags = new ArrayList<>();
Collections.addAll(baseTags, TAG_TEST, TAG_WARNING, TAG_SENSOR, TAG_CONTROLLER, TAG_NETWORK,
TAG_APP, TAG_REMOTE, TAG_AUTO, TAG_FACE, TAG_FRP);
for (String tag : baseTags) {
tags.add(tag);
tags.add(tag + TAG_ERROR);
}
return tags; return tags;
} }
@@ -17,11 +17,19 @@ import timber.log.Timber;
* 日志用户查看用 * 日志用户查看用
*/ */
public class UserLog extends Timber.Tree { public class UserLog extends Timber.Tree {
public static final String FILTER_ALL = "全部上报日志"; public static final String FILTER_ALL = "全部日志";
public static final String FILTER_USER_ALL = "全部用户日志";
public static final String FILTER_DIAGNOSTIC = "系统诊断日志";
public static final String FILTER_DIAGNOSTIC_AUTO = "自动控制诊断";
public static final String FILTER_DIAGNOSTIC_CONTROLLER = "控制器诊断";
public static final String FILTER_DIAGNOSTIC_SENSOR = "传感器诊断";
public static final String FILTER_DIAGNOSTIC_NETWORK = "网络诊断";
public static final String FILTER_OPERATE = "操作日志"; public static final String FILTER_OPERATE = "操作日志";
public static final String FILTER_USER_SENSOR = "用户传感器日志";
public static final String FILTER_SENSOR = "传感器日志"; public static final String FILTER_SENSOR = "传感器日志";
public static final String FILTER_CONTROLLER = "控制器日志";
public static final String FILTER_TASK = "作业日志"; public static final String FILTER_TASK = "作业日志";
public static final String FILTER_SYSTEM = "系统日志"; public static final String FILTER_SYSTEM = "用户系统日志";
/** /**
* 用户-操作类 * 用户-操作类
@@ -47,17 +55,34 @@ public class UserLog extends Timber.Tree {
} }
public static List<String> getUserQueryFilters() { public static List<String> getUserQueryFilters() {
return Arrays.asList(FILTER_ALL, FILTER_OPERATE, FILTER_SENSOR, FILTER_TASK, FILTER_SYSTEM); return Arrays.asList(FILTER_ALL, FILTER_USER_ALL, FILTER_OPERATE, FILTER_USER_SENSOR,
FILTER_TASK, FILTER_SYSTEM, FILTER_DIAGNOSTIC, FILTER_DIAGNOSTIC_AUTO,
FILTER_CONTROLLER, FILTER_SENSOR, FILTER_DIAGNOSTIC_NETWORK);
} }
/** /**
* 将界面筛选项转换为实际存储标签,成功和错误级别都包含在同一分类中。 * 将界面筛选项转换为实际存储标签,成功和错误级别都包含在同一分类中。
*/ */
public static List<String> getStorageTagsForFilter(String filter) { public static List<String> getStorageTagsForFilter(String filter) {
if (FILTER_DIAGNOSTIC.equals(filter)) {
return MyLog.getSystemLogTags();
}
if (FILTER_DIAGNOSTIC_AUTO.equals(filter)) {
return tagsWithError(MyLog.TAG_AUTO);
}
if (FILTER_DIAGNOSTIC_CONTROLLER.equals(filter) || FILTER_CONTROLLER.equals(filter)) {
return tagsWithError(MyLog.TAG_CONTROLLER);
}
if (FILTER_DIAGNOSTIC_SENSOR.equals(filter) || FILTER_SENSOR.equals(filter)) {
return tagsWithError(MyLog.TAG_SENSOR);
}
if (FILTER_DIAGNOSTIC_NETWORK.equals(filter)) {
return tagsWithError(MyLog.TAG_NETWORK);
}
if (FILTER_OPERATE.equals(filter) || TAG_USER_OPERATE.equals(filter)) { if (FILTER_OPERATE.equals(filter) || TAG_USER_OPERATE.equals(filter)) {
return tagsWithError(TAG_USER_OPERATE); return tagsWithError(TAG_USER_OPERATE);
} }
if (FILTER_SENSOR.equals(filter) || TAG_USER_SENSOR.equals(filter)) { if (FILTER_USER_SENSOR.equals(filter) || TAG_USER_SENSOR.equals(filter)) {
return tagsWithError(TAG_USER_SENSOR); return tagsWithError(TAG_USER_SENSOR);
} }
if (FILTER_TASK.equals(filter) || TAG_USER_TASK.equals(filter)) { if (FILTER_TASK.equals(filter) || TAG_USER_TASK.equals(filter)) {
@@ -70,6 +95,9 @@ public class UserLog extends Timber.Tree {
for (String userTag : getUserLogTags()) { for (String userTag : getUserLogTags()) {
tags.addAll(tagsWithError(userTag)); tags.addAll(tagsWithError(userTag));
} }
if (FILTER_ALL.equals(filter) || filter == null) {
tags.addAll(MyLog.getSystemLogTags());
}
return tags; return tags;
} }
@@ -78,19 +106,7 @@ public class UserLog extends Timber.Tree {
} }
public static String getCategoryName(String tag) { public static String getCategoryName(String tag) {
if (tag == null) { return tag == null ? "未知" : tag;
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) { public static void operate(String msg) {
@@ -137,6 +153,10 @@ public class UserLog extends Timber.Tree {
Timber.tag(TAG_USER_SYSTEM + MyLog.TAG_ERROR).e(msg); Timber.tag(TAG_USER_SYSTEM + MyLog.TAG_ERROR).e(msg);
} }
public static void systemError(String source, String msg) {
systemError(formatSource(source, msg));
}
/** /**
* 崩溃场景使用非阻塞入口,防止数据库繁忙时阻断进程重启。 * 崩溃场景使用非阻塞入口,防止数据库繁忙时阻断进程重启。
*/ */
@@ -35,6 +35,8 @@ import org.json.JSONObject;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
/** /**
* MQTT 主题 * MQTT 主题
@@ -47,6 +49,8 @@ public class TopicClass {
private static String areaDataTemp = ""; private static String areaDataTemp = "";
private static String sensorDataTemp = ""; private static String sensorDataTemp = "";
private static String sensorInfoDataTemp = ""; private static String sensorInfoDataTemp = "";
private static final int MAX_PENDING_LOGS = 200;
private static final Queue<PendingLog> PENDING_LOGS = new ConcurrentLinkedQueue<>();
//————————————————————————————————————————————————————————————————————————————————获取区域集合———————————————————————————————————————————————————————————————— //————————————————————————————————————————————————————————————————————————————————获取区域集合————————————————————————————————————————————————————————————————
@@ -309,16 +313,56 @@ public class TopicClass {
} }
public static void uploadLog(int tag, int priority, String message, Date datetime) { public static void uploadLog(int tag, int priority, String message, Date datetime) {
PendingLog pendingLog = new PendingLog(tag, priority, message, datetime);
if (!MQTTService.getMQTTConnect()) {
while (PENDING_LOGS.size() >= MAX_PENDING_LOGS) {
PENDING_LOGS.poll();
}
PENDING_LOGS.offer(pendingLog);
MyLog.network("用户日志等待MQTT连接后上传:类型=" + tag + ",级别=" + priority
+ ",待上传=" + PENDING_LOGS.size() + "");
return;
}
publishLog(pendingLog);
}
private static void publishLog(PendingLog pendingLog) {
JSONObject json = new JSONObject(); JSONObject json = new JSONObject();
try { try {
json.put("areacode", RoomSetting.getAreaCode()); json.put("areacode", RoomSetting.getAreaCode());
json.put("tage", tag); json.put("tage", pendingLog.tag);
json.put("msglevel", priority); json.put("msglevel", pendingLog.priority);
json.put("msg", message); json.put("msg", pendingLog.message);
json.put("datetime", MyUtil.getDateTime(datetime)); json.put("datetime", MyUtil.getDateTime(pendingLog.datetime));
MQTTService.publish(DeviceRunLogsPost(), json.toString()); MQTTService.publish(DeviceRunLogsPost(), json.toString());
} catch (JSONException e) { } catch (JSONException e) {
e.printStackTrace(); MyLog.networkError("用户日志序列化失败:类型=" + pendingLog.tag + ",原因=" + e.getMessage());
}
}
public static void flushPendingLogs() {
int count = 0;
PendingLog pendingLog;
while (MQTTService.getMQTTConnect() && (pendingLog = PENDING_LOGS.poll()) != null) {
publishLog(pendingLog);
count++;
}
if (count > 0) {
MyLog.network("MQTT连接恢复,已补传本次进程缓存的用户日志" + count + "");
}
}
private static final class PendingLog {
private final int tag;
private final int priority;
private final String message;
private final Date datetime;
private PendingLog(int tag, int priority, String message, Date datetime) {
this.tag = tag;
this.priority = priority;
this.message = message;
this.datetime = datetime;
} }
} }
@@ -564,6 +608,18 @@ public class TopicClass {
//唯一区域码 之前通过唯一区域码获得索引,现在就一个房间,所以不需要 //唯一区域码 之前通过唯一区域码获得索引,现在就一个房间,所以不需要
String code = jsons.getString("code"); String code = jsons.getString("code");
if (RoomSetting.getAreaCode().equals(code)) { if (RoomSetting.getAreaCode().equals(code)) {
String oldProgram = AIModelSet.getAiSchemeName();
double oldTempMax = AutoModelSet.getMaxTemperature();
double oldTempMin = AutoModelSet.getMinTemperature();
double oldHumidityMax = AutoModelSet.getMaxHumidity();
double oldHumidityMin = AutoModelSet.getMinHumidity();
boolean oldAutoTemperature = AutoModelSet.isAutoTemperature();
double oldTargetTemperature = AutoModelSet.getTargetTemperature();
boolean oldAutoHumidity = AutoModelSet.isAutoHumidity();
double oldTargetHumidity = AutoModelSet.getTargetHumidity();
boolean oldAutoVentilator = AutoModelSet.isAutoVentilator();
int oldVentilatorOpen = AutoModelSet.getCycleStart();
int oldVentilatorClose = AutoModelSet.getCycleStop();
//预警值 //预警值
MMKVUtil.put(AIModelSet.AI_SCHEME_NAME, jsons.getString("autocontrolprogram")); MMKVUtil.put(AIModelSet.AI_SCHEME_NAME, jsons.getString("autocontrolprogram"));
MMKVUtil.put(AutoModelSet.MAX_TEMPERATURE, jsons.getDouble("tempmaxwarning")); MMKVUtil.put(AutoModelSet.MAX_TEMPERATURE, jsons.getDouble("tempmaxwarning"));
@@ -604,7 +660,20 @@ public class TopicClass {
//2025年5月7日:取消以下逻辑 //2025年5月7日:取消以下逻辑
// //切换为自动模式 // //切换为自动模式
// RxBusUtils.get().post(RxTag.UPDATE_MAIN, 3); // RxBusUtils.get().post(RxTag.UPDATE_MAIN, 3);
UserLog.operate("远程控制", "自动模式参数更新成功"); String changeSummary = "自动模式参数更新:方案=" + oldProgram + "" + AIModelSet.getAiSchemeName()
+ ";温控启用=" + oldAutoTemperature + "" + AutoModelSet.isAutoTemperature()
+ ",目标=" + oldTargetTemperature + "" + AutoModelSet.getTargetTemperature() + ""
+ ",区间=" + oldTempMin + "~" + oldTempMax + ""
+ AutoModelSet.getMinTemperature() + "~" + AutoModelSet.getMaxTemperature() + ""
+ ";湿控启用=" + oldAutoHumidity + "" + AutoModelSet.isAutoHumidity()
+ ",目标=" + oldTargetHumidity + "" + AutoModelSet.getTargetHumidity() + "%"
+ ",区间=" + oldHumidityMin + "~" + oldHumidityMax + ""
+ AutoModelSet.getMinHumidity() + "~" + AutoModelSet.getMaxHumidity() + "%"
+ ";换气启用=" + oldAutoVentilator + "" + AutoModelSet.isAutoVentilator()
+ ",开启=" + oldVentilatorOpen + "" + AutoModelSet.getCycleStart() + "分钟"
+ ",关闭=" + oldVentilatorClose + "" + AutoModelSet.getCycleStop() + "分钟";
UserLog.operate("远程控制", changeSummary);
MyLog.remote(changeSummary + ",区域码=" + code);
if (RoomSetting.getMode() == 1) { if (RoomSetting.getMode() == 1) {
// 如果是自动模式,则刷新自动模式参数 // 如果是自动模式,则刷新自动模式参数
RxBusUtils.get().post(RxTag.UPDATE_AUTO, 3); RxBusUtils.get().post(RxTag.UPDATE_AUTO, 3);