commit 09/02/2026

This commit is contained in:
2026-02-10 17:05:39 +07:00
parent e18976ace3
commit 546f2e27af
24 changed files with 1205 additions and 384 deletions

View File

@@ -0,0 +1,431 @@
/* !
*
* ../css/litepicker.css
* Litepicker v2.0.12 (https://github.com/wakirin/Litepicker)
* Package: litepicker (https://www.npmjs.com/package/litepicker)
* License: MIT (https://github.com/wakirin/Litepicker/blob/master/LICENCE.md)
* Copyright 2019-2021 Rinat G.
*
* Hash: 2f11f1f0300ea13b17b5
* */
:root {
--litepicker-container-months-color-bg: #fff;
--litepicker-container-months-box-shadow-color: #ddd;
--litepicker-footer-color-bg: #fafafa;
--litepicker-footer-box-shadow-color: #ddd;
--litepicker-tooltip-color-bg: #fff;
--litepicker-month-header-color: #333;
--litepicker-button-prev-month-color: #9e9e9e;
--litepicker-button-next-month-color: #9e9e9e;
--litepicker-button-prev-month-color-hover: #2196f3;
--litepicker-button-next-month-color-hover: #2196f3;
--litepicker-month-width: calc(var(--litepicker-day-width) * 7);
--litepicker-month-weekday-color: #9e9e9e;
--litepicker-month-week-number-color: #9e9e9e;
--litepicker-day-width: 38px;
--litepicker-day-color: #333;
--litepicker-day-color-hover: #2196f3;
--litepicker-is-today-color: #f44336;
--litepicker-is-in-range-color: #bbdefb;
--litepicker-is-locked-color: #9e9e9e;
--litepicker-is-start-color: #fff;
--litepicker-is-start-color-bg: #2196f3;
--litepicker-is-end-color: #fff;
--litepicker-is-end-color-bg: #2196f3;
--litepicker-button-cancel-color: #fff;
--litepicker-button-cancel-color-bg: #9e9e9e;
--litepicker-button-apply-color: #fff;
--litepicker-button-apply-color-bg: #2196f3;
--litepicker-button-reset-color: #909090;
--litepicker-button-reset-color-hover: #2196f3;
--litepicker-highlighted-day-color: #333;
--litepicker-highlighted-day-color-bg: #ffeb3b;
}
.show-week-numbers {
--litepicker-month-width: calc(var(--litepicker-day-width) * 8);
}
.litepicker {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
font-size: 0.8em;
display: none;
}
.litepicker button {
border: none;
background: none;
}
.litepicker .container__main {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
}
.litepicker .container__months {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
background-color: var(--litepicker-container-months-color-bg);
border-radius: 5px;
-webkit-box-shadow: 0 0 5px var(--litepicker-container-months-box-shadow-color);
box-shadow: 0 0 5px var(--litepicker-container-months-box-shadow-color);
width: calc(var(--litepicker-month-width) + 10px);
-webkit-box-sizing: content-box;
box-sizing: content-box;
}
.litepicker .container__months.columns-2 {
width: calc((var(--litepicker-month-width) * 2) + 20px);
}
.litepicker .container__months.columns-3 {
width: calc((var(--litepicker-month-width) * 3) + 30px);
}
.litepicker .container__months.columns-4 {
width: calc((var(--litepicker-month-width) * 4) + 40px);
}
.litepicker .container__months.split-view .month-item-header .button-previous-month, .litepicker .container__months.split-view .month-item-header .button-next-month {
visibility: visible;
}
.litepicker .container__months .month-item {
padding: 5px;
width: var(--litepicker-month-width);
-webkit-box-sizing: content-box;
box-sizing: content-box;
}
.litepicker .container__months .month-item-header {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
font-weight: 500;
padding: 10px 5px;
text-align: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
color: var(--litepicker-month-header-color);
}
.litepicker .container__months .month-item-header div {
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
}
.litepicker .container__months .month-item-header div > .month-item-name {
margin-right: 5px;
}
.litepicker .container__months .month-item-header div > .month-item-year {
padding: 0;
}
.litepicker .container__months .month-item-header .reset-button {
color: var(--litepicker-button-reset-color);
}
.litepicker .container__months .month-item-header .reset-button > svg {
fill: var(--litepicker-button-reset-color);
}
.litepicker .container__months .month-item-header .reset-button * {
pointer-events: none;
}
.litepicker .container__months .month-item-header .reset-button:hover {
color: var(--litepicker-button-reset-color-hover);
}
.litepicker .container__months .month-item-header .reset-button:hover > svg {
fill: var(--litepicker-button-reset-color-hover);
}
.litepicker .container__months .month-item-header .button-previous-month, .litepicker .container__months .month-item-header .button-next-month {
visibility: hidden;
text-decoration: none;
padding: 3px 5px;
border-radius: 3px;
-webkit-transition: color 0.3s, border 0.3s;
transition: color 0.3s, border 0.3s;
cursor: default;
}
.litepicker .container__months .month-item-header .button-previous-month *, .litepicker .container__months .month-item-header .button-next-month * {
pointer-events: none;
}
.litepicker .container__months .month-item-header .button-previous-month {
color: var(--litepicker-button-prev-month-color);
}
.litepicker .container__months .month-item-header .button-previous-month > svg, .litepicker .container__months .month-item-header .button-previous-month > img {
fill: var(--litepicker-button-prev-month-color);
}
.litepicker .container__months .month-item-header .button-previous-month:hover {
color: var(--litepicker-button-prev-month-color-hover);
}
.litepicker .container__months .month-item-header .button-previous-month:hover > svg {
fill: var(--litepicker-button-prev-month-color-hover);
}
.litepicker .container__months .month-item-header .button-next-month {
color: var(--litepicker-button-next-month-color);
}
.litepicker .container__months .month-item-header .button-next-month > svg, .litepicker .container__months .month-item-header .button-next-month > img {
fill: var(--litepicker-button-next-month-color);
}
.litepicker .container__months .month-item-header .button-next-month:hover {
color: var(--litepicker-button-next-month-color-hover);
}
.litepicker .container__months .month-item-header .button-next-month:hover > svg {
fill: var(--litepicker-button-next-month-color-hover);
}
.litepicker .container__months .month-item-weekdays-row {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
justify-self: center;
-webkit-box-pack: start;
-ms-flex-pack: start;
justify-content: flex-start;
color: var(--litepicker-month-weekday-color);
}
.litepicker .container__months .month-item-weekdays-row > div {
padding: 5px 0;
font-size: 85%;
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
width: var(--litepicker-day-width);
text-align: center;
}
.litepicker .container__months .month-item:first-child .button-previous-month {
visibility: visible;
}
.litepicker .container__months .month-item:last-child .button-next-month {
visibility: visible;
}
.litepicker .container__months .month-item.no-previous-month .button-previous-month {
visibility: hidden;
}
.litepicker .container__months .month-item.no-next-month .button-next-month {
visibility: hidden;
}
.litepicker .container__days {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
justify-self: center;
-webkit-box-pack: start;
-ms-flex-pack: start;
justify-content: flex-start;
text-align: center;
-webkit-box-sizing: content-box;
box-sizing: content-box;
}
.litepicker .container__days > div, .litepicker .container__days > a {
padding: 5px 0;
width: var(--litepicker-day-width);
}
.litepicker .container__days .day-item {
color: var(--litepicker-day-color);
text-align: center;
text-decoration: none;
border-radius: 3px;
-webkit-transition: color 0.3s, border 0.3s;
transition: color 0.3s, border 0.3s;
cursor: default;
}
.litepicker .container__days .day-item:hover {
color: var(--litepicker-day-color-hover);
-webkit-box-shadow: inset 0 0 0 1px var(--litepicker-day-color-hover);
box-shadow: inset 0 0 0 1px var(--litepicker-day-color-hover);
}
.litepicker .container__days .day-item.is-today {
color: var(--litepicker-is-today-color);
}
.litepicker .container__days .day-item.is-locked {
color: var(--litepicker-is-locked-color);
}
.litepicker .container__days .day-item.is-locked:hover {
color: var(--litepicker-is-locked-color);
-webkit-box-shadow: none;
box-shadow: none;
cursor: default;
}
.litepicker .container__days .day-item.is-in-range {
background-color: var(--litepicker-is-in-range-color);
border-radius: 0;
}
.litepicker .container__days .day-item.is-start-date {
color: var(--litepicker-is-start-color);
background-color: var(--litepicker-is-start-color-bg);
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.litepicker .container__days .day-item.is-start-date.is-flipped {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
}
.litepicker .container__days .day-item.is-end-date {
color: var(--litepicker-is-end-color);
background-color: var(--litepicker-is-end-color-bg);
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
}
.litepicker .container__days .day-item.is-end-date.is-flipped {
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.litepicker .container__days .day-item.is-start-date.is-end-date {
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
}
.litepicker .container__days .day-item.is-highlighted {
color: var(--litepicker-highlighted-day-color);
background-color: var(--litepicker-highlighted-day-color-bg);
}
.litepicker .container__days .week-number {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
color: var(--litepicker-month-week-number-color);
font-size: 85%;
}
.litepicker .container__footer {
text-align: right;
padding: 10px 5px;
margin: 0 5px;
background-color: var(--litepicker-footer-color-bg);
-webkit-box-shadow: inset 0px 3px 3px 0px var(--litepicker-footer-box-shadow-color);
box-shadow: inset 0px 3px 3px 0px var(--litepicker-footer-box-shadow-color);
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
.litepicker .container__footer .preview-date-range {
margin-right: 10px;
font-size: 90%;
}
.litepicker .container__footer .button-cancel {
background-color: var(--litepicker-button-cancel-color-bg);
color: var(--litepicker-button-cancel-color);
border: 0;
padding: 3px 7px 4px;
border-radius: 3px;
}
.litepicker .container__footer .button-cancel * {
pointer-events: none;
}
.litepicker .container__footer .button-apply {
background-color: var(--litepicker-button-apply-color-bg);
color: var(--litepicker-button-apply-color);
border: 0;
padding: 3px 7px 4px;
border-radius: 3px;
margin-left: 10px;
margin-right: 10px;
}
.litepicker .container__footer .button-apply:disabled {
opacity: 0.7;
}
.litepicker .container__footer .button-apply * {
pointer-events: none;
}
.litepicker .container__tooltip {
position: absolute;
margin-top: -4px;
padding: 4px 8px;
border-radius: 4px;
background-color: var(--litepicker-tooltip-color-bg);
-webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.25);
box-shadow: 0 1px 3px rgba(0,0,0,0.25);
white-space: nowrap;
font-size: 11px;
pointer-events: none;
visibility: hidden;
}
.litepicker .container__tooltip:before {
position: absolute;
bottom: -5px;
left: calc(50% - 5px);
border-top: 5px solid rgba(0,0,0,0.12);
border-right: 5px solid transparent;
border-left: 5px solid transparent;
content: "";
}
.litepicker .container__tooltip:after {
position: absolute;
bottom: -4px;
left: calc(50% - 4px);
border-top: 4px solid var(--litepicker-tooltip-color-bg);
border-right: 4px solid transparent;
border-left: 4px solid transparent;
content: "";
}

File diff suppressed because one or more lines are too long

View File

@@ -21,7 +21,7 @@ dtLog = null;
function fill_logtablebody(vv) {
dtLog.clear();
if (!Array.isArray(vv) || vv.length === 0) {
$('#btnExport').prop('disabled', true);
//$('#btnExport').prop('disabled', true);
return;
}
dtLog.rows.add(vv);
@@ -29,7 +29,7 @@ function fill_logtablebody(vv) {
$('#tablesize').text("Table Size: " + vv.length);
$('#btnExport').prop('disabled', false);
//$('#btnExport').prop('disabled', false);
}
/**
@@ -43,8 +43,10 @@ function reloadLogs(APIURL = "Log/", date, filter) {
date: date,
filter: filter
})
console.log("Loading logs with params: " + params.toString());
window.logdata = [];
fetchAPI(APIURL + "List?" + params.toString(), "GET", {}, null, (okdata) => {
console.log("Logs loaded: " + okdata.length);
if (Array.isArray(okdata)) {
window.logdata.push(...okdata);
fill_logtablebody(window.logdata);
@@ -54,16 +56,33 @@ function reloadLogs(APIURL = "Log/", date, filter) {
});
}
datepicker = null;
$btnGet = null;
$(document).ready(function () {
console.log("log.js ready");
let selectedlogdate = "";
let logfilter = "";
let APIURL = "Log/";
$btnGet = $('#btnGet');
datepicker = new Litepicker({
element: document.getElementById('logdate'),
format: 'DD/MM/YYYY',
lang: 'en-US',
autoApply: true,
singleMode: true,
startDate: new Date(),
onSelect: (date) => {
selectedlogdate = date.format('DD/MM/YYYY');
console.log("Selected date: " + selectedlogdate);
}
})
if (dtLog === null) {
dtLog = new DataTable('#logtable', {
dom: 'Bfrtip',
data: [],
pageLength: 25,
columns: [
@@ -72,36 +91,42 @@ $(document).ready(function () {
{ title: "Time", data: "timenya" },
{ title: "Machine", data: "machine" },
{ title: "Description", data: "description" }
]
],
buttons: ['print', 'pdf', 'excel']
});
}
if (!$('#logdate').val()) {
const today = new Date();
const dd = String(today.getDate()).padStart(2, '0');
const mm = String(today.getMonth() + 1).padStart(2, '0');
const yyyy = today.getFullYear();
$('#logdate').val(`${yyyy}-${mm}-${dd}`);
selectedlogdate = `${dd}-${mm}-${yyyy}`;
reloadLogs(APIURL, selectedlogdate, logfilter);
}
$('#logdate').off('change').on('change', function () {
const selected = $(this).val();
if (selected) {
const [year, month, day] = selected.split('-');
selectedlogdate = `${day}-${month}-${year}`;
reloadLogs(APIURL, selectedlogdate, logfilter);
// findalldate is checkbox, if checked will disable datepicker
$('#findalldate').off('change').on('change', function () {
if ($(this).is(':checked')) {
datepicker.disabled = true;
selectedlogdate = "alldate";
console.log("Find all date checked, omitting date filter");
} else {
datepicker.disabled = false;
const date = datepicker.getDate();
selectedlogdate = date.format('DD/MM/YYYY');
console.log("Find all date unchecked, selected date: " + selectedlogdate);
}
});
$('#searchfilter').off('input').on('input', function () {
logfilter = $(this).val();
//reloadLogs(APIURL, selectedlogdate, logfilter);
});
$btnGet.click(function () {
let checked = $('#findalldate').is(':checked');
if (checked && logfilter.trim() === "") {
alert("Please enter a filter when 'Find All Date' is checked to avoid large data load.");
return;
}
//$(this).data('selectedlogdate', selectedlogdate);
//$(this).data('logfilter', logfilter);
reloadLogs(APIURL, selectedlogdate, logfilter);
});
$('#btnExport').off('click').on('click', function () {
DoExport(APIURL, "log.xlsx", { date: selectedlogdate, filter: logfilter });
});
selectedlogdate = datepicker.getDate().format('DD/MM/YYYY');
console.log("Initial selected date: " + selectedlogdate);
$btnGet.trigger('click'); // load logs on page load
});

View File

@@ -22,6 +22,15 @@ window.schedulebankdata = [];
window.selectedschedulerow = null;
dtScheduleBank = null;
dtTodaySchedule = null;
function fill_todayscheduletablebody(vv) {
dtTodaySchedule.clear();
if (!Array.isArray(vv) || vv.length === 0) return;
dtTodaySchedule.rows.add(vv);
dtTodaySchedule.draw();
}
/**
* Fill schedulebank table body with values
@@ -117,7 +126,19 @@ function reloadTimerBank(APIURL = "ScheduleBank/") {
});
}
function reloadTodaySchedule(APIURL = "ScheduleBank/") {
fetchAPI(APIURL + "TodaySchedule", "GET", {}, null, (okdata) => {
if (Array.isArray(okdata)) {
console.log("Today's Schedule: ", okdata);
fill_todayscheduletablebody(okdata);
}
}, (errdata) => {
alert("Error loading today's schedule : " + errdata.message);
});
}
dayViewMode = 'all'; // all, everyday, monday, tuesday, wednesday, thursday, friday, saturday, sunday
scheduledate = null; // Litepicker instance for schedule date selection
$(document).ready(function () {
console.log("schedulebank.js loaded successfully");
@@ -169,6 +190,26 @@ $(document).ready(function () {
});
}
if (dtTodaySchedule === null) {
dtTodaySchedule = new DataTable('#todaytable', {
dom: 'Bfrtip',
data: [],
pageLength: 25,
columns: [
{ title: "No", data: "index" },
{ title: "Description", data: "description" },
{ title: "Day", data: "day" },
{ title: "Time", data: "time" },
{ title: "Message", data: "soundpath" },
{ title: "Repeat", data: "repeat" },
{ title: "Enable", data: "enable" },
{ title: "Broadcast Zones", data: "broadcastZones" },
{ title: "Language", data: "language" }
],
buttons: ['print', 'pdf']
});
}
$.fn.dataTable.ext.search.push(function (settings, data, dataIndex, rowData) {
if (settings.nTable.id !== 'schedulebanktable') return true;
switch (dayViewMode) {
@@ -223,16 +264,30 @@ $(document).ready(function () {
let $weeklyselect = $schedulemodal.find('#weeklyselect');
// radio button for specific date
let $schedulespecialdate = $schedulemodal.find('#schedulespecialdate');
scheduledate = new Litepicker({
element: document.getElementById('scheduledate'),
format: 'DD/MM/YYYY',
lang: 'en-US',
autoApply: true,
singleMode: true,
startDate: new Date(),
onSelect: (date) => {
console.log("Selected special date: " + date.format('DD/MM/YYYY'));
}
})
// date input
let $scheduledate = $schedulemodal.find('#scheduledate');
//let $scheduledate = $schedulemodal.find('#scheduledate');
// select2 for language
let $languageselect = $schedulemodal.find('#languageselect');
$schedulespecialdate.off('change').on('change', function () {
if ($(this).is(':checked')) {
$scheduledate.prop('disabled', false);
//$scheduledate.prop('disabled', false);
scheduledate.disabled = false
} else {
$scheduledate.prop('disabled', true);
//$scheduledate.prop('disabled', true);
scheduledate.disabled = true
}
});
@@ -261,7 +316,8 @@ $(document).ready(function () {
dropdownParent: $('#schedulemodal')
});
$scheduledate.prop('disabled', true).val('');
//$scheduledate.prop('disabled', true).val('');
scheduledate.disabled = true;
$schedulezones.empty().select2({
data: window.BroadcastZoneList.map(zone => ({ id: zone.description, text: zone.description })),
placeholder: 'Select broadcast zones',
@@ -284,24 +340,28 @@ $(document).ready(function () {
$scheduleeveryday.off('change').on('change', function () {
if ($(this).is(':checked')) {
$weeklyselect.prop('disabled', true);
$scheduledate.prop('disabled', true);
//$scheduledate.prop('disabled', true);
scheduledate.disabled = true;
}
});
$scheduleweekly.off('change').on('change', function () {
if ($(this).is(':checked')) {
$weeklyselect.prop('disabled', false);
$scheduledate.prop('disabled', true);
//$scheduledate.prop('disabled', true);
scheduledate.disabled = true;
}
});
$schedulespecialdate.off('change').on('change', function () {
if ($(this).is(':checked')) {
$weeklyselect.prop('disabled', true);
$scheduledate.prop('disabled', false);
//$scheduledate.prop('disabled', false);
scheduledate.disabled = false;
}
});
}
reloadTimerBank(APIURL);
reloadTodaySchedule(APIURL);
reloadBroadcastZones();
getLanguages();
getScheduledDays();
@@ -309,6 +369,7 @@ $(document).ready(function () {
$btnClear.click(() => {
DoClear(APIURL, "Timerbank", (okdata) => {
reloadTimerBank(APIURL);
reloadTodaySchedule(APIURL);
alert("Success clear schedulebank : " + okdata.message);
}, (errdata) => {
alert("Error clear schedulebank : " + errdata.message);
@@ -317,7 +378,9 @@ $(document).ready(function () {
});
$btnAdd.click(() => {
$schedulemodal.modal('show');
clearScheduleModal();
$scheduleeveryday.prop('checked', true).trigger('click');
$schedulemodal.off('click.scheduleclose').on('click.scheduleclose', '#scheduleclose', function () {
$schedulemodal.modal('hide');
@@ -333,7 +396,7 @@ $(document).ready(function () {
if ($scheduleeveryday.is(':checked')) {
_Day = "Everyday";
} else if ($schedulespecialdate.is(':checked')) {
_Day = Convert_input_date_to_string($scheduledate.val());
_Day = Convert_input_date_to_string(scheduledate.getDate().format('DD/MM/YYYY'));
} else if ($scheduleweekly.is(':checked')) {
_Day = $weeklyselect.val();
}
@@ -363,6 +426,7 @@ $(document).ready(function () {
fetchAPI(APIURL + "Add", "POST", {}, scheduleObj, (okdata) => {
reloadTimerBank(APIURL);
reloadTodaySchedule(APIURL);
alert("Success add schedule: " + okdata.message);
}, (errdata) => {
alert("Error add schedule: " + errdata.message);
@@ -393,6 +457,7 @@ $(document).ready(function () {
if (confirm(`Are you sure to delete schedule [${sr.index}] Description=${sr.description}?`)) {
fetchAPI(APIURL + "DeleteByIndex/" + sr.index, "DELETE", {}, null, (okdata) => {
reloadTimerBank(APIURL);
reloadTodaySchedule(APIURL);
alert("Success delete schedule : " + okdata.message);
}, (errdata) => {
alert("Error delete schedule : " + errdata.message);
@@ -438,7 +503,8 @@ $(document).ready(function () {
$weeklyselect.val(null).trigger('change');
$weeklyselect.prop('disabled', true);
$scheduledate.prop('disabled', true);
//$scheduledate.prop('disabled', true);
scheduledate.disabled = true;
break;
case 'Sunday':
case 'Monday':
@@ -452,7 +518,8 @@ $(document).ready(function () {
$weeklyselect.val(sr.Day).trigger('change');
$weeklyselect.prop('disabled', false);
$scheduledate.prop('disabled', true);
//$scheduledate.prop('disabled', true);
scheduledate.disabled = true;
break;
default:
console.log("Assuming special date for Day: ", sr.Day);
@@ -461,8 +528,10 @@ $(document).ready(function () {
if (/^\d{2}\/\d{2}\/\d{4}$/.test(sr.Day)) {
$schedulespecialdate.prop('checked', true);
$scheduledate.val(Convert_string_to_input_date(sr.Day));
$scheduledate.prop('disabled', false);
//$scheduledate.val(Convert_string_to_input_date(sr.Day));
// $scheduledate.prop('disabled', false);
scheduledate.setDate(dayjs(sr.Day,'DD/MM/YYYY'));
scheduledate.disabled = false;
$weeklyselect.val(null).trigger('change');
$weeklyselect.prop('disabled', true);
@@ -484,7 +553,8 @@ $(document).ready(function () {
Day = "Everyday";
} else if ($schedulespecialdate.is(':checked')) {
// convert date from yyyy-mm-dd to dd/mm/yyyy
Day = Convert_input_date_to_string($scheduledate.val());
//Day = Convert_input_date_to_string($scheduledate.val());
Day = scheduledate.getDate().format('DD/MM/YYYY');
} else if ($scheduleweekly.is(':checked')) {
Day = $weeklyselect.val();
}
@@ -516,6 +586,7 @@ $(document).ready(function () {
fetchAPI(APIURL + "UpdateByIndex/" + sr.index, "PATCH", {}, scheduleObj, (okdata) => {
alert("Success edit schedule: " + okdata.message);
reloadTimerBank(APIURL);
reloadTodaySchedule(APIURL);
}, (errdata) => {
alert("Error edit schedule: " + errdata.message);
});
@@ -537,6 +608,7 @@ $(document).ready(function () {
$btnImport.click(() => {
DoImport(APIURL, (okdata) => {
reloadTimerBank(APIURL);
reloadTodaySchedule(APIURL);
alert("Success import schedulebank from XLSX : " + okdata.message);
}, (errdata) => {
alert("Error importing schedulebank from XLSX : " + errdata.message);

View File

@@ -292,9 +292,9 @@
</div>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/bs-init.js"></script>
<script src="assets/js/datatables.js"></script>
<script src="assets/js/soundchannel.js"></script>
<script src="assets/js/broadcastzones.js"></script>
<script src="assets/js/datatables.js"></script>
</body>
</html>

View File

@@ -159,10 +159,10 @@
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/bs-init.js"></script>
<script src="assets/js/jquery-3.7.1.min.js"></script>
<script src="assets/js/script.js"></script>
<script src="assets/js/all.min.js"></script>
<script src="assets/js/datatables.js"></script>
<script src="assets/js/select2.min.js"></script>
<script src="assets/js/datatables.js"></script>
<script src="assets/js/all.min.js"></script>
<script src="assets/js/script.js"></script>
</body>
</html>

View File

@@ -119,10 +119,10 @@
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/bs-init.js"></script>
<script src="assets/js/jquery-3.7.1.min.js"></script>
<script src="assets/js/script.js"></script>
<script src="assets/js/all.min.js"></script>
<script src="assets/js/datatables.js"></script>
<script src="assets/js/select2.min.js"></script>
<script src="assets/js/datatables.js"></script>
<script src="assets/js/all.min.js"></script>
<script src="assets/js/script.js"></script>
</body>
</html>

View File

@@ -99,8 +99,8 @@
</div>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/bs-init.js"></script>
<script src="assets/js/languagelink.js"></script>
<script src="assets/js/datatables.js"></script>
<script src="assets/js/languagelink.js"></script>
</body>
</html>

View File

@@ -15,6 +15,7 @@
<link rel="stylesheet" href="assets/css/FontAwesome.css">
<link rel="stylesheet" href="assets/css/bss-overrides.css">
<link rel="stylesheet" href="assets/css/datatables.css">
<link rel="stylesheet" href="assets/css/litepicker.css">
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
<link rel="stylesheet" href="assets/css/styles.css">
</head>
@@ -26,16 +27,26 @@
</div>
</div>
<div class="row">
<div class="col-4 col-sm-4 col-md-2 col-lg-2 col-xl-2">
<p class="text-add">Select Log Date</p>
<div class="col-5 ms-1 me-1">
<div class="row">
<div class="col-auto">
<p class="text-add">Select Log Date</p>
</div>
<div class="col-auto"><input type="text" id="logdate" class="form-control"></div>
<div class="col">
<div class="form-check w-100 h-100 align-content-center"><input class="form-check-input" type="checkbox" id="findalldate"><label class="form-check-label" for="formCheck-1">All Date</label></div>
</div>
</div>
</div>
<div class="col-4 col-sm-4 col-md-2 col-lg-2 col-xl-2"><input id="logdate" class="form-control" type="date"></div>
<div class="col-4 col-sm-4 col-md-2 col-lg-2 col-xl-2"><button class="btn w-100 pad-button btn-round-basic color-import" id="btnExport" type="button">Export</button></div>
<div class="col-md-2 col-lg-2 col-xl-2"></div>
<div class="col-4 col-sm-4 col-md-2 col-lg-2 col-xl-2 d-none">
<p class="text-add">Search</p>
<div class="col-5 ms-1 me-1">
<div class="row">
<div class="col-auto">
<p class="text-add">Search</p>
</div>
<div class="col"><input type="text" id="searchfilter" class="form-control" placeholder="Search Filter"></div>
</div>
</div>
<div class="col-4 col-sm-4 col-md-2 col-lg-2 col-xl-2 d-none"><input type="text" id="searchfilter" class="form-control" placeholder="Search Filter"></div>
<div class="col ms-1 me-1"><button class="btn w-100 pad-button btn-round-basic color-import" id="btnGet" type="button">Get</button></div>
</div>
<div class="row">
<div class="col">
@@ -60,8 +71,9 @@
</div>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/bs-init.js"></script>
<script src="assets/js/log.js"></script>
<script src="assets/js/litepicker.js"></script>
<script src="assets/js/datatables.js"></script>
<script src="assets/js/log.js"></script>
</body>
</html>

View File

@@ -136,8 +136,8 @@
</div>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/bs-init.js"></script>
<script src="assets/js/messagebank.js"></script>
<script src="assets/js/datatables.js"></script>
<script src="assets/js/messagebank.js"></script>
</body>
</html>

View File

@@ -120,8 +120,8 @@
</div>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/bs-init.js"></script>
<script src="assets/js/soundbank.js"></script>
<script src="assets/js/datatables.js"></script>
<script src="assets/js/soundbank.js"></script>
</body>
</html>

View File

@@ -15,6 +15,7 @@
<link rel="stylesheet" href="assets/css/FontAwesome.css">
<link rel="stylesheet" href="assets/css/bss-overrides.css">
<link rel="stylesheet" href="assets/css/datatables.css">
<link rel="stylesheet" href="assets/css/litepicker.css">
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
<link rel="stylesheet" href="assets/css/styles.css">
</head>
@@ -25,46 +26,6 @@
<h2 style="text-align: center;">Schedule Bank</h2>
</div>
</div>
<div class="row d-none pad-row-search">
<div class="col-md-7 col-lg-7 col-xl-7"></div>
<div class="col-2 col-sm-2 col-md-2 col-lg-2 col-xl-2 search">
<p class="text-add">Search</p>
</div>
<div class="col-10 col-sm-10 col-md-3 col-lg-3 col-xl-3"><input class="w-100 form-control" type="text" id="findschedule" placeholder="Search keyword" name="findsoundbank"></div>
</div>
<div class="row">
<div class="col-3 col-sm-3 col-md-3 col-lg-2 col-xl-2"><button class="btn w-100 pad-button btn-round-basic color-add" id="btnClear" type="button">Clear</button></div>
<div class="col-3 col-sm-3 col-md-3 col-lg-2 col-xl-2"><button class="btn w-100 pad-button btn-round-basic color-add" id="btnAdd" type="button">Add</button></div>
<div class="col-3 col-sm-3 col-md-3 col-lg-2 col-xl-2"><button class="btn w-100 pad-button btn-round-basic color-remove" id="btnRemove" type="button">Remove</button></div>
<div class="col-3 col-sm-3 col-md-3 col-lg-2 col-xl-2"><button class="btn w-100 pad-button btn-round-basic color-edit" id="btnEdit" type="button">Edit</button></div>
<div class="col-6 col-sm-6 col-md-6 col-lg-2 col-xl-2"><button class="btn w-100 pad-button btn-round-basic color-import" id="btnExport" type="button">Export</button></div>
<div class="col-6 col-sm-6 col-md-6 col-lg-2 col-xl-2"><button class="btn w-100 pad-button btn-round-basic color-import" id="btnImport" type="button">Import</button></div>
</div>
<div class="row">
<div class="col">
<p id="tablesize" class="text-add" style="text-align: center;">Table Length : N/A</p>
</div>
</div>
<div class="row">
<div class="table-responsive">
<table class="table" id="schedulebanktable">
<thead>
<tr>
<th class="class05">No</th>
<th class="class15">Description</th>
<th class="class15">Day</th>
<th class="class10">Time</th>
<th class="class15">Sound Path</th>
<th class="class10">Repeat</th>
<th class="class05">Enable</th>
<th class="class15">Broadcast Zones</th>
<th class="class10">Language</th>
</tr>
</thead>
<tbody id="schedulebanktablebody"></tbody>
</table>
</div>
</div>
<div class="modal fade" role="dialog" tabindex="-1" id="schedulemodal">
<div class="modal-dialog" role="document">
<div class="modal-content">
@@ -106,10 +67,10 @@
</div>
</div>
<div class="row py-2">
<div class="col-7 col-sm-7 col-md-7 col-lg-6 col-xl-6 pad-day">
<div class="col-auto pad-day">
<div class="form-check"><input class="form-check-input" type="radio" id="schedulespecialdate" name="dayselect"><label class="form-check-label" for="schedulespecialdate">Special Date</label></div>
</div>
<div class="col-sm-5 col-md-5 col-lg-6 col-xl-6"><input id="scheduledate" class="form-control" type="date"></div>
<div class="col"><input type="text" id="scheduledate" class="form-control"></div>
</div>
</div>
</div>
@@ -181,10 +142,80 @@
</div>
</div>
</div>
<div class="accordion" role="tablist" id="accordion-1">
<div class="accordion-item">
<h2 class="accordion-header" role="tab"><button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#accordion-1 .item-1" aria-expanded="false" aria-controls="accordion-1 .item-1">Schedule Table</button></h2>
<div class="accordion-collapse collapse item-1" role="tabpanel" data-bs-parent="#accordion-1">
<div class="accordion-body">
<div class="row">
<div class="col-3 col-sm-3 col-md-3 col-lg-2 col-xl-2"><button class="btn w-100 pad-button btn-round-basic color-add" id="btnClear" type="button">Clear</button></div>
<div class="col-3 col-sm-3 col-md-3 col-lg-2 col-xl-2"><button class="btn w-100 pad-button btn-round-basic color-add" id="btnAdd" type="button">Add</button></div>
<div class="col-3 col-sm-3 col-md-3 col-lg-2 col-xl-2"><button class="btn w-100 pad-button btn-round-basic color-remove" id="btnRemove" type="button">Remove</button></div>
<div class="col-3 col-sm-3 col-md-3 col-lg-2 col-xl-2"><button class="btn w-100 pad-button btn-round-basic color-edit" id="btnEdit" type="button">Edit</button></div>
<div class="col-6 col-sm-6 col-md-6 col-lg-2 col-xl-2"><button class="btn w-100 pad-button btn-round-basic color-import" id="btnExport" type="button">Export</button></div>
<div class="col-6 col-sm-6 col-md-6 col-lg-2 col-xl-2"><button class="btn w-100 pad-button btn-round-basic color-import" id="btnImport" type="button">Import</button></div>
</div>
<div class="row">
<div class="col">
<p id="tablesize" class="text-add" style="text-align: center;">Table Length : N/A</p>
</div>
</div>
<div class="row">
<div class="table-responsive">
<table class="table" id="schedulebanktable">
<thead>
<tr>
<th class="class05">No</th>
<th class="class15">Description</th>
<th class="class15">Day</th>
<th class="class10">Time</th>
<th class="class15">Sound Path</th>
<th class="class10">Repeat</th>
<th class="class05">Enable</th>
<th class="class15">Broadcast Zones</th>
<th class="class10">Language</th>
</tr>
</thead>
<tbody id="schedulebanktablebody"></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header" role="tab"><button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#accordion-1 .item-2" aria-expanded="true" aria-controls="accordion-1 .item-2">Today's Schedule</button></h2>
<div class="accordion-collapse collapse show item-2" role="tabpanel" data-bs-parent="#accordion-1">
<div class="accordion-body">
<div class="row">
<div class="table-responsive">
<table class="table" id="todaytable">
<thead>
<tr>
<th class="class05">No</th>
<th class="class15">Description</th>
<th class="class15">Day</th>
<th class="class10">Time</th>
<th class="class15">Sound Path</th>
<th class="class10">Repeat</th>
<th class="class05">Enable</th>
<th class="class15">Broadcast Zones</th>
<th class="class10">Language</th>
</tr>
</thead>
<tbody id="schedulebanktablebody-1"></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/bs-init.js"></script>
<script src="assets/js/schedulebank.js"></script>
<script src="assets/js/litepicker.js"></script>
<script src="assets/js/datatables.js"></script>
<script src="assets/js/schedulebank.js"></script>
</body>
</html>

View File

@@ -16,6 +16,10 @@ import content.Language
import content.VoiceType
import database.data.Log
import database.MariaDB
import database.table.Table_BroadcastZones
import database.table.Table_Logs
import database.table.Table_Messagebank
import database.table.Table_SoundChannel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
@@ -44,8 +48,12 @@ const val version = "0.0.29 (09/02/2026)"
const val max_channel = 64
val apptick : Long = System.currentTimeMillis()
// dipakai untuk ambil messagebank berdasarkan id
// 4 tabel utama , kepakai dimana-mana, ditaruh di root biar gampang aksesnya
lateinit var broadcastDB: Table_BroadcastZones
lateinit var soundchannelDB: Table_SoundChannel
lateinit var messageDB: Table_Messagebank
lateinit var logDB: Table_Logs
val contentCache = ContentCache()
@@ -235,14 +243,14 @@ fun main(args: Array<String>) {
val androidserver = TCP_Android_Command_Server()
androidserver.StartTcpServer(5003){
Logger.info { it }
db.logDB.Add(Log.NewLog("ANDROID", it))
logDB.Add(Log.NewLog("ANDROID", it))
}
val barixserver = TCP_Barix_Command_Server()
barixserver.StartTcpServer { cmd ->
val _tcp = barixserver.getSocket(cmd.ipaddress)
val _streamer = StreamerOutputs[cmd.ipaddress]
val _sc = db.soundchannelDB.List.find { it.ip == cmd.ipaddress }
val _sc = soundchannelDB.List.find { it.ip == cmd.ipaddress }
if (_streamer == null) {
// belum create BarixConnection untuk ipaddress ini
@@ -297,13 +305,13 @@ fun main(args: Array<String>) {
}
db.logDB.Add("AAS"," Application started")
logDB.Add("AAS"," Application started")
// shutdown hook
Runtime.getRuntime().addShutdownHook(Thread ({
db.logDB.Add("AAS"," Application stopping")
logDB.Add("AAS"," Application stopping")
Logger.info { "Shutdown hook called, stopping services..." }
barixserver.StopTcpCommand()
androidserver.StopTcpCommand()

View File

@@ -7,7 +7,6 @@ import codes.Somecodes.Companion.IsNumber
import codes.Somecodes.Companion.Make_WAV_FileName
import codes.Somecodes.Companion.SoundbankResult_directory
import codes.Somecodes.Companion.ValidFile
import codes.Somecodes.Companion.ValidIPV4
import codes.Somecodes.Companion.ValidString
import codes.Somecodes.Companion.dateformat1
import codes.Somecodes.Companion.datetimeformat1
@@ -20,7 +19,6 @@ import content.VoiceType
import database.data.Messagebank
import database.data.QueueTable
import database.data.Soundbank
import org.tinylog.Logger
import java.time.DayOfWeek
import java.time.LocalDate
@@ -37,93 +35,7 @@ import java.time.LocalTime
*/
class MainExtension01 {
data class InvalidZoneDetail(val zonename: String, val reason: String)
data class ValidZoneDetail(
val zonename: String,
val soundchanel: String,
val ip: String,
val boxid: String,
val contacts: String
)
class CheckBroadcastZoneResult {
var allvalid: Boolean = false
var message: String? = null
var validzones = mutableListOf<ValidZoneDetail>()
var invalidzones = mutableListOf<InvalidZoneDetail>()
}
/**
* Fungsi untuk cek apakah semua broadcast zone valid
* Valid berarti nama broadcast zone ada di tabel BroadcastZones, dan SoundChannel-nya ada di tabel SoundChannel, dan IP-nya valid
* @param bz List of broadcast zone (SoundChannel)
* @return CheckBroadcastZoneResult object containing allvalid flag and list of invalid zones
*/
fun AllBroadcastZonesValid(bz: List<String>): CheckBroadcastZoneResult {
val result = CheckBroadcastZoneResult()
if (bz.isNotEmpty()) {
bz.forEach { zz ->
if (ValidString(zz)) { // string tidak kosong
val findzone = db.broadcastDB.List.find {
ValidString(it.description) && ValidString(it.SoundChannel) && zz.equals(
it.description,
true
)
}
if (findzone != null) { // ketemu zona dengan deskripsi sesuai
val findsc = db.soundchannelDB.List.find {
findzone.SoundChannel.equals(
it.channel,
true
) && ValidIPV4(it.ip)
}
if (findsc != null) { // ketemu soundchannel dengan channel sesuai dan IP valid
// check apakah offline atau online
if (StreamerOutputs.containsKey(findsc.ip)) {
val bc = StreamerOutputs[findsc.ip]
if (bc != null && bc.isOnline()) {
result.validzones.add(
ValidZoneDetail(
zz,
findzone.SoundChannel,
findsc.ip,
findzone.id,
findzone.bp
)
)
} else result.invalidzones.add(
InvalidZoneDetail(
zz,
"SoundChannel ${findzone.SoundChannel} with IP ${findsc.ip} is offline"
)
)
} else result.invalidzones.add(
InvalidZoneDetail(
zz,
"SoundChannel ${findzone.SoundChannel} with IP ${findsc.ip} is not connected in StreamerOutputs"
)
)
} else result.invalidzones.add(
InvalidZoneDetail(
zz,
"SoundChannel ${findzone.SoundChannel} not found or has invalid IP in SoundChannel table"
)
)
} else result.invalidzones.add(InvalidZoneDetail(zz, "Zone $zz not found in BroadcastZones table"))
} else result.invalidzones.add(InvalidZoneDetail(zz, "Invalid broadcast zone string"))
}
if (result.validzones.size == bz.size) {
result.allvalid = true
result.message = "All requested broadcast zones are valid"
} else {
result.message = "Some requested broadcast zones are not registered in BroadcastZone table"
}
} else {
result.message = "No Broadcast Zones checked for validity"
}
return result
}
/**
@@ -168,7 +80,7 @@ class MainExtension01 {
var selected_voice = config.Get(configKeys.DEFAULT_VOICE_TYPE.key)
if (selected_voice.isEmpty()) selected_voice = VoiceType.VOICE_1.name
languages.forEach { lang ->
db.messageDB.List
messageDB.List
.find { mb ->
mb.ANN_ID == id.toUInt() && mb.Language.equals(lang, true) && mb.Voice_Type.equals(
selected_voice,
@@ -771,7 +683,7 @@ class MainExtension01 {
if (!ValidFile(qp.Message)) throw Exception("Invalid audio file ${qp.Message}")
if (qp.BroadcastZones.isEmpty()) throw Exception("Empty broadcast zones")
val zz = qp.BroadcastZones.split(";", ",").map { it.trim() }.filter { ValidString(it) }
AllBroadcastZonesValid(zz).let { checkresult ->
broadcastDB.AllBroadcastZonesValid(zz).let { checkresult ->
if (!checkresult.allvalid) {
val reasons =
checkresult.invalidzones.joinToString("; ") { iz -> "Zone '${iz.zonename}': ${iz.reason}" }
@@ -793,10 +705,10 @@ class MainExtension01 {
so.ActivateRelay(relays)
so.SendData(afi.bytes, cbOK = {
so.SetAudioFileInfo(null)
db.logDB.Add("AAS", it)
logDB.Add("AAS", it)
}, cbFail = {
so.SetAudioFileInfo(null)
db.logDB.Add("AAS", it)
logDB.Add("AAS", it)
}, cbPlaying = { isplaying ->
if (!isplaying) {
so.ClearUsedByBroadcastZones()
@@ -809,7 +721,7 @@ class MainExtension01 {
val logmessage =
"Broadcast started PAGING with Filename '${qp.Message}' to zones: ${qp.BroadcastZones}"
Logger.info { logmessage }
db.logDB.Add("AAS", logmessage)
logDB.Add("AAS", logmessage)
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
db.queuepagingDB.Resort()
return true
@@ -819,7 +731,7 @@ class MainExtension01 {
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
db.queuepagingDB.Resort()
val msg = "Canceled paging message $qp due to exception: ${e.message}"
db.logDB.Add("AAS", msg)
logDB.Add("AAS", msg)
Logger.error { msg }
}
return false
@@ -840,7 +752,7 @@ class MainExtension01 {
if (mblist.isEmpty()) throw Exception("ANN_ID $ann_id not found in Messagebank")
if (qp.BroadcastZones.isEmpty()) throw Exception("Empty broadcast zones")
val zz = qp.BroadcastZones.split(";", ",").map { it.trim() }.filter { ValidString(it) }
AllBroadcastZonesValid(zz).let { checkresult ->
broadcastDB.AllBroadcastZonesValid(zz).let { checkresult ->
if (!checkresult.allvalid) {
val reasons =
checkresult.invalidzones.joinToString("; ") { iz -> "Zone '${iz.zonename}': ${iz.reason}" }
@@ -880,7 +792,7 @@ class MainExtension01 {
listafi,
targetfile, true,
)
db.logDB.Add("AAS", result.message)
logDB.Add("AAS", result.message)
if (!result.success) {
throw Exception(result.message)
}
@@ -899,11 +811,11 @@ class MainExtension01 {
targetafi.bytes,
{
so.SetAudioFileInfo(null)
db.logDB.Add("AAS", it)
logDB.Add("AAS", it)
},
{
so.SetAudioFileInfo(null)
db.logDB.Add("AAS", it)
logDB.Add("AAS", it)
}, cbPlaying = { isplaying ->
if (!isplaying) {
so.ClearUsedByBroadcastZones()
@@ -916,7 +828,7 @@ class MainExtension01 {
val logmsg =
"Broadcast started SHALAT message with generated file '$targetfile' to zones: ${qp.BroadcastZones}"
Logger.info { logmsg }
db.logDB.Add("AAS", logmsg)
logDB.Add("AAS", logmsg)
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
db.queuepagingDB.Resort()
return true
@@ -926,7 +838,7 @@ class MainExtension01 {
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
db.queuepagingDB.Resort()
val msg = "Canceled shalat message $qp due to exception: ${e.message}"
db.logDB.Add("AAS", msg)
logDB.Add("AAS", msg)
Logger.error { msg }
}
return false
@@ -947,7 +859,7 @@ class MainExtension01 {
if (mblist.isEmpty()) throw Exception("ANN_ID $ann_id not found in Messagebank")
if (qa.BroadcastZones.isEmpty()) throw Exception("Empty broadcast zones")
val zz = qa.BroadcastZones.split(";")
AllBroadcastZonesValid(zz).let { checkresult ->
broadcastDB.AllBroadcastZonesValid(zz).let { checkresult ->
if (!checkresult.allvalid) {
val reasons =
checkresult.invalidzones.joinToString("; ") { iz -> "Zone '${iz.zonename}': ${iz.reason}" }
@@ -997,13 +909,13 @@ class MainExtension01 {
so.SetAudioFileInfo(null)
so.ClearUsedByBroadcastZones()
so.DeactivateRelay()
db.logDB.Add("AAS", it)
logDB.Add("AAS", it)
},
{
so.SetAudioFileInfo(null)
so.ClearUsedByBroadcastZones()
so.DeactivateRelay()
db.logDB.Add("AAS", it)
logDB.Add("AAS", it)
}, cbPlaying = { isplaying ->
if (!isplaying) {
so.ClearUsedByBroadcastZones()
@@ -1015,7 +927,7 @@ class MainExtension01 {
val logmsg =
"Broadcast started TIMER message with generated file '$targetfile' to zones: ${qa.BroadcastZones}"
Logger.info { logmsg }
db.logDB.Add("AAS", logmsg)
logDB.Add("AAS", logmsg)
db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.queuetableDB.Resort()
return true
@@ -1025,7 +937,7 @@ class MainExtension01 {
db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.queuetableDB.Resort()
val msg = "Canceled TIMER message $qa due to exception: ${e.message}"
db.logDB.Add("AAS", msg)
logDB.Add("AAS", msg)
Logger.error { msg }
}
return false
@@ -1045,7 +957,7 @@ class MainExtension01 {
if (qa.BroadcastZones.isEmpty()) throw Exception("Empty broadcast zones")
val zz = qa.BroadcastZones.split(";")
AllBroadcastZonesValid(zz).let { checkresult ->
broadcastDB.AllBroadcastZonesValid(zz).let { checkresult ->
if (!checkresult.allvalid) {
val reasons =
checkresult.invalidzones.joinToString("; ") { iz -> "Zone '${iz.zonename}': ${iz.reason}" }
@@ -1071,7 +983,7 @@ class MainExtension01 {
// not available from variables, try to get from Message column
// ada ini, karena protokol FIS dulu tidak ada ANN_ID tapi pake Remark
val remark = variables?.get("REMARK").orEmpty()
db.logDB.Add("AAS", "Trying to get ANN_ID from REMARK field: $remark")
logDB.Add("AAS", "Trying to get ANN_ID from REMARK field: $remark")
Logger.info { "Trying to get ANN_ID from REMARK field: $remark" }
when (remark) {
"GOP" -> {
@@ -1099,9 +1011,9 @@ class MainExtension01 {
}
}
Logger.info { "Found ANN_ID from REMARK field: $ann_id" }
db.logDB.Add("AAS", "Found ANN_ID from REMARK field: $ann_id")
logDB.Add("AAS", "Found ANN_ID from REMARK field: $ann_id")
} else {
db.logDB.Add("AAS", "Found ANN_ID from SB_TAGS variables: $ann_id")
logDB.Add("AAS", "Found ANN_ID from SB_TAGS variables: $ann_id")
Logger.info { "Found ANN_ID from SB_TAGS variables: $ann_id" }
}
@@ -1152,13 +1064,13 @@ class MainExtension01 {
so.SetAudioFileInfo(null)
so.ClearUsedByBroadcastZones()
so.DeactivateRelay()
db.logDB.Add("AAS", it)
logDB.Add("AAS", it)
},
{
so.SetAudioFileInfo(null)
so.ClearUsedByBroadcastZones()
so.DeactivateRelay()
db.logDB.Add("AAS", it)
logDB.Add("AAS", it)
}, cbPlaying = { isplaying ->
if (!isplaying) {
so.ClearUsedByBroadcastZones()
@@ -1171,7 +1083,7 @@ class MainExtension01 {
val logmsg =
"Broadcast started SOUNDBANK message with generated file '$targetfile' to zones: ${qa.BroadcastZones}"
Logger.info { logmsg }
db.logDB.Add("AAS", logmsg)
logDB.Add("AAS", logmsg)
db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.queuetableDB.Resort()
return true
@@ -1180,7 +1092,7 @@ class MainExtension01 {
} catch (e: Exception) {
val msg = "Canceled SOUNDBANK message $qa due to exception: ${e.message}"
db.logDB.Add("AAS", msg)
logDB.Add("AAS", msg)
Logger.error { msg }
db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.queuetableDB.Resort()

View File

@@ -664,6 +664,14 @@ class Somecodes {
}
}
/**
* Get today's date as a string in the format "dd/MM/yyyy".
* @return A string representing today's date.
*/
fun Today_to_DateString() : String {
return dateformat1.format(LocalDateTime.now())
}
/**
* Check if a string is a valid time in the format "hh:mm:ss".
* @param value The string to check.
@@ -709,6 +717,21 @@ class Somecodes {
return sd?.name
}
/**
* Check if a string is a valid language code.
* A valid language code is one that matches any of the Language enum values.
* @param value The string to check.
* @return True if the string is a valid language code, false otherwise.
*/
fun ValidLanguage(value: String) : Boolean{
value.split(";",",").forEach { ll ->
Language.entries.forEach { l ->
if (l.value.equals(ll,true)) return true
}
}
return false
}
/**
* Check if a string is a valid schedule day or a valid date.
* A valid schedule day is either one of the ScheduleDay enum names or a date in the format "dd/MM/yyyy".

View File

@@ -15,6 +15,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import messageDB
import org.tinylog.Logger
import tcpreceiver
import udpreceiver
@@ -347,7 +348,7 @@ class TCP_Android_Command_Server {
// iterasi setiap ANN_ID
.forEach { annid ->
// masukin ke VARMESSAGES yang unik secara ANN_ID dan Language
val xx = db.messageDB.List
val xx = messageDB.List
.asSequence()
.filter{it.ANN_ID == annid.toUInt()}
.distinctBy { it.ANN_ID }
@@ -447,13 +448,13 @@ class TCP_Android_Command_Server {
VARMESSAGES.forEachIndexed { index, msg ->
val ann_id = msg.ANN_ID
val msg_indo = db.messageDB.List.find {
val msg_indo = messageDB.List.find {
it.ANN_ID == ann_id && it.Language.equals(
Language.INDONESIA.name,
true
)
}
val msg_eng = db.messageDB.List.find {
val msg_eng = messageDB.List.find {
it.ANN_ID == ann_id && it.Language.equals(
Language.ENGLISH.name,
true

View File

@@ -1,5 +1,7 @@
package content
import java.time.DayOfWeek
@Suppress("unused")
enum class ScheduleDay(val day: String) {
Sunday("Sunday"),
@@ -9,5 +11,22 @@ enum class ScheduleDay(val day: String) {
Thursday("Thursday"),
Friday("Friday"),
Saturday("Saturday"),
Everyday("Everyday")
}
Everyday("Everyday");
companion object {
/**
* Converts a DayOfWeek to a ScheduleDay
*/
fun from_LocalDate_DOW(value : DayOfWeek) : ScheduleDay{
return when(value){
DayOfWeek.SUNDAY -> Sunday
DayOfWeek.MONDAY -> Monday
DayOfWeek.TUESDAY -> Tuesday
DayOfWeek.WEDNESDAY -> Wednesday
DayOfWeek.THURSDAY -> Thursday
DayOfWeek.FRIDAY -> Friday
DayOfWeek.SATURDAY -> Saturday
}}
}
}

View File

@@ -21,34 +21,21 @@ import database.table.Table_SoundChannel
import database.table.Table_Soundbank
import database.table.Table_Users
import messageDB
import broadcastDB
import logDB
import soundchannelDB
/**
* A class to manage a connection to a MariaDB database.
*
* @property address The address of the MariaDB server.
* @property port The port number of the MariaDB server.
* @property dbName The name of the database to connect to.
* @property username The username for the database connection.
* @property password The password for the database connection.
*/
class MariaDB(
address: String = config.Get(configKeys.DATABASE_HOST.key),
port: Int = config.Get(configKeys.DATABASE_PORT.key).toInt(),
dbName: String = config.Get(configKeys.DATABASE_NAME.key),
username: String = config.Get(configKeys.DATABASE_USER.key),
password: String = config.Get(configKeys.DATABASE_PASSWORD.key)
) {
class MariaDB {
var connected: Boolean = false
lateinit var connection: Connection
var connection: Connection? = null
lateinit var soundDB: Table_Soundbank
lateinit var messageDB: Table_Messagebank
lateinit var languageDB: Table_LanguageLink
lateinit var scheduleDB: Table_Schedule
lateinit var broadcastDB: Table_BroadcastZones
lateinit var queuetableDB: Table_QueueSoundbank
lateinit var queuepagingDB: Table_QueuePaging
lateinit var soundchannelDB: Table_SoundChannel
lateinit var logDB: Table_Logs
lateinit var userDB: Table_Users
lateinit var logSemiAuto: Table_LogSemiAuto
@@ -79,20 +66,10 @@ class MariaDB(
}
init {
try {
connection =
DriverManager.getConnection(
"jdbc:mysql://$address:$port/$dbName?sslMode=REQUIRED",
username,
password
) as Connection
Logger.info("Connected to MySQL" as Any)
connected = true
// create databas 'aas' if not exists
val statement = connection.createStatement()
statement.executeUpdate("CREATE DATABASE IF NOT EXISTS aas")
statement.executeUpdate("USE aas")
CreateConnection()?.let { connection ->
this.connection = connection
Logger.info { "Connected to MariaDB successfully"}
CreateDatabase(connection)
soundDB = Table_Soundbank(connection)
messageDB = Table_Messagebank(connection)
@@ -123,9 +100,10 @@ class MariaDB(
messageDB.Get()
soundDB.Get()
languageDB.Get()
scheduleDB.Get()
broadcastDB.Get()
soundchannelDB.Get()
scheduleDB.Get()
userDB.Get()
}
}
@@ -140,10 +118,6 @@ class MariaDB(
Logger.info { "BroadcastZones count: ${broadcastDB.List.size}" }
Logger.info { "SoundChannel count: ${soundchannelDB.List.size}" }
Logger.info { "User count: ${userDB.List.size}" }
} catch (e: Exception) {
Logger.error("Failed to connect to MariaDB: ${e.message}" as Any)
}
}
@@ -152,13 +126,58 @@ class MariaDB(
*/
fun close() {
try {
connection.close()
this.connection?.close()
Logger.info("Connection to MariaDB closed" as Any)
} catch (e: Exception) {
Logger.error("Error closing MariaDB connection: ${e.message}" as Any)
} finally {
this.connection = null
connected = false
}
}
/**
* Creates a new connection to the MariaDB database.
* @return A Connection object if successful, null otherwise.
*/
fun CreateConnection(): Connection? {
val address: String = config.Get(configKeys.DATABASE_HOST.key)
val port: Int = config.Get(configKeys.DATABASE_PORT.key).toInt()
val dbName: String = config.Get(configKeys.DATABASE_NAME.key)
val username: String = config.Get(configKeys.DATABASE_USER.key)
val password: String = config.Get(configKeys.DATABASE_PASSWORD.key)
connected = false
try{
val cc = DriverManager.getConnection(
"jdbc:mysql://$address:$port/$dbName?sslMode=REQUIRED",
username,
password
)
connected = true
return cc
} catch (e : Exception){
Logger.error("Failed to create connection to MariaDB: ${e.message}" as Any)
return null
}
}
/**
* Creates the database if it does not exist.
* @param connection The Connection object to use for creating the database.
* @return True if the database was created or already exists, false otherwise.
*/
fun CreateDatabase(connection: Connection) : Boolean{
try {
// create databas 'aas' if not exists
val statement = connection.createStatement()
statement.executeUpdate("CREATE DATABASE IF NOT EXISTS aas")
statement.executeUpdate("USE aas")
return true
} catch (e : Exception){
Logger.error { "Error creating database: ${e.message}" }
}
return false
}

View File

@@ -6,8 +6,30 @@ import java.sql.Connection
import java.util.function.Consumer
@Suppress("unused", "SqlDialectInspection", "SqlSourceToSinkFlow")
abstract class dbFunctions<T>(val dbName: String, val connection: Connection, requiredcolumns: List<String>) {
abstract class dbFunctions<T>(val dbName: String, conn: Connection, requiredcolumns: List<String>) {
var List : ArrayList<T> = ArrayList()
var connection = conn
/**
* Check if the database connection is valid
* @return true if valid, false otherwise
*/
fun IsConnected() : Boolean{
return try {
connection.isValid(2)
} catch (e: Exception) {
Logger.error("Database connection is not valid: ${e.message}" as Any)
false
}
}
/**
* Change the database connection
* @param newcon The new Connection object
*/
fun ChangeConnection(newcon: Connection){
connection = newcon
}
init{
val columns = GetColumnInfo()

View File

@@ -1,9 +1,14 @@
package database.table
import StreamerOutputs
import broadcastDB
import codes.Somecodes.Companion.ValidIPV4
import codes.Somecodes.Companion.ValidString
import database.data.BroadcastZones
import database.dbFunctions
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import org.tinylog.Logger
import soundchannelDB
import java.sql.Connection
import java.util.function.Consumer
@@ -200,4 +205,129 @@ class Table_BroadcastZones(connection: Connection) : dbFunctions<BroadcastZones>
.map { it.description }
.sorted()
}
data class InvalidZoneDetail(val zonename: String, val reason: String)
data class ValidZoneDetail(
val zonename: String,
val soundchanel: String,
val ip: String,
val boxid: String,
val contacts: String
)
class CheckBroadcastZoneResult {
var allvalid: Boolean = false
var message: String? = null
var validzones = mutableListOf<ValidZoneDetail>()
var invalidzones = mutableListOf<InvalidZoneDetail>()
}
/**
* Check if all broadcast zones in a comma-separated string are valid
* Valid means the broadcast zone name exists in the BroadcastZones table, its SoundChannel exists in the SoundChannel table, and its IP is valid
* @param zones Comma-separated string of broadcast zones
* @param checkOnline Whether to check if the sound channel is really online, recorded in StreamerOutputs map
* @return true if all broadcast zones are valid, false otherwise
*/
fun AllBroadcastZonesValid(zones: String, checkOnline: Boolean = true) : Boolean{
val bzlist = zones.split(",",";").map { it.trim() }.filter { it.isNotEmpty() }
//println("Checking all broadcast zones validity for: $bzlist")
AllBroadcastZonesValid(bzlist, checkOnline).let { result ->
if (result.allvalid) {
//Logger.info("All broadcast zones are valid: ${bzlist.joinToString(", ")}" as Any)
return true
} else {
//Logger.warn("Some broadcast zones are invalid:" as Any)
result.invalidzones.forEach { iz ->
Logger.warn(" - Zone '${iz.zonename}' is invalid: ${iz.reason}" as Any)
}
return false
}
}
//return AllBroadcastZonesValid(bzlist).allvalid
}
/**
* Fungsi untuk cek apakah semua broadcast zone valid
* Valid berarti nama broadcast zone ada di tabel BroadcastZones, dan SoundChannel-nya ada di tabel SoundChannel, dan IP-nya valid
* @param bz List of broadcast zone (SoundChannel)
* @param checkOnline Whether to check if the sound channel is really online, recorded in StreamerOutputs map
* @return CheckBroadcastZoneResult object containing allvalid flag and list of invalid zones
*/
fun AllBroadcastZonesValid(bz: List<String>, checkOnline: Boolean = true): CheckBroadcastZoneResult {
val result = CheckBroadcastZoneResult()
if (bz.isNotEmpty()) {
bz.forEach { zz ->
if (ValidString(zz)) { // string tidak kosong
val findzone = List.find{
ValidString(it.description) && ValidString(it.SoundChannel) && it.description.equals(zz,true)
}
if (findzone != null) { // ketemu zona dengan deskripsi sesuai
val findsc = soundchannelDB.List.find {
findzone.SoundChannel.equals(
it.channel,
true
) && ValidIPV4(it.ip)
}
if (findsc != null) { // ketemu soundchannel dengan channel sesuai dan IP valid
// check apakah offline atau online
if (checkOnline){
if (StreamerOutputs.containsKey(findsc.ip)) {
val bc = StreamerOutputs[findsc.ip]
if (bc != null && bc.isOnline()) {
result.validzones.add(
ValidZoneDetail(
zz,
findzone.SoundChannel,
findsc.ip,
findzone.id,
findzone.bp
)
)
} else result.invalidzones.add(
InvalidZoneDetail(
zz,
"SoundChannel ${findzone.SoundChannel} with IP ${findsc.ip} is offline"
)
)
} else result.invalidzones.add(
InvalidZoneDetail(
zz,
"SoundChannel ${findzone.SoundChannel} with IP ${findsc.ip} is not connected in StreamerOutputs"
)
)
} else {
result.validzones.add(
ValidZoneDetail(
zz,
findzone.SoundChannel,
findsc.ip,
findzone.id,
findzone.bp
)
)
}
} else result.invalidzones.add(
InvalidZoneDetail(
zz,
"SoundChannel ${findzone.SoundChannel} not found or has invalid IP in SoundChannel table"
)
)
} else result.invalidzones.add(InvalidZoneDetail(zz, "Zone $zz not found in BroadcastZones table"))
} else result.invalidzones.add(InvalidZoneDetail(zz, "Invalid broadcast zone string"))
}
if (result.validzones.size == bz.size) {
result.allvalid = true
result.message = "All requested broadcast zones are valid"
} else {
result.message = "Some requested broadcast zones are not registered in BroadcastZone table"
}
} else {
result.message = "No Broadcast Zones checked for validity"
}
return result
}
}

View File

@@ -2,10 +2,14 @@ package database.table
import database.data.Log
import database.dbFunctions
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import org.tinylog.Logger
import java.sql.Connection
import java.sql.Date
import java.sql.Statement
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import java.util.function.Consumer
@@ -47,33 +51,42 @@ class Table_Logs(connection: Connection) : dbFunctions<Log>("logs", connection,l
fun GetLogForHtml(date: String, filter: String?, cbOK: Consumer<ArrayList<Log>>?, cbFail: Consumer<String>?){
try{
val valid_date : Date? = when{
dateformat1.matches(date) -> {
Date.valueOf(LocalDate.parse(date, DateTimeFormatter.ofPattern("dd/MM/yyyy")))
var statement : Statement?
if ("alldate" == date){
if (filter.isNullOrEmpty()) throw Exception("Filter is required when date is 'alldate'")
statement = connection.prepareStatement("SELECT * FROM ${super.dbName} WHERE description LIKE ?")
statement?.setString(1, "%$filter%")
} else {
val valid_date : Date? = when{
dateformat1.matches(date) -> {
Date.valueOf(LocalDate.parse(date, DateTimeFormatter.ofPattern("dd/MM/yyyy")))
}
dateformat2.matches(date) -> {
Date.valueOf(LocalDate.parse(date, DateTimeFormatter.ofPattern("dd-MM-yyyy")))
}
dateformat3.matches(date) -> {
Date.valueOf(LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy/MM/dd")))
}
dateformat4.matches(date) -> {
Date.valueOf(LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd")))
}
else -> null
}
dateformat2.matches(date) -> {
Date.valueOf(LocalDate.parse(date, DateTimeFormatter.ofPattern("dd-MM-yyyy")))
}
dateformat3.matches(date) -> {
Date.valueOf(LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy/MM/dd")))
}
dateformat4.matches(date) -> {
Date.valueOf(LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd")))
}
else -> null
if (valid_date!=null){
// use coalescing for different datenya formats
statement = if (filter.isNullOrEmpty()){
connection.prepareStatement("SELECT * FROM ${super.dbName} WHERE COALESCE(STR_TO_DATE(datenya,'%d/%m/%Y'), STR_TO_DATE(datenya,'%d-%m-%Y'), STR_TO_DATE(datenya,'%Y/%m/%d'), STR_TO_DATE(datenya,'%Y-%m-%d')) = ?")
} else {
connection.prepareStatement("SELECT * FROM ${super.dbName} WHERE COALESCE(STR_TO_DATE(datenya,'%d/%m/%Y'), STR_TO_DATE(datenya,'%d-%m-%Y'), STR_TO_DATE(datenya,'%Y/%m/%d'), STR_TO_DATE(datenya,'%Y-%m-%d')) = ? AND description LIKE ?")
}
statement?.setDate(1, valid_date)
if (!filter.isNullOrEmpty()){
statement?.setString(2, "%$filter%")
}
} else throw Exception("Invalid date")
}
if (valid_date!=null){
// use coalescing for different datenya formats
val statement = if (filter.isNullOrEmpty()){
connection.prepareStatement("SELECT * FROM ${super.dbName} WHERE COALESCE(STR_TO_DATE(datenya,'%d/%m/%Y'), STR_TO_DATE(datenya,'%d-%m-%Y'), STR_TO_DATE(datenya,'%Y/%m/%d'), STR_TO_DATE(datenya,'%Y-%m-%d')) = ?")
} else {
connection.prepareStatement("SELECT * FROM ${super.dbName} WHERE COALESCE(STR_TO_DATE(datenya,'%d/%m/%Y'), STR_TO_DATE(datenya,'%d-%m-%Y'), STR_TO_DATE(datenya,'%Y/%m/%d'), STR_TO_DATE(datenya,'%Y-%m-%d')) = ? AND description LIKE ?")
}
statement?.setDate(1, valid_date)
if (!filter.isNullOrEmpty()){
statement?.setString(2, "%$filter%")
}
val resultSet = statement?.executeQuery()
if (statement!=null){
val resultSet = statement.executeQuery()
val tempList = ArrayList<Log>()
while (resultSet?.next() == true) {
val log = Log(
@@ -86,7 +99,7 @@ class Table_Logs(connection: Connection) : dbFunctions<Log>("logs", connection,l
tempList.add(log)
}
cbOK?.accept(tempList)
} else throw Exception("Invalid date")
} else throw Exception("Failed to prepare statement")
} catch (e : Exception){
if (filter.isNullOrEmpty()){
cbFail?.accept("Failed to Get logs for date $date: ${e.message}")
@@ -94,29 +107,38 @@ class Table_Logs(connection: Connection) : dbFunctions<Log>("logs", connection,l
cbFail?.accept("Failed to Get logs for date $date with filter $filter: ${e.message}")
}
}
}
/****
* Fetches all log entries from the database and populates the local List.
* @param cbOK Optional callback invoked upon successful retrieval.
* @param cbFail Optional callback invoked upon failure with an error message.
*/
override fun Get(cbOK: Consumer<Unit>?, cbFail: Consumer<String>?) {
List.clear()
try {
val statement = connection.createStatement()
val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}")
while (resultSet?.next() == true) {
val log = Log(
resultSet.getLong("index").toULong(),
resultSet.getString("datenya"),
resultSet.getString("timenya"),
resultSet.getString("machine"),
resultSet.getString("description")
)
List.add(log)
}
cbOK?.accept(Unit)
} catch (e: Exception) {
cbFail?.accept("Error fetching ${super.dbName}: ${e.message}")
Logger.error("Error fetching ${super.dbName}: ${e.message}" as Any)
CoroutineScope(Dispatchers.IO).launch {
List.clear()
try {
val statement = connection.createStatement()
val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}")
while (resultSet?.next() == true) {
val log = Log(
resultSet.getLong("index").toULong(),
resultSet.getString("datenya"),
resultSet.getString("timenya"),
resultSet.getString("machine"),
resultSet.getString("description")
)
List.add(log)
}
cbOK?.accept(Unit)
} catch (e: Exception) {
cbFail?.accept("Error fetching ${super.dbName}: ${e.message}")
Logger.error("Error fetching ${super.dbName}: ${e.message}" as Any)
}
}
}
fun Add(machine: String, description: String): Boolean {

View File

@@ -236,4 +236,26 @@ class Table_Messagebank(connection: Connection) : dbFunctions<Messagebank>("mess
.map { it.ANN_ID }
.sorted()
}
// valid messagedetail is message_name [ann_id]
// so we need regex to check if messagedetail matches that format
private val messageDetailRegex = """^(.*?)\s*\[(\d+)]$""".toRegex()
/**
* Check if a messagebank entry exists based on messagedetail and languages
* @param messagedetail the messagedetail in format "message_name [ann_id]"
* @param languages a comma or semicolon separated string of languages to check
* @return true if the messagebank entry exists for all specified languages, false otherwise
*/
fun Messagebank_Exists(messagedetail: String, languages: String) : Boolean{
val match = messageDetailRegex.find(messagedetail)
val ll = languages.split(",",";").map { it.trim() }
if (match != null){
val msg = match.groupValues[1].trim()
val annid = match.groupValues[2].toUIntOrNull() ?: return false // kalau bukan number, return false
val ff = List.filter{ it.ANN_ID == annid && it.Description == msg }
return ll.all{ lang -> ff.any{ it.Language.equals(lang, ignoreCase = true) } }
}
return false
}
}

View File

@@ -1,6 +1,9 @@
package database.table
import codes.Somecodes.Companion.ValidLanguage
import codes.Somecodes.Companion.ValidScheduleDay
import codes.Somecodes.Companion.ValidScheduleTime
import content.ScheduleDay
import database.MariaDB.Companion.ValidTime
import database.data.ScheduleBank
import database.dbFunctions
@@ -9,7 +12,54 @@ import org.tinylog.Logger
import java.sql.Connection
import java.util.function.Consumer
import broadcastDB
import codes.Somecodes
import messageDB
import java.time.LocalDate
class Table_Schedule(connection: Connection) : dbFunctions<ScheduleBank>("schedulebank", connection, listOf("index", "Description", "Day", "Time", "Soundpath", "Repeat", "Enable", "BroadcastZones", "Language")) {
/**
* A list to hold today's schedule entries.
*/
val todaySchedule = ArrayList<ScheduleBank>()
/**
* Update today's schedule
*/
fun UpdateTodaySchedule(){
todaySchedule.clear()
fun Find_Enabled_Schedules() : List<ScheduleBank>{
return List
.filter{it.Enable} // yang enabled saja
.filter{ValidScheduleTime(it.Time)} // yang timenya dalam format HH:MM
.filter{ValidLanguage(it.Language)} // yang bahasanya valid
.filter{broadcastDB.AllBroadcastZonesValid(it.BroadcastZones, false)} // yang broadcastzonesnya valid
// Soundpath ini coding typo, aslinya Messagebank description
.filter{messageDB.Messagebank_Exists(it.Soundpath, it.Language)}
}
val eligibleSchedule = Find_Enabled_Schedules()
val tempMap = mutableMapOf<String, ScheduleBank>()
// prioritas paling rendah adalah everyday
eligibleSchedule.filter { it.Day == ScheduleDay.Everyday.day }.forEach { tempMap[it.Time] = it }
// lebih tinggi adalah weekly, akan replace everyday jika time nya sama
val today_DOW = ScheduleDay.from_LocalDate_DOW(LocalDate.now().dayOfWeek)
eligibleSchedule.filter { it.Day == today_DOW.day }.forEach { tempMap[it.Time] = it }
// paling tinggi adalah specific date, akan replace yang lain jika time nya sama
val today = Somecodes.Today_to_DateString()
eligibleSchedule.filter { it.Day == today }.forEach { tempMap[it.Time] = it }
// masukin ke todaySchedule yang sudah di sort by Time
todaySchedule.addAll(tempMap.values.sortedBy { it.Time })
println("Todays schedule : $todaySchedule")
}
override fun Create() {
val tabledefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" +
"`index` INT AUTO_INCREMENT PRIMARY KEY," +
@@ -44,6 +94,7 @@ class Table_Schedule(connection: Connection) : dbFunctions<ScheduleBank>("schedu
)
List.add(schedulebank)
}
UpdateTodaySchedule()
cbOK?.accept(Unit)
} catch (e: Exception) {
Logger.error("Error fetching ${super.dbName}: ${e.message}" as Any)

View File

@@ -2,6 +2,7 @@ package web
import StreamerOutputs
import barix.BarixConnection
import broadcastDB
import codes.Somecodes
import codes.Somecodes.Companion.GetAppUpTime
import codes.Somecodes.Companion.GetSensorsInfo
@@ -49,7 +50,10 @@ import google.GoogleTTS
import google.autoadd
import google.fileoperation
import io.javalin.websocket.WsCloseStatus
import logDB
import messageDB
import org.tinylog.Logger
import soundchannelDB
import version
import java.io.File
import java.nio.ByteBuffer
@@ -730,7 +734,7 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
db.soundDB.Resort()
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
db.logDB.Add("AAS", "Deleted sound bank with index $index")
logDB.Add("AAS", "Deleted sound bank with index $index")
} else {
it.status(500)
.result(objectmapper.writeValueAsString(resultMessage("Failed to delete soundbank with index $index")))
@@ -863,15 +867,15 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
path("MessageBank") {
get("List") { ctx ->
// get messagebank list
db.messageDB.Get({
ctx.result(MariaDB.ArrayListtoString(db.messageDB.List))
messageDB.Get({
ctx.result(MariaDB.ArrayListtoString(messageDB.List))
}, { msgFail ->
ctx.status(500).result(objectmapper.writeValueAsString(resultMessage(msgFail)))
})
}
get("MessageIDs") { ctx ->
val value = db.messageDB.List
val value = messageDB.List
.distinctBy { it.ANN_ID }
.sortedBy { it.ANN_ID }
.map { KeyValueMessage(it.ANN_ID.toString(), it.Description) }
@@ -902,10 +906,10 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
message_tags
)
val existed =
db.messageDB.List.any { it.ANN_ID == mb.ANN_ID && it.Language == mb.Language && it.Voice_Type == mb.Voice_Type }
messageDB.List.any { it.ANN_ID == mb.ANN_ID && it.Language == mb.Language && it.Voice_Type == mb.Voice_Type }
if (!existed) {
if (db.messageDB.Add(mb)) {
db.messageDB.Resort()
if (messageDB.Add(mb)) {
messageDB.Resort()
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
} else it.status(500)
.result(objectmapper.writeValueAsString(resultMessage("Failed to add messagebank to database")))
@@ -926,8 +930,8 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
}
delete("List") {
// truncate messagebank table
if (db.messageDB.Clear()) {
db.messageDB.Get()
if (messageDB.Clear()) {
messageDB.Get()
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
} else {
it.status(500)
@@ -941,10 +945,10 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
it.status(400)
.result(objectmapper.writeValueAsString(resultMessage("Invalid index")))
} else {
if (db.messageDB.DeleteByIndex(index.toInt())) {
db.messageDB.Resort()
if (messageDB.DeleteByIndex(index.toInt())) {
messageDB.Resort()
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
db.logDB.Add("AAS", "Deleted message bank with index $index")
logDB.Add("AAS", "Deleted message bank with index $index")
} else {
it.status(500)
.result(objectmapper.writeValueAsString(resultMessage("Failed to delete messagebank with index $index")))
@@ -958,7 +962,7 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
it.status(400)
.result(objectmapper.writeValueAsString(resultMessage("Invalid index")))
} else {
val mb = db.messageDB.List.find { xx -> xx.index == index }
val mb = messageDB.List.find { xx -> xx.index == index }
if (mb == null) {
it.status(404)
.result(objectmapper.writeValueAsString(resultMessage("Messagebank with index $index not found")))
@@ -1007,8 +1011,8 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
changed = true
}
if (changed) {
if (db.messageDB.UpdateByIndex(index.toInt(), mb)) {
db.messageDB.Resort()
if (messageDB.UpdateByIndex(index.toInt(), mb)) {
messageDB.Resort()
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
} else it.status(500)
.result(objectmapper.writeValueAsString(resultMessage("Failed to update messagebank with index $index")))
@@ -1019,7 +1023,7 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
}
}
get("ExportXLSX") {
val xlsxdata = db.messageDB.Export_XLSX()
val xlsxdata = messageDB.Export_XLSX()
if (xlsxdata != null) {
it.header(
"Content-Type",
@@ -1043,8 +1047,8 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
}
try {
val xlsx = XSSFWorkbook(uploaded.content())
if (db.messageDB.Import_XLSX(xlsx)) {
db.messageDB.Resort()
if (messageDB.Import_XLSX(xlsx)) {
messageDB.Resort()
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
} else {
it.status(500)
@@ -1133,7 +1137,7 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
if (db.languageDB.DeleteByIndex(index.toInt())) {
db.languageDB.Resort()
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
db.logDB.Add("AAS", "Deleted language link with index $index")
logDB.Add("AAS", "Deleted language link with index $index")
} else {
it.status(500)
.result(objectmapper.writeValueAsString(resultMessage("Failed to delete language link with index $index")))
@@ -1219,11 +1223,14 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
}
}
path("ScheduleBank") {
get("TodaySchedule"){ ctx ->
ctx.json(db.scheduleDB.todaySchedule)
}
get("List") { ctx ->
db.scheduleDB.Get({
// get timer list
ctx.result(MariaDB.ArrayListtoString(db.scheduleDB.List))
ctx.json(db.scheduleDB.List)
}, { msgFail ->
ctx.status(500).result(objectmapper.writeValueAsString(resultMessage(msgFail)))
})
@@ -1257,7 +1264,7 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
if (ValidString(broadcast_zones)) {
val zones = broadcast_zones.split(";")
if (zones.all { zz ->
db.broadcastDB.List.any { xx ->
broadcastDB.List.any { xx ->
xx.description.equals(
zz,
true
@@ -1318,7 +1325,7 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
if (db.scheduleDB.DeleteByIndex(index.toInt())) {
db.scheduleDB.Resort()
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
db.logDB.Add("AAS", "Deleted schedule bank with index $index")
logDB.Add("AAS", "Deleted schedule bank with index $index")
} else {
it.status(500)
.result(objectmapper.writeValueAsString(resultMessage("Failed to delete schedule with index $index")))
@@ -1407,21 +1414,21 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
}
get("GetMessageAndBroadcastZones") {
val result = object {
val messages = db.messageDB.List
val messages = messageDB.List
.filter { mb ->
!mb.Message_Detail.contains("[") && !mb.Message_Detail.contains(
"]"
)
}
.map { mb -> "${mb.Description} [${mb.ANN_ID}]" }
val broadcastzones = db.broadcastDB.List
val broadcastzones = broadcastDB.List
}
it.result(objectmapper.writeValueAsString(result))
}
// Kirim list language dari Messagebank berdasarkan ANN_ID
get("GetLanguageList/{ANN_ID}") { get1 ->
val langlist = db.messageDB.List
val langlist = messageDB.List
.filter { it.ANN_ID == get1.pathParam("ANN_ID").toInt().toUInt() }
.map { it.Language }.distinct()
get1.result(objectmapper.writeValueAsString(langlist))
@@ -1525,7 +1532,7 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
.map { it.trim() }
.filter { it.isNotEmpty() }.distinct()
.mapNotNull { it.toUIntOrNull() }
val mbankids = db.messageDB.Get_MessageID_List()
val mbankids = messageDB.Get_MessageID_List()
if (!mbids.all { id -> mbankids.any { it == id } }) {
ctx.status(400)
.result(objectmapper.writeValueAsString(resultMessage("Some ANN_ID not found in Messagebank")))
@@ -1537,7 +1544,7 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
val bzdesc =
broadcastzones.split(";").map { it.trim() }
.filter { it.isNotEmpty() }.distinct()
val bzlist = db.broadcastDB.Get_BroadcastZone_List()
val bzlist = broadcastDB.Get_BroadcastZone_List()
val missing_broadcastzones = ArrayList<String>()
bzdesc.forEach { bz ->
if (!bzlist.any { it.equals(bz, true) }) {
@@ -1594,7 +1601,7 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
if (db.userDB.DeleteByIndex(index.toInt())) {
db.userDB.Resort()
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
db.logDB.Add("AAS", "Deleted user with index $index")
logDB.Add("AAS", "Deleted user with index $index")
} else it.status(500)
.result(objectmapper.writeValueAsString(resultMessage("Failed to delete user with index $index")))
}
@@ -1700,7 +1707,7 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
.map { it.trim() }
.filter { it.isNotEmpty() }.distinct()
.mapNotNull { it.toUIntOrNull() }
val mbankids = db.messageDB.Get_MessageID_List()
val mbankids = messageDB.Get_MessageID_List()
val missing_broadcastids = ArrayList<UInt>()
mbids.forEach { mbid ->
if (!mbankids.any { it == mbid }) {
@@ -1727,7 +1734,7 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
val bzdesc = _broadcastzones.split(";").map { it.trim() }
.filter { it.isNotEmpty() }.distinct()
val bzlist = db.broadcastDB.Get_BroadcastZone_List()
val bzlist = broadcastDB.Get_BroadcastZone_List()
val missing_broadcastzones = ArrayList<String>()
bzdesc.forEach { bz ->
if (!bzlist.any { it.equals(bz, true) }) {
@@ -1832,8 +1839,8 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
get("GetMessageAndBroadcastZones") {
val result = object {
val messages = db.messageDB.List
val broadcastzones = db.broadcastDB.List
val messages = messageDB.List
val broadcastzones = broadcastDB.List
}
it.result(objectmapper.writeValueAsString(result))
}
@@ -1842,8 +1849,10 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
get("List") { get1 ->
val logdate = get1.queryParam("date") ?: ""
val logfilter = get1.queryParam("filter")
db.logDB.GetLogForHtml(logdate, logfilter, { loglist ->
get1.result(objectmapper.writeValueAsString(loglist))
Logger.info{"Client ${get1.ip()} requested log list for date $logdate with filter $logfilter"}
logDB.GetLogForHtml(logdate, logfilter, { loglist ->
get1.contentType("application/json")
get1.json(loglist)
}, { msgFail ->
get1.status(500).result(objectmapper.writeValueAsString(resultMessage(msgFail)))
})
@@ -1853,9 +1862,9 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
val logfilter = get1.queryParam("filter") ?: ""
if (ValiDateForLogHtml(logdate)) {
val xlsxdata = if (ValidString(logfilter)) {
db.logDB.Export_Log_XLSX(logdate.replace('-', '/'), logfilter)
logDB.Export_Log_XLSX(logdate.replace('-', '/'), logfilter)
} else {
db.logDB.Export_Log_XLSX(logdate.replace('-', '/'), "")
logDB.Export_Log_XLSX(logdate.replace('-', '/'), "")
}
if (xlsxdata != null) {
get1.header(
@@ -1876,22 +1885,22 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
}
path("BroadcastZones") {
get("List") { ctx ->
db.broadcastDB.Get({
ctx.result(MariaDB.ArrayListtoString(db.broadcastDB.List))
broadcastDB.Get({
ctx.json(broadcastDB.List)
}, { msgFail ->
ctx.status(500).result(objectmapper.writeValueAsString(resultMessage(msgFail)))
})
}
get("BroadcastZoneDescriptions") { ctx ->
val value = db.broadcastDB.List
val value = broadcastDB.List
.distinctBy { it.description }
.map { it.description }
ctx.result(objectmapper.writeValueAsString(value))
}
delete("List") {
// truncate broadcast zones table
if (db.broadcastDB.Clear()) {
db.broadcastDB.Get()
if (broadcastDB.Clear()) {
broadcastDB.Get()
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
} else {
it.status(500)
@@ -1910,8 +1919,8 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
if (ValidString(_relay)) {
val newbp =
BroadcastZones(0u, _description, _soundchannel, _box, _relay)
if (db.broadcastDB.Add(newbp)) {
db.broadcastDB.Resort()
if (broadcastDB.Add(newbp)) {
broadcastDB.Resort()
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
} else it.status(500)
.result(objectmapper.writeValueAsString(resultMessage("Failed to add broadcast zone to database")))
@@ -1931,10 +1940,10 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
it.status(400)
.result(objectmapper.writeValueAsString(resultMessage("Invalid index")))
} else {
if (db.broadcastDB.DeleteByIndex(index.toInt())) {
db.broadcastDB.Resort()
if (broadcastDB.DeleteByIndex(index.toInt())) {
broadcastDB.Resort()
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
db.logDB.Add("AAS", "Deleted broadcast zone with index $index")
logDB.Add("AAS", "Deleted broadcast zone with index $index")
} else {
it.status(500)
.result(objectmapper.writeValueAsString(resultMessage("Failed to delete broadcast zone with index $index")))
@@ -1948,7 +1957,7 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
it.status(400)
.result(objectmapper.writeValueAsString(resultMessage("Invalid index")))
} else {
val bz = db.broadcastDB.List.find { xx -> xx.index == index }
val bz = broadcastDB.List.find { xx -> xx.index == index }
if (bz == null) {
it.status(404)
.result(objectmapper.writeValueAsString(resultMessage("Broadcast zone with index $index not found")))
@@ -1980,8 +1989,8 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
changed = true
}
if (changed) {
if (db.broadcastDB.UpdateByIndex(index.toInt(), bz)) {
db.broadcastDB.Resort()
if (broadcastDB.UpdateByIndex(index.toInt(), bz)) {
broadcastDB.Resort()
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
} else it.status(500)
.result(objectmapper.writeValueAsString(resultMessage("Failed to update broadcast zone with index $index")))
@@ -1993,7 +2002,7 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
}
get("ExportXLSX") {
val xlsxdata = db.broadcastDB.Export_XLSX()
val xlsxdata = broadcastDB.Export_XLSX()
if (xlsxdata != null) {
it.header(
"Content-Type",
@@ -2017,8 +2026,8 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
}
try {
val xlsx = XSSFWorkbook(uploaded.content())
if (db.broadcastDB.Import_XLSX(xlsx)) {
db.broadcastDB.Resort()
if (broadcastDB.Import_XLSX(xlsx)) {
broadcastDB.Resort()
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
} else {
it.status(500)
@@ -2033,20 +2042,20 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
}
path("SoundChannel") {
get("List") { ctx ->
db.soundchannelDB.Get({
ctx.result(MariaDB.ArrayListtoString(db.soundchannelDB.List))
soundchannelDB.Get({
ctx.json(soundchannelDB.List)
}, { msgFail ->
ctx.status(500).result(objectmapper.writeValueAsString(resultMessage(msgFail)))
})
}
get("SoundChannelDescriptions") {
it.result(objectmapper.writeValueAsString(db.soundchannelDB.Get_SoundChannel_List()))
it.result(objectmapper.writeValueAsString(soundchannelDB.Get_SoundChannel_List()))
}
delete("List") {
// truncate sound channel table
if (db.soundchannelDB.Clear()) {
db.soundchannelDB.Get()
if (soundchannelDB.Clear()) {
soundchannelDB.Get()
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
} else {
it.status(500)
@@ -2060,7 +2069,7 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
it.status(400)
.result(objectmapper.writeValueAsString(resultMessage("Invalid index")))
} else {
val sc = db.soundchannelDB.List.find { xx -> xx.index == index }
val sc = soundchannelDB.List.find { xx -> xx.index == index }
if (sc == null) {
it.status(404)
.result(objectmapper.writeValueAsString(resultMessage("Sound channel with index $index not found")))
@@ -2082,7 +2091,7 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
// cek apakah ada soundchannel lain yang pakai ip dan channel yang sama
val othersc =
db.soundchannelDB.List.filter { sc -> sc.ip == _ip }
soundchannelDB.List.filter { sc -> sc.ip == _ip }
.filter { sc -> sc.index != index }
if (othersc.isNotEmpty()) {
it.status(400)
@@ -2090,8 +2099,8 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
} else {
// ada sesuatu yang ganti
val newsc = SoundChannel(0u, _channel, _ip)
if (db.soundchannelDB.UpdateByIndex(index.toInt(), newsc)) {
db.soundchannelDB.Resort()
if (soundchannelDB.UpdateByIndex(index.toInt(), newsc)) {
soundchannelDB.Resort()
it.result(
objectmapper.writeValueAsString(
resultMessage(
@@ -2128,7 +2137,7 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
}
get("ExportXLSX") {
val xlsxdata = db.soundchannelDB.Export_XLSX()
val xlsxdata = soundchannelDB.Export_XLSX()
if (xlsxdata != null) {
it.header(
"Content-Type",
@@ -2152,8 +2161,8 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
}
try {
val xlsx = XSSFWorkbook(uploaded.content())
if (db.soundchannelDB.Import_XLSX(xlsx)) {
db.soundchannelDB.Resort()
if (soundchannelDB.Import_XLSX(xlsx)) {
soundchannelDB.Resort()
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
} else {
it.status(500)
@@ -2169,7 +2178,7 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
path("QueuePaging") {
get("List") { ctx ->
db.queuepagingDB.Get({
ctx.result(MariaDB.ArrayListtoString(db.queuepagingDB.List))
ctx.json(db.queuepagingDB.List)
}, { msgFail ->
ctx.status(500).result(objectmapper.writeValueAsString(resultMessage(msgFail)))
})
@@ -2195,7 +2204,7 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
if (db.queuepagingDB.DeleteByIndex(index.toInt())) {
db.queuepagingDB.Resort()
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
db.logDB.Add("AAS", "Deleted queue paging with index $index")
logDB.Add("AAS", "Deleted queue paging with index $index")
} else {
it.status(500)
.result(objectmapper.writeValueAsString(resultMessage("Failed to delete queue paging with index $index")))
@@ -2207,7 +2216,7 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
path("QueueTable") {
get("List") { ctx ->
db.queuetableDB.Get({
ctx.result(MariaDB.ArrayListtoString(db.queuetableDB.List))
ctx.json(db.queuetableDB.List)
}, { msgFail ->
ctx.status(500).result(objectmapper.writeValueAsString(resultMessage(msgFail)))
})
@@ -2233,7 +2242,7 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
if (db.queuetableDB.DeleteByIndex(index.toInt())) {
db.queuetableDB.Resort()
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
db.logDB.Add("AAS", "Deleted queue sound with index $index")
logDB.Add("AAS", "Deleted queue sound with index $index")
} else {
it.status(500)
@@ -2521,7 +2530,7 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
_config.Set(configKeys.DEFAULT_VOICE_TYPE.key, defaultvoice)
_config.Save()
Logger.info { "Changed FIS Codes" }
db.logDB.Add(
logDB.Add(
"AAS",
"Save FIS Codes Message: GOP=$_gop, GBD=$_gbd, GFC=$_gfc, FLD=$_fld, DefaultVoice=$defaultvoice"
)
@@ -2636,7 +2645,7 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
// messages
String_To_List(user.messagebank_ann_id).forEach { msg ->
//println("Looking for message ANN_ID: $msg")
db.messageDB.List.filter { it.ANN_ID == msg.toUInt() }.forEach { xx ->
messageDB.List.filter { it.ANN_ID == msg.toUInt() }.forEach { xx ->
//println("Adding message: ${xx.ANN_ID};${xx.Description};${xx.Language};${xx.Message_Detail}")
result.messages.add("${xx.ANN_ID};${xx.Description};${xx.Language};${xx.Message_Detail}")
}
@@ -2755,9 +2764,9 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
val logfilter = get1.queryParam("filter") ?: ""
if (ValiDateForLogHtml(logdate)) {
val xlsxdata = if (ValidString(logfilter)) {
db.logDB.Export_Log_XLSX(logdate.replace('-', '/'), logfilter)
logDB.Export_Log_XLSX(logdate.replace('-', '/'), logfilter)
} else {
db.logDB.Export_Log_XLSX(logdate.replace('-', '/'), "")
logDB.Export_Log_XLSX(logdate.replace('-', '/'), "")
}
if (xlsxdata != null) {
get1.header(
@@ -2780,7 +2789,7 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
val datelog = ctx.pathParam("datelog")
println("Request log for date: $datelog")
db.logSemiAuto.GetLogSemiAutoForHtml(datelog, null, {
ctx.result(MariaDB.ArrayListtoString(it))
ctx.json(it)
}, { err ->
ctx.status(500).result(objectmapper.writeValueAsString(resultMessage(err)))
})
@@ -2829,8 +2838,8 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
fun Get_Barix_Connection_by_ZoneName(zonename: String): BarixConnection? {
if (ValidString(zonename)) {
val bz = db.broadcastDB.List.find { it.description == zonename }
val sc = if (bz != null) db.soundchannelDB.List.find { it.channel == bz.SoundChannel } else null
val bz = broadcastDB.List.find { it.description == zonename }
val sc = if (bz != null) soundchannelDB.List.find { it.channel == bz.SoundChannel } else null
val ip = sc?.ip ?: ""
if (ValidIPV4(ip)) {
// ketemu ip-nya