#![deny(missing_docs)]
#[macro_use]
extern crate serde_derive;
#[cfg(test)]
#[macro_use]
extern crate serde_json;
extern crate indexmap;
extern crate ommui_string_patterns;
extern crate uuid;
extern crate xio_base_datatypes;
mod extract_descriptions;
pub use extract_descriptions::{
CommandDescription, ConditionDescription, ExtractDescriptions,
};
use indexmap::IndexMap;
use uuid::Uuid;
use xio_base_datatypes as base;
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct JobSet {
pub metadata: Metadata,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub parameters: Vec<ParameterSet>,
#[serde(default, skip_serializing_if = "IndexMap::is_empty")]
pub jobs: IndexMap<String, Job>,
#[serde(default, skip_serializing_if = "IndexMap::is_empty")]
pub channels: IndexMap<String, ChannelAssignment>,
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct Metadata {
pub uuid: Uuid,
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ParameterSet {
pub metadata: ParameterSetMetadata,
#[serde(default, skip_serializing_if = "IndexMap::is_empty")]
pub descriptions: IndexMap<String, ParameterDescription>,
#[serde(default, skip_serializing_if = "IndexMap::is_empty")]
pub values: IndexMap<String, base::DataValueDescriptive>,
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ParameterSetMetadata {
pub name: String,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ParameterDescription {
#[serde(rename = "description")]
pub description: String,
#[serde(rename = "type")]
pub data_type: base::DataType,
pub storage: base::StorageType,
#[serde(rename = "override")]
pub override_option: base::OverrideOption,
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct Job {
pub commands: Vec<Command>,
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct Command {
#[serde(
rename = "_message",
skip_serializing_if = "String::is_empty",
default
)]
pub message: String,
pub description: String,
#[serde(rename = "type")]
pub command_type: String,
#[serde(default, skip_serializing_if = "IndexMap::is_empty")]
pub parameters: IndexMap<String, String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub conditions: Vec<Condition>,
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct Condition {
#[serde(
rename = "_message",
skip_serializing_if = "String::is_empty",
default
)]
pub message: String,
pub description: String,
#[serde(rename = "type")]
pub command_type: String,
pub exit_job: bool,
#[serde(default, skip_serializing_if = "IndexMap::is_empty")]
pub parameters: IndexMap<String, String>,
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ChannelAssignment {
pub capability: String,
pub channel: String,
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct HardwareAssignment {
pub hardware_board: String,
pub mapping: IndexMap<String, ChannelAssignment>,
}
#[cfg(test)]
mod tests {
use base::{
DataType, DataValue, DataValueDescriptive, OverrideOption,
StorageType,
};
use indexmap::IndexSet;
use {
serde_json, ChannelAssignment, Command, Condition, IndexMap, Job,
JobSet, Metadata, ParameterDescription, ParameterSet,
ParameterSetMetadata, Uuid,
};
#[test]
fn example_jobset() {
let json = json!({
"metadata": {
"uuid": "bf6e3ca9-176e-46e0-a886-84bfc57e13b4"
},
"channels":{
"a1":{
"capability":"motor",
"channel":"speed"
},
"a3":{
"capability":"adc",
"channel":"voltage"
}
},
"jobs":{
"init":{
"commands":[
{
"description":"init job command 1",
"conditions":[
{
"_message": "condition 1 matched",
"description":"init job cmd 1 condition 1",
"exitJob":true,
"parameters":{"a":"a1","b":"b1"},
"type":"equals"
}
],
"parameters":{"a":"b1","b":"a1"},
"type":"wait"
},
{
"_message":"waiting some time",
"description":"init job command 2",
"parameters":{"a":"b1"},
"type":"wait"
}
]
},
"measurement":{
"commands": [
{
"_message":"waiting for something",
"description":"measurement job command 1",
"type":"wait"
},
{
"_message":"waiting for another thing",
"description":"measurement job command 2",
"type":"wait"
}
]
}
},
"parameters":[
{
"descriptions":{
"a1":{
"description":"Parameter A 1",
"override":"forbid",
"storage":"channel",
"type":"uint8"
},
"a2":{
"description":"Parameter A 2",
"override":"enforce",
"storage":"fixed",
"type":"parametermask"
},
"a3":{
"description":"Parameter A 3",
"override":"allow",
"storage":"channel",
"type":"boolean"
}
},
"metadata": {
"name":"base"
},
"values":{"a1":3,"a2":["a3","b1"]}
},
{
"descriptions":{
"b1":{
"description":"Parameter B 1",
"override":"forbid",
"storage":"channel",
"type":"uint8"
}
},
"metadata": {
"name":"overlay"
},
"values":{"b1":14}
}
]
});
let parameterset1 = ParameterSet {
metadata: ParameterSetMetadata {
name: "base".to_string(),
},
descriptions: vec![
(
"a1".to_string(),
ParameterDescription {
description: "Parameter A 1".to_string(),
data_type: DataType::UInt8,
storage: StorageType::Channel,
override_option: OverrideOption::Forbid,
},
),
(
"a2".to_string(),
ParameterDescription {
description: "Parameter A 2".to_string(),
data_type: DataType::ParameterMask,
storage: StorageType::Fixed,
override_option: OverrideOption::Enforce,
},
),
(
"a3".to_string(),
ParameterDescription {
description: "Parameter A 3".to_string(),
data_type: DataType::Boolean,
storage: StorageType::Channel,
override_option: OverrideOption::Allow,
},
),
]
.into_iter()
.collect::<IndexMap<String, ParameterDescription>>(),
values: vec![
("a1".to_string(), DataValue::UInt8(3u8)),
(
"a2".to_string(),
DataValue::ParameterMask(
vec!["a3".to_string(), "b1".to_string()]
.into_iter()
.collect::<IndexSet<String>>(),
),
),
]
.into_iter()
.collect::<IndexMap<String, DataValueDescriptive>>(),
};
let parameterset2 = ParameterSet {
metadata: ParameterSetMetadata {
name: "overlay".to_string(),
},
descriptions: vec![(
"b1".to_string(),
ParameterDescription {
description: "Parameter B 1".to_string(),
data_type: DataType::UInt8,
storage: StorageType::Channel,
override_option: OverrideOption::Forbid,
},
)]
.into_iter()
.collect::<IndexMap<String, ParameterDescription>>(),
values: vec![("b1".to_string(), DataValue::UInt8(14u8))]
.into_iter()
.collect::<IndexMap<String, DataValueDescriptive>>(),
};
let job1 = Job {
commands: vec![
Command {
message: "".to_string(),
description: "init job command 1".to_string(),
conditions: vec![Condition {
message: "condition 1 matched".to_string(),
description: "init job cmd 1 condition 1"
.to_string(),
exit_job: true,
parameters: vec![("a", "a1"), ("b", "b1")]
.into_iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect(),
command_type: "equals".to_string(),
}],
command_type: "wait".to_string(),
parameters: vec![("a", "b1"), ("b", "a1")]
.into_iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect(),
},
Command {
message: "waiting some time".to_string(),
description: "init job command 2".to_string(),
conditions: Vec::new(),
command_type: "wait".to_string(),
parameters: vec![("a", "b1")]
.into_iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect(),
},
],
};
let job2 = Job {
commands: vec![
Command {
message: "waiting for something".to_string(),
description: "measurement job command 1".to_string(),
conditions: Vec::new(),
command_type: "wait".to_string(),
parameters: IndexMap::new(),
},
Command {
message: "waiting for another thing".to_string(),
description: "measurement job command 2".to_string(),
conditions: Vec::new(),
command_type: "wait".to_string(),
parameters: IndexMap::new(),
},
],
};
let jobset = JobSet {
metadata: Metadata {
uuid: Uuid::from_bytes([
0xbf, 0x6e, 0x3c, 0xa9, 0x17, 0x6e, 0x46, 0xe0, 0xa8,
0x86, 0x84, 0xbf, 0xc5, 0x7e, 0x13, 0xb4,
]),
},
parameters: vec![parameterset1, parameterset2],
jobs: vec![
("init".to_string(), job1),
("measurement".to_string(), job2),
]
.into_iter()
.collect(),
channels: vec![
(
"a1".to_string(),
ChannelAssignment {
capability: "motor".to_string(),
channel: "speed".to_string(),
},
),
(
"a3".to_string(),
ChannelAssignment {
capability: "adc".to_string(),
channel: "voltage".to_string(),
},
),
]
.into_iter()
.collect(),
};
let deserialized = serde_json::from_value(json.clone()).unwrap();
assert_eq!(jobset, deserialized);
let serialized = serde_json::to_value(&jobset).unwrap();
assert_eq!(serialized, json);
}
}