Jump to content


  • Content Count

  • Joined

  • Last visited

  • Days Won


GregFreeman last won the day on April 19 2018

GregFreeman had the most liked content!

Community Reputation


About GregFreeman

  • Rank
    Extremely Active
  • Birthday 09/13/1986

Profile Information

  • Gender
  • Location

LabVIEW Information

  • Version
    LabVIEW 2013
  • Since

Recent Profile Visitors

2,392 profile views
  1. 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
  2. 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.
  3. 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.
  4. 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;
  5. 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; }
  6. Very helpful, now it's working. Another big problem I realized is that I had the CLFN set to WINAPI, not C, calling convention 🤦‍♂️
  7. 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. 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; }
  8. 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?
  9. 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?
  10. 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?
  11. Thanks very much. This is really helpful and makes a lot of sense. My only follow up question is, is it necessary to size the LStrHandle to fit the string and use strcpy? Or can I just assign h -> str to point at the address of s_deviceInfo -> serial_number?
  12. 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; }
  13. 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.
  14. 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.
  15. Thanks very much everyone. And I really appreciate the UML -- very helpful
  • Create New...

Important Information

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