using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading; // threading using System.Runtime.InteropServices; // for GetLastError() using AIOWDMNet; // the namespace exposes the AIOWDM Class interface namespace WDMSample { public partial class Form1 : Form { // Thread for Interrupt handling: Thread myThread; public bool GnericOnlyFlag = false; // Using MS Mutex example to manage thread(s): // Create a new Mutex. The creating thread does not own the Mutex. private static Mutex mut = new Mutex(); private const int numIterations = 1; private const int numThreads = 1; UInt32 Status = 0; // This is the basic data to discover and interface with the card: const UInt32 MAX_CARDS = 10; static int NumCards = 0; static int CardNum = 0; public UInt32 Offset = 0; // This the offset that will be used based on the base address of the card public bool RunFlag = false; static int LastError = 0; static bool bAbort = false; // Unlike our other language samples we will be using classes instead of structures in C# to store the card data: // Card Specific Data Class: public class TCOSData { public TCOSData(byte i) { A = B = C = i; } public byte A, B, C; } public class TCardData { public TCardData(int sizeCOS) { mi = sizeCOS; IsValid = false; IsSelected = false; DeviceID = 0; Base = 0; PPIs = 0; IsWDG = false; IRQCount = 0; LastCOSData = new TCOSData[4]; //$$$ 5 max Log = "NULL"; WDGPets = 0; } public int mi; public bool IsValid; public bool IsSelected; public uint DeviceID; public uint Base; public uint PPIs; public bool IsWDG; public uint IRQCount; public TCOSData[] LastCOSData; public String Log; public uint WDGPets; } // Declare an array of class objects: public TCardData[] CardData = new TCardData[MAX_CARDS]; // array of 10 max structs // Interval Timer fou WDG updates: // (Note: A Windows Forms timer designed for single threaded environments not a System Timer 55 ms min res) static System.Windows.Forms.Timer myWDGTimer = new System.Windows.Forms.Timer(); public Form1() { InitializeComponent(); // Create the card data array elements: for (int i = 0; i != MAX_CARDS; i++) { CardData[i] = new TCardData(4); for (int j = 0; j != 4; j++) { CardData[i].LastCOSData[j] = new TCOSData(0); } } // Add the event and the event handler for the method that will // process the WDG timer event to the timer: myWDGTimer.Tick += new EventHandler(WDGTimerEventProcessor); // Set the timer interval in miliseconds and start (55ms min resolution): // start later on demand for WDG: //myWDGTimer.Interval = 1000; } private void Form1_Load(object sender, EventArgs e) { // Test for driver install using an absolute address: if (AIOWDM.InPortB(0x61) == 0xAA55) { MessageBox.Show("ACCESNT.SYS not detected. Please copy ACCESNT.SYS into [NT]/system32/drivers and re-run this sample.", "Warning"); } // This will get all the installed card info: FindCardsWDM(); } private void FindCardsWDM() { bool found = false; // Vars for QueryCardInfo() : UInt32 DeviceID, Base; UInt32 NameSize = 256; UInt16[] Name = new UInt16[256]; String strname; // Init all CardData: for (int i = 0; i < MAX_CARDS; i++) { CardData[i].IsValid = false; CardData[i].IsSelected = false; CardData[i].DeviceID = 0; CardData[i].Base = 0; CardData[i].PPIs = 0; CardData[i].IRQCount = 0; CardData[i].WDGPets = 0; CardData[i].IsWDG = false; for (int j = 0; j < 4; j++) { CardData[i].LastCOSData[j].A = 0; CardData[i].LastCOSData[j].B = 0; CardData[i].LastCOSData[j].C = 0; } } RunFlag = true; // Get the total number of cards installed: NumCards = AIOWDM.GetNumCards(); if (NumCards == 0) // no cards present { strname = String.Format("No Card Found"); comboBoxCard.Items.Add(strname); comboBoxCard.SelectedIndex = 0; textBoxMessage.Text = "No cards were found!"; textBoxMessage.Text += "This may mean the card is not installed. "; textBoxMessage.Text += "Check Device Manager for a card and its status"; textBoxMessage.Text += "You may consider rebooting your system."; comboBoxCard.Enabled = false; RunFlag = false; } else { // Loop through the all cards detected and validate and store card data: for (CardNum = 0; CardNum != NumCards; CardNum++) { CardData[CardNum].IsWDG = false; // Get each cards data: Status = AIOWDM.QueryCardInfo(CardNum, out DeviceID, out Base, out NameSize, out strname); // Populate list box with cards found and set flags: switch (DeviceID) { case 0x0E50: //PCI-DIO-24S strname = String.Format("PCI-DIO-24S I/O Card"); comboBoxCard.Items.Add(strname); CardData[CardNum].PPIs = 1; CardData[CardNum].Base = Base; CardData[CardNum].IsValid = true; CardData[CardNum].DeviceID = DeviceID; found = true; break; case 0x0E56: //PCI-DIO-24DCS strname = String.Format("PCI-DIO-24DCS I/O Card"); comboBoxCard.Items.Add(strname); CardData[CardNum].PPIs = 1; CardData[CardNum].Base = Base; CardData[CardNum].IsValid = true; CardData[CardNum].DeviceID = DeviceID; found = true; break; case 0x0E60: //PCI-DIO-48S strname = String.Format("PCI-DIO-48S Parallel Digital I/O Card"); comboBoxCard.Items.Add(strname); CardData[CardNum].PPIs = 2; CardData[CardNum].Base = Base; CardData[CardNum].IsValid = true; CardData[CardNum].DeviceID = DeviceID; found = true; break; case 0x22C0://PCI-WDG-CSM case 0x25C0: case 0x2FC0: case 0x2FC1: //PCI-WDG-CSM, P104-WDG-E, P104-WDG-CSM, and P104-WDG-CSMA strname = String.Format("WDG Watchdog Board"); comboBoxCard.Items.Add(strname); CardData[CardNum].IsWDG = true; AIOWDM.WDGInit(CardNum); CardData[CardNum].Base = Base; CardData[CardNum].IsValid = true; CardData[CardNum].DeviceID = DeviceID; found = true; break; case 0x2EE0: //PCIe-DIO-24s-CTR12 strname = String.Format("PCIe-DIO-24S-CTR12 Parallel Digital I/O Card"); comboBoxCard.Items.Add(strname); comboBoxCard.SelectedIndex = 0; CardData[CardNum].PPIs = 1; CardData[CardNum].Base = Base; CardData[CardNum].IsValid = true; CardData[CardNum].DeviceID = DeviceID; found = true; break; }//switch // Save and Display card specific text information: if (CardData[CardNum].PPIs != 0) { CardData[CardNum].Log = ("AIOWDM provides generic IRQ-handling functions for all cards " + "and special IRQ-handling functions for COS cards like this one. \r\n\r\n" + "Click 'Detect Generic IRQs' to handle IRQs generically for the card selected above, " + "or 'Detect COS IRQs' to handle IRQs for the card selected above by updating COS data. \r\n\r\n"); } else { if (CardData[CardNum].IsWDG) { CardData[CardNum].Log = ("AIOWDM provides generic IRQ-handling functions for all cards " + "and special functions for watchdog cards like this one.\r\n\r\n" + "Click 'Detect Generic IRQs' to handle IRQs generically for the card selected above, " + "or 'Handle Watchdog IRQ' to handle the next IRQ for the card selected above with the selected action.\r\n\r\n" + "You can also click 'Test Watchdog Timing' to set the watchdog timer, pet it a few times, then let it time out, " + "'Read Temp' to read the temperature(if installed)," + "'Read Status' to read the card''s status byte," + "or 'Reboot' to quickly reboot your computer.\r\n\r\n"); } else { CardData[CardNum].Log = ("AIOWDM provides generic IRQ-handling functions for all cards. \r\n\r\n" + "Click 'Detect Generic IRQs' to handle IRQs for the card selected above.\r\n\r\n"); } } }// end loop on validate present cards (Card NUm is +1) }//end else card present // If card(s) are present but no valid cards were found: if ((NumCards != 0) && (found == false)) { strname = String.Format("No Valid Card Found"); comboBoxCard.Items.Add(strname); comboBoxCard.SelectedIndex = 0; textBoxMessage.Text = "No valid card was found!"; textBoxMessage.Text += "This may mean the card is not installed. "; textBoxMessage.Text += "Check Device Manager for a card and its status"; textBoxMessage.Text += "You may consider rebooting your system."; comboBoxCard.Enabled = false; RunFlag = false; } // Once done set initial default values: if (RunFlag) { // This is the result setting the initial Card Num and offset to be used: CardNum = 0; // default to 0 first card found (before next call) Offset = 0; //default to 0 offset from base for any relative Port read or writes calls // Default to first card found: // This will cause a call to selected index changed function and setup the GUI comboBoxCard.SelectedIndex = 0; } textBoxMessage.Text = CardData[CardNum].Log; } private void comboBoxAddress_SelectedIndexChanged(object sender, EventArgs e) { // Set the new Card Num and Offset to be used: CardNum = comboBoxCard.SelectedIndex; // set CardNum to match card index selected Offset = 0; //default to 0 offset from base for any relative Port read or writes // Display correct log text textBoxMessage.Text = CardData[CardNum].Log; // Display all data etc here as needed: string formatString; // Generic IRQs : if (CardData[CardNum].IRQCount == 0) { labelGeneric.Text = ""; } else { formatString = String.Format("{0,3:D}", CardData[CardNum].IRQCount); labelGeneric.Text = formatString; // in main thread } // COS IRQs : // If card has PPI then display COS data A,B,C labelCOS.Text = ""; if (CardData[CardNum].PPIs != 0) { formatString = ""; for (int i = 0; i < (int)CardData[CardNum].PPIs; i++) { formatString += String.Format("{0,2:X2} ", CardData[CardNum].LastCOSData[i].A); formatString += String.Format("{0,2:X2} ", CardData[CardNum].LastCOSData[i].B); formatString += String.Format("{0,2:X2} ", CardData[CardNum].LastCOSData[i].C); } labelCOS.Text = formatString; // Clear/Enable COS Interrupt for selected board: AIOWDM.RelOutPortB(CardNum, Offset + 11, 0x00); // Note: This has to be called to do IRQ's } // Set which features are present in GUI ie COS and WDG: UpdateGUIFeatures(); } private void UpdateGUIFeatures() { // Set which features are present COS and WDG: if (CardData[CardNum].PPIs != 0) groupBoxCOS.Enabled = true; else groupBoxCOS.Enabled = false; if (CardData[CardNum].IsWDG) groupBoxWDG.Enabled = true; else groupBoxWDG.Enabled = false; } //private static void MyThreadProc() private void MyThreadProc() { // Here we will wait on generic or COS IRQ based on presence of PPIs: // Later we will process results in recieve based on same information UInt32 retval = 0; // temp holder for COS data: byte[] tempData = new byte[12]; while (true) { // Flag for IRQ type: if (!GnericOnlyFlag) { retval = AIOWDM.COSWaitForIRQ(CardNum, (uint)CardData[CardNum].PPIs, tempData); // Copy COS data array into struct: // COS boards will only have 1 or 2 PPIs so 3 or 6 bytes for chan ABC int j = 0; for (int i = 0; i < 4; i++) { j = i * 3; CardData[CardNum].LastCOSData[i].A = tempData[j]; CardData[CardNum].LastCOSData[i].B = tempData[j + 1]; CardData[CardNum].LastCOSData[i].C = tempData[j + 2]; } } else { retval = AIOWDM.WaitForIRQ(CardNum); } if (retval != 0) { if (!bAbort) // If you exit Main could be gone before this thread is done ReportIRQ(); } else { if (!bAbort) // If you exit Main could be gone before this thread is done { ReportError(); } break; } } // Once you get here and leave this function the thread will exit: } // Updates the GUI with ERR codes: private void UpdateErrorText(string text) { // Set the GUI text. textBoxMessage.Text = text; } // Must use threadsafe delegate across threads: public delegate void UpdateErrTextCallback(string text); //private static void ReportError() private void ReportError() { // Wait until it is safe to enter. mut.WaitOne(); LastError = Marshal.GetLastWin32Error(); // These are Windows Error codes used for Errors and expected status messages: int ERROR_SUCCESS = 0; int ERROR_INVALID_FUNCTION = 1; int ERROR_FILE_NOT_FOUND = 2; int ERROR_NO_MORE_FILES = 18; int ERROR_OPERATION_ABORTED = 995; int Error = LastError; string strMessage; if (Error == ERROR_OPERATION_ABORTED) strMessage = "IRQ request aborted.\r\n"; else if (Error == ERROR_INVALID_FUNCTION) strMessage = "Error: this card already has a pending IRQ request. IRQ request aborted.\r\n"; else strMessage = "Some error occurred in the driver. IRQ request aborted.\r\n"; // Now from the thread you can safely call this: textBoxMessage.Invoke(new UpdateTextCallback(this.UpdateErrorText), new object[] { strMessage }); // Release the Mutex. mut.ReleaseMutex(); } // Comments left in from ms C# threading example // This method represents a resource that must be synchronized // so that only one thread at a time can enter. private void ReportIRQ() { // Wait until it is safe to enter. mut.WaitOne(); //Console.WriteLine("{0} has entered the protected area", // Thread.CurrentThread.Name); // Access non-reentrant resources here. // Do some work. // Here recieve IRQ process as either generic or COS: if(GnericOnlyFlag) ReceiveGenIRQ(CardNum); else ReceiveCOSData(CardNum); //Console.WriteLine("{0} is leaving the protected area\r\n", //Thread.CurrentThread.Name); // Release the Mutex. mut.ReleaseMutex(); } // Updates the GUI: private void UpdateGenericText(string text) { // Set the GUI text. labelGeneric.Text = text; } // Must use threadsafe delegate across threads: public delegate void UpdateTextCallback(string text); public void ReceiveGenIRQ(int CardNum) { // If you exit App, Main thread could be gone before this thread is done // so cant go here on exit because this calls into the main thread // Increment count convert and display: CardData[CardNum].IRQCount++; int count = (int)CardData[CardNum].IRQCount; string formatString; formatString = String.Format("{0,3:D}", count); // Now from the thread you can safely call this: labelGeneric.Invoke(new UpdateTextCallback(this.UpdateGenericText), new object[] { formatString }); } // Updates the GUI: private void UpdateCOSText(string text) { // Set the GUI text. labelCOS.Text = text; } public void ReceiveCOSData(int CardNum) { // Display COS data: String formatString = ""; for (int i = 0; i < (int)CardData[CardNum].PPIs; i++) { formatString += String.Format("{0,2:X2} ", CardData[CardNum].LastCOSData[i].A); formatString += String.Format("{0,2:X2} ", CardData[CardNum].LastCOSData[i].B); formatString += String.Format("{0,2:X2} ", CardData[CardNum].LastCOSData[i].C); } // Now from the thread you can safely call this: labelCOS.Invoke(new UpdateTextCallback(this.UpdateCOSText), new object[] { formatString }); } private void btnDetectGeneric_Click(object sender, EventArgs e) { // Simple threading scenario: // Start a static method running on a second thread. // Enable COS Interrupt //AIOWDM.RelOutPortB(CardNum, Offset + 10, 0x00); // Clear/Enable COS Interrupt (does both) AIOWDM.RelOutPortB(CardNum, Offset + 11, 0x00); // Flag for IRQ type: GnericOnlyFlag = true; // Create the thread(s) that will use the protected resource. bAbort = false; // lower flag myThread = new Thread(new ThreadStart(MyThreadProc)); myThread.Name = String.Format("Thread{0}", 1); myThread.Start(); textBoxMessage.Text = "Detecting generic IRQs; generate IRQs to see this sample count them. Press 'Abort IRQ Detect' to cancel when done.\r\n"; } private void btnDetectCOS_Click(object sender, EventArgs e) { // Simple threading scenario: // Start a static method running on a second thread. // Enable COS Interrupt //AIOWDM.RelOutPortB(CardNum, Offset + 10, 0x00); // Clear/Enable COS Interrupt AIOWDM.RelOutPortB(CardNum, Offset + 11, 0x00); // Flag for IRQ type: GnericOnlyFlag = false; // Create the thread(s) that will use the protected resource. bAbort = false; // lower flag myThread = new Thread(new ThreadStart(MyThreadProc)); myThread.Name = String.Format("Thread{0}", 1); myThread.Start(); textBoxMessage.Text = "Detecting COS IRQs; change the input data to see the COS data update. Press 'Abort IRQ Detect' to cancel when done.\r\n"; } private void btnAbort_Click(object sender, EventArgs e) { UInt32 retval = 0; retval = AIOWDM.AbortRequest(0); // abort wait state on the card and exit win thread if (retval != 0) { textBoxMessage.Text = "Aborting IRQ detection...\r\n"; } else { textBoxMessage.Text = "No IRQ request to abort.\r\n"; } } private void btnExit_Click(object sender, EventArgs e) { // Close the form and Application: this.Close(); } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { // If user exits and thread is running: /* Abort all pending IRQ requests. If we don't do this before exiting, later when an IRQ comes in or somebody else calls AbortRequest the pending IRQ request will attempt to unlock a thread that no longer exists, which is bad. (We can AbortRequest for a card with no pending requests, it'll just tell us that, and it's easier than remembering which cards have pending requests.) */ // Wait until it is safe to enter. mut.WaitOne(); bAbort = true; UInt32 retval = 0; for (int i = 0; i < MAX_CARDS; i++) { retval = AIOWDM.AbortRequest(i); // abort wait state and exit thread } // Release the Mutex. mut.ReleaseMutex(); } private void btnReboot_Click(object sender, EventArgs e) { // Reboot: DialogResult dr = MessageBox.Show("Are you certain you want to reboot? ", " Emergency Reboot !!! ", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation); if(dr == DialogResult.Yes) { if (AIOWDM.EmergencyReboot() != 0) textBoxMessage.Text += "Rebooting...\r\n"; else textBoxMessage.Text += "Reboot failed!\r\n"; } } private void btnReadStatus_Click(object sender, EventArgs e) { // Read status: uint temp; temp = AIOWDM.WDGReadStatus(CardNum); labelStatus.Text = temp.ToString("X"); } private void btnReadTemp_Click(object sender, EventArgs e) { // Note: if this hardware feature is not preset This will still read a fixed value: // Read the temperature: double temp; temp = AIOWDM.WDGReadTemp(CardNum); labelTemperature.Text = temp.ToString("F"); } private void btnCancelWDGTiming_Click(object sender, EventArgs e) { AIOWDM.WDGStop(CardNum); myWDGTimer.Stop(); myWDGTimer.Enabled = false; textBoxMessage.Text += "Watchdog timing cancelled.\r\n"; int num = textBoxMessage.TextLength; textBoxMessage.Select(num, textBoxMessage.TextLength - 1); textBoxMessage.ScrollToCaret(); } private void btnHandleWDGIRQ_Click(object sender, EventArgs e) { uint WDGAction; if(radioSoft.Checked) WDGAction = AIOWDM.WDG_ACTION_SOFT_RESTART; else if(radioMostlySoft.Checked) WDGAction = AIOWDM.WDG_ACTION_MOSTLY_SOFT_RESTART; else WDGAction = AIOWDM.WDG_ACTION_IGNORE; if(checkBoxDisableWDGTimer.Checked) WDGAction = WDGAction + AIOWDM.WDG_ACTION_DISABLE; AIOWDM.WDGHandleIRQ(CardNum, WDGAction); } private void btnTestWDGTiming_Click(object sender, EventArgs e) { AIOWDM.WDGSetTimeout(CardNum, 2 * 1000, AIOWDM.PCI_WDG_CSM_RATE); //2 sec timeout AIOWDM.WDGSetResetDuration(CardNum, 1, AIOWDM.PCI_WDG_CSM_RATE); //infinite reset //WDGSetResetDuration(CardNum, 250, PCI_WDG_CSM_RATE); //250 ms reset (option) AIOWDM.WDGStart(CardNum); textBoxMessage.Text += "Watchdog timing started...\r\n"; int num = textBoxMessage.TextLength; textBoxMessage.Select(num, textBoxMessage.TextLength - 1); textBoxMessage.ScrollToCaret(); CardData[CardNum].WDGPets = 0; // reset pet myWDGTimer.Tick -= WDGTimerWaitProcessor; //remove wait handler myWDGTimer.Tick += new EventHandler(WDGTimerEventProcessor); // add event and pet handler myWDGTimer.Interval = 1000; // 1 sec timer myWDGTimer.Enabled = true; myWDGTimer.Start(); } private void WDGTimerEventProcessor(Object myObject, EventArgs myEventArgs) { // This is the 1st method that runs when the WDDG timer event is raised // We will call this a few times to test WDG Pet function // Then drop into a wait function until a WDG timeout is reported //CardNum := WDGTime.Tag; if ((AIOWDM.RelInPortB(CardNum, Offset + 4) & 0x01) == 0) { textBoxMessage.Text += "Watchdog timed out early!\r\n"; int num = textBoxMessage.TextLength; textBoxMessage.Select(num, textBoxMessage.TextLength - 1); textBoxMessage.ScrollToCaret(); myWDGTimer.Stop(); myWDGTimer.Enabled = false; return; //Exit; } CardData[CardNum].WDGPets++; if (CardData[CardNum].WDGPets >= 4) { textBoxMessage.Text += "Waiting for timeout...\r\n"; int num = textBoxMessage.TextLength; textBoxMessage.Select(num, textBoxMessage.TextLength - 1); textBoxMessage.ScrollToCaret(); myWDGTimer.Tick -= WDGTimerEventProcessor; //remove basic event and pet handler myWDGTimer.Tick += new EventHandler(WDGTimerWaitProcessor); // add new wait handler myWDGTimer.Interval = 100; // increase speed now } else { AIOWDM.WDGPet(CardNum); textBoxMessage.Text += "Petting watchdog, pet " + CardData[CardNum].WDGPets.ToString() + " of 3..." + Environment.NewLine; int num = textBoxMessage.TextLength; textBoxMessage.Select(num, textBoxMessage.TextLength - 1); textBoxMessage.ScrollToCaret(); } } private void WDGTimerWaitProcessor(Object myObject, EventArgs myEventArgs) { // This just polls the state of WDG for a timeout: if( (AIOWDM.RelInPortB(CardNum, Offset + 4) & 0x01) == 0) { textBoxMessage.Text += "Watchdog timed out.\r\n"; int num = textBoxMessage.TextLength; textBoxMessage.Select(num, textBoxMessage.TextLength - 1); textBoxMessage.ScrollToCaret(); myWDGTimer.Stop(); myWDGTimer.Enabled = false; } } }// end class }