Как записать значение в тег OPC UA с помощью C#?

Всем доброго времени суток! Мне необходимо записывать значения в OPC UA сервер. Подключение к нему удалось реализовать с помощью библиотек OPC.UA.... от OPCFoundation. По итогу у меня есть сессия (Session) и подписка (Opc.UA.Client.Subscription), по которой я могу получать обновления значений тегов, на которые я подписан. Сессия имеет метод Write(), который по идее и должен выполнять запись, но я постоянно получаю результат "BadWriteNotSupported", хотя другие клиенты спокойно пишут в эти теги. Если ли кто знает как правильно записывать значения в OPC UA теги?


Ответы (1 шт):

Автор решения: Иван Козлов

После некоторого времени поиска я наткнулся на вот это. По итогу данный пример решил все мои проблемы. Сама запись в OPC UA - сервер производится следующим образом:

 WriteValue valueToWrite = new WriteValue();
 valueToWrite.NodeId = "NODE_TO_WRITE_ID";
 valueToWrite.AttributeId = "NODE_TO_WRITE_ATTRIBUTE_ID";
 valueToWrite.Value.Value = ChangeType(oldvalue,newvalue);
 valueToWrite.Value.StatusCode = StatusCodes.Good;
 valueToWrite.Value.ServerTimestamp = DateTime.MinValue;
 valueToWrite.Value.SourceTimestamp = DateTime.MinValue; 
 WriteValueCollection valuesToWrite = new WriteValueCollection();
 valuesToWrite.Add(valueToWrite);
 //В данных коллекциях будут возвращены результаты записи и указания на ошибки. Сам метод записи исключений при ошибочной записи не выбрасывает
 StatusCodeCollection results = null;
 DiagnosticInfoCollection diagnosticInfos = null;
 //непосредственно запись значения
 OPCSession.Write(
                null,
                valuesToWrite,
                out results,
                out diagnosticInfos);

Крайне важно учитывать, что встроенные типы данных протокола OPC UA отличаются от типов данных C#. Если не учесть этого, то получим ошибку "BadTypeMissmatch". Для приведения типов используется функция ChangeType().

private object ChangeType(DataValue m_value, string newvalue)
    {
        object value = (m_value != null)?m_value.Value:null;

        switch (m_value.WrappedValue.TypeInfo.BuiltInType)
        {
            case BuiltInType.Boolean:
            {
                value = Convert.ToBoolean(newvalue);
                break;
            }

            case BuiltInType.SByte:
            {
                value = Convert.ToSByte(newvalue);
                break;
            }

            case BuiltInType.Byte:
            {
                value = Convert.ToByte(newvalue);
                break;
            }

            case BuiltInType.Int16:
            {
                value = Convert.ToInt16(newvalue);
                break;
            }

            case BuiltInType.UInt16:
            {
                value = Convert.ToUInt16(newvalue);
                break;
            }

            case BuiltInType.Int32:
            {
                value = Convert.ToInt32(newvalue);
                break;
            }

            case BuiltInType.UInt32:
            {
                value = Convert.ToUInt32(newvalue);
                break;
            }

            case BuiltInType.Int64:
            {
                value = Convert.ToInt64(newvalue);
                break;
            }

            case BuiltInType.UInt64:
            {
                value = Convert.ToUInt64(newvalue);
                break;
            }

            case BuiltInType.Float:
            {
                value = Convert.ToSingle(newvalue);
                break;
            }

            case BuiltInType.Double:
            {
                value = Convert.ToDouble(newvalue);
                break;
            }

            default:
            {
                value = ValueTB.Text;
                break;
            }
        }

        return value;
    }

На вход функции необходимо передать уже имеющееся значение узла, в который собираемся писать, чтобы определить тип значения данного узла. (В принципе, если твердо знать, какой тип нужен, то функция не нужна).

Собственно именно такой вариант записи значений меня полностью устроил.

→ Ссылка