Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify sensor tracker data #93

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion services/redis_context/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions services/redis_context/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
[package]
name = "redis_context"
version = "0.1.0"
version = "0.2.0"
authors = ["Terkwood <[email protected]>"]
edition = "2018"

[dependencies]
# 🤖 This artificially low version of rand core will compile on ARMv7
rand_core="0.2.2"
redis = "0.9"
uuid = { version = "0.7", features = ["v4", "v5"] } # v4 is random, v5 is name-based
uuid = { version = "0.7", features = ["v4", "v5"] }
25 changes: 2 additions & 23 deletions services/redis_context/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,9 @@ impl RedisContext {
}

/// This is the "name" field that will be used to form a V5 UUID
pub fn get_external_device_namespace(
&self,
device_type: String,
) -> Result<Uuid, redis::RedisError> {
pub fn get_external_device_namespace(&self) -> Result<Uuid, redis::RedisError> {
let key = format!("{}/external_device_namespace", self.namespace);
let r: Option<String> = self.conn.hget(&key, device_type)?;
let r: Option<String> = self.conn.get(&key)?;

match r {
None => {
Expand All @@ -43,21 +40,3 @@ impl RedisContext {
}
}
}


pub enum ExternalDevice {
Temp,
PH,
Unknown
}


impl From<String> for ExternalDevice {
fn from(device_type: String) -> Self {
match device_type.to_lowercase().trim() {
"temp" => ExternalDevice::Temp,
"ph" => ExternalDevice::PH,
_ => ExternalDevice::Unknown
}
}
}
13 changes: 7 additions & 6 deletions services/sensor_tracker/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions services/sensor_tracker/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "sensor_tracker"
version = "0.3.1"
version = "0.4.0"
authors = ["Terkwood <[email protected]>"]
edition = "2018"

Expand All @@ -13,7 +13,8 @@ envy = "*"
rand_core="0.2.2"
rumqtt = "*"
redis = "^0.9"
redis_context = { git = "https://github.com/Terkwood/prawnalith/", branch = "unstable" }
# TODO restore branch
redis_context = { git = "https://github.com/Terkwood/prawnalith/", branch = "feature/simplified-sensor-tracker-data" }
redis_delta = { git = "https://github.com/Terkwood/prawnalith/", branch = "unstable" }
serde = "*"
serde_derive = "*"
Expand Down
29 changes: 14 additions & 15 deletions services/sensor_tracker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,20 @@ Additionally, it creates an entry in the Redis `<namespace>/sensors/<temp_or_ph>

This is useful for sensors generating temperature and/or pH data.

Such data comes into an MQTT topic looking like this:
Such data might come into an MQTT topic looking like this:

```json
{ "device_id": <hex>, "temp_f": 81.71, "temp_c": 23.45, "ph": 7.77, "ph_mv": 453.05 }
```

If the sensor isn't, it will create the following type of stub record
for the temp sensor based on a UUID V5 ID conversion:
If the device hasn't ever been tracked, it will create the following type of stub record with an internal device ID. The internal device ID is a (namespaced) UUID V5:

```text
HMSET <namespace>/sensors/<temp_or_ph>/<uuid_v5_id> create_time <epoch>
```

The operator is encouraged to later amend the hash to include
a helpful reference to the tank which the sensor serves, so
a helpful reference to the area which the sensing device serves, so
that the LED status utility can properly format messages.

```text
Expand All @@ -41,9 +40,9 @@ See `build.sh` and `run.sh` for entry points.

#### temp sensor

`> hgetall namespace/sensors/temp/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa`
`> hgetall namespace/devices/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa`

```
```text
1) "create_time"
2) "1540598539"
3) "ext_device_id"
Expand All @@ -60,11 +59,11 @@ See `build.sh` and `run.sh` for entry points.
14) "1"
```

**pH sensor**
#### pH sensor

`> hgetall namespace/sensors/ph/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa`
`> hgetall namespace/devices/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa`

```
```text
1) "low_ph_ref"
2) "4.00"
3) "low_mv"
Expand All @@ -87,19 +86,19 @@ See `build.sh` and `run.sh` for entry points.
20) "286cbc98090000bd"
```

**tank counter**
#### area counter

`> get namespace/tanks`
`> get namespace/areas`

```
```text
"1"
```

**tank hash**
#### area hash

`> hgetall namespace/tanks/1`
`> hgetall namespace/areas/1`

```
```text
hgetall namespace/tanks/1
1) "temp_f"
2) "81.16"
Expand Down
19 changes: 8 additions & 11 deletions services/sensor_tracker/src/logic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,14 @@ pub fn receive_updates(
if let Some(sensor_message) = deser_message(&payload) {
let ext_device_id: &str = &sensor_message.device_id;

sensor_message.measurements().iter().for_each(|measure| {
if let Ok(delta_events) = predis::update(redis_ctx, &measure, ext_device_id)
{
// emit all changed keys & hash field names to redis
// on the appropriate redis pub/sub topic.
// these will be processed later by the gcloud_push utility
predis::publish_updates(redis_ctx, delta_event_topic, delta_events)
}
});
} else {
println!("couldnt deserialize message payload: {:?}", payload)
if let Ok(delta_events) =
predis::update(redis_ctx, &sensor_message, ext_device_id)
{
// emit all changed keys & hash field names to redis
// on the appropriate redis pub/sub topic.
// these will be processed later by the gcloud_push utility
predis::publish_updates(redis_ctx, delta_event_topic, delta_events)
};
}
},
Ok(n) => println!("IGNORE {:?}", n),
Expand Down
126 changes: 26 additions & 100 deletions services/sensor_tracker/src/model.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
/// This message is emitted to an MQTT channel by
/// some device with access to a temp sensor (DS18B20, etc)
/// `external_device_id` is usually reported as a
/// e.g. "28654597090000e4"

#[derive(Serialize, Deserialize, Debug)]
pub struct SensorMessage {
pub device_id: String,
Expand All @@ -13,110 +16,33 @@ pub struct SensorMessage {
pub heat_index_f: Option<f64>,
}

/// `external_device_id` is usually reported as a
/// e.g. "28654597090000e4"
impl SensorMessage {
pub fn measurements(&self) -> Vec<Measurement> {
let mut v: Vec<Measurement> = vec![];
if let (
Some(humidity),
Some(status),
Some(temp_f),
Some(temp_c),
Some(heat_index_f),
Some(heat_index_c),
) = (
self.humidity,
&self.status,
self.temp_f,
self.temp_c,
self.heat_index_f,
self.heat_index_c,
) {
v.push(Measurement::DHT {
status: status.to_owned(),
humidity,
temp_f,
temp_c,
heat_index_f,
heat_index_c,
})
} else if let (Some(temp_f), Some(temp_c)) = (self.temp_f, self.temp_c) {
v.push(Measurement::Temp { temp_f, temp_c })
pub fn to_redis(&self) -> Vec<(&str, String)> {
let mut data = vec![];
if let Some(s) = &self.status {
data.push(("status", s.to_string()));
}

if let (Some(ph), Some(ph_mv)) = (self.ph, self.ph_mv) {
v.push(Measurement::PH { ph, ph_mv })
if let Some(humidity) = self.humidity {
data.push(("humidity", humidity.to_string()));
}

v
}
}

#[derive(Debug)]
pub enum Measurement {
Temp {
temp_f: f64,
temp_c: f64,
},
PH {
ph: f64,
ph_mv: f64,
},
/// Digital humidity and temp, e.g. DHT11 sensor
DHT {
status: String,
humidity: f64,
temp_f: f64,
temp_c: f64,
heat_index_f: f64,
heat_index_c: f64,
},
}

impl Measurement {
pub fn name(&self) -> String {
match self {
Measurement::Temp {
temp_f: _,
temp_c: _,
} => "temp".to_string(),
Measurement::PH { ph: _, ph_mv: _ } => "ph".to_string(),
Measurement::DHT {
status: _,
humidity: _,
temp_f: _,
temp_c: _,
heat_index_f: _,
heat_index_c: _,
} => "dht".to_string(),
if let Some(tf) = self.temp_f {
data.push(("temp_f", tf.to_string()));
}
}

pub fn to_redis(&self) -> Vec<(&str, String)> {
match self {
Measurement::Temp { temp_f, temp_c } => vec![
("temp_f", temp_f.to_string()),
("temp_c", temp_c.to_string()),
],
Measurement::PH { ph, ph_mv } => {
vec![("ph", ph.to_string()), ("ph_mv", ph_mv.to_string())]
}
Measurement::DHT {
status,
humidity,
temp_f,
temp_c,
heat_index_f,
heat_index_c,
} => vec![
("status", status.to_string()),
("humidity", humidity.to_string()),
("temp_f", temp_f.to_string()),
("temp_c", temp_c.to_string()),
("heat_index_f", heat_index_f.to_string()),
("heat_index_c", heat_index_c.to_string()),
],
if let Some(tc) = self.temp_c {
data.push(("temp_c", tc.to_string()));
}
if let Some(hf) = self.heat_index_f {
data.push(("heat_index_f", hf.to_string()));
}
if let Some(hc) = self.heat_index_c {
data.push(("heat_index_c", hc.to_string()));
}
if let Some(ph) = self.ph {
data.push(("ph", ph.to_string()))
}
if let Some(ph_mv) = self.ph_mv {
data.push(("ph_mv", ph_mv.to_string()))
}
data
}
}
Loading