更新太和e护配置wifi失败问题

This commit is contained in:
wyf
2025-11-13 09:56:02 +08:00
parent a9992f40ee
commit 776275aa3d
34 changed files with 5470 additions and 552 deletions

View File

@@ -91,9 +91,11 @@ class _InstantBodyPageState extends State<InstantBodyPage>
bodyMotion = data['bodyMotion'] == null ? -1 : data['bodyMotion'];
breathrate = data["breathRate"] == null ? -1 : data["breathRate"];
heartrate = data['heartRate'] == null ? -1 : data['heartRate'];
snores = data['snores'] == null || data['snores'] == ""
snores = data['snores'] == null ||
data['snores'] == "" ||
data['snores'] == "".tr
? "".tr
: "".tr;
: "${data['snores']}".tr;
}
if (mounted) {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -29,7 +29,7 @@ class _MessagePageState extends State<MessagePage> {
_pageController =
PageController(initialPage: messageController.model.type == 1 ? 0 : 1);
messageController.getMessageStatus();
// _fetchMessageData();
_fetchMessageData();
}
void _fetchMessageData() {

View File

@@ -20,7 +20,6 @@ import 'package:vbvs_app/model/BleDeviceData.dart';
import 'package:vbvs_app/model/api_response.dart';
import 'package:vbvs_app/pages/mh_page/component/mht_bind_dialog.dart';
import 'package:vbvs_app/pages/mh_page/device/component/tool/BedControlService.dart';
import 'package:vbvs_app/pages/mh_page/device/component/tool/DeviceType.dart';
import 'package:vbvs_app/pages/mh_page/device/controller/mht_bluetooth_controller.dart';
import 'package:vbvs_app/pages/mh_page/device/model/BlueToothDataModel.dart';
import 'package:vbvs_app/pages/mh_page/homepage/controller/mht_home_controller.dart';
@@ -177,7 +176,7 @@ class _DeviceComponentWidgetState extends State<DeviceComponentWidget> {
),
].divide(SizedBox(width: 33.rpx)),
),
].divide(SizedBox(height: 37.rpx)),
].divide(SizedBox(height: 20.rpx)),
),
),
CustomCard(
@@ -340,7 +339,7 @@ class _DeviceComponentWidgetState extends State<DeviceComponentWidget> {
),
],
),
].divide(SizedBox(height: 37.rpx)),
].divide(SizedBox(height: 20.rpx)),
),
);
}

View File

@@ -0,0 +1,637 @@
import 'dart:async';
import 'package:EasyDartModule/EasyDartModule.dart' as edm;
import 'package:easydevice/easydevice.dart';
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:flutterflow_ui/flutterflow_ui.dart';
import 'package:mhtctrl/mhtctrl.dart';
import 'package:vbvs_app/common/color/ServiceConstant.dart';
import 'package:vbvs_app/common/color/appConstants.dart';
import 'package:vbvs_app/common/color/app_uri_status.dart';
import 'package:vbvs_app/common/util/BluetoothFirmwareUpdater.dart';
import 'package:vbvs_app/common/util/DailyLogUtils.dart';
import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/common/util/requestWithLog.dart';
import 'package:vbvs_app/component/tool/ClickableContainer.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
import 'package:vbvs_app/controller/theme_controller/ThemeController.dart';
import 'package:vbvs_app/enum/APPDeviceUpgrade.dart';
import 'package:vbvs_app/model/BleDeviceData.dart';
import 'package:vbvs_app/pages/mh_page/device/component/tool/BedControlService.dart';
import 'package:vbvs_app/pages/mh_page/device/controller/mht_bluetooth_controller.dart';
import 'package:vbvs_app/pages/mh_page/device/model/BlueToothDataModel.dart';
import 'package:vbvs_app/pages/mh_page/device/upgrade/device_upgrade.dart';
import 'package:vbvs_app/pages/mh_page/device/upgrade/tool/device_upgrade_tool.dart';
import 'package:vbvs_app/pages/mh_page/homepage/controller/mht_home_controller.dart';
class UpgradeDevice extends StatefulWidget {
BlueToothDataModel bleDevice;
var deviceType;
UpgradeDevice({
super.key,
required this.bleDevice,
required this.deviceType,
});
@override
State<UpgradeDevice> createState() => _UpgradeDeviceState();
}
class _UpgradeDeviceState extends State<UpgradeDevice> {
ThemeController themeController = Get.find();
MHTBlueToothController blueteethBindController = Get.find();
MHTHomeController homeController = Get.find();
var lisObj;
late MattressControlService service;
late BedControlService bedService;
MHTBlueToothController mhtBlueToothController = Get.find();
@override
Widget build(BuildContext context) {
Map device = {
"name": widget.bleDevice.name,
"mac".tr: widget.bleDevice.mac,
"rssi": widget.bleDevice.rssi,
"bind": widget.bleDevice.bind,
"version": widget.bleDevice.version,
"selected": widget.bleDevice.selected,
};
// ef.log("[rssi绘制]:--》${widget.bleDevice.rssi}");
return ClickableContainer(
backgroundColor: Colors.white,
highlightColor: Colors.white,
borderRadius: 20.rpx,
padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 36.rpx, 36.rpx, 36.rpx),
onTap: () async {
widget.bleDevice.selected = !widget.bleDevice.selected!;
device['selected'] = !device['selected'];
mhtBlueToothController.updateAll();
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisSize: MainAxisSize.max,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"${device['name']}",
style: TextStyle(
color: stringToColor("#333333"), fontSize: 30.rpx),
),
Obx(() {
var aa = mhtBlueToothController.allSelect.value;
print("${aa}");
double h = 33.rpx;
bool check = device['selected'] ?? false;
if (widget.bleDevice.upgradeStatus! ==
APPDeviceUpgrade.upgrading.value) {
return ClickableContainer(
backgroundColor: Colors.transparent,
highlightColor: Colors.transparent,
padding: EdgeInsets.all(0),
onTap: () {
//取消升级
MultiDeviceFirmwareUpdater().cancelUpgrade(widget.bleDevice.mac);
},
child: Row(
children: [
Text(
"${widget.bleDevice.process}%",
style: TextStyle(
color: themeController.currentColor.sc9,
fontSize: AppConstants().normal_text_fontSize,
),
),
Text(
"取消".tr,
style: TextStyle(
color: themeController.currentColor.sc9,
fontSize: AppConstants().normal_text_fontSize,
),
),
],
),
);
}
return Container(
height: 33.rpx,
child: Row(
children: [
AspectRatio(
aspectRatio: 1,
child: Center(
child: Container(
height: h,
width: h,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(h / 2),
border: Border.all(
width: check ? 1 : 0.5,
color: Color(0xFFC8CBD2),
),
),
child: check
? Center(
child: ClipOval(
child: Container(
width: h * 0.6,
height: h * 0.6,
color: const Color(0xFF6BFDAC),
),
),
)
: null,
),
),
),
].divide(SizedBox(width: 10.rpx)),
));
}),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: MediaQuery.sizeOf(context).width * 0.14,
constraints: BoxConstraints(minWidth: 106.rpx),
child: Text(
"MAC".tr,
style: TextStyle(
color: stringToColor("#929699"),
fontSize: 26.rpx,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
Text(
"${device['mac']}",
style: TextStyle(
fontSize: 26.rpx,
color: stringToColor("#333333")),
),
].divide(SizedBox(width: 33.rpx)),
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: MediaQuery.sizeOf(context).width * 0.14,
constraints: BoxConstraints(minWidth: 106.rpx),
child: Text(
"信号强度".tr,
style: TextStyle(
color: stringToColor("#929699"),
fontSize: 26.rpx,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
Text(
"${widget.bleDevice.rssi}" + "dBm".tr,
style: TextStyle(
fontSize: 26.rpx,
color: stringToColor("#333333")),
),
].divide(SizedBox(width: 33.rpx)),
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: MediaQuery.sizeOf(context).width * 0.14,
constraints: BoxConstraints(minWidth: 106.rpx),
child: Text(
"当前版本".tr,
style: TextStyle(
color: stringToColor("#929699"),
fontSize: 26.rpx,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
Text(
"${device['version']}",
style: TextStyle(
fontSize: 26.rpx,
color: Colors.black,
),
),
].divide(SizedBox(width: 33.rpx)),
),
].divide(SizedBox(height: 20.rpx)),
),
),
],
),
].divide(SizedBox(height: 20.rpx)),
),
);
}
//更新设备绑定状态
void updateDeviceBindStatus(BleDeviceData device) {
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.user_setting;
String mac = device.mac!;
String type = "device_bind_status_${mac}";
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
Map<String, dynamic> data = {
"type": type,
"mac".tr: mac,
"wifi": false,
"celibration": false,
"person_info": false,
"time": DateTime.now().millisecondsSinceEpoch,
};
requestWithLog(
logTitle: "更新用户绑定流程".tr,
method: MyHttpMethod.put,
queryUrl: queryUrl,
data: data,
onSuccess: (res) {},
onFailure: (res) {},
);
}
Widget _buildDeviceInfoSection(device, BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsetsDirectional.fromSTEB(0.rpx, 0.rpx, 30.rpx, 0.rpx),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
device.name ?? '蓝牙绑定.默认设备名称'.tr,
style: TextStyle(
fontFamily: 'Inter',
color: const Color(0xFFF6FAFD),
fontSize: 30.rpx,
letterSpacing: 0.0,
),
),
Obx(() {
if (blueteethBindController.currentDeviceMac.value ==
device.mac) {
return SizedBox(
width: 24.rpx,
height: 24.rpx,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(
themeController.currentColor.sc1,
),
),
);
}
return Container();
}),
],
),
),
Row(
children: [
Text(
"蓝牙绑定.信号强度".tr + '${device.rssi ?? '-'}dBm',
style: TextStyle(
fontFamily: 'Inter',
color: const Color(0xFFEBF2F8),
fontSize: 26.rpx,
letterSpacing: 0.0,
),
),
SizedBox(width: 40.rpx),
Text(
"蓝牙绑定.SN".tr + '${device.sn ?? '-'}',
style: TextStyle(
fontFamily: 'Inter',
color: const Color(0xFFF5F9FD),
fontSize: 26.rpx,
letterSpacing: 0.0,
),
),
],
),
Text(
"蓝牙绑定.蓝牙地址".tr + '${device.deviceId ?? '-'}',
style: TextStyle(
fontFamily: 'Inter',
color: const Color(0xFFF6FAFD),
fontSize: 26.rpx,
letterSpacing: 0.0,
),
),
Text(
"蓝牙绑定.mac".tr + '${device.mac ?? '-'}',
style: TextStyle(
fontFamily: 'Inter',
color: const Color(0xFFF6FAFD),
fontSize: 26.rpx,
letterSpacing: 0.0,
),
),
Row(
children: [
Text(
"蓝牙绑定.网络".tr +
'${device.isOnline == true ? '蓝牙绑定.在线'.tr : '蓝牙绑定.离线'.tr}',
style: TextStyle(
fontFamily: 'Inter',
color: const Color(0xFFEBF2F8),
fontSize: 26.rpx,
letterSpacing: 0.0,
),
),
SizedBox(width: 145.rpx),
Row(
children: [
Text(
"蓝牙绑定.传感器".tr + "",
style: TextStyle(
fontFamily: 'Inter',
color: const Color(0xFFEBF2F8),
fontSize: 26.rpx,
letterSpacing: 0.0,
),
),
Text(
device.bind == false ? '蓝牙绑定.可绑定'.tr : '蓝牙绑定.已被绑定'.tr,
style: TextStyle(
fontFamily: 'Inter',
color: device.bind == false
? const Color(0xFF1AD2B5)
: themeController.currentColor.sc9,
fontSize: 26.rpx,
letterSpacing: 0.0,
),
),
],
),
],
),
Text(
"版本".tr + '${device.version ?? '-'}',
style: TextStyle(
fontFamily: 'Inter',
color: const Color(0xFFF6FAFD),
fontSize: 26.rpx,
letterSpacing: 0.0,
),
),
].divide(SizedBox(
height: 37.rpx,
)),
);
}
//获取智能床/床垫mac
Future<String> getBindTHMAC(
BuildContext context, BlueToothDataModel device, Map deviceType) async {
const int maxRetries = 2;
const Duration timeout = Duration(seconds: 5);
String? macAddress;
try {
// 连接设备
THapp bledevice = THapp(device: device.scanResult.device);
await bledevice.connect();
var res2 = bledevice.isConnected;
if (!res2) {
edm.EasyDartModule.logger.error("蓝牙连接失败".tr);
DailyLogUtils.printLog("蓝牙连接失败".tr);
TopSlideNotification.show(
context,
text: "蓝牙连接失败".tr,
textColor: themeController.currentColor.sc9,
);
throw Exception("蓝牙连接失败".tr);
}
blueteethBindController.blueConnectFlag.value = 2;
blueteethBindController.currentDevice = bledevice;
await Future.delayed(Duration(seconds: 2));
if (deviceType['type'] == 3) {
//智能床垫
macAddress = await getMacFromType3(bledevice, timeout);
} else if (deviceType['type'] == 2) {
//智能床
macAddress = await getMacFromType2(bledevice, timeout);
} else {
throw Exception("不支持的设备类型".tr);
}
if (macAddress == null) {
throw Exception("未能获取到MAC地址".tr);
}
// device.macA = macAddress;
print('MAC地址: $macAddress');
return macAddress;
} catch (e) {
blueteethBindController.currentDeviceMac.value = "";
edm.EasyDartModule.logger.error("蓝牙获取MAC失败$e");
DailyLogUtils.printLog("蓝牙获取MAC失败$e");
TopSlideNotification.show(
context,
// text: e.message ?? "设备连接失败,请重试".tr,
text: "获取不到传感器mac,请重试".tr,
textColor: themeController.currentColor.sc9,
);
rethrow;
}
}
fillTHMac(
String mac, BlueToothDataModel bleDevice, BuildContext context) async {
bool flag = false;
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.get_bluetooth_device_status;
String queryUrl = "$serviceAddress$serviceName$serviceApi";
String macParam =
"mac=${Uri.encodeQueryComponent(mac.replaceAll(':', ''))}";
if (queryUrl.contains('?')) {
queryUrl += '&$macParam';
} else {
queryUrl += '?$macParam';
}
await requestWithLog(
logTitle: "获取设备状态".tr,
method: MyHttpMethod.get,
queryUrl: queryUrl,
onSuccess: (res) {
flag = true;
if (res.code != HttpStatusCodes.ok) return;
if (res.data != null && res.data is List) {
List<dynamic> responseList = res.data;
// 查找当前MAC对应的数据
String macKey = mac.replaceAll(':', '').toUpperCase();
for (var item in responseList) {
if (item['mac'.tr].toString().toUpperCase() == macKey) {
// 更新 bleDevice 的状态
//如果传感器已经绑定 暂时不处理
// bleDevice.bind = item['bind'] ?? bleDevice.bind;
bleDevice.macA = item['mac'.tr];
if (item['bindMac'] != null &&
item['bindMac'].toString().isNotEmpty) {
bleDevice.macB = item['bindMac'];
}
break;
}
}
}
},
onFailure: (res) {
flag = false;
TopSlideNotification.show(context,
text: "获取设备状态失败".tr, textColor: themeController.currentColor.sc9);
},
);
return flag;
}
Future<String> getMacFromType3(THapp bledevice, Duration timeout) async {
final read = bledevice.getresource('fff0/fff1');
await read!.characteristic.setNotifyValue(true);
final write = bledevice.getresource('fff0/fff2');
const int maxRetries = 2;
for (int attempt = 0; attempt < maxRetries; attempt++) {
final completer = Completer<String>();
final subscription = read.characteristic.onValueReceived.listen((data) {
if (data.length >= 14) {
completer.complete(parseMacFromBleResponse(data));
}
});
final order = [
0xFF,
0xFF,
0xFF,
0xFF,
0x00,
0x03,
0x40,
0x01,
0x01,
0x00,
0x45,
0xFD
];
await write!.characteristic.write(order);
try {
final mac = await completer.future.timeout(timeout);
if (mac == null || mac.isEmpty) {
throw Exception("获取MAC失败".tr);
}
if (mac == "000000000000") {
throw Exception("获取MAC失败".tr);
}
await subscription.cancel();
return mac;
} catch (_) {
await subscription.cancel();
if (attempt == maxRetries - 1) rethrow;
}
}
throw Exception("获取MAC超时".tr);
}
Future<String> getMacFromType2(THapp bledevice, Duration timeout) async {
try {
final read = bledevice.getresource('ffe0/ffe1');
await read!.characteristic.setNotifyValue(true);
final write =
bledevice.getresource('ffe0/ffe1'); // 与 read 同 characteristic
const int maxRetries = 2;
for (int attempt = 0; attempt < maxRetries; attempt++) {
final completer = Completer<String>();
final subscription = read.characteristic.onValueReceived.listen((data) {
if (data.length >= 17) {
completer.complete(parseMacFromTH2Response(data));
}
});
final order = [0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x0C, 0x0B, 0x0A];
int checksum = order.reduce((a, b) => a + b) & 0xFFFF;
order.add(checksum & 0xFF); // 低位
order.add((checksum >> 8) & 0xFF); // 高位
await write!.characteristic.write(order);
try {
final mac = await completer.future.timeout(timeout);
await subscription.cancel();
return mac;
} catch (_) {
await subscription.cancel();
if (attempt == maxRetries - 1) rethrow;
}
}
} catch (e) {
ef.log("[获取设备 MAC]:失败:$e");
}
throw Exception("获取MAC超时".tr);
}
}
String parseMacFromBleResponse(List<int> data) {
// 先做简单的合法性判断
if (data.length >= 17 &&
data[0] == 0xFF &&
data[1] == 0xFF &&
data[2] == 0xFF &&
data[3] == 0xFF &&
data[6] == 0x40 &&
data[7] == 0x01) {
// 取出 Byte8 ~ Byte13
List<int> macBytes = data.sublist(8, 14);
String macAddress = macBytes
.map((b) => b.toRadixString(16).padLeft(2, '0').toUpperCase())
.join('');
return macAddress;
} else {
throw Exception("BLE返回数据格式不正确".tr);
}
}
String parseMacFromTH2Response(List<int> data) {
if (data.length < 17) {
throw Exception("数据长度不足无法解析MAC".tr);
}
int status = data[8];
if (status != 0x03 && status != 0x04) {
throw Exception("未连接心率带".tr);
}
// 提取9~14字节的MAC地址
List<int> macBytes = data.sublist(9, 15);
return macBytes
.map((b) => b.toRadixString(16).padLeft(2, '0'))
.join(":")
.toUpperCase();
}

View File

@@ -9,6 +9,7 @@ import 'package:json_annotation/json_annotation.dart';
import 'package:vbvs_app/common/color/ServiceConstant.dart';
import 'package:vbvs_app/common/color/app_uri_status.dart';
import 'package:vbvs_app/common/util/DailyLogUtils.dart';
import 'package:vbvs_app/common/util/FirmwareVersionService.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/common/util/requestWithLog.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
@@ -24,7 +25,7 @@ class MHTBlueToothModel {
double? singal = -100;
@JsonKey(ignore: true)
List<BlueToothDataModel>? blueRawData; //蓝牙原始数据
List<BlueToothDataModel>? blueRawData = []; //蓝牙原始数据
@JsonKey(ignore: true)
List<BlueToothDataModel>? deviceDataStatus; //已经请求过状态的数据
@@ -76,7 +77,11 @@ class MHTBlueToothController extends GetControllerEx<MHTBlueToothModel> {
RxMap<String, Map> localUpgradeMac = <String, Map>{}.obs; //mac 进度
String? currentUpgradeVersion; //最新版本号
String? currentUpgradeName; //最新固件名
String? currentUpgradeUrl; //最新固件
String? currentUpgradeUrl; //最新固件下载地址
List<FirmwareVersionInfo> firmwareList = []; //固件版本列表
RxBool allSelect = false.obs; //升级是否全选
RxBool autoUpgrade = false.obs; //是否自动升级
void startStatusPolling() {
updateDeviceStatus().then((res) {
@@ -286,7 +291,7 @@ class MHTBlueToothController extends GetControllerEx<MHTBlueToothModel> {
resFlag = true;
},
onFailure: (res) {
resFlag = false;
resFlag = false;
},
);
return resFlag;

View File

@@ -0,0 +1,176 @@
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:flutterflow_ui/flutterflow_ui.dart';
import 'package:vbvs_app/common/color/appConstants.dart';
import 'package:vbvs_app/common/color/app_uri_status.dart';
import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/component/tool/CustomCard.dart';
import 'package:vbvs_app/component/tool/NewTopSlideNotification.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
import 'package:vbvs_app/controller/mh_controller/mh_language_controller.dart';
import 'package:vbvs_app/controller/user_info_controller.dart';
import 'package:vbvs_app/model/api_response.dart';
class DeviceMaintain extends StatefulWidget {
@override
_DeviceMaintainState createState() => _DeviceMaintainState();
}
class _DeviceMaintainState extends State<DeviceMaintain> {
MHLanguageController languageController = Get.find();
UserInfoController userInfoController = Get.find();
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, bodySize) {
return GestureDetector(
// onTap: () => FocusScope.of(context).unfocus(),,
child: Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/new_background.png'), // 本地图片
fit: BoxFit.fill, // 填满整个 Container
),
),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
backgroundColor: Colors.transparent,
iconTheme: const IconThemeData(color: Colors.white),
titleSpacing: 0,
automaticallyImplyLeading: false,
title: SizedBox(
width: double.infinity,
height: 180.rpx,
child: Stack(
alignment: Alignment.center,
children: [
// 中间居中的标题
Text(
'设备维护'.tr,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 30.rpx,
),
),
// 左侧图标
Positioned(
left: 0.rpx,
child: returnIconButtomNew(),
),
],
),
),
centerTitle: false,
),
body: SafeArea(
top: true,
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 0),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Padding(
padding: EdgeInsetsDirectional.fromSTEB(
0.rpx, 0.rpx, 0.rpx, 0),
child: CustomCard(
borderRadius: 16.rpx,
onTap: () {
Get.toNamed("/deviceUpgrade");
},
colors: [
Color(0xFF003058),
], // 渐变色是同一个色,也可以根据需要调整
child: Container(
width:
// MediaQuery.sizeOf(context).width * 0.66,
bodySize.maxWidth,
height: MediaQuery.sizeOf(context).height * 0.055,
constraints: BoxConstraints(
minWidth: 500.rpx,
minHeight: 90.rpx,
),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'批量升级'.tr,
style: TextStyle(
color: Colors.white,
fontFamily: 'Inter',
fontSize:
AppConstants().normal_text_fontSize,
letterSpacing: 0.0,
),
),
].divide(SizedBox(
width: 17.rpx,
)),
),
),
),
),
Padding(
padding: EdgeInsetsDirectional.fromSTEB(
0.rpx, 0.rpx, 0.rpx, 0),
child: CustomCard(
borderRadius: 16.rpx,
onTap: () {
// Get.toNamed("/deviceUpgrade");
NewTopSlideNotification.show(
text: "功能开发中...".tr,
);
},
colors: [
Color(0xFF003058),
], // 渐变色是同一个色,也可以根据需要调整
child: Container(
width:
// MediaQuery.sizeOf(context).width * 0.66,
bodySize.maxWidth,
height: MediaQuery.sizeOf(context).height * 0.055,
constraints: BoxConstraints(
minWidth: 500.rpx,
minHeight: 90.rpx,
),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'远程诊断'.tr,
style: TextStyle(
color: Colors.white,
fontFamily: 'Inter',
fontSize:
AppConstants().normal_text_fontSize,
letterSpacing: 0.0,
),
),
].divide(SizedBox(
width: 17.rpx,
)),
),
),
),
),
].divide(SizedBox(
height: 20.rpx,
)),
),
),
),
),
)),
);
});
}
}

View File

@@ -1,4 +1,5 @@
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:vbvs_app/common/util/FirmwareVersionService.dart';
class BlueToothDataModel {
String name; // 设备型号
@@ -13,6 +14,12 @@ class BlueToothDataModel {
DateTime lastSeen; // 最后可见时间
String? deviceID; // 设备ID
int? version; // ✅ 新增版本号,可为空
int? rssi; //信号强度
bool? selected; //是否选中
int? process = 0; //升级进度
int? newVersion; //升级版本
FirmwareVersionInfo? upgradeInfo;//升级固件信息
int? upgradeStatus;//升级状态
BlueToothDataModel({
this.name = '',
@@ -25,6 +32,12 @@ class BlueToothDataModel {
required this.lastSeen,
this.deviceID,
this.version, // ✅ 构造函数参数
this.rssi, // ✅ 构造函数参数
this.selected, // ✅ 构造函数参数
this.process, // ✅ 构造函数参数
this.newVersion, // ✅ 构造函数参数
this.upgradeInfo, // ✅ 构造函数参数
this.upgradeStatus, // ✅ 构造函数参数
});
factory BlueToothDataModel.fromScanResult(
@@ -35,6 +48,12 @@ class BlueToothDataModel {
String mac = '',
String? deviceID,
int? version, // ✅ 工厂方法参数
int? rssi, // ✅ 工厂方法参数
bool? selected, // ✅ 工厂方法参数
int? process, // ✅ 工厂方法参数
int? newVersion, // ✅ 工厂方法参数
FirmwareVersionInfo? upgradeInfo, // ✅ 工厂方法参数
int? upgradeStatus, // ✅ 工厂方法参数
}) {
String finalName =
name.isNotEmpty ? name : (result.advertisementData.localName ?? '');
@@ -50,6 +69,12 @@ class BlueToothDataModel {
lastSeen: DateTime.now(),
deviceID: deviceID,
version: version, // ✅ 赋值
rssi: rssi,
selected: selected,
process: process,
newVersion: newVersion,
upgradeInfo: upgradeInfo,
upgradeStatus: upgradeStatus,
);
}
}

View File

@@ -0,0 +1,165 @@
// 批量升级封装类
import 'package:easydevice/easydevice.dart';
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:vbvs_app/common/util/BluetoothFirmwareUpdater.dart';
import 'package:vbvs_app/component/tool/NewTopSlideNotification.dart';
import 'package:vbvs_app/pages/mh_page/device/model/BlueToothDataModel.dart';
import 'package:vbvs_app/pages/mh_page/device/upgrade/device_upgrade_controller.dart';
class BatchUpgradeManager {
static final BatchUpgradeManager _instance = BatchUpgradeManager._internal();
factory BatchUpgradeManager() => _instance;
BatchUpgradeManager._internal();
final MultiDeviceFirmwareUpdater _updater = MultiDeviceFirmwareUpdater();
final MHTDeviceUpgradeController _upgradeController = Get.find();
bool _isBatchUpgrading = false;
final List<BlueToothDataModel> _batchDevices = [];
// 开始批量升级
Future<void> startBatchUpgrade(List<BlueToothDataModel> devices) async {
if (_isBatchUpgrading) {
throw Exception("批量升级正在进行中".tr);
}
_isBatchUpgrading = true;
_batchDevices.clear();
_upgradeController.clearUpgradingList();
// 验证设备并添加到升级列表
for (var device in devices) {
if (device.selected == true) {
_batchDevices.add(device);
_upgradeController.addToUpgradingList(device);
}
}
if (_batchDevices.isEmpty) {
_isBatchUpgrading = false;
throw Exception("没有选择要升级的设备".tr);
}
// 开始批量升级
try {
await _startBatchUpgradeInternal();
} catch (e) {
_isBatchUpgrading = false;
rethrow;
}
}
// 内部批量升级实现
Future<void> _startBatchUpgradeInternal() async {
final List<Future> upgradeFutures = [];
for (var device in _batchDevices) {
final future = _startSingleDeviceUpgrade(device);
upgradeFutures.add(future);
}
// 等待所有升级完成
try {
await Future.wait(upgradeFutures, eagerError: false);
} finally {
_isBatchUpgrading = false;
_showBatchUpgradeCompleteNotification();
}
}
// 单个设备升级
Future<void> _startSingleDeviceUpgrade(BlueToothDataModel device) async {
try {
// 创建 THapp 实例
final thapp = THapp(device: device.scanResult.device);
// 获取固件URL - 需要您提供实现
final firmwareUrl = await _getFirmwareUrl();
if (firmwareUrl == null) {
throw Exception("固件URL为空".tr);
}
// 使用现有的升级器进行升级
await _updater.startUpgrade(
thapp: thapp,
mac: device.mac,
firmwareUrl: firmwareUrl,
);
// 升级成功,从列表中移除
_upgradeController.removeFromUpgradingList(device.mac);
} catch (e) {
// 升级失败,也从列表中移除(或者您可以保留失败设备用于重试)
_upgradeController.removeFromUpgradingList(device.mac);
rethrow;
}
}
// 获取固件URL - 需要您根据实际情况实现
Future<String?> _getFirmwareUrl() async {
// 这里可以从配置、用户选择或其他地方获取固件URL
// 例如return _upgradeController.selectedFirmwareUrl;
return null;
}
// 取消批量升级
void cancelBatchUpgrade() {
for (var device in _batchDevices) {
if (_updater.isDeviceUpgrading(device.mac)) {
_updater.cancelUpgrade(device.mac);
}
_upgradeController.removeFromUpgradingList(device.mac);
}
_isBatchUpgrading = false;
_batchDevices.clear();
NewTopSlideNotification.show(
text: "批量升级已取消".tr,
textColor: Colors.orange,
);
}
// 显示批量升级完成通知
void _showBatchUpgradeCompleteNotification() {
final allStatus = _updater.getAllUpgradeStatus();
final completedCount = allStatus.values.where((status) =>
status['status'] == 'completed').length;
final failedCount = allStatus.values.where((status) =>
status['status'] == 'failed').length;
String message;
if (failedCount == 0) {
message = "批量升级完成,所有 ${_batchDevices.length} 个设备升级成功".tr;
} else {
message = "批量升级完成,$completedCount 个成功,$failedCount 个失败".tr;
}
NewTopSlideNotification.show(
text: message,
textColor: failedCount == 0 ? Colors.green : Colors.orange,
);
}
// 检查是否正在批量升级
bool get isBatchUpgrading => _isBatchUpgrading;
// 获取批量升级统计
Map<String, int> getBatchUpgradeStatistics() {
final allStatus = _updater.getAllUpgradeStatus();
return {
'total': _batchDevices.length,
'completed': allStatus.values.where((status) =>
status['status'] == 'completed').length,
'failed': allStatus.values.where((status) =>
status['status'] == 'failed').length,
'upgrading': allStatus.values.where((status) =>
status['status'] == 'upgrading' || status['status'] == 'downloading').length,
'waiting': allStatus.values.where((status) =>
status['status'] == 'waiting').length,
};
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,244 @@
import 'package:easydevice/easydevice.dart';
import 'package:ef/ef.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:vbvs_app/common/util/BluetoothFirmwareUpdater.dart';
import 'package:vbvs_app/common/util/FirmwareVersionService.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/component/tool/NewTopSlideNotification.dart';
import 'package:vbvs_app/enum/APPDeviceUpgrade.dart';
import 'package:vbvs_app/pages/mh_page/device/controller/mht_bluetooth_controller.dart';
import 'package:vbvs_app/pages/mh_page/device/model/BlueToothDataModel.dart';
part 'device_upgrade_controller.g.dart';
@JsonSerializable()
class MHTDeviceUpgradeModel {
bool? bluetooth = false; // 蓝牙开关
double? singal = -100;
@JsonKey(ignore: true)
List<BlueToothDataModel>? blueRawData = []; //蓝牙原始数据
@JsonKey(ignore: true)
List<BlueToothDataModel>? upgradingDevices = []; // 正在升级的设备列表
MHTDeviceUpgradeModel();
static MHTDeviceUpgradeModel fromJson(Map<String, dynamic> json) =>
_$MHTDeviceUpgradeModelFromJson(json);
Map<String, dynamic> toJson() => _$MHTDeviceUpgradeModelToJson(this);
}
class MHTDeviceUpgradeController
extends GetControllerEx<MHTDeviceUpgradeModel> {
MHTDeviceUpgradeController() {
attr = GetModel(MHTDeviceUpgradeModel()).obs;
}
//选择的固件
FirmwareVersionInfo? selectVerison; //当前选中的固件信息
int maxLimit = 5;
MHTBlueToothController mhtBlueToothController = Get.find();
int? upgradeType = 1;
List<FirmwareVersionInfo>? firmwareList = [];
RxList? selectedDiseaseIds = [].obs; //已选中的列表
RxList diseaseList = [].obs; //升级类型列表
// 添加设备到升级列表
void addToUpgradingList(BlueToothDataModel device) {
if (!model.upgradingDevices!.any((d) => d.mac == device.mac)) {
model.upgradingDevices!.add(device);
update();
}
}
// 从升级列表移除设备
void removeFromUpgradingList(String mac) {
//todo 移除蓝牙连接
model.upgradingDevices!.removeWhere((device) => device.mac == mac);
update();
}
// 清空升级列表
void clearUpgradingList() {
//todo 移除蓝牙连接
model.upgradingDevices!.clear();
update();
}
// 获取正在升级的设备数量
int get upgradingDeviceCount => model.upgradingDevices?.length ?? 0;
// 检查设备是否正在升级
bool isDeviceUpgrading(String mac) {
return model.upgradingDevices!.any((device) => device.mac == mac);
}
//开始更新
Future<void> startUpgrade() async {
// 获取全部设备数据
var allDevices = model.blueRawData;
// 获取正在升级的设备数据
var upgradingDevices = model.upgradingDevices;
// 筛选出所有选中的设备
var selectedDevices =
allDevices!.where((device) => device.selected == true).toList();
// 排除正在升级的设备(即将已经在升级中的设备从选中的设备列表中移除)
var devicesToUpgrade = selectedDevices.where((device) {
// 检查设备是否在正在升级的设备列表中
return !upgradingDevices!
.any((upgradingDevice) => upgradingDevice.mac == device.mac);
}).toList();
if (devicesToUpgrade.isEmpty) {
NewTopSlideNotification.show(
text: "没有需要升级的设备".tr, textColor: themeController.currentColor.sc9);
ef.log("没有需要升级的设备");
return;
}
// 检查固件信息
if (selectVerison == null || selectVerison!.downloadUrl.isEmpty) {
NewTopSlideNotification.show(
text: "请先选择有效的固件版本".tr, textColor: themeController.currentColor.sc9);
throw Exception("请先选择有效的固件版本");
}
// mhtBlueToothController.currentUpgradeUrl = selectVerison!.downloadUrl;
// 打印最终需要升级的设备
ef.log("需要升级的设备:${devicesToUpgrade}");
for (var device in devicesToUpgrade) {
// await _startSingleDeviceUpgrade(device);
_startSingleDeviceUpgrade(device);
}
if (devicesToUpgrade.isNotEmpty) {
devicesToUpgrade.forEach((device) {
// 添加设备到正在升级列表
device.process = 0;
device.upgradeInfo = selectVerison;
addToUpgradingList(device);
// 发送升级指令
// mhtBlueToothController.sendUpgradeCommand(device);
});
}
print(model.upgradingDevices!);
}
// 启动单个设备升级
Future<void> _startSingleDeviceUpgrade(BlueToothDataModel device) async {
try {
// 更新设备状态
device.process = 0;
device.upgradeInfo = selectVerison;
device.selected = false; // 升级开始后取消选中状态
device.upgradeStatus = APPDeviceUpgrade.upgrading.value;
// 添加到升级列表
addToUpgradingList(device);
// 创建 THapp 实例
final thapp = THapp(device: device.scanResult.device);
// 确保设备连接
if (!thapp.isConnected) {
await thapp.device.connect();
}
// 使用 MultiDeviceFirmwareUpdater 开始升级
await MultiDeviceFirmwareUpdater().startUpgrade(
thapp: thapp,
mac: device.mac,
firmwareUrl: selectVerison!.downloadUrl,
);
ef.log("设备 ${device.mac} 升级任务已启动");
} catch (e, stack) {
ef.log("设备 ${device.mac} 升级启动失败: $e\n$stack");
// 更新设备状态为失败
device.process = -1;
device.upgradeStatus = APPDeviceUpgrade.fail.value;
update();
// 从升级列表中移除失败设备(可选,取决于业务需求)
// removeFromUpgradingList(device.mac);
rethrow;
}
}
// 取消单个设备升级
void cancelUpgrade(String mac) {
try {
MultiDeviceFirmwareUpdater().cancelUpgrade(mac);
removeFromUpgradingList(mac);
ef.log("设备 $mac 升级已取消");
} catch (e, stack) {
ef.log("取消设备 $mac 升级失败: $e\n$stack");
}
}
// 取消所有设备升级
void cancelAllUpgrades() {
try {
MultiDeviceFirmwareUpdater().cancelAllUpgrades();
clearUpgradingList();
ef.log("所有设备升级已取消");
} catch (e, stack) {
ef.log("取消所有设备升级失败: $e\n$stack");
}
}
// 获取设备升级状态
Map<String, dynamic>? getUpgradeStatus(String mac) {
return MultiDeviceFirmwareUpdater().getUpgradeStatus(mac);
}
// 获取所有设备升级状态
Map<String, Map<String, dynamic>> getAllUpgradeStatus() {
return MultiDeviceFirmwareUpdater().getAllUpgradeStatus();
}
// 检查设备是否正在升级(包括底层状态)
bool isDeviceInUpgrading(String mac) {
return MultiDeviceFirmwareUpdater().isDeviceUpgrading(mac) ||
isDeviceUpgrading(mac);
}
// 清理已完成的任务
void cleanupCompletedTasks() {
MultiDeviceFirmwareUpdater().cleanupCompletedTasks();
// 同时清理本地升级列表中已完成的任务
model.upgradingDevices!.removeWhere((device) {
final status = getUpgradeStatus(device.mac);
return status == null ||
['completed', 'failed', 'cancelled'].contains(status['status']);
});
update();
}
// 更新设备进度(供 MultiDeviceFirmwareUpdater 回调使用)
void updateDeviceProgress(String mac, int progress, String status) {
final device = model.upgradingDevices!.firstWhere((d) => d.mac == mac);
if (device != null) {
device.process = progress;
// 如果升级完成或失败,更新相关状态
if (status == 'completed' && progress == 100) {
device.newVersion = int.parse(selectVerison?.version ?? '0');
// 可以在这里添加其他完成后的逻辑
} else if (status == 'failed') {
device.process = -1;
}
update();
}
}
}

View File

@@ -0,0 +1,20 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'device_upgrade_controller.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
MHTDeviceUpgradeModel _$MHTDeviceUpgradeModelFromJson(
Map<String, dynamic> json) =>
MHTDeviceUpgradeModel()
..bluetooth = json['bluetooth'] as bool?
..singal = (json['singal'] as num?)?.toDouble();
Map<String, dynamic> _$MHTDeviceUpgradeModelToJson(
MHTDeviceUpgradeModel instance) =>
<String, dynamic>{
'bluetooth': instance.bluetooth,
'singal': instance.singal,
};

View File

@@ -0,0 +1,568 @@
import 'dart:async';
import 'dart:io';
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:flutterflow_ui/flutterflow_ui.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:vbvs_app/common/color/appConstants.dart';
import 'package:vbvs_app/common/util/BluetoothHelper.dart';
import 'package:vbvs_app/common/util/CommonVariables.dart';
import 'package:vbvs_app/common/util/FirmwareVersionService.dart';
import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
import 'package:vbvs_app/controller/main_bottom/global_controller.dart';
import 'package:vbvs_app/controller/theme_controller/ThemeController.dart';
import 'package:vbvs_app/controller/user_info_controller.dart';
import 'package:vbvs_app/pages/common/selectDialog.dart';
import 'package:vbvs_app/pages/mh_page/component/mht_bind_dialog.dart';
import 'package:vbvs_app/pages/mh_page/device/component/UpgradeDevice.dart';
import 'package:vbvs_app/pages/mh_page/device/controller/mht_bluetooth_controller.dart';
import 'package:vbvs_app/pages/mh_page/device/mht_blueteeth_device_page.dart';
import 'package:vbvs_app/pages/mh_page/device/model/BlueToothDataModel.dart';
import 'package:vbvs_app/pages/mh_page/device/upgrade/device_upgrade_controller.dart';
class DeviceUpgradeList extends StatefulWidget {
DeviceUpgradeList({super.key});
@override
State<DeviceUpgradeList> createState() => _DeviceUpgradeState();
}
class _DeviceUpgradeState extends State<DeviceUpgradeList> {
MHTBlueToothController mhtBlueToothController = Get.find();
GlobalController globalController = Get.find();
UserInfoController userInfoController = Get.find();
ThemeController themeController = Get.find();
late FlutterBluePlus flutterBlue;
List<ScanResult> scanResults = [];
bool isScanning = false;
Timer? _timer;
bool _isDialogShowing = false;
var currentConnectedDeviceProp;
var connectDeviceCurrent = null;
List bleDevice = [];
String currentMsg = "寻找设备中...";
Timer? connectTimer;
bool isFind = false;
StreamSubscription<List<ScanResult>>? _scanSubscription;
var formFieldController = FormFieldController<String>(null);
MHTDeviceUpgradeController mhtDeviceUpgradeController = Get.find();
@override
void initState() {
super.initState();
mhtBlueToothController.allSelect.value = false;
mhtBlueToothController.model.blueRawData = [];
mhtBlueToothController.model.deviceDataStatus = [];
flutterBlue = FlutterBluePlus();
_checkBluetoothPermission();
mhtBlueToothController.search.value = "";
mhtBlueToothController.currentDeviceMac?.value = "";
BluetoothHelper.listenBluetoothState((isOn) {
mhtBlueToothController.model.bluetooth = isOn;
mhtBlueToothController.updateAll();
if (!isOn && !_isDialogShowing) {
_isDialogShowing = true;
mhtBlueToothController.model.blueRawData = [];
mhtBlueToothController.model.deviceDataStatus = [];
mhtBlueToothController.updateAll();
_showBluetoothNotEnabledDialog().then((_) {
_isDialogShowing = false;
});
}
});
_initFirmwareVersions();
}
//扫描设备
Future<void> _checkBluetoothPermission() async {
PermissionStatus bluetoothStatus = await Permission.bluetooth.status;
PermissionStatus locationStatus = await Permission.location.status;
if (bluetoothStatus.isGranted && locationStatus.isGranted) {
_startScanning();
_startPeriodicScan();
} else {
_requestBluetoothPermission();
}
}
Future<void> _requestBluetoothPermission() async {
Map<Permission, PermissionStatus> statuses = {};
bool dialogShown = false; // 标记是否弹过权限提示弹窗
if (Platform.isIOS) {
PermissionStatus isBleGranted = await Permission.bluetooth.request();
print('checkBlePermissions-ios, isBleGranted=$isBleGranted');
if (isBleGranted.isGranted) {
// startBluetoothScanning();
_startScanning();
_startPeriodicScan();
} else {
try {
_startScanning();
_startPeriodicScan();
} catch (e) {
TopSlideNotification.show(context,
text: "蓝牙权限未开启,请在设置中开启蓝牙权限".tr,
textColor: themeController.currentColor.sc9);
}
}
} else if (Platform.isAndroid) {
try {
// 检查是否已授权
bool alreadyGranted = await Permission.bluetoothScan.isGranted &&
await Permission.bluetoothConnect.isGranted &&
await Permission.location.isGranted;
if (!alreadyGranted) {
// 弹出自定义提示
showPermissionInfoDialog(
Get.context!, CommonVariables().bluetoothpermissionInfo);
dialogShown = true;
await Future.delayed(const Duration(milliseconds: 300));
// 请求权限
statuses = await [
Permission.bluetoothScan,
Permission.bluetoothConnect,
Permission.location,
].request();
ef.log("权限状态: $statuses");
} else {
statuses = {
Permission.bluetoothScan: PermissionStatus.granted,
Permission.bluetoothConnect: PermissionStatus.granted,
Permission.location: PermissionStatus.granted,
};
}
} catch (e) {
ef.log("申请权限出错: $e");
} finally {
// 只有真的弹过提示才关闭
if (dialogShown && Get.context != null) {
Navigator.of(Get.context!, rootNavigator: true).pop();
}
}
bool allGranted = statuses[Permission.bluetoothScan]?.isGranted == true &&
statuses[Permission.bluetoothConnect]?.isGranted == true &&
statuses[Permission.location]?.isGranted == true;
if (allGranted) {
_startScanning();
_startPeriodicScan();
} else {
_showPermissionDeniedDialog(context);
}
} else {
TopSlideNotification.show(context,
text: "当前系统不支持蓝牙,无法使用此功能".tr,
textColor: themeController.currentColor.sc9);
}
}
void _showPermissionDeniedDialog(BuildContext context) {
TopSlideNotification.show(context,
text: "应用需要蓝牙和位置权限才能扫描设备。请授予权限。".tr,
textColor: themeController.currentColor.sc9);
}
void _startScanning() async {
try {
if (!mounted || isScanning || !mhtBlueToothController.shouldScan.value)
return;
_scanSubscription?.cancel();
mhtBlueToothController.updateAll();
if (!mhtBlueToothController.model.bluetooth!) return;
if (!isScanning) {
setState(() {
isScanning = true;
});
await FlutterBluePlus.startScan(timeout: Duration(seconds: 10));
_scanSubscription = FlutterBluePlus.scanResults.listen((results) {
if (!mounted) return;
final signalThreshold = mhtBlueToothController.model.singal!;
final searchKey =
mhtBlueToothController.search.value.trim().toLowerCase();
final filteredResults = results.map((r) {
Map<String, dynamic> d = {
"updateTime": DateTime.now().millisecondsSinceEpoch,
"name": r.advertisementData.localName?.trim() ??
r.advertisementData.advName?.trim() ??
r.device.name ??
"",
"rssi": r.rssi,
"device": r.device,
"connectable": r.advertisementData.connectable
};
// 从 manufacturerData 解析设备唯一 ID
Map<int, List<int>> m_d = r.advertisementData.manufacturerData;
String? deviceId;
m_d.forEach((key, value) {
if (value == null || value.isEmpty) return;
if (key == 65517) {
List<int> a = [0, 0, ...value];
advertisDataFormatter(a, d); // 你原来的处理
if (d['adData']?['deviceId'] != null)
deviceId = d['adData']['deviceId'];
} else if (key == 11125 && value.length == 8) {
deviceId = ab2str(value.sublist(2, 8)).toUpperCase();
}
});
d['id'] = deviceId ?? r.device.remoteId.str; // fallback UUID
return BlueToothDataModel.fromScanResult(
r,
1,
bind: false,
name: d['name'],
mac: d['id'],
version: d['adData']?['version'],
rssi: d['rssi'],
selected: mhtBlueToothController.allSelect.value,
);
}).where((d) {
// 信号强度过滤(用 d.scanResult.rssi 或 d.rssi 都可以)
if (d.scanResult.rssi <= signalThreshold) return false;
// 搜索关键字过滤
if (searchKey.isNotEmpty) {
String name = d.name.toLowerCase();
String mac = d.mac.toLowerCase();
if (!name.contains(searchKey) && !mac.contains(searchKey))
return false;
}
// 名称过滤规则,必须包含 deviceType['reg'] 列表中的某个字符串
List<String> regList = [];
if (regList.isNotEmpty) {
String lowerName = d.name.toLowerCase();
bool match =
regList.any((reg) => lowerName.contains(reg.toLowerCase()));
if (!match) return false;
}
// 修正:不要使用外部的 r改从 d.scanResult 读取广告数据
final adv = d.scanResult.advertisementData;
final isTarget = (d.rssi ?? d.scanResult.rssi) > signalThreshold &&
(adv.localName == "AITH-V2" || adv.localName == "SLAVE") &&
adv.manufacturerData.containsKey(0xFFED);
if (!isTarget) return false;
return true;
}).toList();
final currentDevices = mhtBlueToothController.model.blueRawData ?? [];
final newDevices = <BlueToothDataModel>[];
// for (var newDevice in filteredResults) {
// final existingIndex =
// currentDevices.indexWhere((d) => d.mac == newDevice.mac);
// if (existingIndex >= 0) {
// currentDevices[existingIndex] = newDevice;
// } else {
// newDevices.add(newDevice);
// }
// }
for (var newDevice in filteredResults) {
final existingIndex =
currentDevices.indexWhere((d) => d.mac == newDevice.mac);
if (existingIndex >= 0) {
// 如果设备已存在更新信号强度rssi
currentDevices[existingIndex].rssi = newDevice.rssi;
} else {
// 如果设备不存在,添加到新设备列表
newDevices.add(newDevice);
}
}
setState(() {
mhtBlueToothController.model.blueRawData = [
...currentDevices,
...newDevices
];
mhtBlueToothController.updateAll();
});
});
await Future.delayed(Duration(seconds: 10));
await FlutterBluePlus.stopScan();
if (mounted) {
setState(() {
isScanning = false;
});
}
}
} catch (e) {
ef.log("$e");
} finally {
setState(() {
isScanning = false;
});
}
}
void _startPeriodicScan() {
_timer = Timer.periodic(Duration(seconds: 3), (timer) {
if (mhtBlueToothController.shouldScan.value && !isScanning) {
_removeOldDevices(); // 先清理老旧设备
_startScanning();
}
});
}
void _stopScanning() {
if (isScanning) {
FlutterBluePlus.stopScan();
_scanSubscription?.cancel();
if (mounted) {
setState(() {
isScanning = false;
});
}
}
}
void _stopPeriodicScan() {
_timer?.cancel();
}
@override
void dispose() {
_stopPeriodicScan();
_stopScanning();
_scanSubscription?.cancel();
connectTimer?.cancel();
mhtBlueToothController.stopStatusPolling();
mhtBlueToothController.model.blueRawData = [];
mhtBlueToothController.model.deviceDataStatus = [];
BluetoothHelper.cancelListener();
super.dispose();
}
bool isTargetDevice(String? name, List<String> keywords) {
if (name == null) return false;
return keywords.any((k) => name.contains(k));
}
_showBluetoothNotEnabledDialog() async {
await showTipDialog(
backgroundColor: Colors.white,
context,
Column(
children: [
Text(
"蓝牙未开启".tr,
style: TextStyle(
fontSize: AppConstants().title_text_fontSize,
color: stringToColor("#333333"),
),
),
SizedBox(
height: 20.rpx,
),
Text(
"请先打开蓝牙在进行设备扫描".tr,
style: TextStyle(
fontSize: AppConstants().normal_text_fontSize,
color: stringToColor("#333333"),
),
),
],
));
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, boxConstraints) => GestureDetector(
// onTap: () => FocusScope.of(context).unfocus(),,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/new_background.png'),
fit: BoxFit.fill,
),
),
child: Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: Colors.transparent,
appBar: AppBar(
iconTheme: IconThemeData(color: themeController.currentColor.sc3),
backgroundColor: Colors.transparent,
automaticallyImplyLeading: false,
titleSpacing: 0,
title: Container(
width: double.infinity,
height: 180.rpx,
child: Stack(
alignment: Alignment.center,
children: [
Text(
'升级列表'.tr,
style: TextStyle(
fontFamily: 'Readex Pro',
color: themeController.currentColor.sc3,
letterSpacing: 0,
fontSize: 30.rpx,
),
),
Positioned(
left: 0,
child: returnIconButtom,
),
],
),
),
actions: [],
centerTitle: false,
),
body: SafeArea(
top: true,
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 0),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: Column(
children: [
Obx(() {
if (mhtDeviceUpgradeController.model
.upgradingDevices!.isNotEmpty) {
final sortedList = mhtDeviceUpgradeController.model
.upgradingDevices!
.toList()
..sort((a, b) => b.rssi!.compareTo(a.rssi!));
// if (sortedList.length > 0) {
// ef.log("[rssi变化]--》${sortedList[0].rssi}");
// }
return Expanded(
child: Container(
width: double.infinity,
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
...sortedList
.map((device) {
return UpgradeDevice(
bleDevice: device,
deviceType: 1,
);
})
.toList()
.divide(SizedBox(height: 30.rpx))
.addToEnd(SizedBox(height: 30.rpx)),
],
),
),
),
);
}
return Container();
}),
].divide(SizedBox(
height: 30.rpx,
)),
)),
].divide(SizedBox(height: 30.rpx)),
),
),
),
),
),
),
);
}
void _removeOldDevices() {
final now = DateTime.now();
final currentDevices = mhtBlueToothController.model.blueRawData ?? [];
// 移除30秒内未出现的设备
final updatedDevices = currentDevices.where((device) {
return now.difference(device.lastSeen) < Duration(seconds: 30);
}).toList();
if (updatedDevices.length != currentDevices.length) {
setState(() {
mhtBlueToothController.model.blueRawData = updatedDevices;
});
}
}
String getDeviceId(ScanResult result) {
// AndroidremoteId 就是 MAC
if (Platform.isAndroid) {
return result.device.remoteId.str.replaceAll(':', '').toUpperCase();
}
// iOS尝试从 manufacturerData 里解析
Map<int, List<int>> mData = result.advertisementData.manufacturerData;
for (var key in mData.keys) {
List<int>? bytes = mData[key];
if (bytes != null && bytes.length == 6) {
// 假设这 6 个字节就是 MAC
return bytes
.map((b) => b.toRadixString(16).padLeft(2, '0'))
.join('')
.toUpperCase();
}
}
return result.device.remoteId.str.toUpperCase();
}
Future<void> _initFirmwareVersions() async {
try {
const baseUrl = "https://share.file.he-info.cn";
const firmwarePath = "/ota/aithv2/";
final versions = await FirmwareVersionService.getAllFirmwareVersions(
baseUrl: baseUrl,
firmwarePath: firmwarePath,
);
if (versions.isNotEmpty) {
// 这里你可以存到控制器里
mhtBlueToothController.firmwareList = versions;
print("✅ 获取到 ${versions.length} 个固件版本:");
for (final v in versions) {
print("${v.fileName} (${v.version})");
}
if (mhtDeviceUpgradeController.selectVerison == null) {
mhtDeviceUpgradeController.selectVerison = versions.first;
formFieldController.value =
mhtDeviceUpgradeController.selectVerison!.version!;
mhtBlueToothController.updateAll();
} else {
formFieldController.value =
mhtDeviceUpgradeController.selectVerison!.version!;
}
} else {
print("⚠ 没有获取到固件版本文件");
}
} catch (e) {
print("❌ 初始化获取固件版本失败: $e");
}
}
}

View File

@@ -0,0 +1,328 @@
import 'dart:io';
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:vbvs_app/common/color/appConstants.dart';
import 'package:vbvs_app/common/util/FirmwareVersionService.dart';
import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/component/tool/SelectableTagButton.dart';
import 'package:vbvs_app/enum/APPDeviceUpgrade.dart';
import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart';
import 'package:vbvs_app/pages/mh_page/device/controller/mht_bluetooth_controller.dart';
import 'package:vbvs_app/pages/mh_page/device/model/BlueToothDataModel.dart';
import 'package:vbvs_app/pages/mh_page/device/upgrade/device_upgrade_controller.dart';
MHTDeviceUpgradeController mhtDeviceUpgradeController = Get.find();
Future<void> initDeviceUpgradeType() async {
try {
// 模拟异步加载
// await Future.delayed(const Duration(milliseconds: 300));
final dataList = APPDeviceUpgradeExtension.list;
ef.log("✅ 初始化固件升级类型成功,共 ${dataList.length}");
for (var item in dataList) {
ef.log(" ${item['id']} - ${item['name']}");
}
// 你可以在这里存入 controller 变量中,例如:
mhtDeviceUpgradeController.diseaseList.value = dataList;
} catch (e) {
ef.log("❌ 初始化获取固件版本失败: $e");
}
}
Future<void> initFirmwareVersions(formFieldController) async {
try {
const baseUrl = "https://share.file.he-info.cn";
const firmwarePath = "/ota/aithv2/";
final versions = await FirmwareVersionService.getAllFirmwareVersions(
baseUrl: baseUrl,
firmwarePath: firmwarePath,
);
if (versions.isNotEmpty) {
// 这里你可以存到控制器里
mhtDeviceUpgradeController.firmwareList = versions;
print("✅ 获取到 ${versions.length} 个固件版本:");
for (final v in versions) {
print("${v.fileName} (${v.version})");
}
if (mhtDeviceUpgradeController.selectVerison == null) {
mhtDeviceUpgradeController.selectVerison = versions.first;
formFieldController.value =
mhtDeviceUpgradeController.selectVerison!.version!;
final MHTBlueToothController _btController = Get.find();
_btController.currentUpgradeVersion =
mhtDeviceUpgradeController.selectVerison!.version!;
_btController.currentUpgradeName =
mhtDeviceUpgradeController.selectVerison!.fileName!;
_btController.currentUpgradeUrl =
mhtDeviceUpgradeController.selectVerison!.downloadUrl!;
mhtDeviceUpgradeController.updateAll();
} else {
formFieldController.value =
mhtDeviceUpgradeController.selectVerison!.version!;
}
mhtDeviceUpgradeController.updateAll();
} else {
print("⚠ 没有获取到固件版本文件");
}
} catch (e) {
print("❌ 初始化获取固件版本失败: $e");
}
}
getQueryList() {
return Padding(
padding: EdgeInsets.all(AppConstants().content_left_right_padding),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"筛选条件".tr,
style: TextStyle(
fontSize: AppConstants().title_text_fontSize,
color: Colors.white,
),
),
Obx(() {
final selectedIds = mhtDeviceUpgradeController.selectedDiseaseIds;
final diseases = mhtDeviceUpgradeController.diseaseList;
return Padding(
padding: EdgeInsetsDirectional.fromSTEB(0.rpx, 40.rpx, 0.rpx, 0),
child: Wrap(
spacing: 20.rpx,
runSpacing: 20.rpx,
children: diseases.map<Widget>((disease) {
final id = disease['id'];
final name = disease['name'];
final isSelected = selectedIds!.contains(id);
return SelectableTagButton(
selectedTextColor: Colors.black,
unselectedTextColor: Colors.white,
colors: [stringToColor("#84F5FF")],
borderRadius: AppConstants().button_container_radius,
label: name,
selected: isSelected,
onTap: () {
final allId = APPDeviceUpgrade.ALL.value; // 全部的id一般是1
if (id == allId) {
// 👉 点击的是“全部”
if (isSelected) {
// 当前是选中状态 → 取消“全部”选择(清空所有)
selectedIds.clear();
} else {
// 当前未选中 → 全部选中添加所有状态的id
selectedIds
..clear()
..addAll(APPDeviceUpgradeExtension.valueList);
}
} else {
// 👉 点击的不是“全部”
if (isSelected) {
// 已选中 → 取消当前id
selectedIds.remove(id);
// 若取消后,“全部”还在选中 → 去掉“全部”
if (selectedIds.contains(allId)) {
selectedIds.remove(allId);
}
} else {
// 未选中 → 选中当前id
selectedIds.add(id);
// 若选中的数量 == 除“全部”外的所有类型数 → 自动勾选“全部”
final allOtherIds = APPDeviceUpgradeExtension
.valueList
.where((v) => v != allId);
if (selectedIds.toSet().containsAll(allOtherIds)) {
selectedIds
..clear()
..addAll(APPDeviceUpgradeExtension.valueList);
}
}
}
mhtDeviceUpgradeController.updateAll();
});
}).toList(),
),
);
}),
],
),
),
);
}
// int getRemainingDeviceCount() {
// // 获取所有设备数据
// var allDevices = mhtDeviceUpgradeController.model.blueRawData!;
// // 获取正在升级的设备数据
// var upgradingDevices = mhtDeviceUpgradeController.model.upgradingDevices;
// // 获取正在升级的设备 mac 地址列表
// var upgradingMacs = upgradingDevices!.map((device) => device.mac).toList();
// // 计算所有设备中不在升级设备列表中的设备数量
// var remainingDevices = allDevices
// .where((device) => !upgradingMacs.contains(device.mac))
// .toList();
// // 返回剩余设备的数量,最小为 0
// mhtDeviceUpgradeController.selectVerison;
// return remainingDevices.length;
// }
int getRemainingDeviceCount() {
// 获取所有设备数据
var allDevices = mhtDeviceUpgradeController.model.blueRawData ?? [];
// 获取正在升级的设备数据
var upgradingDevices =
mhtDeviceUpgradeController.model.upgradingDevices ?? [];
// 获取当前选择的升级版本
var selectedVersion = mhtDeviceUpgradeController.selectVerison;
// 获取正在升级的设备 mac 地址列表
var upgradingMacs = upgradingDevices.map((device) => device.mac).toList();
// 过滤出:
// 1. 不在正在升级的设备列表中;
// 2. 且版本号不等于当前选中的版本(表示无需升级的排除掉)
var remainingDevices = allDevices.where((device) {
// bool notUpgrading = !upgradingMacs.contains(device.mac);
bool notUpgrading = true;
//todo wyf 恢复
// bool needUpgrade = device.version.toString() != selectedVersion!.version;
bool needUpgrade = true;
return notUpgrading && needUpgrade;
}).toList();
// 返回剩余需要升级的设备数量
List<BlueToothDataModel> displayDevices = [
// 用 upgrading 覆盖 raw 中的相同 mac
...allDevices.map((d) {
final u = upgradingDevices.firstWhereOrNull((x) => x.mac == d.mac);
return u ?? d;
}),
// 加入那些只在 upgradingDevices 中但不在 raw 的
...upgradingDevices.where((u) => !allDevices.any((d) => d.mac == u.mac)),
];
return displayDevices.length;
return remainingDevices.length;
}
String judgeUpgradeDeviceCount() {
List selectedDevices = mhtDeviceUpgradeController.model.blueRawData!
.where((device) => device.selected == true)
.toList();
if (selectedDevices.length == 0) {
return "请至少选择一项设备";
}
if (selectedDevices.length > mhtDeviceUpgradeController.maxLimit) {
return "超出数量限制,请等待";
}
final devices = mhtDeviceUpgradeController.model.upgradingDevices;
if (devices == null || devices.isEmpty) {
return "";
} else {
if (devices.length >= mhtDeviceUpgradeController.maxLimit) {
return "超出数量限制,请等待";
}
if (devices.length + selectedDevices.length >
mhtDeviceUpgradeController.maxLimit) {
return "超出数量限制,请等待";
}
}
return "";
}
String getDeviceId(ScanResult result) {
// AndroidremoteId 就是 MAC
if (Platform.isAndroid) {
return result.device.remoteId.str.replaceAll(':', '').toUpperCase();
}
// iOS尝试从 manufacturerData 里解析
Map<int, List<int>> mData = result.advertisementData.manufacturerData;
for (var key in mData.keys) {
List<int>? bytes = mData[key];
if (bytes != null && bytes.length == 6) {
// 假设这 6 个字节就是 MAC
return bytes
.map((b) => b.toRadixString(16).padLeft(2, '0'))
.join('')
.toUpperCase();
}
}
return result.device.remoteId.str.toUpperCase();
}
void removeOldDevices() {
final now = DateTime.now();
final currentDevices = mhtDeviceUpgradeController.model.blueRawData! ?? [];
// 移除30秒内未出现的设备
final updatedDevices = currentDevices.where((device) {
return now.difference(device.lastSeen) < Duration(seconds: 30);
}).toList();
if (updatedDevices.length != currentDevices.length) {
mhtDeviceUpgradeController.model.blueRawData = updatedDevices;
mhtDeviceUpgradeController.updateAll();
}
}
showBluetoothNotEnabledDialog(BuildContext context) async {
await showTipDialog(
backgroundColor: Colors.white,
context,
Column(
children: [
Text(
"蓝牙未开启".tr,
style: TextStyle(
fontSize: AppConstants().title_text_fontSize,
color: stringToColor("#333333"),
),
),
SizedBox(
height: 20.rpx,
),
Text(
"请先打开蓝牙在进行设备扫描".tr,
style: TextStyle(
fontSize: AppConstants().normal_text_fontSize,
color: stringToColor("#333333"),
),
),
],
));
}
List<BlueToothDataModel> filterDeviceType(List<BlueToothDataModel> devices) {
final selectedIds = mhtDeviceUpgradeController.selectedDiseaseIds;
// 如果未选择或包含“全部”,直接返回全部设备
if (selectedIds!.isEmpty ||
selectedIds.contains(APPDeviceUpgrade.ALL.value)) {
return devices;
}
// 否则按类型过滤
return devices
.where((device) => selectedIds.contains(device.upgradeStatus))
.toList();
}

View File

@@ -236,19 +236,23 @@ class _NewHomePageState extends State<NewHomePage> {
);
}),
const Spacer(), // 左右分隔
FloatingSvgIcon(
assetPath: 'assets/img/icon/xiaoyi.svg',
width: 60.rpx,
height: 60.rpx,
onTap: () {
// print("点击了小鹅图标");
if (userInfoController.model.login == 0) {
Get.toNamed("/loginPage");
}
Get.toNamed("/xiaoEPage",
arguments: "https://xiaoe.he-info.cn/");
},
),
if (userInfoController.model.login != null &&
userInfoController.model.login != 0 &&
userInfoController.model.user!.phone != null &&
userInfoController.model.user!.phone != "17649984946")
FloatingSvgIcon(
assetPath: 'assets/img/icon/xiaoyi.svg',
width: 60.rpx,
height: 60.rpx,
onTap: () {
// print("点击了小鹅图标");
if (userInfoController.model.login == 0) {
Get.toNamed("/loginPage");
}
Get.toNamed("/xiaoEPage",
arguments: "https://xiaoe.he-info.cn/");
},
),
SizedBox(width: 40.rpx),
],
),
@@ -563,6 +567,7 @@ class _NewHomePageState extends State<NewHomePage> {
),
),
),
InkWell(
onTap: () {
if (formFieldController

View File

@@ -490,6 +490,65 @@ class _SettingPageState extends State<SettingPage> {
),
),
),
if (userInfoController.model.user != null &&
userInfoController.model.user!.fac !=
null &&
userInfoController.model.user!.fac ==
true)
ClickableContainer(
backgroundColor:
Colors.transparent, // 容器背景色
highlightColor: themeController
.currentColor.sc21, // 点击时的背景色
padding: EdgeInsetsDirectional.fromSTEB(
0.rpx, 0.rpx, 0.rpx, 0.rpx),
onTap: () {
// Get.toNamed("/privacyPolicyPage");
Get.toNamed("/deviceMaintain");
},
child: Container(
child: Padding(
padding:
EdgeInsetsDirectional.fromSTEB(
40.rpx,
30.rpx,
40.rpx,
30.rpx),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Row(
mainAxisSize: MainAxisSize.max,
children: [
Text(
'设备维护'.tr,
style: TextStyle(
fontFamily: 'Inter',
color: Colors.white,
fontSize: AppConstants()
.title_text_fontSize,
letterSpacing: 0.0,
// height: 1.0,
),
),
].divide(
SizedBox(width: 22.rpx)),
),
SvgPicture.asset(
'assets/img/icon/arrow_right.svg',
width: 8.rpx,
height: 14
.rpx, // 如果 SVG 中没有固定颜色,可以这样设置
color: themeController
.currentColor.sc3,
),
],
),
),
),
),
]
.divide(SizedBox(height: 0.rpx))
.addToStart(SizedBox(height: 30.rpx))