blob: 1ccc420a8b08acb736d4cdf0aaaccf5addac922b [file] [log] [blame]
Matt Spinlerd08dbe22017-04-11 13:52:54 -05001#!/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
Matthew Barth702c4a52018-02-28 16:23:11 -060013from mako.lookup import TemplateLookup
Matt Spinlerd08dbe22017-04-11 13:52:54 -050014
Matt Spinler78498c92017-04-11 13:59:46 -050015
Matthew Barth7883f582019-02-14 14:24:46 -060016def 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
Matthew Barthbb12c922017-06-13 13:57:40 -0500124def convertToMap(listOfDict):
125 """
126 Converts a list of dictionary entries to a std::map initialization list.
127 """
Matthew Barth9a5b6992018-01-23 15:32:26 -0600128 listOfDict = listOfDict.replace('\'', '\"')
Matthew Barthbb12c922017-06-13 13:57:40 -0500129 listOfDict = listOfDict.replace('[', '{')
130 listOfDict = listOfDict.replace(']', '}')
131 listOfDict = listOfDict.replace(':', ',')
132 return listOfDict
133
134
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600135def 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"
Matthew Barth146b7392018-03-08 16:17:58 -0600146 e += "\"" + member['interface'] + "\",\n"
147 e += "\"" + member['property'] + "\"\n"
148 e += "},\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600149 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
Matthew Barth1b4de262018-03-06 13:03:16 -0600165 e += "std::vector<Trigger>{\n"
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600166 if ('timer' in event['triggers']) and \
167 (event['triggers']['timer'] is not None):
Matthew Barth1b4de262018-03-06 13:03:16 -0600168 e += "\tmake_trigger(trigger::timer(TimerConf{\n"
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600169 e += "\t" + event['triggers']['timer']['interval'] + ",\n"
170 e += "\t" + event['triggers']['timer']['type'] + "\n"
Matthew Barth016bd242018-03-07 16:06:06 -0600171 e += "\t})),\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600172
Matthew Barth016bd242018-03-07 16:06:06 -0600173 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"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600187 else:
Matthew Barth016bd242018-03-07 16:06:06 -0600188 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")
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600194 else:
Matthew Barth016bd242018-03-07 16:06:06 -0600195 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
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600205 e += "},\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600206
207 e += "}"
208
209 return e
210
211
Matthew Barth6c050692017-12-05 15:30:09 -0600212def 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
Matthew Barth6c050692017-12-05 15:30:09 -0600233 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']
Matthew Barth18c91032019-01-29 15:36:00 -0600246 # 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']
Matthew Barth6c050692017-12-05 15:30:09 -0600250 # 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
Matthew Barth73379f92018-03-15 11:37:10 -0500265def 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
Matthew Bartha69465a2018-03-02 13:50:59 -0600295def 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']
Matthew Barth73379f92018-03-15 11:37:10 -0500324 signal['sparams'] = getHandler(member, group, eSignal, events)
325
326 # Add handler parameters
Matthew Bartha69465a2018-03-02 13:50:59 -0600327 eHandler = next(h for h in events['handlers']
328 if h['name'] == eSignal['handler'])
329 signal['handler'] = eHandler['name']
Matthew Barth73379f92018-03-15 11:37:10 -0500330 signal['hparams'] = getHandler(member, group, eHandler, events)
331
Matthew Bartha69465a2018-03-02 13:50:59 -0600332 signals.append(signal)
333 return signals
334
335
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600336def 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
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600349def getActions(zNum, zCond, edata, actions, events):
Matthew Barth9df74752017-10-11 14:39:31 -0500350 """
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>{"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600368 pActs = getActions(zNum,
369 zCond,
370 edata,
371 eActions,
372 events)
Matthew Barth9df74752017-10-11 14:39:31 -0500373 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 += "}"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600388 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}"
Matthew Barth9df74752017-10-11 14:39:31 -0500399 elif p == 'property':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600400 if isinstance(eActions[p], str) or \
Matthew Barth6c050692017-12-05 15:30:09 -0600401 "string" in str(eActions[p]['type']).lower():
Matthew Barth9a5b6992018-01-23 15:32:26 -0600402 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600403 str(eActions[p]['type']).lower() +
Matthew Barth9a5b6992018-01-23 15:32:26 -0600404 ">(\"" + str(eActions[p]) + "\")")
405 else:
406 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600407 str(eActions[p]['type']).lower() +
408 ">(" + str(eActions[p]['value']).lower() + ")")
Matthew Barth9df74752017-10-11 14:39:31 -0500409 else:
410 # Default type to 'size_t' when not given
411 param += ("size_t>(" + str(eActions[p]).lower() + ")")
412 else:
413 if p == 'timer':
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600414 t = getTimer(eActions[p])
Matthew Barth9df74752017-10-11 14:39:31 -0500415 param = (
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600416 "TimerConf{" + t['interval'] + "," +
417 t['type'] + "}")
Matthew Barth9df74752017-10-11 14:39:31 -0500418 else:
419 param += (str(eActions[p]['type']).lower() + ">(")
420 if p != 'map':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600421 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() + ")"
Matthew Barth9df74752017-10-11 14:39:31 -0500428 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
Matthew Barth7f272fd2017-09-12 16:16:56 -0500438def 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 = {}
Matthew Barth7f272fd2017-09-12 16:16:56 -0500444
Matthew Barth6c050692017-12-05 15:30:09 -0600445 # Add set speed event groups
446 grps = getGroups(zone_num, zone_conditions, e, events_data)
447 if not grps:
Matthew Barth7f272fd2017-09-12 16:16:56 -0500448 return
Matthew Barth6c050692017-12-05 15:30:09 -0600449 event['groups'] = grps
Matthew Barth7f272fd2017-09-12 16:16:56 -0500450
Matthew Barthe3d1c4a2018-01-11 13:53:49 -0600451 # Add optional set speed actions and function parameters
452 event['action'] = []
453 if ('actions' in e) and \
454 (e['actions'] is not None):
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600455 event['action'] = getActions(zone_num,
456 zone_conditions,
457 e,
458 e,
459 events_data)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500460
Matthew Bartha69465a2018-03-02 13:50:59 -0600461 # Add event triggers
462 event['triggers'] = {}
463 for trig in e['triggers']:
464 triggers = []
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600465 if (trig['name'] == "timer"):
466 event['triggers']['timer'] = getTimer(trig)
467 elif (trig['name'] == "signal"):
Matthew Bartha69465a2018-03-02 13:50:59 -0600468 if ('signals' not in event['triggers']):
469 event['triggers']['signals'] = []
470 triggers = getSignal(event['groups'], trig, events_data)
471 event['triggers']['signals'].extend(triggers)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500472
Matthew Barth7f272fd2017-09-12 16:16:56 -0500473 return event
474
475
476def addPrecondition(zNum, zCond, event, events_data):
Matthew Barth9af190c2017-08-08 14:20:43 -0500477 """
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
Matthew Barth6c050692017-12-05 15:30:09 -0600483 grps = getGroups(zNum, zCond, event['precondition'], events_data)
484 if not grps:
485 return
486 precond['pcgrps'] = grps
Matthew Barth9af190c2017-08-08 14:20:43 -0500487
Matthew Barth7f272fd2017-09-12 16:16:56 -0500488 # 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']
Matthew Barth9af190c2017-08-08 14:20:43 -0500493 if p['name'] == event['precondition']['name'])
494 params = []
Matthew Barth7f272fd2017-09-12 16:16:56 -0500495 for p in epc['parameters']:
Matthew Barth9af190c2017-08-08 14:20:43 -0500496 param = {}
497 if p == 'groups':
498 param['type'] = "std::vector<PrecondGroup>"
499 param['open'] = "{"
500 param['close'] = "}"
501 values = []
Matthew Barth6c050692017-12-05 15:30:09 -0600502 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)
Matthew Barth9af190c2017-08-08 14:20:43 -0500519 param['values'] = values
520 params.append(param)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500521 pcs['params'] = params
522 pc.append(pcs)
Matthew Barth9af190c2017-08-08 14:20:43 -0500523 precond['pcact'] = pc
524
Matthew Barth7f272fd2017-09-12 16:16:56 -0500525 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
Matthew Barthf20c3212018-03-02 14:42:55 -0600533 # Add precondition event triggers
534 precond['triggers'] = {}
535 for trig in event['precondition']['triggers']:
536 triggers = []
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600537 if (trig['name'] == "timer"):
538 precond['triggers']['pctime'] = getTimer(trig)
539 elif (trig['name'] == "signal"):
Matthew Barthf20c3212018-03-02 14:42:55 -0600540 if ('pcsigs' not in precond['triggers']):
541 precond['triggers']['pcsigs'] = []
542 triggers = getSignal(precond['pcgrps'], trig, events_data)
543 precond['triggers']['pcsigs'].extend(triggers)
Matthew Barth9af190c2017-08-08 14:20:43 -0500544
545 return precond
546
547
Gunnar Millsb751f322017-06-06 15:14:11 -0500548def getEventsInZone(zone_num, zone_conditions, events_data):
Matthew Barthd4d0f082017-05-16 13:51:10 -0500549 """
550 Constructs the event entries defined for each zone using the events yaml
551 provided.
552 """
553 events = []
Matthew Barthba102b32017-05-16 16:13:56 -0500554
Matthew Barthd4d0f082017-05-16 13:51:10 -0500555 if 'events' in events_data:
556 for e in events_data['events']:
Matthew Barthd4d0f082017-05-16 13:51:10 -0500557 event = {}
Matthew Barth9af190c2017-08-08 14:20:43 -0500558 # Add precondition if given
559 if ('precondition' in e) and \
560 (e['precondition'] is not None):
Matthew Barth7f272fd2017-09-12 16:16:56 -0500561 event['pc'] = addPrecondition(zone_num,
562 zone_conditions,
563 e,
564 events_data)
Matthew Barth90149802017-08-15 10:51:37 -0500565 else:
Matthew Barth7f272fd2017-09-12 16:16:56 -0500566 event = getEvent(zone_num, zone_conditions, e, events_data)
567 if not event:
568 continue
Matthew Barthd4d0f082017-05-16 13:51:10 -0500569 events.append(event)
570
571 return events
572
573
Matt Spinler78498c92017-04-11 13:59:46 -0500574def 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
Gunnar Mills67e95512017-06-02 14:35:18 -0500588 # 'cooling_profile' is optional (use 'all' instead)
Matt Spinler78498c92017-04-11 13:59:46 -0500589 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']
Lei YU069e4402018-01-31 16:47:37 +0800600 fan['target_interface'] = f.get(
601 'target_interface',
602 'xyz.openbmc_project.Control.FanSpeed')
Matt Spinler78498c92017-04-11 13:59:46 -0500603 fans.append(fan)
604
605 return fans
606
607
Matthew Barth7883f582019-02-14 14:24:46 -0600608def 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 = {}
Matthew Barth59096e52019-02-18 12:23:38 -0600627 prop['name'] = p['name']
628 prop['func'] = str(p['name']).lower()
Matthew Barth7883f582019-02-14 14:24:46 -0600629 prop['type'] = parse_cpp_type(p['type'])
Matthew Barth59096e52019-02-18 12:23:38 -0600630 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'
Matthew Barth7883f582019-02-14 14:24:46 -0600637 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
Gunnar Millsee8a2812017-06-02 14:26:47 -0500656def 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):
Matt Spinler78498c92017-04-11 13:59:46 -0500684 """
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 = []
Gunnar Millsee8a2812017-06-02 14:26:47 -0500694 # 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:
Gunnar Millsb751f322017-06-06 15:14:11 -0500699 sys.exit("No zone_conditions YAML file but " +
Gunnar Millsee8a2812017-06-02 14:26:47 -0500700 "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)
Matt Spinler78498c92017-04-11 13:59:46 -0500709
710 zone_group = {}
711 zone_group['conditions'] = conditions
712
713 zones = []
714 for z in group['zones']:
715 zone = {}
716
Gunnar Mills67e95512017-06-02 14:35:18 -0500717 # 'zone' is required
718 if ('zone' not in z) or (z['zone'] is None):
Matt Spinler78498c92017-04-11 13:59:46 -0500719 sys.exit("Missing fan zone number in " + zone_yaml)
720
721 zone['num'] = z['zone']
722
723 zone['full_speed'] = z['full_speed']
724
Matthew Barth1de66622017-06-12 13:13:02 -0500725 zone['default_floor'] = z['default_floor']
726
Matthew Bartha9561842017-06-29 11:43:45 -0500727 # '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
Gunnar Mills67e95512017-06-02 14:35:18 -0500735 # 'cooling_profiles' is optional (use 'all' instead)
736 if ('cooling_profiles' not in z) or \
Matt Spinler78498c92017-04-11 13:59:46 -0500737 (z['cooling_profiles'] is None):
738 profiles = ["all"]
739 else:
740 profiles = z['cooling_profiles']
741
Matthew Barth7883f582019-02-14 14:24:46 -0600742 # 'interfaces' is optional (no default)
Matthew Barth64099cd2019-02-18 09:43:12 -0600743 ifaces = []
Matthew Barth7883f582019-02-14 14:24:46 -0600744 if ('interfaces' in z) and \
745 (z['interfaces'] is not None):
746 ifaces = getIfacesInZone(z['interfaces'])
747
Matt Spinler78498c92017-04-11 13:59:46 -0500748 fans = getFansInZone(z['zone'], profiles, fan_data)
Gunnar Millsb751f322017-06-06 15:14:11 -0500749 events = getEventsInZone(z['zone'], group['zone_conditions'],
750 events_data)
Matt Spinler78498c92017-04-11 13:59:46 -0500751
752 if len(fans) == 0:
753 sys.exit("Didn't find any fans in zone " + str(zone['num']))
754
Matthew Barth7883f582019-02-14 14:24:46 -0600755 if (ifaces):
756 zone['ifaces'] = ifaces
Matt Spinler78498c92017-04-11 13:59:46 -0500757 zone['fans'] = fans
Matthew Barthd4d0f082017-05-16 13:51:10 -0500758 zone['events'] = events
Matt Spinler78498c92017-04-11 13:59:46 -0500759 zones.append(zone)
760
761 zone_group['zones'] = zones
762 zone_groups.append(zone_group)
763
764 return zone_groups
765
766
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500767if __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')
Matthew Barthd4d0f082017-05-16 13:51:10 -0500777 parser.add_argument('-e', '--events_yaml', dest='events_yaml',
778 help='events to set speeds yaml')
Gunnar Millsee8a2812017-06-02 14:26:47 -0500779 parser.add_argument('-c', '--zone_conditions_yaml',
780 dest='zone_conditions_yaml',
781 help='conditions to determine zone yaml')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500782 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()
William A. Kennington III3e781062018-10-19 17:18:34 -0700789 sys.exit(1)
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500790
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
Matthew Barthd4d0f082017-05-16 13:51:10 -0500797 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
Gunnar Millsee8a2812017-06-02 14:26:47 -0500802 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
Matt Spinleree7f6422017-05-09 11:03:14 -0500807 zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
Gunnar Millsee8a2812017-06-02 14:26:47 -0500808 fan_data, events_data, zone_conditions_data)
Matt Spinleree7f6422017-05-09 11:03:14 -0500809
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
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500814
Matthew Barth702c4a52018-02-28 16:23:11 -0600815 tmpls_dir = os.path.join(
816 os.path.dirname(os.path.realpath(__file__)),
817 "templates")
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500818 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
Matthew Barth702c4a52018-02-28 16:23:11 -0600819 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')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500827 with open(output_file, 'w') as output:
Matthew Barth702c4a52018-02-28 16:23:11 -0600828 output.write(tmpl.render(zones=zone_config,
829 mgr_data=manager_config))