W&T collega
Adattatori per TCP/IP, Ethernet, RS-232, RS-485, USB, 20 mA, Fibra ottica di vetro e plastica, http, SNMP, OPC, Modbus TCP, I/O digitale, I/O analogico, ISA, PCI

Tutorial sul Web-IO digitale;

Web-IO digitale con Visual C++ controllo e monitoraggio


Per la creazione di applicazioni Windows, Visual C++ rappresenta una delle piattaforme di sviluppo più utilizzate.

Controllo e monitoraggio con C++

Con il seguente esempio di programma C++ potete raffigurare il vostro Web-IO digitale con i suoi input e output in un’applicazione Windows. Inoltre potete collegare gli output del Web-IO.


Preparativi


Combinazione dei diversi elementi di comando e oggetti di visualizzazione nel modulo Visual C++

Forma Visual C++

Definizione nei file header

Definizione MyDlgData.h

Per evitare una definizione multipla e poter abbinare le variabili membro all’oggetto m_rcData che viene passato per le classi, realizziamo una definizione in MyDlgData.h alcune variabili membro. Inoltre viene qui definito anche CStatusBarCtrl l’oggetto derivato da m_myStatBar.

Per consentire l’accesso a funzioni membro estese in combinazione con l’oggetto dati, questa classe eredita da CObject. Poiché la classe funge prevalentemente da interfaccia, non vengono qui definite altre funzioni membro.

						
							class MyDlgData : public CObject {
						public: //--- Konstruktion
							MyDlgData::MyDlgData() {};
							virtual ~MyDlgData();

						public:
							//---MemberVariablen f. Connection
							CString m_stIPNum;
							CString m_stDataRequest;
							CString m_stRecvbuf;
							CString m_stLookingFor;
							int m_iPortText{};
							BOOL m_bButtonIsChecked{};
							BOOL m_bAbleToRecvDataFromExtern{};
							BOOL m_bCounterNeedUpdateInGUI{};
							BOOL m_bIsThisMentForInput{};

							//---MemberVariablen f. CWebIODlg
							int m_iPolling{};
							CString m_stCounter0;
							CString m_stCounter1;
							CString m_stPassword;
							CString m_stStatBar;
							HWND m_hMainWindow{};

							//---Objekte f. CWebIODlg
							CStatusBarCtrl m_myStatBar{};

							//---MemberVariablen f. ProcessingInputOutput
							int m_iHowMuchInputsDoWeHave{};

							//---Objekte f. ProcessingInputOutput
							CStringArray m_aShortenedValueOnlyArray;
						};
  						
  					
Definizione di Connection.h

Il compito di questa classe è l’affidabile realizzazione di una connessione TCP attraverso un’interfaccia socket. Inoltre contiene funzioni membro per l’ invio e la ricezione di dati. Di seguito esponiamo le variabili membro e le funzioni membro definite precedentemente:

						
							class Connection : public ProcessingInputOutput, public CSocket {
						public: //---Konstruktion
							Connection( MyDlgData &rData );
							~Connection();
							DECLARE_DYNAMIC( Connection );

						private: //--- Attribute
							BOOL m_bResult{};

						public: //--- Attribute
							int m_iResult{};
							CString m_stError;

						public: //--- Implementierungen
							BOOL CreateSocket( UINT uiPrefPort );
							BOOL ConnectSocket( LPCTSTR lpPrefIp, UINT uiPrefPort );
							BOOL SendDataViaSocket();

						protected: //--- Ueberladungen MFC
							virtual void OnReceive( int nErrorCode );
							virtual void OnClose( int nErrorCode );
						};
						
					

Questa classe eredita da ProcessingInputOutput, per poter utilizzare le funzioni membro elaborate successivamente. Inoltre eredita da CSocket, poiché la classe costituisce la base della connessione attraverso le interfacce socket via UDP o TCP.
La connessione avviene in modo asincrono, ovvero lo scambio di dati avviene senza ritmo predefinito e i processi vengono elaborati in secondo piano.
Per questo ci avvaliamo di funzioni di callback che vengono attivate quando viene annunciato un evento Receive (possono essere ricevuti dati) o un evento Close (la connessione è stata interrotta).
Il rispettivo trigger decide quindi dello svolgimento del programma.

Definizione di ProcessingInputOutput.h

Affinché i dati ricevuti dal Web-IO possano essere elaborati correttamente, è necessaria una classe propria, di cui descriviamo qui di seguito la definizione.


					class ProcessingInputOutput {
					public: //--- Konstruktion
						ProcessingInputOutput( MyDlgData &rData );
						virtual ~ProcessingInputOutput();

					private: //--- Attribute
						CString m_stError;
						CString m_stRawInputOutput;

					public: //--- Implementierungen
						void DefineSetOutputRequest( CString stIDNumber );
						void DefineGetAllOutputRequest();
						void DefineGetAllInputRequest();
						void DefineGetAllOutRequest();

						void DefineGetCounterRequest( CString stIDNumber );
						void DefineGetCounterAllRequest();
						void DefineSetClearRequest( CString stIDNumber );
						void DefineClearAllRequest();

						void RemoveDelimiterAndSaveIntoArray( CString stDel );
						void ExtractValuesAndShortenArray();
						void ChooseRightOperation();
						void ProcessRequestAfterLookingForCounter0Only( int iStartingIndex );
						void ProcessRequestAfterLookingForCounter1Only( int iStartingIndex );
						void ProcessRequestAfterLookingForAllCounter( int iStartingIndex );
						void ProcessRequestAfterLookingForAllInputs( int iStartingIndex );
						void ProcessRequestAfterLookingForAllOutputs( int iStartingIndex );
						void BinaryANDing(CString m_stRawInputOutput );

					public: //---Datenstruktur fuer die Uebergabe von BinaryANDing zu ProcessCheckBoxStatus
						typedef struct MyIODataStruct {
							int aIOValues[4]{ 0 };
							int Input0Value = 0;
							int Input1Value = 0;
							int Output0Value = 0;
							int Output1Value = 0;
						}MyIODataStruct;
						MyIODataStruct m_IODataStruct;

					protected: //--- Datenobjekte/ Datenarrays
						MyDlgData &m_rcData;
						CStringArray m_aAnswerStringArray;
					};
					

Questa classe è responsabile esclusivamente per l’elaborazione dei dati trasferiti dall’oggetto dati.
A tal scopo utilizziamo il collegamento della classe con l’oggetto m_rcData.
Oltre alle funzioni membro che dividono, copiano, memorizzano le stringhe di risposta o che le reiterano per applicare operazioni, realizziamo in MyIODataStruct una struttura che ci consente di amministrare comodamente una maggiore quantità di dati utili.

Definizione di CWebIODlg.h

Della rappresentazione grafica della finestra di dialogo e del suo contenuto si occupa CWebIODlg. Questa classe utilizza fra l’altro CDialogEx e viene collegata da MyDlgData con m_rcData e definisce essa stessa l’oggetto m_ccConnect dalla classe Connection.


						class CWebIODlg : public CDialogEx {
							DECLARE_DYNAMIC( CWebIODlg )

						#ifdef AFX_DESIGN_TIME
							enum {
								IDD = IDD_MFCA_DIALOG
							};
						#endif

						public: //--- Konstruktion
							CWebIODlg( MyDlgData &rcData, CWnd* pParent = nullptr );
							virtual ~CWebIODlg();

						protected: //--- Implementierungen / Generierte MFC Message-Map Funktionen
							//--- Timer
							afx_msg void OnTimer( UINT nIDEvent );

							//--- Buttons
							afx_msg void OnBnClickedConnect();
							afx_msg void OnBnClickedDisconnect();
							afx_msg void OnBnClickedRead0();
							afx_msg void OnBnClickedRead1();
							afx_msg void OnBnClickedReadall();
							afx_msg void OnBnClickedClear0();
							afx_msg void OnBnClickedClear1();
							afx_msg void OnBnClickedClearall();
							afx_msg void OnBnClickedReadall0();
							afx_msg void OnBnClickedReadall1();

							//--- Checkboxes
							afx_msg void OnBnClickedOutput0();
							afx_msg void OnBnClickedOutput1();
							afx_msg void OnBnClickedPolling0();
							afx_msg void OnBnClickedPolling1();
							afx_msg void OnBnClickedPollingcounter();

							//--- Edit Controls
							afx_msg void OnEnChangePollingtext();
							afx_msg void OnEnChangeIptext();
							afx_msg void OnEnChangePorttext();
							afx_msg void OnEnChangePasswordtext();

						protected: //--- Implementierung / Eigene Message-Map Funktionen
							afx_msg LRESULT UpdateGUI( WPARAM CounterNeedUpdate, LPARAM Lparam );
							afx_msg LRESULT ConnectionBrokeUp( WPARAM wParam, LPARAM lParam );
							afx_msg LRESULT CWebIODlg::ProcessCheckBoxStatus(WPARAM wParam, LPARAM lParam);
							afx_msg void OnSize( UINT nType, int cx, int cy );

							DECLARE_MESSAGE_MAP()

						protected: //--- Implementierung
							void ChangeAccess( bool bCanConnect );
							void ValidateDataAtStart();
							void InitializeStatusBar();
							void SendGotError();

						protected: //--- Ueberladungen MFC
							virtual BOOL OnInitDialog();
							virtual void DoDataExchange( CDataExchange* pDX );
							virtual BOOL DestroyWindow();

						protected: //--- Datenobjekte
							Connection m_ccConnect;
							MyDlgData  &m_rcData;

						private: //--- Attribute
							CString m_stBuffer;
							CString m_stError;
							int m_iResult{};
						};
					

Qui si trovano principalmente funzioni membro che vengono invocate come callback, se viene cliccato un pulsante, modificato un campo di testo o se viene realizzato qualcosa come la finestra di dialogo. A tal scopo vengono sovraccaricate alcune funzioni membro da CWebDialogEx.

Avvio del programma

Inizializzazione degli elementi di comando

Gli elementi di comando stessi possono essere realizzati in modo relativamente semplice attraverso l’assistente di classi integrato in VisualStudio. Con esso si possono collegare senza fatica anche p. es.: pulsanti con funzioni membro o variabili di controllo. Per prevenire l’utilizzo improprio da parte dell’utente, alcuni elementi di comando vengono parzialmente limitati dopo determinati appelli di funzione e all’avvio. Questo è definito in ChangeAccess().


					void CWebIODlg::ChangeAccess( bool bCanConnect ) {
					// Buttons
					GetDlgItem( IDC_CONNECT )->EnableWindow( bCanConnect );
					GetDlgItem( IDC_DISCONNECT )->EnableWindow( !bCanConnect );
					GetDlgItem( IDC_READ0 )->EnableWindow( !bCanConnect );
					GetDlgItem( IDC_READ1 )->EnableWindow( !bCanConnect );
					GetDlgItem( IDC_READALL )->EnableWindow( !bCanConnect );
					GetDlgItem( IDC_READALL0 )->EnableWindow( !bCanConnect );
					GetDlgItem( IDC_READALL1 )->EnableWindow( !bCanConnect );
					GetDlgItem( IDC_CLEAR0 )->EnableWindow( !bCanConnect );
					GetDlgItem( IDC_CLEAR1 )->EnableWindow( !bCanConnect );
					GetDlgItem( IDC_CLEARALL )->EnableWindow( !bCanConnect );

					//Checkboxes
					GetDlgItem( IDC_OUTPUT0 )->EnableWindow( !bCanConnect );
					GetDlgItem( IDC_OUTPUT1 )->EnableWindow( !bCanConnect );
					GetDlgItem( IDC_INPUT0 )->EnableWindow( !bCanConnect );
					GetDlgItem( IDC_INPUT1 )->EnableWindow( !bCanConnect );
					GetDlgItem( IDC_POLLING0 )->EnableWindow( !bCanConnect );
					GetDlgItem( IDC_POLLING1 )->EnableWindow( !bCanConnect );
					GetDlgItem( IDC_POLLINGCOUNTER )->EnableWindow( !bCanConnect );

					if( bCanConnect ) {
						CheckDlgButton( IDC_POLLING0, BST_UNCHECKED );
						CheckDlgButton( IDC_POLLING1, BST_UNCHECKED );
						CheckDlgButton( IDC_POLLINGCOUNTER, BST_UNCHECKED );

						OnBnClickedPolling0();
						OnBnClickedPolling1();
						OnBnClickedPollingcounter();
					}

					// Edit Controls
					GetDlgItem( IDC_POLLINGTEXT )->EnableWindow( !bCanConnect );
					GetDlgItem( IDC_IPTEXT )->EnableWindow( bCanConnect );
					GetDlgItem( IDC_PORTTEXT )->EnableWindow( bCanConnect );
					GetDlgItem( IDC_PASSWORDTEXT )->EnableWindow( bCanConnect );
				}
				

Per inizializzare la barra di stato e preparare gli elementi di comando in modo adeguato per l’utente, utilizziamo la funzione sovraccaricata OnInitDialog() da CDialogEx. Le operazioni qui contenute vengono richiamate ad ogni avvio. Inoltre utilizziamo la funzione membro, per collegare l’handle sulla finestra principale all’oggetto dati m_rcData.


				BOOL CWebIODlg::OnInitDialog() {
					CDialogEx::OnInitDialog();
					InitializeStatusBar();
					m_rcData.m_hMainWindow = m_hWnd;
					ChangeAccess( TRUE );
					return TRUE;
				}
				

In seguito la realizzazione della barra di stato.


				void CWebIODlg::InitializeStatusBar() {
					m_rcData.m_myStatBar.Create(
						WS_CHILD | WS_VISIBLE | WS_BORDER | CBRS_ALIGN_BOTTOM,
						CRect( 0, 0, 0, 0 ), this, 0
					);

					m_rcData.m_myStatBar.SetText( "Ready! No Connection.", 0, 0 );
				}
				

Affinché la barra di stato si adatti anche dopo la modifica delle dimensioni della finestra, anche qui OnSize() la funzione è stata sovraccaricata e comunica il cambiamento alla barra di stato.


				void CWebIODlg::OnSize( UINT nType, int cx, int cy ) {
					CDialogEx::OnSize( nType, cx, cy );
					if( m_rcData.m_myStatBar.m_hWnd != NULL ) {
						m_rcData.m_myStatBar.SendMessage( WM_SIZE, nType,
						MAKELPARAM( cx, cy )
						);
					}
				}
				

Controllo del collegamento

Inizializzazione del collegamento

Immettendo l’indirizzo IP del Web-IO nel campo di testo IDC_IPTEXT e alla porta 42280 nel campo di testo IDC_PORTTEXT attivando il pulsante IDC_CONNECT può essere stabilita una connessione. Inoltre, in funzione della configurazione, in IDC_PASSWORDTEXT è possibile inserire la password. Di default non è registrata alcuna password. Nel caso si stabilisse la connessione, attraverso ChangeAccess() tutti i pulsanti importanti, le checkbox e i campi di testo vengono attivati. In ogni caso l’utente viene aggiornato attraverso la barra di stato.

1. IPtext


					void CWebIODlg::OnEnChangeIptext() {
						GetDlgItemText( IDC_IPTEXT, m_rcData.m_stIPNum );
						struct sockaddr_in soaddr;
						m_iResult = inet_pton( AF_INET, m_rcData.m_stIPNum, &(
						soaddr.sin_addr )
						);
						if( m_rcData.m_stIPNum == "" ) {
							m_rcData.m_myStatBar.SetText(
							"Please enter valid adressdata!", 0, 0
							);
							GetDlgItem( IDC_CONNECT )->EnableWindow( FALSE );
						}
						else if( m_iResult !=1 ) {
							m_rcData.m_myStatBar.SetText(
							"Please enter valid adressdata!", 0, 0
							);
							GetDlgItem( IDC_CONNECT )->EnableWindow( FALSE );
						}
						else {
							m_rcData.m_myStatBar.SetText( "IPv4 adress is valid.", 0, 0 );
							GetDlgItem( IDC_CONNECT )->EnableWindow( TRUE );
						}
					}
					

2. Porttext


					void CWebIODlg::OnEnChangePorttext() {
						int iPortBuffer;
						GetDlgItemText( IDC_PORTTEXT, m_stBuffer );
						iPortBuffer = atoi( m_stBuffer );
						if( iPortBuffer > 0xFFFF ) {
							m_rcData.m_myStatBar.SetText( "Please choose another port below 65535!", 0, 0 );
							GetDlgItem( IDC_CONNECT )->EnableWindow( FALSE );
						}
						else if( iPortBuffer <= 0 ) {
							m_rcData.m_myStatBar.SetText( "Please choose another port above 0!", 0, 0 );
							GetDlgItem( IDC_CONNECT )->EnableWindow( FALSE );
						}
						else {
							m_rcData.m_myStatBar.SetText( "Valid Port!", 0, 0 );
							m_rcData.m_iPortText = iPortBuffer;
							GetDlgItem( IDC_CONNECT )->EnableWindow( TRUE );
						}
					}
					
Creazione del collegamento

Per stabilire una connessione chiamiamo CreateSocket() e ConnectSocket() e utilizziamo a tal scopo i valori precedentemente verificati dai campi di testo IDC_IPTEXT e IDC_PORTTEXT oltre all’oggetto m_ccConnect che garantisce l’accesso alle funzioni membro della classe Connection.
Le funzioni membro Create() e Connect() sono derivate da CSocket.


					BOOL Connection::CreateSocket( UINT uiPrefPort ) {
						m_iResult = Create( uiPrefPort, SOCK_STREAM, NULL );
						if( m_iResult == 0 ) {
							m_iResult = GetLastError();
							m_bResult = FALSE;
							return m_bResult;
						}
						else {
							m_bResult = TRUE;
							return m_bResult;
						}
					}

					BOOL Connection::ConnectSocket( LPCTSTR lpPrefIp, UINT uiPrefPort ) {
						m_iResult = Connect( lpPrefIp, uiPrefPort );
						if( m_iResult == 0 ) {
							m_iResult = GetLastError();
							m_bResult = FALSE;
							return m_bResult;
						}
						else {
							m_bResult = TRUE;
							return m_bResult;
						}
					}
					
Collegamento realizzato

Se è stata stabilita una connessione, l’applicazione è in grado di iniziare lo scambio dati con il Web-IO. A tal scopo è stata opportunamente attivata l’interfaccia utente.
Attraverso la barra di stato l’utente riceve una notifica sull’attuale stato di connessione.


					void CWebIODlg::OnBnClickedConnect() {
						LPCTSTR lpPrefIP = m_rcData.m_stIPNum;
						UINT uiPrefPort = m_rcData.m_iPortText;
						CWaitCursor m_cWaitCursor;

						if( m_ccConnect.CreateSocket( uiPrefPort ) ) {
							if( m_ccConnect.ConnectSocket( lpPrefIP, uiPrefPort ) ) {
								ChangeAccess( FALSE );
								m_rcData.m_stStatBar.Format(
									_T( "Connected to %s on %i. " ),
									( PCSTR ) m_rcData.m_stIPNum, m_rcData.m_iPortText
								);
								m_rcData.m_myStatBar.SetText( m_rcData.m_stStatBar, 0, 0 );
								m_ccConnect.CancelBlockingCall();
								m_ccConnect.AsyncSelect( FD_READ | FD_CLOSE );
								ValidateDataAtStart();
							}
							else {
								m_stError.Format( "Connection failed, error %d",
								( PCTSTR ) m_ccConnect.m_iResult
								);
								m_rcData.m_myStatBar.SetText( m_stError, 0, 0 );
								m_ccConnect.Close();
							}
						}
						else {
							m_stError.Format( "Socket creation failed, error %d",
							( PCTSTR ) m_ccConnect.m_iResult
							);
							m_rcData.m_myStatBar.SetText( m_stError, 0, 0 );
						}
					}
					
Disinserzione del collegamento

Il collegamento rimane fino a quando non viene autonomamente terminato dall’utente facendo clic sul pulsante Disconnect oppure dal Web-IO. Qui CSocket utilizza la funzione membro derivata Close(). Interrompendo la connessione viene inviata una notifica attraverso la barra di stato all’utente.


					void CWebIODlg::OnBnClickedDisconnect() {
						m_ccConnect.Close();
								m_rcData.m_myStatBar.SetText(
								"Disconnected successfully.", 0, 0
								);
						ChangeAccess( TRUE );
					}
					

Se il Web-IO annulla la connessione, ciò viene registrato dal descrittore di file "CLOSE" e viene applicata la funzione Callback OnClose(). Questa funzione utilizza un messaggio Windows per bloccare nuovamente l’interfaccia utente e per metterne a conoscenza l’utente per mezzo di un MessageBox.


					void Connection::OnClose( int nErrorCode ) {
						AfxMessageBox(
							( "Your Partner just closed the connection. There could be an password"
								" or timeout issue.", MB_ICONINFORMATION )
						);

						if( !( m_iResult = PostMessageA(
							m_rcData.m_hMainWindow, WM_CONNECTION_BROKE_UP, NULL, NULL ) ) ) {
							m_iResult = GetLastError();
							m_stError.Format( "OnClose PostMessage failed, error %d", m_iResult );
							m_rcData.m_myStatBar.SetText( m_stError, 0, 0 );
						}
					}
					

Viene peraltro eseguito lo stesso codice come se avessimo cliccato sul pulsante Disconnect.
In seguito l’applicazione ritorna al livello precedente all’interruzione della connessione ed è possibile stabilire una nuova connessione.


					LRESULT CWebIODlg::ConnectionBrokeUp( WPARAM wParam, LPARAM lParam ) {
						OnBnClickedDisconnect();
						return 1;
					}
					

Utilizzo e comunicazione della parte client

Impostazione degli output

Per attivare gli output sul Web-IO, è necessario fare clic nella rispettiva checkbox che lancia la rispettiva azione. Nel seguente esempio vediamo come attivare le funzioni membro attorno a IDC_OUTPUT0 e/o IDC_OUTPUT1. Dopo aver cliccato nella checkbox, le variabili di controllo vengono adattate opportunamente al case e la stringa m_stLookingFor viene manipolata, perché sarà necessaria in un secondo momento per eseguire determinate operazioni.
Di seguito le variabili di controllo:

  • m_bAbleToRecvDataFromExtern
  • m_bIsThisMentForInput
  • m_stLookingFor
  • m_bCounterNeedUpdateInGUI
Queste influenzano in modo determinante lo svolgimento dell’applicazione e consentono l’elaborazione asincrona di input nonché la decisione affidabile di quale operazione sia quella corretta.


					void CWebIODlg::OnBnClickedOutput0() {

						m_rcData.m_bAbleToRecvDataFromExtern = FALSE;
						m_rcData.m_bIsThisMentForInput = FALSE;
						m_rcData.m_stLookingFor = "output";

						m_rcData.m_bButtonIsChecked = IsDlgButtonChecked( IDC_OUTPUT0 ) ? TRUE : FALSE;

						m_ccConnect.DefineSetOutputRequest( "0" );

						if( m_ccConnect.SendDataViaSocket() == FALSE ) {
							SendGotError();
						}
					}

					void CWebIODlg::OnBnClickedOutput1() {
						m_rcData.m_bAbleToRecvDataFromExtern = FALSE;
						m_rcData.m_stLookingFor = "output";

						m_rcData.m_bButtonIsChecked = IsDlgButtonChecked( IDC_OUTPUT1 ) ? TRUE : FALSE;

						m_ccConnect.DefineSetOutputRequest( "1" );

						if( m_ccConnect.SendDataViaSocket() == FALSE ) {
							SendGotError();
						}

					}
					

Nella prossima fase si verifica se la checkbox è già stata inserita o no. Infine è possibile formulare adeguatamente il comando GET con l’aiuto di DefineSetOutputRequest(). In questo modo il comando è preparato e può essere inviato tramite SendDataViaSocket().


					void ProcessingInputOutput::DefineSetOutputRequest( CString stIDNumber ) {

						if( m_rcData.m_bButtonIsChecked == FALSE ) {
							m_rcData.m_stDataRequest = "GET /outputaccess" + stIDNumber +
							"?PW=" + m_rcData.m_stPassword + "&State=ON&";
						}
						else {
							m_rcData.m_stDataRequest = "GET /outputaccess" + stIDNumber +
							"?PW=" + m_rcData.m_stPassword + "&State=OFF&";
						}
					}
					

Per rendere l’applicazione utilizzabile per più Web-IO diversi, ogni comando GET unico riceve una funzione propria con una stringa di comando fissa in m_stDataRequest e comandi che dovrebbero controllare o cancellare output, input e counter.

Interrogazione dello stato degli output/input

Oltre al fissaggio specifico degli output, possiamo anche interrogare gli stati degli output e input nel complesso. Facendo clic su IDC_READALL0 vengono letti gli output e IDC_READALL1 gli input. Il comando si differenzia leggermente da quello utilizzato per fissare. Di principio, opportunamente per la rispettiva operazione, viene formata una stringa di comando, le variabili di controllo vengono adeguate e in questo modo viene definita la strada dei dati utili dopo il ricevimento da parte di OnReceive() in un punto successivo.


					void CWebIODlg::OnBnClickedReadall0() {
						m_rcData.m_bAbleToRecvDataFromExtern = FALSE;
						m_rcData.m_bIsThisMentForInput = FALSE;

						m_rcData.m_stLookingFor = "output";

						m_ccConnect.DefineGetAllOutputRequest();
						if( m_ccConnect.SendDataViaSocket() == FALSE ) {
							SendGotError();
						}
					}

					void CWebIODlg::OnBnClickedReadall1() {
						m_rcData.m_bAbleToRecvDataFromExtern = TRUE;
						m_rcData.m_bIsThisMentForInput = TRUE;

						m_ccConnect.DefineGetAllInputRequest();
						if( m_ccConnect.SendDataViaSocket() == FALSE ) {
							SendGotError();
						}
					}

					void ProcessingInputOutput::DefineGetAllOutputRequest() {
						m_rcData.m_stDataRequest = "GET /output?PW="
							+ m_rcData.m_stPassword + "&";
					}

					void ProcessingInputOutput::DefineGetAllInputRequest() {
						m_rcData.m_stDataRequest = "GET /input?PW="
							+ m_rcData.m_stPassword + "&";
					}
					
Interrogazione dei counter

Poiché gli episodi in ingresso vengono annotati soltanto nel Web-IO stesso, facendo salire il numero sul contatore interno, questo deve anche poter essere interrogato. La funzione membro OnBnClickedRead0() o OnBnClickedRead1() invia una richiesta al Web-IO e richiede la situazione attuale del rispettivo contatore. Dalla risposta del Web-IO viene tratto in seguito il conteggio del rispettivo counter e visualizzato nell’applicazione.


					void CWebIODlg::OnBnClickedRead0() {
						m_rcData.m_bAbleToRecvDataFromExtern = FALSE;
						m_rcData.m_stLookingFor = "counter0";

						m_ccConnect.DefineGetCounterRequest( "0" );

						if( m_ccConnect.SendDataViaSocket() == FALSE ) {
							SendGotError();
						}
					}

					void CWebIODlg::OnBnClickedRead1() {
						m_rcData.m_bAbleToRecvDataFromExtern = FALSE;
						m_rcData.m_stLookingFor = "counter1";

						m_ccConnect.DefineGetCounterRequest( "1" );

						if( m_ccConnect.SendDataViaSocket() == FALSE ) {
							SendGotError();
						}
					}
					
Visualizza counter

Per visualizzare in modo veritiero i counter in base ai valori appena richiesti nella nostra applicazione, formuliamo funzioni membro che lo realizzano con l’aiuto di messaggi Windows.
La separazione delle classi fra loro rimane così inalterata. Dopo aver inviato la stringa di comando adatta al Web-IO, riceviamo naturalmente una risposta. Essa comprende gli stati dei contatori, eventualmente con informazioni aggiuntive sul Web-IO stesso, che possono essere configurate individualmente.
Questa risposta viene raccolta dalla funzione OnReceive() descritta sotto e, dopo che si è svolta l’elaborazione, invia un messaggio Windows a UpdateGUI() (ultimo passaggio nell’elaborazione della stringa) -> I dati di utilizzo sono ora salvati in modo sicuro e vengono collegati con l’oggetto m_rcData, per renderlo utilizzabile in UpdateGUI():


					void ProcessingInputOutput::ProcessRequestAfterLookingForCounter0Only(
					int StartingIndex ) {
						m_rcData.m_stCounter0 = m_rcData.m_aShortenedValueOnlyArray[StartingIndex];
						m_rcData.m_bAbleToRecvDataFromExtern = TRUE;
						m_rcData.m_bCounterNeedUpdateInGUI = TRUE;
					}

					void ProcessingInputOutput::ProcessRequestAfterLookingForCounter1Only(
					int StartingIndex ) {
						m_rcData.m_stCounter1 = m_rcData.m_aShortenedValueOnlyArray[StartingIndex];
						m_rcData.m_bAbleToRecvDataFromExtern = TRUE;
						m_rcData.m_bCounterNeedUpdateInGUI = TRUE;
					}


					( Auszug aus Connection::OnReceive )
					...
					if( !( m_iResult = PostMessageA(
						m_rcData.m_hMainWindow, WM_PLEASE_UPDATE_GUI,
						m_rcData.m_bCounterNeedUpdateInGUI, NULL ) ) ) {
						m_iResult = GetLastError();
						m_stError.Format( "OnReceive PostMessage failed, error %d", m_iResult );
						m_rcData.m_myStatBar.SetText( m_stError, 0, 0 );
					}
					

Ricezione del messaggio Windows:


					LRESULT CWebIODlg::UpdateGUI( WPARAM CounterNeedUpdate, LPARAM Lparam ) {
						if( CounterNeedUpdate )
						{
							SetDlgItemText( IDC_COUNTERTEXT0, m_rcData.m_stCounter0 );
							SetDlgItemText( IDC_COUNTERTEXT1, m_rcData.m_stCounter1 );
							m_rcData.m_bCounterNeedUpdateInGUI = FALSE;
							return 1;
						}
						else {
							// Do some ErrorHandling here
							return 0;
						}
					}
					

dopo che UpdateGUI() ha ricevuto il messaggio Windows, verifica la variabile di controllo m_bCounterNeedUpdateInGUI e aggiorna la visualizzazione nella GUI. In seguito la variabile di controllo viene resettata per consentire l’attivazione multipla nel tempo di funzionamento.
Se dovessero essere interrogati più counter in Web-IO più grandi, questi devono essere aggiunti qui fra l’altro.

Azzeramento dei counter

Per riportare a 0 gli stati dei counter raffigurati nell’applicazione, usiamo i comandi Clear. Ciò può essere eseguito per stati di counter specifici singolarmente:


					void CWebIODlg::OnBnClickedClear0() {
						m_rcData.m_bAbleToRecvDataFromExtern = FALSE;
						m_rcData.m_stLookingFor = "clear0";
						m_ccConnect.DefineSetClearRequest( "0" );

						if( m_ccConnect.SendDataViaSocket() == FALSE ) {
							SendGotError();
						}
					}

					void CWebIODlg::OnBnClickedClear1() {
						m_rcData.m_bAbleToRecvDataFromExtern = FALSE;
						m_rcData.m_stLookingFor = "clear1";
						m_ccConnect.DefineSetClearRequest( "1" );

						if( m_ccConnect.SendDataViaSocket() == FALSE ) {
							SendGotError();
						}
					}

					void ProcessingInputOutput::DefineSetClearRequest( CString stIDNumber ) {
						m_rcData.m_stDataRequest = "GET /counterclear" + stIDNumber + "?PW="
							+ m_rcData.m_stPassword + "&";
					}
					

Ma possiamo anche resettare tutti insieme gli stati dei contatori del Web-IO:


					void CWebIODlg::OnBnClickedClearall() {
						m_rcData.m_bAbleToRecvDataFromExtern = FALSE;
						m_rcData.m_stLookingFor = "clear";
						m_ccConnect.DefineClearAllRequest();

						if( m_ccConnect.SendDataViaSocket() == FALSE ) {
							SendGotError();
						}
					}

					void ProcessingInputOutput::DefineClearAllRequest() {
						m_rcData.m_stDataRequest = "GET /counterclear?PW="
							+ m_rcData.m_stPassword + "&";
					}
					

Ricezione dei dati dal Web-IO

analisi e visualizzazione dei dati ricevuti:
  • Tutti i comandi e le richieste al Web-IO vengono confermati con una stringa di risposta. Tali risposte hanno una struttura specifica in base al tipo.
  • Tutte le stringe di risposta terminano con 0 byte.
  • Si distingue tra la risposta di un singolo output o di input e la risposta di tutti gli output e input. Lo stesso vale per gli stati dei contatori e per i comandi per il loro reset.
  • Le richieste che si rivolgono a specifici input/output, vengono resettate con "inputx; ON/OFF" o "outputx; ON/OFF"
  • Le richieste che si rivolgono a tutti gli input/output, vengono resettate con "input; valore visualizzato in esadecimali" o "output; valore visualizzato in esadecimali"
  • Le richieste che si rivolgono a specifici counter, vengono resettate con "counterx; stato contatore decimale"
  • Le richieste che si rivolgono a specifici counter, vengono resettate con "counter; stato contatore decimale 0; stato contatore decimale 1; ..."
  • Le richieste di cancellazione degli stati dei contatori vengono resettate come quelle della richiesta degli stati dei contatori.
    Per stati di contatori particolari:
    "counterx; stato contatore decimale"
    Per tutti gli stati di contatori:
    "counter; stato contatore decimale 0; stato contatore decimale 1; ..."
  • Nell’applicazione per la ricezione di un tale messaggio viene attivata la funzione membro OnReceive(). In questa funzione viene elaborata la risposta del Web-IO o le funzioni di elaborazione vengono definite con l’aiuto di variabili di controllo come già noto.

In preparazione di RemoveDelimiterAndSaveIntoArray() viene agganciato alla stringa di risposta ricevuta dal Web-IO un ";" {punto e virgola} e l’array in cui è salvato viene fissato con "0" zero.

Funzioni corrispondenti a OnReceive()

Le seguenti funzioni membro sono responsabili della corretta elaborazione, dei diversi valori, che ci invia il Web-IO. Questo potrebbe essere fra l’altro la condivisione e lettura indifferente delle stringhe così come l’operatore logico di congiunzione AND dei dati di utilizzo esadecimali formattati.


					void Connection::OnReceive( int nErrorCode ) {
						int const default_buflen{ 512 };
						int recvbuflen{ default_buflen };
						char recvbuf[default_buflen];

						memset( recvbuf, 0, sizeof( recvbuf ) );
						m_rcData.m_stRecvbuf.Empty();
						m_iResult = Receive( recvbuf, recvbuflen, 0 );

						if( m_iResult == SOCKET_ERROR ) {
							m_iResult = GetLastError();
							m_stError.Format( "OnReceive failed, error %d", m_iResult );
							m_rcData.m_myStatBar.SetText( m_stError, 0, 0 );
						}
						else if( m_iResult > 0 ) {
							recvbuf[m_iResult] = ’\0’;
							strcat_s( recvbuf, ";" );
							m_rcData.m_stRecvbuf = recvbuf;
							RemoveDelimiterAndSaveIntoArray( ";" );
							ExtractValuesAndShortenArray();
							ChooseRightOperation();
							if( !( m_iResult = PostMessageA(
								m_rcData.m_hMainWindow, WM_PLEASE_UPDATE_GUI,
								m_rcData.m_bCounterNeedUpdateInGUI, NULL ) ) ) {
								m_iResult = GetLastError();
								m_stError.Format( "OnReceive PostMessage failed, error %d", m_iResult );
								m_rcData.m_myStatBar.SetText( m_stError, 0, 0 );
							}
						}
					}
					

Per poter iterare meglio più tardi attraverso la stringa di risposta, questa funzione membro di ";" {punto e virgola} segno di divisione in segno di divisione, copia quella parte e salva le stringhe in un array in m_aAnswerStringArray.


					void ProcessingInputOutput::RemoveDelimiterAndSaveIntoArray( CString stDel ) {
						int iStartPos = 0;
						int iEndPos = m_rcData.m_stRecvbuf.Find( stDel );
						int i = 0;
						CString stCurrentStringPiece;

						for( int iStringLength = 0; iStringLength < m_rcData.m_stRecvbuf.GetLength();
							iStringLength += stCurrentStringPiece.GetLength() + 1 ) {
							stCurrentStringPiece = m_rcData.m_stRecvbuf.Mid( iStartPos, iEndPos - iStartPos );
							m_aAnswerStringArray.Add( stCurrentStringPiece );
							iStartPos = iEndPos + strlen( stDel );
							iEndPos = m_rcData.m_stRecvbuf.Find( stDel, iStartPos );
							i += 1;
						}
					}
					

Poiché adesso in m_aAnswerStringArray possono trovarsi valori molto diversi in posizioni diverse, a seconda che il Web-IO sia configurato in modo da inviare il suo nome e indirizzo IP, (indipendentemente dai dati utili), dobbiamo filtrare i dati utili importanti per noi.
Poiché il formato in cui vengono inviati i dati non cambia, possiamo stabilire per noi la posizione dei dati per noi interessanti sulla base delle keyword precedenti come p. es.: "counter". Alla fine di questa funzione membro abbiamo m_aShortenedValueOnlyArray riempito esclusivamente con dati utili come [0001, 0002,4,1]


					void ProcessingInputOutput::ExtractValuesAndShortenArray() {
						for( int i = 0; i < m_aAnswerStringArray.GetSize();) {
							if( m_aAnswerStringArray[i] == "input" ||
								m_aAnswerStringArray[i] == "output" ) {
								m_rcData.m_aShortenedValueOnlyArray.Add(
								m_aAnswerStringArray[i + 1]
								);
								m_aAnswerStringArray.RemoveAt( i );
							}
							else if( m_aAnswerStringArray[i] == "counter" ) {
								m_rcData.m_aShortenedValueOnlyArray.Add(
								m_aAnswerStringArray[i + 1]
								);
								m_rcData.m_aShortenedValueOnlyArray.Add(
								m_aAnswerStringArray[i + 2]
								);
								m_aAnswerStringArray.RemoveAt( i );
							}
							else if( m_aAnswerStringArray[i] == "counter0" ) {
								m_rcData.m_aShortenedValueOnlyArray.Add(
								m_aAnswerStringArray[i + 1]
								);
								m_aAnswerStringArray.RemoveAt( i );
							}
							else if( m_aAnswerStringArray[i] == "counter1" ) {
								m_rcData.m_aShortenedValueOnlyArray.Add(
								m_aAnswerStringArray[i + 1]
								);
								m_aAnswerStringArray.RemoveAt( i );
							}
							else {
								m_aAnswerStringArray.RemoveAt( i );
							}
						}
					}
					

In funzione delle variabili di controllo precedentemente manipolate vengono ora applicate le operazioni finali al rispettivo caso e vengono collegati i valori del m_aShortenedValueOnlyArray con l’oggetto m_rcData.


					void ProcessingInputOutput::ChooseRightOperation() {
						if( m_rcData.m_bAbleToRecvDataFromExtern ) {
							ProcessRequestAfterLookingForAllInputs( 0 );
							m_rcData.m_aShortenedValueOnlyArray.RemoveAll();
							return;
						}

						if( m_rcData.m_stLookingFor == "counter" ||
						m_rcData.m_stLookingFor == "clear" ) {
							ProcessRequestAfterLookingForAllCounter( 0 );
							m_rcData.m_aShortenedValueOnlyArray.RemoveAll();
						}
						else if( m_rcData.m_stLookingFor == "counter0" ||
						m_rcData.m_stLookingFor == "clear0" ) {
							ProcessRequestAfterLookingForCounter0Only( 0 );
							m_rcData.m_aShortenedValueOnlyArray.RemoveAll();
						}
						else if( m_rcData.m_stLookingFor == "counter1" ||
						m_rcData.m_stLookingFor == "clear1" ) {
							ProcessRequestAfterLookingForCounter1Only( 0 );
							m_rcData.m_aShortenedValueOnlyArray.RemoveAll();
						}
						else if( m_rcData.m_stLookingFor == "allout" ) {
							ProcessRequestAfterLookingForAllInputs( 0 );
							ProcessRequestAfterLookingForAllOutputs( 1 );
							ProcessRequestAfterLookingForAllCounter( 2 );
							m_rcData.m_aShortenedValueOnlyArray.RemoveAll();
						}
						else if( m_rcData.m_stLookingFor == "output" ) {
							ProcessRequestAfterLookingForAllOutputs( 0 );
							m_rcData.m_aShortenedValueOnlyArray.RemoveAll();
						}
						else {
							// Do some ErrorHandling here
						}
					}
					

Dopo che è stata definita l’operazione giusta, i dati utili vengono ora collegati con m_rcData. Infine le variabili di controllo vengono resettate ai valori standard. In caso di elaborazione degli input e output deve avvenire ancora la congiunzione attraverso l’operatore logico AND.


					void ProcessingInputOutput::ProcessRequestAfterLookingForCounter0Only( int StartingIndex ) {
						m_rcData.m_stCounter0 = m_rcData.m_aShortenedValueOnlyArray[StartingIndex];
						m_rcData.m_bAbleToRecvDataFromExtern = TRUE;
						m_rcData.m_bCounterNeedUpdateInGUI = TRUE;
					}

					void ProcessingInputOutput::ProcessRequestAfterLookingForCounter1Only( int StartingIndex ) {
						m_rcData.m_stCounter1 = m_rcData.m_aShortenedValueOnlyArray[StartingIndex];
						m_rcData.m_bAbleToRecvDataFromExtern = TRUE;
						m_rcData.m_bCounterNeedUpdateInGUI = TRUE;
					}

					void ProcessingInputOutput::ProcessRequestAfterLookingForAllCounter( int StartingIndex ) {
						m_rcData.m_stCounter0 = m_rcData.m_aShortenedValueOnlyArray[StartingIndex];
						StartingIndex += 1;
						m_rcData.m_stCounter1 = m_rcData.m_aShortenedValueOnlyArray[StartingIndex];
						m_rcData.m_bAbleToRecvDataFromExtern = TRUE;
						m_rcData.m_bCounterNeedUpdateInGUI = TRUE;
					}

					void ProcessingInputOutput::ProcessRequestAfterLookingForAllInputs( int StartingIndex ) {
						m_stRawInputOutput = m_rcData.m_aShortenedValueOnlyArray[StartingIndex];
						m_rcData.m_bIsThisMentForInput = TRUE;
						BinaryANDing( m_stRawInputOutput );
					}

					void ProcessingInputOutput::ProcessRequestAfterLookingForAllOutputs( int StartingIndex ) {
						m_stRawInputOutput = m_rcData.m_aShortenedValueOnlyArray[StartingIndex];
						m_rcData.m_bIsThisMentForInput = FALSE;
						BinaryANDing( m_stRawInputOutput );
					}
					

Affinché dal valore formattato esadecimale si ottenga uno stato per noi direttamente comprensibile di un determinato input/output, il valore rappresentato alla potenza di 2 deve essere collegato tramite l’operatore logico AND
Il risultato è per ogni punto o un valore di 0 o di != 0 (risultato <0 -> fissare il risultato su 1)
Di base 0= OFF, 1= ON.

Per trasmettere in modo sicuro l’informazione ed eventualmente poter controllare le checkbox dietro IDC_INPUT0, IDC_INPUT1, IDC_OUTPUT0, IDC_OUTPUT1 in funzione del loro stato, i 4 valori p. es.: [0,1,1,0] vengono salvati come array di una struttura di dati realizzata autonomamente m_IODataStruct.aIOValues.


					void ProcessingInputOutput::BinaryANDing( CString m_stRawInputOutput ) {
						int iResult = 0;
						int iInOutAsDez = atoi( m_stRawInputOutput );
						int iCurrentInput = 0;
						unsigned int uiPotenz = 0;

						for( int i = 0; i < m_rcData.m_iHowMuchInputsDoWeHave; i++ )
						{
							uiPotenz = 1 << i; // 2^0;2^1,2^2,2^3
							int iBitStatus = iInOutAsDez & uiPotenz;
							if( iBitStatus > 0 ) {
								iBitStatus = 1;
							}
							if( m_rcData.m_bIsThisMentForInput ) {
								m_IODataStruct.aIOValues[i] = iBitStatus;
							}
							else {
								m_IODataStruct.aIOValues[i + 2] = iBitStatus;
							}

							iCurrentInput += 1;
						}
						if( !( iResult = PostMessageA(
							m_rcData.m_hMainWindow, WM_SEND_INPUT_OUTPUT_STATUS, NULL, NULL ) ) ) {
							iResult = GetLastError();
							m_stError.Format( "BinaryANDing PostMessage failed, error %d", iResult );
							m_rcData.m_myStatBar.SetText( m_stError, 0, 0 );
						}
					}
					

Abbiamo complessivamente 4 valori (2x output, 2x input), per questo ci occupiamo dei valori in sequenza cronologica dei loro elementi di comando e modifichiamo lo stato da checked ad unchecked o viceversa. Ciò significa che nell’utilizzo gli input e output vengono rappresentati attraverso la checkbox.


					LRESULT CWebIODlg::ProcessCheckBoxStatus( WPARAM wParam, LPARAM lParam ) {
						CheckDlgButton( IDC_INPUT0,
							( m_ccConnect.m_IODataStruct.aIOValues[0] == 0 ) ? BST_UNCHECKED : BST_CHECKED
						);
						CheckDlgButton( IDC_INPUT1,
							( m_ccConnect.m_IODataStruct.aIOValues[1] == 0 ) ? BST_UNCHECKED : BST_CHECKED
						);

						CheckDlgButton( IDC_OUTPUT0,
							( m_ccConnect.m_IODataStruct.aIOValues[2] == 0 ) ? BST_UNCHECKED : BST_CHECKED
						);
						CheckDlgButton( IDC_OUTPUT1,
							( m_ccConnect.m_IODataStruct.aIOValues[3] == 0 ) ? BST_UNCHECKED : BST_CHECKED
						);

						m_rcData.m_bAbleToRecvDataFromExtern = TRUE;
						m_rcData.m_bIsThisMentForInput = TRUE;

						return 1;
					}
					

Affinché possiamo essere sicuri che anche nonostante l’applicazione chiusa al prossimo avvio tutti i valori rispecchino lo stato corretto, invochiamo in OnBnClickedConnect() dopo che abbiamo stabilito con successo un collegamento ValidateDataAtStart(). La funzione membro fa si che inviamo inizialmente un comando allout /GET al Web-IO e chiediamo tutte le informazioni e aggiorniamo di conseguenza la GUI. Questa interrogazione si svolge dopo il collegamento con il Web-IO.


					void CWebIODlg::ValidateDataAtStart() {
						OnEnChangePollingtext();

						m_rcData.m_bAbleToRecvDataFromExtern = FALSE;
						m_rcData.m_stLookingFor = "allout";

						m_ccConnect.DefineGetAllOutRequest();
						if( m_ccConnect.SendDataViaSocket() == FALSE ) {
							SendGotError();
						}
					}

					void ProcessingInputOutput::DefineGetAllOutRequest() {
						m_rcData.m_stDataRequest = "GET /allout?PW="
							+ m_rcData.m_stPassword + "&";
					}
					

Polling

Interrogazione ciclica di determinati valori

Per non dover aggiornare manualmente i componenti durante l’uso dell’applicazione, si può utilizzare il procedimento di polling. Se si inserisce un valore intero valido nel ... IDC_POLLINGTEXT si attivano le 3 checkbox importanti per la procedura di polling: IDC_POLLING0, IDC_POLLING1 & IDC_POLLINGCOUNTER. Facendo un segno di spunta è possibile avviare il rispettivo processo di polling all’altezza dell’intero inserito (in ms). I singoli processi di polling sono la lettura automatica di output, input o del counter.

Per ogni variante di polling viene inizializzato un proprio timer. A tal scopo viene utilizzata un’ID timer assegnata direttamente. Se il segno di spunta della checkbox viene di nuovo rimosso, anche il timer viene distrutto.

Rilevamento dell’inserimento utente:


					void CWebIODlg::OnEnChangePollingtext() {
						GetDlgItemText( IDC_POLLINGTEXT, m_stBuffer );
						m_rcData.m_iPolling = atoi( m_stBuffer );
						if( m_rcData.m_iPolling <= 0 ) {
							m_rcData.m_myStatBar.SetText(
								"Please enter a valid intervall for polling!", 0, 0
							);
							GetDlgItem( IDC_POLLING0 )->EnableWindow( FALSE );
							GetDlgItem( IDC_POLLING1 )->EnableWindow( FALSE );
							GetDlgItem( IDC_POLLINGCOUNTER )->EnableWindow( FALSE );

						}
						else if( m_rcData.m_iPolling >= 1 ) {
							m_rcData.m_stStatBar.Format(
								_T( "Range changed to %i ms!" ), m_rcData.m_iPolling
							);
							m_rcData.m_myStatBar.SetText( m_rcData.m_stStatBar, 0, 0 );
							GetDlgItem( IDC_POLLING0 )->EnableWindow( TRUE );
							GetDlgItem( IDC_POLLING1 )->EnableWindow( TRUE );
							GetDlgItem( IDC_POLLINGCOUNTER )->EnableWindow( TRUE );
						}
					}
					

timer per gli output:


					void CWebIODlg::OnBnClickedPolling0() {

						IsDlgButtonChecked( IDC_POLLING0 ) ? SetTimer( 1, m_rcData.m_iPolling, NULL ) :
							KillTimer( 1 );
					}
					

timer per gli input:


					void CWebIODlg::OnBnClickedPolling1() {

						IsDlgButtonChecked( IDC_POLLING1 ) ? SetTimer( 2, m_rcData.m_iPolling, NULL ) :
							KillTimer( 2 );
					}
					

Timer per i counter:


					void CWebIODlg::OnBnClickedPollingcounter() {

						IsDlgButtonChecked( IDC_POLLINGCOUNTER ) ? SetTimer( 3, m_rcData.m_iPolling, NULL ) :
							KillTimer( 3 );
					}
					

Qui viene utilizzata la funzione CDialogEx sovraccarica da OnTimer(). Viene catturato il rispettivo evento del timer, che si attiva, e assegnato a un’azione univoca.


					void CWebIODlg::OnTimer( UINT nIDEvent ) {
						if( nIDEvent == 1 ) OnBnClickedReadall0();
						if( nIDEvent == 2 ) OnBnClickedReadall1();
						if( nIDEvent == 3 ) OnBnClickedReadall();
						CDialogEx::OnTimer( nIDEvent );
					}
					

L’intero programma viene messo a disposizione su questa pagina, anche se contiene alcuni file e funzioni che non sono stati citati qui. L’applicazione MFC in ambiente Visual C++ produce un overhead che crea la struttura grafica, ma che non è collegato direttamente alla funzionalità. Le funzioni e variabili che vi sono state presentate in questa pagina, vengono implementate nel quadro di Visual C++ e funzionano quindi come un’unità con l’interfaccia grafica.


Il programma esempio supporta tutte le comuni funzioni del Web-IO nella modalità stringa di comando, ottimizzata per il Web-IO 2x Digital Input, 2x Digital Output PoE. Gli altri modelli Web-IO devono eventualmente essere adattati al programma. Ulteriori esempi di programma per la programmazione socket sono riportati nelle pagine dei tool per il Web-IO. Una descrizione dettagliata sull’interfaccia socket dei modelli Web-IO digitali è riportata nel manuale di riferimento.

Download esempio di programma


Prodotti