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