blob: a6c34b6b05f25ef11b9c8f84d9e294fb96fcbdf7 [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"
Matthew Barth621a5772018-11-14 14:55:11 -0600140 e += "\"" + event['name'] + "\",\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600141 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 = []
Matthew Barthf7e1cb32018-11-19 11:19:09 -0600244 if ('groups' in edata) and (edata['groups'] is not None):
245 for eGroups in edata['groups']:
246 if ('zone_conditions' in eGroups) and \
247 (eGroups['zone_conditions'] is not None):
248 # Zone conditions are optional in the events yaml but skip
249 # if this event's condition is not in this zone's conditions
250 if all('name' in z and z['name'] is not None and
251 not any(c['name'] == z['name'] for c in zCond)
252 for z in eGroups['zone_conditions']):
253 continue
Matthew Barth6c050692017-12-05 15:30:09 -0600254
Matthew Barthf7e1cb32018-11-19 11:19:09 -0600255 # Zone numbers are optional in the events yaml but skip if this
256 # zone's zone number is not in the event's zone numbers
257 if all('zones' in z and z['zones'] is not None and
258 zNum not in z['zones']
259 for z in eGroups['zone_conditions']):
260 continue
261 eGroup = next(g for g in events['groups']
262 if g['name'] == eGroups['name'])
Matthew Barth6c050692017-12-05 15:30:09 -0600263
Matthew Barthf7e1cb32018-11-19 11:19:09 -0600264 group = {}
265 members = []
266 group['name'] = eGroup['name']
267 for m in eGroup['members']:
268 member = {}
269 member['path'] = eGroup['type']
270 member['object'] = (eGroup['type'] + m)
271 member['interface'] = eGroups['interface']
272 member['property'] = eGroups['property']['name']
273 member['type'] = eGroups['property']['type']
274 # Use defined service to note member on zone object
275 if ('service' in eGroup) and \
276 (eGroup['service'] is not None):
277 member['service'] = eGroup['service']
278 # Add expected group member's property value if given
279 if ('value' in eGroups['property']) and \
280 (eGroups['property']['value'] is not None):
281 if isinstance(eGroups['property']['value'], str) or \
282 "string" in str(member['type']).lower():
283 member['value'] = (
284 "\"" + eGroups['property']['value'] + "\"")
285 else:
286 member['value'] = eGroups['property']['value']
287 members.append(member)
288 group['members'] = members
289 groups.append(group)
Matthew Barth6c050692017-12-05 15:30:09 -0600290 return groups
291
292
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600293def getParameters(member, groups, section, events):
Matthew Barth73379f92018-03-15 11:37:10 -0500294 """
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600295 Extracts and constructs a section's parameters
Matthew Barth73379f92018-03-15 11:37:10 -0500296 """
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600297 params = {}
298 if ('parameters' in section) and \
299 (section['parameters'] is not None):
300 plist = []
301 for sp in section['parameters']:
302 p = str(sp)
303 if (p != 'type'):
304 plist.append(p)
305 if (p != 'group'):
306 params[p] = "\"" + member[p] + "\""
Matthew Barth73379f92018-03-15 11:37:10 -0500307 else:
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600308 params[p] = "Group\n{\n"
309 for g in groups:
310 for m in g['members']:
311 params[p] += (
312 "{\"" + str(m['object']) + "\",\n" +
313 "\"" + str(m['interface']) + "\",\n" +
314 "\"" + str(m['property']) + "\"},\n")
315 params[p] += "}"
Matthew Barth73379f92018-03-15 11:37:10 -0500316 else:
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600317 params[p] = member[p]
318 params['params'] = plist
319 else:
320 params['params'] = []
321 return params
322
323
324def getInit(eGrps, eTrig, events):
325 """
326 Extracts and constructs an init trigger for the event's groups
327 which are required to be of the same type.
328 """
329 method = {}
330 methods = []
Matthew Barth03774012018-10-26 13:25:43 -0500331 if (len(eGrps) > 0):
332 # Use the first group member for retrieving the type
333 member = eGrps[0]['members'][0]
334 if ('method' in eTrig) and \
335 (eTrig['method'] is not None):
336 # Add method parameters
337 eMethod = next(m for m in events['methods']
338 if m['name'] == eTrig['method'])
339 method['method'] = eMethod['name']
340 method['mparams'] = getParameters(
341 member, eGrps, eMethod, events)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600342
Matthew Barth03774012018-10-26 13:25:43 -0500343 # Add handler parameters
344 eHandler = next(h for h in events['handlers']
345 if h['name'] == eTrig['handler'])
346 method['handler'] = eHandler['name']
347 method['hparams'] = getParameters(
348 member, eGrps, eHandler, events)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600349
350 methods.append(method)
351
352 return methods
Matthew Barth73379f92018-03-15 11:37:10 -0500353
354
Matthew Bartha69465a2018-03-02 13:50:59 -0600355def getSignal(eGrps, eTrig, events):
356 """
357 Extracts and constructs for each group member a signal
358 subscription of each match listed in the trigger.
359 """
360 signals = []
361 for group in eGrps:
362 for member in group['members']:
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600363 signal = {}
364 # Add signal parameters
365 eSignal = next(s for s in events['signals']
366 if s['name'] == eTrig['signal'])
367 signal['signal'] = eSignal['name']
368 signal['sparams'] = getParameters(member, eGrps, eSignal, events)
369
370 # If service not given, subscribe to signal match
371 if ('service' not in member):
372 # Add signal match parameters
Matthew Bartha69465a2018-03-02 13:50:59 -0600373 eMatch = next(m for m in events['matches']
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600374 if m['name'] == eSignal['match'])
375 signal['match'] = eMatch['name']
376 signal['mparams'] = getParameters(member, eGrps, eMatch, events)
Matthew Bartha69465a2018-03-02 13:50:59 -0600377
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600378 # Add handler parameters
379 eHandler = next(h for h in events['handlers']
380 if h['name'] == eTrig['handler'])
381 signal['handler'] = eHandler['name']
382 signal['hparams'] = getParameters(member, eGrps, eHandler, events)
Matthew Barth73379f92018-03-15 11:37:10 -0500383
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600384 signals.append(signal)
Matthew Barth73379f92018-03-15 11:37:10 -0500385
Matthew Bartha69465a2018-03-02 13:50:59 -0600386 return signals
387
388
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600389def getTimer(eTrig):
390 """
391 Extracts and constructs the required parameters for an
392 event timer.
393 """
394 timer = {}
395 timer['interval'] = (
396 "static_cast<std::chrono::microseconds>" +
397 "(" + str(eTrig['interval']) + ")")
398 timer['type'] = "TimerType::" + str(eTrig['type'])
399 return timer
400
401
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600402def getActions(zNum, zCond, edata, actions, events):
Matthew Barth9df74752017-10-11 14:39:31 -0500403 """
404 Extracts and constructs the make_action function call for
405 all the actions within the given event.
406 """
407 action = []
408 for eActions in actions['actions']:
409 actions = {}
410 eAction = next(a for a in events['actions']
411 if a['name'] == eActions['name'])
412 actions['name'] = eAction['name']
413 params = []
414 if ('parameters' in eAction) and \
415 (eAction['parameters'] is not None):
416 for p in eAction['parameters']:
417 param = "static_cast<"
418 if type(eActions[p]) is not dict:
419 if p == 'actions':
420 param = "std::vector<Action>{"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600421 pActs = getActions(zNum,
422 zCond,
423 edata,
424 eActions,
425 events)
Matthew Barth9df74752017-10-11 14:39:31 -0500426 for a in pActs:
427 if (len(a['parameters']) != 0):
428 param += (
429 "make_action(action::" +
430 a['name'] +
431 "(\n")
432 for i, ap in enumerate(a['parameters']):
433 if (i+1) != len(a['parameters']):
434 param += (ap + ",")
435 else:
436 param += (ap + ")")
437 else:
438 param += ("make_action(action::" + a['name'])
439 param += "),"
440 param += "}"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600441 elif p == 'defevents' or p == 'altevents':
442 param = "std::vector<SetSpeedEvent>{\n"
443 for i, e in enumerate(eActions[p]):
444 aEvent = getEvent(zNum, zCond, e, events)
445 if not aEvent:
446 continue
447 if (i+1) != len(eActions[p]):
448 param += genEvent(aEvent) + ",\n"
449 else:
450 param += genEvent(aEvent) + "\n"
451 param += "\t}"
Matthew Barth9df74752017-10-11 14:39:31 -0500452 elif p == 'property':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600453 if isinstance(eActions[p], str) or \
Matthew Barth6c050692017-12-05 15:30:09 -0600454 "string" in str(eActions[p]['type']).lower():
Matthew Barth9a5b6992018-01-23 15:32:26 -0600455 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600456 str(eActions[p]['type']).lower() +
Matthew Barth9a5b6992018-01-23 15:32:26 -0600457 ">(\"" + str(eActions[p]) + "\")")
458 else:
459 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600460 str(eActions[p]['type']).lower() +
461 ">(" + str(eActions[p]['value']).lower() + ")")
Matthew Barth9df74752017-10-11 14:39:31 -0500462 else:
463 # Default type to 'size_t' when not given
464 param += ("size_t>(" + str(eActions[p]).lower() + ")")
465 else:
466 if p == 'timer':
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600467 t = getTimer(eActions[p])
Matthew Barth9df74752017-10-11 14:39:31 -0500468 param = (
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600469 "TimerConf{" + t['interval'] + "," +
470 t['type'] + "}")
Matthew Barth9df74752017-10-11 14:39:31 -0500471 else:
472 param += (str(eActions[p]['type']).lower() + ">(")
473 if p != 'map':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600474 if isinstance(eActions[p]['value'], str) or \
475 "string" in str(eActions[p]['type']).lower():
476 param += \
477 "\"" + str(eActions[p]['value']) + "\")"
478 else:
479 param += \
480 str(eActions[p]['value']).lower() + ")"
Matthew Barth9df74752017-10-11 14:39:31 -0500481 else:
482 param += (
483 str(eActions[p]['type']).lower() +
484 convertToMap(str(eActions[p]['value'])) + ")")
485 params.append(param)
486 actions['parameters'] = params
487 action.append(actions)
488 return action
489
490
Matthew Barth7f272fd2017-09-12 16:16:56 -0500491def getEvent(zone_num, zone_conditions, e, events_data):
492 """
493 Parses the sections of an event and populates the properties
494 that construct an event within the generated source.
495 """
496 event = {}
Matthew Barth7f272fd2017-09-12 16:16:56 -0500497
Matthew Barth621a5772018-11-14 14:55:11 -0600498 # Add set speed event name
499 event['name'] = e['name']
500
Matthew Barth6c050692017-12-05 15:30:09 -0600501 # Add set speed event groups
Matthew Barth03774012018-10-26 13:25:43 -0500502 event['groups'] = getGroups(zone_num, zone_conditions, e, events_data)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500503
Matthew Barthe3d1c4a2018-01-11 13:53:49 -0600504 # Add optional set speed actions and function parameters
505 event['action'] = []
506 if ('actions' in e) and \
507 (e['actions'] is not None):
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600508 event['action'] = getActions(zone_num,
509 zone_conditions,
510 e,
511 e,
512 events_data)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500513
Matthew Bartha69465a2018-03-02 13:50:59 -0600514 # Add event triggers
515 event['triggers'] = {}
516 for trig in e['triggers']:
517 triggers = []
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600518 if (trig['name'] == "timer"):
519 event['triggers']['timer'] = getTimer(trig)
520 elif (trig['name'] == "signal"):
Matthew Bartha69465a2018-03-02 13:50:59 -0600521 if ('signals' not in event['triggers']):
522 event['triggers']['signals'] = []
523 triggers = getSignal(event['groups'], trig, events_data)
524 event['triggers']['signals'].extend(triggers)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600525 elif (trig['name'] == "init"):
526 triggers = getInit(event['groups'], trig, events_data)
527 event['triggers']['init'] = triggers
Matthew Barth7f272fd2017-09-12 16:16:56 -0500528
Matthew Barth7f272fd2017-09-12 16:16:56 -0500529 return event
530
531
532def addPrecondition(zNum, zCond, event, events_data):
Matthew Barth9af190c2017-08-08 14:20:43 -0500533 """
534 Parses the precondition section of an event and populates the necessary
535 structures to generate a precondition for a set speed event.
536 """
537 precond = {}
Matthew Barth621a5772018-11-14 14:55:11 -0600538
539 # Add set speed event precondition name
540 precond['pcname'] = event['name']
541
Matthew Barth9af190c2017-08-08 14:20:43 -0500542 # Add set speed event precondition group
Matthew Barth03774012018-10-26 13:25:43 -0500543 precond['pcgrps'] = getGroups(zNum,
544 zCond,
545 event['precondition'],
546 events_data)
Matthew Barth9af190c2017-08-08 14:20:43 -0500547
Matthew Barth7f272fd2017-09-12 16:16:56 -0500548 # Add set speed event precondition actions
549 pc = []
550 pcs = {}
551 pcs['name'] = event['precondition']['name']
552 epc = next(p for p in events_data['preconditions']
Matthew Barth9af190c2017-08-08 14:20:43 -0500553 if p['name'] == event['precondition']['name'])
554 params = []
Matthew Barth7f272fd2017-09-12 16:16:56 -0500555 for p in epc['parameters']:
Matthew Barth9af190c2017-08-08 14:20:43 -0500556 param = {}
557 if p == 'groups':
558 param['type'] = "std::vector<PrecondGroup>"
559 param['open'] = "{"
560 param['close'] = "}"
561 values = []
Matthew Barth6c050692017-12-05 15:30:09 -0600562 for group in precond['pcgrps']:
563 for pcgrp in group['members']:
564 value = {}
565 value['value'] = (
566 "PrecondGroup{\"" +
567 str(pcgrp['object']) + "\",\"" +
568 str(pcgrp['interface']) + "\",\"" +
569 str(pcgrp['property']) + "\"," +
570 "static_cast<" +
571 str(pcgrp['type']).lower() + ">")
572 if isinstance(pcgrp['value'], str) or \
573 "string" in str(pcgrp['type']).lower():
574 value['value'] += ("(" + str(pcgrp['value']) + ")}")
575 else:
576 value['value'] += \
577 ("(" + str(pcgrp['value']).lower() + ")}")
578 values.append(value)
Matthew Barth9af190c2017-08-08 14:20:43 -0500579 param['values'] = values
580 params.append(param)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500581 pcs['params'] = params
582 pc.append(pcs)
Matthew Barth9af190c2017-08-08 14:20:43 -0500583 precond['pcact'] = pc
584
Matthew Barth7f272fd2017-09-12 16:16:56 -0500585 pcevents = []
586 for pce in event['precondition']['events']:
587 pcevent = getEvent(zNum, zCond, pce, events_data)
588 if not pcevent:
589 continue
590 pcevents.append(pcevent)
591 precond['pcevts'] = pcevents
592
Matthew Barthf20c3212018-03-02 14:42:55 -0600593 # Add precondition event triggers
594 precond['triggers'] = {}
595 for trig in event['precondition']['triggers']:
596 triggers = []
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600597 if (trig['name'] == "timer"):
598 precond['triggers']['pctime'] = getTimer(trig)
599 elif (trig['name'] == "signal"):
Matthew Barthf20c3212018-03-02 14:42:55 -0600600 if ('pcsigs' not in precond['triggers']):
601 precond['triggers']['pcsigs'] = []
602 triggers = getSignal(precond['pcgrps'], trig, events_data)
603 precond['triggers']['pcsigs'].extend(triggers)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600604 elif (trig['name'] == "init"):
605 triggers = getInit(precond['pcgrps'], trig, events_data)
606 precond['triggers']['init'] = triggers
Matthew Barth9af190c2017-08-08 14:20:43 -0500607
608 return precond
609
610
Gunnar Millsb751f322017-06-06 15:14:11 -0500611def getEventsInZone(zone_num, zone_conditions, events_data):
Matthew Barthd4d0f082017-05-16 13:51:10 -0500612 """
613 Constructs the event entries defined for each zone using the events yaml
614 provided.
615 """
616 events = []
Matthew Barthba102b32017-05-16 16:13:56 -0500617
Matthew Barthd4d0f082017-05-16 13:51:10 -0500618 if 'events' in events_data:
619 for e in events_data['events']:
Matthew Barthd4d0f082017-05-16 13:51:10 -0500620 event = {}
Matthew Barth621a5772018-11-14 14:55:11 -0600621
Matthew Barth9af190c2017-08-08 14:20:43 -0500622 # Add precondition if given
623 if ('precondition' in e) and \
624 (e['precondition'] is not None):
Matthew Barth7f272fd2017-09-12 16:16:56 -0500625 event['pc'] = addPrecondition(zone_num,
626 zone_conditions,
627 e,
628 events_data)
Matthew Barth90149802017-08-15 10:51:37 -0500629 else:
Matthew Barth7f272fd2017-09-12 16:16:56 -0500630 event = getEvent(zone_num, zone_conditions, e, events_data)
631 if not event:
632 continue
Matthew Barthd4d0f082017-05-16 13:51:10 -0500633 events.append(event)
634
635 return events
636
637
Matt Spinler78498c92017-04-11 13:59:46 -0500638def getFansInZone(zone_num, profiles, fan_data):
639 """
640 Parses the fan definition YAML files to find the fans
641 that match both the zone passed in and one of the
642 cooling profiles.
643 """
644
645 fans = []
646
647 for f in fan_data['fans']:
648
649 if zone_num != f['cooling_zone']:
650 continue
651
Gunnar Mills67e95512017-06-02 14:35:18 -0500652 # 'cooling_profile' is optional (use 'all' instead)
Matt Spinler78498c92017-04-11 13:59:46 -0500653 if f.get('cooling_profile') is None:
654 profile = "all"
655 else:
656 profile = f['cooling_profile']
657
658 if profile not in profiles:
659 continue
660
661 fan = {}
662 fan['name'] = f['inventory']
663 fan['sensors'] = f['sensors']
Lei YU069e4402018-01-31 16:47:37 +0800664 fan['target_interface'] = f.get(
665 'target_interface',
666 'xyz.openbmc_project.Control.FanSpeed')
Matt Spinler78498c92017-04-11 13:59:46 -0500667 fans.append(fan)
668
669 return fans
670
671
Matthew Barth7883f582019-02-14 14:24:46 -0600672def getIfacesInZone(zone_ifaces):
673 """
674 Parse given interfaces for a zone for associating a zone with an interface
675 and set any properties listed to defined values upon fan control starting
676 on the zone.
677 """
678
679 ifaces = []
680 for i in zone_ifaces:
681 iface = {}
682 # Interface name not needed yet for fan zones but
683 # may be necessary as more interfaces are extended by the zones
684 iface['name'] = i['name']
685
686 if ('properties' in i) and \
687 (i['properties'] is not None):
688 props = []
689 for p in i['properties']:
690 prop = {}
Matthew Barth59096e52019-02-18 12:23:38 -0600691 prop['name'] = p['name']
692 prop['func'] = str(p['name']).lower()
Matthew Barth7883f582019-02-14 14:24:46 -0600693 prop['type'] = parse_cpp_type(p['type'])
Matthew Barth59096e52019-02-18 12:23:38 -0600694 if ('persist' in p):
695 persist = p['persist']
696 if (persist is not None):
697 if (isinstance(persist, bool)):
698 prop['persist'] = 'true' if persist else 'false'
699 else:
700 prop['persist'] = 'false'
Matthew Barth7883f582019-02-14 14:24:46 -0600701 vals = []
702 for v in p['values']:
703 val = v['value']
704 if (val is not None):
705 if (isinstance(val, bool)):
706 # Convert True/False to 'true'/'false'
707 val = 'true' if val else 'false'
708 elif (isinstance(val, str)):
709 # Wrap strings with double-quotes
710 val = "\"" + val + "\""
711 vals.append(val)
712 prop['values'] = vals
713 props.append(prop)
714 iface['props'] = props
715 ifaces.append(iface)
716
717 return ifaces
718
719
Gunnar Millsee8a2812017-06-02 14:26:47 -0500720def getConditionInZoneConditions(zone_condition, zone_conditions_data):
721 """
722 Parses the zone conditions definition YAML files to find the condition
723 that match both the zone condition passed in.
724 """
725
726 condition = {}
727
728 for c in zone_conditions_data['conditions']:
729
730 if zone_condition != c['name']:
731 continue
732 condition['type'] = c['type']
733 properties = []
734 for p in c['properties']:
735 property = {}
736 property['property'] = p['property']
737 property['interface'] = p['interface']
738 property['path'] = p['path']
739 property['type'] = p['type'].lower()
740 property['value'] = str(p['value']).lower()
741 properties.append(property)
742 condition['properties'] = properties
743
744 return condition
745
746
747def buildZoneData(zone_data, fan_data, events_data, zone_conditions_data):
Matt Spinler78498c92017-04-11 13:59:46 -0500748 """
749 Combines the zone definition YAML and fan
750 definition YAML to create a data structure defining
751 the fan cooling zones.
752 """
753
754 zone_groups = []
755
756 for group in zone_data:
757 conditions = []
Gunnar Millsee8a2812017-06-02 14:26:47 -0500758 # zone conditions are optional
759 if 'zone_conditions' in group and group['zone_conditions'] is not None:
760 for c in group['zone_conditions']:
761
762 if not zone_conditions_data:
Gunnar Millsb751f322017-06-06 15:14:11 -0500763 sys.exit("No zone_conditions YAML file but " +
Gunnar Millsee8a2812017-06-02 14:26:47 -0500764 "zone_conditions used in zone YAML")
765
766 condition = getConditionInZoneConditions(c['name'],
767 zone_conditions_data)
768
769 if not condition:
770 sys.exit("Missing zone condition " + c['name'])
771
772 conditions.append(condition)
Matt Spinler78498c92017-04-11 13:59:46 -0500773
774 zone_group = {}
775 zone_group['conditions'] = conditions
776
777 zones = []
778 for z in group['zones']:
779 zone = {}
780
Gunnar Mills67e95512017-06-02 14:35:18 -0500781 # 'zone' is required
782 if ('zone' not in z) or (z['zone'] is None):
Matt Spinler78498c92017-04-11 13:59:46 -0500783 sys.exit("Missing fan zone number in " + zone_yaml)
784
785 zone['num'] = z['zone']
786
787 zone['full_speed'] = z['full_speed']
788
Matthew Barth1de66622017-06-12 13:13:02 -0500789 zone['default_floor'] = z['default_floor']
790
Matthew Bartha9561842017-06-29 11:43:45 -0500791 # 'increase_delay' is optional (use 0 by default)
792 key = 'increase_delay'
793 zone[key] = z.setdefault(key, 0)
794
795 # 'decrease_interval' is optional (use 0 by default)
796 key = 'decrease_interval'
797 zone[key] = z.setdefault(key, 0)
798
Gunnar Mills67e95512017-06-02 14:35:18 -0500799 # 'cooling_profiles' is optional (use 'all' instead)
800 if ('cooling_profiles' not in z) or \
Matt Spinler78498c92017-04-11 13:59:46 -0500801 (z['cooling_profiles'] is None):
802 profiles = ["all"]
803 else:
804 profiles = z['cooling_profiles']
805
Matthew Barth7883f582019-02-14 14:24:46 -0600806 # 'interfaces' is optional (no default)
Matthew Barth64099cd2019-02-18 09:43:12 -0600807 ifaces = []
Matthew Barth7883f582019-02-14 14:24:46 -0600808 if ('interfaces' in z) and \
809 (z['interfaces'] is not None):
810 ifaces = getIfacesInZone(z['interfaces'])
811
Matt Spinler78498c92017-04-11 13:59:46 -0500812 fans = getFansInZone(z['zone'], profiles, fan_data)
Gunnar Millsb751f322017-06-06 15:14:11 -0500813 events = getEventsInZone(z['zone'], group['zone_conditions'],
814 events_data)
Matt Spinler78498c92017-04-11 13:59:46 -0500815
816 if len(fans) == 0:
817 sys.exit("Didn't find any fans in zone " + str(zone['num']))
818
Matthew Barth7883f582019-02-14 14:24:46 -0600819 if (ifaces):
820 zone['ifaces'] = ifaces
Matt Spinler78498c92017-04-11 13:59:46 -0500821 zone['fans'] = fans
Matthew Barthd4d0f082017-05-16 13:51:10 -0500822 zone['events'] = events
Matt Spinler78498c92017-04-11 13:59:46 -0500823 zones.append(zone)
824
825 zone_group['zones'] = zones
826 zone_groups.append(zone_group)
827
828 return zone_groups
829
830
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500831if __name__ == '__main__':
832 parser = ArgumentParser(
833 description="Phosphor fan zone definition parser")
834
835 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml',
836 default="example/zones.yaml",
837 help='fan zone definitional yaml')
838 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml',
839 default="example/fans.yaml",
840 help='fan definitional yaml')
Matthew Barthd4d0f082017-05-16 13:51:10 -0500841 parser.add_argument('-e', '--events_yaml', dest='events_yaml',
842 help='events to set speeds yaml')
Gunnar Millsee8a2812017-06-02 14:26:47 -0500843 parser.add_argument('-c', '--zone_conditions_yaml',
844 dest='zone_conditions_yaml',
845 help='conditions to determine zone yaml')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500846 parser.add_argument('-o', '--output_dir', dest='output_dir',
847 default=".",
848 help='output directory')
849 args = parser.parse_args()
850
851 if not args.zone_yaml or not args.fan_yaml:
852 parser.print_usage()
William A. Kennington III3e781062018-10-19 17:18:34 -0700853 sys.exit(1)
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500854
855 with open(args.zone_yaml, 'r') as zone_input:
856 zone_data = yaml.safe_load(zone_input) or {}
857
858 with open(args.fan_yaml, 'r') as fan_input:
859 fan_data = yaml.safe_load(fan_input) or {}
860
Matthew Barthd4d0f082017-05-16 13:51:10 -0500861 events_data = {}
862 if args.events_yaml:
863 with open(args.events_yaml, 'r') as events_input:
864 events_data = yaml.safe_load(events_input) or {}
865
Gunnar Millsee8a2812017-06-02 14:26:47 -0500866 zone_conditions_data = {}
867 if args.zone_conditions_yaml:
868 with open(args.zone_conditions_yaml, 'r') as zone_conditions_input:
869 zone_conditions_data = yaml.safe_load(zone_conditions_input) or {}
870
Matt Spinleree7f6422017-05-09 11:03:14 -0500871 zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
Gunnar Millsee8a2812017-06-02 14:26:47 -0500872 fan_data, events_data, zone_conditions_data)
Matt Spinleree7f6422017-05-09 11:03:14 -0500873
874 manager_config = zone_data.get('manager_configuration', {})
875
876 if manager_config.get('power_on_delay') is None:
877 manager_config['power_on_delay'] = 0
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500878
Matthew Barth702c4a52018-02-28 16:23:11 -0600879 tmpls_dir = os.path.join(
880 os.path.dirname(os.path.realpath(__file__)),
881 "templates")
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500882 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
Matthew Barth702c4a52018-02-28 16:23:11 -0600883 if sys.version_info < (3, 0):
884 lkup = TemplateLookup(
885 directories=tmpls_dir.split(),
886 disable_unicode=True)
887 else:
888 lkup = TemplateLookup(
889 directories=tmpls_dir.split())
890 tmpl = lkup.get_template('fan_zone_defs.mako.cpp')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500891 with open(output_file, 'w') as output:
Matthew Barth702c4a52018-02-28 16:23:11 -0600892 output.write(tmpl.render(zones=zone_config,
893 mgr_data=manager_config))