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

@@ -10,7 +10,7 @@ AudioFile03 = pinkNoiseWav.wav
AudioFile04 = 04.mp3 AudioFile04 = 04.mp3
AudioFile05 = 05.mp3 AudioFile05 = 05.mp3
AudioVolumeOutput = 100 AudioVolumeOutput = 100
SerialPort = /dev/ttyUSB0 SerialPort = /dev/ttyAMA0
SerialBaudRate = 9600 SerialBaudRate = 9600
PanTiltID = 1 PanTiltID = 1
WebUsername = admin WebUsername = admin

View File

@@ -40,23 +40,7 @@
</div> </div>
</div> </div>
</nav> </nav>
<!--<div class="container-fluid container4">-->
<!-- <div class="row">-->
<!-- <div class="col-12 col-sm-12 col-md-12 col-lg-8 col-xl-8 border-1">-->
<!-- <h1 class="hr-lines">Bird Deterrent System</h1>-->
<!-- </div>-->
<!-- <div class="col-12 col-sm-12 col-md-12 col-lg-4 col-xl-4">-->
<!-- <div class="container">-->
<!-- <div class="col-12 col-sm-12 col-md-12 col-lg-12 col-xl-12 outside-logout">-->
<!-- <form action="/logout" method="get">-->
<!-- <button class="btn btn-outline-light" type="submit">Logout</button>-->
<!-- </form>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!--</div>-->
<!-- SECTION 1-->
<!-- SECTION 2 --> <!-- SECTION 2 -->
<div class="container-fluid container3"> <div class="container-fluid container3">
@@ -66,13 +50,43 @@
<img id="camerastream" src="public/images/not-available.png" class="img-cctv class100" alt=""> <img id="camerastream" src="public/images/not-available.png" class="img-cctv class100" alt="">
</div> </div>
<div class="row"> <div class="row">
<div class="col-6 col-sm-6 col-md-8 col-lg-8 col-xl-8"> <div class="col-12 col-sm-4 col-md-4 col-lg-4 col-xl-4">
<p id="streaming_status">No Status</p> <p id="streaming_status">No Status</p>
</div> </div>
<div class="col-6 col-sm-6 col-md-4 col-lg-4 col-xl-4"> <div class="col-12 col-sm-4 col-md-5 col-lg-5 col-xl-5">
<div class="form-check form-switch"> <!-- <p id="system_information"> System Information </p>-->
<input class="form-check-input" type="checkbox" role="switch" id="quality_video" onchange="change_video_quality()"> <div class="row">
<label class="form-check-label" for="quality_video">High Quality Video</label> <div class="col">
<div class="d-flex align-items-center">
<span class="fa-solid fa-microchip icon-system"></span>
<p id="cpu_usage" class="padleft">100 %</p>
</div>
</div>
<div class="col">
<div class="d-flex align-items-center">
<span class="fa-solid fa-temperature-half icon-system"></span>
<p id="cpu_temperature" class="padleft">55 °C</p>
</div>
</div>
<div class="col">
<div class="d-flex align-items-center">
<span class="fa-solid fa-memory icon-system"></span>
<p id="ram_usage" class="padleft">15 %</p>
</div>
</div>
</div>
</div>
<div class="col-12 col-sm-4 col-md-3 col-lg-3 col-xl-3">
<!-- <div class="form-check form-switch">-->
<!-- <input class="form-check-input" type="checkbox" role="switch" id="quality_video" onchange="change_video_quality()">-->
<!-- <label class="form-check-label" for="quality_video">HQ</label>-->
<!-- </div>-->
<div class="container align-right">
<label for="quality_video" class="toggle-label">LQ</label>
<input type="checkbox" class="toggle-switch" id="quality_video" onchange="change_video_quality()">
<label for="quality_video" class="toggle-label">HQ</label>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -14,10 +14,32 @@ let camerastream;
document.addEventListener("DOMContentLoaded", function(){ let files = [null,null,null,null,null];
camerastream = document.getElementById("camerastream");
setInterval(function (){ let getbase64handle;
let getsysteminfohandle;
window.addEventListener("unload",function (){
clearInterval(getbase64handle);
clearInterval(getsysteminfohandle);
if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({
command: "STOP AUDIO",
data: 0
}));
// wait a while
const start = Date.now();
while(Date.now()-start<200){}
}
ws.close();
})
window.addEventListener("load", function (){
camerastream = document.getElementById("camerastream");
// Update camera stream every 50ms / 20fps
getbase64handle = setInterval(function (){
if (ws.readyState === WebSocket.OPEN){ if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({ ws.send(JSON.stringify({
command: "GET BASE64", command: "GET BASE64",
@@ -25,7 +47,16 @@ document.addEventListener("DOMContentLoaded", function(){
})); }));
} else update_camerastream(null); } else update_camerastream(null);
},10); },1000/15);
getsysteminfohandle = setInterval(function (){
if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({
command: "GET SYSTEM INFO",
data: 0
}));
}
}, 5000);
ws = new WebSocket("/ws"); ws = new WebSocket("/ws");
@@ -49,6 +80,10 @@ document.addEventListener("DOMContentLoaded", function(){
command: "GET RESOLUTION", command: "GET RESOLUTION",
data: 0 data: 0
})); }));
ws.send(JSON.stringify({
command: "GET AUDIOFILES",
data: 0
}))
} }
ws.onmessage = function(event){ ws.onmessage = function(event){
@@ -63,7 +98,10 @@ document.addEventListener("DOMContentLoaded", function(){
update_camerastream(dx.data); update_camerastream(dx.data);
} else update_camerastream(null); } else update_camerastream(null);
if (dx.additional && dx.additional.length>0){ if (dx.additional && dx.additional.length>0){
$('#streaming_status').html(dx.additional); let stat = JSON.parse(dx.additional);
if (Array.isArray(stat) && stat.length === 3){
$('#streaming_status').html("Streaming at "+stat[0]+"x"+stat[1]+" "+stat[2]+"fps");
}
} }
break; break;
case "SET VOLUME": case "SET VOLUME":
@@ -75,15 +113,13 @@ document.addEventListener("DOMContentLoaded", function(){
break; break;
case "GET MAX ZOOM": case "GET MAX ZOOM":
console.log("Get Max Zoom: "+dx.data); console.log("Get Max Zoom: "+dx.data);
$('#zoom') let zoom = document.getElementById("zoom");
.attr("max", dx.data) zoom.min = 1;
.attr("min", 1); zoom.max = dx.data;
break; break;
case "GET ZOOM": case "GET ZOOM":
document.getElementById("zoom").value = dx.data;
console.log("Get Zoom: "+dx.data); console.log("Get Zoom: "+dx.data);
$('#zoom').val(dx.data);
break; break;
case "GET RESOLUTION": case "GET RESOLUTION":
console.log("Get Resolution: "+dx.data); console.log("Get Resolution: "+dx.data);
@@ -114,11 +150,60 @@ document.addEventListener("DOMContentLoaded", function(){
break; break;
case "STOP AUDIO": case "STOP AUDIO":
console.log("Stop Audio"); console.log("Stop Audio");
$('#status_player').html("Stop Playback"); document.getElementById("status_player").innerHTML = "Stop Playback";
break; break;
case 'SET VIDEO QUALITY': case 'SET VIDEO QUALITY':
console.log("Set Video Quality: "+dx.data); console.log("Set Video Quality: "+dx.data);
break; break;
case "GET SYSTEM INFO":
//console.log("Get System Info: "+dx.data);
/**
* @type {{cpu_temperature: string, ram_usage: string, cpu: string, cpu0: string, cpu1: string, cpu2: string, cpu3: string}} systeminfo
*/
let systeminfo = JSON.parse(dx.data);
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} %`);
break;
case "GET AUDIOFILES":
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;
play_on(1);
} else {
files[0] = null;
play_off(1);
}
if (audiofiles.preset2 && audiofiles.preset2.length>0 && audiofiles.preset2!== "null") {
files[1] = audiofiles.preset2;
play_on(2);
} else {
files[1] = null;
play_off(2);
}
if (audiofiles.preset3 && audiofiles.preset3.length>0 && audiofiles.preset3!== "null") {
files[2] = audiofiles.preset3;
play_on(3);
} else {
files[2] = null;
play_off(3);
}
if (audiofiles.preset4 && audiofiles.preset4.length>0 && audiofiles.preset4!== "null") {
files[3] = audiofiles.preset4;
play_on(4);
} else {
files[3] = null;
play_off(4);
}
if (audiofiles.preset5 && audiofiles.preset5.length>0 && audiofiles.preset5!== "null") {
files[4] = audiofiles.preset5;
play_on(5);
} else {
files[4] = null;
play_off(5);
}
break;
} }
@@ -132,7 +217,9 @@ document.addEventListener("DOMContentLoaded", function(){
set_pan_speed(32); set_pan_speed(32);
set_tilt_speed(32); set_tilt_speed(32);
}); })
/** /**
* Update camera stream * Update camera stream
@@ -176,7 +263,9 @@ function show_stop_and_hide_play(index){
} }
function allplay(){ function allplay(){
for (let i = 0; i < 5; i++) play_on(i+1); for (let i = 0; i < 5; i++) {
if (files[i]) play_on(i+1);
}
} }
function play_button(index){ function play_button(index){

View File

@@ -48,74 +48,72 @@
<h5 class="text-md-center">Audio Files</h5> <h5 class="text-md-center">Audio Files</h5>
</div> </div>
<div class="card-body bg-gray"> <div class="card-body bg-gray">
<form action="/setting/audiofile" method="post"> <!-- PRESET 1-->
<!-- PRESET 1--> <div class="row mt--2">
<div class="row mt--2"> <div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6"> <p>Preset 1</p>
<p>Preset 1</p>
</div>
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<select id="preset1" class="form-control class100">
<option>Option 1</option>
<option>Option 2</option>
</select>
</div>
</div> </div>
<!-- PRESET 2--> <div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<div class="row mt-2"> <select id="preset1" class="form-control class100">
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6"> <option>Option 1</option>
<p>Preset 2</p> <option>Option 2</option>
</div> </select>
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<select id="preset2" class="form-control class100">
<option>Option 1</option>
<option>Option 2</option>
</select>
</div>
</div> </div>
<!-- PRESET 3--> </div>
<div class="row mt-2"> <!-- PRESET 2-->
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6"> <div class="row mt-2">
<p>Preset 3</p> <div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
</div> <p>Preset 2</p>
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<select id="preset3" class="form-control class100">
<option>Option 1</option>
<option>Option 2</option>
</select>
</div>
</div> </div>
<!-- PRESET 4--> <div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<div class="row mt-2"> <select id="preset2" class="form-control class100">
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6"> <option>Option 1</option>
<p>Preset 4</p> <option>Option 2</option>
</div> </select>
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<select id="preset4" class="form-control class100">
<option>Option 1</option>
<option>Option 2</option>
</select>
</div>
</div> </div>
<!-- PRESET 5--> </div>
<div class="row mt-2"> <!-- PRESET 3-->
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6"> <div class="row mt-2">
<p>Preset 5</p> <div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
</div> <p>Preset 3</p>
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<select id="preset5" class="form-control class100">
<option>Option 1</option>
<option>Option 2</option>
</select>
</div>
</div> </div>
<div class="row mt-2"> <div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<div class="col-6"></div> <select id="preset3" class="form-control class100">
<div class="col-6"> <option>Option 1</option>
<button id="save_audio" type="submit" class="btn btn-primary class100" onclick="save_audio()">SAVE</button> <option>Option 2</option>
</div> </select>
</div> </div>
</form> </div>
<!-- PRESET 4-->
<div class="row mt-2">
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<p>Preset 4</p>
</div>
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<select id="preset4" class="form-control class100">
<option>Option 1</option>
<option>Option 2</option>
</select>
</div>
</div>
<!-- PRESET 5-->
<div class="row mt-2">
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<p>Preset 5</p>
</div>
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<select id="preset5" class="form-control class100">
<option>Option 1</option>
<option>Option 2</option>
</select>
</div>
</div>
<div class="row mt-2">
<div class="col-6"></div>
<div class="col-6">
<button id="save_audio" class="btn btn-primary class100" onclick="save_audio()">SAVE</button>
</div>
</div>
</div> </div>
</div> </div>
@@ -130,7 +128,7 @@
<p> Upload audio files</p> <p> Upload audio files</p>
</div> </div>
<div class="col-6 col-sm-6 col-md-4 col-lg-4 col-xl-4"> <div class="col-6 col-sm-6 col-md-4 col-lg-4 col-xl-4">
<input class="form-control" type="file" name="file" title="Select Audio File"> <input class="form-control" type="file" name="file" required title="Select Audio File">
</div> </div>
<div class="col-6 col-sm-6 col-md-2 col-lg-2 col-xl-2"> <div class="col-6 col-sm-6 col-md-2 col-lg-2 col-xl-2">
<button id="uploadd_file" type="submit" class="btn btn-dark class100">Upload</button> <button id="uploadd_file" type="submit" class="btn btn-dark class100">Upload</button>
@@ -146,53 +144,51 @@
<h5 class="text-center">Camera</h5> <h5 class="text-center">Camera</h5>
</div> </div>
<div class="card-body bg-gray"> <div class="card-body bg-gray">
<form action="/setting/camera" method="post"> <div class="row mt-2">
<div class="row mt-2"> <div class="col-6">
<div class="col-6"> <p>IP Address</p>
<p>IP Address</p>
</div>
<div class="col-6">
<input id="setting_ip" class="form-control" title="IP Address">
</div>
</div> </div>
<div class="row mt-2"> <div class="col-6">
<div class="col-6"> <input id="setting_ip" name="ip" class="form-control" title="IP Address">
<p>Port</p>
</div>
<div class="col-6">
<input id="setting_port" class="form-control" title="Port">
</div>
</div> </div>
</div>
<div class="row mt-2">
<div class="col-6">
<p>Port</p>
</div>
<div class="col-6">
<input id="setting_port" name="port" class="form-control" title="Port">
</div>
</div>
<div class="row mt-2"> <div class="row mt-2">
<div class="col-6"> <div class="col-6">
<p>Username</p> <p>Username</p>
</div>
<div class="col-6">
<input id="setting_username" class="form-control" title="Username">
</div>
</div> </div>
<div class="col-6">
<input id="setting_username" name="username" class="form-control" title="Username">
</div>
</div>
<div class="row mt-2"> <div class="row mt-2">
<div class="col-6"> <div class="col-6">
<p>Password</p> <p>Password</p>
</div> </div>
<div class="col-6"> <div class="col-6">
<div class="input-group"> <div class="input-group">
<input type="password" id="setting_password" class="form-control" title="Password"> <input type="password" id="setting_password" name="password" class="form-control" title="Password">
<span class="input-group-text" onclick="cameraPassword()"> <span class="input-group-text" onclick="cameraPassword()">
<i class="fa-solid fa-eye" id="icon_camera"></i> <i class="fa-solid fa-eye" id="icon_camera"></i>
</span> </span>
</div>
</div> </div>
</div> </div>
<div class="row mt-2"> </div>
<div class="col-6"></div> <div class="row mt-2">
<div class="col-6"> <div class="col-6"></div>
<button id="save_camera" type="submit" class="btn btn-primary class100" onclick="save_camera()">SAVE</button> <div class="col-6">
</div> <button id="save_camera" class="btn btn-primary class100" onclick="save_camera()">SAVE</button>
</div> </div>
</form> </div>
</div> </div>
</div> </div>
@@ -201,48 +197,46 @@
<h5 class="text-center">Login</h5> <h5 class="text-center">Login</h5>
</div> </div>
<div class="card-body bg-gray"> <div class="card-body bg-gray">
<form action="/setting/weblogin" method="post"> <div class="row mt-2">
<div class="row mt-2"> <div class="col-6">
<div class="col-6"> <p>Username</p>
<p>Username</p>
</div>
<div class="col-6">
<input id="login_username" class="form-control" title="Username">
</div>
</div> </div>
<div class="row mt-2"> <div class="col-6">
<div class="col-6"> <input id="login_username" class="form-control" title="Username">
<p>Password</p> </div>
</div> </div>
<div class="col-6"> <div class="row mt-2">
<div class="input-group"> <div class="col-6">
<input type="password" id="edit_password" class="form-control" title="Password"> <p>Password</p>
<span class="input-group-text" onclick="showPassword()"> </div>
<div class="col-6">
<div class="input-group">
<input type="password" id="edit_password" class="form-control" title="Password">
<span class="input-group-text" onclick="showPassword()">
<i class="fa-solid fa-eye" id="icon_password"></i> <i class="fa-solid fa-eye" id="icon_password"></i>
</span> </span>
</div>
</div> </div>
</div> </div>
<div class="row mt-2"> </div>
<div class="col-6"> <div class="row mt-2">
<p>Confirm Password</p> <div class="col-6">
</div> <p>Confirm Password</p>
<div class="col-6"> </div>
<div class="input-group"> <div class="col-6">
<input type="password" id="confirm_password" class="form-control" title="Confirm Password"> <div class="input-group">
<span class="input-group-text" onclick="showConfirm()"> <input type="password" id="confirm_password" class="form-control" title="Confirm Password">
<span class="input-group-text" onclick="showConfirm()">
<i class="fa-solid fa-eye" id="icon_confirm"></i> <i class="fa-solid fa-eye" id="icon_confirm"></i>
</span> </span>
</div>
</div> </div>
</div> </div>
<div class="row mt-2"> </div>
<div class="col-6"></div> <div class="row mt-2">
<div class="col-6"> <div class="col-6"></div>
<button id="save_login" type="submit" class="btn btn-primary class100" onclick="save_login()">SAVE</button> <div class="col-6">
</div> <button id="save_login" class="btn btn-primary class100" onclick="save_login()">SAVE</button>
</div> </div>
</form> </div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -54,7 +54,14 @@ function fill_select(index, values){
*/ */
let preset = document.getElementById("preset"+index); let preset = document.getElementById("preset"+index);
preset.innerHTML = ""; preset.innerHTML = "";
if (values!=null && values.length>0){ if (values!=null && values.length>0){
// add empty option
let option = document.createElement("option");
option.value = "";
option.innerText = "";
preset.appendChild(option);
for (let i = 0; i < values.length; i++) { for (let i = 0; i < values.length; i++) {
const element = values[i]; const element = values[i];
let option = document.createElement("option"); let option = document.createElement("option");
@@ -109,3 +116,90 @@ function showConfirm() {
icon.classList.add('fa-eye'); icon.classList.add('fa-eye');
} }
} }
function save_audio(){
let preset1 = $('#preset1').val();
let preset2 = $('#preset2').val();
let preset3 = $('#preset3').val();
let preset4 = $('#preset4').val();
let preset5 = $('#preset5').val();
if (confirm("Are you sure want to change Audio Preset ?")){
fetch("/setting/audiofile", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: new URLSearchParams({preset1: preset1, preset2: preset2, preset3: preset3, preset4: preset4, preset5: preset5})
}).then(resp => {
if (resp.status === 200) {
alert("Success");
} else {
resp.text().then(text=>alert("Failed to change Audio Preset : "+text));
}
});
}
}
function save_camera(){
let ip = $('#setting_ip').val();
let port = $('#setting_port').val();
let username = $('#setting_username').val();
let password = $('#setting_password').val();
if (confirm("Are you sure want to change Camera Information ?")){
fetch("/setting/camera", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: new URLSearchParams({ip: ip, port: port, username: username, password: password})
}).then(resp => {
if (resp.status === 200) {
alert("Success");
} else {
resp.text().then(text => {
alert("Failed to change Camera Information : "+text);
});
}
});
}
}
function save_login(){
let username = $('#login_username').val();
let password = $('#edit_password').val();
let confirmpassword = $('#confirm_password').val();
if (username.length === 0){
alert("Username cannot be empty");
return;
}
if (password.length === 0){
alert("Password cannot be empty");
return;
}
if (password !== confirmpassword){
alert("Password not match");
return;
}
if (confirm("Are you sure want to change Web Login Information ?")){
fetch("/setting/weblogin", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: new URLSearchParams({username: username, password: password})
}).then(resp => {
if (resp.status === 200) {
alert("Success");
} else {
resp.text().then(text => {
alert("Failed to change Web Login Information : "+text);
});
}
})
}
}

View File

@@ -1,9 +1,9 @@
#Fri Nov 08 16:52:01 WIB 2024 #Mon Nov 11 16:04:42 WIB 2024
AudioFile01=elangWav.wav AudioFile01=elangWav.wav
AudioFile02=gunshotsWav.wav AudioFile02=gunshotsWav.wav
AudioFile03=pinkNoiseWav.wav AudioFile03=pinkNoiseWav.wav
AudioFile04=04.mp3 AudioFile04=
AudioFile05=05.mp3 AudioFile05=null
AudioVolumeOutput=100 AudioVolumeOutput=100
Camera_Rtsp_path=/axis-media/media.amp Camera_Rtsp_path=/axis-media/media.amp
Camera_ip=192.168.10.17 Camera_ip=192.168.10.17
@@ -12,13 +12,8 @@ Camera_port=80
Camera_user=root Camera_user=root
PanTiltID=1 PanTiltID=1
SerialBaudRate=9600 SerialBaudRate=9600
SerialPort=/dev/ttyUSB0 SerialPort=/dev/ttyAMA0
WebHost=0.0.0.0 WebHost=0.0.0.0
WebPassword=bandara WebPassword=bandara
WebPort=8080 WebPort=8080
WebUsername=admin WebUsername=admin
audiofile1=
audiofile2=
audiofile3=
audiofile4=
audiofile5=

193
pom.xml
View File

@@ -8,11 +8,188 @@
<artifactId>BirdStrikeSoetta</artifactId> <artifactId>BirdStrikeSoetta</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.bytedeco</groupId> <groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId> <artifactId>javacv-platform</artifactId>
<version>1.5.10</version> <version>1.5.8</version>
<exclusions>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>leptonica</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>leptonica-platform</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>artoolkitplus</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>artoolkitplus-platform</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>libdc1394</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>libdc1394-platform</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>libfreenect</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>libfreenect-platform</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>libfreenect2</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>libfreenect2-platform</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>flycapture</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>flycapture-platform</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>librealsense</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>librealsense-platform</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>librealsense2</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>librealsense2-platform</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>videoinput</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>videoinput-platform</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>tesseract</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>tesseract-platform</artifactId>
</exclusion>
<!-- platform dibuangin semua, kemudian tambah sendiri linux-arm64, windows-x86_64, linux-x86_64 -->
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp-platform</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>openblas-platform</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>opencv-platform</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg-platform</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<!-- manual tambah untuk platform windows-x86_64 -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.6.0-1.5.8</version>
<classifier>windows-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>1.5.8</version>
<classifier>windows-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>openblas</artifactId>
<version>0.3.21-1.5.8</version>
<classifier>windows-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>5.1.2-1.5.8</version>
<classifier>windows-x86_64</classifier>
</dependency>
<!-- manual tambah untuk platform linux-arm64-->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.6.0-1.5.8</version>
<classifier>linux-arm64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>1.5.8</version>
<classifier>linux-arm64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>openblas</artifactId>
<version>0.3.21-1.5.8</version>
<classifier>linux-arm64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>5.1.2-1.5.8</version>
<classifier>linux-arm64</classifier>
</dependency>
<!-- manual tambah untuk platform linux-x86_64
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.9.0-1.5.10</version>
<classifier>linux-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>1.5.10</version>
<classifier>linux-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>openblas</artifactId>
<version>0.3.26-1.5.10</version>
<classifier>linux-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>6.1.1-1.5.10</version>
<classifier>linux-x86_64</classifier>
</dependency>
-->
<dependency> <dependency>
<groupId>io.javalin</groupId> <groupId>io.javalin</groupId>
<artifactId>javalin</artifactId> <artifactId>javalin</artifactId>
@@ -52,6 +229,11 @@
<artifactId>jackson-databind</artifactId> <artifactId>jackson-databind</artifactId>
<version>2.17.2</version> <version>2.17.2</version>
</dependency> </dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.11.0</version>
</dependency>
</dependencies> </dependencies>
<properties> <properties>
@@ -60,4 +242,13 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> </properties>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.7.0</version>
</extension>
</extensions>
</build>
</project> </project>

View File

@@ -1,14 +1,15 @@
package Audio; package Audio;
import lombok.Getter;
import org.tinylog.Logger; import org.tinylog.Logger;
import java.io.File; import java.io.File;
public class AudioPlayer { public class AudioPlayer {
Bass bass; private final Bass bass;
int deviceid = -1; private int deviceid = -1;
boolean inited = false; @Getter private boolean inited = false;
int playbackhandle = 0; int playbackhandle = 0;
float playbackvolume = 1.0f; 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 * Open Output Device
* @param device device id, starts from 1 * @param device device id, starts from 1
* @param freq output frequency * @param freq output frequency
* @return true if success * @return true if success
*/ */
@SuppressWarnings("UnusedReturnValue")
public boolean OpenDevice(int device, int freq){ 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; 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); 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 * @param value volume value, 0-100
*/ */
public void setMasterVolume(int value){ 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 * @return 0 - 100, or -1 if failed
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
@@ -99,6 +138,11 @@ public class AudioPlayer {
return -1; return -1;
} }
/**
* Get Playback Volume
* is the volume of the currently playing audio file
* @return 0 - 100
*/
public int getPlaybackvolume(){ public int getPlaybackvolume(){
if (playbackvolume<0) if (playbackvolume<0)
return 0; return 0;
@@ -108,6 +152,11 @@ public class AudioPlayer {
return (int)(playbackvolume*100); 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){ public void setPlaybackvolume(int value){
if (value<0) value = 0; if (value<0) value = 0;
if (value>100) value = 100; if (value>100) value = 100;
@@ -119,11 +168,17 @@ public class AudioPlayer {
private float lastplaybackvolume = 1.0f; private float lastplaybackvolume = 1.0f;
/**
* Set Playback Volume to 0
*/
public void Mute(){ public void Mute(){
lastplaybackvolume = playbackvolume; lastplaybackvolume = playbackvolume;
setPlaybackvolume(0); setPlaybackvolume(0);
} }
/**
* Set Playback Volume to last volume before Mute
*/
public void Unmute(){ public void Unmute(){
setPlaybackvolume((int)lastplaybackvolume*100); 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){ public void PlayAudioFile(AudioFileProperties prop, boolean looping, PlaybackEvent event){
if (inited){ if (inited){
if (prop!=null){ if (prop!=null){

View File

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

View File

@@ -3,13 +3,14 @@ package Camera;
import com.fazecast.jSerialComm.SerialPort; import com.fazecast.jSerialComm.SerialPort;
import org.tinylog.Logger; import org.tinylog.Logger;
/** /**
* Pan Tilt Controller * Pan Tilt Controller
* Using PelcoD protocol * 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 { public class PanTiltController {
private final SerialPort serialPort; private SerialPort serialPort;
private final byte cameraid; private final byte cameraid;
/** /**
* Open Pan Tilt Controller * Open Pan Tilt Controller
@@ -17,21 +18,38 @@ public class PanTiltController {
* @param baudrate baudrate used * @param baudrate baudrate used
*/ */
public PanTiltController(String portname, int baudrate, int cameraid){ public PanTiltController(String portname, int baudrate, int cameraid){
serialPort = SerialPort.getCommPort(portname);
serialPort.setBaudRate(baudrate);
this.cameraid = (byte)cameraid; this.cameraid = (byte)cameraid;
if (serialPort.openPort()){ SerialPort[] comports = SerialPort.getCommPorts();
Logger.info("Serial Port {} opened successfully at {}", portname, baudrate); if (comports.length>0){
} else { for (SerialPort port : comports){
Logger.info("Failed to open Serial Port {} at {}", portname, baudrate); 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 * Close Pan Tilt Controller
*/ */
public void Close(){ public void Close(){
serialPort.closePort(); if (serialPort!=null) serialPort.closePort();
Logger.info("Serial Port closed"); Logger.info("Serial Port closed");
} }

View File

@@ -7,24 +7,59 @@ import org.tinylog.Logger;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import static Other.SomeCodes.gson;
public class RtspGrabber { public class RtspGrabber {
private final String rtspUrl; private final String rtspUrl;
private FFmpegFrameGrabber grabber; private FFmpegFrameGrabber grabber;
private final AtomicBoolean isGrabbing = new AtomicBoolean(false); private final AtomicBoolean isGrabbing = new AtomicBoolean(false);
private @Getter Frame lastHQFrame = null; private Frame lastHQFrame = null;
private @Getter Frame lastLQFrame = null; private Frame lastLQFrame = null;
private @Getter String lastHQBase64 = null; private String lastHQBase64 = null;
private @Getter String lastLQBase64 = null; private String lastLQBase64 = null;
private @Getter int HQWidth = 0; private @Getter int HQWidth = 0;
private @Getter int HQHeight = 0; private @Getter int HQHeight = 0;
private @Getter int LQWidth = 0; private @Getter int LQWidth = 0;
private @Getter int LQHeight = 0; private @Getter int LQHeight = 0;
private GrabbingTask grabbingTask;
public RtspGrabber(String ip, String path) { public RtspGrabber(String ip, String path) {
this.rtspUrl = "rtsp://" + ip + path; this.rtspUrl = "rtsp://" + ip + path;
Logger.info("RtspGrabber created with url: " + rtspUrl); 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 * Start grabbing frames from rtsp
* @param useTcp Use tcp instead of udp * @param useTcp Use tcp instead of udp
@@ -34,11 +69,12 @@ public class RtspGrabber {
try{ try{
grabber = FFmpegFrameGrabber.createDefault(rtspUrl); grabber = FFmpegFrameGrabber.createDefault(rtspUrl);
if (useTcp) grabber.setOption("rtsp_transport", "tcp"); if (useTcp) grabber.setOption("rtsp_transport", "tcp");
grabber.setTimeout(2000);
grabber.setImageWidth(width); grabber.setImageWidth(width);
grabber.setImageHeight(height); grabber.setImageHeight(height);
grabber.setPixelFormat(avutil.AV_PIX_FMT_BGR24); grabber.setPixelFormat(avutil.AV_PIX_FMT_BGR24);
grabber.start(); grabber.start();
avutil.av_log_set_level(avutil.AV_LOG_ERROR); avutil.av_log_set_level(avutil.AV_LOG_ERROR);
@@ -49,7 +85,7 @@ public class RtspGrabber {
tt.setOnHQFrameUpdate(value -> { tt.setOnHQFrameUpdate(value -> {
if (value!=null){ if (value!=null){
if (value.imageWidth>0 && value.imageHeight>0){ if (value.imageWidth>0 && value.imageHeight>0){
lastHQFrame = value; setLastHQFrame(value);
HQWidth = value.imageWidth; HQWidth = value.imageWidth;
HQHeight = value.imageHeight; HQHeight = value.imageHeight;
} }
@@ -59,14 +95,15 @@ public class RtspGrabber {
tt.setOnLQFrameUpdate(value -> { tt.setOnLQFrameUpdate(value -> {
if (value!=null){ if (value!=null){
if (value.imageWidth>0 && value.imageHeight>0){ if (value.imageWidth>0 && value.imageHeight>0){
lastLQFrame = value; setLastLQFrame(value);
LQWidth = value.imageWidth; LQWidth = value.imageWidth;
LQHeight = value.imageHeight; LQHeight = value.imageHeight;
} }
} }
}); });
tt.setOnHQBase64Update(value -> lastHQBase64 = value); tt.setOnHQBase64Update(this::setLastHQBase64);
tt.setOnLQBase64Update(value -> lastLQBase64 = value); tt.setOnLQBase64Update(this::setLastLQBase64);
grabbingTask = tt;
new Thread(tt).start(); new Thread(tt).start();
} catch (Exception e){ } catch (Exception e){
@@ -78,17 +115,28 @@ public class RtspGrabber {
* Stop grabbing frames * Stop grabbing frames
*/ */
public void Stop(){ public void Stop(){
isGrabbing.set(false);
if (grabbingTask!=null){
grabbingTask.Stop();
}
grabbingTask = null;
if (grabber!=null) { if (grabber!=null) {
try{ try{
isGrabbing.set(false);
grabber.stop(); grabber.stop();
grabber.releaseUnsafe();
Logger.info("Grabber stopped"); Logger.info("Grabber stopped");
} catch (Exception e){ } catch (Exception e){
Logger.error("Error stopping grabber: " + e.getMessage()); 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; package Other;
import com.google.gson.Gson;
import org.bytedeco.javacpp.Loader; import org.bytedeco.javacpp.Loader;
import org.bytedeco.javacv.Frame; import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter; import org.bytedeco.javacv.Java2DFrameConverter;
@@ -42,6 +43,7 @@ public class SomeCodes {
public static final boolean haveOpenCL = opencv_core.haveOpenCL(); public static final boolean haveOpenCL = opencv_core.haveOpenCL();
public static boolean useOpenCL; public static boolean useOpenCL;
private static final Base64.Encoder base64encoder = java.util.Base64.getEncoder(); private static final Base64.Encoder base64encoder = java.util.Base64.getEncoder();
public static final Gson gson = new Gson();
public static String[] GetAudioFiles(){ public static String[] GetAudioFiles(){
try{ try{
@@ -49,6 +51,7 @@ public class SomeCodes {
} catch (Exception e){ } catch (Exception e){
Logger.error("Error getting audio files: "+e.getMessage()); Logger.error("Error getting audio files: "+e.getMessage());
} }
return new String[0]; 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){ public static @NotNull Properties LoadProperties(String filename){
try{ try{
InputStream is = new FileInputStream(filename); File ff = new File(currentDirectory, filename);
InputStream is = new FileInputStream(ff);
Properties prop = new Properties(); Properties prop = new Properties();
prop.load(is); prop.load(is);
return prop; return prop;
@@ -212,9 +220,16 @@ public class SomeCodes {
return new Properties(); 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){ public static boolean SaveProperties(Properties prop, String filename){
try{ try{
OutputStream os = new FileOutputStream(filename); File ff = new File(currentDirectory, filename);
OutputStream os = new FileOutputStream(ff);
prop.store(os, null); prop.store(os, null);
return true; return true;
} catch (Exception e){ } catch (Exception e){
@@ -236,6 +251,7 @@ public class SomeCodes {
UMat src = new UMat(); UMat src = new UMat();
source.copyTo(src); source.copyTo(src);
UMat dst = new UMat(); UMat dst = new UMat();
opencv_imgproc.resize(src, dst, sz); opencv_imgproc.resize(src, dst, sz);
dst.copyTo(dest); dst.copyTo(dest);
} else { } else {
@@ -249,4 +265,19 @@ public class SomeCodes {
Mat resized = ResizeMat(mat, width, height); Mat resized = ResizeMat(mat, width, height);
return matConverter.convert(resized); 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.Files;
import java.nio.file.Path; import java.nio.file.Path;
@SuppressWarnings("unused")
public class GPIO { public class GPIO {
private static final Path gpioPath = Path.of("/sys/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"); private static final Path gpioUnexportPath = Path.of("/sys/class/gpio/unexport");
public static boolean IsRaspberry64(){ public static boolean HaveGPIO(){
if (Platform.isLinux()){ if (Platform.isLinux()){
if (Platform.isARM()){ if (gpioPath.toFile().isDirectory()){
if (Platform.is64Bit()){ if (gpioExportPath.toFile().isFile()){
if (gpioPath.toFile().isDirectory()){ if (gpioUnexportPath.toFile().isFile()){
if (gpioExportPath.toFile().isFile()){ return true;
if (gpioUnexportPath.toFile().isFile()){ } else Logger.error("GPIO unexport path is not found");
return true; } else Logger.error("GPIO export path is not found");
} else Logger.error("GPIO unexport path is not found"); } else Logger.error("GPIO 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");
} else Logger.info("OS is not Linux"); } else Logger.info("OS is not Linux");
return false; 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 Other.SomeCodes;
import io.javalin.Javalin; import io.javalin.Javalin;
import io.javalin.http.UploadedFile; import io.javalin.http.UploadedFile;
import io.javalin.util.FileUtil;
import io.javalin.util.JavalinException; import io.javalin.util.JavalinException;
import io.javalin.websocket.*; import io.javalin.websocket.*;
import lombok.Getter;
import lombok.Setter;
import org.tinylog.Logger; import org.tinylog.Logger;
import java.nio.file.Files; import java.util.List;
import java.nio.file.Path;
import java.util.Objects; import java.util.Objects;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
@@ -20,20 +22,24 @@ import static io.javalin.apibuilder.ApiBuilder.*;
@SuppressWarnings({"unused"}) @SuppressWarnings({"unused"})
public class WebServer { public class WebServer {
private @Getter @Setter String webusername;
private @Getter @Setter String webpassword;
private final Javalin app; private final Javalin app;
private final Set<WsContext> connectedWebsocketClients = ConcurrentHashMap.newKeySet(); private final Set<WsContext> connectedWebsocketClients = ConcurrentHashMap.newKeySet();
public WebServer(WebsocketEvent event, String webusername, String webpassword){ public WebServer(WebsocketEvent event, String webusername, String webpassword){
this.webusername = webusername;
this.webpassword = webpassword;
app = Javalin.create(config -> { app = Javalin.create(config -> {
config.staticFiles.add("/html"); config.staticFiles.add("/html");
config.router.apiBuilder(()-> path("setting", () ->{ config.router.apiBuilder(()-> path("setting", () ->{
get(ctx -> ctx.json(SettingInfo.getInstance())); get(ctx -> ctx.json(SettingInfo.getInstance()));
path("audiofile",()-> post(ctx -> { path("audiofile",()-> post(ctx -> {
Logger.info("api /setting/audiofile"); Logger.info("api /setting/audiofile");
String audiofile1 = ctx.formParam("1"); String audiofile1 = ctx.formParam("preset1");
String audiofile2 = ctx.formParam("2"); String audiofile2 = ctx.formParam("preset2");
String audiofile3 = ctx.formParam("3"); String audiofile3 = ctx.formParam("preset3");
String audiofile4 = ctx.formParam("4"); String audiofile4 = ctx.formParam("preset4");
String audiofile5 = ctx.formParam("5"); String audiofile5 = ctx.formParam("preset5");
Logger.info("audiofile1: {}", audiofile1); Logger.info("audiofile1: {}", audiofile1);
Logger.info("audiofile2: {}", audiofile2); Logger.info("audiofile2: {}", audiofile2);
Logger.info("audiofile3: {}", audiofile3); Logger.info("audiofile3: {}", audiofile3);
@@ -41,41 +47,59 @@ public class WebServer {
Logger.info("audiofile5: {}", audiofile5); Logger.info("audiofile5: {}", audiofile5);
Properties prop = SomeCodes.LoadProperties("config.properties"); Properties prop = SomeCodes.LoadProperties("config.properties");
prop.setProperty("audiofile1", audiofile1!=null?audiofile1:""); prop.setProperty("AudioFile01", audiofile1!=null?audiofile1:"");
prop.setProperty("audiofile2", audiofile2!=null?audiofile2:""); prop.setProperty("AudioFile02", audiofile2!=null?audiofile2:"");
prop.setProperty("audiofile3", audiofile3!=null?audiofile3:""); prop.setProperty("AudioFile03", audiofile3!=null?audiofile3:"");
prop.setProperty("audiofile4", audiofile4!=null?audiofile4:""); prop.setProperty("AudioFile04", audiofile4!=null?audiofile4:"");
prop.setProperty("audiofile5", audiofile5!=null?audiofile5:""); prop.setProperty("AudioFile05", audiofile5!=null?audiofile5:"");
if (SaveProperties(prop, "config.properties")){ if (SaveProperties(prop, "config.properties")){
Logger.info("audiofile saved"); Logger.info("audiofile saved");
ctx.status(200); ctx.status(200);
} else { } else {
Logger.error("Failed to save audiofile"); Logger.error("Failed to save audiofile");
ctx.status(400); ctx.status(400);
ctx.result("Failed to save audiofile");
} }
})); }));
path("uploadaudiofile", ()-> post(ctx -> { path("uploadaudiofile", ()-> post(ctx -> {
UploadedFile file = ctx.uploadedFile("file"); List<UploadedFile> uploadedFileList = ctx.uploadedFiles();
if (file!=null){ int size = uploadedFileList.size();
try { if (size>0){
Path targetsave = audioPath.resolve(file.filename()); uploadedFileList.forEach(ff ->{
Files.copy(file.content(), targetsave); String targetsave = audioPath.resolve(ff.filename()).toString();
Logger.info("Uploaded file: {}, size: {} saved at {}", file.filename(),file.size(), targetsave); FileUtil.streamToFile(ff.content(), targetsave);
} catch (Exception e){ Logger.info("Uploaded file: {}", targetsave);
Logger.error("Failed to save uploaded file: {}, Message: {}", file.filename(), e.getMessage()); });
} ctx.status(200);
ctx.result("UploadedFiles: "+size);
} else {
ctx.status(400);
ctx.result("No file uploaded");
} }
})); }));
path("weblogin", ()-> post(ctx -> { path("weblogin", ()-> post(ctx -> {
String username = ctx.formParam("username"); String username = ctx.formParam("username");
String password = ctx.formParam("password"); String password = ctx.formParam("password");
Logger.info("api /setting/weblogin");
Logger.info("username: {}", username);
Logger.info("password: {}", password);
Properties prop = SomeCodes.LoadProperties("config.properties"); Properties prop = SomeCodes.LoadProperties("config.properties");
prop.setProperty("WebUsername", ValidString(username)?username:"admin"); prop.setProperty("WebUsername", ValidString(username)?username:"admin");
prop.setProperty("WebPassword", ValidString(password)?password:"bandara"); prop.setProperty("WebPassword", ValidString(password)?password:"bandara");
if (SaveProperties(prop, "config.properties")){ 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 { } else {
Logger.error("Failed to save weblogin");
ctx.status(400); ctx.status(400);
ctx.result("Failed to save weblogin");
} }
})); }));
path("camera",()-> post(ctx -> { path("camera",()-> post(ctx -> {
@@ -83,6 +107,11 @@ public class WebServer {
String camera_port = ctx.formParam("port"); String camera_port = ctx.formParam("port");
String camera_username = ctx.formParam("username"); String camera_username = ctx.formParam("username");
String camera_password = ctx.formParam("password"); 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"); Properties prop = SomeCodes.LoadProperties("config.properties");
prop.setProperty("Camera_ip", ValidString(camera_ip)?camera_ip:"192.168.0.4"); 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_user", ValidString(camera_username)?camera_username:"root");
prop.setProperty("Camera_password", ValidString(camera_password)?camera_password:"password"); prop.setProperty("Camera_password", ValidString(camera_password)?camera_password:"password");
if (SaveProperties(prop, "config.properties")){ if (SaveProperties(prop, "config.properties")){
Logger.info("IP camera setting saved");
ctx.status(200); ctx.status(200);
if (event!=null) event.NewCameraConfiguration();
} else { } else {
Logger.error("Failed to save IP camera setting");
ctx.status(400); ctx.status(400);
ctx.result("Failed to save IP camera setting");
} }
})); }));
@@ -103,7 +136,7 @@ public class WebServer {
if (ctx.sessionAttribute("username")==null) { if (ctx.sessionAttribute("username")==null) {
// belum login // belum login
ctx.redirect("/login.html"); ctx.redirect("/login.html");
} else if (Objects.equals(ctx.sessionAttribute("username"), webusername)){ } else if (Objects.equals(ctx.sessionAttribute("username"), this.webusername)){
// sudah login // sudah login
ctx.redirect("/index.html"); ctx.redirect("/index.html");
} else { } else {
@@ -127,11 +160,12 @@ public class WebServer {
app.post("/login", ctx ->{ app.post("/login", ctx ->{
String username = ctx.formParam("username"); String username = ctx.formParam("username");
String password = ctx.formParam("password"); 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.sessionAttribute("username", username);
ctx.redirect("/index.html"); ctx.redirect("/index.html");
} else { } 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) { if (event!=null) {
WebsocketReply reply = event.onWebsocketCommand(command); WebsocketReply reply = event.onWebsocketCommand(command);
if (reply!=null) ctx.sendAsClass(reply, WebsocketReply.class); if (reply!=null) ctx.sendAsClass(reply, WebsocketReply.class);
//if (reply!=null) SendtoAll(reply);
} }
} catch (Exception e){ } catch (Exception e){
Logger.error("Failed to parse WebSocketCommand message: {}", e.getMessage()); Logger.error("Failed to parse WebSocketCommand message: {}", e.getMessage());
ctx.closeSession();
connectedWebsocketClients.remove(ctx);
} }
}); });
@@ -196,13 +233,13 @@ public class WebServer {
} }
WsConnectHandler connectws = ws ->{ 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)); //ws.headerMap().forEach((key, value) -> Logger.info("HeaderMap {}: {}", key, value));
connectedWebsocketClients.add(ws); connectedWebsocketClients.add(ws);
}; };
WsCloseHandler closews = 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); connectedWebsocketClients.remove(ws);
}; };

View File

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

View File

@@ -2,22 +2,23 @@ package id.co.gtc;
import Audio.AudioFileProperties; import Audio.AudioFileProperties;
import Audio.AudioPlayer; import Audio.AudioPlayer;
import Audio.Bass;
import Audio.PlaybackEvent; import Audio.PlaybackEvent;
import Camera.PanTiltController; import Camera.PanTiltController;
import Camera.RtspGrabber; import Camera.RtspGrabber;
import Camera.VapixProtocol; import Camera.VapixProtocol;
import Other.SomeCodes; import Other.SomeCodes;
import Web.WebServer; import SBC.ProcessorStatus;
import Web.WebsocketCommand; import SBC.RamInformation;
import Web.WebsocketEvent; import SBC.SystemInformation;
import Web.WebsocketReply; import Web.*;
import com.google.gson.JsonObject;
import org.bytedeco.opencv.global.opencv_core; import org.bytedeco.opencv.global.opencv_core;
import org.tinylog.Logger; import org.tinylog.Logger;
import java.io.File; import java.io.File;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Objects; import java.util.*;
import java.util.Properties;
import static Other.SomeCodes.*; import static Other.SomeCodes.*;
@@ -28,6 +29,11 @@ public class Main {
private static PanTiltController panTiltController; private static PanTiltController panTiltController;
private static AudioFileProperties audioFileProperties; private static AudioFileProperties audioFileProperties;
private static VapixProtocol vapixProtocol; 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 // Application start from here
public static void main(String[] args) { public static void main(String[] args) {
@@ -37,8 +43,11 @@ public class Main {
if (rtspGrabber!=null) rtspGrabber.Stop(); if (rtspGrabber!=null) rtspGrabber.Stop();
if (panTiltController!=null) panTiltController.Close(); if (panTiltController!=null) panTiltController.Close();
if (vapixProtocol!=null) vapixProtocol.Close(); if (vapixProtocol!=null) vapixProtocol.Close();
if (timer!=null) timer.cancel();
})); }));
init_system_monitoring();
init_properties(); init_properties();
init_audiofiles(); init_audiofiles();
@@ -49,6 +58,29 @@ public class Main {
init_Vapix(); 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(){ private static void init_properties(){
if (ExtractResource("/tinylog.properties", currentDirectory)==null) Logger.error("Failed to extract tinylog.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"); 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 username = config.getProperty("Camera_user");
String password = config.getProperty("Camera_password"); String password = config.getProperty("Camera_password");
if (ValidString(ip)){ if (ValidString(ip)){
if (ValidInteger(port)){ if (ValidPortNumber(port)){
if (ValidString(username)){ if (ValidString(username)){
if (ValidString(password)){ if (ValidString(password)){
vapixProtocol = new VapixProtocol(ip, Integer.parseInt(port), username, password); if (IpIsReachable(ip)){
Logger.info("Camera Product Number: "+vapixProtocol.GetProductNumber()); Logger.info("Camera IP is reachable");
Logger.info("Camera Serial Number: "+vapixProtocol.GetSerialNumber()); vapixProtocol = new VapixProtocol(ip, Integer.parseInt(port), username, password);
if (vapixProtocol.PTZEnabled()){ Logger.info("Camera Product Number: "+vapixProtocol.GetProductNumber());
Logger.info("PTZ Enabled"); Logger.info("Camera Serial Number: "+vapixProtocol.GetSerialNumber());
} else Logger.error("PTZ Disabled"); if (vapixProtocol.PTZEnabled()){
Logger.info("Max Zoom: "+vapixProtocol.GetPTZMaxZoom()); Logger.info("PTZ Enabled");
Logger.info("Min Zoom: "+vapixProtocol.GetPTZMinZoom()); } else Logger.error("PTZ Disabled");
} else Logger.error("Invalid Camera Password"); Logger.info("Max Zoom: "+vapixProtocol.GetPTZMaxZoom());
} else Logger.error("Invalid Camera Username"); Logger.info("Min Zoom: "+vapixProtocol.GetPTZMinZoom());
} else Logger.error("Invalid Camera Port"); } else Logger.error("Camera IP is not reachable");
} else Logger.error("Invalid Camera IP"); } 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() { private static void init_pantiltcontroller() {
@@ -112,22 +147,32 @@ public class Main {
Properties config = SomeCodes.LoadProperties("config.properties"); Properties config = SomeCodes.LoadProperties("config.properties");
String targetip = config.getProperty("Camera_ip"); String targetip = config.getProperty("Camera_ip");
String rtsppath = config.getProperty("Camera_Rtsp_path"); String rtsppath = config.getProperty("Camera_Rtsp_path");
rtspGrabber = null;
if (ValidString(targetip)){ if (ValidString(targetip)){
if (ValidString(rtsppath)){ if (ValidString(rtsppath)){
rtspGrabber = new RtspGrabber(targetip, rtsppath); if (IpIsReachable(targetip)){
Logger.info("Camera IP is reachable");
rtspGrabber = new RtspGrabber(targetip, rtsppath);
rtspGrabber.Start(true, 1920, 1080); rtspGrabber.Start(true, 1920, 1080);
} else Logger.error("Invalid Camera Path"); } else Logger.error("Camera IP is not reachable");
} else Logger.error("Invalid Camera IP"); } else Logger.error("Camera Path is not valid string");
} else Logger.error("Camera IP is not valid string");
} }
private static void init_audio() { private static void init_audio() {
audioPlayer = new AudioPlayer(); audioPlayer = new AudioPlayer();
audioPlayer.DetectOutputDevices(); audioPlayer.DetectOutputDevices();
audioPlayer.OpenDevice(1,48000); int devid = audioPlayer.FindDeviceIDWithName("USB");
audioPlayer.setMasterVolume(100); if (devid<1) devid = audioPlayer.FindDeviceIDWithName("Speakers");
audioPlayer.setPlaybackvolume(100);
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() { static PlaybackEvent pe = new PlaybackEvent() {
@@ -217,6 +262,7 @@ public class Main {
audioFileProperties = afp; audioFileProperties = afp;
audioPlayer.PlayAudioFile(afp, true, pe); audioPlayer.PlayAudioFile(afp, true, pe);
return new WebsocketReply("PLAY AUDIO", afp.filename); 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", "Failed to open audio file "+filename);
} else return new WebsocketReply("PLAY AUDIO", "Audio file not found : "+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)); } 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": case "GET BASE64":
if (rtspGrabber!=null){ if (rtspGrabber!=null){
if (Objects.equals(command.data,"HQ")) 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 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"); } else return new WebsocketReply("GET BASE64", "RTSP Grabber not initialized");
case "GET RESOLUTION": case "GET RESOLUTION":
if (vapixProtocol!=null){ if (vapixProtocol!=null){
int[] res = vapixProtocol.GetCurrentResolution(1); int[] res = vapixProtocol.GetCurrentResolution(1);
return new WebsocketReply("GET RESOLUTION", String.format("%dx%d", res[0], res[1])); return new WebsocketReply("GET RESOLUTION", String.format("%dx%d", res[0], res[1]));
} else return new WebsocketReply("GET RESOLUTION", "VapixProtocol not initialized"); } 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: default:
return new WebsocketReply("UNKNOWN COMMAND", command.command); 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() { private static void init_webserver() {