物模型这个概念,刚接触IoT平台开发的人往往会低估它。很多人第一次听到"物模型"三个字,第一反应是"不就是给设备定义几个字段吗",然后随手在数据库里建一张设备表,字段用JSON一塞,觉得万事大吉。等到设备类型从3种涨到30种,接入协议从MQTT扩展到Modbus、CoAP、HTTP混着来,业务方今天要查温度、明天要下发指令、后天要订阅告警,才发现当初那张"万能设备表"已经变成了一团解不开的乱麻。物模型真正解决的,不是"怎么存数据",而是"怎么让设备在系统里有一个稳定、可被理解、可被复用的身份"。这篇文章就围绕属性、事件、服务这三个物模型的核心构件,聊聊它们各自承担什么职责、为什么这样划分、以及这套抽象到底怎样决定了一个IoT系统能走多远。
1. 物模型为什么被称为设备的数字身份证
1.1 从"设备表"到"设备身份"的认知转变
大部分IoT项目起步阶段都是这样:产品经理给一张Excel,列着设备编号、设备名称、在线状态、温度、湿度、电量。开发照着建一张表,字段一一对应,接口直接读写。这个阶段没有任何问题,因为设备只有一种,数据只有几个。问题出在扩展的那一刻——当第二种设备接入,它的字段和第一种完全不同,你只能加字段或者加表;当第三种、第四种设备进来,字段数量爆炸,表结构变得谁也不敢动。
物模型的出现,本质上是把"设备长什么样"这件事从数据库表结构里抽离出来,变成一份独立的、可版本化的、可被程序读取的契约。这份契约描述的不是"某台具体设备现在温度是多少",而是"这一类设备应该具备哪些能力"。前者是数据,后者是模型。数据会变,模型相对稳定。把模型独立出来,系统才能在不改代码的前提下接入新设备类型。
打个比方,身份证不是记录你此刻在哪里、在做什么,而是定义"作为一个公民,你有哪些固定属性(姓名、性别、出生日期)、哪些可发生的事件(迁户口、办护照)、哪些可被调用的服务(验证身份、查询记录)"。物模型对设备做的事完全一样:它不关心设备此刻的状态值,它定义的是这台设备"能提供什么、能上报什么、能被要求做什么"。
1.2 属性、事件、服务三件套的分工逻辑
物模型把设备能力拆成三类,这个划分不是拍脑袋定的,而是对应了数据流动的三个方向。
属性(Property)描述设备的状态,是"读"为主的能力。温度、湿度、开关状态、电量、信号强度,这些都是属性。属性的特点是:它有一个当前值,可以被查询,部分可被设置。属性是设备在某一时刻的"快照"。
事件(Event)描述设备主动上报的、有时序意义的信息,是"通知"能力。比如设备上线、设备离线、温度超阈值告警、故障发生、固件升级完成。事件的特点是:它是一次性的、有发生时间的、通常需要被订阅和消费。事件不是状态,它不会"保持"在那里等你来读,错过了就是错过了。
服务(Service)描述设备可以被调用的能力,是"写"或"执行"为主的能力。比如重启设备、校准传感器、下发配置、启动电机、拍照。服务的特点是:它是一次调用请求,有输入参数、有执行结果、可能异步返回。
这三者的划分,恰好覆盖了设备与平台之间所有可能的交互方向:平台读设备(属性)、设备推平台(事件)、平台控设备(服务)。任何一台设备,无论多复杂,它的能力都可以被拆解到这三个篮子里。这个划分的优雅之处在于,它让平台侧的处理逻辑可以标准化——属性走状态存储和查询链路,事件走消息订阅和规则引擎链路,服务走指令下发和回调链路,三条链路互不干扰,各自优化。
1.3 没有物模型的系统会在哪里崩掉
我见过不少项目,前期为了赶进度跳过物模型,直接让设备上报原始JSON,平台侧用脚本解析。这种方案在设备种类少于5种、团队人数少于3人的时候能跑。一旦越过这个规模,问题会集中爆发在几个地方。
第一是设备接入成本失控。每接一种新设备,都要写一套解析逻辑、一套存储逻辑、一套查询接口,代码里全是if-else判断设备类型。第二是数据无法统一查询。A设备的温度存在MySQL,B设备的温度存在时序库,C设备的温度直接扔进消息队列没落库,业务方想查"所有设备的温度"根本做不到。第三是规则引擎无法通用。想做"温度超过30度就告警",结果发现每种设备的温度字段名都不一样,规则得为每种设备单独配。第四是前端展示无法复用。每接一种设备就要重新画一套界面,因为平台不知道设备有哪些属性、什么类型、什么单位。
物模型就是把这些重复劳动一次性收敛掉的工具。定义好模型之后,接入新设备只是"注册一个模型实例",查询、告警、展示全部自动适配。这就是为什么我说它是数字身份证——没有身份证,每个人都要单独登记、单独核验;有了身份证,一套流程走天下。
2. 属性设计:状态建模里最容易埋雷的地方
2.1 属性的数据类型与读写权限怎么定
属性设计的第一道坎是数据类型。物模型里常见的属性类型有整型、浮点、布尔、字符串、枚举、时间、结构体、数组。看起来简单,但选错类型后患无穷。
举个真实的例子。某项目把"设备开关状态"定义成字符串类型,取值"on"/"off"。后来业务方要加一个"未知"状态,又加了"unknown"。再后来要做状态统计,发现字符串比较效率低,还得写映射表转成布尔。如果一开始就定义成布尔类型,这些问题都不存在。类型定义的原则是:能用简单类型就不用复杂类型,能用枚举就不用字符串,能用数值就不用文本。类型越精确,平台侧能做的校验、转换、聚合就越多。
读写权限是第二个坑。属性分只读(R)、只写(W)、读写(RW)三种。只读属性是设备上报、平台查询,比如温度、电量。只写属性是平台下发、设备执行,比如目标温度设定值。读写属性两边都能操作,比如设备名称。这里最容易犯的错是把所有属性都设成读写,结果平台下发了一个设备根本不支持设置的值,设备要么忽略要么报错,状态就乱了。
提示:只写属性在物模型里其实很少见,因为"写"这个动作通常伴随执行语义,更适合用服务来表达。真正纯粹的只写属性,一般是配置类参数,比如上报周期、采样频率。
2.2 属性标识符命名:一次定错,处处返工
属性标识符(identifier)是物模型里属性的唯一键,一旦上线就极难修改,因为所有上报数据、存储记录、规则配置、前端绑定都引用了它。命名这件事,必须在设计阶段就定死规范。
我踩过的坑是:早期用中文拼音缩写,比如wd表示温度、sd表示湿度。三个月后新来的同事完全看不懂,文档也没写全,只能靠猜。后来改成英文全称temperature、humidity,清晰多了。再后来接入第三方设备,对方上报的字段是temp、hum,又得做一层映射。
命名规范我建议这样定:全小写、下划线分隔、语义完整、不带单位。比如battery_level而不是batteryLevel或battery_percent。单位信息放在属性的unit字段里,不要塞进标识符。这样做的原因是,单位可能变化(比如从摄氏度改成华氏度),但标识符不应该跟着变。
还有一点,标识符要避免和平台保留字冲突。有些平台把id、type、status、timestamp作为系统字段,属性标识符如果重名会导致数据覆盖。设计前一定要翻一遍平台的保留字列表。
2.3 属性值的单位、精度与量程约束
属性定义里,单位、精度、量程这三个元数据经常被忽略,但它们直接决定了数据能不能被正确理解和使用。
单位必须显式声明。同样是"温度",摄氏度、华氏度、开尔文差得远。平台如果不记录单位,前端展示时就没法正确显示,数据分析时也没法做单位换算。单位建议用国际标准符号,比如°C、%RH、V、A、kPa。
精度决定存储和展示。温度传感器精度是0.1度,你存成浮点数没问题;但如果精度是0.01度,存成整型再除以100,就要在模型里声明scale或precision。否则平台拿到原始值1234,不知道是1234度还是12.34度。
量程是数据校验的依据。温度属性定义量程-40到125度,那么上报值200度就应该被平台判定为异常数据,要么丢弃要么标记。没有量程约束,脏数据会直接污染存储和告警。量程还能用于前端展示,比如仪表盘的范围就是根据量程画的。
| 属性元数据 | 作用 | 缺失后果 |
|---|---|---|
| 数据类型 | 决定存储格式和校验规则 | 类型混乱,聚合查询失败 |
| 读写权限 | 决定数据流向 | 非法下发,状态不一致 |
| 单位 | 决定数值语义 | 展示错误,换算无法进行 |
| 精度/缩放 | 决定数值还原方式 | 数值放大或缩小100倍 |
| 量程 | 决定数据有效性 | 脏数据入库,误告警 |
2.4 属性上报的时机与频率控制
属性什么时候上报,是设备侧和平台侧要共同约定的事。常见策略有三种:定时上报、变化上报、按需上报。
定时上报最简单,设备每隔N秒上报一次全部属性。缺点是流量浪费,温度半小时不变也照报。变化上报是属性值变化超过阈值才报,省流量但可能丢状态——如果平台在两次上报之间查询,拿到的是旧值。按需上报是平台下发查询指令,设备才上报,适合低频查询的属性。
实际项目里通常是组合策略:关键属性定时上报保底,非关键属性变化上报,特殊属性按需查询。这里要注意的是,上报频率要和属性的业务价值匹配。温度用于实时控制,可能1秒上报一次;电量用于统计,5分钟一次足够。如果所有属性都按最高频率上报,设备流量和平台存储都会吃不消。
还有一个隐藏问题:属性上报的时间戳。设备上报时带的时间戳和平台接收时间可能不一致,网络延迟、设备时钟不准都会导致偏差。物模型里应该明确以哪个时间为准。一般建议平台侧记录接收时间作为权威时间,设备时间作为参考字段保留。
3. 事件机制:从"状态变化"到"业务信号"的桥梁
3.1 事件和属性的边界在哪里
很多人分不清事件和属性,觉得"温度超阈值"既可以是属性变化也可以是事件。这个边界如果划不清,物模型会变得混乱。
判断标准很简单:属性是"现在是什么",事件是"发生了什么"。温度当前35度,这是属性。温度在10:23:15超过了30度阈值,这是事件。属性可以被反复查询,事件只发生一次。属性没有时间维度(虽然有更新时间),事件必须带发生时间。
按这个标准,"设备上线"是事件不是属性,因为上线是一个动作,不是持续状态。虽然设备在线状态可以作为属性存在,但"上线"这个动作本身是事件。"固件升级完成"是事件,"固件版本号"是属性。"故障发生"是事件,"故障状态"是属性。
划清边界的好处是处理链路清晰。属性走状态存储,可以被覆盖、被查询、被聚合。事件走消息通道,被订阅、被消费、被触发规则。如果把事件当属性存,会丢失历史(后一次覆盖前一次);如果把属性当事件发,会产生大量无意义消息。
3.2 事件的结构:标识、类型、参数、时间
一个完整的事件定义包含四部分:事件标识符、事件类型、输出参数、发生时间。
事件标识符是事件的唯一键,命名规范和属性一致。事件类型一般分信息、告警、故障三类。信息类事件是正常业务通知,比如上线、下线、任务完成。告警类事件是需要关注但未必是错误的情况,比如温度偏高、电量偏低。故障类事件是设备异常,比如传感器失效、通信中断。分类的意义在于平台侧可以按类型做不同的处理策略——信息类可能只记录,告警类要通知,故障类要触发工单。
输出参数是事件携带的数据。比如"温度超阈值"事件,参数应该包含当前温度值、阈值、超出的持续时间。参数定义要完整,否则消费方拿到事件后还得反查设备状态,效率低且可能查到已经变化的值。
发生时间是事件的必备字段。设备侧生成事件时打时间戳,平台侧接收时也打时间戳,两个都保留。设备时间用于业务逻辑(比如判断事件顺序),平台时间用于系统逻辑(比如消息去重、延迟统计)。
3.3 事件不更新问题:一个高频踩坑场景
热词里出现了"事件不更新",这是IoT开发里非常典型的问题。现象是:设备明明上报了事件,平台侧却收不到,或者收到了但规则没触发,或者前端界面没刷新。
排查这类问题,我一般按这条链路走:
第一步,确认设备侧是否真的发出了事件。抓设备日志或抓包,看事件报文有没有出去。很多"事件不更新"其实是设备根本没发,比如事件触发条件写错了,或者事件被本地缓存了没及时发。
第二步,确认平台侧是否收到。看消息网关的接入日志,确认报文到达。如果没到,可能是网络问题、鉴权问题、Topic不匹配。
第三步,确认事件是否被正确解析。平台收到报文后要按物模型解析,如果事件标识符和模型定义对不上,解析会失败,事件被丢弃。这一步最常见的原因是设备固件里的事件标识符和物模型里定义的不一致,改了一边忘了改另一边。
第四步,确认事件是否被正确路由。解析成功后,事件要进入消息通道,被规则引擎或订阅方消费。如果路由配置错了,事件进了死信队列或者被过滤掉了。
第五步,确认消费方是否正常处理。规则引擎的SQL写错了、订阅方的回调地址挂了、前端的长连接断了,都会导致"事件不更新"的错觉。
注意:事件不更新问题里,超过一半的根因是标识符不一致或时间戳格式不对。设计阶段把这两件事定死,能省掉大量排查时间。
3.4 事件去重、乱序与丢失的处理策略
设备事件在传输过程中会遇到三个问题:重复、乱序、丢失。
重复的原因是网络重传或设备重发。处理方式是给每个事件带一个唯一ID(设备ID+事件标识+时间戳+序列号),平台侧做幂等去重。去重窗口一般设几分钟,太短会漏掉延迟重传,太长会占用内存。
乱序的原因是不同事件走不同网络路径,到达顺序和发生顺序不一致。处理方式是消费方按事件时间戳排序,而不是按接收顺序。但排序需要缓冲,缓冲多久是个权衡——缓冲越久排序越准,但延迟越大。实时性要求高的场景,可以只对同一设备的事件做局部排序。
丢失的原因是网络不可靠或设备断电。处理方式是设备侧做本地缓存,网络恢复后补发。补发的事件要带原始时间戳,平台侧按时间戳入库,不能按接收时间。对于关键事件,还可以要求设备侧收到平台确认后才删除本地缓存,实现至少一次投递。
4. 服务调用:让设备能力可被编排
4.1 同步服务与异步服务的选型
服务按返回方式分同步和异步。同步服务是平台调用后立即返回结果,比如"查询设备当前配置"。异步服务是平台调用后先返回一个任务ID,设备执行完再通过事件或回调通知结果,比如"重启设备"——重启要几十秒,不可能让调用方一直等着。
选型的依据是执行时长和结果确定性。执行能在几秒内完成且结果确定的,用同步。执行时间长、结果不确定、或者设备可能离线需要排队的,用异步。
同步服务的实现相对简单,平台下发指令,设备执行后在同一连接上返回结果。但要注意超时设置,设备可能因为负载高而响应慢,超时太短会误判失败,太长会阻塞调用方。一般设3到10秒。
异步服务的实现复杂一些,需要任务管理。平台生成任务ID,记录任务状态(待下发、已下发、执行中、成功、失败、超时),设备执行完通过事件上报结果,平台更新任务状态并通知调用方。异步服务还要处理设备离线的情况——任务可以排队,等设备上线后再下发。
4.2 服务输入输出参数的契约设计
服务的输入参数是平台下发给设备的数据,输出参数是设备返回给平台的数据。这两者的定义要像API契约一样严谨。
输入参数要明确每个参数的类型、是否必填、取值范围、默认值。比如"设置上报周期"服务,输入参数interval是整型、必填、范围10到3600秒。平台侧在调用前做校验,不合法直接拒绝,不要下发给设备。这样能减少无效通信,也能避免设备侧因为收到非法参数而异常。
输出参数要明确返回码和返回数据。返回码建议用统一规范,比如0表示成功,非0表示各类错误。错误码要在物模型里定义清楚,不要用魔法数字。返回数据要包含执行结果的关键信息,比如"设置上报周期"成功后返回实际生效的周期值,因为设备可能对输入值做了取整。
参数设计还有一个容易忽略的点:版本兼容。服务定义上线后,如果要加参数,必须保证老设备不传新参数也能正常工作。做法是新参数设为可选,设备侧对缺失参数用默认值处理。如果要删参数,先标记废弃,等所有设备都升级后再真正删除。
4.3 服务调用的超时、重试与幂等
服务调用最怕的是"调了但不知道成没成"。网络超时、设备无响应、结果丢失,都会导致这种状态。
超时是必须设置的。同步服务设调用超时,异步服务设任务超时。超时后平台侧要把任务标记为超时,并决定是否重试。
重试要谨慎。对于幂等的服务(执行多次和执行一次结果相同),比如"查询状态",可以放心重试。对于非幂等的服务,比如"累加计数",重试会导致重复执行。解决办法是给每次调用带唯一请求ID,设备侧记录已处理的请求ID,重复请求直接返回上次结果。这就是幂等设计。
幂等实现的关键是设备侧要维护一个请求ID的缓存,缓存大小和过期时间要权衡。缓存太小会漏判,太大会占内存。一般缓存最近100到1000个请求ID,过期时间设几分钟到几小时。
提示:服务调用的可靠性,设备侧的责任比平台侧更大。平台能做的是超时、重试、记录,但最终执行结果只有设备知道。所以设备固件里一定要实现请求ID去重和结果可靠上报。
4.4 服务编排:把单设备能力组合成业务动作
单个服务调用只能完成一个原子动作,但业务需求往往是组合的。比如"启动生产线"可能要依次调用多台设备的多个服务:先启动传送带,再启动机械臂,最后启动检测仪。这就是服务编排。
服务编排有两种实现方式。一种是在平台侧用规则引擎或工作流引擎编排,平台依次调用各设备服务,根据结果决定下一步。这种方式灵活,改编排不用改设备。另一种是在设备侧编排,平台下发一个组合指令,设备自己协调。这种方式延迟低,但灵活性差。
平台侧编排的关键是处理失败。如果第一步成功第二步失败,要不要回滚第一步?回滚本身也是服务调用,可能也失败。实际项目里,完全的回滚很难做到,通常是记录失败点,人工介入或者执行补偿逻辑。所以编排设计时,要尽量让步骤可重入、可补偿。
编排还要考虑并发。多台设备的服务可以并行调用的,就并行,缩短总时长。但有依赖关系的必须串行。编排引擎要能表达这种依赖关系,常见的是用DAG(有向无环图)描述。
5. 物模型如何决定系统的扩展性上限
5.1 模型复用:新设备接入从"写代码"变成"配模型"
物模型最大的价值,是把设备接入从开发工作变成配置工作。没有物模型时,接一种新设备要写解析代码、存储代码、查询接口、前端页面,一套下来少说几天。有了物模型,如果新设备的属性和事件在已有模型里有对应定义,直接复用,几分钟就能接入。
复用的前提是模型设计得足够抽象。比如"温度"这个属性,不应该定义成"某型号传感器的温度",而应该定义成通用的温度属性,带单位、量程、精度。任何设备只要有温度,就引用这个属性定义。这样属性定义就成了可复用的积木。
平台侧一般会提供标准物模型库,涵盖常见设备类型。接入新设备时,先看标准库有没有匹配的,有就直接用,没有就基于标准库扩展。这种"标准+扩展"的模式,能大幅降低接入成本。
5.2 模型版本管理:设备升级时的兼容难题
物模型不是一成不变的。设备固件升级可能新增属性、修改事件参数、增加服务。模型变了,平台侧怎么兼容老设备?
答案是模型版本管理。每个模型有版本号,设备接入时声明自己用的模型版本。平台侧同时支持多个版本,按设备声明的版本解析数据。新版本模型可以新增属性,但不能删除或修改已有属性的语义,否则老设备的数据会解析错误。
版本升级的策略一般是灰度:先让少量设备升级到新模型,验证没问题再全量。平台侧要能同时处理新旧版本的数据,存储时按版本区分或者做归一化。
这里有个坑:如果新模型修改了某个属性的单位,比如从摄氏度改成华氏度,平台侧存储的历史数据就混了两种单位。解决办法是存储时统一转成基准单位,展示时再按需转换。所以模型设计时,基准单位的选择要慎重,一旦定了就不要改。
5.3 从单设备模型到设备组模型
单设备模型描述一台设备的能力,但实际业务里经常需要按组管理设备。比如一个车间有100台同类设备,它们的模型相同,但需要按组查询、按组下发、按组统计。
设备组模型是在单设备模型之上加一层组织维度。组本身也有属性(比如组的平均温度)、事件(比如组内设备批量离线)、服务(比如组内批量重启)。组模型的定义可以引用单设备模型,也可以自定义。
组模型的难点是聚合逻辑。组的平均温度怎么算?是所有设备温度的平均,还是只算在线设备的?设备离线时组的属性怎么变?这些规则要在模型里定义清楚,否则不同人理解不一致。
5.4 物模型与规则引擎、数据平台的联动
物模型不只是接入层的概念,它贯穿整个IoT系统。规则引擎依赖物模型来配置规则——"温度超过30度"这条规则,引擎需要知道温度是哪个属性、什么类型、什么单位。数据平台依赖物模型来做存储和分析——时序库的schema、数据仓库的表结构,都可以从物模型自动生成。
这种联动的前提是物模型要作为系统的单一事实来源。设备接入、规则配置、数据存储、前端展示,所有环节都从物模型读取定义,而不是各自维护一份。这样才能保证一致性——改了物模型,所有环节自动生效。
实际落地时,物模型通常以元数据服务的形式存在,提供API供各模块查询。元数据服务要保证高可用,因为它挂了整个系统都受影响。同时要支持变更通知,模型更新时推送给订阅方,让它们刷新缓存。
| 系统模块 | 对物模型的依赖 | 模型变更时的影响 |
|---|---|---|
| 设备接入 | 解析上报数据 | 需支持新旧版本并存 |
| 规则引擎 | 配置触发条件 | 规则需重新校验 |
| 时序存储 | 生成表结构 | 需做schema迁移 |
| 前端展示 | 渲染设备面板 | 面板需适配新属性 |
| 数据分析 | 定义指标口径 | 历史数据需归一化 |
6. 落地物模型时的几个实战经验
6.1 先定标准再接入,别让设备牵着模型走
很多团队的做法是:来一种设备就定义一套模型,设备有什么字段就定义什么属性。这种做法短期快,长期乱。因为设备厂商的字段命名、数据类型、语义定义各不相同,照单全收会导致模型库越来越臃肿,同类属性有十几种定义。
正确做法是先定标准物模型,再接入设备。标准模型定义通用属性、事件、服务,接入具体设备时做映射——设备字段映射到标准属性,设备特有字段作为扩展属性。这样模型库保持精简,通用逻辑可以复用。
映射层的工作量不小,但值得。它把设备差异隔离在接入层,上层系统只看到标准模型。换设备厂商时,只改映射,上层不动。
6.2 模型评审:让业务、开发、运维坐在一起
物模型定义错了,返工成本极高。所以定义阶段一定要评审,而且评审的人要全:业务方说清楚设备要支持什么场景,开发方说清楚技术约束,运维方说清楚现场设备的实际情况。
我见过一个案例,模型里定义了"设备重启"服务,但没定义重启后的状态上报事件。结果平台调用重启后,不知道设备什么时候重启完,只能轮询查询状态,效率低还容易误判。如果评审时有运维参与,他们会指出"现场设备重启要一两分钟,必须有完成通知",这个事件就不会漏。
评审的产出是一份模型定义文档,包含所有属性、事件、服务的完整定义,以及和业务场景的对应关系。这份文档是后续开发和运维的依据。
6.3 模型文档化与开发者体验
物模型定义完之后,要让用的人能方便地查到。平台侧应该提供模型文档的自动生成,把模型定义渲染成可读的文档,包含每个属性的类型、单位、量程、读写权限,每个事件的参数、触发条件,每个服务的输入输出、超时设置。
文档还要有示例。属性上报的报文示例、事件上报的报文示例、服务调用的请求响应示例。开发者照着示例就能对接,不用反复问。
开发者体验还体现在工具上。好的平台会提供模型校验工具,开发者定义完模型后一键校验,检查命名规范、类型合法性、引用完整性。还会提供模拟器,模拟设备按模型上报数据,方便平台侧调试。
6.4 从模型反推设备固件需求
物模型不只是平台侧的事,它直接约束设备固件要实现什么。模型里定义了哪些属性,设备就要能采集和上报这些属性;定义了哪些事件,设备就要能检测和上报这些事件;定义了哪些服务,设备就要能接收和执行这些服务。
所以物模型定义要趁早,最好在设备固件开发之前就定下来。这样固件开发有明确的目标,不会做完发现平台要的字段没采集,或者采集的字段平台不需要。
固件和模型对齐的另一个好处是测试。平台侧可以按模型生成测试用例,设备侧按模型实现,双方对齐后测试效率高。如果模型没定就开发固件,测试时只能对着设备实际行为写用例,容易漏测。
物模型这件事,说到底是在给整个IoT系统立规矩。规矩立得早、立得清楚,后面接入设备、配置规则、开发应用都是顺水推舟;规矩没立或者立得含糊,后面每接一种设备都是一次重构。属性、事件、服务这三个构件看起来简单,但每一个的定义细节都会在系统规模扩大后被放大。我的经验是,在模型定义阶段多花一周,能省下后面几个月的返工。尤其是标识符命名、单位精度、事件时间戳这几件事,一旦上线就极难修改,务必在设计时就当成不可逆决策来对待。