前言
我们根据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;@override
State<CoapClientPackage> createState() => CoapClientPackageState();
}class CoapClientPackageState extends State<CoapClientPackage> {
StreamSubscription? _networkStatusSubscription; //监听设备的网络类型
CoapClientUtil? coapClient;//udp获取到的数据
UDPClient? udpClient;
StreamSubscription? _sacnSubscription;
bool isCoap = false;
Timer? timer;
@override
void initState() {
super.initState();
// initUDP();// //监听移动终端联网方式 // _listenNetworkStatus(); getinitState();
}
getinitState() {
print("初始化");
initUDP();
//监听移动终端联网方式
_listenNetworkStatus();
}setDispose() {
print("移除");
_networkStatusSubscription?.cancel(); //取消监听
coapClient?.disply(); //关闭coap连接
udpClient?.disconnectFromUDP(); //关闭udp连接
_sacnSubscription?.cancel(); //取消监听
timer?.cancel(); //取消定时器
}@override
void 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) {
//当前的类型是WiFi
isWifiAfter();
} 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;
}@override
Widget build(BuildContext context) {
return widget.widget;
}
}
-