194 lines
6.2 KiB
Java
194 lines
6.2 KiB
Java
package de.opcua.app.ui;
|
|
|
|
import de.opcua.app.model.NodeRow;
|
|
import de.opcua.app.opc.OpcUaService;
|
|
import javafx.scene.control.*;
|
|
import javafx.scene.control.cell.TextFieldTableCell;
|
|
import javafx.util.StringConverter;
|
|
|
|
/**
|
|
* Makes the Value column editable with OPC UA write-back
|
|
*/
|
|
public class EditableValueCellFactory {
|
|
|
|
private final OpcUaService opcService;
|
|
|
|
public EditableValueCellFactory(OpcUaService opcService) {
|
|
this.opcService = opcService;
|
|
}
|
|
|
|
/**
|
|
* Create editable value column
|
|
*/
|
|
public TableColumn<NodeRow, String> createEditableValueColumn() {
|
|
TableColumn<NodeRow, String> col = new TableColumn<>("Value");
|
|
col.setCellValueFactory(c -> c.getValue().valueProperty());
|
|
col.setCellFactory(tc -> new EditableValueCell());
|
|
col.setEditable(true);
|
|
col.setPrefWidth(150);
|
|
col.setOnEditCommit(event -> handleValueEdit(event));
|
|
|
|
return col;
|
|
}
|
|
|
|
private void handleValueEdit(TableColumn.CellEditEvent<NodeRow, String> event) {
|
|
NodeRow row = event.getRowValue();
|
|
String newValue = event.getNewValue();
|
|
String oldValue = event.getOldValue();
|
|
|
|
if (newValue == null || newValue.equals(oldValue)) {
|
|
return;
|
|
}
|
|
|
|
// Attempt to write to OPC UA
|
|
if (!opcService.isConnected()) {
|
|
showError("Not connected to OPC UA server");
|
|
row.setValue(oldValue); // Revert
|
|
return;
|
|
}
|
|
|
|
if (!row.isReadable()) {
|
|
showError("Node is not writable");
|
|
row.setValue(oldValue); // Revert
|
|
return;
|
|
}
|
|
|
|
// Write to OPC UA
|
|
opcService.writeValue(row.getNodeId(), newValue)
|
|
.thenAccept(success -> {
|
|
javafx.application.Platform.runLater(() -> {
|
|
if (success) {
|
|
row.setValue(newValue);
|
|
System.out.println("[OPC UA] Write successful: " + row.getNodeId() + " = " + newValue);
|
|
} else {
|
|
showError("Write failed for " + row.getNodeId());
|
|
row.setValue(oldValue); // Revert
|
|
}
|
|
});
|
|
})
|
|
.exceptionally(ex -> {
|
|
javafx.application.Platform.runLater(() -> {
|
|
showError("Write error: " + ex.getMessage());
|
|
row.setValue(oldValue); // Revert
|
|
});
|
|
return null;
|
|
});
|
|
}
|
|
|
|
private void showError(String message) {
|
|
Alert alert = new Alert(Alert.AlertType.ERROR);
|
|
alert.setTitle("Write Error");
|
|
alert.setHeaderText("Failed to write value");
|
|
alert.setContentText(message);
|
|
alert.show();
|
|
}
|
|
|
|
/**
|
|
* Custom editable cell with visual feedback
|
|
*/
|
|
private class EditableValueCell extends TableCell<NodeRow, String> {
|
|
private TextField textField;
|
|
|
|
public EditableValueCell() {
|
|
setConverter(new StringConverter<String>() {
|
|
@Override
|
|
public String toString(String object) {
|
|
return object == null ? "" : object;
|
|
}
|
|
|
|
@Override
|
|
public String fromString(String string) {
|
|
return string;
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void startEdit() {
|
|
NodeRow row = getTableRow().getItem();
|
|
if (row == null || !row.isReadable()) {
|
|
// Not editable - show tooltip
|
|
Tooltip tooltip = new Tooltip("This node is read-only");
|
|
setTooltip(tooltip);
|
|
return;
|
|
}
|
|
|
|
if (!opcService.isConnected()) {
|
|
Tooltip tooltip = new Tooltip("Not connected to OPC UA");
|
|
setTooltip(tooltip);
|
|
return;
|
|
}
|
|
|
|
super.startEdit();
|
|
|
|
if (textField == null) {
|
|
createTextField();
|
|
}
|
|
|
|
textField.setText(getItem());
|
|
setText(null);
|
|
setGraphic(textField);
|
|
textField.selectAll();
|
|
textField.requestFocus();
|
|
}
|
|
|
|
@Override
|
|
public void cancelEdit() {
|
|
super.cancelEdit();
|
|
setText(getItem());
|
|
setGraphic(null);
|
|
}
|
|
|
|
@Override
|
|
protected void updateItem(String item, boolean empty) {
|
|
super.updateItem(item, empty);
|
|
|
|
if (empty) {
|
|
setText(null);
|
|
setGraphic(null);
|
|
setStyle("");
|
|
} else {
|
|
if (isEditing()) {
|
|
if (textField != null) {
|
|
textField.setText(getItem());
|
|
}
|
|
setText(null);
|
|
setGraphic(textField);
|
|
} else {
|
|
setText(item);
|
|
setGraphic(null);
|
|
|
|
// Visual indication of editability
|
|
NodeRow row = getTableRow().getItem();
|
|
if (row != null && row.isReadable() && opcService.isConnected()) {
|
|
setStyle("-fx-background-color: #f0fff0;"); // Light green
|
|
setTooltip(new Tooltip("Double-click to edit"));
|
|
} else {
|
|
setStyle("");
|
|
setTooltip(null);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void createTextField() {
|
|
textField = new TextField(getItem());
|
|
textField.setMinWidth(getWidth() - getGraphicTextGap() * 2);
|
|
|
|
textField.setOnAction(event -> {
|
|
commitEdit(textField.getText());
|
|
});
|
|
|
|
textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
|
|
if (!isNowFocused) {
|
|
commitEdit(textField.getText());
|
|
}
|
|
});
|
|
}
|
|
|
|
private void setConverter(StringConverter<String> converter) {
|
|
// Helper method for future enhancements
|
|
}
|
|
}
|
|
}
|