Jump to content

Jeffrey Habets

Members
  • Posts

    193
  • Joined

  • Last visited

  • Days Won

    2

Posts posted by Jeffrey Habets

  1. I just ran your benchmark code on LV2009SP1 and indeed see that calling the static method is about 10x faster than calling the dynamic dispatch one.

    I backsaved to LV8.6 and ran it there, now calling the static method is only 2x faster. Mind you, in both version of LabVIEW the dyn.dispatch was about the same speed, but it seems static calling speed has improved in LV2009.

  2. Hey Mikael,

    It's always nice to get a peak into your design solutions :-)

    Likewise. :)

    I guess you have 2 base class methods Thread 1 and Thread 2 that you override like this:

    This is an other way of solving this. I certainly keep it in mind for the next time I need this.

    What I actually did is add an extra StartThread method that can be called from the childs. I only added the second thread to the child that needed it.

    post-906-126743577634_thumb.png

    Why the extra method for starting threads? The SpawnThread contains some extra logic to wait for the thread to be in a specific state (and is hardcoded to only launch ThreadStub).

    The other thread is started from the currently running thread like this:

    post-906-126743632211_thumb.png

    When StartThread finishes executing, the VI finishes its loop(s) and goes idle (well, it should go idle, but it only does if I have not more than one childs instantiated).

  3. Not sure if the problem I describe here is class-specific, but since it involves OO, I think this is the best category to put it in.

    I use the design below to create child-classes that spawn a process-VI with a specific name (Thread.vi in this case).

    post-906-126739073545_thumb.png

    This concept works as expected without any problems (we have 9 Station child classes running). The station threads are basically all top-level UI-VI's based on the JKI statemachine extended with the ability to be signaled externally through user-events.

    In a later development stadium the need arose to have a second thread in one of the stations and to be able to switch between the two available threads while the application was running. At all times only one of the threads will be running. I implemented code for this and it worked as expected, as long as there was only one child class instantiated.

    (What the new 'thread switch' code does is to signal the current running thread via a user-event to stop and then launch the other thread.)

    Monitoring the thread's exec.state property shows that the initially running thread goes from 'run top-level' to 'idle' when calling the thread switch code (as expected).

    Now, when there are more that one child class instances in memory (e.g. in this test-case I have one instance of Station 1 and one of Station 2) and I run the thread-switch code, the initially running thread of the class with two threads goes from 'run top-level' to 'running' state, preventing me from switching back to this thread. I'm a bit puzzled by his, and have no clue why this should be the case and thus think this could be classified as a bug. This is all LV8.6.

    Any other opinions on this?

    PS. I have a workaround in place, I'm just putting this up here to here your thoughts.

  4. There is no need to store the ref each time you switch the run-mode. Just write the ref ones to the state data, there are several ways to achieve this, here's one example:

    post-906-1264194874_thumb.png

    As a matter of fact you could do this in either of the four "... Change" states since they all get called when the XControl is instantiated.

  5. Maybe that's what AQ is saying...? I read it as creating the User Event, not the Registration Refnum, in the GetUserEvent method, and I don't understand why that is preferable. Creating unique user events for each listener requires a lot more supporting code than creating unique registration refnums.

    The idea is not to create a unique user event for each listener, just store the user event ref on the first call to GetUserEvent and return the previously created refnum on all subsequent calls. Each observer registers it with it's own reg-events node.

    As for the observers being able to destroy the event ref, I see your point, but in general I second Ton's statement: 'delete what you created, leave what you received'.

    Also: Document your API well and your users should be OK. And in the case your user doesn't read the docs/description, make sure errors are propagated correctly when attempting to generate an event.

  6. I agree with everything you said except this. I believe the user event queue exists at the event structure, not the the user event refnum or event registration refnum. If there are no registered event structures, there is no queue to fill up.

    I wouldn't be to sure about that.. In my understanding the queue exists at the event registration refnum. This is also illustrated by the LV help that states you shouldn't wire an event registration ref to more than one event structure to prevent race condition situations. So in order to have multiple observers observe you, you have to either publish the event refnum or a unique event registration ref for every observer.

    Since the user event refnums and event registration refnums are strongly typed, you can only put them in an array if they have the same data type. What's the recommended technique for dynamically registering/unregistering for events that have different data types?

    Practical in your XControl (or class or whatever code module you want to make observable) you have a finite number of possible dynamic events that you can send and they all serve their own purpose. I'd simply put them all in an event-ref cluster inside my internal data structure. No need for putting them in arrays imho. (It would be the same as wanting to stuff several different clusters in an array... which ofcouse is possible using variants classes.. :lol:)

    • Like 1
  7. I'd probably go with creating the event ref in the property node when it's called the first time, all subsequent times you just return the previously created ref that you stored in your state data.

    Although in the past I've used the concept you suggested in option A in some of my classes. It does create a 'useless' ref until someone registers for it, but I doubt it takes up a lot of resources since the actual event queues are created when a listener registers for the event (I guess..).

  8. What about reuse for the constructor* method? I find I regularly use dynamic dispatching (which requires input/output) in case the child method needs to call the parent on override or just needs to use it anyway.

    *This means it's not a true constructor (the LVOOP constant on the BD is really), and I even have to set some data before it sometimes, so it is really better suited to the term initialiser, but I prefer constructor.

    Then I have the problems above - that I can't force the constructor to be used (i.e. I might forget to use it in all cases!!)

    You're right.. You need class wire input on the constructor for the childs to be able to call the parent's constructor. I should have said I just never connect the object constant to the constructor method, simply leave the class input terminal unwired.

    I would love to be able to force a constructor/initialiser or something similar.

    I don't really mis the ability to be able to force it, but I agree it would be nice if it was possible. I wouldn't want the code to be run as part of the cube-constant though, it wouldn't feel natural.

  9. Neither really. The idea of a Create method doesn't bother me, but the requirement of using a Create or Init method on an object constant that looks like it should work fine without one does bother me. More details can be found here.

    To prevent this confusion I never use the object constant on the BD to 'create' objects.. Basically all objects are created by explicitly placing the constructor method on the BD. I should note I use by-ref classes for the major part of my designs and they come naturally with a create method for construction, but for by-value classes I use the same paradigm. The constructor typically has no input terminal for the class wire.

    For most LV users using a create-destroy paradigm is quite natural since there's a lof of that already in LV (instrument I/O, file I/O, Queues, etc..).

  10. Your tool makes me both happy and sad, both motivated and depressed.

    ... Perhaps the palettes should be named in iambic pentameter. Can you make all the icons rhyme?

    Hmmm, I think this would make LV a bit to artistic.. :rolleyes:

    Eventually I went crazy, had a nervous breakdown, and killed the feature. Clearly, anything we did automatically was undesirable. Another member of my team made it so that a .mnu file could be associated with a class so that users could define their own layout for the VIs. I've never felt like revisiting the issue.

    I commend your work on this feature. I am surprised this thread is not full of people complaining about how useless it is.

    Well, at least it's good to here that some people at do NI thinks about usability of features.. :thumbup1:

    I have to agree with some comments made here, something is always better than nothing at all.. It's very cool we've got the native classes, but it's simply to much of a hassle to work with them out-of-the-box. There's always been a lack of usability features for classes and to be honest, if it weren't for Endevo's GDS, I doubt I would be using the native classes today (or I would at least have build some wizards myself..).

    I'm a bit surprised NI didn't just let this feature make it into the release (or at least in a beta release) and let the users give feedback on it. Indeed you'll probably get a lot of comments as you also did get internally. These should then be considered very useful input to consider what should be changed and/or configurable for the next release.

    So now.. Will it come back in 2010 or should I just keep on coding? :)

  11. I can see the difference in your post :yes:

    What version did you post in as I am getting this in LV09?

    (I even downloaded everything again just to make sure)

    This was in version 8.6..

    I just checked 2009 and have to confirm they now look the same.. Looks like this little gem of a hack is broken in LV2009. :(

  12. What are you trying to do?

    What I'm trying to do is get a list of all class methods of the selected wire.. I'll populate the right-click menu with this list so the user can do a quick drop or insert of a method of the specific class without having to browse or going to the project.

    A bug? Only sort of. Yes, it's abug, yes it makes LV unstable, but, um... no, it is unlikely to befixed.

    This is documented somewhere but I can't find it at the moment. If Iload A.lvclass in AppInstance 1, that same piece of data cannot be usedin AppInstance 2 because

    ...

    As I said, this is documented in the shipping docs somewhere, and I know I've typed this up before.

    If it's documented, it's very well hidden.. I wasn't able to find it either.

    Anyway, thanks for the insight, it was very helpfull. :thumbup1:

    I now simply run the sub-vi containing the pictured code via a call-by-ref in the application context of the VI it's operating on and just return the class-path and wire ref.

    Jeffrey

  13. Hi All,

    While writing a RCF plugin I bumped into this problem which I was also able to reproduce outside of the RCF framework, so it's probably not a framework issue.

    The piece of code shown tests a diagram's selection list for class wires and returns the wire ref and class path of the first class-wire found. This code works whenever the VI that it's operating on is in the main application context. When I have opened the same VI from within it's project, the variant to data function returns error code 91 (The data type of the variant is not compatible with the data type wired to the type input). However the datatype in the variant is under both circumstances exactly the same.

    Also, after the error 91, LV becomes instable. Doing edits in my script-VI or even just saving it gives me an fpsane crash on line 432.

    Your thoughts please? :blink: Am I looking at a bug here?

    post-906-125287943175_thumb.png

    Jeffrey

  14. QUOTE (Aristos Queue @ May 22 2009, 10:42 PM)

    Look at the online help for Obtain Queue. Search the help for the phrase "This function might return". Is this the sort of thing you're wanting to see on all functions?

    Yes, exactly... But it deserves a more prominent space, a bold header: Possible Error codes and the errors should be in some sort of table form with the explanations right there, in-place. (Or at the very least a direct link to the correct place in the error-table..) Now I still have to go and look the error-code up to know what it means.

  15. I'll try and add my :2cents: to show the concept of how I handle error logging and visualisation in my applications.

    I basically have the whole error/message logging encapsulated in a by-ref class. This class:

    • handles logging of the errors/messages to disk
    • rotates logs every N days
    • has an active thread of which optionally the UI can be shown as a (floating) window to see the realtime log
    • publishes log events to interested subscribers through dynamic events

    Each parallel loop (including dynamically spawned processes in active objects etc.) takes a reference to the logger object. To make sure all errors are catched all executions chains should end with the AddError method.

    post-906-1242946857.png?width=400

    In the example here, on error, the default logwindow would be shown (which shows all messages since application start). Whether or not you want that depends on the type of application and where you are in the development cycle. I usually use the catched event to determine what error occured and decide what to do with it (e.g. ignore it, show in a nice UI, quit app, etc.)

  16. QUOTE (neBulus @ Mar 21 2009, 02:37 PM)

    Your welcome. :) I actually just finished it for a presentation I have coming up and thought it would well fit in. Pictures say so much more that words imho.

    QUOTE (Michael Aivaliotis @ Mar 22 2009, 05:15 PM)

    Jeffrey, I also do this however I use dynamic events or queues to stop the process. Never abort.

    Where I talked about message and response notifiers, I of couse meant the dynamic events or queues. (Although it could be done using object attributes..)

    And if you really want to make sure that the thread is finished before you take other actions in your destroy method, let the thread send you some kind of notifier when it's finished.

    QUOTE (Michael Aivaliotis @ Mar 22 2009, 05:15 PM)

    I don't like imposing rules on things like this because even though you can have general guidelines and best practices, it really depends on the specific application at hand, the corporate style guidelines and (at the end of the day), the comfort level of the programmer.

    So I'm not saying you should never use abort, but it needs to be designed-in and all editors of the code must understand the caveats of this method.

    I coudn't agree with you more.. In the case of active objects the handling of the active process is fully encapsulated, so the users of the objects don't have to bother knowing what's going on inside. However, for developers that work on the object's internals it's important to know what's going on, so good documenting of the destroy method is a must here.

    When I use the abort method, it's mostly on AO's with a thread that does nothing more than just one thing (e.g. continuously gathering data and pushing it on to a queue or listening on a communication channel and passing the events on to a higher level). If I have no need to send commands to the thread and it's a safe loop to just abort it, I will.. By design.. :)

×
×
  • Create New...

Important Information

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