Jump to content

drjdpowell

Members
  • Posts

    1,994
  • Joined

  • Last visited

  • Days Won

    183

Everything posted by drjdpowell

  1. I have a fix and will post a VIPM package in a moment. The problem results from converting 8 and 16-bit types to 32-bit, and relying on the OpenG functions to down convert. This works, but when we have empty arrays, they are left as the default type, leading to arrays of variants that are mixed in bit length, which the OpenG function can’t handle. So I added explicate type-casts for the 8 and 16-bit integers. ... Here it is lava_lib_json_api-1.1.3.21.vip I have not fully tested, so this may not be the only issue; could you run your tests please? — James
  2. Could you reattach with the control values saved as default?
  3. If you attach a small project showing the problem, I can take a look at it.
  4. I’m snowed under with work and about to go on holiday for two weeks, but people can check out the bitbucket repo and build the VI package to test it if they want (the VIPM build file is in the repo).
  5. I have zero time to look at it at the moment, but it isn’t clear to me how one would use the 2013 JSON primitives dynamically. If I have static clusters defined it should be hopefully much faster, but what if I haven’t got static clusters? How do I take JSON configuration objects from several modules and combine them into a top-level JSON “config” object, without the top level code having the link-to and explicitly use the type-deffed clusters of all the modules?
  6. As a rule, I don’t put classes in libraries at all, and the optional VI are in separate “support” libraries that only contain VIs. Other way around; the Optional VI uses the class (it’s really a static method of the class in form), so always loads the class, but loading the class doesn’t load the Optional VI.
  7. Very problematic, but many VIs are naturally thought-of as methods of the class. What if you later need to override the Optional VI in a child class, for example? You’ll have to change the library membership, and thus namespacing, with the headaches that entails. And if nothing else, it destroys the usefulness of using libraries to organize related code, if you have to place closely related VIs in different libraries. It’s just ugly!
  8. An LVLIB loads all its contained libraries and classes, but not its VIs. So it is OK to have lots of VIs in a LVLIB, even if not all will be used. It is only LVCLASS that loads all contained VIs. Note that if you have a class in a LVLIB, loading the LVLIB will load the class and thus all the class’s VIs. I generally do not put classes in libraries for thus reason. NI really should work at changing this. I can see why it might be necessary to load all dynamic-dispatch VIs, but not static ones.
  9. I find I have to watch out for optional VIs that extend a class’s functionality by using another class. It feels natural to include the optional VIs in the class; but, a class aways loads all it’s contained VIs. So this means that the other class and its dependancies always load when the first does, even if the optional VI isn’t used. If the unneeded-but-loaded classes also have optional VIs that use still other classes, then those classes load. It only takes a few “linker” optional VIs to cause huge numbers of unused classes and VIs to load. The key is to keep these VIs outside the class they are naturally a part of (I organize them in LVLIBs, often with the same name as the parent class). I once created a new project and added my top-level “Message” class, and this loaded twenty to thirty unneeded classes, all traced to two half-forgotten “linker” VIs. From a name-spacing standpoint this is rather undesirable, but until LabVIEW fixes the issue of a class always loading all it’s VIs it’s the only solution. — James
  10. Fix for this in version 1.2.1. Can you give it a quick test?
  11. It’s sister function “Get LV Class Path” is similarly glacial for no obvious reason. As is “GetLVClassInfo” from the VariantType library. I’ve wonders if the problem is just that they call functions running in the UI thread for some reason. But it could also be root loop. The only workaround I see is caching; store a set of default-value objects in a lookup table and check against this before calling “Get LV Class Default Value”. I wish NI would put some effort into improving semi-crippled functions like these.
  12. You could look at some of the packages in the Code Repository that use the OpenG Data tools (Variant Probe for example). I’d say there are two main uses: 1) Make reusable tools to handle an arbitrary cluster. The OpenG Variant Configuration File package is an example. Here the User of the tool at some point needs to convert the variant back to a known type. 2) Make a display that converts an arbitrary type into a string. The VariantProbe above is an example. — James
  13. You mean using LVObject? You should also consider if it is worth having an abstract parent “system” class, and then have the ‘handler’ VI (I use the terminology “shell”, as in “async launch shell”, BTW) be in a child system class. More work but could be more flexible later on. BTW, if/when you get to benchmarking launch times of your handlers, can you try the following: on the first use of a handler clone (when the overhead of creation happens) try it with the handler being part of a class library, and then not part of a class library. I found with my code that it was about four times slower if in a class library. If I recall right, I got initial creation times on the order of a couple of milliseconds if it isn’t in a class and 8 ms if it is (reuse of an existing clone was much faster, something like a 0.2 milliseconds).
  14. I never ‘clean up’ the reference, I let it die automatically with it’s creator.
  15. I keep such generic ‘handler’ VIs in a non-reentrant VI’s shift register. I only need one of them so why put it in class data?
  16. Another hint on the bonus question: To log all the data I collect, I took care to Start Acquisition only after Logger replies that it is started, and I Stop the logger only after Acquisition replies that it has stopped. Yet the Message Display received the stop info published by the Logger first. What happened?
  17. Sending things to the controller is fine, but if all the Plotter does is plot raw Hardware data, then what are you gaining by splitting them? If it’s more than that; if Plotter is more than just a straight UI for Hardware, then it makes sense. Personally, I would always start with a “Hardware” actor running by itself, for initial debugging in isolation. For that, Hardware needs a UI (preferably simple, low-overhead, and one that has no overhead if Hardwares Front Panel isn’t loaded). Often this UI can serve as a part of the application UI by being in a subpanel. Ask yourself this: if you reused Hardware in a new application, would you reuse Plotter as is? If so, maybe it would be simpler to combine them. If thing’s got worse when you reduced the UI update rate then you did it wrong. No. Though you can do the equivalent thing with messages by having them flag the need for a UI update, but not do the update till later (after many messages have arrived. Do you batch signals? Or send a separate 30+ messages/sec for each of 50 signals? 30 messages per second containing all signals can’t have THAT much overhead. Oh, BTW, make sure your not using “Build Array” anywhere as that can be slow.
  18. Why is your Call Parent Method AFTER your “state machine”? It needs to be in parallel to handle messages (such as “Stop”).
  19. I don’t know where the bottleneck is, but you’ll have problems pushing 50k to 200k messages per second between actors. For display operations you should definitely have a flush queue in there somewhere, so updates can run at a slower rate than arriving messages. If you do go with a shared reference, I wouldn’t use a DVR; instead share a data queue like the one you use in your Plotter Actor Core. Create that in Plotter and pass it by message to Hardware. Then you have a direct, low-overhead stream that you flush a few times a second. — James BTW, I don’t know the details of you application, but I would be tempted to make basic plotting of raw data a feature of the Hardware Actor itself.
  20. Could you not have the Hardware Actor batch up measurements, rather than the Controller? Does your Plotter Actor always do slow display updates (Property nodes) for each message? You can write it to only do display updates a few times per second even if new data messages arrive faster.
  21. Bonus question: My screenshot of the Debug window seems to have inadvertently demonstrated a problem with my message-publishing “Observer Register” system; can anyone spot it? Hint: look at the last five messages.
  22. For discussion, here are shots of the “Debug Option”, where I’ve added some extra comments. First the “Debug Option Dialog” case in the Main actor: This is an asynchronous action; the Main code continues while the dialog is displayed. When the User dismisses the dialog (or the 30s timeout occurs) a message is sent in “reply”. Here we are sending the result back to the Main actor, and are relabeling the reply as the command “Dialog::Start Debug Option” (the message contains the label of the button the User pushed). Effectively, this is an asynchronous request-reply to the User. The Main actor receives the “Dialog::Start Debug Option” message: Note that because of the "two-queue" nature of the JKI “state machine”, this message cannot be received before all the initialization cases execute (this is a potential race condition in the original NI example). Now the code triggered if the User clicked the “Yes” button, “Start Debug Option"): Note how we drop the address of the Message Display actor! We don't trace the actor wire outside of this case. This violates the usual rule of always cleaning up references that you create. But it also means we know exactly what this actor will do by just looking at this limited code section. This messaging setup is thus "static", in the sense that once started it will continue until the Main actor stops (the Message Display actor is an "Autoshutdown Slave", meaning it automatically shuts down when the code that launched it goes idle). We can, of course, choose to store the Message Display actor address in the shift register, and thus modifiy its interaction at a later point. But personally, I think being able to drop the wire refering to something improves readability because one can see that something is not being modified elsewhere. Note added later: of course, we don’t truly drop the Display actor’s address, as it is sent off in registration messages to the three other actors. But in this form the address can only be used to send the specified published messages (in this case, all published messages, but with an added prefix), and there is no ability for the other actors to send an arbitrary message to the Display actor. Thus we have fully specified the messaging interaction in the case that launches the Message Display. The ability to create limited-use versions of an “address”, such as causing the addition of a prefix, or wrapping it in a registration message, is an important part of the philosophy of this framework. And this is for code readability; if I pass a bare, unrestricted, address to another code module, I have to inspect that module to verify what that address is used for. But if I pass a limited-use address, then I can be sure of how it might be used, and it greatly reduces that amount of code knowledge I have to fit in my poor brain at one time. Finally, heres the Message Display debug window: Good debug tools are critical in working with asynchronous communication. There is also a custom probe that displays message history in a similar way. Comments? — James
  23. I don’t know enough about your architecture to follow the issue. My “Reply” method is a public method of my message class. If you looked at the “reply address” of a message received through my TCP client server, you would see that it points to a “TCP Connection actor” spawned to handle specific TCP connection. As there is one TCP Connection actor for each client connection, this effectively routes replies to the right destination. As for errors in making a reply, let me offer an alternate philosophy: ignore them. I impose a design constraint that a Request-Reply transaction is the responsibility of the Requestor. The Replier is responsible for attempting to reply to the address provided, but is not concerned with a failure of that address. That is outside it’s scope.
  24. I posted it here. Unfortunately, I feel in that presentation I spent too much time on the inner workings (some of which are complicated), rather than on the use of the API (which I hope is simpler).
  25. You don't need a getter; you need a Reply method.
×
×
  • Create New...

Important Information

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