Tutorial zum Web-IO Digital;
Web-IO Digital mit Visual C++ steuern und überwachen
Für das Erstellen von Windows-Anwendungen ist Visual C++ eine der meistgenutzten Entwicklungsplattformen.
Mit dem folgenden C++ Programmbeispiel können Sie Ihr Web-IO Digital mit seinen Inputs und Outputs in einer Windows-Anwendung abbilden. Darüber hinaus können Sie die Outputs des Web-IO schalten.
Vorbereitungen
- Web-IO mit Spannung versorgen und IOs verdrahten
- Web-IO mit dem Netzwerk verbinden
- IP-Adressen vergeben
Beim Web-IO im Bereich Kommunikationswege >> Socket-API die TCP-ASCII-Sockets aktivieren und die Outputs zum Schalten freigeben
Zusammenstellen der verschiedenen Bedienelemente und Anzeigeobjekte im Visual-C++-Form
Definition in den Header-Dateien
Definition MyDlgData.h
Um eine Mehrfach-Definition zu vermeiden und die Membervariablen an das m_rcData
-Objekt koppeln zu können,
welches durch die Klassen gereicht wird, definieren wir in MyDlgData.h
einige Membervariablen. Zusätzlich ist hier auch das von CStatusBarCtrl
abgeleitete
Objekt m_myStatBar
definiert.
Um Zugriff auf weitreichende Memberfunktionen in Verbindung mit dem Datenobjekt zu ermöglichen, erbt diese Klasse von CObject
.
Da diese Klasse hauptsächlich als Schnittstelle dient, werden hier keinerlei Memberfunktionen definiert.
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;
};
Definition von Connection.h
Die Aufgabe dieser Klasse ist die zuverlässige Erstellung einer TCP-Verbindung über eine Socket-Schnittstelle. Zusätzlich beeinhaltet sie Memberfunktionen zum Senden und Empfangen von Daten. Nachfolgend stellen wir die vorab definierten Membervariablen sowie Memberfunktionen aus:
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 );
};
Diese Klasse erbt von ProcessingInputOutput
, um die Funktionalitäten der später verarbeitenden Memberfunktionen nutzen zu können.
Darüber hinaus erbt sie von CSocket
, da die Klasse die Grundlage der Verbindung über Socket-Schnittstellen via UDP oder TCP bildet.
Die Verbindung findet asynchron statt, also erfolgt der Datenaustausch ohne vorab definierten Takt und die Prozesse werden nebenläufig verarbeitet.
Deswegen machen wir uns Callback-Funktionen zu nutze, die ausgelöst werden,
wenn ein Receive-Event (Daten können empfangen werden) oder ein Close-Event (Die Verbindung wurde getrennt) gemeldet wird.
Der jeweilige Trigger entscheidet dann über den Programmablauf.
Damit die vom Web-IO erhaltenen Daten korrekt verarbeitet werden, bedarf es einer eigenen Klasse, derer Definition wir hier nachfolgend beschreiben.
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;
};
Diese Klasse ist ausschließlich für das Verarbeiten der vom Datenobjekt übergebenen Daten zuständig.
Dazu nutzen wir die Verknüpfung der Klasse mit dem m_rcData
-Objekt.
Neben den Memberfunktionen, welche die Antwort-Strings spalten, kopieren, speichern oder durch sie durch iterieren um Operationen anzuwenden, erstellen wir
unter MyIODataStruct
eine Struktur, die es uns ermöglicht eine größere Menge Nutzdaten komfortabel zu verwalten.
Um die grafische Darstellung des Dialogfensters sowie dessen Inhalts kümmert sich CWebIODlg
.
Diese Klasse bedient sich unter anderem von CDialogEx
und wird aus MyDlgData
mit m_rcData
verknüpft und definiert selbst das Objekt m_ccConnect
aus der Connection
-Klasse.
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{};
};
Hier befinden sich hauptsächlich Memberfunktionen, die als Callback aufgerufen werden, sollte ein Button geklickt, ein Textfeld verändert,
oder etwas erstellt werden wie das Dialogfenster selbst. Dazu werden einige Memberfunktionen aus CWebDialogEx
überladen.
Programmstart
Einrichten der BedienelementeDie Bedienelemente selbst können relativ einfach durch den Klassen-Assistenten, der in VisualStudio integriert ist, erstellt werden.
Durch ihn lassen sich ebenfalls mühelos z.B.: Buttons mit Memberfunktionen oder Kontrollvariablen verknüpfen.
Um unsachgemäße Benutzung durch den Nutzer vorzubeugen, werden einige Bedienelemente nach bestimmten Funktionsaufrufen und am Start partiell eingeschränkt.
Dies ist unter ChangeAccess()
definiert.
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 );
}
Um die Statusleiste zu initialisieren und die Bedienelemente passend für den Nutzer vorbereiten können, verwenden wir die überladene OnInitDialog()
-Funktion
aus CDialogEx
. Die dort enthaltenen Operationen werden einmal pro Start aufgerufen.
Zusätzlich nutzen wir die Memberfunktion, um für andere Memberfunktionen den Handle auf das Main-Window an das Datenobjekt m_rcData
zu binden.
BOOL CWebIODlg::OnInitDialog() {
CDialogEx::OnInitDialog();
InitializeStatusBar();
m_rcData.m_hMainWindow = m_hWnd;
ChangeAccess( TRUE );
return TRUE;
}
Nachfolgend die Erstellung der Statusleiste.
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 );
}
Damit sich die Statusleiste auch nach dem Verändern der Fenstergröße anpasst, wurde hier ebenfalls
die OnSize()
-Funktion überladen und meldet die Veränderung an die Statusleiste.
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 )
);
}
}
Die Verbindungskontrolle
Einleiten der Verbindung
Durch Eingabe der IP-Adresse des Web-IO in das Textfeld IDC_IPTEXT
und dem Port 42280
in das Textfeld IDC_PORTTEXT
kann durch Betätigung des Buttons IDC_CONNECT
eine Verbindung aufgebaut werden.
Zusätzlich kann, je nach Konfiguration, unter IDC_PASSWORDTEXT
das Passwort eingetragen werden.
Standardmäßig ist kein Passwort hinterlegt.
Sollte die Verbindung erfolgreich erstellt worden sein, werden durch ChangeAccess()
alle wichtigen Buttons,
Checkboxen und Textfelder freigeschaltet.
In jedem Fall wird der Nutzer über die Statusleiste auf den aktuellen Stand gebracht.
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 );
}
}
Verbindungsaufbau
Um eine Verbindung herzustellen callen wir CreateSocket()
sowie ConnectSocket()
und
verwenden dazu die vorab geprüften Werte aus den Textfeldern IDC_IPTEXT
und IDC_PORTTEXT
plus das m_ccConnect
-Objekt
welches den Zugriff auf Memberfunktionen der Connection
-Klasse bereitstellt.
Die Memberfunktionen Create()
und Connect()
sind aus CSocket
abgeleitet.
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;
}
}
Verbindung kommt zustande
Ist die Verbindung zustande gekommen, ist die Anwendung in der Lage mit dem Web-IO den Datenaustausch zu beginnen.
Dafür wird die Benutzeroberfläche dementsprechend freigegeben.
Der Nutzer bekommt über die Statusleiste eine Meldung über die aktuellen Verbindungsstatus.
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 );
}
}
Trennen der Verbindung
Die Verbindung bleibt solange bestehen, bis sie vom Nutzer durch Klick auf den Disconnect-Button oder durch das Web-IO selbstständig beendet wird.
Hier wird die von CSocket
abgeleitete Memberfunktion Close()
verwendet.
Beim Trennen der Verbindung wird der Nutzer ebenfalls durch die Statusleiste benachrichtigt.
void CWebIODlg::OnBnClickedDisconnect() {
m_ccConnect.Close();
m_rcData.m_myStatBar.SetText(
"Disconnected successfully.", 0, 0
);
ChangeAccess( TRUE );
}
Sollte das Web-IO die Verbindung auflösen, wird dies über den Filedeskriptor "CLOSE" erfasst und die
Callback-Funktion OnClose()
wird aufgerufen.
Diese nutzt eine Windows-Message, um die Benutzeroberfläche dementsprechend wieder zu sperren und den Nutzer durch
eine MessageBox darüber in Kenntnis zu setzen.
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 );
}
}
Es wird unter anderem derselbe Code ausgeführt, als hätten wir auf den Disconnect
-Button geklickt.
Anschließend ist die Anwendung wieder auf dem Stand, wie vor dem Verbindungsabbruch und es kann eine neue Verbindung hergestellt werden.
LRESULT CWebIODlg::ConnectionBrokeUp( WPARAM wParam, LPARAM lParam ) {
OnBnClickedDisconnect();
return 1;
}
Bedienung und Kommunikation von Client-Seite
Setzen der Outputs
Um die Outputs am Web-IO zu schalten, muss ein Click in die jeweilige Checkbox gemacht werden, welche dann die bestimmte Aktion auslöst. Im folgenden Beispiel sehen wir die Memberfunktionen um IDC_OUTPUT0
und/oder IDC_OUTPUT1
zu setzen.
Nach dem Click in die Checkbox, werden Kontroll-Variablen passend zu dem Case angepasst und der m_stLookingFor
-String manipuliert, denn dieser wird zu späterem Zeitpunkt benötigt, um bestimmte Operationen durchzuführen.
Folgend die Kontroll-Variablen:
m_bAbleToRecvDataFromExtern
m_bIsThisMentForInput
m_stLookingFor
m_bCounterNeedUpdateInGUI
Diese beeinflussen maßgeblich den Ablauf der Anwendung und ermöglichen die asynchrone Verarbeitung von Inputs als auch das zuverlässige Entscheiden welche Operation die Richtige ist.
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();
}
}
Im nächsten Schritt wird geprüft, ob die Checkbox bereits gesetzt worden ist, oder nicht.
Im Anschluss kann der GET-Befehl mithilfe von DefineSetOutputRequest()
passend formuliert werden.
Damit ist der Befehl vorbereitet und kann via SendDataViaSocket()
versendet werden.
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&";
}
}
Um die Anwendung für mehrere verschiedene Web-IOs nutzbar zu machen, erhält jeder einzigartige GET-Befehl eine eigene Funktion mit einem fixen Kommando-String unter m_stDataRequest
und Befehle, die Outputs, Inputs und Counter gezielt ansteuern oder löschen sollen.
Neben dem spezifischen Setzen der Outputs, können wir auch die Zustände der Outputs sowie Inputs in Gänze abfragen.
Der Click auf IDC_READALL0
liest die Outputs und IDC_READALL1
die Inputs aus.
Der Befehl unterscheidet sich leicht zu dem, der zum Setzen verwendet wird.
Grundsätzlich wird passend zur jeweiligen Operation ein Kommando-String geformt, Kontroll-Variablen werden angepasst
und somit der Weg der Nutzdaten nach dem Erhalt durch OnReceive()
zu späterer Stelle bestimmt.
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 + "&";
}
Counter abfragen
Da eintreffende Input-Ereignisse nur im Web-IO selbst vermerkt werden und somit der innere Zähler hochgezählt wird,
sollte dieser auch abgefragt werden können.
Die Memberfunktion OnBnClickedRead0()
oder OnBnClickedRead1()
sendet eine Anfrage an das
Web-IO und fordert den aktuellen Stand des jeweiligen Zählers an.
Aus der Antwort des Web-IO wird später der Zählstand des jeweiligen Counters entnommen und in der
Anwendung angezeigt.
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();
}
}
Counter darstellen
Um die Counter wahrheitsgemäß anhand der soeben abgefragten Werte in unserer Anwendung darzustellen, formulieren wir Memberfunktionen,
die das mithilfe von Windows-Messages erledigen.
Die Trennung der Klassen untereinander bleibt somit erhalten.
Nachdem wir den passenden Kommando-String an das Web-IO gesendet haben, erhalten wir selbstverständlich eine Antwort.
Diese beeinhaltet die Zählerstände, ggf. mit zusätzlichen Informationen zum Web-IO selbst, die individuell konfiguriert werden können.
Diese Antwort wird von der weiter unten beschriebenen OnReceive()
-Funktion gefangen und sendet, nachdem die
Verarbeitung stattgefunden hat, eine Windows-Message an UpdateGUI()
(Letzter Schritt in der Verarbeitung des Strings) -> Die Nutzdaten sind nun zuverlässig gespeichert und werden mit dem m_rcData
-Objekt
verknüpft, um es in UpdateGUI()
nutzbar zu machen:
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 );
}
Empfangen der Windows-Message:
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;
}
}
Nachdem UpdateGUI()
die Windows-Message erhalten hat, prüft sie die Kontroll-Variable m_bCounterNeedUpdateInGUI
und aktualisiert die Darstellung im GUI. Anschließend wird die Kontroll-Variable wieder zurückgesetzt um mehrfaches Auslösen zur Laufzeit zu ermöglichen.
Sollten mehrere Counter bei größeren Web-IOs abgefragt werden, müssen diese dann unter anderem, hier hinzugefügt werden.
Um die in der Anwendung dargestellten Zählerstände wieder auf 0 zurückzusetzen, nutzen wir Clear-Befehle. Dies ist für spezifsche Zählerstände einzeln machbar:
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 + "&";
}
Aber wir können auch in Summe alle Zählerstände des Web-IOs zurücksetzen:
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 + "&";
}
Datenempfang vom Web-IO
Auswerten und Anzeigen der empfangenen Daten:- Alle Kommandos und Anfragen an das Web-IO werden mit einem Antwort-String quittiert. Dabei haben die Antworten je nach Typ einen spezifischen Aufbau.
- Alle Antwort-Strings sind mit einem 0-Byte abgeschlossen.
- Man unterscheidet zwischen der Antwort von einem einzelnen Output bzw. Input und der aller Outputs und Inputs. Gleiches gilt bei Zählerständen sowie bei Befehlen zum Zurücksetzen dieser.
- Anfragen die sich an spezielle Inputs / Outputs richten, werden mit "inputx; ON/OFF" oder "outputx; ON/OFF" quittiert
- Anfragen die sich an an alle Inputs/ Outputs richten, werden mit "input; Hexadezimal dargestellter Wert" oder "output; Hexadezimal dargestellter Wert" quittiert
- Anfragen die sich an spezielle Counter richten, werden mit "counterx; dezimaler Zählerstand" quittiert
- Anfragen die sich an alle Counter richten, werden mit "counter; dezimaler Zählerstand 0; dezimaler Zählerstand 1; ..." quittiert
- Anfragen zum Löschen der Zählerstände werden gleich denen der Anfrage der Zählerstände quittiert.
Bei speziellen Zählerständen:
"counterx; dezimaler Zählerstand"
Bei allen Zählerständen:
"counter; dezimaler Zählerstand 0; dezimaler Zählerstand 1; ..." - In der Anwendung wird zum Empfang einer solchen Nachricht die Memberfunktion
OnReceive()
aufgerufen. In dieser Funktion wird die Antwort des vom Web-IO verarbeitet, bzw. die verarbeitenden Funktionen werden mithilfe von Kontroll-Variablen, wie bereits bekannt, bestimmt.
Vorbereitend für RemoveDelimiterAndSaveIntoArray()
wird dem erhaltenen Antwort-String vom Web-IO ein ";" {semikolon} angehängt
und das Array in dem es gespeichert ist, wird mit "0" Null terminiert.
Mit OnReceive() korrespondierende Funktionen
Die folgenden Memberfunktionen sind für die korrekte Verarbeitung, der verschiedenen Werte, die uns das Web-IO sendet, zuständig. Das könnte unter anderem das stumpfe Teilen und Auslesen der Strings, als auch das logische UND-verknüpfen der hexadezimal formatierten Nutzdaten sein.
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 );
}
}
}
Um später besser durch den Antwort-String iterieren zu können, liest diese Memberfunktion von ";" {semikolon} Trennzeichen zu Trennzeichen,
kopiert jenen Teil und speichert die Strings in einem Array unter 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;
}
}
Da sich jetzt in m_aAnswerStringArray
ganz unterschiedliche Werte an verschiedenen Positionen befinden können, je nach dem
ob das Web-IO so konfiguriert ist den eigenen Namen, sowie IP-Adresse zu senden, (unabhängig der Nutzdaten) müssen wir die für uns wichtigen Nutzdaten herausfiltern.
Da das Format wie die Daten versendet werden sich selbst nicht ändert, können wir die Position der für uns interessanten Daten anhand der vorabstehenden Keywords wie z.B.: "counter", festmachen.
Am Ende dieser Memberfunktion haben wir m_aShortenedValueOnlyArray
mit ausschließlich Nutzdaten befüllt wie z.B.: [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 );
}
}
}
Gemäß den vorab manipulierten Kontroll-Variablen werden nun die letztendlich finalen Operationen auf den jeweiligen Case
angewendet und die Werte aus dem m_aShortenedValueOnlyArray
mit dem m_rcData
-Objekt verknüpft.
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
}
}
Nach dem die richtige Operation bestimmt worden ist, werden die Nutzdaten nun mit m_rcData
verknüpft.
Anschließend werden die Kontrollvariablen auf die Standardwerte zurückgesetzt.
Im Falle der Verarbeitung der Inputs sowie Outputs muss noch die logische UND-Verknüpfung erfolgen.
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 );
}
Damit aus dem hexadezimal formatiertem Wert ein für uns direkt verständlicher Zustand eines bestimmten Inputs / Outputs wird,
muss der dargestellte Wert mit der Potenz von 2 logisch UND-verknüpft werden.
Das Ergebnis ist für jede Stelle entweder ein Wert von 0 oder != 0. (Ergebnis <0 -> Ergebnis auf 1 setzen)
Grundsätzlich gilt 0 = OFF, 1 = ON.
Um die Information zuverlässig zu vermitteln und ggf. die Checkboxen hinter IDC_INPUT0
, IDC_INPUT1
, IDC_OUTPUT0
, IDC_OUTPUT1
entsprechend ihres Status steuern zu können, werden die 4 Werte z.B.: [0,1,1,0] als Array einer eigens erstellten Datenstruktur m_IODataStruct.aIOValues
gespeichert.
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 );
}
}
Wir haben insgesamt 4 Werte (2x Outputs, 2x Inputs), deswegen adressieren wir die Werte in chronologischer Reihenfolge ihrer zugehörigen Bedienelemente und verändern den Status von Checked auf Unchecked oder anders herum. Das heißt in der Anwendung werden die gesetzten Inputs und Outputs durch die Checkbox dargestellt.
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;
}
Damit wir sicher sein können, dass auch trotz geschlossener Anwendung beim nächsten Start alle Werte dem korrekten Status entsprechen,
callen wir in OnBnClickedConnect()
nach dem wir uns erfolgreich verbunden haben ValidateDataAtStart()
. Die Memberfunktion sorgt dafür dass wir
inital einen /GET allout-Befehl an das Web-IO senden und alle Informationen abfragen, sowie dann das GUI entsprechend aktualisieren.
Diese Abfrage findet nur nach dem erfolgreichen Verbinden mit dem Web-IO statt.
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
Zyklisches Abfragen bestimmter WerteUm bei der laufenden Anwendung nicht händisch die Komponenten aktualisieren zu müssen, kann man das Polling-Verfahren nutzen.
Wenn man einen gültigen Integer-Wert in das IDC_POLLINGTEXT
einträgt, aktivieren sich die 3 für das Polling-Verfahren wichtigen Checkboxen:
IDC_POLLING0
, IDC_POLLING1
& IDC_POLLINGCOUNTER
.
Durch das Setzen des Häkchens kann man in Höhe des eingegeben Integers (in ms) den jeweiligen Polling-Prozess anstoßen.
Die einzelnen Polling-Verfahren sind das automatische Auslesen von den Outputs, Inputs oder der Counter.
Für jede Polling-Variante wird ein eigener Timer initialisiert. Dazu wird eine direkt zugewiesene Timer-ID verwendet.
Wenn das Häkchen aus der Checkbox wieder entfernt wird, wird auch der Timer wieder zerstört.
Fangen der Benutzereingabe:
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 für die Outputs:
void CWebIODlg::OnBnClickedPolling0() {
IsDlgButtonChecked( IDC_POLLING0 ) ? SetTimer( 1, m_rcData.m_iPolling, NULL ) :
KillTimer( 1 );
}
Timer für die Inputs:
void CWebIODlg::OnBnClickedPolling1() {
IsDlgButtonChecked( IDC_POLLING1 ) ? SetTimer( 2, m_rcData.m_iPolling, NULL ) :
KillTimer( 2 );
}
Timer für die Counter:
void CWebIODlg::OnBnClickedPollingcounter() {
IsDlgButtonChecked( IDC_POLLINGCOUNTER ) ? SetTimer( 3, m_rcData.m_iPolling, NULL ) :
KillTimer( 3 );
}
Hier wird die aus CDialogEx
überladene OnTimer()
-Funktion verwendet.
Es wird das jeweilige Event des Timers, der sich gerade meldet, gefangen und einer eindeutigen Aktion zugeordnet.
void CWebIODlg::OnTimer( UINT nIDEvent ) {
if( nIDEvent == 1 ) OnBnClickedReadall0();
if( nIDEvent == 2 ) OnBnClickedReadall1();
if( nIDEvent == 3 ) OnBnClickedReadall();
CDialogEx::OnTimer( nIDEvent );
}
Das gesamte Programm wird auf dieser Seite zur Verfügung gestellt, wobei es einige hier nicht erwähnte Dateien und Funktionen zusätzlich enthält. Die MFC Anwendung unter Visual C++ erzeugt einen Overhead, der den graphischen Rahmen bildet, aber mit der Funktionalität nicht direkt in Verbindung steht. Die Funktionen und Variablen, die Ihnen auf dieser Seite präsentiert worden sind, werden in den von Visual C++ erzeugten Rahmen implementiert und arbeiten dann als Einheit mit der graphischen Oberfläche.
Das Beispielprogramm unterstützt alle gängigen Funktionen des Web-IO im Kommando-String-Modus, optimiert für das Web-IO 2x Digital Input, 2x Digital Output PoE. Für die anderen Web-IO-Modelle müssen ggf. Anpassung am Programm vorgenommen werden. Weitere Programmbeispiele zur Socket-Programmierung finden Sie auf den Tool-Seiten zum Web-IO. Eine detaillierte Beschreibung zur Socket-Schnittstelle der Web-IO Digital Modelle finden Sie im Referenzhandbuch.