blob: 30acf47904c31bd299bb1c3bf30d32381dc31ecb [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"
Matthew Barth926df662018-10-09 09:51:12 -0500178 for i, mp in enumerate(s['mparams']['params']):
179 if (i+1) != len(s['mparams']['params']):
180 e += "\t\t\t" + s['mparams'][mp] + ",\n"
Matthew Barth016bd242018-03-07 16:06:06 -0600181 else:
Matthew Barth926df662018-10-09 09:51:12 -0500182 e += "\t\t\t" + s['mparams'][mp] + "\n"
183 e += "\t\t),\n"
184 e += "\t\tmake_handler<SignalHandler>(\n"
Matthew Barth016bd242018-03-07 16:06:06 -0600185 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"
Matthew Barth926df662018-10-09 09:51:12 -0500202 e += "\t\t)\n"
Matthew Barth016bd242018-03-07 16:06:06 -0600203 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"
Matthew Barth926df662018-10-09 09:51:12 -0500208 if ('method' in i):
209 e += "\t\tmake_handler<MethodHandler>(\n"
210 if ('type' in i['mparams']) and \
211 (i['mparams']['type'] is not None):
212 e += i['method'] + "<" + i['mparams']['type'] + ">(\n"
213 else:
214 e += i['method'] + "(\n"
215 for ip in i['mparams']['params']:
216 e += i['mparams'][ip] + ",\n"
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600217 if ('type' in i['hparams']) and \
218 (i['hparams']['type'] is not None):
219 e += ("handler::" + i['handler'] +
220 "<" + i['hparams']['type'] + ">(\n")
221 else:
222 e += "handler::" + i['handler'] + "(\n)"
223 for i, hp in enumerate(i['hparams']['params']):
224 if (i+1) != len(i['hparams']['params']):
225 e += i['hparams'][hp] + ",\n"
226 else:
227 e += i['hparams'][hp] + "\n"
Matthew Barth926df662018-10-09 09:51:12 -0500228 e += "))\n"
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600229 e += "\t\t)\n"
230 e += "\t)),\n"
231
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600232 e += "},\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600233
234 e += "}"
235
236 return e
237
238
Matthew Barth6c050692017-12-05 15:30:09 -0600239def getGroups(zNum, zCond, edata, events):
240 """
241 Extract and construct the groups for the given event.
242 """
243 groups = []
244 for eGroups in edata['groups']:
245 if ('zone_conditions' in eGroups) and \
246 (eGroups['zone_conditions'] is not None):
247 # Zone conditions are optional in the events yaml but skip
248 # if this event's condition is not in this zone's conditions
249 if all('name' in z and z['name'] is not None and
250 not any(c['name'] == z['name'] for c in zCond)
251 for z in eGroups['zone_conditions']):
252 continue
253
254 # Zone numbers are optional in the events yaml but skip if this
255 # zone's zone number is not in the event's zone numbers
256 if all('zones' in z and z['zones'] is not None and
257 zNum not in z['zones']
258 for z in eGroups['zone_conditions']):
259 continue
Matthew Barth6c050692017-12-05 15:30:09 -0600260 eGroup = next(g for g in events['groups']
261 if g['name'] == eGroups['name'])
262
263 group = {}
264 members = []
265 group['name'] = eGroup['name']
266 for m in eGroup['members']:
267 member = {}
268 member['path'] = eGroup['type']
269 member['object'] = (eGroup['type'] + m)
270 member['interface'] = eGroups['interface']
271 member['property'] = eGroups['property']['name']
272 member['type'] = eGroups['property']['type']
Matthew Barth18c91032019-01-29 15:36:00 -0600273 # Use defined service to note member on zone object
274 if ('service' in eGroup) and \
275 (eGroup['service'] is not None):
276 member['service'] = eGroup['service']
Matthew Barth6c050692017-12-05 15:30:09 -0600277 # Add expected group member's property value if given
278 if ('value' in eGroups['property']) and \
279 (eGroups['property']['value'] is not None):
280 if isinstance(eGroups['property']['value'], str) or \
281 "string" in str(member['type']).lower():
282 member['value'] = (
283 "\"" + eGroups['property']['value'] + "\"")
284 else:
285 member['value'] = eGroups['property']['value']
286 members.append(member)
287 group['members'] = members
288 groups.append(group)
289 return groups
290
291
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600292def getParameters(member, groups, section, events):
Matthew Barth73379f92018-03-15 11:37:10 -0500293 """
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600294 Extracts and constructs a section's parameters
Matthew Barth73379f92018-03-15 11:37:10 -0500295 """
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600296 params = {}
297 if ('parameters' in section) and \
298 (section['parameters'] is not None):
299 plist = []
300 for sp in section['parameters']:
301 p = str(sp)
302 if (p != 'type'):
303 plist.append(p)
304 if (p != 'group'):
305 params[p] = "\"" + member[p] + "\""
Matthew Barth73379f92018-03-15 11:37:10 -0500306 else:
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600307 params[p] = "Group\n{\n"
308 for g in groups:
309 for m in g['members']:
310 params[p] += (
311 "{\"" + str(m['object']) + "\",\n" +
312 "\"" + str(m['interface']) + "\",\n" +
313 "\"" + str(m['property']) + "\"},\n")
314 params[p] += "}"
Matthew Barth73379f92018-03-15 11:37:10 -0500315 else:
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600316 params[p] = member[p]
317 params['params'] = plist
318 else:
319 params['params'] = []
320 return params
321
322
323def getInit(eGrps, eTrig, events):
324 """
325 Extracts and constructs an init trigger for the event's groups
326 which are required to be of the same type.
327 """
328 method = {}
329 methods = []
330 # Use the first group member for retrieving the type
331 member = eGrps[0]['members'][0]
332 if ('method' in eTrig) and \
333 (eTrig['method'] is not None):
334 # Add method parameters
335 eMethod = next(m for m in events['methods']
336 if m['name'] == eTrig['method'])
337 method['method'] = eMethod['name']
338 method['mparams'] = getParameters(
339 member, eGrps, eMethod, events)
340
341 # Add handler parameters
342 eHandler = next(h for h in events['handlers']
343 if h['name'] == eTrig['handler'])
344 method['handler'] = eHandler['name']
345 method['hparams'] = getParameters(
346 member, eGrps, eHandler, events)
347
348 methods.append(method)
349
350 return methods
Matthew Barth73379f92018-03-15 11:37:10 -0500351
352
Matthew Bartha69465a2018-03-02 13:50:59 -0600353def getSignal(eGrps, eTrig, events):
354 """
355 Extracts and constructs for each group member a signal
356 subscription of each match listed in the trigger.
357 """
358 signals = []
359 for group in eGrps:
360 for member in group['members']:
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600361 signal = {}
362 # Add signal parameters
363 eSignal = next(s for s in events['signals']
364 if s['name'] == eTrig['signal'])
365 signal['signal'] = eSignal['name']
366 signal['sparams'] = getParameters(member, eGrps, eSignal, events)
367
368 # If service not given, subscribe to signal match
369 if ('service' not in member):
370 # Add signal match parameters
Matthew Bartha69465a2018-03-02 13:50:59 -0600371 eMatch = next(m for m in events['matches']
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600372 if m['name'] == eSignal['match'])
373 signal['match'] = eMatch['name']
374 signal['mparams'] = getParameters(member, eGrps, eMatch, events)
Matthew Bartha69465a2018-03-02 13:50:59 -0600375
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600376 # Add handler parameters
377 eHandler = next(h for h in events['handlers']
378 if h['name'] == eTrig['handler'])
379 signal['handler'] = eHandler['name']
380 signal['hparams'] = getParameters(member, eGrps, eHandler, events)
Matthew Barth73379f92018-03-15 11:37:10 -0500381
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600382 signals.append(signal)
Matthew Barth73379f92018-03-15 11:37:10 -0500383
Matthew Bartha69465a2018-03-02 13:50:59 -0600384 return signals
385
386
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600387def getTimer(eTrig):
388 """
389 Extracts and constructs the required parameters for an
390 event timer.
391 """
392 timer = {}
393 timer['interval'] = (
394 "static_cast<std::chrono::microseconds>" +
395 "(" + str(eTrig['interval']) + ")")
396 timer['type'] = "TimerType::" + str(eTrig['type'])
397 return timer
398
399
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600400def getActions(zNum, zCond, edata, actions, events):
Matthew Barth9df74752017-10-11 14:39:31 -0500401 """
402 Extracts and constructs the make_action function call for
403 all the actions within the given event.
404 """
405 action = []
406 for eActions in actions['actions']:
407 actions = {}
408 eAction = next(a for a in events['actions']
409 if a['name'] == eActions['name'])
410 actions['name'] = eAction['name']
411 params = []
412 if ('parameters' in eAction) and \
413 (eAction['parameters'] is not None):
414 for p in eAction['parameters']:
415 param = "static_cast<"
416 if type(eActions[p]) is not dict:
417 if p == 'actions':
418 param = "std::vector<Action>{"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600419 pActs = getActions(zNum,
420 zCond,
421 edata,
422 eActions,
423 events)
Matthew Barth9df74752017-10-11 14:39:31 -0500424 for a in pActs:
425 if (len(a['parameters']) != 0):
426 param += (
427 "make_action(action::" +
428 a['name'] +
429 "(\n")
430 for i, ap in enumerate(a['parameters']):
431 if (i+1) != len(a['parameters']):
432 param += (ap + ",")
433 else:
434 param += (ap + ")")
435 else:
436 param += ("make_action(action::" + a['name'])
437 param += "),"
438 param += "}"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600439 elif p == 'defevents' or p == 'altevents':
440 param = "std::vector<SetSpeedEvent>{\n"
441 for i, e in enumerate(eActions[p]):
442 aEvent = getEvent(zNum, zCond, e, events)
443 if not aEvent:
444 continue
445 if (i+1) != len(eActions[p]):
446 param += genEvent(aEvent) + ",\n"
447 else:
448 param += genEvent(aEvent) + "\n"
449 param += "\t}"
Matthew Barth9df74752017-10-11 14:39:31 -0500450 elif p == 'property':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600451 if isinstance(eActions[p], str) or \
Matthew Barth6c050692017-12-05 15:30:09 -0600452 "string" in str(eActions[p]['type']).lower():
Matthew Barth9a5b6992018-01-23 15:32:26 -0600453 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600454 str(eActions[p]['type']).lower() +
Matthew Barth9a5b6992018-01-23 15:32:26 -0600455 ">(\"" + str(eActions[p]) + "\")")
456 else:
457 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600458 str(eActions[p]['type']).lower() +
459 ">(" + str(eActions[p]['value']).lower() + ")")
Matthew Barth9df74752017-10-11 14:39:31 -0500460 else:
461 # Default type to 'size_t' when not given
462 param += ("size_t>(" + str(eActions[p]).lower() + ")")
463 else:
464 if p == 'timer':
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600465 t = getTimer(eActions[p])
Matthew Barth9df74752017-10-11 14:39:31 -0500466 param = (
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600467 "TimerConf{" + t['interval'] + "," +
468 t['type'] + "}")
Matthew Barth9df74752017-10-11 14:39:31 -0500469 else:
470 param += (str(eActions[p]['type']).lower() + ">(")
471 if p != 'map':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600472 if isinstance(eActions[p]['value'], str) or \
473 "string" in str(eActions[p]['type']).lower():
474 param += \
475 "\"" + str(eActions[p]['value']) + "\")"
476 else:
477 param += \
478 str(eActions[p]['value']).lower() + ")"
Matthew Barth9df74752017-10-11 14:39:31 -0500479 else:
480 param += (
481 str(eActions[p]['type']).lower() +
482 convertToMap(str(eActions[p]['value'])) + ")")
483 params.append(param)
484 actions['parameters'] = params
485 action.append(actions)
486 return action
487
488
Matthew Barth7f272fd2017-09-12 16:16:56 -0500489def getEvent(zone_num, zone_conditions, e, events_data):
490 """
491 Parses the sections of an event and populates the properties
492 that construct an event within the generated source.
493 """
494 event = {}
Matthew Barth7f272fd2017-09-12 16:16:56 -0500495
Matthew Barth6c050692017-12-05 15:30:09 -0600496 # Add set speed event groups
497 grps = getGroups(zone_num, zone_conditions, e, events_data)
498 if not grps:
Matthew Barth7f272fd2017-09-12 16:16:56 -0500499 return
Matthew Barth6c050692017-12-05 15:30:09 -0600500 event['groups'] = grps
Matthew Barth7f272fd2017-09-12 16:16:56 -0500501
Matthew Barthe3d1c4a2018-01-11 13:53:49 -0600502 # Add optional set speed actions and function parameters
503 event['action'] = []
504 if ('actions' in e) and \
505 (e['actions'] is not None):
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600506 event['action'] = getActions(zone_num,
507 zone_conditions,
508 e,
509 e,
510 events_data)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500511
Matthew Bartha69465a2018-03-02 13:50:59 -0600512 # Add event triggers
513 event['triggers'] = {}
514 for trig in e['triggers']:
515 triggers = []
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600516 if (trig['name'] == "timer"):
517 event['triggers']['timer'] = getTimer(trig)
518 elif (trig['name'] == "signal"):
Matthew Bartha69465a2018-03-02 13:50:59 -0600519 if ('signals' not in event['triggers']):
520 event['triggers']['signals'] = []
521 triggers = getSignal(event['groups'], trig, events_data)
522 event['triggers']['signals'].extend(triggers)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600523 elif (trig['name'] == "init"):
524 triggers = getInit(event['groups'], trig, events_data)
525 event['triggers']['init'] = triggers
Matthew Barth7f272fd2017-09-12 16:16:56 -0500526
Matthew Barth7f272fd2017-09-12 16:16:56 -0500527 return event
528
529
530def addPrecondition(zNum, zCond, event, events_data):
Matthew Barth9af190c2017-08-08 14:20:43 -0500531 """
532 Parses the precondition section of an event and populates the necessary
533 structures to generate a precondition for a set speed event.
534 """
535 precond = {}
536 # Add set speed event precondition group
Matthew Barth6c050692017-12-05 15:30:09 -0600537 grps = getGroups(zNum, zCond, event['precondition'], events_data)
538 if not grps:
539 return
540 precond['pcgrps'] = grps
Matthew Barth9af190c2017-08-08 14:20:43 -0500541
Matthew Barth7f272fd2017-09-12 16:16:56 -0500542 # Add set speed event precondition actions
543 pc = []
544 pcs = {}
545 pcs['name'] = event['precondition']['name']
546 epc = next(p for p in events_data['preconditions']
Matthew Barth9af190c2017-08-08 14:20:43 -0500547 if p['name'] == event['precondition']['name'])
548 params = []
Matthew Barth7f272fd2017-09-12 16:16:56 -0500549 for p in epc['parameters']:
Matthew Barth9af190c2017-08-08 14:20:43 -0500550 param = {}
551 if p == 'groups':
552 param['type'] = "std::vector<PrecondGroup>"
553 param['open'] = "{"
554 param['close'] = "}"
555 values = []
Matthew Barth6c050692017-12-05 15:30:09 -0600556 for group in precond['pcgrps']:
557 for pcgrp in group['members']:
558 value = {}
559 value['value'] = (
560 "PrecondGroup{\"" +
561 str(pcgrp['object']) + "\",\"" +
562 str(pcgrp['interface']) + "\",\"" +
563 str(pcgrp['property']) + "\"," +
564 "static_cast<" +
565 str(pcgrp['type']).lower() + ">")
566 if isinstance(pcgrp['value'], str) or \
567 "string" in str(pcgrp['type']).lower():
568 value['value'] += ("(" + str(pcgrp['value']) + ")}")
569 else:
570 value['value'] += \
571 ("(" + str(pcgrp['value']).lower() + ")}")
572 values.append(value)
Matthew Barth9af190c2017-08-08 14:20:43 -0500573 param['values'] = values
574 params.append(param)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500575 pcs['params'] = params
576 pc.append(pcs)
Matthew Barth9af190c2017-08-08 14:20:43 -0500577 precond['pcact'] = pc
578
Matthew Barth7f272fd2017-09-12 16:16:56 -0500579 pcevents = []
580 for pce in event['precondition']['events']:
581 pcevent = getEvent(zNum, zCond, pce, events_data)
582 if not pcevent:
583 continue
584 pcevents.append(pcevent)
585 precond['pcevts'] = pcevents
586
Matthew Barthf20c3212018-03-02 14:42:55 -0600587 # Add precondition event triggers
588 precond['triggers'] = {}
589 for trig in event['precondition']['triggers']:
590 triggers = []
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600591 if (trig['name'] == "timer"):
592 precond['triggers']['pctime'] = getTimer(trig)
593 elif (trig['name'] == "signal"):
Matthew Barthf20c3212018-03-02 14:42:55 -0600594 if ('pcsigs' not in precond['triggers']):
595 precond['triggers']['pcsigs'] = []
596 triggers = getSignal(precond['pcgrps'], trig, events_data)
597 precond['triggers']['pcsigs'].extend(triggers)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600598 elif (trig['name'] == "init"):
599 triggers = getInit(precond['pcgrps'], trig, events_data)
600 precond['triggers']['init'] = triggers
Matthew Barth9af190c2017-08-08 14:20:43 -0500601
602 return precond
603
604
Gunnar Millsb751f322017-06-06 15:14:11 -0500605def getEventsInZone(zone_num, zone_conditions, events_data):
Matthew Barthd4d0f082017-05-16 13:51:10 -0500606 """
607 Constructs the event entries defined for each zone using the events yaml
608 provided.
609 """
610 events = []
Matthew Barthba102b32017-05-16 16:13:56 -0500611
Matthew Barthd4d0f082017-05-16 13:51:10 -0500612 if 'events' in events_data:
613 for e in events_data['events']:
Matthew Barthd4d0f082017-05-16 13:51:10 -0500614 event = {}
Matthew Barth9af190c2017-08-08 14:20:43 -0500615 # Add precondition if given
616 if ('precondition' in e) and \
617 (e['precondition'] is not None):
Matthew Barth7f272fd2017-09-12 16:16:56 -0500618 event['pc'] = addPrecondition(zone_num,
619 zone_conditions,
620 e,
621 events_data)
Matthew Barth90149802017-08-15 10:51:37 -0500622 else:
Matthew Barth7f272fd2017-09-12 16:16:56 -0500623 event = getEvent(zone_num, zone_conditions, e, events_data)
624 if not event:
625 continue
Matthew Barthd4d0f082017-05-16 13:51:10 -0500626 events.append(event)
627
628 return events
629
630
Matt Spinler78498c92017-04-11 13:59:46 -0500631def getFansInZone(zone_num, profiles, fan_data):
632 """
633 Parses the fan definition YAML files to find the fans
634 that match both the zone passed in and one of the
635 cooling profiles.
636 """
637
638 fans = []
639
640 for f in fan_data['fans']:
641
642 if zone_num != f['cooling_zone']:
643 continue
644
Gunnar Mills67e95512017-06-02 14:35:18 -0500645 # 'cooling_profile' is optional (use 'all' instead)
Matt Spinler78498c92017-04-11 13:59:46 -0500646 if f.get('cooling_profile') is None:
647 profile = "all"
648 else:
649 profile = f['cooling_profile']
650
651 if profile not in profiles:
652 continue
653
654 fan = {}
655 fan['name'] = f['inventory']
656 fan['sensors'] = f['sensors']
Lei YU069e4402018-01-31 16:47:37 +0800657 fan['target_interface'] = f.get(
658 'target_interface',
659 'xyz.openbmc_project.Control.FanSpeed')
Matt Spinler78498c92017-04-11 13:59:46 -0500660 fans.append(fan)
661
662 return fans
663
664
Matthew Barth7883f582019-02-14 14:24:46 -0600665def getIfacesInZone(zone_ifaces):
666 """
667 Parse given interfaces for a zone for associating a zone with an interface
668 and set any properties listed to defined values upon fan control starting
669 on the zone.
670 """
671
672 ifaces = []
673 for i in zone_ifaces:
674 iface = {}
675 # Interface name not needed yet for fan zones but
676 # may be necessary as more interfaces are extended by the zones
677 iface['name'] = i['name']
678
679 if ('properties' in i) and \
680 (i['properties'] is not None):
681 props = []
682 for p in i['properties']:
683 prop = {}
Matthew Barth59096e52019-02-18 12:23:38 -0600684 prop['name'] = p['name']
685 prop['func'] = str(p['name']).lower()
Matthew Barth7883f582019-02-14 14:24:46 -0600686 prop['type'] = parse_cpp_type(p['type'])
Matthew Barth59096e52019-02-18 12:23:38 -0600687 if ('persist' in p):
688 persist = p['persist']
689 if (persist is not None):
690 if (isinstance(persist, bool)):
691 prop['persist'] = 'true' if persist else 'false'
692 else:
693 prop['persist'] = 'false'
Matthew Barth7883f582019-02-14 14:24:46 -0600694 vals = []
695 for v in p['values']:
696 val = v['value']
697 if (val is not None):
698 if (isinstance(val, bool)):
699 # Convert True/False to 'true'/'false'
700 val = 'true' if val else 'false'
701 elif (isinstance(val, str)):
702 # Wrap strings with double-quotes
703 val = "\"" + val + "\""
704 vals.append(val)
705 prop['values'] = vals
706 props.append(prop)
707 iface['props'] = props
708 ifaces.append(iface)
709
710 return ifaces
711
712
Gunnar Millsee8a2812017-06-02 14:26:47 -0500713def getConditionInZoneConditions(zone_condition, zone_conditions_data):
714 """
715 Parses the zone conditions definition YAML files to find the condition
716 that match both the zone condition passed in.
717 """
718
719 condition = {}
720
721 for c in zone_conditions_data['conditions']:
722
723 if zone_condition != c['name']:
724 continue
725 condition['type'] = c['type']
726 properties = []
727 for p in c['properties']:
728 property = {}
729 property['property'] = p['property']
730 property['interface'] = p['interface']
731 property['path'] = p['path']
732 property['type'] = p['type'].lower()
733 property['value'] = str(p['value']).lower()
734 properties.append(property)
735 condition['properties'] = properties
736
737 return condition
738
739
740def buildZoneData(zone_data, fan_data, events_data, zone_conditions_data):
Matt Spinler78498c92017-04-11 13:59:46 -0500741 """
742 Combines the zone definition YAML and fan
743 definition YAML to create a data structure defining
744 the fan cooling zones.
745 """
746
747 zone_groups = []
748
749 for group in zone_data:
750 conditions = []
Gunnar Millsee8a2812017-06-02 14:26:47 -0500751 # zone conditions are optional
752 if 'zone_conditions' in group and group['zone_conditions'] is not None:
753 for c in group['zone_conditions']:
754
755 if not zone_conditions_data:
Gunnar Millsb751f322017-06-06 15:14:11 -0500756 sys.exit("No zone_conditions YAML file but " +
Gunnar Millsee8a2812017-06-02 14:26:47 -0500757 "zone_conditions used in zone YAML")
758
759 condition = getConditionInZoneConditions(c['name'],
760 zone_conditions_data)
761
762 if not condition:
763 sys.exit("Missing zone condition " + c['name'])
764
765 conditions.append(condition)
Matt Spinler78498c92017-04-11 13:59:46 -0500766
767 zone_group = {}
768 zone_group['conditions'] = conditions
769
770 zones = []
771 for z in group['zones']:
772 zone = {}
773
Gunnar Mills67e95512017-06-02 14:35:18 -0500774 # 'zone' is required
775 if ('zone' not in z) or (z['zone'] is None):
Matt Spinler78498c92017-04-11 13:59:46 -0500776 sys.exit("Missing fan zone number in " + zone_yaml)
777
778 zone['num'] = z['zone']
779
780 zone['full_speed'] = z['full_speed']
781
Matthew Barth1de66622017-06-12 13:13:02 -0500782 zone['default_floor'] = z['default_floor']
783
Matthew Bartha9561842017-06-29 11:43:45 -0500784 # 'increase_delay' is optional (use 0 by default)
785 key = 'increase_delay'
786 zone[key] = z.setdefault(key, 0)
787
788 # 'decrease_interval' is optional (use 0 by default)
789 key = 'decrease_interval'
790 zone[key] = z.setdefault(key, 0)
791
Gunnar Mills67e95512017-06-02 14:35:18 -0500792 # 'cooling_profiles' is optional (use 'all' instead)
793 if ('cooling_profiles' not in z) or \
Matt Spinler78498c92017-04-11 13:59:46 -0500794 (z['cooling_profiles'] is None):
795 profiles = ["all"]
796 else:
797 profiles = z['cooling_profiles']
798
Matthew Barth7883f582019-02-14 14:24:46 -0600799 # 'interfaces' is optional (no default)
Matthew Barth64099cd2019-02-18 09:43:12 -0600800 ifaces = []
Matthew Barth7883f582019-02-14 14:24:46 -0600801 if ('interfaces' in z) and \
802 (z['interfaces'] is not None):
803 ifaces = getIfacesInZone(z['interfaces'])
804
Matt Spinler78498c92017-04-11 13:59:46 -0500805 fans = getFansInZone(z['zone'], profiles, fan_data)
Gunnar Millsb751f322017-06-06 15:14:11 -0500806 events = getEventsInZone(z['zone'], group['zone_conditions'],
807 events_data)
Matt Spinler78498c92017-04-11 13:59:46 -0500808
809 if len(fans) == 0:
810 sys.exit("Didn't find any fans in zone " + str(zone['num']))
811
Matthew Barth7883f582019-02-14 14:24:46 -0600812 if (ifaces):
813 zone['ifaces'] = ifaces
Matt Spinler78498c92017-04-11 13:59:46 -0500814 zone['fans'] = fans
Matthew Barthd4d0f082017-05-16 13:51:10 -0500815 zone['events'] = events
Matt Spinler78498c92017-04-11 13:59:46 -0500816 zones.append(zone)
817
818 zone_group['zones'] = zones
819 zone_groups.append(zone_group)
820
821 return zone_groups
822
823
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500824if __name__ == '__main__':
825 parser = ArgumentParser(
826 description="Phosphor fan zone definition parser")
827
828 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml',
829 default="example/zones.yaml",
830 help='fan zone definitional yaml')
831 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml',
832 default="example/fans.yaml",
833 help='fan definitional yaml')
Matthew Barthd4d0f082017-05-16 13:51:10 -0500834 parser.add_argument('-e', '--events_yaml', dest='events_yaml',
835 help='events to set speeds yaml')
Gunnar Millsee8a2812017-06-02 14:26:47 -0500836 parser.add_argument('-c', '--zone_conditions_yaml',
837 dest='zone_conditions_yaml',
838 help='conditions to determine zone yaml')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500839 parser.add_argument('-o', '--output_dir', dest='output_dir',
840 default=".",
841 help='output directory')
842 args = parser.parse_args()
843
844 if not args.zone_yaml or not args.fan_yaml:
845 parser.print_usage()
William A. Kennington III3e781062018-10-19 17:18:34 -0700846 sys.exit(1)
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500847
848 with open(args.zone_yaml, 'r') as zone_input:
849 zone_data = yaml.safe_load(zone_input) or {}
850
851 with open(args.fan_yaml, 'r') as fan_input:
852 fan_data = yaml.safe_load(fan_input) or {}
853
Matthew Barthd4d0f082017-05-16 13:51:10 -0500854 events_data = {}
855 if args.events_yaml:
856 with open(args.events_yaml, 'r') as events_input:
857 events_data = yaml.safe_load(events_input) or {}
858
Gunnar Millsee8a2812017-06-02 14:26:47 -0500859 zone_conditions_data = {}
860 if args.zone_conditions_yaml:
861 with open(args.zone_conditions_yaml, 'r') as zone_conditions_input:
862 zone_conditions_data = yaml.safe_load(zone_conditions_input) or {}
863
Matt Spinleree7f6422017-05-09 11:03:14 -0500864 zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
Gunnar Millsee8a2812017-06-02 14:26:47 -0500865 fan_data, events_data, zone_conditions_data)
Matt Spinleree7f6422017-05-09 11:03:14 -0500866
867 manager_config = zone_data.get('manager_configuration', {})
868
869 if manager_config.get('power_on_delay') is None:
870 manager_config['power_on_delay'] = 0
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500871
Matthew Barth702c4a52018-02-28 16:23:11 -0600872 tmpls_dir = os.path.join(
873 os.path.dirname(os.path.realpath(__file__)),
874 "templates")
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500875 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
Matthew Barth702c4a52018-02-28 16:23:11 -0600876 if sys.version_info < (3, 0):
877 lkup = TemplateLookup(
878 directories=tmpls_dir.split(),
879 disable_unicode=True)
880 else:
881 lkup = TemplateLookup(
882 directories=tmpls_dir.split())
883 tmpl = lkup.get_template('fan_zone_defs.mako.cpp')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500884 with open(output_file, 'w') as output:
Matthew Barth702c4a52018-02-28 16:23:11 -0600885 output.write(tmpl.render(zones=zone_config,
886 mgr_data=manager_config))