WebDAQ REST Control Introduction

Digilent WebDAQ devices, such as the 316, 504, and 904, are standalone data acquisition devices that offer a user-friendly web server interface. This interface, accessible through your preferred Internet browser like Chrome, Edge, or Firefox, eliminates the need to download and install a program. It’s a straightforward approach that ensures you don’t have to worry about software compatibility with your operating system, making your experience with WebDAQ devices a breeze.

The WebDAQ’s user interface is perfect for most everyday use. However, updating the configuration on multiple WebDAQs or ensuring the data aligns with your expectations can be tedious. That’s where the WebDAQ REST API comes in. It’s a powerful tool that allows you to create a program interface using HTTP requests to exchange data, giving you more control over data acquisition and device configuration.

The WebDAQ REST API is limited to GET and POST requests, similar to read and write file operations. A few resources also support DELETE, but it should be avoided. The complete list of uniquely identifiable resources (URI) and associated REST commands can be found by including ‘/API’ at the end of your WebDAQ IP address in your browser.

When performing a REST request, the command takes the form of a URL. For instance, the WebDAQ API version can be read by appending the string ‘/api/version’ to the end of the IP address. This URL is called the data endpoint. To facilitate communication, we need to import a few standard modules in Python. The Session module is primarily used for GET and POST read and write requests.  To demonstrate a simple request, the following code sample gets the API version from a WebDAQ at IP address 192.168.100.100.

from requests import Session
from utility import ScheduleStatus
from numpy import array

s = Session()
r = s.get(‘192.168.100.100/api/version’)

The raise_for_status function waits for the response.

r.raise_for_status()
version_json = r.json()
version = version_json[‘apiVersion’]

The version number is important because it is used in all endpoint requests. Fortunately, WebDAQs currently have only one version, ‘v1.0’. So, our URLs will always begin with the IP address followed by /api/v1.0/:

basel_url = ‘http://’ + ‘192.168.100.100/api/v1.0/’

If you are wondering how to obtain the IP address, Python makes this easy with the socket module.

import socket
ip_address = socket.gethostbyname(‘webdaq-xxxxxx’)

where xxxxxx are the lower six digits of the factory-assigned MAC address. This works with the factory-assigned name as long as it is unchanged. If it was changed in the Browser UI, then use the new name.

A more meaningful WebDAQ operation is to get the schedule because it contains the job name(s) that will be used to request the job JSON.

request_response = s.get(base_url + ‘/schedule/descriptor’)
request_response.raise_for_status()
schedule_json = request_response.json()
jobs = schedule_json[‘jobs’]

However, when the WebDAQ is powered on, its schedule may not be set for auto-start, which means it is not in memory. Therefore, before retrieving the JSON code for the job, we must check the status and, if necessary, load the schedule.

schedule_status_endpoint = base_url + ‘/schedule/status’
schedule_status_response = s.get(schedule_status_endpoint)
schedule_status_response.raise_for_status()
schedule_status_json = schedule_status_response.json()

An empty status means the schedule must be started to load it into memory:

if schedule_status_response[‘statusCode’] == ScheduleStatus[‘empty’]:
          r = s.post(schedule_status_endpoint, json={“run”:True})

If you wish to modify a job configuration, such as the number of samples to acquire, get the job JSON code and access the appropriate property. For example, to change the number of samples to acquire we can do this:

jobs = schedule_status_json[‘jobs’]
job_name = jobs[0]
job_descriptor_endpoint = base_url + /schedule/jobs/’ + job_name + ‘/descriptor’
job_descriptor_response = s.get(job_descriptor_endpoint)
job_descriptor_response.raise_for_status()
job = job_descriptor_response.json()
job[‘acquisition’][‘stopTrigger’][‘sampleCount’] = new_count

We can also modify the channel list besides changing acquisition settings. The following is an example of the JSON descriptor from a WebDAQ 504 IEPE channel, typically used with dynamic sensors such as an accelerometer.

              IEPE = [{'number': 0,
                         'type': 'acceleration',
                         'www': {'productId': 316,
                                 'color': '#DD3222',
                                 'dashboardScalar': True,
                                 'dashboardStripChart': True,
                                 'dashboardFFT': True,
                                 'showFFTPeak': True,
                                 'fftWindowType': 'none',
                                 'fftIntegration': 'none'},
                         'name': 'Camshaft',
                         'range': '12',
                         'unit': 'psi',
                         'couplingMode': 'ac',
                         'iepe': True,
                         'sensorSensitivity': 1,
                         'customScaling': {'type': 'linear', 'linear': {'multiplier': 1000, 'offset': 0}}}]

We can use it as a template to rewrite the channel list. First, we delete the items from the list using the pop() method.

While len( job[‘channels’] ):
	Dummy = job[‘channels'].pop()

Then, we can make a copy of the template. We want a completely independent copy of the template object, so we use the Python deepcopy method like this:

Accelerometer0 = copy.deepcopy(IEPE)
Accelerometer1 = copy.deepcopy(IEPE)

The channels must be assigned a number beginning with zero and have a unique name.

Accelerometer0 [0][‘number’] = 0
Accelerometer0 [0][‘name’] = ‘Camshaft sensor’

Accelerometer1 [0][‘number’] = 1
Accelerometer1 [0][‘name’] = ‘Crankshaft sensor’

job[‘channels’].extend(Accelerometer0)
job[‘channels’].extend(Accelerometer1)

Upload the modified job descriptor.

r = s.post(base_url + /jobs/job1/descriptor’, json=job)
print(‘Status response = ‘ + str(r.status_code)

The WebDAQ will check the uploaded JSON job descriptor and return 200 if no errors are found. So, ensure that the status response indicates 200 and not 400.

Next, now that we made the changes we want to the job descriptor, we can request the WebDAQ send back data from the current job. This could be helpful for automatic monitoring. For instance, you could need to monitor many WebDAQs to ensure the measurements align with expectations.

The data request URL format for our job named ‘job1’ is:

base_url + ‘/schedule/jobs/job1/samples/index/max_count/bin’

The index parameter starts at zero and advances with each request. The max_count is the amount of data to request and must not exceed 10000. Here, we set the index to zero and use GET to request data:

index = 0
data_request = base_url + ‘/schedule/jobs/job1/samples/’ + str(int(index)) +  ‘/100/bin’
data = s.get(data_request)
data.raise_for_status()

Now that data contains data, we must determine how much data it gave us. This is done by dividing the buffer length by the number of channels times the data type size, which is eight bytes for the double data type.

num_of_samples = len( data.content ) / 8 * len( job[‘channels’] )

Next, we need to convert the string of binary codes to meaningful numbers. To do this, we use calcsize and unpack from the Struct module. First, we create a format string specifying ‘d’ for the double data type. We then get the size and use it to unpack the string of binary codes into an array of usable numbers.

read_format = str( int(num_of_samples * num_of_channels ) + ‘d’ )
read_size = calcsize(read_format)
numpy_data = array( unpack( read_format, data.content[0: read_size] ))

More data can be read with another request, but we must advance the index by num_of_samples. It is also good to check the job status after each read to ensure it has stopped.

read_format = str( int(num_of_samples * num_of_channels ) + ‘d’ )
read_size = calcsize(read_format)
numpy_data = array( unpack( read_format, data.content[0: read_size] ))
for chan, item in enumerate(channels):
          print('{:2.5f}'.format(numpy_data[int(chan)]), item['unit'], ' ', end='')

Depending on the WebDAQ and the rate at which data is being acquired, you could get old data. The WebDAQ 504 can acquire data quickly; by the time you have requested 100 samples, it may have recorded several thousand. To minimize this, query the job status for ‘samplesAcquired’ and subtract 100 to get the most recent data.

The purpose of this article was to give you an idea of what is possible with the REST API. The code samples were not meant to be pasted into a program; they were provided merely for demonstration. We covered only the minimum necessary for control. We began by requesting its API version and finished by retrieving live data. In between, we modified a single property in the job JSON code called ‘sampleCount’, and showed how to modify the channel list. We used Python for the demonstration but could have used C#, VB.NET, and LabVIEW. For a complete example program, go to the support page for any WebDAQ to download Digilent’s suite of REST API example programs. The read_data.py example provides all the checking necessary when reading data.

Author

Be the 1st to vote.

Leave a Reply

Your email address will not be published. Required fields are marked *