/// <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(); } }