Jump to content

mje

Members
  • Posts

    1,068
  • Joined

  • Last visited

  • Days Won

    48

Everything posted by mje

  1. So I've been speaking with NI (thanks AQ and Mike!) and the issue has been resolved. It is a manifestation of the same issue reported by Jon Kokott (CAR 255982). The issue actually turned out to be in another VI further up in the call chain. This VI was missing a link to another property defined in a base class, which resulted in the VI's data not being set up correctly. The bad data was then passed to the accessor method shown in my original post which would read garbage data. Performing an explicit recompile of the affected code under LV2010 SP1 fixes the problem. The gory details: The data structure in question is interesting and I believe actually sheds light on why the problem wasn't so quick to go away. The relevant hierarchy is as follows (if you can make sense of the mashup of pseudocode and UML): namespace LabVIEW.Messaging.2.0.lvlib{ class Application.lvclass { property Timeout { public <<Dynamic>> ReadTimeout(inout App:Application, inout Error:Error, inout Timeout:I32) } public Run(inout AppRef:DVR<T->Application>, inout Error:Error) }}namespace MyLib.lvlib{ class MyApp.lvclass : LabVIEW.Messaging.2.0:Application.lvclass { property Timeout { public <<Dynamic>> ReadTimeout(inout App:MyApp, inout Error:Error, inout Timeout:I32) } property AutoSaveInterval { public ReadAutosaveInterval(inout App:MyApp, inout Error:Error, inout AutosaveInterval:I32) } }} If you refer back to the original post, the screenshot of the code would have been from MyLib.lvlib:MyApp.lvclass:ReadAutosaveInterval.vi. The offending VI was Application:Run, it was missing a link the Application:Timeout property. The Application:Run method read the Timeout property via the Application DVR passed to it. Using the property will ultimately call MyApp:ReadTimeout due to the dynamic dispatch, which in turn calls MyApp:ReadAutosaveInterval. The call chain would therefore be: LabVIEW.Messaging.2.0.lvlib:Application.lvclass:Run.vi MyLib.lvlib:MyClass.lvclass:ReadTimeout.vi MyLib.lvlib:MyClass.lvclass:ReadAutosaveInterval.vi Things to learn from this: Recompiling the VI which showed the problem (ReadAutosaveInterval) did not reproducibly fix the problem because it was only a symptom of the problem. The symptomatic VI was actually fine: when running it alone or in isolated test cases, it behaved as it should. Recompiling the hierarchy such that Application:Run is recompiled fixes everything since it is the offending VI. If you see a problem like this, work your way up the call chain to see if there are any VIs which are being left out of your recompile. Cheers, -m
  2. Well it worked great, thanks for the feedback. My splash screen appears within milliseconds of launching the application. I wasn't aware that having VIs included in the exe does not cause them to "load" (for lack of a better word) if not used.
  3. I have used that construct so many times that if the behavior changed, almost all of the applications I've built would have race conditions introduced into them. That behavior is one of the reasons I like the event architecture so much, it is quite elegant in how it separates event consumption from handling. Not that the LabVIEW event implementation is without shortcomings though...
  4. Thanks folks, that's good to know. It is what I've done in the past, but haven't gotten around to doing it yet as the splash screen is pretty low on the priority list. I'll throw something together tonight before I queue up the next build to see how much difference it makes. As for 10,000 VIs, well, I'm still shy of that thankfully. But this is the first project where I've used OOP for everything and the sheer number of VIs for the project is still quite staggering compared to my previous ones. Currently 44% of my VIs are simple read/write accessors for my objects. Right there's one of the reasons the hierarchy has grown so much.
  5. I've done splash screens before, but they've not really served a purpose other than branding so their implementation really didn't matter from a performance perspective. Now I have an application which seems to take quite a bit of long time to load up, to the point where users regularly wonder, "Hmm, did I actually double click that? Better try again." More than once I've been right next to the person and told them, "No just be patient, it will show up." only to watch them try again and again to click that darned icon. Well at least I haven't set allowmultipleinstances=true in the ini file. Yet. I know there will be a constant time as the LabVIEW RTE spins up which is independent of the actual executable I create in LabVIEW. But does the size of my executable also affect load times? I'm under the impression that the whole thing must load, so if it's larger a splash screen can't be shown either way until the entire application has been loaded into memory. I might be wrong here. My current application is at 44 MB, this doesn't seem too large to me... Has anyone played around with dynamically loading their core application logic from outside of the executable to see if that reduced time to display a splash screen? I figure the best way to do this is have the bulk of my code in a LabVIEW built DLL, then have a shell of a LabVIEW exe display the splash screen and proceed to take its time loading the DLL, after which the splash screen hides itself? The DLL will not be statically linked, a path will be built to it at run time. Or is this road fraught with peril? I've never actually built a DLL in LabVIEW, this might be interesting.
  6. I always use the Error Cluster From Error Code.vi that ships with LabVIEW. Doh, never realized it was a poll. Ignore my useless comment.
  7. Haven't tried the always copy idea. Spent a while last night working on this though. The VI in question also seems to get suck in run mode. Forcing it back to edit mode then forcing a recompile of the VI (Shift + Run) fixes the return value, but the VI still gets stuck in run mode after the application returns. Forcing the entire hierarchy (Shift + Control + Run) to recompile fixes the return value and also resolves the stuck in run mode behavior. I did a mass recompile of the folder containing the project and all dependencies which also fixes the issue. Several insane object errors were logged, but none of them related to the class hierarchy in question. I will note though that the folder contains some old VIs which have been orphaned, removed from the project but still persist on disk. To some extent I'd expect some errors... I had come to the same conclusion as AQ with regards to the only way this can happen must be if the unbundle by name primitive is reading the wrong memory address. I've tried editing the VI a few times to no success. Replacing the unbundle by name primitive with a new one, or even a full unbundle did not fix the issue. Several times while attempting to edit the VI, I get a LabVEIW crash: Interesting indeed. I have yet to find a pattern in the returned number. I've been trying to see if the memory is offset by a few bytes in either direction, but have yet to get anywhere with that. Answering some other issues: The object in question is actually contained in a data value reference. Data access is done exclusively through property nodes. What can I say, I'm lazy: property nodes are way easier to deal with than dropping an IPE with strings of VIs each time I want data. I examined the revision deltas more closely, and neither the method nor the class has been edited between the last stable revision and the one which introduced the bug. The library which contains the class though has. I misspoke earlier about that, my apologies (the library bears a similar name to the class and I misread the logs). Given that the VI and class had not changed when the error triggered, I have a sinking feeling this issue is not related to the code for the accessor, but more indicative of an in-placeness bug, possibly relating to synchronization. Not to derail this topic, but I have seen other bugs in the IDE relating to IPE-DVR structures. Have any of you ever tried debugging code which a single VI is in an IPE-DVR structure, and it is impossible to step into that VI? Even slapping a breakpoint in the VI doesn't work, which worries me. Haven't been able to gather much info on that one, but I've seen it many times. There's something funky going on with the in-place algorithm that I think might be the reason behind these issues, but I don't wish to dwell on conjecture and would like to keep focused on the original topic. I've produced an archive of the affected code. I removed all the builds etc and the bare code turns out to be quite small (~30 MB). AQ thank you for your interest, I'll contact you privately to figure out how best to get you the code. -michael
  8. 2010 SP1. It is definitely not a display issue, the returned number is incorrect and causes my application to go absolutely ballistic. Recompiling the affected VI does not fix the issue. I plan on doing a mass recompile tonight of the entire project (the method is a dynamic dispatch, so other implementations exist which might be causing trouble). I've also pinpointed the revision which introduced the bug. Interesting thing is the VI is not one of the changed ones, though the class did change. Behavior persists on two development systems. More to come later as I unravel this.
  9. I'm having trouble where some of my data appears to be...corrupt? See below: This is an object's accessor method. It really doesn't get much simpler than this. Check the two probes out, one is right before the unbundle by name primitive (#10), the other is right after (#11). Note how the #10 probe shows a value of 300 for AutosaveInterval, which is what it should be. The #11 probe shows a value of 805603042-- no idea where that came from. The VI returns the 805603042 value. I'll also note that the value returned changes each time I run the application. Needless to say this has completely halted my development, and I'm at a complete loss on how to debug this. As far as I can tell this is the most basic unit of code I can write to access the data in my class. Have any of you ever seen anything like this? Anyone have any ideas, no matter how esoteric? This is a pretty darn complex application, so I can't exactly extract example code, which is part of the reason I haven't given NI a call already... -m
  10. Just to clarify, I wasn't saying it should produce an error, but rather asking if one should be produced. I see both sides as valid, however if anything I'd say it should stay the way it is for no other reason than precedence has already been set.
  11. *puts his fake CLA hat on* I'm going to have to jump on the "working as intended" bandwagon. Very neat though, I never considered that you could code something like that. This is the exact behavior I'd expect given how event registrations and event structures are implemented in LabVIEW. Chris implied as much already, but the event registration is receiving the event, regardless of whether or not there's an event structure around to handle it. It is these very constructs that allow you to register and receive events long before you've blocked on the code which handles the events. I use this construct all the time when calling asynchronous code, without which proper handshaking would be much more cumbersome. I think the question is should LabVIEW allow you to code Justin's VI at all? Or should it produce an error? Essentially you you have a structure that doesn't handle all possible cases. Since the event registration refnum is a strict type, all events are known at design time: the event structure should be capable of recognizing the missing event frame. This is analogous to wiring up an enumeration to a case structure, where a value is not handled. I can see it both ways and I don't think either is "correct". If the event structure were to produce an error, this would enforce consistency with the event registration refnum, and in my opinion be consistent with other LabVIEW structures. I already described the case structure analogy, but also consider event frames which have no source. Delete a registration for a user event for which there was an existing frame, and an error is produced. Why doesn't the opposite hold true (a registration without a frame)? I do like AQ's use case though. Very interesting. Time to take the fake hat off and eagerly watch the discussion... -michael
  12. Good catch. Truth be told I never executed the attached code, I just saw the Win32 calls and adapted it to my use case. Passing a 64 bit value in place of a 32 bit one would produce at best a crash or exception. The weirdness you're seeing might be the result of stomping over memory which corrupted the display state.
  13. Nope. In that case the window still has the large frame. Though the settings you describe get everything else done (no appearance in the task bar, fixed size, always on top). So at this point, LabVIEW gets all the functionality right, its really only about delivering an aesthetic consistent with standard user interface design. I'll also add that while the code above works, and I have implemented it successfully in one case, I have another VI where it does not work. The call to set the extended style generates an exception each time. Have yet to figure out why.
  14. mje

    Build Servers

    That works, thanks for the info. A VM is a good idea, at least I can minimize it and continue working.
  15. Heh, thought I might have to bust out the Win32 calls again. I guess at this point there's no LabVIEW way of doing it. Indeed, that's exactly what I wanted to do, thanks for the code. I like how you reset the original style when the VI returns. Also the hiding, and re-displaying of the window is a nice touch, I take it to force a redraw?
  16. Last year at NI week I heard some talk about people using build servers for their projects. So what exactly were you referring to? I know some of the local lava people were among those in the discussion, but do any of you actually use one? Is it literally a second computer which you log into, update your local version and run the build specs, or is there a more elaborate interface which I'm missing? -m
  17. Is it possible to set a front panel VI to have a "toolbox" style window frame (the real name for this style has long escaped me). The one with a tiny frame, and a small "X" in the corner etc. Like this: You see frame styles like that all the time for floating windows. There a way to do this in LabVIEW?
  18. Cool, thanks for the tip. To clarify, there are no standing issues, what I have works. I was just wondering what people thought of the pre-canned serializaton mechanics. Consensus seems to be to avoid it, so I'll plan to work something else in before final release. As for databases, we considered them. Well, only SQLite. Ultimately ruled it out due to the frequency with which we need to access the data and the relatively small data set. That and we couldn't justify the risk of introducing an unknown platform on a short development schedule. When I implemented my first binary searches back in the discovery phase of the project and saw the results, it sealed the deal: we decided to keep it all native.
  19. The only reason I didn't do it this time is I'm still trying to wrap my head around the versioning embedded in the native packaging of data. Once you load your object, the previous version info is lost because your object has already mutated. If you want to know the version that's on disk, you need to interpret the byte stream directly (I think?). This can get cumbersome when you realize that each class has a version for itself, every level of ancestor, and of course every contained object in the private data. Seems intimidating. Of course I could just cheat and have a version saved in the class data as well. That is historically what I've always done. Yes, you're very close. Basically my old data had a large array-like piece of data. Searching and sorting that data is slow, so I went ahead and built pre-sorted index arrays that ride along with the original data to enable binary searches etc. So now when I load the document, if the index arrays don't exist, they need to be built. In this case it's not so much a matter of fixing as creating if it doesn't exist. As for typedefs...Damn.
  20. Yes, the version numbers persist in the binary format as in the XML. As for the orders of magnitude, I was comparing a generic variant driven XML library I wrote that uses the native LabVIEW/Xerces parser. Since it's a DOM parser (opposed to SAX), loading big documents becomes...prohibitive as far as memory consumption goes, which affects speed. So the combination of DOM and variants leads to astronomical memory footprints when large data structures get involved. Works beautifully though for small pieces of data because you can literally throw anything at it. As far as what breaks the native serialization, the only thing I'm aware of is changes in the qualified name of the objects. So renaming a the class itself, or the containing library, moving a class to a new library, etc will change the qname. Is there something else? At this stage the product is still in development, and I can write a serialization protocol into it before I ship if it proves to be too much of a risk factor. There's been a reason I've left the cool-aid alone until now, and it was the risk Dak brought up. I'd love to know exactly what can break it.
  21. I've finally decided to drink the cool-aid and trust LabVIEW to serialize my Objects directly That is I just wire up my Object to the binary read/write methods and hope for the best. So far I'm very impressed, it handles mutations of the objects very well. Up until now the mutations have been fairly mundane. They are changes that exist on their own such that if they weren't there to begin with, the default values that get put in place when an old version is loaded into the new code works fine. However now I have some data I've added which essentially serves as an index to other data. When I load an old serialized object version, the index of course doesn't exist, so the old object data mutates to now have the default value (an empty array), meaning my object is missing its index. No big deal, I just have code in place such that whenever I need to use the index, I first check if it's valid, and build it if necessary. This seems a bit clumsy though, I wonder what other strategies people have used for serialization? Up until now, I've always "rolled my own", but the documents I'm dealing with are now big enough that I can't ignore the orders of magnitude of difference in speed by using the native serialization options (the application I'm writing expects to have a median document size in the 100-500 MB range).
  22. OK, I see where you're coming from. For me there's a very distinct difference between a static property, and a singleton. A static property is a variable that is scoped to the underlying class, that is each instance does not have its own copy of the variable. In LabVIEW this is achieved by having an LV2G attached to the class (or any library). This is distinct from an instance property, which would be a variable held in the instance's private data cluster: each instance of the class has its own copy of the variable. A singleton on the other hand, is an attribute of the underlying class-- not the value. It's a pattern that ensures there will only ever be a single instance of a given class/type. At least that has been my understanding over the years. I have only used them in languages like C++ where you need to have all forms of constructors marked private, preventing the user from creating any new instances, either explicitly or via copying. The inability to make this restriction in LabVIEW is why I don't think a singleton class or library is possible*. I think the closest we're going to come to a singleton in LabVIEW is a DVR refnum held in an LV2G. The DVR ensures that no two processes will be able to have the value concurrently. A SEQ would work as well, but I dislike that implementation because if the calling code fails to check the value back in, you get a deadlock the next time you try to use it. *Granted a close approximation can be made as mentioned above using AQ's 2006 example. However copies can still be made via splitting of the wire, and passing the singleton to subVIs is impossible, dramatically limiting the use of these objects.
  23. Good points. I do indeed like the lack of enumerations for the calling code. Consider two tasks trying to access the 2009 implementation: Task A obtains the value via the get method. Once returned, there is no active lock on the underlying value. Task B now obtains it's copy of the value. Note at this point I could create an unbounded number of values. We do not have a singleton. Task A operates on the value it obtained. Task A writes the value via the set method. Task B now has an out of date value. Essentially the 2009 implementation is a static property, with all the same synchronization issues that arise in any language when you use them. I'd go so far as to say it's over implemented, as had the raw DVR been exposed, at least then I could take advantage of the locking mechanism of the IPE structure whenever I need to operate on the value. Agreed.
×
×
  • Create New...

Important Information

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