|
1 | | -Events |
2 | | -====== |
| 1 | +# Events |
3 | 2 |
|
4 | | -[API Reference](../../../api-reference/events/index.md) |
| 3 | +[API Reference](../../api-reference/events/index.md) |
5 | 4 |
|
6 | | -Events are pushed to the client through a publish-subscribe mechanism. Call ``push()`` method on the event to |
7 | | -publish data to clients: |
| 5 | +Events are pushed to the client through a publish-subscribe mechanism through all the protocols. Call `push()` method on the event to publish data to clients: |
8 | 6 |
|
9 | | -```py title="Definition" linenums="1" hl_lines="17-21" |
10 | | ---8<-- "docs/howto/code/events/definition.py:5:25" |
| 7 | +```py title="Definition" linenums="1" hl_lines="21-23" |
| 8 | +--8<-- "docs/beginners-guide/code/events/definition.py:3:25" |
11 | 9 | ``` |
12 | 10 |
|
| 11 | +## Subscription |
| 12 | + |
13 | 13 | One can subscribe to the event using the attribute name: |
14 | 14 |
|
15 | | -```py title="Subscription" linenums="1" hl_lines="9-10" |
16 | | ---8<-- "docs/howto/code/events/event_client.py:1:10" |
17 | | -``` |
18 | | - |
| 15 | +=== "threaded" |
| 16 | + |
| 17 | + In the default synchronous mode, the `subscribe_event` method spawns a thread that listens to the event in backgound: |
| 18 | + |
| 19 | + ```py title="Subscription" linenums="1" hl_lines="9" |
| 20 | + from hololinked.client import ClientFactory |
| 21 | + |
| 22 | + energy_meter = ClientFactory.http(url="http://localhost:8000/energy-meter") |
| 23 | + # energy_meter = ClientFactory.zmq(id="energy_meter", access_point="IPC") |
| 24 | + |
| 25 | + def event_cb(event_data): |
| 26 | + print(event_data) |
19 | 27 |
|
20 | | -One can also supply multiple callbacks which may called in series or threaded: |
| 28 | + energy_meter.subscribe_event(name="data_point_event", callbacks=event_cb) |
| 29 | + ``` |
| 30 | + |
| 31 | +=== "async" |
| 32 | + |
| 33 | + In the asynchronous mode, the `subscribe_event` method creates an event listening task in the running async loop: |
21 | 34 |
|
22 | | -=== "Sequential" |
| 35 | + ```py title="Subscription" linenums="1" hl_lines="9" |
| 36 | + from hololinked.client import ClientFactory |
23 | 37 |
|
24 | | - ```py title="Providing Callbacks" linenums="1" hl_lines="9" |
25 | | - --8<-- "docs/howto/code/events/event_client.py:13:22" |
| 38 | + energy_meter = ClientFactory.http(url="http://localhost:8000/energy-meter") |
| 39 | + # energy_meter = ClientFactory.zmq(id="energy_meter", access_point="IPC") |
| 40 | + |
| 41 | + def event_cb(event_data): |
| 42 | + print(event_data) |
| 43 | + |
| 44 | + energy_meter.subscribe_event( |
| 45 | + name="data_point_event", |
| 46 | + callbacks=event_cb, |
| 47 | + asynch=True |
| 48 | + ) |
26 | 49 | ``` |
27 | 50 |
|
28 | | -=== "Threaded" |
| 51 | +--- |
| 52 | + |
| 53 | +One can also supply multiple callbacks which may called in series, threaded or async: |
| 54 | + |
| 55 | +=== "sequential" |
| 56 | + |
| 57 | + The background thread that listens to the event executes the callbacks in series in its own thread: |
| 58 | + |
| 59 | + ```py title="Sequential Callbacks" linenums="1" hl_lines="9" |
| 60 | + def event_cb1(event_data): |
| 61 | + print("First Callback", event_data) |
| 62 | + |
| 63 | + def event_cb2(event_data): |
| 64 | + print("Second callback", event_data) |
| 65 | + |
| 66 | + energy_meter.subscribe_event( |
| 67 | + name="statistics_event", |
| 68 | + callbacks=[event_cb1, event_cb2] |
| 69 | + ) |
| 70 | + ``` |
| 71 | + |
| 72 | + So please be careful while using GUI frameworks like PyQt where you can paint the GUI only from the main thread. |
| 73 | + You would need to use signals and slots or other mechanisms. |
| 74 | + |
| 75 | +=== "threaded" |
| 76 | + |
| 77 | + The background thread that listens to the event executes the callbacks by spawning new threads: |
| 78 | + |
| 79 | + ```py title="Thread Callbacks" linenums="1" hl_lines="9-10" |
| 80 | + def event_cb1(event_data): |
| 81 | + print("First Callback", event_data) |
| 82 | + |
| 83 | + def event_cb2(event_data): |
| 84 | + print("Second callback", event_data) |
29 | 85 |
|
30 | | - ```py title="Providing Callbacks" linenums="1" hl_lines="9-10" |
31 | | - --8<-- "docs/howto/code/events/event_client.py:13:18" |
32 | | - --8<-- "docs/howto/code/events/event_client.py:24:" |
| 86 | + energy_meter.subscribe_event( |
| 87 | + name="statistics_event", |
| 88 | + callbacks=[event_cb1, event_cb2], |
| 89 | + thread_callbacks=True |
| 90 | + ) |
33 | 91 | ``` |
| 92 | + Again, please be careful while using GUI frameworks like PyQt where you can paint the GUI only from the main thread. |
| 93 | + |
| 94 | +=== "async" |
| 95 | + |
| 96 | + Applies only when listening to event with `async=True`, the `async` method creates new tasks in the current loop: |
| 97 | + |
| 98 | + ```py title="Thread Callbacks" linenums="1" |
| 99 | + async def event_cb1(event_data): |
| 100 | + print("First Callback", event_data) |
| 101 | + await some_async_function1(event_data) |
| 102 | + |
| 103 | + async def event_cb2(event_data): |
| 104 | + print("Second callback", event_data) |
| 105 | + await some_async_function2(event_data) |
| 106 | + |
| 107 | + energy_meter.subscribe_event( |
| 108 | + name="statistics_event", |
| 109 | + callbacks=[event_cb1, event_cb2], |
| 110 | + asynch=True, |
| 111 | + create_task_for_cbs=True |
| 112 | + ) |
| 113 | + ``` |
| 114 | + |
| 115 | +--- |
| 116 | + |
| 117 | +To unsubscribe: |
| 118 | + |
| 119 | +```py title="Unsubscription" linenums="1" |
| 120 | +energy_meter.unsubscribe_event(name="data_point_event") |
| 121 | +``` |
| 122 | + |
| 123 | +## Payload Schema |
34 | 124 |
|
35 | 125 | Schema may be supplied for the validation of the event data on the client: |
36 | 126 |
|
37 | 127 | ```py title="" linenums="1" hl_lines="13" |
38 | | ---8<-- "docs/howto/code/events/definition.py:6:6" |
39 | | ---8<-- "docs/howto/code/events/definition.py:50:62" |
40 | | -``` |
41 | | - |
42 | | -There is no separate validation on the server side. |
43 | | - |
44 | | - |
45 | | -<!-- .. list-table:: Thing Description for Events |
46 | | - :header-rows: 1 |
47 | | -
|
48 | | - * - Key |
49 | | - - Supported |
50 | | - - Comment |
51 | | - * - subscription |
52 | | - - ✖ |
53 | | - - |
54 | | - * - data |
55 | | - - ✔ |
56 | | - - payload schema for the event |
57 | | - * - dataResponse |
58 | | - - ✖ |
59 | | - - schema for response message after arrival of an event, will be supported in future |
60 | | - * - cancellation |
61 | | - - ✖ |
62 | | - - Server sent events can be cancelled by the client directly --> |
| 128 | +class GentecMaestroEnergyMeter(Thing): |
| 129 | + |
| 130 | + data_point_event_schema = { |
| 131 | + "type": "object", |
| 132 | + "properties": { |
| 133 | + "timestamp": {"type": "string", "format": "date-time"}, |
| 134 | + "energy": {"type": "number"} |
| 135 | + }, |
| 136 | + "required": ["timestamp", "energy"], |
| 137 | + } |
| 138 | + |
| 139 | + data_point_event = Event( |
| 140 | + doc="Event raised when a new data point is available", |
| 141 | + label="Data Point Event", |
| 142 | + schema=data_point_event_schema, |
| 143 | + ) |
| 144 | +``` |
| 145 | + |
| 146 | +There is no separate validation on the server side. |
| 147 | + |
| 148 | +???+ "Schema as seen in Thing Description" |
| 149 | + |
| 150 | + ```py |
| 151 | + GentecMaestro.data_point_event.to_affordance().json() |
| 152 | + ``` |
| 153 | + |
| 154 | + ```json |
| 155 | + { |
| 156 | + "description": "Event raised when a new data point is available", |
| 157 | + "data": { |
| 158 | + "type": "object", |
| 159 | + "properties": { |
| 160 | + "timestamp": { |
| 161 | + "type": "string", |
| 162 | + "format": "date-time" |
| 163 | + }, |
| 164 | + "energy": { |
| 165 | + "type": "number" |
| 166 | + } |
| 167 | + }, |
| 168 | + "required": ["timestamp", "energy"] |
| 169 | + } |
| 170 | + } |
| 171 | + ``` |
| 172 | + |
| 173 | +## Thing Description Metadata |
| 174 | + |
| 175 | +| Key | Supported | Comment | |
| 176 | +| ------------ | --------- | ---------------------------------------------------------------------------------- | |
| 177 | +| subscription | ✖ | | |
| 178 | +| data | ✔ | payload schema for the event | |
| 179 | +| dataResponse | ✖ | schema for response message after arrival of an event, will be supported in future | |
| 180 | +| cancellation | - | Server sent events can be cancelled by the client directly | |
0 commit comments