Add more functions

This commit is contained in:
2024-11-13 08:35:32 +07:00
parent 1fe4716bab
commit f7f711d3fe
22 changed files with 1307 additions and 294 deletions

View File

@@ -1,14 +1,15 @@
package Audio;
import lombok.Getter;
import org.tinylog.Logger;
import java.io.File;
public class AudioPlayer {
Bass bass;
int deviceid = -1;
boolean inited = false;
private final Bass bass;
private int deviceid = -1;
@Getter private boolean inited = false;
int playbackhandle = 0;
float playbackvolume = 1.0f;
@@ -48,13 +49,49 @@ public class AudioPlayer {
}
}
/**
* Get BASS_DEVICEINFO for a device
* @param device device id
* @return BASS_DEVICEINFO object or null if failed
*/
public Bass.BASS_DEVICEINFO GetDeviceInfo(int device){
Bass.BASS_DEVICEINFO info = new Bass.BASS_DEVICEINFO();
if (bass.BASS_GetDeviceInfo(device, info)){
return info;
}
return null;
}
/**
* Find Device ID with Name
* @param name device name
* @return device id, or -1 if not found
*/
public int FindDeviceIDWithName(String name){
int result = -1;
int ii = 1;
while(true){
Bass.BASS_DEVICEINFO info = new Bass.BASS_DEVICEINFO();
if (bass.BASS_GetDeviceInfo(ii, info)){
if (info.name.contains(name)){
result = ii;
break;
}
ii++;
} else {
// gak ada lagi
break;
}
}
return result;
}
/**
* Open Output Device
* @param device device id, starts from 1
* @param freq output frequency
* @return true if success
*/
@SuppressWarnings("UnusedReturnValue")
public boolean OpenDevice(int device, int freq){
int flag = Bass.BASS_DEVICE_REINIT | Bass.BASS_DEVICE_16BITS | Bass.BASS_DEVICE_MONO | Bass.BASS_DEVICE_FREQ;
boolean success = bass.BASS_Init(device, freq, flag);
@@ -69,7 +106,8 @@ public class AudioPlayer {
}
/**
* Set Master Volume
* Set Master Output Volume
* Master Output Volume related to the system volume (Alsamixer on Linux, Volume Mixer on Windows)
* @param value volume value, 0-100
*/
public void setMasterVolume(int value){
@@ -84,7 +122,8 @@ public class AudioPlayer {
}
/**
* Get Master Volume
* Get Master Output Volume
* Master Output Volume related to the system volume (Alsamixer on Linux, Volume Mixer on Windows)
* @return 0 - 100, or -1 if failed
*/
@SuppressWarnings("unused")
@@ -99,6 +138,11 @@ public class AudioPlayer {
return -1;
}
/**
* Get Playback Volume
* is the volume of the currently playing audio file
* @return 0 - 100
*/
public int getPlaybackvolume(){
if (playbackvolume<0)
return 0;
@@ -108,6 +152,11 @@ public class AudioPlayer {
return (int)(playbackvolume*100);
}
/**
* Set Playback Volume
* is the volume of the currently playing audio file
* @param value 0 - 100
*/
public void setPlaybackvolume(int value){
if (value<0) value = 0;
if (value>100) value = 100;
@@ -119,11 +168,17 @@ public class AudioPlayer {
private float lastplaybackvolume = 1.0f;
/**
* Set Playback Volume to 0
*/
public void Mute(){
lastplaybackvolume = playbackvolume;
setPlaybackvolume(0);
}
/**
* Set Playback Volume to last volume before Mute
*/
public void Unmute(){
setPlaybackvolume((int)lastplaybackvolume*100);
}
@@ -164,6 +219,12 @@ public class AudioPlayer {
}
}
/**
* Play Audio File
* @param prop AudioFileProperties object to play
* @param looping set true to loop the audio file
* @param event PlaybackEvent object to handle playback event
*/
public void PlayAudioFile(AudioFileProperties prop, boolean looping, PlaybackEvent event){
if (inited){
if (prop!=null){

View File

@@ -9,6 +9,7 @@ import lombok.Getter;
import lombok.Setter;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber;
import org.tinylog.Logger;
public class GrabbingTask implements Runnable {
@Setter private Consumer<String> onMessageUpdate;
@@ -16,13 +17,13 @@ public class GrabbingTask implements Runnable {
@Setter private Consumer<Frame> onLQFrameUpdate;
@Setter private Consumer<String> onHQBase64Update;
@Setter private Consumer<String> onLQBase64Update;
@Getter private int CaptureFPS = 0;
private final AtomicBoolean isGrabbing;
private final FrameGrabber grabber;
@Getter private final int lowquality_width = 640;
@Getter private final int lowquality_height = 360;
private void updateMessage(String message) {
if (onMessageUpdate != null) {
onMessageUpdate.accept(message);
@@ -68,27 +69,44 @@ public class GrabbingTask implements Runnable {
}
public void Stop(){
isGrabbing.set(false);
}
private void grabprocess() throws Exception{
grabber.flush();
Frame fr =grabber.grab();
if (fr!=null){
updateHQFrame(fr);
updateHQBase64(SomeCodes.FrameToBase64(fr));
Frame resized = SomeCodes.ResizeFrame(fr, lowquality_width, lowquality_height);
updateLQFrame(resized);
updateLQBase64(SomeCodes.FrameToBase64(resized));
} else updateMessage("Grabber returned null frame");
}
@Override
public void run() {
isGrabbing.set(true);
Logger.info("Grabbing Task started");
double fps = grabber.getFrameRate();
Logger.info("Grabber framerate = {}", fps);
long starttick = System.currentTimeMillis();
while (isGrabbing.get()) {
try {
Frame fr =grabber.grab();
if (fr!=null){
updateHQFrame(fr);
updateHQBase64(SomeCodes.FrameToBase64(fr));
Frame resized = SomeCodes.ResizeFrame(fr, lowquality_width, lowquality_height);
updateLQFrame(resized);
updateLQBase64(SomeCodes.FrameToBase64(resized));
} else updateMessage("Grabber returned null frame");
} catch (Exception e) {
updateMessage("Error grabbing frame: " + e.getMessage());
long elapsed = System.currentTimeMillis() - starttick;
starttick = System.currentTimeMillis();
//Logger.info("Elapsed time = {} ms", elapsed);
if (elapsed>0) CaptureFPS = (int) (1000 / elapsed);
try{
Thread.yield();
grabprocess();
} catch (Exception e){
Logger.error("Error grabbing frame: "+e.getMessage());
}
}
Logger.info("Grabbing Task stopped");
}
}

View File

@@ -3,13 +3,14 @@ package Camera;
import com.fazecast.jSerialComm.SerialPort;
import org.tinylog.Logger;
/**
* Pan Tilt Controller
* Using PelcoD protocol
* Source : https://www.commfront.com/pages/pelco-d-protocol-tutorial
* Source : <a href="https://www.commfront.com/pages/pelco-d-protocol-tutorial">PelcoD Tutorial</a>
*/
public class PanTiltController {
private final SerialPort serialPort;
private SerialPort serialPort;
private final byte cameraid;
/**
* Open Pan Tilt Controller
@@ -17,21 +18,38 @@ public class PanTiltController {
* @param baudrate baudrate used
*/
public PanTiltController(String portname, int baudrate, int cameraid){
serialPort = SerialPort.getCommPort(portname);
serialPort.setBaudRate(baudrate);
this.cameraid = (byte)cameraid;
if (serialPort.openPort()){
Logger.info("Serial Port {} opened successfully at {}", portname, baudrate);
} else {
Logger.info("Failed to open Serial Port {} at {}", portname, baudrate);
}
SerialPort[] comports = SerialPort.getCommPorts();
if (comports.length>0){
for (SerialPort port : comports){
Logger.info("Available Serial Port : {}", port.getSystemPortName());
if (port.getSystemPortName().equals(portname)){
Logger.info("Serial Port {} found", portname);
serialPort = port;
break;
}
}
if (serialPort!=null){
serialPort.setBaudRate(baudrate);
if (serialPort.openPort()){
Logger.info("Serial Port {} opened successfully at {}", portname, baudrate);
} else {
Logger.info("Failed to open Serial Port {} at {}", portname, baudrate);
}
} else Logger.info("Serial Port {} not found", portname);
} else Logger.info("No Serial Port found");
}
/**
* Close Pan Tilt Controller
*/
public void Close(){
serialPort.closePort();
if (serialPort!=null) serialPort.closePort();
Logger.info("Serial Port closed");
}

View File

@@ -7,24 +7,59 @@ import org.tinylog.Logger;
import java.util.concurrent.atomic.AtomicBoolean;
import static Other.SomeCodes.gson;
public class RtspGrabber {
private final String rtspUrl;
private FFmpegFrameGrabber grabber;
private final AtomicBoolean isGrabbing = new AtomicBoolean(false);
private @Getter Frame lastHQFrame = null;
private @Getter Frame lastLQFrame = null;
private @Getter String lastHQBase64 = null;
private @Getter String lastLQBase64 = null;
private Frame lastHQFrame = null;
private Frame lastLQFrame = null;
private String lastHQBase64 = null;
private String lastLQBase64 = null;
private @Getter int HQWidth = 0;
private @Getter int HQHeight = 0;
private @Getter int LQWidth = 0;
private @Getter int LQHeight = 0;
private GrabbingTask grabbingTask;
public RtspGrabber(String ip, String path) {
this.rtspUrl = "rtsp://" + ip + path;
Logger.info("RtspGrabber created with url: " + rtspUrl);
}
private synchronized void setLastHQFrame(Frame frame){
lastHQFrame = frame;
}
public synchronized Frame getLastHQFrame(){
return lastHQFrame;
}
private synchronized void setLastLQFrame(Frame frame){
lastLQFrame = frame;
}
public synchronized Frame getLastLQFrame(){
return lastLQFrame;
}
private synchronized void setLastHQBase64(String base64){
lastHQBase64 = base64;
}
public synchronized String getLastHQBase64(){
return lastHQBase64;
}
private synchronized void setLastLQBase64(String base64){
lastLQBase64 = base64;
}
public synchronized String getLastLQBase64(){
return lastLQBase64;
}
/**
* Start grabbing frames from rtsp
* @param useTcp Use tcp instead of udp
@@ -34,11 +69,12 @@ public class RtspGrabber {
try{
grabber = FFmpegFrameGrabber.createDefault(rtspUrl);
if (useTcp) grabber.setOption("rtsp_transport", "tcp");
grabber.setTimeout(2000);
grabber.setImageWidth(width);
grabber.setImageHeight(height);
grabber.setPixelFormat(avutil.AV_PIX_FMT_BGR24);
grabber.start();
avutil.av_log_set_level(avutil.AV_LOG_ERROR);
@@ -49,7 +85,7 @@ public class RtspGrabber {
tt.setOnHQFrameUpdate(value -> {
if (value!=null){
if (value.imageWidth>0 && value.imageHeight>0){
lastHQFrame = value;
setLastHQFrame(value);
HQWidth = value.imageWidth;
HQHeight = value.imageHeight;
}
@@ -59,14 +95,15 @@ public class RtspGrabber {
tt.setOnLQFrameUpdate(value -> {
if (value!=null){
if (value.imageWidth>0 && value.imageHeight>0){
lastLQFrame = value;
setLastLQFrame(value);
LQWidth = value.imageWidth;
LQHeight = value.imageHeight;
}
}
});
tt.setOnHQBase64Update(value -> lastHQBase64 = value);
tt.setOnLQBase64Update(value -> lastLQBase64 = value);
tt.setOnHQBase64Update(this::setLastHQBase64);
tt.setOnLQBase64Update(this::setLastLQBase64);
grabbingTask = tt;
new Thread(tt).start();
} catch (Exception e){
@@ -78,17 +115,28 @@ public class RtspGrabber {
* Stop grabbing frames
*/
public void Stop(){
isGrabbing.set(false);
if (grabbingTask!=null){
grabbingTask.Stop();
}
grabbingTask = null;
if (grabber!=null) {
try{
isGrabbing.set(false);
grabber.stop();
grabber.releaseUnsafe();
Logger.info("Grabber stopped");
} catch (Exception e){
Logger.error("Error stopping grabber: " + e.getMessage());
}
}
grabber = null;
}
public String LQStreamingStatus(){
return gson.toJson(new String[]{String.valueOf(LQWidth), String.valueOf(LQHeight) , String.valueOf(grabbingTask.getCaptureFPS())});
}
public String HQStreamingStatus(){
return gson.toJson(new String[]{String.valueOf(HQWidth), String.valueOf(HQHeight) , String.valueOf(grabbingTask.getCaptureFPS())});
}
}

View File

@@ -1,5 +1,6 @@
package Other;
import com.google.gson.Gson;
import org.bytedeco.javacpp.Loader;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
@@ -42,6 +43,7 @@ public class SomeCodes {
public static final boolean haveOpenCL = opencv_core.haveOpenCL();
public static boolean useOpenCL;
private static final Base64.Encoder base64encoder = java.util.Base64.getEncoder();
public static final Gson gson = new Gson();
public static String[] GetAudioFiles(){
try{
@@ -49,6 +51,7 @@ public class SomeCodes {
} catch (Exception e){
Logger.error("Error getting audio files: "+e.getMessage());
}
return new String[0];
}
@@ -199,10 +202,15 @@ public class SomeCodes {
}
/**
* Load properties file
* @param filename properties file name
* @return Properties object loaded, or empty new Properties object if error
*/
public static @NotNull Properties LoadProperties(String filename){
try{
InputStream is = new FileInputStream(filename);
File ff = new File(currentDirectory, filename);
InputStream is = new FileInputStream(ff);
Properties prop = new Properties();
prop.load(is);
return prop;
@@ -212,9 +220,16 @@ public class SomeCodes {
return new Properties();
}
/**
* Save properties file
* @param prop Properties object to save
* @param filename properties file name
* @return true if success, false otherwise
*/
public static boolean SaveProperties(Properties prop, String filename){
try{
OutputStream os = new FileOutputStream(filename);
File ff = new File(currentDirectory, filename);
OutputStream os = new FileOutputStream(ff);
prop.store(os, null);
return true;
} catch (Exception e){
@@ -236,6 +251,7 @@ public class SomeCodes {
UMat src = new UMat();
source.copyTo(src);
UMat dst = new UMat();
opencv_imgproc.resize(src, dst, sz);
dst.copyTo(dest);
} else {
@@ -249,4 +265,19 @@ public class SomeCodes {
Mat resized = ResizeMat(mat, width, height);
return matConverter.convert(resized);
}
/**
* check if an ip address is reachable
* @param ipaddress ip address to check
* @return true if valid and reachable, false otherwise
*/
public static boolean IpIsReachable(String ipaddress){
try{
InetAddress inet = InetAddress.getByName(ipaddress);
return inet.isReachable(1000);
} catch (Exception e){
Logger.error("Error checking ip address: "+e.getMessage());
}
return false;
}
}

View File

@@ -0,0 +1,8 @@
package SBC;
public class CpuInfo {
public int processorCount;
public String revision;
public String serial;
public String model;
}

View File

@@ -6,7 +6,6 @@ import org.tinylog.Logger;
import java.nio.file.Files;
import java.nio.file.Path;
@SuppressWarnings("unused")
public class GPIO {
private static final Path gpioPath = Path.of("/sys/class/gpio");
@@ -14,19 +13,15 @@ public class GPIO {
private static final Path gpioUnexportPath = Path.of("/sys/class/gpio/unexport");
public static boolean IsRaspberry64(){
public static boolean HaveGPIO(){
if (Platform.isLinux()){
if (Platform.isARM()){
if (Platform.is64Bit()){
if (gpioPath.toFile().isDirectory()){
if (gpioExportPath.toFile().isFile()){
if (gpioUnexportPath.toFile().isFile()){
return true;
} else Logger.error("GPIO unexport path is not found");
} else Logger.error("GPIO export path is not found");
} else Logger.error("GPIO path is not found");
} else Logger.info("Device is not 64 bit");
} else Logger.info("Device is not ARM");
if (gpioPath.toFile().isDirectory()){
if (gpioExportPath.toFile().isFile()){
if (gpioUnexportPath.toFile().isFile()){
return true;
} else Logger.error("GPIO unexport path is not found");
} else Logger.error("GPIO export path is not found");
} else Logger.error("GPIO path is not found");
} else Logger.info("OS is not Linux");
return false;
}

View File

@@ -0,0 +1,84 @@
package SBC;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import static Other.SomeCodes.ValidString;
public class NetworkTransmitReceiveInfo {
public String name;
public long bytesReceived;
public long packetsReceived;
public long errorsReceived;
public long droppedReceived;
public long fifoReceived;
public long frameReceived;
public long compressedReceived;
public long multicastReceived;
public long bytesTransmitted;
public long packetsTransmitted;
public long errorsTransmitted;
public long droppedTransmitted;
public long fifoTransmitted;
public long collsTransmitted;
public long carrierTransmitted;
public long compressedTransmitted;
public long timetick;
/**
* Calculate the download speed
* @param prev Previous NetworkTransmitReceiveInfo
* @param unit Speed unit (KB, MB, GB)
* @return Download speed in {unit}/second
*/
public double RxSpeed(NetworkTransmitReceiveInfo prev, String unit){
if (prev!=null){
if (Objects.equals(prev.name, name)){
long timeDiff = timetick - prev.timetick;
if (timeDiff>0){
long bytesDiff = bytesReceived - prev.bytesReceived;
if (ValidString(unit)) unit = unit.toUpperCase();
Double speed = ConvertToUnit(unit, timeDiff, bytesDiff);
if (speed != null) return speed;
}
}
}
return 0;
}
@Nullable
private Double ConvertToUnit(String unit, long timeDiff, long bytesDiff) {
if (bytesDiff>0){
double speed = ((double) bytesDiff / timeDiff) * 1000;
return switch (unit) {
case "KB" -> speed / 1024;
case "MB" -> speed / 1024 / 1024;
case "GB" -> speed / 1024 / 1024 / 1024;
default -> speed;
};
}
return null;
}
/**
* Calculate the upload speed
* @param prev Previous NetworkTransmitReceiveInfo
* @param unit Speed unit (KB, MB, GB)
* @return Upload speed in {unit}/second
*/
public double TxSpeed(NetworkTransmitReceiveInfo prev, String unit){
if (prev!=null){
if (Objects.equals(prev.name, name)){
long timeDiff = timetick - prev.timetick;
if (timeDiff>0){
long bytesDiff = bytesTransmitted - prev.bytesTransmitted;
if (ValidString(unit)) unit = unit.toUpperCase();
Double speed = ConvertToUnit(unit, timeDiff, bytesDiff);
if (speed != null) return speed;
}
}
}
return 0;
}
}

View File

@@ -0,0 +1,45 @@
package SBC;
public class ProcessorStatus {
public String name;
public int user;
public int nice;
public int system;
public int idle;
public int iowait;
public int irq;
public int softirq;
public int steal;
public int guest;
public int guest_nice;
/**
* Calculate total CPU time
* @return Total CPU time
*/
public int total_time(){
return user + nice + system + idle + iowait + irq + softirq + steal + guest + guest_nice;
}
/**
* Calculate idle CPU time
* @return Idle CPU time
*/
public int idle_time(){
return idle + iowait;
}
/**
* Calculate CPU usage percentage
* @param prev Previous CPU information
* @return CPU usage percentage 0 - 100
*/
public int cpu_usage(ProcessorStatus prev){
if (prev!=null){
int total_diff = total_time() - prev.total_time();
int idle_diff = idle_time() - prev.idle_time();
return (int)(100.0 * (total_diff - idle_diff) / total_diff);
}
return 0;
}
}

View File

@@ -0,0 +1,12 @@
package SBC;
public class RamInformation {
public int totalKB;
public int usedKB;
public int availableKB;
public int swapTotalKB;
public int swapFreeKB;
public double RamUsagePercentage(){
return (double) usedKB / totalKB * 100;
}
}

View File

@@ -0,0 +1,179 @@
package SBC;
import com.sun.jna.Platform;
import java.io.File;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SystemInformation {
public static int getCPUTemperature() {
if (Platform.isLinux()){
File ff = new File("/sys/class/thermal/thermal_zone0/temp");
if (ff.isFile() && ff.canRead()){
try{
String value = Files.readString(ff.toPath()).trim();
return Integer.parseInt(value) / 1000;
} catch (Exception ignored) {
}
}
}
return 0;
}
public static NetworkTransmitReceiveInfo[] getNetworkTransmitReceiveInfo(){
if (Platform.isLinux()){
File ff = new File("/proc/net/dev");
if (ff.isFile() && ff.canRead()){
List<NetworkTransmitReceiveInfo> result = new ArrayList<>();
final Pattern pattern = Pattern.compile("\\s+(.*):\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)");
try{
String[] lines = Files.readString(ff.toPath()).split("\n");
for (String line : lines){
Matcher m = pattern.matcher(line);
if (m.find()){
NetworkTransmitReceiveInfo info = new NetworkTransmitReceiveInfo();
info.name = m.group(1).trim();
info.bytesReceived = Long.parseLong(m.group(2));
info.packetsReceived = Long.parseLong(m.group(3));
info.errorsReceived = Long.parseLong(m.group(4));
info.droppedReceived = Long.parseLong(m.group(5));
info.fifoReceived = Long.parseLong(m.group(6));
info.frameReceived = Long.parseLong(m.group(7));
info.compressedReceived = Long.parseLong(m.group(8));
info.multicastReceived = Long.parseLong(m.group(9));
info.bytesTransmitted = Long.parseLong(m.group(10));
info.packetsTransmitted = Long.parseLong(m.group(11));
info.errorsTransmitted = Long.parseLong(m.group(12));
info.droppedTransmitted = Long.parseLong(m.group(13));
info.fifoTransmitted = Long.parseLong(m.group(14));
info.collsTransmitted = Long.parseLong(m.group(15));
info.carrierTransmitted = Long.parseLong(m.group(16));
info.compressedTransmitted = Long.parseLong(m.group(17));
info.timetick = System.currentTimeMillis();
result.add(info);
}
}
} catch (Exception ignored) {
}
return result.toArray(new NetworkTransmitReceiveInfo[0]);
}
}
return new NetworkTransmitReceiveInfo[0];
}
public static CpuInfo getCPUInfo(){
CpuInfo result = new CpuInfo();
if (Platform.isLinux()){
File ff = new File("/proc/cpuinfo");
if (ff.isFile() && ff.canRead()){
final Pattern pattern = Pattern.compile( "\\s*(.*):\\s*(.*)");
try{
String[] lines = Files.readString(ff.toPath()).split("\n");
for (String line : lines){
Matcher m = pattern.matcher(line);
if (m.find()){
String key = m.group(1).trim();
String value = m.group(2).trim();
switch (key){
case "processor":
result.processorCount++;
break;
case "Revision":
result.revision = value;
break;
case "Serial":
result.serial = value;
break;
case "Model":
result.model = value;
break;
}
}
}
} catch (Exception ignored) {
}
}
}
return result;
}
public static ProcessorStatus[] getProcStat(){
if (Platform.isLinux()){
File ff = new File("/proc/stat");
if (ff.isFile() && ff.canRead()){
final Pattern pattern = Pattern.compile( "(cpu\\d?)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)");
List<ProcessorStatus> result = new ArrayList<>();
try{
String[] lines = Files.readString(ff.toPath()).split("\n");
for (String line : lines){
Matcher m = pattern.matcher(line);
if (m.find()){
ProcessorStatus info = new ProcessorStatus();
info.name = m.group(1).trim();
info.user = Integer.parseInt(m.group(2));
info.nice = Integer.parseInt(m.group(3));
info.system = Integer.parseInt(m.group(4));
info.idle = Integer.parseInt(m.group(5));
info.iowait = Integer.parseInt(m.group(6));
info.irq = Integer.parseInt(m.group(7));
info.softirq = Integer.parseInt(m.group(8));
info.steal = Integer.parseInt(m.group(9));
info.guest = Integer.parseInt(m.group(10));
info.guest_nice = Integer.parseInt(m.group(11));
result.add(info);
}
}
return result.toArray(new ProcessorStatus[0]);
} catch (Exception ignored) {
}
}
}
return new ProcessorStatus[0];
}
public static RamInformation getRAMInformation(){
RamInformation result = new RamInformation();
if (Platform.isLinux()){
File ff = new File("/proc/meminfo");
if (ff.isFile() && ff.canRead()){
final Pattern pattern = Pattern.compile("(.*):\\s+(\\d+).kB");
try{
String[] lines = Files.readString(ff.toPath()).split("\n");
for (String line : lines) {
Matcher m = pattern.matcher(line);
if (m.find()){
String key = m.group(1);
int value = Integer.parseInt(m.group(2));
switch (key){
case "MemTotal":
result.totalKB = value;
break;
case "MemAvailable":
result.availableKB = value;
break;
case "SwapTotal":
result.swapTotalKB = value;
break;
case "SwapFree":
result.swapFreeKB = value;
break;
}
}
}
} catch (Exception ignored) {
}
result.usedKB = result.totalKB - result.availableKB;
}
}
return result;
}
}

View File

@@ -0,0 +1,9 @@
package Web;
public class AudioFilesInfo {
public String preset1;
public String preset2;
public String preset3;
public String preset4;
public String preset5;
}

View File

@@ -3,12 +3,14 @@ package Web;
import Other.SomeCodes;
import io.javalin.Javalin;
import io.javalin.http.UploadedFile;
import io.javalin.util.FileUtil;
import io.javalin.util.JavalinException;
import io.javalin.websocket.*;
import lombok.Getter;
import lombok.Setter;
import org.tinylog.Logger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
@@ -20,20 +22,24 @@ import static io.javalin.apibuilder.ApiBuilder.*;
@SuppressWarnings({"unused"})
public class WebServer {
private @Getter @Setter String webusername;
private @Getter @Setter String webpassword;
private final Javalin app;
private final Set<WsContext> connectedWebsocketClients = ConcurrentHashMap.newKeySet();
public WebServer(WebsocketEvent event, String webusername, String webpassword){
this.webusername = webusername;
this.webpassword = webpassword;
app = Javalin.create(config -> {
config.staticFiles.add("/html");
config.router.apiBuilder(()-> path("setting", () ->{
get(ctx -> ctx.json(SettingInfo.getInstance()));
path("audiofile",()-> post(ctx -> {
Logger.info("api /setting/audiofile");
String audiofile1 = ctx.formParam("1");
String audiofile2 = ctx.formParam("2");
String audiofile3 = ctx.formParam("3");
String audiofile4 = ctx.formParam("4");
String audiofile5 = ctx.formParam("5");
String audiofile1 = ctx.formParam("preset1");
String audiofile2 = ctx.formParam("preset2");
String audiofile3 = ctx.formParam("preset3");
String audiofile4 = ctx.formParam("preset4");
String audiofile5 = ctx.formParam("preset5");
Logger.info("audiofile1: {}", audiofile1);
Logger.info("audiofile2: {}", audiofile2);
Logger.info("audiofile3: {}", audiofile3);
@@ -41,41 +47,59 @@ public class WebServer {
Logger.info("audiofile5: {}", audiofile5);
Properties prop = SomeCodes.LoadProperties("config.properties");
prop.setProperty("audiofile1", audiofile1!=null?audiofile1:"");
prop.setProperty("audiofile2", audiofile2!=null?audiofile2:"");
prop.setProperty("audiofile3", audiofile3!=null?audiofile3:"");
prop.setProperty("audiofile4", audiofile4!=null?audiofile4:"");
prop.setProperty("audiofile5", audiofile5!=null?audiofile5:"");
prop.setProperty("AudioFile01", audiofile1!=null?audiofile1:"");
prop.setProperty("AudioFile02", audiofile2!=null?audiofile2:"");
prop.setProperty("AudioFile03", audiofile3!=null?audiofile3:"");
prop.setProperty("AudioFile04", audiofile4!=null?audiofile4:"");
prop.setProperty("AudioFile05", audiofile5!=null?audiofile5:"");
if (SaveProperties(prop, "config.properties")){
Logger.info("audiofile saved");
ctx.status(200);
} else {
Logger.error("Failed to save audiofile");
ctx.status(400);
ctx.result("Failed to save audiofile");
}
}));
path("uploadaudiofile", ()-> post(ctx -> {
UploadedFile file = ctx.uploadedFile("file");
if (file!=null){
try {
Path targetsave = audioPath.resolve(file.filename());
Files.copy(file.content(), targetsave);
Logger.info("Uploaded file: {}, size: {} saved at {}", file.filename(),file.size(), targetsave);
} catch (Exception e){
Logger.error("Failed to save uploaded file: {}, Message: {}", file.filename(), e.getMessage());
}
List<UploadedFile> uploadedFileList = ctx.uploadedFiles();
int size = uploadedFileList.size();
if (size>0){
uploadedFileList.forEach(ff ->{
String targetsave = audioPath.resolve(ff.filename()).toString();
FileUtil.streamToFile(ff.content(), targetsave);
Logger.info("Uploaded file: {}", targetsave);
});
ctx.status(200);
ctx.result("UploadedFiles: "+size);
} else {
ctx.status(400);
ctx.result("No file uploaded");
}
}));
path("weblogin", ()-> post(ctx -> {
String username = ctx.formParam("username");
String password = ctx.formParam("password");
Logger.info("api /setting/weblogin");
Logger.info("username: {}", username);
Logger.info("password: {}", password);
Properties prop = SomeCodes.LoadProperties("config.properties");
prop.setProperty("WebUsername", ValidString(username)?username:"admin");
prop.setProperty("WebPassword", ValidString(password)?password:"bandara");
if (SaveProperties(prop, "config.properties")){
ctx.status(200);
Logger.info("weblogin saved");
//ctx.status(200);
this.webusername = username;
this.webpassword = password;
ctx.redirect("/logout");
} else {
Logger.error("Failed to save weblogin");
ctx.status(400);
ctx.result("Failed to save weblogin");
}
}));
path("camera",()-> post(ctx -> {
@@ -83,6 +107,11 @@ public class WebServer {
String camera_port = ctx.formParam("port");
String camera_username = ctx.formParam("username");
String camera_password = ctx.formParam("password");
Logger.info("api /setting/camera");
Logger.info("camera_ip: {}", camera_ip);
Logger.info("camera_port: {}", camera_port);
Logger.info("camera_username: {}", camera_username);
Logger.info("camera_password: {}", camera_password);
Properties prop = SomeCodes.LoadProperties("config.properties");
prop.setProperty("Camera_ip", ValidString(camera_ip)?camera_ip:"192.168.0.4");
@@ -90,9 +119,13 @@ public class WebServer {
prop.setProperty("Camera_user", ValidString(camera_username)?camera_username:"root");
prop.setProperty("Camera_password", ValidString(camera_password)?camera_password:"password");
if (SaveProperties(prop, "config.properties")){
Logger.info("IP camera setting saved");
ctx.status(200);
if (event!=null) event.NewCameraConfiguration();
} else {
Logger.error("Failed to save IP camera setting");
ctx.status(400);
ctx.result("Failed to save IP camera setting");
}
}));
@@ -103,7 +136,7 @@ public class WebServer {
if (ctx.sessionAttribute("username")==null) {
// belum login
ctx.redirect("/login.html");
} else if (Objects.equals(ctx.sessionAttribute("username"), webusername)){
} else if (Objects.equals(ctx.sessionAttribute("username"), this.webusername)){
// sudah login
ctx.redirect("/index.html");
} else {
@@ -127,11 +160,12 @@ public class WebServer {
app.post("/login", ctx ->{
String username = ctx.formParam("username");
String password = ctx.formParam("password");
if (Objects.equals(username, webusername) && Objects.equals(password, webpassword)){
if (Objects.equals(username, this.webusername) && Objects.equals(password, this.webpassword)){
ctx.sessionAttribute("username", username);
ctx.redirect("/index.html");
} else {
ctx.redirect("/login.html?error=Invalid username or password");
ctx.status(400);
ctx.redirect("/login.html");
}
});
@@ -151,9 +185,12 @@ public class WebServer {
if (event!=null) {
WebsocketReply reply = event.onWebsocketCommand(command);
if (reply!=null) ctx.sendAsClass(reply, WebsocketReply.class);
//if (reply!=null) SendtoAll(reply);
}
} catch (Exception e){
Logger.error("Failed to parse WebSocketCommand message: {}", e.getMessage());
ctx.closeSession();
connectedWebsocketClients.remove(ctx);
}
});
@@ -196,13 +233,13 @@ public class WebServer {
}
WsConnectHandler connectws = ws ->{
Logger.info("WebSocket connected from {}", ws.host());
Logger.info("WebSocket connected from {}", ws.sessionId());
//ws.headerMap().forEach((key, value) -> Logger.info("HeaderMap {}: {}", key, value));
connectedWebsocketClients.add(ws);
};
WsCloseHandler closews = ws ->{
Logger.info("WebSocket closed from {}, code {}, reason {}", ws.host(), ws.status(), ws.reason());
Logger.info("WebSocket closed from {}, code {}, reason {}", ws.sessionId(), ws.status(), ws.reason());
connectedWebsocketClients.remove(ws);
};

View File

@@ -3,4 +3,5 @@ package Web;
public interface WebsocketEvent {
String onMessage(String message);
WebsocketReply onWebsocketCommand(WebsocketCommand command);
void NewCameraConfiguration();
}

View File

@@ -2,22 +2,23 @@ package id.co.gtc;
import Audio.AudioFileProperties;
import Audio.AudioPlayer;
import Audio.Bass;
import Audio.PlaybackEvent;
import Camera.PanTiltController;
import Camera.RtspGrabber;
import Camera.VapixProtocol;
import Other.SomeCodes;
import Web.WebServer;
import Web.WebsocketCommand;
import Web.WebsocketEvent;
import Web.WebsocketReply;
import SBC.ProcessorStatus;
import SBC.RamInformation;
import SBC.SystemInformation;
import Web.*;
import com.google.gson.JsonObject;
import org.bytedeco.opencv.global.opencv_core;
import org.tinylog.Logger;
import java.io.File;
import java.nio.file.Path;
import java.util.Objects;
import java.util.Properties;
import java.util.*;
import static Other.SomeCodes.*;
@@ -28,6 +29,11 @@ public class Main {
private static PanTiltController panTiltController;
private static AudioFileProperties audioFileProperties;
private static VapixProtocol vapixProtocol;
private static Timer timer;
private static int cpuTemperature;
private static RamInformation ramInformation;
private static ProcessorStatus[] previousCpuInfo;
private static final Map<String, Integer> cpuUsage = new HashMap<>();
// Application start from here
public static void main(String[] args) {
@@ -37,8 +43,11 @@ public class Main {
if (rtspGrabber!=null) rtspGrabber.Stop();
if (panTiltController!=null) panTiltController.Close();
if (vapixProtocol!=null) vapixProtocol.Close();
if (timer!=null) timer.cancel();
}));
init_system_monitoring();
init_properties();
init_audiofiles();
@@ -49,6 +58,29 @@ public class Main {
init_Vapix();
}
private static void init_system_monitoring(){
TimerTask tt = new TimerTask() {
@Override
public void run() {
cpuTemperature = SystemInformation.getCPUTemperature();
ramInformation = SystemInformation.getRAMInformation();
ProcessorStatus[] cpuinfo = SystemInformation.getProcStat();
if (cpuinfo.length>0){
if (previousCpuInfo==null || !Objects.equals(previousCpuInfo.length, cpuinfo.length)){
previousCpuInfo = cpuinfo;
} else {
for(int ii=0;ii<previousCpuInfo.length;ii++){
cpuUsage.put(cpuinfo[ii].name, cpuinfo[ii].cpu_usage(previousCpuInfo[ii]));
}
}
}
}
};
timer = new Timer();
timer.scheduleAtFixedRate(tt, 1000, 5000);
}
private static void init_properties(){
if (ExtractResource("/tinylog.properties", currentDirectory)==null) Logger.error("Failed to extract tinylog.properties");
if (ExtractResource("/config.properties", currentDirectory)==null) Logger.error("Failed to extract config.properties");
@@ -73,21 +105,24 @@ public class Main {
String username = config.getProperty("Camera_user");
String password = config.getProperty("Camera_password");
if (ValidString(ip)){
if (ValidInteger(port)){
if (ValidPortNumber(port)){
if (ValidString(username)){
if (ValidString(password)){
vapixProtocol = new VapixProtocol(ip, Integer.parseInt(port), username, password);
Logger.info("Camera Product Number: "+vapixProtocol.GetProductNumber());
Logger.info("Camera Serial Number: "+vapixProtocol.GetSerialNumber());
if (vapixProtocol.PTZEnabled()){
Logger.info("PTZ Enabled");
} else Logger.error("PTZ Disabled");
Logger.info("Max Zoom: "+vapixProtocol.GetPTZMaxZoom());
Logger.info("Min Zoom: "+vapixProtocol.GetPTZMinZoom());
} else Logger.error("Invalid Camera Password");
} else Logger.error("Invalid Camera Username");
} else Logger.error("Invalid Camera Port");
} else Logger.error("Invalid Camera IP");
if (IpIsReachable(ip)){
Logger.info("Camera IP is reachable");
vapixProtocol = new VapixProtocol(ip, Integer.parseInt(port), username, password);
Logger.info("Camera Product Number: "+vapixProtocol.GetProductNumber());
Logger.info("Camera Serial Number: "+vapixProtocol.GetSerialNumber());
if (vapixProtocol.PTZEnabled()){
Logger.info("PTZ Enabled");
} else Logger.error("PTZ Disabled");
Logger.info("Max Zoom: "+vapixProtocol.GetPTZMaxZoom());
Logger.info("Min Zoom: "+vapixProtocol.GetPTZMinZoom());
} else Logger.error("Camera IP is not reachable");
} else Logger.error("Camera Password is not valid string");
} else Logger.error("Camera Username is not valid string");
} else Logger.error("Camera Port is not valid");
} else Logger.error( "Camera IP is not valid string");
}
private static void init_pantiltcontroller() {
@@ -112,22 +147,32 @@ public class Main {
Properties config = SomeCodes.LoadProperties("config.properties");
String targetip = config.getProperty("Camera_ip");
String rtsppath = config.getProperty("Camera_Rtsp_path");
rtspGrabber = null;
if (ValidString(targetip)){
if (ValidString(rtsppath)){
rtspGrabber = new RtspGrabber(targetip, rtsppath);
rtspGrabber.Start(true, 1920, 1080);
} else Logger.error("Invalid Camera Path");
} else Logger.error("Invalid Camera IP");
if (IpIsReachable(targetip)){
Logger.info("Camera IP is reachable");
rtspGrabber = new RtspGrabber(targetip, rtsppath);
rtspGrabber.Start(true, 1920, 1080);
} else Logger.error("Camera IP is not reachable");
} else Logger.error("Camera Path is not valid string");
} else Logger.error("Camera IP is not valid string");
}
private static void init_audio() {
audioPlayer = new AudioPlayer();
audioPlayer.DetectOutputDevices();
audioPlayer.OpenDevice(1,48000);
audioPlayer.setMasterVolume(100);
audioPlayer.setPlaybackvolume(100);
int devid = audioPlayer.FindDeviceIDWithName("USB");
if (devid<1) devid = audioPlayer.FindDeviceIDWithName("Speakers");
if (devid>0){
Bass.BASS_DEVICEINFO info = audioPlayer.GetDeviceInfo(devid);
if (audioPlayer.OpenDevice(devid,48000)){
audioPlayer.setMasterVolume(100);
audioPlayer.setPlaybackvolume(100);
Logger.info("Audio Device ID={} opened : {}", devid, info.name);
} else Logger.error("Failed to open Audio Device ID={}", devid);
} else Logger.error("USB Audio Device not found");
}
static PlaybackEvent pe = new PlaybackEvent() {
@@ -217,6 +262,7 @@ public class Main {
audioFileProperties = afp;
audioPlayer.PlayAudioFile(afp, true, pe);
return new WebsocketReply("PLAY AUDIO", afp.filename);
} else return new WebsocketReply("PLAY AUDIO", "Failed to open audio file "+filename);
} else return new WebsocketReply("PLAY AUDIO", "Audio file not found : "+filename);
} else return new WebsocketReply("PLAY AUDIO", String.format("AudioFile with ID %02d not found", id));
@@ -252,20 +298,64 @@ public class Main {
case "GET BASE64":
if (rtspGrabber!=null){
if (Objects.equals(command.data,"HQ"))
return new WebsocketReply("GET BASE64", "data:image/jpeg;base64,"+ rtspGrabber.getLastHQBase64(), String.format("Streaming at %dx%d", rtspGrabber.getHQWidth(), rtspGrabber.getHQHeight()));
return new WebsocketReply("GET BASE64", "data:image/jpeg;base64,"+ rtspGrabber.getLastHQBase64(), rtspGrabber.HQStreamingStatus());
else
return new WebsocketReply("GET BASE64", "data:image/jpeg;base64,"+ rtspGrabber.getLastLQBase64(), String.format("Streaming at %dx%d", rtspGrabber.getLQWidth(), rtspGrabber.getLQHeight()));
return new WebsocketReply("GET BASE64", "data:image/jpeg;base64,"+ rtspGrabber.getLastLQBase64(), rtspGrabber.LQStreamingStatus());
} else return new WebsocketReply("GET BASE64", "RTSP Grabber not initialized");
case "GET RESOLUTION":
if (vapixProtocol!=null){
int[] res = vapixProtocol.GetCurrentResolution(1);
return new WebsocketReply("GET RESOLUTION", String.format("%dx%d", res[0], res[1]));
} else return new WebsocketReply("GET RESOLUTION", "VapixProtocol not initialized");
case "GET AUDIOFILES":
AudioFilesInfo afi = new AudioFilesInfo();
afi.preset1 = GetConfigAudioFile(1);
afi.preset2 = GetConfigAudioFile(2);
afi.preset3 = GetConfigAudioFile(3);
afi.preset4 = GetConfigAudioFile(4);
afi.preset5 = GetConfigAudioFile(5);
return new WebsocketReply("GET AUDIOFILES", gson.toJson(afi));
case "GET SYSTEM INFO":
JsonObject data = new JsonObject();
data.addProperty("cpu_temperature", String.valueOf(cpuTemperature));
if (ramInformation!=null) {
data.addProperty("ram_usage", String.format("%.1f", ramInformation.RamUsagePercentage()));
}
if (!cpuUsage.isEmpty()) {
for(String key : cpuUsage.keySet()){
data.addProperty(key, String.valueOf(cpuUsage.get(key)));
}
}
return new WebsocketReply("GET SYSTEM INFO", data.toString());
default:
return new WebsocketReply("UNKNOWN COMMAND", command.command);
}
}
@Override
public void NewCameraConfiguration() {
Logger.info("New Camera Configuration detected");
Properties prop = SomeCodes.LoadProperties("config.properties");
String camera_ip = prop.getProperty("Camera_ip");
String camera_port = prop.getProperty("Camera_port");
String camera_user = prop.getProperty("Camera_user");
String camera_password = prop.getProperty("Camera_password");
String camera_rtsp_path = prop.getProperty("Camera_Rtsp_path");
Logger.info("Camera Ip: "+camera_ip);
Logger.info("Camera Port: "+camera_port);
Logger.info("Camera User: "+camera_user);
Logger.info("Camera Password: "+camera_password);
Logger.info("Camera Rtsp Path: "+camera_rtsp_path);
if (rtspGrabber!=null) rtspGrabber.Stop();
init_rtspgrabber();
Logger.info("RtspGrabber recreated");
if (vapixProtocol!=null) vapixProtocol.Close();
init_Vapix();
Logger.info("VapixProtocol recreated");
}
};
private static void init_webserver() {