Commit 8208491d by pye52

Merge branch 'new_master' into new_for_phone

parents 20484d93 ed1d63e3
......@@ -129,26 +129,26 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
SCWebSocketClient.getInstance().getConnectStateEvent().observe(this, event -> {
if (debugLayoutIsNotInflate()) return;
String networkType = NetworkUtils.getNetworkType(this);
String msg;
LogUtils.d(TAG, "connect state: " + event.getState());
switch (event.getState()) {
case ConnectState.OFFLINE:
msg = String.format(getString(R.string.connect_offline), networkType);
msg = String.format(getString(R.string.connect_offline), NetworkUtils.getTryingNetworkType(this));
break;
case ConnectState.CONNECTING:
msg = String.format(getString(R.string.connect_connecting), networkType);
msg = String.format(getString(R.string.connect_connecting), NetworkUtils.getNetworkType(this));
break;
case ConnectState.CONNECTED:
msg = String.format(getString(R.string.connect_connected), event.getHost(), networkType);
msg = String.format(getString(R.string.connect_connected), event.getHost(), NetworkUtils.getNetworkType(this));
// socket链接成功后刷新wifi及eth的ip地址信息
refreshEthInfo();
refreshWifiInfo();
break;
case ConnectState.RECONNECTING:
msg = String.format(getString(R.string.connect_reconnecting), event.getHost(), networkType);
msg = String.format(getString(R.string.connect_reconnecting), event.getHost());
break;
case ConnectState.CHANGE_NETWORK:
msg = String.format(getString(R.string.connect_change_network), event.getHost(), networkType);
msg = String.format(getString(R.string.connect_change_network), NetworkUtils.getTryingNetworkType(this));
break;
default:
msg = "";
......
......@@ -40,6 +40,7 @@ public class LogCommandHandler extends CommandHandler {
private static final String UPLOAD_DIR = File.separator + "upload_log";
private static final long DEFAULT_DELAY = 1000;
private volatile boolean start = false;
private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
private String deviceSN;
private CommandLog commandLog;
......@@ -48,7 +49,29 @@ public class LogCommandHandler extends CommandHandler {
private Date startTime;
private Date endTime;
public LogCommandHandler(Command command, Gson gson, String deviceSN, CommandProgressCallback callback) {
private static LogCommandHandler instance;
public static synchronized LogCommandHandler getInstance(Command command, Gson gson, String deviceSN, CommandProgressCallback callback) {
if (instance == null) {
instance = new LogCommandHandler(command, gson, deviceSN, callback);
} else {
if (!instance.start) {
instance.init(command, gson, deviceSN, callback);
}
}
return instance;
}
private void init(Command command, Gson gson, String deviceSN, CommandProgressCallback callback) {
this.command = command;
this.gson = gson;
this.commandProgressCallback = callback;
this.deviceSN = deviceSN;
this.commandLog = gson.fromJson(command.getData(), CommandLog.class);
parseDate();
}
private LogCommandHandler(Command command, Gson gson, String deviceSN, CommandProgressCallback callback) {
super(command, gson, callback);
this.deviceSN = deviceSN;
this.commandLog = gson.fromJson(command.getData(), CommandLog.class);
......@@ -69,6 +92,7 @@ public class LogCommandHandler extends CommandHandler {
} catch (ParseException e) {
LogUtils.w(TAG, "日志上传,结束时间解析失败: " + data.toString(), e);
}
LogUtils.d(TAG, "筛选从" + startTime.toString() + "到" + endTime.toString() + "的日志文件");
}
// 检查传入的指定日期是否合法
......@@ -114,6 +138,7 @@ public class LogCommandHandler extends CommandHandler {
} else {
logDir = new File(LogUtils.getConfig().getDir());
}
LogUtils.d(TAG, "待上传日志文件类型: " + logType);
return logDir;
}
......@@ -124,6 +149,7 @@ public class LogCommandHandler extends CommandHandler {
FileUtils.delete(dir);
}
boolean result = dir.mkdirs();
LogUtils.d(TAG, "临时目录创建" + (result ? "成功" : "失败"));
if (result) {
return dir;
} else {
......@@ -139,11 +165,16 @@ public class LogCommandHandler extends CommandHandler {
List<File> logFiles = FileUtils.listFilesInDirWithFilter(src, filter, false, null);
boolean copyResult = true;
wait("筛选目标日志文件", 10);
int count = 0;
File descFile;
boolean tempCopyResult;
for (File file : logFiles) {
descFile = new File(descDir, file.getName());
copyResult = copyResult && FileUtils.copy(file, descFile);
tempCopyResult = FileUtils.copy(file, descFile);
copyResult = copyResult && tempCopyResult;
if (tempCopyResult) count++;
}
LogUtils.d(TAG, "待上传日志文件总数: " + logFiles.size() + ", 复制成功文件总数: " + count);
return !logFiles.isEmpty() && copyResult;
}
......@@ -156,6 +187,7 @@ public class LogCommandHandler extends CommandHandler {
"_" + format.format(endTime) +
"_" + deviceSN + ".zip";
fileNameForServer = fileNameForServer.replace(" ", "_");
LogUtils.d(TAG, "开始上传日志: " + fileNameForServer);
RequestBody requestBody = RequestBody.create(zip, MediaType.parse("application/x-zip-compressed"));
MultipartBody.Part body = MultipartBody.Part.createFormData("file", fileNameForServer, requestBody);
try {
......@@ -170,16 +202,24 @@ public class LogCommandHandler extends CommandHandler {
}
@Override
public CommandResponse run() throws InterruptedException {
public synchronized CommandResponse run() throws InterruptedException {
if (start) {
LogUtils.w(TAG, "日志上传任务已启动");
return null;
}
start = true;
if (!checkLogCommand()) {
start = false;
return failedResult("日志上传指令不符合规范");
}
File logFile = getZipLogs();
if (logFile != null) {
wait("上传压缩文件至服务器", 60);
upload(logFile);
start = false;
return successResult("");
}
start = false;
return failedResult("");
}
}
package com.bgycc.smartcanteen.command;
import android.os.Build;
import com.bgycc.smartcanteen.BuildConfig;
import com.bgycc.smartcanteen.api.SCRetrofit;
import com.bgycc.smartcanteen.entity.Command;
import com.bgycc.smartcanteen.entity.CommandResponse;
import com.bgycc.smartcanteen.entity.CommandUpdate;
import com.bgycc.smartcanteen.entity.ProgressResponseBody;
import com.bgycc.smartcanteen.utils.DangerousUtils;
import com.bgycc.smartcanteen.utils.DeviceProxy;
import com.blankj.utilcode.util.AppUtils;
import com.blankj.utilcode.util.FileIOUtils;
import com.blankj.utilcode.util.FileUtils;
......@@ -55,18 +53,14 @@ public class UpdateCommandHandler extends CommandHandler {
private static UpdateCommandHandler instance;
public static UpdateCommandHandler getInstance(Command command, Gson gson, CommandProgressCallback callback) {
public static synchronized UpdateCommandHandler getInstance(Command command, Gson gson, CommandProgressCallback callback) {
if (instance == null) {
synchronized (UpdateCommandHandler.class) {
if (instance == null) {
instance = new UpdateCommandHandler(command, gson, callback);
}
instance = new UpdateCommandHandler(command, gson, callback);
} else {
if (!instance.start) {
instance.init(command, gson, callback);
}
}
// 当callback发生变化时,重新设置
if (!callback.equals(instance.commandProgressCallback)) {
instance.commandProgressCallback = callback;
}
return instance;
}
......@@ -85,7 +79,14 @@ public class UpdateCommandHandler extends CommandHandler {
.build();
this.commandUpdate = gson.fromJson(command.getData(), CommandUpdate.class);
this.updateApk = new File(Utils.getApp().getCacheDir(), UPDATE_APK);
FileUtils.delete(updateApk);
}
private void init(Command command, Gson gson, CommandProgressCallback callback) {
this.command = command;
this.gson = gson;
this.commandProgressCallback = callback;
this.commandUpdate = gson.fromJson(command.getData(), CommandUpdate.class);
this.updateApk = new File(Utils.getApp().getCacheDir(), UPDATE_APK);
}
@Override
......@@ -95,6 +96,7 @@ public class UpdateCommandHandler extends CommandHandler {
return null;
}
start = true;
FileUtils.delete(updateApk);
if (executor == null) {
executor = Executors.newScheduledThreadPool(1);
}
......@@ -149,40 +151,27 @@ public class UpdateCommandHandler extends CommandHandler {
start = false;
return;
}
FileIOUtils.writeFileFromIS(updateApk, body.byteStream());
LogUtils.d(TAG, "更新包下载成功,开始安装");
boolean result = FileIOUtils.writeFileFromIS(updateApk, body.byteStream());
AppUtils.AppInfo info = AppUtils.getApkInfo(updateApk);
LogUtils.d(TAG, "更新包下载: " + (result ? "成功" : "失败") + ",开始安装: " + (info == null ? "null" : info.getPackageName()));
if (info == null || !info.getPackageName().equals(BuildConfig.APPLICATION_ID)) {
FileUtils.delete(updateApk);
LogUtils.w(TAG, "更新包包名非法");
idle("", 0);
start = false;
return;
}
if (info.getVersionCode() == BuildConfig.VERSION_CODE) {
FileUtils.delete(updateApk);
LogUtils.w(TAG, "更新包已安装");
LogUtils.w(TAG, "当前版本: " + BuildConfig.VERSION_CODE + ", 安装包版本: " + info.getVersionCode());
} else if (info.getVersionCode() < BuildConfig.VERSION_CODE) {
FileUtils.delete(updateApk);
LogUtils.d(TAG, "不允许安装低版本");
failed("不允许安装低版本", 0);
} else {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
// 6.0以下安装包需要修改权限才能安装
try {
Process p = Runtime.getRuntime().exec("chmod 755 " + updateApk);
p.waitFor();
DangerousUtils.installAppSilent(updateApk);
LogUtils.d(TAG, "开始安装");
success("开始安装", 100);
} catch (Exception e) {
LogUtils.e(TAG, "安装文件权限修改失败");
failed("安装文件权限修改失败", 0);
}
} else {
DangerousUtils.installAppSilent(updateApk);
LogUtils.d(TAG, "开始安装");
if (DeviceProxy.updateApp(updateApk)) {
success("开始安装", 100);
} else {
failed("安装文件权限修改失败", 0);
}
}
try {
......
......@@ -6,29 +6,64 @@ import com.bgycc.smartcanteen.entity.Command;
import com.bgycc.smartcanteen.entity.CommandResponse;
import com.bgycc.smartcanteen.entity.CommandWifiConfig;
import com.bgycc.smartcanteen.utils.NetworkUtils;
import com.blankj.utilcode.util.LogUtils;
import com.google.gson.Gson;
import static com.bgycc.smartcanteen.utils.SmartCanteenUtils.TAG;
public class WifiConfigCommandHandler extends CommandHandler {
private static final long DEFAULT_DELAY = 3000;
private static final long POLLING_DELAY = 100;
private CommandWifiConfig wifiConfig;
public WifiConfigCommandHandler(Command command, Gson gson, CommandProgressCallback callback) {
private volatile boolean start = false;
private static WifiConfigCommandHandler instance;
public static synchronized WifiConfigCommandHandler getInstance(Command command, Gson gson, CommandProgressCallback callback) {
if (instance == null) {
instance = new WifiConfigCommandHandler(command, gson, callback);
} else {
if (!instance.start) {
instance.init(command, gson, callback);
}
}
return instance;
}
private WifiConfigCommandHandler(Command command, Gson gson, CommandProgressCallback callback) {
super(command, gson, callback);
this.wifiConfig = gson.fromJson(command.getData(), CommandWifiConfig.class);
}
private void init(Command command, Gson gson, CommandProgressCallback callback) {
this.command = command;
this.gson = gson;
this.commandProgressCallback = callback;
this.wifiConfig = gson.fromJson(command.getData(), CommandWifiConfig.class);
}
@Override
public CommandResponse run() throws InterruptedException {
public synchronized CommandResponse run() throws InterruptedException {
if (start) {
LogUtils.w(TAG, "Wifi配置任务已启动");
return null;
}
start = true;
CommandWifiConfig.CommandWifiConfigData data = wifiConfig.getData();
if (data == null) return null;
if (data == null) {
start = false;
return null;
}
if (!NetworkUtils.isWifiEnabled()) {
wait("正在启动Wifi", 5);
if (!NetworkUtils.setEnable(true)) {
String failedMessage = "无法启动Wifi";
wait(failedMessage, 5);
LogUtils.e(TAG, failedMessage);
Thread.sleep(DEFAULT_DELAY);
start = false;
return failedResult(failedMessage);
}
}
......@@ -39,6 +74,7 @@ public class WifiConfigCommandHandler extends CommandHandler {
String type = data.getType();
wait("正在配置Wifi", 10);
LogUtils.d(TAG, "开始配置wifi, ssid: " + ssid + ", identity: " + identity + ", pwd: " + pwd + ", type: " + type);
try {
NetworkUtils.connect(ssid, identity, pwd, type);
// 轮训检查wifi是否链接成功
......@@ -50,12 +86,16 @@ public class WifiConfigCommandHandler extends CommandHandler {
}
String message = "Wifi配置成功";
wait(message, 50);
LogUtils.d(TAG, message);
Thread.sleep(DEFAULT_DELAY);
return successResult(message);
}
} catch (Exception e) {
LogUtils.e(TAG, "链接wifi过程出错: " + e.getMessage(), e);
Thread.sleep(DEFAULT_DELAY);
return failedResult(e.getMessage());
} finally {
start = false;
}
return failedResult("无法连接Wifi");
}
......
......@@ -92,7 +92,7 @@ public class Command {
"id=" + id +
", data='" + data + '\'' +
", action='" + action + '\'' +
", response=" + finish +
", finish=" + finish +
'}';
}
}
......@@ -2,17 +2,16 @@ package com.bgycc.smartcanteen.qrcode;
import android.os.Build;
public class QRCodeScanFactory {
private static final String TPS = "TPS";
private static final String QUAD = "QUAD";
import com.bgycc.smartcanteen.utils.DeviceProxy;
public class QRCodeScanFactory {
private static final String TPS_PORT = "/dev/ttyS0";
private static final String QUAD_PORT = "/dev/ttyS6";
public static IQRCodeScan create() throws Exception {
String model = Build.MODEL;
if (model.contains(TPS)) {
if (model.contains(DeviceProxy.DEVICE_MODEL_TPS)) {
return new TPS(TPS_PORT);
} else if (model.contains(QUAD)) {
} else if (model.contains(DeviceProxy.DEVICE_MODEL_QUAD)) {
return new QUAD(QUAD_PORT);
} else {
throw new RuntimeException("不明设备型号: " + model);
......
......@@ -206,11 +206,6 @@ public class SCWebSocketClient extends WebSocketClient {
for (SCWebSocketListener l : listener) {
l.onError(e);
}
if (forceStop) return;
switchNetwork();
stopHeartbeat();
tryReconnect();
}
@SuppressLint("ObsoleteSdkInt")
......@@ -221,7 +216,8 @@ public class SCWebSocketClient extends WebSocketClient {
}
lastSwitchTime = currentTime;
connectState.postValue(new ConnectState(ConnectState.CHANGE_NETWORK, host.toString()));
LogUtils.d(TAG, "switch network");
connectState.postValue(new ConnectState(ConnectState.CHANGE_NETWORK));
NetworkUtils.switchNetwork(callback);
}
......@@ -245,6 +241,11 @@ public class SCWebSocketClient extends WebSocketClient {
private Runnable reconnectRunnable = () -> {
LogUtils.d(TAG, "开始尝试重连");
connectState.postValue(new ConnectState(ConnectState.RECONNECTING, host.toString()));
// 增加一个延时,让UI界面有足够的时间去显示debug信息
try {
Thread.sleep(RECONNECT_DELAY);
} catch (InterruptedException ignored) {
}
for (SCWebSocketListener l : listener) {
l.onReconnect();
}
......
package com.bgycc.smartcanteen.utils;
import com.bgycc.smartcanteen.qrcode.IQRCodeScan;
import com.bgycc.smartcanteen.qrcode.QRCodeScanFactory;
import java.io.File;
/**
* 多设备相关方案的代理类 <br/>
* 设计该代理类目的是为了增加设备时,务必在下面所有方法的实际执行里进行适配
*/
public class DeviceProxy {
public static final String DEVICE_MODEL_TPS = "TPS";
public static final String DEVICE_MODEL_QUAD = "QUAD";
public static IQRCodeScan createQRCodeScan() throws Exception {
return QRCodeScanFactory.create();
}
public static boolean updateApp(File updateApk) {
return InstallManager.install(updateApk);
}
}
package com.bgycc.smartcanteen.utils;
import android.os.Build;
import com.blankj.utilcode.util.AppUtils;
import com.blankj.utilcode.util.LogUtils;
import java.io.File;
import static com.bgycc.smartcanteen.utils.SmartCanteenUtils.TAG;
public class InstallManager {
public static boolean install(File updateApk) {
String model = Build.MODEL;
if (model.contains(DeviceProxy.DEVICE_MODEL_TPS)) {
// 6.0以下安装包需要修改权限才能安装
try {
Process p = Runtime.getRuntime().exec("chmod 755 " + updateApk);
p.waitFor();
DangerousUtils.installAppSilent(updateApk);
LogUtils.d(TAG, "开始安装");
return true;
} catch (Exception e) {
LogUtils.e(TAG, "安装文件权限修改失败");
return false;
}
} else if (model.contains(DeviceProxy.DEVICE_MODEL_QUAD)) {
AppUtils.installApp(updateApk);
return true;
} else {
throw new RuntimeException("不明设备型号: " + model);
}
}
}
......@@ -151,6 +151,17 @@ public class NetworkUtils {
}
}
/**
* 获取当前正在尝试切换的网络形态
*/
public static String getTryingNetworkType(Context context) {
if (lastTryNetwork == NETWORK_ETHERNET) {
return context.getString(R.string.network_type_ethernet);
} else {
return context.getString(R.string.network_type_wifi);
}
}
public static String getNetworkType(Context context) {
if (Build.VERSION.SDK_INT >= 23) {
Network network = connectivityManager.getActiveNetwork();
......
......@@ -114,8 +114,6 @@ public class CommandViewModel extends ViewModel implements CommandProgressCallba
};
private class RequestRunnable implements Runnable {
// 每条指令执行间隔
private static final long EXEC_INTERVAL = 1500;
private Command command;
RequestRunnable(Command command) {
......@@ -125,16 +123,17 @@ public class CommandViewModel extends ViewModel implements CommandProgressCallba
@Override
public void run() {
CommandHandler handler = null;
LogUtils.d(TAG, "开始执行指令: " + command.toString());
try {
switch (command.getAction()) {
case Command.LOG_UPLOAD:
handler = new LogCommandHandler(command, gson, deviceSN,CommandViewModel.this);
handler = LogCommandHandler.getInstance(command, gson, deviceSN,CommandViewModel.this);
break;
case Command.APP_UPDATE:
handler = UpdateCommandHandler.getInstance(command, gson, CommandViewModel.this);
break;
case Command.CONFIG_WIFI:
handler = new WifiConfigCommandHandler(command, gson, CommandViewModel.this);
handler = WifiConfigCommandHandler.getInstance(command, gson, CommandViewModel.this);
break;
case Command.CONFIG_LOG:
// CONFIG_LOG后直接return
......@@ -155,6 +154,7 @@ public class CommandViewModel extends ViewModel implements CommandProgressCallba
commandState.postValue(new CommandState(CommandState.WAIT));
CommandResponse response = handler.run();
if (response == null) {
LogUtils.d(TAG, "指令无返回结果: " + command.toString());
return;
}
if (response.success()) {
......@@ -171,14 +171,9 @@ public class CommandViewModel extends ViewModel implements CommandProgressCallba
// 将执行完毕的指令更新到数据库,此时会触发dataObserver的动作(会继续搜索下一条未执行完毕的指令)
private void commandFinishAndUpdateDB() {
try {
Thread.sleep(EXEC_INTERVAL);
} catch (InterruptedException e) {
LogUtils.w(TAG, e.getMessage(), e);
} finally {
command.finish();
commandRepository.updateCommand(command);
}
command.finish();
LogUtils.d(TAG, "指令执行完毕: " + command.toString());
commandRepository.updateCommand(command);
}
}
......
......@@ -9,11 +9,11 @@ import androidx.lifecycle.ViewModel;
import com.bgycc.smartcanteen.entity.Command;
import com.bgycc.smartcanteen.entity.PayData;
import com.bgycc.smartcanteen.qrcode.IQRCodeScan;
import com.bgycc.smartcanteen.qrcode.QRCodeScanFactory;
import com.bgycc.smartcanteen.state.QRCodeState;
import com.bgycc.smartcanteen.executor.SCTaskExecutor;
import com.bgycc.smartcanteen.repository.CommandRepository;
import com.bgycc.smartcanteen.repository.PayDataRepository;
import com.bgycc.smartcanteen.utils.DeviceProxy;
import com.bgycc.smartcanteen.utils.SmartCanteenUtils;
import com.blankj.utilcode.util.LogUtils;
import com.google.gson.JsonObject;
......@@ -58,7 +58,7 @@ public class QRCodeViewModel extends ViewModel {
public void initialize() {
try {
scan = QRCodeScanFactory.create();
scan = DeviceProxy.createQRCodeScan();
scan();
} catch (Exception e) {
LogUtils.e(TAG, "串口初始化失败: " + e.getMessage(), e);
......
......@@ -25,6 +25,6 @@
<string name="connect_offline">%s 未连接</string>
<string name="connect_connecting">%s 正在连接…</string>
<string name="connect_connected">服务器: %s,网络类型: %s</string>
<string name="connect_reconnecting">服务器: %s,正在重连… %s</string>
<string name="connect_reconnecting">服务器: %s,正在重连…</string>
<string name="connect_change_network">%s 正在切换…</string>
</resources>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment