Creating an ARDI Live Driver
This tutorial will re-create the ARDI Live Text driver as an example of how to create your own ARDI driver in Python.
The Sketch
To begin, we will sketch out the basic structure of our Python script.
First, create a new folder in /opt/ardi/drivers/live with the name of your driver (ie. /opt/ardi/drivers/live/text2.
Now create a matching python source file - a plain text file - in that folder, named <foldername>.py - ie /opt/ardi/drivers/live/text2/text2.py.
Paste in the following content…
- baselivedriver.py
from ardi.drivers import driverbase, drivercore class MyDriver: def __init__(self): self.connected = False self.polling = True def SetAddress(self, addr): pass def Connect(self): return True def Disconnect(self): pass def Optimise(self): pass def Poll(self): pass class driverfactory: def createinstance(self): return MyDriver() if __name__ == "__main__": sdf = driverfactory() base = driverbase.ardidriver() base.start(sdf)
This is the basic layout of every ARDI Python driver.
Functions
There are five key functions required in your driver class.
SetAddress
This function is called after your driver is first created. It includes an encoded address for your data source from ARDI.
You'll need to split it into its component parts and store them for when its time to connect.
Connect
The connect function is called whenever your driver is to connect to a data source.
It returns True if connection was successful and False if connection fails.
Disconnect
This does the opposite of connect - it closes the connection to your data source and does any cleanup required.
Optimise
The Optimise function is called when the driver has successfully connected, but before the first call to poll.
The function is also called whenever a change occurs to the data links in ARDI that would effect your driver (ie. when a new asset is linked to data from your source).
The code in this function should perform any work that needs to be done to streamline the work of the poll function in getting data from the source.
For example, if this was an SQL data source, it would probably create your SQL statement here.
Poll
The poll function is called in a constant loop every time ARDI would like additional data from the data source (the rate this occurs is the refresh rate of the driver configured in the ARDI web interface).
In this function you perform the actual request for new data (or if your data requests are asynchronous, deliver your most recent data).
Implementation
Your class has access to some important functions and variables.
self.core.NewData()
This function is responsible for queuing live data to be sent to ARDI (the data isn't physically sent until the poll function completes).
Parameter | Purpose |
---|---|
Address | The address of the data point that has been read |
Value | The value read from your data source |
self.core.logger
This member is a Python logging object that can be used to record any information of note, through calls like self.core.logger.info and self.core.logger.debug.
self.core.points
This member contains a list of all of the data points the driver needs to load. It's a Python list containing an object for each point.
Each point contains the following information…
Member | Description |
---|---|
address | The address (or lookup value) for the point. |
code | The ARDI code for this point, in asset id:property id:attribute format. |
While there are other attributes of the point, these are used internally and you should not need to access them.
Splitting Our Address
The address for our text file driver will include the following options…
Option | Description |
---|---|
File Name | The name of the text file to open |
Delimiter | The delimiting character. If blank, it defaults to TAB (/t) |
Lookup Column | The number of the column that contains the value name |
Value Column | The number of the column that contains the value itself |
The address lists these options, separated by a colon (:) character. IE. /tmp/data.txt::1:2.
It's up to this function to…
- Split the address string into its component parts
- Record this information for use in the connect, optimise and poll functions
bits = addr.split(':') self.filename = bits[0] self.delimiter = bits[1] if self.delimiter == "\\t": self.delimiter = "\t" self.lookupcol = bits[2] self.valuecol = bits[3]
Connecting to a Text File
It's up to our connect function to…
- Check that the file exists
Because we want to allow applications to change the file between polling it, we won't actually open the file at this point - we will open the file each time we poll it.
if os.path.isfile(self.file): return True self.core.logger.warn("File not found - Data source could not be opened") return False
Optimising
There isn't a great deal of optimisation to do for this particular driver - we don't need to prepare an SQL query, subscribe to a web service etc.
Polling
To poll this file, we need to…
- Open the file
- Read it line by line, splitting by the delimiter
- If the Lookup Column matches the address of any of our points, we need to give the point the value in our Value Column (this is done using the NewData function)
- Close the file so that other processes can write to it.
with open(self.file,'r') as f: for line in f: bits = line.split(self.delimiter) if bits[0] in self.lookup: vl = str(bits[1]).strip() self.core.NewData(bits[0],vl)
Creating a Web Interface
The last step is to create a web interface for our driver, so that we can set the drivers various options (file name, delimiter, column numbers etc.) in a friendly manner.
This will require some basic HTML & PHP.
Firstly, copy one of the existing drivers user interfaces by copying /opt/ardi/web/data/live/text to /opt/ardi/web/data/live/text2 (the folder name must match the name of the driver folder & file).
There are several PHP files in this folder. Click on them for examples and documentation on what they do.
File | Purpose |
---|---|
info.inc | Provides details on the driver |
configure-source.php | The user interface for setting up the data source |
saveconfig-source.php | Convert the properties set in configure-source.php to a machine-readable address string |
friendlyname.php | Convert the address from saveconfig-source.php to a human-readable text string |
link.php | The user interface for setting up a data link between the source and an asset property |
encode.php | Convert the properties set in link.php to a machine-readable address string |
decode.php | Convert the address from encode.php to a human-readable description |