-
Notifications
You must be signed in to change notification settings - Fork 1
Python
Skinware is written in C, but it provides a Python interface as well. The Python interface is created with the ctypes
package to translate between the C functions and types and the Python's.
The following rules hold in translating between the C names and their Python equivalents.
- A URT type such as
urt_time
translates tourt.time
, - A Skinware type such as
skin_sensor_response
translates toskin.sensor_response
, - A struct such as
skin_reader_callbacks
translates toskin.reader_callbacks
, - A function such as
skin_service_attach
translates toskin.service_attach
,
Callbacks are automatically converted from Python functions to the appropriate CFUNCTYPE
. The functions that take the
callback then return the CFUNCTYPE
object they create for the user to hold on to, unless there is no need for the
callback after the function has returned. For example, a function that iterates over the sensors doesn't return the
callback object, but one that creates a thread that continues to call the callback after the function has returned,
does. It is very important to keep a reference to this returned object to make sure Python doesn't garbage-collect the
callback object.
Currently, URT doesn't implement the URT way in Python, so signal and argument handling is left to the user.
Taking the equivalent of startup()
, body()
and finish()
functions used in the URT way, the Python program would
generally look like the following:
def startup(data):
ret = urt.init()
if ret:
print('failed to init URT:', ret)
return ret
data.skn, ret = skin.init()
if ret:
print('failed to initialize skin:', ret)
urt.exit()
return ret
# other initialization
return 0
def body(data):
# spawn threads etc
def finish(data):
# cleanup
skin.free(data.skn)
urt.exit()
data = Data()
ret = startup(data)
if ret:
sys.exit(ret)
try:
body(data)
finally:
finish(data)
With Python, since the functions can have closures, the user_data
field of the callbacks are often unnecessary. That
said, here is an examples of how a driver can be created in Python, which assumes the above code exists (body()
replaced of course).
import sys
import time
from random import randrange
import skin
from ctypes import *
urt = skin.urt
class Data:
def __init__(self, skn = None):
self.skn = skn
class Driver:
def __init__(self, step, speed):
self.step = step
self.speed = speed
# create a random skin
self.patches = randrange(5, 11)
self.modules = self.patches * randrange(10, 16)
self.sensors = self.modules * randrange(8, 13)
def details(self, driver, revived, details, unused):
# since the skin is random, it is most likely not the same on revive, so just fail
if revived:
return -1
for p in range(self.patches):
details[0].patches[p].module_count = self.modules / self.patches
for m in range(self.modules):
details[0].modules[m].sensor_count = self.sensors / self.modules
for s in range(self.sensors):
details[0].sensors[s].uid = s
details[0].sensors[s].type = s % 3 # give whatever type
return 0
def acquire(self, driver, responses, size, unused):
resps = cast(responses, POINTER(skin.sensor_response))
skin.driver_copy_last_buffer(driver) # really unnecessary, because only resps[0] needs to be carried from
# last buffer, but do this just for demonstration
for i in range(size):
resps[i] = resps[i] + self.speed if i == 0 else resps[i - 1] + self.step
return 0
def body(data):
driver = Driver(50, 2000)
d, error, driver_callbacks_ref = skin.driver_add(data.skn,
skin.driver_attr(driver.patches, driver.modules, driver.sensors),
skin.writer_attr(buffer_count = 5, name = 'PYD'),
urt.task_attr(soft = True, period = 100000000),
skin.driver_callbacks(details = driver.details, acquire = driver.acquire))
if error:
print('failed to create driver:', error)
return
skin.resume(data.skn)
print('driver up and running. Press CTRL+C to stop...')
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print('interrupted. Stopping...')
The above driver creates a random skin and periodically increases the responses. You can see how the driver works by using the visualizer bundled with Skinware:
$ python driver.py
And in another shell:
$ skin_calibrate fake_fill &
$ skin_view show_nontaxel
The show_nontaxel
parameter to skin_view
tells it to also show non-taxel sensors, which is necessary because the
sensor types given by the driver have been meaningless values. When done, terminate the calibrator with kill %1
(or
fg
followed by CTRL+C) and back in the original shell, CTRL+C to close the driver.
In the following, a user program that periodically prints sensor values is present, again assuming the initialization and cleanup code from above are present.
import sys
import time
from random import randrange
import skin
from ctypes import *
urt = skin.urt
class Data:
def __init__(self, skn = None):
self.skn = skn
def body(data):
error = skin.load(data.skn, urt.task_attr(soft = True, period = 1000000000))
if error:
print('failed to load skin:', error)
skin.resume(data.skn)
print('printing values. Press CTRL+C to stop...')
try:
while True:
time.sleep(1)
responses = []
def add_responses(sensor, unused):
responses.append(skin.sensor_get_response(sensor))
return skin.CALLBACK_CONTINUE
# aggregate all responses in one
skin.for_each_sensor(data.skn, add_responses)
# print them (even though they are a lot)
print(responses)
except KeyboardInterrupt:
print('interrupted. Stopping...')
The above example shows how the sensor responses are iterated and that the user_data
parameter is unused because
Python already provides a closure for the function.
Next: See more tutorials.