I have made an application that is able to read the weight from a Dymo S100 Scale. My issue is I have created the .jar on a Mac that is intel, however the deployment environment is going to be a mixture of x86 machines that are all Mac OS and a mixture of M1 arm chips. Now if I go with my IDE over to the arm and I build and package it there, it works (I presume because it grabs the correct dependency for the graphics) and that .jar will work for all the arm chips. However I want one .jar to be able to service both machines. I have seen in other posts people adding:
<classifier>mac</classifier>
I have tried this however it did not work... Here is the Main.java file (to be able to package a fat .jar)
package net.webment;
public class Main {
public static void main(String[] args) {
App.main(args);
}
}
Which calls App.java to start the program:
package net.webment;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
/**
* JavaFX App
*/
public class App extends Application {
private static int response = 1;
private static Scene scene;
@Override
public void start(Stage stage) throws IOException {
scene = new Scene(loadFXML("primary"));
stage.setScene(scene);
stage.show();
}
static void setRoot(String fxml) throws IOException {
scene.setRoot(loadFXML(fxml));
}
private static Parent loadFXML(String fxml) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource(fxml + ".fxml"));
return fxmlLoader.load();
}
public static void main(String[] args) {
launch();
}
}
which calls PrimaryController.java to open:
package net.webment;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ResourceBundle;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class PrimaryController implements Initializable {
private static int response = 1;
private static int stable = 0;
private static double weight = 0;
@FXML
private Button btn_Confirm;
@FXML
private Label current_Weight_Display;
@FXML
private Label current_Weight_Lbl;
@FXML
private Label pounds_Lbl;
@FXML
private HBox weight_Background;
public static void setStable(int value) {
stable = value;
}
public void get_Weight() {
new Thread(() -> {
try {
UsbScale scale = UsbScale.findScale();
assert scale != null;
scale.open();
while (response != 0) {
weight = scale.syncSubmit();
weight = (double) Math.round(weight * 10d) / 10d;
double finalWeight = weight;
Platform.runLater(() -> {
if (stable == -1) {
weight_Background.setStyle("-fx-background-color: red");
current_Weight_Display.setTextFill(Color.WHITE);
pounds_Lbl.setTextFill(Color.WHITE);
} else if (stable == 0) {
weight_Background.setStyle("-fx-background-color: white");
current_Weight_Display.setTextFill(Color.BLACK);
pounds_Lbl.setTextFill(Color.BLACK);
} else if (stable == 1) {
weight_Background.setStyle("-fx-background-color: green");
current_Weight_Display.setTextFill(Color.WHITE);
pounds_Lbl.setTextFill(Color.WHITE);
}
current_Weight_Display.setText(String.valueOf(finalWeight));
});
//here we want to be updating live a variable on the screen.
}
} catch (NullPointerException e) {
create_Error("NullPointerException! " + e.getMessage());
}
}).start();
}
@FXML
void on_Action_Confirm(ActionEvent event) {
if (stable == 1) {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "Are You Sure the Package Weighs: " + weight + " Lbs?", ButtonType.YES, ButtonType.NO);
alert.showAndWait();
if (alert.getResult() == ButtonType.YES) {
create_File(String.valueOf(weight));
Stage stage = (Stage) btn_Confirm.getScene().getWindow();
// do what you have to do
stage.close();
System.exit(0);
}
} else {
Alert error = new Alert(Alert.AlertType.ERROR, "You Cannot Confirm the Weight While until the Background is Green!");
error.showAndWait();
}
}
public static void create_File(String Weight) {
//Use Paths.get in older JVM's
Path f = Path.of(System.getProperty("user.home"), "Documents", "Weight.txt");
try {
Files.writeString(f, Weight, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
String written = Files.readString(f);
if (written.equals(Weight)) {
System.exit(0);
}
} catch (IOException e) {
e.printStackTrace();
System.exit(0);
}
}
public static void create_Error(String error) {
//Use Paths.get in older JVM's
Path f = Path.of(System.getProperty("user.home"), "Documents", "Error.txt");
try {
Files.writeString(f, error, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
String written = Files.readString(f);
if (written.equals(error)) {
System.exit(0);
}
} catch (IOException e) {
e.printStackTrace();
System.exit(0);
}
}
@Override
public void initialize(URL location, ResourceBundle resources) {
get_Weight();
}
}
which calls the UsbScale.java Class:
package net.webment;
import org.usb4java.*;
import javax.usb.*;
import javax.usb.event.UsbPipeDataEvent;
import javax.usb.event.UsbPipeErrorEvent;
import javax.usb.event.UsbPipeListener;
import java.util.List;
public class UsbScale implements UsbPipeListener, AutoCloseable {
private boolean isOpened = false;
private final UsbDevice device;
private UsbInterface iface;
private UsbPipe pipe;
private final byte[] data = new byte[6];
private double finalWeight;
private Context context;
public boolean isOpened() {
return isOpened;
}
private UsbScale(UsbDevice device) {
this.device = device;
}
public static UsbScale findScale() {
UsbServices services;
UsbHub rootHub = null;
try {
services = UsbHostManager.getUsbServices();
rootHub = services.getRootUsbHub();
} catch (UsbException e) {
PrimaryController.create_Error("Error! " + e.getMessage());
}
// Dymo S100 Scale:
assert rootHub != null;
UsbDevice device = findDevice(rootHub, (short) 0x0922, (short) 0x8009);
// Dymo M25 Scale:
//if (device == null) {
// device = findDevice(rootHub, (short) 0x0922, (short) 0x8004);
//}
if (device == null) {
return null;
}
return new UsbScale(device);
}
private static UsbDevice findDevice(UsbHub hub, short vendorId, short productId) {
for (UsbDevice device : (List<UsbDevice>) hub.getAttachedUsbDevices()) {
UsbDeviceDescriptor desc = device.getUsbDeviceDescriptor();
if (desc.idVendor() == vendorId && desc.idProduct() == productId) {
return device;
}
if (device.isUsbHub()) {
device = findDevice((UsbHub) device, vendorId, productId);
if (device != null) {
return device;
}
}
}
return null;
}
public Device findDevice(short vendorId, short productId)
{
int result = LibUsb.init(context);
if (result != LibUsb.SUCCESS) throw new LibUsbException("Unable to initialize libusb.", result);
// Read the USB device list
DeviceList list = new DeviceList();
result = LibUsb.getDeviceList(context, list);
if (result < 0) throw new LibUsbException("Unable to get device list", result);
try
{
// Iterate over all devices and scan for the right one
for (Device device: list)
{
DeviceDescriptor descriptor = new DeviceDescriptor();
result = LibUsb.getDeviceDescriptor(device, descriptor);
if (result != LibUsb.SUCCESS) throw new LibUsbException("Unable to read device descriptor", result);
if (descriptor.idVendor() == vendorId && descriptor.idProduct() == productId) {
return device;
}
}
}
finally
{
// Ensure the allocated device list is freed
LibUsb.freeDeviceList(list, true);
}
// Device not found
return null;
}
public void open() {
try {
context = new Context();
UsbConfiguration configuration = device.getActiveUsbConfiguration();
iface = configuration.getUsbInterface((byte) 0);
// this allows us to steal the lock from the kernel
DeviceHandle handle = new DeviceHandle();
int result = LibUsb.open( findDevice((short) 0x0922, (short) 0x8009), handle);
if (result != LibUsb.SUCCESS) throw new LibUsbException("Unable to open USB device", result);
result = LibUsb.setConfiguration(handle, 0);
if (result != LibUsb.SUCCESS) throw new LibUsbException("Unable to set Configuration", result);
iface.claim(usbInterface -> true);
final List<UsbEndpoint> endpoints = iface.getUsbEndpoints();
pipe = endpoints.get(0).getUsbPipe(); // there is only 1 endpoint
pipe.addUsbPipeListener(this);
pipe.open();
isOpened = true;
}catch (UsbException e) {
PrimaryController.create_Error("Error! "+ e.getMessage());
}
}
public void close() {
if (!isOpened) return;
try {
pipe.close();
iface.release();
LibUsb.exit(context);
} catch (UsbException e) {
PrimaryController.create_Error("Error! "+ e.getMessage());
}
}
public double syncSubmit() {
try {
pipe.syncSubmit(data);
} catch (UsbDisconnectedException disconnectedException) {
PrimaryController.create_Error("UsbDisconnectedException! "+ disconnectedException.getMessage());
} catch (UsbException e) {
PrimaryController.create_Error("Error! "+ e.getMessage());
}
return finalWeight;
}
@Override
public void dataEventOccurred(UsbPipeDataEvent upde) {
//System.out.println(data[1] + ", " + data[2] + ", " + data[3] + ", " + data[4] + ", " + data[5]);
//if data[1] == 4 value is stable, if data[1] == 3 value is unstable, if data[1] == 5 value is negative, if data[1] == 2 value is 0
if (data[2] == 12) { //This means it is in imperial Mode
if (data[1] == 4) {
PrimaryController.setStable(1);
int weight = (data[4] & 0xFF) + (data[5] << 8);
int scalingFactor = data[3];
finalWeight = scaleWeight(weight, scalingFactor); //final weight, applies to both metric and imperial
} else if (data[1]== 3) {
PrimaryController.setStable(0);
int weight = (data[4] & 0xFF) + (data[5] << 8);
int scalingFactor = data[3];
finalWeight = scaleWeight(weight, scalingFactor); //final weight, applies to both metric and imperial
} else if (data[1] == 5) {
PrimaryController.setStable(-1);
int weight = (data[4] & 0xFF) + (data[5] << 8);
int scalingFactor = data[3];
finalWeight = scaleWeight(weight, scalingFactor) * (-1); //final weight, applies to both metric and imperial
} else if (data[1] == 2) {
finalWeight = 0;
}
} else { //This would mean it is in metric
if (data[1] == 4) {
PrimaryController.setStable(1);
int weight = (data[4] & 0xFF) + (data[5] << 8);
int scalingFactor = data[3];
finalWeight = scaleWeight(weight, scalingFactor); //final weight, applies to both metric and imperial
}else if (data[1]== 3) {
PrimaryController.setStable(0);
int weight = (data[4] & 0xFF) + (data[5] << 8);
int scalingFactor = data[3];
finalWeight = (scaleWeight(weight, scalingFactor) * 2.20462); //final weight, applies to both metric and imperial
} else if (data[1] == 5) {
PrimaryController.setStable(-1);
int weight = (data[4] & 0xFF) + (data[5] << 8);
int scalingFactor = data[3];
finalWeight = (scaleWeight(weight, scalingFactor) * 2.20462) * (-1); //final weight, applies to both metric and imperial
} else if (data[1] == 2) {
finalWeight = 0;
}
}
}
private double scaleWeight(int weight, int scalingFactor) {
return weight * Math.pow(10, scalingFactor);
}
@Override
public void errorEventOccurred(UsbPipeErrorEvent usbPipeErrorEvent) {
PrimaryController.create_Error("UsbPipeErrorEvent! " + usbPipeErrorEvent.toString());
}
}
and here is my pom.xml:
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.webment</groupId>
<artifactId>Interface</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.release>11</maven.compiler.release>
<javafx.version>18.0.2</javafx.version>
<javafx.plugin.version>0.0.6</javafx.plugin.version>
<main.class>net.webment.Main</main.class>
</properties>
<dependencies>
<dependency>
<groupId>org.usb4java</groupId>
<artifactId>usb4java-javax</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.github.dsheirer</groupId>
<artifactId>libusb4java-darwin-aarch64</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>18.0.2</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics</artifactId>
<version>18.0.2</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>18.0.2</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.2</version>
</dependency>
</dependencies>
<build>
<finalName>Dymo_S100</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>11</release>
</configuration>
</plugin>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.6</version>
<executions>
<execution>
<!-- Default configuration for running -->
<!-- Usage: mvn clean javafx:run -->
<id>default-cli</id>
<configuration>
<mainClass>net.webment.Main</mainClass>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.2</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>net.webment.Main</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>