#include "lip/app/opcua/server/lip_opcua_server_cfg_defs.h"
#if LIP_OPCUA_SERVER_ENABLE
#include "lip_opcua_binary_types_procbuf.h"
#include "lip_opcua_binary_proto_defs.h"
#include "lip_opcua_binary_obj_enc.h"
#include "lip/app/opcua/lip_opcua_std_nodeids.h"
#include <math.h>






t_lip_opcua_err lip_opcua_procbuf_get_uint8(t_lip_proc_buf *buf, uint8_t *val) {
    t_lip_opcua_err err = lip_opcua_procbuf_get_avsize_check(buf, sizeof(uint8_t));
    if (err == LIP_OPCUA_ERR_GOOD) {
        if (val) {
            *val = *buf->cur++;
        } else {
            lip_procbuf_next(buf, sizeof(uint8_t));
        }
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_uint8(t_lip_proc_buf *buf, uint8_t val) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_avsize_check(buf, sizeof(uint8_t));
    if (err == LIP_OPCUA_ERR_GOOD) {
        *buf->cur++ = val;
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_get_int8(t_lip_proc_buf *buf, int8_t *val) {
    t_lip_opcua_err err = lip_opcua_procbuf_get_avsize_check(buf, sizeof(int8_t));
    if (err == LIP_OPCUA_ERR_GOOD) {
        *val = (int8_t)*buf->cur++;
    }
    return err;
}
t_lip_opcua_err lip_opcua_procbuf_put_int8(t_lip_proc_buf *buf, int8_t val) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_avsize_check(buf, sizeof(uint8_t));
    if (err == LIP_OPCUA_ERR_GOOD) {
        *buf->cur++ = val;
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_get_bool(t_lip_proc_buf *buf, bool *val) {
    uint8_t intval;
    t_lip_opcua_err err = lip_opcua_procbuf_get_uint8(buf, &intval);
    if ((err == LIP_OPCUA_ERR_GOOD) && val) {
        *val = intval != 0;
    }
    return err;
}
t_lip_opcua_err lip_opcua_procbuf_put_bool(t_lip_proc_buf *buf, bool val) {
    return lip_opcua_procbuf_put_uint8(buf, val);
}

t_lip_opcua_err lip_opcua_procbuf_get_uint16(t_lip_proc_buf *buf, uint16_t *val) {
    t_lip_opcua_err err = lip_opcua_procbuf_get_avsize_check(buf, sizeof(uint16_t));
    if (err == LIP_OPCUA_ERR_GOOD) {
        if (val) {
            LIP_OPCUA_GET_UINT16(*val, buf->cur);
        } else {
            lip_procbuf_next(buf, sizeof(uint16_t));
        }
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_uint16(t_lip_proc_buf *buf, uint16_t val) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_avsize_check(buf, sizeof(uint16_t));
    if (err == LIP_OPCUA_ERR_GOOD) {
        LIP_OPCUA_PUT_UINT16(val, buf->cur);
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_get_int16(t_lip_proc_buf *buf, int16_t *val) {
    t_lip_opcua_err err = lip_opcua_procbuf_get_avsize_check(buf, sizeof(int16_t));
    if (err == LIP_OPCUA_ERR_GOOD) {
        if (val) {
            LIP_OPCUA_GET_INT16(*val, buf->cur);
        } else {
            lip_procbuf_next(buf, sizeof(int16_t));
        }
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_int16(t_lip_proc_buf *buf, int16_t val) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_avsize_check(buf, sizeof(int16_t));
    if (err == LIP_OPCUA_ERR_GOOD) {
        LIP_OPCUA_PUT_INT16(val, buf->cur);
    }
    return err;
}


t_lip_opcua_err lip_opcua_procbuf_get_int32(t_lip_proc_buf *buf, int32_t *val) {
    t_lip_opcua_err err = lip_opcua_procbuf_get_avsize_check(buf, sizeof(int32_t));
    if (err == LIP_OPCUA_ERR_GOOD) {
        if (val) {
            LIP_OPCUA_GET_INT32(*val, buf->cur);
        } else {
            lip_procbuf_next(buf, sizeof(int32_t));
        }
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_int32(t_lip_proc_buf *buf, int32_t val) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_avsize_check(buf, sizeof(int32_t));
    if (err == LIP_OPCUA_ERR_GOOD) {
        LIP_OPCUA_PUT_INT32(val, buf->cur);
    }
    return err;
}


t_lip_opcua_err lip_opcua_procbuf_get_uint32(t_lip_proc_buf *buf, uint32_t *val) {
    t_lip_opcua_err err = lip_opcua_procbuf_get_avsize_check(buf, sizeof(uint32_t));
    if (err == LIP_OPCUA_ERR_GOOD) {
        if (val) {
            LIP_OPCUA_GET_UINT32(*val, buf->cur);
        } else {
            lip_procbuf_next(buf, sizeof(uint32_t));
        }
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_uint32(t_lip_proc_buf *buf, uint32_t val) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_avsize_check(buf, sizeof(uint32_t));
    if (err == LIP_OPCUA_ERR_GOOD) {
        LIP_OPCUA_PUT_UINT32(val, buf->cur);
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_get_uint64(t_lip_proc_buf *buf, uint64_t *val) {
    t_lip_opcua_err err = lip_opcua_procbuf_get_avsize_check(buf, sizeof(uint64_t));
    if (err == LIP_OPCUA_ERR_GOOD) {
        if (val) {
            LIP_OPCUA_GET_UINT64(*val, buf->cur);
        } else {
            lip_procbuf_next(buf, sizeof(uint64_t));
        }
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_uint64(t_lip_proc_buf *buf, uint64_t val) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_avsize_check(buf, sizeof(uint64_t));
    if (err == LIP_OPCUA_ERR_GOOD) {
        LIP_OPCUA_PUT_UINT64(val, buf->cur);
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_get_int64(t_lip_proc_buf *buf, int64_t *val) {
    t_lip_opcua_err err = lip_opcua_procbuf_get_avsize_check(buf, sizeof(int64_t));
    if (err == LIP_OPCUA_ERR_GOOD) {
        if (val) {
            LIP_OPCUA_GET_INT64(*val, buf->cur);
        } else {
            lip_procbuf_next(buf, sizeof(int64_t));
        }
    }
    return err;

}
t_lip_opcua_err lip_opcua_procbuf_put_int64(t_lip_proc_buf *buf, int64_t val) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_avsize_check(buf, sizeof(int64_t));
    if (err == LIP_OPCUA_ERR_GOOD) {
        LIP_OPCUA_PUT_INT64(val, buf->cur);
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_get_double(t_lip_proc_buf *buf, double *val) {
    t_lip_opcua_err err = lip_opcua_procbuf_get_avsize_check(buf, sizeof(double));
    if (err == LIP_OPCUA_ERR_GOOD) {
        if (val) {
            memcpy(val, buf->cur, sizeof(double));
        }
        lip_procbuf_next(buf, sizeof(double));
    }
    return err;
}
t_lip_opcua_err lip_opcua_procbuf_put_double(t_lip_proc_buf *buf, double val) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_avsize_check(buf, sizeof(double));
    if (err == LIP_OPCUA_ERR_GOOD) {
        memcpy(buf->cur, &val, sizeof(double));
        lip_procbuf_next(buf, sizeof(double));
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_get_float(t_lip_proc_buf *buf, float *val) {
    t_lip_opcua_err err = lip_opcua_procbuf_get_avsize_check(buf, sizeof(float));
    if (err == LIP_OPCUA_ERR_GOOD) {
        if (val) {
            memcpy(val, buf->cur, sizeof(float));
        }
        lip_procbuf_next(buf, sizeof(float));
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_float(t_lip_proc_buf *buf, float val) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_avsize_check(buf, sizeof(float));
    if (err == LIP_OPCUA_ERR_GOOD) {
        memcpy(buf->cur, &val, sizeof(float));
        lip_procbuf_next(buf, sizeof(float));
    }
    return err;

}



t_lip_opcua_err lip_opcua_procbuf_get_datetime(t_lip_proc_buf *buf, t_lip_opcua_datetime *tstmp) {
    return lip_opcua_procbuf_get_uint64(buf, tstmp);
}

t_lip_opcua_err lip_opcua_procbuf_put_datetime(t_lip_proc_buf *buf, t_lip_opcua_datetime tstmp) {
    return lip_opcua_procbuf_put_uint64(buf, tstmp);
}


t_lip_opcua_err lip_opcua_procbuf_get_string(t_lip_proc_buf *buf, t_lip_opcua_string_ptr *str) {
    int32_t size = -1;
    const uint8_t *data = NULL;
    t_lip_opcua_err err = lip_opcua_procbuf_get_int32(buf, &size);
    if (err == LIP_OPCUA_ERR_GOOD) {
        if (size > 0) {
            err = lip_opcua_procbuf_get_avsize_check(buf, size);
            if (err == LIP_OPCUA_ERR_GOOD) {
                data = buf->cur;
                lip_procbuf_next(buf, size);
            }
        }
    }

    if ((err == LIP_OPCUA_ERR_GOOD) && (str != NULL)){
        str->size = size;
        str->data = (const char*)data;
    }

    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_string(t_lip_proc_buf *buf, const t_lip_opcua_string_ptr *str) {
    int32_t size = str != NULL ? str->size : -1;
    t_lip_opcua_err err = lip_opcua_procbuf_put_int32(buf, size);
    if (err == LIP_OPCUA_ERR_GOOD) {
        if (size > 0) {
            err = lip_opcua_procbuf_put_avsize_check(buf, size);
            if (err == LIP_OPCUA_ERR_GOOD) {
                memcpy(buf->cur, str->data, size);
                lip_procbuf_next(buf, size);
            }
        }
    }
    return err;
}


t_lip_opcua_err lip_opcua_procbuf_get_loc_string(t_lip_proc_buf *buf, t_lip_opcua_loc_string_ptr *locstr) {
    t_lip_opcua_string_ptr locale = LIP_OPCUA_EMPTY_STRING;
    t_lip_opcua_string_ptr text = LIP_OPCUA_EMPTY_STRING;
    uint8_t encoding_mask = 0;
    t_lip_opcua_err err = lip_opcua_procbuf_get_uint8(buf, &encoding_mask);
    if ((encoding_mask & LIP_OPCUA_LOCTEXT_ENCBIT_LOCALE) && (err == LIP_ERR_SUCCESS)) {
        err = lip_opcua_procbuf_get_string(buf, &locale);
    }
    if ((encoding_mask & LIP_OPCUA_LOCTEXT_ENCBIT_TEXT) && (err == LIP_ERR_SUCCESS)) {
        err = lip_opcua_procbuf_get_string(buf, &text);
    }
    if (locstr != NULL) {
        locstr->locale = locale;
        locstr->text = text;
    }
    return err;
}
t_lip_opcua_err lip_opcua_procbuf_put_loc_string(t_lip_proc_buf *buf, const t_lip_opcua_loc_string_ptr *locstr) {
    uint8_t encoding_mask = ((locstr->locale.size > 0) ? LIP_OPCUA_LOCTEXT_ENCBIT_LOCALE : 0)
                            | ((locstr->text.size > 0) ? LIP_OPCUA_LOCTEXT_ENCBIT_TEXT : 0);
    t_lip_opcua_err err = lip_opcua_procbuf_put_uint8(buf, encoding_mask);
    if ((encoding_mask & LIP_OPCUA_LOCTEXT_ENCBIT_LOCALE) && (err == LIP_ERR_SUCCESS)) {
        err = lip_opcua_procbuf_put_string(buf, &locstr->locale);
    }
    if ((encoding_mask & LIP_OPCUA_LOCTEXT_ENCBIT_TEXT) && (err == LIP_ERR_SUCCESS)) {
        err = lip_opcua_procbuf_put_string(buf, &locstr->text);
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_loc_string_from_list(t_lip_proc_buf *buf, const t_lip_opcua_loc_string_ptr_list *loclist, struct st_lip_opcua_session_ctx *session) {
#if LIP_OPCUA_LOCALE_CNT > 1
    /** @todo */
    #error currently not implemented
#else
    t_lip_opcua_loc_string_ptr locstr;
    locstr.locale.size = -1;
    if (loclist) {
        locstr.text = loclist->strlist[0];
    } else {
        locstr.text.size = -1;
    }
    return lip_opcua_procbuf_put_loc_string(buf, &locstr);
#endif
}



t_lip_opcua_err lip_opcua_procbuf_get_string_arr(t_lip_proc_buf *buf, t_lip_opcua_string_ptr_array *arr) {
    int32_t cnt = -1;
    t_lip_opcua_err err = lip_opcua_procbuf_get_int32(buf, &cnt);

    for (int i = 0; (i < cnt) && (err == LIP_OPCUA_ERR_GOOD); ++i) {
        err = lip_opcua_procbuf_get_string(buf, ((arr != NULL) && (i < LIP_OPCUA_STRING_ARR_MAX_SIZE)) ? &arr->strlist[i] : NULL);
    }

    if ((err == LIP_OPCUA_ERR_GOOD) && (arr != NULL)) {
        if (cnt > LIP_OPCUA_STRING_ARR_MAX_SIZE) {
            err = LIP_OPCUA_ERR_BAD_ENCODING_LIMITS_EXCEEDED;
            cnt = LIP_OPCUA_STRING_ARR_MAX_SIZE;
        }

        arr->cnt = cnt;
    }

    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_string_arr(t_lip_proc_buf *buf, const t_lip_opcua_string_ptr_array *arr) {
    t_lip_opcua_err err = LIP_ERR_SUCCESS;
    if (!arr) {
         err = lip_opcua_procbuf_put_int32(buf, -1);
    } else {
        err = lip_opcua_procbuf_put_int32(buf, arr->cnt);
        for (int i = 0; (i < arr->cnt) && (err == LIP_OPCUA_ERR_GOOD); ++i) {
            err = lip_opcua_procbuf_put_string(buf, &arr->strlist[i]);
        }
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_double_arr(t_lip_proc_buf *buf, const double *arr, int32_t size) {
    t_lip_opcua_err err = LIP_ERR_SUCCESS;
    if (!arr && (size != 0)) {
        err = lip_opcua_procbuf_put_int32(buf, -1);
    } else {
        err = lip_opcua_procbuf_put_int32(buf, size);
        for (int i = 0; (i < size) && (err == LIP_OPCUA_ERR_GOOD); ++i) {
            err = lip_opcua_procbuf_put_double(buf, arr[i]);
        }
    }
    return err;
}


t_lip_opcua_err lip_opcua_procbuf_get_qalified_name(t_lip_proc_buf *buf, t_lip_opcua_qualified_name *qname) {
    uint16_t ns_idx;
    t_lip_opcua_string_ptr name;
    t_lip_opcua_err err = lip_opcua_procbuf_get_uint16(buf, &ns_idx);
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_string(buf, &name);
    }
    if ((err == LIP_OPCUA_ERR_GOOD) && qname) {
        qname->ns_idx = ns_idx;
        qname->name = name;
    }
    return err;
}
t_lip_opcua_err lip_opcua_procbuf_put_qalified_name(t_lip_proc_buf *buf, const t_lip_opcua_qualified_name *qname) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_uint16(buf, qname ? qname->ns_idx : 0);
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_string(buf, qname ? &qname->name : NULL);
    }
    return err;
}


t_lip_opcua_err lip_opcua_procbuf_get_signature_data(t_lip_proc_buf *buf, t_lip_opcua_signature_data *sdata) {
    t_lip_opcua_err err = lip_opcua_procbuf_get_string(buf, sdata ? &sdata->algorithm : NULL);
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_string(buf, sdata ? &sdata->signature : NULL);
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_signature_data(t_lip_proc_buf *buf, const t_lip_opcua_signature_data *sdata) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_string(buf, &sdata->algorithm);
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_string(buf, &sdata->signature);
    }
    return err;
}


t_lip_opcua_err lip_opcua_procbuf_get_nodeid(t_lip_proc_buf *buf, t_lip_opcua_nodeid *nodeid) {
    t_lip_opcua_err err = lip_opcua_procbuf_get_avsize_check(buf, 2);
    if (err == LIP_OPCUA_ERR_GOOD) {
        uint8_t enc_type = *buf->cur++;
        /** @todo обход флагов в двух старших битах */
        if (enc_type == LIP_OPCUA_NODEID_ENCTYPE_2BYTE) {
            if (nodeid) {
                nodeid->ns_idx = 0;
                nodeid->intval = *buf->cur++;
                nodeid->type = (nodeid->intval == 0) ? LIP_OPCUA_NODEID_TYPE_INVALID : LIP_OPCUA_NODEID_TYPE_INT;
            } else {
                lip_procbuf_next(buf, 1);
            }
        } else if (enc_type == LIP_OPCUA_NODEID_ENCTYPE_4BYTE) {
            err = lip_opcua_procbuf_get_avsize_check(buf, 3);
            if (err == LIP_OPCUA_ERR_GOOD) {
                if (nodeid) {
                    nodeid->type = LIP_OPCUA_NODEID_TYPE_INT;
                    nodeid->ns_idx = *buf->cur++;
                    LIP_OPCUA_GET_UINT16(nodeid->intval, buf->cur);
                } else {
                    lip_procbuf_next(buf, 3);
                }
            }
        } else {
            err = lip_opcua_procbuf_get_uint16(buf, nodeid ? &nodeid->ns_idx : NULL);
            if (err == LIP_OPCUA_ERR_GOOD) {
                if (enc_type == LIP_OPCUA_NODEID_ENCTYPE_NUMERIC) {
                    err = lip_opcua_procbuf_get_uint32(buf, nodeid ? &nodeid->intval : NULL);
                    if ((err == LIP_OPCUA_ERR_GOOD) && nodeid) {
                        nodeid->type = LIP_OPCUA_NODEID_TYPE_INT;
                    }
                } else if (enc_type == LIP_OPCUA_NODEID_ENCTYPE_STRING) {
                    err = lip_opcua_procbuf_get_string(buf, nodeid ? &nodeid->str : NULL);
                    if ((err == LIP_OPCUA_ERR_GOOD) && nodeid) {
                        nodeid->type = LIP_OPCUA_NODEID_TYPE_STRING;
                    }
                } else if (enc_type == LIP_OPCUA_NODEID_ENCTYPE_GUID) {
                    err = lip_opcua_procbuf_get_avsize_check(buf, LIP_OPCUA_GUID_SIZE);
                    if (err == LIP_OPCUA_ERR_GOOD) {
                        if (nodeid) {
                            nodeid->guid = buf->cur;
                            nodeid->type = LIP_OPCUA_NODEID_TYPE_GUID;
                        }
                        lip_procbuf_next(buf, LIP_OPCUA_GUID_SIZE);
                    }
                } else if (enc_type == LIP_OPCUA_NODEID_ENCTYPE_BYTESTRING) {
                    err = lip_opcua_procbuf_get_string(buf, nodeid ? &nodeid->str : NULL);
                    if ((err == LIP_OPCUA_ERR_GOOD) && nodeid) {
                        nodeid->type = LIP_OPCUA_NODEID_TYPE_BYTESTRING;
                    }
                } else {
                    err = LIP_OPCUA_ERR_BAD_TCP_INTERNAL_ERROR;
                }
            }
        }
    }

    if ((err != LIP_OPCUA_ERR_GOOD) && nodeid) {
        nodeid->type = LIP_OPCUA_NODEID_TYPE_INVALID;
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_nodeid(t_lip_proc_buf *buf, const t_lip_opcua_nodeid *nodeid) {
    t_lip_opcua_err err = LIP_OPCUA_ERR_GOOD;
    if (!nodeid || (nodeid->type == LIP_OPCUA_NODEID_TYPE_INVALID)) {
        err = lip_opcua_procbuf_put_avsize_check(buf, 2);
        if (err == LIP_OPCUA_ERR_GOOD) {
            *buf->cur++ = 0;
            *buf->cur++ = 0;
        }
    } else if (nodeid->type == LIP_OPCUA_NODEID_TYPE_INT) {
        if ((nodeid->ns_idx == 0) && (nodeid->intval < 256)) {
            err = lip_opcua_procbuf_put_avsize_check(buf, 2);
            if (err == LIP_OPCUA_ERR_GOOD) {
                *buf->cur++ = LIP_OPCUA_NODEID_ENCTYPE_2BYTE;
                *buf->cur++ = nodeid->intval;
            }
        } else if ((nodeid->ns_idx < 256) && (nodeid->intval <= 0xFFFF)) {
            err = lip_opcua_procbuf_put_avsize_check(buf, 4);
            if (err == LIP_OPCUA_ERR_GOOD) {
                *buf->cur++ = LIP_OPCUA_NODEID_ENCTYPE_4BYTE;
                *buf->cur++ = nodeid->ns_idx;
                *buf->cur++ = nodeid->intval & 0xFF;
                *buf->cur++ = (nodeid->intval >> 8) & 0xFF;
            }
        } else {
            err = lip_opcua_procbuf_put_avsize_check(buf, 7);
            if (err == LIP_OPCUA_ERR_GOOD) {
                *buf->cur++ = LIP_OPCUA_NODEID_ENCTYPE_NUMERIC;
                LIP_OPCUA_PUT_UINT16(nodeid->ns_idx, buf->cur);
                LIP_OPCUA_PUT_UINT32(nodeid->intval, buf->cur);
            }
        }
    } else if (nodeid->type == LIP_OPCUA_NODEID_TYPE_STRING) {
        err = lip_opcua_procbuf_put_avsize_check(buf, 3);
        if (err == LIP_OPCUA_ERR_GOOD) {
            *buf->cur++ = LIP_OPCUA_NODEID_ENCTYPE_STRING;
            LIP_OPCUA_PUT_UINT16(nodeid->ns_idx, buf->cur);
            err = lip_opcua_procbuf_put_string(buf, &nodeid->str);
        }
    } else if (nodeid->type == LIP_OPCUA_NODEID_TYPE_BYTESTRING) {
        err = lip_opcua_procbuf_put_avsize_check(buf, 3 + LIP_OPCUA_GUID_SIZE);
        if (err == LIP_OPCUA_ERR_GOOD) {
            *buf->cur++ = LIP_OPCUA_NODEID_ENCTYPE_GUID;
            LIP_OPCUA_PUT_UINT16(nodeid->ns_idx, buf->cur);
            memcpy(buf->cur, nodeid->guid, LIP_OPCUA_GUID_SIZE);
            lip_procbuf_next(buf, LIP_OPCUA_GUID_SIZE);
        }
    } else if (nodeid->type == LIP_OPCUA_NODEID_TYPE_GUID) {
        err = lip_opcua_procbuf_put_avsize_check(buf, 3);
        if (err == LIP_OPCUA_ERR_GOOD) {
            *buf->cur++ = LIP_OPCUA_NODEID_ENCTYPE_BYTESTRING;
            LIP_OPCUA_PUT_UINT16(nodeid->ns_idx, buf->cur);
            err = lip_opcua_procbuf_put_string(buf, &nodeid->str);
        }
    } else {
        err = LIP_OPCUA_ERR_BAD_ENCODING_ERROR;
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_get_extobj_info(t_lip_proc_buf *buf, t_lip_opcua_extobj_info *exobj_info) {
    t_lip_opcua_err err = lip_opcua_procbuf_get_nodeid(buf, &exobj_info->type_id);
    if (err == LIP_OPCUA_ERR_GOOD) {
        uint8_t enc;
        err = lip_opcua_procbuf_get_uint8(buf, &enc);
        if (err == LIP_OPCUA_ERR_GOOD) {
            if (enc == LIP_OPCUA_EXTOBJ_BODYENC_NONE) {
                exobj_info->body_len = 0;
                exobj_info->body = NULL;
            } else {
                err = lip_opcua_procbuf_get_uint32(buf, &exobj_info->body_len);
                if (err == LIP_OPCUA_ERR_GOOD) {
                    exobj_info->body = buf->cur;
                    err = lip_opcua_procbuf_get_avsize_check(buf, exobj_info->body_len);
                }
                if (err == LIP_OPCUA_ERR_GOOD) {
                    /** @noto поддерживаем только binary-encoding */
                    if (enc != LIP_OPCUA_EXTOBJ_BODYENC_BYTESTR) {
                        err = LIP_OPCUA_ERR_BAD_DECODING_ERROR;
                    }
                }

                if (err == LIP_OPCUA_ERR_GOOD) {
                    lip_procbuf_next(buf, exobj_info->body_len);
                }
            }
        }
    }

    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_extobj(t_lip_proc_buf *buf, const t_lip_opcua_extobj_info *exobj_info) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_nodeid(buf, &exobj_info->type_id);
    if (err == LIP_OPCUA_ERR_GOOD) {
        if (exobj_info->body_len == 0) {
            err = lip_opcua_procbuf_put_avsize_check(buf, 1);
            if (err == LIP_OPCUA_ERR_GOOD) {
                *buf->cur++ = LIP_OPCUA_EXTOBJ_BODYENC_NONE;
            }
        } else {
            err = lip_opcua_procbuf_put_avsize_check(buf, 1 + 4 + exobj_info->body_len);
            if (err == LIP_OPCUA_ERR_GOOD) {
                *buf->cur++ = LIP_OPCUA_EXTOBJ_BODYENC_BYTESTR;
                LIP_OPCUA_PUT_UINT32(exobj_info->body_len, buf->cur);
                memcpy(buf->cur, exobj_info->body, exobj_info->body_len);
                lip_procbuf_next(buf, exobj_info->body_len);
            }
        }
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_extobj_info(t_lip_proc_buf *buf, const t_lip_opcua_nodeid *exobj_type, t_lip_proc_buf *body_len_buf) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_nodeid(buf, exobj_type);
    if (err == LIP_OPCUA_ERR_GOOD) {
        if (body_len_buf == NULL) {
            err = lip_opcua_procbuf_put_avsize_check(buf, 1);
            if (err == LIP_OPCUA_ERR_GOOD) {
                *buf->cur++ = LIP_OPCUA_EXTOBJ_BODYENC_NONE;
            }
        } else {
            err = lip_opcua_procbuf_put_avsize_check(buf, 1 + 4);
            if (err == LIP_OPCUA_ERR_GOOD) {
                *buf->cur++ = LIP_OPCUA_EXTOBJ_BODYENC_BYTESTR;
                *body_len_buf = *buf;
                LIP_OPCUA_PUT_UINT32(-1, buf->cur);

            }
        }
    }
    return err;
}


t_lip_opcua_err lip_opcua_procbuf_get_variant(t_lip_proc_buf *buf, t_lip_opcua_variant *variant) {
    uint8_t enc_mask;
    t_lip_opcua_err err = lip_opcua_procbuf_get_uint8(buf, &enc_mask);
    if (err == LIP_OPCUA_ERR_GOOD) {
        uint8_t type = enc_mask & LIP_OPCUA_VARIANT_ENCMSK_TYPE;
        int32_t arr_len = -1;
        if (enc_mask & LIP_OPCUA_VARIANT_ENCMSK_ARRAY) {
            err = lip_opcua_procbuf_get_int32(buf, &arr_len);
        }

        if (err == LIP_OPCUA_ERR_GOOD) {
            bool is_array = arr_len >= 0;
            if (variant) {
                variant->type = type;
                variant->is_array = is_array;
            }

            if (!is_array) {
                if (type == LIP_OPCUA_BUILTIN_DATATYPE_BOOL) {
                    bool val;
                    err = lip_opcua_procbuf_get_bool(buf, &val);
                    if ((err == LIP_OPCUA_ERR_GOOD) && variant) {
                        variant->val.vbool = val;
                    }
                } else if (type == LIP_OPCUA_BUILTIN_DATATYPE_SBYTE) {
                    int8_t val;
                    err = lip_opcua_procbuf_get_int8(buf, &val);
                    if ((err == LIP_OPCUA_ERR_GOOD) && variant) {
                        variant->val.sint = val;
                    }
                } else if (type == LIP_OPCUA_BUILTIN_DATATYPE_BYTE) {
                    uint8_t val;
                    err = lip_opcua_procbuf_get_uint8(buf, &val);
                    if ((err == LIP_OPCUA_ERR_GOOD) && variant) {
                        variant->val.uint = val;
                    }
                }  else if (type == LIP_OPCUA_BUILTIN_DATATYPE_INT16) {
                    int16_t val;
                    err = lip_opcua_procbuf_get_int16(buf, &val);
                    if ((err == LIP_OPCUA_ERR_GOOD) && variant) {
                        variant->val.sint = val;
                    }
                } else if (type == LIP_OPCUA_BUILTIN_DATATYPE_UINT16) {
                    uint16_t val;
                    err = lip_opcua_procbuf_get_uint16(buf, &val);
                    if ((err == LIP_OPCUA_ERR_GOOD) && variant) {
                        variant->val.uint = val;
                    }
                } else if (type == LIP_OPCUA_BUILTIN_DATATYPE_INT32) {
                    int32_t val;
                    err = lip_opcua_procbuf_get_int32(buf, &val);
                    if ((err == LIP_OPCUA_ERR_GOOD) && variant) {
                        variant->val.sint = val;
                    }
                } else if (type == LIP_OPCUA_BUILTIN_DATATYPE_UINT32) {
                    uint32_t val;
                    err = lip_opcua_procbuf_get_uint32(buf, &val);
                    if ((err == LIP_OPCUA_ERR_GOOD) && variant) {
                        variant->val.uint = val;
                    }
                } else if (type == LIP_OPCUA_BUILTIN_DATATYPE_INT64) {
                    int64_t val;
                    err = lip_opcua_procbuf_get_int64(buf, &val);
                    if ((err == LIP_OPCUA_ERR_GOOD) && variant) {
                        variant->val.sint = val;
                    }
                } else if (type == LIP_OPCUA_BUILTIN_DATATYPE_UINT64) {
                    uint64_t val;
                    err = lip_opcua_procbuf_get_uint64(buf, &val);
                    if ((err == LIP_OPCUA_ERR_GOOD) && variant) {
                        variant->val.uint = val;
                    }
                } else if (type == LIP_OPCUA_BUILTIN_DATATYPE_FLOAT) {
                    float val;
                    err = lip_opcua_procbuf_get_float(buf, &val);
                    if ((err == LIP_OPCUA_ERR_GOOD) && variant) {
                        variant->val.vfloat = val;
                    }
                } else if (type == LIP_OPCUA_BUILTIN_DATATYPE_DOUBLE) {
                    double val;
                    err = lip_opcua_procbuf_get_double(buf, &val);
                    if ((err == LIP_OPCUA_ERR_GOOD) && variant) {
                        variant->val.vdouble = val;
                    }
                } else if (type == LIP_OPCUA_BUILTIN_DATATYPE_STRING) {
                    t_lip_opcua_string_ptr val;
                    err = lip_opcua_procbuf_get_string(buf, &val);
                    if ((err == LIP_OPCUA_ERR_GOOD) && variant) {
                        variant->val.str = val;
                    }
                } else if (type == LIP_OPCUA_BUILTIN_DATATYPE_DATETIME) {
                    t_lip_opcua_datetime val;
                    err = lip_opcua_procbuf_get_datetime(buf, &val);
                    if ((err == LIP_OPCUA_ERR_GOOD) && variant) {
                        variant->val.datetime = val;
                    }
                } else if (type == LIP_OPCUA_BUILTIN_DATATYPE_NODEID) {
                    t_lip_opcua_nodeid nodeid;
                    err = lip_opcua_procbuf_get_nodeid(buf, &nodeid);
                    if ((err == LIP_OPCUA_ERR_GOOD) && variant) {
                        variant->val.node_id = nodeid;
                    }
                } else if (type == LIP_OPCUA_BUILTIN_DATATYPE_QNAME) {
                    t_lip_opcua_qualified_name qname;
                    err = lip_opcua_procbuf_get_qalified_name(buf, &qname);
                    if ((err == LIP_OPCUA_ERR_GOOD) && variant) {
                        variant->val.qname = qname;
                    }
                } else if (type == LIP_OPCUA_BUILTIN_DATATYPE_LOCTEXT) {
                    t_lip_opcua_loc_string_ptr loctxt;
                    err = lip_opcua_procbuf_get_loc_string(buf, &loctxt);
                    if ((err == LIP_OPCUA_ERR_GOOD) && variant) {
                        variant->val.loctext = loctxt;
                    }
                } else {
                    err = LIP_OPCUA_ERR_BAD_DECODING_ERROR;
                    /** @todo other types; */
                }
            } else {
                /** @todo array proc */
            }
        }

        if ((err == LIP_OPCUA_ERR_GOOD) && (enc_mask & LIP_OPCUA_VARIANT_ENCMSK_DIMENSIONS)) {
            int32_t dim_cnt = 0;
            err = lip_opcua_procbuf_get_int32(buf, &dim_cnt);
            for (int32_t dun_idx = 0; (dun_idx < dim_cnt) && (err == LIP_OPCUA_ERR_GOOD); ++dun_idx) {
                int32_t dim_size;
                err = lip_opcua_procbuf_get_int32(buf, &dim_size);
                /** @todo save dimensions */
            }

        }
    }

    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_variant(t_lip_proc_buf *buf, const t_lip_opcua_variant *var, struct st_lip_opcua_session_ctx *session) {
    uint8_t enc_mask = var->type;
    if (var->is_array) {
        enc_mask |= LIP_OPCUA_VARIANT_ENCMSK_ARRAY;
    }
    t_lip_opcua_err err = lip_opcua_procbuf_put_uint8(buf, enc_mask);
    if (err == LIP_OPCUA_ERR_GOOD) {
        if (!(enc_mask & LIP_OPCUA_VARIANT_ENCMSK_ARRAY)) {
            if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_BOOL) {
                err = lip_opcua_procbuf_put_bool(buf, var->val.vbool);
            } else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_SBYTE) {
                err = lip_opcua_procbuf_put_int8(buf, var->val.sint);
            } else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_BYTE) {
                err = lip_opcua_procbuf_put_uint8(buf, var->val.uint);
            }  else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_INT16) {
                err = lip_opcua_procbuf_put_int16(buf, var->val.sint);
            } else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_UINT16) {
                err = lip_opcua_procbuf_put_uint16(buf, var->val.uint);
            } else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_INT32) {
                err = lip_opcua_procbuf_put_int32(buf, var->val.sint);
            } else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_UINT32) {
                err = lip_opcua_procbuf_put_uint32(buf, var->val.uint);
            } else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_INT64) {
                err = lip_opcua_procbuf_put_int64(buf, var->val.sint64);
            } else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_UINT64) {
                err = lip_opcua_procbuf_put_uint64(buf, var->val.uint64);
            } else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_FLOAT) {
                err = lip_opcua_procbuf_put_float(buf, var->val.vfloat);
            } else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_DOUBLE) {
                err = lip_opcua_procbuf_put_double(buf, var->val.vdouble);
            } else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_STRING) {
                err = lip_opcua_procbuf_put_string(buf, &var->val.str);
            } else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_DATETIME) {
                err = lip_opcua_procbuf_put_datetime(buf, var->val.datetime);
            } else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_NODEID) {
                err = lip_opcua_procbuf_put_nodeid(buf, &var->val.node_id);
            } else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_QNAME) {
                err = lip_opcua_procbuf_put_qalified_name(buf, &var->val.qname);
            } else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_LOCTEXT) {
                err = lip_opcua_procbuf_put_loc_string(buf, &var->val.loctext);
            } else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_EXTOBJ) {
                const t_lip_opcua_obj_enc_descr *enc_descr = lip_opcua_obj_get_enc(var->val.extobj.node_id) ;
                if (!enc_descr || !enc_descr->put) {
                    err = LIP_OPCUA_ERR_BAD_DATA_TYPE_ID_UNKNOWN;
                } else {
                    t_lip_proc_buf objlen_buf;
                    err = lip_opcua_procbuf_put_extobj_info(buf, enc_descr->enc_id, &objlen_buf);
                    if (err == LIP_OPCUA_ERR_GOOD) {
                        err = enc_descr->put(buf, var->val.extobj.objptr, session);
                    }
                    if (err == LIP_OPCUA_ERR_GOOD) {
                        lip_opcua_procbuf_put_objlen_deltabuf(buf, &objlen_buf);
                    }
                }
            } else {
                err = LIP_OPCUA_ERR_BAD_ENCODING_ERROR;
                /** @todo other types; */
            }
        } else  {
            int32_t total_len = var->val.array.items_cnt1 + var->val.array.items_cnt2;
            int32_t raw_elem_size;
            err = lip_opcua_procbuf_put_int32(buf, total_len);
            /* обработка массива простых типов, запись который делается простым копированием */
            if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_BOOL) {
                raw_elem_size = 1;
            } else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_SBYTE) {
                raw_elem_size = 1;
            } else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_BYTE) {
                raw_elem_size = 1;
            }  else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_INT16) {
                raw_elem_size = 2;
            } else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_UINT16) {
                raw_elem_size = 2;
            } else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_INT32) {
                raw_elem_size = 4;
            } else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_UINT32) {
                raw_elem_size = 4;
            } else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_INT64) {
                raw_elem_size = 8;
            } else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_UINT64) {
                raw_elem_size = 8;
            } else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_FLOAT) {
                raw_elem_size = 4;
            } else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_DOUBLE) {
                raw_elem_size = 8;
            } else {
                /** @todo other raw types; */
                raw_elem_size = -1;
            }

            if (raw_elem_size > 0) {
                if (err == LIP_OPCUA_ERR_GOOD) {
                    err = lip_opcua_procbuf_get_avsize_check(buf, total_len * raw_elem_size);
                }
                if (err == LIP_OPCUA_ERR_GOOD) {
                    int32_t cpy_size1 = var->val.array.items_cnt1 * raw_elem_size;
                    if (var->val.array.ptr1 != NULL) {
                        memcpy(buf->cur, var->val.array.ptr1, cpy_size1);
                    } else {
                        memset(buf->cur, 0, cpy_size1);
                    }
                    lip_procbuf_next(buf, cpy_size1);
                    if (var->val.array.items_cnt2 > 0) {
                        int32_t cpy_size2 = var->val.array.items_cnt2 * raw_elem_size;
                        if (var->val.array.ptr2) {
                            memcpy(buf->cur, var->val.array.ptr2, cpy_size2);
                        } else {
                            memset(buf->cur, 0, cpy_size2);
                        }
                        lip_procbuf_next(buf, cpy_size2);
                    }
                }
            } else {
                if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_STRING) {
                    if (var->val.array.items_cnt1 > 0) {
                        const t_lip_opcua_string_ptr *str_list = (const t_lip_opcua_string_ptr *)var->val.array.ptr1;
                        for (int32_t i = 0; (i < var->val.array.items_cnt1) && (err == LIP_OPCUA_ERR_GOOD); ++i) {
                            err = lip_opcua_procbuf_put_string(buf, str_list ? &str_list[i] : NULL);
                        }
                    }
                    if (var->val.array.items_cnt2 > 0) {
                        const t_lip_opcua_string_ptr *str_list = (const t_lip_opcua_string_ptr *)var->val.array.ptr2;
                        for (int32_t i = 0; (i < var->val.array.items_cnt2) && (err == LIP_OPCUA_ERR_GOOD); ++i) {
                            err = lip_opcua_procbuf_put_string(buf, str_list ? &str_list[i] : NULL);
                        }
                    }
                } else if (var->type == LIP_OPCUA_BUILTIN_DATATYPE_LOCTEXT) {
                    if (var->val.array.items_cnt1 > 0) {
                        const t_lip_opcua_loc_string_ptr_list *str_list = (const t_lip_opcua_loc_string_ptr_list *)var->val.array.ptr1;
                        for (int32_t i = 0; (i < var->val.array.items_cnt1) && (err == LIP_OPCUA_ERR_GOOD); ++i) {
                            err = lip_opcua_procbuf_put_loc_string_from_list(buf, str_list ? &str_list[i] : NULL, session);
                        }
                    }
                    if (var->val.array.items_cnt2 > 0) {
                        const t_lip_opcua_loc_string_ptr_list *str_list = (const t_lip_opcua_loc_string_ptr_list *)var->val.array.ptr2;
                        for (int32_t i = 0; (i < var->val.array.items_cnt2) && (err == LIP_OPCUA_ERR_GOOD); ++i) {
                            err = lip_opcua_procbuf_put_loc_string_from_list(buf, str_list ? &str_list[i] : NULL, session);
                        }
                    }
                } else {
                    err = LIP_OPCUA_ERR_BAD_ENCODING_ERROR;
                    /** @todo other types; */
                }
            }
        }
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_objlen_deltabuf(const t_lip_proc_buf *buf, t_lip_proc_buf *objlen_buf) {
    return lip_opcua_procbuf_put_objlen(objlen_buf, buf->cur - objlen_buf->cur - sizeof(uint32_t));
}

t_lip_opcua_err lip_opcua_procbuf_get_datavalue(t_lip_proc_buf *buf, t_lip_opcua_data_value *var) {
    uint8_t enc_mask;
    t_lip_opcua_err err = lip_opcua_procbuf_get_uint8(buf, &enc_mask);
    if (err == LIP_OPCUA_ERR_GOOD) {
        if (enc_mask & LIP_OPCUA_DATAVALUE_ENCMSK_VALUE) {
            err = lip_opcua_procbuf_get_variant(buf, var ? &var->value : NULL);
        } else if (var) {
            lip_opcua_variant_set_null(&var->value);
        }
    }

    if ((err == LIP_OPCUA_ERR_GOOD) && (enc_mask & LIP_OPCUA_DATAVALUE_ENCMSK_STATUS)) {
        err = lip_opcua_procbuf_get_uint32(buf, var ?  &var->status : NULL);
    }


    if (err == LIP_OPCUA_ERR_GOOD) {
        if (enc_mask & LIP_OPCUA_DATAVALUE_ENCMSK_SRC_TSTMP) {
            err = lip_opcua_procbuf_get_datetime(buf, var ?  &var->src_time.ns100 : NULL);
        } else if (var) {
            var->src_time.ns100 = 0;
        }
    }

    if (err == LIP_OPCUA_ERR_GOOD) {
        if (enc_mask & LIP_OPCUA_DATAVALUE_ENCMSK_SRC_PICOSEC) {
#if LIP_OPCUA_TSTMP_PICOSEC_EN
            err = lip_opcua_procbuf_get_uint16(buf, var ?  &var->src_time.ps : NULL);
        } else if (var) {
            var->src_time.ps = 0;
#else
            err = lip_opcua_procbuf_get_uint16(buf, NULL);
#endif
        }
    }


    if (err == LIP_OPCUA_ERR_GOOD) {
        if (enc_mask & LIP_OPCUA_DATAVALUE_ENCMSK_SRV_TSTMP) {
            err = lip_opcua_procbuf_get_datetime(buf, var ?  &var->srv_time.ns100 : NULL);
        } else if (var) {
            var->srv_time.ns100 = 0;
        }
    }

    if (err == LIP_OPCUA_ERR_GOOD) {
        if (enc_mask & LIP_OPCUA_DATAVALUE_ENCMSK_SRV_PICOSEC) {
#if LIP_OPCUA_TSTMP_PICOSEC_EN
            err = lip_opcua_procbuf_get_uint16(buf, var ?  &var->srv_time.ps : NULL);
        } else if (var) {
            var->srv_time.ps = 0;
#else
            err = lip_opcua_procbuf_get_uint16(buf, NULL);
#endif
        }
    }

    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_datavalue(t_lip_proc_buf *buf, const t_lip_opcua_data_value *var, t_lip_opcua_ret_tstmp_types tstmp_types, struct st_lip_opcua_session_ctx *session) {
    uint8_t enc_mask = 0;
    t_lip_opcua_err err = LIP_OPCUA_ERR_GOOD;
    if (!lip_opcua_variant_is_null(&var->value)) {
        enc_mask |= LIP_OPCUA_DATAVALUE_ENCMSK_VALUE;
    }
    if (var->status != LIP_OPCUA_ERR_GOOD) {
        enc_mask |= LIP_OPCUA_DATAVALUE_ENCMSK_STATUS;
    }
    if ((var->src_time.ns100 != 0) && ((tstmp_types == LIP_OPCUA_RET_TSTMP_TYPE_SOURCE) || (tstmp_types == LIP_OPCUA_RET_TSTMP_TYPE_BOTH))) {
        enc_mask |= LIP_OPCUA_DATAVALUE_ENCMSK_SRC_TSTMP
#if LIP_OPCUA_TSTMP_PICOSEC_EN
                  | LIP_OPCUA_DATAVALUE_ENCMSK_SRC_PICOSEC
#endif
            ;

    }
    if ((var->srv_time.ns100 != 0) && ((tstmp_types == LIP_OPCUA_RET_TSTMP_TYPE_SERVER) || (tstmp_types == LIP_OPCUA_RET_TSTMP_TYPE_BOTH))) {
        enc_mask |= LIP_OPCUA_DATAVALUE_ENCMSK_SRV_TSTMP
#if LIP_OPCUA_TSTMP_PICOSEC_EN
                | LIP_OPCUA_DATAVALUE_ENCMSK_SRV_PICOSEC
#endif
            ;
    }
    err = lip_opcua_procbuf_put_uint8(buf, enc_mask);
    if ((err == LIP_OPCUA_ERR_GOOD) && (enc_mask & LIP_OPCUA_DATAVALUE_ENCMSK_VALUE)) {
        err = lip_opcua_procbuf_put_variant(buf, &var->value, session);
    }
    if ((err == LIP_OPCUA_ERR_GOOD) && (enc_mask & LIP_OPCUA_DATAVALUE_ENCMSK_STATUS)) {
        err = lip_opcua_procbuf_put_uint32(buf, var->status);
    }
    if ((err == LIP_OPCUA_ERR_GOOD) && (enc_mask & LIP_OPCUA_DATAVALUE_ENCMSK_SRC_TSTMP)) {
        err = lip_opcua_procbuf_put_datetime(buf, var->src_time.ns100);
#if LIP_OPCUA_TSTMP_PICOSEC_EN
        if (err == LIP_OPCUA_ERR_GOOD) {
            err = lip_opcua_procbuf_put_uint16(buf, var->src_time.ps);
        }
#endif
    }
    if ((err == LIP_OPCUA_ERR_GOOD) && (enc_mask & LIP_OPCUA_DATAVALUE_ENCMSK_SRV_TSTMP)) {
        err = lip_opcua_procbuf_put_datetime(buf, var->srv_time.ns100);
#if LIP_OPCUA_TSTMP_PICOSEC_EN
        if (err == LIP_OPCUA_ERR_GOOD) {
            err = lip_opcua_procbuf_put_uint16(buf, var->srv_time.ps);
        }
#endif
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_get_readvalueid(t_lip_proc_buf *buf, t_lip_opcua_read_value_id *rvalueid) {
    t_lip_opcua_err err = lip_opcua_procbuf_get_nodeid(buf, rvalueid ? &rvalueid->nodeid : NULL);
    if (err == LIP_OPCUA_ERR_GOOD) {
        uint32_t idval;
        err = lip_opcua_procbuf_get_uint32(buf, &idval);
        if ((err == LIP_OPCUA_ERR_GOOD) && rvalueid) {
            rvalueid->attr = idval;
        }
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_string(buf, rvalueid ? &rvalueid->range : NULL);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_qalified_name(buf, rvalueid ? &rvalueid->data_enc : NULL);
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_get_viewdescr(t_lip_proc_buf *buf, t_lip_opcua_view_descr *viewdescr) {
    t_lip_opcua_err err = lip_opcua_procbuf_get_nodeid(buf, viewdescr ? &viewdescr->view_id : NULL);
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_datetime(buf, viewdescr ? &viewdescr->timestamp : NULL);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_uint32(buf, viewdescr ? &viewdescr->view_ver : NULL);
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_get_data_change_filter(t_lip_proc_buf *buf, t_lip_opcua_data_change_filter *filter) {
    uint32_t val;
    t_lip_opcua_err err = lip_opcua_procbuf_get_enum(buf, &val);
    if ((err == LIP_OPCUA_ERR_GOOD) && filter) {
        filter->trigger = val;
    }

    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_uint32(buf, &val);
        if ((err == LIP_OPCUA_ERR_GOOD) && filter) {
            filter->deadband_type = val;
        }
    }

    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_double(buf, filter ? &filter->deadband_value : NULL);
    }

    if (err == LIP_OPCUA_ERR_GOOD) {
        filter->type = LIP_OPCUA_MONITORING_FILTER_TYPE_DATA_CHANGE;
    }

    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_struct_field(t_lip_proc_buf *buf, const t_lip_opcua_struct_field *field, struct st_lip_opcua_session_ctx *session) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_string(buf, &field->name);
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_loc_string_from_list(buf, field->display_name, session);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_nodeid(buf, field->data_descr->type_id);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_int32(buf, field->data_descr->rank);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        const uint32_t *dimensions = NULL;
        int32_t dimensions_cnt = -1;
        /** @todo возможно ввести отдельный callback для ручного получения при желании.
          *  Сейчас для неопределенного числа возвращаем NULL */
        if (field->data_descr->rank > 0) {
            dimensions_cnt = field->data_descr->rank;
            if (dimensions_cnt == 1) {
                dimensions = &field->data_descr->dimension.value;
            } else {
                dimensions = field->data_descr->dimension.list;
            }
        }
        err = lip_opcua_procbuf_put_arrsize(buf, dimensions_cnt);
        for (int32_t dim_idx = 0; (dim_idx < dimensions_cnt) && (err == LIP_OPCUA_ERR_GOOD); ++dim_idx) {
            err = lip_opcua_procbuf_put_uint32(buf, dimensions ? dimensions[dim_idx] : 0);
        }
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_uint32(buf, field->max_str_len);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_bool(buf, field->is_optional);
    }

    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_struct_def(t_lip_proc_buf *buf, const t_lip_opcua_struct_def *struct_def, struct st_lip_opcua_session_ctx *session) {    
    t_lip_opcua_err err = lip_opcua_procbuf_put_nodeid(buf, struct_def->default_encoding_id);
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_nodeid(buf, struct_def->base_data_type_id);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_enum(buf, struct_def->struct_type);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_arrsize(buf, struct_def->fields_cnt);
        for (unsigned field_idx = 0; (field_idx < struct_def->fields_cnt) && (err == LIP_OPCUA_ERR_GOOD); ++field_idx) {
            err = lip_opcua_procbuf_put_struct_field(buf, &struct_def->fields[field_idx], session);
        }
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_enum_value(t_lip_proc_buf *buf, const t_lip_opcua_enum_value *enum_val, struct st_lip_opcua_session_ctx *session) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_int64(buf, enum_val ? enum_val->value : 0);
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_loc_string_from_list(buf, enum_val ? enum_val->display_name : NULL, session);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_loc_string_from_list(buf, enum_val ? enum_val->description : NULL, session);
    }
    return err;
}


t_lip_opcua_err lip_opcua_procbuf_put_build_info(t_lip_proc_buf *buf, const t_lip_opcua_data_build_info *build_info, struct st_lip_opcua_session_ctx *session) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_string(buf, build_info ? &build_info->product_uri : NULL);
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_string(buf, build_info ? &build_info->manufacturer_name : NULL);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_string(buf, build_info ? &build_info->product_name : NULL);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_string(buf, build_info ? &build_info->software_version : NULL);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_string(buf, build_info ? &build_info->build_number : NULL);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_datetime(buf, build_info ? build_info->build_date : 0);
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_server_status(t_lip_proc_buf *buf, const t_lip_opcua_data_server_status *srvstatus, struct st_lip_opcua_session_ctx *session) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_datetime(buf, srvstatus ? srvstatus->start_time : 0);
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_datetime(buf, srvstatus ? srvstatus->current_time : 0);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_enum(buf, srvstatus ? srvstatus->server_state : 0);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_build_info(buf, srvstatus ? srvstatus->build_info : NULL, session);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        /** @todo secondsTillShutdown */
        err = lip_opcua_procbuf_put_uint32(buf, 0);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        /** @todo shutdownReason */
        err = lip_opcua_procbuf_put_loc_string_from_list(buf, NULL, session);
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_da_range(t_lip_proc_buf *buf, const t_lip_opcua_da_range *range, struct st_lip_opcua_session_ctx *session) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_double(buf, range ? range->low : NAN);
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_double(buf, range ? range->high : NAN);
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_da_eu_info(t_lip_proc_buf *buf, const t_lip_opcua_da_eu_info *eu_info, struct st_lip_opcua_session_ctx *session) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_string(buf, eu_info ? &eu_info->namesapce_uri : NULL);
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_int32(buf, eu_info ? eu_info->unit_id : -1);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_loc_string_from_list(buf, eu_info ? &eu_info->display_name : NULL, session);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_loc_string_from_list(buf, eu_info ? &eu_info->description : NULL, session);
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_da_complex_num(t_lip_proc_buf *buf, const t_lip_opcua_da_complex_num *complex_num, struct st_lip_opcua_session_ctx *session) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_float(buf, complex_num ? complex_num->r : NAN);
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_float(buf, complex_num ? complex_num->im : NAN);
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_da_double_complex_num(t_lip_proc_buf *buf, const t_lip_opcua_da_double_complex_num *complex_num, struct st_lip_opcua_session_ctx *session) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_double(buf, complex_num ? complex_num->r : NAN);
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_double(buf, complex_num ? complex_num->im : NAN);
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_da_axis_info(t_lip_proc_buf *buf, const t_lip_opcua_da_axis_info *axis_info, struct st_lip_opcua_session_ctx *session) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_da_eu_info(buf, axis_info ? axis_info->eu : NULL, session);
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_da_range(buf, axis_info ? &axis_info->range : NULL, session);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_loc_string_from_list(buf, axis_info ? &axis_info->title : NULL, session);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_enum(buf, axis_info ? axis_info->type : LIP_OPCUA_DA_AXIS_SCALE_TYPE_LINEAR);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_double_arr(buf, axis_info ? axis_info->steps : NULL, axis_info ? axis_info->steps_cnt : -1);
    }
    return err;
}

t_lip_opcua_err lip_opcua_procbuf_put_da_xv(t_lip_proc_buf *buf, const t_lip_opcua_da_xv *xv, struct st_lip_opcua_session_ctx *session) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_double(buf, xv ? xv->x : NAN);
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_float(buf, xv ? xv->value : NAN);
    }
    return err;
}

#endif
