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