Skip to content

Commit

Permalink
Version of Embedded Java App that has a custom panel.
Browse files Browse the repository at this point in the history
  • Loading branch information
davetcc committed Nov 9, 2024
1 parent 64e1045 commit 88ec627
Show file tree
Hide file tree
Showing 14 changed files with 352 additions and 43 deletions.
19 changes: 11 additions & 8 deletions java/embeddedJavaDeviceUI/README.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,34 @@
# EmbeddedJavaDemo menu application

This application is an example of how to use tcMenu's EmbedControl to produce a basic user interface that would run on an embedded device such as a raspberry PI. The application is composed of objects that are wired together in the main class. This elimiates the need for any kind of dependency framework. Should you wish to, you can use our very light weight database objects that are also used within tcMenu itself.
This application is an example of how to use tcMenu's EmbedControl to produce a basic user interface that would run on an embedded device such as a raspberry PI. The application is composed of objects that are wired together in the main class. We also include a really cut down dependency framework that is fully JPMS compliant and very small. Should you wish to, you can use our very light weight database objects that are also used within tcMenu itself.

## How the app is organised.

The application is split up into several files:

* EmbeddedJavaDemoApp - this is the class that starts up your application and initialises any plugins you selected. You should not touch this class as it is overwritten every time around.
* EmbeddedJavaDemoMenu - this is the class that holds all the menu definitions and the menu tree. It is available in the spring context, and you can inject it into any of your components that need it. It also has short-cut methods to get every menu item.
* EmbeddedJavaDemoController - this is where any callbacks that you register go, at the moment we support only one controller, in future we may provide support for more than one. Each function callback that you declare in TcMenu Designer will turn into a method in here. This also allows you to listen for any menu item, and for start and stop events. Further, you can change the controller's constructor to include other components if needed.
* `EmbeddedJavaDemoApp` - this is the class that starts up your application and initialises any plugins you selected. You should not touch this class as it is overwritten every time around.
* `EmbeddedJavaDemoMenu` - this is the class that holds all the menu definitions and the menu tree. It is available in the spring context, and you can inject it into any of your components that need it. It also has short-cut methods to get every menu item.
* `EmbeddedJavaDemoController` - this is where any callbacks that you register go, at the moment we support only one controller, in future we may provide support for more than one. Each function callback that you declare in TcMenu Designer will turn into a method in here. This also allows you to listen for any menu item, and for start and stop events. Further, you can change the controller's constructor to include other components if needed.

## Building the app

By default, the app uses maven to build, you'll need a couple of things installed to continue:

* Java - Get the most recent version for your platform, the platform must support JavaFX if you're using the UI components, this is nearly all distributions of Linux I've seen, macOS and Windows.
* A recent maven 3 installation. Maven is a very complete build tool and [you can read more about it here](https://maven.apache.org/guides/getting-started/).
* A Java IDE - we mainly use IntelliJ, but have tried the project in Visual-Studio-Code too. Eclipse similarly should work very well with this project.
* A Java IDE - we recommend IntelliJ, but have tried the project in Visual-Studio-Code too. Eclipse similarly should work very well with this project.
* To build from the command line ensure you are in the same directory as this README file and type `mvn clean install`, which will build the application and bring down any dependencies.

## Running the application from the CLI

If you use the standard maven setup, after running the above build steps, you should see the following directory has been created: `target/jfx/` containing an `app` directory and a `deps` directory. We recommend running the application from the `target/jfx/app` directory.

If you used a modular build (IE you have a `module-info.java` file in the `src/main/java` directory) then to run the application ensure that the right version of Java using `java -version` is on your path and then the run command should be `java --module-path ../deps "-Dprism.lcdtext=false" --add-modules com.thecoderscorner.menuexample.embeddedjavademo com.thecoderscorner.menuexample.tcmenu.EmbeddedJavaDemoApp`
If you used a modular build (IE you have a `module-info.java` file in the `src/main/java` directory) then to run the application ensure that the right version of Java using `java -version` is on your path and then the run command should be `java --module-path ../deps "-Dprism.lcdtext=false" --add-modules com.thecoderscorner.menuexample.embeddedjavademo com.thecoderscorner.menu.devicedemo.EmbeddedJavaDemoApp`

## Plugins
## JavaFx local UI

Depending on which plugins you chose to install, there will be other files that are associated with those in the source tree, these are separated out into a `plugins` directory. We've provided a few documentation links below to get you started:
In the optional folder, there's a couple of classes that will generate a JavaFX UI automatically for your UI. These are documented within the folder.

## Web server and TagVal server

There's both a webserver that serves up [EmbedControlJS](https://github.com/TcMenu/embedcontrolJS) to a browser, and also TagVal support too, so you connect to it from the API.
2 changes: 1 addition & 1 deletion java/embeddedJavaDeviceUI/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<properties>
<jfx.version>23.0.1</jfx.version>
<jserialcomm.version>2.11.0</jserialcomm.version>
<tcmenu.api.version>4.4.0</tcmenu.api.version>
<tcmenu.api.version>4.4.1-SNAPSHOT</tcmenu.api.version>
<timestamp>${maven.build.timestamp}</timestamp>
</properties>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.thecoderscorner.menuexample.tcmenu;
package com.thecoderscorner.menu.devicedemo;

import com.thecoderscorner.menu.mgr.MenuInMenu;
import com.thecoderscorner.menu.mgr.MenuManagerServer;
Expand All @@ -7,15 +7,16 @@
import com.thecoderscorner.menu.remote.LocalIdentifier;
import com.thecoderscorner.menu.remote.MenuCommandProtocol;
import com.thecoderscorner.menu.remote.socket.SocketBasedConnector;
import com.thecoderscorner.menuexample.tcmenu.plugins.JfxLocalAutoUI;
import com.thecoderscorner.menuexample.tcmenu.plugins.TcJettyWebServer;
import com.thecoderscorner.menu.devicedemo.optional.JfxLocalAutoUI;
import com.thecoderscorner.menu.devicedemo.optional.TcJettyWebServer;
import javafx.application.Application;

import java.time.Clock;
import java.util.concurrent.ScheduledExecutorService;

/**
* This class is the application class and should not be edited, it will be recreated on each code generation
* This class is the application class that is run when the application starts. You can organize the application in
* here into a series of components. This is and example of how to arrange such an application.
*/
public class EmbeddedJavaDemoApp {
private final MenuManagerServer manager;
Expand All @@ -29,20 +30,34 @@ public EmbeddedJavaDemoApp() {
}

public void start() {
// Firstly we get the menu state serializer, this can load and save menu state
var serializer = context.getBean(MenuStateSerialiser.class);
serializer.loadMenuStatesAndApply();
// save states when the app shuts down
Runtime.getRuntime().addShutdownHook(new Thread(serializer::saveMenuStates));
// the controller receives updates and things happen on the menu, we register it here.
manager.addMenuManagerListener(context.getBean(EmbeddedJavaDemoController.class));
// See the method for more information.
buildMenuInMenuComponents();

// Give the Local UI access to teh context
JfxLocalAutoUI.setAppContext(context);

// here we start a webserver, it is initialised in the config file.
manager.addConnectionManager(webServer);

// and finally, start the local JavaFX UI.
Application.launch(JfxLocalAutoUI.class);
}

public static void main(String[] args) {
new EmbeddedJavaDemoApp().start();
}

///
/// here we demonstrate how to include menu in menu components. You can use tcMenu Designer to generate the menu in
/// menu components and add them here, it can build the entire method for you from `Code -> Menu In Menu`
///
public void buildMenuInMenuComponents() {
MenuManagerServer menuManager = context.getBean(MenuManagerServer.class);
MenuCommandProtocol protocol = context.getBean(MenuCommandProtocol.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.thecoderscorner.menuexample.tcmenu;
package com.thecoderscorner.menu.devicedemo;

import com.thecoderscorner.embedcontrol.core.service.GlobalSettings;
import com.thecoderscorner.embedcontrol.customization.MenuItemStore;
import com.thecoderscorner.embedcontrol.jfx.controlmgr.JfxNavigationHeader;
import com.thecoderscorner.embedcontrol.jfx.controlmgr.JfxNavigationManager;
import com.thecoderscorner.embedcontrol.jfx.controlmgr.TitleWidget;
Expand Down Expand Up @@ -38,16 +37,13 @@ public class EmbeddedJavaDemoController implements MenuManagerListener {
private final JfxNavigationManager navigationManager;
private final ScheduledExecutorService executorService;
private final GlobalSettings globalSettings;
private final MenuItemStore itemStore;

public EmbeddedJavaDemoController(EmbeddedJavaDemoMenu menuDef, JfxNavigationManager navigationManager,
ScheduledExecutorService executorService, GlobalSettings settings,
MenuItemStore itemStore) {
ScheduledExecutorService executorService, GlobalSettings settings) {
this.menuDef = menuDef;
this.navigationManager = navigationManager;
this.executorService = executorService;
this.globalSettings = settings;
this.itemStore = itemStore;
}

@MenuCallback(id=15, listResult=true)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
package com.thecoderscorner.menuexample.tcmenu;
package com.thecoderscorner.menu.devicedemo;

import com.thecoderscorner.menu.domain.*;
import com.thecoderscorner.menu.domain.state.MenuTree;
import com.thecoderscorner.menu.persist.JsonMenuItemSerializer;

/**
* The EmbeddedJavaDemoMenu class initializes a menu tree with predefined menu items
* and provides access to this tree and its JSON serializer. You can get an instance of
* this class from TcMenu Designer from menu: `Code -> Produce Java Menu Class`.
*
* In most applications you'd rename this class to something more suitable for your
* application, EG in an audio amplifier it may be called `AmplifierSettingsMenu`. You
* can refactor the class name in IntelliJ.
*
* This menu system can include various types of menu items such as analog,
* boolean, text items, etc. Each item in the menu is defined in the APP_MENU_ITEMS string
* in JSON format, which is then deserialized and loaded into a MenuTree object.
*/
public class EmbeddedJavaDemoMenu {
private final static String APP_MENU_ITEMS = """
tcMenuCopy:[
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.thecoderscorner.menuexample.tcmenu;
package com.thecoderscorner.menu.devicedemo;

import com.thecoderscorner.embedcontrol.core.service.GlobalSettings;
import com.thecoderscorner.embedcontrol.core.util.BaseMenuConfig;
Expand All @@ -15,19 +15,19 @@
import com.thecoderscorner.menu.persist.PropertiesMenuStateSerialiser;
import com.thecoderscorner.menu.persist.VersionInfo;
import com.thecoderscorner.menu.remote.protocol.ConfigurableProtocolConverter;
import com.thecoderscorner.menuexample.tcmenu.plugins.TcJettyWebServer;
import com.thecoderscorner.menu.devicedemo.optional.TcJettyWebServer;

import java.nio.file.Path;
import java.time.Clock;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

/**
* This class creates an application context out of all these components, and you can request any components that are
* put into the context using getBean(ClassName.class). See the base class BaseMenuConfig for more details. Generally
* don't change the constructor, as it is rebuild each time around. Prefer putting your own code in appCustomConfiguration
*/
/// This class creates an application context out of all these components, and you can request any components that are
/// put into the context using getBean(ClassName.class). See the base class `BaseMenuConfig` for more details.
/// In summary, every function annotated with `@TcComponent` will be added to the "context" and you can then request
/// it later using `getBean`. Consider it a lightweight spring that is JPMS module system compliant. Anything that is
/// marked as a `TcComponent` will be available automatically to other methods marked `TcComponent`.
public class MenuConfig extends BaseMenuConfig {
@TcComponent
public JfxNavigationHeader navMgr(ScheduledExecutorService executorService, GlobalSettings settings) {
Expand Down Expand Up @@ -90,16 +90,16 @@ public TcJettyWebServer webServer(ConfigurableProtocolConverter protocol, Clock

@TcComponent
public EmbeddedJavaDemoController controller(EmbeddedJavaDemoMenu menuDef, JfxNavigationManager navMgr,
ScheduledExecutorService executor, GlobalSettings settings,
MenuItemStore itemStore) {
return new EmbeddedJavaDemoController(menuDef, navMgr, executor, settings, itemStore);
ScheduledExecutorService executor, GlobalSettings settings) {
return new EmbeddedJavaDemoController(menuDef, navMgr, executor, settings);
}

public MenuConfig() {
// Do not change this constructor, it is replaced with each build, put your objects in appCustomConfiguration
super(null, null);
Clock clock = asBean(Clock.systemUTC());
var executorService = asBean(Executors.newScheduledThreadPool(propAsIntWithDefault("threading.pool.size", 4)));
// put a clock and thread pool into the "context"
asBean(Clock.systemUTC());
asBean(Executors.newScheduledThreadPool(propAsIntWithDefault("threading.pool.size", 4)));
// add the menu tree into the context
var menuDef = asBean(new EmbeddedJavaDemoMenu());
asBean(new PropertiesMenuStateSerialiser(menuDef.getMenuTree(), Path.of(resolvedProperties.getProperty("file.menu.storage")).resolve("menuStorage.properties")));
scanForComponents();
Expand Down
Loading

0 comments on commit 88ec627

Please sign in to comment.