From 3bde6954a15ceeb1282ea9a8661d0f9c3caf0135 Mon Sep 17 00:00:00 2001 From: sunbeam Date: Wed, 30 Apr 2025 17:01:25 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 +- lib/CanLib/abstract_can_device.dart | 12 ++- lib/CanLib/can_fd_msg.dart | 24 +++++ lib/CanLib/canfd_init_config.dart | 49 ++++++++++ lib/CanLib/toomoss_can.dart | 142 ++++++++++++++++++++++++++-- lib/device_managge.dart | 95 +++++++++++++++++++ lib/main.dart | 14 +++ 7 files changed, 326 insertions(+), 18 deletions(-) create mode 100644 lib/CanLib/can_fd_msg.dart create mode 100644 lib/CanLib/canfd_init_config.dart create mode 100644 lib/device_managge.dart diff --git a/README.md b/README.md index 9fcae08..12dc3a7 100644 --- a/README.md +++ b/README.md @@ -26,10 +26,10 @@ flutter build windows ## 二、功能 1. 展示 - 1.1 座椅调节 - 1.2 座椅按摩 - 1.3 座椅加热 - 1.4 座椅通风 + 1. 座椅调节 + 1. 座椅按摩 + 1. 座椅加热 + 1. 座椅通风 2. 报文 3. 日志 4. 设置 diff --git a/lib/CanLib/abstract_can_device.dart b/lib/CanLib/abstract_can_device.dart index 11a9313..4f9535e 100644 --- a/lib/CanLib/abstract_can_device.dart +++ b/lib/CanLib/abstract_can_device.dart @@ -1,3 +1,5 @@ +import 'can_fd_msg.dart'; + //CAN设备状态抽象类 class CANDev { //CAN设备名称 @@ -14,6 +16,8 @@ class CANDev { String firmwareversion='0.0.0'; //设备句柄 int devhandle=0; + + int errCounter=0; CANDev({ required this.devname, @@ -43,14 +47,14 @@ abstract class AbstractCANDevice { List getDeviceList(); // 启动CAN设备 - bool start(); + bool start({required int canIndex,required int canChannel , required int baudrate}); // 停止CAN设备 - bool stop(); + bool stop({required int canIndex}); // 发送CAN消息 - Future sendMessage(List data); + Future sendMessage({required int canIndex, required int canChannel, required int canid,required List data}); // 接收CAN消息 - Stream> receiveMessage(); + Stream receiveMessage({required int canIndex, required int canChannel}); } \ No newline at end of file diff --git a/lib/CanLib/can_fd_msg.dart b/lib/CanLib/can_fd_msg.dart new file mode 100644 index 0000000..139befd --- /dev/null +++ b/lib/CanLib/can_fd_msg.dart @@ -0,0 +1,24 @@ +import 'dart:ffi'; + +final class CanFdMsg extends Struct { + @Uint32() + external int id; // 报文ID,bit[30]-RTR,bit[31]-IDE,bit[28..0]-ID + + @Uint8() + external int dlc; // 数据字节长度,可设置为-0,1,2,3,4,5,6,7,8,12,16,20,24,32,48,64 + + @Uint8() + external int flags; // bit[0]-BRS,bit[1]-ESI,bit[2]-FDF,bit[6..5]-Channel,bit[7]-RXD + + @Uint8() + external int res0; // 保留 + + @Uint8() + external int res1; // 保留 + + @Uint32() + external int timeStamp; // 帧接收或者发送时的时间戳,单位为10us + + @Array(64) + external Array data; +} \ No newline at end of file diff --git a/lib/CanLib/canfd_init_config.dart b/lib/CanLib/canfd_init_config.dart new file mode 100644 index 0000000..31ab707 --- /dev/null +++ b/lib/CanLib/canfd_init_config.dart @@ -0,0 +1,49 @@ +import 'dart:ffi'; + +final class CanFdInitConfig extends Struct { + /// 0-正常模式,1-自发自收模式 + @Uint8() + external int mode; + + /// 0-禁止ISO CRC,1-使能ISO CRC + @Uint8() + external int isocrcEnable; + + /// 0-禁止重发,1-无限制重发 + @Uint8() + external int retrySend; + + /// 0-不接入内部120欧终端电阻,1-接入内部120欧终端电阻 + @Uint8() + external int resEnable; + + /// 仲裁段波特率参数, 波特率=40M/NBT_BRP*(1+NBT_SEG1+NBT_SEG2) + @Uint8() + external int nbtBRP; + + @Uint8() + external int nbtSEG1; + + @Uint8() + external int nbtSEG2; + + @Uint8() + external int nbtSJW; + + /// 数据段波特率参数, 波特率=40M/DBT_BRP*(1+DBT_SEG1+DBT_SEG2) + @Uint8() + external int dbtBRP; + + @Uint8() + external int dbtSEG1; + + @Uint8() + external int dbtSEG2; + + @Uint8() + external int dbtSJW; + + /// 预留字段 + @Array(8) + external Array res0; +} \ No newline at end of file diff --git a/lib/CanLib/toomoss_can.dart b/lib/CanLib/toomoss_can.dart index dd6085b..e5a4e28 100644 --- a/lib/CanLib/toomoss_can.dart +++ b/lib/CanLib/toomoss_can.dart @@ -1,5 +1,7 @@ import 'abstract_can_device.dart'; import 'device_info.dart'; +import 'canfd_init_config.dart'; +import 'can_fd_msg.dart'; import 'dart:ffi'; import 'package:ffi/ffi.dart'; import 'package:logging/logging.dart'; @@ -96,27 +98,147 @@ bool initialize() { } @override - bool start() { + bool start({required int canIndex,required int canChannel , required int baudrate}) { // 实现启动逻辑 + try { + final canfdInit = _dll.lookupFunction), int Function(int, int, Pointer)>('CANFD_Init'); + // 假设这里使用第一个设备的句柄,可根据实际情况修改 + if (_deviceList.isNotEmpty) { + var canConfig = calloc(); // 初始化 CANFD_INIT_CONFIG + canConfig.ref.mode = 0; // 正常模式 + canConfig.ref.isocrcEnable = 0; // 禁用 ISO CRC + canConfig.ref.retrySend = 0; // 禁止重发 + canConfig.ref.resEnable = 1; // 接入内部 120 欧终端电阻 + canConfig.ref.nbtBRP = 1; // 仲裁段波特率参数 + canConfig.ref.nbtSEG1 = 59; // 仲裁段波特率参数 + canConfig.ref.nbtSEG2 = 20; // 仲裁段波特率参数 + canConfig.ref.nbtSJW = 2; // 仲裁段波特率参数 + canConfig.ref.dbtBRP = 2; // 数据段波特率参数 + canConfig.ref.dbtSEG1 = 29; // 数据段波特率参数 + canConfig.ref.dbtSEG2 = 10; // 数据段波特率参数 + canConfig.ref.dbtSJW = 2; // 数据段波特率参数 + final result = canfdInit(_deviceList[canIndex].devhandle, canChannel, canConfig); + calloc.free(canConfig); // 释放内存 + if (result != 1) { + _logger.severe('CANFD_Init 初始化失败,返回值: $result'); + return false; + } + //初始化成功 + _logger.info('CANFD_Init 初始化成功,返回值: $result'); + _deviceList[canIndex].channelstatus[canChannel] = true; + _deviceList[canIndex].runstatus = true; + } else { + _logger.severe('无可用设备,无法初始化 CANFD_Init'); + return false; + } + } catch (e) { + _logger.severe('调用 CANFD_Init 时发生异常: $e'); + return false; + } return true; } @override - bool stop() { - // 实现停止逻辑 - return true; + bool stop({required int canIndex}) { + try { + if (_loadDll && _deviceList.isNotEmpty) { + final usbCloseDevice = _dll.lookupFunction('USB_CloseDevice'); + final result = usbCloseDevice(_deviceList[canIndex].devhandle); + if (!result) { + _logger.severe('USB_CloseDevice 调用失败,设备句柄: ${_deviceList[canIndex].devhandle.toRadixString(16).toUpperCase()}'); + } + return result; + } else { + _logger.severe('无法调用 USB_CloseDevice,可能 DLL 未加载或无可用设备。'); + return false; + } + } catch (e) { + _logger.severe('调用 USB_CloseDevice 时发生异常: $e'); + return false; + } } @override - Future sendMessage(List data) { - // 实现发送消息逻辑 - return Future.value(true); + Future sendMessage({required int canIndex, required int canChannel, required int canid,required List data}) { + if(_deviceList[canIndex].runstatus == false || _deviceList[canIndex].channelstatus[canChannel] == false) { + return Future.value(false);//设备/通道未启动 + } + try { + final canfdSendMsg = _dll.lookupFunction, Uint32), int Function(int, int, Pointer, int)>('CANFD_SendMsg'); + if (_deviceList.isNotEmpty) { + final devHandle = _deviceList[canIndex].devhandle; + var canMsg = calloc(); + canMsg.ref.id = canid; + canMsg.ref.dlc = data.length.toUnsigned(8); + for (int i = 0; i < data.length; i++) { + canMsg.ref.data[i] = data[i].toUnsigned(8); + } + final result = canfdSendMsg(devHandle, canChannel, canMsg, 1); + calloc.free(canMsg); + if (result >= 0) { + //_logger.info('CANFD_SendMsg 发送成功,返回值: $result'); + _deviceList[canIndex].errCounter = 0; + return Future.value(true); + + } else { + /* + //函数返回值错误信息定义 + #define CANFD_SUCCESS (0) //函数执行成功 + #define CANFD_ERR_NOT_SUPPORT (-1) //适配器不支持该函数 + #define CANFD_ERR_USB_WRITE_FAIL (-2) //USB写数据失败 + #define CANFD_ERR_USB_READ_FAIL (-3) //USB读数据失败 + #define CANFD_ERR_CMD_FAIL (-4) //命令执行失败 + */ + _logger.severe('CANFD_SendMsg 发送失败,返回值: $result'); + _deviceList[canIndex].errCounter++; + if(_deviceList[canIndex].errCounter > 5) { + _deviceList[canIndex].channelstatus[canChannel] = false; + _deviceList[canIndex].runstatus = false; + } + return Future.value(false); + } + } else { + _logger.severe('无可用设备,无法调用 CANFD_SendMsg'); + return Future.value(false); + } + } catch (e) { + _logger.severe('调用 CANFD_SendMsg 时发生异常: $e'); + _deviceList[canIndex].channelstatus[canChannel] = false; + _deviceList[canIndex].runstatus = false; + return Future.value(false); + } } @override - Stream> receiveMessage() { - // 实现接收消息逻辑 - return Stream.empty(); + Stream receiveMessage({required int canIndex, required int canChannel}) { + return Stream.periodic(Duration(milliseconds: 10), (_) { + if(_deviceList[canIndex].runstatus == false || _deviceList[canIndex].channelstatus[canChannel] == false) { + return null; + } + try { + final canfdGetMsg = _dll.lookupFunction, Int32), int Function(int, int, Pointer, int)>('CANFD_GetMsg'); + final bufferSize = 100; + final canMsgs = calloc(bufferSize); + final result = canfdGetMsg(_deviceList[canIndex].devhandle, canChannel, canMsgs, bufferSize); + if (result > 0) { + List messages = []; + for (int i = 0; i < result; i++) { + // 复制消息数据,避免内存释放后访问问题 + final msgCopy = calloc(); + msgCopy.ref = canMsgs[i]; + messages.add(msgCopy.ref); + } + calloc.free(canMsgs); + return messages; + } else { + calloc.free(canMsgs); + return null; + } + } catch (e) { + _logger.severe('调用 CANFD_GetMsg 时发生异常: $e'); + return null; + } + }).where((messages) => messages != null).expand((messages) => messages!); } diff --git a/lib/device_managge.dart b/lib/device_managge.dart new file mode 100644 index 0000000..a831035 --- /dev/null +++ b/lib/device_managge.dart @@ -0,0 +1,95 @@ +import 'package:flutter/material.dart'; + +class DeviceManagementDialog extends StatelessWidget { + const DeviceManagementDialog({super.key}); + + @override +Widget build(BuildContext context) { + // 定义CAN设备ID、CAN通道和波特率的选项列表 + final List canDeviceIds = ['ID1', 'ID2', 'ID3']; + final List canChannels = ['通道1', '通道2']; + final List baudRates = ['500K', '115200', '250000']; + + // 定义选中的值的状态变量 + String selectedCanDeviceId = canDeviceIds[0]; + String selectedCanChannel = canChannels[0]; + String selectedBaudRate = baudRates[0]; + + return AlertDialog( + title: const Text('设备管理'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // CAN设备ID下拉框 + DropdownButton( + value: selectedCanDeviceId, + onChanged: (newValue) { + if (newValue != null) { + selectedCanDeviceId = newValue; + } + }, + items: canDeviceIds.map>((String value) { + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + ), + // CAN通道下拉框 + DropdownButton( + value: selectedCanChannel, + onChanged: (newValue) { + if (newValue != null) { + selectedCanChannel = newValue; + } + }, + items: canChannels.map>((String value) { + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + ), + // 波特率下拉框 + DropdownButton( + value: selectedBaudRate, + onChanged: (newValue) { + if (newValue != null) { + selectedBaudRate = newValue; + } + }, + items: baudRates.map>((String value) { + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + ), + ], + ), + actions: [ + // 刷新按钮 + TextButton( + child: const Text('刷新'), + onPressed: () { + // 这里可以添加刷新的逻辑 + }, + ), + // 连接按钮 + TextButton( + child: const Text('连接'), + onPressed: () { + // 这里可以添加连接的逻辑 + }, + ), + // 关闭按钮 + TextButton( + child: const Text('关闭'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); +} +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 142ea5e..b14adf0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'Page/display_page.dart'; import 'Page/message_page.dart'; import 'Page/log_page.dart'; +import 'device_managge.dart'; void main() { runApp(const MyApp()); @@ -107,6 +108,19 @@ class _MyHomePageState extends State { _scaffoldKey.currentState?.openDrawer(); }, ), + actions: [ + IconButton( + icon: const Icon(Icons.settings), + onPressed: () { + showDialog( + context: context, + builder: (BuildContext context) { + return const DeviceManagementDialog(); + }, + ); + }, + ), + ], backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), ),