commit b53b37845dd263b28b62bb13493c1e9c9250fe14 Author: Rahix Date: Fri May 31 20:46:43 2024 +0200 Initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..c88dd34 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,325 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + +[[package]] +name = "anstyle-parse" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "bitflags" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "colorchoice" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + +[[package]] +name = "env_filter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "managed" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "process-image" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5af8362e66e347fba28d90c6569235402c7f1b7e41a4a67ed85ca29efade4b0" + +[[package]] +name = "profirust" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "815ad0b21c66b9772b138d0040b36bc63fc893b780658eabceba417f9ba0d1b8" +dependencies = [ + "bitflags 2.5.0", + "bitvec", + "libc", + "log", + "managed", + "rs485", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "rs485" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7491424ed2e9f9fa71ce0f6ec1df2aa6780297047803c4c468e5cb7a89ae835a" +dependencies = [ + "bitflags 0.9.1", + "libc", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "test-jig-software" +version = "0.1.0" +dependencies = [ + "env_logger", + "log", + "process-image", + "profirust", +] + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..d394513 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "test-jig-software" +version = "0.1.0" +edition = "2021" + +[dependencies] +env_logger = "0.11.3" +log = "0.4.21" +process-image = "0.2.0" +profirust = { version = "0.2.0", default-features = false, features = ["phy-linux", "std"] } diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..a10dfe8 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,208 @@ +use process_image as pi; + +use profirust::dp; +use profirust::fdl; +use profirust::phy; + +// Bus Parameters +const MASTER_ADDRESS: u8 = 3; +const BUS_DEVICE: &'static str = "/dev/ttyUSB0"; +const BAUDRATE: profirust::Baudrate = profirust::Baudrate::B500000; + +// Device Addresses +const RIO_ADDRESS: u8 = 42; + +// Parameters +const CYCLE_TIME: u64 = 5000; + +pi::process_image! { + struct Pii: 1024 { + pub bgb1: (X, 0, 0), // Left Limit Switch + pub bgb2: (X, 0, 1), // Right Limit Switch + pub bpb1: (X, 0, 2), // Pressure > 1.5 barg + pub want_run: (X, 0, 4), // Start/Stop Switch + + pub connection_alive: (X, 64, 0), + } +} + +pi::process_image! { + struct mut PiqMut: 1024 { + pub mbb1: (X, 0, 0), // Coil for turning cylinder to the left + pub mbb2: (X, 0, 1), // Coil for turning cylinder to the right + pub pza1_white: (X, 0, 2), // White status indicator light + pub pza1_blue: (X, 0, 3), // Blue status indicator light + pub pza1_orange: (X, 0, 4), // Orange status indicator light + pub pza1_alarm: (X, 0, 5), // Alarm buzzer + + pub timer: (L, 104), + pub fault_time: (L, 112), + pub counter: (L, 120), + pub running: (X, 200, 0), + pub faulted: (X, 200, 1), + pub state: (X, 200, 2), + } +} + +fn program(inp: Pii, mut out: PiqMut, now: profirust::time::Instant) { + let now = u64::try_from(now.total_millis()).unwrap(); + + let running = inp.want_run() && (*out.running() || inp.bpb1()); + let mut faulted = *out.faulted() && running; + *out.pza1_orange() = faulted; + *out.pza1_blue() = !running && !faulted; + *out.pza1_white() = running && !faulted; + *out.pza1_alarm() = faulted && (now - *out.fault_time()) < 1000; + + if running && !inp.connection_alive() { + faulted = true; + } + + if running && !inp.bpb1() { + faulted = true; + } + + if running && (inp.bgb1() | inp.bgb2()) { + faulted = true; + } + + if !running || faulted { + *out.timer() = now; + *out.counter() = 0; + } + if (now - *out.timer()) > CYCLE_TIME && *out.state() { + *out.timer() = now; + *out.state() = false; + *out.counter() += 1; + log::info!("Load cycles: {}", *out.counter()); + } + if (now - *out.timer()) > CYCLE_TIME && !*out.state() { + *out.timer() = now; + *out.state() = true; + *out.counter() += 1; + log::info!("Load cycles: {}", *out.counter()); + } + let mbb1 = *out.state() && running && !faulted; + let mbb2 = !*out.state() && running && !faulted; + + *out.mbb1() = mbb1; + *out.mbb2() = mbb2; + + if !*out.faulted() && faulted { + *out.fault_time() = now; + } + + *out.running() = running; + *out.faulted() = faulted; +} + +fn main() { + env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")) + .format_timestamp_micros() + .init(); + + let mut dp_master = dp::DpMaster::new(vec![]); + + // Options generated by `gsdtool` using "B751_P39.GSD" + let options = profirust::dp::PeripheralOptions { + // "WAGO 750-303 (FW: 01 ... 06) PRO" by "WAGO Kontakttechnik GmbH" + ident_number: 0xb751, + + // Global Parameters: + // - Register-Interface.............: is not used + // - RESET on terminalbus failure...: POWER ON RESET + // - Terminalbus diagnostics........: disabled + // - Evaluation of Clear_Data.......: enabled + // - Diagnostics of binary modules..: is not mapped into PI + // - Extended PI-Update.............: according to PI-Update + // - Evaluation of complex modules..: process data only + // - Data format....................: MOTOROLA + // - Process image update...........: free running + // - Response to PROFIBUS failure...: Output image is cleared + // - Response to terminalbus failure: PROFIBUS communication stops + // - Maximum length of diagnostics..: 16 Byte + // + // Selected Modules: + // [0] 8 Bit binary inputs + // [1] 8 Bit binary outputs + user_parameters: Some(&[ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x0a, + 0x00, + ]), + config: Some(&[0x10, 0x20]), + + // Set max_tsdr depending on baudrate and assert + // that a supported baudrate is used. + max_tsdr: match BAUDRATE { + profirust::Baudrate::B9600 => 60, + profirust::Baudrate::B19200 => 60, + profirust::Baudrate::B93750 => 60, + profirust::Baudrate::B187500 => 60, + profirust::Baudrate::B500000 => 100, + profirust::Baudrate::B1500000 => 150, + profirust::Baudrate::B3000000 => 250, + profirust::Baudrate::B6000000 => 350, + profirust::Baudrate::B12000000 => 550, + b => panic!( + "Peripheral \"WAGO 750-303 (FW: 01 ... 06) PRO\" does not support baudrate {b:?}!" + ), + }, + + fail_safe: false, + ..Default::default() + }; + let mut buffer_inputs = [0u8; 1]; + let mut buffer_outputs = [0u8; 1]; + let mut buffer_diagnostics = [0u8; 64]; + + let rio_handle = dp_master.add( + dp::Peripheral::new( + RIO_ADDRESS, + options, + &mut buffer_inputs, + &mut buffer_outputs, + ) + .with_diag_buffer(&mut buffer_diagnostics), + ); + + let mut fdl_master = fdl::FdlMaster::new( + fdl::ParametersBuilder::new(MASTER_ADDRESS, BAUDRATE) + // We use a rather large T_slot time because USB-RS485 converters + // can induce large delays at times. + .slot_bits(2500) + .watchdog_timeout(profirust::time::Duration::from_secs(2)) + .build_verified(&dp_master), + ); + // We must not poll() too often or to little. T_slot / 2 seems to be a good compromise. + let sleep_time: std::time::Duration = (fdl_master.parameters().slot_time() / 2).into(); + + let mut phy = phy::LinuxRs485Phy::new(BUS_DEVICE, fdl_master.parameters().baudrate); + + let start = profirust::time::Instant::now(); + let mut pii = [0u8; 1024]; + let mut piq = [0u8; 1024]; + + fdl_master.set_online(); + dp_master.enter_operate(); + loop { + let now = profirust::time::Instant::now(); + let events = fdl_master.poll(now, &mut phy, &mut dp_master); + + if events.cycle_completed { + let remoteio = dp_master.get_mut(rio_handle); + let rio_running = remoteio.is_running(); + let rio_pii = remoteio.pi_i(); + pii[0..rio_pii.len()].copy_from_slice(rio_pii); + + *pi::tag_mut!(&mut pii, X, 64, 0) = rio_running; + + program(Pii::from(&pii), PiqMut::from(&mut piq), now); + + let remoteio = dp_master.get_mut(rio_handle); + let rio_piq = remoteio.pi_q_mut(); + rio_piq.copy_from_slice(&piq[0..rio_piq.len()]); + } + + std::thread::sleep(sleep_time); + } +}