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