/***************************************************************************** - - - POLONY SEQUENCER ACQUISITION SUITE - - Church Lab - - Harvard Medical School - - - - Free software for running a Polony Sequencing automated microscope - - - - ========================================================================= - - - - - - cscope.cpp - - - - Interface class to Nikon TE2000 - - - - Written by Michelle Kuykendal, 08-10-2005 - - - - Revised - - 02-15-2006 GP; fixed filter cube functions and added Uniblitz trigger - - - - 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: cscope.cpp **** **** Requirements: cscope.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 CScope class to be **** **** used with a Nikon Eclipse TE2000-E microscope. it enables the user **** **** to set, confirm and get the scope position, to set and get the **** **** filter block changover address, and to initialize and close the **** **** scope. it also provides functions to write to and read from the **** **** scope 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 "cscope.h" //definition of the scope 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 CScope::CScope(Reporter* rep){ r = rep; scopeIsInit = 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 zPos[temp] = NULL; //to hold the current x,y,z position blockAddr[temp] = NULL;//to hold the filter block address } zmin = 0; //minimum z position allowed zmax = 200000; //maximum z position allowed addrmin = 1; //minimum filter block address allowed addrmax = 6; //maximum filter block address 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: ~CScope() is the destructor for the scope class. //RETURN VALUE: none. CScope::~CScope() { } //REQUIREMENTS: a call to Init() should be made prior to this call for // proper execution, however, a failure will not occur otherwise. a call // to ConfirmPosition() should always be made following a call to // SetPosition(). //USE: SetPosition() will check that the z values passed to it are within a // valid range for the scope movement. next, the function will write to the // scope to set its position to z. it will then read from the scope until // it receives a confirmation response that the command was received. //RETURN VALUE: the return will be false if the scope was not previously // initialized, if the passed variable z is outside of the valid range, or // if the WriteScope() call is unsuccessful. otherwise, the return will // be true if the scope is already initialized, the z variable is within // valid range, and the write and read were successful. bool CScope::SetPosition(double z) { int z_insteps = (int) (z / 0.05); if(scopeIsInit)//check to see if the scope has been initialized { char Writer[40];//variable to hold string to be written for(int temp = 0; temp < 40; temp++)//initialization of write string Writer[temp] = NULL; //zmax and zmin are defined as private variables in the scope class. //if z is outside of the valid range, return false, otherwise assign // z to the private variable, setz. if((z_insteps > zmax) || (z_insteps < zmin)) return false; //command to set scope position issued by writing "fSMVz" sprintf(Writer, "fSMV%i%c", z_insteps, 13); //expected response from the scope is "qSMVA" to confirm command // was received properly. check for first character only. sprintf(Response, "qSMVA%c%c", 13, 10); PortScope.mBytesToRead = 7;//read length in bytes //write the command string to the scope if(WriteScope(Writer)) { if(!ReadScope())//the read was unsuccessful { //read until NULL terminator to clear port sprintf(Response, "\0"); while(!ReadScope()); return false; } } else//the write was unsuccessful { //read until NULL terminator to clear port sprintf(Response, "\0"); while(!ReadScope()); return false; } return true;//everything was successful } else//the scope 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 read from the scope until it receives the // code pertaining to the actual execution of the SetPosition() call. if // the first byte read is "o" then the scope has successfully moved to its // location and the function will now read the rest of the response from // the buffer. if an occurred, the function will still read the rest // of the buffer to clear it. //RETURN VALUE: the return will be false if the stage was not previously // initialized, or if the WriteStage() call is ever unsuccessful. otherwise // the return will be true when the stage is in the desired location as was // specified by the call to SetPosition(). bool CScope::ConfirmPosition() { if(scopeIsInit)//check to see if the scope has been initialized { //should read from the input buffer. if it is "oSMV", then the // scope has properly moved to its location designated by the // SetPosition() call. sprintf(Response, "oSMV%c%c",13,10); PortScope.mBytesToRead = 6;//read length in bytes if(!ReadScope()) { //read until NULL terminator to clear port sprintf(Response, "\0"); while(!ReadScope()); return false; } return true;//the scope is now in the expected location } else//the scope was not first initialized return false; } bool CScope::OpenUniblitz(){ char Writer[40];//variable to hold string to be written for(int temp = 0; temp < 40; temp++){//initialization of write string Writer[temp] = NULL; } sprintf(Writer, "cDSC7%c", 13); sprintf(Response, "oDSCA%c%c", 13, 10); PortScope.mBytesToRead = 7;//read length in bytes //write the command string to the scope if(WriteScope(Writer)){ if(ReadScope()){;} else//if the single byte read not "q", which indicates error { //read until NULL terminator to clear port sprintf(Response, "\0"); while(!ReadScope()){;} fprintf(stderr, "ERROR: CScope.OpenUniblitz(): scope did not return correct string.\n"); return false; } } else//the write was unsuccessful { //read until NULL terminator to clear port sprintf(Response, "\0"); while(!ReadScope()){;} fprintf(stderr, "ERROR: CScope.OpenUniblitz(): write was unsuccessful.\n"); return false; } return true;//the uniblitz is now open } bool CScope::CloseUniblitz(){ char Writer[40];//variable to hold string to be written for(int temp = 0; temp < 40; temp++){//initialization of write string Writer[temp] = NULL; } sprintf(Writer, "cDSC8%c", 13); sprintf(Response, "oDSCA%c%c", 13, 10); PortScope.mBytesToRead = 7;//read length in bytes //write the command string to the scope if(WriteScope(Writer)){ if(ReadScope()){;} else//if the single byte read not "q", which indicates error { //read until NULL terminator to clear port sprintf(Response, "\0"); while(!ReadScope()){;} fprintf(stderr, "ERROR: CScope.CloseUniblitz(): scope did not return correct string.\n"); return false; } } else//the write was unsuccessful { //read until NULL terminator to clear port sprintf(Response, "\0"); while(!ReadScope()){;} fprintf(stderr, "ERROR: CScope.CloseUniblitz(): write was unsuccessful.\n"); return false; } return true;//the uniblitz is now closed } bool CScope::SetFilterBlock(int addr) { if(scopeIsInit)//check to see if the scope has been initialized { char Writer[40];//variable to hold string to be written for(int temp = 0; temp < 40; temp++){//initialization of write string Writer[temp] = NULL; } //addrmax and addrmin are defined as private variables in the class. //if addr is outside of the valid range, return false. if((addr > addrmax) || (addr < addrmin)){ fprintf(stderr, "ERROR: CScope.SetFilterBlock(): invalid block number.\n"); return false; } //command to set block position issued by writing "fHDMaddr" sprintf(Writer, "cHDM0%i%c", addr, 13); //expected response from the scope begins with "q" to confirm command // was received properly. check for first character only. sprintf(Response, "oHDM%c%c", 13, 10); PortScope.mBytesToRead = 6;//read length in bytes //write the command string to the scope if(WriteScope(Writer)){ //read from the stage. return will be true if the byte read is "q". if(ReadScope()){ //sprintf(Response, "oHDM%c%c",13,10); //PortScope.mBytesToRead = 6;//read length in bytes //if(!ReadScope()){ // //read until NULL terminator to clear port // sprintf(Response, "\0"); // while(!ReadScope()){;} // return false; //} } else//if the single byte read not "q", which indicates error { //read until NULL terminator to clear port sprintf(Response, "\0"); while(!ReadScope()){;} fprintf(stderr, "ERROR: CScope.SetFilterBlock(): scope did not return correct string.\n"); return false; } } else//the write was unsuccessful { //read until NULL terminator to clear port sprintf(Response, "\0"); while(!ReadScope()){;} fprintf(stderr, "ERROR: CScope.SetFilterBlock(): write was unsuccessful.\n"); return false; } return true;//the block is now in the expected location } else//the scope 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: GetFilterBlock() will write to and read from the scope to get the // current block position. it will assign the position to a string to // be returned. //RETURN VALUE: the return will be a string containing the current scope // position in the format, "Filter Block Address = addr" if the call // is successful. the string will contain "Could not get filter block // address." if the scope was not previously initialized, or if the // WriteScope() call is unsuccessful. char *CScope::GetFilterBlock() { if(scopeIsInit)//check to see if the scope 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 block address issued by writing "rHAR" sprintf(Writer, "rHAR%c", 13); //should read four bytes from the input buffer. if four are "aHAR", // then scope has properly returned the block addr in following byte sprintf(Response, "aHAR"); PortScope.mBytesToRead = 4;//read length in bytes //write the command string to the scope if(WriteScope(Writer)) { //if the read returns an unexpected value, return false. if(!ReadScope()) return false; //otherwise, now read in one byte at a time and look for the // carriage return at the end of the returned string sprintf(Response, "%c", 10); PortScope.mBytesToRead = 1;//read length in bytes ftime(&tGStart);//set the start time tGDiff = 0;//clear the time difference checker for timeout //while the read returns a value that is not the carriage return // or a timeout has not yet occurred keep reading and concatenate // the read byte onto the position holder variable while(!ReadScope() && (tGDiff < tTimeOut)) { strcat(PosHolder,PortScope.mInBuf); ftime(&tGCurrent);//get 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 zPos string should now // hold "Filter block address = addr" if(tGDiff < tTimeOut) sprintf(blockAddr, "Filter block address = %s", PosHolder); else//the loop ended due to a timeout sprintf(blockAddr, "Could not get filter block address."); } else//the write was unsuccessful { sprintf(blockAddr, "Could not get filter block address."); sprintf(Response, "\0");//read until NULL terminator to clear port while(!ReadScope()); } } else//the scope was not first initialized sprintf(blockAddr, "Could not get filter block address."); return blockAddr;//return the address string } //REQUIREMENTS: the scope device should be connected to comm port 3 //USE: Init() will first check to be sure the scope has not already been // initialized. it will then open and setup comm port 3. one command // will be sent to the scope to set it to fine mode (25um/rotation). //RETURN VALUE: the return will be false is the scope was already // initialized or if the write or read failed. the return will // be true if the scope was not already initialized, the comm port was // opened successfully, and the initialization command was properly sent // and executed. bool CScope::Init() { if(!scopeIsInit)//only initialize the scope if it's not already initalized { scopeIsInit = true;//set the initialization checker to true sprintf(PortScope.mPort, "COM3");//COM3 will be opened if(PortScope.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 scope will be the string // "oSJS" for the following command sprintf(Response, "oSJS%c%c", 13, 10); PortScope.mBytesToRead = 6;//read length in bytes //command to set the scope to fine mode (25um/rotation) sprintf(Writer, "cSJS2%c", 13); if(WriteScope(Writer)) { //if the six byte read does not match the Response string // assigned above then an error occurred if(!ReadScope()) { Close();//close the comm port (stdout, "ERROR in CScope:Init() --> scope did not return expected 6 bytes (oSJSCRLF)\n"); return false; } } else//the write was unsuccessful { fprintf(stdout, "ERROR in CScope:Init() --> write was unsuccessful (CScope:WriteScope returned FALSE)\n"); Close();//close the comm port return false; } return true;//the initialization was successful } else//the port was not opened fprintf(stdout, "ERROR in CScope:Init() --> port was not opened (CPort:OpenCPort returned FALSE)\n"); return false; } else//the scope 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 scope was first intialized. it // will then close the comm port that was opened during the call to Init(), // and reset the scope initialization checker to false. //RETURN VALUE: if the port was successfully closed, the return will be // true. otherwise,the return will be false. bool CScope::Close() { if(scopeIsInit)//only close the port if it was previously initialized { if(PortScope.CloseCPort())//close the comm port { scopeIsInit = false;//reset scope initialization check to false return true; } else//the port was not successfully closed return false; } else//the scope was not previously 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: WriteScope() will check to see that the scope 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 scope has not been first // initialized or the WriteCPort() call returned false due to an error. the // return will be true if the scope is already intialized and the write is // successful. bool CScope::WriteScope(char *ToWrite) { if(scopeIsInit)//check to see if the scope has been initialized { //set the port output buffer to the string that will be written sprintf(PortScope.mOutBuf, "%s", ToWrite); if(PortScope.WriteCPort())//write the output buffer to the port { while(PortScope.mResWrite != true)//if the write has not completed PortScope.CheckWrite();//check for completion } else//an error occurred during the write return false; return true;//the write was successful } else//the scope 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 PortScope.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 scope. if it is not first // set, the function will return false. //USE: ReadScope() will check to see that the scope 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 scope 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 scope is already intialized and if what is // read is the same as the Response string bool CScope::ReadScope() { if(scopeIsInit)//check to see if the scope has been initialized { if(PortScope.ReadCPort())//issue the comm port read command { while(PortScope.mResRead != true)//if the read has not completed PortScope.CheckRead();//check for read completion } else{//if an error occurred during the read call fprintf(stdout, "ERROR in CScope:ReadScope() --> error occurred during read (CPort:ReadCPort() returned FALSE)\n"); return false;//if an error occurred during the read call } //the strcmp() function returns 0 if the strings match. it returns // a positive value if they are different if(strcmp(PortScope.mInBuf,Response)){ // fprintf(stdout, "ERROR in CScope:ReadScope() --> wrong value returned by scope (CPort.mInBuf != CScope.Response; ->%s<- ->%s<-)\n", PortScope.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 scope was not first initialized fprintf(stdout, "ERROR in CScope:ReadScope() --> scope was not initialized (scopeIsInit==FALSE)\n"); return false; } } double CScope::GetPos() { if(scopeIsInit){//check to see if the scope 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 scope position issued by writing "rSPR" sprintf(Writer, "rSPR%c", 13); //should read four bytes from the input buffer. if four are "aSPR", // then scope has properly returned the position in following bytes sprintf(Response, "aSPR"); PortScope.mBytesToRead = 4;//read length in bytes //write the command string to the scope if(WriteScope(Writer)){ //if the read returns an unexpected value, return false. if(!ReadScope()){ fprintf(stdout, "ERROR in CScope:GetPos() --> scope did not report proper data (response does not start w/ aSPR)\n"); return false; } //otherwise, now read in one byte at a time and look for the // carriage return at the end of the returned string sprintf(Response, "%c", 10); PortScope.mBytesToRead = 1;//read length in bytes ftime(&tGStart);//set the start time tGDiff = 0;//clear the time difference checker for timeout //while the read returns a value that is not the carriage return // or a timeout has not yet occurred keep reading and concatenate // the read byte onto the position holder variable while(!ReadScope() && (tGDiff < tTimeOut)){ strcat(PosHolder,PortScope.mInBuf); ftime(&tGCurrent);//get 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 zPos string should now // hold "Z = currentZ" if(tGDiff < tTimeOut){ sprintf(zPos, "%s", PosHolder); } else{//the loop ended due to a timeout sprintf(zPos, "Could not get scope position."); fprintf(stdout, "ERROR in CScope:GetPos() --> TIMEOUT\n"); } } else{//the write was unsuccessful sprintf(zPos, "Could not get scope position."); sprintf(Response, "\0");//read until NULL terminator to clear port while(!ReadScope()){;} } } else{//the scope was not first initialized sprintf(zPos, "Could not get scope position."); fprintf(stdout, "ERROR in CScope:GetPos() --> scope was not initialized (CScope.scopeIsInit==FALSE)\n"); } return ( (atof(zPos) * 0.05) );//return the position cast as a double }