use std::fmt;
use crate::bits::{BitWriter, MAX_10BIT, MAX_12BIT, MAX_20BIT};
use crate::environment::config::Config;
use crate::result::CompilerResult;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Segment {
relative_time: u32,
pub relay_instructions: Vec<RelayInstr>,
pub sensor_instructions: Vec<SensorInstr>,
}
impl Segment {
const HEADER_LEN: u32 = 7;
pub fn try_new(relative_time: u32) -> CompilerResult<Self> {
let mut res = CompilerResult::new("Creating an Instruction Segment");
res.assert(
relative_time <= MAX_20BIT,
format!("relative_time {} does not fit in 20 bits", relative_time),
);
res.with_value(Segment {
relative_time,
relay_instructions: Vec::new(),
sensor_instructions: Vec::new(),
})
}
fn append_header_to_buffer(&self, buffer: &mut BitWriter) {
buffer.append_tail(0u8, 3);
buffer.append_tail(0u8, 1);
buffer.append_tail(self.relative_time, 20);
buffer.append(self.sensor_instructions.len() as u16);
buffer.append(self.relay_instructions.len() as u16);
}
pub fn append_to_buffer(&self, buffer: &mut BitWriter) {
self.append_header_to_buffer(buffer);
for sensor_inst in self.sensor_instructions.iter() {
sensor_inst.append_to_buffer(buffer);
}
for relay_inst in self.relay_instructions.iter() {
relay_inst.append_to_buffer(buffer);
}
}
pub fn append_assembly_to_string(&self, lines: &mut String) -> CompilerResult<()> {
let mut res = CompilerResult::status_only("Segment disassembly");
lines.push_str(format!("segment +{}ms\n", self.relative_time).as_str());
for s_instruction in &self.sensor_instructions {
check!(res, s_instruction.append_assembly_to_string(lines));
}
for r_instruction in &self.relay_instructions {
check!(res, r_instruction.append_assembly_to_string(lines));
}
lines.push_str("\n");
res
}
pub fn len(&self) -> u32 {
let sensors_len = self.sensor_instructions.len() as u32 * SensorInstr::len();
let relay_len = self.relay_instructions.len() as u32 * RelayInstr::len();
Segment::HEADER_LEN + sensors_len + relay_len
}
pub fn check_segment_length(&self, config: &Config) -> CompilerResult<()> {
let mut res = CompilerResult::status_only("Check time length of segment instructions.");
let total_length = config.sensor_micros * self.sensor_instructions.len() as u32
+ config.relay_micros * self.relay_instructions.len() as u32;
if total_length > config.max_segment_length_micros {
res.error("This segment has too many sensor and relay instructions");
}
res
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DeviceAddress {
pub value: u16,
pub virtuality: u16,
}
impl fmt::Display for DeviceAddress {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.virtuality {
0 => write!(f, "P{}", self.value),
1 => write!(f, "V{}", self.value),
_ => Err(std::fmt::Error),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RelayInstr {
action: RelayAction,
device_address: DeviceAddress,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RelayAction {
Set,
Unset,
}
impl RelayInstr {
pub fn try_new(action: RelayAction, device_address: DeviceAddress) -> CompilerResult<Self> {
let mut res = CompilerResult::new("Creating a Relay Instruction");
res.assert(
device_address.value <= MAX_10BIT,
format!(
"device address {} does not fit in 10 bits",
device_address.value
),
);
res.with_value(RelayInstr {
action,
device_address,
})
}
pub fn append_to_buffer(&self, buffer: &mut BitWriter) {
let opcode = self.action.get_opcode();
buffer.append_tail(0b001u8, 3);
buffer.append_tail(opcode, 2);
buffer.append_tail(self.device_address.virtuality, 1);
buffer.append_tail(self.device_address.value, 10);
}
pub fn append_assembly_to_string(&self, lines: &mut String) -> CompilerResult<()> {
let res = CompilerResult::status_only("Relay Instruction disassembly");
lines.push_str(format!("{} {}\n", self.action, self.device_address).as_str());
res
}
pub fn len() -> u32 {
2
}
}
impl RelayAction {
pub fn get_opcode(&self) -> u8 {
match self {
RelayAction::Set => 0b00,
RelayAction::Unset => 0b01,
}
}
pub fn parse(text: &str) -> CompilerResult<Self> {
let mut res = CompilerResult::new("Parsing Relay Opcode");
match text {
"set" => res.with_value(RelayAction::Set),
"unset" => res.with_value(RelayAction::Unset),
_ => {
res.error(format!("Could not parse {} as \"set\" or \"unset\"", text));
res
}
}
}
}
impl fmt::Display for RelayAction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
RelayAction::Set => write!(f, "set"),
RelayAction::Unset => write!(f, "unset"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SensorInstr {
action: SensorAction,
abort_idx: u8,
device_address: DeviceAddress,
left_bound: u16,
right_bound: u16,
}
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SensorAction {
is_now_in,
start_check_in,
stop_check,
}
impl fmt::Display for SensorAction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
SensorAction::is_now_in => write!(f, "is_now_in"),
SensorAction::start_check_in => write!(f, "start_check_in"),
SensorAction::stop_check => write!(f, "stop_check"),
}
}
}
impl SensorInstr {
pub fn try_new_stop(device_address: DeviceAddress) -> CompilerResult<Self> {
let mut res = CompilerResult::new("Validating a Sensor Instruction");
res.assert(
device_address.value <= MAX_10BIT,
format!(
"device address {} does not fit in 10 bits",
device_address.value
),
);
res.with_value(SensorInstr {
action: SensorAction::stop_check,
abort_idx: 0,
device_address,
left_bound: 0,
right_bound: 0,
})
}
pub fn try_new(
action: SensorAction,
abort_idx: u8,
device_address: DeviceAddress,
left_bound: u16,
right_bound: u16,
) -> CompilerResult<Self> {
let mut res = CompilerResult::new("Validating a Sensor Instruction");
res.assert(
abort_idx <= 15,
format!("abort_idx {} is greater than the maximum (15)", abort_idx),
);
res.assert(
device_address.value <= MAX_10BIT,
format!(
"Device Address {} does not fit in 10 bits",
device_address.value
),
);
res.assert(
left_bound <= MAX_12BIT,
format!(
"left_bound {} is greater than {} and does not fit in 12 bits",
left_bound, MAX_12BIT
),
);
res.assert(
right_bound <= MAX_12BIT,
format!(
"right_bound {} is greater than {} and does not fit in 12 bits",
left_bound, MAX_12BIT
),
);
res.with_value(SensorInstr {
action,
abort_idx,
device_address,
left_bound,
right_bound,
})
}
pub fn append_to_buffer(&self, buffer: &mut BitWriter) {
let opcode = self.action.get_opcode();
buffer.append_tail(0b010u8, 3);
buffer.append_tail(opcode, 6);
buffer.append_tail(self.abort_idx, 4);
buffer.append_tail(self.device_address.virtuality, 1);
buffer.append_tail(self.device_address.value, 10);
buffer.append_tail(self.left_bound, 12);
buffer.append_tail(self.right_bound, 12);
}
pub fn append_assembly_to_string(&self, lines: &mut String) -> CompilerResult<()> {
let res = CompilerResult::status_only("Sensor Instruction disassembly");
if self.action.get_opcode() != 0 {
lines.push_str(
format!(
"{action} {device_address}, {left:#X}, {right:#X}, #{abort}\n",
action = self.action,
device_address = self.device_address,
left = self.left_bound,
right = self.right_bound,
abort = self.abort_idx
)
.as_str(),
);
} else {
lines.push_str(
format!(
"{action} {device_address}\n",
action = self.action,
device_address = self.device_address,
)
.as_str(),
);
}
res
}
pub fn len() -> u32 {
6
}
pub fn get_action(&self) -> SensorAction {
self.action
}
}
impl SensorAction {
fn get_opcode(&self) -> u8 {
match self {
SensorAction::stop_check => 0,
SensorAction::start_check_in => 1,
SensorAction::is_now_in => 0xFF,
}
}
pub fn parse(text: &str) -> CompilerResult<Self> {
let mut res = CompilerResult::new("Parse Sensor Opcode");
match text {
"is_now_in" => res.with_value(SensorAction::is_now_in),
"start_check_in" => res.with_value(SensorAction::start_check_in),
"stop_check" => res.with_value(SensorAction::stop_check),
_ => {
res.error(format!("Could not parse {} as a Sensor Action", text));
res
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::result::Status;
use crate::{bits::BitReader, bits::BitWriter};
use test_case::test_case;
#[test_case(RelayAction::Set, 0)]
#[test_case(RelayAction::Unset, 1)]
#[test_case(RelayAction::Unset, 143)]
fn virtuality_relay_test(relay_action: RelayAction, device_address_num: u16) {
let physical_relay = RelayInstr::try_new(
relay_action,
DeviceAddress {
value: device_address_num,
virtuality: 0,
},
)
.to_option()
.unwrap();
let mut physical_writer = BitWriter::new();
physical_relay.append_to_buffer(&mut physical_writer);
let mut physical_reader: BitReader = physical_writer.into();
physical_reader.read_u8(5);
assert_eq!(Some(0), physical_reader.read_u8(1));
let virtual_relay = RelayInstr::try_new(
relay_action,
DeviceAddress {
value: device_address_num,
virtuality: 1,
},
)
.to_option()
.unwrap();
let mut virtual_writer = BitWriter::new();
virtual_relay.append_to_buffer(&mut virtual_writer);
let mut virtual_reader: BitReader = virtual_writer.into();
virtual_reader.read_u8(5);
assert_eq!(Some(1), virtual_reader.read_u8(1));
}
#[test]
fn segment_relative_time_within_limits() {
assert_eq!(Segment::try_new(0).get_status(), Status::Passed);
assert_eq!(
Segment::try_new(u32::pow(2, 20) - 1).get_status(),
Status::Passed
);
assert_eq!(
Segment::try_new(u32::pow(2, 20)).get_status(),
Status::Failed
);
assert_eq!(Segment::try_new(u32::MAX).get_status(), Status::Failed);
}
#[test_case(RelayAction::Set, 0, 0)]
#[test_case(RelayAction::Unset, 1, 1)]
#[test_case(RelayAction::Set, 0, 2)]
#[test_case(RelayAction::Unset, 1, 3)]
fn relay_append_to_buffer(relay_action: RelayAction, opcode: u8, device_address: u16) {
let relay = RelayInstr::try_new(
relay_action,
DeviceAddress {
value: device_address,
virtuality: 0,
},
)
.to_option()
.unwrap();
let mut writer = BitWriter::new();
relay.append_to_buffer(&mut writer);
assert_eq!(writer.len() as u32, RelayInstr::len() * 8);
let mut reader: BitReader = writer.into();
assert_eq!(Some(0b001u8), reader.read_u8(3));
assert_eq!(Some(opcode), reader.read_u8(2));
assert_eq!(Some(0), reader.read_u16(1));
assert_eq!(Some(device_address), reader.read_u16(10));
}
#[test_case(660)]
#[test_case(483)]
#[test_case(1023)]
fn sensor_stop_append_to_buffer(device_address: u16) {
let sensor = SensorInstr::try_new_stop(DeviceAddress {
value: device_address,
virtuality: 0,
})
.to_option()
.unwrap();
let mut writer = BitWriter::new();
sensor.append_to_buffer(&mut writer);
assert_eq!(writer.len() as u32, SensorInstr::len() * 8);
let mut reader: BitReader = writer.into();
assert_eq!(Some(0b010u8), reader.read_u8(3));
assert_eq!(Some(0), reader.read_u8(6));
assert_eq!(Some(0), reader.read_u8(4));
assert_eq!(Some(0), reader.read_u16(1));
assert_eq!(Some(device_address), reader.read_u16(10));
assert_eq!(Some(0), reader.read_u16(12));
assert_eq!(Some(0), reader.read_u16(12));
}
#[test_case(SensorAction::is_now_in, 63, 5, 2, 0, 3000)]
#[test_case(SensorAction::start_check_in, 1, 10, 424, 200, 3210)]
fn sensor_check_append_to_buffer(
action: SensorAction,
opcode: u8,
abort_idx: u8,
device_address: u16,
left_bound: u16,
right_bound: u16,
) {
let sensor = SensorInstr::try_new(
action,
abort_idx,
DeviceAddress {
value: device_address,
virtuality: 0,
},
left_bound,
right_bound,
)
.to_option()
.unwrap();
let mut writer = BitWriter::new();
sensor.append_to_buffer(&mut writer);
assert_eq!(writer.len() as u32, SensorInstr::len() * 8);
let mut reader: BitReader = writer.into();
assert_eq!(Some(0b010u8), reader.read_u8(3));
assert_eq!(Some(opcode), reader.read_u8(6));
assert_eq!(Some(abort_idx), reader.read_u8(4));
assert_eq!(Some(0), reader.read_u16(1));
assert_eq!(Some(device_address), reader.read_u16(10));
assert_eq!(Some(left_bound), reader.read_u16(12));
assert_eq!(Some(right_bound), reader.read_u16(12));
}
#[test_case(RelayAction::Set, u16::pow(2, 10))]
#[test_case(RelayAction::Set, u16::pow(2, 12))]
fn test_invalid_device_address_values(relay_action: RelayAction, device_address: u16) {
let relay = RelayInstr::try_new(
relay_action,
DeviceAddress {
value: device_address,
virtuality: 0,
},
);
assert_eq!(relay.get_status(), Status::Failed);
}
#[test]
fn test_parse_valid_relay_set() {
let relay_value = RelayAction::parse("set");
let relay_opcode = RelayAction::Set.get_opcode();
assert_ne!(relay_value.get_status(), Status::Failed);
assert_eq!(relay_opcode, 0b00);
}
#[test]
fn test_parse_valid_relay_unset() {
let relay_value = RelayAction::parse("unset");
let relay_opcode = RelayAction::Unset.get_opcode();
assert_ne!(relay_value.get_status(), Status::Failed);
assert_eq!(relay_opcode, 0b01);
}
}