Jump to content

dealing with input types on dynamic dispatch


Recommended Posts

I am trying to wrap my head around something i've been dealing with lately:

I want to implement a "Messenger" Class for facilitating a switch of communication interfaces in my programs. Basically my current thought process is that I have:

- A parent class called "Messenger" with the methods: initialise(?), connect(), [U8] recieveData(), sendData([U8]), disconnect(), that I can use in my code.
- A child class "TCP Messenger", which overrides the methods: initialise(IP, Port, Timeout, DVR, ConnectionType), connect(), [U8] recieveData(), sendData([U8]), disconnect().
- A child class "Serial Messenger", which also overrides the methods: initialise(Port Number, Timeout, DVR, ConnectionType), connect(), [U8] recieveData(), sendData([U8]), disconnect().
- A child class "Profibus Messenger", which again overrides the methods: initialise(FPGA ref, SlaveAddress, ConnectionType), connect(), [U8] recieveData(), sendData([U8]), disconnect().

...

I would like to change the communication type by simply loading a different class and letting dynamic dispatch do the rest.

All of the methods have similar interfaces, except the initialise method, as every type of connection necessitates different types of parameters. Everything else is basically getting byte Arrays and setting Byte arrays. How would one go about solving this? Do I just create an Initialise method with every possible parameter and only use what I need in the specific implementation?

Robert

Link to comment

Since the connector panes of a child and its parent need to be exactly the same, you need a way to make the passing of parameters generic: either the path of an INI file, or a variant (the child method will know how to cast it back to a cluster), or a string (XML or JSON)...

Keep in mind that the child object will contain both a set of parent private data and a set of child private data. So put any common parameter in the parent private data, and any child-specific parameter in the child private data.

Link to comment

Those work, but you could also just make property node accessors for your class, initialize the parameters you need, and then call init() with no parameters (because the parameters are inside the class). I personally don't like this style but it works as long as you have good defaults.

What I prefer is two classes: "messenger" and "messenger config". You put all your property nodes on "messenger config", then call init() on the config class. Internally, the config class can know what variety of messenger it belongs to and produce right one. I prefer this style as you can add helpers to the config class (like to/from string) and you don't have a bunch of properties on your main (messenger) class where it isn't clear what setting them will do -- for example, will setting baud rate after you call init actually change baud rate or do nothing? There is a line in the sand between the config and the running states.

However this much effort is only useful if you're instantiating these things yourself (as the programmer). If you're just sending over a file which says 'load me these interfaces', you may as well just do init(string cfg) cause why not.

Link to comment
6 hours ago, smithd said:

Those work, but you could also just make property node accessors for your class, initialize the parameters you need, and then call init() with no parameters (because the parameters are inside the class). I personally don't like this style but it works as long as you have good defaults.

What I prefer is two classes: "messenger" and "messenger config". You put all your property nodes on "messenger config", then call init() on the config class. Internally, the config class can know what variety of messenger it belongs to and produce right one. I prefer this style as you can add helpers to the config class (like to/from string) and you don't have a bunch of properties on your main (messenger) class where it isn't clear what setting them will do -- for example, will setting baud rate after you call init actually change baud rate or do nothing? There is a line in the sand between the config and the running states.

However this much effort is only useful if you're instantiating these things yourself (as the programmer). If you're just sending over a file which says 'load me these interfaces', you may as well just do init(string cfg) cause why not.

so am I correct in assuming that what you are suggesting is that I implement a config class of which i pass the correct child instance to the initialise() method of the messenger class and the child instance gets its init data from there?

Link to comment

I spent many hours trying to solve similar problems only to come to the conclusion that the solution I'm looking for makes no sense logically.

Asking for Dynamic dispatch to take care if which VI to call is only valid if the connector pane in invariant.

As soon as the connector pane is no longer invariant, then Dynamic Dispatch alone cannot know what to do.  You can either move to a generic dat ainput type (A solution I personally am not fond of) or you can implement some VIs to pass in arguments BEFORE calling "Initialise" (as others have pointed out).  The second version has the advantage of allowing a new "Initialise" to be called ont he object whenever you might want that as all of the parameters are internalised.

14 minutes ago, kull3rk3ks said:

so am I correct in assuming that what you are suggesting is that I implement a config class of which i pass the correct child instance to the initialise() method of the messenger class and the child instance gets its init data from there?

I wouldn't do this as it doesn't solve your problem, it actually only makes it worse because instead of casting from Variant to whatever data you require in your ACTUAL Initialise funciton, you are trying to cast objects which is (AFAIK) less efficient.  Doing this with classes versus variants brings nothing new to the table.  You can still wire in the wrong configuration class and get run time errors.  The option with internalising the parameters into the object before calling "Initialise" can at least catch type errors (even if it can't catch missing VIs to set certain properties).

Of course the other option is to have a dedicated "Initialise" method for each class and ditch Dynamic Dispatch for this function altogether.  By designing an "Initialise Device X" and "Initialise DEvice Z" methods independently of each other, you can then have the connector pane exactly as you require it with inputs declared required and strict typing.

Of all the methods mentioned here (assuming re-initialisation is not a big need) I would simply create non-dynamic dispatch initialise VIs.

  • Like 1
Link to comment
11 minutes ago, shoneill said:

Of course the other option is to have a dedicated "Initialise" method for each class and ditch Dynamic Dispatch for this function altogether.  By designing an "Initialise Device X" and "Initialise DEvice Z" methods independently of each other, you can then have the connector pane exactly as you require it with inputs declared required and strict typing.

You can also wrap the dedicated inits in a polymorphic VI to give the appearance of a single init (with different inputs-unlike a class) and maintan the design time polymorphism of the class.

  • Like 1
Link to comment

Poly VI works as long as at least the connector pane pattern is the same, even if the data types of the individual connectors is different, this is true.

If the code is rapidly changing, the mainteainance of the poly VI can be a pain though.  Apart from that, I agree it's a very nice way to kind of get the best of both worlds.

Link to comment
On 8/30/2016 at 2:55 AM, kull3rk3ks said:

so am I correct in assuming that what you are suggesting is that I implement a config class of which i pass the correct child instance to the initialise() method of the messenger class and the child instance gets its init data from there?

Not quite, an important point is moving the init to the config class -- the main class can also have an init method, but making the public API be config.init instead of messenger.init means its type safe the whole way through. The downside is you must create a config class for each messenger class (even if the only change is a constant on the diagram).

On 8/30/2016 at 3:14 AM, shoneill said:

I wouldn't do this as it doesn't solve your problem, it actually only makes it worse because instead of casting from Variant to whatever data you require in your ACTUAL Initialise funciton, you are trying to cast objects which is (AFAIK) less efficient.  Doing this with classes versus variants brings nothing new to the table.  You can still wire in the wrong configuration class and get run time errors.

I'm pretty sure casting is faster if it doesn't fail because its just moving pointers around. Could be wrong, but it also doesn't really matter here.

That having been said, see above for my clarification. 

On 8/30/2016 at 8:43 AM, ShaunR said:

If you look at this post (see the attachment). Yair created a transport class for some interfaces. He adds a serial a couple of posts below so that maybe useful to you.

Its also worth pointing out that if you ever share this with anyone, the messenger name (generic as it is) is already used here: http://sine.ni.com/nips/cds/view/p/lang/en/nid/213091
You might consider renaming to something simple like "bytestream" or whatever, since that more closely matches what it currently does.

Edited by smithd
Link to comment
1 hour ago, smithd said:

Its also worth pointing out that if you ever share this with anyone, the messenger name (generic as it is) is already used here: http://sine.ni.com/nips/cds/view/p/lang/en/nid/213091
You might consider renaming to something simple like "bytestream" or whatever, since that more closely matches what it currently does.

Ask the systems team at NI. They are good with names for NAFFISMs. :lol:

Edited by ShaunR
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.