Jump to content

ned

Members
  • Posts

    571
  • Joined

  • Last visited

  • Days Won

    14

Posts posted by ned

  1. Thanks for the reply.

    I took a look at that presentation but I'm not sure it solves my problem.

    It looks like the factory pattern has a vi which determines which sub-class to create an object from. So in my case I can select "serial unit" and I can get a "serial unit" object out. The problem is when they want to edit the "serial unit" object. Since I currently store all my units as "unit" objects (config class), when I try to get a unit that I know is a "serial unit", I get the error mentioned above. I think I'm coming to understand why this does not work. Say I create a new "serial unit" and add this to my configuration object as a "unit". At this point I think I have lost all my "serial unit" specific data, so when I try to go back to access my "serial unit" LabVIEW complains because the "unit" object no longer has my "serial unit" specific attributes.

    Is there a better way of doing what I'm trying to do?

    What you're doing should work. There's no reason your classes should be losing their association with their specific type if everything is coded properly - your serial unit should always "know" that it's a serial unit even if it's passed to a VI that expects a generic unit. You might need "Preserve Run-Time Class" on an output of a VI somewhere.

  2. Oh no, I see what you mean. You mean to create my own lookup table just using an array or whatever, like this right?

    That's one way to do it; another is simply to build an array of all your IDs and use Search 1D Array. Slightly slower but uses slightly less memory; if you're only handling about 30 IDs, I doubt you'll see any difference in speed. Here's a modified version that generates the lookup table on the fly, using Search 1D Array. I don't think the use a build array in a loop in this version will cause noticeable speed issues.

    The array of clusters of arrays looks odd, but in LabVIEW you can't have an array of arrays - it would just be an array with an additional dimension, which forces all the sub-arrays to have matching dimensions. The way around it is to wrap each array in a cluster. If you're expecting roughly the same number of messages from each ID and you know in advance how many messages that is, you could efficiently use a 2D array instead.

    Sorting array-3.vi

  3. Can you change the format of your data? Strings aren't that efficient, and CAN packets are fixed-size. You could convert to a cluster of numeric values, where the first element is the ID. Then, one route would be to use that ID as an index into an array, where each element of the array contains a cluster that contains an array of all data found for that ID. Ideally you would pre-allocate each array, since one reason your example is slow is the use of build array in your loop.

    What output do you actually need? Instead of moving your CAN data around, you could build arrays of indices into the original data, one per ID. This would give you a quick way to retrieve all the data for one ID without making a large number of copies. I've attached a quickly-written sample that demonstrates this, then uses the generated array of indices to extract the data into separate arrays for each node.

    Oops, forgot the attachment - and the "Use Full Editor" button doesn't seem to be working, so I couldn't attach it to the previous post.

    Sorting array-1.vi

    • Like 2
  4. The output of a tree control reference defaults to a strictly typed refnum. (The reference was created by right-clicking the tree control in the front panel->create->reference.) Is there a way to convert this output to weakly typed?

    For most refnums that appear in the front panel there is an option to switch between strict and weak types by deselecting "Include data type." But this method does not seem to be available for the VI server reference. How does one specify a weakly typed output from a tree control reference?

    Why do you need a weaker reference? LabVIEW will automatically upclass references when necessary (so if you build the tree reference into an array of references of other types, it will make an array of the most specific common parent). If you want to do this explicity, use "To More Generic Class."

    Also, I think you might be confusing two types of references. The reference you have right now refers to the tree on your front panel. If you right-click on that reference and choose "Create constant" then you'll have a reference for which you can specify the class. That constant is simply a type specifier, so it can be as specific or generic as you want.

  5. What happens if I have 3 while loops trying to enqueue elements to the same queue? Is the queue "locked" while in the process of being enqueued? If this is the case, do the while loops wait until the queue has unlocked and then grab it for their own enqueue operation?

    Your image shows three loops trying to dequeue, not enqueue. This will not work, since each element in the queue can only be dequeued once, after which it's no longer in the queue. Which loop will dequeue a particular element is random. You should consider using a notifier, if you don't need to capture every element, or use three separate queues.

  6. I already wrote the array implementation - that's where I started, and it worked. As a weekend problem I was investigating other ways to do it. Here's a simplified, stripped down version of my linked list; is there a better way to do this, while still using a recursive data structure?

    By the way, I discovered in the process that the following code crashes LabVIEW, almost immediately and with no error message at all:

    post-3989-126445589277_thumb.png

    Linked List.zip

  7. I'm probably missing something huge here. But the beginning of an array is index(0) and the end is index(length-1). This is true if you add, delete or insert. The next item in the list is current index+1 and you just maintain an index to the current place in the list/array.:unsure: No iteration, uses LV primitives but suffers from re-allocation penalties.

    No, you're not missing anything here - an array is an easy way to do this in LV. I'm just curious if the new features in LV allow it to be done neatly as a linked list, since that's how I'd do it in C. I'm building a list of chemical reactions. The list is read from a file, so I don't know in advance how large the list will need to be. Then I need to run those reactions. Sometimes the user might want to re-run a reaction before proceeding, but I'll never need to back up to a previous reaction, so a linked list is a reasonable structure. From a performance point of view there's no problem with either implementation, since the list is short and I only need to build it once when the run starts. My question is pure curiousity as to whether a LabVIEW implementation exists (or can exist) that does what I want.

  8. Thanks for all the comments, but I should have phrased my original question more clearly. I'm trying to learn about what can be done with recursive data structures (using a parent class as a member of a child class), without using any type of reference (DVR, queue). I'm looking for an implementation, if anyone has one, that can efficiently add items to the end of a list (without having to iterate through the entire list for each insert). The problem is that, as far as I can tell, it's not possible to keep track of both the beginning and end of the list at the same time. Adding an item to the end of the list invalidates the front, because the front can only point at the list at a specific point in time. In C I would use two pointers, one to the beginning of the list and one for the current end, and I can't see a way to duplicate that structure in LabVIEW.

  9. I have a project in which I'm trying to use a linked list. An array is an acceptable alternative, but it seems like a good opportunity to learn about recursive data structures. I've used linked lists in C, which might be hurting my ability to use them in LabVIEW since I'm accustomed to thinking in pointers. Is there a neat way I can maintain a reference to the head of a list while continually adding items to the tail? Aristos Queue posted an implementation that allows an insert at any point but traverses the list recursively in order to do it, which seems inefficient.

    I'd like to maintain the current location in a shift register, with a function in the loop that takes the current index, adds a new element on the end, and returns the new element so it can be put in the shift register. I can't find a way to do that and maintain the list order. Either I keep adding to the beginning of the list, and the elements are in reverse order, or I lose track of the beginning of the list. The best solution I've found so far is to maintain both the previous and next element in the list (a doubly linked list), insert them backwards, and then reverse the list after adding all elements to it. Is there another way?

  10. I would understand if the above examples can be used to send and receive data in labview program and if the c++ application can work using the same port on the same pc

    by means of the tcp protocol to send to and receive data from the labview application

    You can use TCP to send data between two programs on the same PC. The LabVIEW program and the C++ will use different ports because only one application can open a port at a time, but this is not a problem. You will want to pick one of the two applications to be your "server" and the other as a "client." The server opens a specific port and listens for a connection. The client connects to that port (which is not the same as opening it) to establish communication. The client is automatically assigned a random port when it opens the connection.

  11. I am expanding the 'Variant to Control' VI with a 'C-typedef to LabVIEW Control' function.

    This means I have to read a C-typedef. Does anyone have a good reliable syntax description for this?

    If you want to see the definitive syntax, grab a copy of "The C Programming Language" by Kernighan and Ritchie (K&R).

    At first glance, you're missing the qualifiers "short," "long," "signed," and "unsigned." Several of the types you listed (for example, the ones ending in numbers) aren't part of the C specification but are instead defined as their own typedefs in (often platform-specific) header files. There are many other types, especially in Windows programming, that you're likely to come across and are defined in header files. A good start might be the list of types from LabVIEW's "Call DLL" example, but that's pretty short.

  12. Are you getting back the same number of points that you wrote? Do you get any error from Read from Binary File? That information would help determine if there's an offset when reading the file, or if you're writing the correct number of bytes but misinterpreting them. You do have an unnecessary "Set File Position" in the read, but I don't think that's causing your problem.

    There are some style points you should correct in your code. Do you really need to check every 1 ms if the time is elapsed, or can you use a longer, more reasonable wait? You should eliminate most of your global variables and replace them with shift registers (I know, the FIRST framework uses global variables all over the place). Both Current Line and the Previous Data Time should be integers, not floating point values (find the global definition, right-click, and change the representation to I32). Sequence structures, especially stacked ones, are almost never necessary and can be removed.

  13. I'm getting a value from a C++ DLL that returns the seconds since 01/01/1970 ("epoch"). I'm then trying to use the LabVIEW "SecondsToDate/Time" vi to convert this to a readable time format. When I do this the date is off by 300 years.....is there a trick to doing this? Is the labVIEW "SecondsToDate/Time" vi capable of converting time from 01/01/1970 or does it use a different date to convert from??

    If you read the help for the LabVIEW Seconds to Date/Time, you'll see that LabVIEW's "epoch" date is 12:00 a.m., Friday, January 1, 1904. You can determine the number of seconds between your epoch date and LabVIEW's, then add that amount before doing the conversion. Also make sure to use a numeric format with enough bits to store your number of seconds, taking LabVIEW's epoch date into account.

  14. I presume you are considering an AnInst class with a name property and each object instance will have a name that refers to a different instrument? Is your dll thread safe? What happens if one object attempts to make a dll call while the other object has a call pending? Many dll's get upset when asked to do more than one thing at a time. If yours is not thread safe, you'll probably need to figure out a way to throttle your calls. (DLL threading is a little off the edge of my exprience, so I may be wrong.) I have a few different ideas of how to go about doing that... I'd have to prototype them to see how well they work.

    It's an ActiveX application, not direct calls to a DLL, so I don't have to worry about whether it's threadsafe. It's designed to talk to multiple instruments concurrently. The external component I'm calling assigns every instrument a unique name and then requires that name as a parameter to most of its methods. There's some wackiness about the way it handles data - it seems to like structures flattened a bit like LabVIEW except with only a single length byte for strings and opposite endianness for multi-byte values, and it uses Windows messages to send status updates - but I've been able to make the communication work.

    Lucky dog. Our (internal) customers still aren't sure what they want, and we're 96% finished. frusty.gif

    At least that keeps it exciting. We're replacing an existing but outdated system (built by an outside vendor) with newer equipment from other divisions in our company, so the project goal was well-defined from the start. There are only a limited number of chemicals we need to produce and the market for them is mature so future expansion is unlikely.

  15. Thank you for taking the time to comment in so much detail. This is the sort of feedback I was hoping to get.

    The answer partly depends on the framework of your existing code and how much refactoring you and management can tolerate. Given a blank sheet of paper and knowing nothing about your actual physical system, my first inclination is to have each reaction object running in it's own thread, either by using parallel loops or perhaps by implementing them as active objects. Then I'd implement an AnalysisInstrumentManager class that can hold n number AnalysisInstrument objects, one for each actual instrument in the system. The AnInstMgr class would also run in it's own thread. When a reaction needs access to an analysis instrument, it requests one from the AnInstMgr object. If one is available, AnInstMgr sends the AnInst object that refers to the free instrument to the requesting reaction object, which uses it and returns it to AnInstMgr when it's no longer needed. This design gives you a lot of flexibility in the number of of analysis instruments you have in your system, frees you from having to create logic to decide which AnInst object to use, and avoids the danger inherent in using singletons.

    An unspoken assumption I've made in the above paragraph is that the driver for the analysis instruments supports multiple driver instances for multiple devices. For example, if you are communicating with the instruments via string commands over independent serial ports, no problem. If you have to go through the vendor's dll to talk to the device, this design might not work. Most, if not all, dll's I've worked with handle connections to multiple devices internally within a single instance of the dll. If that's the case I believe you'd need to change the design around a bit, though this is an area I don't have much experience in.

    I like this suggestion; I'll see if I can implement it. I do have, as you suspected, the problem of a single ActiveX reference for accessing multiple instruments, but I can use the unique instrument name (which is a parameter to all the relevant ActiveX calls) as a proxy for the actual instrument reference. It seems to me, though, that the manager might be as simple as a queue with a maximum length equal to the total number of instruments available. Is there any reason to make it more complicated than that?

    "What if..." is an excellent question and is not asked nearly enough! To expand on that thought, what happens if one of them breaks and has to be sent in for repair? Will your software still work with a single instrument? What happens if the instrument is irrepairable and you replace it with a different model? What happens when the powers that be decide that 8 concurrent chemical reactions and two liquid handling systems are needed? You probably don't need to build all that flexibility into the system, but you should have a reasonably good idea of how much effort it will take if those kinds of requests come down the pipe and communicate the design limitations to your customers.

    Good point about considering future needs, although fortunately in this specific case we answered a lot of these questions during the initial design discussions for the project.

    Using a DVR in the way you describe creates behavior similar to a singleton, as long as all the wires are connected correctly. As soon as some unsuspecting developer drops a class constant with the expectation that it is a singleton, the hilarity begins. IMO, if you need your class to be a singleton, make your class a singleton. It will avoid problems down the road.

    Now that I've contributed heavily to the level of hot air in the room, the question posed by AQ is valid and important. Why do you think you need a singleton? If it's mainly a matter of convenience, I'd think long about it. I believe there are valid reasons to implement singletons, but I don't think those reasons are commonly encountered.

    I only have one liquid handling robot in the system, so it seems to make sense to treat it as a singleton object. The reference to it is an ActiveX refnum. Because there's only one, it is convenient to avoid wiring that refnum to every function that needs it (also, I don't want to accidentally open multiple references to the same ActiveX object). That's why I was looking at storing the refnum inside a DVR (or SEQ), and then putting that containing reference inside a class global of some sort.

  16. So if I'm understanding this correctly, the action engine has a giant cluster input containing fields for every piece of data that ever needs to be passed into it. And then you have a sub vi for each unique action that the AE can perform.

    This sounds exactly like a class, except less flexible and more complicated. Why wouldn't you simply use a class?

    This is partly the thinking that prompted my question, but if you want to use a class, how do you do it? I couldn't think of an elegant way to get a singleton class where running a VI in that class precludes running another VI in the same class concurrently, unless you use a DVR. If you do use a DVR, do you wire a single instance of the class everywhere (making it essentially a singleton), or store the DVR inside a FGV or global inside the class?

  17. The FGV would be my first thought. I have used it to do exactly the same thing. It also scales well so when my customer said they want a PC at each production line in addition to the master machine that can serve the AE to the other machines via VI Server.

    Ben

    Thanks, I'll see if I can make that approach work. What about the case where my shared instrument needs to do many functions , each with several inputs, such that the functional global approach would result in a very messy connector pane? Or where I need to execute an uninterrupted sequence of commands on the shared instrument?

  18. I'm building a system that can run up to 4 concurrent chemical reactions. My plan is to have 4 independent "reaction" objects that each execute in parallel, but they all need access to some shared components such as the liquid handling system that adds chemicals to each reaction, and the analysis equipment to check reaction progress. I'm looking for comments on the best way to make sure that only one reaction at a time can access the shared equipment. It seems like the traditional approach would be a semaphore, but there are all these other options available. For example, I can put all the functions for the liquid handling system into a single FGV, make it non-reentrant, and prevent overlapping access. Or I can put the automation refnum (it uses ActiveX) into a DVR or single-element queue to limit access. What is your preferred approach? What if I have 2 analysis instruments, and each reaction should use the first one that's free?

    Taking this a step further (and overlapping with the recent questions about singleton classes), say I create a class to wrap the liquid handling operations. Do I wire that class to all the reactions after opening the automation reference? Is there a clean way to store that reference inside the class so that I don't need to directly wire that class everywhere it might be used?

    Thanks for any comments; I already have some of this working but I can't effectively discuss my design with coworkers (I'm the only programmer on the project) so I'm looking here.

×
×
  • Create New...

Important Information

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