I have been working on a library to interface with a USB-4704 data aquisition module for work. I have it working great, but the last thing that I need to do is add functionality to support updating a status variable when the device is plugged in or unplugged. I thought this would be easy...
If I start the program with it unplugged I get the correct 'disconnected' status. If I proceed to plug it in I get the correct 'connected' status.
If I start the program with it plugged in i get the correct 'connected' status.
Now if I unplug it while the program is 'connected' I get the 'disconnected' status but plugging it back in does absolutely nothing.
I am blaming the device driver for this, but I can blame all I want it still won't make the code work. Try explaining to your mechanical engineering boss that you can't get something this trivial to work. He will wonder why he hired you.
I am just wondering if anyone has any C# hacking pro-ess or some kind of idea (who knows maybe one of you has had a similar problem).
I have narrowed the problem down to the DeviceHandle pointer being saved somehow within the driver. This function:
DRV_DeviceOpen(0, ref DeviceHandle);
Finds the device and creates a handle for it, returning an error code with 0 meaning no error. When running the program with the device unplugged this returns an error code (non 0) and sets the devicehandle to 0. Now when I run it plugged in, it gets no error and sets the devicehandle to a real number. However, when I run it plugged in then I unplug it mid-execution this function does not return an error code and the DeviceHandle doesn't get reset to 0. Proceeding to plug in the device does not refresh the handle and still returns no error code. Then when I call this function (Gets the voltage input of a channel):
DRV_AIVoltageIn(DeviceHandle, ref AiVolIn);
It returns an error code. I suspect it is because the original DeviceHandle is being used when in fact a new one should be. I need a super-geek that knows how to hack this dll file, or at least the memory that it references to store this DeviceHandle internally (some clear and consice tips would do just fine!)
Here's the whole library if it helps (or if someone wants to steal my code, I could care less haha):
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Security;
using System.Threading;
namespace Mobile4704
{
unsafe public class Mobile4704
{
#region adsapi32.dll Interopp
volatile private IntPtr DeviceHandle;
private uint ErrCde;
private string szErrMsg;
private bool bRun;
private int gnNumOfSubdevices;
private int AiCtrMode;
private PT_DeviceGetFeatures ptDevGetFeatures;
private DEVFEATURES lpDevFeatures;
private PT_DEVLIST devicelist;
private PT_DEVLIST SubDevicelist;
private PT_AIConfig lpAIConfig;
private PT_AIVoltageIn AiVolIn;
private DEVCONFIG_AI lpDEVCONFIG;
private PT_AIGetConfig lpAIGetConfig;
private PT_AIGetConfig ptAIGetConfig;
private PT_CounterEventStart lpCounterEventStart;
private PT_CounterEventRead lpCounterEventRead;
private struct PT_DeviceGetFeatures
{
public uint buffer;
public int Size;
}
private struct GainList
{
public int usGainCde;
public Single fMaxGainVal;
public Single fMinGainVal;
public byte[] szGainStr;
}
private struct DEVFEATURES
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=8)]
public string szDriverVer;
public byte[] szDriverNam;
public uint dwBoardID;
public int usMaxAIDiffChl;
public int usMaxAISiglChl;
public int usMaxAOChl;
public int usMaxDOChl;
public int usMaxDIChl;
public int usDIOPort;
public int usMaxTimerChl;
public int usMaxAlarmChl;
public int usNumADBit;
public int usNumADByte;
public int usNumDABit;
public int usNumDAByte;
public int usNumGain;
public GainList[] glGainList;
public uint[] dwPermutation;
}
private struct PT_DEVLIST
{
public uint dwDeviceNum;
public byte[] szDeviceName;
public int nNumOfSubdevices;
}
private struct PT_AIConfig
{
public ushort DasChan;
public ushort DasGain;
}
private struct PT_AIVoltageIn
{
public ushort chan;
public ushort gain;
public ushort TigMode;
public IntPtr voltage;
}
public struct DAUGHTERSET
{
public uint dwBoardID;
public int usNum;
public Single fGain;
public int usCards;
}
private struct DEVCONFIG_AI
{
public uint dwBoardID;
public uint ulChanConfig;
public int usGainCtrMode;
public int usPolarity;
public int usDasGain;
public int usNumExpChan;
public int usCjcChannel;
public DAUGHTERSET[] Daughter;
public uint[] ulChanConfigEx;
}
private struct PT_AIGetConfig
{
public uint buffer;
public int Size;
}
private struct PT_CounterEventStart
{
public int counter;
public int GateMode;
}
private struct PT_CounterEventRead
{
public uint counter;
public uint overflow;
public IntPtr Count;
}
[DllImport("adsapi32.dll", CharSet=CharSet.Ansi)]
unsafe private static extern uint DRV_GetAddress([MarshalAs(UnmanagedType.AsAny)] object lpVoid);
[DllImport("adsapi32.dll", CharSet = CharSet.Ansi)]
private static extern uint DRV_DeviceGetList(uint deviceList, int MaxEntries, out int nOutEntries);
[DllImport("adsapi32.dll", CharSet = CharSet.Ansi)]
private static extern void DRV_GetErrorMessage(uint lError, string lpszszErrMsg);
[DllImport("adsapi32.dll", CharSet = CharSet.Ansi)]
private static extern uint DRV_DeviceGetNumOfList(ref int NumOfDevices);
[DllImport("adsapi32.dll", CharSet = CharSet.Ansi)]
private static extern uint DRV_DeviceGetFeatures(IntPtr DriverHandle, PT_DeviceGetFeatures lpDevFeatures);
[DllImport("adsapi32.dll", CharSet = CharSet.Ansi)]
private static extern uint DRV_AIGetConfig(IntPtr DriverHandle, ref PT_AIGetConfig AIGetConfig);
[DllImport("adsapi32.dll", CharSet = CharSet.Ansi)]
private static extern uint DRV_AIConfig(IntPtr DriverHandle, PT_AIGetConfig AIGetConfig);
[DllImport("adsapi32.dll", CharSet = CharSet.Ansi)]
private static extern uint DRV_DeviceClose(IntPtr DriverHandle);
[DllImport("adsapi32.dll", CharSet = CharSet.Ansi)]
private static extern uint DRV_DeviceOpen(uint lDeviceNum, ref IntPtr DriverHandle);
[DllImport("adsapi32.dll", CharSet = CharSet.Ansi)]
private static extern uint DRV_CounterReset(IntPtr DriverHandle, int counter);
[DllImport("adsapi32.dll", CharSet = CharSet.Ansi)]
private static extern uint DRV_CounterEventStart(IntPtr DriverHandle, ref PT_CounterEventStart CounterEventStart);
[DllImport("adsapi32.dll", CharSet = CharSet.Ansi)]
private static extern uint DRV_AIVoltageIn(IntPtr DriverHandle, ref PT_AIVoltageIn AIVoltageIn);
[DllImport("adsapi32.dll", CharSet = CharSet.Ansi)]
private static extern uint DRV_CounterEventRead(IntPtr DriverHandle, ref PT_CounterEventRead CounterEventRead);
#endregion
#region Members
float[] fAnalogIn = new float[16];
long iCounter = 0;
long lStatus = 0;
const int MaxEntries = 255;
const int MODULE_AI_CHANNELS = 8;
Thread Updater;
volatile int iPollRate = 0;
bool bKillThread = false;
delegate void delVoidInt(int iStatus);
delegate void delVoidaFloataFloat(float[] data1, int[] data2);
delVoidaFloataFloat doUpdateData;
delVoidInt doStatusChange;
#endregion
//Empty CTOR since this needs to be interoppable with VB6 eventually
public Mobile4704()
{
}
public void Initialize(int _iPollRate)
{
iPollRate = _iPollRate;
Updater = new Thread(new ThreadStart(Refresh));
doUpdateData = new delVoidaFloataFloat(UpdateData);
doStatusChange = new delVoidInt(UpdateStatus);
Updater.Start();
}
private void Refresh()
{
uint Err = 0;
bool bErr = false;
bool bConnected = true;
Err = DRV_DeviceOpen(0, ref DeviceHandle);
if (Err != 0)
{
doStatusChange.Invoke(1);
bConnected = false;
}
float[] fChannelData = new float[16];
do
{
bErr = false;
if (!bConnected)
{
//DRV_DeviceClose(DeviceHandle);
Marshal.FreeHGlobal(DeviceHandle);
DeviceHandle = new IntPtr();
Err = DRV_DeviceOpen(0, ref DeviceHandle);
if (Err != 0)
bErr = true;
}
IntPtr overflow = new IntPtr();
//get analog inputs
for (ushort i = 0; i < MODULE_AI_CHANNELS; i++)
{
float[] data = new float[1];
AiVolIn.voltage = Marshal.AllocHGlobal(sizeof(float));
lpAIConfig.DasChan = i;
AiVolIn.chan = i; //only possible channel
AiVolIn.gain = 4;//only possible gain on 4704
AiVolIn.TigMode = 0;//only possible trigger
Err = DRV_AIVoltageIn(DeviceHandle, ref AiVolIn);
if (Err != 0)
bErr = true;
else
{
Marshal.Copy(AiVolIn.voltage, data, 0, 1);
Marshal.DestroyStructure(AiVolIn.voltage, AiVolIn.voltage.GetType());
fChannelData[i] = data[0];
}
}
//get counter
int[] countData = new int[1];
lpCounterEventRead.counter = 0;
lpCounterEventRead.overflow = DRV_GetAddress(overflow);
lpCounterEventRead.Count = Marshal.AllocHGlobal(sizeof(int));
Err = DRV_CounterEventRead(DeviceHandle, ref lpCounterEventRead);
if (Err != 0)
bErr = true;
else
{
Marshal.Copy(lpCounterEventRead.Count, countData, 0, 1);
doUpdateData.Invoke(fChannelData, countData);
}
if (bErr)
{
bConnected = false;
doStatusChange.Invoke(1);
}
else
{
bConnected = true;
doStatusChange.Invoke(0);
}
Thread.Sleep(iPollRate);
} while (!bKillThread);
}
private void UpdateData(float[] data1, int[] data2)
{
iCounter = data2[0];
for (int i = 0; i < data1.Length; i++)
fAnalogIn[i] = data1[i];
}
private void UpdateStatus(int iStatus)
{
if (iStatus != 0)
{
uint Err = 0;
if (DeviceHandle != IntPtr.Zero)
Err = DRV_DeviceClose(DeviceHandle);
GC.Collect();
DeviceHandle = IntPtr.Zero;
}
lStatus = iStatus;
}
public long Status()
{
return lStatus;
}
public double GetAIChannel(int iChan)
{
return (double)fAnalogIn[iChan];
}
public long GetCounter()
{
return iCounter;
}
public void ResetCounter()
{
uint Err = 0;
Err = DRV_DeviceOpen(0, ref DeviceHandle);
Err = DRV_CounterReset(DeviceHandle, 0);
lpCounterEventStart.GateMode = 3;
lpCounterEventStart.counter = 0;
Err = DRV_CounterEventStart(DeviceHandle, ref lpCounterEventStart);
string err = "";
DRV_GetErrorMessage(Err, err);
}
public void Kill()
{
bKillThread = true;
try
{
DRV_DeviceClose(DeviceHandle);
}
catch { }
do
{ }
while (Updater.IsAlive);
}
}
}
It took a considerable amount of work just to get the inputs working (... I would not recommend to anyone to use this module lol, Advantec really needs to get their software team up to speed.