This documentation describes the hal Python module, which provides a Python API for creating and accessing HAL pins and signals.
|
Important
|
The classes inside the Python module You should only import and access the |
1. Basic usage
#!/usr/bin/env python3
import hal
import time
comp = hal.component("multiply")
comp.newpin("in-a", hal.Type.REAL, hal.Dir.IN)
comp.newpin("in-b", hal.Type.REAL, hal.Dir.IN)
comp.newpin("out", hal.Type.REAL, hal.Dir.OUT)
comp.ready()
try:
while True:
comp['out'] = comp['in-a'] * comp['in-b']
time.sleep(0.001)
except KeyboardInterrupt:
raise SystemExit
2. Class hal
2.1. hal enumerations and constants
There are many constants used to indicate type, direction, levels and much more. These are usually integer values with a symbolic name. It is preferable in code to use the symbolic name for readability and portability.
Enumerations have been added to aid the readability even more. They use the Python IntEnum base class. The IntEnum values behave as numerical values, but add clear visibility features for readability. The old defined constants are still supported for backwards compatibility.
# Old (deprecated) style:
comp.newpin("in-a", hal.HAL_FLOAT, hal.HAL_IN)
# New style:
comp.newpin("in-a", hal.Type.REAL, hal.Dir.IN)
2.1.1. HAL types
The hal.Type enum is used to specify the type of pin, parameter and signal:
-
hal.Type.BOOL- A boolean usingTrueandFalse -
hal.Type.SINT- A signed quantity with range -263…+263-1 -
hal.Type.UINT- An unsigned quantity with range 0…+264-1 -
hal.Type.REAL- A floating point quantity of range ±1.80×10308 -
hal.Type.PORT- An opaque quantity representing a communication channel (pins only) -
hal.Type.S32- A signed quantity with range -231…+231-1 (see notes below) -
hal.Type.U32- An unsigned quantity with range 0…+232-1 (see notes below)
There are aliases in the hal.Type enum for all types with the HAL_ prefix (e.g. hal.Type.HAL_BOOL, etc.).
|
Note
|
The constants previously used ( |
|
Important
|
The 32-bit types will soon be replaced by 64-bit types. The names will remain for some time, but they will map to the larger type automatically. The |
2.1.2. HAL direction
Just like types, hal.Dir enum is the enumeration to specify direction of pins and parameters. The direction for both pin and parameter direction is unified in one enumerated type:
-
hal.Dir.IN- An input pin -
hal.Dir.OUT- An output pin -
hal.Dir.IO- A bidirections pin -
hal.Dir.RW- A read/write parameter -
hal.Dir.RO- A read-only parameter
Also the direction enums are available with the HAL_ prefix (like hal.Dir.HAL_IN).
2.1.3. Old style HAL constants (deprecated)
Old style constants (deprecated): hal.HAL_BIT, hal.HAL_S32, hal.HAL_U32, hal.HAL_S64, hal.HAL_U64, hal.HAL_FLOAT, hal.HAL_PORT.
Old style deprecated pin direction constants (deprecated): hal.HAL_IN, hal.HAL_OUT, hal.HAL_IO.
Old style deprecated parameter access constants (deprecated): hal.HAL_RO, hal.HAL_RW.
2.1.4. Message level constants
-
hal.MSG_NONE- No messages at all -
hal.MSG_ERR- Only errors -
hal.MSG_WARN- Both warnings and errors -
hal.MSG_INFO- Both informational, warning and error messages -
hal.MSG_DBG- Additionally include debugging information -
hal.MSG_ALL- Print all messages encountered, disregarding level
2.1.5. Realtime type enumerations
The enumerated type hal.RTType is used in the hal.get_realtime_type() method.
-
hal.RTType.UNINITIALIZED- Real time module not running -
hal.RTType.NONE- No realtime available -
hal.RTType.UNKNOWN- Only used when LINUXCNC_FORCE_REALTIME=1 is set. Unknown, no PREEMPT_DYNAMIC but SCHED_FIFO is available. Not recommended. -
hal.RTType.PREEMPT_DYNAMIC- Preempt dynamic: available in vanilla kernel. Only used when LINUXCNC_FORCE_REALTIME=1 is set. Not recommended. -
hal.RTType.PREEMPT_RT- Preempt RT -
hal.RTType.RTAI- RTAI kernel mode -
hal.RTType.LXRT- LXRT, userspace implementation for RTAI -
hal.RTType.XENOMAI- Xenomai 3 -
hal.RTType.XENOMAI_EVL- Xenomai 4 aka Xenomai EVL
The realtime enumerations are also available as constants under the hal.REALTIME_TYPE_xxx name where xxx is one of the above.
2.1.6. Component type enumeration
The enumerated type hal.CompType is used in hal.query.comp() and hal.query.comps().
-
hal.CompType.REALTIME- a realtime component -
hal.CompType.USER- a user non-realtime component -
hal.CompType.OTHER- (HAL internal value; should normally not be visible)
2.1.7. Other constants
System information:
-
hal.is_kernelspace- One (1) if RTAPI runs in the kernel, otherwise zero (0) -
hal.is_userspace- Invertedhal.is_kernelspace -
hal.kernel_version- A string specifying the real-time kernel version ifhal.is_kernelspaceis one. Otherwise it specifies"Not Available". -
hal.is_rt- One (1) if the system runs in real-time, otherwise zero (0) DEPRECATED: Uselcnc_realtime.verify() -
hal.is_sim- Invertedhal.is_rtDEPRECATED: Uselcnc_realtime.verify()
2.2. hal methods
- hal.is_initialized() → bool
-
Returns a boolean to indicate whether hal is initialized. This methods should always return
Truebecause the HAL module is automatically initialized at import time.Example:import hal assert hal.is_initialized(), "fatal: HAL failed to initialize at import" - hal.get_realtime_type() → hal.RTType
-
Returns the type of the running realtime system. Might return
hal.RTType.UNINITIALIZEDifrtapi_appis not running. See realtime type constants. See also LinuxCNC Realtime check. - hal.component_exists(name:string) → bool
-
Returns a boolean to indicate whether or not the specified component exist at this time.
- hal.component_is_ready(name:string) → bool
-
Returns a boolean to indicate whether or not the specified component is in the ready state. Also returns False if the component does not exist.
Example:if not hal.component_is_ready("testpanel"): compmsg = "ready" if hal.component_exists("testpanel") else "loaded" print("Expected component 'testpanel' to be {}".format(compmsg)) os.exit(1) - hal.set_msg_level(lvl:int)
-
Set the message level that controls the amount of information being printed and forwarded from real-time components. The lvl argument must be one of the message constants.
- hal.get_msg_level() → int
-
Return the current message level. See hal.set_msg_level() and message constants for list of possible returned values.
- hal.new_sig(name:string, type:enum) → bool
-
Create a new signal (net) called name. The signal can carry information of type content. Returns
Trueon success.Example:if not hal.new_sig("signalname", hal.Type.BOOL): ...handle error... - hal.connect(pinname:string, signame:string) → bool
-
Connect the pin pinname to signal signame. Both signal and pin must exist and both pin and signal must be of the same type. Returns
Trueon success.Example:if not hal.connect("mycomp.pinname", "signalname"): ...handle error... - hal.disconnect(pinname:string, signame:string) → bool
-
Disconnect the pin pinname from signal signame. Both signal and pin must exist. Returns
Trueon success.Example:if not hal.disconnect("mycomp.pinname"): ...handle error... - hal.pin_has_writer(pinname:string) → bool
-
Returns
Trueif pin with name pinname is attached to a signal and there is at least one writer. Otherwise,Falseis returned.Example:if hal.pin_has_writer("mycomp.0.pin.02"): print("Pin has writer") else: print("Pin has no writer or no signal attached") - hal.set_p(name:string, value:mixed)
-
Sets the pin or param called name to value. The name is the full name of the pin or param. The search order is pin names first, then parameter names.
Throws aRuntimeErrorexception if the name is not found.
The type of value depends on the type of the pin or param. Integer scalar types may use integers or a textual representation of an integer to set the value. Floating point type may use both integer, floating point and textual representation thereof to set the value. Booleans acceptTrue,Falseand map the integer value zero (0) to false. The exact floating point value of zero (0.0) also maps for false. Booleans may also be of text "0", "1", "on", "off", "true", "false", "yes" or "no". Textual representations are case insensitive.Example:hal.set_p("mycomp.0.bit", True) hal.set_p("mycomp.0.float", 99.99) - hal.set_s(name:string, value:mixed)
-
Sets the signal (net) called name to value. The same rules for value apply to set_s() as to set_p().
The set_s() method has one special case when the signal is of typehal.Type.PORTand it is fully connected. In that case, the call uses the value to set the port’s queue size and it must be a positive integer. See below on configuring a port. - hal.get_p(name:string) → bool|int|float|None
-
Returns the value of the pin or param with name. If name refers to a pin and that pin is connected, then the connected signal’s value is returned. Boolean types return
TrueorFalse. Integer scalar types return an integer. Floating point types return a float. The valueNoneis returned if no pin or param is found by that name. - hal.get_s(name:string) → bool|int|float|None
-
Returns the value of the signal with name. Boolean types return
TrueorFalse. Integer scalar types return an integer. Floating point types return a float. The valueNoneis returned if no signal is found by that name. - hal.get_value(name:string) → bool|int|float
-
Returns the value of the pin, param or signal with name, searched in that order. Boolean types return
TrueorFalse. Integer scalar types return an integer. Floating point types return a float. ARuntimeErrorexception is thrown if no pin, param or signal is found by that name.Example:value = hal.get_value("iocontrol.0.emc-enable-in") - hal.get_info_pins() → list(dict)
-
DEPRECATED: replaced by
hal.query.pins().
Returns a list of dictionary tuples as in{"NAME":"pinname", "VALUE":<bool|int|float>, "TYPE":<int>, "DIRECTION":<int>}.Example:# Old style (deprecated): for i in hal.get_info_pins(): print(i['NAME'], i['TYPE'], i['DIRECTION'], i['VALUE']) # New style (see hal.query sub-module below): for pinname,pindetail in hal.query.pins().items(): print(pinname, pindetail['type'], pindetail['dir'], pindetail['value']) - hal.get_info_params() → list(dict)
-
DEPRECATED: replaced by
hal.query.params().
Returns a list of dictionary tuples as in{"NAME":"paramname", "VALUE":<bool|int|float>, "TYPE":<int>, "DIRECTION":<int>}. - hal.get_info_signals() → list(dict)
-
DEPRECATED: replaced by
hal.query.signals().
Returns a list of dictionary tuples as in{"NAME":"signame", "VALUE":<bool|int|float>, "TYPE":<int>, "DRIVER":"name"|None}.Example:# New style (see hal.query sub-module below) for signame,sigdetail in hal.query.signals().items(): print(signame, sigdetail['type'], sigdetail['value'], sigdetail['driver'])
3. Class hal.component
3.1. component methods
- comp = hal.component(name:string [, prefix:string])
-
The component itself is created by a call to the constructor
hal.component. The arguments are the HAL component name and (optionally) the prefix used for pin and param names. If the prefix is not specified, the component name is used.Example:comp = hal.component("passthrough") - pin = comp.newpin(name:string, type:enum, io:enum)
-
Create new pin with actual name
prefix.__name__. The pin type must be one of the types described above. The io specifies the direction of the pin. Throws aValueErrorexception if name already exists.Example:p_in = comp.newpin("in", hal.Type.FLOAT, hal.Dir.IN) - param = comp.newparam(name:string, type:enum, access:enum)
-
Create new parameter with actual name
prefix.__name__. The pin type argument must be one of the types described above. Parameters cannot be of typehal.Type.PORT. The access argument specifies the allowed param access. Throws aValueErrorexception if name already exists.Example:p_bloop = comp.newparam("bloop", hal.Type.FLOAT, hal.Dir.RO) - comp.getitem(name:string) → object
-
Return the pin or param item object name previously created with comp.newpin() or comp.newparam(). Throws an
AttributeErrorexception if no pin or param named name is found. Use comp.getpin() or comp.getparam() to find the specific type. - comp.getpin(pinname:string) → object
-
Return the pin item object pinname previously created with comp.newpin(). Throws an
AttributeErrorexception if no pin names pinname is found. A param called pinname will not be found and throws an exception. Use comp.getitem() to find either. - comp.getparam(paramname:string) → object
-
Return the param item object paramname previously created with copm.newparam(). Throws an
AttributeErrorexception if no pin names paramname is found. A pin called paramname will not be found and throws an exception. Use comp.getitem() to find either. - comp.getpins() → dict
-
Returns a dictionary all pin and param names and their values. The pin or param name is the dictionary key.
- comp.ready()
-
Tells the HAL system the component is initialized. Locks out adding pins.
- comp.unready()
-
Allows a component to add pins after ready() has been called. One should call ready() on the component when done.
- comp.getprefix() → string
-
Returns the current component’s prefix used when creating pins and params. It defaults to the component name when not set in the constructor or by setprefix.
- comp.setprefix(prefix:string)
-
Set the prefix used when creating pins and params. The prefix is used to create pins and params called
__prefix__.nameand defaults to the components name.
4. Class hal.stream
The streamer class enables sending typed value blocks, samples of data, between real-time and non-real-time. The values carried in a stream have the same types as pins and params. A stream can source data from pins or sink data into pins using ready made components. The stream carries values through a FIFO queue up to a specified depth. The stream allocates and uses a shared memory segment that is not part of HAL memory and thus does not burden HAL memory usage. Each data sample may contain up to twenty (20) values. The type of the values in a sample must be configured and specified in a type string. The type string consists of the following characters (case insensitive):
-
b- Boolean -
s- Signed 32-bit -
u- Unsigned 32-bit -
l- Signed 64-bit -
k- Unsigned 64-bit -
f- Floating point (real)
Two components are available, streamer and sampler. The streamer component writes a set of pins from non-real-time data pushed into a stream. The sampler component samples a set of pins in real-time and makes them available in a stream.
|
Note
|
If you use this Python Both the sampler and streamer components create the stream. You only need to attach to it from the Python |
|
Important
|
A stream can only have one (1) reader and one (1) writer. The stream queue is not designed to support operation with multiple readers or writers. |
import hal
import time
comp = hal.component("samplereader")
# Attach to the sampler stream
samplereader = hal.stream(comp, hal.sampler_base, "bffs")
# ...
comp.ready()
# ...
hal.set_value("sampler.0.enable", True) # Start streaming samples
while True:
while samplereader.readable:
print(samplereader.read())
time.sleep(0.001) # Don't busy-loop
loadrt sampler depth=100 cfg=bffs
# Disable sampler before adding function to the thread or it would start
# sampling immediately and could fill the queue before we start reading.
setp sampler.0.enable 0
addf sampler.0 servo-thread
net sample-jogger motion.jog-is-active sampler.0.pin.0
net sample-joint-0 joint.0.pos-fb sampler.0.pin.1
net sample-joint-1 joint.1.pos-fb sampler.0.pin.2
net sample-line motion.program-line sampler.0.pin.3
4.1. stream constants
- hal.sampler_base
-
The sampler component’s (sampler.c) shared memory ID for stream communication.
- hal.stream_base
-
The streamer component’s (streamer.c) shared memory ID for stream communication.
4.2. stream methods
- stream = hal.stream(comp:object, key:int, depth:int, typestr:string)
-
Create a stream for the component comp using the shared memory identifier key. The stream’s queue size is allocated for depth samples of typestr format. See the type string list for type meaning.
- stream = hal.stream(comp:object, key:int [, typestr:string])
-
Attach to a stream for the component comp where the shared memory location is identified by key. If the optional typestr is provided, then it will be checked against the existing stream’s configuration. See the type string list for type meaning.
- stream.read() → tuple|None
-
Returns a tuple of sample data from the queue.
Noneis returned if no samples were available. - stream.write(sample:tuple)
-
Writes the sample argument to the stream queue. The sample must contain the correct number of elements and match the types (or be convertible) of the stream’s configuration. An
IOErrorexception is thrown if the sample could not be written to the queue. - stream.readable() → bool
-
Returns
Trueif there are samples available for reading in the stream queue orFalseif not. - stream.writable() → bool
-
Returns
Trueif there is space available in the stream queue to hold more samples orFalseif not. - stream.depth() → int
-
Returns the number of currently available samples for read in the queue.
- stream.maxdepth() → int
-
Returns the queue size as set when the stream was created (and cannot be changed).
- stream.element_types() → int
-
Return a bytes object with the format type string that was used to create the stream. See type string list for individual elements and type meaning.
- stream.num_underruns() → int
-
Returns the number of times stream.read() was called when no samples were available from the queue.
- stream.num_overruns() → int
-
Returns the number of times stream.write() was called when no samples could be stored in the queue.
4.3. stream members
- stream.sampleno
-
The last successfully read sample ID number as counted by the stream functions.
5. Sub-module hal.query
The hal.query sub-module is for getting information about all of HAL’s internal structures. These include:
-
Pins
-
Parametre
-
Signals
-
Components
-
Funksjoner
-
Threads
Each category can be queried by name or ID to get information about the specific item or you can get them all. The methods in this sub-module return a dictionary with all available information.
5.1. hal.query methods
- hal.query.pin(name:string) → dict|None
-
Retrieve all information of the pin name. Returns
Noneif the name was not found. Otherwise, returns a dictionary with the pin information:result = hal.query.pin("my.pin.name") result = { "haltype" : "pin", "name" : "my.pin.name", "type" : hal.Type.<BOOL,REAL,SINT,UINT,PORT,S32,U32>, "dir" : hal.Dir.<IN,OUT,IO>, "value" : <bool, int or float>, "alias" : "alias.name"|None, "signal" : "signal.name"|None, "comp" : "component-name", "comp_id" : <int> } - hal.query.param(name:string) → dict|None
-
Retrieve all information of the parameter name. Returns
Noneif the name was not found. Otherwise, returns a dictionary with the parameter information:result = hal.query.param("my.param.name") result = { "haltype" : "parameter", "name" : "my.param.name", "type" : hal.Type.<BOOL,REAL,SINT,UINT,S32,U32>, "dir" : hal.Dir.<RO,WR>, "value" : <bool, int or float>, "alias" : "alias.name"|None, "comp" : "component-name", "comp_id" : <int> } - hal.query.signal(name:string) → dict|None
-
Retrieve all information of the signal name. Returns
Noneif the name was not found. Otherwise, returns a dictionary with the signal information:result = hal.query.signal("my.signal.name") result = { "haltype" : "signal", "name" : "my.signal.name", "type" : hal.Type.<BOOL,REAL,SINT,UINT,PORT,S32,U32>, "value" : <bool, int or float>, "writers" : <int>, "readers" : <int>, "bidirs" : <int>, "driver" : "driver.pin.name"|None }The
readers,writersandbidirsindicate how many pins are connected. There can only be onewritersand manyreaders. Or, there can be manybidirsand manyreaders. Thedriveris the connected pin name of typehal.Dir.OUTorNoneifwritersis zero. - hal.query.comp(name:string) → dict|None
- dict|None = hal.query.comp(id:int)
-
Retrieve all information of the component name or by integer component id. Returns
Noneif the name or id was not found. Otherwise, returns a dictionary with the component information:result = hal.query.comp("component-name") result = { "haltype" : "component", "name" : "component-name", "type" : hal.CompType.<REALTIME,USER,OTHER> "id" : <int> "pid" : <int>|0 "ready" : True|False, "insmod" : "comp command line options" }The
pidfield is set to zero (0) for realtime components and the process ID for user-space components. Theinsmodfield is the loadrt command line (excluding loadrt) and is only set for realtime components. Thetypefield is currently only eitherhal.CompType.REALTIMEorhal.CompType.USER. - hal.query.funct(name:string) → dict|None
-
Retrieve all information of the function name. Returns
Noneif the name was not found. Otherwise, returns a dictionary with the function information:result = hal.query.funct("my.funct.name") result = { "haltype" : "function", "name" : "my.funct.name", "comp" : "component-name", "comp_id" : <int>, "users" : <int>, "reentrant" : True|False }The
usersfield indicates how many threads use this function. Only functions that havereentrantset toTruecan have more than one user. - hal.query.thread(name:string) → dict|None
-
Retrieve all information of the thread name. Returns
Noneif the name was not found. Otherwise, returns a dictionary with the thread information:result = hal.query.thread("my.thread.name") result = { "haltype" : "thread", "name" : "my.thread.name", "comp" : "component-name", "comp_id" : <int>, "priority" : <int>, "period" : <int>, # in nanoseconds "functions" : ( { "haltype" : "threadfunction", "name" : "my.funct.name", "index" : <int>, "is_init" : True|False }, ... ) }The
functionsfield is a list of dictionaries. Each entry describes a function that runs in the thread. The order of execution within the thread is indicated by zero-basedindex. Any function in thefunctionslist should have the correspondingusersfield on ahal.query.funct(name)set.
Theis_initfield can only containTrueif the thread was setup but never run. Init functions are automatically removed once they have executed once and never show up again. - hal.query.signalpins(name:string) → dict
-
Retrieve information about all pins connected to signal name. Returns
Noneif the signal name is not found. The dictionary returned may be empty and has the format:result = hal.query.signalpins("my.signal") result = { "pin-r" : { "haltype": "pin", "name": "pin-r", "dir": hal.Dir.IN "signal": "my.signal", ...}, "pin-w" : { "haltype": "pin", "name": "pin-w", "dir": hal.Dir.OUT "signal": "my.signal", ...}, "pin-x" : { "haltype": "pin", ...}, ... }See
hal.query.pin()for description for pin dictionary details. - hal.query.pins() → dict
-
Retrieve information about all pins. The dictionary returned has the format:
{ "pin-a" : { "haltype": "pin", "name": "pin-a", "type":...}, "pin-b" : { "haltype": "pin", ...}, ... }See
hal.query.pin()for description of the pin dictionary details. - hal.query.params() → dict
-
Retrieve information about all params. The dictionary returned has the format:
{ "param-a" : { "haltype": "parameter", "name": "param-a", "type":...}, "param-b" : { "haltype": "parameter", ...}, ... }See
hal.query.param()for description of the param dictionary details. - hal.query.signals() → dict
-
Retrieve information about all signals. The dictionary returned has the format:
{ "sig-a" : { "haltype": "signal", "name": "sig-a", "type":...}, "sig-b" : { "haltype": "signal", ...}, ... }See
hal.query.signal()for description of the signal dictionary details. - hal.query.comps() → dict
-
Retrieve information about all components. The dictionary returned has the format:
{ "comp-a" : { "haltype": "component", "name": "comp-a", "type":...}, "comp-b" : { "haltype": "component", ...}, ... }See
hal.query.comp()for description of the component dictionary details. - hal.query.functs() → dict
-
Retrieve information about all functions. The dictionary returned has the format:
{ "funct-a" : { "haltype": "function", "name": "funct-a", ...}, "funct-b" : { "haltype": "function", ...}, ... }See
hal.query.funct()for description of function dictionary details. - hal.query.threads() → dict
-
Retrieve information about all threads. The dictionary returned has the format:
{ "thread-a" : { "haltype": "thread", "name": "thread-a", ...}, "thread-b" : { "haltype": "thread", ...}, ... }See
hal.query.thread()for description of thread dictionary details.
6. Class hal.shm
The shm class is an interface to create and manage shared memory segments.
The shm class should be considered experimental and may or may not work as intended.
|
Warning
|
Do not rely on this class. It may be removed in future releases. |
6.1. shm methods
- shm = hal.shm(comp:object, key:int, size:int)
-
Allocate a size sized shared memory segment with ID key.
- shm.getbuffer()
-
Returns a Python
memoryviewobject of the shared memory segment. - shm.setsize()
-
This is non-functional. Do not use. You cannot increase or decrease the shared memory segment’s size once it is created.
7. HAL Port pipes
A HAL port is a byte-oriented pipe that streams bytes from the writer to the reader. It may be used to transport data between real-time and non-real-time in either direction. The HAL port pipe facility is distinct from the HAL stream facility in that it has no concept of typed data. The reader and writer of a port must handle a binary byte oriented data-stream. A HAL port uses HAL pins of type hal.Type.PORT to communicate.
import hal
import time
import struct
comp = hal.component("portwriter")
# Create the write-end
portw = comp.newpin("portpin", hal.Type.PORT, hal.Dir.OUT)
# Create a signal to link reader and writer
portsig = hal.new_sig("portsig", hal.Type.PORT)
portw.ready()
# Connect the pins to the signal net
hal.connect("portwriter.portpin", "portsig")
hal.connect("portreader.portpin", "portsig")
# Allocate and set the port's queue size
hal.set_s("portsig", 256)
while True:
if portw.writable() < 2:
time.sleep(0.001) # Don't busy-loop
continue
cmd, arg = read_command()
binvals = struct.pack("bb", cmd, arg)
portw.write(binvals)
component portreader "Reads data from a HAL port";
pin in port portpin "Port's read end";
description "Port reader example component";
option singleton;
option period no;
license "GPL";
function _;
;;
void do_abort(void) { /* you write code here */ }
void kill_switch(char x) { (void)x; /* you write some more code here */ }
FUNCTION(_)
{
unsigned avail = hal_port_readable(portpin_ptr);
if(avail > 1) {
rtapi_u8 buf[2];
if(!hal_port_read(portpin_ptr, buf, sizeof(buf))) {
rtapi_print_msg(RTAPI_MSG_ERR, "Port read failed\n");
hal_port_clear(portpin_ptr); // Try to recover
} else {
switch(buf[0]) {
case 'a': do_abort(); break;
case 'k': kill_switch(buf[1]); break;
// ...
}
}
}
}
loadrt portreader
addf portreader servo.thread
A more comprehensive implementation can be found in the raster.comp component together with the raster programmer.
|
Important
|
A HAL port pipes can only have one (1) reader and one (1) writer. The port queue is not designed to support operation with multiple readers or writers. |
|
Note
|
A HAL port queue cannot be allocated larger than 64 kiB (65536 bytes). The minimum size is one (1) byte, but that is not recommended. You should analyze your usage and find the appropriate size to set. |
7.1. Port pin methods
- port = comp.newpin(name:string, hal.Type.PORT, io:enum)
-
A port is a special pin type. It is created as a normal pin with type
hal.Type.PORT. You need two pins for a port, one input and one output. Both ends of a port are connected with a signal (net). Setting the signal will allocate the port’s queue. - port.readable() → int
-
Returns the number of bytes available for reading from the port queue.
- port.writable() → int
-
Returns the number of bytes possible to write to the port queue.
- port.write(data:bytes) → bool
-
Write a buffer onto the port queue. The argument data can be either a bytes buffer or a string. If it is a string, then it will be converted to a UTF-8 bytes buffer. Returns
Trueif the data was successfully written to the port queue andFalseif not. The write() call fails if the port queue has not enough free capacity for the entire data argument.NoteYou should be careful when writing a string. Strings are Unicode encoded and may expand to multiple bytes when converted to UTF-8. Therefore, the length of the string may not match the length of the UTF-8 bytes buffer and not fit into the queue.
- port.read(size:int) → bytes
-
Reads size bytes from the port and removes the bytes from the port queue. The port is tested whether size bytes are available before attempting to read. Returns a bytes buffer upon success or
Falseon failure. - port.peek(size:int) → bytes
-
Reads size bytes from the port without removing the bytes from the port queue. The port is tested whether size bytes are available before attempting to peek. Returns a bytes buffer upon success or
Falseon failure. - port.peek_commit(size:int) → bool
-
Removes size bytes from the port queue. Returns
Trueon success orFalseif not.
def wait_for(n):
while port.readable() < n:
time.sleep(0.01)
wait_for(2)
# Peek at the first 2 bytes of the data pipe
data = port.peek(2)
# Check the data for a pattern
if data[0] == 123 and data[1] == 42:
port.peek_commit(2) # Discard data from the pipe
else:
# Not the discardable pattern, need 4 bytes then
wait_for(4)
data = port.read(4)
handle_data(data)
- port.clear()
-
Clears the content of the port queue.
- port.size() → int
-
Return the allocated port queue size. The return value is zero (0) if no queue was allocated for the port.