It is currently February 25th, 2021, 10:02 pm

How to execute an IfMatch only if a variable is in a given state?

Help with creating, editing & fixing problems with skins
jn_meter
Posts: 82
Joined: December 27th, 2016, 12:04 pm

How to execute an IfMatch only if a variable is in a given state?

Post by jn_meter »

Dear all

I want to do something along the following lines.

Code: Select all

[SomeMeasure]
IfAboveValue=#processTop_threshold#
IfAboveAction=do something if myVar is <x>, and something else is myVar is <y>.
I cannot seem to work out implement the IfAboveAction in a way that does what I want.

I tried making IfAboveAction run a command measure, but if I do that directly then I get a 'not supported error', and if I do it indirectly, by [#action_runMyCommandMeasure#] and action_runMyCommandMeasure=<whatever>, then the command measure never runs.
User avatar
SilverAzide
Rainmeter Sage
Posts: 1134
Joined: March 23rd, 2015, 5:26 pm

Re: How to execute an IfMatch only if a variable is in a given state?

Post by SilverAzide »

jn_meter wrote: December 30th, 2020, 7:03 pm Dear all

I want to do something along the following lines.

Code: Select all

[SomeMeasure]
IfAboveValue=#processTop_threshold#
IfAboveAction=do something if myVar is <x>, and something else is myVar is <y>.
I cannot seem to work out implement the IfAboveAction in a way that does what I want.

I tried making IfAboveAction run a command measure, but if I do that directly then I get a 'not supported error', and if I do it indirectly, by [#action_runMyCommandMeasure#] and action_runMyCommandMeasure=<whatever>, then the command measure never runs.
It seems like perhaps you need an IfCondition rather than an IfAction:

Code: Select all

[SomeMeasure]
...
IfCondition=(SomeMeasure > #processTop_threshold#) && (#myVar# = <x>)
IfTrueAction=[!DoSomething...]
IfCondition2=(SomeMeasure > #processTop_threshold#) && (#myVar# = <y>)
IfTrueAction2=[!DoSomethingElse...]
Don't forget DynamicVariables=1 if #myVar# (or "x" and "y") is changing.
Gadgets Wiki GitHub More Gadgets...
User avatar
balala
Rainmeter Sage
Posts: 12279
Joined: October 11th, 2010, 6:27 pm
Location: Gheorgheni, Romania

Re: How to execute an IfMatch only if a variable is in a given state?

Post by balala »

jn_meter wrote: December 30th, 2020, 7:03 pm I cannot seem to work out implement the IfAboveAction in a way that does what I want.
I'm not sure I entirely can follow you, but at least if I'm not mistaken, I'd do something like this:

Code: Select all

[Rainmeter]
Update=1000

[Variables]
processTop_threshold=1
X=0
Y=1

[SomeMeasure]
Measure=Calc
Formula=2
IfAboveValue=#processTop_threshold#
IfAboveAction=[!SetVariable MyVar "((#processTop_threshold#=#X#)?10:((#processTop_threshold#=#Y#)?-10:0))"]

[MeterVal]
Meter=STRING
X=0
Y=0
Padding=15,5,15,5
FontColor=220,220,220
FontEffectColor=0,0,0
StringEffect=Shadow
SolidColor=0,0,0,150
FontSize=8
FontFace=Segoe UI
StringStyle=BOLD
StringAlign=LEFT
AntiAlias=1
Text=#MyVar#
DynamicVariables=1
LeftMouseUpAction=[!Refresh]
Note the followings:
  • I defined three variables: processTop_threshold, X and Y. All three are used into the code: processTop_threshold is used by you into the IfAboveValue option and X and Y are used by me into the !SetVariable bang of IfAboveAction option. This option is executed ONLY if the value of the measure is above #processTop_threshold#. Since you didn't specified the other options of the measure, I added a Formula=2 option, to get executed the IfAboveAction in all cases (see bellow as well). I supposed your <x> and <y> are two variables, but <x> and <y>, in this form, in Rainmeter don't make too much sense, that's why I used them as #X# and #Y#.
  • If IfAboveAction is executed (like in the above code), the !SetVariable bang sets a value for the MyVar variable:
    • If the value of the processTop_threshold variable is equal to #X#, the first condition ((#processTop_threshold#=#X#)) is true and accordingly the MyVar variable gets the value 10.
    • If it's not, the second condition is checked ((#processTop_threshold#=#Y#)) and if this proofs to be true, the MyVar variable gets the value -10.
    • If both of the above two conditions are false, the MyVar variable get the value 0.
    Recommend to switch the value of the processTop_threshold variable between 0 and 1, to see what's goinfg on.
If this is not exactly what you want (and I suppose it's not - well in fact it definitely isn't, because can't be), please post a few details on what would you like to achieve.
jn_meter
Posts: 82
Joined: December 27th, 2016, 12:04 pm

Re: How to execute an IfMatch only if a variable is in a given state?

Post by jn_meter »

Azide: thank you. (Balala: please see the end of this post.) Guided by what you said, I tried the following.

Code: Select all

[measure_processTop]
Category=Process
Counter=% Processor Time
DynamicVariables=1
Formula=measure_processTop
IfCondition=(measure_processTop > #processTop_threshold#) && ("[measure_processTop]" = #var_processTop_old#)
IfTrueAction=[!SetVariable var_processTop_old "[measure_processTop]"] [!SetVariable var_meterProcessTopText "[measure_processTop] ([measure_processTop:1]% of [measure_cpu_usage:1]%)"]
IfFalseAction=[!SetVariable var_meterProcessTopText ""] [!SetVariable var_processTop_old "[measure_processTop]"]
Index=1
Measure=Plugin
Percent=1
Plugin=UsageMonitor
UpdateDivider=2
The result was this in the log: Syntax error: IfCondition=(measure_processTop > 10) && ("MsMpEng" = initialDummyString) (System\nj_system.ini - [measure_processTop]). And making things such that I could use a variable instead of a meter name in && ("[measure_processTop]" did not help. (And 'MsMpEng' is some Windows process.)

Balala: I'm looking at your suggestion - for which thanks - now.
User avatar
balala
Rainmeter Sage
Posts: 12279
Joined: October 11th, 2010, 6:27 pm
Location: Gheorgheni, Romania

Re: How to execute an IfMatch only if a variable is in a given state?

Post by balala »

jn_meter wrote: December 30th, 2020, 8:32 pm Balala: I'm looking at your suggestion - for which thanks - now.
No, don't. SilverAzide's solution is far better than mine. I didn't understand what you want to achieve, but he did, so use his solution.
jn_meter
Posts: 82
Joined: December 27th, 2016, 12:04 pm

Re: How to execute an IfMatch only if a variable is in a given state?

Post by jn_meter »

balala - oh, and SilverAzide too, perhaps

Thank you for your work. But let me, as you requested, say more about what I wish to achieve.

The goal is this: when total cpu usage is high, and, for two times running, some particular process is using more CPU than any other process, display the name of that process.

Here, in pseudo-code, is how I am trying to achieve the goal.

Every S seconds, check whether total CPU usage, C, is above a threshold T.
If C > T, then
- Find the process using the greatest amount of CPU.
[Edit: there is also this step: is that process using more than #PerProcessThreshold#? If not, abort.]
- Is the name of that process a process that we have stored previously?
-- If it is, then display the name of the process.
-- If it is not, then display nothing but store the name.


What I have implemented already: getting total CPU usage; getting the name of the process that uses the most CPU. Indeed, previously I did not have the 'for two times running' requirement and my skin did indeed display the, so to speak, hungriest process at any given time.

Here is the non-functioning code (or rather the relevant parts of it) that I have at present.

Code: Select all

processTop_threshold=10
; But display of top process is controlled also by global CPU use.

var_meterProcessTopText=""
var_processTop_old="initialDummyString"

[measure_processTop]
; This measure runs in separate thread every second (unalterably). UpdateDivider sets how often the IfCondition is executed.
; The measure gets toggled on and off by another measure - a measure that measures global CPU.
Category=Process
Counter=% Processor Time
DynamicVariables=1
Formula=measure_processTop
IfCondition=(measure_processTop > #processTop_threshold#) && ("[measure_processTop]" = #var_processTop_old#)
IfTrueAction=[!SetVariable var_processTop_old "[measure_processTop]"] [!SetVariable var_meterProcessTopText "[measure_processTop] ([measure_processTop:1]% of [measure_cpu_usage:1]%)"]
IfFalseAction=[!SetVariable var_meterProcessTopText ""] [!SetVariable var_processTop_old "[measure_processTop]"]
Index=1
Measure=Plugin
Percent=1
Plugin=UsageMonitor
UpdateDivider=2
Last edited by jn_meter on December 30th, 2020, 11:27 pm, edited 1 time in total.
User avatar
balala
Rainmeter Sage
Posts: 12279
Joined: October 11th, 2010, 6:27 pm
Location: Gheorgheni, Romania

Re: How to execute an IfMatch only if a variable is in a given state?

Post by balala »

jn_meter wrote: December 30th, 2020, 8:48 pm Here is the non-functioning code (or rather the relevant parts of it) that I have at present.
Take into account the followings:
  • An IfCondition options (as well as the Formula option of a Calc measure) has to be entirely numeric. You can't check strings match. For instance the ("[measure_processTop]" = #var_processTop_old#) does never work, such an IfCondition never checks if two strings are matching each other.
  • The measure_processTop measure name is a string. In order to get the numeric value of that measure you have to use the [measure_processTop:] section variable. So, replace the (measure_processTop > #processTop_threshold#) condition with ([measure_processTop:] > #processTop_threshold#).
  • The Formula option is not valid on a UsageMonitor plugin measure. Even if it doesn't cause trouble, recommend to remove it from the [measure_processTop] measure.
jn_meter
Posts: 82
Joined: December 27th, 2016, 12:04 pm

Re: How to execute an IfMatch only if a variable is in a given state?

Post by jn_meter »

balala: thanks, that is helpful.

Now, it does seem (from this documentation (subsection, 'A note on number value vs. string value') that I can get the process number of the top process. So presumably I can store that and compare it to its old value - and in that way, I can implement my algorithm.

That said, I have the following problems.
1) If I store any process number in a string, then that number becomes a string.
2) Rainmeter seems not to allow one to have variables that are not strings.
3) Seemingly strangely, within my [measure_processTop] measure, which is set to measure the percentage of CPU used by the top process, [measure_processTop:] does give that percentage, but [measure_processTop] gives the process name and I cannot seem to get the process number - except, perhaps, as the text field in a meter. (On that final point, about the Text field, see again the aforementioned bit of documentation.)

EDITED. (I changed 3.)
jn_meter
Posts: 82
Joined: December 27th, 2016, 12:04 pm

Re: How to execute an IfMatch only if a variable is in a given state?

Post by jn_meter »

Through horrible hackery, I seem to have got the code working. What that code does, as desired, is as follows.

When the global CPU usage is above a certain value V1, then, if the process P that is using the most CPU is using more than some value V2, display that process and how much CPU usage it is using. Or, rather - and because I am uninterested in momentary CPU spikes - show P only if for a while all of the foregoing conditions continue to be satisfied.

Here is an image of the relevant bit of the skin: Image
(The of '100%' means that 100% of the total CPU power was being used - which it actually was.)

I will not post the whole skin - because that skin is long and does a lot - but here are the relevant parts.

Code: Select all

[Variables]

processTop_threshold=10
; But display of top process is controlled also by global CPU use.
; See action_processTop_HideMeterAndDisableMeasures. [But I omit that code here.]

action_processTop_HideMeterAndDisableMeasures=[!HideMeter meter_processTop] [!DisableMeasure measure_processTop] [!DisableMeasure measure_processTop_helper]
action_processTop_EnableMeasure=[!EnableMeasure measure_processTop] [!ShowMeter meter_processTop]

; ========
; Measures
; ========

[measure_processTop_helper]
Measure=String
Disabled=1
DynamicVariables=1
String=#var_processTop_str_current#
IfMatchMode=1
IfMatch=#var_processTop_str_old#
IfMatchAction=[!SetVariable var_processTop_str_old "#var_processTop_str_current#"] [!ShowMeter meter_processTop]
IfNotMatchAction=[!SetVariable var_processTop_str_old "#var_processTop_str_current#"] [!HideMeter meter_processTop] 

[measure_processTop]
; This measure runs in separate thread every second (unalterably). UpdateDivider sets how often the IfCondition is executed.
; The measure gets toggled on and off by another measure - a measure that measures global CPU.
Measure=Plugin
Plugin=UsageMonitor
Category=Process
Counter=% Processor Time
Index=1
Percent=1
Blacklist=_Total|Idle|dwm|wmpnetwk|csrss|svchost|lsass|System|system|Memory Compression|wininit|RemindersServer|spoolsv|IpOverUsbSvc
DynamicVariables=1
IfConditionMode=1
IfCondition=(measure_processTop > #processTop_threshold#)
IfTrueAction=[!SetVariable var_meterProcessTopText "[measure_processTop] ([measure_processTop:1]% of [measure_cpu_usage:1]%)"] [!SetVariable var_processTop_str_current [measure_processTop]] [!EnableMeasure measure_processTop_helper]
IfFalseAction=[!SetVariable var_meterProcessTopText ""] [!HideMeter meter_processTop] [!DisableMeasure measure_processTop_helper]

; ========
; Meter
; ========

[meter_processTop]
MeasureName=measure_processTop_helper
DynamicVariables=1
Hidden=1
LeftMouseUpAction=#path_prog_lasso#
Meter=String
Text=#var_meterProcessTopText#
MeterStyle=style_processTop
User avatar
balala
Rainmeter Sage
Posts: 12279
Joined: October 11th, 2010, 6:27 pm
Location: Gheorgheni, Romania

Re: How to execute an IfMatch only if a variable is in a given state?

Post by balala »

jn_meter wrote: December 30th, 2020, 10:09 pm 1) If I store any process number in a string, then that number becomes a string.
If you're storing a number, it can be used as a number and you can use it into a Calc measure, to make some calculations. For instance the following [MeasureCalc] measure retuns 26, as it has to. However what do you mean by "store"?
jn_meter wrote: December 30th, 2020, 10:09 pm 2) Rainmeter seems not to allow one to have variables that are not strings.
Sorry, I disagree. For example, modifying a little bit the above code, here is a case when the MyVar variable is a number and it is even modified when you click the string:

Code: Select all

[Variables]
MyVar=2

[Measure]
Measure=String
String=25

[MeasureCalc]
Measure=Calc
Formula=( Measure + #MyVar# )
DynamicVariables=1

[MeterResult]
Meter=STRING
MeasureName=Measure
MeasureName2=MeasureCalc
Y=0
Padding=15,5,15,5
FontColor=220,220,220
FontEffectColor=0,0,0
StringEffect=Shadow
SolidColor=0,0,0,150
FontSize=8
FontFace=Segoe UI
StringStyle=BOLD
StringAlign=LEFT
AntiAlias=1
Text=Measure:	%1#CRLF#MeasureCalc:	%2
DynamicVariables=1
LeftMouseUpAction=[!SetVariable MyVar "3"][!UpdateMeasure "MeasureCalc"][!UpdateMeter "MeterResult"][!Redraw]
See that MyVar is a number (2) and the [MeasureCalc] measure is evaluated correctly. When you click the result, the first !SetVariable bang attributes 3 to the MyVar variable and the measure is reevaluated. Correctly.
jn_meter wrote: December 30th, 2020, 10:09 pm 3) Seemingly strangely, within my [measure_processTop] measure, which is set to measure the percentage of CPU used by the top process, [measure_processTop:] does give that percentage, but [measure_processTop] gives the process name and I cannot seem to get the process number - except, perhaps, as the text field in a meter. (On that final point, about the Text field, see again the aforementioned bit of documentation.)
A measure name followed by a colon is a section variable and it means the numeric value of the appropriate measure. The measure name simply (surronded by brackets) is another section variable, but it means the string value of the measure. Even if in most cases these two are the same, this is not always true, like this time.