Compare commits
10 Commits
1fe4716bab
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| ea891d2744 | |||
| f2d560049f | |||
| f515f829cf | |||
| f7e4ee68d6 | |||
| 0942c9936c | |||
| 10fad0e192 | |||
| 19da5914ac | |||
| d2f924f5db | |||
| 068316bb62 | |||
| f7f711d3fe |
6
.idea/jsLibraryMappings.xml
generated
6
.idea/jsLibraryMappings.xml
generated
@@ -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
1
.idea/misc.xml
generated
@@ -1,4 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="MavenProjectsManager">
|
||||
|
||||
@@ -10,7 +10,7 @@ AudioFile03 = pinkNoiseWav.wav
|
||||
AudioFile04 = 04.mp3
|
||||
AudioFile05 = 05.mp3
|
||||
AudioVolumeOutput = 100
|
||||
SerialPort = /dev/ttyUSB0
|
||||
SerialPort = /dev/ttyAMA0
|
||||
SerialBaudRate = 9600
|
||||
PanTiltID = 1
|
||||
WebUsername = admin
|
||||
|
||||
@@ -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,7 +129,7 @@
|
||||
</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>
|
||||
@@ -113,7 +139,7 @@
|
||||
</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 © Galva Technologies. All rights reserved.</p>
|
||||
<p class="footer">2024 Copyright © Galva Technologies. All rights reserved.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -12,43 +12,90 @@ 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);
|
||||
// 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
|
||||
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
|
||||
}));
|
||||
send_get_max_zoom();
|
||||
send_get_volume();
|
||||
send_get_zoom();
|
||||
send_get_resolution();
|
||||
send_get_audiofiles();
|
||||
|
||||
}
|
||||
ws.onmessage = function(event){
|
||||
@@ -57,71 +104,7 @@ document.addEventListener("DOMContentLoaded", function(){
|
||||
* @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;
|
||||
|
||||
}
|
||||
|
||||
process_command(dx);
|
||||
}
|
||||
ws.onclose = function(){
|
||||
console.log("Connection closed");
|
||||
@@ -129,10 +112,11 @@ document.addEventListener("DOMContentLoaded", function(){
|
||||
ws.onerror = function(){
|
||||
console.log("Error in connection");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
set_pan_speed(32);
|
||||
set_tilt_speed(32);
|
||||
});
|
||||
|
||||
/**
|
||||
* 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({
|
||||
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({
|
||||
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({
|
||||
function send_unmute(){
|
||||
let cmd = 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");
|
||||
|
||||
console.log("mute")
|
||||
});
|
||||
if (ws){
|
||||
if (ws.readyState === WebSocket.OPEN){
|
||||
ws.send(JSON.stringify({
|
||||
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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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,148 @@ 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);
|
||||
if (systeminfo.cpu_temperature && systeminfo.cpu_temperature.length>0) $('#cpu_temperature').html(`${systeminfo.cpu_temperature} °C`);
|
||||
if (systeminfo.cpu && systeminfo.cpu.length>0) $('#cpu_usage').html(`${systeminfo.cpu} %`);
|
||||
if (systeminfo.ram_usage && systeminfo.ram_usage.length>0) $('#ram_usage').html(`${systeminfo.ram_usage} %`);
|
||||
if (systeminfo.end0_TX && systeminfo.end0_TX.length>0) $('#ethernet_TX').html(systeminfo.end0_TX);
|
||||
if (systeminfo.end0_RX && systeminfo.end0_RX.length>0) $('#ethernet_RX').html(systeminfo.end0_RX);
|
||||
break;
|
||||
case "GET AUDIOFILES":
|
||||
//console.log("Get Audio Files: "+dx.data);
|
||||
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
7
Html/html/public/js/socket.io.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -9,6 +9,7 @@ html,body{
|
||||
body{
|
||||
overflow: visible;
|
||||
flex-wrap: wrap;
|
||||
background: rgb(51, 51, 51) !important;
|
||||
}
|
||||
/*HEADER */
|
||||
*,
|
||||
@@ -820,3 +821,522 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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,21 +41,21 @@
|
||||
</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">
|
||||
<select id="preset1" class="form-control text-input class100">
|
||||
<option>Option 1</option>
|
||||
<option>Option 2</option>
|
||||
</select>
|
||||
@@ -67,7 +67,7 @@
|
||||
<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">
|
||||
<select id="preset2" class="form-control text-input class100">
|
||||
<option>Option 1</option>
|
||||
<option>Option 2</option>
|
||||
</select>
|
||||
@@ -79,7 +79,7 @@
|
||||
<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">
|
||||
<select id="preset3" class="form-control text-input class100">
|
||||
<option>Option 1</option>
|
||||
<option>Option 2</option>
|
||||
</select>
|
||||
@@ -91,7 +91,7 @@
|
||||
<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">
|
||||
<select id="preset4" class="form-control text-input class100">
|
||||
<option>Option 1</option>
|
||||
<option>Option 2</option>
|
||||
</select>
|
||||
@@ -103,7 +103,7 @@
|
||||
<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">
|
||||
<select id="preset5" class="form-control text-input class100">
|
||||
<option>Option 1</option>
|
||||
<option>Option 2</option>
|
||||
</select>
|
||||
@@ -112,16 +112,15 @@
|
||||
<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>
|
||||
<button id="save_audio" class="btn btn-primary btn-setting class100" onclick="save_audio()">SAVE</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</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,16 +142,15 @@
|
||||
|
||||
<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">
|
||||
<input id="setting_ip" name="ip" class="form-control text-input" title="IP Address">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-2">
|
||||
@@ -160,7 +158,7 @@
|
||||
<p>Port</p>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<input id="setting_port" class="form-control" title="Port">
|
||||
<input id="setting_port" name="port" class="form-control text-input" title="Port">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -169,7 +167,7 @@
|
||||
<p>Username</p>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<input id="setting_username" class="form-control" title="Username">
|
||||
<input id="setting_username" name="username" class="form-control text-input" title="Username">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -179,7 +177,7 @@
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="input-group">
|
||||
<input type="password" id="setting_password" class="form-control" title="Password">
|
||||
<input type="password" id="setting_password" name="password" class="form-control text-input" title="Password">
|
||||
<span class="input-group-text" onclick="cameraPassword()">
|
||||
<i class="fa-solid fa-eye" id="icon_camera"></i>
|
||||
</span>
|
||||
@@ -189,25 +187,23 @@
|
||||
<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>
|
||||
<button id="save_camera" class="btn btn-primary btn-setting class100" onclick="save_camera()">SAVE</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</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">
|
||||
<input id="login_username" class="form-control text-input" title="Username">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-2">
|
||||
@@ -216,7 +212,7 @@
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="input-group">
|
||||
<input type="password" id="edit_password" class="form-control" title="Password">
|
||||
<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>
|
||||
@@ -229,7 +225,7 @@
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="input-group">
|
||||
<input type="password" id="confirm_password" class="form-control" title="Confirm Password">
|
||||
<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>
|
||||
@@ -239,10 +235,9 @@
|
||||
<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>
|
||||
<button id="save_login" class="btn btn-primary btn-setting class100" onclick="save_login()">SAVE</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -250,7 +245,7 @@
|
||||
|
||||
<!-- SECTION 3 -->
|
||||
<div class="container-fluid footer1 outside">
|
||||
<p>2024 Copyright © Galva Technologies. All rights reserved.</p>
|
||||
<p class="footer">2024 Copyright © Galva Technologies. All rights reserved.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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");
|
||||
@@ -109,3 +116,90 @@ function showConfirm() {
|
||||
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);
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
#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=
|
||||
AudioFile05=null
|
||||
AudioVolumeOutput=100
|
||||
Camera_Rtsp_path=/axis-media/media.amp
|
||||
Camera_ip=192.168.10.17
|
||||
@@ -12,13 +12,8 @@ Camera_port=80
|
||||
Camera_user=root
|
||||
PanTiltID=1
|
||||
SerialBaudRate=9600
|
||||
SerialPort=/dev/ttyUSB0
|
||||
SerialPort=/dev/ttyAMA0
|
||||
WebHost=0.0.0.0
|
||||
WebPassword=bandara
|
||||
WebPort=8080
|
||||
WebUsername=admin
|
||||
audiofile1=
|
||||
audiofile2=
|
||||
audiofile3=
|
||||
audiofile4=
|
||||
audiofile5=
|
||||
|
||||
210
pom.xml
210
pom.xml
@@ -7,11 +7,207 @@
|
||||
<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.8</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>leptonica</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>leptonica-platform</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>artoolkitplus</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>artoolkitplus-platform</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>libdc1394</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>libdc1394-platform</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>libfreenect</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>libfreenect-platform</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>libfreenect2</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>libfreenect2-platform</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>flycapture</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>flycapture-platform</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>librealsense</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>librealsense-platform</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>librealsense2</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>librealsense2-platform</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>videoinput</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>videoinput-platform</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>tesseract</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>tesseract-platform</artifactId>
|
||||
</exclusion>
|
||||
<!-- platform dibuangin semua, kemudian tambah sendiri linux-arm64, windows-x86_64, linux-x86_64 -->
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>javacpp-platform</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>openblas-platform</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>opencv-platform</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>ffmpeg-platform</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>flandmark</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>flandmark-platform</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!-- manual tambah untuk platform windows-x86_64 -->
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>opencv</artifactId>
|
||||
<version>4.6.0-1.5.8</version>
|
||||
<classifier>windows-x86_64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>javacpp</artifactId>
|
||||
<version>1.5.8</version>
|
||||
<classifier>windows-x86_64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>openblas</artifactId>
|
||||
<version>0.3.21-1.5.8</version>
|
||||
<classifier>windows-x86_64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>ffmpeg</artifactId>
|
||||
<version>5.1.2-1.5.8</version>
|
||||
<classifier>windows-x86_64</classifier>
|
||||
</dependency>
|
||||
<!-- manual tambah untuk platform linux-arm64-->
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>opencv</artifactId>
|
||||
<version>4.6.0-1.5.8</version>
|
||||
<classifier>linux-arm64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>javacpp</artifactId>
|
||||
<version>1.5.8</version>
|
||||
<classifier>linux-arm64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>openblas</artifactId>
|
||||
<version>0.3.21-1.5.8</version>
|
||||
<classifier>linux-arm64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>ffmpeg</artifactId>
|
||||
<version>5.1.2-1.5.8</version>
|
||||
<classifier>linux-arm64</classifier>
|
||||
</dependency>
|
||||
<!-- manual tambah untuk platform linux-x86_64
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>opencv</artifactId>
|
||||
<version>4.9.0-1.5.10</version>
|
||||
<classifier>linux-x86_64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>javacpp</artifactId>
|
||||
<version>1.5.10</version>
|
||||
<classifier>linux-x86_64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>openblas</artifactId>
|
||||
<version>0.3.26-1.5.10</version>
|
||||
<classifier>linux-x86_64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>ffmpeg</artifactId>
|
||||
<version>6.1.1-1.5.10</version>
|
||||
<classifier>linux-x86_64</classifier>
|
||||
</dependency>
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>com.corundumstudio.socketio</groupId>
|
||||
<artifactId>netty-socketio</artifactId>
|
||||
<version>2.0.12</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.javalin</groupId>
|
||||
@@ -52,6 +248,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 +261,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>
|
||||
@@ -1,14 +1,15 @@
|
||||
package Audio;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.tinylog.Logger;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
||||
public class AudioPlayer {
|
||||
Bass bass;
|
||||
int deviceid = -1;
|
||||
boolean inited = false;
|
||||
private final Bass bass;
|
||||
private int deviceid = -1;
|
||||
@Getter private boolean inited = false;
|
||||
|
||||
int playbackhandle = 0;
|
||||
float playbackvolume = 1.0f;
|
||||
@@ -48,13 +49,49 @@ public class AudioPlayer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get BASS_DEVICEINFO for a device
|
||||
* @param device device id
|
||||
* @return BASS_DEVICEINFO object or null if failed
|
||||
*/
|
||||
public Bass.BASS_DEVICEINFO GetDeviceInfo(int device){
|
||||
Bass.BASS_DEVICEINFO info = new Bass.BASS_DEVICEINFO();
|
||||
if (bass.BASS_GetDeviceInfo(device, info)){
|
||||
return info;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find Device ID with Name
|
||||
* @param name device name
|
||||
* @return device id, or -1 if not found
|
||||
*/
|
||||
public int FindDeviceIDWithName(String name){
|
||||
int result = -1;
|
||||
int ii = 1;
|
||||
while(true){
|
||||
Bass.BASS_DEVICEINFO info = new Bass.BASS_DEVICEINFO();
|
||||
if (bass.BASS_GetDeviceInfo(ii, info)){
|
||||
if (info.name.contains(name)){
|
||||
result = ii;
|
||||
break;
|
||||
}
|
||||
ii++;
|
||||
} else {
|
||||
// gak ada lagi
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open Output Device
|
||||
* @param device device id, starts from 1
|
||||
* @param freq output frequency
|
||||
* @return true if success
|
||||
*/
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
public boolean OpenDevice(int device, int freq){
|
||||
int flag = Bass.BASS_DEVICE_REINIT | Bass.BASS_DEVICE_16BITS | Bass.BASS_DEVICE_MONO | Bass.BASS_DEVICE_FREQ;
|
||||
boolean success = bass.BASS_Init(device, freq, flag);
|
||||
@@ -69,7 +106,8 @@ public class AudioPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Master Volume
|
||||
* Set Master Output Volume
|
||||
* Master Output Volume related to the system volume (Alsamixer on Linux, Volume Mixer on Windows)
|
||||
* @param value volume value, 0-100
|
||||
*/
|
||||
public void setMasterVolume(int value){
|
||||
@@ -84,7 +122,8 @@ public class AudioPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Master Volume
|
||||
* Get Master Output Volume
|
||||
* Master Output Volume related to the system volume (Alsamixer on Linux, Volume Mixer on Windows)
|
||||
* @return 0 - 100, or -1 if failed
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@@ -99,6 +138,11 @@ public class AudioPlayer {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Playback Volume
|
||||
* is the volume of the currently playing audio file
|
||||
* @return 0 - 100
|
||||
*/
|
||||
public int getPlaybackvolume(){
|
||||
if (playbackvolume<0)
|
||||
return 0;
|
||||
@@ -108,6 +152,11 @@ public class AudioPlayer {
|
||||
return (int)(playbackvolume*100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Playback Volume
|
||||
* is the volume of the currently playing audio file
|
||||
* @param value 0 - 100
|
||||
*/
|
||||
public void setPlaybackvolume(int value){
|
||||
if (value<0) value = 0;
|
||||
if (value>100) value = 100;
|
||||
@@ -119,11 +168,17 @@ public class AudioPlayer {
|
||||
|
||||
private float lastplaybackvolume = 1.0f;
|
||||
|
||||
/**
|
||||
* Set Playback Volume to 0
|
||||
*/
|
||||
public void Mute(){
|
||||
lastplaybackvolume = playbackvolume;
|
||||
setPlaybackvolume(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Playback Volume to last volume before Mute
|
||||
*/
|
||||
public void Unmute(){
|
||||
setPlaybackvolume((int)lastplaybackvolume*100);
|
||||
}
|
||||
@@ -164,6 +219,12 @@ public class AudioPlayer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Play Audio File
|
||||
* @param prop AudioFileProperties object to play
|
||||
* @param looping set true to loop the audio file
|
||||
* @param event PlaybackEvent object to handle playback event
|
||||
*/
|
||||
public void PlayAudioFile(AudioFileProperties prop, boolean looping, PlaybackEvent event){
|
||||
if (inited){
|
||||
if (prop!=null){
|
||||
|
||||
@@ -9,20 +9,31 @@ import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.bytedeco.javacv.Frame;
|
||||
import org.bytedeco.javacv.FrameGrabber;
|
||||
import org.tinylog.Logger;
|
||||
|
||||
public class GrabbingTask implements Runnable {
|
||||
// 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;
|
||||
|
||||
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;
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private void updateMessage(String message) {
|
||||
if (onMessageUpdate != null) {
|
||||
onMessageUpdate.accept(message);
|
||||
@@ -39,6 +50,7 @@ public class GrabbingTask implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private void updateLQBase64(String base64) {
|
||||
if (onLQBase64Update != null) {
|
||||
onLQBase64Update.accept(base64);
|
||||
@@ -61,34 +73,63 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
public void Stop(){
|
||||
isGrabbing.set(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
isGrabbing.set(true);
|
||||
while (isGrabbing.get()) {
|
||||
try {
|
||||
Frame fr =grabber.grab();
|
||||
private void processFrame(Frame fr){
|
||||
if (fr!=null){
|
||||
updateHQFrame(fr);
|
||||
updateHQBase64(SomeCodes.FrameToBase64(fr));
|
||||
Frame resized = SomeCodes.ResizeFrame(fr, lowquality_width, lowquality_height);
|
||||
updateLQFrame(resized);
|
||||
updateLQBase64(SomeCodes.FrameToBase64(resized));
|
||||
} else updateMessage("Grabber returned null frame");
|
||||
} catch (Exception e) {
|
||||
updateMessage("Error grabbing frame: " + e.getMessage());
|
||||
} else updateMessage("processFrame have null frame");
|
||||
}
|
||||
|
||||
private void flush_grabber(){
|
||||
try {
|
||||
if (grabber!=null) grabber.flush();
|
||||
} catch (FrameGrabber.Exception e) {
|
||||
Logger.error("Error flushing grabber: "+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
if (framecount % skippedframes == 0) processFrame(frame);
|
||||
} catch (Exception e){
|
||||
Logger.error("Error grabbing frame: "+e.getMessage());
|
||||
}
|
||||
|
||||
long elapsed = System.currentTimeMillis() - starttick;
|
||||
starttick = System.currentTimeMillis();
|
||||
if (elapsed>0) {
|
||||
int xx = (int) (1000 / elapsed);
|
||||
if (xx<fps) CaptureFPS = xx;
|
||||
}
|
||||
//Logger.info("Elapsed time = {} ms, captureFPS = {}", elapsed, CaptureFPS);
|
||||
}
|
||||
Logger.info("Grabbing Task stopped");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
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,12 @@ public class PanTiltController {
|
||||
byte[] command = new byte[]{0, cameraid, 0, 0, 0, 0, 0};
|
||||
command[6] = Checksum(command); // add checksum
|
||||
command[0] = (byte) 0xFF; // add synchronization byte
|
||||
if (isOpen()) {
|
||||
Main.Max485Direction(true);
|
||||
serialPort.writeBytes(command, command.length);
|
||||
Main.Max485Direction(false);
|
||||
Main.Blink_LedPanTilt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,7 +87,12 @@ public class PanTiltController {
|
||||
byte[] command = new byte[]{0, cameraid, 0, 4, speed, 0, 0};
|
||||
command[6] = Checksum(command); // add checksum
|
||||
command[0] = (byte) 0xFF; // add synchronization byte
|
||||
if (isOpen()) {
|
||||
Main.Max485Direction(true);
|
||||
serialPort.writeBytes(command, command.length);
|
||||
Main.Max485Direction(false);
|
||||
Main.Blink_LedPanTilt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,7 +106,12 @@ public class PanTiltController {
|
||||
byte[] command = new byte[]{0, cameraid, 0, 2, speed, 0, 0};
|
||||
command[6] = Checksum(command); // add checksum
|
||||
command[0] = (byte) 0xFF; // add synchronization byte
|
||||
if (isOpen()) {
|
||||
Main.Max485Direction(true);
|
||||
serialPort.writeBytes(command, command.length);
|
||||
Main.Max485Direction(false);
|
||||
Main.Blink_LedPanTilt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,7 +125,12 @@ public class PanTiltController {
|
||||
byte[] command = new byte[]{0, cameraid, 0, 8, speed, 0, 0};
|
||||
command[6] = Checksum(command); // add checksum
|
||||
command[0] = (byte) 0xFF; // add synchronization byte
|
||||
if (isOpen()) {
|
||||
Main.Max485Direction(true);
|
||||
serialPort.writeBytes(command, command.length);
|
||||
Main.Max485Direction(false);
|
||||
Main.Blink_LedPanTilt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,7 +144,12 @@ public class PanTiltController {
|
||||
byte[] command = new byte[]{0, cameraid, 0, 16, speed, 0, 0};
|
||||
command[6] = Checksum(command); // add checksum
|
||||
command[0] = (byte) 0xFF; // add synchronization byte
|
||||
if (isOpen()) {
|
||||
Main.Max485Direction(true);
|
||||
serialPort.writeBytes(command, command.length);
|
||||
Main.Max485Direction(false);
|
||||
Main.Blink_LedPanTilt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,28 +3,65 @@ 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 String lastHQBase64 = null;
|
||||
private String lastLQBase64 = null;
|
||||
private @Getter int HQWidth = 0;
|
||||
private @Getter int HQHeight = 0;
|
||||
private @Getter int LQWidth = 0;
|
||||
private @Getter int LQHeight = 0;
|
||||
private GrabbingTask grabbingTask;
|
||||
|
||||
public RtspGrabber(String ip, String path) {
|
||||
this.rtspUrl = "rtsp://" + ip + path;
|
||||
Logger.info("RtspGrabber created with url: " + rtspUrl);
|
||||
}
|
||||
|
||||
private synchronized void setLastHQFrame(Frame frame){
|
||||
lastHQFrame = frame;
|
||||
}
|
||||
|
||||
public synchronized Frame getLastHQFrame(){
|
||||
return lastHQFrame;
|
||||
}
|
||||
|
||||
private synchronized void setLastLQFrame(Frame frame){
|
||||
lastLQFrame = frame;
|
||||
}
|
||||
|
||||
public synchronized Frame getLastLQFrame(){
|
||||
return lastLQFrame;
|
||||
}
|
||||
|
||||
private synchronized void setLastHQBase64(String base64){
|
||||
lastHQBase64 = base64;
|
||||
}
|
||||
|
||||
public synchronized String getLastHQBase64(){
|
||||
return lastHQBase64;
|
||||
}
|
||||
|
||||
private synchronized void setLastLQBase64(String base64){
|
||||
lastLQBase64 = base64;
|
||||
}
|
||||
|
||||
public synchronized String getLastLQBase64(){
|
||||
return lastLQBase64;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start grabbing frames from rtsp
|
||||
* @param useTcp Use tcp instead of udp
|
||||
@@ -35,21 +72,60 @@ 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);
|
||||
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,10);
|
||||
tt.setOnMessageUpdate(Logger::info);
|
||||
tt.setOnHQFrameUpdate(value -> {
|
||||
if (value!=null){
|
||||
if (value.imageWidth>0 && value.imageHeight>0){
|
||||
lastHQFrame = value;
|
||||
setLastHQFrame(value);
|
||||
HQWidth = value.imageWidth;
|
||||
HQHeight = value.imageHeight;
|
||||
}
|
||||
@@ -59,36 +135,14 @@ public class RtspGrabber {
|
||||
tt.setOnLQFrameUpdate(value -> {
|
||||
if (value!=null){
|
||||
if (value.imageWidth>0 && value.imageHeight>0){
|
||||
lastLQFrame = value;
|
||||
setLastLQFrame(value);
|
||||
LQWidth = value.imageWidth;
|
||||
LQHeight = value.imageHeight;
|
||||
}
|
||||
}
|
||||
});
|
||||
tt.setOnHQBase64Update(value -> lastHQBase64 = value);
|
||||
tt.setOnLQBase64Update(value -> lastLQBase64 = value);
|
||||
new Thread(tt).start();
|
||||
|
||||
} catch (Exception e){
|
||||
Logger.error("Error starting grabber: " + e.getMessage());
|
||||
tt.setOnHQBase64Update(this::setLastHQBase64);
|
||||
tt.setOnLQBase64Update(this::setLastLQBase64);
|
||||
return tt;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop grabbing frames
|
||||
*/
|
||||
public void Stop(){
|
||||
if (grabber!=null) {
|
||||
try{
|
||||
isGrabbing.set(false);
|
||||
grabber.stop();
|
||||
Logger.info("Grabber stopped");
|
||||
} catch (Exception e){
|
||||
Logger.error("Error stopping grabber: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package Other;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import org.bytedeco.javacpp.Loader;
|
||||
import org.bytedeco.javacv.Frame;
|
||||
import org.bytedeco.javacv.Java2DFrameConverter;
|
||||
@@ -42,6 +43,10 @@ public class SomeCodes {
|
||||
public static final boolean haveOpenCL = opencv_core.haveOpenCL();
|
||||
public static boolean useOpenCL;
|
||||
private static final Base64.Encoder base64encoder = java.util.Base64.getEncoder();
|
||||
public static final Gson gson = new Gson();
|
||||
public static final double KB_threshold = 1024.0;
|
||||
public static final double MB_threshold = 1024.0 * 1024.0;
|
||||
public static final double GB_threshold = 1024.0 * 1024.0 * 1024.0;
|
||||
|
||||
public static String[] GetAudioFiles(){
|
||||
try{
|
||||
@@ -49,6 +54,7 @@ public class SomeCodes {
|
||||
} catch (Exception e){
|
||||
Logger.error("Error getting audio files: "+e.getMessage());
|
||||
}
|
||||
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@@ -199,10 +205,15 @@ public class SomeCodes {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Load properties file
|
||||
* @param filename properties file name
|
||||
* @return Properties object loaded, or empty new Properties object if error
|
||||
*/
|
||||
public static @NotNull Properties LoadProperties(String filename){
|
||||
try{
|
||||
InputStream is = new FileInputStream(filename);
|
||||
File ff = new File(currentDirectory, filename);
|
||||
InputStream is = new FileInputStream(ff);
|
||||
Properties prop = new Properties();
|
||||
prop.load(is);
|
||||
return prop;
|
||||
@@ -212,9 +223,16 @@ public class SomeCodes {
|
||||
return new Properties();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save properties file
|
||||
* @param prop Properties object to save
|
||||
* @param filename properties file name
|
||||
* @return true if success, false otherwise
|
||||
*/
|
||||
public static boolean SaveProperties(Properties prop, String filename){
|
||||
try{
|
||||
OutputStream os = new FileOutputStream(filename);
|
||||
File ff = new File(currentDirectory, filename);
|
||||
OutputStream os = new FileOutputStream(ff);
|
||||
prop.store(os, null);
|
||||
return true;
|
||||
} catch (Exception e){
|
||||
@@ -236,6 +254,7 @@ public class SomeCodes {
|
||||
UMat src = new UMat();
|
||||
source.copyTo(src);
|
||||
UMat dst = new UMat();
|
||||
|
||||
opencv_imgproc.resize(src, dst, sz);
|
||||
dst.copyTo(dest);
|
||||
} else {
|
||||
@@ -249,4 +268,19 @@ public class SomeCodes {
|
||||
Mat resized = ResizeMat(mat, width, height);
|
||||
return matConverter.convert(resized);
|
||||
}
|
||||
|
||||
/**
|
||||
* check if an ip address is reachable
|
||||
* @param ipaddress ip address to check
|
||||
* @return true if valid and reachable, false otherwise
|
||||
*/
|
||||
public static boolean IpIsReachable(String ipaddress){
|
||||
try{
|
||||
InetAddress inet = InetAddress.getByName(ipaddress);
|
||||
return inet.isReachable(1000);
|
||||
} catch (Exception e){
|
||||
Logger.error("Error checking ip address: "+e.getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
8
src/main/java/SBC/CpuInfo.java
Normal file
8
src/main/java/SBC/CpuInfo.java
Normal file
@@ -0,0 +1,8 @@
|
||||
package SBC;
|
||||
|
||||
public class CpuInfo {
|
||||
public int processorCount;
|
||||
public String revision;
|
||||
public String serial;
|
||||
public String model;
|
||||
}
|
||||
@@ -1,23 +1,21 @@
|
||||
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()){
|
||||
@@ -25,8 +23,6 @@ public class GPIO {
|
||||
} 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");
|
||||
} 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 RaspberryPi5BPins pin){
|
||||
Path pinPath = gpioPath.resolve("gpio"+pin.gpionumber);
|
||||
return pinPath.toFile().isDirectory();
|
||||
}
|
||||
|
||||
@@ -46,15 +42,15 @@ public class GPIO {
|
||||
* @param pin GPIO pin number
|
||||
* @return true if the pin is successfully exported
|
||||
*/
|
||||
public static boolean ExportPin(int pin){
|
||||
public static boolean ExportPin(@NotNull RaspberryPi5BPins pin){
|
||||
try{
|
||||
if (Files.isWritable(gpioExportPath)){
|
||||
Files.write(gpioExportPath, String.valueOf(pin).getBytes());
|
||||
Logger.info("Pin "+pin+" exported");
|
||||
Files.write(gpioExportPath, String.valueOf(pin.gpionumber).getBytes());
|
||||
Logger.info("Pin "+pin.pin+" exported");
|
||||
return GpioPinExists(pin);
|
||||
} else Logger.error("GPIO export path is not writable");
|
||||
} catch (Exception e){
|
||||
Logger.error("Failed to export pin: "+pin+", Message: "+e.getMessage());
|
||||
Logger.error("Failed to export pin: "+pin.pin+", Message: "+e.getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -64,14 +60,14 @@ public class GPIO {
|
||||
* @param pin GPIO pin number
|
||||
* @return true if the pin is successfully unexported
|
||||
*/
|
||||
public static boolean UnexportPin(int pin){
|
||||
public static boolean UnexportPin(@NotNull RaspberryPi5BPins pin){
|
||||
if (Files.isWritable(gpioUnexportPath)){
|
||||
try{
|
||||
Files.write(gpioUnexportPath, String.valueOf(pin).getBytes());
|
||||
Logger.info("Pin "+pin+" unexported");
|
||||
Files.write(gpioUnexportPath, String.valueOf(pin.gpionumber).getBytes());
|
||||
Logger.info("Pin "+pin.pin+" unexported");
|
||||
return true;
|
||||
} catch (Exception e){
|
||||
Logger.error("Failed to unexport pin: "+pin+", Message: "+e.getMessage());
|
||||
Logger.error("Failed to unexport pin: "+pin.pin+", Message: "+e.getMessage());
|
||||
}
|
||||
} else Logger.error("GPIO unexport path is not writable");
|
||||
|
||||
@@ -83,17 +79,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 RaspberryPi5BPins pin){
|
||||
Path pinPath = gpioPath.resolve("gpio"+pin.gpionumber).resolve("direction");
|
||||
if (pinPath.toFile().isFile()){
|
||||
if (Files.isReadable(pinPath)){
|
||||
try{
|
||||
return Files.readString(pinPath).trim();
|
||||
} catch (Exception e){
|
||||
Logger.error("Failed to read pin direction: "+pin+", Message: "+e.getMessage());
|
||||
Logger.error("Failed to read pin direction: "+pin.pin+", Message: "+e.getMessage());
|
||||
}
|
||||
} else Logger.error("Pin direction file is not readable: "+pin);
|
||||
} else Logger.error("Pin direction file not found: "+pin);
|
||||
} else Logger.error("Pin direction file is not readable: "+pin.pin);
|
||||
} else Logger.error("Pin direction file not found: "+pin.pin);
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
@@ -103,22 +100,22 @@ public class GPIO {
|
||||
* @param direction "in" for input, "out" for output
|
||||
* @return true if the direction is successfully set
|
||||
*/
|
||||
public static boolean SetPinDirection(int pin, String direction){
|
||||
Path pinPath = gpioPath.resolve("gpio"+pin).resolve("direction");
|
||||
public static boolean SetPinDirection(@NotNull RaspberryPi5BPins pin, String direction){
|
||||
Path pinPath = gpioPath.resolve("gpio"+pin.gpionumber).resolve("direction");
|
||||
if (pinPath.toFile().isFile()){
|
||||
if (Files.isWritable(pinPath)){
|
||||
direction = direction.trim().toLowerCase();
|
||||
if ("in".equals(direction) || "out".equals(direction)){
|
||||
try{
|
||||
Files.write(pinPath, direction.getBytes());
|
||||
Logger.info("Pin "+pin+" direction set to "+direction);
|
||||
Logger.info("Pin "+pin.pin+" direction set to "+direction);
|
||||
return true;
|
||||
} catch (Exception e){
|
||||
Logger.error("Failed to set pin direction: "+pin+", Message: "+e.getMessage());
|
||||
Logger.error("Failed to set pin direction: "+pin.pin+", Message: "+e.getMessage());
|
||||
}
|
||||
} else Logger.error("Invalid direction: "+direction);
|
||||
} else Logger.error("Pin direction file is not writable: "+pin);
|
||||
} else Logger.error("Pin direction file not found: "+pin);
|
||||
} else Logger.error("Pin direction file is not writable: "+pin.pin);
|
||||
} else Logger.error("Pin direction file not found: "+pin.pin);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -128,19 +125,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 RaspberryPi5BPins pin, boolean isON){
|
||||
Path pinPath = gpioPath.resolve("gpio"+pin.gpionumber).resolve("value");
|
||||
if (pinPath.toFile().isFile()){
|
||||
if (Files.isWritable(pinPath)){
|
||||
try{
|
||||
Files.write(pinPath, isON?"1".getBytes():"0".getBytes());
|
||||
Logger.info("Pin "+pin+" value set to "+(isON?"1":"0"));
|
||||
//Logger.info("Pin "+pin.pin+" value set to "+(isON?"1":"0"));
|
||||
return true;
|
||||
} catch (Exception e){
|
||||
Logger.error("Failed to set pin value: "+pin+", Message: "+e.getMessage());
|
||||
Logger.error("Failed to set pin value: "+pin.pin+", Message: "+e.getMessage());
|
||||
}
|
||||
} else Logger.error("Pin value file is not writable: "+pin);
|
||||
} else Logger.error("Pin value file not found: "+pin);
|
||||
} else Logger.error("Pin value file is not writable: "+pin.pin);
|
||||
} else Logger.error("Pin value file not found: "+pin.pin);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -149,17 +147,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 RaspberryPi5BPins pin){
|
||||
Path pinPath = gpioPath.resolve("gpio"+pin.gpionumber).resolve("value");
|
||||
if (pinPath.toFile().isFile()){
|
||||
if (Files.isReadable(pinPath)){
|
||||
try{
|
||||
return Files.readString(pinPath).trim();
|
||||
} catch (Exception e){
|
||||
Logger.error("Failed to read pin value: "+pin+", Message: "+e.getMessage());
|
||||
Logger.error("Failed to read pin value: "+pin.pin+", Message: "+e.getMessage());
|
||||
}
|
||||
} else Logger.error("Pin value file is not readable: "+pin);
|
||||
} else Logger.error("Pin value file not found: "+pin);
|
||||
} else Logger.error("Pin value file is not readable: "+pin.pin);
|
||||
} else Logger.error("Pin value file not found: "+pin.pin);
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
84
src/main/java/SBC/NetworkTransmitReceiveInfo.java
Normal file
84
src/main/java/SBC/NetworkTransmitReceiveInfo.java
Normal 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;
|
||||
}
|
||||
}
|
||||
45
src/main/java/SBC/ProcessorStatus.java
Normal file
45
src/main/java/SBC/ProcessorStatus.java
Normal 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;
|
||||
}
|
||||
}
|
||||
12
src/main/java/SBC/RamInformation.java
Normal file
12
src/main/java/SBC/RamInformation.java
Normal 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;
|
||||
}
|
||||
}
|
||||
42
src/main/java/SBC/RaspberryPi5BPins.java
Normal file
42
src/main/java/SBC/RaspberryPi5BPins.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package SBC;
|
||||
|
||||
public enum RaspberryPi5BPins {
|
||||
Pin03(3,"GPIO2/SDA", 573),
|
||||
Pin05(5,"GPIO3/SCL", 574),
|
||||
Pin07(7,"GPIO4/GPCLK0", 575),
|
||||
Pin08(8,"GPIO14/TXD", 585),
|
||||
Pin10(10,"GPIO15/RXD", 586),
|
||||
Pin11(11,"GPIO17", 588),
|
||||
Pin12(12,"GPIO18/PCMCLK", 589),
|
||||
Pin13(13,"GPIO27", 598),
|
||||
Pin15(15,"GPIO22", 593),
|
||||
Pin16(16,"GPIO23", 594),
|
||||
Pin18(18,"GPIO24", 595),
|
||||
Pin19(19,"GPIO10/MOSI", 581),
|
||||
Pin21(21,"GPIO9/MISO", 580),
|
||||
Pin22(22,"GPIO25", 596),
|
||||
Pin23(23,"GPIO11/SCLK", 582),
|
||||
Pin24(24,"GPIO8/CE0", 579),
|
||||
Pin26(26,"GPIO7/CE1", 578),
|
||||
Pin27(27,"GPIO0/IDSD", 587),
|
||||
Pin28(28,"GPIO1/IDSC", 587),
|
||||
Pin29(29,"GPIO5", 576),
|
||||
Pin31(31,"GPIO6", 577),
|
||||
Pin32(32,"GPIO12/PWM0", 583),
|
||||
Pin33(33,"GPIO13/PWM1", 584),
|
||||
Pin35(35,"GPIO19/PCMFS", 590),
|
||||
Pin36(36,"GPIO16", 587),
|
||||
Pin37(37,"GPIO26", 597),
|
||||
Pin38(38,"GPIO20/PCMDIN", 591),
|
||||
Pin40(40,"GPIO21/PCMDOUT", 592);
|
||||
|
||||
public final int pin;
|
||||
public final String name;
|
||||
public final int gpionumber;
|
||||
|
||||
RaspberryPi5BPins(int pin, String name, int gpionumber){
|
||||
this.pin = pin;
|
||||
this.name = name;
|
||||
this.gpionumber = gpionumber;
|
||||
}
|
||||
}
|
||||
179
src/main/java/SBC/SystemInformation.java
Normal file
179
src/main/java/SBC/SystemInformation.java
Normal 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;
|
||||
}
|
||||
}
|
||||
9
src/main/java/Web/AudioFilesInfo.java
Normal file
9
src/main/java/Web/AudioFilesInfo.java
Normal 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;
|
||||
}
|
||||
@@ -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,40 @@ public class WebServer {
|
||||
*/
|
||||
public void Stop(){
|
||||
try{
|
||||
connectedWebsocketClients.forEach(WsContext::closeSession);
|
||||
connectedWebsocketClients.clear();
|
||||
app.stop();
|
||||
Logger.info("Web server stopped");
|
||||
socketIOClients.forEach((key, client) -> client.disconnect());
|
||||
socketIOClients.clear();
|
||||
socketServer.stop();
|
||||
Logger.info("SocketIO server stopped");
|
||||
} catch (JavalinException e){
|
||||
Logger.error("Web server failed to stop: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -3,4 +3,5 @@ package Web;
|
||||
public interface WebsocketEvent {
|
||||
String onMessage(String message);
|
||||
WebsocketReply onWebsocketCommand(WebsocketCommand command);
|
||||
void NewCameraConfiguration();
|
||||
}
|
||||
|
||||
@@ -2,22 +2,23 @@ package id.co.gtc;
|
||||
|
||||
import Audio.AudioFileProperties;
|
||||
import Audio.AudioPlayer;
|
||||
import Audio.Bass;
|
||||
import Audio.PlaybackEvent;
|
||||
import Camera.PanTiltController;
|
||||
import Camera.RtspGrabber;
|
||||
import Camera.VapixProtocol;
|
||||
import Other.SomeCodes;
|
||||
import Web.WebServer;
|
||||
import Web.WebsocketCommand;
|
||||
import Web.WebsocketEvent;
|
||||
import Web.WebsocketReply;
|
||||
import SBC.*;
|
||||
import Web.*;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.bytedeco.opencv.global.opencv_core;
|
||||
import org.tinylog.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import static Other.SomeCodes.*;
|
||||
|
||||
@@ -28,6 +29,21 @@ public class Main {
|
||||
private static PanTiltController panTiltController;
|
||||
private static AudioFileProperties audioFileProperties;
|
||||
private static VapixProtocol vapixProtocol;
|
||||
private static Timer timer;
|
||||
private static int cpuTemperature;
|
||||
private static RamInformation ramInformation;
|
||||
private static ProcessorStatus[] previousCpuInfo;
|
||||
private static final Map<String, Integer> cpuUsage = new HashMap<>();
|
||||
private static NetworkTransmitReceiveInfo[] previousNetworkInfo;
|
||||
private static final Map<String, String> networkTX = new HashMap<>();
|
||||
private static final Map<String, String> networkRX = new HashMap<>();
|
||||
private static RaspberryPi5BPins AmplifierPower = null;
|
||||
private static RaspberryPi5BPins LedWeb = null;
|
||||
private static RaspberryPi5BPins LedIpCamera = null;
|
||||
private static RaspberryPi5BPins LedPanTilt = null;
|
||||
private static RaspberryPi5BPins Max485Direction = null;
|
||||
|
||||
private static ExecutorService gpioExecutor = null;
|
||||
|
||||
// Application start from here
|
||||
public static void main(String[] args) {
|
||||
@@ -37,16 +53,159 @@ public class Main {
|
||||
if (rtspGrabber!=null) rtspGrabber.Stop();
|
||||
if (panTiltController!=null) panTiltController.Close();
|
||||
if (vapixProtocol!=null) vapixProtocol.Close();
|
||||
if (timer!=null) timer.cancel();
|
||||
if (gpioExecutor!=null) gpioExecutor.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));
|
||||
}
|
||||
|
||||
}));
|
||||
|
||||
init_gpio();
|
||||
init_system_monitoring();
|
||||
|
||||
init_properties();
|
||||
init_audiofiles();
|
||||
|
||||
init_Vapix();
|
||||
init_audio();
|
||||
init_pantiltcontroller();
|
||||
init_webserver();
|
||||
init_rtspgrabber();
|
||||
init_pantiltcontroller();
|
||||
init_Vapix();
|
||||
}
|
||||
|
||||
private static void init_gpio(){
|
||||
if (GPIO.HaveGPIO()){
|
||||
gpioExecutor = Executors.newVirtualThreadPerTaskExecutor();
|
||||
AmplifierPower = InitializePin(RaspberryPi5BPins.Pin13, true);
|
||||
LedWeb = InitializePin(RaspberryPi5BPins.Pin15, true);
|
||||
LedIpCamera = InitializePin(RaspberryPi5BPins.Pin19, true);
|
||||
LedPanTilt = InitializePin(RaspberryPi5BPins.Pin21, true);
|
||||
Max485Direction = InitializePin(RaspberryPi5BPins.Pin23, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private static RaspberryPi5BPins InitializePin(RaspberryPi5BPins pin, boolean isOutput){
|
||||
boolean exported = false;
|
||||
if (GPIO.GpioPinExists(pin)){
|
||||
exported = true;
|
||||
} else {
|
||||
if (GPIO.ExportPin(pin)){
|
||||
exported = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (exported){
|
||||
if (GPIO.SetPinDirection(pin, isOutput?"out":"in")){
|
||||
return pin;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// dipanggil di PanTiltController, maka perlu public dan static
|
||||
public static void Max485Direction(boolean isTransmit){
|
||||
if (Max485Direction!=null){
|
||||
GPIO.SetValue(Max485Direction, isTransmit);
|
||||
}
|
||||
}
|
||||
|
||||
// dipanggil di PanTiltController, maka perlu public dan static
|
||||
public static void Blink_LedPanTilt(){
|
||||
Blink(LedPanTilt);
|
||||
}
|
||||
|
||||
private static void AmplifierControl(boolean isON){
|
||||
if (AmplifierPower!=null){
|
||||
GPIO.SetValue(AmplifierPower, isON);
|
||||
}
|
||||
}
|
||||
|
||||
private static void Blink(RaspberryPi5BPins pin){
|
||||
if (pin!=null){
|
||||
if (gpioExecutor!=null){
|
||||
gpioExecutor.submit(()->{
|
||||
GPIO.SetValue(pin, true);
|
||||
try {
|
||||
Thread.sleep(20);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
GPIO.SetValue(pin, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void init_system_monitoring(){
|
||||
TimerTask tt = new TimerTask() {
|
||||
@Override
|
||||
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(){
|
||||
@@ -73,10 +232,13 @@ public class Main {
|
||||
String username = config.getProperty("Camera_user");
|
||||
String password = config.getProperty("Camera_password");
|
||||
if (ValidString(ip)){
|
||||
if (ValidInteger(port)){
|
||||
if (ValidPortNumber(port)){
|
||||
if (ValidString(username)){
|
||||
if (ValidString(password)){
|
||||
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()){
|
||||
@@ -84,10 +246,13 @@ public class Main {
|
||||
} 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");
|
||||
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() {
|
||||
@@ -112,22 +277,32 @@ public class Main {
|
||||
Properties config = SomeCodes.LoadProperties("config.properties");
|
||||
String targetip = config.getProperty("Camera_ip");
|
||||
String rtsppath = config.getProperty("Camera_Rtsp_path");
|
||||
rtspGrabber = null;
|
||||
if (ValidString(targetip)){
|
||||
if (ValidString(rtsppath)){
|
||||
if (IpIsReachable(targetip)){
|
||||
Logger.info("Camera IP is reachable");
|
||||
rtspGrabber = new RtspGrabber(targetip, rtsppath);
|
||||
|
||||
|
||||
rtspGrabber.Start(true, 1920, 1080);
|
||||
} else Logger.error("Invalid Camera Path");
|
||||
} else Logger.error("Invalid Camera IP");
|
||||
} else Logger.error("Camera IP is not reachable");
|
||||
} else Logger.error("Camera Path is not valid string");
|
||||
} else Logger.error("Camera IP is not valid string");
|
||||
}
|
||||
|
||||
private static void init_audio() {
|
||||
audioPlayer = new AudioPlayer();
|
||||
audioPlayer.DetectOutputDevices();
|
||||
audioPlayer.OpenDevice(1,48000);
|
||||
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() {
|
||||
@@ -160,7 +335,7 @@ public class Main {
|
||||
|
||||
@Override
|
||||
public WebsocketReply onWebsocketCommand(WebsocketCommand command) {
|
||||
|
||||
Blink(LedWeb);
|
||||
byte speed;
|
||||
String cmd = command.command.toUpperCase().trim();
|
||||
switch (cmd){
|
||||
@@ -216,7 +391,9 @@ public class Main {
|
||||
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));
|
||||
@@ -226,15 +403,18 @@ public class Main {
|
||||
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":
|
||||
@@ -244,6 +424,7 @@ public class Main {
|
||||
if (zoom<vapixProtocol.GetPTZMinZoom()) zoom = vapixProtocol.GetPTZMinZoom();
|
||||
if (zoom>vapixProtocol.GetPTZMaxZoom()) zoom = vapixProtocol.GetPTZMaxZoom();
|
||||
if (vapixProtocol.Zoom(1, zoom)){
|
||||
Blink(LedIpCamera);
|
||||
return new WebsocketReply("ZOOM", String.valueOf(zoom));
|
||||
} else return new WebsocketReply("ZOOM", "Failed to zoom");
|
||||
} else return new WebsocketReply("ZOOM", "Invalid Zoom Value");
|
||||
@@ -252,20 +433,77 @@ public class Main {
|
||||
case "GET BASE64":
|
||||
if (rtspGrabber!=null){
|
||||
if (Objects.equals(command.data,"HQ"))
|
||||
return new WebsocketReply("GET BASE64", "data:image/jpeg;base64,"+ rtspGrabber.getLastHQBase64(), String.format("Streaming at %dx%d", rtspGrabber.getHQWidth(), rtspGrabber.getHQHeight()));
|
||||
return new WebsocketReply("GET BASE64", "data:image/jpeg;base64,"+ rtspGrabber.getLastHQBase64(), rtspGrabber.HQStreamingStatus());
|
||||
else
|
||||
return new WebsocketReply("GET BASE64", "data:image/jpeg;base64,"+ rtspGrabber.getLastLQBase64(), String.format("Streaming at %dx%d", rtspGrabber.getLQWidth(), rtspGrabber.getLQHeight()));
|
||||
return new WebsocketReply("GET BASE64", "data:image/jpeg;base64,"+ rtspGrabber.getLastLQBase64(), rtspGrabber.LQStreamingStatus());
|
||||
} else return new WebsocketReply("GET BASE64", "RTSP Grabber not initialized");
|
||||
case "GET RESOLUTION":
|
||||
if (vapixProtocol!=null){
|
||||
int[] res = vapixProtocol.GetCurrentResolution(1);
|
||||
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() {
|
||||
|
||||
Reference in New Issue
Block a user