Variables in macros
You can create variables in macros just as you can in a PowerMill project. When you create a variable in a macro, it has the same properties as a PowerMill parameter, and can store either a value or an expression.
- Assigning parameters
- Inputting values into macros
- Carriage return in dialogs
- User selection of entities in macros
- User selection from a list of options
- User selection of a file name
- Arrays and lists
- Using lists
- Adding items to a list summary
- Removing items from a list summary
- Building a list
- Entity variables
- Object variables
- Vectors and points
- Comparing variables
- Logical operators
- Advance variable options
Variable names must start with an alphabetic character (a-z, A-Z) and may contain any number of subsequent alphanumeric characters (a-z, A-Z, 1-9, _). For example, you can name a variable Count1 but not 1Count.
Variable names are case insensitive. For example, Count, count, and CoUnT all refer to the same variable.
All variables must have a type, which can be:
INT — Integer numbers. For example, 1, 21, 5008.
REAL — Real numbers. For example, 201, -70.5, 66.0.
STRING — A sequence of characters. For example, hello.
BOOL — Truth values, either 0 (false) or 1 (true).
ENTITY — A unique value that references an existing PowerMill entity.
Object — A collection of parameters that PowerMill groups together, such as Block, or Connections.
You must declare the variable type, for example:
INT Count = 5 REAL Diameter = 2.5 STRING Tapefile = "MyFile.tap"
You can access any of the PowerMill parameters in variable declarations, expressions, or assignments.
Any variables you create in a macro are only accessible from within the macro. When the macro has finished the variable is no longer accessible and cannot be used in expressions or other macros.
If you need to create a variable that can be used at any time in a PowerMill project then you should create a User Parameter.
Assigning parameters
When you assign a value to a variable the expression is evaluated and the result is assigned, the actual expression is not retained. This is the same as using the EVAL modifier in the PowerMill parameter EDIT PAR command. These two statements are equivalent:
EDIT PAR "Stepover" EVAL "Tool.Diameter * 0.6"
$Stepover = Tool.Diameter * 0.6
Inputting values into macros
An input dialog enables you to enter specific values into a macro.
The basic structure is:
$<variable> = INPUT <string-prompt>
This displays an input dialog with a specified prompt as its title which enables you to enter a value.
For example:
string prompt = "Enter a number"
$i = input $prompt
$err = ERROR i
}
produces this dialog:
You can also use INPUT in the variable definition.
For example:
REAL X = INPUT "Enter a number"
Asking a Yes/No question
A Yes/No query dialog is a very simple dialog.
Selecting Yes assigns 1 to the variable.
Selecting No assigns 0 to the variable.
The basic structure is:
$<variable> = QUERY <string-prompt>
For example:
string yesnoprompt = "You entered 5. Would you like to have another go?"
bool carryon = 0
$carryon = query $yesnoprompt
produces this dialog:
Creating a message dialog
There are three types of message dialogs:
Information dialogs
Warning dialogs
Error dialogs
The basic structure is:
MESSAGE INFO|WARN|ERROR <expression>
For example, an input dialog to enter a number into a macro:
real i = 3
string prompt = "Enter a number"
do {
bool err = 0
do {
$i = input $prompt
$err = ERROR i
if err {
$prompt = "Please 'Enter a number'"
}
} while err
string yesnoprompt = "You entered " + string(i) + ". Would you like to have another go?"
bool carryon = 0
$carryon = query $yesnoprompt
} while $carryon
message info "Thank you!"
An example to find out if a named toolpath exists:
string name = ""
$name = input "Enter the name of a toolpath"
if pathname('toolpath',name) == "" {
message error "Sorry. Couldn't find toolpath " + name
} else {
message info "Yes! Toolpath " + name + " exists!"
}
Carriage return in dialogs
You can specify a carriage return to control the formatting of the text in a message dialog using crlf.
For example, looking at the input dialog to enter a number into a macro:
real i = 3
string prompt = "Enter a number"
do {
bool err = 0
do {
$i = input \$prompt
$err = ERROR i
if err {
$prompt = "Please 'Enter a number'"
}
} while err
string yesnoprompt = "You entered " + string(i) + "." + crlf + " Would you like to have another go?"
bool carryon = 0
$carryon = query $yesnoprompt
} while $carryon
message info "Thank you!"
produces this query dialog:
User selection of entities in macros
Use the INPUT command to prompt the user to select a specific entity in PowerMill, such as a toolpath or a tool. You can use this to:
Display a list of available entities
Prompt the user to select one of them.
For example, to list all the available tools and then ask the user to select one:
STRING ToolName = INPUT ENTITY TOOL "Please select a Tool."
This example creates two folders, creates two tool in each folder, then asks the user to select one of the tools:
// Create some tools in folders
CREATE FOLDER 'Tool' 'Endmills'
CREATE IN 'Tool\\Endmills' TOOL 'End 20' ENDMILL
EDIT TOOL ; DIAMETER 20
CREATE IN 'Tool\\Endmills' TOOL 'End 10' ENDMILL
EDIT TOOL ; DIAMETER 10
CREATE FOLDER 'Tool' 'Balls'
CREATE IN 'Tool\\Balls' TOOL 'Ball 12' BALLNOSED
EDIT TOOL ; DIAMETER 12
CREATE IN 'Tool\\Balls' TOOL 'Ball 10' BALLNOSED
EDIT TOOL ; DIAMETER 10
// Prompt user to pick one
STRING ToolName = ''
$ToolName = INPUT ENTITY TOOL "Please select a Tool."
You can also ask for the selection of a number of entities. The result is the list of entities selected, which can be assigned to either a list of strings, or list of entities.
ENTITY LIST $Selected_Toolpaths = INPUT ENTITY MULTIPLE toolpath "which toolpaths do you want to check?"
STRING LIST ToolpathNames = INPUT ENTITY MULTIPLE TOOLPATH "Select toolpaths to check"
You can then iterate over the user selection with a FOREACH loop:
FOREACH $tp in ToolpathNames {
ACTIVATE TOOLPATH \$tp.Name
EDIT COLLISION APPLY
}
User selection from a list of options
You can use the INPUT command to prompt the user to select from a list of options that your macro supplies. The syntax for this is:
INT value = INPUT CHOICE <string-array> <prompt>
For example, suppose you have a machining macro where everything is setup except that you want to give the user the choice of cut direction to use. You can do this by using a CHOICE input as follows:
// Create an array of strings from the CutDirection parameter
STRING ARRAY Opts[] = values(CutDirection)
INT C = INPUT CHOICE $Opts "Choose the Cut Direction you want"
$CutDirection = $C
}
Or for another example, you can increase or decrease the number of options the user can select. You can limit the options available to only one, such as Gouge Check or Collision Check a toolpath, or you can increase the options available so the user can choose between the two options. To create this list, enter the following:
STRING ARRAY Opts[] = {"Gouge check only", "Collision check only", "Gouge and Collision check"} INT C = INPUT CHOICE $Opts "Pick an option"
SWITCH $C {
CASE 0:
MACRO "Gouge_Check.mac"
BREAK
CASE 2:
MACRO "Gouge_Check.mac"
// Intended fall through to next command
CASE 1:
MACRO "Collision_Check.mac"
BREAK
}
IF $C==0 {
MACRO "Gouge_Check.mac"
} ELSEIF $C==1 {
MACRO "Collision_Check.mac"
} ELSEIF $C==2 {
MACRO "Gouge_Check.mac"
MACRO "Collision_Check.mac"
}
User selection of a file name
You can prompt your user for a filename with Use the FILESELECT command to prompt the user for a file name. This command displays an Open dialog which enables user to browse for a file.
For example:
STRING Filename = ''
$Filename = FILESELECT "Please select a pattern file"
Arrays and lists
Arrays
In addition to simple variables of type INT, REAL, or STRING you can also have arrays of these types. When you declare an array you must initialise all of its members using an initialisation list. When you have specified an array you cannot change its size. The syntax for an array is:
BASIC-TYPE ARRAY name[n] = {…}
For example, to declare an array of three strings:
STRING ARRAY MyArray[3] = {'First','Second','Third'}
All the items in the initialisation list must be the same BASIC-TYPE as the array.
You can access the items of the array by subscripting. The first item in the array is subscript 0. For example:
INT Index = 0
WHILE Index < size(MyArray) {
PRINT MyArray[Index]
$Index = Index + 1
}
Prints:
First
Second
Third
If you leave the size of the array empty, then PowerMill determines its size from the number of elements in the initialisation list. For example:
STRING ARRAY MyArray[] =
{'First','Second','Third','Fourth'}
PRINT = size(MyArray)
Prints:
4
Lists
PowerMill also has a LIST type. The main difference between a list and an array is that the list does not have a fixed size, so you can add and remove items to it. You can create lists:
that are empty to start with
from an initialisation list
from an array.
// Create an empty list STRING LIST MyStrings = {} // Create a list from an array STRING LIST MyList = MyArray // Create a list using an initialisation list STRING LIST MyListTwo = {'First','Second'}
You can use two inbuilt functions add_first()
and add_last()
to add items to a list.
For example using the inbuilt function add_last()
:
CREATE PATTERN Daffy
CREATE PATTERN Duck
// Create an empty list of strings
STRING LIST Patterns = {}
FOREACH pat IN folder('Pattern') {
// Add the name of the pattern to the list
int s = add_last(Patterns, pat.Name)
}
FOREACH name IN Patterns {
PRINT = $name
}
Prints:
Daffy
Duck
You can also add items to the front of a list by using the inbuilt function add_first()
:
CREATE PATTERN Daffy
CREATE PATTERN Duck
// Create an empty list of strings
STRING LIST Patterns = {}
FOREACH pat IN folder('Pattern') {
// Add the name of the pattern to the list
int s = add_first(Patterns, pat.Name)
}
FOREACH name IN Patterns {
PRINT = $name
}
Prints:
Duck
Daffy
Using lists
A list, like an array, contains multiple values. You can create a list with initial values:
INT LIST MyList = {1,2,3,4}
You can specify an empty list:
INT LIST MyEmptyList = {}
You can use lists anywhere you might use an array. For instance, you can use a list in a FOREACH loop:
FOREACH i IN MyList {
PRINT = i
}
or to initialise an array:
INT ARRAY MyArray[] = MyList
You can also use an array to initialise a list:
INT LIST MyList2 = MyArray
You can pass a list to macro functions that expect an array:
FUNCTION PrintArray(INT ARRAY MyArray) {
FOREACH i IN Myarray {
PRINT = i
}
}
FUNCTION Main() {
INT LIST MyList = {10,20,30,40}
CALL PrintArray(MyList)
}
You can access the elements of a list with a FOREACH loop, or you can use the array subscripting notation:
INT Val = MyList[2]
Adding items to a list summary
The main differences between a list and an array is that a list can have items added to it and removed from it.
To add an item to a list you can use either of the inbuilt functions add_first()
or add_last()
.
For example, to collect the names of all the toolpaths in a folder:
// Create an empty list
STRING LIST TpNames = {}
FOREACH tp IN folder('Toolpath\\MyFolder') {
INT Size = add_last(TpNames, tp.name)
}
For more information see Adding comments to macros.
Removing items from a list summary
The main differences between a list and an array is that a list can have items added to it and removed from it.
To remove an item from a list you can use either of the inbuilt functions remove_first() or remove_last()
.
For example, if you have a list of toolpath names some of which are batched and you want to ask the user whether they want them calculated now. You can use a function which removes calculated toolpaths from the list and creates a query message for the rest.
FUNCTION CalculateNow(STRING LIST TpNames) {
// Cycle through the list
FOREACH Name IN TpNames {
IF entity('toolpath',Name).Calculated {
// Toolpath already calculated so
// remove name from list
BOOL success = remove(TpNames,Name)
}
}
// Do we have any names left
IF size(TpNames) > 0 {
// Build the prompt string
STRING Msg = "These toolpaths are uncalculated"
FOREACH name IN TpNames {
$Msg = Msg + CRLF + name
}
$Msg = Msg + CRLF + "Do you want to calculate them now?"
// Ask the user if they want to proceed
bool yes = 0
$yes = QUERY \$msg
IF yes {
// Loop through the toolpaths and calculate them
WHILE size(TpNames) > 0 {
STRING Name = remove_first(TpNames)
ACTIVATE TOOLPATH $Name
EDIT TOOLPATH ; CALCULATE
}
}
}
}
You could use a FOREACH loop rather than a WHILE loop:
FOREACH Name IN TpNames {
ACTIVATE TOOLPATH \$Name
EDIT TOOLPATH ; CALCULATE
}
PowerMill has an inbuilt function which enables you to remove duplicate items from a list: remove_duplicates
. For example, to determine how many different tool diameters there are in your toolpaths you could add the tool diameters from each toolpath and then remove the duplicates:
REAL LIST Diameters = {}
FOREACH tp IN folder('toolpath') {
INT s = add_first(Diameters, tp.Tool.Diameter)
}
INT removed = remove_duplicates(Diameters)
For more information, see Removing items from a list or Removing duplicate items in a list.
Building a list
You can use the inbuilt member()
function in a macro function to build a list of tool names used by toolpaths or boundaries without any duplicates:
FUNCTION ToolNames(STRING FolderName, OUTPUT STRING LIST ToolNames) {
// loop over all the items in FolderName
FOREACH item IN folder(FolderName) {
// Define local working variables
STRING Name = ''
INT size = 0
// check that the item's tool exists
// it might not have been set yet
IF entity_exists(item.Tool) {
// get the name and add it to our list
$Name = item.Tool.Name
IF NOT member(FolderName, Name) {
$dummy = add_last(FolderName, Name)
}
}
// Check whether this item has a reference tool
// and that it has been set
IF active(item.ReferenceTool) AND entity_exists(item.ReferenceTool) {
// get the name and add it to our list
$Name = item.ReferenceTool.Name
IF NOT member(FolderName, Name) {
$dummy = add_last(FolderName, Name)
}
}
}
}
As this function can work on any toolpath or boundary folder, you can collect all the tools used by the toolpaths in one list and all of the tools used by boundaries in another list. You can do this by calling the macro function twice:
STRING LIST ToolpathTools = {}
STRING LIST BoundaryTools = {}
CALL ToolNames('Toolpath',ToolpathTools)
CALL ToolNames('Boundary',BoundaryTools)
To return a list containing the items from both sets with any duplicates removed:
STRING LIST UsedToolNames = set_union(ToolpathTools, BoundaryTools)
Subtract function
You can use the subtract()
function to determine what happened after carrying out a PowerMill command. For example, suppose you to find out if any new toolpaths are created during a toolpath verification. If you get the list of toolpath names before the operation, and the list of names after the operation, and then subtract the ‘before’ names from the ‘after’ names you are left with the names of any new toolpaths.
FUNCTION GetNames(STRING FolderName, OUTPUT STRING LIST Names) {
FOREACH item IN folder(FolderName) {
INT n = add_last(Names, item.Name)
}
}
FUNCTION Main() {
STRING LIST Before = {}
CALL GetNames('toolpath',Before)
EDIT COLLISION APPLY
STRING LIST After = {}
CALL GetNames('toolpath',After)
STRING LIST NewNames = subtract(After, Before)
IF is_empty(NewNames) {
PRINT "No new toolpaths were created."
} ELSE {
PRINT "The new toolpaths created are:"
FOREACH item IN NewNames {
PRINT = item
}
}
}
Entity variables
PowerMill has a special variable type ENTITY. You can use ENTITY variables to refer to existing PowerMill entities such as toolpaths, tools, boundaries, patterns, workplanes, and so on. You cannot use this command to create new entities.
For example:
// create an entity variable that references boundary entity 'Duck'
ENTITY Daffy = entity('boundary','Duck')
The inbuilt functions, such as folder()
return lists of entities so you can store the result of the call in your own list and array variables:
For example:
ENTITY List Toolpaths = folder('toolpath')
When looping over folder items in a FOREACH the loop variable that is automatically created has the type ENTITY. Therefore the following code is syntactically correct:
FOREACH tp IN folder('toolpath') {
ENTITY CurrentTP = tp
PRINT = CurrentTP.Name
}
You can also pass ENTITY variables to functions (and passed back from function) by using an OUTPUT argument:
For example:
FUNCTION Test(OUTPUT ENTITY Ent) {
$Ent = entity('toolpath','2')
}
FUNCTION Main() {
ENTITY TP = entity('toolpath','1')
CALL Test(TP)
PRINT = TP.Name
}
Additionally, you can use an ENTITY variable anywhere in PowerMill that is expecting an entity name.
For example:
ENTITY tp = entity('toolpath','1')
ACTIVATE TOOLPATH $tp
Object variables
PowerMill has a variable type called OBJECT which can hold any collection of variables that PowerMill pre-defines, such as Block or Connections.
For example:
// Get the current set of block parameters
OBJECT myObject = Block
// Activate a toolpath (this may change the block)
ACTIVATE TOOLPATH "Daffy"
// Reset the block to its old state
$Block = myObject
Whilst you cannot create an ARRAY of OBJECT you can create a LIST of OBJECTs:
For example:
OBJECT LIST myObjects = {Block,Connections}
FOREACH ob IN myObjects {
PRINT PAR "ob"
}
As you can see from the above example, each object in a list may be different. It is the responsibility of the macro writer to keep track of the different types of OBJECT. PowerMill has an inbuilt function get_typename()
to help with this.
For example:
OBJECT LIST myObjects = {Block,Connections}
FOREACH ob IN myObjects {
PRINT = get_typename(ob)
}
Which prints:
Block
ToolpathConnections
As with all lists, you can also access the elements by index:
PRINT = get_typename(myObjects[0])
PRINT = get_typename(myObjects[1])
Objects can also be passed to and from macro FUNCTIONs.
For example:
FUNCTION myBlkFunction(OBJECT blk) {
IF get_typename(blk) != "Block" {
MESSAGE ERROR "Expecting a Block object"
MACRO ABORT
}
// Code that works on block objects
}
// Find block with maximum zrange
FUNCTION myZrangeBlockFunc(OUTPUT OBJECT Blk) {
// The
REAL zrange = 0
FOREACH tp IN folder('toolpath') {
// find zlength of this block
REAL z = tp.Block.Limits.ZMax - tp.Block.Limits.ZMin
IF z > zrange {
// Copy if longer than previously
$Blk = Block
$zrange = z
}
}
}
Vectors and points
In PowerMill vectors and points are represented by an array of three reals.
PowerMill contains point and vector parameters, for example the Workplane.Origin, Workplane.ZAxis, ToolAxis.Origin, and ToolAxis.Direction. You can create your own vector and point variables:
REAL ARRAY VecX[] = {1,0,0}
REAL ARRAY VecY[] = {0,1,0}
REAL ARRAY VecZ[] = {0,0,1}
REAL ARRAY MVecZ[] = {0,0,-1}
REAL ARRAY Orig[] = {0,0,0}
For more information, see the inbuilt Vectors and points functions.
Comparing variables
Comparing variables enables you to check information and defines the course of action to take when using IF statements and WHILE statements.
The result of a comparison is either true or false. When true the result is 1, when false the result is 0.
A simple comparison may consist of two variables with a relational operator between them:
Relational operator | Description | |
---|---|---|
Symbol | Text | |
== | EQ | is equal to |
!= | NE | is not equal to |
< | LT | is less than |
<= | LE | is less than or equal to |
> | GT | is greater than |
>= | GE | is greater than or equal to |
For example,
BOOL C = (A == B)
is the same as:
BOOL C = (A EQ B)
C is assigned 1 (true) if A equals B and . If A does not equal B, then C is 0 (false).
If you compare the values of two strings, you must use the correct capitalisation.
For example, if you want to check that the tool is an end mill, then you must use:
Tool.Type == 'end_mill'
and not:
Tool.Type == 'End_Mill
If you are unsure about the case of a string then you can use one of the inbuilt functions lcase() or ucase() to test against the lower case or upper case version of the string:
lcase(Tool.Type) == 'end_mill'
ucase(Tool.Type) == 'END_MILL'
For example, comparing variables:
BOOL bigger = (Tool.Diameter+Thickness
>=ReferenceToolpath.Tool.Diameter+ReferenceToolpath.Thickness)
gives a result of 1 (true) when the Tool.Diameter + Thickness is greater than or equal to the ReferenceToolpath.Tool.Diameter + ReferenceToolpath.Thickness and a result of 0 (false) otherwise.
Logical operators
Logical operators let you to do more than one comparison at a time. There are four logical operators:
AND
OR
XOR
NOT
Using the logical operator AND
The result is true (1) if all operands are true, otherwise the result is false (0).
Operand 1 | Operand 2 | Operand 1 AND Operand 2 |
---|---|---|
true (1) | true (1) | true (1) |
true (1) | false (0) | false (0) |
false (0) | true (1) | false (0) |
false (0) | false (0) | false (0) |
Using the logical operator OR
The result is true (1) if at least one operand is true. If all the operands are false (0) the result is false.
Operand 1 | Operand 2 | Operand 1 OR Operand 2 |
---|---|---|
true (1) | true (1) | true (1) |
true (1) | false (0) | true (1) |
false (0) | true (1) | true (1) |
false (0) | false (0) | false (0) |
Using the logical operator XOR
The result is true (1) if exactly one operand is true. If all the operands are false the result is false (0). If more than one operand is true the result is false (0).
Operand 1 | Operand 2 | Operand 1 XOR Operand 2 |
---|---|---|
true (1) | true (1) | false (0) |
true (1) | false (0) | true (1) |
false (0) | true (1) | true (1) |
false (0) | false (0) | false (0) |
Using the logical operator NOT
The result is the inverse of the input.
Operand 1 | NOT Operand 1 |
---|---|
true (1) | false (0) |
false (0) | true (1) |
Advance variable options
Scratchpad variables
It is possible to create and manipulate variables in the command line window. These are called scratchpad variables as you can use them to test the results of parameter evaluation without having to write a macro.
For example, to test some code, in the command line window type:
STRING Test = Tool.Name
DEACTIVATE TOOL
ACTIVATE TOOL $Test
To clear the scratchpad, in the command line window type:
RESET LOCALVARS
If you do not issue the RESET LOCALVARS
command, the local variable, Test
, remains defined until you exit from PowerMill.
Using variables and parameters in macro commands
You can substitute the value of a variable or parameter in a command wherever the command expects a number or a string. To do this, prefix the variable or parameter name with a $.
$variable
input when the $variable
has a numeric value. You cannot use the $variable
syntax for STRING parameters.For example, to create a tool with a diameter that is half that of the active tool.
// Calculate the new diameter and name of tool
REAL HalfDiam = Tool.Diameter/2
STRING NewName = string(Tool.Type) + " D-" + string(HalfDiam)
// Create a new tool and make it the active one
COPY TOOL ;
ACTIVATE TOOL #
// Now rename the new tool and edit its diameter
RENAME TOOL ; $NewName
EDIT TOOL $NewName DIAMETER $HalfDiam
This creates a tool with half the diameter.
Scope of variables
A variable exists from the time it is declared until the end of the block of code within which it is declared. Blocks of code are macros and control structures (WHILE, DO - WHILE, SWITCH, IF-ELSEIF-ELSE, and FOREACH).
A variable, with a specific name, can only be defined once within any block of code.
For example,
// Define a local variable 'Count'
INT Count = 5
// Define a second local variable 'Count'
INT Count = 2
Gives an error since Count
is defined twice.
However, within an inner block of code you can define another variable with the same name as a variable (already defined) in an outer block:
INT Count = 5
IF Count > 0 {
// Define a new local variable 'Count'
INT Count = 3
// Print 3
PRINT $Count
// The local Count is no longer defined
}
// Print 5
PRINT $Count
A variable defined within an inner block of code hides any variable declared in an outer block. This is also important if you use a name for a variable which matches one of PowerMill’s parameters. For example, if the toolpath stepover is 5 and in your macro you have:
// 'Hide' the global stepover by creating your own variable
REAL Stepover = 2
// Print Stepover
PRINT $Stepover
The value printed is 2 not 5, and the toolpath stepover value is unchanged. To access the current toolpath's stepover parameter you must use toolpath.Stepover.
// 'Hide' the global stepover by creating your own variable
REAL Stepover = 2
// Print 2
PRINT $Stepover
// Print the value of the toolpath's stepover - which is 5
PRINT $toolpath.Stepover
REAL Factor = 0.6
// The next two commands are OK as the expression is evaluated immediately.
$Stepover = Tool.Diameter * Factor
EDIT PAR "Stepover" EVAL "Tool.Diameter * Factor"
// The next command is not OK because the expression is retained.
EDIT PAR "Stepover" "Tool.Diameter * Factor"
The Factor
variable ceases to exist at the end of the macro, so Stepover
evaluates as an error.