06-07-2013 10:08 AM
@crcarlin wrote:
One thing I can clarify is this: my users very honestly don't always know the capabilities of the device they're looking to use. There's nothing I can do about that. The users have neither time or interest to learn the devices inside and out or to learn LVOOP to any depth (that's why they hire folks like me!). They WILL attempt GetTemperature on a thing that doesn't read temperature, so the question is how to deal with that with a minimum of (quite literal) explosions.
This paragraph is ridiculous on two points.
1. If they have a device and they don't know it's capabilities, they shouldn't be using it. Anyone who uses a device should have at least a basic understanding of what it can and can't do before they even touch it. Especially if what they are doing could lead to "quite literal explosions".
2. If the users don't want to learn LVOOP to any depth and are hiring folks like you, then why are they dictating how you should be using LVOOP?
06-07-2013 10:33 AM
Intaris, I'm rereading this thread because there seem to be some confusion with teminologies.
In the mean time (even though it's a bit beside the point):
@RavensFan wrote:
This paragraph is ridiculous on two points.
1. If they have a device and they don't know it's capabilities, they shouldn't be using it. Anyone who uses a device should have at least a basic understanding of what it can and can't do before they even touch it. Especially if what they are doing could lead to "quite literal explosions".
2. If the users don't want to learn LVOOP to any depth and are hiring folks like you, then why are they dictating how you should be using LVOOP?
Property written, the software protects people from themselves. That's the point. Write it wrong--without proper synchronization and care about machine state--and the users can (and do) blow things up. That's part of why design is so crucial here.
But the reality is, if you make the user jump through too many hoops to do things right, he won't. He'll just go around the protections to access the things himself, at which point all of the design is worthelss. Sure we can worry about blame after damage is done, but then it's too late. We can't engineer our users.
Dropping an instrument-representing object on the diagram and calling methods on it to make the instrument do things, they get. They find it a little scary, but they accept it. But when you start talking about "getting" modules representing behaviors and modules that don't have analogy in the real world, that's the bridge too far.... and I agree with them!
06-07-2013 10:48 AM
@crcarlin wrote:
Property written, the software protects people from themselves. That's the point. Write it wrong--without proper synchronization and care about machine state--and the users can (and do) blow things up. That's part of why design is so crucial here.
I disagree. I would never trust software to protect people from themselves. I would never completely trust hardware to protect people from themselves. People need to have proper training and always be willing to follow it. Even that is never 100% to guarantee people are protected. If someone is trying to use a device in a method it was not designed to be used, then obviously those people have not been properly changed and shouldn't be using that device at all.
I feel for you. I think you are in a very tough programming environment. It sounds like you are programming for a bunch of people who know just enough to be dangerous, and not enough to be able to properly minimize the risks of those dangers.
06-07-2013 01:52 PM
I feel for your users 😉
Oversimplification is dangerous too - if you convince them that your software prevents them from making errors, they're less likely to think about what they're doing. I'm not sure you're doing yourself or your users any favors by convincing them that forking wires is bad, and whatever other rules you've given them. There's nothing wrong with forking wires - how else would we use critical LabVIEW concepts like queues? (OK, yes, you can name a queue, but that leads to a whole different set of problems.)
Jumping topics a bit. I understand what you want - a way to drop a constant for any device on the block diagram, wire it to make a certain type of measurement, and have the compiler generate an error if that device can't take the measurement. What I don't understand is how your users don't have to know this already. Don't they need to connect the physical device into the real system somewhere? What happens when they change the real device, but not the software one? Isn't that essentially a run-time error too?
What if you have a device that can take 2 of the same measurement (ie, instead of measuring temperature and pressure, it can measure two different temperatures)? Seems like it might be better to solve that problem, which would be easily generalized to the case where it takes two different measurements.
This is similar to what other people have suggested, but what if you have a Thermometer device, and a Pressure device, etc, and when you create those devices you wire in (a reference to) the real instrument that will take that measurement? I can't come up with a way that this will cause a compiler error, but at least it can cause a run-time error during setup before any measurement is taken saying "I can't make that measurement." It avoids needing to implement all the empty methods. You can still swap instruments easily. Thermometer and Pressure can both inherit from a common class, so you can put them in an array together if you want. If your instrument does measure multiple values, you do branch that instrument wire, creating both a Temperature and Pressure device for it - but to me, that's pretty intuitive.
06-07-2013 03:57 PM
Just from experience I find that when my users fork wires unnecessarily it hurts them later on. It leads to completely unworkably messy block diagrams and spoils some of the flow concepts.
We also deal in large arrays of complex clusters, and I believe forking those leads to performance issues (but I don't exactly remember).
Most relevant to this, though, there are incidents that arise from devices being accessed from multiple parts of the block diagram because the wire acting as the handle had been forked. It has worked well to point to a wire and say, THIS is your handle; don't fork it. The conceptualization of object=instrument works very well for them, so that rule works.
As for changing the devices and the software, so far it's never been a problem. Changing a device is a big enough deal that someone asks me about the software so I can tell them to swap objects. On my list of "nice things to have" is a check during initialization to ensure the expected device is present at the given address. It's lowish priority, though.
Most of our devices DO take two+ of the same measurement, so all of my Get_____ methods actually return arrays. Even the single measurement devices just return an array of one item, which isn't a big hassle. I did consider making a one-object-per-measured-value design, but when you have an instrument that produces over fifty measurements at a time that gets hairy. Heck, we have one thing that needs to produce around 600 measurements, and needs to make them a hundred times per second. I'd be a little afraid of the overhead involved in that many objects!
That's part of the thing, though: a device that takes ten types of measurements would have to split into ten modules, giving ten parallel lines of flow that might have to be ordered among themsleves under what I understand of Intaris's proposal (which I might misunderstand), and would involve at least 21 calls (1 object + GetModule calls + 10 measurement calls) instead of one line of GetTemp, GetPress, GetVolt.... one after another in a flow that involves 11 calls under our scheme. The overhead--practically if not idealistically--is just too much.
06-08-2013 10:13 AM
crcarlin wrote:
That's part of the thing, though: a device that takes ten types of measurements would have to split into ten modules, giving ten parallel lines of flow that might have to be ordered among themsleves under what I understand of Intaris's proposal (which I might misunderstand), and would involve at least 21 calls (1 object + GetModule calls + 10 measurement calls) instead of one line of GetTemp, GetPress, GetVolt.... one after another in a flow that involves 11 calls under our scheme. The overhead--practically if not idealistically--is just too much.
Just a note: Same number of overall VI calls required in both solutions. The Get Module calls would obviously not be called in a loop, they would be done before the loop.
06-08-2013 11:28 AM - edited 06-08-2013 11:29 AM
@nathand wrote:
Jumping topics a bit. I understand what you want - a way to drop a constant for any device on the block diagram, wire it to make a certain type of measurement, and have the compiler generate an error if that device can't take the measurement.
AH! This actually makes more sense now...I see the means to an end. The OP is essentially trying to write an API, that others can use to develop their own tests, with limited LabVIEW knowledge. He wants a compile error if they try to make a measurement on a device that's not supported by said device, vs having them unaware until they get an error at run-time. Am I correct in this point?
06-08-2013 12:35 PM
@for(imstuck) wrote:
@nathand wrote:
Jumping topics a bit. I understand what you want - a way to drop a constant for any device on the block diagram, wire it to make a certain type of measurement, and have the compiler generate an error if that device can't take the measurement.
AH! This actually makes more sense now...I see the means to an end. The OP is essentially trying to write an API, that others can use to develop their own tests, with limited LabVIEW knowledge. He wants a compile error if they try to make a measurement on a device that's not supported by said device, vs having them unaware until they get an error at run-time. Am I correct in this point?
Yep!
To me that's about 60% of what OO programming is: a structure interface--an API--through which to access the encapsulated functionality.
06-10-2013 11:52 AM
> To me that's about 60% of what OO programming is: a
> structure interface--an API--through which to access the encapsulated functionality.
Yes, it is. You are totally right. What I hope I have explained to you is that this is NOT a failure of LabVIEW. What you have asked for is logically impossible in any programming language. If you had a fixed list of devices, you could do this. But you have a dynamic, run-time determined array of devices. That means you are going to have a runtime check *somewhere*, either at the execution of the methods or at the casting to the defined type. The latter solution would have the additional drawback of two different wires that both represent the same physical device.
LabVIEW could gain some new features that would make this situation work better, but your ultimate goal is logically impossible. If you can find an example in another programming language that demonstrates what you're hoping to do in LabVIEW, I'm happy to evaluate it, but as you have laid out the problem, I am quite confident that there is no such solution in any programming language.
The solution you are currently using -- all methods existing on the base class and runtime errors for methods called on physical devices that do not support the method -- is the closest you can get to the utopia you are seeking.
06-10-2013 12:07 PM
We must have a miscommunication somewhere. What I'm describing is akin to what I've implemented in the past in Java by using multiple inheritance and interfaces, and more recently have seen implemented in Python using mix-ins.
Maybe a hint at the miscommunication is the "run-time determined array of devices." I emphatically need no such thing, and in fact the entire [practical] reason I'm here is that run-time determinations are bad for us. I want compile-time determinations, since we're always going to stop the program and swap objects on the block diagram, at which point the compiler will know exactly what's going over each wire.
So far it looks like polymorphic VIs selected based on wired class (and broken when the class has no implementation) is going to be the closest I can get here. I haven't completed that change yet, but over the weekend I did notice someone on the other forums doing something similar.