Device Drivers are very flexible, and for that reason they are also very complex. In fact, creating device drivers is the most complex part in V-Control.
All available device drivers are stored as a template in a database (Devices.rsd). Once they're added to a project, a copy is stored in the project file (*.vc4) too. The Device Editor is used to modify / create these drivers. To modify a driver in an existing project file, the Device Editor is launched via Configure → Edit Devices or the Device Edit toolbar button. Now only devices in the current project are available for editing. This means also that all changes to the device driver concern only the current project. The device database with the templates stays untouched. To edit the device templates, the device editor is launched via Configure → Edit Device Templates. In this case, the drivers in the current project stay untouched.
The Device Editor is split into two main sections. The upper part concerns all Device properties, the lower part contains all commands of a device. The image shows the Device Editor for editing devices that are part of the project, not the Device Templates.
In the upper part of the Device Editor the properties of a device are shown. The fields Type, Manufacturer and Category are used to find devices in the database. Device GUI is not active at the moment and used for later extensions. Add Category is available only if the Device Editor was opened via Configure → Edit Device Templates. In this case a popup window appears asking for a new category name.
Device Variables are used to store the status of a device. The driver is in charge to set the device variables to a value that represents the current status of the device. As example lets look at a projector. A procetor has some inputs, it can be switched on or off and most of them have a lamp timer. The driver collects information about the device and fill this information in the device variables.
A projector could have the variables PowerState, SelectedInput and LampHours. The driver request now information from the projector that tells if the projector is on or off. This information is stored in the PowerState variable. The driver also asks the projector which is the current selected input, and stores this information in the SelectedInput variable etc.
The number of device variables is not limited. It depends on what the designer of the device driver thought was useful to have.
Each time a Device Variable changed, an event is fired. This event can be used by V-Control as a trigger to start tasks.
By default, the event is fired only if the variable changes, and not if the variable is updated but does not change. What does this mean in detail? Lets do an example:
Assume that the driver asks a projector every second if for the power state. If the projector is switched off, the drivcer wil set the PowerState variable to “off”. Now the projector switches on, and the next time the dreiver requests the power information “on” is returned and the content of the PowerState variable changes from “off” to “on”. An event is fired, and if we have programmed an event handler in V-Control, the content of the variable is checked. If the variable content matches the condition defined in the event handler, a task is fired.
One second later the driver asks again for the power state. Because the projector is still on, a “on” value is returned. By default, no event is fired in this case bacause the PowerState variable does not change. However, there might be some rare situations where you want to fire the event each time a new variable value is requested from the device, no matter if it has changed or not. In this case, check the Fire Events on unchanged Variables box.
Many commands sent to a device need parameter(s) to make sense. E.g. a CueUp command for a Player need a Timecode to tell the player where to go. An example for a parameter less command is the Play command. This instruction contains all the information that is needed for the driver, simply turn the device in play mode. As these examples show, it is necessary to refer parameters to a command.
In V-Control, up to six parameters for each command are available. We distinguish between device parameters (DP1 and DP2) and command parameters (P1 - P4). Both parameter types are equivalent and used in the same way.
Device parameters affect more the device itself and less the individual functions. They are visible for each command, and all commands share the same Device Parameters.
One example for a device parameter is the Modbus TCP protocol. Some devices require their own Unit ID, even if they are only accessible under a specific IP address. The Unit ID adresses the device and needs to be involved in every command. So we can use a Device Parameter for that Unit ID.
To enter a parameter, an parameter input field is needed. This could be a text field, a spin button or a drop down box. In the example above a spin button is used to enter the Unit ID (Unit ID is a Device Parameter in this case) and the RelayNb. A drop down box determines whether the relay should be switched on or off. The parameter input field is defined by five controls in the device editor. These are the same for Device Parameter and Command Parameter.
1. Name: The name of the parameter. This is just a label that tells the user what kind of parameter is needed.
2. GUI: The type of the input field. Three types are available.
|#||Any single digit placeholder. The user can type only a digit character in this position.|
|.||Decimal placeholder. The decimal placeholder that is actually used is specified in the user's International settings. The character is treated as a literal (formatting) character for masking purposes.|
|,||Thousands separator. The thousands separator that is actually used is specified in the user's International settings. The character is treated as a literal (formatting) character for masking purposes.|
|:||Time separator. The time separator that is actually used is specified in the user's International settings. The character is treated as a literal (formatting) character for masking purposes.|
|/||Date separator. The date separator that is actually used is specified in the user's International settings. The character is treated as a literal (formatting) character for masking purposes.|
|\||Mask escape character. Treat the next character in the mask as a literal. The escape character enables you to use the '#', '&', 'A', '?' (an so on) characters in the mask. The escapted character is treated as a literal (formatting) character.|
|&||Character placeholder. Valid values are the ASCII characters 32-126 and the non-ASCII characters 128-255.|
|>||Convert all the characters that follow to uppercase. Uppercasing works beyond the ASCII range where appropriate, e.g., ü becomes Ü.|
|<||Convert all the characters that follow to lowercase. Lowercasing works beyond the ASCII range where appropriate, e.g., Ü becomes ü.|
|A||Alphanumeric character placeholder, where entry is mandatory. For example, the spec “AAA” specifies three alphabetic characters.|
|a||Alphanumeric character placeholder, where entry is optional.|
|9||Digit placeholder where entry is optional.|
|C||Character or space placeholder, where entry is optional. It operates like the '&' placeholder.|
|Any literal||All other symbols are displayed as literals for formatting purposes.|
|~||Reserved for future use. If you use “~” it will trigger an exception error. Use \~ instead.|
A device driver as 4 different types of commands:
In the following example, we create a new driver for the Adtec SOLOIST 2 MPEG player. I use this old fashioned device because we see all aspects of device drivers here. To start this job, it is necessary to have the protocol provided by the manufacturer and study it. The protocol describes the RS232 settings (baudrate, start bits, stop bits, parity) and the control cable pin assignment as well as the commands the device understand. The default RS232 parameters are 38400 baud, 8 data bits, 1 stop bit and no parity, so we need to create a channel with these parameters. In this example we call it “soloist”.
There is no device using a protocol that is similar to Adtec's SOLOIST 2, so we load the Generic Device from the I/O category ( see Device Setup ) and assign the channel “soloist” to this device. Then the project is saved as e.g. soloist_test.vc4.
To start we need to launch the device editor via main menu Configure → Edit Devices and select the device GenericDevice_1.
The two commands OnData and SendString are not used and can be deleted. To do that click on the command and then click on Delete Cmd (see screenshot)
Each command for the SOLOIST 2 player need at least one parameter - the name of the device. The “*” character is used as a universal name and every SOLOIST accept this sign as his name. Because the SOLOIST name concern the device and not the command it is implemented as device parameter. In the field DP1 Name enter “Name” as identifier and “*” in the field DP1 Default. To add this parameter to the device click Update Device.
The first command we implement is the STOP command. The command string looks like this: “* STOP” + Chr(13). As feedback (Acknowledge) the SOLOIST 2 send “OK”+ CR+LF+CR+LF. CR means Carriage Return and LF Line Feed. CR is the ASCII Coder 13, or 0D in hexadecimal. LF is 10 or 0A in hexadecimal.
To implement the command follow the instructions in the screenshot.
ScriptResult = DP1 + “ STOP” + ChrB(13)assigns parameter DP1 to ScriptResult. DP1 is the first of two available device parameter and contains in this case the name of the SOLOIST. Totally up to six parameter (DP1,DP2,P1,P2,P3,P4) are available, but this time we use only DP1. Then a space character followed by STOP (“ STOP”) is added. The ChrB function converts an Integer value to ASCII code. The SOLOIST 2 expect a Carriage Return (ASCII code 13) as terminating character which is added by the ChrB function. Now the command string is complete.
The Timeout leads to the question, how V-Control knows if, respectively when, an Acknowledge is present. That tells the field Ack in Hex: Here the expected response is entered. The screenshot below shows that the 0D0A is expected.
As Ack String, only Hex code is accepted. So you need to convert everything to Hex. If you enter text in the Ack in Hex field and press the button To Hex, the text converts ASCII String to hex code.
But the SOLOIST 2 not only send 0D0A as Acknowledge. The complete string is OK0D0A0D0A. Well, we don't care about that. If we received 0A0D then we know that the message was received and processed by the Soloist.
The first command is complete. A click on the button Add Cmd saves the command. Because V-Control compiles the script, we have to click the Compile button. If there is an error, the compiler posts a compiler error, otherwise the device is usable now. To do that, close the Device Editor and select the Device GenericDevice_1 again (see screenshot below). The Stop command now appears in the command list. The device parameter Name has the “*” character as default value. Alternatively we can use the real SOLOIST 2 name, e.g. “SOLOIST”.
If the command is working it is recommended to save the project. Until now, the driver exists only in the project file (“soloist_test.vc4” in this example), and this file contains all the work we did. If the driver is complete, we will export it and then import it to the device database. To complete the driver we start the Device Editor via main menu Configure → Edit Devices again. The table below shows the remaining simple commands for the SOLOIST 2 player. Proceed in the same way as we did with the Stop command.
|Name||Position||Type||Timeout||Script||Ack in Hex|
|Play||11||Comands||500||Scriptresult = DP1 + “ PLAY” + chrB(13)||0D0A|
|Next_Clip||20||Comands||500||Scriptresult = DP1 + “ NEXT” + chrB(13)||0D0A|
|Prev_Clip||21||Comands||500||Scriptresult = DP1 + “ PREVIOUS” + chrB(13)||0D0A|
|Still||22||Comands||500||Scriptresult = DP1 + “ PAUSE” + chrB(13)||0D0A|
|Rewind||23||Comands||500||Scriptresult = DP1 + “ REWIND” + chrB(13)||0D0A|
The result should look like this:
Finally click the Compile button and test the driver.
Now the general conditions to control the device are created. As pointed out in the SOLOIST 2 protocol, the general command format for the device is:
Parameters in angle brackets <> are mandatory Parameters in square bracket  are optional
Respecting the above instructions, the command string for a simple PLAY command sent to a device named “SOLOIST” looks like this:
“SOLOIST PLAY” + chr(13)
In this chapter the SOLOIST 2 driver is enhanced with a couple of more complex commands. These commands need one or more parameters. In the chapter before, we used the Device Parameter 1 (DP1) to address the SOLOIST (DP1 contain the name of the SOLOIST). The following four commands use more then one parameter. Additionally the three types of input fields (Text Field, Spin Button and Drop Down Box) are introduced.
The first new command is the “SetName” command. With this command we can change the SOLOIST's ID. Start the Device Editor again (menu Configure → Edit Device…) and select the device GenericDevice_1.
The creation of this command is very similar to the commands described in the chapter before. The difference is that we use one more parameter (P1). The field P1Name shows the parameters name. Because the command shall change the name we call the parameter “SetName”. P1 GUI stays at Text, because it makes sense to use a text field to enter a new name. In difference to DP1 we don't use a default value.
Crucially again is the line
Scriptresult = DP1+“ NAME ” + P1 + chrB(13). As before, DP1 contains the current ID (name) of the SOLOIST player. Then the command string “ NAME ”, with leading and succeeding space character is added. P1 contains the new name for the SOLOIST and ChrB(13) is for the carriage return (ASCII code 13) termination character. To save the new command click Add Cmd or Update Cmd (if the command was edited) followed by Compile.
The next command is SetErrorLimit, which determines how often the player try to read from hard disk, before playing interrupts. Valid parameter is a number between 0 and 1000. To enter this parameter we want to use a Spin Button. The Device Editor now looks like this:
P1 Name contains the designation of the parameter as usual (“Error Limit”), but P1 GUI is set to Spin Button. P1 MinMax contain, separated by semicolon, the minimum and maximum numeric value that P1 might have. As default value 0 is selected, because usually there should be no reading error. Comparing to the previous command, the script is the same except that we replace “ NAME ” by “ ERRORLIMIT ”. Don't forget to click Add Cmd or Update Cmd followed by Compile.
The StartUp command determines how the SOLOIST player behaves on Power On. If StartUp = On, the player starts playing after power on, if StartUp = Off the player do nothing. There are only two possible values as parameter, “On” and “Off”, so we use a Drop Down Box as input field.
P1 Name contains the designation of the parameter as usual (“Startup”), but P1 GUI is set to Drop Down. P1 MinMax contain, separated by semicolon, the possible values, that P1 might have. Comparing to the previous command, the script is the same except that we replace “ ERRORLIMIT ” by “ STARTUP ”. Don't forget to click Add Cmd or Update Cmd followed by Compile.
The last command is the CueUp command. This command lets the SOLOIST jump to a position in the currently loaded clip. The position is entered as timecode (HH-MM-SS-FF) which addresses an absolute position in the clip. But the SOLOIST also knows relative positions. E.g. “+ 00-01-02-00” jumps forward one minute and two seconds from the current position. “- 00-01-02-00” jumps one minute and two seconds back. So we need three parameters (Soloist Name, absolute or relative position and the position as time code.
The parameter P1 determines the mode (absolute, relative +, relative -), and is implemented as Drop Down Box. The default value is “Absolute”, because this is the most often wanted mode. P2 is the parameter for the time code and implemented as text field. P2 Mask set the input mask for this parameter. In this case, only numeric values are accepted, separated by “-” character. We can not use the “:” as separator in a mask, because it is used for time formats (HH:MM:SS) and not for Time code (HH-MM-FF-SS).
This time, the script is a little more complex:
//Replace all "-" characters In Parameter2 With ":" P2 = ReplaceAll(P2,"-",":") If P1 = "Absolute" Then ScriptResult = DP1 + " INDEX " + P2 + Chr(13) If P1 = "+" Then ScriptResult = DP1 + " INDEX + " + P2 + Chr(13) If P1 = "-" Then ScriptResult = DP1 + " INDEX - " + P2 + Chr(13)
The SOLOIST expects the time code in the format “HH:MM:SS:FF”, with “:” as separator and not “-”. P2 uses “-” as separator, so we have to replace any “-” sign by a “:”. This is what
P2 = ReplaceAll(P2,“-”,“:”) does. Then we compare the value of P1 with “Absolute”. It true (P1 = “Absolute”) then no “+” or “-” sign is leading the time code and for the SOLOIST this is an absolute value.
Don't forget to click Add Cmd or Update Cmd followed by Compile.
Every device driver can have a help text. The text is for the user to give him some hints or necessary information to use the driver and / or the device. It is often used to describe the RS232 properties like baud rate, data bits, parity etc. or the pin assignment for the control cable.
If the help text is complete, the button Update Device saves the text in the database. In the Device GUI it is now retrievable via Help button ?.
In all our examples we used the ACK string (ACK in Hex:). If the attached device send this string as response, we know that the command was understood and executed (usually, some devices send two acknowledges, one if the command was valid and one when execution is finished). If the expected string is not received in the time determined by Timeout, a Timeout error occurs. But there are acknowledges that need to be processed further.
For a player, V-Control should always know the playback position. The Soloist has a command “TIMECODE”, which returns the current position as timecode. We want to have this useful information in a device variable. We need a combination of Monitor command and Ack command to do this.
A monitor command is a command that is executed periodically. It is used to request the status of a device. This can be a projectors lamp hours or power status, or in the following example the timcode position of a player.
To create a Monitor Command select it in the commands type field (drop down box).
The fields Timeout and Ack in Hex are used in the same way then before. Two things are new:
It is important to know that there is only one timer per device. If you have two Monitor Commands, both using 1 as Repeat value, they are not fired every 20ms. In this case they are fired every 40ms (first 20ms slot command 1, second 20 ms slot command 2).
If you want to monitor i.e. a projectors lamp hours, input channel and power state, it does not make sense to fire these request very fast. Usually approx. 1 second is enough. One Second means a Repeat value of 50.
For the timecode position it makes sense to request it very fast, so we use 1 as repeat value here.
The command is complete, whats missing is processing the result and store it in a device variable. This is done by an Ack Command. The Ack Command is selected in the Ack Cmd drop down box. Because we do not have the command right now we need to create it and assign the Ack command to the GetPosition command later.
The result of the GetPosition command has do be stored in a device variable. So the first step is to create one.
Enter the variable name in the Variable Name field and click the Add Variable button.
Next, create a new command as shown below.
Here we have a quite complex script to fetch the timecode from the Soloist returning message. This is because the soloist returns the timecode in the format
OK<CR><0A> H:MM:SS.FF<CR><0A>, but we need
OK, lets go through the lines:
Here wie declare variables we need, all of type string in this case.
dim TC,HH,MM,SS,FF,tmp as string
The variable IOResult is permanently available and does not have to be declared. The Answer from the Soloist is stored here.
If you don't know how
NthField works please read the NthField documentation in the Compiler section.
The content of IOResult is
OK<CR><0A> H:MM:SS.FF<CR><0A>, so we fetch the right part of the first <CR>
tmp = NthField(IOResult,chr(13),2)
now tmp has the content
<0A> H:MM:SS.FF. Next we grab the Hours
HH = NthField(tmp,":",1)
HH is now
then we grab Minutes and Seconds
MM = NthField(tmp,":",2) SS = NthField(tmp,":",3)
Because the Frame delimiter is “.”, we grab the frames by
FF = NthField(tmp,".",2)
Now get the Hours
HH = mid(HH,3)
HH = H now (one digit hours)
Make HH as two digit with preceding “0”
if len(HH) < 2 then HH = "0" + HH
Build the Timecode String
TC = HH+"-"+MM+"-"+SS+"-"+FF
Set the Device Variable
Now we assign this Command as Ack Command for the GetPosition command. Select the GetPosition command, and use the drop down box Ack Cmd to select AckGetPosition.
This example shows how to automatic set the MinMax range of parameters. The SOLOIST knows the commands CUESPOT and PLAYSPOT, both need the name of the spot (clip) as parameter. It is of course possible to proceed in the same way than e.g. with the SetName command, and implement a text field that contains the clip name. But the SOLOIST also knows the command INVENTORY, which returns a list of all available clips on the machine. This leads to a more comfortable way to select a clip. We use the INVENTORY command to get all available clips, and then we put this list in the MinMax definition of the CUESPOT and PLAYSPOT command. This presents the end user a picklist, and there is no need to type the clip names (and make mistakes).
To start with the example we create the CUESPOT command as shown below:
The script is only one line:
ScriptResult = DP1 + " CUESPOT " + """" + P1 + """" + Chr(13)
Respecting the SOLOIST protocol, the spot name has to be in quotation marks. The four quotation marks (“”“”) before and after P1 do this job. P1 GUI is a Drop Down list that contains a list of available clips. But at this time we don't know how many clips are present on the SOLOIST player, so it stays empty.
Then we create the command PLAYSPOT in the same way
ScriptResult = DP1 + " PLAYSPOT " + """" + P1 + """" + Chr(13)
Now we have two new commands, but until now they are not useful because we can't select a clip name as parameter.
To get all available clips we create the command “GetInventory”. His job is to receive the clip list and fill the MinMax definition of PlaySpot and CueSpot commands. The GetInventory command is looks like this:
The GetInventory command does not fill the MinMax definitions. The command only tells the SOLOIST that the inventory list is requested. To read the list and fill the MinMax definitions, we use an acknowledge command, in this case ACK_GetInventory.
The script is quite complex, so here some explanations:
Dim i As Integer Dim tmp, res As String res = "" For i = 1 To CountFields(IOResult,Chr(13)) //Dont use the first row And last two Rows If (i > 1) And (i < CountFields(IOResult,Chr(13)) - 1) Then tmp = NthField(IOResult,Chr(13),i) //Delete first character because it's trash tmp = Mid(tmp,2) res = res + NthField(tmp," ",1) + ";" End Next //Delete last ";" res = Left(res,Len(res)-1) //Replace current MinMax Value with res ChangeCMD_MinMax("CueSpot","P1",res) ChangeCMD_MinMax("PlaySpot","P1",res)
Line 1 declares i as Integer variable. It is used in the For .. Next loop as counter. Tmp is used as temporary variable and res is used to hold the new MinMax list. The function
CountFields(IOResult,Chr(13) tells how many lines (every line is terminated by chr(13)) in IOResult. The For .. Next loop is processed as many times, as chr(13) codes present in IOResult. The first line and the two last lines of IOResult don't contain any valid information, that's why the If .. Then statement present. The statement
tmp = NthField(IOResult,Chr(13),i) assigns content of the i'st line to tmp. Then the content of tmp is added to res, followed by a semicolon.
Now we have to change the GetInventory command an tell it to use the acknowledge command.
Set Ack Cmd to “ACK_GetInventory” (Update Cmd and Compile)
From a device drivers point of view, an event is a message received from a device without requesting it before.
In this example I use a weather station that sends Wind Speed, Temperature and Humidity every second. The format of the string is 4 digit wind speed, 5 digit temperature and two digit Humidity, all packed in one string.
12,323,5087 means 12,3 km/h wind speed, 23,50°C and 87% Humidity. There is no termination character, but we have a defined length (11 characters).
First we need 3 device variables to store the received data
Then we need a new command of type Event. Here we use the ACK Length field to determine the amount of data that we need to process the event. If the Channel, that is associated with the device receives the data, it will look to the Ack in Hex and / or Ack Length field. If one of them matches then the data is send to the device and the event is processed.
To transfer a driver from one system to another, the Export / Import menu item in the device editor is used. Via device editors menu File → Export… the currently selected driver is exported to a file.
In the save dialog enter / select a file name and click Save.
This is also used to move new device drivers to the Devices.rsd database, which has all the drivers shipped with V-Control. If you created a new device like in the examples above, then this device exists only in your project file. Export it and the import it in the Devices.rsd by selecting Configure → Edit Device Templates. Then import the new device.
To transfer a driver from one system to another, the Export / Import menu item in the device editor is used. The Import menu item is available only if the device editor is launched via Configure → Edit Device Templates The File → Import command imports a previously exported driver. A dialog box asks for the name of the new driver.