diff --git a/app/src/main/java/com/iflytop/profilometer/MainActivity.java b/app/src/main/java/com/iflytop/profilometer/MainActivity.java index fc61e5b..d2a05af 100644 --- a/app/src/main/java/com/iflytop/profilometer/MainActivity.java +++ b/app/src/main/java/com/iflytop/profilometer/MainActivity.java @@ -12,10 +12,14 @@ import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; +import com.iflytop.profilometer.api.ws.BleWebsocketManager; +import com.iflytop.profilometer.api.ws.DeviceStateWebsocketManager; import com.iflytop.profilometer.core.bluetooth.BleManager; import com.iflytop.profilometer.server.HttpServer; public class MainActivity extends AppCompatActivity { + private BleManager bleManager; + @SuppressLint({"SetJavaScriptEnabled", "MissingInflatedId"}) @Override protected void onCreate(Bundle savedInstanceState) { @@ -41,7 +45,7 @@ public class MainActivity extends AppCompatActivity { webView.getSettings().setUseWideViewPort(true); webView.loadUrl("http://127.0.0.1:8080/"); - BleManager bleManager = BleManager.getInstance(this); + bleManager = BleManager.getInstance(this); // 针对 Android 12 及以上版本,请求必要的运行时权限 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { bleManager.checkAndRequestPermissions(this); @@ -56,6 +60,6 @@ public class MainActivity extends AppCompatActivity { super.onDestroy(); // Activity 销毁时停止服务器 HttpServer.stop(); - BleManager.getInstance(this).stopScan(); + bleManager.stopScan(); } } \ No newline at end of file diff --git a/app/src/main/java/com/iflytop/profilometer/api/ble/BleApi.java b/app/src/main/java/com/iflytop/profilometer/api/ble/BleApi.java index 6c65373..09964de 100644 --- a/app/src/main/java/com/iflytop/profilometer/api/ble/BleApi.java +++ b/app/src/main/java/com/iflytop/profilometer/api/ble/BleApi.java @@ -3,10 +3,12 @@ package com.iflytop.profilometer.api.ble; import android.content.Context; import android.util.Log; +import com.iflytop.profilometer.api.ws.BleWebsocketManager; +import com.iflytop.profilometer.api.ws.DeviceStateWebsocketManager; import com.iflytop.profilometer.common.result.Result; +import com.iflytop.profilometer.core.bluetooth.BleDeviceListener; import com.iflytop.profilometer.core.bluetooth.BleManager; -import com.iflytop.profilometer.core.migration.driver.BleDeviceUartChannel; -import com.iflytop.profilometer.core.migration.type.protocol.TPMIBasicReport; +import com.iflytop.profilometer.core.migration.channel.BleDeviceUartChannel; /** * 蓝牙接口 @@ -24,8 +26,8 @@ public class BleApi { */ public String start() { try { - BleManager.getInstance(context).startScan(); - BleWebsocketManager.getInstance(context).startWsPush(); + BleManager.getInstance().startScan(); + BleWebsocketManager.getInstance().startWsPush(); return Result.success(); } catch (Exception e) { Log.e(TAG, "开始获取蓝牙列表失败", e); @@ -38,8 +40,8 @@ public class BleApi { */ public String stop() { try { - BleManager.getInstance(context).stopScan(); - BleWebsocketManager.getInstance(context).stopWsPush(); + BleManager.getInstance().stopScan(); + BleWebsocketManager.getInstance().stopWsPush(); return Result.success(); } catch (Exception e) { Log.e(TAG, "结束获取蓝牙设备列表失败", e); @@ -52,19 +54,8 @@ public class BleApi { */ public String connect(String mac) { try { - BleManager.getInstance(context).connectToDevice(mac); - - BleDeviceUartChannel uartChannel = new BleDeviceUartChannel(); - uartChannel.init(); - uartChannel.setOnReportListener(packet -> { - if (packet instanceof TPMIBasicReport basic) { - Log.d("BLE", "温度: " + basic.getTemperature()); - Log.d("BLE", "倾角X: " + basic.getInclinatorX()); - Log.d("BLE", "倾角Y: " + basic.getInclinatorY()); - Log.d("BLE", "电量: " + basic.getPower()); - } - }); - + BleManager.getInstance().connectToDevice(mac); + DeviceStateWebsocketManager.getInstance().startWsPush(); return Result.success(); } catch (Exception e) { Log.e(TAG, "链接蓝牙设备失败", e); @@ -77,7 +68,7 @@ public class BleApi { */ public String disconnect() { try { - BleManager.getInstance(context).disconnect(); + BleManager.getInstance().disconnect(); return Result.success(); } catch (Exception e) { Log.e(TAG, "断开蓝牙设备链接失败", e); diff --git a/app/src/main/java/com/iflytop/profilometer/api/ble/BleWebsocketManager.java b/app/src/main/java/com/iflytop/profilometer/api/ws/BleWebsocketManager.java similarity index 80% rename from app/src/main/java/com/iflytop/profilometer/api/ble/BleWebsocketManager.java rename to app/src/main/java/com/iflytop/profilometer/api/ws/BleWebsocketManager.java index efc0c74..2bd24e2 100644 --- a/app/src/main/java/com/iflytop/profilometer/api/ble/BleWebsocketManager.java +++ b/app/src/main/java/com/iflytop/profilometer/api/ws/BleWebsocketManager.java @@ -1,4 +1,4 @@ -package com.iflytop.profilometer.api.ble; +package com.iflytop.profilometer.api.ws; import android.annotation.SuppressLint; import android.bluetooth.BluetoothDevice; @@ -21,19 +21,18 @@ public class BleWebsocketManager { private static BleWebsocketManager instance; private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); private ScheduledFuture scheduledTask; - private final Context context; - private BleWebsocketManager(Context context) { - this.context = context.getApplicationContext(); + private BleWebsocketManager() { } - public static synchronized BleWebsocketManager getInstance(Context context) { + public static synchronized BleWebsocketManager getInstance() { if (instance == null) { - instance = new BleWebsocketManager(context); + instance = new BleWebsocketManager(); } return instance; } + /** * 开始定时任务 */ @@ -41,14 +40,14 @@ public class BleWebsocketManager { public void startWsPush() { stopWsPush(); scheduledTask = scheduler.scheduleWithFixedDelay(() -> { - List scannedDevices = BleManager.getInstance(context).getScannedDevices(); + List scannedDevices = BleManager.getInstance().getScannedDevices(); scannedDevices.sort(Comparator.comparing(BluetoothDevice::getAddress)); List bleList = new ArrayList<>(); for (BluetoothDevice bluetoothDevice : scannedDevices) { bleList.add(bluetoothDevice.getName() + "_" + bluetoothDevice.getAddress()); } WebSocketManager.send(JSONUtil.toJsonStr(bleList)); - }, 2, 120, TimeUnit.SECONDS); + }, 0, 2000, TimeUnit.MILLISECONDS); } /** diff --git a/app/src/main/java/com/iflytop/profilometer/api/ws/DeviceStateWebsocketManager.java b/app/src/main/java/com/iflytop/profilometer/api/ws/DeviceStateWebsocketManager.java new file mode 100644 index 0000000..8d13322 --- /dev/null +++ b/app/src/main/java/com/iflytop/profilometer/api/ws/DeviceStateWebsocketManager.java @@ -0,0 +1,58 @@ +package com.iflytop.profilometer.api.ws; + +import android.annotation.SuppressLint; +import android.content.Context; + +import com.iflytop.profilometer.core.system.DeviceState; +import com.iflytop.profilometer.core.system.SystemState; +import com.iflytop.profilometer.core.websocket.WebSocketManager; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import cn.hutool.json.JSONUtil; + +public class DeviceStateWebsocketManager { + private static DeviceStateWebsocketManager instance; + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); + private ScheduledFuture scheduledTask; + + private DeviceStateWebsocketManager() { + } + + public static synchronized DeviceStateWebsocketManager getInstance() { + if (instance == null) { + instance = new DeviceStateWebsocketManager(); + } + return instance; + } + + /** + * 开始定时任务 + */ + @SuppressLint("MissingPermission") + public void startWsPush() { + stopWsPush(); + scheduledTask = scheduler.scheduleWithFixedDelay(() -> { + DeviceState deviceState = SystemState.getInstance().getDeviceState(); + Map map = new HashMap<>(); + map.put("type", "peripheral-status"); + map.put("data", deviceState); + WebSocketManager.send(JSONUtil.toJsonStr(map)); + }, 0, 1000, TimeUnit.MILLISECONDS); + } + + /** + * 结束定时任务 + */ + public void stopWsPush() { + if (scheduledTask != null && !scheduledTask.isCancelled()) { + scheduledTask.cancel(false); + } + } + +} diff --git a/app/src/main/java/com/iflytop/profilometer/core/bluetooth/BleDeviceListener.java b/app/src/main/java/com/iflytop/profilometer/core/bluetooth/BleDeviceListener.java new file mode 100644 index 0000000..ad06a19 --- /dev/null +++ b/app/src/main/java/com/iflytop/profilometer/core/bluetooth/BleDeviceListener.java @@ -0,0 +1,37 @@ +package com.iflytop.profilometer.core.bluetooth; + +import android.util.Log; + +import com.iflytop.profilometer.core.migration.type.protocol.TPMIBasicReport; +import com.iflytop.profilometer.core.migration.type.protocol.TPMIPacket; +import com.iflytop.profilometer.core.migration.type.protocol.TPMIPosReport; +import com.iflytop.profilometer.core.system.DeviceState; +import com.iflytop.profilometer.core.system.SystemState; + +public class BleDeviceListener implements BleDeviceOnReportListener { + private static final String TAG = "MyBleDeviceListener"; + + @Override + public void onBasicReport(TPMIBasicReport report) { + Log.i(TAG, "基础上报包:" + report); + DeviceState deviceState = SystemState.getInstance().getDeviceState(); + deviceState.setTemperature(report.getTemperature()); + deviceState.setInclinatorX(report.getInclinatorX()); + deviceState.setInclinatorY(report.getInclinatorY()); + deviceState.setPower(report.getPower()); + deviceState.setFlag(report.getFlag()); + deviceState.setState(report.getState()); + } + + @Override + public void onPosReport(TPMIPosReport report) { + Log.i(TAG, "位置信息上报:"); + Log.i(TAG, "ARM0角度 " + report.getArm1Angle()); + Log.i(TAG, "ARM1角度" + report.getArm2Angle()); + } + + @Override + public void onUnknownReport(TPMIPacket raw) { + Log.w(TAG, "收到未知类型数据包: CMD=" + raw.getCommand() + " LEN=" + raw.getPacketLen()); + } +} diff --git a/app/src/main/java/com/iflytop/profilometer/core/bluetooth/BleDeviceOnReportListener.java b/app/src/main/java/com/iflytop/profilometer/core/bluetooth/BleDeviceOnReportListener.java new file mode 100644 index 0000000..a974e93 --- /dev/null +++ b/app/src/main/java/com/iflytop/profilometer/core/bluetooth/BleDeviceOnReportListener.java @@ -0,0 +1,11 @@ +package com.iflytop.profilometer.core.bluetooth; + +import com.iflytop.profilometer.core.migration.type.protocol.TPMIBasicReport; +import com.iflytop.profilometer.core.migration.type.protocol.TPMIPacket; +import com.iflytop.profilometer.core.migration.type.protocol.TPMIPosReport; + +public interface BleDeviceOnReportListener { + default void onBasicReport(TPMIBasicReport report) {} + default void onPosReport(TPMIPosReport report) {} + default void onUnknownReport(TPMIPacket raw) {} +} diff --git a/app/src/main/java/com/iflytop/profilometer/core/bluetooth/BleManager.java b/app/src/main/java/com/iflytop/profilometer/core/bluetooth/BleManager.java index 7bcb5eb..6864892 100644 --- a/app/src/main/java/com/iflytop/profilometer/core/bluetooth/BleManager.java +++ b/app/src/main/java/com/iflytop/profilometer/core/bluetooth/BleManager.java @@ -27,6 +27,8 @@ import androidx.annotation.RequiresApi; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; +import com.iflytop.profilometer.core.migration.channel.BleDeviceUartChannel; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -189,6 +191,9 @@ public class BleManager { } return instance; } + public static synchronized BleManager getInstance() { + return instance; + } /** * 检查指定权限是否已授予。 @@ -345,6 +350,11 @@ public class BleManager { try { BluetoothDevice device = bluetoothAdapter.getRemoteDevice(macAddress); bluetoothGatt = device.connectGatt(context, false, gattCallback); + + BleDeviceUartChannel channel = new BleDeviceUartChannel(); + channel.init(); + channel.setDeviceOnReportListener(new BleDeviceListener()); + } catch (IllegalArgumentException e) { Log.e(TAG, "无效的 MAC 地址: " + macAddress, e); } diff --git a/app/src/main/java/com/iflytop/profilometer/core/migration/driver/BleDeviceUartChannel.java b/app/src/main/java/com/iflytop/profilometer/core/migration/channel/BleDeviceUartChannel.java similarity index 66% rename from app/src/main/java/com/iflytop/profilometer/core/migration/driver/BleDeviceUartChannel.java rename to app/src/main/java/com/iflytop/profilometer/core/migration/channel/BleDeviceUartChannel.java index 368e1d4..440cf5e 100644 --- a/app/src/main/java/com/iflytop/profilometer/core/migration/driver/BleDeviceUartChannel.java +++ b/app/src/main/java/com/iflytop/profilometer/core/migration/channel/BleDeviceUartChannel.java @@ -1,10 +1,13 @@ -package com.iflytop.profilometer.core.migration.driver; +package com.iflytop.profilometer.core.migration.channel; import android.util.Log; +import com.iflytop.profilometer.core.bluetooth.BleDeviceOnReportListener; import com.iflytop.profilometer.core.bluetooth.BleManager; import com.iflytop.profilometer.core.migration.factory.TPMIPacketFactory; +import com.iflytop.profilometer.core.migration.type.protocol.TPMIBasicReport; import com.iflytop.profilometer.core.migration.type.protocol.TPMIPacket; +import com.iflytop.profilometer.core.migration.type.protocol.TPMIPosReport; import java.util.Arrays; @@ -15,19 +18,24 @@ public class BleDeviceUartChannel { } private OnBleReportListener reportListener; + private BleDeviceOnReportListener deviceOnReportListener; private final byte[] buffer = new byte[1024]; private int bufferLen = 0; public void init() { - BleManager.getInstance(null).setBleDataListener(this::handleReceivedData); + BleManager.getInstance().setBleDataListener(this::handleReceivedData); } public void setOnReportListener(OnBleReportListener listener) { this.reportListener = listener; } + public void setDeviceOnReportListener(BleDeviceOnReportListener listener) { + this.deviceOnReportListener = listener; + } + public void sendCommand(byte[] command) { - BleManager.getInstance(null).sendMessage(new String(command)); + BleManager.getInstance().sendMessage(new String(command)); } private void handleReceivedData(byte[] data) { @@ -49,6 +57,9 @@ public class BleDeviceUartChannel { if (reportListener != null) { reportListener.onReportReceived(report.packet); } + if (deviceOnReportListener != null) { + dispatchTypedPacket(report.packet); + } offset += report.packet.getPacketLen(); } else if (report.result.equals(TPMIPacketFactory.PacketAnalysisResult.CHECKSUM_ERROR)) { Log.w("BLE", "BLE数据包校验失败,跳过 1 字节"); @@ -66,4 +77,14 @@ public class BleDeviceUartChannel { bufferLen -= offset; } } -} \ No newline at end of file + + private void dispatchTypedPacket(TPMIPacket packet) { + if (packet instanceof TPMIBasicReport) { + deviceOnReportListener.onBasicReport((TPMIBasicReport) packet); + } else if (packet instanceof TPMIPosReport) { + deviceOnReportListener.onPosReport((TPMIPosReport) packet); + } else { + deviceOnReportListener.onUnknownReport(packet); + } + } +} diff --git a/app/src/main/java/com/iflytop/profilometer/core/system/DeviceState.java b/app/src/main/java/com/iflytop/profilometer/core/system/DeviceState.java new file mode 100644 index 0000000..c7a48fd --- /dev/null +++ b/app/src/main/java/com/iflytop/profilometer/core/system/DeviceState.java @@ -0,0 +1,77 @@ +package com.iflytop.profilometer.core.system; + +public class DeviceState { + private Boolean isConnected; + private Integer power; + private Integer state; + private Integer flag; + private Double inclinatorX; + private Double inclinatorY; + private Double temperature; + + public DeviceState() { + isConnected = false; + power = 0; + state = 0; + flag = 0; + inclinatorX = 0.0; + inclinatorY = 0.0; + temperature = 0.0; + } + + public Boolean getConnected() { + return isConnected; + } + + public void setConnected(Boolean connected) { + isConnected = connected; + } + + public Integer getPower() { + return power; + } + + public void setPower(Integer power) { + this.power = power; + } + + public Integer getState() { + return state; + } + + public void setState(Integer state) { + this.state = state; + } + + public Integer getFlag() { + return flag; + } + + public void setFlag(Integer flag) { + this.flag = flag; + } + + public Double getInclinatorX() { + return inclinatorX; + } + + public void setInclinatorX(Double inclinatorX) { + this.inclinatorX = inclinatorX; + } + + public Double getInclinatorY() { + return inclinatorY; + } + + public void setInclinatorY(Double inclinatorY) { + this.inclinatorY = inclinatorY; + } + + public Double getTemperature() { + return temperature; + } + + public void setTemperature(Double temperature) { + this.temperature = temperature; + } +} diff --git a/app/src/main/java/com/iflytop/profilometer/core/system/SystemState.java b/app/src/main/java/com/iflytop/profilometer/core/system/SystemState.java index c037318..49c57b1 100644 --- a/app/src/main/java/com/iflytop/profilometer/core/system/SystemState.java +++ b/app/src/main/java/com/iflytop/profilometer/core/system/SystemState.java @@ -6,6 +6,7 @@ import com.iflytop.profilometer.model.entity.AppUser; * 全局系统状态 */ public class SystemState { + private final DeviceState deviceState = new DeviceState(); private SystemState() { } @@ -28,4 +29,8 @@ public class SystemState { public synchronized void setCurrentUser(AppUser currentUser) { this.currentUser = currentUser; } + + public DeviceState getDeviceState() { + return deviceState; + } }