Skip to content

Conversation

@30350n
Copy link

@30350n 30350n commented Jan 24, 2023

Improved version of #5881 (Extend system stats with unixtime), originally proposed by @denis-efimov:

For the moment, it is not possible to make macros based on the host's system time, such as bed leveling once a day or turning things on and off based on real time.
Extending system stats with unixtime, we bring the possibilities of macros to a whole new level.

As described this funcionality would be very useful, especially in combination with [save_variables] to for example track how long you've been using your air filters, without a delayed_gcode script that's continuously running. Here's a more complex example for a set of nevermore/air filter timer macros I've thrown together and used for the past couple of days.

This PR directly adds unixtime to the jinja2 context, instead of adding it to the printer status pseudo-variable, as recommended by @pedrolamas and @Arksine.

Patch has been tested on v0.11.0-86-g6026a99a. I've also added documentation, including an example for using the variable.

@KevinOConnor
Copy link
Collaborator

Thanks, but I'm not sure this is a good fit for the Klipper master branch. As high-level feedback, it seems a bit "esoteric" and I'm not sure it is worth the additional code and documentation complexity.

Separately, the existing (undocumented) printer.toolhead.estimated_print_time status variable is already available. It is a floating point number storing the time in seconds since the main mcu was last reset.

-Kevin

@KevinOConnor KevinOConnor added the pending feedback Topic is pending feedback from submitter label Jan 26, 2023
@30350n
Copy link
Author

30350n commented Jan 26, 2023

it seems a bit "esoteric" and I'm not sure it is worth the additional code and documentation complexity

Do you mean the general concept of having a unixtime like variable or the specific implementation/documentation?

Regarding "code and documentation complexity" the implementation is pretty much as minimal and straightforward as it can get ... the documentation might be suboptimal. I just threw it together quickly, so feel free to cut it down/edit it if you find it confusing.

I didn't know about printer.toolhead.estimated_print_time and while that's definitely useful, it's not as powerful as a unix time stamp, because it doesn't allow you track time beyond printer restarts/when you printer is turned off.

@zellneralex
Copy link
Contributor

zellneralex commented Jan 26, 2023

@KevinOConnor using air filtration gets more and more common at least in the Voron world. Followed by the question, how to monitor the used time and also the service life.
Regardless how it will solved but a possibility to monitor
a) time since a last stored event (even is machine was off for days in between)
b) on time while printing
would be much appreciated.

B) could be solved with printer.toolhead.estimated_print_time but a) is still not possible.

@Sineos
Copy link
Collaborator

Sineos commented Jan 26, 2023

Would such a functionality not better fit into the Moonraker / Web-IF realm?

@zellneralex
Copy link
Contributor

zellneralex commented Jan 26, 2023

@Sineos no as you would spend much more effort to do it there and you would need a specific solution for every component.
We only ask to get the possibility to get a timestamp capability and can work out the rest of it based on macros.

Why off load everything to moonraker where you then need to develop a component for every single piece, instead using the power of macros and give the user the freedom to solve his/her needs? And to be clear for his case you would need also a methode in klipper to push an ON and OFF event of "something" e.g. the Nevermore filter from klipper to moonraker. And also a feedback path from moonraker to klipper to send a notification if a target is reached. The feedback path does not exist at all currently. So you need a much bigger development than in both.

So lets talk about the Nevermore Mini and how I do it currently as I lack a timestamp system:
Nevermore recommendation: 50h printing time, 30d service life; what ever is meet first

So I monitore the printing time and store with SAVE_VARIABLE at every print end. I check the 50h also at the end of print and at klipper start and throw a message if reached.

I do the following, when I change the material:

  • use a macro to reset the active filter time in klipper
  • open up my calendar app in the mobil and add a event that I changed the material

If I do not use the printer for a few days I open the calendar app first to check if the 30 days are reached

@zellneralex
Copy link
Contributor

@Sineos and it should be clear that it could never be done in the WEB-IF as they are only active while open in your browser.

@manu7irl
Copy link

manu7irl commented Jan 26, 2023 via email

@denis-efimov
Copy link

Running a unix script doesn't sound like a good solution because instead of maintaining a macro only, users will be forced to maintain both a macro and a unix script. It will also make sharing macros a headache.
Having a timestamp in macro is a logical and common practice. The comments to this MR showed that the community really needs that functionality.

@zellneralex
Copy link
Contributor

zellneralex commented Jan 26, 2023

@manu7irl sure we can add another client to do all that, but why not do it where it belongs to? The filter is part of a klipper controlled printer. So why not monitor it in there?

As said the whole PR adds only the possibility to readout a timestamp that than can be used later on as the user needs it

kwparams.update(self.template.create_template_context())
kwparams['params'] = gcmd.get_command_parameters()
kwparams['rawparams'] = gcmd.get_raw_command_parameters()
kwparams['unixtime'] = calendar.timegm(time.gmtime())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Python is not my forte, but can't this just be:

Suggested change
kwparams['unixtime'] = calendar.timegm(time.gmtime())
kwparams['unixtime'] = time.time()

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That should actually be fine, yes. I was reading through this and there were some comments that stated that time.time() is not timezone aware and results different results in different timezones, but that seems to have been wrong information.

(Don't forget to also get remove the calendar import.)

@pedrolamas
Copy link
Contributor

I tend to agree with @zellneralex, there are scenarios where something like this would help to solve with just plain Klipper macros.

Having said that, I do wonder if this would be better as a function (set on PrinterGCodeMacro.create_template_context, just like action_emergency_stop and others) instead of a static variable injected into the script context?

The advantage being that we could then have unixtime() returning the unix timestamp, and even something like now() that would return the full easy-to-use struct_time, and as functions, these would only be evaluated if they are actually used in the macro gcode.

@Sineos
Copy link
Collaborator

Sineos commented Jan 26, 2023

@Sineos and it should be clear that it could never be done in the WEB-IF as they are only active while open in your browser.

For me this sounds very much like Arksine/moonraker#124

And it should be clear that I still do not consider this as part of Klipper's core competency

Edit or for the same principle:
Arksine/moonraker#450
Arksine/moonraker#123

@zellneralex
Copy link
Contributor

@Sineos and your opinion counts as much as mine …

yes there are feature requests for that everywhere. If you search for it you might also found similar request at Mainsail, Fluidd and Klipperscreen.

That does not change the fact that nothing exists anywhere.

For me a function to add a time stamp is something I consider as a core function that Klipper should provide.

#
# This file may be distributed under the terms of the GNU GPLv3 license.
import traceback, logging, ast, copy
import traceback, logging, ast, copy, time, calendar
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import traceback, logging, ast, copy, time, calendar
import traceback, logging, ast, copy, time

Remove calendar import, because using time.time() is sufficient.

@30350n
Copy link
Author

30350n commented Jan 26, 2023

And it should be clear that I still do not consider this as part of Klipper's core competency

Even if it's not, what's the problem with having it exactly?
Like ... the end result would be the same, no? Whether you provide such a timestamp directly via klipper, or parse it through 7 different interfaces/abstractions from moonraker, shell scripts or whatever ... it'll still be the same timestamp. Seems pretty pointless to me tbh ...

@30350n
Copy link
Author

30350n commented Jan 26, 2023

@pedrolamas

I do wonder if this would be better as a function (set on PrinterGCodeMacro.create_template_context, just like action_emergency_stop and others) instead of a static variable injected into the script context?

I actually implemented it as a function first, but a) the variable definitions implementation was smaller, b) from how I understand it, what you get in your macro is the same eitherways. Even if it's a function, it'll still be evaluated when the macro will be evaluated thus resulting in the same timestamp throughout your whole macro.

So there's nothing really more "dynamic" about a function from what I can tell. The other possible consideration would be """performance""", because with the variable implementation time.time() would be called for every macro evaluation, while for the function implementation it'd be called for every macro evaluation if the macro contains a unixtime() call, but possibly multiple times, if there are multiple unixtime() calls.
Even worse, those could probably all return slightly different values, which doesn't make much sense and would only be confusing the end user.

The variable implementation is the way to go, imo.

@30350n
Copy link
Author

30350n commented Jan 26, 2023

A possible alternative would be a self caching function I guess.

@KevinOConnor
Copy link
Collaborator

FWIW, there is already a timestamp available (printer.toolhead.estimated_print_time). If desirable, we could add another (static) variable somewhere with the estimated system time that the mcu was last reset. (Thus, unixtime ~= mcu_start_system_time + estimated_print_time.) I'm not sure of a good place to add that additional variable though.

-Kevin

@pedrolamas
Copy link
Contributor

pedrolamas commented Jan 26, 2023

Got to say, I would have never guessed what estimated_print_time is actually for!! 😅

@KevinOConnor that sure sounds like a good compromise to me!

@KevinOConnor
Copy link
Collaborator

Got to say, I would have never guessed what estimated_print_time is actually for!!

That's why it is undocumented - it's a terrible name. Although undocumented, it is used in a few places for display_templates (for animations). It is probably worth renaming it, but I'm not sure what a good naming convention should be.

-Kevin

@pedrolamas
Copy link
Contributor

In *nix land, uptime might be the closest thing, so why not something like mcu_uptime?

@30350n
Copy link
Author

30350n commented Jan 26, 2023

That'd atleast allow for the functionality to be implemented at all, I think I'd still prefer a direct timestamp without the unnecessary calculations, but yeah.

Regarding naming estimated_print_time print time is definitely bad, mcu_startup_time and mcu_uptime seem way more reasonable.

@Arksine
Copy link
Collaborator

Arksine commented Jan 26, 2023

I think it would make most sense to report mcu_startup_time with mcu_uptime in the toolhead object. The toolhead may not use it, however grouping them together at least makes documentation a bit easier since their usage is tied together.

@KevinOConnor
Copy link
Collaborator

It's possible to add these fields to the toolhead object, but the toolhead.py code is already very complex. I'm a little leery of adding unrelated complexity to it.

Another option is to add these fields to the idle_timeout module. That module is already tracking timestamps.

We could add new fields such as current_mcu_time, last_print_start_time, and last_print_start_ostime. Then go on to deprecate printer.toolhead.estimated_print_time and printer.idle_timeout.printing_time. (With printing_time being replaced by current_mcu_time - last_print_start_time and unixtime being current_mcu_time - last_print_start_time + last_print_start_ostime.)

-Kevin

@30350n
Copy link
Author

30350n commented Jan 26, 2023

In that case last_print_start_time and last_print_start_ostime would have to already been set at mcu startup (with idle_timeout.printing_time this wasn't the case iirc), otherwise you'd first have to start a print to use the functionality, which is stupid.

@KevinOConnor
Copy link
Collaborator

Just initialize last_print_start_time and last_print_start_ostime with appropriate values at startup. (The existing printing_time value is always zero when state != "Printing", so seeding initial values has no impact on it.)

-Kevin

@pedrolamas
Copy link
Contributor

pedrolamas commented Jan 26, 2023

In the suggested last_print_start_ostime above, would this be a Unix timestamp for when the MCU started?

Because if that is the case, in my opinion, the name doesn't seem to be very clear.

@30350n
Copy link
Author

30350n commented Jan 26, 2023

What about changing last_print_start_ostime to mcu_startup_time/mcu_startup_unixtime? Basically instead of having a unix time stamp for every print start, having one for the moment the mcu started up.
That'd make the required calculations for unixtime less complicated and more sensible imo (unixtime = mcu_startup_unixtime + current_mcu_time.
I also still think mcu_uptime is more descriptive than current_mcu_time as it makes it clear that the time is relative to mcu startup.

@30350n
Copy link
Author

30350n commented Feb 1, 2023

I reimplemented the functionality with the discussed changes.

@KevinOConnor KevinOConnor removed the pending feedback Topic is pending feedback from submitter label Feb 3, 2023
@KevinOConnor KevinOConnor self-assigned this Feb 13, 2023
@30350n
Copy link
Author

30350n commented Mar 13, 2023

Can someone please review the new implementation?

@pedrolamas
Copy link
Contributor

Just gave this a try on my simulated docker container, works as designed and seems like a nice feature!

@30350n
Copy link
Author

30350n commented Mar 21, 2023

Thanks a lot for reviewing!

@30350n 30350n requested a review from pedrolamas April 3, 2023 16:40
@30350n
Copy link
Author

30350n commented Jun 1, 2023

Any chance someone could take a look at this again?

@KevinOConnor
Copy link
Collaborator

Sorry, for the delay. I do plan to look at this further. Alas, my slow response is due to lack of time.

-Kevin

@Droniac75
Copy link

pr#4694 offers timestamp functionality...without digging into other parts of Klipper

@github-actions github-actions bot added the Stale label Aug 12, 2023
@github-actions github-actions bot closed this Aug 19, 2023
@30350n
Copy link
Author

30350n commented Aug 20, 2023

What's the purpose of this bot?
Can someone please reopen this, this hasn't been merged yet.

@KevinOConnor
Copy link
Collaborator

This PR was inadvertently closed due to a regression introduced by #6293.

-Kevin

@nelsongraca
Copy link

why not just expose everything as a new module called [time]
we can expose everything through get_stats and access through the printer object no?

@30350n
Copy link
Author

30350n commented Dec 15, 2024

Any updates on this?

I've been running this for over 1.5 years now, just manually patched over in my klipper folder and I'm growing tired of having to stash the changes everytime I want to update 😅

(It hasn't been causing any issue this whole time btw.)

@30350n
Copy link
Author

30350n commented Nov 19, 2025

It's been almost another year and I've still been using this without any problems. Because of conflicts with the base repository, my inexperience at that time (committing the changes directly to master) and a generally pretty cluttered git history, I decided to move this to a new PR (#7124), as I would still like this to get merged.

For anyone subscribed to this, feel free to follow the discussions there too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants