@@ -14,54 +14,139 @@ An example Lab Thing built from our ``PretendSpectrometer`` class, complete with
1414
1515.. code-block :: python
1616
17- from labthings import fields, create_app
17+ import time
18+
19+ from labthings import ActionView, PropertyView, create_app, fields, find_component, op
1820 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)
19124
20125
21126 # Create LabThings Flask app
22127 app, labthing = create_app(
23128 __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" ,
27132 )
28133
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
31136 my_spectrometer = PretendSpectrometer()
137+ labthing.add_component(my_spectrometer, " org.labthings.example.mycomponent" )
32138
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- )
41139
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" )
60144
61145
62146 # Start the app
63147 if __name__ == " __main__" :
64148 from labthings import Server
149+
65150 Server(app).run()
66151
67152
0 commit comments