{"id":29294,"date":"2022-08-22T00:55:03","date_gmt":"2022-08-22T07:55:03","guid":{"rendered":"https:\/\/digilent.com\/blog\/?p=29294"},"modified":"2026-09-10T03:33:27","modified_gmt":"2026-09-10T10:33:27","slug":"reading-generated-data-in-a-real-time-distributed-system-part-2","status":"publish","type":"post","link":"https:\/\/digilent.com\/blog\/reading-generated-data-in-a-real-time-distributed-system-part-2\/","title":{"rendered":"Reading Generated Data in a Real-Time Distributed System"},"content":{"rendered":"<p><span style=\"font-weight: 400;\">In today&#8217;s manufacturing world, companies are trying to do more with less staffing by maximizing throughput or looking for the critical status of a production line without interrupting the focus of production staffing.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">In the past, a manager would often need to walk the production floor to see how the various processes are performing. This took time away from other important tasks, and required these managers to generate reports and perhaps present the data to other managers. One way that the process has evolved to reduce or even eliminate the need for manager oversight is by sending the data collected from various systems to a centralized (physical or cloud-based) database.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Many manufacturing facilities utilize data acquisition and control products integrated directly into their production stands. These devices monitor and\/or control the manufacturing process of products from all aspects of life today. The sensor data used to make programmed decisions to any part of the production process can also be used to inform and notify managers, supervisors, directors, administrators, etc., either as a consolidated view or as a report in near real-time.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The key to this is setting up a database as a repository or collection point for all the data being collected. Once the database is created and set up, existing code used can be easily modified to send that telemetry to the database, where folks who need to know can run an app to see what is happening over in production.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">This series of articles demonstrates creating and setting up a database and suggestions for augmenting applications written in but not limited to Visual Basic.NET, LabVIEW, and DASYLab.<\/span><\/p>\n<p><b>Setting Up an MS Database<\/b><\/p>\n<p><span style=\"font-weight: 400;\">Here are my &#8216;step by step&#8217; instructions (as performed on Windows 7\/64 bit).<\/span><\/p>\n<p><span style=\"font-weight: 400;\">You will need to set up an ODBC connection on the computer you&#8217;re working on. Click on Start &gt;&gt; Control Panel &gt;&gt; Administrative Tools, then double-click on &#8220;Data Sources (ODBC)&#8221;. Click on the &#8220;System DSN&#8221; tab. User DSN means that only you will be able to access this data source, and System DSN means that anyone who is on the machine should be able to access the data source (though only if they provide the necessary credentials). Now click on Add.<\/span><\/p>\n<figure id=\"attachment_29281\" aria-describedby=\"caption-attachment-29281\" style=\"width: 472px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/01.png\" alt=\"\" width=\"472\" height=\"392\" class=\"wp-image-29281 size-full\" data-wp-pid=\"29281\" \/><figcaption id=\"caption-attachment-29281\" class=\"wp-caption-text\">ODBC Data Source Administrator, Add button<\/figcaption><\/figure>\n<p><span style=\"font-weight: 400;\">We will be using Microsoft Access, so select &#8216;Microsoft Access Driver (*.mdb, *.accdb),&#8217; then click Finish.<\/span><\/p>\n<figure id=\"attachment_29282\" aria-describedby=\"caption-attachment-29282\" style=\"width: 478px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/02-CreateNewDataSource.png\" alt=\"\" width=\"478\" height=\"360\" class=\"wp-image-29282 size-full\" data-wp-pid=\"29282\" \/><figcaption id=\"caption-attachment-29282\" class=\"wp-caption-text\">Create New Data Source dialog<\/figcaption><\/figure>\n<p><span style=\"font-weight: 400;\">The ODBC Microsoft Access Setup dialog appears. Enter a Data Source Name and Description, and click on Create.<\/span><\/p>\n<figure id=\"attachment_29283\" aria-describedby=\"caption-attachment-29283\" style=\"width: 481px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/03-ODBCMicrosoftAccessSetup.png\" alt=\"\" width=\"481\" height=\"321\" class=\"wp-image-29283 size-full\" data-wp-pid=\"29283\" \/><figcaption id=\"caption-attachment-29283\" class=\"wp-caption-text\">ODBC Microsoft Access Setup dialog<\/figcaption><\/figure>\n<p><span style=\"font-weight: 400;\">The new Database dialog appears. Enter the name of the database. Throughout this article, we will be working with the name MccGenericOdbc, and it is placed in <\/span><span style=\"font-weight: 400;\">C:\\Users\\Public\\Documents<\/span><span style=\"font-weight: 400;\">.<\/span><\/p>\n<figure id=\"attachment_29284\" aria-describedby=\"caption-attachment-29284\" style=\"width: 552px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/04-NewDatabase.png\" alt=\"\" width=\"552\" height=\"373\" class=\"wp-image-29284 size-full\" data-wp-pid=\"29284\" \/><figcaption id=\"caption-attachment-29284\" class=\"wp-caption-text\">New Database dialog<\/figcaption><\/figure>\n<p><span style=\"font-weight: 400;\">Click on OK. If all went correctly, you should see this message:<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/06-ODBCMicrosoftAccessSetup.png\" alt=\"\" width=\"497\" height=\"172\" class=\"aligncenter size-full wp-image-29285\" data-wp-pid=\"29285\" \/><\/p>\n<p><span style=\"font-weight: 400;\">Click on OK, and you will be back to the ODBC Microsoft Setup now with the database name inserted:<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/05-ODBCMicrosoftAccessSetup.png\" alt=\"\" width=\"481\" height=\"321\" class=\"aligncenter size-full wp-image-29288\" data-wp-pid=\"29288\" \/><\/p>\n<p><span style=\"font-weight: 400;\">Click OK, and now your ODBC Data Source Administrator will look like this:<\/span><\/p>\n<p><span style=\"font-weight: 400;\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/06-ODBCDataSourceAdministator1.png\" alt=\"\" width=\"472\" height=\"392\" class=\"aligncenter size-full wp-image-29286\" data-wp-pid=\"29286\" \/><\/span><\/p>\n<p><span style=\"font-weight: 400;\">Open Windows Explorer and navigate to <\/span><span style=\"font-weight: 400;\">C:\\Users\\Public\\Documents<\/span><span style=\"font-weight: 400;\"> to see your new database:<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/07-WindowsExplorer1-1-600x350.png\" alt=\"\" width=\"600\" height=\"350\" class=\"aligncenter size-medium wp-image-29289\" data-wp-pid=\"29289\" srcset=\"https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/07-WindowsExplorer1-1-600x350.png 600w, https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/07-WindowsExplorer1-1.png 878w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p><span style=\"font-weight: 400;\">It is assumed you have a copy of Microsoft Access to complete the following steps. However, if you do not, attached to this article is a usable copy of the database for this project.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Double-click on MCCGenericOdbc.mdb to launch Microsoft Access and open the empty database. Click &#8220;Create &gt; Table&#8221;.<\/span><\/p>\n<p><span style=\"font-weight: 400;\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/08-EmptyDatabase-600x299.png\" alt=\"\" width=\"600\" height=\"299\" class=\"aligncenter size-medium wp-image-29290\" data-wp-pid=\"29290\" srcset=\"https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/08-EmptyDatabase-600x299.png 600w, https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/08-EmptyDatabase-1024x510.png 1024w, https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/08-EmptyDatabase.png 1200w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/span><\/p>\n<p><span style=\"font-weight: 400;\">A new Table appears. The first column is already labeled &#8220;ID&#8221;. We need 5 additional columns for our table. Notice the column to the right of ID is highlighted and labeled &#8220;Click to add&#8221;. Click on it, and a drop-down menu appears with various options for the data type. We want this first column to be the time the data was collected, so select &#8220;Date &amp; Time&#8221;. The cursor moves to the field name location. By default, it is named &#8220;Field1&#8221;. Change that to &#8220;Time&#8221;. Repeat this process for the next 4 columns, making the data types all &#8220;Short Text&#8221;, and with the names changed to &#8220;Temperature&#8221;, &#8220;Pressure&#8221;, &#8220;Tachometer&#8221;, and &#8220;Interlock&#8221;.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">When completed, it will look like this:<\/span><\/p>\n<p><span style=\"font-weight: 400;\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/09-DatabaseWithTable1-600x299.png\" alt=\"\" width=\"600\" height=\"299\" class=\"aligncenter size-medium wp-image-29291\" data-wp-pid=\"29291\" srcset=\"https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/09-DatabaseWithTable1-600x299.png 600w, https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/09-DatabaseWithTable1-1024x510.png 1024w, https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/09-DatabaseWithTable1.png 1200w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/span><\/p>\n<p><span style=\"font-weight: 400;\">Click on &#8220;File&#8221;, &#8220;Save&#8221;, and a Save as dialog appears. Enter a name for the table. Enter &#8220;TestData&#8221; and click OK. Then close Access. We will be reading and writing to this data table in the next sections.<\/span><\/p>\n<h2><b>Creating an Application to Collect and Pass Data<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">As stated at the beginning of this article, for this application we will be using the Measurement Computing USB-2408 and the Universal Library, Microsoft Visual Basic 2010, and an add-in to Visual Basic called BERGtools.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">I&#8217;ve chosen the USB-2408 because it fills the needs of our application with one device. I wanted to emulate a system containing a temperature input from a thermocouple, a voltage input from a pressure transducer, pulsed output from a tachometer, and a digital input signal from a door interlock. The USB-2408 is capable of reading from all these various input types and more. I am emulating the signals externally, using a type T thermocouple, a function generator set to a sine wave, another function generator set to TTL pulsed output, and a 5 VDC output power supply, respectively.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">I&#8217;m using Visual Basic 2010 because I want to keep it compatible with those folks who don&#8217;t have Microsoft&#8217;s latest version but want to build this application. It is assumed you know your way around the Visual Basic IDE, so I&#8217;m going to skip most screenshots so as to focus on the code.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">BERGtools is a free collection of displays and functions including meters, oscilloscopes, knobs, etc., I created to make viewing data &#8216;easier on the eyes&#8217;. We will use them here, but you can easily work around them or use your own objects.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">To begin, launch Visual Basic and start a new project. We will call it &#8220;DataCollectionToDatabase.&#8221; Resize your form to 589,422.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Add a MenuStrip, DataGridView, Timer, OpenFileDialog, Thermometer, 2 KnobDials, an LED, 2 buttons, and 11 labels. Your form design should look like this:<\/span><\/p>\n<figure id=\"attachment_29295\" aria-describedby=\"caption-attachment-29295\" style=\"width: 593px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/10-Form-Design.png\" alt=\"\" width=\"593\" height=\"498\" class=\"wp-image-29295 size-full\" data-wp-pid=\"29295\" \/><figcaption id=\"caption-attachment-29295\" class=\"wp-caption-text\">form design<\/figcaption><\/figure>\n<figure id=\"attachment_29296\" aria-describedby=\"caption-attachment-29296\" style=\"width: 428px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/11-Properties.png\" alt=\"\" width=\"428\" height=\"429\" class=\"wp-image-29296 size-full\" data-wp-pid=\"29296\" srcset=\"https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/11-Properties.png 428w, https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/11-Properties-150x150.png 150w, https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/11-Properties-300x300-cropped.png 300w\" sizes=\"auto, (max-width: 428px) 100vw, 428px\" \/><figcaption id=\"caption-attachment-29296\" class=\"wp-caption-text\">properties<\/figcaption><\/figure>\n<p><span style=\"font-weight: 400;\">Of all the labels, only 4 need to be named:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The one over the thermometer: lblTempValue.\u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The one over kdPressure (to the right): lblPressure.\u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The one over kdTach (to the right): lblTach.\u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The one to the right of Time: lblTime<\/span><\/p>\n<p><span style=\"font-weight: 400;\">All labels should set Autosize = true<\/span><\/p>\n<p><span style=\"font-weight: 400;\">From the Solution Explorer, click on the icon to &#8220;Show All Files&#8221;. Right-click on References, select &#8220;Add Reference.&#8221; The Add Reference dialog will show. Click on the .NET tab, scroll down, and select MccDaq, then click OK.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">From the Solution Explorer, right-click on Form1.vb, and select View Code. Above Public Class Form1, insert the following:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">Imports System.Data.OleDb<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Imports System.Runtime.InteropServices<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Imports System.IO<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">Next, add the variable declarations:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">&#8216;For Universal Library<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim inventory As MccDaq.DaqDeviceDescriptor()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Public Daqboard As New MccDaq.MccBoard()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Public UlStat As MccDaq.ErrorInfo<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim LastCounterValue As Int32 = 0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim bFirstEntry As Boolean = False<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">&#8216;For database<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim sConnectionString As String<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Public objConn As New OleDbConnection()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim daMFGData As New OleDbDataAdapter()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim dsNewScan As New DataSet()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim dtData As DataTable<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim tblTestData As DataTable<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim drCurrent As DataRow<\/span><\/p>\n<p>&nbsp;<\/p><\/blockquote>\n<p><span style=\"font-weight: 400;\">The top sections are the global variables for the Universal Library. The bottom section are the global variables and declarations for the database.<\/span><\/p>\n<h2><b>Instantiate the USB-2408<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">Paste the following into the Form1_Load() event:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">MccDaq.DaqDeviceManager.IgnoreInstaCal()\u00a0 \u00a0 &#8216;don&#8217;t use information from InstaCal.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">System.Windows.Forms.Cursor.Current = Cursors.WaitCursor &#8216;change cursor to wait.<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">&#8216;Load all the boards it can find<\/span><\/p>\n<p><span style=\"font-weight: 400;\">inventory = MccDaq.DaqDeviceManager.GetDaqDeviceInventory(MccDaq.DaqDeviceInterface.Any)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim numDevDiscovered As Integer = inventory.Length \u00a0 &#8216;how many was that?<\/span><\/p>\n<p><span style=\"font-weight: 400;\">System.Windows.Forms.Cursor.Current = Cursors.WaitCursor \u00a0 &#8216;change cursor to wait.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim Boardfound As Boolean = False<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">If numDevDiscovered &gt; 0 Then<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0For boardNum As Integer = 0 To numDevDiscovered &#8211; 1<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Try<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8216;Create a new MccBoard object for Board and assign a board number<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8216;to the specified DAQ device with CreateDaqDevice()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Daqboard = MccDaq.DaqDeviceManager.CreateDaqDevice(boardNum, inventory(boardNum))<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0If Daqboard.BoardName.Contains(&#8220;2408&#8221;) Then<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Boardfound = True<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Daqboard.FlashLED()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Exit For<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Else<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0MccDaq.DaqDeviceManager.ReleaseDaqDevice(Daqboard)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0End If<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Catch ule As MccDaq.ULException<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0MsgBox(ule.ErrorInfo.Message)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0End Try<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Next<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End If<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">If Boardfound = False Then<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0MsgBox(&#8220;No USB-2408 series board found in system. &#8220;, MsgBoxStyle.Critical, &#8220;No Board detected&#8221;)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0End<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End If<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">UlStat = Daqboard.FlashLED()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim MyBoardName As String = Daqboard.BoardName.Trim<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Me.Text = MyBoardName + &#8221; found as board number: &#8221; + Daqboard.BoardNum.ToString<\/span><\/p><\/blockquote>\n<p><span style=\"font-weight: 400;\">This snippet of code queries the system for any supported Measurement Computing devices, accessible through the Universal Library. It sifts through the devices found, looking for a USB-2408. If one is found, it uses it and changes the Form1.Text with an appropriate statement. If not, it pops up a message box stating it didn&#8217;t find a USB-2408.<\/span><\/p>\n<h2><b>Create the Local Database<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">Add this line into the Form1_Load() event after the above code:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">&#8216;Create the local database to DataGridView1<\/span><\/p>\n<p><span style=\"font-weight: 400;\">CreateDataBase()<\/span><\/p><\/blockquote>\n<p><span style=\"font-weight: 400;\">And add the following subs and functions after the End Sub of the Form1_Load() event:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">Private Sub CreateDataBase()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Dim ds As New DataSet<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0ds = CreateDataSet()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0DataGridView1.DataSource = ds.Tables(&#8220;MeasuredData&#8221;)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End Sub<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">Private Function CreateDataSet() As DataSet<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0&#8216;Creating a DataSet object for tables<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Dim MeasuredData As DataSet = New DataSet()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0&#8216;Creating a table object<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Dim dtData As DataTable = CreateDACTable()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0MeasuredData.Tables.Add(dtData)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Return MeasuredData<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End Function<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">Private Function CreateDACTable() As DataTable<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0dtData = New DataTable(&#8220;MeasuredData&#8221;)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0&#8216;Adding columns<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0AddNewColumn(dtData, &#8220;System.String&#8221;, &#8220;Time&#8221;)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0AddNewColumn(dtData, &#8220;System.String&#8221;, &#8220;Temperature&#8221;)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0AddNewColumn(dtData, &#8220;System.String&#8221;, &#8220;Pressure&#8221;)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0AddNewColumn(dtData, &#8220;System.String&#8221;, &#8220;Tachometer&#8221;)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0AddNewColumn(dtData, &#8220;System.String&#8221;, &#8220;Interlock&#8221;)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Return dtData<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End Function<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">Private Sub AddNewColumn(ByRef table As DataTable, ByVal columnType As String, ByVal columnName As String)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Dim column As DataColumn = table.Columns.Add(columnName, Type.GetType(columnType))<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End Sub<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">Private Sub AddNewRow(ByRef table As DataTable, ByRef time As String, ByRef temperature As Double, ByRef pressure As Double, ByRef RPM As Int32, ByRef interlock As Boolean)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Dim newrow As DataRow = table.NewRow()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0newrow(&#8220;Time&#8221;) = time<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0newrow(&#8220;Temperature&#8221;) = temperature<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0newrow(&#8220;Pressure&#8221;) = pressure<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0newrow(&#8220;Tachometer&#8221;) = RPM<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0newrow(&#8220;Interlock&#8221;) = interlock<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0table.Rows.Add(newrow)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End Sub<\/span><\/p><\/blockquote>\n<p><span style=\"font-weight: 400;\">The above does the following: Create a Dataset that includes a table called MeasuredData, and links it to the DataGridView1&#8217;s Datasource. MeasuredData has 1 row, made up of 5 columns with the field names: Time, Temperature, Pressure, Tachometer, and Interlock. Create the 5 columns with these names, and place the names in the top row.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">From the Form View, double-click on the &#8220;Start&#8221; button, and add the following to the btnStartStop_Click() event:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">If btnStartStop.Text = &#8220;Start&#8221; Then<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0btnStartStop.Text = &#8220;Stop&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0OpenDatabase()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0UlStat = Daqboard.CClear(0)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Timer1.Enabled = True<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Else<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0btnStartStop.Text = &#8220;Start&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Timer1.Enabled = False<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End If<\/span><\/p>\n<p>&nbsp;<\/p><\/blockquote>\n<p><span style=\"font-weight: 400;\">Aside from the button text alternating between &#8220;Start&#8221; and &#8220;Stop&#8221;, when the button text is &#8220;Start&#8221;, open the external database, reset the counter, and start Timer1. When the button text is &#8220;Stop&#8221;, stop Timer1.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Opening the external database is essential to this project and has its own subroutine.<\/span><\/p>\n<h2><b>How to Open the External Database<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">Paste this below the End Sub of the btnStartStop_Click() event:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">Private Sub OpenDatabase()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0&#8216;How to open an OLE DB database<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0sConnectionString = &#8220;Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\\Users\\Public\\Documents\\MCCGenericOdbc.mdb&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0objConn = New OleDbConnection(sConnectionString)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0objConn.Open()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0daMFGData = New OleDbDataAdapter(&#8220;Select * From TestData&#8221;, objConn)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0dsNewScan = New DataSet(&#8220;NewScan&#8221;)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0daMFGData.FillSchema(dsNewScan, SchemaType.Source, &#8220;TestData&#8221;)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0daMFGData.Fill(dsNewScan, &#8220;TestData&#8221;)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0&#8216;Create a new instance of a DataTable.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0tblTestData = dsNewScan.Tables(&#8220;TestData&#8221;)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0objConn.Close()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End Sub<\/span><\/p><\/blockquote>\n<p><span style=\"font-weight: 400;\">Opening a link to a Microsoft Access database requires a proper connection string like this:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">sConnectionString = &#8220;Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\\Users\\Public\\Documents\\MCCGenericOdbc.mdb&#8221;<\/span><\/p><\/blockquote>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">The &#8220;;&#8221; delineates the Provider type from the database path and filename. It is important that this be correct and be correctly formatted. The connection string is used to instantiate the database connection, called objConn. Open the connection and create a data adapter by selecting the table &#8220;TestData&#8221; in the database and naming it &#8220;daMFGData&#8221;. Create a dataset, &#8220;daNewScan&#8221;, set it up with the proper fields using FillSchema and Fill. Create a local data table aligned to the one in the database called &#8220;tblTestData&#8221;.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Lastly, close the connection for now.<\/span><\/p>\n<h2><b>Collecting Data and Sending to the Database<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">From the Form view, double-click on Timer1. Enter the following in the Timer1_Tick() event:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">Dim MyDataScan(4) As Object &#8216;array to hold the data generated by the scan<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">&#8216;Read the temperature<\/span><\/p>\n<p><span style=\"font-weight: 400;\">UlStat = Daqboard.TIn(0, MccDaq.TempScale.Celsius, MyDataScan(1), MccDaq.VInOptions.Default)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">If UlStat.Value &lt;&gt; MccDaq.ErrorInfo.ErrorCode.NoErrors Then<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0errhandler(UlStat)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Exit Sub<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End If<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Thermometer1.TempValue = MyDataScan(1)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">lblTempValue.Text = Convert.ToDouble(MyDataScan(1)).ToString(&#8220;##0.00&#8221;) &amp; &#8220;\u00b0C&#8221;<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">&#8216;Read the voltage (Pressure)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">UlStat = Daqboard.VIn(1, MccDaq.Range.Bip10Volts, MyDataScan(2), MccDaq.VInOptions.Default)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">If UlStat.Value &lt;&gt; MccDaq.ErrorInfo.ErrorCode.NoErrors Then<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0errhandler(UlStat)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Exit Sub<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End If<\/span><\/p>\n<p><span style=\"font-weight: 400;\">MyDataScan(2) *= 10 &#8216;apply scaling (Makes the data look cooler, it uses more of the pressure meter!)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">lblPressure.Text = Convert.ToSingle(MyDataScan(2)).ToString(&#8220;##0.00&#8221;)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">kdPressure.UpdateKnob(MyDataScan(2))<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">&#8216;Read the counter (Tachometer)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">UlStat = Daqboard.CIn(0, MyDataScan(3))<\/span><\/p>\n<p><span style=\"font-weight: 400;\">If UlStat.Value &lt;&gt; MccDaq.ErrorInfo.ErrorCode.NoErrors Then<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0errhandler(UlStat)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Exit Sub<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End If<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim NewCountRead As Integer = (MyDataScan(3) &#8211; LastCounterValue)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">LastCounterValue = MyDataScan(3)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim TachVal As Double = (NewCountRead * 60) &#8216;apply scaling to RPM from RPS<\/span><\/p>\n<p><span style=\"font-weight: 400;\">kdTach.UpdateKnob(TachVal \/ 100)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">lblTach.Text = (TachVal).ToString(&#8220;####0&#8221;)<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">&#8216;Read the digital bit (door interlock)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim MyDigBit As Boolean<\/span><\/p>\n<p><span style=\"font-weight: 400;\">UlStat = Daqboard.DBitIn(MccDaq.DigitalPortType.FirstPortA, 0, MyDigBit)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">If UlStat.Value &lt;&gt; MccDaq.ErrorInfo.ErrorCode.NoErrors Then<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0errhandler(UlStat)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Exit Sub<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End If<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Led1.Value = MyDigBit<\/span><\/p>\n<p><span style=\"font-weight: 400;\">MyDataScan(4) = &#8220;0&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">If MyDigBit = True Then MyDataScan(4) = &#8220;1&#8221;<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">&#8216;Read the current Time<\/span><\/p>\n<p><span style=\"font-weight: 400;\">MyDataScan(0) = Convert.ToString(System.DateTime.Now)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">lblTime.Text = MyDataScan(0)<\/span><\/p>\n<p>&nbsp;<\/p><\/blockquote>\n<p><span style=\"font-weight: 400;\">In the above code, create an array called MyDataScan() to hold the collected data. Using Measurement Computing&#8217;s Universal Library functions, read from each of the 4 types of sensors: temperature, voltage, counter, and digital input (TTL). Also read the time.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Note: preceding each library call, there is a small If-Then routine:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">If UlStat.Value &lt;&gt; MccDaq.ErrorInfo.ErrorCode.NoErrors Then<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0errhandler(UlStat)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Exit Sub<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End If<\/span><\/p><\/blockquote>\n<p><span style=\"font-weight: 400;\">Each time a call is made to the Universal Library, an integer value is returned into UlStat. If UlStat equals zero, then the function executed correctly and the data is good. If UlStat is not equal to 0, then there was a problem executing the function, and the value of UlStat can be used to handle the error. If you were writing a more elaborate program, you would want to manage the returned error in your program so as to keep the program from using bad data or worse, crashing. More on that in a bit.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Use the array to scale data if needed, and update all the meters, gauges, and labels.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Next, we are going to write the data to our DataGridView. Here&#8217;s how:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">&#8216;This goes to the datagrid<\/span><\/p>\n<p><span style=\"font-weight: 400;\">If bFirstEntry = False Then<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Dim newrow As DataRow = dtData.NewRow()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0newrow(&#8220;Time&#8221;) = MyDataScan(0)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0newrow(&#8220;Temperature&#8221;) = MyDataScan(1).ToString()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0newrow(&#8220;Pressure&#8221;) = MyDataScan(2).ToString<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0newrow(&#8220;Tachometer&#8221;) = TachVal.ToString<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0newrow(&#8220;Interlock&#8221;) = MyDataScan(4)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0dtData.Rows.Add(newrow)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0bFirstEntry = True<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Else<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Dim drEditRow As DataRow<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0drEditRow = dtData.NewRow()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0drEditRow = dtData.Rows.Item(0)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0drEditRow.BeginEdit()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0drEditRow(&#8220;Time&#8221;) = DateTime.Now.ToString()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0drEditRow(&#8220;Temperature&#8221;) = Format(MyDataScan(1), &#8220;##0.0#&#8221;)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0drEditRow(&#8220;Pressure&#8221;) = Format(MyDataScan(2), &#8220;##0.0#&#8221;)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0drEditRow(&#8220;Tachometer&#8221;) = Format(TachVal, &#8220;####&#8221;)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0drEditRow(&#8220;Interlock&#8221;) = MyDataScan(4)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0drEditRow.EndEdit()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End If<\/span><\/p>\n<p>&nbsp;<\/p><\/blockquote>\n<p><span style=\"font-weight: 400;\">We implement an If-Then-Else routine here because we need to add a new row to the grid. When the finished program runs, each time new data is collected, it is repeatedly written to the first row. We are not looking to collect and store data as it is received; we only want the one row of data. To make that work, we use the IF-Then-Else. The decision (IF) is based on &#8216;is this the first time in this timer loop?&#8217; If it is, then we add a first row. If not, then just keep updating the same row. In the IF portion, we call NewRow, and in the Else portion we call BeginEdit () and EndEdit ().<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Now we will write the data out to our external database. Add the following code:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">Try &#8216;This goes out to the database<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0objConn.Open()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0&#8216;Obtain a new Data Row object from the DataTable.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0drCurrent = tblTestData.NewRow()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0drCurrent = tblTestData.Rows.Find(1)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0drCurrent = tblTestData.Rows(0)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0drCurrent.BeginEdit()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0drCurrent(&#8220;Time&#8221;) = DateTime.Now.ToString()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0drCurrent(&#8220;Temperature&#8221;) = MyDataScan(1).ToString()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0drCurrent(&#8220;Pressure&#8221;) = Format(MyDataScan(2), &#8220;##0.0#&#8221;)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0drCurrent(&#8220;Tachometer&#8221;) = TachVal<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0drCurrent(&#8220;Interlock&#8221;) = MyDataScan(4)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0drCurrent.EndEdit()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Dim objCommandBuilder As New OleDbCommandBuilder(daMFGData)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0objCommandBuilder.QuotePrefix = &#8220;[&#8220;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0objCommandBuilder.QuoteSuffix = &#8220;]&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Dim r As Integer = daMFGData.Update(dsNewScan, &#8220;TestData&#8221;)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0objConn.Close()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Catch ex As OleDbException<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Timer1.Enabled = False<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0objConn.Close()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0MsgBox(ex.Message.ToString(), MsgBoxStyle.Information, &#8220;Error Message&#8221;)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End Try<\/span><\/p><\/blockquote>\n<p><span style=\"font-weight: 400;\">Writing to the real database is almost the same as writing to the DataGridView. However, here we have to open the database connection, find the first row, use the OleDbCommandBuilder () (be sure to include the QuotePrefix and QuoteSuffix), update the table, and close the database connection.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Now, for a little program management. Add our error handler subroutine. After the Timer1_Tick&#8217;s End sub, add the following:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">Public Sub errhandler(ByVal ulstat As MccDaq.ErrorInfo)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0&#8216;Generic UL error handler<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Timer1.Enabled = False<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Try<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0objConn.Close()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Catch ex As Exception<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0End Try<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0MessageBox.Show(ulstat.Message, &#8220;Universal Library Error&#8221;, MessageBoxButtons.OK, MessageBoxIcon.Error)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0btnStartStop.Text = &#8220;Start&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End Sub<\/span><\/p><\/blockquote>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">If there is a Universal Library error, stop the timer, close the database connection if it is open, display an error message stating the problem encountered, and reset the Start\/Stop button to display &#8220;Start&#8221;. In a more involved application, you might have a Case statement, handling the problem programmatically, or offering solutions either in a more verbose message telling the user what happened and how to resolve it.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Add a graceful way to exit the program:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">From the Form view, regarding the MenuStrip1, click on File, and then double-click on Exit, and add the following code:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">Timer1.Enabled = False<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End<\/span><\/p><\/blockquote>\n<p><span style=\"font-weight: 400;\">Because this is Windows, and there is always more than one way to do anything, go back to the Form view, double-click on the &#8216;End&#8217; button, and paste the same syntax there.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">We are still missing a couple of items, but that is as far as we need to go for this section.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">You can now build and run this application. When you click &#8220;Start&#8221;, it will begin reading from the sensors connected to the USB-2408, update the screen, and log the data to the database. A word of caution: if you are expecting to open the database and look at the table hoping to see the data updating in real time, that won&#8217;t happen. What you will see on your screen is a snapshot in time, not real-time updating data. You can stop the application at any time, open the database and table, and see that the table has been updated.<\/span><\/p>\n<h2><b>Creating an Application to View Data with VB.NET<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">Now, we will create the application to read the data back out of the database. It will be a mirror image of the collection application, but no data is collected here \u2014 it will just show what was collected and stored to the database. It will operate like dual-ported memory. This is all in preparation for distributing the two applications so as to have one application (DataCollectionToDatabase) on one computer, and the other app (DatabaseToDataDisplay) on another computer in a different part of the building.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Again, launch Visual Basic, and start a new project. We will call it &#8220;DatabaseToDataDisplay.&#8221; Resize your form to 639, 358.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Add a MenuStrip, DataGridView, Timer, OpenFileDialog, Thermometer, 2 AnalogMeters, an LED, 2 buttons, and 7 labels. Rearrange and resize the objects so that your form design looks like this:<\/span><\/p>\n<p><span style=\"font-weight: 400;\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/10-Form-Design.png\" alt=\"\" width=\"593\" height=\"498\" class=\"aligncenter size-full wp-image-29295\" data-wp-pid=\"29295\" \/><\/span><\/p>\n<p><span style=\"font-weight: 400;\">Change the following properties:<img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/11-Properties.png\" alt=\"\" width=\"428\" height=\"429\" class=\"aligncenter size-full wp-image-29296\" data-wp-pid=\"29296\" srcset=\"https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/11-Properties.png 428w, https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/11-Properties-150x150.png 150w, https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/11-Properties-300x300-cropped.png 300w\" sizes=\"auto, (max-width: 428px) 100vw, 428px\" \/><\/span><\/p>\n<p><span style=\"font-weight: 400;\">Of all the labels, only 2 need to be renamed:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The one under the thermometer: lblTempValue. The one to the right of Time: lblTime<\/span><\/p>\n<p><span style=\"font-weight: 400;\">All labels should be set to Autosize = True<\/span><\/p>\n<p><span style=\"font-weight: 400;\">From the Solution Explorer, right-click on Form1.vb, and select View Code. Above Public Class Form1, insert the following:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">Imports System.Data.OleDb<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Imports System.Runtime.InteropServices<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Imports System.IO<\/span><\/p><\/blockquote>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">Next, add the variable declarations:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">&#8216;For database<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim sConnectionString As String<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Public objConn As New OleDbConnection()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim ds As New DataSet()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim da As OleDb.OleDbDataAdapter<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim sql As String<\/span><\/p><\/blockquote>\n<h2><b>Open the External Database and Read in the Data<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">There is a lot less going on in this application, just about all of it in happening in the Timer, so open the Timer1_Tick() event, and paste in the following:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">Try &#8216;This goes out to the database<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0&#8216;How to open an OLE DB database<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0sConnectionString = &#8220;Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\\Users\\Public\\Documents\\MCCGenericOdbc.mdb&#8221;\u00a0 &#8216;for oleDB<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0objConn = New OleDbConnection(sConnectionString)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0objConn.Open()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0&#8216;Obtain a DataRow object from the DataTable.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0da = New OleDb.OleDbDataAdapter(&#8220;SELECT * FROM TestData&#8221;, objConn)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0da.FillSchema(ds, SchemaType.Source, &#8220;TestData&#8221;)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0da.Fill(ds, &#8220;TestData&#8221;)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0objConn.Close()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Catch ex As OleDbException<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Timer1.Enabled = False<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0objConn.Close()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0MsgBox(ex.Message.ToString(), MsgBoxStyle.Information, &#8220;Error Message&#8221;)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End Try<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">Try<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Dim MyDataScan = ds.Tables(&#8220;TestData&#8221;).Rows(0)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0lblTime.Text = MyDataScan(1).ToString<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Thermometer1.TempValue = Convert.ToDouble(MyDataScan(2))<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0lblTempValue.Text = MyDataScan(2).ToString + &#8220;\u00b0C&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0amPressure.Value = Convert.ToDouble(MyDataScan(3))<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0amTach.Value = Convert.ToDouble(MyDataScan(4) \/ 100)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Dim r As Boolean = Convert.ToBoolean(Val(MyDataScan(5)))<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Led1.Value = r<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Catch ex As OleDbException<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Timer1.Enabled = False<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0MsgBox(ex.Message.ToString(), MsgBoxStyle.Information, &#8220;Error Message&#8221;)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End Try<\/span><\/p><\/blockquote>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">As with the previous app, we want to open the database. But now we want to obtain existing data from the TestData table. When we read in the entire table, we use the Fill Schema and Fill commands. Read in the first row from the table and parse it (Dim MyDataScan = ds.Tables(&#8220;TestData&#8221;). Rows(0)). The data will automatically be placed into the array, &#8216;MyDataScan()&#8217; for dissemination to the various labels and objects in this application. All the data is stored as strings, so there are data conversions going on in the code above.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The app could come up running, but for now let&#8217;s use a Start\/Stop button, and so from the Form View, double-click on the &#8220;Start&#8221; button, and add the following to the btnStartStop_Click() event:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">If btnStartStop.Text = &#8220;Start&#8221; Then<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0btnStartStop.Text = &#8220;Stop&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Timer1.Enabled = True<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Else<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0btnStartStop.Text = &#8220;Start&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Timer1.Enabled = False<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0objConn.Close()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End If<\/span><\/p><\/blockquote>\n<p><span style=\"font-weight: 400;\">The If\/Then\/Else above alternates the Text property of the button between &#8216;Start&#8217; and &#8216;Stop&#8217;, but when the IF condition is true, we also enable or start the timer. When the IF condition is false, we stop the timer and close the link to the database.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Add a graceful way to exit the program:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">From the Form view, regarding the MenuStrip1, click on File, and then double-click on Exit, and add the following code:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">Timer1.Enabled = False<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End<\/span><\/p><\/blockquote>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">Because this is Windows, and there is always more than one way to do anything, go back to the Form view, double-click on the End button, and paste the same syntax there.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">We are still missing a couple of items, but that is as far as we need to go for this section.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The two applications are now complete. Let&#8217;s run both of them from within the Visual Basic.NET environments. Here is what they look like, running in concert:<\/span><\/p>\n<figure id=\"attachment_29300\" aria-describedby=\"caption-attachment-29300\" style=\"width: 492px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/14-VB-Environments-492x600.png\" alt=\"\" width=\"492\" height=\"600\" class=\"wp-image-29300 size-medium\" data-wp-pid=\"29300\" srcset=\"https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/14-VB-Environments-492x600.png 492w, https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/14-VB-Environments.png 642w\" sizes=\"auto, (max-width: 492px) 100vw, 492px\" \/><figcaption id=\"caption-attachment-29300\" class=\"wp-caption-text\">both apps running side by side<\/figcaption><\/figure>\n<p><span style=\"font-weight: 400;\">Despite the different types of meters used in the two applications, you can see the two applications are sharing the data. What you can&#8217;t see is that the top app is sending data to the table &#8220;TestData&#8221; of the database &#8220;MccGenericOdbc.mdb&#8221;, and the bottom app is reading data from the table &#8220;TestData&#8221; of the database &#8220;MccGenericOdbc.mdb&#8221;. Since the two apps are running their own timers, data reads and writes are happening &#8216;on demand.&#8217; For these applications, the data is one second delayed, which is why the data on the two forms is not exactly the same. There will always be some delay due to timers, Windows OS, and network traffic.<\/span><\/p>\n<h2><b>Distributing the Applications to View the Data Remotely<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">The goal of this article series is to demonstrate how to create a data acquisition system on one system, and view the collected data in near real time from another. This can be accomplished by the use of a database and two VB.NET applications.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Up until now, all three components have been on one computer. Here, we will deploy this system onto three computers: one for the database, one to collect the data, and one to view the data. If you have only two computers, you can still make this work by putting the database and data collection program on the same system.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">We will need to make a couple of tweaks to our applications. Nothing major, just to make it easier to find the database. Since the apps are no longer on the same PC, we need a way to have the app find and remember where the database is located. Sure, you could hard-code it, but what if you move the database or one or both apps to other locations? Or, you need to add another computer as a data collection point, or another person needs to see the collected data. Here&#8217;s one way to solve the problem. We will add code to create a configuration file that is automatically loaded at startup. If one is not available, click on a menu item so you can tell the program where the database is, then store that location so the next time you run the program, it will find it upon start up.<\/span><\/p>\n<h2><b>Editing the DataCollectionToDatabase App<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">Starting with the collection app, open DataCollectionToDatabase, go to the Code view of Form1.vb, and scroll to the top. Below Dim drCurrent As DataRow, add the following:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">Dim DatabaseFileNameAndPath As String<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim Filename As String<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim SettingsFileOut As IO.StreamWriter<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim SettingsFileIn As IO.StreamReader<\/span><\/p><\/blockquote>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">And, in the Form1_Load() event, below CreateDataBase() Add the following:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">&#8216;Get file path to user&#8217;s &#8220;MyDocuments\\OledbExample\\&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim pathstring As String = My.Computer.FileSystem.SpecialDirectories.MyDocuments<\/span><\/p>\n<p><span style=\"font-weight: 400;\">pathstring = pathstring + &#8220;\\MeasurementComputing\\OledbExample\\&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">System.IO.Directory.CreateDirectory(pathstring)<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">&#8216;Create a file.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Filename = &#8220;&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Filename = pathstring + &#8220;Database.CFG&#8221;<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">&#8216;Read the data from the file and parse it.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Try<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0SettingsFileIn = New StreamReader(Filename)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0DatabaseFileNameAndPath = SettingsFileIn.ReadLine()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0SettingsFileIn.Close()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Catch ex As Exception<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End Try<\/span><\/p>\n<p>&nbsp;<\/p><\/blockquote>\n<p><span style=\"font-weight: 400;\">What this does: If there is a file named Database.CFG, located in your computer&#8217;s MyDocuments\\Measurement Computing\\OledExample folder, use the path located in that file. If the file does not exist, do nothing.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">From the Solution Explorer, right-click on Form1.vb, and select &#8220;View Designer.&#8221; Double-click on the &#8220;Select Database&#8221; menu item to open the SelectDatabaseToolStripMenuItem_Click() event, and add the following code:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">OpenFileDialog1.Filter = &#8220;MSACCESS (*.mdb)|*.mdb|All Files (*.*)|*.*&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">OpenFileDialog1.FileName = DatabaseFileNameAndPath<\/span><\/p>\n<p><span style=\"font-weight: 400;\">OpenFileDialog1.ShowDialog(Me)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">DatabaseFileNameAndPath = OpenFileDialog1.FileName<\/span><\/p>\n<p><span style=\"font-weight: 400;\">If DatabaseFileNameAndPath = &#8220;&#8221; Then<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0MsgBox(&#8220;No file selected or bad file name selected.&#8221;, MsgBoxStyle.OkOnly, &#8220;Bad file name&#8221;)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Exit Sub<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End If<\/span><\/p><\/blockquote>\n<blockquote><p><span style=\"font-weight: 400;\">&#8216;Create a file path to user&#8217;s &#8220;MyDocuments\\OledbExample\\&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim pathstring As String = My.Computer.FileSystem.SpecialDirectories.MyDocuments<\/span><\/p>\n<p><span style=\"font-weight: 400;\">pathstring = pathstring + &#8220;\\MeasurementComputing\\OledbExample\\&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">System.IO.Directory.CreateDirectory(pathstring)<\/span><\/p><\/blockquote>\n<blockquote><p><span style=\"font-weight: 400;\">&#8216;Create the file.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Filename = pathstring + &#8220;Database.CFG&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">SettingsFileOut = New StreamWriter(Filename)<\/span><\/p><\/blockquote>\n<p>&nbsp;<\/p>\n<blockquote><p><span style=\"font-weight: 400;\">&#8216;Write the DATABASE filename and path to the .cfg file<\/span><\/p>\n<p><span style=\"font-weight: 400;\">SettingsFileOut.WriteLine(DatabaseFileNameAndPath)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">SettingsFileOut.Close()<\/span><\/p><\/blockquote>\n<p><span style=\"font-weight: 400;\">We are using 2 string variables here, and they can be confusing:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">DatabaseFileNameAndPath is a string containing the filename and path to the database itself. Filename is a string containing the filename and path to DatabaseFileNameAndPath.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">When this event is selected, the Open File dialog box appears with filters turned on to show just Microsoft Access files (with the extension of .mdb) or all files. The user navigates to the database, selects it, and clicks on OK. The path and file name are returned in the string DatabaseFilenameAndPath:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">DatabaseFileNameAndPath = OpenFileDialog1.FileName<\/span><\/p><\/blockquote>\n<p><span style=\"font-weight: 400;\">We want to store that path and file name somewhere locally where the app can find it the next time the app starts (that&#8217;s what we added at the end of the Form1_Load() event). As stated above, we want to put the string in a file Database.CFG, and place the file in MyDocuments\\MeasurementComputing\\OledbExample.<\/span><\/p>\n<h2><b>Editing the DatabaseToDataDisplay App<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">Now that we have the first app modified, let&#8217;s modify the monitoring application. These edits are similar to DataCollectionToDatabase. I&#8217;ll just provide the edits.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Open the application. From the Solution Explorer, right-click on Form1.vb, and select &#8220;View Code.&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Scroll to the top. Just below Dim sql As String, add the following:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">Dim DatabaseFileNameAndPath As String<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim Filename As String<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim SettingsFileOut As IO.StreamWriter<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim SettingsFileIn As IO.StreamReader<\/span><\/p><\/blockquote>\n<p><span style=\"font-weight: 400;\">In the Timer1_Tick() event, comment out this line:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">sConnectionString = &#8220;Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\\Users\\Public\\Documents\\MCCGenericOdbc.mdb&#8221; \u00a0 &#8216;for oleDB<\/span><\/p><\/blockquote>\n<p><span style=\"font-weight: 400;\">And add:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">sConnectionString = &#8220;Provider=Microsoft.Jet.OLEDB.4.0;Data Source=&#8221; &amp; DatabaseFileNameAndPath<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">From the Solution Explorer, right-click Form1.vb, select &#8220;View Designer.&#8221; Double click on Form1 to open the Form1_Load() event, and add the following:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">&#8216;Get file path to user&#8217;s &#8220;MyDocuments\\OledbExample\\&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim pathstring As String = My.Computer.FileSystem.SpecialDirectories.MyDocuments<\/span><\/p>\n<p><span style=\"font-weight: 400;\">pathstring = pathstring + &#8220;\\MeasurementComputing\\OledbExample\\&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">System.IO.Directory.CreateDirectory(pathstring)<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">&#8216;Create a file.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Filename = pathstring + &#8220;Database.CFG&#8221;<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">&#8216;Read the data from the file and parse it.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Try<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0SettingsFileIn = New StreamReader(Filename)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0DatabaseFileNameAndPath = SettingsFileIn.ReadLine()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0SettingsFileIn.Close()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Catch ex As Exception<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End Try<\/span><\/p>\n<p>&nbsp;<\/p><\/blockquote>\n<p><span style=\"font-weight: 400;\">Return to the Form1 Design view, double-click on the &#8220;Select database&#8221; menu item, and add the following to the generated event:<\/span><\/p>\n<blockquote><p><span style=\"font-weight: 400;\">OpenFileDialog1.Filter = &#8220;MSACCESS (*.mdb)|*.mdb|All Files (*.*)|*.*&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">OpenFileDialog1.FileName = DatabaseFileNameAndPath<\/span><\/p>\n<p><span style=\"font-weight: 400;\">OpenFileDialog1.ShowDialog(Me)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">DatabaseFileNameAndPath = OpenFileDialog1.FileName<\/span><\/p>\n<p><span style=\"font-weight: 400;\">If DatabaseFileNameAndPath = &#8220;&#8221; Then<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0MsgBox(&#8220;No file selected or bad file name selected.&#8221;, MsgBoxStyle.OkOnly, &#8220;Bad file name&#8221;)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0Exit Sub<\/span><\/p>\n<p><span style=\"font-weight: 400;\">End If<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">&#8216;Create a file path to user&#8217;s &#8220;MyDocuments\\OledbExample\\&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Dim pathstring As String = My.Computer.FileSystem.SpecialDirectories.MyDocuments<\/span><\/p>\n<p><span style=\"font-weight: 400;\">pathstring = pathstring + &#8220;\\MeasurementComputing\\OledbExample\\&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">System.IO.Directory.CreateDirectory(pathstring)<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">&#8216;create the file.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Filename = pathstring + &#8220;Database.CFG&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">SettingsFileOut = New StreamWriter(Filename)<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">&#8216;Write the DATABASE filename and path to the .cfg file<\/span><\/p>\n<p><span style=\"font-weight: 400;\">SettingsFileOut.WriteLine(DatabaseFileNameAndPath)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">SettingsFileOut.Close()<\/span><\/p><\/blockquote>\n<p><span style=\"font-weight: 400;\">Now, you can compile and create deployment projects for these applications so that they may be distributed. Place the database on a centrally accessible computer. If desired, you can do this distributed system on two computers by having both the DataCollectionToDatabase application and MccGenericOdbc.mdb on one computer, and the other computer running the DatabaseToDataDisplay app.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Make sure all computers are mapped to drives containing the database. <\/span><a href=\"https:\/\/files.digilent.com\/resources\/MCC\/resources\/DatacollectionToDatabase.zip\"><span style=\"font-weight: 400;\">Completed application can be found HERE<\/span><\/a><span style=\"font-weight: 400;\">.<\/span><\/p>\n<h2><b>Using Additional Software to View Data Remotely<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">Now that we&#8217;ve created a working distributed data collection and display system (see the sections above), let&#8217;s briefly explore other software packages available to access the database for both reading and writing. Here are two software packages you can use to read the data from the database (of course, they can write to it as well, but that is not part of this article).<\/span><\/p>\n<h2><b>DASYLab<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">We can use DASYLab to retrieve data from our MS ACCESS database. DASYLab is a graphical software package that includes ODBC support. With the help of CJ Butler, we were able to access the local database here in Massachusetts from her office in New Hampshire (about 100 miles!) using her VPN connection to an MCC server.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">All she needed to do was map the Drive to her local computer, run the ODBC Administrator on her local computer, and create the attached worksheet.<\/span><\/p>\n<figure id=\"attachment_29313\" aria-describedby=\"caption-attachment-29313\" style=\"width: 600px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/15-dasylab-600x194.png\" alt=\"\" width=\"600\" height=\"194\" class=\"wp-image-29313 size-medium\" data-wp-pid=\"29313\" srcset=\"https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/15-dasylab-600x194.png 600w, https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/15-dasylab.png 877w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><figcaption id=\"caption-attachment-29313\" class=\"wp-caption-text\">Worksheet<\/figcaption><\/figure>\n<figure id=\"attachment_29314\" aria-describedby=\"caption-attachment-29314\" style=\"width: 600px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.digilent.com\/blog\/wp-content\/uploads\/2022\/08\/16-dasylabdisplay-600x340.png\" alt=\"\" width=\"600\" height=\"340\" class=\"wp-image-29314 size-medium\" data-wp-pid=\"29314\" srcset=\"https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/16-dasylabdisplay-600x340.png 600w, https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/16-dasylabdisplay.png 807w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><figcaption id=\"caption-attachment-29314\" class=\"wp-caption-text\">DASYLab Display<\/figcaption><\/figure>\n<p><span style=\"font-weight: 400;\">Beyond DASYLab, other software packages, including LabVIEW, can also connect to and read from the same database via ODBC, giving you additional flexibility in how you access and visualize your collected data.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">This concludes our series on building a real-time distributed data acquisition system. You have now seen how to create the database, collect and store data, display it in near real time, distribute the applications across multiple computers, and access the same data from alternative ODBC-compatible software. We hope this project gives you a solid foundation for developing your own distributed data acquisition and monitoring applications.<\/span><\/p>\n<div class='watch-action'><div class='watch-position align-left'><div class='action-like'><a class='lbg-style6 like-29294 jlk' data-task='like' data-post_id='29294' data-nonce='b35ab96435' rel='nofollow'><img src='https:\/\/digilent.com\/blog\/wp-content\/plugins\/wti-like-post-pro\/images\/pixel.gif' title='Like' \/><span class='lc-29294 lc'>0<\/span><\/a><\/div><div class='action-unlike'><a class='unlbg-style6 unlike-29294 jlk' data-task='unlike' data-post_id='29294' data-nonce='b35ab96435' rel='nofollow'><img src='https:\/\/digilent.com\/blog\/wp-content\/plugins\/wti-like-post-pro\/images\/pixel.gif' title='Unlike' \/><span class='unlc-29294 unlc'>0<\/span><\/a><\/div><\/div> <div class='status-29294 status align-left'>Be the 1st to vote.<\/div><\/div><div class='wti-clear'><\/div>","protected":false},"excerpt":{"rendered":"<p>In today&#8217;s manufacturing world, companies are trying to do more with less staffing by maximizing throughput or looking for the critical status of a production line without interrupting the focus &hellip; <\/p>\n","protected":false},"author":59,"featured_media":29342,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false},"categories":[4325,4361,4327,1563],"tags":[4359,4358],"ppma_author":[4507],"class_list":["post-29294","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-debug-validation-test","category-data-acquisition","category-projects","category-guide","tag-daq","tag-mcc"],"jetpack_featured_media_url":"https:\/\/digilent.com\/blog\/wp-content\/uploads\/2022\/08\/2022-ReadingGeneratedData2-part2-580.png","jetpack_sharing_enabled":true,"authors":[{"term_id":4507,"user_id":59,"is_guest":0,"slug":"jgreenberg","display_name":"Jeff Greenberg","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/4733b4dee4cf309719f59882b387a0c9fcd17b506ca0858a86a1ecd2c7ed324a?s=96&d=mm&r=g","author_category":"","user_url":"","last_name":"Greenberg","last_name_2":"","first_name":"Jeff","first_name_2":"","job_title":"","description":""}],"post_mailing_queue_ids":[],"_links":{"self":[{"href":"https:\/\/digilent.com\/blog\/wp-json\/wp\/v2\/posts\/29294","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/digilent.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/digilent.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/digilent.com\/blog\/wp-json\/wp\/v2\/users\/59"}],"replies":[{"embeddable":true,"href":"https:\/\/digilent.com\/blog\/wp-json\/wp\/v2\/comments?post=29294"}],"version-history":[{"count":2,"href":"https:\/\/digilent.com\/blog\/wp-json\/wp\/v2\/posts\/29294\/revisions"}],"predecessor-version":[{"id":32600,"href":"https:\/\/digilent.com\/blog\/wp-json\/wp\/v2\/posts\/29294\/revisions\/32600"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/digilent.com\/blog\/wp-json\/wp\/v2\/media\/29342"}],"wp:attachment":[{"href":"https:\/\/digilent.com\/blog\/wp-json\/wp\/v2\/media?parent=29294"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/digilent.com\/blog\/wp-json\/wp\/v2\/categories?post=29294"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/digilent.com\/blog\/wp-json\/wp\/v2\/tags?post=29294"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/digilent.com\/blog\/wp-json\/wp\/v2\/ppma_author?post=29294"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}