Skip to content

Commit 2722805

Browse files
HunterBarclayPepperLola
authored andcommitted
Fix UI issues with ReactFlow graph and checkboxes.
!: Auto Tester doesn't work and it appears the robot can't be controlled via robot code at the moment...
1 parent f7096a4 commit 2722805

5 files changed

Lines changed: 82 additions & 48 deletions

File tree

fission/src/systems/simulation/SimConfigShared.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import type { StimulusType } from "@/systems/simulation/stimulus/Stimulus"
1515
import type { SimFlow, SimReceiver, SimSupplier } from "@/systems/simulation/wpilib_brain/SimDataFlow"
1616
import { getSimMap, receiverTypeMap, supplierTypeMap } from "@/systems/simulation/wpilib_brain/WPILibState"
1717
import World from "@/systems/World"
18-
import { WiringNode } from "@/ui/panels/simulation/WiringNode"
18+
import WiringNode from "@/ui/panels/simulation/WiringNode"
1919
import { random } from "@/util/Random"
2020
import SimAccel from "./wpilib_brain/sim/SimAccel"
2121
import SimCANEncoder from "./wpilib_brain/sim/SimCANEncoder"

fission/src/ui/panels/configuring/assembly-config/interfaces/SimulationInterface.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type { PanelImplProps } from "@/ui/components/Panel"
77
import { Button } from "@/ui/components/StyledComponents"
88
import { CloseType, useUIContext } from "@/ui/helpers/UIProviderHelpers"
99
import AutoTestPanel from "@/ui/panels/simulation/AutoTestPanel"
10-
import { WiringPanel } from "@/ui/panels/simulation/WiringPanel"
10+
import WiringPanel from "@/ui/panels/simulation/WiringPanel"
1111
import type { ConfigurePanelCustomProps } from "../ConfigurePanel"
1212

1313
type SimulationInterfaceProps = {

fission/src/ui/panels/simulation/WiringNode.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ import {
1111
import { CustomTooltip, DeleteButton, EditButton, RefreshButton } from "@/ui/components/StyledComponents"
1212

1313
const WiringNode = ({ data, isConnectable }: NodeProps) => {
14-
const robotInput = data.input as HandleInfo[] | undefined
15-
const robotOutput = data.output as HandleInfo[] | undefined
16-
const onEdit = data.onEdit as (() => void) | undefined
17-
const onRefresh = data.onRefresh as (() => void) | undefined
18-
const onDelete = data.onDelete as (() => void) | undefined
19-
const simConfig = data.simConfig as SimConfigData
20-
const title = data.title as string
21-
const tooltip = data.tooltip as string | undefined
14+
const robotInput = useMemo(() => data.input as HandleInfo[] | undefined, [data])
15+
const robotOutput = useMemo(() => data.output as HandleInfo[] | undefined, [data])
16+
const onEdit = useMemo(() => data.onEdit as (() => void) | undefined, [data])
17+
const onRefresh = useMemo(() => data.onRefresh as (() => void) | undefined, [data])
18+
const onDelete = useMemo(() => data.onDelete as (() => void) | undefined, [data])
19+
const simConfig = useMemo(() => data.simConfig as SimConfigData, [data])
20+
const title = useMemo(() => data.title as string, [data])
21+
const tooltip = useMemo(() => data.tooltip as string | undefined, [data])
2222

2323
const validateConnection = useCallback(
2424
(edge: Edge | Connection) => {
@@ -151,4 +151,4 @@ const WiringNode = ({ data, isConnectable }: NodeProps) => {
151151
)
152152
}
153153

154-
export { WiringNode }
154+
export default WiringNode

fission/src/ui/panels/simulation/WiringPanel.tsx

Lines changed: 58 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ import { Button } from "@/ui/components/StyledComponents"
3737
import FlowControls from "@/ui/components/simulation/FlowControls"
3838
import FlowInfo from "@/ui/components/simulation/FlowInfo"
3939
import { useUIContext } from "../../helpers/UIProviderHelpers"
40-
import { WiringNode } from "./WiringNode"
40+
import WiringNode from "./WiringNode"
41+
42+
/**
43+
* WARNING: Please test *thoroughly* when making changes. React Flow is very tempermental with how nodes
44+
* and object references are maintained.
45+
*/
4146

4247
type ConfigComponentProps = {
4348
setConfigState: (state: ConfigState) => void
@@ -56,7 +61,7 @@ type NodeType = ComponentType<
5661
const nodeTypes: Record<string, NodeType> = [WiringNode].reduce<{
5762
[k: string]: NodeType
5863
}>((prev, next) => {
59-
prev[next.name] = next as NodeType
64+
prev[next.name] = next
6065
return prev
6166
}, {})
6267

@@ -68,7 +73,7 @@ function generateGraph(
6873
const nodes: Map<string, FlowNode> = new Map()
6974
const edges: FlowEdge[] = []
7075

71-
for (const [_k, v] of Object.entries(simConfig.nodes)) {
76+
Object.entries(simConfig.nodes).forEach(([_k, v]) => {
7277
let onEdit: (() => void) | undefined
7378
let onRefresh: (() => void) | undefined
7479
let onDelete: (() => void) | undefined
@@ -99,32 +104,34 @@ function generateGraph(
99104
}
100105

101106
nodes.set(v.id, {
102-
...v,
107+
id: v.id,
108+
type: v.type,
109+
position: v.position,
103110
data: {
104-
title,
105-
onEdit,
106-
onRefresh,
107-
onDelete,
108-
simConfig,
111+
title: title,
112+
onEdit: onEdit,
113+
onRefresh: onRefresh,
114+
onDelete: onDelete,
115+
simConfig: simConfig,
109116
input: [],
110117
output: [],
111118
tooltip: v.tooltip,
112119
},
113120
})
114-
}
121+
})
115122

116-
for (const [_k, v] of Object.entries(simConfig.handles)) {
117-
if (!v.enabled) break
123+
Object.entries(simConfig.handles).forEach(([_k, v]) => {
124+
if (!v.enabled) return
118125
const node = nodes.get(v.nodeId)
119126
if (!node) {
120127
console.warn("Orphaned handle found")
121-
break
128+
return
122129
}
123130
const list = (v.isSource ? node.data.output : node.data.input) as unknown[]
124-
list.push(v)
125-
}
131+
list.push({ ...v })
132+
})
126133

127-
for (const [k, v] of Object.entries(simConfig.edges)) {
134+
Object.entries(simConfig.edges).forEach(([k, v]) => {
128135
const sourceHandle = simConfig.handles[v.sourceId]
129136
const targetHandle = simConfig.handles[v.targetId]
130137

@@ -137,22 +144,28 @@ function generateGraph(
137144
targetHandle: targetHandle.id,
138145
})
139146
}
140-
}
147+
})
141148

142149
return [[...nodes.values()], edges]
143150
}
144151

145152
const SimIoComponent: React.FC<ConfigComponentProps> = ({ setConfigState, simConfig }) => {
146153
const theme = useTheme()
147154

148-
const simOut: HandleInfo[] = []
149-
const simIn: HandleInfo[] = []
150-
for (const [_k, v] of Object.entries(simConfig.handles)) {
151-
if (v.nodeId === NODE_ID_SIM_OUT || v.nodeId === NODE_ID_SIM_IN) {
152-
const list = v.isSource ? simOut : simIn
153-
list.push(v)
155+
const [simOut, setSimOut] = useState<Record<string, HandleInfo>>({})
156+
const [simIn, setSimIn] = useState<Record<string, HandleInfo>>({})
157+
158+
useEffect(() => {
159+
const simOut: Record<string, HandleInfo> = {}
160+
const simIn: Record<string, HandleInfo> = {}
161+
for (const [_k, v] of Object.entries(simConfig.handles)) {
162+
if (v.nodeId === NODE_ID_SIM_OUT || v.nodeId === NODE_ID_SIM_IN) {
163+
v.isSource ? (simOut[v.id] = v) : (simIn[v.id] = v)
164+
}
154165
}
155-
}
166+
setSimOut(simOut)
167+
setSimIn(simIn)
168+
}, [simConfig])
156169

157170
return (
158171
<Stack gap={4} direction={"column"} sx={{ width: "stretch" }}>
@@ -167,13 +180,14 @@ const SimIoComponent: React.FC<ConfigComponentProps> = ({ setConfigState, simCon
167180
<Stack>
168181
<Label size="md">Output</Label>
169182
<ScrollView>
170-
{simOut.sort(handleInfoDisplayCompare).map(handle => (
183+
{Object.values(simOut).sort(handleInfoDisplayCompare).map(handle => (
171184
<Checkbox
172185
label={`${handle.displayName}`}
173186
key={handle.id}
174187
checked={handle.enabled}
175188
onClick={checked => {
176189
handle.enabled = checked
190+
setSimOut({ ...simOut, [handle.id]: handle })
177191
}}
178192
/>
179193
))}
@@ -183,13 +197,14 @@ const SimIoComponent: React.FC<ConfigComponentProps> = ({ setConfigState, simCon
183197
<Stack>
184198
<Label size="md">Input</Label>
185199
<ScrollView>
186-
{simIn.sort(handleInfoDisplayCompare).map(handle => (
200+
{Object.values(simIn).sort(handleInfoDisplayCompare).map(handle => (
187201
<Checkbox
188202
label={`${handle.displayName}`}
189203
key={handle.id}
190204
checked={handle.enabled}
191205
onClick={checked => {
192206
handle.enabled = checked
207+
setSimIn({ ...simIn, [handle.id]: handle })
193208
}}
194209
/>
195210
))}
@@ -206,22 +221,27 @@ const SimIoComponent: React.FC<ConfigComponentProps> = ({ setConfigState, simCon
206221
const RobotIoComponent: React.FC<ConfigComponentProps> = ({ setConfigState, simConfig }) => {
207222
const theme = useTheme()
208223

224+
const [refreshHook, refreshCheckboxes] = useReducer(x => !x, false)
225+
209226
const [canEncoders, canMotors, pwmDevices, accelerometers] = useMemo(() => {
210227
const canEncoders: JSX.Element[] = []
211228
const canMotors: JSX.Element[] = []
212229
const pwmDevices: JSX.Element[] = []
213230
const accelerometers: JSX.Element[] = []
214231

215-
for (const [_k, v] of Object.entries(simConfig.handles)) {
232+
Object.entries(simConfig.handles).forEach(([_k, v]) => {
216233
if (v.nodeId !== NODE_ID_ROBOT_IO) return []
217234

235+
console.debug(v)
236+
218237
const checkbox = (
219238
<Checkbox
220239
label={v.displayName}
221240
key={v.id}
222241
checked={v.enabled}
223242
onClick={enabled => {
224243
v.enabled = enabled
244+
refreshCheckboxes()
225245
}}
226246
/>
227247
)
@@ -234,16 +254,16 @@ const RobotIoComponent: React.FC<ConfigComponentProps> = ({ setConfigState, simC
234254
pwmDevices.push(checkbox)
235255
break
236256
case SimType.CAN_ENCODER:
237-
pwmDevices.push(checkbox)
257+
canEncoders.push(checkbox)
238258
break
239259
case SimType.ACCELEROMETER:
240-
pwmDevices.push(checkbox)
260+
accelerometers.push(checkbox)
241261
break
242262
}
243-
}
263+
})
244264

245265
return [canEncoders, canMotors, pwmDevices, accelerometers]
246-
}, [simConfig])
266+
}, [simConfig, refreshHook])
247267

248268
return (
249269
<Stack gap={4}>
@@ -360,7 +380,7 @@ const WiringComponent: React.FC<ConfigComponentProps> = ({ setConfigState, simCo
360380
const onCreateJunction = useCallback(() => {
361381
SimConfig.AddJunctionNode(simConfig)
362382
refreshGraph()
363-
}, [simConfig])
383+
}, [refreshGraph, simConfig])
364384

365385
return (
366386
<ReactFlow
@@ -402,8 +422,10 @@ const WiringPanel: React.FC<PanelImplProps<void, void>> = ({ panel }) => {
402422

403423
const existingConfig = selectedAssembly.simConfigData
404424
if (existingConfig) {
425+
console.debug('Existing SimConfig found')
405426
setSimConfig(JSON.parse(JSON.stringify(existingConfig))) // Create copy to not force a save
406427
} else {
428+
console.debug('No SimConfig found, creating default...')
407429
setSimConfig(SimConfig.Default(selectedAssembly))
408430
}
409431
}, [selectedAssembly])
@@ -418,6 +440,8 @@ const WiringPanel: React.FC<PanelImplProps<void, void>> = ({ panel }) => {
418440
console.debug(`${flows.length} Flows Successfully Compiled!`)
419441

420442
selectedAssembly.updateSimConfig(simConfig)
443+
} else {
444+
console.warn('Failed to save SimConfig', simConfig, selectedAssembly)
421445
}
422446
}, [selectedAssembly, simConfig])
423447

@@ -429,7 +453,7 @@ const WiringPanel: React.FC<PanelImplProps<void, void>> = ({ panel }) => {
429453

430454
useEffect(() => {
431455
configureScreen(panel!, { title: "Wiring Panel" }, { onBeforeAccept: save })
432-
}, [])
456+
}, [save])
433457

434458
return (
435459
<>
@@ -473,4 +497,4 @@ const WiringPanel: React.FC<PanelImplProps<void, void>> = ({ panel }) => {
473497
)
474498
}
475499

476-
export { WiringPanel }
500+
export default WiringPanel

simulation/samples/JavaAutoSample/src/main/java/frc/robot/Robot.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,18 @@ public class Robot extends TimedRobot {
3939
private String m_autoSelected;
4040
private final SendableChooser<String> m_chooser = new SendableChooser<>();
4141

42-
private ADXL362 m_Accelerometer = new ADXL362(SPI.Port.kMXP, ADXL362.Range.k8G);
42+
private static final int kAutoTurn = 90;
43+
44+
private ADXL362 m_accelerometer = new ADXL362(SPI.Port.kMXP, ADXL362.Range.k8G);
4345
private AHRS m_Gyro = new AHRS();
4446

4547
private CANSparkMax m_sparkLeft = new CANSparkMax(1, MotorType.kBrushless);
4648
private CANSparkMax m_sparkRight = new CANSparkMax(2, MotorType.kBrushless);
4749
private CANSparkMax m_sparkArm = new CANSparkMax(3, MotorType.kBrushless);
4850
private RelativeEncoder m_encoder;
4951

52+
private double m_initAngle = 0;
53+
5054
/**
5155
* This function is run when the robot is first started up and should be used
5256
* for any
@@ -117,13 +121,19 @@ public void autonomousPeriodic() {
117121
m_sparkLeft.set(0.5);
118122
m_sparkRight.set(0.5);
119123
if (m_encoder.getPosition() > 36.0) {
124+
m_initAngle = m_accelerometer.getY();
120125
m_autoState = AutoState.Stage2;
121126
System.out.println("--- Transitioning to Stage 2 ---");
122127
}
123128
break;
124129
case Stage2:
125-
m_sparkLeft.set(0.5);
126-
m_sparkRight.set(-0.5);
130+
double current = (m_accelerometer.getY() - m_initAngle);
131+
double delta = kAutoTurn - current;
132+
double speed = delta * (1.0 / 15.0);
133+
speed = Math.max(Math.min(speed, 1.0), -1.0);
134+
135+
m_sparkLeft.set(speed);
136+
m_sparkRight.set(-speed);
127137
break;
128138
default:
129139
break;

0 commit comments

Comments
 (0)