3. 信息模型基础:类型系统、对象类型、变量类型、数据类型、自定义类型设计
好,咱们今天聊聊信息模型。说实话,这是OPC UA里最核心、也最容易被忽视的一块。很多人一上来就急着写代码、建连接,结果后面发现模型设计得一塌糊涂,改起来比重新写还痛苦。我当年就吃过这个亏。
3.1 类型系统——OPC UA的骨架
OPC UA的类型系统,说白了就是一套“造物规则”。它定义了你能造出什么样的对象、变量、方法。你想想看,如果每个设备厂商都按自己的路子来,那上层应用怎么统一访问?
类型系统分两大类:
- 元类型(Meta Type):用来定义类型的类型。比如ObjectType、VariableType、DataType。它们自己也是类型。
- 实例类型(Instance Type):实际存在的对象、变量、方法。比如一个具体的“温度传感器”对象。
我个人习惯把元类型看作“模具”,实例类型看作“产品”。模具定了,产品才能批量生产。
3.2 对象类型(ObjectType)
对象类型是最常用的。它描述了一类对象的共同特征:包含哪些变量、方法、事件。
举个例子,我要定义一个“电机”对象类型:
// 伪代码,定义MotorType
ObjectType MotorType : BaseObjectType
{
// 属性
Variable Speed : Double; // 转速
Variable Temperature : Double; // 温度
Variable Status : Boolean; // 运行状态
// 方法
Method Start(); // 启动
Method Stop(); // 停止
Method Reset(); // 复位
}
嗯,这里要注意:对象类型可以继承。比如我可以定义一个“变频电机”类型,继承自“电机”,再加一个“频率”变量。我在项目中遇到过有人把所有电机都定义成同一个类型,结果变频电机和普通电机混在一起,逻辑判断写得跟蜘蛛网似的。
3.3 变量类型(VariableType)
变量类型定义了变量的“长相”:数据类型、值范围、单位、工程意义。它和对象类型最大的区别是:变量类型没有子节点,它就是个“值容器”。
举个例子,定义一个“温度”变量类型:
// 定义TemperatureVariableType
VariableType TemperatureVariableType : BaseVariableType
{
DataType : Double; // 数据类型
EngineeringUnit : "°C"; // 工程单位
ValueRange : [-40, 150]; // 值范围
EURange : [-40, 150]; // 工程单位范围
InstrumentRange : [-50, 200]; // 仪表量程
}
为什么要定义变量类型?直接用一个Double不行吗?行,但你会丢失语义。你想想看,一个Double变量,上层应用怎么知道它是温度还是压力?有了变量类型,语义就明确了。
3.4 数据类型(DataType)
数据类型是最基础的。OPC UA内置了很多:Boolean、SByte、Int16、UInt32、Float、Double、String、DateTime……但实际项目中,这些往往不够用。
比如我要表示一个“三维坐标”:
// 自定义结构体数据类型
DataType Position3D : Structure
{
X : Double;
Y : Double;
Z : Double;
}
再比如我要表示一个“报警记录”:
// 自定义枚举数据类型
DataType AlarmSeverity : Enumeration
{
Low = 0;
Medium = 1;
High = 2;
Critical = 3;
}
自定义数据类型的好处是:结构清晰、语义明确、序列化高效。坏处是:客户端必须知道你的类型定义才能解析。所以,发布自定义类型时,一定要把类型描述(TypeDictionary)一起发布出去。
3.5 自定义类型设计——实战经验
好了,理论说完了,咱们聊聊实战。自定义类型设计,我总结了几个原则:
- 单一职责:一个类型只做一件事。别把温度、压力、流量塞到一个类型里。
- 适度抽象:别太具体,也别太抽象。比如“电机”比“西门子V20变频器”更合适。
- 考虑继承:公共属性放基类,特殊属性放子类。
- 版本控制:类型定义一旦发布,尽量不要改。非要改,就新建一个版本。
我记得有一次,一个客户要求把所有设备都定义成同一个类型,说“统一管理方便”。结果呢?温度设备有“温度”变量,压力设备也有“温度”变量,但实际没有意义。最后我不得不给他们重新设计了一套类型体系,多花了两周时间。
3.6 类型系统的实际应用
最后,咱们看一个完整的例子。假设我要设计一个“智能电表”的信息模型:
// 1. 定义数据类型
DataType MeterReading : Structure
{
Timestamp : DateTime;
Voltage : Double; // 电压
Current : Double; // 电流
Power : Double; // 功率
Energy : Double; // 电能
}
// 2. 定义变量类型
VariableType MeterReadingVariableType : BaseVariableType
{
DataType : MeterReading;
EngineeringUnit : "V/A/W/kWh";
}
// 3. 定义对象类型
ObjectType SmartMeterType : BaseObjectType
{
// 属性
Variable MeterID : String; // 电表编号
Variable MeterReading : MeterReadingVariableType; // 读数
Variable Status : Boolean; // 状态
// 方法
Method ReadMeter(); // 读取电表
Method Calibrate(); // 校准
Method Reset(); // 复位
}
这个模型清晰吗?清晰。可扩展吗?可扩展。比如我要加一个“三相电表”,继承SmartMeterType,再加两个读数变量就行了。
好了,这一章就到这里。类型系统是OPC UA的“语法”,掌握了它,你才能写出“好文章”。下一章咱们聊聊地址空间——信息模型怎么在服务器里组织起来。