blob: 7db3d959cfa8130a177226ac311c5a28d772d811 [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 = {}
705 prop['name'] = str(p['name']).lower()
706 prop['type'] = parse_cpp_type(p['type'])
707 vals = []
708 for v in p['values']:
709 val = v['value']
710 if (val is not None):
711 if (isinstance(val, bool)):
712 # Convert True/False to 'true'/'false'
713 val = 'true' if val else 'false'
714 elif (isinstance(val, str)):
715 # Wrap strings with double-quotes
716 val = "\"" + val + "\""
717 vals.append(val)
718 prop['values'] = vals
719 props.append(prop)
720 iface['props'] = props
721 ifaces.append(iface)
722
723 return ifaces
724
725
Gunnar Millsee8a2812017-06-02 14:26:47 -0500726def getConditionInZoneConditions(zone_condition, zone_conditions_data):
727 """
728 Parses the zone conditions definition YAML files to find the condition
729 that match both the zone condition passed in.
730 """
731
732 condition = {}
733
734 for c in zone_conditions_data['conditions']:
735
736 if zone_condition != c['name']:
737 continue
738 condition['type'] = c['type']
739 properties = []
740 for p in c['properties']:
741 property = {}
742 property['property'] = p['property']
743 property['interface'] = p['interface']
744 property['path'] = p['path']
745 property['type'] = p['type'].lower()
746 property['value'] = str(p['value']).lower()
747 properties.append(property)
748 condition['properties'] = properties
749
750 return condition
751
752
753def buildZoneData(zone_data, fan_data, events_data, zone_conditions_data):
Matt Spinler78498c92017-04-11 13:59:46 -0500754 """
755 Combines the zone definition YAML and fan
756 definition YAML to create a data structure defining
757 the fan cooling zones.
758 """
759
760 zone_groups = []
761
762 for group in zone_data:
763 conditions = []
Gunnar Millsee8a2812017-06-02 14:26:47 -0500764 # zone conditions are optional
765 if 'zone_conditions' in group and group['zone_conditions'] is not None:
766 for c in group['zone_conditions']:
767
768 if not zone_conditions_data:
Gunnar Millsb751f322017-06-06 15:14:11 -0500769 sys.exit("No zone_conditions YAML file but " +
Gunnar Millsee8a2812017-06-02 14:26:47 -0500770 "zone_conditions used in zone YAML")
771
772 condition = getConditionInZoneConditions(c['name'],
773 zone_conditions_data)
774
775 if not condition:
776 sys.exit("Missing zone condition " + c['name'])
777
778 conditions.append(condition)
Matt Spinler78498c92017-04-11 13:59:46 -0500779
780 zone_group = {}
781 zone_group['conditions'] = conditions
782
783 zones = []
784 for z in group['zones']:
785 zone = {}
786
Gunnar Mills67e95512017-06-02 14:35:18 -0500787 # 'zone' is required
788 if ('zone' not in z) or (z['zone'] is None):
Matt Spinler78498c92017-04-11 13:59:46 -0500789 sys.exit("Missing fan zone number in " + zone_yaml)
790
791 zone['num'] = z['zone']
792
793 zone['full_speed'] = z['full_speed']
794
Matthew Barth1de66622017-06-12 13:13:02 -0500795 zone['default_floor'] = z['default_floor']
796
Matthew Bartha9561842017-06-29 11:43:45 -0500797 # 'increase_delay' is optional (use 0 by default)
798 key = 'increase_delay'
799 zone[key] = z.setdefault(key, 0)
800
801 # 'decrease_interval' is optional (use 0 by default)
802 key = 'decrease_interval'
803 zone[key] = z.setdefault(key, 0)
804
Gunnar Mills67e95512017-06-02 14:35:18 -0500805 # 'cooling_profiles' is optional (use 'all' instead)
806 if ('cooling_profiles' not in z) or \
Matt Spinler78498c92017-04-11 13:59:46 -0500807 (z['cooling_profiles'] is None):
808 profiles = ["all"]
809 else:
810 profiles = z['cooling_profiles']
811
Matthew Barth7883f582019-02-14 14:24:46 -0600812 # 'interfaces' is optional (no default)
813 if ('interfaces' in z) and \
814 (z['interfaces'] is not None):
815 ifaces = getIfacesInZone(z['interfaces'])
816
Matt Spinler78498c92017-04-11 13:59:46 -0500817 fans = getFansInZone(z['zone'], profiles, fan_data)
Gunnar Millsb751f322017-06-06 15:14:11 -0500818 events = getEventsInZone(z['zone'], group['zone_conditions'],
819 events_data)
Matt Spinler78498c92017-04-11 13:59:46 -0500820
821 if len(fans) == 0:
822 sys.exit("Didn't find any fans in zone " + str(zone['num']))
823
Matthew Barth7883f582019-02-14 14:24:46 -0600824 if (ifaces):
825 zone['ifaces'] = ifaces
Matt Spinler78498c92017-04-11 13:59:46 -0500826 zone['fans'] = fans
Matthew Barthd4d0f082017-05-16 13:51:10 -0500827 zone['events'] = events
Matt Spinler78498c92017-04-11 13:59:46 -0500828 zones.append(zone)
829
830 zone_group['zones'] = zones
831 zone_groups.append(zone_group)
832
833 return zone_groups
834
835
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500836if __name__ == '__main__':
837 parser = ArgumentParser(
838 description="Phosphor fan zone definition parser")
839
840 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml',
841 default="example/zones.yaml",
842 help='fan zone definitional yaml')
843 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml',
844 default="example/fans.yaml",
845 help='fan definitional yaml')
Matthew Barthd4d0f082017-05-16 13:51:10 -0500846 parser.add_argument('-e', '--events_yaml', dest='events_yaml',
847 help='events to set speeds yaml')
Gunnar Millsee8a2812017-06-02 14:26:47 -0500848 parser.add_argument('-c', '--zone_conditions_yaml',
849 dest='zone_conditions_yaml',
850 help='conditions to determine zone yaml')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500851 parser.add_argument('-o', '--output_dir', dest='output_dir',
852 default=".",
853 help='output directory')
854 args = parser.parse_args()
855
856 if not args.zone_yaml or not args.fan_yaml:
857 parser.print_usage()
William A. Kennington III3e781062018-10-19 17:18:34 -0700858 sys.exit(1)
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500859
860 with open(args.zone_yaml, 'r') as zone_input:
861 zone_data = yaml.safe_load(zone_input) or {}
862
863 with open(args.fan_yaml, 'r') as fan_input:
864 fan_data = yaml.safe_load(fan_input) or {}
865
Matthew Barthd4d0f082017-05-16 13:51:10 -0500866 events_data = {}
867 if args.events_yaml:
868 with open(args.events_yaml, 'r') as events_input:
869 events_data = yaml.safe_load(events_input) or {}
870
Gunnar Millsee8a2812017-06-02 14:26:47 -0500871 zone_conditions_data = {}
872 if args.zone_conditions_yaml:
873 with open(args.zone_conditions_yaml, 'r') as zone_conditions_input:
874 zone_conditions_data = yaml.safe_load(zone_conditions_input) or {}
875
Matt Spinleree7f6422017-05-09 11:03:14 -0500876 zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
Gunnar Millsee8a2812017-06-02 14:26:47 -0500877 fan_data, events_data, zone_conditions_data)
Matt Spinleree7f6422017-05-09 11:03:14 -0500878
879 manager_config = zone_data.get('manager_configuration', {})
880
881 if manager_config.get('power_on_delay') is None:
882 manager_config['power_on_delay'] = 0
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500883
Matthew Barth702c4a52018-02-28 16:23:11 -0600884 tmpls_dir = os.path.join(
885 os.path.dirname(os.path.realpath(__file__)),
886 "templates")
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500887 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
Matthew Barth702c4a52018-02-28 16:23:11 -0600888 if sys.version_info < (3, 0):
889 lkup = TemplateLookup(
890 directories=tmpls_dir.split(),
891 disable_unicode=True)
892 else:
893 lkup = TemplateLookup(
894 directories=tmpls_dir.split())
895 tmpl = lkup.get_template('fan_zone_defs.mako.cpp')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500896 with open(output_file, 'w') as output:
Matthew Barth702c4a52018-02-28 16:23:11 -0600897 output.write(tmpl.render(zones=zone_config,
898 mgr_data=manager_config))