Compare commits

...

16 Commits

Author SHA1 Message Date
a1b6ec54ab Patch 17/03/2025 2025-03-17 13:35:05 +07:00
123a89eec9 Patch 04/03/2025 2025-03-04 12:25:02 +07:00
e1d129fa07 Kirim ke Bandara 27/02/2025 2025-02-27 15:11:56 +07:00
5f8d1946c0 YOLODetector is working (25/02/2025) 2025-02-25 13:56:57 +07:00
ce5c8d5946 Change SBC to Jetson Orin 2025-02-25 07:35:38 +07:00
21e8ab1e82 Change SBC to Jetson Orin 2024-12-03 15:59:18 +07:00
ea891d2744 GPIO checked for Raspberry Pi 5B 2024-12-03 15:22:44 +07:00
f2d560049f Changes by Monika DV 2024-11-28 15:00:37 +07:00
f515f829cf GPIO control.
Bug fix.
2024-11-15 11:22:16 +07:00
f7e4ee68d6 GPIO control.
Bug fix.
2024-11-14 10:49:50 +07:00
0942c9936c GPIO control.
Bug fix.
2024-11-14 10:23:50 +07:00
10fad0e192 Trying using socket.io instead of websocket, because some device incompatibility 2024-11-13 14:52:17 +07:00
19da5914ac enable better FPS skipping mechanism. For raspberry , optimal at 10 fps. 2024-11-13 09:32:05 +07:00
d2f924f5db enable better FPS skipping mechanism. For raspberry , optimal at 10 fps. 2024-11-13 09:29:38 +07:00
068316bb62 Changes by admin 2024-11-13 08:36:01 +07:00
f7f711d3fe Add more functions 2024-11-13 08:35:32 +07:00
44 changed files with 4775 additions and 766 deletions

View File

@@ -4,6 +4,7 @@
<inspection_tool class="ExtractMethodRecommender" enabled="true" level="WARNING" enabled_by_default="true">
<option name="minLength" value="745" />
</inspection_tool>
<inspection_tool class="GrazieInspection" enabled="false" level="GRAMMAR_ERROR" enabled_by_default="false" />
<inspection_tool class="HttpUrlsUsage" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="ignoredUrls">
<list>
@@ -35,6 +36,7 @@
</list>
</option>
</inspection_tool>
<inspection_tool class="LanguageDetectionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MethodNameSameAsClassName" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="RedundantCast" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<file url="file://$PROJECT_DIR$" libraries="{jquery.dataTables}" />
</component>
</project>

1
.idea/misc.xml generated
View File

@@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">

View File

@@ -4,13 +4,14 @@ Camera_port = 80
Camera_user = root
Camera_password = password
Camera_Rtsp_path = /axis-media/media.amp
#Camera_Rtsp_path = /video1
AudioFile01 = elangWav.wav
AudioFile02 = gunshotsWav.wav
AudioFile03 = pinkNoiseWav.wav
AudioFile04 = 04.mp3
AudioFile05 = 05.mp3
AudioVolumeOutput = 100
SerialPort = /dev/ttyUSB0
SerialPort = /dev/ttyTHS1
SerialBaudRate = 9600
PanTiltID = 1
WebUsername = admin

View File

@@ -10,6 +10,7 @@
<script src="public/js/jquery-3.7.1.min.js"></script>
<script src="public/js/jquery.dataTables.min.js"></script>
<script src="public/js/all.min.js"></script>
<script src="public/js/socket.io.min.js"></script>
<link rel="stylesheet" href="public/style/all.min.css">
<script src="index.js"></script>
</head>
@@ -29,50 +30,75 @@
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item pad-menu">
<a class="nav-link" href="setting.html">Setting</a>
<a class="nav-link nav-setting" href="setting.html">Setting</a>
</li>
<li class="nav-item pad-menu">
<form action="/logout" method="get">
<button class="btn btn-outline-light" type="submit">Logout</button>
<button class="btn btn-outline-light nav-logout" type="submit">Logout</button>
</form>
</li>
</ul>
</div>
</div>
</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 -->
<div class="container-fluid container3">
<div class="container-fluid container3 min90">
<div class="row">
<div class="col-12 col-sm-12 col-md-12 col-lg-12 col-xl-8">
<div class="outside">
<img id="camerastream" src="public/images/not-available.png" class="img-cctv class100" alt="">
</div>
<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>
</div>
<div class="col-6 col-sm-6 col-md-4 col-lg-4 col-xl-4">
<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">High Quality Video</label>
<div class="col-12 col-sm-4 col-md-5 col-lg-5 col-xl-5">
<div class="row">
<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">0 %</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">0 °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">0 %</p>
</div>
</div>
<div class="col">
<div class="d-flex align-items-center">
<span class="fa-solid fa-upload icon-system"></span>
<p id="ethernet_TX" class="padleft">0 B/s</p>
</div>
</div>
<div class="col">
<div class="d-flex align-items-center">
<span class="fa-solid fa-download icon-system"></span>
<p id="ethernet_RX" class="padleft">0 B/s</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>
@@ -82,7 +108,7 @@
<br>
<div class="row">
<div class="col-3 ">
<p>Pan Speed</p>
<p class="text-dash">Pan Speed</p>
<div class="btn-group-vertical">
<button id="pan1" class="btn btn-outline-dark btn-sm" value="16" onclick="set_pan_speed(16)">1</button>
<button id="pan2" class="btn btn-outline-dark btn-sm" value="32" onclick="set_pan_speed(32)">2</button>
@@ -95,7 +121,7 @@
<div class="row">
<div class="col"></div>
<div class="col">
<a onmousedown="btn_up()" onmouseup="stop_movement()">
<a onmousedown="send_tilt_up()" onmouseup="send_stop_movement()">
<i class="btn-nav fa-solid fa-caret-up pad-top" title="Up"></i>
</a>
</div>
@@ -103,17 +129,17 @@
</div>
<div class="row">
<div class="col">
<a onmousedown="btn_left()" onmouseup="stop_movement()">
<a onmousedown="send_pan_left()" onmouseup="send_stop_movement()">
<i class="btn-nav fa-solid fa-caret-left pad-left" title="Left"></i>
</a>
</div>
<div class="col">
<a>
<i id="btn_center" class="btn-nav-center fa-solid fa-circle"></i>
<a onmousedown="send_stop_movement()">
<i id="btn_center" class="btn-nav-center fa-solid fa-circle" title="Stop Movement"></i>
</a>
</div>
<div class="col">
<a onmousedown="btn_right()" onmouseup="stop_movement()">
<a onmousedown="send_pan_right()" onmouseup="send_stop_movement()">
<i class="btn-nav fa-solid fa-caret-right pad-right" title="Right"></i>
</a>
</div>
@@ -121,7 +147,7 @@
<div class="row">
<div class="col"></div>
<div class="col">
<a onmousedown="btn_down()" onmouseup="stop_movement()">
<a onmousedown="send_tilt_down()" onmouseup="send_stop_movement()">
<i class="i btn-nav fa-solid fa-caret-down pad-bottom" title="Down"></i>
</a>
</div>
@@ -130,7 +156,7 @@
</div>
</div>
<div class="col-3">
<p>Tilt Speed</p>
<p class="text-dash">Tilt Speed</p>
<div class="btn-group-vertical">
<button id="tilt1" class="btn btn-outline-dark btn-sm" value="16" onclick="set_tilt_speed(16)">1</button>
<button id="tilt2" class="btn btn-outline-dark btn-sm" value="32" onclick="set_tilt_speed(32)">2</button>
@@ -140,14 +166,14 @@
</div>
</div>
<div>
<p class="text-center">Zoom</p>
<p class="text-center text-dash">Zoom</p>
<div class="row">
<div class="col-10">
<input id="zoom" type="range" class="form-range" min="1" max="10909" oninput="this.nextElementSibling.value = this.value">
<output class="output"></output>
</div>
<div class="col-2">
<button class="btn btn-primary" onclick="set_zoom()">SET</button>
<button class="btn btn-primary" id="set_zoom" onclick="set_zoom()">SET</button>
</div>
</div>
</div>
@@ -167,7 +193,7 @@
</a>
</div>
<br>
<p> Content 1</p>
<p id="desc_content1"> Content 1</p>
</div>
<div class="col">
@@ -180,7 +206,7 @@
</a>
</div>
<br>
<p> Content 2</p>
<p id="desc_content2"> Content 2</p>
</div>
<div class="col">
@@ -193,7 +219,7 @@
</a>
</div>
<br>
<p> Content 3</p>
<p id="desc_content3"> Content 3</p>
</div>
<div class="col">
@@ -206,7 +232,7 @@
</a>
</div>
<br>
<p> Content 4</p>
<p id="desc_content4"> Content 4</p>
</div>
<div class="col">
@@ -219,12 +245,12 @@
</a>
</div>
<br>
<p>Content 5</p>
<p id="desc_content5">Content 5</p>
</div>
</div>
<br>
<div>
<label for="customRange" class="form-label">Volume</label>
<label for="customRange" class="form-label text-dash">Volume</label>
<div class="row">
<div class="col-10 pad-bar">
@@ -233,10 +259,10 @@
</div>
<div class="col-2">
<div>
<button id="mute" class="btn-mute show" onclick="mute()" title="mute">
<button id="mute" class="btn-mute show" onclick="send_mute()" title="mute">
<i class="fa-solid fa-volume-high"></i>
</button>
<button id="unmute" class="btn-mute hide" onclick="unmute()" title="unmute">
<button id="unmute" class="btn-mute hide" onclick="send_unmute()" title="unmute">
<i class="fa-solid fa-volume-xmark"></i>
</button>
</div>
@@ -250,7 +276,7 @@
<!-- SECTION 3 -->
<div class="container-fluid footer1 outside">
<p>2024 Copyright &copy; Galva Technologies. All rights reserved.</p>
<p class="footer">2024 Copyright &copy; Galva Technologies. All rights reserved.</p>
</div>
</body>
</html>

View File

@@ -12,127 +12,111 @@ let ws;
let camerastream;
// socketio
let socketio;
let files = [null,null,null,null,null];
let getbase64handle;
let getsysteminfohandle;
let $zoom;
let $mute;
let $unmute;
window.addEventListener("unload",function (){
clearInterval(getbase64handle);
clearInterval(getsysteminfohandle);
send_stop_audio();
// wait a while
const start = Date.now();
while(Date.now()-start<200){}
ws.close();
})
window.addEventListener("load", function (){
$zoom = $('#zoom');
$mute = $('#mute');
$unmute = $('#unmute');
document.addEventListener("DOMContentLoaded", function(){
camerastream = document.getElementById("camerastream");
setInterval(function (){
if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({
command: "GET BASE64",
data: video_quality
}));
// pilihan komunikasi, disable salah satu
initialize_socketio();
//initialize_websocket();
} else update_camerastream(null);
},10);
ws = new WebSocket("/ws");
ws.onopen = function(){
console.log("Connected to the server");
// request basic parameters
ws.send(JSON.stringify({
command: "GET MAX ZOOM",
data: 0
}));
ws.send(JSON.stringify({
command: "GET VOLUME",
data: 0
}));
ws.send(JSON.stringify({
command: "GET ZOOM",
data: 0
}));
ws.send(JSON.stringify({
command: "GET RESOLUTION",
data: 0
}));
}
ws.onmessage = function(event){
//console.log("Received data from server: " + event.data);
/**
* @type {{reply: string, data: string|number, additional: string}} dx
*/
let dx = JSON.parse(event.data);
switch (dx.reply){
case "GET BASE64":
if (dx.data.startsWith("data:image/jpeg;base64,")){
update_camerastream(dx.data);
} else update_camerastream(null);
if (dx.additional && dx.additional.length>0){
$('#streaming_status').html(dx.additional);
}
break;
case "SET VOLUME":
console.log("Set Volume: "+dx.data);
break;
case "GET VOLUME":
console.log("Get Volume: "+dx.data);
$('#customRange').val(dx.data);
break;
case "GET MAX ZOOM":
console.log("Get Max Zoom: "+dx.data);
$('#zoom')
.attr("max", dx.data)
.attr("min", 1);
break;
case "GET ZOOM":
console.log("Get Zoom: "+dx.data);
$('#zoom').val(dx.data);
break;
case "GET RESOLUTION":
console.log("Get Resolution: "+dx.data);
break;
case "PAN LEFT":
console.log("Pan Left");
break;
case "PAN RIGHT":
console.log("Pan Right");
break;
case "TILT UP":
console.log("Tilt Up");
break;
case "TILT DOWN":
console.log("Tilt Down");
break;
case "STOP MOVEMENT":
console.log("Stop Movement");
break;
case "SET ZOOM":
console.log("Set Zoom: "+dx.data);
break;
case "PLAY AUDIO":
console.log("Play Audio: "+dx.data);
if (dx.data.startsWith("Failed")){
alert(dx.data);
} else $('#status_player').html("Playing Audio "+dx.data);
break;
case "STOP AUDIO":
console.log("Stop Audio");
$('#status_player').html("Stop Playback");
break;
case 'SET VIDEO QUALITY':
console.log("Set Video Quality: "+dx.data);
break;
}
}
ws.onclose = function(){
console.log("Connection closed");
}
ws.onerror = function(){
console.log("Error in connection");
}
// Update camera stream every 50ms / 20fps
getbase64handle = setInterval(function (){
send_get_base64();
},1000/15);
getsysteminfohandle = setInterval(function (){
send_get_system_info();
}, 5000);
set_pan_speed(32);
set_tilt_speed(32);
});
})
function initialize_socketio(){
socketio = io(":3001/socketio");
if (socketio){
socketio.on("connect",()=>{
console.log("Socket.io Connected to the server");
send_get_max_zoom();
send_get_volume();
send_get_zoom();
send_get_resolution();
send_get_audiofiles();
})
socketio.on("disconnect",(reason)=>{
console.log("Socket.io Disconnected from server because "+reason);
})
socketio.on("connect_error",(error)=>{
console.log("Socket.io Connection error "+error);
});
socketio.on("message",(data)=>{
let dx = JSON.parse(data);
process_command(dx);
});
}
}
function initialize_websocket(){
ws = new WebSocket("/ws");
if (ws){
ws.onopen = function(){
console.log("Connected to the server");
// request basic parameters
send_get_max_zoom();
send_get_volume();
send_get_zoom();
send_get_resolution();
send_get_audiofiles();
}
ws.onmessage = function(event){
//console.log("Received data from server: " + event.data);
/**
* @type {{reply: string, data: string|number, additional: string}} dx
*/
let dx = JSON.parse(event.data);
process_command(dx);
}
ws.onclose = function(){
console.log("Connection closed");
}
ws.onerror = function(){
console.log("Error in connection");
}
}
}
/**
* Update camera stream
@@ -150,35 +134,62 @@ function update_camerastream(value){
}
/**
* Disable play button
* @param index 1 - 5
*/
function play_off(index){
$('#div'+index).prop("className", "button-container div-disabled");
$('#btn_play'+index).prop("className", "btn-play1 show text-light");
$('#btn_play'+index)
.prop("className", "btn-play1 show text-light");
}
/**
* Enable play button
* @param index 1 - 5
*/
function play_on(index){
let content = $('#desc_content'+index).html();
$('#div'+index).prop("className", "button-container");
$('#btn_play'+index).prop("className", "btn-play1 show");
$('#btn_play'+index)
.prop("className", "btn-play1 show")
.prop("title","Play "+content);
$('#btn_stop'+index)
.prop("title","Stop "+content);
}
/**
* Show play button and hide stop button
* @param index 1 - 5
*/
function show_play_and_hide_stop(index){
$('#btn_play'+index).prop("className", "btn-play1 show");
$('#btn_stop'+index).prop("className", "btn-play1 hide");
}
/**
* Show stop button and hide play button
* @param index 1 - 5
*/
function show_stop_and_hide_play(index){
$('#btn_play'+index).prop("className", "btn-play1 hide");
$('#btn_stop'+index).prop("className", "btn-play1 show");
}
/**
* Enable all play buttons
*/
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);
}
}
/**
* When play button is clicked
* @param index 1 - 5
*/
function play_button(index){
show_stop_and_hide_play(index);
$('#status_player').html("Play");
@@ -188,26 +199,159 @@ function play_button(index){
send_play_audio(index);
}
/**
* When stop button is clicked
* @param index 1 - 5
*/
function stop_button(index){
show_play_and_hide_stop(index);
$('#status_player').html("Stop");
allplay();
send_stop_audio();
}
function send_get_audiofiles(){
let cmd = JSON.stringify({
command: "GET AUDIOFILES",
data: 0
});
if (ws){
if (ws.readyState===WebSocket.OPEN){
ws.send(cmd)
} else console.log("WebSocket is not open");
} else if (socketio){
if (socketio.connected){
socketio.emit("message",cmd);
} else console.log("Socket.io is not connected");
}
}
function send_get_resolution(){
let cmd = JSON.stringify({
command: "GET RESOLUTION",
data: 0
});
if (ws){
if (ws.readyState === WebSocket.OPEN){
ws.send(cmd);
} else console.log("WebSocket is not open");
} else if (socketio){
if (socketio.connected){
socketio.emit("message",cmd);
} else console.log("Socket.io is not connected");
}
}
function send_get_zoom(){
let cmd = JSON.stringify({
command: "GET ZOOM",
data: 0
});
if (ws){
if (ws.readyState===WebSocket.OPEN){
ws.send(cmd);
} else console.log("WebSocket is not open");
} else if (socketio){
if (socketio.connected){
socketio.emit("message",cmd);
} else console.log("Socket.io is not connected");
}
}
function send_get_volume(){
let cmd = JSON.stringify({
command: "GET VOLUME",
data: 0
});
if (ws){
if (ws.readyState === WebSocket.OPEN){
ws.send(cmd);
} else console.log("WebSocket is not open");
} else if (socketio){
if (socketio.connected){
socketio.emit("message",cmd);
} else console.log("Socket.io is not connected");
}
}
function send_get_max_zoom(){
let cmd = JSON.stringify({
command: "GET MAX ZOOM",
data: 0
});
if (ws){
if (ws && ws.readyState===WebSocket.OPEN){
ws.send(cmd);
} else console.log("WebSocket is not open");
} else if (socketio){
if (socketio.connected){
socketio.emit("message",cmd);
} else console.log("Socket.io is not connected");
}
}
function send_get_system_info(){
let cmd = JSON.stringify({
command: "GET SYSTEM INFO",
data: 0
});
if (ws){
if (ws.readyState === WebSocket.OPEN){
ws.send(cmd);
} else console.log("WebSocket is not open");
} else if (socketio){
if (socketio.connected){
socketio.emit("message",cmd);
} else console.log("Socket.io is not connected");
}
}
/**
* Send command to stop audio
*/
function send_stop_audio(){
if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({
command: "STOP AUDIO",
data: 0
}));
} else console.log("WebSocket is not open");
let cmd = JSON.stringify({
command: "STOP AUDIO",
data: 0
});
if (ws){
if (ws.readyState === WebSocket.OPEN){
ws.send(cmd);
} else console.log("WebSocket is not open");
} else if (socketio){
if (socketio.connected){
socketio.emit("message",cmd);
} else console.log("Socket.io is not connected");
}
}
function send_get_base64(){
let cmd = JSON.stringify({
command: "GET BASE64",
data: video_quality
});
if (ws){
if (ws.readyState === WebSocket.OPEN){
ws.send(cmd);
return;
} else console.log("WebSocket is not open");
} else if (socketio){
if (socketio.connected){
socketio.emit("message",cmd);
return;
} else console.log("Socket.io is not connected");
}
update_camerastream(null);
}
/**
@@ -215,119 +359,198 @@ function send_stop_audio(){
* @param {number} index 1 - 5
*/
function send_play_audio(index){
if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({
command: "PLAY AUDIO",
data: index
}));
} else console.log("WebSocket is not open");
let cmd = JSON.stringify({
command: "PLAY AUDIO",
data: index
});
if (ws){
if (ws.readyState === WebSocket.OPEN){
ws.send(cmd);
} else console.log("WebSocket is not open");
} else if (socketio){
if (socketio.connected){
socketio.emit("message",cmd);
} else console.log("Socket.io is not connected");
}
}
function unmute(){
$('#mute').prop("className", "btn-mute show");
$('#unmute').prop("className", "btn-mute hide");
console.log("unmute")
if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({
command: "UNMUTE",
data: 0
}));
} else console.log("WebSocket is not open");
}
function mute(){
$('#mute').prop("className", "btn-mute hide");
$('#unmute').prop("className", "btn-mute show");
function send_unmute(){
let cmd = JSON.stringify({
command: "UNMUTE",
data: 0
});
if (ws){
if (ws.readyState === WebSocket.OPEN){
ws.send(cmd);
} else console.log("WebSocket is not open");
} else if (socketio){
if (socketio.connected){
socketio.emit("message",cmd);
} else console.log("Socket.io is not connected");
}
}
function send_mute(){
let cmd = JSON.stringify({
command: "MUTE",
data: 0
});
if (ws){
if (ws.readyState === WebSocket.OPEN){
ws.send(cmd);
} else console.log("WebSocket is not open");
} else if (socketio){
if (socketio.connected){
socketio.emit("message",cmd);
} else console.log("Socket.io is not connected");
}
}
function send_tilt_up(){
let cmd = JSON.stringify({
command: "TILT UP",
data: tilt_speed
});
if (ws){
if (ws.readyState === WebSocket.OPEN){
ws.send(cmd);
btn_center_off()
} else console.log("WebSocket is not open");
} else if (socketio){
if (socketio.connected){
socketio.emit("message",cmd);
btn_center_off()
} else console.log("Socket.io is not connected");
}
}
function send_tilt_down(){
let cmd = JSON.stringify({
command: "TILT DOWN",
data: tilt_speed
});
if (ws){
if (ws.readyState === WebSocket.OPEN){
ws.send(cmd);
btn_center_off()
} else console.log("WebSocket is not open");
} else if (socketio){
if (socketio.connected){
socketio.emit("message",cmd);
btn_center_off()
} else console.log("Socket.io is not connected");
}
}
function send_pan_left(){
let cmd = JSON.stringify({
command: "PAN LEFT",
data: pan_speed
});
if (ws){
if (ws.readyState === WebSocket.OPEN){
ws.send(cmd);
btn_center_off();
} else console.log("WebSocket is not open");
} else if (socketio){
if (socketio.connected){
socketio.emit("message",cmd);
btn_center_off();
} else console.log("Socket.io is not connected");
}
}
function send_pan_right(){
let cmd = JSON.stringify({
command: "PAN RIGHT",
data: pan_speed
});
if (ws){
if (ws.readyState === WebSocket.OPEN){
ws.send(cmd);
btn_center_off()
} else console.log("WebSocket is not open");
} else if (socketio){
if (socketio.connected){
socketio.emit("message",cmd);
btn_center_off()
} else console.log("Socket.io is not connected");
}
}
function send_stop_movement(){
let cmd = JSON.stringify({
command: "STOP MOVEMENT",
data: 0
});
if (ws){
if (ws.readyState === WebSocket.OPEN){
ws.send(cmd);
} else console.log("WebSocket is not open");
} else if (socketio){
if (socketio.connected){
socketio.emit("message",cmd);
} else console.log("Socket.io is not connected");
}
}
function send_set_zoom(value){
let cmd = JSON.stringify({
command: "SET ZOOM",
data: value
});
if (ws){
if (ws.readyState === WebSocket.OPEN){
ws.send(cmd);
} else console.log("WebSocket is not open");
} else if (socketio){
if (socketio.connected){
socketio.emit("message",cmd);
} else console.log("Socket.io is not connected");
}
}
function send_set_volume(value){
let cmd = JSON.stringify({
command: "SET VOLUME",
data: value
});
if (ws){
if (ws.readyState === WebSocket.OPEN){
ws.send(cmd);
} else console.log("WebSocket is not open");
} else if (socketio){
if (socketio.connected){
socketio.emit("message",cmd);
} else console.log("Socket.io is not connected");
}
console.log("mute")
if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({
command: "MUTE",
data: 0
}));
} else console.log("WebSocket is not open");
}
function btn_center_off(){
$('#btn_center').prop("className", "btn-center-unactive fa-solid fa-circle");
}
function btn_up(){
console.log("btn_up")
if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({
command: "TILT UP",
data: tilt_speed
}));
btn_center_off()
} else console.log("WebSocket is not open");
}
function btn_left(){
console.log("btn_left")
if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({
command: "PAN LEFT",
data: pan_speed
}));
btn_center_off();
} else console.log("WebSocket is not open");
}
function btn_right(){
console.log("btn_right")
if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({
command: "PAN RIGHT",
data: pan_speed
}));
btn_center_off()
} else console.log("WebSocket is not open");
}
function btn_down(){
console.log("btn_down")
if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({
command: "TILT DOWN",
data: tilt_speed
}));
btn_center_off()
} else console.log("WebSocket is not open");
}
function stop_movement(){
console.log("stop_movement")
if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({
command: "STOP MOVEMENT",
data: 0
}));
} else console.log("WebSocket is not open");
}
function set_zoom(){
let zoom = document.getElementById("zoom");
console.log("set_zoom "+zoom.value)
if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({
command: "SET ZOOM",
data: zoom.value
}));
} else console.log("WebSocket is not open");
send_set_zoom($('#zoom').val());
}
function set_volumeoutput(value){
$('#volumebarvalue').val(value);
console.log("set_volumeoutput "+value)
if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({
command: "SET VOLUME",
data: value
}));
} else console.log("WebSocket is not open");
send_set_volume(value);
}
function set_pan_speed(value){
pan_speed = value;
console.log("set_pan_speed "+value);
clearpan();
let classvalue = "btn btn-dark btn-sm";
switch(pan_speed){
@@ -349,7 +572,6 @@ function set_pan_speed(value){
function set_tilt_speed(value){
tilt_speed = value;
console.log("set_tilt_speed "+value);
cleartilt();
let classvalue = "btn btn-dark btn-sm";
switch (tilt_speed){
@@ -388,5 +610,149 @@ function change_video_quality(){
video_quality = btn.checked?"HQ":"LQ";
}
/**
* Process Command
* @param {{reply: string, data: string, additional: string}}dx
*/
function process_command(dx){
switch (dx.reply){
case "GET BASE64":
if (dx.data.startsWith("data:image/jpeg;base64,")){
update_camerastream(dx.data);
} else update_camerastream(null);
if (dx.additional && dx.additional.length>0){
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;
case "SET VOLUME":
//console.log("Set Volume: "+dx.data);
break;
case "GET VOLUME":
//console.log("Get Volume: "+dx.data);
$('#customRange').val(dx.data);
break;
case "GET MAX ZOOM":
$zoom
.prop("min", 1)
.prop("max", dx.data);
break;
case "GET ZOOM":
$zoom.val(dx.data);
break;
case "GET RESOLUTION":
//console.log("Get Resolution: "+dx.data);
break;
case "PAN LEFT":
//console.log("Pan Left");
break;
case "PAN RIGHT":
//console.log("Pan Right");
break;
case "TILT UP":
//console.log("Tilt Up");
break;
case "TILT DOWN":
//console.log("Tilt Down");
break;
case "STOP MOVEMENT":
//console.log("Stop Movement");
break;
case "SET ZOOM":
//console.log("Set Zoom: "+dx.data);
break;
case "PLAY AUDIO":
//console.log("Play Audio: "+dx.data);
if (dx.data.startsWith("Failed")){
alert(dx.data);
} else $('#status_player').html("Playing Audio "+dx.data);
break;
case "STOP AUDIO":
//console.log("Stop Audio");
document.getElementById("status_player").innerHTML = "Stop Playback";
break;
case 'SET VIDEO QUALITY':
//console.log("Set Video Quality: "+dx.data);
break;
case "MUTE":
//console.log("Mute");
$mute.prop("className", "btn-mute hide");
$unmute.prop("className", "btn-mute show");
break;
case "UNMUTE":
//console.log("Unmute");
$mute.prop("className", "btn-mute show");
$unmute.prop("className", "btn-mute hide");
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);
//console.log(systeminfo);
if (systeminfo.cpu_temperature && systeminfo.cpu_temperature.length>0) $('#cpu_temperature').html(`${systeminfo.cpu_temperature} °C`);
if (systeminfo.cpu && systeminfo.cpu.length>0) $('#cpu_usage').html(`${systeminfo.cpu} %`);
if (systeminfo.ram_usage && systeminfo.ram_usage.length>0) $('#ram_usage').html(`${systeminfo.ram_usage} %`);
if (systeminfo.enP8p1s0_TX && systeminfo.enP8p1s0_TX.length>0) $('#ethernet_TX').html(systeminfo.enP8p1s0_TX);
if (systeminfo.enP8p1s0_RX && systeminfo.enP8p1s0_RX.length>0) $('#ethernet_RX').html(systeminfo.enP8p1s0_RX);
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;
$('#desc_content1').html(audiofiles.preset1);
play_on(1);
} else {
files[0] = null;
$('#desc_content1').html("");
play_off(1);
}
if (audiofiles.preset2 && audiofiles.preset2.length>0 && audiofiles.preset2!== "null") {
files[1] = audiofiles.preset2;
$('#desc_content2').html(audiofiles.preset2);
play_on(2);
} else {
files[1] = null;
$('#desc_content2').html("");
play_off(2);
}
if (audiofiles.preset3 && audiofiles.preset3.length>0 && audiofiles.preset3!== "null") {
files[2] = audiofiles.preset3;
$('#desc_content3').html(audiofiles.preset3);
play_on(3);
} else {
files[2] = null;
$('#desc_content3').html("");
play_off(3);
}
if (audiofiles.preset4 && audiofiles.preset4.length>0 && audiofiles.preset4!== "null") {
files[3] = audiofiles.preset4;
$('#desc_content4').html(audiofiles.preset4);
play_on(4);
} else {
files[3] = null;
$('#desc_content4').html("");
play_off(4);
}
if (audiofiles.preset5 && audiofiles.preset5.length>0 && audiofiles.preset5!== "null") {
files[4] = audiofiles.preset5;
$('#desc_content5').html(audiofiles.preset5);
play_on(5);
} else {
files[4] = null;
$('#desc_content5').html("");
play_off(5);
}
break;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

7
Html/html/public/js/socket.io.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -9,6 +9,7 @@ html,body{
body{
overflow: visible;
flex-wrap: wrap;
background: rgb(51, 51, 51) !important;
}
/*HEADER */
*,
@@ -819,4 +820,523 @@ input.checkbox{
}
.card-shadow{
box-shadow: rgba(0, 0, 0, 0.45) 0px 25px 20px -20px;
}
}
#streaming_status{
text-align: left;
}
/*SWITCH*/
/* Style the container to align the labels and switch */
.toggle-container {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
}
/* Style the labels (Off and On) */
.toggle-label {
font-size: 16px;
color: #555;
margin-bottom: 15px;
margin-right: 5px;
margin-left: 5px;
text-align: right;
}
/* Style the toggle switch (input) */
.toggle-switch {
-webkit-appearance: none;
appearance: none;
width: 50px;
height: 25px;
background-color: #ccc;
border-radius: 25px;
position: relative;
transition: background-color 0.3s;
cursor: pointer;
}
/* Circle inside the switch */
.toggle-switch::before {
content: '';
position: absolute;
top: 3px;
left: 3px;
width: 19px;
height: 19px;
background-color: white;
border-radius: 50%;
transition: transform 0.3s;
}
/* When checked (toggle to On), change background color and move the circle */
.toggle-switch:checked {
background-color: #0d6efd;
}
.toggle-switch:checked::before {
transform: translateX(25px);
}
.align-right{
text-align: right;
}
.pass-wrong{
/*border-color: red !important;*/
box-shadow: rgba(255, 0, 0, 0.5) 0px 1px 0px, rgba(255, 0, 0, 0.5) 0px 0px 8px;
/*box-shadow: rgba(255, 0, 0, 0.5) 0px 2px 4px 0px, rgba(255, 0, 0, 0.5) 0px 2px 16px 0px;*/
}
.min90{
min-height: 80vh;
}
.container.align-right {
display: flex;
justify-content: flex-end;
}
select {
-webkit-appearance: none; /* Remove default appearance in WebKit browsers */
-moz-appearance: none; /* Remove default appearance in Firefox */
appearance: none; /* Remove default appearance in other browsers */
padding-right: 30px; /* Space for the arrow */
border: 1px solid #ccc; /* Border around the select */
background: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zY3JlZW4vZGVmYXVsdCIgY2xhc3M9InN0b2NrdmlldyI+PHBhdGggZD0iTTEuMzgzMiwyLjU5MjUgTDIuMzEzMywxLjYwNjggTDAsMS42MDAyMyBMIDAsMiBMMi4zMzczOCwxLjYwNjggTDYuMzcwMiwzLjM4NDIiIHN0eWxlPSJzdHJva2U6IzAwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuNzgzMTtzdHJva2UtbGluZWNhcDpyb3VuZDsgc3Ryb2tlLW1vcjp1cmwoI3N0b2NrdmlldyIgc3Ryb2tlLXBhZGRpbmc6IDE3cHggIj48L3BhdGg+PC9zdmc+')
no-repeat right 10px center; /* Custom arrow icon */
background-size: 12px;
border-color: black;
border-width: 30px;
color: black;
}
.icon-system{
margin-top: -13px;
}
.padleft{
padding-left: 3px;
}
.nav-setting{
font-size: 1.2em;
}
.nav-logout{
font-size: 1.2em !important;
}
/*-------------------------------------SET RESPONSIVE -----------------*/
@media (max-width: 900px) {
/*Pan Tilt Speed*/
button#tilt1,
button#tilt2,
button#tilt3,
button#tilt4,
button#pan1,
button#pan2,
button#pan3,
button#pan4{
padding: 0.75rem 1rem;
font-size: 1.2rem;
border-radius: .2rem;
}
/*End Pan Tilt Speed*/
/* ---- NAVIGATION ----- */
.btn-nav{
margin: 0;
font-size: 7em;
color: lightgray;
}
.btn-nav:hover{
color: white;
}
.btn-nav:active{
color: dodgerblue;
}
.btn-nav-center{
font-size: 3em;
top: -12px;
color: dodgerblue;
}
.btn-center-unactive{
font-size: 2em;
color: #434343;
}
.pad-img{
padding: 0;
}
.img-cctv{
box-shadow: rgba(14, 30, 37, 0.12) 0px 2px 4px 0px, rgba(14, 30, 37, 0.32) 0px 2px 16px 0px;
border-radius: 10px;
margin: 1.5em;
}
.btn-circle{
width: 13em;
height: 13em;
background-color: black;
align-items: center;
align-content: center;
margin: 5%;
border-radius: 50%;
box-shadow: rgba(0, 0, 0, 0.2) 0px 12px 28px 0px, rgba(0, 0, 0, 0.1) 0px 2px 4px 0px, rgba(255, 255, 255, 0.05) 0px 0px 0px 1px inset;
}
.pad-top{
margin-top: -17px;
margin-bottom: -10px;
}
.pad-left{
margin-top: -40px;
margin-right: -20px;
}
.pad-right{
margin-top: -40px;
margin-left: -20px;
}
.pad-bottom{
margin-top: -40px;
}
/* ---- END - NAVIGATION ----- */
/* Mute Button */
.btn-mute{
text-decoration: none;
background-color: #f7f7f7;
background-image: -webkit-gradient(linear, left top, left bottom, from(#f7f7f7), to(#e7e7e7));
background-image: -webkit-linear-gradient(top, #f7f7f7, #e7e7e7);
background-image: -moz-linear-gradient(top, #f7f7f7, #e7e7e7);
background-image: -ms-linear-gradient(top, #f7f7f7, #e7e7e7);
background-image: -o-linear-gradient(top, #f7f7f7, #e7e7e7);
color: black;
width: 2.5em;
height: 2.5em;
padding: 0.2em;
position: relative;
text-align: center;
border-radius: 50%;
box-shadow: 0px 3px 8px #aaa, inset 0px 2px 3px #fff;
border: solid 1px transparent;
font-size: 1.1em;
}
/* End Mute Button */
.toggle-label {
font-size: 16px;
color: #555;
margin-bottom: 15px;
margin-right: 5px;
margin-left: 5px;
text-align: right;
}
/* Style the toggle switch (input) */
.toggle-switch {
-webkit-appearance: none;
appearance: none;
width: 55px;
height: 30px;
background-color: #ccc;
border-radius: 25px;
position: relative;
transition: background-color 0.3s;
cursor: pointer;
}
/* Circle inside the switch */
.toggle-switch::before {
content: '';
position: absolute;
top: 3px;
left: 3px;
width: 24px;
height: 24px;
background-color: white;
border-radius: 50%;
transition: transform 0.3s;
}
/* When checked (toggle to On), change background color and move the circle */
.toggle-switch:checked {
background-color: #0d6efd;
}
.toggle-switch:checked::before {
transform: translateX(25px);
}
.align-right{
text-align: right;
}
/* End Switch */
}
@media (min-width: 900px) and (max-width: 1050px) {
#streaming_status{
text-align: left;
font-size: 1.8rem;
}
.padleft{
padding-left: 3px;
font-size: 1.8rem;
}
.icon-system{
margin-top: -13px;
font-size: 1.8rem;
}
button#tilt1,
button#tilt2,
button#tilt3,
button#tilt4,
button#pan1,
button#pan2,
button#pan3,
button#pan4{
padding: 0.75rem 1.5rem;
font-size: 2.5rem;
border-radius: .2rem;
}
.text-dash{
font-size: 1.8rem;
}
.text-status1{
color: black;
font-size: 3em;
}
/* ----- SWITCH ------ */
.toggle-label {
font-size: 1.8rem;
color: #555;
margin-bottom: 15px;
margin-right: 5px;
margin-left: 5px;
text-align: right;
}
/* Style the toggle switch (input) */
.toggle-switch {
-webkit-appearance: none;
appearance: none;
/*width: 50px;*/
/*height: 25px;*/
width: 75px;
height: 50px;
background-color: #ccc;
border-radius: 25px;
position: relative;
transition: background-color 0.3s;
cursor: pointer;
}
/* Circle inside the switch */
.toggle-switch::before {
content: '';
position: absolute;
top: 3px;
left: 3px;
width: 44px;
height: 44px;
background-color: white;
border-radius: 50%;
transition: transform 0.3s;
}
/* When checked (toggle to On), change background color and move the circle */
.toggle-switch:checked {
background-color: #0d6efd;
}
.toggle-switch:checked::before {
transform: translateX(25px);
}
/*----- END SWITCH -----*/
/* --- Button CONTENT ---- */
.btn-play1{
text-decoration: none;
background-color: #f7f7f7;
background-image: -webkit-gradient(linear, left top, left bottom, from(#f7f7f7), to(#e7e7e7));
background-image: -webkit-linear-gradient(top, #f7f7f7, #e7e7e7);
background-image: -moz-linear-gradient(top, #f7f7f7, #e7e7e7);
background-image: -ms-linear-gradient(top, #f7f7f7, #e7e7e7);
background-image: -o-linear-gradient(top, #f7f7f7, #e7e7e7);
color: black;
/*width: 4.2em;*/
/*height: 4.2em;*/
width: 7em;
height: 7em;
position: relative;
text-align: center;
border-radius: 50%;
box-shadow: 0px 3px 8px #aaa, inset 0px 2px 3px #fff;
border: solid 1px transparent;
}
.btn-play1:before {
content: "";
display: block;
background: #fff;
border-top: 2px solid #ddd;
position: absolute;
z-index: -1;
border-radius: 50%;
box-shadow: inset 0px 8px 48px #ddd;
}
.btn-play1:active {
box-shadow: 0px 3px 8px #aaa inset, 0px 2px 3px #fff;
}
.btn-play1:hover {
text-decoration: none;
/*color: #555;*/
color: black;
background: #f5f5f5;
}
.icon1{
margin: 27%;
font-size: 4.5em;
position: absolute;
top: -9px;
left: -1px;
bottom: 0;
right: 0;
text-align: center;
}
.icon2{
margin: 27%;
font-size: 4.5em;
position: absolute;
top: -9px;
left: 5px;
bottom: 0;
right: 0;
text-align: center;
}
/* --- END Button CONTENT ---- */
.btn-mute{
text-decoration: none;
background-color: #f7f7f7;
background-image: -webkit-gradient(linear, left top, left bottom, from(#f7f7f7), to(#e7e7e7));
background-image: -webkit-linear-gradient(top, #f7f7f7, #e7e7e7);
background-image: -moz-linear-gradient(top, #f7f7f7, #e7e7e7);
background-image: -ms-linear-gradient(top, #f7f7f7, #e7e7e7);
background-image: -o-linear-gradient(top, #f7f7f7, #e7e7e7);
color: black;
width: 2.2em;
height: 2.2em;
padding: 0.1em;
position: relative;
text-align: center;
border-radius: 50%;
box-shadow: 0px 3px 8px #aaa, inset 0px 2px 3px #fff;
border: solid 1px transparent;
font-size: 2.2em;
}
/* ---- NAVIGATION ----- */
.btn-nav{
margin: 0;
font-size: 13em;
color: lightgray;
}
.btn-nav:hover{
color: white;
}
.btn-nav:active{
color: dodgerblue;
}
.btn-nav-center{
font-size: 5em;
top: -20px;
color: dodgerblue;
}
.btn-center-unactive{
font-size: 2em;
color: #434343;
}
.pad-img{
padding: 0;
}
.img-cctv{
box-shadow: rgba(14, 30, 37, 0.12) 0px 2px 4px 0px, rgba(14, 30, 37, 0.32) 0px 2px 16px 0px;
border-radius: 10px;
margin: 1.5em;
}
.btn-circle{
width: 20em;
height: 20em;
background-color: black;
align-items: center;
align-content: center;
margin: 5%;
border-radius: 70%;
box-shadow: rgba(0, 0, 0, 0.2) 0px 12px 28px 0px, rgba(0, 0, 0, 0.1) 0px 2px 4px 0px, rgba(255, 255, 255, 0.05) 0px 0px 0px 1px inset;
}
.pad-top{
margin-top: -40px;
margin-bottom: -45px;
}
.pad-left{
margin-top: -65px;
margin-right: -40px;
}
.pad-right{
margin-top: -65px;
margin-left: -40px;
}
.pad-bottom{
margin-top: -110px;
}
#set_zoom{
padding: .5rem 1rem;
font-size: 1.7rem;
border-radius: .3rem;
}
.footer{
font-size: 1.5em;
}
.p-title{
font-size: 3.5em !important;
}
.nav-setting{
font-size: 1.8em;
line-height: 2em;
}
.nav-logout{
font-size: 1.8em !important;
}
input[type="range"] {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 20px;
background: #ddd;
border-radius: 10px;
}
input[type="range"]::-webkit-slider-runnable-track {
height: 20px;
background: #dddddd;
border-radius: 10px;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 30px;
height: 30px;
background: #0d6efd;
border-radius: 50%;
cursor: pointer;
}
.output{
padding-left: 20%;
font-size: 1.8em;
}
.text-setting{
font-size: 1.7em;
}
.text-input{
font-size: 0.9em !important;
}
#icon_confirm,
#icon_password,
#icon_camera{
font-size: 1.5em;
}
.btn-setting{
padding: .5rem 1rem !important;
font-size: 1.1rem !important;
border-radius: .3rem !important;
}
.setting-title{
font-size: 1.3em;
}
span.navbar-toggler-icon {
font-size: 1.8em;
}
}

View File

@@ -28,11 +28,11 @@
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item pad-menu">
<a class="nav-link" href="setting.html">Setting</a>
<a class="nav-link nav-setting" href="setting.html">Setting</a>
</li>
<li class="nav-item pad-menu">
<form action="/logout" method="get">
<button class="btn btn-outline-light" type="submit">Logout</button>
<button class="btn btn-outline-light nav-logout" type="submit">Logout</button>
</form>
</li>
</ul>
@@ -41,87 +41,86 @@
</nav>
<!-- SECTION 2 -->
<div class="container-fluid container-setting">
<div class="container-fluid container-setting min90">
<br>
<div class="container">
<div class="card mt-5 card-shadow">
<div class="card card-shadow">
<div class="card-header bg-secondary text-light">
<h5 class="text-md-center">Audio Files</h5>
<h5 class="text-md-center setting-title">Audio Files</h5>
</div>
<div class="card-body bg-gray">
<form action="/setting/audiofile" method="post">
<!-- PRESET 1-->
<div class="row mt--2">
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<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>
<!-- PRESET 1-->
<div class="row mt--2">
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<p>Preset 1</p>
</div>
<!-- PRESET 2-->
<div class="row mt-2">
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<p>Preset 2</p>
</div>
<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 class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<select id="preset1" class="form-control text-input class100">
<option>Option 1</option>
<option>Option 2</option>
</select>
</div>
<!-- PRESET 3-->
<div class="row mt-2">
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<p>Preset 3</p>
</div>
<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>
<!-- PRESET 2-->
<div class="row mt-2">
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<p>Preset 2</p>
</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 class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<select id="preset2" class="form-control text-input class100">
<option>Option 1</option>
<option>Option 2</option>
</select>
</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>
<!-- PRESET 3-->
<div class="row mt-2">
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<p>Preset 3</p>
</div>
<div class="row mt-2">
<div class="col-6"></div>
<div class="col-6">
<button id="save_audio" type="submit" class="btn btn-primary class100" onclick="save_audio()">SAVE</button>
</div>
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<select id="preset3" class="form-control text-input class100">
<option>Option 1</option>
<option>Option 2</option>
</select>
</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 text-input 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 text-input 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 btn-setting class100" onclick="save_audio()">SAVE</button>
</div>
</div>
</div>
</div>
<div class="card mt-5 card-shadow">
<div class="card-header bg-secondary text-light">
<h5 class="text-center"> Upload Audio</h5>
<h5 class="text-center setting-title"> Upload Audio</h5>
</div>
<div class="card-body bg-gray">
<form action="/setting/uploadaudiofile" method="post" enctype="multipart/form-data">
@@ -130,10 +129,10 @@
<p> Upload audio files</p>
</div>
<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 text-input" type="file" name="file" required title="Select Audio File">
</div>
<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-setting btn-dark class100">Upload</button>
</div>
</div>
</form>
@@ -143,106 +142,102 @@
<div class="card mt-5 card-shadow">
<div class="card-header bg-secondary text-light ">
<h5 class="text-center">Camera</h5>
<h5 class="text-center setting-title">Camera</h5>
</div>
<div class="card-body bg-gray">
<form action="/setting/camera" method="post">
<div class="row mt-2">
<div class="col-6">
<p>IP Address</p>
</div>
<div class="col-6">
<input id="setting_ip" class="form-control" title="IP Address">
</div>
<div class="row mt-2">
<div class="col-6">
<p>IP Address</p>
</div>
<div class="row mt-2">
<div class="col-6">
<p>Port</p>
</div>
<div class="col-6">
<input id="setting_port" class="form-control" title="Port">
</div>
<div class="col-6">
<input id="setting_ip" name="ip" class="form-control text-input" title="IP Address">
</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 text-input" title="Port">
</div>
</div>
<div class="row mt-2">
<div class="col-6">
<p>Username</p>
</div>
<div class="col-6">
<input id="setting_username" class="form-control" title="Username">
</div>
<div class="row mt-2">
<div class="col-6">
<p>Username</p>
</div>
<div class="col-6">
<input id="setting_username" name="username" class="form-control text-input" title="Username">
</div>
</div>
<div class="row mt-2">
<div class="col-6">
<p>Password</p>
</div>
<div class="col-6">
<div class="input-group">
<input type="password" id="setting_password" class="form-control" title="Password">
<span class="input-group-text" onclick="cameraPassword()">
<div class="row mt-2">
<div class="col-6">
<p>Password</p>
</div>
<div class="col-6">
<div class="input-group">
<input type="password" id="setting_password" name="password" class="form-control text-input" title="Password">
<span class="input-group-text" onclick="cameraPassword()">
<i class="fa-solid fa-eye" id="icon_camera"></i>
</span>
</div>
</div>
</div>
<div class="row mt-2">
<div class="col-6"></div>
<div class="col-6">
<button id="save_camera" type="submit" class="btn btn-primary class100" onclick="save_camera()">SAVE</button>
</div>
</div>
<div class="row mt-2">
<div class="col-6"></div>
<div class="col-6">
<button id="save_camera" class="btn btn-primary btn-setting class100" onclick="save_camera()">SAVE</button>
</div>
</form>
</div>
</div>
</div>
<div class="card mt-5 card-shadow">
<div class="card-header bg-secondary text-light">
<h5 class="text-center">Login</h5>
<h5 class="text-center setting-title">Login</h5>
</div>
<div class="card-body bg-gray">
<form action="/setting/weblogin" method="post">
<div class="row mt-2">
<div class="col-6">
<p>Username</p>
</div>
<div class="col-6">
<input id="login_username" class="form-control" title="Username">
</div>
<div class="row mt-2">
<div class="col-6">
<p>Username</p>
</div>
<div class="row mt-2">
<div class="col-6">
<p>Password</p>
</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()">
<div class="col-6">
<input id="login_username" class="form-control text-input" title="Username">
</div>
</div>
<div class="row mt-2">
<div class="col-6">
<p>Password</p>
</div>
<div class="col-6">
<div class="input-group">
<input type="password" id="edit_password" class="form-control text-input" title="Password">
<span class="input-group-text" onclick="showPassword()">
<i class="fa-solid fa-eye" id="icon_password"></i>
</span>
</div>
</div>
</div>
<div class="row mt-2">
<div class="col-6">
<p>Confirm Password</p>
</div>
<div class="col-6">
<div class="input-group">
<input type="password" id="confirm_password" class="form-control" title="Confirm Password">
<span class="input-group-text" onclick="showConfirm()">
</div>
<div class="row mt-2">
<div class="col-6">
<p>Confirm Password</p>
</div>
<div class="col-6">
<div class="input-group">
<input type="password" id="confirm_password" class="form-control text-input" title="Confirm Password">
<span class="input-group-text" onclick="showConfirm()">
<i class="fa-solid fa-eye" id="icon_confirm"></i>
</span>
</div>
</div>
</div>
<div class="row mt-2">
<div class="col-6"></div>
<div class="col-6">
<button id="save_login" type="submit" class="btn btn-primary class100" onclick="save_login()">SAVE</button>
</div>
</div>
<div class="row mt-2">
<div class="col-6"></div>
<div class="col-6">
<button id="save_login" class="btn btn-primary btn-setting class100" onclick="save_login()">SAVE</button>
</div>
</form>
</div>
</div>
</div>
</div>
@@ -250,7 +245,7 @@
<!-- SECTION 3 -->
<div class="container-fluid footer1 outside">
<p>2024 Copyright &copy; Galva Technologies. All rights reserved.</p>
<p class="footer">2024 Copyright &copy; Galva Technologies. All rights reserved.</p>
</div>
</body>
</html>

View File

@@ -54,7 +54,14 @@ function fill_select(index, values){
*/
let preset = document.getElementById("preset"+index);
preset.innerHTML = "";
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++) {
const element = values[i];
let option = document.createElement("option");
@@ -108,4 +115,91 @@ function showConfirm() {
icon.classList.remove('fa-eye-slash');
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,24 +1,20 @@
#Fri Nov 08 16:52:01 WIB 2024
#Mon Nov 11 16:04:42 WIB 2024
AudioFile01=elangWav.wav
AudioFile02=gunshotsWav.wav
AudioFile03=pinkNoiseWav.wav
AudioFile04=04.mp3
AudioFile05=05.mp3
AudioFile04=null
AudioFile05=null
AudioVolumeOutput=100
Camera_Rtsp_path=/axis-media/media.amp
Camera_ip=192.168.10.17
#Camera_Rtsp_path=/video1
Camera_ip=172.17.195.51
Camera_password=password
Camera_port=80
Camera_user=root
PanTiltID=1
SerialBaudRate=9600
SerialPort=/dev/ttyUSB0
SerialPort=/dev/ttyTHS1
WebHost=0.0.0.0
WebPassword=bandara
WebPort=8080
WebUsername=admin
audiofile1=
audiofile2=
audiofile3=
audiofile4=
audiofile5=

80
onnx/coco.names Normal file
View File

@@ -0,0 +1,80 @@
person
bicycle
car
motorbike
aeroplane
bus
train
truck
boat
traffic light
fire hydrant
stop sign
parking meter
bench
bird
cat
dog
horse
sheep
cow
elephant
bear
zebra
giraffe
backpack
umbrella
handbag
tie
suitcase
frisbee
skis
snowboard
sports ball
kite
baseball bat
baseball glove
skateboard
surfboard
tennis racket
bottle
wine glass
cup
fork
knife
spoon
bowl
banana
apple
sandwich
orange
broccoli
carrot
hot dog
pizza
donut
cake
chair
sofa
pottedplant
bed
diningtable
toilet
tvmonitor
laptop
mouse
remote
keyboard
cell phone
microwave
oven
toaster
sink
refrigerator
book
clock
vase
scissors
teddy bear
hair drier
toothbrush

BIN
onnx/yolov8n.onnx Normal file

Binary file not shown.

101
pom.xml
View File

@@ -7,16 +7,95 @@
<groupId>id.co.gtc</groupId>
<artifactId>BirdStrikeSoetta</artifactId>
<version>1.0-SNAPSHOT</version>
<repositories>
<repository>
<name>Maven Repository</name>
<id>mvnrepository</id>
<url>https://mvnrepository.com/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.10</version>
<artifactId>javacv</artifactId>
<version>1.5.11</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.10.0-1.5.11</version>
<classifier>windows-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.10.0-1.5.11</version>
<classifier>linux-arm64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>7.1-1.5.11</version>
<classifier>windows-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>7.1-1.5.11</version>
<classifier>linux-arm64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>openblas</artifactId>
<version>0.3.28-1.5.11</version>
<classifier>windows-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>openblas</artifactId>
<version>0.3.28-1.5.11</version>
<classifier>linux-arm64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>tensorrt</artifactId>
<version>8.6-1.5.10</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>cuda</artifactId>
<version>12.6-9.5-1.5.11</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>1.5.11</version>
<classifier>windows-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>1.5.11</version>
<classifier>linux-arm64</classifier>
</dependency>
<dependency>
<groupId>com.corundumstudio.socketio</groupId>
<artifactId>netty-socketio</artifactId>
<version>2.0.12</version>
</dependency>
<dependency>
<groupId>io.javalin</groupId>
<artifactId>javalin</artifactId>
<version> 6.3.0</version>
<version> 6.4.0</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
@@ -31,7 +110,7 @@
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
<version>1.18.36</version>
<scope>provided</scope>
</dependency>
@@ -52,6 +131,11 @@
<artifactId>jackson-databind</artifactId>
<version>2.17.2</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.11.0</version>
</dependency>
</dependencies>
<properties>
@@ -60,4 +144,13 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.7.0</version>
</extension>
</extensions>
</build>
</project>

View File

@@ -1,14 +1,33 @@
package Audio;
public class AudioFileProperties {
public final int handle;
public final String filename;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import java.util.Arrays;
import java.util.List;
public class AudioFileProperties extends Structure {
public int handle;
public String filename;
public long Length;
public double duration;
public AudioFileProperties(int handle, String filename){
public AudioFileProperties(Pointer p){
super(p);
read();
}
public AudioFileProperties(int handle, String filename, long length, double duration){
this.handle = handle;
this.filename = filename;
this.Length = length;
this.duration = duration;
write();
}
protected List<String> getFieldOrder() {
return Arrays.asList("handle", "filename", "Length", "duration");
}
}

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);
}
@@ -138,9 +193,9 @@ public class AudioPlayer {
int handle = bass.BASS_StreamCreateFile(false, ff.getAbsolutePath(), 0, 0,0);
if (handle!=0){
Logger.info("Audio file {} opened successfully", ff.getName());
AudioFileProperties prop = new AudioFileProperties(handle, ff.getName());
prop.Length = bass.BASS_ChannelGetLength(handle, Bass.BASS_POS_BYTE);
prop.duration = bass.BASS_ChannelBytes2Seconds(handle, prop.Length);
long Length = bass.BASS_ChannelGetLength(handle, Bass.BASS_POS_BYTE);
double duration = bass.BASS_ChannelBytes2Seconds(handle, Length);
AudioFileProperties prop = new AudioFileProperties(handle, ff.getName(), Length, duration);
return prop;
} else Logger.error("Failed to open audio file {}, Error Code {}", ff.getAbsolutePath(), bass.BASS_ErrorGetCode());
} else Logger.info("AudioPlayer not initialized");
@@ -162,26 +217,63 @@ public class AudioPlayer {
} else Logger.info("Audio file {} closed successfully", prop.filename);
} else Logger.info("AudioPlayer not initialized");
}
}
PlaybackEvent playbackEvent = null;
Bass.SYNCPROC endproc = (handle, channel, data, user)->{
if (user!=null){
AudioFileProperties ap = new AudioFileProperties(user);
if (playbackEvent!=null){
playbackEvent.onPlaybackFinished(ap);
} else Logger.warn("endproc playbackEvent is null");
} else Logger.warn("endproc user is null");
};
Bass.SYNCPROC freeproc = (handle, channel, data, user)->{
if (user!=null){
AudioFileProperties ap = new AudioFileProperties(user);
if (playbackEvent!=null){
playbackEvent.onPlaybackFinished(ap);
} else Logger.warn("freeproc playbackEvent is null");
} else Logger.warn("freeproc user is null");
};
Bass.SYNCPROC failproc = (handle, channel, data, user)->{
if (user!=null){
AudioFileProperties ap = new AudioFileProperties(user);
if (playbackEvent!=null){
playbackEvent.onPlaybackFailure(ap, "Device Failure");
} else Logger.warn("failproc playbackEvent is null");
} else Logger.warn("failproc user is null");
};
/**
* 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){
this.playbackEvent = null;
if (inited){
if (prop!=null){
if (bass.BASS_ChannelStart(prop.handle)){
this.playbackEvent = event;
playbackhandle = prop.handle;
// SyncProcValue spv = new SyncProcValue(prop);
// spv.write();
bass.BASS_ChannelSetAttribute(prop.handle, Bass.BASS_ATTRIB_VOL, playbackvolume);
if (looping) bass.BASS_ChannelFlags(prop.handle, Bass.BASS_SAMPLE_LOOP, Bass.BASS_SAMPLE_LOOP);
if (event!=null) event.onPlaybackStart(prop);
int devfailsync = bass.BASS_ChannelSetSync(prop.handle, Bass.BASS_SYNC_DEV_FAIL,0, (handle, channel, data, user)->{
if (event!=null) event.onPlaybackFailure(prop, "Device Failure");
}, null);
int devfailsync = bass.BASS_ChannelSetSync(prop.handle, Bass.BASS_SYNC_DEV_FAIL,0, failproc, prop.getPointer());
if (devfailsync==0) Logger.error("Failed to set Device Failure Sync, Error Code {}", bass.BASS_ErrorGetCode());
int endsync = bass.BASS_ChannelSetSync(prop.handle, Bass.BASS_SYNC_END, 0, (handle, channel, data, user)->{
if (looping) {
if (event!=null) event.onPlaybackLooped(prop);
} else if (event!=null) event.onPlaybackFinished(prop);
}, null);
int endsync = bass.BASS_ChannelSetSync(prop.handle, Bass.BASS_SYNC_END , 0, endproc, prop.getPointer());
if (endsync==0) Logger.error("Failed to set End Sync, Error Code {}", bass.BASS_ErrorGetCode());
int freesync = bass.BASS_ChannelSetSync(prop.handle, Bass.BASS_SYNC_FREE , 0, freeproc, prop.getPointer());
if (freesync==0) Logger.error("Failed to set Free Sync, Error Code {}", bass.BASS_ErrorGetCode());
} else {
if (event!=null) event.onPlaybackFailure(prop, String.format("Failed to play audio file %s, Error Code %d", prop.filename, bass.BASS_ErrorGetCode()));
}
@@ -193,4 +285,23 @@ public class AudioPlayer {
}
}
// static class SyncProcValue extends Structure{
// public AudioFileProperties ap;
// public SyncProcValue(AudioFileProperties ap){
// this.ap = ap;
// }
//
// public SyncProcValue(Pointer p){
// super(p);
// read();
// }
//
// @Override
// protected List<String> getFieldOrder() {
// return Arrays.asList("ap");
// }
//
//
// }
}

View File

@@ -2,6 +2,8 @@ package Audio;
import com.sun.jna.*;
import java.util.Objects;
@SuppressWarnings("unused")
public interface Bass extends Library {

View File

@@ -0,0 +1,324 @@
package Audio;
import lombok.Getter;
import org.tinylog.Logger;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class MultiUSBAudioPlayer {
private final Bass bass;
private int[] deviceid;
@Getter
private boolean inited = false;
PlaybackHandlewithId[] playbackhandle;
float playbackvolume = 1.0f;
List<BassDeviceInfoWithId> playbackdevices = new ArrayList<>();
/**
* Create Multi USB Audio Player
*/
public MultiUSBAudioPlayer() {
bass = Bass.Instance;
Logger.info("Bass Version = {}", Integer.toHexString(bass.BASS_GetVersion()));
}
/**
* Unload Multi USB Audio Player
*/
public void Unload(){
if (inited){
if (deviceid!=null && deviceid.length>0){
for(int id: deviceid){
Logger.info("Freeing Device {}", id);
bass.BASS_SetDevice(id);
bass.BASS_Free();
}
}
}
deviceid = null;
inited = false;
}
/**
* Detect Output Devices
*/
public void DetectOutputDevices(){
Logger.info("Detecting Output Devices...");
int ii = 1;
playbackdevices.clear();
while (true){
Bass.BASS_DEVICEINFO info = new Bass.BASS_DEVICEINFO();
if (bass.BASS_GetDeviceInfo(ii, info)){
//Logger.info("Device {} = {}, flags = {}", ii, info.name, Integer.toHexString(info.flags));
playbackdevices.add(new BassDeviceInfoWithId(ii, info));
ii++;
} else {
break;
}
}
}
/**
* Find playback devices with name
* @param name specific name
* @return array of device id
*/
public int[] FindDeviceIDWithName(String name){
if (playbackdevices!=null && !playbackdevices.isEmpty()){
List<Integer> result = new ArrayList<>();
for(BassDeviceInfoWithId dev : playbackdevices){
if (dev.info.name.toLowerCase().contains(name.toLowerCase())){
Logger.info("Found Device id={}, name={}", dev.id, dev.info.name);
result.add(dev.id);
}
}
return result.stream().mapToInt(i->i).toArray();
}
return new int[0];
}
/**
* Open Devices
* @param device list of device id
* @param freq frequency
* @return true if any device is successfully initialized
*/
public boolean OpenDevice(int[] device, int freq){
if (device!=null && device.length>0){
int flag = Bass.BASS_DEVICE_REINIT | Bass.BASS_DEVICE_16BITS | Bass.BASS_DEVICE_MONO | Bass.BASS_DEVICE_FREQ;
List<Integer> validdevice = new ArrayList<>();
for(int i : device){
boolean result = bass.BASS_Init(i, freq, flag);
if (result){
Logger.info("Device {} initialized", i);
validdevice.add(i);
inited = true;
} else {
Logger.error("Failed to initialize device {}, errorcode={}", i, bass.BASS_ErrorGetCode());
}
}
deviceid = validdevice.stream().mapToInt(i->i).toArray();
return inited;
}
return false;
}
/**
* 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){
if (value<0) value = 0;
if (value>100) value = 100;
if (inited){
if (deviceid!=null && deviceid.length>0){
for(int id : deviceid){
bass.BASS_SetDevice(id);
if (!bass.BASS_SetVolume(value/100.0f)){
Logger.error("Failed to set Master Volume to {}, Error Code {}", value, bass.BASS_ErrorGetCode());
} else Logger.info("Master Volume set to {}", value);
}
} else Logger.error("No device initialized");
} else Logger.info("MultiUSBAudioPlayer not initialized");
}
/**
* Get Playback Volume
* is the volume of the currently playing audio file
* @return 0 - 100
*/
public int getPlaybackvolume(){
if (playbackvolume<0)
return 0;
else if (playbackvolume>1.0f)
return 100;
else
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;
playbackvolume = value/100.0f;
if (playbackhandle!=null && playbackhandle.length>0){
for(PlaybackHandlewithId ph : playbackhandle){
if (ph.handle.handle!=0){
bass.BASS_SetDevice(ph.id);
bass.BASS_ChannelSetAttribute(ph.handle.handle, Bass.BASS_ATTRIB_VOL, playbackvolume);
}
}
}
}
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);
}
/**
* Open Audio File
* @param ff audio file name to open
* @return AudioFileProperties object or null if failed
*/
public PlaybackHandlewithId[] OpenAudioFile(File ff){
if (inited){
AudioFileProperties prop;
List<PlaybackHandlewithId> phs = new ArrayList<>();
for(int id: deviceid){
bass.BASS_SetDevice(id);
int handle = bass.BASS_StreamCreateFile(false, ff.getAbsolutePath(), 0, 0,0);
if (handle!=0){
long Length = bass.BASS_ChannelGetLength(handle, Bass.BASS_POS_BYTE);
double duration = bass.BASS_ChannelBytes2Seconds(handle, Length);
prop = new AudioFileProperties(handle, ff.getName(), Length, duration);
PlaybackHandlewithId ph = new PlaybackHandlewithId(id, prop);
phs.add(ph);
}
}
if (!phs.isEmpty()){
playbackhandle = phs.toArray(new PlaybackHandlewithId[0]);
Logger.info("Audio file {} opened successfully", ff.getName());
return playbackhandle;
} else {
Logger.error("Failed to open audio file {}, Error Code {}", ff.getName(), bass.BASS_ErrorGetCode());
}
} else Logger.info("AudioPlayer not initialized");
return new PlaybackHandlewithId[0];
}
/**
* Close Audio File
* @param prop AudioFileProperties object to close
*/
public void CloseAudioFile(PlaybackHandlewithId[] prop) {
if (inited) {
if (prop != null && prop.length>0) {
for(PlaybackHandlewithId ph : prop){
if (ph.handle.handle!=0){
bass.BASS_SetDevice(ph.id);
if (!bass.BASS_ChannelFree(ph.handle.handle)){
Logger.error("Failed to close audio file {}, Error Code {}", ph.handle.filename, bass.BASS_ErrorGetCode());
} else {
Logger.info("Audio file {} closed successfully", ph.handle.filename);
}
bass.BASS_Stop();
}
}
} else Logger.info("AudioPlayer not initialized");
}
}
PlaybackEvent playbackEvent = null;
Bass.SYNCPROC endproc = (handle, channel, data, user)->{
if (user!=null){
AudioFileProperties ap = new AudioFileProperties(user);
if (playbackEvent!=null){
playbackEvent.onPlaybackFinished(ap);
} else Logger.warn("endproc playbackEvent is null");
} else Logger.warn("endproc user is null");
};
Bass.SYNCPROC freeproc = (handle, channel, data, user)->{
if (user!=null){
AudioFileProperties ap = new AudioFileProperties(user);
if (playbackEvent!=null){
playbackEvent.onPlaybackFinished(ap);
} else Logger.warn("freeproc playbackEvent is null");
} else Logger.warn("freeproc user is null");
};
Bass.SYNCPROC failproc = (handle, channel, data, user)->{
if (user!=null){
AudioFileProperties ap = new AudioFileProperties(user);
if (playbackEvent!=null){
playbackEvent.onPlaybackFailure(ap, "Device Failure");
} else Logger.warn("failproc playbackEvent is null");
} else Logger.warn("failproc user is null");
};
/**
* 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(PlaybackHandlewithId[] prop, boolean looping, PlaybackEvent event){
if (inited){
if (prop!=null && prop.length>0){
this.playbackEvent = event;
for(PlaybackHandlewithId ph : prop){
if (ph.handle.handle!=0){
bass.BASS_SetDevice(ph.id);
bass.BASS_Start();
if (bass.BASS_ChannelStart(ph.handle.handle)) {
bass.BASS_ChannelSetAttribute(ph.handle.handle, Bass.BASS_ATTRIB_VOL, playbackvolume);
if (looping) bass.BASS_ChannelFlags(ph.handle.handle, Bass.BASS_SAMPLE_LOOP, Bass.BASS_SAMPLE_LOOP);
if (event != null) event.onPlaybackStart(ph.handle);
int devfailsync = bass.BASS_ChannelSetSync(ph.handle.handle, Bass.BASS_SYNC_DEV_FAIL, 0, failproc, ph.handle.getPointer());
if (devfailsync == 0) Logger.error("Failed to set Device Failure Sync, Error Code {}", bass.BASS_ErrorGetCode());
int endsync = bass.BASS_ChannelSetSync(ph.handle.handle, Bass.BASS_SYNC_END, 0, endproc, ph.handle.getPointer());
if (endsync == 0) Logger.error("Failed to set End Sync, Error Code {}", bass.BASS_ErrorGetCode());
int freesync = bass.BASS_ChannelSetSync(ph.handle.handle, Bass.BASS_SYNC_FREE, 0, freeproc, ph.handle.getPointer());
if (freesync == 0) Logger.error("Failed to set Free Sync, Error Code {}", bass.BASS_ErrorGetCode());
} else {
if (event != null){
event.onPlaybackFailure(ph.handle, String.format("Failed to play audio file %s, Error Code %d", ph.handle.filename, bass.BASS_ErrorGetCode()));
}
}
}
}
} else {
if (event!=null) event.onPlaybackFailure(null, "AudioFileProperties is null");
}
} else {
if (event!=null) event.onPlaybackFailure(null, "AudioPlayer not initialized");
}
}
public static class PlaybackHandlewithId{
public final int id;
public final AudioFileProperties handle;
public PlaybackHandlewithId(int id, AudioFileProperties handle){
this.id = id;
this.handle = handle;
}
}
static class BassDeviceInfoWithId{
public final int id;
public final Bass.BASS_DEVICEINFO info;
public BassDeviceInfoWithId(int id, Bass.BASS_DEVICEINFO info){
this.id = id;
this.info = info;
}
}
}

View File

@@ -5,33 +5,61 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import Other.SomeCodes;
import id.co.gtc.Main;
import lombok.Getter;
import lombok.Setter;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.javacv.OpenCVFrameConverter;
import org.opencv.imgproc.Imgproc;
import org.opencv.core.*;
import org.tinylog.Logger;
public class GrabbingTask implements Runnable {
// for while loop control
private final AtomicBoolean isGrabbing;
// for grabbing frames
private final FrameGrabber grabber;
// Consumers
@Setter private Consumer<String> onMessageUpdate;
@Setter private Consumer<Frame> onHQFrameUpdate;
@Setter private Consumer<Frame> onLQFrameUpdate;
@Setter private Consumer<String> onHQBase64Update;
@Setter private Consumer<String> onLQBase64Update;
@Setter private Consumer<Frame> onYoloUpdate;
@Setter private Consumer<String> onYoloBase64Update;
private final AtomicBoolean isGrabbing;
private final FrameGrabber grabber;
@Getter private final int lowquality_width = 640;
@Getter private final int lowquality_height = 360;
// status of capture fps
@Getter private int CaptureFPS = 0;
@Getter @Setter private int lowquality_width = 640;
@Getter @Setter private int lowquality_height = 360;
// for FPS calculation
private int intendedFps = 10;
private final YoloDetector_opencv yolo;
@SuppressWarnings("SameParameterValue")
private void updateMessage(String message) {
if (onMessageUpdate != null) {
onMessageUpdate.accept(message);
}
}
private void updateYoloBase64(String base64) {
if (onYoloBase64Update != null) {
onYoloBase64Update.accept(base64);
}
}
private void updateYoloFrame(Frame frame) {
if (onYoloUpdate != null) {
onYoloUpdate.accept(frame);
}
}
private void updateHQBase64(String base64) {
if (onHQBase64Update != null) {
@@ -39,6 +67,7 @@ public class GrabbingTask implements Runnable {
}
}
@SuppressWarnings("SameParameterValue")
private void updateLQBase64(String base64) {
if (onLQBase64Update != null) {
onLQBase64Update.accept(base64);
@@ -61,34 +90,133 @@ public class GrabbingTask implements Runnable {
public GrabbingTask(AtomicBoolean isGrabbing, FrameGrabber grabber) {
public GrabbingTask(AtomicBoolean isGrabbing, FrameGrabber grabber, int fps) {
this.isGrabbing = isGrabbing;
this.grabber = grabber;
if (fps>0) intendedFps = fps;
if (Main.use_Yolo) {
yolo = new YoloDetector_opencv();
} else yolo = null;
}
public void Stop(){
isGrabbing.set(false);
}
@Override
public void run() {
isGrabbing.set(true);
while (isGrabbing.get()) {
try {
Frame fr =grabber.grab();
if (fr!=null){
@SuppressWarnings({"CallToPrintStackTrace", "CatchMayIgnoreException"})
private Mat processFrame(Frame fr){
if (fr!=null && fr.image!=null && fr.imageWidth>0 && fr.imageHeight>0){
try(var converter = new OpenCVFrameConverter.ToOrgOpenCvCoreMat()){
Mat mat = converter.convert(fr);
if (mat.cols()>0 && mat.rows()>0){
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());
updateHQBase64(SomeCodes.MatToBase64(mat));
Size lowsize = new Size(lowquality_width, lowquality_height);
Mat resized = new Mat();
Imgproc.resize(mat, resized, lowsize);
mat.release();
if (resized.cols()>0 && resized.rows()>0){
Frame resizedFrame = converter.convert(resized);
updateLQFrame(resizedFrame);
updateLQBase64(SomeCodes.MatToBase64(resized));
return resized;
} else Logger.error("processFrame resized size is 0");
} else Logger.error("processFrame Mat size is 0");
} catch (Exception e){
if (SomeCodes.ValidString(e.getMessage())) {
if (!e.getMessage().startsWith("unknown exception")) {
Logger.error("Error processing frame: "+e.getMessage());
e.printStackTrace();
}
}
}
}
// else {
// Logger.warn("processFrame have null frame");
// updateMessage("processFrame have null frame");
// }
return null;
}
private void DetectYolo(Mat mat){
if (yolo!=null){
if (mat!=null && mat.cols()>0 && mat.rows()>0){
try {
// [0] = predict, [1] = mask
Mat[] processed = yolo.Detect(mat);
// gambar dalam bentuk Mat
Mat yolomat = yolo.Process(mat, processed[0], processed[1]);
// convert jadi frame
Frame yoloFrame = SomeCodes.MatToFrame(yolomat);
updateYoloFrame(yoloFrame);
updateYoloBase64(SomeCodes.MatToBase64(yolomat));
} catch (Exception e) {
Logger.error("error processing YOLO, Message : " + e.getMessage());
updateYoloFrame(null);
updateYoloBase64("");
}
} else Logger.error("DetectYolo mat is null");
}
}
private void flush_grabber(){
try {
if (grabber!=null) grabber.flush();
} catch (FrameGrabber.Exception e) {
Logger.error("Error flushing grabber: "+e.getMessage());
}
}
@SuppressWarnings("CallToPrintStackTrace")
@Override
public void run() {
isGrabbing.set(true);
Logger.info("Grabbing Task started");
double fps = grabber.getFrameRate();
int skippedframes = (int)(fps / intendedFps);
Logger.info("Grabber framerate = {}, intendedFps = {}, Skipping frames = {}", fps, intendedFps, skippedframes);
int framecount = 0;
flush_grabber();
long starttick = System.currentTimeMillis();
while (isGrabbing.get()) {
try{
Thread.yield();
Frame frame = grabber.grab();
if (framecount<Integer.MAX_VALUE) framecount++; else framecount = 0;
Mat resized = processFrame(frame);
// no need to skip frames, just process all frames
if (framecount % skippedframes == 0) {
if (Main.use_Yolo){
DetectYolo(resized);
}
}
resized.release();
} catch (Exception e){
Logger.error("Error grabbing frame: "+e.getMessage());
e.printStackTrace();
}
long elapsed = System.currentTimeMillis() - starttick;
starttick = System.currentTimeMillis();
if (elapsed>0) {
int xx = (int) (1000 / elapsed);
//if (xx<fps) CaptureFPS = xx;
CaptureFPS = xx;
}
//Logger.info("Elapsed time = {} ms, captureFPS = {}", elapsed, CaptureFPS);
}
Logger.info("Grabbing Task stopped");
}
}

View File

@@ -1,15 +1,17 @@
package Camera;
import com.fazecast.jSerialComm.SerialPort;
import id.co.gtc.Main;
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,24 +19,48 @@ 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 (portname.contains(port.getSystemPortName())){
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");
}
/**
* Check if Serial Port is open
* @return true if open, false otherwise
*/
public boolean isOpen(){
return serialPort != null && serialPort.isOpen();
}
/**
* Stop Pan Tilt Movement
*/
@@ -42,7 +68,13 @@ public class PanTiltController {
byte[] command = new byte[]{0, cameraid, 0, 0, 0, 0, 0};
command[6] = Checksum(command); // add checksum
command[0] = (byte) 0xFF; // add synchronization byte
serialPort.writeBytes(command, command.length);
if (isOpen()) {
Main.Max485Direction(true);
int written = serialPort.writeBytes(command, command.length);
Main.Max485Direction(false);
Main.Blink_LedPanTilt();
Logger.info("Stop movement written {}", written);
} else Logger.warn("Stop Movement failed, serial port not open");
}
/**
@@ -56,7 +88,13 @@ public class PanTiltController {
byte[] command = new byte[]{0, cameraid, 0, 4, speed, 0, 0};
command[6] = Checksum(command); // add checksum
command[0] = (byte) 0xFF; // add synchronization byte
serialPort.writeBytes(command, command.length);
if (isOpen()) {
Main.Max485Direction(true);
int written = serialPort.writeBytes(command, command.length);
Main.Max485Direction(false);
Main.Blink_LedPanTilt();
Logger.info("Pan Left written {} bytes", written);
} else Logger.warn("Pan Left failed, serial port not open");
}
/**
@@ -70,7 +108,13 @@ public class PanTiltController {
byte[] command = new byte[]{0, cameraid, 0, 2, speed, 0, 0};
command[6] = Checksum(command); // add checksum
command[0] = (byte) 0xFF; // add synchronization byte
serialPort.writeBytes(command, command.length);
if (isOpen()) {
Main.Max485Direction(true);
int written = serialPort.writeBytes(command, command.length);
Main.Max485Direction(false);
Main.Blink_LedPanTilt();
Logger.info("Pan Right written {} bytes", written);
} else Logger.warn("Pan Right failed, serial port not open");
}
/**
@@ -81,10 +125,16 @@ public class PanTiltController {
if (speed<0) speed = 0;
if (speed>0x3F) speed = 0x3F;
byte[] command = new byte[]{0, cameraid, 0, 8, speed, 0, 0};
byte[] command = new byte[]{0, cameraid, 0, 8, 0, speed, 0};
command[6] = Checksum(command); // add checksum
command[0] = (byte) 0xFF; // add synchronization byte
serialPort.writeBytes(command, command.length);
if (isOpen()) {
Main.Max485Direction(true);
int written = serialPort.writeBytes(command, command.length);
Main.Max485Direction(false);
Main.Blink_LedPanTilt();
Logger.info("Tilt Up written {} bytes", written);
} else Logger.warn("Tilt Up failed, serial port not open");
}
/**
@@ -95,10 +145,58 @@ public class PanTiltController {
if (speed<0) speed = 0;
if (speed>0x3F) speed = 0x3F;
byte[] command = new byte[]{0, cameraid, 0, 16, speed, 0, 0};
byte[] command = new byte[]{0, cameraid, 0, 16, 0, speed, 0};
command[6] = Checksum(command); // add checksum
command[0] = (byte) 0xFF; // add synchronization byte
serialPort.writeBytes(command, command.length);
if (isOpen()) {
Main.Max485Direction(true);
int written = serialPort.writeBytes(command, command.length);
Main.Max485Direction(false);
Main.Blink_LedPanTilt();
Logger.info("Tilt Down written {} bytes", written);
} else Logger.warn("Tilt Down failed, serial port not open");
}
public void DisableMovementTest(){
byte[] command = new byte[]{(byte)0xFF, cameraid, 0, 7, 0, (byte)0x54, (byte)0x5c};
//command[6] = Checksum(command);
//command[0] = (byte) 0xFF;
if (isOpen()) {
Main.Max485Direction(true);
int written = serialPort.writeBytes(command, command.length);
Main.Max485Direction(false);
Main.Blink_LedPanTilt();
Logger.info("DisableMovement test written {} bytes", written);
} else Logger.warn("DisableMovement failed, serial port not open");
}
public void EnableMovementTest(){
byte[] command = new byte[]{(byte)0xFF, cameraid, 0, 3, 0, (byte)0x54, (byte) 0x58};
//command[6] = Checksum(command);
//command[0] = (byte) 0xFF;
if (isOpen()) {
Main.Max485Direction(true);
int written = serialPort.writeBytes(command, command.length);
Main.Max485Direction(false);
Main.Blink_LedPanTilt();
Logger.info("EnableMovement test written {} bytes", written);
} else Logger.warn("EnableMovement failed, serial port not open");
}
/**
* Go to Preset
* @param preset preset number
*/
public void GoToPreset(byte preset){
byte[] command = new byte[]{0, cameraid, 0, 15, preset, 0, 0};
command[6] = Checksum(command); // add checksum
command[0] = (byte) 0xFF; // add synchronization byte
if (isOpen()) {
Main.Max485Direction(true);
serialPort.writeBytes(command, command.length);
Main.Max485Direction(false);
Main.Blink_LedPanTilt();
}
}
/**

View File

@@ -3,28 +3,84 @@ import lombok.Getter;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.jetbrains.annotations.NotNull;
import org.tinylog.Logger;
import java.util.concurrent.atomic.AtomicBoolean;
import static Other.SomeCodes.gson;
@SuppressWarnings("unused")
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 Frame lastYoloFrame = null;
private String lastHQBase64 = null;
private String lastLQBase64 = null;
private String lastYoloBase64 = 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;
}
private synchronized void setLastYoloBase64(String base64){
lastYoloBase64 = base64;
}
public synchronized String getLastYoloBase64(){
return lastYoloBase64;
}
private synchronized void setLastYoloFrame(Frame frame){
lastYoloFrame = frame;
}
public synchronized Frame getLastYoloFrame(){
return lastYoloFrame;
}
/**
* Start grabbing frames from rtsp
* @param useTcp Use tcp instead of udp
@@ -35,38 +91,14 @@ public class RtspGrabber {
grabber = FFmpegFrameGrabber.createDefault(rtspUrl);
if (useTcp) grabber.setOption("rtsp_transport", "tcp");
grabber.setImageWidth(width);
grabber.setImageHeight(height);
grabber.setPixelFormat(avutil.AV_PIX_FMT_BGR24);
grabber.start();
avutil.av_log_set_level(avutil.AV_LOG_ERROR);
Logger.info("Grabber started");
GrabbingTask tt = new GrabbingTask(isGrabbing, grabber);
tt.setOnMessageUpdate(Logger::info);
tt.setOnHQFrameUpdate(value -> {
if (value!=null){
if (value.imageWidth>0 && value.imageHeight>0){
lastHQFrame = value;
HQWidth = value.imageWidth;
HQHeight = value.imageHeight;
}
}
});
tt.setOnLQFrameUpdate(value -> {
if (value!=null){
if (value.imageWidth>0 && value.imageHeight>0){
lastLQFrame = value;
LQWidth = value.imageWidth;
LQHeight = value.imageHeight;
}
}
});
tt.setOnHQBase64Update(value -> lastHQBase64 = value);
tt.setOnLQBase64Update(value -> lastLQBase64 = value);
GrabbingTask tt = getGrabbingTask();
grabbingTask = tt;
new Thread(tt).start();
} catch (Exception e){
@@ -74,21 +106,64 @@ 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!=null ? grabbingTask.getCaptureFPS():"0")});
}
public String HQStreamingStatus(){
return gson.toJson(new String[]{String.valueOf(HQWidth), String.valueOf(HQHeight) , String.valueOf(grabbingTask!=null ? grabbingTask.getCaptureFPS(): "0")});
}
@NotNull
private GrabbingTask getGrabbingTask() {
GrabbingTask tt = new GrabbingTask(isGrabbing, grabber,10);
tt.setOnMessageUpdate(Logger::info);
tt.setOnHQFrameUpdate(value -> {
if (value!=null){
if (value.imageWidth>0 && value.imageHeight>0){
setLastHQFrame(value);
HQWidth = value.imageWidth;
HQHeight = value.imageHeight;
}
}
});
tt.setOnLQFrameUpdate(value -> {
if (value!=null){
if (value.imageWidth>0 && value.imageHeight>0){
setLastLQFrame(value);
LQWidth = value.imageWidth;
LQHeight = value.imageHeight;
}
}
});
tt.setOnHQBase64Update(this::setLastHQBase64);
tt.setOnLQBase64Update(this::setLastLQBase64);
tt.setOnYoloUpdate(this::setLastYoloFrame);
tt.setOnYoloBase64Update(this::setLastYoloBase64);
return tt;
}
}

View File

@@ -0,0 +1,169 @@
package Camera;
import lombok.Getter;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.jetbrains.annotations.NotNull;
import org.tinylog.Logger;
import java.util.concurrent.atomic.AtomicBoolean;
import static Other.SomeCodes.gson;
@SuppressWarnings("unused")
public class RtspGrabber_opencv {
private final String rtspUrl;
private FFmpegFrameGrabber grabber;
private final AtomicBoolean isGrabbing = new AtomicBoolean(false);
private Frame lastHQFrame = null;
private Frame lastLQFrame = null;
private Frame lastYoloFrame = null;
private String lastHQBase64 = null;
private String lastLQBase64 = null;
private String lastYoloBase64 = 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_opencv(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;
}
private synchronized void setLastYoloBase64(String base64){
lastYoloBase64 = base64;
}
public synchronized String getLastYoloBase64(){
return lastYoloBase64;
}
private synchronized void setLastYoloFrame(Frame frame){
lastYoloFrame = frame;
}
public synchronized Frame getLastYoloFrame(){
return lastYoloFrame;
}
/**
* Start grabbing frames from rtsp
* @param useTcp Use tcp instead of udp
*/
public void Start(boolean useTcp, final int width, final int height){
try{
grabber = FFmpegFrameGrabber.createDefault(rtspUrl);
if (useTcp) grabber.setOption("rtsp_transport", "tcp");
grabber.setPixelFormat(avutil.AV_PIX_FMT_BGR24);
grabber.start();
avutil.av_log_set_level(avutil.AV_LOG_ERROR);
Logger.info("Grabber started");
GrabbingTask tt = getGrabbingTask();
grabbingTask = tt;
new Thread(tt).start();
} catch (Exception e){
Logger.error("Error starting grabber: " + e.getMessage());
}
}
/**
* Stop grabbing frames
*/
public void Stop(){
isGrabbing.set(false);
if (grabbingTask!=null){
grabbingTask.Stop();
}
grabbingTask = null;
if (grabber!=null) {
try{
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!=null ? grabbingTask.getCaptureFPS():"0")});
}
public String HQStreamingStatus(){
return gson.toJson(new String[]{String.valueOf(HQWidth), String.valueOf(HQHeight) , String.valueOf(grabbingTask!=null ? grabbingTask.getCaptureFPS(): "0")});
}
@NotNull
private GrabbingTask getGrabbingTask() {
GrabbingTask tt = new GrabbingTask(isGrabbing, grabber,5);
tt.setOnMessageUpdate(Logger::info);
tt.setOnHQFrameUpdate(value -> {
if (value!=null){
if (value.imageWidth>0 && value.imageHeight>0){
setLastHQFrame(value);
HQWidth = value.imageWidth;
HQHeight = value.imageHeight;
}
}
});
tt.setOnLQFrameUpdate(value -> {
if (value!=null){
if (value.imageWidth>0 && value.imageHeight>0){
setLastLQFrame(value);
LQWidth = value.imageWidth;
LQHeight = value.imageHeight;
}
}
});
tt.setOnHQBase64Update(this::setLastHQBase64);
tt.setOnLQBase64Update(this::setLastLQBase64);
tt.setOnYoloUpdate(this::setLastYoloFrame);
tt.setOnYoloBase64Update(this::setLastYoloBase64);
return tt;
}
}

View File

@@ -1,5 +1,6 @@
package Camera;
import lombok.Getter;
import org.tinylog.Logger;
import java.net.http.HttpClient;
@@ -27,7 +28,7 @@ public class VapixProtocol {
private String[] ImageResolutions = new String[0];
private String[] ImageFormats = new String[0];
private int CurrentZoomValue = 0;
@Getter private boolean successQuery = false;
/**
* Create a new VapixProtocol object
* @param ip The IP address of the camera
@@ -83,6 +84,7 @@ public class VapixProtocol {
if (ValidString(value)) ImageFormats = value.split(",");
//Logger.info("Format: "+value);
}
successQuery = true;
}
}

View File

@@ -0,0 +1,169 @@
package Camera;
import Other.SomeCodes;
import id.co.gtc.Main;
import lombok.Getter;
import org.bytedeco.javacpp.DoublePointer;
import org.bytedeco.javacpp.FloatPointer;
import org.bytedeco.javacpp.IntPointer;
import org.bytedeco.opencv.global.opencv_core;
import org.bytedeco.opencv.global.opencv_dnn;
import org.bytedeco.opencv.global.opencv_imgproc;
import org.bytedeco.opencv.opencv_core.*;
import org.bytedeco.opencv.opencv_dnn.Net;
import org.opencv.imgproc.Imgproc;
import org.tinylog.Logger;
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;
import static org.bytedeco.opencv.global.opencv_core.CV_32F;
public class YoloDetector {
private Net net;
private @Getter boolean NetLoaded;
private List<String> classes ;
public YoloDetector() {
NetLoaded = false;
net = null;
classes = null;
String namesfile = SomeCodes.ExtractResource("/coco.names", SomeCodes.currentDirectory);
String netfile = SomeCodes.ExtractResource("/yolov8n.onnx", SomeCodes.currentDirectory);
if (SomeCodes.ValidFile(netfile)){
net = opencv_dnn.readNetFromONNX(netfile);
if (Main.haveCuda){
net.setPreferableBackend(opencv_dnn.DNN_BACKEND_CUDA);
net.setPreferableTarget(opencv_dnn.DNN_TARGET_CUDA_FP16);
Logger.info("Net loaded with CUDA");
} else {
net.setPreferableBackend(opencv_dnn.DNN_BACKEND_OPENCV);
net.setPreferableTarget(opencv_dnn.DNN_TARGET_CPU);
Logger.info("Net loaded with CPU");
}
} else Logger.error("net file not found");
if (net!=null){
classes = new ArrayList<>();
if (namesfile!=null){
try(BufferedReader br = new BufferedReader(new FileReader(namesfile))) {
String line;
while ((line = br.readLine()) != null) {
classes.add(line);
}
} catch (Exception e){
Logger.error("reading names file, Exception : ",e.getMessage());
}
}
if (!net.empty()){
if (!classes.isEmpty()){
NetLoaded = true;
} else Logger.error("names file is empty");
} else Logger.error("net is empty");
} else Logger.error("Net is not loaded");
}
// Source : https://blog.csdn.net/taoli188/article/details/134720614
public Mat[] Detect(Mat frame){
try{
if (NetLoaded){
Size inputSize = new Size(640, 640);
Scalar mean = new Scalar(0, 0, 0, 0);
double scaleFactor = 1.0f/255.0f;
boolean swapRB = true;
boolean crop = false;
//Mat blob = opencv_dnn.blobFromImage(frame, scaleFactor, inputSize, mean , swapRB, crop, CV_32F);
Mat blob = new Mat();
opencv_dnn.blobFromImage(frame, blob, scaleFactor, inputSize, mean, swapRB, crop, CV_32F);
net.setInput(blob);
Mat predict = net.forward();
Mat mask = predict.reshape(0,1).reshape(0, predict.size(1));
return new Mat[]{predict,mask};
} else {
Logger.error("Net not loaded");
return new Mat[0];
}
} catch (Exception e){
Logger.error("Error detecting: "+e.getMessage());
return new Mat[0];
}
}
@SuppressWarnings("CallToPrintStackTrace")
public Mat Process(Mat frame, Mat predict, Mat mask){
try{
double width = frame.cols() / 640.0;
double height = frame.rows() / 640.0;
Rect2d[] rect2d = new Rect2d[mask.cols()];
float[] scoref = new float[mask.cols()];
int[] classid = new int[mask.cols()];
for(int i=0;i<mask.cols();i++){
double[] x = SomeCodes.getColumnValues(mask,i, new int[]{0});
double[] y = SomeCodes.getColumnValues(mask,i, new int[]{1});
double[] w = SomeCodes.getColumnValues(mask,i, new int[]{2});
double[] h = SomeCodes.getColumnValues(mask,i, new int[]{3});
rect2d[i] = new Rect2d((x[0]-w[0]/2)*width, (y[0]-h[0]/2)*height, w[0]*width, h[0]*height);
//Mat score = mask.col(i).submat(4, predict.size(1)-1,0,1) ;
Mat score = SomeCodes.getSubmat(mask,i,4,predict.size(1)-1);
// Core.MinMaxLocResult mm = Core.minMaxLoc(score);
// scoref[i] = (float)mm.maxVal;
// classid[i] = (int)mm.maxLoc.y;
DoublePointer minVal = new DoublePointer(1);
DoublePointer maxVal = new DoublePointer(1);
Point minLoc = new Point();
Point maxLoc = new Point();
opencv_core.minMaxLoc(score, minVal, maxVal, minLoc, maxLoc, null);
scoref[i] = (float)maxVal.get();
classid[i] = (int)maxLoc.y();
}
//MatOfRect2d bboxes = new MatOfRect2d(rect2d);
Rect2dVector bboxes = new Rect2dVector(rect2d);
//MatOfFloat scores = new MatOfFloat(scoref);
FloatPointer scores = new FloatPointer(scoref);
//MatOfInt indices = new MatOfInt();
//IntVector indices = new IntVector();
IntPointer indices = new IntPointer();
// minimum confidence for detections
float confidence_threshold = 0.4f;
// Non-maximum suppression threshold
float nms_threshold = 0.4f;
opencv_dnn.NMSBoxes(bboxes, scores, confidence_threshold, nms_threshold, indices);
if (indices.limit()>0){
int[] idx = new int[(int)indices.limit()];
indices.get(idx);
for(int ii : idx){
Rect rr = new Rect(SomeCodes.toPoint(rect2d[ii].tl()), SomeCodes.toSize(rect2d[ii].size()));
opencv_imgproc.rectangle(frame,rr, Scalar.GREEN);
opencv_imgproc.putText(frame, classes.get(classid[ii])+" "+String.format("%.2f", scoref[ii]), rr.tl(), Imgproc.FONT_HERSHEY_SIMPLEX, 1.0, Scalar.RED);
}
}
} catch (Exception e){
Logger.error("Error processing detection: "+e.getMessage());
e.printStackTrace();
}
return frame;
}
}

View File

@@ -0,0 +1,146 @@
package Camera;
import Other.SomeCodes;
import id.co.gtc.Main;
import lombok.Getter;
import org.opencv.core.*;
import org.opencv.dnn.Dnn;
import org.opencv.dnn.Net;
import org.opencv.imgproc.Imgproc;
import org.tinylog.Logger;
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;
import static org.bytedeco.opencv.global.opencv_core.CV_32F;
public class YoloDetector_opencv {
private Net net;
private @Getter boolean NetLoaded;
private List<String> classes ;
public YoloDetector_opencv() {
NetLoaded = false;
net = null;
classes = null;
String namesfile = SomeCodes.ExtractResource("/coco.names", SomeCodes.currentDirectory);
String netfile = SomeCodes.ExtractResource("/yolov8n.onnx", SomeCodes.currentDirectory);
if (SomeCodes.ValidFile(netfile)){
net = Dnn.readNetFromONNX(netfile);
if (Main.haveCuda){
net.setPreferableBackend(Dnn.DNN_BACKEND_CUDA);
net.setPreferableTarget(Dnn.DNN_TARGET_CUDA_FP16);
Logger.info("Net loaded with CUDA");
} else {
net.setPreferableBackend(Dnn.DNN_BACKEND_OPENCV);
net.setPreferableTarget(Dnn.DNN_TARGET_CPU);
Logger.info("Net loaded with CPU");
}
} else Logger.error("net file not found");
if (net!=null){
classes = new ArrayList<>();
if (namesfile!=null){
try(BufferedReader br = new BufferedReader(new FileReader(namesfile))) {
String line;
while ((line = br.readLine()) != null) {
classes.add(line);
}
} catch (Exception e){
Logger.error("reading names file, Exception : ",e.getMessage());
}
}
if (!net.empty()){
if (!classes.isEmpty()){
NetLoaded = true;
} else Logger.error("names file is empty");
} else Logger.error("net is empty");
} else Logger.error("Net is not loaded");
}
// Source : https://blog.csdn.net/taoli188/article/details/134720614
public Mat[] Detect(Mat frame){
try{
if (NetLoaded){
Size inputSize = new Size(640, 640);
Scalar mean = new Scalar(0, 0, 0, 0);
float scaleFactor = 1.0f/255.0f;
boolean swapRB = true;
boolean crop = false;
Mat blob = Dnn.blobFromImage(frame, scaleFactor, inputSize, mean , swapRB, crop, CV_32F);
//Mat blob = Dnn.blobFromImage(frame, scaleFactor, inputSize);
net.setInput(blob);
Mat predict = net.forward();
Mat mask = predict.reshape(0,1).reshape(0, predict.size(1));
return new Mat[]{predict,mask};
} else {
Logger.error("Net not loaded");
return new Mat[0];
}
} catch (Exception e){
Logger.error("Error detecting: "+e.getMessage());
return new Mat[0];
}
}
@SuppressWarnings("CallToPrintStackTrace")
public Mat Process(Mat frame, Mat predict, Mat mask){
try{
double width = frame.cols() / 640.0;
double height = frame.rows() / 640.0;
Rect2d[] rect2d = new Rect2d[mask.cols()];
float[] scoref = new float[mask.cols()];
int[] classid = new int[mask.cols()];
for(int i=0;i<mask.cols();i++){
double[] x = mask.col(i).get(0, 0);
double[] y = mask.col(i).get(1, 0);
double[] w = mask.col(i).get(2, 0);
double[] h = mask.col(i).get(3, 0);
rect2d[i] = new Rect2d((x[0]-w[0]/2)*width, (y[0]-h[0]/2)*height, w[0]*width, h[0]*height);
Mat score = mask.col(i).submat(4, predict.size(1)-1,0,1) ;
Core.MinMaxLocResult mm = Core.minMaxLoc(score);
scoref[i] = (float)mm.maxVal;
classid[i] = (int)mm.maxLoc.y;
}
MatOfRect2d bboxes = new MatOfRect2d(rect2d);
MatOfFloat scores = new MatOfFloat(scoref);
MatOfInt indices = new MatOfInt();
// minimum confidence for detections
float confidence_threshold = 0.4f;
// Non-maximum suppression threshold
float nms_threshold = 0.4f;
Dnn.NMSBoxes(bboxes, scores, confidence_threshold, nms_threshold, indices);
if (!indices.empty()){
int[] idx = indices.toArray();
for(int ii : idx){
Rect rr = new Rect(rect2d[ii].tl(), rect2d[ii].size());
Imgproc.rectangle(frame, rr, new Scalar(0, 255, 0), 2);
Imgproc.putText(frame, classes.get(classid[ii])+" "+String.format("%.2f", scoref[ii]), rr.tl(), Imgproc.FONT_HERSHEY_SIMPLEX, 1.0, new Scalar(0, 0, 255));
}
}
} catch (Exception e){
Logger.error("Error processing detection: "+e.getMessage());
e.printStackTrace();
}
return frame;
}
}

View File

@@ -1,59 +1,65 @@
package Other;
import org.bytedeco.javacpp.Loader;
import com.google.gson.Gson;
import com.sun.jna.Platform;
import org.bytedeco.javacpp.BytePointer;
import org.bytedeco.javacpp.indexer.DoubleIndexer;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.bytedeco.javacv.OpenCVFrameConverter;
import org.bytedeco.opencv.global.opencv_core;
import org.bytedeco.opencv.global.opencv_imgcodecs;
import org.bytedeco.opencv.global.opencv_imgproc;
import org.bytedeco.opencv.opencv_core.Mat;
import org.bytedeco.opencv.opencv_core.Size;
import org.bytedeco.opencv.opencv_core.UMat;
import org.bytedeco.opencv.opencv_java;
import org.bytedeco.opencv.opencv_core.*;
import org.jetbrains.annotations.NotNull;
import org.opencv.core.MatOfByte;
import org.opencv.imgcodecs.Imgcodecs;
import org.tinylog.Logger;
import java.io.*;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.nio.file.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.Properties;
@SuppressWarnings("unused")
public class SomeCodes {
static{
Loader.load(opencv_java.class);
//Loader.load(opencv_java.class);
if (Platform.isLinux()){
LoadLinuxLibrary("/usr/local/lib/libopencv_java4100.so");
} else Logger.info("Platform is not Linux, loading standard opencv");
}
public final static String currentDirectory = System.getProperty("user.dir");
public final static Path audioPath = Path.of(currentDirectory, "audiofiles");
private static final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static final OpenCVFrameConverter.ToMat matConverter = new OpenCVFrameConverter.ToMat();
public static final OpenCVFrameConverter.ToOrgOpenCvCoreMat CoreMatConverter = new OpenCVFrameConverter.ToOrgOpenCvCoreMat();
public static final Java2DFrameConverter frameConverter = new Java2DFrameConverter();
public static final Path logsPath = Path.of(currentDirectory, "logs");
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 final double KB_threshold = 1024.0;
public static final double MB_threshold = 1024.0 * 1024.0;
public static final double GB_threshold = 1024.0 * 1024.0 * 1024.0;
private static void LoadLinuxLibrary(String library){
if (Platform.isLinux()){
File ff = new File(library);
if (ff.isFile()){
Logger.info("Loading library: "+library);
System.load(ff.getAbsolutePath());
}
}
}
@SuppressWarnings("resource")
public static String[] GetAudioFiles(){
try{
return Files.list(audioPath).map(f -> f.getFileName().toString()).toArray(String[]::new);
} catch (Exception e){
Logger.error("Error getting audio files: "+e.getMessage());
}
return new String[0];
}
public static String LocalDateTimeToString(LocalDateTime x){
return x.format(dtf);
return new String[0];
}
public static String ExtractResource(String filename, String targetdirectory){
@@ -83,13 +89,7 @@ public class SomeCodes {
public static boolean ValidDirectory(String path){
if (ValidString(path)){
File ff = new File(path);
return ff.isDirectory();
}
return false;
}
public static boolean ValidFile(String filename){
if (ValidString(filename)){
@@ -116,6 +116,13 @@ public class SomeCodes {
return defaultspeed;
}
public static boolean ValidIntArray(int[] xx){
if (xx!=null){
return xx.length>0;
}
return false;
}
public static boolean ValidInteger(String x){
try{
Integer.parseInt(x);
@@ -125,6 +132,15 @@ public class SomeCodes {
}
}
public static String IntArrayToString(int[] id){
StringBuilder sb = new StringBuilder();
for (int i : id){
if (!sb.isEmpty()) sb.append(",");
sb.append(i);
}
return "["+ sb +"]";
}
public static boolean ValidPortNumber(int port){
return port>0 && port<65536;
}
@@ -138,71 +154,126 @@ public class SomeCodes {
}
}
public static boolean ValidIPV4(String ipaddress){
if (ValidString(ipaddress)){
try{
InetAddress inet = InetAddress.getByName(ipaddress);
if (inet instanceof Inet4Address){
if (inet.getHostAddress().equals(ipaddress)){
return true;
}
public static String MatToBase64(Mat mat){
if (mat!=null){
if (mat.cols()>0 && mat.rows()>0){
try{
BytePointer mob = new BytePointer();
opencv_imgcodecs.imencode(".jpg", mat, mob);
byte[] data = new byte[(int) mob.limit()];
mob.get(data);
String base64 = base64encoder.encodeToString(data);
mob.deallocate();
return base64;
} catch (Exception e){
Logger.error("Error converting Mat to Base64: "+e.getMessage());
}
} catch (Exception ignored) {
}
}
return false;
}
public static boolean ValidIPV6(String ipaddress){
if (ValidString(ipaddress)){
try{
InetAddress inet = InetAddress.getByName(ipaddress);
if (inet instanceof Inet6Address){
if (inet.getHostAddress().equals(ipaddress)){
return true;
}
}
} catch (Exception ignored) {
}
}
return false;
}
public static String GetFileName(String filepath){
if (ValidString(filepath)){
File ff = new File(filepath);
if (ff.isFile()){
return ff.getName();
}
}
return "";
}
// Function ini pakai opencv, bukan javacv, jadi perlu Loader.load(opencv_java.class) di awal
// lebih optimal untuk konversi frame ke base64
public static String FrameToBase64(Frame frame){
if (frame!=null){
org.opencv.core.Mat converted = CoreMatConverter.convert(frame);
if (converted!=null){
if (!converted.empty()){
public static String MatToBase64(org.opencv.core.Mat mat){
if (mat!=null){
if (mat.cols()>0 && mat.rows()>0){
try{
MatOfByte mob = new MatOfByte();
Imgcodecs.imencode(".jpg", converted, mob);
byte[] jpgdata = mob.toArray();
Imgcodecs.imencode(".jpg", mat, mob);
String base64 = base64encoder.encodeToString(mob.toArray());
mob.release();
converted.release();
return base64encoder.encodeToString(jpgdata);
return base64;
} catch (Exception e){
Logger.error("Error converting Mat to Base64: "+e.getMessage());
}
}
}
return "";
}
public static Frame MatToFrame(Mat mat){
if (mat!=null && mat.cols()>0 && mat.rows()>0){
try{
return CoreMatConverter.convert(mat);
} catch (Exception e){
Logger.error("Error converting Mat to Frame: "+e.getMessage());
}
}
return null;
}
public static Frame MatToFrame(org.opencv.core.Mat mat){
if (mat!=null && mat.cols()>0 && mat.rows()>0){
try{
return CoreMatConverter.convert(mat);
} catch (Exception e){
Logger.error("Error converting Mat to Frame: "+e.getMessage());
}
}
return null;
}
public static double[] getColumnValues(Mat mat, int colIndex, int[] rowIndices) {
if (mat.empty()) {
throw new IllegalArgumentException("Mat is empty.");
}
if (colIndex < 0 || colIndex >= mat.cols()) {
throw new IllegalArgumentException("Column index out of range.");
}
// Get the column as a new Mat
Mat colMat = mat.col(colIndex);
// Create an indexer for efficient access
DoubleIndexer indexer = colMat.createIndexer();
// Extract specific row values
double[] values = new double[rowIndices.length];
for (int i = 0; i < rowIndices.length; i++) {
values[i] = indexer.get(rowIndices[i], 0);
}
return values;
}
public static Point toPoint(Point2d p) {
return new Point((int)p.x(), (int)p.y());
}
public static Size toSize(Size2d s) {
return new Size((int)s.width(), (int)s.height());
}
public static Mat getSubmat(Mat mask, int colIndex, int startRow, int endRow) {
if (mask.empty()) {
throw new IllegalArgumentException("Mat is empty.");
}
if (colIndex < 0 || colIndex >= mask.cols()) {
throw new IllegalArgumentException("Column index out of range.");
}
if (startRow < 0 || endRow > mask.rows() || startRow >= endRow) {
throw new IllegalArgumentException("Invalid row range.");
}
// Extract column i
Mat colMat = mask.col(colIndex);
// Extract submatrix from row startRow to endRow
return colMat.rowRange(startRow, endRow);
}
/**
* 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 +283,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){
@@ -229,6 +307,7 @@ public class SomeCodes {
return config.getProperty(key, null);
}
@Deprecated
public static Mat ResizeMat(Mat source, int width, int height){
Size sz = new Size(width, height);
Mat dest = new Mat();
@@ -244,9 +323,35 @@ public class SomeCodes {
return dest;
}
public static Frame ResizeFrame(Frame source, int width, int height){
Mat mat = matConverter.convertToMat(source);
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;
}
public static void Sleep(int milliseconds){
try{
Thread.sleep(milliseconds);
} catch (Exception e){
Logger.error("Error sleeping: "+e.getMessage());
}
}
public static GpuMat ConvertToGpuMat(Mat mat){
GpuMat gmat = new GpuMat();
gmat.upload(mat);
return gmat;
}
}

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

@@ -1,32 +1,28 @@
package SBC;
import com.sun.jna.Platform;
import org.jetbrains.annotations.NotNull;
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");
public static final Path gpioPath = Path.of("/sys/class/gpio");
private static final Path gpioExportPath = Path.of("/sys/class/gpio/export");
private static final Path gpioUnexportPath = Path.of("/sys/class/gpio/unexport");
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;
}
@@ -36,8 +32,8 @@ public class GPIO {
* @param pin GPIO pin number
* @return true if the pin is already exported
*/
public static boolean GpioPinExists(int pin){
Path pinPath = gpioPath.resolve("gpio"+pin);
public static boolean GpioPinExists(@NotNull JetsonOrinPins pin){
Path pinPath = gpioPath.resolve("gpio"+pin.gpionumber);
return pinPath.toFile().isDirectory();
}
@@ -46,32 +42,33 @@ public class GPIO {
* @param pin GPIO pin number
* @return true if the pin is successfully exported
*/
public static boolean ExportPin(int pin){
public static boolean ExportPin(@NotNull JetsonOrinPins pin){
try{
if (Files.isWritable(gpioExportPath)){
Files.write(gpioExportPath, String.valueOf(pin).getBytes());
Logger.info("Pin "+pin+" exported");
Files.write(gpioExportPath, String.valueOf(pin.gpionumber).getBytes());
Logger.info("Pin "+pin.pin+" exported");
return GpioPinExists(pin);
} else Logger.error("GPIO export path is not writable");
} catch (Exception e){
Logger.error("Failed to export pin: "+pin+", Message: "+e.getMessage());
Logger.error("Failed to export pin: "+pin.pin+", Message: "+e.getMessage());
}
return false;
}
/**
* Unexport the pin
* @param pin GPIO pin number
* @return true if the pin is successfully unexported
*/
public static boolean UnexportPin(int pin){
public static boolean UnexportPin(@NotNull JetsonOrinPins pin){
if (Files.isWritable(gpioUnexportPath)){
try{
Files.write(gpioUnexportPath, String.valueOf(pin).getBytes());
Logger.info("Pin "+pin+" unexported");
Files.write(gpioUnexportPath, String.valueOf(pin.gpionumber).getBytes());
Logger.info("Pin "+pin.pin+" unexported");
return true;
} catch (Exception e){
Logger.error("Failed to unexport pin: "+pin+", Message: "+e.getMessage());
Logger.error("Failed to unexport pin: "+pin.pin+", Message: "+e.getMessage());
}
} else Logger.error("GPIO unexport path is not writable");
@@ -83,17 +80,18 @@ public class GPIO {
* @param pin GPIO pin number
* @return "in" if the pin is input, "out" if the pin is output, "unknown" if the direction is unknown
*/
public static String GetPinDirection(int pin){
Path pinPath = gpioPath.resolve("gpio"+pin).resolve("direction");
@SuppressWarnings("unused")
public static String GetPinDirection(@NotNull JetsonOrinPins pin){
Path pinPath = gpioPath.resolve("gpio"+pin.gpionumber).resolve("direction");
if (pinPath.toFile().isFile()){
if (Files.isReadable(pinPath)){
try{
return Files.readString(pinPath).trim();
} catch (Exception e){
Logger.error("Failed to read pin direction: "+pin+", Message: "+e.getMessage());
Logger.error("Failed to read pin direction: "+pin.pin+", Message: "+e.getMessage());
}
} else Logger.error("Pin direction file is not readable: "+pin);
} else Logger.error("Pin direction file not found: "+pin);
} else Logger.error("Pin direction file is not readable: "+pin.pin);
} else Logger.error("Pin direction file not found: "+pin.pin);
return "unknown";
}
@@ -103,22 +101,22 @@ public class GPIO {
* @param direction "in" for input, "out" for output
* @return true if the direction is successfully set
*/
public static boolean SetPinDirection(int pin, String direction){
Path pinPath = gpioPath.resolve("gpio"+pin).resolve("direction");
public static boolean SetPinDirection(@NotNull JetsonOrinPins pin, String direction){
Path pinPath = gpioPath.resolve("gpio"+pin.gpionumber).resolve("direction");
if (pinPath.toFile().isFile()){
if (Files.isWritable(pinPath)){
direction = direction.trim().toLowerCase();
if ("in".equals(direction) || "out".equals(direction)){
try{
Files.write(pinPath, direction.getBytes());
Logger.info("Pin "+pin+" direction set to "+direction);
Logger.info("Pin "+pin.pin+" direction set to "+direction);
return true;
} catch (Exception e){
Logger.error("Failed to set pin direction: "+pin+", Message: "+e.getMessage());
Logger.error("Failed to set pin direction: "+pin.pin+", Message: "+e.getMessage());
}
} else Logger.error("Invalid direction: "+direction);
} else Logger.error("Pin direction file is not writable: "+pin);
} else Logger.error("Pin direction file not found: "+pin);
} else Logger.error("Pin direction file is not writable: "+pin.pin);
} else Logger.error("Pin direction file not found: "+pin.pin);
return false;
}
@@ -128,19 +126,20 @@ public class GPIO {
* @param isON true to set the pin value to 1, false to set the pin value to 0
* @return true if the value is successfully set
*/
public static boolean SetValue(int pin, boolean isON){
Path pinPath = gpioPath.resolve("gpio"+pin).resolve("value");
@SuppressWarnings("UnusedReturnValue")
public static boolean SetValue(@NotNull JetsonOrinPins pin, boolean isON){
Path pinPath = gpioPath.resolve("gpio"+pin.gpionumber).resolve("value");
if (pinPath.toFile().isFile()){
if (Files.isWritable(pinPath)){
try{
Files.write(pinPath, isON?"1".getBytes():"0".getBytes());
Logger.info("Pin "+pin+" value set to "+(isON?"1":"0"));
//Logger.info("Pin "+pin.pin+" value set to "+(isON?"1":"0"));
return true;
} catch (Exception e){
Logger.error("Failed to set pin value: "+pin+", Message: "+e.getMessage());
Logger.error("Failed to set pin value: "+pin.pin+", Message: "+e.getMessage());
}
} else Logger.error("Pin value file is not writable: "+pin);
} else Logger.error("Pin value file not found: "+pin);
} else Logger.error("Pin value file is not writable: "+pin.pin);
} else Logger.error("Pin value file not found: "+pin.pin);
return false;
}
@@ -149,17 +148,18 @@ public class GPIO {
* @param pin GPIO pin number
* @return "1" if the pin value is 1, "0" if the pin value is 0, "unknown" if the value is unknown
*/
public static String GetValue(int pin){
Path pinPath = gpioPath.resolve("gpio"+pin).resolve("value");
@SuppressWarnings("unused")
public static String GetValue(@NotNull JetsonOrinPins pin){
Path pinPath = gpioPath.resolve("gpio"+pin.gpionumber).resolve("value");
if (pinPath.toFile().isFile()){
if (Files.isReadable(pinPath)){
try{
return Files.readString(pinPath).trim();
} catch (Exception e){
Logger.error("Failed to read pin value: "+pin+", Message: "+e.getMessage());
Logger.error("Failed to read pin value: "+pin.pin+", Message: "+e.getMessage());
}
} else Logger.error("Pin value file is not readable: "+pin);
} else Logger.error("Pin value file not found: "+pin);
} else Logger.error("Pin value file is not readable: "+pin.pin);
} else Logger.error("Pin value file not found: "+pin.pin);
return "unknown";
}
}

View File

@@ -0,0 +1,38 @@
package SBC;
import lombok.Getter;
public class I2C_BUS {
private final Linux_C_lib C = Linux_C_lib.INSTANCE;
private @Getter boolean opened = false;
private final @Getter String i2c_name;
private @Getter int i2c_handle = -1;
public I2C_BUS(int bus_number){
this.i2c_name = "/dev/i2c-"+bus_number;
}
public boolean Open_Bus(){
if (!opened){
final int O_RDWR = 0x00000002;
i2c_handle = C.open(i2c_name, O_RDWR);
if (i2c_handle>=0){
opened = true;
return true;
}
}
return false;
}
public void Close_Bus(){
if (opened){
if (i2c_handle>=0){
C.close(i2c_handle);
i2c_handle = -1;
}
opened = false;
}
}
}

View File

@@ -0,0 +1,147 @@
package SBC;
import lombok.Getter;
@SuppressWarnings("unused")
public class I2C_Device {
Linux_C_lib C = Linux_C_lib.INSTANCE;
private @Getter int bus_handle;
private @Getter int device_address;
private @Getter int dev_handle;
private @Getter boolean opened = false;
/**
* Create I2C Device
* @param bus_handle I2C Bus Handle, from opening I2C_Bus
* @param device_address I2C Device Address
*/
public I2C_Device(int bus_handle, int device_address){
opened = false;
if (bus_handle<0 || device_address<0) return;
this.bus_handle = bus_handle;
this.device_address = device_address;
}
/**
* Open I2C Device in Slave mode
* @return true if successfully opened
*/
public boolean Open_Slave(){
boolean sucess = false;
dev_handle = C.ioctl(bus_handle, I2C_Flags.I2C_SLAVE, device_address);
if (dev_handle>=0){
sucess = true;
}
opened = sucess;
return opened;
}
/**
* Open I2C Device in Slave mode with Force
* @return true if successfully opened
*/
public boolean Open_SlaveForce(){
boolean sucess = false;
dev_handle = C.ioctl(bus_handle, I2C_Flags.I2C_SLAVE_FORCE, device_address);
if (dev_handle>=0){
C.ioctl(bus_handle,I2C_Flags.I2C_RETRIES, 3);
C.ioctl(bus_handle,I2C_Flags.I2C_TIMEOUT, 1000);
sucess = true;
}
opened = sucess;
return opened;
}
/**
* Open I2C Device in Read Write mode
* @return true if successfully opened
*/
public boolean Open_RDWR(){
boolean sucess = false;
dev_handle = C.ioctl(bus_handle, I2C_Flags.I2C_RDWR, device_address);
if (dev_handle>=0){
sucess = true;
}
opened = sucess;
return opened;
}
/**
* Open I2C Device in SMBus mode
* @return true if successfully opened
*/
public boolean Open_SMBus(){
boolean sucess = false;
dev_handle = C.ioctl(bus_handle, I2C_Flags.I2C_SMBUS, device_address);
if (dev_handle>=0){
sucess = true;
}
opened = sucess;
return opened;
}
/**
* Close I2C Device
*/
public void Close(){
C.close(dev_handle);
opened = false;
dev_handle = -1;
}
/**
* Write Bytes to I2C Device
* @param data bytes to write
* @return number of bytes written
*/
public int WriteBytes(byte[] data){
if (bus_handle<0 || dev_handle<0 || device_address<0) return -1;
return C.write(bus_handle, data, data.length);
}
/**
* Write Byte to I2C Device
* @param register_address register address
* @param data byte to write
* @return number of bytes written
*/
public int WriteBytes(int register_address, byte data){
byte[] cmd = new byte[2];
cmd[0] = (byte) register_address;
cmd[1] = data;
return WriteBytes(cmd);
}
/**
* Write Bytes to I2C Device
* @param register_address register address
* @param data bytes to write
* @return number of bytes written
*/
public int WriteBytes(int register_address, byte[] data){
byte[] cmd = new byte[data.length+1];
cmd[0] = (byte) register_address;
System.arraycopy(data, 0, cmd, 1, data.length);
return WriteBytes(cmd);
}
/**
* Read Bytes from I2C Device
* @param readsize number of bytes to read
* @return bytes read
*/
public byte[] ReadBytes(int readsize){
if (bus_handle<0 || dev_handle<0 || device_address<0) return null;
byte[] data = new byte[readsize];
int read = C.read(bus_handle, data, readsize);
if (read<0) return null;
if (read<readsize){
byte[] temp = new byte[read];
System.arraycopy(data, 0, temp, 0, read);
return temp;
}
return data;
}
}

View File

@@ -0,0 +1,13 @@
package SBC;
public class I2C_Flags {
public final static int I2C_RETRIES = 0x701; /* number of times a device address should be polled when not acknowledging */
public final static int I2C_TIMEOUT = 0x702; /* set timeout in units of 10 ms */
public final static int I2C_SLAVE = 0x703; /* Command at ioctl, means : Use this slave address */
public final static int I2C_TENBIT = 0x704; /* 0 for 7 bit addrs, != 0 for 10 bit */
public final static int I2C_FUNCS = 0x705; /* Command at ioctl, means : Get the adapter functionality */
public final static int I2C_SLAVE_FORCE = 0x706; /* Command at ioctl, means : Use this slave address, even if it is already in use by a driver! */
public final static int I2C_RDWR = 0x707; /* Command at ioctl, means : Combined R/W transfer (one stop only) */
public final static int I2C_PEC = 0x708; /* != 0 to use PEC with SMBus */
public final static int I2C_SMBUS = 0x720; /* SMBus transfer */
}

View File

@@ -0,0 +1,38 @@
package SBC;
/**
* Source : <a href="https://jetsonhacks.com/nvidia-jetson-orin-nano-gpio-header-pinout/">...</a>
*/
public enum JetsonOrinPins {
Pin07(7,"AUDIO_MCLK",144),
Pin11(11,"UART1_RTS",112),
Pin12(12,"I2S0_SCLK",50),
Pin13(13,"SPI1_SCK",122),
Pin15(15,"GPIO12",85),
Pin16(16,"SPI1_CS1",126),
Pin18(18,"SPI1_CS0",125),
Pin19(19,"SPI0_MOSI",135),
Pin21(21,"SPI0_MISO",134),
Pin22(22,"SPI1_MISO",123),
Pin23(23,"SPI0_SCK",133),
Pin24(24,"SPI0_CS0",136),
Pin25(26,"SPI0_CS1",137),
Pin29(29,"GPIO01",105),
Pin31(31,"GPIO11",106),
Pin32(32,"GPIO07",41),
Pin33(33,"GPIO13",43),
Pin35(35,"I2S0_FS",53),
Pin36(36,"UART1_CTS",113),
Pin37(37,"SPI1_MOSI",124),
Pin38(38,"I2S0_SDIN",52),
Pin40(40,"I2S0_SDOUT",51);
public final int pin;
public final String name;
public final int gpionumber;
JetsonOrinPins(int pin, String name, int gpionumber){
this.pin = pin;
this.name = name;
this.gpionumber = gpionumber;
}
}

View File

@@ -0,0 +1,20 @@
package SBC;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
public interface LibGpioD extends Library {
String CHIP_NAME = "/dev/gpiochip0"; // First GPIO chip
LibGpioD Instance = Native.load("gpiod", LibGpioD.class);
Pointer gpiod_chip_open(String chipname);
void gpiod_chip_close(Pointer chip);
Pointer gpiod_line_get(Pointer chip, int offset);
int gpiod_line_request_output(Pointer line, String consumer, int default_val);
int gpiod_line_request_input(Pointer line, String consumer);
int gpiod_line_set_value(Pointer line, int value);
int gpiod_line_get_value(Pointer line);
void gpiod_line_release(Pointer line);
}

View File

@@ -0,0 +1,69 @@
package SBC;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Native;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
public interface Linux_C_lib extends com.sun.jna.Library {
Linux_C_lib INSTANCE = (Linux_C_lib) Native.load("c", Linux_C_lib.class);
long memcpy(int[] dst, short[] src, long n);
int memcpy(int[] dst, short[] src, int n);
int pipe(int[] fds);
int tcdrain(int fd);
int fcntl(int bus_handle, int command, int args);
int ioctl(int bus_handle, int command, int args);
int ioctl(int bus_handle, int command, int... args);
int ioctl(int bus_handle, int command, Pointer args);
int open(String path, int flags);
int close(int fd);
int write(int bus_handle, byte[] buffer, int count);
int read(int bus_handle, byte[] buffer, int count);
long write(int bus_handle, byte[] buffer, long count);
long read(int bus_handle, byte[] buffer, long count);
int select(int n, int[] read, int[] write, int[] error, timeval timeout);
int poll(int[] fds, int nfds, int timeout);
int tcflush(int fd, int qs);
void perror(String msg);
int tcsendbreak(int fd, int duration);
public class timeval extends Structure {
public NativeLong tv_sec;
public NativeLong tv_usec;
protected List<String> getFieldOrder() {
return Arrays.asList(//
"tv_sec",//
"tv_usec"//
);
}
public timeval(long second, long usecond) {
tv_sec = new NativeLong(second);
tv_usec = new NativeLong(usecond);
}
}
}

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,164 @@
package SBC;
import lombok.Getter;
@SuppressWarnings("unused")
public class PCF8574 extends I2C_Device{
private Byte data = 0;
private final @Getter String[] pinNames = new String[8];
/**
* Create new PCF8574
* @param bus_number I2C Bus Number
* @param address PCF8574 Address
*/
public PCF8574(int bus_number, int address){
super(bus_number, address);
for(int i=0; i<8; i++){
pinNames[i] = i+"";
}
}
/**
* Set new Pin Name
* @param pin pin number, 0-7
* @param name new name
*/
public void setPinName(int pin, String name){
if (pin<0 || pin>7) return;
pinNames[pin] = name;
}
/**
* Get Pin Name
* @param pin pin number, 0-7
* @return pin name, or null if not available
*/
public String getPinName(int pin){
if (pin<0 || pin>7) return null;
return pinNames[pin];
}
/**
* Set All Pins High
* @return true if successful
*/
public boolean AllOn(){
if (super.isOpened()){
if (super.WriteBytes(new byte[]{(byte)0xFF})>0){
data = (byte)0xFF;
return true;
}
}
return false;
}
/**
* Set all Pins Low
* @return true if successful
*/
public boolean AllOff(){
if (super.isOpened()){
if (super.WriteBytes(new byte[]{(byte)0x00})>0){
data = (byte)0x00;
return true;
}
}
return false;
}
/**
* Set Pin by Pin Name
* @param pinName pin name
* @return true if successful
*/
public boolean SetPin(String pinName){
for(int i=0; i<8; i++){
if (pinNames[i].equals(pinName)){
return SetPin(i);
}
}
return false;
}
/**
* Set Pin High
* @param pin pin number 0-7
* @return true if successful
*/
public boolean SetPin(int pin){
if (pin<0 || pin>7) return false;
if (super.isOpened()){
synchronized (data){
data = (byte) (data | (1<<pin));
return super.WriteBytes(new byte[]{data})>0;
}
}
return false;
}
/**
* Clear Pin by Pin Name
* @param pinName pin name
* @return true if successful
*/
public boolean ClearPin(String pinName){
for(int i=0; i<8; i++){
if (pinNames[i].equals(pinName)){
return ClearPin(i);
}
}
return false;
}
/**
* Set Pin Low
* @param pin pin number 0-7
* @return true if successful
*/
public boolean ClearPin(int pin){
if (pin<0 || pin>7) return false;
if (super.isOpened()){
synchronized (data){
data = (byte) (data & ~(1<<pin));
return super.WriteBytes(new byte[]{data})>0;
}
}
return false;
}
/**
* Read Pin by Pin Name
* @param pinName pin name
* @return 1 if high, 0 if low, -1 if error
*/
public int ReadPin(String pinName){
for(int i=0; i<8; i++){
if (pinNames[i].equals(pinName)){
return ReadPin(i);
}
}
return -1;
}
/**
* Read Pin
* @param pin pin to read, 0 - 7
* @return 1 if high, 0 if low, -1 if error
*/
public int ReadPin(int pin){
if (pin<0 || pin>7) return -1;
if (super.isOpened()){
byte[] buffer = super.ReadBytes(1);
byte masker = (byte) (1<<pin);
return (buffer[0] & masker) > 0 ? 1 : 0;
}
return -1;
}
}

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

@@ -1,17 +1,21 @@
package Web;
import Other.SomeCodes;
import com.corundumstudio.socketio.Configuration;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.listener.ConnectListener;
import com.corundumstudio.socketio.listener.DisconnectListener;
import io.javalin.Javalin;
import io.javalin.http.UploadedFile;
import io.javalin.util.FileUtil;
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.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import static Other.SomeCodes.*;
@@ -20,20 +24,30 @@ 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();
private final SocketIOServer socketServer;
private final Configuration socketConfig;
private final HashMap<String, SocketIOClient> socketIOClients = new HashMap<>();
public WebServer(WebsocketEvent event, String webusername, String webpassword){
this.webusername = webusername;
this.webpassword = webpassword;
app = Javalin.create(config -> {
config.useVirtualThreads = true;
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 +55,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 +115,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 +127,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 +144,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 +168,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");
}
});
@@ -140,10 +182,13 @@ public class WebServer {
ctx.redirect("/login.html");
});
/*
WebSocket Communication
This is temporary, later must choose either WebSocket or SocketIO
*/
app.ws("/ws", ws -> {
ws.onConnect(connectws);
ws.onClose(closews);
//ws.onError(errorws);
ws.onMessage(ctx -> {
try{
//Logger.info("WebSocket message {}", ctx.message());
@@ -151,13 +196,39 @@ 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);
}
});
});
/*
SocketIO Communication
This is temporary, later must choose either WebSocket or SocketIO
*/
socketConfig = new Configuration();
socketConfig.setHostname("0.0.0.0");
socketConfig.setPort(3001);
socketServer = new SocketIOServer(socketConfig);
socketServer.addConnectListener(connectListener);
socketServer.addDisconnectListener(disconnectListener);
socketServer.addNamespace("/socketio").addEventListener("message", String.class, (client, data, ackSender) -> {
//Logger.info("SocketIO message from namespace /socketio from {}: {}", client.getRemoteAddress(), data);
WebsocketCommand command = gson.fromJson(data, WebsocketCommand.class);
if (event!=null) {
WebsocketReply reply = event.onWebsocketCommand(command);
if (reply!=null) client.sendEvent("message", gson.toJson(reply));
}
});
}
/**
@@ -165,7 +236,12 @@ public class WebServer {
* @param obj Object to send
*/
public void SendtoAll(Object obj){
connectedWebsocketClients.forEach(wsContext -> wsContext.send(obj));
connectedWebsocketClients
.stream()
.filter(ws -> ws.session.isOpen())
.forEach(ws -> ws.send(obj));
socketIOClients.forEach((key, client) -> client.sendEvent("message", gson.toJson(obj)));
}
/**
@@ -175,9 +251,10 @@ public class WebServer {
*/
public void Start(String localip, int port){
try{
connectedWebsocketClients.forEach(WsContext::closeSession);
app.start(localip, port);
Logger.info("Web server started at {}:{}", localip, port);
socketServer.start();
Logger.info("SocketIO server started at {}:{}", socketConfig.getHostname(), socketConfig.getPort());
} catch (JavalinException e){
Logger.error("Web server failed to start: {}", e.getMessage());
}
@@ -188,25 +265,42 @@ public class WebServer {
*/
public void Stop(){
try{
connectedWebsocketClients.forEach(WsContext::closeSession);
connectedWebsocketClients.clear();
app.stop();
Logger.info("Web server stopped");
socketServer.stop();
socketIOClients.forEach((key, client) -> client.disconnect());
socketIOClients.clear();
Logger.info("SocketIO server stopped");
} catch (JavalinException e){
Logger.error("Web server failed to stop: {}", e.getMessage());
e.printStackTrace();
}
}
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);
};
//WsErrorHandler errorws = ws -> Logger.error("WebSocket error from {}, error {}", ws.host(), ws.error());
ConnectListener connectListener = client -> {
Logger.info("SocketIO connected, id {} from {}", client.getSessionId(), client.getRemoteAddress());
socketIOClients.put(client.getSessionId().toString(), client);
};
DisconnectListener disconnectListener = client -> {
Logger.info("SocketIO disconnected, id {} from {}", client.getSessionId(), client.getRemoteAddress());
socketIOClients.remove(client.getSessionId().toString());
};

View File

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

View File

@@ -1,52 +1,403 @@
package id.co.gtc;
import Audio.AudioFileProperties;
import Audio.AudioPlayer;
import Audio.PlaybackEvent;
import Audio.*;
import Camera.PanTiltController;
import Camera.RtspGrabber;
import Camera.RtspGrabber_opencv;
import Camera.VapixProtocol;
import Other.SomeCodes;
import Web.WebServer;
import Web.WebsocketCommand;
import Web.WebsocketEvent;
import Web.WebsocketReply;
import SBC.*;
import Web.*;
import com.google.gson.JsonObject;
import com.sun.jna.Platform;
import com.sun.jna.Pointer;
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 java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static Other.SomeCodes.*;
public class Main {
//change parameter ini untuk single audio output atau multi audio output
private static final boolean use_multiusb_audio = true;
private static MultiUSBAudioPlayer multiUSBAudioPlayer;
private static MultiUSBAudioPlayer.PlaybackHandlewithId[] playbackHandles;
private static AudioPlayer audioPlayer;
private static WebServer webServer;
private static RtspGrabber rtspGrabber;
private static PanTiltController panTiltController;
private static AudioFileProperties audioFileProperties;
private static VapixProtocol vapixProtocol;
private static WebServer webServer;
//change parameter ini untuk menggunakan Yolo atau tidak
public static final boolean use_Yolo = false;
private static RtspGrabber_opencv rtspGrabber;
public static boolean haveCuda = false;
private static PanTiltController panTiltController;
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<>();
private static NetworkTransmitReceiveInfo[] previousNetworkInfo;
private static final Map<String, String> networkTX = new HashMap<>();
private static final Map<String, String> networkRX = new HashMap<>();
private static JetsonOrinPins AmplifierPower = null;
private static JetsonOrinPins LedWeb = null;
private static JetsonOrinPins LedIpCamera = null;
private static JetsonOrinPins LedPanTilt = null;
private static JetsonOrinPins Max485Direction = null;
private static ExecutorService gpioExecutor = null;
private static PCF8574 pcf8574 = null;
private static final String Version = "V1.09 (17/03/2025)";
// Application start from here
public static void main(String[] args) {
System.setProperty("jna.debug_load", "false");
System.out.println("BirdStrikeSoetta "+Version);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
if (audioPlayer!=null) audioPlayer.Unload();
if (webServer!=null) webServer.Stop();
if (rtspGrabber!=null) rtspGrabber.Stop();
if (panTiltController!=null) panTiltController.Close();
if (vapixProtocol!=null) vapixProtocol.Close();
if (use_multiusb_audio) {
if (multiUSBAudioPlayer != null) {
multiUSBAudioPlayer.Unload();
Logger.info("MultiUSB Audio Unloaded");
}
} else {
if (audioPlayer != null) {
audioPlayer.Unload();
Logger.info("Audio Unloaded");
}
}
if (webServer!=null) {
webServer.Stop();
Logger.info("Web Server stopped");
}
if (rtspGrabber!=null) {
rtspGrabber.Stop();
Logger.info("Rtsp Grabber stopped");
}
if (panTiltController!=null) {
panTiltController.Close();
Logger.info("Pan Tilt Controller closed");
}
if (vapixProtocol!=null) {
vapixProtocol.Close();
Logger.info("Vapix Protocol closed");
}
if (timer!=null) {
timer.cancel();
Logger.info("Timer cancelled");
}
if (gpioExecutor!=null) {
gpioExecutor.shutdown();
Logger.info("GPIO Executor shutdown");
}
if (AmplifierPower!=null) {
Logger.info("GPIO pin {} unexport : {}",AmplifierPower.pin, GPIO.UnexportPin(AmplifierPower));
}
if (LedWeb!=null) {
Logger.info("GPIO pin {} unexport : {}", LedWeb.pin,GPIO.UnexportPin(LedWeb));
}
if (LedIpCamera!=null) {
Logger.info("GPIO pin {} unexport : {}", LedIpCamera.pin, GPIO.UnexportPin(LedIpCamera));
}
if (LedPanTilt!=null) {
Logger.info("GPIO pin {} unexport : {}", LedPanTilt.pin,GPIO.UnexportPin(LedPanTilt));
}
if (Max485Direction!=null) {
Logger.info("GPIO pin {} unexport : {}", Max485Direction.pin, GPIO.UnexportPin(Max485Direction));
}
if (pcf8574!=null){
pcf8574.Close();
Logger.info("PCF8574 closed");
}
Logger.info("Application Stopped");
}));
Logger.info("Current Directory : "+currentDirectory);
//init_gpio();
init_pcf8574();
init_system_monitoring();
init_properties();
init_audiofiles();
init_audio();
init_Vapix();
if (use_multiusb_audio) {
Logger.info("Using MultiUSB Audio");
init_multiusb_audio();
} else {
Logger.info("Using USB Audio");
init_audio();
}
init_pantiltcontroller();
init_webserver();
init_rtspgrabber();
init_pantiltcontroller();
init_Vapix();
}
private static void init_pcf8574(){
if (Platform.isLinux()){
I2C_BUS i2c_bus = new I2C_BUS(7);
if (i2c_bus.Open_Bus()){
Logger.info("I2C Bus opened");
pcf8574 = new PCF8574(i2c_bus.getI2c_handle(), 0x27);
if (pcf8574.Open_Slave()){
Logger.info("PCF8574 opened");
pcf8574.AllOff();
// pcf8574.setPinName(0, "Amplifier Power");
// pcf8574.setPinName(1, "Led Web");
// pcf8574.setPinName(2, "Led IP Camera");
// pcf8574.setPinName(3, "Led Pan Tilt");
// pcf8574.setPinName(4, "Max485 Direction");
gpioExecutor = Executors.newVirtualThreadPerTaskExecutor();
} else {
Logger.error("Failed to open PCF8574");
}
} else {
Logger.error("Failed to open I2C Bus");
}
} else Logger.error("PCF8574 not supported on this platform");
}
// Untuk test libgpiod
// use for jetpack 6.x
@SuppressWarnings("unused")
private static void init_gpio(){
LibGpioD gpio;
Pointer openPointer;
// Source : https://jetsonhacks.com/nvidia-jetson-orin-nano-gpio-header-pinout/
int gpio_MOSI = 135;
int gpio_MISO = 134;
if (Platform.isLinux()){
gpio = LibGpioD.Instance;
openPointer = gpio.gpiod_chip_open(LibGpioD.CHIP_NAME);
if (openPointer!=null){
Logger.info("GPIO Chip opened : {}", LibGpioD.CHIP_NAME);
Pointer miso = gpio.gpiod_line_get(openPointer, gpio_MISO);
if (miso!=null){
Logger.info("GPIO Line MISO opened : {}", gpio_MISO);
if (gpio.gpiod_line_request_input(miso, "MISO")!=0){
Logger.error("Failed to set MISO as input");
} else {
Logger.info("MISO opened");
Logger.info("MISO value : {}", gpio.gpiod_line_get_value(miso));
}
gpio.gpiod_line_release(miso);
Logger.info("GPIO Line MISO released : {}", gpio_MISO);
} else Logger.error("Failed to open GPIO Line : {}", gpio_MISO);
Pointer mosi = gpio.gpiod_line_get(openPointer, gpio_MOSI);
if (mosi!=null){
Logger.info("GPIO Line MOSI opened : {}", gpio_MOSI);
if (gpio.gpiod_line_request_output(mosi, "MOSI", 0)!=0){
Logger.error("Failed to set MOSI as output");
} else {
Logger.info("MOSI opened");
int result = gpio.gpiod_line_set_value(mosi, 1);
if (result!=0){
Logger.error("Failed to set MOSI value : {}", result);
} else Logger.info("Success set MOSI value : 1");
}
gpio.gpiod_line_release(mosi);
Logger.info("GPIO Line MOSI released : {}", gpio_MOSI);
} else Logger.error("Failed to open GPIO Line : {}", gpio_MOSI);
gpio.gpiod_chip_close(openPointer);
Logger.info("GPIO Chip closed : {}", LibGpioD.CHIP_NAME);
} else Logger.error("Failed to open GPIO Chip : {}", LibGpioD.CHIP_NAME);
} else Logger.info("GPIO not supported on this platform");
}
// use for jetpack 4.x and 5.x
@SuppressWarnings("unused")
private static void init_gpio_sysfs(){
if (Platform.isLinux()){
if (GPIO.HaveGPIO()){
gpioExecutor = Executors.newVirtualThreadPerTaskExecutor();
AmplifierPower = InitializePin(JetsonOrinPins.Pin13, true);
LedWeb = InitializePin(JetsonOrinPins.Pin15, true);
LedIpCamera = InitializePin(JetsonOrinPins.Pin19, true);
LedPanTilt = InitializePin(JetsonOrinPins.Pin21, true);
Max485Direction = InitializePin(JetsonOrinPins.Pin23, true);
GPIO.SetValue(AmplifierPower,false);
GPIO.SetValue(LedWeb,false);
GPIO.SetValue(LedIpCamera,false);
GPIO.SetValue(LedPanTilt,false);
GPIO.SetValue(Max485Direction,false);
}
} else Logger.info("GPIO not supported on this platform");
}
@SuppressWarnings("SameParameterValue")
private static JetsonOrinPins InitializePin(JetsonOrinPins pin, boolean isOutput){
boolean exported = false;
if (GPIO.GpioPinExists(pin)){
exported = true;
} else {
if (GPIO.ExportPin(pin)){
exported = true;
}
}
if (exported){
if (GPIO.SetPinDirection(pin, isOutput?"out":"in")){
return pin;
}
}
return null;
}
// dipanggil di PanTiltController, maka perlu public dan static
public static void Max485Direction(boolean isTransmit){
if (Max485Direction!=null){
GPIO.SetValue(Max485Direction, isTransmit);
} else if (pcf8574!=null && pcf8574.isOpened()){
// if (isTransmit){
// pcf8574.SetPin("Max485 Direction");
// } else {
// pcf8574.ClearPin("Max485 Direction");
// }
}
}
// dipanggil di PanTiltController, maka perlu public dan static
public static void Blink_LedPanTilt(){
if (LedPanTilt!=null){
Blink(LedPanTilt);
} else if (pcf8574!=null && pcf8574.isOpened()){
Blink(1);
}
}
public static void Blink_LedWeb(){
if (LedWeb!=null){
Blink(LedWeb);
} else if (pcf8574!=null && pcf8574.isOpened()){
Blink(3);
}
}
public static void Blink_LedIpCamera(){
if (LedIpCamera!=null){
Blink(LedIpCamera);
} else if (pcf8574!=null && pcf8574.isOpened()){
Blink(2);
}
}
private static void AmplifierControl(boolean isON){
if (AmplifierPower!=null){
Logger.info("GPIO Amplifier Power : ",isON);
GPIO.SetValue(AmplifierPower, isON);
} else if (pcf8574!=null && pcf8574.isOpened()){
if (isON){
Logger.info("PCF8574 Amplifier Power ON");
pcf8574.SetPin(6);
pcf8574.SetPin(5);
pcf8574.SetPin(4);
} else {
Logger.info("PCF8574 Amplifier Power OFF");
pcf8574.ClearPin(6);
pcf8574.ClearPin(5);
pcf8574.ClearPin(4);
}
}
}
private static void Blink(int pin){
if (pcf8574!=null && pcf8574.isOpened()){
if (gpioExecutor!=null){
gpioExecutor.submit(()->{
pcf8574.SetPin(pin);
Sleep(20);
pcf8574.ClearPin(pin);
});
}
}
}
private static void Blink(JetsonOrinPins pin){
if (pin!=null){
if (gpioExecutor!=null){
gpioExecutor.submit(()->{
GPIO.SetValue(pin, true);
Sleep(20);
GPIO.SetValue(pin, false);
});
}
}
}
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]));
}
previousCpuInfo = cpuinfo;
}
}
NetworkTransmitReceiveInfo[] ntri = SystemInformation.getNetworkTransmitReceiveInfo();
if (ntri.length>0){
if (previousNetworkInfo==null || !Objects.equals(previousNetworkInfo.length, ntri.length)){
previousNetworkInfo = ntri;
} else {
for(int ii=0;ii<previousNetworkInfo.length;ii++){
double txspeed = ntri[ii].TxSpeed(previousNetworkInfo[ii], "B");
String txspeedstr;
if (txspeed < KB_threshold){
txspeedstr = String.format("%.0f B/s", txspeed);
} else if (txspeed < MB_threshold){
txspeedstr = String.format("%.1f KB/s", ntri[ii].TxSpeed(previousNetworkInfo[ii], "KB"));
} else if (txspeed < GB_threshold){
txspeedstr = String.format("%.1f MB/s", ntri[ii].TxSpeed(previousNetworkInfo[ii], "MB"));
} else {
txspeedstr = String.format("%.1f GB/s", ntri[ii].TxSpeed(previousNetworkInfo[ii], "GB"));
}
double rxspeed = ntri[ii].RxSpeed(previousNetworkInfo[ii], "B");
String rxspeedstr;
if (rxspeed < KB_threshold){
rxspeedstr = String.format("%.0f B/s", rxspeed);
} else if (rxspeed < MB_threshold){
rxspeedstr = String.format("%.1f KB/s", ntri[ii].RxSpeed(previousNetworkInfo[ii], "KB"));
} else if (rxspeed < GB_threshold){
rxspeedstr = String.format("%.1f MB/s", ntri[ii].RxSpeed(previousNetworkInfo[ii], "MB"));
} else {
rxspeedstr = String.format("%.1f GB/s", ntri[ii].RxSpeed(previousNetworkInfo[ii], "GB"));
}
networkTX.put(ntri[ii].name, txspeedstr);
networkRX.put(ntri[ii].name, rxspeedstr);
}
previousNetworkInfo = ntri;
}
}
}
};
timer = new Timer();
timer.scheduleAtFixedRate(tt, 1000, 5000);
}
private static void init_properties(){
@@ -68,37 +419,57 @@ public class Main {
private static void init_Vapix(){
Properties config = SomeCodes.LoadProperties("config.properties");
Logger.info("Saved Configuration for Camera");
String ip = config.getProperty("Camera_ip");
String port = config.getProperty("Camera_port");
String username = config.getProperty("Camera_user");
String password = config.getProperty("Camera_password");
Logger.info("Camera IP : "+ip);
Logger.info("Camera Port : "+port);
Logger.info("Camera Username : "+username);
Logger.info("Camera Password : "+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);
if (vapixProtocol.isSuccessQuery()){
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());
Blink_LedIpCamera();
} else Logger.error("Failed to query camera");
} 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() {
Properties config = SomeCodes.LoadProperties("config.properties");
String portname = config.getProperty("SerialPort");
String baudrate = config.getProperty("SerialBaudRate");
String PanTiltID = config.getProperty("PanTiltID");
Logger.info("Saved Configuration for Pan Tilt Controller");
Logger.info("Serial Port : "+portname);
Logger.info("Serial Baud Rate : "+baudrate);
Logger.info("Pan Tilt ID : "+PanTiltID);
if (ValidString(portname)){
if (ValidInteger(baudrate)){
if (ValidInteger(PanTiltID)){
panTiltController = new PanTiltController(portname, Integer.parseInt(baudrate), Integer.parseInt(PanTiltID));
//if (panTiltController.isOpen()) test_pantiltcontroller();
panTiltController.DisableMovementTest();
}
} else Logger.error("Invalid PTZ Baudrate");
} else Logger.error("Invalid PTZ Port");
@@ -109,41 +480,99 @@ public class Main {
// check if really activated
useOpenCL = opencv_core.useOpenCL();
Logger.info("OpenCL available={}, activated={}", haveOpenCL, useOpenCL);
haveCuda = opencv_core.getCudaEnabledDeviceCount()>0;
if (haveCuda){
Logger.info("CUDA enabled devices found: " + opencv_core.getCudaEnabledDeviceCount());
//opencv_core.printCudaDeviceInfo(0);
//Logger.info("CUDA enabled : "+opencv_core.useOptimized());
opencv_core.setUseOptimized(true);
Logger.info("CUDA enabled : "+opencv_core.useOptimized());
} else {
Logger.info("No CUDA enabled devices found");
}
// BytePointer buildinfo = opencv_core.getBuildInformation();
// if (buildinfo!=null){
// Logger.info("Opencv build information : {}", buildinfo.getString());
// } else{
// Logger.error("Failed to get OpenCV build information");
// }
Properties config = SomeCodes.LoadProperties("config.properties");
String targetip = config.getProperty("Camera_ip");
String rtsppath = config.getProperty("Camera_Rtsp_path");
int width = 1920;
int height = 1080;
// test pakai sony camera, nanti hapus
// targetip = "172.17.195.51";
// rtsppath = "/video1";
// width = 1280;
// height = 720;
Logger.info("Camera IP: {}, Rtsp Path: {}, Width: {}, Height: {}", targetip, rtsppath, width,height);
rtspGrabber = null;
if (ValidString(targetip)){
if (ValidString(rtsppath)){
rtspGrabber = new RtspGrabber(targetip, rtsppath);
if (IpIsReachable(targetip)){
Logger.info("Camera IP : "+targetip+" is reachable");
rtspGrabber = new RtspGrabber_opencv(targetip, rtsppath);
rtspGrabber.Start(true, width, height);
} else Logger.warn("Camera IP : "+targetip+" is not reachable");
} else Logger.warn("Camera Path : "+rtsppath+" is not valid string");
} else Logger.warn("Camera IP : "+targetip+" is not valid string");
}
rtspGrabber.Start(true, 1920, 1080);
} else Logger.error("Invalid Camera Path");
} else Logger.error("Invalid Camera IP");
private static void init_multiusb_audio(){
multiUSBAudioPlayer = new MultiUSBAudioPlayer();
multiUSBAudioPlayer.DetectOutputDevices();
int[] devs = multiUSBAudioPlayer.FindDeviceIDWithName("USB");
if (devs!=null && devs.length>0){
Logger.info("MultiUSB Audio Device found : {}", IntArrayToString(devs));
if (multiUSBAudioPlayer.OpenDevice(devs, 48000)){
multiUSBAudioPlayer.setMasterVolume(100);
multiUSBAudioPlayer.setPlaybackvolume(100);
Logger.info("MultiUSB Audio Device ID={} opened", IntArrayToString(devs));
} else Logger.error("Failed to open MultiUSB Audio Device ID={}", IntArrayToString(devs));
} else Logger.error("MultiUSB Audio Device not found");
}
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() {
@Override
public void onPlaybackStart(AudioFileProperties prop) {
Logger.info("Playback started for {}", prop.filename);
AmplifierControl(true);
}
@Override
public void onPlaybackFinished(AudioFileProperties prop) {
Logger.info("Playback finished for {}", prop.filename);
AmplifierControl(false);
}
@Override
public void onPlaybackFailure(AudioFileProperties prop, String reason) {
Logger.error("Playback failed for {}: {}", prop.filename, reason);
AmplifierControl(false);
}
@Override
@@ -160,36 +589,63 @@ public class Main {
@Override
public WebsocketReply onWebsocketCommand(WebsocketCommand command) {
Blink_LedWeb();
byte speed;
String cmd = command.command.toUpperCase().trim();
switch (cmd){
// Pan Tilt Movement Commands
case "PAN LEFT" :
Logger.info("PAN LEFT");
speed = GetPanTiltSpeed(command.data, (byte)0x20);
if (panTiltController!=null) panTiltController.PanLeft(speed);
if (panTiltController!=null) {
panTiltController.PanLeft(speed);
}
return new WebsocketReply("PAN LEFT", String.valueOf(speed));
case "PAN RIGHT" :
Logger.info("PAN RIGHT");
speed = GetPanTiltSpeed(command.data, (byte)0x20);
if (panTiltController!=null) panTiltController.PanRight(speed);
if (panTiltController!=null) {
panTiltController.PanRight(speed);
}
return new WebsocketReply("PAN RIGHT", String.valueOf(speed));
case "TILT UP" :
Logger.info("TILT UP");
speed = GetPanTiltSpeed(command.data, (byte)0x20);
if (panTiltController!=null) panTiltController.TiltUp(speed);
// kebalik
//if (panTiltController!=null) panTiltController.TiltUp(speed);
if (panTiltController!=null) {
panTiltController.TiltDown(speed);
}
return new WebsocketReply("TILT UP", String.valueOf(speed));
case "TILT DOWN" :
Logger.info("TILT DOWN");
speed = GetPanTiltSpeed(command.data, (byte)0x20);
if (panTiltController!=null) panTiltController.TiltDown(speed);
// kebalik
//if (panTiltController!=null) panTiltController.TiltDown(speed);
if (panTiltController!=null) {
panTiltController.TiltUp(speed);
}
return new WebsocketReply("TILT DOWN", String.valueOf(speed));
case "STOP MOVEMENT" :
if (panTiltController!=null) panTiltController.StopMovement();
Logger.info("STOP MOVEMENT");
if (panTiltController!=null) {
panTiltController.StopMovement();
}
return new WebsocketReply("STOP MOVEMENT", "");
// Audio Related Commands
case "MUTE":
audioPlayer.Mute();
if (use_multiusb_audio){
if (multiUSBAudioPlayer!=null) multiUSBAudioPlayer.Mute();
} else {
if (audioPlayer!=null) audioPlayer.Mute();
}
return new WebsocketReply("MUTE", "");
case "UNMUTE":
audioPlayer.Unmute();
if (use_multiusb_audio){
if (multiUSBAudioPlayer!=null) multiUSBAudioPlayer.Unmute();
} else {
if (audioPlayer!=null) audioPlayer.Unmute();
}
return new WebsocketReply("UNMUTE", "");
case "SET VOLUME" :
int volume=-1;
@@ -199,11 +655,20 @@ public class Main {
if (volume>100) volume = 100;
}
if (volume>=0){
audioPlayer.setPlaybackvolume(volume);
return new WebsocketReply("SET VOLUME", String.valueOf(volume));
if (use_multiusb_audio){
if (multiUSBAudioPlayer!=null) multiUSBAudioPlayer.setPlaybackvolume(volume);
} else{
if (audioPlayer!=null) audioPlayer.setPlaybackvolume(volume);
}
return new WebsocketReply("SET VOLUME", String.valueOf(volume));
} else return new WebsocketReply("SET VOLUME", "Invalid Volume Value");
case "GET VOLUME" :
int vol = audioPlayer.getPlaybackvolume();
int vol = 0;
if (use_multiusb_audio){
if (multiUSBAudioPlayer!=null) vol = multiUSBAudioPlayer.getPlaybackvolume();
} else {
if (audioPlayer!=null) vol = audioPlayer.getPlaybackvolume();
}
return new WebsocketReply("GET VOLUME", String.valueOf(vol));
case "PLAY AUDIO" :
if (ValidInteger(command.data)){
@@ -212,60 +677,162 @@ public class Main {
if (ValidString(filename)){
File filetoplay = new File(Path.of(currentDirectory, "audiofiles", filename).toString());
if (filetoplay.isFile()){
AudioFileProperties afp = audioPlayer.OpenAudioFile(filetoplay);
if (afp!=null){
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);
if (use_multiusb_audio){
MultiUSBAudioPlayer.PlaybackHandlewithId[] phs = multiUSBAudioPlayer.OpenAudioFile(filetoplay);
if (phs!=null && phs.length>0){
playbackHandles = phs;
multiUSBAudioPlayer.PlayAudioFile(phs, true, pe);
return new WebsocketReply("PLAY AUDIO", filename);
}
return new WebsocketReply("PLAY AUDIO", "Failed to open audio file "+filename);
} else {
AudioFileProperties afp = audioPlayer.OpenAudioFile(filetoplay);
if (afp!=null){
audioFileProperties = afp;
audioPlayer.PlayAudioFile(afp, true, pe);
//AmplifierControl(true);
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));
} else return new WebsocketReply("PLAY AUDIO", "Invalid Audio ID");
case "STOP AUDIO" :
if (audioFileProperties!=null){
audioPlayer.CloseAudioFile(audioFileProperties); // close previous audio file
String filename = audioFileProperties.filename;
audioFileProperties = null;
return new WebsocketReply("STOP AUDIO", filename);
} else return new WebsocketReply("STOP AUDIO", "No audioFileProperties");
if (use_multiusb_audio){
if (playbackHandles!=null && playbackHandles.length>0){
multiUSBAudioPlayer.CloseAudioFile(playbackHandles);
String filename = playbackHandles[0].handle.filename;
playbackHandles = null;
return new WebsocketReply("STOP AUDIO", filename);
} else return new WebsocketReply("STOP AUDIO", "No playbackHandles");
} else {
if (audioFileProperties!=null){
audioPlayer.CloseAudioFile(audioFileProperties); // close previous audio file
String filename = audioFileProperties.filename;
audioFileProperties = null;
//AmplifierControl(false);
return new WebsocketReply("STOP AUDIO", filename);
} else return new WebsocketReply("STOP AUDIO", "No audioFileProperties");
}
// ZOOM Related Commands
case "GET MAX ZOOM":
if (vapixProtocol!=null){
Blink_LedIpCamera();
return new WebsocketReply("GET MAX ZOOM", String.valueOf(vapixProtocol.GetPTZMaxZoom()));
} else return new WebsocketReply("GET MAX ZOOM", "VapixProtocol not initialized");
case "GET ZOOM":
if (vapixProtocol!=null){
Blink_LedIpCamera();
return new WebsocketReply("GET ZOOM", String.valueOf(vapixProtocol.GetCurrentZoomValue()));
} else return new WebsocketReply("GET ZOOM", "VapixProtocol not initialized");
case "SET ZOOM":
if (vapixProtocol.PTZEnabled()){
if (ValidInteger(command.data)){
int zoom = Integer.parseInt(command.data);
if (zoom<vapixProtocol.GetPTZMinZoom()) zoom = vapixProtocol.GetPTZMinZoom();
if (zoom>vapixProtocol.GetPTZMaxZoom()) zoom = vapixProtocol.GetPTZMaxZoom();
if (vapixProtocol.Zoom(1, zoom)){
return new WebsocketReply("ZOOM", String.valueOf(zoom));
} else return new WebsocketReply("ZOOM", "Failed to zoom");
} else return new WebsocketReply("ZOOM", "Invalid Zoom Value");
} else return new WebsocketReply("ZOOM", "Zoom not supported");
if (vapixProtocol!=null){
if (vapixProtocol.PTZEnabled()){
if (ValidInteger(command.data)){
int zoom = Integer.parseInt(command.data);
if (zoom<vapixProtocol.GetPTZMinZoom()) zoom = vapixProtocol.GetPTZMinZoom();
if (zoom>vapixProtocol.GetPTZMaxZoom()) zoom = vapixProtocol.GetPTZMaxZoom();
Blink_LedIpCamera();
if (vapixProtocol.Zoom(1, zoom)){
return new WebsocketReply("ZOOM", String.valueOf(zoom));
} else return new WebsocketReply("ZOOM", "Failed to zoom");
} else return new WebsocketReply("ZOOM", "Invalid Zoom Value");
} else return new WebsocketReply("ZOOM", "Zoom not supported");
}
// Live Streaming Related Commands
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()));
else
return new WebsocketReply("GET BASE64", "data:image/jpeg;base64,"+ rtspGrabber.getLastLQBase64(), String.format("Streaming at %dx%d", rtspGrabber.getLQWidth(), rtspGrabber.getLQHeight()));
if (use_Yolo){
return switch (command.data) {
case "HQ" ->
new WebsocketReply("GET BASE64", "data:image/jpeg;base64," + rtspGrabber.getLastHQBase64(), rtspGrabber.HQStreamingStatus());
case "YOLO" ->
new WebsocketReply("GET BASE64", "data:image/jpeg;base64," + rtspGrabber.getLastLQBase64(), rtspGrabber.LQStreamingStatus());
case "LQ" ->
new WebsocketReply("GET BASE64", "data:image/jpeg;base64," + rtspGrabber.getLastYoloBase64(), rtspGrabber.LQStreamingStatus());
default -> new WebsocketReply("GET BASE64", "RTSP Grabber not initialized");
};
} else {
return switch (command.data) {
case "HQ" ->
new WebsocketReply("GET BASE64", "data:image/jpeg;base64," + rtspGrabber.getLastHQBase64(), rtspGrabber.HQStreamingStatus());
case "LQ" ->
new WebsocketReply("GET BASE64", "data:image/jpeg;base64," + rtspGrabber.getLastLQBase64(), rtspGrabber.LQStreamingStatus());
default -> new WebsocketReply("GET BASE64", "RTSP Grabber not initialized");
};
}
} else return new WebsocketReply("GET BASE64", "RTSP Grabber not initialized");
case "GET RESOLUTION":
if (vapixProtocol!=null){
int[] res = vapixProtocol.GetCurrentResolution(1);
Blink_LedIpCamera();
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)));
}
}
if (!networkTX.isEmpty()) {
for(String key : networkTX.keySet()){
if (key.equals("lo")) continue;
data.addProperty(key+"_TX", networkTX.get(key));
}
}
if (!networkRX.isEmpty()) {
for(String key : networkRX.keySet()){
if (key.equals("lo")) continue;
data.addProperty(key+"_RX", networkRX.get(key));
}
}
return new WebsocketReply("GET SYSTEM INFO", data.toString());
default:
return new WebsocketReply("UNKNOWN COMMAND", command.command);
}
}
@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() {