07-22-2022 12:21 AM - edited 07-22-2022 12:28 AM
So this KB article (Differences Between Type Definitions and Strict Type Definitions) describes how the "item name on a ring control" is not part of a Typedef for a Ring.
What is the datatype of the Ring when typedef'd? Is this useless?
Reason for the question:
I have a typedef'd value for an error code (see related Add sparse enums to LabVIEW, declined idea) that I've built into a PPL and use to identify that type of error.
This works fine 🙂
I created in another library, a JKI VI Tester test case, and checked if the error was of that type. VI Tester's PassIfError VI takes an array of numeric values to compare to the "code" of an error to check if the error in question is the correct one - so I wired my typedef (from the PPL) to Build Array and then into the PassIfError VI.
I was surprised that the error didn't seem to satisfy the test, especially because it did seem to correctly parse as an error of that type.
On closer inspection, the numeric value I'd dropped was wrong, and moreover, changeable via right clicking the constant and choosing Edit Items.
Where did this number (505410) come from? Why is it here? The copy in the PPL correctly produces the expected code, and the test passes if I change it to look for 224455, but I'm confused as to what value the typedef has here. Would a strict typedef have prevented this?
I guess the solution for me is to remove the control and instead have an inlined VI that just outputs a numeric constant to the indicator and a tiny icon...
07-22-2022 12:58 AM
IMO, to meet your expectation, the solution is to use an ENUM instead of a RING.
RING control despite being type def or strictly typed, any changes to the CTL file will not be propagated to all instances of the RING control.
Let's see the pros of using a RING control, you would notice ring controls used widely in drivers where it is highly probable that the future versions might add more features to the ring control which should not require you to update your code that uses an older version. Whereas if you had used an ENUM, the upgraded driver will break your old code as the items in the ENUM do not match and have to be updated.
During execution, the ring data type just carries the numeric value and the string is just merely there for readability whereas, in an ENUM, the text is the data carried in the wire as long as it is not coerced (if coerced to int, the text is lost and only the numeric equivalent is passed).
07-22-2022 01:11 AM
In this case, an enum is quite impractical - enums are limited to sequential positive values, and start from 0. Error code 0 is generally no error, so I'd need some "filler" elements. And to get into a "user range" (although I accept that my current value, 224455, does not fall in one) I'd need at least 5000 filler elements 😉
Since my code checks this value and then handles the error in a specific manner, I can't use a value that would appear in errors commonly (or ideally, ever) thrown by LabVIEW.
07-22-2022 03:38 AM
Sadly, without the sparse enum, we have to use an enum and from\to conversion VIs.
07-22-2022 05:56 AM - edited 07-22-2022 06:33 AM
I personally never felt a strong need for a sparse enum. As error code I usually use a normal enum with a fixed offset. A special error handler does add this offset and generates a standard LabVIEW error, optionally logs those errors, posts them to a separate error handler GUI and a syslog broadcaster.
This makes debugging remote applications including embedded cRIO substantially easier too.
What I have more than once wished would be a Bitflags type. Problem here is that they are not so much sparse (well they are if you want to treat them as enum) but rather non exclusive. You usually want to be able to select any numbers of those flags including None.
07-22-2022 06:08 AM
@rolfk wrote:What I have more than once wished would be a Bitflags type. Problem here is that they are not so much sparse (well they are if you want to treat them as enum) but rather non exclusive. You usually want to be able to select any numbers of those flags including None.
Like a radio button that allows multiple selections, constants looking like a cluster of Booleans, but with an unsigned integer type.
I wanted this pretty much every time I have a 'flag' input to something.
I can't find it on the idea exchange (at least no proper suggestion: Bit flags - NI Community).
07-22-2022 06:31 AM
@cbutcher wrote:
In this case, an enum is quite impractical - enums are limited to sequential positive values, and start from 0. Error code 0 is generally no error, so I'd need some "filler" elements. And to get into a "user range" (although I accept that my current value, 224455, does not fall in one) I'd need at least 5000 filler elements 😉
Another example where it is inconvenient is when there are multiple unused numbers:
#define MSGF_DIALOGBOX 0
#define MSGF_MESSAGEBOX 1
#define MSGF_MENU 2
#define MSGF_SCROLLBAR 5
#define MSGF_NEXTWINDOW 6
You can use an enum, but you'd have to insert multiple dummy entries:
MSGF_DIALOGBOX, MSGF_MESSAGEBOX, MSGF_MENU 2, unused_3, unused_4, MSGF_SCROLLBAR, MSGF_NEXTWINDOW
Or when there are more then one entries with one number:
#define SB_LINEUP 0
#define SB_LINELEFT 0
#define SB_LINEDOWN 1
#define SB_LINERIGHT 1
#define SB_PAGEUP 2
#define SB_PAGELEFT 2
#define SB_PAGEDOWN 3
#define SB_PAGERIGHT 3
All can be fixed with a conversion VI (or two), but a sparse enum would be much nicer.
07-22-2022 09:11 AM
wiebe@CARYA wrote:
@cbutcher wrote:
In this case, an enum is quite impractical - enums are limited to sequential positive values, and start from 0. Error code 0 is generally no error, so I'd need some "filler" elements. And to get into a "user range" (although I accept that my current value, 224455, does not fall in one) I'd need at least 5000 filler elements 😉
Another example where it is inconvenient is when there are multiple unused numbers:
#define MSGF_DIALOGBOX 0
#define MSGF_MESSAGEBOX 1
#define MSGF_MENU 2
#define MSGF_SCROLLBAR 5
#define MSGF_NEXTWINDOW 6
You can use an enum, but you'd have to insert multiple dummy entries:
MSGF_DIALOGBOX, MSGF_MESSAGEBOX, MSGF_MENU 2, unused_3, unused_4, MSGF_SCROLLBAR, MSGF_NEXTWINDOW
Or when there are more then one entries with one number:
#define SB_LINEUP 0
#define SB_LINELEFT 0
#define SB_LINEDOWN 1
#define SB_LINERIGHT 1
#define SB_PAGEUP 2
#define SB_PAGELEFT 2
#define SB_PAGEDOWN 3
#define SB_PAGERIGHT 3
All can be fixed with a conversion VI (or two), but a sparse enum would be much nicer.
I think NI has all it needs to make sparse enums, because it is already hiding unused values. it just happens that they are coming after the last defined value. By "hidden", I mean it is not displaying all possible values for an enum, just the ones we define. Why not just let us define numbers that aren't sequential? Or maybe start with a ring, but make enum-"ish" properties part of the data type?
07-22-2022 09:13 AM
2¢...
I strongly supported sparse enums, but AQ's posts in that idea thread got me thinking a while back and I came to the conclusion that what I really want is something more akin to "scoped enums".
Yes, they should be able to be sparsely defined. There should also be more type safety. If someone wires a numeric value to my enum, I want the wire to break, not automatic coercion. The numeric value is not what the enumeration represents, and it shouldn't be simply assumed that these are compatible types.
Keeping in mind that enums in languages like C# are just syntactic sugar for creating a class, I've put a little bit of thought into getting what I want to go with LVOOP. I haven't really tried to implement it, but in my mind it would take a good bit of scripting and creation of IDE plugins to create something nicely useable for the developer, and the syntax, if that's the right word for a graphical language, would still be pretty clunky. In other words, (I think) LabVIEW has the underpinnings to create the object I want to use, but lacks the syntactic sugar to make it not a total PITA.
07-22-2022 11:06 AM - edited 07-22-2022 11:21 AM
Variant attributes always worked for me and now we have maps.
In either case protecting the data from a write via a private initialize method creates a relatively safe sparse enumish construct that can be written to a UI Ring as strings and values and even contain section separators.
Now back to the Original problem. You are using a type def to define custom errors! Bad, Bad, Bad. They belong in *_errors.txt period! They belong in your lvproj. They should be included in your installer specification. Testing "Pseudo error like rings" just does not work because Errors are not Rings. Even the error ring is not a ring! It's not an error until it appears in *_errors.txt and until it does its merely a cluster of <bool, I32, String!> that LabVIEW can't treat like a defined error and look up by * to see what can throw it and write the description in the explanation.