// cmmos.cpp : Defines the initialization routines for the DLL.

#include "stdafx.h"
#include <afxdllx.h>
#include <afxtempl.h>
#include "execlist.h"
#if GETBUILDVERSION >= 5000
#include "interfacGlobals.h"
#endif
#include "partprog.h"
#include "tipdata.h"
#include <stdio.h>
#include "interfac.h"
#include "globals.h"
#include "cmmos.h"
#include "ini.h"
#include "executeo.h"
#include "daxis.h"
#include "resource.h"
#include "tab.h"
#include "CmmosStylus.h"
#include "Generic_mapping.h"
#include "Loadprob.h"
#include "interface_version.h"

#if GETBUILDVERSION <= 5500
//class CBitVector NONE = CBitVector(); //needed to link .... //  [9/21/2010 Michel]		
#define INTERFAC_EXTERN
#endif

#pragma message ("Building " MACHINE_INTERFACE_ORIGINAL_FILENAME ". Version: " MACHINE_INTERFACE_FILE_VERSION_STR)
#pragma warning (disable :4100)
#pragma warning (disable :4189)


#define SECTION_HEADER _T("CMMOS")

#define PNAME_ERROR_RESET "PrObEnAmErEsEtAfTeRsToPoReRrOr"
// 40x 50ms [9/28/2010 Michel]
#define TIME_TO_RECEIVE_QUALDATA 40

#pragma warning(disable:4996) // accept swscanf and co .... [5/30/2007 Michel]

#if GETBUILDVERSION <=5500
class CBitVector NONE = CBitVector(); //needed to link .... 
#endif

/*
Version  Date        Who  Description
-------      --------         ---     -----------
1.35	 10/aug/12  MP   PCD- xxxx	  Sarrelouis : with probe changer, 2nd arm probe data after change not properly updated -> sent.
1.34     18/apr/12  MP	 PCD-4135     Sarrelouis: on DSE, if AB angle not exactly nominal(only DSE),values become biased .... +solved other issue in PCDMIS 
									  for tip recalculations which are not needed since we do not have a real mapping.+ prevented CAAWrist to be 
									  considered manual (why that ????).
1.33     15/jun/11  MP   Tk# 110643   added forcing special learn mode off.
1.32     02/mar/11  MP   PR# 271616   Reopened - error in line 1688 condition for single arm mode not tested, dual arm selected probe overwritten. 
1.31     09/Feb/11  MP   PR# 271616   Probe tips on arm2 need to be forwarded to arm1 - force mechanism + force send on received diameter.
1.30     30/Nov/10  RK   PR# 270867   PCDMIS core 2011 allows USER_CMMOS and debug registry options can be moved to USER areas from ADMIN.
1.29     29/Nov/10  RK   PR# 270807   added driver_add_entries() to see CMMOS in SettingsEditor, removed INIReplaceDefaults and only SaveDefaults is used.
1.28     28/oct/10  MP   PR# 270459   fixed PQ answer from RO updating could desync further measure points -> 99999999 results
1.27     13/sep/10  MP   PR# 270031   dualarm  refactor / review / test with simu of tool change process.
1.26     12/sep/10  MP   PR# 270031   dualarm  
1.25     05/sep/10  MP   PR# 270031   dualarm, no toolchanger, config not loaded ! 
1.24     30/sep/10  MP   PR# 268220   dualarm, be notified of probe name changes      
1.23     27/sep/10  MP   Task# 108379 Use of CMM/OS on manual machines + Zeiss "CMM-OS counter" SP 4.6.
1.22     23/sep/10  MP   PR #269706  made sure capitalize applies too to probe rack change.
1.21     23/sep/10  MP   PR #269706  checked values in inches + relative moves.
1.20     20/feb/10  MP	 WIP Ford Saarlouis - PR# 266366 manage tip offsets inside ... ->UsesTipOffsets setting TRUE/FALSE
						 + reading probeconfig with position (user changes config behind PC-DMIS back) 
						 + ConfigSelectionStrategy setting, default 0 for older systems,1 doesn't change tip on moving cursor. 
1.19     14/jan/10  RK   Debug manager support
1.18     19/Jun/09  MP   Ford: crash on find hole mono arm 
1.17     17/Jun/09  MP   Ford: #262002 fixed desync when CMM-OS goes on executing a cmd while we think it's killed + changes
                                in the read_position feedback , setting ABC on globalcommanded position.
1.16     10/Jun/09  MP   Ford: #262001 filter out tip changes for other arm (based on Leitz code)!
1.15     09/Jun/09  MP   Ford: Add man speed to be restored when GoManual is called.+ remove path for probe name #262001
1.14     27/Mar/09  MP   Ford: Enable/disable Zeiss log in finishInitialize() +circular move changed from PositionOnCylinder to PositionOnSphere.     
1.13     22/Jan/09  MP   Ford: Tool changer implementation
1.12     21/Jan/09  MP   Ford: corrected desync and exception on find hole + cleanup + tag for move between T600 and T699 
1.11     05/Dec/08  MP   Ford: special functions opcodes 150 to 155 to manipulate CMM/OS CSY to and from PC-DMIS
                         use of move with report MZx....T666 -> CS then MZSY6xx when executed + buffered moves.
                         Circular move.
1.10     22/Aug/08  MP   Ford Niehl/Koeln new setting + attempt to buffer moves via MZ command
1.09     21/Aug/08  MP   Ford Niehl/Koeln added rule for capitalization (2)=> number in 1st pos-> don;'t touch. if not capitalize except 1st char.
                          + change of CAA Wrist cmm/os and PCDMIS probe sync strategy.
1.08     07/Sep/06  MP   VW Wolfsburg  using the "global_is_iplusplus_type_cmm" with 4.1 and no measured offset nulling (CAD issue)
1.07     10/Mar/05  MP   EDAG : fixed a sync prob + correct connection to manual MZ1060
1.06     09/Mar/05  MP   Further tests by EDAG (DCC and MANUAL mode)
1.05     09/Feb/05  MP   Further tests by BMW Dingo 
1.04     09/Dec/03  MP   Further tests by Daimler Sifi 
1.03     08/Dec/03  MP   First test in Esslingen 
1.02     16/Nov/03  MP   Port to unicode vor V35/36/40
1.01     ??/??/??  TE   Initial version

*/

#define MIN_TCPOPENDELAY 100
#define MIN_CTRLTCPTIMEOUT 500

#define MOVES_COMPLETE_STRING  _T("MovesComplete")
//  [12/5/2008 Michel]
#define CIRC_COMPLETE_STRING   _T("MovesCirDone")
#define PH9_COMPLETE_STRING    _T("HeadDone")
#define NO_SEND_FILENAME       _T("dontsend.ini")

// V1.34 refactor [4/18/2012 Michel]
#define MAX_DSE_DELTA_ALLOWED  (0.5)
void AssignAngles(double*);

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

#define  MACHINE_ID_STRING  (m_pMachine ? m_pMachine->m_id : _T("<undefined>"))
#define  SET_PART_PROGRAM_POINTER    m_pPartProgram=m_pMachine->m_pPartProgram

static AFX_EXTENSION_MODULE cmmosDLL = { NULL, NULL };
//  [12/9/2003]
CPoint3 RefOffset;

CStringList StringList;
CStringList ErrorList;

CMachineInterface *m_pMachine = NULL;
CPCDpart_program *m_pPartProgram = NULL;
CPCDinterface *active_call_back = NULL;
//CList < CPCDinterface *, CPCDinterface * > call_back_list;

CmmosStylusList StylusList;

int scan_can_recover = FALSE;
int needs_home = FALSE;
double cur_move_speed = 0.0;
double cur_touch_speed = 0.0;
double cur_search_dist = 0.0;
CString cur_probe_name;
double max_speed = 0.0;
/************************************************************************/
/*	Greg Guilbeau - 2011/07/28                                          */
/*	The following line(s) have been modified to handle x64 conversion   */
/************************************************************************/
/*  UINT ManualPositionUpdate = 0;  */
UINT_PTR ManualPositionUpdate = 0;
SOCKET my_socket = INVALID_SOCKET;
int serious_tcp_error = FALSE;
CString in_string;
CString out_string;
CString LastSentString;
/************************************************************************/
/*	Greg Guilbeau - 2011/07/28                                          */
/*	The following line(s) have been modified to handle x64 conversion   */
/************************************************************************/
/*  UINT TCPTimeout_id = 0;  */
UINT_PTR TCPTimeout_id = 0;
int TCPIP_port = 0;
int ReadoutInterval = 0;
int TCPOpenDelay = 0;
int CtrlTCPTimeout = 0;
CString cmm_ip_address;
double last_rotab = 0.0;
int ConsecutiveIdenticalErrors = 0;
BOOL ErrorRecoveryInProgress = FALSE;
BOOL ErrorLoadingCmmosProbeConfig = FALSE;
BOOL FirstWaitEnd = TRUE;
BOOL WaitEndPending = FALSE;
BOOL BufferedMoves = TRUE;
BOOL ExchangeABAxes = TRUE;
BOOL FlipAAxis = TRUE;
BOOL FlipBAxis = TRUE;
BOOL UseCmmosCalData = TRUE;
BOOL RunCmmosCal = TRUE;
//  [12/8/2003]
BOOL bHideCmmOsDialog = TRUE;
BOOL bUseCaaWrist = TRUE;
BOOL bIgnoreZAZZ = FALSE;
//  [2/9/2005]
double current_man_retract = 0.0;
BOOL bOneShotMoveUnbuffered = FALSE; //  [8/20/2008 Michel]
//  [12/2/2008 Michel]
BOOL bInListingWPS = FALSE;
CStringList WpsList;
BOOL bWpStorageOK = FALSE;
CTransform WpStorage;
CPoint3 LastKnownPosition;
//  [9/28/2010 Michel]
int LastSelectedTipNumber = -1;
//  [9/30/2010 Michel]
CString sLastProbeFromChangeTool;
BOOL bWaitingForPQDone = false;
BOOL bWaitingForTipDiameters = false;//  [1/28/2011 Michel]

//////////////////////////
CString PanelcodeDone;
CString PanelcodeEraseHit;
CString PanelcodeInsertMove;
CString PanelcodeStop;

enum
{
  MODE_MANUAL, MODE_DCC, MODE_UNKNOWN
};
int CurrentMachineMode = MODE_UNKNOWN;

int MachinePresent;
int MachineHomed;
int HeadIsIndexable;
int HeadHomed;
int ProbeChangerPresent;
int ProbeCurrentlyLoaded;
int ProbeRackPresent;
int NotUsed;
int RotaryTablePresent;
int RotaryTableHomed;
//  [2/9/2005]
int TipNameCapRule;
//  [3/9/2005]
int ManualMachineType;
BOOL bNoErrorOnProbeNotFound;
BOOL bNoTipSelected = TRUE;
BOOL bSilentError = FALSE;
double myversion;
//  [8/22/2008 Michel]
BOOL  bUseMZForMove = TRUE;
int   ExtDebug = 0; // bitmapped to trace more into the debug .... [12/5/2008 Michel]
BOOL bUseCirc = TRUE;
//  [1/22/2009 Michel]  tool changer implementation
BOOL bUseToolChanger = TRUE;
CString sToolInHeadName;
CString sRequestedToolName;
BOOL bToolChangeInProgress = FALSE;
CString sCmmosNoToolName; // at ford was not_defined
CString sUnloadToolName;
BOOL bTcAllowUnload = FALSE;
//  [6/9/2009 Michel]
double man_speed = 100.0;
//  [6/16/2009 Michel]
CArray <int> valid_move_tag_array;
static BOOL debugUseDebugManager = TRUE;
// [2/15/2010 Michel]
BOOL bUsesTipOffsets = TRUE;
int ConfigSelectionStrategy = 0;
//  [10/11/2010 Michel]
int dummy = 1;
//  [2/9/2011 Michel]
int AllowTipErrorMessages = 0;
//  [4/18/2012 Michel]

static INI_INTERFACE_INFO ini_info[] =
{
  { _T("TcpipPort"),           INI_INT,     & TCPIP_port,         _T( "8888") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("TcpipIPAddress"),      INI_CSTRING, & cmm_ip_address,     _T( "127.0.0.1") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
#if GETBUILDVERSION >= 5500 // 2011 -- PR# 270867 (USER_CMMOS allowed by PCDMIS core)
  { _T("DebugLog"),          INI_BOOL,    & debugLog,             _T("FALSE") , NOFUNCTION, USER, ENABLED, ENABLED },
  { _T("DebugFile"),         INI_CSTRING, & debugFilename,        DEFAULT_DEBUGFILENAME , NOFUNCTION, USER, ENABLED, ENABLED },
  { _T("DebugPosReport"),    INI_BYTE,    & debugPosReports,     _T("0") , NOFUNCTION, USER, ENABLED, ENABLED },
  { _T("DebugCPU"),          INI_BYTE,    & debugCPU,            _T("3") , NOFUNCTION, USER, ENABLED, ENABLED },
  { _T("DebugCMM"),          INI_BYTE,    & debugCMM,            _T("3") , NOFUNCTION, USER, ENABLED, ENABLED },
  { _T("DebugWindow"),       INI_BOOL,    & debugWindow,         _T("FALSE") , NOFUNCTION, USER, ENABLED, ENABLED },
  { _T("DebugLogReset"),     INI_BOOL,    & debugLogReset,       _T("FALSE"), NOFUNCTION, USER, ENABLED, ENABLED },
  { _T("DebugTitle"),        INI_CSTRING, & debugTitle,          DEFAULT_DEBUGTITLE , NOFUNCTION, USER, ENABLED, ENABLED },
  { _T("UseDebugManager"),   INI_BOOL,    & debugUseDebugManager, _T("TRUE"), NOFUNCTION, USER, ENABLED, ENABLED },
#else
  { _T("DebugLog"),            INI_BOOL,    & debugLog,            _T("FALSE") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("DebugFile"),           INI_CSTRING, & debugFilename,       DEFAULT_DEBUGFILENAME , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("DebugPosReport"),      INI_BYTE,    & debugPosReports,    _T( "0") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("DebugCPU"),            INI_BYTE,    & debugCPU,            _T("3") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("DebugCMM"),            INI_BYTE,    & debugCMM,            _T("3") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("DebugWindow"),         INI_BOOL,    & debugWindow,         _T("FALSE") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("DebugTitle"),          INI_CSTRING, & debugTitle,          DEFAULT_DEBUGTITLE , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("DebugLogReset"),       INI_BOOL,    & debugLogReset,   _T("FALSE") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("UseDebugManager"),     INI_BOOL,    & debugUseDebugManager, _T("TRUE"), NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
#endif
  { _T("Version"),             INI_DOUBLE,  & myversion,           _T("0.0") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("AxisX"),               INI_BYTE,    & axis[3],            _T( "0") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("AxisY"),               INI_BYTE,    & axis[4],             _T("2") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("AxisZ"),               INI_BYTE,    & axis[5],             _T("4") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("MaxSpeed"),            INI_DOUBLE,  & max_speed,           _T("50.0") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("ManSpeed"),            INI_DOUBLE,  & man_speed,           _T("100.0") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("ReadoutInterval"),     INI_INT,     & ReadoutInterval,     _T("500") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("TCPOpenDelay"),        INI_INT,     & TCPOpenDelay,        _T("500") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("CtrlTCPTimeout"),      INI_INT,     & CtrlTCPTimeout,      _T("8000") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("BufferedMoves"),       INI_BOOL,    & BufferedMoves,       _T("TRUE") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("ExchangeABAxes"),      INI_BOOL,    & ExchangeABAxes,      _T( "TRUE") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("FlipAAxis"),           INI_BOOL,    & FlipAAxis,           _T("TRUE") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("FlipBAxis"),           INI_BOOL,    & FlipBAxis,           _T("TRUE") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("UseCmmosCalData"),     INI_BOOL,    & UseCmmosCalData,     _T("TRUE") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("RunCmmosCal"),         INI_BOOL,    & RunCmmosCal,         _T( "TRUE") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("PanelcodeDone"),       INI_CSTRING, & PanelcodeDone,       _T("F1") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("PanelcodeEraseHit"),   INI_CSTRING, & PanelcodeEraseHit,   _T("F2") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("PanelcodeInsertMove"), INI_CSTRING, & PanelcodeInsertMove, _T("TWPoint") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("PanelcodeStop"),       INI_CSTRING, & PanelcodeStop,       _T("F4") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("HideCmmOsDialog"),     INI_BOOL,    & bHideCmmOsDialog,    _T("TRUE") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("UseCaaWrist"),         INI_BOOL,    & bUseCaaWrist   ,     _T("TRUE") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("TipNameCapRule"),      INI_INT ,    & TipNameCapRule ,     _T("1")  , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("ManualMachineType"),   INI_INT ,    & ManualMachineType,   _T("0")  , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("NoErrorOnProbeNotFound"), INI_BOOL, & bNoErrorOnProbeNotFound,   _T("FALSE")  , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("UseMZForMove"),       INI_BOOL,    & bUseMZForMove,       _T("TRUE")  , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("ExtDebug"),           INI_INT,     & ExtDebug,             _T("0")  , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("UseCircularMoves"),    INI_BOOL,   & bUseCirc,             _T("TRUE")  , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("UseToolChanger"),      INI_BOOL,   & bUseToolChanger,      _T("FALSE")  , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("CmmOsNoToolName"),     INI_CSTRING,& sCmmosNoToolName,     _T("not_defined")  , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("TcAllowUnload"),       INI_BOOL,   & bTcAllowUnload,       _T("FALSE")  , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("UsesTipOffsets"),        INI_BOOL,    &bUsesTipOffsets       ,     _T("TRUE") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("ConfigSelectionStrategy"),    INI_INT,    &ConfigSelectionStrategy ,_T("0") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { _T("AllowTipErrorMessages"),  INI_INT,    &AllowTipErrorMessages ,_T("0") , NOFUNCTION, ADMINISTRATOR, ENABLED, ENABLED },
  { NULL,                  INI_END,     NULL,                  NULL }
};

void MyWriteCommBlock(NPCOMMINFO np, const TCHAR* str, int response = TRUE);
int SendNext(int got_response = 0);
BOOL set_cmmos_active_probe(int add_callback = FALSE);
void check_probe_data_ok();
void capitalize(CString& sTipName);
void SetCurrentTipValues(CString sAnswer);
void AddToWPList(CString sResponse);
void LoadWpStorage(CString sResponse);
BOOL SaveTransformToCmmOs(CString name, CTransform Csy);
BOOL SaveTransformToFile(CString Filename, CTransform Csy);
void SetupOptionsFromCmmOsVersion(CString sRevId);
void AddPointToString(CPoint3 pnt, CString& str, BOOL bSpaceAtStart);

void remove_path(CString& name); //  [6/16/2009 Michel]
void add_path(CString& name); //  [2/9/2011 Michel]
void SetAllProbesToBeSentToArm1(void); //  [2/9/2011 Michel]
void ForceTipStatusToChanged(CPCDtip_data* pTip); //  [2/9/2011 Michel]
void my_show_error(char* str);
void my_show_error(CString & str);
void update_pcdmis_tip_data(CPCDtip_data* tip, double tip_diameter,BOOL bForce);
BOOL NeedToRotate(double, double);// and following lines [2/17/2011 Michel]
void FillListFromFile(CString sFilename);
BOOL IsNameInList(CString MyName);
void SetCurrentTipValues(int tipnumber,double a,double b);

class Ccall_back { // from I++ //  [2/15/2010 Michel]
public:
	CPCDinterface* pInterface;
	CPCDtip_data* pTip;
	Ccall_back(CPCDinterface* i,CPCDtip_data* t)
	{
		pInterface=i;
		pTip = t;
	}
	Ccall_back()
	{
		pInterface = (CPCDinterface*)NULL;
		pTip = (CPCDtip_data*)NULL;
	}
	/************************************************************************/
	/*	Greg Guilbeau - 2012/06/12                                          */
	/*	The following line(s) have been modified to handle x64 conversion   */
	/************************************************************************/
	/*  	Ccall_back(int i,int t)
	{
		pInterface=(CPCDinterface*)i;
		pTip = (CPCDtip_data*)t;
	}  */
	Ccall_back(int i,int t)
	{
		pInterface=(CPCDinterface*)IntToPtr(i);
		pTip = (CPCDtip_data*)IntToPtr(t);
	}

};

CList < Ccall_back, Ccall_back > call_back_list; // replaces CPCDInterface* // from I++ [2/15/2010 Michel]
CList <CString,CString>  DontSendList; //  [2/17/2011 Michel]

void CallbackListAddTail(CPCDinterface* ptr,CPCDtip_data* pTip,CString text = CString(L"")); // added the tip we need to store now. [2/15/2010 Michel]
Ccall_back CallbackListRemoveHead(CString text = CString(L""));
void CallbackRemoveAll(CString text = CString(L""));
//  [6/17/2009 Michel]
void CmmOsSelectTip(int tip); //refactoring

//////////////////////////////////////////////////////////////////////////
//  [9/23/2010 Michel]
double to_metric(double v_in)
{
  return v_in * (25.4 / global_units); 
}
double to_native(double v_in)
{
  return v_in / (25.4 / global_units); 
}
CPoint3 to_metric(CPoint3 pt_in)
{
  return pt_in * (25.4 / global_units); 
}
CPoint3 to_native(CPoint3 pt_in)
{
  return pt_in / (25.4 / global_units); 
}
// refactoring a bit [9/28/2010 Michel]
void mapHeadAngles(double& a,double& b)
{
  if (FlipAAxis)
    a *= -1.0;
  if (FlipBAxis)
    b *= -1.0;
  if (ExchangeABAxes)
  {
    double tmpdbl = b;
    b = a;
    a = tmpdbl;
  }
}

//////////////////////////////////////////////////////////////////////////
 //  [6/16/2009 Michel]
void ValidMoveTagArrayAdd(int tag);
void ValidMoveTagArrayRemoveAll(void);
BOOL IsMoveTagValid(int tag);

//////////////////////////////////////////////////////////////////////////
// [6/17/2009 Michel]
void CmmOsSelectTip(int tipnumber)
{
  CString tmpstr;
  tmpstr.Format(_T("AON%d\r\n"), tipnumber);
  MyWriteCommBlock(controller, tmpstr);
}
//////////////////////////////////////////////////////////////////////////
//  [2/17/2011 Michel]  

BOOL IsCurrentProbe(CPCDload_probes* pProbe, BOOL bCheckAnglesToo = FALSE, CPCDtip_data* pTip = NULL)

{
  BOOL bCurrent = FALSE;
  if (pProbe)
  {
    CString pcd_probe_name = pProbe->file_name;
    remove_path(pcd_probe_name);
    if (pcd_probe_name == cur_probe_name)
    {
      if (bCheckAnglesToo && pTip)
      {
        double a = pTip->get_ph9a();
        double b = pTip->get_ph9b();
        bCurrent = ! NeedToRotate(a,b); // current == no need to turn head !
      }
      else
      {
        bCurrent = TRUE;
      }
    }
  }
  return(bCurrent);
}
//////////////////////////////////////////////////////////////////////////
//  [2/16/2010 Michel]  copied from I++ client
//  [4/18/2012 Michel]  V1.34 refactor:  angles transmitted to PCDMIS core should match the ones used to AddTipOffset 
//						if not, we will have a precision issue: Added offsets are nominal from tip, 
//						while subtracted offsets in PCD are based on the transmitted angle wrist calculation. We need to ensure angles are the same
//						Probe tip position being always correct (CMMOS/I++). If real angle far from theoretical, we keep real to authorize
//						wrist tip automatic insertion.

void AssignAngles(double* pReturnedAngles)
{
	if (pReturnedAngles)
	{
		pReturnedAngles[0]=lastph9[0];  // "returned" if no global active tip or at least 1 current values is too different from nominal.
		pReturnedAngles[1]=lastph9[1];
		pReturnedAngles[2]=lastph9[2];

		if (global_active_tip)
		{
			// need to allow recognize the probe attitude change .... 
			if ((fabs(lastph9[0] - global_active_tip->get_ph9a()) <= MAX_DSE_DELTA_ALLOWED)
				&& (fabs(lastph9[1] - global_active_tip->get_ph9b()) <= MAX_DSE_DELTA_ALLOWED)
				&&(fabs(lastph9[2] - global_active_tip->get_ph9c()) <= MAX_DSE_DELTA_ALLOWED))  //we're inside the allowed range
			{
				pReturnedAngles[0]=global_active_tip->get_ph9a();
				pReturnedAngles[1]=global_active_tip->get_ph9b();
				pReturnedAngles[2]=global_active_tip->get_ph9c();
			}
			else
			{
				debug.SendAndFlush(_T("CMM=%s AssignAngles: detecting large tip attitude change (or no global_active_tip) lastph9 values returned\n"), MACHINE_ID_STRING);
			}

		}
	}
}

//////////////////////////////////////////////////////////////////////////
//  [2/16/2010 Michel]  copied from I++ client

void AddTipOffset(CPoint3& MyPoint, CPCDtip_data* Tip) //  [10/1/2009 Michel]
{
	debug.SendAndFlush(_T("CMM=%s AddTipOffset input point : %lf %lf %lf\n"), MACHINE_ID_STRING, MyPoint[0],MyPoint[1],MyPoint[2]);

	if (Tip && bUsesTipOffsets)
	{
		int i;
		CPoint3 offset = Tip->get_meas_xyz();
		debug.SendAndFlush(_T("CMM=%s AddTipOffset applying meas offset: %lf %lf %lf\n"), MACHINE_ID_STRING, offset[0],offset[1],offset[2]);
		CPoint3 FastOffset = Tip->get_meas_xyz_fast();
		debug.SendAndFlush(_T("CMM=%s AddTipOffset -info- fastoffset: %lf %lf %lf)\n"), MACHINE_ID_STRING, FastOffset[0],FastOffset[1],FastOffset[2]);

		for (i = 0; i < 3; i++)
			MyPoint[i] += offset[i];
	}
  else
  {
	debug.SendAndFlush(_T("CMM=%s AddTipOffset ********* NOT TIP (NULL) ***********\n"), MACHINE_ID_STRING);
  }
	debug.SendAndFlush(_T("CMM=%s AddTipOffset on exit : %lf %lf %lf\n"), MACHINE_ID_STRING, MyPoint[0],MyPoint[1],MyPoint[2]);

}
void SubTipOffset(CPoint3& MyPoint, CPCDtip_data* Tip) //  [10/1/2009 Michel]
{
	debug.SendAndFlush(_T("CMM=%s SubTipOffset input point : %lf %lf %lf\n"), MACHINE_ID_STRING, MyPoint[0],MyPoint[1],MyPoint[2]);

	if (Tip && bUsesTipOffsets)
	{
		int i;
		CPoint3 offset = Tip->get_meas_xyz();
		for (i = 0; i < 3; i++)
			MyPoint[i] -= offset[i];
	}
	debug.SendAndFlush(_T("CMM=%s SubTipOffset on exit : %lf %lf %lf\n"), MACHINE_ID_STRING, MyPoint[0],MyPoint[1],MyPoint[2]);
}
void CheckTipStrategyOnce(void) //  [10/5/2009 Michel]
{
	//idea is to test when global_active_tip is not null if adding offsets is performed.
	// when this is case, we must switch on the substract / adding of tip offsets ....
	static BOOL bTestNotYetPerformed = TRUE; // just do it once.
	if (bTestNotYetPerformed)
	{
		if (global_active_tip)
		{
			CPoint3 dummy; //init at 0.0,0.0,0.0
			AddTipOffset(dummy, global_active_tip);
			if (fabs(dummy[0])< 1e-3 && fabs(dummy[1])< 1e-3 && fabs(dummy[2])< 1e-3) //point not changes, old core program
			{
				bUsesTipOffsets = FALSE;
			}
			else
			{
				bUsesTipOffsets = TRUE;
			}
			bTestNotYetPerformed = FALSE;
		}
	}
}

//////////////////////////////////////////////////////////////////////////
//  [6/16/2009 Michel]  valid_move_tag handling
void ValidMoveTagArrayRemoveAll(void)
{
  valid_move_tag_array.RemoveAll();
  TRACE(L"----- valid move tag array cleared \n");
}
void ValidMoveTagArrayAdd(int tag)
{
  valid_move_tag_array.Add(tag);
  /************************************************************************/
  /*	Greg Guilbeau - 2011/07/28                                          */
  /*	The following line(s) have been modified to handle x64 conversion   */
  /************************************************************************/
  /*    int count = valid_move_tag_array.GetCount();  */
  INT_PTR count = valid_move_tag_array.GetCount();
  TRACE(L"----- valid move tag %d added to array, count = %d \n",tag,count);
}
BOOL IsMoveTagValid(int tag)
{
  BOOL retvalue = FALSE;
  /************************************************************************/
  /*	Greg Guilbeau - 2011/07/28                                          */
  /*	The following line(s) have been modified to handle x64 conversion   */
  /************************************************************************/
  /*    int count = valid_move_tag_array.GetCount();  */
  INT_PTR count = valid_move_tag_array.GetCount();
  for (int i=0; i<count; i++)
  {
    if (tag == valid_move_tag_array[i])
    {
      valid_move_tag_array.RemoveAt(i);
      retvalue = TRUE;
      break;
    }
  }
  if (retvalue)
    TRACE(L"----- tag %d is valid , removed \n",tag);
  else
    TRACE(L"-----*****  tag %d not found in valid tag array \n",tag);

  return retvalue;
}
// checks if current tool is the one in head, then issue change command//////////////////////////////////////////////////////////////////////////
//  [1/22/2009 Michel]
// checks if current tool is the one in head, then issue change command
void ChangeProbe(CString sProbeName)
{
  CString sCurrentToolName;
  CString sCommand;
  sCurrentToolName = sProbeName;


  if (!sProbeName.IsEmpty()) // don't check for tool name, always request.
  {
    sRequestedToolName = sCurrentToolName;
    if (sRequestedToolName != sToolInHeadName)
    {
      // special case for unload probe
      if (bTcAllowUnload && (0 == instr(sCurrentToolName,sUnloadToolName))) // use TU instead of GetProbexxxx
      {
        sCommand = _T("TU\r\n");
      }
      else
      {
        sCommand.Format(_T("GetProbeFromRackWithProbeName(%s)\r\n"),sCurrentToolName);
      }

      MyWriteCommBlock(controller,sCommand,FALSE);
      Sleep(200);
      MyWriteCommBlock(controller,CString(_T("SYChangeProbeDone\r\n")));
      bToolChangeInProgress = TRUE;
    }
  }
}

void ChangeProbeReset(void)
{
  bToolChangeInProgress = FALSE;
  sRequestedToolName.Empty();
  if (bUseToolChanger)
  {
    MyWriteCommBlock(controller,L"AC\r\n"); //AC answer will update the sToolInHeadName
  }
}
void ChangeProbeDone(void) // called when SYChangeProbeDone is received
{
  sToolInHeadName = sRequestedToolName;
  ChangeProbeReset();
}
//////////////////////////////////////////////////////////////////////////
//[2/17/2011 Michel]

void FillListFromFile(CString sFilename)
{
  CString sOneLine;
  BOOL bNotEof=FALSE;
  try
  {
    CStdioFile input(sFilename,CFile::modeRead);
    do 
    {
     bNotEof = input.ReadString(sOneLine);
     if (! sOneLine.IsEmpty())
     {
      DontSendList.AddTail(sOneLine);
     }
    } while (bNotEof);
  input.Close();
  }
  catch (...)
  {
  }
}
BOOL IsNameInList(CString MyName)
{
  BOOL bIsInList = FALSE;
  try
  {
    int i=MyName.Find(L".PRB");
    if (-1 != i) //remove .PRB
    {
      MyName = MyName.Left(i);
    }
    bIsInList = (NULL != DontSendList.Find(MyName));
  }
  catch (...)
  {
  }
  return bIsInList;
}

//////////////////////////////////////////////////////////////////////////
//  [1/21/2009 Michel]

int GetNextSyTag(void) // returns a consecutive  number from 00 to 99
{

  static int SyTag = 0;
  if (99 <= SyTag)
  {

    SyTag = 0;
  }
  return SyTag++ ;
}

//////////////////////////////////////////////////////////////////////////
//  [12/4/2008 Michel] updated for list change [2/16/2010 Michel]

void CallbackListAddTail(CPCDinterface* ptr,CPCDtip_data* pTip, CString text)
{
  Ccall_back cb(ptr,pTip);
  call_back_list.AddTail(cb);
  /************************************************************************/
  /*	Greg Guilbeau - 2011/07/28                                          */
  /*	The following line(s) have been modified to handle x64 conversion   */
  /************************************************************************/
  /*    int cnt = call_back_list.GetCount();  */
  INT_PTR cnt = call_back_list.GetCount();
  if(ExtDebug  & 1)
  {
    CString diag;
    diag.Format(L"CMM=%s >>----- Added: interf=0x%lx pTip=0x%lx cnt=%ld ",MACHINE_ID_STRING,ptr,pTip,cnt);
    diag += text + L"\n\r";
    debug.SendAndFlush(diag);
  }
}

Ccall_back CallbackListRemoveHead(CString text )
{
  /************************************************************************/
  /*	Greg Guilbeau - 2011/07/28                                          */
  /*	The following line(s) have been modified to handle x64 conversion   */
  /************************************************************************/
  /*    int cnt = call_back_list.GetCount();  */
  INT_PTR cnt = call_back_list.GetCount();
  Ccall_back cb;
  cb = call_back_list.RemoveHead();

  if(ExtDebug  & 1)
  {
    CString diag;
    diag.Format(L"CMM=%s ----->> Removed: interf=0x%x pTip=0x%x cnt=%ld  Removed:",MACHINE_ID_STRING,cb.pInterface,cb.pTip,cnt);
    diag += text + L"\n\r";
    debug.SendAndFlush(diag);
  }
  return cb;
}
void CallbackListRemoveAll(CString text )
{
  call_back_list.RemoveAll();
  if(ExtDebug  & 1)
  {
    CString diag;
    diag.Format(L"CMM=%s ----- Callback list cleared :",MACHINE_ID_STRING);
    diag += text + L"\n\r";
    debug.SendAndFlush(diag);
  }
}
//////////////////////////////////////////////////////////////////////////
 void ForceTipStatusToChanged(CPCDtip_data* pTip)
 {
  if (pTip)
  {
    double CurrentDiam = pTip->get_meas_diam();
    pTip->set_meas_diam(-1.0); //force the changed status in the tip 
    pTip->set_meas_diam(CurrentDiam); // "

  }

 }
//
//-------------------------------------------------------------------
//

void SetCurrentTipValues(CString sAnswer)
//extracts probe diameter value and set current tip property - for CAA WRISTS !
{
  CString tmpstr;
  int itmp;
  double diameter = 0.0;
  CPCDtip_data *tip = NULL;
  CPoint3 Zero; //constructed as 0,0,0

  itmp = sAnswer.Find('D');
  if(-1 == itmp)
    return; // we have an error there !
  tmpstr = sAnswer.Mid(itmp+1);
  diameter = _ttof(tmpstr);
  
  diameter = to_native(diameter); //  [9/23/2010 Michel]

  debug.SendAndFlush(_T("CMM=%s executing SetCurrentTipValues, setting to %lf\n"), MACHINE_ID_STRING,diameter);

  if (global_active_tip)
  {
    tip = global_active_tip;
    if (tip)
    {
      update_pcdmis_tip_data(tip,diameter,TRUE);
    }
  }
}
void SetCurrentTipValues(int tipnumber,double a=0.0,double b=0.0)
//extracts probe diameter value and set current tip property - for CAA WRISTS !
{
  double diameter = 0.0;
  CPCDtip_data *tip = NULL;
  CmmosStylus* lpStylus = NULL;

  if(bUseCaaWrist || !global_has_ph9) // angles don;t matter in this case
  {
    lpStylus = StylusList.GetCmmosStylus(tipnumber);
  }
  else //we have a ph9 ....
  {
    lpStylus = StylusList.GetCmmosStylus(tipnumber,a,b);
  }
  if (lpStylus)
  {
    diameter = lpStylus->GetDiameter();
  }

  if (global_active_tip)
  {
    tip = global_active_tip;
    if (tip)
    {
      debug.SendAndFlush(_T("CMM=%s executing SetCurrentTipValues, setting PCDMIS tip %ls to %lf\n"), MACHINE_ID_STRING,tip->get_id(),diameter);
      update_pcdmis_tip_data(tip,diameter,TRUE);
    }
  }
}
//  [12/4/2008 Michel]
void SetupOptionsFromCmmOsVersion(CString sRevId)
{
  ; // tbd
}
void send_move_speed(double MyNewSpeed) // refactoring [6/9/2009 Michel]
{
  if (MyNewSpeed >100.0)
  {
    MyNewSpeed = 100.0;
  }
  if (MyNewSpeed <=0.0)
  {
    MyNewSpeed = 20.0;
  }

  CString tmpstr;
  if (cur_move_speed != MyNewSpeed)
  {
    cur_move_speed = MyNewSpeed;
    tmpstr.Format(_T("MS%lf\r\n"), MyNewSpeed);
    MyWriteCommBlock(controller, tmpstr);
  }
}
//
void capitalize(CString& sTipName)
{

  CString str = sTipName;

  switch(TipNameCapRule)
  {
  case 3: 
	  str.MakeLower();
	  if (0 == (str.Left(3).CompareNoCase(_T("CZR"))))
	  {
		  str.SetAt(0,L'C');
		  str.SetAt(1,L'Z');
		  str.SetAt(2,L'R');
	  }
	  break;	

  case 2: // check for number in 1st pos. If true, don't touch. else apply rule as 1 //  [8/20/2008 Michel]
      {
      if ((str[0] >= L'0' && str[0]<=L'9'))
        {
         break;
        }
      } //here we fall thru case 1 !!!! (no break)

  case 1: // put a Capital on the 1st letter, and keep all other lower case
    // except if 3 starting letters are CZR then capitalise the 3 !!!
      {
      
      str.MakeLower();
      if (0 == (str.Left(3).CompareNoCase(_T("CZR"))))
      {
        str.SetAt(0,L'C');
        str.SetAt(1,L'Z');
        str.SetAt(2,L'R');
      }
      else // just 1st letter to cap ...
      {
        wchar_t c = str[0];

        if (c >= L'A' && c<=L'z') //  [8/20/2008 Michel] // fixed it : order is ABC..Zabc...z and not a to Z !!! [2/22/2010 Michel]
        {
          str.SetAt(0,_toupper(c));
        }
      }
      break;
    }
  default: // if 0 or undefined, do not change the string ...
    break;
  }
  sTipName = str;
}
void remove_path(CString& my_name)
{
  // ford: dual arm, arm 2 sees pname as being "C:\xxxx\yyyy\4R" instead of 4R -> remove path [6/9/2009 Michel]
  int last_slash = my_name.ReverseFind(L'\\');
  if (-1 != last_slash)
  {
    my_name = my_name.Mid(last_slash+1);
  }
}

void add_path(CString& my_name)
{
#if 5400 <= GETBUILDVERSION
  my_name = GetDirectoryFromFilePath(m_pPartProgram) + my_name;
#endif
}
void check_probe_data_ok()
{
  if(ErrorLoadingCmmosProbeConfig)
  {
    ErrorLoadingCmmosProbeConfig = FALSE;
    set_cmmos_active_probe();
  }
}

//
//-------------------------------------------------------------------
//
BOOL set_cmmos_active_probe(int add_callback)
{
  CString tmpstr;
  CString pname;
  BOOL bProbeChanged = FALSE;

  if ((!UseCmmosCalData) && (current_command == DRIVER_CALIBRATE_PROBE))
    return bProbeChanged;
  CString sElement; 
  
   CPCDload_probes* pPrb = NULL;
   pPrb = global_active_probe;

   if (global_active_probe && !global_in_relay_mode)  //mono arm only
  {

    // if in dual arm, do NOT react on changes of active probe for the other arm
    BOOL bProbeIsValid = FALSE;
    bProbeIsValid = global_active_probe->IsExecutedByArm(m_pMachine->m_number); //  [6/10/2009 Michel]
    if (!bProbeIsValid)
    {
      debug.SendAndFlush(L"CMM = %s ---- set_cmmos_active_probe: Active probe ignored, not for this arm\n ",MACHINE_ID_STRING);
      return bProbeChanged;
    }
    else
    {
      debug.SendAndFlush(L"CMM = %s ---- set_cmmos_active_probe: Active probe used ...\n ",MACHINE_ID_STRING);
    }

    if (pname.IsEmpty ())
    {
      bNoTipSelected = TRUE;
      pname = global_active_probe->file_name;
    }

    //  [10/5/2010 Michel]

   }
   else // usually so far dual arm .... 
   {
     debug.SendAndFlush(L"CMM = %s ---- set_cmmos_active_probe: Slave arm mode, setting pname as %ls\n ",MACHINE_ID_STRING
       ,sLastProbeFromChangeTool);

     pname = sLastProbeFromChangeTool;
   }



   remove_path(pname); // on arm2 double arm mode, probe name becoming C:\xxxxx\xxxxx\probename -> not found [6/9/2009 Michel]
  
   capitalize(pname); //  [2/9/2005] //-moved here [10/5/2010 Michel]

  // If there was an error loading the cmmos data for this probe there are two main
  // causes for this with each having a different recovery scenario
  // to recover from this
  // 1) The user picked the PC-DMIS probe they wanted but it doesn't exist (or at least
  //    not properly) within cmmos.  In this case they should go into cmmos and create the
  //    probe or whatever is required to correct the problem.  Then they should go into
  //    the PC-DMIS probe utilities and click on measure to invoke driver_calibrate_probe.
  //    That procedure will clear the ErrorLoadingCmmosProbeConfig flag, run the cmmos
  //    automated cmmos calibration procedure if requested and then update the PC-DMIS
  //    probe data to correspond to this.
  // 2) The user accidentally picked a probe that is not what they really want to use and
  //    it doesn't exist in cmmos (and isn't supposed to.  In this case, once they select
  //    the probe they really want to use we will see a name change so clear the flag and
  //    try to get the data for this new probe name.

  //  if ((pname != cur_probe_name) && (cur_probe_name == PNAME_ERROR_RESET)) //  [12/9/2003]
  //    ErrorLoadingCmmosProbeConfig = FALSE;
  //
   debug.SendAndFlush(L"CMM = %s ---- set_cmmos_active_probe: pname= %ls cur_probe_name= %ls bTCinprogress %d ErrorLoadding %d\n ",MACHINE_ID_STRING
     ,pname,cur_probe_name,bToolChangeInProgress,ErrorLoadingCmmosProbeConfig);


  if (!pname.IsEmpty() && (pname != cur_probe_name) && !ErrorLoadingCmmosProbeConfig && !bToolChangeInProgress)// added TC test [1/22/2009 Michel]
  {
    cur_probe_name = pname;
    bProbeChanged = TRUE;
    if (bUseToolChanger)
    {
      ChangeProbe(pname);
    }
    else
    {
      tmpstr.Format(_T("LC%s\r\n"), pname);
      MyWriteCommBlock(controller, tmpstr);
    }
//       bWaitingForTipDiameters = TRUE; // to allow  TE answer to update the PCDMIS probe file [1/28/2011 Michel]
//       MyWriteCommBlock(controller, _T("GX\r\n"));
  }
  bWaitingForTipDiameters = TRUE; // to allow  TE answer to update the PCDMIS probe file [1/28/2011 Michel]
  MyWriteCommBlock(controller, _T("GX\r\n"));

  return(bProbeChanged);
}

BOOL NeedToRotate(double machine_a,double machine_b) //  [2/17/2011 Michel]
{
BOOL bRotate = FALSE;
if ((fabs(lastph9[0] - machine_a) > MIN_PH9_ANGLE_DIFF) || (fabs(lastph9[1] - machine_b) > MIN_PH9_ANGLE_DIFF))
{
  bRotate = TRUE;
}

debug.SendAndFlush(L"CMM = %s Need to Rotate to %lf %lf returns %x \n",MACHINE_ID_STRING
                   , machine_a,machine_b,bRotate);
return (bRotate);
}

void update_cmmos_tip_number() //  [8/21/2008 Michel]
{
  CPCDtip_data* myTip = NULL;
  CString tmpstr;
  BOOL slave = global_in_relay_mode; // we just act on move ph9, global_active_tip not reliable in this mode [6/17/2009 Miche
 
  if (!slave && global_active_tip && !bToolChangeInProgress && IsCurrentProbe(global_active_probe,TRUE,global_active_tip)) // added TC state [1/22/2009 Michel]
  { 
    int tipnumber = global_active_tip->get_tip();
    if((!CString(myTip->get_id()).IsEmpty()) && !(0 == tipnumber))//  [6/17/2009 Michel]
    {
      CmmOsSelectTip(tipnumber);
    }
  }
}

//
//-------------------------------------------------------------------
//
void LoadWpStorage(CString sResponse)
{
  debug.SendAndFlush(_T("CMM=%s LoadWpStorage: parsing %s\n"), MACHINE_ID_STRING,sResponse);
  CString sElement;
  double MyValue = 0.0;
  bWpStorageOK = FALSE;
  int CurPos = 0;
  CPoint3 Origin;
  for (int i=0;i<2;i++) // LoadWPSystem + name of alignment
  {
    sElement = sResponse.Tokenize(L" ",CurPos);
    if (sElement.IsEmpty())
      goto Done;
  }
  for (int i=0;i<3;i++) // rotation matrix
  {  
    for(int j=0;j<3;j++)
    {
      sElement = sResponse.Tokenize(L" ",CurPos);
      if (sElement.IsEmpty())
        goto Done;
      VERSION_INDEPENDENT_STSCANF(sElement, L"%lf", &MyValue);
      WpStorage.m_data[i+1][j] = MyValue;
    }
  }

  for (int i=0;i<3;i++) // read CMM/OS origin - TRANS then ROT system
  {
    sElement = sResponse.Tokenize(L" ",CurPos);
    if (sElement.IsEmpty())
      goto Done;
    VERSION_INDEPENDENT_STSCANF(sElement, L"%lf", &MyValue);
    Origin[i] = MyValue;
  }
  // recalc origin in new CSY - PCDMIS 1st rotate then translates, while CMM/OS 1st trans then Rotate
  WpStorage.aaback(ROTONLY,&Origin[0],&Origin[0]);
  WpStorage.m_data[0][0] = Origin[0];
  WpStorage.m_data[0][1] = Origin[1];
  WpStorage.m_data[0][2] = Origin[2];

  bWpStorageOK = TRUE;

Done:; 
  debug.SendAndFlush(_T("CMM=%s LoadWpStorage: Done, Status = %d\n"), MACHINE_ID_STRING,bWpStorageOK);

}


void AddToWPList(CString sResponse) //  [12/2/2008 Michel]
{
  TRACE(sResponse);
  if (instr(sResponse,_T( "ListWPS")) == 0) // start of list, end of line is 1st argument
  {
    bInListingWPS = TRUE; // will send next line to this routines
    CString sFirstWps;
    sFirstWps = sResponse.Mid(14); //line = ListWPSystems + blank + 1st CSY (MKS)
    WpsList.AddTail(sFirstWps); 
    TRACE ("Adding to List: ",sFirstWps);
    debug.SendAndFlush(_T("CMM=%s AddToWPList: Adding %s\n"), MACHINE_ID_STRING,sFirstWps);
  }
  else if (L"TE" == sResponse) // end of list
  {
    debug.SendAndFlush(_T("CMM=%s AddToWPList: received TE, end of adding to liost\n"), MACHINE_ID_STRING);
    bInListingWPS = FALSE;
  }
  else
  {
    TRACE ("Adding to List: ",sResponse);
    WpsList.AddTail(sResponse);
    debug.SendAndFlush(_T("CMM=%s AddToWPList: Adding %s\n"), MACHINE_ID_STRING,sResponse );
  }
}
//
//-------------------------------------------------------------------
//
void update_pcdmis_tip_data(CPCDtip_data* tip, double tip_diameter,BOOL bForce=FALSE)
{
  TCHAR time[50], date[50];
  GetDateFormat(
    LOCALE_USER_DEFAULT,  // locale for which date is to be formatted
    DATE_SHORTDATE,       // flags specifying function options
    NULL,                 // date to be formatted (NULL causes use of local system date)
    NULL,                 // date format string (NULL causes use of format for specified locale)
    date,                 // buffer for storing formatted string
    50                    // size of buffer
    );
  GetTimeFormat(
    LOCALE_USER_DEFAULT,    // locale for which time is to be formatted
    LOCALE_NOUSEROVERRIDE,  // flags specifying function options
    NULL,                   // time to be formatted (NULL reads local system time)
    NULL,                   // time format string (NULL uses the system locale format)
    time,                   // buffer for storing formatted string
    50                      // buffer size
    );
  if (tip)
  {
    tip->set_meas_diam(tip_diameter);
    tip->set_date((const TCHAR *) date);
    tip->set_time((const TCHAR *) time);
    if(bForce)
      ForceTipStatusToChanged(tip);// in case the diameter didn't change, still make sure it's seen as new [2/9/2011 Michel]
  }
}

void update_pcdmis_probe_data()
{
  CPCDtip_data *tip = NULL;
  CmmosStylus *lpStylus = NULL;
  double machine_a;
  double machine_b;
  double TheDiameter = 0.0;
  BOOL bTheDiameterSet = FALSE;
  BOOL ProcessingFirstMatchingTip = FALSE;
  int tipnumber = 0;

  CPCDload_probes* pMyProbe = NULL;
  CString MyProbeName;

  debug.SendAndFlush(_T("CMM=%s executing update_pcdmis_probe_data\n"), MACHINE_ID_STRING);
  
  pMyProbe = global_active_probe;

  if (global_in_relay_mode && bUseToolChanger) //just for arm2
  {
	  debug.SendAndFlush(_T("CMM=%s driver_notify_tool in Arm2: taking care of changed probe .... \n"),MACHINE_ID_STRING);

	  // V1.35 code added for 2nd arm, to set the active probe - always was one loaded during last session  ....  [8/10/2012 Michel]

	  pMyProbe = new CPCDload_probes(m_pPartProgram, NONE, sLastProbeFromChangeTool);
	  pMyProbe->load_tips();
	  

  }	

  if (NULL == pMyProbe)
  {
    debug.SendAndFlush(_T("CMM=%s update_pcdmis GlobalActPrb null, **Assuming double arm**\n"), MACHINE_ID_STRING);

    // usually double arm : retrieve probe file and update it ... 
    pMyProbe = get_last_used_probe_for_arm(m_pMachine->m_number);

	CString sProbeNameForVerification;
	if (pMyProbe)
	{
		sProbeNameForVerification = pMyProbe->file_name;
		debug.SendAndFlush(_T("CMM=%s update_pcdmis get_last_used_probe_for_arm gave back %s\n"), MACHINE_ID_STRING,sProbeNameForVerification);
	}

  }
  MyProbeName = pMyProbe ? pMyProbe->file_name : CString("--NONE--");

  debug.SendAndFlush(_T("CMM=%s update_pcdmis GlobalActPrb = %x Being updated = %x = %s\n"), MACHINE_ID_STRING,global_active_probe,pMyProbe,MyProbeName);
  //
//  if (global_active_probe)
  if (pMyProbe)
  {
//    tip = global_active_probe->start_tip;
#if (GETBUILDVERSION < 5500)
    tip = pMyProbe->start_tip;
#else
    tip = pMyProbe->get_start_tip();
#endif
    while (tip)
    {
      tipnumber = tip->get_tip();
      machine_a = tip->get_ph9a(); //returns 0 for fixed head configs too.
      machine_b = tip->get_ph9b();
      mapHeadAngles(machine_a,machine_b);

      if(bUseCaaWrist || !global_has_ph9) // angles don;t matter in this case
      {
        lpStylus = StylusList.GetCmmosStylus(tipnumber);
      }
      else //we have a ph9 ....
      {
        lpStylus = StylusList.GetCmmosStylus(tipnumber,machine_a, machine_b);
      }

      if (NULL == lpStylus)
      {
        CString MyError;
        if (global_has_ph9)
        {
        MyError.Format(L"Cannot find CMMOS stylus for angles %lf %lf and tip #%d",machine_a,machine_b, tipnumber);
        }
        else
        {
          MyError.Format(L"Cannot find CMMOS stylus for tip #%d",tipnumber);
        }
        if (AllowTipErrorMessages)
        {
          my_show_error(MyError);
        }
      }
      else
      {
        double pcd_tip_diam = tip->get_meas_diam();
        double cmmos_tip_diam = lpStylus->GetDiameter();
        cmmos_tip_diam = to_native(cmmos_tip_diam); //  [9/23/2010 Michel]
        update_pcdmis_tip_data(tip,cmmos_tip_diam,TRUE);
        debug.SendAndFlush(_T("CMM=%s update_pcdmis1 tip updated to %lf\n"), MACHINE_ID_STRING,cmmos_tip_diam);
      }
      tip = tip->next;
    } //while tip


    // now save the tips 
    pMyProbe->save_tips();
//    pMyProbe->load_tips();

    if (global_in_relay_mode) //just for arm2
   {
    /************************************************************************/
    /*	Greg Guilbeau - 2011/07/28                                          */
    /*	The following line(s) have been modified to handle x64 conversion   */
    /************************************************************************/
    /*      int cnt = global_changed_probe_files[0].GetCount();
    int cnt2 = global_changed_probe_files[1].GetCount();  */
    INT_PTR cnt = global_changed_probe_files[0].GetCount();
    INT_PTR cnt2 = global_changed_probe_files[1].GetCount();
    global_changed_probe_files[0].RemoveAll();
    CString sMyFile;
    sMyFile = pMyProbe->file_name;
    add_path(sMyFile);
    global_changed_probe_files[0].AddTail(sMyFile);
    debug.SendAndFlush(_T("CMM=%s update_pcdmis_probe_data: sending %s to Arm1\n"), MACHINE_ID_STRING,sMyFile);
    pMyProbe->get_string(sMyFile);
    }
   }//probe valid
  debug.SendAndFlush(_T("CMM=%s update_pcdmis_probe_data: exit\n"), MACHINE_ID_STRING);
}


//
//-------------------------------------------------------------------
//

void go_dcc()
{
	if (CurrentMachineMode != MODE_DCC)
	{
		CPoint3 CurPosition;
		check_probe_data_ok(); //  [12/8/2003]
#ifdef BUILDVERSION_LESS_THAN_43
		CurPosition = global_interface_position;
		global_commanded_position = CurPosition;
#else
#pragma message( "Building for V43 and up ")
		CurPosition = global_interface_position.GetPoint(m_pMachine);
		global_commanded_position.SetPoint(m_pMachine,CurPosition);
#endif

		CurrentMachineMode = MODE_DCC;
		if (!ManualMachineType)
		{
			ValidMoveTagArrayRemoveAll(); //  [6/16/2009 Michel]
			MyWriteCommBlock(controller, _T("BI\r\n"));
		}
	}
}

//
//-------------------------------------------------------------------
//
void go_manual()
{
	if (CurrentMachineMode != MODE_MANUAL)
	{
		check_probe_data_ok(); //  [12/8/2003]
		CurrentMachineMode = MODE_MANUAL;
		if (!ManualMachineType)
		{
			MyWriteCommBlock(controller,_T( "EI\r\n"));
		}

	}
}


//
//-------------------------------------------------------------------
//


void my_show_error(CString & str)
{
  int ShowErrorReturnCode = 0;

  if (!bSilentError)
  {
#if GETBUILDVERSION >= 5400 // 2010MR1
    bool showShutDownButton = false;
	  ShowErrorReturnCode = show_error(m_pMachine, str, scan_can_recover, -1, showShutDownButton);
#else
    ShowErrorReturnCode = show_error(m_pMachine, str, scan_can_recover);
#endif

    if (0 == ShowErrorReturnCode)
    {
      debug.SendAndFlush(_T("CMM=%s Cannot automatically recover from error\n"), MACHINE_ID_STRING);
    }
    else
      debug.SendAndFlush(_T("CMM=%s Automatically recover from error\n"), MACHINE_ID_STRING);
  }
  else
  {
    debug.SendAndFlush(_T("CMM=%s Error %s was silenced !!! \n"), MACHINE_ID_STRING,str);
  }
  bSilentError = FALSE;
  scan_can_recover = 0;
  active_call_back = NULL;
  StringList.RemoveAll();
  ValidMoveTagArrayRemoveAll();

//   if (0 == ShowErrorReturnCode) // clear list only if error not handled nicely [1/21/2009 Michel]
//   {
    CallbackListRemoveAll(CString(L" MyShowError with return code != 0 "));
//   }
  if ((current_command != DRIVER_INITIALIZE) && (ConsecutiveIdenticalErrors <= 4))
  {
    Sleep(100);
    if (! ManualMachineType) // benchmark VW [9/27/2010 Michel]
    {
      MyWriteCommBlock(controller,_T( "ZC\r\n"));
    }
    MyWriteCommBlock(controller,_T( "EC\r\n"),FALSE); // test [6/16/2009 Michel] // added no need for response to this one. [2/22/2010 Michel]
  }
  current_command = 0;
  bWaitingForPQDone = FALSE; //  [10/28/2010 Michel] replaces the older PositionRequested (inverted logic)
  FirstWaitEnd = TRUE;
  WaitEndPending = FALSE;
  cur_move_speed = 0.0;
  cur_touch_speed = 0.0;
  cur_search_dist = 0.0;
  go_manual(); // 2010 doesn't call go_manual on cancel !!!! [2/22/2010 Michel]

  //  cur_probe_name = PNAME_ERROR_RESET; // moved to load controller_notify [12/9/2003]
  ChangeProbeReset();
  LastSelectedTipNumber = -1; //  [9/28/2010 Michel]
  return;
}

void my_show_error(UINT msgID)
{
  CString msg;
  msg.LoadString(msgID);
  my_show_error(msg);
  return;
}

void my_show_error(char* str)
{
  CString t;
  t = str;
  my_show_error(t);
}

//
//-------------------------------------------------------------------
//
void send_tcpip(const TCHAR* str)
{

  if (! str || ! _tcslen(str) || my_socket == INVALID_SOCKET)
    return;  // nothing to do

  out_string += str;
  char buffer[2000];
  int nTot = out_string.GetLength();

  for(int i=0;i<nTot;i++)
  {
    buffer[i]=((char)out_string[i])& 0xff;
  }

  int nRet = send(my_socket, buffer, nTot, 0);
  if (nRet == SOCKET_ERROR)
  {
    int WSAErr = WSAGetLastError();
    if (WSAErr != WSAEWOULDBLOCK)
    {
      serious_tcp_error = TRUE;
      CString rr;
      rr.Format(IDS_IFAC_TCPIP_ERROR, WSAErr, WSAErr);
      PCDMessageBox(m_pPartProgram, rr);
      return;
    }
  }
  else if (nRet < nTot)
  {
    out_string = out_string.Mid(nRet);
  }
  else
    out_string.Empty();
}

//
//-------------------------------------------------------------------
//
int SendNext(int got_response)
{
  /************************************************************************/
  /*	Greg Guilbeau - 2011/07/28                                          */
  /*	The following line(s) have been modified to handle x64 conversion   */
  /************************************************************************/
  /*    int errcnt;
  int strcnt;  */
  INT_PTR errcnt;
  INT_PTR strcnt;
  CString tmpstr;
  static clock_t wait_end;
loop:
  errcnt = ErrorList.GetCount();
  strcnt = StringList.GetCount();
  wait_end = 0;
  
  if (strcnt || errcnt)
  {
    debug.SendAndFlush(_T("CMM=%s String cnt: %d; Error cnt: %d;\n"), MACHINE_ID_STRING, strcnt, errcnt);
    CString s;
    if (ErrorList.GetCount())
      s = ErrorList.RemoveHead();
    else if (StringList.GetCount())
      s = StringList.RemoveHead();
    debug.SendAndFlush(_T("CMM=%s SendNext:%s\n"), MACHINE_ID_STRING, s);
    if (instr(s,_T( "WriteComm:") )== 0)
    {
      s = s.Mid(10);
      LastSentString = s;
      Sleep(50);
      //			TRACE(_T("------- sending %s count = %d %d\n"),s,errcnt,strcnt);
      debug.SendAndFlush(_T("CMM=%s %d>>>%s\n"), MACHINE_ID_STRING,strcnt,s);
      send_tcpip(s);
      got_response = 0;
      goto loop;
    }
    else if (instr(s,_T( "WaitEnd:")) == 0)
    {
      // Looking at our call_back_list is not a sufficient test.
      // For this interface the store_point for the move may happen as soon
      // as the move command is buffered.  We need to send a command that
      // won't respond until all moves are done and then let the response
      // for that command clear the WaitEndPending status
      if (FirstWaitEnd)
      {
        WaitEndPending = TRUE;
        // The RE command should echo back the specified string but it won't
        //  that's SY command actually !  [12/5/2003]
        // send it until all moves are complete so we can use this as a means
        // to do the WaitEnd
        debug.SendAndFlush(_T("CMM=%s FirstWaitEnd=TRUE, sending SY%s\n"), MACHINE_ID_STRING, MOVES_COMPLETE_STRING);
        FirstWaitEnd = FALSE;
        //        tmpstr.Format(_T("RE%s\r\n"), MOVES_COMPLETE_STRING);
        tmpstr.Format(_T("SY%s\r\n"), MOVES_COMPLETE_STRING); //  [12/5/2003]
        send_tcpip(tmpstr);
      }

      if (WaitEndPending)
      {
        // still some more moves so continue to wait
        if (errcnt)
          ErrorList.AddHead(_T("WaitEnd:"));
        else
          StringList.AddHead(_T("WaitEnd:"));
      }
      else
      {
        FirstWaitEnd = TRUE;
//         if (call_back_list.GetCount())
//         {
//           active_call_back = call_back_list.RemoveHead();
//         }
        if (active_call_back)
        {
          if (bWaitingForPQDone && call_back_list.GetCount()) //  [10/28/2010 Michel]
          {
            Ccall_back Cb;

            Cb = CallbackListRemoveHead(CString(L" Received PQ !") ); 
            bWaitingForPQDone = FALSE; //  [10/28/2010 Michel]
            CPCDinterface* inter = Cb.pInterface;
            if (inter)  
             {
				 double HeadAngles[3]={0.0,0.0,0.0};	
				 AssignAngles(HeadAngles);
			 debug.SendAndFlush(_T("CMM=%s Received PQ while waiting for PQDone, doing a store point on %x, lastph9 = %lf, %lf\n"), MACHINE_ID_STRING
					 ,inter,HeadAngles[0], HeadAngles[1]);
// old		 inter->store_point(NULL, NULL, last_rotab, lastph9);
			 inter->store_point(NULL, NULL, last_rotab, HeadAngles); // 1.34 [4/18/2012 Michel]
             if (active_call_back == inter)
                    active_call_back = 0;
             }
          }
          else
          {
            if (! (active_call_back->flagword & WAITENDONLY))
              go_manual();
            if (current_command == DRIVER_GO_MANUAL)
            {
              current_command = 0;
              debug.SendAndFlush(_T("CMM=%s call back after WaitEnd\n"), MACHINE_ID_STRING);
              active_call_back->store_point();
              active_call_back = NULL;
            }
          }
        }
        else
          go_manual();
        got_response = 0;
        goto loop;
      }
    }
    else if (instr(s,_T( "WaitResponse:")) == 0)
    {
      if (! got_response)
      { // continue waiting for a response
        if (errcnt)
          ErrorList.AddHead(_T("WaitResponse:"));
        else
          StringList.AddHead(_T("WaitResponse:"));
      }
      else
      {
        got_response = 0;
        goto loop;
      }
    }
    else if (instr(s, _T("Nap:")) == 0)
    {
      s = s.Mid(4); // s is number of milliseconds
      //      nap(_ttoi(s));
      Sleep(_ttoi(s));
      ; //  [3/9/2005]
      got_response = 0;
      goto loop;
    }
    else if (instr(s,_T( "Wait:")) == 0)
    {
      if (! wait_end)
      {
        CString t = s.Mid(5);
        wait_end = clock() + _ttoi(t) * CLOCKS_PER_SEC;
      }
      else if (clock() >= wait_end)
      {
        wait_end = 0;
        got_response = 0;
        goto loop;
      }
      if (errcnt)
        ErrorList.AddHead(s);
      else
        StringList.AddHead(s);
    }
    else if (instr(s,_T( "AskHome:")) == 0)
    {
      if (needs_home && PCDMessageBox(m_pPartProgram, IDS_MB_HOME_CMM, MB_OKCANCEL) != IDCANCEL)
      {
        // add commands to home the cmm
        MyWriteCommBlock(controller, _T("HC\r\n"));
        send_message(m_pMachine, IDS_SM_HOME_CMM);
      }
      else
      {
        got_response = 0;
        goto loop;
      }
    }
    else if (instr(s,_T( "WpsLoad:")) == 0)
    {
      CString sWps = s.Mid(8);
      if (! sWps.IsEmpty())
      {
        sWps = sWps.Trim();
        CString cmd;
        cmd = L"ReadWPSystem("+sWps+L")\r\n";
        MyWriteCommBlock(controller, cmd);
      }
    }

    else if (instr(s,_T( "SetCmmInitialState:")) == 0)
    {
      // hide possibly CMMOS windows
      if (bHideCmmOsDialog)
      {
        MyWriteCommBlock(controller, _T("VI0\r\n"),FALSE);//  [12/8/2003]
      }
      MyWriteCommBlock(controller, _T("SCMETRIC\r\n"));
      go_manual();
      // Turn on Manual Hit mode that reports vectors
      MyWriteCommBlock(controller, _T("RMP1\r\n"));
      // Switch to machine coordinates
      //      MyWriteCommBlock(controller, _T("CTP1\r\n")); //  [12/8/2003]
      //      MyWriteCommBlock(controller, _T("ResetWorkpieceSystem()\r\n"),FALSE);//  [12/8/2003]
      MyWriteCommBlock(controller, _T("CTP3\r\n"),FALSE); // [12/8/2003]
      // Enable function keys
      MyWriteCommBlock(controller, _T("PK1\r\n"));
      // list current WPS // V1.10 [12/2/2008 Michel]
      MyWriteCommBlock(controller,_T("ListWPSystems()\r\n"));
    }
    return TRUE;
  }
  return FALSE;
}

//
//-------------------------------------------------------------------
//
void AddString(const TCHAR* str)
{
  /************************************************************************/
  /*	Greg Guilbeau - 2011/07/28                                          */
  /*	The following line(s) have been modified to handle x64 conversion   */
  /************************************************************************/
  /*    int	iii = call_back_list.GetCount();
  int jjj = StringList.GetCount();  */
  INT_PTR	iii = call_back_list.GetCount();
  INT_PTR jjj = StringList.GetCount();

  CPCDinterface* ptr = NULL;
  if(iii)
  {
	  ptr = call_back_list.GetTail().pInterface;
  }
  StringList.AddTail(str);
  TRACE(_T("------ Sent %s, count = %d / callback count = %d obj = %x\n"),str,jjj,iii,ptr);
  if (StringList.GetCount() == 1 || instr(StringList.GetHead(), _T("WriteComm:")) == 0)
  {
    SendNext();
  }
}

//
//-------------------------------------------------------------------
//
void MyWriteCommBlock(NPCOMMINFO np, const TCHAR* str, int response)
{
  CString s;
  s.Format(_T("WriteComm:%s"), str);
  debug.SendAndFlush(_T("CMM=%s AddStringList:%s\n"), MACHINE_ID_STRING, s);
  AddString(LPCTSTR(s));
  if (response)
  {
    debug.SendAndFlush(_T("CMM=%s AddStringList:%s\n"), MACHINE_ID_STRING, "WaitResponse:");
    AddString(_T("WaitResponse:"));
  }
}

//
//-------------------------------------------------------------------
//
void finish_driver_init()
{
  //////////////////////////////////////////////////////////////////////////
  //test zone
//   CString MyProbeName(L"c:\\program files\\wai\\probe\\4r");
//   remove_path(MyProbeName);
//   remove_path(MyProbeName);
//   TRACE (MyProbeName);

  //////////////////////////////////////////////////////////////////////////
 
  debug.SendAndFlush(_T("CMM=%s finish_driver_init ! \n"), MACHINE_ID_STRING);
    current_command = DRIVER_INITIALIZE;

  //  [3/27/2009 Michel]   switches on/off Zeiss logging f(PC-DMIS debug log)
  CString logOnOffCommand;
  logOnOffCommand = debugLog ? _T("LN\r\n") : _T("LF\r\n"); //LN turns Zeiss Log on
  MyWriteCommBlock(controller, logOnOffCommand,FALSE); //answer to be ignored
  // ende [3/27/2009 Michel]

  if (!ManualMachineType)
  {
    MyWriteCommBlock(controller, _T("MissingBoreMode(1)\r\n"),FALSE);
  }
  MyWriteCommBlock(controller, _T("CH\r\n"));
  MyWriteCommBlock(controller, _T("RevNrCMMOS()\r\n"));
  if (bUseToolChanger)
  {
    MyWriteCommBlock(controller, _T("AC\r\n"));
  }
  MyWriteCommBlock(controller, _T("GX\r\n"));//  [9/27/2010 Michel]


  CString sDontSendFileName; 
  CString ProbesPath,szProbe;

  add_path(ProbesPath); // = set the probe path since ProbesPath is empty ;-)
  debug.SendAndFlush(_T("CMM=%s finish_driver_init directory to explore = %s \n"), MACHINE_ID_STRING,ProbesPath);
  
  // start by building the not-to-send list
  add_path(sDontSendFileName);
  sDontSendFileName += NO_SEND_FILENAME;
  FillListFromFile(sDontSendFileName);
 
  CFileFind finder;
  ProbesPath += _T("*.prb");

  BOOL bResult = finder.FindFile(ProbesPath);

  CPCDload_probes* pTestProbe = new CPCDload_probes(m_pPartProgram);
  
  while(bResult)
  {
    bResult = finder.FindNextFile();
    szProbe = finder.GetFileName();
    if (!IsNameInList(szProbe))
    { 
      szProbe = szProbe.Left(szProbe.Find(_T(".")));
      add_path(szProbe);
      global_changed_probe_files[0].AddTail(szProbe);
      debug.SendAndFlush(_T("CMM=%s finish_driver_init adding filename %s \n"), MACHINE_ID_STRING,szProbe);
      current_command = DRIVER_INITIALIZE;

      if (pTestProbe)
      {
        pTestProbe->file_name = szProbe;
        pTestProbe->load_tips();
#if (GETBUILDVERSION < 5500)
        CPCDtip_data* pFirstTip = pTestProbe->start_tip;
#else
        CPCDtip_data* pFirstTip = pTestProbe->get_start_tip();
#endif
        if (pFirstTip)
        {
           ForceTipStatusToChanged(pFirstTip);
           pTestProbe->save_tips();
           pTestProbe->load_tips();
        }
      }
    }
  }//while  
  delete pTestProbe;
}

//
//-------------------------------------------------------------------
//
void CALLBACK TimerOpenTcpip(HWND window, UINT msg, UINT idEvent, DWORD sysTime)
{
  debug.SendAndFlush(_T("CMM=%s TimerOpenTcpip! \n"), MACHINE_ID_STRING);
  KillTimer(NULL, idEvent);
  finish_driver_init();
}

//
//-------------------------------------------------------------------
//
void CALLBACK TimerControllerTCPTimeout(HWND window, UINT msg, UINT idEvent, DWORD sysTime)
{
  debug.SendAndFlush(_T("CMM=%s TimerOpenTcpip called back ! \n"), MACHINE_ID_STRING);
  KillTimer(NULL, idEvent);
  PCDMessageBox(m_pPartProgram, IDS_MB_CMM_NOT_RESPOND);
}

//
//-------------------------------------------------------------------
//

void CALLBACK TimerPositionUpdate(HWND window, UINT msg, UINT idEvent, DWORD sysTime)
{
  CString sActiveProbeName;

  SET_PART_PROGRAM_POINTER;
  KillTimer(NULL, idEvent);
  // If we have a serious tcp error, don't reactivate the timer.  The user will need
  // to exit PC-DMIS anyway to clear it up.
  if (serious_tcp_error)
    return;
  if (ReadoutInterval > 0)
	{
		/************************************************************************/
		/*	Greg Guilbeau - 2011/07/28                                          */
		/*	The following line(s) have been modified to handle x64 conversion   */
		/************************************************************************/
		/*  		ManualPositionUpdate = SetTimer(NULL, 0, ReadoutInterval, TimerPositionUpdate);  */
		ManualPositionUpdate = SetTimer(NULL, 0, ReadoutInterval, (TIMERPROC)TimerPositionUpdate);
	}
  // Will stop asking for updates when we are executing dcc moves
  if (! SendNext() && !bWaitingForPQDone && !ErrorRecoveryInProgress && !call_back_list.GetCount() && //  [10/28/2010 Michel]
    !StringList.GetCount() && !ErrorList.GetCount() && (CurrentMachineMode == MODE_MANUAL) )
  {
    // make sure that if we are using the CmmosCal it gets updated properly
    if (!bUseToolChanger) // TC change tool updates CMM/OS config does it [1/22/2009 Michel]
    {
		if (!(ConfigSelectionStrategy & 1)) // don't change in editor. [2/22/2010 Michel]
		  set_cmmos_active_probe();
    }
    else //TC on, if we have same active probe as in head, allow tip change
    {
      if (global_active_probe && !global_in_relay_mode) // single arm mode only ! [3/2/2011 Michel]
      {
        sLastProbeFromChangeTool = global_active_probe->file_name;
      }

      sActiveProbeName = sLastProbeFromChangeTool; // if no global (dual arm), shouild be set by NotifyToolChange
      if (0 == instr(sActiveProbeName,sToolInHeadName) && sActiveProbeName.GetLength() == sToolInHeadName.GetLength())
      {
        update_cmmos_tip_number();
      }

    }
    // ask for the current position for the readouts

    if (!ManualMachineType && HeadIsIndexable && (current_command != DRIVER_MANUAL_SCAN) && (current_command != DRIVER_MOVE_PH9))
    {
      MyWriteCommBlock(controller, _T("PQ\r\n"));  // AB position
    }
	MyWriteCommBlock(controller, _T("PG\r\n"));  // XYZ position
	MyWriteCommBlock(controller, _T("AC\r\n"));  // Config Name update for PCD //  [2/22/2010 Michel]
  }
}

//
//-------------------------------------------------------------------
//

int to_cmm_coordinates(CPoint3 & v_in, CPoint3 & out, CPCDtip_data * tip)
{
  out = to_metric(v_in);
  AxisPoint(out, FALSE);

  return 1;
}

//
//-------------------------------------------------------------------
//
void xyz_parse(CString &string, CPoint3 & out, CPoint3 & vec, CPCDtip_data * tip)
{
  int i;
  CPoint3 zero, tipm;
  double vals[7];

  for (i=0;i<7;i++) // added init [6/16/2009 Michel]
  {
    vals[i] = 0.0;
  }

  out *= 0.0;
  vec *= 0.0;

  if ((i = instr(string,_T( "X"))) == -1)
  {
    return;
  }

  if ((i=instr(string, _T("U"))) == -1)
    VERSION_INDEPENDENT_STSCANF(string, _T("X%lfY%lfZ%lf"), &vals[0], &vals[1], &vals[2]);
  else
    VERSION_INDEPENDENT_STSCANF(string,_T( "X%lfY%lfZ%lfU%lfV%lfW%lfR%lf"), &vals[0], &vals[1], &vals[2], &vals[3], &vals[4], &vals[5], &vals[6]);
  for (i = 0; i < 3; i++)
  {
    out[i] = vals[i];
    vec[i] = vals[i+3];
  }
  AxisPoint(out);
  vec.normalize();
  vec *= -1.0;
  AxisPoint(vec);

  out = to_native(out);
}

//
//-------------------------------------------------------------------
//
void ab_parse(CString &string, CPoint3 & out)
{
  int i;
  double machine_a, machine_b;

  out *= 0.0;

  if ((i = instr(string,_T( "A"))) == -1)
  {
    return;
  }

  VERSION_INDEPENDENT_STSCANF(string, _T("A%lfB%lf"), &machine_a, &machine_b);
  if (ExchangeABAxes)
  {
    out[1] = machine_a;
    out[0] = machine_b;
  }
  else
  {
    out[0] = machine_a;
    out[1] = machine_b;
  }
  if (FlipAAxis)
    out[0] *= -1.0;
  if (FlipBAxis)
    out[1] *= -1.0;
}

//
//-------------------------------------------------------------------
//
void open_io()
{
  if (TCPIP_port)
  {
    // kill the serial comm timer and reset the id to zero then open the
    // socket
    m_pMachine->KillTimer(INIT_TIMER);
    m_pMachine->m_init_timer_id = 0;
    CString tt;
    tt.Format(_T("%d"), TCPIP_port);
    my_socket = open_tcpip_socket(m_pMachine->m_hWnd, FALSE, tt, NULL, cmm_ip_address);
    if (my_socket == INVALID_SOCKET)
      TCPIP_port = 0;
  }
}

//
//-------------------------------------------------------------------
//
INTERFAC_EXTERN __declspec(dllexport) void driver_begin_manual_scan(CPCDinterface * call_back, double* target, double* vector, CPCDtip_data * tip)
{
  SET_PART_PROGRAM_POINTER;
  // hard probe scan
  debug.SendAndFlush(_T("CMM=%s driver_begin_manual_scan call_back=%x,call_back_count=%d,strlist_count=%d tip=%s\n"), MACHINE_ID_STRING,
    call_back, call_back_list.GetCount(), StringList.GetCount(), tip ? tip->get_id() :_T( "NONE"));
  if (!ManualMachineType)
  {
    AddString(_T("WaitEnd:"));
  }
  // start reading probe positions for the scan
  current_command = DRIVER_MANUAL_SCAN;
  active_call_back = call_back;
}

//
//-------------------------------------------------------------------
//
INTERFAC_EXTERN __declspec(dllexport) void driver_end_manual_scan()
{
  SET_PART_PROGRAM_POINTER;
  debug.SendAndFlush(_T("CMM=%s driver_end_manual_scan call_back=%x,call_back_count=%d,strlist_count=%d tip=%s\n"), MACHINE_ID_STRING,
    NULL, call_back_list.GetCount(), StringList.GetCount(), _T("NONE"));
  current_command = 0;
  if (active_call_back)
  {
    active_call_back->sbit(COMPLETE, 1);
    active_call_back->sbit(PAUSE, 0);
    active_call_back->sbit(TEMPPAUSE, 0);
  }
  active_call_back = NULL;
}

//
//-------------------------------------------------------------------
//
INTERFAC_EXTERN __declspec(dllexport) void driver_manual_hit(CPCDinterface * call_back, double* target, double* vector, CPCDtip_data * tip)
{
  SET_PART_PROGRAM_POINTER;
  debug.SendAndFlush(_T("CMM=%s driver_manual_hit call_back=%x,call_back_count=%d,strlist_count=%d tip=%s\n"), MACHINE_ID_STRING,
    call_back, call_back_list.GetCount(), StringList.GetCount(), tip ? tip->get_id() : _T("NONE"));
  if (!ManualMachineType && (CurrentMachineMode != MODE_MANUAL)) // Added test NOT to send SYxxx when in manual mode: stops joystick move [3/3/2011 Michel]
  {
    AddString(_T("WaitEnd:")); // wait for end of moves before going into manual to take the hit
  }
  active_call_back = call_back;
  current_command = DRIVER_MANUAL_HIT;
}

//
//-------------------------------------------------------------------
//
INTERFAC_EXTERN __declspec(dllexport) void driver_read_position(CPCDinterface * call_back, CPCDtip_data * tip)
{
  SET_PART_PROGRAM_POINTER;
  debug.SendAndFlush(_T("CMM=%s driver_read_position call_back=%x,call_back_count=%d,strlist_count=%d tip=%s\n"), MACHINE_ID_STRING,
    call_back, call_back_list.GetCount(), StringList.GetCount(), tip ? tip->get_id() : _T("NONE"));
  if (! call_back_list.GetCount())
  {  // manual mode so get position
    // request positition from the cmm
    if (HeadIsIndexable)
    {
      MyWriteCommBlock(controller, _T("PQ\r\n"));  // AB position
    }
    MyWriteCommBlock(controller, _T("PG\r\n"));  // XYZ position
    current_command = DRIVER_READ_POSITION;
    active_call_back = call_back;
  }
  else
  {  // dcc mode so return last commanded position
#ifdef BUILDVERSION_LESS_THAN_43
   CPoint3 MyPoint = global_interface_position; // all 3 current lines changed [2/16/2010 Michel]-
   AddTipOffset(MyPoint,tip);
   call_back->store_point(MyPoint);
#else
#pragma message( "Building for V43 and up ")
	CPoint3 MyPoint = global_interface_position.GetPoint(m_pMachine); // all 3 current lines changed [2/16/2010 Michel]-
	AddTipOffset(MyPoint,tip);
	call_back->store_point_axes(&global_interface_position);
#endif
  }
  return;
}

//
//-------------------------------------------------------------------
//
INTERFAC_EXTERN __declspec(dllexport) void driver_go_manual(CPCDinterface * call_back)
{
  SET_PART_PROGRAM_POINTER;
  debug.SendAndFlush(_T("CMM=%s driver_go_manual call_back=%x,call_back_count=%d,strlist_count=%d tip=%s\n"), MACHINE_ID_STRING,
    call_back, call_back_list.GetCount(), StringList.GetCount(),_T("NONE"));

  // Looking at the call back list is not reliable since moves may be buffered
  // and in that case we don't get final notification when the move actually
  // completes.
  // Go ahead and use the WaitEnd

  //  [2/9/2005]
  double retract = global_Manual_Retract;
  retract = to_metric(retract);
  
  if (!ManualMachineType) 
  {
    if(CurrentMachineMode != MODE_MANUAL) //1.32 don't call waitend when already in man mode - stops probe manual move!!!  [3/3/2011 Michel])
    {
      AddString(_T("WaitEnd:")); // MOVED HERE !!! [8/20/2008 Michel]

      if (fabs(current_man_retract - retract)>.001)
      {
        CString tmpstr;
        tmpstr.Format(_T("RetractDistanceManProbe(%lf)\r\n"), retract);
        MyWriteCommBlock(controller, tmpstr);
        //CallbackListAddTail((CPCDinterface*)NULL,CString(L" driver_goManual- sending RetractDIstanceManProbe, 'RetractDIstanceManProbe' answer"));
  //      call_back_list.AddTail((CPCDinterface *) NULL);
        current_man_retract = retract;
      }
      
      // reset manual speed to registry setting one [6/9/2009 Michel]
      send_move_speed(man_speed); //  [6/9/2009 Michel]

      current_command = DRIVER_GO_MANUAL;
      active_call_back = call_back;
    }// case not in manual ...
    else //already in manual, don't do much ....  [3/3/2011 Michel]
    {
      if (call_back)
      {
        call_back->store_point();
      }
    }
  }
  else // added [9/27/2010 Michel] manual machine
  {
    if (call_back)
    { 

      CmmosStylus *lpStylus = NULL;
      CPCDtip_data* myTip = NULL;
      myTip = call_back->tip;
      double a=0.0,b=0.0;
      if(myTip)
      {
        a=myTip->get_ph9a();
        b=myTip->get_ph9b();
        mapHeadAngles(a,b); //handle angles reversed/swapped
        if (UseCmmosCalData)
        {
          if (!bUseCaaWrist)
          {
            lpStylus = StylusList.GetCmmosStylus(a,b);
            if (lpStylus)
            {
              int TipToSelect = lpStylus->GetIndex();
              if (TipToSelect != LastSelectedTipNumber)
              {
                LastSelectedTipNumber = TipToSelect;
                CmmOsSelectTip(TipToSelect);
              }
            }
            else //no such stylus
            {
            CString sErrmEss;
            sErrmEss.Format(L"Angles a=%4.1lf b=%4.1lf do not exist in current CMM-OS configuration",a,b);
            my_show_error(sErrmEss);
            }
          }
          else // CaaWrist: we must still select tip ! 
          { 
            int tipnumber = myTip->get_tip();
            CmmOsSelectTip(tipnumber);
          }
        }
     }
     call_back->store_point();
    }
  }
  // end if/else manual machine
}

//
//-------------------------------------------------------------------
//
  void AddPointToString(CPoint3 pnt, CString& str,BOOL bSpaceAtStart  = TRUE) // add a formated point to str 1 space between 3 doubles formated. CMM/OS style [12/4/2008 Michel]
    // i.e.  " 1.2345 2.4567 -3.5555"  1st space avoided is bSpaceAtStart FALSE
   
  {
    CString temp;
  
    for (int i=0;i<3;i++)
    {
      temp.Format(L"%12.8lf",pnt[i]);
      temp.Trim();
      if ((0 != i) || bSpaceAtStart)
      {
        str += L" ";
      }
      str += temp;
    }
  }
//
//-------------------------------------------------------------------
//
INTERFAC_EXTERN __declspec(dllexport) void driver_circular_move(CPCDinterface * call_back, double* move_point, double* center_point,
                                                BOOL large_arc, CPCDtip_data * tip, double speed)
{
  double myspeed = 0.0;
  CPoint3 center = center_point;
  CPoint3 endPnt = move_point;
  CPoint3 startPnt;
  CString tmpstr;
  int i = 0;

  SET_PART_PROGRAM_POINTER;
  debug.SendAndFlush(_T("CMM=%s driver_circular_move call_back=%x,call_back_count=%d,strlist_count=%d tip=%s\n"), MACHINE_ID_STRING,
    call_back, call_back_list.GetCount(), StringList.GetCount(), tip ? tip->get_id() :_T( "NONE"));
  debug.SendAndFlush(_T("CMM=%s circular movept=%lf,%lf,%lf cntrpt=%lf,%lf,%lf large_arc=%s speed=%lf\n"), MACHINE_ID_STRING,
    move_point[0], move_point[1], move_point[2], center_point[0], center_point[1], center_point[2],
    (large_arc ? _T("TRUE") :_T( "FALSE")), speed);
// implementation [12/4/2008 Michel]

#ifdef BUILDVERSION_LESS_THAN_43
 startPnt = global_commanded_position;
 global_commanded_position = move_point;
#else
#pragma message( "Building for V43 and up ")
 startPnt = global_commanded_position.GetPoint(m_pMachine);
 CPoint3 cmove_point(move_point);
 global_commanded_position.SetPoint(m_pMachine, cmove_point);
#endif


  go_dcc();

  // Speeds in this interface have to be sent as a percentage of max
  if (GETOPTION(ABSOLUTESPEEDS))
    myspeed = speed / max_speed * 100.0;
  else
    myspeed = speed;

  send_move_speed(myspeed);

    debug.SendAndFlush(_T("CMM=%s circular start prog = %lf,%lf,%lf end = %lf,%lf,%lf l\n"), MACHINE_ID_STRING,
    startPnt[0], startPnt[1], startPnt[2], endPnt[0], endPnt[1], endPnt[2]);
  // output more debug info
  CPoint3 rad_v;
  rad_v = startPnt - center;
  double radius_start = rad_v.norm();

  rad_v = endPnt - center;
  double radius_end = rad_v.norm();

  debug.SendAndFlush(_T("CMM=%s circular start radius = %lf, end = %lf l\n"), MACHINE_ID_STRING,
    radius_start,radius_end);

  // get data in machine coords, then calculate angle 
  to_cmm_coordinates(startPnt,startPnt,tip);
  to_cmm_coordinates(center,center,tip);
  to_cmm_coordinates(endPnt,endPnt,tip);
  // add the tip offsets [2/16/2010 Michel]
  SubTipOffset(startPnt,tip);
  SubTipOffset(center,tip);
  SubTipOffset(endPnt,tip);

  debug.SendAndFlush(_T("CMM=%s circular start machine = %lf,%lf,%lf end = %lf,%lf,%lf l\n"), MACHINE_ID_STRING,
    startPnt[0], startPnt[1], startPnt[2], endPnt[0], endPnt[1], endPnt[2]);

  /* //removed for position on sphere

  CPoint3 vecStart,vecEnd,cylAxis;
  vecStart = startPnt - center;
  vecStart.normalize();
  vecEnd = endPnt - center;
  vecEnd.normalize();
  cylAxis = vecEnd.cross(vecStart);
  double rotAngle = RADTODEG(ACOS(vecStart*vecEnd));
  debug.SendAndFlush(_T("CMM=%s circular angle = %lf\n"), MACHINE_ID_STRING,
    rotAngle);
*/


  //tbd  check is we are in a condition 180+/-1.1 since then CMM/OS errors ....

  //

  // send cmd;
  
// change from PositionOnCylinder to Position On Sphere [3/27/2009 Michel]
//  tmpstr = L"PositionOnCylinder(C";
  tmpstr = L"PositionOnSphere(C";
  AddPointToString(center,tmpstr);
  
//  tmpstr += L" N";   // pointless on cylinder [3/27/2009 Michel]
//  AddPointToString(cylAxis,tmpstr);
  
  tmpstr += L" P";
  AddPointToString(endPnt,tmpstr);

  tmpstr += L" R ";
  tmpstr += large_arc ? L"1" : L"0";
  tmpstr += L")\r\n";

  current_command = DRIVER_MEASURE_POINT;
  active_call_back = call_back;

  debug.SendAndFlush(_T("CMM=%s *** driver_circular_move adding callback = %x \n"), MACHINE_ID_STRING,call_back);
  CallbackListAddTail(call_back,tip,CString(L" driver_circular_move- sending PositionOnCylinder, CS answer ignored but MoveCirDone sync used"));

  MyWriteCommBlock(controller, tmpstr);

  tmpstr.Format(_T("SY%s\r\n"), CIRC_COMPLETE_STRING); // for the circ cmd, CS *IS* end of move .... BAD ! have to find another signal ! [12/5/2003]
  MyWriteCommBlock(controller, tmpstr);

  return;
}

//
//-------------------------------------------------------------------
//
INTERFAC_EXTERN __declspec(dllexport) void driver_move_absolute(CPCDinterface * call_back, double* move_point, CPCDtip_data * tip, double speed)
{
  CPoint3 pnt = move_point;
  double myspeed;
  CString tmpstr;

  SET_PART_PROGRAM_POINTER;
  debug.SendAndFlush(_T("CMM=%s driver_move_absolute call_back=%x,call_back_count=%d,strlist_count=%d tip=%s\n"), MACHINE_ID_STRING,
    call_back, call_back_list.GetCount(), StringList.GetCount(), tip ? tip->get_id() : _T("NONE"));
  debug.SendAndFlush(_T("CMM=%s absolute movept=%lf,%lf,%lf speed=%lf\n"), MACHINE_ID_STRING, move_point[0], move_point[1], move_point[2], speed);

  go_dcc();

  // Speeds in this interface have to be sent as a percentage of max
  if (GETOPTION(ABSOLUTESPEEDS))
    myspeed = speed / max_speed * 100.0; // max_speed always in Metric
  else
    myspeed = to_metric(speed);
    

  send_move_speed(myspeed);

//  [2/16/2010 Michel]  we are still in native
  SubTipOffset(pnt,tip); // + changed below the MZ,MP,MW to use MyuPoint [2/16/2010 Michel]

  last_position = move_point;
  to_cmm_coordinates(pnt, pnt, tip); // goto metric here
  CPoint3 MyPoint = pnt; 

  // send move command to controller
  if (BufferedMoves && bUseMZForMove) //added [8/22/2008 Michel]
  {
    int tag = GetNextSyTag(); //  [6/16/2009 Michel]
    tmpstr.Format(_T("MZX%lfY%lfZ%lfP5T6%02d\r\n"), MyPoint[0], MyPoint[1], MyPoint[2],tag); // change from T666 to T6xx [1/21/2009 Michel]
    ValidMoveTagArrayAdd(600+tag);
  }
  else if (BufferedMoves && !bOneShotMoveUnbuffered) // added the one shot ... [8/20/2008 Michel]
  {
    tmpstr.Format(_T("MPX%lfY%lfZ%lf\r\n"), MyPoint[0], MyPoint[1], MyPoint[2]);
  }
  else
  {
    tmpstr.Format(_T("MWX%lfY%lfZ%lf\r\n"), MyPoint[0], MyPoint[1], MyPoint[2]);
    bOneShotMoveUnbuffered = FALSE; //  [8/20/2008 Michel]
  }



#ifdef BUILDVERSION_LESS_THAN_43
  global_commanded_position = move_point;
#else
#pragma message( "Building for V43 and up ")
  CPoint3 cmove_point(move_point);
  global_commanded_position.SetPoint(m_pMachine, cmove_point);
#endif
  current_command = DRIVER_MEASURE_POINT;
  active_call_back = call_back;

  debug.SendAndFlush(_T("CMM=%s *** driver_movabs adding callback = %x \n"), MACHINE_ID_STRING,call_back);
  CallbackListAddTail(call_back,tip,CString(L" driver_Move_absolute- sending MZ move/tag command, 'MZCY6xx' as answer"));

  MyWriteCommBlock(controller, tmpstr);
  return;
}

//
//-------------------------------------------------------------------
//
INTERFAC_EXTERN __declspec(dllexport) void driver_move_relative(CPCDinterface * call_back, double* move_point, CPCDtip_data * tip, double speed)
{
  CPoint3 absolute;

  SET_PART_PROGRAM_POINTER;
  debug.SendAndFlush(_T("CMM=%s driver_move_relative call_back=%x,call_back_count=%d,strlist_count=%d tip=%s\n"), MACHINE_ID_STRING,
    call_back, call_back_list.GetCount(), StringList.GetCount(), tip ? tip->get_id() :_T( "NONE"));
  debug.SendAndFlush(_T("CMM=%s relative movept=%lf,%lf,%lf speed=%lf\n"), MACHINE_ID_STRING, move_point[0], move_point[1], move_point[2], speed);

#ifdef BUILDVERSION_LESS_THAN_43
  absolute = global_commanded_position;
#else
#pragma message( "Building for V43 and up ")
  absolute = global_commanded_position.GetPoint(m_pMachine);
#endif

  absolute += move_point;
  bOneShotMoveUnbuffered = TRUE; // make sure we wait for this end of move .... [8/20/2008 Michel]
  driver_move_absolute(call_back, absolute, tip, speed);
 // AddString(L"WaitEnd:");
  return;
}

//
//-------------------------------------------------------------------
//
INTERFAC_EXTERN __declspec(dllexport) void driver_measure_point(CPCDinterface * call_back, double* in_target, double* in_vector, CPCDtip_data * tip,
                                                double speed, double touch_speed, double prehit, double retract, double check)
{
  CPoint3 retract_point, target, vector, prehit_point, contact_point;
  double search_dist, mytouch_speed;
  double diam;
  CString tmpstr;


  SET_PART_PROGRAM_POINTER;
  debug.SendAndFlush(_T("CMM=%s driver_measure_point call_back=%x,call_back_count=%d,strlist_count=%d tip=%s\n"), MACHINE_ID_STRING,
    call_back, call_back_list.GetCount(), StringList.GetCount(), tip ? tip->get_id() : _T("NONE"));
  debug.SendAndFlush(_T("CMM=%s meas vals=%lf,%lf,%lf,%lf,%lf,%lf speed=%lf touchspeed=%lf prehit=%lf retract=%lf check=%lf\n"), MACHINE_ID_STRING,
    in_target[0], in_target[1], in_target[2], in_vector[0], in_vector[1], in_vector[2],
    speed, touch_speed, prehit, retract, check);

  //AddString(_T("WaitEnd:"));

  target = in_target;
  vector = in_vector;

  diam = (tip ? tip->get_diam() * 0.5 : 0.0);
  prehit_point = target + vector * (prehit + diam);
  contact_point = target + vector * diam;
  retract_point = target + vector * (retract + diam);

  if (check)
    search_dist = check;
  else
    search_dist = prehit * 3.0;
  search_dist = to_metric(search_dist);
  if (search_dist != cur_search_dist)
  {
    cur_search_dist = search_dist;
    tmpstr.Format(_T("SS%lf\r\n"), search_dist);
    MyWriteCommBlock(controller, tmpstr);
    //CallbackListAddTail((CPCDinterface*)NULL,CString(L" driver_Measure_point- sending SS search dist command, 'SSxx' as answer"));
//    call_back_list.AddTail((CPCDinterface *) NULL);
  }

  // Speeds in this interface have to be sent as a percentage of max
  if (GETOPTION(ABSOLUTESPEEDS))
    mytouch_speed = touch_speed / max_speed * 100.0;
  else
    mytouch_speed = to_metric(touch_speed); //max speed always metric, absolute speed in native.

  if (mytouch_speed > 100.0)
    mytouch_speed = 100.0;
  if (mytouch_speed <= 0.0)
    mytouch_speed = 2.0;
  if (mytouch_speed != cur_touch_speed)
  {
    cur_touch_speed = mytouch_speed;
    // add commands to change the speed
    tmpstr.Format(_T("PS%lf\r\n"), mytouch_speed);
    MyWriteCommBlock(controller, tmpstr);
    //CallbackListAddTail((CPCDinterface*)NULL,CString(L" driver_Measure_point- sending PS search dist command, 'PSxx' as answer"));
//     call_back_list.AddTail((CPCDinterface *) NULL);
  }

  debug.SendAndFlush(_T("CMM=%s *** driver_measure_point calling moveabs prehit \n"), MACHINE_ID_STRING);
  driver_move_absolute(NULL, prehit_point, tip, speed);

  // add commands to search for the part until target
  SubTipOffset(contact_point,tip);//  [2/16/2010 Michel] // moved, we're still native units here [9/23/2010 Michel]
  to_cmm_coordinates(contact_point, contact_point, tip);

//   //will 1st receive a CS => addinf null callback
//   call_back_list.AddTail((CPCDinterface*)NULL);

  debug.SendAndFlush(_T("CMM=%s *** driver_measure_point adding own callback %x\n"), MACHINE_ID_STRING,call_back);
  CallbackListAddTail(call_back,tip,CString(L" driver_Measure_point- sending MM probe command, 'MMxx' as answer"));
//  call_back_list.AddTail(call_back);

  tmpstr.Format(_T("MMX%lfY%lfZ%lf\r\n"), contact_point[0], contact_point[1], contact_point[2]);
  MyWriteCommBlock(controller, tmpstr);

  debug.SendAndFlush(_T("CMM=%s *** driver_measure_point calling moveabs retract \n"), MACHINE_ID_STRING);
  driver_move_absolute(NULL, retract_point, tip, speed); //will record the point !

  current_command = DRIVER_MEASURE_POINT;
  active_call_back = call_back;
}

//
//-------------------------------------------------------------------
//
INTERFAC_EXTERN __declspec(dllexport) TCHAR* driver_get_version(void)
{
  static TCHAR version[50];

  m_pPartProgram = m_pMachine->m_pPartProgram;
  VERSION_INDEPENDENT_STPRINTF(version, _T("cmmos %s %s %s"), _T(__DATE__),
#ifdef _DEBUG
    _T("debug"),
#else
    _T("release"),
#endif
    MACHINE_INTERFACE_VERSION);
  return version;
}

//
//-------------------------------------------------------------------
//
INTERFAC_EXTERN __declspec(dllexport) BOOL driver_initialize()
{
  int section_exists;

  SET_PART_PROGRAM_POINTER;
  AfxSetResourceHandle(global_Inst_ResDLL);  // set resource handle to resource dll
  global_is_online = TRUE;
 
#if GETBUILDVERSION > 4000
  global_is_iplusplus_type_cmm  = 2; // changed to 2 to allow to differentiate I++ and CMMOS [4/17/2012 Michel]
#endif

  CallbackListRemoveAll(CString(L" driver_initialize"));
  call_back_list.RemoveAll();
  StringList.RemoveAll();

  AddEntries(ini_info, m_pMachine->add_cmm(SECTION_HEADER));
  section_exists = LoadDefaults(global_main_winapp, ini_info, m_pMachine->add_cmm(SECTION_HEADER));
  if (TCPOpenDelay < MIN_TCPOPENDELAY)
    TCPOpenDelay = MIN_TCPOPENDELAY;
  if (CtrlTCPTimeout < MIN_CTRLTCPTIMEOUT)
    CtrlTCPTimeout = MIN_CTRLTCPTIMEOUT;
  if ((! section_exists) || (myversion < _ttof(MACHINE_INTERFACE_VERSION)))
  {
    myversion = _ttof(MACHINE_INTERFACE_VERSION);
    // we stop cleaning the CMMOS registry folder
    //INIReplaceDefaults(global_main_winapp, ini_info, m_pMachine->add_cmm(SECTION_HEADER));
    SaveDefaults(global_main_winapp, ini_info, m_pMachine->add_cmm(SECTION_HEADER));
  }
  global_has_circular_moves = bUseCirc; //  [12/5/2008 Michel]
#if GETBUILDVERSION > 5200
  BOOL bEnabledDebugManager = EnabledDebugManager();
  debug.EnableDebugManager(bEnabledDebugManager!=0 && debugUseDebugManager!=0);
#endif
#if GETBUILDVERSION > 5300
  debug.SetPath(global_calibration_folder);
#endif

  if (debugLog || global_force_debug)
  {
    debug.CreateLog(debugFilename,ini_info,m_pMachine->add_cmm(SECTION_HEADER));
  }
  global_resetDebug = debugLogReset;
  if (debugWindow)
    debug.CreateView((LPCTSTR) debugTitle, VIEW_BLUE_WHITE);
  debug.SendAndFlush(_T("CMM=%s PCDMIS    version info: %s\n"), MACHINE_ID_STRING, PCDLRNGetVersStr());
  debug.SendAndFlush(_T("CMM=%s Interface version info: %s\n"), MACHINE_ID_STRING, driver_get_version());
  INIShowActiveValues(ini_info, m_pMachine->add_cmm(SECTION_HEADER));
  debug.SendAndFlush(_T("CMM=%s driver_initialize call_back=%x,call_back_count=%d,strlist_count=%d tip=%s\n"), MACHINE_ID_STRING,
    NULL, call_back_list.GetCount(), StringList.GetCount(),_T( "NONE"));
  InitializeScaleAxis();
  // TC [1/22/2009 Michel]
  sUnloadToolName = PCDGetProfileString(_T("Option"), _T("ProbeFileUsedToForceUnloadWithNoPickup"));

  if (UseCmmosCalData)
    global_needs_custom_probecal = TRUE;
  else
    global_needs_custom_probecal = FALSE;
  if (ManualMachineType) //  [9/27/2010 Michel]
  {
    global_top_speed = 0.0;
  }
    open_io();

  // If using TCPIP set a timer that will expire if we don't get a response
  // within a reasonable time and also set a timer that will finish the init
  // sequence after the TCP connection has had time to complete.
  // If not using TCPIP, go ahead and finish the init sequence now.
  if (TCPIP_port)
  {
    /************************************************************************/
    /*	Greg Guilbeau - 2011/07/28                                          */
    /*	The following line(s) have been modified to handle x64 conversion   */
    /************************************************************************/
    /*      TCPTimeout_id = SetTimer(NULL, 0, CtrlTCPTimeout, TimerControllerTCPTimeout);
    SetTimer(NULL, 0, TCPOpenDelay, TimerOpenTcpip);  */
    TCPTimeout_id = SetTimer(NULL, 0, CtrlTCPTimeout, (TIMERPROC)TimerControllerTCPTimeout);
    SetTimer(NULL, 0, TCPOpenDelay, (TIMERPROC)TimerOpenTcpip);
  }
  else
    finish_driver_init();

  return (FALSE);
}
//
//-------------------------------------------------------------------
//

BOOL SaveTransformToCmmOs(CString name, CTransform Csy) //  [12/3/2008 Michel]
{

  BOOL bReturn = FALSE; 
  CPoint3 Origin;
  CString cmd;
  CString field;
  TRY
  {
    
      // transform origin back in CMM/OS since Zeiss uses TRANS -> ROT convention
    Origin[0] = Csy.m_data[0][0];
    Origin[1] = Csy.m_data[0][1];
    Origin[2] = Csy.m_data[0][2];
    
    Csy.aaforw(ROTONLY,Origin, Origin);

    // format command : Watch out: fields are separated by **one** space !
    cmd = "WriteComm:SaveWPSystem(";
    cmd += name.Trim();

    for (int i=0;i<3;i++) // rotation matrix
    {  
      for(int j=0;j<3;j++)
      {
        field.Format(L"%lf", Csy.m_data[i+1][j]);
        cmd += L" ";
        cmd += field.Trim();
      }
    }
    // origin now
    for (int i=0;i<3;i++)
    {
      field.Format(L"%lf",Origin[i]);
      cmd += L" ";
      cmd += field.Trim();
    }
    // and close then send
    cmd += L")\r\n";
    AddString(cmd);
    bReturn = TRUE;
  }
  CATCH(CException,e)
  {
    return FALSE;
  }
  END_CATCH;

  return bReturn; 
}

BOOL SaveTransformToFile(CString Filename, CTransform Csy)
{
int file_has_own_path=FALSE;
CString dir;
CString PathName;
BOOL bReturn = FALSE; 

if ( Filename.Find( _T( "\\" ) ) >= 0 )
file_has_own_path=TRUE;

if ((! global_file_path.IsEmpty() && ! Filename.IsEmpty()) || file_has_own_path)
{

  if(file_has_own_path)
    PathName = Filename;
  else
{
    if (global_search_order & (1<<0) &&
      (global_search_order & (1<<2) ||
      !(global_search_order & (1<<1))))
      dir=global_file_path;
    else
      dir=global_recall_alignment_user_search_directory;
    if ( dir.Right( 1 ) != _T( "\\" ) )
      dir += _T( "\\" );

    PathName = dir + Filename;
  }

  CFile outfile;
  PathName += ".aln";

  if (outfile.Open(PathName, CFile::modeCreate | CFile::modeWrite | CFile::shareExclusive))
    {

      CArchive archive(&outfile, CArchive::store);
      long num_aln = 1; 
      CString external_id;
      external_id="CMMOS"; //arbitrary

      // save header
	  	
	  archive << global_PRGSCHEMA;
//	  archive << GetPRGSchema(m_pPartProgram);
      archive << MM;
      archive << num_aln;
      archive << external_id ;

      CTransform IdentityAlign; // by default

      for (int i = 0; i < 3; i++)
      {
        for (int j = 0; j < 4; j++)
        {
          archive << IdentityAlign.m_data[j][i]; //we have no CADTOPART !
          archive << Csy.m_data[j][i];
        }

      }
      TRY
      {
        archive.Close();
      }
      CATCH(CException,e)
      {
        archive.Abort();
        TRY
        {
          outfile.Close();
        }
        CATCH(CException,e)
        {
          outfile.Abort();
        }
        END_CATCH;
        return FALSE;
      }
      END_CATCH;
      TRY
      {
        outfile.Close();
        bReturn = TRUE;
      }
      CATCH(CException,e)
      {
        outfile.Abort();
        return FALSE;
      }
      END_CATCH;
    }

  }

  return bReturn;
}
//
//-------------------------------------------------------------------
//
INTERFAC_EXTERN __declspec(dllexport) BOOL driver_execute_custom_command(int command_number, int *int_in_data, int int_in_data_cnt, double * double_in_data, int double_in_data_cnt, int **int_out_data , int &int_out_data_cnt, double **double_out_data, int &double_out_data_cnt)
{ //  [12/2..4/2008 Michel]
  // command opcodes:
  // 150 : loads in a string list names of  CMM/OS alignments : reset Wps list, reads names, and return count in int_out_data
  // 151 : enumerate contend of alignment names list;
  // 152 : loads the CMM/OS wps according to name in indata in an intermediate storage csy
  // 153 : indicates in the integer if previous op was successful, and feedbacks content (PCDMIS conventions),in out doubles zone 
  // 154 : saves content of intermediate storage in PC-DMIS .aln file, name in in_data
  // 155 : saves current alignment in cmm/os, with name in in_data.

  static   POSITION pos;
  TRACE(_T("##### driver_execute_custom_command: %d \n"), command_number);
  m_pPartProgram = m_pMachine->m_pPartProgram;
  int i =0;
  BOOL   bRetval = FALSE; //true = success
  TRY
  {
  debug.Send (_T("CMM=%s  driver_execute_custom_command: %d \n"), MACHINE_ID_STRING, command_number);
  if (150 == command_number)
  {
    pos = WpsList.GetHeadPosition();

    *int_out_data    = new int[1];
    int_out_data_cnt = 1;
    
    /************************************************************************/
    /*	Greg Guilbeau - 2011/07/28                                          */
    /*	The following line(s) have been modified to handle x64 conversion   */
    /************************************************************************/
    /*      (*int_out_data)[0] = WpsList.GetCount();  */
#ifdef _WIN64
    (*int_out_data)[0] = WAI64bit::to32bit(WpsList.GetCount(),__FILE__,__LINE__);
#else
    (*int_out_data)[0] = WpsList.GetCount();
#endif
  }

  else if (151 == command_number) // return next alignment in list.
  {
    CString NextAlignName;
    NextAlignName = WpsList.GetNext(pos);

    if (! NextAlignName.IsEmpty())
    {
      int tsize = NextAlignName.GetLength();
     
      *int_out_data    = new int[tsize];
      int_out_data_cnt = tsize;
      
      for (int i =0;i<tsize;i++)
      {
        (*int_out_data)[i] = NextAlignName[i];
      }
      bRetval = TRUE;
    }
  }

  else if (152 == command_number) //load a CMM/OS WP f(name)
  {
    CString MyString; // go from our counted char array to CString, CString has nice functions 						
    for (i=0;i<int_in_data_cnt;++i)
    {
      MyString += (char)int_in_data[i];
    }
    // test
//     MyString = "STELLW_LHS";
    if (! MyString.IsEmpty())
    {
    CString MyCmd;
    MyCmd = "WpsLoad:";
    MyCmd += MyString;
    AddString(MyCmd);
    bRetval = TRUE;
    }
  }
  else if (153 == command_number) //load OK
  {
    *int_out_data    = new int[1];
    int_out_data_cnt = 1;

    if (bWpStorageOK)
      (*int_out_data)[0] = 1;
    else
      (*int_out_data)[0] = 0;

    *double_out_data = new double[4*3];
    double_out_data_cnt = 12;

    for (int i=0;i<4;i++)
    {
      for (int j=0;j<3;j++)
      {
        (*double_out_data)[(i*3)+j] = WpStorage.m_data[i][j];
      }
    }
  }

  else if (154 == command_number) // save a PCDMIS ALN = WpStorage.
  {
    CString file_id; // go from our counted char array to CString, CString has nice functions 						
    for (i=0;i<int_in_data_cnt;++i)
    {
      file_id += (char)int_in_data[i];
    }

    bRetval = SaveTransformToFile(file_id,WpStorage);
  }

  else if (155 == command_number) // save current PCDMIS ALN => CMM/OS
  {
    CString file_id; // go from our counted char array to CString, CString has nice functions 						
    for (i=0;i<int_in_data_cnt;++i)
    {
      file_id += (char)int_in_data[i];
    }

    bRetval = SaveTransformToCmmOs(file_id,global_align->get_alignment(MACHINETOPARTS));
  }
  return (bRetval);

}
CATCH(CException,e)
{
  return FALSE;
}
END_CATCH;
}
//
//-------------------------------------------------------------------
//
INTERFAC_EXTERN __declspec(dllexport) void driver_close()
{
  static BOOL already_closed = FALSE;
  if (already_closed)
    return;

  already_closed = TRUE;
  SET_PART_PROGRAM_POINTER;

  CString s_done =_T("CF\r\n");
  send_tcpip(s_done);

  if (ManualPositionUpdate)
    KillTimer(NULL, ManualPositionUpdate);
  ManualPositionUpdate = 0;
}

//
//-------------------------------------------------------------------
//
#pragma warning (push) // to be able to restore state later on
#pragma warning(disable:4706)

BOOL controller_notify(LPARAM lParam)
{
  static int nLength = 0;
  static char string[512];
  char byte_info[2];
  CPCDinterface * inter;
  CPoint3 TmpPnt, TmpVec;
  clock_t end;
  int k = 0;
  int iloc = 0;
  /************************************************************************/
  /*	Greg Guilbeau - 2011/07/28                                          */
  /*	The following line(s) have been modified to handle x64 conversion   */
  /************************************************************************/
  /*    int errcnt = 0;
  int strcnt = 0;  */
  INT_PTR errcnt = 0;
  INT_PTR strcnt = 0;
  CString tmpstr;
  CString logstring;
  CString EditedString;
  CString ErrorMessage;
  static CString LastErrorString;
  /************************************************************************/
  /*	Greg Guilbeau - 2011/07/28                                          */
  /*	The following line(s) have been modified to handle x64 conversion   */
  /************************************************************************/
  /*    int iii =0;  */
  INT_PTR iii =0;
  //void* ppptr = NULL;
  Ccall_back cB;
  /************************************************************************/
  /*	Greg Guilbeau - 2011/07/28                                          */
  /*	The following line(s) have been modified to handle x64 conversion   */
  /************************************************************************/
  /*    int cnt = 0;  */
  INT_PTR cnt = 0;

  if (! nLength)
    string[0] = '\0';

loop:
  end = clock() + CLOCKS_PER_SEC;
loop2:
  if (in_string.GetLength())
  {
    byte_info[0] = (char)in_string[0];
    in_string = in_string.Mid(1);
    k = 1;
  }
  else
    return (FALSE);

  string[nLength++] = byte_info[0];
  string[nLength] = '\0';
  if (byte_info[0] != '\r')
    goto loop2;

  if (nLength > 1)
    --nLength;
  else
    goto loop;
  string[nLength] = '\0';
  nLength = 0;

  if (TCPTimeout_id)
  {
    KillTimer(NULL, TCPTimeout_id);
    TCPTimeout_id = 0;
  }
  logstring.Format(_T("CMM=%s cmm:"), MACHINE_ID_STRING);
  logstring += string;
  logstring += _T("\n");
  debug.SendExactAndFlush(logstring);

  // Now get rid of any imbedded line feeds and for any imbedded backspace characters get
  // rid of the backspace itself and the character before it.  This makes it easier if
  // using some software tool to simulate the controller via manual typing for testing
  // purposes.
  EditedString = string;
  while ((iloc = EditedString.Find('\n')) != -1)
    EditedString = EditedString.Left(iloc) + EditedString.Mid(iloc + 1);
  while ((iloc = EditedString.Find('\r')) != -1)
    EditedString = EditedString.Left(iloc) + EditedString.Mid(iloc + 1);
  while ((iloc = EditedString.Find('\010')) != -1)
  {
    if (iloc > 0)
      EditedString = EditedString.Left(iloc - 1) + EditedString.Mid(iloc + 1);
    else
      EditedString = EditedString.Mid(iloc + 1);
  }
  iii = call_back_list.GetCount();
  Sleep(50);
//  ppptr = NULL;
  if (iii)
  {
    cB = call_back_list.GetHead();
  }
  TRACE(_T("------- received %s count = %d obj = %x \n"),EditedString,iii,cB);

  // process the string
  if (bInListingWPS) //  [12/2/2008 Michel]
  {
    AddToWPList(EditedString);
    if (!bInListingWPS)
    {
      SendNext(1);
    }
    else
      goto loop;
  }

  if (instr(EditedString,_T( "EF")) < 0)
    ConsecutiveIdenticalErrors = 0;

  if (instr(EditedString,_T( "EC"))== 0)
  {
    ConsecutiveIdenticalErrors = 0;
  }
  // ignore cases ...  [12/8/2003]
  else if (instr(EditedString, _T("WKA")) == 0)
  {
    ;
  }
  else if (instr(EditedString, _T("MissingBoreMode")) == 0)
  {
    ;
  }
  else if (instr(EditedString, _T("Logbookflag")) == 0) //  [3/27/2009 Michel]
  {
    ;
  }
  else if (instr(EditedString, _T("CTP")) == 0)
  {
    ;
  }
  else if (instr(EditedString, _T("RevNrCMMOS")) == 0)
  {
    CString rev = EditedString.Mid(11);
    rev.Trim(L")");
    SetupOptionsFromCmmOsVersion(rev);
    SendNext(1);
  }

  // probe changer feedback [1/22/2009 Michel]
  else if (instr(EditedString, _T("ChangeProbeDone")) == 0)
  {
    ChangeProbeDone(); 
    SendNext(1);
  }

  else if (instr(EditedString, _T("AC")) == 0) // name of current probe in head
  {
    CString RepliedName = EditedString.Mid(2); // for safety - simulator 1.0 returned "" right now [1/26/2011 Michel]
    if (! RepliedName.IsEmpty())
    {
      cur_probe_name = RepliedName;
      if (bTcAllowUnload && (0 == instr(cur_probe_name,sCmmosNoToolName)))
      {
        cur_probe_name = sUnloadToolName;
      }
      sToolInHeadName = cur_probe_name;
    }
    SendNext(1);
  }


  else if (instr(EditedString, _T("EF")) == 0)
  {
    BOOL bDoNotSignalError = FALSE;
    ErrorRecoveryInProgress = TRUE;
	EditedString.Trim();

	if (EditedString == LastErrorString)
      ConsecutiveIdenticalErrors++;
    else
      ConsecutiveIdenticalErrors = 0;
    LastErrorString = EditedString;
    ErrorMessage = EditedString.Mid(2);
    //
    iloc = ErrorMessage.Find(' ');
    if (iloc >= 0)
      tmpstr = ErrorMessage.Left(iloc);
    else
      tmpstr = ErrorMessage;
    iloc = _ttoi(tmpstr);

    switch (iloc)
    {
    case 1005:
      // Error Loading a Probe Config from CmmOS (usually due to invalid name requested)
      if (bNoTipSelected && bNoErrorOnProbeNotFound)
      {
        bSilentError = TRUE;
      }
      else
      {
        scan_can_recover = 0;
        ErrorLoadingCmmosProbeConfig = TRUE;
        cur_probe_name = PNAME_ERROR_RESET;
      }
      break;
    case 6141:
      // Unexpected hit
      scan_can_recover = 1;
      break;
    case 6023:
      // Missed the part
      cnt = call_back_list.GetCount();//  we need to remove the measure callback - if not, desync and exception on Findhole ! [1/21/2009 Michel]
      if (cnt)
      { 
		Ccall_back Cb; // next 3 line to use new CCall [2/15/2010 Michel]
        Cb = CallbackListRemoveHead(CString(L" Received Error 6023 missed part !"));
		debug.SendAndFlush(_T("CMM=%s *** controller notify treating a EF6023, removed callback = %x from list\n"), MACHINE_ID_STRING,Cb.pInterface);
      }
      scan_can_recover = 2;
      break;
    default:
      // All other errors
      scan_can_recover = 0;
      break;
    }
    bIgnoreZAZZ = TRUE; // will be reset after a PG read position ... [12/8/2003]
    my_show_error(ErrorMessage);
  }
  else if (instr(EditedString, _T("Panelcode=")) == 0)
  {
    EditedString = EditedString.Mid(10);
    debug.SendAndFlush(_T("CMM=%s Received Panelcode string: %s\n"), MACHINE_ID_STRING, EditedString);
    if ((instr(EditedString, PanelcodeDone) == 0) || (instr(EditedString, _T("ok")) == 0))
    {
      debug.SendAndFlush(_T("CMM=%s sending command ID_UTIL_END\n"), MACHINE_ID_STRING);
      send_command(m_pMachine, ID_UTIL_END);
    }
    else if (instr(EditedString, PanelcodeEraseHit) == 0)
    {
      debug.SendAndFlush(_T("CMM=%s sending command ID_UTIL_ERASE\n"), MACHINE_ID_STRING);
      send_command(m_pMachine, ID_UTIL_ERASE);
    }
    else if (instr(EditedString, PanelcodeInsertMove) == 0)
    {
      debug.SendAndFlush(_T("CMM=%s sending command ID_DCC_INSERT_MOVE\n"), MACHINE_ID_STRING);
      send_command(m_pMachine, ID_DCC_INSERT_MOVE);
    }
    else if (instr(EditedString, PanelcodeStop) == 0)
    {
      debug.SendAndFlush(_T("CMM=%s sending command ID_EXECUTE_STOP\n"), MACHINE_ID_STRING);
      send_command(m_pMachine, ID_EXECUTE_STOP);
    }
    else
    {
      tmpstr.Format(IDS_IFAC_CMMOS_UNUSED_PANELCODE, EditedString);
      send_message(m_pMachine, tmpstr);
    }
    SendNext(1);
  }
  else if (instr(EditedString, _T("CH")) == 0)
  {
      EditedString = EditedString.Mid(2);
      VERSION_INDEPENDENT_STSCANF(EditedString,_T( "M%1d%1dH%1d%1dP%1d%1dR%1d%1dT%1d%1d"),
        &MachinePresent, &MachineHomed,
        &HeadIsIndexable, &HeadHomed,
        &ProbeChangerPresent, &ProbeCurrentlyLoaded,
        &ProbeRackPresent, &NotUsed,
        &RotaryTablePresent, &RotaryTableHomed);
      debug.SendAndFlush(_T("CMM=%s MachinePresent=%1d, MachineHomed=%1d\n"), MACHINE_ID_STRING,
        MachinePresent, MachineHomed);
      debug.SendAndFlush(_T("CMM=%s HeadIsIndexable=%1d, HeadHomed=%1d\n"), MACHINE_ID_STRING,
        HeadIsIndexable, HeadHomed);
      debug.SendAndFlush(_T("CMM=%s ProbeChangerPresent=%1d, ProbeCurrentlyLoaded=%1d\n"), MACHINE_ID_STRING,
        ProbeChangerPresent, ProbeCurrentlyLoaded);
      debug.SendAndFlush(_T("CMM=%s ProbeRackPresent=%1d, NotUsed=%1d\n"), MACHINE_ID_STRING,
        ProbeRackPresent, NotUsed);
      debug.SendAndFlush(_T("CMM=%s RotaryTablePresent=%1d, RotaryTableHomed=%1d\n"), MACHINE_ID_STRING,
        RotaryTablePresent, RotaryTableHomed);
      needs_home = ((MachinePresent && !MachineHomed) || (HeadIsIndexable && !HeadHomed) || (RotaryTablePresent && !RotaryTableHomed));
      if (needs_home)
      {
        AddString(_T("AskHome:"));
      }
      else
      {
        if (ReadoutInterval > 0)
				{
					/************************************************************************/
					/*	Greg Guilbeau - 2011/07/28                                          */
					/*	The following line(s) have been modified to handle x64 conversion   */
					/************************************************************************/
					/*  					ManualPositionUpdate = SetTimer(NULL, 0, ReadoutInterval, TimerPositionUpdate);  */
					ManualPositionUpdate = SetTimer(NULL, 0, ReadoutInterval, (TIMERPROC)TimerPositionUpdate);
				}
        m_pMachine->MachineInitComplete();
        current_command = 0;
        send_message(m_pMachine, IDS_SM_CMM_OK);
        AddString(_T("SetCmmInitialState:"));
      }
    SendNext(1);
  }
  else if (instr(EditedString,_T( "CS")) == 0 || instr(EditedString,_T( "EC")) == 0 ) // added EC [2/22/2010 Michel]
  {
    if (current_command == DRIVER_INITIALIZE)
    {
      if (ReadoutInterval > 0)
			{
				/************************************************************************/
				/*	Greg Guilbeau - 2011/07/28                                          */
				/*	The following line(s) have been modified to handle x64 conversion   */
				/************************************************************************/
				/*  				ManualPositionUpdate = SetTimer(NULL, 0, ReadoutInterval, TimerPositionUpdate);  */
				ManualPositionUpdate = SetTimer(NULL, 0, ReadoutInterval, (TIMERPROC)TimerPositionUpdate);
			}
      m_pMachine->MachineInitComplete();
      current_command = 0;
      send_message(m_pMachine, IDS_SM_CMM_OK);
      AddString(_T("SetCmmInitialState:"));
    }
    else if (call_back_list.GetCount() )
    {
      cnt = call_back_list.GetCount();
    }
    else if (ErrorRecoveryInProgress)
    {
      ErrorRecoveryInProgress = FALSE;
    }            
    SendNext(1);
  }
  else if (0 == instr(EditedString,L"ReadWPSystem")) //[12/2/2008 Michel]
  {
    LoadWpStorage(EditedString);
    SendNext(1);
  }

  else if (instr(EditedString,_T( "MZSY6")) == 0)// ack for a buffered move with tag [8/22/2008 Michel]
  {
     cnt = call_back_list.GetCount();
     int mytag = -1;
     int itemsCount = 0;
     
     if (0 != cnt)
     {
     itemsCount = swscanf(EditedString,L"MZSY%d",&mytag);
     TRACE(L"------ going to check tag %d  (-1 is default) \n",mytag);
     if (0 == itemsCount || ! IsMoveTagValid(mytag))
     
     {
       TRACE(L"------ not processing callback list \n");
       debug.SendAndFlush(_T("CMM=%s *** controller notify treating a MZSY, not a valid one - skipping it \n"), MACHINE_ID_STRING);
     }
     else
        { 
		 Ccall_back Cb ;	// next 3 lines, replacing interface ptr list by a new one having tip inside [2/15/2010 Michel]
		 Cb = CallbackListRemoveHead(CString(L" Received MZSY6xx !"));
		 inter = Cb.pInterface;

		 debug.SendAndFlush(_T("CMM=%s *** controller notify treating a MZSY, removed callback = %x from list\n"), MACHINE_ID_STRING,inter);
         if(inter) 
         {
			 double HeadAngles[3]={0.0,0.0,0.0};	
			 AssignAngles(HeadAngles);

             debug.SendAndFlush(_T("CMM=%s *** controller notify treating a MZSY on %x\n"), MACHINE_ID_STRING,inter);
             inter->store_point(NULL, NULL, last_rotab, HeadAngles); // 1.34 changed from lastph9 [4/18/2012 Michel]
             if (active_call_back == inter)
                 active_call_back = 0;
          }
          else
          {
           debug.SendAndFlush(_T("CMM=%s *** controller notify treating a MZSY, callback null, getting on with it...\n"), MACHINE_ID_STRING);
          }
        }
      }
       else
       {
         debug.SendAndFlush(_T("CMM=%s *** controller notify treating a MZSY, nothing in list ... "), MACHINE_ID_STRING);
       }
     

     SendNext(1); //  [12/4/2008 Michel]
  }
  else if (instr(EditedString,_T( "ZAZZ")) == 0)
  {
    if((!ErrorRecoveryInProgress) && !bIgnoreZAZZ)
    {
      if (MODE_DCC != CurrentMachineMode)
      {
        bSilentError = TRUE;
      }
     my_show_error(IDS_CMMOS_USER_CANCEL);
     bSilentError = FALSE;
    }
  }
  else if (instr(EditedString, _T("ZZZZ")) == 0)
  {
    debug.SendAndFlush(_T("CMM=%S User paused operation from cmmos\n"), MACHINE_ID_STRING);
  }
  else if (instr(EditedString, _T("ZCZZ")) == 0)
  {
    debug.SendAndFlush(_T("CMM=%s User continued operation from cmmos\n"), MACHINE_ID_STRING);
    if (ErrorRecoveryInProgress)
    {
      errcnt = ErrorList.GetCount();
      strcnt = StringList.GetCount();
      if (!errcnt && !strcnt)
        ErrorRecoveryInProgress = FALSE;
    }
    SendNext(1);
  }
  else if ((instr(EditedString, _T("MS")) == 0) || (instr(EditedString, _T("PS")) == 0) ||
    (instr(EditedString, _T("SS")) == 0) || (instr(EditedString,_T( "CT")) == 0) ||
    (instr(EditedString, _T("RetractDistanceManProbe")) == 0)) //  [2/9/2005]
  {

//    if (call_back_list.GetCount() && (inter = call_back_list.RemoveHead()))
//       if (call_back_list.GetCount() && (inter = CallbackListRemoveHead(CString(L" Received MS orPS or SS or CT or RetractDistMan !") ) ) )//  [12/5/2008 Michel]
//     {
//       inter->store_point(NULL, NULL, last_rotab, lastph9);
//       if (active_call_back == inter)
//         active_call_back = 0;
//     }
    SendNext(1);
  }
  else if ((instr(EditedString, MOVES_COMPLETE_STRING)) == 0)
  {
    WaitEndPending = FALSE;
    SendNext(1);
  }

  else if ((instr(EditedString, CIRC_COMPLETE_STRING)) == 0) //  [12/5/2008 Michel]
  {
    Ccall_back Cb;  
    if (call_back_list.GetCount())
	{
		Cb = CallbackListRemoveHead(CString(L" Received CircMoveDOne  !") ); //  [12/5/2008 Michel]
		inter = Cb.pInterface;
		if(inter)
		{
          double HeadAngles[3]={0.0,0.0,0.0};	
		  AssignAngles(HeadAngles);

		  inter->store_point(NULL, NULL, last_rotab, HeadAngles); // V1.34 changed lastph9 to HeadAngles [4/18/2012 Michel]
		  if (active_call_back == inter)
			active_call_back = 0;
		}
	}
    SendNext(1);
  }
  
  else if (instr(EditedString, _T("PG")) == 0)
  {
    bIgnoreZAZZ = FALSE;// [12/8/2003]
    EditedString = EditedString.Mid(2);
    xyz_parse(EditedString, TmpPnt, TmpVec, global_active_tip);

	
	CPoint3 HeadPosition = TmpPnt;//  [2/16/2010 Michel]
	AddTipOffset(HeadPosition,global_active_tip);
  last_position = HeadPosition;

    if (active_call_back && ((active_call_back->type() == READPOSITION) || (current_command == DRIVER_MANUAL_SCAN)))
    {
//		active_call_back->store_point(TmpPnt, NULL);
		active_call_back->store_point(HeadPosition, NULL); //  [2/16/2010 Michel]
      if (active_call_back->type() == READPOSITION)
      {
        active_call_back = NULL;
        current_command = 0;
      }
    }
	if(bUseCaaWrist) global_has_manual_ph9 = FALSE; // DSE in saarlouis was seen as "manual" .... -> no tip insertion [4/16/2012 Michel]
	//	update_readouts(HeadPosition, m_pMachine, last_rotab, lastph9); //  [2/16/2010 Michel]
	double HeadAngles[3]={0.0,0.0,0.0};	
	AssignAngles(HeadAngles);
	update_readouts(HeadPosition, m_pMachine, last_rotab, HeadAngles); // V1.34 [4/18/2012 Michel]

	if (! call_back_list.GetCount())
    {
#ifdef BUILDVERSION_LESS_THAN_43
      global_commanded_position = HeadPosition;
#else
#pragma message( "Building for V43 and up ")
      global_commanded_position.SetPoint(m_pMachine,HeadPosition );
#endif
    }
    SendNext(1);
  }
  else if ((instr(EditedString, _T("MR") )== 0) || (instr(EditedString, _T("MM")) == 0))
  {
    // It's a hit, MH are manual hits, MM are DCC hits
    EditedString = EditedString.Mid(2);
	Ccall_back Cb; // next lines refactored [2/15/2010 Michel]
	
	CPCDtip_data* pMyTip = NULL;

    if (call_back_list.GetCount())
	{	
		Cb = CallbackListRemoveHead(CString(L" Received MR or MM !") );
	  inter = Cb.pInterface;

	  if (inter)
	   pMyTip = inter->tip;

	  xyz_parse(EditedString, TmpPnt, TmpVec, pMyTip);
	  AddTipOffset(TmpPnt,pMyTip); //  [2/16/2010 Michel]
	  //      if (!call_back_list.GetCount() && (current_command == DRIVER_MEASURE_POINT)) // removed since objects are destroyed by the  [3/9/2005]
	  {
		current_command = 0;
		active_call_back = NULL;
	  }
	  if(inter)
	  {
        double HeadAngles[3]={0.0,0.0,0.0};	
        AssignAngles(HeadAngles);

		inter->store_point(TmpPnt, NULL, last_rotab, HeadAngles);// V1.34 replaced usual lastph9 [4/18/2012 Michel]
	  }	
	}
    else if (active_call_back && (active_call_back->type() == MANUALHIT || active_call_back->type() == MEASUREPOINT))
    {
      xyz_parse(EditedString, TmpPnt, TmpVec, active_call_back->tip);
	  AddTipOffset(TmpPnt,active_call_back->tip); //  [2/16/2010 Michel]
	  double HeadAngles[3]={0.0,0.0,0.0};	
	  AssignAngles(HeadAngles);

      active_call_back->store_point(TmpPnt, TmpVec, last_rotab, HeadAngles);// V1.34 replaced usual lastph9 [4/18/2012 Michel]
      active_call_back = NULL;
      current_command = 0;
    }
    else
    {
      xyz_parse(EditedString, TmpPnt, TmpVec, global_active_tip);
	  AddTipOffset(TmpPnt,global_active_tip); //  [2/16/2010 Michel]
      double HeadAngles[3]={0.0,0.0,0.0};	
      AssignAngles(HeadAngles);
      send_hit(m_pMachine, TmpPnt, TmpVec, HeadAngles, last_rotab);// V1.34 replaced usual lastph9 [4/18/2012 Michel]
    }
    SendNext(1);
  }
  else if (instr(EditedString,_T( "PQ")) == 0)
  {
    EditedString = EditedString.Mid(2);
    ab_parse(EditedString, TmpPnt);
    if (fabs(lastph9[0] - TmpPnt[0]) > MIN_PH9_ANGLE_DIFF)
      lastph9[0] = TmpPnt[0];
    if (fabs(lastph9[1] - TmpPnt[1]) > MIN_PH9_ANGLE_DIFF) // Bug !! was cut and paste from upper line :-) [2/9/2005]
      lastph9[1] = TmpPnt[1];
    
    // If we have an indexable head the PQ response could be from the position timer
    // driver_move_ph9 also does a PQ as the final step

    if (bWaitingForPQDone && call_back_list.GetCount()) //  [10/28/2010 Michel]
	{
	//	Ccall_back Cb;
//		Cb = CallbackListRemoveHead(CString(L" Received PQ !") ); 
 //   bWaitingForPQDone = FALSE; //  [10/28/2010 Michel]
// 		if (inter = Cb.pInterface)  
//      {
//       debug.SendAndFlush(_T("CMM=%s Received PQ while waiting for PQDone, doing a store point on %x, lastph9 = %lf, %lf\n"), MACHINE_ID_STRING
//         ,inter,lastph9[0], lastph9[1]);
//       inter->store_point(NULL, NULL, last_rotab, lastph9);
//       if (active_call_back == inter)
//         active_call_back = 0;
// 		 }
	}
    SendNext(1);
  }
  else if ((instr(EditedString, _T("TSD"))) == 0)
  {
    EditedString = EditedString.Mid(2);
    StylusList.Empty();
    LastSelectedTipNumber = -1;
    bWaitingForTipDiameters = TRUE;
  }
  else if ((instr(EditedString, _T("TDN"))) == 0)
  {
    EditedString = EditedString.Mid(2);
    StylusList.ParseAndSaveStylus(EditedString);
    CmmosStylus* pSty = NULL;
    pSty = StylusList.GetAt(StylusList.GetCount()-1);
    if (pSty)
    {
      debug.SendAndFlush(_T("CMM=%s received TDN line, parsed into stylus #%d = %ls diam = %lf\r\n"), MACHINE_ID_STRING,
        pSty->GetIndex(),pSty->GetName(),pSty->GetDiameter());
    }
  }
  else if ((instr(EditedString,_T( "ListWPS"))) == 0) //  [12/2/2008 Michel]
  {
    AddToWPList(EditedString);
  }
  else if ((instr(EditedString,_T( "TE"))) == 0) //  [1/28/2011 Michel]
  {
    if (bWaitingForTipDiameters)
    {

      bWaitingForTipDiameters = FALSE;
      ErrorLoadingCmmosProbeConfig = FALSE;

      update_pcdmis_probe_data();
      update_cmmos_tip_number();
      current_command = 0;

    } //if not bWaitingForTipDiameters, just ignore , part of WKP sending
    debug.SendAndFlush(_T("CMM=%s End of 'TE' processing going to SendNext\n"), MACHINE_ID_STRING);
    SendNext(1);
    
  }
  else
  {
    ErrorMessage.LoadString(IDS_IFAC_CMMOS_UNKNOWN_RESPONSE);
    ErrorMessage += string;
    send_message(m_pMachine, ErrorMessage);
    //my_show_error(ErrorMessage);
  }

  goto loop;
}
#pragma warning (pop)
//
//-------------------------------------------------------------------
//
INTERFAC_EXTERN __declspec(dllexport) LRESULT HandleCommNotification(WPARAM wParam, LPARAM lParam)
{
  SET_PART_PROGRAM_POINTER;
  debug.SendAndFlush(_T("CMM=%s HandleCommNotification call_back=%x,call_back_count=%d,strlist_count=%d tip=%s\n"), MACHINE_ID_STRING, active_call_back, call_back_list.GetCount(), StringList.GetCount(), "NONE");
  if (TCPIP_port && (wParam == 0))
  {
    in_string += ((char*) lParam);
    return ((LRESULT)controller_notify(lParam));
  }
  if (TCPIP_port && (wParam == (UINT) -1))
  {
    // ready to send the next portion of the current output string
    SOCKET hSock = (SOCKET) lParam;
    int nTot = out_string.GetLength();
    char buffer[2000]; //  [11/22/2003]
    for (int i = 0;i<nTot;i++)
    {
      buffer[i] = ((char)out_string[i])& 0xff;
    }

    //    int nRet = send(hSock, out_string, nTot, 0);
    int nRet = send(hSock, buffer, nTot, 0);
    if (nRet == SOCKET_ERROR)
    {
      // Display significant errors
      int WSAErr = WSAGetLastError();
      if (WSAErr != WSAEWOULDBLOCK)
      {
        serious_tcp_error = TRUE;
        CString tt;
        tt.Format(IDS_IFAC_TCPIP_ERROR, WSAErr, WSAErr);
        PCDMessageBox(m_pPartProgram, tt);
      }
    }
    else if (nRet < nTot)
    {
      out_string = out_string.Mid(nRet);
    }
    else
    {
      out_string.Empty();
    }
  }
  return (TRUE);
}

//
//-------------------------------------------------------------------
//
INTERFAC_EXTERN __declspec(dllexport) void driver_stop_probe()
{
  SET_PART_PROGRAM_POINTER;
  debug.SendAndFlush(_T("CMM=%s driver_stop_probe call_back=%x,call_back_count=%d,strlist_count=%d tip=%s\n"), MACHINE_ID_STRING,
    active_call_back, call_back_list.GetCount(), StringList.GetCount(), _T("NONE"));
  /************************************************************************/
  /*	Greg Guilbeau - 2011/07/28                                          */
  /*	The following line(s) have been modified to handle x64 conversion   */
  /************************************************************************/
  /*    int count = call_back_list.GetCount();  */
  INT_PTR count = call_back_list.GetCount();
  current_command = 0;
  CallbackListRemoveAll(CString(L" Driver_stop_probe"));
  //call_back_list.RemoveAll();
  StringList.RemoveAll();
  active_call_back = 0;
  if (count || (CurrentMachineMode == MODE_DCC) || (RunCmmosCal && (current_command == DRIVER_CALIBRATE_PROBE)))
  {
    if (!ManualMachineType)
    {
      MyWriteCommBlock(controller,_T( "ZA\r\n"));
      //CallbackListAddTail((CPCDinterface*)NULL,CString(L" driver_stop_probe- sending ZA stop command, 'ZAZZ ???' as answer"));
//      call_back_list.AddTail((CPCDinterface *) NULL); //   [3/10/2005]
      Sleep(100);
      MyWriteCommBlock(controller,_T( "ZC\r\n"));
      //CallbackListAddTail((CPCDinterface*)NULL,CString(L" driver_stop_probe- sending ZC clear command, '????' as answer"));
//      call_back_list.AddTail((CPCDinterface *) NULL); //   [3/10/2005]
    }
  }
  CurrentMachineMode = MODE_UNKNOWN;
  go_manual();
  bWaitingForPQDone = FALSE; //  [10/28/2010 Michel]
  if (!ManualMachineType)
  {
    FirstWaitEnd = TRUE;
    ErrorRecoveryInProgress = TRUE;// changed to TRUE [12/4/2008 Michel] // and moved here for man machines [9/27/2010 Michel]
  }

  WaitEndPending = FALSE;
  cur_move_speed = 0.0;
  cur_touch_speed = 0.0;
  cur_search_dist = 0.0;
  //  cur_probe_name = PNAME_ERROR_RESET; // why ?  [12/9/2003]
}

//
//-------------------------------------------------------------------
//
INTERFAC_EXTERN __declspec(dllexport) BOOL driver_in_manual()
{
  SET_PART_PROGRAM_POINTER;
  debug.SendAndFlush(_T("CMM=%s driver_in_manual call_back=%x,call_back_count=%d,strlist_count=%d tip=%s\n"), MACHINE_ID_STRING,
    NULL, call_back_list.GetCount(), StringList.GetCount(), _T("NONE"));
  if (!call_back_list.GetCount() && (CurrentMachineMode == MODE_MANUAL))
    return (TRUE);
  return (FALSE);
}

//
//-------------------------------------------------------------------
//
INTERFAC_EXTERN __declspec(dllexport) void driver_read_ph9(CPCDinterface * call_back)
{
  SET_PART_PROGRAM_POINTER;
  debug.SendAndFlush(_T("CMM=%s driver_read_ph9 call_back=%x,call_back_count=%d,strlist_count=%d tip=%s\n"), MACHINE_ID_STRING,
    call_back, call_back_list.GetCount(), StringList.GetCount(), "NONE");
  // we get here each time we exit from probe dialog ....
  ErrorLoadingCmmosProbeConfig = FALSE; // will trigger an error on reread soon ![12/8/2003]
  if (call_back)
    call_back->store_point(lastph9);
  return;
}

//
//-------------------------------------------------------------------
//
INTERFAC_EXTERN __declspec(dllexport) void driver_move_ph9(CPCDinterface * call_back, CPCDtip_data * tip)
{
  SET_PART_PROGRAM_POINTER;
  CString tmpstr;
//  CmmosStylus *lpStylus;
  double goal_a = -999;
  double goal_b = -999;
  BOOL bMustWait = FALSE;
  BOOL bProbeChanged = FALSE;

  debug.SendAndFlush(_T("CMM=%s driver_move_ph9 call_back=%x,call_back_count=%d,strlist_count=%d tip=%s\n"), MACHINE_ID_STRING,
    call_back, call_back_list.GetCount(), StringList.GetCount(), tip ? tip->get_id() :_T( "NONE"));
  
  if (! tip )
  {
    if (call_back)
      call_back->store_point();
    return;
  }

  bProbeChanged = set_cmmos_active_probe(); 

  // move probe if it's right.

  goal_a = tip->get_ph9a();
  goal_b = tip->get_ph9b();

  debug.SendAndFlush(_T("CMM=%s a,b user=%lf,%lf\n"), MACHINE_ID_STRING, goal_a,goal_b);

  if (! call_back_list.GetCount())
  {
#ifdef BUILDVERSION_LESS_THAN_43
    global_interface_position = last_position;
#else
#pragma message( "Building for V43 and up ")
    global_interface_position.SetPoint(m_pMachine, last_position);
#endif
  }

  if (UseCmmosCalData)
  {
    int tipnumber = tip->get_tip();
    CmmOsSelectTip(tipnumber);
    SetCurrentTipValues(tipnumber,goal_a,goal_b);
  }

  // send comand to rotate the ph9
  if (!ManualMachineType && (global_has_ph9 || bUseCaaWrist))
  {
    if(NeedToRotate(goal_a,goal_b) || bProbeChanged)
    {
      go_dcc();// moved here from outside this test... [9/27/2010 Michel]
      mapHeadAngles(goal_a,goal_b); //goes to e.g. Zeiss convention 
      debug.SendAndFlush(_T("CMM=%s sending head to a,b machine=%lf,%lf\n"), MACHINE_ID_STRING, goal_a,goal_b);
      tmpstr.Format(_T("PPA%lfB%lf\r\n"), goal_a, goal_b);
      MyWriteCommBlock(controller, tmpstr);
      bMustWait = TRUE;
    }
  }

  if (!ManualMachineType && (global_has_ph9 || bUseCaaWrist) && bMustWait) //get back the actual angles if needed / wait
  {
    MyWriteCommBlock(controller, _T("PQ\r\n"));  
    
    CallbackListAddTail(call_back,tip,CString(L" driver_move_ph9 sending PQ command, 'PQxxx' as answer"));
    active_call_back = call_back;
    current_command = DRIVER_MEASURE_POINT;
    bWaitingForPQDone = TRUE;
    // #####
    if (!ManualMachineType)
    {
      AddString(_T("WaitEnd:"));
    }
    //\#####

  }
  else //  [9/27/2010 Michel]
  {
    if (call_back)
    {
      call_back->store_point();
    }
  }
 
  
  return;
}

//
//-------------------------------------------------------------------
//
INTERFAC_EXTERN __declspec(dllexport) void driver_send_readouts(double* xyz, double angle, double* ab_angle)
{
  SET_PART_PROGRAM_POINTER;
}

//
//-------------------------------------------------------------------
//
INTERFAC_EXTERN __declspec(dllexport) void driver_calibrate_probe()
{
  SET_PART_PROGRAM_POINTER;

  CString tmpstr;
  CString tmpmsg;
  CString pname;
  int id_response;

  current_command = DRIVER_CALIBRATE_PROBE;

  if (global_active_probe)
  {
    pname = global_active_probe->file_name;
    capitalize(pname);
    ErrorLoadingCmmosProbeConfig = FALSE;
    // Select the desired probe
    tmpstr.Format(_T("LC%s\r\n"), pname);
    MyWriteCommBlock(controller, tmpstr);
    //CallbackListAddTail((CPCDinterface*)NULL,CString(L" driver_calibrate_probe - sending LC command, 'CS' as answer"));
//    call_back_list.AddTail((CPCDinterface *) NULL); // Was missing ! [3/9/2005]
    // Run the DCC cal procedure if desired
    if (RunCmmosCal && !ManualMachineType) // manual machine handling added [9/28/2010 Michel]
    {
      MyWriteCommBlock(controller,_T( "CC\r\n"));
      call_back_list.AddTail(Ccall_back()); // Was missing ! [3/9/2005] // will put null ptr and tip ptr. [2/15/2010 Michel]
    }
    // Retrieve the cal data
    MyWriteCommBlock(controller, _T("GX\r\n"));
    if (RunCmmosCal)
      tmpstr.LoadString(IDS_CMMOS_RUNNING_QUAL);
    else
      tmpstr.LoadString(IDS_CMMOS_RETRIEVING_QUAL);
    tmpmsg.Format(tmpstr, pname);
    PCDMessageBox(m_pPartProgram, tmpmsg);

    // In handling the final TE response after retrieving the data we reset current_command=0
    // so if we're here and current_command is still DRIVER_CALIBRATE_PROBE it means they
    // clicked OK before we have finished retrieving all the data (usually because they asked
    // it to run the calibration before retrieving the data and then got impatient)

    tmpstr.LoadString(IDS_CMMOS_PROBEQUAL_NOT_DONE);
    tmpmsg.Format(tmpstr, pname);
    while (current_command == DRIVER_CALIBRATE_PROBE)
    {
      id_response = PCDMessageBox(m_pPartProgram, tmpmsg, MB_OKCANCEL);
      if (id_response == IDCANCEL)
        current_command = 0;
    }

  }

  current_command = 0;
}

//
//-------------------------------------------------------------------
//
INTERFAC_EXTERN __declspec(dllexport) void driver_move_rotab(CPCDinterface * call_back, double angle, int direction, CPCDtip_data * tip, double speed)
{
  SET_PART_PROGRAM_POINTER;
  debug.SendAndFlush(_T("CMM=%s driver_move_rotab call_back=%x,call_back_count=%d,strlist_count=%d tip=%s\n"), MACHINE_ID_STRING,
    call_back, call_back_list.GetCount(), StringList.GetCount(), tip ? tip->get_id() : _T("NONE"));
  debug.SendAndFlush(_T("CMM=%s move rotab angle=%lf direction=%d speed=%lf\n"), MACHINE_ID_STRING, angle, direction, speed);
  if (call_back)
    call_back->store_point();
}

//
//-------------------------------------------------------------------
//
INTERFAC_EXTERN __declspec(dllexport) void driver_change_tool(CPCDinterface * call_back, double* drop_off, double* pick_up,
                                              double* in_mount_point, int datum_mode, double in_ph9a, double in_ph9b,
                                              CPCDtip_data * tip, double speed)
{
  SET_PART_PROGRAM_POINTER;
  debug.SendAndFlush(_T("CMM=%s driver_change_tool call_back=%x,call_back_count=%d,strlist_count=%d tip=%s\n"), MACHINE_ID_STRING,
    call_back, call_back_list.GetCount(), StringList.GetCount(), tip ? tip->get_id() : _T("NONE"));
  if (call_back)
    call_back->store_point();
}

//
//-------------------------------------------------------------------
//

INTERFAC_EXTERN __declspec(dllexport) void driver_notify_tool(CPCDinterface* call_back,void* pToolName)
{
  //action
  sLastProbeFromChangeTool = *reinterpret_cast<CString*>(pToolName);
  m_pPartProgram=m_pMachine->m_pPartProgram;
  debug.SendAndFlush(_T("CMM=%s driver_notify_tool call_back=%x,call_back_count=%d,strlist_count=%d tool=%s\n"), MACHINE_ID_STRING,
    call_back, call_back_list.GetCount(), StringList.GetCount(),sLastProbeFromChangeTool);
  
  
  // nothing else to do
  if (call_back)
    call_back->store_point();
  return;
}
//
//-------------------------------------------------------------------
//
INTERFAC_EXTERN __declspec(dllexport) void driver_cmm_setup_dialog(CWinApp * main_app)
{
  SET_PART_PROGRAM_POINTER;
  CTab options(m_pPartProgram, IDS_CMM_OPTIONS);
  debug.SendAndFlush(_T("CMM=%s driver_cmm_setup_dialog call_back=%x,call_back_count=%d,strlist_count=%d tip=%s\n"), MACHINE_ID_STRING,
    NULL, call_back_list.GetCount(), StringList.GetCount(), _T("NONE"));
  if (options.DoModal() == IDOK)
  {
    myversion = _ttof(MACHINE_INTERFACE_VERSION);
    if (TCPOpenDelay < MIN_TCPOPENDELAY)
      TCPOpenDelay = MIN_TCPOPENDELAY;
    if (CtrlTCPTimeout < MIN_CTRLTCPTIMEOUT)
      CtrlTCPTimeout = MIN_CTRLTCPTIMEOUT;
    SaveDefaults(global_main_winapp, ini_info, m_pMachine->add_cmm(SECTION_HEADER));
  }
  debug.SendAndFlush(_T("CMM=%s Exit from setup dialog\n"), MACHINE_ID_STRING);
  debug.SendAndFlush(_T("CMM=%s PCDMIS    version info: %s\n"), MACHINE_ID_STRING, PCDLRNGetVersStr());
  debug.SendAndFlush(_T("CMM=%s Interface version info: %s\n"), MACHINE_ID_STRING, driver_get_version());
  INIShowActiveValues(ini_info, m_pMachine->add_cmm(SECTION_HEADER));
}

//
//-------------------------------------------------------------------
//
INTERFAC_EXTERN __declspec(dllexport) void driver_begin_dcc_scan(CPCDinterface * call_back, CPCDtip_data * tip)
{
  SET_PART_PROGRAM_POINTER;
  debug.SendAndFlush(_T("CMM=%s driver_begin_dcc_scan call_back=%x,call_back_count=%d,strlist_count=%d tip=%s\n"), MACHINE_ID_STRING,
    call_back, call_back_list.GetCount(), StringList.GetCount(), tip ? tip->get_id() :_T( "NONE"));
  if (call_back)
  {
    call_back->sbit(COMPLETE, 1);
    call_back->sbit(PAUSE, 0);
    call_back->sbit(TEMPPAUSE, 0);
    call_back->store_point();
  }
}

INTERFAC_EXTERN __declspec(dllexport) void driver_add_entries()
{
  AddEntries(ini_info, m_pMachine->add_cmm(SECTION_HEADER));
}

_declspec(dllexport) int driver_special_learnmode_option()
{
  // Whether or not a particular interface provides an implementation
  // for this function is optional.  If implemented the interface can
  // then modify the behavior of PC-DMIS with respect to the
  // "UseSpecialLearnmodeProbeParameterHandling" registry settings
  //
  // NOTE: This function will be called before driver_initialize()
  //
  // This function should return one of the following values:
  //
  // SPECIALLEARNMODE_OPTION_ALLOWED_WITH_DEFAULT_ON
  //      - This will allow use of the registry setting and will provide a
  //        default of on (equivalent to registry setting to 1).  This
  //        default only applies if a value has not explicitly been set
  //        with the settings editor.
  // SPECIALLEARNMODE_OPTION_ALLOWED_WITH_DEFAULT_OFF 
  //      - This will allow use of the registry setting and will provide a
  //        default of off (equivalent to registry setting to 0).  This
  //        default only applies if a value has not explicitly been set
  //        with the settings editor.
  // SPECIALLEARNMODE_OPTION_FORCED_ON
  //      - By returning this the special learnmode probe parameter handling
  //        will be used regardless of the actual registry setting.
  // SPECIALLEARNMODE_OPTION_FORCED_OFF
  //      - By returning this the special learnmode probe parameter handling
  //        will NOT be used regardless of the actual registry setting.

  int  rc = SPECIALLEARNMODE_OPTION_FORCED_OFF;
  return rc;
}
//
//-------------------------------------------------------------------
//
INTERFAC_EXTERN __declspec(dllexport) void driver_wait_end()
{
  SET_PART_PROGRAM_POINTER;
  //  debug.SendAndFlush(_T("CMM=%s driver_wait_end \n"), MACHINE_ID_STRING);
}

//
//-------------------------------------------------------------------
//
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
  if (dwReason == DLL_PROCESS_ATTACH)
  {
    TRACE(_T("CMMOS.DLL Initializing!\n"));
    m_pMachine = global_machine_active;
    SET_PART_PROGRAM_POINTER;

    // Extension DLL one-time initialization
    AfxInitExtensionModule(cmmosDLL, hInstance);

    // Insert this DLL into the resource chain
    new CDynLinkLibrary(cmmosDLL);
  }
  else if (dwReason == DLL_PROCESS_DETACH)
  {
    // driver_close();
    TRACE(_T("CMMOS.DLL Terminating!\n"));
  }
  return 1;   // ok
}


