Jump to content

Techniques for componentizing code


Recommended Posts

Giving it a name (or some sort of identifier) at creation is one solution if you have access to the source code. For clarity I'd code it so it returned <MySlaveName:Response> instead of <SerialDevice:MySlaveName:Response> and give it the name SerialDevice:MySlaveName upon creation. That's just personal preference though...

Another option if you don't want to modify your slave class (say, if you have a bunch of tests written for it or have already used it elsewhere and don't want to risk breaking compatibility) is to create an adapter class that wraps the slave. The name property will be part of the adapter class and it will modify all the output message names from the original slave to be uniquely identifiable. Then in your main code you just replace the slave vis with the adapter vis.

Link to comment

My code has evolved into a similar (if less refined and sophisticated) pattern. Each physical instrument's slave loop responds to "show" and "hide" messages to display the front panel. Many of the instruments are streaming, so there are Start and Stop buttons, and appropriate displays of data that's being sent off to data queues (AEs). There are several different test VIs. Each test calls some of the instruments and some of the data queues (flexibility is key). Each test is called by reference so that "Main.vi" can do other stuff - such as run parallel tests with the other instruments, or allow the user to click Show and Hide buttons so they can watch the pretty data stream on by (or view the current collection rates or dropped packet count).

The way I initially chose to use an instrument in a test is:

1. create a class in the top-level VI

2. send it to a dynamically dispatched* "handler" - the slave loop, which has a named queue

3. from a test, send messages to the named queue

I'm trying to figure out how to get more flexibility in a test. If I add a duplicate instrument, I have to create a second named queue handler - along with its associated "add to queue" VIs. Perhaps I'll try bundling up all the instruments' classes and send them to the test. However, I also have "simulator" child classes of each instrument. A different top-level VI (separate app) has buttons for sending fake data on any instrument that was defined by the user as being simulated. Class methods are used for setting private data, and are accessed via the named queue. I want to start using a class that holds an instrument's class in private data (due to one version of an instrument having a custom GPIO interface that consists of several NI DAQ resource names, and another version having ethernet), but I haven't figured out how to access that class from another top-level VI without using something like a named queue.

* This is dynamic because multiple versions of multiple protocols for multiple instances of multiple instruments must be continuously supported.

Link to comment
One thing I was thinking is to maybe give the slave loop a name property. The name would be set when the slave is created and become part of the response message name.

Another option if you don't want to modify your slave class (say, if you have a bunch of tests written for it or have already used it elsewhere and don't want to risk breaking compatibility) is to create an adapter class that wraps the slave. The name property will be part of the adapter class and it will modify all the output message names from the original slave to be uniquely identifiable. Then in your main code you just replace the slave vis with the adapter vis.

A third option would be to put the functionality into your MessageQueue class. Make a "PrefixedMessageQueue" child class with a "Prefix" data item, an extra "Specify Message Prefix" method, and an override of "Enqueue Message" that adds the prefix to the message name before calling the parent method. Use this child for the master's incoming queue and, just before passing it to each individual slave, call the "Specify Message Prefix" method with prefixes such as "SerialDevice:DeviceB:" or whatever.

The advantage of this method is that you only have to create the child class once, rather than modifying each slave to have a name property, or making a separate adapter class for each slave class.

-- James

Edited by drjdpowell
  • Like 1
Link to comment

A third option would be to put the functionality into your MessageQueue class.

Oooo.... I like that. :star:

I did run into a bit of trouble when I went to implement an example. I didn't create protected accessors to the MessageQueue's private data. This means the PrefixedMessageQueue class has to contain an instance of its parent and you have to override every method to unbundle the parent object. Seems kind of wasteful and unnecessary...

Here's an example of the EnqueueMessage override:

post-7603-0-32444900-1319051815_thumb.pn

And here's an example of how it would be used:

post-7603-0-55156300-1319052106_thumb.pn

(I'll have to think about releasing a LapDog.Messaging minor update that includes protected accessors. That would allow you to only override the enqueue methods instead of all of them, but I need to ponder it for a bit.)

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

My code has evolved into a similar (if less refined and sophisticated) pattern.

It's hard to follow what you're describing. Can you post code?

PrefixMessageQueue.zip

  • Like 1
Link to comment
I did run into a bit of trouble when I went to implement an example. I didn't create protected accessors to the MessageQueue's private data. This means the PrefixedMessageQueue class has to contain an instance of its parent and you have to override every method to unbundle the parent object. Seems kind of wasteful and unnecessary...

Ah yes, the "Decorator Pattern". I like using it but it is a royal pain if your class has a lot of methods, and that limits its use. Direct inheritance is easier.

(I'll have to think about releasing a LapDog.Messaging minor update that includes protected accessors. That would allow you to only override the enqueue methods instead of all of them, but I need to ponder it for a bit.)

Instead of accessors, you could alternately make a "Create" method that accepts a MessageQueue object as input. Then you can provide a PrefixedMessageQueue constant as input and it will initialize the internal queue. Then PrefixedMessageQueue never needs to directly access the data items of MessageQueue (I think; I'm still waiting for a replacement of my development computer that broke weeks ago, so I can't experiment). You can, of course, make the "Create" method protected and wrap it in a "Create PrefixedMessageQueue" method that doesn't accept a class input, thus maintaining your current public API.

-- James

Edited by drjdpowell
Link to comment

I'm not yet able to keep up with this discussion. At the risk of being a distraction from the topic at hand - and at the greater risk of causing more confusion than clarification by posting incomplete code - here is a stripped down project. (Note that queue destruction and error handling are not completely implemented, here. Nor are all the accessor, and such. In the production code, many other helper VIs are in place.)

The biggest pieces not included are the tests (which interact with instruments only via Data AEs and named queues), and the emulator - which also uses named queues to control what the simulated instruments are doing. A user (or a VI) can move sliders and push buttons in the emulator in order to test the tests.

Sample Project.zip

Link to comment
  • 2 weeks later...

I'm still swimming here, so here's the standard disclaimer: beware my imprecise and disorganized statements below!

I'm finally beginning to understand this thread, and I have a question that relates to Steven's about multiple "execute" slave loops that handle the same type of instrument: Why not use non-classed VIs for create, execute and destroy and make them reentrant? Is classing "create" and "execute" just a handy way to bundle up the input queue, output queue, object, and mediator's queue? Oh, perhaps this:

-When using multiple slaves, I don't have to worry at all about accidentally sending a message to the wrong loop. The slave object can only be wired to it's own methods. Again, a mistake here is a compiler error instead of a runtime error, saving me a bunch of time down the road.

I made an API (set of VIs) that basically just send/receive messages to/from the slave's input/output queues. If the APIs are methods of a class, they have to match the object (bundle of queues) sent to them. I don't like having the instrument's object available in the mediator loop, though.

Link to comment

Why not use non-classed VIs for create, execute and destroy and make them reentrant? Is classing "create" and "execute" just a handy way to bundle up the input queue, output queue, object, and mediator's queue?

Not every use of LVOOP is some complicated thing that can't be done with "regular" LabVIEW. Quite often it is just a clean way to make a type-def-cluster with some handy extra features and it's own distinct "type" identity. Don't downplay the value of giving your cluster it's own, unique, pretty coloured wire that will break if you accidently wire it to the wrong thing.

-- James

  • Like 1
Link to comment

James is right. In fact, I think you can duplicate all the features of LVOOP using normal G techniques if you really wanted to and were willing to write the framework code. (Probably take a performance hit though.) Not much point in it imo... NI already provided it for us in a nice LVOOP-branded package.

I'm finally beginning to understand this thread...

It's fun when the ideas click into place, isn't it? :D

I don't like having the instrument's object available in the mediator loop, though.

The instrument object isn't available in the mediator loop. You should be interacting the slave object via it's messages.

As an aside, I think my original post emphasizes the OO aspect of slaves too much. The single owner nature of slave loops is the important concept. The class is just packaging you can put around the slave loop when the need arises.

Another problem with the original post is there's an unspoken principle underlying my approach to programming. I have an overall grasp of what works for me and what doesn't, but it's hard to distill it into something under 250 pages. Here's a feeble attempt...

As developers we're constantly juggling the conflicting requirements of separation and combination. We need to separate our code to prevent a big ball of mud, but we also need to combine our code to do anything useful. Many developers appear to take a vi-centric approach to programming--the vi is the basic unit of execution. (I'll call it "vi-ism" for shorthand.) VI-ism tends to view a vi amorphously. Multiple loops on the block diagram are just tasks running in parallel. The data is shared with any loop needing it via local variables or FGs. If a race condition is observed locks or timing mechanisms are added. Pretty standard stuff.

VI-ism leads to multiple loops on the same block diagram sharing resources (local variables, control references, etc.) or data (FGs, DVRs, etc.) When the vi grows (which they always do) to the point where it's time to refactor some of the loops into sub vis, those vi-specific resources make it very, very hard to do so safely. VI-ism equates separation with a sub vi. In once sense it is correct. Sub vis provide a physical separation of the details. The dangerous corollary falling out of VI-ism is "no sub vi means no separation." In reality there are logical separations that may (and often should) exist on a single block diagram vi-ism doesn't recognize. One important logical separation is the loop.

In my world the loop, not the vi, is the basic unit of execution. Instead of a connector pane the loop's input and output messages define its interface. Data sources and sinks (fp controls, files on disk, etc) are "owned" by a loop, not by the vi the loop is on. If other loops need that data they must go through the loop's interface to get it regardless of whether the loop is on the same block diagram or not. The vi's role is changed to the basic unit of implementation.

At the risk of confusing the issue more, let me replace "loop" with a new word to more accurately describe my meaning--agent. In this context I'll define an agent as an arbitrary block of code with well defined data ownership, is designed to run in parallel with other loops, and reacts to messages from code external to it. (Don't confuse my use of the word with Agent Oriented Programming, which seems to be closer to the Actor Framework implementation.) It's an abstract concept of a kind of abstraction.

How is an agent different from a loop?

-Not all loops have or need a messaging interface. A dumb timer loop spitting out data at regular intervals is one example of a non-agent loop.

-Agents encapsulate data sources and sinks (when it has them.) For example, an instrument agent exposes read and write messages to the rest of the application, but it doesn't expose any information allowing other code to contact the instrument directly. All other code must interact with the agent if they need the instrument's services.

Encapsulation sounds like an oop. Isn't an agent just another word for a class?

No, a class is one way to encapsulate code. I can replace a cluster with a class and accessor methods, but it doesn't mean "cluster" and "class" are equivalent. The nature of the data isn't inherently classy. In the same way an agent encapsulates parallel functionality. You can put it in a class when you need the benefits classes provide, but it doesn't require a class.

Agents sound a lot like Actors and Active Objects. What's up with that?

I view agents as a more abstract concept than either Actors or Active Objects. First, Actors (as used in the Actor framework) and Active Objects are both objects instantiated from classes. Agents may or may not be in a class--the packaging is irrelevant to its core behavior. Second, I believe dynamically launching the object's execution loop is a built-in feature of those objects. (It's currently unclear to me if all Actors are dynamically launched.) Agents can be dynamically launched, but that would be a specific agent's behavior, not a universal agent characteristic. I think of Actors and Active Objects as subsets of agents with specific characteristics.

Clear as mud?

Link to comment

And here's an example of how it would be used:

post-7603-0-55156300-1319052106_thumb.pn

(I'll have to think about releasing a LapDog.Messaging minor update that includes protected accessors. That would allow you to only override the enqueue methods instead of all of them, but I need to ponder it for a bit.)

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

It's hard to follow what you're describing. Can you post code?

PrefixMessageQueue.zip

Uncanny. I haven't really been following the thread (so I may end up trying to eat both feet) but saw your master slaves post.......this looks virtually identical to my standard app designs.

I found that using a colon separator (I started by trying to emulate SCPI command composition) means you have to write more decomposing/error checking code when you start sending paths around. (I eventually settled on "->"). May not be an issue with your stuff, but thought I'd mention it.

Edited by ShaunR
Link to comment

I found that using a colon separator (I started by trying to emulate SCPI command composition) means you have to write more decomposing/error checking code when you start sending paths around.

It sounds like you do something similar to JKI's state machine. Personally I don't like that technique. I think parsing the message name makes the code harder to read. I don't ever include data, arguments, or compose multiple messages as part of the message name. It's only an identifier; it could be replaced with an integer or enum. The path would be in the data part of the message.

The colon (I've also used a dot... haven't standardized yet) doesn't delineate commands or arguments. It's just a way to visually separate the name of the sender from the message description. Why (and when) do I include the sender name as part of the message? To help explain this, here's another new term: Event Agent.

All agents define their api as a set of input and output messages. An event agent is an agent that defines its api without knowledge of any code external to it. Its input messages are commands/requests for action and its output messages are "events." Not user or fp control event constructs, but "events" in that they are messages telling the rest of the world something the agent has already done. An event agent's output messages are never commands to the external code. They are event announcements.

In the example above the Slave1 and Slave2 loops are event agents. The master loop, in and of itself, is an agent but is not an event agent. The loop contains code to manage Slave1 and Slave2 specifically. On the other hand, the master loop combined with the two slave loops is an event agent (or at least it would be if it had an output queue.)

An event agent is an independent unit of functionality and provides a natural decoupling point. Because it doesn't know or care who is sending messages, input messages do not include the sender's name. However, as the example above shows, often the code using the agent needs to know which agent a message came from. Output messages include a prefix containing the event agent's identifier.

The example's app-specific glue code would be contained in the master loop. More generally, in an event-oriented application all of the glue code would be contained in mediator loops at each level of abstraction. (A master loop may or may not be a mediator.) Every other loop is copy-and-paste reusable. It's possible--in principle--to have a library of event agents and create an app just by writing custom mediator loops.

-Dave

Oh yeah, I've also found the Sender.MessageName format to be very useful for backtracking a message during debugging.

Link to comment

It sounds like you do something similar to JKI's state machine. Personally I don't like that technique. I think parsing the message name makes the code harder to read. I don't ever include data, arguments, or compose multiple messages as part of the message name. It's only an identifier; it could be replaced with an integer or enum. The path would be in the data part of the message.

I see no difference in terms of features or function, only in realisation and symatics. You've identified that it is useful to identify the target (and consequently the origin) and whilst you have used the "Slave1,2 etc I would think that later on you would probably realise that a vi name is far more flexible and transparent (with one caveat).

By the way. I don't consider copy and paste reuse. I consider it "replication" and an "anti-pattern" (if the term can be applied in that sense). If I remember correctly, didn't suggest (incorrectly) that I would have to copy and paste to extend transport.lib and use it as an argument against?

If copy and paste reuse works for you, it's certainly an easier way to go about it. The tradeoffs aren't workable for everybody.

Edited by ShaunR
Link to comment

I see no difference in terms of features or function, only in realisation and symatics.

Agreed. (I think my semantics are easier to read, but it's a personal opinion.)

By the way. I don't consider copy and paste reuse.

Neither do I. It's just a test to see how decoupled a block of code is from the rest of the vi. If you can copy and paste a loop to a new project and it still works it's pretty well decoupled. If I want to actually reuse it I'll wrap the loop in a class and drop the class methods on the block diagrams that need the loop.

You've identified that it is useful to identify the target (and consequently the origin) and whilst you have used the "Slave1,2 etc I would think that later on you would probably realise that a vi name is far more flexible and transparent (with one caveat).

I'm curious what your caveat is.

Tieing the sender's ID to the vi (or class) name only works if each slave is wrapped in its own vi. As a practical matter I'm not going to do that. It gets in the way of understanding how a master manages the messaging relationship between it's caller and the slaves. I also often use relatively long descriptive file names. Making it part of the message itself is messy. I prefer a short sender ID for clarity and ease of use.

Besides, linking the prefix to the file name doesn't solve the problem Steve raised--multiple instances of the same slave. I'm heavily leaning towards using James' PrefixQueue idea. That allows the master to define the sender ID in the context in which it is being used, regardless of whether the slave loop is on the same block diagram as the master or in a sub vi. To put it another way, I don't want transparent message names--that makes my abstraction layers leaky. I want message names that reflect the way the master is using the slave.

Link to comment

I found that using a colon separator (I started by trying to emulate SCPI command composition) means you have to write more decomposing/error checking code when you start sending paths around. (I eventually settled on "->"). May not be an issue with your stuff, but thought I'd mention it.

I don't use this (yet?) but I feel like it would be better to have your Enqueue VI be polymorphic and have an instance which takes either a 1D array or cluster of strings to facilitate arguments. No precarious parsing and your dequeue always returns two strings, one case and one argument. It seems cleaner. If you need more complex parameters than a string, you could go off the deep end and have the parameter be a variant which is VTD'd by small-footprint helper VIs in specific cases. No muss, no fuss.

Anyone ever tried using explicit parameterization like this?

Link to comment

I don't use this (yet?) but I feel like it would be better to have your Enqueue VI be polymorphic and have an instance which takes either a 1D array or cluster of strings to facilitate arguments. No precarious parsing and your dequeue always returns two strings, one case and one argument. It seems cleaner. If you need more complex parameters than a string, you could go off the deep end and have the parameter be a variant which is VTD'd by small-footprint helper VIs in specific cases. No muss, no fuss.

Anyone ever tried using explicit parameterization like this?

For reconstitution, I have a separate vi that is basically just a split string with a smaller footprint. This means that I can just cascade the splits to whatever depth is required. I found trying to do it in the actual queue vi meant that you end up either limiting the depth (n outputs), or with array indexing which basically is the same, but with a bigger footprint.

For creating the message, I have just stuck to a string input (the ultimate variant). This has one major benefit over strictly typing and making a polymorphic "translator" in that you can load commands directly from a file (instant scripting). You can create easily maintainable test harnesses (load file then squirt to module) that works for all modules by simply selecting the appropriate file.

My view on polymorphic VI's is that in most cases they basically replicate code, so the "user ease" benefit must be far in excess of the effort and in my case; I don't think it is.I leave that to others to "inherit" from my vis if they want that feature.

Having arrays for inputs is fine on paper, but in reality it is no different. The user still has to create the array and uses concatenate array instead of concatenate string. Arguably he/she doesn't have to add the delimiter, but it's one of those "6 of one 1/2 dozen of the other" since if you just have simple, straight commands, you then have to build an array rather than just type in a string.

Link to comment

Hello,

Not entirely following the latest part of the discussion, but there may be some confusion between what Daklu was doing, extending the message identifier with "Slave1:", and the common method of adding parameters into a text command (as, for example, in the JKI statemachine with its ">>" separator). Parameters have to be parsed out of the command when received, thus the need to decide on separating characters and write subVIs to do the parsing. But Daklu's "Slave1:ActionDone" never needs to be parsed, as it is an indivisible, parameterless command that triggers a specific case in his "Master" case structure (see his example diagram).

Now, if he did want to separate the "Slave1" and "ActionDone" parts (for example, to use the same "ActionDone" code with messages from both slaves using the slave name as a parameter) then he could parse the message name on the ":" or other separator. But there is an alternate possibility that avoids any possibility of an error in parsing (Shaun, stop reading, this is getting OOP-y; there's even a pattern used, "Decorator"). Instead of using a "PrefixedMessageQueue", use a variation of that idea that I might call an "OuterEnvelopeMessageQueue". The later queue takes every message enqueued and encloses it as the data of another message with the enclosing messages all having the name specified by the Master ("Slave1" for example). When the Master receives any "Slave1" message, it extracts the internal message and then acts on it, with the knowledge of which slave the message came from.

You can use "OuterEnvelopes" to accomplish the same things as prefixes, though it is more work to read the message (though not more work than parsing a prefixed message) and less human-readable (unless you make a custom probe). It may useful, though.

-- James

Note: I put an OuterEnvelope ability in my messaging design, but I've never actually used it yet, so I'm speaking theoretically.

Link to comment

Now, if he did want to separate the "Slave1" and "ActionDone" parts (for example, to use the same "ActionDone" code with messages from both slaves using the slave name as a parameter) then he could parse the message name on the ":" or other separator.

Actually, in rare situations I do parse the sender name from the rest of the message. Since the master can't exit until it receives exit messages from the slaves, sometimes I'll combine all the exit messages into a single case and keep track of who has sent exit messages via a feedback loop. It's contains the functionality a little more than having separate handlers for each slave's exit message.

there's even a pattern used, "Decorator"

Structurally what you're describing is, if I'm understanding correctly, similar to the decorator pattern but it is not (imo) a decorator. Patterns are defined by their intent, not their structure. Decorators are optional composable bits of functionality. An object can be wrapped in 0..n decorators and still behave correctly. I don't think what you're describing quite fits.

Instead of using a "PrefixedMessageQueue", use a variation of that idea that I might call an "OuterEnvelopeMessageQueue". The later queue takes every message enqueued and encloses it as the data of another message with the enclosing messages all having the name specified by the Master ("Slave1" for example). When the Master receives any "Slave1" message, it extracts the internal message and then acts on it, with the knowledge of which slave the message came from.

All problems in computer science can be solved by another level of indirection... except for the problem of too many layers of indirection.

Forgive me for saying so, but applying that in this particular case smells of too much indirection. I don't see that it provides any real benefit in this use case. One place where I have considered boxing messages in other messages is with errors. It's just something I've thought about though... haven't tried implementing anything yet.

Link to comment

Structurally what you're describing is, if I'm understanding correctly, similar to the decorator pattern but it is not (imo) a decorator.

You're right, it doesn't really fit the intention of the "Decorator pattern", but structurally it is very similar.

Forgive me for saying so, but applying that in this particular case smells of too much indirection. I don't see that it provides any real benefit in this use case.

In the mental image I use for my messages, envelops with contents inside and a label on the outside, having the Messenger place the received envelope inside another envelop and labeling it "This is a message from from Slave1" doesn't seem confusing to me. Not much more than altering the label of the original message and then feeding into a parser, anyway.

Just an idea; as I said, I have never used it yet.

-- James

Link to comment
  • 2 months later...

Just an idea; as I said, I have never used it yet.

FYI from a couple of months later: I have now made use of "outer envelopes". They were very useful in the writing of TCP Messengers for my message-sending reuse framework, allowing the sent messages to be packaged inside outer envelopes carrying labels that mean something to the "Client" and "Connection" actors that run the TCP communication. Using the outer envelope label obviated the need for any parsing or inspecting of a message to determine what to do with it, and led to clearer code. For example, the "Client" receives envelopes labelled "Send Via TCP", while replies to messages, to be routed back through the TCP connection, are received by the "Connection" actor in envelopes labelled "Route Back Message".

Note: all use of these outer envelopes is internal to the TCP messaging structure, and are completely transparent to the processes at each end of the connection, which do not need to do any marking of messages themselves.

-- James

post-18176-0-37102800-1325592819_thumb.p

Part of "TCP Client Actor" where messages to be sent through TCP are received inside "outer envelopes" marked "Send Via TCP" (the marking is done by the "RemoteTCPMessenger" class to which the messages are initially "sent").

Link to comment
  • 1 month later...

As per Daklu's request in this thread, copy and pasting discussion on the slave loop idea.

Hey Daklu,

Yeah it is probably possible. Right now I'm sipping my morning coffee and going over your Slave Loops concept. I feel that it's probably very similar to what I'm trying to do with my code but implemented in an OOP sense. Hopefully I can draw some parallels and a light will click. I'm particularly interested in the idea of launching and shutting down a slave process as needed.

I've gone through a really torturous route of wrapping each process inside a message handler framework, with an FGV pertinent to each process which is solely responsible for passing the stop message from the message handler to its process. The "process-nested-inside-message-handler" is in turn launched by a master message handler which handles input from the user to determine what tests should take place. I just had a blinding realisation as I wrote this that I could have handled this much simpler with a 0 time out queue in each process that listened for a stop message and if timed out did whatever the process was trying to do. One of the key assumptions that led me down my torturous path was that I must have a default case that handles messages which don't correspond to real messages. *Realisation* In fact, I could just use the timeout line on the queue to nest another case structure and still handle the default (incorrect message) case, gah, so much time wasted.

From looking at your slave loops template posted in this thread, am I right in assuming that if you wanted a different slave behaviour (a new plug in if you will), that you would merely make a new execute method in the SlaveLoop class? Or would you instantiate a whole new child class of SlaveLoop.lvclass with its own execute method?

Following on from my confession of my own wrangling efforts, how would you formulate one of your slave loops that needed to be a continuous process but also accept stop commands, would you use the timeout method I thought of above, or something else?

What would you do if you wanted to be able to close and reopen your slave loop without locking your code up? What I'm getting at here is, what is the OOP equivalent to launching a sub-vi using a property node with "Wait Until Done" set false?

Thank you for the opportunity to pick your brains! Your posts are always amazingly informative, even if it does take me months for things to finally click into place...

Edited by AlexA
Link to comment

Hi Daklu,

Grrr, I know what you mean, I just lost a post :(.

Anyway, I was saying that I've figured out that new slave loops should be children of your parent class with different execution loops (blindingly obvious in retrospect). I've struck something strange though. The children don't seem to actually inherit the parents data type (the messenger queues). The same messenger queue types need to be placed into the child data.ctl file manually or else the childs execution loop, which I've copied from your parent and exchagned the object type from parent to child, is broken.

From my own experimentation and understanding, children should always inherit all the data fields of their parents, even if they're not visible on the child data.ctl front panel, correct me if I'm wrong.

Here are some pictures showing what I mean.

With inheritance checked in the class properties but the data not manually moved onto the child data.ctl file:

JPdRq.png

With inheritance checked and the data moved in manually:

ECJPi.png

Your thoughts?

Cheers,

Alex

Edited by AlexA
Link to comment

Hi Alex,

One slightly annoying thing at first about LVOOP child classes is that they cannot directly access their own “parent” data via “unbundle”. All child objects have the parent private-data cluster, but methods of the child class need to access it via methods of the parent class. If you create VIs in the parent that return InputQ and OutputQ, then the child class VIs can use them instead of unbundle. Creating “accessor” VIs like this is automated, so it takes less than a minute to make several. One can even make them be usable in a Property Node structure to access several things at once.

The reason for this, I believe, is to allow the designer of the parent class to have a well-defined public interface, and thus he/she can change the internal private structure without breaking any children written by other developers.

Edited by drjdpowell
Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.