blob: 0539ef94944d49e0129ee3f5dd7077ba0a834cd7 [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 = []
Matthew Barth03774012018-10-26 13:25:43 -0500330 if (len(eGrps) > 0):
331 # Use the first group member for retrieving the type
332 member = eGrps[0]['members'][0]
333 if ('method' in eTrig) and \
334 (eTrig['method'] is not None):
335 # Add method parameters
336 eMethod = next(m for m in events['methods']
337 if m['name'] == eTrig['method'])
338 method['method'] = eMethod['name']
339 method['mparams'] = getParameters(
340 member, eGrps, eMethod, events)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600341
Matthew Barth03774012018-10-26 13:25:43 -0500342 # Add handler parameters
343 eHandler = next(h for h in events['handlers']
344 if h['name'] == eTrig['handler'])
345 method['handler'] = eHandler['name']
346 method['hparams'] = getParameters(
347 member, eGrps, eHandler, events)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600348
349 methods.append(method)
350
351 return methods
Matthew Barth73379f92018-03-15 11:37:10 -0500352
353
Matthew Bartha69465a2018-03-02 13:50:59 -0600354def getSignal(eGrps, eTrig, events):
355 """
356 Extracts and constructs for each group member a signal
357 subscription of each match listed in the trigger.
358 """
359 signals = []
360 for group in eGrps:
361 for member in group['members']:
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600362 signal = {}
363 # Add signal parameters
364 eSignal = next(s for s in events['signals']
365 if s['name'] == eTrig['signal'])
366 signal['signal'] = eSignal['name']
367 signal['sparams'] = getParameters(member, eGrps, eSignal, events)
368
369 # If service not given, subscribe to signal match
370 if ('service' not in member):
371 # Add signal match parameters
Matthew Bartha69465a2018-03-02 13:50:59 -0600372 eMatch = next(m for m in events['matches']
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600373 if m['name'] == eSignal['match'])
374 signal['match'] = eMatch['name']
375 signal['mparams'] = getParameters(member, eGrps, eMatch, events)
Matthew Bartha69465a2018-03-02 13:50:59 -0600376
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600377 # Add handler parameters
378 eHandler = next(h for h in events['handlers']
379 if h['name'] == eTrig['handler'])
380 signal['handler'] = eHandler['name']
381 signal['hparams'] = getParameters(member, eGrps, eHandler, events)
Matthew Barth73379f92018-03-15 11:37:10 -0500382
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600383 signals.append(signal)
Matthew Barth73379f92018-03-15 11:37:10 -0500384
Matthew Bartha69465a2018-03-02 13:50:59 -0600385 return signals
386
387
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600388def getTimer(eTrig):
389 """
390 Extracts and constructs the required parameters for an
391 event timer.
392 """
393 timer = {}
394 timer['interval'] = (
395 "static_cast<std::chrono::microseconds>" +
396 "(" + str(eTrig['interval']) + ")")
397 timer['type'] = "TimerType::" + str(eTrig['type'])
398 return timer
399
400
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600401def getActions(zNum, zCond, edata, actions, events):
Matthew Barth9df74752017-10-11 14:39:31 -0500402 """
403 Extracts and constructs the make_action function call for
404 all the actions within the given event.
405 """
406 action = []
407 for eActions in actions['actions']:
408 actions = {}
409 eAction = next(a for a in events['actions']
410 if a['name'] == eActions['name'])
411 actions['name'] = eAction['name']
412 params = []
413 if ('parameters' in eAction) and \
414 (eAction['parameters'] is not None):
415 for p in eAction['parameters']:
416 param = "static_cast<"
417 if type(eActions[p]) is not dict:
418 if p == 'actions':
419 param = "std::vector<Action>{"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600420 pActs = getActions(zNum,
421 zCond,
422 edata,
423 eActions,
424 events)
Matthew Barth9df74752017-10-11 14:39:31 -0500425 for a in pActs:
426 if (len(a['parameters']) != 0):
427 param += (
428 "make_action(action::" +
429 a['name'] +
430 "(\n")
431 for i, ap in enumerate(a['parameters']):
432 if (i+1) != len(a['parameters']):
433 param += (ap + ",")
434 else:
435 param += (ap + ")")
436 else:
437 param += ("make_action(action::" + a['name'])
438 param += "),"
439 param += "}"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600440 elif p == 'defevents' or p == 'altevents':
441 param = "std::vector<SetSpeedEvent>{\n"
442 for i, e in enumerate(eActions[p]):
443 aEvent = getEvent(zNum, zCond, e, events)
444 if not aEvent:
445 continue
446 if (i+1) != len(eActions[p]):
447 param += genEvent(aEvent) + ",\n"
448 else:
449 param += genEvent(aEvent) + "\n"
450 param += "\t}"
Matthew Barth9df74752017-10-11 14:39:31 -0500451 elif p == 'property':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600452 if isinstance(eActions[p], str) or \
Matthew Barth6c050692017-12-05 15:30:09 -0600453 "string" in str(eActions[p]['type']).lower():
Matthew Barth9a5b6992018-01-23 15:32:26 -0600454 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600455 str(eActions[p]['type']).lower() +
Matthew Barth9a5b6992018-01-23 15:32:26 -0600456 ">(\"" + str(eActions[p]) + "\")")
457 else:
458 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600459 str(eActions[p]['type']).lower() +
460 ">(" + str(eActions[p]['value']).lower() + ")")
Matthew Barth9df74752017-10-11 14:39:31 -0500461 else:
462 # Default type to 'size_t' when not given
463 param += ("size_t>(" + str(eActions[p]).lower() + ")")
464 else:
465 if p == 'timer':
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600466 t = getTimer(eActions[p])
Matthew Barth9df74752017-10-11 14:39:31 -0500467 param = (
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600468 "TimerConf{" + t['interval'] + "," +
469 t['type'] + "}")
Matthew Barth9df74752017-10-11 14:39:31 -0500470 else:
471 param += (str(eActions[p]['type']).lower() + ">(")
472 if p != 'map':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600473 if isinstance(eActions[p]['value'], str) or \
474 "string" in str(eActions[p]['type']).lower():
475 param += \
476 "\"" + str(eActions[p]['value']) + "\")"
477 else:
478 param += \
479 str(eActions[p]['value']).lower() + ")"
Matthew Barth9df74752017-10-11 14:39:31 -0500480 else:
481 param += (
482 str(eActions[p]['type']).lower() +
483 convertToMap(str(eActions[p]['value'])) + ")")
484 params.append(param)
485 actions['parameters'] = params
486 action.append(actions)
487 return action
488
489
Matthew Barth7f272fd2017-09-12 16:16:56 -0500490def getEvent(zone_num, zone_conditions, e, events_data):
491 """
492 Parses the sections of an event and populates the properties
493 that construct an event within the generated source.
494 """
495 event = {}
Matthew Barth7f272fd2017-09-12 16:16:56 -0500496
Matthew Barth6c050692017-12-05 15:30:09 -0600497 # Add set speed event groups
Matthew Barth03774012018-10-26 13:25:43 -0500498 event['groups'] = getGroups(zone_num, zone_conditions, e, events_data)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500499
Matthew Barthe3d1c4a2018-01-11 13:53:49 -0600500 # Add optional set speed actions and function parameters
501 event['action'] = []
502 if ('actions' in e) and \
503 (e['actions'] is not None):
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600504 event['action'] = getActions(zone_num,
505 zone_conditions,
506 e,
507 e,
508 events_data)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500509
Matthew Bartha69465a2018-03-02 13:50:59 -0600510 # Add event triggers
511 event['triggers'] = {}
512 for trig in e['triggers']:
513 triggers = []
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600514 if (trig['name'] == "timer"):
515 event['triggers']['timer'] = getTimer(trig)
516 elif (trig['name'] == "signal"):
Matthew Bartha69465a2018-03-02 13:50:59 -0600517 if ('signals' not in event['triggers']):
518 event['triggers']['signals'] = []
519 triggers = getSignal(event['groups'], trig, events_data)
520 event['triggers']['signals'].extend(triggers)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600521 elif (trig['name'] == "init"):
522 triggers = getInit(event['groups'], trig, events_data)
523 event['triggers']['init'] = triggers
Matthew Barth7f272fd2017-09-12 16:16:56 -0500524
Matthew Barth7f272fd2017-09-12 16:16:56 -0500525 return event
526
527
528def addPrecondition(zNum, zCond, event, events_data):
Matthew Barth9af190c2017-08-08 14:20:43 -0500529 """
530 Parses the precondition section of an event and populates the necessary
531 structures to generate a precondition for a set speed event.
532 """
533 precond = {}
534 # Add set speed event precondition group
Matthew Barth03774012018-10-26 13:25:43 -0500535 precond['pcgrps'] = getGroups(zNum,
536 zCond,
537 event['precondition'],
538 events_data)
Matthew Barth9af190c2017-08-08 14:20:43 -0500539
Matthew Barth7f272fd2017-09-12 16:16:56 -0500540 # Add set speed event precondition actions
541 pc = []
542 pcs = {}
543 pcs['name'] = event['precondition']['name']
544 epc = next(p for p in events_data['preconditions']
Matthew Barth9af190c2017-08-08 14:20:43 -0500545 if p['name'] == event['precondition']['name'])
546 params = []
Matthew Barth7f272fd2017-09-12 16:16:56 -0500547 for p in epc['parameters']:
Matthew Barth9af190c2017-08-08 14:20:43 -0500548 param = {}
549 if p == 'groups':
550 param['type'] = "std::vector<PrecondGroup>"
551 param['open'] = "{"
552 param['close'] = "}"
553 values = []
Matthew Barth6c050692017-12-05 15:30:09 -0600554 for group in precond['pcgrps']:
555 for pcgrp in group['members']:
556 value = {}
557 value['value'] = (
558 "PrecondGroup{\"" +
559 str(pcgrp['object']) + "\",\"" +
560 str(pcgrp['interface']) + "\",\"" +
561 str(pcgrp['property']) + "\"," +
562 "static_cast<" +
563 str(pcgrp['type']).lower() + ">")
564 if isinstance(pcgrp['value'], str) or \
565 "string" in str(pcgrp['type']).lower():
566 value['value'] += ("(" + str(pcgrp['value']) + ")}")
567 else:
568 value['value'] += \
569 ("(" + str(pcgrp['value']).lower() + ")}")
570 values.append(value)
Matthew Barth9af190c2017-08-08 14:20:43 -0500571 param['values'] = values
572 params.append(param)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500573 pcs['params'] = params
574 pc.append(pcs)
Matthew Barth9af190c2017-08-08 14:20:43 -0500575 precond['pcact'] = pc
576
Matthew Barth7f272fd2017-09-12 16:16:56 -0500577 pcevents = []
578 for pce in event['precondition']['events']:
579 pcevent = getEvent(zNum, zCond, pce, events_data)
580 if not pcevent:
581 continue
582 pcevents.append(pcevent)
583 precond['pcevts'] = pcevents
584
Matthew Barthf20c3212018-03-02 14:42:55 -0600585 # Add precondition event triggers
586 precond['triggers'] = {}
587 for trig in event['precondition']['triggers']:
588 triggers = []
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600589 if (trig['name'] == "timer"):
590 precond['triggers']['pctime'] = getTimer(trig)
591 elif (trig['name'] == "signal"):
Matthew Barthf20c3212018-03-02 14:42:55 -0600592 if ('pcsigs' not in precond['triggers']):
593 precond['triggers']['pcsigs'] = []
594 triggers = getSignal(precond['pcgrps'], trig, events_data)
595 precond['triggers']['pcsigs'].extend(triggers)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600596 elif (trig['name'] == "init"):
597 triggers = getInit(precond['pcgrps'], trig, events_data)
598 precond['triggers']['init'] = triggers
Matthew Barth9af190c2017-08-08 14:20:43 -0500599
600 return precond
601
602
Gunnar Millsb751f322017-06-06 15:14:11 -0500603def getEventsInZone(zone_num, zone_conditions, events_data):
Matthew Barthd4d0f082017-05-16 13:51:10 -0500604 """
605 Constructs the event entries defined for each zone using the events yaml
606 provided.
607 """
608 events = []
Matthew Barthba102b32017-05-16 16:13:56 -0500609
Matthew Barthd4d0f082017-05-16 13:51:10 -0500610 if 'events' in events_data:
611 for e in events_data['events']:
Matthew Barthd4d0f082017-05-16 13:51:10 -0500612 event = {}
Matthew Barth9af190c2017-08-08 14:20:43 -0500613 # Add precondition if given
614 if ('precondition' in e) and \
615 (e['precondition'] is not None):
Matthew Barth7f272fd2017-09-12 16:16:56 -0500616 event['pc'] = addPrecondition(zone_num,
617 zone_conditions,
618 e,
619 events_data)
Matthew Barth90149802017-08-15 10:51:37 -0500620 else:
Matthew Barth7f272fd2017-09-12 16:16:56 -0500621 event = getEvent(zone_num, zone_conditions, e, events_data)
622 if not event:
623 continue
Matthew Barthd4d0f082017-05-16 13:51:10 -0500624 events.append(event)
625
626 return events
627
628
Matt Spinler78498c92017-04-11 13:59:46 -0500629def getFansInZone(zone_num, profiles, fan_data):
630 """
631 Parses the fan definition YAML files to find the fans
632 that match both the zone passed in and one of the
633 cooling profiles.
634 """
635
636 fans = []
637
638 for f in fan_data['fans']:
639
640 if zone_num != f['cooling_zone']:
641 continue
642
Gunnar Mills67e95512017-06-02 14:35:18 -0500643 # 'cooling_profile' is optional (use 'all' instead)
Matt Spinler78498c92017-04-11 13:59:46 -0500644 if f.get('cooling_profile') is None:
645 profile = "all"
646 else:
647 profile = f['cooling_profile']
648
649 if profile not in profiles:
650 continue
651
652 fan = {}
653 fan['name'] = f['inventory']
654 fan['sensors'] = f['sensors']
Lei YU069e4402018-01-31 16:47:37 +0800655 fan['target_interface'] = f.get(
656 'target_interface',
657 'xyz.openbmc_project.Control.FanSpeed')
Matt Spinler78498c92017-04-11 13:59:46 -0500658 fans.append(fan)
659
660 return fans
661
662
Matthew Barth7883f582019-02-14 14:24:46 -0600663def getIfacesInZone(zone_ifaces):
664 """
665 Parse given interfaces for a zone for associating a zone with an interface
666 and set any properties listed to defined values upon fan control starting
667 on the zone.
668 """
669
670 ifaces = []
671 for i in zone_ifaces:
672 iface = {}
673 # Interface name not needed yet for fan zones but
674 # may be necessary as more interfaces are extended by the zones
675 iface['name'] = i['name']
676
677 if ('properties' in i) and \
678 (i['properties'] is not None):
679 props = []
680 for p in i['properties']:
681 prop = {}
Matthew Barth59096e52019-02-18 12:23:38 -0600682 prop['name'] = p['name']
683 prop['func'] = str(p['name']).lower()
Matthew Barth7883f582019-02-14 14:24:46 -0600684 prop['type'] = parse_cpp_type(p['type'])
Matthew Barth59096e52019-02-18 12:23:38 -0600685 if ('persist' in p):
686 persist = p['persist']
687 if (persist is not None):
688 if (isinstance(persist, bool)):
689 prop['persist'] = 'true' if persist else 'false'
690 else:
691 prop['persist'] = 'false'
Matthew Barth7883f582019-02-14 14:24:46 -0600692 vals = []
693 for v in p['values']:
694 val = v['value']
695 if (val is not None):
696 if (isinstance(val, bool)):
697 # Convert True/False to 'true'/'false'
698 val = 'true' if val else 'false'
699 elif (isinstance(val, str)):
700 # Wrap strings with double-quotes
701 val = "\"" + val + "\""
702 vals.append(val)
703 prop['values'] = vals
704 props.append(prop)
705 iface['props'] = props
706 ifaces.append(iface)
707
708 return ifaces
709
710
Gunnar Millsee8a2812017-06-02 14:26:47 -0500711def getConditionInZoneConditions(zone_condition, zone_conditions_data):
712 """
713 Parses the zone conditions definition YAML files to find the condition
714 that match both the zone condition passed in.
715 """
716
717 condition = {}
718
719 for c in zone_conditions_data['conditions']:
720
721 if zone_condition != c['name']:
722 continue
723 condition['type'] = c['type']
724 properties = []
725 for p in c['properties']:
726 property = {}
727 property['property'] = p['property']
728 property['interface'] = p['interface']
729 property['path'] = p['path']
730 property['type'] = p['type'].lower()
731 property['value'] = str(p['value']).lower()
732 properties.append(property)
733 condition['properties'] = properties
734
735 return condition
736
737
738def buildZoneData(zone_data, fan_data, events_data, zone_conditions_data):
Matt Spinler78498c92017-04-11 13:59:46 -0500739 """
740 Combines the zone definition YAML and fan
741 definition YAML to create a data structure defining
742 the fan cooling zones.
743 """
744
745 zone_groups = []
746
747 for group in zone_data:
748 conditions = []
Gunnar Millsee8a2812017-06-02 14:26:47 -0500749 # zone conditions are optional
750 if 'zone_conditions' in group and group['zone_conditions'] is not None:
751 for c in group['zone_conditions']:
752
753 if not zone_conditions_data:
Gunnar Millsb751f322017-06-06 15:14:11 -0500754 sys.exit("No zone_conditions YAML file but " +
Gunnar Millsee8a2812017-06-02 14:26:47 -0500755 "zone_conditions used in zone YAML")
756
757 condition = getConditionInZoneConditions(c['name'],
758 zone_conditions_data)
759
760 if not condition:
761 sys.exit("Missing zone condition " + c['name'])
762
763 conditions.append(condition)
Matt Spinler78498c92017-04-11 13:59:46 -0500764
765 zone_group = {}
766 zone_group['conditions'] = conditions
767
768 zones = []
769 for z in group['zones']:
770 zone = {}
771
Gunnar Mills67e95512017-06-02 14:35:18 -0500772 # 'zone' is required
773 if ('zone' not in z) or (z['zone'] is None):
Matt Spinler78498c92017-04-11 13:59:46 -0500774 sys.exit("Missing fan zone number in " + zone_yaml)
775
776 zone['num'] = z['zone']
777
778 zone['full_speed'] = z['full_speed']
779
Matthew Barth1de66622017-06-12 13:13:02 -0500780 zone['default_floor'] = z['default_floor']
781
Matthew Bartha9561842017-06-29 11:43:45 -0500782 # 'increase_delay' is optional (use 0 by default)
783 key = 'increase_delay'
784 zone[key] = z.setdefault(key, 0)
785
786 # 'decrease_interval' is optional (use 0 by default)
787 key = 'decrease_interval'
788 zone[key] = z.setdefault(key, 0)
789
Gunnar Mills67e95512017-06-02 14:35:18 -0500790 # 'cooling_profiles' is optional (use 'all' instead)
791 if ('cooling_profiles' not in z) or \
Matt Spinler78498c92017-04-11 13:59:46 -0500792 (z['cooling_profiles'] is None):
793 profiles = ["all"]
794 else:
795 profiles = z['cooling_profiles']
796
Matthew Barth7883f582019-02-14 14:24:46 -0600797 # 'interfaces' is optional (no default)
Matthew Barth64099cd2019-02-18 09:43:12 -0600798 ifaces = []
Matthew Barth7883f582019-02-14 14:24:46 -0600799 if ('interfaces' in z) and \
800 (z['interfaces'] is not None):
801 ifaces = getIfacesInZone(z['interfaces'])
802
Matt Spinler78498c92017-04-11 13:59:46 -0500803 fans = getFansInZone(z['zone'], profiles, fan_data)
Gunnar Millsb751f322017-06-06 15:14:11 -0500804 events = getEventsInZone(z['zone'], group['zone_conditions'],
805 events_data)
Matt Spinler78498c92017-04-11 13:59:46 -0500806
807 if len(fans) == 0:
808 sys.exit("Didn't find any fans in zone " + str(zone['num']))
809
Matthew Barth7883f582019-02-14 14:24:46 -0600810 if (ifaces):
811 zone['ifaces'] = ifaces
Matt Spinler78498c92017-04-11 13:59:46 -0500812 zone['fans'] = fans
Matthew Barthd4d0f082017-05-16 13:51:10 -0500813 zone['events'] = events
Matt Spinler78498c92017-04-11 13:59:46 -0500814 zones.append(zone)
815
816 zone_group['zones'] = zones
817 zone_groups.append(zone_group)
818
819 return zone_groups
820
821
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500822if __name__ == '__main__':
823 parser = ArgumentParser(
824 description="Phosphor fan zone definition parser")
825
826 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml',
827 default="example/zones.yaml",
828 help='fan zone definitional yaml')
829 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml',
830 default="example/fans.yaml",
831 help='fan definitional yaml')
Matthew Barthd4d0f082017-05-16 13:51:10 -0500832 parser.add_argument('-e', '--events_yaml', dest='events_yaml',
833 help='events to set speeds yaml')
Gunnar Millsee8a2812017-06-02 14:26:47 -0500834 parser.add_argument('-c', '--zone_conditions_yaml',
835 dest='zone_conditions_yaml',
836 help='conditions to determine zone yaml')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500837 parser.add_argument('-o', '--output_dir', dest='output_dir',
838 default=".",
839 help='output directory')
840 args = parser.parse_args()
841
842 if not args.zone_yaml or not args.fan_yaml:
843 parser.print_usage()
William A. Kennington III3e781062018-10-19 17:18:34 -0700844 sys.exit(1)
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500845
846 with open(args.zone_yaml, 'r') as zone_input:
847 zone_data = yaml.safe_load(zone_input) or {}
848
849 with open(args.fan_yaml, 'r') as fan_input:
850 fan_data = yaml.safe_load(fan_input) or {}
851
Matthew Barthd4d0f082017-05-16 13:51:10 -0500852 events_data = {}
853 if args.events_yaml:
854 with open(args.events_yaml, 'r') as events_input:
855 events_data = yaml.safe_load(events_input) or {}
856
Gunnar Millsee8a2812017-06-02 14:26:47 -0500857 zone_conditions_data = {}
858 if args.zone_conditions_yaml:
859 with open(args.zone_conditions_yaml, 'r') as zone_conditions_input:
860 zone_conditions_data = yaml.safe_load(zone_conditions_input) or {}
861
Matt Spinleree7f6422017-05-09 11:03:14 -0500862 zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
Gunnar Millsee8a2812017-06-02 14:26:47 -0500863 fan_data, events_data, zone_conditions_data)
Matt Spinleree7f6422017-05-09 11:03:14 -0500864
865 manager_config = zone_data.get('manager_configuration', {})
866
867 if manager_config.get('power_on_delay') is None:
868 manager_config['power_on_delay'] = 0
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500869
Matthew Barth702c4a52018-02-28 16:23:11 -0600870 tmpls_dir = os.path.join(
871 os.path.dirname(os.path.realpath(__file__)),
872 "templates")
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500873 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
Matthew Barth702c4a52018-02-28 16:23:11 -0600874 if sys.version_info < (3, 0):
875 lkup = TemplateLookup(
876 directories=tmpls_dir.split(),
877 disable_unicode=True)
878 else:
879 lkup = TemplateLookup(
880 directories=tmpls_dir.split())
881 tmpl = lkup.get_template('fan_zone_defs.mako.cpp')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500882 with open(output_file, 'w') as output:
Matthew Barth702c4a52018-02-28 16:23:11 -0600883 output.write(tmpl.render(zones=zone_config,
884 mgr_data=manager_config))