zoukankan      html  css  js  c++  java
  • 【IoT最佳实践】设备获取实时天气DEMO代码解读

    【摘要】 之前,我们曾为您介绍如何实现设备实时获取天气信息,本文将为您从代码逻辑层面解读该实时天气应用的DEMO。

    本文承接【IoT最佳实践】设备获取实时天气信息,为您解读实时天气DEMO的代码逻辑,助您开发自己的实时天气应用。

    业务逻辑

    在看代码之前,让我们先来了解下这个设备获取实时天气应用的业务逻辑。

    1577259798263576.png

    1. 首先,应用需要先向物联网平台订阅设备数据变化通知,这样设备上报数据时平台才会将数据推送至应用。

    2~3. 然后,设备需要向平台上报一条包含城市码的数据,平台将其推送至应用。

    4~5. 应用收到包含城市码的数据后,根据城市码查询查询城市名称,再查询对应城市的天气信息缓存。

    6~8. 若查询缓存未命中或者缓存中的天气信息已缓存了一个小时以上,则应用向气象平台查询最新的天气信息并写入缓存。

    9~10. 应用将天气信息通过物联网平台下发至设备。

    代码实现

    接下来让我们看下这个DEMO是如何用JAVA实现这个业务逻辑的。

    首先查看作为应用程序入口的Main函数。

    public static void main(String[] args) throws Exception {
       //------------调用订阅数据变化接口-------------------
       SubscribeDataChange.subscribe();
       System.out.println("正在搭建消息接收服务器。");
       //------------启动上报数据接收服务器-------------------
       SpringApplication.run(Main.class);
       System.out.println("消息接收服务器搭建完成。");
    }

    这个Main函数一共做了两件事,一是调用了物联网平台订阅借口订阅了设备数据变化,二是启动了一个消息接受服务器(基于SpringBoot框架)。

    订阅设备数据变化时,Main函数调用了SubscribeDataChange类的subscribe方法:

    public static void subscribe() throws Exception{
       /**---------------------initialize northApiClient------------------------*/
       NorthApiClient northApiClient = AuthUtil.initApiClient();
       SubscriptionManagement subscriptionManagement = new SubscriptionManagement(northApiClient);
       /**---------------------get accessToken at first------------------------*/
       Authentication authentication = new Authentication(northApiClient);
       AuthOutDTO authOutDTO = authentication.getAuthToken();
       String accessToken = authOutDTO.getAccessToken();
       /**---------------------sub deviceAdded notification------------------------*/
       //note: 10.X.X.X is a LAN IP, not a public IP, so subscription callbackUrl's IP cannot be 10.X.X.X
       String callbackUrl = PropertyUtil.getProperty("subscription_CallbackUrl");//this is a test callbackUrl
       SubscriptionDTO subDTO = subDeviceData(subscriptionManagement, "deviceDataChanged", callbackUrl, accessToken);
    }private static SubscriptionDTO subDeviceData(SubscriptionManagement subscriptionManagement, String notifyType, String callbackUrl, String accessToken) {
           SubDeviceDataInDTO sddInDTO = new SubDeviceDataInDTO();
           sddInDTO.setNotifyType(notifyType);
           sddInDTO.setCallbackUrl(callbackUrl);       try {
              SubscriptionDTO subDTO = subscriptionManagement.subDeviceData(sddInDTO, null, accessToken);
              System.out.println("上报数据订阅成功"+subDTO.toString());          return subDTO;
           } catch (NorthApiException e) {
              System.out.println("订阅接口已使用,若是重复订阅请忽略"+e.toString());
          }
          return null;
    }

    该方法通过华为云设备管理服务的北向应用的SDK完成了物联网平台的接入鉴权和设备数据变化通知的订阅。

    Main函数执行完之后,应用处于等待调用的状态,等待平台推送设备数据上来。

    当应用收到了平台推送的设备数据后,会调用handleDeviceDataChanged方法,这个方法由设备管理服务北向应用SDK定义,我们需要重写它来实现我们的业务逻辑。

    @Overridepublic void handleDeviceDataChanged(NotifyDeviceDataChangedDTO body) {
       String cityCode = body.getService().getData().get("areaCode").asText();
       //------------查询天气-------------------
       Map<String,Object>  apiBody = null;   try {
          apiBody = WeatherUtil.getApiBody(cityCode);
       } catch (IOException e) {
          e.printStackTrace();
       } catch (ParseException e) {
          e.printStackTrace();
       }try {
          System.out.println("正在下发命令给设备。");
          //------------调用下发命令接口-------------------
          InvokeDeviceService.invocation(apiBody);
       } catch (Exception e) {
          e.printStackTrace();
       }
       System.out.println("命令下发已完成。");
    }

    在这个方法中,应用首先通过城市码查询天气,然后将查询到的天气通过命令下发的方式的下发给设备。下发命令和订阅一样是通过调用设备管理服务北向应用SDK的接口实现,本文不再展开,让我们展开看下应用通过城市码查询天气的具体流程。

    public static Map<String,Object> getApiBody(String cityCode) throws IOException, ParseException {
        Map<String, String> cityCodeName = new HashMap<String, String>();
        cityCodeName.put("755", "深圳");
        cityCodeName.put("20", "广州");
        cityCodeName.put("10", "北京");
        cityCodeName.put("21", "上海");
        String cityName = cityCodeName.get(cityCode);
        System.out.println("接收到设备上报的数据,设备上报的cityCode是"+cityCode+",对应的城市名是"+cityName+"。");

    首先应用根据城市码查询城市名。在这个DEMO中,我们现场构造了一个cityCodeName并写入了几大城市的城市码和城市名的对应关系,但在实际应用中,这个关系表应该是维护在持久化数据库中,此处从数据库中读取即可。

    File file=new File("cache/"+cityName+"dateTime"+".txt");
    Boolean isWeatherCur = false;if(!file.exists()){
        System.out.println("缓存没有天气数据,正在调用天气查询接口获取天气信息。");
        SimpleDateFormat sdf=new SimpleDateFormat("yy/MM/dd HH:mm:ss");
        String str=sdf.format(new Date());
        ReadWrTxt.writeFile(cityName+"dateTime",str);
    }else{
        String fileDateTime = ReadWrTxt.readFile(cityName+"dateTime");
        SimpleDateFormat format = new SimpleDateFormat("yy/MM/dd HH:mm:ss");
        String nowTime=format.format(new Date());
        Date d1 = format.parse(nowTime);
        Date d2 = format.parse(fileDateTime);    long diff = d1.getTime() - d2.getTime();    long diffMinutes = TimeUnit.MILLISECONDS.toMinutes(diff);    if(diffMinutes<=60 && diffMinutes>=0){
            System.out.println("读取缓存天气数据成功。");
            isWeatherCur = true;
        }else {
            System.out.println("天气数据缓存超过一个小时,已过期,正在调用天气查询接口获取天气信息。");
            SimpleDateFormat sdf=new SimpleDateFormat("yy/MM/dd HH:mm:ss");        String str=sdf.format(new Date());        ReadWrTxt.writeFile(cityName+"dateTime",str);    }}

    然后应用判断是否缓存过该城市的天气数据,以及缓存的时候是否已超过一个小时。此处我们采用比较简单做法,缓存是txt文件,文件名直接包括城市名,天气信息和缓存时间各保存一个文件。程序通过判断缓存时间txt文件是否存在判断是否缓存过天气信息,再读取缓存时间文件判断缓存是否超时。在您开发您自己的应用时,建议不要使用txt文件作为缓存,请使用redis之类的缓存数据库。

    File weatherFile=new File("cache/"+cityName+"weather"+".txt");
    Map<String,Object> apiBodySave = null;if(!weatherFile.exists()||!isWeatherCur){
        //------------调用天气查询接口,这里使用的是华为APIG SDK-------------------
        apiBodySave= Weather.getApiBody(cityName);
        String apiBodyStr = apiBodySave.toString();
        ReadWrTxt.writeFile(cityName+"weather",apiBodyStr);
    }
    String fileApiBodyStr = ReadWrTxt.readFile(cityName+"weather");
    fileApiBodyStr = fileApiBodyStr.replace("{","");
    fileApiBodyStr = fileApiBodyStr.replace("}","");
    fileApiBodyStr = fileApiBodyStr.replaceAll(" ","");
    String[] afterSplit = fileApiBodyStr.split(",");
    Map<String, Object> apiBody = new HashMap<String, Object>();for (String ele: afterSplit) {
        String[] keyVal = ele.split("=");
        keyVal[1]=keyVal[1].trim();
        apiBody.put(keyVal[0], keyVal[1]);
    }return apiBody;}

    若未缓存过该城市的天气信息或者缓存已过期,则通过华为云APIG调用气象平台的天气查询借口,并缓存天气信息。若已缓存过且天气信息未过期,则跳过该步骤,直接读取缓存的天气信息并返回。

    至此,设备获取实时天气的业务逻辑已打通,剩下的一些实现细节本文不再讲解,若您感兴趣,可以参考上一篇博客自行下载DEMO研究代码。

    作者:华为云专家  我是卤蛋

  • 相关阅读:
    Compression algorithm (deflate)
    tcpip数据包编码解析(chunk and gzip)_space of Jialy_百度空间
    What's the difference between the "gzip" and "deflate" HTTP 1.1 encodings?
    gzip压缩算法: gzip 所使用压缩算法的基本原理
    Decompressing a GZip Stream with Zlib
    Frequently Asked Questions about zlib
    how to decompress gzip stream with zlib
    自己动手写web服务器四(web服务器是如何通过压缩数据,web服务器的gzip模块的实现)
    What's the difference between the "gzip" and "deflate" HTTP 1.1 encodings?
    C语言抓http gzip包并解压 失败 C/C++ ChinaUnix.net
  • 原文地址:https://www.cnblogs.com/2020-zhy-jzoj/p/13165036.html
Copyright © 2011-2022 走看看