NI TestStand

cancel
Showing results for 
Search instead for 
Did you mean: 

Evaluate expression in another context (actually in a different execution)

Hi.

 

I have a challenge I've puzzled over the last days; I need to set the values of local variables in a client sequence running in a different execution from the one wherein the values were defined as strings. I present here a simplified problem, but one that demonstrates my issue;

 

The callstack looks like this: TopLevel sequence "Top" -> SeqCall to "Wrapper" (same execution as Top) -> SeqCall to "Client" (new execution).

 

Let's assume three local variables in Top: Locals.N (number), Locals.Expr = "Locals.N + 4" (string), and Locals.ClientVarName = "X" (string).

Let's also assume a local variable and two parameters in Client: Locals.X (number), Parameters.Expr (string), and Parameters.ClientVarName (string).

Expr and ClientVarName also passes through Wrapper of course.

 

The task is now to set the variable ClientVarName in Client to the value defined by Expr (e.g. X = 9 if N = 5). Pseudo-code executed in Client would be something like this: Evaluate("Locals." & Parameters.ClientVarName & "=Parameters.Expr"). Of course Parameters.Expr will need to be evaluated in the Top context for this to work, but that's quite hard since Client is in a different execution to Top. But that is option A.

Since N may change over time, I may not evaluate Expr before in Wrapper just at the call to Client. This is option B.

 

Option A:

The Evaluate() function always operate in ThisContext, so it can't be used for Expr in Client. Do you know of any other way to evaluate an expression string in a specific context? I could pass the Top context along to Client, but I can't figure out how to use for instance Expression.Evaluate which takes evaluationContext as its first argument.

 

Option B:

In Wrapper I can shift the context of Expr up one level in the callstack by modifying the string and evalute it like this: Locals.Expr = Evaluate("RunState.Caller.Locals.N + 4"). But I have no success in passing the resulting PropertyObject as a parameter to Client, and assign its value to X. What type should the new Expr be? I have tried with Expr in Wrapper being the named type 'Expression', which evaluates and transfers as a parameter to Client without any runtime errors being issued. But the assignment to Locals.X in Client fails: Evaluate("Locals." & Parameters.ClientVarName & "=Parameters.Expr") -> runtime error '-17308 specified value does not have the expected type'. And that's probably because I try to assign an expression to a number. Any ideas how I could transfer the output from Evaluate() in Wrapper, as a parameter, to Client? The variable "X" could, and will be, any type - not just numbers. Or is getting option A to work better?

 

Thanks, if you've made it this far 🙂

 

Cheers,

Steen

CLA, CTA, CLED & LabVIEW Champion
0 Kudos
Message 1 of 6
(3,561 Views)

Maybe I am missing something here, but why dont you just pass the actual value to the 'client' via the parameters. 

Regards
Ray Farmer
0 Kudos
Message 2 of 6
(3,548 Views)

Hi Ray.

 

That's my option B, to evaluate the expression in Wrapper and pass the result to Client. In this case my problem is that I don't know beforehand what type the result will be, so I need a parameter that will accommodate any type. Evaluate() returns a PropertyObject, but I can't seem to pass this to Client.

 

In addition to this I actually need to pass an array of these results to Client (I don't know the number of parameters before runtime either, and each element might be a different type) - that was one of the details I left out initially to simplify the problem. So I must be able to make an array of these PropertyObjects.

 

Cheers,

Steen

CLA, CTA, CLED & LabVIEW Champion
0 Kudos
Message 3 of 6
(3,543 Views)

Hi,

 

you have what appears to evaluate out to Local.X on the client but you have an Expr in the parameter so therefore you need to evaluate this out so that you can make a new expression so that "locals.x = ???".

 

If I get a chance tonight I'll have a little play with this

Regards
Ray Farmer
0 Kudos
Message 4 of 6
(3,534 Views)

I'd recommend that any variables you want to access/set across executions you should pass as by-reference parameters to the new execution. Your expressions should then be updated to access the variables underneath the parameters group and not in the context of the other execution as needed.

 

Trying to access variables from another execution or thread can be tricky because there is the possibility of race conditions if you aren't careful. TestStand protects getting and setting variables' values from multithreaded access, but structural changes to the variable hierarchy are not protected and you would have to protect such changes and accesses yourself with Lock steps or something similar. By instead passing only the specific variables you need as parameters to the new execution/thread you avoid a lot of the possible structural changes that might be happening to parents of that variable.

 

Hope this helps,

-Doug

0 Kudos
Message 5 of 6
(3,528 Views)

I'm not worried about race conditions, as evaluation of the expression takes place as the last thing before the new execution is started. It must either be done just before the SequenceCall, or as the first thing in the Setup group of the subsequence.

 

It's fragile to modify the expression at runtime to mimic a different context (by replacing stuff like "Locals." with "RunState.Caller.Locals." for instance, if they were in the same callstack), since such string replacement could never be exhaustive. What about "Step."? What about constructs like ("L" & "ocals.")? I could never identify them all.

 

I could split the expression up in two, like this (evaluated in the subsequence):

 

Evaluate("Locals." + Locals.TargetName) = Evaluate("RunState.Caller.Locals.SourceName")

 

The above works if Locals.SourceName was in the caller context, but my problem is a bit more complex than that; I need the right hand side to evaluate any expression string, and I need to supply a context to do it within. It's too fragile to string-replace "Locals." with "RunState.Caller.Locals." etc., as the expression might contain "Parameters.", "FileGlobals.", or much more complex access patterns from the caller context.

 

One way would be to make a new expression object and use Expression.Evaluate(context,evaloptions) on that in the subsequence, with context passed from the caller, but how do I make and pass an expression object to the sequence in which to set the TargetName local variable? It has something to do with Engine.NewExpression, but where do I store this expression object in the caller? And how do I set its Text property to "Locals.SourceName" (in this particular example, but remember it could be any expression)? And finally how do I pass this expression object to the subsequence via a parameter (what type should the parameter be)?

 

Cheers,

Steen

CLA, CTA, CLED & LabVIEW Champion
0 Kudos
Message 6 of 6
(3,477 Views)