/// <summary> /// Merge properties /// </summary> /// <param name="target"></param> /// <param name="source"></param> private Dictionary <string, VariantValue> Merge( Dictionary <string, VariantValue> target, Dictionary <string, VariantValue> source) { if (source == null) { return(target); } if (target == null) { return(source); } foreach (var item in source) { if (target.ContainsKey(item.Key)) { if (VariantValueEx.IsNull(item.Value) || VariantValueEx.IsNull(item.Value)) { target.Remove(item.Key); } else { target[item.Key] = item.Value; } } else if (!VariantValueEx.IsNull(item.Value)) { target.Add(item.Key, item.Value); } } return(target); }
public async Task NodeReadStaticArrayByteStringValueVariableTestAsync() { var browser = _services(); var node = "http://test.org/UA/Data/#i=10314"; var expected = await _readExpected(_endpoint, node); // Act var result = await browser.NodeValueReadAsync(_endpoint, new ValueReadRequestModel { NodeId = node }); // Assert Assert.NotNull(result); Assert.NotNull(result.Value); Assert.NotNull(result.SourceTimestamp); Assert.NotNull(result.ServerTimestamp); AssertEqualValue(expected, result.Value); if (VariantValueEx.IsNull(result.Value)) { return; } Assert.True(result.Value.IsListOfValues); if (result.Value.Count == 0) { return; } // TODO: Can be null. Assert.Equal(VariantValueType.String, (result.Value)[0].Type); // TODO: Assert.Equal(VariantValueType.Bytes, (result.Value)[0].Type); Assert.Equal("ByteString", result.DataType); }
public async Task NodeReadStaticArrayByteValueVariableTestAsync() { var browser = _services(); var node = "http://test.org/UA/Data/#i=10302"; var expected = await _readExpected(_endpoint, node); // Act var result = await browser.NodeValueReadAsync(_endpoint, new ValueReadRequestModel { NodeId = node }); // Assert Assert.NotNull(result); Assert.NotNull(result.Value); Assert.NotNull(result.SourceTimestamp); Assert.NotNull(result.ServerTimestamp); AssertEqualValue(expected, result.Value); if (VariantValueEx.IsNull(result.Value)) { return; } Assert.True(result.Value.IsString); // TODO: Returns a bytestring, not byte array. Investigate. // Assert.Equal(VariantValueType.Bytes, result.Value.Type); // Assert.True(result.Value.IsArray); // if ((result.Value).Count == 0) return; // Assert.Equal(VariantValueType.Integer, (result.Value)[0].Type); Assert.Equal("ByteString", result.DataType); // TODO: Assert.Equal("Byte", result.DataType); }
/// <summary> /// Equality comparison /// </summary> /// <param name="model"></param> /// <param name="that"></param> /// <returns></returns> public static bool IsSameAs(this CredentialModel model, CredentialModel that) { if (model == that) { return(true); } if (model == null || that == null) { return(false); } if ((that.Type ?? CredentialType.None) != (model.Type ?? CredentialType.None)) { return(false); } if (VariantValueEx.IsNull(that.Value) || VariantValueEx.IsNull(model.Value)) { if (VariantValueEx.IsNull(that.Value) && VariantValueEx.IsNull(model.Value)) { return(true); } return(false); } if (!VariantValue.DeepEquals(that.Value, model.Value)) { return(false); } return(true); }
/// <inheritdoc/> public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { switch (value) { case JsonVariantValue json: json.Token.WriteTo(writer, serializer.Converters.ToArray()); break; case VariantValue variant: if (VariantValueEx.IsNull(variant)) { writer.WriteNull(); } else if (variant.IsListOfValues) { writer.WriteStartArray(); foreach (var item in variant.Values) { WriteJson(writer, item, serializer); } writer.WriteEndArray(); } else if (variant.IsObject) { writer.WriteStartObject(); foreach (var key in variant.PropertyNames) { var item = variant[key]; if (VariantValueEx.IsNull(item)) { if (serializer.NullValueHandling != NullValueHandling.Include || serializer.DefaultValueHandling != DefaultValueHandling.Include) { break; } } writer.WritePropertyName(key); WriteJson(writer, item, serializer); } writer.WriteEndObject(); } else if (variant.TryGetValue(out var primitive)) { serializer.Serialize(writer, primitive); break; } else { serializer.Serialize(writer, variant.Value); break; } break; default: throw new NotSupportedException("Unexpected type passed"); } }
/// <summary> /// Synchronize controllers with current reported twin state /// </summary> /// <param name="twin"></param> /// <param name="reported"></param> /// <returns></returns> private async Task ReportControllerStateAsync(Twin twin, Dictionary <string, VariantValue> reported) { var processed = await _settings.GetSettingsStateAsync(); // If there are changes, update what should be reported back. foreach (var property in processed) { var exists = twin.Properties.Reported.Contains(property.Key); if (VariantValueEx.IsNull(property.Value)) { if (exists) { // If exists as reported, remove reported.AddOrUpdate(property.Key, null); _reported.Remove(property.Key); } } else { if (exists) { // If exists and same as property value, continue var r = (VariantValue)this._serializer.FromObject( twin.Properties.Reported[property.Key]); if (r == property.Value) { continue; } } else if (VariantValueEx.IsNull(property.Value)) { continue; } // Otherwise, add to reported properties reported[property.Key] = property.Value; _reported.AddOrUpdate(property.Key, property.Value); } } if (reported.Count > 0) { _logger.Debug("Reporting controller state..."); var collection = new TwinCollection(); foreach (var item in reported) { collection[item.Key] = item.Value?.ConvertTo <object>(); } await Client.UpdateReportedPropertiesAsync(collection); _logger.Debug("Complete controller state reported (properties: {@settings}).", reported.Keys); } }
/// <inheritdoc/> public int Serialize(ref byte[] bytes, int offset, T value, MessagePackSerializerOptions options) { if (value is MessagePackVariantValue packed) { return(MsgPack.Serialize(packed._value?.GetType() ?? typeof(object), ref bytes, offset, packed._value, options)); } else if (value is null) { return(MsgPackWriter.WriteNil(ref bytes, offset)); } else if (value is VariantValue variant) { if (VariantValueEx.IsNull(variant)) { return(MsgPackWriter.WriteNil(ref bytes, offset)); } else if (variant.IsListOfValues) { var written = MsgPackWriter.WriteArrayHeader( ref bytes, offset, variant.Count); foreach (var item in variant.Values) { written += MsgPack.Serialize(item?.GetType() ?? typeof(object), ref bytes, offset + written, item, options); } return(written); } else if (variant.IsObject) { // Serialize objects as key value pairs var dict = variant.PropertyNames .ToDictionary(k => k, k => variant[k]); return(MsgPack.Serialize(dict.GetType(), ref bytes, offset, dict, options)); } else if (variant.TryGetValue(out var primitive)) { return(MsgPack.Serialize(primitive?.GetType() ?? typeof(object), ref bytes, offset, primitive, options)); } else { return(MsgPack.Serialize(variant.Value?.GetType() ?? typeof(object), ref bytes, offset, variant.Value, options)); } } else { return(offset); } }
/// <inheritdoc/> public Variant Decode(VariantValue value, BuiltInType builtinType) { if (VariantValueEx.IsNull(value)) { return(Variant.Null); } // // Sanitize json input from user // value = Sanitize(value, builtinType == BuiltInType.String); VariantValue json; if (builtinType == BuiltInType.Null || (builtinType == BuiltInType.Variant && value.IsObject)) { // // Let the decoder try and decode the json variant. // json = Serializer.FromObject(new { value }); } else { // // Give decoder a hint as to the type to use to decode. // json = Serializer.FromObject(new { value = new { Body = value, Type = (byte)builtinType } }); } // // Decode json to a real variant // using (var text = new StringReader(Serializer.SerializeToString(json))) using (var reader = new Newtonsoft.Json.JsonTextReader(text)) using (var decoder = new JsonDecoderEx(reader, Context)) { return(decoder.ReadVariant(nameof(value))); } }
public async Task NodeReadStaticArrayByteValueVariableTestAsync() { var browser = _services(); var node = "http://test.org/UA/Data/#i=10302"; var expected = await _readExpected(_endpoint, node); // Act var result = await browser.NodeValueReadAsync(_endpoint, new ValueReadRequestModel { NodeId = node }); // Assert Assert.NotNull(result); Assert.NotNull(result.SourceTimestamp); Assert.NotNull(result.ServerTimestamp); AssertEqualValue(expected, result.Value); Assert.Equal("ByteString", result.DataType); Assert.True(VariantValueEx.IsNull(result.Value) || result.Value.IsBytes); }
/// <summary> /// Consolidated /// </summary> /// <param name="model"></param> /// <returns></returns> public static Dictionary <string, VariantValue> GetConsolidatedProperties( this DeviceTwinModel model) { var desired = model.Properties?.Desired; var reported = model.Properties?.Reported; if (reported == null || desired == null) { return((reported ?? desired) ?? new Dictionary <string, VariantValue>()); } var properties = new Dictionary <string, VariantValue>(desired); // Merge with reported foreach (var prop in reported) { if (properties.TryGetValue(prop.Key, out var existing)) { if (VariantValueEx.IsNull(existing) || VariantValueEx.IsNull(prop.Value)) { if (VariantValueEx.IsNull(existing) && VariantValueEx.IsNull(prop.Value)) { continue; } } else if (VariantValue.DeepEquals(existing, prop.Value)) { continue; } properties[prop.Key] = prop.Value; } else { properties.Add(prop.Key, prop.Value); } } return(properties); }
/// <summary> /// Sanitizes user input by removing quotes around non strings, /// or adding array brackets to comma seperated values that are /// not string type and recursing through arrays to do the same. /// The output is a pure json token that can be passed to the /// json decoder. /// </summary> /// <param name="value"></param> /// <param name="isString"></param> /// <returns></returns> internal VariantValue Sanitize(VariantValue value, bool isString) { if (VariantValueEx.IsNull(value)) { return(value); } if (!value.TryGetString(out var asString, true)) { asString = Serializer.SerializeToString(value); } if (!value.IsObject && !value.IsListOfValues && !value.IsString) { // // If this should be a string - return as such // return(isString ? asString : value); } if (string.IsNullOrWhiteSpace(asString)) { return(value); } // // Try to parse string as json // if (!value.IsString) { asString = asString.Replace("\\\"", "\""); } var token = Try.Op(() => Serializer.Parse(asString)); if (!(token is null)) { value = token; } if (value.IsString) { // // try to split the string as comma seperated list // var elements = asString.Split(','); if (isString) { // // If all elements are quoted, then this is a // string array // if (elements.Length > 1) { var array = new List <string>(); foreach (var element in elements) { var trimmed = element.Trim().TrimQuotes(); if (trimmed == element) { // Treat entire string as value return(value); } array.Add(trimmed); } // No need to sanitize contents return(Serializer.FromObject(array)); } } else { // // First trim any quotes from string before splitting. // if (elements.Length > 1) { // // Parse as array // var trimmed = elements.Select(e => e.TrimQuotes()).ToArray(); try { value = Serializer.Parse( "[" + trimmed.Aggregate((x, y) => x + "," + y) + "]"); } catch { value = Serializer.Parse( "[\"" + trimmed.Aggregate((x, y) => x + "\",\"" + y) + "\"]"); } } else { // // Try to remove next layer of quotes and try again. // var trimmed = asString.Trim().TrimQuotes(); if (trimmed != asString) { return(Sanitize(trimmed, isString)); } } } } if (value.IsListOfValues) { // // Sanitize each element accordingly // return(Serializer.FromObject(value.Values .Select(t => Sanitize(t, isString)))); } return(value); }