提交 cb7f106b authored 作者: Serhij S's avatar Serhij S

example

上级 4f8f3e2c
...@@ -9,6 +9,10 @@ your own risk! ...@@ -9,6 +9,10 @@ your own risk!
RoboPLC is a part of [EVA ICS](https://www.eva-ics.com/) industrial RoboPLC is a part of [EVA ICS](https://www.eva-ics.com/) industrial
automation platform. automation platform.
## Examples
Can be found at <https://github.com/eva-ics/roboplc/tree/main/examples>
## DataBuffer ## DataBuffer
[`buf::DataBuffer`] covers a typical data exchange pattern when data [`buf::DataBuffer`] covers a typical data exchange pattern when data
......
...@@ -3,7 +3,7 @@ use roboplc::{ ...@@ -3,7 +3,7 @@ use roboplc::{
time::interval, time::interval,
}; };
use roboplc::{io::modbus::prelude::*, prelude::*}; use roboplc::{io::modbus::prelude::*, prelude::*};
use tracing::{error, info}; use tracing::{error, info, warn};
const MODBUS_TIMEOUT: Duration = Duration::from_secs(1); const MODBUS_TIMEOUT: Duration = Duration::from_secs(1);
...@@ -35,7 +35,7 @@ struct Relay1 { ...@@ -35,7 +35,7 @@ struct Relay1 {
enum Message { enum Message {
#[data_delivery(single)] #[data_delivery(single)]
#[data_expires(TtlCell::is_expired)] #[data_expires(TtlCell::is_expired)]
SensorData(TtlCell<EnvironmentSensors>), EnvSensorData(TtlCell<EnvironmentSensors>),
} }
// First worker, to pull data from Modbus // First worker, to pull data from Modbus
...@@ -44,8 +44,13 @@ enum Message { ...@@ -44,8 +44,13 @@ enum Message {
struct ModbusPuller1 { struct ModbusPuller1 {
sensor_mapping: ModbusMapping, sensor_mapping: ModbusMapping,
} }
impl ModbusPuller1 { impl ModbusPuller1 {
fn create(modbus_client: &Client) -> Result<Self, Box<dyn std::error::Error>> { fn create(modbus_client: &Client) -> Result<Self, Box<dyn std::error::Error>> {
// creates a Modbus register mapping for a structure
// Modbus registers do not need to be parsed manually
// RoboPLC fieldbus mappings use [binrw](https://crates.io/crates/binrw) crate for
// automatic parsing
let sensor_mapping = ModbusMapping::create(modbus_client, 2, "h0", 2)?; let sensor_mapping = ModbusMapping::create(modbus_client, 2, "h0", 2)?;
Ok(Self { sensor_mapping }) Ok(Self { sensor_mapping })
} }
...@@ -55,15 +60,13 @@ impl ModbusPuller1 { ...@@ -55,15 +60,13 @@ impl ModbusPuller1 {
// controller context // controller context
impl Worker<Message, Variables> for ModbusPuller1 { impl Worker<Message, Variables> for ModbusPuller1 {
fn run(&mut self, context: &Context<Message, Variables>) { fn run(&mut self, context: &Context<Message, Variables>) {
let hc = context // this worker does not need to be subscribed to any events
.hub() let hub = context.hub();
.register(self.worker_name(), event_matches!(Message::SensorData(_)))
.unwrap();
for _ in interval(Duration::from_millis(500)) { for _ in interval(Duration::from_millis(500)) {
match self.sensor_mapping.read::<EnvironmentSensors>() { match self.sensor_mapping.read::<EnvironmentSensors>() {
Ok(v) => { Ok(v) => {
context.variables().lock().temperature = v.temperature; context.variables().lock().temperature = v.temperature;
hc.send(Message::SensorData(TtlCell::new_with_value( hub.send(Message::EnvSensorData(TtlCell::new_with_value(
ENV_DATA_TTL, ENV_DATA_TTL,
v, v,
))); )));
...@@ -92,13 +95,18 @@ impl ModbusRelays1 { ...@@ -92,13 +95,18 @@ impl ModbusRelays1 {
impl Worker<Message, Variables> for ModbusRelays1 { impl Worker<Message, Variables> for ModbusRelays1 {
fn run(&mut self, context: &Context<Message, Variables>) { fn run(&mut self, context: &Context<Message, Variables>) {
// this worker needs to be subscribed to EnvSensorData kind of events
let hc = context let hc = context
.hub() .hub()
.register(self.worker_name(), event_matches!(Message::SensorData(_))) .register(
self.worker_name(),
event_matches!(Message::EnvSensorData(_)),
)
.unwrap(); .unwrap();
while let Ok(msg) = hc.recv() { while let Ok(msg) = hc.recv() {
match msg { match msg {
Message::SensorData(mut cell) => { Message::EnvSensorData(mut cell) => {
// if data is not expired
if let Some(s) = cell.take() { if let Some(s) = cell.take() {
info!(worker=self.worker_name(), value=%s.temperature, info!(worker=self.worker_name(), value=%s.temperature,
elapsed=?cell.set_at().elapsed()); elapsed=?cell.set_at().elapsed());
...@@ -114,6 +122,10 @@ impl Worker<Message, Variables> for ModbusRelays1 { ...@@ -114,6 +122,10 @@ impl Worker<Message, Variables> for ModbusRelays1 {
error!(worker=self.worker_name(), err=%e, "Modbus send error"); error!(worker=self.worker_name(), err=%e, "Modbus send error");
} }
} }
} else {
// Should never happen as policy channels do not deliver expired data if
// the policy specifies an expiration condition
warn!(worker=self.worker_name(), elapsed=?cell.set_at().elapsed(), "Expired data");
} }
} }
} }
...@@ -123,16 +135,23 @@ impl Worker<Message, Variables> for ModbusRelays1 { ...@@ -123,16 +135,23 @@ impl Worker<Message, Variables> for ModbusRelays1 {
// Main function, to start the controller and workers // Main function, to start the controller and workers
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
// sets the simulated mode for the real-time module, do not set any thread real-time parameters
roboplc::thread_rt::set_simulated(); roboplc::thread_rt::set_simulated();
// initializes a debug logger
env_logger::builder() env_logger::builder()
.filter_level(log::LevelFilter::Info) .filter_level(log::LevelFilter::Info)
.init(); .init();
let modbus_tcp_client = tcp::connect("10.90.34.111:5505", MODBUS_TIMEOUT)?; // creates a controller instance
let mut controller: Controller<Message, Variables> = Controller::new(); let mut controller: Controller<Message, Variables> = Controller::new();
// creates a reliable auto-reconnecting shared TCP port connection
let modbus_tcp_client = tcp::connect("10.90.34.111:5505", MODBUS_TIMEOUT)?;
// creates the first worker and spawns it
let worker = ModbusPuller1::create(&modbus_tcp_client)?; let worker = ModbusPuller1::create(&modbus_tcp_client)?;
controller.spawn_worker(worker)?; controller.spawn_worker(worker)?;
// creates the second worker and spawns it
let worker = ModbusRelays1::create(&modbus_tcp_client)?; let worker = ModbusRelays1::create(&modbus_tcp_client)?;
controller.spawn_worker(worker)?; controller.spawn_worker(worker)?;
// block the main thread until the controller is in the online state
controller.block_while_online(); controller.block_while_online();
Ok(()) Ok(())
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论