/***************************************************************************** - - - POLONY SEQUENCER ACQUISITION SUITE - - Church Lab - - Harvard Medical School - - - - Free software for running a Polony Sequencing automated microscope - - - - ========================================================================= - - - - - - cstage.cpp - - - - Interface class to Prior encoded stage - - - - Written by Michelle Kuykendal, 08-10-2005 - - - - Revised - - 02-15-2006 GP; added functions to return stage pos, commented nested - - blocks - - - - This software may be used, modified, and distributed freely, but this - - header may not be modified and must appear at the top of this file. - - - - - *****************************************************************************/ /****************************************************************************** ******************************************************************************* **** File: cstage.cpp **** **** Requirements: cstage.h and cport.h should exist in the same **** **** directory as this file. if the file is being used in the GUI_Devices **** **** project, the StdAfx.h and StdAfx.cpp files should also exit in the **** **** same directory. if this file is used outside of the GUI_Devices **** **** project(or any Microsoft Visual project) then the line of code **** **** "#include "StdAfx.h" may be commented. **** **** Purpose: this file defines the functions of the CStage class to be **** **** used with a Prior ProScan II Motorized Microscope Stage. it enables **** **** the user to set, confirm, and get the stage position and to **** **** initialize and. it also provides functions to write to and read **** **** from the stage via the comm port to enable the addition of new **** **** functions in the future. **** **** Initial Version: Aug. 10, 2005 **** **** Programmer: Michelle Kuykendal **** ******************************************************************************* ******************************************************************************/ //#include "StdAfx.h" //header file for microsoft visual functionality #include "cstage.h" //definition of the stage class #include "cport.h" //serial port class #include //used for retrieving system clock time #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif CStage::CStage(Reporter* rep){ r = rep; stageIsInit = false;//variable used to check if the stage is initialized //initialize variable arrays for(int temp = 0; temp < 40; temp++) { Response[temp] = NULL;//to hold expected read string xyzPos[temp] = NULL; //to hold the current x,y,z position } xmin = -2248344;//minimum x position allowed xmax = 0; //maximum x position allowed ymin = -1442565;//minimum y position allowed ymax = 0; //maximum y position allowed //when looping to check for read variables, tTimeOut is the maximum wait // time allowed. it is determined by reading the system clock time. tTimeOut = 10000;//in milliseconds } //REQUIREMENTS: none. //USE: ~CStage() is the destructor for the stage class. //RETURN VALUE: none. CStage::~CStage() { } //REQUIREMENTS: a call to Init() should be made prior to this call for // proper execution, however, a failure will not occur otherwise. //USE: SetPosition() will check that the x and y values passed to it are // within a valid range for the stage movement. if the variables are in // the valid range, the function will set the private variables, setx and // sety to the values passed. next, the function will write to the stage // to set its position to x,y. it will not set the z position of the stage. // it will then read from the stage until it receives a confirmation // response that the command was received. //RETURN VALUE: the return will be false if the stage was not previously // initialized, if either of the passd variables (x or y) are outside of // the valid range, or if the WriteStage() call is unsuccessful. otherwise, // the return will be true if the stage is already initialized, the x and y // variables are within valid range, and the write and read were // successful. bool CStage::SetPosition(int x, int y) { if(stageIsInit)//check to see if the stage has been initialized { //time structures to hold the start and current times struct timeb tSStart, tSCurrent; int tSDiff;//the difference between current and start time in millisec char Writer[40];//variable to hold string to be written for(int temp = 0; temp < 40; temp++){//initialization of write string Writer[temp] = NULL; } setx = x; sety = y; //command to set stage position issued by writing "G,X,Y" sprintf(Writer, "G,%d,%d%c", x, y, 13); //expected response from the stage is "R" to confirm command // was received. sprintf(Response, "R%c", 13); PortStage.mBytesToRead = 2;//read length in bytes //write the command string to the stage if(WriteStage(Writer)) { ftime(&tSStart);//set the start time from system clock tSDiff = 0;//clear the time difference checker for timeout //read from the stage in two byte chunks until "R" is read // or until the difference in the current time and the start // time is greater than the designated timeout while(!ReadStage() && tSDiff < tTimeOut) { ftime(&tSCurrent);//set the current time from sys clock //calculate the time difference. the time parameter is // stored in seconds, and the millitm parameter is millisec tSDiff = (int) (1000.0 * (tSCurrent.time - tSStart.time) + (tSCurrent.millitm - tSStart.millitm)); } if(tSDiff > tTimeOut) return false;//if the loop ended due to a timeout } else//the write was unsuccessful return false; return true;//everything was successful } else//the stage was not first initialized return false; } //REQUIREMENTS: a call to Init() should be made prior to this call for // proper execution, however, a failure will not occur otherwise. // also, a call to SetPosition() should first be made and should return // true. //USE: ConfirmPosition() will continuously write to and read from the stage // to get the current stage position and will compare it to the position // that should have been set during the call to SetPosition(). it will // accept that the stage is in the correct position when it moves within // 200 units on both the x and y axis. //RETURN VALUE: the return will be false if the stage was not previously // initialized, if the WriteStage() call is ever unsuccessful, or if the // function looking for the appropriate position times out. otherwise, // the return will be true when the stage is in the desired location as was // specified by the call to SetPosition(). bool CStage::ConfirmPosition() { if(stageIsInit)//check to see if the stage has been initialized { //time structures to hold the start and current times struct timeb tCStart, tCCurrent; int tCDiff;//the difference between current and start time in millisec int Xdiff;//the difference between the set and retrieved x value int Ydiff;//the difference between the set and retrieved y value ftime(&tCStart);//set the start time do//always execute the following code at least once { //call the stage GetPosition function to retrieve a string in the // format "X,Y,Z = currentX,currentY,0" where currentX and // currentY are the current stage locations char *PosHolder = GetPosition(); //check to see if the GetPosition() failed. the return string // will then be "Could not get stage position." if(!strcmp(PosHolder,"Could not get stage position.")) return false; //parse the position string into desired x and y sections char *token, *Xtoken, *Ytoken; token = strtok(PosHolder," ");//token is "X,Y,Z" token = strtok(NULL," ");//token is "=" Xtoken = strtok(NULL,",");//Xtoken is "-xxxx" unknown # of x's Ytoken = strtok(NULL,",");//Ytoken is "-yyyy" unknown # of y's int Xer = atoi(Xtoken);//convert the x value string to int int Yer = atoi(Ytoken);//convert the y value string to int //calculate the difference between the desired x and y positions // and the returned current x and y positions Xdiff = abs(Xer - setx); Ydiff = abs(Yer - sety); ftime(&tCCurrent);//get the current time //calculate the time difference. the time parameter is // stored in seconds, and the millitm parameter is millisec tCDiff = (int) (1000.0 * (tCCurrent.time - tCStart.time) + (tCCurrent.millitm - tCStart.millitm)); } //keep looping until the stage is withing 200 units of the desired // position on both the X and Y axis or a timeout occurs while(!((Xdiff < 2) && (Ydiff < 2)) && (tCDiff < tTimeOut)); if(tCDiff > tTimeOut) return false;//the loop ended due to a timeout return true;//the stage is now in the desired position } else//the stage was not first initialized return false; } //REQUIREMENTS: a call to Init() should be made prior to this call for // proper execution, however, a failure will not occur otherwise. //USE: GetPosition() will write to and read from the stage to get the // current stage position. it will assign the position to a string to be // returned. //RETURN VALUE: if the call is successful the return will be a string // containing the current stage position in the format, // "X,Y,Z = currentX,currentY,0" where currentX and current Y are the // current stage positions. the string will contain "Could not set the // stage position" if the stage was not previously initialized, or if // the WriteStage() call is unsuccessful. char *CStage::GetPosition() { if(stageIsInit)//check to see if the stage has been initialized { //time structures to hold the start and current times struct timeb tGStart, tGCurrent; int tGDiff;//the difference between current and start time in millisec char PosHolder[40];//local variable to hold the returned position char Writer[40];//variable to hold string to be written //initialization of the write and position holder strings for(int temp = 0; temp < 40; temp++) { PosHolder[temp] = NULL; Writer[temp] = NULL; } //command to retrieve stage position issued by writing "P" sprintf(Writer, "P%c", 13); //designates that the the read will be one byte at a time and will look // for the carriage return located at the end of the returned string sprintf(Response, "%c", 13); PortStage.mBytesToRead = 1; //write the command string to the stage if(WriteStage(Writer)) { ftime(&tGStart);//set the start time tGDiff = 0;//clear the time difference checker for timeout //keep reading until either the read byte is a carriage return // "" or a timeout occurrs. concatenate the read byte onto // the position holder string on each loop while(!ReadStage() && (tGDiff < tTimeOut)) { strcat(PosHolder,PortStage.mInBuf); ftime(&tGCurrent);//set the current time //calculate the time difference. the time parameter is // stored in seconds, and the millitm parameter is millisec tGDiff = (int) (1000.0 * (tGCurrent.time - tGStart.time) + (tGCurrent.millitm - tGStart.millitm)); } //if a timeout did not occur the xyzPos string should now hold // "X,Y,Z = currentX,currentY,0" if(tGDiff < tTimeOut){ sprintf(xyzPos, "X,Y,Z = %s", PosHolder); } else{//if the loop ended due to a timeout sprintf(xyzPos, "Could not get stage position."); } } else{//the write was unsuccessful sprintf(xyzPos, "Could not get stage position."); } } else{//the stage was not first initialized sprintf(xyzPos, "Could not get stage position."); } return xyzPos;//return the position string } //REQUIREMENTS: the stage device should be connected to comm port 8 //USE: Init() will first check to be sure the stage has not already been // initialized. it will then open and setup comm port 8. three commands // will be sent to the stage to set its compatibility mode, acceleration // and speed. //RETURN VALUE: the return will be false is the stage was already // initialized or if any of the writes and reads failed. the return will // be true if the stage was not already initialized, the comm port was // opened successfully, and the three initialization commands were // properly sent and executed. bool CStage::Init() { if(!stageIsInit)//only initialize the scope if it's not already initalized { stageIsInit = true;//set the initialization checker to true sprintf(PortStage.mPort, "COM8");//COM8 will be opened if(PortStage.OpenCPort())//open and setup the comm port { char Writer[40];//variable to hold string to be written for(int temp = 0; temp < 40; temp++)//initialize the write string Writer[temp] = NULL; //expected response from the stage will be the string "0" for // each of the following commands sprintf(Response, "0%c", 13); PortStage.mBytesToRead = 2;//read in two byte chunks //set the compatibility mode to 0 for proper communication sprintf(Writer, "COMP,0%c", 13); if(WriteStage(Writer)) { //if the two byte read does not match the Response string // assigned above then an error occurred if(!ReadStage()) { Close();//close the comm port return false; } } else//if the write was unsuccessful { Close();//close the comm port return false; } //set the stage speed (0 - 100) to max 100 sprintf(Writer, "SMS,100%c", 13); if(WriteStage(Writer)) { //if the two byte read does not match the Response string // assigned above then an error occurred if(!ReadStage()) { Close();//close the comm port return false; } } else//if the write was unsuccessful { Close();//close the comm port return false; } //set the stage acceleration (0 - 100) to max 100 sprintf(Writer, "SAS,100%c", 13); if(WriteStage(Writer)) { //if the two byte read does not match the Response string // assigned above then an error occurred if(!ReadStage()) { Close();//close the comm port return false; } } else//the write was unsuccessful { Close();//close the comm port return false; } return true;//the initialization was successful } else//the port was not opened return false; } else//the stage is already initialized return false; } //REQUIREMENTS: a call to Init() should be made prior to this call for // proper execution, however, a failure will not occur otherwise. //USE: Close() will check to see that the stage was first intialized. it // will then close the comm port that was opened during the call to Init(), // and reset the stage initialization checker to false. //RETURN VALUE: if the port was successfully closed, the return will be // true. otherwise,the return will be false. bool CStage::Close() { if(stageIsInit)//only close the port if it was previously initialized { if(PortStage.CloseCPort())//close the comm port { stageIsInit = false;//reset the init checker to false return true; } else//the comm port could not be closed return false; } else//the stage was not first initialized return false; } //REQUIREMENTS: a call to Init() should be made prior to this call for // proper execution, however, a failure will not occur otherwise. //USE: WriteStage() will check to see that the stage was first intialized. // it will then write the passed string to the comm port that was opened // during the call to Init(). while the write has not completed (known // because mResWrite, the result of the write, will be false) the function // will continuously check for completion. NOTE: the WriteCPort() command // will only return false if an error occurs. the return value of the // function should not be used to determine if the write was completed. // the return value should only be used to confirm that an error did or did // not occur. if the write command was executed, but the write was not // immediately completed, it will return false and the mResWrite variable // will remain false. if the write was immediately completed, the mResWrite // variable will be true. //RETURN VALUE: the return will be false if the stage has not been first // initialized or the WriteCPort() call returned false due to an error. the // return will be true if the stage is already intialized and te write is // successful. bool CStage::WriteStage(char *ToWrite) { if(stageIsInit)//check to see if the stage has been initialized { //set the port output buffer to the string that will be written sprintf(PortStage.mOutBuf, "%s", ToWrite); if(PortStage.WriteCPort())//write the output buffer to the port { while(PortStage.mResWrite != true)//if the write has not completed PortStage.CheckWrite();//check for completion } else//an error occurred during the write return false; return true;//the write was successful } else//the stage was not first initialized return false; } //REQUIREMENTS: a call to Init() should be made prior to this call for // proper execution, however, a failure will not occur otherwise. the // mBytesToRead parameter of the comm port should first be designated // by the command PortStage.mBytesToRead = num; where num is the number // of bytes to be read. the Response string should first be set to the // string that is expected to be read from the stage. if it is not first // set, the function will return false. //USE: ReadStage() will check to see that the stage was first intialized. // it will then read from the comm port that was opened during the call // to Init() in byte chunks of a size that was designated by the parameter // mBytesToRead. while the read has not completed (known because mResRead, // the result of the read, will be false) the function will continuously // check for completion. it will then compare the read string to the // expected Response string. NOTE: the ReadCPort() command will only // return false if an error occurs. the return value of the function should // not be used to determine if the read was completed. the return value // should only be used to confirm that an error did or did not occur. if // the read command was executed, but the read was not immediately // completed, it will return false and the mResRead variable will remain // false. if the read was immediately completed, the mResRead variable // will be true. //RETURN VALUE: the return will be false if the stage has not been first // initialized, the ReadCPort() call returned false due to an error, or // because the string read was not the same as the Response string. the // return will be true if the stage is already intialized and if what is // read is the same as the Response string bool CStage::ReadStage() { if(stageIsInit)//check to see if the stage has been initialized { if(PortStage.ReadCPort())//issue the comm port read command { while(PortStage.mResRead != true)//if the read has not completed PortStage.CheckRead();//check for read completion } else//an error occurred during the read call return false; //the strcmp() function returns 0 if the strings match. it returns // a positive value if they are different if(strcmp(PortStage.mInBuf,Response)) return false;//the read string did not match Response //the read was successful and the read string matched Response return true; } else//the stage was not first initialized return false; } double CStage::GetPosX() { double return_x; if(stageIsInit)//check to see if the stage has been initialized { //time structures to hold the start and current times struct timeb tGStart, tGCurrent; int tGDiff;//the difference between current and start time in millisec char PosHolder[40];//local variable to hold the returned position char Writer[40];//variable to hold string to be written //initialization of the write and position holder strings for(int temp = 0; temp < 40; temp++) { PosHolder[temp] = NULL; Writer[temp] = NULL; } //command to retrieve stage position issued by writing "P" sprintf(Writer, "Px%c", 13); //designates that the the read will be one byte at a time and will look // for the carriage return located at the end of the returned string sprintf(Response, "%c", 13); PortStage.mBytesToRead = 1; //write the command string to the stage if(WriteStage(Writer)) { ftime(&tGStart);//set the start time tGDiff = 0;//clear the time difference checker for timeout //keep reading until either the read byte is a carriage return // "" or a timeout occurrs. concatenate the read byte onto // the position holder string on each loop while(!ReadStage() && (tGDiff < tTimeOut)) { strcat(PosHolder,PortStage.mInBuf); ftime(&tGCurrent);//set the current time //calculate the time difference. the time parameter is // stored in seconds, and the millitm parameter is millisec tGDiff = (int) (1000.0 * (tGCurrent.time - tGStart.time) + (tGCurrent.millitm - tGStart.millitm)); } //if a timeout did not occur the xyzPos string should now hold // "X,Y,Z = currentX,currentY,0" if(tGDiff < tTimeOut){ return_x = atof(PosHolder); } else//if the loop ended due to a timeout fprintf(stderr, "ERROR: TIMEOUT: could not get stage position\n"); } else//the write was unsuccessful fprintf(stderr, "ERROR: WRITE UNSUCCESSFUL: could not get stage position\n"); } else//the stage was not first initialized fprintf(stderr, "ERROR: STAGE NOT INITIALIZED: could not get stage position\n"); return return_x;//return the position double } double CStage::GetPosY() { double return_y; if(stageIsInit)//check to see if the stage has been initialized { //time structures to hold the start and current times struct timeb tGStart, tGCurrent; int tGDiff;//the difference between current and start time in millisec char PosHolder[40];//local variable to hold the returned position char Writer[40];//variable to hold string to be written //initialization of the write and position holder strings for(int temp = 0; temp < 40; temp++) { PosHolder[temp] = NULL; Writer[temp] = NULL; } //command to retrieve stage position issued by writing "P" sprintf(Writer, "Py%c", 13); //designates that the the read will be one byte at a time and will look // for the carriage return located at the end of the returned string sprintf(Response, "%c", 13); PortStage.mBytesToRead = 1; //write the command string to the stage if(WriteStage(Writer)) { ftime(&tGStart);//set the start time tGDiff = 0;//clear the time difference checker for timeout //keep reading until either the read byte is a carriage return // "" or a timeout occurrs. concatenate the read byte onto // the position holder string on each loop while(!ReadStage() && (tGDiff < tTimeOut)) { strcat(PosHolder,PortStage.mInBuf); ftime(&tGCurrent);//set the current time //calculate the time difference. the time parameter is // stored in seconds, and the millitm parameter is millisec tGDiff = (int) (1000.0 * (tGCurrent.time - tGStart.time) + (tGCurrent.millitm - tGStart.millitm)); } //if a timeout did not occur the xyzPos string should now hold // "X,Y,Z = currentX,currentY,0" if(tGDiff < tTimeOut){ return_y = atof(PosHolder); } else//if the loop ended due to a timeout fprintf(stderr, "ERROR: TIMEOUT: could not get stage position\n"); } else//the write was unsuccessful fprintf(stderr, "ERROR: WRITE UNSUCCESSFUL: could not get stage position\n"); } else//the stage was not first initialized fprintf(stderr, "ERROR: STAGE NOT INITIALIZED: could not get stage position\n"); return return_y;//return the position double }