Hi everyone,
this took me a few days to figure out...
I must thank rhtuttle for pointing me in the right direction
and ART for giving me a list of commands and a few more tips.
If you found this helpful make sure to thank those guys.
The following code will
Connect to Mach 3
reset mach
, home the axes
, close any open file
, load a specified file
,rewind it
, set feedrate to 300%
and CycleStart
If anyone has a full list with the button numbers that would be a great help, Mach needs to ref all home before cyclestart idk what the buttonr is though...
If you spot any errors or have suggestions or found something cool to add please post them below.
What should be added is some way to check if mach is ready to cyclestart that usleep is ugly and also reset should check if the reset already occurred...
Compiled In Code::Blocks with the MinGW compiler->linker settings
-lole32
-loleaut32
-luuid
#include <iostream>
#include <ole2.h>
#include <Windows.h>
#include <Objbase.h>
#include <OleAuto.h>
#include <cstring>
#include <string.h>
#include <unistd.h>
CLSID clsid,clsid2;
bool connected;
using namespace std;
TCHAR *GetLastErrorMessage(DWORD last_error)
{
static TCHAR errmsg[512];
if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,0,last_error,0,errmsg,511,NULL)) {
return (GetLastErrorMessage(GetLastError()));
}
return errmsg;
}
HRESULT OLEMethod(int nType, VARIANT *pvResult,
IDispatch *pDisp,LPOLESTR ptName, int cArgs...)
{
if(!pDisp) return E_FAIL;
va_list marker;
va_start(marker, cArgs);
DISPPARAMS dp = { NULL, NULL, 0, 0 };
DISPID dispidNamed = DISPID_PROPERTYPUT;
DISPID dispID;
char szName[200];
// Convert down to ANSI
WideCharToMultiByte(CP_ACP, 0, ptName, -1, szName, 256, NULL, NULL);
// Get DISPID for name passed...
HRESULT hr= pDisp->GetIDsOfNames(IID_NULL, &ptName, 1,
LOCALE_USER_DEFAULT, &dispID);
if(FAILED(hr)) {
return hr;
}
// Allocate memory for arguments...
VARIANT *pArgs = new VARIANT[cArgs+1];
// Extract arguments...
for(int i=0; i<cArgs; i++) {
pArgs[i] = va_arg(marker, VARIANT);
}
// Build DISPPARAMS
dp.cArgs = cArgs;
dp.rgvarg = pArgs;
// Handle special-case for property-puts!
if(nType & DISPATCH_PROPERTYPUT) {
dp.cNamedArgs = 1;
dp.rgdispidNamedArgs = &dispidNamed;
}
// Make the call!
hr = pDisp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT,
nType, &dp, pvResult, NULL, NULL);
if(FAILED(hr)) {
return hr;
}
// End variable-argument section...
va_end(marker);
delete [] pArgs;
return hr;
}
void CloseMach(LPDISPATCH &InterfPtr)
{
HRESULT ivres;
DISPPARAMS dp = { NULL, NULL, 0, 0 };
ivres=InterfPtr->Invoke(0x4, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD, &dp, NULL, NULL, NULL);
cout<<GetLastErrorMessage(ivres)<<endl;
}
void LoadFile(LPDISPATCH &InterfPtr, OLECHAR*Filename)
{
HRESULT ivres;
VARIANT vtFileName;
vtFileName.vt = VT_BSTR;
vtFileName.bstrVal = SysAllocString(Filename);
ivres=OLEMethod(DISPATCH_METHOD, NULL, InterfPtr, L"LoadGCodeFile", 1, vtFileName ); // reversed order is using more options
cout<<GetLastErrorMessage(ivres)<<endl;
}
void CloseFile(LPDISPATCH &InterfPtr)
{
HRESULT ivres;
DISPPARAMS dp = { NULL, NULL, 0, 0 };
ivres=InterfPtr->Invoke(0x2, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD, &dp, NULL, NULL, NULL);
cout<<GetLastErrorMessage(ivres)<<endl;
}
void CycleStart(LPDISPATCH &InterfPtr)
{
HRESULT ivres;
DISPPARAMS dp = { NULL, NULL, 0, 0 };
ivres=OLEMethod(DISPATCH_METHOD, NULL, InterfPtr, L"CycleStart", NULL, dp );
cout<<"CycleStart"<<GetLastErrorMessage(ivres)<<endl;
}
void RewindGcode(LPDISPATCH &InterfPtr)
{
HRESULT ivres;
DISPPARAMS dp = { NULL, NULL, 0, 0 };
ivres=OLEMethod(DISPATCH_METHOD, NULL, InterfPtr, L"RewindGcode", NULL, dp );
cout<<GetLastErrorMessage(ivres)<<endl;
}
void SetFRO(LPDISPATCH &InterfPtr,double FeedOverride)
{
HRESULT ivres;
VARIANT FRO;
FRO.vt = VT_R8;
FRO.dblVal = FeedOverride;
ivres=OLEMethod(DISPATCH_METHOD, NULL, InterfPtr, L"SetFRO", 1, FRO ); // reversed order is using more options
cout<<GetLastErrorMessage(ivres)<<endl;
}
void Reset(LPDISPATCH &InterfPtr)
{
HRESULT ivres;
VARIANT BNnr;
BNnr.vt = VT_I2;
BNnr.iVal = 21;
ivres=OLEMethod(DISPATCH_METHOD, NULL, InterfPtr, L"DoButton",1,BNnr ); // reversed order is using more options
cout<<GetLastErrorMessage(ivres)<<endl;
}
IDispatch* GetScriptDispatch(LPDISPATCH &InterfPtr)
{
HRESULT ivres;
VARIANT result;
VariantInit(&result);
ivres= OLEMethod(DISPATCH_PROPERTYGET, &result, InterfPtr, L"GetScriptDispatch", NULL, NULL ); // reversed order is using more options
return(result.pdispVal);
cout<<GetLastErrorMessage(ivres)<<endl;
}
void HomeAll(LPDISPATCH &InterfPtr)
{
HRESULT ivres;
VARIANT BNnr;
BNnr.vt = VT_I2;
BNnr.iVal = 24;
ivres=OLEMethod(DISPATCH_METHOD, NULL, InterfPtr, L"DoButton",1,BNnr ); // reversed order is using more options
cout<<GetLastErrorMessage(ivres)<<endl;
BNnr.vt = VT_I2;
BNnr.iVal = 23;
ivres=OLEMethod(DISPATCH_METHOD, NULL, InterfPtr, L"DoButton",1,BNnr ); // reversed order is using more options
cout<<GetLastErrorMessage(ivres)<<endl;
BNnr.vt = VT_I2;
BNnr.iVal = 22;
ivres=OLEMethod(DISPATCH_METHOD, NULL, InterfPtr, L"DoButton",1,BNnr ); // reversed order is using more options
cout<<GetLastErrorMessage(ivres)<<endl;
BNnr.vt = VT_I2;
BNnr.iVal = 25;
ivres=OLEMethod(DISPATCH_METHOD, NULL, InterfPtr, L"DoButton",1,BNnr ); // reversed order is using more options
cout<<GetLastErrorMessage(ivres)<<endl;
}
void OnBnClickedConnect()
{
LPUNKNOWN lpUnk;
LPDISPATCH lpDispatch=NULL;
LPDISPATCH Scripter=NULL;
//COleException e;
HRESULT res;
// Initialize COM for this thread...
CoInitialize(NULL);
// Get CLSID for our server...
if (CLSIDFromProgID(L"Mach4.Document", &clsid) != NOERROR)
{
cout << "error CLSIDFromProgID" << endl;
return;
}
if (res = GetActiveObject( clsid, NULL, &lpUnk) == NOERROR) {
HRESULT hr = lpUnk->QueryInterface(IID_IDispatch,
(LPVOID*)&lpDispatch); //get a pointer to the object's IDispatch interface.
lpUnk->Release();
if (hr == NOERROR) {
connected = true;
cout << "connected" << endl;
Scripter=GetScriptDispatch(lpDispatch);
if(Scripter!=NULL) {
cout << "gotScripter" << endl;
Reset(Scripter);
HomeAll(Scripter);
CloseFile(lpDispatch);
LoadFile(lpDispatch, L"C:\\R1.ngc");
RewindGcode(lpDispatch);
SetFRO(lpDispatch,300.0);
usleep(300000);
CycleStart(lpDispatch);
}
}
//m_Status = "Connected to Mach3.";
// UpdateData(false);
// SetTimer( NULL,1, 200, NULL );
return ;
} else {
cout << "error GetActiveObject" << endl;
}
//m_Status = "No Connection to Mach3.";
//UpdateData(false);
// Uninitialize COM for this thread...
CoUninitialize();
if(lpDispatch!=NULL){
free(lpDispatch);
}
if(Scripter!=NULL){
free(Scripter);
}
}
int main()
{
connected = false;
OnBnClickedConnect();
return 0;
}
Mach3 should be running. If this fails to connect make sure you got all registry keys...
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\CLSID\{CA7992B2-2653-4342-8061-D7D385C07809}]
@="Mach4.Document"
[HKEY_CLASSES_ROOT\CLSID\{CA7992B2-2653-4342-8061-D7D385C07809}\InprocHandler32]
@="ole32.dll"
[HKEY_CLASSES_ROOT\CLSID\{CA7992B2-2653-4342-8061-D7D385C07809}\LocalServer32]
@="C:\\Mach3\\Mach3.exe"
[HKEY_CLASSES_ROOT\CLSID\{CA7992B2-2653-4342-8061-D7D385C07809}\ProgID]
@="Mach4.Document"
[HKEY_CLASSES_ROOT\Mach4.Document]
@="Mach4.Document"
[HKEY_CLASSES_ROOT\Mach4.Document\CLSID]
@="{CA7992B2-2653-4342-8061-D7D385C07809}"
Thanks to machpendant for that regfix.
Use at your own risk...