second commit 20/01/2026

This commit is contained in:
2026-01-20 17:02:55 +07:00
parent 61f22936c7
commit 38f3fd54b7
15 changed files with 409 additions and 6 deletions

13
.idea/deviceManager.xml generated Normal file
View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DeviceTable">
<option name="columnSorters">
<list>
<ColumnSorterState>
<option name="column" value="Name" />
<option name="order" value="ASCENDING" />
</ColumnSorterState>
</list>
</option>
</component>
</project>

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,57 @@
.bs-icon {
--bs-icon-size: .75rem;
display: flex;
flex-shrink: 0;
justify-content: center;
align-items: center;
font-size: var(--bs-icon-size);
width: calc(var(--bs-icon-size) * 2);
height: calc(var(--bs-icon-size) * 2);
color: var(--bs-primary);
}
.bs-icon-xs {
--bs-icon-size: 1rem;
width: calc(var(--bs-icon-size) * 1.5);
height: calc(var(--bs-icon-size) * 1.5);
}
.bs-icon-sm {
--bs-icon-size: 1rem;
}
.bs-icon-md {
--bs-icon-size: 1.5rem;
}
.bs-icon-lg {
--bs-icon-size: 2rem;
}
.bs-icon-xl {
--bs-icon-size: 2.5rem;
}
.bs-icon.bs-icon-primary {
color: var(--bs-white);
background: var(--bs-primary);
}
.bs-icon.bs-icon-primary-light {
color: var(--bs-primary);
background: rgba(var(--bs-primary-rgb), .2);
}
.bs-icon.bs-icon-semi-white {
color: var(--bs-primary);
background: rgba(255, 255, 255, .5);
}
.bs-icon.bs-icon-rounded {
border-radius: .5rem;
}
.bs-icon.bs-icon-circle {
border-radius: 50%;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

2
html/assets/js/jquery-3.7.1.min.js vendored Normal file

File diff suppressed because one or more lines are too long

35
html/home.html Normal file
View File

@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html data-bs-theme="light" lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>FarmToAAS</title>
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
</head>
<body>
<nav class="navbar navbar-expand-md bg-body py-3">
<div class="container"><a class="navbar-brand d-flex align-items-center" href="#"><span class="bs-icon-sm bs-icon-rounded bs-icon-primary d-flex justify-content-center align-items-center me-2 bs-icon"><img class="img-fluid" width="80" height="80" src="assets/img/gtc%20icon%2080x80.png"></span><span>Farm To AAS</span></a><button data-bs-toggle="collapse" class="navbar-toggler" data-bs-target="#navcol-1"><span class="visually-hidden">Toggle navigation</span><span class="navbar-toggler-icon"></span></button>
<div class="collapse navbar-collapse" id="navcol-1">
<ul class="navbar-nav me-auto">
<li class="nav-item"><a class="nav-link active" href="#">Overview</a></li>
<li class="nav-item"><a class="nav-link" href="#">Setting</a></li>
<li class="nav-item"><a class="nav-link" href="#">Log</a></li>
</ul><button class="btn btn-primary" type="button">Logout</button>
</div>
</div>
</nav>
<div class="row mb-3">
<div class="col ms-1 me-1"><label class="col-form-label w-100 h-100">FARM Status : N/A</label></div>
<div class="col ms-1 me-1"><label class="col-form-label w-100 h-100">AAS 1 Status : N/A</label></div>
<div class="col ms-1 me-1"><label class="col-form-label w-100 h-100">AAS 2 Status : N/A</label></div>
<div class="col ms-1 me-1"><label class="col-form-label w-100 h-100">AAS 3 Status : N/A</label></div>
</div>
<div class="row"></div>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/jquery-3.7.1.min.js"></script>
</body>
</html>

50
html/log.html Normal file
View File

@@ -0,0 +1,50 @@
<!DOCTYPE html>
<html data-bs-theme="light" lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>FarmToAAS</title>
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
</head>
<body>
<nav class="navbar navbar-expand-md bg-body py-3">
<div class="container"><a class="navbar-brand d-flex align-items-center" href="#"><span class="bs-icon-sm bs-icon-rounded bs-icon-primary d-flex justify-content-center align-items-center me-2 bs-icon"><img class="img-fluid" width="80" height="80" src="assets/img/gtc%20icon%2080x80.png"></span><span>Farm To AAS</span></a><button data-bs-toggle="collapse" class="navbar-toggler" data-bs-target="#navcol-1"><span class="visually-hidden">Toggle navigation</span><span class="navbar-toggler-icon"></span></button>
<div class="collapse navbar-collapse" id="navcol-1">
<ul class="navbar-nav me-auto">
<li class="nav-item"><a class="nav-link" href="#">Overview</a></li>
<li class="nav-item"><a class="nav-link" href="#">Setting</a></li>
<li class="nav-item"><a class="nav-link active" href="#">Log</a></li>
</ul><button class="btn btn-primary" type="button">Logout</button>
</div>
</div>
</nav>
<div class="row mb-2">
<div class="col-2"><label class="col-form-label w-100 h-100 ms-1">Log Chooser</label></div>
<div class="col"><input class="form-control-lg" type="date"></div>
</div>
<div class="row mb-2">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th class="col-1">No</th>
<th class="col-2">Date</th>
<th class="col-2">Time</th>
<th class="col-2">Category</th>
<th>Message</th>
</tr>
</thead>
<tbody id="logtablebody">
<tr></tr>
</tbody>
</table>
</div>
</div>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/jquery-3.7.1.min.js"></script>
</body>
</html>

43
html/login.html Normal file
View File

@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html data-bs-theme="light" lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>FarmToAAS</title>
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
</head>
<body>
<section class="position-relative py-4 py-xl-5">
<div class="container">
<div class="row mb-5">
<div class="col-md-8 col-xl-6 text-center mx-auto">
<h2>Log in</h2>
</div>
</div>
<div class="row d-flex justify-content-center">
<div class="col-md-6 col-xl-4">
<div class="card mb-5">
<div class="card-body d-flex flex-column align-items-center">
<div class="bs-icon-xl bs-icon-circle bs-icon-primary my-4 bs-icon"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" class="bi bi-person">
<path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6m2-3a2 2 0 1 1-4 0 2 2 0 0 1 4 0m4 8c0 1-1 1-1 1H3s-1 0-1-1 1-4 6-4 6 3 6 4m-1-.004c-.001-.246-.154-.986-.832-1.664C11.516 10.68 10.289 10 8 10c-2.29 0-3.516.68-4.168 1.332-.678.678-.83 1.418-.832 1.664z"></path>
</svg></div>
<form class="text-center" method="post">
<div class="mb-3"><input class="form-control" type="text" name="username" placeholder="Username"></div>
<div class="mb-3"><input class="form-control" type="password" name="password" placeholder="Password"></div>
<div class="mb-3"><button class="btn btn-primary w-100 d-block" type="submit">Login</button></div>
<p class="text-muted">Forgot your password?</p>
</form>
</div>
</div>
</div>
</div>
</div>
</section>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/jquery-3.7.1.min.js"></script>
</body>
</html>

73
html/setting.html Normal file
View File

@@ -0,0 +1,73 @@
<!DOCTYPE html>
<html data-bs-theme="light" lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>FarmToAAS</title>
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
</head>
<body>
<nav class="navbar navbar-expand-md bg-body py-3">
<div class="container"><a class="navbar-brand d-flex align-items-center" href="#"><span class="bs-icon-sm bs-icon-rounded bs-icon-primary d-flex justify-content-center align-items-center me-2 bs-icon"><img class="img-fluid" width="80" height="80" src="assets/img/gtc%20icon%2080x80.png"></span><span>Farm To AAS</span></a><button data-bs-toggle="collapse" class="navbar-toggler" data-bs-target="#navcol-1"><span class="visually-hidden">Toggle navigation</span><span class="navbar-toggler-icon"></span></button>
<div class="collapse navbar-collapse" id="navcol-1">
<ul class="navbar-nav me-auto">
<li class="nav-item"><a class="nav-link" href="#">Overview</a></li>
<li class="nav-item"><a class="nav-link active" href="#">Setting</a></li>
<li class="nav-item"><a class="nav-link" href="#">Log</a></li>
</ul><button class="btn btn-primary" type="button">Logout</button>
</div>
</div>
</nav>
<div class="card mb-3">
<div class="card-body">
<h4 class="card-title">FARM Setting</h4>
<div class="col">
<div class="row mb-1">
<div class="col-3"><label class="col-form-label w-100 h-100">Broker URL</label></div>
<div class="col"><input class="w-100 h-100 ms-1 me-1" type="text" id="brokerurl" placeholder="Broker URL"></div>
</div>
<div class="row mb-1">
<div class="col-3"><label class="col-form-label w-100 h-100">Username</label></div>
<div class="col"><input class="w-100 h-100 ms-1 me-1" type="text" id="brokerusername" placeholder="Username"></div>
</div>
<div class="row mb-1">
<div class="col-3"><label class="col-form-label w-100 h-100">Password</label></div>
<div class="col"><input class="w-100 h-100 ms-1 me-1" type="password" id="brokerpassword" placeholder="Password"></div>
</div>
<div class="row mb-1">
<div class="col-3"><label class="col-form-label w-100 h-100">Queue Name</label></div>
<div class="col"><input class="w-100 h-100 ms-1 me-1" type="text" id="brokerqueue" placeholder="Queue Name"></div>
</div>
</div><button class="btn btn-primary btn-lg mt-2" id="savefarm" type="button">Save</button>
</div>
</div>
<div class="card mb-3">
<div class="card-body">
<h4 class="card-title">AAS Setting</h4>
<div class="col">
<div class="row mb-1">
<div class="col-6"><input class="w-100 h-100 ms-1 me-1" type="text" placeholder="AAS IP 1"></div>
<div class="col-3"><input class="w-100 h-100 ms-1 me-1" type="text" placeholder="DB Username"></div>
<div class="col-3"><input class="w-100 h-100 ms-1 me-1" type="password" placeholder="DB Password"></div>
</div>
<div class="row mb-1">
<div class="col-6"><input class="w-100 h-100 ms-1 me-1" type="text" placeholder="AAS IP 2"></div>
<div class="col-3"><input class="w-100 h-100 ms-1 me-1" type="text" placeholder="DB Username"></div>
<div class="col-3"><input class="w-100 h-100 ms-1 me-1" type="password" placeholder="DB Password"></div>
</div>
<div class="row mb-1">
<div class="col-6"><input class="w-100 h-100 ms-1 me-1" type="text" placeholder="AAS IP 3"></div>
<div class="col-3"><input class="w-100 h-100 ms-1 me-1" type="text" placeholder="DB Username"></div>
<div class="col-3"><input class="w-100 h-100 ms-1 me-1" type="password" placeholder="DB Password"></div>
</div>
</div><button class="btn btn-primary btn-lg mt-2" id="saveaas" type="button">Save</button>
</div>
</div>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/jquery-3.7.1.min.js"></script>
</body>
</html>

View File

@@ -21,7 +21,11 @@ class ActiveMQClient {
private lateinit var destination : Queue private lateinit var destination : Queue
private lateinit var consumer: MessageConsumer private lateinit var consumer: MessageConsumer
var MessageConsumer : Consumer<Message>?= null var MessageConsumer : Consumer<Message>?= null
init{
/**
* Start ActiveMQ Connection
*/
fun Start(){
try{ try{
connection = ActiveMQConnectionFactory(config.ActiveMQ_BrokerURL, config.ActiveMQ_Username, config.ActiveMQ_Password).createConnection() connection = ActiveMQConnectionFactory(config.ActiveMQ_BrokerURL, config.ActiveMQ_Username, config.ActiveMQ_Password).createConnection()
connection.start() connection.start()
@@ -58,9 +62,11 @@ class ActiveMQClient {
} catch (e : Exception){ } catch (e : Exception){
Logger.error { "Failed to create connection, Message : ${e.message}" } Logger.error { "Failed to create connection, Message : ${e.message}" }
} }
} }
/**
* Stop ActiveMQ Connection
*/
fun Stop(){ fun Stop(){
try{ try{
consumer.close() consumer.close()

View File

@@ -9,8 +9,11 @@ fun main() {
config = Config() config = Config()
config.Load() config.Load()
val webUI = WebUI() val webUI = WebUI()
webUI.Start()
val activeclient = ActiveMQClient() val activeclient = ActiveMQClient()
activeclient.Start()
val mysql = MySQLInjector() val mysql = MySQLInjector()
mysql.Start()
activeclient.MessageConsumer = Consumer{ message -> activeclient.MessageConsumer = Consumer{ message ->

View File

@@ -16,6 +16,12 @@ class Config {
var WebPort : Int var WebPort : Int
get() = prop.getProperty("webport").toInt() get() = prop.getProperty("webport").toInt()
set(value) {prop.setProperty("webport", value.toString())} set(value) {prop.setProperty("webport", value.toString())}
var WebUsername : String
get() = prop.getProperty("webusername")
set(value) {prop.setProperty("webusername", value)}
var WebPassword : String
get() = prop.getProperty("webpassword")
set(value) {prop.setProperty("webpassword", value)}
var ActiveMQ_BrokerURL : String var ActiveMQ_BrokerURL : String
get() = prop.getProperty("activemq_brokerurl") get() = prop.getProperty("activemq_brokerurl")
set(value) {prop.setProperty("activemq_brokerurl", value)} set(value) {prop.setProperty("activemq_brokerurl", value)}
@@ -61,6 +67,8 @@ class Config {
if (Files.isRegularFile(Path(filename))){ if (Files.isRegularFile(Path(filename))){
prop.load(FileInputStream(filename)) prop.load(FileInputStream(filename))
if (!prop.contains("webport")) throw Exception("Invalid config file: missing 'webport'") if (!prop.contains("webport")) throw Exception("Invalid config file: missing 'webport'")
if (!prop.contains("webusername")) throw Exception("Invalid config file: missing 'webusername'")
if (!prop.contains("webpassword")) throw Exception("Invalid config file: missing 'webpassword'")
if (!prop.contains("activemq_brokerurl")) throw Exception("Invalid config file: missing 'activemq_brokerurl'") if (!prop.contains("activemq_brokerurl")) throw Exception("Invalid config file: missing 'activemq_brokerurl'")
if (!prop.contains("activemq_username")) throw Exception("Invalid config file: missing 'activemq_username'") if (!prop.contains("activemq_username")) throw Exception("Invalid config file: missing 'activemq_username'")
if (!prop.contains("activemq_password")) throw Exception("Invalid config file: missing 'activemq_password'") if (!prop.contains("activemq_password")) throw Exception("Invalid config file: missing 'activemq_password'")
@@ -103,6 +111,8 @@ class Config {
fun CreateDefault(){ fun CreateDefault(){
prop.clear() prop.clear()
prop.setProperty("webport", "7000") prop.setProperty("webport", "7000")
prop.setProperty("webusername", "admin")
prop.setProperty("webpassword", "admin")
prop.setProperty("activemq_brokerurl", "tcp://localhost:61616") prop.setProperty("activemq_brokerurl", "tcp://localhost:61616")
prop.setProperty("activemq_username", "admin") prop.setProperty("activemq_username", "admin")
prop.setProperty("activemq_password", "admin") prop.setProperty("activemq_password", "admin")

View File

@@ -2,20 +2,110 @@ package Web
import config import config
import io.javalin.Javalin import io.javalin.Javalin
import io.javalin.apibuilder.ApiBuilder.get
import io.javalin.apibuilder.ApiBuilder.path
import io.javalin.apibuilder.ApiBuilder.post
import io.javalin.apibuilder.ApiBuilder.ws
import org.tinylog.Logger
@Suppress("unused") @Suppress("unused")
/** /**
* Start WebUI Server * Start WebUI Server
*/ */
class WebUI{ class WebUI{
private var app : Javalin = Javalin.create { config -> private var app : Javalin = Javalin.create { cfg ->
config.staticFiles.add("/html") cfg.staticFiles.add("/html")
}.start(config.WebPort) cfg.router.apiBuilder {
path("/"){
get {
if (config.WebUsername==it.cookie("username")){
it.redirect("home.html")
} else{
it.redirect("login.html")
}
}
}
path("login"){
post{
val username = it.formParam("username")
val password = it.formParam("password")
if (config.WebUsername==username && config.WebPassword==password) {
it.cookie("username", username)
it.redirect("home.html")
} else {
it.redirect("/login.html?error=1")
}
}
}
path("logout"){
get {
it.removeCookie("username")
it.redirect("login.html")
}
}
path("login.html"){
get {
it.removeCookie("username")
}
}
path("home.html"){
get {
if (config.WebUsername!=it.cookie("username")){
it.redirect("login.html")
return@get
}
}
ws("/ws"){ ws ->
ws.onConnect { wsconnectcontext -> Logger.info { "WebSocket connected: ${wsconnectcontext.sessionId()}"; wsconnectcontext.enableAutomaticPings() } }
ws.onClose { wsclosecontext -> Logger.info { "WebSocket closed: ${wsclosecontext.sessionId()}" } }
ws.onError { wserrorcontext -> Logger.error { "WebSocket error in session ${wserrorcontext.sessionId()}: ${wserrorcontext.error()?.message}" } }
ws.onMessage { wsMessageContext ->
// TODO handle incoming messages
}
}
}
path("log.html"){
get {
if (config.WebUsername!=it.cookie("username")){
it.redirect("login.html")
return@get
}
val logdate = it.queryParam("logdate")
if (logdate.isNullOrEmpty()) return@get
}
}
path("setting.html"){
get {
if (config.WebUsername!=it.cookie("username")){
it.redirect("login.html")
return@get
}
// TODO send config values in JSON format
}
post {
// TODO save config values from form parameters
}
}
}
}
/**
* Start WebUI Server
*/
fun Start(){
app.start(config.WebPort)
}
/** /**
* Stop WebUI Server * Stop WebUI Server
*/ */
fun Stop(){ fun Stop(){
app.stop() app.stop()
} }
} }

View File

@@ -1,8 +1,18 @@
package database package database
@Suppress("UNUSED")
class MySQLInjector { class MySQLInjector {
/**
* Start MySQL Injector
*/
fun Start(){
}
/**
* Stop MySQL Injector
*/
fun Stop(){ fun Stop(){
} }