package org.point85.app.opc.da; import org.point85.app.AppUtils; import org.point85.app.FXMLLoaderFactory; import org.point85.app.ImageManager; import org.point85.app.Images; import org.point85.app.charts.DataSubscriber; import org.point85.app.charts.TrendChartController; import org.point85.app.designer.ConnectionState; import org.point85.domain.collector.CollectorDataSource; import org.point85.domain.opc.da.OpcDaDataChangeListener; import org.point85.domain.opc.da.OpcDaMonitoredGroup; import org.point85.domain.opc.da.OpcDaMonitoredItem; import org.point85.domain.opc.da.OpcDaServerStatus; import org.point85.domain.opc.da.OpcDaSource; import org.point85.domain.opc.da.OpcDaVariant; import org.point85.domain.opc.da.TagGroupInfo; import org.point85.domain.opc.da.TagItemInfo; import org.point85.domain.plant.Equipment; import org.point85.domain.script.EventResolver; import javafx.concurrent.Service; import javafx.concurrent.Task; import javafx.concurrent.WorkerStateEvent; import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.scene.control.Button; import javafx.scene.control.ContentDisplay; import javafx.scene.control.Label; import javafx.scene.control.ProgressIndicator; import javafx.scene.control.SplitPane; import javafx.stage.WindowEvent; public class OpcDaTrendController extends OpcDaController implements OpcDaDataChangeListener, DataSubscriber { // trend chart private TrendChartController trendChartController; // trend chart pane private SplitPane spTrendChart; // the tag being monitored private TagItemInfo monitoredTag; // the monitored group private OpcDaMonitoredGroup opcDaGroup; @FXML private Button btConnect; @FXML private Button btDisconnect; @FXML private Button btCancelConnect; // subscribed item @FXML private Label lbSourceId; // connection status @FXML private ProgressIndicator piConnection; @FXML private Label lbState; public SplitPane initializeTrend() throws Exception { if (trendChartController == null) { // Load the fxml file and create the anchor pane FXMLLoader loader = FXMLLoaderFactory.trendChartLoader(); spTrendChart = (SplitPane) loader.getRoot(); trendChartController = loader.getController(); trendChartController.initialize(getApp()); // data provider trendChartController.setProvider(this); setImages(); getDialogStage().setOnCloseRequest((WindowEvent event1) -> { onDisconnect(); }); } return spTrendChart; } // images for buttons @Override protected void setImages() throws Exception { super.setImages(); // connect btConnect.setGraphic(ImageManager.instance().getImageView(Images.CONNECT)); btConnect.setContentDisplay(ContentDisplay.RIGHT); // disconnect btDisconnect.setGraphic(ImageManager.instance().getImageView(Images.DISCONNECT)); btDisconnect.setContentDisplay(ContentDisplay.RIGHT); // cancel connect btCancelConnect.setGraphic(ImageManager.instance().getImageView(Images.CANCEL)); btCancelConnect.setContentDisplay(ContentDisplay.RIGHT); } public void setScriptResolver(EventResolver eventResolver) throws Exception { eventResolver.setWatchMode(true); trendChartController.setEventResolver(eventResolver); OpcDaSource dataSource = (OpcDaSource) eventResolver.getDataSource(); setSource(dataSource); String pathName = eventResolver.getSourceId(); TagItemInfo tagItem = new TagItemInfo(pathName); setMonitoredTag(tagItem); String trendedItem = dataSource.getId() + " [" + tagItem.toString() + "]"; lbSourceId.setText(trendedItem); updateConnectionStatus(ConnectionState.DISCONNECTED); } @FXML private void onConnect() { try { if (connectionState.equals(ConnectionState.CONNECTED)) { // disconnect first onDisconnect(); } // connect updateConnectionStatus(ConnectionState.CONNECTING); startConnectionService(); } catch (Exception e) { AppUtils.showErrorDialog(e); } } @FXML private void onDisconnect() { try { // disconnect terminateConnectionService(); unsubscribeFromDataSource(); updateConnectionStatus(ConnectionState.DISCONNECTED); } catch (Exception e) { AppUtils.showErrorDialog(e); } } @Override @FXML protected void onOK() { super.onOK(); try { unsubscribeFromDataSource(); } catch (Exception e) { AppUtils.showErrorDialog(e); } } @FXML protected void onCancelConnect() { try { cancelConnectionService(); updateConnectionStatus(ConnectionState.DISCONNECTED); } catch (Exception e) { AppUtils.showErrorDialog(e); } } private void updateConnectionStatus(ConnectionState state) throws Exception { connectionState = state; switch (state) { case CONNECTED: piConnection.setVisible(false); OpcDaServerStatus status = getApp().getOpcDaClient().getServerStatus(); if (status != null) { // state lbState.setText(status.getServerState()); lbState.setTextFill(ConnectionState.CONNECTED_COLOR); trendChartController.enableTrending(true); } else { lbState.setText(ConnectionState.DISCONNECTED.toString()); lbState.setTextFill(ConnectionState.DISCONNECTED_COLOR); trendChartController.enableTrending(false); } break; case CONNECTING: piConnection.setVisible(true); lbState.setText(ConnectionState.CONNECTING.toString()); lbState.setTextFill(ConnectionState.CONNECTING_COLOR); trendChartController.enableTrending(false); break; case DISCONNECTED: piConnection.setVisible(false); lbState.setText(ConnectionState.DISCONNECTED.toString()); lbState.setTextFill(ConnectionState.DISCONNECTED_COLOR); trendChartController.enableTrending(false); break; default: break; } } @Override public void onOpcDaDataChange(OpcDaMonitoredItem item) { try { ResolutionService service = new ResolutionService(item); service.setOnSucceeded(new EventHandler<WorkerStateEvent>() { @Override public void handle(WorkerStateEvent event) { String value = (String) event.getSource().getValue(); if (!value.equals(NO_ERROR)) { // connection failed AppUtils.showErrorDialog(value); } } }); service.start(); } catch (Exception e) { AppUtils.showErrorDialog(e); } } @Override protected void onConnectionSucceeded() throws Exception { updateConnectionStatus(ConnectionState.CONNECTED); // subscribe for data change events subscribeToDataSource(); } public void setMonitoredTag(TagItemInfo tagItem) throws Exception { this.monitoredTag = tagItem; } private String createGroupName() throws Exception { Equipment equipment = (Equipment) getApp().getPhysicalModelController().getSelectedEntity(); return equipment.getName() + '.' + trendChartController.getEventResolver().getSourceId(); } @Override public boolean isSubscribed() { return opcDaGroup != null ? true : false; } @Override public void subscribeToDataSource() throws Exception { if (opcDaGroup == null) { // new group using equipment name Integer updatePeriod = trendChartController.getEventResolver().getUpdatePeriod(); TagGroupInfo tagGroup = new TagGroupInfo(createGroupName()); tagGroup.setUpdatePeriod( updatePeriod != null ? updatePeriod.intValue() : CollectorDataSource.DEFAULT_UPDATE_PERIOD_MSEC); tagGroup.addTagItem(monitoredTag); // register for data change events opcDaGroup = getApp().getOpcDaClient().registerTags(tagGroup, this); } // start monitoring opcDaGroup.startMonitoring(); } @Override public void unsubscribeFromDataSource() throws Exception { if (opcDaGroup == null) { return; } opcDaGroup.stopMonitoring(); opcDaGroup = null; } // service class for callbacks on received data private class ResolutionService extends Service<String> { private final OpcDaMonitoredItem item; private ResolutionService(OpcDaMonitoredItem item) { this.item = item; } @Override protected Task<String> createTask() { return new Task<String>() { @Override protected String call() throws Exception { String errorMessage = NO_ERROR; try { // resolve the input value into a reason OpcDaVariant varientValue = item.getValue(); Object value = varientValue.getValueAsObject(); trendChartController.invokeResolver(getApp().getAppContext(), value, item.getLocalTimestamp(), null); } catch (Exception e) { errorMessage = e.getMessage(); } return errorMessage; } }; } } }