[[cha:GStat]]

= GStat

== Intro

GStat is a python class used to send messages from linuxcnc to other python programs. +
It uses GObject to deliver messages, making it easy to listen for specific information. +
This is referred to as event-driven programming, which is more efficient then every program +
polling linuxcnc at the same time. +
GladeVCP, Gscreen, Gmoccapy and QtVCP use GStat extensively. +
GStat is in the hal_glib module

== Overview

First a program imports the hal_glib module and instantiates GStat. +
Then it 'connects' to the messages it wishes to monitor. +
GStat checks linuxcnc's status every 100ms and if there are differences from +
the last check, it will send a callback message to all the connected programs +
with the current status. +
When GStat calls the registered function, it sends the GStat object plus any return codes from the message. +
typical code signatures: +
[source,python]
----
GSTAT.connect('MESSGAE-TO-LISTEN-FOR', FUNCTION_TO_CALL)

def FUNCTION_TO_CALL(gstat_object, return_codes):
----

Often LAMBDA is used to strip the GSTAT object and manipulate the return codes): +

[source,python]
----
GSTAT.connect('MESSGAE-TO-LISTEN-FOR', lambda o, return: FUNCTION_TO_CALL(not return))

def FUNCTION_TO_CALL(return_codes):
----

== Sample GStat Code

There are some basic patterns for using GStat, depending on what library +
You are using them in. +
If using GStat with Gladevcp, Gscreen, or Qtvcp, the gobject library is not needed, +
Those toolkits already set up Gobject. +

=== Sample HAL component code pattern
This program creates two HAL pins that output the status +
of G20/G21.

[source,python]
----
#!/usr/bin/env python

import hal
from hal_glib import GStat
import gobject
GSTAT = GStat()

# callback to change HAL pin state
def mode_changed(obj, data):
        h['g20'] = not data
        h['g21'] = data

# Make a component and pins
h = hal.component("metric_status")
h.newpin("g20", hal.HAL_BIT, hal.HAL_OUT)
h.newpin("g21", hal.HAL_BIT, hal.HAL_OUT)
h.ready()

# connect a GSTAT message to a callback function
GSTAT.connect("metric-mode-changed",mode_changed)

# force GSTAT to initialize states
GSTAT.forced_update()

# loop till exit
try:
        gobject.MainLoop().run()
except KeyboardInterrupt:
    raise SystemExit
----

This would be loaded with 'loadusr python PATH-TO-FILE/FILENAME.py' +
or if you need to wait for the pins to be made before continuing: +
'loadusr python -Wn metric_status PATH-TO-FILE/FILENAME.py' +
The pins would be: 'metric_status.g20' and 'metric_status.g21' +

=== GladeVCP python extension code pattern
This file assumes there are three GTK labels named: +
'state_label' +
'e_state_label' +
'interp_state_label' +

[source,python]
----
#!/usr/bin/env python

from hal_glib import GStat
GSTAT = GStat()

class HandlerClass:

    def __init__(self, halcomp, builder, useropts):
        self.builder = builder

        GSTAT.connect("state-estop",lambda w: self.update_estate_label('ESTOP'))
        GSTAT.connect("state-estop-reset",lambda w: self.update_estate_label('RESET'))

        GSTAT.connect("state-on",lambda w: self.update_state_label('MACHIBE ON'))
        GSTAT.connect("state-off",lambda w: self.update_state_label('MACHINE OFF'))

        GSTAT.connect("interp-paused",lambda w: self.update_interp_label('Paused'))
        GSTAT.connect("interp-run",lambda w: self.update_interp_label('Run'))
        GSTAT.connect("interp-idle",lambda w: self.update_interp_label('Idle'))

    def update_state_label(self,text):
        self.builder.get_object('state_label').set_label("State: %s" % (text))

    def update_estate_label(self,text):
        self.builder.get_object('e_state_label').set_label("E State: %s" % (text))

    def update_interp_label(self,text):
        self.builder.get_object('interp_state_label').set_label("Interpeter State: %s" % (text))

def get_handlers(halcomp,builder,useropts):
    return [HandlerClass(halcomp,builder,useropts)]
----

=== QTVCP python extension code pattern
Qtvcp extends GStat, so must be loaded differently but all the messages are available in Qtvcp. +
This handler file assumes there are three QLabels named: +
'state_label' +
'e_state_label' +
'interp_state_label' +
[source,python]
----
#!/usr/bin/env python

from qtvcp.core import Status
GSTAT = Status()

class HandlerClass:

    def __init__(self, halcomp,widgets,paths):
        self.w = widgets

        GSTAT.connect("state-estop",lambda w: self.update_estate_label('ESTOP'))
        GSTAT.connect("state-estop-reset",lambda w: self.update_estate_label('RESET'))

        GSTAT.connect("state-on",lambda w: self.update_state_label('MACHIBE ON'))
        GSTAT.connect("state-off",lambda w: self.update_state_label('MACHINE OFF'))

        GSTAT.connect("interp-paused",lambda w: self.update_interp_label('Paused'))
        GSTAT.connect("interp-run",lambda w: self.update_interp_label('Run'))
        GSTAT.connect("interp-idle",lambda w: self.update_interp_label('Idle'))

    def update_state_label(self,text):
        self.w.state_label.setText("State: %s" % (text))

    def update_estate_label(self,text):
        self.w.e_state_label.setText("E State: %s" % (text))

    def update_interp_label(self,text):
        self.winterp_state_label.setText("Interpeter State: %s" % (text))

def get_handlers(halcomp,builder,useropts):
    return [HandlerClass(halcomp,widgets,paths)]
----
==  Messages

*periodic* :: '(returns nothing)' -
sent every 100 ms.

*state-estop* :: '(returns nothing)' -
Sent when linuxcnc is goes into estop.

*state-estop-reset*:: '(returns nothing)' -
Sent when linuxcnc comes out of estop.

*state-on* :: '(returns nothing)' -
Sent when linuxcnc is in machine on state.

*state-off*:: '(returns nothing)' -
Sent when linuxcnc is in machine off state.

*homed* :: '(returns string)' -
Sent as each joint is homed.

*all-homed* :: '(returns nothing)' -
Sent when all defined joints are homed.

*not-all-homed* :: '(returns string)' -
Sends a list of joints not currently homed.

*override_limits_changed* :: '(returns string)' -
Sent if linuxcnc has been directed to override it's limits.

*hard-limits-tripped* :: '(returns bool, Python List)' -
Sent when any hard limit is tripped.
bool indicates if any limit is tripped, the list shows all available joint's current limit values.

*mode-manual* :: '(returns nothing)' -
Sent when linuxcnc switches to manual mode.

*mode-mdi* :: '(returns nothing)' -
Sent when linuxcnc switches to mdi mode.

*mode-auto* :: '(returns nothing)' -
Sent when linuxcnc switches to auto mode.

*command-running* :: '(returns nothing)' -
Sent when running a program or MDI

*command-stopped* :: '(returns nothing)' -
Sent when a program or MDI stopped

*command-error* :: '(returns nothing)' -
Sent when there is a command error

*interp-run* :: '(returns nothing)' -
Sent when linuxcnc's interpreter is running an MDI or program.

*interp-idle* :: '(returns nothing)' -
Sent when linuxcnc's interpreter idle.

*interp-paused* :: '(returns nothing)' -
Sent when linuxcnc's interpreter is paused.

*interp-reading* :: '(returns nothing)' -
Sent when linuxcnc's interpreter is reading.

*interp-waiting* :: '(returns nothing)' -
Sent when linuxcnc's interpreter is waiting.

*jograte-changed* :: '(returns float)' -
Sent when jog rate has changed. +
Linuxcnc does not have an internal jog rate. +
This is GStat's internal jog rate. +
It is expected to be in the machine's native units regardless of the current unit mode .

*jograte-angular-changed* :: '(returns float)' -
Sent when the angular jog rate has changed. +
Linuxcnc does not have an internal angular jog rate. +
This is GStat's internal jog rate. +
It is expected to be in the machine's native units regardless of the current unit mode .

*jogincrement-changed* :: '(returns float)' -
Sent when jog increment has changed. +
Linuxcnc does not have an internal jog increment. +
This is GStat's internal jog increment. +
It is expected to be in the machine's native units regardless of the current unit mode .

*jogincrement-angular-changed* :: '(returns float)' -
Sent when angular jog increment has changed. +
Linuxcnc does not have an internal angular jog increment. +
This is GStat's internal angular jog increment. +
It is expected to be in the machine's native units regardless of the current unit mode .

*program-pause-changed* :: '(returns bool)' -
Sent when program is paused/unpaused.

*optional-stop-changed* :: '(returns bool)' -
Sent when optional stop is set/unset

*block-delete-changed* :: '(returns bool)' -
sent when block delete is set/unset.

*file-loaded* :: '(returns string)' -
Sent when linuxcnc has loaded a file

*reload-display* :: '(returns nothing)' -
Sent when there is a request to reload the display

*line-changed* :: '(returns integer)' -
Sent when linuxcnc has read a new line. +
Linuxcnc does not update this for every type of line.

*tool-in-spindle-changed* :: '(returns integer)' -
Sent when the tool has changed.

*tool-info-changed* :: '(returns python object)' -
Sent when current tool info changes.

*current-tool-offset* :: '(returns python object)' -
Sent when the current tool offsets change.

*motion-mode-changed* :: '(returns integer)' -
Sent when motion's mode has changed

*spindle-control-changed* :: '(returns bool, integer)' -
Sent when spindle direction or running status changes.

*current-feed-rate* :: '(returns float)' -
Sent when the current feed rate changes.

*current-x-rel-position* :: '(returns float)' -
Sent when the X relative position changes.

*current-position* :: '(returns pyobject, pyobject, pyobject)' -
Sent as the current positions changes. +
returns tuples of position,relative position and distance-to-go.

*current-z-rotation* :: '(returns float)' -
Sent as the current rotatated angle around the Z axis changes +

*requested-spindle-speed-changed* :: '(returns float)' -
Sent when the current requested RPM changes

*actual-spindle-speed-changed* :: '(returns float)' -
Sent when the actual RPM changes based on the HAL pin 'spindle.0.speed-in'. +

*spindle-override-changed* :: '(returns float)' -
Sent when the spindle override value changes +
in percent

*feed-override-changed* :: '(returns float)' -
Sent when the feed override value changes +
in percent

*rapid-override-changed* :: '(returns float)' -
Sent when the rapid override value changes +
in percent 0-100

*max-velocity-override-changed* :: '(returns float)' -
Sent when the maximum velocity override value changes +
in units per minute +

*feed-hold-enabled-changed* :: '(returns bool)' -
Sent when feed hold status changes

*itime-mode* :: '(returns bool)' -
Sent when G93 status changes +
(inverse time mode)

*fpm-mode* :: '(returns bool)' -
Sent when G94 status changes +
(feed per minute mode)

*fpr-mode* :: '(returns bool)' -
Sent when G95 status changes +
(feed per revolution mode)

*css-mode* :: '(returns bool)' -
Sent when G96 status changes +
(constant surface feed mode)

*rpm-mode* :: '(returns bool)' -
Sent when G97 status changes +
(constatnt RPM mode)

*radius-mode* :: '(returns bool)' -
Sent when G8 status changes +
display X in radius mode

*diameter-mode* :: '(returns bool)' -
Sent when G7 status changes +
display X in Diameter mode

*flood-changed* :: '(returns bool)' -
Sent when flood coolant state changes.

*mist-changed* :: '(returns bool )' -
Sent when mist coolant state changes.

*m-code-changed* :: '(returns string)' -
Sent when active M codes change

*g-code-changed* :: '(returns string)' -
Sent when active G code change

*metric-mode-changed* :: '(returns bool)' -
Sent when G21 status changes

*user-system-changed* :: '(returns string)' -
Sent when the reference coordinate system (G5x) changes

*mdi-line-selected* :: '(returns string, string)' -
intended to be sent when an MDI line is selected by user. +
This depends on the widget/libraries used. +

*gcode-line-selected* :: '(returns integer)' -
intended to be sent when a gcode line is selected by user. +
This depends on the widget/libraries used. +

*graphics-line-selected* :: '(returns integer)' -
intended to be sent when graphics line is selected by user. +
This depends on the widget/libraries used. +

*graphics-gcode-error* :: '(returns string)' -
intended to be sent when a gcode error is found when loading. +
This depends on the widget/libraries used. +

*graphics-gcode-properties* :: '(returns string)' -
intended to be sent when gcode is loaded. +
This depends on the widget/libraries used. +

*graphics-view-changed* :: '(returns string, python Dict or None)' -
intended to be sent when graphics view is changed. +
This depends on the widget/libraries used. +

*mdi-history-changed* :: '(returns None)' -
intended to be sent when an MDI history needs to be reloaded. +
This depends on the widget/libraries used. +

*machine-log-changed* :: '(returns None)' -
intended to be sent when machine log has changed. +
This depends on the widget/libraries used. +

*update-machine-log* :: '(returns string, string)' -
intended to be sent when updating the machine. +
This depends on the widget/libraries used. +

*move-text-lineup* :: '(returns None)' -
intended to be sent when moving the cursor one line up in gcode display. +
This depends on the widget/libraries used. +

*move-text-linedown* :: '(returns None)' -
intended to be sent when moving the cursor one line down in gcode display. +
This depends on the widget/libraries used. +

*dialog-request* :: '(returns python dict)' -
intended to be sent when requesting a gui dialog. +
It uses a python dict for communication. +
The dict must include the following keyname pair: +
* NAME: 'requested dialog name' +
The dict usually has more keyname pair - it depends on the dialog. +
dialogs return information using a general message +
This depends on the widget/libraries used. +

*focus-overlay-changed* :: '(returns bool, string, python object)' -
intended to be sent when requesting an overlay to be put over the display. +
This depends on the widget/libraries used. +

*play-sound* :: '(returns string)' -
intended to be sent when requesting a specific sound file to be played. +
This depends on the widget/libraries used. +

*virtual-keyboard* :: '(returns string)' -
intended to be sent when requesting a on screen keyboard. +
This depends on the widget/libraries used. +

*dro-reference-change-request* :: '(returns integer)' -
intended to be sent when requesting a DRO widget to change it's reference. +
0 = machine, 1 = relative, 3 = distance-to-go +
This depends on the widget/libraries used. +

*show-preferences* :: '(returns None)' -
intended to be sent when requesting the screen preferences to be displayed. +
This depends on the widget/libraries used. +

*shutdown* :: '(returns None)' -
intended to be sent when requesting linuxcnc to shutdown. +
This depends on the widget/libraries used. +

*error* :: '(returns integer, string)' -
intended to be sent when an error has been reported . +
integer represents the kind of error. ERROR, TEXT or DISPLAY +
string is the actual error message. +
This depends on the widget/libraries used. +

*general* :: '(returns python dict)' -
intended to be sent when message must be sent that is not covered by a more specific message. +
General message should be used a sparsely as reasonable because all object connected to it will have to parse it. +
It uses a python dict for communication. +
The dict should include and be checked for a unique id  keyname pair: +
* ID: 'UNIQUE_ID_CODE' +
The dict usually has more keyname pair - it depends on implementation. +

*forced-update* :: '(returns None)' -
intended to be sent when one wishes to initialize or arbitrarily update an object. +
This depends on the widget/libraries used. +

== Functions
These are convenience functions that are commonly used in programming

*set_jograte* :: '(float)' -
Linuxcnc has no internal concept of jog rate -each GUI has it's own. +
This is not always convenient. +
This function allows one to set a jog rate for all objects connected to the +
signal 'jograte-changed'. +
It defaults to 15 +
GSTAT.set_jog_rate(10) would set the jog rate to 10 machine-units-per-minute and emit the jograte-changed signal. +

*get_jograte()* :: '(Nothing)' -
x = GSTAT.get_jograte would return GSTAT's current internal jograte.

*set_jograte_angular* :: '(float)' -

*get_jograte_angular* :: '(None)' -

*set_jog_increment_angular* :: '(float, string)' -

*get_jog_increment_angular* :: '(None)' -

*set_jog_increments* :: '(float, string)' -

*get_jog_increments* :: '(None)' -

*is_all_homed* :: '(nothing)' -
This will return the current state of all_homed (BOOL).

*machine_is_on* :: '(nothing)' -
This will return the current state of machine (BOOL).

*estop_is_clear* :: '(nothing)' -
This will return the state of Estop (BOOL)

*set_tool_touchoff* :: '(tool,axis,value)' -
This command will record the current mode, switch to MDI mode, +
invoke the MDI command: G10 L10 P[TOOL] [AXIS] [VALUE] +
wait for it to complete +
invoke G43 +
wait for it to complete +
switch back to the original mode. +

*set_axis_origin* :: '(axis,value)' -
This command will record the current mode, switch to MDI mode, +
invoke the MDI command: G10 L20 P0 [AXIS] [VALUE] +
wait for it to complete +
switch back to the original mode. +
emit a 'reload-display' signal. +

*do_jog* :: '(axis_number,direction, distance)' -
This will jog an axis continuously or at a set distance. +
You must be in the proper mode to jog.

*check_for_modes* :: '(mode)' -
This function checks for required linuxcnc mode. +
It returns a python tuple (state, mode) +
mode will be set the mode the system is in +
state will set to: +
false if mode is 0 +
false if machine is busy +
true if linuxcnc is in the requested mode +
None if possible to change, but not in requested mode +

*get_current_mode* :: '(nothing)' -
returns integer: the current linuxcnc mode. +

*set_selected_joint* :: '(integer)' -
records the selected joint number internally. +
requests the joint to be selected by emitting the +
'joint-selection-changed' message. +

*get_selected_joint* :: '(None)' -
returns integer representing the internal selected joint number. +

*set_selected_axis* :: '(string)' -
records the selected axis letter internally. +
requests the axis to be selected by emitting the +
'axis-selection-changed' message. +

*get_selected_axis* :: '(None)' -
returns string representing the internal selected axis letter. +

*is_man_mode* :: '(None)' -

*is_mdi_mode* :: '(None)' -

*is_auto_mode* :: '(None)' -

*is_on_and_idle* :: '(None)' -

*is_auto_running* :: '(None)' -

*is_auto_paused* :: '(None)' -

*is_file_loaded* :: '(None)' -

*is_metric_mode* :: '(None)' -

*is_spindle_on* :: '(None)' -

*shutdown* :: '(None)' -

== Known Issues

Some status points are reported wrongly during a running program. +
This is because the interpreter runs ahead of the current position of a running program. +
This will hopefully be resolved with the merge of state-tags branch. +


