main
rahix 1 year ago
commit 767bbb1c56

1
.gitignore vendored

@ -0,0 +1 @@
/target

316
Cargo.lock generated

@ -0,0 +1,316 @@
# 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.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]]
name = "anstyle-parse"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
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.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[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.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]]
name = "env_filter"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab"
dependencies = [
"log",
"regex",
]
[[package]]
name = "env_logger"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d"
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.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "libc"
version = "0.2.162"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
[[package]]
name = "log"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[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.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "profirust"
version = "0.2.0"
dependencies = [
"bitflags 2.6.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.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[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 = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "valve-tester"
version = "0.1.0"
dependencies = [
"env_logger",
"log",
"profirust",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
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.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "wyz"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
dependencies = [
"tap",
]

@ -0,0 +1,9 @@
[package]
name = "valve-tester"
version = "0.1.0"
edition = "2021"
[dependencies]
env_logger = "0.11.5"
log = "0.4.22"
profirust = { version = "0.2.0", path = "../profirust", default-features = false, features = ["phy-linux", "std"] }

@ -0,0 +1,88 @@
use profirust::dp;
use profirust::fdl;
use profirust::phy;
mod tester_io;
mod valve_terminal;
// Bus Parameters
const MASTER_ADDRESS: u8 = 1;
const BUS_DEVICE: &'static str = "/dev/ttyUSB0";
const BAUDRATE: profirust::Baudrate = profirust::Baudrate::B500000;
fn main() -> ! {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info"))
.format_timestamp_micros()
.init();
log::info!("Ventilinsel Teststation");
let mut dp_scanner = dp::scan::DpScanner::new();
let mut dp_master = dp::DpMaster::new(vec![]);
let mut tester_io = tester_io::TesterIo::new(&mut dp_master, BAUDRATE);
let mut valve_terminal = valve_terminal::ValveTerminal::new(&mut dp_master, BAUDRATE);
let mut fdl = fdl::FdlActiveStation::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)
// .token_rotation_bits(512)
.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.parameters().slot_time() / 2).into();
log::info!("Connecting to the bus...");
let mut phy = phy::LinuxRs485Phy::new(BUS_DEVICE, fdl.parameters().baudrate);
fdl.set_online();
dp_master.enter_operate();
loop {
fdl.poll_multi(
profirust::time::Instant::now(),
&mut phy,
&mut [&mut dp_master, &mut dp_scanner],
);
let dp_events = dp_master.take_last_events();
tester_io.update(&mut dp_master, &dp_events);
valve_terminal.update(&mut dp_master, &dp_events);
// Ventil Zustände von den Eingängen auf die Ventilausgänge übertragen.
valve_terminal.update_valve_states(tester_io.valve_states());
let scanner_event = dp_scanner.take_last_event();
match scanner_event {
Some(dp::scan::DpScanEvent::PeripheralFound(desc)) => {
if valve_terminal.check_ident(desc.ident) {
// Wenn die Ventilinsel nicht erreichbar ist, wird eine neue Adresse vom "DP Scanner"
// übernommen.
if !valve_terminal.is_running(&mut dp_master) {
log::info!(
"Ventilinsel an Adresse #{} gefunden. Parametrieren...",
desc.address
);
valve_terminal.init_at_address(desc.address, &mut dp_master);
} else {
// Alte Ventilinsel ist noch aktiv??
log::warn!(
"Neue Ventilinsel an Adresse #{} gefunden, obwohl alte noch aktiv. Wird ignoriert.",
desc.address
);
}
} else {
log::info!("Andere Station an Adresse #{} gefunden:", desc.address);
log::info!(" - Ident: 0x{:04x}", desc.ident);
}
}
Some(dp::scan::DpScanEvent::PeripheralLost(address)) => {
log::info!("Station an Adresse #{} verloren.", address);
}
_ => (),
}
std::thread::sleep(sleep_time);
}
}

@ -0,0 +1,106 @@
//! E/A für die Teststation
//!
//! Als E/A ist hier eine Siemens ET200B 8DI 8DO vorgesehen. Natürlich lässt sich dies auf jede
//! andere E/A Station umbauen. Folgende Geräte sind angeschlossen:
//!
//! ## Eingänge
//! - 8x Kippschalter zum Schalten der einzelnen Ventile
//!
//! ## Ausgänge
//! - Leuchtmelder "Test-Station bereit"
//! - Leuchtmelder "Ventilinsel erkannt"
//! - Leuchtmelder "Fehler"
use profirust::dp;
const ET200B_ADDRESS: profirust::Address = 13;
pub enum TesterStatus {
/// Test-Station bereit, keine Ventilinsel verbunden
Ready,
/// Ventilinsel erkannt und verbunden
ValveTerminalConnected,
/// Fehler
Error,
}
pub struct TesterIo {
peripheral: dp::PeripheralHandle,
valve_states: u8,
tester_status: TesterStatus,
}
impl TesterIo {
pub fn new(dp_master: &mut dp::DpMaster, baudrate: profirust::Baudrate) -> Self {
// Options generated by `gsdtool` using "et200b-8di8do.gsd"
let options = profirust::dp::PeripheralOptions {
// "B-8DI/8DO DP " by "SIEMENS "
ident_number: 0x000b,
// Global Parameters:
// (none)
//
// Selected Modules:
// [0] 1 Byte Out, 1 Byte In
user_parameters: Some(&[0x00, 0x00, 0x00, 0x00, 0x00]),
config: Some(&[0x20, 0x10]),
// 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::B45450 => 250,
profirust::Baudrate::B93750 => 60,
profirust::Baudrate::B187500 => 60,
profirust::Baudrate::B500000 => 100,
profirust::Baudrate::B1500000 => 150,
profirust::Baudrate::B3000000 => 250,
profirust::Baudrate::B6000000 => 450,
profirust::Baudrate::B12000000 => 800,
b => panic!(
"Peripheral \"B-8DI/8DO DP \" does not support baudrate {b:?}!"
),
},
fail_safe: false,
..Default::default()
};
let buffer_inputs = vec![0u8; 1].leak();
let buffer_outputs = vec![0u8; 1].leak();
let buffer_diagnostics = vec![0u8; 13].leak();
let handle = dp_master.add(
dp::Peripheral::new(ET200B_ADDRESS, options, buffer_inputs, buffer_outputs)
.with_diag_buffer(buffer_diagnostics),
);
Self {
peripheral: handle,
valve_states: 0,
tester_status: TesterStatus::Ready,
}
}
pub fn valve_states(&self) -> u8 {
self.valve_states
}
pub fn update(&mut self, dp_master: &mut dp::DpMaster, events: &dp::DpEvents) {
let peripheral = dp_master.get_mut(self.peripheral);
if peripheral.is_running() && events.cycle_completed {
// Die 8 Eingänge der ET200B werden auf die 8 Bits für die Ventile gemapped.
self.valve_states = peripheral.pi_i()[0];
// Ausgänge:
// - Bit 0: Leuchtmelder "Test-Station bereit"
// - Bit 1: Leuchtmelder "Ventilinsel erkannt"
// - Bit 2: Leuchtmelder "Fehler"
let outputs = match self.tester_status {
TesterStatus::Ready => 0x01,
TesterStatus::ValveTerminalConnected => 0x02,
TesterStatus::Error => 0x04,
};
peripheral.pi_q_mut()[0] = outputs;
}
}
}

@ -0,0 +1,106 @@
//! Steuerung für die Ventilinseln
//!
//! Verbindet sich dynamisch mit einer Ventilinsel, die am Bus unter beliebiger Adresse gefunden
//! wurde. Es wird eine bekannte Parametrierung vorausgesetzt.
//!
//! Zur demo ist hier eine CPV14 mit CPV14-GE-DI02-8 Anschaltung vorgesehen.
use profirust::dp;
// "Festo CPV-DI02" by "Festo AG&Co."
const VALVE_TERMINAL_IDENT_NUMBER: u16 = 0x0a35;
pub struct ValveTerminal {
peripheral: dp::PeripheralHandle,
address: profirust::Address,
valve_states: u8,
}
impl ValveTerminal {
pub fn new(dp_master: &mut dp::DpMaster, baudrate: profirust::Baudrate) -> Self {
// Als erstes wird die Ventilinsel mit einer Standard-Adresse parametriert. Die Adresse
// wird dann später geändert, sobald ein tatsächliches Gerät auf dem Bus gefunden wurde.
let default_address = 42;
// Options generated by `gsdtool` using "cpv_0A35.gse"
let options = profirust::dp::PeripheralOptions {
// "Festo CPV-DI02" by "Festo AG&Co."
ident_number: 0x0a35,
// Global Parameters:
// (none)
//
// Selected Modules:
// [0] Base module:16DO
// - monitor U-Load.........: active
// - monitor failure CP-Line: inactive
// - fault mode.............: reset outputs
// - fault state 2 byte.....: 0
user_parameters: Some(&[0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00]),
config: Some(&[0x21]),
// Set max_tsdr depending on baudrate and assert
// that a supported baudrate is used.
max_tsdr: match baudrate {
profirust::Baudrate::B9600 => 20,
profirust::Baudrate::B19200 => 20,
profirust::Baudrate::B93750 => 20,
profirust::Baudrate::B187500 => 20,
profirust::Baudrate::B500000 => 20,
profirust::Baudrate::B1500000 => 25,
profirust::Baudrate::B3000000 => 50,
profirust::Baudrate::B6000000 => 100,
profirust::Baudrate::B12000000 => 200,
b => panic!("Peripheral \"Festo CPV-DI02\" does not support baudrate {b:?}!"),
},
fail_safe: false,
..Default::default()
};
let mut buffer_inputs = vec![0u8; 0].leak();
let mut buffer_outputs = vec![0u8; 2].leak();
let mut buffer_diagnostics = vec![0u8; 16].leak();
let handle = dp_master.add(
dp::Peripheral::new(default_address, options, buffer_inputs, buffer_outputs)
.with_diag_buffer(buffer_diagnostics),
);
Self {
peripheral: handle,
address: default_address,
valve_states: 0,
}
}
/// Überprüfen ob ein gefundenes Gerät eine Ventilinsel von unserem Typ ist
pub fn check_ident(&self, ident: u16) -> bool {
ident == VALVE_TERMINAL_IDENT_NUMBER
}
/// Gibt `true` zurück wenn die Ventilinsel an der aktuell parametrierten Adresse erfolgreich
/// kommuniziert.
pub fn is_running(&self, dp_master: &mut dp::DpMaster) -> bool {
let peripheral = dp_master.get_mut(self.peripheral);
peripheral.is_running()
}
pub fn init_at_address(&mut self, address: profirust::Address, dp_master: &mut dp::DpMaster) {
if address != self.address {
let peripheral = dp_master.get_mut(self.peripheral);
peripheral.reset_address(address);
peripheral.pi_q_mut().fill(0u8);
self.valve_states = 0;
}
}
pub fn update_valve_states(&mut self, valve_states: u8) {
self.valve_states = valve_states;
}
pub fn update(&mut self, dp_master: &mut dp::DpMaster, events: &dp::DpEvents) {
let peripheral = dp_master.get_mut(self.peripheral);
if peripheral.is_running() && events.cycle_completed {
peripheral.pi_q_mut()[0] = self.valve_states;
}
}
}
Loading…
Cancel
Save