/// <summary> /// Pushes a new AIC-Message to the client. /// </summary> /// <param name="alarms">The list of active alarms.</param> /// <param name="wasConnectionState">The WAS connection-state.</param> /// <param name="type">The type of message (request or response).</param> private void PushAicMessage(List <Alarm> alarms, bool wasConnectionState, MessageType type) { if (this.mTcpClient == null || !this.mTcpClient.IsConnected()) { this.mLogger.LogWarning(string.Format("Can't push message because TCP-Client (IP: {0}) has already gone", this.ClientAddress), WarningType.ClientSession_CantPushMessage); return; } AicMessage message = new AicMessage { Alarms = alarms, ConnectionWasToServerOk = wasConnectionState, MessageType = type }; try { message.Serialize(this.mTcpClient.GetStream().GetUnderlyingStream()); this.mLogger.LogInformation(string.Format("New {0}-push to client (IP: {1}) performed", type, this.ClientAddress), InformationType.ClientSession_PerformedPush); } catch (Exception ex) { this.mLogger.LogError("(ClientSession/PushNewState/Exception)", ErrorType.Undefined, ex); } }
/// <summary> /// Checks if two AicMessage objects have the same values. /// </summary> /// <param name="other">Another AicMessage.</param> /// <returns>True if the values are equal.</returns> public bool Equals(AicMessage other) { if (ReferenceEquals(null, other)) { return(false); } if (ReferenceEquals(this, other)) { return(true); } return(MessageType == other.MessageType && ConnectionWasToServerOk == other.ConnectionWasToServerOk && Alarms.SequenceEqual(other.Alarms)); }
/// <summary> /// Sends the KeepAlive-Message to the client to avoid dirty connection states. /// </summary> private void SendKeepAliveMessage() { try { AicMessage message = new AicMessage { Alarms = new List <Alarm>(), ConnectionWasToServerOk = true, MessageType = MessageType.KeepAlive }; message.Serialize(this.mTcpClient.GetStream().GetUnderlyingStream()); } catch (Exception ex) { this.mLogger.LogError("(ClientListener/SendKeepAliveMessage/Exception)", ErrorType.Undefined, ex); } }
/// <summary> /// Method for handling the connection (requests / responses) with the client. You must not call this method twice or after the session got destroyed. /// In case the connection to the client got broken, the session will destroy itself. /// </summary> private void SessionHandling(object state) { CancellationToken cancellationToken = (CancellationToken)state; System.Timers.Timer timer = null; this.PushNewState(); try { timer = new System.Timers.Timer(this.mClientKeepAliveTimeout.TotalMilliseconds); timer.Elapsed += delegate { SendKeepAliveMessage(); }; timer.Start(); while (!cancellationToken.IsCancellationRequested) { // Wait while there's no request from the client and check if the connection got refused / broken while (!this.mTcpClient.GetStream().DataAvailable) { cancellationToken.ThrowIfCancellationRequested(); Thread.Sleep(Constants.ClientCycleTimeout); // Check connection if (!this.mTcpClient.IsConnected()) { throw new SocketException((int)SocketError.ConnectionReset); } // If a response after a push is pending if (this.mPushAckPending) { this.mAckCycleTimeElapsed += Constants.ClientCycleTimeout; // Appropriate amount of time is over - now send a request again if (this.mAckCycleTimeElapsed >= Constants.NetworkTimeout) { this.PushAicMessage(this.mGetAlarmList(), this.mGetWasConnectionState(), MessageType.Request); this.mAckCycleTimeElapsed = 0; this.mPushRetryCount++; this.mLogger.LogWarning( string.Format("No response from client yet (IP: {0}). Retry {1} from {2}", this.ClientAddress, mPushRetryCount, Constants.MaxPushRetry), WarningType.ClientSession_NoClientResponse); // No response after several retries if (this.mPushRetryCount == Constants.MaxPushRetry) { this.mLogger.LogError(string.Format("No response-message after {0} retries", Constants.MaxPushRetry), ErrorType.ClientSession_NoClientResponse); throw new AicException(string.Format("No response-message after {0} retries", Constants.MaxPushRetry), AicExceptionType.ClientSession_NoClientResponse); } } } } AicMessage message = null; try { using (MemoryStream memoryStream = this.mTcpClient.ReadNetworkStream(AicMessage.XmlEndTagRegex)) { memoryStream.Position = 0; message = AicMessage.Deserialize(memoryStream); } } catch (Exception ex) { this.mLogger.LogError("(ClientSession/SessionHandling/Exception)", ErrorType.Undefined, ex); } if (message == null) { continue; } else if (message.MessageType == MessageType.Request) { this.mLogger.LogInformation(string.Format("Request from client (IP: {0})", this.ClientAddress), InformationType.ClientSession_ReceivedRequest); PushAicMessage(this.mGetAlarmList(), this.mGetWasConnectionState(), MessageType.Response); } else if (message.MessageType == MessageType.Response) { this.mLogger.LogInformation(string.Format("Response from client (IP: {0})", this.ClientAddress), InformationType.ClientSession_ReceivedResponse); lock (this.mLocker) { this.mPushAckPending = false; this.mPushRetryCount = 0; Monitor.Pulse(this.mLocker); } } else if (message.MessageType == MessageType.KeepAlive) { this.mLogger.LogInformation(string.Format("Keep-Alive from client (IP: {0})", this.ClientAddress), InformationType.ClientSession_ReceivedKeepAlive); } else { this.mLogger.LogError(string.Format("Message type '{0}' isn't defined", message.MessageType), ErrorType.ClientSession_UnknownMessageType); throw new AicException(string.Format("Message type '{0}' isn't defined", message.MessageType), AicExceptionType.ClientSession_UnknownMessageType); } } } catch (OperationCanceledException) { this.mLogger.LogInformation(string.Format("Thread handling connection with client (IP: {0}) canceled", this.ClientAddress), InformationType.ClientSession_ThreadCanceled); } catch (SocketException ex) { if (ex.SocketErrorCode == SocketError.ConnectionReset) { this.mLogger.LogInformation(string.Format("Stopped listening to client (IP: {0})", this.ClientAddress), InformationType.ClientSession_StoppedListening); } else { this.mLogger.LogError("(ClientSession/SessionHandling/SocketException)", ErrorType.UnknownSocketException, ex); } } catch (Exception ex) { this.mLogger.LogError("(ClientSession/SessionHandling/Exception)", ErrorType.Undefined, ex); } finally { if (timer != null) { timer.Dispose(); } lock (this.mLocker) { if (!this.SessionDestroyed) { this.DestroySession(true, false); } } } }
/// <summary> /// Initializes a new instance of the MessageReceivedEventArgs class. /// </summary> /// <param name="aicMessage">The received AicMessage.</param> public MessageReceivedEventArgs(AicMessage aicMessage) { AicMessage = aicMessage; }
/// <summary> /// Starts listening to the WAS. /// <exception cref="InvalidOperationException">Thrown when the WAS listener is running.</exception> /// <exception cref="InvalidOperationException">Thrown when the reconnect process is currently running.</exception> /// <exception cref="AicException">If the ping to the WAS failed.</exception> /// </summary> public void StartWasListening() { lock (this.mLockerObject) { if (this.IsWasListenerRunning) { this.mLogger.LogError("WAS listener is already running", ErrorType.ListeningManager_WasListenerAlreadyRunning); throw new InvalidOperationException("WAS listener is already running"); } if (this.mIsWasReconnectRunning) { this.mLogger.LogError("The WAS reconnect process is currently running", ErrorType.ListeningManager_WasReconnectCurrentlyRunning); throw new InvalidOperationException("The WAS reconnect process is currently running"); } // Check if the WAS can be pinged if (!Network.PingAddress(AicSettings.Global.WasIp)) { this.mLogger.LogWarning("WAS can't be pinged", WarningType.ListeningManager_CantPingWas); throw new AicException("WAS can't be pinged", AicExceptionType.ListeningManager_CantPingWas); } try { // isShutdown: True if the broken connection is forced (due to shutdown) this.mWasListener.WasConnectionStateChanged += delegate(bool connected, bool isShutdown) { this.mLogger.LogInformation(string.Format("WAS connection-state changed (Connected: {0} - Shutdown: {1})", connected, isShutdown), InformationType.ListeningManager_WasConnectionStateChanged); lock (this.mLockerObject) { this.mIsWasListenerRunning = connected; if (this.WasConnectionStateChanged != null) { this.WasConnectionStateChanged(connected); } if (MessageReceived != null) { var aicMessage = new AicMessage { Alarms = mAlarmState.Alarms, ConnectionWasToServerOk = connected }; var args = new MessageReceivedEventArgs(aicMessage); MessageReceived(null, args); } if (!connected && !isShutdown) { // Broken connection (not forced) this.mLogger.LogWarning("WAS connection got broken", WarningType.ListeningManager_BrokenWasConnection); this.TryWASReconnect(); } } if (this.mClientListener != null) { // Push new state of the backend to the clients this.mClientListener.PushNewState(); } }; this.mWasListener.WasObjectChanged += delegate(WasObject obj) { this.mLogger.LogInformation("New WAS-Data available", InformationType.ListeningManager_NewWasDataAvailable); this.mAlarmState.UpdateAlarmObject(obj); if (this.AlarmsChanged != null) { this.AlarmsChanged(this.mAlarmState.Alarms); } if (MessageReceived != null) { var aicMessage = new AicMessage { Alarms = mAlarmState.Alarms, ConnectionWasToServerOk = true }; var args = new MessageReceivedEventArgs(aicMessage); MessageReceived(null, args); } if (AicSettings.Global.UploadEnabled) { this.mAlarmState.Alarms.ForEach(alarm => AlarmSend.SendAsync(AicSettings.Global.UploadUrl, alarm, (id, ex) => { this.mLogger.LogError("Error while uploading alarm (ID:" + id + ")!", ex); })); } if (this.mClientListener != null) { this.mClientListener.PushNewState(); } }; this.mWasListener.StartListening(); this.mIsWasListenerRunning = true; } catch (Exception ex) { this.mLogger.LogError("(ListeningManager/StartWasListening/Exception)", ErrorType.Undefined, ex); throw ex; } } }