use addr::*;
use message::*;
use arrayvec::ArrayVec;
pub enum MessageBuilderError {
MaximumDataReached,
}
struct Packet {
pid: u8,
cmd: u8,
data: [u8; 16],
data_size: usize,
}
impl Default for Packet {
fn default() -> Packet {
Packet {
pid: 0,
cmd: 0,
data: [0; 16],
data_size: 0,
}
}
}
impl Packet {
fn build(self) -> HerkulexMessage {
let mut result = HerkulexMessage::new();
let size: u8 = self.data_size as u8 + 7;
let mut checksum1: u8 = size ^ self.pid ^ self.cmd;
result.push(0xFF);
result.push(0xFF);
result.push(size);
result.push(self.pid);
result.push(self.cmd);
for i in 0..self.data_size {
result.push(self.data[i]);
checksum1 ^= self.data[i];
}
checksum1 &= 0xFE;
let checksum2: u8 = (!checksum1) & 0xFE;
result.insert(5, checksum1);
result.insert(6, checksum2);
result
}
fn push_data(&mut self, data: u8) {
self.data[self.data_size] = data;
self.data_size += 1;
}
}
pub type HerkulexMessage = ArrayVec<[u8; 128]>;
#[derive(Default)]
pub struct MessageBuilder {}
pub struct MessageBuilderCmd {
pid: u8,
}
pub struct MessageBuilderMem {
pid: u8,
addr: RegisterRequest,
size: u8,
}
pub struct MessageBuilderPositionSJOG {
pid: u8,
pos: SJogRequest,
}
pub struct MessageBuilderPositionIJOG {
pid: u8,
pos: IJogRequest,
}
pub struct MessageBuilderSpecial {
pid: u8,
kind: SpecialRequest,
}
impl MessageBuilder {
pub fn new() -> MessageBuilder {
MessageBuilder {}
}
pub fn new_with_id(id: u8) -> MessageBuilderCmd {
MessageBuilderCmd { pid: id }
}
pub fn id(self, id: u8) -> MessageBuilderCmd {
MessageBuilderCmd { pid: id }
}
}
impl MessageBuilderCmd {
pub fn read_ram<T: Into<Option<u8>>>(
self,
ram_addr: ReadableRamAddr,
size: T,
) -> MessageBuilderMem {
MessageBuilderMem {
pid: self.pid,
addr: RegisterRequest::RamRead(ram_addr),
size: match size.into() {
Some(s) => s,
None => ram_addr.bytes(),
},
}
}
pub fn write_ram(self, ram_addr: WritableRamAddr) -> MessageBuilderMem {
MessageBuilderMem {
pid: self.pid,
addr: RegisterRequest::RamWrite(ram_addr),
size: ram_addr.bytes(),
}
}
pub fn read_eep<T: Into<Option<u8>>>(
self,
eep_addr: ReadableEEPAddr,
size: T,
) -> MessageBuilderMem {
MessageBuilderMem {
pid: self.pid,
addr: RegisterRequest::EEPRead(eep_addr),
size: match size.into() {
Some(s) => s,
None => eep_addr.bytes(),
},
}
}
pub fn write_eep(self, eep_addr: WritableEEPAddr) -> MessageBuilderMem {
MessageBuilderMem {
pid: self.pid,
addr: RegisterRequest::EEPWrite(eep_addr),
size: eep_addr.bytes(),
}
}
pub fn reboot(self) -> MessageBuilderSpecial {
MessageBuilderSpecial {
pid: self.pid,
kind: SpecialRequest::Reboot,
}
}
pub fn rollback(self, flags: Rollback) -> MessageBuilderSpecial {
let kind = match flags {
Rollback::SkipId => SpecialRequest::Rollback {
skip_id: 1,
skip_baud: 0,
},
Rollback::SkipBaud => SpecialRequest::Rollback {
skip_id: 0,
skip_baud: 1,
},
Rollback::SkipBoth => SpecialRequest::Rollback {
skip_id: 1,
skip_baud: 1,
},
Rollback::SkipNone => SpecialRequest::Rollback {
skip_id: 0,
skip_baud: 0,
},
};
MessageBuilderSpecial {
pid: self.pid,
kind,
}
}
pub fn stat(self) -> MessageBuilderSpecial {
MessageBuilderSpecial {
pid: self.pid,
kind: SpecialRequest::Stat,
}
}
pub fn s_jog(
self,
playtime: u8,
mode: JogMode,
color: JogColor,
id: u8,
) -> MessageBuilderPositionSJOG {
let mut result = MessageBuilderPositionSJOG {
pid: self.pid,
pos: SJogRequest {
data: ArrayVec::new(),
playtime,
},
};
result.pos.data.push(SJogData::new(mode, color, id));
result
}
pub fn i_jog(
self,
playtime: u8,
mode: JogMode,
color: JogColor,
id: u8,
) -> MessageBuilderPositionIJOG {
let mut result = MessageBuilderPositionIJOG {
pid: self.pid,
pos: ArrayVec::new(),
};
result.pos.push(IJogData::new(mode, color, playtime, id));
result
}
}
impl MessageBuilderMem {
pub fn build(self) -> HerkulexMessage {
let pid = self.pid;
let cmd = match self.addr {
RegisterRequest::EEPWrite(_) => 0x01,
RegisterRequest::EEPRead(_) => 0x02,
RegisterRequest::RamWrite(_) => 0x03,
RegisterRequest::RamRead(_) => 0x04,
};
let mut packet = Packet::default();
packet.pid = pid;
packet.cmd = cmd;
match self.addr {
RegisterRequest::EEPWrite(addr) => {
packet.push_data(addr.into());
packet.push_data(self.size);
let (d1, opt_d2) = addr.associated_data();
packet.push_data(d1);
if let Some(d2) = opt_d2 {
packet.push_data(d2);
}
}
RegisterRequest::RamWrite(addr) => {
packet.push_data(addr.into());
packet.push_data(self.size);
let (d1, opt_d2) = addr.associated_data();
packet.push_data(d1);
if let Some(d2) = opt_d2 {
packet.push_data(d2);
}
}
RegisterRequest::EEPRead(addr) => {
packet.push_data(addr.into());
packet.push_data(self.size);
}
RegisterRequest::RamRead(addr) => {
packet.push_data(addr.into());
packet.push_data(self.size);
}
}
packet.build()
}
}
impl MessageBuilderSpecial {
pub fn build(self) -> HerkulexMessage {
let cmd = match self.kind {
SpecialRequest::Stat => 0x07,
SpecialRequest::Rollback { .. } => 0x08,
SpecialRequest::Reboot => 0x09,
};
let mut packet = Packet::default();
packet.pid = self.pid;
packet.cmd = cmd;
if let SpecialRequest::Rollback {
skip_id: id_bit,
skip_baud: baud_bit,
} = self.kind
{
packet.push_data(id_bit);
packet.push_data(baud_bit);
}
packet.build()
}
}
impl MessageBuilderPositionSJOG {
pub fn s_jog(
&mut self,
mode: JogMode,
color: JogColor,
id: u8,
) -> Result<(), MessageBuilderError> {
if self.pos.data.is_full() {
Err(MessageBuilderError::MaximumDataReached)
} else {
self.pos.data.push(SJogData::new(mode, color, id));
Ok(())
}
}
pub fn build(self) -> HerkulexMessage {
let mut packet = Packet::default();
packet.pid = self.pid;
packet.cmd = 6;
packet.push_data(self.pos.playtime);
for data in self.pos.data {
let d = data.mode.associated_data();
let lsb = (d) as u8;
let msb = (d >> 8) as u8;
packet.push_data(lsb);
packet.push_data(msb);
let mut set: u8 = 0;
match data.mode {
JogMode::Normal { .. } => set |= 0b0000_0000,
JogMode::Continuous { .. } => set |= 0b0000_0010,
}
match data.color {
JogColor::Blue => set |= 0b0000_1000,
JogColor::Green => set |= 0b0000_0100,
JogColor::Red => set |= 0b0001_0000,
}
packet.push_data(set);
packet.push_data(data.id);
}
packet.build()
}
}
impl MessageBuilderPositionIJOG {
pub fn s_jog(
&mut self,
mode: JogMode,
color: JogColor,
playtime: u8,
id: u8,
) -> Result<(), MessageBuilderError> {
if self.pos.is_full() {
Err(MessageBuilderError::MaximumDataReached)
} else {
self.pos.push(IJogData::new(mode, color, playtime, id));
Ok(())
}
}
pub fn build(self) -> HerkulexMessage {
let mut packet = Packet::default();
packet.pid = self.pid;
packet.cmd = 5;
for data in self.pos {
let d = data.mode.associated_data();
let lsb = (d) as u8;
let msb = (d >> 8) as u8;
packet.push_data(lsb);
packet.push_data(msb);
let mut set: u8 = 0;
match data.mode {
JogMode::Normal { .. } => set |= 0b0000_0000,
JogMode::Continuous { .. } => set |= 0b0000_0010,
}
match data.color {
JogColor::Blue => set |= 0b0000_1000,
JogColor::Green => set |= 0b0000_0100,
JogColor::Red => set |= 0b0001_0000,
}
packet.push_data(set);
packet.push_data(data.id);
packet.push_data(data.playtime);
}
packet.build()
}
}
#[cfg(test)]
mod test {
use addr::ReadableEEPAddr;
use builder::*;
#[test]
fn reboot_message() {
let message = MessageBuilder::new().id(0xFD).reboot().build();
assert_eq!(message.len(), 0x07);
assert_eq!(
message.as_slice(),
&[0xFF, 0xFF, 0x07, 0xFD, 0x09, 0xF2, 0x0C]
);
}
#[test]
fn ram_read_message() {
let message = MessageBuilder::new()
.id(0xFD)
.read_ram(ReadableRamAddr::LEDControl, None)
.build();
assert_eq!(message.len(), 0x09);
assert_eq!(
message.as_slice(),
&[0xFF, 0xFF, 0x09, 0xFD, 0x04, 0xC4, 0x3A, 0x35, 0x01]
)
}
#[test]
fn ram_write_message() {
let message = MessageBuilder::new()
.id(0xFD)
.write_ram(WritableRamAddr::LEDControl(0x01))
.build();
assert_eq!(
message.as_slice(),
&[0xFF, 0xFF, 0x0A, 0xFD, 0x03, 0xC0, 0x3E, 0x35, 0x01, 0x01]
);
let message = MessageBuilder::new()
.id(0xFD)
.write_ram(WritableRamAddr::TorqueControl(0x60))
.build();
assert_eq!(
message.as_slice(),
&[0xFF, 0xFF, 0x0A, 0xFD, 0x03, 0xA0, 0x5E, 0x34, 0x01, 0x60]
);
}
#[test]
fn rollback_message() {
let message = MessageBuilder::new()
.id(0xFD)
.rollback(Rollback::SkipBoth)
.build();
assert_eq!(
message.as_slice(),
&[0xFF, 0xFF, 0x09, 0xFD, 0x08, 0xFC, 0x02, 1, 1]
)
}
#[test]
fn stat_message() {
let message = MessageBuilder::new().id(0xFD).stat().build();
assert_eq!(
message.as_slice(),
&[0xFF, 0xFF, 0x07, 0xFD, 0x07, 0xFC, 0x02]
);
}
#[test]
fn eep_read() {
let message = MessageBuilder::new()
.id(0xFD)
.read_eep(ReadableEEPAddr::PositionKp, 4)
.build();
assert_eq!(
message.as_slice(),
&[0xFF, 0xFF, 0x09, 0xFD, 0x02, 0xEC, 0x12, 0x1E, 0x04]
);
}
#[test]
fn sjog_message() {
let message = MessageBuilder::new()
.id(0xFD)
.s_jog(60, JogMode::Normal { position: 512 }, JogColor::Green, 0xFD)
.build();
assert_eq!(
message.as_slice(),
&[0xFF, 0xFF, 0x0C, 0xFD, 0x06, 0x30, 0xCE, 0x3C, 0x00, 0x02, 0x04, 0xFD,]
);
let message = MessageBuilder::new()
.id(0xFD)
.s_jog(60, JogMode::Continuous { speed: 320, rotation: Rotation::CounterClockwise }, JogColor::Blue, 0xFD)
.build();
assert_eq!(
message.as_slice(),
&[0xFF, 0xFF, 0x0C, 0xFD, 0x06, 124, 130, 0x3C, 0x40, 0x01, 0x0A, 0xFD,]
);
let message = MessageBuilder::new()
.id(0xFD)
.s_jog(60, JogMode::Continuous { speed: 320, rotation: Rotation::Clockwise }, JogColor::Blue, 0xFD)
.build();
assert_eq!(
message.as_slice(),
&[0xFF, 0xFF, 0x0C, 0xFD, 0x06, 0x3C, 0xC2, 0x3C, 0x40, 0x41, 0x0A, 0xFD,]
)
}
#[test]
fn ijog_message() {
let message = MessageBuilder::new()
.id(0xFD)
.i_jog(60, JogMode::Normal { position: 512 }, JogColor::Green, 0xFD)
.build();
assert_eq!(
message.as_slice(),
&[0xFF, 0xFF, 0x0C, 0xFD, 0x05, 0x32, 0xCC, 0x00, 0x02, 0x04, 0xFD, 0x3C,]
);
let message = MessageBuilder::new()
.id(0xFD)
.i_jog(60, JogMode::Continuous { speed: 320, rotation: Rotation::CounterClockwise }, JogColor::Blue, 0xFD)
.build();
assert_eq!(
message.as_slice(),
&[0xFF, 0xFF, 0x0C, 0xFD, 0x05, 0x7E, 0x80, 0x40, 0x01, 0x0A, 0xFD, 0x3C,]
);
let message = MessageBuilder::new()
.id(0xFD)
.i_jog(60, JogMode::Continuous { speed: 320, rotation: Rotation::Clockwise }, JogColor::Blue, 0xFD)
.build();
assert_eq!(
message.as_slice(),
&[0xFF, 0xFF, 0x0C, 0xFD, 0x05, 0x3E, 0xC0, 0x40, 0x41, 0x0A, 0xFD, 0x3C,]
)
}
}