I am new to Rust and used to learn programming by writing code for my own little project. I tried to port C++ code to Rust but have not find an appropriate way.
I have different instruments wtih serial port communication. In C++, I created a base class Device which is inherited from CWinThread. The base class Device implemented serial port communication. i.e. Once the object is created, it starts a thread loop.
while (bLoop)
{
// Always check if the port is still open
if(bOpen != IsPortOpen() || !bValid)
{
bOpen = IsPortOpen();
m_ConnStatus = bOpen? EConnStatusPortOpen : EConnStatusNotConnected;
NotifyConnectionUpdated();
if(!bOpen)
bValid = false;
}
int timeout = m_UpdateConnInterval;
if (bOpen && bValid)
timeout = m_UpdateDevInterval;
switch(WaitForMultipleObjectsEx(3, hEventList, false, timeout, true))
{
case WAIT_OBJECT_0:
// Terminate thread
bLoop = false;
break;
case WAIT_OBJECT_0 + 1:
// Port change, reopen
bOpen = OpenPort();
m_ConnStatus = bOpen? EConnStatusPortOpen : EConnStatusNotConnected;
NotifyConnectionUpdated();
bValid = false;
break;
case WAIT_OBJECT_0 + 2:
// Send command and wait for response
{
CDeviceCommand cmd = ReadFromSendQueue();
if (!cmd.m_Cmd.IsEmpty())
{
if (bOpen)
cmd.m_Resp = SendCommand(cmd.m_Cmd, cmd.m_Timeout);
else
cmd.m_Resp = _T("ERROR: port not open!");
if (cmd.HasCallback())
(*cmd.m_Callback)(cmd);
else
AddToReceiveQueue(cmd);
}
}
break;
case WAIT_IO_COMPLETION:
// This happens when IO is finished
break;
default:
if (!bOpen)
{
// Port not open: try to open
bOpen = OpenPort();
m_ConnStatus = bOpen? EConnStatusPortOpen : EConnStatusNotConnected;
NotifyConnectionUpdated();
bValid = false;
}
else if (bValid)
{
// Port open, connection valid: update device
if (UpdateDevice())
{
bValid = true;
m_Retries = 0;
}
else if (m_Retries++ == 5)
{
// After 5 retries, disconnect is signalled
bValid = false;
}
NotifyDeviceUpdated();
}
else
{
// Port open, connection not valid: update connection
bool newValid = UpdateConnection();
if (bValid != newValid)
{
bValid = newValid;
m_ConnStatus = bValid? EConnStatusConnected : EConnStatusPortOpen;
NotifyConnectionUpdated();
}
}
break;
}
}
The thread continously check and update the serial port connection; if the port is closed or the port name is changed, try to open the port; if there is command in the command queue, send the command to the port and put response to response queue. Call virtual methods to actually handle connection status and unpdate the status in UI.
virtual void GetPortSettings(SPortSettings *ps) const {}
virtual bool UpdateDevice() { return false; }
virtual void NotifyDeviceUpdated() const {}
virtual bool UpdateConnection() { return false; }
virtual void NotifyConnectionUpdated() const {}
Each kind of instrument has its own class inherited the base class Device and actually implemented the virtual methods defined in the base class Device and their own method for instrument control.
In Rust, I ended up very ugly implementation with struct and the code is not reusable as C++ since there is no inheritance in Rust. Here is my rust code, and I don't like it at all. How shall I implement such easy feature in C++ in the Rust? Look forward to your guidance.
#[derive(Debug, Clone)]
pub struct Device {
pub terminator: String,
pub name: Arc<RwLock<String>>,
pub baud_rate: u32,
pub data_bits: DataBits,
pub flow_control: FlowControl,
pub parity: Parity,
pub stop_bits: StopBits,
pub timeout: Duration,
port_changed: Arc<RwLock<bool>>,
connected_lock: Arc<RwLock<bool>>,
cmd_tx: Sender<String>,
resp_rx: Arc<Mutex<Receiver<Result<Packet>>>>,
}
impl Device{
pub fn new(terminator:&str, ...)>) -> Self{
...
let _thread_handle = thread::spawn(move|| {
serial_thread(
port_name_clone,
terminator_t,
....
cmd_rx,
resp_tx,
port_changed_clone,
connected_lock_clone,
);
});
}
}
fn serial_thread(
port_name: Arc<RwLock<String>>,
terminator: String,
settings: PortSettings,
cmd_rx: Receiver<String>,
resp_tx: Sender<Result<Packet>>,
port_changed:Arc<RwLock<bool>>,
connected_lock:Arc<RwLock<bool>>,
firmware_data: Arc<Mutex<Vec<u8>>>,
progress_tx: Sender<(String, u32, Option<String>)>
) {
loop: ...
}