Random Ramblings on LabVIEW Design

Community Browser
Labels
cancel
Showing results for 
Search instead for 
Did you mean: 

Re: If LabVIEW was SteveVIEW

swatts
Active Participant

The discussion on functional encapsulation and all the comments got me thinking....

I've been working this way for any years and have got used to the limitations of the action engine, but what if I had it all my own way.

The stated limitations are...

  • Managing Un-used connections
  • lack of inherent scaleability
  • people prefer lots of coloured wires
  • people prefer functional decomposition (i.e. lots of non-cohesive functions acting on data)

Now the latter 2 we view as a design weakness and they view as a strength and therefore it comes down to who's biggest. Actually in the end if you are writing great software in whatever method it doesn't really matter. So if you don't mind I'll concentrate on the first 2.

So in my blue sky world LVOOP would go like this.

From some point on the Functions Palette, quick drop, Tools Menu or Project instantiate a new object.

This will give a list of the classes in the project and allow 1 to be selected.

When selected the object will drop on the block diagram and it will get a new instance name, this instance name can be changed to better suit the problem.

Instantiate1.PNG

The first 2 blocks on the menu would be standard for all classes.

Go to Class Diagram--> will bring up the UML diagram for the class, similar to the Symbio tool.

Changes can be made to the classes and then we return to the block diagram.

Create new instance -->does what it says - this will give us some simple scaleability.

--------------------------------------------------------

Get Ref -->this will allow standard LVOOP to work

Class ID, Class Name and Get Instance Name-->basic housekeeping

--------------------------------------------------------

Initialise-->Close these are the main PSU classes methods

--------------------------------------------------------

Set Over-current and Set Over-voltage-->sub-class methods for new capabilities

The data encapsulation of the class will be globally assessable inside the scope of the object instance.

The method selection will set the inputs and outputs as in a polymorphic VI.

So finally we'll get back to our familiar diagram.

Instantiate2.PNG

Is this too ambitious to put on the ideas exchange??

I think that's just about enough action engine stuff for the next year or so. My next blog will be a cartoon.

Hugs and Kisses

Steve

Steve


Opportunity to learn from experienced developers / entrepeneurs (Fab,Joerg and Brian amongst them):
DSH Pragmatic Software Development Workshop


Random Ramblings Index
My Profile

Comments
drjdpowell
Trusted Enthusiast

Re (3), I actually prefer a few coloured wires.  Lots of wires cluttering up the diagram suggests too much detail being dealt with in the same place.  If your code starts to look like spaghetti, time to step back for a rethink.  A problem with hidden wires is that they allow for hidden spaghetti.

Another issue is scope.  If I see a coloured wire confined to one area of the block diagram, not running anywhere else, then I know that that object isn't accessed and modified elsewhere.  With your code examples above, though, I have no visual clue as to who or what is accessing your PSU objects. 

swatts
Active Participant

In retrospect my items on 3 and 4 sound far too judgemental, it wasn't really my intention (sometimes the written word is a blunt medium), I was rushing to get the ideas out of my head as it was haunting me.

In my scheme of things I wouldn't need to know the scope of who is accessing the PSU objects because it will only be accessed by another component further up the system hierarchy, i.e the block diagram tells me all I need to know. So for example a Drive component, which is called by a Test Component etc etc.

The only thing I haven't got my head around with this sort of schema is how dynamic dispatch will work, if indeed I want dynamic dispatch.

(I like it a great deal, but it has dangers I think).

I was really just a mental exercise, I need to get them down in writing to stop myself mulling them over.

Steve


Opportunity to learn from experienced developers / entrepeneurs (Fab,Joerg and Brian amongst them):
DSH Pragmatic Software Development Workshop


Random Ramblings Index
My Profile

tyk007
Active Participant

I agree with drjdpowell with regards to the end-developer understanding the life-time and scope of an item (eg. by wire). I too have tried to better encapsulate APIs to the point where the level of understanding a client needs is minimised to the extreme. But if I add a feature (read: remove more wires) and the client doesn't better understand or worse adds confusion then this is the wrong decision. Including a "reference" wire or the like is akin to how things are handled in other OOP langauges - you are operating on an object, and this is the reference to that object to operate on (whatever it is). I know adding a reference in some other way (such as via a label or some-such as you described) is similar but it is also not apparent from a developers perspective the scope of that object; again, in a text-based language the object variable lives inside it's current scope or the reference is passed (directly) to another scope. Unfortunately in LabVIEW, FGVs/AEs have gloabl scope; this is a blessing from a begineer's persepctive and a curse to an experienced developer.

swatts
Active Participant

Action Engines are not FGVs!

FGVs are a design of Action Engine that allows global data and as such are as dangerous as a global variable, public scoped queue, shared variable etc etc. i.e. they are OK for sharing data globally, but will cause issues if used in an unmanaged way.

The argument I have about references is that they are only interesting on the block diagram when they are part of the problem they are trying to solve. If the problem you are solving has no mention of references, than the nearer to the customer you get with your code the less references you should see. This is just abstraction.

This is a reasonable representation of how it would appear in smalltalk.

PSU1=PSUClass    \\ Instantiate PSU Object

PSU1 Set Current: 1.00.   \\ Send message Set Current to PSU1 with a data value 1.00

I see no references here, the instance name is in charge of the scope.

In any decent text based language the method would appear as a drop down after the object has be declared. This is essentially what I'm talking about.

Now I will never want to program in a text based language again, but that is not to say there are lessons we can take from them. This particular discussion has it's roots in about 1976, when academia was moving from structured analysis (Yourdon) to a more OO technique (Booch et al).

Steve


Opportunity to learn from experienced developers / entrepeneurs (Fab,Joerg and Brian amongst them):
DSH Pragmatic Software Development Workshop


Random Ramblings Index
My Profile

swatts
Active Participant

Oh and the thing I would like the most is that the Method dictates the inputs and outputs. Life would be wonderful if that could happen.

Steve


Opportunity to learn from experienced developers / entrepeneurs (Fab,Joerg and Brian amongst them):
DSH Pragmatic Software Development Workshop


Random Ramblings Index
My Profile

tyk007
Active Participant

Haha I think you took my comment more to heart than intended. I fully understand what the two are different. When I refer to FGVs / AEs etc. what I am actually referring to is that they are based on a specific LabVIEW "feature" - the way LabVIEW handles instances of VIs within an Application Instance. By default, all VIs are singletons. The fact that you use a single VI to encapsulate atomic behaviour (AE) is an example of how that can be both useful and also potentially problematic. Your AE is, by definition, a global from the client's perspective. Should we make all our public objects global? Accessible from everywhere in the application? This is what drjbpowell was referring to - globals can be hard to track down and in this instance; as a matter of personal preference, wires help logically group that code together.

I am not as familair with smalltalk (I'm more a .Net kind of guy) but in your example you create the variable PSU1 - is that not your "reference"? You need to refer to something - this is the definition of "reference" after all! So we don't have to refer to a specific part of memory where the origin of an object is stored - that's the compilers job. I'm also guessing that your object PSU1 only lives within the scope it was defined. That's different than the default AE you have described where by the nature of LabVIEW it is always a global. This is my main beef with the concept (and to a certain extent LabVIEW itself) - public objects end up being global instances, rather than it being a specific design decision that you can make on a case-by-case basis.

But this is a matter of personal preference - it's ok for us to disagree

As far as usability goes - as you say, that is the focus of your discussion - I like the idea of being able to select on the block diagram the particular method (call it message) of an object without having to browse for a VI or drag from the Project Explorer. If you put this on the IE I'd certainly kudo is, as would a lot of other OOP "style" developers.

swatts
Active Participant

If we didn't disagree the conversations would be very dull indeed.

(FGVs are the bain of my life almost guaranteed to get a response https://decibel.ni.com/content/blogs/labviewramblings/2012/11/30/a-rant-about-fgvs)

You're correct about the reference, it's just hidden (or implied) in the name. I rather liked that idea.

I disagree about the global arguement, surely if you didn't want them accessable everywhere wouldn't you put them in an lvlib and scope them. I have to admit I have no issue with having all my public VIs accessable, if people want to use them they have the defined interface.

If I could get dynamic dispatch clear in my head I'd probably submit it to the idea exchange as most of the mechanisms are already available. My class is a combination of the the current class definition and a polymorphic VI driven by the method essentially.

But it's easy for me and probably significantly more difficult for AristosQueue and his merry band of LabVIEW makers.

Steve


Opportunity to learn from experienced developers / entrepeneurs (Fab,Joerg and Brian amongst them):
DSH Pragmatic Software Development Workshop


Random Ramblings Index
My Profile

AristosQueue (NI)
NI Employee (retired)

> If we didn't disagree the conversations would be very dull indeed.

Why, yes, I would like some tea. Sugar? Oh, no thank you. Isn't the sky blue today? Ah.

Steve: In your ponderings, I'd like you to think about that error cluster wire down there. Suppose that it were an object, just like your now-vanished references-to-hardware. How would you instantiate and work with it under your proposed scheme? Waveforms? Timestamps? Had LV 8.2 existed before those data types were created, they would all be classes. What you are proposing is a by-reference system of objects. Those comprise a very small percentage of the class data types I would expect to see in most applications (the fact is, I see them comprise the majority because few LV users actually take advantage of the full power of LV classes for their by-value types, prefering to just use raw clusters, raw arrays, raw strings, and raw numerics, but that is changing over time, so I don't think that's what we should be designing the class system around).  I don't know how you would instantiate and pass around those objects -- especially arrays of those objects and objects nested inside of each other as data values -- if they are all reference types with no wires.

I have done a lot of work trying to put all the methods into a single block diagram -- representing a class as an event structure with one frame per method is one of my most promising approaches to merging the by value and by ref semantics problem. Here's where that system falls apart entirely -- Class X has two methods, A and B, and A needs to call B, and after it calls B, A has more work to do. There's no good way I've found to draw that since you can't recursively call a particular instance. Creating a common subVI just obscures the work done by method B. In fact, that same problem bites the code you've proposed generally -- What if your "Initialise" ('s', not 'z', damn that's hard to type) needs to call "Set over current" as part of its work but not all of its work? You create a subVI of the common code, right? But that way leads right back to having a separate VI for every method, just so it can be called as part of other methods.

swatts
Active Participant

Key words here...Blue Sky and ponderings

AristosQueue wrote:


                       

Here's where that system falls apart entirely -- Class X has two methods, A and B, and A needs to call B, and after it calls B, A has more work to do.

That strikes me as extremely poor cohesion and I'm trying to rack my brain as to when I might see this. Warrants a bit more thought I think.

My way of handling the passing as objects etc is my Get Ref method, I think I'd need some additional mechanism to handle dynamic dispatch (something a little more elegant than Set Ref would be nice), but this is all related. I think the combination approach will be really handy.

AristosQueue wrote:

- What if your "Initialise" ('s', not 'z', damn that's hard to type)

I've read that 'z' is the original way of spelling initialize and us darned English changed it fairly recently. My code has a peculiar mixture of initialise and initializes in it. Like the strange mixture of metric and imperial my generation is stuck in. That said driving on the left is historically correct (to allow your sword arm freedom to hit bandits) and we pronounce aluminium correctly.

The only real worry I have about using Objects for data typing is that is abstracts the block diagram away from the actual data it is working on. These things move LabVIEW further away from the being a tool that anyone can pick up and comprehend, they also make it harder for us majority of average programmers to understand. That's why I moved it to a specific Get Ref method so that the design decision to use it is announced.

Thanks as always to you and tyk007 for your insights.

Steve


Opportunity to learn from experienced developers / entrepeneurs (Fab,Joerg and Brian amongst them):
DSH Pragmatic Software Development Workshop


Random Ramblings Index
My Profile

AristosQueue (NI)
NI Employee (retired)

swatts wrote:

That strikes me as extremely poor cohesion and I'm trying to rack my brain as to when I might see this. Warrants a bit more thought I think.

Classes call their own methods frequently. Some basic use cases...

----------------------------------------------------

[No inheritance use case] Setup and shutdown routines frequently involve calls to the other API operations. Suppose you have a robotic arm and an API for issuing instructions to that robotic arm -- it has joints for shoulder, elbow and wrist. One instruction that users can issue is "Straighten Elbow", which flattens out the elbow joint. Similar instructions exist for "Straighten Shoulder" and "Straighten Wrist". A fourth instruction is "Reinitialize", which is going to move the robotic arm to some defined canonical starting position. The "Reinitialize" is going to call all three of the other operations as subroutines.

----------------------------------------------------

It is very common for parent APIs to define operations that the child classes will use. Check out the LVOOP shipping example

<labview>\examples\lvoop\BoardTesting

Resistor.lvclass:Self Test.vi calls Component.lvclass:Get Row And Column.vi. The former is a member of the child class. The latter is a member of the parent class (which makes it a method of both classes by inheritance).

----------------------------------------------------

A similar use case is where the parent API defines a public method and then some internal protected methods that the children override. The parent function calls the children at various points through the execution.This allows the parent to define the basic algorithm and allows the children to fill in the details needed for themselves. Again in the BoardTesting example, check out Board Design.lvclass:Check Image Matches Design.vi. This calls Get Components.vi, which is overridden by each of the children. They can each provide the list of their components in different ways -- some hard coded, some programmatically generated, but entirely unknown to the parent that just needs the list somehow in order to run the board testing algorithm.

----------------------------------------------------

[No inheritance use case] Many software systems have layers of APIs, all of which are public. Your robotic arm is a whole bunch of low-level encoders, and the API has functions for "Move N Degrees Clockwise" or "Move N Degrees Counterclockwise", which contain a whole mess of code for counting the motor encoders and doing the math for figuring out how many degrees have been moved. The "Straighten Elbow" function could redo all of that motor encoder counting for the higher level operation, or it just calls "Get Current Angle" followed by "Move N Degrees ..." to straighten itself out.

----------------------------------------------------

[No inheritance use case] A class frequently has some sort of "Is Equal To" function to compare two objects. "Is Equal To" may call "Compute Hashcode" on both objects and then compare the hash codes -- in some systems, that is a high speed way to check inequality since the hashcode may be cached and thus already computed, and only do the full work of testing all the fields if the hashes match. You can imagine a class Matrix.lvclass whose "Determinent" function is defined as

      if (matrix.determinentIsCached is FALSE) then

      {

             matrix.determinentValue = <compute determinent>;

             matrix.determinentIsCached = TRUE;

      }

      return matrix.determinentValue;

Then imagine an "Is Equal" operation that says

     if (matrixA.Determinent != matrixB.Determinent)

          return NotEqual

     else

           return comparison of every value in matrixA to matrixB

In a system where the determinent is frequently already computed by something upstream, this is more efficient. In a system where the determinent is rarely computed, this is less efficient. This is just one example.

swatts
Active Participant

Well that saves me thinking about it!, as always a stunningly detailed response. I am amazed at the effort everyone puts in!

Your argument may have gone flying over my head, so stop me if I'm going off on a tangent or not picking up your point correctly.

I'm not expecting anything clever from this i.e its going to operate synchronously do it's called method to completion before moving onto it's next method. I have no problem with a method calling another method. Any rules and hardships are therefore then only dataflow related.

I don't envisage the actual structure of the methods changing from how they look now (the only bit I'd like is for the Object Reference to be implied if the whole structure is on the block diagram).

I think if I add a Set Reference to the standard class method list it might give it the flexibility to handle dynamic dispatch.

If only it was so easy!.............. I feel a bit guilty about putting it out there, it's really only a mental exercise.

Steve


Opportunity to learn from experienced developers / entrepeneurs (Fab,Joerg and Brian amongst them):
DSH Pragmatic Software Development Workshop


Random Ramblings Index
My Profile

AristosQueue (NI)
NI Employee (retired)

Here's a visual of the problem I keep running into:

Untitled.png

crossrulz
Knight of NI

What I have done in this instance is create a state machine inside of the AE.  I know, it sounds kind of weird.  But with a state machine, I am able to perform many steps in 1 call.  So back to the power supply example.  I have a task for Setup Supply.  So the state machine would perform the Set Current, Set Voltage, and Set Output states (I know my terminology isn't 100% correct here, but I have to distinguish betwen the commanded task and the steps to perform).  I also have a task for each of the Set Current, Set Voltage, and Set Output.  Each of these only call the state that is needed.  I had many other tasks and states, but this is a head exercise right now, not a code review.


GCentral
There are only two ways to tell somebody thanks: Kudos and Marked Solutions
Unofficial Forum Rules and Guidelines
"Not that we are sufficient in ourselves to claim anything as coming from us, but our sufficiency is from God" - 2 Corinthians 3:5
swatts
Active Participant

The initial LCOD component design was indeed a state machine with pre-condition and post condition checking, and we use it like this every now and again as required. We stripped it back for the book because it is much easier to describe and we like simple.

Steve


Opportunity to learn from experienced developers / entrepeneurs (Fab,Joerg and Brian amongst them):
DSH Pragmatic Software Development Workshop


Random Ramblings Index
My Profile

tyk007
Active Participant

I just wanted to update my thoughts on global accessibility of objects... When you create a basic LV ByRef class, the "object" is the cluster passed between your method calls on that object. If this is housed within a single VI (say for example), then the object only exists in that VI, even though the methods are publicly accessible. Lets presume for the moment that there is no privately scoped global of any kind specific to the class rather than the object.

If I create a second VI and perform the same activity set of class method calls, the second object is still unique and seperate from the the first. In this case, the first and second objects have different scope - they can't be directly accessed from outside the VI without additional messaging of some kind (take your pick). So you can be reasonably confident that modifications to one object will not affect the other. As a developer, the lifetime of the object is clear.

Looking at VIs storing data (S/R, FN) as singletons in an Application Instance - this object is, by nature, gloabl to any and all VIs that exist in the Application instance, since the object is encapsulated as both data and methods. While this is a more theoretical/abstract view of an object (which sounds good and on this thought I have no quibble) my concern lies in what happens when I have hundreds or thousands of objects in my system, all global variables so to speak. Without a great search feature (perhaps one that checks VIs that aren't in memory? hint hint) your level of discipline as a developer must surely increase. Moving away from global variables in structured langauges was another proported advantage of "OOP" style development.

As much as we like to discuss those annoying "text" languages - take a look at how they generally handle scoped variables (including the smalltalk example). The developer at that scope can easily see what the variable is used for and how. He also has very high confidence that the variable is only used in that scope. If, in a completely different scope, I create the same named variable, perhaps because it is natural to do so, then the two do not conflict.

Also - what if the variable doesn't represent or interact with some physical hardware (such as the power supply) where a singleton might be appropriate. What about some other non-physical class encapsulating behaviour that doesn't interact with hardware but could be frequently used (a recent example for me is a XML-based object that maintained an XML state and permitted operations upon it). It's quite likely that said objects could be used frequently but for only limited scope, then discarded.

These discussions are good. Meh, maybe I have been bitten too many times by the developers at my workplace over-utilising FGVs / AEs without a clear understanding of scope and atomicity. Don't get me started on the number of bugs due to those all important but Recommended inputs. I now enforce wrappers around them with Required inputs to prevent the "whoops look my data is now 0 I wonder where that happened" trick. It's all well and good when you have only a single "Value in" input, right? Ironically I am admitting to using the same tactics that I am critiquing . But I try not to over-populate a design with said globals.

So .. any chances of implementing Intellisense for LabVIEW like swatts is describing? Quick-drop is reasonable enough but there might be a more Usability oriented solution.

swatts
Active Participant

I think some of this falls back to my previous blog entry regarding functional encapsulation vs data encapsulation. Our primary view is that an object is some privately scoped data that has a cohesive set of methods applied to it and we pretty much stop there. Using objects as a data type is advantageous to us as a method of passing them around, but our main use is to wrap up our functions and hide our data. We actually don't have much in the way of global data and our general technique with LabVIEW is pretty standard. This allows our block diagrams to be pretty simple. We have components that handle the user interface, errors, hardware, reporting, configuration. They all have No Command - Error, Initialise,......,Close that define their life. We seldom use dynamic anything, do use patterns but think the Gamma stuff is a bit low level. We seldom have race problems and finishing projects is not traumatic, adding and maintaining is easy. One of the biggest issues is that it is a bit boring, but I guess that's the flipside of low stress.

That said, as with anyone who produces anything we have skeletons in our cupboards, mostly these are human factors, for example I spend too much time doing this rather than writing software.

It's really interesting to hear your experiences (we tend to work in our own little bubble). I'll spend a bit more time and read through all your responses of the coming weeks as I think I will learn something.

Anyway I've got to do some work on my cartoon for the next blog entry.

Steve


Opportunity to learn from experienced developers / entrepeneurs (Fab,Joerg and Brian amongst them):
DSH Pragmatic Software Development Workshop


Random Ramblings Index
My Profile

AristosQueue (NI)
NI Employee (retired)

The most interesting part of Steve's posts on this topic is that all of this works for him. I have done a lot over the years to give people tools to move away from that kind of code specifically because it does not work for most users. I have helped salvage many projects that have gone off the rails with these kinds of global storage. That leads me to ask a very basic question: what is Steve doing different? I can see only two possible answers:

  1. He is working on different types of problems than most other users
  2. He has a different software *process* than most other users

Steve: It might be worth exploring both of those topics in future blog posts. What is a typical project for you? What kind of software process do you use to do them?

tyk007
Active Participant

That sounds like a great idea. Consider a kudos from me for such a blog post.

swatts
Active Participant

I'm thinking this too!

We just write software, we have no specific remit. Our current work list is....

Realtime system monitoring on passenger ferrys

Shop-floor tracking system

Nuclear Laboratory Management Software for 3 Labs

Explosion Measuring System

Helicopter Fuel Testing System

Helicopter Filter Testing System

Air Bearing Test System

Airport Lighting and ACMS system

Couple of Pressure/PRT test systems

Wafer Prober

Battery Test System

So I don't think it's item 1.

There's 3 of us writing software in a similar fashion.

Item 2 is the place to look I think.

Steve


Opportunity to learn from experienced developers / entrepeneurs (Fab,Joerg and Brian amongst them):
DSH Pragmatic Software Development Workshop


Random Ramblings Index
My Profile

drjdpowell
Trusted Enthusiast

AristosQueue wrote:


                       

That leads me to ask a very basic question: what is Steve doing different?

I took note of Steve's first reply above:

"In my scheme of things I wouldn't need to know the scope of who is accessing the PSU objects because it will only be accessed by another component further up the system hierarchy, i.e the block diagram tells me all I need to know. So for example a Drive component, which is called by a Test Component etc etc."

In my limited experience, Action Engines can be used as connections between seemingly independant components (even worse: connections between deep subVis of components).  That's why I prefer wires; I want to see these connections.  If Steve is only using a particular action engine in one compenent, then he's not making these hidden connections. 

tyk007
Active Participant

Yes, my main concern around the use of AE in this context is when the AE is intended to be used in more than one component (ie. object sharing). But when done diligently (ie. rules) AE can be used effectively, which is probably Steve's point. My concern is always the diligence and understanding of the developer and I hate to make that any harder than necessary.

swatts
Active Participant

I think the issues you are describing is to do with poor coupling, so if you are regarding a AE as being a global data problem it is because you are exposing the data inside the AE to a global audience. Similar to named queues this really will cause issues. This is probably because I have seen FGVs being taught as a way of avoiding race conditions. This is obviously a no-no. I don't really like this homing in on Race conditions as a problem, I would rather people were taught it was a symptom of poor coupling. Essentially everyone sharing a big bucket of data is really poor modular design practice. We have always stated that the data of a component (AE) should be inaccessable to the outside world.

Coupling is a very good place to gain benefits with LabVIEW, because of it's visual nature, the area I'm really interested in is control coupling. I have a half written blog on this, but need to do more research.

Steve


Opportunity to learn from experienced developers / entrepeneurs (Fab,Joerg and Brian amongst them):
DSH Pragmatic Software Development Workshop


Random Ramblings Index
My Profile

swatts
Active Participant

Remember that action engines are not our term either.

We like to think of it as a hierarchy of components calling components, the approach is similar to OOP, but we try to concentrate on it being pragmatic (i.e. our components are very similar to the components in the system being modelled Hardware, Display, Report, Config, Database, Error Handling). Each of these components is self-contained and can be dropped into a new project.

Steve


Opportunity to learn from experienced developers / entrepeneurs (Fab,Joerg and Brian amongst them):
DSH Pragmatic Software Development Workshop


Random Ramblings Index
My Profile

AristosQueue (NI)
NI Employee (retired)

swatts wrote:

so if you are regarding a AE as being a global data problem it is because you are exposing the data inside the AE to a global audience


                   

You don't have to expose the internals of the component (AE) to the outside world to have a data problem. If any function of the component has an output, you have a potential data problem. I've seen code that is essentially this:

Components B & C are both calling into a component A to do something. Most of the time they do not ask A at the same time. But sometimes B asks A to do something and A answers, "Sorry, I can't do that, I've got another assignment right now." The programmer then used the fact that he got an error back to deduce that C must be doing task X, so it would be safe for B to do task Y, which normally is unsafe for B to do, but is safe because B thinks he knows that C is busy doing something else. Then C got modified so that it talked to A at a different time in its own algorithm, and suddenly B is broken.

Any output that is not entirely based on the inputs of the function -- in other words, anything that is computed even remotely from the internal state of the component -- becomes tainted as it now has a value that is dependent upon the global state.

I am NOT saying that is happening in your case. Nor am I implying that running wires always fixes this -- there are lots of ways to set up these sorts of dependency traps. Just trying to explain why your components and AEs and FGVs get so easily conflated in peoples' minds. Any sort of global reference poses these sorts of risks, so I push to move as much as possible out of that global space and put everything -- everything -- into the wires.

tyk007
Active Participant

With regards to Race conditions I whole-heartedly agree with your sentiments. They occur becuase you are sharing an object in a such a way that atomicity of operations cannot be guaranteed, and FGVs have fallen a-foul of propaganda on this point.

The only way I am aware of to ensure that you don't expose an AE to a global audience is to make it a private object in, say, a Project Library (let's ignore LVOOP for now ). But however, at some point, you do need to be able to share objects regardless of how well coupled they are. Perhaps this power supply is not a good example of this (after all, you may have only one of them and only one component cares about it) but if you needed to have access to this object from more than one component, how would you design the interface for this? I am trying to think of ways that don't require you to increse the scope of your component to include everything that needs private knowledge of the AE (this is obviously poor modularity and coupling).

swatts
Active Participant

I'm a practical learner, I'll need to try and drag up an example of where I might have bumped into these sort of issues (it's getting late in blighty). Going back to AQs question about what I'm doing different, nothing really springs to mind and therefore am I designing these problems out at the start of the project. I refuse to believe that I've only worked on really easy software in the last 15 years! There's part of the puzzle missing here.

I'll ponder for a while.

Thanks as ever for your interest!

Steve


Opportunity to learn from experienced developers / entrepeneurs (Fab,Joerg and Brian amongst them):
DSH Pragmatic Software Development Workshop


Random Ramblings Index
My Profile

drjdpowell
Trusted Enthusiast

swatts wrote:

We like to think of it as a hierarchy of components calling components, the approach is similar to OOP, but we try to concentrate on it being pragmatic (i.e. our components are very similar to the components in the system being modelled Hardware, Display, Report, Config, Database, Error Handling). Each of these components is self-contained and can be dropped into a new project.


                   

Just as a side question, how do you handle components that need to be "active"; with some process that needs to occur all the time.  A PID temperature controller, for example, or a DAQ? 

swatts
Active Participant

DAQ tends to be triggered so we have a DAQ component pulled of to a seperate Loop doing DAQ stuff Asynchronously (This is so much easier nowadays with tasks). With control the majority of our projects use dedicated process controllers so it's just a component that sets setpoints and gets process variables via whatever communications bus you choose (but I'm guessing you're more interested in how we would do it in LabVIEW ). Essentially the design would be messages sent to client/server on an RT system. The host component would be similar to a serial type process controller (Initialise, Set Setpoint, Get Setpoint, Get PV, Close etc etc) and this would pass requests to the server. If needed we could dynamically spawn control VIs to run asynchronously, but again this would be handled by the Initialise command (keeping the implementation hidden). These techniques are chosen on requirement, always pushing the most complex to the end. The interface to them is always fairly similar, the reason is that whatever technique you use, the actual commands for a PID loop stay pretty much the same.

Early morning responses are never too coherent, so apologies up-front

Steve


Opportunity to learn from experienced developers / entrepeneurs (Fab,Joerg and Brian amongst them):
DSH Pragmatic Software Development Workshop


Random Ramblings Index
My Profile

drjdpowell
Trusted Enthusiast

swatts wrote:


                       

If needed we could dynamically spawn control VIs to run asynchronously, but again this would be handled by the Initialise command (keeping the implementation hidden). These techniques are chosen on requirement, always pushing the most complex to the end. The interface to them is always fairly similar, the reason is that whatever technique you use, the actual commands for a PID loop stay pretty much the same.


                   

I differ in that I prefer to use the dynamically spawned VI for every component, with the complexity eliminated by hiding it in a parent class (my actual component classes generally contain only one VI each).  I use message sending/receiving more explicitly, rather than hiding that behind an API.  That has an advantage that compenents can "push" information up to their callers (for example, the PID controller could report an over-temperature alarm, rather than requiring the calling code to explicitly ask about alarm conditions).

-- James

swatts
Active Participant

This comes back to pushing the complexity to the end, I used to have components that got configuration data and set themselves up, this resulted in them becoming less re-useable. So I tend to take a very minimalist approach to my hardware components and wrap extra functionality around them. i.e. I pass configuration data into them as a seperate method.

My experience with dynamically spawned VIs is there is a design cost in that you lose immediacy (if that's a word) of debugging the system as a whole. I like LabVIEW because I can fit a reasonably sized program in my head, so debugging is easy. I stopped using VB 6.0 because it essentially became a load of dynamic communicating processes. I found it very hard to squeeze it into my bonce and debugging became a real trial. I'm quite brutal about these problems and put VB down and never picked it up again!.

From a purity of design view, your technique works very well for me. The only other design cost is the amount of communications between components, this may be a coupling issue resulting in complexity (or difficulty in getting it in your head). Design cost is not a bad thing by the way, every decision we make has a design cost,

I've said this a few times, it doesn't matter what anyone else thinks, the only actual objective judgement of your designs is at the end of the project. Quality of design is inversely proportional to the amount of hassle it gives you at the end.

Thanks for your input James, it's been a really interesting (for me) and enlightening discussion.

Steve


Opportunity to learn from experienced developers / entrepeneurs (Fab,Joerg and Brian amongst them):
DSH Pragmatic Software Development Workshop


Random Ramblings Index
My Profile

drjdpowell
Trusted Enthusiast

My experience with dynamically spawned VIs is there is a design cost in that you lose immediacy (if that's a word) of debugging the system as a whole. I like LabVIEW because I can fit a reasonably sized program in my head, so debugging is easy.

Dynamically spawning really shouldn't be more complex than putting another loop on the block diagram.  In either case, if you need to understand a complex multi-loop interaction that can't be reduced into manageable parts, then your in trouble.  But that's tight coupling or poor cohesion.  With loosely-coupled processes most debugging is done considering processes one-by-one or in pairs.

The only other design cost is the amount of communications between components, this may be a coupling issue resulting in complexity (or difficulty in getting it in your head).


                   

Ideally, the number of messages should roughly match the number of API calls in an alternate design (well, be less than twice as many, as an API call with outputs requires two messages).

PSU Actor.png