精华内容
下载资源
问答
  • 上一节我们写了一个简单的程序,它可以把一个视频文件解码成多张图片。我们只是简单的使用的...这一节,主要分析avformat_open_input函数的具体实现。avformat_open_input函数如下:/** * Open an input stream and

    上一节我们写了一个简单的程序,它可以把一个视频文件解码成多张图片。我们只是简单的使用的ffmepg提供的api来实现这一过程的,但对api具体的实现过程却一无所知,因此,从这篇博客看是,就逐步分析这些api的内部实现原理。这一节,主要分析avformat_open_input函数的具体实现。

    avformat_open_input函数如下:

    /**
     * Open an input stream and read the header. The codecs are not opened.
     * The stream must be closed with avformat_close_input().
     *
     * @param ps Pointer to user-supplied AVFormatContext (allocated by avformat_alloc_context).
     *           May be a pointer to NULL, in which case an AVFormatContext is allocated by this
     *           function and written into ps.
     *           Note that a user-supplied AVFormatContext will be freed on failure.
     * @param url URL of the stream to open.
     * @param fmt If non-NULL, this parameter forces a specific input format.
     *            Otherwise the format is autodetected.
     * @param options  A dictionary filled with AVFormatContext and demuxer-private options.
     *                 On return this parameter will be destroyed and replaced with a dict containing
     *                 options that were not found. May be NULL.
     *
     * @return 0 on success, a negative AVERROR on failure.
     *
     * @note If you want to use custom IO, preallocate the format context and set its pb field.
     */
    int avformat_open_input(AVFormatContext **ps, const char *filename,
                            AVInputFormat *fmt, AVDictionary **options)
    {
        AVFormatContext *s = *ps;
        int i, ret = 0;
        AVDictionary *tmp = NULL;
        ID3v2ExtraMeta *id3v2_extra_meta = NULL;
    
        if (!s && !(s = avformat_alloc_context()))
            return AVERROR(ENOMEM);
        if (!s->av_class) {
            av_log(NULL, AV_LOG_ERROR, "Input context has not been properly allocated by avformat_alloc_context() and is not NULL either\n");
            return AVERROR(EINVAL);
        }
        if (fmt)
            s->iformat = fmt;
    
        if (options)
            av_dict_copy(&tmp, *options, 0);
    
        if (s->pb) // must be before any goto fail
            s->flags |= AVFMT_FLAG_CUSTOM_IO;
    
        if ((ret = av_opt_set_dict(s, &tmp)) < 0)
            goto fail;
    
        if ((ret = init_input(s, filename, &tmp)) < 0)
            goto fail;
        s->probe_score = ret;
    
        if (!s->protocol_whitelist && s->pb && s->pb->protocol_whitelist) {
            s->protocol_whitelist = av_strdup(s->pb->protocol_whitelist);
            if (!s->protocol_whitelist) {
                ret = AVERROR(ENOMEM);
                goto fail;
            }
        }
    
        if (!s->protocol_blacklist && s->pb && s->pb->protocol_blacklist) {
            s->protocol_blacklist = av_strdup(s->pb->protocol_blacklist);
            if (!s->protocol_blacklist) {
                ret = AVERROR(ENOMEM);
                goto fail;
            }
        }
    
        if (s->format_whitelist && av_match_list(s->iformat->name, s->format_whitelist, ',') <= 0) {
            av_log(s, AV_LOG_ERROR, "Format not on whitelist \'%s\'\n", s->format_whitelist);
            ret = AVERROR(EINVAL);
            goto fail;
        }
    
        avio_skip(s->pb, s->skip_initial_bytes);
    
        /* Check filename in case an image number is expected. */
        if (s->iformat->flags & AVFMT_NEEDNUMBER) {
            if (!av_filename_number_test(filename)) {
                ret = AVERROR(EINVAL);
                goto fail;
            }
        }
    
        s->duration = s->start_time = AV_NOPTS_VALUE;
        av_strlcpy(s->filename, filename ? filename : "", sizeof(s->filename));
    
        /* Allocate private data. */
        if (s->iformat->priv_data_size > 0) {
            if (!(s->priv_data = av_mallocz(s->iformat->priv_data_size))) {
                ret = AVERROR(ENOMEM);
                goto fail;
            }
            if (s->iformat->priv_class) {
                *(const AVClass **) s->priv_data = s->iformat->priv_class;
                av_opt_set_defaults(s->priv_data);
                if ((ret = av_opt_set_dict(s->priv_data, &tmp)) < 0)
                    goto fail;
            }
        }
    
        /* e.g. AVFMT_NOFILE formats will not have a AVIOContext */
        if (s->pb)
            ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta, 0);
    
        if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header)
            if ((ret = s->iformat->read_header(s)) < 0)//read header,reader mp4 box info
                goto fail;
    
        if (id3v2_extra_meta) {
            if (!strcmp(s->iformat->name, "mp3") || !strcmp(s->iformat->name, "aac") ||
                !strcmp(s->iformat->name, "tta")) {
                if ((ret = ff_id3v2_parse_apic(s, &id3v2_extra_meta)) < 0)
                    goto fail;
            } else
                av_log(s, AV_LOG_DEBUG, "demuxer does not support additional id3 data, skipping\n");
        }
        ff_id3v2_free_extra_meta(&id3v2_extra_meta);
    
        if ((ret = avformat_queue_attached_pictures(s)) < 0)
            goto fail;
    
        if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->pb && !s->internal->data_offset)
            s->internal->data_offset = avio_tell(s->pb);
    
        s->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;
    
        for (i = 0; i < s->nb_streams; i++)
            s->streams[i]->internal->orig_codec_id = s->streams[i]->codecpar->codec_id;
    
        if (options) {
            av_dict_free(options);
            *options = tmp;
        }
        *ps = s;
        return 0;
    
    fail:
        ff_id3v2_free_extra_meta(&id3v2_extra_meta);
        av_dict_free(&tmp);
        if (s->pb && !(s->flags & AVFMT_FLAG_CUSTOM_IO))
            avio_closep(&s->pb);
        avformat_free_context(s);
        *ps = NULL;
        return ret;
    }
    
    

    结合注释,我们可以知道这个函数的作用是:打开一个输入流并且读它的头部信息,编解码器不会被打开,这个方法调用结束后,一定要调用avformat_close_input()关闭流。
    使用要这个函数要注意一点:如果fmt参数非空,也就是人为的指定了一个AVInputFormat的实例,那么这个函数就不会再检测输入文件的格式了,相反,如果fmt为空,那么这个函数就会自动探测输入文件的格式等信息。
    从源码来看,这个函数主要做了四件事:
    1.分配一个AVFormatContext的实例。
    2.调用init_input函数初始化输入流的信息。这里会初始化AVInputFormat。
    3.根据上一步初始化好的AVInputFormat的类型,调用它的read_header方法,读取文件头。
    该函数不管做的事情有多么复杂,它主要还是围绕着初始化下面一些数据结构来的:
    这里写图片描述
    这三件事说起来简单,具体分析起来相当复杂,我们一件一件来看:

    第一件事情

    分配一个AVFormatContext结构体。使用的是avformat_alloc_context函数,这个函数定义如下:

    AVFormatContext *avformat_alloc_context(void)
    {
        AVFormatContext *ic;
        ic = av_malloc(sizeof(AVFormatContext));
        if (!ic) return ic;
        avformat_get_context_defaults(ic);
    
        ic->internal = av_mallocz(sizeof(*ic->internal));
        if (!ic->internal) {
            avformat_free_context(ic);
            return NULL;
        }
        ic->internal->offset = AV_NOPTS_VALUE;
        ic->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;
        ic->internal->shortest_end = AV_NOPTS_VALUE;
    
        return ic;
    }

    这个函数中调用av_malloc函数分配一块内容以后,使用avformat_get_context_defaults方法来做一些默认的初始化。之后给internal成员分配内存并做初始化工作。
    avformat_get_context_defaults函数如下,看看它做了哪些初始化工作。

    static void avformat_get_context_defaults(AVFormatContext *s)
    {
        memset(s, 0, sizeof(AVFormatContext));
    
        s->av_class = &av_format_context_class;
    
        s->io_open  = io_open_default;
        s->io_close = io_close_default;
    
        av_opt_set_defaults(s);
    }

    这里做的初始化工作有:
    1.av_class成员,指向一个av_format_context_class结构体,这个结构体如下:

    static const AVClass av_format_context_class = {
        .class_name     = "AVFormatContext",
        .item_name      = format_to_name,
        .option         = avformat_options,
        .version        = LIBAVUTIL_VERSION_INT,
        .child_next     = format_child_next,
        .child_class_next = format_child_class_next,
        .category       = AV_CLASS_CATEGORY_MUXER,
        .get_category   = get_category,
    };
    

    这个结构体的option选项赋值为avformat_options,其值如下:

    static const AVOption avformat_options[] = {
    {"avioflags", NULL, OFFSET(avio_flags), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, INT_MIN, INT_MAX, D|E, "avioflags"},
    {"direct", "reduce buffering", 0, AV_OPT_TYPE_CONST, {.i64 = AVIO_FLAG_DIRECT }, INT_MIN, INT_MAX, D|E, "avioflags"},
    {"probesize", "set probing size", OFFSET(probesize), AV_OPT_TYPE_INT64, {.i64 = 5000000 }, 32, INT64_MAX, D},
    {"formatprobesize", "number of bytes to probe file format", OFFSET(format_probesize), AV_OPT_TYPE_INT, {.i64 = PROBE_BUF_MAX}, 0, INT_MAX-1, D},
    {"packetsize", "set packet size", OFFSET(packet_size), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, INT_MAX, E},

    内容非常多,这里只列出其中一些。
    那么AVClass,AVOption有什么作用呢?
    很多的Ffmpeg中的结构体都有AVClass成员,用来描述属主结构体。AVClass有一定会有一个AVOption成员,存放这个结构体需要的一些值,以键值对的形式存放,还有帮助信息等,这些字段都是可以修改的,而且ffmpeg提供了一系列专门用于操作AVOption的函数,这些函数定义在opt.h文件中:

    int av_opt_set         (void *obj, const char *name, const char *val, int search_flags);
    const AVOption *av_opt_next(const void *obj, const AVOption *prev);
    const AVOption *av_opt_find2(void *obj, const char *name, const char *unit,
    ...

    这些函数的作用从名字上可以猜个大概,这里就不展开了。
    接下来的av_opt_set_defaults(s)函数主要就是设置这些字段的:

    void av_opt_set_defaults(void *s)
    {
        av_opt_set_defaults2(s, 0, 0);
    }
    
    void av_opt_set_defaults2(void *s, int mask, int flags)
    {
        const AVOption *opt = NULL;
        while ((opt = av_opt_next(s, opt))) {
            void *dst = ((uint8_t*)s) + opt->offset;
    
            if ((opt->flags & mask) != flags)
                continue;
    
            if (opt->flags & AV_OPT_FLAG_READONLY)
                continue;
    
            switch (opt->type) {
                case AV_OPT_TYPE_CONST:
                    /* Nothing to be done here */
                    break;
                case AV_OPT_TYPE_BOOL:
                case AV_OPT_TYPE_FLAGS:
                case AV_OPT_TYPE_INT:
                case AV_OPT_TYPE_INT64:
                case AV_OPT_TYPE_DURATION:
                case AV_OPT_TYPE_CHANNEL_LAYOUT:
                case AV_OPT_TYPE_PIXEL_FMT:
                case AV_OPT_TYPE_SAMPLE_FMT:
                    write_number(s, opt, dst, 1, 1, opt->default_val.i64);
                    break;
                case AV_OPT_TYPE_DOUBLE:
                case AV_OPT_TYPE_FLOAT: {
                    double val;
                    val = opt->default_val.dbl;
                    write_number(s, opt, dst, val, 1, 1);
                }
                break;
                case AV_OPT_TYPE_RATIONAL: {
                    AVRational val;
                    val = av_d2q(opt->default_val.dbl, INT_MAX);
                    write_number(s, opt, dst, 1, val.den, val.num);
                }
                break;
                case AV_OPT_TYPE_COLOR:
                    set_string_color(s, opt, opt->default_val.str, dst);
                    break;
                case AV_OPT_TYPE_STRING:
                    set_string(s, opt, opt->default_val.str, dst);
                    break;
                case AV_OPT_TYPE_IMAGE_SIZE:
                    set_string_image_size(s, opt, opt->default_val.str, dst);
                    break;
                case AV_OPT_TYPE_VIDEO_RATE:
                    set_string_video_rate(s, opt, opt->default_val.str, dst);
                    break;
                case AV_OPT_TYPE_BINARY:
                    set_string_binary(s, opt, opt->default_val.str, dst);
                    break;
                case AV_OPT_TYPE_DICT:
                    /* Cannot set defaults for these types */
                break;
            default:
                av_log(s, AV_LOG_DEBUG, "AVOption type %d of option %s not implemented yet\n",
                       opt->type, opt->name);
            }
        }
    }
    

    这里会遍历AVOption的每一个项,根据每一项的类型,写入一个默认的值。
    也就是说,创建AVFormatContext结构体的主要工作是初始化它的internal和AVClass以及AVClass中的AVOption成员。还有就是给它的io_open赋值为io_open_default函数,io_close 赋值为io_close_default函数。

    第二件事情

    第二件事其实就是init_input函数做的事,这个函数定义如下:

    /* Open input file and probe the format if necessary. */
    static int init_input(AVFormatContext *s, const char *filename,
                          AVDictionary **options)
    {
        int ret;
        AVProbeData pd = { filename, NULL, 0 };
        int score = AVPROBE_SCORE_RETRY;
    
        if (s->pb) {
            s->flags |= AVFMT_FLAG_CUSTOM_IO;
            if (!s->iformat)
                return av_probe_input_buffer2(s->pb, &s->iformat, filename,
                                             s, 0, s->format_probesize);
            else if (s->iformat->flags & AVFMT_NOFILE)
                av_log(s, AV_LOG_WARNING, "Custom AVIOContext makes no sense and "
                                          "will be ignored with AVFMT_NOFILE format.\n");
            return 0;
        }
    
        if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) ||
            (!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score))))
            return score;
    
        if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0)
            return ret;
    
        if (s->iformat)
            return 0;
        return av_probe_input_buffer2(s->pb, &s->iformat, filename,
                                     s, 0, s->format_probesize);
    }

    从注释上来看,其作用是打开输入文件并且探测它的格式。
    因此,这个函数做药做了两件事:
    1.打开输入文件
    2.探测输入文件的格式
    打开输入文件使用的s->io_open,io_open是一个函数指针,着我们在分析第一件事情的时候已经说过了,它的值是io_open_default,看下这个函数,这个函数定义在libavformat/options.c文件中:

    static int io_open_default(AVFormatContext *s, AVIOContext **pb,
                               const char *url, int flags, AVDictionary **options)
    {
    #if FF_API_OLD_OPEN_CALLBACKS
    FF_DISABLE_DEPRECATION_WARNINGS
        if (s->open_cb)
            return s->open_cb(s, pb, url, flags, &s->interrupt_callback, options);
    FF_ENABLE_DEPRECATION_WARNINGS
    #endif
    
        return ffio_open_whitelist(pb, url, flags, &s->interrupt_callback, options, s->protocol_whitelist, s->protocol_blacklist);
    }

    open_cb肯定为空的,我们没有设置过,因此这里会调用ffio_open_whitelist方法。
    这个函数定义在Libavformat/aviobuf.c文件中:

    int ffio_open_whitelist(AVIOContext **s, const char *filename, int flags,
                             const AVIOInterruptCB *int_cb, AVDictionary **options,
                             const char *whitelist, const char *blacklist
                            )
    {
        URLContext *h;
        int err;
    
        err = ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist, blacklist, NULL);
        if (err < 0)
            return err;
        err = ffio_fdopen(s, h);
        if (err < 0) {
            ffurl_close(h);
            return err;
        }
        return 0;
    }
    

    这里首先调用ffurl_open_whitelist方法,这个方法定义在libavformat/avio.c中:

    int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags,
                             const AVIOInterruptCB *int_cb, AVDictionary **options,
                             const char *whitelist, const char* blacklist,
                             URLContext *parent)
    {
        AVDictionary *tmp_opts = NULL;
        AVDictionaryEntry *e;
        int ret = ffurl_alloc(puc, filename, flags, int_cb);
        if (ret < 0)
            return ret;
        if (parent)
            av_opt_copy(*puc, parent);
        if (options &&
            (ret = av_opt_set_dict(*puc, options)) < 0)
            goto fail;
        if (options && (*puc)->prot->priv_data_class &&
            (ret = av_opt_set_dict((*puc)->priv_data, options)) < 0)
            goto fail;
    
        if (!options)
            options = &tmp_opts;
    
        av_assert0(!whitelist ||
                   !(e=av_dict_get(*options, "protocol_whitelist", NULL, 0)) ||
                   !strcmp(whitelist, e->value));
        av_assert0(!blacklist ||
                   !(e=av_dict_get(*options, "protocol_blacklist", NULL, 0)) ||
                   !strcmp(blacklist, e->value));
    
        if ((ret = av_dict_set(options, "protocol_whitelist", whitelist, 0)) < 0)
            goto fail;
    
        if ((ret = av_dict_set(options, "protocol_blacklist", blacklist, 0)) < 0)
            goto fail;
    
        if ((ret = av_opt_set_dict(*puc, options)) < 0)
            goto fail;
    
        ret = ffurl_connect(*puc, options);
    
        if (!ret)
            return 0;
    fail:
        ffurl_close(*puc);
        *puc = NULL;
        return ret;
    }
    

    这个函数首先会使用ffurl_alloc方法分配一个URLContext,这个函数也是定义在avio.c文件中:

    int ffurl_alloc(URLContext **puc, const char *filename, int flags,
                    const AVIOInterruptCB *int_cb)
    {
        URLProtocol *p = NULL;
    
        if (!first_protocol) {
            av_log(NULL, AV_LOG_WARNING, "No URL Protocols are registered. "
                                         "Missing call to av_register_all()?\n");
        }
    
        p = url_find_protocol(filename);
        if (p)
           return url_alloc_for_protocol(puc, p, filename, flags, int_cb);
    
        *puc = NULL;
        if (av_strstart(filename, "https:", NULL))
            av_log(NULL, AV_LOG_WARNING, "https protocol not found, recompile FFmpeg with "
                                         "openssl, gnutls,\n"
                                         "or securetransport enabled.\n");
        return AVERROR_PROTOCOL_NOT_FOUND;
    }
    

    这个函数会根据所给的文件名找到对应的协议。使用的是url_find_protocol函数,这个函数也是定义在avio.c文件:

    static struct URLProtocol *url_find_protocol(const char *filename)
    {
        URLProtocol *up = NULL;
        char proto_str[128], proto_nested[128], *ptr;
        size_t proto_len = strspn(filename, URL_SCHEME_CHARS);
        printf("proto_len=%ld\n",proto_len);
        printf("name is %s\n",filename);
        //hello.mp4
        if (filename[proto_len] != ':' &&
            (strncmp(filename, "subfile,", 8) || !strchr(filename + proto_len + 1, ':')) ||
            is_dos_path(filename))
            strcpy(proto_str, "file");
        else
            av_strlcpy(proto_str, filename,
                       FFMIN(proto_len + 1, sizeof(proto_str)));
        //proto_str = file
        printf("proto_str=%s\n",proto_str);
        if ((ptr = strchr(proto_str, ',')))
            *ptr = '\0';
        av_strlcpy(proto_nested, proto_str, sizeof(proto_nested));
        //proto_nested = proto_str = file
        printf("proto_nested=%s\n",proto_nested);
        if ((ptr = strchr(proto_nested, '+')))
            *ptr = '\0';
    
        while (up = ffurl_protocol_next(up)) {
            printf("int while:::up->name %s\n",up->name);
            if (!strcmp(proto_str, up->name))
                break;
            if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME &&
                !strcmp(proto_nested, up->name))
                break;
        }
        printf("up->name %s\n",up->name);
        return up;
    }
    

    这里为了理解程序,我加了一些打印。如果我们传入的参数是一个xxx.mp4这样的本地文件,这个函数找到的协议是file协议,这个协议定义在libavformat/file.c文件中:

    const URLProtocol ff_file_protocol = {
        .name                = "file",
        .url_open            = file_open,
        .url_read            = file_read,
        .url_write           = file_write,
        .url_seek            = file_seek,
        .url_close           = file_close,
        .url_get_file_handle = file_get_handle,
        .url_check           = file_check,
        .url_delete          = file_delete,
        .url_move            = file_move,
        .priv_data_size      = sizeof(FileContext),
        .priv_data_class     = &file_class,
        .url_open_dir        = file_open_dir,
        .url_read_dir        = file_read_dir,
        .url_close_dir       = file_close_dir,
        .default_whitelist   = "file,crypto"
    };

    可见,这个协议定义了文件的操作方法,以后,我们打开和读写视频文件都会用到这里对应的方法。现在我们有了一个URLProtocol结构体的实例,它里面定义了对文件的操作函数。找到协议以后,要使用url_alloc_for_protocol函数来创建URLContext 的实例,并且做一堆的初始化工作。这个函数也是定义在avio.c文件中:

    static int url_alloc_for_protocol(URLContext **puc, const URLProtocol *up,
                                      const char *filename, int flags,
                                      const AVIOInterruptCB *int_cb)
    {
        URLContext *uc;
        int err;
    
    #if CONFIG_NETWORK
        if (up->flags & URL_PROTOCOL_FLAG_NETWORK && !ff_network_init())
            return AVERROR(EIO);
    #endif
        if ((flags & AVIO_FLAG_READ) && !up->url_read) {
            av_log(NULL, AV_LOG_ERROR,
                   "Impossible to open the '%s' protocol for reading\n", up->name);
            return AVERROR(EIO);
        }
        if ((flags & AVIO_FLAG_WRITE) && !up->url_write) {
            av_log(NULL, AV_LOG_ERROR,
                   "Impossible to open the '%s' protocol for writing\n", up->name);
            return AVERROR(EIO);
        }
        uc = av_mallocz(sizeof(URLContext) + strlen(filename) + 1);
        if (!uc) {
            err = AVERROR(ENOMEM);
            goto fail;
        }
        uc->av_class = &ffurl_context_class;
        uc->filename = (char *)&uc[1];
        strcpy(uc->filename, filename);
        uc->prot            = up;
        uc->flags           = flags;
        uc->is_streamed     = 0; /* default = not streamed */
        uc->max_packet_size = 0; /* default: stream file */
        if (up->priv_data_size) {
            uc->priv_data = av_mallocz(up->priv_data_size);
            if (!uc->priv_data) {
                err = AVERROR(ENOMEM);
                goto fail;
            }
            if (up->priv_data_class) {
                int proto_len= strlen(up->name);
                char *start = strchr(uc->filename, ',');
                *(const AVClass **)uc->priv_data = up->priv_data_class;
                av_opt_set_defaults(uc->priv_data);
                if(!strncmp(up->name, uc->filename, proto_len) && uc->filename + proto_len == start){
                    int ret= 0;
                    char *p= start;
                    char sep= *++p;
                    char *key, *val;
                    p++;
    
                    if (strcmp(up->name, "subfile"))
                        ret = AVERROR(EINVAL);
    
                    while(ret >= 0 && (key= strchr(p, sep)) && p<key && (val = strchr(key+1, sep))){
                        *val= *key= 0;
                        if (strcmp(p, "start") && strcmp(p, "end")) {
                            ret = AVERROR_OPTION_NOT_FOUND;
                        } else
                            ret= av_opt_set(uc->priv_data, p, key+1, 0);
                        if (ret == AVERROR_OPTION_NOT_FOUND)
                            av_log(uc, AV_LOG_ERROR, "Key '%s' not found.\n", p);
                        *val= *key= sep;
                        p= val+1;
                    }
                    if(ret<0 || p!=key){
                        av_log(uc, AV_LOG_ERROR, "Error parsing options string %s\n", start);
                        av_freep(&uc->priv_data);
                        av_freep(&uc);
                        err = AVERROR(EINVAL);
                        goto fail;
                    }
                    memmove(start, key+1, strlen(key));
                }
            }
        }
        if (int_cb)
            uc->interrupt_callback = *int_cb;
    
        *puc = uc;
        return 0;
    fail:
        *puc = NULL;
        if (uc)
            av_freep(&uc->priv_data);
        av_freep(&uc);
    #if CONFIG_NETWORK
        if (up->flags & URL_PROTOCOL_FLAG_NETWORK)
            ff_network_close();
    #endif
        return err;
    }
    

    这里创建了一个URLContext的实例,并且做了很多的初始化:

        uc->av_class = &ffurl_context_class;
        uc->filename = (char *)&uc[1];
        strcpy(uc->filename, filename);
        uc->prot            = up;
        uc->flags           = flags;
        uc->is_streamed     = 0; /* default = not streamed */
        uc->max_packet_size = 0; /* default: stream file */

    结束以后,我们就有了URLContext的实例 了,当然也有URLProtocol的实例,的它保存在uc->prot里面。
    回到ffurl_open_whitelist函数,接下来,该函数会调用ffurl_connect函数,这个函数是要建立连接的意思,我们的文件是本地文件,应该会打开它。这个函数依旧定义在avio.c中:

    int ffurl_connect(URLContext *uc, AVDictionary **options)
    {
        int err;
        AVDictionary *tmp_opts = NULL;
        AVDictionaryEntry *e;
    ...
    
        err =
            uc->prot->url_open2 ? uc->prot->url_open2(uc,
                                                      uc->filename,
                                                      uc->flags,
                                                      options) :
            uc->prot->url_open(uc, uc->filename, uc->flags);
    
        av_dict_set(options, "protocol_whitelist", NULL, 0);
        av_dict_set(options, "protocol_blacklist", NULL, 0);
    
        if (err)
            return err;
        uc->is_connected = 1;
        /* We must be careful here as ffurl_seek() could be slow,
         * for example for http */
        if ((uc->flags & AVIO_FLAG_WRITE) || !strcmp(uc->prot->name, "file"))
            if (!uc->is_streamed && ffurl_seek(uc, 0, SEEK_SET) < 0)
                uc->is_streamed = 1;
        return 0;
    }

    果然,这里出现了url_open函数的调用,那么对应的就会调用file_open函数,这个函数定义在libavformat/file.c中:

    static int file_open(URLContext *h, const char *filename, int flags)
    {
        FileContext *c = h->priv_data;
        int access;
        int fd;
        struct stat st;
    
        av_strstart(filename, "file:", &filename);
    
        if (flags & AVIO_FLAG_WRITE && flags & AVIO_FLAG_READ) {
            access = O_CREAT | O_RDWR;
            if (c->trunc)
                access |= O_TRUNC;
        } else if (flags & AVIO_FLAG_WRITE) {
            access = O_CREAT | O_WRONLY;
            if (c->trunc)
                access |= O_TRUNC;
        } else {
            access = O_RDONLY;
        }
    #ifdef O_BINARY
        access |= O_BINARY;
    #endif
        fd = avpriv_open(filename, access, 0666);
        if (fd == -1)
            return AVERROR(errno);
        c->fd = fd;
    
        h->is_streamed = !fstat(fd, &st) && S_ISFIFO(st.st_mode);
    
        return 0;
    }

    这里参数检查以后,会调用avpriv_open函数,这个函数定义在libavutil/file_open.c中:

    int avpriv_open(const char *filename, int flags, ...)
    {
        int fd;
        unsigned int mode = 0;
        va_list ap;
    
        va_start(ap, flags);
        if (flags & O_CREAT)
            mode = va_arg(ap, unsigned int);
        va_end(ap);
    
    #ifdef O_CLOEXEC
        flags |= O_CLOEXEC;
    #endif
    #ifdef O_NOINHERIT
        flags |= O_NOINHERIT;
    #endif
    
        fd = open(filename, flags, mode);
    #if HAVE_FCNTL
        if (fd != -1) {
            if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
                av_log(NULL, AV_LOG_DEBUG, "Failed to set close on exec\n");
        }
    #endif
    
        return fd;
    }

    可以看到这里回归到了open这样的系统调用函数了,调用open打开文件后返回文件描述符,这个函数的任务就完成了。这个函数返回到file_open函数后,就把得到的文件描述符赋值给FileContext结构体的fd成员:
    c->fd = fd;
    之后函数继续返回,函数返回到avio.c中的ffio_open_whitelist函数,接下来调用ffio_fdopen函数,这个函数定义在libavformat下的aviobuf.c中:

    int ffio_fdopen(AVIOContext **s, URLContext *h)
    {
        AVIOInternal *internal = NULL;
        uint8_t *buffer = NULL;
        int buffer_size, max_packet_size;
    
        max_packet_size = h->max_packet_size;
        if (max_packet_size) {
            buffer_size = max_packet_size; /* no need to bufferize more than one packet */
        } else {
            buffer_size = IO_BUFFER_SIZE;
        }
        buffer = av_malloc(buffer_size);
        if (!buffer)
            return AVERROR(ENOMEM);
    
        internal = av_mallocz(sizeof(*internal));
        if (!internal)
            goto fail;
    
        internal->h = h;
    
        *s = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE,
                                internal, io_read_packet, io_write_packet, io_seek);
        if (!*s)
            goto fail;
    
        (*s)->protocol_whitelist = av_strdup(h->protocol_whitelist);
        if (!(*s)->protocol_whitelist && h->protocol_whitelist) {
            avio_closep(s);
            goto fail;
        }
        (*s)->protocol_blacklist = av_strdup(h->protocol_blacklist);
        if (!(*s)->protocol_blacklist && h->protocol_blacklist) {
            avio_closep(s);
            goto fail;
        }
        (*s)->direct = h->flags & AVIO_FLAG_DIRECT;
    
        (*s)->seekable = h->is_streamed ? 0 : AVIO_SEEKABLE_NORMAL;
        (*s)->max_packet_size = max_packet_size;
        if(h->prot) {
            (*s)->read_pause = io_read_pause;
            (*s)->read_seek  = io_read_seek;
        }
        (*s)->av_class = &ff_avio_class;
        return 0;
    fail:
        av_freep(&internal);
        av_freep(&buffer);
        return AVERROR(ENOMEM);
    }
    

    这个函数还是做了很多的事情的:
    1.分配一快buffer内存,其大小有这里定义:#define IO_BUFFER_SIZE 32768
    buffer的大小本来取决于URLContext的max_packet_size,前面在url_alloc_for_protocol函数中初始化URLContext的时候将其初始化为0了,因此,这里分配的大小为32768。
    2.分配一个AVIOContext结构体,这个结构体也很重要,分配完以后我们就有AVIOContext结构体的实例了,分配完成后做了一些初始化工作。

        (*s)->direct = h->flags & AVIO_FLAG_DIRECT;
        (*s)->seekable = h->is_streamed ? 0 : AVIO_SEEKABLE_NORMAL;
        (*s)->max_packet_size = max_packet_size;
        (*s)->av_class = &ff_avio_class;

    这要注意seekable 是AVIO_SEEKABLE_NORMAL,因为is_streamed 为0,这在url_alloc_for_protocol函数中初始化URLContext的时候将其初始化。
    这两件事情做完后函数一路返回,又来到了init_input函数中,是不是都快忘记了,这里就是本文分析的avformat_open_input函数做的第二件事情了。

    init_input函数接下来调用av_probe_input_buffer2函数,探测输入的buffer,用来确定文件的格式。这里会创建给长重要的AVInputFormat结构体的实例。这个函数定义在libavformat/format.c中:

    int av_probe_input_buffer2(AVIOContext *pb, AVInputFormat **fmt,
                              const char *filename, void *logctx,
                              unsigned int offset, unsigned int max_probe_size)
    {
        AVProbeData pd = { filename ? filename : "" };
        uint8_t *buf = NULL;
        int ret = 0, probe_size, buf_offset = 0;
        int score = 0;
        int ret2;
    
        if (!max_probe_size)
            max_probe_size = PROBE_BUF_MAX;
        else if (max_probe_size < PROBE_BUF_MIN) {
            av_log(logctx, AV_LOG_ERROR,
                   "Specified probe size value %u cannot be < %u\n", max_probe_size, PROBE_BUF_MIN);
            return AVERROR(EINVAL);
        }
    
        if (offset >= max_probe_size)
            return AVERROR(EINVAL);
    
        if (pb->av_class) {
            uint8_t *mime_type_opt = NULL;
            av_opt_get(pb, "mime_type", AV_OPT_SEARCH_CHILDREN, &mime_type_opt);
            pd.mime_type = (const char *)mime_type_opt;
        }
    #if 0
        if (!*fmt && pb->av_class && av_opt_get(pb, "mime_type", AV_OPT_SEARCH_CHILDREN, &mime_type) >= 0 && mime_type) {
            if (!av_strcasecmp(mime_type, "audio/aacp")) {
                *fmt = av_find_input_format("aac");
            }
            av_freep(&mime_type);
        }
    #endif
    
        for (probe_size = PROBE_BUF_MIN; probe_size <= max_probe_size && !*fmt;
             probe_size = FFMIN(probe_size << 1,
                                FFMAX(max_probe_size, probe_size + 1))) {
            score = probe_size < max_probe_size ? AVPROBE_SCORE_RETRY : 0;
    
            /* Read probe data. */
            if ((ret = av_reallocp(&buf, probe_size + AVPROBE_PADDING_SIZE)) < 0)
                goto fail;
            if ((ret = avio_read(pb, buf + buf_offset,
                                 probe_size - buf_offset)) < 0) {
                /* Fail if error was not end of file, otherwise, lower score. */
                if (ret != AVERROR_EOF)
                    goto fail;
    
                score = 0;
                ret   = 0;          /* error was end of file, nothing read */
            }
            buf_offset += ret;
            if (buf_offset < offset)
                continue;
            pd.buf_size = buf_offset - offset;
            pd.buf = &buf[offset];
    
            memset(pd.buf + pd.buf_size, 0, AVPROBE_PADDING_SIZE);
    
            /* Guess file format. */
            *fmt = av_probe_input_format2(&pd, 1, &score);
            if (*fmt) {
                /* This can only be true in the last iteration. */
                if (score <= AVPROBE_SCORE_RETRY) {
                    av_log(logctx, AV_LOG_WARNING,
                           "Format %s detected only with low score of %d, "
                           "misdetection possible!\n", (*fmt)->name, score);
                } else
                    av_log(logctx, AV_LOG_DEBUG,
                           "Format %s probed with size=%d and score=%d\n",
                           (*fmt)->name, probe_size, score);
    #if 0
                FILE *f = fopen("probestat.tmp", "ab");
                fprintf(f, "probe_size:%d format:%s score:%d filename:%s\n", probe_size, (*fmt)->name, score, filename);
                fclose(f);
    #endif
            }
        }
    
        if (!*fmt)
            ret = AVERROR_INVALIDDATA;
    
    fail:
        /* Rewind. Reuse probe buffer to avoid seeking. */
        ret2 = ffio_rewind_with_probe_data(pb, &buf, buf_offset);
        if (ret >= 0)
            ret = ret2;
    
        av_freep(&pd.mime_type);
        return ret < 0 ? ret : score;
    }
    

    这个函数做的事情就是探测文件的格式。它会不断的读文件,然后分析,知道确定了文件的格式位置。
    这个函数的for循环每次读PROBE_BUF_MIN个字节的内容,其实就是2048了,然后调用av_probe_input_format2函数来猜测文件的格式:
    这个函数也定义在format.c文件中:

    AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score_max)
    {
        int score_ret;
        AVInputFormat *fmt = av_probe_input_format3(pd, is_opened, &score_ret);
        if (score_ret > *score_max) {
            *score_max = score_ret;
            return fmt;
        } else
            return NULL;
    }

    这里调用av_probe_input_format3继续处理:
    该函数还是在format.c中:

    AVInputFormat *av_probe_input_format3(AVProbeData *pd, int is_opened,
                                          int *score_ret)
    {
        AVProbeData lpd = *pd;
        AVInputFormat *fmt1 = NULL, *fmt;
        int score, nodat = 0, score_max = 0;
        const static uint8_t zerobuffer[AVPROBE_PADDING_SIZE];
    
        if (!lpd.buf)
            lpd.buf = zerobuffer;
    
        if (lpd.buf_size > 10 && ff_id3v2_match(lpd.buf, ID3v2_DEFAULT_MAGIC)) {
            int id3len = ff_id3v2_tag_len(lpd.buf);
            if (lpd.buf_size > id3len + 16) {
                lpd.buf      += id3len;
                lpd.buf_size -= id3len;
            } else if (id3len >= PROBE_BUF_MAX) {
                nodat = 2;
            } else
                nodat = 1;
        }
    
        fmt = NULL;
        while ((fmt1 = av_iformat_next(fmt1))) {
            if (!is_opened == !(fmt1->flags & AVFMT_NOFILE) && strcmp(fmt1->name, "image2"))
                continue;
            score = 0;
            if (fmt1->read_probe) {
                score = fmt1->read_probe(&lpd);
                if (score)
                    av_log(NULL, AV_LOG_TRACE, "Probing %s score:%d size:%d\n", fmt1->name, score, lpd.buf_size);
                if (fmt1->extensions && av_match_ext(lpd.filename, fmt1->extensions)) {
                    if      (nodat == 0) score = FFMAX(score, 1);
                    else if (nodat == 1) score = FFMAX(score, AVPROBE_SCORE_EXTENSION / 2 - 1);
                    else                 score = FFMAX(score, AVPROBE_SCORE_EXTENSION);
                }
            } else if (fmt1->extensions) {
                if (av_match_ext(lpd.filename, fmt1->extensions))
                    score = AVPROBE_SCORE_EXTENSION;
            }
            if (av_match_name(lpd.mime_type, fmt1->mime_type))
                score = FFMAX(score, AVPROBE_SCORE_MIME);
            if (score > score_max) {
                score_max = score;
                fmt       = fmt1;
            } else if (score == score_max)
                fmt = NULL;
        }
        if (nodat == 1)
            score_max = FFMIN(AVPROBE_SCORE_EXTENSION / 2 - 1, score_max);
        *score_ret = score_max;
        av_log(NULL, AV_LOG_TRACE, "format----- %s score:%d size:%d\n", fmt->name, score, lpd.buf_size);
        return fmt;
    }
    

    这个函数会根据文件名的后缀,遍历AVInputFormat的全局链表,比如我们输入的xxx.mp4,那么匹配成功的必然是ff_mov_demuxer了,这个AVInputFormat 实例定义如下:
    mp4文件格式解复用器:

    AVInputFormat ff_mov_demuxer = {
        .name           = "mov,mp4,m4a,3gp,3g2,mj2",
        .long_name      = NULL_IF_CONFIG_SMALL("QuickTime / MOV"),
        .priv_class     = &mov_class,
        .priv_data_size = sizeof(MOVContext),
        .extensions     = "mov,mp4,m4a,3gp,3g2,mj2",
        .read_probe     = mov_probe,
        .read_header    = mov_read_header,
        .read_packet    = mov_read_packet,
        .read_close     = mov_read_close,
        .read_seek      = mov_read_seek,
        .flags          = AVFMT_NO_BYTE_SEEK,
    };
    

    找到对应的AVInputFormat 的实例后,我们的AVFormatContext中的iformat成员就有内容了。至此,我们已经知道了文件的格式了,接下来需要做第三件事情了。
    我们简单回顾下。第二件事情做完后,我们的AVFormatContext中的一下成员就被初始化了:

    const AVClass *av_class;
    struct AVInputFormat *iformat;
    void *priv_data;
    AVIOContext *pb;

    第三件事情

    第三件事情就是调用AVInputFormat的read_header方法来解析文件了。我们找到的AVInputFormat的是ff_mov_demuxer ,它的.read_header 初始化为 mov_read_header,这个函数定义在libavformat/mov.c文件中:

    static int mov_read_header(AVFormatContext *s)
    {
        MOVContext *mov = s->priv_data;
        AVIOContext *pb = s->pb;
        int j, err;
        MOVAtom atom = { AV_RL32("root") };//AV)RL32把字符串组合成int类型
        int i;
    
        if (mov->decryption_key_len != 0 && mov->decryption_key_len != AES_CTR_KEY_SIZE) {
            av_log(s, AV_LOG_ERROR, "Invalid decryption key len %d expected %d\n",
                mov->decryption_key_len, AES_CTR_KEY_SIZE);
            return AVERROR(EINVAL);
        }
    
        mov->fc = s;
        mov->trak_index = -1;
        /* .mov and .mp4 aren't streamable anyway (only progressive download if moov is before mdat) */
        if (pb->seekable)
            atom.size = avio_size(pb);
        else
            atom.size = INT64_MAX;
    
        /* check MOV header */
        do {
        if (mov->moov_retry)
            avio_seek(pb, 0, SEEK_SET);
        if ((err = mov_read_default(mov, pb, atom)) < 0) {
            av_log(s, AV_LOG_ERROR, "error reading header\n");
            mov_read_close(s);
            return err;
        }
        } while (pb->seekable && !mov->found_moov && !mov->moov_retry++);
        if (!mov->found_moov) {
            av_log(s, AV_LOG_ERROR, "moov atom not found\n");
            mov_read_close(s);
            return AVERROR_INVALIDDATA;
        }
        av_log(mov->fc, AV_LOG_TRACE, "on_parse_exit_offset=%"PRId64"\n", avio_tell(pb));
    
        if (pb->seekable) {
            if (mov->nb_chapter_tracks > 0 && !mov->ignore_chapters)
                mov_read_chapters(s);
            for (i = 0; i < s->nb_streams; i++)
                if (s->streams[i]->codecpar->codec_tag == AV_RL32("tmcd")) {
                    mov_read_timecode_track(s, s->streams[i]);
                } else if (s->streams[i]->codecpar->codec_tag == AV_RL32("rtmd")) {
                    mov_read_rtmd_track(s, s->streams[i]);
                }
        }
    
        /* copy timecode metadata from tmcd tracks to the related video streams */
        for (i = 0; i < s->nb_streams; i++) {
            AVStream *st = s->streams[i];
            MOVStreamContext *sc = st->priv_data;
            if (sc->timecode_track > 0) {
                AVDictionaryEntry *tcr;
                int tmcd_st_id = -1;
    
                for (j = 0; j < s->nb_streams; j++)
                    if (s->streams[j]->id == sc->timecode_track)
                        tmcd_st_id = j;
    
                if (tmcd_st_id < 0 || tmcd_st_id == i)
                    continue;
                tcr = av_dict_get(s->streams[tmcd_st_id]->metadata, "timecode", NULL, 0);
                if (tcr)
                    av_dict_set(&st->metadata, "timecode", tcr->value, 0);
            }
        }
        export_orphan_timecode(s);
    
        for (i = 0; i < s->nb_streams; i++) {
            AVStream *st = s->streams[i];
            MOVStreamContext *sc = st->priv_data;
            fix_timescale(mov, sc);
            if(st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->codecpar->codec_id == AV_CODEC_ID_AAC) {
                st->skip_samples = sc->start_pad;
            }
            if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && sc->nb_frames_for_fps > 0 && sc->duration_for_fps > 0)
                av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den,
                          sc->time_scale*(int64_t)sc->nb_frames_for_fps, sc->duration_for_fps, INT_MAX);
            if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
                if (st->codecpar->width <= 0 || st->codecpar->height <= 0) {
                    st->codecpar->width  = sc->width;
                    st->codecpar->height = sc->height;
                }
                if (st->codecpar->codec_id == AV_CODEC_ID_DVD_SUBTITLE) {
                    if ((err = mov_rewrite_dvd_sub_extradata(st)) < 0)
                        return err;
                }
            }
            if (mov->handbrake_version &&
                mov->handbrake_version <= 1000000*0 + 1000*10 + 2 &&  // 0.10.2
                st->codecpar->codec_id == AV_CODEC_ID_MP3
            ) {
                av_log(s, AV_LOG_VERBOSE, "Forcing full parsing for mp3 stream\n");
                st->need_parsing = AVSTREAM_PARSE_FULL;
            }
        }
    
        if (mov->trex_data) {
            for (i = 0; i < s->nb_streams; i++) {
                AVStream *st = s->streams[i];
                MOVStreamContext *sc = st->priv_data;
                if (st->duration > 0)
                    st->codecpar->bit_rate = sc->data_size * 8 * sc->time_scale / st->duration;
            }
        }
    
        if (mov->use_mfra_for > 0) {
            for (i = 0; i < s->nb_streams; i++) {
                AVStream *st = s->streams[i];
                MOVStreamContext *sc = st->priv_data;
                if (sc->duration_for_fps > 0) {
                    st->codecpar->bit_rate = sc->data_size * 8 * sc->time_scale /
                        sc->duration_for_fps;
                }
            }
        }
    
        for (i = 0; i < mov->bitrates_count && i < s->nb_streams; i++) {
            if (mov->bitrates[i]) {
                s->streams[i]->codecpar->bit_rate = mov->bitrates[i];
            }
        }
    
        ff_rfps_calculate(s);
    
        for (i = 0; i < s->nb_streams; i++) {
            AVStream *st = s->streams[i];
            MOVStreamContext *sc = st->priv_data;
    
            switch (st->codecpar->codec_type) {
            case AVMEDIA_TYPE_AUDIO:
                err = ff_replaygain_export(st, s->metadata);
                if (err < 0) {
                    mov_read_close(s);
                    return err;
                }
                break;
            case AVMEDIA_TYPE_VIDEO:
                if (sc->display_matrix) {
                    AVPacketSideData *sd, *tmp;
    
                    tmp = av_realloc_array(st->side_data,
                                           st->nb_side_data + 1, sizeof(*tmp));
                    if (!tmp)
                        return AVERROR(ENOMEM);
    
                    st->side_data = tmp;
                    st->nb_side_data++;
    
                    sd = &st->side_data[st->nb_side_data - 1];
                    sd->type = AV_PKT_DATA_DISPLAYMATRIX;
                    sd->size = sizeof(int32_t) * 9;
                    sd->data = (uint8_t*)sc->display_matrix;
                    sc->display_matrix = NULL;
                }
                break;
            }
        }
        ff_configure_buffers_for_index(s, AV_TIME_BASE);
    
        return 0;
    }
    

    要看懂这个函数的话,的先对mp4文件的格式有所了解(我这里以mp4文件为例)。这里就不展开分析Mp4文件的格式了,请大家自行了解。
    mp4文件由一系列的box组成,如下图:
    这里写图片描述

    从图中可以看出来这些box是有层级关系的。每一个box它的头部的8个字节是固定的,前四个字节是这个box的大小,后四个字节是这个box的类型,也就是途中的fytp,moov之类的。这个信息在ffmpeg中使用MOVAtom来标示:
    

    typedef struct MOVAtom {
    uint32_t type;
    int64_t size; /* total size (excluding the size and type fields) */
    } MOVAtom;

    这个函数的一开始,就够早了一个叫做root的MOVAtom ,并从它开始,遍历这些box,怎么遍历的呢?调用mov_read_default方法,这个方法中,又会递归的调用mov_read_default方法来解析它的子box,一次来推,最终解析所有的box。
    接下来我们看看mov_read_default方法;
    
    static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom)
    {
        int64_t total_size = 0;
        MOVAtom a;
        int i;
        char mmmss[5];
    
        if (c->atom_depth > 10) {
            av_log(c->fc, AV_LOG_ERROR, "Atoms too deeply nested\n");
            return AVERROR_INVALIDDATA;
        }
        c->atom_depth ++;
        printf("mov_read_default\n");
        printf("c->atom_depth=%d\n",c->atom_depth);
    
        if (atom.size < 0)
            atom.size = INT64_MAX;
        printf("total_size=%ld\n",total_size);
        printf("atom.size=%ld\n",atom.size);
        printf("avio_feof(pb)=%d\n",avio_feof(pb));
        while (total_size + 8 <= atom.size && !avio_feof(pb)) {
            int (*parse)(MOVContext*, AVIOContext*, MOVAtom) = NULL;
            a.size = atom.size;
            a.type=0;
            if (atom.size >= 8) {
                a.size = avio_rb32(pb);
                a.type = avio_rl32(pb);
                if (a.type == MKTAG('f','r','e','e') &&
                    a.size >= 8 &&
                    c->moov_retry) {
                    uint8_t buf[8];
                    uint32_t *type = (uint32_t *)buf + 1;
                    if (avio_read(pb, buf, 8) != 8)
                        return AVERROR_INVALIDDATA;
                    avio_seek(pb, -8, SEEK_CUR);
                    if (*type == MKTAG('m','v','h','d') ||
                        *type == MKTAG('c','m','o','v')) {
                        av_log(c->fc, AV_LOG_ERROR, "Detected moov in a free atom.\n");
                        a.type = MKTAG('m','o','o','v');
                    }
                }
                if (atom.type != MKTAG('r','o','o','t') &&
                    atom.type != MKTAG('m','o','o','v'))
                {
                    if (a.type == MKTAG('t','r','a','k') || a.type == MKTAG('m','d','a','t'))
                    {
                        av_log(c->fc, AV_LOG_ERROR, "Broken file, trak/mdat not at top-level\n");
                        avio_skip(pb, -8);
                        c->atom_depth --;
                        return 0;
                    }
                }
                total_size += 8;
                if (a.size == 1 && total_size + 8 <= atom.size) { /* 64 bit extended size */
                    a.size = avio_rb64(pb) - 8;
                    total_size += 8;
                }
            }
            av_log(c->fc, AV_LOG_TRACE, "type: %08x '%.4s' parent:'%.4s' sz: %"PRId64" %"PRId64" %"PRId64"\n",
                    a.type, (char*)&a.type, (char*)&atom.type, a.size, total_size, atom.size);
            if (a.size == 0) {
                a.size = atom.size - total_size + 8;
            }
            a.size -= 8;
            if (a.size < 0)
                break;
            a.size = FFMIN(a.size, atom.size - total_size);
    
            for (i = 0; mov_default_parse_table[i].type; i++)
                if (mov_default_parse_table[i].type == a.type) {
                    parse = mov_default_parse_table[i].parse;
                    break;
                }
    
            // container is user data
            if (!parse && (atom.type == MKTAG('u','d','t','a') ||
                           atom.type == MKTAG('i','l','s','t')))
                parse = mov_read_udta_string;
    
            if (!parse) { /* skip leaf atoms data */
                avio_skip(pb, a.size);
            } else {
                int64_t start_pos = avio_tell(pb);
                int64_t left;
                int err = parse(c, pb, a);
                if (err < 0) {
                    c->atom_depth --;
                    return err;
                }
                if (c->found_moov && c->found_mdat &&
                    ((!pb->seekable || c->fc->flags & AVFMT_FLAG_IGNIDX) ||
                     start_pos + a.size == avio_size(pb))) {
                    if (!pb->seekable || c->fc->flags & AVFMT_FLAG_IGNIDX)
                        c->next_root_atom = start_pos + a.size;
                    c->atom_depth --;
                    return 0;
                }
                left = a.size - avio_tell(pb) + start_pos;
                if (left > 0) /* skip garbage at atom end */
                    avio_skip(pb, left);
                else if (left < 0) {
                    av_log(c->fc, AV_LOG_WARNING,
                           "overread end of atom '%.4s' by %"PRId64" bytes\n",
                           (char*)&a.type, -left);
                    avio_seek(pb, left, SEEK_CUR);
                }
            }
    
            total_size += a.size;
         mmmss[4]='\0';
         printf("-------------while----------");
         printf("total_size=%ld\n",total_size);
         printf("a.size=%ld\n",a.size);
         AV_WL32(mmmss,a.type);
         printf("a.type=%s\n",mmmss);
        }
    

    为了更好的理解,我加了写打印。这个函数中,没找到一个box,读出它的类型以后,就会调用对应的parse方法。配型与parse方法通过一下数组来对应:

    static const MOVParseTableEntry mov_default_parse_table[] = {
    { MKTAG('A','C','L','R'), mov_read_aclr },
    { MKTAG('A','P','R','G'), mov_read_avid },
    { MKTAG('A','A','L','P'), mov_read_avid },
    { MKTAG('A','R','E','S'), mov_read_ares },
    { MKTAG('a','v','s','s'), mov_read_avss },
    { MKTAG('c','h','p','l'), mov_read_chpl },
    { MKTAG('c','o','6','4'), mov_read_stco },
    { MKTAG('c','o','l','r'), mov_read_colr },
    { MKTAG('c','t','t','s'), mov_read_ctts }, /* composition time to sample */
    { MKTAG('d','i','n','f'), mov_read_default },
    { MKTAG('D','p','x','E'), mov_read_dpxe },
    { MKTAG('d','r','e','f'), mov_read_dref },
    { MKTAG('e','d','t','s'), mov_read_default },
    { MKTAG('e','l','s','t'), mov_read_elst },
    { MKTAG('e','n','d','a'), mov_read_enda },
    { MKTAG('f','i','e','l'), mov_read_fiel },
    { MKTAG('f','t','y','p'), mov_read_ftyp },
    { MKTAG('g','l','b','l'), mov_read_glbl },
    { MKTAG('h','d','l','r'), mov_read_hdlr },
    { MKTAG('i','l','s','t'), mov_read_ilst },
    { MKTAG('j','p','2','h'), mov_read_jp2h },
    { MKTAG('m','d','a','t'), mov_read_mdat },
    { MKTAG('m','d','h','d'), mov_read_mdhd },
    { MKTAG('m','d','i','a'), mov_read_default },
    { MKTAG('m','e','t','a'), mov_read_meta },
    { MKTAG('m','i','n','f'), mov_read_default },
    { MKTAG('m','o','o','f'), mov_read_moof },
    { MKTAG('m','o','o','v'), mov_read_moov },
    { MKTAG('m','v','e','x'), mov_read_default },
    { MKTAG('m','v','h','d'), mov_read_mvhd },
    { MKTAG('S','M','I',' '), mov_read_svq3 },
    { MKTAG('a','l','a','c'), mov_read_alac }, /* alac specific atom */
    { MKTAG('a','v','c','C'), mov_read_glbl },
    { MKTAG('p','a','s','p'), mov_read_pasp },
    { MKTAG('s','t','b','l'), mov_read_default },
    { MKTAG('s','t','c','o'), mov_read_stco },
    { MKTAG('s','t','p','s'), mov_read_stps },
    { MKTAG('s','t','r','f'), mov_read_strf },
    { MKTAG('s','t','s','c'), mov_read_stsc },
    { MKTAG('s','t','s','d'), mov_read_stsd }, /* sample description */
    { MKTAG('s','t','s','s'), mov_read_stss }, /* sync sample */
    { MKTAG('s','t','s','z'), mov_read_stsz }, /* sample size */
    { MKTAG('s','t','t','s'), mov_read_stts },
    { MKTAG('s','t','z','2'), mov_read_stsz }, /* compact sample size */
    { MKTAG('t','k','h','d'), mov_read_tkhd }, /* track header */
    { MKTAG('t','f','d','t'), mov_read_tfdt },
    { MKTAG('t','f','h','d'), mov_read_tfhd }, /* track fragment header */
    { MKTAG('t','r','a','k'), mov_read_trak },
    { MKTAG('t','r','a','f'), mov_read_default },
    { MKTAG('t','r','e','f'), mov_read_default },
    { MKTAG('t','m','c','d'), mov_read_tmcd },
    { MKTAG('c','h','a','p'), mov_read_chap },
    { MKTAG('t','r','e','x'), mov_read_trex },
    { MKTAG('t','r','u','n'), mov_read_trun },
    { MKTAG('u','d','t','a'), mov_read_default },
    { MKTAG('w','a','v','e'), mov_read_wave },
    { MKTAG('e','s','d','s'), mov_read_esds },
    { MKTAG('d','a','c','3'), mov_read_dac3 }, /* AC-3 info */
    { MKTAG('d','e','c','3'), mov_read_dec3 }, /* EAC-3 info */
    { MKTAG('w','i','d','e'), mov_read_wide }, /* place holder */
    { MKTAG('w','f','e','x'), mov_read_wfex },
    { MKTAG('c','m','o','v'), mov_read_cmov },
    { MKTAG('c','h','a','n'), mov_read_chan }, /* channel layout */
    { MKTAG('d','v','c','1'), mov_read_dvc1 },
    { MKTAG('s','b','g','p'), mov_read_sbgp },
    { MKTAG('h','v','c','C'), mov_read_glbl },
    { MKTAG('u','u','i','d'), mov_read_uuid },
    { MKTAG('C','i','n', 0x8e), mov_read_targa_y216 },
    { MKTAG('f','r','e','e'), mov_read_free },
    { MKTAG('-','-','-','-'), mov_read_custom },
    { 0, NULL }
    };

    以trak为例,其解析方法如下:

    static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom)
    {
        AVStream *st;
        MOVStreamContext *sc;
        int ret;
    
        st = avformat_new_stream(c->fc, NULL);
        if (!st) return AVERROR(ENOMEM);
        st->id = c->fc->nb_streams;
        sc = av_mallocz(sizeof(MOVStreamContext));
        if (!sc) return AVERROR(ENOMEM);
    
        st->priv_data = sc;
        st->codec->codec_type = AVMEDIA_TYPE_DATA;
        sc->ffindex = st->index;
    
        if ((ret = mov_read_default(c, pb, atom)) < 0)
            return ret;
    
        /* sanity checks */
        if (sc->chunk_count && (!sc->stts_count || !sc->stsc_count ||
                                (!sc->sample_size && !sc->sample_count))) {
            av_log(c->fc, AV_LOG_ERROR, "stream %d, missing mandatory atoms, broken header\n",
                   st->index);
            return 0;
        }
    
        fix_timescale(c, sc);
    
        avpriv_set_pts_info(st, 64, 1, sc->time_scale);
    
        mov_build_index(c, st);
    
        if (sc->dref_id-1 < sc->drefs_count && sc->drefs[sc->dref_id-1].path) {
            MOVDref *dref = &sc->drefs[sc->dref_id - 1];
            if (c->enable_drefs) {
                if (mov_open_dref(c, &sc->pb, c->fc->filename, dref,
                                  &c->fc->interrupt_callback) < 0)
                    av_log(c->fc, AV_LOG_ERROR,
                           "stream %d, error opening alias: path='%s', dir='%s', "
                           "filename='%s', volume='%s', nlvl_from=%d, nlvl_to=%d\n",
                           st->index, dref->path, dref->dir, dref->filename,
                           dref->volume, dref->nlvl_from, dref->nlvl_to);
            } else {
                av_log(c->fc, AV_LOG_WARNING,
                       "Skipped opening external track: "
                       "stream %d, alias: path='%s', dir='%s', "
                       "filename='%s', volume='%s', nlvl_from=%d, nlvl_to=%d."
                       "Set enable_drefs to allow this.\n",
                       st->index, dref->path, dref->dir, dref->filename,
                       dref->volume, dref->nlvl_from, dref->nlvl_to);
            }
        } else {
            sc->pb = c->fc->pb;
            sc->pb_is_copied = 1;
        }
    
        if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            if (!st->sample_aspect_ratio.num && st->codec->width && st->codec->height &&
                sc->height && sc->width &&
                (st->codec->width != sc->width || st->codec->height != sc->height)) {
                st->sample_aspect_ratio = av_d2q(((double)st->codec->height * sc->width) /
                                                 ((double)st->codec->width * sc->height), INT_MAX);
            }
    
    #if FF_API_R_FRAME_RATE
            if (sc->stts_count == 1 || (sc->stts_count == 2 && sc->stts_data[1].count == 1))
                av_reduce(&st->r_frame_rate.num, &st->r_frame_rate.den,
                          sc->time_scale, sc->stts_data[0].duration, INT_MAX);
    #endif
        }
    
        // done for ai5q, ai52, ai55, ai1q, ai12 and ai15.
        if (!st->codec->extradata_size && st->codec->codec_id == AV_CODEC_ID_H264 &&
            TAG_IS_AVCI(st->codec->codec_tag)) {
            ret = ff_generate_avci_extradata(st);
            if (ret < 0)
                return ret;
        }
    
        switch (st->codec->codec_id) {
    #if CONFIG_H261_DECODER
        case AV_CODEC_ID_H261:
    #endif
    #if CONFIG_H263_DECODER
        case AV_CODEC_ID_H263:
    #endif
    #if CONFIG_MPEG4_DECODER
        case AV_CODEC_ID_MPEG4:
    #endif
            st->codec->width = 0; /* let decoder init width/height */
            st->codec->height= 0;
            break;
        }
    
        /* Do not need those anymore. */
        av_freep(&sc->chunk_offsets);
        av_freep(&sc->stsc_data);
        av_freep(&sc->sample_sizes);
        av_freep(&sc->keyframes);
        av_freep(&sc->stts_data);
        av_freep(&sc->stps_data);
        av_freep(&sc->elst_data);
        av_freep(&sc->rap_group);
    
        return 0;
    }

    一开始,创建一个新流:

    AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c)
    {
        AVStream *st;
        int i;
        AVStream **streams;
    
        if (s->nb_streams >= INT_MAX/sizeof(*streams))
            return NULL;
        streams = av_realloc_array(s->streams, s->nb_streams + 1, sizeof(*streams));
        if (!streams)
            return NULL;
        s->streams = streams;
    
        st = av_mallocz(sizeof(AVStream));
        if (!st)
            return NULL;
        if (!(st->info = av_mallocz(sizeof(*st->info)))) {
            av_free(st);
            return NULL;
        }
        st->info->last_dts = AV_NOPTS_VALUE;
    
    #if FF_API_LAVF_AVCTX
    FF_DISABLE_DEPRECATION_WARNINGS
        st->codec = avcodec_alloc_context3(c);
        if (!st->codec) {
            av_free(st->info);
            av_free(st);
            return NULL;
        }
    FF_ENABLE_DEPRECATION_WARNINGS
    #endif
    
        st->internal = av_mallocz(sizeof(*st->internal));
        if (!st->internal)
            goto fail;
    
        st->codecpar = avcodec_parameters_alloc();
        if (!st->codecpar)
            goto fail;
    
        st->internal->avctx = avcodec_alloc_context3(NULL);
        if (!st->internal->avctx)
            goto fail;
    
        if (s->iformat) {
    #if FF_API_LAVF_AVCTX
    FF_DISABLE_DEPRECATION_WARNINGS
            /* no default bitrate if decoding */
            st->codec->bit_rate = 0;
    FF_ENABLE_DEPRECATION_WARNINGS
    #endif
    
            /* default pts setting is MPEG-like */
            avpriv_set_pts_info(st, 33, 1, 90000);
            /* we set the current DTS to 0 so that formats without any timestamps
             * but durations get some timestamps, formats with some unknown
             * timestamps have their first few packets buffered and the
             * timestamps corrected before they are returned to the user */
            st->cur_dts = RELATIVE_TS_BASE;
        } else {
            st->cur_dts = AV_NOPTS_VALUE;
        }
    
        st->index      = s->nb_streams;
        st->start_time = AV_NOPTS_VALUE;
        st->duration   = AV_NOPTS_VALUE;
        st->first_dts     = AV_NOPTS_VALUE;
        st->probe_packets = MAX_PROBE_PACKETS;
        st->pts_wrap_reference = AV_NOPTS_VALUE;
        st->pts_wrap_behavior = AV_PTS_WRAP_IGNORE;
    
        st->last_IP_pts = AV_NOPTS_VALUE;
        st->last_dts_for_order_check = AV_NOPTS_VALUE;
        for (i = 0; i < MAX_REORDER_DELAY + 1; i++)
            st->pts_buffer[i] = AV_NOPTS_VALUE;
    
        st->sample_aspect_ratio = (AVRational) { 0, 1 };
    
    #if FF_API_R_FRAME_RATE
        st->info->last_dts      = AV_NOPTS_VALUE;
    #endif
        st->info->fps_first_dts = AV_NOPTS_VALUE;
        st->info->fps_last_dts  = AV_NOPTS_VALUE;
    
        st->inject_global_side_data = s->internal->inject_global_side_data;
    
        st->internal->need_context_update = 1;
    
        s->streams[s->nb_streams++] = st;
        return st;
    fail:
        free_stream(&st);
        return NULL;
    }
    
    
    1. 最后将创建好的新流添加到AVFormatContext中的streams成员,添加到最后一项,并且让nb_streams成员自增。
      2.递归解析下一级的box
      通过这种解析,可以看到我们获取到了流的信息。
      trak包含了很多的子box,这些box描述了trak的功能。比如我们要获取这个流是视频流呢还是音频流呢?亦或者是不是字幕流呢就要通过解析hdlr这个子box来获取:
        if ((ret = mov_read_default(c, pb, atom)) < 0)
            return ret;

    区分音频流还是视频流:

    static int mov_read_hdlr(MOVContext *c, AVIOContext *pb, MOVAtom atom)
    {
        AVStream *st;
        uint32_t type;
        uint32_t av_unused ctype;
        int64_t title_size;
        char *title_str;
        int ret;
    
        if (c->fc->nb_streams < 1) // meta before first trak
            return 0;
    
        st = c->fc->streams[c->fc->nb_streams-1];
    
        avio_r8(pb); /* version */
        avio_rb24(pb); /* flags */
    
        /* component type */
        ctype = avio_rl32(pb);
        type = avio_rl32(pb); /* component subtype */
    
        av_log(c->fc, AV_LOG_TRACE, "ctype= %.4s (0x%08x)\n", (char*)&ctype, ctype);
        av_log(c->fc, AV_LOG_TRACE, "stype= %.4s\n", (char*)&type);
    
        if     (type == MKTAG('v','i','d','e'))
            st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
        else if (type == MKTAG('s','o','u','n'))
            st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
        else if (type == MKTAG('m','1','a',' '))
            st->codec->codec_id = AV_CODEC_ID_MP2;
        else if ((type == MKTAG('s','u','b','p')) || (type == MKTAG('c','l','c','p')))
            st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
    
        avio_rb32(pb); /* component  manufacture */
        avio_rb32(pb); /* component flags */
        avio_rb32(pb); /* component flags mask */
    
        title_size = atom.size - 24;
        if (title_size > 0) {
            title_str = av_malloc(title_size + 1); /* Add null terminator */
            if (!title_str)
                return AVERROR(ENOMEM);
    
            ret = ffio_read_size(pb, title_str, title_size);
            if (ret < 0) {
                av_freep(&title_str);
                return ret;
            }
            title_str[title_size] = 0;
            if (title_str[0]) {
                int off = (!c->isom && title_str[0] == title_size - 1);
                av_dict_set(&st->metadata, "handler_name", title_str + off, 0);
            }
            av_freep(&title_str);
        }
    
        return 0;
    }
    

    其核心代码:

        if     (type == MKTAG('v','i','d','e'))
            st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
        else if (type == MKTAG('s','o','u','n'))
            st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
        else if (type == MKTAG('m','1','a',' '))
            st->codec->codec_id = AV_CODEC_ID_MP2;
        else if ((type == MKTAG('s','u','b','p')) || (type == MKTAG('c','l','c','p')))
            st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;

    如果hdlr的第16-20个字节的内容为vide,那么就是视频流,如果是soun,那么就是音频流,如说是subp或者clcp,那么就是字幕流。
    我们不可能把所有box的解析都分析一遍,这里对box的解析就到这里,其他的请感兴趣的reader自行分析了。

    展开全文
  • 函数rtmp_open()

    2016-12-07 11:38:00
    "Detected librtmp style URL parameters, these aren't supported " "by the libavformat internal RTMP handler currently enabled. " "See the documentation for the correct way to pass parameters.\n"); ...

    FFMPEG版本为3.2 release。

    libavformat/rtmpproto.c

     

    调用关系:

    avformat_open_input(&ic, filename, file_iformat, &o->g->format_opts) ->
    
    init_input(s, filename, &tmp) ->
    
    s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options),实际回调函数io_open_default() ->
    
    ffio_open_whitelist(pb, url, flags, &s->interrupt_callback, options, s->protocol_whitelist, s->protocol_blacklist); ->
    
    ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist, blacklist, NULL);
    
    ffurl_connect(*puc, options) ->
    
    uc->prot->url_open(uc, uc->filename, uc->flags)实际回调函数rtmp_open()

    Breakpoint 3, rtmp_open (s=0x2768fc0, uri=0x2769018 "rtmp://223.203.1.34:1936/live?vhost=cc.com/stream1_1", flags=1) at libavformat/rtmpproto.c:2608
    2608	{
    (gdb) bt
    #0  rtmp_open (s=0x2768fc0, uri=0x2769018 "rtmp://223.203.1.34:1936/live?vhost=cc.com/stream1_1", flags=1) at libavformat/rtmpproto.c:2608
    #1  0x00000000005f35dd in ffurl_connect (uc=0x2768fc0, options=0x7fffffffdaf0) at libavformat/avio.c:210
    #2  0x00000000005f3b53 in ffurl_open_whitelist (puc=0x7fffffffda68, filename=<value optimized out>, flags=1, int_cb=<value optimized out>, options=0x7fffffffdaf0, whitelist=0x0, 
        blacklist=0x0, parent=0x0) at libavformat/avio.c:347
    #3  0x00000000005f8b55 in ffio_open_whitelist (s=0x2768700, filename=<value optimized out>, flags=<value optimized out>, int_cb=<value optimized out>, options=<value optimized out>, 
        whitelist=<value optimized out>, blacklist=0x0) at libavformat/aviobuf.c:1046
    #4  0x00000000006ad1e1 in io_open_default (s=<value optimized out>, pb=<value optimized out>, url=<value optimized out>, flags=<value optimized out>, options=<value optimized out>)
        at libavformat/options.c:112
    #5  0x00000000007044bd in init_input (ps=0x7fffffffdbd8, filename=0x7fffffffe758 "rtmp://223.203.1.34:1936/live?vhost=cc.com/stream1_1", fmt=<value optimized out>, options=0x2768428)
        at libavformat/utils.c:415
    #6  avformat_open_input (ps=0x7fffffffdbd8, filename=0x7fffffffe758 "rtmp://223.203.1.34:1936/live?vhost=cc.com/stream1_1", fmt=<value optimized out>, options=0x2768428)
        at libavformat/utils.c:529
    #7  0x000000000047e65a in open_input_file (o=0x7fffffffdc80, filename=<value optimized out>) at ffmpeg_opt.c:997
    #8  0x000000000047bb76 in open_files (l=0x2767998, inout=0x18b68a6 "input", open_file=0x47e220 <open_input_file>) at ffmpeg_opt.c:3135
    #9  0x000000000047bde7 in ffmpeg_parse_options (argc=<value optimized out>, argv=<value optimized out>) at ffmpeg_opt.c:3175
    #10 0x00000000004927c4 in main (argc=12, argv=0x7fffffffe488) at ffmpeg.c:4564

     

     

    Open RTMP connection and verify that the stream can be played.

    static int rtmp_open(URLContext *s, const char *uri, int flags)
    {
        RTMPContext *rt = s->priv_data;
        char proto[8], hostname[256], path[1024], auth[100], *fname;
        char *old_app, *qmark, *n, fname_buffer[1024];
        uint8_t buf[2048];
        int port;
        AVDictionary *opts = NULL;
        int ret;
    
        if (rt->listen_timeout > 0)
            rt->listen = 1;
    
        rt->is_input = !(flags & AVIO_FLAG_WRITE);
    
        av_url_split(proto, sizeof(proto), auth, sizeof(auth),
                     hostname, sizeof(hostname), &port,
                     path, sizeof(path), s->filename);
    
        n = strchr(path, ' ');
        if (n) {
            av_log(s, AV_LOG_WARNING,
                   "Detected librtmp style URL parameters, these aren't supported "
                   "by the libavformat internal RTMP handler currently enabled. "
                   "See the documentation for the correct way to pass parameters.\n");
            *n = '\0'; // Trim not supported part
        }
    
        if (auth[0]) {
            char *ptr = strchr(auth, ':');
            if (ptr) {
                *ptr = '\0';
                av_strlcpy(rt->username, auth, sizeof(rt->username));
                av_strlcpy(rt->password, ptr + 1, sizeof(rt->password));
            }
        }
    
        if (rt->listen && strcmp(proto, "rtmp")) {
            av_log(s, AV_LOG_ERROR, "rtmp_listen not available for %s\n",
                   proto);
            return AVERROR(EINVAL);
        }
        if (!strcmp(proto, "rtmpt") || !strcmp(proto, "rtmpts")) {
            if (!strcmp(proto, "rtmpts"))
                av_dict_set(&opts, "ffrtmphttp_tls", "1", 1);
    
            /* open the http tunneling connection */
            ff_url_join(buf, sizeof(buf), "ffrtmphttp", NULL, hostname, port, NULL);
        } else if (!strcmp(proto, "rtmps")) {
            /* open the tls connection */
            if (port < 0)
                port = RTMPS_DEFAULT_PORT;
            ff_url_join(buf, sizeof(buf), "tls", NULL, hostname, port, NULL);
        } else if (!strcmp(proto, "rtmpe") || (!strcmp(proto, "rtmpte"))) {
            if (!strcmp(proto, "rtmpte"))
                av_dict_set(&opts, "ffrtmpcrypt_tunneling", "1", 1);
    
            /* open the encrypted connection */
            ff_url_join(buf, sizeof(buf), "ffrtmpcrypt", NULL, hostname, port, NULL);
            rt->encrypted = 1;
        } else {
            /* open the tcp connection */
            if (port < 0)
                port = RTMP_DEFAULT_PORT;
            if (rt->listen)
                ff_url_join(buf, sizeof(buf), "tcp", NULL, hostname, port,
                            "?listen&listen_timeout=%d",
                            rt->listen_timeout * 1000);
            else
                ff_url_join(buf, sizeof(buf), "tcp", NULL, hostname, port, NULL);
        }
    
    reconnect:
        if ((ret = ffurl_open_whitelist(&rt->stream, buf, AVIO_FLAG_READ_WRITE,
                                        &s->interrupt_callback, &opts,
                                        s->protocol_whitelist, s->protocol_blacklist, s)) < 0) {
            av_log(s , AV_LOG_ERROR, "Cannot open connection %s\n", buf);
            goto fail;
        }
    
        if (rt->swfverify) {
            if ((ret = rtmp_calc_swfhash(s)) < 0)
                goto fail;
        }
    
        rt->state = STATE_START;
        if (!rt->listen && (ret = rtmp_handshake(s, rt)) < 0)
            goto fail;
        if (rt->listen && (ret = rtmp_server_handshake(s, rt)) < 0)
            goto fail;
    
        rt->out_chunk_size = 128;
        rt->in_chunk_size  = 128; // Probably overwritten later
        rt->state = STATE_HANDSHAKED;
    
        // Keep the application name when it has been defined by the user.
        old_app = rt->app;
    
        rt->app = av_malloc(APP_MAX_LENGTH);
        if (!rt->app) {
            ret = AVERROR(ENOMEM);
            goto fail;
        }
    
        //extract "app" part from path
        qmark = strchr(path, '?');
        if (qmark && strstr(qmark, "slist=")) {
            char* amp;
            // After slist we have the playpath, the full path is used as app
            av_strlcpy(rt->app, path + 1, APP_MAX_LENGTH);
            fname = strstr(path, "slist=") + 6;
            // Strip any further query parameters from fname
            amp = strchr(fname, '&');
            if (amp) {
                av_strlcpy(fname_buffer, fname, FFMIN(amp - fname + 1,
                                                      sizeof(fname_buffer)));
                fname = fname_buffer;
            }
        } else if (!strncmp(path, "/ondemand/", 10)) {
            fname = path + 10;
            memcpy(rt->app, "ondemand", 9);
        } else {
            char *next = *path ? path + 1 : path;
            char *p = strchr(next, '/');
            if (!p) {
                if (old_app) {
                    // If name of application has been defined by the user, assume that
                    // playpath is provided in the URL
                    fname = next;
                } else {
                    fname = NULL;
                    av_strlcpy(rt->app, next, APP_MAX_LENGTH);
                }
            } else {
                // make sure we do not mismatch a playpath for an application instance
                char *c = strchr(p + 1, ':');
                fname = strchr(p + 1, '/');
                if (!fname || (c && c < fname)) {
                    fname = p + 1;
                    av_strlcpy(rt->app, path + 1, FFMIN(p - path, APP_MAX_LENGTH));
                } else {
                    fname++;
                    av_strlcpy(rt->app, path + 1, FFMIN(fname - path - 1, APP_MAX_LENGTH));
                }
            }
        }
    
        if (old_app) {
            // The name of application has been defined by the user, override it.
            if (strlen(old_app) >= APP_MAX_LENGTH) {
                ret = AVERROR(EINVAL);
                goto fail;
            }
            av_free(rt->app);
            rt->app = old_app;
        }
    
        if (!rt->playpath) {
            rt->playpath = av_malloc(PLAYPATH_MAX_LENGTH);
            if (!rt->playpath) {
                ret = AVERROR(ENOMEM);
                goto fail;
            }
    
            if (fname) {
                int len = strlen(fname);
                if (!strchr(fname, ':') && len >= 4 &&
                    (!strcmp(fname + len - 4, ".f4v") ||
                     !strcmp(fname + len - 4, ".mp4"))) {
                    memcpy(rt->playpath, "mp4:", 5);
                } else {
                    if (len >= 4 && !strcmp(fname + len - 4, ".flv"))
                        fname[len - 4] = '\0';
                    rt->playpath[0] = 0;
                }
                av_strlcat(rt->playpath, fname, PLAYPATH_MAX_LENGTH);
            } else {
                rt->playpath[0] = '\0';
            }
        }
    
        if (!rt->tcurl) {
            rt->tcurl = av_malloc(TCURL_MAX_LENGTH);
            if (!rt->tcurl) {
                ret = AVERROR(ENOMEM);
                goto fail;
            }
            ff_url_join(rt->tcurl, TCURL_MAX_LENGTH, proto, NULL, hostname,
                        port, "/%s", rt->app);
        }
    
        if (!rt->flashver) {
            rt->flashver = av_malloc(FLASHVER_MAX_LENGTH);
            if (!rt->flashver) {
                ret = AVERROR(ENOMEM);
                goto fail;
            }
            if (rt->is_input) {
                snprintf(rt->flashver, FLASHVER_MAX_LENGTH, "%s %d,%d,%d,%d",
                        RTMP_CLIENT_PLATFORM, RTMP_CLIENT_VER1, RTMP_CLIENT_VER2,
                        RTMP_CLIENT_VER3, RTMP_CLIENT_VER4);
            } else {
                snprintf(rt->flashver, FLASHVER_MAX_LENGTH,
                        "FMLE/3.0 (compatible; %s)", LIBAVFORMAT_IDENT);
            }
        }
    
        rt->client_report_size = 1048576;
        rt->bytes_read = 0;
        rt->has_audio = 0;
        rt->has_video = 0;
        rt->received_metadata = 0;
        rt->last_bytes_read = 0;
        rt->server_bw = 2500000;
        rt->duration = 0;
    
        av_log(s, AV_LOG_DEBUG, "Proto = %s, path = %s, app = %s, fname = %s\n",
               proto, path, rt->app, rt->playpath);
        if (!rt->listen) {
            if ((ret = gen_connect(s, rt)) < 0)
                goto fail;
        } else {
            if ((ret = read_connect(s, s->priv_data)) < 0)
                goto fail;
        }
    
        do {
            ret = get_packet(s, 1);
        } while (ret == AVERROR(EAGAIN));
        if (ret < 0)
            goto fail;
    
        if (rt->do_reconnect) {
            int i;
            ffurl_close(rt->stream);
            rt->stream       = NULL;
            rt->do_reconnect = 0;
            rt->nb_invokes   = 0;
            for (i = 0; i < 2; i++)
                memset(rt->prev_pkt[i], 0,
                       sizeof(**rt->prev_pkt) * rt->nb_prev_pkt[i]);
            free_tracked_methods(rt);
            goto reconnect;
        }
    
        if (rt->is_input) {
            // generate FLV header for demuxer
            rt->flv_size = 13;
            if ((ret = av_reallocp(&rt->flv_data, rt->flv_size)) < 0)
                goto fail;
            rt->flv_off  = 0;
            memcpy(rt->flv_data, "FLV\1\0\0\0\0\011\0\0\0\0", rt->flv_size);
    
            // Read packets until we reach the first A/V packet or read metadata.
            // If there was a metadata package in front of the A/V packets, we can
            // build the FLV header from this. If we do not receive any metadata,
            // the FLV decoder will allocate the needed streams when their first
            // audio or video packet arrives.
            while (!rt->has_audio && !rt->has_video && !rt->received_metadata) {
                if ((ret = get_packet(s, 0)) < 0)
                   goto fail;
            }
    
            // Either after we have read the metadata or (if there is none) the
            // first packet of an A/V stream, we have a better knowledge about the
            // streams, so set the FLV header accordingly.
            if (rt->has_audio) {
                rt->flv_data[4] |= FLV_HEADER_FLAG_HASAUDIO;
            }
            if (rt->has_video) {
                rt->flv_data[4] |= FLV_HEADER_FLAG_HASVIDEO;
            }
    
            // If we received the first packet of an A/V stream and no metadata but
            // the server returned a valid duration, create a fake metadata packet
            // to inform the FLV decoder about the duration.
            if (!rt->received_metadata && rt->duration > 0) {
                if ((ret = inject_fake_duration_metadata(rt)) < 0)
                    goto fail;
            }
        } else {
            rt->flv_size = 0;
            rt->flv_data = NULL;
            rt->flv_off  = 0;
            rt->skip_bytes = 13;
        }
    
        s->max_packet_size = rt->stream->max_packet_size;
        s->is_streamed     = 1;
        return 0;
    
    fail:
        av_dict_free(&opts);
        rtmp_close(s);
        return ret;
    }

    该函数主要包括四个功能:

    1.解析url

    2.打开连接

    3. 三次握手

    4. Generate 'connect' call and send it to the server

    解析url:rtmp://IP:port/app?vhost=name/stream_name

    void av_url_split(char *proto, int proto_size,
                      char *authorization, int authorization_size,
                      char *hostname, int hostname_size,
                      int *port_ptr, char *path, int path_size, const char *url)
    {
        const char *p, *ls, *ls2, *at, *at2, *col, *brk;
    
        if (port_ptr)
            *port_ptr = -1;
        if (proto_size > 0)
            proto[0] = 0;
        if (authorization_size > 0)
            authorization[0] = 0;
        if (hostname_size > 0)
            hostname[0] = 0;
        if (path_size > 0)
            path[0] = 0;
    
        /* parse protocol */
        if ((p = strchr(url, ':'))) {
            av_strlcpy(proto, url, FFMIN(proto_size, p + 1 - url));
            p++; /* skip ':' */
            if (*p == '/')
                p++;
            if (*p == '/')
                p++;
        } else {
            /* no protocol means plain filename */
            av_strlcpy(path, url, path_size);
            return;
        }
    
        /* separate path from hostname */
        ls = strchr(p, '/');
        ls2 = strchr(p, '?');
        if (!ls)
            ls = ls2;
        else if (ls && ls2)
            ls = FFMIN(ls, ls2);
        if (ls)
            av_strlcpy(path, ls, path_size);
        else
            ls = &p[strlen(p)];  // XXX
    
        /* the rest is hostname, use that to parse auth/port */
        if (ls != p) {
            /* authorization (user[:pass]@hostname) */
            at2 = p;
            while ((at = strchr(p, '@')) && at < ls) {
                av_strlcpy(authorization, at2,
                           FFMIN(authorization_size, at + 1 - at2));
                p = at + 1; /* skip '@' */
            }
    
            if (*p == '[' && (brk = strchr(p, ']')) && brk < ls) {
                /* [host]:port */
                av_strlcpy(hostname, p + 1,
                           FFMIN(hostname_size, brk - p));
                if (brk[1] == ':' && port_ptr)
                    *port_ptr = atoi(brk + 2);
            } else if ((col = strchr(p, ':')) && col < ls) {
                av_strlcpy(hostname, p,
                           FFMIN(col + 1 - p, hostname_size));
                if (port_ptr)
                    *port_ptr = atoi(col + 1);
            } else
                av_strlcpy(hostname, p,
                           FFMIN(ls + 1 - p, hostname_size));
        }
    }

    根据输入url获取hostname,端口号,路径参数。

    打开tcp连接

    int ff_url_join(char *str, int size, const char *proto,
                    const char *authorization, const char *hostname,
                    int port, const char *fmt, ...)
    {
    #if CONFIG_NETWORK
        struct addrinfo hints = { 0 }, *ai;
    #endif
    
        str[0] = '\0';
        if (proto)
            av_strlcatf(str, size, "%s://", proto);
        if (authorization && authorization[0])
            av_strlcatf(str, size, "%s@", authorization);
    #if CONFIG_NETWORK && defined(AF_INET6)
        /* Determine if hostname is a numerical IPv6 address,
         * properly escape it within [] in that case. */
        hints.ai_flags = AI_NUMERICHOST;
        if (!getaddrinfo(hostname, NULL, &hints, &ai)) {
            if (ai->ai_family == AF_INET6) {
                av_strlcat(str, "[", size);
                av_strlcat(str, hostname, size);
                av_strlcat(str, "]", size);
            } else {
                av_strlcat(str, hostname, size);
            }
            freeaddrinfo(ai);
        } else
    #endif
            /* Not an IPv6 address, just output the plain string. */
            av_strlcat(str, hostname, size);
    
        if (port >= 0)
            av_strlcatf(str, size, ":%d", port);
        if (fmt) {
            va_list vl;
            size_t len = strlen(str);
    
            va_start(vl, fmt);
            vsnprintf(str + len, size > len ? size - len : 0, fmt, vl);
            va_end(vl);
        }
        return strlen(str);
    }

    例如,输入url为 rtmp://IP:1936/live?vhost=cc.com/stream_1,

    则调用函数ff_url_join(buf, sizeof(buf), "tcp", NULL, hostname, port, NULL);因为rtmp是基于tcp协议。所以buf的内容为 "tcp://IP:1936"

     

    打开URL,即调用tcp协议

    int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags,
                             const AVIOInterruptCB *int_cb, AVDictionary **options,
                             const char *whitelist, const char* blacklist,
                             URLContext *parent)
    {
        AVDictionary *tmp_opts = NULL;
        AVDictionaryEntry *e;
        int ret = ffurl_alloc(puc, filename, flags, int_cb);
        if (ret < 0)
            return ret;
        if (parent)
            av_opt_copy(*puc, parent);
        if (options &&
            (ret = av_opt_set_dict(*puc, options)) < 0)
            goto fail;
        if (options && (*puc)->prot->priv_data_class &&
            (ret = av_opt_set_dict((*puc)->priv_data, options)) < 0)
            goto fail;
    
        if (!options)
            options = &tmp_opts;
    
        av_assert0(!whitelist ||
                   !(e=av_dict_get(*options, "protocol_whitelist", NULL, 0)) ||
                   !strcmp(whitelist, e->value));
        av_assert0(!blacklist ||
                   !(e=av_dict_get(*options, "protocol_blacklist", NULL, 0)) ||
                   !strcmp(blacklist, e->value));
    
        if ((ret = av_dict_set(options, "protocol_whitelist", whitelist, 0)) < 0)
            goto fail;
    
        if ((ret = av_dict_set(options, "protocol_blacklist", blacklist, 0)) < 0)
            goto fail;
    
        if ((ret = av_opt_set_dict(*puc, options)) < 0)
            goto fail;
    
        ret = ffurl_connect(*puc, options);
    
        if (!ret)
            return 0;
    fail:
        ffurl_close(*puc);
        *puc = NULL;
        return ret;
    }

    即函数uc->prot->url_open(uc, uc->filename, uc->flags)回调tcp_open()

     

    函数tcp_open()

    /* return non zero if error */
    static int tcp_open(URLContext *h, const char *uri, int flags)
    {
        struct addrinfo hints = { 0 }, *ai, *cur_ai;
        int port, fd = -1;
        TCPContext *s = h->priv_data;
        const char *p;
        char buf[256];
        int ret;
        char hostname[1024],proto[1024],path[1024];
        char portstr[10];
        s->open_timeout = 5000000;
    
        av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname),
            &port, path, sizeof(path), uri);
        if (strcmp(proto, "tcp"))
            return AVERROR(EINVAL);
        if (port <= 0 || port >= 65536) {
            av_log(h, AV_LOG_ERROR, "Port missing in uri\n");
            return AVERROR(EINVAL);
        }
        p = strchr(uri, '?');
        if (p) {
            if (av_find_info_tag(buf, sizeof(buf), "listen", p)) {
                char *endptr = NULL;
                s->listen = strtol(buf, &endptr, 10);
                /* assume if no digits were found it is a request to enable it */
                if (buf == endptr)
                    s->listen = 1;
            }
            if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) {
                s->rw_timeout = strtol(buf, NULL, 10);
            }
            if (av_find_info_tag(buf, sizeof(buf), "listen_timeout", p)) {
                s->listen_timeout = strtol(buf, NULL, 10);
            }
        }
        if (s->rw_timeout >= 0) {
            s->open_timeout =
            h->rw_timeout   = s->rw_timeout;
        }
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;
        snprintf(portstr, sizeof(portstr), "%d", port);
        if (s->listen)
            hints.ai_flags |= AI_PASSIVE;
        if (!hostname[0])
            ret = getaddrinfo(NULL, portstr, &hints, &ai);
        else
            ret = getaddrinfo(hostname, portstr, &hints, &ai);
        if (ret) {
            av_log(h, AV_LOG_ERROR,
                   "Failed to resolve hostname %s: %s\n",
                   hostname, gai_strerror(ret));
            return AVERROR(EIO);
        }
    
        cur_ai = ai;
    
     restart:
        fd = ff_socket(cur_ai->ai_family,
                       cur_ai->ai_socktype,
                       cur_ai->ai_protocol);
        if (fd < 0) {
            ret = ff_neterrno();
            goto fail;
        }
    
        if (s->listen == 2) {
            // multi-client
            if ((ret = ff_listen(fd, cur_ai->ai_addr, cur_ai->ai_addrlen)) < 0)
                goto fail1;
        } else if (s->listen == 1) {
            // single client
            if ((ret = ff_listen_bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
                                      s->listen_timeout, h)) < 0)
                goto fail1;
            // Socket descriptor already closed here. Safe to overwrite to client one.
            fd = ret;
        } else {
            if ((ret = ff_listen_connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
                                         s->open_timeout / 1000, h, !!cur_ai->ai_next)) < 0) {
    
                if (ret == AVERROR_EXIT)
                    goto fail1;
                else
                    goto fail;
            }
        }
    
        h->is_streamed = 1;
        s->fd = fd;
        /* Set the socket's send or receive buffer sizes, if specified.
           If unspecified or setting fails, system default is used. */
        if (s->recv_buffer_size > 0) {
            setsockopt (fd, SOL_SOCKET, SO_RCVBUF, &s->recv_buffer_size, sizeof (s->recv_buffer_size));
        }
        if (s->send_buffer_size > 0) {
            setsockopt (fd, SOL_SOCKET, SO_SNDBUF, &s->send_buffer_size, sizeof (s->send_buffer_size));
        }
    
        freeaddrinfo(ai);
        return 0;
    
     fail:
        if (cur_ai->ai_next) {
            /* Retry with the next sockaddr */
            cur_ai = cur_ai->ai_next;
            if (fd >= 0)
                closesocket(fd);
            ret = 0;
            goto restart;
        }
     fail1:
        if (fd >= 0)
            closesocket(fd);
        freeaddrinfo(ai);
        return ret;
    }

     

    握手rtmp_handshake(s, rt)

    /**
     * Perform handshake with the server by means of exchanging pseudorandom data
     * signed with HMAC-SHA2 digest.
     *
     * @return 0 if handshake succeeds, negative value otherwise
     */
    static int rtmp_handshake(URLContext *s, RTMPContext *rt)
    {
        AVLFG rnd;
        uint8_t tosend    [RTMP_HANDSHAKE_PACKET_SIZE+1] = {
            3,                // unencrypted data
            0, 0, 0, 0,       // client uptime
            RTMP_CLIENT_VER1,
            RTMP_CLIENT_VER2,
            RTMP_CLIENT_VER3,
            RTMP_CLIENT_VER4,
        };
        uint8_t clientdata[RTMP_HANDSHAKE_PACKET_SIZE];
        uint8_t serverdata[RTMP_HANDSHAKE_PACKET_SIZE+1];
        int i;
        int server_pos, client_pos;
        uint8_t digest[32], signature[32];
        int ret, type = 0;
    
        av_log(s, AV_LOG_DEBUG, "Handshaking...\n");
    
        av_lfg_init(&rnd, 0xDEADC0DE);
        // generate handshake packet - 1536 bytes of pseudorandom data
        for (i = 9; i <= RTMP_HANDSHAKE_PACKET_SIZE; i++)
            tosend[i] = av_lfg_get(&rnd) >> 24;
    
        if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
            /* When the client wants to use RTMPE, we have to change the command
             * byte to 0x06 which means to use encrypted data and we have to set
             * the flash version to at least 9.0.115.0. */
            tosend[0] = 6;
            tosend[5] = 128;
            tosend[6] = 0;
            tosend[7] = 3;
            tosend[8] = 2;
    
            /* Initialize the Diffie-Hellmann context and generate the public key
             * to send to the server. */
            if ((ret = ff_rtmpe_gen_pub_key(rt->stream, tosend + 1)) < 0)
                return ret;
        }
    
        client_pos = rtmp_handshake_imprint_with_digest(tosend + 1, rt->encrypted);
        if (client_pos < 0)
            return client_pos;
    
        if ((ret = ffurl_write(rt->stream, tosend,
                               RTMP_HANDSHAKE_PACKET_SIZE + 1)) < 0) {
            av_log(s, AV_LOG_ERROR, "Cannot write RTMP handshake request\n");
            return ret;
        }
    
        if ((ret = ffurl_read_complete(rt->stream, serverdata,
                                       RTMP_HANDSHAKE_PACKET_SIZE + 1)) < 0) {
            av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
            return ret;
        }
    
        if ((ret = ffurl_read_complete(rt->stream, clientdata,
                                       RTMP_HANDSHAKE_PACKET_SIZE)) < 0) {
            av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
            return ret;
        }
    
        av_log(s, AV_LOG_DEBUG, "Type answer %d\n", serverdata[0]);
        av_log(s, AV_LOG_DEBUG, "Server version %d.%d.%d.%d\n",
               serverdata[5], serverdata[6], serverdata[7], serverdata[8]);
    
        if (rt->is_input && serverdata[5] >= 3) {
            server_pos = rtmp_validate_digest(serverdata + 1, 772);
            if (server_pos < 0)
                return server_pos;
    
            if (!server_pos) {
                type = 1;
                server_pos = rtmp_validate_digest(serverdata + 1, 8);
                if (server_pos < 0)
                    return server_pos;
    
                if (!server_pos) {
                    av_log(s, AV_LOG_ERROR, "Server response validating failed\n");
                    return AVERROR(EIO);
                }
            }
    
            /* Generate SWFVerification token (SHA256 HMAC hash of decompressed SWF,
             * key are the last 32 bytes of the server handshake. */
            if (rt->swfsize) {
                if ((ret = rtmp_calc_swf_verification(s, rt, serverdata + 1 +
                                                      RTMP_HANDSHAKE_PACKET_SIZE - 32)) < 0)
                    return ret;
            }
    
            ret = ff_rtmp_calc_digest(tosend + 1 + client_pos, 32, 0,
                                      rtmp_server_key, sizeof(rtmp_server_key),
                                      digest);
            if (ret < 0)
                return ret;
    
            ret = ff_rtmp_calc_digest(clientdata, RTMP_HANDSHAKE_PACKET_SIZE - 32,
                                      0, digest, 32, signature);
            if (ret < 0)
                return ret;
    
            if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
                /* Compute the shared secret key sent by the server and initialize
                 * the RC4 encryption. */
                if ((ret = ff_rtmpe_compute_secret_key(rt->stream, serverdata + 1,
                                                       tosend + 1, type)) < 0)
                    return ret;
    
                /* Encrypt the signature received by the server. */
                ff_rtmpe_encrypt_sig(rt->stream, signature, digest, serverdata[0]);
            }
    
            if (memcmp(signature, clientdata + RTMP_HANDSHAKE_PACKET_SIZE - 32, 32)) {
                av_log(s, AV_LOG_ERROR, "Signature mismatch\n");
                return AVERROR(EIO);
            }
    
            for (i = 0; i < RTMP_HANDSHAKE_PACKET_SIZE; i++)
                tosend[i] = av_lfg_get(&rnd) >> 24;
            ret = ff_rtmp_calc_digest(serverdata + 1 + server_pos, 32, 0,
                                      rtmp_player_key, sizeof(rtmp_player_key),
                                      digest);
            if (ret < 0)
                return ret;
    
            ret = ff_rtmp_calc_digest(tosend, RTMP_HANDSHAKE_PACKET_SIZE - 32, 0,
                                      digest, 32,
                                      tosend + RTMP_HANDSHAKE_PACKET_SIZE - 32);
            if (ret < 0)
                return ret;
    
            if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
                /* Encrypt the signature to be send to the server. */
                ff_rtmpe_encrypt_sig(rt->stream, tosend +
                                     RTMP_HANDSHAKE_PACKET_SIZE - 32, digest,
                                     serverdata[0]);
            }
    
            // write reply back to the server
            if ((ret = ffurl_write(rt->stream, tosend,
                                   RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
                return ret;
    
            if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
                /* Set RC4 keys for encryption and update the keystreams. */
                if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0)
                    return ret;
            }
        } else {
            if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
                /* Compute the shared secret key sent by the server and initialize
                 * the RC4 encryption. */
                if ((ret = ff_rtmpe_compute_secret_key(rt->stream, serverdata + 1,
                                tosend + 1, 1)) < 0)
                    return ret;
    
                if (serverdata[0] == 9) {
                    /* Encrypt the signature received by the server. */
                    ff_rtmpe_encrypt_sig(rt->stream, signature, digest,
                                         serverdata[0]);
                }
            }
    
            if ((ret = ffurl_write(rt->stream, serverdata + 1,
                                   RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
                return ret;
    
            if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
                /* Set RC4 keys for encryption and update the keystreams. */
                if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0)
                    return ret;
            }
        }
    
        return 0;
    }

    其中:

    (ret = ffurl_write(rt->stream, tosend,
                               RTMP_HANDSHAKE_PACKET_SIZE + 1))发送C0和C1到服务器,共1537个字节。写操作调用函数tcp_write()。

    (ret = ffurl_read_complete(rt->stream, serverdata,
                                       RTMP_HANDSHAKE_PACKET_SIZE + 1))接受服务器的响应S0和S1的数据,共1537个字节。读操作调用函数tcp_read()。

    (ret = ffurl_read_complete(rt->stream, clientdata,
                                       RTMP_HANDSHAKE_PACKET_SIZE))接受服务器的响应S2的数据,共1536个字节。读操作调用函数tcp_read()。

    (ret = ffurl_write(rt->stream, serverdata + 1,
                                   RTMP_HANDSHAKE_PACKET_SIZE))发送C2(内容为S1)到服务器,共1536个字节。写操作调用函数tcp_write()。

    Generate 'connect' call and send it to the server.

    /**
     * Generate 'connect' call and send it to the server.
     */
    static int gen_connect(URLContext *s, RTMPContext *rt)
    {
        RTMPPacket pkt;
        uint8_t *p;
        int ret;
    
        if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
                                         0, 4096 + APP_MAX_LENGTH)) < 0)
            return ret;
    
        p = pkt.data;
    
        ff_amf_write_string(&p, "connect");
        ff_amf_write_number(&p, ++rt->nb_invokes);
        ff_amf_write_object_start(&p);
        ff_amf_write_field_name(&p, "app");
        ff_amf_write_string2(&p, rt->app, rt->auth_params);
    
        if (!rt->is_input) {
            ff_amf_write_field_name(&p, "type");
            ff_amf_write_string(&p, "nonprivate");
        }
        ff_amf_write_field_name(&p, "flashVer");
        ff_amf_write_string(&p, rt->flashver);
    
        if (rt->swfurl) {
            ff_amf_write_field_name(&p, "swfUrl");
            ff_amf_write_string(&p, rt->swfurl);
        }
    
        ff_amf_write_field_name(&p, "tcUrl");
        ff_amf_write_string2(&p, rt->tcurl, rt->auth_params);
        if (rt->is_input) {
            ff_amf_write_field_name(&p, "fpad");
            ff_amf_write_bool(&p, 0);
            ff_amf_write_field_name(&p, "capabilities");
            ff_amf_write_number(&p, 15.0);
    
            /* Tell the server we support all the audio codecs except
             * SUPPORT_SND_INTEL (0x0008) and SUPPORT_SND_UNUSED (0x0010)
             * which are unused in the RTMP protocol implementation. */
            ff_amf_write_field_name(&p, "audioCodecs");
            ff_amf_write_number(&p, 4071.0);
            ff_amf_write_field_name(&p, "videoCodecs");
            ff_amf_write_number(&p, 252.0);
            ff_amf_write_field_name(&p, "videoFunction");
            ff_amf_write_number(&p, 1.0);
    
            if (rt->pageurl) {
                ff_amf_write_field_name(&p, "pageUrl");
                ff_amf_write_string(&p, rt->pageurl);
            }
        }
        ff_amf_write_object_end(&p);
    
        if (rt->conn) {
            char *param = rt->conn;
    
            // Write arbitrary AMF data to the Connect message.
            while (param) {
                char *sep;
                param += strspn(param, " ");
                if (!*param)
                    break;
                sep = strchr(param, ' ');
                if (sep)
                    *sep = '\0';
                if ((ret = rtmp_write_amf_data(s, param, &p)) < 0) {
                    // Invalid AMF parameter.
                    ff_rtmp_packet_destroy(&pkt);
                    return ret;
                }
    
                if (sep)
                    param = sep + 1;
                else
                    break;
            }
        }
    
        pkt.size = p - pkt.data;
    
        return rtmp_send_packet(rt, &pkt, 1);
    }

     

    转载于:https://my.oschina.net/u/2326611/blog/801342

    展开全文
  • open62541中文文档

    万次阅读 多人点赞 2018-12-04 23:11:10
    介绍 open62541(http://open62541.org)是一个开源的免费实现OPC UA(OPC统一架构),用C99和C ++ 98语言的通用...open62541库与平台无关。所有特定于平台的功能都是通过可交换的插件实现的。为主要操作系统提...

     

    介绍

    open62541(http://open62541.org)是一个开源的免费实现OPC UA(OPC统一架构),用C99和C ++ 98语言的通用子集编写。该库可与所有主要编译器一起使用,并提供实现专用OPC UA客户端和服务器的必要工具,或将基于OPC UA的通信集成到现有应用程序中。open62541库与平台无关。所有特定于平台的功能都是通过可交换的插件实现的。为主要操作系统提供了插件实现。

    open62541根据Mozilla Public License v2.0获得许可。因此open62541库可用于非开源项目。只有对open62541库本身的更改才需要在同一许可下发布。插件以及服务器和客户端示例都属于公共域(CC0许可证)。它们可以在任何许可下重复使用,并且不必发布更改。

    OPC统一架构

    OPC UA是工业通信协议,已在IEC 62541系列中标准化。OPC UA的核心是

    • 一种异步协议(建立在TCP,HTTP或SOAP之上),通过会话,(在)安全通信通道(在)原始连接之上定义消息交换,
    • 用于协议消息的类型系统,具有二进制和基于XML的编码方案,
    • 信息建模的元模型,它将面向对象与语义三重关系相结合,并且
    • 一组37个标准服务,用于与服务器端信息模型交互。每个服务的签名被定义为协议类型系统中的请求和响应消息。

    标准本身可以从IEC购买或者在OPC基金会的网站上免费下载,网址为https://opcfoundation.org/(您需要使用有效的电子邮件注册)。

    OPC基金会推动标准的不断改进和配套规范的发展。伴随规范将已建立的概念和可重用组件从应用程序域转换为OPC UA。它们是与应用领域的既定行业委员会或标准化机构共同创建的。此外,OPC基金会还组织活动以传播标准,并为合规认证提供基础设施和工具。

    open62541功能

    open62541实现了OPC UA二进制协议栈以及客户端和服务器SDK。它目前支持Micro Embedded Device Server Profile以及一些其他功能。服务器二进制文件的大小可能低于100kb,具体取决于所包含的信息模型。

    • 通讯栈
      • OPC UA二进制协议
      • 分块(分割大信息)
      • 可交换网络层(插件),用于使用自定义网络API(例如,在嵌入式目标上)
      • 加密通信
      • 客户端中的异步服务请求
    • 信息模型
      • 支持所有OPC UA节点类型(包括方法节点)
      • 支持在运行时添加和删除节点和引用。
      • 支持对象和变量类型的继承和实例化(自定义构造函数/析构函数,子节点的实例化)
      • 单个节点的访问控制
    • 订阅
      • 支持订阅/监视项目以获取数据更改通知
      • 每个受监视值的资源消耗非常低(基于事件的服务器体系结构)
    • 代码生成
      • 支持从标准XML定义生成数据类型
      • 支持从标准XML定义生成服务器端信息模型(节点集)

    0.3版本系列路线图中的功能但在初始v0.3版本中缺失的是:

    • 客户端中的加密通信
    • 事件(对象发出的通知,数据更改通知已实现)
    • 客户端中的事件循环(后台任务)

    建立open62541 

    构建示例

    使用GCC编译器,以下调用在Linux上构建示例。

    cp /path-to/open62541.* . # copy single-file distribution to the local directory
    cp /path-to/examples/server_variable.c . # copy the example server
    gcc -std=c99 open62541.c server_variable.c -o server

    构建库

    在Ubuntu或Debian上使用CMake构建

    sudo apt-get install git build-essential gcc pkg-config cmake python python-six
    
    # enable additional features
    sudo apt-get install cmake-curses-gui # for the ccmake graphical interface
    sudo apt-get install libmbedtls-dev # for encryption support
    sudo apt-get install check # for unit tests
    sudo apt-get install python-sphinx graphviz # for documentation generation
    sudo apt-get install python-sphinx-rtd-theme # documentation style
    
    cd open62541
    mkdir build
    cd build
    cmake ..
    make
    
    # select additional features
    ccmake ..
    make
    
    # build documentation
    make doc # html documentation
    make doc_pdf # pdf documentation (requires LaTeX)

    在Windows上使用CMake构建

    这里我们解释Visual Studio(2013或更新版本)的构建过程。要使用MinGW构建,只需在调用CMake时替换编译器选择。

    cd <path-to>\open62541
    mkdir build
    cd build
    <path-to>\cmake.exe .. -G "Visual Studio 14 2015"
    :: You can use use cmake-gui for a graphical user-interface to select features
    • 然后buildopen62541.sln在Visual Studio 2015中打开并像往常一样构建

    在OS X上构建

    brew install cmake
    pip install six # python 2/3 compatibility workarounds
    pip install sphinx # for documentation generation
    pip install sphinx_rtd_theme # documentation style
    brew install graphviz # for graphics in the documentation
    brew install check # for unit tests
    brew install userspace-rcu # for multi-threading support

    在没有apt-get命令的情况下遵循Ubuntu指令,因为上述包由这些指令处理。

    在OpenBSD上构建

    以下过程适用于OpenBSD 5.8,包含gcc版本4.8.4,cmake版本3.2.3和Python版本2.7.10。

    • 安装最近的gcc,python和cmake:
    pkg_add gcc python cmake
    • 告诉系统实际使用最近的gcc(它在OpenBSD上作为egcc安装):
    export CC=egcc CXX=eg++
    • 现在按照Ubuntu / Debian的描述进行:
    cd open62541
    mkdir build
    cd build
    cmake ..
    make

    构建选项

    open62541项目使用CMake来管理构建选项,用于代码生成以及为不同的系统和IDE生成构建项目。ccmakecmake-gui工具可用于以图形方式设置构建选项。

    在代码生成之后,大多数选项都可以手动更改ua_config.hopen62541.h对于单文件版本)。但通常没有必要调整它们。

    构建类型和日志记录

    CMAKE_BUILD_TYPE

    • RelWithDebInfo -O2优化调试符号
    • Release -O2优化没有调试符号
    • Debug -O0优化调试符号
    • MinSizeRel -Os优化没有调试符号

    UA_LOGLEVEL

    SDK仅记录在其中定义的级别的事件UA_LOGLEVEL。记录事件级别如下:

    • 600:致命
    • 500:错误
    • 400:警告
    • 300:信息
    • 200:调试
    • 100:跟踪

    UA_BUILD_ *组

    默认情况下,只有共享对象libopen62541.so或库open62541.dll和open62541.dll.a。open62541.lib正在构建。可以通过以下选项指定其他工件:

    UA_BUILD_EXAMPLES

    编译示例服务器和客户端。静态和动态二进制文件分别链接。examples/xyz.c

    UA_BUILD_UNIT_TESTS

    使用Check框架编译单元测试。测试可以执行make test

    UA_BUILD_EXAMPLES_NODESET_COMPILER

    从节点集XML生成OPC UA信息模型(实验)

    UA_BUILD_SELFSIGNED_CERTIFICATE

    为服务器生成自签名证书(需要openSSL)

    UA_ENABLE_ *组

    该组包含与支持的OPC UA功能相关的构建选项。

    UA_ENABLE_SUBSCRIPTIONS

    启用订阅

    UA_ENABLE_METHODCALLS

    启用方法服务集

    UA_ENABLE_NODEMANAGEMENT

    在运行时启用动态添加和删除节点

    UA_ENABLE_AMALGAMATION

    编译单个文件释放到文件open62541.copen62541.h

    UA_ENABLE_MULTITHREADING

    启用多线程支持

    UA_ENABLE_COVERAGE

    测量单元测试的覆盖范围

    UA_ENABLE_DISCOVERY

    启用发现服务(LDS)

    UA_ENABLE_DISCOVERY_MULTICAST

    使用多播支持启用发现服务(LDS-ME)

    UA_ENABLE_DISCOVERY_SEMAPHORE

    启用Discovery Semaphore支持

    某些选项标记为高级。需要切换高级选项以在cmake GUI中可见。

    UA_ENABLE_TYPENAMES

    将类型和成员名称添加到UA_DataType结构。默认情况下启用。

    UA_ENABLE_STATUSCODE_DESCRIPTIONS

    将StatusCodes的人类可读名称编译为二进制文件。默认情况下启用。

    UA_ENABLE_FULL_NS0

    使用完整的NS0而不是最小的命名空间0节点集 UA_FILE_NS0用于指定从namespace0文件夹生成NS0的文件。默认值是Opc.Ua.NodeSet2.xml

    UA_ENABLE_NONSTANDARD_UDP

    启用udp扩展

    UA_DEBUG_ *组

    该组包含主要用于库本身开发的构建选项。

    UA_DEBUG

    启用不适用于生产版本的断言和其他定义

    UA_DEBUG_DUMP_PKGS

    将服务器收到的每个包转储为hexdump格式

    构建共享库

    open62541足够小,大多数用户都希望将库静态链接到他们的程序中。如果需要共享库(.dll,.so),可以使用该BUILD_SHARED_LIBS选项在CMake中启用此功能。请注意,此选项会修改单文件分发中ua_config.h也包含 open62541.h的文件。

    最小化二进制大小

    通过调整构建配置,可以显着减小生成的二进制文件的大小。首先,在CMake中,可以将构建类型设置为 CMAKE_BUILD_TYPE=MinSizeRel。这将设置编译器标志以最小化二进制大小。构建类型也会删除调试信息。其次,可以通过上述构建标志删除特征来减少二进制大小。

    特别是,日志记录占用了二进制文件中的大量空间,在嵌入式方案中可能不需要。设置UA_LOGLEVEL为600以上的值(= FATAL)将禁用所有日志记录。此外,功能标记 UA_ENABLE_TYPENAMESUA_ENABLE_STATUSCODE_DESCRIPTIONS向二进制文件添加静态信息,仅用于人类可读的日志记录和调试。

    服务器的RAM要求主要是由于以下设置:

    • 信息模型的大小
    • 连接客户端的数量
    • 已分配的已配置最大邮件大小

    安装和包装

    您可以使用众所周知的make install命令安装open62541 。这允许您为自己的项目使用预先构建的库和标头。

    要覆盖默认安装目录,请使用。根据您选择的SDK功能,如上所述,这些功能也将包含在安装中。因此,我们建议为已安装的二进制文件启用尽可能多的功能。cmake -DCMAKE_INSTALL_PREFIX=/some/path

    在您自己的CMake项目中,您可以使用以下命令包含open62541库:

    find_package(open62541 0.4.0 REQUIRED COMPONENTS Events DiscoveryMulticast)
    add_executable(main main.cpp )
    target_link_libraries(main open62541)

    使用数据类型

    OPC UA为可以在协议消息中编码的值定义类型系统。本教程介绍了可用数据类型及其用法的一些示例。有关完整定义,请参阅有关数据类型的部分。

    基本数据处理

    本节介绍数据类型的基本交互模式。确保与中的类型定义进行比较ua_types.h

    #include <assert.h>
    #include "open62541.h"
    
    static void
    variables_basic(void) {
        /* Int32 */
        UA_Int32 i = 5;
        UA_Int32 j;
        UA_Int32_copy(&i, &j);
    
        UA_Int32 *ip = UA_Int32_new();
        UA_Int32_copy(&i, ip);
        UA_Int32_delete(ip);
    
        /* String */
        UA_String s;
        UA_String_init(&s); /* _init zeroes out the entire memory of the datatype */
        char *test = "test";
        s.length = strlen(test);
        s.data = (UA_Byte*)test;
    
        UA_String s2;
        UA_String_copy(&s, &s2);
        UA_String_deleteMembers(&s2); /* Copying heap-allocated the dynamic content */
    
        UA_String s3 = UA_STRING("test2");
        UA_String s4 = UA_STRING_ALLOC("test2"); /* Copies the content to the heap */
        UA_Boolean eq = UA_String_equal(&s3, &s4);
        UA_String_deleteMembers(&s4);
        if(!eq)
            return;
    
        /* Structured Type */
        UA_CallRequest cr;
        UA_init(&cr, &UA_TYPES[UA_TYPES_CALLREQUEST]); /* Generic method */
        UA_CallRequest_init(&cr); /* Shorthand for the previous line */
    
        cr.requestHeader.timestamp = UA_DateTime_now(); /* Members of a structure */
    
        cr.methodsToCall = (UA_CallMethodRequest *)UA_Array_new(5, &UA_TYPES[UA_TYPES_CALLMETHODREQUEST]);
        cr.methodsToCallSize = 5; /* Array size needs to be made known */
    
        UA_CallRequest *cr2 = UA_CallRequest_new();
        UA_copy(&cr, cr2, &UA_TYPES[UA_TYPES_CALLREQUEST]);
        UA_CallRequest_deleteMembers(&cr);
        UA_CallRequest_delete(cr2);
    }

    NodeIds 

    OPC UA信息模型由节点和节点之间的引用组成。每个节点都有一个唯一的NodeId。NodeIds引用具有附加标识符值的命名空间,该标识符值可以是整数,字符串,guid或字节串。

    static void
    variables_nodeids(void) {
        UA_NodeId id1 = UA_NODEID_NUMERIC(1, 1234);
        id1.namespaceIndex = 3;
    
        UA_NodeId id2 = UA_NODEID_STRING(1, "testid"); /* the string is static */
        UA_Boolean eq = UA_NodeId_equal(&id1, &id2);
        if(eq)
            return;
    
        UA_NodeId id3;
        UA_NodeId_copy(&id2, &id3);
        UA_NodeId_deleteMembers(&id3);
    
        UA_NodeId id4 = UA_NODEID_STRING_ALLOC(1, "testid"); /* the string is copied
                                                                to the heap */
        UA_NodeId_deleteMembers(&id4);
    }

    变种

    数据类型Variant属于OPC UA的内置数据类型,用作容器类型。变体可以将任何其他数据类型保存为标量(变体除外)或数组。阵列变体还可以表示附加整数阵列中的数据的维度(例如,2×3矩阵)。

    static void
    variables_variants(void) {
        /* Set a scalar value */
        UA_Variant v;
        UA_Int32 i = 42;
        UA_Variant_setScalar(&v, &i, &UA_TYPES[UA_TYPES_INT32]);
    
        /* Make a copy */
        UA_Variant v2;
        UA_Variant_copy(&v, &v2);
        UA_Variant_deleteMembers(&v2);
    
        /* Set an array value */
        UA_Variant v3;
        UA_Double d[9] = {1.0, 2.0, 3.0,
                          4.0, 5.0, 6.0,
                          7.0, 8.0, 9.0};
        UA_Variant_setArrayCopy(&v3, d, 9, &UA_TYPES[UA_TYPES_DOUBLE]);
    
        /* Set array dimensions */
        v3.arrayDimensions = (UA_UInt32 *)UA_Array_new(2, &UA_TYPES[UA_TYPES_UINT32]);
        v3.arrayDimensionsSize = 2;
        v3.arrayDimensions[0] = 3;
        v3.arrayDimensions[1] = 3;
        UA_Variant_deleteMembers(&v3);
    }

    它遵循主要功能,利用上述定义。

    int main(void) {
        variables_basic();
        variables_nodeids();
        variables_variants();
        return 0;
    }

    构建一个简单的服务器

    本系列教程将指导您完成open62541的第一步。要编译示例,您需要一个编译器(MS Visual Studio 2015或更新版本,GCC,Clang和MinGW32都已知有效)。编制说明是为GCC提供的,但应该很容易适应。

    安装带有图形前端的OPC UA客户端也非常有用,例如Unified Automation的UAExpert。这将使您能够检查任何OPC UA服务器的信息模型。

    要开始,请从http://open62541.org下载open62541单文件版本, 或者根据构建说明生成它,并启用“amalgamation”选项。从现在开始,我们假设您拥有open62541.c/.h当前文件夹中的文件。现在创建一个myServer.c使用以下内容调用的新C源文件:

    #include <signal.h>
    #include "open62541.h"
    
    UA_Boolean running = true;
    static void stopHandler(int sig) {
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "received ctrl-c");
        running = false;
    }
    
    int main(void) {
        signal(SIGINT, stopHandler);
        signal(SIGTERM, stopHandler);
    
        UA_ServerConfig *config = UA_ServerConfig_new_default();
        UA_Server *server = UA_Server_new(config);
    
        UA_StatusCode retval = UA_Server_run(server, &running);
        UA_Server_delete(server);
        UA_ServerConfig_delete(config);
        return (int)retval;
    }

    这就是简单的OPC UA服务器所需要的一切。使用GCC编译器,以下命令生成可执行文件:

    $ gcc -std=c99 open62541.c myServer.c -o myServer

    在MinGW环境中,必须添加Winsock库。

    $ gcc -std=c99 open62541.c myServer.c -lws2_32 -o myServer.exe

    现在启动服务器(使用ctrl-c停止):

    $ ./myServer

    您现在已经编译并运行了第一个OPC UA服务器。您可以继续使用客户端浏览信息模型。服务器正在监听 opc.tcp://localhost:4840。在接下来的两节中,我们将继续详细解释代码的不同部分。

    服务器配置和插件

    open62541为构建OPC UA服务器和客户端提供了灵活的框架。目标是拥有一个适用于所有用例并在所有平台上运行的核心库。然后,用户可以通过配置和开发(特定于平台的)插件来调整库以适合其用例。核心库仅基于C99,甚至不需要基本的POSIX支持。例如,低级网络代码实现为可交换插件。但别担心。open62541提供了大多数平台的插件实现和开箱即用的合理默认配置。

    在上面的服务器代码中,我们简单地采用默认服务器配置并添加一个在端口4840上进行连接的TCP网络层。

    服务器生命周期

    此示例中的代码显示了服务器生命周期管理的三个部分:创建服务器,运行服务器和删除服务器。一旦配置完成,创建和删除服务器就很简单了。服务器启动时UA_Server_run。在内部,服务器然后使用超时来安排常规任务。在超时之间,服务器在网络层上侦听传入消息。

    您可能会问服务器如何知道何时停止运行。为此,我们创建了一个全局变量running。此外,我们已经注册了stopHandler捕获程序在操作系统尝试关闭时接收的信号(中断)的方法。例如,当您在终端程序中按ctrl-c时会发生这种情况。然后,信号处理程序将变量设置running为false,服务器一旦收回控制权就会关闭。[1]

    为了将OPC UA集成到具有自己的主循环的单线程应用程序中(例如由GUI工具包提供),可以选择手动驱动服务器。有关详细信息,请参阅Server Lifecycle上的服务器文档部分 。

    所有服务器都需要服务器配置和生命周期管理。我们将在以下教程中使用它而无需进一步评论。

    [1]在多线程应用程序中要小心全局变量。您可能希望running在堆上分配变量。

    将变量添加到服务器

    本教程介绍如何使用数据类型以及如何将变量节点添加到服务器。首先,我们向服务器添加一个新变量。查看UA_VariableAttrbitues结构的定义以查看为VariableNodes定义的所有属性的列表。

    请注意,默认设置将变量值的AccessLevel设置为只读。请参阅下文,了解变量是否可写。

    #include <signal.h>
    #include <stdio.h>
    #include "open62541.h"
    
    static void
    addVariable(UA_Server *server) {
        /* Define the attribute of the myInteger variable node */
        UA_VariableAttributes attr = UA_VariableAttributes_default;
        UA_Int32 myInteger = 42;
        UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
        attr.description = UA_LOCALIZEDTEXT("en-US","the answer");
        attr.displayName = UA_LOCALIZEDTEXT("en-US","the answer");
        attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
        attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
    
        /* Add the variable node to the information model */
        UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
        UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
        UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
        UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
        UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
                                  parentReferenceNodeId, myIntegerName,
                                  UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr, NULL, NULL);
    }

    现在我们使用写入服务更改值。这使用了相同的服务实现,也可以通过OPC UA客户端在网络上实现。

    static void
    writeVariable(UA_Server *server) {
        UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
    
        /* Write a different integer value */
        UA_Int32 myInteger = 43;
        UA_Variant myVar;
        UA_Variant_init(&myVar);
        UA_Variant_setScalar(&myVar, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
        UA_Server_writeValue(server, myIntegerNodeId, myVar);
    
        /* Set the status code of the value to an error code. The function
         * UA_Server_write provides access to the raw service. The above
         * UA_Server_writeValue is syntactic sugar for writing a specific node
         * attribute with the write service. */
        UA_WriteValue wv;
        UA_WriteValue_init(&wv);
        wv.nodeId = myIntegerNodeId;
        wv.attributeId = UA_ATTRIBUTEID_VALUE;
        wv.value.status = UA_STATUSCODE_BADNOTCONNECTED;
        wv.value.hasStatus = true;
        UA_Server_write(server, &wv);
    
        /* Reset the variable to a good statuscode with a value */
        wv.value.hasStatus = false;
        wv.value.value = myVar;
        wv.value.hasValue = true;
        UA_Server_write(server, &wv);
    }

    注意我们最初如何将变量节点的DataType属性设置为Int32数据类型的NodeId。这禁止写入不是Int32的值。以下代码显示了如何为每次写入执行此一致性检查。

    static void
    writeWrongVariable(UA_Server *server) {
        UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
    
        /* Write a string */
        UA_String myString = UA_STRING("test");
        UA_Variant myVar;
        UA_Variant_init(&myVar);
        UA_Variant_setScalar(&myVar, &myString, &UA_TYPES[UA_TYPES_STRING]);
        UA_StatusCode retval = UA_Server_writeValue(server, myIntegerNodeId, myVar);
        printf("Writing a string returned statuscode %s\n", UA_StatusCode_name(retval));
    }

    它遵循主服务器代码,使用上述定义。

    UA_Boolean running = true;
    static void stopHandler(int sign) {
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
        running = false;
    }
    
    int main(void) {
        signal(SIGINT, stopHandler);
        signal(SIGTERM, stopHandler);
    
        UA_ServerConfig *config = UA_ServerConfig_new_default();
        UA_Server *server = UA_Server_new(config);
    
        addVariable(server);
        writeVariable(server);
        writeWrongVariable(server);
    
        UA_StatusCode retval = UA_Server_run(server, &running);
        UA_Server_delete(server);
        UA_ServerConfig_delete(config);
        return (int)retval;
    }

    将变量与物理过程连接

    在基于OPC UA的体系结构中,服务器通常位于信息源附近。在工业环境中,这转化为靠近物理过程的服务器和在运行时消耗数据的客户端。在上一个教程中,我们了解了如何将变量添加到OPC UA信息模型中。本教程介绍如何将变量连接到运行时信息,例如从物理过程的测量值。为简单起见,我们将系统时钟作为基础“过程”。

    以下代码片段分别涉及在运行时更新变量值的不同方法。总之,代码片段定义了可编译的源文件。

    手动更新变量

    作为起点,假设已在服务器中创建了DateTime类型值的变量, 标识符为“ns = 1,s = current-time”。假设当一个新值从底层进程到达时我们的应用程序被触发,我们就可以写入变量。

    #include <signal.h>
    #include "open62541.h"
    
    static void
    updateCurrentTime(UA_Server *server) {
        UA_DateTime now = UA_DateTime_now();
        UA_Variant value;
        UA_Variant_setScalar(&value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
        UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time");
        UA_Server_writeValue(server, currentNodeId, value);
    }
    
    static void
    addCurrentTimeVariable(UA_Server *server) {
        UA_DateTime now = 0;
        UA_VariableAttributes attr = UA_VariableAttributes_default;
        attr.displayName = UA_LOCALIZEDTEXT("en-US", "Current time");
        attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
        UA_Variant_setScalar(&attr.value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
    
        UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time");
        UA_QualifiedName currentName = UA_QUALIFIEDNAME(1, "current-time");
        UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
        UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
        UA_NodeId variableTypeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
        UA_Server_addVariableNode(server, currentNodeId, parentNodeId,
                                  parentReferenceNodeId, currentName,
                                  variableTypeNodeId, attr, NULL, NULL);
    
        updateCurrentTime(server);
    }

    变量值回调

    当值连续变化时,例如系统时间,在紧密循环中更新值将占用大量资源。值回调允许将变量值与外部表示同步。它们将回调附加到在每次读取之前和每次写入操作之后执行的变量。

    static void
    beforeReadTime(UA_Server *server,
                   const UA_NodeId *sessionId, void *sessionContext,
                   const UA_NodeId *nodeid, void *nodeContext,
                   const UA_NumericRange *range, const UA_DataValue *data) {
        UA_DateTime now = UA_DateTime_now();
        UA_Variant value;
        UA_Variant_setScalar(&value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
        UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time");
        UA_Server_writeValue(server, currentNodeId, value);
    }
    
    static void
    afterWriteTime(UA_Server *server,
                   const UA_NodeId *sessionId, void *sessionContext,
                   const UA_NodeId *nodeId, void *nodeContext,
                   const UA_NumericRange *range, const UA_DataValue *data) {
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
                    "The variable was updated");
    }
    
    static void
    addValueCallbackToCurrentTimeVariable(UA_Server *server) {
        UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time");
        UA_ValueCallback callback ;
        callback.onRead = beforeReadTime;
        callback.onWrite = afterWriteTime;
        UA_Server_setVariableNode_valueCallback(server, currentNodeId, callback);
    }

    可变数据源

    使用值回调,该值仍存储在变量节点中。所谓的数据源更进了一步。服务器将每个读写请求重定向到回调函数。在读取时,回调提供当前值的副本。在内部,数据源需要实现自己的内存管理。

    static UA_StatusCode
    readCurrentTime(UA_Server *server,
                    const UA_NodeId *sessionId, void *sessionContext,
                    const UA_NodeId *nodeId, void *nodeContext,
                    UA_Boolean sourceTimeStamp, const UA_NumericRange *range,
                    UA_DataValue *dataValue) {
        UA_DateTime now = UA_DateTime_now();
        UA_Variant_setScalarCopy(&dataValue->value, &now,
                                 &UA_TYPES[UA_TYPES_DATETIME]);
        dataValue->hasValue = true;
        return UA_STATUSCODE_GOOD;
    }
    
    static UA_StatusCode
    writeCurrentTime(UA_Server *server,
                     const UA_NodeId *sessionId, void *sessionContext,
                     const UA_NodeId *nodeId, void *nodeContext,
                     const UA_NumericRange *range, const UA_DataValue *data) {
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
                    "Changing the system time is not implemented");
        return UA_STATUSCODE_BADINTERNALERROR;
    }
    
    static void
    addCurrentTimeDataSourceVariable(UA_Server *server) {
        UA_VariableAttributes attr = UA_VariableAttributes_default;
        attr.displayName = UA_LOCALIZEDTEXT("en-US", "Current time - data source");
        attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
    
        UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time-datasource");
        UA_QualifiedName currentName = UA_QUALIFIEDNAME(1, "current-time-datasource");
        UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
        UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
        UA_NodeId variableTypeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
    
        UA_DataSource timeDataSource;
        timeDataSource.read = readCurrentTime;
        timeDataSource.write = writeCurrentTime;
        UA_Server_addDataSourceVariableNode(server, currentNodeId, parentNodeId,
                                            parentReferenceNodeId, currentName,
                                            variableTypeNodeId, attr,
                                            timeDataSource, NULL, NULL);
    }

    它遵循主服务器代码,使用上述定义。

    UA_Boolean running = true;
    static void stopHandler(int sign) {
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
        running = false;
    }
    
    int main(void) {
        signal(SIGINT, stopHandler);
        signal(SIGTERM, stopHandler);
    
        UA_ServerConfig *config = UA_ServerConfig_new_default();
        UA_Server *server = UA_Server_new(config);
    
        addCurrentTimeVariable(server);
        addValueCallbackToCurrentTimeVariable(server);
        addCurrentTimeDataSourceVariable(server);
    
        UA_StatusCode retval = UA_Server_run(server, &running);
        UA_Server_delete(server);
        UA_ServerConfig_delete(config);
        return (int)retval;
    }

    DataChange通知

    对变量的当前值感兴趣的客户端不需要定期轮询变量。相反,他可以使用订阅机制来通知有关更改。

    在Subscription中,客户端添加了所谓的MonitoredItems。DataChange MonitoredItem定义监视更改的节点属性(通常是值属性)。服务器在内部读取定义的时间间隔内的值并生成相应的通知。上面讨论的更新节点值的三种方式都可以与通知结合使用。这是因为通知使用标准的读取服务来查找值更改。

    使用变量类型

    变量类型有三个功能:

    • 约束该类型变量的可能数据类型,值排名和数组维度。这允许根据泛型类型定义编写接口代码,因此它适用于所有实例。
    • 提供合理的默认值
    • 根据变量的类型启用变量的语义解释

    在本教程的示例中,我们通过double值数组表示2D空间中的一个点。以下函数将相应的VariableTypeNode添加到变量类型的层次结构中。

    #include <signal.h>
    #include "open62541.h"
    
    static UA_NodeId pointTypeId;
    
    static void
    addVariableType2DPoint(UA_Server *server) {
        UA_VariableTypeAttributes vtAttr = UA_VariableTypeAttributes_default;
        vtAttr.dataType = UA_TYPES[UA_TYPES_DOUBLE].typeId;
        vtAttr.valueRank = UA_VALUERANK_ONE_DIMENSION;
        UA_UInt32 arrayDims[1] = {2};
        vtAttr.arrayDimensions = arrayDims;
        vtAttr.arrayDimensionsSize = 1;
        vtAttr.displayName = UA_LOCALIZEDTEXT("en-US", "2DPoint Type");
    
        /* a matching default value is required */
        UA_Double zero[2] = {0.0, 0.0};
        UA_Variant_setArray(&vtAttr.value, zero, 2, &UA_TYPES[UA_TYPES_DOUBLE]);
    
        UA_Server_addVariableTypeNode(server, UA_NODEID_NULL,
                                      UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
                                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
                                      UA_QUALIFIEDNAME(1, "2DPoint Type"), UA_NODEID_NULL,
                                      vtAttr, NULL, &pointTypeId);
    }

    现在可以在创建新变量期间引用2DPoint的新变量类型。如果未给出值,则在实例化期间复制变量类型的缺省值。

    static UA_NodeId pointVariableId;
    
    static void
    addVariable(UA_Server *server) {
        /* Prepare the node attributes */
        UA_VariableAttributes vAttr = UA_VariableAttributes_default;
        vAttr.dataType = UA_TYPES[UA_TYPES_DOUBLE].typeId;
        vAttr.valueRank = UA_VALUERANK_ONE_DIMENSION;
        UA_UInt32 arrayDims[1] = {2};
        vAttr.arrayDimensions = arrayDims;
        vAttr.arrayDimensionsSize = 1;
        vAttr.displayName = UA_LOCALIZEDTEXT("en-US", "2DPoint Variable");
        vAttr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
        /* vAttr.value is left empty, the server instantiates with the default value */
    
        /* Add the node */
        UA_Server_addVariableNode(server, UA_NODEID_NULL,
                                  UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                                  UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                                  UA_QUALIFIEDNAME(1, "2DPoint Type"), pointTypeId,
                                  vAttr, NULL, &pointVariableId);
    }

    在创建类型的新变量实例时,会强制执行变量类型的约束。在以下函数中,添加带有字符串值的2DPoint类型的变量 失败,因为该值与变量类型约束不匹配。

    static void
    addVariableFail(UA_Server *server) {
        /* Prepare the node attributes */
        UA_VariableAttributes vAttr = UA_VariableAttributes_default;
        vAttr.dataType = UA_TYPES[UA_TYPES_DOUBLE].typeId;
        vAttr.valueRank = UA_VALUERANK_SCALAR; /* a scalar. this is not allowed per the variable type */
        vAttr.displayName = UA_LOCALIZEDTEXT("en-US", "2DPoint Variable (fail)");
        UA_String s = UA_STRING("2dpoint?");
        UA_Variant_setScalar(&vAttr.value, &s, &UA_TYPES[UA_TYPES_STRING]);
    
        /* Add the node will return UA_STATUSCODE_BADTYPEMISMATCH*/
        UA_Server_addVariableNode(server, UA_NODEID_NULL,
                                  UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                                  UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                                  UA_QUALIFIEDNAME(1, "2DPoint Type (fail)"), pointTypeId,
                                  vAttr, NULL, NULL);
    }

    在编写变量的数据类型,valuerank和arraydimensions属性时,会强制执行变量类型的约束。反过来,这会约束变量的value属性。

    static void
    writeVariable(UA_Server *server) {
        UA_StatusCode retval = UA_Server_writeValueRank(server, pointVariableId, UA_VALUERANK_ONE_OR_MORE_DIMENSIONS);
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
                    "Setting the Value Rank failed with Status Code %s",
                    UA_StatusCode_name(retval));
    
    }

    它遵循主服务器代码,使用上述定义。

    UA_Boolean running = true;
    static void stopHandler(int sign) {
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
        running = false;
    }
    
    int main(void) {
        signal(SIGINT, stopHandler);
        signal(SIGTERM, stopHandler);
    
        UA_ServerConfig *config = UA_ServerConfig_new_default();
        UA_Server *server = UA_Server_new(config);
    
        addVariableType2DPoint(server);
        addVariable(server);
        addVariableFail(server);
        writeVariable(server);
    
        UA_StatusCode retval = UA_Server_run(server, &running);
        UA_Server_delete(server);
        UA_ServerConfig_delete(config);
        return (int)retval;
    }

    使用对象和对象类型

    使用对象构建信息模型

    假设我们想要在OPC UA信息模型中对一组泵及其运行时状态进行建模。当然,所有泵的表示都应遵循相同的基本结构。例如,我们可能在SCADA可视化中具有泵的图形表示,该可视化应对所有泵都是可恢复的。

    遵循面向对象的编程范例,每个泵都由一个具有以下布局的对象表示:

    有向图树{fixedsize = true;  node [width = 2,height = 0,shape = box,fillcolor =“#E5E5E5”,concentrate = true] node_root [label = << I> ObjectNode </ I> <BR/> Pump>] {rank = same point_1 [shape = point] node_1 [label = << I> VariableNode </ I> <BR/> ManufacturerName>]} node_root  - > point_1 [arrowhead = none] point_1  - > node_1 [label =“hasComponent”] {rank = same point_2 [shape = point] node_2 [label = << I> VariableNode </ I> <BR/> ModelName>]} point_1  - > point_2 [arrowhead = none] point_2  - > node_2 [label =“hasComponent”] {rank =相同point_4 [shape = point] node_4 [label = << I> VariableNode </ I> <BR/>状态>]} point_2  - > point_4 [arrowhead = none] point_4  - > node_4 [label =“hasComponent”

    以下代码手动定义泵及其成员变量。我们省略了对变量值的设置约束,因为这不是本教程的重点,并且已经涵盖了。

    #include <signal.h>
    #include "open62541.h"
    
    static void
    manuallyDefinePump(UA_Server *server) {
        UA_NodeId pumpId; /* get the nodeid assigned by the server */
        UA_ObjectAttributes oAttr = UA_ObjectAttributes_default;
        oAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Pump (Manual)");
        UA_Server_addObjectNode(server, UA_NODEID_NULL,
                                UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                                UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
                                UA_QUALIFIEDNAME(1, "Pump (Manual)"), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
                                oAttr, NULL, &pumpId);
    
        UA_VariableAttributes mnAttr = UA_VariableAttributes_default;
        UA_String manufacturerName = UA_STRING("Pump King Ltd.");
        UA_Variant_setScalar(&mnAttr.value, &manufacturerName, &UA_TYPES[UA_TYPES_STRING]);
        mnAttr.displayName = UA_LOCALIZEDTEXT("en-US", "ManufacturerName");
        UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpId,
                                  UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                                  UA_QUALIFIEDNAME(1, "ManufacturerName"),
                                  UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), mnAttr, NULL, NULL);
    
        UA_VariableAttributes modelAttr = UA_VariableAttributes_default;
        UA_String modelName = UA_STRING("Mega Pump 3000");
        UA_Variant_setScalar(&modelAttr.value, &modelName, &UA_TYPES[UA_TYPES_STRING]);
        modelAttr.displayName = UA_LOCALIZEDTEXT("en-US", "ModelName");
        UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpId,
                                  UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                                  UA_QUALIFIEDNAME(1, "ModelName"),
                                  UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), modelAttr, NULL, NULL);
    
        UA_VariableAttributes statusAttr = UA_VariableAttributes_default;
        UA_Boolean status = true;
        UA_Variant_setScalar(&statusAttr.value, &status, &UA_TYPES[UA_TYPES_BOOLEAN]);
        statusAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Status");
        UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpId,
                                  UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                                  UA_QUALIFIEDNAME(1, "Status"),
                                  UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), statusAttr, NULL, NULL);
    
        UA_VariableAttributes rpmAttr = UA_VariableAttributes_default;
        UA_Double rpm = 50.0;
        UA_Variant_setScalar(&rpmAttr.value, &rpm, &UA_TYPES[UA_TYPES_DOUBLE]);
        rpmAttr.displayName = UA_LOCALIZEDTEXT("en-US", "MotorRPM");
        UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpId,
                                  UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                                  UA_QUALIFIEDNAME(1, "MotorRPMs"),
                                  UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), rpmAttr, NULL, NULL);
    }

    对象类型,类型层次结构和实例化

    手动构建每个对象需要我们编写大量代码。此外,客户端无法检测到对象代表泵。(我们可能会使用命名约定或类似方法来检测泵。但这并不是一个干净的解决方案。)此外,我们可能拥有的设备多于泵。我们要求所有设备共享一些共同的结构。解决方案是在具有继承关系的层次结构中定义ObjectType。

    有向图树{fixedsize = true;  node [width = 2,height = 0,shape = box,fillcolor =“#E5E5E5”,concentrate = true] node_root [label = << I> ObjectTypeNode </ I> <BR/> Device>] {rank = same point_1 [shape = point] node_1 [label = <I> VariableNode </ I> <BR/> ManufacturerName <BR/>(强制)>]} node_root  - > point_1 [arrowhead = none] point_1  - > node_1 [label =“ hasComponent“] {rank = same point_2 [shape = point] node_2 [label = << I> VariableNode </ I> <BR/> ModelName>]} point_1  - > point_2 [arrowhead = none] point_2  - > node_2 [label = “hasComponent”] {rank = same point_3 [shape = point] node_3 [label = << I> ObjectTypeNode </ I> <BR/> Pump>]} point_2  - > point_3 [arrowhead = none] point_3  - > node_3 [label = “hasSubtype”

    标记为必需的子项将与父对象一起自动实例化。这由对代表强制建模规则的对象的hasModellingRule引用指示。

    /* predefined identifier for later use */
    UA_NodeId pumpTypeId = {1, UA_NODEIDTYPE_NUMERIC, {1001}};
    
    static void
    defineObjectTypes(UA_Server *server) {
        /* Define the object type for "Device" */
        UA_NodeId deviceTypeId; /* get the nodeid assigned by the server */
        UA_ObjectTypeAttributes dtAttr = UA_ObjectTypeAttributes_default;
        dtAttr.displayName = UA_LOCALIZEDTEXT("en-US", "DeviceType");
        UA_Server_addObjectTypeNode(server, UA_NODEID_NULL,
                                    UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
                                    UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
                                    UA_QUALIFIEDNAME(1, "DeviceType"), dtAttr,
                                    NULL, &deviceTypeId);
    
        UA_VariableAttributes mnAttr = UA_VariableAttributes_default;
        mnAttr.displayName = UA_LOCALIZEDTEXT("en-US", "ManufacturerName");
        UA_NodeId manufacturerNameId;
        UA_Server_addVariableNode(server, UA_NODEID_NULL, deviceTypeId,
                                  UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                                  UA_QUALIFIEDNAME(1, "ManufacturerName"),
                                  UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), mnAttr, NULL, &manufacturerNameId);
        /* Make the manufacturer name mandatory */
        UA_Server_addReference(server, manufacturerNameId,
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE),
                               UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY), true);
    
    
        UA_VariableAttributes modelAttr = UA_VariableAttributes_default;
        modelAttr.displayName = UA_LOCALIZEDTEXT("en-US", "ModelName");
        UA_Server_addVariableNode(server, UA_NODEID_NULL, deviceTypeId,
                                  UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                                  UA_QUALIFIEDNAME(1, "ModelName"),
                                  UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), modelAttr, NULL, NULL);
    
        /* Define the object type for "Pump" */
        UA_ObjectTypeAttributes ptAttr = UA_ObjectTypeAttributes_default;
        ptAttr.displayName = UA_LOCALIZEDTEXT("en-US", "PumpType");
        UA_Server_addObjectTypeNode(server, pumpTypeId,
                                    deviceTypeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
                                    UA_QUALIFIEDNAME(1, "PumpType"), ptAttr,
                                    NULL, NULL);
    
        UA_VariableAttributes statusAttr = UA_VariableAttributes_default;
        statusAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Status");
        statusAttr.valueRank = UA_VALUERANK_SCALAR;
        UA_NodeId statusId;
        UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpTypeId,
                                  UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                                  UA_QUALIFIEDNAME(1, "Status"),
                                  UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), statusAttr, NULL, &statusId);
        /* Make the status variable mandatory */
        UA_Server_addReference(server, statusId,
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE),
                               UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY), true);
    
        UA_VariableAttributes rpmAttr = UA_VariableAttributes_default;
        rpmAttr.displayName = UA_LOCALIZEDTEXT("en-US", "MotorRPM");
        rpmAttr.valueRank = UA_VALUERANK_SCALAR;
        UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpTypeId,
                                  UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                                  UA_QUALIFIEDNAME(1, "MotorRPMs"),
                                  UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), rpmAttr, NULL, NULL);
    }

    现在我们为从设备对象类型继承的泵添加派生的ObjectType。结果对象包含所有必需的子变量。这些只是从对象类型复制而来。该对象具有hasTypeDefinition对象类型的类型引用,以便客户端可以在运行时检测类型 - 实例关系。

    static void
    addPumpObjectInstance(UA_Server *server, char *name) {
        UA_ObjectAttributes oAttr = UA_ObjectAttributes_default;
        oAttr.displayName = UA_LOCALIZEDTEXT("en-US", name);
        UA_Server_addObjectNode(server, UA_NODEID_NULL,
                                UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                                UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
                                UA_QUALIFIEDNAME(1, name),
                                pumpTypeId, /* this refers to the object type
                                               identifier */
                                oAttr, NULL, NULL);
    }

    通常,我们希望在新对象上运行构造函数。当在运行时实例化对象(使用AddNodes服务)并且可以手动定义与底层进程的集成时,这尤其有用。在以下构造函数示例中,我们只需将泵状态设置为on。

    static UA_StatusCode
    pumpTypeConstructor(UA_Server *server,
                        const UA_NodeId *sessionId, void *sessionContext,
                        const UA_NodeId *typeId, void *typeContext,
                        const UA_NodeId *nodeId, void **nodeContext) {
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "New pump created");
    
        /* Find the NodeId of the status child variable */
        UA_RelativePathElement rpe;
        UA_RelativePathElement_init(&rpe);
        rpe.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT);
        rpe.isInverse = false;
        rpe.includeSubtypes = false;
        rpe.targetName = UA_QUALIFIEDNAME(1, "Status");
    
        UA_BrowsePath bp;
        UA_BrowsePath_init(&bp);
        bp.startingNode = *nodeId;
        bp.relativePath.elementsSize = 1;
        bp.relativePath.elements = &rpe;
    
        UA_BrowsePathResult bpr =
            UA_Server_translateBrowsePathToNodeIds(server, &bp);
        if(bpr.statusCode != UA_STATUSCODE_GOOD ||
           bpr.targetsSize < 1)
            return bpr.statusCode;
    
        /* Set the status value */
        UA_Boolean status = true;
        UA_Variant value;
        UA_Variant_setScalar(&value, &status, &UA_TYPES[UA_TYPES_BOOLEAN]);
        UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
        UA_BrowsePathResult_deleteMembers(&bpr);
    
        /* At this point we could replace the node context .. */
    
        return UA_STATUSCODE_GOOD;
    }
    
    static void
    addPumpTypeConstructor(UA_Server *server) {
        UA_NodeTypeLifecycle lifecycle;
        lifecycle.constructor = pumpTypeConstructor;
        lifecycle.destructor = NULL;
        UA_Server_setNodeTypeLifecycle(server, pumpTypeId, lifecycle);
    }

    它遵循主服务器代码,使用上述定义。

    UA_Boolean running = true;
    static void stopHandler(int sign) {
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
        running = false;
    }
    
    int main(void) {
        signal(SIGINT, stopHandler);
        signal(SIGTERM, stopHandler);
    
        UA_ServerConfig *config = UA_ServerConfig_new_default();
        UA_Server *server = UA_Server_new(config);
    
        manuallyDefinePump(server);
        defineObjectTypes(server);
        addPumpObjectInstance(server, "pump2");
        addPumpObjectInstance(server, "pump3");
        addPumpTypeConstructor(server);
        addPumpObjectInstance(server, "pump4");
        addPumpObjectInstance(server, "pump5");
    
        UA_StatusCode retval = UA_Server_run(server, &running);
        UA_Server_delete(server);
        UA_ServerConfig_delete(config);
        return (int)retval;
    }

    向对象添加方法

    OPC UA信息模型中的对象可以包含与编程语言中的对象类似的方法。方法由MethodNode表示。请注意,多个对象可能引用相同的MethodNode。实例化对象类型时,将添加对方法的引用,而不是复制MethodNode。因此,在调用方法时,始终显式声明上下文对象的标识符。

    方法回调将附加到方法节点的自定义数据指针,调用该方法的对象的标识符以及输入和输出参数的两个数组作为输入。输入和输出参数都是Variant类型。每个变体又可以包含任何数据类型的(多维)数组或标量。

    方法参数的约束是根据数据类型,值排名和数组维度(类似于变量定义)定义的。参数定义存储在MethodNode的子VariableNodes中,并带有相应的BrowseNames 和。(0, "InputArguments")(0, "OutputArguments")

    示例:Hello World方法

    该方法采用字符串标量并返回前缀为“Hello”的字符串标量。SDK内部检查输入参数的类型和长度,这样我们就不必验证回调中的参数。

    #include <signal.h>
    #include "open62541.h"
    
    static UA_StatusCode
    helloWorldMethodCallback(UA_Server *server,
                             const UA_NodeId *sessionId, void *sessionHandle,
                             const UA_NodeId *methodId, void *methodContext,
                             const UA_NodeId *objectId, void *objectContext,
                             size_t inputSize, const UA_Variant *input,
                             size_t outputSize, UA_Variant *output) {
        UA_String *inputStr = (UA_String*)input->data;
        UA_String tmp = UA_STRING_ALLOC("Hello ");
        if(inputStr->length > 0) {
            tmp.data = (UA_Byte *)UA_realloc(tmp.data, tmp.length + inputStr->length);
            memcpy(&tmp.data[tmp.length], inputStr->data, inputStr->length);
            tmp.length += inputStr->length;
        }
        UA_Variant_setScalarCopy(output, &tmp, &UA_TYPES[UA_TYPES_STRING]);
        UA_String_deleteMembers(&tmp);
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Hello World was called");
        return UA_STATUSCODE_GOOD;
    }
    
    static void
    addHellWorldMethod(UA_Server *server) {
        UA_Argument inputArgument;
        UA_Argument_init(&inputArgument);
        inputArgument.description = UA_LOCALIZEDTEXT("en-US", "A String");
        inputArgument.name = UA_STRING("MyInput");
        inputArgument.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
        inputArgument.valueRank = UA_VALUERANK_SCALAR;
    
        UA_Argument outputArgument;
        UA_Argument_init(&outputArgument);
        outputArgument.description = UA_LOCALIZEDTEXT("en-US", "A String");
        outputArgument.name = UA_STRING("MyOutput");
        outputArgument.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
        outputArgument.valueRank = UA_VALUERANK_SCALAR;
    
        UA_MethodAttributes helloAttr = UA_MethodAttributes_default;
        helloAttr.description = UA_LOCALIZEDTEXT("en-US","Say `Hello World`");
        helloAttr.displayName = UA_LOCALIZEDTEXT("en-US","Hello World");
        helloAttr.executable = true;
        helloAttr.userExecutable = true;
        UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1,62541),
                                UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                                UA_NODEID_NUMERIC(0, UA_NS0ID_HASORDEREDCOMPONENT),
                                UA_QUALIFIEDNAME(1, "hello world"),
                                helloAttr, &helloWorldMethodCallback,
                                1, &inputArgument, 1, &outputArgument, NULL, NULL);
    }

    增加数组值方法

    该方法采用5个整数和标量的数组作为输入。它返回数组的副本,每个条目都以标量增加。

    static UA_StatusCode
    IncInt32ArrayMethodCallback(UA_Server *server,
                                const UA_NodeId *sessionId, void *sessionContext,
                                const UA_NodeId *methodId, void *methodContext,
                                const UA_NodeId *objectId, void *objectContext,
                                size_t inputSize, const UA_Variant *input,
                                size_t outputSize, UA_Variant *output) {
        UA_Int32 *inputArray = (UA_Int32*)input[0].data;
        UA_Int32 delta = *(UA_Int32*)input[1].data;
    
        /* Copy the input array */
        UA_StatusCode retval = UA_Variant_setArrayCopy(output, inputArray, 5,
                                                       &UA_TYPES[UA_TYPES_INT32]);
        if(retval != UA_STATUSCODE_GOOD)
            return retval;
    
        /* Increate the elements */
        UA_Int32 *outputArray = (UA_Int32*)output->data;
        for(size_t i = 0; i < input->arrayLength; i++)
            outputArray[i] = inputArray[i] + delta;
    
        return UA_STATUSCODE_GOOD;
    }
    
    static void
    addIncInt32ArrayMethod(UA_Server *server) {
        /* Two input arguments */
        UA_Argument inputArguments[2];
        UA_Argument_init(&inputArguments[0]);
        inputArguments[0].description = UA_LOCALIZEDTEXT("en-US", "int32[5] array");
        inputArguments[0].name = UA_STRING("int32 array");
        inputArguments[0].dataType = UA_TYPES[UA_TYPES_INT32].typeId;
        inputArguments[0].valueRank = UA_VALUERANK_ONE_DIMENSION;
        UA_UInt32 pInputDimension = 5;
        inputArguments[0].arrayDimensionsSize = 1;
        inputArguments[0].arrayDimensions = &pInputDimension;
    
        UA_Argument_init(&inputArguments[1]);
        inputArguments[1].description = UA_LOCALIZEDTEXT("en-US", "int32 delta");
        inputArguments[1].name = UA_STRING("int32 delta");
        inputArguments[1].dataType = UA_TYPES[UA_TYPES_INT32].typeId;
        inputArguments[1].valueRank = UA_VALUERANK_SCALAR;
    
        /* One output argument */
        UA_Argument outputArgument;
        UA_Argument_init(&outputArgument);
        outputArgument.description = UA_LOCALIZEDTEXT("en-US", "int32[5] array");
        outputArgument.name = UA_STRING("each entry is incremented by the delta");
        outputArgument.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
        outputArgument.valueRank = UA_VALUERANK_ONE_DIMENSION;
        UA_UInt32 pOutputDimension = 5;
        outputArgument.arrayDimensionsSize = 1;
        outputArgument.arrayDimensions = &pOutputDimension;
    
        /* Add the method node */
        UA_MethodAttributes incAttr = UA_MethodAttributes_default;
        incAttr.description = UA_LOCALIZEDTEXT("en-US", "IncInt32ArrayValues");
        incAttr.displayName = UA_LOCALIZEDTEXT("en-US", "IncInt32ArrayValues");
        incAttr.executable = true;
        incAttr.userExecutable = true;
        UA_Server_addMethodNode(server, UA_NODEID_STRING(1, "IncInt32ArrayValues"),
                                UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                                UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                                UA_QUALIFIEDNAME(1, "IncInt32ArrayValues"),
                                incAttr, &IncInt32ArrayMethodCallback,
                                2, inputArguments, 1, &outputArgument,
                                NULL, NULL);
    }

    它遵循主服务器代码,使用上述定义。

    UA_Boolean running = true;
    static void stopHandler(int sign) {
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
        running = false;
    }
    
    int main(void) {
        signal(SIGINT, stopHandler);
        signal(SIGTERM, stopHandler);
    
        UA_ServerConfig *config = UA_ServerConfig_new_default();
        UA_Server *server = UA_Server_new(config);
    
        addHellWorldMethod(server);
        addIncInt32ArrayMethod(server);
    
        UA_StatusCode retval = UA_Server_run(server, &running);
        UA_Server_delete(server);
        UA_ServerConfig_delete(config);
        return (int)retval;
    }

    构建一个简单的客户端

    您应该已经拥有了之前教程中的基本服务器。open62541提供服务器和客户端API,因此创建客户端就像创建服务器一样简单。将以下内容复制到myClient.c文件中:

    #include <stdio.h>
    #include "open62541.h"
    
    int main(void) {
        UA_Client *client = UA_Client_new(UA_ClientConfig_default);
        UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
        if(retval != UA_STATUSCODE_GOOD) {
            UA_Client_delete(client);
            return (int)retval;
        }
    
        /* Read the value attribute of the node. UA_Client_readValueAttribute is a
         * wrapper for the raw read service available as UA_Client_Service_read. */
        UA_Variant value; /* Variants can hold scalar values and arrays of any type */
        UA_Variant_init(&value);
    
        /* NodeId of the variable holding the current time */
        const UA_NodeId nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
        retval = UA_Client_readValueAttribute(client, nodeId, &value);
    
        if(retval == UA_STATUSCODE_GOOD &&
           UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_DATETIME])) {
            UA_DateTime raw_date = *(UA_DateTime *) value.data;
            UA_DateTimeStruct dts = UA_DateTime_toStruct(raw_date);
            UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "date is: %u-%u-%u %u:%u:%u.%03u\n",
                        dts.day, dts.month, dts.year, dts.hour, dts.min, dts.sec, dts.milliSec);
        }
    
        /* Clean up */
        UA_Variant_deleteMembers(&value);
        UA_Client_delete(client); /* Disconnects the client internally */
        return UA_STATUSCODE_GOOD;
    }

    编译类似于服务器示例。

    $ gcc -std=c99 open62541.c myClient.c -o myClient

    在MinGW环境中,必须添加Winsock库。

    $ gcc -std=c99 open62541.c myClient.c -lws2_32 -o myClient.exe

    进一步的任务

    • 尝试通过更改opc.tcp://localhost:4840为适当的地址来连接到其他OPC UA服务器 (请记住,查询的节点包含在任何OPC UA服务器中)。
    • 尝试使用“UA_Client_write”函数设置包含Int32示例服务器(在构建简单服务器中 构建)的变量节点(ns = 1,i =“the.answer”)的值。示例服务器需要一些更改,即更改请求类型。答案可以在“examples / exampleClient.c”中找到。
    • 协议

      在本节中,我们将概述OPC UA二进制协议。我们专注于二进制,因为这是在open62541中实现的。基于TCP的二进制协议是迄今为止OPC UA最常用的传输层。一般概念还转换为标准中定义的HTTP和基于SOAP的通信。通过以下关键原则开始,可以最好地理解OPC UA中的通信:

      请求/响应

      所有通信都基于请求/响应模式。只有客户端才能向服务器发送请求。服务器只能发送对请求的响应。通常,服务器托管在(物理)设备上,例如传感器或机床。

      异步响应

      服务器不必立即响应请求,并且响应可以以不同的顺序发送。这在需要时间直到特定请求被处理(例如方法调用或从具有延迟的传感器读取)时保持服务器响应。此外,订阅(也称为推送通知)是通过特殊请求实现的,其中响应被延迟直到生成通知。

      建立连接

      OPC UA中的客户端 - 服务器连接由三个嵌套级别组成:原始连接,SecureChannel和会话。有关完整的详细信息,请参阅OPC UA标准的第6部分。

      原始连接

      通过打开与相应主机名和端口的TCP连接以及初始HEL / ACK握手来创建原始连接。握手建立连接的基本设置,例如最大消息长度。

      SecureChannel

      SecureChannels是在原始TCP连接之上创建的。使用OpenSecureChannel请求和响应消息对建立SecureChannel。 注意!即使SecureChannel是必需的,加密仍可能被禁用。所述SECURITYMODE一个SecureChannel的可以是 NoneSign,或SignAndEncrypt。截至open62541的0.2版本,消息签名和加密仍在进行中。

      启用消息签名或加密后,OpenSecureChannel消息将使用非对称加密算法(公钥加密)[1]进行加密。作为OpenSecureChannel消息的一部分,客户端和服务器在最初不安全的通道上建立共同的秘密。对于后续消息,公共秘密用于对称加密,其优点是速度更快。

      不同的SecurityPolicies - 在OPC UA标准的第7部分中定义 - 指定非对称和对称加密的算法,加密密钥长度,消息签名的散列函数等。示例SecurityPolicies None用于传输明文, Basic256Sha256并强制要求使用用于非对称加密的SHA256证书散列和用于对称加密的AES256的RSA变体。

      使用端点列表描述服务器的可能SecurityPolicies 。端点共同定义SecurityMode,SecurityPolicy和用于验证会话的方法(在下一节中讨论)以便连接到某个服务器。该GetEndpoints服务返回可用端点的列表。通常可以在没有会话和未加密的SecureChannel的情况下调用此服务。这允许客户端首先发现可用的端点,然后使用可能需要打开会话的相应SecurityPolicy。

      会议

      会话在SecureChannel之上创建。这可确保用户可以在不以明文形式发送其凭据(例如用户名和密码)的情况下进行身份验证。当前定义的身份验证机制是匿名登录,用户名/密码,Kerberos和x509证书。后者要求请求消息附有签名,以证明发件人拥有创建证书的私钥。

      建立会话需要两个消息交换: CreateSessionActicateSession。ActivateSession服务可用于将现有会话切换到其他SecureChannel。这很重要,例如,当连接中断并且现有会话与新的SecureChannel一起重用时。

      [1]这需要客户端和服务器交换所谓的公钥。公钥可能带有来自密钥签名机构的证书,或者可以通过外部密钥存储库进行验证。但我们不会在本节中详细讨论证书管理。

      协议消息的结构

      有关OPC UA协议消息结构的以下介绍,请考虑使用Wireshark工具记录和显示的示例OPC UA二进制会话, 如图1所示。

      Wireshark中的OPC UA对话

      图1 Wireshark中显示的OPC UA对话

      Wireshark窗口的顶部按顺序显示来自对话的消息。绿线包含应用的过滤器。在这里,我们只想查看OPC UA协议消息。第一条消息(来自TCP数据包49到56)显示客户端打开未加密的SecureChannel并检索服务器的端点。然后,从分组63开始,根据其中一个端点创建新连接和SecureChannel。在此SecureChannel之上,客户端可以创建并激活会话。选择以下ReadRequest 消息,并在底部窗口中更详细地介绍。

      左下方窗口显示所选ReadRequest 消息的结构。消息的目的是调用Read 服务。消息被构造为标题和消息正文。请注意,我们不会在此处考虑加密或签名消息。

      消息标题

      如前所述,OPC UA定义了一种异步协议。因此,回复可能会失灵。消息头包含一些基本信息,例如消息的长度,以及将消息与SecureChannel相关联的必要信息以及对相应响应的每个请求。“分块”是指比最大网络分组大小更长的消息的分割和重组。

      邮件正文

      每个OPC UA 服务都有一个请求和响应数据结构形式的签名。这些是根据OPC UA协议类型系统定义的。请参阅与服务请求和响应对应的数据类型的自动生成的类型定义。消息正文以以下数据类型的标识符开头。然后,消息的主要有效负载如下。

      右下方窗口显示所选ReadRequest 消息的二进制有效负载。邮件标题以浅灰色突出显示。蓝色突出显示的消息正文显示编码的ReadRequest数据结构。

    • 数据类型

      OPC UA协议定义了25种内置数据类型和三种将它们组合成高阶类型的方法:数组,结构和联合。在open62541中,仅手动定义内置数据类型。所有其他数据类型都是从标准XML定义生成的。可以在https://opcfoundation.org/UA/schemas/Opc.Ua.Types.bsd.xml查找其确切定义。

      对于初次使用open62541的用户,在深入了解实现细节之前,请先阅读使用数据类型教程

      内置类型

      布尔

      双态逻辑值(true或false)。

      typedef bool UA_Boolean;
      #define UA_TRUE true
      #define UA_FALSE false

      SByte 

      -128到127之间的整数值。

      typedef int8_t UA_SByte;
      #define UA_SBYTE_MIN (-128)
      #define UA_SBYTE_MAX 127

      字节

      0到255之间的整数值。

      typedef uint8_t UA_Byte;
      #define UA_BYTE_MIN 0
      #define UA_BYTE_MAX 255

      INT16 

      介于-32 768和32 767之间的整数值。

      typedef int16_t UA_Int16;
      #define UA_INT16_MIN (-32768)
      #define UA_INT16_MAX 32767

      UINT16 

      介于0和65之间的整数值535。

      typedef uint16_t UA_UInt16;
      #define UA_UINT16_MIN 0
      #define UA_UINT16_MAX 65535

      INT32 

      -2 147 483 648和2 147 483 647之间的整数值。

      typedef int32_t UA_Int32;
      #define UA_INT32_MIN (-2147483648)
      #define UA_INT32_MAX 2147483647

      UInt32的

      介于0和4之间的整数值294 967 295。

      typedef uint32_t UA_UInt32;
      #define UA_UINT32_MIN 0
      #define UA_UINT32_MAX 4294967295

      Int64的

      介于-9 223 372 036 854 775 808和9 223 372 036 854 775 807之间的整数值。

      typedef int64_t UA_Int64;
      #define UA_INT64_MIN ((int64_t)-9223372036854775808)
      #define UA_INT64_MAX (int64_t)9223372036854775807

      UINT64 

      介于0和18之间的整数值446 744 073 709 551 615。

      typedef uint64_t UA_UInt64;
      #define UA_UINT64_MIN (int64_t)0
      #define UA_UINT64_MAX (int64_t)18446744073709551615

      浮点

      IEEE单精度(32位)浮点值。

      typedef float UA_Float;

      IEEE双精度(64位)浮点值。

      typedef double UA_Double;

      的StatusCode 

      与值或操作关联的错误或条件的数字标识符。有关特定代码的含义,请参阅StatusCodes部分。

      typedef uint32_t UA_StatusCode;
      
      /* Returns the human-readable name of the StatusCode. If no matching StatusCode
       * is found, a default string for "Unknown" is returned. This feature might be
       * disabled to create a smaller binary with the
       * UA_ENABLE_STATUSCODE_DESCRIPTIONS build-flag. Then the function returns an
       * empty string for every StatusCode. */
      const char *
      UA_StatusCode_name(UA_StatusCode code);

      字符串

      一系列Unicode字符。字符串只是一个UA_Byte数组。

      typedef struct {
          size_t length; /* The length of the string */
          UA_Byte *data; /* The content (not null-terminated) */
      } UA_String;
      
      /* Copies the content on the heap. Returns a null-string when alloc fails */
      UA_String UA_String_fromChars(char const src[]);
      
      UA_Boolean UA_String_equal(const UA_String *s1, const UA_String *s2);
      
      extern const UA_String UA_STRING_NULL;

      UA_STRING返回指向原始char数组的字符串。 UA_STRING_ALLOC是简历,UA_String_fromChars并制作char数组的副本。

      static UA_INLINE UA_String
      UA_STRING(char *chars) {
          UA_String str; str.length = strlen(chars);
          str.data = (UA_Byte*)chars; return str;
      }
      
      #define UA_STRING_ALLOC(CHARS) UA_String_fromChars(CHARS)
      
      /* Define strings at compile time (in ROM) */
      #define UA_STRING_STATIC(CHARS) {sizeof(CHARS)-1, (UA_Byte*)CHARS}

      日期时间

      一个实例。DateTime值被编码为64位有符号整数,表示自1601年1月1日(UTC)以来100纳秒间隔的数量。

      提供系统时钟接口的方法由与图书馆静态链接的“插件”提供。

      typedef int64_t UA_DateTime;
      
      /* Multiples to convert durations to DateTime */
      #define UA_DATETIME_USEC 10LL
      #define UA_DATETIME_MSEC (UA_DATETIME_USEC * 1000LL)
      #define UA_DATETIME_SEC (UA_DATETIME_MSEC * 1000LL)
      
      /* The current time in UTC time */
      UA_DateTime UA_DateTime_now(void);
      
      /* Offset between local time and UTC time */
      UA_Int64 UA_DateTime_localTimeUtcOffset(void);
      
      /* CPU clock invariant to system time changes. Use only to measure durations,
       * not absolute time. */
      UA_DateTime UA_DateTime_nowMonotonic(void);
      
      /* Represents a Datetime as a structure */
      typedef struct UA_DateTimeStruct {
          UA_UInt16 nanoSec;
          UA_UInt16 microSec;
          UA_UInt16 milliSec;
          UA_UInt16 sec;
          UA_UInt16 min;
          UA_UInt16 hour;
          UA_UInt16 day;
          UA_UInt16 month;
          UA_UInt16 year;
      } UA_DateTimeStruct;
      
      UA_DateTimeStruct UA_DateTime_toStruct(UA_DateTime t);
      
      /* The C99 standard (7.23.1) says: "The range and precision of times
       * representable in clock_t and time_t are implementation-defined." On most
       * systems, time_t is a 4 or 8 byte integer counting seconds since the UTC Unix
       * epoch. The following methods are used for conversion. */
      
      /* Datetime of 1 Jan 1970 00:00 */
      #define UA_DATETIME_UNIX_EPOCH (11644473600LL * UA_DATETIME_SEC)
      
      static UA_INLINE UA_Int64
      UA_DateTime_toUnixTime(UA_DateTime date) {
          return (date - UA_DATETIME_UNIX_EPOCH) / UA_DATETIME_SEC;
      }
      
      static UA_INLINE UA_DateTime
      UA_DateTime_fromUnixTime(UA_Int64 unixDate) {
          return (unixDate * UA_DATETIME_SEC) + UA_DATETIME_UNIX_EPOCH;
      }

      GUID 

      一个16字节的值,可用作全局唯一标识符。

      typedef struct {
          UA_UInt32 data1;
          UA_UInt16 data2;
          UA_UInt16 data3;
          UA_Byte   data4[8];
      } UA_Guid;
      
      UA_Boolean UA_Guid_equal(const UA_Guid *g1, const UA_Guid *g2);
      
      extern const UA_Guid UA_GUID_NULL;

      字节字符串

      一系列八位字节。

      typedef UA_String UA_ByteString;
      
      static UA_INLINE UA_Boolean
      UA_ByteString_equal(const UA_ByteString *string1,
                          const UA_ByteString *string2) {
          return UA_String_equal((const UA_String*)string1,
                                 (const UA_String*)string2);
      }
      
      /* Allocates memory of size length for the bytestring.
       * The content is not set to zero. */
      UA_StatusCode
      UA_ByteString_allocBuffer(UA_ByteString *bs, size_t length);
      
      extern const UA_ByteString UA_BYTESTRING_NULL;
      
      static UA_INLINE UA_ByteString
      UA_BYTESTRING(char *chars) {
          UA_ByteString str; str.length = strlen(chars);
          str.data = (UA_Byte*)chars; return str;
      }
      
      static UA_INLINE UA_ByteString
      UA_BYTESTRING_ALLOC(const char *chars) {
          UA_String str = UA_String_fromChars(chars); UA_ByteString bstr;
          bstr.length = str.length; bstr.data = str.data; return bstr;
      }

      XmlElement

      一个XML元素。

      typedef UA_String UA_XmlElement;

      NodeId 

      OPC UA服务器地址空间中节点的标识符。

      enum UA_NodeIdType {
          UA_NODEIDTYPE_NUMERIC    = 0, /* In the binary encoding, this can also
                                           become 1 or 2 (2byte and 4byte encoding of
                                           small numeric nodeids) */
          UA_NODEIDTYPE_STRING     = 3,
          UA_NODEIDTYPE_GUID       = 4,
          UA_NODEIDTYPE_BYTESTRING = 5
      };
      
      typedef struct {
          UA_UInt16 namespaceIndex;
          enum UA_NodeIdType identifierType;
          union {
              UA_UInt32     numeric;
              UA_String     string;
              UA_Guid       guid;
              UA_ByteString byteString;
          } identifier;
      } UA_NodeId;
      
      extern const UA_NodeId UA_NODEID_NULL;
      
      UA_Boolean UA_NodeId_isNull(const UA_NodeId *p);
      
      UA_Boolean UA_NodeId_equal(const UA_NodeId *n1, const UA_NodeId *n2);
      
      /* Returns a non-cryptographic hash for the NodeId */
      UA_UInt32 UA_NodeId_hash(const UA_NodeId *n);

      以下函数是创建NodeId的简写。

      static UA_INLINE UA_NodeId
      UA_NODEID_NUMERIC(UA_UInt16 nsIndex, UA_UInt32 identifier) {
          UA_NodeId id; id.namespaceIndex = nsIndex;
          id.identifierType = UA_NODEIDTYPE_NUMERIC;
          id.identifier.numeric = identifier; return id;
      }
      
      static UA_INLINE UA_NodeId
      UA_NODEID_STRING(UA_UInt16 nsIndex, char *chars) {
          UA_NodeId id; id.namespaceIndex = nsIndex;
          id.identifierType = UA_NODEIDTYPE_STRING;
          id.identifier.string = UA_STRING(chars); return id;
      }
      
      static UA_INLINE UA_NodeId
      UA_NODEID_STRING_ALLOC(UA_UInt16 nsIndex, const char *chars) {
          UA_NodeId id; id.namespaceIndex = nsIndex;
          id.identifierType = UA_NODEIDTYPE_STRING;
          id.identifier.string = UA_STRING_ALLOC(chars); return id;
      }
      
      static UA_INLINE UA_NodeId
      UA_NODEID_GUID(UA_UInt16 nsIndex, UA_Guid guid) {
          UA_NodeId id; id.namespaceIndex = nsIndex;
          id.identifierType = UA_NODEIDTYPE_GUID;
          id.identifier.guid = guid; return id;
      }
      
      static UA_INLINE UA_NodeId
      UA_NODEID_BYTESTRING(UA_UInt16 nsIndex, char *chars) {
          UA_NodeId id; id.namespaceIndex = nsIndex;
          id.identifierType = UA_NODEIDTYPE_BYTESTRING;
          id.identifier.byteString = UA_BYTESTRING(chars); return id;
      }
      
      static UA_INLINE UA_NodeId
      UA_NODEID_BYTESTRING_ALLOC(UA_UInt16 nsIndex, const char *chars) {
          UA_NodeId id; id.namespaceIndex = nsIndex;
          id.identifierType = UA_NODEIDTYPE_BYTESTRING;
          id.identifier.byteString = UA_BYTESTRING_ALLOC(chars); return id;
      }

      ExpandedNodeId 

      NodeId,允许指定名称空间URI而不是索引。

      typedef struct {
          UA_NodeId nodeId;
          UA_String namespaceUri;
          UA_UInt32 serverIndex;
      } UA_ExpandedNodeId;
      
      UA_Boolean UA_ExpandedNodeId_equal(const UA_ExpandedNodeId *n1,
                                                   const UA_ExpandedNodeId *n2);
      
      extern const UA_ExpandedNodeId UA_EXPANDEDNODEID_NULL;

      以下函数是创建ExpandedNodeIds的简写。

      static UA_INLINE UA_ExpandedNodeId
      UA_EXPANDEDNODEID_NUMERIC(UA_UInt16 nsIndex, UA_UInt32 identifier) {
          UA_ExpandedNodeId id; id.nodeId = UA_NODEID_NUMERIC(nsIndex, identifier);
          id.serverIndex = 0; id.namespaceUri = UA_STRING_NULL; return id;
      }
      
      static UA_INLINE UA_ExpandedNodeId
      UA_EXPANDEDNODEID_STRING(UA_UInt16 nsIndex, char *chars) {
          UA_ExpandedNodeId id; id.nodeId = UA_NODEID_STRING(nsIndex, chars);
          id.serverIndex = 0; id.namespaceUri = UA_STRING_NULL; return id;
      }
      
      static UA_INLINE UA_ExpandedNodeId
      UA_EXPANDEDNODEID_STRING_ALLOC(UA_UInt16 nsIndex, const char *chars) {
          UA_ExpandedNodeId id; id.nodeId = UA_NODEID_STRING_ALLOC(nsIndex, chars);
          id.serverIndex = 0; id.namespaceUri = UA_STRING_NULL; return id;
      }
      
      static UA_INLINE UA_ExpandedNodeId
      UA_EXPANDEDNODEID_STRING_GUID(UA_UInt16 nsIndex, UA_Guid guid) {
          UA_ExpandedNodeId id; id.nodeId = UA_NODEID_GUID(nsIndex, guid);
          id.serverIndex = 0; id.namespaceUri = UA_STRING_NULL; return id;
      }
      
      static UA_INLINE UA_ExpandedNodeId
      UA_EXPANDEDNODEID_BYTESTRING(UA_UInt16 nsIndex, char *chars) {
          UA_ExpandedNodeId id; id.nodeId = UA_NODEID_BYTESTRING(nsIndex, chars);
          id.serverIndex = 0; id.namespaceUri = UA_STRING_NULL; return id;
      }
      
      static UA_INLINE UA_ExpandedNodeId
      UA_EXPANDEDNODEID_BYTESTRING_ALLOC(UA_UInt16 nsIndex, const char *chars) {
          UA_ExpandedNodeId id; id.nodeId = UA_NODEID_BYTESTRING_ALLOC(nsIndex, chars);
          id.serverIndex = 0; id.namespaceUri = UA_STRING_NULL; return id;
      }

      QualifiedName的

      由命名空间限定的名称。

      typedef struct {
          UA_UInt16 namespaceIndex;
          UA_String name;
      } UA_QualifiedName;
      
      static UA_INLINE UA_Boolean
      UA_QualifiedName_isNull(const UA_QualifiedName *q) {
          return (q->namespaceIndex == 0 && q->name.length == 0);
      }
      
      static UA_INLINE UA_QualifiedName
      UA_QUALIFIEDNAME(UA_UInt16 nsIndex, char *chars) {
          UA_QualifiedName qn; qn.namespaceIndex = nsIndex;
          qn.name = UA_STRING(chars); return qn;
      }
      
      static UA_INLINE UA_QualifiedName
      UA_QUALIFIEDNAME_ALLOC(UA_UInt16 nsIndex, const char *chars) {
          UA_QualifiedName qn; qn.namespaceIndex = nsIndex;
          qn.name = UA_STRING_ALLOC(chars); return qn;
      }
      
      UA_Boolean
      UA_QualifiedName_equal(const UA_QualifiedName *qn1,
                             const UA_QualifiedName *qn2);

      LocalizedText 

      具有可选区域设置标识符的人类可读文本。

      typedef struct {
          UA_String locale;
          UA_String text;
      } UA_LocalizedText;
      
      static UA_INLINE UA_LocalizedText
      UA_LOCALIZEDTEXT(char *locale, char *text) {
          UA_LocalizedText lt; lt.locale = UA_STRING(locale);
          lt.text = UA_STRING(text); return lt;
      }
      
      static UA_INLINE UA_LocalizedText
      UA_LOCALIZEDTEXT_ALLOC(const char *locale, const char *text) {
          UA_LocalizedText lt; lt.locale = UA_STRING_ALLOC(locale);
          lt.text = UA_STRING_ALLOC(text); return lt;
      }

      NumericRange 

      NumericRanges用于指示(多维)数组的子集。它们在OPC UA标准中没有官方数据类型,只能使用字符串编码传输,例如“1:2,0:3,5”。冒号分隔最小/最大索引,逗号分隔维度。单个值表示具有单个元素的范围(min == max)。

      typedef struct {
          UA_UInt32 min;
          UA_UInt32 max;
      } UA_NumericRangeDimension;
      
      typedef struct  {
          size_t dimensionsSize;
          UA_NumericRangeDimension *dimensions;
      } UA_NumericRange;

      变种

      变体可以包含任何类型的值以及内容的描述。有关如何描述类型的信息,请参阅“ 通用类型处理 ”一节。标准要求变体仅包含内置数据类型。如果该值不是内置类型,则将其包装到ExtensionObject中。open62541在编码层中透明地隐藏了这个包装。如果接收方未知数据类型,则变体包含二进制或XML编码的原始ExtensionObject。

      变体可能包含标量值或数组。有关数组处理的详细信息,请参阅有关数组处理的部分。数组变体可以具有在维度长度数组中定义的附加维度(矩阵,3张量,...)。实际值保存在维度为1的数组中。对于直接使用高维数组的用户,请记住,较高等级的维度首先被序列化(最高等级维度具有步幅1并且元素直接相互跟随)。通常,最简单的方法是通过UA_NumericRange 描述与更高维数组进行交互(参见数组处理)。

      为了区分标量/数组变体,使用以下定义。UA_Variant_isScalar提供对这些检查的简化访问。

    • arrayLength == 0 && data == NULL:未定义的长度为-1的数组
    • arrayLength == 0 && data == UA_EMPTY_ARRAY_SENTINEL:长度为0的数组
    • arrayLength == 0 && data > UA_EMPTY_ARRAY_SENTINEL:标量值
    • arrayLength > 0:给定长度的数组
    • 变体也可以是空的。然后,指向类型描述的指针是 NULL

      /* Forward declaration. See the section on Generic Type Handling */
      struct UA_DataType;
      typedef struct UA_DataType UA_DataType;
      
      #define UA_EMPTY_ARRAY_SENTINEL ((void*)0x01)
      
      typedef enum {
          UA_VARIANT_DATA,          /* The data has the same lifecycle as the
                                       variant */
          UA_VARIANT_DATA_NODELETE /* The data is "borrowed" by the variant and
                                       shall not be deleted at the end of the
                                       variant's lifecycle. */
      } UA_VariantStorageType;
      
      typedef struct {
          const UA_DataType *type;      /* The data type description */
          UA_VariantStorageType storageType;
          size_t arrayLength;           /* The number of elements in the data array */
          void *data;                   /* Points to the scalar or array data */
          size_t arrayDimensionsSize;   /* The number of dimensions */
          UA_UInt32 *arrayDimensions;   /* The length of each dimension */
      } UA_Variant;
      
      /* Returns true if the variant has no value defined (contains neither an array
       * nor a scalar value).
       *
       * @param v The variant
       * @return Is the variant empty */
      static UA_INLINE UA_Boolean
      UA_Variant_isEmpty(const UA_Variant *v) {
          return v->type == NULL;
      }
      
      /* Returns true if the variant contains a scalar value. Note that empty variants
       * contain an array of length -1 (undefined).
       *
       * @param v The variant
       * @return Does the variant contain a scalar value */
      static UA_INLINE UA_Boolean
      UA_Variant_isScalar(const UA_Variant *v) {
          return (v->arrayLength == 0 && v->data > UA_EMPTY_ARRAY_SENTINEL);
      }
      
      /* Returns true if the variant contains a scalar value of the given type.
       *
       * @param v The variant
       * @param type The data type
       * @return Does the variant contain a scalar value of the given type */
      static UA_INLINE UA_Boolean
      UA_Variant_hasScalarType(const UA_Variant *v, const UA_DataType *type) {
          return UA_Variant_isScalar(v) && type == v->type;
      }
      
      /* Returns true if the variant contains an array of the given type.
       *
       * @param v The variant
       * @param type The data type
       * @return Does the variant contain an array of the given type */
      static UA_INLINE UA_Boolean
      UA_Variant_hasArrayType(const UA_Variant *v, const UA_DataType *type) {
          return (!UA_Variant_isScalar(v)) && type == v->type;
      }
      
      /* Set the variant to a scalar value that already resides in memory. The value
       * takes on the lifecycle of the variant and is deleted with it.
       *
       * @param v The variant
       * @param p A pointer to the value data
       * @param type The datatype of the value in question */
      void
      UA_Variant_setScalar(UA_Variant *v, void *p,
                           const UA_DataType *type);
      
      /* Set the variant to a scalar value that is copied from an existing variable.
       * @param v The variant
       * @param p A pointer to the value data
       * @param type The datatype of the value
       * @return Indicates whether the operation succeeded or returns an error code */
      UA_StatusCode
      UA_Variant_setScalarCopy(UA_Variant *v, const void *p,
                               const UA_DataType *type);
      
      /* Set the variant to an array that already resides in memory. The array takes
       * on the lifecycle of the variant and is deleted with it.
       *
       * @param v The variant
       * @param array A pointer to the array data
       * @param arraySize The size of the array
       * @param type The datatype of the array */
      void
      UA_Variant_setArray(UA_Variant *v, void *array,
                          size_t arraySize, const UA_DataType *type);
      
      /* Set the variant to an array that is copied from an existing array.
       *
       * @param v The variant
       * @param array A pointer to the array data
       * @param arraySize The size of the array
       * @param type The datatype of the array
       * @return Indicates whether the operation succeeded or returns an error code */
      UA_StatusCode
      UA_Variant_setArrayCopy(UA_Variant *v, const void *array,
                              size_t arraySize, const UA_DataType *type);
      
      /* Copy the variant, but use only a subset of the (multidimensional) array into
       * a variant. Returns an error code if the variant is not an array or if the
       * indicated range does not fit.
       *
       * @param src The source variant
       * @param dst The target variant
       * @param range The range of the copied data
       * @return Returns UA_STATUSCODE_GOOD or an error code */
      UA_StatusCode
      UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst,
                           const UA_NumericRange range);
      
      /* Insert a range of data into an existing variant. The data array can't be
       * reused afterwards if it contains types without a fixed size (e.g. strings)
       * since the members are moved into the variant and take on its lifecycle.
       *
       * @param v The variant
       * @param dataArray The data array. The type must match the variant
       * @param dataArraySize The length of the data array. This is checked to match
       *        the range size.
       * @param range The range of where the new data is inserted
       * @return Returns UA_STATUSCODE_GOOD or an error code */
      UA_StatusCode
      UA_Variant_setRange(UA_Variant *v, void *array,
                          size_t arraySize, const UA_NumericRange range);
      
      /* Deep-copy a range of data into an existing variant.
       *
       * @param v The variant
       * @param dataArray The data array. The type must match the variant
       * @param dataArraySize The length of the data array. This is checked to match
       *        the range size.
       * @param range The range of where the new data is inserted
       * @return Returns UA_STATUSCODE_GOOD or an error code */
      UA_StatusCode
      UA_Variant_setRangeCopy(UA_Variant *v, const void *array,
                              size_t arraySize, const UA_NumericRange range);

      ExtensionObject 

      ExtensionObjects可能包含任何数据类型的标量。即使那些接收器不知道的人。有关如何描述类型的信息,请参阅“ 通用类型处理 ”一节。如果接收的数据类型未知,则存储编码的字符串和目标NodeId而不是解码的值。

      typedef enum {
          UA_EXTENSIONOBJECT_ENCODED_NOBODY     = 0,
          UA_EXTENSIONOBJECT_ENCODED_BYTESTRING = 1,
          UA_EXTENSIONOBJECT_ENCODED_XML        = 2,
          UA_EXTENSIONOBJECT_DECODED            = 3,
          UA_EXTENSIONOBJECT_DECODED_NODELETE   = 4 /* Don't delete the content
                                                       together with the
                                                       ExtensionObject */
      } UA_ExtensionObjectEncoding;
      
      typedef struct {
          UA_ExtensionObjectEncoding encoding;
          union {
              struct {
                  UA_NodeId typeId;   /* The nodeid of the datatype */
                  UA_ByteString body; /* The bytestring of the encoded data */
              } encoded;
              struct {
                  const UA_DataType *type;
                  void *data;
              } decoded;
          } content;
      } UA_ExtensionObject;

      DataValue 

      具有关联状态代码和时间戳的数据值。

      typedef struct {
          UA_Boolean    hasValue             : 1;
          UA_Boolean    hasStatus            : 1;
          UA_Boolean    hasSourceTimestamp   : 1;
          UA_Boolean    hasServerTimestamp   : 1;
          UA_Boolean    hasSourcePicoseconds : 1;
          UA_Boolean    hasServerPicoseconds : 1;
          UA_Variant    value;
          UA_StatusCode status;
          UA_DateTime   sourceTimestamp;
          UA_UInt16     sourcePicoseconds;
          UA_DateTime   serverTimestamp;
          UA_UInt16     serverPicoseconds;
      } UA_DataValue;

      DiagnosticInfo 

      包含与StatusCode关联的详细错误和诊断信息的结构。

      typedef struct UA_DiagnosticInfo {
          UA_Boolean    hasSymbolicId          : 1;
          UA_Boolean    hasNamespaceUri        : 1;
          UA_Boolean    hasLocalizedText       : 1;
          UA_Boolean    hasLocale              : 1;
          UA_Boolean    hasAdditionalInfo      : 1;
          UA_Boolean    hasInnerStatusCode     : 1;
          UA_Boolean    hasInnerDiagnosticInfo : 1;
          UA_Int32      symbolicId;
          UA_Int32      namespaceUri;
          UA_Int32      localizedText;
          UA_Int32      locale;
          UA_String     additionalInfo;
          UA_StatusCode innerStatusCode;
          struct UA_DiagnosticInfo *innerDiagnosticInfo;
      } UA_DiagnosticInfo;

      通用类型处理

      有关(内置/结构化)数据类型的所有信息都存储在a中 UA_DataType。该数组UA_TYPES包含所有标准定义类型的描述。此类型描述用于以下适用于所有类型的通用操作:

    • void T_init(T *ptr):初始化数据类型。这与清零记忆是同义词,即。memset(ptr, 0, sizeof(T))
    • T* T_new():分配并返回数据类型的内存。该值已初始化。
    • UA_StatusCode T_copy(const T *src, T *dst):复制数据类型的内容。返回UA_STATUSCODE_GOODUA_STATUSCODE_BADOUTOFMEMORY
    • void T_deleteMembers(T *ptr):删除动态分配的数据类型内容并执行a T_init重置类型。
    • void T_delete(T *ptr):删除数据类型的内容和数据类型本身的内存。
    • 生成的数据类型定义

      以XML格式定义自动生成以下数据类型。

    • 在包含类型描述的数组中为每个类型分配一个索引。这些描述在类型处理(复制,删除,二进制编码......)中使用。

      #define UA_TYPES_COUNT 199
      extern const UA_DataType UA_TYPES[UA_TYPES_COUNT];

      布尔

      #define UA_TYPES_BOOLEAN 0

      为SByte 

      #define UA_TYPES_SBYTE 1

      字节

      #define UA_TYPES_BYTE 2

      INT16 

      #define UA_TYPES_INT16 3

      UINT16 

      #define UA_TYPES_UINT16 4

      INT32 

      #define UA_TYPES_INT32 5

      UInt32的

      #define UA_TYPES_UINT32 6

      Int64的

      #define UA_TYPES_INT64 7

      UINT64 

      #define UA_TYPES_UINT64 8

      浮点

      #define UA_TYPES_FLOAT 9

      #define UA_TYPES_DOUBLE 10

      字符串

      #define UA_TYPES_STRING 11

      日期时间

      #define UA_TYPES_DATETIME 12

      GUID 

      #define UA_TYPES_GUID 13

      字节字符串

      #define UA_TYPES_BYTESTRING 14

      XmlElement的

      #define UA_TYPES_XMLELEMENT 15

      的NodeId 

      #define UA_TYPES_NODEID 16

      ExpandedNodeId 

      #define UA_TYPES_EXPANDEDNODEID 17

      的StatusCode 

      #define UA_TYPES_STATUSCODE 18

      QualifiedName的

      #define UA_TYPES_QUALIFIEDNAME 19

      LocalizedText 

      #define UA_TYPES_LOCALIZEDTEXT 20

      ExtensionObject 

      #define UA_TYPES_EXTENSIONOBJECT 21

      DataValue 

      #define UA_TYPES_DATAVALUE 22

      变种

      #define UA_TYPES_VARIANT 23

      DiagnosticInfo 

      #define UA_TYPES_DIAGNOSTICINFO 24

      SignedSoftwareCertificate 

      带有数字签名的软件证书。

      typedef struct {
          UA_ByteString certificateData;
          UA_ByteString signature;
      } UA_SignedSoftwareCertificate;
      
      #define UA_TYPES_SIGNEDSOFTWARECERTIFICATE 25

      SemanticChangeStructureDataType 

      typedef struct {
          UA_NodeId affected;
          UA_NodeId affectedType;
      } UA_SemanticChangeStructureDataType;
      
      #define UA_TYPES_SEMANTICCHANGESTRUCTUREDATATYPE 26

      StatusChangeNotification 

      typedef struct {
          UA_StatusCode status;
          UA_DiagnosticInfo diagnosticInfo;
      } UA_StatusChangeNotification;
      
      #define UA_TYPES_STATUSCHANGENOTIFICATION 27

      BrowsePathTarget 

      翻译路径的目标。

      typedef struct {
          UA_ExpandedNodeId targetId;
          UA_UInt32 remainingPathIndex;
      } UA_BrowsePathTarget;
      
      #define UA_TYPES_BROWSEPATHTARGET 28

      ViewAttributes 

      视图节点的属性。

      typedef struct {
          UA_UInt32 specifiedAttributes;
          UA_LocalizedText displayName;
          UA_LocalizedText description;
          UA_UInt32 writeMask;
          UA_UInt32 userWriteMask;
          UA_Boolean containsNoLoops;
          UA_Byte eventNotifier;
      } UA_ViewAttributes;
      
      #define UA_TYPES_VIEWATTRIBUTES 29

      RequestHeader 

      每个服务器请求传递的标头。

      typedef struct {
          UA_NodeId authenticationToken;
          UA_DateTime timestamp;
          UA_UInt32 requestHandle;
          UA_UInt32 returnDiagnostics;
          UA_String auditEntryId;
          UA_UInt32 timeoutHint;
          UA_ExtensionObject additionalHeader;
      } UA_RequestHeader;
      
      #define UA_TYPES_REQUESTHEADER 30

      MonitoredItemModifyResult 

      typedef struct {
          UA_StatusCode statusCode;
          UA_Double revisedSamplingInterval;
          UA_UInt32 revisedQueueSize;
          UA_ExtensionObject filterResult;
      } UA_MonitoredItemModifyResult;
      
      #define UA_TYPES_MONITOREDITEMMODIFYRESULT 31

      ElementOperand 

      typedef struct {
          UA_UInt32 index;
      } UA_ElementOperand;
      
      #define UA_TYPES_ELEMENTOPERAND 32

      CloseSecureChannelRequest 

      关闭安全通道。

      typedef struct {
          UA_RequestHeader requestHeader;
      } UA_CloseSecureChannelRequest;
      
      #define UA_TYPES_CLOSESECURECHANNELREQUEST 33

      AddNodesResult 

      添加节点操作的结果。

      typedef struct {
          UA_StatusCode statusCode;
          UA_NodeId addedNodeId;
      } UA_AddNodesResult;
      
      #define UA_TYPES_ADDNODESRESULT 34

      VariableAttributes 

      变量节点的属性。

      typedef struct {
          UA_UInt32 specifiedAttributes;
          UA_LocalizedText displayName;
          UA_LocalizedText description;
          UA_UInt32 writeMask;
          UA_UInt32 userWriteMask;
          UA_Variant value;
          UA_NodeId dataType;
          UA_Int32 valueRank;
          size_t arrayDimensionsSize;
          UA_UInt32 *arrayDimensions;
          UA_Byte accessLevel;
          UA_Byte userAccessLevel;
          UA_Double minimumSamplingInterval;
          UA_Boolean historizing;
      } UA_VariableAttributes;
      
      #define UA_TYPES_VARIABLEATTRIBUTES 35

      NotificationMessage 

      typedef struct {
          UA_UInt32 sequenceNumber;
          UA_DateTime publishTime;
          size_t notificationDataSize;
          UA_ExtensionObject *notificationData;
      } UA_NotificationMessage;
      
      #define UA_TYPES_NOTIFICATIONMESSAGE 36

      FindServersOnNetworkRequest 

      typedef struct {
          UA_RequestHeader requestHeader;
          UA_UInt32 startingRecordId;
          UA_UInt32 maxRecordsToReturn;
          size_t serverCapabilityFilterSize;
          UA_String *serverCapabilityFilter;
      } UA_FindServersOnNetworkRequest;
      
      #define UA_TYPES_FINDSERVERSONNETWORKREQUEST 37

      EventFieldList 

      typedef struct {
          UA_UInt32 clientHandle;
          size_t eventFieldsSize;
          UA_Variant *eventFields;
      } UA_EventFieldList;
      
      #define UA_TYPES_EVENTFIELDLIST 38

      MonitoringMode 

      typedef enum {
          UA_MONITORINGMODE_DISABLED = 0,
          UA_MONITORINGMODE_SAMPLING = 1,
          UA_MONITORINGMODE_REPORTING = 2,
          __UA_MONITORINGMODE_FORCE32BIT = 0x7fffffff
      } UA_MonitoringMode;
      UA_STATIC_ASSERT(sizeof(UA_MonitoringMode) == sizeof(UA_Int32), enum_must_be_32bit);
      
      #define UA_TYPES_MONITORINGMODE 39

      MdnsDiscoveryConfiguration 

      mDNS注册所需的发现信息。

      typedef struct {
          UA_String mdnsServerName;
          size_t serverCapabilitiesSize;
          UA_String *serverCapabilities;
      } UA_MdnsDiscoveryConfiguration;
      
      #define UA_TYPES_MDNSDISCOVERYCONFIGURATION 40

      CallMethodResult 

      typedef struct {
          UA_StatusCode statusCode;
          size_t inputArgumentResultsSize;
          UA_StatusCode *inputArgumentResults;
          size_t inputArgumentDiagnosticInfosSize;
          UA_DiagnosticInfo *inputArgumentDiagnosticInfos;
          size_t outputArgumentsSize;
          UA_Variant *outputArguments;
      } UA_CallMethodResult;
      
      #define UA_TYPES_CALLMETHODRESULT 41

      ParsingResult 

      typedef struct {
          UA_StatusCode statusCode;
          size_t dataStatusCodesSize;
          UA_StatusCode *dataStatusCodes;
          size_t dataDiagnosticInfosSize;
          UA_DiagnosticInfo *dataDiagnosticInfos;
      } UA_ParsingResult;
      
      #define UA_TYPES_PARSINGRESULT 42

      RelativePathElement 

      相对路径中的元素。

      typedef struct {
          UA_NodeId referenceTypeId;
          UA_Boolean isInverse;
          UA_Boolean includeSubtypes;
          UA_QualifiedName targetName;
      } UA_RelativePathElement;
      
      #define UA_TYPES_RELATIVEPATHELEMENT 43

      BrowseDirection 

      返回的引用方向。

      typedef enum {
          UA_BROWSEDIRECTION_FORWARD = 0,
          UA_BROWSEDIRECTION_INVERSE = 1,
          UA_BROWSEDIRECTION_BOTH = 2,
          UA_BROWSEDIRECTION_INVALID = 3,
          __UA_BROWSEDIRECTION_FORCE32BIT = 0x7fffffff
      } UA_BrowseDirection;
      UA_STATIC_ASSERT(sizeof(UA_BrowseDirection) == sizeof(UA_Int32), enum_must_be_32bit);
      
      #define UA_TYPES_BROWSEDIRECTION 44

      CallMethodRequest 

      typedef struct {
          UA_NodeId objectId;
          UA_NodeId methodId;
          size_t inputArgumentsSize;
          UA_Variant *inputArguments;
      } UA_CallMethodRequest;
      
      #define UA_TYPES_CALLMETHODREQUEST 45

      RedundancySupport 

      typedef enum {
          UA_REDUNDANCYSUPPORT_NONE = 0,
          UA_REDUNDANCYSUPPORT_COLD = 1,
          UA_REDUNDANCYSUPPORT_WARM = 2,
          UA_REDUNDANCYSUPPORT_HOT = 3,
          UA_REDUNDANCYSUPPORT_TRANSPARENT = 4,
          UA_REDUNDANCYSUPPORT_HOTANDMIRRORED = 5,
          __UA_REDUNDANCYSUPPORT_FORCE32BIT = 0x7fffffff
      } UA_RedundancySupport;
      UA_STATIC_ASSERT(sizeof(UA_RedundancySupport) == sizeof(UA_Int32), enum_must_be_32bit);
      
      #define UA_TYPES_REDUNDANCYSUPPORT 46

      EventNotificationList 

      typedef struct {
          size_t eventsSize;
          UA_EventFieldList *events;
      } UA_EventNotificationList;
      
      #define UA_TYPES_EVENTNOTIFICATIONLIST 47

      UnregisterNodesRequest 

      取消注册一个或多个先前注册的节点。
      
      typedef struct {
          UA_RequestHeader requestHeader;
          size_t nodesToUnregisterSize;
          UA_NodeId *nodesToUnregister;
      } UA_UnregisterNodesRequest;
      
      #define UA_TYPES_UNREGISTERNODESREQUEST 48

      ContentFilterElementResult 

      typedef struct {
          UA_StatusCode statusCode;
          size_t operandStatusCodesSize;
          UA_StatusCode *operandStatusCodes;
          size_t operandDiagnosticInfosSize;
          UA_DiagnosticInfo *operandDiagnosticInfos;
      } UA_ContentFilterElementResult;
      
      #define UA_TYPES_CONTENTFILTERELEMENTRESULT 49

      SimpleAttributeOperand 

      typedef struct {
          UA_NodeId typeDefinitionId;
          size_t browsePathSize;
          UA_QualifiedName *browsePath;
          UA_UInt32 attributeId;
          UA_String indexRange;
      } UA_SimpleAttributeOperand;
      
      #define UA_TYPES_SIMPLEATTRIBUTEOPERAND 50

      LiteralOperand 

      typedef struct {
          UA_Variant value;
      } UA_LiteralOperand;
      
      #define UA_TYPES_LITERALOPERAND 51

      QueryDataSet 

      typedef struct {
          UA_ExpandedNodeId nodeId;
          UA_ExpandedNodeId typeDefinitionNode;
          size_t valuesSize;
          UA_Variant *values;
      } UA_QueryDataSet;
      
      #define UA_TYPES_QUERYDATASET 52

      AnonymousIdentityToken 

      表示匿名用户的令牌。

      typedef struct {
          UA_String policyId;
      } UA_AnonymousIdentityToken;
      
      #define UA_TYPES_ANONYMOUSIDENTITYTOKEN 53

      SetPublishingModeRequest 

      typedef struct {
          UA_RequestHeader requestHeader;
          UA_Boolean publishingEnabled;
          size_t subscriptionIdsSize;
          UA_UInt32 *subscriptionIds;
      } UA_SetPublishingModeRequest;
      
      #define UA_TYPES_SETPUBLISHINGMODEREQUEST 54

      MonitoredItemCreateResult 

      typedef struct {
          UA_StatusCode statusCode;
          UA_UInt32 monitoredItemId;
          UA_Double revisedSamplingInterval;
          UA_UInt32 revisedQueueSize;
          UA_ExtensionObject filterResult;
      } UA_MonitoredItemCreateResult;
      
      #define UA_TYPES_MONITOREDITEMCREATERESULT 55

      时间戳返回

      typedef enum {
          UA_TIMESTAMPSTORETURN_SOURCE = 0,
          UA_TIMESTAMPSTORETURN_SERVER = 1,
          UA_TIMESTAMPSTORETURN_BOTH = 2,
          UA_TIMESTAMPSTORETURN_NEITHER = 3,
          UA_TIMESTAMPSTORETURN_INVALID = 4,
          __UA_TIMESTAMPSTORETURN_FORCE32BIT = 0x7fffffff
      } UA_TimestampsToReturn;
      UA_STATIC_ASSERT(sizeof(UA_TimestampsToReturn) == sizeof(UA_Int32), enum_must_be_32bit);
      
      #define UA_TYPES_TIMESTAMPSTORETURN 56

      CallRequest 

      typedef struct {
          UA_RequestHeader requestHeader;
          size_t methodsToCallSize;
          UA_CallMethodRequest *methodsToCall;
      } UA_CallRequest;
      
      #define UA_TYPES_CALLREQUEST 57

      MethodAttributes 

      方法节点的属性。

      typedef struct {
          UA_UInt32 specifiedAttributes;
          UA_LocalizedText displayName;
          UA_LocalizedText description;
          UA_UInt32 writeMask;
          UA_UInt32 userWriteMask;
          UA_Boolean executable;
          UA_Boolean userExecutable;
      } UA_MethodAttributes;
      
      #define UA_TYPES_METHODATTRIBUTES 58

      DeleteReferencesItem 

      从服务器地址空间删除节点的请求。

      typedef struct {
          UA_NodeId sourceNodeId;
          UA_NodeId referenceTypeId;
          UA_Boolean isForward;
          UA_ExpandedNodeId targetNodeId;
          UA_Boolean deleteBidirectional;
      } UA_DeleteReferencesItem;
      
      #define UA_TYPES_DELETEREFERENCESITEM 59

      WriteValue 

      typedef struct {
          UA_NodeId nodeId;
          UA_UInt32 attributeId;
          UA_String indexRange;
          UA_DataValue value;
      } UA_WriteValue;
      
      #define UA_TYPES_WRITEVALUE 60

      NodeAttributesMask 

      用于指定新节点的默认属性的位。

      typedef enum {
          UA_NODEATTRIBUTESMASK_NONE = 0,
          UA_NODEATTRIBUTESMASK_ACCESSLEVEL = 1,
          UA_NODEATTRIBUTESMASK_ARRAYDIMENSIONS = 2,
          UA_NODEATTRIBUTESMASK_BROWSENAME = 4,
          UA_NODEATTRIBUTESMASK_CONTAINSNOLOOPS = 8,
          UA_NODEATTRIBUTESMASK_DATATYPE = 16,
          UA_NODEATTRIBUTESMASK_DESCRIPTION = 32,
          UA_NODEATTRIBUTESMASK_DISPLAYNAME = 64,
          UA_NODEATTRIBUTESMASK_EVENTNOTIFIER = 128,
          UA_NODEATTRIBUTESMASK_EXECUTABLE = 256,
          UA_NODEATTRIBUTESMASK_HISTORIZING = 512,
          UA_NODEATTRIBUTESMASK_INVERSENAME = 1024,
          UA_NODEATTRIBUTESMASK_ISABSTRACT = 2048,
          UA_NODEATTRIBUTESMASK_MINIMUMSAMPLINGINTERVAL = 4096,
          UA_NODEATTRIBUTESMASK_NODECLASS = 8192,
          UA_NODEATTRIBUTESMASK_NODEID = 16384,
          UA_NODEATTRIBUTESMASK_SYMMETRIC = 32768,
          UA_NODEATTRIBUTESMASK_USERACCESSLEVEL = 65536,
          UA_NODEATTRIBUTESMASK_USEREXECUTABLE = 131072,
          UA_NODEATTRIBUTESMASK_USERWRITEMASK = 262144,
          UA_NODEATTRIBUTESMASK_VALUERANK = 524288,
          UA_NODEATTRIBUTESMASK_WRITEMASK = 1048576,
          UA_NODEATTRIBUTESMASK_VALUE = 2097152,
          UA_NODEATTRIBUTESMASK_DATATYPEDEFINITION = 4194304,
          UA_NODEATTRIBUTESMASK_ROLEPERMISSIONS = 8388608,
          UA_NODEATTRIBUTESMASK_ACCESSRESTRICTIONS = 16777216,
          UA_NODEATTRIBUTESMASK_ALL = 33554431,
          UA_NODEATTRIBUTESMASK_BASENODE = 26501220,
          UA_NODEATTRIBUTESMASK_OBJECT = 26501348,
          UA_NODEATTRIBUTESMASK_OBJECTTYPE = 26503268,
          UA_NODEATTRIBUTESMASK_VARIABLE = 26571383,
          UA_NODEATTRIBUTESMASK_VARIABLETYPE = 28600438,
          UA_NODEATTRIBUTESMASK_METHOD = 26632548,
          UA_NODEATTRIBUTESMASK_REFERENCETYPE = 26537060,
          UA_NODEATTRIBUTESMASK_VIEW = 26501356,
          __UA_NODEATTRIBUTESMASK_FORCE32BIT = 0x7fffffff
      } UA_NodeAttributesMask;
      UA_STATIC_ASSERT(sizeof(UA_NodeAttributesMask) == sizeof(UA_Int32), enum_must_be_32bit);
      
      #define UA_TYPES_NODEATTRIBUTESMASK 61

      MessageSecurityMode 

      要在邮件上使用的安全性类型。

      typedef enum {
          UA_MESSAGESECURITYMODE_INVALID = 0,
          UA_MESSAGESECURITYMODE_NONE = 1,
          UA_MESSAGESECURITYMODE_SIGN = 2,
          UA_MESSAGESECURITYMODE_SIGNANDENCRYPT = 3,
          __UA_MESSAGESECURITYMODE_FORCE32BIT = 0x7fffffff
      } UA_MessageSecurityMode;
      UA_STATIC_ASSERT(sizeof(UA_MessageSecurityMode) == sizeof(UA_Int32), enum_must_be_32bit);
      
      #define UA_TYPES_MESSAGESECURITYMODE 62

      监控参数

      typedef struct {
          UA_UInt32 clientHandle;
          UA_Double samplingInterval;
          UA_ExtensionObject filter;
          UA_UInt32 queueSize;
          UA_Boolean discardOldest;
      } UA_MonitoringParameters;
      
      #define UA_TYPES_MONITORINGPARAMETERS 63

      ReferenceNode 

      指定属于节点的引用。

      typedef struct {
          UA_NodeId referenceTypeId;
          UA_Boolean isInverse;
          UA_ExpandedNodeId targetId;
      } UA_ReferenceNode;
      
      #define UA_TYPES_REFERENCENODE 64

      参数

      方法的参数。

      typedef struct {
          UA_String name;
          UA_NodeId dataType;
          UA_Int32 valueRank;
          size_t arrayDimensionsSize;
          UA_UInt32 *arrayDimensions;
          UA_LocalizedText description;
      } UA_Argument;
      
      #define UA_TYPES_ARGUMENT 65

      ChannelSecurityToken 

      标识主动安全通道的一组密钥的令牌。

      typedef struct {
          UA_UInt32 channelId;
          UA_UInt32 tokenId;
          UA_DateTime createdAt;
          UA_UInt32 revisedLifetime;
      } UA_ChannelSecurityToken;
      
      #define UA_TYPES_CHANNELSECURITYTOKEN 66

      UserIdentityToken 

      用户标识令牌的基本类型。

      typedef struct {
          UA_String policyId;
      } UA_UserIdentityToken;
      
      #define UA_TYPES_USERIDENTITYTOKEN 67

      签名数据

      数字签名。

      typedef struct {
          UA_String algorithm;
          UA_ByteString signature;
      } UA_SignatureData;
      
      #define UA_TYPES_SIGNATUREDATA 68

      ObjectTypeAttributes 

      对象类型节点的属性。

      typedef struct {
          UA_UInt32 specifiedAttributes;
          UA_LocalizedText displayName;
          UA_LocalizedText description;
          UA_UInt32 writeMask;
          UA_UInt32 userWriteMask;
          UA_Boolean isAbstract;
      } UA_ObjectTypeAttributes;
      
      #define UA_TYPES_OBJECTTYPEATTRIBUTES 69

      死区类型

      typedef enum {
          UA_DEADBANDTYPE_NONE = 0,
          UA_DEADBANDTYPE_ABSOLUTE = 1,
          UA_DEADBANDTYPE_PERCENT = 2,
          __UA_DEADBANDTYPE_FORCE32BIT = 0x7fffffff
      } UA_DeadbandType;
      UA_STATIC_ASSERT(sizeof(UA_DeadbandType) == sizeof(UA_Int32), enum_must_be_32bit);
      
      #define UA_TYPES_DEADBANDTYPE 70

      SecurityTokenRequestType 

      指示是否创建或续订令牌。

      typedef enum {
          UA_SECURITYTOKENREQUESTTYPE_ISSUE = 0,
          UA_SECURITYTOKENREQUESTTYPE_RENEW = 1,
          __UA_SECURITYTOKENREQUESTTYPE_FORCE32BIT = 0x7fffffff
      } UA_SecurityTokenRequestType;
      UA_STATIC_ASSERT(sizeof(UA_SecurityTokenRequestType) == sizeof(UA_Int32), enum_must_be_32bit);
      
      #define UA_TYPES_SECURITYTOKENREQUESTTYPE 71

      NodeAttributes 

      所有节点的基本属性。

      typedef struct {
          UA_UInt32 specifiedAttributes;
          UA_LocalizedText displayName;
          UA_LocalizedText description;
          UA_UInt32 writeMask;
          UA_UInt32 userWriteMask;
      } UA_NodeAttributes;
      
      #define UA_TYPES_NODEATTRIBUTES 72

      DataChangeTrigger 

      typedef enum {
          UA_DATACHANGETRIGGER_STATUS = 0,
          UA_DATACHANGETRIGGER_STATUSVALUE = 1,
          UA_DATACHANGETRIGGER_STATUSVALUETIMESTAMP = 2,
          __UA_DATACHANGETRIGGER_FORCE32BIT = 0x7fffffff
      } UA_DataChangeTrigger;
      UA_STATIC_ASSERT(sizeof(UA_DataChangeTrigger) == sizeof(UA_Int32), enum_must_be_32bit);
      
      #define UA_TYPES_DATACHANGETRIGGER 73

      BuildInfo 

      typedef struct {
          UA_String productUri;
          UA_String manufacturerName;
          UA_String productName;
          UA_String softwareVersion;
          UA_String buildNumber;
          UA_DateTime buildDate;
      } UA_BuildInfo;
      
      #define UA_TYPES_BUILDINFO 74

      结点类别

      指定节点类的掩码。

      typedef enum {
          UA_NODECLASS_UNSPECIFIED = 0,
          UA_NODECLASS_OBJECT = 1,
          UA_NODECLASS_VARIABLE = 2,
          UA_NODECLASS_METHOD = 4,
          UA_NODECLASS_OBJECTTYPE = 8,
          UA_NODECLASS_VARIABLETYPE = 16,
          UA_NODECLASS_REFERENCETYPE = 32,
          UA_NODECLASS_DATATYPE = 64,
          UA_NODECLASS_VIEW = 128,
          __UA_NODECLASS_FORCE32BIT = 0x7fffffff
      } UA_NodeClass;
      UA_STATIC_ASSERT(sizeof(UA_NodeClass) == sizeof(UA_Int32), enum_must_be_32bit);
      
      #define UA_TYPES_NODECLASS 75

      SubscriptionDiagnosticsDataType 

      typedef struct {
          UA_NodeId sessionId;
          UA_UInt32 subscriptionId;
          UA_Byte priority;
          UA_Double publishingInterval;
          UA_UInt32 maxKeepAliveCount;
          UA_UInt32 maxLifetimeCount;
          UA_UInt32 maxNotificationsPerPublish;
          UA_Boolean publishingEnabled;
          UA_UInt32 modifyCount;
          UA_UInt32 enableCount;
          UA_UInt32 disableCount;
          UA_UInt32 republishRequestCount;
          UA_UInt32 republishMessageRequestCount;
          UA_UInt32 republishMessageCount;
          UA_UInt32 transferRequestCount;
          UA_UInt32 transferredToAltClientCount;
          UA_UInt32 transferredToSameClientCount;
          UA_UInt32 publishRequestCount;
          UA_UInt32 dataChangeNotificationsCount;
          UA_UInt32 eventNotificationsCount;
          UA_UInt32 notificationsCount;
          UA_UInt32 latePublishRequestCount;
          UA_UInt32 currentKeepAliveCount;
          UA_UInt32 currentLifetimeCount;
          UA_UInt32 unacknowledgedMessageCount;
          UA_UInt32 discardedMessageCount;
          UA_UInt32 monitoredItemCount;
          UA_UInt32 disabledMonitoredItemCount;
          UA_UInt32 monitoringQueueOverflowCount;
          UA_UInt32 nextSequenceNumber;
          UA_UInt32 eventQueueOverFlowCount;
      } UA_SubscriptionDiagnosticsDataType;
      
      #define UA_TYPES_SUBSCRIPTIONDIAGNOSTICSDATATYPE 76

      FilterOperand 

      typedef void * UA_FilterOperand;
      
      #define UA_TYPES_FILTEROPERAND 77

      MonitoredItemNotification 

      typedef struct {
          UA_UInt32 clientHandle;
          UA_DataValue value;
      } UA_MonitoredItemNotification;
      
      #define UA_TYPES_MONITOREDITEMNOTIFICATION 78

      DeleteNodesItem 

      将节点删除到服务器地址空间的请求。

      typedef struct {
          UA_NodeId nodeId;
          UA_Boolean deleteTargetReferences;
      } UA_DeleteNodesItem;
      
      #define UA_TYPES_DELETENODESITEM 79

      DeleteSubscriptionsRequest 

      typedef struct {
          UA_RequestHeader requestHeader;
          size_t subscriptionIdsSize;
          UA_UInt32 *subscriptionIds;
      } UA_DeleteSubscriptionsRequest;
      
      #define UA_TYPES_DELETESUBSCRIPTIONSREQUEST 80

      SubscriptionAcknowledgement 

      typedef struct {
          UA_UInt32 subscriptionId;
          UA_UInt32 sequenceNumber;
      } UA_SubscriptionAcknowledgement;
      
      #define UA_TYPES_SUBSCRIPTIONACKNOWLEDGEMENT 81

      ReadValueId 

      typedef struct {
          UA_NodeId nodeId;
          UA_UInt32 attributeId;
          UA_String indexRange;
          UA_QualifiedName dataEncoding;
      } UA_ReadValueId;
      
      #define UA_TYPES_READVALUEID 82

      DataTypeAttributes 

      数据类型节点的属性。

      typedef struct {
          UA_UInt32 specifiedAttributes;
          UA_LocalizedText displayName;
          UA_LocalizedText description;
          UA_UInt32 writeMask;
          UA_UInt32 userWriteMask;
          UA_Boolean isAbstract;
      } UA_DataTypeAttributes;
      
      #define UA_TYPES_DATATYPEATTRIBUTES 83

      ResponseHeader 

      标头随每个服务器响应一起传递。

      typedef struct {
          UA_DateTime timestamp;
          UA_UInt32 requestHandle;
          UA_StatusCode serviceResult;
          UA_DiagnosticInfo serviceDiagnostics;
          size_t stringTableSize;
          UA_String *stringTable;
          UA_ExtensionObject additionalHeader;
      } UA_ResponseHeader;
      
      #define UA_TYPES_RESPONSEHEADER 84

      DeleteMonitoredItemsRequest 

      typedef struct {
          UA_RequestHeader requestHeader;
          UA_UInt32 subscriptionId;
          size_t monitoredItemIdsSize;
          UA_UInt32 *monitoredItemIds;
      } UA_DeleteMonitoredItemsRequest;
      
      #define UA_TYPES_DELETEMONITOREDITEMSREQUEST 85

      ViewDescription 

      要浏览的视图。

      typedef struct {
          UA_NodeId viewId;
          UA_DateTime timestamp;
          UA_UInt32 viewVersion;
      } UA_ViewDescription;
      
      #define UA_TYPES_VIEWDESCRIPTION 86

      ServerOnNetwork 

      typedef struct {
          UA_UInt32 recordId;
          UA_String serverName;
          UA_String discoveryUrl;
          size_t serverCapabilitiesSize;
          UA_String *serverCapabilities;
      } UA_ServerOnNetwork;
      
      #define UA_TYPES_SERVERONNETWORK 87

      DeleteMonitoredItemsResponse 

      typedef struct {
          UA_ResponseHeader responseHeader;
          size_t resultsSize;
          UA_StatusCode *results;
          size_t diagnosticInfosSize;
          UA_DiagnosticInfo *diagnosticInfos;
      } UA_DeleteMonitoredItemsResponse;
      
      #define UA_TYPES_DELETEMONITOREDITEMSRESPONSE 88

      FindServersOnNetworkResponse 

      typedef struct {
          UA_ResponseHeader responseHeader;
          UA_DateTime lastCounterResetTime;
          size_t serversSize;
          UA_ServerOnNetwork *servers;
      } UA_FindServersOnNetworkResponse;
      
      #define UA_TYPES_FINDSERVERSONNETWORKRESPONSE 89

      RelativePath 

      从引用类型和浏览名称构造的相对路径。

      typedef struct {
          size_t elementsSize;
          UA_RelativePathElement *elements;
      } UA_RelativePath;
      
      #define UA_TYPES_RELATIVEPATH 90

      RegisterNodesRequest 

      注册一个或多个节点以在会话中重复使用。

      typedef struct {
          UA_RequestHeader requestHeader;
          size_t nodesToRegisterSize;
          UA_NodeId *nodesToRegister;
      } UA_RegisterNodesRequest;
      
      #define UA_TYPES_REGISTERNODESREQUEST 91

      AggregateConfiguration 

      typedef struct {
          UA_Boolean useServerCapabilitiesDefaults;
          UA_Boolean treatUncertainAsBad;
          UA_Byte percentDataBad;
          UA_Byte percentDataGood;
          UA_Boolean useSlopedExtrapolation;
      } UA_AggregateConfiguration;
      
      #define UA_TYPES_AGGREGATECONFIGURATION 92

      DeleteNodesRequest 

      从服务器地址空间中删除一个或多个节点。

      typedef struct {
          UA_RequestHeader requestHeader;
          size_t nodesToDeleteSize;
          UA_DeleteNodesItem *nodesToDelete;
      } UA_DeleteNodesRequest;
      
      #define UA_TYPES_DELETENODESREQUEST 93

      PublishResponse 

      typedef struct {
          UA_ResponseHeader responseHeader;
          UA_UInt32 subscriptionId;
          size_t availableSequenceNumbersSize;
          UA_UInt32 *availableSequenceNumbers;
          UA_Boolean moreNotifications;
          UA_NotificationMessage notificationMessage;
          size_t resultsSize;
          UA_StatusCode *results;
          size_t diagnosticInfosSize;
          UA_DiagnosticInfo *diagnosticInfos;
      } UA_PublishResponse;
      
      #define UA_TYPES_PUBLISHRESPONSE 94

      MonitoredItemModifyRequest 

      typedef struct {
          UA_UInt32 monitoredItemId;
          UA_MonitoringParameters requestedParameters;
      } UA_MonitoredItemModifyRequest;
      
      #define UA_TYPES_MONITOREDITEMMODIFYREQUEST 95

      ServiceCounterDataType 

      typedef struct {
          UA_UInt32 totalCount;
          UA_UInt32 errorCount;
      } UA_ServiceCounterDataType;
      
      #define UA_TYPES_SERVICECOUNTERDATATYPE 96

      ModelChangeStructureDataType 

      typedef struct {
          UA_NodeId affected;
          UA_NodeId affectedType;
          UA_Byte verb;
      } UA_ModelChangeStructureDataType;
      
      #define UA_TYPES_MODELCHANGESTRUCTUREDATATYPE 97

      UserNameIdentityToken 

      表示由用户名和密码标识的用户的标记。

      typedef struct {
          UA_String policyId;
          UA_String userName;
          UA_ByteString password;
          UA_String encryptionAlgorithm;
      } UA_UserNameIdentityToken;
      
      #define UA_TYPES_USERNAMEIDENTITYTOKEN 98

      IdType 

      节点标识中使用的标识符类型。

      typedef enum {
          UA_IDTYPE_NUMERIC = 0,
          UA_IDTYPE_STRING = 1,
          UA_IDTYPE_GUID = 2,
          UA_IDTYPE_OPAQUE = 3,
          __UA_IDTYPE_FORCE32BIT = 0x7fffffff
      } UA_IdType;
      UA_STATIC_ASSERT(sizeof(UA_IdType) == sizeof(UA_Int32), enum_must_be_32bit);
      
      #define UA_TYPES_IDTYPE 99

      UserTokenType 

      可能的用户令牌类型。

      typedef enum {
          UA_USERTOKENTYPE_ANONYMOUS = 0,
          UA_USERTOKENTYPE_USERNAME = 1,
          UA_USERTOKENTYPE_CERTIFICATE = 2,
          UA_USERTOKENTYPE_ISSUEDTOKEN = 3,
          __UA_USERTOKENTYPE_FORCE32BIT = 0x7fffffff
      } UA_UserTokenType;
      UA_STATIC_ASSERT(sizeof(UA_UserTokenType) == sizeof(UA_Int32), enum_must_be_32bit);
      
      #define UA_TYPES_USERTOKENTYPE 100

      SetTriggeringResponse 

      typedef struct {
          UA_ResponseHeader responseHeader;
          size_t addResultsSize;
          UA_StatusCode *addResults;
          size_t addDiagnosticInfosSize;
          UA_DiagnosticInfo *addDiagnosticInfos;
          size_t removeResultsSize;
          UA_StatusCode *removeResults;
          size_t removeDiagnosticInfosSize;
          UA_DiagnosticInfo *removeDiagnosticInfos;
      } UA_SetTriggeringResponse;
      
      #define UA_TYPES_SETTRIGGERINGRESPONSE 101

      TimeZoneDataType 

      typedef struct {
          UA_Int16 offset;
          UA_Boolean daylightSavingInOffset;
      } UA_TimeZoneDataType;
      
      #define UA_TYPES_TIMEZONEDATATYPE 102

      ActivateSessionRequest 

      激活与服务器的会话。

      typedef struct {
          UA_RequestHeader requestHeader;
          UA_SignatureData clientSignature;
          size_t clientSoftwareCertificatesSize;
          UA_SignedSoftwareCertificate *clientSoftwareCertificates;
          size_t localeIdsSize;
          UA_String *localeIds;
          UA_ExtensionObject userIdentityToken;
          UA_SignatureData userTokenSignature;
      } UA_ActivateSessionRequest;
      
      #define UA_TYPES_ACTIVATESESSIONREQUEST 103

      OpenSecureChannelResponse 

      使用服务器创建安全通道。

      typedef struct {
          UA_ResponseHeader responseHeader;
          UA_UInt32 serverProtocolVersion;
          UA_ChannelSecurityToken securityToken;
          UA_ByteString serverNonce;
      } UA_OpenSecureChannelResponse;
      
      #define UA_TYPES_OPENSECURECHANNELRESPONSE 104

      ApplicationType 

      应用程序的类型。

      typedef enum {
          UA_APPLICATIONTYPE_SERVER = 0,
          UA_APPLICATIONTYPE_CLIENT = 1,
          UA_APPLICATIONTYPE_CLIENTANDSERVER = 2,
          UA_APPLICATIONTYPE_DISCOVERYSERVER = 3,
          __UA_APPLICATIONTYPE_FORCE32BIT = 0x7fffffff
      } UA_ApplicationType;
      UA_STATIC_ASSERT(sizeof(UA_ApplicationType) == sizeof(UA_Int32), enum_must_be_32bit);
      
      #define UA_TYPES_APPLICATIONTYPE 105

      ServerState 

      typedef enum {
          UA_SERVERSTATE_RUNNING = 0,
          UA_SERVERSTATE_FAILED = 1,
          UA_SERVERSTATE_NOCONFIGURATION = 2,
          UA_SERVERSTATE_SUSPENDED = 3,
          UA_SERVERSTATE_SHUTDOWN = 4,
          UA_SERVERSTATE_TEST = 5,
          UA_SERVERSTATE_COMMUNICATIONFAULT = 6,
          UA_SERVERSTATE_UNKNOWN = 7,
          __UA_SERVERSTATE_FORCE32BIT = 0x7fffffff
      } UA_ServerState;
      UA_STATIC_ASSERT(sizeof(UA_ServerState) == sizeof(UA_Int32), enum_must_be_32bit);
      
      #define UA_TYPES_SERVERSTATE 106

      QueryNextResponse 

      typedef struct {
          UA_ResponseHeader responseHeader;
          size_t queryDataSetsSize;
          UA_QueryDataSet *queryDataSets;
          UA_ByteString revisedContinuationPoint;
      } UA_QueryNextResponse;
      
      #define UA_TYPES_QUERYNEXTRESPONSE 107

      DiscoveryConfiguration 

      发现配置信息的基本类型。

      typedef void * UA_DiscoveryConfiguration;
      
      #define UA_TYPES_DISCOVERYCONFIGURATION 108

      ActivateSessionResponse 

      激活与服务器的会话。

      typedef struct {
          UA_ResponseHeader responseHeader;
          UA_ByteString serverNonce;
          size_t resultsSize;
          UA_StatusCode *results;
          size_t diagnosticInfosSize;
          UA_DiagnosticInfo *diagnosticInfos;
      } UA_ActivateSessionResponse;
      
      #define UA_TYPES_ACTIVATESESSIONRESPONSE 109

      EndpointUrlListDataType 

      typedef struct {
          size_t endpointUrlListSize;
          UA_String *endpointUrlList;
      } UA_EndpointUrlListDataType;
      
      #define UA_TYPES_ENDPOINTURLLISTDATATYPE 110

      FilterOperator 

      typedef enum {
          UA_FILTEROPERATOR_EQUALS = 0,
          UA_FILTEROPERATOR_ISNULL = 1,
          UA_FILTEROPERATOR_GREATERTHAN = 2,
          UA_FILTEROPERATOR_LESSTHAN = 3,
          UA_FILTEROPERATOR_GREATERTHANOREQUAL = 4,
          UA_FILTEROPERATOR_LESSTHANOREQUAL = 5,
          UA_FILTEROPERATOR_LIKE = 6,
          UA_FILTEROPERATOR_NOT = 7,
          UA_FILTEROPERATOR_BETWEEN = 8,
          UA_FILTEROPERATOR_INLIST = 9,
          UA_FILTEROPERATOR_AND = 10,
          UA_FILTEROPERATOR_OR = 11,
          UA_FILTEROPERATOR_CAST = 12,
          UA_FILTEROPERATOR_INVIEW = 13,
          UA_FILTEROPERATOR_OFTYPE = 14,
          UA_FILTEROPERATOR_RELATEDTO = 15,
          UA_FILTEROPERATOR_BITWISEAND = 16,
          UA_FILTEROPERATOR_BITWISEOR = 17,
          __UA_FILTEROPERATOR_FORCE32BIT = 0x7fffffff
      } UA_FilterOperator;
      UA_STATIC_ASSERT(sizeof(UA_FilterOperator) == sizeof(UA_Int32), enum_must_be_32bit);
      
      #define UA_TYPES_FILTEROPERATOR 111

      QueryNextRequest 

      typedef struct {
          UA_RequestHeader requestHeader;
          UA_Boolean releaseContinuationPoint;
          UA_ByteString continuationPoint;
      } UA_QueryNextRequest;
      
      #define UA_TYPES_QUERYNEXTREQUEST 112

      WriteResponse 

      typedef struct {
          UA_ResponseHeader responseHeader;
          size_t resultsSize;
          UA_StatusCode *results;
          size_t diagnosticInfosSize;
          UA_DiagnosticInfo *diagnosticInfos;
      } UA_WriteResponse;
      
      #define UA_TYPES_WRITERESPONSE 113

      BrowseNextRequest 

      继续一个或多个浏览操作。

      typedef struct {
          UA_RequestHeader requestHeader;
          UA_Boolean releaseContinuationPoints;
          size_t continuationPointsSize;
          UA_ByteString *continuationPoints;
      } UA_BrowseNextRequest;
      
      #define UA_TYPES_BROWSENEXTREQUEST 114

      CreateSubscriptionRequest 

      typedef struct {
          UA_RequestHeader requestHeader;
          UA_Double requestedPublishingInterval;
          UA_UInt32 requestedLifetimeCount;
          UA_UInt32 requestedMaxKeepAliveCount;
          UA_UInt32 maxNotificationsPerPublish;
          UA_Boolean publishingEnabled;
          UA_Byte priority;
      } UA_CreateSubscriptionRequest;
      
      #define UA_TYPES_CREATESUBSCRIPTIONREQUEST 115

      VariableTypeAttributes 

      变量类型节点的属性。

      typedef struct {
          UA_UInt32 specifiedAttributes;
          UA_LocalizedText displayName;
          UA_LocalizedText description;
          UA_UInt32 writeMask;
          UA_UInt32 userWriteMask;
          UA_Variant value;
          UA_NodeId dataType;
          UA_Int32 valueRank;
          size_t arrayDimensionsSize;
          UA_UInt32 *arrayDimensions;
          UA_Boolean isAbstract;
      } UA_VariableTypeAttributes;
      
      #define UA_TYPES_VARIABLETYPEATTRIBUTES 116

      BrowsePathResult 

      翻译操作的结果。

      typedef struct {
          UA_StatusCode statusCode;
          size_t targetsSize;
          UA_BrowsePathTarget *targets;
      } UA_BrowsePathResult;
      
      #define UA_TYPES_BROWSEPATHRESULT 117

      ModifySubscriptionResponse 

      typedef struct {
          UA_ResponseHeader responseHeader;
          UA_Double revisedPublishingInterval;
          UA_UInt32 revisedLifetimeCount;
          UA_UInt32 revisedMaxKeepAliveCount;
      } UA_ModifySubscriptionResponse;
      
      #define UA_TYPES_MODIFYSUBSCRIPTIONRESPONSE 118

      RedundantServerDataType 

      typedef struct {
          UA_String serverId;
          UA_Byte serviceLevel;
          UA_ServerState serverState;
      } UA_RedundantServerDataType;
      
      #define UA_TYPES_REDUNDANTSERVERDATATYPE 119

      RegisterNodesResponse 

      注册一个或多个节点以在会话中重复使用。

      typedef struct {
          UA_ResponseHeader responseHeader;
          size_t registeredNodeIdsSize;
          UA_NodeId *registeredNodeIds;
      } UA_RegisterNodesResponse;
      
      #define UA_TYPES_REGISTERNODESRESPONSE 120

      CloseSessionRequest 

      关闭与服务器的会话。

      typedef struct {
          UA_RequestHeader requestHeader;
          UA_Boolean deleteSubscriptions;
      } UA_CloseSessionRequest;
      
      #define UA_TYPES_CLOSESESSIONREQUEST 121

      ModifyMonitoredItemsResponse 

      typedef struct {
          UA_ResponseHeader responseHeader;
          size_t resultsSize;
          UA_MonitoredItemModifyResult *results;
          size_t diagnosticInfosSize;
          UA_DiagnosticInfo *diagnosticInfos;
      } UA_ModifyMonitoredItemsResponse;
      
      #define UA_TYPES_MODIFYMONITOREDITEMSRESPONSE 122

      ModifySubscriptionRequest 

      typedef struct {
          UA_RequestHeader requestHeader;
          UA_UInt32 subscriptionId;
          UA_Double requestedPublishingInterval;
          UA_UInt32 requestedLifetimeCount;
          UA_UInt32 requestedMaxKeepAliveCount;
          UA_UInt32 maxNotificationsPerPublish;
          UA_Byte priority;
      } UA_ModifySubscriptionRequest;
      
      #define UA_TYPES_MODIFYSUBSCRIPTIONREQUEST 123

      ServerDiagnosticsSummaryDataType 

      typedef struct {
          UA_UInt32 serverViewCount;
          UA_UInt32 currentSessionCount;
          UA_UInt32 cumulatedSessionCount;
          UA_UInt32 securityRejectedSessionCount;
          UA_UInt32 rejectedSessionCount;
          UA_UInt32 sessionTimeoutCount;
          UA_UInt32 sessionAbortCount;
          UA_UInt32 currentSubscriptionCount;
          UA_UInt32 cumulatedSubscriptionCount;
          UA_UInt32 publishingIntervalCount;
          UA_UInt32 securityRejectedRequestsCount;
          UA_UInt32 rejectedRequestsCount;
      } UA_ServerDiagnosticsSummaryDataType;
      
      #define UA_TYPES_SERVERDIAGNOSTICSSUMMARYDATATYPE 124

      UserTokenPolicy 

      描述可以与服务器一起使用的用户令牌。

      typedef struct {
          UA_String policyId;
          UA_UserTokenType tokenType;
          UA_String issuedTokenType;
          UA_String issuerEndpointUrl;
          UA_String securityPolicyUri;
      } UA_UserTokenPolicy;
      
      #define UA_TYPES_USERTOKENPOLICY 125

      ReferenceTypeAttributes 

      引用类型节点的属性。

      typedef struct {
          UA_UInt32 specifiedAttributes;
          UA_LocalizedText displayName;
          UA_LocalizedText description;
          UA_UInt32 writeMask;
          UA_UInt32 userWriteMask;
          UA_Boolean isAbstract;
          UA_Boolean symmetric;
          UA_LocalizedText inverseName;
      } UA_ReferenceTypeAttributes;
      
      #define UA_TYPES_REFERENCETYPEATTRIBUTES 126

      浏览路径

      将路径转换为节点ID的请求。

      typedef struct {
          UA_NodeId startingNode;
          UA_RelativePath relativePath;
      } UA_BrowsePath;
      
      #define UA_TYPES_BROWSEPATH 127

      SetMonitoringModeRequest 

      typedef struct {
          UA_RequestHeader requestHeader;
          UA_UInt32 subscriptionId;
          UA_MonitoringMode monitoringMode;
          size_t monitoredItemIdsSize;
          UA_UInt32 *monitoredItemIds;
      } UA_SetMonitoringModeRequest;
      
      #define UA_TYPES_SETMONITORINGMODEREQUEST 128

      UnregisterNodesResponse 

      取消注册一个或多个先前注册的节点。

      typedef struct {
          UA_ResponseHeader responseHeader;
      } UA_UnregisterNodesResponse;
      
      #define UA_TYPES_UNREGISTERNODESRESPONSE 129

      WriteRequest 

      typedef struct {
          UA_RequestHeader requestHeader;
          size_t nodesToWriteSize;
          UA_WriteValue *nodesToWrite;
      } UA_WriteRequest;
      
      #define UA_TYPES_WRITEREQUEST 130

      ObjectAttributes 

      对象节点的属性。

      typedef struct {
          UA_UInt32 specifiedAttributes;
          UA_LocalizedText displayName;
          UA_LocalizedText description;
          UA_UInt32 writeMask;
          UA_UInt32 userWriteMask;
          UA_Byte eventNotifier;
      } UA_ObjectAttributes;
      
      #define UA_TYPES_OBJECTATTRIBUTES 131

      BrowseResultMask 

      一个位掩码,指定应在浏览响应中返回的内容。

      typedef enum {
          UA_BROWSERESULTMASK_NONE = 0,
          UA_BROWSERESULTMASK_REFERENCETYPEID = 1,
          UA_BROWSERESULTMASK_ISFORWARD = 2,
          UA_BROWSERESULTMASK_NODECLASS = 4,
          UA_BROWSERESULTMASK_BROWSENAME = 8,
          UA_BROWSERESULTMASK_DISPLAYNAME = 16,
          UA_BROWSERESULTMASK_TYPEDEFINITION = 32,
          UA_BROWSERESULTMASK_ALL = 63,
          UA_BROWSERESULTMASK_REFERENCETYPEINFO = 3,
          UA_BROWSERESULTMASK_TARGETINFO = 60,
          __UA_BROWSERESULTMASK_FORCE32BIT = 0x7fffffff
      } UA_BrowseResultMask;
      UA_STATIC_ASSERT(sizeof(UA_BrowseResultMask) == sizeof(UA_Int32), enum_must_be_32bit);
      
      #define UA_TYPES_BROWSERESULTMASK 132

      BrowseDescription 

      从节点浏览引用的请求。

      typedef struct {
          UA_NodeId nodeId;
          UA_BrowseDirection browseDirection;
          UA_NodeId referenceTypeId;
          UA_Boolean includeSubtypes;
          UA_UInt32 nodeClassMask;
          UA_UInt32 resultMask;
      } UA_BrowseDescription;
      
      #define UA_TYPES_BROWSEDESCRIPTION 133

      SetTriggeringRequest 

      typedef struct {
          UA_RequestHeader requestHeader;
          UA_UInt32 subscriptionId;
          UA_UInt32 triggeringItemId;
          size_t linksToAddSize;
          UA_UInt32 *linksToAdd;
          size_t linksToRemoveSize;
          UA_UInt32 *linksToRemove;
      } UA_SetTriggeringRequest;
      
      #define UA_TYPES_SETTRIGGERINGREQUEST 134

      SessionSecurityDiagnosticsDataType 

      typedef struct {
          UA_NodeId sessionId;
          UA_String clientUserIdOfSession;
          size_t clientUserIdHistorySize;
          UA_String *clientUserIdHistory;
          UA_String authenticationMechanism;
          UA_String encoding;
          UA_String transportProtocol;
          UA_MessageSecurityMode securityMode;
          UA_String securityPolicyUri;
          UA_ByteString clientCertificate;
      } UA_SessionSecurityDiagnosticsDataType;
      
      #define UA_TYPES_SESSIONSECURITYDIAGNOSTICSDATATYPE 135

      RepublishRequest 

      typedef struct {
          UA_RequestHeader requestHeader;
          UA_UInt32 subscriptionId;
          UA_UInt32 retransmitSequenceNumber;
      } UA_RepublishRequest;
      
      #define UA_TYPES_REPUBLISHREQUEST 136

      GetEndpointsRequest 

      获取服务器使用的端点。

      typedef struct {
          UA_RequestHeader requestHeader;
          UA_String endpointUrl;
          size_t localeIdsSize;
          UA_String *localeIds;
          size_t profileUrisSize;
          UA_String *profileUris;
      } UA_GetEndpointsRequest;
      
      #define UA_TYPES_GETENDPOINTSREQUEST 137

      PublishRequest 

      typedef struct {
          UA_RequestHeader requestHeader;
          size_t subscriptionAcknowledgementsSize;
          UA_SubscriptionAcknowledgement *subscriptionAcknowledgements;
      } UA_PublishRequest;
      
      #define UA_TYPES_PUBLISHREQUEST 138

      DeleteSubscriptionsResponse 

      typedef struct {
          UA_ResponseHeader responseHeader;
          size_t resultsSize;
          UA_StatusCode *results;
          size_t diagnosticInfosSize;
          UA_DiagnosticInfo *diagnosticInfos;
      } UA_DeleteSubscriptionsResponse;
      
      #define UA_TYPES_DELETESUBSCRIPTIONSRESPONSE 139

      AddNodesResponse 

      将一个或多个节点添加到服务器地址空间。

      typedef struct {
          UA_ResponseHeader responseHeader;
          size_t resultsSize;
          UA_AddNodesResult *results;
          size_t diagnosticInfosSize;
          UA_DiagnosticInfo *diagnosticInfos;
      } UA_AddNodesResponse;
      
      #define UA_TYPES_ADDNODESRESPONSE 140

      DataChangeNotification 

      typedef struct {
          size_t monitoredItemsSize;
          UA_MonitoredItemNotification *monitoredItems;
          size_t diagnosticInfosSize;
          UA_DiagnosticInfo *diagnosticInfos;
      } UA_DataChangeNotification;
      
      #define UA_TYPES_DATACHANGENOTIFICATION 141

      CloseSecureChannelResponse 

      关闭安全通道。

      typedef struct {
          UA_ResponseHeader responseHeader;
      } UA_CloseSecureChannelResponse;
      
      #define UA_TYPES_CLOSESECURECHANNELRESPONSE 142

      ModifyMonitoredItemsRequest 

      typedef struct {
          UA_RequestHeader requestHeader;
          UA_UInt32 subscriptionId;
          UA_TimestampsToReturn timestampsToReturn;
          size_t itemsToModifySize;
          UA_MonitoredItemModifyRequest *itemsToModify;
      } UA_ModifyMonitoredItemsRequest;
      
      #define UA_TYPES_MODIFYMONITOREDITEMSREQUEST 143

      SetMonitoringModeResponse 

      typedef struct {
          UA_ResponseHeader responseHeader;
          size_t resultsSize;
          UA_StatusCode *results;
          size_t diagnosticInfosSize;
          UA_DiagnosticInfo *diagnosticInfos;
      } UA_SetMonitoringModeResponse;
      
      #define UA_TYPES_SETMONITORINGMODERESPONSE 144

      FindServersRequest 

      查找发现服务器已知的服务器。

      typedef struct {
          UA_RequestHeader requestHeader;
          UA_String endpointUrl;
          size_t localeIdsSize;
          UA_String *localeIds;
          size_t serverUrisSize;
          UA_String *serverUris;
      } UA_FindServersRequest;
      
      #define UA_TYPES_FINDSERVERSREQUEST 145

      参考描述

      参考文献的描述。

      typedef struct {
          UA_NodeId referenceTypeId;
          UA_Boolean isForward;
          UA_ExpandedNodeId nodeId;
          UA_QualifiedName browseName;
          UA_LocalizedText displayName;
          UA_NodeClass nodeClass;
          UA_ExpandedNodeId typeDefinition;
      } UA_ReferenceDescription;
      
      #define UA_TYPES_REFERENCEDESCRIPTION 146

      SetPublishingModeResponse 

      typedef struct {
          UA_ResponseHeader responseHeader;
          size_t resultsSize;
          UA_StatusCode *results;
          size_t diagnosticInfosSize;
          UA_DiagnosticInfo *diagnosticInfos;
      } UA_SetPublishingModeResponse;
      
      #define UA_TYPES_SETPUBLISHINGMODERESPONSE 147

      ContentFilterResult 

      typedef struct {
          size_t elementResultsSize;
          UA_ContentFilterElementResult *elementResults;
          size_t elementDiagnosticInfosSize;
          UA_DiagnosticInfo *elementDiagnosticInfos;
      } UA_ContentFilterResult;
      
      #define UA_TYPES_CONTENTFILTERRESULT 148

      RegisterServerResponse 

      使用发现服务器注册服务器。

      typedef struct {
          UA_ResponseHeader responseHeader;
      } UA_RegisterServerResponse;
      
      #define UA_TYPES_REGISTERSERVERRESPONSE 149

      AddReferencesItem 

      添加对服务器地址空间的引用的请求。

      typedef struct {
          UA_NodeId sourceNodeId;
          UA_NodeId referenceTypeId;
          UA_Boolean isForward;
          UA_String targetServerUri;
          UA_ExpandedNodeId targetNodeId;
          UA_NodeClass targetNodeClass;
      } UA_AddReferencesItem;
      
      #define UA_TYPES_ADDREFERENCESITEM 150

      QueryDataDescription 

      typedef struct {
          UA_RelativePath relativePath;
          UA_UInt32 attributeId;
          UA_String indexRange;
      } UA_QueryDataDescription;
      
      #define UA_TYPES_QUERYDATADESCRIPTION 151

      CreateSubscriptionResponse 

      typedef struct {
          UA_ResponseHeader responseHeader;
          UA_UInt32 subscriptionId;
          UA_Double revisedPublishingInterval;
          UA_UInt32 revisedLifetimeCount;
          UA_UInt32 revisedMaxKeepAliveCount;
      } UA_CreateSubscriptionResponse;
      
      #define UA_TYPES_CREATESUBSCRIPTIONRESPONSE 152

      NetworkGroupDataType 

      typedef struct {
          UA_String serverUri;
          size_t networkPathsSize;
          UA_EndpointUrlListDataType *networkPaths;
      } UA_NetworkGroupDataType;
      
      #define UA_TYPES_NETWORKGROUPDATATYPE 153

      DeleteReferencesResponse 

      从服务器地址空间中删除一个或多个引用。

      typedef struct {
          UA_ResponseHeader responseHeader;
          size_t resultsSize;
          UA_StatusCode *results;
          size_t diagnosticInfosSize;
          UA_DiagnosticInfo *diagnosticInfos;
      } UA_DeleteReferencesResponse;
      
      #define UA_TYPES_DELETEREFERENCESRESPONSE 154

      CreateMonitoredItemsResponse 

      typedef struct {
          UA_ResponseHeader responseHeader;
          size_t resultsSize;
          UA_MonitoredItemCreateResult *results;
          size_t diagnosticInfosSize;
          UA_DiagnosticInfo *diagnosticInfos;
      } UA_CreateMonitoredItemsResponse;
      
      #define UA_TYPES_CREATEMONITOREDITEMSRESPONSE 155

      CallResponse 

      typedef struct {
          UA_ResponseHeader responseHeader;
          size_t resultsSize;
          UA_CallMethodResult *results;
          size_t diagnosticInfosSize;
          UA_DiagnosticInfo *diagnosticInfos;
      } UA_CallResponse;
      
      #define UA_TYPES_CALLRESPONSE 156

      DeleteNodesResponse 

      从服务器地址空间中删除一个或多个节点。

      typedef struct {
          UA_ResponseHeader responseHeader;
          size_t resultsSize;
          UA_StatusCode *results;
          size_t diagnosticInfosSize;
          UA_DiagnosticInfo *diagnosticInfos;
      } UA_DeleteNodesResponse;
      
      #define UA_TYPES_DELETENODESRESPONSE 157

      RepublishResponse 

      typedef struct {
          UA_ResponseHeader responseHeader;
          UA_NotificationMessage notificationMessage;
      } UA_RepublishResponse;
      
      #define UA_TYPES_REPUBLISHRESPONSE 158

      MonitoredItemCreateRequest 

      typedef struct {
          UA_ReadValueId itemToMonitor;
          UA_MonitoringMode monitoringMode;
          UA_MonitoringParameters requestedParameters;
      } UA_MonitoredItemCreateRequest;
      
      #define UA_TYPES_MONITOREDITEMCREATEREQUEST 159

      DeleteReferencesRequest 

      从服务器地址空间中删除一个或多个引用。

      typedef struct {
          UA_RequestHeader requestHeader;
          size_t referencesToDeleteSize;
          UA_DeleteReferencesItem *referencesToDelete;
      } UA_DeleteReferencesRequest;
      
      #define UA_TYPES_DELETEREFERENCESREQUEST 160

      ReadResponse 

      typedef struct {
          UA_ResponseHeader responseHeader;
          size_t resultsSize;
          UA_DataValue *results;
          size_t diagnosticInfosSize;
          UA_DiagnosticInfo *diagnosticInfos;
      } UA_ReadResponse;
      
      #define UA_TYPES_READRESPONSE 161

      AddReferencesRequest 

      添加一个或多个对服务器地址空间的引用。

      typedef struct {
          UA_RequestHeader requestHeader;
          size_t referencesToAddSize;
          UA_AddReferencesItem *referencesToAdd;
      } UA_AddReferencesRequest;
      
      #define UA_TYPES_ADDREFERENCESREQUEST 162

      ReadRequest 

      typedef struct {
          UA_RequestHeader requestHeader;
          UA_Double maxAge;
          UA_TimestampsToReturn timestampsToReturn;
          size_t nodesToReadSize;
          UA_ReadValueId *nodesToRead;
      } UA_ReadRequest;
      
      #define UA_TYPES_READREQUEST 163

      OpenSecureChannelRequest 

      使用服务器创建安全通道。

      typedef struct {
          UA_RequestHeader requestHeader;
          UA_UInt32 clientProtocolVersion;
          UA_SecurityTokenRequestType requestType;
          UA_MessageSecurityMode securityMode;
          UA_ByteString clientNonce;
          UA_UInt32 requestedLifetime;
      } UA_OpenSecureChannelRequest;
      
      #define UA_TYPES_OPENSECURECHANNELREQUEST 164

      RegisterServer2Respon

      AddNodesItem 

      将节点添加到服务器地址空间的请求。

      typedef struct {
          UA_ExpandedNodeId parentNodeId;
          UA_NodeId referenceTypeId;
          UA_ExpandedNodeId requestedNewNodeId;
          UA_QualifiedName browseName;
          UA_NodeClass nodeClass;
          UA_ExtensionObject nodeAttributes;
          UA_ExpandedNodeId typeDefinition;
      } UA_AddNodesItem;
      
      #define UA_TYPES_ADDNODESITEM 166

      NodeTypeDescription 

      typedef struct {
          UA_ExpandedNodeId typeDefinitionNode;
          UA_Boolean includeSubTypes;
          size_t dataToReturnSize;
          UA_QueryDataDescription *dataToReturn;
      } UA_NodeTypeDescription;
      
      #define UA_TYPES_NODETYPEDESCRIPTION 167

      ServerStatusDataType 

      typedef struct {
          UA_DateTime startTime;
          UA_DateTime currentTime;
          UA_ServerState state;
          UA_BuildInfo buildInfo;
          UA_UInt32 secondsTillShutdown;
          UA_LocalizedText shutdownReason;
      } UA_ServerStatusDataType;
      
      #define UA_TYPES_SERVERSTATUSDATATYPE 168

      AttributeOperand 

      typedef struct {
          UA_DateTime startTime;
          UA_DateTime currentTime;
          UA_ServerState state;
          UA_BuildInfo buildInfo;
          UA_UInt32 secondsTillShutdown;
          UA_LocalizedText shutdownReason;
      } UA_ServerStatusDataType;
      
      #define UA_TYPES_SERVERSTATUSDATATYPE 168

      AddReferencesResponse 

      添加一个或多个对服务器地址空间的引用。

      typedef struct {
          UA_ResponseHeader responseHeader;
          size_t resultsSize;
          UA_StatusCode *results;
          size_t diagnosticInfosSize;
          UA_DiagnosticInfo *diagnosticInfos;
      } UA_AddReferencesResponse;
      
      #define UA_TYPES_ADDREFERENCESRESPONSE 170

      EventFilterResult 

      typedef struct {
          size_t selectClauseResultsSize;
          UA_StatusCode *selectClauseResults;
          size_t selectClauseDiagnosticInfosSize;
          UA_DiagnosticInfo *selectClauseDiagnosticInfos;
          UA_ContentFilterResult whereClauseResult;
      } UA_EventFilterResult;
      
      #define UA_TYPES_EVENTFILTERRESULT 171

      TranslateBrowsePathsToNodeIdsResponse 

      转换服务器地址空间中的一个或多个路径。

      typedef struct {
          UA_ResponseHeader responseHeader;
          size_t resultsSize;
          UA_BrowsePathResult *results;
          size_t diagnosticInfosSize;
          UA_DiagnosticInfo *diagnosticInfos;
      } UA_TranslateBrowsePathsToNodeIdsResponse;
      
      #define UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE 172

      DataChangeFilter 

      typedef struct {
          UA_ResponseHeader responseHeader;
          size_t resultsSize;
          UA_BrowsePathResult *results;
          size_t diagnosticInfosSize;
          UA_DiagnosticInfo *diagnosticInfos;
      } UA_TranslateBrowsePathsToNodeIdsResponse;
      
      #define UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE 172

      ContentFilterElement 

      typedef struct {
          UA_FilterOperator filterOperator;
          size_t filterOperandsSize;
          UA_ExtensionObject *filterOperands;
      } UA_ContentFilterElement;
      
      #define UA_TYPES_CONTENTFILTERELEMENT 174

      TranslateBrowsePathsToNodeIdsRequest 

      转换服务器地址空间中的一个或多个路径。

      typedef struct {
          UA_RequestHeader requestHeader;
          size_t browsePathsSize;
          UA_BrowsePath *browsePaths;
      } UA_TranslateBrowsePathsToNodeIdsRequest;
      
      #define UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSREQUEST 175

      CloseSessionResponse 

      关闭与服务器的会话。

      typedef struct {
          UA_ResponseHeader responseHeader;
      } UA_CloseSessionResponse;
      
      #define UA_TYPES_CLOSESESSIONRESPONSE 176

      ApplicationDescription 

      描述应用程序以及如何查找它。

      typedef struct {
          UA_String applicationUri;
          UA_String productUri;
          UA_LocalizedText applicationName;
          UA_ApplicationType applicationType;
          UA_String gatewayServerUri;
          UA_String discoveryProfileUri;
          size_t discoveryUrlsSize;
          UA_String *discoveryUrls;
      } UA_ApplicationDescription;
      
      #define UA_TYPES_APPLICATIONDESCRIPTION 177

      SessionDiagnosticsDataType 

      typedef struct {
          UA_NodeId sessionId;
          UA_String sessionName;
          UA_ApplicationDescription clientDescription;
          UA_String serverUri;
          UA_String endpointUrl;
          size_t localeIdsSize;
          UA_String *localeIds;
          UA_Double actualSessionTimeout;
          UA_UInt32 maxResponseMessageSize;
          UA_DateTime clientConnectionTime;
          UA_DateTime clientLastContactTime;
          UA_UInt32 currentSubscriptionsCount;
          UA_UInt32 currentMonitoredItemsCount;
          UA_UInt32 currentPublishRequestsInQueue;
          UA_ServiceCounterDataType totalRequestCount;
          UA_UInt32 unauthorizedRequestCount;
          UA_ServiceCounterDataType readCount;
          UA_ServiceCounterDataType historyReadCount;
          UA_ServiceCounterDataType writeCount;
          UA_ServiceCounterDataType historyUpdateCount;
          UA_ServiceCounterDataType callCount;
          UA_ServiceCounterDataType createMonitoredItemsCount;
          UA_ServiceCounterDataType modifyMonitoredItemsCount;
          UA_ServiceCounterDataType setMonitoringModeCount;
          UA_ServiceCounterDataType setTriggeringCount;
          UA_ServiceCounterDataType deleteMonitoredItemsCount;
          UA_ServiceCounterDataType createSubscriptionCount;
          UA_ServiceCounterDataType modifySubscriptionCount;
          UA_ServiceCounterDataType setPublishingModeCount;
          UA_ServiceCounterDataType publishCount;
          UA_ServiceCounterDataType republishCount;
          UA_ServiceCounterDataType transferSubscriptionsCount;
          UA_ServiceCounterDataType deleteSubscriptionsCount;
          UA_ServiceCounterDataType addNodesCount;
          UA_ServiceCounterDataType addReferencesCount;
          UA_ServiceCounterDataType deleteNodesCount;
          UA_ServiceCounterDataType deleteReferencesCount;
          UA_ServiceCounterDataType browseCount;
          UA_ServiceCounterDataType browseNextCount;
          UA_ServiceCounterDataType translateBrowsePathsToNodeIdsCount;
          UA_ServiceCounterDataType queryFirstCount;
          UA_ServiceCounterDataType queryNextCount;
          UA_ServiceCounterDataType registerNodesCount;
          UA_ServiceCounterDataType unregisterNodesCount;
      } UA_SessionDiagnosticsDataType;
      
      #define UA_TYPES_SESSIONDIAGNOSTICSDATATYPE 178

      ServiceFault 

      当存在服务级别错误时,所有服务返回的响应。

      typedef struct {
          UA_ResponseHeader responseHeader;
      } UA_ServiceFault;
      
      #define UA_TYPES_SERVICEFAULT 179

      注册服务器

      使用发现服务器注册服务器所需的信息。

      typedef struct {
          UA_String serverUri;
          UA_String productUri;
          size_t serverNamesSize;
          UA_LocalizedText *serverNames;
          UA_ApplicationType serverType;
          UA_String gatewayServerUri;
          size_t discoveryUrlsSize;
          UA_String *discoveryUrls;
          UA_String semaphoreFilePath;
          UA_Boolean isOnline;
      } UA_RegisteredServer;
      
      #define UA_TYPES_REGISTEREDSERVER 180

      AggregateFilter 

      typedef struct {
          UA_DateTime startTime;
          UA_NodeId aggregateType;
          UA_Double processingInterval;
          UA_AggregateConfiguration aggregateConfiguration;
      } UA_AggregateFilter;
      
      #define UA_TYPES_AGGREGATEFILTER 181

      RegisterServerRequest 

      使用发现服务器注册服务器。

      typedef struct {
          UA_RequestHeader requestHeader;
          UA_RegisteredServer server;
      } UA_RegisterServerRequest;
      
      #define UA_TYPES_REGISTERSERVERREQUEST 182

      EndpointDescription 

      可用于访问服务器的端点的描述。

      typedef struct {
          UA_String endpointUrl;
          UA_ApplicationDescription server;
          UA_ByteString serverCertificate;
          UA_MessageSecurityMode securityMode;
          UA_String securityPolicyUri;
          size_t userIdentityTokensSize;
          UA_UserTokenPolicy *userIdentityTokens;
          UA_String transportProfileUri;
          UA_Byte securityLevel;
      } UA_EndpointDescription;
      
      #define UA_TYPES_ENDPOINTDESCRIPTION 183

      CreateMonitoredItemsRequest 

      typedef struct {
          UA_RequestHeader requestHeader;
          UA_UInt32 subscriptionId;
          UA_TimestampsToReturn timestampsToReturn;
          size_t itemsToCreateSize;
          UA_MonitoredItemCreateRequest *itemsToCreate;
      } UA_CreateMonitoredItemsRequest;
      
      #define UA_TYPES_CREATEMONITOREDITEMSREQUEST 184

      ContentFilter 

      typedef struct {
          size_t elementsSize;
          UA_ContentFilterElement *elements;
      } UA_ContentFilter;
      
      #define UA_TYPES_CONTENTFILTER 185

      QueryFirstResponse 

      typedef struct {
          UA_ResponseHeader responseHeader;
          size_t queryDataSetsSize;
          UA_QueryDataSet *queryDataSets;
          UA_ByteString continuationPoint;
          size_t parsingResultsSize;
          UA_ParsingResult *parsingResults;
          size_t diagnosticInfosSize;
          UA_DiagnosticInfo *diagnosticInfos;
          UA_ContentFilterResult filterResult;
      } UA_QueryFirstResponse;
      
      #define UA_TYPES_QUERYFIRSTRESPONSE 186

      AddNodesRequest 

      将一个或多个节点添加到服务器地址空间。

      typedef struct {
          UA_RequestHeader requestHeader;
          size_t nodesToAddSize;
          UA_AddNodesItem *nodesToAdd;
      } UA_AddNodesRequest;
      
      #define UA_TYPES_ADDNODESREQUEST 187

      BrowseRequest 

      从服务器地址空间浏览一个或多个节点的引用。

      typedef struct {
          UA_RequestHeader requestHeader;
          UA_ViewDescription view;
          UA_UInt32 requestedMaxReferencesPerNode;
          size_t nodesToBrowseSize;
          UA_BrowseDescription *nodesToBrowse;
      } UA_BrowseRequest;
      
      #define UA_TYPES_BROWSEREQUEST 188

      BrowseResult 

      浏览操作的结果。

      typedef struct {
          UA_StatusCode statusCode;
          UA_ByteString continuationPoint;
          size_t referencesSize;
          UA_ReferenceDescription *references;
      } UA_BrowseResult;
      
      #define UA_TYPES_BROWSERESULT 189

      RegisterServer2Request 

      typedef struct {
          UA_RequestHeader requestHeader;
          UA_RegisteredServer server;
          size_t discoveryConfigurationSize;
          UA_ExtensionObject *discoveryConfiguration;
      } UA_RegisterServer2Request;
      
      #define UA_TYPES_REGISTERSERVER2REQUEST 190

      CreateSessionRequest 

      与服务器创建新会话。

      typedef struct {
          UA_RequestHeader requestHeader;
          UA_ApplicationDescription clientDescription;
          UA_String serverUri;
          UA_String endpointUrl;
          UA_String sessionName;
          UA_ByteString clientNonce;
          UA_ByteString clientCertificate;
          UA_Double requestedSessionTimeout;
          UA_UInt32 maxResponseMessageSize;
      } UA_CreateSessionRequest;
      
      #define UA_TYPES_CREATESESSIONREQUEST 191

      EventFilter 

      typedef struct {
          size_t selectClausesSize;
          UA_SimpleAttributeOperand *selectClauses;
          UA_ContentFilter whereClause;
      } UA_EventFilter;
      
      #define UA_TYPES_EVENTFILTER 192

      GetEndpointsResponse 

      获取服务器使用的端点。

      typedef struct {
          size_t selectClausesSize;
          UA_SimpleAttributeOperand *selectClauses;
          UA_ContentFilter whereClause;
      } UA_EventFilter;
      
      #define UA_TYPES_EVENTFILTER 192

      FindServersResponse 

      查找发现服务器已知的服务器。

      typedef struct {
          UA_ResponseHeader responseHeader;
          size_t serversSize;
          UA_ApplicationDescription *servers;
      } UA_FindServersResponse;
      
      #define UA_TYPES_FINDSERVERSRESPONSE 194

      BrowseNextResponse 

      继续一个或多个浏览操作。

      typedef struct {
          UA_ResponseHeader responseHeader;
          size_t resultsSize;
          UA_BrowseResult *results;
          size_t diagnosticInfosSize;
          UA_DiagnosticInfo *diagnosticInfos;
      } UA_BrowseNextResponse;
      
      #define UA_TYPES_BROWSENEXTRESPONSE 195

      BrowseResponse 

      从服务器地址空间浏览一个或多个节点的引用。

      typedef struct {
          UA_ResponseHeader responseHeader;
          size_t resultsSize;
          UA_BrowseResult *results;
          size_t diagnosticInfosSize;
          UA_DiagnosticInfo *diagnosticInfos;
      } UA_BrowseResponse;
      
      #define UA_TYPES_BROWSERESPONSE 196

      CreateSessionResponse 

      与服务器创建新会话。

      typedef struct {
          UA_ResponseHeader responseHeader;
          UA_NodeId sessionId;
          UA_NodeId authenticationToken;
          UA_Double revisedSessionTimeout;
          UA_ByteString serverNonce;
          UA_ByteString serverCertificate;
          size_t serverEndpointsSize;
          UA_EndpointDescription *serverEndpoints;
          size_t serverSoftwareCertificatesSize;
          UA_SignedSoftwareCertificate *serverSoftwareCertificates;
          UA_SignatureData serverSignature;
          UA_UInt32 maxRequestMessageSize;
      } UA_CreateSessionResponse;
      
      #define UA_TYPES_CREATESESSIONRESPONSE 197

      QueryFirstRequest 

      typedef struct {
          UA_RequestHeader requestHeader;
          UA_ViewDescription view;
          size_t nodeTypesSize;
          UA_NodeTypeDescription *nodeTypes;
          UA_ContentFilter filter;
          UA_UInt32 maxDataSetsToReturn;
          UA_UInt32 maxReferencesToReturn;
      } UA_QueryFirstRequest;
      
      #define UA_TYPES_QUERYFIRSTREQUEST 198

      UtcTime 

      以通用协调时间(UTC)指定的日期/时间值。

      typedef UA_DateTime UA_UtcTime;
      
      #define UA_TYPES_UTCTIME UA_TYPES_DATETIME

      LocaleID 

      用户区域设置的标识符。

      typedef UA_String UA_LocaleId;
      
      #define UA_TYPES_LOCALEID UA_TYPES_STRING

      持续时间

      以毫秒为单位测量的一段时间。

      typedef UA_Double UA_Duration;
      
      #define UA_TYPES_DURATION UA_TYPES_DOUBLE

       

    • 不推荐使用的数据类型

      以下定义已弃用,将在open62541的未来版本中删除。

      typedef struct {
          UA_StatusCode code;      /* The numeric value of the StatusCode */
          const char* name;        /* The symbolic name */
          const char* explanation; /* Short message explaining the StatusCode */
      } UA_StatusCodeDescription;
      
      extern const UA_StatusCodeDescription statusCodeExplanation_default;
      
      UA_DEPRECATED static UA_INLINE const UA_StatusCodeDescription *
      UA_StatusCode_description(UA_StatusCode code) {
          return &statusCodeExplanation_default;
      }
      
      UA_DEPRECATED static UA_INLINE const char *
      UA_StatusCode_explanation(UA_StatusCode code) {
          return statusCodeExplanation_default.name;
      }
      
      UA_DEPRECATED UA_String
      UA_DateTime_toString(UA_DateTime t);
      
      /* The old DateTime conversion macros */
      UA_DEPRECATED static UA_INLINE double
      deprecatedDateTimeMultiple(double multiple) {
          return multiple;
      }
      
      #define UA_USEC_TO_DATETIME deprecatedDateTimeMultiple((UA_Double)UA_DATETIME_USEC)
      #define UA_MSEC_TO_DATETIME deprecatedDateTimeMultiple((UA_Double)UA_DATETIME_MSEC)
      #define UA_SEC_TO_DATETIME deprecatedDateTimeMultiple((UA_Double)UA_DATETIME_SEC)
      #define UA_DATETIME_TO_USEC deprecatedDateTimeMultiple(1.0 / ((UA_Double)UA_DATETIME_USEC))
      #define UA_DATETIME_TO_MSEC deprecatedDateTimeMultiple(1.0 / ((UA_Double)UA_DATETIME_MSEC))
      #define UA_DATETIME_TO_SEC deprecatedDateTimeMultiple(1.0 / ((UA_Double)UA_DATETIME_SEC))

      专业化,例如UA_Int32_new()从泛型类型操作派生为静态内联函数。

      typedef struct {
      #ifdef UA_ENABLE_TYPENAMES
          const char *memberName;
      #endif
          UA_UInt16 memberTypeIndex;    /* Index of the member in the array of data
                                           types */
          UA_Byte   padding;            /* How much padding is there before this
                                           member element? For arrays this is the
                                           padding before the size_t length member.
                                           (No padding between size_t and the
                                           following ptr.) */
          UA_Boolean namespaceZero : 1; /* The type of the member is defined in
                                           namespace zero. In this implementation,
                                           types from custom namespace may contain
                                           members from the same namespace or
                                           namespace zero only.*/
          UA_Boolean isArray       : 1; /* The member is an array */
      } UA_DataTypeMember;
      
      struct UA_DataType {
      #ifdef UA_ENABLE_TYPENAMES
          const char *typeName;
      #endif
          UA_NodeId  typeId;           /* The nodeid of the type */
          UA_UInt16  memSize;          /* Size of the struct in memory */
          UA_UInt16  typeIndex;        /* Index of the type in the datatypetable */
          UA_Byte    membersSize;      /* How many members does the type have? */
          UA_Boolean builtin      : 1; /* The type is "builtin" and has dedicated de-
                                          and encoding functions */
          UA_Boolean pointerFree  : 1; /* The type (and its members) contains no
                                          pointers that need to be freed */
          UA_Boolean overlayable  : 1; /* The type has the identical memory layout in
                                          memory and on the binary stream. */
          UA_UInt16  binaryEncodingId; /* NodeId of datatype when encoded as binary */
          //UA_UInt16  xmlEncodingId;  /* NodeId of datatype when encoded as XML */
          UA_DataTypeMember *members;
      };
      
      /* Test if the data type is a numeric builtin data type. This includes Boolean,
       * integers and floating point numbers. Not included are DateTime and
       * StatusCode. */
      UA_Boolean
      isDataTypeNumeric(const UA_DataType *type);
      
      /* The following is used to exclude type names in the definition of UA_DataType
       * structures if the feature is disabled. */
      #ifdef UA_ENABLE_TYPENAMES
      # define UA_TYPENAME(name) name,
      #else
      # define UA_TYPENAME(name)
      #endif

      可以使用UA_TYPES [UA_TYPES_XXX]访问内置数据类型,其中XXX是数据类型的名称。如果只知道类型的NodeId,请使用以下方法检索数据类型说明。

      typedef struct {
      #ifdef UA_ENABLE_TYPENAMES
          const char *memberName;
      #endif
          UA_UInt16 memberTypeIndex;    /* Index of the member in the array of data
                                           types */
          UA_Byte   padding;            /* How much padding is there before this
                                           member element? For arrays this is the
                                           padding before the size_t length member.
                                           (No padding between size_t and the
                                           following ptr.) */
          UA_Boolean namespaceZero : 1; /* The type of the member is defined in
                                           namespace zero. In this implementation,
                                           types from custom namespace may contain
                                           members from the same namespace or
                                           namespace zero only.*/
          UA_Boolean isArray       : 1; /* The member is an array */
      } UA_DataTypeMember;
      
      struct UA_DataType {
      #ifdef UA_ENABLE_TYPENAMES
          const char *typeName;
      #endif
          UA_NodeId  typeId;           /* The nodeid of the type */
          UA_UInt16  memSize;          /* Size of the struct in memory */
          UA_UInt16  typeIndex;        /* Index of the type in the datatypetable */
          UA_Byte    membersSize;      /* How many members does the type have? */
          UA_Boolean builtin      : 1; /* The type is "builtin" and has dedicated de-
                                          and encoding functions */
          UA_Boolean pointerFree  : 1; /* The type (and its members) contains no
                                          pointers that need to be freed */
          UA_Boolean overlayable  : 1; /* The type has the identical memory layout in
                                          memory and on the binary stream. */
          UA_UInt16  binaryEncodingId; /* NodeId of datatype when encoded as binary */
          //UA_UInt16  xmlEncodingId;  /* NodeId of datatype when encoded as XML */
          UA_DataTypeMember *members;
      };
      
      /* Test if the data type is a numeric builtin data type. This includes Boolean,
       * integers and floating point numbers. Not included are DateTime and
       * StatusCode. */
      UA_Boolean
      isDataTypeNumeric(const UA_DataType *type);
      
      /* The following is used to exclude type names in the definition of UA_DataType
       * structures if the feature is disabled. */
      #ifdef UA_ENABLE_TYPENAMES
      # define UA_TYPENAME(name) name,
      #else
      # define UA_TYPENAME(name)
      #endif

      以下函数用于数据类型的通用处理。

      typedef struct {
      #ifdef UA_ENABLE_TYPENAMES
          const char *memberName;
      #endif
          UA_UInt16 memberTypeIndex;    /* Index of the member in the array of data
                                           types */
          UA_Byte   padding;            /* How much padding is there before this
                                           member element? For arrays this is the
                                           padding before the size_t length member.
                                           (No padding between size_t and the
                                           following ptr.) */
          UA_Boolean namespaceZero : 1; /* The type of the member is defined in
                                           namespace zero. In this implementation,
                                           types from custom namespace may contain
                                           members from the same namespace or
                                           namespace zero only.*/
          UA_Boolean isArray       : 1; /* The member is an array */
      } UA_DataTypeMember;
      
      struct UA_DataType {
      #ifdef UA_ENABLE_TYPENAMES
          const char *typeName;
      #endif
          UA_NodeId  typeId;           /* The nodeid of the type */
          UA_UInt16  memSize;          /* Size of the struct in memory */
          UA_UInt16  typeIndex;        /* Index of the type in the datatypetable */
          UA_Byte    membersSize;      /* How many members does the type have? */
          UA_Boolean builtin      : 1; /* The type is "builtin" and has dedicated de-
                                          and encoding functions */
          UA_Boolean pointerFree  : 1; /* The type (and its members) contains no
                                          pointers that need to be freed */
          UA_Boolean overlayable  : 1; /* The type has the identical memory layout in
                                          memory and on the binary stream. */
          UA_UInt16  binaryEncodingId; /* NodeId of datatype when encoded as binary */
          //UA_UInt16  xmlEncodingId;  /* NodeId of datatype when encoded as XML */
          UA_DataTypeMember *members;
      };
      
      /* Test if the data type is a numeric builtin data type. This includes Boolean,
       * integers and floating point numbers. Not included are DateTime and
       * StatusCode. */
      UA_Boolean
      isDataTypeNumeric(const UA_DataType *type);
      
      /* The following is used to exclude type names in the definition of UA_DataType
       * structures if the feature is disabled. */
      #ifdef UA_ENABLE_TYPENAMES
      # define UA_TYPENAME(name) name,
      #else
      # define UA_TYPENAME(name)
      #endif

      数组处理

      在OPC UA中,数组的长度可以为零或更多,具有通常的含义。此外,数组可能未定义。然后,他们甚至没有长度。在二进制编码中,这由长度为-1的数组指示。

      但是在open62541中,我们使用size_t数组长度。未定义的数组长度为0,数据指针为NULL。长度为0的数组也具有长度0但是数据指针UA_EMPTY_ARRAY_SENTINEL

      /* Allocates and initializes an array of variables of a specific type
       *
       * @param size The requested array length
       * @param type The datatype description
       * @return Returns the memory location of the variable or NULL if no memory
                 could be allocated */
      void * UA_Array_new(size_t size, const UA_DataType *type);
      
      /* Allocates and copies an array
       *
       * @param src The memory location of the source array
       * @param size The size of the array
       * @param dst The location of the pointer to the new array
       * @param type The datatype of the array members
       * @return Returns UA_STATUSCODE_GOOD or UA_STATUSCODE_BADOUTOFMEMORY */
      UA_StatusCode
      UA_Array_copy(const void *src, size_t size, void **dst,
                    const UA_DataType *type);
      
      /* Deletes an array.
       *
       * @param p The memory location of the array
       * @param size The size of the array
       * @param type The datatype of the array members */
      void UA_Array_delete(void *p, size_t size, const UA_DataType *type);

      随机数生成器

      如果定义了UA_ENABLE_MULTITHREADING,则种子将存储在线程本地存储中。为服务器/客户端中的每个线程初始化种子。

      void UA_random_seed(UA_UInt64 seed);
      UA_UInt32 UA_UInt32_random(void); /* no cryptographic entropy */
      UA_Guid UA_Guid_random(void);     /* no cryptographic entropy */

      服务

      在OPC UA中,所有通信都基于服务调用,每个服务调用由请求和响应消息组成。这些消息定义为具有二进制编码的数据结构,并在“ 生成的数据类型定义”中列出。由于所有服务都是在标准中预定义的,因此用户无法对其进行修改。但您可以使用Call服务在服务器上调用用户定义的方法。

      以下服务签名是内部的,用户不可见。不过,我们在此提供它们,以概述OPC UA的功能。请参阅客户端服务器 API,其中服务向最终用户公开。请参阅OPC UA标准的第4部分,了解服务的权威定义及其行为。

      大多数服务将服务器,当前会话以及指向请求和响应结构的指针作为输入。可能的错误代码作为响应的一部分返回。

      typedef void (*UA_Service)(UA_Server*, UA_Session*,
                                 const void *request, void *response);
      
      typedef UA_StatusCode (*UA_InSituService)(UA_Server*, UA_Session*, UA_MessageContext *mc,
                                                const void *request, UA_ResponseHeader *rh);

      发现服务集

      此服务集定义用于发现服务器实现的端点的服务,并读取这些端点的安全配置。

      FindServers服务

      返回服务器或Discovery Server已知的服务器。客户端可以通过指定筛选条件来减少返回的结果数

      void Service_FindServers(UA_Server *server, UA_Session *session,
                               const UA_FindServersRequest *request,
                               UA_FindServersResponse *response);

      GetEndpoints服务

      返回服务器支持的端点以及建立SecureChannel和Session所需的所有配置信息。

      void Service_FindServers(UA_Server *server, UA_Session *session,
                               const UA_FindServersRequest *request,
                               UA_FindServersResponse *response);

      FindServersOnNetwork服务

      返回Discovery Server已知的服务器。与FindServer不同,此服务仅由Discovery Servers实现。它还返回可能通过多播检测到的服务

      void Service_FindServersOnNetwork(UA_Server *server, UA_Session *session,
                                        const UA_FindServersOnNetworkRequest *request,
                                        UA_FindServersOnNetworkResponse *response);
      
      # endif /* UA_ENABLE_DISCOVERY_MULTICAST */

      RegisterServer 

      在本地发现服务中注册远程服务器。

      void Service_RegisterServer(UA_Server *server, UA_Session *session,
                                  const UA_RegisterServerRequest *request,
                                  UA_RegisterServerResponse *response);

      RegisterServer2 

      此服务允许服务器向Discovery Server注册其DiscoveryUrls和功能。它使用FindServersOnNetwork所需的信息扩展了RegisterServer的注册信息。

      void Service_RegisterServer(UA_Server *server, UA_Session *session,
                                  const UA_RegisterServerRequest *request,
                                  UA_RegisterServerResponse *response);

      SecureChannel服务集

      此服务集定义用于打开通信通道的服务,以确保与服务器交换的所有消息的机密性和完整性。

      OpenSecureChannel服务

      打开或更新SecureChannel,可用于确保会话期间消息交换的机密性和完整性。

      void Service_OpenSecureChannel(UA_Server *server, UA_SecureChannel* channel,
                                     const UA_OpenSecureChannelRequest *request,
                                     UA_OpenSecureChannelResponse *response);

      CloseSecureChannel服务

      用于终止SecureChannel。

      void Service_CloseSecureChannel(UA_Server *server, UA_SecureChannel *channel);

      会话服务集

      此服务集为会话上下文中的应用程序层连接建立定义服务。

      CreateSession服务

      由OPC UA客户端用于创建会话,服务器返回两个唯一标识会话的值。第一个值是sessionId,用于标识审计日志和服务器地址空间中的会话。第二个是authenticationToken,用于将传入请求与Session关联。

      void Service_CloseSecureChannel(UA_Server *server, UA_SecureChannel *channel);

      ActivateSession 

      客户端使用它将SoftwareCertificates提交给服务器进行验证并指定与会话关联的用户的身份。此服务请求应在客户在CreateSession之后发出任何其他服务请求之前发出。如果不这样做,将导致服务器关闭会话。

      void Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel,
                                   UA_Session *session,
                                   const UA_ActivateSessionRequest *request,
                                   UA_ActivateSessionResponse *response);

      CloseSession 

      用于终止会话。

      void Service_CloseSession(UA_Server *server, UA_Session *session,
                                const UA_CloseSessionRequest *request,
                                UA_CloseSessionResponse *response);

      取消服务

      用于取消未完成的服务请求。成功取消的服务请求应使用Bad_RequestCancelledByClient进行响应。

      /* Not Implemented */
      

      NodeManagement服务集

      此服务集定义服务以添加和删除它们之间的AddressSpace节点和引用。即使创建它们的客户端与服务器断开连接,所有添加的节点仍继续存在于AddressSpace中。

      AddNodes服务

      用于将一个或多个节点添加到AddressSpace层次结构中。

      void Service_AddNodes(UA_Server *server, UA_Session *session,
                            const UA_AddNodesRequest *request,
                            UA_AddNodesResponse *response);

      AddReferences服务

      用于向一个或多个节点添加一个或多个引用。

      void Service_AddReferences(UA_Server *server, UA_Session *session,
                                 const UA_AddReferencesRequest *request,
                                 UA_AddReferencesResponse *response);

      DeleteNodes服务

      用于从AddressSpace中删除一个或多个节点。

      void Service_DeleteNodes(UA_Server *server, UA_Session *session,
                               const UA_DeleteNodesRequest *request,
                               UA_DeleteNodesResponse *response);

      DeleteReferences 

      用于删除节点的一​​个或多个引用。

      void Service_DeleteReferences(UA_Server *server, UA_Session *session,
                                    const UA_DeleteReferencesRequest *request,
                                    UA_DeleteReferencesResponse *response);

      查看服务集

      客户端使用View Service Set的浏览服务来浏览AddressSpace或通过作为AddressSpace子集的View。

      浏览服务

      用于发现指定节点的引用。浏览可以通过使用View进一步限制。此Browse Service还支持原始过滤功能。

      void Service_Browse(UA_Server *server, UA_Session *session,
                          const UA_BrowseRequest *request,
                          UA_BrowseResponse *response);

      BrowseNext服务

      用于请求下一组Browse或BrowseNext响应信息,这些信息太大而无法在单个响应中发送。在此上下文中“太大”意味着服务器无法返回更大的响应,或者返回的结果数超过客户端在原始浏览请求中指定的最大返回结果数。

      void Service_BrowseNext(UA_Server *server, UA_Session *session,
                              const UA_BrowseNextRequest *request,
                              UA_BrowseNextResponse *response);

      TranslateBrowsePathsToNodeIds服务

      用于将文本节点路径转换为各自的ID。

      void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session,
                   const UA_TranslateBrowsePathsToNodeIdsRequest *request,
                   UA_TranslateBrowsePathsToNodeIdsResponse *response);

      RegisterNodes服务

      客户端用于注册他们知道将重复访问的节点(例如,写入,呼叫)。它允许服务器设置所需的任何内容,以便访问操作更有效。

      void Service_RegisterNodes(UA_Server *server, UA_Session *session,
                                 const UA_RegisterNodesRequest *request,
                                 UA_RegisterNodesResponse *response);

      UnregisterNodes服务

      此服务用于取消注册通过RegisterNodes服务获取的NodeId。

      void Service_UnregisterNodes(UA_Server *server, UA_Session *session,
                                   const UA_UnregisterNodesRequest *request,
                                   UA_UnregisterNodesResponse *response);

      查询服务集

      此服务集用于向服务器发出查询。OPC UA查询是通用的,因为它提供了独立的底层存储机制查询功能,可用于访问各种OPC UA数据存储和信息管理系统。OPC UA查询允许客户端访问服务器维护的数据,而不了解用于内部存储数据的逻辑模式。了解AddressSpace就足够了。

      QueryFirst服务

      此服务用于向服务器发出查询请求。

      /* Not Implemented */
      

      QueryNext服务

      此服务用于请求下一组QueryFirst或QueryNext响应信息,这些信息太大而无法在单个响应中发送。

      /* Not Impelemented */
      

      属性服务集

      此服务集提供服务以访问属于节点的属性。

      阅读服务

      用于读取节点的属性。对于其元素已编制索引的构造属性值(例如数组),此服务允许客户端将整个索引值集合作为复合读取,读取单个元素或读取复合元素的元素范围。

      UA_StatusCode Service_Read(UA_Server *server, UA_Session *session, UA_MessageContext *mc,
                                 const UA_ReadRequest *request, UA_ResponseHeader *responseHeader);

      写服务

      用于编写节点的属性。对于其元素已编制索引的构造属性值(例如数组),此服务允许客户端将整组索引值编写为复合,写入单个元素或写入复合元素的范围。

      void Service_Write(UA_Server *server, UA_Session *session,
                         const UA_WriteRequest *request,
                         UA_WriteResponse *response);

      HistoryRead服务

      用于读取一个或多个节点的历史值或事件。服务器可以使用此服务为客户端提供历史值,尽管历史值本身在AddressSpace中不可见。

      /* Not Implemented */
      

      HistoryUpdate服务

      用于更新一个或多个节点的历史值或事件。多个请求参数指示服务器如何更新历史值或事件。有效操作是插入,替换或删除。

      /* Not Implemented */
      

      方法服务集

      方法服务集定义了调用方法的方法。方法应该是Object的一个组件。有关 更多信息,请参阅MethodNodes一节。

      呼叫服务

      用于调用(调用)方法。在现有会话的上下文中调用每个方法调用。如果会话终止,则方法执行的结果不能返回给客户端并被丢弃。

      void Service_Call(UA_Server *server, UA_Session *session,
                        const UA_CallRequest *request,
                        UA_CallResponse *response);

      MonitoredItem服务集

      客户端定义MonitoredItems以订阅数据和事件。每个MonitoredItem标识要监视的项目以及用于发送通知的订阅。要监视的项目可以是任何节点属性。

      CreateMonitoredItems服务

      用于创建一个或多个MonitoredItem并将其添加到订阅。删除订阅时,服务器会自动删除MonitoredItem。删除MonitoredItem会导致删除整个触发项链接集,但不会对触发项引用的MonitoredItems产生影响。

      void Service_CreateMonitoredItems(UA_Server *server, UA_Session *session,
                                        const UA_CreateMonitoredItemsRequest *request,
                                        UA_CreateMonitoredItemsResponse *response);

      DeleteMonitoredItems服务

      用于删除订阅的一个或多个MonitoredItems。删除MonitoredItem时,也会删除其触发的项目链接。

      void Service_DeleteMonitoredItems(UA_Server *server, UA_Session *session,
                                        const UA_DeleteMonitoredItemsRequest *request,
                                        UA_DeleteMonitoredItemsResponse *response);

      ModifyMonitoredItems服务

      用于修改订阅的MonitoredItems。服务器应立即应用对MonitoredItem设置的更改。它们尽快生效,但不迟于新修订的SampleSterlingInterval的两倍。

      可以修改的参数的非法请求值不会产生错误。相反,服务器将选择默认值并在相应的修订参数中指明它们。

      void Service_ModifyMonitoredItems(UA_Server *server, UA_Session *session,
                                        const UA_ModifyMonitoredItemsRequest *request,
                                        UA_ModifyMonitoredItemsResponse *response);

      SetMonitoringMode服务

      用于为订阅的一个或多个MonitoredItem设置监视模式。

      void Service_SetMonitoringMode(UA_Server *server, UA_Session *session,
                                     const UA_SetMonitoringModeRequest *request,
                                     UA_SetMonitoringModeResponse *response);

      SetTriggering服务

      用于创建和删除触发项的触发链接。

      /* Not Implemented */
      

      订阅服务集

      订阅用于向客户报告通知。

      CreateSubscription服务

      用于创建订阅。订阅监视一组MonificationsItems for Notifications并将它们返回给客户端以响应发布请求。

      void Service_CreateSubscription(UA_Server *server, UA_Session *session,
                                      const UA_CreateSubscriptionRequest *request,
                                      UA_CreateSubscriptionResponse *response);

      ModifySubscription服务

      用于修改订阅。

      void Service_ModifySubscription(UA_Server *server, UA_Session *session,
                                      const UA_ModifySubscriptionRequest *request,
                                      UA_ModifySubscriptionResponse *response);

      SetPublishingMode服务

      用于在一个或多个订阅上启用通知发送。

      void Service_SetPublishingMode(UA_Server *server, UA_Session *session,
                                     const UA_SetPublishingModeRequest *request,
                                     UA_SetPublishingModeResponse *response);

      发布服务

      用于两个目的。首先,它用于确认收到一个或多个订阅的NotificationMessages。其次,它用于请求服务器返回NotificationMessage或保持活动消息。

      请注意,服务签名是一个例外,不包含指向PublishResponse的指针。这是因为服务在内部对发布请求进行排队,并根据超时异步发送响应。

      void Service_Publish(UA_Server *server, UA_Session *session,
                           const UA_PublishRequest *request, UA_UInt32 requestId);

      重新发布服务

      请求订阅从其重新传输队列重新发布NotificationMessage。

      void Service_Republish(UA_Server *server, UA_Session *session,
                             const UA_RepublishRequest *request,
                             UA_RepublishResponse *response);

      DeleteSubscriptions服务

      调用以删除属于客户端会话的一个或多个订阅。

      void Service_DeleteSubscriptions(UA_Server *server, UA_Session *session,
                                       const UA_DeleteSubscriptionsRequest *request,
                                       UA_DeleteSubscriptionsResponse *response);

      TransferSubscription服务

      用于将Subscription及其MonitoredItems从一个Session转移到另一个Session。例如,客户可能需要重新打开会话,然后将其订阅转移到该会话。一个客户也可以使用它通过将订阅转移到其会话来从另一个客户接管订阅。

      /* Not Implemented */
    • 信息建模

      OPC UA中的信息建模结合了面向对象和语义建模的概念。OPC UA信息模型的核心是由图组成的图形

    • 节点:有八种可能的节点类型(变量,对象,方法......)
    • 参考:两个节点之间的类型和方向关系
    • 每个节点都由唯一的(在服务器内)NodeId标识。参考是表格的三元组。节点之间的示例引用是 变量及其VariableType之间的引用。某些ReferenceType是分层的,不能形成有向循环。有关可能的引用及其语义的更多详细信息,请参阅ReferenceTypes一节。(source-nodeid, referencetype-nodeid, target-nodeid)hasTypeDefinition

      警告!!本节中定义的结构仅与自定义Nodestores的开发人员相关。只能通过OPC UA 服务与信息模型进行交互。因此,以下部分纯粹是信息性的,以便用户可以拥有明确的基础表示的心理模型。

      基本节点属性

      节点根据节点类型包含属性。基节点属性对所有节点类型都是通用的。在OPC UA 服务中,属性通过包含节点的NodeId和整数属性ID来引用。

      在内部,open62541用于UA_Node确切的节点类型未知或不重要的地方。该nodeClass属性用于确保从UA_Node特定节点类型转换的正确性。

      /* List of reference targets with the same reference type and direction */
      typedef struct {
          UA_NodeId referenceTypeId;
          UA_Boolean isInverse;
          size_t targetIdsSize;
          UA_ExpandedNodeId *targetIds;
      } UA_NodeReferenceKind;
      
      #define UA_NODE_BASEATTRIBUTES                  \
          UA_NodeId nodeId;                           \
          UA_NodeClass nodeClass;                     \
          UA_QualifiedName browseName;                \
          UA_LocalizedText displayName;               \
          UA_LocalizedText description;               \
          UA_UInt32 writeMask;                        \
          size_t referencesSize;                      \
          UA_NodeReferenceKind *references;           \
                                                      \
          /* Members specific to open62541 */         \
          void *context;
      
      typedef struct {
          UA_NODE_BASEATTRIBUTES
      } UA_Node;

      VariableNode 

      变量将DataValue中的值与内省的元数据一起存储。最值得注意的是,属性数据类型,值排名和数组维度限制了变量可以采用的可能值。

      变量有两种形式:属性和数据变量。属性与具有hasProperty引用的父级相关,并且可能本身不具有子节点。Datavariables可能包含properties(hasProperty)和datavariables(hasComponents)。

      所有变量都是一些VariableTypeNode的实例,作为回报约束可能的数据类型,值排名和数组维度属性。

      数据类型

      变量的(标量)数据类型被约束为特定类型或类型层次结构中的子类之一。数据类型以NodeId的形式给出,该NodeId指向类型层次结构中的DataTypeNode。有关更多详细信息,请参见部分DataTypeNode

      如果数据类型属性指向UInt32,则value属性必须是该类型,因为UInt32类型层次结构中没有子项。如果数据类型属性指向Number,则值属性的类型可能仍然是UInt32Float或者 也是Byte

      确保变量中的数据类型属性与其VariableTypeNode之间的一致性 。

      价值等级

      此属性指示变量的value属性是否为数组以及数组具有的维数。它可能具有以下值:

    • n >= 1:值是具有指定维数的数组
    • n =  0:值是具有一个或多个维度的数组
    • n = -1:值是标量
    • n = -2:值可以是标量或具有任意数量维度的数组
    • n = -3:值可以是标量或一维数组
    • 确保变量中的值秩属性与其VariableTypeNode之间的一致性 。

      数组维度

      如果值等级允许该值为(多维)数组,则可以使用此属性进一步约束每个维度中的确切长度。

    • 对于正长度,保证变量值在此维度中具有相同的长度。
    • 维度长度零是通配符,实际值可以在此维度中具有任何长度。
    • 确保变量中的数组维度属性与其VariableTypeNode之间的一致性 。

      /* Indicates whether a variable contains data inline or whether it points to an
       * external data source */
      typedef enum {
          UA_VALUESOURCE_DATA,
          UA_VALUESOURCE_DATASOURCE
      } UA_ValueSource;
      
      #define UA_NODE_VARIABLEATTRIBUTES                                      \
          /* Constraints on possible values */                                \
          UA_NodeId dataType;                                                 \
          UA_Int32 valueRank;                                                 \
          size_t arrayDimensionsSize;                                         \
          UA_UInt32 *arrayDimensions;                                         \
                                                                              \
          /* The current value */                                             \
          UA_ValueSource valueSource;                                         \
          union {                                                             \
              struct {                                                        \
                  UA_DataValue value;                                         \
                  UA_ValueCallback callback;                                  \
              } data;                                                         \
              UA_DataSource dataSource;                                       \
          } value;
      
      typedef struct {
          UA_NODE_BASEATTRIBUTES
          UA_NODE_VARIABLEATTRIBUTES
          UA_Byte accessLevel;
          UA_Double minimumSamplingInterval;
          UA_Boolean historizing; /* currently unsupported */
      } UA_VariableNode;

      VariableTypeNode 

      VariableTypes用于为变量提供类型定义。VariableTypes约束变量实例的数据类型,值排名和数组维度属性。此外,从特定变量类型实例化可以提供语义信息。例如,实例fromMotorTemperatureVariableType比实例化的float变量更有意义BaseDataVariable

      typedef struct {
          UA_NODE_BASEATTRIBUTES
          UA_NODE_VARIABLEATTRIBUTES
          UA_Boolean isAbstract;
      
          /* Members specific to open62541 */
          UA_NodeTypeLifecycle lifecycle;
      } UA_VariableTypeNode;

      MethodNode 

      方法定义可调用函数,并使用Call服务调用。MethodNodes可能具有QualifiedName和的特殊属性(带hasProperty引用的变量childen )。输入和输出参数都是通过数组来描述的。虽然Call服务使用Variant的通用数组作为输入和输出,但检查实际参数值以匹配MethodNode的签名。(0, "InputArguments")(0, "OutputArguments")UA_Argument

      请注意,可以从多个对象(和对象类型)引用相同的MethodNode。为此,方法的NodeId 和提供上下文的对象是Call请求消息的一部分。

      typedef struct {
          UA_NODE_BASEATTRIBUTES
          UA_Boolean executable;
      
          /* Members specific to open62541 */
          UA_MethodCallback method;
      } UA_MethodNode;

      ObjectNode 

      对象用于表示系统,系统组件,现实世界对象和软件对象。对象是对象类型的实例,可能包含变量,方法和其他对象。

      typedef struct {
          UA_NODE_BASEATTRIBUTES
          UA_Byte eventNotifier;
      } UA_ObjectNode;

      ObjectTypeNode 

      ObjectTypes提供对象的定义。抽象对象无法实例化。有关构造函数和析构函数回调的使用,请参阅节点生命周期:构造函数,析构函数和节点上下文

      typedef struct {
          UA_NODE_BASEATTRIBUTES
          UA_Boolean isAbstract;
      
          /* Members specific to open62541 */
          UA_NodeTypeLifecycle lifecycle;
      } UA_ObjectTypeNode;

      ReferenceTypeNode 

      两个节点之间的每个引用都使用ReferenceType键入,该类型赋予关系含义。OPC UA标准将一组ReferenceType定义为OPC UA信息模型的必需部分。

    • Abstract AbstractTypes不能在实际引用中使用,仅用于构造ReferenceTypes层次结构
    • 从源节点和目标节点的角度来看,对称引用具有相同的含义
    • 下图显示了标准ReferenceTypes的层次结构(箭头表示hasSubType关系)。有关每个ReferenceType的完整语义,请参阅OPC UA规范的第3部分。

      有向图树{node [height = 0,shape = box,fillcolor =“#E5E5E5”,concent = true] references [label =“References \ n(Abstract,Symmetric)”] hierarchical_references [label =“HierarchicalReferences \ n(Abstract) “] references  - > hierarchical_references nonhierarchical_references [label =”NonHierarchicalReferences \ n(Abstract,Symmetric)“] references  - > nonhierarchical_references haschild [label =”HasChild \ n(Abstract)“] hierarchical_references  - > haschild aggregate [label =”Aggregates \ n (摘要)“] haschild  - >聚合组织[label =”Organizes“] hierarchical_references  - > organizes hascomponent [label =”HasComponent“]聚合 - > hascomponent hasorderedcomponent [label =”HasOrderedComponent“] hascomponent  - >hasorderedcomponent hasproperty [label =“HasProperty”]聚合 - > hasproperty hassubtype [label =“HasSubtype”] haschild  - > hassubtype hasmodellingrule [label =“HasModellingRule”] nonhierarchical_references  - > hasmodellingrule hastypedefinition [label =“HasTypeDefinition”] nonhierarchical_references  - > hastypedefinition hasencoding [label =“HasEncoding”] nonhierarchical_references  - > hasencoding hasdescription [label =“HasDescription”] nonhierarchical_references  - > hasdescription haseventsource [label =“HasEventSource”] hierarchical_references  - > haseventsource hasnotifier [label =“HasNotifier”] hierarchical_references  - > hasnotifier generatesevent [label =“GeneratesEvent”] nonhierarchical_references  - >generatesevent alwaysgeneratesevent [label =“AlwaysGeneratesEvent”] generatesevent  - > alwaysgeneratesevent {rank = same hierarchical_references nonhierarchical_references} {rank = same generatesevent haseventsource hasmodellingrule hasencoding hassubtype} {rank = same alwaysgeneratesevent hasproperty}}

      可以使用用户定义的ReferenceTypes扩展ReferenceType层次结构。OPC UA的许多配套规范定义了要在其感兴趣的域中使用的新ReferenceType。

      对于以下自定义ReferenceTypes示例,我们尝试对技术系统的结构进行建模。为此,我们引入了两个自定义ReferenceTypes。首先,分层containsReferenceType指示系统(由OPC UA对象表示)包含组件(或子系统)。这产生了遏制关系的树形结构。例如,电动机(物体)包含在汽车中,曲轴包含在电动机中。其次,对称connectedToReferenceType表示连接了两个组件。例如,电动机的曲轴连接到齿轮箱。连接独立于包含层次结构,可以引发一般的图形结构。进一步的亚型connectedTo可用于区分物理,电气和信息相关的连接。然后,客户端可以基于对两种自定义引用类型的共同理解来学习OPC UA信息模型中表示的(物理)系统的布局。

      typedef struct {
          UA_NODE_BASEATTRIBUTES
          UA_Boolean isAbstract;
          UA_Boolean symmetric;
          UA_LocalizedText inverseName;
      } UA_ReferenceTypeNode;

      DataTypeNode 

      DataTypes表示简单和结构化数据类型。DataTypes可能包含数组。但它们总是描述单个实例的结构。在open62541中,信息模型层次结构中的DataTypeNodes 通过其NodeId UA_DataType通用类型处理的类型描述相匹配 。

      抽象DataTypes(例如Number)不能是实际值的类型。它们用于将值约束到可能的子DataTypes(例如 UInt32)。

      typedef struct {
          UA_NODE_BASEATTRIBUTES
          UA_Boolean isAbstract;
      } UA_DataTypeNode;

      ViewNode 

      每个View定义AddressSpace中节点的子集。浏览信息模型时可以使用视图,只关注节点和引用的子集。可以创建ViewNodes并与之交互。但是它们在浏览服务中的使用目前在open62541中不受支持。

      typedef struct {
          UA_NODE_BASEATTRIBUTES
          UA_Byte eventNotifier;
          UA_Boolean containsNoLoops;
      } UA_ViewNode;

      Nodestore插件API 

      以下定义用于实现自定义节点存储后端。大多数用户都希望使用默认的nodestore,而不需要使用nodestore API

      在自定义节点存储实现之外,用户不应手动编辑节点。请使用OPC UA服务。否则,省略所有一致性检查。这最终可能会使应用程序崩溃。

      typedef void (*UA_NodestoreVisitor)(void *visitorContext, const UA_Node *node);
      
      typedef struct {
          /* Nodestore context and lifecycle */
          void *context;
          void (*deleteNodestore)(void *nodestoreContext);
      
          /* For non-multithreaded access, some nodestores allow that nodes are edited
           * without a copy/replace. This is not possible when the node is only an
           * intermediate representation and stored e.g. in a database backend. */
          UA_Boolean inPlaceEditAllowed;
      
          /* The following definitions are used to create empty nodes of the different
           * node types. The memory is managed by the nodestore. Therefore, the node
           * has to be removed via a special deleteNode function. (If the new node is
           * not added to the nodestore.) */
          UA_Node * (*newNode)(void *nodestoreContext, UA_NodeClass nodeClass);
      
          void (*deleteNode)(void *nodestoreContext, UA_Node *node);
      
          /* ``Get`` returns a pointer to an immutable node. ``Release`` indicates
           * that the pointer is no longer accessed afterwards. */
      
          const UA_Node * (*getNode)(void *nodestoreContext, const UA_NodeId *nodeId);
      
          void (*releaseNode)(void *nodestoreContext, const UA_Node *node);
      
          /* Returns an editable copy of a node (needs to be deleted with the
           * deleteNode function or inserted / replaced into the nodestore). */
          UA_StatusCode (*getNodeCopy)(void *nodestoreContext, const UA_NodeId *nodeId,
                                       UA_Node **outNode);
      
          /* Inserts a new node into the nodestore. If the NodeId is zero, then a
           * fresh numeric NodeId is assigned. If insertion fails, the node is
           * deleted. */
          UA_StatusCode (*insertNode)(void *nodestoreContext, UA_Node *node,
                                      UA_NodeId *addedNodeId);
      
          /* To replace a node, get an editable copy of the node, edit and replace
           * with this function. If the node was already replaced since the copy was
           * made, UA_STATUSCODE_BADINTERNALERROR is returned. If the NodeId is not
           * found, UA_STATUSCODE_BADNODEIDUNKNOWN is returned. In both error cases,
           * the editable node is deleted. */
          UA_StatusCode (*replaceNode)(void *nodestoreContext, UA_Node *node);
      
          /* Removes a node from the nodestore. */
          UA_StatusCode (*removeNode)(void *nodestoreContext, const UA_NodeId *nodeId);
      
          /* Execute a callback for every node in the nodestore. */
          void (*iterate)(void *nodestoreContext, void* visitorContext,
                          UA_NodestoreVisitor visitor);
      } UA_Nodestore;

      以下方法在内部专门针对不同的节点类(由nodeClass成员区分)

      /* Attributes must be of a matching type (VariableAttributes, ObjectAttributes,
       * and so on). The attributes are copied. Note that the attributes structs do
       * not contain NodeId, NodeClass and BrowseName. The NodeClass of the node needs
       * to be correctly set before calling this method. UA_Node_deleteMembers is
       * called on the node when an error occurs internally. */
      UA_StatusCode
      UA_Node_setAttributes(UA_Node *node, const void *attributes,
                            const UA_DataType *attributeType);
      
      /* Reset the destination node and copy the content of the source */
      UA_StatusCode
      UA_Node_copy(const UA_Node *src, UA_Node *dst);
      
      /* Allocate new node and copy the values from src */
      UA_Node *
      UA_Node_copy_alloc(const UA_Node *src);
      
      /* Add a single reference to the node */
      UA_StatusCode
      UA_Node_addReference(UA_Node *node, const UA_AddReferencesItem *item);
      
      /* Delete a single reference from the node */
      UA_StatusCode
      UA_Node_deleteReference(UA_Node *node, const UA_DeleteReferencesItem *item);
      
      /* Delete all references of the node */
      void
      UA_Node_deleteReferences(UA_Node *node);
      
      /* Remove all malloc'ed members of the node */
      void
      UA_Node_deleteMembers(UA_Node *node);

     

    服务器

    服务器配置

    初始化期间将配置结构传递给服务器。服务器期望在运行时期间不修改配置。目前,一次只能有一台服务器使用配置。在关闭期间,服务器将清理通过提供的API在运行时修改的配置部分。

    /plugins文件夹中提供了配置示例。通常的用法如下:

    1. 使用默认设置作为起点创建服务器配置
    2. 修改配置,例如通过添加服务器证书
    3. 使用它实例化服务器
    4. 关闭服务器后,清理配置(可用内存)

    教程为此提供一个良好的起点。

    typedef struct {
        UA_UInt32 min;
        UA_UInt32 max;
    } UA_UInt32Range;
    
    typedef struct {
        UA_Duration min;
        UA_Duration max;
    } UA_DurationRange;
    
    struct UA_ServerConfig {
        UA_UInt16 nThreads; /* only if multithreading is enabled */
        UA_Logger logger;
    
        /* Server Description */
        UA_BuildInfo buildInfo;
        UA_ApplicationDescription applicationDescription;
        UA_ByteString serverCertificate;
    
        /* MDNS Discovery */
    #ifdef UA_ENABLE_DISCOVERY
        UA_String mdnsServerName;
        size_t serverCapabilitiesSize;
        UA_String *serverCapabilities;
    #endif
    
        /* Custom DataTypes */
        size_t customDataTypesSize;
        UA_DataType *customDataTypes;

    注意请参阅有关通用类型处理的部分。使用自定义数据类型的示例如下 /examples/custom_datatype/

    /* Nodestore */
    UA_Nodestore nodestore;
    
    /* Networking */
    size_t networkLayersSize;
    UA_ServerNetworkLayer *networkLayers;
    UA_String customHostname;
    
    /* Available endpoints */
    size_t endpointsSize;
    UA_Endpoint *endpoints;
    
    /* Node Lifecycle callbacks */
    UA_GlobalNodeLifecycle nodeLifecycle;

    请参阅节点生命周期处理部分

    /* Access Control */
    UA_AccessControl accessControl;

    注意请参阅访问控制处理部分

    /* Certificate Verification */
        UA_CertificateVerification certificateVerification;
    
        /* Limits for SecureChannels */
        UA_UInt16 maxSecureChannels;
        UA_UInt32 maxSecurityTokenLifetime; /* in ms */
    
        /* Limits for Sessions */
        UA_UInt16 maxSessions;
        UA_Double maxSessionTimeout; /* in ms */
    
        /* Operation limits */
        UA_UInt32 maxNodesPerRead;
        UA_UInt32 maxNodesPerWrite;
        UA_UInt32 maxNodesPerMethodCall;
        UA_UInt32 maxNodesPerBrowse;
        UA_UInt32 maxNodesPerRegisterNodes;
        UA_UInt32 maxNodesPerTranslateBrowsePathsToNodeIds;
        UA_UInt32 maxNodesPerNodeManagement;
        UA_UInt32 maxMonitoredItemsPerCall;
    
        /* Limits for Requests */
        UA_UInt32 maxReferencesPerNode;
    
        /* Limits for Subscriptions */
        UA_UInt32 maxSubscriptionsPerSession;
        UA_DurationRange publishingIntervalLimits; /* in ms (must not be less than 5) */
        UA_UInt32Range lifeTimeCountLimits;
        UA_UInt32Range keepAliveCountLimits;
        UA_UInt32 maxNotificationsPerPublish;
        UA_UInt32 maxRetransmissionQueueSize; /* 0 -> unlimited size */
    
        /* Limits for MonitoredItems */
        UA_UInt32 maxMonitoredItemsPerSubscription;
        UA_DurationRange samplingIntervalLimits; /* in ms (must not be less than 5) */
        UA_UInt32Range queueSizeLimits; /* Negotiated with the client */
    
        /* Limits for PublishRequests */
        UA_UInt32 maxPublishReqPerSession;
    
        /* Discovery */
    #ifdef UA_ENABLE_DISCOVERY
        /* Timeout in seconds when to automatically remove a registered server from
         * the list, if it doesn't re-register within the given time frame. A value
         * of 0 disables automatic removal. Default is 60 Minutes (60*60). Must be
         * bigger than 10 seconds, because cleanup is only triggered approximately
         * ervery 10 seconds. The server will still be removed depending on the
         * state of the semaphore file. */
        UA_UInt32 discoveryCleanupTimeout;
    #endif
    };
        服务器生命周期
    UA_Server * UA_Server_new(const UA_ServerConfig *config);
    void UA_Server_delete(UA_Server *server);
    
    /* Runs the main loop of the server. In each iteration, this calls into the
     * networklayers to see if messages have arrived.
     *
     * @param server The server object.
     * @param running The loop is run as long as *running is true.
     *        Otherwise, the server shuts down.
     * @return Returns the statuscode of the UA_Server_run_shutdown method */
    UA_StatusCode
    UA_Server_run(UA_Server *server, volatile UA_Boolean *running);
    
    /* The prologue part of UA_Server_run (no need to use if you call
     * UA_Server_run) */
    UA_StatusCode
    UA_Server_run_startup(UA_Server *server);
    
    /* Executes a single iteration of the server's main loop.
     *
     * @param server The server object.
     * @param waitInternal Should we wait for messages in the networklayer?
     *        Otherwise, the timouts for the networklayers are set to zero.
     *        The default max wait time is 50millisec.
     * @return Returns how long we can wait until the next scheduled
     *         callback (in ms) */
    UA_UInt16
    UA_Server_run_iterate(UA_Server *server, UA_Boolean waitInternal);
    
    /* The epilogue part of UA_Server_run (no need to use if you call
     * UA_Server_run) */
    UA_StatusCode
    UA_Server_run_shutdown(UA_Server *server);

    重复回调

    typedef void (*UA_ServerCallback)(UA_Server *server, void *data);
    
    /* Add a callback for cyclic repetition to the server.
     *
     * @param server The server object.
     * @param callback The callback that shall be added.
     * @param interval The callback shall be repeatedly executed with the given interval
     *        (in ms). The interval must be larger than 5ms. The first execution
     *        occurs at now() + interval at the latest.
     * @param callbackId Set to the identifier of the repeated callback . This can be used to cancel
     *        the callback later on. If the pointer is null, the identifier is not set.
     * @return Upon success, UA_STATUSCODE_GOOD is returned.
     *         An error code otherwise. */
    UA_StatusCode
    UA_Server_addRepeatedCallback(UA_Server *server, UA_ServerCallback callback,
                                  void *data, UA_UInt32 interval, UA_UInt64 *callbackId);
    
    UA_StatusCode
    UA_Server_changeRepeatedCallbackInterval(UA_Server *server, UA_UInt64 callbackId,
                                             UA_UInt32 interval);
    
    /* Remove a repeated callback.
     *
     * @param server The server object.
     * @param callbackId The id of the callback that shall be removed.
     * @return Upon success, UA_STATUSCODE_GOOD is returned.
     *         An error code otherwise. */
    UA_StatusCode
    UA_Server_removeRepeatedCallback(UA_Server *server, UA_UInt64 callbackId);

    读写节点属性

    读取和写入节点属性的函数调用后台的常规读写服务,这些服务也通过网络使用。

    无法读取以下属性,因为本地“admin”用户始终拥有完整权限。

    • UserWriteMask
    • UserAccessLevel
    • UserExecutable
    /* Read an attribute of a node. The specialized functions below provide a more
     * concise syntax.
     *
     * @param server The server object.
     * @param item ReadValueIds contain the NodeId of the target node, the id of the
     *             attribute to read and (optionally) an index range to read parts
     *             of an array only. See the section on NumericRange for the format
     *             used for array ranges.
     * @param timestamps Which timestamps to return for the attribute.
     * @return Returns a DataValue that contains either an error code, or a variant
     *         with the attribute value and the timestamps. */
    UA_DataValue
    UA_Server_read(UA_Server *server, const UA_ReadValueId *item,
                   UA_TimestampsToReturn timestamps);
    
    /* Don't use this function. There are typed versions for every supported
     * attribute. */
    UA_StatusCode
    __UA_Server_read(UA_Server *server, const UA_NodeId *nodeId,
                     UA_AttributeId attributeId, void *v);
    
    static UA_INLINE UA_StatusCode
    UA_Server_readNodeId(UA_Server *server, const UA_NodeId nodeId,
                         UA_NodeId *outNodeId) {
        return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_NODEID, outNodeId);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_readNodeClass(UA_Server *server, const UA_NodeId nodeId,
                            UA_NodeClass *outNodeClass) {
        return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_NODECLASS,
                                outNodeClass);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_readBrowseName(UA_Server *server, const UA_NodeId nodeId,
                             UA_QualifiedName *outBrowseName) {
        return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_BROWSENAME,
                                outBrowseName);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_readDisplayName(UA_Server *server, const UA_NodeId nodeId,
                              UA_LocalizedText *outDisplayName) {
        return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_DISPLAYNAME,
                                outDisplayName);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_readDescription(UA_Server *server, const UA_NodeId nodeId,
                              UA_LocalizedText *outDescription) {
        return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_DESCRIPTION,
                                outDescription);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_readWriteMask(UA_Server *server, const UA_NodeId nodeId,
                            UA_UInt32 *outWriteMask) {
        return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_WRITEMASK,
                                outWriteMask);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_readIsAbstract(UA_Server *server, const UA_NodeId nodeId,
                             UA_Boolean *outIsAbstract) {
        return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_ISABSTRACT,
                                outIsAbstract);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_readSymmetric(UA_Server *server, const UA_NodeId nodeId,
                            UA_Boolean *outSymmetric) {
        return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_SYMMETRIC,
                                outSymmetric);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_readInverseName(UA_Server *server, const UA_NodeId nodeId,
                              UA_LocalizedText *outInverseName) {
        return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_INVERSENAME,
                                outInverseName);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_readContainsNoLoop(UA_Server *server, const UA_NodeId nodeId,
                                 UA_Boolean *outContainsNoLoops) {
        return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_CONTAINSNOLOOPS,
                                outContainsNoLoops);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_readEventNotifier(UA_Server *server, const UA_NodeId nodeId,
                                UA_Byte *outEventNotifier) {
        return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_EVENTNOTIFIER,
                                outEventNotifier);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_readValue(UA_Server *server, const UA_NodeId nodeId,
                        UA_Variant *outValue) {
        return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_VALUE, outValue);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_readDataType(UA_Server *server, const UA_NodeId nodeId,
                           UA_NodeId *outDataType) {
        return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_DATATYPE,
                                outDataType);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_readValueRank(UA_Server *server, const UA_NodeId nodeId,
                            UA_Int32 *outValueRank) {
        return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_VALUERANK,
                                outValueRank);
    }
    
    /* Returns a variant with an int32 array */
    static UA_INLINE UA_StatusCode
    UA_Server_readArrayDimensions(UA_Server *server, const UA_NodeId nodeId,
                                  UA_Variant *outArrayDimensions) {
        return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_ARRAYDIMENSIONS,
                                outArrayDimensions);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_readAccessLevel(UA_Server *server, const UA_NodeId nodeId,
                              UA_Byte *outAccessLevel) {
        return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_ACCESSLEVEL,
                                outAccessLevel);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_readMinimumSamplingInterval(UA_Server *server, const UA_NodeId nodeId,
                                          UA_Double *outMinimumSamplingInterval) {
        return __UA_Server_read(server, &nodeId,
                                UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL,
                                outMinimumSamplingInterval);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_readHistorizing(UA_Server *server, const UA_NodeId nodeId,
                              UA_Boolean *outHistorizing) {
        return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_HISTORIZING,
                                outHistorizing);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_readExecutable(UA_Server *server, const UA_NodeId nodeId,
                             UA_Boolean *outExecutable) {
        return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_EXECUTABLE,
                                outExecutable);
    }

    创建节点后,无法更改以下节点属性:

    • 结点类别
    • 的NodeId
    • 对称
    • ContainsNoLoop

    无法从服务器写入以下属性,因为它们特定于不同的用户并由访问控制回调设置:

    • UserWriteMask
    • UserAccessLevel
    • UserExecutable

    历史记录目前尚不受支持

    /* Overwrite an attribute of a node. The specialized functions below provide a
     * more concise syntax.
     *
     * @param server The server object.
     * @param value WriteValues contain the NodeId of the target node, the id of the
     *              attribute to overwritten, the actual value and (optionally) an
     *              index range to replace parts of an array only. of an array only.
     *              See the section on NumericRange for the format used for array
     *              ranges.
     * @return Returns a status code. */
    UA_StatusCode
    UA_Server_write(UA_Server *server, const UA_WriteValue *value);
    
    /* Don't use this function. There are typed versions with no additional
     * overhead. */
    UA_StatusCode
    __UA_Server_write(UA_Server *server, const UA_NodeId *nodeId,
                      const UA_AttributeId attributeId,
                      const UA_DataType *attr_type, const void *attr);
    
    static UA_INLINE UA_StatusCode
    UA_Server_writeBrowseName(UA_Server *server, const UA_NodeId nodeId,
                              const UA_QualifiedName browseName) {
        return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_BROWSENAME,
                                 &UA_TYPES[UA_TYPES_QUALIFIEDNAME], &browseName);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_writeDisplayName(UA_Server *server, const UA_NodeId nodeId,
                               const UA_LocalizedText displayName) {
        return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_DISPLAYNAME,
                                 &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], &displayName);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_writeDescription(UA_Server *server, const UA_NodeId nodeId,
                               const UA_LocalizedText description) {
        return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_DESCRIPTION,
                                 &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], &description);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_writeWriteMask(UA_Server *server, const UA_NodeId nodeId,
                             const UA_UInt32 writeMask) {
        return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_WRITEMASK,
                                 &UA_TYPES[UA_TYPES_UINT32], &writeMask);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_writeIsAbstract(UA_Server *server, const UA_NodeId nodeId,
                              const UA_Boolean isAbstract) {
        return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_ISABSTRACT,
                                 &UA_TYPES[UA_TYPES_BOOLEAN], &isAbstract);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_writeInverseName(UA_Server *server, const UA_NodeId nodeId,
                               const UA_LocalizedText inverseName) {
        return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_INVERSENAME,
                                 &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], &inverseName);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_writeEventNotifier(UA_Server *server, const UA_NodeId nodeId,
                                 const UA_Byte eventNotifier) {
        return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_EVENTNOTIFIER,
                                 &UA_TYPES[UA_TYPES_BYTE], &eventNotifier);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_writeValue(UA_Server *server, const UA_NodeId nodeId,
                         const UA_Variant value) {
        return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_VALUE,
                                 &UA_TYPES[UA_TYPES_VARIANT], &value);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_writeDataType(UA_Server *server, const UA_NodeId nodeId,
                            const UA_NodeId dataType) {
        return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_DATATYPE,
                                 &UA_TYPES[UA_TYPES_NODEID], &dataType);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_writeValueRank(UA_Server *server, const UA_NodeId nodeId,
                             const UA_Int32 valueRank) {
        return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_VALUERANK,
                                 &UA_TYPES[UA_TYPES_INT32], &valueRank);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_writeArrayDimensions(UA_Server *server, const UA_NodeId nodeId,
                                   const UA_Variant arrayDimensions) {
        return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_VALUE,
                                 &UA_TYPES[UA_TYPES_VARIANT], &arrayDimensions);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_writeAccessLevel(UA_Server *server, const UA_NodeId nodeId,
                               const UA_Byte accessLevel) {
        return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_ACCESSLEVEL,
                                 &UA_TYPES[UA_TYPES_BYTE], &accessLevel);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_writeMinimumSamplingInterval(UA_Server *server, const UA_NodeId nodeId,
                                           const UA_Double miniumSamplingInterval) {
        return __UA_Server_write(server, &nodeId,
                                 UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL,
                                 &UA_TYPES[UA_TYPES_DOUBLE],
                                 &miniumSamplingInterval);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_writeExecutable(UA_Server *server, const UA_NodeId nodeId,
                              const UA_Boolean executable) {
        return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_EXECUTABLE,
                                 &UA_TYPES[UA_TYPES_BOOLEAN], &executable); }

    浏览

    UA_BrowseResult
    UA_Server_browse(UA_Server *server, UA_UInt32 maxrefs,
                     const UA_BrowseDescription *descr);
    
    UA_BrowseResult
    UA_Server_browseNext(UA_Server *server, UA_Boolean releaseContinuationPoint,
                         const UA_ByteString *continuationPoint);
    
    UA_BrowsePathResult
    UA_Server_translateBrowsePathToNodeIds(UA_Server *server,
                                           const UA_BrowsePath *browsePath);
    
    #ifndef HAVE_NODEITER_CALLBACK
    #define HAVE_NODEITER_CALLBACK
    /* Iterate over all nodes referenced by parentNodeId by calling the callback
     * function for each child node (in ifdef because GCC/CLANG handle include order
     * differently) */
    typedef UA_StatusCode
    (*UA_NodeIteratorCallback)(UA_NodeId childId, UA_Boolean isInverse,
                               UA_NodeId referenceTypeId, void *handle);
    #endif
    
    UA_StatusCode
    UA_Server_forEachChildNodeCall(UA_Server *server, UA_NodeId parentNodeId,
                                   UA_NodeIteratorCallback callback, void *handle);
    
    #ifdef UA_ENABLE_DISCOVERY

    发现

    /* Register the given server instance at the discovery server.
     * This should be called periodically.
     * The semaphoreFilePath is optional. If the given file is deleted,
     * the server will automatically be unregistered. This could be
     * for example a pid file which is deleted if the server crashes.
     *
     * When the server shuts down you need to call unregister.
     *
     * @param server
     * @param discoveryServerUrl if set to NULL, the default value
     *        'opc.tcp://localhost:4840' will be used
     * @param semaphoreFilePath optional parameter pointing to semaphore file. */
    UA_StatusCode
    UA_Server_register_discovery(UA_Server *server, const char* discoveryServerUrl,
                                 const char* semaphoreFilePath);
    
    /* Unregister the given server instance from the discovery server.
     * This should only be called when the server is shutting down.
     * @param server
     * @param discoveryServerUrl if set to NULL, the default value
     *        'opc.tcp://localhost:4840' will be used */
    UA_StatusCode
    UA_Server_unregister_discovery(UA_Server *server, const char* discoveryServerUrl);
    
     /* Adds a periodic callback to register the server with the LDS (local discovery server)
      * periodically. The interval between each register call is given as second parameter.
      * It should be 10 minutes by default (= 10*60*1000).
      *
      * The delayFirstRegisterMs parameter indicates the delay for the first register call.
      * If it is 0, the first register call will be after intervalMs milliseconds,
      * otherwise the server's first register will be after delayFirstRegisterMs.
      *
      * When you manually unregister the server, you also need to cancel the
      * periodic callback, otherwise it will be automatically be registered again.
      *
      * If you call this method multiple times for the same discoveryServerUrl, the older
      * periodic callback will be removed.
      *
      * @param server
      * @param discoveryServerUrl if set to NULL, the default value
      *        'opc.tcp://localhost:4840' will be used
      * @param intervalMs
      * @param delayFirstRegisterMs
      * @param periodicCallbackId */
    UA_StatusCode
    UA_Server_addPeriodicServerRegisterCallback(UA_Server *server, const char* discoveryServerUrl,
                                                UA_UInt32 intervalMs,
                                                UA_UInt32 delayFirstRegisterMs,
                                                UA_UInt64 *periodicCallbackId);
    
    /* Callback for RegisterServer. Data is passed from the register call */
    typedef void (*UA_Server_registerServerCallback)(const UA_RegisteredServer *registeredServer,
                                                     void* data);
    
    /* Set the callback which is called if another server registeres or unregisters
     * with this instance. If called multiple times, previous data will be
     * overwritten.
     *
     * @param server
     * @param cb the callback
     * @param data data passed to the callback
     * @return UA_STATUSCODE_SUCCESS on success */
    void
    UA_Server_setRegisterServerCallback(UA_Server *server, UA_Server_registerServerCallback cb,
                                        void* data);
    
    #ifdef UA_ENABLE_DISCOVERY_MULTICAST
    
    /* Callback for server detected through mDNS. Data is passed from the register
     * call
     *
     * @param isServerAnnounce indicates if the server has just been detected. If
     *        set to false, this means the server is shutting down.
     * @param isTxtReceived indicates if we already received the corresponding TXT
     *        record with the path and caps data */
    typedef void (*UA_Server_serverOnNetworkCallback)(const UA_ServerOnNetwork *serverOnNetwork,
                                                      UA_Boolean isServerAnnounce,
                                                      UA_Boolean isTxtReceived, void* data);
    
    /* Set the callback which is called if another server is found through mDNS or
     * deleted. It will be called for any mDNS message from the remote server, thus
     * it may be called multiple times for the same instance. Also the SRV and TXT
     * records may arrive later, therefore for the first call the server
     * capabilities may not be set yet. If called multiple times, previous data will
     * be overwritten.
     *
     * @param server
     * @param cb the callback
     * @param data data passed to the callback
     * @return UA_STATUSCODE_SUCCESS on success */
    void
    UA_Server_setServerOnNetworkCallback(UA_Server *server,
                                         UA_Server_serverOnNetworkCallback cb,
                                         void* data);
    
    #endif /* UA_ENABLE_DISCOVERY_MULTICAST */
    
    #endif /* UA_ENABLE_DISCOVERY */

    信息模型回调

    有三个地方可以发生从信息模型到用户定义代码的回调。

    • 自定义节点构造函数和析构函数
    • 将VariableNodes与外部数据源链接
    • MethodNode回调

    节点生命周期:构造函数,析构函数和节点上下文

    要完成节点的实例化,将执行(用户定义的)构造函数回调。可以存在所有节点的全局构造函数和特定于新节点的TypeDefinition的节点类型构造函数(附加到ObjectTypeNode或VariableTypeNode)。

    在ObjectTypes和VariableTypes的层次结构中,仅执行为新节点定义的(最低)类型的构造函数。请注意,每个对象和变量只能有一个isTypeOf引用。但是类型节点在技术上可以有多个hasSubType引用来实现多重继承。构造函数中的(多)继承问题需要由用户解决。

    销毁节点时,在全局析构函数之前调用节点类型析构函数。所以整个节点的生命周期如下:

    1. 全局构造函数(在服务器配置中设置)
    2. 节点类型构造函数(用于VariableType或ObjectTypes)
    3. (节点的使用期限)
    4. 节点类型析构函数
    5. 全局析构函数

    NULL在这种情况下,可以将构造函数和析构函数回调设置为并且不使用它们。如果node-type构造函数失败,则在删除节点之前将调用全局析构函数。假设析构函数永远不会失败。

    每个节点都带有用户上下文和构造函数上下文指针。用户上下文用于将自定义数据附加到节点。但是(用户定义的)构造函数和析构函数可以替换用户上下文指针,如果他们希望这样做的话。构造函数上下文的初始值是NULL。当AddNodes通过网络使用服务时,新节点的用户上下文指针也最初设置为NULL

    /* To be set in the server config. */
    typedef struct {
        /* Can be NULL. May replace the nodeContext */
        UA_StatusCode (*constructor)(UA_Server *server,
                                     const UA_NodeId *sessionId, void *sessionContext,
                                     const UA_NodeId *nodeId, void **nodeContext);
    
        /* Can be NULL. The context cannot be replaced since the node is destroyed
         * immediately afterwards anyway. */
        void (*destructor)(UA_Server *server,
                           const UA_NodeId *sessionId, void *sessionContext,
                           const UA_NodeId *nodeId, void *nodeContext);
    } UA_GlobalNodeLifecycle;
    
    typedef struct {
        /* Can be NULL. May replace the nodeContext */
        UA_StatusCode (*constructor)(UA_Server *server,
                                     const UA_NodeId *sessionId, void *sessionContext,
                                     const UA_NodeId *typeNodeId, void *typeNodeContext,
                                     const UA_NodeId *nodeId, void **nodeContext);
    
        /* Can be NULL. May replace the nodeContext. */
        void (*destructor)(UA_Server *server,
                           const UA_NodeId *sessionId, void *sessionContext,
                           const UA_NodeId *typeNodeId, void *typeNodeContext,
                           const UA_NodeId *nodeId, void **nodeContext);
    } UA_NodeTypeLifecycle;
    
    UA_StatusCode
    UA_Server_setNodeTypeLifecycle(UA_Server *server, UA_NodeId nodeId,
                                   UA_NodeTypeLifecycle lifecycle);
    
    UA_StatusCode
    UA_Server_getNodeContext(UA_Server *server, UA_NodeId nodeId,
                             void **nodeContext);
    
    /* Careful! The user has to ensure that the destructor callbacks still work. */
    UA_StatusCode
    UA_Server_setNodeContext(UA_Server *server, UA_NodeId nodeId,
                             void *nodeContext);

    数据源回调

    服务器有一种处理变量内容的独特方式。该节点可以指向具有本地数据提供者的函数,而不是存储附加到变量节点的变体。每当读取value属性时,将调用该函数并要求其提供包含值内容和其他时间戳的UA_DataValue返回值。

    期望实现读回调。写回调可以设置为空指针。

    typedef struct {
        /* Copies the data from the source into the provided value.
         *
         * !! ZERO-COPY OPERATIONS POSSIBLE !!
         * It is not required to return a copy of the actual content data. You can
         * return a pointer to memory owned by the user. Memory can be reused
         * between read callbacks of a DataSource, as the result is already encoded
         * on the network buffer between each read operation.
         *
         * To use zero-copy reads, set the value of the `value->value` Variant
         * without copying, e.g. with `UA_Variant_setScalar`. Then, also set
         * `value->value.storageType` to `UA_VARIANT_DATA_NODELETE` to prevent the
         * memory being cleaned up. Don't forget to also set `value->hasValue` to
         * true to indicate the presence of a value.
         *
         * @param handle An optional pointer to user-defined data for the
         *        specific data source
         * @param nodeid Id of the read node
         * @param includeSourceTimeStamp If true, then the datasource is expected to
         *        set the source timestamp in the returned value
         * @param range If not null, then the datasource shall return only a
         *        selection of the (nonscalar) data. Set
         *        UA_STATUSCODE_BADINDEXRANGEINVALID in the value if this does not
         *        apply.
         * @param value The (non-null) DataValue that is returned to the client. The
         *        data source sets the read data, the result status and optionally a
         *        sourcetimestamp.
         * @return Returns a status code for logging. Error codes intended for the
         *         original caller are set in the value. If an error is returned,
         *         then no releasing of the value is done. */
        UA_StatusCode (*read)(UA_Server *server, const UA_NodeId *sessionId,
                              void *sessionContext, const UA_NodeId *nodeId,
                              void *nodeContext, UA_Boolean includeSourceTimeStamp,
                              const UA_NumericRange *range, UA_DataValue *value);
    
        /* Write into a data source. This method pointer can be NULL if the
         * operation is unsupported.
         *
         * @param handle An optional pointer to user-defined data for the
         *        specific data source
         * @param nodeid Id of the node being written to
         * @param data The data to be written into the data source
         * @param range An optional data range. If the data source is scalar or does
         *        not support writing of ranges, then an error code is returned.
         * @return Returns a status code that is returned to the user */
        UA_StatusCode (*write)(UA_Server *server, const UA_NodeId *sessionId,
                               void *sessionContext, const UA_NodeId *nodeId,
                               void *nodeContext, const UA_NumericRange *range,
                               const UA_DataValue *value);
    } UA_DataSource;
    
    UA_StatusCode
    UA_Server_setVariableNode_dataSource(UA_Server *server, const UA_NodeId nodeId,
                                         const UA_DataSource dataSource);

    值回调

    值回调可以附加到变量和变量类型节点。如果没有NULL,则分别在阅读前和写入后调用它们。

    方法回调

    通过网络添加方法节点时,方法回调设置为NULL(不可执行)。理论上,UA_Server_setMethodNode_callback当真正需要通过网络添加方法时,可以通过全局构造函数添加回调 。

    typedef UA_StatusCode
    (*UA_MethodCallback)(UA_Server *server, const UA_NodeId *sessionId,
                         void *sessionContext, const UA_NodeId *methodId,
                         void *methodContext, const UA_NodeId *objectId,
                         void *objectContext, size_t inputSize,
                         const UA_Variant *input, size_t outputSize,
                         UA_Variant *output);
    
    #ifdef UA_ENABLE_METHODCALLS
    
    UA_StatusCode
    UA_Server_setMethodNode_callback(UA_Server *server,
                                     const UA_NodeId methodNodeId,
                                     UA_MethodCallback methodCallback);
    UA_CallMethodResult
    UA_Server_call(UA_Server *server, const UA_CallMethodRequest *request);
    
    #endif

    节点添加和删除

    在运行时创建动态节点实例时,您可能不关心新节点的特定NodeId,只要您稍后可以引用它。当使用数字标识符0传递数字NodeId时,堆栈将其评估为“在该命名空间中选择随机未分配的数字NodeId”。要找出实际分配给新节点的NodeId,您可以传递一个指针outNewNodeId,它将(在成功插入节点之后)包含新节点的nodeId。NULL如果不需要此结果,您也可以传递 指针。

    请参阅节点生命周期:构造函数中的构造函数,析构函数和节点上下文以及将用户定义的数据附加到节点。

    节点添加和删除的方法主要采用未修改的const参数。创建节点时,会创建节点标识符,节点属性等的深层副本。因此,可以例如UA_Server_addVariablenode使用指向堆栈上的存储器位置的值属性(Variant)来调用。如果需要更改变量值以在特定内存位置显示,请使用数据源回调值回调

    /* Protect against redundant definitions for server/client */
    #ifndef UA_DEFAULT_ATTRIBUTES_DEFINED
    #define UA_DEFAULT_ATTRIBUTES_DEFINED
    /* The default for variables is "BaseDataType" for the datatype, -2 for the
     * valuerank and a read-accesslevel. */
    extern const UA_VariableAttributes UA_VariableAttributes_default;
    extern const UA_VariableTypeAttributes UA_VariableTypeAttributes_default;
    /* Methods are executable by default */
    extern const UA_MethodAttributes UA_MethodAttributes_default;
    /* The remaining attribute definitions are currently all zeroed out */
    extern const UA_ObjectAttributes UA_ObjectAttributes_default;
    extern const UA_ObjectTypeAttributes UA_ObjectTypeAttributes_default;
    extern const UA_ReferenceTypeAttributes UA_ReferenceTypeAttributes_default;
    extern const UA_DataTypeAttributes UA_DataTypeAttributes_default;
    extern const UA_ViewAttributes UA_ViewAttributes_default;
    #endif
    
    /* Don't use this function. There are typed versions as inline functions. */
    UA_StatusCode
    __UA_Server_addNode(UA_Server *server, const UA_NodeClass nodeClass,
                        const UA_NodeId *requestedNewNodeId,
                        const UA_NodeId *parentNodeId,
                        const UA_NodeId *referenceTypeId,
                        const UA_QualifiedName browseName,
                        const UA_NodeId *typeDefinition,
                        const UA_NodeAttributes *attr,
                        const UA_DataType *attributeType,
                        void *nodeContext, UA_NodeId *outNewNodeId);
    
    static UA_INLINE UA_StatusCode
    UA_Server_addVariableNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
                              const UA_NodeId parentNodeId,
                              const UA_NodeId referenceTypeId,
                              const UA_QualifiedName browseName,
                              const UA_NodeId typeDefinition,
                              const UA_VariableAttributes attr,
                              void *nodeContext, UA_NodeId *outNewNodeId) {
        return __UA_Server_addNode(server, UA_NODECLASS_VARIABLE, &requestedNewNodeId,
                                   &parentNodeId, &referenceTypeId, browseName,
                                   &typeDefinition, (const UA_NodeAttributes*)&attr,
                                   &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],
                                   nodeContext, outNewNodeId);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_addVariableTypeNode(UA_Server *server,
                                  const UA_NodeId requestedNewNodeId,
                                  const UA_NodeId parentNodeId,
                                  const UA_NodeId referenceTypeId,
                                  const UA_QualifiedName browseName,
                                  const UA_NodeId typeDefinition,
                                  const UA_VariableTypeAttributes attr,
                                  void *nodeContext, UA_NodeId *outNewNodeId) {
        return __UA_Server_addNode(server, UA_NODECLASS_VARIABLETYPE,
                                   &requestedNewNodeId, &parentNodeId, &referenceTypeId,
                                   browseName, &typeDefinition,
                                   (const UA_NodeAttributes*)&attr,
                                   &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES],
                                   nodeContext, outNewNodeId);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_addObjectNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
                            const UA_NodeId parentNodeId,
                            const UA_NodeId referenceTypeId,
                            const UA_QualifiedName browseName,
                            const UA_NodeId typeDefinition,
                            const UA_ObjectAttributes attr,
                            void *nodeContext, UA_NodeId *outNewNodeId) {
        return __UA_Server_addNode(server, UA_NODECLASS_OBJECT, &requestedNewNodeId,
                                   &parentNodeId, &referenceTypeId, browseName,
                                   &typeDefinition, (const UA_NodeAttributes*)&attr,
                                   &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],
                                   nodeContext, outNewNodeId);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_addObjectTypeNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
                                const UA_NodeId parentNodeId,
                                const UA_NodeId referenceTypeId,
                                const UA_QualifiedName browseName,
                                const UA_ObjectTypeAttributes attr,
                                void *nodeContext, UA_NodeId *outNewNodeId) {
        return __UA_Server_addNode(server, UA_NODECLASS_OBJECTTYPE, &requestedNewNodeId,
                                   &parentNodeId, &referenceTypeId, browseName,
                                   &UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
                                   &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES],
                                   nodeContext, outNewNodeId);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_addViewNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
                          const UA_NodeId parentNodeId,
                          const UA_NodeId referenceTypeId,
                          const UA_QualifiedName browseName,
                          const UA_ViewAttributes attr,
                          void *nodeContext, UA_NodeId *outNewNodeId) {
        return __UA_Server_addNode(server, UA_NODECLASS_VIEW, &requestedNewNodeId,
                                   &parentNodeId, &referenceTypeId, browseName,
                                   &UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
                                   &UA_TYPES[UA_TYPES_VIEWATTRIBUTES],
                                   nodeContext, outNewNodeId);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_addReferenceTypeNode(UA_Server *server,
                                   const UA_NodeId requestedNewNodeId,
                                   const UA_NodeId parentNodeId,
                                   const UA_NodeId referenceTypeId,
                                   const UA_QualifiedName browseName,
                                   const UA_ReferenceTypeAttributes attr,
                                   void *nodeContext, UA_NodeId *outNewNodeId) {
        return __UA_Server_addNode(server, UA_NODECLASS_REFERENCETYPE,
                                   &requestedNewNodeId, &parentNodeId, &referenceTypeId,
                                   browseName, &UA_NODEID_NULL,
                                   (const UA_NodeAttributes*)&attr,
                                   &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES],
                                   nodeContext, outNewNodeId);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Server_addDataTypeNode(UA_Server *server,
                              const UA_NodeId requestedNewNodeId,
                              const UA_NodeId parentNodeId,
                              const UA_NodeId referenceTypeId,
                              const UA_QualifiedName browseName,
                              const UA_DataTypeAttributes attr,
                              void *nodeContext, UA_NodeId *outNewNodeId) {
        return __UA_Server_addNode(server, UA_NODECLASS_DATATYPE, &requestedNewNodeId,
                                   &parentNodeId, &referenceTypeId, browseName,
                                   &UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
                                   &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],
                                   nodeContext, outNewNodeId);
    }
    
    UA_StatusCode
    UA_Server_addDataSourceVariableNode(UA_Server *server,
                                        const UA_NodeId requestedNewNodeId,
                                        const UA_NodeId parentNodeId,
                                        const UA_NodeId referenceTypeId,
                                        const UA_QualifiedName browseName,
                                        const UA_NodeId typeDefinition,
                                        const UA_VariableAttributes attr,
                                        const UA_DataSource dataSource,
                                        void *nodeContext, UA_NodeId *outNewNodeId);
    
    UA_StatusCode
    UA_Server_addMethodNodeEx(UA_Server *server, const UA_NodeId requestedNewNodeId,
                              const UA_NodeId parentNodeId,
                              const UA_NodeId referenceTypeId,
                              const UA_QualifiedName browseName,
                              const UA_MethodAttributes attr, UA_MethodCallback method,
                              size_t inputArgumentsSize, const UA_Argument *inputArguments,
                              const UA_NodeId inputArgumentsRequestedNewNodeId,
                              UA_NodeId *inputArgumentsOutNewNodeId,
                              size_t outputArgumentsSize, const UA_Argument *outputArguments,
                              const UA_NodeId outputArgumentsRequestedNewNodeId,
                              UA_NodeId *outputArgumentsOutNewNodeId,
                              void *nodeContext, UA_NodeId *outNewNodeId);
    
    static UA_INLINE UA_StatusCode
    UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
                            const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
                            const UA_QualifiedName browseName, const UA_MethodAttributes attr,
                            UA_MethodCallback method,
                            size_t inputArgumentsSize, const UA_Argument *inputArguments,
                            size_t outputArgumentsSize, const UA_Argument *outputArguments,
                            void *nodeContext, UA_NodeId *outNewNodeId) {
        return UA_Server_addMethodNodeEx(server, requestedNewNodeId,  parentNodeId,
                                         referenceTypeId, browseName, attr, method,
                                         inputArgumentsSize, inputArguments, UA_NODEID_NULL, NULL,
                                         outputArgumentsSize, outputArguments, UA_NODEID_NULL, NULL,
                                         nodeContext, outNewNodeId);
    }

    方法对UA_Server_addNode_begin和_finish将AddNodes服务分为两部分。如果在完成实例化之前修改节点,这将非常有用。例如,添加具有特定NodeIds的子项。否则,使用伪随机唯一NodeId添加强制子节点(例如,ObjectType)。在_finish部分期间通过其匹配的BrowseName检测现有子项。

    _begin方法:

    • 准备节点并将其添加到nodestore
    • 在内部从TypeDefinition节点复制一些未分配的属性
    • 添加对父项的引用(如果适用,还添加TypeDefinition)
    • 执行变量的类型检查。

    如果将parentNodeId和referenceTypeId设置为UA_NODE_ID_NULL,则可以添加没有父对象的对象节点。然后,在调用_finish方法之前,需要自己添加父引用和hasTypeDef引用。并不是说这只允许对象节点。

    _finish方法:

    • 复制强制性儿童
    • 最后调用节点构造函数
    • 可能会在遇到错误时删除该节点。

    特殊的UA_Server_addMethodNode_finish方法需要用于方法节点,因为您需要显式指定在完成步骤中添加的输入和输出参数(如果尚未存在)

    VariableAttributes对于变量,ObjectAttributes对象等。如果适用,将从TypeDefinition节点获取缺少的属性。

    UA_StatusCode
    UA_Server_addNode_begin(UA_Server *server, const UA_NodeClass nodeClass,
                            const UA_NodeId requestedNewNodeId,
                            const UA_NodeId parentNodeId,
                            const UA_NodeId referenceTypeId,
                            const UA_QualifiedName browseName,
                            const UA_NodeId typeDefinition,
                            const void *attr, const UA_DataType *attributeType,
                            void *nodeContext, UA_NodeId *outNewNodeId);
    
    UA_StatusCode
    UA_Server_addNode_finish(UA_Server *server, const UA_NodeId nodeId);
    
    UA_StatusCode
    UA_Server_addMethodNode_finish(UA_Server *server, const UA_NodeId nodeId,
                             UA_MethodCallback method,
                             size_t inputArgumentsSize, const UA_Argument* inputArguments,
                             size_t outputArgumentsSize, const UA_Argument* outputArguments);
    
    /* Deletes a node and optionally all references leading to the node. */
    UA_StatusCode
    UA_Server_deleteNode(UA_Server *server, const UA_NodeId nodeId,
                         UA_Boolean deleteReferences);

    参考管理

    UA_StatusCode
    UA_Server_addReference(UA_Server *server, const UA_NodeId sourceId,
                           const UA_NodeId refTypeId,
                           const UA_ExpandedNodeId targetId, UA_Boolean isForward);
    
    UA_StatusCode
    UA_Server_deleteReference(UA_Server *server, const UA_NodeId sourceNodeId,
                              const UA_NodeId referenceTypeId, UA_Boolean isForward,
                              const UA_ExpandedNodeId targetNodeId,
                              UA_Boolean deleteBidirectional);

    效用函数

    /* Add a new namespace to the server. Returns the index of the new namespace */
    UA_UInt16 UA_Server_addNamespace(UA_Server *server, const char* name);

    不推荐使用的服务器

    此文件包含为保持向后兼容性而保留的过时API定义。请切换到新API,因为最终将删除以下定义。

    UA_Job API 

    UA_Job被替换,因为它不必要地将服务器内部暴露给最终用户。请改用普通的UA_ServerCallbacks。以下UA_Job定义仅包含对最终用户有用的原始结构的一部分。

    typedef enum {
        UA_JOBTYPE_METHODCALL
    } UA_JobType;
    
    typedef struct {
        UA_JobType type;
        union {
            struct {
                void *data;
                UA_ServerCallback method;
            } methodCall;
        } job;
    } UA_Job;
    
    UA_DEPRECATED static UA_INLINE UA_StatusCode
    UA_Server_addRepeatedJob(UA_Server *server, UA_Job job,
                             UA_UInt32 interval, UA_Guid *jobId) {
        return UA_Server_addRepeatedCallback(server, job.job.methodCall.method,
                                             job.job.methodCall.data, interval,
                                             (UA_UInt64*)(uintptr_t)jobId);
    }
    
    UA_DEPRECATED static UA_INLINE UA_StatusCode
    UA_Server_removeRepeatedJob(UA_Server *server, UA_Guid jobId) {
        return UA_Server_removeRepeatedCallback(server,
                                                *(UA_UInt64*)(uintptr_t)&jobId);
    }

    客户

    客户端实现允许远程访问所有OPC UA服务。为方便起见,某些功能已包含在高级抽象中

    但是:此时,客户端尚未包含自己的线程或事件驱动的主循环。因此客户端不会在后台自动执行任何操作。这与订阅尤其相关。用户必须定期调用UA_Client_Subscriptions_manuallySendPublishRequest。另见这里

    客户端配置

    客户端配置用于设置客户端使用的连接参数和其他设置。传递给客户端后,不应修改配置。目前,一次只能有一个客户端使用配置。

    /plugins文件夹中提供了配置示例。通常的用法如下:

    1. 使用默认设置创建客户端配置作为起点
    2. 修改配置,例如修改超时
    3. 使用它实例化客户端
    4. 关闭客户端后,清理配置(可用内存)

    教程为此提供一个良好的起点。

    typedef enum {
        UA_CLIENTSTATE_DISCONNECTED,        /* The client is disconnected */
        UA_CLIENTSTATE_CONNECTED,           /* A TCP connection to the server is open */
        UA_CLIENTSTATE_SECURECHANNEL,       /* A SecureChannel to the server is open */
        UA_CLIENTSTATE_SESSION,             /* A session with the server is open */
        UA_CLIENTSTATE_SESSION_RENEWED      /* A session with the server is open (renewed) */
    } UA_ClientState;
    
    
    struct UA_Client;
    typedef struct UA_Client UA_Client;

    客户端生命周期回调

    typedef void (*UA_ClientStateCallback)(UA_Client *client, UA_ClientState clientState);

    订阅不活动回调

    #ifdef UA_ENABLE_SUBSCRIPTIONS
    typedef void (*UA_SubscriptionInactivityCallback)(UA_Client *client, UA_UInt32 subscriptionId, void *subContext);
    #endif

    不活动回调

    typedef void (*UA_InactivityCallback)(UA_Client *client);

    客户端配置数据

    typedef struct UA_ClientConfig {
        UA_UInt32 timeout;               /* ASync + Sync response timeout in ms */
        UA_UInt32 secureChannelLifeTime; /* Lifetime in ms (then the channel needs
                                            to be renewed) */
        UA_Logger logger;
        UA_ConnectionConfig localConnectionConfig;
        UA_ConnectClientConnection connectionFunc;
    
        /* Custom DataTypes */
        size_t customDataTypesSize;
        const UA_DataType *customDataTypes;
    
        /* Callback function */
        UA_ClientStateCallback stateCallback;
    #ifdef UA_ENABLE_SUBSCRIPTIONS
        /* When outStandingPublishRequests is greater than 0,
         * the server automatically create publishRequest when
         * UA_Client_runAsync is called. If the client don't receive
         * a publishResponse after :
         *     (sub->publishingInterval * sub->maxKeepAliveCount) +
         *     client->config.timeout)
         * then, the client call subscriptionInactivityCallback
         * The connection can be closed, this in an attempt to
         * recreate a healthy connection. */
        UA_SubscriptionInactivityCallback subscriptionInactivityCallback;
    #endif
    
        /* When connectivityCheckInterval is greater than 0,
         * every connectivityCheckInterval (in ms), a async read request
         * is performed on the server. inactivityCallback is called
         * when the client receive no response for this read request
         * The connection can be closed, this in an attempt to
         * recreate a healthy connection. */
        UA_InactivityCallback inactivityCallback;
    
        void *clientContext;
    
    #ifdef UA_ENABLE_SUBSCRIPTIONS
        /* number of PublishResponse standing in the sever
         * 0 = background task disabled                    */
        UA_UInt16 outStandingPublishRequests;
    #endif
        /* connectivity check interval in ms
         * 0 = background task disabled */
        UA_UInt32 connectivityCheckInterval;
    } UA_ClientConfig;
    
    
    /* Get the client configuration from the configuration plugin. Used by the
     * server when it needs client functionality to register to a discovery server
     * or when the server needs to create a client for other purposes
     *
     * @return The client configuration structure */
    UA_ClientConfig
    UA_Server_getClientConfig(void);

    客户生命周期

    /* Create a new client */
    UA_Client *
    UA_Client_new(UA_ClientConfig config);
    
    /* Get the client connection status */
    UA_ClientState
    UA_Client_getState(UA_Client *client);
    
    /* Get the client context */
    void *
    UA_Client_getContext(UA_Client *client);
    
    /* Reset a client */
    void
    UA_Client_reset(UA_Client *client);
    
    /* Delete a client */
    void
    UA_Client_delete(UA_Client *client);

    连接服务器

    /* Connect to the server
     *
     * @param client to use
     * @param endpointURL to connect (for example "opc.tcp://localhost:4840")
     * @return Indicates whether the operation succeeded or returns an error code */
    UA_StatusCode
    UA_Client_connect(UA_Client *client, const char *endpointUrl);
    
    /* Connect to the selected server with the given username and password
     *
     * @param client to use
     * @param endpointURL to connect (for example "opc.tcp://localhost:4840")
     * @param username
     * @param password
     * @return Indicates whether the operation succeeded or returns an error code */
    UA_StatusCode
    UA_Client_connect_username(UA_Client *client, const char *endpointUrl,
                               const char *username, const char *password);
    
    /* Disconnect and close a connection to the selected server */
    UA_StatusCode
    UA_Client_disconnect(UA_Client *client);
    
    /* Close a connection to the selected server */
    UA_StatusCode
    UA_Client_close(UA_Client *client);
    
    /* Renew the underlying secure channel */
    UA_StatusCode
    UA_Client_manuallyRenewSecureChannel(UA_Client *client);

    发现

    /* Gets a list of endpoints of a server
     *
     * @param client to use. Must be connected to the same endpoint given in
     *        serverUrl or otherwise in disconnected state.
     * @param serverUrl url to connect (for example "opc.tcp://localhost:4840")
     * @param endpointDescriptionsSize size of the array of endpoint descriptions
     * @param endpointDescriptions array of endpoint descriptions that is allocated
     *        by the function (you need to free manually)
     * @return Indicates whether the operation succeeded or returns an error code */
    UA_StatusCode
    UA_Client_getEndpoints(UA_Client *client, const char *serverUrl,
                           size_t* endpointDescriptionsSize,
                           UA_EndpointDescription** endpointDescriptions);
    
    /* Gets a list of all registered servers at the given server.
     *
     * You can pass an optional filter for serverUris. If the given server is not registered,
     * an empty array will be returned. If the server is registered, only that application
     * description will be returned.
     *
     * Additionally you can optionally indicate which locale you want for the server name
     * in the returned application description. The array indicates the order of preference.
     * A server may have localized names.
     *
     * @param client to use. Must be connected to the same endpoint given in
     *        serverUrl or otherwise in disconnected state.
     * @param serverUrl url to connect (for example "opc.tcp://localhost:4840")
     * @param serverUrisSize Optional filter for specific server uris
     * @param serverUris Optional filter for specific server uris
     * @param localeIdsSize Optional indication which locale you prefer
     * @param localeIds Optional indication which locale you prefer
     * @param registeredServersSize size of returned array, i.e., number of found/registered servers
     * @param registeredServers array containing found/registered servers
     * @return Indicates whether the operation succeeded or returns an error code */
    UA_StatusCode
    UA_Client_findServers(UA_Client *client, const char *serverUrl,
                          size_t serverUrisSize, UA_String *serverUris,
                          size_t localeIdsSize, UA_String *localeIds,
                          size_t *registeredServersSize,
                          UA_ApplicationDescription **registeredServers);
    
    /* Get a list of all known server in the network. Only supported by LDS servers.
     *
     * @param client to use. Must be connected to the same endpoint given in
     * serverUrl or otherwise in disconnected state.
     * @param serverUrl url to connect (for example "opc.tcp://localhost:4840")
     * @param startingRecordId optional. Only return the records with an ID higher
     *        or equal the given. Can be used for pagination to only get a subset of
     *        the full list
     * @param maxRecordsToReturn optional. Only return this number of records
    
     * @param serverCapabilityFilterSize optional. Filter the returned list to only
     *        get servers with given capabilities, e.g. "LDS"
     * @param serverCapabilityFilter optional. Filter the returned list to only get
     *        servers with given capabilities, e.g. "LDS"
     * @param serverOnNetworkSize size of returned array, i.e., number of
     *        known/registered servers
     * @param serverOnNetwork array containing known/registered servers
     * @return Indicates whether the operation succeeded or returns an error code */
    UA_StatusCode
    UA_Client_findServersOnNetwork(UA_Client *client, const char *serverUrl,
                                   UA_UInt32 startingRecordId, UA_UInt32 maxRecordsToReturn,
                                   size_t serverCapabilityFilterSize, UA_String *serverCapabilityFilter,
                                   size_t *serverOnNetworkSize, UA_ServerOnNetwork **serverOnNetwork);

    服务

    原始OPC UA服务向客户端公开。但是大多数时候,最好使用ua_client_highlevel.h 包装原始服务的便利功能。

    /* Don't use this function. Use the type versions below instead. */
    void
    __UA_Client_Service(UA_Client *client, const void *request,
                        const UA_DataType *requestType, void *response,
                        const UA_DataType *responseType);
    
    /*
     * Attribute Service Set
     * ^^^^^^^^^^^^^^^^^^^^^ */
    static UA_INLINE UA_ReadResponse
    UA_Client_Service_read(UA_Client *client, const UA_ReadRequest request) {
        UA_ReadResponse response;
        __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_READREQUEST],
                            &response, &UA_TYPES[UA_TYPES_READRESPONSE]);
        return response;
    }
    
    static UA_INLINE UA_WriteResponse
    UA_Client_Service_write(UA_Client *client, const UA_WriteRequest request) {
        UA_WriteResponse response;
        __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_WRITEREQUEST],
                            &response, &UA_TYPES[UA_TYPES_WRITERESPONSE]);
        return response;
    }
    
    /*
     * Method Service Set
     * ^^^^^^^^^^^^^^^^^^ */
    #ifdef UA_ENABLE_METHODCALLS
    static UA_INLINE UA_CallResponse
    UA_Client_Service_call(UA_Client *client, const UA_CallRequest request) {
        UA_CallResponse response;
        __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_CALLREQUEST],
                            &response, &UA_TYPES[UA_TYPES_CALLRESPONSE]);
        return response;
    }
    #endif
    
    /*
     * NodeManagement Service Set
     * ^^^^^^^^^^^^^^^^^^^^^^^^^^ */
    static UA_INLINE UA_AddNodesResponse
    UA_Client_Service_addNodes(UA_Client *client, const UA_AddNodesRequest request) {
        UA_AddNodesResponse response;
        __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_ADDNODESREQUEST],
                            &response, &UA_TYPES[UA_TYPES_ADDNODESRESPONSE]);
        return response;
    }
    
    static UA_INLINE UA_AddReferencesResponse
    UA_Client_Service_addReferences(UA_Client *client,
                                    const UA_AddReferencesRequest request) {
        UA_AddReferencesResponse response;
        __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_ADDREFERENCESREQUEST],
                            &response, &UA_TYPES[UA_TYPES_ADDREFERENCESRESPONSE]);
        return response;
    }
    
    static UA_INLINE UA_DeleteNodesResponse
    UA_Client_Service_deleteNodes(UA_Client *client,
                                  const UA_DeleteNodesRequest request) {
        UA_DeleteNodesResponse response;
        __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_DELETENODESREQUEST],
                            &response, &UA_TYPES[UA_TYPES_DELETENODESRESPONSE]);
        return response;
    }
    
    static UA_INLINE UA_DeleteReferencesResponse
    UA_Client_Service_deleteReferences(UA_Client *client,
                                       const UA_DeleteReferencesRequest request) {
        UA_DeleteReferencesResponse response;
        __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_DELETEREFERENCESREQUEST],
                            &response, &UA_TYPES[UA_TYPES_DELETEREFERENCESRESPONSE]);
        return response;
    }
    
    /*
     * View Service Set
     * ^^^^^^^^^^^^^^^^ */
    static UA_INLINE UA_BrowseResponse
    UA_Client_Service_browse(UA_Client *client, const UA_BrowseRequest request) {
        UA_BrowseResponse response;
        __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_BROWSEREQUEST],
                            &response, &UA_TYPES[UA_TYPES_BROWSERESPONSE]);
        return response;
    }
    
    static UA_INLINE UA_BrowseNextResponse
    UA_Client_Service_browseNext(UA_Client *client,
                                 const UA_BrowseNextRequest request) {
        UA_BrowseNextResponse response;
        __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_BROWSENEXTREQUEST],
                            &response, &UA_TYPES[UA_TYPES_BROWSENEXTRESPONSE]);
        return response;
    }
    
    static UA_INLINE UA_TranslateBrowsePathsToNodeIdsResponse
    UA_Client_Service_translateBrowsePathsToNodeIds(UA_Client *client,
                            const UA_TranslateBrowsePathsToNodeIdsRequest request) {
        UA_TranslateBrowsePathsToNodeIdsResponse response;
        __UA_Client_Service(client, &request,
                            &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSREQUEST],
                            &response,
                            &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE]);
        return response;
    }
    
    static UA_INLINE UA_RegisterNodesResponse
    UA_Client_Service_registerNodes(UA_Client *client,
                                    const UA_RegisterNodesRequest request) {
        UA_RegisterNodesResponse response;
        __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_REGISTERNODESREQUEST],
                            &response, &UA_TYPES[UA_TYPES_REGISTERNODESRESPONSE]);
        return response;
    }
    
    static UA_INLINE UA_UnregisterNodesResponse
    UA_Client_Service_unregisterNodes(UA_Client *client,
                                      const UA_UnregisterNodesRequest request) {
        UA_UnregisterNodesResponse response;
        __UA_Client_Service(client, &request,
                            &UA_TYPES[UA_TYPES_UNREGISTERNODESREQUEST],
                            &response, &UA_TYPES[UA_TYPES_UNREGISTERNODESRESPONSE]);
        return response;
    }
    
    /*
     * Query Service Set
     * ^^^^^^^^^^^^^^^^^ */
    static UA_INLINE UA_QueryFirstResponse
    UA_Client_Service_queryFirst(UA_Client *client,
                                 const UA_QueryFirstRequest request) {
        UA_QueryFirstResponse response;
        __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_QUERYFIRSTREQUEST],
                            &response, &UA_TYPES[UA_TYPES_QUERYFIRSTRESPONSE]);
        return response;
    }
    
    static UA_INLINE UA_QueryNextResponse
    UA_Client_Service_queryNext(UA_Client *client,
                                const UA_QueryNextRequest request) {
        UA_QueryNextResponse response;
        __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_QUERYFIRSTREQUEST],
                            &response, &UA_TYPES[UA_TYPES_QUERYFIRSTRESPONSE]);
        return response;
    }

    异步服务

    所有OPC UA服务本质上都是异步的。因此,可以在不等待响应的情况下进行多次服务调用。Responess可能会有不同的顺序。

    /* Listen on the network and process arriving asynchronous responses in the
     * background. Internal housekeeping and subscription management is done as
     * well. */
    UA_StatusCode
    UA_Client_runAsync(UA_Client *client, UA_UInt16 timeout);
    
    typedef void
    (*UA_ClientAsyncServiceCallback)(UA_Client *client, void *userdata,
                                     UA_UInt32 requestId, void *response,
                                     const UA_DataType *responseType);
    
    /* Use the type versions of this method. See below. However, the general
     * mechanism of async service calls is explained here.
     *
     * We say that an async service call has been dispatched once this method
     * returns UA_STATUSCODE_GOOD. If there is an error after an async service has
     * been dispatched, the callback is called with an "empty" response where the
     * statusCode has been set accordingly. This is also done if the client is
     * shutting down and the list of dispatched async services is emptied.
     *
     * The statusCode received when the client is shutting down is
     * UA_STATUSCODE_BADSHUTDOWN.
     *
     * The statusCode received when the client don't receive response
     * after specified config->timeout (in ms) is
     * UA_STATUSCODE_BADTIMEOUT.
     *
     * Instead, you can use __UA_Client_AsyncServiceEx to specify
     * a custom timeout
     *
     * The userdata and requestId arguments can be NULL. */
    UA_StatusCode
    __UA_Client_AsyncService(UA_Client *client, const void *request,
                             const UA_DataType *requestType,
                             UA_ClientAsyncServiceCallback callback,
                             const UA_DataType *responseType,
                             void *userdata, UA_UInt32 *requestId);
    
    /* Use the type versions of this method. See below. However, the general
     * mechanism of async service calls is explained here.
     *
     * We say that an async service call has been dispatched once this method
     * returns UA_STATUSCODE_GOOD. If there is an error after an async service has
     * been dispatched, the callback is called with an "empty" response where the
     * statusCode has been set accordingly. This is also done if the client is
     * shutting down and the list of dispatched async services is emptied.
     *
     * The statusCode received when the client is shutting down is
     * UA_STATUSCODE_BADSHUTDOWN.
     *
     * The statusCode received when the client don't receive response
     * after specified timeout (in ms) is
     * UA_STATUSCODE_BADTIMEOUT.
     *
     * The timeout can be disabled by setting timeout to 0
     *
     * The userdata and requestId arguments can be NULL. */
    UA_StatusCode
    __UA_Client_AsyncServiceEx(UA_Client *client, const void *request,
                               const UA_DataType *requestType,
                               UA_ClientAsyncServiceCallback callback,
                               const UA_DataType *responseType,
                               void *userdata, UA_UInt32 *requestId,
                               UA_UInt32 timeout);
    
    static UA_INLINE UA_StatusCode
    UA_Client_AsyncService_read(UA_Client *client, const UA_ReadRequest *request,
                                UA_ClientAsyncServiceCallback callback,
                                void *userdata, UA_UInt32 *requestId) {
        return __UA_Client_AsyncService(client, (const void*)request,
                                        &UA_TYPES[UA_TYPES_READREQUEST], callback,
                                        &UA_TYPES[UA_TYPES_READRESPONSE],
                                        userdata, requestId);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_AsyncService_write(UA_Client *client, const UA_WriteRequest *request,
                                 UA_ClientAsyncServiceCallback callback,
                                 void *userdata, UA_UInt32 *requestId) {
        return __UA_Client_AsyncService(client, (const void*)request,
                                        &UA_TYPES[UA_TYPES_WRITEREQUEST], callback,
                                        &UA_TYPES[UA_TYPES_WRITERESPONSE],
                                        userdata, requestId);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_AsyncService_call(UA_Client *client, const UA_CallRequest *request,
                                UA_ClientAsyncServiceCallback callback,
                                void *userdata, UA_UInt32 *requestId) {
        return __UA_Client_AsyncService(client, (const void*)request,
                                        &UA_TYPES[UA_TYPES_CALLREQUEST], callback,
                                        &UA_TYPES[UA_TYPES_CALLRESPONSE],
                                        userdata, requestId);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_AsyncService_browse(UA_Client *client, const UA_BrowseRequest *request,
                                  UA_ClientAsyncServiceCallback callback,
                                  void *userdata, UA_UInt32 *requestId) {
        return __UA_Client_AsyncService(client, (const void*)request,
                                        &UA_TYPES[UA_TYPES_BROWSEREQUEST], callback,
                                        &UA_TYPES[UA_TYPES_BROWSERESPONSE],
                                        userdata, requestId);
    }

    高级客户端功能

    以下定义是在后台使用标准OPC UA服务的便捷功能。这是一种处理堆栈的灵活性较低的方法,因为在许多地方都会假定合理的默认值; 同时使用这些功能是实现OPC UA应用程序的最简单方法,因为您不必考虑进入OPC UA服务的所有细节。如果需要更大的灵活性,您始终可以使用原始OPC UA服务实现相同的功能。

    读属性

    以下函数可用于检索单个节点属性。使用常规服务一次读取多个属性。

    /* Don't call this function, use the typed versions */
    UA_StatusCode
    __UA_Client_readAttribute(UA_Client *client, const UA_NodeId *nodeId,
                              UA_AttributeId attributeId, void *out,
                              const UA_DataType *outDataType);
    
    static UA_INLINE UA_StatusCode
    UA_Client_readNodeIdAttribute(UA_Client *client, const UA_NodeId nodeId,
                                  UA_NodeId *outNodeId) {
        return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_NODEID,
                                         outNodeId, &UA_TYPES[UA_TYPES_NODEID]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_readNodeClassAttribute(UA_Client *client, const UA_NodeId nodeId,
                                     UA_NodeClass *outNodeClass) {
        return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_NODECLASS,
                                         outNodeClass, &UA_TYPES[UA_TYPES_NODECLASS]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_readBrowseNameAttribute(UA_Client *client, const UA_NodeId nodeId,
                                      UA_QualifiedName *outBrowseName) {
        return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_BROWSENAME,
                                         outBrowseName,
                                         &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_readDisplayNameAttribute(UA_Client *client, const UA_NodeId nodeId,
                                       UA_LocalizedText *outDisplayName) {
        return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_DISPLAYNAME,
                                         outDisplayName,
                                         &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_readDescriptionAttribute(UA_Client *client, const UA_NodeId nodeId,
                                       UA_LocalizedText *outDescription) {
        return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_DESCRIPTION,
                                         outDescription,
                                         &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_readWriteMaskAttribute(UA_Client *client, const UA_NodeId nodeId,
                                     UA_UInt32 *outWriteMask) {
        return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_WRITEMASK,
                                         outWriteMask, &UA_TYPES[UA_TYPES_UINT32]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_readUserWriteMaskAttribute(UA_Client *client, const UA_NodeId nodeId,
                                         UA_UInt32 *outUserWriteMask) {
        return __UA_Client_readAttribute(client, &nodeId,
                                         UA_ATTRIBUTEID_USERWRITEMASK,
                                         outUserWriteMask,
                                         &UA_TYPES[UA_TYPES_UINT32]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_readIsAbstractAttribute(UA_Client *client, const UA_NodeId nodeId,
                                      UA_Boolean *outIsAbstract) {
        return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_ISABSTRACT,
                                         outIsAbstract, &UA_TYPES[UA_TYPES_BOOLEAN]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_readSymmetricAttribute(UA_Client *client, const UA_NodeId nodeId,
                                     UA_Boolean *outSymmetric) {
        return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_SYMMETRIC,
                                         outSymmetric, &UA_TYPES[UA_TYPES_BOOLEAN]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_readInverseNameAttribute(UA_Client *client, const UA_NodeId nodeId,
                                       UA_LocalizedText *outInverseName) {
        return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_INVERSENAME,
                                         outInverseName,
                                         &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_readContainsNoLoopsAttribute(UA_Client *client, const UA_NodeId nodeId,
                                           UA_Boolean *outContainsNoLoops) {
        return __UA_Client_readAttribute(client, &nodeId,
                                         UA_ATTRIBUTEID_CONTAINSNOLOOPS,
                                         outContainsNoLoops,
                                         &UA_TYPES[UA_TYPES_BOOLEAN]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_readEventNotifierAttribute(UA_Client *client, const UA_NodeId nodeId,
                                         UA_Byte *outEventNotifier) {
        return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_EVENTNOTIFIER,
                                         outEventNotifier, &UA_TYPES[UA_TYPES_BYTE]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_readValueAttribute(UA_Client *client, const UA_NodeId nodeId,
                                 UA_Variant *outValue) {
        return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_VALUE,
                                         outValue, &UA_TYPES[UA_TYPES_VARIANT]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_readDataTypeAttribute(UA_Client *client, const UA_NodeId nodeId,
                                    UA_NodeId *outDataType) {
        return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_DATATYPE,
                                         outDataType, &UA_TYPES[UA_TYPES_NODEID]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_readValueRankAttribute(UA_Client *client, const UA_NodeId nodeId,
                                     UA_Int32 *outValueRank) {
        return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_VALUERANK,
                                         outValueRank, &UA_TYPES[UA_TYPES_INT32]);
    }
    
    UA_StatusCode
    UA_Client_readArrayDimensionsAttribute(UA_Client *client, const UA_NodeId nodeId,
                                           size_t *outArrayDimensionsSize,
                                           UA_UInt32 **outArrayDimensions);
    
    static UA_INLINE UA_StatusCode
    UA_Client_readAccessLevelAttribute(UA_Client *client, const UA_NodeId nodeId,
                                       UA_Byte *outAccessLevel) {
        return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_ACCESSLEVEL,
                                         outAccessLevel, &UA_TYPES[UA_TYPES_BYTE]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_readUserAccessLevelAttribute(UA_Client *client, const UA_NodeId nodeId,
                                           UA_Byte *outUserAccessLevel) {
        return __UA_Client_readAttribute(client, &nodeId,
                                         UA_ATTRIBUTEID_USERACCESSLEVEL,
                                         outUserAccessLevel,
                                         &UA_TYPES[UA_TYPES_BYTE]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_readMinimumSamplingIntervalAttribute(UA_Client *client,
                                                   const UA_NodeId nodeId,
                                                   UA_Double *outMinSamplingInterval) {
        return __UA_Client_readAttribute(client, &nodeId,
                                         UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL,
                                         outMinSamplingInterval,
                                         &UA_TYPES[UA_TYPES_DOUBLE]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_readHistorizingAttribute(UA_Client *client, const UA_NodeId nodeId,
                                       UA_Boolean *outHistorizing) {
        return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_HISTORIZING,
                                         outHistorizing, &UA_TYPES[UA_TYPES_BOOLEAN]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_readExecutableAttribute(UA_Client *client, const UA_NodeId nodeId,
                                      UA_Boolean *outExecutable) {
        return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_EXECUTABLE,
                                         outExecutable, &UA_TYPES[UA_TYPES_BOOLEAN]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_readUserExecutableAttribute(UA_Client *client, const UA_NodeId nodeId,
                                          UA_Boolean *outUserExecutable) {
        return __UA_Client_readAttribute(client, &nodeId,
                                         UA_ATTRIBUTEID_USEREXECUTABLE,
                                         outUserExecutable,
                                         &UA_TYPES[UA_TYPES_BOOLEAN]);
    }

    写属性

    以下函数可用于一次写入单个节点属性。使用常规写入服务一次写入多个属性。

    /* Don't call this function, use the typed versions */
    UA_StatusCode
    __UA_Client_writeAttribute(UA_Client *client, const UA_NodeId *nodeId,
                               UA_AttributeId attributeId, const void *in,
                               const UA_DataType *inDataType);
    
    static UA_INLINE UA_StatusCode
    UA_Client_writeNodeIdAttribute(UA_Client *client, const UA_NodeId nodeId,
                                   const UA_NodeId *newNodeId) {
        return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_NODEID,
                                          newNodeId, &UA_TYPES[UA_TYPES_NODEID]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_writeNodeClassAttribute(UA_Client *client, const UA_NodeId nodeId,
                                      const UA_NodeClass *newNodeClass) {
        return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_NODECLASS,
                                          newNodeClass, &UA_TYPES[UA_TYPES_NODECLASS]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_writeBrowseNameAttribute(UA_Client *client, const UA_NodeId nodeId,
                                       const UA_QualifiedName *newBrowseName) {
        return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_BROWSENAME,
                                          newBrowseName,
                                          &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_writeDisplayNameAttribute(UA_Client *client, const UA_NodeId nodeId,
                                        const UA_LocalizedText *newDisplayName) {
        return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_DISPLAYNAME,
                                          newDisplayName,
                                          &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_writeDescriptionAttribute(UA_Client *client, const UA_NodeId nodeId,
                                        const UA_LocalizedText *newDescription) {
        return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_DESCRIPTION,
                                          newDescription,
                                          &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_writeWriteMaskAttribute(UA_Client *client, const UA_NodeId nodeId,
                                      const UA_UInt32 *newWriteMask) {
        return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_WRITEMASK,
                                          newWriteMask, &UA_TYPES[UA_TYPES_UINT32]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_writeUserWriteMaskAttribute(UA_Client *client, const UA_NodeId nodeId,
                                          const UA_UInt32 *newUserWriteMask) {
        return __UA_Client_writeAttribute(client, &nodeId,
                                          UA_ATTRIBUTEID_USERWRITEMASK,
                                          newUserWriteMask,
                                          &UA_TYPES[UA_TYPES_UINT32]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_writeIsAbstractAttribute(UA_Client *client, const UA_NodeId nodeId,
                                       const UA_Boolean *newIsAbstract) {
        return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_ISABSTRACT,
                                          newIsAbstract, &UA_TYPES[UA_TYPES_BOOLEAN]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_writeSymmetricAttribute(UA_Client *client, const UA_NodeId nodeId,
                                      const UA_Boolean *newSymmetric) {
        return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_SYMMETRIC,
                                          newSymmetric, &UA_TYPES[UA_TYPES_BOOLEAN]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_writeInverseNameAttribute(UA_Client *client, const UA_NodeId nodeId,
                                        const UA_LocalizedText *newInverseName) {
        return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_INVERSENAME,
                                          newInverseName,
                                          &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_writeContainsNoLoopsAttribute(UA_Client *client, const UA_NodeId nodeId,
                                            const UA_Boolean *newContainsNoLoops) {
        return __UA_Client_writeAttribute(client, &nodeId,
                                          UA_ATTRIBUTEID_CONTAINSNOLOOPS,
                                          newContainsNoLoops,
                                          &UA_TYPES[UA_TYPES_BOOLEAN]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_writeEventNotifierAttribute(UA_Client *client, const UA_NodeId nodeId,
                                          const UA_Byte *newEventNotifier) {
        return __UA_Client_writeAttribute(client, &nodeId,
                                          UA_ATTRIBUTEID_EVENTNOTIFIER,
                                          newEventNotifier,
                                          &UA_TYPES[UA_TYPES_BYTE]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_writeValueAttribute(UA_Client *client, const UA_NodeId nodeId,
                                  const UA_Variant *newValue) {
        return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_VALUE,
                                          newValue, &UA_TYPES[UA_TYPES_VARIANT]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_writeDataTypeAttribute(UA_Client *client, const UA_NodeId nodeId,
                                     const UA_NodeId *newDataType) {
        return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_DATATYPE,
                                          newDataType, &UA_TYPES[UA_TYPES_NODEID]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_writeValueRankAttribute(UA_Client *client, const UA_NodeId nodeId,
                                      const UA_Int32 *newValueRank) {
        return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_VALUERANK,
                                          newValueRank, &UA_TYPES[UA_TYPES_INT32]);
    }
    
    UA_StatusCode
    UA_Client_writeArrayDimensionsAttribute(UA_Client *client, const UA_NodeId nodeId,
                                            size_t newArrayDimensionsSize,
                                            const UA_UInt32 *newArrayDimensions);
    
    static UA_INLINE UA_StatusCode
    UA_Client_writeAccessLevelAttribute(UA_Client *client, const UA_NodeId nodeId,
                                        const UA_Byte *newAccessLevel) {
        return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_ACCESSLEVEL,
                                          newAccessLevel, &UA_TYPES[UA_TYPES_BYTE]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_writeUserAccessLevelAttribute(UA_Client *client, const UA_NodeId nodeId,
                                            const UA_Byte *newUserAccessLevel) {
        return __UA_Client_writeAttribute(client, &nodeId,
                                          UA_ATTRIBUTEID_USERACCESSLEVEL,
                                          newUserAccessLevel,
                                          &UA_TYPES[UA_TYPES_BYTE]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_writeMinimumSamplingIntervalAttribute(UA_Client *client,
                                                    const UA_NodeId nodeId,
                                                    const UA_Double *newMinInterval) {
        return __UA_Client_writeAttribute(client, &nodeId,
                                          UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL,
                                          newMinInterval, &UA_TYPES[UA_TYPES_DOUBLE]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_writeHistorizingAttribute(UA_Client *client, const UA_NodeId nodeId,
                                        const UA_Boolean *newHistorizing) {
        return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_HISTORIZING,
                                          newHistorizing, &UA_TYPES[UA_TYPES_BOOLEAN]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_writeExecutableAttribute(UA_Client *client, const UA_NodeId nodeId,
                                       const UA_Boolean *newExecutable) {
        return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_EXECUTABLE,
                                          newExecutable, &UA_TYPES[UA_TYPES_BOOLEAN]);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_writeUserExecutableAttribute(UA_Client *client, const UA_NodeId nodeId,
                                           const UA_Boolean *newUserExecutable) {
        return __UA_Client_writeAttribute(client, &nodeId,
                                          UA_ATTRIBUTEID_USEREXECUTABLE,
                                          newUserExecutable,
                                          &UA_TYPES[UA_TYPES_BOOLEAN]);
    }

    方法调用

    UA_StatusCode
    UA_Client_call(UA_Client *client, const UA_NodeId objectId,
                   const UA_NodeId methodId, size_t inputSize, const UA_Variant *input,
                   size_t *outputSize, UA_Variant **output);

    节点管理

    请参阅有关服务器端节点管理的部分

    UA_StatusCode
    UA_Client_addReference(UA_Client *client, const UA_NodeId sourceNodeId,
                           const UA_NodeId referenceTypeId, UA_Boolean isForward,
                           const UA_String targetServerUri,
                           const UA_ExpandedNodeId targetNodeId,
                           UA_NodeClass targetNodeClass);
    
    UA_StatusCode
    UA_Client_deleteReference(UA_Client *client, const UA_NodeId sourceNodeId,
                              const UA_NodeId referenceTypeId, UA_Boolean isForward,
                              const UA_ExpandedNodeId targetNodeId,
                              UA_Boolean deleteBidirectional);
    
    UA_StatusCode
    UA_Client_deleteNode(UA_Client *client, const UA_NodeId nodeId,
                         UA_Boolean deleteTargetReferences);
    
    /* Protect against redundant definitions for server/client */
    #ifndef UA_DEFAULT_ATTRIBUTES_DEFINED
    #define UA_DEFAULT_ATTRIBUTES_DEFINED
    /* The default for variables is "BaseDataType" for the datatype, -2 for the
     * valuerank and a read-accesslevel. */
    extern const UA_VariableAttributes UA_VariableAttributes_default;
    extern const UA_VariableTypeAttributes UA_VariableTypeAttributes_default;
    /* Methods are executable by default */
    extern const UA_MethodAttributes UA_MethodAttributes_default;
    /* The remaining attribute definitions are currently all zeroed out */
    extern const UA_ObjectAttributes UA_ObjectAttributes_default;
    extern const UA_ObjectTypeAttributes UA_ObjectTypeAttributes_default;
    extern const UA_ReferenceTypeAttributes UA_ReferenceTypeAttributes_default;
    extern const UA_DataTypeAttributes UA_DataTypeAttributes_default;
    extern const UA_ViewAttributes UA_ViewAttributes_default;
    #endif
    
    /* Don't call this function, use the typed versions */
    UA_StatusCode
    __UA_Client_addNode(UA_Client *client, const UA_NodeClass nodeClass,
                        const UA_NodeId requestedNewNodeId,
                        const UA_NodeId parentNodeId,
                        const UA_NodeId referenceTypeId,
                        const UA_QualifiedName browseName,
                        const UA_NodeId typeDefinition, const UA_NodeAttributes *attr,
                        const UA_DataType *attributeType, UA_NodeId *outNewNodeId);
    
    static UA_INLINE UA_StatusCode
    UA_Client_addVariableNode(UA_Client *client, const UA_NodeId requestedNewNodeId,
                              const UA_NodeId parentNodeId,
                              const UA_NodeId referenceTypeId,
                              const UA_QualifiedName browseName,
                              const UA_NodeId typeDefinition,
                              const UA_VariableAttributes attr,
                              UA_NodeId *outNewNodeId) {
        return __UA_Client_addNode(client, UA_NODECLASS_VARIABLE, requestedNewNodeId,
                                   parentNodeId, referenceTypeId, browseName,
                                   typeDefinition, (const UA_NodeAttributes*)&attr,
                                   &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],
                                   outNewNodeId);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_addVariableTypeNode(UA_Client *client,
                                  const UA_NodeId requestedNewNodeId,
                                  const UA_NodeId parentNodeId,
                                  const UA_NodeId referenceTypeId,
                                  const UA_QualifiedName browseName,
                                  const UA_VariableTypeAttributes attr,
                                  UA_NodeId *outNewNodeId) {
        return __UA_Client_addNode(client, UA_NODECLASS_VARIABLETYPE,
                                   requestedNewNodeId,
                                   parentNodeId, referenceTypeId, browseName,
                                   UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
                                   &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES],
                                   outNewNodeId);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_addObjectNode(UA_Client *client, const UA_NodeId requestedNewNodeId,
                            const UA_NodeId parentNodeId,
                            const UA_NodeId referenceTypeId,
                            const UA_QualifiedName browseName,
                            const UA_NodeId typeDefinition,
                            const UA_ObjectAttributes attr, UA_NodeId *outNewNodeId) {
        return __UA_Client_addNode(client, UA_NODECLASS_OBJECT, requestedNewNodeId,
                                   parentNodeId, referenceTypeId, browseName,
                                   typeDefinition, (const UA_NodeAttributes*)&attr,
                                   &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES], outNewNodeId);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_addObjectTypeNode(UA_Client *client, const UA_NodeId requestedNewNodeId,
                                const UA_NodeId parentNodeId,
                                const UA_NodeId referenceTypeId,
                                const UA_QualifiedName browseName,
                                const UA_ObjectTypeAttributes attr,
                                UA_NodeId *outNewNodeId) {
        return __UA_Client_addNode(client, UA_NODECLASS_OBJECTTYPE, requestedNewNodeId,
                                   parentNodeId, referenceTypeId, browseName,
                                   UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
                                   &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES],
                                   outNewNodeId);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_addViewNode(UA_Client *client, const UA_NodeId requestedNewNodeId,
                          const UA_NodeId parentNodeId,
                          const UA_NodeId referenceTypeId,
                          const UA_QualifiedName browseName,
                          const UA_ViewAttributes attr,
                          UA_NodeId *outNewNodeId) {
        return __UA_Client_addNode(client, UA_NODECLASS_VIEW, requestedNewNodeId,
                                   parentNodeId, referenceTypeId, browseName,
                                   UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
                                   &UA_TYPES[UA_TYPES_VIEWATTRIBUTES], outNewNodeId);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_addReferenceTypeNode(UA_Client *client,
                                   const UA_NodeId requestedNewNodeId,
                                   const UA_NodeId parentNodeId,
                                   const UA_NodeId referenceTypeId,
                                   const UA_QualifiedName browseName,
                                   const UA_ReferenceTypeAttributes attr,
                                   UA_NodeId *outNewNodeId) {
        return __UA_Client_addNode(client, UA_NODECLASS_REFERENCETYPE,
                                   requestedNewNodeId,
                                   parentNodeId, referenceTypeId, browseName,
                                   UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
                                   &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES],
                                   outNewNodeId);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_addDataTypeNode(UA_Client *client, const UA_NodeId requestedNewNodeId,
                              const UA_NodeId parentNodeId,
                              const UA_NodeId referenceTypeId,
                              const UA_QualifiedName browseName,
                              const UA_DataTypeAttributes attr,
                              UA_NodeId *outNewNodeId) {
        return __UA_Client_addNode(client, UA_NODECLASS_DATATYPE, requestedNewNodeId,
                                   parentNodeId, referenceTypeId, browseName,
                                   UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
                                   &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],
                                   outNewNodeId);
    }
    
    static UA_INLINE UA_StatusCode
    UA_Client_addMethodNode(UA_Client *client, const UA_NodeId requestedNewNodeId,
                            const UA_NodeId parentNodeId,
                            const UA_NodeId referenceTypeId,
                            const UA_QualifiedName browseName,
                            const UA_MethodAttributes attr,
                            UA_NodeId *outNewNodeId) {
        return __UA_Client_addNode(client, UA_NODECLASS_METHOD, requestedNewNodeId,
                                   parentNodeId, referenceTypeId, browseName,
                                   UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
                                   &UA_TYPES[UA_TYPES_METHODATTRIBUTES], outNewNodeId);
    }

    其他高级功能

    /* Get the namespace-index of a namespace-URI
     *
     * @param client The UA_Client struct for this connection
     * @param namespaceUri The interested namespace URI
     * @param namespaceIndex The namespace index of the URI. The value is unchanged
     *        in case of an error
     * @return Indicates whether the operation succeeded or returns an error code */
    UA_StatusCode
    UA_Client_NamespaceGetIndex(UA_Client *client, UA_String *namespaceUri,
                                UA_UInt16 *namespaceIndex);
    
    #ifndef HAVE_NODEITER_CALLBACK
    #define HAVE_NODEITER_CALLBACK
    /* Iterate over all nodes referenced by parentNodeId by calling the callback
       function for each child node */
    typedef UA_StatusCode (*UA_NodeIteratorCallback)(UA_NodeId childId, UA_Boolean isInverse,
                                                     UA_NodeId referenceTypeId, void *handle);
    #endif
    
    UA_StatusCode
    UA_Client_forEachChildNodeCall(UA_Client *client, UA_NodeId parentNodeId,
                                   UA_NodeIteratorCallback callback, void *handle) ;

    订阅

    OPC UA中的订阅是异步的。也就是说,客户端向服务器发送多个PublishRequests。服务器返回带有通知的PublishResponses。但仅在生成通知时。客户端不等待响应并继续正常操作。

    请注意Subscriptions和MonitoredItems之间的区别。订阅用于报告回发通知。MonitoredItems用于生成通知。每个MonitoredItem都附加到一个订阅。订阅可以包含许多MonitoredItems。

    客户端在后台自动处理PublishResponses(带回调)并在传输过程中保留足够的PublishRequests。可以在同步服务呼叫期间或在同一服务呼叫期间接收PublishResponses UA_Client_runAsync

    /* Callbacks defined for Subscriptions */
    typedef void (*UA_Client_DeleteSubscriptionCallback)
        (UA_Client *client, UA_UInt32 subId, void *subContext);
    
    typedef void (*UA_Client_StatusChangeNotificationCallback)
        (UA_Client *client, UA_UInt32 subId, void *subContext,
         UA_StatusChangeNotification *notification);
    
    /* Provides default values for a new subscription.
     *
     * RequestedPublishingInterval:  500.0 [ms]
     * RequestedLifetimeCount: 10000
     * RequestedMaxKeepAliveCount: 10
     * MaxNotificationsPerPublish: 0 (unlimited)
     * PublishingEnabled: true
     * Priority: 0 */
    static UA_INLINE UA_CreateSubscriptionRequest
    UA_CreateSubscriptionRequest_default(void) {
        UA_CreateSubscriptionRequest request;
        UA_CreateSubscriptionRequest_init(&request);
    
        request.requestedPublishingInterval = 500.0;
        request.requestedLifetimeCount = 10000;
        request.requestedMaxKeepAliveCount = 10;
        request.maxNotificationsPerPublish = 0;
        request.publishingEnabled = true;
        request.priority = 0;
        return request;
    }
    
    UA_CreateSubscriptionResponse
    UA_Client_Subscriptions_create(UA_Client *client,
                                   const UA_CreateSubscriptionRequest request,
                                   void *subscriptionContext,
                                   UA_Client_StatusChangeNotificationCallback statusChangeCallback,
                                   UA_Client_DeleteSubscriptionCallback deleteCallback);
    
    UA_ModifySubscriptionResponse
    UA_Client_Subscriptions_modify(UA_Client *client, const UA_ModifySubscriptionRequest request);
    
    UA_DeleteSubscriptionsResponse
    UA_Client_Subscriptions_delete(UA_Client *client,
                                   const UA_DeleteSubscriptionsRequest request);
    
    /* Delete a single subscription */
    UA_StatusCode
    UA_Client_Subscriptions_deleteSingle(UA_Client *client, UA_UInt32 subscriptionId);
    
    static UA_INLINE UA_SetPublishingModeResponse
    UA_Client_Subscriptions_setPublishingMode(UA_Client *client,
                                              const UA_SetPublishingModeRequest request) {
        UA_SetPublishingModeResponse response;
        __UA_Client_Service(client, &request,
                            &UA_TYPES[UA_TYPES_SETPUBLISHINGMODEREQUEST], &response,
                            &UA_TYPES[UA_TYPES_SETPUBLISHINGMODERESPONSE]);
        return response;
    }

    MonitoredItems 

    事件的MonitoredItems表示EventNotifier属性。这表示服务器不监视属性的更改,而是转发来自该节点的事件通知。

    在创建MonitoredItem期间,服务器可能会返回已更改的已调整参数。使用UA_Client_MonitoredItem_getParameters来获得当前的参数。

    /* Provides default values for a new monitored item. */
    static UA_INLINE UA_MonitoredItemCreateRequest
    UA_MonitoredItemCreateRequest_default(UA_NodeId nodeId) {
        UA_MonitoredItemCreateRequest request;
        UA_MonitoredItemCreateRequest_init(&request);
        request.itemToMonitor.nodeId = nodeId;
        request.itemToMonitor.attributeId = UA_ATTRIBUTEID_VALUE;
        request.monitoringMode = UA_MONITORINGMODE_REPORTING;
        request.requestedParameters.samplingInterval = 250;
        request.requestedParameters.discardOldest = true;
        request.requestedParameters.queueSize = 1;
        return request;
    }
    
    /* Callback for the deletion of a MonitoredItem */
    typedef void (*UA_Client_DeleteMonitoredItemCallback)
        (UA_Client *client, UA_UInt32 subId, void *subContext,
         UA_UInt32 monId, void *monContext);
    
    /* Callback for DataChange notifications */
    typedef void (*UA_Client_DataChangeNotificationCallback)
        (UA_Client *client, UA_UInt32 subId, void *subContext,
         UA_UInt32 monId, void *monContext,
         UA_DataValue *value);
    
    /* Callback for Event notifications */
    typedef void (*UA_Client_EventNotificationCallback)
        (UA_Client *client, UA_UInt32 subId, void *subContext,
         UA_UInt32 monId, void *monContext,
         size_t nEventFields, UA_Variant *eventFields);
    
    /* Don't use to monitor the EventNotifier attribute */
    UA_CreateMonitoredItemsResponse
    UA_Client_MonitoredItems_createDataChanges(UA_Client *client,
                const UA_CreateMonitoredItemsRequest request, void **contexts,
                UA_Client_DataChangeNotificationCallback *callbacks,
                UA_Client_DeleteMonitoredItemCallback *deleteCallbacks);
    
    UA_MonitoredItemCreateResult
    UA_Client_MonitoredItems_createDataChange(UA_Client *client, UA_UInt32 subscriptionId,
              UA_TimestampsToReturn timestampsToReturn, const UA_MonitoredItemCreateRequest item,
              void *context, UA_Client_DataChangeNotificationCallback callback,
              UA_Client_DeleteMonitoredItemCallback deleteCallback);
    
    /* Monitor the EventNotifier attribute only */
    UA_CreateMonitoredItemsResponse
    UA_Client_MonitoredItems_createEvents(UA_Client *client,
                const UA_CreateMonitoredItemsRequest request, void **contexts,
                UA_Client_EventNotificationCallback *callbacks,
                UA_Client_DeleteMonitoredItemCallback *deleteCallback);
    
    UA_MonitoredItemCreateResult
    UA_Client_MonitoredItems_createEvent(UA_Client *client, UA_UInt32 subscriptionId,
              UA_TimestampsToReturn timestampsToReturn, const UA_MonitoredItemCreateRequest item,
              void *context, UA_Client_EventNotificationCallback callback,
              UA_Client_DeleteMonitoredItemCallback deleteCallback);
    
    UA_DeleteMonitoredItemsResponse
    UA_Client_MonitoredItems_delete(UA_Client *client, const UA_DeleteMonitoredItemsRequest);
    
    UA_StatusCode
    UA_Client_MonitoredItems_deleteSingle(UA_Client *client, UA_UInt32 subscriptionId, UA_UInt32 monitoredItemId);

    以下服务调用直接发送到服务器。MonitoredItem设置未存储在客户端中。

    static UA_INLINE UA_ModifyMonitoredItemsResponse
    UA_Client_MonitoredItems_modify(UA_Client *client,
                                    const UA_ModifyMonitoredItemsRequest request) {
        UA_ModifyMonitoredItemsResponse response;
        __UA_Client_Service(client,
                            &request, &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSREQUEST],
                            &response, &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSRESPONSE]);
        return response;
    }
    
    static UA_INLINE UA_SetMonitoringModeResponse
    UA_Client_MonitoredItems_setMonitoringMode(UA_Client *client,
                                               const UA_SetMonitoringModeRequest request) {
        UA_SetMonitoringModeResponse response;
        __UA_Client_Service(client,
                            &request, &UA_TYPES[UA_TYPES_SETMONITORINGMODEREQUEST],
                            &response, &UA_TYPES[UA_TYPES_SETMONITORINGMODERESPONSE]);
        return response;
    }
    
    static UA_INLINE UA_SetTriggeringResponse
    UA_Client_MonitoredItems_setTriggering(UA_Client *client,
                                           const UA_SetTriggeringRequest request) {
        UA_SetTriggeringResponse response;
        __UA_Client_Service(client,
                            &request, &UA_TYPES[UA_TYPES_SETTRIGGERINGREQUEST],
                            &response, &UA_TYPES[UA_TYPES_SETTRIGGERINGRESPONSE]);
        return response;
    }

    不推荐使用的

    保留以下API以实现向后兼容性。它将在以后的版本中删除。

    typedef struct {
        UA_Double requestedPublishingInterval;
        UA_UInt32 requestedLifetimeCount;
        UA_UInt32 requestedMaxKeepAliveCount;
        UA_UInt32 maxNotificationsPerPublish;
        UA_Boolean publishingEnabled;
        UA_Byte priority;
    } UA_SubscriptionSettings;
    
    extern const UA_SubscriptionSettings UA_SubscriptionSettings_default;
    
    UA_DEPRECATED UA_StatusCode
    UA_Client_Subscriptions_new(UA_Client *client, UA_SubscriptionSettings settings,
                                UA_UInt32 *newSubscriptionId);
    
    UA_DEPRECATED UA_StatusCode
    UA_Client_Subscriptions_remove(UA_Client *client, UA_UInt32 subscriptionId);
    
    /* Send a publish request and wait until a response to the request is processed.
     * Note that other publish responses may be processed in the background until
     * then. */
    UA_DEPRECATED UA_StatusCode
    UA_Client_Subscriptions_manuallySendPublishRequest(UA_Client *client);
    
    /* For monitoring DataChanges */
    typedef void (*UA_MonitoredItemHandlingFunction)(UA_Client *client, UA_UInt32 monId,
                                                     UA_DataValue *value, void *context);
    
    UA_DEPRECATED UA_StatusCode
    UA_Client_Subscriptions_addMonitoredItems(UA_Client *client, const UA_UInt32 subscriptionId,
                                              UA_MonitoredItemCreateRequest *items, size_t itemsSize,
                                              UA_MonitoredItemHandlingFunction *hfs,
                                              void **hfContexts, UA_StatusCode *itemResults,
                                              UA_UInt32 *newMonitoredItemIds);
    
    UA_DEPRECATED UA_StatusCode
    UA_Client_Subscriptions_addMonitoredItem(UA_Client *client, UA_UInt32 subscriptionId,
                                             UA_NodeId nodeId, UA_UInt32 attributeID,
                                             UA_MonitoredItemHandlingFunction hf,
                                             void *hfContext,
                                             UA_UInt32 *newMonitoredItemId,
                                             UA_Double samplingInterval);
    
    /* Monitored Events have different payloads from DataChanges. So they use a
     * different callback method signature. */
    typedef void (*UA_MonitoredEventHandlingFunction)(UA_Client *client,
                                                      const UA_UInt32 monId,
                                                      const size_t nEventFields,
                                                      const UA_Variant *eventFields,
                                                      void *context);
    
    UA_DEPRECATED UA_StatusCode
    UA_Client_Subscriptions_addMonitoredEvents(UA_Client *client, const UA_UInt32 subscriptionId,
                                               UA_MonitoredItemCreateRequest *items, size_t itemsSize,
                                               UA_MonitoredEventHandlingFunction *hfs,
                                               void **hfContexts, UA_StatusCode *itemResults,
                                               UA_UInt32 *newMonitoredItemIds);
    
    /* TODO for 0.4: attribute is fix for events. */
    UA_DEPRECATED UA_StatusCode
    UA_Client_Subscriptions_addMonitoredEvent(UA_Client *client, UA_UInt32 subscriptionId,
                                              const UA_NodeId nodeId, UA_UInt32 attributeID,
                                              const UA_SimpleAttributeOperand *selectClauses,
                                              size_t selectClausesSize,
                                              const UA_ContentFilterElement *whereClauses,
                                              size_t whereClausesSize,
                                              const UA_MonitoredEventHandlingFunction hf,
                                              void *hfContext, UA_UInt32 *newMonitoredItemId);
    
    UA_DEPRECATED UA_StatusCode
    UA_Client_Subscriptions_removeMonitoredItem(UA_Client *client, UA_UInt32 subscriptionId,
                                                UA_UInt32 monitoredItemId);
    
    UA_DEPRECATED UA_StatusCode
    UA_Client_Subscriptions_removeMonitoredItems(UA_Client *client, UA_UInt32 subscriptionId,
                                                 UA_UInt32 *monitoredItemIds, size_t itemsSize,
                                                 UA_StatusCode *itemResults);
    
    #endif

    标准定义常数

    本节包含OPC UA标准中定义的数字和字符串常量。

    属性

    OPC UA信息模型中的每个节点都包含取决于节点类型的属性。可能的属性如下:

    typedef enum {
        UA_ATTRIBUTEID_NODEID                  = 1,
        UA_ATTRIBUTEID_NODECLASS               = 2,
        UA_ATTRIBUTEID_BROWSENAME              = 3,
        UA_ATTRIBUTEID_DISPLAYNAME             = 4,
        UA_ATTRIBUTEID_DESCRIPTION             = 5,
        UA_ATTRIBUTEID_WRITEMASK               = 6,
        UA_ATTRIBUTEID_USERWRITEMASK           = 7,
        UA_ATTRIBUTEID_ISABSTRACT              = 8,
        UA_ATTRIBUTEID_SYMMETRIC               = 9,
        UA_ATTRIBUTEID_INVERSENAME             = 10,
        UA_ATTRIBUTEID_CONTAINSNOLOOPS         = 11,
        UA_ATTRIBUTEID_EVENTNOTIFIER           = 12,
        UA_ATTRIBUTEID_VALUE                   = 13,
        UA_ATTRIBUTEID_DATATYPE                = 14,
        UA_ATTRIBUTEID_VALUERANK               = 15,
        UA_ATTRIBUTEID_ARRAYDIMENSIONS         = 16,
        UA_ATTRIBUTEID_ACCESSLEVEL             = 17,
        UA_ATTRIBUTEID_USERACCESSLEVEL         = 18,
        UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL = 19,
        UA_ATTRIBUTEID_HISTORIZING             = 20,
        UA_ATTRIBUTEID_EXECUTABLE              = 21,
        UA_ATTRIBUTEID_USEREXECUTABLE          = 22
    } UA_AttributeId;

    访问级别掩码

    节点的访问级别由以下与常规访问级别进行AND运算的常量给出。

    #define UA_ACCESSLEVELMASK_READ           (0x01<<0)
    #define UA_ACCESSLEVELMASK_WRITE          (0x01<<1)
    #define UA_ACCESSLEVELMASK_HISTORYREAD    (0x01<<2)
    #define UA_ACCESSLEVELMASK_HISTORYWRITE   (0x01<<3)
    #define UA_ACCESSLEVELMASK_SEMANTICCHANGE (0x01<<4)
    #define UA_ACCESSLEVELMASK_STATUSWRITE    (0x01<<5)
    #define UA_ACCESSLEVELMASK_TIMESTAMPWRITE (0x01<<6)

    写掩码

    写掩码和用户写掩码由以下常量给出,这些常量对整个写掩码进行AND运算。第3部分:5.2.7表2

    #define UA_WRITEMASK_ACCESSLEVEL             (0x01<<0)
    #define UA_WRITEMASK_ARRRAYDIMENSIONS        (0x01<<1)
    #define UA_WRITEMASK_BROWSENAME              (0x01<<2)
    #define UA_WRITEMASK_CONTAINSNOLOOPS         (0x01<<3)
    #define UA_WRITEMASK_DATATYPE                (0x01<<4)
    #define UA_WRITEMASK_DESCRIPTION             (0x01<<5)
    #define UA_WRITEMASK_DISPLAYNAME             (0x01<<6)
    #define UA_WRITEMASK_EVENTNOTIFIER           (0x01<<7)
    #define UA_WRITEMASK_EXECUTABLE              (0x01<<8)
    #define UA_WRITEMASK_HISTORIZING             (0x01<<9)
    #define UA_WRITEMASK_INVERSENAME             (0x01<<10)
    #define UA_WRITEMASK_ISABSTRACT              (0x01<<11)
    #define UA_WRITEMASK_MINIMUMSAMPLINGINTERVAL (0x01<<12)
    #define UA_WRITEMASK_NODECLASS               (0x01<<13)
    #define UA_WRITEMASK_NODEID                  (0x01<<14)
    #define UA_WRITEMASK_SYMMETRIC               (0x01<<15)
    #define UA_WRITEMASK_USERACCESSLEVEL         (0x01<<16)
    #define UA_WRITEMASK_USEREXECUTABLE          (0x01<<17)
    #define UA_WRITEMASK_USERWRITEMASK           (0x01<<18)
    #define UA_WRITEMASK_VALUERANK               (0x01<<19)
    #define UA_WRITEMASK_WRITEMASK               (0x01<<20)
    #define UA_WRITEMASK_VALUEFORVARIABLETYPE    (0x01<<21)

    StatusCodes 

    StatusCodes广泛用于OPC UA协议和open62541 API。它们由StatusCode数据类型表示。以下定义是从Opc.Ua.StatusCodes.csvOPC UA标准提供的文件中自动生成的。

    #define UA_WRITEMASK_ACCESSLEVEL             (0x01<<0)
    #define UA_WRITEMASK_ARRRAYDIMENSIONS        (0x01<<1)
    #define UA_WRITEMASK_BROWSENAME              (0x01<<2)
    #define UA_WRITEMASK_CONTAINSNOLOOPS         (0x01<<3)
    #define UA_WRITEMASK_DATATYPE                (0x01<<4)
    #define UA_WRITEMASK_DESCRIPTION             (0x01<<5)
    #define UA_WRITEMASK_DISPLAYNAME             (0x01<<6)
    #define UA_WRITEMASK_EVENTNOTIFIER           (0x01<<7)
    #define UA_WRITEMASK_EXECUTABLE              (0x01<<8)
    #define UA_WRITEMASK_HISTORIZING             (0x01<<9)
    #define UA_WRITEMASK_INVERSENAME             (0x01<<10)
    #define UA_WRITEMASK_ISABSTRACT              (0x01<<11)
    #define UA_WRITEMASK_MINIMUMSAMPLINGINTERVAL (0x01<<12)
    #define UA_WRITEMASK_NODECLASS               (0x01<<13)
    #define UA_WRITEMASK_NODEID                  (0x01<<14)
    #define UA_WRITEMASK_SYMMETRIC               (0x01<<15)
    #define UA_WRITEMASK_USERACCESSLEVEL         (0x01<<16)
    #define UA_WRITEMASK_USEREXECUTABLE          (0x01<<17)
    #define UA_WRITEMASK_USERWRITEMASK           (0x01<<18)
    #define UA_WRITEMASK_VALUERANK               (0x01<<19)
    #define UA_WRITEMASK_WRITEMASK               (0x01<<20)
    #define UA_WRITEMASK_VALUEFORVARIABLETYPE    (0x01<<21)

    命名空间零NodeIds 

    命名空间零中标准定义节点的数字标识符。以下定义是从NodeIds.csvOPC UA标准提供的文件中自动生成的。

    #define UA_NS0ID_BOOLEAN 1 // DataType
    #define UA_NS0ID_SBYTE 2 // DataType
    #define UA_NS0ID_BYTE 3 // DataType
    #define UA_NS0ID_INT16 4 // DataType
    #define UA_NS0ID_UINT16 5 // DataType
    #define UA_NS0ID_INT32 6 // DataType
    #define UA_NS0ID_UINT32 7 // DataType
    #define UA_NS0ID_INT64 8 // DataType
    #define UA_NS0ID_UINT64 9 // DataType
    #define UA_NS0ID_FLOAT 10 // DataType
    #define UA_NS0ID_DOUBLE 11 // DataType
    #define UA_NS0ID_STRING 12 // DataType
    #define UA_NS0ID_DATETIME 13 // DataType
    #define UA_NS0ID_GUID 14 // DataType
    #define UA_NS0ID_BYTESTRING 15 // DataType
    #define UA_NS0ID_XMLELEMENT 16 // DataType
    #define UA_NS0ID_NODEID 17 // DataType
    #define UA_NS0ID_EXPANDEDNODEID 18 // DataType
    #define UA_NS0ID_STATUSCODE 19 // DataType
    #define UA_NS0ID_QUALIFIEDNAME 20 // DataType
    #define UA_NS0ID_LOCALIZEDTEXT 21 // DataType
    #define UA_NS0ID_STRUCTURE 22 // DataType
    #define UA_NS0ID_DATAVALUE 23 // DataType
    #define UA_NS0ID_BASEDATATYPE 24 // DataType
    #define UA_NS0ID_DIAGNOSTICINFO 25 // DataType
    #define UA_NS0ID_NUMBER 26 // DataType
    #define UA_NS0ID_INTEGER 27 // DataType
    #define UA_NS0ID_UINTEGER 28 // DataType
    #define UA_NS0ID_ENUMERATION 29 // DataType
    #define UA_NS0ID_IMAGE 30 // DataType
    #define UA_NS0ID_REFERENCES 31 // ReferenceType
    #define UA_NS0ID_NONHIERARCHICALREFERENCES 32 // ReferenceType
    #define UA_NS0ID_HIERARCHICALREFERENCES 33 // ReferenceType
    #define UA_NS0ID_HASCHILD 34 // ReferenceType
    #define UA_NS0ID_ORGANIZES 35 // ReferenceType
    #define UA_NS0ID_HASEVENTSOURCE 36 // ReferenceType
    #define UA_NS0ID_HASMODELLINGRULE 37 // ReferenceType
    #define UA_NS0ID_HASENCODING 38 // ReferenceType
    #define UA_NS0ID_HASDESCRIPTION 39 // ReferenceType
    #define UA_NS0ID_HASTYPEDEFINITION 40 // ReferenceType
    #define UA_NS0ID_GENERATESEVENT 41 // ReferenceType
    #define UA_NS0ID_AGGREGATES 44 // ReferenceType
    #define UA_NS0ID_HASSUBTYPE 45 // ReferenceType
    #define UA_NS0ID_HASPROPERTY 46 // ReferenceType
    #define UA_NS0ID_HASCOMPONENT 47 // ReferenceType
    #define UA_NS0ID_HASNOTIFIER 48 // ReferenceType
    #define UA_NS0ID_HASORDEREDCOMPONENT 49 // ReferenceType
    #define UA_NS0ID_FROMSTATE 51 // ReferenceType
    #define UA_NS0ID_TOSTATE 52 // ReferenceType
    #define UA_NS0ID_HASCAUSE 53 // ReferenceType
    #define UA_NS0ID_HASEFFECT 54 // ReferenceType
    #define UA_NS0ID_HASHISTORICALCONFIGURATION 56 // ReferenceType
    #define UA_NS0ID_BASEOBJECTTYPE 58 // ObjectType
    #define UA_NS0ID_FOLDERTYPE 61 // ObjectType
    #define UA_NS0ID_BASEVARIABLETYPE 62 // VariableType
    #define UA_NS0ID_BASEDATAVARIABLETYPE 63 // VariableType
    #define UA_NS0ID_PROPERTYTYPE 68 // VariableType
    #define UA_NS0ID_DATATYPEDESCRIPTIONTYPE 69 // VariableType
    #define UA_NS0ID_DATATYPEDICTIONARYTYPE 72 // VariableType
    #define UA_NS0ID_DATATYPESYSTEMTYPE 75 // ObjectType
    #define UA_NS0ID_DATATYPEENCODINGTYPE 76 // ObjectType
    #define UA_NS0ID_MODELLINGRULETYPE 77 // ObjectType
    #define UA_NS0ID_MODELLINGRULE_MANDATORY 78 // Object
    #define UA_NS0ID_MODELLINGRULE_MANDATORYSHARED 79 // Object
    #define UA_NS0ID_MODELLINGRULE_OPTIONAL 80 // Object
    #define UA_NS0ID_MODELLINGRULE_EXPOSESITSARRAY 83 // Object
    #define UA_NS0ID_ROOTFOLDER 84 // Object
    #define UA_NS0ID_OBJECTSFOLDER 85 // Object
    #define UA_NS0ID_TYPESFOLDER 86 // Object
    #define UA_NS0ID_VIEWSFOLDER 87 // Object
    #define UA_NS0ID_OBJECTTYPESFOLDER 88 // Object
    #define UA_NS0ID_VARIABLETYPESFOLDER 89 // Object
    #define UA_NS0ID_DATATYPESFOLDER 90 // Object
    #define UA_NS0ID_REFERENCETYPESFOLDER 91 // Object
    #define UA_NS0ID_XMLSCHEMA_TYPESYSTEM 92 // Object
    #define UA_NS0ID_OPCBINARYSCHEMA_TYPESYSTEM 93 // Object
    #define UA_NS0ID_MODELLINGRULE_MANDATORY_NAMINGRULE 112 // Variable
    #define UA_NS0ID_MODELLINGRULE_OPTIONAL_NAMINGRULE 113 // Variable
    #define UA_NS0ID_MODELLINGRULE_EXPOSESITSARRAY_NAMINGRULE 114 // Variable
    #define UA_NS0ID_MODELLINGRULE_MANDATORYSHARED_NAMINGRULE 116 // Variable
    #define UA_NS0ID_HASSUBSTATEMACHINE 117 // ReferenceType
    #define UA_NS0ID_NAMINGRULETYPE 120 // DataType
    #define UA_NS0ID_DECIMAL128 121 // DataType
    #define UA_NS0ID_IDTYPE 256 // DataType
    #define UA_NS0ID_NODECLASS 257 // DataType
    #define UA_NS0ID_NODE 258 // DataType
    #define UA_NS0ID_NODE_ENCODING_DEFAULTXML 259 // Object
    #define UA_NS0ID_NODE_ENCODING_DEFAULTBINARY 260 // Object
    #define UA_NS0ID_OBJECTNODE 261 // DataType
    #define UA_NS0ID_OBJECTNODE_ENCODING_DEFAULTXML 262 // Object
    #define UA_NS0ID_OBJECTNODE_ENCODING_DEFAULTBINARY 263 // Object
    #define UA_NS0ID_OBJECTTYPENODE 264 // DataType
    #define UA_NS0ID_OBJECTTYPENODE_ENCODING_DEFAULTXML 265 // Object
    #define UA_NS0ID_OBJECTTYPENODE_ENCODING_DEFAULTBINARY 266 // Object
    #define UA_NS0ID_VARIABLENODE 267 // DataType
    #define UA_NS0ID_VARIABLENODE_ENCODING_DEFAULTXML 268 // Object
    #define UA_NS0ID_VARIABLENODE_ENCODING_DEFAULTBINARY 269 // Object
    #define UA_NS0ID_VARIABLETYPENODE 270 // DataType
    #define UA_NS0ID_VARIABLETYPENODE_ENCODING_DEFAULTXML 271 // Object
    #define UA_NS0ID_VARIABLETYPENODE_ENCODING_DEFAULTBINARY 272 // Object
    #define UA_NS0ID_REFERENCETYPENODE 273 // DataType
    #define UA_NS0ID_REFERENCETYPENODE_ENCODING_DEFAULTXML 274 // Object
    #define UA_NS0ID_REFERENCETYPENODE_ENCODING_DEFAULTBINARY 275 // Object
    #define UA_NS0ID_METHODNODE 276 // DataType
    #define UA_NS0ID_METHODNODE_ENCODING_DEFAULTXML 277 // Object
    #define UA_NS0ID_METHODNODE_ENCODING_DEFAULTBINARY 278 // Object
    #define UA_NS0ID_VIEWNODE 279 // DataType
    #define UA_NS0ID_VIEWNODE_ENCODING_DEFAULTXML 280 // Object
    #define UA_NS0ID_VIEWNODE_ENCODING_DEFAULTBINARY 281 // Object
    #define UA_NS0ID_DATATYPENODE 282 // DataType
    #define UA_NS0ID_DATATYPENODE_ENCODING_DEFAULTXML 283 // Object
    #define UA_NS0ID_DATATYPENODE_ENCODING_DEFAULTBINARY 284 // Object
    #define UA_NS0ID_REFERENCENODE 285 // DataType
    #define UA_NS0ID_REFERENCENODE_ENCODING_DEFAULTXML 286 // Object
    #define UA_NS0ID_REFERENCENODE_ENCODING_DEFAULTBINARY 287 // Object
    #define UA_NS0ID_INTEGERID 288 // DataType
    #define UA_NS0ID_COUNTER 289 // DataType
    #define UA_NS0ID_DURATION 290 // DataType
    #define UA_NS0ID_NUMERICRANGE 291 // DataType
    #define UA_NS0ID_TIME 292 // DataType
    #define UA_NS0ID_DATE 293 // DataType
    #define UA_NS0ID_UTCTIME 294 // DataType
    #define UA_NS0ID_LOCALEID 295 // DataType
    #define UA_NS0ID_ARGUMENT 296 // DataType
    #define UA_NS0ID_ARGUMENT_ENCODING_DEFAULTXML 297 // Object
    #define UA_NS0ID_ARGUMENT_ENCODING_DEFAULTBINARY 298 // Object
    #define UA_NS0ID_STATUSRESULT 299 // DataType
    #define UA_NS0ID_STATUSRESULT_ENCODING_DEFAULTXML 300 // Object
    #define UA_NS0ID_STATUSRESULT_ENCODING_DEFAULTBINARY 301 // Object
    #define UA_NS0ID_MESSAGESECURITYMODE 302 // DataType
    #define UA_NS0ID_USERTOKENTYPE 303 // DataType
    #define UA_NS0ID_USERTOKENPOLICY 304 // DataType
    #define UA_NS0ID_USERTOKENPOLICY_ENCODING_DEFAULTXML 305 // Object
    #define UA_NS0ID_USERTOKENPOLICY_ENCODING_DEFAULTBINARY 306 // Object
    #define UA_NS0ID_APPLICATIONTYPE 307 // DataType
    #define UA_NS0ID_APPLICATIONDESCRIPTION 308 // DataType
    #define UA_NS0ID_APPLICATIONDESCRIPTION_ENCODING_DEFAULTXML 309 // Object
    #define UA_NS0ID_APPLICATIONDESCRIPTION_ENCODING_DEFAULTBINARY 310 // Object
    #define UA_NS0ID_APPLICATIONINSTANCECERTIFICATE 311 // DataType
    #define UA_NS0ID_ENDPOINTDESCRIPTION 312 // DataType
    #define UA_NS0ID_ENDPOINTDESCRIPTION_ENCODING_DEFAULTXML 313 // Object
    #define UA_NS0ID_ENDPOINTDESCRIPTION_ENCODING_DEFAULTBINARY 314 // Object
    #define UA_NS0ID_SECURITYTOKENREQUESTTYPE 315 // DataType
    #define UA_NS0ID_USERIDENTITYTOKEN 316 // DataType
    #define UA_NS0ID_USERIDENTITYTOKEN_ENCODING_DEFAULTXML 317 // Object
    #define UA_NS0ID_USERIDENTITYTOKEN_ENCODING_DEFAULTBINARY 318 // Object
    #define UA_NS0ID_ANONYMOUSIDENTITYTOKEN 319 // DataType
    #define UA_NS0ID_ANONYMOUSIDENTITYTOKEN_ENCODING_DEFAULTXML 320 // Object
    #define UA_NS0ID_ANONYMOUSIDENTITYTOKEN_ENCODING_DEFAULTBINARY 321 // Object
    #define UA_NS0ID_USERNAMEIDENTITYTOKEN 322 // DataType
    #define UA_NS0ID_USERNAMEIDENTITYTOKEN_ENCODING_DEFAULTXML 323 // Object
    #define UA_NS0ID_USERNAMEIDENTITYTOKEN_ENCODING_DEFAULTBINARY 324 // Object
    #define UA_NS0ID_X509IDENTITYTOKEN 325 // DataType
    #define UA_NS0ID_X509IDENTITYTOKEN_ENCODING_DEFAULTXML 326 // Object
    #define UA_NS0ID_X509IDENTITYTOKEN_ENCODING_DEFAULTBINARY 327 // Object
    #define UA_NS0ID_ENDPOINTCONFIGURATION 331 // DataType
    #define UA_NS0ID_ENDPOINTCONFIGURATION_ENCODING_DEFAULTXML 332 // Object
    #define UA_NS0ID_ENDPOINTCONFIGURATION_ENCODING_DEFAULTBINARY 333 // Object
    #define UA_NS0ID_BUILDINFO 338 // DataType
    #define UA_NS0ID_BUILDINFO_ENCODING_DEFAULTXML 339 // Object
    #define UA_NS0ID_BUILDINFO_ENCODING_DEFAULTBINARY 340 // Object
    #define UA_NS0ID_SIGNEDSOFTWARECERTIFICATE 344 // DataType
    #define UA_NS0ID_SIGNEDSOFTWARECERTIFICATE_ENCODING_DEFAULTXML 345 // Object
    #define UA_NS0ID_SIGNEDSOFTWARECERTIFICATE_ENCODING_DEFAULTBINARY 346 // Object
    #define UA_NS0ID_ATTRIBUTEWRITEMASK 347 // DataType
    #define UA_NS0ID_NODEATTRIBUTESMASK 348 // DataType
    #define UA_NS0ID_NODEATTRIBUTES 349 // DataType
    #define UA_NS0ID_NODEATTRIBUTES_ENCODING_DEFAULTXML 350 // Object
    #define UA_NS0ID_NODEATTRIBUTES_ENCODING_DEFAULTBINARY 351 // Object
    #define UA_NS0ID_OBJECTATTRIBUTES 352 // DataType
    #define UA_NS0ID_OBJECTATTRIBUTES_ENCODING_DEFAULTXML 353 // Object
    #define UA_NS0ID_OBJECTATTRIBUTES_ENCODING_DEFAULTBINARY 354 // Object
    #define UA_NS0ID_VARIABLEATTRIBUTES 355 // DataType
    #define UA_NS0ID_VARIABLEATTRIBUTES_ENCODING_DEFAULTXML 356 // Object
    #define UA_NS0ID_VARIABLEATTRIBUTES_ENCODING_DEFAULTBINARY 357 // Object
    #define UA_NS0ID_METHODATTRIBUTES 358 // DataType
    #define UA_NS0ID_METHODATTRIBUTES_ENCODING_DEFAULTXML 359 // Object
    #define UA_NS0ID_METHODATTRIBUTES_ENCODING_DEFAULTBINARY 360 // Object
    #define UA_NS0ID_OBJECTTYPEATTRIBUTES 361 // DataType
    #define UA_NS0ID_OBJECTTYPEATTRIBUTES_ENCODING_DEFAULTXML 362 // Object
    #define UA_NS0ID_OBJECTTYPEATTRIBUTES_ENCODING_DEFAULTBINARY 363 // Object
    #define UA_NS0ID_VARIABLETYPEATTRIBUTES 364 // DataType
    #define UA_NS0ID_VARIABLETYPEATTRIBUTES_ENCODING_DEFAULTXML 365 // Object
    #define UA_NS0ID_VARIABLETYPEATTRIBUTES_ENCODING_DEFAULTBINARY 366 // Object
    #define UA_NS0ID_REFERENCETYPEATTRIBUTES 367 // DataType
    #define UA_NS0ID_REFERENCETYPEATTRIBUTES_ENCODING_DEFAULTXML 368 // Object
    #define UA_NS0ID_REFERENCETYPEATTRIBUTES_ENCODING_DEFAULTBINARY 369 // Object
    #define UA_NS0ID_DATATYPEATTRIBUTES 370 // DataType
    #define UA_NS0ID_DATATYPEATTRIBUTES_ENCODING_DEFAULTXML 371 // Object
    #define UA_NS0ID_DATATYPEATTRIBUTES_ENCODING_DEFAULTBINARY 372 // Object
    #define UA_NS0ID_VIEWATTRIBUTES 373 // DataType
    #define UA_NS0ID_VIEWATTRIBUTES_ENCODING_DEFAULTXML 374 // Object
    #define UA_NS0ID_VIEWATTRIBUTES_ENCODING_DEFAULTBINARY 375 // Object
    #define UA_NS0ID_ADDNODESITEM 376 // DataType
    #define UA_NS0ID_ADDNODESITEM_ENCODING_DEFAULTXML 377 // Object
    #define UA_NS0ID_ADDNODESITEM_ENCODING_DEFAULTBINARY 378 // Object
    #define UA_NS0ID_ADDREFERENCESITEM 379 // DataType
    #define UA_NS0ID_ADDREFERENCESITEM_ENCODING_DEFAULTXML 380 // Object
    #define UA_NS0ID_ADDREFERENCESITEM_ENCODING_DEFAULTBINARY 381 // Object
    #define UA_NS0ID_DELETENODESITEM 382 // DataType
    #define UA_NS0ID_DELETENODESITEM_ENCODING_DEFAULTXML 383 // Object
    #define UA_NS0ID_DELETENODESITEM_ENCODING_DEFAULTBINARY 384 // Object
    #define UA_NS0ID_DELETEREFERENCESITEM 385 // DataType
    #define UA_NS0ID_DELETEREFERENCESITEM_ENCODING_DEFAULTXML 386 // Object
    #define UA_NS0ID_DELETEREFERENCESITEM_ENCODING_DEFAULTBINARY 387 // Object
    #define UA_NS0ID_SESSIONAUTHENTICATIONTOKEN 388 // DataType
    #define UA_NS0ID_REQUESTHEADER 389 // DataType
    #define UA_NS0ID_REQUESTHEADER_ENCODING_DEFAULTXML 390 // Object
    #define UA_NS0ID_REQUESTHEADER_ENCODING_DEFAULTBINARY 391 // Object
    #define UA_NS0ID_RESPONSEHEADER 392 // DataType
    #define UA_NS0ID_RESPONSEHEADER_ENCODING_DEFAULTXML 393 // Object
    #define UA_NS0ID_RESPONSEHEADER_ENCODING_DEFAULTBINARY 394 // Object
    #define UA_NS0ID_SERVICEFAULT 395 // DataType
    #define UA_NS0ID_SERVICEFAULT_ENCODING_DEFAULTXML 396 // Object
    #define UA_NS0ID_SERVICEFAULT_ENCODING_DEFAULTBINARY 397 // Object
    #define UA_NS0ID_FINDSERVERSREQUEST 420 // DataType
    #define UA_NS0ID_FINDSERVERSREQUEST_ENCODING_DEFAULTXML 421 // Object
    #define UA_NS0ID_FINDSERVERSREQUEST_ENCODING_DEFAULTBINARY 422 // Object
    #define UA_NS0ID_FINDSERVERSRESPONSE 423 // DataType
    #define UA_NS0ID_FINDSERVERSRESPONSE_ENCODING_DEFAULTXML 424 // Object
    #define UA_NS0ID_FINDSERVERSRESPONSE_ENCODING_DEFAULTBINARY 425 // Object
    #define UA_NS0ID_GETENDPOINTSREQUEST 426 // DataType
    #define UA_NS0ID_GETENDPOINTSREQUEST_ENCODING_DEFAULTXML 427 // Object
    #define UA_NS0ID_GETENDPOINTSREQUEST_ENCODING_DEFAULTBINARY 428 // Object
    #define UA_NS0ID_GETENDPOINTSRESPONSE 429 // DataType
    #define UA_NS0ID_GETENDPOINTSRESPONSE_ENCODING_DEFAULTXML 430 // Object
    #define UA_NS0ID_GETENDPOINTSRESPONSE_ENCODING_DEFAULTBINARY 431 // Object
    #define UA_NS0ID_REGISTEREDSERVER 432 // DataType
    #define UA_NS0ID_REGISTEREDSERVER_ENCODING_DEFAULTXML 433 // Object
    #define UA_NS0ID_REGISTEREDSERVER_ENCODING_DEFAULTBINARY 434 // Object
    #define UA_NS0ID_REGISTERSERVERREQUEST 435 // DataType
    #define UA_NS0ID_REGISTERSERVERREQUEST_ENCODING_DEFAULTXML 436 // Object
    #define UA_NS0ID_REGISTERSERVERREQUEST_ENCODING_DEFAULTBINARY 437 // Object
    #define UA_NS0ID_REGISTERSERVERRESPONSE 438 // DataType
    #define UA_NS0ID_REGISTERSERVERRESPONSE_ENCODING_DEFAULTXML 439 // Object
    #define UA_NS0ID_REGISTERSERVERRESPONSE_ENCODING_DEFAULTBINARY 440 // Object
    #define UA_NS0ID_CHANNELSECURITYTOKEN 441 // DataType
    #define UA_NS0ID_CHANNELSECURITYTOKEN_ENCODING_DEFAULTXML 442 // Object
    #define UA_NS0ID_CHANNELSECURITYTOKEN_ENCODING_DEFAULTBINARY 443 // Object
    #define UA_NS0ID_OPENSECURECHANNELREQUEST 444 // DataType
    #define UA_NS0ID_OPENSECURECHANNELREQUEST_ENCODING_DEFAULTXML 445 // Object
    #define UA_NS0ID_OPENSECURECHANNELREQUEST_ENCODING_DEFAULTBINARY 446 // Object
    #define UA_NS0ID_OPENSECURECHANNELRESPONSE 447 // DataType
    #define UA_NS0ID_OPENSECURECHANNELRESPONSE_ENCODING_DEFAULTXML 448 // Object
    #define UA_NS0ID_OPENSECURECHANNELRESPONSE_ENCODING_DEFAULTBINARY 449 // Object
    #define UA_NS0ID_CLOSESECURECHANNELREQUEST 450 // DataType
    #define UA_NS0ID_CLOSESECURECHANNELREQUEST_ENCODING_DEFAULTXML 451 // Object
    #define UA_NS0ID_CLOSESECURECHANNELREQUEST_ENCODING_DEFAULTBINARY 452 // Object
    #define UA_NS0ID_CLOSESECURECHANNELRESPONSE 453 // DataType
    #define UA_NS0ID_CLOSESECURECHANNELRESPONSE_ENCODING_DEFAULTXML 454 // Object
    #define UA_NS0ID_CLOSESECURECHANNELRESPONSE_ENCODING_DEFAULTBINARY 455 // Object
    #define UA_NS0ID_SIGNATUREDATA 456 // DataType
    #define UA_NS0ID_SIGNATUREDATA_ENCODING_DEFAULTXML 457 // Object
    #define UA_NS0ID_SIGNATUREDATA_ENCODING_DEFAULTBINARY 458 // Object
    #define UA_NS0ID_CREATESESSIONREQUEST 459 // DataType
    #define UA_NS0ID_CREATESESSIONREQUEST_ENCODING_DEFAULTXML 460 // Object
    #define UA_NS0ID_CREATESESSIONREQUEST_ENCODING_DEFAULTBINARY 461 // Object
    #define UA_NS0ID_CREATESESSIONRESPONSE 462 // DataType
    #define UA_NS0ID_CREATESESSIONRESPONSE_ENCODING_DEFAULTXML 463 // Object
    #define UA_NS0ID_CREATESESSIONRESPONSE_ENCODING_DEFAULTBINARY 464 // Object
    #define UA_NS0ID_ACTIVATESESSIONREQUEST 465 // DataType
    #define UA_NS0ID_ACTIVATESESSIONREQUEST_ENCODING_DEFAULTXML 466 // Object
    #define UA_NS0ID_ACTIVATESESSIONREQUEST_ENCODING_DEFAULTBINARY 467 // Object
    #define UA_NS0ID_ACTIVATESESSIONRESPONSE 468 // DataType
    #define UA_NS0ID_ACTIVATESESSIONRESPONSE_ENCODING_DEFAULTXML 469 // Object
    #define UA_NS0ID_ACTIVATESESSIONRESPONSE_ENCODING_DEFAULTBINARY 470 // Object
    #define UA_NS0ID_CLOSESESSIONREQUEST 471 // DataType
    #define UA_NS0ID_CLOSESESSIONREQUEST_ENCODING_DEFAULTXML 472 // Object
    #define UA_NS0ID_CLOSESESSIONREQUEST_ENCODING_DEFAULTBINARY 473 // Object
    #define UA_NS0ID_CLOSESESSIONRESPONSE 474 // DataType
    #define UA_NS0ID_CLOSESESSIONRESPONSE_ENCODING_DEFAULTXML 475 // Object
    #define UA_NS0ID_CLOSESESSIONRESPONSE_ENCODING_DEFAULTBINARY 476 // Object
    #define UA_NS0ID_CANCELREQUEST 477 // DataType
    #define UA_NS0ID_CANCELREQUEST_ENCODING_DEFAULTXML 478 // Object
    #define UA_NS0ID_CANCELREQUEST_ENCODING_DEFAULTBINARY 479 // Object
    #define UA_NS0ID_CANCELRESPONSE 480 // DataType
    #define UA_NS0ID_CANCELRESPONSE_ENCODING_DEFAULTXML 481 // Object
    #define UA_NS0ID_CANCELRESPONSE_ENCODING_DEFAULTBINARY 482 // Object
    #define UA_NS0ID_ADDNODESRESULT 483 // DataType
    #define UA_NS0ID_ADDNODESRESULT_ENCODING_DEFAULTXML 484 // Object
    #define UA_NS0ID_ADDNODESRESULT_ENCODING_DEFAULTBINARY 485 // Object
    #define UA_NS0ID_ADDNODESREQUEST 486 // DataType
    #define UA_NS0ID_ADDNODESREQUEST_ENCODING_DEFAULTXML 487 // Object
    #define UA_NS0ID_ADDNODESREQUEST_ENCODING_DEFAULTBINARY 488 // Object
    #define UA_NS0ID_ADDNODESRESPONSE 489 // DataType
    #define UA_NS0ID_ADDNODESRESPONSE_ENCODING_DEFAULTXML 490 // Object
    #define UA_NS0ID_ADDNODESRESPONSE_ENCODING_DEFAULTBINARY 491 // Object
    #define UA_NS0ID_ADDREFERENCESREQUEST 492 // DataType
    #define UA_NS0ID_ADDREFERENCESREQUEST_ENCODING_DEFAULTXML 493 // Object
    #define UA_NS0ID_ADDREFERENCESREQUEST_ENCODING_DEFAULTBINARY 494 // Object
    #define UA_NS0ID_ADDREFERENCESRESPONSE 495 // DataType
    #define UA_NS0ID_ADDREFERENCESRESPONSE_ENCODING_DEFAULTXML 496 // Object
    #define UA_NS0ID_ADDREFERENCESRESPONSE_ENCODING_DEFAULTBINARY 497 // Object
    #define UA_NS0ID_DELETENODESREQUEST 498 // DataType
    #define UA_NS0ID_DELETENODESREQUEST_ENCODING_DEFAULTXML 499 // Object
    #define UA_NS0ID_DELETENODESREQUEST_ENCODING_DEFAULTBINARY 500 // Object
    #define UA_NS0ID_DELETENODESRESPONSE 501 // DataType
    #define UA_NS0ID_DELETENODESRESPONSE_ENCODING_DEFAULTXML 502 // Object
    #define UA_NS0ID_DELETENODESRESPONSE_ENCODING_DEFAULTBINARY 503 // Object
    #define UA_NS0ID_DELETEREFERENCESREQUEST 504 // DataType
    #define UA_NS0ID_DELETEREFERENCESREQUEST_ENCODING_DEFAULTXML 505 // Object
    #define UA_NS0ID_DELETEREFERENCESREQUEST_ENCODING_DEFAULTBINARY 506 // Object
    #define UA_NS0ID_DELETEREFERENCESRESPONSE 507 // DataType
    #define UA_NS0ID_DELETEREFERENCESRESPONSE_ENCODING_DEFAULTXML 508 // Object
    #define UA_NS0ID_DELETEREFERENCESRESPONSE_ENCODING_DEFAULTBINARY 509 // Object
    #define UA_NS0ID_BROWSEDIRECTION 510 // DataType
    #define UA_NS0ID_VIEWDESCRIPTION 511 // DataType
    #define UA_NS0ID_VIEWDESCRIPTION_ENCODING_DEFAULTXML 512 // Object
    #define UA_NS0ID_VIEWDESCRIPTION_ENCODING_DEFAULTBINARY 513 // Object
    #define UA_NS0ID_BROWSEDESCRIPTION 514 // DataType
    #define UA_NS0ID_BROWSEDESCRIPTION_ENCODING_DEFAULTXML 515 // Object
    #define UA_NS0ID_BROWSEDESCRIPTION_ENCODING_DEFAULTBINARY 516 // Object
    #define UA_NS0ID_BROWSERESULTMASK 517 // DataType
    #define UA_NS0ID_REFERENCEDESCRIPTION 518 // DataType
    #define UA_NS0ID_REFERENCEDESCRIPTION_ENCODING_DEFAULTXML 519 // Object
    #define UA_NS0ID_REFERENCEDESCRIPTION_ENCODING_DEFAULTBINARY 520 // Object
    #define UA_NS0ID_CONTINUATIONPOINT 521 // DataType
    #define UA_NS0ID_BROWSERESULT 522 // DataType
    #define UA_NS0ID_BROWSERESULT_ENCODING_DEFAULTXML 523 // Object
    #define UA_NS0ID_BROWSERESULT_ENCODING_DEFAULTBINARY 524 // Object
    #define UA_NS0ID_BROWSEREQUEST 525 // DataType
    #define UA_NS0ID_BROWSEREQUEST_ENCODING_DEFAULTXML 526 // Object
    #define UA_NS0ID_BROWSEREQUEST_ENCODING_DEFAULTBINARY 527 // Object
    #define UA_NS0ID_BROWSERESPONSE 528 // DataType
    #define UA_NS0ID_BROWSERESPONSE_ENCODING_DEFAULTXML 529 // Object
    #define UA_NS0ID_BROWSERESPONSE_ENCODING_DEFAULTBINARY 530 // Object
    #define UA_NS0ID_BROWSENEXTREQUEST 531 // DataType
    #define UA_NS0ID_BROWSENEXTREQUEST_ENCODING_DEFAULTXML 532 // Object
    #define UA_NS0ID_BROWSENEXTREQUEST_ENCODING_DEFAULTBINARY 533 // Object
    #define UA_NS0ID_BROWSENEXTRESPONSE 534 // DataType
    #define UA_NS0ID_BROWSENEXTRESPONSE_ENCODING_DEFAULTXML 535 // Object
    #define UA_NS0ID_BROWSENEXTRESPONSE_ENCODING_DEFAULTBINARY 536 // Object
    #define UA_NS0ID_RELATIVEPATHELEMENT 537 // DataType
    #define UA_NS0ID_RELATIVEPATHELEMENT_ENCODING_DEFAULTXML 538 // Object
    #define UA_NS0ID_RELATIVEPATHELEMENT_ENCODING_DEFAULTBINARY 539 // Object
    #define UA_NS0ID_RELATIVEPATH 540 // DataType
    #define UA_NS0ID_RELATIVEPATH_ENCODING_DEFAULTXML 541 // Object
    #define UA_NS0ID_RELATIVEPATH_ENCODING_DEFAULTBINARY 542 // Object
    #define UA_NS0ID_BROWSEPATH 543 // DataType
    #define UA_NS0ID_BROWSEPATH_ENCODING_DEFAULTXML 544 // Object
    #define UA_NS0ID_BROWSEPATH_ENCODING_DEFAULTBINARY 545 // Object
    #define UA_NS0ID_BROWSEPATHTARGET 546 // DataType
    #define UA_NS0ID_BROWSEPATHTARGET_ENCODING_DEFAULTXML 547 // Object
    #define UA_NS0ID_BROWSEPATHTARGET_ENCODING_DEFAULTBINARY 548 // Object
    #define UA_NS0ID_BROWSEPATHRESULT 549 // DataType
    #define UA_NS0ID_BROWSEPATHRESULT_ENCODING_DEFAULTXML 550 // Object
    #define UA_NS0ID_BROWSEPATHRESULT_ENCODING_DEFAULTBINARY 551 // Object
    #define UA_NS0ID_TRANSLATEBROWSEPATHSTONODEIDSREQUEST 552 // DataType
    #define UA_NS0ID_TRANSLATEBROWSEPATHSTONODEIDSREQUEST_ENCODING_DEFAULTXML 553 // Object
    #define UA_NS0ID_TRANSLATEBROWSEPATHSTONODEIDSREQUEST_ENCODING_DEFAULTBINARY 554 // Object
    #define UA_NS0ID_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE 555 // DataType
    #define UA_NS0ID_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE_ENCODING_DEFAULTXML 556 // Object
    #define UA_NS0ID_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE_ENCODING_DEFAULTBINARY 557 // Object
    #define UA_NS0ID_REGISTERNODESREQUEST 558 // DataType
    #define UA_NS0ID_REGISTERNODESREQUEST_ENCODING_DEFAULTXML 559 // Object
    #define UA_NS0ID_REGISTERNODESREQUEST_ENCODING_DEFAULTBINARY 560 // Object
    #define UA_NS0ID_REGISTERNODESRESPONSE 561 // DataType
    #define UA_NS0ID_REGISTERNODESRESPONSE_ENCODING_DEFAULTXML 562 // Object
    #define UA_NS0ID_REGISTERNODESRESPONSE_ENCODING_DEFAULTBINARY 563 // Object
    #define UA_NS0ID_UNREGISTERNODESREQUEST 564 // DataType
    #define UA_NS0ID_UNREGISTERNODESREQUEST_ENCODING_DEFAULTXML 565 // Object
    #define UA_NS0ID_UNREGISTERNODESREQUEST_ENCODING_DEFAULTBINARY 566 // Object
    #define UA_NS0ID_UNREGISTERNODESRESPONSE 567 // DataType
    #define UA_NS0ID_UNREGISTERNODESRESPONSE_ENCODING_DEFAULTXML 568 // Object
    #define UA_NS0ID_UNREGISTERNODESRESPONSE_ENCODING_DEFAULTBINARY 569 // Object
    #define UA_NS0ID_QUERYDATADESCRIPTION 570 // DataType
    #define UA_NS0ID_QUERYDATADESCRIPTION_ENCODING_DEFAULTXML 571 // Object
    #define UA_NS0ID_QUERYDATADESCRIPTION_ENCODING_DEFAULTBINARY 572 // Object
    #define UA_NS0ID_NODETYPEDESCRIPTION 573 // DataType
    #define UA_NS0ID_NODETYPEDESCRIPTION_ENCODING_DEFAULTXML 574 // Object
    #define UA_NS0ID_NODETYPEDESCRIPTION_ENCODING_DEFAULTBINARY 575 // Object
    #define UA_NS0ID_FILTEROPERATOR 576 // DataType
    #define UA_NS0ID_QUERYDATASET 577 // DataType
    #define UA_NS0ID_QUERYDATASET_ENCODING_DEFAULTXML 578 // Object
    #define UA_NS0ID_QUERYDATASET_ENCODING_DEFAULTBINARY 579 // Object
    #define UA_NS0ID_NODEREFERENCE 580 // DataType
    #define UA_NS0ID_NODEREFERENCE_ENCODING_DEFAULTXML 581 // Object
    #define UA_NS0ID_NODEREFERENCE_ENCODING_DEFAULTBINARY 582 // Object
    #define UA_NS0ID_CONTENTFILTERELEMENT 583 // DataType
    #define UA_NS0ID_CONTENTFILTERELEMENT_ENCODING_DEFAULTXML 584 // Object
    #define UA_NS0ID_CONTENTFILTERELEMENT_ENCODING_DEFAULTBINARY 585 // Object
    #define UA_NS0ID_CONTENTFILTER 586 // DataType
    #define UA_NS0ID_CONTENTFILTER_ENCODING_DEFAULTXML 587 // Object
    #define UA_NS0ID_CONTENTFILTER_ENCODING_DEFAULTBINARY 588 // Object
    #define UA_NS0ID_FILTEROPERAND 589 // DataType
    #define UA_NS0ID_FILTEROPERAND_ENCODING_DEFAULTXML 590 // Object
    #define UA_NS0ID_FILTEROPERAND_ENCODING_DEFAULTBINARY 591 // Object
    #define UA_NS0ID_ELEMENTOPERAND 592 // DataType
    #define UA_NS0ID_ELEMENTOPERAND_ENCODING_DEFAULTXML 593 // Object
    #define UA_NS0ID_ELEMENTOPERAND_ENCODING_DEFAULTBINARY 594 // Object
    #define UA_NS0ID_LITERALOPERAND 595 // DataType
    #define UA_NS0ID_LITERALOPERAND_ENCODING_DEFAULTXML 596 // Object
    #define UA_NS0ID_LITERALOPERAND_ENCODING_DEFAULTBINARY 597 // Object
    #define UA_NS0ID_ATTRIBUTEOPERAND 598 // DataType
    #define UA_NS0ID_ATTRIBUTEOPERAND_ENCODING_DEFAULTXML 599 // Object
    #define UA_NS0ID_ATTRIBUTEOPERAND_ENCODING_DEFAULTBINARY 600 // Object
    #define UA_NS0ID_SIMPLEATTRIBUTEOPERAND 601 // DataType
    #define UA_NS0ID_SIMPLEATTRIBUTEOPERAND_ENCODING_DEFAULTXML 602 // Object
    #define UA_NS0ID_SIMPLEATTRIBUTEOPERAND_ENCODING_DEFAULTBINARY 603 // Object
    #define UA_NS0ID_CONTENTFILTERELEMENTRESULT 604 // DataType
    #define UA_NS0ID_CONTENTFILTERELEMENTRESULT_ENCODING_DEFAULTXML 605 // Object
    #define UA_NS0ID_CONTENTFILTERELEMENTRESULT_ENCODING_DEFAULTBINARY 606 // Object
    #define UA_NS0ID_CONTENTFILTERRESULT 607 // DataType
    #define UA_NS0ID_CONTENTFILTERRESULT_ENCODING_DEFAULTXML 608 // Object
    #define UA_NS0ID_CONTENTFILTERRESULT_ENCODING_DEFAULTBINARY 609 // Object
    #define UA_NS0ID_PARSINGRESULT 610 // DataType
    #define UA_NS0ID_PARSINGRESULT_ENCODING_DEFAULTXML 611 // Object
    #define UA_NS0ID_PARSINGRESULT_ENCODING_DEFAULTBINARY 612 // Object
    #define UA_NS0ID_QUERYFIRSTREQUEST 613 // DataType
    #define UA_NS0ID_QUERYFIRSTREQUEST_ENCODING_DEFAULTXML 614 // Object
    #define UA_NS0ID_QUERYFIRSTREQUEST_ENCODING_DEFAULTBINARY 615 // Object
    #define UA_NS0ID_QUERYFIRSTRESPONSE 616 // DataType
    #define UA_NS0ID_QUERYFIRSTRESPONSE_ENCODING_DEFAULTXML 617 // Object
    #define UA_NS0ID_QUERYFIRSTRESPONSE_ENCODING_DEFAULTBINARY 618 // Object
    #define UA_NS0ID_QUERYNEXTREQUEST 619 // DataType
    #define UA_NS0ID_QUERYNEXTREQUEST_ENCODING_DEFAULTXML 620 // Object
    #define UA_NS0ID_QUERYNEXTREQUEST_ENCODING_DEFAULTBINARY 621 // Object
    #define UA_NS0ID_QUERYNEXTRESPONSE 622 // DataType
    #define UA_NS0ID_QUERYNEXTRESPONSE_ENCODING_DEFAULTXML 623 // Object
    #define UA_NS0ID_QUERYNEXTRESPONSE_ENCODING_DEFAULTBINARY 624 // Object
    #define UA_NS0ID_TIMESTAMPSTORETURN 625 // DataType
    #define UA_NS0ID_READVALUEID 626 // DataType
    #define UA_NS0ID_READVALUEID_ENCODING_DEFAULTXML 627 // Object
    #define UA_NS0ID_READVALUEID_ENCODING_DEFAULTBINARY 628 // Object
    #define UA_NS0ID_READREQUEST 629 // DataType
    #define UA_NS0ID_READREQUEST_ENCODING_DEFAULTXML 630 // Object
    #define UA_NS0ID_READREQUEST_ENCODING_DEFAULTBINARY 631 // Object
    #define UA_NS0ID_READRESPONSE 632 // DataType
    #define UA_NS0ID_READRESPONSE_ENCODING_DEFAULTXML 633 // Object
    #define UA_NS0ID_READRESPONSE_ENCODING_DEFAULTBINARY 634 // Object
    #define UA_NS0ID_HISTORYREADVALUEID 635 // DataType
    #define UA_NS0ID_HISTORYREADVALUEID_ENCODING_DEFAULTXML 636 // Object
    #define UA_NS0ID_HISTORYREADVALUEID_ENCODING_DEFAULTBINARY 637 // Object
    #define UA_NS0ID_HISTORYREADRESULT 638 // DataType
    #define UA_NS0ID_HISTORYREADRESULT_ENCODING_DEFAULTXML 639 // Object
    #define UA_NS0ID_HISTORYREADRESULT_ENCODING_DEFAULTBINARY 640 // Object
    #define UA_NS0ID_HISTORYREADDETAILS 641 // DataType
    #define UA_NS0ID_HISTORYREADDETAILS_ENCODING_DEFAULTXML 642 // Object
    #define UA_NS0ID_HISTORYREADDETAILS_ENCODING_DEFAULTBINARY 643 // Object
    #define UA_NS0ID_READEVENTDETAILS 644 // DataType
    #define UA_NS0ID_READEVENTDETAILS_ENCODING_DEFAULTXML 645 // Object
    #define UA_NS0ID_READEVENTDETAILS_ENCODING_DEFAULTBINARY 646 // Object
    #define UA_NS0ID_READRAWMODIFIEDDETAILS 647 // DataType
    #define UA_NS0ID_READRAWMODIFIEDDETAILS_ENCODING_DEFAULTXML 648 // Object
    #define UA_NS0ID_READRAWMODIFIEDDETAILS_ENCODING_DEFAULTBINARY 649 // Object
    #define UA_NS0ID_READPROCESSEDDETAILS 650 // DataType
    #define UA_NS0ID_READPROCESSEDDETAILS_ENCODING_DEFAULTXML 651 // Object
    #define UA_NS0ID_READPROCESSEDDETAILS_ENCODING_DEFAULTBINARY 652 // Object
    #define UA_NS0ID_READATTIMEDETAILS 653 // DataType
    #define UA_NS0ID_READATTIMEDETAILS_ENCODING_DEFAULTXML 654 // Object
    #define UA_NS0ID_READATTIMEDETAILS_ENCODING_DEFAULTBINARY 655 // Object
    #define UA_NS0ID_HISTORYDATA 656 // DataType
    #define UA_NS0ID_HISTORYDATA_ENCODING_DEFAULTXML 657 // Object
    #define UA_NS0ID_HISTORYDATA_ENCODING_DEFAULTBINARY 658 // Object
    #define UA_NS0ID_HISTORYEVENT 659 // DataType
    #define UA_NS0ID_HISTORYEVENT_ENCODING_DEFAULTXML 660 // Object
    #define UA_NS0ID_HISTORYEVENT_ENCODING_DEFAULTBINARY 661 // Object
    #define UA_NS0ID_HISTORYREADREQUEST 662 // DataType
    #define UA_NS0ID_HISTORYREADREQUEST_ENCODING_DEFAULTXML 663 // Object
    #define UA_NS0ID_HISTORYREADREQUEST_ENCODING_DEFAULTBINARY 664 // Object
    #define UA_NS0ID_HISTORYREADRESPONSE 665 // DataType
    #define UA_NS0ID_HISTORYREADRESPONSE_ENCODING_DEFAULTXML 666 // Object
    #define UA_NS0ID_HISTORYREADRESPONSE_ENCODING_DEFAULTBINARY 667 // Object
    #define UA_NS0ID_WRITEVALUE 668 // DataType
    #define UA_NS0ID_WRITEVALUE_ENCODING_DEFAULTXML 669 // Object
    #define UA_NS0ID_WRITEVALUE_ENCODING_DEFAULTBINARY 670 // Object
    #define UA_NS0ID_WRITEREQUEST 671 // DataType
    #define UA_NS0ID_WRITEREQUEST_ENCODING_DEFAULTXML 672 // Object
    #define UA_NS0ID_WRITEREQUEST_ENCODING_DEFAULTBINARY 673 // Object
    #define UA_NS0ID_WRITERESPONSE 674 // DataType
    #define UA_NS0ID_WRITERESPONSE_ENCODING_DEFAULTXML 675 // Object
    #define UA_NS0ID_WRITERESPONSE_ENCODING_DEFAULTBINARY 676 // Object
    #define UA_NS0ID_HISTORYUPDATEDETAILS 677 // DataType
    #define UA_NS0ID_HISTORYUPDATEDETAILS_ENCODING_DEFAULTXML 678 // Object
    #define UA_NS0ID_HISTORYUPDATEDETAILS_ENCODING_DEFAULTBINARY 679 // Object
    #define UA_NS0ID_UPDATEDATADETAILS 680 // DataType
    #define UA_NS0ID_UPDATEDATADETAILS_ENCODING_DEFAULTXML 681 // Object
    #define UA_NS0ID_UPDATEDATADETAILS_ENCODING_DEFAULTBINARY 682 // Object
    #define UA_NS0ID_UPDATEEVENTDETAILS 683 // DataType
    #define UA_NS0ID_UPDATEEVENTDETAILS_ENCODING_DEFAULTXML 684 // Object
    #define UA_NS0ID_UPDATEEVENTDETAILS_ENCODING_DEFAULTBINARY 685 // Object
    #define UA_NS0ID_DELETERAWMODIFIEDDETAILS 686 // DataType
    #define UA_NS0ID_DELETERAWMODIFIEDDETAILS_ENCODING_DEFAULTXML 687 // Object
    #define UA_NS0ID_DELETERAWMODIFIEDDETAILS_ENCODING_DEFAULTBINARY 688 // Object
    #define UA_NS0ID_DELETEATTIMEDETAILS 689 // DataType
    #define UA_NS0ID_DELETEATTIMEDETAILS_ENCODING_DEFAULTXML 690 // Object
    #define UA_NS0ID_DELETEATTIMEDETAILS_ENCODING_DEFAULTBINARY 691 // Object
    #define UA_NS0ID_DELETEEVENTDETAILS 692 // DataType
    #define UA_NS0ID_DELETEEVENTDETAILS_ENCODING_DEFAULTXML 693 // Object
    #define UA_NS0ID_DELETEEVENTDETAILS_ENCODING_DEFAULTBINARY 694 // Object
    #define UA_NS0ID_HISTORYUPDATERESULT 695 // DataType
    #define UA_NS0ID_HISTORYUPDATERESULT_ENCODING_DEFAULTXML 696 // Object
    #define UA_NS0ID_HISTORYUPDATERESULT_ENCODING_DEFAULTBINARY 697 // Object
    #define UA_NS0ID_HISTORYUPDATEREQUEST 698 // DataType
    #define UA_NS0ID_HISTORYUPDATEREQUEST_ENCODING_DEFAULTXML 699 // Object
    #define UA_NS0ID_HISTORYUPDATEREQUEST_ENCODING_DEFAULTBINARY 700 // Object
    #define UA_NS0ID_HISTORYUPDATERESPONSE 701 // DataType
    #define UA_NS0ID_HISTORYUPDATERESPONSE_ENCODING_DEFAULTXML 702 // Object
    #define UA_NS0ID_HISTORYUPDATERESPONSE_ENCODING_DEFAULTBINARY 703 // Object
    #define UA_NS0ID_CALLMETHODREQUEST 704 // DataType
    #define UA_NS0ID_CALLMETHODREQUEST_ENCODING_DEFAULTXML 705 // Object
    #define UA_NS0ID_CALLMETHODREQUEST_ENCODING_DEFAULTBINARY 706 // Object
    #define UA_NS0ID_CALLMETHODRESULT 707 // DataType
    #define UA_NS0ID_CALLMETHODRESULT_ENCODING_DEFAULTXML 708 // Object
    #define UA_NS0ID_CALLMETHODRESULT_ENCODING_DEFAULTBINARY 709 // Object
    #define UA_NS0ID_CALLREQUEST 710 // DataType
    #define UA_NS0ID_CALLREQUEST_ENCODING_DEFAULTXML 711 // Object
    #define UA_NS0ID_CALLREQUEST_ENCODING_DEFAULTBINARY 712 // Object
    #define UA_NS0ID_CALLRESPONSE 713 // DataType
    #define UA_NS0ID_CALLRESPONSE_ENCODING_DEFAULTXML 714 // Object
    #define UA_NS0ID_CALLRESPONSE_ENCODING_DEFAULTBINARY 715 // Object
    #define UA_NS0ID_MONITORINGMODE 716 // DataType
    #define UA_NS0ID_DATACHANGETRIGGER 717 // DataType
    #define UA_NS0ID_DEADBANDTYPE 718 // DataType
    #define UA_NS0ID_MONITORINGFILTER 719 // DataType
    #define UA_NS0ID_MONITORINGFILTER_ENCODING_DEFAULTXML 720 // Object
    #define UA_NS0ID_MONITORINGFILTER_ENCODING_DEFAULTBINARY 721 // Object
    #define UA_NS0ID_DATACHANGEFILTER 722 // DataType
    #define UA_NS0ID_DATACHANGEFILTER_ENCODING_DEFAULTXML 723 // Object
    #define UA_NS0ID_DATACHANGEFILTER_ENCODING_DEFAULTBINARY 724 // Object
    #define UA_NS0ID_EVENTFILTER 725 // DataType
    #define UA_NS0ID_EVENTFILTER_ENCODING_DEFAULTXML 726 // Object
    #define UA_NS0ID_EVENTFILTER_ENCODING_DEFAULTBINARY 727 // Object
    #define UA_NS0ID_AGGREGATEFILTER 728 // DataType
    #define UA_NS0ID_AGGREGATEFILTER_ENCODING_DEFAULTXML 729 // Object
    #define UA_NS0ID_AGGREGATEFILTER_ENCODING_DEFAULTBINARY 730 // Object
    #define UA_NS0ID_MONITORINGFILTERRESULT 731 // DataType
    #define UA_NS0ID_MONITORINGFILTERRESULT_ENCODING_DEFAULTXML 732 // Object
    #define UA_NS0ID_MONITORINGFILTERRESULT_ENCODING_DEFAULTBINARY 733 // Object
    #define UA_NS0ID_EVENTFILTERRESULT 734 // DataType
    #define UA_NS0ID_EVENTFILTERRESULT_ENCODING_DEFAULTXML 735 // Object
    #define UA_NS0ID_EVENTFILTERRESULT_ENCODING_DEFAULTBINARY 736 // Object
    #define UA_NS0ID_AGGREGATEFILTERRESULT 737 // DataType
    #define UA_NS0ID_AGGREGATEFILTERRESULT_ENCODING_DEFAULTXML 738 // Object
    #define UA_NS0ID_AGGREGATEFILTERRESULT_ENCODING_DEFAULTBINARY 739 // Object
    #define UA_NS0ID_MONITORINGPARAMETERS 740 // DataType
    #define UA_NS0ID_MONITORINGPARAMETERS_ENCODING_DEFAULTXML 741 // Object
    #define UA_NS0ID_MONITORINGPARAMETERS_ENCODING_DEFAULTBINARY 742 // Object
    #define UA_NS0ID_MONITOREDITEMCREATEREQUEST 743 // DataType
    #define UA_NS0ID_MONITOREDITEMCREATEREQUEST_ENCODING_DEFAULTXML 744 // Object
    #define UA_NS0ID_MONITOREDITEMCREATEREQUEST_ENCODING_DEFAULTBINARY 745 // Object
    #define UA_NS0ID_MONITOREDITEMCREATERESULT 746 // DataType
    #define UA_NS0ID_MONITOREDITEMCREATERESULT_ENCODING_DEFAULTXML 747 // Object
    #define UA_NS0ID_MONITOREDITEMCREATERESULT_ENCODING_DEFAULTBINARY 748 // Object
    #define UA_NS0ID_CREATEMONITOREDITEMSREQUEST 749 // DataType
    #define UA_NS0ID_CREATEMONITOREDITEMSREQUEST_ENCODING_DEFAULTXML 750 // Object
    #define UA_NS0ID_CREATEMONITOREDITEMSREQUEST_ENCODING_DEFAULTBINARY 751 // Object
    #define UA_NS0ID_CREATEMONITOREDITEMSRESPONSE 752 // DataType
    #define UA_NS0ID_CREATEMONITOREDITEMSRESPONSE_ENCODING_DEFAULTXML 753 // Object
    #define UA_NS0ID_CREATEMONITOREDITEMSRESPONSE_ENCODING_DEFAULTBINARY 754 // Object
    #define UA_NS0ID_MONITOREDITEMMODIFYREQUEST 755 // DataType
    #define UA_NS0ID_MONITOREDITEMMODIFYREQUEST_ENCODING_DEFAULTXML 756 // Object
    #define UA_NS0ID_MONITOREDITEMMODIFYREQUEST_ENCODING_DEFAULTBINARY 757 // Object
    #define UA_NS0ID_MONITOREDITEMMODIFYRESULT 758 // DataType
    #define UA_NS0ID_MONITOREDITEMMODIFYRESULT_ENCODING_DEFAULTXML 759 // Object
    #define UA_NS0ID_MONITOREDITEMMODIFYRESULT_ENCODING_DEFAULTBINARY 760 // Object
    #define UA_NS0ID_MODIFYMONITOREDITEMSREQUEST 761 // DataType
    #define UA_NS0ID_MODIFYMONITOREDITEMSREQUEST_ENCODING_DEFAULTXML 762 // Object
    #define UA_NS0ID_MODIFYMONITOREDITEMSREQUEST_ENCODING_DEFAULTBINARY 763 // Object
    #define UA_NS0ID_MODIFYMONITOREDITEMSRESPONSE 764 // DataType
    #define UA_NS0ID_MODIFYMONITOREDITEMSRESPONSE_ENCODING_DEFAULTXML 765 // Object
    #define UA_NS0ID_MODIFYMONITOREDITEMSRESPONSE_ENCODING_DEFAULTBINARY 766 // Object
    #define UA_NS0ID_SETMONITORINGMODEREQUEST 767 // DataType
    #define UA_NS0ID_SETMONITORINGMODEREQUEST_ENCODING_DEFAULTXML 768 // Object
    #define UA_NS0ID_SETMONITORINGMODEREQUEST_ENCODING_DEFAULTBINARY 769 // Object
    #define UA_NS0ID_SETMONITORINGMODERESPONSE 770 // DataType
    #define UA_NS0ID_SETMONITORINGMODERESPONSE_ENCODING_DEFAULTXML 771 // Object
    #define UA_NS0ID_SETMONITORINGMODERESPONSE_ENCODING_DEFAULTBINARY 772 // Object
    #define UA_NS0ID_SETTRIGGERINGREQUEST 773 // DataType
    #define UA_NS0ID_SETTRIGGERINGREQUEST_ENCODING_DEFAULTXML 774 // Object
    #define UA_NS0ID_SETTRIGGERINGREQUEST_ENCODING_DEFAULTBINARY 775 // Object
    #define UA_NS0ID_SETTRIGGERINGRESPONSE 776 // DataType
    #define UA_NS0ID_SETTRIGGERINGRESPONSE_ENCODING_DEFAULTXML 777 // Object
    #define UA_NS0ID_SETTRIGGERINGRESPONSE_ENCODING_DEFAULTBINARY 778 // Object
    #define UA_NS0ID_DELETEMONITOREDITEMSREQUEST 779 // DataType
    #define UA_NS0ID_DELETEMONITOREDITEMSREQUEST_ENCODING_DEFAULTXML 780 // Object
    #define UA_NS0ID_DELETEMONITOREDITEMSREQUEST_ENCODING_DEFAULTBINARY 781 // Object
    #define UA_NS0ID_DELETEMONITOREDITEMSRESPONSE 782 // DataType
    #define UA_NS0ID_DELETEMONITOREDITEMSRESPONSE_ENCODING_DEFAULTXML 783 // Object
    #define UA_NS0ID_DELETEMONITOREDITEMSRESPONSE_ENCODING_DEFAULTBINARY 784 // Object
    #define UA_NS0ID_CREATESUBSCRIPTIONREQUEST 785 // DataType
    #define UA_NS0ID_CREATESUBSCRIPTIONREQUEST_ENCODING_DEFAULTXML 786 // Object
    #define UA_NS0ID_CREATESUBSCRIPTIONREQUEST_ENCODING_DEFAULTBINARY 787 // Object
    #define UA_NS0ID_CREATESUBSCRIPTIONRESPONSE 788 // DataType
    #define UA_NS0ID_CREATESUBSCRIPTIONRESPONSE_ENCODING_DEFAULTXML 789 // Object
    #define UA_NS0ID_CREATESUBSCRIPTIONRESPONSE_ENCODING_DEFAULTBINARY 790 // Object
    #define UA_NS0ID_MODIFYSUBSCRIPTIONREQUEST 791 // DataType
    #define UA_NS0ID_MODIFYSUBSCRIPTIONREQUEST_ENCODING_DEFAULTXML 792 // Object
    #define UA_NS0ID_MODIFYSUBSCRIPTIONREQUEST_ENCODING_DEFAULTBINARY 793 // Object
    #define UA_NS0ID_MODIFYSUBSCRIPTIONRESPONSE 794 // DataType
    #define UA_NS0ID_MODIFYSUBSCRIPTIONRESPONSE_ENCODING_DEFAULTXML 795 // Object
    #define UA_NS0ID_MODIFYSUBSCRIPTIONRESPONSE_ENCODING_DEFAULTBINARY 796 // Object
    #define UA_NS0ID_SETPUBLISHINGMODEREQUEST 797 // DataType
    #define UA_NS0ID_SETPUBLISHINGMODEREQUEST_ENCODING_DEFAULTXML 798 // Object
    #define UA_NS0ID_SETPUBLISHINGMODEREQUEST_ENCODING_DEFAULTBINARY 799 // Object
    #define UA_NS0ID_SETPUBLISHINGMODERESPONSE 800 // DataType
    #define UA_NS0ID_SETPUBLISHINGMODERESPONSE_ENCODING_DEFAULTXML 801 // Object
    #define UA_NS0ID_SETPUBLISHINGMODERESPONSE_ENCODING_DEFAULTBINARY 802 // Object
    #define UA_NS0ID_NOTIFICATIONMESSAGE 803 // DataType
    #define UA_NS0ID_NOTIFICATIONMESSAGE_ENCODING_DEFAULTXML 804 // Object
    #define UA_NS0ID_NOTIFICATIONMESSAGE_ENCODING_DEFAULTBINARY 805 // Object
    #define UA_NS0ID_MONITOREDITEMNOTIFICATION 806 // DataType
    #define UA_NS0ID_MONITOREDITEMNOTIFICATION_ENCODING_DEFAULTXML 807 // Object
    #define UA_NS0ID_MONITOREDITEMNOTIFICATION_ENCODING_DEFAULTBINARY 808 // Object
    #define UA_NS0ID_DATACHANGENOTIFICATION 809 // DataType
    #define UA_NS0ID_DATACHANGENOTIFICATION_ENCODING_DEFAULTXML 810 // Object
    #define UA_NS0ID_DATACHANGENOTIFICATION_ENCODING_DEFAULTBINARY 811 // Object
    #define UA_NS0ID_STATUSCHANGENOTIFICATION 818 // DataType
    #define UA_NS0ID_STATUSCHANGENOTIFICATION_ENCODING_DEFAULTXML 819 // Object
    #define UA_NS0ID_STATUSCHANGENOTIFICATION_ENCODING_DEFAULTBINARY 820 // Object
    #define UA_NS0ID_SUBSCRIPTIONACKNOWLEDGEMENT 821 // DataType
    #define UA_NS0ID_SUBSCRIPTIONACKNOWLEDGEMENT_ENCODING_DEFAULTXML 822 // Object
    #define UA_NS0ID_SUBSCRIPTIONACKNOWLEDGEMENT_ENCODING_DEFAULTBINARY 823 // Object
    #define UA_NS0ID_PUBLISHREQUEST 824 // DataType
    #define UA_NS0ID_PUBLISHREQUEST_ENCODING_DEFAULTXML 825 // Object
    #define UA_NS0ID_PUBLISHREQUEST_ENCODING_DEFAULTBINARY 826 // Object
    #define UA_NS0ID_PUBLISHRESPONSE 827 // DataType
    #define UA_NS0ID_PUBLISHRESPONSE_ENCODING_DEFAULTXML 828 // Object
    #define UA_NS0ID_PUBLISHRESPONSE_ENCODING_DEFAULTBINARY 829 // Object
    #define UA_NS0ID_REPUBLISHREQUEST 830 // DataType
    #define UA_NS0ID_REPUBLISHREQUEST_ENCODING_DEFAULTXML 831 // Object
    #define UA_NS0ID_REPUBLISHREQUEST_ENCODING_DEFAULTBINARY 832 // Object
    #define UA_NS0ID_REPUBLISHRESPONSE 833 // DataType
    #define UA_NS0ID_REPUBLISHRESPONSE_ENCODING_DEFAULTXML 834 // Object
    #define UA_NS0ID_REPUBLISHRESPONSE_ENCODING_DEFAULTBINARY 835 // Object
    #define UA_NS0ID_TRANSFERRESULT 836 // DataType
    #define UA_NS0ID_TRANSFERRESULT_ENCODING_DEFAULTXML 837 // Object
    #define UA_NS0ID_TRANSFERRESULT_ENCODING_DEFAULTBINARY 838 // Object
    #define UA_NS0ID_TRANSFERSUBSCRIPTIONSREQUEST 839 // DataType
    #define UA_NS0ID_TRANSFERSUBSCRIPTIONSREQUEST_ENCODING_DEFAULTXML 840 // Object
    #define UA_NS0ID_TRANSFERSUBSCRIPTIONSREQUEST_ENCODING_DEFAULTBINARY 841 // Object
    #define UA_NS0ID_TRANSFERSUBSCRIPTIONSRESPONSE 842 // DataType
    #define UA_NS0ID_TRANSFERSUBSCRIPTIONSRESPONSE_ENCODING_DEFAULTXML 843 // Object
    #define UA_NS0ID_TRANSFERSUBSCRIPTIONSRESPONSE_ENCODING_DEFAULTBINARY 844 // Object
    #define UA_NS0ID_DELETESUBSCRIPTIONSREQUEST 845 // DataType
    #define UA_NS0ID_DELETESUBSCRIPTIONSREQUEST_ENCODING_DEFAULTXML 846 // Object
    #define UA_NS0ID_DELETESUBSCRIPTIONSREQUEST_ENCODING_DEFAULTBINARY 847 // Object
    #define UA_NS0ID_DELETESUBSCRIPTIONSRESPONSE 848 // DataType
    #define UA_NS0ID_DELETESUBSCRIPTIONSRESPONSE_ENCODING_DEFAULTXML 849 // Object
    #define UA_NS0ID_DELETESUBSCRIPTIONSRESPONSE_ENCODING_DEFAULTBINARY 850 // Object
    #define UA_NS0ID_REDUNDANCYSUPPORT 851 // DataType
    #define UA_NS0ID_SERVERSTATE 852 // DataType
    #define UA_NS0ID_REDUNDANTSERVERDATATYPE 853 // DataType
    #define UA_NS0ID_SAMPLINGINTERVALDIAGNOSTICSDATATYPE 856 // DataType
    #define UA_NS0ID_SERVERDIAGNOSTICSSUMMARYDATATYPE 859 // DataType
    #define UA_NS0ID_SERVERSTATUSDATATYPE 862 // DataType
    #define UA_NS0ID_SESSIONDIAGNOSTICSDATATYPE 865 // DataType
    #define UA_NS0ID_SESSIONSECURITYDIAGNOSTICSDATATYPE 868 // DataType
    #define UA_NS0ID_SERVICECOUNTERDATATYPE 871 // DataType
    #define UA_NS0ID_SUBSCRIPTIONDIAGNOSTICSDATATYPE 874 // DataType
    #define UA_NS0ID_MODELCHANGESTRUCTUREDATATYPE 877 // DataType
    #define UA_NS0ID_RANGE 884 // DataType
    #define UA_NS0ID_RANGE_ENCODING_DEFAULTXML 885 // Object
    #define UA_NS0ID_RANGE_ENCODING_DEFAULTBINARY 886 // Object
    #define UA_NS0ID_EUINFORMATION 887 // DataType
    #define UA_NS0ID_EUINFORMATION_ENCODING_DEFAULTXML 888 // Object
    #define UA_NS0ID_EUINFORMATION_ENCODING_DEFAULTBINARY 889 // Object
    #define UA_NS0ID_EXCEPTIONDEVIATIONFORMAT 890 // DataType
    #define UA_NS0ID_ANNOTATION 891 // DataType
    #define UA_NS0ID_ANNOTATION_ENCODING_DEFAULTXML 892 // Object
    #define UA_NS0ID_ANNOTATION_ENCODING_DEFAULTBINARY 893 // Object
    #define UA_NS0ID_PROGRAMDIAGNOSTICDATATYPE 894 // DataType
    #define UA_NS0ID_SEMANTICCHANGESTRUCTUREDATATYPE 897 // DataType
    #define UA_NS0ID_EVENTNOTIFICATIONLIST 914 // DataType
    #define UA_NS0ID_EVENTNOTIFICATIONLIST_ENCODING_DEFAULTXML 915 // Object
    #define UA_NS0ID_EVENTNOTIFICATIONLIST_ENCODING_DEFAULTBINARY 916 // Object
    #define UA_NS0ID_EVENTFIELDLIST 917 // DataType
    #define UA_NS0ID_EVENTFIELDLIST_ENCODING_DEFAULTXML 918 // Object
    #define UA_NS0ID_EVENTFIELDLIST_ENCODING_DEFAULTBINARY 919 // Object
    #define UA_NS0ID_HISTORYEVENTFIELDLIST 920 // DataType
    #define UA_NS0ID_HISTORYEVENTFIELDLIST_ENCODING_DEFAULTXML 921 // Object
    #define UA_NS0ID_HISTORYEVENTFIELDLIST_ENCODING_DEFAULTBINARY 922 // Object
    #define UA_NS0ID_ISSUEDIDENTITYTOKEN 938 // DataType
    #define UA_NS0ID_ISSUEDIDENTITYTOKEN_ENCODING_DEFAULTXML 939 // Object
    #define UA_NS0ID_ISSUEDIDENTITYTOKEN_ENCODING_DEFAULTBINARY 940 // Object
    #define UA_NS0ID_NOTIFICATIONDATA 945 // DataType
    #define UA_NS0ID_NOTIFICATIONDATA_ENCODING_DEFAULTXML 946 // Object
    #define UA_NS0ID_NOTIFICATIONDATA_ENCODING_DEFAULTBINARY 947 // Object
    #define UA_NS0ID_AGGREGATECONFIGURATION 948 // DataType
    #define UA_NS0ID_AGGREGATECONFIGURATION_ENCODING_DEFAULTXML 949 // Object
    #define UA_NS0ID_AGGREGATECONFIGURATION_ENCODING_DEFAULTBINARY 950 // Object
    #define UA_NS0ID_IMAGEBMP 2000 // DataType
    #define UA_NS0ID_IMAGEGIF 2001 // DataType
    #define UA_NS0ID_IMAGEJPG 2002 // DataType
    #define UA_NS0ID_IMAGEPNG 2003 // DataType
    #define UA_NS0ID_SERVERTYPE 2004 // ObjectType
    #define UA_NS0ID_SERVERCAPABILITIESTYPE 2013 // ObjectType
    #define UA_NS0ID_SERVERDIAGNOSTICSTYPE 2020 // ObjectType
    #define UA_NS0ID_SESSIONSDIAGNOSTICSSUMMARYTYPE 2026 // ObjectType
    #define UA_NS0ID_SESSIONDIAGNOSTICSOBJECTTYPE 2029 // ObjectType
    #define UA_NS0ID_VENDORSERVERINFOTYPE 2033 // ObjectType
    #define UA_NS0ID_SERVERREDUNDANCYTYPE 2034 // ObjectType
    #define UA_NS0ID_TRANSPARENTREDUNDANCYTYPE 2036 // ObjectType
    #define UA_NS0ID_NONTRANSPARENTREDUNDANCYTYPE 2039 // ObjectType
    #define UA_NS0ID_BASEEVENTTYPE 2041 // ObjectType
    #define UA_NS0ID_AUDITEVENTTYPE 2052 // ObjectType
    #define UA_NS0ID_AUDITSECURITYEVENTTYPE 2058 // ObjectType
    #define UA_NS0ID_AUDITCHANNELEVENTTYPE 2059 // ObjectType
    #define UA_NS0ID_AUDITOPENSECURECHANNELEVENTTYPE 2060 // ObjectType
    #define UA_NS0ID_AUDITSESSIONEVENTTYPE 2069 // ObjectType
    #define UA_NS0ID_AUDITCREATESESSIONEVENTTYPE 2071 // ObjectType
    #define UA_NS0ID_AUDITACTIVATESESSIONEVENTTYPE 2075 // ObjectType
    #define UA_NS0ID_AUDITCANCELEVENTTYPE 2078 // ObjectType
    #define UA_NS0ID_AUDITCERTIFICATEEVENTTYPE 2080 // ObjectType
    #define UA_NS0ID_AUDITCERTIFICATEDATAMISMATCHEVENTTYPE 2082 // ObjectType
    #define UA_NS0ID_AUDITCERTIFICATEEXPIREDEVENTTYPE 2085 // ObjectType
    #define UA_NS0ID_AUDITCERTIFICATEINVALIDEVENTTYPE 2086 // ObjectType
    #define UA_NS0ID_AUDITCERTIFICATEUNTRUSTEDEVENTTYPE 2087 // ObjectType
    #define UA_NS0ID_AUDITCERTIFICATEREVOKEDEVENTTYPE 2088 // ObjectType
    #define UA_NS0ID_AUDITCERTIFICATEMISMATCHEVENTTYPE 2089 // ObjectType
    #define UA_NS0ID_AUDITNODEMANAGEMENTEVENTTYPE 2090 // ObjectType
    #define UA_NS0ID_AUDITADDNODESEVENTTYPE 2091 // ObjectType
    #define UA_NS0ID_AUDITDELETENODESEVENTTYPE 2093 // ObjectType
    #define UA_NS0ID_AUDITADDREFERENCESEVENTTYPE 2095 // ObjectType
    #define UA_NS0ID_AUDITDELETEREFERENCESEVENTTYPE 2097 // ObjectType
    #define UA_NS0ID_AUDITUPDATEEVENTTYPE 2099 // ObjectType
    #define UA_NS0ID_AUDITWRITEUPDATEEVENTTYPE 2100 // ObjectType
    #define UA_NS0ID_AUDITHISTORYUPDATEEVENTTYPE 2104 // ObjectType
    #define UA_NS0ID_AUDITUPDATEMETHODEVENTTYPE 2127 // ObjectType
    #define UA_NS0ID_SYSTEMEVENTTYPE 2130 // ObjectType
    #define UA_NS0ID_DEVICEFAILUREEVENTTYPE 2131 // ObjectType
    #define UA_NS0ID_BASEMODELCHANGEEVENTTYPE 2132 // ObjectType
    #define UA_NS0ID_GENERALMODELCHANGEEVENTTYPE 2133 // ObjectType
    #define UA_NS0ID_SERVERVENDORCAPABILITYTYPE 2137 // VariableType
    #define UA_NS0ID_SERVERSTATUSTYPE 2138 // VariableType
    #define UA_NS0ID_SERVERDIAGNOSTICSSUMMARYTYPE 2150 // VariableType
    #define UA_NS0ID_SAMPLINGINTERVALDIAGNOSTICSARRAYTYPE 2164 // VariableType
    #define UA_NS0ID_SAMPLINGINTERVALDIAGNOSTICSTYPE 2165 // VariableType
    #define UA_NS0ID_SUBSCRIPTIONDIAGNOSTICSARRAYTYPE 2171 // VariableType
    #define UA_NS0ID_SUBSCRIPTIONDIAGNOSTICSTYPE 2172 // VariableType
    #define UA_NS0ID_SESSIONDIAGNOSTICSARRAYTYPE 2196 // VariableType
    #define UA_NS0ID_SESSIONDIAGNOSTICSVARIABLETYPE 2197 // VariableType
    #define UA_NS0ID_SESSIONSECURITYDIAGNOSTICSARRAYTYPE 2243 // VariableType
    #define UA_NS0ID_SESSIONSECURITYDIAGNOSTICSTYPE 2244 // VariableType
    #define UA_NS0ID_SERVER 2253 // Object
    #define UA_NS0ID_SERVER_SERVERARRAY 2254 // Variable
    #define UA_NS0ID_SERVER_NAMESPACEARRAY 2255 // Variable
    #define UA_NS0ID_SERVER_SERVERSTATUS 2256 // Variable
    #define UA_NS0ID_SERVER_SERVERSTATUS_STARTTIME 2257 // Variable
    #define UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME 2258 // Variable
    #define UA_NS0ID_SERVER_SERVERSTATUS_STATE 2259 // Variable
    #define UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO 2260 // Variable
    #define UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTNAME 2261 // Variable
    #define UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTURI 2262 // Variable
    #define UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_MANUFACTURERNAME 2263 // Variable
    #define UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_SOFTWAREVERSION 2264 // Variable
    #define UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER 2265 // Variable
    #define UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDDATE 2266 // Variable
    #define UA_NS0ID_SERVER_SERVICELEVEL 2267 // Variable
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES 2268 // Object
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES_SERVERPROFILEARRAY 2269 // Variable
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES_LOCALEIDARRAY 2271 // Variable
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES_MINSUPPORTEDSAMPLERATE 2272 // Variable
    #define UA_NS0ID_SERVER_SERVERDIAGNOSTICS 2274 // Object
    #define UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY 2275 // Variable
    #define UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_SERVERVIEWCOUNT 2276 // Variable
    #define UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_CURRENTSESSIONCOUNT 2277 // Variable
    #define UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_CUMULATEDSESSIONCOUNT 2278 // Variable
    #define UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_SECURITYREJECTEDSESSIONCOUNT 2279 // Variable
    #define UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_SESSIONTIMEOUTCOUNT 2281 // Variable
    #define UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_SESSIONABORTCOUNT 2282 // Variable
    #define UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_PUBLISHINGINTERVALCOUNT 2284 // Variable
    #define UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_CURRENTSUBSCRIPTIONCOUNT 2285 // Variable
    #define UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_CUMULATEDSUBSCRIPTIONCOUNT 2286 // Variable
    #define UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_SECURITYREJECTEDREQUESTSCOUNT 2287 // Variable
    #define UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_REJECTEDREQUESTSCOUNT 2288 // Variable
    #define UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SAMPLINGINTERVALDIAGNOSTICSARRAY 2289 // Variable
    #define UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SUBSCRIPTIONDIAGNOSTICSARRAY 2290 // Variable
    #define UA_NS0ID_SERVER_SERVERDIAGNOSTICS_ENABLEDFLAG 2294 // Variable
    #define UA_NS0ID_SERVER_VENDORSERVERINFO 2295 // Object
    #define UA_NS0ID_SERVER_SERVERREDUNDANCY 2296 // Object
    #define UA_NS0ID_STATEMACHINETYPE 2299 // ObjectType
    #define UA_NS0ID_STATETYPE 2307 // ObjectType
    #define UA_NS0ID_INITIALSTATETYPE 2309 // ObjectType
    #define UA_NS0ID_TRANSITIONTYPE 2310 // ObjectType
    #define UA_NS0ID_TRANSITIONEVENTTYPE 2311 // ObjectType
    #define UA_NS0ID_AUDITUPDATESTATEEVENTTYPE 2315 // ObjectType
    #define UA_NS0ID_HISTORICALDATACONFIGURATIONTYPE 2318 // ObjectType
    #define UA_NS0ID_HISTORYSERVERCAPABILITIESTYPE 2330 // ObjectType
    #define UA_NS0ID_AGGREGATEFUNCTIONTYPE 2340 // ObjectType
    #define UA_NS0ID_AGGREGATEFUNCTION_INTERPOLATIVE 2341 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_AVERAGE 2342 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_TIMEAVERAGE 2343 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_TOTAL 2344 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_MINIMUM 2346 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_MAXIMUM 2347 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_MINIMUMACTUALTIME 2348 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_MAXIMUMACTUALTIME 2349 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_RANGE 2350 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_ANNOTATIONCOUNT 2351 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_COUNT 2352 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_NUMBEROFTRANSITIONS 2355 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_START 2357 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_END 2358 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_DELTA 2359 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_DURATIONGOOD 2360 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_DURATIONBAD 2361 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_PERCENTGOOD 2362 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_PERCENTBAD 2363 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_WORSTQUALITY 2364 // Object
    #define UA_NS0ID_DATAITEMTYPE 2365 // VariableType
    #define UA_NS0ID_ANALOGITEMTYPE 2368 // VariableType
    #define UA_NS0ID_DISCRETEITEMTYPE 2372 // VariableType
    #define UA_NS0ID_TWOSTATEDISCRETETYPE 2373 // VariableType
    #define UA_NS0ID_MULTISTATEDISCRETETYPE 2376 // VariableType
    #define UA_NS0ID_PROGRAMTRANSITIONEVENTTYPE 2378 // ObjectType
    #define UA_NS0ID_PROGRAMDIAGNOSTICTYPE 2380 // VariableType
    #define UA_NS0ID_PROGRAMSTATEMACHINETYPE 2391 // ObjectType
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXBROWSECONTINUATIONPOINTS 2735 // Variable
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXQUERYCONTINUATIONPOINTS 2736 // Variable
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXHISTORYCONTINUATIONPOINTS 2737 // Variable
    #define UA_NS0ID_SEMANTICCHANGEEVENTTYPE 2738 // ObjectType
    #define UA_NS0ID_AUDITURLMISMATCHEVENTTYPE 2748 // ObjectType
    #define UA_NS0ID_STATEVARIABLETYPE 2755 // VariableType
    #define UA_NS0ID_FINITESTATEVARIABLETYPE 2760 // VariableType
    #define UA_NS0ID_TRANSITIONVARIABLETYPE 2762 // VariableType
    #define UA_NS0ID_FINITETRANSITIONVARIABLETYPE 2767 // VariableType
    #define UA_NS0ID_FINITESTATEMACHINETYPE 2771 // ObjectType
    #define UA_NS0ID_CONDITIONTYPE 2782 // ObjectType
    #define UA_NS0ID_REFRESHSTARTEVENTTYPE 2787 // ObjectType
    #define UA_NS0ID_REFRESHENDEVENTTYPE 2788 // ObjectType
    #define UA_NS0ID_REFRESHREQUIREDEVENTTYPE 2789 // ObjectType
    #define UA_NS0ID_AUDITCONDITIONEVENTTYPE 2790 // ObjectType
    #define UA_NS0ID_AUDITCONDITIONENABLEEVENTTYPE 2803 // ObjectType
    #define UA_NS0ID_AUDITCONDITIONCOMMENTEVENTTYPE 2829 // ObjectType
    #define UA_NS0ID_DIALOGCONDITIONTYPE 2830 // ObjectType
    #define UA_NS0ID_ACKNOWLEDGEABLECONDITIONTYPE 2881 // ObjectType
    #define UA_NS0ID_ALARMCONDITIONTYPE 2915 // ObjectType
    #define UA_NS0ID_SHELVEDSTATEMACHINETYPE 2929 // ObjectType
    #define UA_NS0ID_LIMITALARMTYPE 2955 // ObjectType
    #define UA_NS0ID_SERVER_SERVERSTATUS_SECONDSTILLSHUTDOWN 2992 // Variable
    #define UA_NS0ID_SERVER_SERVERSTATUS_SHUTDOWNREASON 2993 // Variable
    #define UA_NS0ID_SERVER_AUDITING 2994 // Variable
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES_MODELLINGRULES 2996 // Object
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES_AGGREGATEFUNCTIONS 2997 // Object
    #define UA_NS0ID_AUDITHISTORYEVENTUPDATEEVENTTYPE 2999 // ObjectType
    #define UA_NS0ID_AUDITHISTORYVALUEUPDATEEVENTTYPE 3006 // ObjectType
    #define UA_NS0ID_AUDITHISTORYDELETEEVENTTYPE 3012 // ObjectType
    #define UA_NS0ID_AUDITHISTORYRAWMODIFYDELETEEVENTTYPE 3014 // ObjectType
    #define UA_NS0ID_AUDITHISTORYATTIMEDELETEEVENTTYPE 3019 // ObjectType
    #define UA_NS0ID_AUDITHISTORYEVENTDELETEEVENTTYPE 3022 // ObjectType
    #define UA_NS0ID_EVENTQUEUEOVERFLOWEVENTTYPE 3035 // ObjectType
    #define UA_NS0ID_EVENTTYPESFOLDER 3048 // Object
    #define UA_NS0ID_BUILDINFOTYPE 3051 // VariableType
    #define UA_NS0ID_DEFAULTBINARY 3062 // Object
    #define UA_NS0ID_DEFAULTXML 3063 // Object
    #define UA_NS0ID_ALWAYSGENERATESEVENT 3065 // ReferenceType
    #define UA_NS0ID_ICON 3067 // Variable
    #define UA_NS0ID_NODEVERSION 3068 // Variable
    #define UA_NS0ID_LOCALTIME 3069 // Variable
    #define UA_NS0ID_ALLOWNULLS 3070 // Variable
    #define UA_NS0ID_ENUMVALUES 3071 // Variable
    #define UA_NS0ID_INPUTARGUMENTS 3072 // Variable
    #define UA_NS0ID_OUTPUTARGUMENTS 3073 // Variable
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES_SOFTWARECERTIFICATES 3704 // Variable
    #define UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_REJECTEDSESSIONCOUNT 3705 // Variable
    #define UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SESSIONSDIAGNOSTICSSUMMARY 3706 // Object
    #define UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SESSIONSDIAGNOSTICSSUMMARY_SESSIONDIAGNOSTICSARRAY 3707 // Variable
    #define UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SESSIONSDIAGNOSTICSSUMMARY_SESSIONSECURITYDIAGNOSTICSARRAY 3708 // Variable
    #define UA_NS0ID_SERVER_SERVERREDUNDANCY_REDUNDANCYSUPPORT 3709 // Variable
    #define UA_NS0ID_PROGRAMTRANSITIONAUDITEVENTTYPE 3806 // ObjectType
    #define UA_NS0ID_ADDCOMMENTMETHODTYPE 3863 // Method
    #define UA_NS0ID_TIMEDSHELVEMETHODTYPE 6102 // Method
    #define UA_NS0ID_ENUMVALUETYPE 7594 // DataType
    #define UA_NS0ID_MESSAGESECURITYMODE_ENUMSTRINGS 7595 // Variable
    #define UA_NS0ID_BROWSEDIRECTION_ENUMSTRINGS 7603 // Variable
    #define UA_NS0ID_FILTEROPERATOR_ENUMSTRINGS 7605 // Variable
    #define UA_NS0ID_TIMESTAMPSTORETURN_ENUMSTRINGS 7606 // Variable
    #define UA_NS0ID_MONITORINGMODE_ENUMSTRINGS 7608 // Variable
    #define UA_NS0ID_DATACHANGETRIGGER_ENUMSTRINGS 7609 // Variable
    #define UA_NS0ID_REDUNDANCYSUPPORT_ENUMSTRINGS 7611 // Variable
    #define UA_NS0ID_SERVERSTATE_ENUMSTRINGS 7612 // Variable
    #define UA_NS0ID_EXCEPTIONDEVIATIONFORMAT_ENUMSTRINGS 7614 // Variable
    #define UA_NS0ID_TIMEZONEDATATYPE 8912 // DataType
    #define UA_NS0ID_AUDITCONDITIONRESPONDEVENTTYPE 8927 // ObjectType
    #define UA_NS0ID_AUDITCONDITIONACKNOWLEDGEEVENTTYPE 8944 // ObjectType
    #define UA_NS0ID_AUDITCONDITIONCONFIRMEVENTTYPE 8961 // ObjectType
    #define UA_NS0ID_TWOSTATEVARIABLETYPE 8995 // VariableType
    #define UA_NS0ID_CONDITIONVARIABLETYPE 9002 // VariableType
    #define UA_NS0ID_HASTRUESUBSTATE 9004 // ReferenceType
    #define UA_NS0ID_HASFALSESUBSTATE 9005 // ReferenceType
    #define UA_NS0ID_HASCONDITION 9006 // ReferenceType
    #define UA_NS0ID_CONDITIONREFRESHMETHODTYPE 9007 // Method
    #define UA_NS0ID_DIALOGRESPONSEMETHODTYPE 9031 // Method
    #define UA_NS0ID_EXCLUSIVELIMITSTATEMACHINETYPE 9318 // ObjectType
    #define UA_NS0ID_EXCLUSIVELIMITALARMTYPE 9341 // ObjectType
    #define UA_NS0ID_EXCLUSIVELEVELALARMTYPE 9482 // ObjectType
    #define UA_NS0ID_EXCLUSIVERATEOFCHANGEALARMTYPE 9623 // ObjectType
    #define UA_NS0ID_EXCLUSIVEDEVIATIONALARMTYPE 9764 // ObjectType
    #define UA_NS0ID_NONEXCLUSIVELIMITALARMTYPE 9906 // ObjectType
    #define UA_NS0ID_NONEXCLUSIVELEVELALARMTYPE 10060 // ObjectType
    #define UA_NS0ID_NONEXCLUSIVERATEOFCHANGEALARMTYPE 10214 // ObjectType
    #define UA_NS0ID_NONEXCLUSIVEDEVIATIONALARMTYPE 10368 // ObjectType
    #define UA_NS0ID_DISCRETEALARMTYPE 10523 // ObjectType
    #define UA_NS0ID_OFFNORMALALARMTYPE 10637 // ObjectType
    #define UA_NS0ID_TRIPALARMTYPE 10751 // ObjectType
    #define UA_NS0ID_AUDITCONDITIONSHELVINGEVENTTYPE 11093 // ObjectType
    #define UA_NS0ID_BASECONDITIONCLASSTYPE 11163 // ObjectType
    #define UA_NS0ID_PROCESSCONDITIONCLASSTYPE 11164 // ObjectType
    #define UA_NS0ID_MAINTENANCECONDITIONCLASSTYPE 11165 // ObjectType
    #define UA_NS0ID_SYSTEMCONDITIONCLASSTYPE 11166 // ObjectType
    #define UA_NS0ID_AGGREGATECONFIGURATIONTYPE 11187 // ObjectType
    #define UA_NS0ID_HISTORYSERVERCAPABILITIES 11192 // Object
    #define UA_NS0ID_HISTORYSERVERCAPABILITIES_ACCESSHISTORYDATACAPABILITY 11193 // Variable
    #define UA_NS0ID_HISTORYSERVERCAPABILITIES_INSERTDATACAPABILITY 11196 // Variable
    #define UA_NS0ID_HISTORYSERVERCAPABILITIES_REPLACEDATACAPABILITY 11197 // Variable
    #define UA_NS0ID_HISTORYSERVERCAPABILITIES_UPDATEDATACAPABILITY 11198 // Variable
    #define UA_NS0ID_HISTORYSERVERCAPABILITIES_DELETERAWCAPABILITY 11199 // Variable
    #define UA_NS0ID_HISTORYSERVERCAPABILITIES_DELETEATTIMECAPABILITY 11200 // Variable
    #define UA_NS0ID_HISTORYSERVERCAPABILITIES_AGGREGATEFUNCTIONS 11201 // Object
    #define UA_NS0ID_HACONFIGURATION 11202 // Object
    #define UA_NS0ID_HACONFIGURATION_AGGREGATECONFIGURATION 11203 // Object
    #define UA_NS0ID_HACONFIGURATION_AGGREGATECONFIGURATION_TREATUNCERTAINASBAD 11204 // Variable
    #define UA_NS0ID_HACONFIGURATION_AGGREGATECONFIGURATION_PERCENTDATABAD 11205 // Variable
    #define UA_NS0ID_HACONFIGURATION_AGGREGATECONFIGURATION_PERCENTDATAGOOD 11206 // Variable
    #define UA_NS0ID_HACONFIGURATION_AGGREGATECONFIGURATION_USESLOPEDEXTRAPOLATION 11207 // Variable
    #define UA_NS0ID_HACONFIGURATION_STEPPED 11208 // Variable
    #define UA_NS0ID_HACONFIGURATION_DEFINITION 11209 // Variable
    #define UA_NS0ID_HACONFIGURATION_MAXTIMEINTERVAL 11210 // Variable
    #define UA_NS0ID_HACONFIGURATION_MINTIMEINTERVAL 11211 // Variable
    #define UA_NS0ID_HACONFIGURATION_EXCEPTIONDEVIATION 11212 // Variable
    #define UA_NS0ID_HACONFIGURATION_EXCEPTIONDEVIATIONFORMAT 11213 // Variable
    #define UA_NS0ID_ANNOTATIONS 11214 // Variable
    #define UA_NS0ID_HISTORICALEVENTFILTER 11215 // Variable
    #define UA_NS0ID_MODIFICATIONINFO 11216 // DataType
    #define UA_NS0ID_HISTORYMODIFIEDDATA 11217 // DataType
    #define UA_NS0ID_MODIFICATIONINFO_ENCODING_DEFAULTXML 11218 // Object
    #define UA_NS0ID_HISTORYMODIFIEDDATA_ENCODING_DEFAULTXML 11219 // Object
    #define UA_NS0ID_MODIFICATIONINFO_ENCODING_DEFAULTBINARY 11226 // Object
    #define UA_NS0ID_HISTORYMODIFIEDDATA_ENCODING_DEFAULTBINARY 11227 // Object
    #define UA_NS0ID_HISTORYUPDATETYPE 11234 // DataType
    #define UA_NS0ID_MULTISTATEVALUEDISCRETETYPE 11238 // VariableType
    #define UA_NS0ID_HISTORYSERVERCAPABILITIES_ACCESSHISTORYEVENTSCAPABILITY 11242 // Variable
    #define UA_NS0ID_HISTORYSERVERCAPABILITIES_MAXRETURNDATAVALUES 11273 // Variable
    #define UA_NS0ID_HISTORYSERVERCAPABILITIES_MAXRETURNEVENTVALUES 11274 // Variable
    #define UA_NS0ID_HISTORYSERVERCAPABILITIES_INSERTANNOTATIONCAPABILITY 11275 // Variable
    #define UA_NS0ID_HISTORYSERVERCAPABILITIES_INSERTEVENTCAPABILITY 11281 // Variable
    #define UA_NS0ID_HISTORYSERVERCAPABILITIES_REPLACEEVENTCAPABILITY 11282 // Variable
    #define UA_NS0ID_HISTORYSERVERCAPABILITIES_UPDATEEVENTCAPABILITY 11283 // Variable
    #define UA_NS0ID_AGGREGATEFUNCTION_TIMEAVERAGE2 11285 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_MINIMUM2 11286 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_MAXIMUM2 11287 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_RANGE2 11288 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_WORSTQUALITY2 11292 // Object
    #define UA_NS0ID_PERFORMUPDATETYPE 11293 // DataType
    #define UA_NS0ID_UPDATESTRUCTUREDATADETAILS 11295 // DataType
    #define UA_NS0ID_UPDATESTRUCTUREDATADETAILS_ENCODING_DEFAULTXML 11296 // Object
    #define UA_NS0ID_UPDATESTRUCTUREDATADETAILS_ENCODING_DEFAULTBINARY 11300 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_TOTAL2 11304 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_MINIMUMACTUALTIME2 11305 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_MAXIMUMACTUALTIME2 11306 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_DURATIONINSTATEZERO 11307 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_DURATIONINSTATENONZERO 11308 // Object
    #define UA_NS0ID_SERVER_SERVERREDUNDANCY_CURRENTSERVERID 11312 // Variable
    #define UA_NS0ID_SERVER_SERVERREDUNDANCY_REDUNDANTSERVERARRAY 11313 // Variable
    #define UA_NS0ID_SERVER_SERVERREDUNDANCY_SERVERURIARRAY 11314 // Variable
    #define UA_NS0ID_AGGREGATEFUNCTION_STANDARDDEVIATIONSAMPLE 11426 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_STANDARDDEVIATIONPOPULATION 11427 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_VARIANCESAMPLE 11428 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_VARIANCEPOPULATION 11429 // Object
    #define UA_NS0ID_ENUMSTRINGS 11432 // Variable
    #define UA_NS0ID_VALUEASTEXT 11433 // Variable
    #define UA_NS0ID_PROGRESSEVENTTYPE 11436 // ObjectType
    #define UA_NS0ID_SYSTEMSTATUSCHANGEEVENTTYPE 11446 // ObjectType
    #define UA_NS0ID_OPTIONSETTYPE 11487 // VariableType
    #define UA_NS0ID_SERVER_GETMONITOREDITEMS 11492 // Method
    #define UA_NS0ID_SERVER_GETMONITOREDITEMS_INPUTARGUMENTS 11493 // Variable
    #define UA_NS0ID_SERVER_GETMONITOREDITEMS_OUTPUTARGUMENTS 11494 // Variable
    #define UA_NS0ID_GETMONITOREDITEMSMETHODTYPE 11495 // Method
    #define UA_NS0ID_MAXSTRINGLENGTH 11498 // Variable
    #define UA_NS0ID_HISTORYSERVERCAPABILITIES_DELETEEVENTCAPABILITY 11502 // Variable
    #define UA_NS0ID_HACONFIGURATION_STARTOFARCHIVE 11503 // Variable
    #define UA_NS0ID_HACONFIGURATION_STARTOFONLINEARCHIVE 11504 // Variable
    #define UA_NS0ID_AGGREGATEFUNCTION_STARTBOUND 11505 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_ENDBOUND 11506 // Object
    #define UA_NS0ID_AGGREGATEFUNCTION_DELTABOUNDS 11507 // Object
    #define UA_NS0ID_MODELLINGRULE_OPTIONALPLACEHOLDER 11508 // Object
    #define UA_NS0ID_MODELLINGRULE_OPTIONALPLACEHOLDER_NAMINGRULE 11509 // Variable
    #define UA_NS0ID_MODELLINGRULE_MANDATORYPLACEHOLDER 11510 // Object
    #define UA_NS0ID_MODELLINGRULE_MANDATORYPLACEHOLDER_NAMINGRULE 11511 // Variable
    #define UA_NS0ID_MAXARRAYLENGTH 11512 // Variable
    #define UA_NS0ID_ENGINEERINGUNITS 11513 // Variable
    #define UA_NS0ID_OPERATIONLIMITSTYPE 11564 // ObjectType
    #define UA_NS0ID_FILETYPE 11575 // ObjectType
    #define UA_NS0ID_ADDRESSSPACEFILETYPE 11595 // ObjectType
    #define UA_NS0ID_NAMESPACEMETADATATYPE 11616 // ObjectType
    #define UA_NS0ID_NAMESPACESTYPE 11645 // ObjectType
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXARRAYLENGTH 11702 // Variable
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXSTRINGLENGTH 11703 // Variable
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS 11704 // Object
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERREAD 11705 // Variable
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERWRITE 11707 // Variable
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERMETHODCALL 11709 // Variable
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERBROWSE 11710 // Variable
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERREGISTERNODES 11711 // Variable
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERTRANSLATEBROWSEPATHSTONODEIDS 11712 // Variable
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERNODEMANAGEMENT 11713 // Variable
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXMONITOREDITEMSPERCALL 11714 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES 11715 // Object
    #define UA_NS0ID_SERVER_NAMESPACES_ADDRESSSPACEFILE 11716 // Object
    #define UA_NS0ID_SERVER_NAMESPACES_ADDRESSSPACEFILE_SIZE 11717 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_ADDRESSSPACEFILE_OPENCOUNT 11720 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_ADDRESSSPACEFILE_OPEN 11721 // Method
    #define UA_NS0ID_SERVER_NAMESPACES_ADDRESSSPACEFILE_OPEN_INPUTARGUMENTS 11722 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_ADDRESSSPACEFILE_OPEN_OUTPUTARGUMENTS 11723 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_ADDRESSSPACEFILE_CLOSE 11724 // Method
    #define UA_NS0ID_SERVER_NAMESPACES_ADDRESSSPACEFILE_CLOSE_INPUTARGUMENTS 11725 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_ADDRESSSPACEFILE_READ 11726 // Method
    #define UA_NS0ID_SERVER_NAMESPACES_ADDRESSSPACEFILE_READ_INPUTARGUMENTS 11727 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_ADDRESSSPACEFILE_READ_OUTPUTARGUMENTS 11728 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_ADDRESSSPACEFILE_WRITE 11729 // Method
    #define UA_NS0ID_SERVER_NAMESPACES_ADDRESSSPACEFILE_WRITE_INPUTARGUMENTS 11730 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_ADDRESSSPACEFILE_GETPOSITION 11731 // Method
    #define UA_NS0ID_SERVER_NAMESPACES_ADDRESSSPACEFILE_GETPOSITION_INPUTARGUMENTS 11732 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_ADDRESSSPACEFILE_GETPOSITION_OUTPUTARGUMENTS 11733 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_ADDRESSSPACEFILE_SETPOSITION 11734 // Method
    #define UA_NS0ID_SERVER_NAMESPACES_ADDRESSSPACEFILE_SETPOSITION_INPUTARGUMENTS 11735 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_ADDRESSSPACEFILE_EXPORTNAMESPACE 11736 // Method
    #define UA_NS0ID_BITFIELDMASKDATATYPE 11737 // DataType
    #define UA_NS0ID_OPENMETHODTYPE 11738 // Method
    #define UA_NS0ID_CLOSEMETHODTYPE 11741 // Method
    #define UA_NS0ID_READMETHODTYPE 11743 // Method
    #define UA_NS0ID_WRITEMETHODTYPE 11746 // Method
    #define UA_NS0ID_GETPOSITIONMETHODTYPE 11748 // Method
    #define UA_NS0ID_SETPOSITIONMETHODTYPE 11751 // Method
    #define UA_NS0ID_SYSTEMOFFNORMALALARMTYPE 11753 // ObjectType
    #define UA_NS0ID_AUDITPROGRAMTRANSITIONEVENTTYPE 11856 // ObjectType
    #define UA_NS0ID_HACONFIGURATION_AGGREGATEFUNCTIONS 11877 // Object
    #define UA_NS0ID_NODECLASS_ENUMVALUES 11878 // Variable
    #define UA_NS0ID_INSTANCENODE 11879 // DataType
    #define UA_NS0ID_TYPENODE 11880 // DataType
    #define UA_NS0ID_NODEATTRIBUTESMASK_ENUMVALUES 11881 // Variable
    #define UA_NS0ID_ATTRIBUTEWRITEMASK_ENUMVALUES 11882 // Variable
    #define UA_NS0ID_BROWSERESULTMASK_ENUMVALUES 11883 // Variable
    #define UA_NS0ID_INSTANCENODE_ENCODING_DEFAULTXML 11887 // Object
    #define UA_NS0ID_TYPENODE_ENCODING_DEFAULTXML 11888 // Object
    #define UA_NS0ID_INSTANCENODE_ENCODING_DEFAULTBINARY 11889 // Object
    #define UA_NS0ID_TYPENODE_ENCODING_DEFAULTBINARY 11890 // Object
    #define UA_NS0ID_OPENFILEMODE 11939 // DataType
    #define UA_NS0ID_OPENFILEMODE_ENUMVALUES 11940 // Variable
    #define UA_NS0ID_MODELCHANGESTRUCTUREVERBMASK 11941 // DataType
    #define UA_NS0ID_MODELCHANGESTRUCTUREVERBMASK_ENUMVALUES 11942 // Variable
    #define UA_NS0ID_ENDPOINTURLLISTDATATYPE 11943 // DataType
    #define UA_NS0ID_NETWORKGROUPDATATYPE 11944 // DataType
    #define UA_NS0ID_NONTRANSPARENTNETWORKREDUNDANCYTYPE 11945 // ObjectType
    #define UA_NS0ID_ARRAYITEMTYPE 12021 // VariableType
    #define UA_NS0ID_YARRAYITEMTYPE 12029 // VariableType
    #define UA_NS0ID_XYARRAYITEMTYPE 12038 // VariableType
    #define UA_NS0ID_IMAGEITEMTYPE 12047 // VariableType
    #define UA_NS0ID_CUBEITEMTYPE 12057 // VariableType
    #define UA_NS0ID_NDIMENSIONARRAYITEMTYPE 12068 // VariableType
    #define UA_NS0ID_AXISSCALEENUMERATION 12077 // DataType
    #define UA_NS0ID_AXISSCALEENUMERATION_ENUMSTRINGS 12078 // Variable
    #define UA_NS0ID_AXISINFORMATION 12079 // DataType
    #define UA_NS0ID_XVTYPE 12080 // DataType
    #define UA_NS0ID_AXISINFORMATION_ENCODING_DEFAULTXML 12081 // Object
    #define UA_NS0ID_AXISINFORMATION_ENCODING_DEFAULTBINARY 12089 // Object
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERHISTORYREADDATA 12165 // Variable
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERHISTORYREADEVENTS 12166 // Variable
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERHISTORYUPDATEDATA 12167 // Variable
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERHISTORYUPDATEEVENTS 12168 // Variable
    #define UA_NS0ID_VIEWVERSION 12170 // Variable
    #define UA_NS0ID_COMPLEXNUMBERTYPE 12171 // DataType
    #define UA_NS0ID_DOUBLECOMPLEXNUMBERTYPE 12172 // DataType
    #define UA_NS0ID_SERVERONNETWORK 12189 // DataType
    #define UA_NS0ID_FINDSERVERSONNETWORKREQUEST 12190 // DataType
    #define UA_NS0ID_FINDSERVERSONNETWORKRESPONSE 12191 // DataType
    #define UA_NS0ID_REGISTERSERVER2REQUEST 12193 // DataType
    #define UA_NS0ID_REGISTERSERVER2RESPONSE 12194 // DataType
    #define UA_NS0ID_SERVERONNETWORK_ENCODING_DEFAULTXML 12195 // Object
    #define UA_NS0ID_FINDSERVERSONNETWORKREQUEST_ENCODING_DEFAULTXML 12196 // Object
    #define UA_NS0ID_FINDSERVERSONNETWORKRESPONSE_ENCODING_DEFAULTXML 12197 // Object
    #define UA_NS0ID_REGISTERSERVER2REQUEST_ENCODING_DEFAULTXML 12199 // Object
    #define UA_NS0ID_REGISTERSERVER2RESPONSE_ENCODING_DEFAULTXML 12200 // Object
    #define UA_NS0ID_SERVERONNETWORK_ENCODING_DEFAULTBINARY 12207 // Object
    #define UA_NS0ID_FINDSERVERSONNETWORKREQUEST_ENCODING_DEFAULTBINARY 12208 // Object
    #define UA_NS0ID_FINDSERVERSONNETWORKRESPONSE_ENCODING_DEFAULTBINARY 12209 // Object
    #define UA_NS0ID_REGISTERSERVER2REQUEST_ENCODING_DEFAULTBINARY 12211 // Object
    #define UA_NS0ID_REGISTERSERVER2RESPONSE_ENCODING_DEFAULTBINARY 12212 // Object
    #define UA_NS0ID_OPENWITHMASKSMETHODTYPE 12513 // Method
    #define UA_NS0ID_CLOSEANDUPDATEMETHODTYPE 12516 // Method
    #define UA_NS0ID_ADDCERTIFICATEMETHODTYPE 12518 // Method
    #define UA_NS0ID_REMOVECERTIFICATEMETHODTYPE 12520 // Method
    #define UA_NS0ID_TRUSTLISTTYPE 12522 // ObjectType
    #define UA_NS0ID_TRUSTLISTMASKS 12552 // DataType
    #define UA_NS0ID_TRUSTLISTMASKS_ENUMVALUES 12553 // Variable
    #define UA_NS0ID_TRUSTLISTDATATYPE 12554 // DataType
    #define UA_NS0ID_CERTIFICATEGROUPTYPE 12555 // ObjectType
    #define UA_NS0ID_CERTIFICATETYPE 12556 // ObjectType
    #define UA_NS0ID_APPLICATIONCERTIFICATETYPE 12557 // ObjectType
    #define UA_NS0ID_HTTPSCERTIFICATETYPE 12558 // ObjectType
    #define UA_NS0ID_RSAMINAPPLICATIONCERTIFICATETYPE 12559 // ObjectType
    #define UA_NS0ID_RSASHA256APPLICATIONCERTIFICATETYPE 12560 // ObjectType
    #define UA_NS0ID_TRUSTLISTUPDATEDAUDITEVENTTYPE 12561 // ObjectType
    #define UA_NS0ID_UPDATECERTIFICATEMETHODTYPE 12578 // Method
    #define UA_NS0ID_SERVERCONFIGURATIONTYPE 12581 // ObjectType
    #define UA_NS0ID_CERTIFICATEUPDATEDAUDITEVENTTYPE 12620 // ObjectType
    #define UA_NS0ID_SERVERCONFIGURATION 12637 // Object
    #define UA_NS0ID_SERVERCONFIGURATION_SUPPORTEDPRIVATEKEYFORMATS 12639 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_MAXTRUSTLISTSIZE 12640 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_MULTICASTDNSENABLED 12641 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST 12642 // Object
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_SIZE 12643 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_OPENCOUNT 12646 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_OPEN 12647 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_OPEN_INPUTARGUMENTS 12648 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_OPEN_OUTPUTARGUMENTS 12649 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_CLOSE 12650 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_CLOSE_INPUTARGUMENTS 12651 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_READ 12652 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_READ_INPUTARGUMENTS 12653 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_READ_OUTPUTARGUMENTS 12654 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_WRITE 12655 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_WRITE_INPUTARGUMENTS 12656 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_GETPOSITION 12657 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_GETPOSITION_INPUTARGUMENTS 12658 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_GETPOSITION_OUTPUTARGUMENTS 12659 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_SETPOSITION 12660 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_SETPOSITION_INPUTARGUMENTS 12661 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_LASTUPDATETIME 12662 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_OPENWITHMASKS 12663 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_OPENWITHMASKS_INPUTARGUMENTS 12664 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_OPENWITHMASKS_OUTPUTARGUMENTS 12665 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_CLOSEANDUPDATE 12666 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_CLOSEANDUPDATE_OUTPUTARGUMENTS 12667 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_ADDCERTIFICATE 12668 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_ADDCERTIFICATE_INPUTARGUMENTS 12669 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_REMOVECERTIFICATE 12670 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_REMOVECERTIFICATE_INPUTARGUMENTS 12671 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_ADDRESSSPACEFILE_WRITABLE 12696 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_ADDRESSSPACEFILE_USERWRITABLE 12697 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_SERVERCAPABILITIES 12710 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CREATESIGNINGREQUEST 12737 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CREATESIGNINGREQUEST_INPUTARGUMENTS 12738 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CREATESIGNINGREQUEST_OUTPUTARGUMENTS 12739 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_APPLYCHANGES 12740 // Method
    #define UA_NS0ID_CREATESIGNINGREQUESTMETHODTYPE 12741 // Method
    #define UA_NS0ID_OPTIONSETVALUES 12745 // Variable
    #define UA_NS0ID_SERVER_SETSUBSCRIPTIONDURABLE 12749 // Method
    #define UA_NS0ID_SERVER_SETSUBSCRIPTIONDURABLE_INPUTARGUMENTS 12750 // Variable
    #define UA_NS0ID_SERVER_SETSUBSCRIPTIONDURABLE_OUTPUTARGUMENTS 12751 // Variable
    #define UA_NS0ID_SETSUBSCRIPTIONDURABLEMETHODTYPE 12752 // Method
    #define UA_NS0ID_OPTIONSET 12755 // DataType
    #define UA_NS0ID_UNION 12756 // DataType
    #define UA_NS0ID_OPTIONSET_ENCODING_DEFAULTXML 12757 // Object
    #define UA_NS0ID_UNION_ENCODING_DEFAULTXML 12758 // Object
    #define UA_NS0ID_OPTIONSET_ENCODING_DEFAULTBINARY 12765 // Object
    #define UA_NS0ID_UNION_ENCODING_DEFAULTBINARY 12766 // Object
    #define UA_NS0ID_GETREJECTEDLISTMETHODTYPE 12773 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_GETREJECTEDLIST 12777 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_GETREJECTEDLIST_OUTPUTARGUMENTS 12778 // Variable
    #define UA_NS0ID_SERVER_RESENDDATA 12873 // Method
    #define UA_NS0ID_SERVER_RESENDDATA_INPUTARGUMENTS 12874 // Variable
    #define UA_NS0ID_RESENDDATAMETHODTYPE 12875 // Method
    #define UA_NS0ID_NORMALIZEDSTRING 12877 // DataType
    #define UA_NS0ID_DECIMALSTRING 12878 // DataType
    #define UA_NS0ID_DURATIONSTRING 12879 // DataType
    #define UA_NS0ID_TIMESTRING 12880 // DataType
    #define UA_NS0ID_DATESTRING 12881 // DataType
    #define UA_NS0ID_SERVER_ESTIMATEDRETURNTIME 12885 // Variable
    #define UA_NS0ID_SERVER_REQUESTSERVERSTATECHANGE 12886 // Method
    #define UA_NS0ID_SERVER_REQUESTSERVERSTATECHANGE_INPUTARGUMENTS 12887 // Variable
    #define UA_NS0ID_REQUESTSERVERSTATECHANGEMETHODTYPE 12888 // Method
    #define UA_NS0ID_DISCOVERYCONFIGURATION 12890 // DataType
    #define UA_NS0ID_MDNSDISCOVERYCONFIGURATION 12891 // DataType
    #define UA_NS0ID_DISCOVERYCONFIGURATION_ENCODING_DEFAULTXML 12892 // Object
    #define UA_NS0ID_MDNSDISCOVERYCONFIGURATION_ENCODING_DEFAULTXML 12893 // Object
    #define UA_NS0ID_DISCOVERYCONFIGURATION_ENCODING_DEFAULTBINARY 12900 // Object
    #define UA_NS0ID_MDNSDISCOVERYCONFIGURATION_ENCODING_DEFAULTBINARY 12901 // Object
    #define UA_NS0ID_MAXBYTESTRINGLENGTH 12908 // Variable
    #define UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXBYTESTRINGLENGTH 12911 // Variable
    #define UA_NS0ID_CONDITIONREFRESH2METHODTYPE 12914 // Method
    #define UA_NS0ID_CERTIFICATEEXPIRATIONALARMTYPE 13225 // ObjectType
    #define UA_NS0ID_CREATEDIRECTORYMETHODTYPE 13342 // Method
    #define UA_NS0ID_CREATEFILEMETHODTYPE 13345 // Method
    #define UA_NS0ID_DELETEFILEMETHODTYPE 13348 // Method
    #define UA_NS0ID_MOVEORCOPYMETHODTYPE 13350 // Method
    #define UA_NS0ID_FILEDIRECTORYTYPE 13353 // ObjectType
    #define UA_NS0ID_SERVER_NAMESPACES_ADDRESSSPACEFILE_MIMETYPE 13402 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_UPDATECERTIFICATE 13737 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_UPDATECERTIFICATE_INPUTARGUMENTS 13738 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_UPDATECERTIFICATE_OUTPUTARGUMENTS 13739 // Variable
    #define UA_NS0ID_CERTIFICATEGROUPFOLDERTYPE 13813 // ObjectType
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS 14053 // Object
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP 14088 // Object
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST 14089 // Object
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_SIZE 14090 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_WRITABLE 14091 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_USERWRITABLE 14092 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_OPENCOUNT 14093 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_MIMETYPE 14094 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_OPEN 14095 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_OPEN_INPUTARGUMENTS 14096 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_OPEN_OUTPUTARGUMENTS 14097 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_CLOSE 14098 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_CLOSE_INPUTARGUMENTS 14099 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_READ 14100 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_READ_INPUTARGUMENTS 14101 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_READ_OUTPUTARGUMENTS 14102 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_WRITE 14103 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_WRITE_INPUTARGUMENTS 14104 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_GETPOSITION 14105 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_GETPOSITION_INPUTARGUMENTS 14106 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_GETPOSITION_OUTPUTARGUMENTS 14107 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_SETPOSITION 14108 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_SETPOSITION_INPUTARGUMENTS 14109 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_LASTUPDATETIME 14110 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_OPENWITHMASKS 14111 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_OPENWITHMASKS_INPUTARGUMENTS 14112 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_OPENWITHMASKS_OUTPUTARGUMENTS 14113 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_CLOSEANDUPDATE 14114 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_CLOSEANDUPDATE_INPUTARGUMENTS 14115 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_CLOSEANDUPDATE_OUTPUTARGUMENTS 14116 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_ADDCERTIFICATE 14117 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_ADDCERTIFICATE_INPUTARGUMENTS 14118 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_REMOVECERTIFICATE 14119 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_TRUSTLIST_REMOVECERTIFICATE_INPUTARGUMENTS 14120 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP_CERTIFICATETYPES 14121 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP 14122 // Object
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST 14123 // Object
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_SIZE 14124 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_WRITABLE 14125 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_USERWRITABLE 14126 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_OPENCOUNT 14127 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_MIMETYPE 14128 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_OPEN 14129 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_OPEN_INPUTARGUMENTS 14130 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_OPEN_OUTPUTARGUMENTS 14131 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_CLOSE 14132 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_CLOSE_INPUTARGUMENTS 14133 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_READ 14134 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_READ_INPUTARGUMENTS 14135 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_READ_OUTPUTARGUMENTS 14136 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_WRITE 14137 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_WRITE_INPUTARGUMENTS 14138 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_GETPOSITION 14139 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_GETPOSITION_INPUTARGUMENTS 14140 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_GETPOSITION_OUTPUTARGUMENTS 14141 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_SETPOSITION 14142 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_SETPOSITION_INPUTARGUMENTS 14143 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_LASTUPDATETIME 14144 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_OPENWITHMASKS 14145 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_OPENWITHMASKS_INPUTARGUMENTS 14146 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_OPENWITHMASKS_OUTPUTARGUMENTS 14147 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_CLOSEANDUPDATE 14148 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_CLOSEANDUPDATE_INPUTARGUMENTS 14149 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_CLOSEANDUPDATE_OUTPUTARGUMENTS 14150 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_ADDCERTIFICATE 14151 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_ADDCERTIFICATE_INPUTARGUMENTS 14152 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_REMOVECERTIFICATE 14153 // Method
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_TRUSTLIST_REMOVECERTIFICATE_INPUTARGUMENTS 14154 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP_CERTIFICATETYPES 14155 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP 14156 // Object
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_WRITABLE 14157 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_USERWRITABLE 14158 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_MIMETYPE 14159 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_TRUSTLIST_CLOSEANDUPDATE_INPUTARGUMENTS 14160 // Variable
    #define UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP_CERTIFICATETYPES 14161 // Variable
    #define UA_NS0ID_SERVER_SERVERREDUNDANCY_SERVERNETWORKGROUPS 14415 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI 15182 // Object
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_NAMESPACEURI 15183 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_NAMESPACEVERSION 15184 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_NAMESPACEPUBLICATIONDATE 15185 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_ISNAMESPACESUBSET 15186 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_STATICNODEIDTYPES 15187 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_STATICNUMERICNODEIDRANGE 15188 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_STATICSTRINGNODEIDPATTERN 15189 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_NAMESPACEFILE 15190 // Object
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_NAMESPACEFILE_SIZE 15191 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_NAMESPACEFILE_WRITABLE 15192 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_NAMESPACEFILE_USERWRITABLE 15193 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_NAMESPACEFILE_OPENCOUNT 15194 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_NAMESPACEFILE_MIMETYPE 15195 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_NAMESPACEFILE_OPEN 15196 // Method
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_NAMESPACEFILE_OPEN_INPUTARGUMENTS 15197 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_NAMESPACEFILE_OPEN_OUTPUTARGUMENTS 15198 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_NAMESPACEFILE_CLOSE 15199 // Method
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_NAMESPACEFILE_CLOSE_INPUTARGUMENTS 15200 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_NAMESPACEFILE_READ 15201 // Method
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_NAMESPACEFILE_READ_INPUTARGUMENTS 15202 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_NAMESPACEFILE_READ_OUTPUTARGUMENTS 15203 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_NAMESPACEFILE_WRITE 15204 // Method
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_NAMESPACEFILE_WRITE_INPUTARGUMENTS 15205 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_NAMESPACEFILE_GETPOSITION 15206 // Method
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_NAMESPACEFILE_GETPOSITION_INPUTARGUMENTS 15207 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_NAMESPACEFILE_GETPOSITION_OUTPUTARGUMENTS 15208 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_NAMESPACEFILE_SETPOSITION 15209 // Method
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_NAMESPACEFILE_SETPOSITION_INPUTARGUMENTS 15210 // Variable
    #define UA_NS0ID_SERVER_NAMESPACES_OPCUANAMESPACEURI_NAMESPACEFILE_EXPORTNAMESPACE 15211 // Method
    #define UA_NS0ID_HASMODELPARENT 50 // ReferenceType
    
    #define UA_VALUERANK_SCALAR_OR_ONE_DIMENSION  -3
    #define UA_VALUERANK_ANY                      -2
    #define UA_VALUERANK_SCALAR                   -1
    #define UA_VALUERANK_ONE_OR_MORE_DIMENSIONS    0
    #define UA_VALUERANK_ONE_DIMENSION             1
    #define UA_VALUERANK_TWO_DIMENSIONS            2
    #define UA_VALUERANK_THREE_DIMENSIONS          3

    XML节点编译器

    编写应用程序时,使用某些GUI工具创建信息模型会更加舒适。大多数工具可以根据OPC UA Nodeset XML模式导出数据。open62541包含一个基于python的节点集编译器,可以将这些信息模型定义转换为工作服务器。

    请注意,您可以在tools / nodeset_compiler子文件夹中找到的节点集编译器不是 XML转换工具,而是编译器。这意味着它将在解析XML文件时创建内部表示,并尝试理解和验证此表示的正确性,以便生成C代码。

    入门

    我们将以下信息模型片段作为以下教程的起点:

    <UANodeSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:uax="http://opcfoundation.org/UA/2008/02/Types.xsd"
               xmlns="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd"
               xmlns:s1="http://yourorganisation.org/example_nodeset/"
               xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <NamespaceUris>
            <Uri>http://yourorganisation.org/example_nodeset/</Uri>
        </NamespaceUris>
        <Aliases>
            <Alias Alias="Boolean">i=1</Alias>
            <Alias Alias="UInt32">i=7</Alias>
            <Alias Alias="String">i=12</Alias>
            <Alias Alias="HasModellingRule">i=37</Alias>
            <Alias Alias="HasTypeDefinition">i=40</Alias>
            <Alias Alias="HasSubtype">i=45</Alias>
            <Alias Alias="HasProperty">i=46</Alias>
            <Alias Alias="HasComponent">i=47</Alias>
            <Alias Alias="Argument">i=296</Alias>
        </Aliases>
        <Extensions>
            <Extension>
                <ModelInfo Tool="UaModeler" Hash="Zs8w1AQI71W8P/GOk3k/xQ=="
                           Version="1.3.4"/>
            </Extension>
        </Extensions>
        <UAReferenceType NodeId="ns=1;i=4001" BrowseName="1:providesInputTo">
            <DisplayName>providesInputTo</DisplayName>
            <References>
                <Reference ReferenceType="HasSubtype" IsForward="false">
                    i=33
                </Reference>
            </References>
            <InverseName Locale="en-US">inputProcidedBy</InverseName>
        </UAReferenceType>
        <UAObjectType IsAbstract="true" NodeId="ns=1;i=1001"
                      BrowseName="1:FieldDevice">
            <DisplayName>FieldDevice</DisplayName>
            <References>
                <Reference ReferenceType="HasSubtype" IsForward="false">
                    i=58
                </Reference>
                <Reference ReferenceType="HasComponent">ns=1;i=6001</Reference>
                <Reference ReferenceType="HasComponent">ns=1;i=6002</Reference>
            </References>
        </UAObjectType>
        <UAVariable DataType="String" ParentNodeId="ns=1;i=1001"
                    NodeId="ns=1;i=6001" BrowseName="1:ManufacturerName"
                    UserAccessLevel="3" AccessLevel="3">
            <DisplayName>ManufacturerName</DisplayName>
            <References>
                <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
                <Reference ReferenceType="HasModellingRule">i=78</Reference>
                <Reference ReferenceType="HasComponent" IsForward="false">
                    ns=1;i=1001
                </Reference>
            </References>
        </UAVariable>
        <UAVariable DataType="String" ParentNodeId="ns=1;i=1001"
                    NodeId="ns=1;i=6002" BrowseName="1:ModelName"
                    UserAccessLevel="3" AccessLevel="3">
            <DisplayName>ModelName</DisplayName>
            <References>
                <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
                <Reference ReferenceType="HasModellingRule">i=78</Reference>
                <Reference ReferenceType="HasComponent" IsForward="false">
                    ns=1;i=1001
                </Reference>
            </References>
        </UAVariable>
        <UAObjectType NodeId="ns=1;i=1002" BrowseName="1:Pump">
            <DisplayName>Pump</DisplayName>
            <References>
                <Reference ReferenceType="HasComponent">ns=1;i=6003</Reference>
                <Reference ReferenceType="HasComponent">ns=1;i=6004</Reference>
                <Reference ReferenceType="HasSubtype" IsForward="false">
                    ns=1;i=1001
                </Reference>
                <Reference ReferenceType="HasComponent">ns=1;i=7001</Reference>
                <Reference ReferenceType="HasComponent">ns=1;i=7002</Reference>
            </References>
        </UAObjectType>
        <UAVariable DataType="Boolean" ParentNodeId="ns=1;i=1002"
                    NodeId="ns=1;i=6003" BrowseName="1:isOn" UserAccessLevel="3"
                    AccessLevel="3">
            <DisplayName>isOn</DisplayName>
            <References>
                <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
                <Reference ReferenceType="HasModellingRule">i=78</Reference>
                <Reference ReferenceType="HasComponent" IsForward="false">
                    ns=1;i=1002
                </Reference>
            </References>
        </UAVariable>
        <UAVariable DataType="UInt32" ParentNodeId="ns=1;i=1002"
                    NodeId="ns=1;i=6004" BrowseName="1:MotorRPM"
                    UserAccessLevel="3" AccessLevel="3">
            <DisplayName>MotorRPM</DisplayName>
            <References>
                <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
                <Reference ReferenceType="HasModellingRule">i=78</Reference>
                <Reference ReferenceType="HasComponent" IsForward="false">
                    ns=1;i=1002
                </Reference>
            </References>
        </UAVariable>
        <UAMethod ParentNodeId="ns=1;i=1002" NodeId="ns=1;i=7001"
                  BrowseName="1:startPump">
            <DisplayName>startPump</DisplayName>
            <References>
                <Reference ReferenceType="HasModellingRule">i=78</Reference>
                <Reference ReferenceType="HasProperty">ns=1;i=6005</Reference>
                <Reference ReferenceType="HasComponent" IsForward="false">
                    ns=1;i=1002
                </Reference>
            </References>
        </UAMethod>
        <UAVariable DataType="Argument" ParentNodeId="ns=1;i=7001" ValueRank="1"
                    NodeId="ns=1;i=6005" ArrayDimensions="1"
                    BrowseName="OutputArguments">
            <DisplayName>OutputArguments</DisplayName>
            <References>
                <Reference ReferenceType="HasModellingRule">i=78</Reference>
                <Reference ReferenceType="HasProperty"
                           IsForward="false">ns=1;i=7001</Reference>
                <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
            </References>
            <Value>
                <ListOfExtensionObject>
                    <ExtensionObject>
                        <TypeId>
                            <Identifier>i=297</Identifier>
                        </TypeId>
                        <Body>
                            <Argument>
                                <Name>started</Name>
                                <DataType>
                                    <Identifier>i=1</Identifier>
                                </DataType>
                                <ValueRank>-1</ValueRank>
                                <ArrayDimensions></ArrayDimensions>
                                <Description/>
                            </Argument>
                        </Body>
                    </ExtensionObject>
                </ListOfExtensionObject>
            </Value>
        </UAVariable>
        <UAMethod ParentNodeId="ns=1;i=1002" NodeId="ns=1;i=7002"
                  BrowseName="1:stopPump">
            <DisplayName>stopPump</DisplayName>
            <References>
                <Reference ReferenceType="HasModellingRule">i=78</Reference>
                <Reference ReferenceType="HasProperty">ns=1;i=6006</Reference>
                <Reference ReferenceType="HasComponent"
                           IsForward="false">ns=1;i=1002</Reference>
            </References>
        </UAMethod>
        <UAVariable DataType="Argument" ParentNodeId="ns=1;i=7002" ValueRank="1"
                    NodeId="ns=1;i=6006" ArrayDimensions="1"
                    BrowseName="OutputArguments">
            <DisplayName>OutputArguments</DisplayName>
            <References>
                <Reference ReferenceType="HasModellingRule">i=78</Reference>
                <Reference ReferenceType="HasProperty" IsForward="false">
                    ns=1;i=7002
                </Reference>
                <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
            </References>
            <Value>
                <ListOfExtensionObject>
                    <ExtensionObject>
                        <TypeId>
                            <Identifier>i=297</Identifier>
                        </TypeId>
                        <Body>
                            <Argument>
                                <Name>stopped</Name>
                                <DataType>
                                    <Identifier>i=1</Identifier>
                                </DataType>
                                <ValueRank>-1</ValueRank>
                                <ArrayDimensions></ArrayDimensions>
                                <Description/>
                            </Argument>
                        </Body>
                    </ExtensionObject>
                </ListOfExtensionObject>
            </Value>
        </UAVariable>
    </UANodeSet>

    获取上一个代码段并将其保存到文件中myNS.xml。要将此节点集编译为相应的C代码(然后可以由open62541堆栈使用),节点集编译器在调用它时需要一些参数。help命令的输出为您提供以下信息:

    $ python ./nodeset_compiler.py -h
    usage: nodeset_compiler.py [-h] [-e <existingNodeSetXML>] [-x <nodeSetXML>]
                               [--generate-ns0] [--internal-headers]
                               [-b <blacklistFile>] [-i <ignoreFile>]
                               [-t <typesArray>]
                               [--max-string-length MAX_STRING_LENGTH] [-v]
                               <outputFile>
    
    positional arguments:
      <outputFile>          The path/basename for the <output file>.c and <output
                            file>.h files to be generated. This will also be the
                            function name used in the header and c-file.
    
    optional arguments:
      -h, --help            show this help message and exit
      -e <existingNodeSetXML>, --existing <existingNodeSetXML>
                            NodeSet XML files with nodes that are already present
                            on the server.
      -x <nodeSetXML>, --xml <nodeSetXML>
                            NodeSet XML files with nodes that shall be generated.
      --generate-ns0        Omit some consistency checks for bootstrapping
                            namespace 0, create references to parents and type
                            definitions manually
      --internal-headers    Include internal headers instead of amalgamated header
      -b <blacklistFile>, --blacklist <blacklistFile>
                            Loads a list of NodeIDs stored in blacklistFile (one
                            NodeID per line). Any of the nodeIds encountered in
                            this file will be removed from the nodeset prior to
                            compilation. Any references to these nodes will also
                            be removed
      -i <ignoreFile>, --ignore <ignoreFile>
                            Loads a list of NodeIDs stored in ignoreFile (one
                            NodeID per line). Any of the nodeIds encountered in
                            this file will be kept in the nodestore but not
                            printed in the generated code
      -t <typesArray>, --types-array <typesArray>
                            Types array for the given namespace. Can be used
                            mutliple times to define (in the same order as the
                            .xml files, first for --existing, then --xml) the type
                            arrays
      --max-string-length MAX_STRING_LENGTH
                            Maximum allowed length of a string literal. If longer,
                            it will be set to an empty string
      -v, --verbose         Make the script more verbose. Can be applied up to 4
                            times

    结果调用如下所示:

    $ python ./nodeset_compiler.py --types-array=UA_TYPES --existing ../../deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml --xml myNS.xml myNS

    并输出命令:

    INFO:__main__:Preprocessing (existing) ../../deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
    INFO:__main__:Preprocessing myNS.xml
    INFO:__main__:Generating Code
    INFO:__main__:NodeSet generation code successfully printed

    第一个参数--types-array=UA_TYPES定义open62541中全局数组的名称,该名称包含节点集中使用的相应类型NodeSet2.xml。如果您没有定义自己的数据类型,则始终可以使用该UA_TYPES值。本教程稍后将详细介绍。下一个参数指向标准定义的名称空间0(NS0)的XML定义。假定命名空间0预先加载,并提供数据类型,引用类型等的定义。由于我们在myNS.xml中引用NS0中的节点,我们需要告诉节点集编译器它还应该加载该节点集,而不是将其编译到输出中。请注意,您可能需要初始化git子模块以获取文件夹()或手动下载完整文件。争论--existing ../../deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xmldeps/ua-nodesetgit submodule --init --updateNodeSet2.xml--xml myNS.xml指向用户定义的信息模型,其节点将添加到抽象语法树中。然后,该脚本将创建文件myNS.cmyNS.h(由最后一个参数指示myNS),其中包含实例化这些名称空间所需的C代码。

    尽管可以通过这种方式运行编译器,但我们强烈建议不要这样做。如果您想检查CMakeLists.txt(examples / nodeset / CMakeLists.txt),您会发现该文件server_nodeset.xml是使用以下命令编译的:

    COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
     --types-array=UA_TYPES
     --existing ${UA_FILE_NS0}
     --xml ${PROJECT_SOURCE_DIR}/examples/nodeset/server_nodeset.xml
     ${PROJECT_BINARY_DIR}/src_generated/example_nodeset

    如果查看nodeset编译器生成的文件,您将看到它生成了一个名为的方法。您需要包含头文件和源文件,然后在创建服务器实例后立即调用该方法。这将自动将所有节点添加到服务器,如果没有任何错误则返回。此外,您需要通过在CMake中设置来编译具有完整NS0的open62541堆栈。否则,堆栈使用最小子集,其中不包括许多节点,因此添加自定义节点集可能会失败。

    extern UA_StatusCode myNS(UA_Server *server);myNS(server)UA_Server_newUA_STATUSCODE_GOODUA_ENABLE_FULL_NS0=ON

    这是您可以使用nodeset编译器编译open62541堆栈使用的简单NodeSet XML的方法。

    创建对象实例

    定义对象类型的一个主要好处是能够非常容易地创建对象实例。当typedefinition NodeId指向有效的ObjectType节点时,将自动处理对象实例化。objectType定义中包含的所有属性和方法将与对象节点一起实例化。

    虽然变量是从objetType定义复制的(例如允许用户将新的dataSource附加到它们),但方法始终只是链接的。这个范例与C ++这样的语言完全相同:调用的方法始终是同一段代码,但第一个参数是指向对象的指针。同样,在OPC UA中,只能将一个methodCallback附加到特定的methodNode。如果调用了该methodNode,那么父objectId将被传递给方法 - 在那一刻,derefence它所属的对象实例是方法作业。

    让我们看一个示例,该示例将根据myNS.xml中新定义的objectType创建一个泵实例:

    /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
     * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
    
    #include <signal.h>
    #include <stdio.h>
    #include "open62541.h"
    
    /* Files myNS.h and myNS.c are created from myNS.xml */
    #include "myNS.h"
    
    UA_Boolean running = true;
    
    static void stopHandler(int sign) {
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
        running = false;
    }
    
    int main(int argc, char **argv) {
        signal(SIGINT, stopHandler);
        signal(SIGTERM, stopHandler);
    
        UA_ServerConfig *config = UA_ServerConfig_new_default();
        UA_Server *server = UA_Server_new(config);
    
        UA_StatusCode retval;
        /* create nodes from nodeset */
        if (myNS(server) != UA_STATUSCODE_GOOD) {
            UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Could not add the example nodeset. "
                "Check previous output for any error.");
            retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
        } else {
    
    
            UA_NodeId createdNodeId;
            UA_ObjectAttributes object_attr = UA_ObjectAttributes_default;
    
            object_attr.description = UA_LOCALIZEDTEXT("en-US", "A pump!");
            object_attr.displayName = UA_LOCALIZEDTEXT("en-US", "Pump1");
    
            // we assume that the myNS nodeset was added in namespace 2.
            // You should always use UA_Server_addNamespace to check what the
            // namespace index is for a given namespace URI. UA_Server_addNamespace
            // will just return the index if it is already added.
            UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 0),
                                    UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                                    UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
                                    UA_QUALIFIEDNAME(1, "Pump1"),
                                    UA_NODEID_NUMERIC(2, 1002),
                                    object_attr, NULL, &createdNodeId);
    
    
            retval = UA_Server_run(server, &running);
        }
        UA_Server_delete(server);
        UA_ServerConfig_delete(config);
        return (int) retval;
    }

    确保已更新项目中的标头和库,然后重新编译并运行服务器。尤其要确保已添加myNS.h到include文件夹中。

    正如您所看到的,实例化对象与创建对象节点没有太大区别。主要区别在于您必须使用objectType节点作为typeDefinition。

    如果启动服务器并使用UA Expert检查节点,您将在objects文件夹中找到泵,如图2所示

    具有继承子项的实例化泵对象

    图2 具有继承子项的实例化泵对象

    正如您所看到的,泵继承了它的父属性(ManufacturerName和ModelName)。与对象和变量相比,方法从不克隆,而只是链接。原因是您可以将方法回调附加到中央方法,而不是每个对象。如果对象位于您正在创建的对象下方,则会对其进行实例化,因此作为泵的一部分的任何对象(如名为associatedServer ofServerType的对象)也将被实例化。您对象上方的对象永远不会被实例化,因此Fielddevices中的相同ServerType对象将被省略(原因是递归实例化函数保护自己免受无限递归的影响,这些递归在首次提升时很难跟踪,然后重新下载到树中)。

    多个节点集的组合

    在上一节中,您已经了解了如何将节点集编译器与单个节点集一起使用,该节点集取决于默认节点集(NS0)Opc.Ua.NodeSet2.xml。节点集编译器还支持依赖于多个节点集的节点集。我们将使用PLCopen节点集显示此用例。PLCopen节点集Opc.Ua.Plc.NodeSet2.xml取决于DI节点集Opc.Ua.Di.NodeSet2.xml,然后依赖于NS0。这个例子也显示在examples/nodeset/CMakeLists.txt

    对于我们依赖的每个节点集,我们需要从下到上调用节点集编译器。第一个节点集是NS0。如果UA_ENABLE_FULL_NS0=ON在CMake中启用,则由堆栈自动创建。所以我们不需要关心这一点。如果查看主CMakeLists.txt,可以找到对nodeset编译器的相应调用。它使用一些额外的标志,这些标志只应用于NS0,而其他节点集则不需要。

    列表中的下一个节点集是DI节点集。此节点集定义了一些其他数据类型deps/ua-nodeset/DI/Opc.Ua.Di.Types.bsd。由于我们在生成的代码中也需要这些类型,我们首先需要将类型编译成C代码。生成的代码主要是编码和解码所需类型的二进制表示的定义。可以使用tools/generate_datatypes.py脚本完成生成:

    COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py
    --namespace=2
    --type-csv=${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/OpcUaDiModel.csv
    --type-bsd=${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.Types.bsd
    --no-builtin
    ${PROJECT_BINARY_DIR}/src_generated/ua_types_di

    namespace参数指示生成的类型定义的节点ID的名称空间索引。目前,我们需要依赖于命名空间也添加到最终服务器中的此位置。目前还没有自动推断(热烈欢迎拉动请求)。CSV和BSD文件包含类型的元数据和定义。该--no-builtin参数告诉脚本跳过始终包含在堆栈中的内部数据类型。最后一个参数是输出文件,同时是types数组的名称:UA_TYPES_DI

    现在,您可以使用以下命令编译DI节点集XML:

    COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
    --internal-headers
    --types-array=UA_TYPES
    --types-array=UA_TYPES_DI
    --existing ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
    --xml ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
    ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_di

    现在有两个新参数:--internal-headers表示内部头文件(和非公共API)应包含在生成的源代码中。这对于使用结构作为数据值的节点集当前是必需的,并且将来可能会被修复。然后你看到两次--types-array论证。types数组参数与节点集的匹配顺序与它们在命令行中出现的顺序相同:首先是existing1,然后是xml。它告诉nodeset编译器它应该使用哪些类型的数组:UA_TYPESfor Opc.Ua.NodeSet2.xmlUA_TYPES_DIfor Opc.Ua.Di.NodeSet2.xml。这是generate_datatypes.py脚本生成的类型数组。其余部分类似于上一节中的示例:Opc.Ua.NodeSet2.xml假设已经存在并且仅需要加载以进行一致性检查,Opc.Ua.Di.NodeSet2.xml 将在输出文件中生成 ua_namespace_di.c/.h

    接下来我们可以生成PLCopen节点集。由于它不需要任何其他数据类型定义,我们可以立即从nodeset编译器命令开始:

    COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
    --internal-headers
    --types-array=UA_TYPES
    --types-array=UA_TYPES_DI
    # PLCopen has no specific type definition, thus use the default UA_TYPES to ignore it
    --types-array=UA_TYPES
    --existing ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
    --existing ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
    --xml ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/PLCopen/Opc.Ua.Plc.NodeSet2.xml
    ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_plc

    此调用与DI节点集的编译非常相似。如您所见,我们没有为PLCopen节点集定义任何特定类型的数组,而只是UA_TYPES用来忽略它。由于PLCopen节点集依赖于NS0和DI节点集,我们需要告诉节点集编译器这两个节点集应该被视为已经存在。确保订单与XML文件中的顺序相同,例如,在此情况下指示的顺序。Opc.Ua.Plc.NodeSet2.xml -> UANodeSet -> Models -> Model

    作为以前脚本的结果,您将拥有多个源文件:

    • ua_types_di_generated.c
    • ua_types_di_generated.h
    • ua_types_di_generated_encoding_binary.h
    • ua_types_di_generated_handling.h
    • ua_namespace_di.c
    • ua_namespace_di.h
    • ua_namespace_plc.c
    • ua_namespace_plc.h

    最后,您需要在构建过程中包含所有这些文件,并为节点集调用相应的初始化方法。示例应用程序可能如下所示:

    UA_ServerConfig *config = UA_ServerConfig_new_default();
    UA_Server *server = UA_Server_new(config);
    
    /* create nodes from nodeset */
    UA_StatusCode retval = ua_namespace_di(server);
    if(retval != UA_STATUSCODE_GOOD) {
        UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Adding the DI namespace failed. Please check previous error output.");
        UA_Server_delete(server);
        UA_ServerConfig_delete(config);
        return (int)UA_STATUSCODE_BADUNEXPECTEDERROR;
    }
    retval |= ua_namespace_plc(server);
    if(retval != UA_STATUSCODE_GOOD) {
        UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Adding the PLCopen namespace failed. Please check previous error output.");
        UA_Server_delete(server);
        UA_ServerConfig_delete(config);
        return (int)UA_STATUSCODE_BADUNEXPECTEDERROR;
    }
    
    retval = UA_Server_run(server, &running);

    网络插件

    连接

    客户端 - 服务器连接由UA_Connection表示。连接是有状态的,并存储部分接收的消息,依此类推。此外,连接包含指向底层网络实现的函数指针。一个例子是send函数。因此,连接封装了所有必需的网络功能。这使得嵌入式(或其他异域)系统上的用户可以实现自己的网络插件,并具有与主open62541库的清晰接口。

    typedef struct {
        UA_UInt32 protocolVersion;
        UA_UInt32 sendBufferSize;
        UA_UInt32 recvBufferSize;
        UA_UInt32 maxMessageSize;
        UA_UInt32 maxChunkCount;
    } UA_ConnectionConfig;
    
    typedef enum {
        UA_CONNECTION_CLOSED,      /* The socket has been closed and the connection
                                    * will be deleted */
        UA_CONNECTION_OPENING,     /* The socket is open, but the HEL/ACK handshake
                                    * is not done */
        UA_CONNECTION_ESTABLISHED  /* The socket is open and the connection
                                    * configured */
    } UA_ConnectionState;
    
    struct UA_Connection {
        UA_ConnectionState state;
        UA_ConnectionConfig localConf;
        UA_ConnectionConfig remoteConf;
        UA_SecureChannel *channel;       /* The securechannel that is attached to
                                          * this connection */
        UA_Int32 sockfd;                 /* Most connectivity solutions run on
                                          * sockets. Having the socket id here
                                          * simplifies the design. */
        UA_DateTime openingDate;         /* The date the connection was created */
        void *handle;                    /* A pointer to internal data */
        UA_ByteString incompleteMessage; /* A half-received chunk (TCP is a
                                          * streaming protocol) is stored here */
    
        /* Get a buffer for sending */
        UA_StatusCode (*getSendBuffer)(UA_Connection *connection, size_t length,
                                       UA_ByteString *buf);
    
        /* Release the send buffer manually */
        void (*releaseSendBuffer)(UA_Connection *connection, UA_ByteString *buf);
    
        /* Sends a message over the connection. The message buffer is always freed,
         * even if sending fails.
         *
         * @param connection The connection
         * @param buf The message buffer
         * @return Returns an error code or UA_STATUSCODE_GOOD. */
        UA_StatusCode (*send)(UA_Connection *connection, UA_ByteString *buf);
    
        /* Receive a message from the remote connection
         *
         * @param connection The connection
         * @param response The response string. It is allocated by the connection
         *        and needs to be freed with connection->releaseBuffer
         * @param timeout Timeout of the recv operation in milliseconds
         * @return Returns UA_STATUSCODE_BADCOMMUNICATIONERROR if the recv operation
         *         can be repeated, UA_STATUSCODE_GOOD if it succeeded and
         *         UA_STATUSCODE_BADCONNECTIONCLOSED if the connection was
         *         closed. */
        UA_StatusCode (*recv)(UA_Connection *connection, UA_ByteString *response,
                              UA_UInt32 timeout);
    
        /* Release the buffer of a received message */
        void (*releaseRecvBuffer)(UA_Connection *connection, UA_ByteString *buf);
    
        /* Close the connection. The network layer closes the socket. This is picked
         * up during the next 'listen' and the connection is freed in the network
         * layer. */
        void (*close)(UA_Connection *connection);
    
        /* To be called only from within the server (and not the network layer).
         * Frees up the connection's memory. */
        void (*free)(UA_Connection *connection);
    
        /* A message has not been processed yet */
        UA_Boolean pendingMessage;
    };
    
    /* Cleans up half-received messages, and so on. Called from connection->free. */
    void
    UA_Connection_deleteMembers(UA_Connection *connection);

    服务器网络层

    服务器公开两个与远程客户端交互的函数: processBinaryMessage和removeConnection。这些功能由服务器网络层调用。

    服务器网络层的工作是监听TCP套接字,接受新连接,使用收到的消息调用服务器并发信号通知与服务器的关闭连接。

    网络层是服务器配置的一部分。因此,如果提供的示例不适合其架构,则用户可以提供自定义实现。仅从服务器的主循环调用网络层。因此网络层不需要是线程安全的。如果网络层收到阻止侦听的正持续时间,则服务器的主循环将阻塞,直到收到消息或持续时间超时。

    /* Process a binary message (TCP packet). The message can contain partial
     * chunks. (TCP is a streaming protocol and packets may be split/merge during
     * transport.) After processing, the message is freed with
     * connection->releaseRecvBuffer. */
    void
    UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection,
                                   UA_ByteString *message);
    
    /* The server internally cleans up the connection and then calls
     * connection->free. */
    void
    UA_Server_removeConnection(UA_Server *server, UA_Connection *connection);
    
    struct UA_ServerNetworkLayer {
        void *handle; /* Internal data */
        UA_String discoveryUrl;
    
        /* Start listening on the networklayer.
         *
         * @param nl The network layer
         * @return Returns UA_STATUSCODE_GOOD or an error code. */
        UA_StatusCode (*start)(UA_ServerNetworkLayer *nl, const UA_String *customHostname);
    
        /* Listen for new and closed connections and arriving packets. Calls
         * UA_Server_processBinaryMessage for the arriving packets. Closed
         * connections are picked up here and forwarded to
         * UA_Server_removeConnection where they are cleaned up and freed.
         *
         * @param nl The network layer
         * @param server The server for processing the incoming packets and for
         *               closing connections.
         * @param timeout The timeout during which an event must arrive in
         *                milliseconds
         * @return A statuscode for the status of the network layer. */
        UA_StatusCode (*listen)(UA_ServerNetworkLayer *nl, UA_Server *server,
                                UA_UInt16 timeout);
    
        /* Close the network socket and all open connections. Afterwards, the
         * network layer can be safely deleted.
         *
         * @param nl The network layer
         * @param server The server that processes the incoming packets and for
         *               closing connections before deleting them.
         * @return A statuscode for the status of the closing operation. */
        void (*stop)(UA_ServerNetworkLayer *nl, UA_Server *server);
    
        /* Deletes the network layer context. Call only after stopping. */
        void (*deleteMembers)(UA_ServerNetworkLayer *nl);
    };

    客户端网络层

    客户端只有一个连接用于发送和接收二进制消息。

    /* @param localConf the connection config for this client
     * @param endpointUrl to where to connect
     * @param timeout in ms until the connection try times out if remote not reachable
     * @param logger the logger to use */
    typedef UA_Connection
    (*UA_ConnectClientConnection)(UA_ConnectionConfig localConf, const char *endpointUrl,
                                  const UA_UInt32 timeout, UA_Logger logger);

    端点URL解析器

    端点URL解析器通常用于实现网络层插件。

    /* Split the given endpoint url into hostname, port and path. All arguments must
     * be non-NULL. EndpointUrls have the form "opc.tcp://hostname:port/path", port
     * and path may be omitted (together with the prefix colon and slash).
     *
     * @param endpointUrl The endpoint URL.
     * @param outHostname Set to the parsed hostname. The string points into the
     *        original endpointUrl, so no memory is allocated. If an IPv6 address is
     *        given, hostname contains e.g. '[2001:0db8:85a3::8a2e:0370:7334]'
     * @param outPort Set to the port of the url or left unchanged.
     * @param outPath Set to the path if one is present in the endpointUrl.
     *        Starting or trailing '/' are NOT included in the path. The string
     *        points into the original endpointUrl, so no memory is allocated.
     * @return Returns UA_STATUSCODE_BADTCPENDPOINTURLINVALID if parsing failed. */
    UA_StatusCode
    UA_parseEndpointUrl(const UA_String *endpointUrl, UA_String *outHostname,
                        UA_UInt16 *outPort, UA_String *outPath);

    访问控制插件

    访问控制回调用于验证会话并相应地授予访问权限。

    struct UA_AccessControl;
    typedef struct UA_AccessControl UA_AccessControl;
    
    struct UA_AccessControl {
        void *context;
        void (*deleteMembers)(UA_AccessControl *ac);
    
        /* Supported login mechanisms. The server endpoints are created from here. */
        size_t userTokenPoliciesSize;
        UA_UserTokenPolicy *userTokenPolicies;
    
        /* Authenticate a session. The session context is attached to the session and
         * later passed into the node-based access control callbacks. */
        UA_StatusCode (*activateSession)(UA_Server *server, UA_AccessControl *ac,
                                         const UA_NodeId *sessionId,
                                         const UA_ExtensionObject *userIdentityToken,
                                         void **sessionContext);
    
        /* Deauthenticate a session and cleanup */
        void (*closeSession)(UA_Server *server, UA_AccessControl *ac,
                             const UA_NodeId *sessionId, void *sessionContext);
    
        /* Access control for all nodes*/
        UA_UInt32 (*getUserRightsMask)(UA_Server *server, UA_AccessControl *ac,
                                       const UA_NodeId *sessionId, void *sessionContext,
                                       const UA_NodeId *nodeId, void *nodeContext);
    
        /* Additional access control for variable nodes */
        UA_Byte (*getUserAccessLevel)(UA_Server *server, UA_AccessControl *ac,
                                      const UA_NodeId *sessionId, void *sessionContext,
                                      const UA_NodeId *nodeId, void *nodeContext);
    
        /* Additional access control for method nodes */
        UA_Boolean (*getUserExecutable)(UA_Server *server, UA_AccessControl *ac,
                                        const UA_NodeId *sessionId, void *sessionContext,
                                        const UA_NodeId *methodId, void *methodContext);
    
        /* Additional access control for calling a method node in the context of a
         * specific object */
        UA_Boolean (*getUserExecutableOnObject)(UA_Server *server, UA_AccessControl *ac,
                                                const UA_NodeId *sessionId, void *sessionContext,
                                                const UA_NodeId *methodId, void *methodContext,
                                                const UA_NodeId *objectId, void *objectContext);
    
        /* Allow adding a node */
        UA_Boolean (*allowAddNode)(UA_Server *server, UA_AccessControl *ac,
                                   const UA_NodeId *sessionId, void *sessionContext,
                                   const UA_AddNodesItem *item);
    
        /* Allow adding a reference */
        UA_Boolean (*allowAddReference)(UA_Server *server, UA_AccessControl *ac,
                                        const UA_NodeId *sessionId, void *sessionContext,
                                        const UA_AddReferencesItem *item);
    
        /* Allow deleting a node */
        UA_Boolean (*allowDeleteNode)(UA_Server *server, UA_AccessControl *ac,
                                      const UA_NodeId *sessionId, void *sessionContext,
                                      const UA_DeleteNodesItem *item);
    
        /* Allow deleting a reference */
        UA_Boolean (*allowDeleteReference)(UA_Server *server, UA_AccessControl *ac,
                                           const UA_NodeId *sessionId, void *sessionContext,
                                           const UA_DeleteReferencesItem *item);
    };

    记录插件

    服务器和客户端必须在其配置中定义记录器。记录器只是一个函数指针。每条日志消息都包含日志级别,日志类别和字符串消息内容。日志消息的时间戳在记录器中创建。

    typedef enum {
        UA_LOGLEVEL_TRACE,
        UA_LOGLEVEL_DEBUG,
        UA_LOGLEVEL_INFO,
        UA_LOGLEVEL_WARNING,
        UA_LOGLEVEL_ERROR,
        UA_LOGLEVEL_FATAL
    } UA_LogLevel;
    
    typedef enum {
        UA_LOGCATEGORY_NETWORK,
        UA_LOGCATEGORY_SECURECHANNEL,
        UA_LOGCATEGORY_SESSION,
        UA_LOGCATEGORY_SERVER,
        UA_LOGCATEGORY_CLIENT,
        UA_LOGCATEGORY_USERLAND,
        UA_LOGCATEGORY_SECURITYPOLICY
    } UA_LogCategory;

    消息字符串和后续的varargs根据printf命令的规则进行格式化。不要直接调用记录器。相反,请考虑使用ua_config.h中定义的最小日志级别的便捷宏。

    typedef void (*UA_Logger)(UA_LogLevel level, UA_LogCategory category,
                              const char *msg, va_list args);
    
    static UA_INLINE UA_FORMAT(3,4) void
    UA_LOG_TRACE(UA_Logger logger, UA_LogCategory category, const char *msg, ...) {
    #if UA_LOGLEVEL <= 100
        va_list args; va_start(args, msg);
        logger(UA_LOGLEVEL_TRACE, category, msg, args);
        va_end(args);
    #endif
    }
    
    static UA_INLINE UA_FORMAT(3,4) void
    UA_LOG_DEBUG(UA_Logger logger, UA_LogCategory category, const char *msg, ...) {
    #if UA_LOGLEVEL <= 200
        va_list args; va_start(args, msg);
        logger(UA_LOGLEVEL_DEBUG, category, msg, args);
        va_end(args);
    #endif
    }
    
    static UA_INLINE UA_FORMAT(3,4) void
    UA_LOG_INFO(UA_Logger logger, UA_LogCategory category, const char *msg, ...) {
    #if UA_LOGLEVEL <= 300
        va_list args; va_start(args, msg);
        logger(UA_LOGLEVEL_INFO, category, msg, args);
        va_end(args);
    #endif
    }
    
    static UA_INLINE UA_FORMAT(3,4) void
    UA_LOG_WARNING(UA_Logger logger, UA_LogCategory category, const char *msg, ...) {
    #if UA_LOGLEVEL <= 400
        va_list args; va_start(args, msg);
        logger(UA_LOGLEVEL_WARNING, category, msg, args);
        va_end(args);
    #endif
    }
    
    static UA_INLINE UA_FORMAT(3,4) void
    UA_LOG_ERROR(UA_Logger logger, UA_LogCategory category, const char *msg, ...) {
    #if UA_LOGLEVEL <= 500
        va_list args; va_start(args, msg);
        logger(UA_LOGLEVEL_ERROR, category, msg, args);
        va_end(args);
    #endif
    }
    
    static UA_INLINE UA_FORMAT(3,4) void
    UA_LOG_FATAL(UA_Logger logger, UA_LogCategory category, const char *msg, ...) {
    #if UA_LOGLEVEL <= 600
        va_list args; va_start(args, msg);
        logger(UA_LOGLEVEL_FATAL, category, msg, args);
        va_end(args);
    #endif
    }

    复杂类型的便捷宏

    #define UA_PRINTF_GUID_FORMAT "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x"
    #define UA_PRINTF_GUID_DATA(GUID) (GUID).data1, (GUID).data2, (GUID).data3, \
            (GUID).data4[0], (GUID).data4[1], (GUID).data4[2], (GUID).data4[3], \
            (GUID).data4[4], (GUID).data4[5], (GUID).data4[6], (GUID).data4[7]
    
    #define UA_PRINTF_STRING_FORMAT "\"%.*s\""
    #define UA_PRINTF_STRING_DATA(STRING) (int)(STRING).length, (STRING).data

     

    展开全文
  • 本节主要分析avformat_open_input:打开输入流的过程中所做的操作。 源码解析 首先我们先给出avformat_open_input的完整代码及详解注释(代码中标注的两个TODO的解析在后面): avformat_open_input /** * Open an...

    写在前面

    本节主要分析avformat_open_input:打开输入流的过程中所做的操作。


    源码解析

    首先我们先给出avformat_open_input的完整代码及详解注释(代码中标注的两个TODO的解析在后面):

    avformat_open_input

    /**
     * Open an input stream and read the header. The codecs are not opened.
     * The stream must be closed with avformat_close_input().
     * 打开输入流并读取头部。 编解码器未打开。
     * 必须使用avformat_close_input()关闭该流。
     *
     * @param ps Pointer to user-supplied AVFormatContext (allocated by avformat_alloc_context).
     *           May be a pointer to NULL, in which case an AVFormatContext is allocated by this
     *           function and written into ps.
     *           Note that a user-supplied AVFormatContext will be freed on failure.
     *           指向用户提供的AVFormatContext的指针(由avformat_alloc_context分配)。
     *           可能是指向NULL的指针,在这种情况下,AVFormatContext由此函数分配并写入ps。
     *           请注意,用户提供的AVFormatContext将在失败时释放。
     *
     * @param url URL of the stream to open.
     *            要打开的流的URL。
     *
     * @param fmt If non-NULL, this parameter forces a specific input format.
     *            Otherwise the format is autodetected.
     *            如果为非NULL,则此参数强制使用特定的输入格式。
     *            否则,格式将被自动检测。
     *
     * @param options  A dictionary filled with AVFormatContext and demuxer-private options.
     *                 On return this parameter will be destroyed and replaced with a dict containing
     *                 options that were not found. May be NULL.
     *                 一个充满AVFormatContext和demuxer-private选项的字典。
     *                 返回时,此参数将被销毁并替换为包含未找到选项的字典。 可能是NULL。
     *
     * @return 0 on success, a negative AVERROR on failure.
     *
     * @note If you want to use custom IO, preallocate the format context and set its pb field.
     *       如果要使用自定义IO,请预先分配格式上下文并设置其pb字段。
     */
    int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);
    
    int avformat_open_input(AVFormatContext **ps, const char *filename,
                            ff_const59 AVInputFormat *fmt, AVDictionary **options)
    {
        AVFormatContext *s = *ps;
        int i, ret = 0;
        AVDictionary *tmp = NULL;
        // ID3标签是MP3音乐档案中的歌曲附加讯息,它能够在MP3中附加曲子的演出者、作者以及其它类别资讯,方便众多乐曲的管理。
        // 缺少ID3标签并不会影响 MP3的播放,但若没有的话,管理音乐文件也会相当的麻烦。
        ID3v2ExtraMeta *id3v2_extra_meta = NULL;
    
        // 分配一个AVFormatContext TODO:1
        if (!s && !(s = avformat_alloc_context()))
            return AVERROR(ENOMEM);
        if (!s->av_class) {
            av_log(NULL, AV_LOG_ERROR, "Input context has not been properly allocated by avformat_alloc_context() and is not NULL either\n");
            return AVERROR(EINVAL);
        }
    
        // 输入容器格式
        if (fmt)
            s->iformat = fmt;
    
        if (options)
            /**
             * Copy entries from one AVDictionary struct into another.
             * 将条目从一个AVDictionary结构复制到另一个。
             *
             * @param dst pointer to a pointer to a AVDictionary struct. If *dst is NULL,
             *            this function will allocate a struct for you and put it in *dst
             *            指向AVDictionary结构的指针。 如果* dst为NULL,则此函数将重新分配一个结构并将其放入* dst
             *
             * @param src pointer to source AVDictionary struct
             *            指向源AVDictionary结构的指针
             *
             * @param flags flags to use when setting entries in *dst
             *              在* dst中设置条目时使用的标志
             *
             * @note metadata is read using the AV_DICT_IGNORE_SUFFIX flag
             *       使用AV_DICT_IGNORE_SUFFIX标志读取元数据
             *
             * @return 0 on success, negative AVERROR code on failure. If dst was allocated
             *           by this function, callers should free the associated memory.
             *           如果dst由此函数分配,则调用者应释放相关的内存。
             */
            // int av_dict_copy(AVDictionary **dst, const AVDictionary *src, int flags);
            av_dict_copy(&tmp, *options, 0);
    
        if (s->pb) // must be before any goto fail
            /**
             * I/O context.
             *
             * - demuxing: either set by the user before avformat_open_input() (then
             *             the user must close it manually) or set by avformat_open_input().
             * - muxing: set by the user before avformat_write_header(). The caller must
             *           take care of closing / freeing the IO context.
             *
             * Do NOT set this field if AVFMT_NOFILE flag is set in
             * iformat/oformat.flags. In such a case, the (de)muxer will handle
             * I/O in some other way and this field will be NULL.
             */
            // AVIOContext *pb;
            s->flags |= AVFMT_FLAG_CUSTOM_IO;
    
        if ((ret = av_opt_set_dict(s, &tmp)) < 0)
            /**
             * Set all the options from a given dictionary on an object.
             * 设置对象上给定字典的所有选项。
             *
             * @param obj a struct whose first element is a pointer to AVClass
             *            一个struct,其第一个元素是指向AVClass的指针
             *
             * @param options options to process. This dictionary will be freed and replaced
             *                by a new one containing all options not found in obj.
             *                Of course this new dictionary needs to be freed by caller
             *                with av_dict_free().
             *                需要处理的选项。 该字典将被释放并替换为包含obj中未找到的所有选项的新字典。
             *                当然这个新词典需要被调用者用av_dict_free()释放。
             *
             * @return 0 on success, a negative AVERROR if some option was found in obj,
             *         but could not be set.
             *
             * @see av_dict_copy()
             */
            // int av_opt_set_dict(void *obj, struct AVDictionary **options);
            goto fail;
    
        if (!(s->url = av_strdup(filename ? filename : ""))) {
            // 参考资料:https://blog.csdn.net/ice_ly000/article/details/90510494
            /**
             * Duplicate a string.
             *
             * //入参s指向需要拷贝的字符串
             * @param s String to be duplicated
             *
             * //返回一个指向新分配的内存,该内存拷贝了一份字符串,如果无法分配出空间,则返回NULL
             * @return Pointer to a newly-allocated string containing a
             *         copy of `s` or `NULL` if the string cannot be allocated
             * @see av_strndup()
             */
            // char *av_strdup(const char *s) av_malloc_attrib;
            goto fail;
        }
    
    #if FF_API_FORMAT_FILENAME
    FF_DISABLE_DEPRECATION_WARNINGS
        av_strlcpy(s->filename, filename ? filename : "", sizeof(s->filename));
    FF_ENABLE_DEPRECATION_WARNINGS
    #endif
        // 打开输入文件,并在必要时探查格式 TODO:2
        if ((ret = init_input(s, filename, &tmp)) < 0)
            goto fail;
        s->probe_score = ret;
    
        // 拷贝白名单
        if (!s->protocol_whitelist && s->pb && s->pb->protocol_whitelist) {
            s->protocol_whitelist = av_strdup(s->pb->protocol_whitelist);
            if (!s->protocol_whitelist) {
                ret = AVERROR(ENOMEM);
                goto fail;
            }
        }
    
        // 拷贝黑名单
        if (!s->protocol_blacklist && s->pb && s->pb->protocol_blacklist) {
            s->protocol_blacklist = av_strdup(s->pb->protocol_blacklist);
            if (!s->protocol_blacklist) {
                ret = AVERROR(ENOMEM);
                goto fail;
            }
        }
    
        // 校验当前要使用的格式是否在白名单上
        if (s->format_whitelist && av_match_list(s->iformat->name, s->format_whitelist, ',') <= 0) {
            av_log(s, AV_LOG_ERROR, "Format not on whitelist \'%s\'\n", s->format_whitelist);
            ret = AVERROR(EINVAL);
            goto fail;
        }
    
        // int64_t avio_skip(AVIOContext *s,int64_t offset)
        // Skip given number of bytes forward.
        //
        // Returns
        // new position or AVERROR.
        avio_skip(s->pb, s->skip_initial_bytes);
    
        /* Check filename in case an image number is expected. */
        // 来自于avformat.h的宏定义:AVFMT_NEEDNUMBER    0x0002 /**< Needs '%d' in filename. */
        if (s->iformat->flags & AVFMT_NEEDNUMBER) {
            if (!av_filename_number_test(filename)) {
                ret = AVERROR(EINVAL);
                goto fail;
            }
        }
    
        s->duration = s->start_time = AV_NOPTS_VALUE;
    
        /* Allocate private data. */
        // 为AVInputFormat中的私有数据开辟空间
        if (s->iformat->priv_data_size > 0) {
            if (!(s->priv_data = av_mallocz(s->iformat->priv_data_size))) {
                ret = AVERROR(ENOMEM);
                goto fail;
            }
            if (s->iformat->priv_class) {
                *(const AVClass **) s->priv_data = s->iformat->priv_class;
                av_opt_set_defaults(s->priv_data);
                if ((ret = av_opt_set_dict(s->priv_data, &tmp)) < 0)
                    goto fail;
            }
        }
    
        /* e.g. AVFMT_NOFILE formats will not have a AVIOContext */
        if (s->pb)
            // 将ID3v2标签读入指定的词典并检索支持的额外元数据
            ff_id3v2_read_dict(s->pb, &s->internal->id3v2_meta, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta);
    
        // 读取header
        if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header)
            if ((ret = s->iformat->read_header(s)) < 0)
                goto fail;
    
        // 将ID3v2标签数据存放到s->metadata
        if (!s->metadata) {
            s->metadata = s->internal->id3v2_meta;
            s->internal->id3v2_meta = NULL;
        } else if (s->internal->id3v2_meta) {
            int level = AV_LOG_WARNING;
            if (s->error_recognition & AV_EF_COMPLIANT)
                level = AV_LOG_ERROR;
            av_log(s, level, "Discarding ID3 tags because more suitable tags were found.\n");
            av_dict_free(&s->internal->id3v2_meta);
            if (s->error_recognition & AV_EF_EXPLODE)
                return AVERROR_INVALIDDATA;
        }
    
        // 解析id3
        if (id3v2_extra_meta) {
            if (!strcmp(s->iformat->name, "mp3") || !strcmp(s->iformat->name, "aac") ||
                !strcmp(s->iformat->name, "tta") || !strcmp(s->iformat->name, "wav")) {
                // 为从ID3v2标头提取的每个APIC(附加图片)创建一个流。
                if ((ret = ff_id3v2_parse_apic(s, &id3v2_extra_meta)) < 0)
                    goto fail;
                // 为在ID3v2标头中找到的所有CHAP标签创建章节。
                if ((ret = ff_id3v2_parse_chapters(s, &id3v2_extra_meta)) < 0)
                    goto fail;
                // 在ID3v2标头中为所有PRIV标签添加元数据。
                if ((ret = ff_id3v2_parse_priv(s, &id3v2_extra_meta)) < 0)
                    goto fail;
            } else
                av_log(s, AV_LOG_DEBUG, "demuxer does not support additional id3 data, skipping\n");
        }
        ff_id3v2_free_extra_meta(&id3v2_extra_meta);
    
        if ((ret = avformat_queue_attached_pictures(s)) < 0)
            goto fail;
    
        if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->pb && !s->internal->data_offset)
            // 同avio_seek
            // #define avio_tell(s) avio_seek((s), 0, SEEK_CUR)
            // static av_always_inline int64_t avio_tell(AVIOContext *s)
            // {
            //     return avio_seek(s, 0, SEEK_CUR);
            // }
            s->internal->data_offset = avio_tell(s->pb);
    
        s->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;
    
        // 更新AVFormatContext中流的信息
        update_stream_avctx(s);
    
        for (i = 0; i < s->nb_streams; i++)
            s->streams[i]->internal->orig_codec_id = s->streams[i]->codecpar->codec_id;
    
        // free options
        if (options) {
            av_dict_free(options);
            *options = tmp;
        }
        *ps = s;
        return 0;
    
    // 统一fail处理:释放空间
    fail:
        ff_id3v2_free_extra_meta(&id3v2_extra_meta);
        av_dict_free(&tmp);
        if (s->pb && !(s->flags & AVFMT_FLAG_CUSTOM_IO))
            avio_closep(&s->pb);
        avformat_free_context(s);
        *ps = NULL;
        return ret;
    }
    

    代码虽然很长,但是逻辑很清晰,详细的注释已经加到了代码之中。注释中有两个TODO,我们先来看一下这两个TODO的地方:

    TODO 1.avformat_alloc_context

    avformat_alloc_context为AVFormatContext分配空间,下面给出源码:

    /**
     * Allocate an AVFormatContext.
     * avformat_free_context() can be used to free the context and everything
     * allocated by the framework within it.
     * 分配一个AVFormatContext。
     * avformat_free_context()可用于释放上下文以及框架在其中分配的所有内容。
     */
    AVFormatContext *avformat_alloc_context(void);
    
    AVFormatContext *avformat_alloc_context(void)
    {
        AVFormatContext *ic;
        AVFormatInternal *internal;
    
        // 分配AVFormatContext空间
        ic = av_malloc(sizeof(AVFormatContext));
        if (!ic) return ic;
    
        // 分配AVFormatInternal空间
        internal = av_mallocz(sizeof(*internal));
        if (!internal) {
            av_free(ic);
            return NULL;
        }
        
        // 为AVFormatContext设置默认值
        avformat_get_context_defaults(ic);
        ic->internal = internal;
        ic->internal->offset = AV_NOPTS_VALUE;
        ic->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;
        ic->internal->shortest_end = AV_NOPTS_VALUE;
    
        return ic;
    }
    

    从上面可以看出,最终又调用了avformat_get_context_defaults

    /**
     * 设置初始io开关函数
     */
    static void avformat_get_context_defaults(AVFormatContext *s)
    {
        // reset
        memset(s, 0, sizeof(AVFormatContext));
    
        s->av_class = &av_format_context_class;
    
        s->io_open  = io_open_default;
        s->io_close = io_close_default;
    
        av_opt_set_defaults(s);
    }
    
    static const AVClass av_format_context_class = {
        .class_name     = "AVFormatContext",
        .item_name      = format_to_name,
        .option         = avformat_options,
        .version        = LIBAVUTIL_VERSION_INT,
        .child_next     = format_child_next,
        .child_class_next = format_child_class_next,
        .category       = AV_CLASS_CATEGORY_MUXER,
        .get_category   = get_category,
    };
    

    至此初始化部分就结束了,但是最后设置的io_open_default是什么呢?它即是在通过判断白名单打开资源文件,感兴趣的可以看一下这部分:

    static int io_open_default(AVFormatContext *s, AVIOContext **pb,
                               const char *url, int flags, AVDictionary **options)
    {
        // decide log level
        int loglevel;
    
        if (!strcmp(url, s->url) ||
            s->iformat && !strcmp(s->iformat->name, "image2") ||
            s->oformat && !strcmp(s->oformat->name, "image2")
        ) {
            loglevel = AV_LOG_DEBUG;
        } else
            loglevel = AV_LOG_INFO;
    
        av_log(s, loglevel, "Opening \'%s\' for %s\n", url, flags & AVIO_FLAG_WRITE ? "writing" : "reading");
    
    #if FF_API_OLD_OPEN_CALLBACKS
    FF_DISABLE_DEPRECATION_WARNINGS
        if (s->open_cb)
            return s->open_cb(s, pb, url, flags, &s->interrupt_callback, options);
    FF_ENABLE_DEPRECATION_WARNINGS
    #endif
    
        return ffio_open_whitelist(pb, url, flags, &s->interrupt_callback, options, s->protocol_whitelist, s->protocol_blacklist);
    }
    
    int ffio_open_whitelist(AVIOContext **s, const char *filename, int flags,
                             const AVIOInterruptCB *int_cb, AVDictionary **options,
                             const char *whitelist, const char *blacklist
                            )
    {
        URLContext *h;
        int err;
    
        // 核心逻辑
        err = ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist, blacklist, NULL);
        if (err < 0)
            return err;
        err = ffio_fdopen(s, h);
        if (err < 0) {
            ffurl_close(h);
            return err;
        }
        return 0;
    }
    
    /**
     * Create an URLContext for accessing to the resource indicated by
     * url, and open it.
     * 创建一个URLContext来访问由url指示的资源,然后将其打开。
     *
     * @param puc pointer to the location where, in case of success, the
     * function puts the pointer to the created URLContext
     * 指向成功的情况下,函数将指针放置到创建的URLContext的位置的指针
     *
     * @param flags flags which control how the resource indicated by url
     * is to be opened
     * 标志,用于控制如何打开由url指示的资源
     *
     * @param int_cb interrupt callback to use for the URLContext, may be
     * NULL
     * 用于URLContext的中断回调,可能为NULL
     *
     * @param options  A dictionary filled with protocol-private options. On return
     * this parameter will be destroyed and replaced with a dict containing options
     * that were not found. May be NULL.
     * 协议专用选项的字典。
     * 返回时,此参数将被销毁并替换为包含未找到的选项的dict。可能为NULL。
     *
     * @param parent An enclosing URLContext, whose generic options should
     *               be applied to this URLContext as well.
     * 封闭的URLContext,其通用选项也应应用于此URLContext。
     *
     * @return >= 0 in case of success, a negative value corresponding to an
     * AVERROR code in case of failure
     * return> = 0,如果成功,则为负值;如果失败,则为AVERROR
     */
    int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags,
                   const AVIOInterruptCB *int_cb, AVDictionary **options,
                   const char *whitelist, const char* blacklist,
                   URLContext *parent);
                   
    int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags,
                             const AVIOInterruptCB *int_cb, AVDictionary **options,
                             const char *whitelist, const char* blacklist,
                             URLContext *parent)
    {
        AVDictionary *tmp_opts = NULL;
        AVDictionaryEntry *e;
        
        // alloc
        int ret = ffurl_alloc(puc, filename, flags, int_cb);
        if (ret < 0)
            return ret;
        if (parent)
            av_opt_copy(*puc, parent);
        if (options &&
            (ret = av_opt_set_dict(*puc, options)) < 0)
            goto fail;
        if (options && (*puc)->prot->priv_data_class &&
            (ret = av_opt_set_dict((*puc)->priv_data, options)) < 0)
            goto fail;
    
        if (!options)
            options = &tmp_opts;
    
        av_assert0(!whitelist ||
                   !(e=av_dict_get(*options, "protocol_whitelist", NULL, 0)) ||
                   !strcmp(whitelist, e->value));
        av_assert0(!blacklist ||
                   !(e=av_dict_get(*options, "protocol_blacklist", NULL, 0)) ||
                   !strcmp(blacklist, e->value));
    
        // 设置白名单,黑名单
        if ((ret = av_dict_set(options, "protocol_whitelist", whitelist, 0)) < 0)
            goto fail;
    
        if ((ret = av_dict_set(options, "protocol_blacklist", blacklist, 0)) < 0)
            goto fail;
    
        if ((ret = av_opt_set_dict(*puc, options)) < 0)
            goto fail;
    
        // url connect
        ret = ffurl_connect(*puc, options);
    
        if (!ret)
            return 0;
            
    // 统一fail管理:关闭并置空
    fail:
        ffurl_close(*puc);
        *puc = NULL;
        return ret;
    }
    

    TODO 2.init_input

    这个函数的作用是打开输入文件,并在必要时探查格式,即完善AVFormatContext中的AVIOContext和AVInputFormat。下面我们来看一下源码:

    /**
     * Open input file and probe the format if necessary.
     * 打开输入文件,并在必要时探查格式
     */
    static int init_input(AVFormatContext *s, const char *filename,
                          AVDictionary **options)
    {
        int ret;
        AVProbeData pd = { filename, NULL, 0 };
        int score = AVPROBE_SCORE_RETRY;
    
        if (s->pb) {
            s->flags |= AVFMT_FLAG_CUSTOM_IO;
            if (!s->iformat)
                // 探测字节流以确定输入格式
                return av_probe_input_buffer2(s->pb, &s->iformat, filename,
                                             s, 0, s->format_probesize);
            else if (s->iformat->flags & AVFMT_NOFILE)
                av_log(s, AV_LOG_WARNING, "Custom AVIOContext makes no sense and "
                                          "will be ignored with AVFMT_NOFILE format.\n");
            return 0;
        }
    
        // 猜测文件格式
        if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) ||
            (!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score))))
            return score;
    
        if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0)
            return ret;
    
        if (s->iformat)
            return 0;
        return av_probe_input_buffer2(s->pb, &s->iformat, filename,
                                     s, 0, s->format_probesize);
    }
    

    代码调用了av_probe_input_buffer2 & av_probe_input_format2:

    /**
     * Probe a bytestream to determine the input format. Each time a probe returns
     * with a score that is too low, the probe buffer size is increased and another
     * attempt is made. When the maximum probe size is reached, the input format
     * with the highest score is returned.
     * 探测字节流以确定输入格式。
     * 每次探针返回的分数过低时,探针缓冲区的大小都会增加,并会进行另一次尝试。
     * 当达到最大探针大小时,将返回得分最高的输入格式。
     *
     * @param pb the bytestream to probe 要探测的字节流
     * @param fmt the input format is put here 输入格式
     * @param url the url of the stream 流的url
     * @param logctx the log context
     * @param offset the offset within the bytestream to probe from 要从中探测的字节流中的偏移量
     * @param max_probe_size the maximum probe buffer size (zero for default) 最大探针缓冲区大小(默认为0)
     * @return the score in case of success, a negative value corresponding to an
     *         the maximal score is AVPROBE_SCORE_MAX
     * AVERROR code otherwise
     */
    int av_probe_input_buffer2(AVIOContext *pb, ff_const59 AVInputFormat **fmt,
                               const char *url, void *logctx,
                               unsigned int offset, unsigned int max_probe_size);
                               
    /**
     * Guess the file format.
     * 猜测文件格式
     *
     * @param pd        data to be probed 要探查的数据
     * @param is_opened Whether the file is already opened; determines whether
     *                  demuxers with or without AVFMT_NOFILE are probed.
     *                  文件是否已经打开;确定demuxers是否具有已探测的AVFMT_NOFILE。
     *
     * @param score_max A probe score larger that this is required to accept a
     *                  detection, the variable is set to the actual detection
     *                  score afterwards.
     *                  大于接受检测所需的探针分数,此后将变量设置为实际检测分数。
     *                  If the score is <= AVPROBE_SCORE_MAX / 4 it is recommended
     *                  to retry with a larger probe buffer.
     */
    ff_const59 AVInputFormat *av_probe_input_format2(ff_const59 AVProbeData *pd, int is_opened, int *score_max);
    

    知识拓展

    1.AVFormatInternal结构体解析
    struct AVFormatInternal {
        /**
         * Number of streams relevant for interleaving.
         * Muxing only.
         * 相关交错的流的数量
         * 仅用于混合流
         */
        int nb_interleaved_streams;
    
        /**
         * This buffer is only needed when packets were already buffered but
         * not decoded, for example to get the codec parameters in MPEG
         * streams.
         * 仅当数据包已经缓冲但尚未解码时才需要此缓冲区,例如,以获取MPEG流中的编解码器参数。
         */
        struct AVPacketList *packet_buffer;
        struct AVPacketList *packet_buffer_end;
    
        /* av_seek_frame() support */
        int64_t data_offset; /**< offset of the first packet */
    
        /**
         * Raw packets from the demuxer, prior to parsing and decoding.
         * This buffer is used for buffering packets until the codec can
         * be identified, as parsing cannot be done without knowing the
         * codec.
         * 来自解复用器的原始数据包,然后进行解析和解码。
         * 此缓冲区用于缓冲数据包,直到可以识别编解码器为止,因为在不知道编解码器的情况下无法进行解析。
         */
        struct AVPacketList *raw_packet_buffer;
        struct AVPacketList *raw_packet_buffer_end;
        /**
         * Packets split by the parser get queued here.
         * 解析器拆分的数据包在此处排队。
         */
        struct AVPacketList *parse_queue;
        struct AVPacketList *parse_queue_end;
        /**
         * Remaining size available for raw_packet_buffer, in bytes.
         */
    #define RAW_PACKET_BUFFER_SIZE 2500000
        int raw_packet_buffer_remaining_size;
    
        /**
         * Offset to remap timestamps to be non-negative.
         * Expressed in timebase units.
         * 将时间戳重新映射为非负值的偏移量。
         * 以timebase单位表示。
         * @see AVStream.mux_ts_offset
         */
        int64_t offset;
    
        /**
         * Timebase for the timestamp offset.
         * 时间戳偏移量的时基。
         */
        AVRational offset_timebase;
    
    #if FF_API_COMPUTE_PKT_FIELDS2
        int missing_ts_warning;
    #endif
    
        int inject_global_side_data;
    
        int avoid_negative_ts_use_pts;
    
        /**
         * Timestamp of the end of the shortest stream.
         * 最短流结束的时间戳。
         */
        int64_t shortest_end;
    
        /**
         * Whether or not avformat_init_output has already been called
         * 是否已调用avformat_init_output
         */
        int initialized;
    
        /**
         * Whether or not avformat_init_output fully initialized streams
         * avformat_init_output是否已完全初始化了流
         */
        int streams_initialized;
    
        /**
         * ID3v2 tag useful for MP3 demuxing
         * ID3v2标签,可用于MP3解复用
         */
        AVDictionary *id3v2_meta;
    
        /*
         * Prefer the codec framerate for avg_frame_rate computation.
         * 使用avg_frame_rate进行计算时,想要控制的编解码帧率
         */
        int prefer_codec_framerate;
    };
    
    2.avformat_init_output
    /**
     * Allocate the stream private data and initialize the codec, but do not write the header.
     * May optionally be used before avformat_write_header to initialize stream parameters
     * before actually writing the header.
     * If using this function, do not pass the same options to avformat_write_header.
     * 分配流私有数据并初始化编解码器,但不要写入头部。
     * 可以选择在avformat_write_header之前使用,以在实际写入头部之前初始化流参数。
     * 如果使用此功能,请勿将相同的选项传递给avformat_write_header。
     *
     * @param s Media file handle, must be allocated with avformat_alloc_context().
     *          Its oformat field must be set to the desired output format;
     *          Its pb field must be set to an already opened AVIOContext.
     *          媒体文件句柄必须使用avformat_alloc_context()分配。
     *          必须将其oformat字段设置为所需的输出格式。
     *          必须将其pb字段设置为已打开的AVIOContext。
     *
     * @param options  An AVDictionary filled with AVFormatContext and muxer-private options.
     *                 On return this parameter will be destroyed and replaced with a dict containing
     *                 options that were not found. May be NULL.
     *                 一个充满AVFormatContext和muxer-private选项的AVDictionary。
     *                 返回时,此参数将被销毁并替换为包含未找到的选项的dict。 可能为NULL。
     *
     * @return AVSTREAM_INIT_IN_WRITE_HEADER on success if the codec requires avformat_write_header to fully initialize,
     *         AVSTREAM_INIT_IN_INIT_OUTPUT  on success if the codec has been fully initialized,
     *         negative AVERROR on failure.
     *
     * @see av_opt_find, av_dict_set, avio_open, av_oformat_next, avformat_write_header.
     */
    av_warn_unused_result
    int avformat_init_output(AVFormatContext *s, AVDictionary **options);
    
    3.av_strlcpy
    /**
     * Copy the string src to dst, but no more than size - 1 bytes, and
     * null-terminate dst.
     * 将字符串src复制到dst,但不超过(size - 1)个字节,null终止目标缓冲区
     *
     * This function is the same as BSD strlcpy().
     * 此函数与BSD strlcpy()相同。
     *
     * @param dst destination buffer 目标缓冲区
     * @param src source string 源字符串
     * @param size size of destination buffer 目标缓冲区的大小
     * @return the length of src src的长度
     *
     * @warning since the return value is the length of src, src absolutely
     * _must_ be a properly 0-terminated string, otherwise this will read beyond
     * the end of the buffer and possibly crash.
     * 因为返回值是src的长度,src一定且必须是一个正确的以0结尾的字符串,否则函数将读取超出缓冲区的末尾的位置,可能崩溃
     */
    size_t av_strlcpy(char *dst, const char *src, size_t size);
    

    总结

    经过了一系列的操作,avformat_open_input完成了打开文件并解析文件中的各个参数,最终将解析出的所有信息放入了AVFormatContext中,供后续的操作使用。

    If you like this, it is written by Johnny Deng.
    If not, then I don’t know who wrote if.

    展开全文
  • FFmpeg源码分析:avformat_open_input

    千次阅读 2019-06-20 20:05:03
    avformat_open_input(),该函数用于打开多媒体数据并且获取一些信息,它的声明位于libavformat/avformat.h: /** * Open an input stream and read the header. The codecs are not opened. * 打开输入流,并且...
  • FFmpeg学习 avformat_open_input()函数分析

    千次阅读 2021-02-03 10:15:57
    本文梳理libavformat包下的avformat_open_input函数。 通读avformat_open_input,主要功能是: 根据传入的filename确定了要使用的协议URLProtocol,比如http的或是file类型的协议; 然后按该协议循环从2048byte...
  • 在上篇文章中的demo中,main函数的流程里调用的第一个函数就是avformat_open_input()。直观看来,其最明显的功能就是制定了要播放的文件名了。但是除了问价名之外还有几个结构体作为了函数的参数。那么这个函数的...
  • avformat_open_input()

    2021-10-11 16:09:33
    avformat_open_input()。该函数用于打开多媒体数据并且获得一些相关的信息。它的声明位于libavformat\avformat.h,如下所示。 avformat_open_input() * 打开一个输入流并读取头部。未打开编解码器。 * 必须使用 ...
  • avformat_open_input [cpp] view plaincopy //参数ps包含一切媒体相关的上下文结构,有它就有了一切,本函数如果打开媒体成功,  //会返回一个AVFormatContext的实例.  //参数filename是媒体文件名或...
  • 本文分析了FFMPEG中的媒体打开函数avformat_open_input() //参数ps包含一切媒体相关的上下文结构,有它就有了一切,本函数如果打开媒体成功,  //会返回一个AVFormatContext的实例.  //参数filename是媒体...
  • decoder应用demo详见: ... 这篇文章主要分析下demo里面decoder接口的具体实现过程。 1. avformat_open_input 解析头信息 ffmpeg/libavformat/utils.c // 此函数在demo中只有filename是从命令行输入的,...
  • avformat_open_input(),该函数用于打开多媒体数据并且获取一些信息,它的声明位于libavformat/avformat.h。主要工作 1)通过init_input打开流媒体数据,根据probe探测流媒体最合适的协议类型AVInputFormat 2)...
  • 0avformat_open_input() 功能详细描述 1 avformat_open_input()源码分析 1.1init_input() 1.1.1av_probe_input_buffer2() //1.1.1.2ffio_rewind_with_probe_data() 1.1.2av_probe_input_format2() ...
  • FFMPEG-avformat_open_input

    千次阅读 2013-11-04 14:37:50
    original blog:http://wodamazi.iteye.com/blog/1293994... store here in case for convenience. ----------------------------------------------------------------- avformat_open_input [cpp]
  • 摘要: avformat_open_input 的源码分析 版本: FFMPEG代码为3.2 release版本, 输入为flv文件。 分析结论: FFMPEG中函数avformat_open_input()作用为打开输入文件,并将输入文件中的数据读入到buf...
  • Real-time lane departure warning system based on a single FPGA Xiangjing An, Erke ShangEmail author, Jinze Song, Jian Li and Hangen He EURASIP Journal on Image and Video Proc
  • FFMPEG打开媒体的的过程开始于avformat_open_input,因此该函数的重要性不可忽视。 在该函数中,FFMPEG完成了: 输入输出结构体AVIOContext的初始化; 输入数据的协议(例如RTMP,或者file)的识别:1判断文件名的...
  • FFmpeg源代码简单分析:avformat_open_input()

    万次阅读 多人点赞 2015-03-05 00:13:10
    本文简单分析FFmpeg中一个常用的函数:avformat_open_input()。该函数用于打开多媒体数据并且获得一些相关的信息。
  • 今天看一下ffmpeg里面avformat_open_input这个函数,我个人认为这个函数算是在ffmpeg中最重要的函数了,因为其实在执行了这个函数之后,基本上所有的文件信息都就出来了。比如这一段流里面有几股流,每一股流都是...
  • 【FFmpeg编程进阶】(六)avformat_open_input 函数源代码分析一、avformat_open_input() avformat_open_input() 函数主要用于打开音视频多媒体数据,且获得AVFormatContext 信息, 其定义在 libavformat\avformat.h...
  • 从学龄前开始解读FFMPEG代码 之 avformat_open_input函数 二开始学龄前学习前想说的话av_probe_input_buffer2()的定义和功能avio_read()函数所做的事av_probe_input_format2()函数学习收尾的一些话 开始学龄前学习前...
  • //处理来自Libteec的请求,主要包括open session, close session, invoke等 thread_alloc_and_run(args); }  只有在libteec中触发的smc后,需要OP-TEE作出相应的操作后才可能产生来自RPC请求,故先介绍OP-TEE...
  • FPGA常见的警告以及处理方法

    千次阅读 2018-05-29 19:05:11
    9.warning: circuit may not operate.detected 46 non-operational paths clocked by clock clk44 with clock skew larger than data delay 原因 : 时钟抖动大于数据延时, 当时钟很快, 而 if 等类的层次过多就会...
  • int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options) { AVFormatContext *s = *ps;9 int ret = 0; AVFormatParameters ap = { { 0 } }; ...
  • 512 字节 / 512 字节 安装lvm2 创建物理卷 pvcreate 查看物理卷 pvdisplay root@linww-PowerEdge-T630:/home/linww# pvcreate /dev/sdf WARNING: ext4 signature detected on /dev/sdf at offset 1080. Wipe it? [y/...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,773
精华内容 1,509
关键字:

casedetectedopenwarning