Sequencing Reads in a Pump-Off Controller
Reading the Dynacard data (Dynacard Upload Driver Tag) from a pump-off controller is a multi-step process that involves a number of Modbus messages being exchanged with the controller. The Dynacard Upload Driver tags control the sequencing of the messages, and if you are using a standard, supported pump controller you need not worry about this sequence. However, if you have a non-standard controller or are adding support for a new type of flow computer, you may wish to edit this sequence.
You can review your Dynacard's current message sequence by using the Export button provided in the Communications tab of your Dynacard Upload Driver tag’s properties folder. Use this to export the current message sequence to a CSV file, which can be opened up in Excel.
Use the Import button in the same tab to read an edited CSV file and import its sequence into the Dynacard Upload Driver tag.
An example of an exported Dynacard sequence:
The columns are as follows:
Column | Description |
---|---|
Step Name | A unique name given to this step in the sequence. |
Step Module | The name of the sequencer module to run during this step. |
Parm1 - Parm20 | Parameters to the sequencer module. |
When a read is triggered in the Dynacard Upload Driver, it will start at the top of this list and run each step module one after the other in sequence. Refer to the following discussion of each of these modules, their parameters and how they work. Combining them should allow you to perform the necessary sequence of reads and writes to get data from your flow computer.
The Dynacard Structure
Raw registers are read from the pump-off controller and saved in memory. These registers are then parsed for the card data, which is saved in a defined structure within the driver. This structure is logged to the VTScada historian, and certain elements of the Dynacard structure (for example, the force / position data) are shown on the Dynacard screen.
The Dynacard structure, and all of its elements, are listed in the chart below. Not every element will be used for every pump-off controller, but your sequence CSV file should use these names exactly in order to ensure that the data is saved properly.
DataGroup | Variable | Comment |
---|---|---|
Card1 | CardXY | The force/position data used in the Dynacard chart |
TimeStamp | If no time stamp is provided, the time at which the data was read will be used. | |
ShutdownID | Shutdown Event ID | |
NumPoints | ||
MaxLoad | ||
MinLoad | ||
StrokeLength | ||
StrokePeriod | ||
ControlMode | ||
DHCardXY | Downhole force/position data | |
DHTimeStamp | Downhole time stamp | |
DHMaxLoad | ||
DHMinLoad | ||
DHShutdownID | Downhole Shutdown Event ID | |
DHNumPoints | ||
DHGrossStroke | ||
DHNetStroke | ||
DHPumpFillage | ||
DHFluidLoad | ||
Card2 | Same variables as Card1 data group | |
Card3 | Same variables as Card1 data group | |
Card4 | Same variables as Card1 data group | |
Card5 | Same variables as Card1 data group | |
Card6 | Same variables as Card1 data group | |
Card7 | Same variables as Card1 data group | |
Card8 | Same variables as Card1 data group | |
Card9 | Same variables as Card1 data group | |
Card10 | Same variables as Card1 data group |
Sequencer Modules
This table provides an overview of each module. Full descriptions follow the table.
Module | Description |
---|---|
ReadValues | Reads registers from the pump-off controller and puts them in memory |
NewDataGroup | Adds a new DataGroup to the Dynacard structure |
GetValue_Numeric | Reads values from memory and saves them in the Dynacard structure as a single numeric value |
GetValue_XYCardData | Reads values from memory and saves them in the Dynacard structure as a set of XY Dynacard data. This module assumes that the X and Y values are interleaved within a single block of registers (see full description below) |
GetValue_XYCardData_Blocks | Reads values from memory and saves them in the Dynacard structure as a set of XY Dynacard data. This module assumes that the X and Y values are stored in separate blocks of registers (see full description below) |
WriteValue | Writes a value to the pump-off controller |
ResetValues | Clears memory of the previously read values |
SetNextStep | Sets the next step of the sequence that should be run |
CondNextStep | Sets the next step of the sequence based on some condition |
CounterLimit | Sets the next step or sets an error flag when it has been run a designated number of times |
CounterReset | Resets the counter used in the CounterLimit module |
TimeDelay | Waits a specified amount of time before proceeding to the next module |
EndSequence | Ends the sequence; should be the last module in every sequence |
Reads registers from an RTU and stores them in memory.
Parameters:
1 | StartRegister | First register to read |
2 | N | Number of registers to read |
This module reads any number of registers from the RTU and stores them in memory to be accessed by other modules. Note that this function does not take into account the content of the data being read in any way, including data types. If you wish to read a double floating point, for example, you would start at the appropriate register and request a read of four registers in order to bring back the whole thing.
Note that in many cases, particularly if the Modbus mapping in the pump-off controller is well-organized, it is more efficient to read several values in a single message. If, for example, you wanted to read the data from registers 40010, 40011 and 40013 (but you don’t care about 40012), its usually best to do a single four-register read rather than breaking it out into two smaller reads.
Adds a new data group to the Dynacard structure
Parameters:
1 | GroupName | Label to use for the new data group (Card1, Card2, etc.) |
This module adds a new data group to the Dynacard structure, and should be used to separate the data read from different cards. Even if only one card’s data is being read, the “Card1” group should still be added before running any “GetValue” modules.
Reads values from memory and adds them to the Dynacard structure
Parameters:
1 | VarLabel | Label for the variable in the Dynacard structure |
2 | StartRegister | Starting memory register to read (note that these register numbers will match those in the controller's Modbus map) |
3 | FormatType | Data type that is being read - see list of acceptable data types below. |
4 | UnscaledMin | Unscaled minimum value - used to scale the value we're reading |
5 | UnscaledMax | Unscaled maximum value - used to scale the value we're reading |
6 | ScaledMin | Scaled minimum value - used to scale the value we're reading |
7 | ScaledMax | Scaled maximum value - used to scale the value we're reading |
8 | ReverseRegOrder | Set to 1 to read the registers in reverse order (may be necessary for some controllers) |
9 | IsTimeStamp | Set to 1 if we are reading a timestamp |
The VarLabel should match one of those listed in The Dynacard Structure table at the beginning of this article.
This module reads the registers in memory (see: ReadValues) and interprets them as data values. You can think of this as similar to a standard driver’s read of an RTU, only we’re reading from internal VTScada memory instead of from the pump-off controller directly. The memory registers are mapped the same as those in the pump-off controller, and you can use the parameters to control everything that you would in a standard driver’s read (data type, scaling, reverse order, etc.)
One of the following constants should be used for the FormatType parameter:
Constant | Data Type | Bytes |
---|---|---|
0 | Short Integer | 2 |
1 | Long Integer | 4 |
2 | Unsigned Short Integer | 2 |
3 | Unsigned Long Integer | 4 |
4 | Single Precision Floating Point | 4 |
5 | Double Precision Floating Point | 8 |
6 | Single Byte (High) | 1 |
7 | Single Byte (Low) | 1 |
GetValue_XYCardData
Reads force / position values from memory and adds them to the Dynacard structure.
Parameters
1 | VarLabel | Label for the variable in the Dynacard structure (should be either CardXY or DHCardXY) |
2 | StartRegister | Starting memory register to read (note that these register numbers will match those in the controller's Modbus map) |
3 | FormatType | Data type that is being read - see list of acceptable data types in the GetValue_Numeric section. |
4 | NumPairs | Number of X-Y pairs to read |
5 | UnscaledMinX | Unscaled minimum value - used to scale the value we're reading |
6 | UnscaledMaxX | Unscaled maximum value - used to scale the value we're reading |
7 | ScaledMinX | Scaled minimum value - used to scale the value we're reading |
8 | ScaledMaxX | Scaled maximum value - used to scale the value we're reading |
9 | UnscaledMinY | Unscaled minimum value - used to scale the value we're reading |
10 | UnscaledMaxY | Unscaled maximum value - used to scale the value we're reading |
11 | ScaledMinY | Scaled minimum value - used to scale the value we're reading |
12 | ScaledMaxY | Scaled maximum value - used to scale the value we're reading |
This module reads the position/load registers in memory (see: ReadValues) and interprets them as data values which are later used to create the Dynacard graph. Beginning at the start register, the FormatType (see the GetValue_Numeric section) and the NumPairs variables combine to tell the sequencer how many registers should be read.
This module assumes that X and Y values in the device are interleaved within a single block of registers:
[X1, Y1, X2, Y2, …, Xn, Yn]
GetValue_XYCardData_Blocks
Reads force / position values from memory and adds them to the Dynacard structure.
Parameters
1 | VarLabel | Label for the variable in the Dynacard structure (should be either CardXY or DHCardXY) |
2 | StartRegisterX | Starting memory register to read (note that these register numbers will match those in the controller's Modbus map) |
3 | StartRegisterY | Starting memory register to read (note that these register numbers will match those in the controller's Modbus map) |
4 | FormatTypeX | Data type that is being read - see list of acceptable data types in the GetValue_Numeric section. |
5 | FormatTypeY | Data type that is being read - see list of acceptable data types in the GetValue_Numeric section. |
6 | NumPairs | Number of X-Y pairs to read |
7 | UnscaledMinX | Unscaled minimum value - used to scale the value we're reading |
8 | UnscaledMaxX | Unscaled maximum value - used to scale the value we're reading |
9 | ScaledMinX | Scaled minimum value - used to scale the value we're reading |
10 | ScaledMaxX | Scaled maximum value - used to scale the value we're reading |
11 | UnscaledMinY | Unscaled minimum value - used to scale the value we're reading |
12 | UnscaledMaxY | Unscaled maximum value - used to scale the value we're reading |
13 | ScaledMinY | Scaled minimum value - used to scale the value we're reading |
14 | ScaledMaxY | Scaled maximum value - used to scale the value we're reading |
This module reads the position/load registers in memory (see: ReadValues) and interprets them as data values which are later used to create the Dynacard graph. Beginning at the start register, the FormatType (see the GetValue_Numeric section) and the NumPairs variables combine to tell the sequencer how many registers should be read.
This module assumes that X and Y values in the device are separated into two distinct blocks of registers:
[X1, X2, …, Xn] and [Y1, Y2, …, Yn]
WriteValue
Writes a value to a register in the pump-off controller.
Parameters
1 | Address | Address of the register in the pump-off controller to write to |
2 | Value | Value to write |
This module writes a single value to a register in the pump-off controller. The address should be written as though you were performing a standard write with the child Modbus driver. If you wish to write a floating point to register 40001, for example, then the address should be “40001/FLOAT”.
ResetValues
Clears the memory holding the values previously read from the RTU
Parameters
-None
This module clears the memory that holds the values that had previously been read from the RTU by the ReadValues module. Usually this module is not necessary, but you may need to run it in cases where the same registers in the pump-off controller are loaded with different information over the course of the read sequence.
SetNextStep
Sets the next sequence step to be run.
Parameters
1 | StepName | Name of the step to run next |
Usually, the steps of the sequence are run sequentially from top to bottom. This module is used to change that order, specifying the name of the next step to be run (as defined in the first column in the sequence CSV file). After that step runs, the steps will continue to run sequentially from that new point.
CondNextStep
Sets the next sequence step to be run based on a condition being true.
Parameters
1 | CompValue | Main value to compare with the others |
2 | DataGroup | DataGroup from the Dynacard structure (optional) |
3 | CondCode | Condition comparison code (see notes below) |
4 | Comparison1 | Value to compare with CompValue (see notes below) |
5 | Step1 | Sequence step to run next if comparison is true |
6 | DataGroup1 | DataGroup corresponding to Comparison1 (optional) |
7 | Comparison2 | Value to compare with CompValue (see notes below) |
8 | Step2 | Sequence step to run next if comparison is true |
9 | DataGroup2 | DataGroup corresponding to Comparison2 (optional) |
10 | Comparison3 | Value to compare with CompValue (see notes below) |
11 | Step3 | Sequence step to run next if comparison is true |
12 | DataGroup3 | DataGroup corresponding to Comparison3 (optional) |
13 | Comparison4 | Value to compare with CompValue (see notes below) |
14 | Step4 | Sequence step to run next if comparison is true |
15 | DataGroup4 | DataGroup corresponding to Comparison4 (optional) |
16 | Comparison5 | Value to compare with CompValue (see notes below) |
17 | Step5 | Sequence step to run next if comparison is true |
18 | DataGroup5 | DataGroup corresponding to Comparison5 (optional) |
Usually, the steps of the sequence are run sequentially from top to bottom. This module is used to change that order, specifying the name of the next step to be run (as defined in the first column in the sequence CSV file) provided a certain condition is met. After that step runs, the steps will continue to run sequentially from that new point.
As an example, this module is used in the Lufkin SAM pump controller to decide how many registers to read. First we read the control mode, and then if it is either 1 (“Downhole”) or 4 (“VFD – Downhole”) then we read the downhole registers in addition to the standard registers.
The CompValue parameter is the value that will be compared against each of the other Comparison parameters. Each of these parameters can be either of the following:
- A value that had previously been saved in the Dynacard structure
- A constant
For example, in the Lufkin SAM case mentioned above we had previously read the control mode with a Get-Value_Numeric, giving it the label “ControlMode”. When doing the comparison to see if it is either a 1 or a 4, we would set the CompValue parameter to “ControlMode”, the Comparison1 parameter to “1” and the Comparison2 parameter to “4”.
If the CompValue or Comparison parameters are values from the Dynacard structure, the DataGroup parameters specify which DataGroup the values reside in within the structure. If no DataGroup parameters are provided, the current DataGroup will be used.
The various Step parameters indicate which step of the sequence to progress to in the event that the comparison is true. The comparisons will progress sequentially until one of them is found to be true, in which case that step will be used and the rest of the comparisons won’t be made. If none of the comparisons is true, the sequencer carries on as usual to the next sequential step.
The CondCode parameter tells the system what kind of comparison should be made. The following chart shows the numeric codes that represent the different options.
CondCode | Operation |
---|---|
0 | == |
1 | != |
2 | < |
3 | > |
4 | <= |
5 | >= |
The same CondCode is used for all comparisons.
CounterLimit
Sets the next sequence step or returns an error message when this module has been run a predetermined number of times.
Parameters
1 | VarName | (Optional) Name of counter variable |
2 | CountLimit | Number of times this should be run before triggering the next step/the error message |
3 | StepName | Sequence step to run next |
4 | CustomErrMessage | Error message to return |
This module can be used to repeat a section of the sequence several times. A tally is kept of each time this module is run. Once it has run a number of times equal to or greater than its CountLimit, the module will trigger. If the module has not yet reached its count limit, the sequencer will carry on to the next module as usual.
If StepName is valid, the sequencer will progress to that state upon reaching the limit. If the StepName field has been left blank, an error will be returned and the entire sequencer will stop (declaring the read to be a failure). In this latter case, CustomErrMess can be used to set the error message that will be displayed by the driver’s ‘Stats’ button.
VarName can usually be ignored, and is only necessary if you need to maintain two separate counters within the same sequence. In this case, each counter should be given a unique VarName.
CounterReset
Resets the counter used in the CounterLimit module
Parameters
1 | VarName | Optional) Name of counter variable |
This module is used in conjunction with the CounterLimit module defined above. If CounterLimit is going to be used for a given VarName (or lack thereof) multiple times within the same sequence, it can be reset to 0 by calling the Coun-terReset function with that VarName.
TimeDelay
Wait a specified number of seconds before progressing to the next module
Parameters
1 | Delay |
Time in seconds |
EndSequence
Ends the sequence
Parameters
-None