gdp / doc / developer / tutorial / gdp-tutorial-part2.md @ master
History | View | Annotate | Download (10.5 KB)
1 | 8949c8d7 | Nitesh Mor | % Writing GDP applications |
---|---|---|---|
2 | |||
3 | # A typical application |
||
4 | |||
5 | The log interface acts as a proxy to interact with sensors and actuators. We |
||
6 | describe how one would go about designing a simple application. Imagine an |
||
7 | office building with some temperature sensors and some motor controlled window |
||
8 | blinds. We would like to design an application that looks at the current |
||
9 | temperature and some weather prediction feeds from the cloud to make a smart |
||
10 | decision about the window blinds. |
||
11 | |||
12 |  |
||
13 | |||
14 | As shown above, in order to do so, the application subscribes to all the |
||
15 | relevant logs and writes the actuation values to the actuator log. A gateway |
||
16 | working on behalf of the actuator subscribes to this actuation log and controls |
||
17 | the motors for the window blinds. Note that instead of two separate logs, a |
||
18 | composite log can be created for `Sensor 1` and `Sensor 2` in the diagram |
||
19 | above, provided that the gateway implements the single-writer semantics. |
||
20 | |||
21 | --- |
||
22 | |||
23 | # Software Installation and Configuration |
||
24 | |||
25 | In this section, we talk about how to get access, install and use the GDP |
||
26 | client side library. As mentioned earlier, we have a C library for clients with |
||
27 | wrappers in other languages around this C-library. |
||
28 | |||
29 | eebee62a | Nitesh Mor | The main GDP repository can be accessed in read-only mode by using |
30 | 8949c8d7 | Nitesh Mor | |
31 | eebee62a | Nitesh Mor | ``` |
32 | git clone https://repo.eecs.berkeley.edu/git-anon/projects/swarmlab/gdp.git |
||
33 | ``` |
||
34 | |||
35 | Or, in read-write mode either using HTTPS (requires username, password), or |
||
36 | using SSH (requires key setup): |
||
37 | 8949c8d7 | Nitesh Mor | |
38 | ``` |
||
39 | git clone https://repo.eecs.berkeley.edu/git/projects/swarmlab/gdp.git |
||
40 | git clone repoman@repo.eecs.berkeley.edu:projects/swarmlab/gdp.git |
||
41 | ``` |
||
42 | |||
43 | eebee62a | Nitesh Mor | The main GDP repository contains the core GDP library (`libgdp`), client-side |
44 | applications and language bindings, and the log-server (`gdplogd`). GDP-router |
||
45 | is maintained in a separate repository (`gdp_router_click.git`). However, you |
||
46 | should not need to worry about it if you are just playing around with GDP. |
||
47 | 2d3b765a | Eric Allman | |
48 | eebee62a | Nitesh Mor | The repository `gdp-if.git` contains various interfaces to the GDP. It is |
49 | 2d3b765a | Eric Allman | not really part of the GDP itself, but it may prove instructive. |
50 | 8949c8d7 | Nitesh Mor | |
51 | |||
52 | ## Compilation |
||
53 | eebee62a | Nitesh Mor | |
54 | In summary, assuming you have the required dependencies installed, `make |
||
55 | ee62a420 | Eric Allman | install-dev-c` should install the C include files, C libraries, and basic |
56 | 390904c6 | Eric Allman | utility applications into system path. `make install-python` should |
57 | install the Python bindings in the system path as well. These `make` |
||
58 | targets do not create any necessary configuration files, however (see |
||
59 | below). For more details, refer to README.md in the main git tree. |
||
60 | 8949c8d7 | Nitesh Mor | |
61 | ## Infrastructure information |
||
62 | 390904c6 | Eric Allman | |
63 | eebee62a | Nitesh Mor | Refer to README.md in the main git tree. |
64 | 8949c8d7 | Nitesh Mor | |
65 | *Note that the software/infrastructure is still in very early experimental |
||
66 | phase. We do not make any guarantees on data retention/backups at this moment. |
||
67 | As the code stabilizes, we will make better effort to maintain data. In the |
||
68 | meantime, contact us if you would like to use GDP for anything serious.* |
||
69 | |||
70 | ## Configuration |
||
71 | |||
72 | eebee62a | Nitesh Mor | The GDP library, log-server, and various other utility programs consult a |
73 | configuration file for the correct parameters to use. At the very minimum, your |
||
74 | configuration file should contain the GDP router that your client should connect |
||
75 | to (unless someone else is running a local router in the same subnet as you are |
||
76 | in, in which case zeroconf should work). Refer to README.md in the main git |
||
77 | tree. |
||
78 | 8949c8d7 | Nitesh Mor | |
79 | ## Creating logs |
||
80 | |||
81 | 1a02be05 | Nitesh Mor | The main mechanism to create a log is using `gdp-create` (should be in your |
82 | eebee62a | Nitesh Mor | system path after `make install-*`). For example, |
83 | 8949c8d7 | Nitesh Mor | |
84 | ``` |
||
85 | 1a02be05 | Nitesh Mor | gdp-create -k none org.example.project.log17a |
86 | 8949c8d7 | Nitesh Mor | ``` |
87 | |||
88 | f0f1f0cf | Nitesh Mor | will create a log named `org.example.project.log17a` on one of the default |
89 | eebee62a | Nitesh Mor | log-servers at Berkeley. |
90 | |||
91 | Although you can create logs with any name, please stick to this convention |
||
92 | (with "project" being the project name or the user name, as appropriate) so we |
||
93 | 1a02be05 | Nitesh Mor | can avoid name collisions. `-k none` means that `gdp-create` will not attempt to |
94 | eebee62a | Nitesh Mor | create a new signature key for signing appended data. Although crucial to the |
95 | operation, key-management is better deferred to a stage when you are familiar |
||
96 | 1a02be05 | Nitesh Mor | with the basic operations of the GDP. Also, note that `gdp-create` has several |
97 | eebee62a | Nitesh Mor | other command-line options that will be useful later on. |
98 | |||
99 | Note that if you don't explicitly specify log-placement, a log-server at |
||
100 | Berkeley is picked at random for hosting your log. This is especially important |
||
101 | if you are running your own log-servers and want control over where data goes. |
||
102 | 8949c8d7 | Nitesh Mor | |
103 | --- |
||
104 | |||
105 | # Writing applications in Python |
||
106 | |||
107 | Even though the GDP library is written in C, we provide a python package `gdp` |
||
108 | that acts as a wrapper around the C-library. This python package enables quick |
||
109 | prototyping using an object-oriented interface to GDP. What follows is a quick |
||
110 | how-to on writing simple GDP programs in Python. Note that this document is |
||
111 | just a starting point and is not intended to be a comprehensive guide to the |
||
112 | complete interface. For a more thorough API documentation, refer to |
||
113 | `/lang/python/README`. |
||
114 | |||
115 | b0f1da2a | Nitesh Mor | ## Python package installation |
116 | |||
117 | The package `gdp` should be installed in your system path for python packages. |
||
118 | Once you have the required dependencies for compilation installed, something |
||
119 | like `make install-python` from the root of repository should do the trick (note |
||
120 | eebee62a | Nitesh Mor | that running with `sudo` may be required). Note that this also installs the |
121 | 1a02be05 | Nitesh Mor | client side C libraries and various utilities (such as `gdp-create`) in system |
122 | eebee62a | Nitesh Mor | path. |
123 | b0f1da2a | Nitesh Mor | |
124 | 8949c8d7 | Nitesh Mor | ## Appending data |
125 | |||
126 | Let's start with a simple `Hello world` program, that writes some data to a |
||
127 | 214b1337 | Nitesh Mor | log and reads it back. Before we begin, we need to create the log; see *Creating |
128 | logs* above. The tutorial uses the logname `edu.berkeley.eecs.mor.01`, but |
||
129 | please replace it with the name of the log you create. |
||
130 | 8949c8d7 | Nitesh Mor | |
131 | b0f1da2a | Nitesh Mor | We need to import the package `gdp` to begin with. |
132 | |||
133 | 8949c8d7 | Nitesh Mor | ```python |
134 | >>> import gdp |
||
135 | ``` |
||
136 | |||
137 | Once imported, we need to initialize the GDP package by calling `gdp_init()`. |
||
138 | An optional argument to `gdp_init()` is the address of a GDP-router. If no |
||
139 | address provided, a default value of the parameter `swarm.gdp.routers` is used |
||
140 | eebee62a | Nitesh Mor | as configured by EP library. (See README.md for details). |
141 | 8949c8d7 | Nitesh Mor | |
142 | ```python |
||
143 | 09a91891 | Nitesh Mor | >>> # the following picks a router based on EP library configuration |
144 | 8949c8d7 | Nitesh Mor | >>> gdp.gdp_init() |
145 | 09a91891 | Nitesh Mor | >>> # For a specific router, use the following: |
146 | >>> # gdp.gdp_init('gdp-01.eecs.berkeley.edu:8007') |
||
147 | 8949c8d7 | Nitesh Mor | ``` |
148 | |||
149 | As mentioned earlier, we support human readable names for logs. The mechanism |
||
150 | for translating a human readable name to a 256-bit name is probably going to |
||
151 | change in the future, however, it is our hope that it should be a simple |
||
152 | change. The Python wrapper uses instances of class `gdp.GDP_NAME` for a name, |
||
153 | which could be initialized using a human readable name. |
||
154 | |||
155 | ```python |
||
156 | >>> # Create a GDP_NAME object from a human readable python string |
||
157 | 1a02be05 | Nitesh Mor | >>> gin_name = gdp.GDP_NAME("edu.berkeley.eecs.mor.01") |
158 | 8949c8d7 | Nitesh Mor | ``` |
159 | |||
160 | Once we have a `GDP_NAME`, we can use this to open a handle to a log/GCL. A log |
||
161 | handle works like a file handle in some ways. We need to tell whether we want |
||
162 | to open the GCL in read-only mode (`gdp.GDP_MODE_RO`), or append-only mode |
||
163 | (`gdp.GDP_MODE_AO`), or read-and-append mode (`gdp.GDP_MODE_RA`). |
||
164 | |||
165 | ```python |
||
166 | >>> # assume that this log already exists. |
||
167 | 1a02be05 | Nitesh Mor | >>> gin_handle = gdp.GDP_GIN(gin_name, gdp.GDP_MODE_RA) |
168 | 8949c8d7 | Nitesh Mor | ``` |
169 | |||
170 | Next, let's append a few records to the log. The unit of read/write to a log is |
||
171 | eebee62a | Nitesh Mor | called a record--data with some automatically generated metadata--represented |
172 | 1a02be05 | Nitesh Mor | by a `GDP_DATUM` object. The `GDP_DATUM` object contains a `GDP_BUF` that holds |
173 | the actual data. (Please see the C-api for more details on the behavior of |
||
174 | buffer objects, and such). |
||
175 | |||
176 | 8949c8d7 | Nitesh Mor | |
177 | ```python |
||
178 | 1a02be05 | Nitesh Mor | >>> d = gdp.GDP_DATUM() |
179 | 8949c8d7 | Nitesh Mor | >>> for idx in xrange(10): |
180 | 1a02be05 | Nitesh Mor | ... d["buf"].reset() |
181 | ... d["buf"].write("Hello world " + str(idx)} |
||
182 | ... gin_handle.append(d) |
||
183 | 8949c8d7 | Nitesh Mor | ``` |
184 | |||
185 | That's it. Ideally, it should finish without throwing any errors, resulting in |
||
186 | 10 records append to the log specified. |
||
187 | |||
188 | Look at `/lang/python/apps/writer_test.py` for a full example. |
||
189 | |||
190 | ## Reading data by record number |
||
191 | |||
192 | Next, let's read some data back and see if it matches what we wrote. Note that |
||
193 | we need to tell what record number we want to read, and record numbers start |
||
194 | 1a02be05 | Nitesh Mor | from 1. To read data, we just use `read_by_recno` method of the GDP_GIN instance |
195 | with the record number. |
||
196 | 8949c8d7 | Nitesh Mor | |
197 | ```python |
||
198 | >>> for idx in xrange(1,11): |
||
199 | 1a02be05 | Nitesh Mor | ... datum = gin_handle.read_by_recno(idx) |
200 | ... print datum["recno"], datum["buf"].peek() |
||
201 | (1, 'Hello world 0') |
||
202 | (2, 'Hello world 1') |
||
203 | (3, 'Hello world 2') |
||
204 | (4, 'Hello world 3') |
||
205 | (5, 'Hello world 4') |
||
206 | (6, 'Hello world 5') |
||
207 | (7, 'Hello world 6') |
||
208 | (8, 'Hello world 7') |
||
209 | (9, 'Hello world 8') |
||
210 | (10, 'Hello world 9') |
||
211 | 8949c8d7 | Nitesh Mor | ``` |
212 | |||
213 | So far, we saw how to read and write data by record number. However, most of |
||
214 | the times, we are interested in the most recent record. For this, we support |
||
215 | negative record numbers, i.e. `-1` refers to the most recent record, `-2` |
||
216 | refers to the second most recent record, and so on. |
||
217 | |||
218 | Look at `/lang/python/apps/reader_test.py` for a full example. |
||
219 | |||
220 | ## Subscriptions |
||
221 | |||
222 | Next, let's see how can we subscribe to a log to get new data from a log as it |
||
223 | 1a02be05 | Nitesh Mor | gets appended. For this, we use `subscribe_by_recno` method of the `gdp.GDP_GIN` |
224 | 8949c8d7 | Nitesh Mor | instance. |
225 | |||
226 | ```python |
||
227 | >>> # ignore the parameters for the moment |
||
228 | 1a02be05 | Nitesh Mor | >>> gin_handle.subscribe_by_recno(0, 0, None) |
229 | 8949c8d7 | Nitesh Mor | ``` |
230 | |||
231 | This subscription returns events, that we need to process in order to get |
||
232 | notified of the data as it appears. |
||
233 | |||
234 | ```python |
||
235 | >>> while True: |
||
236 | ... # this blocks until there is a new event |
||
237 | 1a02be05 | Nitesh Mor | ... event = gin_handle.get_next_event(None) |
238 | ... # Events can be used to get the associated datum |
||
239 | 8949c8d7 | Nitesh Mor | ... if event["type"] == gdp.GDP_EVENT_DATA: |
240 | ... datum = event["datum"] |
||
241 | 1a02be05 | Nitesh Mor | ... print datum["buf"].peek() |
242 | 8949c8d7 | Nitesh Mor | ... else: |
243 | ... # we ignore other event types for simplicity |
||
244 | ... break |
||
245 | ``` |
||
246 | |||
247 | 1a02be05 | Nitesh Mor | In the above code, `event` is an object of type `GDP_EVENT`, which can be used |
248 | to get the associated `GDP_DATUM` (and then `GDP_BUF`). In order to see the |
||
249 | above code in action, open another python console (while this is running), and |
||
250 | append some new data to the log just the way you saw above. |
||
251 | 8949c8d7 | Nitesh Mor | |
252 | Look at `/lang/python/apps/reader_test_subscribe.py` for a full example. |
||
253 | |||
254 | 1a02be05 | Nitesh Mor | ## Reading multiple records at a time |
255 | 8949c8d7 | Nitesh Mor | |
256 | Reading one record at a time can be very inefficient, especially when reading |
||
257 | 1a02be05 | Nitesh Mor | large amount of data. For this, we support asynchronous reads to read a range of |
258 | records at a time. The interface is similar to `subscribe_by_recno` in some |
||
259 | sense--events are returned as a result of an asynchronous call. |
||
260 | 8949c8d7 | Nitesh Mor | |
261 | 1a02be05 | Nitesh Mor | Look at `/lang/python/apps/reader_test_async.py` for a full example. |
262 | 8949c8d7 | Nitesh Mor | |
263 | |||
264 | ## Asynchronous write |
||
265 | |||
266 | *Partially implemented*. In the normal `append` call above, a client sends some |
||
267 | data to the log-server and waits for an acknowledgement before returning |
||
268 | control back to the application. In order to convert this blocking operation to |
||
269 | a non-blocking operation, `append_async` could be used instead of regular |
||
270 | `append`. |
||
271 | |||
272 | Refer to the API documentation at `/lang/python/README` for more details. |