/// <summary> /// Converts a message's data into the message. This method is thread-safe! /// </summary> /// <param name="data">The message's data.</param> /// <param name="messagePrototype">The empty message prototype.</param> /// <returns>The message containing the data or null.</returns> public IICCCMessage convertData2Message(NameValueCollection data, IICCCMessage messagePrototype) { try { // Check the preconditions: if(data == null || data.Count < 2 || messagePrototype == null) // channel + command = 2 { return null; } // Get the type of the message: var type = messagePrototype.GetType(); // Get all properties: var properties = type.GetProperties(); // The .NET ICCC driver is based on properties. // In case that the type has no properties, we are // done here. if (!properties.Any()) { // We return the prototype, because there are many // messages without any data. Thus, this is a valid case. return messagePrototype; } // Loop over all data entries: foreach(var key in data.AllKeys) { // Get the data for this key: var value = data[key]; if(key.Contains(':')) { // Decode the base64 encoding. Thus, get the bytes: var valueBytes = Convert.FromBase64String(value); // Split the key into data type and name: var elements = key.Split(':'); var dataType = elements[0]; var name = elements[1]; // Get the matching property: var property = properties.FirstOrDefault(n => n.Name == name); // No property found? if(property == null) { continue; } // Store for the final value: var messageValue = null as object; // For some array convertions: var countNumbers = 0; // Choose the right decoding regarding the data type: switch(dataType) { case "str": // Decode the value: messageValue = Uri.UnescapeDataString(Encoding.UTF8.GetString(valueBytes)); break; case "f64": // Decode the value: messageValue = BitConverter.ToDouble(valueBytes, 0); break; case "int": // Decode the value: messageValue = BitConverter.ToInt64(valueBytes, 0); break; case "bool": // Decode the value: messageValue = valueBytes[0] == 0x1 ? true : false; break; case "ui8": // Decode the value: messageValue = valueBytes[0]; break; case "ui8[]": // Decode the value: messageValue = valueBytes; break; case "int[]": // Decode the value: countNumbers = valueBytes.Length / 8; // 8 byte per int64 i.e. long messageValue = new long[countNumbers]; Buffer.BlockCopy(valueBytes, 0, (Array)messageValue, 0, countNumbers * 8); break; case "bool[]": // Decode the value: messageValue = valueBytes.Select(n => n == 0x0 ? false : true).ToArray(); break; case "str[]": // Decode the value: messageValue = Encoding.UTF8.GetString(valueBytes).Split('\n').Select(n => Uri.UnescapeDataString(n)).ToArray(); break; case "f64[]": // Decode the value: countNumbers = valueBytes.Length / 8; // 8 byte per float 64 i.e. double messageValue = new double[countNumbers]; Buffer.BlockCopy(valueBytes, 0, (Array)messageValue, 0, countNumbers * 8); break; } // Store the value in the message: property.SetValue(messagePrototype, messageValue); } } return messagePrototype; } catch { return null; } }
/// <summary> /// Converts a message into data. This method is thread-safe! /// </summary> /// <param name="message">The message.</param> /// <returns>The data object which is may empty.</returns> public NameValueCollection convertMessage2Data(IICCCMessage message) { try { // The result's data: var data = new NameValueCollection(); // No message? if(message == null) { return data; } // Store the channel: data.Add("channel", message.getChannel()); // Store the command: data.Add("command", message.getCommand()); // Get the type of the message: var type = message.GetType(); // Get all properties: var properties = type.GetProperties(); // The .NET ICCC driver is based on properties. // In case that the type has no properties, we are // done here. if(!properties.Any()) { // We return the current data, because there are many // messages without any additional data. Thus, this is a valid case. return data; } // Loop over all properties: foreach(var property in properties) { // Read the type of this property: var dataType = property.PropertyType; // Read the value: var value = property.GetValue(message); // Read the name: var name = property.Name; // Find the matching encoding: switch(dataType.Name) { case "String": data.Add("str:" + name, Convert.ToBase64String(Encoding.UTF8.GetBytes(Uri.EscapeDataString(value as string)))); break; case "Int64": data.Add("int:" + name, Convert.ToBase64String(BitConverter.GetBytes((long)value))); break; case "Double": data.Add("f64:" + name, Convert.ToBase64String(BitConverter.GetBytes((double)value))); break; case "Boolean": var boolValue = (bool)value; data.Add("bool:" + name, Convert.ToBase64String(new byte[] { boolValue ? (byte)0x1 : (byte)0x0 })); break; case "Byte": data.Add("ui8:" + name, Convert.ToBase64String(new byte[] { (byte)value })); break; case "Byte[]": data.Add("ui8[]:" + name, Convert.ToBase64String(value as byte[])); break; case "Boolean[]": data.Add("bool[]:" + name, Convert.ToBase64String((value as bool[]).Select(n => n ? (byte)0x1 : (byte)0x0).ToArray())); break; case "Double[]": var doubleData = value as double[]; var doubleBuffer = new byte[doubleData.Length*8]; Buffer.BlockCopy(doubleData, 0, doubleBuffer, 0, doubleData.Length*8); data.Add("f64[]:" + name, Convert.ToBase64String(doubleBuffer)); break; case "Int64[]": var longData = value as long[]; var longBuffer = new byte[longData.Length*8]; Buffer.BlockCopy(longData, 0, longBuffer, 0, longData.Length*8); data.Add("int[]:" + name, Convert.ToBase64String(longBuffer)); break; case "String[]": var strings = value as string[]; data.Add("str[]:" + name, Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Join("\n", strings.Select(n => Uri.EscapeDataString(n)))))); break; } } return data; } catch { return new NameValueCollection(); } }