Msmax

Use a var in component expression

Recommended Posts

Need some help with using/accessing component names and variables in component expression.

Is it possible to use a local var in a component expression?

My own created symbol has an expression like:
MB_Faults[x] + (MB_FaultsAck[x])*2 + MB_Fires[x]*4 + MB_FiresAck[x]*8+ MB_Inhibit[x]*16

Now I have to replace the "x" every time for every symbol with the index of the register, which is a lot of work if the component is used few hundred times.

Would be great to use:
private x = 770
MB_Faults[x] + (MB_FaultsAck[x])*2 + MB_Fires[x]*4 + MB_FiresAck[x]*8+ MB_Inhibit[x]*16

or even better if I can use the component name as reference:
MB_Faults[MyName] + (MB_FaultsAck[MyName])*2 + MB_Fires[MyName]*4 + MB_FiresAck[MyName]*8+ MB_Inhibit[MyName]*16

Then I could load the values from a file.

But is it possible to get the component name in the expression?
 

Share this post


Link to post
Share on other sites

I'm not quite sure what you mean.  Do you mean retrieve the name of the component from script inside the component (like a Quick Sequence or Event)?   If so, there is a local variable called "ComponentName" that you can access from inside the component.

Or do you mean access a component by building its name from strings and variables?  In that case, use execute() or evaluate():

execute("component.myComponent" + x + ".strCaption = 'comp #" + x + " caption'")

 

Share this post


Link to post
Share on other sites

Thanks for your clear explanation. Think I was looking for ComponentName...

I have created quite some user defined components which animates according bits set in several data registers filled via modbus read sequences.
For each component I place on the screen I have to set the bit numbers (the "x" which refers to the index of the bit in the array) manually now which is very time consuming.

MB_Faults[x] + (MB_FaultsAck[x])*2 + MB_Fires[x]*4 + MB_FiresAck[x]*8+ MB_Inhibit[x]*16

I assume I can replace the "x" by ComponentName in the script inside the component and load a file which assigns the correct bit number to the ComponentName in memory.
Then my script does not have to change if I rename the components correctly.

Some where at startup I will load the "x" values for the components:
ComponentName = Value
L01D01 = 1
L01D02 = 2

etc...

Next part is script within the component for animation:
MB_Faults[ComponentName] + (MB_FaultsAck[ComponentName])*2 + MB_Fires[ComponentName]*4 + MB_FiresAck[ComponentName]*8+ MB_Inhibit[ComponentName]*16


I will test this and if it does not work then I will make a small sample project to explain better what I'm looking for.
The first project was more than 10.000 bits to assign... 
The one I'm working on now has more or less 2500 bits to assign...

Share this post


Link to post
Share on other sites

Ok... not sure if I understand this right...

If I create a user defined component, rename it to L01D01, and write in the Quick Sequence:
?"ComponentName: " ComponentName

Then I expect to see the component name printed in the Command/Alert window if I click on the component... nothing printed.
I expected to see: "ComponentName: L01D01"

If I leave out the last word then "ComponentName:" is printed.

Is this the right way to use ComponentName in a script within the component self?
 

Share this post


Link to post
Share on other sites

Hmm... I see that this was introduced in version 18.1 while I still use version 17.1.

Can I upgrade WITHOUT any problems with existing CTL documents created with 17.1?
Those applications are already installed and commissioned. Have to know if I can expect any troubles by upgrading to 18.1.

Share this post


Link to post
Share on other sites

There should not be problems with that upgrade, but truthfully I'm not sure your approach is the best anyway.  For one thing, you can't index on a string, so:

MB_Faults[ComponentName]

won't work because componentName isn't a number.

Also, I would use array math to combine your entire array of bits into the bytes as you are doing on an individual basis.  So where you are doing:

MB_Faults[x] + (MB_FaultsAck[x])*2 + MB_Fires[x]*4 + MB_FiresAck[x]*8+ MB_Inhibit[x]*16

you can actually just do:

MB_Status = MB_Faults + (MB_FaultsAck)*2 + MB_Fires*4 + MB_FiresAck*8+ MB_Inhibit*16

assuming all 5 of those variables are arrays of the same size and they line up.  Do this in some sequence to keep it up to date, then you can just use:

MB_Status[x]

A better way to do all this is not to use the component name, but rather to use a component property  which you create in the OnLoad event using CreateProperty() or CreateStringProperty().  These functions create a property of the component name that you can edit from script or the docking properties window (View-Properties) and that get saved with the document.  You could have one that holds the index into the MB_Status array.

If I then had a bunch of components to create that are the same except for, say, an index property, I would create the first one with the index property, call it, say MyComponent0, then duplicate it the required number of times.  This will auto-name the components MyComponent1, MyComponent2, etc.  Then I would create a simple sequence to set the "index" property:

for (private i = 0, i < 100, i++) // 100 components, 0-99
   execute("Component.MyComponent" + i + ".index = i")
endfor

You can use a similar script to change other properties of all the controls.

Share this post


Link to post
Share on other sites

Thanks for the explanation. I do not get all of it yet, but will definately play with the setup as you advise.

I know that the ComponentName is not a number but I was thinking to use define statement to assign a number to the ComponentName.
For example... 
At startup sequence:
L01D01 = 1

 

Share this post


Link to post
Share on other sites

That won't work because ComponentName returns a string.  So if ComponentName is "myComponent" doing:

MB_Faults[componentName]

would be the same as:

MB_Faults["myComponent"]

not

MB_Faults[myComponent]

You would have to do:

MB_Faults[evaluate(myComponent)]

and given the # of elements, you should avoid evaluate() because it is pretty slow.  

 

Share this post


Link to post
Share on other sites
Posted (edited)

Please ignore the previous post... accidently pressed save.
Complete post is below.

 

Thanks for the explanation. I do not get all of it yet, but will definately play with the setup as you advise as soon I get some spare time.

I discovered that the ComponentName is not a number and can not be used like this, but I was thinking to use define statement to assign a number to a tag same as the componentname.
For example... 
At startup sequence:
define L01D01 = 1
define L01D02 = 2
define L08D34 = 6
define L10D80 = 8
define L16D45 = 9

Now I would place some components on the screen and name them:
component 1: L01D01
component 2: L01D02
component 3: L08D34
etc...

The idea was to use in the component expression: 
x= evaluate(ComponentName)
MB_Faults[x] + (MB_FaultsAck[x])*2 + MB_Fires[x]*4 + MB_FiresAck[x]*8+ MB_Inhibit[x]*16

Not sure if this will work. Have to install 18.1 to test this.


I can not use the counting method because the name of the component, is the tag as used in the field and that is not in sequence.
Later I would like to add display of the ComponentName in a popup text box when the mouse pointer is on top of the symbol. That would be very nice...

Edited by Msmax

Share this post


Link to post
Share on other sites
11 minutes ago, AzeoTech said:

That won't work because ComponentName returns a string.  So if ComponentName is "myComponent" doing:

MB_Faults[componentName]

would be the same as:

MB_Faults["myComponent"]

not

MB_Faults[myComponent]

You would have to do:

MB_Faults[evaluate(myComponent)]

and given the # of elements, you should avoid evaluate() because it is pretty slow.  

 

OK.. then I have to forget evaluate. System must be fast responsive... don't want a slow system. 
Customer will not be happy!

Share this post


Link to post
Share on other sites

Well, you could get around it by using a local variable in the component and call evaluate() from the OnLoad().  Then evaluate() is only used once.   So, something like:

local index = evaluate(componentName)

Then you can use index in the expression.  Index is just a number so will execute quite fast, especially compared to evaluate().

Note, the OnLoad() is only calculated when the component is created (i.e. when first displayed).  You can trigger this event after the fact using component.myComponent.redoLoadEvent()

For speed though I would also make sure and do what I said about calculating a combined result for the entire array:

MB_Status = MB_Faults + (MB_FaultsAck)*2 + MB_Fires*4 + MB_FiresAck*8+ MB_Inhibit*16

and then the expression in component just becomes:

MB_Status[index]

 

Share this post


Link to post
Share on other sites
5 minutes ago, AzeoTech said:

Well, you could get around it by using a local variable in the component and call evaluate() from the OnLoad().  Then evaluate() is only used once.   So, something like:

local index = evaluate(componentName)

Then you can use index in the expression.  Index is just a number so will execute quite fast, especially compared to evaluate().

Note, the OnLoad() is only calculated when the component is created (i.e. when first displayed).  You can trigger this event after the fact using component.myComponent.redoLoadEvent()

For speed though I would also make sure and do what I said about calculating a combined result for the entire array:

MB_Status = MB_Faults + (MB_FaultsAck)*2 + MB_Fires*4 + MB_FiresAck*8+ MB_Inhibit*16

and then the expression in component just becomes:

MB_Status[index]

 

Ok. I get the first part... that is clear for me now. Will need 18.1 to test that.

The second part is interesting..
MB_Status = MB_Faults + MB_FaultsAck*2 + MB_Fires*4 + MB_FiresAck*8+ MB_Inhibit*16

This will calculate all in 1 line without using counters, indexes etc.?? 
I can do this already with 17.1, this will save me a lot of time for sure. Only have to change 1 index in stead of 4 for each component.
All arrays are the same size and the indexes do match. 
Thanks!

Share this post


Link to post
Share on other sites

Yes, DAQFactory as an interpreted language is not particularly fast.  Script is compiled, but it is compiled into pseudo code and it still has to parse symbol names in real time which, relative to true compiled languages is quite slow.  A single line of script might take 100 microseconds or more to execute, depending on the processor.  But, because it is interpreted means that it can do a lot of things that truly compiled languages won't handle, such as editing parts of the script while the rest of the application is still running.  The big disadvantage of course is that it is slow, especially when doing big loops.  So, if you needed to add 1 to an array of 1000 values you would normally do something like this in most languages:

for (private i = 0, i < numrows(x), i++)
   x = x + 1
endfor

The above of course works in DAQFactory, but on my workstation for example, which is pretty speedy, it still takes 0.115 seconds to do an array of 1000 points.  Fortunately DAQFactory has lots of functionality for working with arrays where the work is done at a low level and is super fast, thus avoiding the necessity to create a loop in script.  In this case, instead of the code above, I could just do:

x = x + 1

and it would do it to the entire array in one single statement.   For that, it takes DAQFactory only 75 microseconds on my machine, less actually because I had to time a loop doing the above 100,000 times to get that number, and that means the number includes the time for the for() loop as well.

Anyhow, the bottom line is to always try and do operations that can apply to the whole array (and most operators / functions support that) rather than loop through.  There are a lot of tricks you can do with boolean math for this sort of thing.  For example, to determine if one or more values of x are > 3, you would just do:

max(x > 3)

In this case, x > 3 returns an array of 1's and 0's, one for each element of x corresponding to whether it is > 3.  Then doing max() just tells us if any of those elements are 1 and returns 1 if so, and 0 if not.

A couple other useful array functions are search() and filter().

I suggest reviewing 4.5-4.12 of the user's guide when you have a chance.

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now