@@ -14,54 +14,139 @@ An example Lab Thing built from our ``PretendSpectrometer`` class, complete with
14
14
15
15
.. code-block :: python
16
16
17
- from labthings import fields, create_app
17
+ import time
18
+
19
+ from labthings import ActionView, PropertyView, create_app, fields, find_component, op
18
20
from labthings.example_components import PretendSpectrometer
21
+ from labthings.json import encode_json
22
+
23
+ """
24
+ Class for our lab component functionality. This could include serial communication,
25
+ equipment API calls, network requests, or a "virtual" device as seen here.
26
+ """
27
+
28
+
29
+ """
30
+ Create a view to view and change our integration_time value,
31
+ and register is as a Thing property
32
+ """
33
+
34
+
35
+ # Wrap in a semantic annotation to autmatically set schema and args
36
+ class DenoiseProperty (PropertyView ):
37
+ """ Value of integration_time"""
38
+
39
+ schema = fields.Int(required = True , minimum = 100 , maximum = 500 )
40
+ semtype = " LevelProperty"
41
+
42
+ @op.readproperty
43
+ def get (self ):
44
+ # When a GET request is made, we'll find our attached component
45
+ my_component = find_component(" org.labthings.example.mycomponent" )
46
+ return my_component.integration_time
47
+
48
+ @op.writeproperty
49
+ def put (self , new_property_value ):
50
+ # Find our attached component
51
+ my_component = find_component(" org.labthings.example.mycomponent" )
52
+
53
+ # Apply the new value
54
+ my_component.integration_time = new_property_value
55
+
56
+ return my_component.integration_time
57
+
58
+ @op.observeproperty
59
+ def websocket (self , ws ):
60
+ # Find our attached component
61
+ my_component = find_component(" org.labthings.example.mycomponent" )
62
+ initial_value = None
63
+ while not ws.closed:
64
+ time.sleep(1 )
65
+ if my_component.integration_time != initial_value:
66
+ ws.send(encode_json(my_component.integration_time))
67
+ initial_value = my_component.integration_time
68
+
69
+
70
+ """
71
+ Create a view to quickly get some noisy data, and register is as a Thing property
72
+ """
73
+
74
+
75
+ class QuickDataProperty (PropertyView ):
76
+ """ Show the current data value"""
77
+
78
+ # Marshal the response as a list of floats
79
+ schema = fields.List(fields.Float())
80
+
81
+ @op.readproperty
82
+ def get (self ):
83
+ # Find our attached component
84
+ my_component = find_component(" org.labthings.example.mycomponent" )
85
+ return my_component.data
86
+
87
+ @op.observeproperty
88
+ def websocket (self , ws ):
89
+ # Find our attached component
90
+ my_component = find_component(" org.labthings.example.mycomponent" )
91
+ while not ws.closed:
92
+ ws.send(encode_json(my_component.data))
93
+
94
+
95
+ """
96
+ Create a view to start an averaged measurement, and register is as a Thing action
97
+ """
98
+
99
+
100
+ class MeasurementAction (ActionView ):
101
+ # Expect JSON parameters in the request body.
102
+ # Pass to post function as dictionary argument.
103
+ args = {
104
+ " averages" : fields.Integer(
105
+ missing = 20 , example = 20 , description = " Number of data sets to average over" ,
106
+ )
107
+ }
108
+ # Marshal the response as a list of numbers
109
+ schema = fields.List(fields.Number)
110
+
111
+ # Main function to handle POST requests
112
+ @op.invokeaction
113
+ def post (self , args ):
114
+ """ Start an averaged measurement"""
115
+
116
+ # Find our attached component
117
+ my_component = find_component(" org.labthings.example.mycomponent" )
118
+
119
+ # Get arguments and start a background task
120
+ n_averages = args.get(" averages" )
121
+
122
+ # Return the task information
123
+ return my_component.average_data(n_averages)
19
124
20
125
21
126
# Create LabThings Flask app
22
127
app, labthing = create_app(
23
128
__name__ ,
24
- title = " My PretendSpectrometer API" ,
25
- description = " LabThing API for PretendSpectrometer " ,
26
- version = " 0.1.0"
129
+ title = " My Lab Device API" ,
130
+ description = " Test LabThing-based API" ,
131
+ version = " 0.1.0" ,
27
132
)
28
133
29
-
30
- # Make some properties and actions out of our component
134
+ # Attach an instance of our component
135
+ # Usually a Python object controlling some piece of hardware
31
136
my_spectrometer = PretendSpectrometer()
137
+ labthing.add_component(my_spectrometer, " org.labthings.example.mycomponent" )
32
138
33
- # Single-shot data property
34
- labthing.build_property(
35
- my_spectrometer, # Python object
36
- " data" , # Objects attribute name
37
- description = " A single-shot measurement" ,
38
- readonly = True ,
39
- schema = fields.List(fields.Number())
40
- )
41
139
42
- # Integration time property
43
- labthing.build_property(
44
- my_spectrometer, # Python object
45
- " integration_time" , # Objects attribute name
46
- description = " Single-shot integration time" ,
47
- schema = fields.Int(min = 100 , max = 500 , example = 200 , unit = " microsecond" )
48
- )
49
-
50
- # Averaged measurement action
51
- labthing.build_action(
52
- my_spectrometer, # Python object
53
- " average_data" , # Objects method name
54
- description = " Take an averaged measurement" ,
55
- schema = fields.List(fields.Number()),
56
- args = { # How do we convert from the request input to function arguments?
57
- " n" : fields.Int(description = " Number of averages to take" , example = 5 , default = 5 )
58
- },
59
- )
140
+ # Add routes for the API views we created
141
+ labthing.add_view(DenoiseProperty, " /integration_time" )
142
+ labthing.add_view(QuickDataProperty, " /quick-data" )
143
+ labthing.add_view(MeasurementAction, " /actions/measure" )
60
144
61
145
62
146
# Start the app
63
147
if __name__ == " __main__" :
64
148
from labthings import Server
149
+
65
150
Server(app).run()
66
151
67
152
0 commit comments