All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.basyx.models.controlcomponent.SimpleProxyControlComponent Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (C) 2021 the Eclipse BaSyx Authors
 * 
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * 
 * SPDX-License-Identifier: MIT
 ******************************************************************************/
package org.eclipse.basyx.models.controlcomponent;

import org.eclipse.basyx.components.netcomm.NetworkReceiver;
import org.eclipse.basyx.components.netcomm.TCPServer;
import org.eclipse.basyx.vab.service.api.BaSyxService;

/**
 * Simplified control component for proxy implementations. The device
 * communicates via TCP with this component.
 * 
 * This control component implements the following behavior - It initially moves
 * to 'idle' state - Operation mode change is forwarded to device - Only
 * start/clear/reset commands are supported - Implement a simplified model that
 * only consists of states idle/execute/complete/aborted/stopped - Device
 * indicates abort transition with "abort" string, transition to complete state
 * with "complete" string
 * 
 * @author kuhn
 *
 */
public class SimpleProxyControlComponent extends SimpleControlComponent implements NetworkReceiver, BaSyxService {

	/**
	 * Version information for serialized instances
	 */
	private static final long serialVersionUID = 1L;

	/**
	 * TCP server
	 */
	protected TCPServer tcpServer = null;

	/**
	 * Component name
	 */
	protected String componentName = null;

	/**
	 * Server thread
	 */
	protected Thread serverThread = null;

	/**
	 * Flag that indicates the request for ending the execution
	 */
	protected boolean endExecution = false;

	/**
	 * Constructor
	 */
	public SimpleProxyControlComponent(int serverPort) {
		// Create TCP server
		tcpServer = new TCPServer(serverPort);
		// - Register TCP server callback for received messages
		tcpServer.addTCPMessageListener(this);
	}

	/**
	 * Received message from TCP server
	 */
	@Override
	public void onReceive(byte[] message) {
		// Convert received message to string
		String rxMessage = TCPServer.toString(message);

		// For now, support "abort", "idle", and "complete" messages
		if (rxMessage.equalsIgnoreCase(buildMessage(ExecutionState.IDLE))) {
			this.setExecutionState(ExecutionState.IDLE.getValue());
			return;
		}
		if (rxMessage.equalsIgnoreCase(buildMessage(ExecutionOrder.ABORT))) {
			this.setCommand(ExecutionOrder.ABORT.getValue().toLowerCase());
			return;
		}
		if (rxMessage.equalsIgnoreCase(buildMessage(ExecutionState.COMPLETE))) {
			// Process message based on state
			if (this.getExecutionState().equalsIgnoreCase(ExecutionState.EXECUTE.getValue()))
				this.finishState();
			else
				throw new RuntimeException("Semantic error detected");

			// Return
			return;
		}
		if (rxMessage.equalsIgnoreCase(buildMessage(ExecutionOrder.RESET))) {
			// Process message based on state
			if (this.getExecutionState().equalsIgnoreCase(ExecutionOrder.COMPLETE.getValue()))
				this.put(ControlComponent.CMD, ExecutionOrder.RESET.getValue().toLowerCase());
			else
				throw new RuntimeException("Semantic error detected");

			// Return
			return;
		}

		// End state
		if (rxMessage.equalsIgnoreCase("finishState")) {
			this.finishState();

			// Return
			return;
		}

		// Indicate error: unexpected message
		throw new RuntimeException("Unexpected message received");
	}

	/**
	 * Indicate an execution state change
	 */
	@Override
	protected String filterExecutionState(String newExecutionState) {
		// Invoke base implementation
		super.filterExecutionState(newExecutionState);

		// Only continue if TCP server exists
		// - If constructor changes state, this will be invoked before TCP server is
		// ready
		if (tcpServer == null)
			return newExecutionState;

		// Implement a simplified model that only consists of states
		// idle/execute/complete/aborted/stopped
		switch (ExecutionState.byValue(newExecutionState)) {
		// Only process the following states, ignore all other state transitions
		case IDLE:
			tcpServer.sendMessage(buildMessage(ExecutionState.IDLE));
			break;
		case EXECUTE:
			tcpServer.sendMessage(buildMessage(ExecutionState.EXECUTE));
			break;
		case COMPLETE:
			tcpServer.sendMessage(buildMessage(ExecutionState.COMPLETE));
			break;
		case ABORTED:
			tcpServer.sendMessage(buildMessage(ExecutionState.ABORTED));
			break;
		case STOPPED:
			tcpServer.sendMessage(buildMessage(ExecutionState.STOPPED));
			break;
		case RESETTING:
			tcpServer.sendMessage(buildMessage(ExecutionState.RESETTING));
			break;
		default:
			break;
		}

		// Return the unchanged execution state
		return newExecutionState;
	}

	/**
	 * Indicate operation mode change
	 */
	@Override
	public String filterOperationMode(String newOperationMode) {
		// Only continue if TCP server exists
		// - If constructor changes operation mode, this will be invoked before TCP
		// server is ready
		if (tcpServer == null)
			return newOperationMode;

		// Communicate new operation mode to device
		tcpServer.sendMessage("opMode:" + newOperationMode);

		// Return the unchanged operation mode
		return newOperationMode;
	}

	/**
	 * Start this service
	 */
	@Override
	public void start() {
		// Create and start TCP server
		serverThread = new Thread(tcpServer);
		serverThread.start();
	}

	/**
	 * Staop this service
	 */
	@Override
	public void stop() {
		// Indicate requested end of execution
		endExecution = true;

		// End TCP communication
		tcpServer.close();
		tcpServer.closeServer();

		// End thread
		try {
			serverThread.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Wait for end of service
	 */
	@Override
	public void waitFor() {
		// Wait for end of thread
		try {
			serverThread.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Change service name
	 */
	@Override
	public BaSyxService setName(String newName) {
		// Store name
		componentName = newName;

		// Return this
		return this;
	}

	/**
	 * Return name
	 */
	@Override
	public String getName() {
		return componentName;
	}

	/**
	 * Check end execution flag of this service
	 */
	@Override
	public boolean hasEnded() {
		// Return end execution flag
		return endExecution;
	}

	private String buildMessage(ExecutionState state) {
		return "state:" + state.getValue().toLowerCase();
	}

	private String buildMessage(ExecutionOrder state) {
		return "state:" + state.getValue().toLowerCase();
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy