YangeIT大约 20 分钟中州养老AIIOT版本MysqlApifoxHTTPGETPOST

1. WebUI设计

1.1. IOT模块简介

前言

从今天开始,我们开始一个新的模块开发,叫做智能监测,我们可以先来看一张图: image

老人入住养老院之后,可以通过选择的配置来配备智能硬件设备,它有两类场景:安全场景和健康场景​

  • 安全场景:防跌倒意外、防走失、睡眠中意外、区域滞留、环境安全​
  • 健康场景:健康监测、健康小屋;​

通过硬件设备来监控老人以及老人活动场所的情况。后台时刻做监控,一旦有意外发生就可以迅速处理解决。​

其中关于智能设备的数据传输和上报,采用的华为云提供的设备接入服务(IoTDA),我们也会重点讲解设备如何上报数据到IOT以及如何从IOT平台拉取数据​

今日的目标 ​:

  • 能够熟悉华为云IoTDA平台的特点​
  • 能够掌握华为云IoTDA平台的一些概念,如:产品、设备、物模型等​
  • 能够掌握使用华为云IoTDA平台提供的接口创建设备,并绑定业务数据(老人、位置)​
  • 能够熟悉智能设备对接华为云IoTDA平台上报数据的方式

1.2. 物联网

物联网

在开发智能监测之前,我们必须要先熟悉和掌握华为云IoTDA平台的使用及对接,才能开发我们的项目。

什么是物联网image 把所有物品通过信息传感设备与互联网连接起来,进行信息交换,即物物相息,以实现智能化识别和管理。​ 物联网(英文:Internet of Things,缩写:IoT)起源于传媒领域,是信息科技产业的第三次革命。物联网是指通过信息传感设备,按约定的协议,将任何物体与网络相连接,物体通过信息传播媒介进行信息交换和通信,以实现智能化识别、定位、跟踪、监管等功能。​

常见的应用场景: 👇​

  • 共享充电宝​
    • 充电宝设备接入物联网平台后,可上报充电宝电量和借用状态等信息到物联网平台云端。充电宝用户扫码后,云端低延时向充电宝下发指令,使其弹出。同时,企业运营者能够实时获知充电宝的运行状况。​
  • 智能音箱 ​
    • 播报音箱接入物联网平台后,用户扫码完成支付后,将支付金额实时通过音箱,向用户和商家进行语音播报。​
  • 智能家居​
    • 智能家居技术已经成为当今家庭装潢的一大特色。比如,通过智能灯泡,可以实现远程控制灯光和电视等设备,调节温度和湿度,实现智能化生活。​
  • 智能农耕​
    • 智能农耕可以通过物联网技术来监测、传输、分析、管理农业生产过程中的信息。比如作物的生长情况、土壤的状况等,以提高农业生产的效率,改善利润率,减少污染,节约农业资源。​
  • 智能医疗​
    • 在智慧医疗中,可以捕捉人的生理状态信息,例如心跳频率、体力消耗、血压高低等。然后对采集数据进行备份、加工和分析,以便个人或医生快速查询。在物联网平台接入传感器设备,采集人体及周边环境参数的信息,通过数据服务处理数据后,反馈给用户

接下来讲解开通华为云IoTDA 👇

产品文档:https://support.huaweicloud.com/iothub/index.html​open in new window

设备接入服务(IoTDA)是华为云的物联网平台,提供海量设备连接上云、设备和云端双向消息通信、批量设备管理、远程控制和监控、OTA升级、设备联动规则等能力,并可将设备数据灵活流转到华为云其他服务。​ 我们作为一个开发者,基本的设备与后台调度思路,如下: image

1. 注册华为云账号,并且实名认证后,开通服务(微信扫码即可)

前往华为云官网注册账号。如果已有注册账号,请跳过此步骤。​ 进入华为云首页后,如果没有华为云的账户需要先进行注册,才可以进行登录。由于注册较为简单,自己摸索下。

华为云官网open in new window

如果没有实名认证,需要进行实名认证 image

建议使用微信扫码认证 image

认证成功 image

2. 开通物联网平台

登录账号以后,我们可以在产品中搜索设备接入IoTDA

image
image

选择按需计费,并且点击立即创建 image

自动跳转到实例列表,如下: image

点击实例名字,可以查看实例详情,如下: image

至此,我们开通了华为云IoTDA平台,

接下来我们开始创建产品,绑定设备,配置物模型,最后上设备,上传数据。

1.3. 创建产品

前言

在物联网平台中,某一类具有相同能力或特征的设备的合集被称为一款产品

产品是设备接入物联网平台的载体,是设备与平台进行数据交互的桥梁。设备接入物联网平台后,需要先绑定产品,然后才能进行数据上报和接收。​

image
image

1. 创建产品流程,如下:image

2. 创建产品模型

产品模型(Product Model),也称物模型,用于描述设备具备的能力和特性。开发者通过定义产品模型,在物联网平台构建一款设备的抽象模型,使平台理解该款设备支持的服务、属性、命令等信息。​

  • 手表本身:耗电量,使用时间​
  • 指标数据:身体血压、血氧、体温数据​
  • 像这些耗电量、血压、血氧数据都属于产品的功能,也叫做物模型

2.1 点击产品名字,进入产品详情 👇

image
image

2.2 点击自定义模型,创建服务(这个服务提供心率,血压,血氧,定位等服务)

image
image

2.3 然后可以给这个服务添加具体的物模型(如体温,血氧,血压,心率,用电量,定位信息等)

image
image
image
image

2.4 4个功能创建完毕,如下:

image
image

至此,产品模型创建完毕,接下来我们开始创建设备,绑定业务数据。

image
image

1.4. 创建设备

前言

设备是产品的一个实例,是物联网平台上的最小管理单元,每个设备具有一个唯一的标识码。设备通过接入物联网平台,可以上报数据,接收平台下发的指令。设备与产品的关系是一对多的关系,即一个产品可以创建多个设备,一个设备只能属于一个产品。

前提条件 :设备是绑定在产品上的,所以必须先创建产品才行​

操作步骤:​

  • 在左侧导航栏,选择设备-->所有设备​
  • 在设备页面,单击注册设备。​
  • 在添加设备对话框中,输入设备信息,单击确认。
image
image

创建成功之后,会分配一个设备的信息(设备id,和设备秘钥) ,非常重要,需要保存下来,方便后面调试链接

image
image

设备中可以查看,创建好的设备

image
image

设备详情中,可以查看设备信息,如下:

image
image

1.5. 设备接入-数据上报

前言

属性数据上报是一种需要平台解析、缓存,通过数据流转功能将数据转发到应用侧或华为云其他云服务的一种方式。需要在平台建立产品模型。对于属性上报的数据,平台会记录属性的最新一次上报值,对符合产品模型定义的属性数据进行存储。 参考华为官网例子:

基于NB-IoT小熊派的智慧烟感open in new window

image
image

由于官网案例,有很多例子open in new window,放到了github中,国内访问速度较慢,所以这里我们直接使用焱哥提供的代码模拟设备上传,如下:

华为官网教程:https://support.huaweicloud.com/sdkreference-iothub/iot_02_0089.htmlopen in new window

image
image

导入下列的华为iot设备依赖到,pom中

<dependency>
  <groupId>com.huaweicloud.sdk</groupId>
  <artifactId>huaweicloud-sdk-core</artifactId>
  <version>3.1.76</version>
</dependency>
<dependency>
  <groupId>com.huaweicloud.sdk</groupId>
  <artifactId>huaweicloud-sdk-iotda</artifactId>
  <version>3.1.76</version>
</dependency>
<!-- amqp 1.0 qpid client -->
<dependency>
  <groupId>org.apache.qpid</groupId>
  <artifactId>qpid-jms-client</artifactId>
  <version>0.61.0</version>
</dependency>
<dependency>
  <groupId>com.huaweicloud</groupId>
  <artifactId>iot-device-sdk-java</artifactId>
  <version>1.2.0</version>
</dependency>

导入ca.jks文件到resources下(文件焱哥在发送在群里image

导入下面的模拟设备代码,到iotdevice包下,如下:


/**
 * 演示如何直接使用DeviceClient进行设备属性的上报和读写
 */
public class PropertySample {

    //  加载iot平台的ca证书,进行服务端校验
    private static final String IOT_ROOT_CA_RES_PATH = "ca.jks";

    //  加载iot平台的ca证书,进行服务端校验
    private static final String IOT_ROOT_CA_TMP_PATH = "huaweicloud-iotda-tmp-" + IOT_ROOT_CA_RES_PATH;

    private static final Logger log = LogManager.getLogger(PropertySample.class);

    public static void main(String[] args) throws InterruptedException, IOException {

        // 加载iot平台的ca证书,进行服务端校验
        File tmpCAFile = new File(IOT_ROOT_CA_TMP_PATH);
        try (InputStream resource = PropertySample.class.getClassLoader().getResourceAsStream(IOT_ROOT_CA_RES_PATH)) {
            Files.copy(resource, tmpCAFile.toPath(), REPLACE_EXISTING);
        }

        // 创建设备并初始化. 用户请替换为自己的接入地址。-->watch01
        IoTDevice device = new IoTDevice(
                //接入地址获取方式:登录华为云IoTDA控制台左侧导航栏“总览”页签,在选择的实例基本信息中,单击“接入信息”-->设备接入-->MQTT。选择8883端
                "ssl://ce67d填写自己的1.st1.iotda-device.cn-north-4.myhuaweicloud.com:8883",
                //设备id
                "686c872填写自己的93245_watch001",
                //设备秘钥  设备创建成功后会自动生成
                "6d961c6a87填写自己的9d8693aca854f", tmpCAFile);
        if (device.init() != 0) {
            return;
        }

        // 定时上报属性
        while (true) {

            Map<String, Object> json = new HashMap<>();
            Random rand = new SecureRandom();

            // 按照物模型设置属性,根据实际情况设置,下面是智能报警手表的物模型
            json.put("BodyTemp", 36);
            json.put("xueyang", rand.nextFloat()*100.0f);
            json.put("HeartRate", rand.nextFloat()*100.0f);
            json.put("BatteryPercentage", rand.nextFloat() * 100.0f);

            ServiceProperty serviceProperty = new ServiceProperty();
            serviceProperty.setProperties(json);
            serviceProperty.setServiceId("watch_services"); // serviceId要和物模型一致

            device.getClient().reportProperties(Arrays.asList(serviceProperty), new ActionListener() {
                @Override
                public void onSuccess(Object context) {
                    log.info("pubMessage success");
                }

                @Override
                public void onFailure(Object context, Throwable var2) {
                    log.error("reportProperties failed" + var2.toString());
                }
            });

            Thread.sleep(10000);
        }
    }
}
接入地址获取方式1
接入地址获取方式1
接入地址获取方式2
接入地址获取方式2

右键启动之后的效果: image

消息发送成功后,可以在华为云IoTDA平台中查看该设备上报的数据,并且设备显示在线 image 上面还能看到模拟设备的数据 image

2. 集成IOT到业务平台

2.1. 设备接入-设备控制

前言

我们熟悉了IOT平台的基本概念以及基本操作后,可以现在来分析后台系统的功能需求,在后台管理系统中,是需要自己维护设备,不需要创建产品,因为产品直接物联网平台创建添加即可​ 需要单独维护设备的原因是,设备需要跟养老院的老人或者位置进行绑定,才能做到精准的监控

比如:​

  • 烟雾报警器需要绑定到某个房间​
  • 智能手表需要绑定某个老人
image
image

接下来,完成设备同步到本地,和设备列表的增删改查工功能。 其中需要导入基础代码。完成设备和数据的基本增删改查。

1. 创建表(设备表+和设备数据表)

-- 如果device表存在,就删除这个表
DROP TABLE IF EXISTS `device`;
-- 创建设备表
CREATE TABLE `device` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `secret` varchar(50) DEFAULT NULL COMMENT '设备秘钥',
  `iot_id` varchar(255) DEFAULT NULL COMMENT '物联网设备ID',
  `binding_location` varchar(50) DEFAULT NULL COMMENT '绑定位置',
  `location_type` int(11) DEFAULT NULL COMMENT '位置类型 0:随身设备 1:固定设备',
  `physical_location_type` int(11) DEFAULT NULL COMMENT '物理位置类型 0楼层 1房间 2床位',
  `device_name` varchar(100) DEFAULT NULL COMMENT '设备名称',
  `nickname` varchar(100) DEFAULT NULL COMMENT '备注名称',
  `product_key` varchar(100) DEFAULT NULL COMMENT '产品key',
  `product_name` varchar(100) DEFAULT NULL COMMENT '产品名称',
  `device_description` varchar(255) DEFAULT NULL COMMENT '位置备注',
  `have_entrance_guard` int(11) NOT NULL DEFAULT '0' COMMENT '产品是否包含门禁,0:否,1:是',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `create_by` bigint(20) DEFAULT NULL COMMENT '创建人id',
  `update_by` bigint(20) DEFAULT NULL COMMENT '更新人id',
  `remark` varchar(255) DEFAULT NULL COMMENT '备注',
  `node_id` varchar(50) DEFAULT NULL COMMENT '节点id',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `binding_location_location_type_physical_location_type_product_id` (`binding_location`,`location_type`,`physical_location_type`,`product_key`) USING BTREE,
  KEY `device_id` (`iot_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=234 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;

-- 如果device_data表存在,就删除这个表
DROP TABLE IF EXISTS `device_data`;
-- 创建设备数据表

CREATE TABLE `device_data` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '告警规则ID,自增主键',
  `device_name` varchar(255) DEFAULT NULL COMMENT '设备名称',
  `iot_id` varchar(255) DEFAULT NULL COMMENT '设备ID',
  `nickname` varchar(255) DEFAULT NULL COMMENT '备注名称',
  `product_key` varchar(500) DEFAULT NULL COMMENT '所属产品的key',
  `product_name` varchar(500) DEFAULT NULL COMMENT '产品名称',
  `function_id` varchar(255) DEFAULT NULL COMMENT '功能名称',
  `access_location` varchar(255) DEFAULT NULL COMMENT '接入位置',
  `location_type` int(11) DEFAULT NULL COMMENT '位置类型 0:随身设备 1:固定设备',
  `physical_location_type` int(11) DEFAULT NULL COMMENT '物理位置类型 0楼层 1房间 2床位',
  `device_description` varchar(255) DEFAULT NULL COMMENT '位置备注',
  `data_value` varchar(50) DEFAULT NULL COMMENT '数据值',
  `alarm_time` datetime DEFAULT NULL COMMENT '数据上报时间',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `create_by` bigint(20) DEFAULT NULL COMMENT '创建人id',
  `update_by` bigint(20) DEFAULT NULL COMMENT '更新人id',
  `remark` varchar(255) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`id`) USING BTREE,
  KEY `iot_id` (`iot_id`) USING BTREE,
  KEY `function_name` (`function_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=39876 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;

2. 在pojo和mapper包中,导入实体类(设备表和设备数据表)

导入到pojo包中

@TableName(value ="device")
@Data
public class Device {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String secret; //  设备秘钥
    private String iotId;//  物联网设备ID
    private String bindingLocation;//绑定位置
    private Integer locationType;//位置类型 0:随身设备 1:固定设备
    private Integer physicalLocationType;// 物理位置类型 0楼层 1房间 2床位
    private String deviceName;//设备名称
    private String nickname;//备注名称
    private String productKey;//产品key
    private String productName;//产品名称
    private String deviceDescription;//位置备注
    private Integer haveEntranceGuard;//产品是否包含门禁,0:否,1:是
    private LocalDateTime createTime;//创建时间
    private LocalDateTime updateTime;// 更新时间
    private Long createBy;//创建人id
    private Long updateBy;//更新人id
    private String remark;// 备注
    private String nodeId;//节点id
}
@TableName(value ="device_data")
@Data
public class DeviceData {

    @TableId(type = IdType.AUTO)
    private Long id;//  主键
    private String deviceName;//设备名称
    private String iotId;//设备ID
    private String nickname;// 备注名称
    private String productKey;// 所属产品的key
    private String productName;//产品名称
    private String functionId;// 功能名称
    private String accessLocation;// 接入位置
    private Integer locationType;//位置类型 0:随身设备 1:固定设备
    private Integer physicalLocationType;//物理位置类型 0楼层 1房间 2床位
    private String deviceDescription;//位置备注
    private String dataValue;//数据值
    private LocalDateTime alarmTime;//数据上报时间
    private LocalDateTime createTime;//创建时间
    private LocalDateTime updateTime;// 更新时间
    private Long createBy;//创建人id
    private Long updateBy;//更新人id
    private String remark;//备注
}

导入到mapper包中

public interface DeviceMapper extends BaseMapper<Device> {

}

public interface DeviceDataMapper extends BaseMapper<DeviceData> {

}

3. 导入DeviceController和DeviceDataController的基础代码(列表和删除代码)

@RestController
@Slf4j
@RequestMapping("/admin/device")
@Tag(name = "管理端-设备接口")
public class DeviceController
{

    @Autowired
    private DeviceMapper deviceMapper;

    /**
     * 查询设备列表
     */
    @PostMapping("/list")
    @Operation(summary = "查询设备列表")
    public AjaxResult list(@RequestBody AdminPageDTO dto) {
        Page<Device> page = new Page<>(dto.getPageNum(), dto.getPageSize());
        QueryWrapper<Device> wrapper = new QueryWrapper<>();
        // 模糊匹配 name id_card_no phone
        wrapper.like(ObjectUtil.isNotEmpty(dto.getSearchKey()), "device_name", dto.getSearchKey())
                .or()
                .like(ObjectUtil.isNotEmpty(dto.getSearchKey()), "nickname", dto.getSearchKey())
                .or()
                .like(ObjectUtil.isNotEmpty(dto.getSearchKey()), "product_key", dto.getSearchKey())
         .or()
                .like(ObjectUtil.isNotEmpty(dto.getSearchKey()), "product_name", dto.getSearchKey())
          .or()
                .like(ObjectUtil.isNotEmpty(dto.getSearchKey()), "device_description", dto.getSearchKey())
           .or()
                .like(ObjectUtil.isNotEmpty(dto.getSearchKey()), "iot_id", dto.getSearchKey());
        wrapper.orderByDesc("create_time");
        Page<Device> result = deviceMapper.selectPage(page, wrapper);
        return AjaxResult.success(result);
    }
}

设备数据类


@RestController
@RequestMapping("/admin/devicedata")
@Tag(name = "管理端-设备数据的接口")
public class DeviceDataController
{
    @Autowired
    private DeviceDataMapper deviceDataMapper;

    /**
     * 查询设备数据列表
     */
    @Operation(summary = "查询设备数据列表")
    @PostMapping("/list")
    public AjaxResult list(@RequestBody AdminPageDTO dto)
    {
        Page<DeviceData> page = new Page<>(dto.getPageNum(), dto.getPageSize());
        QueryWrapper<DeviceData> wrapper = new QueryWrapper<>();
        // 模糊匹配 name id_card_no phone
        wrapper.like(ObjectUtil.isNotEmpty(dto.getSearchKey()), "device_name", dto.getSearchKey())
                .or()
                .like(ObjectUtil.isNotEmpty(dto.getSearchKey()), "nickname", dto.getSearchKey())
                .or()
                .like(ObjectUtil.isNotEmpty(dto.getSearchKey()), "product_key", dto.getSearchKey())
                .or()
                .like(ObjectUtil.isNotEmpty(dto.getSearchKey()), "product_name", dto.getSearchKey())
                .or()
                .like(ObjectUtil.isNotEmpty(dto.getSearchKey()), "device_description", dto.getSearchKey())
                .or()
                .like(ObjectUtil.isNotEmpty(dto.getSearchKey()), "iot_id", dto.getSearchKey());
        wrapper.orderByDesc("create_time");
        Page<DeviceData> result = deviceDataMapper.selectPage(page, wrapper);
        return AjaxResult.success(result);
    }



  /**
   * 获取设备数据详细信息
   */
  @Operation(summary = "获取设备数据详细信息")
  @GetMapping(value = "/{iot_id}")
  public AjaxResult getInfo(@Parameter(description = "设备数据ID", required = true)
          @PathVariable("iot_id") String iot_id)
  {
      QueryWrapper<DeviceData> wrapper = new QueryWrapper<>();
      wrapper.eq("iot_id", iot_id);
      wrapper.orderByDesc("create_time");
      List<DeviceData> deviceData = deviceDataMapper.selectList(wrapper);


      return  AjaxResult.success(deviceData);

  }




  /**
   * 删除设备数据
   */
@Operation(summary = "删除设备数据")
@DeleteMapping("/{iot_id}")
  public AjaxResult remove(@Parameter(description = "设备数据ID", required = true)@PathVariable String iot_id)
  {
      QueryWrapper<DeviceData> wrapper = new QueryWrapper<>();
      wrapper.eq("iot_id", iot_id);
      int rows = deviceDataMapper.delete(wrapper);
      if (rows > 0) return AjaxResult.success();
      return AjaxResult.error("删除失败");

  }
}

4.导入dto和vo数据类 记得放到对应的admin下

放到vo下的admin包中

@Data
//"设备详情响应模型")
public class DeviceDetailVo {
    @Schema(description =  "设备id")
    private Long id;
    @Schema(description =  "物联网设备id")
    private String iotId;
    @Schema(description =  "设备名称")
    private String deviceName;
    @Schema(description =  "设备标识码")
    private String nodeId;
    @Schema(description =  "设备秘钥")
    private String secret;
    @Schema(description =  "产品id")
    public String productKey;
    @Schema(description =  "产品名称")
    public String productName;
    @Schema(description =  "位置类型 0 随身设备 1固定设备")
    private Integer locationType;
    @Schema(description =  "绑定位置,如果是随身设备为老人id,如果是固定设备为位置的最后一级id")
    private Long bindingLocation;
    @Schema(description =  "接入位置")
    private String remark;
    @Schema(description =  "设备状态,ONLINE:设备在线,OFFLINE:设备离线,ABNORMAL:设备异常,INACTIVE:设备未激活,FROZEN:设备冻结")
    private String deviceStatus;
    @Schema(description =  "激活时间,格式:yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime activeTime;
    @Schema(description =  "创建时间,格式:yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime createTime;
    @Schema(description =  "创建人id")
    private Long createBy;
    @Schema(description =  "创建人名称")
    private String creator;
    @Schema(description =  "位置备注")
    private String deviceDescription;
}

放到vo下的admin包中

@Data
@Builder
//"产品信息响应模型")
public class ProductVo {
   @Schema(description = "产品的ProductKey,物联网平台产品唯一标识")
    private String productId;
    @Schema(description = "产品名称")
    private String name;
}

放到dto下的admin包中

@Data
public class DeviceDto {

    private Long id;
    @Schema(description = "备注")
    private String remark;
    @Schema(description = "备标识码,通常使用IMEI、MAC地址或Serial No作为node_id", required = true)
    private String nodeId;
    @Schema(description =  "设备id")
    public String iotId;
    @Schema(description =  "产品的id")
    public String productKey;
    @Schema(description =  "产品名称")
    private String productName;
    @Schema(description =  "位置名称回显字段")
    private String deviceDescription;
    @Schema(description =  "位置类型 0 老人 1位置")
    Integer locationType;
    @Schema(description =  "绑定位置,如床号,房间号,位置id,如果是老人,则为老人id")
    Long bindingLocation;
    @Schema(description =  "设备名称")
    String deviceName;
    @Schema(description =  "物理位置类型 0楼层 1房间 2床位")
    Integer physicalLocationType;
}

2.2. 设备接入-新增设备

前言

直接在华为云中增加设备,是存在不足的,因为设备需要和某个人绑定,所以需要后台管理系统中增加设备,并且绑定老人,这样在设备或者数据出现异常时,可以及时通知老人或者其家属或者工作人员。

流程图
流程图

1. 在application.yml中增加华为云的配置

# 华为云相关
huaweicloud:
  #IOT相关配置
  ak: HPUAQGTEUVRVRP0ZOTCR  # 鼠标悬停右上角账户名 -> 我的凭证 -> 访问秘钥
  sk: bHfv2HIpy3oNYtt3oEtpq7VTZNdWXE3ytvSdM4cz   # 鼠标悬停右上角账户名 -> 我的凭证 -> 访问秘钥
  projectId: c72110da677a49179e0f849fc4253ec4   # # 鼠标悬停右上角账户名 -> 我的凭证 -> API凭证 -> 所属区域对应的项目ID
  regionId: cn-north-4      #如果是上海一,请填写"cn-east-3";如果是北京四,请填写"cn-north-4";
  endpoint: ce67dcf391.st1.iotda-app.cn-north-4.myhuaweicloud.com # IOT控制台 -> 总览 -> 接入信息 -> 应用接入 -> HTTPS (443) -> 接入地址
  #amqp相关配置
  host: 2a2cb2fbc0.st1.iotda-app.cn-north-4.myhuaweicloud.com # IOT控制台 -> 总览 -> 接入信息 -> 应用接入 -> AMQPS (5671) -> 接入地址
  accessKey: ZK3rLCgD   # IOT控制台 -> 总览 -> 接入信息 -> 应用接入 -> AMQPS (5671) ->  拖到右侧:访问控制对应的 凭证KEY
  accessCode: Fro73nYhy6TCA3wGSx0KlJFnxRAkzuju # IOT控制台 -> 总览 -> 接入信息 -> 应用接入 -> AMQPS (5671) ->  拖到右侧:访问控制对应的 凭证CODE
  queueName: DefaultQueue #默认,无需改动

2. 增加配置类


/**
 * 创建IoTDA实例
 */
@Configuration
public class IotClientConfig {

    @Value("${huaweicloud.ak}")
    String ak;
    @Value("${huaweicloud.sk}")
    String sk;

    @Value("${huaweicloud.projectId}")
    String projectId;
    @Value("${huaweicloud.regionId}")
    String regionId;
    @Value("${huaweicloud.endpoint}")
    String endpoint;



    @Bean
    public IoTDAClient huaWeiIotInstance() {
        ICredential auth = new BasicCredentials()
                .withAk(ak)
                .withSk(sk)
                // 标准版/企业版需要使用衍生算法,基础版请删除配置"withDerivedPredicate"
                .withDerivedPredicate(BasicCredentials.DEFAULT_DERIVED_PREDICATE)
                .withProjectId(projectId);

        return IoTDAClient.newBuilder()
                .withCredential(auth)
                // 标准版/企业版:需自行创建Region对象,基础版:请使用IoTDARegion的region对象,如"withRegion(IoTDARegion.CN_NORTH_4)"
                .withRegion(new Region(regionId, endpoint))
                // .withRegion(IoTDARegion.CN_NORTH_4)
                .build();
    }
}

3. 在DeviceController方法中增加同步产品方法 接口信息如下:

请求:POST /admin/device/syncProductList
参数:无
响应:
{
    "msg": "同步成功",
    "code": 200
}

代码如下:

@Autowired
private RedisTemplate<String,String> redisTemplate;

@Autowired
private IoTDAClient client;

@PostMapping("/syncProductList")
@Operation(summary = "从物联网平台同步产品列表")
public AjaxResult syncProductList() {
    //请求参数
    ListProductsRequest listProductsRequest = new ListProductsRequest();
    //设置条数
    listProductsRequest.setLimit(50);
    //发起请求
    ListProductsResponse response = client.listProducts(listProductsRequest);
    if(response.getHttpStatusCode() != 200){
        throw new BaseException("物联网接口 - 查询产品,同步失败");
    }
    //存储到redis
    redisTemplate.opsForValue().set("iot:all_product", JSONUtil.toJsonStr(response.getProducts()));

    return AjaxResult.success("同步成功");

}
//
@GetMapping("/allProduct")
@Operation(summary = "查询所有产品列表")
public AjaxResult allProduct() {
    //从redis中查询数据
    String jsonStr = redisTemplate.opsForValue().get("iot:all_product");
    //如果数据为空,则返回一个空集合
    if(StringUtils.isEmpty(jsonStr)){
        return AjaxResult.success( Collections.emptyList());
    }
    //解析数据,并返回
    List<ProductVo> productVoList = JSONUtil.toList(jsonStr, ProductVo.class);
    return AjaxResult.success(productVoList) ;
}

重启项目,测试,观察是否可以下拉选择产品

image
image

4. 在DeviceController方法中增加新增的方法 接口信息如下:

请求:POST /admin/device/register
参数:
{
    "bindingLocation": "260",
    "locationType": 0,
    "physicalLocationType": -1,
    "deviceName": "watch_005",
    "productKey": "k1bokZ23mEk",
    "productName": "智能手表"
}
响应:
{
    "msg": "新增成功",
    "code": 200
}

代码如下:

@PostMapping("/register")
@Operation(summary = "注册设备")
public AjaxResult registerDevice(@RequestBody DeviceDto deviceDto) {


    //判断设备名称是否重复
    QueryWrapper<Device> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("device_name", deviceDto.getDeviceName());
    if(deviceMapper.selectCount(queryWrapper) > 0){
        throw new BaseException("设备名称已存在,请重新输入");
    }
    //检验设备标识码是否重复
    QueryWrapper<Device> queryWrapperNodeId = new QueryWrapper<>();
    queryWrapperNodeId.eq("node_id", deviceDto.getNodeId());
    if(deviceMapper.selectCount(queryWrapperNodeId) > 0){
        throw new BaseException("设备标识码已存在,请重新输入");
    }

    //校验同一位置是否绑定了同一类产品
    QueryWrapper<Device> condition = new QueryWrapper<>();
    condition.eq("product_key", deviceDto.getProductKey())
            .eq("location_type", deviceDto.getLocationType())
            .eq("physical_location_type", deviceDto.getPhysicalLocationType())
            .eq("binding_location", deviceDto.getBindingLocation());
    if (deviceMapper.selectCount(condition) > 0) {
        throw new BaseException("该老人/位置已绑定该产品,请重新选择");
    }

    //iot中新增设备
    AddDeviceRequest request = new AddDeviceRequest();
    AddDevice body = new AddDevice();
    body.withProductId(deviceDto.getProductKey());
    body.withDeviceName(deviceDto.getDeviceName());
    //设备标识码,使用uuid代替
    body.withNodeId(UUID.randomUUID().toString());
    request.withBody(body);
    AuthInfo authInfo = new AuthInfo();
    //秘钥
    String secret = UUID.randomUUID().toString().replaceAll("-", "");
    authInfo.withSecret(secret);
    body.setAuthInfo(authInfo);
    AddDeviceResponse response;
    try {
        response = client.addDevice(request);
    } catch (Exception e) {
        e.printStackTrace();
        throw new BaseException("物联网接口 - 注册设备,调用失败");
    }

    //设备数据保存到数据库
    //属性拷贝
    Device device = BeanUtil.toBean(deviceDto, Device.class);
    //设备id、设备绑定状态
    device.setIotId(response.getDeviceId());
    //秘钥
    device.setSecret(secret);

    //在数据库中新增设备
    deviceMapper.insert(device);

    return   AjaxResult.success("注册成功");
}
image
image