Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "BaSyx / Introductory Examples / Java / Example 3"

m (Updates example to new control component interface)
m (Adds line numbers and expected output)
 
(5 intermediate revisions by 3 users not shown)
Line 10: Line 10:
  
 
First, in ''startMyControlComponent()'' the control component is created and hosted on a TCP server. Next, the code connects to the Control Component via direct address input and controls it via the [[BaSyx_/_Documentation_/_API_/_ControlComponent#Virtual_Automation_Bus_.28VAB.29_implementation | Control Component VAB API mapping]].
 
First, in ''startMyControlComponent()'' the control component is created and hosted on a TCP server. Next, the code connects to the Control Component via direct address input and controls it via the [[BaSyx_/_Documentation_/_API_/_ControlComponent#Virtual_Automation_Bus_.28VAB.29_implementation | Control Component VAB API mapping]].
<syntaxhighlight lang="java" style="margin-left: 4em">
+
<syntaxhighlight lang="java" style="margin-left: 4em" line>
 +
import org.eclipse.basyx.models.controlcomponent.ControlComponent;
 +
import org.eclipse.basyx.models.controlcomponent.ExecutionState;
 +
import org.eclipse.basyx.vab.coder.json.connector.JSONConnector;
 +
import org.eclipse.basyx.vab.modelprovider.VABElementProxy;
 +
import org.eclipse.basyx.vab.modelprovider.api.IModelProvider;
 +
import org.eclipse.basyx.vab.modelprovider.map.VABMapProvider;
 +
import org.eclipse.basyx.vab.protocol.basyx.connector.BaSyxConnector;
 +
import org.eclipse.basyx.vab.protocol.basyx.server.BaSyxTCPServer;
 +
import org.slf4j.Logger;
 +
import org.slf4j.LoggerFactory;
 +
 
 
/**
 
/**
 
  * The control component provides an additional abstraction for native device handling and has a specified interface.
 
  * The control component provides an additional abstraction for native device handling and has a specified interface.
Line 32: Line 43:
 
// Initializes a logger for the output
 
// Initializes a logger for the output
 
private static final Logger logger = LoggerFactory.getLogger(Scenario3.class);
 
private static final Logger logger = LoggerFactory.getLogger(Scenario3.class);
 
+
 
public static void main(String[] args) throws Exception {
 
public static void main(String[] args) throws Exception {
 
// Create the virtual oven specific to this HandsOn
 
// Create the virtual oven specific to this HandsOn
 
Oven myOven = new Oven();
 
Oven myOven = new Oven();
 
+
 
// Write a function, that starts a control component for the virtual oven
 
// Write a function, that starts a control component for the virtual oven
 
startMyControlComponent(myOven);
 
startMyControlComponent(myOven);
 
+
 
// Connect to the control component, see service interface here
 
// Connect to the control component, see service interface here
 
// This code also shows how to directly connect to a known location without
 
// This code also shows how to directly connect to a known location without
Line 46: Line 57:
 
// change
 
// change
 
VABElementProxy proxy = new VABElementProxy("", new JSONConnector(new BaSyxConnector("localhost", 4002)));
 
VABElementProxy proxy = new VABElementProxy("", new JSONConnector(new BaSyxConnector("localhost", 4002)));
 
+
 
// Select the operation mode for heating
 
// Select the operation mode for heating
proxy.setModelPropertyValue("STATUS/OPMODE", OvenControlComponent.OPMODE_HEAT);
+
proxy.setValue("STATUS/OPMODE", OvenControlComponent.OPMODE_HEAT);
 
+
 
// Start the selected operation in the control component
 
// Start the selected operation in the control component
 
proxy.invokeOperation("OPERATIONS/START");
 
proxy.invokeOperation("OPERATIONS/START");
Line 57: Line 68:
 
logger.info("CurrentTemperature: " + myOven.getSensor().readTemperature());
 
logger.info("CurrentTemperature: " + myOven.getSensor().readTemperature());
 
// Return true, if the control component has completed its operation
 
// Return true, if the control component has completed its operation
String currentState = (String) proxy.getModelPropertyValue("STATUS/EXSTATE");
+
String currentState = (String) proxy.getValue("STATUS/EXST");
 
if (currentState.equals(ExecutionState.COMPLETE.getValue())) {
 
if (currentState.equals(ExecutionState.COMPLETE.getValue())) {
 
// Reset the control component
 
// Reset the control component
Line 64: Line 75:
 
}
 
}
 
}
 
}
 
+
 
logger.info("Waiting for oven to cool down...");
 
logger.info("Waiting for oven to cool down...");
 
Thread.sleep(2500);
 
Thread.sleep(2500);
 
logger.info("CurrentTemperature: " + myOven.getSensor().readTemperature());
 
logger.info("CurrentTemperature: " + myOven.getSensor().readTemperature());
 
}
 
}
 
+
 
public static void startMyControlComponent(Oven oven) {
 
public static void startMyControlComponent(Oven oven) {
 
// Given is a local control component that can directly control the virtual oven device
 
// Given is a local control component that can directly control the virtual oven device
 
ControlComponent cc = new OvenControlComponent(oven);
 
ControlComponent cc = new OvenControlComponent(oven);
 
+
 
// Like the VAB model created before, the structure of the control component is a Map
 
// Like the VAB model created before, the structure of the control component is a Map
 
// Map ccModel = (Map) cc;
 
// Map ccModel = (Map) cc;
 
+
 
// Create a server for the Control Component and provide it in the VAB (at port 4002)
 
// Create a server for the Control Component and provide it in the VAB (at port 4002)
 
VABMapProvider ccProvider = new VABMapProvider(cc);
 
VABMapProvider ccProvider = new VABMapProvider(cc);
Line 92: Line 103:
 
Please note, that the ''OvenControlComponent'' takes some shortcuts for simplicity's sake in the context of this example. For example, having the control component also be its own ChangeListener may not be viable for a real world application.
 
Please note, that the ''OvenControlComponent'' takes some shortcuts for simplicity's sake in the context of this example. For example, having the control component also be its own ChangeListener may not be viable for a real world application.
  
<syntaxhighlight lang="java" style="margin-left: 4em">
+
<syntaxhighlight lang="java" style="margin-left: 4em" line>
 +
import org.eclipse.basyx.models.controlcomponent.ControlComponentChangeListener;
 +
import org.eclipse.basyx.models.controlcomponent.ExecutionMode;
 +
import org.eclipse.basyx.models.controlcomponent.ExecutionState;
 +
import org.eclipse.basyx.models.controlcomponent.OccupationState;
 +
import org.eclipse.basyx.models.controlcomponent.SimpleControlComponent;
 +
import org.slf4j.Logger;
 +
import org.slf4j.LoggerFactory;
 +
 
 
/**
 
/**
 
  * Control Component for controlling the oven. Has an additional operation mode named HEAT.
 
  * Control Component for controlling the oven. Has an additional operation mode named HEAT.
Line 100: Line 119:
 
private static final long serialVersionUID = 1L;
 
private static final long serialVersionUID = 1L;
 
         private static final Logger logger = LoggerFactory.getLogger(OvenControlComponent.class);
 
         private static final Logger logger = LoggerFactory.getLogger(OvenControlComponent.class);
 
+
 
public static final String OPMODE_BASIC = "BSTATE";
 
public static final String OPMODE_BASIC = "BSTATE";
 
public static final String OPMODE_HEAT = "HEAT";
 
public static final String OPMODE_HEAT = "HEAT";
 
+
 
private Oven oven;
 
private Oven oven;
 
+
 
public OvenControlComponent(Oven oven) {
 
public OvenControlComponent(Oven oven) {
 
this.oven = oven;
 
this.oven = oven;
 
addControlComponentChangeListener(this);
 
addControlComponentChangeListener(this);
 
}
 
}
 
+
 
@Override
 
@Override
 
public void onChangedExecutionState(ExecutionState newExecutionState) {
 
public void onChangedExecutionState(ExecutionState newExecutionState) {
Line 122: Line 141:
 
}
 
}
 
}
 
}
 
+
 
protected void controlHeater() {
 
protected void controlHeater() {
 
new Thread(() -> {
 
new Thread(() -> {
Line 141: Line 160:
 
}).start();
 
}).start();
 
}
 
}
 
+
 
@Override
 
@Override
 
public void onVariableChange(String varName, Object newValue) {
 
public void onVariableChange(String varName, Object newValue) {
 
}
 
}
 
+
 
@Override
 
@Override
 
public void onNewOccupier(String occupierId) {
 
public void onNewOccupier(String occupierId) {
 
}
 
}
 
+
 
@Override
 
@Override
 
public void onNewOccupationState(OccupationState state) {
 
public void onNewOccupationState(OccupationState state) {
 
}
 
}
 
+
 
@Override
 
@Override
 
public void onChangedExecutionMode(ExecutionMode newExecutionMode) {
 
public void onChangedExecutionMode(ExecutionMode newExecutionMode) {
 
}
 
}
 
+
 
@Override
 
@Override
 
public void onChangedOperationMode(String newOperationMode) {
 
public void onChangedOperationMode(String newOperationMode) {
 
}
 
}
 
+
 
@Override
 
@Override
 
public void onChangedWorkState(String newWorkState) {
 
public void onChangedWorkState(String newWorkState) {
 
}
 
}
 
+
 
@Override
 
@Override
 
public void onChangedErrorState(String newWorkState) {
 
public void onChangedErrorState(String newWorkState) {
Line 171: Line 190:
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 +
== Expected Output ==
 +
 +
The first output will be the info about the execution state of the OvenControlComponent, which will be '''EXECUTE'''.
 +
After this the heater will be deactivated and activated multiple times and log the current temperature at a few intervals.
 +
Once the execution state changes to '''COMPLETE''' the temperature will fall and the state will change to '''IDLE''' as the temperature falls again.
 +
 +
<pre style="margin-left: 4em">
 +
15:50:07.729 [org.eclipse.basyx.vab.protocol.basyx.server.VABBaSyxTCPInterface 1629726607727] INFO  i.OvenControlComponent - OvenControlComponent: new execution state: EXECUTE
 +
Heater: activated
 +
15:50:07.731 [main] INFO  i.Scenario3 - Using the control component to start the HEAT operation
 +
Heater: deactivated
 +
Heater: activated
 +
...
 +
15:50:08.735 [main] INFO  i.Scenario3 - CurrentTemperature: 31.27908533
 +
...
 +
15:50:09.744 [main] INFO  i.Scenario3 - CurrentTemperature: 30.035964578619193
 +
...
 +
15:50:13.160 [Thread-4] INFO  i.OvenControlComponent - OvenControlComponent: new execution state: COMPLETE
 +
15:50:13.781 [main] INFO  i.Scenario3 - CurrentTemperature: 25.93672972478958
 +
15:50:13.784 [org.eclipse.basyx.vab.protocol.basyx.server.VABBaSyxTCPInterface 1629726613784] INFO  i.OvenControlComponent - OvenControlComponent: new execution state: IDLE
 +
15:50:13.785 [main] INFO  i.Scenario3 - Waiting for oven to cool down...
 +
15:50:16.297 [main] INFO  i.Scenario3 - CurrentTemperature: 20.526168681839124
 +
</pre>

Latest revision as of 09:55, 23 August 2021

Example 3

Previously, Example 1 and 2 had set up the connection of the oven to the VAB. Through this connection, the oven is controllable regardless of location and protocol.

However, the service invocation is still custom made and may differ from oven to oven. Thus, an abstraction of service interface is needed. This is done by the Control Component.

In this example, a control component for the oven is created by extending the SimpleControlComponent class of the Components project.

Example Code

Again, the code is separated between local and remote code. First, the Control Component is set up. Please note, that in this tutorial it is not registered at the Registry. This is done to showcase how to connect to known entities on the VAB that for one reason or another are not registered.

First, in startMyControlComponent() the control component is created and hosted on a TCP server. Next, the code connects to the Control Component via direct address input and controls it via the Control Component VAB API mapping.

  1. import org.eclipse.basyx.models.controlcomponent.ControlComponent;
  2. import org.eclipse.basyx.models.controlcomponent.ExecutionState;
  3. import org.eclipse.basyx.vab.coder.json.connector.JSONConnector;
  4. import org.eclipse.basyx.vab.modelprovider.VABElementProxy;
  5. import org.eclipse.basyx.vab.modelprovider.api.IModelProvider;
  6. import org.eclipse.basyx.vab.modelprovider.map.VABMapProvider;
  7. import org.eclipse.basyx.vab.protocol.basyx.connector.BaSyxConnector;
  8. import org.eclipse.basyx.vab.protocol.basyx.server.BaSyxTCPServer;
  9. import org.slf4j.Logger;
  10. import org.slf4j.LoggerFactory;
  11.  
  12. /**
  13.  * The control component provides an additional abstraction for native device handling and has a specified interface.
  14.  * It can also be connected to the virtual automation bus.
  15.  * For more information on control components, see:
  16.  * https://wiki.eclipse.org/BaSyx_/_Documentation_/_API_/_ControlComponent
  17.  * 
  18.  * There, the VAB API is specified:
  19.  * https://wiki.eclipse.org/BaSyx_/_Documentation_/_API_/_ControlComponent#Virtual_Automation_Bus_.28VAB.29_implementation
  20.  * 
  21.  * In this HandsOn, a given control component for the virtual (proprietary) oven is utilized via the VAB.
  22.  * 
  23.  * Expected console output in this HandsOn:
  24.  * - state outputs from the OvenControlComponent
  25.  * - oven is activated and deactivated multiple times (not manually, but automatically using the control component this
  26.  * time)
  27.  * - temperature values at ~30
  28.  * - the oven cooling down after the control component is finished
  29.  */
  30. public class Scenario3 {
  31. 	// Initializes a logger for the output
  32. 	private static final Logger logger = LoggerFactory.getLogger(Scenario3.class);
  33.  
  34. 	public static void main(String[] args) throws Exception {
  35. 		// Create the virtual oven specific to this HandsOn
  36. 		Oven myOven = new Oven();
  37.  
  38. 		// Write a function, that starts a control component for the virtual oven
  39. 		startMyControlComponent(myOven);
  40.  
  41. 		// Connect to the control component, see service interface here
  42. 		// This code also shows how to directly connect to a known location without
  43. 		// using the Registry/ConnectionManager.
  44. 		// However, this assumes that the address of the Control Component will never
  45. 		// change
  46. 		VABElementProxy proxy = new VABElementProxy("", new JSONConnector(new BaSyxConnector("localhost", 4002)));
  47.  
  48. 		// Select the operation mode for heating
  49. 		proxy.setValue("STATUS/OPMODE", OvenControlComponent.OPMODE_HEAT);
  50.  
  51. 		// Start the selected operation in the control component
  52. 		proxy.invokeOperation("OPERATIONS/START");
  53. 		logger.info("Using the control component to start the HEAT operation");
  54. 		for (int i = 0; i < 10; i++) {
  55. 			Thread.sleep(1000);
  56. 			logger.info("CurrentTemperature: " + myOven.getSensor().readTemperature());
  57. 			// Return true, if the control component has completed its operation
  58. 			String currentState = (String) proxy.getValue("STATUS/EXST");
  59. 			if (currentState.equals(ExecutionState.COMPLETE.getValue())) {
  60. 				// Reset the control component
  61. 				proxy.invokeOperation("OPERATIONS/RESET");
  62. 				break;
  63. 			}
  64. 		}
  65.  
  66. 		logger.info("Waiting for oven to cool down...");
  67. 		Thread.sleep(2500);
  68. 		logger.info("CurrentTemperature: " + myOven.getSensor().readTemperature());
  69. 	}
  70.  
  71. 	public static void startMyControlComponent(Oven oven) {
  72. 		// Given is a local control component that can directly control the virtual oven device
  73. 		ControlComponent cc = new OvenControlComponent(oven);
  74.  
  75. 		// Like the VAB model created before, the structure of the control component is a Map
  76. 		// Map ccModel = (Map) cc;
  77.  
  78. 		// Create a server for the Control Component and provide it in the VAB (at port 4002)
  79. 		VABMapProvider ccProvider = new VABMapProvider(cc);
  80. 		// This time, a BaSyx-specific TCP interface is used.
  81. 		// Likewise, it is also possible to wrap the control component using a http servlet as before
  82. 		BaSyxTCPServer<IModelProvider> server = new BaSyxTCPServer<>(ccProvider, 4002);
  83. 		server.start();
  84. 	}
  85. }

Oven Control Component

For this example, a simple control component exposing the HEAT opmode is created. The Control Component activates and deactivates the oven depending on the temperature multiple times.

Please note, that the OvenControlComponent takes some shortcuts for simplicity's sake in the context of this example. For example, having the control component also be its own ChangeListener may not be viable for a real world application.

  1. import org.eclipse.basyx.models.controlcomponent.ControlComponentChangeListener;
  2. import org.eclipse.basyx.models.controlcomponent.ExecutionMode;
  3. import org.eclipse.basyx.models.controlcomponent.ExecutionState;
  4. import org.eclipse.basyx.models.controlcomponent.OccupationState;
  5. import org.eclipse.basyx.models.controlcomponent.SimpleControlComponent;
  6. import org.slf4j.Logger;
  7. import org.slf4j.LoggerFactory;
  8.  
  9. /**
  10.  * Control Component for controlling the oven. Has an additional operation mode named HEAT.
  11.  * This is a "black-box" example for a control component for the HandsOn.
  12.  */
  13. public class OvenControlComponent extends SimpleControlComponent implements ControlComponentChangeListener {
  14. 	private static final long serialVersionUID = 1L;
  15.         private static final Logger logger = LoggerFactory.getLogger(OvenControlComponent.class);
  16.  
  17. 	public static final String OPMODE_BASIC = "BSTATE";
  18. 	public static final String OPMODE_HEAT = "HEAT";
  19.  
  20. 	private Oven oven;
  21.  
  22. 	public OvenControlComponent(Oven oven) {
  23. 		this.oven = oven;
  24. 		addControlComponentChangeListener(this);
  25. 	}
  26.  
  27. 	@Override
  28. 	public void onChangedExecutionState(ExecutionState newExecutionState) {
  29. 		logger.info("OvenControlComponent: new execution state: " + newExecutionState);
  30. 		if (newExecutionState == ExecutionState.EXECUTE) {
  31. 			if (this.getOperationMode().equals(OPMODE_HEAT)) {
  32. 				controlHeater();
  33. 			} else {
  34. 				setExecutionState(ExecutionState.COMPLETE.getValue());
  35. 			}
  36. 		}
  37. 	}
  38.  
  39. 	protected void controlHeater() {
  40. 		new Thread(() -> {
  41. 			for (int i = 0; i < 50; i++) {
  42. 				if (oven.getSensor().readTemperature() < 30.0d) {
  43. 					oven.getHeater().activate();
  44. 				} else {
  45. 					oven.getHeater().deactivate();
  46. 				}
  47. 				try {
  48. 					Thread.sleep(100);
  49. 				} catch (InterruptedException e) {
  50. 					e.printStackTrace();
  51. 				}
  52. 			}
  53. 			oven.getHeater().deactivate();
  54. 			setExecutionState(ExecutionState.COMPLETE.getValue());
  55. 		}).start();
  56. 	}
  57.  
  58. 	@Override
  59. 	public void onVariableChange(String varName, Object newValue) {
  60. 	}
  61.  
  62. 	@Override
  63. 	public void onNewOccupier(String occupierId) {
  64. 	}
  65.  
  66. 	@Override
  67. 	public void onNewOccupationState(OccupationState state) {
  68. 	}
  69.  
  70. 	@Override
  71. 	public void onChangedExecutionMode(ExecutionMode newExecutionMode) {
  72. 	}
  73.  
  74. 	@Override
  75. 	public void onChangedOperationMode(String newOperationMode) {
  76. 	}
  77.  
  78. 	@Override
  79. 	public void onChangedWorkState(String newWorkState) {
  80. 	}
  81.  
  82. 	@Override
  83. 	public void onChangedErrorState(String newWorkState) {
  84. 	}
  85. }

Expected Output

The first output will be the info about the execution state of the OvenControlComponent, which will be EXECUTE. After this the heater will be deactivated and activated multiple times and log the current temperature at a few intervals. Once the execution state changes to COMPLETE the temperature will fall and the state will change to IDLE as the temperature falls again.

15:50:07.729 [org.eclipse.basyx.vab.protocol.basyx.server.VABBaSyxTCPInterface 1629726607727] INFO  i.OvenControlComponent - OvenControlComponent: new execution state: EXECUTE
Heater: activated
15:50:07.731 [main] INFO  i.Scenario3 - Using the control component to start the HEAT operation
Heater: deactivated
Heater: activated
...
15:50:08.735 [main] INFO  i.Scenario3 - CurrentTemperature: 31.27908533
...
15:50:09.744 [main] INFO  i.Scenario3 - CurrentTemperature: 30.035964578619193
...
15:50:13.160 [Thread-4] INFO  i.OvenControlComponent - OvenControlComponent: new execution state: COMPLETE
15:50:13.781 [main] INFO  i.Scenario3 - CurrentTemperature: 25.93672972478958
15:50:13.784 [org.eclipse.basyx.vab.protocol.basyx.server.VABBaSyxTCPInterface 1629726613784] INFO  i.OvenControlComponent - OvenControlComponent: new execution state: IDLE
15:50:13.785 [main] INFO  i.Scenario3 - Waiting for oven to cool down...
15:50:16.297 [main] INFO  i.Scenario3 - CurrentTemperature: 20.526168681839124

Back to the top