物模型定义及设备如何配网

什么是物模型

物模型是物理世界的实体东西的一个抽象,是进行数字化描述后,用于数字世界的数字模型。这么说可能有点绕,更直接一点说就是,物模型是使用计算机可以理解的语言,说清楚这个产品是什么、能做什么事情,以及可以提供哪些信息。

而抽象就是要提取出产品的共同特征,形成模型。以智能灯为例,不同的灯,尽管规格不同,但它们的属性是相似,比如都有开关状态的属性,功能逻辑也相仿。我们可以将这些特征标准化,形成智能灯的物模型。反过来,物模型也规约了设备的功能。新增加的设备,如果是同一类型的,在设计、研发中,会遵循相同的功能定义,有相同的特征,实现相同的服务。比如,灯都应该有“开”和“关”两种状态。

物模型定义及设备如何配网_第1张图片

为什么要使用物模型

基于共同的抽象特征,物模型可以让应用程序不再针对一个个的产品设备,而是同一类设备采用相同的处理逻辑。这实际上是应用开发的基础。当烟感传感器的数值触发报警时,即使是不同品牌的烟感产品,应用程序也可以对数值做相同的处理和判断,否则只能分别进行数值分析。另外,物模型中,设备的功能是明确定义的,可以方便地实现场景联动。比如,光线传感器可以基于光照强度,向智能电灯发送亮度的控制命令,或者开和关的命令。

如何定义物模型

定义物模型的方法:通过属性、事件和动作这三种功能元素来定义

属性(Property):描述了产品设备运行时的某种状态,属性的特点是可读可写。也就是说,应用程序可以读取属性,也可以设置设备的属性。我们还可以看到类似的例子,比如环境监测设备的温度、湿度这两个属性等。

事件(Event):由产品设备在运行过程中产生的信息、告警和故障等,一个事件可以包含多个输出参数。事件不同于属性,事件是设备上报的,不能由应用来设置。类似的例子,还有某任务完成时的消息,环境传感器检测到污染物的告警等。

如果智能电灯在运行过程中,出现了低电压的情况,或者发生了硬件故障,那么联网的设备可以将这些信息发送出去,通知你来及时作出处理。

动作(Action)或者服务(service):设备可以被调用的能力或者方法。动作由应用下发给设备,设备可以返回结果给应用。从执行的流程看,动作还可以进一步分为同步和异步。这取决于动作是否是个耗时的操作,以及其他应用逻辑对于动作执行结果的依赖关系。

生活中关于灯的一个使用场景:第一次约会的时候,你希望灯能够烘托出浪漫的气氛,就要调节灯的颜色、亮度和色温。如果分别设置属性,将会非常繁琐,这时你会想到要为灯增加一个场景模式的功能,一个命令就可以设置到浪漫模式。

物模型定义及设备如何配网_第2张图片

作为一种模型语言,物模型的数据也有不同的数据类型。它们主要包括六种:

1、布尔型(Bool):非真即假的二值型变量。例如,开关功能只有开、关两种状态。

2、整数型(Int):可用于线性调节的整数变量。例如,电灯的亮度是一个整数范围。

3、字符串型(String):以字符串形式表达的功能点。例如,灯的位置。

4、浮点型(Float):精度为浮点型的功能点。例如,电压值的范围是 0.0 - 24.0。

5、枚举型(Enum):自定义的有限集合值。例如,灯的颜色有白色、红色、黄色等。

6、时间型(Timestamp):String 类型的 UTC 时间戳。

对于整数型、浮点型的数值,它们的单位可以是百分比、电压、米等。物模型一般是用 JSON 格式来表述模型元素。JSON 是 Web 开发中,经常使用的数据格式,相比于 XML,它更加简洁、清晰,也更轻量级。

例子:用JSON个事来定义智能电灯的物模型,智能电灯的开关属性是布尔类型,是必须有的属性。它可以通过 JSON 表述如下:

智能电灯的开关属性是布尔类型,是必须有的属性。它可以通过 JSON 表述如下:


{
      "id": "power_switch",   //属性的唯一标识
      "name": "电灯开关",      //名称
      "desc": "控制电灯开灭",   //属性的详细描述
      "required": true,       //表示此属性是否必需包含,是
      "mode": "rw",           //属性的模式,r代表读,w代表写
      "define": {             //属性的数值定义
        "type": "bool",       //数值的类型,布尔
        "mapping": {          //具体数值的含义
          "0": "关",           //0表示灯关闭
          "1": "开"            //1表示灯打开
        }
      }
    }

智能电灯的电压是需要监控的数值,当电压低时,可以上报这个事件。这个事件有一个参数,即电压值,数据类型是浮点类型。JSON 格式的描述如下:


{
      "id": "low_voltage",      //事件唯一标识
      "name": "LowVoltage",      //事件名称
      "desc": "Alert for device voltage is low",  //事件的描述
      "type": "alert",          //事件的类型,告警
      "required": false,        //表示此属性是否必需包含,否
      "params": [                //事件的参数
        {
          "id": "voltage",        //事件参数的唯一标识
          "name": "Voltage",      //事件参数的名称
          "desc": "Current voltage",  //参数的描述
          "define": {                 //参数的数值定义
            "type": "float",          //数值类型,浮点数
            "unit": "V",              //数值的单位,伏
            "step": "1",              //数值变化的步长,1
            "min": "0.0",              //数值的最小值
            "max": "24.0",             //数值的最大值
            "start": "1"                //事件的起始值
          }
        }
      ]
    }

动作的定义,和属性、事件的定义过程类似,直接将所有属性、事件和动作合并,就得到了智能电灯物模型的完整 JSON 格式:


{
  "version": "1.0",            //模型版本
  "properties": [              //属性列表
    {
      "id": "power_switch",    //电灯开关属性
      "name": "电灯开关",
      "desc": "控制电灯开灭",
      "required": true,
      "mode": "rw",
      "define": {
        "type": "bool",
        "mapping": {
          "0": "关",
          "1": "开"
        }
      }
    },
    {
      "id": "brightness",        //亮度属性
      "name": "亮度",
      "desc": "灯光亮度",
      "mode": "rw",
      "define": {
        "type": "int",
        "unit": "%",
        "step": "1",
        "min": "0",
        "max": "100",
        "start": "1"
      }
    },
    {
      "id": "color",            //电灯颜色属性
      "name": "颜色",
      "desc": "灯光颜色",
      "mode": "rw",
      "define": {
        "type": "enum",
        "mapping": {
          "0": "Red",
          "1": "Green",
          "2": "Blue"
        }
      }
    },
    {
      "id": "color_temp",        //色温属性
      "name": "色温",
      "desc": "灯光冷暖",
      "mode": "rw",
      "define": {
        "type": "int",
        "min": "0",
        "max": "100",
        "start": "0",
        "step": "10",
        "unit": "%"
      }
    }
  ],
  "events": [                        //事件列表
    {
      "id": "status_report",          //运行状态报告
      "name": "DeviceStatus",
      "desc": "Report the device status",
      "type": "info",
      "required": false,
      "params": [                      //事件参数列表
        {
          "id": "status",
          "name": "running_state",
          "desc": "Report current device running state",
          "define": {
            "type": "bool",
            "mapping": {
              "0": "normal",
              "1": "fault"
            }
          }
        },
        {
          "id": "message",
          "name": "Message",
          "desc": "Some extra message",
          "define": {
            "type": "string",
            "min": "0",
            "max": "64"
          }
        }
      ]
    },
    {
      "id": "low_voltage",            //低电压告警事件
      "name": "LowVoltage",
      "desc": "Alert for device voltage is low",
      "type": "alert",
      "required": false,
      "params": [
        {
          "id": "voltage",
          "name": "Voltage",
          "desc": "Current voltage",
          "define": {
            "type": "float",
            "unit": "V",
            "step": "1",
            "min": "0.0",
            "max": "24.0",
            "start": "1"
          }
        }
      ]
    },
    {
      "id": "hardware_fault",            //硬件错误事件
      "name": "Hardware_fault",
      "desc": "Report hardware fault",
      "type": "fault",
      "required": false,
      "params": [
        {
          "id": "name",
          "name": "Name",
          "desc": "Name like: memory,tf card, censors ...",
          "define": {
            "type": "string",
            "min": "0",
            "max": "64"
          }
        },
        {
          "id": "error_code",
          "name": "Error_Code",
          "desc": "Error code for fault",
          "define": {
            "type": "int",
            "unit": "",
            "step": "1",
            "min": "0",
            "max": "2000",
            "start": "1"
          }
        }
      ]
    }
  ],
  "actions": [],                  //动作列表
  "profile": {                    //产品参数
    "ProductId": "8D1GQLE4VA",    //产品ID
    "CategoryId": "141"            //产品分类编号
  }
}

 物模型的定义方法

创建模型的时候,有拷贝和继承两种模式,这两种创建模式的不同主要体现在模型关系上。“拷贝”模式类似于编程语言中的值拷贝,新建模型与被拷贝模型有完全相同的三元素,两个模型相互独立,模型变更互不影响。“继承”模式就是面向对象编程中的继承概念,新建模型被定义为“子模型”,被继承的模型定义为“父模型”。

继承的具体特征是:

1、子模型继承父模型的所有要素,且继承的元素无法被修改。

2、子模型可以再被继承,支持多层的继承关系。

3、子模型可以创建独立的要素,但子模型中新增的要素不可以和所有上级父模型中的元素重名。

4、当父模型中的元素发生变更时,子模型中继承自父模型的元素同步变更,保持与父模型一致。以我们刚刚定义的智能电灯的物模型为例,如果要增加安装位置的属性,可以继承已有的模型,然后再增加安装位置的属性。(注意:下面的 JSON 表述省略了与父模型重复的内容。)


{
...
{
      "id": "name",                  //灯位置属性
      "name": "灯位置名称",
      "desc": "灯位置名称:书房、客厅等",
      "mode": "rw",
      "required": false,
      "define": {
        "type": "string",
        "min": "0",
        "max": "64"
      }
    }
...
}

物模型的拓展概率(设备影子、数字孪生) 

影子设备

设备影子用于缓存设备状态。应用程序可以通过设备影子直接获取设备最后一次更新的属性值,而无需每次都访问设备。设备在线时,可以直接获取应用指令;设备离线后,再次上线可以主动拉取应用指令。

我们可以再想象一个场景。如果设备网络稳定,很多应用程序请求获取设备状态,设备需要根据请求响应多次,即使响应的结果是一样的。但是可能设备本身处理能力有限,其实无法负载被请求多次的情况。使用设备影子机制,设备只需要主动同步状态给设备影子一次,多个应用程序请求设备影子获取设备状态,即可获取设备最新状态,做到应用程序和设备的解耦。再比如,智能电灯的开关状态这个属性,手机 App 可以远程控制,你也可以在本地通过物理开关改变。如果网络不稳定,那么平台上存储的状态,和电灯设备的真实状态可能会不一致,导致后续操作逻辑错误。设备影子可以通过双向的同步,实现服务器端和设备端属性的一致,从而解决这个问题。

数字孪生(Digital Twin)

物模型是物理实体的数字化模型,但主要针对的是物联网中应用的开发和设备的互操作。这个模型如果更进一步,集成了物理实体的各类数据,那就是物理实体的忠实映射。同时,在物理实体的整个生命周期中,它会和实体一起进化,积累各种信息和知识,并且促进物理实体的优化。这样的模型就是物理实体的数字孪生。

零配置组网,设备如何快速发现

 Wi-Fi 设备的配网方式,比如一键配网技术(Smart Config)和设备热点配网技术。这些技术已经可以实现一定程度的自动化,让设备比较方便地连接上 Wi-Fi 热点。零配置配网方式,它试图通过已连接上 Wi-Fi 热点的设备,来实现可信任设备完全自动化的配网。

零配置配网(Zero Configuration Provisioning)和零配置组网(Zero Configuration Networking ,ZEROCONF)不同。配网只是第一步,因为物联网设备无法方便地输入信息,所以相比手机或电脑,我们引入了这个额外的操作步骤。配网成功之后,我们还需要组网,也就是让设备获得一个自己的 IP 地址,同时也知道局域网内的路由器(Router)的 IP 地址和 DNS(Domain Name System)的 IP 地址等信息。设备自己的 IP 地址是它在 TCP/IP 网络中的唯一标识;路由器可以把设备的数据包正确地转发出去;而 DNS 服务器可以帮忙解析出数据包中需要设置的目的地 IP 地址。

零配置组网就是把这些工作自动化,不需要用户手动去操作,甚至可以让这个设备与网络内的其他设备配合工作。比如,你要为自己的智能家居系统加一个投屏设备(它可以外接普通电视或者显示器,让你可以将手机或者电脑的屏幕在大屏上显示出来)。你希望这个投屏设备接入 Wi-Fi 热点之后,你就可以在手机上直接发现它,并且马上开始投屏,看自己想看的电影。

设备如何获取IP地址

零配置组网的第一步,就是自动分配 IP 地址。-DHCP (Dynamic Host Configuration Protocol,动态主机配置协议)实现的。DHCP 协议的前身是 BOOTP 协议(Bootstrap Protocol)

DHCP 使用了服务器 - 客户端的架构模型。当一个设备(你的手机)接入网络时,它自己就会作为 DHCP 客户端,请求网络地址。然后 DHCP 服务器(家里的 Wi-Fi 路由器)会从地址池中挑选一个 IP 地址,分配给这个设备。当设备不再使用这个 IP 时(你带着手机出门 / 睡觉飞行模式),DHCP 服务器会进行回收,之后再分配给其他有需要的设备(你新买的平板)使用。DHCP 服务器与设备之间的通信是通过 UDP 传输协议完成的。因为 UDP 有一个优势,那就是不需要提前建立连接关系。DHCP 服务器的端口号是 67,设备的端口号是 68,它们一般的交互过程是这样的:

物模型定义及设备如何配网_第3张图片

 Discover:设备以广播的方式发送 DHCP Discover 消息,表示需要获取 IP 地址。

Offer:DHCP 服务器收到这个消息后,会发出 DHCP Offer 消息,作为回应。消息中带有 DHCP 服务器为设备分配的 IP 地址,也会包含其自身的 IP 地址。

Request:设备收到 DHCP Offer 消息后,将会广播一条 DHCP Request 消息,正式向 DHCP 服务器请求这个 IP 地址。

ACK:DHCP 服务器收到 DHCP Request 消息后,会判断服务器 IP 是否和自己的地址一致。如果一致,马上向设备回复 DHCP ACK 消息,并指定好 IP 地址和它的租用期限。

Decline:设备收到 DHCP ACK 消息后,还会验证一下 IP 地址是否可用。如果地址冲突,就说明不可用,它会发出 DHCP Decline 消息;如果地址不冲突,就可用的,设备将会按照租期使用这个 IP 地址。

Release:当设备不使用这个 IP 地址时,设备可以通过发送 DHCP Release 消息,来释放它。这样 DHCP 服务器可以重新分配这个 IP 地址。

手机自动发现投影仪的原理(UPnP协议)

借助 DHCP 协议,当你的投屏设备接入 Wi-Fi 热点后,就可以自动获得一个自己的 IP 地址,比如“192.168.1.100”,同时还会自动得到路由器(Router)的 IP 地址,并且完成 DNS 的 IP 地址的自动设置。

UPnP 是 Universal Plug and Play 的简称,它要实现的目标就是网络设备的即插即用

UPnP 由设备寻址、设备发现、设备描述、设备控制、事件通知和基于 HTML 的描述界面六部分构成。其中设备寻址同样是基于我刚介绍过的 DHCP 实现,如果网络内没有 DHCP 服务器,UPnP 会基于自己的 AutoIP 方法指定一个 IP 地址。从整体看,UPnP 是一个多层协议构成的框架体系,每一层都以相邻的下层为基础,同时又是相邻上层的基础,直至达到应用层为止。你可以参考下面的图片。

物模型定义及设备如何配网_第4张图片

第三层(从下往上数),它基于 HTTP、HTTPU、HTTPMU 协议,属于传送协议层。传送的内容都是经过“封装”之后,存放在特定的 XML 文件中的。用于设备和服务发现的 SSDP(Simple Service Discovery Protocol,简单服务发现协议)协议就是基于 XML 传送数据的。你不要被 SSDP 协议的名字迷惑了,其实它既提供了服务发现的功能,也提供了设备发现的功能。我们可以基于 SSDP 中的 M-SEARCH 方法来查询设备,然后基于设备的响应,获得设备的服务能力的描述信息。同时设备可以通过 NOTIFY 方法向网络通知自己的服务能力。借助 UPnP 协议,你的设备就可以自动被发现和使用了。这个自动化的过程,我们通常用一个专有名词来概括,也就是零配置组网。梳理一下,零配置组网包括三个方面的技术内核: 

1、为网络设备自动分配 IP 地址,一般涉及 DHCP 协议和 AutoIP 方法;

2、自动发现和解析设备,主要是基于 SSDP 协议;

3、自动传播和发现各网络设备提供的服务,主要也是基于 SSDP 协议。

小米电视盒子的一个 UPnP 的组播包的内容。它每 3 秒就有一包,这样用户体验更好,手机或者电脑投屏时更容易发现它。


NOTIFY * HTTP/1.1
HOST: 239.255.255.250:1900
CACHE-CONTROL: max-age=66
LOCATION: http://192.168.31.188:49152/description.xml
NT: urn:schemas-upnp-org:service:ConnectionManager:1
NTS: ssdp:alive
SERVER: Linux/3.14.29, UPnP/1.0, Portable SDK for UPnP devices/1.6.13
USN: uuid:F7CA5454-3F48-4390-8009-483842e84c17::urn:schemas-upnp-org:service:ConnectionManager:1 

 它是基于 HTTP1.1 版本,准确地说,是 HTTPMU 协议;同时你可以看到组播地址,端口号是 1900。那为了实现 UPnP 协议,你可以使用哪些开源的代码实现呢?比较流行的开源库是 libupnp,小米电视盒子使用的就是这个开源库。另外还有一个选择是GUPnP 项目,它包括几个不同的子项目,比如实现 SSDP 协议的GSSDP 项目。

mDNS 和 DNS-SD除了 UPnP 协议,零配置组网还可以使用别的协议标准,比如 mDNS 和 DNS-SD 协议。说起这两个协议,你可能比较陌生,不过苹果设备的 AirDrop 功能你可能比较熟悉。AirDrop 是通过 Bonjour 服务来发现网络上的其他苹果设备的。这个 Bonjour 服务就是 mDNS 协议和 DNS-SD 协议的具体实现。mDNS (Multicast DNS)协议允许设备在本地的 DNS 名字空间,设置一个本地的域名。之后被询问的时候,它就通过 UDP 把 IP 地址广播出来,这样其它的设备就可以找到它。你可以简单地把 mDNS 理解为 DNS 的本地网络版本。比如,主机 A 是 FTP 服务,它接入网络,并开启了 mDNS 服务,就会向 mDNS 服务注册服务信息:我提供 FTP 服务,我的 IP 是 192.168.1.101,端口是 21。当主机 B 接入相同的网络时,如果它向主机 B 的 mDNS 服务请求,需要找局域网内 FTP 服务器,主机 B 的 mDNS 就会在局域网内向其他设备的 mDNS 询问。然后,它就会找到主机 A 的 IP 地址和端口号。DNS-SD(DNS Service Discovery) 协议,一般是和 mDNS 一起使用的。它使用三种 DNS 协议的记录类型来定义协议内容,三个记录分别是:PTR 记录、SRV 记录和 TXT 记录。它提供了服务发现的功能,作用类似于上面讲到的 SSDP 协议。

物模型定义及设备如何配网_第5张图片

 

你可能感兴趣的:(IoT,iot)