-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/main'
# Conflicts: # README.md
- Loading branch information
Showing
12 changed files
with
640 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
name: Build | ||
|
||
on: | ||
push: | ||
pull_request: | ||
branches: [ "main" ] | ||
|
||
jobs: | ||
build: | ||
|
||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- uses: actions/checkout@v4 | ||
- name: Set up JDK | ||
uses: actions/setup-java@v4 | ||
with: | ||
java-version: '23' | ||
distribution: 'liberica' | ||
cache: maven | ||
|
||
- name: Build EmbedControl | ||
run: mvn -B install -Dgpg.skip=true --file java/embedControlJavaFx/pom.xml | ||
|
||
- name: Build EmbeddedUi | ||
run: mvn -B install -Dgpg.skip=true --file java/embeddedJavaDeviceUI/pom.xml | ||
|
||
- name: Build Examples | ||
run: mvn -B install -Dgpg.skip=true --file java/javaApiExamples/pom.xml | ||
|
||
- name: Build Websocket Example | ||
run: mvn -B install -Dgpg.skip=true --file java/javaApiWebsocketServer/pom.xml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
Various demonstrations of how to use the Java API in your applications. Kept as simple as possible so that you can quickly apply these in your solution. | ||
|
||
## Examples for when the API is acting as a device | ||
|
||
These examples cover the cases where the API is acting as a device, for example an embedded Raspberry PI. | ||
|
||
* An example showing a device that tries to connect to client connections - `DeviceWithClientConnectionExample.java` | ||
* This code allows you to make AES encryption keys `MakeEncryptionKeyExample.java` | ||
|
||
## Examples where the API is running remotely connecting to a device | ||
|
||
These examples cover the API running remotely and connecting to a device. | ||
|
||
* `ClientThatAcceptsForRemoteExample.java` an API "client" that accepts connections from a device, where the device provides the bootstrap and you can control the remote application. | ||
* `ConnectToRemoteDeviceServerExample.java` connect to a remote device on a particular socket configuration. | ||
* `StandaloneRs232Test.java` same as above but an RS232 example. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!-- it is safe to edit this file, it will not be replaced by TcMenu designer unless you delete it --><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
<groupId>com.thecoderscorner.menuexample</groupId> | ||
<artifactId>javaApiExamples</artifactId> | ||
<name>javaApiExamples</name> | ||
<description>Example usage of TcMenu/EmbedControl APIs</description> | ||
<version>0.0.1-SNAPSHOT</version> | ||
|
||
<properties> | ||
<jserialcomm.version>2.11.0</jserialcomm.version> | ||
<tcmenu.api.version>4.4.0</tcmenu.api.version> | ||
<timestamp>${maven.build.timestamp}</timestamp> | ||
</properties> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>com.fazecast</groupId> | ||
<artifactId>jSerialComm</artifactId> | ||
<version>${jserialcomm.version}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.thecoderscorner.tcmenu</groupId> | ||
<artifactId>tcMenuJavaAPI</artifactId> | ||
<version>${tcmenu.api.version}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.thecoderscorner.tcmenu</groupId> | ||
<artifactId>embedCONTROLCore</artifactId> | ||
<version>${tcmenu.api.version}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.google.code.gson</groupId> | ||
<artifactId>gson</artifactId> | ||
<version>2.11.0</version> | ||
</dependency> | ||
</dependencies> | ||
|
||
<build> | ||
<finalName>javaApiExamples</finalName> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-compiler-plugin</artifactId> | ||
<version>3.13.0</version> | ||
<configuration> | ||
<source>22</source> | ||
<target>22</target> | ||
</configuration> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
</project> |
136 changes: 136 additions & 0 deletions
136
.../javaApiExamples/src/main/java/com/thecoderscorner/menu/examples/StandaloneRs232Test.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
/* | ||
* Copyright (c) 2016-2019 https://www.thecoderscorner.com (Dave Cherry). | ||
* This product is licensed under an Apache license, see the LICENSE file in the top-level directory. | ||
* | ||
*/ | ||
|
||
package com.thecoderscorner.menu.examples; | ||
|
||
import com.thecoderscorner.menu.domain.MenuItem; | ||
import com.thecoderscorner.menu.domain.state.MenuTree; | ||
import com.thecoderscorner.menu.remote.AuthStatus; | ||
import com.thecoderscorner.menu.remote.RemoteControllerListener; | ||
import com.thecoderscorner.menu.remote.RemoteInformation; | ||
import com.thecoderscorner.menu.remote.RemoteMenuController; | ||
import com.thecoderscorner.menu.remote.commands.AckStatus; | ||
import com.thecoderscorner.menu.remote.commands.MenuDialogCommand; | ||
import com.thecoderscorner.menu.remote.protocol.CorrelationId; | ||
import com.thecoderscorner.menu.remote.socket.SocketControllerBuilder; | ||
|
||
import java.util.Optional; | ||
import java.util.UUID; | ||
|
||
import static java.lang.System.Logger.Level.INFO; | ||
|
||
/** | ||
* An example showing the use of an RS232 based connection between a device and the API acting as a client. In this | ||
* case the device acts as a server and the API acts as a client receiving the menu tree from the device. You can | ||
* monitor and control the menu on the device over the serial connection. The protocol is quite lightweight and works | ||
* from about 9600 baud upward depending on number of updates per second. | ||
*/ | ||
public class StandaloneRs232Test { | ||
|
||
private final static System.Logger logger = System.getLogger("StandaloneRs232Test"); | ||
|
||
// Before use change the UUID shown below. From jshell run UUID.randomUUID() to get a new one | ||
private final UUID uuid = UUID.fromString("575d327e-fe76-4e68-b0b8-45eea154a126"); | ||
|
||
/** | ||
* A MenuTree object represents a hierarchy of MenuItem's in a tree like format. | ||
* We pass an empty one of these to the controller, and it populates them. Note that | ||
* each controller manages exactly one MenuTree. | ||
*/ | ||
private final MenuTree menuTree = new MenuTree(); | ||
private RemoteMenuController controller; | ||
|
||
/** | ||
* Just create an instance of the class to proceed. | ||
*/ | ||
public static void main(String[] args) { | ||
new StandaloneRs232Test().start(); | ||
} | ||
|
||
/** | ||
* Here we create the connection and register our listener. | ||
*/ | ||
public void start() { | ||
|
||
// This is the name that will appear on the Arduino side for this connection | ||
String myName = "OfficeMac"; | ||
|
||
// Change this to the name of your serial port | ||
String portName = "/dev/cu.usbmodemFD131"; | ||
|
||
// Change this to set the baud rate | ||
int baud = 9600; | ||
|
||
logger.log(INFO, "Creating an rs232 connection to {0} at {1} baud", portName, baud); | ||
|
||
// Now we use the rs232 builder to make a suitably configured instance of a | ||
// controller that can talk over serial and work with our menuTree. | ||
controller = new SocketControllerBuilder() | ||
.withAddress("192.168.0.22") | ||
.withPort(3333) | ||
.withMenuTree(menuTree) | ||
.withLocalName(myName) | ||
.withUUID(uuid) | ||
//.withHeartbeatFrequency(10000000) // uncomment when debugging to prevent timeouts. | ||
.build(); | ||
|
||
// now we simply add our remote listener (class definition below) and start up the comms. | ||
controller.addListener(new MyRemoteListener()); | ||
controller.start(); | ||
|
||
// here you could do whatever tasks you'd normally perform.. | ||
while(!Thread.currentThread().isInterrupted()) { | ||
try { | ||
Thread.sleep(1000); | ||
} catch (InterruptedException e) { | ||
Thread.currentThread().interrupt(); | ||
} | ||
} | ||
} | ||
|
||
private class MyRemoteListener implements RemoteControllerListener { | ||
@Override | ||
public void menuItemChanged(MenuItem item, boolean valueOnly) { | ||
logger.log(INFO, "Menu Item has changed: " + item); | ||
} | ||
|
||
@Override | ||
public void treeFullyPopulated() { | ||
logger.log(INFO, "Tree is fully populated"); | ||
|
||
// here we first traverse through all the submenus (even ROOT is a submenu)! | ||
menuTree.getAllSubMenus().forEach(subMenu -> { | ||
logger.log(INFO, "SubMenu {0} has the following child elements", subMenu); | ||
// and then we go through all the items within that submenu. | ||
menuTree.getMenuItems(subMenu).forEach(item -> logger.log(INFO, "----->>> " + item)); | ||
}); | ||
|
||
// how to get an item by its id, we look for ID 1 in the root menu, if it's there we log it and send | ||
// a delta change command. | ||
Optional<MenuItem> maybeItem = menuTree.getMenuById(1); | ||
maybeItem.ifPresent( item -> { | ||
logger.log(INFO, "Retrieved {0} by its ID {1}, change by 5", item.getName(), item.getId()); | ||
CorrelationId id = controller.sendDeltaUpdate(item, +5); | ||
logger.log(INFO, "Correlation id was " + id); | ||
}); | ||
} | ||
|
||
@Override | ||
public void connectionState(RemoteInformation remoteInformation, AuthStatus connected) { | ||
logger.log(INFO, "Connection information: " + remoteInformation + ". Connected: " + connected); | ||
} | ||
|
||
@Override | ||
public void ackReceived(CorrelationId key, MenuItem item, AckStatus st) { | ||
logger.log(INFO, "Ack -" + key + " item " + item + " status " + st); | ||
} | ||
|
||
@Override | ||
public void dialogUpdate(MenuDialogCommand cmd) { | ||
// not interested in dialog updates. | ||
} | ||
} | ||
} |
100 changes: 100 additions & 0 deletions
100
...main/java/com/thecoderscorner/menu/examples/client/ClientThatAcceptsForRemoteExample.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package com.thecoderscorner.menu.examples.client; | ||
|
||
import com.thecoderscorner.menu.domain.state.MenuTree; | ||
import com.thecoderscorner.menu.remote.RemoteMenuController; | ||
import com.thecoderscorner.menu.remote.socket.SocketClientRemoteConnector; | ||
import com.thecoderscorner.menu.remote.socket.SocketClientServerListener; | ||
import com.thecoderscorner.menu.remote.socket.SocketControllerBuilder; | ||
|
||
import java.io.IOException; | ||
import java.lang.System.Logger.Level; | ||
import java.util.Arrays; | ||
import java.util.Map; | ||
import java.util.UUID; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
import java.util.logging.LogManager; | ||
|
||
import static com.thecoderscorner.menu.remote.socket.SocketClientRemoteServer.UuidAndSerial; | ||
import static java.util.logging.Level.FINEST; | ||
|
||
/** | ||
* This example is a client in that it receives menu state and can control menu applications running on a device, but | ||
* however it is the accepting side of the connection. You can control the number of connections that you're prepared | ||
* to accept, maybe in some cases like now to 1, but by default the acceptor will allow up to 99999 connections. | ||
* | ||
* See DeviceWithClientConnectionExample for the other side of this example | ||
*/ | ||
public class ClientThatAcceptsForRemoteExample { | ||
private final static System.Logger logger = System.getLogger("ExampleClient"); | ||
|
||
// the port on which we will accept. | ||
public static final int MY_PORT = 3333; | ||
private static final String MY_LOCAL_NAME = "Test Client"; | ||
private static final UUID MY_LOCAL_UUID = UUID.fromString("8A19E904-B007-498A-9BCB-5F0C0A7B9D71"); | ||
public static final String ENCRYPTED_AES_KEY = "A8UvLzdTzUCYeqir6DODRquIbch04kN1EuyocNqoJI4="; | ||
public static final String ENCRYPTED_AES_IV = "PouIJPG+eN5WtbbGESuPeg=="; | ||
|
||
public static void main(String[] args) throws IOException { | ||
// enable all logging including debug. | ||
LogManager.getLogManager().getLogger("").setLevel(FINEST); | ||
Arrays.stream(LogManager.getLogManager().getLogger("").getHandlers()).forEach(h -> h.setLevel(FINEST)); | ||
|
||
// here we minimally configure a socket accept client, that in this case will only accept one connection at once | ||
// but you control that using withMaximumInstances. | ||
var builder = new SocketControllerBuilder() | ||
.withMaximumInstances(1) | ||
.withPort(MY_PORT) | ||
.withLocalName(MY_LOCAL_NAME) | ||
.withUUID(MY_LOCAL_UUID) | ||
.withAESEncryption(ENCRYPTED_AES_KEY, ENCRYPTED_AES_IV); | ||
var deviceConnections = builder.buildClient(); | ||
|
||
// Now we add a listener that gets notified every time a connection is created or closed. | ||
deviceConnections.addConnectionListener(new MyConnectionListener()); | ||
|
||
// start up the server, at this point it starts accepting connections. | ||
deviceConnections.start(); | ||
|
||
// | ||
// if you wanted to access device connections yourself, without using the below listener support | ||
// | ||
|
||
//var allConnections = deviceConnections.getConnections(); | ||
|
||
var maybeUuid = deviceConnections.getFirstConnectionWithUUID(UUID.randomUUID()); | ||
maybeUuid.ifPresent(connector -> logger.log(Level.INFO, "Connector for my UUID is " + connector)); | ||
|
||
var maybeUuidSerial = deviceConnections.getConnection(UUID.randomUUID(), 1234); | ||
maybeUuidSerial.ifPresent(connector -> logger.log(Level.INFO, "Connector is " + connector)); | ||
} | ||
|
||
/** | ||
* This is an example of how you'd process connections as they come in, you get notified when there's a new connection | ||
* and when one is closed. In most cases you'd want a RemoteMenuController, unless you wanted custom control over the | ||
* protocol yourself. You could create your own object here that managed the connection using the methods available | ||
* on either the connector or the controller. | ||
*/ | ||
private static class MyConnectionListener implements SocketClientServerListener { | ||
private final Map<UuidAndSerial, RemoteMenuController> remoteMenuControllers = new ConcurrentHashMap<>(); | ||
@Override | ||
public void onConnectionCreated(SocketClientRemoteConnector connector) { | ||
logger.log(Level.INFO, "Connection has been received from " + connector.getRemoteParty()); | ||
// here you could create an object that represented your side of the connection etc. | ||
// in this case I just create a remote controller that handles the bootstrapping and update logic | ||
// Creating and starting a remote controller also starts the underlying connection. | ||
var controller = new RemoteMenuController(connector, new MenuTree()); | ||
controller.start(); | ||
remoteMenuControllers.put(UuidAndSerial.fromRemote(connector.getRemoteParty()), controller); | ||
} | ||
|
||
@Override | ||
public void onConnectionClosed(SocketClientRemoteConnector connector) { | ||
logger.log(Level.INFO, "Connection closed from " + connector.getRemoteParty()); | ||
// here you would do any cleaning up needed when a connection closed. | ||
var controller = remoteMenuControllers.get(UuidAndSerial.fromRemote(connector.getRemoteParty())); | ||
if(controller != null) { | ||
controller.stop(); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.