blob: 23c37b99499b8b0e2da38515a3dac2782241f0df [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 Barthcd3bfbc2018-03-07 16:26:03 -0600205 if ('init' in event['triggers']):
206 for i in event['triggers']['init']:
207 e += "\tmake_trigger(trigger::init(\n"
208 if ('handler' in i):
209 e += "\t\tmake_handler(\n"
210 if ('type' in i['hparams']) and \
211 (i['hparams']['type'] is not None):
212 e += ("handler::" + i['handler'] +
213 "<" + i['hparams']['type'] + ">(\n")
214 else:
215 e += "handler::" + i['handler'] + "(\n)"
216 for i, hp in enumerate(i['hparams']['params']):
217 if (i+1) != len(i['hparams']['params']):
218 e += i['hparams'][hp] + ",\n"
219 else:
220 e += i['hparams'][hp] + "\n"
221 e += ")\n"
222 e += "\t\t)\n"
223 e += "\t)),\n"
224
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600225 e += "},\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600226
227 e += "}"
228
229 return e
230
231
Matthew Barth6c050692017-12-05 15:30:09 -0600232def getGroups(zNum, zCond, edata, events):
233 """
234 Extract and construct the groups for the given event.
235 """
236 groups = []
237 for eGroups in edata['groups']:
238 if ('zone_conditions' in eGroups) and \
239 (eGroups['zone_conditions'] is not None):
240 # Zone conditions are optional in the events yaml but skip
241 # if this event's condition is not in this zone's conditions
242 if all('name' in z and z['name'] is not None and
243 not any(c['name'] == z['name'] for c in zCond)
244 for z in eGroups['zone_conditions']):
245 continue
246
247 # Zone numbers are optional in the events yaml but skip if this
248 # zone's zone number is not in the event's zone numbers
249 if all('zones' in z and z['zones'] is not None and
250 zNum not in z['zones']
251 for z in eGroups['zone_conditions']):
252 continue
Matthew Barth6c050692017-12-05 15:30:09 -0600253 eGroup = next(g for g in events['groups']
254 if g['name'] == eGroups['name'])
255
256 group = {}
257 members = []
258 group['name'] = eGroup['name']
259 for m in eGroup['members']:
260 member = {}
261 member['path'] = eGroup['type']
262 member['object'] = (eGroup['type'] + m)
263 member['interface'] = eGroups['interface']
264 member['property'] = eGroups['property']['name']
265 member['type'] = eGroups['property']['type']
Matthew Barth18c91032019-01-29 15:36:00 -0600266 # Use defined service to note member on zone object
267 if ('service' in eGroup) and \
268 (eGroup['service'] is not None):
269 member['service'] = eGroup['service']
Matthew Barth6c050692017-12-05 15:30:09 -0600270 # Add expected group member's property value if given
271 if ('value' in eGroups['property']) and \
272 (eGroups['property']['value'] is not None):
273 if isinstance(eGroups['property']['value'], str) or \
274 "string" in str(member['type']).lower():
275 member['value'] = (
276 "\"" + eGroups['property']['value'] + "\"")
277 else:
278 member['value'] = eGroups['property']['value']
279 members.append(member)
280 group['members'] = members
281 groups.append(group)
282 return groups
283
284
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600285def getParameters(member, groups, section, events):
Matthew Barth73379f92018-03-15 11:37:10 -0500286 """
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600287 Extracts and constructs a section's parameters
Matthew Barth73379f92018-03-15 11:37:10 -0500288 """
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600289 params = {}
290 if ('parameters' in section) and \
291 (section['parameters'] is not None):
292 plist = []
293 for sp in section['parameters']:
294 p = str(sp)
295 if (p != 'type'):
296 plist.append(p)
297 if (p != 'group'):
298 params[p] = "\"" + member[p] + "\""
Matthew Barth73379f92018-03-15 11:37:10 -0500299 else:
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600300 params[p] = "Group\n{\n"
301 for g in groups:
302 for m in g['members']:
303 params[p] += (
304 "{\"" + str(m['object']) + "\",\n" +
305 "\"" + str(m['interface']) + "\",\n" +
306 "\"" + str(m['property']) + "\"},\n")
307 params[p] += "}"
Matthew Barth73379f92018-03-15 11:37:10 -0500308 else:
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600309 params[p] = member[p]
310 params['params'] = plist
311 else:
312 params['params'] = []
313 return params
314
315
316def getInit(eGrps, eTrig, events):
317 """
318 Extracts and constructs an init trigger for the event's groups
319 which are required to be of the same type.
320 """
321 method = {}
322 methods = []
323 # Use the first group member for retrieving the type
324 member = eGrps[0]['members'][0]
325 if ('method' in eTrig) and \
326 (eTrig['method'] is not None):
327 # Add method parameters
328 eMethod = next(m for m in events['methods']
329 if m['name'] == eTrig['method'])
330 method['method'] = eMethod['name']
331 method['mparams'] = getParameters(
332 member, eGrps, eMethod, events)
333
334 # Add handler parameters
335 eHandler = next(h for h in events['handlers']
336 if h['name'] == eTrig['handler'])
337 method['handler'] = eHandler['name']
338 method['hparams'] = getParameters(
339 member, eGrps, eHandler, events)
340
341 methods.append(method)
342
343 return methods
Matthew Barth73379f92018-03-15 11:37:10 -0500344
345
Matthew Bartha69465a2018-03-02 13:50:59 -0600346def getSignal(eGrps, eTrig, events):
347 """
348 Extracts and constructs for each group member a signal
349 subscription of each match listed in the trigger.
350 """
351 signals = []
352 for group in eGrps:
353 for member in group['members']:
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600354 signal = {}
355 # Add signal parameters
356 eSignal = next(s for s in events['signals']
357 if s['name'] == eTrig['signal'])
358 signal['signal'] = eSignal['name']
359 signal['sparams'] = getParameters(member, eGrps, eSignal, events)
360
361 # If service not given, subscribe to signal match
362 if ('service' not in member):
363 # Add signal match parameters
Matthew Bartha69465a2018-03-02 13:50:59 -0600364 eMatch = next(m for m in events['matches']
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600365 if m['name'] == eSignal['match'])
366 signal['match'] = eMatch['name']
367 signal['mparams'] = getParameters(member, eGrps, eMatch, events)
Matthew Bartha69465a2018-03-02 13:50:59 -0600368
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600369 # Add handler parameters
370 eHandler = next(h for h in events['handlers']
371 if h['name'] == eTrig['handler'])
372 signal['handler'] = eHandler['name']
373 signal['hparams'] = getParameters(member, eGrps, eHandler, events)
Matthew Barth73379f92018-03-15 11:37:10 -0500374
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600375 signals.append(signal)
Matthew Barth73379f92018-03-15 11:37:10 -0500376
Matthew Bartha69465a2018-03-02 13:50:59 -0600377 return signals
378
379
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600380def getTimer(eTrig):
381 """
382 Extracts and constructs the required parameters for an
383 event timer.
384 """
385 timer = {}
386 timer['interval'] = (
387 "static_cast<std::chrono::microseconds>" +
388 "(" + str(eTrig['interval']) + ")")
389 timer['type'] = "TimerType::" + str(eTrig['type'])
390 return timer
391
392
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600393def getActions(zNum, zCond, edata, actions, events):
Matthew Barth9df74752017-10-11 14:39:31 -0500394 """
395 Extracts and constructs the make_action function call for
396 all the actions within the given event.
397 """
398 action = []
399 for eActions in actions['actions']:
400 actions = {}
401 eAction = next(a for a in events['actions']
402 if a['name'] == eActions['name'])
403 actions['name'] = eAction['name']
404 params = []
405 if ('parameters' in eAction) and \
406 (eAction['parameters'] is not None):
407 for p in eAction['parameters']:
408 param = "static_cast<"
409 if type(eActions[p]) is not dict:
410 if p == 'actions':
411 param = "std::vector<Action>{"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600412 pActs = getActions(zNum,
413 zCond,
414 edata,
415 eActions,
416 events)
Matthew Barth9df74752017-10-11 14:39:31 -0500417 for a in pActs:
418 if (len(a['parameters']) != 0):
419 param += (
420 "make_action(action::" +
421 a['name'] +
422 "(\n")
423 for i, ap in enumerate(a['parameters']):
424 if (i+1) != len(a['parameters']):
425 param += (ap + ",")
426 else:
427 param += (ap + ")")
428 else:
429 param += ("make_action(action::" + a['name'])
430 param += "),"
431 param += "}"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600432 elif p == 'defevents' or p == 'altevents':
433 param = "std::vector<SetSpeedEvent>{\n"
434 for i, e in enumerate(eActions[p]):
435 aEvent = getEvent(zNum, zCond, e, events)
436 if not aEvent:
437 continue
438 if (i+1) != len(eActions[p]):
439 param += genEvent(aEvent) + ",\n"
440 else:
441 param += genEvent(aEvent) + "\n"
442 param += "\t}"
Matthew Barth9df74752017-10-11 14:39:31 -0500443 elif p == 'property':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600444 if isinstance(eActions[p], str) or \
Matthew Barth6c050692017-12-05 15:30:09 -0600445 "string" in str(eActions[p]['type']).lower():
Matthew Barth9a5b6992018-01-23 15:32:26 -0600446 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600447 str(eActions[p]['type']).lower() +
Matthew Barth9a5b6992018-01-23 15:32:26 -0600448 ">(\"" + str(eActions[p]) + "\")")
449 else:
450 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600451 str(eActions[p]['type']).lower() +
452 ">(" + str(eActions[p]['value']).lower() + ")")
Matthew Barth9df74752017-10-11 14:39:31 -0500453 else:
454 # Default type to 'size_t' when not given
455 param += ("size_t>(" + str(eActions[p]).lower() + ")")
456 else:
457 if p == 'timer':
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600458 t = getTimer(eActions[p])
Matthew Barth9df74752017-10-11 14:39:31 -0500459 param = (
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600460 "TimerConf{" + t['interval'] + "," +
461 t['type'] + "}")
Matthew Barth9df74752017-10-11 14:39:31 -0500462 else:
463 param += (str(eActions[p]['type']).lower() + ">(")
464 if p != 'map':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600465 if isinstance(eActions[p]['value'], str) or \
466 "string" in str(eActions[p]['type']).lower():
467 param += \
468 "\"" + str(eActions[p]['value']) + "\")"
469 else:
470 param += \
471 str(eActions[p]['value']).lower() + ")"
Matthew Barth9df74752017-10-11 14:39:31 -0500472 else:
473 param += (
474 str(eActions[p]['type']).lower() +
475 convertToMap(str(eActions[p]['value'])) + ")")
476 params.append(param)
477 actions['parameters'] = params
478 action.append(actions)
479 return action
480
481
Matthew Barth7f272fd2017-09-12 16:16:56 -0500482def getEvent(zone_num, zone_conditions, e, events_data):
483 """
484 Parses the sections of an event and populates the properties
485 that construct an event within the generated source.
486 """
487 event = {}
Matthew Barth7f272fd2017-09-12 16:16:56 -0500488
Matthew Barth6c050692017-12-05 15:30:09 -0600489 # Add set speed event groups
490 grps = getGroups(zone_num, zone_conditions, e, events_data)
491 if not grps:
Matthew Barth7f272fd2017-09-12 16:16:56 -0500492 return
Matthew Barth6c050692017-12-05 15:30:09 -0600493 event['groups'] = grps
Matthew Barth7f272fd2017-09-12 16:16:56 -0500494
Matthew Barthe3d1c4a2018-01-11 13:53:49 -0600495 # Add optional set speed actions and function parameters
496 event['action'] = []
497 if ('actions' in e) and \
498 (e['actions'] is not None):
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600499 event['action'] = getActions(zone_num,
500 zone_conditions,
501 e,
502 e,
503 events_data)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500504
Matthew Bartha69465a2018-03-02 13:50:59 -0600505 # Add event triggers
506 event['triggers'] = {}
507 for trig in e['triggers']:
508 triggers = []
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600509 if (trig['name'] == "timer"):
510 event['triggers']['timer'] = getTimer(trig)
511 elif (trig['name'] == "signal"):
Matthew Bartha69465a2018-03-02 13:50:59 -0600512 if ('signals' not in event['triggers']):
513 event['triggers']['signals'] = []
514 triggers = getSignal(event['groups'], trig, events_data)
515 event['triggers']['signals'].extend(triggers)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600516 elif (trig['name'] == "init"):
517 triggers = getInit(event['groups'], trig, events_data)
518 event['triggers']['init'] = triggers
Matthew Barth7f272fd2017-09-12 16:16:56 -0500519
Matthew Barth7f272fd2017-09-12 16:16:56 -0500520 return event
521
522
523def addPrecondition(zNum, zCond, event, events_data):
Matthew Barth9af190c2017-08-08 14:20:43 -0500524 """
525 Parses the precondition section of an event and populates the necessary
526 structures to generate a precondition for a set speed event.
527 """
528 precond = {}
529 # Add set speed event precondition group
Matthew Barth6c050692017-12-05 15:30:09 -0600530 grps = getGroups(zNum, zCond, event['precondition'], events_data)
531 if not grps:
532 return
533 precond['pcgrps'] = grps
Matthew Barth9af190c2017-08-08 14:20:43 -0500534
Matthew Barth7f272fd2017-09-12 16:16:56 -0500535 # Add set speed event precondition actions
536 pc = []
537 pcs = {}
538 pcs['name'] = event['precondition']['name']
539 epc = next(p for p in events_data['preconditions']
Matthew Barth9af190c2017-08-08 14:20:43 -0500540 if p['name'] == event['precondition']['name'])
541 params = []
Matthew Barth7f272fd2017-09-12 16:16:56 -0500542 for p in epc['parameters']:
Matthew Barth9af190c2017-08-08 14:20:43 -0500543 param = {}
544 if p == 'groups':
545 param['type'] = "std::vector<PrecondGroup>"
546 param['open'] = "{"
547 param['close'] = "}"
548 values = []
Matthew Barth6c050692017-12-05 15:30:09 -0600549 for group in precond['pcgrps']:
550 for pcgrp in group['members']:
551 value = {}
552 value['value'] = (
553 "PrecondGroup{\"" +
554 str(pcgrp['object']) + "\",\"" +
555 str(pcgrp['interface']) + "\",\"" +
556 str(pcgrp['property']) + "\"," +
557 "static_cast<" +
558 str(pcgrp['type']).lower() + ">")
559 if isinstance(pcgrp['value'], str) or \
560 "string" in str(pcgrp['type']).lower():
561 value['value'] += ("(" + str(pcgrp['value']) + ")}")
562 else:
563 value['value'] += \
564 ("(" + str(pcgrp['value']).lower() + ")}")
565 values.append(value)
Matthew Barth9af190c2017-08-08 14:20:43 -0500566 param['values'] = values
567 params.append(param)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500568 pcs['params'] = params
569 pc.append(pcs)
Matthew Barth9af190c2017-08-08 14:20:43 -0500570 precond['pcact'] = pc
571
Matthew Barth7f272fd2017-09-12 16:16:56 -0500572 pcevents = []
573 for pce in event['precondition']['events']:
574 pcevent = getEvent(zNum, zCond, pce, events_data)
575 if not pcevent:
576 continue
577 pcevents.append(pcevent)
578 precond['pcevts'] = pcevents
579
Matthew Barthf20c3212018-03-02 14:42:55 -0600580 # Add precondition event triggers
581 precond['triggers'] = {}
582 for trig in event['precondition']['triggers']:
583 triggers = []
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600584 if (trig['name'] == "timer"):
585 precond['triggers']['pctime'] = getTimer(trig)
586 elif (trig['name'] == "signal"):
Matthew Barthf20c3212018-03-02 14:42:55 -0600587 if ('pcsigs' not in precond['triggers']):
588 precond['triggers']['pcsigs'] = []
589 triggers = getSignal(precond['pcgrps'], trig, events_data)
590 precond['triggers']['pcsigs'].extend(triggers)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600591 elif (trig['name'] == "init"):
592 triggers = getInit(precond['pcgrps'], trig, events_data)
593 precond['triggers']['init'] = triggers
Matthew Barth9af190c2017-08-08 14:20:43 -0500594
595 return precond
596
597
Gunnar Millsb751f322017-06-06 15:14:11 -0500598def getEventsInZone(zone_num, zone_conditions, events_data):
Matthew Barthd4d0f082017-05-16 13:51:10 -0500599 """
600 Constructs the event entries defined for each zone using the events yaml
601 provided.
602 """
603 events = []
Matthew Barthba102b32017-05-16 16:13:56 -0500604
Matthew Barthd4d0f082017-05-16 13:51:10 -0500605 if 'events' in events_data:
606 for e in events_data['events']:
Matthew Barthd4d0f082017-05-16 13:51:10 -0500607 event = {}
Matthew Barth9af190c2017-08-08 14:20:43 -0500608 # Add precondition if given
609 if ('precondition' in e) and \
610 (e['precondition'] is not None):
Matthew Barth7f272fd2017-09-12 16:16:56 -0500611 event['pc'] = addPrecondition(zone_num,
612 zone_conditions,
613 e,
614 events_data)
Matthew Barth90149802017-08-15 10:51:37 -0500615 else:
Matthew Barth7f272fd2017-09-12 16:16:56 -0500616 event = getEvent(zone_num, zone_conditions, e, events_data)
617 if not event:
618 continue
Matthew Barthd4d0f082017-05-16 13:51:10 -0500619 events.append(event)
620
621 return events
622
623
Matt Spinler78498c92017-04-11 13:59:46 -0500624def getFansInZone(zone_num, profiles, fan_data):
625 """
626 Parses the fan definition YAML files to find the fans
627 that match both the zone passed in and one of the
628 cooling profiles.
629 """
630
631 fans = []
632
633 for f in fan_data['fans']:
634
635 if zone_num != f['cooling_zone']:
636 continue
637
Gunnar Mills67e95512017-06-02 14:35:18 -0500638 # 'cooling_profile' is optional (use 'all' instead)
Matt Spinler78498c92017-04-11 13:59:46 -0500639 if f.get('cooling_profile') is None:
640 profile = "all"
641 else:
642 profile = f['cooling_profile']
643
644 if profile not in profiles:
645 continue
646
647 fan = {}
648 fan['name'] = f['inventory']
649 fan['sensors'] = f['sensors']
Lei YU069e4402018-01-31 16:47:37 +0800650 fan['target_interface'] = f.get(
651 'target_interface',
652 'xyz.openbmc_project.Control.FanSpeed')
Matt Spinler78498c92017-04-11 13:59:46 -0500653 fans.append(fan)
654
655 return fans
656
657
Matthew Barth7883f582019-02-14 14:24:46 -0600658def getIfacesInZone(zone_ifaces):
659 """
660 Parse given interfaces for a zone for associating a zone with an interface
661 and set any properties listed to defined values upon fan control starting
662 on the zone.
663 """
664
665 ifaces = []
666 for i in zone_ifaces:
667 iface = {}
668 # Interface name not needed yet for fan zones but
669 # may be necessary as more interfaces are extended by the zones
670 iface['name'] = i['name']
671
672 if ('properties' in i) and \
673 (i['properties'] is not None):
674 props = []
675 for p in i['properties']:
676 prop = {}
Matthew Barth59096e52019-02-18 12:23:38 -0600677 prop['name'] = p['name']
678 prop['func'] = str(p['name']).lower()
Matthew Barth7883f582019-02-14 14:24:46 -0600679 prop['type'] = parse_cpp_type(p['type'])
Matthew Barth59096e52019-02-18 12:23:38 -0600680 if ('persist' in p):
681 persist = p['persist']
682 if (persist is not None):
683 if (isinstance(persist, bool)):
684 prop['persist'] = 'true' if persist else 'false'
685 else:
686 prop['persist'] = 'false'
Matthew Barth7883f582019-02-14 14:24:46 -0600687 vals = []
688 for v in p['values']:
689 val = v['value']
690 if (val is not None):
691 if (isinstance(val, bool)):
692 # Convert True/False to 'true'/'false'
693 val = 'true' if val else 'false'
694 elif (isinstance(val, str)):
695 # Wrap strings with double-quotes
696 val = "\"" + val + "\""
697 vals.append(val)
698 prop['values'] = vals
699 props.append(prop)
700 iface['props'] = props
701 ifaces.append(iface)
702
703 return ifaces
704
705
Gunnar Millsee8a2812017-06-02 14:26:47 -0500706def getConditionInZoneConditions(zone_condition, zone_conditions_data):
707 """
708 Parses the zone conditions definition YAML files to find the condition
709 that match both the zone condition passed in.
710 """
711
712 condition = {}
713
714 for c in zone_conditions_data['conditions']:
715
716 if zone_condition != c['name']:
717 continue
718 condition['type'] = c['type']
719 properties = []
720 for p in c['properties']:
721 property = {}
722 property['property'] = p['property']
723 property['interface'] = p['interface']
724 property['path'] = p['path']
725 property['type'] = p['type'].lower()
726 property['value'] = str(p['value']).lower()
727 properties.append(property)
728 condition['properties'] = properties
729
730 return condition
731
732
733def buildZoneData(zone_data, fan_data, events_data, zone_conditions_data):
Matt Spinler78498c92017-04-11 13:59:46 -0500734 """
735 Combines the zone definition YAML and fan
736 definition YAML to create a data structure defining
737 the fan cooling zones.
738 """
739
740 zone_groups = []
741
742 for group in zone_data:
743 conditions = []
Gunnar Millsee8a2812017-06-02 14:26:47 -0500744 # zone conditions are optional
745 if 'zone_conditions' in group and group['zone_conditions'] is not None:
746 for c in group['zone_conditions']:
747
748 if not zone_conditions_data:
Gunnar Millsb751f322017-06-06 15:14:11 -0500749 sys.exit("No zone_conditions YAML file but " +
Gunnar Millsee8a2812017-06-02 14:26:47 -0500750 "zone_conditions used in zone YAML")
751
752 condition = getConditionInZoneConditions(c['name'],
753 zone_conditions_data)
754
755 if not condition:
756 sys.exit("Missing zone condition " + c['name'])
757
758 conditions.append(condition)
Matt Spinler78498c92017-04-11 13:59:46 -0500759
760 zone_group = {}
761 zone_group['conditions'] = conditions
762
763 zones = []
764 for z in group['zones']:
765 zone = {}
766
Gunnar Mills67e95512017-06-02 14:35:18 -0500767 # 'zone' is required
768 if ('zone' not in z) or (z['zone'] is None):
Matt Spinler78498c92017-04-11 13:59:46 -0500769 sys.exit("Missing fan zone number in " + zone_yaml)
770
771 zone['num'] = z['zone']
772
773 zone['full_speed'] = z['full_speed']
774
Matthew Barth1de66622017-06-12 13:13:02 -0500775 zone['default_floor'] = z['default_floor']
776
Matthew Bartha9561842017-06-29 11:43:45 -0500777 # 'increase_delay' is optional (use 0 by default)
778 key = 'increase_delay'
779 zone[key] = z.setdefault(key, 0)
780
781 # 'decrease_interval' is optional (use 0 by default)
782 key = 'decrease_interval'
783 zone[key] = z.setdefault(key, 0)
784
Gunnar Mills67e95512017-06-02 14:35:18 -0500785 # 'cooling_profiles' is optional (use 'all' instead)
786 if ('cooling_profiles' not in z) or \
Matt Spinler78498c92017-04-11 13:59:46 -0500787 (z['cooling_profiles'] is None):
788 profiles = ["all"]
789 else:
790 profiles = z['cooling_profiles']
791
Matthew Barth7883f582019-02-14 14:24:46 -0600792 # 'interfaces' is optional (no default)
Matthew Barth64099cd2019-02-18 09:43:12 -0600793 ifaces = []
Matthew Barth7883f582019-02-14 14:24:46 -0600794 if ('interfaces' in z) and \
795 (z['interfaces'] is not None):
796 ifaces = getIfacesInZone(z['interfaces'])
797
Matt Spinler78498c92017-04-11 13:59:46 -0500798 fans = getFansInZone(z['zone'], profiles, fan_data)
Gunnar Millsb751f322017-06-06 15:14:11 -0500799 events = getEventsInZone(z['zone'], group['zone_conditions'],
800 events_data)
Matt Spinler78498c92017-04-11 13:59:46 -0500801
802 if len(fans) == 0:
803 sys.exit("Didn't find any fans in zone " + str(zone['num']))
804
Matthew Barth7883f582019-02-14 14:24:46 -0600805 if (ifaces):
806 zone['ifaces'] = ifaces
Matt Spinler78498c92017-04-11 13:59:46 -0500807 zone['fans'] = fans
Matthew Barthd4d0f082017-05-16 13:51:10 -0500808 zone['events'] = events
Matt Spinler78498c92017-04-11 13:59:46 -0500809 zones.append(zone)
810
811 zone_group['zones'] = zones
812 zone_groups.append(zone_group)
813
814 return zone_groups
815
816
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500817if __name__ == '__main__':
818 parser = ArgumentParser(
819 description="Phosphor fan zone definition parser")
820
821 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml',
822 default="example/zones.yaml",
823 help='fan zone definitional yaml')
824 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml',
825 default="example/fans.yaml",
826 help='fan definitional yaml')
Matthew Barthd4d0f082017-05-16 13:51:10 -0500827 parser.add_argument('-e', '--events_yaml', dest='events_yaml',
828 help='events to set speeds yaml')
Gunnar Millsee8a2812017-06-02 14:26:47 -0500829 parser.add_argument('-c', '--zone_conditions_yaml',
830 dest='zone_conditions_yaml',
831 help='conditions to determine zone yaml')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500832 parser.add_argument('-o', '--output_dir', dest='output_dir',
833 default=".",
834 help='output directory')
835 args = parser.parse_args()
836
837 if not args.zone_yaml or not args.fan_yaml:
838 parser.print_usage()
William A. Kennington III3e781062018-10-19 17:18:34 -0700839 sys.exit(1)
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500840
841 with open(args.zone_yaml, 'r') as zone_input:
842 zone_data = yaml.safe_load(zone_input) or {}
843
844 with open(args.fan_yaml, 'r') as fan_input:
845 fan_data = yaml.safe_load(fan_input) or {}
846
Matthew Barthd4d0f082017-05-16 13:51:10 -0500847 events_data = {}
848 if args.events_yaml:
849 with open(args.events_yaml, 'r') as events_input:
850 events_data = yaml.safe_load(events_input) or {}
851
Gunnar Millsee8a2812017-06-02 14:26:47 -0500852 zone_conditions_data = {}
853 if args.zone_conditions_yaml:
854 with open(args.zone_conditions_yaml, 'r') as zone_conditions_input:
855 zone_conditions_data = yaml.safe_load(zone_conditions_input) or {}
856
Matt Spinleree7f6422017-05-09 11:03:14 -0500857 zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
Gunnar Millsee8a2812017-06-02 14:26:47 -0500858 fan_data, events_data, zone_conditions_data)
Matt Spinleree7f6422017-05-09 11:03:14 -0500859
860 manager_config = zone_data.get('manager_configuration', {})
861
862 if manager_config.get('power_on_delay') is None:
863 manager_config['power_on_delay'] = 0
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500864
Matthew Barth702c4a52018-02-28 16:23:11 -0600865 tmpls_dir = os.path.join(
866 os.path.dirname(os.path.realpath(__file__)),
867 "templates")
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500868 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
Matthew Barth702c4a52018-02-28 16:23:11 -0600869 if sys.version_info < (3, 0):
870 lkup = TemplateLookup(
871 directories=tmpls_dir.split(),
872 disable_unicode=True)
873 else:
874 lkup = TemplateLookup(
875 directories=tmpls_dir.split())
876 tmpl = lkup.get_template('fan_zone_defs.mako.cpp')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500877 with open(output_file, 'w') as output:
Matthew Barth702c4a52018-02-28 16:23:11 -0600878 output.write(tmpl.render(zones=zone_config,
879 mgr_data=manager_config))