blob: a5e4809ed3477dc1e7c8ccb41c32ae0cb7edca94 [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"
146 e += "{\"" + member['interface'] + "\",\n"
147 e += " \"" + member['property'] + "\"}\n"
148 e += "},\n"
149 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
165 e += "TimerConf{\n"
166 e += "\t" + event['timer']['interval'] + ",\n"
167 e += "\t" + event['timer']['type'] + "\n"
168 e += "},\n"
169
170 e += "std::vector<Signal>{\n"
171 for s in event['signals']:
172 e += "Signal{\n"
173 e += "match::" + s['match'] + "(\n"
174 for i, mp in enumerate(s['mparams']):
175 if (i+1) != len(s['mparams']):
176 e += "\"" + mp + "\",\n"
177 else:
178 e += "\"" + mp + "\"\n"
179 e += "),\n"
180 e += "make_handler(\n"
181 if ('type' in s['sparams']) and (s['sparams']['type'] is not None):
182 e += s['signal'] + "<" + s['sparams']['type'] + ">(\n"
183 else:
184 e += s['signal'] + "(\n"
185 for sp in s['sparams']['params']:
186 e += s['sparams'][sp] + ",\n"
187 if ('type' in s['hparams']) and (s['hparams']['type'] is not None):
188 e += ("handler::" + s['handler'] +
189 "<" + s['hparams']['type'] + ">(\n")
190 else:
191 e += "handler::" + s['handler'] + "(\n)"
192 for i, hp in enumerate(s['hparams']['params']):
193 if (i+1) != len(s['hparams']['params']):
194 e += s['hparams'][hp] + ",\n"
195 else:
196 e += s['hparams'][hp] + "\n"
197 e += "))\n"
198 e += ")\n"
199 e += "},\n"
200 e += "}\n"
201
202 e += "}"
203
204 return e
205
206
Matthew Barth6c050692017-12-05 15:30:09 -0600207def getGroups(zNum, zCond, edata, events):
208 """
209 Extract and construct the groups for the given event.
210 """
211 groups = []
212 for eGroups in edata['groups']:
213 if ('zone_conditions' in eGroups) and \
214 (eGroups['zone_conditions'] is not None):
215 # Zone conditions are optional in the events yaml but skip
216 # if this event's condition is not in this zone's conditions
217 if all('name' in z and z['name'] is not None and
218 not any(c['name'] == z['name'] for c in zCond)
219 for z in eGroups['zone_conditions']):
220 continue
221
222 # Zone numbers are optional in the events yaml but skip if this
223 # zone's zone number is not in the event's zone numbers
224 if all('zones' in z and z['zones'] is not None and
225 zNum not in z['zones']
226 for z in eGroups['zone_conditions']):
227 continue
Matthew Barth6c050692017-12-05 15:30:09 -0600228 eGroup = next(g for g in events['groups']
229 if g['name'] == eGroups['name'])
230
231 group = {}
232 members = []
233 group['name'] = eGroup['name']
234 for m in eGroup['members']:
235 member = {}
236 member['path'] = eGroup['type']
237 member['object'] = (eGroup['type'] + m)
238 member['interface'] = eGroups['interface']
239 member['property'] = eGroups['property']['name']
240 member['type'] = eGroups['property']['type']
Matthew Barth18c91032019-01-29 15:36:00 -0600241 # Use defined service to note member on zone object
242 if ('service' in eGroup) and \
243 (eGroup['service'] is not None):
244 member['service'] = eGroup['service']
Matthew Barth6c050692017-12-05 15:30:09 -0600245 # Add expected group member's property value if given
246 if ('value' in eGroups['property']) and \
247 (eGroups['property']['value'] is not None):
248 if isinstance(eGroups['property']['value'], str) or \
249 "string" in str(member['type']).lower():
250 member['value'] = (
251 "\"" + eGroups['property']['value'] + "\"")
252 else:
253 member['value'] = eGroups['property']['value']
254 members.append(member)
255 group['members'] = members
256 groups.append(group)
257 return groups
258
259
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600260def getActions(zNum, zCond, edata, actions, events):
Matthew Barth9df74752017-10-11 14:39:31 -0500261 """
262 Extracts and constructs the make_action function call for
263 all the actions within the given event.
264 """
265 action = []
266 for eActions in actions['actions']:
267 actions = {}
268 eAction = next(a for a in events['actions']
269 if a['name'] == eActions['name'])
270 actions['name'] = eAction['name']
271 params = []
272 if ('parameters' in eAction) and \
273 (eAction['parameters'] is not None):
274 for p in eAction['parameters']:
275 param = "static_cast<"
276 if type(eActions[p]) is not dict:
277 if p == 'actions':
278 param = "std::vector<Action>{"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600279 pActs = getActions(zNum,
280 zCond,
281 edata,
282 eActions,
283 events)
Matthew Barth9df74752017-10-11 14:39:31 -0500284 for a in pActs:
285 if (len(a['parameters']) != 0):
286 param += (
287 "make_action(action::" +
288 a['name'] +
289 "(\n")
290 for i, ap in enumerate(a['parameters']):
291 if (i+1) != len(a['parameters']):
292 param += (ap + ",")
293 else:
294 param += (ap + ")")
295 else:
296 param += ("make_action(action::" + a['name'])
297 param += "),"
298 param += "}"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600299 elif p == 'defevents' or p == 'altevents':
300 param = "std::vector<SetSpeedEvent>{\n"
301 for i, e in enumerate(eActions[p]):
302 aEvent = getEvent(zNum, zCond, e, events)
303 if not aEvent:
304 continue
305 if (i+1) != len(eActions[p]):
306 param += genEvent(aEvent) + ",\n"
307 else:
308 param += genEvent(aEvent) + "\n"
309 param += "\t}"
Matthew Barth9df74752017-10-11 14:39:31 -0500310 elif p == 'property':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600311 if isinstance(eActions[p], str) or \
Matthew Barth6c050692017-12-05 15:30:09 -0600312 "string" in str(eActions[p]['type']).lower():
Matthew Barth9a5b6992018-01-23 15:32:26 -0600313 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600314 str(eActions[p]['type']).lower() +
Matthew Barth9a5b6992018-01-23 15:32:26 -0600315 ">(\"" + str(eActions[p]) + "\")")
316 else:
317 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600318 str(eActions[p]['type']).lower() +
319 ">(" + str(eActions[p]['value']).lower() + ")")
Matthew Barth9df74752017-10-11 14:39:31 -0500320 else:
321 # Default type to 'size_t' when not given
322 param += ("size_t>(" + str(eActions[p]).lower() + ")")
323 else:
324 if p == 'timer':
325 param = (
William A. Kennington III122b8432018-10-30 18:39:21 -0700326 "TimerConf{static_cast<std::chrono::seconds>(" +
Matthew Barth9df74752017-10-11 14:39:31 -0500327 str(eActions[p]['delay']) + "), " +
William A. Kennington III0ce353e2018-10-30 18:30:36 -0700328 "TimerType::" +
Matthew Barth9df74752017-10-11 14:39:31 -0500329 str(eActions[p]['type']) + "}")
330 else:
331 param += (str(eActions[p]['type']).lower() + ">(")
332 if p != 'map':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600333 if isinstance(eActions[p]['value'], str) or \
334 "string" in str(eActions[p]['type']).lower():
335 param += \
336 "\"" + str(eActions[p]['value']) + "\")"
337 else:
338 param += \
339 str(eActions[p]['value']).lower() + ")"
Matthew Barth9df74752017-10-11 14:39:31 -0500340 else:
341 param += (
342 str(eActions[p]['type']).lower() +
343 convertToMap(str(eActions[p]['value'])) + ")")
344 params.append(param)
345 actions['parameters'] = params
346 action.append(actions)
347 return action
348
349
Matthew Barth7f272fd2017-09-12 16:16:56 -0500350def getEvent(zone_num, zone_conditions, e, events_data):
351 """
352 Parses the sections of an event and populates the properties
353 that construct an event within the generated source.
354 """
355 event = {}
Matthew Barth7f272fd2017-09-12 16:16:56 -0500356
Matthew Barth6c050692017-12-05 15:30:09 -0600357 # Add set speed event groups
358 grps = getGroups(zone_num, zone_conditions, e, events_data)
359 if not grps:
Matthew Barth7f272fd2017-09-12 16:16:56 -0500360 return
Matthew Barth6c050692017-12-05 15:30:09 -0600361 event['groups'] = grps
Matthew Barth7f272fd2017-09-12 16:16:56 -0500362
Matthew Barthe3d1c4a2018-01-11 13:53:49 -0600363 # Add optional set speed actions and function parameters
364 event['action'] = []
365 if ('actions' in e) and \
366 (e['actions'] is not None):
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600367 event['action'] = getActions(zone_num,
368 zone_conditions,
369 e,
370 e,
371 events_data)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500372
Matthew Barth67967f92017-09-22 12:43:57 -0500373 # Add signal handlers
374 signals = []
Matthew Barth6c050692017-12-05 15:30:09 -0600375 for group in event['groups']:
376 for member in group['members']:
377 for eMatches in e['matches']:
378 signal = {}
379 eMatch = next(m for m in events_data['matches']
380 if m['name'] == eMatches['name'])
Matthew Barth18c91032019-01-29 15:36:00 -0600381 # If service not given, subscribe to signal match
382 if ('service' not in member):
383 signal['match'] = eMatch['name']
384 params = []
385 if ('parameters' in eMatch) and \
386 (eMatch['parameters'] is not None):
387 for p in eMatch['parameters']:
388 params.append(member[str(p)])
389 signal['mparams'] = params
390
Matthew Barthe7d87052018-03-21 13:58:31 -0500391 if ('parameters' in eMatch['signal']) and \
392 (eMatch['signal']['parameters'] is not None):
393 eSignal = eMatch['signal']
394 else:
395 eSignal = next(s for s in events_data['signals']
396 if s['name'] == eMatch['signal'])
Matthew Barth6c050692017-12-05 15:30:09 -0600397 signal['signal'] = eSignal['name']
398 sparams = {}
399 if ('parameters' in eSignal) and \
400 (eSignal['parameters'] is not None):
401 splist = []
402 for p in eSignal['parameters']:
403 sp = str(p)
404 if (sp != 'type'):
405 splist.append(sp)
406 if (sp != 'group'):
407 sparams[sp] = "\"" + member[sp] + "\""
408 else:
409 sparams[sp] = "Group{\n"
410 for m in group['members']:
411 sparams[sp] += (
412 "{\n" +
413 "\"" + str(m['object']) + "\",\n" +
414 "{\"" + str(m['interface']) + "\"," +
415 "\"" + str(m['property']) + "\"}\n" +
416 "},\n")
417 sparams[sp] += "}"
Matthew Barth9f964bf2017-10-02 15:07:00 -0500418 else:
Matthew Barth6c050692017-12-05 15:30:09 -0600419 sparams[sp] = member[sp]
420 sparams['params'] = splist
421 signal['sparams'] = sparams
422 # Add signal handler
423 eHandler = next(h for h in events_data['handlers']
424 if h['name'] == eSignal['handler'])
425 signal['handler'] = eHandler['name']
426 hparams = {}
427 if ('parameters' in eHandler) and \
428 (eHandler['parameters'] is not None):
429 hplist = []
430 for p in eHandler['parameters']:
431 hp = str(p)
432 if (hp != 'type'):
433 hplist.append(hp)
434 if (hp != 'group'):
435 hparams[hp] = "\"" + member[hp] + "\""
436 else:
437 hparams[hp] = "Group{\n"
438 for m in group['members']:
439 hparams[hp] += (
440 "{\n" +
441 "\"" + str(m['object']) + "\",\n" +
442 "{\"" + str(m['interface']) + "\"," +
443 "\"" + str(m['property']) + "\"}\n" +
444 "},\n")
445 hparams[hp] += "}"
Matthew Barth9f964bf2017-10-02 15:07:00 -0500446 else:
Matthew Barth6c050692017-12-05 15:30:09 -0600447 hparams[hp] = member[hp]
448 hparams['params'] = hplist
449 signal['hparams'] = hparams
450 signals.append(signal)
Matthew Barth67967f92017-09-22 12:43:57 -0500451 event['signals'] = signals
Matthew Barth7f272fd2017-09-12 16:16:56 -0500452
453 # Add optional action call timer
454 timer = {}
455 interval = "static_cast<std::chrono::seconds>"
456 if ('timer' in e) and \
457 (e['timer'] is not None):
458 timer['interval'] = (interval +
459 "(" +
460 str(e['timer']['interval']) +
461 ")")
462 else:
463 timer['interval'] = (interval +
464 "(" + str(0) + ")")
William A. Kennington III0ce353e2018-10-30 18:30:36 -0700465 timer['type'] = "TimerType::repeating"
Matthew Barth7f272fd2017-09-12 16:16:56 -0500466 event['timer'] = timer
467
468 return event
469
470
471def addPrecondition(zNum, zCond, event, events_data):
Matthew Barth9af190c2017-08-08 14:20:43 -0500472 """
473 Parses the precondition section of an event and populates the necessary
474 structures to generate a precondition for a set speed event.
475 """
476 precond = {}
477 # Add set speed event precondition group
Matthew Barth6c050692017-12-05 15:30:09 -0600478 grps = getGroups(zNum, zCond, event['precondition'], events_data)
479 if not grps:
480 return
481 precond['pcgrps'] = grps
Matthew Barth9af190c2017-08-08 14:20:43 -0500482
Matthew Barth7f272fd2017-09-12 16:16:56 -0500483 # Add set speed event precondition actions
484 pc = []
485 pcs = {}
486 pcs['name'] = event['precondition']['name']
487 epc = next(p for p in events_data['preconditions']
Matthew Barth9af190c2017-08-08 14:20:43 -0500488 if p['name'] == event['precondition']['name'])
489 params = []
Matthew Barth7f272fd2017-09-12 16:16:56 -0500490 for p in epc['parameters']:
Matthew Barth9af190c2017-08-08 14:20:43 -0500491 param = {}
492 if p == 'groups':
493 param['type'] = "std::vector<PrecondGroup>"
494 param['open'] = "{"
495 param['close'] = "}"
496 values = []
Matthew Barth6c050692017-12-05 15:30:09 -0600497 for group in precond['pcgrps']:
498 for pcgrp in group['members']:
499 value = {}
500 value['value'] = (
501 "PrecondGroup{\"" +
502 str(pcgrp['object']) + "\",\"" +
503 str(pcgrp['interface']) + "\",\"" +
504 str(pcgrp['property']) + "\"," +
505 "static_cast<" +
506 str(pcgrp['type']).lower() + ">")
507 if isinstance(pcgrp['value'], str) or \
508 "string" in str(pcgrp['type']).lower():
509 value['value'] += ("(" + str(pcgrp['value']) + ")}")
510 else:
511 value['value'] += \
512 ("(" + str(pcgrp['value']).lower() + ")}")
513 values.append(value)
Matthew Barth9af190c2017-08-08 14:20:43 -0500514 param['values'] = values
515 params.append(param)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500516 pcs['params'] = params
517 pc.append(pcs)
Matthew Barth9af190c2017-08-08 14:20:43 -0500518 precond['pcact'] = pc
519
Matthew Barth7f272fd2017-09-12 16:16:56 -0500520 pcevents = []
521 for pce in event['precondition']['events']:
522 pcevent = getEvent(zNum, zCond, pce, events_data)
523 if not pcevent:
524 continue
525 pcevents.append(pcevent)
526 precond['pcevts'] = pcevents
527
Matthew Barth67967f92017-09-22 12:43:57 -0500528 # Add precondition signal handlers
529 signals = []
Matthew Barth6c050692017-12-05 15:30:09 -0600530 for group in precond['pcgrps']:
531 for member in group['members']:
532 for eMatches in event['precondition']['matches']:
533 signal = {}
534 eMatch = next(m for m in events_data['matches']
535 if m['name'] == eMatches['name'])
Matthew Barth18c91032019-01-29 15:36:00 -0600536 # If service not given, subscribe to signal match
537 if ('service' not in member):
538 signal['match'] = eMatch['name']
539 params = []
540 if ('parameters' in eMatch) and \
541 (eMatch['parameters'] is not None):
542 for p in eMatch['parameters']:
543 params.append(member[str(p)])
544 signal['mparams'] = params
545
Matthew Barthe7d87052018-03-21 13:58:31 -0500546 if ('parameters' in eMatch['signal']) and \
547 (eMatch['signal']['parameters'] is not None):
548 eSignal = eMatch['signal']
549 else:
550 eSignal = next(s for s in events_data['signals']
551 if s['name'] == eMatch['signal'])
Matthew Barth6c050692017-12-05 15:30:09 -0600552 signal['signal'] = eSignal['name']
553 sparams = {}
554 if ('parameters' in eSignal) and \
555 (eSignal['parameters'] is not None):
556 splist = []
557 for p in eSignal['parameters']:
558 sp = str(p)
559 if (sp != 'type'):
560 splist.append(sp)
561 if (sp != 'group'):
562 sparams[sp] = "\"" + member[sp] + "\""
563 else:
564 sparams[sp] = "Group{\n"
565 for m in group:
566 sparams[sp] += (
567 "{\n" +
568 "\"" + str(m['object']) + "\",\n" +
569 "{\"" + str(m['interface']) + "\"," +
570 "\"" + str(m['property']) + "\"}\n" +
571 "},\n")
572 sparams[sp] += "}"
Matthew Barth9f964bf2017-10-02 15:07:00 -0500573 else:
Matthew Barth6c050692017-12-05 15:30:09 -0600574 sparams[sp] = member[sp]
575 sparams['params'] = splist
576 signal['sparams'] = sparams
577 # Add signal handler
578 eHandler = next(h for h in events_data['handlers']
579 if h['name'] == eSignal['handler'])
580 signal['handler'] = eHandler['name']
581 hparams = {}
582 if ('parameters' in eHandler) and \
583 (eHandler['parameters'] is not None):
584 hplist = []
585 for p in eHandler['parameters']:
586 hp = str(p)
587 if (hp != 'type'):
588 hplist.append(hp)
589 if (hp != 'group'):
590 hparams[hp] = "\"" + member[hp] + "\""
591 else:
592 hparams[hp] = "Group{\n"
593 for m in group:
594 hparams[hp] += (
595 "{\n" +
596 "\"" + str(m['object']) + "\",\n" +
597 "{\"" + str(m['interface']) + "\"," +
598 "\"" + str(m['property']) + "\"}\n" +
599 "},\n")
600 hparams[hp] += "}"
Matthew Barth9f964bf2017-10-02 15:07:00 -0500601 else:
Matthew Barth6c050692017-12-05 15:30:09 -0600602 hparams[hp] = member[hp]
603 hparams['params'] = hplist
604 signal['hparams'] = hparams
605 signals.append(signal)
Matthew Barth67967f92017-09-22 12:43:57 -0500606 precond['pcsigs'] = signals
Matthew Barth9af190c2017-08-08 14:20:43 -0500607
Matthew Barth90149802017-08-15 10:51:37 -0500608 # Add optional action call timer
609 timer = {}
610 interval = "static_cast<std::chrono::seconds>"
611 if ('timer' in event['precondition']) and \
612 (event['precondition']['timer'] is not None):
613 timer['interval'] = (interval +
614 "(" +
615 str(event['precondition']['timer']['interval']) +
616 ")")
617 else:
618 timer['interval'] = (interval +
619 "(" + str(0) + ")")
William A. Kennington III0ce353e2018-10-30 18:30:36 -0700620 timer['type'] = "TimerType::repeating"
Matthew Barth90149802017-08-15 10:51:37 -0500621 precond['pctime'] = timer
622
Matthew Barth9af190c2017-08-08 14:20:43 -0500623 return precond
624
625
Gunnar Millsb751f322017-06-06 15:14:11 -0500626def getEventsInZone(zone_num, zone_conditions, events_data):
Matthew Barthd4d0f082017-05-16 13:51:10 -0500627 """
628 Constructs the event entries defined for each zone using the events yaml
629 provided.
630 """
631 events = []
Matthew Barthba102b32017-05-16 16:13:56 -0500632
Matthew Barthd4d0f082017-05-16 13:51:10 -0500633 if 'events' in events_data:
634 for e in events_data['events']:
Matthew Barthd4d0f082017-05-16 13:51:10 -0500635 event = {}
Matthew Barth9af190c2017-08-08 14:20:43 -0500636 # Add precondition if given
637 if ('precondition' in e) and \
638 (e['precondition'] is not None):
Matthew Barth7f272fd2017-09-12 16:16:56 -0500639 event['pc'] = addPrecondition(zone_num,
640 zone_conditions,
641 e,
642 events_data)
Matthew Barth90149802017-08-15 10:51:37 -0500643 else:
Matthew Barth7f272fd2017-09-12 16:16:56 -0500644 event = getEvent(zone_num, zone_conditions, e, events_data)
645 if not event:
646 continue
Matthew Barthd4d0f082017-05-16 13:51:10 -0500647 events.append(event)
648
649 return events
650
651
Matt Spinler78498c92017-04-11 13:59:46 -0500652def getFansInZone(zone_num, profiles, fan_data):
653 """
654 Parses the fan definition YAML files to find the fans
655 that match both the zone passed in and one of the
656 cooling profiles.
657 """
658
659 fans = []
660
661 for f in fan_data['fans']:
662
663 if zone_num != f['cooling_zone']:
664 continue
665
Gunnar Mills67e95512017-06-02 14:35:18 -0500666 # 'cooling_profile' is optional (use 'all' instead)
Matt Spinler78498c92017-04-11 13:59:46 -0500667 if f.get('cooling_profile') is None:
668 profile = "all"
669 else:
670 profile = f['cooling_profile']
671
672 if profile not in profiles:
673 continue
674
675 fan = {}
676 fan['name'] = f['inventory']
677 fan['sensors'] = f['sensors']
Lei YU069e4402018-01-31 16:47:37 +0800678 fan['target_interface'] = f.get(
679 'target_interface',
680 'xyz.openbmc_project.Control.FanSpeed')
Matt Spinler78498c92017-04-11 13:59:46 -0500681 fans.append(fan)
682
683 return fans
684
685
Matthew Barth7883f582019-02-14 14:24:46 -0600686def getIfacesInZone(zone_ifaces):
687 """
688 Parse given interfaces for a zone for associating a zone with an interface
689 and set any properties listed to defined values upon fan control starting
690 on the zone.
691 """
692
693 ifaces = []
694 for i in zone_ifaces:
695 iface = {}
696 # Interface name not needed yet for fan zones but
697 # may be necessary as more interfaces are extended by the zones
698 iface['name'] = i['name']
699
700 if ('properties' in i) and \
701 (i['properties'] is not None):
702 props = []
703 for p in i['properties']:
704 prop = {}
Matthew Barth59096e52019-02-18 12:23:38 -0600705 prop['name'] = p['name']
706 prop['func'] = str(p['name']).lower()
Matthew Barth7883f582019-02-14 14:24:46 -0600707 prop['type'] = parse_cpp_type(p['type'])
Matthew Barth59096e52019-02-18 12:23:38 -0600708 if ('persist' in p):
709 persist = p['persist']
710 if (persist is not None):
711 if (isinstance(persist, bool)):
712 prop['persist'] = 'true' if persist else 'false'
713 else:
714 prop['persist'] = 'false'
Matthew Barth7883f582019-02-14 14:24:46 -0600715 vals = []
716 for v in p['values']:
717 val = v['value']
718 if (val is not None):
719 if (isinstance(val, bool)):
720 # Convert True/False to 'true'/'false'
721 val = 'true' if val else 'false'
722 elif (isinstance(val, str)):
723 # Wrap strings with double-quotes
724 val = "\"" + val + "\""
725 vals.append(val)
726 prop['values'] = vals
727 props.append(prop)
728 iface['props'] = props
729 ifaces.append(iface)
730
731 return ifaces
732
733
Gunnar Millsee8a2812017-06-02 14:26:47 -0500734def getConditionInZoneConditions(zone_condition, zone_conditions_data):
735 """
736 Parses the zone conditions definition YAML files to find the condition
737 that match both the zone condition passed in.
738 """
739
740 condition = {}
741
742 for c in zone_conditions_data['conditions']:
743
744 if zone_condition != c['name']:
745 continue
746 condition['type'] = c['type']
747 properties = []
748 for p in c['properties']:
749 property = {}
750 property['property'] = p['property']
751 property['interface'] = p['interface']
752 property['path'] = p['path']
753 property['type'] = p['type'].lower()
754 property['value'] = str(p['value']).lower()
755 properties.append(property)
756 condition['properties'] = properties
757
758 return condition
759
760
761def buildZoneData(zone_data, fan_data, events_data, zone_conditions_data):
Matt Spinler78498c92017-04-11 13:59:46 -0500762 """
763 Combines the zone definition YAML and fan
764 definition YAML to create a data structure defining
765 the fan cooling zones.
766 """
767
768 zone_groups = []
769
770 for group in zone_data:
771 conditions = []
Gunnar Millsee8a2812017-06-02 14:26:47 -0500772 # zone conditions are optional
773 if 'zone_conditions' in group and group['zone_conditions'] is not None:
774 for c in group['zone_conditions']:
775
776 if not zone_conditions_data:
Gunnar Millsb751f322017-06-06 15:14:11 -0500777 sys.exit("No zone_conditions YAML file but " +
Gunnar Millsee8a2812017-06-02 14:26:47 -0500778 "zone_conditions used in zone YAML")
779
780 condition = getConditionInZoneConditions(c['name'],
781 zone_conditions_data)
782
783 if not condition:
784 sys.exit("Missing zone condition " + c['name'])
785
786 conditions.append(condition)
Matt Spinler78498c92017-04-11 13:59:46 -0500787
788 zone_group = {}
789 zone_group['conditions'] = conditions
790
791 zones = []
792 for z in group['zones']:
793 zone = {}
794
Gunnar Mills67e95512017-06-02 14:35:18 -0500795 # 'zone' is required
796 if ('zone' not in z) or (z['zone'] is None):
Matt Spinler78498c92017-04-11 13:59:46 -0500797 sys.exit("Missing fan zone number in " + zone_yaml)
798
799 zone['num'] = z['zone']
800
801 zone['full_speed'] = z['full_speed']
802
Matthew Barth1de66622017-06-12 13:13:02 -0500803 zone['default_floor'] = z['default_floor']
804
Matthew Bartha9561842017-06-29 11:43:45 -0500805 # 'increase_delay' is optional (use 0 by default)
806 key = 'increase_delay'
807 zone[key] = z.setdefault(key, 0)
808
809 # 'decrease_interval' is optional (use 0 by default)
810 key = 'decrease_interval'
811 zone[key] = z.setdefault(key, 0)
812
Gunnar Mills67e95512017-06-02 14:35:18 -0500813 # 'cooling_profiles' is optional (use 'all' instead)
814 if ('cooling_profiles' not in z) or \
Matt Spinler78498c92017-04-11 13:59:46 -0500815 (z['cooling_profiles'] is None):
816 profiles = ["all"]
817 else:
818 profiles = z['cooling_profiles']
819
Matthew Barth7883f582019-02-14 14:24:46 -0600820 # 'interfaces' is optional (no default)
Matthew Barth64099cd2019-02-18 09:43:12 -0600821 ifaces = []
Matthew Barth7883f582019-02-14 14:24:46 -0600822 if ('interfaces' in z) and \
823 (z['interfaces'] is not None):
824 ifaces = getIfacesInZone(z['interfaces'])
825
Matt Spinler78498c92017-04-11 13:59:46 -0500826 fans = getFansInZone(z['zone'], profiles, fan_data)
Gunnar Millsb751f322017-06-06 15:14:11 -0500827 events = getEventsInZone(z['zone'], group['zone_conditions'],
828 events_data)
Matt Spinler78498c92017-04-11 13:59:46 -0500829
830 if len(fans) == 0:
831 sys.exit("Didn't find any fans in zone " + str(zone['num']))
832
Matthew Barth7883f582019-02-14 14:24:46 -0600833 if (ifaces):
834 zone['ifaces'] = ifaces
Matt Spinler78498c92017-04-11 13:59:46 -0500835 zone['fans'] = fans
Matthew Barthd4d0f082017-05-16 13:51:10 -0500836 zone['events'] = events
Matt Spinler78498c92017-04-11 13:59:46 -0500837 zones.append(zone)
838
839 zone_group['zones'] = zones
840 zone_groups.append(zone_group)
841
842 return zone_groups
843
844
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500845if __name__ == '__main__':
846 parser = ArgumentParser(
847 description="Phosphor fan zone definition parser")
848
849 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml',
850 default="example/zones.yaml",
851 help='fan zone definitional yaml')
852 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml',
853 default="example/fans.yaml",
854 help='fan definitional yaml')
Matthew Barthd4d0f082017-05-16 13:51:10 -0500855 parser.add_argument('-e', '--events_yaml', dest='events_yaml',
856 help='events to set speeds yaml')
Gunnar Millsee8a2812017-06-02 14:26:47 -0500857 parser.add_argument('-c', '--zone_conditions_yaml',
858 dest='zone_conditions_yaml',
859 help='conditions to determine zone yaml')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500860 parser.add_argument('-o', '--output_dir', dest='output_dir',
861 default=".",
862 help='output directory')
863 args = parser.parse_args()
864
865 if not args.zone_yaml or not args.fan_yaml:
866 parser.print_usage()
William A. Kennington III3e781062018-10-19 17:18:34 -0700867 sys.exit(1)
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500868
869 with open(args.zone_yaml, 'r') as zone_input:
870 zone_data = yaml.safe_load(zone_input) or {}
871
872 with open(args.fan_yaml, 'r') as fan_input:
873 fan_data = yaml.safe_load(fan_input) or {}
874
Matthew Barthd4d0f082017-05-16 13:51:10 -0500875 events_data = {}
876 if args.events_yaml:
877 with open(args.events_yaml, 'r') as events_input:
878 events_data = yaml.safe_load(events_input) or {}
879
Gunnar Millsee8a2812017-06-02 14:26:47 -0500880 zone_conditions_data = {}
881 if args.zone_conditions_yaml:
882 with open(args.zone_conditions_yaml, 'r') as zone_conditions_input:
883 zone_conditions_data = yaml.safe_load(zone_conditions_input) or {}
884
Matt Spinleree7f6422017-05-09 11:03:14 -0500885 zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
Gunnar Millsee8a2812017-06-02 14:26:47 -0500886 fan_data, events_data, zone_conditions_data)
Matt Spinleree7f6422017-05-09 11:03:14 -0500887
888 manager_config = zone_data.get('manager_configuration', {})
889
890 if manager_config.get('power_on_delay') is None:
891 manager_config['power_on_delay'] = 0
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500892
Matthew Barth702c4a52018-02-28 16:23:11 -0600893 tmpls_dir = os.path.join(
894 os.path.dirname(os.path.realpath(__file__)),
895 "templates")
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500896 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
Matthew Barth702c4a52018-02-28 16:23:11 -0600897 if sys.version_info < (3, 0):
898 lkup = TemplateLookup(
899 directories=tmpls_dir.split(),
900 disable_unicode=True)
901 else:
902 lkup = TemplateLookup(
903 directories=tmpls_dir.split())
904 tmpl = lkup.get_template('fan_zone_defs.mako.cpp')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500905 with open(output_file, 'w') as output:
Matthew Barth702c4a52018-02-28 16:23:11 -0600906 output.write(tmpl.render(zones=zone_config,
907 mgr_data=manager_config))