2. ThingWorx实体模型与扩展点解析
ThingWorx 的建模能力来自一套层次分明的元模型。理解这套元模型,是二次开发能否“扩展得对、扩展得稳”的前提。很多初学者把 Thing 当成一张普通数据表,把 Service 当成一段脚本,结果在需要复用、继承、批量部署时处处受阻。本节从元模型出发,梳理 Thing、ThingTemplate、ThingShape、DataShape、Service 五类核心实体的职责与关系,识别真正可扩展的实体类型与扩展点,并通过一个自定义 ThingTemplate 添加服务的案例,把“扩展点”落到可执行的操作上。
一、五类核心实体的职责边界
ThingWorx 的实体不是平级罗列,而是有明确的“定义—实例—数据—行为”分工。
- ThingTemplate(事物模板):定义一类事物的能力集合,是“类”的概念。它声明该类事物应具备哪些属性、服务、事件和订阅,本身通常不承载具体业务数据。例如“设备模板”声明了温度、状态、启动、停止等能力。
- ThingShape(事物形态):可复用的能力片段,是 ThingWorx 实现“多继承式组合”的关键。一个 ThingShape 可以包含属性、服务、事件,被多个 ThingTemplate 或 Thing 引用。它解决的是“横向能力复用”,例如“可维护性形态”包含保养记录、故障上报等服务。
- Thing(事物):ThingTemplate 的实例,是真正承载数据、绑定连接、对外暴露服务的对象。一个 Thing 可以基于一个模板,同时实现多个 ThingShape。
- DataShape(数据形态):定义数据的字段结构,即“一张表有哪些列、每列什么类型”。它被服务的输入输出、属性、事件、流等引用,是数据契约的载体。
- Service(服务):可被调用的一段逻辑,是行为的最小单元。服务可以定义在 Thing、ThingTemplate、ThingShape 上,也可作为独立资源存在。
这五者的关系可以概括为:ThingTemplate 与 ThingShape 共同“声明能力”,Thing 负责“实例化并承载数据”,DataShape 约束“数据长什么样”,Service 提供“能做什么”。
二、哪些实体真正可扩展
并非所有实体都适合作为扩展点。ThingWorx 中可扩展的实体类型主要有四类:ThingTemplate、ThingShape、Thing、DataShape。Service 本身是行为载体,它既是被扩展的对象,也是扩展的入口。
选择扩展点时,可以遵循一条判断链:如果一项能力需要被多个不同模板共享,应优先做成 ThingShape;如果它定义的是某一类事物的统一能力,应扩展 ThingTemplate;如果只是某个具体实例的特殊逻辑,直接在 Thing 上新增或覆盖 Service 即可;如果涉及数据结构变化,先扩展 DataShape,再调整服务签名。这样做的目的是让扩展点落在正确的层级,避免把通用逻辑写死在单个 Thing 上,导致后续无法复用。
三、案例:为自定义 ThingTemplate 添加服务
假设我们要为“智能温控设备”建立模板 SmartThermostatTemplate,并添加一个计算目标温度偏差的服务。步骤如下。
第一步:创建 DataShape。新建 TemperatureResult,字段包括 deviceId(STRING)、currentTemp(NUMBER)、targetTemp(NUMBER)、deviation(NUMBER)、status(STRING)。这个 DataShape 将作为服务的输出契约。
第二步:创建 ThingTemplate。新建 SmartThermostatTemplate,基类型选择 GenericThing。在模板上添加属性 currentTemp、targetTemp,类型均为 NUMBER。
第三步:在模板上新增 Service。服务名 calcDeviation,输入参数 deviceId(STRING),输出使用 TemperatureResult。服务脚本如下:
// 计算当前温度与目标温度的偏差
var result = {};
result.deviceId = deviceId;
result.currentTemp = me.currentTemp;
result.targetTemp = me.targetTemp;
result.deviation = me.currentTemp - me.targetTemp;
if (result.deviation > 2) {
result.status = "偏高";
} else if (result.deviation < -2) {
result.status = "偏低";
} else {
result.status = "正常";
}
var params = {
infoTableName: "TemperatureResult",
dataShapeName: "TemperatureResult"
};
var resultTable = Resources["InfoTableFunctions"].CreateInfoTableFromDataShape(params);
resultTable.AddRow(result);
// 输出到服务结果
result = resultTable;
这段脚本的关键点有三处:一是通过 me.currentTemp 读取模板属性,说明服务定义在模板上时,属性访问以模板为上下文;二是用 CreateInfoTableFromDataShape 按 DataShape 构造返回表,保证输出结构一致;三是把结果写回 result 变量,ThingWorx 会将其作为服务输出。
第四步:实例化并验证。基于 SmartThermostatTemplate 创建 Thing Thermostat_A01,设置 currentTemp = 26、targetTemp = 22。调用 calcDeviation,传入 deviceId = "A01"。预期返回:currentTemp=26、targetTemp=22、deviation=4、status="偏高"。若返回的 deviation 为 0 或 status 为空,应检查属性是否绑定到实例、DataShape 字段名是否与脚本赋值一致。
四、扩展时的常见判断依据
在实际项目中,判断扩展点是否选对,可以观察三个信号:一是新增能力后,是否需要复制到多个模板;二是修改一个服务时,是否会影响不相关的实例;三是数据结构变化时,是否需要同时改动多处脚本。如果出现前两种情况,说明扩展点层级偏低,应上移到 ThingShape 或 ThingTemplate;如果出现第三种情况,说明 DataShape 没有被统一引用,应先把数据契约收敛到 DataShape 层。
此外,服务命名和输入输出应保持稳定。服务一旦被其他服务、混搭或外部系统调用,修改签名就会产生连锁影响。建议在模板层定义服务时,输入参数尽量少而通用,输出统一使用 DataShape 包装,这样后续扩展字段时不必改动调用方。
掌握这五类实体的职责与扩展点选择原则后,后续无论是做服务编排、混搭绑定,还是与外部系统集成,都能在正确的层级上扩展,而不是在错误的层级上反复修补。