Matt_AM Posted November 19, 2020 Report Share Posted November 19, 2020 Hey all, I want to start my first big design in LV with OOP by laying out and designing a motor class. I was wondering if there are any tools that I should look into to help me with the process. I've built a power supply class which is basically an abstract factory where all the children create the concrete classes. I am still trying to understand a lot of the different architectures/design patterns from the LV forums but am getting a better understanding each time I go through them again. My biggest issue I can see is having child classes return different data types without it getting overly complicated (any suggestions on designs on this front are more than welcome). Digressing back to the original question, As of right now I am looking into GOOP, but its occasionally crashing LV 2015. It seems like theres some good stuff with GOOP, but the lack of documentation is a bit rough to slog through. I was wondering if you fine folks could recommend any tools for me to look into as well. I'm sorry this is a generic request, but I don't know what I don't know. Matt Quote Link to comment
ThomasGutzler Posted November 19, 2020 Report Share Posted November 19, 2020 Factory is a good way to create your instruments but you probably want one factory for all instruments rather than one for each type. That way you only pass the parent (abstract) reference around, which makes it easy to swap out one concrete instrument for another of the same type. Returning different data types from classes of the same instrument type is something you don't want. When designing your classes, you should already know what kind of data you expect that class structure to handle and all instruments of one type should be able to put their data into that one given format. Maybe @MikaelH can throw the Introduction to OpenGDS pdf your way. I can't find it. Quote Link to comment
MikaelH Posted November 19, 2020 Report Share Posted November 19, 2020 Hi, I just posted my GOOP Training course material here: https://forums.ni.com/t5/GDS-Goop-Development-Suite/GOOP-Course-Material/td-p/4101113 1 Quote Link to comment
drjdpowell Posted November 20, 2020 Report Share Posted November 20, 2020 18 hours ago, Matt_AM said: My biggest issue I can see is having child classes return different data types without it getting overly complicated (any suggestions on designs on this front are more than welcome). You need see past your specific motor types to think what your generic motor needs to do. Is it returning configuration info? That can be JSON, rather than a typed cluster. Or a Variant, or an object of a "config" subclass. My advice is to NOT learn OOP with a Motor Parent class unless you have enough experience with different motor types so that you can form a solid view of what a generic motor does. Quote Link to comment
Matt_AM Posted November 20, 2020 Author Report Share Posted November 20, 2020 @MikaelH Thanks for the links good sir, I appreciate it! @ThomasGutzler What do you mean by "Returning different data types from classes of the same instrument type is something you don't want."? I'm assuming you mean something like use the parent "Power Supply" object for my connector panes and define the child (such as TDK Lambda) during the initialization section of my test. This way the if I wanted to change the PS from TDK Lambda to say Sorenson, all I'd have to do is change the test's initialization section since all my connector panes are using the Power Supply parent class in their connector pane. If this is the case, I am doing that already, I may just be bad with my vocabulary. @drjdpowell Fair point, learning to walk makes running a lot easier. I guess what I am currently trying to do is define my end goal and figure out the different steps along the way. Then start working on the individual steps to build up to my end goal. Like for a generic motor (in my case) I'll need a power supply class and CAN communication class as my main two classes along with some other private methods/data (ignition task and methods, motor info, stuff like that). As far as the CAN coms, I am actually running into a bit of an issue trying to figure out how exactly I want to lay it out. I'm using XNET and I'm trying to figure out how I should handle having things like single point signal/frame as well as queued signal/frame. If I go down the splitting out all of those into their own child class, then I'm going to have 4 child classes for my basic XNET write class. OR I could try and go into a strategy pattern where the XNET class has some other class information that contains how the data is supposed to be written (something like the CookingStrategyPattern from here.) Thanks all for responding, I appreciate the help! Quote Link to comment
Neil Pate Posted November 21, 2020 Report Share Posted November 21, 2020 (edited) On 11/20/2020 at 5:30 PM, Matt_AM said: @ThomasGutzler What do you mean by "Returning different data types from classes of the same instrument type is something you don't want."? I'm assuming you mean something like use the parent "Power Supply" object for my connector panes and define the child (such as TDK Lambda) during the initialization section of my test. This way the if I wanted to change the PS from TDK Lambda to say Sorenson, all I'd have to do is change the test's initialization section since all my connector panes are using the Power Supply parent class in their connector pane. If this is the case, I am doing that already, I may just be bad with my vocabulary. I think what Thomas meant is that if you are going to use Dynamic Dispatch (DD) then you are forced to have the same data type as the connector pane of the concrete DD VIs all have to be identical. If you already have something working its probably ok. As an example you cannot have Instrument 1 return an array of DBL and Instrument 2 return an array of SGL for a "Read.vi". Edited November 21, 2020 by Neil Pate Quote Link to comment
ThomasGutzler Posted November 22, 2020 Report Share Posted November 22, 2020 On 11/21/2020 at 2:30 AM, Matt_AM said: @ThomasGutzler What do you mean by "Returning different data types from classes of the same instrument type is something you don't want."? I'm assuming you mean something like use the parent "Power Supply" object for my connector panes and define the child (such as TDK Lambda) during the initialization section of my test. This way the if I wanted to change the PS from TDK Lambda to say Sorenson, all I'd have to do is change the test's initialization section since all my connector panes are using the Power Supply parent class in their connector pane. If this is the case, I am doing that already, I may just be bad with my vocabulary. What Neil said. Looks like you've got it worked out. The hardest thing to get your head around is the factory instrument creation. This is the only place where you might not use dynamic dispatch to call instrument specific vis containing their specific configuration. But as @drjdpowell said, you can use JSON for that. Quote Link to comment
FixedWire Posted November 23, 2020 Report Share Posted November 23, 2020 Just be really careful if you intend to used packed libraries. Start building the exe & ppls now. It is not trivial. It's of limited use if it can't be deployed. This is the best documentation I've found yet. Effectively_Using_Packed_Project_Libraries_SEPAD.pdf 2772 KB https://forums.ni.com/t5/NIWeek-Session-Content/Software-Engineering-Processes-Architecture-and-Design-nbsp/ta-p/3929895?profile.language=en Quote Link to comment
Matt_AM Posted December 23, 2020 Author Report Share Posted December 23, 2020 On 11/21/2020 at 11:32 AM, Neil Pate said: I think what Thomas meant is that if you are going to use Dynamic Dispatch (DD) then you are forced to have the same data type as the connector pane of the concrete DD VIs all have to be identical. If you already have something working its probably ok. As an example you cannot have Instrument 1 return an array of DBL and Instrument 2 return an array of SGL for a "Read.vi". This is where my hiccup is, I am using the factory template for my power supply class since I know what my outputs and inputs are. I can use the DD to set/read my voltage, current, or output state since I know they will be double, double, and boolean respectively. I was thinking about using the factory pattern for an XNET Read class, but there are 3 basic methods to read from XNET - signal, frame, and XY. Theoretically, i could force all of these into an XY format. I am trying to figure out how I can use something like a dynamic dispatch to read anything from XNET without having to create 3 specific XNET read subclasses to an XNET read class. And then taking that further, what about a generic read class where I could read DAQmx data as well as XNET data? My current thought is so have a "Read" and "Read Type" class used to build my XNET read. Read would be an abstract class containing queues and events so my Read Type Class knows where to send the data. Read Type would be another class that would implement how I want to read this specific type of class. For example, Read Tye could be XNET signal/frame/XY or DAQmx Analog/digital/counter since the read type class is my strategy of how I want to read my data. Quote Link to comment
Neil Pate Posted December 23, 2020 Report Share Posted December 23, 2020 3 hours ago, Matt_AM said: This is where my hiccup is, I am using the factory template for my power supply class since I know what my outputs and inputs are. I can use the DD to set/read my voltage, current, or output state since I know they will be double, double, and boolean respectively. I was thinking about using the factory pattern for an XNET Read class, but there are 3 basic methods to read from XNET - signal, frame, and XY. Theoretically, i could force all of these into an XY format. I am trying to figure out how I can use something like a dynamic dispatch to read anything from XNET without having to create 3 specific XNET read subclasses to an XNET read class. And then taking that further, what about a generic read class where I could read DAQmx data as well as XNET data? My current thought is so have a "Read" and "Read Type" class used to build my XNET read. Read would be an abstract class containing queues and events so my Read Type Class knows where to send the data. Read Type would be another class that would implement how I want to read this specific type of class. For example, Read Tye could be XNET signal/frame/XY or DAQmx Analog/digital/counter since the read type class is my strategy of how I want to read my data. Sounds... Complicated. I honestly think OOP designs like this are generally more trouble than the return you get from it. Quote Link to comment
LogMAN Posted December 23, 2020 Report Share Posted December 23, 2020 You use some patterns in the wrong context. 4 hours ago, Matt_AM said: I am using the factory template for my power supply class since I know what my outputs and inputs are. 4 hours ago, Matt_AM said: I was thinking about using the factory pattern for an XNET Read class, but there are 3 basic methods to read from XNET - signal, frame, and XY. The Factory pattern is only a means to create new objects without explicitly referencing them. Here is a very simple example using a Boolean to select from two classes. In this example, Class 1 and Class 2 both are children of Superclass. Depending on the input value, one of them will be returned. The caller doesn't need to know how to create those objects and only sets the input value to True or False. A typical implementation uses a String instead of a Boolean, which allows for many more options and adding more classes later. In any case, the output of a Factory is the most common ancestor of all possible children (Superclass in this example). 4 hours ago, Matt_AM said: I am trying to figure out how I can use something like a dynamic dispatch to read anything from XNET without having to create 3 specific XNET read subclasses to an XNET read class. And then taking that further, what about a generic read class where I could read DAQmx data as well as XNET data? Dynamic dispatching is not something that magically merges different functions into one. It is only a way to change the behavior of a function at runtime. Perhaps you are familiar with Polymorphic VIs. Dynamic dispatching is actually very similar to polymorphism. The difference is how they are resolved. Polymorphic VIs are resolved at edit time. Dynamic dispatch VIs on the other hand are resolved at runtime. This is why dynamic dispatch VIs must always have the same terminal pattern. This is of course a very simplified explanation. For your particular question, there are two parts to it: Create specific subclasses for each type of power supply - For example, XNET and DAQmx are entirely different technologies, so it makes sense to have separate classes for each. Use the Strategy Pattern to change the behavior of a particular method - For example, your XNET class could use different strategies to do the actual read operation (XY, double, etc.). The Strategy Pattern encapsulates an operation in an object. You need to write one object for every possible strategy and then provide the desired strategy at runtime (i.e. using a Factory). Here is a basic example. In this example, XNET is a subclass of Power Supply, which has a Read Data method that returns an array of double. When XNET is created, the desired read strategy is passed as an argument. The strategy has another dynamic dispatch method to do the actual read operation. The Read Data method then uses the strategy to read the data. DAQmx would work similarly, perhaps with its own set of strategies. I believe that this comes very close to what you have in mind. 5 hours ago, Matt_AM said: My current thought is so have a "Read" and "Read Type" class used to build my XNET read. Read would be an abstract class containing queues and events so my Read Type Class knows where to send the data. Read Type would be another class that would implement how I want to read this specific type of class. For example, Read Tye could be XNET signal/frame/XY or DAQmx Analog/digital/counter since the read type class is my strategy of how I want to read my data. Don't put events and queues inside the reader class. Instead, have a separate class that uses the reader class to produce events or populate queues (these should be separate classes altogether, one for queues and one for events). I suggest you play around with the different patterns to get used to them before you use them in production. OOP can get confusing very quickly if you are only used to functional programming. Quote Link to comment
Matt_AM Posted December 28, 2020 Author Report Share Posted December 28, 2020 On 12/23/2020 at 4:01 PM, LogMAN said: Dynamic dispatching is not something that magically merges different functions into one. It is only a way to change the behavior of a function at runtime. Agreed completely. I understand that DD is basically allowing the child to redefine a method via overwrite or extend it via overwrite w/ calling the parent's method. Because of this, as you said, all the connector panes for the DD VI must be the exact same. So, if I want to be able to read multiple types of data from a class, DD won't work for a genera "read", hence my question. On 12/23/2020 at 4:01 PM, LogMAN said: When XNET is created, the desired read strategy is passed as an argument. The strategy has another dynamic dispatch method to do the actual read operation. The Read Data method then uses the strategy to read the data. DAQmx would work similarly, perhaps with its own set of strategies. You are correct, this is basically what I am thinking. I think having a separate read type class that implements how the data is read, or its strategy, is how I can get around multiple read types. From there I was thinking about sending the data via queue to a different "display class". This way, the read type class can have a DD VI called read, where it calls the child class read function (XNET signal/frame/XY) and send it to the display via queue. The queue control will most likely be a variant and enum, variant being the data read from read type and enum being the read type it came from (I've seen that I'm not supposed to send classes via queues so this is my work around to properly cast my variant to the correct data). On 12/23/2020 at 4:01 PM, LogMAN said: Don't put events and queues inside the reader class. Instead, have a separate class that uses the reader class to produce events or populate queues (these should be separate classes altogether, one for queues and one for events). When I was talking about events/queues, I mean more like a queue to send my data (not read) or an event struct with a 0ms timeout to monitor if a stop condition occurred. I understand I don't want to induce additional delays in my read to avoid timeouts. Quote Link to comment
LogMAN Posted December 28, 2020 Report Share Posted December 28, 2020 1 hour ago, Matt_AM said: I've seen that I'm not supposed to send classes via queues so this is my work around to properly cast my variant to the correct data Not sure where you got that. It's a valid approach: Command pattern - LabVIEW Wiki The Actor Framework, for example, takes this idea to the extreme. 1 hour ago, Matt_AM said: When I was talking about events/queues, I mean more like a queue to send my data (not read) or an event struct with a 0ms timeout to monitor if a stop condition occurred. I understand I don't want to induce additional delays in my read to avoid timeouts. I'm not a fan of the 0ms timeout case because it adds unnecessary polling. The rest sounds good to me. It is probably best if you build a prototype to see what works best for you. Quote Link to comment
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.