/// Launches a subprocess and reads its output line by line. Useful to connect RoboPLC with 3rd /// party software which can not be embedded. use roboplc::controller::prelude::*; use roboplc::io::pipe::{self, Pipe}; use roboplc::{prelude::*, Error}; const SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(5); type Message = (); type Variables = (); #[derive(WorkerOpts)] #[worker_opts(cpu = 0, priority = 50, scheduling = "fifo", blocking = true)] struct Worker1 { reader: pipe::Reader, } impl Worker for Worker1 { fn run(&mut self, _context: &Context) -> WResult { loop { let line = self.reader.line()?; println!("Worker1: {}", line.trim_end()); } } } #[derive(WorkerOpts)] #[worker_opts(cpu = 0, priority = 50, scheduling = "fifo", blocking = true)] struct PipeRunner { pipe: Pipe, } impl Worker for PipeRunner { /// The piped subprocess needs to be run by a worker. The subprocess inherits the scheduling /// policy and priority of the worker. fn run(&mut self, _context: &Context) -> WResult { self.pipe.run(); Err(Error::failed("pipe exited").into()) } } fn main() -> Result<(), Box> { roboplc::setup_panic(); roboplc::configure_logger(roboplc::LevelFilter::Info); if !roboplc::is_production() { roboplc::thread_rt::set_simulated(); } roboplc::thread_rt::prealloc_heap(10_000_000)?; let mut controller = Controller::::new(); let (pipe, reader) = Pipe::new("/path/to/subprogram"); controller.spawn_worker(Worker1 { reader })?; controller.spawn_worker(PipeRunner { pipe })?; controller.register_signals(SHUTDOWN_TIMEOUT)?; controller.block(); Ok(()) }