xref: /openbmc/phosphor-fan-presence/control/gen-fan-zone-defs.py (revision e7d87053021411f908944555d8a1735c21c61b95)
1#!/usr/bin/env python
2
3"""
4This script reads in fan definition and zone definition YAML
5files and generates a set of structures for use by the fan control code.
6"""
7
8import os
9import sys
10import yaml
11from argparse import ArgumentParser
12from mako.template import Template
13
14tmpl = '''\
15<%!
16def indent(str, depth):
17    return ''.join(4*' '*depth+line for line in str.splitlines(True))
18%>\
19<%def name="genHandler(sig)" buffered="True">
20%if ('type' in sig['sparams']) and \
21    (sig['sparams']['type'] is not None):
22${sig['signal']}<${sig['sparams']['type']}>(
23%else:
24${sig['signal']}(
25%endif
26%for spk in sig['sparams']['params']:
27${sig['sparams'][spk]},
28%endfor
29%if ('type' in sig['hparams']) and \
30    (sig['hparams']['type'] is not None):
31handler::${sig['handler']}<${sig['hparams']['type']}>(
32%else:
33handler::${sig['handler']}(
34%endif
35%for i, hpk in enumerate(sig['hparams']['params']):
36    %if (i+1) != len(sig['hparams']['params']):
37    ${sig['hparams'][hpk]},
38    %else:
39    ${sig['hparams'][hpk]}
40    %endif
41%endfor
42))
43</%def>\
44<%def name="genSSE(event)" buffered="True">
45Group{
46%for group in event['groups']:
47%for member in group['members']:
48{
49    "${member['object']}",
50    {"${member['interface']}",
51     "${member['property']}"}
52},
53%endfor
54%endfor
55},
56std::vector<Action>{
57%for a in event['action']:
58%if len(a['parameters']) != 0:
59make_action(action::${a['name']}(
60%else:
61make_action(action::${a['name']}
62%endif
63%for i, p in enumerate(a['parameters']):
64%if (i+1) != len(a['parameters']):
65    ${p},
66%else:
67    ${p})
68%endif
69%endfor
70),
71%endfor
72},
73Timer{
74    ${event['timer']['interval']},
75    ${event['timer']['type']}
76},
77std::vector<Signal>{
78%for s in event['signals']:
79    Signal{
80        match::${s['match']}(
81        %for i, mp in enumerate(s['mparams']):
82        %if (i+1) != len(s['mparams']):
83        "${mp}",
84        %else:
85        "${mp}"
86        %endif
87        %endfor
88        ),
89        make_handler(\
90        ${indent(genHandler(sig=s), 3)}\
91        )
92    },
93%endfor
94}
95</%def>\
96/* This is a generated file. */
97#include "manager.hpp"
98#include "functor.hpp"
99#include "actions.hpp"
100#include "handlers.hpp"
101#include "preconditions.hpp"
102#include "matches.hpp"
103
104using namespace phosphor::fan::control;
105
106const unsigned int Manager::_powerOnDelay{${mgr_data['power_on_delay']}};
107
108const std::vector<ZoneGroup> Manager::_zoneLayouts
109{
110%for zone_group in zones:
111    ZoneGroup{
112        std::vector<Condition>{
113        %for condition in zone_group['conditions']:
114            Condition{
115                "${condition['type']}",
116                std::vector<ConditionProperty>{
117                %for property in condition['properties']:
118                    ConditionProperty{
119                        "${property['property']}",
120                        "${property['interface']}",
121                        "${property['path']}",
122                        static_cast<${property['type']}>(${property['value']}),
123                    },
124                    %endfor
125                },
126            },
127            %endfor
128        },
129        std::vector<ZoneDefinition>{
130        %for zone in zone_group['zones']:
131            ZoneDefinition{
132                ${zone['num']},
133                ${zone['full_speed']},
134                ${zone['default_floor']},
135                ${zone['increase_delay']},
136                ${zone['decrease_interval']},
137                std::vector<FanDefinition>{
138                %for fan in zone['fans']:
139                    FanDefinition{
140                        "${fan['name']}",
141                        std::vector<std::string>{
142                        %for sensor in fan['sensors']:
143                            "${sensor}",
144                        %endfor
145                        },
146                        "${fan['target_interface']}"
147                    },
148                %endfor
149                },
150                std::vector<SetSpeedEvent>{
151                %for event in zone['events']:
152                    %if ('pc' in event) and \
153                        (event['pc'] is not None):
154                    SetSpeedEvent{
155                        Group{
156                        %for group in event['pc']['pcgrps']:
157                        %for member in group['members']:
158                        {
159                            "${member['object']}",
160                            {"${member['interface']}",
161                             "${member['property']}"}
162                        },
163                        %endfor
164                        %endfor
165                        },
166                        std::vector<Action>{
167                        %for i, a in enumerate(event['pc']['pcact']):
168                        %if len(a['params']) != 0:
169                        make_action(
170                            precondition::${a['name']}(
171                        %else:
172                        make_action(
173                            precondition::${a['name']}
174                        %endif
175                        %for p in a['params']:
176                        ${p['type']}${p['open']}
177                        %for j, v in enumerate(p['values']):
178                        %if (j+1) != len(p['values']):
179                            ${v['value']},
180                        %else:
181                            ${v['value']}
182                        %endif
183                        %endfor
184                        ${p['close']},
185                        %endfor
186                        %if (i+1) != len(event['pc']['pcact']):
187                        %if len(a['params']) != 0:
188                        )),
189                        %else:
190                        ),
191                        %endif
192                        %endif
193                        %endfor
194                    std::vector<SetSpeedEvent>{
195                    %for pcevt in event['pc']['pcevts']:
196                    SetSpeedEvent{\
197                    ${indent(genSSE(event=pcevt), 6)}\
198                    },
199                    %endfor
200                    %else:
201                    SetSpeedEvent{\
202                    ${indent(genSSE(event=event), 6)}
203                    %endif
204                    %if ('pc' in event) and (event['pc'] is not None):
205                    }
206                        %if len(event['pc']['pcact'][-1]['params']) != 0:
207                        )),
208                        %else:
209                        ),
210                        %endif
211                        },
212                        Timer{
213                            ${event['pc']['pctime']['interval']},
214                            ${event['pc']['pctime']['type']}
215                        },
216                        std::vector<Signal>{
217                        %for s in event['pc']['pcsigs']:
218                            Signal{
219                                match::${s['match']}(
220                                %for i, mp in enumerate(s['mparams']):
221                                %if (i+1) != len(s['mparams']):
222                                "${mp}",
223                                %else:
224                                "${mp}"
225                                %endif
226                                %endfor
227                                ),
228                                make_handler(\
229                                ${indent(genHandler(sig=s), 9)}\
230                                )
231                            },
232                        %endfor
233                        }
234                    %endif
235                    },
236                %endfor
237                }
238            },
239        %endfor
240        }
241    },
242%endfor
243};
244'''
245
246
247def convertToMap(listOfDict):
248    """
249    Converts a list of dictionary entries to a std::map initialization list.
250    """
251    listOfDict = listOfDict.replace('\'', '\"')
252    listOfDict = listOfDict.replace('[', '{')
253    listOfDict = listOfDict.replace(']', '}')
254    listOfDict = listOfDict.replace(':', ',')
255    return listOfDict
256
257
258def getGroups(zNum, zCond, edata, events):
259    """
260    Extract and construct the groups for the given event.
261    """
262    groups = []
263    for eGroups in edata['groups']:
264        if ('zone_conditions' in eGroups) and \
265           (eGroups['zone_conditions'] is not None):
266            # Zone conditions are optional in the events yaml but skip
267            # if this event's condition is not in this zone's conditions
268            if all('name' in z and z['name'] is not None and
269                   not any(c['name'] == z['name'] for c in zCond)
270                   for z in eGroups['zone_conditions']):
271                continue
272
273            # Zone numbers are optional in the events yaml but skip if this
274            # zone's zone number is not in the event's zone numbers
275            if all('zones' in z and z['zones'] is not None and
276                   zNum not in z['zones']
277                   for z in eGroups['zone_conditions']):
278                continue
279
280        eGroup = next(g for g in events['groups']
281                      if g['name'] == eGroups['name'])
282
283        group = {}
284        members = []
285        group['name'] = eGroup['name']
286        for m in eGroup['members']:
287            member = {}
288            member['path'] = eGroup['type']
289            member['object'] = (eGroup['type'] + m)
290            member['interface'] = eGroups['interface']
291            member['property'] = eGroups['property']['name']
292            member['type'] = eGroups['property']['type']
293            # Add expected group member's property value if given
294            if ('value' in eGroups['property']) and \
295               (eGroups['property']['value'] is not None):
296                    if isinstance(eGroups['property']['value'], str) or \
297                            "string" in str(member['type']).lower():
298                        member['value'] = (
299                            "\"" + eGroups['property']['value'] + "\"")
300                    else:
301                        member['value'] = eGroups['property']['value']
302            members.append(member)
303        group['members'] = members
304        groups.append(group)
305    return groups
306
307
308def getActions(edata, actions, events):
309    """
310    Extracts and constructs the make_action function call for
311    all the actions within the given event.
312    """
313    action = []
314    for eActions in actions['actions']:
315        actions = {}
316        eAction = next(a for a in events['actions']
317                       if a['name'] == eActions['name'])
318        actions['name'] = eAction['name']
319        params = []
320        if ('parameters' in eAction) and \
321           (eAction['parameters'] is not None):
322            for p in eAction['parameters']:
323                param = "static_cast<"
324                if type(eActions[p]) is not dict:
325                    if p == 'actions':
326                        param = "std::vector<Action>{"
327                        pActs = getActions(edata, eActions, events)
328                        for a in pActs:
329                            if (len(a['parameters']) != 0):
330                                param += (
331                                    "make_action(action::" +
332                                    a['name'] +
333                                    "(\n")
334                                for i, ap in enumerate(a['parameters']):
335                                    if (i+1) != len(a['parameters']):
336                                        param += (ap + ",")
337                                    else:
338                                        param += (ap + ")")
339                            else:
340                                param += ("make_action(action::" + a['name'])
341                            param += "),"
342                        param += "}"
343                    elif p == 'property':
344                        if isinstance(eActions[p], str) or \
345                           "string" in str(eActions[p]['type']).lower():
346                            param += (
347                                str(eActions[p]['type']).lower() +
348                                ">(\"" + str(eActions[p]) + "\")")
349                        else:
350                            param += (
351                                str(eActions[p]['type']).lower() +
352                                ">(" + str(eActions[p]['value']).lower() + ")")
353                    else:
354                        # Default type to 'size_t' when not given
355                        param += ("size_t>(" + str(eActions[p]).lower() + ")")
356                else:
357                    if p == 'timer':
358                        param = (
359                            "Timer{static_cast<std::chrono::seconds>(" +
360                            str(eActions[p]['delay']) + "), " +
361                            "util::Timer::TimerType::" +
362                            str(eActions[p]['type']) + "}")
363                    else:
364                        param += (str(eActions[p]['type']).lower() + ">(")
365                        if p != 'map':
366                            if isinstance(eActions[p]['value'], str) or \
367                               "string" in str(eActions[p]['type']).lower():
368                                param += \
369                                    "\"" + str(eActions[p]['value']) + "\")"
370                            else:
371                                param += \
372                                    str(eActions[p]['value']).lower() + ")"
373                        else:
374                            param += (
375                                str(eActions[p]['type']).lower() +
376                                convertToMap(str(eActions[p]['value'])) + ")")
377                params.append(param)
378        actions['parameters'] = params
379        action.append(actions)
380    return action
381
382
383def getEvent(zone_num, zone_conditions, e, events_data):
384    """
385    Parses the sections of an event and populates the properties
386    that construct an event within the generated source.
387    """
388    event = {}
389
390    # Add set speed event groups
391    grps = getGroups(zone_num, zone_conditions, e, events_data)
392    if not grps:
393        return
394    event['groups'] = grps
395
396    # Add optional set speed actions and function parameters
397    event['action'] = []
398    if ('actions' in e) and \
399       (e['actions'] is not None):
400        event['action'] = getActions(e, e, events_data)
401
402    # Add signal handlers
403    signals = []
404    for group in event['groups']:
405        for member in group['members']:
406            for eMatches in e['matches']:
407                signal = {}
408                eMatch = next(m for m in events_data['matches']
409                              if m['name'] == eMatches['name'])
410                signal['match'] = eMatch['name']
411                params = []
412                if ('parameters' in eMatch) and \
413                   (eMatch['parameters'] is not None):
414                    for p in eMatch['parameters']:
415                        params.append(member[str(p)])
416                signal['mparams'] = params
417                if ('parameters' in eMatch['signal']) and \
418                   (eMatch['signal']['parameters'] is not None):
419                    eSignal = eMatch['signal']
420                else:
421                    eSignal = next(s for s in events_data['signals']
422                                   if s['name'] == eMatch['signal'])
423                signal['signal'] = eSignal['name']
424                sparams = {}
425                if ('parameters' in eSignal) and \
426                   (eSignal['parameters'] is not None):
427                    splist = []
428                    for p in eSignal['parameters']:
429                        sp = str(p)
430                        if (sp != 'type'):
431                            splist.append(sp)
432                            if (sp != 'group'):
433                                sparams[sp] = "\"" + member[sp] + "\""
434                            else:
435                                sparams[sp] = "Group{\n"
436                                for m in group['members']:
437                                    sparams[sp] += (
438                                        "{\n" +
439                                        "\"" + str(m['object']) + "\",\n" +
440                                        "{\"" + str(m['interface']) + "\"," +
441                                        "\"" + str(m['property']) + "\"}\n" +
442                                        "},\n")
443                                sparams[sp] += "}"
444                        else:
445                            sparams[sp] = member[sp]
446                    sparams['params'] = splist
447                signal['sparams'] = sparams
448                # Add signal handler
449                eHandler = next(h for h in events_data['handlers']
450                                if h['name'] == eSignal['handler'])
451                signal['handler'] = eHandler['name']
452                hparams = {}
453                if ('parameters' in eHandler) and \
454                   (eHandler['parameters'] is not None):
455                    hplist = []
456                    for p in eHandler['parameters']:
457                        hp = str(p)
458                        if (hp != 'type'):
459                            hplist.append(hp)
460                            if (hp != 'group'):
461                                hparams[hp] = "\"" + member[hp] + "\""
462                            else:
463                                hparams[hp] = "Group{\n"
464                                for m in group['members']:
465                                    hparams[hp] += (
466                                        "{\n" +
467                                        "\"" + str(m['object']) + "\",\n" +
468                                        "{\"" + str(m['interface']) + "\"," +
469                                        "\"" + str(m['property']) + "\"}\n" +
470                                        "},\n")
471                                hparams[hp] += "}"
472                        else:
473                            hparams[hp] = member[hp]
474                    hparams['params'] = hplist
475                signal['hparams'] = hparams
476                signals.append(signal)
477    event['signals'] = signals
478
479    # Add optional action call timer
480    timer = {}
481    interval = "static_cast<std::chrono::seconds>"
482    if ('timer' in e) and \
483       (e['timer'] is not None):
484        timer['interval'] = (interval +
485                             "(" +
486                             str(e['timer']['interval']) +
487                             ")")
488    else:
489        timer['interval'] = (interval +
490                             "(" + str(0) + ")")
491    timer['type'] = "util::Timer::TimerType::repeating"
492    event['timer'] = timer
493
494    return event
495
496
497def addPrecondition(zNum, zCond, event, events_data):
498    """
499    Parses the precondition section of an event and populates the necessary
500    structures to generate a precondition for a set speed event.
501    """
502    precond = {}
503    # Add set speed event precondition group
504    grps = getGroups(zNum, zCond, event['precondition'], events_data)
505    if not grps:
506        return
507    precond['pcgrps'] = grps
508
509    # Add set speed event precondition actions
510    pc = []
511    pcs = {}
512    pcs['name'] = event['precondition']['name']
513    epc = next(p for p in events_data['preconditions']
514               if p['name'] == event['precondition']['name'])
515    params = []
516    for p in epc['parameters']:
517        param = {}
518        if p == 'groups':
519            param['type'] = "std::vector<PrecondGroup>"
520            param['open'] = "{"
521            param['close'] = "}"
522            values = []
523            for group in precond['pcgrps']:
524                for pcgrp in group['members']:
525                    value = {}
526                    value['value'] = (
527                        "PrecondGroup{\"" +
528                        str(pcgrp['object']) + "\",\"" +
529                        str(pcgrp['interface']) + "\",\"" +
530                        str(pcgrp['property']) + "\"," +
531                        "static_cast<" +
532                        str(pcgrp['type']).lower() + ">")
533                    if isinstance(pcgrp['value'], str) or \
534                       "string" in str(pcgrp['type']).lower():
535                        value['value'] += ("(" + str(pcgrp['value']) + ")}")
536                    else:
537                        value['value'] += \
538                            ("(" + str(pcgrp['value']).lower() + ")}")
539                    values.append(value)
540            param['values'] = values
541        params.append(param)
542    pcs['params'] = params
543    pc.append(pcs)
544    precond['pcact'] = pc
545
546    pcevents = []
547    for pce in event['precondition']['events']:
548        pcevent = getEvent(zNum, zCond, pce, events_data)
549        if not pcevent:
550            continue
551        pcevents.append(pcevent)
552    precond['pcevts'] = pcevents
553
554    # Add precondition signal handlers
555    signals = []
556    for group in precond['pcgrps']:
557        for member in group['members']:
558            for eMatches in event['precondition']['matches']:
559                signal = {}
560                eMatch = next(m for m in events_data['matches']
561                              if m['name'] == eMatches['name'])
562                signal['match'] = eMatch['name']
563                params = []
564                if ('parameters' in eMatch) and \
565                   (eMatch['parameters'] is not None):
566                    for p in eMatch['parameters']:
567                        params.append(member[str(p)])
568                signal['mparams'] = params
569                if ('parameters' in eMatch['signal']) and \
570                   (eMatch['signal']['parameters'] is not None):
571                    eSignal = eMatch['signal']
572                else:
573                    eSignal = next(s for s in events_data['signals']
574                                   if s['name'] == eMatch['signal'])
575                signal['signal'] = eSignal['name']
576                sparams = {}
577                if ('parameters' in eSignal) and \
578                   (eSignal['parameters'] is not None):
579                    splist = []
580                    for p in eSignal['parameters']:
581                        sp = str(p)
582                        if (sp != 'type'):
583                            splist.append(sp)
584                            if (sp != 'group'):
585                                sparams[sp] = "\"" + member[sp] + "\""
586                            else:
587                                sparams[sp] = "Group{\n"
588                                for m in group:
589                                    sparams[sp] += (
590                                        "{\n" +
591                                        "\"" + str(m['object']) + "\",\n" +
592                                        "{\"" + str(m['interface']) + "\"," +
593                                        "\"" + str(m['property']) + "\"}\n" +
594                                        "},\n")
595                                sparams[sp] += "}"
596                        else:
597                            sparams[sp] = member[sp]
598                    sparams['params'] = splist
599                signal['sparams'] = sparams
600                # Add signal handler
601                eHandler = next(h for h in events_data['handlers']
602                                if h['name'] == eSignal['handler'])
603                signal['handler'] = eHandler['name']
604                hparams = {}
605                if ('parameters' in eHandler) and \
606                   (eHandler['parameters'] is not None):
607                    hplist = []
608                    for p in eHandler['parameters']:
609                        hp = str(p)
610                        if (hp != 'type'):
611                            hplist.append(hp)
612                            if (hp != 'group'):
613                                hparams[hp] = "\"" + member[hp] + "\""
614                            else:
615                                hparams[hp] = "Group{\n"
616                                for m in group:
617                                    hparams[hp] += (
618                                        "{\n" +
619                                        "\"" + str(m['object']) + "\",\n" +
620                                        "{\"" + str(m['interface']) + "\"," +
621                                        "\"" + str(m['property']) + "\"}\n" +
622                                        "},\n")
623                                hparams[hp] += "}"
624                        else:
625                            hparams[hp] = member[hp]
626                    hparams['params'] = hplist
627                signal['hparams'] = hparams
628                signals.append(signal)
629    precond['pcsigs'] = signals
630
631    # Add optional action call timer
632    timer = {}
633    interval = "static_cast<std::chrono::seconds>"
634    if ('timer' in event['precondition']) and \
635       (event['precondition']['timer'] is not None):
636        timer['interval'] = (interval +
637                             "(" +
638                             str(event['precondition']['timer']['interval']) +
639                             ")")
640    else:
641        timer['interval'] = (interval +
642                             "(" + str(0) + ")")
643    timer['type'] = "util::Timer::TimerType::repeating"
644    precond['pctime'] = timer
645
646    return precond
647
648
649def getEventsInZone(zone_num, zone_conditions, events_data):
650    """
651    Constructs the event entries defined for each zone using the events yaml
652    provided.
653    """
654    events = []
655
656    if 'events' in events_data:
657        for e in events_data['events']:
658            event = {}
659            # Add precondition if given
660            if ('precondition' in e) and \
661               (e['precondition'] is not None):
662                event['pc'] = addPrecondition(zone_num,
663                                              zone_conditions,
664                                              e,
665                                              events_data)
666            else:
667                event = getEvent(zone_num, zone_conditions, e, events_data)
668                if not event:
669                    continue
670            events.append(event)
671
672    return events
673
674
675def getFansInZone(zone_num, profiles, fan_data):
676    """
677    Parses the fan definition YAML files to find the fans
678    that match both the zone passed in and one of the
679    cooling profiles.
680    """
681
682    fans = []
683
684    for f in fan_data['fans']:
685
686        if zone_num != f['cooling_zone']:
687            continue
688
689        # 'cooling_profile' is optional (use 'all' instead)
690        if f.get('cooling_profile') is None:
691            profile = "all"
692        else:
693            profile = f['cooling_profile']
694
695        if profile not in profiles:
696            continue
697
698        fan = {}
699        fan['name'] = f['inventory']
700        fan['sensors'] = f['sensors']
701        fan['target_interface'] = f.get(
702            'target_interface',
703            'xyz.openbmc_project.Control.FanSpeed')
704        fans.append(fan)
705
706    return fans
707
708
709def getConditionInZoneConditions(zone_condition, zone_conditions_data):
710    """
711    Parses the zone conditions definition YAML files to find the condition
712    that match both the zone condition passed in.
713    """
714
715    condition = {}
716
717    for c in zone_conditions_data['conditions']:
718
719        if zone_condition != c['name']:
720            continue
721        condition['type'] = c['type']
722        properties = []
723        for p in c['properties']:
724            property = {}
725            property['property'] = p['property']
726            property['interface'] = p['interface']
727            property['path'] = p['path']
728            property['type'] = p['type'].lower()
729            property['value'] = str(p['value']).lower()
730            properties.append(property)
731        condition['properties'] = properties
732
733        return condition
734
735
736def buildZoneData(zone_data, fan_data, events_data, zone_conditions_data):
737    """
738    Combines the zone definition YAML and fan
739    definition YAML to create a data structure defining
740    the fan cooling zones.
741    """
742
743    zone_groups = []
744
745    for group in zone_data:
746        conditions = []
747        # zone conditions are optional
748        if 'zone_conditions' in group and group['zone_conditions'] is not None:
749            for c in group['zone_conditions']:
750
751                if not zone_conditions_data:
752                    sys.exit("No zone_conditions YAML file but " +
753                             "zone_conditions used in zone YAML")
754
755                condition = getConditionInZoneConditions(c['name'],
756                                                         zone_conditions_data)
757
758                if not condition:
759                    sys.exit("Missing zone condition " + c['name'])
760
761                conditions.append(condition)
762
763        zone_group = {}
764        zone_group['conditions'] = conditions
765
766        zones = []
767        for z in group['zones']:
768            zone = {}
769
770            # 'zone' is required
771            if ('zone' not in z) or (z['zone'] is None):
772                sys.exit("Missing fan zone number in " + zone_yaml)
773
774            zone['num'] = z['zone']
775
776            zone['full_speed'] = z['full_speed']
777
778            zone['default_floor'] = z['default_floor']
779
780            # 'increase_delay' is optional (use 0 by default)
781            key = 'increase_delay'
782            zone[key] = z.setdefault(key, 0)
783
784            # 'decrease_interval' is optional (use 0 by default)
785            key = 'decrease_interval'
786            zone[key] = z.setdefault(key, 0)
787
788            # 'cooling_profiles' is optional (use 'all' instead)
789            if ('cooling_profiles' not in z) or \
790                    (z['cooling_profiles'] is None):
791                profiles = ["all"]
792            else:
793                profiles = z['cooling_profiles']
794
795            fans = getFansInZone(z['zone'], profiles, fan_data)
796            events = getEventsInZone(z['zone'], group['zone_conditions'],
797                                     events_data)
798
799            if len(fans) == 0:
800                sys.exit("Didn't find any fans in zone " + str(zone['num']))
801
802            zone['fans'] = fans
803            zone['events'] = events
804            zones.append(zone)
805
806        zone_group['zones'] = zones
807        zone_groups.append(zone_group)
808
809    return zone_groups
810
811
812if __name__ == '__main__':
813    parser = ArgumentParser(
814        description="Phosphor fan zone definition parser")
815
816    parser.add_argument('-z', '--zone_yaml', dest='zone_yaml',
817                        default="example/zones.yaml",
818                        help='fan zone definitional yaml')
819    parser.add_argument('-f', '--fan_yaml', dest='fan_yaml',
820                        default="example/fans.yaml",
821                        help='fan definitional yaml')
822    parser.add_argument('-e', '--events_yaml', dest='events_yaml',
823                        help='events to set speeds yaml')
824    parser.add_argument('-c', '--zone_conditions_yaml',
825                        dest='zone_conditions_yaml',
826                        help='conditions to determine zone yaml')
827    parser.add_argument('-o', '--output_dir', dest='output_dir',
828                        default=".",
829                        help='output directory')
830    args = parser.parse_args()
831
832    if not args.zone_yaml or not args.fan_yaml:
833        parser.print_usage()
834        sys.exit(-1)
835
836    with open(args.zone_yaml, 'r') as zone_input:
837        zone_data = yaml.safe_load(zone_input) or {}
838
839    with open(args.fan_yaml, 'r') as fan_input:
840        fan_data = yaml.safe_load(fan_input) or {}
841
842    events_data = {}
843    if args.events_yaml:
844        with open(args.events_yaml, 'r') as events_input:
845            events_data = yaml.safe_load(events_input) or {}
846
847    zone_conditions_data = {}
848    if args.zone_conditions_yaml:
849        with open(args.zone_conditions_yaml, 'r') as zone_conditions_input:
850            zone_conditions_data = yaml.safe_load(zone_conditions_input) or {}
851
852    zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
853                                fan_data, events_data, zone_conditions_data)
854
855    manager_config = zone_data.get('manager_configuration', {})
856
857    if manager_config.get('power_on_delay') is None:
858        manager_config['power_on_delay'] = 0
859
860    output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
861    with open(output_file, 'w') as output:
862        output.write(Template(tmpl).render(zones=zone_config,
863                     mgr_data=manager_config))
864