-
Notifications
You must be signed in to change notification settings - Fork 22
Writing Scripts
Scripts are the python code that drive the temporal logic for the experiments. They are a set of the high-level step-by-step instructions that call on test script (ts) object, abstraction layers, and other functions to execute the experiment.
Scripts have several critical structural components that can largely be studied and duplicated from ([an extensively commented script] insert link to commented script). The primary SVP python package used in the SVP to assit with the execution logic, results manuscript generation, and GUI interaction is ([script.py]insert link to new script.py location). It is instantiated in the main of the Script using:
test_script = script.Script(info=script_info(), config_file=config_file, params=params)Test script is then passed to run(test_script) which logs the parameters for the test, runs the test result = test_run(), and generates the results. test_script or ts is always transfered to every part of the code in order to store parameter changes.
For 95% of cases, copying another script and modifying the parameters and test_run() will be sufficient for new users.
Depending on the needs of the script, different elements may be incorporated through the abstraction layers. Import the components that are needed like this:
from svpelab import das # Data acquisition system
from svpelab import der # DER communication modules
from svpelab import hil # Hardware-in-the-loop tools
from svpelab import pvsim # PV simulator
from svpelab import gridsim # Grid (AC) simulator
from svpelab import loadsim # Loadbanks or simulatorsSeveral short and simple examples of script logic is presented in the ([SVP Energy Lab] insert link to SVPelab scripts) with a heavily commented script which explains the different parts it is marked with a _commented in the name of the Script.
Some basic functionality includes:
- instantiating the device drivers
- logging information to the screen
- setting the test result
- producing a results manifest
Once the abstraction has been imported and GUI parameters set (see below), a device driver will be selected for the object being instantiated. This is done via the <abstraction>_init() method, such as the following:
chil = hil.hil_init(ts)
grid = gridsim.gridsim_init(ts)
daq = das.das_init(ts)It was found in several experiments--especially HIL testing--that a link to the HIL, PVSim, and GridSim objects were sometimes necessary for the other abstraction layers. These are passed as a dict in the support_interfaces parameter:
grid = gridsim.gridsim_init(ts, support_interfaces={'hil': chil})
pv = pvsim.pvsim_init(ts, support_interfaces={'hil': chil})The data acquisition abstraction layer includes a "soft channel" feature that's also used commonly. Please see ([DAS Abstraction]insert link to correct documentation or code) for more details on using that feature.
daq = das.das_init(ts, sc_points=das_points['sc'], support_interfaces={'hil': chil}) After this point, you can start interacting with the device methods. For example, you could configure the HIL environment with chil.config() or set the PV power using pv.power_set(p_rated).
The GUI (technically script.py) generates different GUI logs at the "Info (I)", "Error (E)", "Debug (D)", and "Warning (W)" levels. Messages can be displayed to the SVP operator with the following:
ts.log('This is an Information Message') # This is Black
ts.log_warning('This is a Warning Message') # This is Yellow
ts.log_debug('This is a Debug Message') # This is Purple
ts.log_error('This is an Error Message') # This is Red
ts.confirm('Display a popup prompt') # This is a confirmation promptBased on the pass/fail logic, the script should return a result equal to script.RESULT_PASS, script.RESULT_FAIL, or script.RESULT_COMPLETE. This will populate the icon next to the results in the GUI to indicate the result of the test.
The SVP includes the capability to add different data to the results manifest associated with the test. When running a test, this data will be placed in Results/<TimeStamp>__<script>__<test> and will have a directory structure similar to this:
<TimeStamp>__<script>__<test>
├── <script>__<test>
│ ├── <script>__<test>.log
│ ├── <test>.xlsx
│ └── <user_defined>.csv
└── <TimeStamp>__<script>__<test>.rlt
The .rlt file is an XML encoded document with all the manifest metadata:
<result name="2022-12-30_18-47-44-598__FW__FW_1_BELOW" type="result">
<params />
<results>
<result name="FW/FW_1_BELOW" type="test" status="Fail" filename="FW__FW_1_BELOW\FW__FW_1_BELOW.log">
<params />
<results>
<result name="result_summary.csv" type="file" filename="FW__FW_1_BELOW\result_summary.csv">
<params />
<results />
</result>
<result name="FW_1_BELOW.xlsx" type="file" filename="FW__FW_1_BELOW\FW_1_BELOW.xlsx">
<params />
<results />
</result>
</results>
</result>
</results>
</result>The .log file is a text file with the GUI output from the test.
2022-12-30 18:47:48.308 D
2022-12-30 18:47:48.308 D ************** Starting FW_1_BELOW **************
2022-12-30 18:47:48.308 D Script: FW.py 1.4.3
2022-12-30 18:47:48.308 I Test Parameters:
2022-12-30 18:47:48.308 I Frequency Watt mode (Above or Below nominal frequency) = Below
2022-12-30 18:47:48.308 I Characteristic 1 curve = Enabled
2022-12-30 18:47:48.308 I Response time (s) for curve 1 = 0.1
2022-12-30 18:47:48.308 I Characteristic 2 curve = Disabled
2022-12-30 18:47:48.318 I EUT Parameters:
2022-12-30 18:47:48.318 I Phases = Single phase
2022-12-30 18:47:48.318 I Apparent power rating (VA) = 10000.0
...
CSV files can be created using the data aquisition system datasets like this:
ds = daq.data_capture_dataset()
ts.log('Saving file: %s' % dataset_filename)
ds.to_csv(ts.result_file_path(dataset_filename))or more manually with test data like this:
result_summary = open(ts.result_file_path(result_summary_filename), 'a+')
ts.result_file(result_summary_filename)
result_summary.write('Some, Data, Goes, Here\n')
result_summary.close()Lastly, Excel sheets can be created using ([result.py] insert link). Here's an example of populating a summary Excel file based on all the .csv files in ts.results_dir():
from svpelab import result as rslt
excelfile = ts.config_name() + '.xlsx'
rslt.result_workbook(excelfile, ts.results_dir(), ts.result_dir())
ts.result_file(excelfile)These excel sheets can also have figures generated. See ResultWorkbook() in result.py:
ds = daq.data_capture_dataset()
ts.log('Saving file: %s' % rms_dataset_filename)
ds.to_csv(ts.result_file_path(rms_dataset_filename))
ds.remove_none_row(ts.result_file_path(rms_dataset_filename), "TIME")
result_params = {
'plot.title': rms_dataset_filename.split('.csv')[0],
'plot.x.title': 'Time (sec)',
'plot.x.points': 'TIME',
'plot.y.points': 'AC_VRMS_1, AC_VRMS_2, AC_VRMS_3',
'plot.y.title': 'Voltage (V)',
'plot.y2.points': 'AC_IRMS_1, AC_IRMS_2, AC_IRMS_3',
'plot.y2.title': 'Current (A)',
}
ts.result_file(rms_dataset_filename, params=result_params)
excelfile = ts.config_name() + '.xlsx'
rslt.result_workbook(excelfile, ts.results_dir(), ts.result_dir())
ts.result_file(excelfile)Note the use of dataset object, defined in ([dataset.py]insert link).
Parameters are fields presented to the user in the GUI. They include a name, description, default value, and either a text field or drop-down menu. There are parameters associated with the script and each abstraction layer/device driver. Please see the ([commented code]insert link) for a good example of how this is done.
Adding the parameters for the abstraction layers is done with:
der.params(info)
gridsim.params(info)
pvsim.params(info)
das.params(info)
hil.params(info)The additional script parameters are added in the following way:
info = script.ScriptInfo(name=os.path.basename(__file__), run=run, version='1.4.3')
# FW test parameters
info.param_group('fw', label='Test Parameters')
info.param('fw.mode', label='Frequency Watt mode (Above or Below nominal frequency)', default='Both',\
values=['Above', 'Below'])
info.param('fw.test_1', label='Characteristic 1 curve', default='Enabled', values=['Disabled', 'Enabled'])
info.param('fw.test_1_tr', label='Response time (s) for curve 1', default=10.0,active='fw.test_1',\
active_value=['Enabled'])
info.param('fw.test_2', label='Characteristic 2 curve', default='Enabled', values=['Disabled', 'Enabled'])
info.param('fw.test_2_tr', label='Response time (s) for curve 2', default=10.0, active='fw.test_2',\
active_value=['Enabled'])
info.param('fw.power_lvl', label='Power Levels', default='All', values=['100%', '66%', '20%', 'All'],\
active='fw.mode', active_value=['Above'])Here we can see several new parameters that include a name, label, and default value. The name is what is used to get the parameter value in the test_run() function. The label is displayed to the user. The default field is what is initially populated and it defines the type of value (str, float, int) so be careful with this selection. Additionally, some of the parameters include a list of values, e.g., values=['Disabled', 'Enabled']. These will be a drop-down menu for the user. Lastly, the active and active_value parameters define if the parameter is presented to the user. For example, the fw.test_2_tr parameter will only appear in the GUI if fw.test_2 is set to Enabled.
Taking this example, let's see how the GUI represents it:
To get the parameter into the test use value = ts.param_value('<group>.<name>'), like:
test_1_tr = ts.param_value('fw.test_1_tr')For more details on parameters, please refer to the source code in script.ScriptInfo().
You can create multiple instances of the objects using the format:
gridsim.params(info, '1', 'Grid Simulation 1')
gridsim.params(info, '2', 'Grid Simulation 2')and then initialize them with:
# initialize grid simulation
gsim_1 = gridsim.gridsim_init(ts, '1')
ts.log('Grid Simulation 1: %s' % gsim_1.info())
gsim_2 = gridsim.gridsim_init(ts, '2')
ts.log('Grid Simulation 2: %s' % gsim_2.info())