1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use std::io::{self, Read, Write};
use std::time::{Instant, Duration};

use crate::Optolink;

use super::Protocol;

const RESET: u8 = 0x04;
const SYNC: u8  = 0x05;

#[derive(Debug)]
pub struct Kw2;

impl Kw2 {
  fn sync(o: &mut Optolink) -> Result<(), std::io::Error> {
    log::trace!("Kw2::sync(…)");

    let mut buf = [0xff];

    let start = Instant::now();

    // Reset the Optolink connection to get a faster SYNC (`0x05`).
    Self::negotiate(o)?;

    loop {
      log::trace!("Kw2::sync(…) loop");

      if o.read_exact(&mut buf).is_ok() && buf == [SYNC] {
        o.purge()?;
        return Ok(())
      }

      let stop = Instant::now();

      if (stop - start) > Optolink::TIMEOUT {
        break
      }
    }

    Err(io::Error::new(io::ErrorKind::TimedOut, "sync timed out"))
  }
}

impl Protocol for Kw2 {
  fn negotiate(o: &mut Optolink) -> Result<(), io::Error> {
    log::trace!("Kw2::negotiate(…)");

    o.purge()?;
    o.write_all(&[RESET])?;
    o.flush()?;

    Ok(())
  }

  fn get(o: &mut Optolink, addr: &[u8], buf: &mut [u8]) -> Result<(), io::Error> {
    log::trace!("Kw2::get(…)");

    let mut vec = Vec::new();
    vec.extend(&[0x01, 0xf7]);
    vec.extend(addr);
    vec.extend(&[buf.len() as u8]);

    let start = Instant::now();

    Self::sync(o)?;

    loop {
      log::trace!("Kw2::get(…) loop");

      o.write_all(&vec)?;
      o.flush()?;

      let read_start = Instant::now();

      o.read_exact(buf)?;

      let stop = Instant::now();

      // Retry if the response contains `SYNC` (`0x05`),
      // since these could be synchronization bytes.
      if buf.iter().any(|byte| *byte == SYNC) {
        let read_time = stop - read_start;

        log::debug!("Kw2::get(…) buf = {}", buf.iter().map(|b| format!("{:02X}", b)).collect::<Vec<String>>().join(" "));
        log::debug!("Kw2::get(…) read_time = {:?}", read_time);

        // Return `Ok` if the response was received in a short amount of time,
        // since then they most likely are not synchronization bytes.
        if read_time < Duration::from_millis(500 * buf.len() as u64) {
          return Ok(())
        }

        o.purge()?;
      } else {
        return Ok(())
      }

      if (stop - start) > Optolink::TIMEOUT {
        break
      }
    }

    Err(io::Error::new(io::ErrorKind::TimedOut, "get timed out"))
  }

  fn set(o: &mut Optolink, addr: &[u8], value: &[u8]) -> Result<(), io::Error> {
    log::trace!("Kw2::set(…)");

    let mut vec = Vec::new();
    vec.extend(&[0x01, 0xf4]);
    vec.extend(addr);
    vec.extend(&[value.len() as u8]);
    vec.extend(value);

    let start = Instant::now();

    Self::sync(o)?;

    loop {
      o.write_all(&vec)?;
      o.flush()?;

      let mut buf = [0xff];
      o.read_exact(&mut buf)?;

      let stop = Instant::now();

      if buf == [0x00] {
        return Ok(())
      }

      if (stop - start) > Optolink::TIMEOUT {
        break
      }
    }

    Err(io::Error::new(io::ErrorKind::TimedOut, "set timed out"))
  }
}