|
|
Here, I'll walk you through the implementation of the [Light Animator](What-am-I-looking-at-here#light-animator). Additional startup procedures are explained in the documentation for the `start_component.sh` and the `run_demo.sh` scripts.
|
|
|
|
|
|
We'll skip the license headers, imports and comments, as they are either pretty much self explanatory or not the product of conscious development effort.
|
|
|
|
|
|
# ILightAnimator
|
|
|
The discussed file can be found [here](https://gitlab.hrz.tu-chemnitz.de/vws-demo/vws-spielwiese/-/blob/6d65d24412bda2e555e70a4f9581eb142096bd49/basyx.lichterkette/src/main/java/de/olipar/basyx/lichterkette/ILightAnimator.java).
|
|
|
|
|
|
This interface defines what methods a light animator has to provide:
|
|
|
| Method | Purpose |
|
|
|
| --------------------- | ---------------------------------------------------------------- |
|
|
|
| `staticLight()` | Instruct all found light controllers to turn on all their lights |
|
|
|
| `animation1()` | Orchestrate all found light controllers to play animation 1 |
|
|
|
| `animation2()` | Orchestrate all found light controllers to play animation 2 |
|
|
|
| `deactivate()` | Stop controlling any light controllers until reactivation |
|
|
|
| `updateControllers()` | Refresh the list of known light controllers to orchestrate |
|
|
|
| `isActive()` | Return whether this animator is active or not. |
|
|
|
|
|
|
# LightAnimator
|
|
|
|
|
|
The discussed file can be found [here](https://gitlab.hrz.tu-chemnitz.de/vws-demo/vws-spielwiese/-/blob/6d65d24412bda2e555e70a4f9581eb142096bd49/basyx.lichterkette/src/main/java/de/olipar/basyx/lichterkette/LightAnimator.java).
|
|
|
|
|
|
```java
|
|
|
public class LightAnimator implements ILightAnimator {
|
|
|
```
|
|
|
Just like the LightController, its currently the only implementation of its interface in this demo. It provides all the methods specified above.
|
|
|
|
|
|
```java
|
|
|
private boolean isActive = false;
|
|
|
private boolean isStaticLight = false;
|
|
|
private boolean isAnimation1 = false;
|
|
|
private boolean isAnimation2 = false;
|
|
|
private int animationState = 0;
|
|
|
```
|
|
|
It starts off very similar, too, by defining a few internal variables. Of note here is the `animationState`, which will be used to cycle through the animations defined later.
|
|
|
|
|
|
```java
|
|
|
private final double updatePeriod = 3.0D;
|
|
|
```
|
|
|
Similarly an update period is defined (in seconds). It is the minimum amount of time to be waited between applying state changes.
|
|
|
|
|
|
```java
|
|
|
private final AASRegistryProxy registry;
|
|
|
private ConnectedAssetAdministrationShellManager manager;
|
|
|
private List<ISubmodel> lightControllers = new LinkedList<ISubmodel>();
|
|
|
```
|
|
|
We also use a registry proxy and AAS Manager to fill our list of Submodels of light controllers, comparable to how the light controller fills its list of lights.
|
|
|
|
|
|
```java
|
|
|
private boolean isStale = true;
|
|
|
```
|
|
|
If this is set to true, the list of light controllers will be updated on the next main loop.
|
|
|
|
|
|
```java
|
|
|
public LightAnimator(final AASRegistryProxy registry) {
|
|
|
this.registry = registry;
|
|
|
this.manager = new ConnectedAssetAdministrationShellManager(registry);
|
|
|
```
|
|
|
Then, we enter the constructor. it sets the registry to the one given and with it instantiates an AAS manager.
|
|
|
|
|
|
```java
|
|
|
new Thread(() -> {
|
|
|
while (true) {
|
|
|
```
|
|
|
We then create our main work thread, which consists of the main loop of the animator.
|
|
|
|
|
|
```java
|
|
|
try {
|
|
|
Thread.sleep((long) (updatePeriod * 1000));
|
|
|
} catch (InterruptedException e) {
|
|
|
e.printStackTrace();
|
|
|
}
|
|
|
if (isActive) {
|
|
|
```
|
|
|
At the start, we wait for at least our defined `updatePeriod` seconds and if we're not active, just go back to sleep, like the light controller does, too.
|
|
|
|
|
|
```java
|
|
|
if (isStale) {
|
|
|
System.out.println("Controller list of this animator is stale. Updating...");
|
|
|
this.updateControllerList();
|
|
|
isStale = false;
|
|
|
}
|
|
|
```
|
|
|
If the list of light controllers is marked stale, it is being updated via the `updateControllerList()` method explained below.
|
|
|
|
|
|
We then enter a `try`-block. Inside, our possible states are executed accordingly, utilizing methods declared below. Note that when we are to orchestrate static lighting, `allLights()` is only called once, as the `animationState` is not set to zero but incremented.
|
|
|
|
|
|
Animation1 Switches on all lights, then only the red ones, then only the green ones and back to all lights on repeat.
|
|
|
|
|
|
Animation2 switches between blinking lights and all lights being switched on
|
|
|
|
|
|
```java
|
|
|
catch (ResourceNotFoundException e) {
|
|
|
if (!isStale) {
|
|
|
System.out.println("Invoking a controller failed. List will be updated on next update while active.");
|
|
|
}
|
|
|
isStale = true;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
If we can't reach an endpoint, then we'll issue a warning and update our list of available controllers on the next iteration.
|
|
|
|
|
|
```java
|
|
|
}).start();
|
|
|
```
|
|
|
Then we start the work thread.
|
|
|
|
|
|
```java
|
|
|
private void activate() {
|
|
|
if (!isActive) {
|
|
|
System.out.println("Light animator activated");
|
|
|
isActive = true;
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
This method is used internally to log the activation of the light animator, if an animation or static lighting is invoked.
|
|
|
|
|
|
```java
|
|
|
private void redLight() {
|
|
|
for (ISubmodel controller : lightControllers) {
|
|
|
((IOperation) controller.getSubmodelElement("redLight")).invoke();
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
Following that, there are four methods defined, that are used inside the main loop to make it more readable. They encapsulate the actual incokation of the Operations exposed by the controllers.
|
|
|
|
|
|
```java
|
|
|
public void updateControllers() {
|
|
|
isStale = true;
|
|
|
}
|
|
|
```
|
|
|
This method is exposed by the interface to update the local list of available controllers. It sets `isStale` to `true`, causing an update on the next update period. This is to not cause concurrent modification of the list while iterating through it.
|
|
|
|
|
|
```java
|
|
|
public void updateControllerList() {
|
|
|
lightControllers.clear();
|
|
|
```
|
|
|
This method actually updates the list of controllers found in the environment. It starts by clearing the existing list.
|
|
|
|
|
|
```java
|
|
|
for (AASDescriptor iter : registry.lookupAll()) {
|
|
|
if (iter.getIdShort().contains("lightController")) {
|
|
|
```
|
|
|
We then look up all registered AAS and filter for those, whose idShort contains the string "lightController"
|
|
|
|
|
|
```java
|
|
|
for (SubmodelDescriptor controllerModel : iter.getSubmodelDescriptors()) {
|
|
|
if (controllerModel.getIdShort().equals("lightControllerSM")) {
|
|
|
```
|
|
|
Having found such, its Submodels are searched for one, whose shortId equals "lightControllerSM"
|
|
|
|
|
|
```java
|
|
|
ISubmodel submodel = manager.retrieveSubmodel(iter.getIdentifier(), controllerModel.getIdentifier());
|
|
|
lightControllers.add(submodel);
|
|
|
```
|
|
|
Now that we have found a compatible the AAS with a submodel we want, we use the manager to add it to our list.
|
|
|
|
|
|
This, too, could probably be done more elegantly by looking for implementations of Types of AAS and Submodels, but that is left to a possible [future project](TODO).
|
|
|
|
|
|
```java
|
|
|
public void animation1() {
|
|
|
activate();
|
|
|
isAnimation1 = true;
|
|
|
isAnimation2 = false;
|
|
|
isStaticLight = false;
|
|
|
animationState = 0;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
Then, four methods like above are defined, setting internal states in order to expose the correct orchestration done in the main work loop.
|
|
|
These should be pretty much self explanatory.
|
|
|
|
|
|
Only thing of note here, is that upon deactivation, the light animator orchestrates all known light controllers to also deactivate.
|
|
|
|
|
|
# LightAnimatorSubmodelProvider
|
|
|
|
|
|
Please read the walkthrough of the [lightControllerSubmodelProvider](Light-Controller-walkthrough#lightcontrollersubmodelprovider). It only differs by assigned ids and in the number of Properties and Operations defined. It is also quite extensively commented, so this section would only be a repetition.
|
|
|
|
|
|
You can find the file [here](https://gitlab.hrz.tu-chemnitz.de/vws-demo/vws-spielwiese/-/blob/6d65d24412bda2e555e70a4f9581eb142096bd49/basyx.lichterkette/src/main/java/de/olipar/basyx/lichterkette/LightAnimatorSubmodelProvider.java). |
|
|
\ No newline at end of file |