Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Power and Thermal Specification for RPE - Front end #275

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
328 changes: 258 additions & 70 deletions src/components/Tables/PowerSummaryTable.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React from 'react';
import React, { useState, useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import { IoMdCloseCircleOutline } from 'react-icons/io';
import { Tooltip } from 'antd';
import { PowerCell } from './TableCells';
import { fixed, color } from '../../utils/common';
import { State } from '../ComponentsLib';

import { api, PATCH, GET } from '../../utils/serverAPI';
import '../style/PowerSummaryTable.css';

function PowerSummaryTableToolTip({ title, statusColor }) {
Expand All @@ -17,91 +17,271 @@
</Tooltip>
);
}

PowerSummaryTableToolTip.propTypes = {
title: PropTypes.string.isRequired,
statusColor: PropTypes.string.isRequired,
};

function PowerSummaryTable({
title, data, total, percent,
title, data = [], total = 0, percent = 0, deviceId = 'MPW1',
}) {
function getErrors(messages) {
if (messages === undefined) return [];
const errors = messages.filter((item) => item.filter((inner) => inner.type === 'error').length > 0);
return errors;
}
function getWarning(messages) {
if (messages === undefined) return [];
const warnings = messages.filter((item) => item.filter((inner) => inner.type === 'warn').length > 0);
return warnings;
}
function buildMessage(messages) {
return messages.reduce((sum, item, currentIndex) => {
item.forEach((i, index) => sum.push(
// eslint-disable-next-line react/no-array-index-key
<span key={`${currentIndex}+${index}`}>
{i.text}
<br />
</span>,
));
return sum;
}, []);
}
function message(messages) {
const errors = getErrors(messages);
if (errors.length > 0) {
return buildMessage(errors);
const [thermalData, setThermalData] = useState({
ambientTypical: 25,
ambientWorstCase: 50,
thetaJa: 10,
});

const [powerData, setPowerData] = useState({
powerBudget: 1.0,
fpgaScaling: 25,
pcScaling: 25,
});

const ambientTypicalRef = useRef(null);
const ambientWorstCaseRef = useRef(null);
const thetaJaRef = useRef(null);
const powerBudgetRef = useRef(null);
const fpgaScalingRef = useRef(null);
const pcScalingRef = useRef(null);

useEffect(() => {
const fetchData = async () => {
try {
const result = await GET(api.consumption('consumption', deviceId));
if (result && result.specification) {
const { specification } = result;
setThermalData({

Check warning on line 54 in src/components/Tables/PowerSummaryTable.js

View check run for this annotation

Codecov / codecov/patch

src/components/Tables/PowerSummaryTable.js#L53-L54

Added lines #L53 - L54 were not covered by tests
ambientTypical: specification.thermal.ambient.typical,
ambientWorstCase: specification.thermal.ambient.worstcase,
thetaJa: specification.thermal.theta_ja,
});
setPowerData({

Check warning on line 59 in src/components/Tables/PowerSummaryTable.js

View check run for this annotation

Codecov / codecov/patch

src/components/Tables/PowerSummaryTable.js#L59

Added line #L59 was not covered by tests
powerBudget: specification.power.budget,
fpgaScaling: specification.power.typical_dynamic_scaling.fpga_complex * 100,
pcScaling: specification.power.typical_dynamic_scaling.processing_complex * 100,
});
}
} catch (error) {
// Log error for debugging purposes
console.error('Error fetching data:', error.message);

Check warning on line 67 in src/components/Tables/PowerSummaryTable.js

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest)

Unexpected console statement
}
};

fetchData();
}, [deviceId]);

const updateBackend = async (updatedData) => {
try {
await PATCH(api.deviceInfo(deviceId), updatedData);

Check warning on line 76 in src/components/Tables/PowerSummaryTable.js

View check run for this annotation

Codecov / codecov/patch

src/components/Tables/PowerSummaryTable.js#L76

Added line #L76 was not covered by tests
} catch (error) {
// Log error for debugging purposes
console.error('Error updating backend:', error.message);

Check warning on line 79 in src/components/Tables/PowerSummaryTable.js

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest)

Unexpected console statement

Check warning on line 79 in src/components/Tables/PowerSummaryTable.js

View check run for this annotation

Codecov / codecov/patch

src/components/Tables/PowerSummaryTable.js#L79

Added line #L79 was not covered by tests
}
const warnings = getWarning(messages);
if (warnings.length > 0) {
return buildMessage(warnings);
};

const handleFieldUpdate = (field, value) => {
const updatedThermalData = { ...thermalData };
const updatedPowerData = { ...powerData };

Check warning on line 85 in src/components/Tables/PowerSummaryTable.js

View check run for this annotation

Codecov / codecov/patch

src/components/Tables/PowerSummaryTable.js#L84-L85

Added lines #L84 - L85 were not covered by tests

if (field in thermalData) {
updatedThermalData[field] = value;
} else {

Check warning on line 89 in src/components/Tables/PowerSummaryTable.js

View check run for this annotation

Codecov / codecov/patch

src/components/Tables/PowerSummaryTable.js#L89

Added line #L89 was not covered by tests
updatedPowerData[field] = value;
}
return '';
}

function isError(messages) { return getErrors(messages).length > 0; }
function isWarning(messages) { return getWarning(messages).length > 0; }
function statusColor(messages) {
return color(isError(messages), isWarning(messages));
}

setThermalData(updatedThermalData);
setPowerData(updatedPowerData);

const updatedData = {
specification: {
thermal: updatedThermalData,
power: updatedPowerData,
},
};
updateBackend(updatedData);
};

const enforceNumericInput = (e) => {
const { value } = e.target;
if (!/^-?\d*\.?\d*%?$/.test(value)) {
e.target.value = value.slice(0, -1);
}
};

const handleKeyDown = (e, nextFieldRef) => {
if (e.key === 'Enter' && nextFieldRef?.current) {
nextFieldRef.current.focus();
}
};

const getErrors = (messages) => (
messages?.filter((item) => item.some((inner) => inner.type === 'error')) || []
);
const getWarnings = (messages) => (
messages?.filter((item) => item.some((inner) => inner.type === 'warn')) || []
);

const buildMessage = (messages) => messages.reduce((acc, item, currentIndex) => {
item.forEach((i, index) => acc.push(
// <span key={`${currentIndex}-${index}`}>
<span key={i.id || `${currentIndex}-${index}`}>
{i.text}
<br />
</span>,
));
return acc;
}, []);

const message = (messages) => {
const errors = getErrors(messages);
return errors.length > 0 ? buildMessage(errors) : buildMessage(getWarnings(messages));
};

const statusColor = (messages) => color(
getErrors(messages).length > 0,
getWarnings(messages).length > 0,
);

return (
<div className="pst-container main-border">
<div className="no-wrap bold-text-title">{title}</div>
{title === 'FPGA Complex and Core Power' && (
<div className="thermal-power-specification">
<div className="spec-header">Thermal Specification</div>
<table className="spec-table">
<thead>
<tr>
<th />
<th className="typical-header">Typical</th>
<th className="worst-header">Worst-Case</th>
</tr>
</thead>
<tbody>
<tr className="ambient-row">
<td>Ambient</td>
<td className="value-cell">
<input
type="text"
value={thermalData.ambientTypical}
onChange={(e) => handleFieldUpdate('ambientTypical', e.target.value)}

Check warning on line 166 in src/components/Tables/PowerSummaryTable.js

View check run for this annotation

Codecov / codecov/patch

src/components/Tables/PowerSummaryTable.js#L166

Added line #L166 was not covered by tests
onInput={enforceNumericInput}
ref={ambientTypicalRef}
onKeyDown={(e) => handleKeyDown(e, ambientWorstCaseRef)}

Check warning on line 169 in src/components/Tables/PowerSummaryTable.js

View check run for this annotation

Codecov / codecov/patch

src/components/Tables/PowerSummaryTable.js#L169

Added line #L169 was not covered by tests
/>
°C
</td>
<td className="value-cell">
<input
type="text"
value={thermalData.ambientWorstCase}
onChange={(e) => handleFieldUpdate('ambientWorstCase', e.target.value)}

Check warning on line 177 in src/components/Tables/PowerSummaryTable.js

View check run for this annotation

Codecov / codecov/patch

src/components/Tables/PowerSummaryTable.js#L177

Added line #L177 was not covered by tests
onInput={enforceNumericInput}
ref={ambientWorstCaseRef}
onKeyDown={(e) => handleKeyDown(e, thetaJaRef)}

Check warning on line 180 in src/components/Tables/PowerSummaryTable.js

View check run for this annotation

Codecov / codecov/patch

src/components/Tables/PowerSummaryTable.js#L180

Added line #L180 was not covered by tests
/>
°C
</td>
</tr>
<tr className="theta-row">
<td colSpan="3" className="value-cell">
ΘJA:
<input
type="text"
value={thermalData.thetaJa}
onChange={(e) => handleFieldUpdate('thetaJa', e.target.value)}

Check warning on line 191 in src/components/Tables/PowerSummaryTable.js

View check run for this annotation

Codecov / codecov/patch

src/components/Tables/PowerSummaryTable.js#L191

Added line #L191 was not covered by tests
onInput={enforceNumericInput}
ref={thetaJaRef}
onKeyDown={(e) => handleKeyDown(e, powerBudgetRef)}

Check warning on line 194 in src/components/Tables/PowerSummaryTable.js

View check run for this annotation

Codecov / codecov/patch

src/components/Tables/PowerSummaryTable.js#L194

Added line #L194 was not covered by tests
/>
°C/W
</td>
</tr>
</tbody>
</table>
<div className="spec-header">Power Specification</div>
<table className="power-spec-table">
<tbody>
<tr>
<td>Power Budget</td>
<td className="scaling-cell">
<input
type="text"
value={powerData.powerBudget}
onChange={(e) => handleFieldUpdate('powerBudget', e.target.value)}

Check warning on line 210 in src/components/Tables/PowerSummaryTable.js

View check run for this annotation

Codecov / codecov/patch

src/components/Tables/PowerSummaryTable.js#L210

Added line #L210 was not covered by tests
onInput={enforceNumericInput}
ref={powerBudgetRef}
onKeyDown={(e) => handleKeyDown(e, fpgaScalingRef)}

Check warning on line 213 in src/components/Tables/PowerSummaryTable.js

View check run for this annotation

Codecov / codecov/patch

src/components/Tables/PowerSummaryTable.js#L213

Added line #L213 was not covered by tests
/>
W
</td>
</tr>
<tr>
<td>Typical Dynamic Scaling %</td>
<td className="scaling-cell">
FPGA:
<input
type="text"
value={powerData.fpgaScaling}
onChange={(e) => handleFieldUpdate('fpgaScaling', e.target.value)}

Check warning on line 225 in src/components/Tables/PowerSummaryTable.js

View check run for this annotation

Codecov / codecov/patch

src/components/Tables/PowerSummaryTable.js#L225

Added line #L225 was not covered by tests
onInput={enforceNumericInput}
ref={fpgaScalingRef}
onKeyDown={(e) => handleKeyDown(e, pcScalingRef)}

Check warning on line 228 in src/components/Tables/PowerSummaryTable.js

View check run for this annotation

Codecov / codecov/patch

src/components/Tables/PowerSummaryTable.js#L228

Added line #L228 was not covered by tests
/>
%
</td>
<td className="scaling-cell">
PC:
<input
type="text"
value={powerData.pcScaling}
onChange={(e) => handleFieldUpdate('pcScaling', e.target.value)}

Check warning on line 237 in src/components/Tables/PowerSummaryTable.js

View check run for this annotation

Codecov / codecov/patch

src/components/Tables/PowerSummaryTable.js#L237

Added line #L237 was not covered by tests
onInput={enforceNumericInput}
ref={pcScalingRef}
/>
%
</td>
</tr>
</tbody>
</table>
</div>
)}
<div className="no-wrap bold-text-title">{title || 'FPGA Complex and Core Power'}</div>
<div>
<table className="pst-table">
<tbody>
{
data.map((item, index) => (
// eslint-disable-next-line react/no-array-index-key
<tr key={index}>
<td className="dot-td"><State messages={item.messages} baseClass="dot" /></td>
<td className="no-wrap">{item.text}</td>
<PowerCell val={item.power} />
<td className="no-wrap" style={{ textAlign: 'right' }}>
{`${fixed(item.percent, 0)} %`}
</td>
<td className="fixed-col">
{
(isError(item.messages) || isWarning(item.messages)) && (
<PowerSummaryTableToolTip
title={message(item.messages)}
statusColor={statusColor(item.messages)}
/>
)
}
</td>
</tr>
))
}
{data.map((item) => (
<tr key={item.uniqueId}>
<td className="dot-td"><State messages={item.messages} baseClass="dot" /></td>
<td className="no-wrap">{item.text || 'N/A'}</td>
<PowerCell val={item.power || 0} />
<td className="no-wrap" style={{ textAlign: 'right' }}>
{`${fixed(item.percent || 0, 0)} %`}
</td>
<td className="fixed-col">
{(getErrors(item.messages).length > 0
|| getWarnings(item.messages).length > 0) && (
<PowerSummaryTableToolTip
title={message(item.messages)}
statusColor={statusColor(item.messages)}
/>
)}
</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="spacer" />
<div className="pst-bottom">
<div className="pst-bottom-progress">
<label htmlFor="progress-bar">
{`${fixed(percent, 0)} %`}
{`${fixed(percent || 0, 0)} %`}
</label>
<progress id="progress-bar" value={percent} max={100} />
<progress id="progress-bar" value={percent || 0} max={100} />
</div>
<div className="pst-bottom-total bold-text-title">
Total
<span className="bold-text-title">{` ${fixed(total)} W`}</span>
<span className="bold-text-title">{` ${fixed(total || 0)} W`}</span>
</div>
</div>
</div>
Expand All @@ -110,11 +290,19 @@

PowerSummaryTable.propTypes = {
title: PropTypes.string.isRequired,
data: PropTypes.oneOfType([
PropTypes.array,
]).isRequired,
data: PropTypes.arrayOf(PropTypes.shape({
uniqueId: PropTypes.string.isRequired,
text: PropTypes.string,
power: PropTypes.number,
percent: PropTypes.number,
messages: PropTypes.arrayOf(PropTypes.shape({
type: PropTypes.string,
text: PropTypes.string,
})),
})).isRequired,
total: PropTypes.number.isRequired,
percent: PropTypes.number.isRequired,
deviceId: PropTypes.string.isRequired,
};

export default PowerSummaryTable;
Loading
Loading