rsiot/components/cmp_linux_uart_master/
uart_comm.rs

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
140
141
142
143
144
145
146
147
148
149
150
151
152
use std::{thread::sleep, time::Duration};

use linux_embedded_hal::{
    gpio_cdev::{Chip, LineRequestFlags},
    serialport,
};
use tokio::sync::{broadcast, mpsc};
use tracing::{trace, warn};

use crate::{
    components_config::uart_general::{self, calculate_transmission_time, UartResponse},
    executor::CheckCapacity,
};

use super::bytes_per_second;

const READ_BUFFER_LEN: usize = 100;
const READ_BUFFER_CHUNK: usize = 32;

pub struct UartComm {
    pub ch_rx_devices_to_fieldbus: mpsc::Receiver<uart_general::UartRequest>,
    pub ch_tx_fieldbus_to_devices: broadcast::Sender<uart_general::UartResponse>,
    pub pin_rts: Option<u32>,

    pub timeout: Duration,
    pub port: &'static str,
    pub baudrate: uart_general::Baudrate,
    pub data_bits: uart_general::DataBits,
    pub parity: uart_general::Parity,
    pub stop_bits: uart_general::StopBits,
    pub gpio_chip: &'static str,
}

impl UartComm {
    pub fn spawn(mut self) -> super::Result<()> {
        let bytes_per_second = bytes_per_second(&self.baudrate, &self.data_bits, &self.stop_bits);

        let serial_port_builder = serialport::new("", 0)
            .path(self.port)
            .baud_rate(self.baudrate.into())
            .data_bits(self.data_bits.into())
            .parity(self.parity.into())
            .stop_bits(self.stop_bits.into())
            .timeout(self.timeout);
        let mut port = serial_port_builder
            .open()
            .map_err(|e| super::Error::OpenSerialPort(e.to_string()))?;

        // Настраиваем пин для сигнала RTS
        let pin_rts = match self.pin_rts {
            Some(pin_rts) => {
                let mut chip = Chip::new(self.gpio_chip)
                    .map_err(|e| super::Error::GpioSetup(e.to_string()))?;
                let pin_rts = chip
                    .get_line(pin_rts)
                    .map_err(|e| super::Error::GpioSetup(e.to_string()))?;
                let pin_rts = pin_rts
                    .request(LineRequestFlags::OUTPUT, 0, "uart-rts")
                    .map_err(|e| super::Error::GpioSetup(e.to_string()))?;
                Some(pin_rts)
            }
            None => None,
        };

        while let Some(request) = self.ch_rx_devices_to_fieldbus.blocking_recv() {
            // TODO
            self.ch_rx_devices_to_fieldbus
                .check_capacity(0.2, "uart_write");
            let address = request.address;
            let request_creation_time = request.request_creation_time;

            trace!("Send: {:?}", request);

            let write_buffer = request.to_write_buffer()?;

            // Устанавливаем пин RTS
            if let Some(pin_rts) = &pin_rts {
                pin_rts
                    .set_value(1)
                    .map_err(|e| super::Error::GpioPinSet(e.to_string()))?;
            }
            port.clear(serialport::ClearBuffer::All).unwrap();

            // Записываем буфер и ждем, пока данные отправятся
            port.write(&write_buffer)
                .map_err(|e| super::Error::UartWrite(e.to_string()))?;

            // Рассчитываем время передачи данных.
            // Если использовать port.flush(), то время ожидания будет больше примерно на 10 мс
            let transmission_time = calculate_transmission_time(
                bytes_per_second,
                write_buffer.len(),
                Duration::from_millis(0),
            );
            sleep(transmission_time);

            port.clear(serialport::ClearBuffer::All).unwrap();

            // Сбрасываем пин RTS
            if let Some(pin_rts) = &pin_rts {
                pin_rts
                    .set_value(0)
                    .map_err(|e| super::Error::GpioPinSet(e.to_string()))?;
            }

            let mut read_buffer = vec![0; READ_BUFFER_LEN];
            let mut read_buffer_offset: usize = 0;

            // Читаем данные из порта по частям
            let read_buffer = loop {
                let mut read_buffer_chunk = vec![0; READ_BUFFER_CHUNK];
                let port_read_result = port.read(&mut read_buffer_chunk);
                match port_read_result {
                    Ok(bytes_read) => {
                        // Перемещаем все данные в один буфер
                        (0..bytes_read).for_each(|i| {
                            read_buffer[i + read_buffer_offset] = read_buffer_chunk[i];
                        });
                        // Увеличиваем смещение на количество прочитанных байт
                        read_buffer_offset += bytes_read;
                    }
                    Err(err) => break Err(err),
                }
                // Пробуем востановить ответ. В ответе содержится CRC32. Если контрольная сумма
                // совпала - прекращаем читать из буфера, возвращаем ответ. Если не совпала,
                // то опять читаем из буфера
                let response = UartResponse::from_read_buffer(&mut read_buffer);
                if let Ok(read_buffer) = response {
                    break Ok(read_buffer);
                }
            };

            let mut response = match read_buffer {
                Ok(val) => val,
                Err(err) => {
                    let err = err.to_string();
                    warn!("UART read error: {}; address: {}", err, address);
                    // TODO - возможно, отправлять на устройство ответ, что есть проблема чтения
                    continue;
                }
            };

            response.set_request_creation_time(request_creation_time);

            self.ch_tx_fieldbus_to_devices
                .send(response)
                .map_err(|e| super::Error::TokioSyncBroadcastSend(e.to_string()))?;
        }

        Ok(())
    }
}