Flutter封装Coap
前言
我们根据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;
}
}