// MainDlg.cpp : implementation file // #include "stdafx.h" #include "SerialTester.h" #include "MainDlg.h" #include "serial/SerialMFC.h" #include "SlParaDlg.h" #include "DynamicLED.h" #include "SlControl.h" #include "Slpara.h" #include "SerialParaDlg.h" #include #include #include #include "math.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CAboutDlg dialog used for App About class CAboutDlg : public CDialog { public: CAboutDlg(); // Dialog Data //{{AFX_DATA(CAboutDlg) enum { IDD = IDD_ABOUTBOX }; //}}AFX_DATA // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CAboutDlg) protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL // Implementation protected: //{{AFX_MSG(CAboutDlg) //}}AFX_MSG DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) { //{{AFX_DATA_INIT(CAboutDlg) //}}AFX_DATA_INIT } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CAboutDlg) //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) //{{AFX_MSG_MAP(CAboutDlg) // No message handlers //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CMainDlg dialog /* The constructor. The initialisation of a lot of boolean values. The preferences are read from a file. */ CMainDlg::CMainDlg(CWnd* pParent /*=NULL*/) : CDialog(CMainDlg::IDD, pParent) { //{{AFX_DATA_INIT(CMainDlg) //}}AFX_DATA_INIT m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); m_pEditBkBrush = new CBrush(RGB(255, 255, 255)); m_bParamASent = false; m_bParamBSent = false; m_bParamCSent = false; m_bAutoScroll = true; m_bKeyDownDown=false; m_bKeyUpDown=false; m_bKeyLeftDown=false; m_bKeyRightDown=false; m_bKeyNextDown=false; m_bKeyPriorDown=false; // Set prefs to default values m_buffer = 5000; m_interval = 1000; // Read prefs.cfg try { ifstream file("prefs.cfg"); string line; if (getline (file,line)) { stringstream ss(line.c_str()); ss >> m_buffer; } if (getline (file,line)) { stringstream ss(line.c_str()); ss >> m_interval; } if (getline (file,line)) { stringstream ss(line.c_str()); ss >> m_bAutoSave; } file.close(); } catch (CException* e) { m_StatusBar.SetWindowText("Error: Cannot read prefs.cfg"); e->Delete(); } } /* The destructor frees all the object instances and saves the preferences to a file. */ CMainDlg::~CMainDlg() { if (m_pController->IsConnectionReady()) m_pController->CloseComms(); m_pController->~IControl(); if (m_bAutoSave) m_TermLog->SaveLog(GetLogFileName()); delete m_TermLog; delete m_pController; if (m_pParamDlgA) m_pParamDlgA->DestroyWindow(); if (m_pParamDlgB) m_pParamDlgB->DestroyWindow(); if (m_pParamDlgC) m_pParamDlgC->DestroyWindow(); delete m_pEditBkBrush; delete m_pPref; delete m_pCommsDlg; // Save preferences, overwriting prefs.cfg. try { ofstream file("prefs.cfg"); if (file) { stringstream ss; ss << m_buffer; file<Delete(); } } void CMainDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); for (int i=0;iReadResponse(); break; case CSerialMFC::EEventError: // TODO: Display error message in status bar break; } // Return successful return 0; } /* The instances for CSlControl, CTermLog, CSLParaDlg and CSerialParaDlg are created here. The autostatus timer is also started. */ BOOL CMainDlg::OnInitDialog() { CDialog::OnInitDialog(); // Add "About..." menu item to system menu. // IDM_ABOUTBOX must be in the system command range. ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { CString strAboutMenu; strAboutMenu.LoadString(IDS_ABOUTBOX); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon InitLeds(); m_pCommsDlg = new CSerialParaDlg; m_pParamDlgA = new CSlParaDlg(this, 'A'); m_pParamDlgA->Create(IDD_DIALOGPPage,GetDesktopWindow()); m_pParamDlgB = new CSlParaDlg(this, 'B'); m_pParamDlgB->Create(IDD_DIALOGPPage,GetDesktopWindow()); m_pParamDlgC = new CSlParaDlg(this, 'C'); m_pParamDlgC->Create(IDD_DIALOGPPage,GetDesktopWindow()); m_pPref = NULL; m_pController=new CSlControl(this, m_pCommsDlg, m_pParamDlgA, m_pParamDlgB, m_pParamDlgC); m_TermLog=new CTermLog(); SetTimer(TIMER_AUTOSTATUS,m_interval,NULL); SetTimer(TIMER_TIMEOUT,100,NULL); m_bAutoStatus=false; SetBuffer( m_buffer ); return TRUE; // return TRUE unless you set the focus to a control } void CMainDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialog::OnSysCommand(nID, lParam); } } // If you add a minimize button to your dialog, you will need the code below // to draw the icon. For MFC applications using the document/view model, // this is automatically done for you by the framework. void CMainDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // device context for painting SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0); // Center icon in client rectangle int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // Draw the icon dc.DrawIcon(x, y, m_hIcon); } else { CDialog::OnPaint(); } } // The system calls this to obtain the cursor to display while the user drags // the minimized window. HCURSOR CMainDlg::OnQueryDragIcon() { return (HCURSOR) m_hIcon; } /* The method responsible for showing the correct parameter dialog to the user. */ void CMainDlg::CreateParamDlg(char motorchar) { CSlParaDlg *ParamDlg; ParamDlg=0; CString title; switch(motorchar) { case 'A' : ParamDlg=m_pParamDlgA; title="SerialTester - Parameters for Motor A"; break; case 'B' : ParamDlg=m_pParamDlgB; title="SerialTester - Parameters for Motor B"; break; case 'C' : ParamDlg=m_pParamDlgC; title="SerialTester - Parameters for Motor C"; break; } if (ParamDlg) { ParamDlg->ShowWindow(SW_SHOW); ParamDlg->SetForegroundWindow(); } ParamDlg->SetWindowText(title); ParamDlg->GetValues(); } void CMainDlg::OnShowWindow(BOOL bShow, UINT nStatus) { CDialog::OnShowWindow(bShow, nStatus); } /* Some handling here to stretch the log and commands views */ void CMainDlg::OnSize(UINT nType, int cx, int cy) { // CDialog::OnSize(nType, cx, cy); // Check that log control is really created. if (::IsWindow(m_LogView.GetSafeHwnd())) { CRect rc, rt; // Get current position (relative to screen) m_LogView.GetWindowRect(&rc); m_Terminal.GetWindowRect(&rt); // Convert screen coordinates relative to this dialog ScreenToClient(&rc); ScreenToClient(&rt); // Calculate new width int i_width = cx - rc.TopLeft().x -10; // Don't change height. int i_height = rc.Height(); m_LogView.SetWindowPos(0,0,0,i_width,i_height,SWP_NOMOVE|SWP_NOZORDER); int t_width = cx - rt.TopLeft().x -10; int t_height = rt.Height(); m_Terminal.SetWindowPos(0,0,0,t_width,t_height,SWP_NOMOVE|SWP_NOZORDER); } } void CMainDlg::OnFileExit() { DestroyWindow(); } /* The method called when a LED is clicked on. Only for setting the controller output states. */ void CMainDlg::OnLEDClick(UINT nID) { if (!m_pController->IsConnectionReady()) return; if (LEDID_FIRST_SL_OUT <= nID && nID <= LEDID_LAST_SL_OUT) { if (!m_LedSLOut[nID-LEDID_FIRST_SL_OUT].m_bBright) { m_pController->SetControllerOutOn(nID-LEDID_FIRST_SL_OUT+1); m_LedSLOut[nID-LEDID_FIRST_SL_OUT].SwitchOn(); } else { m_pController->SetControllerOutOff(nID-LEDID_FIRST_SL_OUT+1); m_LedSLOut[nID-LEDID_FIRST_SL_OUT].SwitchOff(); } } } /* Connects to the controller using rs232. Calls m_pController::InitComms() and sets the status bar text accordingly. */ void CMainDlg::OnConnect() { try { if (!m_pController->IsConnectionReady()) { m_pController->InitComms(); m_StatusBar.SetWindowText("Connected"); } else { m_StatusBar.SetWindowText("Port is already opened"); } } catch (CException* e) { m_StatusBar.SetWindowText("Connection failure"); e->Delete(); } } /* Initialises the controller via m_pController and sets the status bar text accordingly. */ void CMainDlg::OnInitSL() { try { m_pController->InitController(); m_StatusBar.SetWindowText("Initialization command sent"); } catch (CException* e) { m_StatusBar.SetWindowText("Initialization failure"); e->Delete(); } } /* Closes the rs202 port via m_pController and sets the status bar text accordingly. */ void CMainDlg::OnClosePort() { try { if (m_pController->IsConnectionReady()) { m_pController->CloseComms(); m_StatusBar.SetWindowText("Disconnected"); } } catch (CException* e) { m_StatusBar.SetWindowText("Exception during comms shutdown"); e->Delete(); } } void CMainDlg::OnButtonTermSend() { try { SendMessage(UWM_EDIT_COMPLETE, GetDlgCtrlID(), (LPARAM)this); } catch (CException* e) { m_StatusBar.SetWindowText("Command failure"); e->Delete(); } } /* The next three methods send the motor target position (read from the GUI target fields) to the controller and start running the motor in question. */ void CMainDlg::OnButtonStart1() { try { if (!m_bParamASent) CreateParamDlg('A'); //check if parameters sent else { m_Target1.GetWindowText(sCommandStr); m_pController->StartMotor('A',(LPCTSTR)sCommandStr); } } catch (CException* e) { m_StatusBar.SetWindowText("Exception during parameter dialog initialization"); e->Delete(); } } void CMainDlg::OnButtonStart2() { try { if (!m_bParamBSent) CreateParamDlg('B'); else { m_Target2.GetWindowText(sCommandStr); m_pController->StartMotor('B',(LPCTSTR)sCommandStr); } } catch (CException* e) { m_StatusBar.SetWindowText("Exception during parameter dialog initialization"); e->Delete(); } } void CMainDlg::OnButtonStart3() { try { if (!m_bParamCSent) CreateParamDlg('C'); else { m_Target3.GetWindowText(sCommandStr); m_pController->StartMotor('C',(LPCTSTR)sCommandStr); } } catch (CException* e) { m_StatusBar.SetWindowText("Exception during parameter dialog initialization"); e->Delete(); } } /* The next three methods send stop messages to the motors. */ void CMainDlg::OnButtonStop1() { try { if (!m_bParamASent) CreateParamDlg('A'); //check if parameters sent else { m_pController->StopMotor('A'); } } catch (CException* e) { m_StatusBar.SetWindowText("STOP command failure for motor A"); e->Delete(); } } void CMainDlg::OnButtonStop2() { try { if (!m_bParamBSent) CreateParamDlg('B'); else { m_pController->StopMotor('B'); } } catch (CException* e) { m_StatusBar.SetWindowText("STOP command failure for motor B"); e->Delete(); } } void CMainDlg::OnButtonStop3() { try { if (!m_bParamCSent) CreateParamDlg('C'); else { m_pController->StopMotor('C'); } } catch (CException* e) { m_StatusBar.SetWindowText("STOP command failure for motor C"); e->Delete(); } } /* The next three methods drive the motor in question to the target with calibration speed. The calibration speed parameter is first read from the parameter dialogs and then it is sent to the controller. */ void CMainDlg::OnButtonCalA() { try { if (!m_bParamASent) CreateParamDlg('A'); else { m_Target1.GetWindowText(sCommandStr); m_pController->CalibrateMotor('A',(LPCTSTR)sCommandStr); } } catch (CException* e) { m_StatusBar.SetWindowText("CALIBRATE command failure for motor A"); e->Delete(); } } void CMainDlg::OnButtonCalB() { try { if (!m_bParamBSent) CreateParamDlg('B'); else { m_Target2.GetWindowText(sCommandStr); m_pController->CalibrateMotor('B',(LPCTSTR)sCommandStr); } } catch (CException* e) { m_StatusBar.SetWindowText("CALIBRATE command failure for motor B"); e->Delete(); } } void CMainDlg::OnButtonCalC() { try { if (!m_bParamCSent) CreateParamDlg('C'); else { m_Target3.GetWindowText(sCommandStr); m_pController->CalibrateMotor('C',(LPCTSTR)sCommandStr); } } catch (CException* e) { m_StatusBar.SetWindowText("CALIBRATE command failure for motor C"); e->Delete(); } } /* The next three methods send the commands for reading status information for a motor. */ void CMainDlg::OnButtonReadA() { try { m_pController->ReadMotorStatus("A"); } catch (CException* e) { m_StatusBar.SetWindowText("READ command failure for motor A"); e->Delete(); } } void CMainDlg::OnButtonReadB() { try { m_pController->ReadMotorStatus("B"); } catch (CException* e) { m_StatusBar.SetWindowText("READ command failure for motor B"); e->Delete(); } } void CMainDlg::OnButtonReadC() { try { m_pController->ReadMotorStatus("C"); } catch (CException* e) { m_StatusBar.SetWindowText("READ command failure for motor C"); e->Delete(); } } /* The method sends the commands for reading status information for the controller I/O LEDs. */ void CMainDlg::OnButtonReadSl() { try { m_pController->ReadControllerStatus("0"); //in two 8 bit parts m_pController->ReadControllerStatus("1"); } catch (CException* e) { m_StatusBar.SetWindowText("READ command failure for controller"); e->Delete(); } } /* Return the line where the caretis from the Commands view. */ string CMainDlg::GetEditLine(int p_nLineNumber) { int i=0; WORD rivi; char cs[MAX_EDITLINE_CHARS]; CString homppa; POINT pt; pt=m_Terminal.GetCaretPos(); rivi=HIWORD(m_Terminal.CharFromPosFix(pt)); i = m_Terminal.GetLine(rivi,cs,100); cs[i] = '\0'; return string(cs); } /* When the return key is pressed in the Commands view, we take the line where the caret is and if the line is a command which reads a motor or an input status we send the command and the info forward to the SendString method. */ LRESULT CMainDlg::OnEditComplete(WPARAM, LPARAM lParam) { try { CEditEnter * edit = (CEditEnter *)lParam; string sEditLine; int n; n = edit->GetLines() - 1; sEditLine = GetEditLine(n); string receiver = ""; if (m_pController->IsConnectionReady() && sEditLine.length()==MOTOR_IN_COMMAND_LENGTH) { if (sEditLine.substr(0,2) == "DS") SwitchOnLed(sEditLine.substr(2,2)); else if (sEditLine.substr(0,2)=="DR") SwitchOffLed(sEditLine.substr(2,2)); } if (sEditLine == "MLA") receiver = "MStatusA"; if (sEditLine == "MLB") receiver = "MStatusB"; if (sEditLine == "MLC") receiver = "MStatusC"; if (sEditLine == "DI0") receiver = "ReadDIn0"; if (sEditLine == "DI1") receiver = "ReadDIn1"; if (sEditLine == "IN") receiver = "init"; if ( receiver != "" ) { this->SendString(sEditLine,receiver); } else { this->SendString(sEditLine, "Terminal"); } } catch (CException* e) { m_StatusBar.SetWindowText("Exception during manual command input"); e->Delete(); } return 0; } /* All the status LEDs are initialised here. */ void CMainDlg::InitLeds() { for (int i=0;iDoModal(); } catch (CException* e) { m_StatusBar.SetWindowText("Exception during dialog initialization"); e->Delete(); } } /* Return the current time in format yyyy-mm-dd hh:mm:ss,s */ CString CMainDlg::GetSysTime() const { SYSTEMTIME SysTime; GetLocalTime(&SysTime); ostringstream oss; oss << SysTime.wYear << "-" << setfill('0') << setw(2) << SysTime.wMonth << "-" << setfill('0') << setw(2) << SysTime.wDay << " " << setfill('0') << setw(2) << SysTime.wHour << ":" << setfill('0') << setw(2) << SysTime.wMinute << ":" << setfill('0') << setw(2) << SysTime.wSecond << "," << SysTime.wMilliseconds/100; return oss.str().c_str(); } /* Set the pause state on/off. If the pause is on, no text is written to the log view. */ void CMainDlg::OnButtonPauseLog() { try { bool bState; bState = m_TermLog->GetPauseState(); if ( !bState ) { m_TermLog->Pause(); m_PauseButton.SetWindowText("Continue"); } if ( bState ) { this->UpdateLogView(); m_TermLog->Continue(); m_PauseButton.SetWindowText("Pause"); } } catch (CException* e) { m_StatusBar.SetWindowText("Pause routine exception"); e->Delete(); } } /* Writes the new log line to the end of log view. If the pause is on no text will be written. If the selected log buffer is full, old lines will be deleted. If the autoscrolling is off, the log view will stay in place. */ void CMainDlg::UpdateLogView() { try { CString sLogLine; int n, k; k = m_LogView.GetFirstVisibleLine(); if ( m_TermLog->GetPauseState() ) { int m=m_TermLog->GetPauseSetOn(); int n=m_TermLog->GetLogSize(); for( int i=m; iGetLogLine(i) + "\r\n").c_str(); } } else { sLogLine = (m_TermLog->GetLastLogLine() + "\r\n").c_str(); } if ( m_TermLog->GetLogBufferFull() ) { m_LogView.SetSel( 0, m_LogView.LineLength(0) + 2 ); m_LogView.ReplaceSel( "" ); } n = m_LogView.GetWindowTextLength(); m_LogView.LineScroll( m_LogView.GetLineCount() ); m_LogView.SetSel(n, -1); m_LogView.ReplaceSel( sLogLine ); if ( !m_bAutoScroll ) { m_LogView.LineScroll( -m_LogView.GetLineCount() ); m_LogView.LineScroll( k ); } } catch (CException* e) { m_StatusBar.SetWindowText("Log update failure"); e->Delete(); } } /* Add the latest sent command to the end of the Commands. */ void CMainDlg::UpdateTerminal() { try { CString sLogLine; int n; sLogLine = (m_TermLog->GetLastCommandLine() + "\r\n").c_str(); n = m_Terminal.GetWindowTextLength(); m_Terminal.LineScroll( m_Terminal.GetLineCount() ); m_Terminal.SetSel(n, -1); m_Terminal.ReplaceSel( sLogLine ); } catch (CException* e) { m_StatusBar.SetWindowText("Terminal update failure"); e->Delete(); } } /* Color the background of the log view as white. */ HBRUSH CMainDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { if (pWnd->GetDlgCtrlID() == IDC_EDITLOG) { switch (nCtlColor) { case CTLCOLOR_STATIC: return (HBRUSH)(m_pEditBkBrush->GetSafeHandle()); default: return CDialog::OnCtlColor(pDC, pWnd, nCtlColor); } } return CDialog::OnCtlColor(pDC, pWnd, nCtlColor); } /* Add the mark ito the end of the log. */ void CMainDlg::OnButtonMark() { try { int n; string st; n = m_LogView.GetLineCount() - 2; if ( n < 0 ) return; m_TermLog->AddMark(n); if ( !m_TermLog->GetPauseState() ) UpdateLogView(); } catch (CException* e) { m_StatusBar.SetWindowText("Mark failure"); e->Delete(); } } /* Saves the current log view. */ void CMainDlg::OnButtonSaveLog() { try { CFileDialog dlg( FALSE, NULL, GetLogFileName().c_str(), NULL | OFN_HIDEREADONLY, "Text Files (*.txt)|*.txt||", this); dlg.m_ofn.lpstrTitle = "Save Log"; if ( dlg.DoModal() == IDOK) { string file = dlg.GetPathName(); // This is your selected file name with path m_TermLog->SaveLog(file); } } catch (CException* e) { m_StatusBar.SetWindowText("Error: Cannot save log"); e->Delete(); } } /* Calls up the preferences dialog and sets the autostatus timer to a new value if necessary. */ void CMainDlg::OnSetupPreferences() { try { if (m_pPref != NULL) m_pPref->DoModal(); else { m_pPref = new CPrefDlg(); m_pPref->DoModal(); } SetTimer(TIMER_AUTOSTATUS,m_interval,NULL); } catch (CException* e) { m_StatusBar.SetWindowText("Preference setup error"); e->Delete(); } } /* Shows the about box. */ void CMainDlg::OnHelpAbout() { try { CAboutDlg About; About.DoModal(); } catch (CException* e) { m_StatusBar.SetWindowText("Cannot initiate About dialog"); e->Delete(); } } /* Set the buffer size of the log. */ void CMainDlg::SetBuffer(int buffersize) { m_buffer = buffersize; m_TermLog->SetLogBufferFull(false); m_LogView.SetLimitText(1000 * m_buffer); } int CMainDlg::GetBuffer() { return m_buffer; } void CMainDlg::SetInterval(int intervalparam) { m_interval = intervalparam; } int CMainDlg::GetInterval() { return m_interval; } /* A method used to switch a led on or off according to the wanted bit in a hex string. */ void CMainDlg::SetLedStatus(string hexstring, int Led,int bit, CDynamicLED* p_Led) { try { if (Led!=LED_ERROR) { int i,x; istrstream is(hexstring.c_str()); is >> hex >> i; x=pow(2,bit); if (((i & x)!=0) ^ p_Led[Led].bReversed) { if (!p_Led[Led].m_bBright) p_Led[Led].SwitchOn(); return; } if (p_Led[Led].m_bBright) p_Led[Led].SwitchOff(); } } catch (CException* e) { m_StatusBar.SetWindowText("Exception during Statusled update"); e->Delete(); } } /* This method updates the status LEDs for a single motor according to status information received from the controller. Also updates the LEDs common to all three motors (i.e power on/off...) */ void CMainDlg::UpdateMotorStatus(string Motor,string response) { if (response=="ERROR") return; try { string position; if (response.length() >= MAX_RESPONSE) position=response.substr(MAX_RESPONSE); CDynamicLED* p_MotorLed; p_MotorLed=0; if (Motor=="A") { p_MotorLed=m_LedMotorA; m_StaticPosA.SetWindowText(position.c_str()); } if (Motor=="B") { p_MotorLed=m_LedMotorB; m_StaticPosB.SetWindowText(position.c_str()); } if (Motor=="C") { p_MotorLed=m_LedMotorC; m_StaticPosC.SetWindowText(position.c_str()); } //create a table of the LED indexes int Leds[24]={3,2,1,0,4,0,1,2, LED_ERROR,LED_ERROR,10,9,LED_ERROR,8,LED_ERROR,7,LED_ERROR,6,5, LED_ERROR,LED_ERROR,LED_ERROR,LED_ERROR,LED_ERROR}; string respchar; if (response !="") respchar=response.substr(0,1); for (int i=0; i<=3; i++) { SetLedStatus(respchar,Leds[i],i,p_MotorLed); } respchar=response.substr(1,1); SetLedStatus(respchar,Leds[4],3,p_MotorLed); SetLedStatus(respchar,Leds[5],2,m_LedSLMotors); SetLedStatus(respchar,Leds[6],1,m_LedSLMotors); SetLedStatus(respchar,Leds[7],0,m_LedSLMotors); respchar=response.substr(2,4); for (i=0; i<16; i++) { SetLedStatus(respchar,Leds[i+8],i,p_MotorLed); } } catch(CException *e) { m_StatusBar.SetWindowText("Input led update error"); e->Delete(); } } void CMainDlg::UpdateControllerStatus(string response,int port) { int n=port*NUM_PORT_BITS; for (int i=0; iDelete(); } } } /* The method handles the autostatus timer and the timeout timer. The autostatus timer reads the status information for all the motors and the controller I/O:s. The timers are separated with an ID. */ void CMainDlg::OnTimer(UINT nIDEvent) { if (nIDEvent == TIMER_AUTOSTATUS) { try { if (m_bAutoStatus) { m_pController->ReadMotorStatus("A"); m_pController->ReadMotorStatus("B"); m_pController->ReadMotorStatus("C"); OnButtonReadSl(); } } catch (CException* e) { m_StatusBar.SetWindowText("Autopolling failure"); e->Delete(); } } if (nIDEvent==TIMER_TIMEOUT) { m_pController->CheckTimeout(); } if (nIDEvent==TIMER_COMMAND_INTERVAL) { if (!(commandbuffer.empty())) { pair p1; p1 = commandbuffer.front(); commandbuffer.pop(); m_pController->SendToController(p1.first,p1.second); } } } /* Manual drive button handling for all the motors. The mehod sends start messages to motors according to GUI button id:s. */ void CMainDlg::OnDriveButton(UINT nID) { switch(nID) { case IDC_BUTTONFwdA : if ( !m_bParamASent) CreateParamDlg('A'); else m_pController->StartMotor('A',"16384"); break; case IDC_BUTTONFwdB : if (!m_bParamBSent) CreateParamDlg('B'); else m_pController->StartMotor('B',"16384"); break; case IDC_BUTTONFwdC : if (!m_bParamCSent) CreateParamDlg('C'); else m_pController->StartMotor('C',"16384"); break; case IDC_BUTTONBackA : if (!m_bParamASent) CreateParamDlg('A'); else m_pController->StartMotor('A',"-16384"); break; case IDC_BUTTONBackB : if (!m_bParamBSent) CreateParamDlg('B'); else m_pController->StartMotor('B',"-16384"); break; case IDC_BUTTONBackC : if (!m_bParamCSent) CreateParamDlg('C'); else m_pController->StartMotor('C',"-16384"); break; } } /* The drive buttons call this method when the button is released. It sends the correct stop messages according to button id:s. */ void CMainDlg::OnDriveBtnUp(UINT nID) { try { switch(nID) { case IDC_BUTTONFwdA : OnButtonStop1();break; case IDC_BUTTONFwdB : OnButtonStop2();break; case IDC_BUTTONFwdC : OnButtonStop3();break; case IDC_BUTTONBackA : OnButtonStop1();break; case IDC_BUTTONBackB : OnButtonStop2();break; case IDC_BUTTONBackC : OnButtonStop3();break; } } catch (CException* e) { m_StatusBar.SetWindowText("Manual motor running exception"); e->Delete(); } } /* A method for switching autostatus on or off. */ void CMainDlg::OnSetupAutostatus() { try { m_bAutoStatus=!m_bAutoStatus; } catch (CException* e) { m_StatusBar.SetWindowText("Exception during Autostatus menu item handling"); e->Delete(); } } /* For updating the autostatus check mark in the command menu. */ void CMainDlg::OnUpdateSetupAutostatus(CCmdUI* pCmdUI) { pCmdUI->SetCheck(m_bAutoStatus); } /* This method is for getting the command menu items (check marks) to display correctly. (needs some overriding in a dialog based application) */ void CMainDlg::OnInitMenuPopup(CMenu *pPopupMenu, UINT nIndex, BOOL bSysMenu) { ASSERT(pPopupMenu != NULL); // Check the enabled state of various menu items. CCmdUI state; state.m_pMenu = pPopupMenu; ASSERT(state.m_pOther == NULL); ASSERT(state.m_pParentMenu == NULL); // Determine if menu is popup in top-level menu and set m_pOther to // it if so (m_pParentMenu == NULL indicates that it is secondary // popup). HMENU hParentMenu; if (AfxGetThreadState()->m_hTrackingMenu == pPopupMenu->m_hMenu) state.m_pParentMenu = pPopupMenu; // Parent == child for tracking //popup. else if ((hParentMenu = ::GetMenu(m_hWnd)) != NULL) { CWnd* pParent = this; // Child windows don't have menus--need to go to the top! if (pParent != NULL && (hParentMenu = ::GetMenu(pParent->m_hWnd)) != NULL) { int nIndexMax = ::GetMenuItemCount(hParentMenu); for (int nIndex = 0; nIndex < nIndexMax; nIndex++){ if (::GetSubMenu(hParentMenu, nIndex) == pPopupMenu->m_hMenu){ // When popup is found, m_pParentMenu is containing menu. state.m_pParentMenu = CMenu::FromHandle(hParentMenu); break; } } } } state.m_nIndexMax = pPopupMenu->GetMenuItemCount(); for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax; state.m_nIndex++) { state.m_nID = pPopupMenu->GetMenuItemID(state.m_nIndex); if (state.m_nID == 0) continue; // Menu separator or invalid cmd - ignore it. ASSERT(state.m_pOther == NULL); ASSERT(state.m_pMenu != NULL); if (state.m_nID == (UINT)-1) { // Possibly a popup menu, route to first item of that popup. state.m_pSubMenu = pPopupMenu->GetSubMenu(state.m_nIndex); if (state.m_pSubMenu == NULL || (state.m_nID = state.m_pSubMenu->GetMenuItemID(0)) == 0 || state.m_nID == (UINT)-1) { continue; // First item of popup can't be routed to. } state.DoUpdate(this, TRUE); // Popups are never auto disabled. } else { // Normal menu item. // Auto enable/disable if frame window has m_bAutoMenuEnable // set and command is _not_ a system command. state.m_pSubMenu = NULL; state.DoUpdate(this, FALSE); } // Adjust for menu deletions and additions. UINT nCount = pPopupMenu->GetMenuItemCount(); if (nCount < state.m_nIndexMax) { state.m_nIndex -= (state.m_nIndexMax - nCount); while (state.m_nIndex < nCount && pPopupMenu->GetMenuItemID(state.m_nIndex) == state.m_nID) { state.m_nIndex++; } } state.m_nIndexMax = nCount; } } /* The next three methods are handlers for the situations where the user wishes to call up the parameters dialog (i.e the buttons and the command menu) */ void CMainDlg::OnButtonParamA() { try { CreateParamDlg('A'); } catch (CException* e) { m_StatusBar.SetWindowText("Error: Cannot create parameter dialog for motor A"); e->Delete(); } } void CMainDlg::OnButtonParamB() { try { CreateParamDlg('B'); } catch (CException* e) { m_StatusBar.SetWindowText("Error: Cannot create parameter dialog for motor B"); e->Delete(); } } void CMainDlg::OnButtonParamC() { try { CreateParamDlg('C'); } catch (CException* e) { m_StatusBar.SetWindowText("Error: Cannot create parameter dialog for motor C"); e->Delete(); } } /* If the string get as parameter is not empty and the connection is open, the command will be sent to the target controller and written to the log and to the Commands field. */ void CMainDlg::SendString(string p_sLine, string recvd) { try { if (m_pController->IsConnectionReady() && p_sLine!="") { if (recvd!="Terminal" && recvd!="MStatusA" && recvd!="MStatusB" && recvd!="MStatusC" && recvd!="ReadDIn0" && recvd!="ReadDIn1" && recvd!="init") { m_TermLog->SendCommand(p_sLine); this->UpdateTerminal(); } if (recvd=="Terminal") m_TermLog->SendCommand(p_sLine); pair p1(p_sLine,recvd); SetTimer(TIMER_COMMAND_INTERVAL,COMMAND_INTERVAL,NULL); commandbuffer.push(p1); string st; st = GetSysTime(); st = st + " Sent> " + p_sLine; if ( (m_LogView.GetWindowTextLength() + MAX_LINE_LENGTH) > m_LogView.GetLimitText() ) { m_TermLog->SetLogBufferFull(true); } m_TermLog->LogCommand(st); if ( !m_TermLog->GetPauseState() ) { this->UpdateLogView(); } } } catch (CException* e) { m_StatusBar.SetWindowText("Error: Cannot send command"); e->Delete(); } } /* Add a string received from the target to the log. */ void CMainDlg::ReceiveString(string p_sLine) { try { string st; st = GetSysTime(); st = st + " Recv> " + p_sLine; if ( (m_LogView.GetWindowTextLength() + MAX_LINE_LENGTH) > m_LogView.GetLimitText() ) { m_TermLog->SetLogBufferFull(true); } m_TermLog->LogResponse(st); if ( !m_TermLog->GetPauseState() ) { this->UpdateLogView(); } } catch (CException* e) { m_StatusBar.SetWindowText("Error: Bad ReceiveString call"); e->Delete(); } } void CMainDlg::OnButtonSaveTerminal() { try { CFileDialog dlg( FALSE, "txt", NULL, NULL | OFN_HIDEREADONLY, "Text Files (*.txt)|*.txt||", this); dlg.m_ofn.lpstrTitle = "Save Command File"; if ( dlg.DoModal() == IDOK){ string file = dlg.GetPathName(); char cs[MAX_EDITLINE_CHARS]; int k=0; m_TermLog->ClearCommands(); for( int i=0; iSendCommand( string(cs) ); } m_TermLog->SaveTerminal(file); } } catch (CException* e) { m_StatusBar.SetWindowText("Error: Cannot save command buffer"); e->Delete(); } } /* Clear the Commands field and write the lines from a file to the Commands field. */ void CMainDlg::OnButtonLoadCommandFile() { try { CFileDialog dlg( TRUE, NULL, NULL, NULL | OFN_HIDEREADONLY, "Text Files (*.txt)|*.txt||", this); dlg.m_ofn.lpstrTitle = "Load Command File"; if (dlg.DoModal() == IDOK) { string file = dlg.GetPathName(); // This is your selected file name with path CString sCommandLine; m_Terminal.SetWindowText(""); m_TermLog->LoadCommandFile(file); int n=m_TermLog->GetCommandsSize(); for (int i=0; iGetCommandLine(i).c_str(); sCommandLine = sCommandLine + "\r\n"; } m_Terminal.SetWindowText(sCommandLine); } } catch (CException* e) { m_StatusBar.SetWindowText("Error: Cannot load command file"); e->Delete(); } } /* Send all lines from the Commands. */ void CMainDlg::OnButtonRunAll() { try { int i, k=0; char cs[MAX_EDITLINE_CHARS]; int n=m_Terminal.GetLineCount(); for (i=0; iSendString( string(cs),"Terminal" ); } m_Terminal.LineScroll( m_Terminal.GetLineCount(), 0 ); } catch (CException* e) { m_StatusBar.SetWindowText("Exception during SENDALL command"); e->Delete(); } } /* This method sends the motor power on message to the controller via m_pController. */ void CMainDlg::OnButtonPowerOn() { try { m_pController->MotorPower(true); } catch (CException* e) { m_StatusBar.SetWindowText("Error: Cannot turn power on"); e->Delete(); } } /* This method sends the motor power off message to the controller via m_pController. */ void CMainDlg::OnButtonPowerOff() { try { m_pController->MotorPower(false); } catch (CException* e) { m_StatusBar.SetWindowText("Error: Cannot turn power off"); e->Delete(); } } /* SetParamsSent() sets boolean m_bParamXSent true if params are sent, so that running/stopping/calibrating motors doesn't open Parameter dialog. */ void CMainDlg::SetParamsSent(char motor, bool boolean) { if (motor=='A') m_bParamASent = boolean; if (motor=='B') m_bParamBSent = boolean; if (motor=='C') m_bParamCSent = boolean; } void CMainDlg::OnHelpHelp() { try { ShellExecute(NULL,"open","st_help.htm", NULL, NULL, SW_SHOWNORMAL); } catch (CException* e) { m_StatusBar.SetWindowText("Error: Cannot open st_help.htm"); e->Delete(); } } // SwitchOnLed switches Led on if it's not already switched off. void CMainDlg::SwitchOnLed(string command) { std::istringstream is(command); int i; is >> i; //port 0 if (0 <= i && i < NUM_PORT_BITS) { if (!m_LedSLOut[i].m_bBright) m_LedSLOut[i].SwitchOn(); } //port 1,1st bit last bit else if (NUM_PORT_BITS+2 <= i && i <= (NUM_PORT_BITS*2)+1) { if (!m_LedSLOut[i-2].m_bBright) m_LedSLOut[i-2].SwitchOn(); } } // SwitchOffLed switches Led off if it's not already switched off. void CMainDlg::SwitchOffLed(string command) { std::istringstream is(command); int i; is >> i; //port 0 if (0 <= i && i < NUM_PORT_BITS) { if (m_LedSLOut[i].m_bBright) m_LedSLOut[i].SwitchOff(); } //port 1 else if (NUM_PORT_BITS+2 <= i && i <= (NUM_PORT_BITS*2)+1) { if (m_LedSLOut[i-2].m_bBright) m_LedSLOut[i-2].SwitchOff(); } } void CMainDlg::OnSetupAutoscroll() { m_bAutoScroll=!m_bAutoScroll; } void CMainDlg::OnUpdateSetupAutoscroll(CCmdUI* pCmdUI) { pCmdUI->SetCheck(m_bAutoScroll); } /* GetLogFileName() returns name for the log file. */ string CMainDlg::GetLogFileName() { SYSTEMTIME SysTime; GetLocalTime(&SysTime); ostringstream oss; oss << "Log_" << SysTime.wYear << "-" << setfill('0') << setw(2) << SysTime.wMonth << "-" << setfill('0') << setw(2) << SysTime.wDay << "_" << setfill('0') << setw(2) << SysTime.wHour << setfill('0') << setw(2) << SysTime.wMinute << ".txt"; return oss.str(); } /* Log autosaving. */ bool CMainDlg::GetAutoSaving() { return m_bAutoSaving; } void CMainDlg::SetAutoSaving(bool autosave) { m_bAutoSaving = autosave; } /* The next three methods are for setting the motor status to zero both in the GUI and in the controller. */ void CMainDlg::OnButtonzeroa() { m_StaticPosA.SetWindowText("0"); m_pController->SetMotorToZero('A'); } void CMainDlg::OnButtonzerob() { m_StaticPosB.SetWindowText("0"); m_pController->SetMotorToZero('B'); } void CMainDlg::OnButtonzeroc() { m_StaticPosC.SetWindowText("0"); m_pController->SetMotorToZero('C'); } /* Keyboard handling. All the keyboard shortcuts are defined here. The drive button keyboard handling is done by checking whether the message is of type WM_KEYUP and then stopping the motors accordingly. Some of the shortcuts are not in effect if the focus is set to the "commands" area in the GUI. */ BOOL CMainDlg::PreTranslateMessage(MSG* pMsg) { bool FocusNotCommands = (::GetFocus() != m_Terminal); //Key handling for drive button release if ((pMsg->message == WM_KEYUP) && FocusNotCommands) { if ((pMsg->wParam==VK_DOWN) || (pMsg->wParam==VK_UP)) { m_bKeyDownDown=false; m_bKeyUpDown=false; OnButtonStop1(); } if ((pMsg->wParam==VK_NEXT) || (pMsg->wParam==VK_PRIOR)) { m_bKeyNextDown=false; m_bKeyPriorDown=false; OnButtonStop2(); } if ((pMsg->wParam==VK_LEFT) || (pMsg->wParam==VK_RIGHT)) { m_bKeyLeftDown=false; m_bKeyRightDown=false; OnButtonStop3(); } return TRUE; } if (pMsg->message == WM_KEYDOWN || pMsg->message == WM_SYSKEYDOWN) { switch (pMsg->wParam) { //Escape caught so the main dialog won't close case VK_ESCAPE: return true; //Key handling for Motor A controls case VK_F1: if (GetKeyState( VK_CONTROL ) < -1) { OnButtonStart1(); return TRUE; } OnButtonReadA(); return TRUE; case VK_F2: if (GetKeyState( VK_CONTROL ) < -1) { OnButtonStop1(); return TRUE; } OnButtonParamA(); return TRUE; case VK_F3: OnButtonCalA(); return TRUE; case VK_F4: OnButtonzeroa(); return TRUE; case VK_DOWN: if (!m_bKeyDownDown && FocusNotCommands) { OnDriveButton(IDC_BUTTONFwdA); m_bKeyDownDown=true; return TRUE; } break; case VK_UP: if (!m_bKeyUpDown && FocusNotCommands) { OnDriveButton(IDC_BUTTONBackA); m_bKeyUpDown=true; return TRUE; } break; //Key handling for Motor B controls case VK_F5: if (GetKeyState( VK_CONTROL ) < -1) { OnButtonStart2(); return TRUE; } OnButtonReadB(); return TRUE; case VK_F6: if (GetKeyState( VK_CONTROL ) < -1) { OnButtonStop2(); return TRUE; } OnButtonParamB(); return TRUE; case VK_F7: OnButtonCalB(); return TRUE; case VK_F8: OnButtonzerob(); return TRUE; case VK_PRIOR: if (!m_bKeyPriorDown && FocusNotCommands) { OnDriveButton(IDC_BUTTONFwdB); m_bKeyPriorDown=true; return TRUE; } break; case VK_NEXT: if (!m_bKeyNextDown && FocusNotCommands) { OnDriveButton(IDC_BUTTONBackB); m_bKeyNextDown=true; return TRUE; } break; //Key handling for Motor C controls case VK_F9: if (GetKeyState( VK_CONTROL ) < -1) { OnButtonStart3(); return TRUE; } OnButtonReadC(); return TRUE; case VK_F10: if (GetKeyState( VK_CONTROL ) < -1) { if (pMsg->message == WM_SYSKEYDOWN) return TRUE; OnButtonStop3(); return TRUE; } OnButtonParamC(); return TRUE; case VK_F11: OnButtonCalC(); return TRUE; case VK_F12: OnButtonzeroc(); return TRUE; case VK_RIGHT: if (!m_bKeyRightDown && FocusNotCommands) { OnDriveButton(IDC_BUTTONFwdC); m_bKeyRightDown=true; return TRUE; } break; case VK_LEFT: if (!m_bKeyLeftDown && FocusNotCommands) { OnDriveButton(IDC_BUTTONBackC); m_bKeyLeftDown=true; return TRUE; } break; //Key handling for the terminal and log views and the controller read button case 'M': if (GetKeyState( VK_CONTROL ) < -1) { OnButtonMark(); return TRUE; } break; case VK_PAUSE: { OnButtonPauseLog(); return TRUE; } break; case 'S': if (GetKeyState( VK_CONTROL ) < -1) { OnButtonSaveLog(); return TRUE; } break; case 'L': if (GetKeyState( VK_CONTROL ) < -1) { OnButtonLoadCommandFile(); return TRUE; } break; case 'R': if (GetKeyState( VK_CONTROL ) < -1) { OnButtonReadSl(); return TRUE; } break; case 'A': if (GetKeyState( VK_CONTROL ) < -1) { OnSetupAutostatus(); return TRUE; } break; case 'P': if (GetKeyState( VK_CONTROL ) < -1) { OnButtonPowerOn(); return TRUE; } break; case '0': if (GetKeyState( VK_CONTROL ) < -1) { OnButtonPowerOff(); return TRUE; } break; } } return CDialog::PreTranslateMessage( pMsg ); } /* Clear the Log view. */ void CMainDlg::OnEditClear() { m_TermLog->ClearLog(); m_LogView.SetWindowText(""); } /* For overriding the windows F1 help behaviour */ BOOL CMainDlg::OnHelpInfo(HELPINFO* pHelpInfo) { return false; } /* Overridden so the main dialog won't close if the user presses Enter */ void CMainDlg::OnOK() { } void CMainDlg::OnContextMenu(CWnd* pWnd, CPoint point) { // TODO: Add your message handler code here }