Skip to content

Commit a433b21

Browse files
committed
Added basic docs
1 parent 4bf8412 commit a433b21

File tree

3 files changed

+155
-6
lines changed

3 files changed

+155
-6
lines changed

docs/basic_usage/index.rst

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
Basic usage
2+
===========
3+
4+
The easiest way to get started with Python-LabThings is via the :mod:`labthings.quick` module, and the :class:`labthings.LabThing` builder methods.
5+
6+
We will assume that for basic usage you already have some basic instrument control code. In our example, this is in the form of a ``PretendSpectrometer`` class, which will generate some data like your instrument control code might. Our ``PretendSpectrometer`` class has a ``data`` property which quickly returns a spectrum, an ``x_range`` property which determines the range of data we'll return, a ``magic_denoise`` property for cleaning up our signal, and a slow ``average_data(n)`` method to average ``n`` individual data measurements.
7+
8+
Building an API from this class requires a few extra considerations. In order to tell our API what data to expect from users, we need to construct a schema for each of our interactions. This schema simply maps variable names to JSON-compatible types, and is made simple via the :mod:`labthings.fields` module.
9+
10+
For properties, the input and output MUST be formatted the same, and so a single ``schema`` argument handles both. For actions, the input parameters and output response may be different. In this case, we can pass a ``schema`` argument to format the output, and an ``args`` argument to specify the input parameters,
11+
12+
An example Lab Thing built from our ``PretendSpectrometer`` class, complete with schemas, might look like:
13+
14+
15+
.. code-block:: python
16+
17+
from labthings.server.quick import create_app
18+
from labthings.server import fields
19+
20+
from my_components import PretendSpectrometer
21+
22+
23+
# Create LabThings Flask app
24+
app, labthing = create_app(
25+
__name__,
26+
title="My PretendSpectrometer API",
27+
description="LabThing API for PretendSpectrometer",
28+
version="0.1.0"
29+
)
30+
31+
32+
# Make some properties and actions out of our component
33+
34+
# Single-shot data property
35+
labthing.build_property(
36+
my_component, # Python object
37+
"data", # Objects attribute name
38+
"/data", # URL to bind the property to
39+
description="A single-shot measurement",
40+
readonly=True,
41+
schema=fields.List(fields.Number())
42+
)
43+
44+
# Magic denoise property
45+
labthing.build_property(
46+
my_component, # Python object
47+
"magic_denoise", # Objects attribute name
48+
"/denoise", # URL to bind the property to
49+
description="A magic denoise property",
50+
schema=fields.Int(min=100, max=500, example=200)
51+
)
52+
53+
# Averaged measurement action
54+
labthing.build_action(
55+
my_component.average_data, # Python function
56+
"/average", # URL to bind the action to
57+
description="Take an averaged measurement",
58+
args={ # How do we convert from the request input to function arguments?
59+
"n": fields.Int(description="Number of averages to take", example=5, default=5)
60+
},
61+
)
62+
63+
64+
# Start the app
65+
if __name__ == "__main__":
66+
from labthings.server.wsgi import Server
67+
Server(app).run()
68+
69+
70+
71+
For completeness of the examples, our ``PretendSpectrometer`` class code is:
72+
73+
.. code-block:: python
74+
75+
import random
76+
import math
77+
import time
78+
79+
class PretendSpectrometer:
80+
def __init__(self):
81+
self.x_range = range(-100, 100)
82+
self.magic_denoise = 200
83+
84+
def make_spectrum(self, x, mu=0.0, sigma=25.0):
85+
"""
86+
Generate a noisy gaussian function (to act as some pretend data)
87+
88+
Our noise is inversely proportional to self.magic_denoise
89+
"""
90+
x = float(x - mu) / sigma
91+
return (
92+
math.exp(-x * x / 2.0) / math.sqrt(2.0 * math.pi) / sigma
93+
+ (1 / self.magic_denoise) * random.random()
94+
)
95+
96+
@property
97+
def data(self):
98+
"""Return a 1D data trace."""
99+
return [self.make_spectrum(x) for x in self.x_range]
100+
101+
def average_data(self, n: int):
102+
"""Average n-sets of data. Emulates a measurement that may take a while."""
103+
summed_data = self.data
104+
105+
for _ in range(n):
106+
summed_data = [summed_data[i] + el for i, el in enumerate(self.data)]
107+
time.sleep(0.25)
108+
109+
summed_data = [i / n for i in summed_data]
110+
111+
return summed_data

docs/core_concepts.rst

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
Core Concepts
2+
=============
3+
4+
LabThings is rooted in the `W3C Web of Things standards <https://www.w3.org/WoT/>`_. Using IP networking in labs is not itself new, though perhaps under-used. However lack of proper standardisation has stiffled widespread adoption. LabThings, rather than try to introduce new competing standards, uses the architecture and terminology introduced by the W3C Web of Things. A full description of the core architecture can be found in the `Web of Things (WoT) Architecture <https://www.w3.org/TR/wot-architecture/#sec-wot-architecture>`_ document. However, a brief outline of core terminology is given below.
5+
6+
Web Thing
7+
---------
8+
9+
A Web Thing is defined as "an abstraction of a physical or a virtual entity whose metadata and interfaces are described by a WoT Thing description." For LabThings this corresponds to an individual lab instrument that exposes functionality available to the user via a web API, and that functionality is described by a Thing Description.
10+
11+
LabThings automatically generates a Thing Description as you add functionality. The functionality you add falls into one of three categories of "interaction affordance": Properties, Actions, and Events.
12+
13+
Properties
14+
----------
15+
16+
A Property is defined as "an Interaction Affordance that exposes the state of the Thing. The state exposed by a Property MUST be retrievable (readable). Optionally, the state exposed by a Property MAY be updated (writeable)."
17+
18+
As a rule of thumb, any attribute of your device that can be quickly read, or optionally written, should be a Property. For example, simple device settings, or one-shot measurements such as temperature.
19+
20+
Actions
21+
-------
22+
23+
An Action is defined as "an Interaction Affordance that allows to invoke a function of the Thing. An Action MAY manipulate state that is not directly exposed (cf. Properties), manipulate multiple Properties at a time, or manipulate Properties based on internal logic (e.g., toggle). Invoking an Action MAY also trigger a process on the Thing that manipulates state (including physical state through actuators) over time."
24+
25+
The key point here is that Actions are typically more complex in functionality than simply setting or getting a property. For example, they can set multiple properties simultaneously (for example, auto-exposing a camera), or they can manipulate the state of the Thing over time, for example starting a long-running data acquisition.
26+
27+
Python-LabThings automatically handles offloading Actions into background threads where appropriate. The Action has a short timeout to complete quickly and respond to the API request, after which time a ``201 - Started`` response is sent to the client with information on how to check the state of the Action at a later time. This is particularly useful for long-running data acquisitions, as it allows users to start an acquisition and get immediate confirmation that it has started, after which point they can poll for changes in status.
28+
29+
Events
30+
------
31+
32+
An event "describes an event source that pushes data asynchronously from the Thing to the Consumer. Here not state, but state transitions (i.e., events) are communicated. Events MAY be triggered through conditions that are not exposed as Properties."
33+
34+
Common examples are notifying clients when a Property is changed, or when an Action starts or finishes. However, Thing developers can introduce new Events such as warnings, status messages, and logs. For example, a device may emit an events when the internal temperature gets too high, or when an interlock is tripped. This Event can then be pushed to both users AND other Things, allowing automtic response to external conditions.
35+
36+
A good example of this might be having Things automatically pause data-acquisition Actions upon detection of an overheat or interlock Event from another Thing.

docs/index.rst

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
Welcome to Python-LabThings' documentation!
2-
=======================================================
2+
===========================================
33

44
.. toctree::
55
:maxdepth: 2
66
:caption: Contents:
77

8-
Indices and tables
9-
==================
10-
* :ref:`genindex`
11-
* :ref:`modindex`
12-
* :ref:`search`
8+
basic_usage/index.rst
9+
10+
11+
Installation
12+
------------
13+
14+
``pip install labthings``

0 commit comments

Comments
 (0)