Commit 16cc47d9 authored by hywang's avatar hywang

1.蓝牙连接

parent 0611ffc4
...@@ -108,8 +108,9 @@ public class RfidPlugin implements FlutterPlugin, MethodChannel.MethodCallHandle ...@@ -108,8 +108,9 @@ public class RfidPlugin implements FlutterPlugin, MethodChannel.MethodCallHandle
@Override @Override
public void getBluetoothData(byte[] bytes) { public void getBluetoothData(byte[] bytes) {
Map<String, byte[]> result = new HashMap<>(); Map result = new HashMap();
result.put("BluetoothData", bytes); result.put("key", "BluetoothData");
result.put("value", bytes);
_eventSink.success(result); _eventSink.success(result);
} }
...@@ -119,7 +120,8 @@ public class RfidPlugin implements FlutterPlugin, MethodChannel.MethodCallHandle ...@@ -119,7 +120,8 @@ public class RfidPlugin implements FlutterPlugin, MethodChannel.MethodCallHandle
String epc = TextUtil.byteToHexString(inventoryData.getEPC_Data(), inventoryData.getEpcLength()).substring(1); String epc = TextUtil.byteToHexString(inventoryData.getEPC_Data(), inventoryData.getEpcLength()).substring(1);
String tid = TextUtil.byteToHexString(inventoryData.getData(), inventoryData.getDataLength()); String tid = TextUtil.byteToHexString(inventoryData.getData(), inventoryData.getDataLength());
result.put("InventoryData", epc + "," + tid); result.put("key", "InventoryData");
result.put("value", epc + "," + tid);
_eventSink.success(result); _eventSink.success(result);
} }
} }
import 'dart:async'; import 'dart:async';
import 'package:anchor_collect_flutter/utils/dialog_utils.dart'; import 'package:anchor_collect_flutter/utils/dialog_utils.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_reactive_ble/flutter_reactive_ble.dart'; import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
...@@ -11,20 +12,29 @@ import '../models/cattle_resume_entity.dart'; ...@@ -11,20 +12,29 @@ import '../models/cattle_resume_entity.dart';
import '../models/dict_entity.dart'; import '../models/dict_entity.dart';
import '../models/user_model.dart'; import '../models/user_model.dart';
class BleService extends GetxService { typedef void DiscoveredCallback(args);
typedef void ConnectCallback(args);
class BleService extends GetxService {
static BleService get to => Get.find();
late DiscoveredCallback? discoveredCallback;
late ConnectCallback? connectCallback;
// 添加一个 StreamController // 添加一个 StreamController
final _readController = StreamController<List<int>>(); final _readController = StreamController<List<int>>();
Stream<List<int>> get readStream => _readController.stream; Stream<List<int>> get readStream => _readController.stream;
late Uuid _serviceId;
late Uuid _writeCharacteristicId;
late Uuid _notifiCharacteristicId;
// 添加一个标志,用于控制读取循环是否应该继续 // 添加一个标志,用于控制读取循环是否应该继续
bool _shouldContinueReading = false; bool _shouldContinueReading = false;
static BleService get to => Get.find();
var isScanning = false.obs; var isScanning = false.obs;
final ble = FlutterReactiveBle(); final ble = FlutterReactiveBle();
final deviceList = <DiscoveredDevice>[].obs;
//判断是否正在扫描 //判断是否正在扫描
StreamSubscription<DiscoveredDevice>? scanSubscription; StreamSubscription<DiscoveredDevice>? scanSubscription;
...@@ -33,14 +43,18 @@ class BleService extends GetxService { ...@@ -33,14 +43,18 @@ class BleService extends GetxService {
final selectedDevice = Rx<DiscoveredDevice?>(null); final selectedDevice = Rx<DiscoveredDevice?>(null);
//判断设备连接状态 //判断设备连接状态
final deviceStatus = ''.obs; String deviceStatus = '';
// ignore: cancel_subscriptions
late StreamSubscription<ConnectionStateUpdate> streamSubscriptionConnection;
Future<BleService> init() async { Future<BleService> init() async {
return this; return this;
} }
// 切换扫描状态 // 切换扫描状态
Future<void> toggleScan() async { Future<void> toggleScan(DiscoveredCallback callback) async {
discoveredCallback = callback;
if (isScanning.value) { if (isScanning.value) {
await scanSubscription?.cancel(); await scanSubscription?.cancel();
isScanning.value = false; isScanning.value = false;
...@@ -59,7 +73,6 @@ class BleService extends GetxService { ...@@ -59,7 +73,6 @@ class BleService extends GetxService {
} }
// 扫描开始时,设置 isScanning 为 true // 扫描开始时,设置 isScanning 为 true
isScanning.value = true; isScanning.value = true;
deviceList.clear();
// 扫描设备 // 扫描设备
final scanStream = ble.scanForDevices( final scanStream = ble.scanForDevices(
...@@ -73,23 +86,29 @@ class BleService extends GetxService { ...@@ -73,23 +86,29 @@ class BleService extends GetxService {
scanSubscription = scanStream.listen((device) { scanSubscription = scanStream.listen((device) {
// 如果设备名称不为空,且列表中没有该设备,则添加到列表中 // 如果设备名称不为空,且列表中没有该设备,则添加到列表中
if (device.name != null && device.name!.isNotEmpty) { if (device.name != null && device.name!.isNotEmpty) {
// 如果列表中没有该设备,则添加到列表中 if (discoveredCallback != null) {
if (!deviceList.any((element) => element.id == device.id)) { discoveredCallback!(device);
deviceList.add(device);
deviceList.refresh(); // 手动触发 Obx 的更新
} }
} }
}); });
///扫描10秒
Future.delayed(const Duration(seconds: 15), () async {
await scanSubscription?.cancel();
isScanning.value = false;
DialogUtils.showToast('自动停止扫描');
});
} }
// 连接设备 // 连接设备
Future<void> connectToDevice(DiscoveredDevice device, BuildContext context) async { Future<void> connectToDevice(DiscoveredDevice device, BuildContext context, ConnectCallback callback) async {
connectCallback = callback;
selectedDevice.value = device; // 设置当前选中的设备 selectedDevice.value = device; // 设置当前选中的设备
await scanSubscription?.cancel(); // 在连接设备之前,停止扫描 await scanSubscription?.cancel(); // 在连接设备之前,停止扫描
isScanning.value = false; // 设置 isScanning 为 false isScanning.value = false; // 设置 isScanning 为 false
DialogUtils.showLoadingDialog('Connecting to ${device.name}'); DialogUtils.showLoadingDialog('正在连接到 ${device.name}');
// 显示正在连接的对话框 // 显示正在连接的对话框
// showDialog( // showDialog(
// context: context, // context: context,
...@@ -103,49 +122,71 @@ class BleService extends GetxService { ...@@ -103,49 +122,71 @@ class BleService extends GetxService {
// ); // );
// 连接设备 // 连接设备
final connection = ble.connectToDevice( var connection = ble.connectToDevice(
id: device.id, id: device.id,
connectionTimeout: const Duration(milliseconds: 10000),
servicesWithCharacteristicsToDiscover: {}, servicesWithCharacteristicsToDiscover: {},
); );
// 监听连接状态 // 监听连接状态
connection.listen((update) async { streamSubscriptionConnection = connection.listen((update) async {
// 如果连接状态为 connected,则显示连接成功的提示 // 如果连接状态为 connected,则显示连接成功的提示
if (update.connectionState == DeviceConnectionState.connected) { if (update.connectionState == DeviceConnectionState.connected) {
// 发现服务和特性 // 发现服务和特性
final services = await ble.discoverServices(device.id); final services = await ble.discoverServices(device.id);
for (final service in services) { for (final service in services) {
print('Discovered service ${service.serviceId}'); if (service.serviceId.toString() == '000180') {
for (final characteristic in service.characteristics) { _serviceId = service.serviceId;
print('Discovered characteristic ${characteristic.characteristicId} of service ${service.serviceId}'); if (kDebugMode) {
print('Discovered service ${service.serviceId}');
}
for (final characteristic in service.characteristics) {
if(characteristic.isWritableWithoutResponse) {
_writeCharacteristicId = characteristic.characteristicId;
if (kDebugMode) {
print('WritableWithoutResponse characteristic ${characteristic.characteristicId} of service ${service.serviceId}');
}
}
if(characteristic.isNotifiable) {
_notifiCharacteristicId = characteristic.characteristicId;
if (kDebugMode) {
print('Notifiable characteristic ${characteristic.characteristicId} of service ${service.serviceId}');
}
}
}
} }
} }
// 设置最大传输单元 // 设置最大传输单元
await ble.requestMtu(deviceId: device.id, mtu: 512); await ble.requestMtu(deviceId: device.id, mtu: 512);
await Future.delayed(Duration(seconds: 1)); // 添加延迟 await Future.delayed(const Duration(seconds: 1)); // 添加延迟
deviceStatus.value = 'Connected'; // 更新设备的连接状态 deviceStatus = 'Connected'; // 更新设备的连接状态
Get.snackbar('Connected', 'Connected to ${device.name}'); // Get.snackbar('Connected', 'Connected to ${device.name}');
// Navigator.of(context).pop(); // Navigator.of(context).pop();
if (connectCallback != null) {
connectCallback!(device);
}
// 关闭对话框 // 关闭对话框
DialogUtils.dismissDialog(); DialogUtils.dismissDialog();
} else if (update.connectionState == DeviceConnectionState.connecting) {
DialogUtils.showToast('设备连接中');
} else { } else {
deviceStatus.value = 'Disconnected'; // 更新设备的连接状态 DialogUtils.dismissDialog();
DialogUtils.showToast('连接设备失败');
deviceStatus = 'Disconnected'; // 更新设备的连接状态
} }
}); });
} }
// 读取数据 // 读取数据
Future<List<int>> readData(Uuid serviceUuid, Uuid characteristicUuid) async { Future<List<int>> readData() async {
if (selectedDevice.value == null) { if (selectedDevice.value == null) {
throw Exception('No device connected'); throw Exception('No device connected');
} }
final characteristic = QualifiedCharacteristic( final characteristic = QualifiedCharacteristic(
deviceId: selectedDevice.value!.id, deviceId: selectedDevice.value!.id,
serviceId: serviceUuid, serviceId: _serviceId,
characteristicId: characteristicUuid, characteristicId: _writeCharacteristicId,
); );
final result = await ble.readCharacteristic(characteristic); final result = await ble.readCharacteristic(characteristic);
...@@ -154,31 +195,31 @@ class BleService extends GetxService { ...@@ -154,31 +195,31 @@ class BleService extends GetxService {
} }
// 写入数据 // 写入数据
Future<void> writeData(Uuid serviceUuid, Uuid characteristicUuid, List<int> data) async { Future<void> writeData(List<int> data) async {
if (selectedDevice.value == null) { if (selectedDevice.value == null) {
throw Exception('No device connected'); throw Exception('No device connected');
} }
final characteristic = QualifiedCharacteristic( final characteristic = QualifiedCharacteristic(
deviceId: selectedDevice.value!.id, deviceId: selectedDevice.value!.id,
serviceId: serviceUuid, serviceId: _serviceId,
characteristicId: characteristicUuid, characteristicId: _writeCharacteristicId,
); );
await ble.writeCharacteristicWithResponse(characteristic, value: data); await ble.writeCharacteristicWithResponse(characteristic, value: data);
} }
// 启动循环读取数据 // 启动循环读取数据
Future<void> startReading(Uuid serviceUuid,Uuid characteristicUuid) async { Future<void> startReading() async {
_shouldContinueReading = true; _shouldContinueReading = true;
while (_shouldContinueReading) { while (_shouldContinueReading) {
// 读取数据 // 读取数据
final data = await readData(serviceUuid,characteristicUuid); final data = await readData();
// 将数据添加到 Stream 中 // 将数据添加到 Stream 中
_readController.add(data); _readController.add(data);
// 等待一段时间,然后再次读取数据 // 等待一段时间,然后再次读取数据
await Future.delayed(Duration(seconds: 1)); await Future.delayed(const Duration(seconds: 1));
} }
} }
...@@ -186,13 +227,13 @@ class BleService extends GetxService { ...@@ -186,13 +227,13 @@ class BleService extends GetxService {
void stopReading() { void stopReading() {
_shouldContinueReading = false; _shouldContinueReading = false;
} }
@override @override
void onClose() { void onClose() {
_readController.close(); _readController.close();
super.onClose(); super.onClose();
} }
// 检查权限 // 检查权限
Future<bool> requestBlePermissions() async { Future<bool> requestBlePermissions() async {
var isLocationGranted = await Permission.locationWhenInUse.request(); var isLocationGranted = await Permission.locationWhenInUse.request();
...@@ -212,7 +253,4 @@ class BleService extends GetxService { ...@@ -212,7 +253,4 @@ class BleService extends GetxService {
isBleConnectGranted == PermissionStatus.granted && isBleConnectGranted == PermissionStatus.granted &&
isBleAdvertiseGranted == PermissionStatus.granted; isBleAdvertiseGranted == PermissionStatus.granted;
} }
} }
...@@ -10,6 +10,7 @@ import 'package:flutter_easyloading/flutter_easyloading.dart'; ...@@ -10,6 +10,7 @@ import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'controllers/ble_service.dart';
import 'controllers/global_service.dart'; import 'controllers/global_service.dart';
final mDialog = FlutterSmartDialog.init(); final mDialog = FlutterSmartDialog.init();
...@@ -41,6 +42,7 @@ Future<void> initServices() async { ...@@ -41,6 +42,7 @@ Future<void> initServices() async {
///这里是你放get_storage、hive、shared_pref初始化的地方。 ///这里是你放get_storage、hive、shared_pref初始化的地方。
///或者moor连接,或者其他什么异步的东西。 ///或者moor连接,或者其他什么异步的东西。
await Get.putAsync(() => GlobalService().init()); await Get.putAsync(() => GlobalService().init());
await Get.putAsync(() => BleService().init());
print('All services started...'); print('All services started...');
} }
......
...@@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; ...@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../../congifs.dart'; import '../../congifs.dart';
import '../../routes/routes.dart';
import 'home_logic.dart'; import 'home_logic.dart';
class HomePage extends StatelessWidget { class HomePage extends StatelessWidget {
...@@ -18,8 +19,10 @@ class HomePage extends StatelessWidget { ...@@ -18,8 +19,10 @@ class HomePage extends StatelessWidget {
elevation: 10, elevation: 10,
actions: [ actions: [
InkWell( InkWell(
child: Icon(Icons.settings), child: const Icon(Icons.settings),
onTap: () => {}, onTap: () => {
Get.toNamed(AppRoute.SETTING),
},
), ),
], ],
), ),
......
...@@ -15,11 +15,11 @@ class MainPage extends StatelessWidget { ...@@ -15,11 +15,11 @@ class MainPage extends StatelessWidget {
final state = Get.find<MainLogic>().state; final state = Get.find<MainLogic>().state;
final syncLogic = Get.find<SyncLogic>(); final syncLogic = Get.find<SyncLogic>();
final messageLogic = Get.find<MessageLogic>(); final messageLogic = Get.find<MessageLogic>();
static List<Widget> _pageList = [ static final List<Widget> _pageList = [
HomePage(), HomePage(),
MessagePage(), MessagePage(),
SyncPage(), SyncPage(),
MinePage(), const MinePage(),
]; ];
final PageController _pageController = PageController( final PageController _pageController = PageController(
......
import 'package:anchor_collect_flutter/controllers/ble_service.dart';
import 'package:anchor_collect_flutter/routes/routes.dart';
import 'package:anchor_collect_flutter/utils/dialog_utils.dart';
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'setting_state.dart'; import 'setting_state.dart';
class SettingLogic extends GetxController { class SettingLogic extends GetxController {
final SettingState state = SettingState(); final SettingState state = SettingState();
@override @override
...@@ -13,6 +16,39 @@ class SettingLogic extends GetxController { ...@@ -13,6 +16,39 @@ class SettingLogic extends GetxController {
requestBlePermissions(); requestBlePermissions();
} }
refreshBle() {
if (BleService.to.isScanning.value) {
DialogUtils.showToast('停止扫描');
} else {
state.deviceList.clear();
DialogUtils.showToast('开始扫描');
}
BleService.to.toggleScan((device) => {
// 如果列表中没有该设备,则添加到列表中
if (!state.deviceList.any((element) => element.id == device.id))
{
state.deviceList.add(device),
state.deviceList.refresh(), // 手动触发 Obx 的更新
}
});
}
connectBle(BuildContext context, int index) {
if (BleService.to.deviceStatus == 'Connected') {
if (state.deviceList[index].id != BleService.to.selectedDevice.value!.id) {
BleService.to.streamSubscriptionConnection.cancel();
} else {
DialogUtils.showToast('请勿重复链接蓝牙设备');
return;
}
}
BleService.to.connectToDevice(state.deviceList[index], context, (args) {
DialogUtils.showToast('蓝牙设备连接成功');
// DialogUtils.dismissDialog();
Get.back();
});
}
// 检查权限 // 检查权限
Future<bool> requestBlePermissions() async { Future<bool> requestBlePermissions() async {
var isLocationGranted = await Permission.locationWhenInUse.request(); var isLocationGranted = await Permission.locationWhenInUse.request();
...@@ -32,5 +68,4 @@ class SettingLogic extends GetxController { ...@@ -32,5 +68,4 @@ class SettingLogic extends GetxController {
isBleConnectGranted == PermissionStatus.granted && isBleConnectGranted == PermissionStatus.granted &&
isBleAdvertiseGranted == PermissionStatus.granted; isBleAdvertiseGranted == PermissionStatus.granted;
} }
} }
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
import 'package:get/get.dart';
class SettingState { class SettingState {
List<Widget> items = []; final deviceList = <DiscoveredDevice>[].obs;
bool isBleOn = false; bool isBleOn = false;
......
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../../controllers/ble_service.dart';
import 'setting_logic.dart'; import 'setting_logic.dart';
class SettingPage extends StatelessWidget { class SettingPage extends StatelessWidget {
...@@ -9,7 +10,9 @@ class SettingPage extends StatelessWidget { ...@@ -9,7 +10,9 @@ class SettingPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final logic = Get.find<SettingLogic>(); final logic = Get.find<SettingLogic>();
final state = Get.find<SettingLogic>().state; final state = Get
.find<SettingLogic>()
.state;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
...@@ -18,33 +21,75 @@ class SettingPage extends StatelessWidget { ...@@ -18,33 +21,75 @@ class SettingPage extends StatelessWidget {
), ),
body: Center( body: Center(
child: Column( child: Column(
mainAxisSize: MainAxisSize.max,
children: [ children: [
Obx(() {
return Expanded(
child: ListView.separated(
itemCount: state.deviceList.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(state.deviceList[index].name),
subtitle: Text(state.deviceList[index].id),
onTap: ()=>{
logic.connectBle(context,index),
},
);
},
separatorBuilder: (BuildContext context, int index) {
return Container(
height: 1,
color: Colors.black12,
);
},
),
);
}),
SizedBox(
height: 1,
child: Container(
height: 1,
color: Colors.black54,
),
),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
TextButton( TextButton(
onPressed: () => {}, onPressed: () => {
child: const Text('刷新'), logic.refreshBle(),
},
child: Obx(() {
return Text(
BleService.to.isScanning.value ? '停止' : '刷新',
style: TextStyle(
fontSize: 20,
),
);
}),
), ),
// TextButton(
// onPressed: () => {},
// child: const Text(
// '停止',
// style: TextStyle(
// fontSize: 20,
// ),
// ),
// ),
TextButton( TextButton(
onPressed: () => {}, onPressed: () => {
child: const Text('停止'), state.deviceList.clear(),
), },
TextButton( child: const Text(
onPressed: () => {}, '清空',
child: const Text('清空'), style: TextStyle(
fontSize: 20,
),
),
), ),
], ],
), ),
ListView.separated(
itemCount: 3,
itemBuilder: (BuildContext context, int index) {},
separatorBuilder: (BuildContext context, int index) {
return Container(
height: 3,
color: Colors.black12,
);
},
),
], ],
), ),
), ),
......
...@@ -4,6 +4,8 @@ import 'package:anchor_collect_flutter/utils/empty_utils.dart'; ...@@ -4,6 +4,8 @@ import 'package:anchor_collect_flutter/utils/empty_utils.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import '../controllers/ble_service.dart';
typedef void EventCallback(args, args2); typedef void EventCallback(args, args2);
class RfidPlugin { class RfidPlugin {
...@@ -18,11 +20,11 @@ class RfidPlugin { ...@@ -18,11 +20,11 @@ class RfidPlugin {
String rfidTagTemp = ''; String rfidTagTemp = '';
RfidPlugin() { RfidPlugin() {
_streamSubscription = _eventChannel.receiveBroadcastStream().listen((event) { _streamSubscription = _eventChannel.receiveBroadcastStream().listen((event) async {
final Map<dynamic, dynamic> map = event; final Map<dynamic, dynamic> map = event;
switch (map['key']) { switch (map['key']) {
case 'BluetoothData': case 'BluetoothData':
//TODO 发送给蓝牙 await BleService.to.writeData(map['BluetoothData']);
break; break;
case 'InventoryData': case 'InventoryData':
checkInventory(map['value'].toString()); checkInventory(map['value'].toString());
...@@ -50,6 +52,14 @@ class RfidPlugin { ...@@ -50,6 +52,14 @@ class RfidPlugin {
await _methodChannel.invokeMethod('stop_inventory'); await _methodChannel.invokeMethod('stop_inventory');
} }
Future<void> pushRemoteRFIDData() async {
List<int> readData = await BleService.to.readData();
Map params = {
"value":readData,
};
await _methodChannel.invokeMethod('push_data', params);
}
errorEventListen(Object obj) { errorEventListen(Object obj) {
final Object e = obj; final Object e = obj;
if (kDebugMode) { if (kDebugMode) {
......
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 to comment