open62541自定义数据类型

概述

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的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在使用open62541写数据时,需要进行以下步骤: 1. 首先,需要在CMakeLists.txt中添加相关的依赖库和头文件路径。例如,使用`include_directories`指令包含open62541的头文件路径,使用`find_library`指令找到open62541的库文件。 2. 然后,在代码中创建一个UA_DataSource结构体实例,该结构体包含了读和写两个函数指针。 3. 对于写数据,可以使用`UA_Server_writeValue`函数来写入指定的节点的值。该函数需要传入UA_Server实例、节点ID、写入的值等参数。 4. 在写入数据之前,需要先创建一个UA_VariableAttributes结构体实例来设置节点的属性,包括数据类型、访问级别等。 5. 然后,可以使用`UA_Server_addVariableNode`函数来创建一个变量节点,并将其添加到服务器中。该函数需要传入UA_Server实例、节点ID、父节点ID、节点属性等参数。 6. 接下来,可以使用写入函数指针writeForVariable来写入数据。该函数指针需要自定义实现,用于将数据写入到指定的节点中。 这样,就可以使用open62541来写入数据了。 <span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [open62541简单的封装类](https://download.csdn.net/download/weixin_43829959/12920766)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [学习open62541 --- [29] 数据源的使用细节](https://blog.csdn.net/whahu1989/article/details/106298733)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

复杂的世界311

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值