Jump to content

GregFreeman

Members
  • Posts

    323
  • Joined

  • Last visited

  • Days Won

    5

Posts posted by GregFreeman

  1. I don't believe there are any DVRs, but I can't be certain. There are however various references types everywhere. That being said, for now I have just extended the timeout and it seems to be fine. I am going to assume there is something going on akin to what you mentioned in some way, shape, or form. The code base is a tangled mess and trying to get to root cause may just be a futile exercise. 

  2. I am working with some legacy code that I didn't write and am seeing a strange issue but really am not sure where it has come from. In the last year I have worked with this code, this hasn't come up and I haven't changed anything that I think would effect it.

    In this code, VIs are launched dynamically from a launcher loop using the Run VI method. When the dynamic VI is finished, it sends an event back to the launcher loop, which is then responsible for dynamically launching the next VI. When the launcher loop receives the message that the dynamically launched VI is finished, it polls that VI's Running state until that VI's state changes from Top Level VI to Idle before launching the next VI.  It does this polling with a two second timeout.

    This all works fine most of the time, but then randomly the state of the launched VI will continue to be returned as Top Level VI, not Idle, even though from what I can tell the VI has stopped. I proved this by logging a timestamp to a log file as the very last thing my dynamically launched VI does. I then logged a timestamp after the timeout and they are 2 seconds apart. So this tells me even though I think the launched VI is finished, its Running state as seen by LabVIEW is not changing to Idle.

    I am aware this is not the best way for this to be implemented, but as I mentioned this is a legacy system and I really don't have the ability to refactor the entire application. I'm just trying to grasp an understanding of what may be going on. For now, I've just increased the timeout, hoping this will mitigate the issue in the random cases that this pops up. But I'd really like to find the root cause.

  3. Quote

    The choice you're making is to consider only methods that require lots of boilerplate, methods where one needs to write lots of code that basically duplicates templates with different types, be it user events or Do VIs. 

    ...

    Your original post is basically asking if you should do the simple but ugly shortcut rather than the correct but hard way.  

    Yes, basically.

    The deeper I get into this I think what I need to solve my problems are generics, to get generality and combine it with type safety, and I simply don't have them, so I have to work within the tools I am given.

  4. Quote

    doesn't mean there is a binary choice between multiple User Events and the Command Pattern

    I agree and I didn't mean to imply they are mutually exclusive.

    Quote

      Personally, I wouldn't recommend either of them for general use (unless you are a scripting wizard, perhaps). 

    I assume you mean the fact that you have to create many messages or many refnums which results in a lot of extra classes/code? If so, I do somewhat agree with this. If not, maybe you can provide some more detail into the "why?"

    Quote

    And a single message to multiple registered processes is not hard to do without User Events.

    I also completely agree with this, I am just partial to leveraging built in mechanisms where I can, and I think it makes for clean, readable code with native structures. That's not to say there aren't other viable, valuable solutions. But it is the reason why I tend to lean towards user events.

    I guess if I were to summarize, the thing I have trouble with can be boiled down to two points:

    1) I want to limit the amount a developer has to know about how to create additional user events for a particular process as a regular part of a framework, and how to pass them back to the caller when necessary.  I just used the AF as an example here because I think it handles the ownership of the queues nicely. But if you do want to add in additional queues or events, for example, you have to write that same boiler plate to create the communication mechanism and pass it back to the caller. I want to enforce this, with say a must override VI to create child events and a must register VI in the caller to register for them, but the strict typing of events makes this difficult due to connector pane differences. I have tried leveraging malleable VI's to limit some of the boilerplate code but haven't come up with an ideal solution yet. 

    2) I don't like that I have to expose raw refnums to other processes in order to register for events. But I suppose this is something I have to live with for now.

  5. 16 hours ago, Francois Normandin said:

    I, too, have been uneasy about this for a long time, but the convenience of the mechanism is such that I generally hope that the user is going to use it properly.

    There are two alternatives that I know of:

    - Use the mediator pattern and make sure the user event is unique for your event consumer (if the consumer closes the reference, the mediator can just assume it doesn't want to be notified again and can destroy the thread)

    - Use Actor Framework or another message-based framework that does not expose the queue/event/notifier publicly.

     

    Thanks! The mediator pattern may be a good option. The main drawback I have with any sort of command pattern is that I can't leverage a huge benefit of user events which is the fact that you can have multiple processes register, and only have to send the message out once for them all to receive it. It's great to be able to send a single message and have 3 different windows receive it, my logger receive it, etc and all handle it differently. The command pattern I believe would require you to dynamic dispatch on the messages Do.vi to get a concrete message type, and then immediately pass that concrete type into an Actor's dynamic dispatch "Handle <xyz> Message" VI, which you could override for each child actor. The problem here is you're bumping the "Handle Message" into the base class of the Actor which may not be applicable to many of its children. 

    It seems the correct answer is that everything has its tradeoffs, and it's just a matter of which you want to work with.

  6. On 4/22/2020 at 4:41 AM, pawhan11 said:

    Problems I have with event structure is that it exposes back userevent. Nothing stops user from self generating events or closing this reference.Capture.PNG.088844a02a38b5c8c3190e6622405223.PNG 

    Yes, and not only this but you are essentially required to expose a refnum to a non-owner in order for it to register, which has never sat correctly with me. I think I should just live with it since I've revisited this what seems like hundreds of times.

    I've tried wrapping the logic up in a class with register methods exposed, but it becomes more difficult if a VI is launched asynchronously. In this case you want the parallel VI to create its references so they are tied to the lifetime of that VI, not the caller. Because classes are by value, if you do this you have to pass back all the created references, a-la actor framework and its queues. AF does this well, but I find the overhead of implementing this paradigm with multiple user events in child classes a lot more tedious.

    In some old thread I saw the idea pitched for "registration only" user event refnums which would I think be a fine compromise and something I could live with.

  7. I have been going back and forth on the best method for handling shared data between two processes which may or may not exist at the same time. Assume you have processes A and B, and process A is running, B is not. At some point in the future when B begins running, A needs to register for some events from B. I see two ways to handle this. The first way is aggregation. Some parent process that has visibility of both A and B is responsible for creating the user event refnum and passing it to both processes if/when they are created. This is a simple solution but can be a bit ugly in my opinion, since the refnum will exist even in scenarios where it is unused, among some other issues with encapsulation and the like.

    The second option is to use composition and create the refnum in process B which it is launched, and pass it to process A. This would be done by sending some message to process A to give it the refnum in order to register. This seems to be the cleanest method from an encapsulation standpoint, but also seems to add a lot additional code to the framework as compared to the aggregation method above. 

    There are of course other pros and cons to each of these but those are the most generalized ones I see. I just want to get a feel for what other people out there are doing to solve this.

  8. FWIW, I have always just stored this in a variant in the class private data, and written a private wrapper VI to convert to and from the reference/variant. It makes me twitch slightly every time I do it, but it's functional so that's good enough for me. 

    It's ironic because I just used this the other day, and was debating posting a question on this subject -- whether or not anyone else was using start async call nodes with wait on async call to run things like dialogs. I have been using this to manage the shutdown sequence of windows or processes that run dynamically,  even if I don't need data back, and ensuring they finish before I close references etc. Each dynamic process keeps a reference to any child processes they launch, and when my app shuts down, each layer is responsible for waiting for all async calls to finish before they finish. It seems to nicely propagate up the stack. Just here waiting for someone to tell me about some caveat I didn't consider :)

  9. On 4/4/2020 at 11:53 PM, Aristos Queue said:

    Actors in Actor Framework inherit from each other, and if the parent defines a message to be handled, then the children can handle that message.

    Put another way -- I built an entire framework to handle that exact problem. It doesn't use the Event Structure.

    The only solution I came up with that I liked that used the Event Structure was an entirely new conception of inheritance, a new fundamental type of VI, and a new editor. It not only solves the problem of event inheritance but also front panel inheritance. I have it all mocked up in PowerPoint to build someday (when LV NXG is mature enough).

    Sounds good. I have decided to just register for the events scoped to the parent, in the childrens' event structures. It does result in some duplicate event handling code between children, which I'd like to live in the parent, but it's not really that big a deal. 

    I may refactor to use something more like the actor framework for this class, but I think with the minimal number of events I don't get a whole lot of lift from refactoring

  10. 2 hours ago, LogMAN said:

    Yes you can. Have a dynamic dispatch EventHandler.vi that handles the events from Parent in Parent:EventHandler.vi and a Child:EventHandler.vi for every child. Use Call Parent Method in Child:EventHandler.vi parallel to the event loop of the child. Only the Stop event needs to be handled by each child.

    Here is an example I put together (saved for LabVIEW 2013): Reusable Events 13.0.zip

    Yes, this is the solution I keep wanting to land on except it doesn't cover the case where Event C wants to do something with parent state which is updated from an event. Using your example, assume event A is triggered and the string value becomes state that belongs to the parent. Now assume event C triggers writing that string to a file. The child event handler loop will not have access to the string that was updated in the parent event handler, since the wire was split. I am starting to think that I am trying to make what is more of a by-reference paradigm and fit it into a by value language, and the right solution if this is really needed is to use a by reference class.

  11. I have struggled with this for a long time, but haven't really come up with a solution. I actually found an old message I sent to AQ about 6 years ago (wow) which is related to this, but his response fell by the wayside and I want to revisit it. In that case, it was about dynamic events to reuse code that is part of a Dialog, much like Windows would use OnOK and OnCancel callbacks that were not coupled to a specific dialog, but instead took a dynamic reference to controls on the child dialog. 

    Assume you have a parent class Base and a child class Child. Base has events A and B, Child has event C. You have dynamic dispatch VI's as event handlers in your Base class -- OnA and OnB. What I haven't figured out is, can I reuse OnA and OnB without having to register for Event A and Event B in the child class's event structure? It seems the general methodology would be to have your child class have an event structure, your parent class have a method called "RegisterForEvents" which returns a registration refnum for the A and B events, and then your child class register for these events in its own event structure. Inside the child's event structure, you would then drop the OnA and OnB methods in their respective event cases. 

    Now assume you have Child1, Child2, and Child3, which all inherit from Base. They all have this duplicate registration code, duplicate event cases, and duplicate handler VIs dropped in those event cases. I would like to be able to pull that duplicate logic out. But the only way I can figure out to do that is to have a VI with an event structure in the Base class to encapsulate all the parent event logic, then another in the child class to encapsulate the child event handling. But, now I end up needing two event handling loops on a block diagram, and because classes are by value, I can't easily share state. 

    Has anyone else battled with this problem? Hopefully this makes sense. I can draw up an example if needed.

     

  12. Quote

    You completely borked it! An array of clusters is not an array of pointers to a C struct! It's simply an array of clusters all inlined in one single memory block!

    Look at resize_array_handle_if_required() and what you try to do in there! The first NumericArrayResize() should not allocate n * uPtr anymore but rather use a type of uB and allocate n * sizeof(DevInfo). Of course you still need to allocate any string handles inside the struct but that would be done in:

    Got it, this makes sense. Thanks!

    I suppose from this output when I create 'c file I just didn't correctly reverse engineer the data structures in terms of what LabVIEW wanted. TD1 has a TD2 not a TD2Handle. Looking at it again with your clarification makes sense

    typedef struct {
    	int32_t numPorts;
    	LStrHandle Sn;
    	} TD2;
    
    typedef struct {
    	int32_t dimSize;
    	TD2 elt[1];
    	} TD1;
    typedef TD1 **TD1Hdl;

     

  13. I've taken this one step further, because I realized I will need to return more than just an array of strings, but instead return an array of clusters. I have modified the dll, and sprintf statement seems to output the correct values, but I'm getting garbage back in LabVIEW. My best guess is it has something to do with what my handles are pointing at, but I haven't been able to figure out the issue.

    /*Free an enumeration Linked List*/
    void EXPORT_API iir_usb_relay_device_free_enumerate(IIR_USB_RELAY_DEVICE_INFO_T* info) {
    	//usb_relay_device_free_enumerate((struct usb_relay_device_info*)info);
    	IIR_USB_RELAY_DEVICE_INFO_T *t;
    	if (info) {
    		while (info) {
    			t = info;
    			info = info->next;
    			free(t->serial_number);
    			free(t);
    		}
    	}
    }
    static MgErr resize_array_handle_if_required(DevInfoArrayHdl* hdl, const int32 requestedSize, int32* currentSize) {
    	MgErr err = mgNoErr;
    	if (requestedSize >= *currentSize)
    	{
    		if (*currentSize)
    			*currentSize = *currentSize << 1;
    		else
    			*currentSize = 8;	
    		err = NumericArrayResize(uPtr, 1, (UHandle*)hdl, *currentSize);
    		for (int i = 0; i < *currentSize;i++) {
    			*((**hdl)->elm + i) = (DevInfoHandle)DSNewHClr(sizeof(DevInfo));
    		}
    	}
    	return err;
    }
    
    static MgErr add_dev_info_to_labview_array(DevInfoHandle* pH, const IIR_USB_RELAY_DEVICE_INFO_T* info) {
    	MgErr err = mgNoErr;
    	int len, i = 0;
    	(**pH)->iir_usb_relay_device_type = info->type;
    
    	len = strlen(info->serial_number);
    	err = NumericArrayResize(uB, 1, (UHandle*)&((**pH)->elm), len);
    	if (!err)
    	{
    		MoveBlock(info->serial_number, LStrBuf((*(**pH)->elm)), len);
    		LStrLen(*(**pH)->elm) = len;
    	}
    	return err;
    }
    
    static void free_unused_array_memory(DevInfoArrayHdl* hdl) {
    	int n, i = 0;
    	DevInfoHandle* pH = NULL;
    	if (*hdl)
    	{
    		/* If the incoming array was bigger than the new one, make sure to deallocate superfluous strings in the array! This may look
    			superstitious but is a very valid possibility as LabVIEW may decide to reuse the array from a previous call to this function
    			in a any Call Library Node instance! */
    		n = (**hdl)->cnt;
    		for (pH = (**hdl)->elm + (n - 1); n > i; n--, pH--)
    		{
    			if (*pH)
    			{
    				DSDisposeHandle(*pH);
    				*pH = NULL;
    			}
    		}
    	}
    }
    
    IIR_USB_RELAY_DEVICE_INFO_T EXPORT_API * iir_usb_relay_device_enumerate(void) {
    	//return (IIR_USB_RELAY_DEVICE_INFO_T*)usb_relay_device_enumerate();
    	IIR_USB_RELAY_DEVICE_INFO_T* ptr = NULL;
    	IIR_USB_RELAY_DEVICE_INFO_T* deviceInfo = NULL;
    	IIR_USB_RELAY_DEVICE_INFO_T *prev = NULL;
    	int len = 0;
    	const char* sn[] = {
    		"abcd",
    		"efgh",
    		"ijkl",
    		NULL
    	};
    	IIR_USB_RELAY_DEVICE_TYPE deviceType[] = {
    		IIR_USB_RELAY_DEVICE_ONE_CHANNEL,
    		IIR_USB_RELAY_DEVICE_TWO_CHANNEL,
    		IIR_USB_RELAY_DEVICE_FOUR_CHANNEL
    	};
    	for (int j = 0;sn[j];j++) {
    		IIR_USB_RELAY_DEVICE_INFO_T* info = (IIR_USB_RELAY_DEVICE_INFO_T*)malloc(sizeof(IIR_USB_RELAY_DEVICE_INFO_T));
    
    		len = (int)strlen(sn[j]) + 1;
    		info->serial_number = (unsigned char*)malloc(len);
    		info->type = deviceType[j];
    		memcpy(info->serial_number, sn[j], len);
    		info->next = NULL;
    		if (!deviceInfo) {
    			deviceInfo = info;
    		}
    		else {
    			prev->next = info;
    		}
    		prev = info;
    	}
    	return deviceInfo;
    }
    
    int EXPORT_API iir_get_device_info(DevInfoArrayHdl *arr) {
    	MgErr err = mgNoErr;
    
    	IIR_USB_RELAY_DEVICE_INFO_T* ptr = NULL, *prev = NULL;
    	DevInfoHandle* pDevInfo = NULL;
    	IIR_USB_RELAY_DEVICE_INFO_T* deviceInfo = (IIR_USB_RELAY_DEVICE_INFO_T*)iir_usb_relay_device_enumerate();
    
    	int i = 0, n = (*arr) ? (**arr)->cnt : 0;
    
    	for (ptr = deviceInfo; ptr; ptr = ptr->next, i++)
    	{
    		err = resize_array_handle_if_required(arr, i, &n);
    		if (err)
    			break;
    		pDevInfo = (**arr)->elm + i;
    		err = add_dev_info_to_labview_array(pDevInfo,ptr);
    		if(err)
    			break;
    	}
    
    	iir_usb_relay_device_free_enumerate(deviceInfo);
    	free_unused_array_memory(arr);
    
    	DevInfoHandle* hdl2;
    	char buf[1024];
    	(**arr)->cnt = i;
    	for (hdl2 = (**arr)->elm,i=0; i<(**arr)->cnt ;i++,hdl2++) {
    		sprintf_s(buf, 1024, "%s: %d", (*(**hdl2)->elm)->str, (**hdl2)->iir_usb_relay_device_type);
    	}
    	return err;
    }
          

     

  14. On 11/15/2019 at 5:54 PM, Rolf Kalbermatter said:

    There were a few errors in how you created the linked list. I did this and did a quick test and it seemed to work.

    The chunk in the debugger is expected. The debugger does not know anything about LabVIEW Long Pascal handles and that they contain an int32 cnt parameter in front of the string and no NULL termination character. So it sees a char array element and tries to interpret it until it sees a NULL value, which is of course more or less beyond the actual valid information.

     

    Very helpful, now it's working. Another big problem I realized is that I had the CLFN set to WINAPI, not C, calling convention 🤦‍♂️

  15. Ah, that makes sense. Needs to be a pointer to the handle...

    I am not currently crashing mid-function anymore, but i do crash when it reaches the end. I have attached the code as it stands now. I'm creating a linked list myself since I don't have access to the hardware. I am seeing something strange in the visual studio debugger. Notice the value of the str variable in hdl has some junk after "efgh." It's making me think something in the resize and MoveBlock aren't quite right, but I can't figure out what. 

    image.png.c1ecd2dbb1b1d4bbc83cf88f5d66e6e8.png

    
    int EXPORT_API iir_get_serial_numbers(LStrArrayHandle *arr) {
    	MgErr err = mgNoErr;
    
    	IIR_USB_RELAY_DEVICE_INFO_T* ptr = NULL;
    	IIR_USB_RELAY_DEVICE_INFO_T* deviceInfo = NULL;
    	IIR_USB_RELAY_DEVICE_INFO_T *prev = NULL;
    	LStrHandle *pH = NULL;
    
    	const char* sn[] = {
    		"abcd",
    		"efgh",
    		"ijkl"
    	};
    
    	int len, i = 0, n = (*arr) ? (**arr)->cnt : 0;
    
    	for (int j = 0;j < 3;++j) {
    		IIR_USB_RELAY_DEVICE_INFO_T* info = (IIR_USB_RELAY_DEVICE_INFO_T*)malloc(sizeof(IIR_USB_RELAY_DEVICE_INFO_T));
    		info->serial_number = (unsigned char*)malloc(5*sizeof(unsigned char*));
    		strcpy_s(info->serial_number,5*sizeof(unsigned char*),sn[j]);
    		info->next = NULL;
    		if (!deviceInfo) {
    			deviceInfo = info;
    		}
    		else {
    			prev->next = info;
    		}
    		prev = info;
    	}
    	//IIR_USB_RELAY_DEVICE_INFO_T* deviceInfo = (IIR_USB_RELAY_DEVICE_INFO_T*)iir_usb_relay_device_enumerate();
    
    	/* This only works reliably if there is guaranteed that the deviceInfo linked list won't change
    		in the background while we are in this function! */
    	for (ptr = deviceInfo; ptr; ptr = ptr->next, i++)
    	{
    		/* Resize the array handle only in power of 2 intervals to reduce the potential overhead
    			for resizing and reallocating the array buffer every time! */
    		if (i >= n)
    		{
    			if (n)
    				n = n << 1;
    			else
    				n = 8;
    			err = NumericArrayResize(uPtr, 1, (UHandle*)arr, n);
    			if (err)
    				break;
    		}
    
    		len = strlen(ptr->serial_number);
    		pH = (**arr)->elm + i;
    		err = NumericArrayResize(uB, 1, (UHandle*)pH, len);
    		if (!err)
    		{
    			MoveBlock(ptr->serial_number, LStrBuf(**pH), len);
    			LStrLen(**pH) = len;
    		}
    		else
    			break;
    	}
    	if (deviceInfo) {
    		IIR_USB_RELAY_DEVICE_INFO_T *t;
    		while (deviceInfo != NULL) {
    			t = deviceInfo;
    			deviceInfo = deviceInfo->next;
    			free(t);
    		}
    	}
    
    	/* If we did not find any device AND the incoming array was empty it may be NULL as this is the canonical empty array value in LabVIEW.
    		So check that we have not such a canonical empty array before trying to do anything with it! It is valid to return a valid array handle
    		with the count value set to 0 to indicate an empty array!*/
    	if (*arr)
    	{
    		/* If the incoming array was bigger than the new one, make sure to deallocate superfluous strings in the array! This may look
    			superstitious but is a very valid possibility as LabVIEW may decide to reuse the array from a previous call to this function
    			in a any Call Library Node instance! */
    		n = (**arr)->cnt;
    		for (pH = (**arr)->elm + (n - 1); n > i; n--, pH--)
    		{
    			if (*pH)
    			{
    				DSDisposeHandle(*pH);
    				*pH = NULL;
    			}
    		}
    		(**arr)->cnt = i + 1;
    		char buf[1024];
    		LStrHandle hdl;
    		for (int k = 0; k < ((**arr)->cnt);++k) {
    			hdl = (**arr)->elm[k];
    		}
    
    	}
    	//iir_usb_relay_device_free_enumerate(deviceInfo);
    	return err;
    }

     

  16. 3 hours ago, Rolf Kalbermatter said:

    A string array is simply an array handle of string handles! Sounds clear doesn't it? 😄

    In reality we didn't just arbitrarily create this data structure but LKSH simply defined it knowing how a LabVIEW array of string handles is defined. You can actually get this definition from LabVIEW by creating the Call Library Node with the Parameter configured to Adapt to Type, Pass Array Handle Pointer, then right click on the Call Library Node and select "Create .c file". LabVIEW then creates a C file with all datatype definitions and an empty function body.

    Actually, I did this yesterday to try and figure out the datatypes 😂. I thought some other magic was happening but I suppose not. I guess my question then becomes, why do we choose the particular sizes for uPtr, and how does that translate to our struct size? I suppose I'm just not understanding the relation between uPtr, the cast of arr to UHandle, and 'n' in terms of what is being done inside that resize function.

     

    Anyways, unfortunately I keep crashing here:

    err = NumericArrayResize(uB, 1, (UHandle*)pH, len);

    I did get a compiler error here: 

    pH = (**strArr)->elm + i;

    I assume it should be (*strArr) -> elm + i; but I'm not certain. I did change it to this however to get it to compile

    I have confirmed my variable 'len' = 4 for my serial_number 'abcd' 

    Other than that, I'm not sure what the problem may be. Do we need to include the \0 in the len?

    I believe doing (UHandle*)(&pH) gets us closer?

     

  17. 2 hours ago, Rolf Kalbermatter said:

    Another option is that the API returns a newly allocated list and you need to call a freeDeviceInfo() or similar function everytime afterwards to deallocate the entire list.

    Looking at the API, I believe this is the case. 

    @Rolf Kalbermatter would you mind explaining what is happening with the first NumericArrayResize? The one for the string buffer is pretty self explanatory, but I don't quite understand how the first one that resizes the LStrArrayHandle works. How does the function know how to resize memory for a data structure that we have defined ourselves?

  18. Very helpful, both of you. Thanks!

    Rolf, you mention this will only work if the linked list doesn't change in the background, which of course makes sense. Theoretically it won't change, but it is coming from another API, so I assume there is no way to handle keeping this safe? Even if I were to loop through the linked list immediately and make copies of the items and/or serial numbers, during my looping there is still the risk it could change, correct?

  19. I have a DLL for a device which holds a linked list of structs. Obviously I cannot pass this to LabVIEW, but all I really need are the serial numbers from within the struct. So my idea is to write a wrapper to map the serial numbers to a string array and return them from a dll. Due to my lack of experience with C and lack of examples with the LabVIEW memory manager dll, I'm having quite a hard time. Because the linked list can be of a dynamic length, I am not sure how to handle the memory allocation. This is my attempt so far.

    My questions are

    1) How do I properly size DSNewHandle to fit the serial number, or is there no need to since it's a pointer which will just be set equal to the address of s_deviceInfo -> serialNumber

    2) how do I assign each new 'h' to 'arr'

    3) since the number of devices/serial numbers can vary, can I still manage this on the LabVIEW side by passing in an empty array constant as 'arr' to the dll? Or do I need to modify how I'm doing things?

        typedef struct { 
            int32 len;  
            LStrHandle str[];
        } **LStrArrayHandle;
     
       int iir_get_serial_numbers(LStrArrayHandle arr) {
            int i = 0;
     
            while(s_deviceInfo -> next) {
                LStrHandle h = (LStrHandle)DSNewHandle(sizeof(LStrHandle));
                (*h) -> cnt = strlen(s_deviceInfo -> serial_number);
                (*h) -> str = s_deviceInfo -> serial_number;
            }
            return 0;
        }
  20. Nope, we have explicit, static registrations for controls in our subpanels. Nothing dynamic or using references. 

    We did potentially trace things back to our error logger. We have a subVI that just throws errors in a queue and then a process that flushes the queue every few seconds and logs them. Seems when we aren't logging errors the problem goes away, so I'm not really sure what's going on. Possibly something there blocking our UI thread somehow and events getting missed but the flush and write happens within 100 ms so it definitely still seems a bit strange. Right now it seems the problem may not be happening in the executable and FWIW we are also updating a good amount of controls by reference with defer panel updates set to true. But the VI Analyzer shows this only taking ~50 ms so I'm not convinced that's the issue either.

  21. I have an application that is using subpanels, and I am having some issues where they seem to be missing button clicks. This is sporadic and not isolated to one specific button but seems to happen throughout the application on various screens. I look in the event inspector window and the value change never fires when these events are missed, so it's as if whatever is managing these events is missing it completely. After two or three clicks it will take. I know it's tough to help without a reproducible case, so I am mostly posting this to see if others have run into this behavior because I have been spinning my wheels. 

  22. Sorry for the ambiguous title...hard to convey the problem without description.

    Right now I have an application that takes various measurements, but for now I'm going to focus on current. The issue is that there are many devices that can take current measurements, which our customer will swap out. But they don't necessarily have a parent/child relationship. Everything I think of screams to me "This would be easy with interfaces" but I'm really hoping there is some solution with composition. 

    The application has a lot of situations where there is a power supply, but they may use a DVM to take the current measurement because they get better resolution on their results. There are other times they just use the current measurement the power supply reports. So, I thought about having a current measurement class that is composed of a "Source" object and a "Measurement" object. In some cases the Source and Measurement objects would both be a power supply. In other cases one may be a power supply and the other a DVM. But, I also want all my power supplies to inherit from BasePowerSupply and the DVM to inherit from BaseDVM. But if I want to do this and have either a power supply or a DVM be the measurement class, they both have to inherit from the same base class with a MeasureCurrent must-override method. However, as soon as I do this, if I want to create a a Source class, the BasePowerSupply can't inherit from it too. 

    To me this just screams having an ICurrentMeasurable interface and an ISourceable interface that classes can implement. But alas, I cannot. So, any suggestions are appreciated. It just seems to me the more complex instruments get the more I really want interfaces to keep overlapping functionality required while avoiding coupling of unrelated devices through inheritance.

×
×
  • Create New...

Important Information

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