LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

abstraction layer concept for portability

Solved!
Go to solution

I have a goal in mind but not sure how to implement.

 

I have a set of rather low-level custom hardware libraries that I'm trying to keep devoid of any specific LabWindows GUI controls. In other words, these libraries only consist of hardware accessing functions and do not themselves access a GUI control (such as LEDs or switches, etc.). Then my GUI would make calls to these functions and interact with the GUI controls directly.  My #1 reason for this is portability.  I want to be able to drop this library into any LabWindows project which has its own unique GUI and set of controls and use the library immediately.

 

Now the challenge: some of these hardware functions contain unavoidable looping.  For instance, one such function is a simple memory reader / writer.  It would be ideal to have the GUI that calls this function show a progress bar that updates for each memory address in the range.  But I don't see a way to do that without having the control updating done at the lower hardware function level.

 

I've experimented with ProcessDrawEvents and ProcessSystemEvents inside the hardware function, but this doesn't work.  I could also simply add a couple parameters, such as "panel" and "control" to the hardware library function and then inside the function do a

 

SetCtrlVal(panel,control,loopCount);

But even this isn't totally portable.  What if in application A I prefer to do not just a SetCtrlVal, but a SetCtrlAttribute for each loop count?  The library function would have to change per application in this case.

 

Isn't there some way of sending the caller a periodic update of the called function's status while it's running?

0 Kudos
Message 1 of 6
(4,560 Views)

Hi,

You can keep the progress in a global variable (a thread-safe variable will be safer) in your hardware library and write a new function that returns its current value from another thread in your specific GUI application.

 

You can put your progress bar in a separate panel related with that new thread. Thread will do the looping at the GUI side and your hardware function will do the looping internally. You can get updated only through the new function which queries the progress.


Do not forget to initialize any such global in your hardware init function which needs to be called first by any application which uses your library.

Hope this helps,

S. Eren BALCI
IMESTEK
Message 2 of 6
(4,544 Views)

Another option is to put the extra thread also into your hardware driver.

 

That thread may sleep most of its life and then after being signaled by the hardware function that it is entering into looping, the thread can keep track of the progress in its own loop and display it.

 

That thread, again, can have its own panel, which will look like a floating little window over all other panels.

 

If you like, you can add an input to your hardware function as an option to display the progress or not.

If user do not want the progress bar it passes 0 to this parameter and you do not signal the thread in that case.

 

Adding a panel and a thread to your driver requires extra setup and cleanup, all to be handled by the 'initialize' and 'finalize' functions of your driver. If you do not have such functions it is time to write them 😉

S. Eren BALCI
IMESTEK
Message 3 of 6
(4,540 Views)
Solution
Accepted by ElectroLund

Another alternative is to pass to your hardware function the address of a function: the low level routine could then call this function in PostDeferredCall passing the percentage of completion in callbackData parameter.

The deferred function [ void CVICALLBACK myFunction (void *callbackData);is called in the main thread and can take care of updating the user interface; this way, the low level function has no need to know panel handle and control ID, which are maintained at the UI level.



Proud to use LW/CVI from 3.1 on.

My contributions to the Developer Community
________________________________________
If I have helped you, why not giving me a kudos?
0 Kudos
Message 4 of 6
(4,526 Views)

ebalci, I like your suggestions with multithreading.  However, I don't think I'm ready yet for that approach.

 

Roberto, you've uncovered another corner of CVI I didn't know existed!  So I've done some experiments with your approach and it's quite simple and elegant.  I've attached a test project I made.

 

But I'm not totally clear how it works.  I was under the impression that a for/while/do while loop was blocking, i.e., the user interface did not process other events until the loop terminated.  As you'll see in my sample project, I'm only doing a PostDeferredCall inside my loop, not a ProcessSystemEvents.

 

Now one odd side effect is that if you click the Go button multiple times before the hardware function completes, it will re-enter.  Do you have a clever way of preventing that?  The obvious solution would be to dim any controls that would call the hardware loop function.  But that would still be inherently dangerous, as it would be up to the developer to remember that.

 

 

0 Kudos
Message 5 of 6
(4,503 Views)

It's not true you are not processing events: inside your loop you have a DelayWithEventProcessing () that is clearly proccessing events Smiley Wink If you replace that instruction with a simple Delay you'll have your UIR blocked until the function ends.

 

How your hardware-related function is implemented can have a great impact on the application behavior: using a simple loop can completely block it down unless you process events someway. In this respect, ebalci suggestion to implement some form of multithreading is perhaps the best way to go, unless you accept this interaction. Keep in mind that if the function has no relation with the rest of the application (i.e. it does not exchange data with other threads) multithreading it can be very simple: I don't know what the function is expected to do, but as a very general approach you could implement is as a state machine with an asynchronous timer, which is multithreaded by itself.

 

With reference to your last question, how to block execution of the function until previous instance has terminated can be solved various ways. I can imagine at least two scenarios:

  • Implement the interlock in the UI thread: either by dimming the button or by setting some flag to test before calling the low level function
  • Implement interlock in the low level function, for example using a thread safe variable, a thread lock or some other mechanism of interprocess synchronization

In my opinion the first scenario is easier to implement but imples more risk: if you are developing your own application you can do this way; on the other hand, you you are developing a library to supply to other programmers / customers it's better to implement some lock at low level: for example the hardware function could create and get a lock, releasing it before completion; if called another time it fails in creating the lock and terminates returning an error.



Proud to use LW/CVI from 3.1 on.

My contributions to the Developer Community
________________________________________
If I have helped you, why not giving me a kudos?
Message 6 of 6
(4,498 Views)