Jump to content


  • Posts

  • Joined

  • Last visited

  • Days Won


Everything posted by ned

  1. Hi Jed - I'm also in San Francisco and had the same question when I moved three years ago. There's a rarely-used Google group, NorCalFirst-Mentors, which you might want to join if you haven't already. Try posting there, it might get you replies from teams looking for help. If you don't get any responses, try again when the school year starts. Also see the Western Region Robotics Forum. They organize CalGames, an October repeat of the previous season's FIRST game, to get teams ready for the upcoming season. After posting to the NorCalFirst-Mentors list, Cecilia "Ceal" from WRRF suggested that I attend CalGames and look for her there; she introduced me to a team that needed help. Finally, the team I've been working with, in San Mateo, might welcome more help. I'm not sure what their situation will be this season, but it's been mostly one teacher and me for the past two years.
  2. Yes, thanks for the clarification. I meant that LabVIEW will remove padding when flattening to a string; a flattened string is portable between LabVIEW platforms (although different platforms may not produce identical strings or values due to differences in floating-point representations). Since it sounds like your data is stored without any padding (it would be unusual if it did include the padding bytes, especially on an old system designed when storage space was more expensive) it is likely you can flatten or unflatten on any LabVIEW platform without problems. ShaunR's comments apply when manipulating LabVIEW data in memory on different platforms, for example when passing a LabVIEW cluster to a C dll. That's not what's happening here, though.
  3. What makes you positive that the typecast won't work? If "byte alignment option set to 1" means there's no padding (aligned on bytes, rather than multiples of bytes) then it's possible that you won't need much more than a typecast, since LabVIEW packs clusters with no padding. I'd recommend that you flatten to/unflatten from a string instead of typecast since that gives you the option to swap endianness. You might want to use data structures that do not exactly match the ones in C. For example you could create a cluster that is only the header and then separate clusters for each item of the union, even if those are all part of the same struct in C. Then you read only the header cluster, and extract the relevant field to determine which other cluster to use for the data that follows. I'm making some assumptions here about how the struct is written to disk; if the WriteFile function does something interesting with it, then you'll need to look at that. I assume you have some sample files sitting around. I'd start simple by trying to read them with a simple unflatten from string. If you get the right values for any one-byte values and anything longer is wrong, swap the endianness and try again.
  4. This comment from Aristos Queue about variants might be enlightening: "VARIANTS AREN'T FLAT. The data is picked up off the wire whole, with all the hair hanging off of it (arrays of clusters of arrays of clusters of arrays of... etc) and put in the variant along with the type of the wire." Based on that, it would seem that the variant you're passing is just a pointer (or handle) to a new type description plus a pointer to the data. Since the data itself isn't copied, there should be some reference counting, the same way LabVIEW would do normally with multiple copies of the same data. Not sure this helps you pull that data apart, though.
  5. You wrote earlier: Do you have the ability to change the DLL? If so, then there are several ways to make it work. If not, then you're out of luck.
  6. None of those declarations are correct, and the errorIn and errorOut parameters are probably not optional - they're passed by reference and should be allocated properly. Passing a pointer to an incompatible data type will likely lead to problems. It will be difficult to get the errorIn and errorOut declarations correct in VisualBasic, though, because it would involve calling functions in the LabVIEW library to allocate an LStr and then creating a handle to it. ALL of the parameters, except color and len, are by reference (that's what the asterisk in front of them in the function prototype indicates). Further, as everyone else has explained, the ComPort parameter is a reference to a VISA session that is already open (specifically it's a pointer to an integer, where that integer value has meaning only as a reference to a particular session). Passing a constant that corresponds to the COM port you want to open WILL NOT WORK. You cannot call this function successfully without a prior call to open the session. If that function does not exist in the DLL, then you need to ask the DLL creator to add it for you. At the same time they should remove the errorIn and errorOut parameters, or at the least replace them with a data type that is more compatible with VisualBasic.
  7. On thing you can do (although I haven't tested this with RT FIFOs) is create a type definition for the cluster that is the fixed-size array. Then, put that cluster inside the cluster that contains the integer. It is then easy to convert the nested cluster into an array (using cluster to array). Going the other direction is only slightly more complicated. If you know that the array size won't change, you can use array to cluster. Otherwise you can typecast the array to the cluster type definition; this may be less efficient (my understanding is that typecast will always flatten to a string, then unflatten to the new representation, instead of a more direct conversion) but is more flexible.
  8. Why are you certain that there was a timeout at UDP Open? If there is no error on the error wire, then the function most likely did not time out. What is the actual problem you're seeing?
  9. I'm not sure if it's incorrect behavior although it is odd. See the help for "backslash codes display." It appears that Format Into String is interpreting backslash codes in the format string.
  10. Many of us would probably find that content interesting, but we aren't allowed to access it. Any chance you can share those presentations somewhere public, so that those of us who are not CLAs can learn from them? If not, could you please refrain from posting links to private communities in public locations?
  11. Yes, that's exactly right. Take a look at the Visitor example I linked - the parent Visitor class has a method for each type of DataItem, but they don't do anything. All the real work is in the Visitor child implementations. This allows you to call any Receiver class from within Message, without knowing the specific Receiver implementation. The pattern also allows you to easily drop in a new receiver for a different application, which it sounds like is desirable for you. You just override all the parent's methods in a new class with the appropriate logic, and add a constant of that new receiver class as the desired implementation.
  12. The message class is statically linked to the parent Receiver class, which doesn't actually implement anything. When you receive a message you use the child Receiver class, which contains all the logic and links to other classes. No cast is necessary to get the correct child Receiver class; it will just work through the magic of class inheritance and dynamic dispatch, because you will dynamically dispatch on the Receiver class when you call the specific Receive method for that particular message type.
  13. Poke through the code a little bit more - a DataItemCollection is a DataItem that contains an array of DataItems, so it does implement a tree (the reason the visitors are reentrant - they can be called recursively). That's why I mentioned one good exercise with it is to write a visitor that formats the structure into a tree for display.
  14. That's quite the framework you've built - a lot of functionality and still readable enough that I could understand what it's doing pretty quickly. Here's the discussion about the Visitor pattern: http://lavag.org/topic/16696-ah-yes-another-oo-architecture-question/ You don't need separate internal messages; the idea is that the messages stay the same with the exception of adding a Receive method and possibly an accessor or two if needed. In Remote Send (inside the Remote Extensions library), put a constant of the child (implemented) Receiver class on the block diagram and pass it to the Receive method of the message that arrives. The message class only needs to be linked to the parent Receiver class, and all the dependencies should be in the children that actually implement receiving the message. If I've understood correctly, that accomplishes your goal. If you're only going to have one receiver class then you don't strictly need the two layers of dynamic dispatch, although I think it helps keep things modular. It also allows you to add new actions on Messages fairly easily - you could handle sending the same way. You'd just want to name the method in Message something more generic than Receive, since it could do any action that's passed to it (in the form of a class). There's a manual programming step that makes this work. Inside the message's Receive method, it needs to make a call to the specific Receive method for that message type. That is, a message of type Message1 has a Receive method that calls Receiver.ReceiveMessage1. Message2 calls Receiver.ReceiveMessage2, and can't call ReceiveMessage1 because the connectors don't match (it has a connector for Message2, not for Message1). You don't have a separate Receiver for each type of Message; you have one child Receiver that has a separate method for each message type. That child Receiver can be a constant on the block diagram of the VI that gets called through VI Server.
  15. Move all the receiving logic into a separate class, Receiver, that mirrors the message structure (you'll need one method per type of message that you want to receive), similar to the Visitor pattern that was recently discussed in another thread. Add a method to the message class that takes, as a parameter, a class of the receiver type, and calls a method inside that receiver class, passing itself (the message) as a parameter. Then all the dependencies are inside the receiving class. The parent receiving class can basically be a no-op on the sending side with no dependencies since it should never be called there. On the receiving side, you implement a child receiver that has all the logic and links. Say we have an instance of Message of type Message1, and an instance of Receiver (both are classes). Upon receipt of a message, the following happens: 1) code calls Message.Receive(Receiver). This dynamically dispatches to the correct instance for Message1. 2) the implementation of Receive for Message1 calls Receiver.ReceiveMessage1(Message). 3) ReceiveMessage now has strictly-typed instances of both Receiver and Message, and can do the correct operations to handle that message.
  16. Here's another Visitor Pattern example that follows the traditional pattern more closely than the NI one. I learned the Visitor Pattern years ago in a compilers class that was taught in Java, and it was an interesting exercise to put together this example in LabVIEW. I apologize for the lack of comments and icons, I put it together rather quickly, but I hope it's helpful and I'm happy to fill in details if necessary. The example contains two class hierarchies: DataItem, and Visitor. The DataItem class has three children: IntegerDataItem, StringDataItem, and DataItemCollection (an array of DataItems). The Visitor class has two children: Generate DataItem Collection, and To Comma Separated String. The first uses the Visitor pattern to build up a DataItem Collection containing random numbers. Strings get negative numbers, Integers get positive numbers. The second visits each DataItem in the collection, and if it's a string or integer it converts to a string (if needed) and appends it to a comma-separated list. Each child of the Visitor class has a method for every child of DataItem (Visit DataItemCollection, Visit IntegerDataItem, and Visit DataItemString). To add a new action, create a new class that inherits from Visitor and implements those three methods. To get familiar with the pattern, here are some ideas for exercises: - Create a Sum class that adds all the numbers in the DataItemCollection (converting the strings to integers) - Create a TreeDisplay class that formats the DataItemCollection into a nice format for display, showing the levels of nested collections - Add a new DataItem type (for example, floating point) and add appropriate classes to each Visitor class. Note that when you want to do an action on the DataItem, you pass the Visitor (action) class to the DataItem in a call to the Do VI. This is the first level of dynamic dispatch. Then, the Do VI calls the appropriate action from the Visitor and passes the DataItem to it, adding a second level of dynamic dispatch. This is why the pattern is also known as double-dispatch. This example is in LabVIEW 2012. Visitor Pattern Example.zip
  17. Are you sure you're sending the correct command over TCP? When you connect over telnet, do you have to hit Enter after typing the command? It might be as simple as adding an end-of-line character (carriage return, new line, or both) to the end of the string that you send over TCP.
  18. Right-click on the chart, choose "Chart History Length..." and set it to a number large enough to cover all your data (right now it's set to 1000). Or, use a graph instead of a chart.
  19. Yes, there is a name: it is the Visitor pattern.
  20. As far as I know, you can't do it. I don't believe there's a way to tell the LabVIEW plugin (that lets you see a front panel inside a web browser) to send the web browser to a different URL.
  21. The controls in every element of the array need to be identical; you cannot have a different items in the combobox in different elements of the array. You'll need to come up with some other approach. One option might be a table indicator with a combobox on top of the active cell, which would let you change the contents of the combobox each time you display it in a new location. I provided some code that demonstrates the idea here: http://forums.ni.com/t5/LabVIEW/array-of-cluster/m-p/1822451#M625032 although that has a ring control, not a combobox, with constant contents. However, it would be easy to update the ring elements each time it's moved.
  22. I put together a simplified version of the VI in the screenshot that shows how to do this: http://forums.ni.com/t5/LabVIEW/array-of-cluster/m-p/1822451#M625032
  23. What makes you think it's the TCP port? I assume by "same port" you mean that all the servers listen on the same port. On the client side, the operating system assigns each connection a different random port when the connection opens. You would see the same behavior even if all the servers were on different ports. My guess is either that you're running out of threads to handle all the instances of the reentrant VI, or at some much lower level access to some part of the TCP/IP system is serialized. Can you try a few experiments? First, try doing all the writes first, then doing all the reads once all the writes have completed. Or, add a short wait after the write such that when you call the read the data should have already arrived. Also try using threadconfig to increase the number of threads in the execution subsystem in which your re-entrant VIs run.
  24. A couple of additional comments: 1) One common solution is to make two function calls: one that returns the size of the array that will need to be allocated, and a second that then fills the pre-allocated array with data. The second function should still accept a size parameter so it can confirm that the data will fit and return an error if it will not. Some Windows functions combine these into one function, by passing the size parameter by pointer. If the array pointer is null, or the value pointed to by the size parameter is 0, then the function returns the needed size in the size parameter; otherwise, it fills the array. You should not use Pascal strings, it will be a pain for anyone calling the library from C. If you're using a string and not an array of bytes, then you need to keep in mind that C strings are null-terminated (the last byte in them is 0) and the size needs to include that terminating byte. When you call a DLL from within LabVIEW and you pass the parameter as a string, LabVIEW automatically adds the null termination. However, if you pass the string as an array of U8, then LabVIEW doesn't know it's a string and will NOT add that null terminator. The DLL, having been built in LabVIEW, will expect to find that null terminator to determine the appropriate string length and will likely generate an error if it doesn't find a null byte before the end of the string (a null before the end of the string is fine although you may lose data that follows it; no null at all in the string is not fine). 2) Yes, you should use C calling conventions, mostly because it's more common (with the important exception of almost all DLLs that are part of Windows).
  25. Minor style point: straight wires look nicer than ones with lots of bends. I suspect that you could eliminate the ~100hz signal on the chart by wrapping the chart terminal in a case structure so that it only writes new data to the chart when the frequency is above some minimal value.
  • Create New...

Important Information

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