CamBam
News:
 
*
Welcome, Guest. Please login or register.
Did you miss your activation email?
June 16, 2019, 13:30:21 pm


Login with username, password and session length


Pages: [1] 2 3 4
  Print  
Author Topic: Need help to start scripting (especially automated MOP creation)  (Read 6853 times)
gmoo
Droid
**
Offline Offline

Posts: 79

Jam it !


View Profile
« on: November 14, 2018, 13:47:11 pm »

Hi everybody,

my name´s Bernhard (54), once upon a time I´ve been studying informatics.
I am using a selfmade CNC Router for my hobby.
I´m from germany, so please excuse my poor english.
I´m using CAMBAM since summer of 2017 and after learning how to use I really like it.

Now I want to start writing a few simple scripts in order get a speedup in my workflow.

But where to start ... I see the examples in VB and Python, and I see an API
to control Cambam. The API´s documentation is a little ... let´s say thin.
Maybe I am not asking the proper questions.

So it´s hard for me to start. I am speaking C/C++,
but not really Python or VB. But I can read it, (often) understand an existing code
and I can change/adapt it.


My workflow is : Creating DXF file using QCAD, put it into Cambam an create the GCode.
Works usually fine.
But I see me doing always the same stuff in Cambam, after changing the DXF file
doing the same things again and again.


What do I want to program / automize ?

I have usually 2.5D stuff, not very complex.
So my idea is :

Putting some informations for creating the MOPs and their order into the DXF-file´s layernames.
(f.e. MOP order, milling depth, number of holding tabs, CAM-Style ...)
Using a cambam script parse this information and create the MOPs. Done.

Most things I realized by changing the demo-script "Automate.py" after
reading the whole internet to get snippets of information, the most from this forum here.


Now my questions :


- Python or VB ? (python seems to easier to me.)

- is there a way to get some Intellisense/Autocompletion during script writing ?

- Actually I am able create a new part and naming it by scripting.
  But I did not find a way to overload the active part´s CAM-Style.
  (dealing with MOPs I can)
 
- Using python I could not find a way to read out, alter and rewrite the holding tab´s properties of a MOP.
  (especially number of tabs and setting to "auto" 

 
If you want to, I can put here my rudimentary quick´n´dirty script.
And I have to create a simple DXF file for demonstrating.


I would be glad get a little help. 


Best regards

Bernhard
Logged

best regards

Bernhard
lloydsp
CNC Jedi
*****
Offline Offline

Posts: 7886



View Profile
« Reply #1 on: November 14, 2018, 14:53:35 pm »

Bernhard,

I once wrote extensions, plugins, and post-build processors.  But now I'm 'obsolete' by the quality of scripts and automation these guys do.

I think the conversion to DXF parts has already been done by someone, and the issue of holding tabs discussed.  If I recall correctly, their properties are not exposed, or are somehow 'hidden' from the interface components we use for scripts and post-build processors.  (obviously, their definitions must be somewhere in CB's libraries, or CamBam couldn't access them, either! <grin>)

And, sir, your English is better than that of many Americans who speak it as their only language, so you need not apologize!

Lloyd
Logged

"Pyro for Fun and Profit for More Than Fifty Years"
EddyCurrent
CNC Jedi
*****
Offline Offline

Posts: 3950



View Profile
« Reply #2 on: November 14, 2018, 17:50:39 pm »

Bernhard,

As you say, the API document is thin, I think most script/plugin authors do the same as myself which is to inspect what others have written and try to build upon that.

If you are writing scripts then Python seems to be the most powerful and concise.

To get more information on the API or to write plugins you should install,   Visual Studio Community 2017 C#
You will be fine with C# and Visual Studio includes an "Object Browser" tab where all the methods etc. can be exposed from the CamBam dll files.

Another way is to use ILspy; https://github.com/icsharpcode/ILSpy, this gives access to the code within CamBam dll files and also plugins that others have written. The C# code can be exported from ILSpy to a Visual Studio 'solution' however on occasion some manual work is required to fix small translation issues.

I use Notepad++ for scripts but as you say, unlike Visual Studio, there is no Intellisense etc.

Please post your script, it is highly likely that someone has the answers you seek.

Logged

Made in England
gmoo
Droid
**
Offline Offline

Posts: 79

Jam it !


View Profile
« Reply #3 on: November 14, 2018, 19:14:13 pm »

@Lloyd : Thank you, especially for the "Sir" !  Grin



Thanks, Eddy,

I´ll try that tomorrow, here in Germany is now time to open beer and watch TV.  Grin

But I was not lazy in between and translated the comments in my "magic script" into english
for a better readability for most of you.

A DXF file for demonstration I will create tomorrow, it should be completely free of my personal CAMBAM settings.

best regards

Bernhard

Code:
#
#  _BG_AUTO_MAGIC.py
#
# create MOPs based upon infos written in DXF Layer names
#
# Layer 0 will be ignored
# only layers using names beginning with a number can auto-create MOPs
#
#
# LAYERNAME STYLE : Number Name ProfileType [options]
# ProfileTypes (not case-senitive)
#         innen (=inside)
#         aussen (=outside)
#         tasche (=pocket)
#         gravur (=engrave)
#         NT (Neues Teil anlegen / create a new Part)
#
# Options :
#            Caaaaa  CAM-Style  
#            Tn.nnn  Tiefe, wird automatisch negativ / Target depth, will be converted to negtive value
#            Zn.nnn  Zustellung / depth increment    
#            An.nnn  Aufmass    / roughing clearance                    
# x          Hn      n Haltestege generieren / generate n holding tabs and set Tab method to automatic
#            Wn.nnn  Werkstückoberfläche / Stock Surface
#            In      Innenecken räumen 0=nein  1=ja / Corner overcut 0=no 1=yes
#            Sn.nnn  seitliche Zustellung 0.00  ... 1.00  / stepover
#            Vnnnnn  Vorschub / feed speed
#            Nnnnnn  Drehzahl / spindle speed
#            Dn.nnnn Durchmesser / Tool diameter
# x          Ra      O-Obere Ebene zuerst    T-Tiefe zuerst  / Cut ordering  a=O Level First a=T Depth First    
#
# Special Case : Layer "00 name [options]"
# x             - Name sets name of 1st Part
#               - no ProfileType
#               - will set the default values of MACHINING, no MOP is generated
# x          Mn.nnn  Materialdicke / stock size Z
#            Baaaaa  Bibliothek / MACHINING default CAM library (must exist, there is no check)
#            Caaaaa  CAM-Style  / MACHINING default CAM styl (must exist, there is no check)
#              
# x = not implemented
#

from CamBam.CAM import *
from CamBam.UI import *
from CamBam.Values import *

TypeList = ["INNEN", "AUSSEN", "TASCHE", "BOHREN", "GRAVUR","NT"]



# The 'doc' global variable is always the CADFile before any file opens,
# so we create a new varialbe pointing to the newly opened active file...
newdoc = CamBamUI.MainUI.ActiveView.CADFile




#REMEMBER !!! always ignore layer "0" !!!



################################################################################
# first of all, let´s re-sort the layers by the numbers given in the layer´s name
################################################################################

for i in range (0, newdoc.Layers.Count-1) :
    # if a number is defined, extract it
    if(newdoc.Layers[i].Name !="0"):  # ignore layer "0"
        TokenList = newdoc.Layers[i].Name.split()
        if (TokenList[0].isnumeric()) :
            ActualNumber = int(TokenList[0])
            # run through the following layers and look for smaller numbers given in the layer´s name
            for Index in range (i+1,newdoc.Layers.Count) :
                if(newdoc.Layers[i].Name !="0"):  # ignore layer "0"
                    # extract defined number from layernames
                    TokenList = newdoc.Layers[Index].Name.split()
                    if (TokenList[0].isnumeric()) :
                        FollowingNumber = int(TokenList[0])
                        #compare following to actual number
                        if (FollowingNumber < ActualNumber) :
                            #if smaller, change layer order
                            newdoc.ChangeLayerOrder(Index,i)
                            ActualNumber = FollowingNumber


                    
                    
################################################################################
# Remove all parts
################################################################################                        
newdoc.Parts.Clear()    

                    

################################################################################
# now process all layers with a leading number, except layer 0
################################################################################                
for i in range (0, newdoc.Layers.Count) :
  if(newdoc.Layers[i].Name !="0"):  # ignore layer "0"
    # if a number is defined, extract it
    TokenList = newdoc.Layers[i].Name.split()
    Number = 0
    Name = ""
    PartName = ""
    Type = ""
    OptionList= ""
    
    if (TokenList[0].isnumeric()) :
        if len(TokenList) > 0 :
            Number = int(TokenList[0])
        if len(TokenList) > 1 :
            Name = TokenList[1]
            PartName = Name
        if len(TokenList) > 2 :
            Type = str(TokenList[2]).upper()
        if len(TokenList) > 3 :
            OptionList = TokenList[3:]
        
        # special case : Default definitions given in Layer 00
        if TokenList[0] == "00":
            # create a new part
            PartName = Name
            CamBamUI.MainUI.ActiveView.CADFile.CreatePart(PartName)
            CamBamUI.MainUI.ActiveView.CADFile.SetActivePart(PartName)
            # scan the paramters
            if len(TokenList) > 2 :
                OptionList = TokenList[2:]
                for Index in range (0, len(OptionList)) :
                    Command = OptionList[Index].upper()[0]
                    if (Command=="B"):
                        newdoc.MachiningOptions.StyleLibrary = OptionList[Index][1:]
                    if (Command=="C"):
                        newdoc.MachiningOptions.Style = OptionList[Index][1:]
                    #if (Command=="M"):  TODO
                    #    newdoc.MachiningOptions.Stock.StockSize.Y = float(OptionList[Index][1:])

                        

            
        else:  
            # create the MOP (depending on type)
            # Use all the drawing objects from the selected layer
            
            #special case : new Part (NT neues Teil)
            if (Type=="NT"):
                dummy=0
              
            
            # default profile. just for the python parser, will be overloaded later on
            profile=MOPProfile(newdoc,newdoc.Layers[i].Entities.ToArray())
              
            
            if Type not in TypeList:
                app.log ("Illegal type : " + Type + "\nLAYER : " + newdoc.Layers[i].Name + "\nbetter use " + str(TypeList))
                quit()
            
            if (Type == "INNEN"):
                profile=MOPProfile(newdoc,newdoc.Layers[i].Entities.ToArray())
                profile.InsideOutside = CBValue[InsideOutsideOptions](InsideOutsideOptions.Inside)    
                
            if (Type == "AUSSEN"):
                profile=MOPProfile(newdoc,newdoc.Layers[i].Entities.ToArray())
                profile.InsideOutside = CBValue[InsideOutsideOptions](InsideOutsideOptions.Outside)    

            if (Type == "TASCHE"):
                profile=MOPPocket(newdoc,newdoc.Layers[i].Entities.ToArray())

            if (Type == "GRAVUR"):
                profile=MOPEngrave(newdoc,newdoc.Layers[i].Entities.ToArray())
                
            if (Type == "NT"):
                PartName = Name
                CamBamUI.MainUI.ActiveView.CADFile.CreatePart(PartName)
                CamBamUI.MainUI.ActiveView.CADFile.SetActivePart(PartName)
                continue
                
                    
                    
            # search for a overloading MOP´s style
            for Index in range (0, len(OptionList)) :
                Command = OptionList[Index].upper()[0]
                if (Command=="C"):
                    profile.Style = OptionList[Index][1:]
                    break   # use only the first one


 
            # set the MOP´s name
            profile.Name = newdoc.Layers[i].Name
            app.log(profile.Name)
            
            
            # process the options
            for Index in range (0, len(OptionList)) :
                Command = OptionList[Index].upper()[0]
                Parameter = OptionList[Index][1:]
                if (Command=="T"):
                    profile.TargetDepth = CBValue[float](-(float) (Parameter))              
                    
                if (Command=="Z"):
                    profile.DepthIncrement = CBValue[float]((float) (Parameter))                
            
                if (Command=="V"):
                    profile.CutFeedrate = CBValue[float]((float) (Parameter))                
            
                if (Command=="N"):
                    profile.SpindleSpeed = CBValue[int]((int) (Parameter))              
            
                if (Command=="S"):
                    profile.StepOver = CBValue[float]((float) (Parameter))  
                  
                if (Command=="A"):
                    profile.RoughingClearance = CBValue[float]((float) (Parameter))
                  
                if (Command=="W"):
                    profile.StockSurface = CBValue[float]((float) (Parameter))
                  
                if (Command=="I"):
                    if (Parameter == "0"):
                        profile.CornerOvercut = CBValue[bool] (bool (0))
                    if (Parameter == "1"):
                        profile.CornerOvercut = CBValue[bool] (bool (1))
    
                if (Command=="D"):
                    profile.ToolDiameter = profile.ToolDiameter.NewValue((float) (Parameter))



            # wait for the thinking message to disapear...
            while CamBamUI.MainUI.ActiveView.CurrentEditMode is not None:
                app.Sleep(1)
            

            
            # add the machine op to the drawing...
            CamBamUI.MainUI.InsertMOP(profile)

            # wait for the thinking message to disapear...
            while CamBamUI.MainUI.ActiveView.CurrentEditMode is not None:
                app.Sleep(1)
               

#######################################               
# generate and show all toolspaths               
#######################################

CAMUtils.GenerateToolpaths(view)               
# wait for the thinking message to disapear...
while view.CurrentEditMode is not None:
    app.Sleep(1)


« Last Edit: November 15, 2018, 09:07:29 am by gmoo » Logged

best regards

Bernhard
gmoo
Droid
**
Offline Offline

Posts: 79

Jam it !


View Profile
« Reply #4 on: November 15, 2018, 09:13:25 am »

Here´s a simple DXF file for demonstration.

Just load it into cambam, select in MACHINING any milling tool with
diameter of roundabout 2mm.

Start the script and enjoy the show !


@Eddy : I had a look at your suggestions for retrieving the dlls ... brrrrrr....
Too much stuff for me, have merci on my poor little computer.  Grin

Best regards

Bernhard

* Test.dxf (101.51 KB - downloaded 30 times.)
Logged

best regards

Bernhard
EddyCurrent
CNC Jedi
*****
Offline Offline

Posts: 3950



View Profile
« Reply #5 on: November 15, 2018, 09:56:14 am »

Good script !

This is a view of the Visual Studio Object Browser after searching for "HoldingTab"

You don't need a fancy computer for ILSpy

Edit: ILSpy view also attached

The binaries are here; https://github.com/icsharpcode/ILSpy/releases

I've just downloaded the version4 beta and it's fine.

After running ILSpy.exe open CamBam.CAD.dll then use search.


* tabs.jpg (190.97 KB, 1500x650 - viewed 67 times.)

* tabs2.jpg (131.32 KB, 1000x741 - viewed 64 times.)
« Last Edit: November 15, 2018, 10:10:52 am by EddyCurrent » Logged

Made in England
gmoo
Droid
**
Offline Offline

Posts: 79

Jam it !


View Profile
« Reply #6 on: November 15, 2018, 11:03:19 am »

Ahhhhhh - these old eyes ... and the dark sunglasses ... Roll Eyes
I didn´t see the binaries, thank you, Eddy.

Now I will spy around a little, I hope this helps to solve the open issues of my magic script.

Best regards

Bernhard
Logged

best regards

Bernhard
gmoo
Droid
**
Offline Offline

Posts: 79

Jam it !


View Profile
« Reply #7 on: November 15, 2018, 12:43:16 pm »

Another "Aaaaaaahhh" !

I figured out setting the Machining stocksize´s Z and the new part´s CAMStyle.
Enough success for this day !

Not so much issues are open :
- generate n holding tabs and set Tab method to automatic
- Cut ordering  a=O Level First a=T Depth First

The actual script, if you want to play around with it :


Code:

#
#  _BG_AUTO_MAGIC.py
#
# create MOPs based upon infos written in DXF Layer names
#
# Layer 0 will be ignored
# only layers using names beginning with a number can auto-create MOPs
#
#
# LAYERNAME STYLE : Number Name ProfileType [options]
# ProfileTypes (not case-senitive)
#         innen (=inside)
#         aussen (=outside)
#         tasche (=pocket)
#         gravur (=engrave)
#         NT (Neues Teil anlegen / create a new Part)
#
# Options :
#            Caaaaa  CAM-Style 
#            Tn.nnn  Tiefe, wird automatisch negativ / Target depth, will be converted to negtive value
#            Zn.nnn  Zustellung / depth increment   
#            An.nnn  Aufmass    / roughing clearance                   
# x          Hn      n Haltestege generieren / generate n holding tabs and set Tab method to automatic
#            Wn.nnn  Werkstückoberfläche / Stock Surface
#            In      Innenecken räumen 0=nein  1=ja / Corner overcut 0=no 1=yes
#            Sn.nnn  seitliche Zustellung 0.00  ... 1.00  / stepover
#            Vnnnnn  Vorschub / feed speed
#            Nnnnnn  Drehzahl / spindle speed
#            Dn.nnnn Durchmesser / Tool diameter
# x          Ra      O-Obere Ebene zuerst    T-Tiefe zuerst  / Cut ordering  a=O Level First a=T Depth First   
#
# Special Case : Layer "00 name [options]"
#               - Name sets name of 1st Part
#               - no ProfileType
#               - will set the default values of MACHINING, no MOP is generated
#            Mn.nnn  Materialdicke / stock size Z
#            Baaaaa  Bibliothek / MACHINING default CAM library (must exist, there is no check)
#            Caaaaa  CAM-Style  / MACHINING default CAM styl (must exist, there is no check)
#             
# x = not implemented
#

from CamBam.CAM    import *
from CamBam.UI     import *
from CamBam.Values import *

TypeList = ["INNEN", "AUSSEN", "TASCHE", "BOHREN", "GRAVUR", "NT"]



# The 'doc' global variable is always the CADFile before any file opens,
# so we create a new varialbe pointing to the newly opened active file...
newdoc = CamBamUI.MainUI.ActiveView.CADFile




#REMEMBER !!! always ignore the layer "0" !!!



################################################################################
# first of all, let´s re-sort the layers by the numbers given in the layer´s name
################################################################################

for i in range (0, newdoc.Layers.Count-1) :
    # if a number is defined, extract it
    if(newdoc.Layers[i].Name !="0"):  # ignore layer "0"
        TokenList = newdoc.Layers[i].Name.split()
        if (TokenList[0].isnumeric()) :
            ActualNumber = int(TokenList[0])
            # run through the following layers and look for smaller numbers given in the layer´s name
            for Index in range (i+1,newdoc.Layers.Count) :
                if(newdoc.Layers[i].Name !="0"):  # ignore layer "0"
                    # extract defined number from layernames
                    TokenList = newdoc.Layers[Index].Name.split()
                    if (TokenList[0].isnumeric()) :
                        FollowingNumber = int(TokenList[0])
                        #compare following to actual number
                        if (FollowingNumber < ActualNumber) :
                            #if smaller, change layer order
                            newdoc.ChangeLayerOrder(Index,i)
                            ActualNumber = FollowingNumber


                   
                   
################################################################################
# Remove all parts
################################################################################                       
newdoc.Parts.Clear()   

                   

################################################################################
# now process all layers with a leading number, except layer 0
################################################################################               
for i in range (0, newdoc.Layers.Count) :
  if(newdoc.Layers[i].Name !="0"):  # ignore layer "0"
    # if a number is defined, extract it
    TokenList = newdoc.Layers[i].Name.split()
    Number = 0
    Name = ""
    PartName = ""
    Type = ""
    OptionList= ""
   
    if (TokenList[0].isnumeric()) :
        if len(TokenList) > 0 :
            Number = int(TokenList[0])
        if len(TokenList) > 1 :
            Name = TokenList[1]
            PartName = Name
        if len(TokenList) > 2 :
            Type = str(TokenList[2]).upper()
        if len(TokenList) > 3 :
            OptionList = TokenList[3:]
       
        # special case : Default definitions given in Layer 00
        if TokenList[0] == "00":
            # create a new part
            PartName = Name
            CamBamUI.MainUI.ActiveView.CADFile.CreatePart(PartName)
            CamBamUI.MainUI.ActiveView.CADFile.SetActivePart(PartName)
            # scan the paramters
            if len(TokenList) > 2 :
                OptionList = TokenList[2:]
                for Index in range (0, len(OptionList)) :
                    Command = OptionList[Index].upper()[0]
                    if (Command=="B"):
                        newdoc.MachiningOptions.StyleLibrary = OptionList[Index][1:]
                    if (Command=="C"):
                        newdoc.MachiningOptions.Style = OptionList[Index][1:]
                    if (Command=="M"): 
                       # get existing data
                        Buffer = newdoc.MachiningOptions.Stock.StockSize
                        #overload stocksize Z
                        a=Point3F(Buffer.X, Buffer.Y, (float) (OptionList[Index][1:]))
                        newdoc.MachiningOptions.Stock.StockSize = a
                       

           
        else:   
            # create the MOP (depending on type)
            # Use all the drawing objects from the selected layer
            # default profile. just for the python parser, will be overloaded later on
            profile=MOPProfile(newdoc,newdoc.Layers[i].Entities.ToArray())
             
           
            if Type not in TypeList:
                app.log ("Illegal type : " + Type + "\nLAYER : " + newdoc.Layers[i].Name + "\nbetter use " + str(TypeList))
                quit()
           
            if (Type == "INNEN"):
                profile=MOPProfile(newdoc,newdoc.Layers[i].Entities.ToArray())
                profile.InsideOutside = CBValue[InsideOutsideOptions](InsideOutsideOptions.Inside)   
               
            if (Type == "AUSSEN"):
                profile=MOPProfile(newdoc,newdoc.Layers[i].Entities.ToArray())
                profile.InsideOutside = CBValue[InsideOutsideOptions](InsideOutsideOptions.Outside)   

            if (Type == "TASCHE"):
                profile=MOPPocket(newdoc,newdoc.Layers[i].Entities.ToArray())

            if (Type == "GRAVUR"):
                profile=MOPEngrave(newdoc,newdoc.Layers[i].Entities.ToArray())
               
            if (Type == "NT"):
                PartName = Name
                CamBamUI.MainUI.ActiveView.CADFile.CreatePart(PartName)
                CamBamUI.MainUI.ActiveView.CADFile.SetActivePart(PartName)            # search for a overloading MOP´s style
                for Index in range (0, len(OptionList)) :
                    Command = OptionList[Index].upper()[0]
                    if (Command=="C"):
                        newdoc.ActivePart.Style = OptionList[Index][1:]
                        break   # use only the first one
                continue
               
                   
                   
            # search for a overloading MOP´s style
            for Index in range (0, len(OptionList)) :
                Command = OptionList[Index].upper()[0]
                if (Command=="C"):
                    profile.Style = OptionList[Index][1:]
                    break   # use only the first one


 
            # set the MOP´s name
            profile.Name = newdoc.Layers[i].Name
            app.log(profile.Name)
           
           
            # process the options
            for Index in range (0, len(OptionList)) :
                Command = OptionList[Index].upper()[0]
                Parameter = OptionList[Index][1:]
                if (Command=="T"):
                    profile.TargetDepth = CBValue[float](-(float) (Parameter))             
                   
                if (Command=="Z"):
                    profile.DepthIncrement = CBValue[float]((float) (Parameter))               
           
                if (Command=="V"):
                    profile.CutFeedrate = CBValue[float]((float) (Parameter))               
           
                if (Command=="N"):
                    profile.SpindleSpeed = CBValue[int]((int) (Parameter))               
           
                if (Command=="S"):
                    profile.StepOver = CBValue[float]((float) (Parameter)) 
                   
                if (Command=="A"):
                    profile.RoughingClearance = CBValue[float]((float) (Parameter))
                   
                if (Command=="W"):
                    profile.StockSurface = CBValue[float]((float) (Parameter))
                   
                if (Command=="I"):
                    if (Parameter == "0"):
                        profile.CornerOvercut = CBValue[bool] (bool (0))
                    if (Parameter == "1"):
                        profile.CornerOvercut = CBValue[bool] (bool (1))
     
                if (Command=="D"):
                    profile.ToolDiameter = profile.ToolDiameter.NewValue((float) (Parameter))



            # wait for the thinking message to disapear...
            while CamBamUI.MainUI.ActiveView.CurrentEditMode is not None:
                app.Sleep(1)
           

           
            # add the machine op to the drawing...
            CamBamUI.MainUI.InsertMOP(profile)

            # wait for the thinking message to disapear...
            while CamBamUI.MainUI.ActiveView.CurrentEditMode is not None:
                app.Sleep(1)

               
               
#######################################               
# generate and show all toolspaths               
#######################################

CAMUtils.GenerateToolpaths(view)               
# wait for the thinking message to disapear...
while view.CurrentEditMode is not None:
    app.Sleep(1)   

 
Logged

best regards

Bernhard
EddyCurrent
CNC Jedi
*****
Offline Offline

Posts: 3950



View Profile
« Reply #8 on: November 15, 2018, 14:11:57 pm »

Have a read here; http://www.cambam.co.uk/forum/index.php?topic=6253.msg49745#msg49745
might be useful ?

« Last Edit: November 15, 2018, 14:16:30 pm by EddyCurrent » Logged

Made in England
onekk
Wookie
****
Offline Offline

Posts: 450


View Profile
« Reply #9 on: November 15, 2018, 16:36:53 pm »

The CAM part is somewhat obscure, some feature are not accessible from scripting, C# is the way to make a "proper plugin", the reason is that you can "inspect" the exposed C# part in the CamBam Libraries.

Generally the MOP creation is quite difficult at the first approach, some properties are not accessible directly but you have to create an object and work on this new object and then assign it to the proper place.

Code:
static void CreatePocketMOP(CAMPart part, List<int> IDs, string p_name, double depth)
{
// WARNING: Target depth sould be negative
ICollection<Entity> objects = new List<Entity>();
foreach (int ID in IDs)
{
Entity primitive = myfile.FindPrimitive(ID);
objects.Add(primitive);
}

CamBam.Values.CBValue<int> t_number = new CamBam.Values.CBValue<int>(CAM_D.t_index);

MOPPocket Pc = new MOPPocket(myfile, objects);
Pc.Name = p_name;
Pc.TargetDepth = new CamBam.Values.CBValue<double>(depth);
Pc.DepthIncrement = new CamBam.Values.CBValue<double>(CAM_D.t_dia * CAM_D.depth_incr);
Pc.ToolNumber = t_number; // The Part ToolNumber is not taken in account, so we assign the toolnumber directly
Pc.Update();
part.MachineOps.Add(Pc);

}

as you can see (I hope) you have to pass the "Part" istance you want to populate and then pass the "primitives" to the operation
 
 
Code:
MOPPocket Pc = new MOPPocket(myfile, objects);

myfile is the reference of the actual open file as a CamBam CADFile entity.

Then you have to do and Update() of the new values and then add the newly created operation to the part.MachineOps

Code:
Pc.Update();
part.MachineOps.Add(Pc);

Some values could be assigned directly, mostly the string values, some other are to be created ad "casted" to a proper CamNam.Values.Something

The MOP are subclass off CamBam.CAM....

Another example "more complex" from one of my plugins is the Profile MOP


Code:
static void CreateProfileMOP(CAMPart part, List<int> IDs, bool outside, string p_name, int n_side)
{
// dims 0 = t_length 1 = max n. of tabs
ICollection<Entity> objects = new List<Entity>();

int min_tabs = 2;
int max_tabs = 8;

double r_fact = 10; // number of decimals for the tab dimensions
double perim = 0;
double min_dist = 0;

if (r_unit == 0)
{
r_fact = 10;
min_dist = 30;
}
else if (r_unit == 1)
{
r_fact = 100;
min_dist = 1.5;
}

foreach (int ID in IDs)
{
Entity primitive = myfile.FindPrimitive(ID);
objects.Add(primitive);
if (primitive.GetType() == typeof(Polyline))
{
perim += PerimBB((primitive as Polyline).GetRegion().P1, (primitive as Polyline).GetRegion().P2);
}
else if (primitive.GetType() == typeof(CamBam.CAD.Region))
{
Point3F min = new Point3F();
Point3F max = new Point3F();
(primitive as CamBam.CAD.Region).GetExtrema(ref min, ref max);
perim += PerimBB(new Point2F(min.X, min.Y), new Point2F(max.X, max.Y));
}
}

double factor1 = perim / IDs.Count;

LogAppend("MOP name: " + p_name, 3);
LogAppend(String.Format(" Perim {0:######.#####} , Factor1 {1:#####.#####}  min_dist {2:#####.#####} ", perim, factor1, min_dist), 4);

if (factor1 >= (min_dist * 3))
{
min_dist = min_dist * 3;
max_tabs = (int)(perim / min_dist);
}
else
{
max_tabs = min_tabs;
}

//(String.Format(" min_dist {0:#####.#####}  max_tabs {1:#####.#####} ", min_dist, max_tabs), 4);


MOPProfile mop = new MOPProfile(myfile, objects);

CamBam.Values.CBValue<int> t_number = new CamBam.Values.CBValue<int>(CAM_D.t_index);

mop.Name = p_name;
if (outside)
{
mop.InsideOutside = new CamBam.Values.CBValue<InsideOutsideOptions>(InsideOutsideOptions.Outside);
}
else
{
mop.InsideOutside = new CamBam.Values.CBValue<InsideOutsideOptions>(InsideOutsideOptions.Inside);
}
mop.ToolNumber = t_number; // The Part ToolNumber is not taken in account, so we assign ToolNumber directly.
HoldingTabInfo HoldTab = new HoldingTabInfo();


HoldTab.Height = Math.Round((Box.matTh * 0.5) * r_fact) / r_fact;
HoldTab.Width = Math.Round((Box.matTh * 0.75) * r_fact) / r_fact;

HoldTab.TabDistance = min_dist;
HoldTab.MinimumTabs = min_tabs;
HoldTab.MaximumTabs = max_tabs;

HoldTab.UseLeadIns = false;
HoldTab.TabStyle = HoldingTabStyles.Triangle;

HoldTab.TabMethod = TabMethodOption.Automatic;

mop.HoldingTabs = new CamBam.Values.CBValue<HoldingTabInfo>(HoldTab);

mop.DepthIncrement = new CamBam.Values.CBValue<double>(CAM_D.t_dia * CAM_D.depth_incr);

mop.TargetDepth = new CamBam.Values.CBValue<double>(CAM_D.target_depth * -1.0D); // Target depth sould be negative

mop.Update();

            part.MachineOps.Add(mop);


}


Note this

Code:
new CamBam.Values.CBValue<InsideOutsideOptions>(InsideOutsideOptions.Outside);

and this

Code:
HoldingTabInfo HoldTab = new HoldingTabInfo();


HoldTab.Height = Math.Round((Box.matTh * 0.5) * r_fact) / r_fact;
HoldTab.Width = Math.Round((Box.matTh * 0.75) * r_fact) / r_fact;

HoldTab.TabDistance = min_dist;
HoldTab.MinimumTabs = min_tabs;
HoldTab.MaximumTabs = max_tabs;

HoldTab.UseLeadIns = false;
HoldTab.TabStyle = HoldingTabStyles.Triangle;

HoldTab.TabMethod = TabMethodOption.Automatic;

mop.HoldingTabs = new CamBam.Values.CBValue<HoldingTabInfo>(HoldTab);

mop.DepthIncrement = new CamBam.Values.CBValue<double>(CAM_D.t_dia * CAM_D.depth_incr);

mop.TargetDepth = new CamBam.Values.CBValue<double>(CAM_D.target_depth * -1.0D); // Target depth sould be negative

mop.Update();




As you can see many properties are to be created on the fly with the proper values derived from the internal CamBam Class, and this could be done only with the ILSPY or in Linux using Monodevelop, that could display some CamBam internals.

The python bindings are not very well documented and it is a very hard work to make them work other than in the few examples Andy has posted.

Regards

Carlo D.
Logged

Carlo D. (onekk)

eShapeoko #343 750x1000 mm + GRBL + bCNC + CamBam
EddyCurrent
CNC Jedi
*****
Offline Offline

Posts: 3950



View Profile
« Reply #10 on: November 15, 2018, 19:52:25 pm »

Bernhard,

If you want to write a plugin I made a simple example Visual Studio project here; http://www.cambam.co.uk/forum/index.php?topic=6668.msg53974#msg53974

It uses C# and once you get it to compile you will be flying !
Logged

Made in England
gmoo
Droid
**
Offline Offline

Posts: 79

Jam it !


View Profile
« Reply #11 on: November 16, 2018, 09:59:16 am »

Thank you all for your hints, it helped pretty much !

Actually I find no way to readout HoldingTabInfo member variables,
for I´m not a python specialist.

Setting the profile´s holding tab infos works well like this:

                    Buffer = HoldingTabInfo()
                    Buffer.MinimumTabs = 1
                    Buffer.MaximumTabs = 4
                    Buffer.TabStyle  = HoldingTabStyles.Triangle
                    Buffer.TabMethod = TabMethodOption.Automatic
                    profile.HoldingTabs = CBValue[HoldingTabInfo] (Buffer)

But I only want to overload MaximumTabs and the TabMethod,
the rest shall stay untouched, because it´s already defined in the CAM-style.
Setting directly doesn´t work, I have to pass a complete HoldingTabInfo.

How can I copy the already existing data into my Buffer ?
Logged

best regards

Bernhard
onekk
Wookie
****
Offline Offline

Posts: 450


View Profile
« Reply #12 on: November 16, 2018, 12:25:23 pm »

I don't know how it is feasible, and expecially via Python.

The Python API is not very documented and it is only usable in the CamBam Internal script editor, (in Linux it have serious usability problems).

Generally speaking, a setting is done using a proper "construct" usually some  CamBam.Values.something like:

Code:
new CamBam.Values.CBValue<HoldingTabInfo>(HoldTab);


In this the C# as a somewhat vantage to permit some inspection.

Maybe another way could be setting it directly in the XML file using an XML postprocessor, maybe written in Python.

Regards

Carlo D.
Logged

Carlo D. (onekk)

eShapeoko #343 750x1000 mm + GRBL + bCNC + CamBam
gmoo
Droid
**
Offline Offline

Posts: 79

Jam it !


View Profile
« Reply #13 on: November 20, 2018, 10:44:40 am »

Code:
# Creating a new instance of holdingtabs filled with default values works fine :
Buffer = HoldingTabInfo()

# Changing properties also works fine :
Buffer.TabMethod = TabMethodOption.Automatic
Buffer.MaximumTabs = 13

# shooting this into the actual MOP works, too :
profile.HoldingTabs = CBValue[HoldingTabInfo] (Buffer)

My problem is actually :
I do not want a fresh instance of HoldingTabInfo,
I want to load the MOP´s HoldingTabInfo into the Buffer,
change one or two particular properties, then shoot it back to the MOP.
A direct access to the properties is obviously not available.

Code:
# Trying the "other direction" throws an error :
Buffer = CBValue[HoldingTabInfo] (profile.HoldingTabs)

(If the question is silly, please excuse me and keep in mind, I´m not a professional python programmer)


EDIT : The error message :
Python Error : TypeError: Multiple targets could match:
CreateInstance(object), CreateInstance(CBValue[HoldingTabinfo]


* Zwischenablage02.jpg (17.03 KB, 1120x62 - viewed 49 times.)
« Last Edit: November 20, 2018, 10:47:47 am by gmoo » Logged

best regards

Bernhard
onekk
Wookie
****
Offline Offline

Posts: 450


View Profile
« Reply #14 on: November 20, 2018, 11:32:55 am »

Holdingtabsinfo are somewhat obscure.

I have tried in C# to make some assumptions on how to correct the wrong tabs that automatically Cambam is putting on the "profile" (as I'm using CamBam mostly for plywood cutting), but haven't had any success to access the property to modify the values, I didn't remember well but I've stop any attempts, and I correct them by hand when i do automatical MOP creation.

In the interface there is also a position and the ability to edit in a tabular way the Tabs, but as in Linux this is done with a poorly supported widget of Mono I have had many crash in the attempts to edit them, so I edit them via the visual interface.


In case you wanto to inspect the XML file (your ?.cb file) you will find the Tabs exposed like:

Code:
</HoldingTabs>
          <Tabs>
            <HoldingTab>
              <NormalInverted>False</NormalInverted>
              <ParentEntityID>24</ParentEntityID>
              <ParametricPoint>0.136624767014219</ParametricPoint>
              <Normal>
                <X>0.653619870346084</X>
                <Y>0.756823007769167</Y>
              </Normal>
            </HoldingTab>
            <HoldingTab>
              <NormalInverted>False</NormalInverted>
              <ParentEntityID>24</ParentEntityID>
              <ParametricPoint>0.353435323331192</ParametricPoint>
              <Normal>
                <X>-0.605109424626999</X>
                <Y>0.796142314054204</Y>
              </Normal>
            </HoldingTab>
            <HoldingTab>
              <NormalInverted>False</NormalInverted>
              <ParentEntityID>24</ParentEntityID>
              <ParametricPoint>0.589058872218654</ParametricPoint>
              <Normal>
                <X>-0.847481639324712</X>
                <Y>-0.530824708361902</Y>
              </Normal>
            </HoldingTab>

This happens even if you modify by hands the points.

The XML file holds all the relevant informations but they are not easy to interpreter, as they are expressed as a

ParametriPoint, a Normal with an attribute of <NormalInverted>.

The only assumptions that I could do is the ParametricPoint is a fraction of "profile" Perimeter and that Normal is the direction of the "holding tab" maybe the vector that hold the "center" of the holding tab relative to the point on the perimeter.

<NormalInverted> is another mistery, maybe it is the direction of the polyline defining the perimeter taken from his starting point and relative to the direction of the machining MOP.

Some research are to be done on this matter.

These are the only data that i could pass to you.

In Python i don't know as I generally wrote plugins to make my things, once accustomed it is more flexible than using the internal editor of CamBam or Python.

Regards

Carlo D.
Logged

Carlo D. (onekk)

eShapeoko #343 750x1000 mm + GRBL + bCNC + CamBam
Pages: [1] 2 3 4
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.21 | SMF © 2015, Simple Machines

Valid XHTML 1.0! Valid CSS! Dilber MC Theme by HarzeM
Page created in 0.192 seconds with 19 queries.

Copyright © 2018 HexRay Ltd. | Sitemap