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