/// <summary> /// Processes a forwarder message after it has been parsed /// </summary> /// <param name="message">The forwarder message</param> private async Task HandleMessageAsync(ForwarderMessage message) { HashSet <ICalculatedChannel> calculatedChannelsToUpdate = new HashSet <ICalculatedChannel>(); foreach (var dataPair in message.DataValues) { long period; if (!_dataTimes.TryGetValue(dataPair.Key, out Stopwatch lastMessageStopwatch)) { lastMessageStopwatch = new Stopwatch(); period = int.MaxValue; } else { period = Math.Max(1, lastMessageStopwatch.ElapsedMilliseconds); } double frequency = 1000d / period; //if (!_dataCount.TryAdd(channelName, 0)) // _dataCount[channelName]++; if (frequency > 500) { //_logger.Trace("{0}: {1} - {2} Hz", dataPair.Key, dataPair.Value, frequency); } else if (frequency <= 10) { if (_calculatedChannels.TryGetValue(dataPair.Key, out var calculatedChannels)) { foreach (ICalculatedChannel calculatedChannel in calculatedChannels) { calculatedChannel.UpdateValue(dataPair.Key, dataPair.Value); calculatedChannelsToUpdate.Add(calculatedChannel); } } _dataTimes[dataPair.Key] = lastMessageStopwatch; lastMessageStopwatch.Restart(); await Program.Server.WriteToAllClients( $"{{ \"channel\": \"{dataPair.Key}\", \"data\": {dataPair.Value.ToString().Replace(',', '.')} }}", dataPair.Key); } } foreach (ICalculatedChannel calculatedChannel in calculatedChannelsToUpdate) { await Program.Server.WriteToAllClients( $"{{ \"channel\": \"{calculatedChannel.Name}\", \"data\": {calculatedChannel.CalculatedValue.ToString().Replace(',', '.')} }}", calculatedChannel.Name); } }
/// <summary> /// The main loop of the forwarder connection. Listens and processes connections, and runs forever /// </summary> /// <returns></returns> public async Task ListenAsync() { try { InitCalculatedChannels(); TcpListener listener = new TcpListener(IPAddress.Any, 1221); // TODO: Make configurable listener.Start(); while (true) { IsConnected = false; Program.Server.UpdateConsoleTitle(); Program.Server.StartSimulator(); TcpClient client = await listener.AcceptTcpClientAsync(); IsConnected = true; Program.Server.UpdateConsoleTitle(); _logger.Info("Got connection from pit!"); Program.Server.StopSimulator(); using (NetworkStream stream = client.GetStream()) { while (client.Connected) { ForwarderMessage message = await ReadMessageAsync(stream); if (message == null) { client.Close(); break; } await HandleMessageAsync(message); } } _logger.Info("Lost connection from pit"); } } catch (Exception e) { _logger.Error(e); } }
/// <summary> /// Reads and parses a forwarder message from a network stream /// </summary> /// <param name="stream">The stream to read from</param> /// <returns>A new ForwarderMessage containing the read data, or null if an error occurred</returns> private async Task <ForwarderMessage> ReadMessageAsync(NetworkStream stream) { // TODO: Could possibly be moved to StreamExtensions, but it's only used in this class ForwarderMessage message; try { byte[] timestampBytes = await stream.ReadFixedAmountAsync(sizeof(long)); if (timestampBytes == null) { return(null); } long timestamp = BitConverter.ToInt64(timestampBytes, 0); message = new ForwarderMessage(timestamp); byte[] valuesCountBytes = await stream.ReadFixedAmountAsync(1); if (valuesCountBytes == null) { return(null); } for (int i = 0; i < valuesCountBytes[0]; i++) { byte[] channelNameLengthBytes = await stream.ReadFixedAmountAsync(1); if (channelNameLengthBytes == null) { return(null); } byte[] channelNameBytes = await stream.ReadFixedAmountAsync(channelNameLengthBytes[0]); if (channelNameBytes == null) { return(null); } byte[] dataBytes = await stream.ReadFixedAmountAsync(sizeof(double)); if (dataBytes == null) { return(null); } string channelName = Encoding.UTF8.GetString(channelNameBytes); double data = BitConverter.ToDouble(dataBytes, 0); if (!message.DataValues.TryAdd(channelName, data)) { _logger.Warn("Received a message with 2 data points from the same channel"); } } } catch (IOException e) when(e.InnerException is SocketException socketException) { // These exceptions usually gets called when the client disconnects _logger.Trace("Lost connection: SocketError.{0}", socketException.SocketErrorCode); return(null); } catch (Exception e) { _logger.Error(e); return(null); } return(message); }