Accessing Updated Macro Variable Values

Basic Information:

Printer Model: All
MCU / Printerboard: All
Host / SBC: All
klippy.log: N/A for now

As part of my functional test code macro, I want to keep track of the executed test steps and to do this, I wanted to use a counter.

I couldn’t find an example and I seem to remember somebody saying that if you are updating a macro variable, I had to do it in another macro.

The current code I’m working with is:

[respond]
default_type: echo

[gcode_macro test_number] ### Executing Test Number
variable_testnumber:  1 
gcode:

[gcode_macro run_increment_test_number]
gcode:
   RESPOND TYPE=command MSG="Current Test Number {printer["gcode_macro test_number"].testnumber}"
   increment_test_number
   RESPOND TYPE=command MSG="Test Number after increment {printer["gcode_macro test_number"].testnumber}"

[gcode_macro increment_test_number]
gcode:
   SET_GCODE_VARIABLE MACRO=test_number VARIABLE=testnumber VALUE={ printer["gcode_macro test_number"].testnumber + 1 }

Now, when I execute “Run Increment Test_Number” for the first time and look in the console window, I get:

Which seems to indcate that the testnumber variable isn’t updated.

But, when I execute the macro again, I get:

It appears that the testnumber variable was executed but, when accessing it within the macro, it doesn’t appear to be updated.

Anybody have any hints for me?

Thanx!

Yep! Macros are not programs they are templates.

The first macro is templated before any of the calls to the second macro ever run. If you were thinking of it like a program: non of the sub routines are called before the main routine exits.

Make a third macro that prints the number and call it.

You can look at what I recently did in here. It’s capturing data in a loop and then calculating a result.

Thanx, I think I understand it now.

I changed my macro code to:

[gcode_macro test_number] ### Executing Test Number
variable_testnumber:  1 
gcode:

[gcode_macro increment]
variable_original: 0
variable_updated: 0
gcode:
   RESPOND TYPE=command MSG="Current Test Number { printer["gcode_macro test_number"].testnumber }"
   {% set original = printer["gcode_macro test_number"].testnumber %}
   RESPOND TYPE=command MSG="Original Test Number { original }"

   SET_GCODE_VARIABLE MACRO=test_number VARIABLE=testnumber VALUE={ printer["gcode_macro test_number"].testnumber + 1 }

   {% set updated = printer["gcode_macro test_number"].testnumber %}

   RESPOND TYPE=command MSG="Updated Test Number { updated } <== Should not be changed from the 'Original'"
   {% if original == updated %}
     RESPOND TYPE=command MSG="Test Number NOT Updated"
   {% else %}
     RESPOND TYPE=command MSG="Test Number UPDATED"
   {% endif %}
   print_final_test_number

[gcode_macro print_final_test_number]
gcode:
   RESPOND TYPE=command MSG="Test Number after increment { printer["gcode_macro test_number"].testnumber }"

In the increment macro, everything after the {% set ... statement is me checking to see if the value in the macro changes after the incrementing (as expected, it doesn’t). After these statements, I invoke the separate macro to print the final number.


The big concern I had was how to structure the macro code so that I could run individual tests and a) stop after the failing one (ie Stop On First Fail) and b) have a value for the test operator/debug technician that indicates which test failed.

Using the knowledge above, I came up with the macro/code format of:

[gcode_macro bump_number]
variable_bumpnumber: 0
variable_bumpok: 1
gcode:

[gcode_macro bump]
gcode:
   SET_GCODE_VARIABLE MACRO=bump_number VARIABLE=bumpnumber VALUE={ 0 }
   SET_GCODE_VARIABLE MACRO=bump_number VARIABLE=bumpok     VALUE={ 1 }
   bump1
   bump2
   bump3
   print_final_bump_number

[gcode_macro bump1]   
gcode:
   {% if printer["gcode_macro bump_number"].bumpok %}
      RESPOND TYPE=command MSG="bump1: Current Bump Number { printer["gcode_macro bump_number"].bumpnumber }"
      SET_GCODE_VARIABLE MACRO=bump_number VARIABLE=bumpnumber VALUE={ printer["gcode_macro bump_number"].bumpnumber + 1 }
#$# Test Code Here
   {% endif %}
   
[gcode_macro bump2]   
gcode:
   {% if printer["gcode_macro bump_number"].bumpok %}
      RESPOND TYPE=command MSG="bump2: Current Bump Number { printer["gcode_macro bump_number"].bumpnumber }"
      SET_GCODE_VARIABLE MACRO=bump_number VARIABLE=bumpnumber VALUE={ printer["gcode_macro bump_number"].bumpnumber + 1 }
#$# Test Code Here
      SET_GCODE_VARIABLE MACRO=bump_number VARIABLE=bumpok VALUE={ 0 } #$# <== Simulate a Test Fail Here
   {% endif %}
   
[gcode_macro bump3]   
gcode:
   {% if printer["gcode_macro bump_number"].bumpok %}
      RESPOND TYPE=command MSG="bump3: Current Bump Number { printer["gcode_macro bump_number"].bumpnumber }"
      SET_GCODE_VARIABLE MACRO=bump_number VARIABLE=bumpnumber VALUE={ printer["gcode_macro bump_number"].bumpnumber + 1 }
#$# Test Code Here
   {% endif %}

[gcode_macro print_final_bump_number]
gcode:
   RESPOND TYPE=command MSG="Bump Number after three simulated tests { printer["gcode_macro bump_number"].bumpnumber }"

The test code for each individual test is placed at the #$# line in the bump# macros. If the test fails the bumpok variable is set to 0 so that subsequent tests are not run.

I’m putting this code in here for your comments as well as a reference for anybody else looking to do something similar.

1 Like

This is one of the main features of DynamicMacros. By using three empty lines, or a custom delimeter (highly recommended), you can split the macro and receive variable updates. Note that this doesn’t work inside if statements or for loops.

DynamicMacro version of your macro:

[gcode_macro run_increment_test_number]
gcode:
   RESPOND TYPE=command MSG="Current Test Number {printer["gcode_macro test_number"].testnumber}"
   increment_test_number



   RESPOND TYPE=command MSG="Test Number after increment {printer["gcode_macro test_number"].testnumber}"
1 Like

Thanx for the suggestion.

I’m not going to use this because the manufacturer is going to support the tests and, right now, they’re at their limit with Klipper right now. I don’t want to add any non-incorporated packages into the mix.

The approach I have is modular and, I think, understandable for anybody familiar with programming.

Thanx again for the suggestion.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.