Skip to content

Commit 64e7321

Browse files
authored
Merge pull request #4132 from hdiethelm/halcmd_getrt
Proper and safe evaluation of realtime capability
2 parents 9af46f6 + ac599f1 commit 64e7321

24 files changed

Lines changed: 547 additions & 211 deletions

docs/po4a.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
[type: AsciiDoc_def] src/config/moveoff.adoc $lang:build/adoc/$lang/config/moveoff.adoc
5252
[type: AsciiDoc_def] src/config/pncconf.adoc $lang:build/adoc/$lang/config/pncconf.adoc
5353
[type: AsciiDoc_def] src/config/python-hal-interface.adoc $lang:build/adoc/$lang/config/python-hal-interface.adoc
54+
[type: AsciiDoc_def] src/config/python-lcnc_realtime.adoc $lang:build/adoc/$lang/config/python-lcnc_realtime.adoc
5455
[type: AsciiDoc_def] src/config/python-interface.adoc $lang:build/adoc/$lang/config/python-interface.adoc
5556
[type: AsciiDoc_def] src/config/stepconf.adoc $lang:build/adoc/$lang/config/stepconf.adoc
5657
[type: AsciiDoc_def] src/config/stepper.adoc $lang:build/adoc/$lang/config/stepper.adoc

docs/src/Master_Documentation.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,8 @@ include::config/python-interface.adoc[]
300300

301301
include::config/python-hal-interface.adoc[]
302302

303+
include::config/python-lcnc_realtime.adoc[]
304+
303305
include::gui/gstat.adoc[]
304306

305307
include::gui/vismach.adoc[]

docs/src/Submakefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ DOC_SRCS_EN := \
141141
config/moveoff.adoc \
142142
config/pncconf.adoc \
143143
config/python-hal-interface.adoc \
144+
config/python-lcnc_realtime.adoc \
144145
config/python-interface.adoc \
145146
config/stepconf.adoc \
146147
config/stepper-diagnostics.adoc \

docs/src/config/python-hal-interface.adoc

Lines changed: 64 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -48,23 +48,63 @@ Message level constants:
4848
* `hal.MSG_DBG` - Additionally include debugging information
4949
* `hal.MSG_ALL` - Print all messages encountered, disregarding level
5050

51+
Realtime type constants:
52+
53+
* `hal.REALTIME_TYPE_UNINITIALIZED` - Real time module not running
54+
* `hal.REALTIME_TYPE_NONE` - No realtime available
55+
* `hal.REALTIME_TYPE_UNKNOWN` - Only used when LINUXCNC_FORCE_REALTIME=1 is set. Unknown, no PREEMPT_DYNAMIC but SCHED_FIFO is available. Not recommended.
56+
* `hal.REALTIME_TYPE_PREEMPT_DYNAMIC` - Preempt dynamic: available in vanilla kernel. Only used when LINUXCNC_FORCE_REALTIME=1 is set. Not recommended.
57+
* `hal.REALTIME_TYPE_PREEMPT_RT` - Preempt RT
58+
* `hal.REALTIME_TYPE_RTAI` - RTAI kernel mode
59+
* `hal.REALTIME_TYPE_LXRT` - LXRT, userspace implementation for RTAI
60+
* `hal.REALTIME_TYPE_XENOMAI` - Xenomai 3
61+
* `hal.REALTIME_TYPE_XENOMAI_EVL` - Xenomai 4 aka Xenomai EVL
62+
5163
System information:
5264

5365
* `hal.is_kernelspace` - One (1) if RTAPI runs in the kernel, otherwise zero (0)
5466
* `hal.is_userspace` - Inverted `hal.is_kernelspace`
5567
* `hal.kernel_version` - A string specifying the real-time kernel version if `hal.is_kernelspace` is one.
5668
Otherwise it specifies `"Not Available"`.
57-
* `hal.is_rt` - One (1) if the system runs in real-time, otherwise zero (0)
58-
* `hal.is_sim` - Inverted `hal.is_rt`
69+
* `hal.is_rt` - One (1) if the system runs in real-time, otherwise zero (0) *DEPRECATED*: Use `lcnc_realtime.verify()`
70+
* `hal.is_sim` - Inverted `hal.is_rt` *DEPRECATED*: Use `lcnc_realtime.verify()`
5971

6072
=== hal methods
6173

74+
hal.is_initialized()::
75+
Returns a boolean to indicate whether hal is initialized. The hal is initialized when there is
76+
at least one component. If this is not the case, many of the following functions will throw
77+
a `RuntimeError` exception: `Cannot call before creating component`
78+
79+
.Example:
80+
[source,python]
81+
----
82+
#The hal must be initialized to use many of the hal functions
83+
#If it is not initialized, create a component which will initialize it
84+
#The component will be cleaned up after comp goes out of scope
85+
#Use pid as component identifier, so it is unlikely to collide
86+
#with existing components
87+
comp_name = f"halpy{os.getpid()}"
88+
if not hal.is_initialized():
89+
comp = hal.component(comp_name)
90+
91+
type = hal.get_realtime_type()
92+
----
93+
94+
hal.get_realtime_type()::
95+
Returns the type of the running realtime system.
96+
Might return `hal.REALTIME_TYPE_UNINITIALIZED` if `rtapi_app` is not running.
97+
See xref:_hal_constants[realtime type constants]. +
98+
Throws a `RuntimeError` exception if HAL is not initialized.
99+
62100
hal.component_exists(_name_:string)::
63-
Returns a boolean to indicate whether or not the specified component exist at this time.
101+
Returns a boolean to indicate whether or not the specified component exist at this time. +
102+
Throws a `RuntimeError` exception if HAL is not initialized.
64103

65104
hal.component_is_ready(_name_:string)::
66105
Returns a boolean to indicate whether or not the specified component is in the ready state.
67-
Also returns False if the component does not exist.
106+
Also returns False if the component does not exist. +
107+
Throws a `RuntimeError` exception if HAL is not initialized.
68108

69109
.Example:
70110
[source,python]
@@ -86,7 +126,8 @@ See 'hal.set_msg_level()' and xref:_hal_constants[message constants] for list of
86126
hal.new_sig(_name_:string, _type_:enum)::
87127
Create a new signal (net) called _name_.
88128
The signal can carry information of _type_ content.
89-
Returns `True` on success.
129+
Returns `True` on success. +
130+
Throws a `RuntimeError` exception if HAL is not initialized.
90131

91132
.Example:
92133
[source,python]
@@ -98,7 +139,8 @@ if not hal.new_sig("signalname", hal.HAL_BIT):
98139
hal.connect(_pinname_:string, _signame_:string)::
99140
Connect the pin _pinname_ to signal _signame_.
100141
Both signal and pin must exist and both pin and signal must be of the same type.
101-
Returns `True` on success.
142+
Returns `True` on success. +
143+
Throws a `RuntimeError` exception if HAL is not initialized.
102144

103145
.Example:
104146
[source,python]
@@ -110,7 +152,8 @@ if not hal.connect("mycomp.pinname", "signalname"):
110152
hal.disconnect(_pinname_:string, _signame_:string)::
111153
Disconnect the pin _pinname_ from signal _signame_.
112154
Both signal and pin must exist.
113-
Returns `True` on success.
155+
Returns `True` on success. +
156+
Throws a `RuntimeError` exception if HAL is not initialized.
114157

115158
.Example:
116159
[source,python]
@@ -121,7 +164,8 @@ if not hal.disconnect("mycomp.pinname"):
121164

122165
hal.pin_has_writer(_pinname_:string)::
123166
Returns `True` if pin with name _pinname_ is attached to a signal and there is at least one writer.
124-
Otherwise, `False` is returned.
167+
Otherwise, `False` is returned. +
168+
Throws a `RuntimeError` exception if HAL is not initialized.
125169

126170
.Example:
127171
[source,python]
@@ -135,8 +179,9 @@ else:
135179
hal.set_p(_name_:string, _value_:mixed)::
136180
Sets the pin or param called _name_ to _value_.
137181
The _name_ is the full name of the pin or param.
138-
The search order is pin names first, then parameter names.
182+
The search order is pin names first, then parameter names. +
139183
Throws a `RuntimeError` exception if the _name_ is not found. +
184+
Throws a `RuntimeError` exception if HAL is not initialized. +
140185
The type of _value_ depends on the type of the pin or param.
141186
Integer scalar types may use integers or a textual representation of an integer to set the value.
142187
Floating point type may use both integer, floating point and textual representation thereof to set the value.
@@ -158,14 +203,16 @@ The same rules for _value_ apply to 'set_s()' as to 'set_p()'. +
158203
+
159204
The 'set_s()' method has one special case when the signal is of type `hal.HAL_PORT` and it is fully connected.
160205
In that case, the call uses the _value_ to set the port's queue size and it must be a positive integer.
161-
See below xref:_hal_port_pipes[on configuring a port].
206+
See below xref:_hal_port_pipes[on configuring a port]. +
207+
Throws a `RuntimeError` exception if HAL is not initialized.
162208

163209
hal.get_value(_name_:string)::
164210
Returns the value of the pin, param or signal with _name_, searched in that order.
165211
Boolean types return `True` or `False`.
166212
Integer scalar types return an integer.
167213
Floating point types return a float.
168-
A `RuntimeError` exception is thrown if no pin, param or signal is found by that name.
214+
A `RuntimeError` exception is thrown if no pin, param or signal is found by that name. +
215+
Throws a `RuntimeError` exception if HAL is not initialized.
169216

170217
.Example:
171218
[source,python]
@@ -174,13 +221,16 @@ value = hal.get_value("iocontrol.0.emc-enable-in")
174221
----
175222

176223
hal.get_info_pins()::
177-
Returns a list of dictionary tuples as in `{"NAME":"pinname", "VALUE":<bool|int|float>, "TYPE":<int>, "DIRECTION":<int>}`.
224+
Returns a list of dictionary tuples as in `{"NAME":"pinname", "VALUE":<bool|int|float>, "TYPE":<int>, "DIRECTION":<int>}`. +
225+
Throws a `RuntimeError` exception if HAL is not initialized.
178226

179227
hal.get_info_params()::
180-
Returns a list of dictionary tuples as in `{"NAME":"paramname", "VALUE":<bool|int|float>, "TYPE":<int>, "DIRECTION":<int>}`.
228+
Returns a list of dictionary tuples as in `{"NAME":"paramname", "VALUE":<bool|int|float>, "TYPE":<int>, "DIRECTION":<int>}`. +
229+
Throws a `RuntimeError` exception if HAL is not initialized.
181230

182231
hal.get_info_signals()::
183-
Returns a list of dictionary tuples as in `{"NAME":"signame", "VALUE":<bool|int|float>, "TYPE":<int>, "DRIVER":"name"|None}`.
232+
Returns a list of dictionary tuples as in `{"NAME":"signame", "VALUE":<bool|int|float>, "TYPE":<int>, "DRIVER":"name"|None}`. +
233+
Throws a `RuntimeError` exception if HAL is not initialized.
184234

185235
.Example:
186236
[source,python]
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
:lang: en
2+
:toc:
3+
4+
[[cha:python-lcnc_realtime]]
5+
= Python module for managing the realtime backend
6+
7+
This documentation describes the `lcnc_realtime` Python module, which provides
8+
a Python API for managing the realtime backend.
9+
10+
This is a Python wrapper of the realtime script.
11+
12+
== Basic usage
13+
14+
.Verify realtime capability and check if the realtime backend is running
15+
[source,python]
16+
----
17+
import lcnc_realtime
18+
19+
print("lcnc_realtime.verify " + str(lcnc_realtime.verify()))
20+
print("lcnc_realtime.status " + str(lcnc_realtime.status()))
21+
----
22+
23+
== methods
24+
25+
hal.verify()::
26+
Returns a boolean to indicate whether the system is realtime capable.
27+
28+
hal.status()::
29+
Returns a boolean to indicate whether the realtime backend is running.

docs/src/hal/halmodule.adoc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,9 @@ Use these to specify details rather then the value they hold.
199199

200200
Read these to acquire information about the realtime system.
201201

202+
* `lcnc_realtime`: See <<cha:python-lcnc_realtime, Python module for managing the realtime backend>>.
202203
* `is_kernelspace`
203-
* `is_rt`
204-
* `is_sim`
205204
* `is_userspace`
205+
* `get_realtime_type()`
206206

207207
// vim: set syntax=asciidoc:
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
:manvolnum: 3
2+
3+
= hal_get_realtime_type(3)
4+
5+
== NAME
6+
7+
hal_get_realtime_type - Get the type of the running realtime
8+
9+
== SYNTAX
10+
11+
hal_realtime_type_t hal_get_realtime_type()
12+
13+
== RETURN VALUE
14+
15+
*hal_get_realtime_type* returns the type of the running realtime system or a HAL status code on failure.
16+
17+
For uspace, this returns *REALTIME_TYPE_UNINITIALIZED* if *rtapi_app* is not running. It is safe to assume
18+
this never happens in realtime components. But userspace components can be loaded without *rtapi_app* being
19+
started.
20+
21+
For rtai, this always returns *REALTIME_TYPE_RTAI*.

docs/src/man/man3/rtapi_is.3.adoc

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,18 @@ int rtapi_is_realtime();
1818
and zero when they run in userspace (e.g., under uspace).
1919

2020
*rtapi_is_realtime()* returns nonzero when capable of running with realtime guarantees.
21+
22+
Always use *hal_get_realtime_type()* instead. *rtapi_is_realtime()* is only available in realtime context.
23+
2124
For rtai, this always returns nonzero (but actually loading realtime modules will fail if not running under the appropriate kernel).
22-
For uspace, this returns nonzero when the running kernel indicates it is capable of realtime performance.
23-
If *rtapi_app* is not setuid root,
24-
this returns nonzero even though *rtapi_app* will not be able to obtain realtime scheduling or hardware access,
25-
so e.g., attempting to *loadrt* a hardware driver will fail.
25+
For uspace, this returns nonzero when the running kernel indicates it is capable of realtime performance and *rtapi_app* has the
26+
required capabilities or is setuid root.
27+
If *rtapi_app* is not setuid root or setcap with the proper capabilities,
28+
this returns zero.
2629

2730
== REALTIME CONSIDERATIONS
2831

29-
May be called from non-realtime or from realtime setup code.
32+
May only be called from realtime setup code.
3033
*rtapi_is_realtime()* may perform filesystem I/O.
3134

3235
== RETURN VALUE

lib/python/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
nf.py
2+
lcnc_realtime.py
23
gscreen
34
gmoccapy/
45
stepconf/

lib/python/hal.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,19 @@
2929

3030
import _hal
3131
from _hal import *
32+
import warnings
33+
import lcnc_realtime
34+
35+
def __getattr__(name):
36+
if name == 'is_rt':
37+
warnings.warn(f"{name} is deprecated, use lcnc_realtime.verify() instead", FutureWarning, stacklevel=2)
38+
return lcnc_realtime.verify()
39+
40+
if name == 'is_sim':
41+
warnings.warn(f"{name} is deprecated, use lcnc_realtime.verify() instead", FutureWarning, stacklevel=2)
42+
return not lcnc_realtime.verify()
43+
44+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
3245

3346
class _ItemWrap(object):
3447
def __new__(cls, item):

0 commit comments

Comments
 (0)