Jump to content

MarkCG

Members
  • Posts

    147
  • Joined

  • Last visited

  • Days Won

    17

Posts posted by MarkCG

  1. I like it. There's so many ways you can combine clusters, arrays, classes, and variant hashes.. I can recall at some time or another storing all sorts of data including classes inside variants for name lookup, iterating over arrays of classes containing arrays of classes, making arrays of clusters of arrays, many clustosauruses,   etc etc etc etc. Having a generic solution is really useful and hopefully leads to cleaner / easier to interpret code.. What would be very useful too is a way to visit every element in the tree.  

  2. Prefixes -> Use libraries/classes instead (you'll get a namespace 'Library.lvlib::Member.vi'), how would that look with prefixes: 'prefix_Library.lvlib::prefix_Member.vi' ( :nono: )... You have to organize the files on disk to prevent name conflicts though. If there is a need to prefix everything, just change the library/class name (linking issues are conveniantly easy to solve this way in my experience).

    Spaces -> One of the most obvious things that make LabVIEW different to languages like C. Good for starters and easy to read. However if you build DLL files (or enable ActiveX afaik), spaces are a problem.

    CamelCase -> Sometimes hard to read and maintain, but in combination with libraries the most flexible one in my opinion. Makes it easier to reason with people from the other side.

    Can't agree more with this. I find libraries full of VIs prefixed with the library name really annoying because the unnecessary duplication, and it makes it difficult to pick out the actual VI name when looking at the title bar. And if you are sticking members of different libraries in the exact same folder to where you NEED prefixes, may God help you...
  3. I like to store my refs (queue, event etc) in LV2 style VIs (functional globals). That way you do not need to litter reference wires in shift registers all over your diagrams.

     

    What bothers me about this is that it seems to defeat parallelism somewhat. Two loops that really ought to just be sharing data via a the queue are now have to depend on a non-rentrant VI to get the queue. Say we have non-empty queue, and A is the producer loop and B is the consumer. If loop A is calling the LV2 global to get the  queue to in order to enqueue data, B has to wait until the LV2 global is finished executing in order to dequeue data that is already available. 

     

    I'm sure the performance hit is negligible, but it's the principle of the thing that bugs me. I like to pass queues into through front panel, in a cluster if need be. 

  4. One of a TC's advantages is that they are cheap and have very low time constant, meaning you can measure rapid temperature variations. The bare TC tip is small and is easy to get into good thermal contact with the thing to be measured.  RTDs are more expensive and physically bigger. In the HVAC industry they'd twist TC bare wires together, and solder it to copper tubing of the air conditioners. When test is over, you just cut the wire off near the tip.  

    • Like 1
  5. Hi,

    Your hunch is potentially correct. There is a certain amount of overhead associated with each shared variable (time stamp, FIFO if selected) and so grouping elements can help reduce the processor associated with it.

    Don't forget though this means larger memory usage everywhere you access it (as you read the whole cluster), increases network usage (as all data is sent one one update) and may force some nice race condition risky read-modify-write actions if you have to update a single element so it isn't without cost! They shouldn't bee too large.

     

    James, I am glad someone else shares my hunch on performance. I have given that a lot of thought to race consitions. I really hate and abhor "signaling globals" but have done the best I can to mitigate. All NSVs are in either a "control"  library or an "Indicator" library. NSVs in the indicator library are only written to in one place in the target/cRIO code, and all elements at once. 

     
    Control NSVs will be written in only one place in the HMI /host code. "Signaling" NSVs that need to set a setpoint consist of a cluster with a "set?" boolean and the numeric quantity that is the setpoint, to avoid race condition between the "set?" flag and the setpoint. 

     

     

    Be a little cautious when choosing NSVs over some other comms technology.

     

    There is no other tool in LabVIEW that will make you go grey quicker than when your shared variables are not working. My (and other developers I have spoken with) experience has been that either they work perfectly, or are a complete dog, there is no middle ground.

     

    Personally I will never touch them again. Network Streams are my current "protocol" of choice for communicating over IP.

     

     

     

    Neil, I also share your distrust of NSV based on what I have heard. In the past I have used Network Streams and the Current Value Table and was pretty happy with the performance.

     

    I should probably give a little background as to my choice in using NSVs in the first place. The reason I am using them is that I have inherited one of the usual monsters (one giant block diagram, interloop communication via local variables) and my task is to port it to cRIO . 

     
    The client does not want to re-write it from scratch yet due to number of reasons. (no spec exists, it was grown "organically" let us say). Fortunately they are very aware that it's a mess, I decided that at least modularizing, undoing some of the local variable mess, and using shared variables to get the data out is the best option,  

     

     

    For us they work on middle ground, we have intermittent software failures due to NSVs not being reachable in between. In this case restarting the component (but not the SV Engine) brings us back to live. Some other workpackage reported incidents where the SV Engine lost all it's values and needed to be restarted, but I didn't see this personally.

     

    But actually we're using the SVE for a system size for which it clearly wasn't designed.

     

     

     

    Flintstone, this does scare me. I have worked with NSVs running on PXI chassis, there where about 20 different machines, each with a library of say 50-60 NSVs. They seemed pretty easy to connect to via the Distributed System Manager. But then again that was PXI and not cRIO. I know NSVs had a whole lot of problems in the early days. It usually takes a few years to work out the bugs with any NI technology as far as I have noticed. Hopefully they are ok now...

  6. Hi all,

     

    I am working on a typical HMI+cRIO 9074 control system... porting over / lightly refactoring some code that was written to run on a PC.  There are going to be quite a few variables to keep track of, but many of them are logically related and I would internally keep them in type -defed clusters. Instead of creating individual shared variables for every single boolean, floating point and string,

     

    I am thinking that grouping the variables into typedefed shared variables will be good for managing the information, and also improve performance (reduce CPU load on cRIO) by reducing the total number of shared variables.  

     

    Is my hunch about performance correct or will this be immaterial?

  7. Thank you for the advice! I didn't want to call, and go through the process of explaining the whole thing/ waiting on phone. I'll definitely use the online service request, thank you for that pointer Antoine. I will post CAR # when I get it Mr Mike. And to Darin and AQ, it's not so much a proprietary thing as that I was advised that I should try to disclose in a responsible way given nature of bug.  

  8. I discovered, by accident, a way to render a library or class corrupt, by doing a certain operation on it from within LabVIEW. Breaking the lvlib also breaks its member VIs, until they are unlinked.  No, it does not involve munging the XML or raw text of the lvlib. I have demo VI but am a bit loathe to just put it out there for the world. What should I do? What's a good place to report it?

     

     

  9. Thank you Ned for the very nice Visitor pattern example!  The data object of a given type has a do method, that takes in a visitor of whatever type  , and executes a that visitor "Visit" method specific to that data type. (visitIntegerDataType eg) .

     

    I can see now how setting things up this way could be very useful. You have visitors that do all kind of different things, but you have to only write the iteration code once-- in this case it's just a for loop in DataItemCollection.lvclass, but I can see that if you had oh, binary trees or oct trees or graphs doing it that way would be awfully useful, since visiting every member there involves a bit more than dropping a for loop on the block diagram. Very cool.. glad I learned something today!

  10. This is what I went with...using the class that already has the reference as the visitor. I have dug into AQs example on NIs site and am having a little bit of trouble understanding, mostly due to the fact that it doesn't look like the wiki examples (albeit they are text are in text languages). It looks to me basically like the command pattern (I understand they are similar). I don't see anywhere a "visitable" object is being given a visitor, unless I'm just not thinking about it correctly, which is most likely the case . It seems to me some of this may stem from the fact that we can determine at run-time if an object matches a specific type or not (by using to more specific class) so there is no need to have all the polymorphic functions for different visitable types (i.e. the second dispatch). For instance, in his class that is incrementing, he just does a cast and if there is an error because it isn't the correct type, he doesn't call the increment function. I may be off base here, but I am continuing on, getting this to work, and I can come back and adjust as need be later. In the mean time, any further clarification is always appreciated.

     

     

    I have looked at that example before as well, and I don't get it either.

     

    In my admittedly poor understanding, it involves a "visitor" class iterating through a collection of objects and performing some operation on each one. The collections could be something as simple as an array or something like a tree or graph of objects. The visitor "knows" how to get to (well, visit I suppose) every object in that collection, and then do whatever it's supposed to do to that object. So in your case, the "thing" to iterate over, is maybe an array of channel classes, each with an array of check classes. And the thing to do is to execute the check and take action if necessary. 

     

    I see it like this-- here's some ASCII art: 

    x's channel classes, o's are check classes

     

    x x x x x x x   channel class array

    o o o o o o o   c

    o o   o   o o   h

    o     o     o   e

    o               c

    o               k

                    a

                    r

                    r

                    a

                    y

                    s

     

    basically the visitor knows how go through the class array, and for each class array go through the check array. Basically nested for loops.

     

    AQ is this at all close? 

  11. I recently did something pretty similar-- each channel represented by a class, with a variable number of different condition check objects stored in an array in each channel class, all configured via a spreadsheet file. I went the route of create a manager class that would iterate through all the channel  object condition checks and at the end decide what action to take. 

     

    My approach would have the condition checks classes send messages to "DO output" actor / loop. The DO output loop holds on to the actual DAQmx task, and it can decide when and how to set the digital outs. You could cause the DO object to set lines only every 100 ms or whatever, and then at that time determine what to do based on what messages it received in the last 100ms

    • Like 1
  12. That's the one! Of course the VI just makes a call to a bunch of system DLLs, so I don't think it would work on Linux/Mac/Etc. Ultimately you're really not gaining anything over .NET other than you don't need to deal with it directly.

    attachicon.gifversion.png

    The snippet above probably doesn't require the file/directory info primitive, but I've always used it as such and not found with any trouble. I'd love to play with some scripting or use j's code to have version info in the IDE, but that's so low on the priority list since ultimately the application is always distributed in built form.

     

    It does look like the OSX version of LV2012 doesn't have anything equivalent to 

    "vi.libplatformfileVersionInfo.llbFileVersionInfo.vi" . Still though, I can see my app's version number under when I "Get Info" on it with Finder. There has got to be a way to get to it-- anyone any ideas? 

  13. Finally, I could still have the "zones to execute" in the manager class. But, each zone could hold a CAN bus reference and they could handle sending out the CAN messages from within themselves. I'm not sure I like the idea of the single CAN reference being held by multiple different zones, though, when it is the manager handling the opening and closing of the reference.

    Why not make the CAN reference an input to whichever zone class methods will use it?

  14. Invariably, in most large projects I do, I end up needing to do one thing which I have not found a clean way to do yet. And that is handle messages between components differently based on state of the component / actor.

    Simple example of what I mean-- a machine that can be in an idle or running state. If machine is off/idle, a shutdown message to the actor or component representing the machine does nothing- the message is read but nothing happens, it is already off. If running, it goes through shutdown sequence, actually controlling physical hardware. Likewise, if running, a "Run message" has no effect.

    In the past, pre-oop, I handled by separating the messaging from the states. Essentially nesting your standard enum+variant dequeue + case structure INSIDE each case of the state machine. So inside the while loop, a case structure for each each state, and a case structure to handle messages inside each case of the state machine. This worked fine and was easy to debug, but it was cumbersome and one had to be very diligent about handling every message in every state correctly.

    Now I see I can apply the State Pattern to solve the above problem-- abstract methods in a parent class, with child "state" classes that have concrete implementations of those methods for each message where needed.

    However, there something about this that doesn't quite sit right with me. Let's see if I can explain it via some pseudo-code

    ParentState

    ----- method_1() {}; //abstract method

    ----- method_2() {}; //abstract method

    ConcreteState_A :: extends ParentState

    -----method_1() {do stuff;} // only method 1 is implemented in State A. Method 2 should do nothing.

    ConcreteState_B :: extends ParentState

    -----method_2() {do other stuff;}// only method 2 is implemented in State B. Method 1 should do nothing.

    ConcreteState_C :: extends ParentState

    ----method_1() {do stuff;}//same implementation as in ConcreteState_A

    ----method_2() {do other stuff;}//same implementation as in ConcreteState_B

    ConcreteState_A does only method 1, ConcreteState_B does only method 2, ConcreteState_C can do either, and the implementation is the exact same as in A and B respectively.

    Clearly I can move the concrete implementations of method_1 and method_2 up to the parent class. Sure. But in order to make ConcreteState_B do nothing when method_1 is called on it, I would have to override method_1 with an empty method!!! Otherwise the parent method_1 will get executed (dynamic dispatch), and something will happen when I don't want it to. Likewise for ConcreteState_A, I will have to override method_2() with and empty method. This seems like a backwards way of doing it.

    I could leave the ParentState methods abstract, and I could move all the method_1 functionality into a function and use method_1 as a wrapper, but I would have to still create overrides for each concrete state that needed to implement method_1, duplicating code.

    I Imagine that I had 20 classes, each needing to be able to implement / not implement a large combination of methods, things would get hairy with the above solutions.

    What is it that I am looking for here? Either of the above methods seems kludgey. Is there a way to explicitly say to a child class--- "I know the parent has concrete method implemented, but YOU are not allowed to execute it!" (do as I say, not as I do :) )

    Are "Interfaces" what I am looking for??

  15. So jet velocity call it "u(x,y)", is in Z direction, is function of x and y, and you want to take the derivative uxx , uyy ,and uxy ? You might try to smooth the data with "2D interpolation scattered.vi" (look for example), found in math palette then take the derivative of the interpolating data/function.

    There are higher order stencils you can use to compute the derivatives and Laplacian as well--- they can be derivatived by Taylor series expansion. This will help in smoothing the derivative function also.

  16. Hi Darin, one little trick to get get around the typedef explosion is to wrap that constant in a subVI- new VI, drop the constant on the block diagram, create and wire it to an output. Make the VI icon look like a cluster constant or whatever you want. The typedef constant can remain in its exploded state and you can drop that subVI wherever you want.

  17. If its rs485, they probably use Modbus protocol. Read up on this. Get the NI modbus library. This will allow you to read modbus registers.

    Read the controller manual, find out where what the registers are. Read registers you need with modbus library vis.

    You will need to typecast those registers to the data type you need. For example, feedback temperature will probably be represented as 32-bit float.

    For example, a modbus 32-bit floating point is contained inside two registers. You need to read both those registers and typecast them to a single precision float.

  18. I just right now was making a serial comms VI library to talk to an instrument and for some reason the VISA line feed auto-append (termination character) didn't do it for me, although Hyperterminal will do the same thing and it worked.

    I just concatenated the "line feed constant" from the string palette with each of my commands when writing, then it worked fine.

  19. I feel your pain Alex. I need to sit down and DO as well. I'll probably never get time to implement anything OO on my current project, but hopefully my next gig will involve making a nice desktop VI from scratch and I can do it in an OO fashion right from the start. Meanwhile I'll do a CLD exercise or two using an OO framework, to give me something to do.

×
×
  • Create New...

Important Information

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