Reading multiple test step outputs (python)

I couldn’t find any related issue posted here, so I’ll try in this separate post.

I am having trouble trying to handling several test step outputs for a calculation in a single step (i.e., an average). Currently I’ve based my test step on the existing example for an input/output test step.

@attribute(Display("Test Step Demo Input", "An example of using data shared from an output step as an input.", "Python Example"))
class InputDemoStep(TestStep):
    InputValues = property(Input[List[Double]], None)\
        .add_attribute(Display("Input Value"))
    def __init__(self):
        super().__init__() # The base class initializer should be invoked.
        self.InputValues = Input[Double]()
        # self.InputValues.Add(Input[Double]())
        # self.InputValues.Add(2.0)

    def Run(self):

        try:
            self.log.Info("Input value: {0}", self.InputValue.Value)
        except Exception as e:
            self.log.Error("Input value is not configured to the Output value.")
            self.log.Debug(e)

I’ve tried several approaches to adding the values, but I suspect I haven’t fully grasped how the input/output works.
I attempted to adapt to this issue: https://forum.opentap.io/t/list-of-input-t-properties/257/2

Any help is appreciated.

I am writing the code in Python.

Hi @jb111

Looks like you are trying to assign an Input to a property of type Input<List>. This will not work.

Did you already try this?

self.InputValues = Input[List[Double]]()
1 Like

No - just tried and it did indeed work out some of my misunderstanding.

Now I guess my follow-up question would be how to select the outputs that should be used. The dropdown menu only shows “None” as an option.

My second question is what is the difference between:
List[Input[Double]]
and
Input[List[Double]]
If I want to use inputs from several output test steps?

  • List[Input[Double]] : This is a List of inputs of type double. So if you want to read many outputs you could use this.
  • Input[List[double]] : This is an input of List of double, which means it is a single input, but it takes a list of values.

It sounds like the first option is what you want, but that is not actually a supported use case, at the moment.

Can you explain a bit more in depth why you need something like this?
Also, which version of OpenTAP are you using? We might be able to find a workaround.

1 Like

Thank you for the help.

I found another pretty neat and generalizable solution to my issue by using the ComponentSettings which works well for my purposes.

My solution does have one weakness for when the componentsetting class is a dependency separately in multiple plugins/test steps/etc. I was informed that it would work as a singleton, but it seems that several instances are created of the same object.

Would you @rolf_madsen know how to work around this fact?

I am using version 9.24.1

Hi @jb111,

From a certain perspective it is a singleton, but settings can be invalidated which will cause them to get new instances. However from a test plan run, it should look like a singleton - across test plan runs it might not.

Again, it would be easier to help if you could give a bit more information about what you are trying to do.

Maybe you could do it a bit differently? For example, the child steps could save their outputs in the parent step and use that to share data instead. Then you have something that is a bit more contained.

Thanks - I see.

All the purposes for the data sharing has not been defined yet but currently our team has to be able to read individual IDs from multiple ECUs, perform an action and then read the IDs again to compare some functionality before an after that action. The test steps are ready besides the data sharing and comparison. The amount of IDs returned can vary, which is why we are looking for a semi-dynamic way to save the data.

If we were to use child steps, how would we ensure the data is stored in let’s say an array/list without defining the length of it before?

I hope this helps clarify our purposes.

Here is an example of a child step sharing data with a parent step.

@attribute(OpenTap.Display("Sharing Child Step", "This step shares data with it's parent step", "DataSharingExample"))
class SharingChildStep (TestStep):
    Number = property(Int32, 1000)

    def __init__(self):
        super(SharingChildStep, self).__init__()
    
    def Run(self):
        # print the number
        self.log.Debug("Number: {0}", Int32(self.Number))

        # give the number to the parent if there is a parent step of type SharingParentStpe
        parent = self.GetParent[SharingParentStep]()
        if parent != None:
            parent.AddValue(self.Number)
        
@attribute(OpenTap.Display("Sharing Parent Step", "A step that gets data from its child steps", "DataSharingExample"))
@attribute(OpenTap.AllowAnyChild())
class SharingParentStep (TestStep):
    
    def __init__(self):
        super(SharingParentStep, self).__init__()
        self.values = []
    
    def AddValue(self, value):
        self.values.append(value)
    
    def Run(self):
        # clear the list
        self.values =[]

        # values are populated by child steps
        self.RunChildSteps()

        # print out the values
        self.log.Debug("Numbers: {0}", self.values)
1 Like

Thank you very much for the code example!

I have a follow-up question:
Let’s say I perform a measurement (an Int32) in a child of child step (child of SharingChildStep), then pass the value as an Output-Input to SharingChildStep.
The value is at this stage an Input[Int32].
Then it’s passed from SharingChild to SharingParent.
At this point (or the one before) we want to convert the values to Int32 to conform with what the list expects.
How is the value casted to an Int32? I’ve been getting errors if I try to cast as you’ve done in the example code above (ie, Int32(x))

P.S. What’s the reason you define ‘Values’ and then another list called ‘values’? Will they be accessible as two or one object?

Thanks in advance and sorry for deviating from the initial question.

The Values list was a mistake I was testing out a few different ways this could be done.

I’m my case, the value was always an int32, so actually I donøt think the cast is necessary. What kind of error are you getting?

So your test plan now looks like this?

  • Measure A
  • Measure B
  • Measure C
  • Sharing Parent ← Aggregates the values from A,B,C
    • Collect A
    • Collect B
    • Collect C

My current general concept looks like this:
image
With the possibility of aggregating data on further collections of measurements.

I figured out the casting “issue” in the meantime, but the challenge lies in getting the aggregated List[Int32] to be an output available for the final test step as an input. My current code looks like this:

@attribute(OpenTap.Display("Sharing Parent Step", "A step that gets data from its child steps", "DataSharingExample"))
@attribute(OpenTap.AllowAnyChild())
class SharingParentStep (TestStep):
    Values = property(List[Double], None)

    OutputList = property(List[Double], None)\
        .add_attribute(Output())
    
    def __init__(self):
        super(SharingParentStep, self).__init__()
        self.Values = List[Double]

    
    def AddValue(self, value):
        self.Values.append(value)
    
    def Run(self):
        # clear the list
        self.Values = List[Double]

        # values are populated by child steps
        self.RunChildSteps()

        self.OutputList = self.Values

        # print out the values
        self.log.Debug("Numbers: {0}", self.Values)

This obviously gives an error, unfortunately my knowledge of C#/.Net basic methods is extremely limited.
How would it be best suited to propagate the aggregated List[Double] as an Output?

AddValue should look like this:

    def AddValue(self, value):
        self.Values.Add(value)

You need to make an instance of List[Double] like this:

   self.Values = List[Double]()

Finally, you should probably share a copy of the values instead of the values themselves

self.OutputList = List[Double](self.Values)