-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added enumeration/numerical to git tracked
- Loading branch information
Showing
2 changed files
with
266 additions
and
0 deletions.
There are no files selected for viewing
151 changes: 151 additions & 0 deletions
151
software/tracksight/frontend/src/app/testing/components/Enumeration.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import React, { useState, useEffect, useRef } from "react"; | ||
|
||
interface EnumerationGraphProps { | ||
signalName: string; | ||
currentState: string; | ||
currentTime: number; | ||
enumStates: string[]; | ||
} | ||
|
||
interface StateHistoryItem { | ||
state: string; | ||
startTime: number; | ||
} | ||
|
||
interface StateBar { | ||
state: string; | ||
startTime: number; | ||
left: number; | ||
width: number; | ||
} | ||
|
||
const EnumerationGraph: React.FC<EnumerationGraphProps> = ({ | ||
signalName, | ||
currentState, | ||
currentTime, | ||
enumStates, | ||
}) => { | ||
const containerRef = useRef<HTMLDivElement>(null); | ||
const [stateHistory, setStateHistory] = useState<StateHistoryItem[]>([]); | ||
|
||
// Define the total time window displayed on the graph (e.g., last 60 seconds) | ||
const timeWindow = 11; // seconds | ||
|
||
useEffect(() => { | ||
// Update state history when currentState changes | ||
setStateHistory((prevHistory) => { | ||
const lastState = prevHistory[prevHistory.length - 1]; | ||
if (!lastState || lastState.state !== currentState) { | ||
// If state changed, add new state with current time | ||
return [ | ||
...prevHistory, | ||
{ state: currentState, startTime: currentTime }, | ||
]; | ||
} else { | ||
// State didn't change, return previous history | ||
return prevHistory; | ||
} | ||
}); | ||
}, [currentState, currentTime]); | ||
|
||
useEffect(() => { | ||
// Remove states that end before the current time window | ||
setStateHistory((prevHistory) => | ||
prevHistory.filter((state, index) => { | ||
const nextState = prevHistory[index + 1]; | ||
const endTime = nextState ? nextState.startTime : currentTime; | ||
return endTime > currentTime - timeWindow; | ||
}) | ||
); | ||
}, [currentTime]); | ||
|
||
// Calculate pixels per second | ||
const containerWidth = containerRef.current?.offsetWidth || window.innerWidth; | ||
const pixelsPerSecond = containerWidth / timeWindow; | ||
|
||
// Calculate positions and widths for each state | ||
const stateBars: StateBar[] = stateHistory.map((state, index) => { | ||
const nextState = stateHistory[index + 1]; | ||
|
||
// Adjust start and end times to the bounds of the time window | ||
const stateStartTime = state.startTime; | ||
const stateEndTime = nextState ? nextState.startTime : currentTime; | ||
|
||
const barStartTime = Math.max(stateStartTime, currentTime - timeWindow); | ||
const barEndTime = Math.min(stateEndTime, currentTime); | ||
|
||
const startOffset = | ||
(barStartTime - (currentTime - timeWindow)) * pixelsPerSecond; | ||
const duration = barEndTime - barStartTime; | ||
const width = duration * pixelsPerSecond; | ||
|
||
return { | ||
...state, | ||
left: startOffset, | ||
width, | ||
}; | ||
}); | ||
|
||
// Colors for the states, mapping to indices | ||
const stateColors: string[] = [ | ||
"#FF3B2F", | ||
"#FFCC02", | ||
"#FF9500", | ||
"#35C759", | ||
"#007AFF", | ||
]; | ||
|
||
// Helper function to get color based on state | ||
const getStateColor = (state: string): string => { | ||
const index = enumStates.indexOf(state); | ||
return stateColors[index % stateColors.length]; // Wrap around if more states | ||
}; | ||
|
||
return ( | ||
<div className="w-full relative"> | ||
{/* Signal Name and Legend */} | ||
<div className="mx-4 flex gap-5 items-center"> | ||
<div className="font-bold my-2 text-white bg-blue-500 rounded-full inline-block p-1 text-sm"> | ||
{signalName} | ||
</div> | ||
{/* Legend */} | ||
<div className="mt-2 text-xs flex gap-4"> | ||
{enumStates.map((state, index) => ( | ||
<div key={index} className="flex items-center mb-1"> | ||
<span | ||
className="w-3 h-3 inline-block mr-2" | ||
style={{ backgroundColor: getStateColor(state) }} | ||
></span> | ||
<span className="text-gray-500">{state}</span> | ||
</div> | ||
))} | ||
</div> | ||
</div> | ||
{/* Graph */} | ||
<div | ||
className="relative w-full h-6 bg-gray-100 overflow-hidden" | ||
ref={containerRef} | ||
> | ||
{stateBars.map( | ||
(bar, index) => | ||
bar.width > 0 && ( | ||
<div | ||
key={index} | ||
className="absolute top-0 bottom-0" | ||
style={{ | ||
left: `${bar.left}px`, | ||
width: `${bar.width}px`, | ||
backgroundColor: getStateColor(bar.state), | ||
}} | ||
title={`${bar.state} (${new Date( | ||
bar.startTime * 1000 | ||
).toLocaleTimeString()})`} | ||
></div> | ||
) | ||
)} | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default EnumerationGraph; |
115 changes: 115 additions & 0 deletions
115
software/tracksight/frontend/src/app/testing/components/Numerical.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import React, { useState, useEffect } from "react"; | ||
import { | ||
AreaChart, | ||
Area, | ||
XAxis, | ||
YAxis, | ||
Tooltip, | ||
ResponsiveContainer, | ||
} from "recharts"; | ||
|
||
interface DataPoint { | ||
time: number; | ||
[signalName: string]: number; // Dynamic keys for signal values | ||
} | ||
|
||
interface NumericalGraphProps { | ||
currentTime: number; | ||
numericalSignals: string[]; | ||
} | ||
|
||
const NumericalGraph: React.FC<NumericalGraphProps> = ({ | ||
currentTime, | ||
numericalSignals, | ||
}) => { | ||
const [data, setData] = useState<DataPoint[]>([]); | ||
const updateInterval = 200; // Update every 200 milliseconds | ||
|
||
useEffect(() => { | ||
const newDataPoint: DataPoint = { time: currentTime }; | ||
|
||
// Generate random values for each numerical signal | ||
numericalSignals.forEach((signalName) => { | ||
newDataPoint[signalName] = Math.floor(Math.random() * 100); | ||
}); | ||
|
||
setData((prevData) => { | ||
const timeWindow = 30000; // 30 seconds in milliseconds | ||
const cutoffTime = currentTime - timeWindow; | ||
const filteredData = prevData.filter((d) => d.time >= cutoffTime); | ||
return [...filteredData, newDataPoint]; | ||
}); | ||
}, [currentTime, numericalSignals]); | ||
|
||
const colors = ["#ff4d4f", "#ffa940", "#36cfc9", "#597ef7", "#73d13d"]; | ||
|
||
return ( | ||
<div className="w-full h-64"> | ||
<div className="ml-24 mt-4 text-xs flex gap-4"> | ||
{numericalSignals.map((signalName) => ( | ||
<div key={signalName} className="flex items-center mb-1"> | ||
<div | ||
className="w-3 h-3 inline-block mr-2" | ||
style={{ | ||
backgroundColor: colors[numericalSignals.indexOf(signalName)], | ||
}} | ||
></div> | ||
<span className="text-gray-500">{signalName}</span> | ||
</div> | ||
))} | ||
</div> | ||
<ResponsiveContainer width="100%" height="100%"> | ||
<AreaChart data={data}> | ||
<defs> | ||
{numericalSignals.map((signalName, index) => ( | ||
<linearGradient | ||
key={signalName} | ||
id={`color${signalName}`} | ||
x1="0" | ||
y1="0" | ||
x2="0" | ||
y2="1" | ||
> | ||
<stop | ||
offset="5%" | ||
stopColor={colors[index % colors.length]} | ||
stopOpacity={0.8} | ||
/> | ||
<stop | ||
offset="95%" | ||
stopColor={colors[index % colors.length]} | ||
stopOpacity={0} | ||
/> | ||
</linearGradient> | ||
))} | ||
</defs> | ||
<XAxis | ||
dataKey="time" | ||
type="number" | ||
domain={["dataMin", "dataMax"]} | ||
hide | ||
/> | ||
<YAxis /> | ||
<Tooltip | ||
labelFormatter={(value) => new Date(value).toLocaleTimeString()} | ||
formatter={(value, name) => [`${value}`, `${name}`]} | ||
/> | ||
{numericalSignals.map((signalName, index) => ( | ||
<Area | ||
key={signalName} | ||
type="monotone" | ||
dataKey={signalName} | ||
stroke={colors[index % colors.length]} | ||
fillOpacity={1} | ||
fill={`url(#color${signalName})`} | ||
isAnimationActive={true} | ||
animationDuration={updateInterval} | ||
/> | ||
))} | ||
</AreaChart> | ||
</ResponsiveContainer> | ||
</div> | ||
); | ||
}; | ||
|
||
export default NumericalGraph; |