-
Notifications
You must be signed in to change notification settings - Fork 246
Refactor Connect/Disconnect out to CClient with an explicit connection state #3805
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -59,6 +59,7 @@ CClient::CClient ( const quint16 iPortNumber, | |
| strClientName ( strNClientName ), | ||
| pSignalHandler ( CSignalHandler::getSingletonP() ), | ||
| pSettings ( nullptr ), | ||
| eConnectionState ( CS_DISCONNECTED ), | ||
| Channel ( false ), /* we need a client channel -> "false" */ | ||
| CurOpusEncoder ( nullptr ), | ||
| CurOpusDecoder ( nullptr ), | ||
|
|
@@ -145,7 +146,7 @@ CClient::CClient ( const quint16 iPortNumber, | |
| // The first ConClientListMesReceived handler performs the necessary cleanup and has to run first: | ||
| QObject::connect ( &Channel, &CChannel::ConClientListMesReceived, this, &CClient::OnConClientListMesReceived ); | ||
|
|
||
| QObject::connect ( &Channel, &CChannel::Disconnected, this, &CClient::Disconnected ); | ||
| QObject::connect ( &Channel, &CChannel::Disconnected, this, &CClient::Stop ); | ||
|
|
||
| QObject::connect ( &Channel, &CChannel::NewConnection, this, &CClient::OnNewConnection ); | ||
|
|
||
|
|
@@ -618,6 +619,9 @@ bool CClient::SetServerAddr ( QString strNAddr ) | |
| // apply address to the channel | ||
| Channel.SetAddress ( HostAddress ); | ||
|
|
||
| // By default, set server name to HostAddress. If using the Connect() method, this may be overwritten | ||
| SetConnectedServerName ( HostAddress.toString() ); | ||
|
|
||
| return true; | ||
| } | ||
| else | ||
|
|
@@ -905,11 +909,8 @@ void CClient::OnHandledSignal ( int sigNum ) | |
| { | ||
| case SIGINT: | ||
| case SIGTERM: | ||
| // if connected, terminate connection (needed for headless mode) | ||
| if ( IsRunning() ) | ||
| { | ||
| Stop(); | ||
| } | ||
| // if connected, Stop client (needed for headless mode) | ||
| Stop(); | ||
|
|
||
| // this should trigger OnAboutToQuit | ||
| QCoreApplication::instance()->exit(); | ||
|
|
@@ -1003,6 +1004,9 @@ void CClient::OnClientIDReceived ( int iServerChanID ) | |
| SetRemoteChanGain ( iChanID, 0, false ); | ||
| } | ||
|
|
||
| // the server has assigned us a channel ID, so the connection is established | ||
| SetConnectionState ( CS_CONNECTED ); | ||
|
|
||
| emit ClientIDReceived ( iChanID ); | ||
| } | ||
|
|
||
|
|
@@ -1045,8 +1049,19 @@ void CClient::Start() | |
| // Disable hibernation or display dimming if the app is running on Windows | ||
| SetThreadExecutionState ( ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED ); | ||
| #endif | ||
|
|
||
| // the connection is requested now but not yet established: the transition | ||
| // to CS_CONNECTED happens when the server assigns our channel ID | ||
| // (see OnClientIDReceived) | ||
| SetConnectionState ( CS_CONNECTING ); | ||
|
|
||
| emit Connecting ( GetConnectedServerName() ); | ||
| } | ||
|
|
||
| /// @method | ||
| /// @brief Stops client and disconnects from server | ||
| /// @emit Disconnected | ||
| /// Use to set CClientDlg to show not being connected | ||
| void CClient::Stop() | ||
| { | ||
| // stop audio interface | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The IsRunning() guard is not needed? (don't remember the initial argument of my PR.)
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please check this - especially for the sigterm case: https://github.com/jamulussoftware/jamulus/pull/3805/changes#diff-7bc46552432fa03e009f9f455e4729312853d92775935ef20849797f24ac0c60R912-R913
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed together with the guard above in efee722 —
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch — a guard is needed, but
Fixed in efee722 by keying the guard off the explicit state this PR introduces, and routing SIGTERM through void CClient::Disconnect()
{
if ( GetConnectionState() != CS_DISCONNECTED ) { Stop(); }
}
// OnHandledSignal now: Disconnect(); (was: raw Stop();)So Verified with a UDP capture: SIGTERM while connected → server receives |
||
|
|
@@ -1088,6 +1103,66 @@ void CClient::Stop() | |
| // Allow hibernation or display dimming if the app is running again (Windows) | ||
| SetThreadExecutionState ( ES_CONTINUOUS ); | ||
| #endif | ||
|
|
||
| SetConnectionState ( CS_DISCONNECTED ); | ||
|
|
||
| // emit Disconnected() to inform UI of disconnection | ||
| emit Disconnected(); | ||
| } | ||
|
|
||
| /// @method | ||
| /// @brief Stops the client if the client is running | ||
| /// @emit Disconnected | ||
| void CClient::Disconnect() | ||
| { | ||
| if ( IsRunning() ) | ||
| { | ||
| Stop(); | ||
| } | ||
| } | ||
|
|
||
| /// @method | ||
| /// @brief Connects to strServerAddress. If a connection is currently requested | ||
| /// or established, that connection is terminated first. | ||
| /// @emit Connecting (strServerName) if SetServerAddr was valid. emit happens through Start(). | ||
| /// Use to set CClientDlg to show being connected | ||
| /// @emit ConnectingFailed (error) if an error occurred | ||
| /// Use to display error message in CClientDlg | ||
| /// @param strServerAddress - the server address to connect to | ||
| /// @param strServerName - the human readable server name passed to Connecting() | ||
| void CClient::Connect ( QString strServerAddress, QString strServerName ) | ||
| { | ||
| try | ||
| { | ||
| // disconnect from any current server first so that connecting to a | ||
| // different server while connected behaves as a reconnect | ||
| Disconnect(); | ||
|
|
||
| // Set server address and connect if valid address was supplied | ||
| if ( SetServerAddr ( strServerAddress ) ) | ||
| { | ||
| SetConnectedServerName ( strServerName ); | ||
| Start(); | ||
| } | ||
| else | ||
| { | ||
| throw CGenErr ( tr ( "Received invalid server address. Please check for typos in the provided server address." ) ); | ||
| } | ||
| } | ||
| catch ( const CGenErr& generr ) | ||
| { | ||
| Stop(); | ||
| emit ConnectingFailed ( generr.GetErrorText() ); | ||
| } | ||
| } | ||
|
|
||
| void CClient::SetConnectionState ( const EConnectionState eNewConnectionState ) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add documentation like for connect/disconnect.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in efee722 — added a |
||
| { | ||
| if ( eConnectionState != eNewConnectionState ) | ||
| { | ||
| eConnectionState = eNewConnectionState; | ||
| emit ConnectionStateChanged ( eConnectionState ); | ||
| } | ||
| } | ||
|
|
||
| void CClient::Init() | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -159,6 +159,15 @@ class CClient : public QObject | |
|
|
||
| void Start(); | ||
| void Stop(); | ||
| void Disconnect(); | ||
| void Connect ( QString strServerAddress, QString strServerName ); | ||
|
|
||
| // The ConnectedServerName is emitted by Connecting() to update the UI with a human readable server name | ||
| void SetConnectedServerName ( const QString strServerName ) { strConnectedServerName = strServerName; }; | ||
| QString GetConnectedServerName() const { return strConnectedServerName; }; | ||
|
|
||
| EConnectionState GetConnectionState() const { return eConnectionState; } | ||
|
|
||
| bool IsRunning() { return Sound.IsRunning(); } | ||
| bool IsCallbackEntered() const { return Sound.IsCallbackEntered(); } | ||
| bool SetServerAddr ( QString strNAddr ); | ||
|
|
@@ -353,6 +362,14 @@ class CClient : public QObject | |
| void FreeClientChannel ( const int iServerChannelID ); | ||
| int FindClientChannel ( const int iServerChannelID, const bool bCreateIfNew ); // returns a client channel ID or INVALID_INDEX | ||
| bool ReorderLevelList ( CVector<uint16_t>& vecLevelList ); // modifies vecLevelList, passed by reference | ||
| // information for the connected server | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Which kind of information? Comment is unclear.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in efee722 — reworded to "human-readable name of the current server, shown in the UI". |
||
| QString strConnectedServerName; | ||
|
|
||
| // single source of truth for the connection state, only to be modified | ||
| // via SetConnectionState() so that every change emits ConnectionStateChanged() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does not really add much and smells to much AI ish.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in efee722 — trimmed to "current connection state; set only via SetConnectionState()". |
||
| EConnectionState eConnectionState; | ||
|
|
||
| void SetConnectionState ( const EConnectionState eNewConnectionState ); | ||
|
|
||
| // only one channel is needed for client application | ||
| CChannel Channel; | ||
|
|
@@ -464,7 +481,8 @@ protected slots: | |
| { | ||
| if ( InetAddr == Channel.GetAddress() ) | ||
| { | ||
| emit Disconnected(); | ||
| // Stop client in case it received a Disconnection request | ||
| Stop(); | ||
| } | ||
| } | ||
| void OnCLPingReceived ( CHostAddress InetAddr, int iMs ); | ||
|
|
@@ -507,7 +525,11 @@ protected slots: | |
|
|
||
| void CLChannelLevelListReceived ( CHostAddress InetAddr, CVector<uint16_t> vecLevelList ); | ||
|
|
||
| void ConnectionStateChanged ( EConnectionState eConnectionState ); | ||
| void Connecting ( QString strServerName ); | ||
| void ConnectingFailed ( QString errorMessage ); | ||
| void Disconnected(); | ||
|
|
||
| void SoundDeviceChanged ( QString strError ); | ||
| void ControllerInFaderLevel ( int iChannelIdx, int iValue ); | ||
| void ControllerInPanValue ( int iChannelIdx, int iValue ); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -50,7 +50,6 @@ | |
| /* Implementation *************************************************************/ | ||
| CClientDlg::CClientDlg ( CClient* pNCliP, | ||
| CClientSettings* pNSetP, | ||
| const QString& strConnOnStartupAddress, | ||
| const bool bNewShowComplRegConnList, | ||
| const bool bShowAnalyzerConsole, | ||
| const bool bMuteStream, | ||
|
|
@@ -292,14 +291,6 @@ CClientDlg::CClientDlg ( CClient* pNCliP, | |
| TimerCheckAudioDeviceOk.setSingleShot ( true ); // only check once after connection | ||
| TimerDetectFeedback.setSingleShot ( true ); | ||
|
|
||
| // Connect on startup ------------------------------------------------------ | ||
| if ( !strConnOnStartupAddress.isEmpty() ) | ||
| { | ||
| // initiate connection (always show the address in the mixer board | ||
| // (no alias)) | ||
| Connect ( strConnOnStartupAddress, strConnOnStartupAddress ); | ||
| } | ||
|
|
||
| // File menu -------------------------------------------------------------- | ||
| QMenu* pFileMenu = new QMenu ( tr ( "&File" ), this ); | ||
|
|
||
|
|
@@ -507,7 +498,11 @@ CClientDlg::CClientDlg ( CClient* pNCliP, | |
| // other | ||
| QObject::connect ( pClient, &CClient::ConClientListMesReceived, this, &CClientDlg::OnConClientListMesReceived ); | ||
|
|
||
| QObject::connect ( pClient, &CClient::Disconnected, this, &CClientDlg::OnDisconnected ); | ||
| QObject::connect ( pClient, &CClient::Connecting, this, &CClientDlg::OnConnecting ); | ||
|
|
||
| QObject::connect ( pClient, &CClient::ConnectingFailed, this, &CClientDlg::OnConnectingFailed ); | ||
|
|
||
| QObject::connect ( pClient, &CClient::Disconnected, this, &CClientDlg::OnDisconnect ); | ||
|
|
||
| QObject::connect ( pClient, &CClient::ChatTextReceived, this, &CClientDlg::OnChatTextReceived ); | ||
|
|
||
|
|
@@ -646,11 +641,8 @@ void CClientDlg::closeEvent ( QCloseEvent* Event ) | |
| ConnectDlg.close(); | ||
| AnalyzerConsole.close(); | ||
|
|
||
| // if connected, terminate connection | ||
| if ( pClient->IsRunning() ) | ||
| { | ||
| pClient->Stop(); | ||
| } | ||
| // Disconnect if needed | ||
| pClient->Disconnect(); | ||
|
|
||
| // make sure all current fader settings are applied to the settings struct | ||
| MainMixerBoard->StoreAllFaderSettings(); | ||
|
|
@@ -768,15 +760,8 @@ void CClientDlg::OnConnectDlgAccepted() | |
| } | ||
| } | ||
|
|
||
| // first check if we are already connected, if this is the case we have to | ||
| // disconnect the old server first | ||
| if ( pClient->IsRunning() ) | ||
| { | ||
| Disconnect(); | ||
| } | ||
|
|
||
| // initiate connection | ||
| Connect ( strSelectedAddress, strMixerBoardLabel ); | ||
| // initiate connection (terminates any current connection first) | ||
| pClient->Connect ( strSelectedAddress, strMixerBoardLabel ); | ||
|
|
||
| // reset flag | ||
| bConnectDlgWasShown = false; | ||
|
|
@@ -788,11 +773,12 @@ void CClientDlg::OnConnectDisconBut() | |
| // the connect/disconnect button implements a toggle functionality | ||
| if ( pClient->IsRunning() ) | ||
| { | ||
| Disconnect(); | ||
| SetMixerBoardDeco ( RS_UNDEFINED, pClient->GetGUIDesign() ); | ||
| pClient->Disconnect(); | ||
| } | ||
| else | ||
| { | ||
| // If the client isn't running, we assume that we weren't connected. Thus show the connect dialog | ||
| // TODO: Refactor to have robust error handling | ||
| ShowConnectionSetupDialog(); | ||
| } | ||
| } | ||
|
|
@@ -904,7 +890,7 @@ void CClientDlg::OnLicenceRequired ( ELicenceType eLicenceType ) | |
| // disconnect from that server. | ||
| if ( !LicenceDlg.exec() ) | ||
| { | ||
| Disconnect(); | ||
| pClient->Disconnect(); | ||
| } | ||
|
|
||
| // unmute the client output stream if local mute button is not pressed | ||
|
|
@@ -1205,10 +1191,7 @@ void CClientDlg::OnSoundDeviceChanged ( QString strError ) | |
| if ( !strError.isEmpty() ) | ||
| { | ||
| // the sound device setup has a problem, disconnect any active connection | ||
| if ( pClient->IsRunning() ) | ||
| { | ||
| Disconnect(); | ||
| } | ||
| pClient->Disconnect(); | ||
|
|
||
| // show the error message of the device setup | ||
| QMessageBox::critical ( this, APP_NAME, strError, tr ( "Ok" ), nullptr ); | ||
|
|
@@ -1236,65 +1219,38 @@ void CClientDlg::OnCLPingTimeWithNumClientsReceived ( CHostAddress InetAddr, int | |
| ConnectDlg.SetPingTimeAndNumClientsResult ( InetAddr, iPingTime, iNumClients ); | ||
| } | ||
|
|
||
| void CClientDlg::Connect ( const QString& strSelectedAddress, const QString& strMixerBoardLabel ) | ||
| void CClientDlg::OnConnecting ( const QString& strMixerBoardLabel ) | ||
| { | ||
| // set address and check if address is valid | ||
| if ( pClient->SetServerAddr ( strSelectedAddress ) ) | ||
| { | ||
| // try to start client, if error occurred, do not go in | ||
| // running state but show error message | ||
| try | ||
| { | ||
| if ( !pClient->IsRunning() ) | ||
| { | ||
| pClient->Start(); | ||
| } | ||
| } | ||
|
|
||
| catch ( const CGenErr& generr ) | ||
| { | ||
| // show error message and return the function | ||
| QMessageBox::critical ( this, APP_NAME, generr.GetErrorText(), "Close", nullptr ); | ||
| return; | ||
| } | ||
|
|
||
| // hide label connect to server | ||
| lblConnectToServer->hide(); | ||
| lbrInputLevelL->setEnabled ( true ); | ||
| lbrInputLevelR->setEnabled ( true ); | ||
| // hide label connect to server | ||
| lblConnectToServer->hide(); | ||
| lbrInputLevelL->setEnabled ( true ); | ||
| lbrInputLevelR->setEnabled ( true ); | ||
|
|
||
| // change connect button text to "disconnect" | ||
| butConnect->setText ( tr ( "&Disconnect" ) ); | ||
| // change connect button text to "disconnect" | ||
| butConnect->setText ( tr ( "&Disconnect" ) ); | ||
|
|
||
| // set server name in audio mixer group box title | ||
| MainMixerBoard->SetServerName ( strMixerBoardLabel ); | ||
| // set server name in audio mixer group box title | ||
| MainMixerBoard->SetServerName ( strMixerBoardLabel ); | ||
|
|
||
| // start timer for level meter bar and ping time measurement | ||
| TimerSigMet.start ( LEVELMETER_UPDATE_TIME_MS ); | ||
| TimerBuffersLED.start ( BUFFER_LED_UPDATE_TIME_MS ); | ||
| TimerPing.start ( PING_UPDATE_TIME_MS ); | ||
| TimerCheckAudioDeviceOk.start ( CHECK_AUDIO_DEV_OK_TIME_MS ); // is single shot timer | ||
| // start timer for level meter bar and ping time measurement | ||
| TimerSigMet.start ( LEVELMETER_UPDATE_TIME_MS ); | ||
| TimerBuffersLED.start ( BUFFER_LED_UPDATE_TIME_MS ); | ||
| TimerPing.start ( PING_UPDATE_TIME_MS ); | ||
| TimerCheckAudioDeviceOk.start ( CHECK_AUDIO_DEV_OK_TIME_MS ); // is single shot timer | ||
|
|
||
| // audio feedback detection | ||
| if ( pSettings->bEnableFeedbackDetection ) | ||
| { | ||
| TimerDetectFeedback.start ( DETECT_FEEDBACK_TIME_MS ); // single shot timer | ||
| bDetectFeedback = true; | ||
| } | ||
| // audio feedback detection | ||
| if ( pSettings->bEnableFeedbackDetection ) | ||
| { | ||
| TimerDetectFeedback.start ( DETECT_FEEDBACK_TIME_MS ); // single shot timer | ||
| bDetectFeedback = true; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not part of this PR, but yes, this should also not be in the UI.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed, out of scope for this PR — left as-is for a separate change. |
||
| } | ||
| } | ||
|
|
||
| void CClientDlg::Disconnect() | ||
| { | ||
| // only stop client if currently running, in case we received | ||
| // the stopped message, the client is already stopped but the | ||
| // connect/disconnect button and other GUI controls must be | ||
| // updated | ||
| if ( pClient->IsRunning() ) | ||
| { | ||
| pClient->Stop(); | ||
| } | ||
| void CClientDlg::OnConnectingFailed ( const QString& strError ) { QMessageBox::critical ( this, APP_NAME, strError, tr ( "Close" ), nullptr ); } | ||
|
|
||
| void CClientDlg::OnDisconnect() | ||
| { | ||
| // change connect button text to "connect" | ||
| butConnect->setText ( tr ( "C&onnect" ) ); | ||
|
|
||
|
|
@@ -1336,6 +1292,9 @@ void CClientDlg::Disconnect() | |
|
|
||
| // clear mixer board (remove all faders) | ||
| MainMixerBoard->HideAll(); | ||
|
|
||
| // Reset the deco | ||
| SetMixerBoardDeco ( RS_UNDEFINED, pClient->GetGUIDesign() ); | ||
| } | ||
|
|
||
| void CClientDlg::UpdateDisplay() | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.