Part 5: Data Types, Assignments, Casting, and Scope
Data Types
Previously we have discussed variables and objects and “types” have been
mentioned. In the world of programming, everything that has a value has
a type. The type refers to how that value is stored in memory and how it
is used within the program. Individual characters and words are called
strings
. By default, any parameters being passed into a macro are created
as strings. Many values accessed within a macro are also string
types.
When using a string
in a comparison or assignment, the value must be
encapsulated in quotes. Single or double quotes are allowable to be used
as long as the beginning quote matches the end quote. So
{% if printer.homed_axes != 'XYZ' %}
and
{% if printer.homed_axes != "XYZ" %}
are both valid, but
{% if printer.homed_axes != "XYZ' %}
is not. String comparisons are always case-sensitive, meaning
'My Value' is not equal to 'my value'
In addition to string
, there are also float
, and int
types (plus many
more, but that is another topic).
A float
is any numerical value that is not a whole number (has a decimal point).
It is called float
as shorthand for “floating point”. Floating point means
that the number of digits before and after the decimal point varies.
This is really important for the program but not so much for us. All we
should concern ourselves with is that it is not a whole number.
If we need only need whole numbers we can use an int
data type.
Assignments
Sometimes it may be necessary to create a variable inside of our macro
that is not being supplied by a parameter, or if a parameter is supplied
and we want to access it as particular type for the scope of our macro.
To create a new variable, we use the Jinja2 set
expression. So if we
wanted to create a variable called “toolTemp” and have it be the value of
the parameter ‘TOOL_TEMP’ we would do
{% set toolTemp = params.TOOL_TEMP %}
This would create a a variable called toolTemp
and it would be equal
to the value of the TOOL_TEMP parameter at the time the set
operation
was performed. As previously mentioned, parameters are always a string
,
so before we can do any math or comparisions on it, we need to make it
into something a bit more numeric.
Casting
Casting refers to changing one type to another. Going from an int
to a
float
is fine. An int
value of 42 cast as a float
would yield
42.000..
. Going from a float
to an int
, however, “truncates” the
digits after the decimal point, so 42.9999
would become 42
in many
cases you will want to round the number up to the nearest whole number
before casting.
So how do we cast types? Jinja2 has what are called filters. Filters
are applied by supplying a |
(pipe) followed by the filter type. So
to cast our TOOL_TEMP parameter string
as an int
, we would do
params.TOOL_TEMP|int
in order to round a float
to the nearest whole number in order to cast
it as an int
we need to apply the round
filter the round filter takes
a parameter that specifies how many digits after the decimal point to
round the number to. So to get the variable a
rounded to the nearest
whole number and cast it to int
, the filters would look like this
a|round(0)|int
Why are types important? Say in our start print macro we want to preheat
our bed and hotend at the same time but we want to add a sort of wait timer
at the end so the bed temperature has time to “soak in” to the entire bed.
We want this time to be calculated based on the temperature the bed was
when the start print macro was called vs the target temperature of the
print. If we do something like
{% set dwell = params.BED_TEMP * 3 %}
If our BED_TEMP parameter is 10
then BED_TEMP * 3
would be 101010
because the *
operator when applied to a string value duplicates that
string value a number of times. So to multiply bed temp by 3, we have to
first cast it as an int
.
{% set dwell = params.BED_TEMP|int * 3 %}
Scope
Any variable we create within a macro only exists within that macro and
can only be accessed within that macro. This is called scope. There are
some cases where a variable can only be accessed from within a control
structure (such a for loop) and those will be discussed later.
Bringing it together
So let us apply the delay to our macro. If my starting bed temperature
is already at the target temperature because we have been preheating things,
we can say that we only want to “soak it” for 1 minute.
If the bed temperature at the start of the macro execution is less than
20 degrees below the target, we want to “soak it” for 5 minutes.
{% set target = params.BED_TEMP|int %}
{% set current = printer.heater_bed.temperature %}
{% if current < target - 20 %}
G4 P{ 5 * 60 * 1000 } #Milliseconds to dwell
{% else %}
G4 P{ 1 * 60 * 1000 }
{% endif %}