first commit

This commit is contained in:
2024-11-09 08:52:49 +07:00
commit 2450f9f42a
90 changed files with 16323 additions and 0 deletions

38
.gitignore vendored Normal file
View File

@@ -0,0 +1,38 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

8
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

7
.idea/encodings.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>

View File

@@ -0,0 +1,67 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="ExtractMethodRecommender" enabled="true" level="WARNING" enabled_by_default="true">
<option name="minLength" value="745" />
</inspection_tool>
<inspection_tool class="HttpUrlsUsage" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="ignoredUrls">
<list>
<option value="http://" />
<option value="http://0.0.0.0" />
<option value="http://127.0.0.1" />
<option value="http://activemq.apache.org/schema/" />
<option value="http://cxf.apache.org/schemas/" />
<option value="http://java.sun.com/" />
<option value="http://javafx.com/fxml" />
<option value="http://javafx.com/javafx/" />
<option value="http://json-schema.org/draft" />
<option value="http://localhost" />
<option value="http://maven.apache.org/POM/" />
<option value="http://maven.apache.org/xsd/" />
<option value="http://primefaces.org/ui" />
<option value="http://schema.cloudfoundry.org/spring/" />
<option value="http://schemas.xmlsoap.org/" />
<option value="http://tiles.apache.org/" />
<option value="http://www.ibm.com/webservices/xsd" />
<option value="http://www.jboss.com/xml/ns/" />
<option value="http://www.jboss.org/j2ee/schema/" />
<option value="http://www.springframework.org/schema/" />
<option value="http://www.springframework.org/security/tags" />
<option value="http://www.springframework.org/tags" />
<option value="http://www.thymeleaf.org" />
<option value="http://www.w3.org/" />
<option value="http://xmlns.jcp.org/" />
</list>
</option>
</inspection_tool>
<inspection_tool class="MethodNameSameAsClassName" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="RedundantCast" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
<inspection_tool class="VulnerableLibrariesLocal" enabled="true" level="WARNING" enabled_by_default="true">
<option name="isIgnoringEnabled" value="true" />
<option name="ignoredModules">
<list>
<option value="BirdStrikeSoetta" />
<option value="BirdStrikeSoetta" />
</list>
</option>
<option name="ignoredPackages">
<list>
<option value="org.eclipse.jetty:jetty-http:11.0.23" />
<option value="org.eclipse.jetty:jetty-server:11.0.23" />
</list>
</option>
<option name="ignoredReasons">
<list>
<option value="Not exploitable" />
<option value="Not exploitable" />
</list>
</option>
</inspection_tool>
</profile>
</component>

6
.idea/jsLibraryMappings.xml generated Normal file
View File

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

62
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="PWA">
<option name="enabled" value="true" />
<option name="wasEnabledAtLeastOnce" value="true" />
</component>
<component name="ProjectInspectionProfilesVisibleTreeState">
<entry key="Project Default">
<profile-state>
<expanded-state>
<State />
<State>
<id>Android</id>
</State>
<State>
<id>CodePlugin DevKit</id>
</State>
<State>
<id>ComplianceLintAndroid</id>
</State>
<State>
<id>CorrectnessLintAndroid</id>
</State>
<State>
<id>Java</id>
</State>
<State>
<id>Java language level migration aidsJava</id>
</State>
<State>
<id>LintAndroid</id>
</State>
<State>
<id>PerformanceLintAndroid</id>
</State>
<State>
<id>Plugin DevKit</id>
</State>
<State>
<id>UsabilityLintAndroid</id>
</State>
</expanded-state>
<selected-state>
<State>
<id>Android</id>
</State>
</selected-state>
</profile-state>
</entry>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="liberica-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

124
.idea/uiDesigner.xml generated Normal file
View File

@@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

BIN
AudioFiles/elangWav.wav Normal file

Binary file not shown.

BIN
AudioFiles/gunshotsWav.wav Normal file

Binary file not shown.

BIN
AudioFiles/pinkNoiseWav.wav Normal file

Binary file not shown.

19
Config/config.properties Normal file
View File

@@ -0,0 +1,19 @@
# suppress inspection "UnusedProperty" for whole file
Camera_ip = 192.168.10.17
Camera_port = 80
Camera_user = root
Camera_password = password
Camera_Rtsp_path = /axis-media/media.amp
AudioFile01 = elangWav.wav
AudioFile02 = gunshotsWav.wav
AudioFile03 = pinkNoiseWav.wav
AudioFile04 = 04.mp3
AudioFile05 = 05.mp3
AudioVolumeOutput = 100
SerialPort = /dev/ttyUSB0
SerialBaudRate = 9600
PanTiltID = 1
WebUsername = admin
WebPassword = bandara
WebHost = 0.0.0.0
WebPort = 8080

View File

@@ -0,0 +1,34 @@
# SLF4J's SimpleLogger configuration file
# Simple implementation of Logger that sends all enabled log messages, for all defined loggers, to System.err.
# Default logging detail level for all instances of SimpleLogger.
# Must be one of ("trace", "debug", "info", "warn", or "error").
# If not specified, defaults to "info".
org.slf4j.simpleLogger.defaultLogLevel=warn
# Logging detail level for a SimpleLogger instance named "xxxxx".
# Must be one of ("trace", "debug", "info", "warn", or "error").
# If not specified, the default logging detail level is used.
#org.slf4j.simpleLogger.log.xxxxx=
# Set to true if you want the current date and time to be included in output messages.
# Default is false, and will output the number of milliseconds elapsed since startup.
#org.slf4j.simpleLogger.showDateTime=false
# The date and time format to be used in the output messages.
# The pattern describing the date and time format is the same that is used in java.text.SimpleDateFormat.
# If the format is not specified or is invalid, the default format is used.
# The default format is yyyy-MM-dd HH:mm:ss:SSS Z.
#org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS Z
# Set to true if you want to output the current thread name.
# Defaults to true.
#org.slf4j.simpleLogger.showThreadName=true
# Set to true if you want the Logger instance name to be included in output messages.
# Defaults to true.
#org.slf4j.simpleLogger.showLogName=true
# Set to true if you want the last component of the name to be included in output messages.
# Defaults to false.
#org.slf4j.simpleLogger.showShortLogName=false

View File

@@ -0,0 +1,9 @@
//writer = rolling file
writer = console
writer.file = logs/{date:yyyy-MM-dd}.log
writer.format = {date:yyyy-MM-dd HH:mm:ss} {level}: {class}.{method}() {message}
writer.policies = daily
writer.buffered = true
writer.charset = UTF-8
writer.level = info
writer.append = true

5
Html/html/.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,5 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/

8
Html/html/.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/LOGIN TENGAH.iml" filepath="$PROJECT_DIR$/.idea/LOGIN TENGAH.iml" />
</modules>
</component>
</project>

256
Html/html/index.html Normal file
View File

@@ -0,0 +1,256 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Bird Deterrent System</title>
<link rel="stylesheet" type="text/css" href="public/style/style.css">
<link rel="stylesheet" type="text/css" href="public/style/bootstrap.min.css">
<script src="public/js/bootstrap.bundle.min.js"></script>
<link rel="stylesheet" href="public/style/jquery.dataTables.min.css">
<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>
<link rel="stylesheet" href="public/style/all.min.css">
<script src="index.js"></script>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark text-light container4">
<div class="container-fluid">
<!-- <p class="p-title">Bird Deterrent System</p>-->
<a class="navbar-brand p-title" href="index.html">Bird Deterrent System</a>
<!-- Toggler/collapsible Button -->
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<!-- Collapsible Navbar -->
<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>
</li>
<li class="nav-item pad-menu">
<form action="/logout" method="get">
<button class="btn btn-outline-light" 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="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">
<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>
</div>
</div>
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-12 col-xl-4 ">
<br>
<div class="row">
<div class="col-3 ">
<p>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>
<button id="pan3" class="btn btn-outline-dark btn-sm" value="48" onclick="set_pan_speed(48)">3</button>
<button id="pan4" class="btn btn-outline-dark btn-sm" value="63" onclick="set_pan_speed(63)">4</button>
</div>
</div>
<div class="col-6 outside">
<div class="btn-circle">
<div class="row">
<div class="col"></div>
<div class="col">
<a onmousedown="btn_up()" onmouseup="stop_movement()">
<i class="btn-nav fa-solid fa-caret-up pad-top" title="Up"></i>
</a>
</div>
<div class="col"></div>
</div>
<div class="row">
<div class="col">
<a onmousedown="btn_left()" onmouseup="stop_movement()">
<i class="btn-nav fa-solid fa-caret-left pad-left" title="Left"></i>
</a>
</div>
<div class="col">
<a>
<i id="btn_center" class="btn-nav-center fa-solid fa-circle"></i>
</a>
</div>
<div class="col">
<a onmousedown="btn_right()" onmouseup="stop_movement()">
<i class="btn-nav fa-solid fa-caret-right pad-right" title="Right"></i>
</a>
</div>
</div>
<div class="row">
<div class="col"></div>
<div class="col">
<a onmousedown="btn_down()" onmouseup="stop_movement()">
<i class="i btn-nav fa-solid fa-caret-down pad-bottom" title="Down"></i>
</a>
</div>
<div class="col"></div>
</div>
</div>
</div>
<div class="col-3">
<p>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>
<button id="tilt3" class="btn btn-outline-dark btn-sm" value="48" onclick="set_tilt_speed(48)">3</button>
<button id="tilt4" class="btn btn-outline-dark btn-sm" value="63" onclick="set_tilt_speed(63)">4</button>
</div>
</div>
</div>
<div>
<p class="text-center">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>
</div>
</div>
</div>
<!-- <br>-->
<div class="text-center">
<h1 id="status_player" class="text-status1">Stop</h1>
</div>
<!-- <br>-->
<div class="row">
<div class="col">
<div id="div1" class="button-container">
<a id="btn_play1" class="btn-play1 show" onclick='play_button(1)' title='Play Content 1'>
<i class="fa-solid fa-play icon1"></i>
</a>
<a id="btn_stop1" class="btn-play1 hide" onclick='stop_button(1)' title='Stop Content 1'>
<i class="fa-solid fa-stop icon1"></i>
</a>
</div>
<br>
<p> Content 1</p>
</div>
<div class="col">
<div id="div2" class="button-container">
<a id="btn_play2" class="btn-play1 show" onclick='play_button(2)' title='Play Content 2'>
<i class="fa-solid fa-play icon1"></i>
</a>
<a id="btn_stop2" class="btn-play1 hide" onclick='stop_button(2)' title='Stop Content 2'>
<i class="fa-solid fa-stop icon1"></i>
</a>
</div>
<br>
<p> Content 2</p>
</div>
<div class="col">
<div id="div3" class="button-container">
<a id="btn_play3" class="btn-play1 show" onclick='play_button(3)' title='Play Content 3'>
<i class="fa-solid fa-play icon1"></i>
</a>
<a id="btn_stop3" class="btn-play1 hide" onclick='stop_button(3)' title='Stop Content 3'>
<i class="fa-solid fa-stop icon1"></i>
</a>
</div>
<br>
<p> Content 3</p>
</div>
<div class="col">
<div id="div4" class="button-container">
<a id="btn_play4" class="btn-play1 show" onclick='play_button(4)' title='Play Content 4'>
<i class="fa-solid fa-play icon1"></i>
</a>
<a id="btn_stop4" class="btn-play1 hide" onclick='stop_button(4)' title='Stop Content 4'>
<i class="fa-solid fa-stop icon1"></i>
</a>
</div>
<br>
<p> Content 4</p>
</div>
<div class="col">
<div id="div5" class="button-container">
<a id="btn_play5" class="btn-play1 show" onclick='play_button(5)' title='Play Content 5'>
<i class="fa-solid fa-play icon1"></i>
</a>
<a id="btn_stop5" class="btn-play1 hide" onclick='stop_button(5)' title='Stop Content 5'>
<i class="fa-solid fa-stop icon1"></i>
</a>
</div>
<br>
<p>Content 5</p>
</div>
</div>
<br>
<div>
<label for="customRange" class="form-label">Volume</label>
<div class="row">
<div class="col-10 pad-bar">
<input id="customRange" type="range" class="form-range" min="0" max="100" step="10" oninput="set_volumeoutput(this.value)">
<output id="volumebarvalue" class="output"></output>
</div>
<div class="col-2">
<div>
<button id="mute" class="btn-mute show" onclick="mute()" title="mute">
<i class="fa-solid fa-volume-high"></i>
</button>
<button id="unmute" class="btn-mute hide" onclick="unmute()" title="unmute">
<i class="fa-solid fa-volume-xmark"></i>
</button>
</div>
</div>
</div>
<br>
</div>
</div>
</div>
</div>
<!-- SECTION 3 -->
<div class="container-fluid footer1 outside">
<p>2024 Copyright &copy; Galva Technologies. All rights reserved.</p>
</div>
</body>
</html>

400
Html/html/index.js Normal file
View File

@@ -0,0 +1,400 @@
/**
* Speed of pan and tilt
* @type {number} 0 - 3F (hex) / 63 (dec)
*/
let pan_speed = 20;
let tilt_speed = 20;
/**
* @type {WebSocket}
*/
let ws;
let camerastream;
let no_stream_counter = 0;
document.addEventListener("DOMContentLoaded", function(){
camerastream = document.getElementById("camerastream");
setInterval(function (){
no_stream_counter++;
if (no_stream_counter>=20){
if (camerastream){
if (camerastream.src !== "public/images/not-available.png"){
camerastream.src = "public/images/not-available.png";
}
}
}
},100);
ws = new WebSocket("/ws");
ws.onopen = function(){
console.log("Connected to the server");
// request basic parameters
ws.send(JSON.stringify({
command: "GET MAX ZOOM",
data: 0
}));
ws.send(JSON.stringify({
command: "GET VOLUME",
data: 0
}));
ws.send(JSON.stringify({
command: "GET ZOOM",
data: 0
}));
ws.send(JSON.stringify({
command: "GET RESOLUTION",
data: 0
}));
ws.send(JSON.stringify({
command: "STREAMING STATUS",
data: 0
}));
}
ws.onmessage = function(event){
//console.log("Received data from server: " + event.data);
/**
* @type {{reply: string, data: string|number}} 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);
no_stream_counter = 0;
} else update_camerastream(null);
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;
case "STREAMING STATUS":
console.log("Streaming Status: "+dx.data);
$('#streaming_status').html(dx.data);
break;
}
}
ws.onclose = function(){
console.log("Connection closed");
}
ws.onerror = function(){
console.log("Error in connection");
}
set_pan_speed(32);
set_tilt_speed(32);
});
/**
* Update camera stream
* @param {String} value
*/
function update_camerastream(value){
if (value && value.length>0){
if (camerastream) camerastream.src = value;
} else {
if (camerastream) camerastream.src = "public/images/not-available.png";
}
}
function play_off(index){
$('#div'+index).prop("className", "button-container div-disabled");
$('#btn_play'+index).prop("className", "btn-play1 show text-light");
}
function play_on(index){
$('#div'+index).prop("className", "button-container");
$('#btn_play'+index).prop("className", "btn-play1 show");
}
function show_play_and_hide_stop(index){
$('#btn_play'+index).prop("className", "btn-play1 show"); ;
$('#btn_stop'+index).prop("className", "btn-play1 hide");
}
function show_stop_and_hide_play(index){
$('#btn_play'+index).prop("className", "btn-play1 hide");
$('#btn_stop'+index).prop("className", "btn-play1 show");
}
function allplay(){
for (let i = 0; i < 5; i++) play_on(i+1);
}
function play_button(index){
show_stop_and_hide_play(index);
$('#status_player').html("Play");
for (let i = 1; i <= 5; i++){
if (i!==index) play_off(i);
}
send_play_audio(index);
}
function stop_button(index){
show_play_and_hide_stop(index);
$('#status_player').html("Stop");
allplay();
send_stop_audio();
}
/**
* Send command to stop audio
*/
function send_stop_audio(){
if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({
command: "STOP AUDIO",
data: 0
}));
} else console.log("WebSocket is not open");
}
/**
* Send command to play audio
* @param {number} index 1 - 5
*/
function send_play_audio(index){
if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({
command: "PLAY AUDIO",
data: index
}));
} else console.log("WebSocket is not open");
}
function unmute(){
$('#mute').prop("className", "btn-mute show");
$('#unmute').prop("className", "btn-mute hide");
console.log("unmute")
if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({
command: "UNMUTE",
data: 0
}));
} else console.log("WebSocket is not open");
}
function mute(){
$('#mute').prop("className", "btn-mute hide");
$('#unmute').prop("className", "btn-mute show");
console.log("mute")
if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({
command: "MUTE",
data: 0
}));
} else console.log("WebSocket is not open");
}
function btn_center_off(){
$('#btn_center').prop("className", "btn-center-unactive fa-solid fa-circle");
}
function btn_up(){
console.log("btn_up")
if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({
command: "TILT UP",
data: tilt_speed
}));
btn_center_off()
} else console.log("WebSocket is not open");
}
function btn_left(){
console.log("btn_left")
if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({
command: "PAN LEFT",
data: pan_speed
}));
btn_center_off();
} else console.log("WebSocket is not open");
}
function btn_right(){
console.log("btn_right")
if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({
command: "PAN RIGHT",
data: pan_speed
}));
btn_center_off()
} else console.log("WebSocket is not open");
}
function btn_down(){
console.log("btn_down")
if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({
command: "TILT DOWN",
data: tilt_speed
}));
btn_center_off()
} else console.log("WebSocket is not open");
}
function stop_movement(){
console.log("stop_movement")
if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({
command: "STOP MOVEMENT",
data: 0
}));
} else console.log("WebSocket is not open");
}
function set_zoom(){
let zoom = document.getElementById("zoom");
console.log("set_zoom "+zoom.value)
if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({
command: "SET ZOOM",
data: zoom.value
}));
} else console.log("WebSocket is not open");
}
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");
}
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){
case 16:
$('#pan1').prop("className", classvalue );
break;
case 32:
$('#pan2').prop("className", classvalue);
break;
case 48:
$('#pan3').prop("className", classvalue);
break;
case 63:
$('#pan4').prop("className", classvalue);
break;
}
}
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){
case 16:
$('#tilt1').prop("className", classvalue);
break;
case 32:
$('#tilt2').prop("className", classvalue);
break;
case 48:
$('#tilt3').prop("className", classvalue);
break;
case 63:
$('#tilt4').prop("className", classvalue);
break;
}
}
// Pan Tilt Speed
function clearpan(){
for(let i = 1; i <= 4; i++){
$('#pan'+i).prop("className", "btn btn-outline-dark btn-sm");
}
}
function cleartilt(){
for(let i = 1; i <= 4; i++){
$('#tilt'+i).prop("className", "btn btn-outline-dark btn-sm");
}
}
function change_video_quality(){
let btn = document.getElementById("quality_video")
console.log('High Quality Video: '+btn.checked?'ON':'OFF');
if (ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({
command: "SET VIDEO QUALITY",
data: btn.checked?"HQ":"LQ"
}));
}
}

41
Html/html/login.html Normal file
View File

@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Bird Deterrent System</title>
<link rel="stylesheet" type="text/css" href="public/style/style.css">
<link rel="stylesheet" type="text/css" href="public/style/bootstrap.min.css">
<script src="index.js"></script>
<script src="public/js/bootstrap.bundle.min.js"></script>
<link rel="stylesheet" href="public/style/jquery.dataTables.min.css">
<script src="public/js/jquery-3.7.1.min.js"></script>
<script src="public/js/jquery.dataTables.min.js"></script>
<!--Link for Icon Fontawesome-->
<script src="public/js/all.min.js"></script>
<link rel="stylesheet" href="public/style/all.min.css">
<script src="login.js"></script>
</head>
<body>
<div class="bg-img">
<div class="content">
<header>Login</header>
<form action="/login" method="post">
<div class="field space input-rad">
<span class="fa-solid fa-user"></span>
<input id="username" placeholder="username" name="username" required>
</div>
<div class="field space input-rad">
<span class="fa-solid fa-lock"></span>
<input id='login_password' type='password' placeholder='Password' name='password' required>
<span class="fa-solid fa-eye" id='buttonShowPasswordLogin' onclick="showPasswordLogin()"></span>
</div>
<div class="class100 space">
<button class="btn btn-primary btn-lg w-100" type="submit">Log In</button>
</div>
</form>
</div>
</div>
</body>
</html>

10
Html/html/login.js Normal file
View File

@@ -0,0 +1,10 @@
function showPasswordLogin() {
const show = document.getElementById('login_password').type;
if (show === 'password') {
document.getElementById('login_password').type = 'text';
document.getElementById('buttonShowPasswordLogin').className = 'fa-solid fa-eye-slash';
} else {
document.getElementById('login_password').type = 'password';
document.getElementById('buttonShowPasswordLogin').className = 'fa-solid fa-eye';
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

6
Html/html/public/js/all.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

12
Html/html/public/style/all.min.css vendored Normal file

File diff suppressed because one or more lines are too long

515
Html/html/public/style/bootstrap.css vendored Normal file
View File

@@ -0,0 +1,515 @@
@charset "UTF-8";
:root {
--dt-row-selected: 13, 110, 253;
--dt-row-selected-text: 255, 255, 255;
--dt-row-selected-link: 9, 10, 11;
--dt-row-stripe: 0, 0, 0;
--dt-row-hover: 0, 0, 0;
--dt-column-ordering: 0, 0, 0;
--dt-html-background: white;
}
:root.dark {
--dt-html-background: rgb(33, 37, 41);
}
table.dataTable td.dt-control {
text-align: center;
cursor: pointer;
}
table.dataTable td.dt-control:before {
display: inline-block;
box-sizing: border-box;
content: "";
border-top: 5px solid transparent;
border-left: 10px solid rgba(0, 0, 0, 0.5);
border-bottom: 5px solid transparent;
border-right: 0px solid transparent;
}
table.dataTable tr.dt-hasChild td.dt-control:before {
border-top: 10px solid rgba(0, 0, 0, 0.5);
border-left: 5px solid transparent;
border-bottom: 0px solid transparent;
border-right: 5px solid transparent;
}
html.dark table.dataTable td.dt-control:before,
:root[data-bs-theme=dark] table.dataTable td.dt-control:before,
:root[data-theme=dark] table.dataTable td.dt-control:before {
border-left-color: rgba(255, 255, 255, 0.5);
}
html.dark table.dataTable tr.dt-hasChild td.dt-control:before,
:root[data-bs-theme=dark] table.dataTable tr.dt-hasChild td.dt-control:before,
:root[data-theme=dark] table.dataTable tr.dt-hasChild td.dt-control:before {
border-top-color: rgba(255, 255, 255, 0.5);
border-left-color: transparent;
}
div.dt-scroll {
width: 100%;
}
div.dt-scroll-body thead tr,
div.dt-scroll-body tfoot tr {
height: 0;
}
div.dt-scroll-body thead tr th, div.dt-scroll-body thead tr td,
div.dt-scroll-body tfoot tr th,
div.dt-scroll-body tfoot tr td {
height: 0 !important;
padding-top: 0px !important;
padding-bottom: 0px !important;
border-top-width: 0px !important;
border-bottom-width: 0px !important;
}
div.dt-scroll-body thead tr th div.dt-scroll-sizing, div.dt-scroll-body thead tr td div.dt-scroll-sizing,
div.dt-scroll-body tfoot tr th div.dt-scroll-sizing,
div.dt-scroll-body tfoot tr td div.dt-scroll-sizing {
height: 0 !important;
overflow: hidden !important;
}
table.dataTable thead > tr > th:active,
table.dataTable thead > tr > td:active {
outline: none;
}
table.dataTable thead > tr > th.dt-orderable-asc span.dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order:before,
table.dataTable thead > tr > td.dt-orderable-asc span.dt-column-order:before,
table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order:before {
position: absolute;
display: block;
bottom: 50%;
content: "▲";
content: "▲"/"";
}
table.dataTable thead > tr > th.dt-orderable-desc span.dt-column-order:after, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order:after,
table.dataTable thead > tr > td.dt-orderable-desc span.dt-column-order:after,
table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order:after {
position: absolute;
display: block;
top: 50%;
content: "▼";
content: "▼"/"";
}
table.dataTable thead > tr > th.dt-orderable-asc, table.dataTable thead > tr > th.dt-orderable-desc, table.dataTable thead > tr > th.dt-ordering-asc, table.dataTable thead > tr > th.dt-ordering-desc,
table.dataTable thead > tr > td.dt-orderable-asc,
table.dataTable thead > tr > td.dt-orderable-desc,
table.dataTable thead > tr > td.dt-ordering-asc,
table.dataTable thead > tr > td.dt-ordering-desc {
position: relative;
padding-right: 30px;
}
table.dataTable thead > tr > th.dt-orderable-asc span.dt-column-order, table.dataTable thead > tr > th.dt-orderable-desc span.dt-column-order, table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order,
table.dataTable thead > tr > td.dt-orderable-asc span.dt-column-order,
table.dataTable thead > tr > td.dt-orderable-desc span.dt-column-order,
table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order,
table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order {
position: absolute;
right: 12px;
top: 0;
bottom: 0;
width: 12px;
}
table.dataTable thead > tr > th.dt-orderable-asc span.dt-column-order:before, table.dataTable thead > tr > th.dt-orderable-asc span.dt-column-order:after, table.dataTable thead > tr > th.dt-orderable-desc span.dt-column-order:before, table.dataTable thead > tr > th.dt-orderable-desc span.dt-column-order:after, table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order:after, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order:after,
table.dataTable thead > tr > td.dt-orderable-asc span.dt-column-order:before,
table.dataTable thead > tr > td.dt-orderable-asc span.dt-column-order:after,
table.dataTable thead > tr > td.dt-orderable-desc span.dt-column-order:before,
table.dataTable thead > tr > td.dt-orderable-desc span.dt-column-order:after,
table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order:before,
table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order:after,
table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order:before,
table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order:after {
left: 0;
opacity: 0.125;
line-height: 9px;
font-size: 0.8em;
}
table.dataTable thead > tr > th.dt-orderable-asc, table.dataTable thead > tr > th.dt-orderable-desc,
table.dataTable thead > tr > td.dt-orderable-asc,
table.dataTable thead > tr > td.dt-orderable-desc {
cursor: pointer;
}
table.dataTable thead > tr > th.dt-orderable-asc:hover, table.dataTable thead > tr > th.dt-orderable-desc:hover,
table.dataTable thead > tr > td.dt-orderable-asc:hover,
table.dataTable thead > tr > td.dt-orderable-desc:hover {
outline: 2px solid rgba(0, 0, 0, 0.05);
outline-offset: -2px;
}
table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order:after,
table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order:before,
table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order:after {
opacity: 0.6;
}
table.dataTable thead > tr > th.sorting_desc_disabled span.dt-column-order:after, table.dataTable thead > tr > th.sorting_asc_disabled span.dt-column-order:before,
table.dataTable thead > tr > td.sorting_desc_disabled span.dt-column-order:after,
table.dataTable thead > tr > td.sorting_asc_disabled span.dt-column-order:before {
display: none;
}
table.dataTable thead > tr > th:active,
table.dataTable thead > tr > td:active {
outline: none;
}
div.dt-scroll-body > table.dataTable > thead > tr > th,
div.dt-scroll-body > table.dataTable > thead > tr > td {
overflow: hidden;
}
:root.dark table.dataTable thead > tr > th.dt-orderable-asc:hover, :root.dark table.dataTable thead > tr > th.dt-orderable-desc:hover,
:root.dark table.dataTable thead > tr > td.dt-orderable-asc:hover,
:root.dark table.dataTable thead > tr > td.dt-orderable-desc:hover,
:root[data-bs-theme=dark] table.dataTable thead > tr > th.dt-orderable-asc:hover,
:root[data-bs-theme=dark] table.dataTable thead > tr > th.dt-orderable-desc:hover,
:root[data-bs-theme=dark] table.dataTable thead > tr > td.dt-orderable-asc:hover,
:root[data-bs-theme=dark] table.dataTable thead > tr > td.dt-orderable-desc:hover {
outline: 2px solid rgba(255, 255, 255, 0.05);
}
div.dt-processing {
position: absolute;
top: 50%;
left: 50%;
width: 200px;
margin-left: -100px;
margin-top: -22px;
text-align: center;
padding: 2px;
z-index: 10;
}
div.dt-processing > div:last-child {
position: relative;
width: 80px;
height: 15px;
margin: 1em auto;
}
div.dt-processing > div:last-child > div {
position: absolute;
top: 0;
width: 13px;
height: 13px;
border-radius: 50%;
background: rgb(13, 110, 253);
background: rgb(var(--dt-row-selected));
animation-timing-function: cubic-bezier(0, 1, 1, 0);
}
div.dt-processing > div:last-child > div:nth-child(1) {
left: 8px;
animation: datatables-loader-1 0.6s infinite;
}
div.dt-processing > div:last-child > div:nth-child(2) {
left: 8px;
animation: datatables-loader-2 0.6s infinite;
}
div.dt-processing > div:last-child > div:nth-child(3) {
left: 32px;
animation: datatables-loader-2 0.6s infinite;
}
div.dt-processing > div:last-child > div:nth-child(4) {
left: 56px;
animation: datatables-loader-3 0.6s infinite;
}
@keyframes datatables-loader-1 {
0% {
transform: scale(0);
}
100% {
transform: scale(1);
}
}
@keyframes datatables-loader-3 {
0% {
transform: scale(1);
}
100% {
transform: scale(0);
}
}
@keyframes datatables-loader-2 {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(24px, 0);
}
}
table.dataTable.nowrap th, table.dataTable.nowrap td {
white-space: nowrap;
}
table.dataTable th,
table.dataTable td {
box-sizing: border-box;
}
table.dataTable th.dt-left,
table.dataTable td.dt-left {
text-align: left;
}
table.dataTable th.dt-center,
table.dataTable td.dt-center {
text-align: center;
}
table.dataTable th.dt-right,
table.dataTable td.dt-right {
text-align: right;
}
table.dataTable th.dt-justify,
table.dataTable td.dt-justify {
text-align: justify;
}
table.dataTable th.dt-nowrap,
table.dataTable td.dt-nowrap {
white-space: nowrap;
}
table.dataTable th.dt-empty,
table.dataTable td.dt-empty {
text-align: center;
vertical-align: top;
}
table.dataTable th.dt-type-numeric, table.dataTable th.dt-type-date,
table.dataTable td.dt-type-numeric,
table.dataTable td.dt-type-date {
text-align: right;
}
table.dataTable thead th,
table.dataTable thead td,
table.dataTable tfoot th,
table.dataTable tfoot td {
text-align: left;
}
table.dataTable thead th.dt-head-left,
table.dataTable thead td.dt-head-left,
table.dataTable tfoot th.dt-head-left,
table.dataTable tfoot td.dt-head-left {
text-align: left;
}
table.dataTable thead th.dt-head-center,
table.dataTable thead td.dt-head-center,
table.dataTable tfoot th.dt-head-center,
table.dataTable tfoot td.dt-head-center {
text-align: center;
}
table.dataTable thead th.dt-head-right,
table.dataTable thead td.dt-head-right,
table.dataTable tfoot th.dt-head-right,
table.dataTable tfoot td.dt-head-right {
text-align: right;
}
table.dataTable thead th.dt-head-justify,
table.dataTable thead td.dt-head-justify,
table.dataTable tfoot th.dt-head-justify,
table.dataTable tfoot td.dt-head-justify {
text-align: justify;
}
table.dataTable thead th.dt-head-nowrap,
table.dataTable thead td.dt-head-nowrap,
table.dataTable tfoot th.dt-head-nowrap,
table.dataTable tfoot td.dt-head-nowrap {
white-space: nowrap;
}
table.dataTable tbody th.dt-body-left,
table.dataTable tbody td.dt-body-left {
text-align: left;
}
table.dataTable tbody th.dt-body-center,
table.dataTable tbody td.dt-body-center {
text-align: center;
}
table.dataTable tbody th.dt-body-right,
table.dataTable tbody td.dt-body-right {
text-align: right;
}
table.dataTable tbody th.dt-body-justify,
table.dataTable tbody td.dt-body-justify {
text-align: justify;
}
table.dataTable tbody th.dt-body-nowrap,
table.dataTable tbody td.dt-body-nowrap {
white-space: nowrap;
}
/*! Bootstrap 5 integration for DataTables
*
* ©2020 SpryMedia Ltd, all rights reserved.
* License: MIT datatables.net/license/mit
*/
table.table.dataTable {
clear: both;
margin-bottom: 0;
max-width: none;
border-spacing: 0;
}
table.table.dataTable.table-striped > tbody > tr:nth-of-type(2n+1) > * {
box-shadow: none;
}
table.table.dataTable > :not(caption) > * > * {
background-color: var(--bs-table-bg);
}
table.table.dataTable > tbody > tr {
background-color: transparent;
}
table.table.dataTable > tbody > tr.selected > * {
box-shadow: inset 0 0 0 9999px rgb(13, 110, 253);
box-shadow: inset 0 0 0 9999px rgb(var(--dt-row-selected));
color: rgb(255, 255, 255);
color: rgb(var(--dt-row-selected-text));
}
table.table.dataTable > tbody > tr.selected a {
color: rgb(9, 10, 11);
color: rgb(var(--dt-row-selected-link));
}
table.table.dataTable.table-striped > tbody > tr:nth-of-type(2n+1) > * {
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-stripe), 0.05);
}
table.table.dataTable.table-striped > tbody > tr:nth-of-type(2n+1).selected > * {
box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.95);
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.95);
}
table.table.dataTable.table-hover > tbody > tr:hover > * {
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.075);
}
table.table.dataTable.table-hover > tbody > tr.selected:hover > * {
box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.975);
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.975);
}
div.dt-container div.dt-layout-start > *:not(:last-child) {
margin-right: 1em;
}
div.dt-container div.dt-layout-end > *:not(:first-child) {
margin-left: 1em;
}
div.dt-container div.dt-layout-full {
width: 100%;
}
div.dt-container div.dt-layout-full > *:only-child {
margin-left: auto;
margin-right: auto;
}
div.dt-container div.dt-layout-table > div {
display: block !important;
}
@media screen and (max-width: 767px) {
div.dt-container div.dt-layout-start > *:not(:last-child) {
margin-right: 0;
}
div.dt-container div.dt-layout-end > *:not(:first-child) {
margin-left: 0;
}
}
div.dt-container div.dt-length label {
font-weight: normal;
text-align: left;
white-space: nowrap;
}
div.dt-container div.dt-length select {
width: auto;
display: inline-block;
margin-right: 0.5em;
}
div.dt-container div.dt-search {
text-align: right;
}
div.dt-container div.dt-search label {
font-weight: normal;
white-space: nowrap;
text-align: left;
}
div.dt-container div.dt-search input {
margin-left: 0.5em;
display: inline-block;
width: auto;
}
div.dt-container div.dt-paging {
margin: 0;
}
div.dt-container div.dt-paging ul.pagination {
margin: 2px 0;
flex-wrap: wrap;
}
div.dt-container div.dt-row {
position: relative;
}
div.dt-scroll-head table.dataTable {
margin-bottom: 0 !important;
}
div.dt-scroll-body {
border-bottom-color: var(--bs-border-color);
border-bottom-width: var(--bs-border-width);
border-bottom-style: solid;
}
div.dt-scroll-body > table {
border-top: none;
margin-top: 0 !important;
margin-bottom: 0 !important;
}
div.dt-scroll-body > table > tbody > tr:first-child {
border-top-width: 0;
}
div.dt-scroll-body > table > thead > tr {
border-width: 0 !important;
}
div.dt-scroll-body > table > tbody > tr:last-child > * {
border-bottom: none;
}
div.dt-scroll-foot > .dt-scroll-footInner {
box-sizing: content-box;
}
div.dt-scroll-foot > .dt-scroll-footInner > table {
margin-top: 0 !important;
border-top: none;
}
div.dt-scroll-foot > .dt-scroll-footInner > table > tfoot > tr:first-child {
border-top-width: 0 !important;
}
@media screen and (max-width: 767px) {
div.dt-container div.dt-length,
div.dt-container div.dt-search,
div.dt-container div.dt-info,
div.dt-container div.dt-paging {
text-align: center;
}
div.dt-container .row {
--bs-gutter-y: 0.5rem;
}
div.dt-container div.dt-paging ul.pagination {
justify-content: center !important;
}
}
table.dataTable.table-sm > thead > tr th.dt-orderable-asc, table.dataTable.table-sm > thead > tr th.dt-orderable-desc, table.dataTable.table-sm > thead > tr th.dt-ordering-asc, table.dataTable.table-sm > thead > tr th.dt-ordering-desc,
table.dataTable.table-sm > thead > tr td.dt-orderable-asc,
table.dataTable.table-sm > thead > tr td.dt-orderable-desc,
table.dataTable.table-sm > thead > tr td.dt-ordering-asc,
table.dataTable.table-sm > thead > tr td.dt-ordering-desc {
padding-right: 20px;
}
table.dataTable.table-sm > thead > tr th.dt-orderable-asc span.dt-column-order, table.dataTable.table-sm > thead > tr th.dt-orderable-desc span.dt-column-order, table.dataTable.table-sm > thead > tr th.dt-ordering-asc span.dt-column-order, table.dataTable.table-sm > thead > tr th.dt-ordering-desc span.dt-column-order,
table.dataTable.table-sm > thead > tr td.dt-orderable-asc span.dt-column-order,
table.dataTable.table-sm > thead > tr td.dt-orderable-desc span.dt-column-order,
table.dataTable.table-sm > thead > tr td.dt-ordering-asc span.dt-column-order,
table.dataTable.table-sm > thead > tr td.dt-ordering-desc span.dt-column-order {
right: 5px;
}
div.dt-scroll-head table.table-bordered {
border-bottom-width: 0;
}
div.table-responsive > div.dt-container > div.row {
margin: 0;
}
div.table-responsive > div.dt-container > div.row > div[class^=col-]:first-child {
padding-left: 0;
}
div.table-responsive > div.dt-container > div.row > div[class^=col-]:last-child {
padding-right: 0;
}
:root[data-bs-theme=dark] {
--dt-row-hover: 255, 255, 255;
--dt-row-stripe: 255, 255, 255;
--dt-column-ordering: 255, 255, 255;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,822 @@
html,body{
width: 100%;
height: 100%;
overflow-y: scroll;
scrollbar-width: none;
-ms-overflow-style: none;
background-color: black;
}
body{
overflow: visible;
flex-wrap: wrap;
}
/*HEADER */
*,
*:after,
*:before {
box-sizing: border-box;
}
:root {
--header-outer-height: 110px;
--header-inner-height: 70px;
--header-height-difference: calc(
var(--header-outer-height) - var(--header-inner-height)
);
--header-bg: #fff;
}
body {
font-family: "DM Sans", sans-serif;
background-color: #f2f5f7;
line-height: 1.5;
/*min-height: 300vh;*/
position: relative;
}
.responsive-wrapper {
width: 90%;
max-width: 1280px;
margin-left: auto;
margin-right: auto;
}
/* Sticky header */
.header-outer {
/* Make it stick */
height: var(--header-outer-height);
position: sticky;
top: calc(
var(--header-height-difference) * -1
); /* Multiply by -1 to get a negative value */
display: flex;
align-items: center;
/* Other */
background-color: var(--header-bg);
box-shadow: 0 2px 10px 0 rgba(0,0,0, 0.1);
z-index: 3;
}
.header-inner {
/* Make it stick */
height: var(--header-inner-height);
position: sticky;
top: 0;
/* Other */
display: flex;
align-items: center;
justify-content: space-between;
}
/* Styling of other elements */
.header-logo img {
display: block;
height: calc(var(--header-inner-height) - 30px);
}
.header-navigation {
display: flex;
flex-wrap: wrap;
}
.header-navigation a,
.header-navigation button {
font-size: 1.125rem;
color: inherit;
margin-left: 1.75rem;
position: relative;
font-weight: 500;
}
.header-navigation a {
display: none;
font-size: 1.125rem;
color: inherit;
text-decoration: none;
}
.header-navigation button {
border: 0;
background-color: transparent;
padding: 0;
}
.header-navigation a:hover:after,
.header-navigation button:hover:after {
transform: scalex(1);
}
.header-navigation a:after,
.header-navigation button:after {
transition: 0.25s ease;
content: "";
display: block;
width: 100%;
height: 2px;
background-color: currentcolor;
transform: scalex(0);
position: absolute;
bottom: -2px;
left: 0;
}
@media (min-width: 800px) {
.header-navigation a {
display: inline-block;
}
.header-navigation button {
display: none;
}
}
.card-left{
background-color: white;
}
.card-right{
background-color: cornsilk;
}
.container1{
background-color: white;
/*height: 500px;*/
}
.container2{
/*background-color: #e6eef3;*/
background: linear-gradient(rgb(253, 252, 251), rgb(226, 229, 231));
/*height: 500px;*/
color: #454647;
}
.container3{
background: linear-gradient(rgb(253, 252, 251), rgb(226, 229, 231));
/*height: 25em;*/
text-align: center;
align-items: center;
min-height: 70vh;
}
.block1{
background: linear-gradient(rgb(253, 252, 251), rgb(226, 229, 231));
height: 100%;
}
.block2{
background: linear-gradient(rgb(249, 251, 252), rgb(207, 230, 241));
}
.button-green1{
background: linear-gradient(90deg, rgb(80, 167, 186), rgb(103, 147, 174));
/*color: rgb(255, 255, 255);*/
color: white !important;
font-weight: bold;
}
.heigh200{
height: 200px;
padding-top: 20px;
padding-left: 30px;
padding-bottom: 20px;
/*margin: 10px;*/
/*padding: 20px;*/
}
.text-right{
text-align: right;
}
.text-left{
text-align: left;
}
.padding-text-footer{
padding-top: 1em;
padding-bottom: 1em;
}
.img-footer{
width: 6em;
}
.main{
padding: 3em;
}
.card-white {
width: 100%;
max-width: 600px;
border-radius: 8px;
box-shadow: 0 15px 30px 0 rgba(0,0,0, 0.1);
background-color: #fff;
padding: 2.5rem;
margin-left: auto;
margin-right: auto;
margin-bottom: 2rem;
font-size: 1.125rem;
}
.h1-grad{
text-transform: capitalize;
letter-spacing: 0.8px;
font-family: "Roboto", sans-serif;
font-weight: 900;
font-size: clamp(3.4375rem, 3.25rem + 0.75vw, 4rem);
background-color: #005baa;
background-image: linear-gradient(45deg, #005baa, #000000);
background-size: 100%;
background-repeat: repeat;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
-moz-background-clip: text;
-moz-text-fill-color: transparent;
}
.hr1 {
display: block;
background: #005baa;
height: 1rem;
width: 6.25rem;
border: none;
margin: 1.125rem 0 1.875rem 0;
}
main a {
display: inline-block;
text-decoration: none;
text-transform: uppercase;
color: #717171;
font-weight: 500;
background: #fff;
border-radius: 3.125rem;
transition: 0.3s ease-in-out;
}
.btn-contact {
display: inline-block;
text-decoration: none;
text-transform: uppercase;
color: #717171;
font-weight: 500;
background: #fff;
border-radius: 3.125rem;
transition: 0.3s ease-in-out;
border: 2px solid #c2c2c2;
margin-top: 2.188rem;
padding: 0.625rem 1.875rem;
}
.btn-contact:hover {
border: 0.125rem solid #005baa;
color: #005baa;
}
.img{
margin-top: 1.5em;
margin-bottom: 1.5em;
padding: 2em;
width: 100%;
border-radius: 2.8em;
}
.img:hover{
-webkit-transform: scale(1.2);
-ms-transform: scale(1.2);
transform: scale(1.2);
transition: 1s ease;
z-index: 1;
}
.swiper-slide {
width: 18.75rem;
height: 28.125rem;
display: flex;
flex-direction: column;
justify-content: end;
align-items: self-start;
}
.swiper-slide h2 {
color: #fff;
font-family: "Roboto", sans-serif;
font-weight: 400;
font-size: 1.4rem;
line-height: 1.4;
margin-bottom: 0.625rem;
padding: 0 0 0 1.563rem;
text-transform: uppercase;
}
.swiper-slide p {
color: #dadada;
font-family: "Roboto", sans-serif;
font-weight: 300;
padding: 0 1.563rem;
line-height: 1.6;
font-size: 0.75rem;
display: -webkit-box;
-webkit-line-clamp: 4;
-webkit-box-orient: vertical;
overflow: hidden;
}
.swiper-slide a {
margin: 1.25rem 1.563rem 3.438rem 1.563rem;
padding: 0.438em 1.875rem;
font-size: 0.9rem;
}
.swiper-slide a:hover {
color: #005baa;
}
.swiper-slide div {
display: none;
opacity: 0;
padding-bottom: 0.625rem;
}
.swiper-slide--one {
background: linear-gradient(to top, #0f2027, #203a4300, #2c536400),
url("https://images.unsplash.com/photo-1628944682084-831f35256163?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=687&q=80")
no-repeat 50% 50% / cover;
color: white;
border-radius: 10px;
}
.swiper-slide--two {
background: linear-gradient(to top, #0f2027, #203a4300, #2c536400),
url("https://images.unsplash.com/photo-1570481662006-a3a1374699e8?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=765&q=80")
no-repeat 50% 50% / cover;
color: white;
border-radius: 10px;
}
.swiper-slide--three {
background: linear-gradient(to top, #0f2027, #203a4300, #2c536400),
url("https://images.unsplash.com/photo-1515309025403-4b0184873cef?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=735&q=80")
no-repeat 50% 50% / cover;
color: white;
border-radius: 10px;
}
.padd-card{
padding: 1em;
}
.div-left{
background-color: cadetblue;
}
.div-right{
background-color: cornsilk;
}
/*---------------------BIRD STRIKE---------------------*/
.checkbox{
width: 17x;
height: 17px;
border-radius: 10px
}
.class50{
width: 50%;
}
.class100{
width: 100%;
}
.show{
display: block;
}
.hide{
display: none;
}
.round-button{
width: 10em;
height: 10em;
border-radius: 100px !important;
color: rebeccapurple !important;
}
/*------------------------------------------------------------------*/
.row.paddless{
margin-bottom: -10px !important;
}
input.checkbox{
border-color: red;
margin-bottom: -20px !important;
margin-top: -20px !important;
padding-bottom: -20px;
right: auto;
}
.icon{
margin: 26%;
font-size: 70px;
}
.opt1{
margin-top: -5px;
margin-bottom: -5px;
}
.opt-left{
margin-left: -1.2em;
}
/*---------------------------------------------------------------------------------------------START HERE ------------------*/
.body{
background-color: whitesmoke;
/*height: 100%;*/
}
.footer1{
background: rgb(51, 51, 51);
color: white;
height:100px;
}
.container4{
background: linear-gradient(rgb(67, 67, 67), rgb(14, 14, 14));
/*height: 25em;*/
text-align: center;
align-items: center;
}
.p-bird{
font-size: 7em;
font-weight: bold;
text-align: left;
padding-left: 0.5em;
margin: 0;
font-family: Tahoma, Geneva, Verdana, sans-serif;
color: ghostwhite;
}
.p-strike{
font-size: 7em;
font-weight: bold;
text-align: left;
padding-left: 1.5em;
margin: 0;
font-family: Tahoma, Geneva, Verdana, sans-serif;
color: ghostwhite;
}
.card-page1{
border: none;
box-shadow: rgba(0, 0, 0, 0.25) 0px 25px 50px -12px;
/*box-shadow: rgba(0, 0, 0, 0.45) 0px 25px 20px -20px;*/
border-radius: 20px;
}
.btn-nav{
margin: 0;
font-size: 5em;
color: lightgray;
}
.btn-nav:hover{
color: white;
/*box-shadow: rgba(0, 0, 0, 0.4) 0px 30px 90px;*/
}
.btn-nav:active{
color: dodgerblue;
}
.btn-nav-center{
font-size: 2em;
/*color: #434343;*/
color: dodgerblue;
}
.btn-center-unactive{
font-size: 2em;
color: #434343;
}
.pad-img{
/*padding: 2em;*/
}
.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: 10em;
height: 10em;
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: -12px;*/
/*margin-bottom: -40px;*/
margin-top: -13px;
margin-bottom: -3px;
}
.pad-left{
margin-top: -25px;
/*margin-bottom: -20px;*/
margin-right: -20px;
}
.pad-right{
margin-top: -25px;
/*margin-bottom: -20px;*/
margin-left: -20px;
}
.pad-bottom{
/*margin-top: -20px;*/
/*margin-bottom: -20px;*/
margin-top: -25px;
/*margin-bottom: -12px;*/
}
/*--------------------------------BUTTON-------------------------------*/
.btn-round{
height: 150px;
width: 150px;
border-radius: 100%;
background: white;
border: 2px solid whitesmoke;
box-shadow: 0 4px 4px rgba(0, 0, 0, .3);
}
.button-container {
display: flex;
justify-content: center; /* Center horizontally */
align-items: center; /* Center vertically */
/*height: 100vh; !* Full viewport height *!*/
}
.outside{
display: flex;
align-items: center;
justify-content: center;
}
.outside-logout{
/*position: absolute;*/
display: flex;
/*text-align: right;*/
margin-top: 3.5em;
padding-bottom: 1em;
justify-content: end;
bottom: 10px; /* Adjust as needed */
right: 10px; /* Adjust as needed */
}
/*---------HEADER 2-------------------*/
.p-bird2{
font-size: 6rem;
font-weight: lighter;
text-align: left;
padding-left: 0.5em;
margin: 0;
font-family: Tahoma, Geneva, Verdana, sans-serif;
color: ghostwhite;
}
.hr-lines{
position: relative;
max-width: 100%;
margin: 35px auto;
text-align: center;
font-size: 3.7em;
color: white;
/*z-index: 2;*/
}
.hr-lines:before {
content:" ";
height: 4px;
width: 50px;
background: red;
display: block;
position: absolute;
top: 50%;
left: 0;
/*stroke-dasharray: 10px;*/
}
.hr-lines:after{
content:" ";
height: 4px;
width: 50px;
background: red;
display: block;
position: absolute;
top: 50%;
right: 0;
}
/*------------------------------------------------5 BUTTON PLAY KONTEN -------------------------*/
.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: #a7a7a7;*/
color: black;
/*width: 100%;*/
/*padding-top: 100%;*/
width: 4.2em;
height: 4.2em;
/*padding-top: 100%;*/
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;
/*top: -18px;*/
/*left: -18px;*/
/*bottom: -18px;*/
/*right: -18px;*/
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: 2.2em;
position: absolute;
top: -2px;
left: 3px;
bottom: 0;
right: 0;
text-align: center;
}
.text-status1{
/*padding-top: 1em;*/
color: black;
font-size: 2em;
}
.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: #a7a7a7;*/
color: black;
width: 2em;
height: 2em;
padding: 0.2em;
/*width: 100%;*/
/*padding-top: 100%;*/
position: relative;
text-align: center;
border-radius: 50%;
box-shadow: 0px 3px 8px #aaa, inset 0px 2px 3px #fff;
border: solid 1px transparent;
}
.pad-val-volume{
padding-left: 0 !important;
}
.pad-bar{
padding: 0.5em;
}
/*----------------------------------------------------LOGIN------------------------------------*/
.bg-img{
background: url('../images/airport-terminal.jpg');
/*background: url('../images/airport-terminal-bw.jpg');*/
height: 100vh;
background-size: cover;
background-position: center;
}
.bg-img:after{
position: absolute;
content: '';
top: 0;
left: 0;
height: 100%;
width: 100%;
background: rgb(40 67 73 / 40%);
}
.content{
position: absolute;
top: 50%;
left: 50%;
border-radius: 15px;
z-index: 999;
text-align: center;
backdrop-filter: blur(10px);
padding: 60px 32px;
width: 370px;
transform: translate(-50%,-50%);
background: rgba(255,255,255,0.04);
box-shadow: -1px 4px 28px 0px rgba(0,0,0,0.75);
}
.content header{
color: white;
font-size: 33px;
font-weight: 600;
margin: 0 0 35px 0;
font-family: 'Montserrat',sans-serif;
}
.field{
position: relative;
height: 45px;
width: 100%;
display: flex;
background: rgba(255,255,255,0.94);
align-items: center;
padding: 10px;
/*border-radius: 4px;*/
}
.field span{
color: #222;
width: 40px;
line-height: 45px;
}
.field input{
height: 100%;
width: 100%;
background: transparent;
border: none;
outline: none;
color: #222;
font-size: 16px;
font-family: 'Poppins',sans-serif;
padding: 10px;
}
.space{
margin-top: 16px;
}
.showw{
position: absolute;
right: 13px;
font-size: 13px;
font-weight: 700;
color: #222;
display: none;
cursor: pointer;
font-family: 'Montserrat',sans-serif;
}
.pass-key:valid ~ .show{
display: block;
}
.pass{
text-align: left;
margin: 10px 0;
}
.pass a{
color: white;
text-decoration: none;
font-family: 'Poppins',sans-serif;
}
.pass:hover a{
text-decoration: underline;
}
.field input[type="submit"]{
background: #3498db;
border: 1px solid #2691d9;
color: white;
font-size: 18px;
letter-spacing: 1px;
font-weight: 600;
cursor: pointer;
font-family: 'Montserrat',sans-serif;
}
.field input[type="submit"]:hover{
background: #2691d9;
}
.login{
color: white;
margin: 20px 0;
font-family: 'Poppins',sans-serif;
}
.input-rad{
border-radius: 4px;
}
.padding-lock{
padding-left: 10px;
padding-right: 10px;
}
.padding-btn{
padding: 0;
}
.pad-br{
padding-bottom: 12px;
}
.div-disabled{
pointer-events: none;
}
.output{
padding-left: 20%;
}
.pad-menu{
padding-right: 20px;
}
.container-setting{
background: linear-gradient(rgb(253, 252, 251), rgb(226, 229, 231));
min-height: 70vh;
}
.p-title{
font-size: 2.5em !important;
}
.bg-gray{
background: linear-gradient(rgb(253, 252, 251), rgb(226, 229, 231));
}
.card-shadow{
box-shadow: rgba(0, 0, 0, 0.45) 0px 25px 20px -20px;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

256
Html/html/setting.html Normal file
View File

@@ -0,0 +1,256 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Bird Deterrent System</title>
<link rel="stylesheet" type="text/css" href="public/style/style.css">
<link rel="stylesheet" type="text/css" href="public/style/bootstrap.min.css">
<script src="public/js/bootstrap.bundle.min.js"></script>
<link rel="stylesheet" href="public/style/jquery.dataTables.min.css">
<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>
<link rel="stylesheet" href="public/style/all.min.css">
<script src="setting.js"></script>
</head>
<body onload="onload()">
<nav class="navbar navbar-expand-lg navbar-dark bg-dark text-light container4">
<div class="container-fluid">
<a class="navbar-brand p-title" href="index.html">Bird Deterrent System</a>
<!-- Toggler/collapsible Button -->
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<!-- Collapsible Navbar -->
<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>
</li>
<li class="nav-item pad-menu">
<form action="/logout" method="get">
<button class="btn btn-outline-light" type="submit">Logout</button>
</form>
</li>
</ul>
</div>
</div>
</nav>
<!-- SECTION 2 -->
<div class="container-fluid container-setting">
<div class="container">
<div class="card mt-5 card-shadow">
<div class="card-header bg-secondary text-light">
<h5 class="text-md-center">Audio Files</h5>
</div>
<div class="card-body bg-gray">
<form action="/setting/audiofile" method="post">
<!-- PRESET 1-->
<div class="row mt--2">
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<p>Preset 1</p>
</div>
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<select id="preset1" class="form-control class100">
<option>Option 1</option>
<option>Option 2</option>
</select>
</div>
</div>
<!-- PRESET 2-->
<div class="row mt-2">
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<p>Preset 2</p>
</div>
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<select id="preset2" class="form-control class100">
<option>Option 1</option>
<option>Option 2</option>
</select>
</div>
</div>
<!-- PRESET 3-->
<div class="row mt-2">
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<p>Preset 3</p>
</div>
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<select id="preset3" class="form-control class100">
<option>Option 1</option>
<option>Option 2</option>
</select>
</div>
</div>
<!-- PRESET 4-->
<div class="row mt-2">
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<p>Preset 4</p>
</div>
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<select id="preset4" class="form-control class100">
<option>Option 1</option>
<option>Option 2</option>
</select>
</div>
</div>
<!-- PRESET 5-->
<div class="row mt-2">
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<p>Preset 5</p>
</div>
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6">
<select id="preset5" class="form-control class100">
<option>Option 1</option>
<option>Option 2</option>
</select>
</div>
</div>
<div class="row mt-2">
<div class="col-6"></div>
<div class="col-6">
<button id="save_audio" type="submit" class="btn btn-primary 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>
</div>
<div class="card-body bg-gray">
<form action="/setting/uploadaudiofile" method="post" enctype="multipart/form-data">
<div class="row mt-2">
<div class="col-12 col-sm-12 col-md-6 col-lg-6 col-xl-6">
<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">
</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>
</div>
</div>
</form>
</div>
</div>
<div class="card mt-5 card-shadow">
<div class="card-header bg-secondary text-light ">
<h5 class="text-center">Camera</h5>
</div>
<div class="card-body bg-gray">
<form action="/setting/camera" method="post">
<div class="row mt-2">
<div class="col-6">
<p>IP Address</p>
</div>
<div class="col-6">
<input id="setting_ip" class="form-control" title="IP Address">
</div>
</div>
<div class="row mt-2">
<div class="col-6">
<p>Port</p>
</div>
<div class="col-6">
<input id="setting_port" class="form-control" title="Port">
</div>
</div>
<div class="row mt-2">
<div class="col-6">
<p>Username</p>
</div>
<div class="col-6">
<input id="setting_username" class="form-control" title="Username">
</div>
</div>
<div class="row mt-2">
<div class="col-6">
<p>Password</p>
</div>
<div class="col-6">
<div class="input-group">
<input type="password" id="setting_password" class="form-control" title="Password">
<span class="input-group-text" onclick="cameraPassword()">
<i class="fa-solid fa-eye" id="icon_camera"></i>
</span>
</div>
</div>
</div>
<div class="row mt-2">
<div class="col-6"></div>
<div class="col-6">
<button id="save_camera" type="submit" class="btn btn-primary class100" onclick="save_camera()">SAVE</button>
</div>
</div>
</form>
</div>
</div>
<div class="card mt-5 card-shadow">
<div class="card-header bg-secondary text-light">
<h5 class="text-center">Login</h5>
</div>
<div class="card-body bg-gray">
<form action="/setting/weblogin" method="post">
<div class="row mt-2">
<div class="col-6">
<p>Username</p>
</div>
<div class="col-6">
<input id="login_username" class="form-control" title="Username">
</div>
</div>
<div class="row mt-2">
<div class="col-6">
<p>Password</p>
</div>
<div class="col-6">
<div class="input-group">
<input type="password" id="edit_password" class="form-control" title="Password">
<span class="input-group-text" onclick="showPassword()">
<i class="fa-solid fa-eye" id="icon_password"></i>
</span>
</div>
</div>
</div>
<div class="row mt-2">
<div class="col-6">
<p>Confirm Password</p>
</div>
<div class="col-6">
<div class="input-group">
<input type="password" id="confirm_password" class="form-control" title="Confirm Password">
<span class="input-group-text" onclick="showConfirm()">
<i class="fa-solid fa-eye" id="icon_confirm"></i>
</span>
</div>
</div>
</div>
<div class="row mt-2">
<div class="col-6"></div>
<div class="col-6">
<button id="save_login" type="submit" class="btn btn-primary class100" onclick="save_login()">SAVE</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- SECTION 3 -->
<div class="container-fluid footer1 outside">
<p>2024 Copyright &copy; Galva Technologies. All rights reserved.</p>
</div>
</body>
</html>

111
Html/html/setting.js Normal file
View File

@@ -0,0 +1,111 @@
// script for setting.html start here
async function onload(){
const resp = await fetch("/setting");
if (resp.status === 200) {
/**
* @type {Object}
* @property {String[]} AudioFiles
* @property {String} AudioFile1
* @property {String} AudioFile2
* @property {String} AudioFile3
* @property {String} AudioFile4
* @property {String} AudioFile5
* @property {String} CameraIP
* @property {String} CameraPort
* @property {String} CameraUsername
* @property {String} CameraPassword
* @property {String} LoginUsername
* @property {String} LoginPassword
*/
const data = await resp.json();
fill_select(1, data["AudioFiles"]);
fill_select(2, data["AudioFiles"]);
fill_select(3, data["AudioFiles"]);
fill_select(4, data["AudioFiles"]);
fill_select(5, data["AudioFiles"]);
$("#preset1").val(data["AudioFile1"]);
$("#preset2").val(data["AudioFile2"]);
$("#preset3").val(data["AudioFile3"]);
$("#preset4").val(data["AudioFile4"]);
$("#preset5").val(data["AudioFile5"]);
$("#setting_ip").val(data["CameraIP"]);
$("#setting_port").val(data["CameraPort"]);
$("#setting_username").val(data["CameraUsername"]);
$("#setting_password").val(data["CameraPassword"]);
$("#login_username").val(data["LoginUsername"]);
$("#edit_password").val(data["LoginPassword"]);
$("#confirm_password").val(data["LoginPassword"]);
}
}
/**
* Fill select with values
* @param {number} index start from 1
* @param {String[]} values array of values
*/
function fill_select(index, values){
/**
*
* @type {HTMLSelectElement}
*/
let preset = document.getElementById("preset"+index);
preset.innerHTML = "";
if (values!=null && values.length>0){
for (let i = 0; i < values.length; i++) {
const element = values[i];
let option = document.createElement("option");
option.value = element;
option.innerText = element;
preset.appendChild(option);
}
}
}
function cameraPassword() {
const passwordField = document.getElementById('setting_password');
const icon = document.getElementById('icon_camera');
if (passwordField.type === 'password') {
passwordField.type = 'text';
icon.classList.remove('fa-eye');
icon.classList.add('fa-eye-slash');
} else {
passwordField.type = 'password';
icon.classList.remove('fa-eye-slash');
icon.classList.add('fa-eye');
}
}
function showPassword() {
const passwordField = document.getElementById('edit_password');
const icon = document.getElementById('icon_password');
if (passwordField.type === 'password') {
passwordField.type = 'text';
icon.classList.remove('fa-eye');
icon.classList.add('fa-eye-slash');
} else {
passwordField.type = 'password';
icon.classList.remove('fa-eye-slash');
icon.classList.add('fa-eye');
}
}
function showConfirm() {
const passwordField = document.getElementById('confirm_password');
const icon = document.getElementById('icon_confirm');
if (passwordField.type === 'password') {
passwordField.type = 'text';
icon.classList.remove('fa-eye');
icon.classList.add('fa-eye-slash');
} else {
passwordField.type = 'password';
icon.classList.remove('fa-eye-slash');
icon.classList.add('fa-eye');
}
}

24
config.properties Normal file
View File

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

1722
hs_err_pid16944.log Normal file

File diff suppressed because one or more lines are too long

1721
hs_err_pid18892.log Normal file

File diff suppressed because one or more lines are too long

1727
hs_err_pid23232.log Normal file

File diff suppressed because one or more lines are too long

2001
hs_err_pid29656.log Normal file

File diff suppressed because one or more lines are too long

1722
hs_err_pid32272.log Normal file

File diff suppressed because one or more lines are too long

1723
hs_err_pid39288.log Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

BIN
lib/linux-armhf/libbass.so Normal file

Binary file not shown.

BIN
lib/linux-x86-64/libbass.so Normal file

Binary file not shown.

BIN
lib/linux-x86/libbass.so Normal file

Binary file not shown.

BIN
lib/win32-arm64/bass.dll Normal file

Binary file not shown.

BIN
lib/win32-x86-64/bass.dll Normal file

Binary file not shown.

BIN
lib/win32-x86/bass.dll Normal file

Binary file not shown.

63
pom.xml Normal file
View File

@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>id.co.gtc</groupId>
<artifactId>BirdStrikeSoetta</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.10</version>
</dependency>
<dependency>
<groupId>io.javalin</groupId>
<artifactId>javalin</artifactId>
<version> 6.3.0</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.15.0</version>
</dependency>
<dependency>
<groupId>org.tinylog</groupId>
<artifactId>tinylog-impl</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fazecast</groupId>
<artifactId>jSerialComm</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.16</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.2</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

34
simplelogger.properties Normal file
View File

@@ -0,0 +1,34 @@
# SLF4J's SimpleLogger configuration file
# Simple implementation of Logger that sends all enabled log messages, for all defined loggers, to System.err.
# Default logging detail level for all instances of SimpleLogger.
# Must be one of ("trace", "debug", "info", "warn", or "error").
# If not specified, defaults to "info".
org.slf4j.simpleLogger.defaultLogLevel=warn
# Logging detail level for a SimpleLogger instance named "xxxxx".
# Must be one of ("trace", "debug", "info", "warn", or "error").
# If not specified, the default logging detail level is used.
#org.slf4j.simpleLogger.log.xxxxx=
# Set to true if you want the current date and time to be included in output messages.
# Default is false, and will output the number of milliseconds elapsed since startup.
#org.slf4j.simpleLogger.showDateTime=false
# The date and time format to be used in the output messages.
# The pattern describing the date and time format is the same that is used in java.text.SimpleDateFormat.
# If the format is not specified or is invalid, the default format is used.
# The default format is yyyy-MM-dd HH:mm:ss:SSS Z.
#org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS Z
# Set to true if you want to output the current thread name.
# Defaults to true.
#org.slf4j.simpleLogger.showThreadName=true
# Set to true if you want the Logger instance name to be included in output messages.
# Defaults to true.
#org.slf4j.simpleLogger.showLogName=true
# Set to true if you want the last component of the name to be included in output messages.
# Defaults to false.
#org.slf4j.simpleLogger.showShortLogName=false

View File

@@ -0,0 +1,14 @@
package Audio;
public class AudioFileProperties {
public final int handle;
public final String filename;
public long Length;
public double duration;
public AudioFileProperties(int handle, String filename){
this.handle = handle;
this.filename = filename;
}
}

View File

@@ -0,0 +1,196 @@
package Audio;
import org.tinylog.Logger;
import java.io.File;
public class AudioPlayer {
Bass bass;
int deviceid = -1;
boolean inited = false;
int playbackhandle = 0;
float playbackvolume = 1.0f;
public AudioPlayer(){
bass = Bass.Instance;
Logger.info("Bass Version = {}", Integer.toHexString(bass.BASS_GetVersion()));
}
/**
* Unload Bass
*/
public void Unload(){
if (inited){
Logger.info("Freeing Device {}", deviceid);
bass.BASS_SetDevice(deviceid);
bass.BASS_Free();
}
deviceid = -1;
inited = false;
}
/**
* Detect Output Devices
*/
public void DetectOutputDevices(){
Logger.info("Detecting Output Devices...");
int ii = 1;
while (true){
Bass.BASS_DEVICEINFO info = new Bass.BASS_DEVICEINFO();
if (bass.BASS_GetDeviceInfo(ii, info)){
Logger.info("Device {} = {}, flags = {}", ii, info.name, Integer.toHexString(info.flags));
ii++;
} else {
break;
}
}
}
/**
* 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);
if (success){
Logger.info("Device {} opened successfully", device);
deviceid = device;
inited = true;
} else {
Logger.error("Failed to open device {}, Error Code {}", device, bass.BASS_ErrorGetCode());
}
return success;
}
/**
* Set Master Volume
* @param value volume value, 0-100
*/
public void setMasterVolume(int value){
if (value<0) value = 0;
if (value>100) value = 100;
if (inited){
bass.BASS_SetDevice(deviceid);
if (!bass.BASS_SetVolume(value/100.0f)){
Logger.error("Failed to set Master Volume to {}, Error Code {}", value, bass.BASS_ErrorGetCode());
} else Logger.info("Master Volume set to {}", value);
} else Logger.info("AudioPlayer not initialized");
}
/**
* Get Master Volume
* @return 0 - 100, or -1 if failed
*/
@SuppressWarnings("unused")
public int getMasterVolume(){
if (inited){
bass.BASS_SetDevice(deviceid);
float vol = bass.BASS_GetVolume();
if (vol>=0 && vol<=1){
return (int)(vol*100);
}
} else Logger.info("AudioPlayer not initialized");
return -1;
}
public int getPlaybackvolume(){
if (playbackvolume<0)
return 0;
else if (playbackvolume>1.0f)
return 100;
else
return (int)(playbackvolume*100);
}
public void setPlaybackvolume(int value){
if (value<0) value = 0;
if (value>100) value = 100;
playbackvolume = value/100.0f;
if (playbackhandle!=0){
bass.BASS_ChannelSetAttribute(playbackhandle, Bass.BASS_ATTRIB_VOL, playbackvolume);
}
}
private float lastplaybackvolume = 1.0f;
public void Mute(){
lastplaybackvolume = playbackvolume;
setPlaybackvolume(0);
}
public void Unmute(){
setPlaybackvolume((int)lastplaybackvolume*100);
}
/**
* Open Audio File
* @param ff audio file name to open
* @return AudioFileProperties object or null if failed
*/
public AudioFileProperties OpenAudioFile(File ff){
if (inited){
int handle = bass.BASS_StreamCreateFile(false, ff.getAbsolutePath(), 0, 0,0);
if (handle!=0){
Logger.info("Audio file {} opened successfully", ff.getName());
AudioFileProperties prop = new AudioFileProperties(handle, ff.getName());
prop.Length = bass.BASS_ChannelGetLength(handle, Bass.BASS_POS_BYTE);
prop.duration = bass.BASS_ChannelBytes2Seconds(handle, prop.Length);
return prop;
} else Logger.error("Failed to open audio file {}, Error Code {}", ff.getAbsolutePath(), bass.BASS_ErrorGetCode());
} else Logger.info("AudioPlayer not initialized");
return null;
}
/**
* Close Audio File
* @param prop AudioFileProperties object to close
*/
public void CloseAudioFile(AudioFileProperties prop) {
playbackhandle = 0;
if (inited) {
if (prop != null) {
if (!bass.BASS_StreamFree(prop.handle)) {
{
Logger.error("Failed to close audio file {}, Error Code {}", prop.filename, bass.BASS_ErrorGetCode());
}
} else Logger.info("Audio file {} closed successfully", prop.filename);
} else Logger.info("AudioPlayer not initialized");
}
}
public void PlayAudioFile(AudioFileProperties prop, boolean looping, PlaybackEvent event){
if (inited){
if (prop!=null){
if (bass.BASS_ChannelStart(prop.handle)){
playbackhandle = prop.handle;
bass.BASS_ChannelSetAttribute(prop.handle, Bass.BASS_ATTRIB_VOL, playbackvolume);
if (looping) bass.BASS_ChannelFlags(prop.handle, Bass.BASS_SAMPLE_LOOP, Bass.BASS_SAMPLE_LOOP);
if (event!=null) event.onPlaybackStart(prop);
int devfailsync = bass.BASS_ChannelSetSync(prop.handle, Bass.BASS_SYNC_DEV_FAIL,0, (handle, channel, data, user)->{
if (event!=null) event.onPlaybackFailure(prop, "Device Failure");
}, null);
if (devfailsync==0) Logger.error("Failed to set Device Failure Sync, Error Code {}", bass.BASS_ErrorGetCode());
int endsync = bass.BASS_ChannelSetSync(prop.handle, Bass.BASS_SYNC_END, 0, (handle, channel, data, user)->{
if (looping) {
if (event!=null) event.onPlaybackLooped(prop);
} else if (event!=null) event.onPlaybackFinished(prop);
}, null);
if (endsync==0) Logger.error("Failed to set End Sync, Error Code {}", bass.BASS_ErrorGetCode());
} else {
if (event!=null) event.onPlaybackFailure(prop, String.format("Failed to play audio file %s, Error Code %d", prop.filename, bass.BASS_ErrorGetCode()));
}
} else {
if (event!=null) event.onPlaybackFailure(null, "AudioFileProperties is null");
}
} else {
if (event!=null) event.onPlaybackFailure(prop, "AudioPlayer not initialized");
}
}
}

View File

@@ -0,0 +1,790 @@
package Audio;
import com.sun.jna.*;
@SuppressWarnings("unused")
public interface Bass extends Library {
Bass Instance = (Bass) Native.load("bass", Bass.class);
int BASSVERSION = 0x204; // API version
String BASSVERSIONTEXT = "2.4";
// Error codes returned by BASS_ErrorGetCode
int BASS_OK = 0; // all is OK
int BASS_ERROR_MEM = 1; // memory error
int BASS_ERROR_FILEOPEN = 2; // can't open the file
int BASS_ERROR_DRIVER = 3; // can't find a free/valid driver
int BASS_ERROR_BUFLOST = 4; // the sample buffer was lost
int BASS_ERROR_HANDLE = 5; // invalid handle
int BASS_ERROR_FORMAT = 6; // unsupported sample format
int BASS_ERROR_POSITION = 7; // invalid position
int BASS_ERROR_INIT = 8; // BASS_Init has not been successfully called
int BASS_ERROR_START = 9; // BASS_Start has not been successfully called
int BASS_ERROR_SSL = 10; // SSL/HTTPS support isn't available
int BASS_ERROR_REINIT = 11; // device needs to be reinitialized
int BASS_ERROR_ALREADY = 14; // already initialized/paused/whatever
int BASS_ERROR_NOTAUDIO = 17; // file does not contain audio
int BASS_ERROR_NOCHAN = 18; // can't get a free channel
int BASS_ERROR_ILLTYPE = 19; // an illegal type was specified
int BASS_ERROR_ILLPARAM = 20; // an illegal parameter was specified
int BASS_ERROR_NO3D = 21; // no 3D support
int BASS_ERROR_NOEAX = 22; // no EAX support
int BASS_ERROR_DEVICE = 23; // illegal device number
int BASS_ERROR_NOPLAY = 24; // not playing
int BASS_ERROR_FREQ = 25; // illegal sample rate
int BASS_ERROR_NOTFILE = 27; // the stream is not a file stream
int BASS_ERROR_NOHW = 29; // no hardware voices available
int BASS_ERROR_EMPTY = 31; // the file has no sample data
int BASS_ERROR_NONET = 32; // no internet connection could be opened
int BASS_ERROR_CREATE = 33; // couldn't create the file
int BASS_ERROR_NOFX = 34; // effects are not available
int BASS_ERROR_NOTAVAIL = 37; // requested data/action is not available
int BASS_ERROR_DECODE = 38; // the channel is a "decoding channel"
int BASS_ERROR_DX = 39; // a sufficient DirectX version is not installed
int BASS_ERROR_TIMEOUT = 40; // connection timedout
int BASS_ERROR_FILEFORM = 41; // unsupported file format
int BASS_ERROR_SPEAKER = 42; // unavailable speaker
int BASS_ERROR_VERSION = 43; // invalid BASS version (used by add-ons)
int BASS_ERROR_CODEC = 44; // codec is not available/supported
int BASS_ERROR_ENDED = 45; // the channel/file has ended
int BASS_ERROR_BUSY = 46; // the device is busy
int BASS_ERROR_UNSTREAMABLE = 47; // unstreamable file
int BASS_ERROR_PROTOCOL = 48; // unsupported protocol
int BASS_ERROR_DENIED = 49; // access denied
int BASS_ERROR_UNKNOWN = -1; // some other mystery problem
int BASS_ERROR_JAVA_CLASS = 500; // object class problem
// BASS_SetConfig options
int BASS_CONFIG_BUFFER = 0;
int BASS_CONFIG_UPDATEPERIOD = 1;
int BASS_CONFIG_GVOL_SAMPLE = 4;
int BASS_CONFIG_GVOL_STREAM = 5;
int BASS_CONFIG_GVOL_MUSIC = 6;
int BASS_CONFIG_CURVE_VOL = 7;
int BASS_CONFIG_CURVE_PAN = 8;
int BASS_CONFIG_FLOATDSP = 9;
int BASS_CONFIG_3DALGORITHM = 10;
int BASS_CONFIG_NET_TIMEOUT = 11;
int BASS_CONFIG_NET_BUFFER = 12;
int BASS_CONFIG_PAUSE_NOPLAY = 13;
int BASS_CONFIG_NET_PREBUF = 15;
int BASS_CONFIG_NET_PASSIVE = 18;
int BASS_CONFIG_REC_BUFFER = 19;
int BASS_CONFIG_NET_PLAYLIST = 21;
int BASS_CONFIG_MUSIC_VIRTUAL = 22;
int BASS_CONFIG_VERIFY = 23;
int BASS_CONFIG_UPDATETHREADS = 24;
int BASS_CONFIG_DEV_BUFFER = 27;
int BASS_CONFIG_DEV_DEFAULT = 36;
int BASS_CONFIG_NET_READTIMEOUT = 37;
int BASS_CONFIG_HANDLES = 41;
int BASS_CONFIG_SRC = 43;
int BASS_CONFIG_SRC_SAMPLE = 44;
int BASS_CONFIG_ASYNCFILE_BUFFER = 45;
int BASS_CONFIG_OGG_PRESCAN = 47;
int BASS_CONFIG_DEV_NONSTOP = 50;
int BASS_CONFIG_VERIFY_NET = 52;
int BASS_CONFIG_DEV_PERIOD = 53;
int BASS_CONFIG_FLOAT = 54;
int BASS_CONFIG_NET_SEEK = 56;
int BASS_CONFIG_AM_DISABLE = 58;
int BASS_CONFIG_NET_PLAYLIST_DEPTH = 59;
int BASS_CONFIG_NET_PREBUF_WAIT = 60;
int BASS_CONFIG_ANDROID_SESSIONID = 62;
int BASS_CONFIG_ANDROID_AAUDIO = 67;
int BASS_CONFIG_SAMPLE_ONEHANDLE = 69;
int BASS_CONFIG_DEV_TIMEOUT = 70;
int BASS_CONFIG_NET_META = 71;
int BASS_CONFIG_NET_RESTRATE = 72;
int BASS_CONFIG_REC_DEFAULT = 73;
int BASS_CONFIG_NORAMP = 74;
// BASS_SetConfigPtr options
int BASS_CONFIG_NET_AGENT = 16;
int BASS_CONFIG_NET_PROXY = 17;
int BASS_CONFIG_LIBSSL = 64;
int BASS_CONFIG_FILENAME = 75;
int BASS_CONFIG_THREAD = 0x40000000; // flag: thread-specific setting
// BASS_Init flags
int BASS_DEVICE_8BITS = 1; // unused
int BASS_DEVICE_MONO = 2; // mono
int BASS_DEVICE_3D = 4; // unused
int BASS_DEVICE_16BITS = 8; // limit output to 16-bit
int BASS_DEVICE_REINIT = 128; // reinitialize
int BASS_DEVICE_LATENCY = 0x100; // unused
int BASS_DEVICE_SPEAKERS = 0x800; // force enabling of speaker assignment
int BASS_DEVICE_NOSPEAKER = 0x1000; // ignore speaker arrangement
int BASS_DEVICE_DMIX = 0x2000; // use ALSA "dmix" plugin
int BASS_DEVICE_FREQ = 0x4000; // set device sample rate
int BASS_DEVICE_STEREO = 0x8000; // limit output to stereo
int BASS_DEVICE_AUDIOTRACK = 0x20000; // use AudioTrack output
int BASS_DEVICE_DSOUND = 0x40000; // use DirectSound output
int BASS_DEVICE_SOFTWARE = 0x80000; // disable hardware/fastpath output
@Structure.FieldOrder({"name", "driver", "flags"})
class BASS_DEVICEINFO extends Structure {
public String name; // description
public String driver; // driver
public int flags;
}
// BASS_DEVICEINFO flags
int BASS_DEVICE_ENABLED = 1;
int BASS_DEVICE_DEFAULT = 2;
int BASS_DEVICE_INIT = 4;
@Structure.FieldOrder({"flags", "hwsize", "hwfree", "freesam", "free3d", "minrate", "maxrate", "eax", "minbuf", "dsver", "latency", "initflags", "speakers", "freq"})
class BASS_INFO extends Structure{
public int flags; // device capabilities (DSCAPS_xxx flags)
public int hwsize; // unused
public int hwfree; // unused
public int freesam; // unused
public int free3d; // unused
public int minrate; // unused
public int maxrate; // unused
public int eax; // unused
public int minbuf; // recommended minimum buffer length in ms
public int dsver; // DirectSound version
public int latency; // average delay (in ms) before start of playback
public int initflags; // BASS_Init "flags" parameter
public int speakers; // number of speakers available
public int freq; // current output rate
}
// Recording device info structure
@Structure.FieldOrder({"flags", "formats", "inputs", "singlein", "freq"})
class BASS_RECORDINFO extends Structure {
public int flags; // device capabilities (DSCCAPS_xxx flags)
public int formats; // supported standard formats (WAVE_FORMAT_xxx flags)
public int inputs; // number of inputs
public boolean singlein; // TRUE = only 1 input can be set at a time
public int freq; // current input rate
}
// Sample info structure
@Structure.FieldOrder({"freq", "chans", "flags", "length", "max", "origres", "chans", "mingap", "mode3d", "mindist", "maxdist", "iangle", "oangle", "outvol", "vam", "priority"})
class BASS_SAMPLE extends Structure {
public int freq; // default playback rate
public float volume; // default volume (0-1)
public float pan; // default pan (-1=left, 0=middle, 1=right)
public int flags; // BASS_SAMPLE_xxx flags
public int length; // length (in bytes)
public int max; // maximum simultaneous playbacks
public int origres; // original resolution bits
public int chans; // number of channels
public int mingap; // minimum gap (ms) between creating channels
public int mode3d; // BASS_3DMODE_xxx mode
public float mindist; // minimum distance
public float maxdist; // maximum distance
public int iangle; // angle of inside projection cone
public int oangle; // angle of outside projection cone
public float outvol; // delta-volume outside the projection cone
public int vam; // unused
public int priority; // unused
}
int BASS_SAMPLE_8BITS = 1; // 8 bit
int BASS_SAMPLE_FLOAT = 256; // 32-bit floating-point
int BASS_SAMPLE_MONO = 2; // mono
int BASS_SAMPLE_LOOP = 4; // looped
int BASS_SAMPLE_3D = 8; // 3D functionality
int BASS_SAMPLE_SOFTWARE = 16; // unused
int BASS_SAMPLE_MUTEMAX = 32; // mute at max distance (3D only)
int BASS_SAMPLE_VAM = 64; // unused
int BASS_SAMPLE_FX = 128; // unused
int BASS_SAMPLE_OVER_VOL = 0x10000; // override lowest volume
int BASS_SAMPLE_OVER_POS = 0x20000; // override longest playing
int BASS_SAMPLE_OVER_DIST = 0x30000; // override furthest from listener (3D only)
int BASS_STREAM_PRESCAN = 0x20000; // scan file for accurate seeking and length
int BASS_STREAM_AUTOFREE = 0x40000; // automatically free the stream when it stops/ends
int BASS_STREAM_RESTRATE = 0x80000; // restrict the download rate of internet file streams
int BASS_STREAM_BLOCK = 0x100000; // download/play internet file stream in small blocks
int BASS_STREAM_DECODE = 0x200000; // don't play the stream, only decode (BASS_ChannelGetData)
int BASS_STREAM_STATUS = 0x800000; // give server status info (HTTP/ICY tags) in DOWNLOADPROC
int BASS_MP3_IGNOREDELAY = 0x200; // ignore LAME/Xing/VBRI/iTunes delay & padding info
int BASS_MP3_SETPOS = BASS_STREAM_PRESCAN;
int BASS_MUSIC_FLOAT = BASS_SAMPLE_FLOAT;
int BASS_MUSIC_MONO = BASS_SAMPLE_MONO;
int BASS_MUSIC_LOOP = BASS_SAMPLE_LOOP;
int BASS_MUSIC_3D = BASS_SAMPLE_3D;
int BASS_MUSIC_FX = BASS_SAMPLE_FX;
int BASS_MUSIC_AUTOFREE = BASS_STREAM_AUTOFREE;
int BASS_MUSIC_DECODE = BASS_STREAM_DECODE;
int BASS_MUSIC_PRESCAN = BASS_STREAM_PRESCAN; // calculate playback length
int BASS_MUSIC_CALCLEN = BASS_MUSIC_PRESCAN;
int BASS_MUSIC_RAMP = 0x200; // normal ramping
int BASS_MUSIC_RAMPS = 0x400; // sensitive ramping
int BASS_MUSIC_SURROUND = 0x800; // surround sound
int BASS_MUSIC_SURROUND2 = 0x1000; // surround sound (mode 2)
int BASS_MUSIC_FT2PAN = 0x2000; // apply FastTracker 2 panning to XM files
int BASS_MUSIC_FT2MOD = 0x2000; // play .MOD as FastTracker 2 does
int BASS_MUSIC_PT1MOD = 0x4000; // play .MOD as ProTracker 1 does
int BASS_MUSIC_NONINTER = 0x10000; // non-interpolated sample mixing
int BASS_MUSIC_SINCINTER = 0x800000; // sinc interpolated sample mixing
int BASS_MUSIC_POSRESET = 0x8000; // stop all notes when moving position
int BASS_MUSIC_POSRESETEX = 0x400000; // stop all notes and reset bmp/etc when moving position
int BASS_MUSIC_STOPBACK = 0x80000; // stop the music on a backwards jump effect
int BASS_MUSIC_NOSAMPLE = 0x100000; // don't load the samples
// Speaker assignment flags
int BASS_SPEAKER_FRONT = 0x1000000; // front speakers
int BASS_SPEAKER_REAR = 0x2000000; // rear speakers
int BASS_SPEAKER_CENLFE = 0x3000000; // center & LFE speakers (5.1)
int BASS_SPEAKER_SIDE = 0x4000000; // side speakers (7.1)
static int BASS_SPEAKER_N(int n) { return n<<24; } // n'th pair of speakers (max 15)
int BASS_SPEAKER_LEFT = 0x10000000; // modifier: left
int BASS_SPEAKER_RIGHT = 0x20000000; // modifier: right
int BASS_SPEAKER_FRONTLEFT = BASS_SPEAKER_FRONT | BASS_SPEAKER_LEFT;
int BASS_SPEAKER_FRONTRIGHT = BASS_SPEAKER_FRONT | BASS_SPEAKER_RIGHT;
int BASS_SPEAKER_REARLEFT = BASS_SPEAKER_REAR | BASS_SPEAKER_LEFT;
int BASS_SPEAKER_REARRIGHT = BASS_SPEAKER_REAR | BASS_SPEAKER_RIGHT;
int BASS_SPEAKER_CENTER = BASS_SPEAKER_CENLFE | BASS_SPEAKER_LEFT;
int BASS_SPEAKER_LFE = BASS_SPEAKER_CENLFE | BASS_SPEAKER_RIGHT;
int BASS_SPEAKER_SIDELEFT = BASS_SPEAKER_SIDE | BASS_SPEAKER_LEFT;
int BASS_SPEAKER_SIDERIGHT = BASS_SPEAKER_SIDE | BASS_SPEAKER_RIGHT;
int BASS_SPEAKER_REAR2 = BASS_SPEAKER_SIDE;
int BASS_SPEAKER_REAR2LEFT = BASS_SPEAKER_SIDELEFT;
int BASS_SPEAKER_REAR2RIGHT = BASS_SPEAKER_SIDERIGHT;
int BASS_ASYNCFILE = 0x40000000; // read file asynchronously
int BASS_RECORD_PAUSE = 0x8000; // start recording paused
// Channel info structure
@Structure.FieldOrder({"freq", "chans", "flags", "ctype", "origres", "plugin", "sample", "filename"})
class BASS_CHANNELINFO extends Structure {
public int freq; // default playback rate
public int chans; // channels
public int flags;
public int ctype; // type of channel
public int origres; // original resolution
public int plugin;
public int sample;
public String filename;
}
int BASS_ORIGRES_FLOAT = 0x10000;
// BASS_CHANNELINFO types
int BASS_CTYPE_SAMPLE = 1;
int BASS_CTYPE_RECORD = 2;
int BASS_CTYPE_STREAM = 0x10000;
int BASS_CTYPE_STREAM_VORBIS = 0x10002;
int BASS_CTYPE_STREAM_OGG = 0x10002;
int BASS_CTYPE_STREAM_MP1 = 0x10003;
int BASS_CTYPE_STREAM_MP2 = 0x10004;
int BASS_CTYPE_STREAM_MP3 = 0x10005;
int BASS_CTYPE_STREAM_AIFF = 0x10006;
int BASS_CTYPE_STREAM_CA = 0x10007;
int BASS_CTYPE_STREAM_MF = 0x10008;
int BASS_CTYPE_STREAM_AM = 0x10009;
int BASS_CTYPE_STREAM_SAMPLE = 0x1000a;
int BASS_CTYPE_STREAM_DUMMY = 0x18000;
int BASS_CTYPE_STREAM_DEVICE = 0x18001;
int BASS_CTYPE_STREAM_WAV = 0x40000; // WAVE flag (LOWORD=codec)
int BASS_CTYPE_STREAM_WAV_PCM = 0x50001;
int BASS_CTYPE_STREAM_WAV_FLOAT = 0x50003;
int BASS_CTYPE_MUSIC_MOD = 0x20000;
int BASS_CTYPE_MUSIC_MTM = 0x20001;
int BASS_CTYPE_MUSIC_S3M = 0x20002;
int BASS_CTYPE_MUSIC_XM = 0x20003;
int BASS_CTYPE_MUSIC_IT = 0x20004;
int BASS_CTYPE_MUSIC_MO3 = 0x00100; // MO3 flag
@Structure.FieldOrder({"ctype", "name", "exts"})
class BASS_PLUGINFORM extends Structure {
int ctype; // channel type
String name; // format description
String exts; // file extension filter (*.ext1;*.ext2;etc...)
}
@Structure.FieldOrder({"version", "formatc", "formats"})
class BASS_PLUGININFO extends Structure {
int version; // version (same form as BASS_GetVersion)
int formatc; // number of formats
BASS_PLUGINFORM[] formats; // the array of formats
}
// 3D vector (for 3D positions/velocities/orientations)
class BASS_3DVECTOR {
BASS_3DVECTOR() {}
BASS_3DVECTOR(float _x, float _y, float _z) { x=_x; y=_y; z=_z; }
float x; // +=right, -=left
float y; // +=up, -=down
float z; // +=front, -=behind
}
// 3D channel modes
int BASS_3DMODE_NORMAL = 0; // normal 3D processing
int BASS_3DMODE_RELATIVE = 1; // position is relative to the listener
int BASS_3DMODE_OFF = 2; // no 3D processing
// software 3D mixing algorithms (used with BASS_CONFIG_3DALGORITHM)
int BASS_3DALG_DEFAULT = 0;
int BASS_3DALG_OFF = 1;
int BASS_3DALG_FULL = 2;
int BASS_3DALG_LIGHT = 3;
// BASS_SampleGetChannel flags
int BASS_SAMCHAN_NEW = 1; // get a new playback channel
int BASS_SAMCHAN_STREAM = 2; // create a stream
interface STREAMPROC extends Callback
{
int STREAMPROC(int handle, Pointer buffer, int length, Pointer user);
/* User stream callback function.
handle : The stream that needs writing
buffer : Buffer to write the samples in
length : Number of bytes to write
user : The 'user' parameter value given when calling BASS_StreamCreate
RETURN : Number of bytes written. Set the BASS_STREAMPROC_END flag to end
the stream. */
}
int BASS_STREAMPROC_END = 0x80000000; // end of user stream flag
// Special STREAMPROCs
int STREAMPROC_DUMMY = 0; // "dummy" stream
int STREAMPROC_PUSH = -1; // push stream
int STREAMPROC_DEVICE = -2; // device mix stream
int STREAMPROC_DEVICE_3D = -3; // device 3D mix stream
// BASS_StreamCreateFileUser file systems
int STREAMFILE_NOBUFFER = 0;
int STREAMFILE_BUFFER = 1;
int STREAMFILE_BUFFERPUSH = 2;
interface BASS_FILEPROCS extends Callback
{
// User file stream callback functions
void FILECLOSEPROC(Pointer user);
long FILELENPROC(Pointer user) ;
int FILEREADPROC(Pointer buffer, int length, Pointer user);
boolean FILESEEKPROC(long offset, Pointer user);
}
// BASS_StreamPutFileData options
int BASS_FILEDATA_END = 0; // end & close the file
// BASS_StreamGetFilePosition modes
int BASS_FILEPOS_CURRENT = 0;
int BASS_FILEPOS_DECODE = BASS_FILEPOS_CURRENT;
int BASS_FILEPOS_DOWNLOAD = 1;
int BASS_FILEPOS_END = 2;
int BASS_FILEPOS_START = 3;
int BASS_FILEPOS_CONNECTED = 4;
int BASS_FILEPOS_BUFFER = 5;
int BASS_FILEPOS_SOCKET = 6;
int BASS_FILEPOS_ASYNCBUF = 7;
int BASS_FILEPOS_SIZE = 8;
int BASS_FILEPOS_BUFFERING = 9;
int BASS_FILEPOS_AVAILABLE = 10;
interface DOWNLOADPROC extends Callback
{
void DOWNLOADPROC(Pointer buffer, int length, Pointer user);
/* Internet stream download callback function.
buffer : Buffer containing the downloaded data... NULL=end of download
length : Number of bytes in the buffer
user : The 'user' parameter value given when calling BASS_StreamCreateURL */
}
// BASS_ChannelSetSync types
int BASS_SYNC_POS = 0;
int BASS_SYNC_END = 2;
int BASS_SYNC_META = 4;
int BASS_SYNC_SLIDE = 5;
int BASS_SYNC_STALL = 6;
int BASS_SYNC_DOWNLOAD = 7;
int BASS_SYNC_FREE = 8;
int BASS_SYNC_SETPOS = 11;
int BASS_SYNC_MUSICPOS = 10;
int BASS_SYNC_MUSICINST = 1;
int BASS_SYNC_MUSICFX = 3;
int BASS_SYNC_OGG_CHANGE = 12;
int BASS_SYNC_DEV_FAIL = 14;
int BASS_SYNC_DEV_FORMAT = 15;
int BASS_SYNC_THREAD = 0x20000000; // flag: call sync in other thread
int BASS_SYNC_MIXTIME = 0x40000000; // flag: sync at mixtime, else at playtime
int BASS_SYNC_ONETIME = 0x80000000; // flag: sync only once, else continuously
interface SYNCPROC extends Callback
{
void SYNCPROC(int handle, int channel, int data, Pointer user);
/* Sync callback function.
handle : The sync that has occured
channel: Channel that the sync occured in
data : Additional data associated with the sync's occurance
user : The 'user' parameter given when calling BASS_ChannelSetSync */
}
interface DSPPROC extends Callback
{
void DSPPROC(int handle, int channel, Pointer buffer, int length, Pointer user);
/* DSP callback function.
handle : The DSP handle
channel: Channel that the DSP is being applied to
buffer : Buffer to apply the DSP to
length : Number of bytes in the buffer
user : The 'user' parameter given when calling BASS_ChannelSetDSP */
}
interface RECORDPROC extends Callback
{
boolean RECORDPROC(int handle, Pointer buffer, int length, Pointer user);
/* Recording callback function.
handle : The recording handle
buffer : Buffer containing the recorded sample data
length : Number of bytes
user : The 'user' parameter value given when calling BASS_RecordStart
RETURN : true = continue recording, false = stop */
}
// BASS_ChannelIsActive return values
int BASS_ACTIVE_STOPPED = 0;
int BASS_ACTIVE_PLAYING =1;
int BASS_ACTIVE_STALLED = 2;
int BASS_ACTIVE_PAUSED = 3;
int BASS_ACTIVE_PAUSED_DEVICE = 4;
// Channel attributes
int BASS_ATTRIB_FREQ = 1;
int BASS_ATTRIB_VOL = 2;
int BASS_ATTRIB_PAN = 3;
int BASS_ATTRIB_EAXMIX = 4;
int BASS_ATTRIB_NOBUFFER = 5;
int BASS_ATTRIB_VBR = 6;
int BASS_ATTRIB_CPU = 7;
int BASS_ATTRIB_SRC = 8;
int BASS_ATTRIB_NET_RESUME = 9;
int BASS_ATTRIB_SCANINFO = 10;
int BASS_ATTRIB_NORAMP = 11;
int BASS_ATTRIB_BITRATE = 12;
int BASS_ATTRIB_BUFFER = 13;
int BASS_ATTRIB_GRANULE = 14;
int BASS_ATTRIB_USER = 15;
int BASS_ATTRIB_TAIL = 16;
int BASS_ATTRIB_PUSH_LIMIT = 17;
int BASS_ATTRIB_DOWNLOADPROC = 18;
int BASS_ATTRIB_VOLDSP = 19;
int BASS_ATTRIB_VOLDSP_PRIORITY = 20;
int BASS_ATTRIB_MUSIC_AMPLIFY = 0x100;
int BASS_ATTRIB_MUSIC_PANSEP = 0x101;
int BASS_ATTRIB_MUSIC_PSCALER = 0x102;
int BASS_ATTRIB_MUSIC_BPM = 0x103;
int BASS_ATTRIB_MUSIC_SPEED = 0x104;
int BASS_ATTRIB_MUSIC_VOL_GLOBAL = 0x105;
int BASS_ATTRIB_MUSIC_VOL_CHAN = 0x200; // + channel #
int BASS_ATTRIB_MUSIC_VOL_INST = 0x300; // + instrument #
// BASS_ChannelSlideAttribute flags
int BASS_SLIDE_LOG = 0x1000000;
// BASS_ChannelGetData flags
int BASS_DATA_AVAILABLE = 0; // query how much data is buffered
int BASS_DATA_NOREMOVE = 0x10000000; // flag: don't remove data from recording buffer
int BASS_DATA_FIXED = 0x20000000; // unused
int BASS_DATA_FLOAT = 0x40000000; // flag: return floating-point sample data
int BASS_DATA_FFT256 = 0x80000000; // 256 sample FFT
int BASS_DATA_FFT512 = 0x80000001; // 512 FFT
int BASS_DATA_FFT1024 = 0x80000002; // 1024 FFT
int BASS_DATA_FFT2048 = 0x80000003; // 2048 FFT
int BASS_DATA_FFT4096 = 0x80000004; // 4096 FFT
int BASS_DATA_FFT8192 = 0x80000005; // 8192 FFT
int BASS_DATA_FFT16384 = 0x80000006; // 16384 FFT
int BASS_DATA_FFT32768 = 0x80000007; // 32768 FFT
int BASS_DATA_FFT_INDIVIDUAL = 0x10; // FFT flag: FFT for each channel, else all combined
int BASS_DATA_FFT_NOWINDOW = 0x20; // FFT flag: no Hanning window
int BASS_DATA_FFT_REMOVEDC = 0x40; // FFT flag: pre-remove DC bias
int BASS_DATA_FFT_COMPLEX = 0x80; // FFT flag: return complex data
int BASS_DATA_FFT_NYQUIST = 0x100; // FFT flag: return extra Nyquist value
// BASS_ChannelGetLevelEx flags
int BASS_LEVEL_MONO = 1; // get mono level
int BASS_LEVEL_STEREO = 2; // get stereo level
int BASS_LEVEL_RMS = 4; // get RMS levels
int BASS_LEVEL_VOLPAN = 8; // apply VOL/PAN attributes to the levels
int BASS_LEVEL_NOREMOVE = 16; // don't remove data from recording buffer
// BASS_ChannelGetTags types : what's returned
int BASS_TAG_ID3 = 0; // ID3v1 tags : TAG_ID3
int BASS_TAG_ID3V2 = 1; // ID3v2 tags : ByteBuffer
int BASS_TAG_OGG = 2; // OGG comments : String array
int BASS_TAG_HTTP = 3; // HTTP headers : String array
int BASS_TAG_ICY = 4; // ICY headers : String array
int BASS_TAG_META = 5; // ICY metadata : String
int BASS_TAG_APE = 6; // APE tags : String array
int BASS_TAG_MP4 = 7; // MP4/iTunes metadata : String array
int BASS_TAG_VENDOR = 9; // OGG encoder : String
int BASS_TAG_LYRICS3 = 10; // Lyric3v2 tag : String
int BASS_TAG_WAVEFORMAT = 14; // WAVE format : ByteBuffer containing WAVEFORMATEEX structure
int BASS_TAG_AM_NAME = 16; // Android Media codec name : String
int BASS_TAG_ID3V2_2 = 17; // ID3v2 tags (2nd block) : ByteBuffer
int BASS_TAG_AM_MIME = 18; // Android Media MIME type : String
int BASS_TAG_LOCATION = 19; // redirected URL : String
int BASS_TAG_RIFF_INFO = 0x100; // RIFF "INFO" tags : String array
int BASS_TAG_RIFF_BEXT = 0x101; // RIFF/BWF "bext" tags : TAG_BEXT
int BASS_TAG_RIFF_CART = 0x102; // RIFF/BWF "cart" tags : TAG_CART
int BASS_TAG_RIFF_DISP = 0x103; // RIFF "DISP" text tag : String
int BASS_TAG_RIFF_CUE = 0x104; // RIFF "cue " chunk : TAG_CUE structure
int BASS_TAG_RIFF_SMPL = 0x105; // RIFF "smpl" chunk : TAG_SMPL structure
int BASS_TAG_APE_BINARY = 0x1000; // + index #, binary APE tag : TAG_APE_BINARY
int BASS_TAG_MUSIC_NAME = 0x10000; // MOD music name : String
int BASS_TAG_MUSIC_MESSAGE = 0x10001; // MOD message : String
int BASS_TAG_MUSIC_ORDERS = 0x10002; // MOD order list : ByteBuffer
int BASS_TAG_MUSIC_AUTH = 0x10003; // MOD author : UTF-8 string
int BASS_TAG_MUSIC_INST = 0x10100; // + instrument #, MOD instrument name : String
int BASS_TAG_MUSIC_CHAN = 0x10200; // + channel #, MOD channel name : String
int BASS_TAG_MUSIC_SAMPLE = 0x10300; // + sample #, MOD sample name : String
int BASS_TAG_BYTEBUFFER = 0x10000000; // flag: return a ByteBuffer instead of a String or TAG_ID3
// ID3v1 tag structure
@Structure.FieldOrder({"id", "title", "artist", "album", "year", "comment", "genre", "track"})
class TAG_ID3 extends Structure {
String id;
String title;
String artist;
String album;
String year;
String comment;
byte genre;
byte track;
}
// Binary APE tag structure
@Structure.FieldOrder({"key", "data", "length"})
class TAG_APE_BINARY extends Structure {
String key;
Pointer data;
int length;
}
// BASS_ChannelGetLength/GetPosition/SetPosition modes
int BASS_POS_BYTE = 0; // byte position
int BASS_POS_MUSIC_ORDER = 1; // order.row position, MAKELONG(order,row)
int BASS_POS_OGG = 3; // OGG bitstream number
int BASS_POS_END = 0x10; // trimmed end position
int BASS_POS_LOOP = 0x11; // loop start positiom
int BASS_POS_FLUSH = 0x1000000; // flag: flush decoder/FX buffers
int BASS_POS_RESET = 0x2000000; // flag: reset user file buffers
int BASS_POS_RELATIVE = 0x4000000; // flag: seek relative to the current position
int BASS_POS_INEXACT = 0x8000000; // flag: allow seeking to inexact position
int BASS_POS_DECODE = 0x10000000; // flag: get the decoding (not playing) position
int BASS_POS_DECODETO = 0x20000000; // flag: decode to the position instead of seeking
int BASS_POS_SCAN = 0x40000000; // flag: scan to the position
// BASS_ChannelSetDevice/GetDevice option
int BASS_NODEVICE = 0x20000;
// DX8 effect types, use with BASS_ChannelSetFX
int BASS_FX_DX8_CHORUS = 0;
int BASS_FX_DX8_COMPRESSOR = 1;
int BASS_FX_DX8_DISTORTION = 2;
int BASS_FX_DX8_ECHO = 3;
int BASS_FX_DX8_FLANGER = 4;
int BASS_FX_DX8_GARGLE = 5;
int BASS_FX_DX8_I3DL2REVERB = 6;
int BASS_FX_DX8_PARAMEQ = 7;
int BASS_FX_DX8_REVERB = 8;
int BASS_FX_VOLUME = 9;
@Structure.FieldOrder({"fWetDryMix", "fDepth", "fFeedback", "fFrequency", "lWaveform", "fDelay", "lPhase"})
class BASS_DX8_CHORUS extends Structure {
float fWetDryMix;
float fDepth;
float fFeedback;
float fFrequency;
int lWaveform; // 0=triangle, 1=sine
float fDelay;
int lPhase; // BASS_DX8_PHASE_xxx
}
@Structure.FieldOrder({"fGain","fEdge","fPostEQCenterFrequency","fPostEQBandwidth","fPreLowpassCutoff"})
class BASS_DX8_DISTORTION extends Structure {
float fGain;
float fEdge;
float fPostEQCenterFrequency;
float fPostEQBandwidth;
float fPreLowpassCutoff;
}
@Structure.FieldOrder({"fWetDryMix","fFeedback","fLeftDelay","fRightDelay","lPanDelay"})
class BASS_DX8_ECHO extends Structure {
float fWetDryMix;
float fFeedback;
float fLeftDelay;
float fRightDelay;
boolean lPanDelay;
}
@Structure.FieldOrder({"fWetDryMix","fDepth","fFeedback","fFrequency","lWaveform","fDelay","lPhase"})
class BASS_DX8_FLANGER extends Structure {
float fWetDryMix;
float fDepth;
float fFeedback;
float fFrequency;
int lWaveform; // 0=triangle, 1=sine
float fDelay;
int lPhase; // BASS_DX8_PHASE_xxx
}
@Structure.FieldOrder({"fCenter","fBandwidth","fGain"})
class BASS_DX8_PARAMEQ extends Structure {
float fCenter;
float fBandwidth;
float fGain;
}
@Structure.FieldOrder({"fInGain","fReverbMix","fReverbTime","fHighFreqRTRatio"})
class BASS_DX8_REVERB extends Structure {
float fInGain;
float fReverbMix;
float fReverbTime;
float fHighFreqRTRatio;
}
int BASS_DX8_PHASE_NEG_180 = 0;
int BASS_DX8_PHASE_NEG_90 = 1;
int BASS_DX8_PHASE_ZERO = 2;
int BASS_DX8_PHASE_90 = 3;
int BASS_DX8_PHASE_180 = 4;
@Structure.FieldOrder({"fTarget","fCurrent","fTime","lCurve"})
class BASS_FX_VOLUME_PARAM extends Structure {
float fTarget;
float fCurrent;
float fTime;
int lCurve;
}
class FloatValue {
public float value;
}
boolean BASS_SetConfig(int option, int value);
int BASS_GetConfig(int option);
boolean BASS_SetConfigPtr(int option, Pointer value);
Object BASS_GetConfigPtr(int option);
int BASS_GetVersion();
int BASS_ErrorGetCode();
boolean BASS_GetDeviceInfo(int device, BASS_DEVICEINFO info);
boolean BASS_Init(int device, int freq, int flags);
boolean BASS_Free();
boolean BASS_SetDevice(int device);
int BASS_GetDevice();
boolean BASS_GetInfo(BASS_INFO info);
boolean BASS_Start();
boolean BASS_Stop();
boolean BASS_Pause();
int BASS_IsStarted();
boolean BASS_Update(int length);
float BASS_GetCPU();
boolean BASS_SetVolume(float volume);
float BASS_GetVolume();
boolean BASS_Set3DFactors(float distf, float rollf, float doppf);
boolean BASS_Get3DFactors(FloatValue distf, FloatValue rollf, FloatValue doppf);
boolean BASS_Set3DPosition(BASS_3DVECTOR pos, BASS_3DVECTOR vel, BASS_3DVECTOR front, BASS_3DVECTOR top);
boolean BASS_Get3DPosition(BASS_3DVECTOR pos, BASS_3DVECTOR vel, BASS_3DVECTOR front, BASS_3DVECTOR top);
void BASS_Apply3D();
int BASS_PluginLoad(String file, int flags);
boolean BASS_PluginFree(int handle);
boolean BASS_PluginEnable(int handle, boolean enable);
BASS_PLUGININFO BASS_PluginGetInfo(int handle);
int BASS_SampleLoad(String file, long offset, int length, int max, int flags);
int BASS_SampleLoad(Pointer file, long offset, int length, int max, int flags);
int BASS_SampleCreate(int length, int freq, int chans, int max, int flags);
boolean BASS_SampleFree(int handle);
boolean BASS_SampleSetData(int handle, Pointer buffer);
boolean BASS_SampleGetData(int handle, Pointer buffer);
boolean BASS_SampleGetInfo(int handle, BASS_SAMPLE info);
boolean BASS_SampleSetInfo(int handle, BASS_SAMPLE info);
int BASS_SampleGetChannel(int handle, boolean onlynew);
int BASS_SampleGetChannels(int handle, int[] channels);
boolean BASS_SampleStop(int handle);
int BASS_StreamCreate(int freq, int chans, int flags, STREAMPROC proc, Pointer user);
int BASS_StreamCreateFile(boolean mem, String file, long offset, long length, int flags);
int BASS_StreamCreateFile(Pointer file, long offset, long length, int flags);
int BASS_StreamCreateURL(String url, int offset, int flags, DOWNLOADPROC proc, Pointer user);
int BASS_StreamCreateFileUser(int system, int flags, BASS_FILEPROCS procs, Pointer user);
boolean BASS_StreamFree(int handle);
long BASS_StreamGetFilePosition(int handle, int mode);
int BASS_StreamPutData(int handle, Pointer buffer, int length);
int BASS_StreamPutFileData(int handle, Pointer buffer, int length);
int BASS_MusicLoad(String file, long offset, int length, int flags, int freq);
int BASS_MusicLoad(Pointer file, long offset, int length, int flags, int freq);
boolean BASS_MusicFree(int handle);
boolean BASS_RecordGetDeviceInfo(int device, BASS_DEVICEINFO info);
boolean BASS_RecordInit(int device);
boolean BASS_RecordFree();
boolean BASS_RecordSetDevice(int device);
int BASS_RecordGetDevice();
boolean BASS_RecordGetInfo(BASS_RECORDINFO info);
String BASS_RecordGetInputName(int input);
boolean BASS_RecordSetInput(int input, int flags, float volume);
int BASS_RecordGetInput(int input, FloatValue volume);
int BASS_RecordStart(int freq, int chans, int flags, RECORDPROC proc, Pointer user);
double BASS_ChannelBytes2Seconds(int handle, long pos);
long BASS_ChannelSeconds2Bytes(int handle, double pos);
int BASS_ChannelGetDevice(int handle);
boolean BASS_ChannelSetDevice(int handle, int device);
int BASS_ChannelIsActive(int handle);
boolean BASS_ChannelGetInfo(int handle, BASS_CHANNELINFO info);
Object BASS_ChannelGetTags(int handle, int tags);
long BASS_ChannelFlags(int handle, int flags, int mask);
boolean BASS_ChannelLock(int handle, boolean lock);
boolean BASS_ChannelFree(int handle);
boolean BASS_ChannelPlay(int handle, boolean restart);
boolean BASS_ChannelStart(int handle);
boolean BASS_ChannelStop(int handle);
boolean BASS_ChannelPause(int handle);
boolean BASS_ChannelUpdate(int handle, int length);
boolean BASS_ChannelSetAttribute(int handle, int attrib, float value);
boolean BASS_ChannelGetAttribute(int handle, int attrib, FloatValue value);
boolean BASS_ChannelSlideAttribute(int handle, int attrib, float value, int time);
boolean BASS_ChannelIsSliding(int handle, int attrib);
boolean BASS_ChannelSetAttributeEx(int handle, int attrib, Pointer value, int size);
boolean BASS_ChannelSetAttributeDOWNLOADPROC(int handle, DOWNLOADPROC proc, Pointer user);
int BASS_ChannelGetAttributeEx(int handle, int attrib, Pointer value, int size);
boolean BASS_ChannelSet3DAttributes(int handle, int mode, float min, float max, int iangle, int oangle, float outvol);
boolean BASS_ChannelGet3DAttributes(int handle, Integer mode, FloatValue min, FloatValue max, Integer iangle, Integer oangle, FloatValue outvol);
boolean BASS_ChannelSet3DPosition(int handle, BASS_3DVECTOR pos, BASS_3DVECTOR orient, BASS_3DVECTOR vel);
boolean BASS_ChannelGet3DPosition(int handle, BASS_3DVECTOR pos, BASS_3DVECTOR orient, BASS_3DVECTOR vel);
long BASS_ChannelGetLength(int handle, int mode);
boolean BASS_ChannelSetPosition(int handle, long pos, int mode);
long BASS_ChannelGetPosition(int handle, int mode);
int BASS_ChannelGetLevel(int handle);
boolean BASS_ChannelGetLevelEx(int handle, float[] levels, float length, int flags);
int BASS_ChannelGetData(int handle, Pointer buffer, int length);
int BASS_ChannelSetSync(int handle, int type, long param, SYNCPROC proc, Pointer user);
boolean BASS_ChannelRemoveSync(int handle, int sync);
boolean BASS_ChannelSetLink(int handle, int chan);
boolean BASS_ChannelRemoveLink(int handle, int chan);
int BASS_ChannelSetDSP(int handle, DSPPROC proc, Pointer user, int priority);
boolean BASS_ChannelRemoveDSP(int handle, int dsp);
int BASS_ChannelSetFX(int handle, int type, int priority);
boolean BASS_ChannelRemoveFX(int handle, int fx);
boolean BASS_FXSetParameters(int handle, Object params);
boolean BASS_FXGetParameters(int handle, Object params);
boolean BASS_FXSetPriority(int handle, int priority);
boolean BASS_FXReset(int handle);
// gak bisa
int BASS_StreamCreate(int freq, int chans, int flags, int proc, Pointer user);
}

View File

@@ -0,0 +1,8 @@
package Audio;
public interface PlaybackEvent {
void onPlaybackStart(AudioFileProperties prop);
void onPlaybackFinished(AudioFileProperties prop);
void onPlaybackFailure(AudioFileProperties prop, String reason);
void onPlaybackLooped(AudioFileProperties prop);
}

View File

@@ -0,0 +1,122 @@
package Camera;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import Other.SomeCodes;
import lombok.Getter;
import lombok.Setter;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.opencv.opencv_core.Mat;
public class GrabbingTask implements Runnable {
@Setter private Consumer<String> onMessageUpdate;
@Setter private Consumer<Mat> onMatUpdate;
@Setter private Consumer<Frame> onFrameUpdate;
@Setter private Consumer<String> onBase64Update;
@Setter private Consumer<String> onStreamingStatusUpdate;
private final AtomicBoolean isGrabbing;
private final FrameGrabber grabber;
@Getter private final int lowquality_width = 640;
@Getter private final int lowquality_height = 360;
@Getter @Setter private boolean HQ = false;
@Getter private int streaming_width = 0;
@Getter private int streaming_height = 0;
@Getter private int streaming_fps = 0;
AtomicBoolean streamingstatuschanged = new AtomicBoolean(false);
private void updateMessage(String message) {
if (onMessageUpdate != null) {
onMessageUpdate.accept(message);
}
}
private void updateMat(Mat value) {
if (onMatUpdate != null) {
onMatUpdate.accept(value);
}
}
private void updateBase64(String base64) {
if (onBase64Update != null) {
onBase64Update.accept(base64);
}
}
private void updateFrame(Frame frame) {
if (onFrameUpdate != null) {
onFrameUpdate.accept(frame);
}
}
private void updateStreamingStatus(String status) {
if (onStreamingStatusUpdate != null) {
onStreamingStatusUpdate.accept(status);
}
}
public GrabbingTask(AtomicBoolean isGrabbing, FrameGrabber grabber) {
this.isGrabbing = isGrabbing;
this.grabber = grabber;
}
public String GetStreamingStatus(){
return "Streaming at " + streaming_width + "x" + streaming_height + " " + streaming_fps + "fps";
}
@Override
public void run() {
isGrabbing.set(true);
AtomicInteger framecount = new AtomicInteger(0);
TimerTask task = new TimerTask() {
@Override
public void run() {
if (streaming_fps != framecount.get()) {
streaming_fps = framecount.get();
streamingstatuschanged.set(true);
}
framecount.set(0);
if (streamingstatuschanged.get()) {
updateStreamingStatus(GetStreamingStatus());
streamingstatuschanged.set(false);
}
}
};
Timer timer = new Timer();
timer.scheduleAtFixedRate(task, 1000, 1000);
while (isGrabbing.get()) {
try {
//Thread.sleep(100); // 10 fps
Frame fr =grabber.grab();
if (fr!=null){
if (!HQ) fr = SomeCodes.ResizeFrame(fr, lowquality_width, lowquality_height);
updateFrame(fr);
updateBase64(SomeCodes.BufferedImageToBase64(SomeCodes.FrameToBufferedImage(fr)));
Mat mat = SomeCodes.matConverter.convert(fr);
updateMat(mat);
if (streaming_width != fr.imageWidth) {
streaming_width = fr.imageWidth;
streamingstatuschanged.set(true);
}
if (streaming_height != fr.imageHeight) {
streaming_height = fr.imageHeight;
streamingstatuschanged.set(true);
}
framecount.incrementAndGet();
} else updateMessage("Grabber returned null frame");
} catch (Exception e) {
updateMessage("Error grabbing frame: " + e.getMessage());
}
}
timer.cancel();
}
}

View File

@@ -0,0 +1,117 @@
package Camera;
import com.fazecast.jSerialComm.SerialPort;
import org.tinylog.Logger;
/**
* Pan Tilt Controller
* Using PelcoD protocol
* Source : https://www.commfront.com/pages/pelco-d-protocol-tutorial
*/
public class PanTiltController {
private final SerialPort serialPort;
private final byte cameraid;
/**
* Open Pan Tilt Controller
* @param portname serial port name used
* @param baudrate baudrate used
*/
public PanTiltController(String portname, int baudrate, int cameraid){
serialPort = SerialPort.getCommPort(portname);
serialPort.setBaudRate(baudrate);
this.cameraid = (byte)cameraid;
if (serialPort.openPort()){
Logger.info("Serial Port {} opened successfully at {}", portname, baudrate);
} else {
Logger.info("Failed to open Serial Port {} at {}", portname, baudrate);
}
}
/**
* Close Pan Tilt Controller
*/
public void Close(){
serialPort.closePort();
Logger.info("Serial Port closed");
}
/**
* Stop Pan Tilt Movement
*/
public void StopMovement(){
byte[] command = new byte[]{0, cameraid, 0, 0, 0, 0, 0};
command[6] = Checksum(command); // add checksum
command[0] = (byte) 0xFF; // add synchronization byte
serialPort.writeBytes(command, command.length);
}
/**
* Pan Left
* @param speed speed of movement, 0-63
*/
public void PanLeft(byte speed){
if (speed<0) speed = 0;
if (speed>0x3F) speed = 0x3F;
byte[] command = new byte[]{0, cameraid, 0, 4, speed, 0, 0};
command[6] = Checksum(command); // add checksum
command[0] = (byte) 0xFF; // add synchronization byte
serialPort.writeBytes(command, command.length);
}
/**
* Pan Right
* @param speed speed of movement, 0-63
*/
public void PanRight(byte speed){
if (speed<0) speed = 0;
if (speed>0x3F) speed = 0x3F;
byte[] command = new byte[]{0, cameraid, 0, 2, speed, 0, 0};
command[6] = Checksum(command); // add checksum
command[0] = (byte) 0xFF; // add synchronization byte
serialPort.writeBytes(command, command.length);
}
/**
* Tilt Up
* @param speed speed of movement, 0-63
*/
public void TiltUp(byte speed){
if (speed<0) speed = 0;
if (speed>0x3F) speed = 0x3F;
byte[] command = new byte[]{0, cameraid, 0, 8, speed, 0, 0};
command[6] = Checksum(command); // add checksum
command[0] = (byte) 0xFF; // add synchronization byte
serialPort.writeBytes(command, command.length);
}
/**
* Tilt Down
* @param speed speed of movement, 0-63
*/
public void TiltDown(byte speed){
if (speed<0) speed = 0;
if (speed>0x3F) speed = 0x3F;
byte[] command = new byte[]{0, cameraid, 0, 16, speed, 0, 0};
command[6] = Checksum(command); // add checksum
command[0] = (byte) 0xFF; // add synchronization byte
serialPort.writeBytes(command, command.length);
}
/**
* Sum of bytes, then modulo by 256
* @param data data to be summed
* @return checksum
*/
private byte Checksum(byte[] data){
int sum = 0;
for (byte b : data){
sum += b;
}
return (byte)(sum % 256);
}
}

View File

@@ -0,0 +1,11 @@
package Camera;
import org.bytedeco.javacv.Frame;
import org.bytedeco.opencv.opencv_core.Mat;
public interface RtspEvent {
void onMatReceived(Mat mat);
void onFrameReceived(Frame frame);
void onBase64Received(String base64);
void onStreamingStatusReceived(String status);
}

View File

@@ -0,0 +1,120 @@
package Camera;
import lombok.Getter;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.opencv.opencv_core.Mat;
import org.tinylog.Logger;
import java.util.concurrent.atomic.AtomicBoolean;
@SuppressWarnings("unused")
public class RtspGrabber {
private final String rtspUrl;
private FFmpegFrameGrabber grabber;
private final AtomicBoolean isGrabbing = new AtomicBoolean(false);
private @Getter Frame lastFrame = null;
private @Getter String lastBase64 = null;
private @Getter Mat lastMat = null;
private GrabbingTask grabbingTask = null;
public RtspGrabber(String ip, int port, String username, String password, String path) {
rtspUrl = "rtsp://" + username + ":" + password + "@" + ip + ":" + port + path;
Logger.info("RtspGrabber created with url: " + rtspUrl);
}
public RtspGrabber(String ip, String path) {
this.rtspUrl = "rtsp://" + ip + path;
Logger.info("RtspGrabber created with url: " + rtspUrl);
}
/**
* Start grabbing frames from rtsp
* @param useTcp Use tcp instead of udp
* @param event Event to be called when frame is received
*/
public void Start(boolean useTcp, final int width, final int height, RtspEvent event){
try{
grabber = FFmpegFrameGrabber.createDefault(rtspUrl);
if (useTcp) grabber.setOption("rtsp_transport", "tcp");
//grabber.setImageWidth(width);
//grabber.setImageHeight(height);
grabber.setPixelFormat(avutil.AV_PIX_FMT_BGR24);
grabber.start();
avutil.av_log_set_level(avutil.AV_LOG_ERROR);
Logger.info("Grabber started");
GrabbingTask tt = new GrabbingTask(isGrabbing, grabber);
tt.setOnMessageUpdate(Logger::info);
tt.setOnMatUpdate(value -> {
// Kalau butuh Mat untuk diproses
lastMat = value;
if (event!=null) event.onMatReceived(value);
});
tt.setOnFrameUpdate(value -> {
// Kalau butuh Frame untuk ditampilkan
lastFrame = value;
if (event!=null) event.onFrameReceived(value);
});
tt.setOnBase64Update(value -> {
// Kalau butuh Base64 untuk dikirim ke Websocket
lastBase64 = value;
if (event!=null) event.onBase64Received(value);
});
tt.setOnStreamingStatusUpdate(value -> {
// Kalau butuh status streaming
if (event!=null) event.onStreamingStatusReceived(value);
});
new Thread(tt).start();
grabbingTask = tt;
} catch (Exception e){
Logger.error("Error starting grabber: " + e.getMessage());
}
}
/**
* 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());
}
}
}
/**
* Check if grabber is grabbing
* @return True if grabbing
*/
public boolean IsGrabbing(){
return isGrabbing.get();
}
public void ChangeVideoQuality(boolean HQ){
if (IsGrabbing()){
if (grabbingTask!=null){
grabbingTask.setHQ(HQ);
}
}
}
public String GetStreamingStatus(){
if (grabbingTask!=null){
return grabbingTask.GetStreamingStatus();
}
return "No Status";
}
}

View File

@@ -0,0 +1,348 @@
package Camera;
import org.tinylog.Logger;
import java.awt.*;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static Other.SomeCodes.ValidInteger;
import static Other.SomeCodes.ValidString;
@SuppressWarnings("unused")
public class VapixProtocol {
private final String url;
private final HttpClient client;
private String parameters;
private String ProductNumberValue = "";
private String SerialNumberValue = "";
private int CameraIDValue = 0;
private int MaxZoomValue = 0;
private int MinZoomValue = 0;
private boolean PTZEnabledValue = false;
private String[] ImageResolutions = new String[0];
private String[] ImageFormats = new String[0];
private int CurrentZoomValue = 0;
/**
* Create a new VapixProtocol object
* @param ip The IP address of the camera
* @param port The HTTP port of the camera
* @param username The username to access the camera (optional)
* @param password The password to access the camera (optional)
*/
public VapixProtocol(String ip, int port, String username, String password) {
url = "http://" + ip + ":" + port+"/axis-cgi/";
String auth = username + ":" + password;
//encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8));
client = HttpClient.newHttpClient();
GetParameters(null);
if (ValidString(parameters)){
Pattern ProductNumber = Pattern.compile(".*ProdNbr=(.*)");
Matcher m1 = ProductNumber.matcher(parameters);
if (m1.find()) ProductNumberValue = m1.group(1);
Pattern SerialNumber = Pattern.compile(".*SerialNumber=(.*)");
Matcher m2 = SerialNumber.matcher(parameters);
if (m2.find()) SerialNumberValue = m2.group(1);
Pattern PTZCamId = Pattern.compile(".*CamId=(.*)");
Matcher m3 = PTZCamId.matcher(parameters);
if (m3.find()) {
String value = m3.group(1);
if (ValidInteger(value)) CameraIDValue = Integer.parseInt(value);
}
Pattern PTZMaxZoom = Pattern.compile(".*MaxZoom=(.*)");
Matcher m4 = PTZMaxZoom.matcher(parameters);
if (m4.find()) {
String value = m4.group(1);
if (ValidInteger(value)) MaxZoomValue = Integer.parseInt(value);
}
Pattern PTZMinZoom = Pattern.compile(".*MinZoom=(.*)");
Matcher m5 = PTZMinZoom.matcher(parameters);
if (m5.find()) {
String value = m5.group(1);
if (ValidInteger(value)) MinZoomValue = Integer.parseInt(value);
}
Pattern PTZEnabled = Pattern.compile(".*PTZ.PTZ=(.*)");
Matcher m6 = PTZEnabled.matcher(parameters);
if (m6.find()) PTZEnabledValue = m6.group(1).contains("yes");
Pattern Resolution = Pattern.compile(".*Image.Resolution=(.*)");
Matcher m7 = Resolution.matcher(parameters);
if (m7.find()) {
String value = m7.group(1);
if (ValidString(value)) ImageResolutions = value.split(",");
//Logger.info("Resolution: "+value);
}
Pattern Format = Pattern.compile(".*Image.Format=(.*)");
Matcher m8 = Format.matcher(parameters);
if (m8.find()) {
String value = m8.group(1);
if (ValidString(value)) ImageFormats = value.split(",");
//Logger.info("Format: "+value);
}
}
}
@SuppressWarnings("SameParameterValue")
private void GetParameters(String group){
String command = url+"param.cgi?action=list";
if (ValidString(group)) command+="&group="+group;
HttpRequest request = HttpRequest.newBuilder()
.uri(java.net.URI.create(command))
.build();
parameters = "";
try{
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode()==200){
parameters = response.body();
} else Logger.info("Error getting parameters: "+response.statusCode());
} catch (Exception e){
Logger.error("Error getting parameters: "+e.getMessage());
}
}
/**
* Get Product Number of the camera
* @return Product Number, or empty string if not available
*/
public String GetProductNumber(){
return ProductNumberValue;
}
/**
* Get Serial Number of the camera
* @return Serial Number, or empty string if not available
*/
public String GetSerialNumber(){
return SerialNumberValue;
}
/**
* Get PTZ Camera ID
* @return Camera ID
*/
public int GetPTZCameraID(){
return CameraIDValue;
}
/**
* Get Maximum Zoom Value
* @return Maximum Zoom Value
*/
public int GetPTZMaxZoom(){
return MaxZoomValue;
}
/**
* Get Minimum Zoom Value
* @return Minimum Zoom Value
*/
public int GetPTZMinZoom(){
return MinZoomValue;
}
/**
* Check if PTZ Control enabled for this camera
* @return true if PTZ Control enabled, false otherwise
*/
public boolean PTZEnabled() {
return PTZEnabledValue;
}
/**
* Get Available Image Resolutions
* @return array of String, in format "widthxheight"
*/
public String[] GetImageResolutions(){
return ImageResolutions;
}
/**
* Get Available Image Formats
* @return array of String, example "jpeg","mjpeg","h264","bitmap"
*/
public String[] GetImageFormats(){
return ImageFormats;
}
/**
* Close VapiX Protocol
*/
public void Close(){
if (client!=null) client.close();
}
/**
* Zoom In / Out
* @param cameraid Camera ID, get it from GetPTZCameraID()
* @param zoom Zoom value, value between GetPTZMinZoom and GetPTZMaxZoom()
* @return true if successful, false otherwise
*/
public boolean Zoom(int cameraid, int zoom){
if (zoom < MinZoomValue) zoom = MinZoomValue;
if (zoom > MaxZoomValue) zoom = MaxZoomValue;
String command = url+"com/ptz.cgi?zoom="+zoom+"&camera="+cameraid;
HttpRequest request = HttpRequest.newBuilder()
.uri(java.net.URI.create(command))
.build();
try{
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode()==200){
Logger.info("Zoom: "+response.body());
return true;
}
} catch (Exception e){
Logger.error("Error zooming: "+e.getMessage());
}
return false;
}
/**
* Get Current Zoom Value
* @return Current Zoom Value
*/
public int GetCurrentZoomValue(){
update_ptz_query_position();
return CurrentZoomValue;
}
/**
* Get Current Image Resolution
* @param cameraid Camera ID, get it from GetPTZCameraID()
* @return array of int, [0] = width, [1] = height
*/
public int[] GetCurrentResolution(int cameraid){
int[] result = new int[]{0,0};
String command = url+"imagesize.cgi?camera="+cameraid;
HttpRequest request = HttpRequest.newBuilder()
.uri(java.net.URI.create(command))
.build();
try{
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode()==200){
String values = response.body();
//Something like this:
//image width = 1920
//image height = 1080
for (String s : values.split("\n")) {
String[] parts = s.split("=");
if (parts.length==2){
String part0 = parts[0].trim();
String part1 = parts[1].trim();
if (part0.contains("width")){
if (ValidInteger(part1)) result[0] = Integer.parseInt(part1);
}
if (part0.contains("height")) {
if (ValidInteger(part1)) result[1] = Integer.parseInt(part1);
}
}
}
}
} catch (Exception e){
Logger.error("Error getting resolution: "+e.getMessage());
}
return result;
}
/**
* Set Image Resolution
* @param cameraid Camera ID, get it from GetPTZCameraID()
* @param width Width of the image
* @param height Height of the image
* @return true if successful, false otherwise
*/
public boolean SetResolution(int cameraid, int width, int height){
String resolution = width+"x"+height;
boolean supported = false;
for(String r: ImageResolutions){
if (r.equals(resolution)){
supported = true;
break;
}
}
if (supported){
String command = url+"imagesize.cgi?camera="+cameraid+"&resolution="+resolution;
HttpRequest request = HttpRequest.newBuilder()
.uri(java.net.URI.create(command))
.build();
try{
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode()==200){
Logger.info("Resolution set: "+response.body());
String values = response.body();
//Something like this:
//image width = 1920
//image height = 1080
boolean width_ok = false;
boolean height_ok = false;
for (String s : values.split("\n")) {
String[] parts = s.split("=");
if (parts.length==2){
String part0 = parts[0].trim();
String part1 = parts[1].trim();
if (part0.contains("width")){
if (ValidInteger(part1)) {
if (Integer.parseInt(part1)==width)
width_ok = true;
else Logger.error("Width failed, target: "+width+" actual: "+part1);
}
}
if (part0.contains("height")) {
if (ValidInteger(part1)) {
if (Integer.parseInt(part1)==height)
height_ok = true;
else Logger.error("Height failed, target: "+height+" actual: "+part1);
}
}
}
}
return width_ok && height_ok;
}
} catch (Exception e){
Logger.error("Error setting resolution: "+e.getMessage());
}
} else Logger.info("Resolution not supported: "+resolution);
return false;
}
private void update_ptz_query_position(){
String command = url+"com/ptz.cgi?query=position";
HttpRequest request = HttpRequest.newBuilder()
.uri(java.net.URI.create(command))
.build();
try{
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode()==200){
String values = response.body();
//Something like this:
//pan=0.0000
//tilt=0.0000
//zoom=1
//iris=5000
//focus=9574
//brightness=5000
//autofocus=on
//autoiris=off
for (String s : values.split("\n")) {
String[] parts = s.split("=");
if (parts.length==2){
if (parts[0].equals("zoom")) CurrentZoomValue = Integer.parseInt(parts[1].trim());
}
}
}
} catch (Exception e){
Logger.error("Error getting PTZ position: "+e.getMessage());
}
}
}

View File

@@ -0,0 +1,245 @@
package Other;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.bytedeco.javacv.OpenCVFrameConverter;
import org.bytedeco.opencv.global.opencv_core;
import org.bytedeco.opencv.global.opencv_imgproc;
import org.bytedeco.opencv.opencv_core.Mat;
import org.bytedeco.opencv.opencv_core.Size;
import org.bytedeco.opencv.opencv_core.UMat;
import org.jetbrains.annotations.NotNull;
import org.tinylog.Logger;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.nio.file.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Properties;
@SuppressWarnings("unused")
public class SomeCodes {
public final static String currentDirectory = System.getProperty("user.dir");
public final static Path audioPath = Path.of(currentDirectory, "audiofiles");
private static final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static final OpenCVFrameConverter.ToMat matConverter = new OpenCVFrameConverter.ToMat();
public static final Java2DFrameConverter frameConverter = new Java2DFrameConverter();
public static final Path logsPath = Path.of(currentDirectory, "logs");
public static final boolean haveOpenCL = opencv_core.haveOpenCL();
public static boolean useOpenCL;
public static String[] GetAudioFiles(){
try{
return Files.list(audioPath).map(f -> f.getFileName().toString()).toArray(String[]::new);
} catch (Exception e){
Logger.error("Error getting audio files: "+e.getMessage());
}
return new String[0];
}
public static String LocalDateTimeToString(LocalDateTime x){
return x.format(dtf);
}
public static String ExtractResource(String filename, String targetdirectory){
try {
File destination = new File(targetdirectory, filename);
if (destination.exists()) {
Logger.info("Resource File already exists: " + filename);
return destination.getAbsolutePath();
}
InputStream is = SomeCodes.class.getResourceAsStream(filename);
if (is!=null){
Files.copy(is, destination.toPath());
Logger.info("Resource File extracted: "+filename);
return destination.getAbsolutePath();
} else {
Logger.error("Resource File not found: "+filename);
}
} catch (Exception e){
Logger.error("Error extracting resource: "+filename+", Message : "+e.getMessage());
}
return null;
}
public static boolean ValidDirectory(String path){
if (ValidString(path)){
File ff = new File(path);
return ff.isDirectory();
}
return false;
}
public static boolean ValidFile(String filename){
if (ValidString(filename)){
File ff = new File(filename);
return ff.isFile();
}
return false;
}
public static boolean ValidString(String x){
if (x!=null){
return !x.isEmpty();
}
return false;
}
public static byte GetPanTiltSpeed(String data, byte defaultspeed){
if (ValidInteger(data)){
int speed = Integer.parseInt(data);
if (speed<0) speed = 0;
if (speed>0x3F) speed = 0x3F;
return (byte)speed;
}
return defaultspeed;
}
public static boolean ValidInteger(String x){
try{
Integer.parseInt(x);
return true;
} catch (Exception e){
return false;
}
}
public static boolean ValidPortNumber(int port){
return port>0 && port<65536;
}
public static boolean ValidPortNumber(String port){
try{
int portx = Integer.parseInt(port);
return ValidPortNumber(portx);
} catch (Exception e){
return false;
}
}
public static boolean ValidIPV4(String ipaddress){
if (ValidString(ipaddress)){
try{
InetAddress inet = InetAddress.getByName(ipaddress);
if (inet instanceof Inet4Address){
if (inet.getHostAddress().equals(ipaddress)){
return true;
}
}
} catch (Exception ignored) {
}
}
return false;
}
public static boolean ValidIPV6(String ipaddress){
if (ValidString(ipaddress)){
try{
InetAddress inet = InetAddress.getByName(ipaddress);
if (inet instanceof Inet6Address){
if (inet.getHostAddress().equals(ipaddress)){
return true;
}
}
} catch (Exception ignored) {
}
}
return false;
}
public static String GetFileName(String filepath){
if (ValidString(filepath)){
File ff = new File(filepath);
if (ff.isFile()){
return ff.getName();
}
}
return "";
}
public static BufferedImage FrameToBufferedImage(Frame frame){
return frameConverter.getBufferedImage(frame);
}
public static BufferedImage MatToBufferedImage(Mat mat){
return frameConverter.getBufferedImage(matConverter.convert(mat));
}
public static String BufferedImageToBase64(BufferedImage image){
if (image!=null){
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try{
javax.imageio.ImageIO.write(image, "jpg", baos);
baos.flush();
byte[] imageInByte = baos.toByteArray();
baos.close();
return java.util.Base64.getEncoder().encodeToString(imageInByte);
} catch (Exception e){
Logger.error("Error converting BufferedImage to Base64: "+e.getMessage());
}
}
return "";
}
public static @NotNull Properties LoadProperties(String filename){
try{
InputStream is = new FileInputStream(filename);
Properties prop = new Properties();
prop.load(is);
return prop;
} catch (Exception e){
Logger.error("Error loading properties file: "+e.getMessage());
}
return new Properties();
}
public static boolean SaveProperties(Properties prop, String filename){
try{
OutputStream os = new FileOutputStream(filename);
prop.store(os, null);
return true;
} catch (Exception e){
Logger.error("Error saving properties file: "+e.getMessage());
}
return false;
}
public static String GetConfigAudioFile(int index){
Properties config = LoadProperties("config.properties");
String key = String.format("AudioFile%02d", index);
return config.getProperty(key, null);
}
public static Mat ResizeMat(Mat source, int width, int height){
Size sz = new Size(width, height);
Mat dest = new Mat();
if (useOpenCL){
UMat src = new UMat();
source.copyTo(src);
UMat dst = new UMat();
opencv_imgproc.resize(src, dst, sz);
dst.copyTo(dest);
} else {
opencv_imgproc.resize(source, dest, sz);
}
return dest;
}
public static Frame ResizeFrame(Frame source, int width, int height){
Mat mat = matConverter.convertToMat(source);
Mat resized = ResizeMat(mat, width, height);
return matConverter.convert(resized);
}
}

165
src/main/java/SBC/GPIO.java Normal file
View File

@@ -0,0 +1,165 @@
package SBC;
import com.sun.jna.Platform;
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");
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(){
if (Platform.isLinux()){
if (Platform.isARM()){
if (Platform.is64Bit()){
if (gpioPath.toFile().isDirectory()){
if (gpioExportPath.toFile().isFile()){
if (gpioUnexportPath.toFile().isFile()){
return true;
} else Logger.error("GPIO unexport path is not found");
} else Logger.error("GPIO export path is not found");
} else Logger.error("GPIO path is not found");
} else Logger.info("Device is not 64 bit");
} else Logger.info("Device is not ARM");
} else Logger.info("OS is not Linux");
return false;
}
/**
* Check if the pin is already exported
* @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);
return pinPath.toFile().isDirectory();
}
/**
* Export the pin
* @param pin GPIO pin number
* @return true if the pin is successfully exported
*/
public static boolean ExportPin(int pin){
try{
if (Files.isWritable(gpioExportPath)){
Files.write(gpioExportPath, String.valueOf(pin).getBytes());
Logger.info("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());
}
return false;
}
/**
* Unexport the pin
* @param pin GPIO pin number
* @return true if the pin is successfully unexported
*/
public static boolean UnexportPin(int pin){
if (Files.isWritable(gpioUnexportPath)){
try{
Files.write(gpioUnexportPath, String.valueOf(pin).getBytes());
Logger.info("Pin "+pin+" unexported");
return true;
} catch (Exception e){
Logger.error("Failed to unexport pin: "+pin+", Message: "+e.getMessage());
}
} else Logger.error("GPIO unexport path is not writable");
return false;
}
/**
* Get Direction of the pin
* @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");
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());
}
} else Logger.error("Pin direction file is not readable: "+pin);
} else Logger.error("Pin direction file not found: "+pin);
return "unknown";
}
/**
* Set the direction of the pin
* @param pin GPIO pin number
* @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");
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);
return true;
} catch (Exception e){
Logger.error("Failed to set pin direction: "+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);
return false;
}
/**
* Set the value of the output pin
* @param pin GPIO pin number
* @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");
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"));
return true;
} catch (Exception e){
Logger.error("Failed to set pin value: "+pin+", Message: "+e.getMessage());
}
} else Logger.error("Pin value file is not writable: "+pin);
} else Logger.error("Pin value file not found: "+pin);
return false;
}
/**
* Get the value of the pin
* @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");
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());
}
} else Logger.error("Pin value file is not readable: "+pin);
} else Logger.error("Pin value file not found: "+pin);
return "unknown";
}
}

View File

@@ -0,0 +1,38 @@
package Web;
import Other.SomeCodes;
import java.util.Properties;
public class SettingInfo {
public String[] AudioFiles;
public String AudioFile1;
public String AudioFile2;
public String AudioFile3;
public String AudioFile4;
public String AudioFile5;
public String CameraIP;
public String CameraPort;
public String CameraUsername;
public String CameraPassword;
public String LoginUsername;
public String LoginPassword;
public static SettingInfo getInstance() {
SettingInfo settingInfo = new SettingInfo();
Properties prop = SomeCodes.LoadProperties("config.properties");
settingInfo.AudioFiles = SomeCodes.GetAudioFiles();
settingInfo.AudioFile1 = prop.getProperty("AudioFile01","").trim();
settingInfo.AudioFile2 = prop.getProperty("AudioFile02","").trim();
settingInfo.AudioFile3 = prop.getProperty("AudioFile03","").trim();
settingInfo.AudioFile4 = prop.getProperty("AudioFile04","").trim();
settingInfo.AudioFile5 = prop.getProperty("AudioFile05","").trim();
settingInfo.CameraIP = prop.getProperty("Camera_ip","").trim();
settingInfo.CameraPort = prop.getProperty("Camera_port","").trim();
settingInfo.CameraUsername = prop.getProperty("Camera_user","").trim();
settingInfo.CameraPassword = prop.getProperty("Camera_password","").trim();
settingInfo.LoginUsername = prop.getProperty("WebUsername","").trim();
settingInfo.LoginPassword = prop.getProperty("WebPassword","").trim();
return settingInfo;
}
}

View File

@@ -0,0 +1,214 @@
package Web;
import Other.SomeCodes;
import io.javalin.Javalin;
import io.javalin.http.UploadedFile;
import io.javalin.util.JavalinException;
import io.javalin.websocket.*;
import lombok.extern.java.Log;
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.concurrent.ConcurrentHashMap;
import static Other.SomeCodes.*;
import static io.javalin.apibuilder.ApiBuilder.*;
@SuppressWarnings({"unused"})
public class WebServer {
private final Javalin app;
private final Set<WsContext> connectedWebsocketClients = ConcurrentHashMap.newKeySet();
public WebServer(WebsocketEvent event, String webusername, String webpassword){
app = Javalin.create(config -> {
config.staticFiles.add("/html");
config.router.apiBuilder(()-> path("setting", () ->{
get(ctx -> ctx.json(SettingInfo.getInstance()));
path("audiofile",()-> post(ctx -> {
Logger.info("api /setting/audiofile");
String audiofile1 = ctx.formParam("1");
String audiofile2 = ctx.formParam("2");
String audiofile3 = ctx.formParam("3");
String audiofile4 = ctx.formParam("4");
String audiofile5 = ctx.formParam("5");
Logger.info("audiofile1: {}", audiofile1);
Logger.info("audiofile2: {}", audiofile2);
Logger.info("audiofile3: {}", audiofile3);
Logger.info("audiofile4: {}", audiofile4);
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:"");
if (SaveProperties(prop, "config.properties")){
Logger.info("audiofile saved");
ctx.status(200);
} else {
Logger.error("Failed to save audiofile");
ctx.status(400);
}
}));
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());
}
}
}));
path("weblogin", ()-> post(ctx -> {
String username = ctx.formParam("username");
String password = ctx.formParam("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);
} else {
ctx.status(400);
}
}));
path("camera",()-> post(ctx -> {
String camera_ip = ctx.formParam("ip");
String camera_port = ctx.formParam("port");
String camera_username = ctx.formParam("username");
String camera_password = ctx.formParam("password");
Properties prop = SomeCodes.LoadProperties("config.properties");
prop.setProperty("Camera_ip", ValidString(camera_ip)?camera_ip:"192.168.0.4");
prop.setProperty("Camera_port", ValidString(camera_port)?camera_port:"80");
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")){
ctx.status(200);
} else {
ctx.status(400);
}
}));
}));
});
app.get("/", ctx-> {
if (ctx.sessionAttribute("username")==null) {
// belum login
ctx.redirect("/login.html");
} else if (Objects.equals(ctx.sessionAttribute("username"), webusername)){
// sudah login
ctx.redirect("/index.html");
} else {
// sudah login tapi bukan username yang benar
ctx.redirect("/login.html");
}
});
app.before("/index.html", ctx ->{
if (ctx.sessionAttribute("username")==null){
ctx.redirect("/login.html");
}
});
app.before("/setting.html", ctx ->{
if (ctx.sessionAttribute("username")==null){
ctx.redirect("/login.html");
}
});
app.post("/login", ctx ->{
String username = ctx.formParam("username");
String password = ctx.formParam("password");
if (Objects.equals(username, webusername) && Objects.equals(password, webpassword)){
ctx.sessionAttribute("username", username);
ctx.redirect("/index.html");
} else {
ctx.redirect("/login.html?error=Invalid username or password");
}
});
app.get("/logout", ctx ->{
ctx.sessionAttribute("username", null);
ctx.redirect("/login.html");
});
app.ws("/ws", ws -> {
ws.onConnect(connectws);
ws.onClose(closews);
//ws.onError(errorws);
ws.onMessage(ctx -> {
try{
//Logger.info("WebSocket message {}", ctx.message());
WebsocketCommand command = ctx.messageAsClass(WebsocketCommand.class);
if (event!=null) {
WebsocketReply reply = event.onWebsocketCommand(command);
if (reply!=null) ctx.sendAsClass(reply, WebsocketReply.class);
}
} catch (Exception e){
Logger.error("Failed to parse WebSocketCommand message: {}", e.getMessage());
}
});
});
}
/**
* Send Object message to all connected websocket clients
* @param obj Object to send
*/
public void SendtoAll(Object obj){
connectedWebsocketClients.forEach(wsContext -> wsContext.send(obj));
}
/**
* Start the web server
* @param localip Local IP address to bind
* @param port Port to bind
*/
public void Start(String localip, int port){
try{
connectedWebsocketClients.forEach(WsContext::closeSession);
app.start(localip, port);
Logger.info("Web server started at {}:{}", localip, port);
} catch (JavalinException e){
Logger.error("Web server failed to start: {}", e.getMessage());
}
}
/**
* Stop the web server
*/
public void Stop(){
try{
app.stop();
Logger.info("Web server stopped");
} catch (JavalinException e){
Logger.error("Web server failed to stop: {}", e.getMessage());
}
}
WsConnectHandler connectws = ws ->{
Logger.info("WebSocket connected from {}", ws.host());
//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());
connectedWebsocketClients.remove(ws);
};
//WsErrorHandler errorws = ws -> Logger.error("WebSocket error from {}, error {}", ws.host(), ws.error());
}

View File

@@ -0,0 +1,14 @@
package Web;
public class WebsocketCommand {
public String command;
public String data;
public WebsocketCommand(){
this.command = "";
this.data = "";
}
public WebsocketCommand(String command, String data){
this.command = command;
this.data = data;
}
}

View File

@@ -0,0 +1,6 @@
package Web;
public interface WebsocketEvent {
String onMessage(String message);
WebsocketReply onWebsocketCommand(WebsocketCommand command);
}

View File

@@ -0,0 +1,10 @@
package Web;
public class WebsocketReply {
public String reply;
public String data;
public WebsocketReply(String reply, String data){
this.reply = reply;
this.data = data;
}
}

View File

@@ -0,0 +1,314 @@
package id.co.gtc;
import Audio.AudioFileProperties;
import Audio.AudioPlayer;
import Audio.PlaybackEvent;
import Camera.PanTiltController;
import Camera.RtspEvent;
import Camera.RtspGrabber;
import Camera.VapixProtocol;
import Other.SomeCodes;
import Web.WebServer;
import Web.WebsocketCommand;
import Web.WebsocketEvent;
import Web.WebsocketReply;
import org.bytedeco.javacv.Frame;
import org.bytedeco.opencv.global.opencv_core;
import org.bytedeco.opencv.opencv_core.Mat;
import org.tinylog.Logger;
import java.io.File;
import java.nio.file.Path;
import java.util.Objects;
import java.util.Properties;
import static Other.SomeCodes.*;
public class Main {
private static AudioPlayer audioPlayer;
private static WebServer webServer;
private static RtspGrabber rtspGrabber;
private static PanTiltController panTiltController;
private static AudioFileProperties audioFileProperties;
private static VapixProtocol vapixProtocol;
// Application start from here
public static void main(String[] args) {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
if (audioPlayer!=null) audioPlayer.Unload();
if (webServer!=null) webServer.Stop();
if (rtspGrabber!=null) rtspGrabber.Stop();
if (panTiltController!=null) panTiltController.Close();
if (vapixProtocol!=null) vapixProtocol.Close();
}));
init_properties();
init_audiofiles();
init_audio();
init_webserver();
init_rtspgrabber();
init_pantiltcontroller();
init_Vapix();
}
private static void init_properties(){
if (ExtractResource("/tinylog.properties", currentDirectory)==null) Logger.error("Failed to extract tinylog.properties");
if (ExtractResource("/config.properties", currentDirectory)==null) Logger.error("Failed to extract config.properties");
if (ExtractResource("/simplelogger.properties", currentDirectory)==null) Logger.error("Failed to extract simplelogger.properties");
}
private static void init_audiofiles(){
if (audioPath.toFile().isDirectory()) {
Logger.info("Audio Files Directory exists : "+audioPath);
} else {
if (audioPath.toFile().mkdirs()) Logger.info("Audio Files Directory created : "+audioPath);
}
if (ExtractResource("/elangWav.wav", audioPath.toString())==null) Logger.error("Failed to extract elangWav.wav");
if (ExtractResource("/gunshotsWav.wav", audioPath.toString())==null) Logger.error("Failed to extract gunshotsWav.wav");
if (ExtractResource("/pinkNoiseWav.wav", audioPath.toString())==null) Logger.error("Failed to extract pinkNoiseWav.wav");
}
private static void init_Vapix(){
Properties config = SomeCodes.LoadProperties("config.properties");
String ip = config.getProperty("Camera_ip");
String port = config.getProperty("Camera_port");
String username = config.getProperty("Camera_user");
String password = config.getProperty("Camera_password");
if (ValidString(ip)){
if (ValidInteger(port)){
if (ValidString(username)){
if (ValidString(password)){
vapixProtocol = new VapixProtocol(ip, Integer.parseInt(port), username, password);
Logger.info("Camera Product Number: "+vapixProtocol.GetProductNumber());
Logger.info("Camera Serial Number: "+vapixProtocol.GetSerialNumber());
if (vapixProtocol.PTZEnabled()){
Logger.info("PTZ Enabled");
} else Logger.error("PTZ Disabled");
Logger.info("Max Zoom: "+vapixProtocol.GetPTZMaxZoom());
Logger.info("Min Zoom: "+vapixProtocol.GetPTZMinZoom());
} else Logger.error("Invalid Camera Password");
} else Logger.error("Invalid Camera Username");
} else Logger.error("Invalid Camera Port");
} else Logger.error("Invalid Camera IP");
}
private static void init_pantiltcontroller() {
Properties config = SomeCodes.LoadProperties("config.properties");
String portname = config.getProperty("SerialPort");
String baudrate = config.getProperty("SerialBaudRate");
String PanTiltID = config.getProperty("PanTiltID");
if (ValidString(portname)){
if (ValidInteger(baudrate)){
if (ValidInteger(PanTiltID)){
panTiltController = new PanTiltController(portname, Integer.parseInt(baudrate), Integer.parseInt(PanTiltID));
}
} else Logger.error("Invalid PTZ Baudrate");
} else Logger.error("Invalid PTZ Port");
}
private static void init_rtspgrabber() {
if (haveOpenCL) opencv_core.setUseOpenCL(true);
// check if really activated
useOpenCL = opencv_core.useOpenCL();
Logger.info("OpenCL available={}, activated={}", haveOpenCL, useOpenCL);
Properties config = SomeCodes.LoadProperties("config.properties");
String targetip = config.getProperty("Camera_ip");
String rtsppath = config.getProperty("Camera_Rtsp_path");
if (ValidString(targetip)){
if (ValidString(rtsppath)){
rtspGrabber = new RtspGrabber(targetip, rtsppath);
RtspEvent re = new RtspEvent() {
@Override
public void onMatReceived(Mat mat) {
//TODO : kalau butuh Mat, ambil disini
}
@Override
public void onFrameReceived(Frame frame) {
//TODO : kalau butuh Frame, ambil disini
}
@Override
public void onBase64Received(String base64) {
WebsocketReply wr = new WebsocketReply("GET BASE64", "data:image/jpeg;base64,"+ base64);
webServer.SendtoAll(wr);
}
@Override
public void onStreamingStatusReceived(String status) {
WebsocketReply wr = new WebsocketReply("STREAMING STATUS", status);
webServer.SendtoAll(wr);
}
};
rtspGrabber.Start(true, 1920, 1080, re);
} else Logger.error("Invalid Camera Path");
} else Logger.error("Invalid Camera IP");
}
private static void init_audio() {
audioPlayer = new AudioPlayer();
audioPlayer.DetectOutputDevices();
audioPlayer.OpenDevice(1,48000);
audioPlayer.setMasterVolume(100);
audioPlayer.setPlaybackvolume(100);
}
static PlaybackEvent pe = new PlaybackEvent() {
@Override
public void onPlaybackStart(AudioFileProperties prop) {
Logger.info("Playback started for {}", prop.filename);
}
@Override
public void onPlaybackFinished(AudioFileProperties prop) {
Logger.info("Playback finished for {}", prop.filename);
}
@Override
public void onPlaybackFailure(AudioFileProperties prop, String reason) {
Logger.error("Playback failed for {}: {}", prop.filename, reason);
}
@Override
public void onPlaybackLooped(AudioFileProperties prop) {
Logger.info("Playback looped for {}", prop.filename);
}
};
static WebsocketEvent we = new WebsocketEvent() {
@Override
public String onMessage(String message) {
return "";
}
@Override
public WebsocketReply onWebsocketCommand(WebsocketCommand command) {
byte speed;
String cmd = command.command.toUpperCase().trim();
switch (cmd){
// Pan Tilt Movement Commands
case "PAN LEFT" :
speed = GetPanTiltSpeed(command.data, (byte)0x20);
if (panTiltController!=null) panTiltController.PanLeft(speed);
return new WebsocketReply("PAN LEFT", String.valueOf(speed));
case "PAN RIGHT" :
speed = GetPanTiltSpeed(command.data, (byte)0x20);
if (panTiltController!=null) panTiltController.PanRight(speed);
return new WebsocketReply("PAN RIGHT", String.valueOf(speed));
case "TILT UP" :
speed = GetPanTiltSpeed(command.data, (byte)0x20);
if (panTiltController!=null) panTiltController.TiltUp(speed);
return new WebsocketReply("TILT UP", String.valueOf(speed));
case "TILT DOWN" :
speed = GetPanTiltSpeed(command.data, (byte)0x20);
if (panTiltController!=null) panTiltController.TiltDown(speed);
return new WebsocketReply("TILT DOWN", String.valueOf(speed));
case "STOP MOVEMENT" :
if (panTiltController!=null) panTiltController.StopMovement();
return new WebsocketReply("STOP MOVEMENT", "");
// Audio Related Commands
case "MUTE":
audioPlayer.Mute();
return new WebsocketReply("MUTE", "");
case "UNMUTE":
audioPlayer.Unmute();
return new WebsocketReply("UNMUTE", "");
case "SET VOLUME" :
int volume=-1;
if (ValidInteger(command.data)){
volume = Integer.parseInt(command.data);
if (volume<0) volume = 0;
if (volume>100) volume = 100;
}
if (volume>=0){
audioPlayer.setPlaybackvolume(volume);
return new WebsocketReply("SET VOLUME", String.valueOf(volume));
} else return new WebsocketReply("SET VOLUME", "Invalid Volume Value");
case "GET VOLUME" :
int vol = audioPlayer.getPlaybackvolume();
return new WebsocketReply("GET VOLUME", String.valueOf(vol));
case "PLAY AUDIO" :
if (ValidInteger(command.data)){
int id = Integer.parseInt(command.data);
String filename = GetConfigAudioFile(id);
if (ValidString(filename)){
File filetoplay = new File(Path.of(currentDirectory, "audiofiles", filename).toString());
if (filetoplay.isFile()){
AudioFileProperties afp = audioPlayer.OpenAudioFile(filetoplay);
if (afp!=null){
audioFileProperties = afp;
audioPlayer.PlayAudioFile(afp, true, pe);
return new WebsocketReply("PLAY AUDIO", afp.filename);
} else return new WebsocketReply("PLAY AUDIO", "Failed to open audio file "+filename);
} else return new WebsocketReply("PLAY AUDIO", "Audio file not found : "+filename);
} else return new WebsocketReply("PLAY AUDIO", String.format("AudioFile with ID %02d not found", id));
} else return new WebsocketReply("PLAY AUDIO", "Invalid Audio ID");
case "STOP AUDIO" :
if (audioFileProperties!=null){
audioPlayer.CloseAudioFile(audioFileProperties); // close previous audio file
String filename = audioFileProperties.filename;
audioFileProperties = null;
return new WebsocketReply("STOP AUDIO", filename);
} else return new WebsocketReply("STOP AUDIO", "No audioFileProperties");
// ZOOM Related Commands
case "GET MAX ZOOM":
if (vapixProtocol!=null){
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){
return new WebsocketReply("GET ZOOM", String.valueOf(vapixProtocol.GetCurrentZoomValue()));
} else return new WebsocketReply("GET ZOOM", "VapixProtocol not initialized");
case "SET ZOOM":
if (vapixProtocol.PTZEnabled()){
if (ValidInteger(command.data)){
int zoom = Integer.parseInt(command.data);
if (zoom<vapixProtocol.GetPTZMinZoom()) zoom = vapixProtocol.GetPTZMinZoom();
if (zoom>vapixProtocol.GetPTZMaxZoom()) zoom = vapixProtocol.GetPTZMaxZoom();
if (vapixProtocol.Zoom(1, zoom)){
return new WebsocketReply("ZOOM", String.valueOf(zoom));
} else return new WebsocketReply("ZOOM", "Failed to zoom");
} else return new WebsocketReply("ZOOM", "Invalid Zoom Value");
} else return new WebsocketReply("ZOOM", "Zoom not supported");
// Live Streaming Related Commands
case "GET BASE64":
if (rtspGrabber!=null){
return new WebsocketReply("GET BASE64", "data:image/jpeg;base64,"+ rtspGrabber.getLastBase64());
} else return new WebsocketReply("GET BASE64", "RTSP Grabber not initialized");
case "GET RESOLUTION":
if (vapixProtocol!=null){
int[] res = vapixProtocol.GetCurrentResolution(1);
return new WebsocketReply("GET RESOLUTION", String.format("%dx%d", res[0], res[1]));
} else return new WebsocketReply("GET RESOLUTION", "VapixProtocol not initialized");
case "SET VIDEO QUALITY":
if (Objects.equals(command.data,"HQ")){
if (rtspGrabber!=null) rtspGrabber.ChangeVideoQuality(true);
return new WebsocketReply("SET VIDEO QUALITY", "High Quality");
} else if (Objects.equals(command.data,"LQ")){
if (rtspGrabber!=null) rtspGrabber.ChangeVideoQuality(false);
return new WebsocketReply("SET VIDEO QUALITY", "Low Quality");
} else return new WebsocketReply("SET VIDEO QUALITY", "Invalid Video Quality");
case "STREAMING STATUS":
if (rtspGrabber!=null){
return new WebsocketReply("STREAMING STATUS", rtspGrabber.GetStreamingStatus());
} else return new WebsocketReply("STREAMING STATUS", "RTSP Grabber not initialized");
default:
return new WebsocketReply("UNKNOWN COMMAND", command.command);
}
}
};
private static void init_webserver() {
Properties config = SomeCodes.LoadProperties("config.properties");
String webusername = config.getProperty("WebUsername", "admin");
String webpassword = config.getProperty("WebPassword", "bandara");
String webhost = config.getProperty("WebHost","0.0.0.0");
String webport = config.getProperty("WebPort","8080");
webServer = new WebServer(we, webusername, webpassword);
webServer.Start(webhost, Integer.parseInt(webport));
}
}

9
tinylog.properties Normal file
View File

@@ -0,0 +1,9 @@
//writer = rolling file
writer = console
writer.file = logs/{date:yyyy-MM-dd}.log
writer.format = {date:yyyy-MM-dd HH:mm:ss} {level}: {class}.{method}() {message}
writer.policies = daily
writer.buffered = true
writer.charset = UTF-8
writer.level = info
writer.append = true