使用 JStruct 建模
本节以使用 JStruct 工具对云原生验证平台中陀螺仪功能进行微服务建模为例介绍 JStruct 的使用方法。JStruct 工具的详细使用方法可以参考 JStruct 用户手册。
建立数据模型
首先需要将陀螺仪提供的数据定义为 axis
结构体,该步骤可以使用 JStruct 工具进行建模,具体方法如下:
步骤 1:编写配置文件
JStruct 的配置文件支持使用 conf.json 或 YAML 文件的方式。
注意:
- conf.json 可以直接在已部署了 JSRE 运行环境的 SylixOS 设备上使用,也可以在 Windows 或 Linux 系统的 Node.js 的环境下使用。
- YAML 只能在安装有 yaml 解析库的 Node.js 环境下使用。
通过 conf.json 或 YAML 文件定义 axis
结构体的示例如下:
- conf_axis.json 文件示例:
{
"name": "axis",
"struct": {
"name": "axis",
"member": [
{ "key": "roll", "type": "int", "req": true },
{ "key": "pitch", "type": "int", "req": true },
{ "key": "yaw", "type": "int", "req": true }
]
}
}
- conf_axis.yaml 文件示例:
name: axis
struct:
name: axis
member:
-
key: roll
type: int
req: true
-
key: pitch
type: int
req: true
-
key: yaw
type: int
req: true
详细的字段说明以及支持的数据类型请参考 JStruct 用户手册。
步骤 2:使用 JStruct 工具
JStruct 可以在支持云原生 SDK 的 SylixOS 系统或 Node.js 环境中运行,下面分别介绍 JStruct 在两种环境的使用方法:
SylixOS 环境
在 SylixOS 环境中使用 JStruct 工具时,只能使用 conf_axis.json 配置文件。
参考 上传镜像包 将 jstruct.js、file.js、func.js 与 conf_axis.json 文件上传至 SylixOS 系统。
在命令行执行如下命令,即可得到 axis_jstruct.c 和 axis_jstruct.h 文件。
[root@sylixos:/apps]# javascript -a conf_axis.json jstruct.js
Node.js 环境
- 若使用 conf_axis.json 配置文件,则在命令行执行如下命令(以 Windows 下 Node.js 环境为例),即可得到 axis_jstruct.c 和 axis_jstruct.h 文件。
PS C:\> node jstruct.js conf_axis.json
- 若使用 conf_axis.yaml 配置文件,则在命令行执行如下命令(以 Windows 下 Node.js 环境为例),即可得到 axis_jstruct.c 和 axis_jstruct.h 文件。
PS C:\> node jstruct.js conf_axis.yaml
步骤 3:自动生成代码
JStruct 可以自动生成如下代码:
struct axis {
int roll; /* roll angle */
int pitch; /* pitch angle */
int yaw; /* yaw angle */
void *json;
};
自动生成序列化与反序列化代码:
/* Deserialize the JSON string into a structure 'axis' */
bool axis_json_parse(struct axis *, const char *, size_t);
/* Free axis_json_parse() buffer, */
/* Warning: string type member can no longer be used */
void axis_json_parse_free(struct axis *);
/* Serialize the structure 'axis' into a JSON string */
char *axis_json_stringify(const struct axis *);
/* Free axis_json_stringify() return value */
void axis_json_stringify_free(char *);
验证数据模型
通过以下范例对 JStruct 自动生成的代码进行验证。
使用 RPC 宏验证
结合 JStruct 自动生成的代码,在实现 RPC 时可以使用 VSOA_PARAM_SYNCER_RPC
宏,此宏实现了对 struct axis
结构体的读取、赋值等操作。请参考如下代码片段:
/*
* Command '/axis' routine
*/
static void command_axis (void *arg, vsoa_server_t *server, vsoa_cli_id_t cid,
vsoa_header_t *vsoa_hdr, vsoa_url_t *url,
vsoa_payload_t *payload)
{
VSOA_PARAM_SYNCER_RPC(server, cid, axis, &my_server.data.axis, vsoa_hdr,
payload, NULL, NULL);
}
使用发布订阅宏验证
结合 JStruct 自动生成的代码,在实现发布订阅时可以使用 VSOA_PARAM_SYNCER_PUBLISH
宏直接将 /axis
的资源发布出去。请参考如下代码片段:
url.url = "/axis";
url.url_len = strlen(url.url);
VSOA_PARAM_SYNCER_PUBLISH(server.server, &url, axis, &server.data.axis,
NULL, 0);
RPC 与发布订阅完整验证
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "vsoa_server.h"
#include "vsoa_syncer.h"
#include "vsoa_platform.h"
#include "axis_jstruct.h"
/* My server remote client keep alive */
#define MY_SERVER_CLI_KEEPALIVE true
/* My server password */
#define MY_SERVER_PASSWD "123456"
/* My server type */
typedef struct {
vsoa_server_t *server;
struct axis data;
} my_server_t;
/* My server */
static my_server_t my_server;
/*
* Command '/axis' routine
*/
static void command_axis (void *arg, vsoa_server_t *server, vsoa_cli_id_t cid,
vsoa_header_t *vsoa_hdr, vsoa_url_t *url,
vsoa_payload_t *payload)
{
VSOA_PARAM_SYNCER_RPC(server, cid, axis, &my_server.data, vsoa_hdr,
payload, NULL, NULL);
}
#if MY_SERVER_CLI_KEEPALIVE
/*
* Remote client connect keep alive
*/
static void on_cli (void *arg, vsoa_server_t *server, vsoa_cli_id_t id,
bool connect)
{
if (connect) {
vsoa_server_cli_keepalive(server, id, 5);
}
}
#endif /* MY_SERVER_CLI_KEEPALIVE */
/*
* main function
*/
int main (int argc, char **argv)
{
int cnt;
int max_fd;
fd_set fds;
vsoa_url_t url;
struct sockaddr_in addr;
struct timespec timeout = { 1, 0 };
my_server.data.roll = 30;
my_server.data.pitch = 40;
my_server.data.yaw = 50;
bzero(&addr, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(3002);
addr.sin_addr.s_addr = INADDR_ANY;
#ifdef VSOA_HAS_SIN_LEN
addr.sin_len = sizeof(struct sockaddr_in);
#endif
my_server.server = vsoa_server_create("{\"name\":\"axis_server\"}");
if (!my_server.server) {
fprintf(stderr, "Can not create VSOA server!\n");
return (-1);
}
#if MY_SERVER_CLI_KEEPALIVE
vsoa_server_on_cli(my_server.server, on_cli, NULL);
#endif
#ifdef MY_SERVER_PASSWD
vsoa_server_passwd(my_server.server, MY_SERVER_PASSWD);
#endif
url.url = "/axis";
url.url_len = strlen(url.url);
vsoa_server_add_listener(my_server.server, &url, command_axis, NULL);
if (!vsoa_server_start(my_server.server,
(struct sockaddr *)&addr,
sizeof(struct sockaddr_in))) {
vsoa_server_close(my_server.server);
fprintf(stderr, "Can not start VSOA server!\n");
return (-1);
}
while (1) {
FD_ZERO(&fds);
max_fd = vsoa_server_fds(my_server.server, &fds);
cnt = pselect(max_fd + 1, &fds, NULL, NULL, &timeout, NULL);
if (cnt > 0) {
vsoa_server_input_fds(my_server.server, &fds);
}
url.url = "/axis";
url.url_len = strlen(url.url);
VSOA_PARAM_SYNCER_PUBLISH(my_server.server, &url, axis,
&my_server.data, NULL, 0);
}
return (0);
}
注意
- JStruct 工具自动生成的结构体中不带有对应的初始化或构造函数。因此,在 C 语言程序开发过程中,需要对创建的结构体对象中的每一个字段进行初始化,以免使用了错误的数据。
- 特别的,对于数据类型是 char * 类型的字段,在使用过程中需要将其置空或者赋值。