Как записать значение в тег 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;
}
На вход функции необходимо передать уже имеющееся значение узла, в который собираемся писать, чтобы определить тип значения данного узла. (В принципе, если твердо знать, какой тип нужен, то функция не нужна).
Собственно именно такой вариант записи значений меня полностью устроил.