TestController, Configuration of a SCPI devices

FirstImage

Many SCPI devices can be configured without any real programming, "just" by writing a configuration file. All existing devices have a configuration file that can be looked in for examples.
Non SCPI devices have internal driver code and used the definitions as a supplement to this, multiple devices may share the same internal driver, but with different definition files.
Main Test controller page


Contents
    Overview
    The device identification and connection parametes
    GPIB settings
    Data type definitions
    Basic communication
        #initCmd
        #finalCmd
        #prepareSample
        #askMode
        #askValues
        #outputOff
    Fixing format and other issues
        readFormat
        mathFormat & readmath
        Timing issues
    Selecting mode (Mostly for multimeters)
    Configuration menu
        Tags
        Control number
        Control numberInt
        Control numberDual
        Control button
        Control buttons
        Control buttonsOn
        Control radio
        Control checkbox
        Control combobox
        Control comboboxHot
        Control info
        Control slider
        Control indicator
        Control indicatornum
        Control color
        Control selector
        Control updater
    A generic interface
        Definition of set...
        Definition of get...
        Definition of read...
        The #deviceType definition
    Miscellaneous tags
        #author
        #mayModifyMode
        #notes
    Creating multiple devices from one configuration file
        #meta
        #metadef
        #idstring, #name, #handle, #port, #driver, #baudrate, #subdriver
        #replace
        #remove
        #removeline
        #replacetext
        #sections
        #metaDebug
Main page
Functions for use in calculator and definitions


Overview

A device definition consist of different sections, some are mandatory before the device will work at all, other is nice to have, but not strictly required. The sections are not separated in the device configuration, but the identification section must be first for the definition always to work.

The sections are:
The 3 first sections must be implemented for the device to work at all. For bench multimeters a mode selection is usually required and for power supplies, electronic loads, and many other devices a configuration menu is very nice to have.
The interface is never required, but will make scripting much more flexible.

It is possible to create multiple similar devices from one configuration file, this has its own chaper.



The device identification and connection parametes

These must be placed first in the configuration file.

The start of the file is:
Code:
#idString first two parts of *idn? answer
#name The device name
#handle A short handle
#port The port used
#baudrate Baudrate for serial devices
#driver Internal driver to use, not used for SCPI
#help Help file to use
#eol Used to change the standard LF end of line to CR or CRLF.

Two real examples:
Code:
#idString Keysight Technologies,34465A,
#name Keysight 34465A
#handle K34465
#port 5025
Code:
#idString HEWLETT-PACKARD,34401A,
#name Agilent HP34401A
#handle HP34401
#port com
#baudrate 9600

The idstring is the brand name and the device name, the two last parameters must not be included.
The name is the name used in TestController for the device

The port can contain one or more of off:
   Number: The port number for a socket connection, this is usually hidden somewhere in the documentation for the device.
   LXI: LXI connection (Avoid this if possible, I have only tested it on one device).
   com: Serial port with baudrate setting on the "Load Devices" page.
   comFixedBaud: Serial port with fixed baudrate
   comNoBaud: A virtual serial port that do not use baudrate.
   GPIB: A IEEE488/GPIB port, this requires the use of adapter, the adapter type is not related to the device configuration.
When multiple ports are listed it is possible to select between them with a combobox on the "Load devices" page.
For a modern device with multiple connections ports it could be something like:
Code:
#port number LXI com GPIB
Both number and LXI uses a network connection, com can be a physical RS232 port or a virtual USB com port, GPIB requires the use of a supported IEEE488/GPIB adapter.

The baudrate can be any baudrate, but can also contain more parameters line: 19200O71Dr
The D or d sets DTR high or low and R or r sets RTS high or low.

For eol a string is used, to write control character escapes must be used, use: /r for CR and /r/n for CRLF



GPIB settings

The GBIP port has some settings that can be adjusted, these settings will track the device, i.e. multiple devices on the same GPIB adapter may use different settings:

Code:
#gpibReadEol eoi
#gpibReadEol characterNumber
#gpibWriteDelay timeInMs
#gpibWriteReadDelay timeInMs

The #gpibReadEol defines how the device terminiates its answers, the default uses eoi. Specifying "#gpibReadEol 10" would expect a LF character for terminating a answer.

#gpibWriteDelay will add a small delay after each GPIB write, this can slow down the processing for old slow devices.

#gpibWriteReadDelay minimum time before trying to read a answer from a device. If the GPIB adapter is connected to multiple devices, this time may be used for communicating with other devices.



Data type definitions

This defines what data the device returns that must be shown on "Current values" and logged.
It will be one or more lines, each defining one data type, not all needs to be used at the same time or even used at all (The last part is when making definitions that cover multiple devices).

Code:
#value name unit format {selector}

A typical definition for a power supply:
Code:
#value Voltage V D3 
#value Current A D3 
#value VoltageSet V D3 
#value CurrentSet A D3 

A typical definition for a bench multimeter, only one is used at a time.
Code:
#value VoltageDC V d6 VDC,d
#value CurrentDC A si ADC
#value VoltageAC V d6 VAC
#value CurrentAC A si AAC
#value Resistance ohm si ohm,ohm4
#value Frequency Hz si hz
#value Ratio - d3 Ratio
#value Period s d6 Period

The name will be used for labeling the value and also for creating variables. It must be one word that starts with a letter and only contains letters, numbers or _.

The unit is shown at various places to help identify what the value is, for measurements without a unit use -.

The format uses a predefined numeric formats to show the value, the format used in a CSV file is not the same format as used on the screen, but will be with higher precision.
D0..D6 is floating point with 0 to 6 decimals.
X8 is floating point with up to 8 decimals
INT is integer format and is similar to D0, except when saving.
SI will use SI prefix, i.e. Mkmu etc. after the number and will show from 0 to 7 decimals.
DIGITAL(B0,B1,B2,...) This switches the value to digital mode, this means all values are converted to integer and handled as digital bits. The parameters are names for each bit, use - to skip a bit. It is recommended to use short names (2 or 3 characters) for the bits.

The selector is used together with #askMode and #cmdMode, it is a list of device modes that must either match the mode name from #cmdMode or the result returned from #askMode. Multiple modes can be listed for each column, use a comma as delimiter.



Basic communication

This is the SCPI commands used to read and control the device. The SCPI commands can be listed after the tag with ; between them and/or on the following lines, until the next # tag.
Any command with a ? will be assumed to return an answer. A single line may contain multiple commands that each returns answer.
If a delay is needed between the SCPI commands use [delay].

Code:
#initCmd SCPI commands
#finalCmd SCPI commands
#prepareSample SCPI commands
#askMode SCPI commands
#askValues SCPI commands
#outputOff SCPI commands

Some examples, the different lines are not from the same device:
Code:
#askValues V1O?;I1O?;V1?;I1?
#askValues read?
#askValues :read? "defbuffer1",read
#initCmd :SYSTEM:REMOTE;[500]
#finalCmd abort;*cls;*rst;init;system:local
#prepareSample abort;sample:count 1;trig:source imm;trig:count inf;init


#initCmd

This command is used once after the device is connected, it is typically used to switch the user interface to remote control. This is not always required.



#finalCmd

This command is used once just before the connection is closed, it is typically used to switch the user interface to local control, turn power supplies off and set voltage and current to low values, turn electronic loads off and turn generators off.  This is not always required.



#prepareSample

This is used before reading from the device and after each mode change, this is used to setup trigger mode. This is not always required.



#askMode

Returns the current mode, this is used to select the data types to expect when reading from the device. It must match either either directly with the select from #value or with the mode from #cmdMode
The value from the meter may not always work directly, see next chapter for ways to modify the results.



#askValues

This is the SCPI string to return all data for "Current values" and logging, it can contain more command with ? in them, but it will limit the fastest logging rate. All values must be numeric values.
The input string is split into segments on all spaces, commas and semicolons and each segment is assumed to be a value.
The values from the meter may not always work directly, see next chapter for ways to modify the results.



#outputOff

Turn power supplies, electronic loads and generator output off. This definition is used when the "Outputs off" button is pressed.



Fixing format and other issues

When asking for values not all devices return just the value, some will add something before or after the value, this will prevent the #askValues & #askModes from working directly, the result must be adjusted a bit.

Code:
#askValuesReadFormat a list of what value to use and what value to remove
#askValuesMathFormat  expression, input is called value
#askModeMathFormat  expression, input is called value
#readingDelay time in seconds
#modeChangeDelay time in seconds



readFormat

This is a simple way to edit the returned values while converting the data to numbers. It consist of a list of characters, each with a specific meaning: The input is processes by readmath and split on , and space before using this function.



mathFormat & readmath

This makes it possible to run a script on the returned string, before any other processing is done. The original input string is called value.
A simple script may be to remove quotes around the value:

Code:
#askModeMathFormat unQuote(value);

See a list of useful functions at the end of this document.
When using any type of readmath it may be a good idea to enable debugging. Use the command "#debug handle" where handle is the handle for the device you are working on. This will list a couple of items. Each item can be enabled with "#debug handle item".



Timing issues

There are a couple of ways to handle timing issues, each has advantages and disadvantages.

Use a [*OPC] tag after the SCPI command will delay until that command is done, this can be a good solution when doing different types of mode changes, but may block the communication if used after a trigger command. The [*OPC] tag will not wait more than 10 second. This function uses the *OPC SCPI command followed by some *ESR? commands, when logging only the actual delay will be shown.

Use a [delay] tag after a SCPI command, the maximum delay is [9999]. This will give the device a fixed time to process the command, before more communication is attempted. This is a last resort solution, it is better to use [*OPC].

Example:
Code:
:OUTP:STAT ON;[*OPC]
:OUTP:STAT ON;[500]

Another way to handle a slow device is to increase the timeout. The default timeout is 1 second, this may not always be enough, there are two settings for this:

#readingDelay is used to allow more time for reading values from the device
#modeChangeDelay give the device time to change mode, a value of 10 (seconds) will usual be fine.




Selecting mode (Mostly for multimeters)

Some devices can run in different modes, that may mean they will return different values. To control this a mode change menu need to be defined. These commands cannot be used while logging, because they need to change what columns of data the device returns.

PopupModeChange

It may look like the above.

Code:
#cmdMode modeLabel deviceModeString SCPI commands

Each mode (button) needs a definition consisting of:

A example for one mode:
Code:
#cmdMode ADC CURR:DC
*cls;
:SENSE:FUNCTION:ON "CURR:DC"
sense:curr:dc:nplc 1

All defined modes are shown in a popup window, where they are sorted in alphabetically order. It is possible to take over definition of this window by defining the window size:

Code:
#cmdModeLayout columns row

It is also possible to add empty positions with:
Code:
#cmdMode





Configuration menu

This is a popup form that allows setting parameters on the device. This part of the definition file can be the most complicated, depending on how much is included in the setup form.
Parameters cannot include spaces, but a underscore will be replace with a space when displaying a text.

popupSetupExmaple1
popupSetupExmaple2
popupSetupExmaple3

Above is a fairly complicated setup, but it do not support all functions on the device, only the most common.

Code:
#cmdSetup controlType name {page}
tags and parameters on the following lines

The definition looks simple enough. I will explain all the types and tags below, the name is the name shown to the left, it is possible to give multiple controls the same name. With no page defined the controls will be shown without any pages, but if a page is defined the menu will have a page for each name and controls without a page will be shown below the pages.
Generally each #cmdSetup will use one line on the form, but a few will combine on the same line if they have the same name. The form will use a grid with as many lines and columns as needed. Most elements in a control needs one column in the grid, but some elements may expand to more columns on grids with many columns.
A few controls do not directly add lines, but have other functions, they are: color, selector and updater
Most of the controls will synchronize with the device when the form is opened and when used, with a update tag it is also possible to make them synchronize when other controls are used. All controls with the same name will synchronize together.



Tags

Each item will have one or more tags with or without parameters.
*Some controls will create variables with their name or pageName.name if pages are used, they are:
Number, NumberInt, NumberDual (Adds 1 & 2 to the names), buttonsOn, radio, combobox


Example:
Code:
:enable: Config==0
:enable: !Output_2.Output
:visible: inList(Output_1.Waveform,"SQUARE PULSE");


Control number

number

A single number, it uses a :read: to show the current setting in the box and a :write: to change the setting when the button is pressed. The button will be labeled "Set" but this can be changed with the :buttontext: tag.
After the tags there must be one configuration line with "unit min max"
It is possible to have one value outside the min-max range by using the :emptyvalue: tag. If a special SCPI command is needed to set this up use :emptywrite: with the command.

Code:
#cmdSetup number downTime
:read: downTime?
:write: downTime
:update: Frequency,Period
:tip: Time output is low in each period
sec 0 3600


Control numberInt

number

The same as number, but will not accept decimal numbers.



Control numberDual

numberdual

This control uses two input boxes on the same line, it uses the same tags as number.
After the tags there must be two lines each with four parameters: "unit min max name"

Code:
#cmdSetup numberDual Limits_1
:read: limits? 1
:write: limits 1
:tip: Limits, when output is outside an error is raised (Display shows RANGE)
C -2048 2047.94 Low
C -2048 2047.94 High



Control button

button

A single wide button, there will be no name to the left, the button will use the full width.

Code:
#cmdSetup button Clear_protection Limits
:write: clearProtection
:tip: Clear current active protection (Display shows OCP or OPP)


Control buttons

buttons

One or more buttons on a line, these buttons will always have the name to the left. The command need a :write: tag and a line with two parameters for each button: "ButtonText SCPIParam"

Code:
#cmdSetup buttons RemoteSense
:write: SENSE1
:tip: Use sense wires to get exact voltage at target
Off 0
On 1


Control buttonsOn
buttonson

One or more buttons on a line with a indicator last on the line. A :read: tag is needed to get the status for the indicator, it will be on if the read value matches second parameter line. The rest is similar to buttons.
The read result is supposed to be a numeric result, use a :string: tag for a non-numeric result.

Code:
#cmdSetup buttonsOn Output
:read: OP1?
:write: TRIPRST;*cls;OP1
:tip: Turn output on or off
:updatealloff:
Off 0
On 1


Control radio

radio

A list of radio buttons that will send the setting when selected. A :read: tag is needed to select the current setting, if the returned value do not match any setting, none will be selected (It is valid to do this). The :write: tag will send the the command when a radio button is selected. For each radio button a line with two parameters are needed: "Text SCPIParam"
The read result is supposed to be a numeric result, use a :string: tag for a non-numeric result.

Code:
#cmdSetup radio Voltage
:read: V1?;
:readFormat: xf
:write: V1 #; OP1 1
:tip: Setup this voltage, turn output on
3.3V 3.3
5V 5.0
12V 12.0
24V 24.0
60V 60.0


Control checkbox

checkbox

A single checkbox that will set/reset a setting when checked/unchecked. Multiple checkboxes with the same name will be grouped on a single line.
Both the :read: and :write: tag is needed for this control. A single line with 3 parameters is needed: "Text uncheckedValue checkedValue".
The read result is supposed to be a numeric result, use a :string: tag for a non-numeric result.

Code:
#cmdSetup checkbox Remote_sense Main
:read: sense?
:write: sense
:update: Status
on 0 1


Control combobox

combo

A combobox that can be used to select between different non-numeric items. It needs both :read: and :write: tags and a line for each item in the checkbox with two parameters: "Text SCPIparam".

Code:
#cmdSetup combobox Waveform Output_1
:write: c1:BSWV WVTP,# 
:read: c1:BSWV?
:readmath: getElement(value,1,",")
:update: Frequency Periode Amplitude Offset Duty_cycle Symmetry Arbitrary
:updatedelayed: 0.1 
Sine SINE
Square SQUARE
Ramp RAMP
Pulse PULSE
Noise NOISE
Arbitrary ARB
DC DC



Control comboboxHot

comboxhot

This is similar to a combobox but will change the setting immediately when used and not wait for a press on a "Set" button. The configuration is the same.

Code:
#cmdSetup comboboxhot Sensor_1
:write: sensor 1
:read: sensor? 1
Type_B B
Type_E E
Type_J J
Type_K K
Type_N N
Type_R R
Type_S S
Type_T T
Voltage_1_(x8) x8
Voltage_2_(x32) x32


Control info

info

This is used to display information, multiple items with the same name will be combined on one line.
It needs a :read: tag and two lines each with one parameter, first line is unit, second line is text. To be updated other controls may need to list this control in their update tag.

Code:
#cmdSetup info Wave
:read: freq?
Hz
Frequency


Control slider

slider

A slider that can be used for coarse adjustment of integer values. If much more than 10 steps or precise knowledge of the value is needed a numberInt control is probably better.
It need :read: and :write: tags and a single line with 3 parameters: "Text min max"

Code:
#cmdSetup slider Display_brightness
:read: BRIGHTNESS?
:write: BRIGHTNESS
:tip: 0=off, 1=Minimal brightness, 16=Maximal brightness
_ 0 16


Control indicator

indicator

indicator2

A indicator lamp, can be used to show the status of some functions (Typically output on/off). More indicators with same name will be grouped on one line.
It needs a :read: and one or more lines with 3 parameters each: "Text expression color".
The first line will be select when no match is found and the color will be in a off (low intensity) state. When a match is found the color will be shown at full intensity. The text will be shown next to the indicator.
The value returned from read is named "value" and the expression must not contain any spaces.

Code:
#cmdSetup indicator Mode
:read: mode?
Active 1 blue
Idle getElement(value,0)=="IDLE" yellow
Discharge getElement(value,0)=="DISC" red
Pulsing getElement(value,0)=="PULSE" magenta


Control indicatornum

indicator

This is the same as indicator, but instead of an expression it will match on a number. When grouping indicators on a line this will be grouped together with indicator, as long as the name match.

Code:
#cmdSetup indicatornum Status Main
:read: on?
:updatealloff:
Off 99 red
On 1 Red



Control color

color

This control do not add a line to the setup, but adds a colored border to the setup form, it will also color the tab for multipage setups. It can be used for each page in a multipage control to match devices with color coded channels.
Like all #cmdSetup specifications it requires a name, it is never shown and can be a dash.
The color is either a name or (r,g,b) format. To find the rgb values a drawing program with a good color selector can be used.
It is also possible to use a :bgcolor: tag, this will change the background color for the page, but it is not recommended.

Code:
#cmdSetup color - Output_2
(250,140,20)


Control selector

By itself it is an invisible control, but it can control visibility of other controls. Invisible controls will not be updated, this means no slow update, even with many hidden controls. The controls can be enabled either based on a SCPI command or based on another control.
The first line with controls will be used for previews and when the value is empty.

Code:
#cmdSetup selector name {page}
expression
value1 control {control {control {control {control ...}}}}
value2 control {control {control {control {control ...}}}}
...

Code:
#cmdSetup selector name {page}
:read: SCPI command
value1 control {control {control {control {control ...}}}}
value2 control {control {control {control {control ...}}}}
...

The "control" is the name of a control, when pages are used use page.name or page. for all controls on that page (The page will not be created then, because all controls are inside the selector).
It is possible to include the same control on more than one selector value.
:update: & :enable: tags on the selector will affect all selected controls.
:update: & :enable: on the individual controls will only be active when the control is selected.



Control updater

This is an invisible control that is used to update other controls at regular intervals, generally it is not recommended to use, except for devices that do not disable local control.
This control only use a :update: tag that must list the controls to update and a single parameter line with one value that is the time in seconds between automatic updates.

Code:
#cmdSetup Updater update
:update:Out1 Out2 Out3 Out4 Out
0.5




A generic interface

This is a interface to use from scripting that is supposed to be mostly independent of selected device, making it easy to swap between different devices of the same type in a test setup. It can be used after an = sign or in expressions. There is a special test form in the "Popups" menu.

There are 3 group of commands:
The ... is what to set/get/read, it will typically be:
The call format for a function is one of:
Code:
setPower(device,value)
getPower(device)
readPower(device)

Where device is a string with the device handle and optionally a ":channel" in the same string. Channel is used for multichannel devices and defines what channel to use, when not included 1 is assumed. Value is the desired value and is usually a number (Waveform and Mode are strings).

Code:
=readVoltage("QPX1200")

In log window:
;; 11.995
Code:
=readAmplitude("sdg2122x:2")

In log window:
;; 2.0


Definition of set...

Code:
#interface set... SCPICommand (value)
#interface set... SCPICommand (channel) (value)

The (value) and (channel) can contain expressions when needed.

Code:
#interface setVoltage voltage (value)
#interface setFrequency C(channel):BSWV FRQ,(value)
#interface setOn c(channel):outp (getElement("off on",value," "))


Definition of get...

Code:
#interface get... SCPICommand 
#interface get... SCPICommand (channel) 
:readmath:
:readformat:

This function gets a setting from the device. It is possible to use :readmath: and :readformat: to get the value into a acceptable format. In a few cases a :string: tag can be needed to allow string handling and returning a string value.

Code:
#interface getVoltage voltage?

#interface getAmplitude c(channel):BSWV?
:readmath: getElement(getMatch(value,"AMP,[^,]*,"),1,",")
:readformat: u


Definition of read...

Code:
#interface read... column
#interface read... column column...

The definition of read is very easy, because it uses the same reading as "current values" and logging, i.e. the "#askValues" command and only the actual index of the value must be specified. For multichannel devices multiple indexes must be listed, one for each channel. The first value the device returns is numbered 0.



The #deviceType definition

In addition to the actual interface calls it is also possible to assign a type to the device, this is used as a way to get a generic device from a script. This work likes this:
Code:
=var ps=getDevice("PS");
=setVoltage(ps,10);
=setCurrent(ps,1);
This means find the first power supply on the loaded device list and assigns its handle to ps. Now the different interface function can be used to setup parameters. Use getDevice("+PS") to get the second power supply or getDevice("PS:3") to get the 3 channel of the first power supply with 3 or more channels.

For this to work each device must have a #device type listing the possible types support be this device:
Code:
#deviceType PS
#deviceType PS PS:2 PS:3
First line is for a single channel power supply, second line is for a multichannel  power supply. A device may list more than one device type.

Device types: More will be added over time.



Miscellaneous tags

#author

This tag is used to add information to the about popup. All text after the tag is place on that popup, together with the name of the driver it is in.
It is a way to get credit for defining definitions for devices.


#mayModifyMode

Each tag list the start of a command, if there is a match to any command typed from the Command screen the current column layout is invalidated and a new one will be created using the askmode function.


#notes

With tag will add a small "view" button to the device list and pressing this button will show any text after #notes until the next # tag
This can be used to write about required settings on the device, limitations in the device definition, etc.



Creating multiple devices from one configuration file

With similar devices it is possible to make many device definitions from one configuration file.



#meta

Mark a definition to not be include in the device list, but only used as the basic for other definitions. This type of definition is referred to by their filename (Without path and extension).



#metadef

Create a definition based on another definition, either a real definition or a #meta definition. It is possible to replace some tags, change parameters and remove tags, but it is not possible to add new tags. The original definition must contain everything needed.

Code:
#metadef
#metadef filename without path and extension
#metadef device name

The #metadef tag can be used 3 ways, without a name means it will expect a #meta definition in the same file and then with either the filename of a file with a #meta tag or the device name of a existing device configuration.

After the #metadef there will often be a couple of lines to modify the definition, but for definition in the same file there may be one #metadef without any modification instruction.



#idstring, #name, #handle, #port, #driver, #baudrate, #subdriver

These tag after a #metadef will replace the corresponding tag in the original definition.

Code:
#metadef
#idString ITECH Ltd, IT8511A,  
#name Itech IT8511A+
#handle IT8511A



#replace

Code:
#replace #tag params
...

Replace all settings between a tag and the following tag.

Code:
#replace #cmdSetup indicator State
:read: test?
off 0 red
on 1 green



#remove

Remove section, both line with #tag and all lines until the next #tag

Code:
#remove #cmdSetup indicator State



#removeline

Remove any line that starts with the text after the tag.

Code:
#removeLine 60A
#removeLine 120A
#removeLine 240A



#replacetext

Replaces the first word after the tag with the second word after the text. This makes it possible to adjust limits and other values in the original definition.

Code:
#replaceText MinVoltage 0.1
#replaceText MinResistance 0.05
#replaceText MaxCurrent 30
#replaceText MaxVoltage 150
#replaceText MaxResistance 7500
#replaceText MaxPower 150



#sections

This tag is used together with #metaSection in the actual definition to include or exclude larger blocks of lines. Only one #sections tag is used in each #metadef section.


Code:
#sections
#sections dual
#sections dual 20A
The above are examples of section specifications in the #metadef part

Code:
#metaSection dual

#metaSection dual 20A

#metaSection 
In the actual definition the #metaSection tag is used. The first one will be include in any #metadef/#sections that specifies dual, the second one will only be include for #metadef/#sections that both specify dual and 20A. The last is always included and is used to end a conditional (#metaSection) section.

Note: #metaSection is only valid in #meta definitions, a normal definition will always fail to load when containing a #metaSection tag.



#metaDebug

This is a debug tag that can be placed on the line after a #metadef tag, this will list all performed modification instructions on the console output (This requires starting testController from the command line).

Code:
#metaDebug
#metaDebug save
Adding a save tag after the #metaDebug will save a file with the processed definition