qitech-lib: Refactor out seperate library layer then can not just be used by us, but also by other people #1153
Replies: 7 comments 3 replies
-
|
(Second message from @Oshgnacknak ) we might actually do this. One idea could be a bus based architecture where different components (ethercat machines, other machines, rest api, or something entirely different), can emit or listen to events to communicate. The bus should not need to know in what context (async/threaded/realtime/etc.) bus components are being executed. All a component would need is a channel to send events and a channel to receive events from. A similar approach is currently already taken by the machines themselves. |
Beta Was this translation helpful? Give feedback.
-
|
From #910 By the latest refactor of our machine logic, the API server only needs two channels to send/receive data form/to the machines. Therefore, I propose moving our http API into a separate subcrate that will implizitly depend on tokio. Tokio should be banished from all other crates. It will run on a single thread that only does web server things. Compile time wise, tokio seems acceptable to me, but should not be mixed with other async runtimes. Optimally, the api would be a component, that you can include or not, depending on onces use case. |
Beta Was this translation helpful? Give feedback.
-
|
From #1036
#[repr(u16)]
enum MyEnum {
Qitech = 1,
Other = 2
}
const x: u16 = MyEnum::Qitech.into();As part of this, we should also redo the machine ids. Using Enums for things we control. But more importantly, don't use a nested struct - thats just a pain. Having exactly 6 bytes (2 vendor, 2 product, 2 serial) we could even consider using mac addresses. |
Beta Was this translation helpful? Give feedback.
-
|
@just-some-entity and I did some prototyping on how we can store the discovered hardware (by whatever discovery mechanism) and allow a Machine(Factory) take specific hardware from the hardware map. Importantly, we use Any to allow other library dependents to add their own hardware into the mix without changes on our part. use std::{any::{self, Any, TypeId}, collections::HashMap, fmt::Debug};
trait Hardware: Any + Debug {}
#[derive(Debug)]
struct PullerHardware {}
impl PullerHardware {
fn print(&self) {
println!("Pulling se beaches");
}
}
impl Hardware for PullerHardware {}
fn foo<H: Hardware, P: FnMut(&H) -> bool>(hardware: &mut HashMap<any::TypeId, Vec<Box<dyn Hardware>>>, mut pred: P) -> Option<H> {
if let Some(vec) = hardware.get_mut(&TypeId::of::<H>()) {
let index = vec.iter().position(|h| {
let any = h.as_ref() as &dyn Any;
let casted = any.downcast_ref().expect("Cast failed despite check!");
pred(casted)
});
if let Some(index) = index {
let box_any = vec.remove(index) as Box<dyn Any>;
let casted = box_any.downcast::<H>().expect("Cast failed despite check!");
return Some(*casted)
}
}
None
}
fn main() {
let mut hardware: HashMap<any::TypeId, Vec<Box<dyn Hardware>>> = HashMap::default();
println!("{:?}", hardware);
hardware.insert(TypeId::of::<PullerHardware>(), vec![Box::new(PullerHardware {})]);
if let Some(p) = foo(&mut hardware, |_: &PullerHardware| true) {
p.print();
}
println!("{:?}", hardware)
} |
Beta Was this translation helpful? Give feedback.
-
|
@kraemr Sorry for the confusion, I did not write down all our notes due to it being so late. I just saved a code snipped we got to compile, because it was not save in my Let me try to write down what we discussed from the bottom up! Our discussion was lead by the following questions:
The last question's answer will be incorporated into the others. Q: What does the winder do? Q: What should the winder not do? Q: Where would we put the devices we discover, so the winder can pick them up? Q: If the winder does not select its devices, what should? Note that the device constructors can fail, if hardware (like the EL1100) is not found. Alternatively, devices are also be stored in such a DeviceRegistry and have their own factory. The data structure from my previous post was an attempt to realize the Q: What mutates the Winder? Note here, that we only mutate the target state without locking - or in any other way, understanding how the change passed down to the hardware. We propose this pattern, to keep mutations as simple as possible. It should be the devices (only) responsibility to match make the actual hardware state match the target state as closely as possible. Importantly, the Mutation gets the machine, not the other way around. Here again, we have to pay attention to types. We want to allow someone to send us a Q: How does the winder publish its values? A: Who owns the Winder?
|
Beta Was this translation helpful? Give feedback.
-
|
@kraemr pls post a version how you would do it |
Beta Was this translation helpful? Give feedback.
-
|
What do you guys consider a plausible next step for QiTech Lib? The way I see it, we should push towards making an example for each new feature we add (or already have) into the lib. Importantly, the examples should show us, how easy to use our lib really is. The more strait forward the example, the better. Examples should become stable as soon as possible because in the end, they show others how our API can used. How we implement the examples, we can iterate over and over again. For cleaning up the EtherCAT a bit, if already started with this approach and I would like to make a few suggestions right of the bat.
As if now, I would like to get the following two example running, assuming the renames: fn main() {
let ethercat = qitech::ethercat::get_ethercat("eth0").expect("start");
ethercat.goto_state(EtherCATState::PreOp).expect("goto preop");
let subdevices = ethercat.get_subdevices().expect("subdevices");
println!("{:?}", subdevices);
}under the #[tokio::main]
async fn main() {
let ethercat = qitech::ethercat::get_ethercat("eth0").await.expect("start");
ethercat.goto_state(EtherCATState::PreOp).await.expect("goto preop");
let subdevices = ethercat.get_subdevices().await.expect("subdevices");
println!("{:?}", subdevices);
} |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
(Moved from @Oshgnacknak's issue: #1018)
In the long run, we want to allow third party to make us of out software stack. Currently they'd have to fork our codebase to do so. If instead, our code serves as a framework to built open, they could implement the machine trait themselves whilst only adding something like
qitech-libinto their dependencies. This way, wago for instance, could develop and test there minimal machines on their own.As an initial pitch, imagine each minimal machine lives in its own crate and only features such a simple main function.
Beta Was this translation helpful? Give feedback.
All reactions