1# events.json
2
3This file defines the events that dictate how fan control operates.  Each event
4can contain groups, triggers, and actions.
5
6Actions are where fan targets are calculated and set, among other things.
7Triggers specify when an action should run. Groups specify which D-Bus objects
8the triggers and actions should operate on.
9
10Some actions have modifiers, which help calculate a value.
11
12- [Groups](#groups)
13- [Triggers](#triggers)
14- [Actions](#actions)
15- [Modifiers](#modifiers)
16
17## Example
18
19```
20[
21   {
22     "name": "fan(s) missing",
23     "groups": [
24       {
25         "name": "fan inventory",
26         "interface": "xyz.openbmc_project.Inventory.Item",
27         "property": { "name": "Present" }
28       }
29     ],
30     "triggers": [
31       {
32         "class": "init",
33         "method": "get_properties"
34       },
35       {
36         "class": "signal",
37         "signal": "properties_changed"
38       }
39     ],
40     "actions": [
41       {
42         "name": "count_state_before_target",
43         "count": 1,
44         "state": false,
45         "target": 15000
46       }
47     ]
48   }
49]
50```
51
52The above event is an example of a method to set the fan target to 15000 when
53one or more fans is missing. The basic behavior with this config is:
54
55The trigger `init.get_properties` will, on fan control startup, read the
56Present property on the `xyz.openbmc_project.Inventory.Item` interface from
57every D-Bus object in the 'fan inventory' group, update those values in the
58object cache, and then call the `count_state_before_target` action.
59
60The trigger `signal.properties_changed` will watch for changes on the Present
61property on each D-Bus object in the group, update the new value in the object
62cache, and call the `count_state_before_target` action when the value changes.
63
64The `count_state_before_target` action will look at the object cache value of
65the Present property on each member of the group and set the fan target hold to
6615000 when one or more of them is false.  Otherwise, it will clear its fan
67target hold.
68
69## Groups
70
71```
72"groups": [
73    {
74        "name": "<name>",
75        "interface": "<interface>",
76        "property": { "name": "<name>" }
77    }
78    ...
79]
80```
81
82### name
83The name of a group that must be also be defined in
84[groups.json](groups.md).
85
86### interface
87The actions and triggers defined with this group will look at this D-Bus
88interface on the members of this group.
89
90### property: name
91The actions and triggers defined with this group will look at this D-Bus
92property on the members of this group.
93
94## Triggers
95
96There are several classes of triggers, and the JSON configuration is different
97for each.
98
99### init
100Init triggers run when fan control events are enabled on fan control startup.
101After invoking the configured method, any actions configured for this trigger
102will run.
103
104```
105{
106    "class": "init",
107    "method": "<method>"
108}
109```
110
111#### methods
112
1131. `get_properties` - Read the property configured for the group from every
114   member of the group, and store it in fan control's object cache.
115
1162. `name_has_owner` - Populates the service owned state from D-Bus for each
117   group member in fan control's D-Bus service cache map.
118
119### signal
120Signal triggers subscribe to certain D-Bus signals for each member of its
121configured group.  After handling the signal, any configured actions are run.
122
123```
124{
125    "class": "signal",
126    "signal": "<signal>"
127}
128```
129
130#### signal types
1311. `properties_changed` - Subscribes to the PropertiesChanged signal for the
132   D-Bus interface and property specified in the group definition for each
133   group member.  When the signal occurs, the new property value will be added
134   to or updated in the object cache.
135
1362. `interfaces_added` - Subscribes to the InterfacesAdded signal for the D-Bus
137   interface specified in the group definition for each group member.  When the
138   signal occurs, the interface and its properties will be added to the object
139   cache.
140
1413. `interfaces_removed` - Subscribes to the InterfacesRemoved signal for the
142   D-Bus interface specified in the group definition for each group member.
143   When the signal occurs, the interface and properties will be removed from
144   the object cache.
145
1464. `name_owner_changed` - Subscribes to the NameOwnerChanged signal for the
147   services that host the D-bus interface specified in the group definition for
148   each group member.  When the signal occurs, the service owned state will be
149   updated in the service cache map.
150
1515. `member` - Subscribes to the signal listed on each group member.  No caches
152   are updated when the signal occurs.
153
154### timer
155Timer triggers run actions after the configured type of timer expires.
156
157```
158{
159    "class": "timer",
160    "type": "<type>",
161    "interval": "<interval>",
162    "preload_groups": "<true/false>"
163}
164```
165
166#### type
1671. `oneshot` - Starts a timer that runs once.
168
1692. `repeating` - Starts a repeating timer.
170
171#### interval
172The timer length in microseconds
173
174#### preload_groups
175Optional, if set to true, will update the D-Bus properties from the configured
176groups in the object cache after the timer expires but before any actions run.
177
178### parameter
179Parameter triggers run actions after a parameter changes.
180
181```
182{
183    "class": "parameter",
184    "parameter": "<parameter>"
185}
186```
187#### parameter
188The parameter value to watch.
189
190### poweron
191PowerOn triggers run when the power turns on.  Functionally, they behave like
192an init trigger.
193
194```
195{
196    "class": "poweron",
197    "method": "<method>"
198}
199```
200
201#### method
202The methods are the same as with the init trigger.
203
204### poweroff
205
206PowerOff triggers run when the power turns off.  Functionally, they behave like
207an init trigger.
208
209```
210{
211    "class": "poweroff",
212    "method": "<method>"
213}
214```
215
216#### method
217The methods are the same as with the init trigger.
218
219## Actions
220Actions can either operate on the groups listed with the event, or on the
221groups listed within the action's JSON config.
222
223Most actions set fan targets or floors.  This can be done by requesting a
224target value explicitly, or by requesting an increase or decrease delta.
225Targets and floors can also be set with a hold, meaning another action can't
226set a floor/target below the one that is held.
227
228Some actions set or read a key/value pair called a parameter.  These can be
229created, updated, and deleted as necessary.  For example, one action may
230calculate and set a `floor_index` parameter, and another action may then read
231that parameter to help choose a fan floor.
232
233The available actions are:
234
235- [set_net_increase_target](#set_net_increase_target)
236- [set_net_decrease_target](#set_net_decrease_target)
237- [count_state_floor](#count_state_floor)
238- [count_state_before_target](#count_state_before_target)
239- [default_floor_on_missing_owner](#default_floor_on_missing_owner)
240- [mapped_floor](#mapped_floor)
241- [set_target_on_missing_owner](#set_target_on_missing_owner)
242- [override_fan_target](#override_fan_target)
243- [pcie_card_floors](#pcie_card_floors)
244- [set_request_target_base_with_max](#set_request_target_base_with_max)
245- [set_parameter_from_group_max](#set_parameter_from_group_max)
246- [target_from_group_max](#target_from_group_max)
247- [call_actions_based_on_timer](#call_actions_based_on_timer)
248- [get_managed_objects](#get_managed_objects)
249
250### set_net_increase_target
251
252Calculates the net target increase to be requested based on the value of each
253property given within a group. The net target increase is based on the maximum
254difference between the `delta` JSON value and all of the properties of the group.
255The final result is the increase change that's requested to the current target
256of a zone.
257
258The group values can be compared to either a value hardcoded in the JSON, or a
259parameter value.
260
261```
262{
263    "name": "set_net_increase_target",
264    "groups": [{
265        "name": "pcie temps",
266        "interface": "xyz.openbmc_project.Sensor.Value",
267        "property": { "name": "Value" }
268      }],
269    "state": 70.0,
270    "delta": 255
271}
272```
273
274The above config uses a hardcoded state value:
2751. For each member of the 'pcie temps' group:
276  - Read its 'Value' D-Bus property.
277  - If that property value is greater than the 'state' value of 70.0:
278   - Subtracts 70.0 from the property value.
279   - Multiplies that difference by the 'delta' value of 255.
2802. Requests an increase of the largest calculated delta value, if there is
281   one.
282
283```
284{
285    "name": "set_net_increase_target",
286    "groups": [{
287        "name": "proc0 core temps",
288        "interface": "xyz.openbmc_project.Sensor.Value",
289        "property": { "name": "Value" }
290      }],
291    "state_parameter_name": "proc_0_core_dvfs_increase_temp",
292    "delta": 300
293}
294```
295
296The above config uses a parameter as the state value:
2971. For each member of the 'proc 0 core temps' group:
298  - Read its 'Value' D-Bus property.
299  - If that property value is greater than the value of the parameter listed in
300    the 'state_parameter_name' field, in this case
301    'proc_0_core_dvfs_increase_temp':
302   - Subtracts that parameter value from the property value.
303   - Multiplies that difference by the 'delta' value of 300.
3042. Requests an increase of the largest calculated delta value, if there is one.
305
306### set_net_decrease_target
307Calculates the net target decrease to be requested based on the value of each
308property given within a group. The net target decrease is based on the minimum
309difference between the `delta` JSON value and all properties in the group. The
310final result is the decrease change that's requested to the current target of a
311zone.
312
313The group values can be compared to either a value hardcoded in the JSON, or a
314parameter value.
315
316```
317{
318    "name": "set_net_decrease_target",
319    "groups": [{
320        "name": "pcie temps",
321        "interface": "xyz.openbmc_project.Sensor.Value",
322        "property": { "name": "Value" }
323      }],
324    "state": 65.0,
325    "delta": 80
326}
327
328```
329
330The above config uses a hardcoded state value:
3311. For each member of the 'pcie temps' group:
332  - Read its 'Value' D-Bus property.
333  - If that property value is less than the 'state' value of 65.0:
334   - Subtracts the property value from 65.0.
335   - Multiplies that difference by the 'delta' value of 80.
3362. Requests a decrease of the smallest calculated delta value, if there is
337   one.
338
339```
340{
341    "name": "set_net_decrease_target",
342    "groups": [{
343        "name": "proc 0 core temps",
344        "interface": "xyz.openbmc_project.Sensor.Value",
345        "property": { "name": "Value" }
346      }],
347    "state_parameter_name": "proc_0_core_dvfs_decrease_temp",
348    "delta": 50
349}
350```
351
352The above config uses a parameter as the state value:
3531. For each member of the 'proc 0 core temps' group:
354  - Read its 'Value' D-Bus property.
355  - If that property value is less than the value of the parameter listed
356    the 'state_parameter_name' field, in this case
357    'proc_0_core_dvfs_decrease_temp':
358   - Subtracts the property value from the parameter value.
359   - Multiplies that difference by the 'delta' value of 50.
3602. Requests a decrease of the smallest calculated delta value, if there is
361   one.
362
363### count_state_floor
364 Sets the fans to a configured floor when a number of members within the
365 group are at a configured state. Once the number of members at the given
366 state falls below the configured count, the floor hold is released.
367
368```
369{
370    "name": "count_state_floor",
371    "count": 2,
372    "state": false,
373    "floor": 18000
374}
375```
376
377The above config reads the configured D-Bus property on each group member
378configured for the action.  If two or more members have a property value of
379false, a floor hold will be requested with a value of 18000.  Otherwise, the
380floor hold will be released (if it was previously requested).
381
382### count_state_before_target
383Sets the fans to a configured target when a number of members within the group
384are at a configured state. Once the number of members at the given state falls
385below the configured count, active fan target changes are allowed.
386
387```
388{
389    "name": "count_state_before_target",
390    "count": 1,
391    "state": false,
392    "target": 18000
393}
394```
395
396The above config reads the configured D-Bus property on each group member
397configured for the action.  If one or more members have a property value of
398false, a target hold will be requested with a value of 18000.  Otherwise, the
399hold will be released (if it was previously requested).
400
401### default_floor_on_missing_owner
402Sets the fan floor to the defined zone's default fan floor when a service
403associated to a given group has terminated. Once all services are functional
404and providing the sensors, the fan floor is allowed to be set normally again.
405
406There is no additional JSON config for this action.
407
408### mapped_floor
409This action can be used to set a floor value based on 2 or more groups
410having values within certain ranges, where the key group chooses the set
411of tables in which to check the remaining group values.
412
413```
414{
415  "name": "mapped_floor",
416  "key_group": "ambient temp",
417  "default_floor": 5555,
418  "fan_floors": [
419   {
420     "key": 25,
421     "default_floor": 4444,
422     "floor_offset_parameter": "ambient_25_altitude_offset",
423     "floors": [
424       {
425         "parameter": "pcie_floor_index",
426         "floors": [
427           { "value": 1, "floor": 2000 },
428           { "value": 2, "floor": 3000 },
429           { "value": 3, "floor": 4000 },
430           { "value": 4, "floor": 5000 },
431           { "value": 5, "floor": 6000 }
432         ]
433       },
434       {
435         "group": "power save",
436         "floors": [
437            { "value": true, "floor": 1000 }
438         ]
439       }
440     ]
441   }
442}
443```
444
445The above config will use the maximum value of the 'ambient temp' group as the
446key into the 'fan_floors' tables.  There is one of those tables listed, and it
447will be used when the key group has a max value of less than 25.
448
449It will then traverse the contained floors arrays, keeping track of the highest
450valid floor value it finds.  If it doesn't find any valid floor values, it will
451use the `default_floor` value of 4444, though that value is optional.
452
453If no valid tables were found given the key value, the `default_floor` value of
4545555 would be used, though that is optional and if not supplied the code would
455default to the default floor of the zone.
456
457At the end of the analysis, a floor hold will be set with the final floor
458value.
459
460### set_target_on_missing_owner
461Sets the fans to a configured target when any service owner associated to the
462group is missing. Once all services are functional and providing all the
463group data again, active fan target changes are allowed.
464
465```
466{
467    "name": "set_target_on_missing_owner",
468    "groups": [{
469        "name": "fan inventory",
470        "interface": "xyz.openbmc_project.Inventory.Item",
471        "property": { "name": "Present" }
472      }],
473    "target": 18000
474}
475```
476
477The above config will set a target hold of 18000 when the service associated
478with the 'fan inventory' group is lost.
479
480### override_fan_target
481This action locks fans at configured targets when the configured `count` amount
482of fans meet criterion for the particular condition. A locked fan maintains its
483override target until unlocked (or locked at a higher target). Upon unlocking,
484it will either revert to temperature control or activate the next-highest
485target remaining in its list of locks.
486
487```
488{
489    "name": "override_fan_target",
490    "count": 1,
491    "state": false,
492    "fans": [ "fan0", "fan1", "fan2", "fan3" ],
493    "target": 10000
494}
495```
496
497The above config will lock all fans in the fans array at a target of 10000 when
498one or more members in its configured group have a group property value of
499false.
500
501This could be used for example, to lock the rotors of a multirotor fan to a
502high target when one of its rotors has a functional property equal to false.
503
504### pcie_card_floors
505Sets the `pcie_floor_index` parameter based on the current configuration of
506plugged and powered on PCIe cards, using data from the `pcie_cards.json` file.
507
508It chooses the highest index from the active PCIe cards to set in the
509parameter.
510
511It must be configured with the following groups and properties:
512- The PCIe slots with the PowerState property
513- The PCIe cards with the following properties: Function0DeviceId,
514  Function0VendorId, Function0SubsystemId, Function0SubsystemVendorId
515
516```
517{
518    "name": "pcie_card_floors",
519    "use_config_specific_files": true,
520    "settle_time": 2
521}
522```
523
524The `use_config_specific_files` field tells the code to look for the
525'pcie_cards.json' files in the same system specific directories as
526'events.json'.  If missing or false, looks in the base fan control directory.
527
528The `settle_time` field is the amount of time in seconds that needs to pass
529without a call to run() from a group property value changing.  As the
530PCIeDevice attributes are written close together by the host, this allows the
531action to wait until the writes are done before selecting the index.
532
533Additional details are in the [header
534file](../../control/json/actions/pcie_card_floors.hpp)
535
536### set_request_target_base_with_max
537Determines the maximum value from the properties of the group of D-Bus objects
538and sets the requested target base to this value. Only positive integer or
539floating point types are supported as these are the only valid types for a fan
540target to be based off of.
541
542The `requested target base` value is the base value to apply a target delta
543to.  By default, it's the current zone target unless modified by this action.
544
545```
546{
547    "name": "set_request_target_base_with_max",
548    "groups": [{
549        "name": "fan targets",
550        "interface": "xyz.openbmc_project.Fan.Target",
551        "property": { "name": "Target" }
552      }]
553}
554```
555
556The above config will set the requested target base to the maximum Target
557property value of all members of the 'fan targets' group.
558
559### set_parameter_from_group_max
560Sets a parameter value based on the maximum group property value.  The property
561value can be modified before storing it if the JSON specifies a valid modifier
562expression.
563
564 ```
565{
566    "name": "set_parameter_from_group_max",
567    "parameter_name": "proc_0_throttle_temp",
568    "modifier": {
569      "expression": "minus",
570      "value": 4
571    }
572}
573 ```
574
575The above config will first find the max of its groups property values,
576subtract 4, and then store the resulting value in the `proc_0_throttle_temp`
577parameter.
578
579### target_from_group_max
580
581The action sets target of Zone to a value corresponding to the maximum
582value from maximum group property value. The mapping is based on a provided
583table. If there are more than one event using this action, the maximum speed
584derived from the mapping of all groups will be set to the zone's target.
585
586...
587{
588    "name": "target_from_group_max",
589    "groups": [
590        {
591          "name": "zone0_ambient",
592          "interface": "xyz.openbmc_project.Sensor.Value",
593          "property": { "name": "Value" }
594        }
595    ],
596    "neg_hysteresis": 1,
597    "pos_hysteresis": 0,
598    "map": [
599        { "value": 10.0, "target": 38.0 },
600        ...
601    ]
602}
603
604The above JSON will cause the action to read the property specified in the
605group "zone0_ambient" from all members of the group. The change in the group's
606members value will be checked against "neg_hysteresis" and "pos_hysteresis"
607to decide if it is worth taking action. "neg_hysteresis" is for the increasing
608case and "pos_hysteresis" is for the decreasing case. The maximum property value
609in the group will be mapped to the "map" to get the output "target".
610Each configured event using this action will be provided with a key in a static map
611to store its mapping result. The static map will be shared across the events of this
612action. Therefore, the updated "target" value derived from "zone0_ambient" will be
613stored in that static map with its own key. Each time it calls this action running
614for each event, after the new value is updated to the static map, the maximum value
615from it will be used to set to the Zone's target.
616
617### call_actions_based_on_timer
618This action starts and stops a timer that runs a list of actions whenever the
619timer expires.  A timer can be either `oneshot` or `repeating`.
620
621When all groups have a configured value to compare against, that will
622be compared against all members within each group to start/stop the
623timer. When all group members have a given value and it matches what's
624in the cache, the timer is started and if any do not match, the timer is
625stopped.
626
627When any group does not have a configured value to be compared against,
628the groups' service owned state is used to start/stop the timer. When
629any service providing a group member is not owned, the timer is started
630and if all members' services are owned, the timer is stopped.
631
632Consider the following action config:
633```
634{
635    "name": "call_actions_based_on_timer",
636    "timer": {
637        "interval": 5000000,
638        "type": "oneshot"
639    },
640    "actions": [{
641        "name": "test"
642    }]
643}
644
645```
646If its group configuration has a property value listed, like:
647
648```
649{
650    "name": "fan inventory",
651    "interface": "xyz.openbmc_project.Inventory.Item",
652    "property": { "name": "Present", "value": true }
653}
654```
655Then a oneshot timer of 5000000us will be started when every member of the fan
656inventory group has a value of true.  Otherwise, the timer will be stopped if
657it's running.
658
659If the group configuration has no property value listed, like:
660
661```
662{
663    "name": "fan inventory",
664    "interface": "xyz.openbmc_project.Inventory.Item",
665    "property": { "name": "Present" }
666}
667```
668Then the timer will be started when any service providing a group member isn't
669owned (on D-Bus).  Otherwise, it will stop the timer if it's running.
670
671### get_managed_objects
672This action adds the members of its groups to the object cache by using the
673GetManagedObjects D-Bus method to find and add the results.  When that is done,
674it then runs any actions listed in the JSON.
675
676This allows an action to run with the latest values in the cache without having
677to subscribe to propertiesChanged for them all.
678
679```
680{
681   "name": "get_managed_objects",
682   "groups": [
683     {
684       "name": "proc temps",
685       "interface": "xyz.openbmc_project.Sensor.Value",
686       "property": { "name": "Value" }
687     }
688   ],
689   "actions": [
690     {
691       "name": "set_net_increase_target",
692       "state": 30,
693       "delta": 100
694     }
695   ]
696}
697```
698
699The above config will make the GetManagedObjects call on all services that own
700the configured groups and then add all resulting property values to the object
701cache.  After that, it will call the `set_net_increase_target` action using the
702same groups.
703
704## Modifiers
705Modifiers are used by some actions to help calculate values.
706
707### minus
708Subtract the `value` field from the passed in value.
709
710```
711"modifier": {
712    "expression": "minus",
713    "value": 4
714}
715```
716
717The above config subtracts 4 from what is passed to it.
718
719### less_than
720Returns a value from a data table that is selected when the argument passed in
721is less than the `arg_value` entry in the table row.  If there is a
722`default_value` field supplied, then that will be returned if the argument is
723greater than the `arg_value` of the last row.
724
725```
726"modifier": {
727  "operator": "less_than",
728  "default_value": 10000,
729  "value": [
730      { "arg_value": 500, "parameter_value": 1 },
731      { "arg_value": 1000, "parameter_value": 2 },
732      { "arg_value": 1500, "parameter_value": 3 }
733  ]
734}
735```
736
737The above config returns 1 if the arg passed is less than 500, 2 if less than
7381000, and 3 if less than 1500.  Otherwise returns 10000, the default value.
739