/// <summary> /// Sets the answer object /// </summary> /// <param name="answer"></param> public void setAnswerObject(IICCCMessage answer) { }
/// <summary> /// Sets the answer object. /// </summary> /// <param name="answer"></param> public void setAnswerObject(IICCCMessage answer) { this.answer = answer; }
private bool sendRegistration(IICCCMessage message) { // Precheck the local endpoint: if(this.localEndpoint == string.Empty) { this.earlyMessages.Enqueue(message); return false; } // Check the local endpoint inside the message: var registrationMessage = message as ICCCRegisterListener; if(registrationMessage.IPAddressPort == string.Empty) { registrationMessage.IPAddressPort = this.localEndpoint; } // Send the message now: var ok = false; for(var tries = 0; tries < 10; tries++) { var answer = ICCCConnection.INSTANCE.send2Any(message, ICCCKind.KindOcean) as ICCCDefaultAnswer; if(answer == null || !answer.CommandSuccessful) { Thread.Sleep(TimeSpan.FromSeconds(10)); continue; } else { ok = true; break; } } // Was not possible? Store it for later: if(!ok) { this.earlyMessages.Enqueue(message); return false; } return true; }
/// <summary> /// Sets the answer object. /// </summary> /// <param name="answer">The new answer object</param> public void setAnswerObject(IICCCMessage answer) { this.answerPrototype = answer; }
private bool sendDeregistration(IICCCMessage message) { // Precheck the local endpoint: if(this.localEndpoint == string.Empty) { return false; } // Check the local endpoint inside the message: var deregistrationMessage = message as ICCCListenerUpdate; if(deregistrationMessage.IPAddressPort == string.Empty) { deregistrationMessage.IPAddressPort = this.localEndpoint; } // Send the message now: var ok = false; for(var tries = 0; tries < 10; tries++) { var answer = ICCCConnection.INSTANCE.send2Any(message, ICCCKind.KindOcean) as ICCCDefaultAnswer; if(answer == null || !answer.CommandSuccessful) { Thread.Sleep(TimeSpan.FromSeconds(5)); continue; } else { ok = true; break; } } return ok; }
/// <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(); } }
/// <summary> /// Sends a message to all matching listeners. This method is thread-safe! /// </summary> /// <param name="message">The message with the channel and command.</param> /// <param name="kind">The matching kind of listeners. Thus, you can send a message only to Ocean servers, component servers or to both.</param> /// <returns>All received answers.</returns> public IEnumerable<IICCCMessage> send2All(IICCCMessage message, byte kind) { // No message? No answer! if(message == null) { return new IICCCMessage[0]; } // We read the cache, thus, ensure the read access: this.cacheLock.EnterReadLock(); try { // Convert the message to data: var data = ICCCProcessor.INSTANCE.convertMessage2Data(message); // No valid message? if(data == null || data.Count < 2) // channel + command = 2 { return new IICCCMessage[0]; } // Get all matching listeners: var matchingListeners = this.icccListenerCache.Where(n => n.IsActive && (kind == ICCCKind.KindALL || n.Kind == kind)).Where(n => n.Channel == message.getChannel() && n.Command == message.getCommand()).ToArray(); // No matching listener? if(matchingListeners.Length == 0) { return new IICCCMessage[0]; } // Space for all threads and results: var tasks = new Task<IICCCMessage>[matchingListeners.Length]; // Loop over all matching listeners: for(var n = 0; n < matchingListeners.Length; n++) { // Get an listener: var listener = matchingListeners[n]; // Start a new thread: tasks[n] = Task.Run<IICCCMessage>(() => { // Send the message and read the answer: var answerData = this.sendMessage(data, listener); // Create another empty instance for the answer: var type = message.getAnswerObject().GetType(); var answerObj = type.GetConstructors().First(info => !info.GetParameters().Any()).Invoke(null) as IICCCMessage; // Create the answer's message and return it: return ICCCProcessor.INSTANCE.convertData2Message(answerData, answerObj); }); } // Wait for all answers: Task.WaitAll(tasks); return tasks.Where(n => n.Result != null).Select(t => t.Result).ToArray(); } catch { return new IICCCMessage[0]; } finally { this.cacheLock.ExitReadLock(); } }
/// <summary> /// Sends a message to any matching listener. This method is thread-safe! /// </summary> /// <param name="message">The message with the channel and command.</param> /// <param name="kind">The matching kind of listeners. Thus, you can send a message only to an Ocean server, component server or to both.</param> /// <returns>The received answer or null.</returns> public IICCCMessage send2Any(IICCCMessage message, byte kind) { // No message? No answer! if(message == null) { return null; } // We read the cache, thus, ensure the read access: this.cacheLock.EnterReadLock(); try { // Convert the message to data: var data = ICCCProcessor.INSTANCE.convertMessage2Data(message); // No valid message? if(data == null || data.Count < 2) // channel + command = 2 { return null; } // Get all matching listeners: var matchingListeners = this.icccListenerCache.Where(n => n.IsActive && (kind == ICCCKind.KindALL || n.Kind == kind)).Where(n => n.Channel == message.getChannel() && n.Command == message.getCommand()).ToArray(); // The chosen listener: var chosenListener = null as ICCCListener; // No matching listener? if(matchingListeners.Length == 0) { return null; } if(matchingListeners.Length == 1) { // Take the only one: chosenListener = matchingListeners[0]; } else { // Choose a random listener: chosenListener = matchingListeners[BetterRND.INSTANCE.nextInt(0, matchingListeners.Length)]; } // Send the message and read the answer: var answerData = this.sendMessage(data, chosenListener); // Create the answer's message and return it: return ICCCProcessor.INSTANCE.convertData2Message(answerData, message.getAnswerObject()); } catch { return null; } finally { this.cacheLock.ExitReadLock(); } }