Skip to content

Commit

Permalink
update CTF v1.8.2 release
Browse files Browse the repository at this point in the history
  • Loading branch information
blueoceanwater committed Jan 9, 2024
1 parent 74885e5 commit f421f1b
Show file tree
Hide file tree
Showing 22 changed files with 569 additions and 107 deletions.
33 changes: 27 additions & 6 deletions ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,32 @@ in the `CTF Documentation` directory of the CTF releases' Assets (https://github

## Release Notes

### v1.8.2
11/20/2023

* CTF Core Changes

* Add an optional logging configuration `csv_tlm_log`. If True, CTF creates an additional tlm CSV log file.

* Minor improvements and bug fixes.

* CTF Plugins Changes

* Updates to CFS Plugin
* Display full data attributes of nested structures in tlm logs.

* Resolve user-defined variables for all test instructions.

* Fix a regression with shutdown and restart of CFS targets in the same test script.

* Change CFS shutdown behavior to only shut down targets automatically if the target was started by `StartCfs`.

### v1.8.1
09/15/2023

* CTF Plugins Changes
* Fix a regression in the CFS Plugin that caused CTF to not communicate with CFS targets which were not started via `StartCfs`.

### v1.8
09/27/2023
* CTF Core Changes
Expand Down Expand Up @@ -49,12 +75,7 @@ in the `CTF Documentation` directory of the CTF releases' Assets (https://github
* Fix CCSDS v1 message header issue.

* Fix a regression issue of CCDD JSON reader not reusing known data types referenced by name.

* Updates to Gateway Plugin
* Improve `SendCfsCommandWithAggregatedPayload` instruction to allow different targets in payload, and its syntax.

* Fix inner command header attribute assignment issue of test instruction.



* CTF Tool and Scripts Changes
* Consolidate unit tests for open source release and internal release.
Expand Down
4 changes: 2 additions & 2 deletions ctf
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ from lib.ctf_utility import expand_path
from lib.logger import logger as log, set_logger_options_from_config, change_log_file
from lib.exceptions import CtfTestError

CTF_VERSION = "v1.8"
CTF_RELEASE_DATE = "Sep 27 2023"
CTF_VERSION = "v1.8.2"
CTF_RELEASE_DATE = "Nov 20 2023"


def main():
Expand Down
65 changes: 65 additions & 0 deletions example_scripts/config_required/Test_CTF_CFS_Restart.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"test_script_number": "CTF-CFS-Restart-Test",
"test_script_name": "Test_CTF_CFS_Restart.json",
"owner": "CTF",
"description": "CTF Example Script showing use of a CFS target after shutdown and restart",
"requirements": {
"MyRequirement": "N/A"
},
"ctf_options": {
"verify_timeout": 4
},
"test_setup": "",
"import": {},
"tests": [
{
"test_number": "CFS-Restart-Test-1",
"description": "",
"instructions": [
{
"instruction": "ShutdownCfs",
"data": {
"target": "tgt1"
}
},
{
"instruction": "StartCfs",
"data": {
"target": "tgt1"
},
"wait": 3
},
{
"instruction": "EnableCfsOutput",
"data": {
"target": "tgt1"
},
"wait": 90
},
{
"instruction": "SendCfsCommand",
"data": {
"target": "tgt1",
"mid": "TO_CMD_MID",
"cc": "TO_RESET_CC",
"args": {}
}
},
{
"instruction": "CheckTlmValue",
"data": {
"target": "tgt1",
"mid": "TO_HK_TLM_MID",
"args": [
{
"compare": "==",
"variable": "usCmdCnt",
"value": 0
}
]
}
}
]
}
]
}
12 changes: 10 additions & 2 deletions functional_tests/plugin_tests/Test_CTF_Variable_Plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"data": {
"variable_name": "my_var",
"operator": "=",
"value": 13,
"value": "0xD",
"variable_type": "int"
}
},
Expand Down Expand Up @@ -57,6 +57,14 @@
"variable_type": "int"
}
},
{
"instruction": "CheckUserVariable",
"data": {
"variable_name": "my_var",
"operator": "==",
"value": 9
}
},
{
"instruction": "SetUserVariable",
"data": {
Expand Down Expand Up @@ -103,7 +111,7 @@
"data": {
"variable_name": "my_var",
"operator": "==",
"value": 244
"value": "0xF4"
}
},
{
Expand Down
31 changes: 28 additions & 3 deletions lib/ctf_utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@ def set_variable(variable_name, op_code, value, variable_type=None):
if variable_type and variable_type in type_map:
user_passed_type = type_map[variable_type]

# cast values from hex to int conversion
if variable_type == 'int' and isinstance(value, str):
try:
value = int(value, 0)
except ValueError as exception:
log.error('Could not cast {} to int type, trigger exception {}'.format(value, exception))
return False

if op_code == "=":
if variable_type:
if variable_type in type_map:
Expand Down Expand Up @@ -129,9 +137,6 @@ def set_variable(variable_name, op_code, value, variable_type=None):

if user_passed_type and not isinstance(value, user_passed_type):
log.info("Converting value {} to type {}".format(value, user_passed_type.__name__))
# add a special case for int hex conversion
if isinstance(value, str) and variable_type == "int" and ("x" in value or "X" in value):
value = int(value, 16)
try:
value = user_passed_type(value)
except ValueError:
Expand Down Expand Up @@ -200,6 +205,26 @@ def resolve_variable(variable):
return variable


def resolve_dic_variable(var_obj: dict):
"""
Recursively resolve the user defined variables in dictionary object.
The function resolve_variable only resolve str type variables.
"""
if not isinstance(var_obj, dict):
return var_obj
new_object = dict()
for key, value in var_obj.items():
resolved_key = resolve_variable(key)
if isinstance(value, dict):
resolved_value = resolve_dic_variable(value)
elif isinstance(value, list):
resolved_value = [resolve_dic_variable(v) for v in value]
else:
resolved_value = resolve_variable(value)
new_object[resolved_key] = resolved_value
return new_object


INDEX_PATTERN = r'\[(.*?)\]'


Expand Down
5 changes: 5 additions & 0 deletions lib/plugin_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
from inspect import signature

from lib.ctf_global import Global
from lib.ctf_utility import resolve_dic_variable
from lib.exceptions import CtfTestError
from lib.logger import logger as log

Expand Down Expand Up @@ -131,6 +132,10 @@ def process_command(self, **kwargs):
(required + optional)
"""
result = False

# resolve variables in kwargs, so that individual instruction does not need to process the argument
kwargs = resolve_dic_variable(kwargs)

instruction = kwargs["instruction"]
if "data" in kwargs.keys():
data = kwargs["data"]
Expand Down
75 changes: 70 additions & 5 deletions plugins/ccsds_plugin/readers/ccdd_export_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,21 @@
import fnmatch
import json
import ctypes
from pprint import pformat

from lib.logger import logger as log
from lib.exceptions import CtfTestError
from lib.ctf_global import Global
from plugins.ccsds_plugin.ccsds_interface import CCSDSInterface

try:
TLM_FORMATTER = Global.config.get("logging", "tlm_formatter", fallback=None)
PPRINT_DEPTH = int(Global.config.get("logging", "pprint_depth", fallback="7"))
except (AttributeError, ValueError) as exception:
log.error("Exception from getting INI logging config")
TLM_FORMATTER = "compact"
PPRINT_DEPTH = 7


# Helper Functions
def ctypes_name(name):
Expand All @@ -53,6 +63,56 @@ def dynamic_init(self, *args, **kwargs):
self.__dict__.update(kwargs)


def build_obj_from_ctype(ctype_object):
# pylint: disable=protected-access
"""
Build a python dictionary object from a ctypes structure object with mapped attributes.
@note - Utility function used by __str__ for formatted output.
@param ctype_object: The ctypes structure object instance being represented
"""
shadow_obj = dict()
key = ctype_object.__class__.__name__
if isinstance(ctype_object, ctypes.Structure):
val = dict()
for field in ctype_object._fields_:
val[field[0]] = build_obj_from_ctype(getattr(ctype_object, field[0]))
shadow_obj[key] = val
elif isinstance(ctype_object, ctypes.Array):
lst = [build_obj_from_ctype(field) for field in ctype_object]
shadow_obj[key] = lst
else:
return ctype_object

return shadow_obj


def build_str_from_ctype(ctype_object):
# pylint: disable=protected-access
"""
Build a string from a ctype object that prints name-value pairs of each field.
@note - Utility function used by __str__ for formatted output.
@param ctype_object: The ctypes structure object instance being represented
"""

max_shown_array = 1000
string = ""
name = ctype_object.__class__.__name__
if hasattr(ctype_object, '_fields_'):
string = "{}: {{{}}}".format(name, ", ".join(["{}: {}".format(
field[0], build_str_from_ctype(getattr(ctype_object, field[0]))) for field in ctype_object._fields_]))
elif hasattr(ctype_object, '_length_'):
string = "[{}{}]".format(", ".join([build_str_from_ctype(e) for e in ctype_object[:max_shown_array]]),
f"... ({len(ctype_object) - max_shown_array} more)"
if len(ctype_object) > max_shown_array else "")
else:
string = str(ctype_object)
return string


def to_string(self):
"""
A generic implementation of __str__ for a dynamic type that prints name-value pairs for each of its fields.
Expand All @@ -61,9 +121,12 @@ def to_string(self):
@param self: The object instance being represented
"""
string = "{}: {{{}}}".format(self.__class__.__name__,
", ".join(["{}: {}".format(field[0], getattr(self, field[0]))
for field in self._fields_])) # pylint: disable=protected-access
string = ""
if TLM_FORMATTER == "pprint":
dict_obj = build_obj_from_ctype(self)
string = pformat(dict_obj, sort_dicts=False, depth=PPRINT_DEPTH, width=400)
else:
string = build_str_from_ctype(self)
return string


Expand Down Expand Up @@ -458,8 +521,10 @@ def process_types(self, json_list):
mid['mid_name'], self.mids[mid['mid_name']]))
if int(mid['mid_value'], 0) in self.mids.values():
log.error("Found duplicate MID value: {}".format(mid['mid_value']))
log.debug("Update mids dict with key:{} value:{} ".format(mid['mid_name'],
int(mid['mid_value'], 0)))
log.debug("Update mids dict with key:{} value:{} ({})".format(mid['mid_name'],
int(mid['mid_value'], 0),
hex(int(mid['mid_value'],0)))
)
self.mids.update({mid['mid_name']: int(mid['mid_value'], 0)})
else:
log.error("Invalid type definition in {}".format(self.current_file_name))
Expand Down
Loading

0 comments on commit f421f1b

Please sign in to comment.