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;
      }
      }

相关推荐
Summer不秃3 小时前
Flutter之使用mqtt进行连接和信息传输的使用案例
前端·flutter
旭日猎鹰3 小时前
Flutter踩坑记录(二)-- GestureDetector+Expanded点击无效果
前端·javascript·flutter
sunly_3 小时前
Flutter:AnimatedSwitcher当子元素改变时,触发动画
flutter
旭日猎鹰9 小时前
Flutter踩坑记录(三)-- 更改入口执行文件
flutter
旭日猎鹰9 小时前
Flutter踩坑记录(一)debug运行生成的项目,不能手动点击运行
flutter
️ 邪神9 小时前
【Android、IOS、Flutter、鸿蒙、ReactNative 】自定义View
flutter·ios·鸿蒙·reactnative·anroid
比格丽巴格丽抱21 小时前
flutter项目苹果编译运行打包上线
flutter·ios
SoaringHeart21 小时前
Flutter进阶:基于 MLKit 的 OCR 文字识别
前端·flutter
AiFlutter1 天前
Flutter通过 Coap发送组播
flutter