From 0942c9936cf8049cc0a65a38abd50b10b6f41565 Mon Sep 17 00:00:00 2001 From: rdkartono Date: Thu, 14 Nov 2024 10:23:50 +0700 Subject: [PATCH] GPIO control. Bug fix. --- .idea/jsLibraryMappings.xml | 6 - Html/html/index.html | 13 +- Html/html/index.js | 37 +++-- pom.xml | 26 ++-- src/main/java/Camera/PanTiltController.java | 47 ++++-- src/main/java/Camera/RtspGrabber.java | 4 +- src/main/java/Camera/VapixProtocol.java | 4 +- src/main/java/Other/SomeCodes.java | 3 + src/main/java/SBC/GPIO.java | 67 ++++----- src/main/java/SBC/RaspberryPi5BPins.java | 42 ++++++ src/main/java/Web/WebServer.java | 44 ++++-- src/main/java/id/co/gtc/Main.java | 151 ++++++++++++++++++-- 12 files changed, 340 insertions(+), 104 deletions(-) delete mode 100644 .idea/jsLibraryMappings.xml create mode 100644 src/main/java/SBC/RaspberryPi5BPins.java diff --git a/.idea/jsLibraryMappings.xml b/.idea/jsLibraryMappings.xml deleted file mode 100644 index 04d44cb..0000000 --- a/.idea/jsLibraryMappings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Html/html/index.html b/Html/html/index.html index 09ddc22..834bd2a 100644 --- a/Html/html/index.html +++ b/Html/html/index.html @@ -55,7 +55,6 @@

No Status

-
@@ -76,6 +75,18 @@

0 %

+
+
+ +

0 B/s

+
+
+
+
+ +

0 B/s

+
+
diff --git a/Html/html/index.js b/Html/html/index.js index 9719dd3..4a4ea2e 100644 --- a/Html/html/index.js +++ b/Html/html/index.js @@ -76,9 +76,9 @@ function initialize_socketio(){ console.log("Socket.io Connection error "+error); }); + socketio.on("message",(data)=>{ let dx = JSON.parse(data); - //console.log("Received data from server: "+data); process_command(dx); }); } @@ -291,7 +291,6 @@ function send_get_max_zoom(){ } else if (socketio){ if (socketio.connected){ socketio.emit("message",cmd); - console.log("get_max_zoom sent using socketio") } else console.log("Socket.io is not connected"); } @@ -552,7 +551,6 @@ function set_volumeoutput(value){ function set_pan_speed(value){ pan_speed = value; - console.log("set_pan_speed "+value); clearpan(); let classvalue = "btn btn-dark btn-sm"; switch(pan_speed){ @@ -574,7 +572,6 @@ function set_pan_speed(value){ function set_tilt_speed(value){ tilt_speed = value; - console.log("set_tilt_speed "+value); cleartilt(); let classvalue = "btn btn-dark btn-sm"; switch (tilt_speed){ @@ -631,10 +628,10 @@ function process_command(dx){ } break; case "SET VOLUME": - console.log("Set Volume: "+dx.data); + //console.log("Set Volume: "+dx.data); break; case "GET VOLUME": - console.log("Get Volume: "+dx.data); + //console.log("Get Volume: "+dx.data); $('#customRange').val(dx.data); break; case "GET MAX ZOOM": @@ -646,46 +643,46 @@ function process_command(dx){ $zoom.val(dx.data); break; case "GET RESOLUTION": - console.log("Get Resolution: "+dx.data); + //console.log("Get Resolution: "+dx.data); break; case "PAN LEFT": - console.log("Pan Left"); + //console.log("Pan Left"); break; case "PAN RIGHT": - console.log("Pan Right"); + //console.log("Pan Right"); break; case "TILT UP": - console.log("Tilt Up"); + //console.log("Tilt Up"); break; case "TILT DOWN": - console.log("Tilt Down"); + //console.log("Tilt Down"); break; case "STOP MOVEMENT": - console.log("Stop Movement"); + //console.log("Stop Movement"); break; case "SET ZOOM": - console.log("Set Zoom: "+dx.data); + //console.log("Set Zoom: "+dx.data); break; case "PLAY AUDIO": - console.log("Play Audio: "+dx.data); + //console.log("Play Audio: "+dx.data); if (dx.data.startsWith("Failed")){ alert(dx.data); } else $('#status_player').html("Playing Audio "+dx.data); break; case "STOP AUDIO": - console.log("Stop Audio"); + //console.log("Stop Audio"); document.getElementById("status_player").innerHTML = "Stop Playback"; break; case 'SET VIDEO QUALITY': - console.log("Set Video Quality: "+dx.data); + //console.log("Set Video Quality: "+dx.data); break; case "MUTE": - console.log("Mute"); + //console.log("Mute"); $mute.prop("className", "btn-mute hide"); $unmute.prop("className", "btn-mute show"); break; case "UNMUTE": - console.log("Unmute"); + //console.log("Unmute"); $mute.prop("className", "btn-mute show"); $unmute.prop("className", "btn-mute hide"); break; @@ -698,9 +695,11 @@ function process_command(dx){ if (systeminfo.cpu_temperature && systeminfo.cpu_temperature.length>0) $('#cpu_temperature').html(`${systeminfo.cpu_temperature} °C`); if (systeminfo.cpu && systeminfo.cpu.length>0) $('#cpu_usage').html(`${systeminfo.cpu} %`); if (systeminfo.ram_usage && systeminfo.ram_usage.length>0) $('#ram_usage').html(`${systeminfo.ram_usage} %`); + if (systeminfo.end0_TX && systeminfo.end0_TX.length>0) $('#ethernet_TX').html(systeminfo.end0_TX); + if (systeminfo.end0_RX && systeminfo.end0_RX.length>0) $('#ethernet_RX').html(systeminfo.end0_RX); break; case "GET AUDIOFILES": - console.log("Get Audio Files: "+dx.data); + //console.log("Get Audio Files: "+dx.data); let audiofiles = JSON.parse(dx.data); if (audiofiles.preset1 && audiofiles.preset1.length>0 && audiofiles.preset1!== "null") { files[0] = audiofiles.preset1; diff --git a/pom.xml b/pom.xml index 5eb114f..db1e03e 100644 --- a/pom.xml +++ b/pom.xml @@ -7,6 +7,13 @@ id.co.gtc BirdStrikeSoetta 1.0-SNAPSHOT + + + Maven Repository + mvnrepository + https://mvnrepository.com/ + + @@ -111,6 +118,14 @@ org.bytedeco ffmpeg-platform + + org.bytedeco + flandmark + + + org.bytedeco + flandmark-platform + @@ -189,20 +204,11 @@ linux-x86_64 --> - - com.corundumstudio.socketio netty-socketio - 2.0.11 + 2.0.12 - - - org.slf4j - slf4j-simple - 2.0.16 - - io.javalin javalin diff --git a/src/main/java/Camera/PanTiltController.java b/src/main/java/Camera/PanTiltController.java index bfae9c5..a286155 100644 --- a/src/main/java/Camera/PanTiltController.java +++ b/src/main/java/Camera/PanTiltController.java @@ -1,6 +1,7 @@ package Camera; import com.fazecast.jSerialComm.SerialPort; +import id.co.gtc.Main; import org.tinylog.Logger; @@ -40,9 +41,6 @@ public class PanTiltController { } else Logger.info("Serial Port {} not found", portname); } else Logger.info("No Serial Port found"); - - - } /** @@ -53,6 +51,14 @@ public class PanTiltController { Logger.info("Serial Port closed"); } + /** + * Check if Serial Port is open + * @return true if open, false otherwise + */ + public boolean isOpen(){ + return serialPort != null && serialPort.isOpen(); + } + /** * Stop Pan Tilt Movement */ @@ -60,7 +66,12 @@ public class PanTiltController { byte[] command = new byte[]{0, cameraid, 0, 0, 0, 0, 0}; command[6] = Checksum(command); // add checksum command[0] = (byte) 0xFF; // add synchronization byte - if (serialPort!=null && serialPort.isOpen()) serialPort.writeBytes(command, command.length); + if (isOpen()) { + Main.Max485Direction(true); + serialPort.writeBytes(command, command.length); + Main.Max485Direction(false); + Main.Blink_LedPanTilt(); + } } /** @@ -74,7 +85,12 @@ public class PanTiltController { byte[] command = new byte[]{0, cameraid, 0, 4, speed, 0, 0}; command[6] = Checksum(command); // add checksum command[0] = (byte) 0xFF; // add synchronization byte - if (serialPort!=null && serialPort.isOpen()) serialPort.writeBytes(command, command.length); + if (isOpen()) { + Main.Max485Direction(true); + serialPort.writeBytes(command, command.length); + Main.Max485Direction(false); + Main.Blink_LedPanTilt(); + } } /** @@ -88,7 +104,12 @@ public class PanTiltController { byte[] command = new byte[]{0, cameraid, 0, 2, speed, 0, 0}; command[6] = Checksum(command); // add checksum command[0] = (byte) 0xFF; // add synchronization byte - if (serialPort!=null && serialPort.isOpen()) serialPort.writeBytes(command, command.length); + if (isOpen()) { + Main.Max485Direction(true); + serialPort.writeBytes(command, command.length); + Main.Max485Direction(false); + Main.Blink_LedPanTilt(); + } } /** @@ -102,7 +123,12 @@ public class PanTiltController { byte[] command = new byte[]{0, cameraid, 0, 8, speed, 0, 0}; command[6] = Checksum(command); // add checksum command[0] = (byte) 0xFF; // add synchronization byte - if (serialPort!=null && serialPort.isOpen()) serialPort.writeBytes(command, command.length); + if (isOpen()) { + Main.Max485Direction(true); + serialPort.writeBytes(command, command.length); + Main.Max485Direction(false); + Main.Blink_LedPanTilt(); + } } /** @@ -116,7 +142,12 @@ public class PanTiltController { byte[] command = new byte[]{0, cameraid, 0, 16, speed, 0, 0}; command[6] = Checksum(command); // add checksum command[0] = (byte) 0xFF; // add synchronization byte - if (serialPort!=null && serialPort.isOpen()) serialPort.writeBytes(command, command.length); + if (isOpen()) { + Main.Max485Direction(true); + serialPort.writeBytes(command, command.length); + Main.Max485Direction(false); + Main.Blink_LedPanTilt(); + } } /** diff --git a/src/main/java/Camera/RtspGrabber.java b/src/main/java/Camera/RtspGrabber.java index c5d62b4..0f25b26 100644 --- a/src/main/java/Camera/RtspGrabber.java +++ b/src/main/java/Camera/RtspGrabber.java @@ -111,11 +111,11 @@ public class RtspGrabber { } public String LQStreamingStatus(){ - return gson.toJson(new String[]{String.valueOf(LQWidth), String.valueOf(LQHeight) , String.valueOf(grabbingTask.getCaptureFPS())}); + return gson.toJson(new String[]{String.valueOf(LQWidth), String.valueOf(LQHeight) , String.valueOf(grabbingTask!=null ? grabbingTask.getCaptureFPS():"0")}); } public String HQStreamingStatus(){ - return gson.toJson(new String[]{String.valueOf(HQWidth), String.valueOf(HQHeight) , String.valueOf(grabbingTask.getCaptureFPS())}); + return gson.toJson(new String[]{String.valueOf(HQWidth), String.valueOf(HQHeight) , String.valueOf(grabbingTask!=null ? grabbingTask.getCaptureFPS(): "0")}); } @NotNull diff --git a/src/main/java/Camera/VapixProtocol.java b/src/main/java/Camera/VapixProtocol.java index 1aea7e3..d86dd0b 100644 --- a/src/main/java/Camera/VapixProtocol.java +++ b/src/main/java/Camera/VapixProtocol.java @@ -1,5 +1,6 @@ package Camera; +import lombok.Getter; import org.tinylog.Logger; import java.net.http.HttpClient; @@ -27,7 +28,7 @@ public class VapixProtocol { private String[] ImageResolutions = new String[0]; private String[] ImageFormats = new String[0]; private int CurrentZoomValue = 0; - + @Getter private boolean successQuery = false; /** * Create a new VapixProtocol object * @param ip The IP address of the camera @@ -83,6 +84,7 @@ public class VapixProtocol { if (ValidString(value)) ImageFormats = value.split(","); //Logger.info("Format: "+value); } + successQuery = true; } } diff --git a/src/main/java/Other/SomeCodes.java b/src/main/java/Other/SomeCodes.java index cb573fd..f778ab6 100644 --- a/src/main/java/Other/SomeCodes.java +++ b/src/main/java/Other/SomeCodes.java @@ -44,6 +44,9 @@ public class SomeCodes { public static boolean useOpenCL; private static final Base64.Encoder base64encoder = java.util.Base64.getEncoder(); public static final Gson gson = new Gson(); + public static final double KB_threshold = 1024.0; + public static final double MB_threshold = 1024.0 * 1024.0; + public static final double GB_threshold = 1024.0 * 1024.0 * 1024.0; public static String[] GetAudioFiles(){ try{ diff --git a/src/main/java/SBC/GPIO.java b/src/main/java/SBC/GPIO.java index 1deadd4..62c8b7f 100644 --- a/src/main/java/SBC/GPIO.java +++ b/src/main/java/SBC/GPIO.java @@ -1,6 +1,7 @@ package SBC; import com.sun.jna.Platform; +import org.jetbrains.annotations.NotNull; import org.tinylog.Logger; import java.nio.file.Files; @@ -8,7 +9,7 @@ import java.nio.file.Path; public class GPIO { - private static final Path gpioPath = Path.of("/sys/class/gpio"); + public static final Path gpioPath = Path.of("/sys/class/gpio"); private static final Path gpioExportPath = Path.of("/sys/class/gpio/export"); private static final Path gpioUnexportPath = Path.of("/sys/class/gpio/unexport"); @@ -31,8 +32,8 @@ public class GPIO { * @param pin GPIO pin number * @return true if the pin is already exported */ - public static boolean GpioPinExists(int pin){ - Path pinPath = gpioPath.resolve("gpio"+pin); + public static boolean GpioPinExists(@NotNull RaspberryPi5BPins pin){ + Path pinPath = gpioPath.resolve("gpio"+pin.gpionumber); return pinPath.toFile().isDirectory(); } @@ -41,15 +42,15 @@ public class GPIO { * @param pin GPIO pin number * @return true if the pin is successfully exported */ - public static boolean ExportPin(int pin){ + public static boolean ExportPin(@NotNull RaspberryPi5BPins pin){ try{ if (Files.isWritable(gpioExportPath)){ - Files.write(gpioExportPath, String.valueOf(pin).getBytes()); - Logger.info("Pin "+pin+" exported"); + Files.write(gpioExportPath, String.valueOf(pin.gpionumber).getBytes()); + Logger.info("Pin "+pin.pin+" exported"); return GpioPinExists(pin); } else Logger.error("GPIO export path is not writable"); } catch (Exception e){ - Logger.error("Failed to export pin: "+pin+", Message: "+e.getMessage()); + Logger.error("Failed to export pin: "+pin.pin+", Message: "+e.getMessage()); } return false; } @@ -59,14 +60,14 @@ public class GPIO { * @param pin GPIO pin number * @return true if the pin is successfully unexported */ - public static boolean UnexportPin(int pin){ + public static boolean UnexportPin(@NotNull RaspberryPi5BPins pin){ if (Files.isWritable(gpioUnexportPath)){ try{ - Files.write(gpioUnexportPath, String.valueOf(pin).getBytes()); - Logger.info("Pin "+pin+" unexported"); + Files.write(gpioUnexportPath, String.valueOf(pin.gpionumber).getBytes()); + Logger.info("Pin "+pin.pin+" unexported"); return true; } catch (Exception e){ - Logger.error("Failed to unexport pin: "+pin+", Message: "+e.getMessage()); + Logger.error("Failed to unexport pin: "+pin.pin+", Message: "+e.getMessage()); } } else Logger.error("GPIO unexport path is not writable"); @@ -78,17 +79,17 @@ public class GPIO { * @param pin GPIO pin number * @return "in" if the pin is input, "out" if the pin is output, "unknown" if the direction is unknown */ - public static String GetPinDirection(int pin){ - Path pinPath = gpioPath.resolve("gpio"+pin).resolve("direction"); + public static String GetPinDirection(@NotNull RaspberryPi5BPins pin){ + Path pinPath = gpioPath.resolve("gpio"+pin.gpionumber).resolve("direction"); if (pinPath.toFile().isFile()){ if (Files.isReadable(pinPath)){ try{ return Files.readString(pinPath).trim(); } catch (Exception e){ - Logger.error("Failed to read pin direction: "+pin+", Message: "+e.getMessage()); + Logger.error("Failed to read pin direction: "+pin.pin+", Message: "+e.getMessage()); } - } else Logger.error("Pin direction file is not readable: "+pin); - } else Logger.error("Pin direction file not found: "+pin); + } else Logger.error("Pin direction file is not readable: "+pin.pin); + } else Logger.error("Pin direction file not found: "+pin.pin); return "unknown"; } @@ -98,22 +99,22 @@ public class GPIO { * @param direction "in" for input, "out" for output * @return true if the direction is successfully set */ - public static boolean SetPinDirection(int pin, String direction){ - Path pinPath = gpioPath.resolve("gpio"+pin).resolve("direction"); + public static boolean SetPinDirection(@NotNull RaspberryPi5BPins pin, String direction){ + Path pinPath = gpioPath.resolve("gpio"+pin.gpionumber).resolve("direction"); if (pinPath.toFile().isFile()){ if (Files.isWritable(pinPath)){ direction = direction.trim().toLowerCase(); if ("in".equals(direction) || "out".equals(direction)){ try{ Files.write(pinPath, direction.getBytes()); - Logger.info("Pin "+pin+" direction set to "+direction); + Logger.info("Pin "+pin.pin+" direction set to "+direction); return true; } catch (Exception e){ - Logger.error("Failed to set pin direction: "+pin+", Message: "+e.getMessage()); + Logger.error("Failed to set pin direction: "+pin.pin+", Message: "+e.getMessage()); } } else Logger.error("Invalid direction: "+direction); - } else Logger.error("Pin direction file is not writable: "+pin); - } else Logger.error("Pin direction file not found: "+pin); + } else Logger.error("Pin direction file is not writable: "+pin.pin); + } else Logger.error("Pin direction file not found: "+pin.pin); return false; } @@ -123,19 +124,19 @@ public class GPIO { * @param isON true to set the pin value to 1, false to set the pin value to 0 * @return true if the value is successfully set */ - public static boolean SetValue(int pin, boolean isON){ - Path pinPath = gpioPath.resolve("gpio"+pin).resolve("value"); + public static boolean SetValue(@NotNull RaspberryPi5BPins pin, boolean isON){ + Path pinPath = gpioPath.resolve("gpio"+pin.gpionumber).resolve("value"); if (pinPath.toFile().isFile()){ if (Files.isWritable(pinPath)){ try{ Files.write(pinPath, isON?"1".getBytes():"0".getBytes()); - Logger.info("Pin "+pin+" value set to "+(isON?"1":"0")); + Logger.info("Pin "+pin.pin+" value set to "+(isON?"1":"0")); return true; } catch (Exception e){ - Logger.error("Failed to set pin value: "+pin+", Message: "+e.getMessage()); + Logger.error("Failed to set pin value: "+pin.pin+", Message: "+e.getMessage()); } - } else Logger.error("Pin value file is not writable: "+pin); - } else Logger.error("Pin value file not found: "+pin); + } else Logger.error("Pin value file is not writable: "+pin.pin); + } else Logger.error("Pin value file not found: "+pin.pin); return false; } @@ -144,17 +145,17 @@ public class GPIO { * @param pin GPIO pin number * @return "1" if the pin value is 1, "0" if the pin value is 0, "unknown" if the value is unknown */ - public static String GetValue(int pin){ - Path pinPath = gpioPath.resolve("gpio"+pin).resolve("value"); + public static String GetValue(@NotNull RaspberryPi5BPins pin){ + Path pinPath = gpioPath.resolve("gpio"+pin.gpionumber).resolve("value"); if (pinPath.toFile().isFile()){ if (Files.isReadable(pinPath)){ try{ return Files.readString(pinPath).trim(); } catch (Exception e){ - Logger.error("Failed to read pin value: "+pin+", Message: "+e.getMessage()); + Logger.error("Failed to read pin value: "+pin.pin+", Message: "+e.getMessage()); } - } else Logger.error("Pin value file is not readable: "+pin); - } else Logger.error("Pin value file not found: "+pin); + } else Logger.error("Pin value file is not readable: "+pin.pin); + } else Logger.error("Pin value file not found: "+pin.pin); return "unknown"; } } diff --git a/src/main/java/SBC/RaspberryPi5BPins.java b/src/main/java/SBC/RaspberryPi5BPins.java new file mode 100644 index 0000000..3400379 --- /dev/null +++ b/src/main/java/SBC/RaspberryPi5BPins.java @@ -0,0 +1,42 @@ +package SBC; + +public enum RaspberryPi5BPins { + Pin03(3,"GPIO2/SDA", 573), + Pin05(5,"GPIO3/SCL", 574), + Pin07(7,"GPIO4/GPCLK0", 575), + Pin08(8,"GPIO14/TXD", 585), + Pin10(10,"GPIO15/RXD", 586), + Pin11(11,"GPIO17", 588), + Pin12(12,"GPIO18/PCMCLK", 589), + Pin13(13,"GPIO27", 598), + Pin15(15,"GPIO22", 593), + Pin16(16,"GPIO23", 594), + Pin18(18,"GPIO24", 595), + Pin19(19,"GPIO10/MOSI", 581), + Pin21(21,"GPIO9/MISO", 580), + Pin22(22,"GPIO25", 596), + Pin23(23,"GPIO11/SCLK", 582), + Pin24(24,"GPIO8/CE0", 579), + Pin26(26,"GPIO7/CE1", 578), + Pin27(27,"GPIO0/IDSD", 587), + Pin28(28,"GPIO1/IDSC", 587), + Pin29(29,"GPIO5", 576), + Pin31(31,"GPIO6", 577), + Pin32(32,"GPIO12/PWM0", 583), + Pin33(33,"GPIO13/PWM1", 584), + Pin35(35,"GPIO19/PCMFS", 590), + Pin36(36,"GPIO16", 587), + Pin37(37,"GPIO26", 597), + Pin38(38,"GPIO20/PCMDIN", 591), + Pin40(40,"GPIO21/PCMDOUT", 592); + + public final int pin; + public final String name; + public final int gpionumber; + + RaspberryPi5BPins(int pin, String name, int gpionumber){ + this.pin = pin; + this.name = name; + this.gpionumber = gpionumber; + } +} diff --git a/src/main/java/Web/WebServer.java b/src/main/java/Web/WebServer.java index e738663..f4f9306 100644 --- a/src/main/java/Web/WebServer.java +++ b/src/main/java/Web/WebServer.java @@ -2,7 +2,10 @@ package Web; import Other.SomeCodes; import com.corundumstudio.socketio.Configuration; +import com.corundumstudio.socketio.SocketIOClient; import com.corundumstudio.socketio.SocketIOServer; +import com.corundumstudio.socketio.listener.ConnectListener; +import com.corundumstudio.socketio.listener.DisconnectListener; import io.javalin.Javalin; import io.javalin.http.UploadedFile; import io.javalin.util.FileUtil; @@ -12,10 +15,7 @@ import lombok.Getter; import lombok.Setter; import org.tinylog.Logger; -import java.util.List; -import java.util.Objects; -import java.util.Properties; -import java.util.Set; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import static Other.SomeCodes.*; @@ -30,6 +30,8 @@ public class WebServer { private final Set connectedWebsocketClients = ConcurrentHashMap.newKeySet(); private final SocketIOServer socketServer; private final Configuration socketConfig; + private final HashMap socketIOClients = new HashMap<>(); + public WebServer(WebsocketEvent event, String webusername, String webpassword){ this.webusername = webusername; this.webpassword = webpassword; @@ -178,10 +180,13 @@ public class WebServer { ctx.redirect("/login.html"); }); + /* + WebSocket Communication + This is temporary, later must choose either WebSocket or SocketIO + */ app.ws("/ws", ws -> { ws.onConnect(connectws); ws.onClose(closews); - //ws.onError(errorws); ws.onMessage(ctx -> { try{ //Logger.info("WebSocket message {}", ctx.message()); @@ -200,16 +205,16 @@ public class WebServer { }); + /* + SocketIO Communication + This is temporary, later must choose either WebSocket or SocketIO + */ socketConfig = new Configuration(); socketConfig.setHostname("0.0.0.0"); socketConfig.setPort(3001); socketServer = new SocketIOServer(socketConfig); - socketServer.addConnectListener(client -> { - Logger.info("SocketIO connected, id {} from {}", client.getSessionId(), client.getRemoteAddress()); - }); - socketServer.addDisconnectListener(client -> { - Logger.info("SocketIO disconnected, id {} from {}", client.getSessionId(), client.getRemoteAddress()); - }); + socketServer.addConnectListener(connectListener); + socketServer.addDisconnectListener(disconnectListener); socketServer.addNamespace("/socketio").addEventListener("message", String.class, (client, data, ackSender) -> { //Logger.info("SocketIO message from namespace /socketio from {}: {}", client.getRemoteAddress(), data); WebsocketCommand command = gson.fromJson(data, WebsocketCommand.class); @@ -232,6 +237,7 @@ public class WebServer { .stream() .filter(ws -> ws.session.isOpen()) .forEach(ws -> ws.send(obj)); + socketIOClients.forEach((key, client) -> client.sendEvent("message", gson.toJson(obj))); } /** @@ -241,7 +247,6 @@ public class WebServer { */ public void Start(String localip, int port){ try{ - connectedWebsocketClients.forEach(WsContext::closeSession); app.start(localip, port); Logger.info("Web server started at {}:{}", localip, port); socketServer.start(); @@ -256,8 +261,12 @@ public class WebServer { */ public void Stop(){ try{ + connectedWebsocketClients.forEach(WsContext::closeSession); + connectedWebsocketClients.clear(); app.stop(); Logger.info("Web server stopped"); + socketIOClients.forEach((key, client) -> client.disconnect()); + socketIOClients.clear(); socketServer.stop(); Logger.info("SocketIO server stopped"); } catch (JavalinException e){ @@ -276,7 +285,16 @@ public class WebServer { connectedWebsocketClients.remove(ws); }; - //WsErrorHandler errorws = ws -> Logger.error("WebSocket error from {}, error {}", ws.host(), ws.error()); + ConnectListener connectListener = client -> { + Logger.info("SocketIO connected, id {} from {}", client.getSessionId(), client.getRemoteAddress()); + socketIOClients.put(client.getSessionId().toString(), client); + }; + + DisconnectListener disconnectListener = client -> { + Logger.info("SocketIO disconnected, id {} from {}", client.getSessionId(), client.getRemoteAddress()); + socketIOClients.remove(client.getSessionId().toString()); + }; + diff --git a/src/main/java/id/co/gtc/Main.java b/src/main/java/id/co/gtc/Main.java index f14ed88..909b13d 100644 --- a/src/main/java/id/co/gtc/Main.java +++ b/src/main/java/id/co/gtc/Main.java @@ -8,9 +8,7 @@ import Camera.PanTiltController; import Camera.RtspGrabber; import Camera.VapixProtocol; import Other.SomeCodes; -import SBC.ProcessorStatus; -import SBC.RamInformation; -import SBC.SystemInformation; +import SBC.*; import Web.*; import com.google.gson.JsonObject; import org.bytedeco.opencv.global.opencv_core; @@ -19,6 +17,8 @@ import org.tinylog.Logger; import java.io.File; import java.nio.file.Path; import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import static Other.SomeCodes.*; @@ -34,6 +34,16 @@ public class Main { private static RamInformation ramInformation; private static ProcessorStatus[] previousCpuInfo; private static final Map cpuUsage = new HashMap<>(); + private static NetworkTransmitReceiveInfo[] previousNetworkInfo; + private static final Map networkTX = new HashMap<>(); + private static final Map networkRX = new HashMap<>(); + private static RaspberryPi5BPins AmplifierPower = null; + private static RaspberryPi5BPins LedWeb = null; + private static RaspberryPi5BPins LedIpCamera = null; + private static RaspberryPi5BPins LedPanTilt = null; + private static RaspberryPi5BPins Max485Direction = null; + + private static ExecutorService gpioExecutor = null; // Application start from here public static void main(String[] args) { @@ -44,8 +54,10 @@ public class Main { if (panTiltController!=null) panTiltController.Close(); if (vapixProtocol!=null) vapixProtocol.Close(); if (timer!=null) timer.cancel(); + if (gpioExecutor!=null) gpioExecutor.shutdown(); })); + init_gpio(); init_system_monitoring(); init_properties(); @@ -58,6 +70,70 @@ public class Main { init_rtspgrabber(); } + private static void init_gpio(){ + if (GPIO.HaveGPIO()){ + gpioExecutor = Executors.newVirtualThreadPerTaskExecutor(); + AmplifierPower = InitializePin(RaspberryPi5BPins.Pin13, true); + LedWeb = InitializePin(RaspberryPi5BPins.Pin15, true); + LedIpCamera = InitializePin(RaspberryPi5BPins.Pin19, true); + LedPanTilt = InitializePin(RaspberryPi5BPins.Pin21, true); + Max485Direction = InitializePin(RaspberryPi5BPins.Pin23, true); + } + } + + + @SuppressWarnings("SameParameterValue") + private static RaspberryPi5BPins InitializePin(RaspberryPi5BPins pin, boolean isOutput){ + boolean exported = false; + if (GPIO.GpioPinExists(pin)){ + exported = true; + } else { + if (GPIO.ExportPin(pin)){ + exported = true; + } + } + + if (exported){ + if (GPIO.SetPinDirection(pin, isOutput?"out":"in")){ + return pin; + } + } + return null; + } + + // dipanggil di PanTiltController, maka perlu public dan static + public static void Max485Direction(boolean isTransmit){ + if (Max485Direction!=null){ + GPIO.SetValue(Max485Direction, isTransmit); + } + } + + // dipanggil di PanTiltController, maka perlu public dan static + public static void Blink_LedPanTilt(){ + Blink(LedPanTilt); + } + + private static void AmplifierControl(boolean isON){ + if (AmplifierPower!=null){ + GPIO.SetValue(AmplifierPower, isON); + } + } + + private static void Blink(RaspberryPi5BPins pin){ + if (pin!=null){ + if (gpioExecutor!=null){ + gpioExecutor.submit(()->{ + GPIO.SetValue(pin, true); + try { + Thread.sleep(20); + } catch (InterruptedException ignored) { + } + GPIO.SetValue(pin, false); + }); + } + } + } + private static void init_system_monitoring(){ TimerTask tt = new TimerTask() { @Override @@ -72,6 +148,41 @@ public class Main { for(int ii=0;ii0){ + if (previousNetworkInfo==null || !Objects.equals(previousNetworkInfo.length, ntri.length)){ + previousNetworkInfo = ntri; + } else { + for(int ii=0;iivapixProtocol.GetPTZMaxZoom()) zoom = vapixProtocol.GetPTZMaxZoom(); if (vapixProtocol.Zoom(1, zoom)){ + Blink(LedIpCamera); return new WebsocketReply("ZOOM", String.valueOf(zoom)); } else return new WebsocketReply("ZOOM", "Failed to zoom"); } else return new WebsocketReply("ZOOM", "Invalid Zoom Value"); @@ -326,6 +443,18 @@ public class Main { data.addProperty(key, String.valueOf(cpuUsage.get(key))); } } + if (!networkTX.isEmpty()) { + for(String key : networkTX.keySet()){ + if (key.equals("lo")) continue; + data.addProperty(key+"_TX", networkTX.get(key)); + } + } + if (!networkRX.isEmpty()) { + for(String key : networkRX.keySet()){ + if (key.equals("lo")) continue; + data.addProperty(key+"_RX", networkRX.get(key)); + } + } return new WebsocketReply("GET SYSTEM INFO", data.toString()); default: return new WebsocketReply("UNKNOWN COMMAND", command.command);