概述
open62541中提供了自定义数据类型的功能,我们可以按照需求去定义类型。自定义数据类型这篇文章中讲述了如何去自定义数据类型以及一些参数含义,这里仅在这里做点补充,主要是验证使用多个自定义类型去构造类型看是否可行。
自定义类型中包含自定义数据类型
这里也是以open62541/examples/custom_datatype目录下的例程进行验证,其中custom_datatype.h中自定义了四种数据类型,分别是Point,Measurements,Opt和Uni。这里我们就用这四个类型去组合成一个新类型看是否能够正常使用。
custom_datatype.h中添加我们的类型,如下
........
typedef struct {
UA_String description;
Point point;
Measurements measurements;
Opt opt;
Uni uni;
} All;
static UA_DataTypeMember AllType_members[5] = {
{
UA_TYPENAME("Collection of all types") /* .memberName */
&UA_TYPES[UA_TYPES_STRING], /* .memberType */
0, /* .padding */
false, /* .isArray */
false /* .isOptional */
},
{
UA_TYPENAME("Point") /* .memberName */
&PointType, /* .memberType */
offsetof(All, point) - offsetof(All, description) - sizeof(UA_String), /* .padding */
false, /* .isArray */
false /* .isOptional */
},
{
UA_TYPENAME("Measurements") /* .memberName */
&MeasurementType, /* .memberType */
offsetof(All, measurements) - offsetof(All, point) - sizeof(Point), /* .padding */
false, /* .isArray */
false /* .isOptional */
},
{
UA_TYPENAME("Opt") /* .memberName */
&OptType, /* .memberType */
offsetof(All, opt) - offsetof(All, measurements) - sizeof(Measurements), /* .padding */
false, /* .isArray */
false /* .isOptional */
},
{
UA_TYPENAME("Uni") /* .memberName */
&UniType, /* .memberType */
offsetof(All, uni) - offsetof(All, opt) - sizeof(Opt), /* .padding */
false, /* .isArray */
false /* .isOptional */
}
};
static const UA_DataType AllType = {
UA_TYPENAME("AllType") /* .typeName */
{
1, UA_NODEIDTYPE_NUMERIC, { 8888 }
}, /* .typeId */
{ 1, UA_NODEIDTYPE_NUMERIC, { All_encoding_id } }, /* .binaryEncodingId, the numeric
identifier used on the wire (the
namespaceindex is from .typeId) */
sizeof(All), /* .memSize */
UA_DATATYPEKIND_STRUCTURE, /* .typeKind */
false, /* .pointerFree */
false, /* .overlayable (depends on endianness and
the absence of padding) */
5, /* .membersSize */
AllType_members
};
这里我们自定义了All这个类型,它包含了一个UA_String类型变量和四种自定义类型变量并且将每个成员进行了描述,这样server端就能够添加对应的数据类型。server_types_custom.c中主要是添加添加All数据类型操作,添加的代码如下
......
//设定变量类型nodeid
const UA_NodeId allTypeVariableTypeId = {
1, UA_NODEIDTYPE_NUMERIC, { 8889 } };
......
static void addAllTypeDataType(UA_Server* server)
{
UA_DataTypeAttributes attr = UA_DataTypeAttributes_default;
attr.displayName = UA_LOCALIZEDTEXT("en-US", "All Type");
UA_Server_addDataTypeNode(
server, AllType.typeId, UA_NODEID_NUMERIC(0, UA_NS0ID_STRUCTURE),
UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE), UA_QUALIFIEDNAME(1, "All Type"), attr, NULL, NULL);
}
static void
addAllTypeVariableType(UA_Server *server) {
UA_VariableTypeAttributes dattr = UA_VariableTypeAttributes_default;
dattr.description = UA_LOCALIZEDTEXT("en-US", "All Type");
dattr.displayName = UA_LOCALIZEDTEXT("en-US", "All Type");
dattr.dataType = AllType.typeId;
dattr.valueRank = UA_VALUERANK_SCALAR;
All all;
all.description = UA_STRING("all type");
//Point类型变量默认值
all.point.x = 1.0;
all.point.y = 2.0;
all.point.z = 3.0;
//Measurements类型变量默认值
memset(&all.measurements, 0, sizeof(Measurements));
all.measurements.description = UA_STRING("all type measuresment description");
all.measurements.measurementSize = 3;
all.measurements.measurement = (UA_Float *)UA_Array_new(all.measurements.measurementSize, &MeasurementType);
all.measurements.measurement[0] = (UA_Float) 19.1;
all.measurements.measurement[1] = (UA_Float) 20.2;
all.measurements.measurement[2] = (UA_Float) 19.7;
//Opt类型变量默认值
memset(&all.opt, 0, sizeof(Opt));
all.opt.a = 3;
all.opt.b = NULL;
all.opt.c = UA_Float_new();
*all.opt.c = (UA_Float) 10.10;
//Uni类型变量默认值
memset(&all.uni, 0, sizeof(Uni));
all.uni.switchField = UA_UNISWITCH_OPTIONB;
all.uni.fields.optionB = UA_STRING("optionB");
UA_Variant_setScalar(&dattr.value, &all, &AllType);
UA_Server_addVariableTypeNode(server, allTypeVariableTypeId,
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
UA_QUALIFIEDNAME(1, "all type"),
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
dattr, NULL, NULL);
}
static void
addAllTypeVariable(UA_Server *server) {
UA_VariableAttributes vattr = UA_VariableAttributes_default;
vattr.description = UA_LOCALIZEDTEXT("en-US", "all type");
vattr.displayName = UA_LOCALIZEDTEXT("en-US", "all type");
vattr.dataType = AllType.typeId;
vattr.valueRank = UA_VALUERANK_SCALAR;
vattr.accessLevel = UA_ACCESSLEVELMASK_WRITE | UA_ACCESSLEVELMASK_READ;
//UA_Variant_setScalar(&vattr.value, &mypoint, &MyPointType);
UA_Server_addVariableNode(server, UA_NODEID_STRING(1, "all type"),
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
UA_QUALIFIEDNAME(1, "all type"),
allTypeVariableTypeId, vattr, NULL, NULL);
}
......
int main(void)
{
......
types[4] = AllType;
UA_DataTypeMember *allMembers = (UA_DataTypeMember*)UA_malloc(sizeof(UA_DataTypeMember) * 5);
allMembers[0] = AllType_members[0];
allMembers[1] = AllType_members[1];
allMembers[2] = AllType_members[2];
allMembers[3] = AllType_members[3];
allMembers[4] = AllType_members[4];
types[4].members = allMembers;
......
addAllTypeDataType(server);
addAllTypeVariableType(server);
addAllTypeVariable(server);
......
}
编译运行server,使用UaExpert去进行连接可以看到我们生成all type节点,查看节点信息如下
可以看到每个参数值都对应上了。这里我们修改client_types_custom.c去进行连接server然后打印这个node内值,修改的代码如下
UA_DataType types[5];
......
types[4] = AllType;
UA_DataTypeArray customDataTypes = {NULL, 5, types};
.....
nodeId =
UA_NODEID_STRING(1, "all type");
retval = UA_Client_readValueAttribute(client, nodeId, &value);
if (retval == UA_STATUSCODE_GOOD){
All *a = (All *)value.data;
printf("********************all member********************\n");
printf("description: %.*s\n", a->description.length, a->description.data);
printf("point: x = %f, y = %f, z = %f\n", a->point.x, a->point.y, a->point.z);
printf("measurements: description: %.*s\nmeasurementSize: %d\nvlaue: %f %f %f\n",
a->measurements.description.length, a->measurements.description.data, a->measurements.measurementSize,
a->measurements.measurement[0], a->measurements.measurement[1], a->measurements.measurement[2]);
printf("Opt: a = %d, b = %p, c = %f\n", a->opt.a, a->opt.b, *(a->opt.c));
printf("Uni: switchField = %d, fields.optionB = %.*s\n", a->uni.switchField, a->uni.fields.optionB.length, a->uni.fields.optionB.data);
}
......
这里运行client端查看结果
可以看到与实际设置的值是一致的。然后这里说一下padding参数
翻译:在这个成员元素之前有多少填充?对于数组,这是size_t长度成员之前的填充。(在size_t和下面的ptr之间没有填充。)对于联合,填充包括切换字段的大小(从联合类型开始算起的偏移量)
emm,好像有点绕,我们先来看看非数组非联合类型的含义,写个测试代码
typedef struct {
UA_Float f;
UA_Int16 b;
UA_Int32 i;
}testStruct;
testStruct test;
printf("%p %p %p %p\n", &test, &test.f, &test.b, &test.i);
printf("sizeof: UA_Float = %d, UA_Int16 = %d, UA_Int32 = %d\n", sizeof(UA_Float), sizeof(UA_Int16), sizeof(UA_Int32));
printf("testStruct size = %d\n", sizeof(testStruct));
printf("padding b -> f = %d\n", offsetof(testStruct, b) - offsetof(testStruct, f) - sizeof(UA_Float));
printf("padding i -> b = %d\n", offsetof(testStruct, i) - offsetof(testStruct, b) - sizeof(UA_Int16));
我们看看输出结果
由于字节补齐的原因,这个测试结构体所占空间为12字节,其中b与f之间空间所占为4四个字节,而b是UA_Float类型本身占四个字节,所以其padding为0;i与b之间也是占了四个字节,i类型为UA_Int16实际只占了2字节,有两个字节是补上去,所以i->b之间的padding为2。
对于数组计算的是size_t长度成员前的填充
这里measurement的padding实际就是description的填充,因为是第一个所以是0。
对于联合体是从联合类型开始计算的,
即直接从union{}里面的参数开始计算。
结论
这次验证说明自定义数据类型定义包含自定义类型是OK的。