前言
我们根据Coap数据通信流程写一个公共组件,用户只要在原本的组件外嵌套这个公共组件就可以使用Coap的功能,这样做更加的方便便捷。
具体步骤
封装一个udp函数
- 创建一个工厂函数,工厂函数初始化时监听广播数据
- 发送广播函数:入参有发送的内容,目标的ip地址(默认是255.255.255.255)、ip端口(默认端口为1234)
-
import 'dart:async'; import 'dart:io';import 'package:my_app/common/value/serve.dart';class UDPClient {factory UDPClient() => _getInstance();static UDPClient get instance => _getInstance();static UDPClient? _instance;late RawDatagramSocket udpSocket;final StreamController<List<int>> _getDataController =StreamController.broadcast(); //监听数据流的控制器StreamController get getDataController => _getDataController; //获取数据流的控制器//初始化static UDPClient _getInstance() {_instance ??= UDPClient._internal();return _instance!;}//创建一个UDPClient实例UDPClient._internal() {(InternetAddress.lookup('pool.ntp.org')).then((value) {var serverAddress = value.first;// print("获取到的数据:----${serverAddress.type}-----${InternetAddress.anyIPv4}");RawDatagramSocket.bind(serverAddress.type == InternetAddressType.IPv6? InternetAddress.anyIPv6: InternetAddress.anyIPv4,0).then((value) {udpSocket = value;udpSocket.listen(handleUDPDatagram);udpSocket.broadcastEnabled = true;});});}//断开连接void disconnectFromUDP() {udpSocket.close();_instance = null;}//监听到的数据void handleUDPDatagram(RawSocketEvent event) {if (event == RawSocketEvent.read) {Datagram? datagram = udpSocket.receive();if (datagram != null) {List<int> data = datagram.data;// print("广播接收内容:$data");_getDataController.sink.add(data);}}}//发送数据void sendUDPData(List<int> data, {String ip = '255.255.255.255',int port = UDP_PORT,}) {print("${InternetAddress(ip)}");udpSocket.send(data, InternetAddress(ip), port);} }
封装一个Coap函数
- 创建一个Coap的工厂函数,可以传入ip地址和端口号
- 如果ip、port改变了则会创建一个工厂函数
- 封装Get、post、postBytes、put函数
import 'dart:async';import 'package:coap/coap.dart';
import 'package:my_app/common/value/serve.dart';
import 'package:typed_data/typed_data.dart';import '../value/coap_config.dart';class CoapClientUtil {factory CoapClientUtil({String? host, int? port}) =>_getInstance(host: host, port: port);static CoapClientUtil get instance => _getInstance(host: _currentHost);static CoapClientUtil? _instance;static CoapClient? client;static String _currentHost = COAP_API_URL;static int _currentPort = COAP_PORT;static CoapClientUtil _getInstance({String? host, int? port}) {String localHost = host ?? COAP_API_URL;int localPort = port ?? COAP_PORT;if (_instance == null ||_currentHost != localHost ||_currentPort != localPort) {_instance = CoapClientUtil._internal(localHost, localPort);_currentHost = localHost;_currentPort = localPort;}return _instance!;}CoapClientUtil._internal(String host, int port) {CoapConfig conf = CoapConfig();var baseUri = Uri(scheme: 'coap', host: host, port: port);client = CoapClient(baseUri, config: conf);}// 发送GET请求Future<CoapResponse?> get(final String path, {final CoapMediaType? accept,final bool confirmable = true,final List<Option<Object?>>? options,final bool earlyBlock2Negotiation = false,final int maxRetransmit = 0,final CoapMulticastResponseHandler? onMulticastResponse,}) async {try {var response = await client!.get(path,accept: accept,confirmable: confirmable,options: options,earlyBlock2Negotiation: earlyBlock2Negotiation,maxRetransmit: maxRetransmit,onMulticastResponse: onMulticastResponse,);return response;} catch (e) {print("错误的内容:${e}");return null;}}// 发送POST请求Future<CoapResponse?> post(final String path, {required final String payload,final CoapMediaType? format,final CoapMediaType? accept,final bool confirmable = true,final List<Option<Object?>>? options,final bool earlyBlock2Negotiation = false,final int maxRetransmit = 0,final CoapMulticastResponseHandler? onMulticastResponse,}) async {try {var response = await client!.post(path,payload: payload,format: format,accept: accept,confirmable: confirmable,options: options,earlyBlock2Negotiation: earlyBlock2Negotiation,maxRetransmit: maxRetransmit,onMulticastResponse: onMulticastResponse,);return response;} catch (e) {print("错误的内容:${e}");return null;}}/// 发送post请求,且携带的参数为二进制数组/// 需要注意的是如果返回的数据也是二进制数组则打印的response中的Payload为<<<< Payload incomplete >>>>>这是因为展示的payload走的是res.payloadString,看下发源码可知,转换成utf8抛出异常了,我们只要拿数据的时候使用res.payload即可/// String get payloadString {/// final payload = this.payload;/// if (payload.isNotEmpty) {/// try {/// final ret = utf8.decode(payload);/// return ret;/// } on FormatException catch (_) {/// // The payload may be incomplete, if so and the conversion/// // fails indicate this./// return '<<<< Payload incomplete >>>>>';/// }/// }/// return '';/// }Future<CoapResponse?> postBytes(final String path, {required final Uint8Buffer payload,final CoapMediaType? format,final CoapMediaType? accept,final bool confirmable = true,final List<Option<Object?>>? options,final bool earlyBlock2Negotiation = false,final int maxRetransmit = 0,final CoapMulticastResponseHandler? onMulticastResponse,}) async {try {var response = await client!.postBytes(path,payload: payload,format: format,accept: accept,confirmable: confirmable,options: options,earlyBlock2Negotiation: earlyBlock2Negotiation,maxRetransmit: maxRetransmit,onMulticastResponse: onMulticastResponse,);return response;} catch (e) {print("错误的内容:${e}");return null;}}// 发送PUT请求Future<CoapResponse?> put(final String path, {required final String payload,final CoapMediaType? format,final CoapMediaType? accept,final bool confirmable = true,// final List<Uint8Buffer>? etags,final MatchEtags matchEtags = MatchEtags.onMatch,final List<Option<Object?>>? options,final bool earlyBlock2Negotiation = false,final int maxRetransmit = 0,final CoapMulticastResponseHandler? onMulticastResponse,}) async {try {var response = await client!.put(path,payload: payload,format: format,accept: accept,confirmable: confirmable,// etags: etags,matchEtags: matchEtags,options: options,earlyBlock2Negotiation: earlyBlock2Negotiation,maxRetransmit: maxRetransmit,onMulticastResponse: onMulticastResponse,);return response;} catch (e) {print("错误的内容:${e}");return null;}}close() {client?.close();}disply() {client?.close();client = null;_instance = null;}
}
封装一个通用组件
- 传参内容:mac地址、Widget、key
- mac地址:因为通信协议中地址需要使用mac地址
- Widget:要展示的界面
- key:根据key值调用封装组件中的函数
- 初始化逻辑:Coap数据通信流程
- 如果要进行界面跳转
- 调用setDispose,关闭coap、udp等信息
- 同步界面跳转
- 创建一个定时器,定时器中包含初始化udp、coap
import 'dart:async';
import 'dart:convert';import 'package:coap/coap.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/material.dart';
import 'package:my_app/common/utils/coap.dart';
import 'package:my_app/common/utils/fun.dart';
import 'package:my_app/common/utils/udp.dart';
import 'package:my_app/pages/device_settings/time_switch_addtime/bottom_picker.dart';
import 'package:typed_data/typed_data.dart';///macStr 是测试的,后期要改
class CoapClientPackage extends StatefulWidget {const CoapClientPackage({super.key,this.mac = '11:22:33:44:55:66',required this.widget,});final String mac;final Widget widget;@overrideState<CoapClientPackage> createState() => CoapClientPackageState();
}class CoapClientPackageState extends State<CoapClientPackage> {StreamSubscription? _networkStatusSubscription; //监听设备的网络类型CoapClientUtil? coapClient;//udp获取到的数据UDPClient? udpClient;StreamSubscription? _sacnSubscription;bool isCoap = false;Timer? timer;@overridevoid initState() {super.initState();// initUDP();// //监听移动终端联网方式// _listenNetworkStatus();getinitState();}getinitState() {print("初始化");initUDP();//监听移动终端联网方式_listenNetworkStatus();}setDispose() {print("移除");_networkStatusSubscription?.cancel(); //取消监听coapClient?.disply(); //关闭coap连接udpClient?.disconnectFromUDP(); //关闭udp连接_sacnSubscription?.cancel(); //取消监听timer?.cancel(); //取消定时器}@overridevoid dispose() {setDispose();super.dispose();}initUDP() {udpClient = UDPClient.instance;_sacnSubscription?.cancel();_sacnSubscription = udpClient?.getDataController.stream.listen((data) {if (data is List<int>) {print("这是哪个数据:$data");setState(() {isCoap = false;});switch (data[2]) {case 129: //if (data[0] == 170 && data.length == 15 && data[14] == 85) {//将从data[7]开始截取数组// List macArr = data.sublist(3, 9); //截取mac地址// //将十进制数组转换成十六进制mac并添加:// String macStr =// macArr.map((e) => e.toRadixString(16).padLeft(2, '0')).join(':');// print("macArr:$macArr -----${macStr}");timer?.cancel();String serverIp ="${data[9]}.${data[10]}.${data[11]}.${data[12]}";print("获取到了ip地址:$serverIp");//创建一个coap服务coapClient = CoapClientUtil(host: serverIp,port: 5683,);setState(() {isCoap = true;});}break;default:}}});}//监听移动终端联网方式void _listenNetworkStatus() async {_networkStatusSubscription?.cancel(); //取消之前的监听bool isWif = await isWifi();if (isWif) {isWifiAfter();} else {_networkStatusSubscription = Connectivity().onConnectivityChanged.listen((ConnectivityResult result) {if (result == ConnectivityResult.wifi) {//当前的类型是WiFiisWifiAfter();} else {setState(() {isCoap = false;});}});}}isWifiAfter() {print('当前的类型是WiFi');//这里需要在发送mac地址,这里使用模拟的数据String macStr = widget.mac;// String macStr = '11:22:33:44:55:66';//将macStr装换成List<int>List<int> macArr =macStr.split(':').map((e) => int.parse(e, radix: 16)).toList();List<int> sendData = sendCoapData('01', macArr);print("-----=====------macArr:${sendData},macStr");Timer(Duration(seconds: 1), () {udpClient?.sendUDPData(sendData, port: 1234);});timer?.cancel();timer = Timer.periodic(Duration(seconds: 3), (timer) {if (udpClient != null) {try {udpClient?.sendUDPData(sendData, port: 1234);} catch (e) {print("发送出现了问题:${e}");}} else {print("为空");}});}//发送coap请求Future<CoapResponse?> sendCoap(String payload) async {String macStr = widget.mac;// String macStr = '11:22:33:44:55:66';var res = await coapClient?.post('/api/v1/$macStr/rpc',accept: CoapMediaType.applicationJson, payload: payload);return res;}//发送透传数据Future<CoapResponse?> sendTranCoap(String payload) async {String macStr = widget.mac;// String macStr = '11:22:33:44:55:66';var res = await coapClient?.post('/api/v1/$macStr/trans',accept: CoapMediaType.applicationJson, payload: payload);return res;}//发送透传数据Future<CoapResponse?> sendTranCoap1(Uint8Buffer payload) async {String macStr = widget.mac;// String macStr = '11:22:33:44:55:66';var res = await coapClient?.postBytes('/api/v1/$macStr/trans',accept: CoapMediaType.applicationJson, payload: payload);return res;}@overrideWidget build(BuildContext context) {return widget.widget;}
}