@@ -162,9 +162,177 @@ jobs:
162162 ci_stats.html
163163 *results.csv
164164
165+ power-stats :
166+ needs : [gen-config]
167+ strategy :
168+ matrix :
169+ board : ${{ fromJson(needs.gen-config.outputs.ci-boards) }}
170+ runs-on : ubuntu-22.04
171+ permissions :
172+ actions : read
173+ steps :
174+ - name : Setup Node.js
175+ uses : actions/setup-node@v4
176+ with :
177+ node-version : ' 20'
178+
179+ - name : Install dependencies
180+ run : |
181+ npm install adm-zip csv-parse csv-stringify
182+ pip install plotly pandas
183+
184+ - name : Checkout
185+ uses : actions/checkout@v4
186+ with :
187+ path : tt-zephyr-platforms
188+
189+ - name : Download Past Power Recordings
190+ uses : actions/github-script@v8
191+ with :
192+ github-token : ${{ secrets.GITHUB_TOKEN }}
193+ script : |
194+ const AdmZip = require('adm-zip');
195+ const fs = require('fs');
196+ const { parse } = require('csv-parse/sync');
197+ const { stringify } = require('csv-stringify/sync');
198+
199+ // Aggregate power data by workflow run
200+ const powerResults = {};
201+ const artifacts = await github.rest.actions.listArtifactsForRepo({
202+ owner: context.repo.owner,
203+ repo: context.repo.repo,
204+ name: 'Power recording (${{ matrix.board }})',
205+ });
206+ console.log(`Total artifacts found: ${artifacts.data.artifacts.length}`);
207+
208+ for (const artifact of artifacts.data.artifacts) {
209+ const download = await github.rest.actions.downloadArtifact({
210+ owner: context.repo.owner,
211+ repo: context.repo.repo,
212+ artifact_id: artifact.id,
213+ archive_format: 'zip'
214+ });
215+
216+ const fname = `${artifact.name}-${artifact.id}-${artifact.workflow_run?.head_sha || 'unknown'}.zip`;
217+ fs.writeFileSync(fname, Buffer.from(download.data));
218+
219+ const zip = new AdmZip(fname);
220+ const zipEntries = zip.getEntries();
221+
222+ zipEntries.forEach(function (zipEntry) {
223+ if (zipEntry.name === "power_recording.csv") {
224+ console.log(`Processing ${zipEntry.entryName} from artifact ${artifact.name} (${artifact.id})`);
225+ const content = zipEntry.getData().toString('utf8');
226+
227+ // Parse CSV content
228+ const records = parse(content, {
229+ columns: true,
230+ trim: true
231+ });
232+
233+ // Filter out N/A values and calculate statistics
234+ const powerValues = records
235+ .map(record => {
236+ const power = record.power_watts;
237+ if (power && power !== 'N/A') {
238+ const numPower = parseFloat(power);
239+ if (!isNaN(numPower)) {
240+ return numPower;
241+ }
242+ }
243+ return null;
244+ })
245+ .filter(val => val !== null);
246+
247+ if (powerValues.length === 0) {
248+ console.log(`No valid power values found in ${zipEntry.entryName}`);
249+ return;
250+ }
251+
252+ const workflowRunId = artifact.workflow_run?.id;
253+ if (!workflowRunId) {
254+ console.log(`Skipping artifact ${artifact.id}, no workflow_run`);
255+ return;
256+ }
257+
258+ if (artifact.workflow_run.head_branch != 'main') {
259+ console.log(`Skipping artifact ${artifact.id}, branch ${artifact.workflow_run.head_branch}`);
260+ return;
261+ }
262+
263+ // Calculate aggregate statistics
264+ const avgPower = powerValues.reduce((a, b) => a + b, 0) / powerValues.length;
265+ const maxPower = Math.max(...powerValues);
266+ const minPower = Math.min(...powerValues);
267+
268+ if (!powerResults[workflowRunId]) {
269+ powerResults[workflowRunId] = {
270+ commit: artifact.workflow_run.head_sha,
271+ branch: artifact.workflow_run.head_branch,
272+ timestamp: artifact.created_at,
273+ workflow_run_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${workflowRunId}`,
274+ avg_power: avgPower,
275+ max_power: maxPower,
276+ min_power: minPower,
277+ sample_count: powerValues.length
278+ };
279+ } else {
280+ // If multiple recordings for same run, aggregate them
281+ const existing = powerResults[workflowRunId];
282+ const totalSamples = existing.sample_count + powerValues.length;
283+ existing.avg_power = (existing.avg_power * existing.sample_count + avgPower * powerValues.length) / totalSamples;
284+ existing.max_power = Math.max(existing.max_power, maxPower);
285+ existing.min_power = Math.min(existing.min_power, minPower);
286+ existing.sample_count = totalSamples;
287+ }
288+ }
289+ });
290+
291+ // Clean up zip file
292+ fs.unlinkSync(fname);
293+ }
294+
295+ // Create CSV file with aggregated power data
296+ const csvData = [
297+ ['Average Power (W)', 'Max Power (W)', 'Min Power (W)', 'Sample Count', 'Commit', 'Branch', 'Workflow Run URL', 'Timestamp']
298+ ];
299+
300+ // Flatten the results into an array, sorted by timestamp
301+ Object.values(powerResults)
302+ .sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp))
303+ .forEach(result => {
304+ csvData.push([
305+ result.avg_power.toFixed(2),
306+ result.max_power.toFixed(2),
307+ result.min_power.toFixed(2),
308+ result.sample_count,
309+ result.commit,
310+ result.branch,
311+ result.workflow_run_url,
312+ result.timestamp
313+ ]);
314+ });
315+
316+ // Write CSV file
317+ const csvContent = stringify(csvData);
318+ fs.writeFileSync('power results.csv', csvContent);
319+ console.log(`Created power results.csv with ${csvData.length - 1} workflow runs`);
320+
321+ - name : Generate Power Graph
322+ run : |
323+ python3 tt-zephyr-platforms/scripts/ci/render_power_graph.py . power_stats.html ${{ matrix.board }}
324+
325+ - name : Upload Power Graph
326+ uses : actions/upload-artifact@v4
327+ with :
328+ name : power-graph-summaries ${{ matrix.board }}
329+ path : |
330+ power_stats.html
331+ power results.csv
332+
165333 build :
166334 runs-on : ubuntu-22.04
167- needs : [ci-stats, gen-config]
335+ needs : [ci-stats, power-stats, gen-config]
168336 steps :
169337 - name : Checkout
170338 uses : actions/checkout@v4
@@ -224,6 +392,12 @@ jobs:
224392 path : ci-results
225393 pattern : test-result-summaries*
226394
395+ - name : Download Power Results
396+ uses : actions/download-artifact@v4
397+ with :
398+ path : power-results
399+ pattern : power-graph-summaries*
400+
227401 - name : Copy CI Results
228402 run : |
229403 CI_BOARDS="$(jq -r -c ".[]" <<< '${{ needs.gen-config.outputs.ci-boards }}')"
@@ -234,6 +408,9 @@ jobs:
234408 for board in ${CI_BOARD_REVS[@]}; do
235409 cp "ci-results/test-result-summaries ${board}/ci_stats.html" \
236410 tt-zephyr-platforms/doc/deploy/${board}_ci_stats.html
411+
412+ cp "power-results/power-graph-summaries ${board}/power_stats.html" \
413+ tt-zephyr-platforms/doc/deploy/${board}_power_stats.html
237414 done
238415
239416 - name : Setup pages
0 commit comments