363 self.setWindowTitle(
"Actuator Testing – Current Sense + Control")
364 self.resize(1100, 750)
373 i: deque(maxlen=MAX_POINTS)
for i
in range(1, NUM_ACTUATORS + 1)
376 i:
True for i
in range(1, NUM_ACTUATORS + 1)
386 self.
_send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
387 self.
_send_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
391 self.
_send_sock.bind((
"", SENSOR_RECEIVE_PORT))
394 self.
_send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
414 central = QtWidgets.QWidget()
415 self.setCentralWidget(central)
416 layout = QtWidgets.QHBoxLayout(central)
419 left = QtWidgets.QWidget()
420 left_layout = QtWidgets.QVBoxLayout(left)
422 net_row = QtWidgets.QHBoxLayout()
423 net_row.addWidget(QtWidgets.QLabel(
"Actuator IP:"))
426 "Target for commands, server heartbeat, and actuator config. "
427 "Auto-filled from BOARD_HEARTBEAT source; last heartbeat wins if multiple boards."
429 net_row.addWidget(self.
ip_edit)
430 net_row.addWidget(QtWidgets.QLabel(
"Cmd port:"))
435 left_layout.addLayout(net_row)
437 hb_grp = QtWidgets.QGroupBox(
"Board (heartbeats)")
438 hb_layout = QtWidgets.QVBoxLayout(hb_grp)
459 "Send actuator config (minimal)"
462 "Sends DAQv2-Comms ACTUATOR_CONFIG: not abort controller, serial printing on, no abort tables."
469 left_layout.addWidget(hb_grp)
472 chan_row = QtWidgets.QHBoxLayout()
473 chan_row.addWidget(QtWidgets.QLabel(
"Graph actuators:"))
475 for aid
in range(1, NUM_ACTUATORS + 1):
476 cb = QtWidgets.QCheckBox(f
"A{aid}")
478 r, g, b = SENSOR_COLORS[(aid - 1) % len(SENSOR_COLORS)]
480 f
"QCheckBox {{ color: rgb({r},{g},{b}); font-weight: bold; }}"
482 cb.stateChanged.connect(
486 chan_row.addWidget(cb)
487 chan_row.addStretch()
488 left_layout.addLayout(chan_row)
491 mode_row = QtWidgets.QHBoxLayout()
494 "When checked, convert sense voltage to current using V/20/0.05."
498 mode_row.addStretch()
499 left_layout.addLayout(mode_row)
501 grid = QtWidgets.QGridLayout()
505 for i
in range(NUM_ACTUATORS):
510 btn = QtWidgets.QPushButton(f
"Actuator {actuator_id}")
511 btn.setCheckable(
True)
516 vlabel = QtWidgets.QLabel(
"0.000 V")
517 vlabel.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
518 vlabel.setStyleSheet(
519 "font-size: 9pt; padding: 0 2px; color: white; background-color: transparent;"
525 grid.addWidget(btn, row, col)
526 grid.addWidget(vlabel, row, col + 1)
528 left_layout.addLayout(grid)
529 left_layout.addStretch()
532 self.
status_label.setStyleSheet(
"color: #8ab; font-size: 11px;")
536 right = QtWidgets.QWidget()
537 right_layout = QtWidgets.QVBoxLayout(right)
540 axis_style = {
"color":
"#FFFFFF",
"font-size":
"11pt"}
541 self.
plot_widget.setLabel(
"left",
"Current sense (V)", **axis_style)
542 self.
plot_widget.setLabel(
"bottom",
"Time (s)", **axis_style)
543 self.
plot_widget.showGrid(x=
True, y=
True, alpha=0.3)
547 for axis_name
in (
"left",
"bottom"):
548 ax = self.
plot_widget.getPlotItem().getAxis(axis_name)
550 if hasattr(ax,
"setStyle"):
551 ax.setStyle(tickTextColor=
"#FFFFFF", tickFont=QtGui.QFont(
None, 10))
555 self.
curves: Dict[int, pg.PlotDataItem] = {}
556 for actuator_id
in range(1, NUM_ACTUATORS + 1):
557 r, g, b = SENSOR_COLORS[(actuator_id - 1) % len(SENSOR_COLORS)]
558 pen = pg.mkPen(color=(r, g, b), width=2)
559 curve = self.
plot_widget.plot([], [], pen=pen, name=f
"A{actuator_id}")
560 self.
curves[actuator_id] = curve
564 layout.addWidget(left, stretch=0)
565 layout.addWidget(right, stretch=1)