Machsupport Forum

Mach Discussion => Mach SDK plugin questions and answers. => Topic started by: CWare on March 21, 2016, 12:12:13 PM

Title: [TUTORIAL] Mach3 Automation C++ ConsoleApp Without VisualStudio Mach3 exControl
Post by: CWare on March 21, 2016, 12:12:13 PM
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



Code: [Select]
#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...


Code: [Select]
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...
Title: Re: [TUTORIAL] Mach3 Automation C++ ConsoleApp Without VisualStudio Mach3 exControl
Post by: kccheng on April 09, 2019, 10:35:32 AM
Hi,

Thanks for this good example.  It's a good start point.
I have a question.   In this example, there are some OLEMethod()  such as

LoadGCodeFile
CycleStart
RewindGcode
SetFRO
DoButton
GetScriptDispatch


where can I find the document of the above commands ??  Is there a complete list of supported commands ?
thanks a lot.

Regards,
KC
Title: Re: [TUTORIAL] Mach3 Automation C++ ConsoleApp Without VisualStudio Mach3 exControl
Post by: reuelt on April 09, 2019, 10:31:48 PM
Link to MACH3 OEM codes for Buttons, LED's & DRO's   
http://www.machsupport.com/forum/index.php?action=dlattach;topic=9312.0;attach=6986
Title: Re: [TUTORIAL] Mach3 Automation C++ ConsoleApp Without VisualStudio Mach3 exControl
Post by: kccheng on April 09, 2019, 10:46:00 PM
That's helpful, Thanks