Skip to content

Commit cefd8de

Browse files
author
Niccolo Rigacci
committed
Added a PHP script and some instructions to create an AirPi datacenter
1 parent c2d7d08 commit cefd8de

File tree

4 files changed

+140
-0
lines changed

4 files changed

+140
-0
lines changed

extras/datacenter/README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# How to Create an AirPi Datacenter
2+
3+
Do you have several AirPi stations? Do you want to collect all the data
4+
into a **single database** and access them from a **single web server**?
5+
6+
* Get an Apache + PHP + Database host. The PHP code uses the MDB2 library.
7+
* Create a database and initialize it with the required tables and indexes.
8+
* Populate the "stations" table with name, login and password for each
9+
station.
10+
* Install the update.php script into the datacenter host; it will receive
11+
data from the AirPi stations.
12+
* On each station configure the HTTP_REQUEST_URL, login and password.
13+
14+
## Asyncronous and Incremental Data Sumbission
15+
16+
Each station is not required to be on-line 24h/24h to submit data.
17+
To make an example: you can give internet connection to a station only once
18+
a day, by using a WiFi adapter and a mobile phone with tethering enabled.
19+
When the station tries to upload its data to the datacenter (default
20+
is every 5 minutes) it will flush all the data measured an stored locally.
21+
22+
## Files
23+
24+
* **update.php** This is the script which will collect the data from the
25+
stations. The **db_dsn.php** snippet contains the database credentials.
26+
The same script is called by the station to know how much of past data
27+
is to be uploaded. The station must point its configuration parameter
28+
**HTTP_REQUEST_URL** to the URL for this script; e.g. https://host/update.php.
29+
* **db-initialize-pgsql.sql** Use this SQL script to create the tables
30+
and the indexes into the database. This is for a PostgreSQL database, you
31+
should adapt for other engines, like MySQL, etc.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
-- CREATE USER "airpi" PASSWORD '****';
2+
-- CREATE DATABASE airpi OWNER airpi ENCODING 'UTF8';
3+
4+
CREATE TABLE stations2 (
5+
id SERIAL PRIMARY KEY,
6+
name VARCHAR UNIQUE NOT NULL,
7+
login VARCHAR UNIQUE NOT NULL,
8+
password VARCHAR NOT NULL,
9+
lat DOUBLE PRECISION,
10+
lon DOUBLE PRECISION
11+
);
12+
13+
CREATE TABLE data (
14+
station_id INTEGER NOT NULL,
15+
time_stamp TIMESTAMP NOT NULL,
16+
type VARCHAR NOT NULL,
17+
value FLOAT
18+
);
19+
ALTER TABLE data ADD CONSTRAINT data_station_id_fkey FOREIGN KEY (station_id) REFERENCES stations(id);
20+
CREATE UNIQUE INDEX data_time_stamp_type_idx ON data (station_id, time_stamp, type);
21+
CREATE INDEX data_time_stamp_idx ON data (time_stamp);
22+
CREATE INDEX data_type_idx ON data (type);

extras/datacenter/db_dsn.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
// Connection to the database, using the MDB2 abstraction.
4+
require_once 'MDB2.php';
5+
$dsn = 'pgsql://airpi:MySecretPass@localhost:5432/airpi';
6+
7+
?>

extras/datacenter/update.php

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
// Store sensors data from remote stations into a datacenter database.
4+
5+
require_once('db_dsn.php');
6+
7+
// ISO 8601 UTC timestamp format. See strftime() PHP function.
8+
define('TIMESTAMP_FORMAT', '%Y-%m-%dT%H:%M:%SZ');
9+
10+
$login = (isset($_REQUEST['login'])) ? $_REQUEST['login'] : NULL;
11+
$password = (isset($_REQUEST['password'])) ? $_REQUEST['password'] : NULL;
12+
if ($login == NULL) exit('Invalid login');
13+
14+
// Connect to the database, use the MDB2 abstraction.
15+
$mdb2 = MDB2::connect($dsn);
16+
if (PEAR::isError($mdb2)) exit($mdb2->getMessage());
17+
$mdb2->setFetchMode(MDB2_FETCHMODE_ASSOC);
18+
19+
// Authenticate the client.
20+
$sql = 'SELECT id, password FROM stations WHERE login = ?';
21+
$query = $mdb2->prepare($sql, array('text'), array('integer', 'text'));
22+
$result = $query->execute($login);
23+
if (PEAR::isError($result)) exit($result->getMessage());
24+
$station = $result->fetchRow();
25+
$query->free();
26+
$result->free();
27+
if (! isset($station['password'])) exit('Login failed');
28+
if ($station['password'] != md5($password)) exit('Login failed');
29+
30+
// Received some data, INSERT into the database.
31+
if (isset($_REQUEST['sensors_data'])) {
32+
$sensors_data = json_decode($_REQUEST['sensors_data']);
33+
//error_log("JSON decoded sensors_data = " . print_r($sensors_data, true));
34+
// Open a transaction
35+
$res = $mdb2->beginTransaction();
36+
$sql = 'INSERT INTO data (station_id, time_stamp, type, value) VALUES (?, ?, ?, ?)';
37+
$query = $mdb2->prepare($sql, array('integer', 'text', 'text', 'float'));
38+
if (PEAR::isError($query)) exit($query->getMessage());
39+
// TODO: Insert the whole array with a single query?
40+
// TODO: Enclose in BEGIN/COMMIT
41+
foreach($sensors_data as $row) {
42+
array_unshift($row, $station['id']);
43+
$result = $query->execute($row);
44+
if (PEAR::isError($result)) {
45+
$mdb2->rollback();
46+
exit($result->getMessage());
47+
}
48+
}
49+
$mdb2->commit();
50+
$query->free();
51+
}
52+
53+
// Get the timestamp of last inserted data.
54+
$sql = 'SELECT max(time_stamp) AS time_stamp FROM data WHERE station_id = ?';
55+
$query = $mdb2->prepare($sql, array('integer'), array('timestamp'));
56+
if (PEAR::isError($query)) exit($query->getMessage());
57+
$result = $query->execute($station['id']);
58+
if (PEAR::isError($result)) exit($result->getMessage());
59+
$row = $result->fetchRow();
60+
$query->free();
61+
$result->free();
62+
if ($row['time_stamp'] == NULL) {
63+
// Unix Epoch (January 1 1970 00:00:00 GMT)
64+
$utc_timestamp = gmstrftime(TIMESTAMP_FORMAT, 0);
65+
} else {
66+
$utc_timestamp = substr($row['time_stamp'], 0, 10) . 'T' . substr($row['time_stamp'], 11, 8) . 'Z';
67+
if (!preg_match('/^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\dZ$/', $utc_timestamp)) {
68+
exit('Invalid database timestamp format');
69+
}
70+
}
71+
72+
$mdb2->disconnect();
73+
74+
header('Content-type: application/json');
75+
echo json_encode(array(
76+
'name' => $login,
77+
'latest_timestamp' => $utc_timestamp
78+
));
79+
80+
?>

0 commit comments

Comments
 (0)