Skip to content

Commit 9ac10c2

Browse files
committed
initial image_store_file_action
1 parent d980c23 commit 9ac10c2

File tree

8 files changed

+205
-26
lines changed

8 files changed

+205
-26
lines changed

Cargo.lock

Lines changed: 5 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "camera-traps"
3-
version = "0.2.1"
3+
version = "0.2.2"
44
edition = "2021"
55

66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -18,8 +18,9 @@ path-absolutize = "3.0.13"
1818
rand = "0.8.5"
1919
serde = "1.0.144"
2020
serde_derive = "1.0.144"
21+
serde_json = "1.0.92"
2122
shellexpand = "2.1.2"
2223
thiserror = "1.0.32"
2324
toml = "0.5.9"
24-
uuid = { version = "1.1", features = ["v4"] }
25+
uuid = { version = "1.1", features = ["v4", "serde"] }
2526
zmq = "0.9.2"

resources/traps.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ internal = [
2929
# take when new work is received. If no action is specified for a plugin, its no-op action
3030
# is used by default.
3131
internal_actions = [
32-
"image_recv_write_file_action"
32+
"image_recv_write_file_action",
33+
"image_store_file_action"
3334
]
3435

3536
# External plugins require more configuration information than internal plugins.

src/config/errors.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,15 @@ pub enum Errors {
6969
#[error("Plugin {} is unable to convert a {} event to vector of bytes: {}", .0, .1, .2)]
7070
EventToBytesError(String, String, String),
7171

72+
#[error("Plugin {} is unable to convert a {} event to vector to JSON: {}", .0, .1, .2)]
73+
EventToJsonError(String, String, String),
74+
7275
#[error("Expected event type {}, but received event {} instead.", .0, .1)]
7376
EventUnexpectedError(String, String),
7477

78+
#[error("Failed to delete file {0}: {1}")]
79+
FileDeleteError(String, String),
80+
7581
#[error("File IO error: {}", .0)]
7682
FileIOError(String),
7783

src/events.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use event_engine::events::{Event, EventType};
33
use flatbuffers::{FlatBufferBuilder, InvalidFlatbuffer};
44
use std::error::Error;
55
use uuid::Uuid;
6+
use serde::Serialize;
67

78
// Logging imports.
89
use anyhow::Result;
@@ -379,6 +380,7 @@ impl ImageReceivedEvent {
379380
// ------------------------------
380381
// ------ ImageLabelScore
381382
// ------------------------------
383+
#[derive(Serialize)]
382384
pub struct ImageLabelScore {
383385
image_uuid: Uuid,
384386
label: String,
@@ -432,6 +434,7 @@ impl ImageLabelScore {
432434
// ------------------------------
433435
// ------ ImageScoredEvent
434436
// ------------------------------
437+
#[derive(Serialize)]
435438
pub struct ImageScoredEvent {
436439
created: String,
437440
image_uuid: Uuid,
@@ -544,10 +547,26 @@ impl Event for ImageScoredEvent {
544547
// ------ Associated Functions
545548
// ------------------------------
546549
impl ImageScoredEvent {
550+
// ----------------------------------------------------------------------
551+
// accessors:
552+
// ----------------------------------------------------------------------
553+
pub fn get_created(&self) -> &String {
554+
return &self.created;
555+
}
556+
pub fn get_image_uuid(&self) -> &Uuid {
557+
return &self.image_uuid;
558+
}
559+
pub fn get_image_format(&self) -> &String {
560+
return &self.image_format;
561+
}
562+
pub fn get_scores(&self) -> &Vec<ImageLabelScore> {
563+
return &self.scores;
564+
}
565+
547566
// ----------------------------------------------------------------------
548567
// new:
549568
// ----------------------------------------------------------------------
550-
#![allow(unused)]
569+
#[allow(unused)]
551570
pub fn new(image_uuid: Uuid, image_format: String, scores: Vec<ImageLabelScore>) -> Self {
552571
ImageScoredEvent {
553572
created: timestamp_str(),

src/plugins/actions/image_store_actions.rs

Lines changed: 135 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,21 @@
33
use crate::{Config, traps_utils};
44
use crate::plugins::image_store_plugin::{ImageStorePlugin, StoreAction, StoreParms};
55
use crate::events_generated::gen_events::ImageScoredEvent;
6-
use crate::{config::errors::Errors};
7-
use anyhow::{Result, anyhow};
6+
use crate::{events, config::errors::Errors};
7+
use event_engine::{plugins::Plugin};
88

9+
use anyhow::{Result, anyhow};
10+
use std::fs;
11+
use serde_json;
912

1013
use log::{info, error};
1114

1215
// The search string prefix for this plugin.
1316
const PREFIX: &str = "image_store_";
1417

18+
// The score file suffix.
19+
const SCORE_SUFFIX: &str = "score";
20+
1521
// ***************************************************************************
1622
// PUBLIC FUNCTIONS
1723
// ***************************************************************************
@@ -96,10 +102,10 @@ pub fn image_store_file_action(plugin: &ImageStorePlugin, event: &ImageScoredEve
96102

97103
// Perform the action.
98104
match store_action {
99-
StoreAction::Delete => action_delete(event),
100-
StoreAction::Noop => action_noop(event),
101-
StoreAction::ReduceSave => action_reduce_save(event),
102-
StoreAction::Save => action_save(event),
105+
StoreAction::Delete => action_delete(plugin, event),
106+
StoreAction::Noop => {},
107+
StoreAction::ReduceSave => action_reduce_save(plugin, event),
108+
StoreAction::Save => action_save(plugin, event),
103109
}
104110

105111
// Return the action taken.
@@ -150,39 +156,149 @@ fn get_action_for_score(store_parms_ref: &StoreParms, score: f32) -> StoreAction
150156
}
151157

152158
// ---------------------------------------------------------------------------
153-
// create_image_filepath:
159+
// make_image_filepath:
154160
// ---------------------------------------------------------------------------
155161
/** Create absolute file path for the image. */
156-
fn create_image_filepath(plugin: &ImageStorePlugin, uuid_str: &str, suffix: &str) -> String {
157-
return traps_utils::create_image_filepath(&plugin.get_runctx().abs_image_dir,
158-
&plugin.get_runctx().parms.config.image_file_prefix,
159-
uuid_str,
160-
suffix);
162+
fn make_image_filepath(plugin: &ImageStorePlugin, event: &ImageScoredEvent) -> Option<String> {
163+
// Get the uuid string for use in the file name.
164+
let uuid_str = match event.image_uuid() {
165+
Some(s) => s,
166+
None => {
167+
// Log the error and just return.
168+
let msg = format!("{}", Errors::PluginEventAccessUuidError(
169+
plugin.get_name(), "NewImageEvent".to_string()));
170+
error!("{}", msg);
171+
return None;
172+
}
173+
};
174+
175+
// Standardize image type suffixes to lowercase.
176+
let suffix = match event.image_format() {
177+
Some(s) => s.to_string().to_lowercase(),
178+
None => {
179+
// Log the error and just return.
180+
let msg = format!("{}", Errors::ActionImageFormatTypeError(
181+
plugin.get_name(), "NewImageEvent".to_string()));
182+
error!("{}", msg);
183+
return None;
184+
}
185+
};
186+
187+
// Get the path.
188+
let path = traps_utils::create_image_filepath(&plugin.get_runctx().abs_image_dir,
189+
&plugin.get_runctx().parms.config.image_file_prefix,
190+
uuid_str,
191+
suffix.as_str());
192+
193+
Option::Some(path)
161194
}
162195

163196
// ---------------------------------------------------------------------------
164-
// action_delete:
197+
// make_score_filepath:
165198
// ---------------------------------------------------------------------------
166-
fn action_delete(event: &ImageScoredEvent) {
167-
let uuid = event.image_uuid();
199+
/** Create absolute file path for the image. */
200+
fn make_score_filepath(plugin: &ImageStorePlugin, event: &ImageScoredEvent) -> Option<String> {
201+
// Get the uuid string for use in the file name.
202+
let uuid_str = match event.image_uuid() {
203+
Some(s) => s,
204+
None => {
205+
// Log the error and just return.
206+
let msg = format!("{}", Errors::PluginEventAccessUuidError(
207+
plugin.get_name(), "NewImageEvent".to_string()));
208+
error!("{}", msg);
209+
return None;
210+
}
211+
};
212+
213+
// Get the path.
214+
let path = traps_utils::create_image_filepath(&plugin.get_runctx().abs_image_dir,
215+
&plugin.get_runctx().parms.config.image_file_prefix,
216+
uuid_str,
217+
SCORE_SUFFIX);
218+
219+
Option::Some(path)
168220
}
169221

170222
// ---------------------------------------------------------------------------
171-
// action_noop:
223+
// action_delete:
172224
// ---------------------------------------------------------------------------
173-
fn action_noop(event: &ImageScoredEvent) {
225+
/** Delete the image file and don't save the scores.
226+
*/
227+
fn action_delete(plugin: &ImageStorePlugin, event: &ImageScoredEvent) {
228+
// Create absolute file path for the image. Errors have alread been logged.
229+
let filepath = match make_image_filepath(plugin, event) {
230+
Some(p) => p,
231+
None => return,
232+
};
174233

234+
// Delete the file.
235+
match fs::remove_file(filepath.clone()){
236+
Ok(_) => {},
237+
Err(e) => {
238+
// Log error.
239+
let msg = format!("{}", Errors::FileDeleteError(
240+
filepath, e.to_string()));
241+
error!("{}", msg);
242+
}
243+
};
175244
}
176245

177246
// ---------------------------------------------------------------------------
178247
// action_reduce_save:
179248
// ---------------------------------------------------------------------------
180-
fn action_reduce_save(event: &ImageScoredEvent) {
249+
/** Reduce the image resolution and then save it and it's scores.
250+
*/
251+
fn action_reduce_save(plugin: &ImageStorePlugin, event: &ImageScoredEvent) {
252+
// TODO: reduce image size
181253

254+
action_save(plugin, event)
182255
}
256+
183257
// ---------------------------------------------------------------------------
184258
// action_save:
185259
// ---------------------------------------------------------------------------
186-
fn action_save(event: &ImageScoredEvent) {
260+
/** Leave the image file as-is and save its scores. On error, just log and return.
261+
*/
262+
fn action_save(plugin: &ImageStorePlugin, event: &ImageScoredEvent) {
263+
// Extract the image uuid from the new image event.
264+
let image_scored_event = match events::ImageScoredEvent::new_from_gen(*event) {
265+
Ok(ev) => ev,
266+
Err(e) => {
267+
let msg = format!("{}", Errors::PluginEventDeserializationError(
268+
plugin.get_name(), "ImageScoredEvent".to_string()));
269+
error!("{}: {}", msg, e.to_string());
270+
return
271+
}
272+
};
187273

274+
// Convert the event scores to json.
275+
let json_str = match serde_json::to_string(&image_scored_event) {
276+
Ok(s) => s,
277+
Err(e) => {
278+
let msg = format!("{}", Errors::EventToJsonError(
279+
plugin.get_name(), "ImageScoredEvent".to_string(), e.to_string()));
280+
error!("{}", msg);
281+
return;
282+
}
283+
};
284+
285+
// Construct the score output path name.
286+
let filepath = match make_score_filepath(plugin, event) {
287+
Some(fp)=> fp,
288+
None => {
289+
// Error already logged.
290+
return;
291+
}
292+
};
293+
294+
// Write the json to the score output file.
295+
match traps_utils::create_or_replace_file(&filepath, json_str.as_bytes()) {
296+
Ok(_) => (),
297+
Err(e) => {
298+
let msg = format!("{}", Errors::ActionWriteFileError(plugin.get_name(),
299+
"action_save".to_string(), filepath, e.to_string()));
300+
error!("{}", msg);
301+
return;
302+
}
303+
};
188304
}

src/plugins/image_store_plugin.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,9 @@ impl ImageStorePlugin {
370370
#[cfg(test)]
371371
mod tests {
372372
use crate::plugins::image_store_plugin::StoreAction;
373+
use crate::events::{ImageLabelScore, ImageScoredEvent};
374+
use uuid::Uuid;
375+
use serde_json;
373376

374377
#[test]
375378
fn here_i_am() {
@@ -417,6 +420,24 @@ mod tests {
417420
assert_eq!(&list[4], &(15u8, StoreAction::Noop));
418421
assert_eq!(&list[5], &(5u8, StoreAction::Save));
419422
assert_eq!(&list[6], &(0u8, StoreAction::Delete));
423+
}
424+
425+
#[test]
426+
fn sertest() {
427+
// Image uuid.
428+
let uuid = Uuid::new_v4();
429+
430+
// Create label vector.
431+
let label1 = ImageLabelScore::new(uuid.clone(), "cow".to_string(), 0.8);
432+
let label2 = ImageLabelScore::new(uuid.clone(), "dog".to_string(), 0.3);
433+
let labels:Vec<ImageLabelScore> = vec!(label1, label2);
434+
435+
// Create the event.
436+
let ev = ImageScoredEvent::new(uuid.clone(), "png".to_string(), labels);
420437

438+
// Serialize event to json.
439+
let json_str = serde_json::to_string(&ev).unwrap();
440+
println!("{}", json_str); // assert is difficult because of timestamp.
421441
}
442+
422443
}

0 commit comments

Comments
 (0)