xref: /openbmc/phosphor-fan-presence/control/gen-fan-zone-defs.py (revision 6c050693b6ac5a067013baadce62007e42678b6c)
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 set speed actions and function parameters
397    event['action'] = getActions(e, e, events_data)
398
399    # Add signal handlers
400    signals = []
401    for group in event['groups']:
402        for member in group['members']:
403            for eMatches in e['matches']:
404                signal = {}
405                eMatch = next(m for m in events_data['matches']
406                              if m['name'] == eMatches['name'])
407                signal['match'] = eMatch['name']
408                params = []
409                if ('parameters' in eMatch) and \
410                   (eMatch['parameters'] is not None):
411                    for p in eMatch['parameters']:
412                        params.append(member[str(p)])
413                signal['mparams'] = params
414                eSignal = next(s for s in events_data['signals']
415                               if s['name'] == eMatch['signal'])
416                signal['signal'] = eSignal['name']
417                sparams = {}
418                if ('parameters' in eSignal) and \
419                   (eSignal['parameters'] is not None):
420                    splist = []
421                    for p in eSignal['parameters']:
422                        sp = str(p)
423                        if (sp != 'type'):
424                            splist.append(sp)
425                            if (sp != 'group'):
426                                sparams[sp] = "\"" + member[sp] + "\""
427                            else:
428                                sparams[sp] = "Group{\n"
429                                for m in group['members']:
430                                    sparams[sp] += (
431                                        "{\n" +
432                                        "\"" + str(m['object']) + "\",\n" +
433                                        "{\"" + str(m['interface']) + "\"," +
434                                        "\"" + str(m['property']) + "\"}\n" +
435                                        "},\n")
436                                sparams[sp] += "}"
437                        else:
438                            sparams[sp] = member[sp]
439                    sparams['params'] = splist
440                signal['sparams'] = sparams
441                # Add signal handler
442                eHandler = next(h for h in events_data['handlers']
443                                if h['name'] == eSignal['handler'])
444                signal['handler'] = eHandler['name']
445                hparams = {}
446                if ('parameters' in eHandler) and \
447                   (eHandler['parameters'] is not None):
448                    hplist = []
449                    for p in eHandler['parameters']:
450                        hp = str(p)
451                        if (hp != 'type'):
452                            hplist.append(hp)
453                            if (hp != 'group'):
454                                hparams[hp] = "\"" + member[hp] + "\""
455                            else:
456                                hparams[hp] = "Group{\n"
457                                for m in group['members']:
458                                    hparams[hp] += (
459                                        "{\n" +
460                                        "\"" + str(m['object']) + "\",\n" +
461                                        "{\"" + str(m['interface']) + "\"," +
462                                        "\"" + str(m['property']) + "\"}\n" +
463                                        "},\n")
464                                hparams[hp] += "}"
465                        else:
466                            hparams[hp] = member[hp]
467                    hparams['params'] = hplist
468                signal['hparams'] = hparams
469                signals.append(signal)
470    event['signals'] = signals
471
472    # Add optional action call timer
473    timer = {}
474    interval = "static_cast<std::chrono::seconds>"
475    if ('timer' in e) and \
476       (e['timer'] is not None):
477        timer['interval'] = (interval +
478                             "(" +
479                             str(e['timer']['interval']) +
480                             ")")
481    else:
482        timer['interval'] = (interval +
483                             "(" + str(0) + ")")
484    timer['type'] = "util::Timer::TimerType::repeating"
485    event['timer'] = timer
486
487    return event
488
489
490def addPrecondition(zNum, zCond, event, events_data):
491    """
492    Parses the precondition section of an event and populates the necessary
493    structures to generate a precondition for a set speed event.
494    """
495    precond = {}
496    # Add set speed event precondition group
497    grps = getGroups(zNum, zCond, event['precondition'], events_data)
498    if not grps:
499        return
500    precond['pcgrps'] = grps
501
502    # Add set speed event precondition actions
503    pc = []
504    pcs = {}
505    pcs['name'] = event['precondition']['name']
506    epc = next(p for p in events_data['preconditions']
507               if p['name'] == event['precondition']['name'])
508    params = []
509    for p in epc['parameters']:
510        param = {}
511        if p == 'groups':
512            param['type'] = "std::vector<PrecondGroup>"
513            param['open'] = "{"
514            param['close'] = "}"
515            values = []
516            for group in precond['pcgrps']:
517                for pcgrp in group['members']:
518                    value = {}
519                    value['value'] = (
520                        "PrecondGroup{\"" +
521                        str(pcgrp['object']) + "\",\"" +
522                        str(pcgrp['interface']) + "\",\"" +
523                        str(pcgrp['property']) + "\"," +
524                        "static_cast<" +
525                        str(pcgrp['type']).lower() + ">")
526                    if isinstance(pcgrp['value'], str) or \
527                       "string" in str(pcgrp['type']).lower():
528                        value['value'] += ("(" + str(pcgrp['value']) + ")}")
529                    else:
530                        value['value'] += \
531                            ("(" + str(pcgrp['value']).lower() + ")}")
532                    values.append(value)
533            param['values'] = values
534        params.append(param)
535    pcs['params'] = params
536    pc.append(pcs)
537    precond['pcact'] = pc
538
539    pcevents = []
540    for pce in event['precondition']['events']:
541        pcevent = getEvent(zNum, zCond, pce, events_data)
542        if not pcevent:
543            continue
544        pcevents.append(pcevent)
545    precond['pcevts'] = pcevents
546
547    # Add precondition signal handlers
548    signals = []
549    for group in precond['pcgrps']:
550        for member in group['members']:
551            for eMatches in event['precondition']['matches']:
552                signal = {}
553                eMatch = next(m for m in events_data['matches']
554                              if m['name'] == eMatches['name'])
555                signal['match'] = eMatch['name']
556                params = []
557                if ('parameters' in eMatch) and \
558                   (eMatch['parameters'] is not None):
559                    for p in eMatch['parameters']:
560                        params.append(member[str(p)])
561                signal['mparams'] = params
562                eSignal = next(s for s in events_data['signals']
563                               if s['name'] == eMatch['signal'])
564                signal['signal'] = eSignal['name']
565                sparams = {}
566                if ('parameters' in eSignal) and \
567                   (eSignal['parameters'] is not None):
568                    splist = []
569                    for p in eSignal['parameters']:
570                        sp = str(p)
571                        if (sp != 'type'):
572                            splist.append(sp)
573                            if (sp != 'group'):
574                                sparams[sp] = "\"" + member[sp] + "\""
575                            else:
576                                sparams[sp] = "Group{\n"
577                                for m in group:
578                                    sparams[sp] += (
579                                        "{\n" +
580                                        "\"" + str(m['object']) + "\",\n" +
581                                        "{\"" + str(m['interface']) + "\"," +
582                                        "\"" + str(m['property']) + "\"}\n" +
583                                        "},\n")
584                                sparams[sp] += "}"
585                        else:
586                            sparams[sp] = member[sp]
587                    sparams['params'] = splist
588                signal['sparams'] = sparams
589                # Add signal handler
590                eHandler = next(h for h in events_data['handlers']
591                                if h['name'] == eSignal['handler'])
592                signal['handler'] = eHandler['name']
593                hparams = {}
594                if ('parameters' in eHandler) and \
595                   (eHandler['parameters'] is not None):
596                    hplist = []
597                    for p in eHandler['parameters']:
598                        hp = str(p)
599                        if (hp != 'type'):
600                            hplist.append(hp)
601                            if (hp != 'group'):
602                                hparams[hp] = "\"" + member[hp] + "\""
603                            else:
604                                hparams[hp] = "Group{\n"
605                                for m in group:
606                                    hparams[hp] += (
607                                        "{\n" +
608                                        "\"" + str(m['object']) + "\",\n" +
609                                        "{\"" + str(m['interface']) + "\"," +
610                                        "\"" + str(m['property']) + "\"}\n" +
611                                        "},\n")
612                                hparams[hp] += "}"
613                        else:
614                            hparams[hp] = member[hp]
615                    hparams['params'] = hplist
616                signal['hparams'] = hparams
617                signals.append(signal)
618    precond['pcsigs'] = signals
619
620    # Add optional action call timer
621    timer = {}
622    interval = "static_cast<std::chrono::seconds>"
623    if ('timer' in event['precondition']) and \
624       (event['precondition']['timer'] is not None):
625        timer['interval'] = (interval +
626                             "(" +
627                             str(event['precondition']['timer']['interval']) +
628                             ")")
629    else:
630        timer['interval'] = (interval +
631                             "(" + str(0) + ")")
632    timer['type'] = "util::Timer::TimerType::repeating"
633    precond['pctime'] = timer
634
635    return precond
636
637
638def getEventsInZone(zone_num, zone_conditions, events_data):
639    """
640    Constructs the event entries defined for each zone using the events yaml
641    provided.
642    """
643    events = []
644
645    if 'events' in events_data:
646        for e in events_data['events']:
647            event = {}
648            # Add precondition if given
649            if ('precondition' in e) and \
650               (e['precondition'] is not None):
651                event['pc'] = addPrecondition(zone_num,
652                                              zone_conditions,
653                                              e,
654                                              events_data)
655            else:
656                event = getEvent(zone_num, zone_conditions, e, events_data)
657                if not event:
658                    continue
659            events.append(event)
660
661    return events
662
663
664def getFansInZone(zone_num, profiles, fan_data):
665    """
666    Parses the fan definition YAML files to find the fans
667    that match both the zone passed in and one of the
668    cooling profiles.
669    """
670
671    fans = []
672
673    for f in fan_data['fans']:
674
675        if zone_num != f['cooling_zone']:
676            continue
677
678        # 'cooling_profile' is optional (use 'all' instead)
679        if f.get('cooling_profile') is None:
680            profile = "all"
681        else:
682            profile = f['cooling_profile']
683
684        if profile not in profiles:
685            continue
686
687        fan = {}
688        fan['name'] = f['inventory']
689        fan['sensors'] = f['sensors']
690        fan['target_interface'] = f.get(
691            'target_interface',
692            'xyz.openbmc_project.Control.FanSpeed')
693        fans.append(fan)
694
695    return fans
696
697
698def getConditionInZoneConditions(zone_condition, zone_conditions_data):
699    """
700    Parses the zone conditions definition YAML files to find the condition
701    that match both the zone condition passed in.
702    """
703
704    condition = {}
705
706    for c in zone_conditions_data['conditions']:
707
708        if zone_condition != c['name']:
709            continue
710        condition['type'] = c['type']
711        properties = []
712        for p in c['properties']:
713            property = {}
714            property['property'] = p['property']
715            property['interface'] = p['interface']
716            property['path'] = p['path']
717            property['type'] = p['type'].lower()
718            property['value'] = str(p['value']).lower()
719            properties.append(property)
720        condition['properties'] = properties
721
722        return condition
723
724
725def buildZoneData(zone_data, fan_data, events_data, zone_conditions_data):
726    """
727    Combines the zone definition YAML and fan
728    definition YAML to create a data structure defining
729    the fan cooling zones.
730    """
731
732    zone_groups = []
733
734    for group in zone_data:
735        conditions = []
736        # zone conditions are optional
737        if 'zone_conditions' in group and group['zone_conditions'] is not None:
738            for c in group['zone_conditions']:
739
740                if not zone_conditions_data:
741                    sys.exit("No zone_conditions YAML file but " +
742                             "zone_conditions used in zone YAML")
743
744                condition = getConditionInZoneConditions(c['name'],
745                                                         zone_conditions_data)
746
747                if not condition:
748                    sys.exit("Missing zone condition " + c['name'])
749
750                conditions.append(condition)
751
752        zone_group = {}
753        zone_group['conditions'] = conditions
754
755        zones = []
756        for z in group['zones']:
757            zone = {}
758
759            # 'zone' is required
760            if ('zone' not in z) or (z['zone'] is None):
761                sys.exit("Missing fan zone number in " + zone_yaml)
762
763            zone['num'] = z['zone']
764
765            zone['full_speed'] = z['full_speed']
766
767            zone['default_floor'] = z['default_floor']
768
769            # 'increase_delay' is optional (use 0 by default)
770            key = 'increase_delay'
771            zone[key] = z.setdefault(key, 0)
772
773            # 'decrease_interval' is optional (use 0 by default)
774            key = 'decrease_interval'
775            zone[key] = z.setdefault(key, 0)
776
777            # 'cooling_profiles' is optional (use 'all' instead)
778            if ('cooling_profiles' not in z) or \
779                    (z['cooling_profiles'] is None):
780                profiles = ["all"]
781            else:
782                profiles = z['cooling_profiles']
783
784            fans = getFansInZone(z['zone'], profiles, fan_data)
785            events = getEventsInZone(z['zone'], group['zone_conditions'],
786                                     events_data)
787
788            if len(fans) == 0:
789                sys.exit("Didn't find any fans in zone " + str(zone['num']))
790
791            zone['fans'] = fans
792            zone['events'] = events
793            zones.append(zone)
794
795        zone_group['zones'] = zones
796        zone_groups.append(zone_group)
797
798    return zone_groups
799
800
801if __name__ == '__main__':
802    parser = ArgumentParser(
803        description="Phosphor fan zone definition parser")
804
805    parser.add_argument('-z', '--zone_yaml', dest='zone_yaml',
806                        default="example/zones.yaml",
807                        help='fan zone definitional yaml')
808    parser.add_argument('-f', '--fan_yaml', dest='fan_yaml',
809                        default="example/fans.yaml",
810                        help='fan definitional yaml')
811    parser.add_argument('-e', '--events_yaml', dest='events_yaml',
812                        help='events to set speeds yaml')
813    parser.add_argument('-c', '--zone_conditions_yaml',
814                        dest='zone_conditions_yaml',
815                        help='conditions to determine zone yaml')
816    parser.add_argument('-o', '--output_dir', dest='output_dir',
817                        default=".",
818                        help='output directory')
819    args = parser.parse_args()
820
821    if not args.zone_yaml or not args.fan_yaml:
822        parser.print_usage()
823        sys.exit(-1)
824
825    with open(args.zone_yaml, 'r') as zone_input:
826        zone_data = yaml.safe_load(zone_input) or {}
827
828    with open(args.fan_yaml, 'r') as fan_input:
829        fan_data = yaml.safe_load(fan_input) or {}
830
831    events_data = {}
832    if args.events_yaml:
833        with open(args.events_yaml, 'r') as events_input:
834            events_data = yaml.safe_load(events_input) or {}
835
836    zone_conditions_data = {}
837    if args.zone_conditions_yaml:
838        with open(args.zone_conditions_yaml, 'r') as zone_conditions_input:
839            zone_conditions_data = yaml.safe_load(zone_conditions_input) or {}
840
841    zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
842                                fan_data, events_data, zone_conditions_data)
843
844    manager_config = zone_data.get('manager_configuration', {})
845
846    if manager_config.get('power_on_delay') is None:
847        manager_config['power_on_delay'] = 0
848
849    output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
850    with open(output_file, 'w') as output:
851        output.write(Template(tmpl).render(zones=zone_config,
852                     mgr_data=manager_config))
853