Compare commits
34 Commits
c0e920d1d5
...
feature-we
| Author | SHA1 | Date | |
|---|---|---|---|
| cdcb02e976 | |||
| 17b4485e69 | |||
| 4d02ab6d07 | |||
| 8bd57f216a | |||
| 8978a0986d | |||
| bc3f5c5691 | |||
| e5d8d8059e | |||
| 1b84ec133b | |||
| 1a6b7de6ec | |||
| 4da5a2fb05 | |||
| 2ca7004b70 | |||
| 1563e233d6 | |||
| 2fe4a46e3e | |||
| a53270aaed | |||
| 4d3dc538bd | |||
| d2a2626fd5 | |||
| de54d142ae | |||
| 8c49bb827f | |||
| b15470845e | |||
| 5f57e1bf2e | |||
| a133f9a170 | |||
| 1d2c8d1307 | |||
| e426522380 | |||
| 110e6d5b12 | |||
| 470f61c79d | |||
| e78fb932b2 | |||
| e0cdf74dec | |||
| 41d6dd7f47 | |||
| fdc7556dd7 | |||
| 7f647fe9c3 | |||
| d549aee42c | |||
| 6b00bc7eb0 | |||
| 8409307631 | |||
| 2ad26c3ef6 |
88
html/webpage/assets/css/bss-overrides.css
Normal file
88
html/webpage/assets/css/bss-overrides.css
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
:root, [data-bs-theme=light] {
|
||||||
|
--bs-primary: #0d6efd;
|
||||||
|
--bs-primary-rgb: 13,110,253;
|
||||||
|
--bs-primary-text-emphasis: #052C65;
|
||||||
|
--bs-primary-bg-subtle: #CFE2FF;
|
||||||
|
--bs-primary-border-subtle: #9EC5FE;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
--bs-btn-color: #fff;
|
||||||
|
--bs-btn-bg: #0d6efd;
|
||||||
|
--bs-btn-border-color: #0d6efd;
|
||||||
|
--bs-btn-hover-color: #fff;
|
||||||
|
--bs-btn-hover-bg: #0B5ED7;
|
||||||
|
--bs-btn-hover-border-color: #0A58CA;
|
||||||
|
--bs-btn-focus-shadow-rgb: 219,233,255;
|
||||||
|
--bs-btn-active-color: #fff;
|
||||||
|
--bs-btn-active-bg: #0A58CA;
|
||||||
|
--bs-btn-active-border-color: #0A53BE;
|
||||||
|
--bs-btn-disabled-color: #fff;
|
||||||
|
--bs-btn-disabled-bg: #0d6efd;
|
||||||
|
--bs-btn-disabled-border-color: #0d6efd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-outline-primary {
|
||||||
|
--bs-btn-color: #0d6efd;
|
||||||
|
--bs-btn-border-color: #0d6efd;
|
||||||
|
--bs-btn-focus-shadow-rgb: 13,110,253;
|
||||||
|
--bs-btn-hover-color: #fff;
|
||||||
|
--bs-btn-hover-bg: #0d6efd;
|
||||||
|
--bs-btn-hover-border-color: #0d6efd;
|
||||||
|
--bs-btn-active-color: #fff;
|
||||||
|
--bs-btn-active-bg: #0d6efd;
|
||||||
|
--bs-btn-active-border-color: #0d6efd;
|
||||||
|
--bs-btn-disabled-color: #0d6efd;
|
||||||
|
--bs-btn-disabled-bg: transparent;
|
||||||
|
--bs-btn-disabled-border-color: #0d6efd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-4 {
|
||||||
|
margin-top: 1.5rem!important;
|
||||||
|
margin-bottom: 1.5rem!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mt-0 {
|
||||||
|
margin-top: 0!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.me-2 {
|
||||||
|
margin-right: .5rem!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-2 {
|
||||||
|
margin-bottom: .5rem!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-3 {
|
||||||
|
margin-bottom: 1rem!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-4 {
|
||||||
|
margin-bottom: 1.5rem!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-5 {
|
||||||
|
margin-bottom: 3rem!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-7 {
|
||||||
|
margin-bottom: 6rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-auto {
|
||||||
|
margin-bottom: auto!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width:768px) {
|
||||||
|
.me-md-auto {
|
||||||
|
margin-right: auto!important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width:768px) {
|
||||||
|
.mb-md-0 {
|
||||||
|
margin-bottom: 0!important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
629
html/webpage/assets/css/select2.min.css
vendored
Normal file
629
html/webpage/assets/css/select2.min.css
vendored
Normal file
@@ -0,0 +1,629 @@
|
|||||||
|
.select2-container {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container .select2-selection--single {
|
||||||
|
box-sizing: border-box;
|
||||||
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
height: 28px;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container .select2-selection--single .select2-selection__rendered {
|
||||||
|
display: block;
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container .select2-selection--single .select2-selection__clear {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered {
|
||||||
|
padding-right: 8px;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container .select2-selection--multiple {
|
||||||
|
box-sizing: border-box;
|
||||||
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
min-height: 32px;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container .select2-selection--multiple .select2-selection__rendered {
|
||||||
|
display: inline-block;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-left: 8px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container .select2-search--inline {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container .select2-search--inline .select2-search__field {
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: none;
|
||||||
|
font-size: 100%;
|
||||||
|
margin-top: 5px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-dropdown {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
left: -100000px;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 1051;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-results {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-results__options {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-results__option {
|
||||||
|
padding: 6px;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-results__option[aria-selected] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--open .select2-dropdown {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--open .select2-dropdown--above {
|
||||||
|
border-bottom: none;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--open .select2-dropdown--below {
|
||||||
|
border-top: none;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-search--dropdown {
|
||||||
|
display: block;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-search--dropdown .select2-search__field {
|
||||||
|
padding: 4px;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-search--dropdown.select2-search--hide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-close-mask {
|
||||||
|
border: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: block;
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
min-height: 100%;
|
||||||
|
min-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
width: auto;
|
||||||
|
opacity: 0;
|
||||||
|
z-index: 99;
|
||||||
|
background-color: #fff;
|
||||||
|
filter: alpha(opacity=0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-hidden-accessible {
|
||||||
|
border: 0 !important;
|
||||||
|
clip: rect(0 0 0 0) !important;
|
||||||
|
-webkit-clip-path: inset(50%) !important;
|
||||||
|
clip-path: inset(50%) !important;
|
||||||
|
height: 1px !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
position: absolute !important;
|
||||||
|
width: 1px !important;
|
||||||
|
white-space: nowrap !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--single {
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--single .select2-selection__rendered {
|
||||||
|
color: #444;
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--single .select2-selection__clear {
|
||||||
|
cursor: pointer;
|
||||||
|
float: right;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--single .select2-selection__placeholder {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--single .select2-selection__arrow {
|
||||||
|
height: 26px;
|
||||||
|
position: absolute;
|
||||||
|
top: 1px;
|
||||||
|
right: 1px;
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--single .select2-selection__arrow b {
|
||||||
|
border-color: #888 transparent transparent transparent;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 5px 4px 0 4px;
|
||||||
|
height: 0;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -4px;
|
||||||
|
margin-top: -2px;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow {
|
||||||
|
left: 1px;
|
||||||
|
right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default.select2-container--disabled .select2-selection--single {
|
||||||
|
background-color: #eee;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b {
|
||||||
|
border-color: transparent transparent #888 transparent;
|
||||||
|
border-width: 0 4px 5px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--multiple {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--multiple .select2-selection__rendered {
|
||||||
|
box-sizing: border-box;
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 5px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--multiple .select2-selection__rendered li {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--multiple .select2-selection__clear {
|
||||||
|
cursor: pointer;
|
||||||
|
float: right;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-right: 10px;
|
||||||
|
padding: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--multiple .select2-selection__choice {
|
||||||
|
background-color: #e4e4e4;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: default;
|
||||||
|
float: left;
|
||||||
|
margin-right: 5px;
|
||||||
|
margin-top: 5px;
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
|
||||||
|
color: #999;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
|
||||||
|
margin-left: 2px;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default.select2-container--focus .select2-selection--multiple {
|
||||||
|
border: solid black 1px;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default.select2-container--disabled .select2-selection--multiple {
|
||||||
|
background-color: #eee;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default.select2-container--disabled .select2-selection__choice__remove {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default.select2-container--open.select2-container--above .select2-selection--single, .select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple {
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default.select2-container--open.select2-container--below .select2-selection--single, .select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple {
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-search--dropdown .select2-search__field {
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-search--inline .select2-search__field {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
outline: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
-webkit-appearance: textfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-results > .select2-results__options {
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-results__option[role=group] {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-results__option[aria-disabled=true] {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-results__option[aria-selected=true] {
|
||||||
|
background-color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-results__option .select2-results__option {
|
||||||
|
padding-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-results__option .select2-results__option .select2-results__group {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-results__option .select2-results__option .select2-results__option {
|
||||||
|
margin-left: -1em;
|
||||||
|
padding-left: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
|
||||||
|
margin-left: -2em;
|
||||||
|
padding-left: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
|
||||||
|
margin-left: -3em;
|
||||||
|
padding-left: 4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
|
||||||
|
margin-left: -4em;
|
||||||
|
padding-left: 5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
|
||||||
|
margin-left: -5em;
|
||||||
|
padding-left: 6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-results__option--highlighted[aria-selected] {
|
||||||
|
background-color: #5897fb;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-results__group {
|
||||||
|
cursor: default;
|
||||||
|
display: block;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic .select2-selection--single {
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: 0;
|
||||||
|
background-image: -webkit-linear-gradient(top, #fff 50%, #eee 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #fff 50%, #eee 100%);
|
||||||
|
background-image: linear-gradient(to bottom, #fff 50%, #eee 100%);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic .select2-selection--single:focus {
|
||||||
|
border: 1px solid #5897fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic .select2-selection--single .select2-selection__rendered {
|
||||||
|
color: #444;
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic .select2-selection--single .select2-selection__clear {
|
||||||
|
cursor: pointer;
|
||||||
|
float: right;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic .select2-selection--single .select2-selection__placeholder {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic .select2-selection--single .select2-selection__arrow {
|
||||||
|
background-color: #ddd;
|
||||||
|
border: none;
|
||||||
|
border-left: 1px solid #aaa;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
height: 26px;
|
||||||
|
position: absolute;
|
||||||
|
top: 1px;
|
||||||
|
right: 1px;
|
||||||
|
width: 20px;
|
||||||
|
background-image: -webkit-linear-gradient(top, #eee 50%, #ccc 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #eee 50%, #ccc 100%);
|
||||||
|
background-image: linear-gradient(to bottom, #eee 50%, #ccc 100%);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic .select2-selection--single .select2-selection__arrow b {
|
||||||
|
border-color: #888 transparent transparent transparent;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 5px 4px 0 4px;
|
||||||
|
height: 0;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -4px;
|
||||||
|
margin-top: -2px;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow {
|
||||||
|
border: none;
|
||||||
|
border-right: 1px solid #aaa;
|
||||||
|
border-radius: 0;
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
left: 1px;
|
||||||
|
right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic.select2-container--open .select2-selection--single {
|
||||||
|
border: 1px solid #5897fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b {
|
||||||
|
border-color: transparent transparent #888 transparent;
|
||||||
|
border-width: 0 4px 5px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single {
|
||||||
|
border-top: none;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
background-image: -webkit-linear-gradient(top, #fff 0%, #eee 50%);
|
||||||
|
background-image: -o-linear-gradient(top, #fff 0%, #eee 50%);
|
||||||
|
background-image: linear-gradient(to bottom, #fff 0%, #eee 50%);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single {
|
||||||
|
border-bottom: none;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
background-image: -webkit-linear-gradient(top, #eee 50%, #fff 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #eee 50%, #fff 100%);
|
||||||
|
background-image: linear-gradient(to bottom, #eee 50%, #fff 100%);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic .select2-selection--multiple {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: text;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic .select2-selection--multiple:focus {
|
||||||
|
border: 1px solid #5897fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic .select2-selection--multiple .select2-selection__rendered {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic .select2-selection--multiple .select2-selection__clear {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic .select2-selection--multiple .select2-selection__choice {
|
||||||
|
background-color: #e4e4e4;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: default;
|
||||||
|
float: left;
|
||||||
|
margin-right: 5px;
|
||||||
|
margin-top: 5px;
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove {
|
||||||
|
color: #888;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover {
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
|
||||||
|
float: right;
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
|
||||||
|
margin-left: 2px;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic.select2-container--open .select2-selection--multiple {
|
||||||
|
border: 1px solid #5897fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple {
|
||||||
|
border-top: none;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple {
|
||||||
|
border-bottom: none;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic .select2-search--dropdown .select2-search__field {
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic .select2-search--inline .select2-search__field {
|
||||||
|
outline: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic .select2-dropdown {
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic .select2-dropdown--above {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic .select2-dropdown--below {
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic .select2-results > .select2-results__options {
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic .select2-results__option[role=group] {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic .select2-results__option[aria-disabled=true] {
|
||||||
|
color: grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic .select2-results__option--highlighted[aria-selected] {
|
||||||
|
background-color: #3875d7;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic .select2-results__group {
|
||||||
|
cursor: default;
|
||||||
|
display: block;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--classic.select2-container--open .select2-dropdown {
|
||||||
|
border-color: #5897fb;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -62,9 +62,9 @@ body {
|
|||||||
|
|
||||||
.btn-round-basic:focus {
|
.btn-round-basic:focus {
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
border-radius: 20px;
|
border-radius: 8px;
|
||||||
box-shadow: inset 4px 4px 10px #88a5bf7b, inset -4px -4px 10px #ffffff;
|
box-shadow: inset 4px 4px 10px #88a5bf7b, inset -4px -4px 10px #ffffff;
|
||||||
color: #4d4d4d;
|
/*color: #4d4d4d;*/
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
transition: all 0.2s ease-in-out;
|
transition: all 0.2s ease-in-out;
|
||||||
@@ -72,7 +72,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn-round-basic {
|
.btn-round-basic {
|
||||||
border-radius: 20px;
|
border-radius: 08px;
|
||||||
box-shadow: rgba(136, 165, 191, 0.48) 6px 2px 16px 0px, rgba(255, 255, 255, 0.8) -6px -2px 16px 0px;
|
box-shadow: rgba(136, 165, 191, 0.48) 6px 2px 16px 0px, rgba(255, 255, 255, 0.8) -6px -2px 16px 0px;
|
||||||
--bs-btn-hover-bg: #ffffff;
|
--bs-btn-hover-bg: #ffffff;
|
||||||
}
|
}
|
||||||
@@ -82,29 +82,29 @@ body {
|
|||||||
box-shadow: inset 2px 2px 5px #bcbcbc, inset -2px -2px 5px #ffffff, 2px 2px 5px #bcbcbc, -2px -2px 5px #ffffff;
|
box-shadow: inset 2px 2px 5px #bcbcbc, inset -2px -2px 5px #ffffff, 2px 2px 5px #bcbcbc, -2px -2px 5px #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-import {
|
.color-import, .color-import:hover, .color-import:focus {
|
||||||
color: var(--bs-teal);
|
color: var(--bs-teal);
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-remove {
|
.color-remove, .color-remove:hover, .color-remove:focus {
|
||||||
color: var(--bs-danger);
|
color: var(--bs-danger);
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-edit {
|
.color-edit, .color-edit:hover, .color-edit:focus {
|
||||||
color: var(--bs-primary-text-emphasis);
|
color: var(--bs-primary-text-emphasis);
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-add {
|
.color-add, .color-add:hover, .color-add:focus {
|
||||||
color: var(--bs-primary);
|
color: var(--bs-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-login {
|
.input-login {
|
||||||
border-radius: 50px;
|
border-radius: 8px;
|
||||||
box-shadow: rgba(9, 30, 66, 0.25) 0px 4px 8px -2px, rgba(9, 30, 66, 0.08) 0px 0px 0px 1px;
|
box-shadow: rgba(9, 30, 66, 0.25) 0px 4px 8px -2px, rgba(9, 30, 66, 0.08) 0px 0px 0px 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-login {
|
.btn-login {
|
||||||
border-radius: 20px;
|
border-radius: 8px;
|
||||||
box-shadow: rgba(136, 165, 191, 0.48) 6px 2px 16px 0px, rgba(255, 255, 255, 0.8) -6px -2px 16px 0px;
|
box-shadow: rgba(136, 165, 191, 0.48) 6px 2px 16px 0px, rgba(255, 255, 255, 0.8) -6px -2px 16px 0px;
|
||||||
--bs-btn-hover-bg: #5780f2;
|
--bs-btn-hover-bg: #5780f2;
|
||||||
background-color: #5278e1;
|
background-color: #5278e1;
|
||||||
@@ -227,25 +227,23 @@ nav-item:focus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.accordion-item.active {
|
.accordion-item.active {
|
||||||
/*background: rgba(45, 53, 120, 0.15);*/
|
background: rgba(45, 53, 120, 0.15);
|
||||||
/*box-shadow: inset 4px 4px 10px rgba(45, 53, 120, 0.25), inset -4px -4px 10px rgba(255, 255, 255, 0.8);*/
|
box-shadow: inset 4px 4px 10px rgba(45, 53, 120, 0.25), inset -4px -4px 10px rgba(255, 255, 255, 0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-accordion {
|
.bg-accordion {
|
||||||
border: white 2px;
|
border: white 2px;
|
||||||
/*border-radius: 10px;*/
|
border-radius: 8px;
|
||||||
background: #f8f9fd;
|
background: #f8f9fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-channel {
|
.card-channel {
|
||||||
border-radius: 12px;
|
border-radius: 8px;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
backdrop-filter: blur(12px);
|
backdrop-filter: blur(12px);
|
||||||
-webkit-backdrop-filter: blur(12px);
|
-webkit-backdrop-filter: blur(12px);
|
||||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||||
border: #ffffff solid 2px !important;
|
border: #ffffff solid 2px !important;
|
||||||
/*padding-left: 2px;*/
|
|
||||||
/*padding-right: 5px;*/
|
|
||||||
padding-top: 12px;
|
padding-top: 12px;
|
||||||
padding-bottom: 12px;
|
padding-bottom: 12px;
|
||||||
}
|
}
|
||||||
@@ -255,3 +253,68 @@ nav-item:focus {
|
|||||||
border: white solid 3px;
|
border: white solid 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
background-color: #172066;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-modal-body {
|
||||||
|
background-color: #f8f9fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-header-modal {
|
||||||
|
background-color: #f0f2ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pad-row-btn {
|
||||||
|
margin: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pad-2 {
|
||||||
|
border-radius: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-login {
|
||||||
|
text-align: left;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0;
|
||||||
|
color: #3E4C66;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h-login {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #2E3A59;
|
||||||
|
}
|
||||||
|
|
||||||
|
#file-input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-setting {
|
||||||
|
border-radius: 8px;
|
||||||
|
border: white solid 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#drop-area {
|
||||||
|
border: 2px dashed #ccc;
|
||||||
|
border-radius: 20px;
|
||||||
|
width: 400px;
|
||||||
|
height: 200px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-family: sans-serif;
|
||||||
|
color: #666;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s, border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#drop-area.highlight {
|
||||||
|
background: #f0f8ff;
|
||||||
|
border-color: #0d6efd;
|
||||||
|
color: #0d6efd;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* @typedef {Object} BroadcastZone
|
||||||
|
* @property {number} index
|
||||||
|
* @property {string} description
|
||||||
|
* @property {String} SoundChannel
|
||||||
|
* @property {String} Box
|
||||||
|
* @property {String} Relay
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of broadcast zones available
|
||||||
|
* @type {BroadcastZone[]}
|
||||||
|
*/
|
||||||
|
window.BroadcastZoneList ??= [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Currently selected broadcast zone row in the table
|
* Currently selected broadcast zone row in the table
|
||||||
@@ -6,6 +19,12 @@
|
|||||||
*/
|
*/
|
||||||
window.selectedBroadcastZoneRow = null;
|
window.selectedBroadcastZoneRow = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of sound channels available
|
||||||
|
* @type {String[]}
|
||||||
|
*/
|
||||||
|
window.SoundChannelList = []
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fill broadcast zone table body with values
|
* Fill broadcast zone table body with values
|
||||||
* @param {BroadcastZone[]} vv values to fill
|
* @param {BroadcastZone[]} vv values to fill
|
||||||
@@ -23,7 +42,7 @@ function fill_broadcastzonetablebody(vv) {
|
|||||||
</tr>`;
|
</tr>`;
|
||||||
$('#broadcastzonetablebody').append(row);
|
$('#broadcastzonetablebody').append(row);
|
||||||
let $addedrow = $('#broadcastzonetablebody tr:last');
|
let $addedrow = $('#broadcastzonetablebody tr:last');
|
||||||
$addedrow.click(function () {
|
$addedrow.off('click').on('click', function () {
|
||||||
if (window.selectedBroadcastZoneRow) {
|
if (window.selectedBroadcastZoneRow) {
|
||||||
window.selectedBroadcastZoneRow.find('td').css('background-color', '');
|
window.selectedBroadcastZoneRow.find('td').css('background-color', '');
|
||||||
if (window.selectedBroadcastZoneRow.is($(this))) {
|
if (window.selectedBroadcastZoneRow.is($(this))) {
|
||||||
@@ -42,7 +61,34 @@ function fill_broadcastzonetablebody(vv) {
|
|||||||
$('#tablesize').text("Table Size: " + vv.length);
|
$('#tablesize').text("Table Size: " + vv.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reload broadcast zones from server
|
||||||
|
* @param {String} APIURL API URL endpoint (default "BroadcastZones/")
|
||||||
|
*/
|
||||||
|
function reloadBroadcastZones(APIURL = "BroadcastZones/") {
|
||||||
|
window.BroadcastZoneList = [];
|
||||||
|
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
||||||
|
if (Array.isArray(okdata)) {
|
||||||
|
//console.log("reloadBroadcastZones : ", okdata)
|
||||||
|
window.BroadcastZoneList.push(...okdata);
|
||||||
|
fill_broadcastzonetablebody(window.BroadcastZoneList);
|
||||||
|
} else console.log("reloadBroadcastZones: okdata is not array");
|
||||||
|
}, (errdata) => {
|
||||||
|
alert("Error loading broadcast zones : " + errdata.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchSoundChannels(APIURL = "SoundChannel/") {
|
||||||
|
window.SoundChannelList = [];
|
||||||
|
fetchAPI(APIURL + "SoundChannelDescriptions", "GET", {}, null, (okdata) => {
|
||||||
|
if (Array.isArray(okdata)) {
|
||||||
|
//console.log("fetchSoundChannels : ", okdata)
|
||||||
|
window.SoundChannelList.push(...okdata);
|
||||||
|
} else console.log("fetchSoundChannels: okdata is not array");
|
||||||
|
}, (errdata) => {
|
||||||
|
alert("Error loading sound channels : " + errdata.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
console.log("broadcastzones.js loaded successfully");
|
console.log("broadcastzones.js loaded successfully");
|
||||||
@@ -67,7 +113,7 @@ $(document).ready(function () {
|
|||||||
|
|
||||||
|
|
||||||
let $findzone = $('#findzone');
|
let $findzone = $('#findzone');
|
||||||
$findzone.on('input', function () {
|
$findzone.off('input').on('input', function () {
|
||||||
let searchTerm = $findzone.val().trim().toLowerCase();
|
let searchTerm = $findzone.val().trim().toLowerCase();
|
||||||
if (searchTerm.length > 0) {
|
if (searchTerm.length > 0) {
|
||||||
window.selectedBroadcastZoneRow = null;
|
window.selectedBroadcastZoneRow = null;
|
||||||
@@ -97,36 +143,35 @@ $(document).ready(function () {
|
|||||||
$broadcastzonedescription.val('');
|
$broadcastzonedescription.val('');
|
||||||
// fill broadcastzonesoundchannel from SoundChannelList
|
// fill broadcastzonesoundchannel from SoundChannelList
|
||||||
$broadcastzonesoundchannel.empty();
|
$broadcastzonesoundchannel.empty();
|
||||||
if (Array.isArray(SoundChannelList) && SoundChannelList.length > 0) {
|
console.log("SoundChannelList:", window.SoundChannelList);
|
||||||
|
if (Array.isArray(window.SoundChannelList) && window.SoundChannelList.length > 0) {
|
||||||
// SoundChannelList ada isinya
|
// SoundChannelList ada isinya
|
||||||
SoundChannelList.forEach(ch => {
|
window.SoundChannelList.forEach(ch => {
|
||||||
if (ch.channel && ch.channel.length > 0){
|
if (ch && ch.length>0){
|
||||||
// hanya yang punya channel saja
|
$broadcastzonesoundchannel.append($('<option>').val(ch).text(ch));
|
||||||
$broadcastzonesoundchannel.append($('<option>').val(ch.channel).text(ch.channel));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
$broadcastzonebox.val('');
|
$broadcastzonebox.val('1').prop('disabled', true);
|
||||||
for (let i = 1; i <= 32; i++) {
|
for (let i = 1; i <= 32; i++) {
|
||||||
cbRelay(i).prop('checked', false);
|
cbRelay(i).prop('checked', false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reloadBroadcastZones(APIURL_BroadcastZone, () => {
|
fetchSoundChannels();
|
||||||
fill_broadcastzonetablebody(window.BroadcastZoneList);
|
reloadBroadcastZones(APIURL_BroadcastZone);
|
||||||
});
|
|
||||||
|
|
||||||
$btnClear.click(() => {
|
$btnClear.off('click').on('click', () => {
|
||||||
DoClear(APIURL_BroadcastZone, "BroadcastZones", (okdata) => {
|
DoClear(APIURL_BroadcastZone, "BroadcastZones", (okdata) => {
|
||||||
reloadBroadcastZones(APIURL_BroadcastZone);
|
reloadBroadcastZones(APIURL_BroadcastZone);
|
||||||
fill_broadcastzonetablebody(window.BroadcastZoneList);
|
|
||||||
alert("Success clear broadcast zones: " + okdata.message);
|
alert("Success clear broadcast zones: " + okdata.message);
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error clear broadcast zones: " + errdata.message);
|
alert("Error clear broadcast zones: " + errdata.message);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$btnAdd.click(() => {
|
$btnAdd.off('click').on('click', () => {
|
||||||
$broadcastzonemodal.modal('show');
|
$broadcastzonemodal.modal('show');
|
||||||
clearBroadcastZoneModal();
|
clearBroadcastZoneModal();
|
||||||
|
|
||||||
@@ -165,10 +210,8 @@ $(document).ready(function () {
|
|||||||
Relay: relay
|
Relay: relay
|
||||||
};
|
};
|
||||||
fetchAPI(APIURL_BroadcastZone + "Add", "POST", {}, bz, (okdata) => {
|
fetchAPI(APIURL_BroadcastZone + "Add", "POST", {}, bz, (okdata) => {
|
||||||
reloadBroadcastZones(APIURL_BroadcastZone, () => {
|
reloadBroadcastZones(APIURL_BroadcastZone);
|
||||||
fill_broadcastzonetablebody(window.BroadcastZoneList);
|
alert("Success add new broadcast zone: " + okdata.message);
|
||||||
alert("Success add new broadcast zone: " + okdata.message);
|
|
||||||
});
|
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error add new broadcast zone: " + errdata.message);
|
alert("Error add new broadcast zone: " + errdata.message);
|
||||||
});
|
});
|
||||||
@@ -180,12 +223,12 @@ $(document).ready(function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$btnRemove.click(() => {
|
$btnRemove.off('click').on('click', () => {
|
||||||
if (window.selectedBroadcastZoneRow) {
|
if (window.selectedBroadcastZoneRow) {
|
||||||
let cells = window.selectedBroadcastZoneRow.find('td');
|
let cells = window.selectedBroadcastZoneRow.find('td');
|
||||||
/** @type {BroadcastZone} */
|
/** @type {BroadcastZone} */
|
||||||
let bz = {
|
let bz = {
|
||||||
index: cells.eq(0).text(),
|
index: Number(cells.eq(0).text()),
|
||||||
description: cells.eq(1).text(),
|
description: cells.eq(1).text(),
|
||||||
SoundChannel: cells.eq(2).text(),
|
SoundChannel: cells.eq(2).text(),
|
||||||
Box: cells.eq(3).text(),
|
Box: cells.eq(3).text(),
|
||||||
@@ -193,10 +236,8 @@ $(document).ready(function () {
|
|||||||
};
|
};
|
||||||
if (confirm(`Are you sure to delete broadcast zone [${bz.index}] Description=${bz.description}?`)) {
|
if (confirm(`Are you sure to delete broadcast zone [${bz.index}] Description=${bz.description}?`)) {
|
||||||
fetchAPI(APIURL_BroadcastZone + "DeleteByIndex/" + bz.index, "DELETE", {}, null, (okdata) => {
|
fetchAPI(APIURL_BroadcastZone + "DeleteByIndex/" + bz.index, "DELETE", {}, null, (okdata) => {
|
||||||
reloadBroadcastZones(APIURL_BroadcastZone, () => {
|
reloadBroadcastZones(APIURL_BroadcastZone);
|
||||||
fill_broadcastzonetablebody(window.BroadcastZoneList);
|
alert("Success delete broadcast zone: " + okdata.message);
|
||||||
alert("Success delete broadcast zone: " + okdata.message);
|
|
||||||
});
|
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error delete broadcast zone: " + errdata.message);
|
alert("Error delete broadcast zone: " + errdata.message);
|
||||||
});
|
});
|
||||||
@@ -204,12 +245,12 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$btnEdit.click(() => {
|
$btnEdit.off('click').on('click', () => {
|
||||||
if (window.selectedBroadcastZoneRow) {
|
if (window.selectedBroadcastZoneRow) {
|
||||||
let cells = window.selectedBroadcastZoneRow.find('td');
|
let cells = window.selectedBroadcastZoneRow.find('td');
|
||||||
/** @type {BroadcastZone} */
|
/** @type {BroadcastZone} */
|
||||||
let bz = {
|
let bz = {
|
||||||
index: cells.eq(0).text(),
|
index: Number(cells.eq(0).text()),
|
||||||
description: cells.eq(1).text(),
|
description: cells.eq(1).text(),
|
||||||
SoundChannel: cells.eq(2).text(),
|
SoundChannel: cells.eq(2).text(),
|
||||||
Box: cells.eq(3).text(),
|
Box: cells.eq(3).text(),
|
||||||
@@ -221,12 +262,12 @@ $(document).ready(function () {
|
|||||||
$broadcastzoneindex.val(bz.index);
|
$broadcastzoneindex.val(bz.index);
|
||||||
$broadcastzonedescription.val(bz.description);
|
$broadcastzonedescription.val(bz.description);
|
||||||
$broadcastzonesoundchannel.val(bz.SoundChannel);
|
$broadcastzonesoundchannel.val(bz.SoundChannel);
|
||||||
$broadcastzonebox.val(bz.id);
|
$broadcastzonebox.val(bz.Box);
|
||||||
if (bz.bp) {
|
if (bz.Relay && bz.Relay.length > 0) {
|
||||||
bz.bp.split(';').forEach(relayId => {
|
bz.Relay.split(';').map(Number).filter(n => !isNaN(n) && n>=1 && n<=8).forEach(relayId => {
|
||||||
let id = parseInt(relayId, 10);
|
cbRelay(relayId).prop('checked', true);
|
||||||
cbRelay(id).prop('checked', true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
$broadcastzonemodal.off('click.broadcastzonesave').on('click.broadcastzonesave', '#broadcastzonesave', function () {
|
$broadcastzonemodal.off('click.broadcastzonesave').on('click.broadcastzonesave', '#broadcastzonesave', function () {
|
||||||
let description = $broadcastzonedescription.val().trim();
|
let description = $broadcastzonedescription.val().trim();
|
||||||
@@ -262,10 +303,8 @@ $(document).ready(function () {
|
|||||||
Relay: relay
|
Relay: relay
|
||||||
};
|
};
|
||||||
fetchAPI(APIURL_BroadcastZone + "UpdateByIndex/" + bz.index, "PATCH", {}, bzUpdate, (okdata) => {
|
fetchAPI(APIURL_BroadcastZone + "UpdateByIndex/" + bz.index, "PATCH", {}, bzUpdate, (okdata) => {
|
||||||
reloadBroadcastZones(APIURL_BroadcastZone, () => {
|
reloadBroadcastZones(APIURL_BroadcastZone);
|
||||||
fill_broadcastzonetablebody(window.BroadcastZoneList);
|
alert("Success edit broadcast zone: " + okdata.message);
|
||||||
alert("Success edit broadcast zone: " + okdata.message);
|
|
||||||
});
|
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error edit broadcast zone: " + errdata.message);
|
alert("Error edit broadcast zone: " + errdata.message);
|
||||||
});
|
});
|
||||||
@@ -278,16 +317,14 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$btnExport.click(() => {
|
$btnExport.off('click').on('click', () => {
|
||||||
DoExport(APIURL_BroadcastZone, "broadcastzones.xlsx", {});
|
DoExport(APIURL_BroadcastZone, "broadcastzones.xlsx", {});
|
||||||
});
|
});
|
||||||
|
|
||||||
$btnImport.click(() => {
|
$btnImport.off('click').on('click', () => {
|
||||||
DoImport(APIURL_BroadcastZone, (okdata) => {
|
DoImport(APIURL_BroadcastZone, (okdata) => {
|
||||||
reloadBroadcastZones(APIURL_BroadcastZone, () => {
|
reloadBroadcastZones(APIURL_BroadcastZone);
|
||||||
fill_broadcastzonetablebody(window.BroadcastZoneList);
|
alert("Success import broadcast zones: " + okdata.message);
|
||||||
alert("Success import broadcast zones: " + okdata.message);
|
|
||||||
});
|
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error importing broadcast zones from XLSX: " + errdata.message);
|
alert("Error importing broadcast zones from XLSX: " + errdata.message);
|
||||||
});
|
});
|
||||||
|
|||||||
7
html/webpage/assets/js/bs-init.js
Normal file
7
html/webpage/assets/js/bs-init.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
|
||||||
|
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bss-tooltip]'));
|
||||||
|
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
|
||||||
|
return new bootstrap.Tooltip(tooltipTriggerEl);
|
||||||
|
})
|
||||||
|
}, false);
|
||||||
31
html/webpage/assets/js/dragdrop.js
vendored
Normal file
31
html/webpage/assets/js/dragdrop.js
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
const dropArea = document.getElementById("drop-area");
|
||||||
|
const fileInput = document.getElementById("file-input");
|
||||||
|
|
||||||
|
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
||||||
|
dropArea.addEventListener(eventName, e => e.preventDefault());
|
||||||
|
dropArea.addEventListener(eventName, e => e.stopPropagation());
|
||||||
|
});
|
||||||
|
|
||||||
|
['dragenter', 'dragover'].forEach(eventName => {
|
||||||
|
dropArea.addEventListener(eventName, () => dropArea.classList.add('highlight'));
|
||||||
|
});
|
||||||
|
|
||||||
|
['dragleave', 'drop'].forEach(eventName => {
|
||||||
|
dropArea.addEventListener(eventName, () => dropArea.classList.remove('highlight'));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
dropArea.addEventListener('click', () => fileInput.click());
|
||||||
|
|
||||||
|
dropArea.addEventListener('drop', e => {
|
||||||
|
const files = e.dataTransfer.files;
|
||||||
|
handleFiles(files);
|
||||||
|
});
|
||||||
|
|
||||||
|
fileInput.addEventListener('change', e => {
|
||||||
|
handleFiles(e.target.files);
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleFiles(files) {
|
||||||
|
console.log("file dropped");
|
||||||
|
}
|
||||||
@@ -1,4 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* @typedef {Object} LanguageBank
|
||||||
|
* @property {number} index
|
||||||
|
* @property {string} tag
|
||||||
|
* @property {string} language
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** List of Languagebank data loaded from server
|
||||||
|
* @type {LanguageBank[]}
|
||||||
|
*/
|
||||||
|
window.languagebankdata = [];
|
||||||
/**
|
/**
|
||||||
* Currently selected languagebank row in the table
|
* Currently selected languagebank row in the table
|
||||||
* @type {JQuery<HTMLElement>|null}
|
* @type {JQuery<HTMLElement>|null}
|
||||||
@@ -39,7 +50,22 @@ function fill_languagebanktablebody(vv) {
|
|||||||
$('#tablesize').text("Table Size: " + vv.length);
|
$('#tablesize').text("Table Size: " + vv.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reload language bank from server
|
||||||
|
* @param {string} APIURL API URL endpoint, default "LanguageLink/"
|
||||||
|
*/
|
||||||
|
function reloadLanguageBank(APIURL = "LanguageLink/") {
|
||||||
|
window.languagebankdata = [];
|
||||||
|
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
||||||
|
if (Array.isArray(okdata)) {
|
||||||
|
window.languagebankdata.push(...okdata);
|
||||||
|
window.selectedlanguagerow = null;
|
||||||
|
fill_languagebanktablebody(window.languagebankdata);
|
||||||
|
}
|
||||||
|
}, (errdata) => {
|
||||||
|
alert("Error loading languagebank : " + errdata.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
console.log('languagebank.js loaded');
|
console.log('languagebank.js loaded');
|
||||||
@@ -89,15 +115,11 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
reloadLanguageBank(APIURL, () => {
|
reloadLanguageBank(APIURL);
|
||||||
fill_languagebanktablebody(window.languagebankdata);
|
|
||||||
});
|
|
||||||
$btnClear.click(() => {
|
$btnClear.click(() => {
|
||||||
DoClear(APIURL, "LanguageLink", (okdata) => {
|
DoClear(APIURL, "LanguageLink", (okdata) => {
|
||||||
reloadLanguageBank(APIURL, () => {
|
reloadLanguageBank(APIURL);
|
||||||
fill_languagebanktablebody(window.languagebankdata);
|
alert("Success clear languageLink : " + okdata.message);
|
||||||
alert("Success clear languageLink : " + okdata.message);
|
|
||||||
});
|
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error clear languageLink : " + errdata.message);
|
alert("Error clear languageLink : " + errdata.message);
|
||||||
});
|
});
|
||||||
@@ -134,10 +156,8 @@ $(document).ready(function () {
|
|||||||
language: langString
|
language: langString
|
||||||
}
|
}
|
||||||
fetchAPI(APIURL + "Add", "POST", {}, ll, (okdata) => {
|
fetchAPI(APIURL + "Add", "POST", {}, ll, (okdata) => {
|
||||||
reloadLanguageBank(APIURL, () => {
|
alert("Success add language : " + okdata.message);
|
||||||
fill_languagebanktablebody(window.languagebankdata);
|
reloadLanguageBank(APIURL);
|
||||||
alert("Success add language : " + okdata.message);
|
|
||||||
});
|
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error add language : " + errdata.message);
|
alert("Error add language : " + errdata.message);
|
||||||
});
|
});
|
||||||
@@ -155,16 +175,14 @@ $(document).ready(function () {
|
|||||||
let cells = window.selectedlanguagerow.find('td');
|
let cells = window.selectedlanguagerow.find('td');
|
||||||
/** @type {Language} */
|
/** @type {Language} */
|
||||||
let ll = {
|
let ll = {
|
||||||
index: cells.eq(0).text(),
|
index: Number(cells.eq(0).text()),
|
||||||
tag: cells.eq(1).text(),
|
tag: cells.eq(1).text(),
|
||||||
language: cells.eq(2).text()
|
language: cells.eq(2).text()
|
||||||
}
|
}
|
||||||
if (confirm(`Are you sure to delete language [${ll.index}] Tag=${ll.tag} Language=${ll.language}?`)) {
|
if (confirm(`Are you sure to delete language [${ll.index}] Tag=${ll.tag} Language=${ll.language}?`)) {
|
||||||
fetchAPI(APIURL + "DeleteByIndex/" + ll.index, "DELETE", {}, null, (okdata) => {
|
fetchAPI(APIURL + "DeleteByIndex/" + ll.index, "DELETE", {}, null, (okdata) => {
|
||||||
reloadLanguageBank(APIURL, () => {
|
reloadLanguageBank(APIURL);
|
||||||
fill_languagebanktablebody(window.languagebankdata);
|
alert("Success delete language : " + okdata.message);
|
||||||
alert("Success delete language : " + okdata.message);
|
|
||||||
});
|
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error delete language : " + errdata.message);
|
alert("Error delete language : " + errdata.message);
|
||||||
});
|
});
|
||||||
@@ -176,7 +194,7 @@ $(document).ready(function () {
|
|||||||
let cells = window.selectedlanguagerow.find('td');
|
let cells = window.selectedlanguagerow.find('td');
|
||||||
/** @type {Language} */
|
/** @type {Language} */
|
||||||
let ll = {
|
let ll = {
|
||||||
index: cells.eq(0).text(),
|
index: Number(cells.eq(0).text()),
|
||||||
tag: cells.eq(1).text(),
|
tag: cells.eq(1).text(),
|
||||||
language: cells.eq(2).text()
|
language: cells.eq(2).text()
|
||||||
}
|
}
|
||||||
@@ -221,10 +239,8 @@ $(document).ready(function () {
|
|||||||
ll.tag = tag;
|
ll.tag = tag;
|
||||||
ll.language = langString;
|
ll.language = langString;
|
||||||
fetchAPI(APIURL + "UpdateByIndex/" + ll.index, "PATCH", {}, ll, (okdata) => {
|
fetchAPI(APIURL + "UpdateByIndex/" + ll.index, "PATCH", {}, ll, (okdata) => {
|
||||||
reloadLanguageBank(APIURL, () => {
|
reloadLanguageBank(APIURL);
|
||||||
fill_languagebanktablebody(window.languagebankdata);
|
alert("Success edit language : " + okdata.message);
|
||||||
alert("Success edit language : " + okdata.message);
|
|
||||||
});
|
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error edit language : " + errdata.message);
|
alert("Error edit language : " + errdata.message);
|
||||||
});
|
});
|
||||||
@@ -246,10 +262,8 @@ $(document).ready(function () {
|
|||||||
});
|
});
|
||||||
$btnImport.click(() => {
|
$btnImport.click(() => {
|
||||||
DoImport(APIURL, (okdata) => {
|
DoImport(APIURL, (okdata) => {
|
||||||
reloadLanguageBank(APIURL, () => {
|
reloadLanguageBank(APIURL);
|
||||||
fill_languagebanktablebody(window.languagebankdata);
|
alert("Success import languagebank : " + okdata.message);
|
||||||
alert("Success import languagebank : " + okdata.message);
|
|
||||||
});
|
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error importing languagebank from XLSX : " + errdata.message);
|
alert("Error importing languagebank from XLSX : " + errdata.message);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* @typedef {Object} MessageBank
|
||||||
|
* @property {number} index
|
||||||
|
* @property {string} description
|
||||||
|
* @property {string} language
|
||||||
|
* @property {number} aNN_ID
|
||||||
|
* @property {string} voice_Type
|
||||||
|
* @property {string} message_Detail
|
||||||
|
* @property {string} message_TAGS
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of Messagebank data loaded from server
|
||||||
|
* @type {MessageBank[]}
|
||||||
|
*/
|
||||||
|
window.messagebankdata ??= [];
|
||||||
/**
|
/**
|
||||||
* Currently selected messagebank row in the table
|
* Currently selected messagebank row in the table
|
||||||
* @type {JQuery<HTMLElement>|null}
|
* @type {JQuery<HTMLElement>|null}
|
||||||
@@ -44,7 +59,22 @@ function fill_messagebanktablebody(vv) {
|
|||||||
$('#tablesize').text("Table Size: " + vv.length);
|
$('#tablesize').text("Table Size: " + vv.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reload message bank from server
|
||||||
|
* @param {string} APIURL API URL endpoint, default "MessageBank/"
|
||||||
|
*/
|
||||||
|
function reloadMessageBank(APIURL = "MessageBank/") {
|
||||||
|
window.messagebankdata ??= [];
|
||||||
|
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
||||||
|
if (Array.isArray(okdata)) {
|
||||||
|
window.messagebankdata.push(...okdata);
|
||||||
|
window.selectedmessagerow = null;
|
||||||
|
fill_messagebanktablebody(window.messagebankdata);
|
||||||
|
}
|
||||||
|
}, (errdata) => {
|
||||||
|
alert("Error loading messagebank : " + errdata.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
console.log("messagebank.js loaded");
|
console.log("messagebank.js loaded");
|
||||||
@@ -91,17 +121,32 @@ $(document).ready(function () {
|
|||||||
function refill_messageavailablevariables() {
|
function refill_messageavailablevariables() {
|
||||||
$messageavailablevariables.empty();
|
$messageavailablevariables.empty();
|
||||||
categories.forEach(cat => {
|
categories.forEach(cat => {
|
||||||
$messageavailablevariables.append(ListItem(`{${cat}}`));
|
$messageavailablevariables.append(ListItem(`[${cat}]`));
|
||||||
});
|
});
|
||||||
if ($messagelanguage.val() && $messagevoicetype.val()) {
|
let lang = $messagelanguage.val();
|
||||||
soundbankdata
|
let vt = $messagevoicetype.val();
|
||||||
.filter(sb => sb.language.toLowerCase() === $messagelanguage.val().toLowerCase())
|
if (lang && lang.length > 0){
|
||||||
.filter(sb => sb.voiceType.toLowerCase() === $messagevoicetype.val().toLowerCase())
|
console.log("Selected Language:", lang);
|
||||||
.filter(sb => sb.category.toLowerCase() === "phrase")
|
if (vt && vt.length > 0){
|
||||||
.forEach(sb => {
|
console.log("Selected Voice Type:", vt);
|
||||||
$messageavailablevariables.append(ListItem(`[${sb.Description}]`));
|
|
||||||
|
fetchAPI(`SoundBank/GetPhrases/${lang}/${vt}`, "GET", {}, null, (okdata) => {
|
||||||
|
if (Array.isArray(okdata) && okdata.length > 0) {
|
||||||
|
console.log(`Loaded ${okdata.length} phrases from soundbank for language=${lang} and voiceType=${vt}`);
|
||||||
|
console.log(JSON.stringify(okdata));
|
||||||
|
okdata.forEach(sb => {
|
||||||
|
if (sb.description && sb.description.length > 0) {
|
||||||
|
$messageavailablevariables.append(ListItem(`${sb.description} [${sb.TAG}]`));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, (errdata) => {
|
||||||
|
//alert("Error loading phrases from soundbank : " + errdata.message);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +158,7 @@ $(document).ready(function () {
|
|||||||
$messagedescription.val('');
|
$messagedescription.val('');
|
||||||
// fill messagelanguage options from languages[]
|
// fill messagelanguage options from languages[]
|
||||||
$messagelanguage.empty();
|
$messagelanguage.empty();
|
||||||
languages.forEach(lang => {
|
window.languages.forEach(lang => {
|
||||||
$messagelanguage.append(new Option(lang, lang));
|
$messagelanguage.append(new Option(lang, lang));
|
||||||
});
|
});
|
||||||
$messagelanguage.val(null);
|
$messagelanguage.val(null);
|
||||||
@@ -124,7 +169,7 @@ $(document).ready(function () {
|
|||||||
$messageannid.val(1);
|
$messageannid.val(1);
|
||||||
// fill messagevoicetype options from voiceTypes[]
|
// fill messagevoicetype options from voiceTypes[]
|
||||||
$messagevoicetype.empty();
|
$messagevoicetype.empty();
|
||||||
voiceTypes.forEach(vt => {
|
window.voiceTypes.forEach(vt => {
|
||||||
$messagevoicetype.append(new Option(vt, vt));
|
$messagevoicetype.append(new Option(vt, vt));
|
||||||
});
|
});
|
||||||
$messagevoicetype.val(null);
|
$messagevoicetype.val(null);
|
||||||
@@ -178,15 +223,11 @@ $(document).ready(function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
reloadMessageBank(APIURL, () => {
|
reloadMessageBank(APIURL);
|
||||||
fill_messagebanktablebody(window.messagebankdata);
|
|
||||||
});
|
|
||||||
$btnClear.click(() => {
|
$btnClear.click(() => {
|
||||||
DoClear(APIURL, "Messagebank", (okdata) => {
|
DoClear(APIURL, "Messagebank", (okdata) => {
|
||||||
reloadMessageBank(APIURL, () => {
|
reloadMessageBank(APIURL);
|
||||||
fill_messagebanktablebody(window.messagebankdata);
|
alert("Success clear messagebank : " + okdata.message);
|
||||||
alert("Success clear messagebank : " + okdata.message);
|
|
||||||
});
|
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error clear messagebank : " + errdata.message);
|
alert("Error clear messagebank : " + errdata.message);
|
||||||
});
|
});
|
||||||
@@ -261,10 +302,8 @@ $(document).ready(function () {
|
|||||||
};
|
};
|
||||||
// send to server using fetchAPI
|
// send to server using fetchAPI
|
||||||
fetchAPI(APIURL + "Add", "POST", mb, null, (okdata) => {
|
fetchAPI(APIURL + "Add", "POST", mb, null, (okdata) => {
|
||||||
reloadMessageBank(APIURL, () => {
|
reloadMessageBank(APIURL);
|
||||||
fill_messagebanktablebody(window.messagebankdata);
|
alert("Success add new messagebank : " + okdata.message);
|
||||||
alert("Success add new messagebank : " + okdata.message);
|
|
||||||
});
|
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error add new messagebank : " + errdata.message);
|
alert("Error add new messagebank : " + errdata.message);
|
||||||
});
|
});
|
||||||
@@ -281,7 +320,7 @@ $(document).ready(function () {
|
|||||||
let cells = window.selectedmessagerow.find('td');
|
let cells = window.selectedmessagerow.find('td');
|
||||||
/** @type {MessageBank} */
|
/** @type {MessageBank} */
|
||||||
let mb = {
|
let mb = {
|
||||||
index: cells.eq(0).text(),
|
index: Number(cells.eq(0).text()),
|
||||||
description: cells.eq(1).text(),
|
description: cells.eq(1).text(),
|
||||||
language: cells.eq(2).text(),
|
language: cells.eq(2).text(),
|
||||||
aNN_ID: parseInt(cells.eq(3).text()),
|
aNN_ID: parseInt(cells.eq(3).text()),
|
||||||
@@ -292,10 +331,8 @@ $(document).ready(function () {
|
|||||||
|
|
||||||
if (confirm(`Are you sure to delete messagebank [${mb.index}] Description=${mb.description}? ANN_ID=${mb.aNN_ID} Language=${mb.language} Voice_Type=${mb.voice_Type} `)) {
|
if (confirm(`Are you sure to delete messagebank [${mb.index}] Description=${mb.description}? ANN_ID=${mb.aNN_ID} Language=${mb.language} Voice_Type=${mb.voice_Type} `)) {
|
||||||
fetchAPI(APIURL + "DeleteByIndex/" + mb.index, "DELETE", {}, null, (okdata) => {
|
fetchAPI(APIURL + "DeleteByIndex/" + mb.index, "DELETE", {}, null, (okdata) => {
|
||||||
reloadMessageBank(APIURL, () => {
|
reloadMessageBank(APIURL);
|
||||||
fill_messagebanktablebody(window.messagebankdata);
|
alert("Success delete messagebank : " + okdata.message);
|
||||||
alert("Success delete messagebank : " + okdata.message);
|
|
||||||
});
|
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error delete messagebank : " + errdata.message);
|
alert("Error delete messagebank : " + errdata.message);
|
||||||
});
|
});
|
||||||
@@ -307,7 +344,7 @@ $(document).ready(function () {
|
|||||||
let cells = window.selectedmessagerow.find('td');
|
let cells = window.selectedmessagerow.find('td');
|
||||||
/** @type {MessageBank} */
|
/** @type {MessageBank} */
|
||||||
let mb = {
|
let mb = {
|
||||||
index: cells.eq(0).text(),
|
index: Number(cells.eq(0).text()),
|
||||||
description: cells.eq(1).text(),
|
description: cells.eq(1).text(),
|
||||||
language: cells.eq(2).text(),
|
language: cells.eq(2).text(),
|
||||||
aNN_ID: parseInt(cells.eq(3).text()),
|
aNN_ID: parseInt(cells.eq(3).text()),
|
||||||
@@ -322,19 +359,8 @@ $(document).ready(function () {
|
|||||||
// Fill modal fields with selected messagebank data
|
// Fill modal fields with selected messagebank data
|
||||||
$messageindex.val(mb.index).prop('disabled', true);
|
$messageindex.val(mb.index).prop('disabled', true);
|
||||||
$messagedescription.val(mb.description);
|
$messagedescription.val(mb.description);
|
||||||
// Fill messagelanguage options and select current
|
|
||||||
$messagelanguage.empty();
|
|
||||||
languages.forEach(lang => {
|
|
||||||
$messagelanguage.append(new Option(lang, lang));
|
|
||||||
});
|
|
||||||
$messagelanguage.val(mb.language);
|
$messagelanguage.val(mb.language);
|
||||||
// Fill messagevoicetype options and select current
|
|
||||||
$messagevoicetype.empty();
|
|
||||||
voiceTypes.forEach(vt => {
|
|
||||||
$messagevoicetype.append(new Option(vt, vt));
|
|
||||||
});
|
|
||||||
$messagevoicetype.val(mb.voice_Type);
|
$messagevoicetype.val(mb.voice_Type);
|
||||||
// Set annid
|
|
||||||
$messageannid.val(mb.aNN_ID);
|
$messageannid.val(mb.aNN_ID);
|
||||||
// Refill message available variables
|
// Refill message available variables
|
||||||
refill_messageavailablevariables();
|
refill_messageavailablevariables();
|
||||||
@@ -401,10 +427,8 @@ $(document).ready(function () {
|
|||||||
Message_TAGS: messagetags
|
Message_TAGS: messagetags
|
||||||
};
|
};
|
||||||
fetchAPI(APIURL + "UpdateByIndex/" + mb.index, "PATCH", mbUpdate, null, (okdata) => {
|
fetchAPI(APIURL + "UpdateByIndex/" + mb.index, "PATCH", mbUpdate, null, (okdata) => {
|
||||||
reloadMessageBank(APIURL, () => {
|
reloadMessageBank(APIURL);
|
||||||
fill_messagebanktablebody(window.messagebankdata);
|
alert("Success edit messagebank : " + okdata.message);
|
||||||
alert("Success edit messagebank : " + okdata.message);
|
|
||||||
});
|
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error edit messagebank : " + errdata.message);
|
alert("Error edit messagebank : " + errdata.message);
|
||||||
});
|
});
|
||||||
@@ -423,11 +447,8 @@ $(document).ready(function () {
|
|||||||
});
|
});
|
||||||
$btnImport.click(() => {
|
$btnImport.click(() => {
|
||||||
DoImport(APIURL, (okdata) => {
|
DoImport(APIURL, (okdata) => {
|
||||||
reloadMessageBank(APIURL, () => {
|
reloadMessageBank(APIURL);
|
||||||
fill_messagebanktablebody(window.messagebankdata);
|
alert("Success import messagebank : " + okdata.message);
|
||||||
alert("Success import messagebank : " + okdata.message);
|
|
||||||
});
|
|
||||||
|
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error importing messagebank from XLSX : " + errdata.message);
|
alert("Error importing messagebank from XLSX : " + errdata.message);
|
||||||
});
|
});
|
||||||
|
|||||||
359
html/webpage/assets/js/overview.js
Normal file
359
html/webpage/assets/js/overview.js
Normal file
@@ -0,0 +1,359 @@
|
|||||||
|
/**
|
||||||
|
* @typedef {Object} StreamerOutputData
|
||||||
|
* @property {number} index - The index of the Barix connection.
|
||||||
|
* @property {string} channel - The channel name of the Barix connection.
|
||||||
|
* @property {string} ipaddress - The IP address of the Barix connection.
|
||||||
|
* @property {number} bufferRemain - The remaining buffer size of the Barix connection.
|
||||||
|
* @property {boolean} isPlaying - true = playback started, false = playback stopped
|
||||||
|
* @property {number} vu - The VU level of the Barix connection, 0 to 100.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} PagingQueue
|
||||||
|
* @property {number} index - The index of the paging queue item.
|
||||||
|
* @property {string} Date_Time - The date and time of the paging queue item.
|
||||||
|
* @property {string} Source - The source of the paging queue item.
|
||||||
|
* @property {string} Type - The type of the paging queue item.
|
||||||
|
* @property {string} Message - The message of the paging queue item.
|
||||||
|
* @property {string} BroadcastZones - The broadcast zones of the paging queue item.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} StreamerCard
|
||||||
|
* @property {JQuery<HTMLElement> | null} title - The jQuery result should be <h4> element.
|
||||||
|
* @property {JQuery<HTMLElement> | null} ip - The jQuery result should be <h6> element.
|
||||||
|
* @property {JQuery<HTMLElement> | null} buffer - The jQuery result should be <h6> element.
|
||||||
|
* @property {JQuery<HTMLElement> | null} status - The jQuery result should be <p> element.
|
||||||
|
* @property {JQuery<HTMLElement> | null} vu - The jQuery result should be <progress-bar> element.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function getCardByIndex(index) {
|
||||||
|
let obj = {
|
||||||
|
// title is <h4> element wiht id `streamertitle${index}`, with index as two digit number, e.g. 01, 02, 03
|
||||||
|
title: $(`#streamertitle${index.toString().padStart(2, '0')}`),
|
||||||
|
// ip is <h6> element with id `streamerip${index}`, with index as two digit number, e.g. 01, 02, 03
|
||||||
|
ip: $(`#streamerip${index.toString().padStart(2, '0')}`),
|
||||||
|
// buffer is <h6> element with id `streamerbuffer${index}`, with index as two digit number, e.g. 01, 02, 03
|
||||||
|
buffer: $(`#streamerbuffer${index.toString().padStart(2, '0')}`),
|
||||||
|
// status is <p> element with id `streamerstatus${index}`, with index as two digit number, e.g. 01, 02, 03
|
||||||
|
status: $(`#streamerstatus${index.toString().padStart(2, '0')}`),
|
||||||
|
// vu is <progress-bar> element with id `streamervu${index}`, with index as two digit number, e.g. 01, 02, 03
|
||||||
|
vu: $(`#streamervu${index.toString().padStart(2, '0')} .progress-bar`),
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the streamer card with the provided values.
|
||||||
|
* @param {StreamerOutputData[]} values
|
||||||
|
*/
|
||||||
|
function UpdateStreamerCard(values) {
|
||||||
|
if (!Array.isArray(values) || values.length === 0) return;
|
||||||
|
|
||||||
|
function setProgress(index, $bar, value, max = 100) {
|
||||||
|
const v = Number(value ?? 0);
|
||||||
|
const pct = Math.max(0, Math.min(100, Math.round((v / max) * 100)));
|
||||||
|
//if (index!==1) return; // only update index 1 for testing
|
||||||
|
//console.log(`setProgress: index=${index}, value=${v}, pct=${pct}`);
|
||||||
|
$bar
|
||||||
|
.attr('aria-valuenow', v) // semantic value
|
||||||
|
.css('width', pct + '%') // visual width
|
||||||
|
.text(pct); // optional label
|
||||||
|
}
|
||||||
|
for (let i = 1; i <= 64; i++) {
|
||||||
|
let vv = values.find(v => v.index === i);
|
||||||
|
let card = getCardByIndex(i);
|
||||||
|
if (vv) {
|
||||||
|
// there is value for this index
|
||||||
|
if (card.title) card.title.text(vv.channel ? vv.channel : `Channel ${i.toString().padStart(2, '0')}`);
|
||||||
|
if (card.ip) card.ip.text(`IP Address: ${vv.ipaddress ? vv.ipaddress : 'N/A'}`);
|
||||||
|
if (card.buffer) card.buffer.text(`Buffer: ${vv.bufferRemain !== undefined && vv.bufferRemain !== null ? vv.bufferRemain.toString() : 'N/A'}`);
|
||||||
|
if (card.status) card.status.text(`Status: ${vv.isPlaying ? 'Playing' : 'Stopped'}`);
|
||||||
|
if (card.vu) {
|
||||||
|
setProgress(i, card.vu, vv.vu, 100);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// no value for this index, disable the card
|
||||||
|
if (card.title) card.title.text(`Channel ${i.toString().padStart(2, '0')}`);
|
||||||
|
if (card.ip) card.ip.text(`IP Address: N/A`);
|
||||||
|
if (card.buffer) card.buffer.text(`Buffer: N/A`);
|
||||||
|
if (card.status) card.status.text(`Status: Disconnected`);
|
||||||
|
if (card.vu) {
|
||||||
|
setProgress(i, card.vu, 0, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {PagingQueue[]}
|
||||||
|
*/
|
||||||
|
window.PagingQueue = [];
|
||||||
|
/**
|
||||||
|
* @type {JQuery<HTMLElement> | null}
|
||||||
|
*/
|
||||||
|
window.selectedpagingrow = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} QueueTable
|
||||||
|
* @property {number} index - The index of the automatic queue item.
|
||||||
|
* @property {string} Date_Time - The date and time of the automatic queue item.
|
||||||
|
* @property {string} Source - The source of the automatic queue item.
|
||||||
|
* @property {string} Type - The type of the automatic queue item.
|
||||||
|
* @property {string} Message - The message of the automatic queue item.
|
||||||
|
* @property {string} SB_TAGS - The SB_TAGS of the automatic queue item.
|
||||||
|
* @property {string} BroadcastZones - The broadcast zones of the automatic queue item.
|
||||||
|
* @property {number} Repeat - The repeat count of the automatic queue item.
|
||||||
|
* @property {string} Language - The language of the automatic queue item.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {QueueTable[]}
|
||||||
|
*/
|
||||||
|
window.QueueTable = [];
|
||||||
|
/**
|
||||||
|
* @type {JQuery<HTMLElement> | null}
|
||||||
|
*/
|
||||||
|
window.selectedautomaticrow = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fills the paging queue table body with the provided data.
|
||||||
|
* @param {PagingQueue[]} vv array of PagingQueue objects
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function fill_pagingqueuetablebody(vv) {
|
||||||
|
$('#pagingqueuetable').empty();
|
||||||
|
if (!Array.isArray(vv) || vv.length === 0) return;
|
||||||
|
vv.forEach(item => {
|
||||||
|
// fill index and description columns using item properties
|
||||||
|
$('#pagingqueuetable').append(`<tr>
|
||||||
|
<td>${item.index}</td>
|
||||||
|
<td>${item.date_Time}</td>
|
||||||
|
<td>${item.source}</td>
|
||||||
|
<td>${item.type}</td>
|
||||||
|
<td>${item.message}</td>
|
||||||
|
<td>${item.broadcastZones}</td>
|
||||||
|
</tr>`);
|
||||||
|
let $addedrow = $('#pagingqueuetable tr:last');
|
||||||
|
$addedrow.off('click').on('click', function () {
|
||||||
|
if (window.selectedpagingrow) {
|
||||||
|
window.selectedpagingrow.find('td').css('background-color', '');
|
||||||
|
if (window.selectedpagingrow.is($(this))) {
|
||||||
|
window.selectedpagingrow = null;
|
||||||
|
$('#removepagingqueue').prop('disabled', true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.selectedpagingrow = $(this);
|
||||||
|
window.selectedpagingrow.find('td').css('background-color', 'lightblue');
|
||||||
|
$('#removepagingqueue').prop('disabled', false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fills the automatic queue table body with the provided data.
|
||||||
|
* @param {QueueTable[]} vv array of QueueTable objects
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function fill_automaticqueuetablebody(vv) {
|
||||||
|
$('#automaticqueuetable').empty();
|
||||||
|
if (!Array.isArray(vv) || vv.length === 0) return;
|
||||||
|
vv.forEach(item => {
|
||||||
|
// fill index and description columns using item properties
|
||||||
|
//console.log("fill_automaticqueuetablebody: item", item);
|
||||||
|
$('#automaticqueuetable').append(`<tr>
|
||||||
|
<td>${item.index}</td>
|
||||||
|
<td>${item.date_Time}</td>
|
||||||
|
<td>${item.source}</td>
|
||||||
|
<td>${item.type}</td>
|
||||||
|
<td>${item.message}</td>
|
||||||
|
<td>${item.broadcastZones}</td>
|
||||||
|
</tr>`);
|
||||||
|
let $addedrow = $('#automaticqueuetable tr:last');
|
||||||
|
$addedrow.off('click').on('click', function () {
|
||||||
|
if (window.selectedautomaticrow) {
|
||||||
|
window.selectedautomaticrow.find('td').css('background-color', '');
|
||||||
|
if (window.selectedautomaticrow.is($(this))) {
|
||||||
|
window.selectedautomaticrow = null;
|
||||||
|
$('#removeautomatictable').prop('disabled', true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.selectedautomaticrow = $(this);
|
||||||
|
window.selectedautomaticrow.find('td').css('background-color', 'lightblue');
|
||||||
|
$('#removeautomatictable').prop('disabled', false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function reloadPagingQueue(APIURL = "QueuePaging/") {
|
||||||
|
window.PagingQueue = [];
|
||||||
|
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
||||||
|
if (Array.isArray(okdata) && okdata.length > 0) {
|
||||||
|
window.PagingQueue.push(...okdata);
|
||||||
|
fill_pagingqueuetablebody(window.PagingQueue);
|
||||||
|
} else {
|
||||||
|
console.log("reloadPagingQueue: okdata is not array");
|
||||||
|
}
|
||||||
|
}, (errdata) => {
|
||||||
|
console.log("reloadPagingQueue: errdata", errdata);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function reloadAutomaticQueue(APIURL = "QueueTable/") {
|
||||||
|
window.QueueTable = [];
|
||||||
|
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
||||||
|
if (Array.isArray(okdata) && okdata.length > 0) {
|
||||||
|
window.QueueTable.push(...okdata);
|
||||||
|
fill_automaticqueuetablebody(window.QueueTable);
|
||||||
|
} else {
|
||||||
|
console.log("reloadAutomaticQueue: okdata is not array");
|
||||||
|
}
|
||||||
|
}, (errdata) => {
|
||||||
|
console.log("reloadAutomaticQueue: errdata", errdata);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function RemovePagingQueueByIndex(index, APIURL = "QueuePaging/") {
|
||||||
|
fetchAPI(APIURL + "DeleteByIndex/" + index, "DELETE", {}, null, (okdata) => {
|
||||||
|
console.log("RemovePagingQueueByIndex: okdata", okdata);
|
||||||
|
reloadPagingQueue(APIURL);
|
||||||
|
}, (errdata) => {
|
||||||
|
console.log("RemovePagingQueueByIndex: errdata", errdata);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function RemoveAutomaticQueueByIndex(index, APIURL = "QueueTable/") {
|
||||||
|
fetchAPI(APIURL + "DeleteByIndex/" + index, "DELETE", {}, null, (okdata) => {
|
||||||
|
console.log("RemoveAutomaticQueueByIndex: okdata", okdata);
|
||||||
|
reloadAutomaticQueue(APIURL);
|
||||||
|
}, (errdata) => {
|
||||||
|
console.log("RemoveAutomaticQueueByIndex: errdata", errdata);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
console.log("overview.js loaded");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$('#clearpagingqueue').off('click').on('click', function () {
|
||||||
|
DoClear("QueuePaging/", "Paging Queue", (okdata) => {
|
||||||
|
reloadPagingQueue();
|
||||||
|
alert("Success clear Paging Queue: " + okdata.message);
|
||||||
|
}, (errdata) => {
|
||||||
|
alert("Error clear Paging Queue: " + errdata.message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
$('#removepagingqueue').off('click').on('click', function () {
|
||||||
|
if (window.selectedpagingrow) {
|
||||||
|
let cells = window.selectedpagingrow.find('td');
|
||||||
|
let index = Number(cells.eq(0).text());
|
||||||
|
let description = cells.eq(1).text();
|
||||||
|
if (!isNaN(index) && description && description.length > 0) {
|
||||||
|
if (confirm(`Are you sure to remove Paging Queue Index: ${index} Description: ${description} ?`)) {
|
||||||
|
RemovePagingQueueByIndex(index);
|
||||||
|
window.selectedpagingrow = null;
|
||||||
|
$('#removepagingqueue').prop('disabled', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$('#clearautomatictable').off('click').on('click', function () {
|
||||||
|
DoClear("QueueTable/", "Automatic Queue", (okdata) => {
|
||||||
|
reloadAutomaticQueue();
|
||||||
|
alert("Success clear Automatic Queue: " + okdata.message);
|
||||||
|
}, (errdata) => {
|
||||||
|
alert("Error clear Automatic Queue: " + errdata.message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
$('#removeautomatictable').off('click').on('click', function () {
|
||||||
|
if (window.selectedautomaticrow) {
|
||||||
|
let cells = window.selectedautomaticrow.find('td');
|
||||||
|
let index = Number(cells.eq(0).text());
|
||||||
|
let description = cells.eq(1).text();
|
||||||
|
if (!isNaN(index) && description && description.length > 0) {
|
||||||
|
if (confirm(`Are you sure to remove Automatic Queue Index: ${index} Description: ${description} ?`)) {
|
||||||
|
RemoveAutomaticQueueByIndex(index);
|
||||||
|
window.selectedautomaticrow = null;
|
||||||
|
$('#removeautomatictable').prop('disabled', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let intervaljob1 = null;
|
||||||
|
let intervaljob2 = null;
|
||||||
|
function runIntervalJob() {
|
||||||
|
if (intervaljob1) clearInterval(intervaljob1);
|
||||||
|
intervaljob1 = setInterval(() => {
|
||||||
|
sendCommand("getStreamerOutputs", "");
|
||||||
|
}, 100);
|
||||||
|
if (intervaljob2) clearInterval(intervaljob2);
|
||||||
|
intervaljob2 = setInterval(() => {
|
||||||
|
sendCommand("getPagingQueue", "");
|
||||||
|
sendCommand("getAASQueue", "");
|
||||||
|
}, 2000);
|
||||||
|
console.log("overview.js interval job started");
|
||||||
|
}
|
||||||
|
|
||||||
|
runIntervalJob();
|
||||||
|
|
||||||
|
window.addEventListener('ws_connected', () => {
|
||||||
|
console.log("overview.js ws_connected event triggered");
|
||||||
|
runIntervalJob();
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('ws_disconnected', () => {
|
||||||
|
console.log("overview.js ws_disconnected event triggered");
|
||||||
|
if (intervaljob) clearInterval(intervaljob);
|
||||||
|
intervaljob = null;
|
||||||
|
});
|
||||||
|
window.addEventListener('ws_message', (event) => {
|
||||||
|
let rep = event.detail;
|
||||||
|
let cmd = rep.reply;
|
||||||
|
let data = rep.data;
|
||||||
|
if (cmd && cmd.length > 0) {
|
||||||
|
switch (cmd) {
|
||||||
|
case "getPagingQueue":
|
||||||
|
let pq = JSON.parse(data);
|
||||||
|
//console.log("getPagingQueue:", pq);
|
||||||
|
window.PagingQueue = [];
|
||||||
|
if (Array.isArray(pq) && pq.length > 0) {
|
||||||
|
window.PagingQueue.push(...pq);
|
||||||
|
}
|
||||||
|
fill_pagingqueuetablebody(window.PagingQueue);
|
||||||
|
break;
|
||||||
|
case "getAASQueue":
|
||||||
|
let aq = JSON.parse(data);
|
||||||
|
//console.log("getAASQueue:", aq);
|
||||||
|
window.QueueTable = [];
|
||||||
|
if (Array.isArray(aq) && aq.length > 0) {
|
||||||
|
window.QueueTable.push(...aq);
|
||||||
|
}
|
||||||
|
fill_automaticqueuetablebody(window.QueueTable);
|
||||||
|
break;
|
||||||
|
case "getStreamerOutputs":
|
||||||
|
/**
|
||||||
|
* @type {StreamerOutputData[]}
|
||||||
|
*/
|
||||||
|
let so = JSON.parse(data);
|
||||||
|
UpdateStreamerCard(so);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$(window).on('beforeunload', function () {
|
||||||
|
console.log("overview.js beforeunload event triggered");
|
||||||
|
clearInterval(intervaljob1);
|
||||||
|
clearInterval(intervaljob2);
|
||||||
|
intervaljob1 = null;
|
||||||
|
intervaljob2 = null;
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,4 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* @typedef {Object} ScheduleBank
|
||||||
|
* @property {number} index
|
||||||
|
* @property {string} description
|
||||||
|
* @property {string} day
|
||||||
|
* @property {string} time
|
||||||
|
* @property {string} soundpath
|
||||||
|
* @property {number} repeat
|
||||||
|
* @property {boolean} enable
|
||||||
|
* @property {string} broadcastZones
|
||||||
|
* @property {string} language
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** List of Schedulebank data loaded from server
|
||||||
|
* @type {ScheduleBank[]}
|
||||||
|
*/
|
||||||
|
window.schedulebankdata = [];
|
||||||
/**
|
/**
|
||||||
* Currently selected schedulebank row in the table
|
* Currently selected schedulebank row in the table
|
||||||
* @type {JQuery<HTMLElement>|null}
|
* @type {JQuery<HTMLElement>|null}
|
||||||
@@ -45,7 +61,22 @@ function fill_schedulebanktablebody(vv) {
|
|||||||
$('#tablesize').text("Table Size: " + vv.length);
|
$('#tablesize').text("Table Size: " + vv.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reload timer bank from server
|
||||||
|
* @param {string} APIURL API URL endpoint, default "ScheduleBank/"
|
||||||
|
*/
|
||||||
|
function reloadTimerBank(APIURL = "ScheduleBank/") {
|
||||||
|
window.schedulebankdata = [];
|
||||||
|
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
||||||
|
if (Array.isArray(okdata)) {
|
||||||
|
window.schedulebankdata.push(...okdata);
|
||||||
|
selectedschedulerow = null;
|
||||||
|
fill_schedulebanktablebody(window.schedulebankdata);
|
||||||
|
}
|
||||||
|
}, (errdata) => {
|
||||||
|
alert("Error loading schedulebank : " + errdata.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
console.log("schedulebank.js loaded successfully");
|
console.log("schedulebank.js loaded successfully");
|
||||||
@@ -70,28 +101,30 @@ $(document).ready(function () {
|
|||||||
let $schedulehour = $schedulemodal.find('#schedulehour');
|
let $schedulehour = $schedulemodal.find('#schedulehour');
|
||||||
// number input 0-59
|
// number input 0-59
|
||||||
let $scheduleminute = $schedulemodal.find('#scheduleminute');
|
let $scheduleminute = $schedulemodal.find('#scheduleminute');
|
||||||
// select for messagebank
|
// select2 for message
|
||||||
let $schedulesoundpath = $schedulemodal.find('#schedulesoundpath');
|
let $schedulemessage = $schedulemodal.find('#schedulemessage');
|
||||||
|
$schedulemessage.select2({});
|
||||||
// number input 0-5
|
// number input 0-5
|
||||||
let $schedulerepeat = $schedulemodal.find('#schedulerepeat');
|
let $schedulerepeat = $schedulemodal.find('#schedulerepeat');
|
||||||
// checkbox
|
// checkbox
|
||||||
let $scheduleenable = $schedulemodal.find('#scheduleenable');
|
let $scheduleenable = $schedulemodal.find('#scheduleenable');
|
||||||
// div for list of checkboxes
|
// select2 for broadcastzones
|
||||||
let $schedulezones = $schedulemodal.find('#schedulezones');
|
let $schedulezones = $schedulemodal.find('#schedulezones');
|
||||||
|
$schedulezones.select2({});
|
||||||
// radio button for everyday
|
// radio button for everyday
|
||||||
let $scheduleeveryday = $schedulemodal.find('#scheduleeveryday');
|
let $scheduleeveryday = $schedulemodal.find('#scheduleeveryday');
|
||||||
// radio button for weekdays
|
// radio button for weekly
|
||||||
let $schedulesunday = $schedulemodal.find('#schedulesunday');
|
let $scheduleweekly = $schedulemodal.find('#scheduleweekly');
|
||||||
let $schedulemonday = $schedulemodal.find('#schedulemonday');
|
// select2 for weekly selection
|
||||||
let $scheduletuesday = $schedulemodal.find('#scheduletuesday');
|
let $weeklyselect = $schedulemodal.find('#weeklyselect');
|
||||||
let $schedulewednesday = $schedulemodal.find('#schedulewednesday');
|
$weeklyselect.select2({});
|
||||||
let $schedulethursday = $schedulemodal.find('#schedulethursday');
|
|
||||||
let $schedulefriday = $schedulemodal.find('#schedulefriday');
|
|
||||||
let $schedulesaturday = $schedulemodal.find('#schedulesaturday');
|
|
||||||
// radio button for specific date
|
// radio button for specific date
|
||||||
let $schedulespecialdate = $schedulemodal.find('#schedulespecialdate');
|
let $schedulespecialdate = $schedulemodal.find('#schedulespecialdate');
|
||||||
// date input
|
// date input
|
||||||
let $scheduledate = $schedulemodal.find('#scheduledate');
|
let $scheduledate = $schedulemodal.find('#scheduledate');
|
||||||
|
// select2 for language
|
||||||
|
let $languageselect = $schedulemodal.find('#languageselect');
|
||||||
|
$languageselect.select2({});
|
||||||
|
|
||||||
$schedulespecialdate.on('change', function () {
|
$schedulespecialdate.on('change', function () {
|
||||||
if ($(this).is(':checked')) {
|
if ($(this).is(':checked')) {
|
||||||
@@ -106,26 +139,10 @@ $(document).ready(function () {
|
|||||||
$scheduledescription.val('');
|
$scheduledescription.val('');
|
||||||
$schedulehour.val('0');
|
$schedulehour.val('0');
|
||||||
$scheduleminute.val('0');
|
$scheduleminute.val('0');
|
||||||
|
$schedulerepeat.val('0');
|
||||||
$schedulesoundpath.empty();
|
|
||||||
if (Array.isArray(window.messagebankdata) && window.messagebankdata.length > 0) {
|
|
||||||
window.messagebankdata.forEach(item => {
|
|
||||||
let str = item.description+" ["+item.aNN_ID+"]";
|
|
||||||
let option = `<option value="${str}">${str}</option>`;
|
|
||||||
if ($schedulesoundpath.find(`option[value="${str}"]`).length === 0) $schedulesoundpath.append(option); // check if $schedulesoundpath already has this option
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
$schedulerepeat.val('1');
|
|
||||||
$scheduleenable.prop('checked', true);
|
$scheduleenable.prop('checked', true);
|
||||||
$scheduleeveryday.prop('checked', false);
|
$scheduleeveryday.prop('checked', false);
|
||||||
$schedulesunday.prop('checked', false);
|
|
||||||
$schedulemonday.prop('checked', false);
|
|
||||||
$scheduletuesday.prop('checked', false);
|
|
||||||
$schedulewednesday.prop('checked', false);
|
|
||||||
$schedulethursday.prop('checked', false);
|
|
||||||
$schedulefriday.prop('checked', false);
|
|
||||||
$schedulesaturday.prop('checked', false);
|
|
||||||
$schedulespecialdate.prop('checked', false);
|
$schedulespecialdate.prop('checked', false);
|
||||||
$scheduledate.prop('disabled', true).val('');
|
$scheduledate.prop('disabled', true).val('');
|
||||||
|
|
||||||
@@ -149,15 +166,11 @@ $(document).ready(function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
reloadTimerBank(APIURL, () => {
|
reloadTimerBank(APIURL);
|
||||||
fill_schedulebanktablebody(window.schedulebankdata);
|
|
||||||
});
|
|
||||||
$btnClear.click(() => {
|
$btnClear.click(() => {
|
||||||
DoClear(APIURL, "Timerbank", (okdata) => {
|
DoClear(APIURL, "Timerbank", (okdata) => {
|
||||||
reloadTimerBank(APIURL,() => {
|
reloadTimerBank(APIURL);
|
||||||
fill_schedulebanktablebody(window.schedulebankdata);
|
alert("Success clear schedulebank : " + okdata.message);
|
||||||
alert("Success clear schedulebank : " + okdata.message);
|
|
||||||
});
|
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error clear schedulebank : " + errdata.message);
|
alert("Error clear schedulebank : " + errdata.message);
|
||||||
});
|
});
|
||||||
@@ -218,10 +231,8 @@ $(document).ready(function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fetchAPI(APIURL + "Add", "POST", {}, scheduleObj, (okdata) => {
|
fetchAPI(APIURL + "Add", "POST", {}, scheduleObj, (okdata) => {
|
||||||
reloadTimerBank(APIURL, () => {
|
alert("Success add schedule: " + okdata.message);
|
||||||
fill_schedulebanktablebody(window.schedulebankdata);
|
reloadTimerBank(APIURL);
|
||||||
alert("Success add schedule: " + okdata.message);
|
|
||||||
});
|
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error add schedule: " + errdata.message);
|
alert("Error add schedule: " + errdata.message);
|
||||||
});
|
});
|
||||||
@@ -234,7 +245,7 @@ $(document).ready(function () {
|
|||||||
let cells = window.selectedschedulerow.find('td');
|
let cells = window.selectedschedulerow.find('td');
|
||||||
/** @type {ScheduleBank} */
|
/** @type {ScheduleBank} */
|
||||||
let sr = {
|
let sr = {
|
||||||
index: cells.eq(0).text(),
|
index: Number(cells.eq(0).text()),
|
||||||
description: cells.eq(1).text(),
|
description: cells.eq(1).text(),
|
||||||
day: cells.eq(2).text(),
|
day: cells.eq(2).text(),
|
||||||
time: cells.eq(3).text(),
|
time: cells.eq(3).text(),
|
||||||
@@ -246,10 +257,8 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
if (confirm(`Are you sure to delete schedule [${sr.index}] Description=${sr.description}?`)) {
|
if (confirm(`Are you sure to delete schedule [${sr.index}] Description=${sr.description}?`)) {
|
||||||
fetchAPI(APIURL + "DeleteByIndex/" + sr.index, "DELETE", {}, null, (okdata) => {
|
fetchAPI(APIURL + "DeleteByIndex/" + sr.index, "DELETE", {}, null, (okdata) => {
|
||||||
reloadTimerBank(APIURL, () => {
|
reloadTimerBank(APIURL);
|
||||||
fill_schedulebanktablebody(window.schedulebankdata);
|
alert("Success delete schedule : " + okdata.message);
|
||||||
alert("Success delete schedule : " + okdata.message);
|
|
||||||
});
|
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error delete schedule : " + errdata.message);
|
alert("Error delete schedule : " + errdata.message);
|
||||||
});
|
});
|
||||||
@@ -261,7 +270,7 @@ $(document).ready(function () {
|
|||||||
let cells = window.selectedschedulerow.find('td');
|
let cells = window.selectedschedulerow.find('td');
|
||||||
/** @type {ScheduleBank} */
|
/** @type {ScheduleBank} */
|
||||||
let sr = {
|
let sr = {
|
||||||
index: cells.eq(0).text(),
|
index: Number(cells.eq(0).text()),
|
||||||
description: cells.eq(1).text(),
|
description: cells.eq(1).text(),
|
||||||
day: cells.eq(2).text(),
|
day: cells.eq(2).text(),
|
||||||
time: cells.eq(3).text(),
|
time: cells.eq(3).text(),
|
||||||
@@ -366,10 +375,8 @@ $(document).ready(function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fetchAPI(APIURL + "UpdateByIndex/" + sr.index, "PATCH", {}, scheduleObj, (okdata) => {
|
fetchAPI(APIURL + "UpdateByIndex/" + sr.index, "PATCH", {}, scheduleObj, (okdata) => {
|
||||||
reloadTimerBank(APIURL, () => {
|
alert("Success edit schedule: " + okdata.message);
|
||||||
fill_schedulebanktablebody(window.schedulebankdata);
|
reloadTimerBank(APIURL);
|
||||||
alert("Success edit schedule: " + okdata.message);
|
|
||||||
});
|
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error edit schedule: " + errdata.message);
|
alert("Error edit schedule: " + errdata.message);
|
||||||
});
|
});
|
||||||
@@ -384,10 +391,8 @@ $(document).ready(function () {
|
|||||||
});
|
});
|
||||||
$btnImport.click(() => {
|
$btnImport.click(() => {
|
||||||
DoImport(APIURL, (okdata) => {
|
DoImport(APIURL, (okdata) => {
|
||||||
reloadTimerBank(APIURL, () => {
|
reloadTimerBank(APIURL);
|
||||||
fill_schedulebanktablebody(window.schedulebankdata);
|
alert("Success import schedulebank from XLSX : " + okdata.message);
|
||||||
alert("Success import schedulebank from XLSX : " + okdata.message);
|
|
||||||
});
|
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error importing schedulebank from XLSX : " + errdata.message);
|
alert("Error importing schedulebank from XLSX : " + errdata.message);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,263 +20,6 @@ window.languages = [];
|
|||||||
*/
|
*/
|
||||||
window.scheduledays = []
|
window.scheduledays = []
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {Object} MessageBank
|
|
||||||
* @property {number} index
|
|
||||||
* @property {string} description
|
|
||||||
* @property {string} language
|
|
||||||
* @property {number} aNN_ID
|
|
||||||
* @property {string} voice_Type
|
|
||||||
* @property {string} message_Detail
|
|
||||||
* @property {string} message_TAGS
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of Messagebank data loaded from server
|
|
||||||
* taruh di sini karena dipakai di banyak tempat
|
|
||||||
* @type {MessageBank[]}
|
|
||||||
*/
|
|
||||||
window.messagebankdata ??= [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reload message bank from server
|
|
||||||
* @param {string} APIURL API URL endpoint, default "MessageBank/"
|
|
||||||
* @param {Function|null} callback Optional callback function to execute after loading
|
|
||||||
*/
|
|
||||||
function reloadMessageBank(APIURL = "MessageBank/", callback=null) {
|
|
||||||
window.messagebankdata ??= [];
|
|
||||||
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
|
||||||
if (Array.isArray(okdata)) {
|
|
||||||
window.messagebankdata.push(...okdata);
|
|
||||||
console.log("Loaded " + window.messagebankdata.length + " message bank items");
|
|
||||||
window.selectedmessagerow = null;
|
|
||||||
if (callback && typeof callback === 'function') callback();
|
|
||||||
}
|
|
||||||
}, (errdata) => {
|
|
||||||
alert("Error loading messagebank : " + errdata.message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {Object} ScheduleBank
|
|
||||||
* @property {number} index
|
|
||||||
* @property {string} description
|
|
||||||
* @property {string} day
|
|
||||||
* @property {string} time
|
|
||||||
* @property {string} soundpath
|
|
||||||
* @property {number} repeat
|
|
||||||
* @property {boolean} enable
|
|
||||||
* @property {string} broadcastZones
|
|
||||||
* @property {string} language
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** List of Schedulebank data loaded from server
|
|
||||||
* @type {ScheduleBank[]}
|
|
||||||
*/
|
|
||||||
window.schedulebankdata = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reload timer bank from server
|
|
||||||
* taruh di sini karena dipakai di banyak tempat
|
|
||||||
* @param {string} APIURL API URL endpoint, default "ScheduleBank/"
|
|
||||||
* @param {Function|null} callback Optional callback function to execute after loading
|
|
||||||
*/
|
|
||||||
function reloadTimerBank(APIURL = "ScheduleBank/", callback=null) {
|
|
||||||
window.schedulebankdata = [];
|
|
||||||
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
|
||||||
if (Array.isArray(okdata)) {
|
|
||||||
window.schedulebankdata.push(...okdata);
|
|
||||||
selectedschedulerow = null;
|
|
||||||
console.log("Loaded " + window.schedulebankdata.length + " schedule bank items");
|
|
||||||
if (callback && typeof callback === 'function') callback();
|
|
||||||
}
|
|
||||||
}, (errdata) => {
|
|
||||||
alert("Error loading schedulebank : " + errdata.message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {Object} BroadcastZone
|
|
||||||
* @property {number} index
|
|
||||||
* @property {string} description
|
|
||||||
* @property {String} SoundChannel
|
|
||||||
* @property {String} Box
|
|
||||||
* @property {String} Relay
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of broadcast zones available
|
|
||||||
* @type {BroadcastZone[]}
|
|
||||||
*/
|
|
||||||
window.BroadcastZoneList ??= [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reload broadcast zones from server
|
|
||||||
* taruh di sini karena dipakai di banyak tempat
|
|
||||||
* @param {String} APIURL API URL endpoint (default "BroadcastZones/")
|
|
||||||
* @param {Function|null} callback Optional callback function to execute after loading
|
|
||||||
*/
|
|
||||||
function reloadBroadcastZones(APIURL = "BroadcastZones/", callback=null) {
|
|
||||||
window.BroadcastZoneList = [];
|
|
||||||
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
|
||||||
if (Array.isArray(okdata)) {
|
|
||||||
//console.log("reloadBroadcastZones : ", okdata)
|
|
||||||
window.BroadcastZoneList.push(...okdata);
|
|
||||||
console.log("Loaded " + window.BroadcastZoneList.length + " broadcast zone items");
|
|
||||||
window.selectedbroadcastzonerow = null;
|
|
||||||
if (callback && typeof callback === 'function') callback();
|
|
||||||
} else console.log("reloadBroadcastZones: okdata is not array");
|
|
||||||
}, (errdata) => {
|
|
||||||
alert("Error loading broadcast zones : " + errdata.message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {Object} SoundBank
|
|
||||||
* @property {number} index
|
|
||||||
* @property {string} description
|
|
||||||
* @property {string} tag
|
|
||||||
* @property {string} category
|
|
||||||
* @property {string} language
|
|
||||||
* @property {string} voiceType
|
|
||||||
* @property {string} path
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {Object} Select2item
|
|
||||||
* @property {number} id
|
|
||||||
* @property {string} text
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of Soundbank data loaded from server
|
|
||||||
* taruh di sini karena dipakai di banyak tempat
|
|
||||||
* @type {SoundBank[]}
|
|
||||||
*/
|
|
||||||
window.soundbankdata = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of sound files in the soundbank directory, that ends with .wav or .mp3
|
|
||||||
* taruh di sini karena dipakai di banyak tempat
|
|
||||||
* @type {string[]}
|
|
||||||
*/
|
|
||||||
window.soundbankfiles = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Select2 data source
|
|
||||||
* See https://select2.org/data-sources/formats
|
|
||||||
* @type {Select2item[]}
|
|
||||||
*/
|
|
||||||
window.select2data = [];
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reload sound bank from server
|
|
||||||
* @param {String} APIURL API URL endpoint, default "SoundBank/"
|
|
||||||
* @param {Function|null} callback Optional callback function to execute after loading
|
|
||||||
*/
|
|
||||||
function reloadSoundBank(APIURL = "SoundBank/", callback=null) {
|
|
||||||
window.soundbankdata = [];
|
|
||||||
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
|
||||||
|
|
||||||
if (Array.isArray(okdata)) {
|
|
||||||
window.soundbankdata.push(...okdata);
|
|
||||||
window.selectedsoundrow = null;
|
|
||||||
console.log("Loaded " + window.soundbankdata.length + " sound bank items");
|
|
||||||
if (callback && typeof callback === 'function') callback();
|
|
||||||
}
|
|
||||||
}, (errdata) => {
|
|
||||||
alert("Error loading soundbank : " + errdata.message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reload soundbank files from server
|
|
||||||
* @param {String} APIURL API URL endpoint (default "SoundBank/")
|
|
||||||
* @param {Function|null} callback Optional callback function to execute after loading
|
|
||||||
*/
|
|
||||||
function reloadSoundbankFiles(APIURL = "SoundBank/",callback=null) {
|
|
||||||
window.soundbankfiles = [];
|
|
||||||
fetchAPI(APIURL + "ListFiles", "GET", {}, null, (okdata) => {
|
|
||||||
// okdata is a string contains elements separated by semicolon ;
|
|
||||||
if (Array.isArray(okdata)) {
|
|
||||||
window.soundbankfiles = okdata.filter(item => item.trim().length > 0);
|
|
||||||
// refill select2data
|
|
||||||
window.select2data = window.soundbankfiles.map((item, index) => ({ id: index + 1, text: item }));
|
|
||||||
console.log("Loaded " + window.soundbankfiles.length + " sound bank files");
|
|
||||||
if (callback && typeof callback === 'function') callback();
|
|
||||||
} else console.log("reloadSoundbankFiles: okdata is not array");
|
|
||||||
}, (errdata) => {
|
|
||||||
alert("Error loading soundbank files : " + errdata.message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {Object} SoundChannel
|
|
||||||
* @property {number} index - The index of the sound channel.
|
|
||||||
* @property {string} channel - The name of the sound channel.
|
|
||||||
* @property {string} ip - The IP address associated with the sound channel.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {SoundChannel[]}
|
|
||||||
*/
|
|
||||||
window.soundChannels = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reload sound channels from server
|
|
||||||
* @param {String} APIURL API URL endpoint (default "SoundChannel/")
|
|
||||||
* @param {Function|null} callback Optional callback function to execute after loading
|
|
||||||
*/
|
|
||||||
function reloadSoundChannel(APIURL = "SoundChannel/", callback=null) {
|
|
||||||
window.soundChannels = [];
|
|
||||||
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
|
||||||
if (Array.isArray(okdata)) {
|
|
||||||
//console.log("reloadSoundChannel : ", okdata)
|
|
||||||
window.soundChannels.push(...okdata);
|
|
||||||
window.selectedsoundchannelrow = null;
|
|
||||||
console.log("Loaded " + window.soundChannels.length + " sound channel items");
|
|
||||||
if (callback && typeof callback === 'function') callback();
|
|
||||||
} else console.log("reloadSoundChannel: okdata is not array");
|
|
||||||
}, (errdata) => {
|
|
||||||
alert("Error loading sound channels : " + errdata.message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {Object} LanguageBank
|
|
||||||
* @property {number} index
|
|
||||||
* @property {string} tag
|
|
||||||
* @property {string} language
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** List of Languagebank data loaded from server
|
|
||||||
* @type {LanguageBank[]}
|
|
||||||
*/
|
|
||||||
window.languagebankdata = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reload language bank from server
|
|
||||||
* @param {string} APIURL API URL endpoint, default "LanguageLink/"
|
|
||||||
* @param {Function|null} callback Optional callback function to execute after loading
|
|
||||||
*/
|
|
||||||
function reloadLanguageBank(APIURL = "LanguageLink/", callback=null) {
|
|
||||||
window.languagebankdata = [];
|
|
||||||
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
|
||||||
if (Array.isArray(okdata)) {
|
|
||||||
window.languagebankdata.push(...okdata);
|
|
||||||
window.selectedlanguagerow = null;
|
|
||||||
console.log("Loaded " + window.languagebankdata.length + " language bank items");
|
|
||||||
if (callback && typeof callback === 'function') callback();
|
|
||||||
}
|
|
||||||
}, (errdata) => {
|
|
||||||
alert("Error loading languagebank : " + errdata.message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a list item element
|
* Create a list item element
|
||||||
* @param {String} text Text Content for the list item
|
* @param {String} text Text Content for the list item
|
||||||
@@ -326,10 +69,10 @@ function fetchAPI(endpoint, method, headers = {}, body = null, cbOK, cbError) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fetch(url, options)
|
fetch(url, options)
|
||||||
.then(async(response) => {
|
.then(async (response) => {
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
let msg ;
|
let msg;
|
||||||
try{
|
try {
|
||||||
let _xxx = await response.json();
|
let _xxx = await response.json();
|
||||||
msg = _xxx.message || response.statusText;
|
msg = _xxx.message || response.statusText;
|
||||||
} catch {
|
} catch {
|
||||||
@@ -538,10 +281,10 @@ window.redcircle = null;
|
|||||||
*/
|
*/
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
document.title = "Automatic Announcement System"
|
document.title = "Automatic Announcement System"
|
||||||
if (window.greencircle === null){
|
if (window.greencircle === null) {
|
||||||
fetchImg('green_circle.png', (url) => { window.greencircle = url; }, (err) => { console.error("Error loading green_circle.png : ", err); });
|
fetchImg('green_circle.png', (url) => { window.greencircle = url; }, (err) => { console.error("Error loading green_circle.png : ", err); });
|
||||||
}
|
}
|
||||||
if (window.redcircle === null){
|
if (window.redcircle === null) {
|
||||||
fetchImg('red_circle.png', (url) => { window.redcircle = url; }, (err) => { console.error("Error loading red_circle.png : ", err); });
|
fetchImg('red_circle.png', (url) => { window.redcircle = url; }, (err) => { console.error("Error loading red_circle.png : ", err); });
|
||||||
}
|
}
|
||||||
const wsURL = window.location.pathname + '/ws'
|
const wsURL = window.location.pathname + '/ws'
|
||||||
@@ -549,8 +292,8 @@ $(document).ready(function () {
|
|||||||
alert("Runtime error: " + chrome.runtime.lastError.message);
|
alert("Runtime error: " + chrome.runtime.lastError.message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// reset status indicators
|
// reset status indicators
|
||||||
function resetStatusIndicators() {
|
function resetStatusIndicators() {
|
||||||
@@ -568,60 +311,83 @@ $(document).ready(function () {
|
|||||||
getCategories();
|
getCategories();
|
||||||
getLanguages();
|
getLanguages();
|
||||||
getScheduledDays();
|
getScheduledDays();
|
||||||
reloadMessageBank();
|
|
||||||
reloadTimerBank();
|
|
||||||
reloadBroadcastZones();
|
|
||||||
reloadSoundBank();
|
|
||||||
reloadSoundbankFiles();
|
|
||||||
reloadSoundChannel();
|
|
||||||
reloadLanguageBank();
|
|
||||||
|
|
||||||
// Initialize WebSocket connection
|
|
||||||
window.ws = new WebSocket(wsURL);
|
|
||||||
|
|
||||||
window.ws.onopen = () => {
|
// reconnect handle
|
||||||
console.log('WebSocket connection established');
|
let ws_reconnect;
|
||||||
$('#onlineindicator').attr('src', window.greencircle);
|
|
||||||
};
|
function reconnect() {
|
||||||
window.ws.onmessage = (event) => {
|
if (window.ws && window.ws.readyState === WebSocket.OPEN) return;
|
||||||
if ($('#onlineindicator').attr('src') !== window.greencircle) {
|
const s = new WebSocket(wsURL);
|
||||||
|
s.addEventListener('open', () => {
|
||||||
|
console.log('WebSocket connection established');
|
||||||
$('#onlineindicator').attr('src', window.greencircle);
|
$('#onlineindicator').attr('src', window.greencircle);
|
||||||
}
|
|
||||||
let rep = JSON.parse(event.data);
|
|
||||||
let cmd = rep.reply
|
|
||||||
let data = rep.data;
|
|
||||||
if (cmd && cmd.length > 0) {
|
|
||||||
switch (cmd) {
|
|
||||||
case "getCPUStatus":
|
|
||||||
$('#cpustatus').text("CPU : " + data)
|
|
||||||
break;
|
|
||||||
case "getMemoryStatus":
|
|
||||||
$('#ramstatus').text("RAM : " + data)
|
|
||||||
break;
|
|
||||||
case "getDiskStatus":
|
|
||||||
$('#diskstatus').text("Disk : " + data)
|
|
||||||
break;
|
|
||||||
case "getNetworkStatus":
|
|
||||||
$('#networkstatus').text("Network : " + data)
|
|
||||||
break;
|
|
||||||
case "getSystemTime":
|
|
||||||
$('#datetimetext').text(data)
|
|
||||||
break;
|
|
||||||
|
|
||||||
|
if (ws_reconnect) {
|
||||||
|
// stop reconnect attempts
|
||||||
|
clearTimeout(ws_reconnect);
|
||||||
|
ws_reconnect = null;
|
||||||
}
|
}
|
||||||
|
window.dispatchEvent(new Event('ws_connected'));
|
||||||
|
});
|
||||||
|
s.addEventListener('close', () => {
|
||||||
|
console.log('WebSocket connection closed');
|
||||||
|
window.dispatchEvent(new Event('ws_disconnected'));
|
||||||
|
resetStatusIndicators();
|
||||||
|
if (!ws_reconnect) {
|
||||||
|
clearTimeout(ws_reconnect);
|
||||||
|
ws_reconnect = null;
|
||||||
|
}
|
||||||
|
ws_reconnect = setTimeout(reconnect, 5000); // try to reconnect every 5 seconds
|
||||||
|
});
|
||||||
|
s.addEventListener('message', (event) => {
|
||||||
|
if ($('#onlineindicator').attr('src') !== window.greencircle) {
|
||||||
|
$('#onlineindicator').attr('src', window.greencircle);
|
||||||
|
}
|
||||||
|
let rep = JSON.parse(event.data);
|
||||||
|
window.dispatchEvent(new CustomEvent('ws_message', { detail: rep }));
|
||||||
|
let cmd = rep.reply
|
||||||
|
let data = rep.data;
|
||||||
|
if (cmd && cmd.length > 0) {
|
||||||
|
switch (cmd) {
|
||||||
|
case "getCPUStatus":
|
||||||
|
$('#cpustatus').text("CPU : " + data)
|
||||||
|
break;
|
||||||
|
case "getMemoryStatus":
|
||||||
|
$('#ramstatus').text("RAM : " + data)
|
||||||
|
break;
|
||||||
|
case "getDiskStatus":
|
||||||
|
$('#diskstatus').text("Disk : " + data)
|
||||||
|
break;
|
||||||
|
case "getNetworkStatus":
|
||||||
|
let result = "";
|
||||||
|
let json = JSON.parse(data);
|
||||||
|
if (Array.isArray(json) && json.length > 0) {
|
||||||
|
json.forEach((net) => {
|
||||||
|
if (result.length > 0) result += "\n"
|
||||||
|
result += `${net.displayName} (${net.ipV4addr.join(";")}) TX:${(net.txSpeed / 1024).toFixed(1)} KB/s RX:${(net.rxSpeed / 1024).toFixed(1)} KB/s`
|
||||||
|
})
|
||||||
|
} else result = "N/A";
|
||||||
|
$('#networkstatus').text(result)
|
||||||
|
break;
|
||||||
|
case "getSystemTime":
|
||||||
|
$('#datetimetext').text(data)
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
window.ws = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
reconnect();
|
||||||
|
window.addEventListener('beforeunload', () => {
|
||||||
|
try{
|
||||||
|
window.ws?.close(1000, "Client closed connection");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error closing WebSocket connection:", error);
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
window.ws.onclose = () => {
|
|
||||||
console.log('WebSocket connection closed');
|
|
||||||
resetStatusIndicators();
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
// window.ws.onerror = (error) => {
|
|
||||||
// console.error('WebSocket error:', error);
|
|
||||||
// };
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
sendCommand("getCPUStatus", "")
|
sendCommand("getCPUStatus", "")
|
||||||
@@ -648,7 +414,7 @@ $(document).ready(function () {
|
|||||||
if (status === "success") {
|
if (status === "success") {
|
||||||
console.log("Soundbank content loaded successfully");
|
console.log("Soundbank content loaded successfully");
|
||||||
// pindah soundbank.js
|
// pindah soundbank.js
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
console.error("Error loading soundbank content : ", xhr.status, xhr.statusText);
|
console.error("Error loading soundbank content : ", xhr.status, xhr.statusText);
|
||||||
}
|
}
|
||||||
@@ -661,7 +427,7 @@ $(document).ready(function () {
|
|||||||
if (status === "success") {
|
if (status === "success") {
|
||||||
console.log("Messagebank content loaded successfully");
|
console.log("Messagebank content loaded successfully");
|
||||||
// pindah messagebank.js
|
// pindah messagebank.js
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
console.error("Error loading messagebank content : ", xhr.status, xhr.statusText);
|
console.error("Error loading messagebank content : ", xhr.status, xhr.statusText);
|
||||||
}
|
}
|
||||||
@@ -674,7 +440,7 @@ $(document).ready(function () {
|
|||||||
if (status === "success") {
|
if (status === "success") {
|
||||||
console.log("Language content loaded successfully");
|
console.log("Language content loaded successfully");
|
||||||
// pindah languagelink.js
|
// pindah languagelink.js
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
console.error("Error loading language content : ", xhr.status, xhr.statusText);
|
console.error("Error loading language content : ", xhr.status, xhr.statusText);
|
||||||
}
|
}
|
||||||
@@ -698,7 +464,7 @@ $(document).ready(function () {
|
|||||||
if (status === "success") {
|
if (status === "success") {
|
||||||
console.log("Timer content loaded successfully");
|
console.log("Timer content loaded successfully");
|
||||||
// pindah ke schedulebank.js
|
// pindah ke schedulebank.js
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
console.error("Error loading timer content : ", xhr.status, xhr.statusText);
|
console.error("Error loading timer content : ", xhr.status, xhr.statusText);
|
||||||
}
|
}
|
||||||
@@ -722,7 +488,7 @@ $(document).ready(function () {
|
|||||||
if (status === "success") {
|
if (status === "success") {
|
||||||
console.log("User Management content loaded successfully");
|
console.log("User Management content loaded successfully");
|
||||||
// pindah ke usermanagement.js
|
// pindah ke usermanagement.js
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
console.error("Error loading user management content:", xhr.status, xhr.statusText);
|
console.error("Error loading user management content:", xhr.status, xhr.statusText);
|
||||||
}
|
}
|
||||||
|
|||||||
2
html/webpage/assets/js/select2.min.js
vendored
Normal file
2
html/webpage/assets/js/select2.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -1,4 +1,27 @@
|
|||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} Select2item
|
||||||
|
* @property {number} id
|
||||||
|
* @property {string} text
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} SoundBank
|
||||||
|
* @property {number} index
|
||||||
|
* @property {string} description
|
||||||
|
* @property {string} tag
|
||||||
|
* @property {string} category
|
||||||
|
* @property {string} language
|
||||||
|
* @property {string} voiceType
|
||||||
|
* @property {string} path
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of Soundbank data loaded from server
|
||||||
|
* @type {SoundBank[]}
|
||||||
|
*/
|
||||||
|
window.soundbankdata = [];
|
||||||
/**
|
/**
|
||||||
* Currently selected soundbank row in the table
|
* Currently selected soundbank row in the table
|
||||||
* @type {JQuery<HTMLElement>|null}
|
* @type {JQuery<HTMLElement>|null}
|
||||||
@@ -7,13 +30,37 @@ window.selectedsoundrow = null;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select2 data source
|
||||||
|
* See https://select2.org/data-sources/formats
|
||||||
|
* @type {Select2item[]}
|
||||||
|
*/
|
||||||
|
window.select2data = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reload sound bank from server
|
||||||
|
* @param {String} APIURL API URL endpoint, default "SoundBank/"
|
||||||
|
*/
|
||||||
|
function reloadSoundBank(APIURL = "SoundBank/") {
|
||||||
|
window.soundbankdata = [];
|
||||||
|
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
||||||
|
|
||||||
|
if (Array.isArray(okdata)) {
|
||||||
|
window.soundbankdata.push(...okdata);
|
||||||
|
window.selectedsoundrow = null;
|
||||||
|
fill_soundbanktablebody(window.soundbankdata);
|
||||||
|
}
|
||||||
|
}, (errdata) => {
|
||||||
|
alert("Error loading soundbank : " + errdata.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fill soundbank table body with values
|
* Fill soundbank table body with values
|
||||||
* @param {SoundBank[]} vv values to fill
|
* @param {SoundBank[]} vv values to fill
|
||||||
*/
|
*/
|
||||||
function fill_soundbanktablebody(vv) {
|
function fill_soundbanktablebody(vv) {
|
||||||
$('#soundbanktablebody').empty();
|
$('#soundbanktablebody').empty();
|
||||||
console.log("Filling soundbank table with " + vv.length + " items");
|
|
||||||
if (!Array.isArray(vv) || vv.length === 0) return;
|
if (!Array.isArray(vv) || vv.length === 0) return;
|
||||||
vv.forEach(item => {
|
vv.forEach(item => {
|
||||||
const row = `<tr>
|
const row = `<tr>
|
||||||
@@ -48,12 +95,60 @@ function fill_soundbanktablebody(vv) {
|
|||||||
$('#tablesize').text("Table Size: " + vv.length);
|
$('#tablesize').text("Table Size: " + vv.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reload soundbank files from server and filter by language, category, and voiceType
|
||||||
|
* @param {String} language
|
||||||
|
* @param {String} category
|
||||||
|
* @param {String} voiceType
|
||||||
|
* @param {Function} cb callback function when done
|
||||||
|
*/
|
||||||
|
function reloadSoundbankFiles(language, category, voiceType, cb=null) {
|
||||||
|
window.select2data = [];
|
||||||
|
$('#modalpath').empty().trigger('change');
|
||||||
|
if (language && language.length > 0) {
|
||||||
|
if (category && category.length > 0) {
|
||||||
|
if (voiceType && voiceType.length > 0) {
|
||||||
|
fetchAPI(`ListFiles/${language}/${voiceType}/${category}`, "GET", {}, null, (okdata) => {
|
||||||
|
console.log("reloadSoundbankFiles: got " + okdata.length + " items");
|
||||||
|
if (Array.isArray(okdata)){
|
||||||
|
window.select2data = okdata.map(p => ({id: getFilenameFromPath(p), text: getFilenameFromPath(p)}));
|
||||||
|
|
||||||
|
$('#modalpath').select2({
|
||||||
|
data: window.select2data,
|
||||||
|
placeholder: 'Select a sound file',
|
||||||
|
allowClear: true,
|
||||||
|
width: '100%',
|
||||||
|
dropdownParent: $('#soundbankmodal')
|
||||||
|
});
|
||||||
|
if (cb) cb();
|
||||||
|
}
|
||||||
|
}, (errdata) => {
|
||||||
|
alert("Error loading soundbank files : " + errdata.message);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFilenameFromPath(path) {
|
||||||
|
if (!path || path.length === 0) return "";
|
||||||
|
if (path.includes('\\')) {
|
||||||
|
let parts = path.split('\\');
|
||||||
|
return parts[parts.length - 1];
|
||||||
|
} else if (!path.includes('/')) {
|
||||||
|
let parts = path.split('/');
|
||||||
|
return parts[parts.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
console.log("soundbank.js loaded successfully");
|
console.log("soundbank.js loaded successfully");
|
||||||
reloadSoundbankFiles();
|
|
||||||
$('#soundbanktablebody').empty();
|
$('#soundbanktablebody').empty();
|
||||||
window.selectedsoundrow = null;
|
window.selectedsoundrow = null;
|
||||||
let $btnClear = $('#btnClear');
|
let $btnClear = $('#btnClear');
|
||||||
@@ -72,7 +167,9 @@ $(document).ready(function () {
|
|||||||
let $modalcategory = $modal.find('#modalcategory');
|
let $modalcategory = $modal.find('#modalcategory');
|
||||||
let $modallanguage = $modal.find('#modallanguage');
|
let $modallanguage = $modal.find('#modallanguage');
|
||||||
let $modalvoicetype = $modal.find('#modalvoicetype');
|
let $modalvoicetype = $modal.find('#modalvoicetype');
|
||||||
let $modalpath = $modal.find('#modalpath');
|
let selected_category = null;
|
||||||
|
let selected_language = null;
|
||||||
|
let selected_voicetype = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear soundbank modal inputs
|
* Clear soundbank modal inputs
|
||||||
@@ -99,18 +196,11 @@ $(document).ready(function () {
|
|||||||
$modalvoicetype.append(new Option(vt, vt));
|
$modalvoicetype.append(new Option(vt, vt));
|
||||||
});
|
});
|
||||||
$modalvoicetype.val(null);
|
$modalvoicetype.val(null);
|
||||||
// fill modalpath options from soundbankfiles[]
|
$('#modalpath').select2()
|
||||||
// TODO read https://jeesite.com/front/jquery-select2/4.0/index.htm
|
|
||||||
console.log("window.select2data has " + window.select2data.length + " items");
|
|
||||||
$('#modalpath').select2({
|
|
||||||
data: window.select2data
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reloadSoundBank(APIURL, () => {
|
reloadSoundBank(APIURL);
|
||||||
fill_soundbanktablebody(window.soundbankdata);
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#findsoundbank').on('input', function () {
|
$('#findsoundbank').on('input', function () {
|
||||||
let searchTerm = $(this).val().trim().toLowerCase();
|
let searchTerm = $(this).val().trim().toLowerCase();
|
||||||
if (searchTerm.length > 0) {
|
if (searchTerm.length > 0) {
|
||||||
@@ -124,23 +214,88 @@ $(document).ready(function () {
|
|||||||
});
|
});
|
||||||
$btnClear.click(() => {
|
$btnClear.click(() => {
|
||||||
DoClear(APIURL, "Soundbank", (okdata) => {
|
DoClear(APIURL, "Soundbank", (okdata) => {
|
||||||
reloadSoundBank(APIURL, () => {
|
reloadSoundBank(APIURL);
|
||||||
fill_soundbanktablebody(window.soundbankdata);
|
alert("Success clear soundbank : " + okdata.message);
|
||||||
alert("Success clear soundbank : " + okdata.message);
|
|
||||||
});
|
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error clear soundbank : " + errdata.message);
|
alert("Error clear soundbank : " + errdata.message);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function SetupEventForCategoryLanguageVoiceType() {
|
||||||
|
$modalcategory.off('change').on('change', function () {
|
||||||
|
selected_category = $(this).val();
|
||||||
|
reloadSoundbankFiles(selected_language, selected_category, selected_voicetype);
|
||||||
|
});
|
||||||
|
$modallanguage.off('change').on('change', function () {
|
||||||
|
selected_language = $(this).val();
|
||||||
|
reloadSoundbankFiles(selected_language, selected_category, selected_voicetype);
|
||||||
|
});
|
||||||
|
$modalvoicetype.off('change').on('change', function () {
|
||||||
|
selected_voicetype = $(this).val();
|
||||||
|
reloadSoundbankFiles(selected_language, selected_category, selected_voicetype);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$btnAdd.click(() => {
|
$btnAdd.click(() => {
|
||||||
|
|
||||||
$modal.modal('show');
|
$modal.modal('show');
|
||||||
clearSoundbankModal();
|
clearSoundbankModal();
|
||||||
|
// event on selection change of language, category, voiceType
|
||||||
|
SetupEventForCategoryLanguageVoiceType();
|
||||||
|
|
||||||
|
|
||||||
// event on Click save button
|
// event on Click save button
|
||||||
$modal.off('click.soundbanksave').on('click.soundbanksave', '#soundbanksave', function () {
|
$modal.off('click.soundbanksave').on('click.soundbanksave', '#soundbanksave', function () {
|
||||||
//TODO Add soundbank save process here
|
let description = $modaldescription.val().trim();
|
||||||
|
let tag = $modaltag.val().trim();
|
||||||
|
let category = $modalcategory.val();
|
||||||
|
let language = $modallanguage.val();
|
||||||
|
let voiceType = $modalvoicetype.val();
|
||||||
|
let path = $('#soundbankmodal #modalpath').val();
|
||||||
|
if (!description || description.length === 0) {
|
||||||
|
alert("Description is required");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!tag || tag.length === 0) {
|
||||||
|
alert("Tag is required");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!category || category.length === 0) {
|
||||||
|
alert("Category is required");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!language || language.length === 0) {
|
||||||
|
alert("Language is required");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!voiceType || voiceType.length === 0) {
|
||||||
|
alert("Voice Type is required");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!path || path.length === 0) {
|
||||||
|
alert("Path is required");
|
||||||
|
return;
|
||||||
|
}
|
||||||
$modal.modal('hide');
|
$modal.modal('hide');
|
||||||
|
/**
|
||||||
|
* @type {SoundBank}
|
||||||
|
*/
|
||||||
|
let nsb = {
|
||||||
|
index: 0,
|
||||||
|
Description: description,
|
||||||
|
TAG: tag,
|
||||||
|
Category: category,
|
||||||
|
Language: language,
|
||||||
|
VoiceType: voiceType,
|
||||||
|
Path: path
|
||||||
|
}
|
||||||
|
fetchAPI(APIURL + "Add", "POST", {}, nsb, (okdata) => {
|
||||||
|
reloadSoundBank(APIURL);
|
||||||
|
alert("Success add soundbank : " + okdata.message);
|
||||||
|
}, (errdata) => {
|
||||||
|
alert("Error add soundbank : " + errdata.message);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
// event on Click close button
|
// event on Click close button
|
||||||
$modal.off('click.soundbankclose').on('click.soundbankclose', '#soundbankclose', function () {
|
$modal.off('click.soundbankclose').on('click.soundbankclose', '#soundbankclose', function () {
|
||||||
@@ -152,7 +307,7 @@ $(document).ready(function () {
|
|||||||
let cells = window.selectedsoundrow.find('td');
|
let cells = window.selectedsoundrow.find('td');
|
||||||
/** @type {SoundBank} */
|
/** @type {SoundBank} */
|
||||||
let sb = {
|
let sb = {
|
||||||
index: cells.eq(0).text(),
|
index: Number(cells.eq(0).text()),
|
||||||
description: cells.eq(1).text(),
|
description: cells.eq(1).text(),
|
||||||
tag: cells.eq(2).text(),
|
tag: cells.eq(2).text(),
|
||||||
category: cells.eq(3).text(),
|
category: cells.eq(3).text(),
|
||||||
@@ -162,10 +317,8 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
if (confirm(`Are you sure to delete soundbank [${sb.index}] Description=${sb.description} Tag=${sb.tag}?`)) {
|
if (confirm(`Are you sure to delete soundbank [${sb.index}] Description=${sb.description} Tag=${sb.tag}?`)) {
|
||||||
fetchAPI(APIURL + "DeleteByIndex/" + sb.index, "DELETE", {}, null, (okdata) => {
|
fetchAPI(APIURL + "DeleteByIndex/" + sb.index, "DELETE", {}, null, (okdata) => {
|
||||||
reloadSoundBank(APIURL, () => {
|
reloadSoundBank(APIURL);
|
||||||
fill_soundbanktablebody(window.soundbankdata);
|
alert("Success delete soundbank : " + okdata.message);
|
||||||
alert("Success delete soundbank : " + okdata.message);
|
|
||||||
});
|
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error delete soundbank : " + errdata.message);
|
alert("Error delete soundbank : " + errdata.message);
|
||||||
});
|
});
|
||||||
@@ -177,28 +330,91 @@ $(document).ready(function () {
|
|||||||
let cells = window.selectedsoundrow.find('td');
|
let cells = window.selectedsoundrow.find('td');
|
||||||
/** @type {SoundBank} */
|
/** @type {SoundBank} */
|
||||||
let sb = {
|
let sb = {
|
||||||
index: cells.eq(0).text(),
|
index: Number(cells.eq(0).text()),
|
||||||
description: cells.eq(1).text(),
|
Description: cells.eq(1).text(),
|
||||||
tag: cells.eq(2).text(),
|
TAG: cells.eq(2).text(),
|
||||||
category: cells.eq(3).text(),
|
Category: cells.eq(3).text(),
|
||||||
language: cells.eq(4).text(),
|
Language: cells.eq(4).text(),
|
||||||
voiceType: cells.eq(5).text(),
|
VoiceType: cells.eq(5).text(),
|
||||||
path: cells.eq(6).text()
|
Path: cells.eq(6).text()
|
||||||
}
|
}
|
||||||
if (confirm(`Are you sure to edit soundbank [${sb.index}] Description=${sb.description} Tag=${sb.tag}?`)) {
|
if (confirm(`Are you sure to edit soundbank [${sb.index}] Description=${sb.Description} Tag=${sb.TAG}?`)) {
|
||||||
$modal.modal('show');
|
$modal.modal('show');
|
||||||
$modal.off('hidden.bs.modal').on('hidden.bs.modal', function () {
|
clearSoundbankModal();
|
||||||
|
SetupEventForCategoryLanguageVoiceType();
|
||||||
|
$modalindex.val(sb.index).prop('disabled', true);
|
||||||
|
$modaldescription.val(sb.Description);
|
||||||
|
$modaltag.val(sb.TAG);
|
||||||
|
$modalcategory.val(sb.Category);
|
||||||
|
selected_category = sb.Category;
|
||||||
|
$modallanguage.val(sb.Language);
|
||||||
|
selected_language = sb.Language;
|
||||||
|
$modalvoicetype.val(sb.VoiceType);
|
||||||
|
selected_voicetype = sb.VoiceType;
|
||||||
|
// load soundbank files for selected language, category, voiceType
|
||||||
|
reloadSoundbankFiles(selected_language, selected_category, selected_voicetype, () => {
|
||||||
|
// set selected path
|
||||||
|
$('#modalpath').val(getFilenameFromPath(sb.Path)).trigger('change');
|
||||||
|
});
|
||||||
|
|
||||||
// event on Click save button
|
|
||||||
|
|
||||||
|
// event on Click save button
|
||||||
$modal.off('click.soundbanksave').on('click.soundbanksave', '#soundbanksave', function () {
|
$modal.off('click.soundbanksave').on('click.soundbanksave', '#soundbanksave', function () {
|
||||||
//TODO Add soundbank save process here
|
let description = $modaldescription.val().trim();
|
||||||
|
let tag = $modaltag.val().trim();
|
||||||
|
let category = $modalcategory.val();
|
||||||
|
let language = $modallanguage.val();
|
||||||
|
let voiceType = $modalvoicetype.val();
|
||||||
|
let path = $('#soundbankmodal #modalpath').val();
|
||||||
|
if (!description || description.length === 0) {
|
||||||
|
alert("Description is required");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!tag || tag.length === 0) {
|
||||||
|
alert("Tag is required");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!category || category.length === 0) {
|
||||||
|
alert("Category is required");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!language || language.length === 0) {
|
||||||
|
alert("Language is required");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!voiceType || voiceType.length === 0) {
|
||||||
|
alert("Voice Type is required");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!path || path.length === 0) {
|
||||||
|
alert("Path is required");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (description === sb.Description && tag === sb.TAG && category === sb.Category && language === sb.Language && voiceType === sb.VoiceType && path === sb.Path) {
|
||||||
|
alert("No changes detected");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sb.Description = description;
|
||||||
|
sb.TAG = tag;
|
||||||
|
sb.Category = category;
|
||||||
|
sb.Language = language;
|
||||||
|
sb.VoiceType = voiceType;
|
||||||
|
sb.Path = path;
|
||||||
|
fetchAPI(APIURL + "UpdateByIndex/" + sb.index, "PATCH", {}, sb, (okdata) => {
|
||||||
|
reloadSoundBank(APIURL);
|
||||||
|
alert("Success update soundbank : " + okdata.message);
|
||||||
|
}, (errdata) => {
|
||||||
|
alert("Error update soundbank : " + errdata.message);
|
||||||
|
});
|
||||||
$modal.modal('hide');
|
$modal.modal('hide');
|
||||||
|
|
||||||
});
|
});
|
||||||
// event on Click close button
|
// event on Click close button
|
||||||
$modal.off('click.soundbankclose').on('click.soundbankclose', '#soundbankclose', function () {
|
$modal.off('click.soundbankclose').on('click.soundbankclose', '#soundbankclose', function () {
|
||||||
$modal.modal('hide');
|
$modal.modal('hide');
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -207,10 +423,8 @@ $(document).ready(function () {
|
|||||||
});
|
});
|
||||||
$btnImport.click(() => {
|
$btnImport.click(() => {
|
||||||
DoImport(APIURL, (okdata) => {
|
DoImport(APIURL, (okdata) => {
|
||||||
reloadSoundBank(APIURL, () => {
|
reloadSoundBank(APIURL);
|
||||||
fill_soundbanktablebody(window.soundbankdata);
|
alert("Success import soundbank : " + okdata.message);
|
||||||
alert("Success import soundbank : " + okdata.message);
|
|
||||||
});
|
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error importing soundbank from XLSX : " + errdata.message);
|
alert("Error importing soundbank from XLSX : " + errdata.message);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* @typedef {Object} SoundChannel
|
||||||
|
* @property {number} index - The index of the sound channel.
|
||||||
|
* @property {string} channel - The name of the sound channel.
|
||||||
|
* @property {string} ip - The IP address associated with the sound channel.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {SoundChannel[]}
|
||||||
|
*/
|
||||||
|
window.soundChannels = [];
|
||||||
|
|
||||||
// Currently selected sound channel row in the table
|
// Currently selected sound channel row in the table
|
||||||
window.selectedSoundChannel = null;
|
window.selectedSoundChannel = null;
|
||||||
@@ -8,10 +18,9 @@ window.selectedSoundChannel = null;
|
|||||||
* @param {SoundChannel[]} vv Sound channel data to populate the table.
|
* @param {SoundChannel[]} vv Sound channel data to populate the table.
|
||||||
*/
|
*/
|
||||||
function fill_soundchanneltablebody(vv) {
|
function fill_soundchanneltablebody(vv) {
|
||||||
let $tbody = $('#soundchanneltablebody');
|
|
||||||
let $btnEditSoundChannel = $('#btnEditSoundChannel');
|
let $btnEditSoundChannel = $('#btnEditSoundChannel');
|
||||||
let $tablesizeSoundChannel = $('#tablesizeSoundChannel');
|
let $tablesizeSoundChannel = $('#tablesizeSoundChannel');
|
||||||
$tbody.empty();
|
$('#soundchanneltablebody').empty();
|
||||||
|
|
||||||
$tablesizeSoundChannel.text('Table Length : N/A');
|
$tablesizeSoundChannel.text('Table Length : N/A');
|
||||||
if (!Array.isArray(vv) || vv.length === 0) return;
|
if (!Array.isArray(vv) || vv.length === 0) return;
|
||||||
@@ -22,9 +31,9 @@ function fill_soundchanneltablebody(vv) {
|
|||||||
<td>${item.channel}</td>
|
<td>${item.channel}</td>
|
||||||
<td>${item.ip}</td>
|
<td>${item.ip}</td>
|
||||||
</tr>`;
|
</tr>`;
|
||||||
$tbody.append(row);
|
$('#soundchanneltablebody').append(row);
|
||||||
let $addedrow = $('#soundchanneltablebody tr:last');
|
let $addedrow = $('#soundchanneltablebody tr:last');
|
||||||
$addedrow.click(function () {
|
$addedrow.off('click').on('click', function () {
|
||||||
if (selectedSoundChannel) {
|
if (selectedSoundChannel) {
|
||||||
selectedSoundChannel.find('td').css('background-color', '');
|
selectedSoundChannel.find('td').css('background-color', '');
|
||||||
if (selectedSoundChannel.is($(this))) {
|
if (selectedSoundChannel.is($(this))) {
|
||||||
@@ -41,7 +50,22 @@ function fill_soundchanneltablebody(vv) {
|
|||||||
$tablesizeSoundChannel.text("Table Size: " + vv.length);
|
$tablesizeSoundChannel.text("Table Size: " + vv.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reload sound channels from server
|
||||||
|
* @param {String} APIURL API URL endpoint (default "SoundChannel/")
|
||||||
|
*/
|
||||||
|
function reloadSoundChannel(APIURL = "SoundChannel/") {
|
||||||
|
window.soundChannels = [];
|
||||||
|
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
||||||
|
if (Array.isArray(okdata)) {
|
||||||
|
//console.log("reloadSoundChannel : ", okdata)
|
||||||
|
window.soundChannels.push(...okdata);
|
||||||
|
fill_soundchanneltablebody(window.soundChannels);
|
||||||
|
} else console.log("reloadSoundChannel: okdata is not array");
|
||||||
|
}, (errdata) => {
|
||||||
|
alert("Error loading sound channels : " + errdata.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
console.log("soundchannel.js loaded successfully");
|
console.log("soundchannel.js loaded successfully");
|
||||||
@@ -58,7 +82,7 @@ $(document).ready(function () {
|
|||||||
$btnEditSoundChannel.prop('disabled', true);
|
$btnEditSoundChannel.prop('disabled', true);
|
||||||
let API_SoundChannel = "SoundChannel/";
|
let API_SoundChannel = "SoundChannel/";
|
||||||
|
|
||||||
$findsoundchannel.on('input', function () {
|
$findsoundchannel.off('input').on('input', function () {
|
||||||
let searchTerm = $(this).val().toLowerCase();
|
let searchTerm = $(this).val().toLowerCase();
|
||||||
if (searchTerm.length==0){
|
if (searchTerm.length==0){
|
||||||
window.selectedSoundChannel = null;
|
window.selectedSoundChannel = null;
|
||||||
@@ -83,20 +107,16 @@ $(document).ready(function () {
|
|||||||
$soundchannelip.val('');
|
$soundchannelip.val('');
|
||||||
}
|
}
|
||||||
|
|
||||||
reloadSoundChannel(API_SoundChannel, () => {
|
reloadSoundChannel(API_SoundChannel);
|
||||||
fill_soundchanneltablebody(window.soundChannels);
|
$btnReinitializeSoundChannel.off('click').on('click', () => {
|
||||||
});
|
|
||||||
$btnReinitializeSoundChannel.click(() => {
|
|
||||||
DoClear(API_SoundChannel, "SoundChannels", (okdata) => {
|
DoClear(API_SoundChannel, "SoundChannels", (okdata) => {
|
||||||
reloadSoundChannel(API_SoundChannel, () => {
|
reloadSoundChannel(API_SoundChannel);
|
||||||
fill_soundchanneltablebody(window.soundChannels);
|
alert("Success clear sound channels: " + okdata.message);
|
||||||
alert("Success clear sound channels: " + okdata.message);
|
|
||||||
});
|
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error clear sound channels: " + errdata.message);
|
alert("Error clear sound channels: " + errdata.message);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$btnEditSoundChannel.click(() => {
|
$btnEditSoundChannel.off('click').on('click', () => {
|
||||||
if (selectedSoundChannel) {
|
if (selectedSoundChannel) {
|
||||||
let cells = selectedSoundChannel.find('td');
|
let cells = selectedSoundChannel.find('td');
|
||||||
/** @type {SoundChannel} */
|
/** @type {SoundChannel} */
|
||||||
@@ -135,10 +155,8 @@ $(document).ready(function () {
|
|||||||
|
|
||||||
|
|
||||||
fetchAPI(API_SoundChannel + "UpdateByIndex/" + newsc.index, "PATCH", {}, newsc, (okdata) => {
|
fetchAPI(API_SoundChannel + "UpdateByIndex/" + newsc.index, "PATCH", {}, newsc, (okdata) => {
|
||||||
reloadSoundChannel(API_SoundChannel, () => {
|
reloadSoundChannel(API_SoundChannel);
|
||||||
fill_soundchanneltablebody(window.soundChannels);
|
alert("Success edit sound channel: " + okdata.message);
|
||||||
alert("Success edit sound channel: " + okdata.message);
|
|
||||||
});
|
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error edit sound channel: " + errdata.message);
|
alert("Error edit sound channel: " + errdata.message);
|
||||||
});
|
});
|
||||||
@@ -152,16 +170,14 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$btnExportSoundChannel.click(() => {
|
$btnExportSoundChannel.off('click').on('click', () => {
|
||||||
DoExport(API_SoundChannel, "soundchannels.xlsx", {});
|
DoExport(API_SoundChannel, "soundchannels.xlsx", {});
|
||||||
});
|
});
|
||||||
|
|
||||||
$btnImportSoundChannel.click(() => {
|
$btnImportSoundChannel.off('click').on('click', () => {
|
||||||
DoImport(API_SoundChannel, (okdata) => {
|
DoImport(API_SoundChannel, (okdata) => {
|
||||||
reloadSoundChannel(API_SoundChannel, () => {
|
reloadSoundChannel(API_SoundChannel);
|
||||||
fill_soundchanneltablebody(window.soundChannels);
|
alert("Success import sound channels: " + okdata.message);
|
||||||
alert("Success import sound channels: " + okdata.message);
|
|
||||||
});
|
|
||||||
}, (errdata) => {
|
}, (errdata) => {
|
||||||
alert("Error importing sound channels from XLSX: " + errdata.message);
|
alert("Error importing sound channels from XLSX: " + errdata.message);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,9 +4,10 @@
|
|||||||
* @property {string} username Username
|
* @property {string} username Username
|
||||||
* @property {string} password Password (plain)
|
* @property {string} password Password (plain)
|
||||||
* @property {string} location Location
|
* @property {string} location Location
|
||||||
* @property {string} soundbank_tags Soundbank variable tags separated by semicolon ;
|
* @property {string} airline_tags Airline variable tags (string) separated by semicolon ;
|
||||||
* @property {string} messagebank_ann_id Messagebank announcement ID separated by semicolon ;
|
* @property {string} city_tags City variable tags (string) separated by semicolon ;
|
||||||
* @property {string} broadcastzones Broadcast zones separated by semicolon ;
|
* @property {string} messagebank_ann_id Messagebank announcement ID (number) separated by semicolon ;
|
||||||
|
* @property {string} broadcastzones Broadcast zones description (string) separated by semicolon ;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** List of UserDB data loaded from server
|
/** List of UserDB data loaded from server
|
||||||
@@ -20,13 +21,96 @@ window.userdb = [];
|
|||||||
*/
|
*/
|
||||||
window.selecteduserrow = null;
|
window.selecteduserrow = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} KeyValueMessage
|
||||||
|
* @property {string} key
|
||||||
|
* @property {string} value
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of airline tags loaded from server
|
||||||
|
* @type {KeyValueMessage[]}
|
||||||
|
*/
|
||||||
|
window.airlinetags = [];
|
||||||
|
/**
|
||||||
|
* List of city tags loaded from server
|
||||||
|
* @type {KeyValueMessage[]}
|
||||||
|
*/
|
||||||
|
window.citytags = [];
|
||||||
|
/**
|
||||||
|
* List of message bank IDs loaded from server
|
||||||
|
* @type {KeyValueMessage[]}
|
||||||
|
*/
|
||||||
|
window.messagebankids = [];
|
||||||
|
/**
|
||||||
|
* List of broadcast zones description loaded from server
|
||||||
|
* @type {string[]}
|
||||||
|
*/
|
||||||
|
window.broadcastzones = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Messagebank ANN_IDs from server
|
||||||
|
*/
|
||||||
|
function get_messagebankids() {
|
||||||
|
window.messagebankids = [];
|
||||||
|
fetchAPI("MessageBank/" + "MessageIDs", "GET", {}, null, (okdata) => {
|
||||||
|
if (Array.isArray(okdata)) {
|
||||||
|
window.messagebankids.push(...okdata);
|
||||||
|
}
|
||||||
|
}, (errdata) => {
|
||||||
|
alert("Error loading message bank IDs : " + errdata.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Airline Tags from server
|
||||||
|
*/
|
||||||
|
function get_airlinetags() {
|
||||||
|
window.airlinetags = [];
|
||||||
|
fetchAPI("SoundBank/" + "AirlineTags", "GET", {}, null, (okdata) => {
|
||||||
|
if (Array.isArray(okdata)) {
|
||||||
|
window.airlinetags.push(...okdata);
|
||||||
|
}
|
||||||
|
}, (errdata) => {
|
||||||
|
alert("Error loading airline tags : " + errdata.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get City Tags from server
|
||||||
|
*/
|
||||||
|
function get_citytags() {
|
||||||
|
window.citytags = [];
|
||||||
|
fetchAPI("SoundBank/" + "CityTags", "GET", {}, null, (okdata) => {
|
||||||
|
if (Array.isArray(okdata)) {
|
||||||
|
window.citytags.push(...okdata);
|
||||||
|
}
|
||||||
|
}, (errdata) => {
|
||||||
|
alert("Error loading city tags : " + errdata.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Broadcast Zones descriptions from server
|
||||||
|
*/
|
||||||
|
function get_broadcastzones_descriptions() {
|
||||||
|
window.broadcastzones = [];
|
||||||
|
fetchAPI("BroadcastZones/" + "BroadcastZoneDescriptions", "GET", {}, null, (okdata) => {
|
||||||
|
if (Array.isArray(okdata)) {
|
||||||
|
window.broadcastzones.push(...okdata);
|
||||||
|
}
|
||||||
|
}, (errdata) => {
|
||||||
|
alert("Error loading broadcast zones : " + errdata.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fill user table body with values
|
* Fill user table body with values
|
||||||
* @param {UserDB[]} vv values to fill
|
* @param {UserDB[]} vv values to fill
|
||||||
*/
|
*/
|
||||||
function fill_usertablebody(vv) {
|
function fill_usertablebody(vv) {
|
||||||
$('#usertablebody').empty();
|
$('#usertablebody').empty();
|
||||||
|
|
||||||
if (!Array.isArray(vv) || vv.length === 0) {
|
if (!Array.isArray(vv) || vv.length === 0) {
|
||||||
$('#btnExport').prop('disabled', true);
|
$('#btnExport').prop('disabled', true);
|
||||||
return;
|
return;
|
||||||
@@ -34,14 +118,16 @@ function fill_usertablebody(vv) {
|
|||||||
vv.forEach(item => {
|
vv.forEach(item => {
|
||||||
const row = `<tr>
|
const row = `<tr>
|
||||||
<td>${item.index}</td>
|
<td>${item.index}</td>
|
||||||
<td>${item.datenya}</td>
|
<td>${item.username}</td>
|
||||||
<td>${item.timenya}</td>
|
<td>${item.location}</td>
|
||||||
<td>${item.machine}</td>
|
<td>${item.airline_tags}</td>
|
||||||
<td>${item.description}</td>
|
<td>${item.city_tags}</td>
|
||||||
|
<td>${item.messagebank_ann_id}</td>
|
||||||
|
<td>${item.broadcastzones}</td>
|
||||||
</tr>`;
|
</tr>`;
|
||||||
$('#usertablebody').append(row);
|
$('#usertablebody').append(row);
|
||||||
let $addedrow = $('#usertablebody tr:last');
|
let $addedrow = $('#usertablebody tr:last');
|
||||||
$addedrow.on('click', function () {
|
$addedrow.off('click').on('click', function () {
|
||||||
if (window.selecteduserrow) {
|
if (window.selecteduserrow) {
|
||||||
window.selecteduserrow.find('td').css('background-color', '');
|
window.selecteduserrow.find('td').css('background-color', '');
|
||||||
if (window.selecteduserrow.is($(this))) {
|
if (window.selecteduserrow.is($(this))) {
|
||||||
@@ -63,9 +149,9 @@ function fill_usertablebody(vv) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Reload UserDB from server with date and filter
|
* Reload UserDB from server with date and filter
|
||||||
* @param {String} APIURL API URL endpoint , default "User/"
|
* @param {String} APIURL API URL endpoint , default "UserManagement/"
|
||||||
*/
|
*/
|
||||||
function reloaduserDB(APIURL = "User/") {
|
function reloaduserDB(APIURL = "UserManagement/") {
|
||||||
window.userdb = [];
|
window.userdb = [];
|
||||||
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
||||||
if (Array.isArray(okdata)) {
|
if (Array.isArray(okdata)) {
|
||||||
@@ -79,50 +165,395 @@ function reloaduserDB(APIURL = "User/") {
|
|||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
console.log("usermanagement.js ready");
|
console.log("usermanagement.js ready");
|
||||||
let $usertablebody = $('#usertablebody');
|
get_airlinetags();
|
||||||
let $finduser = $('#finduser');
|
get_citytags();
|
||||||
let $btnClear = $('#btnClear');
|
get_messagebankids();
|
||||||
let $btnAdd = $('#btnAdd');
|
get_broadcastzones_descriptions();
|
||||||
let $btnRemove = $('#btnRemove');
|
|
||||||
let $btnEdit = $('#btnEdit');
|
let APIURL = "UserManagement/";
|
||||||
let $btnExport = $('#btnExport');
|
|
||||||
let $btnImport = $('#btnImport');
|
function clearAddModal() {
|
||||||
let APIURL = "User/";
|
$('#modalindex').val("");
|
||||||
|
$('#modalusername').val("");
|
||||||
|
$('#modalpassword').val("");
|
||||||
|
$('#modalverifypassword').val("");
|
||||||
|
$('#modalairlinetags').val("");
|
||||||
|
$('#modalcitytags').val("");
|
||||||
|
$('#modalmessagebank').val("");
|
||||||
|
$('#modalbroadcastzones').val("");
|
||||||
|
$('#modallocation').val("");
|
||||||
|
}
|
||||||
|
|
||||||
|
function fill_citylist() {
|
||||||
|
$('#citylist').empty();
|
||||||
|
citytags.forEach(tag => {
|
||||||
|
let value = `${tag.value} [${tag.key}]`;
|
||||||
|
const row = `<div class="form-check">
|
||||||
|
<input class="form-check-input citytagcheckbox" type="checkbox" value="${tag.key}" id="citytag_${tag.key}">
|
||||||
|
<label class="form-check-label" for="citytag_${tag.key}">
|
||||||
|
${value}
|
||||||
|
</label>
|
||||||
|
</div>`;
|
||||||
|
$('#citylist').append(row);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function fill_airlinelist() {
|
||||||
|
$('#airlinelist').empty();
|
||||||
|
airlinetags.forEach(tag => {
|
||||||
|
let value = `${tag.value} [${tag.key}]`;
|
||||||
|
const row = `<div class="form-check">
|
||||||
|
<input class="form-check-input airlinetagcheckbox" type="checkbox" value="${tag.key}" id="airlinetag_${tag.key}">
|
||||||
|
<label class="form-check-label" for="airlinetag_${tag.key}">
|
||||||
|
${value}
|
||||||
|
</label>
|
||||||
|
</div>`;
|
||||||
|
$('#airlinelist').append(row);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// add / edit modal elements
|
|
||||||
let $addmodal = $('#addmodal');
|
|
||||||
let $modalindex = $('#modalindex');
|
|
||||||
let $modalusername = $('#modalusername');
|
|
||||||
let $modalpassword = $('#modalpassword');
|
|
||||||
let $modalverifypassword = $('#modalverifypassword');
|
|
||||||
let $modalsoundbank = $('#modalsoundbank');
|
|
||||||
let $modalmessagebank = $('#modalmessagebank');
|
|
||||||
let $modalbroadcastzones = $('#modalbroadcastzones');
|
|
||||||
let $btnShowSoundbankModal = $('#btnShowSoundbankModal');
|
|
||||||
let $btnShowMessagebankModal = $('#btnShowMessagebankModal');
|
|
||||||
let $btnShowBroaadcastZoneModal = $('#btnShowBroaadcastZoneModal');
|
|
||||||
let $usermanagementsave = $('#usermanagementsave');
|
|
||||||
let $usermanagementclose = $('#usermanagementclose');
|
|
||||||
|
|
||||||
// soundbank selection modal elements
|
|
||||||
let $soundbankmodal = $('#soundbankmodal');
|
|
||||||
let $soundbankselection = $('#soundbankselection');
|
|
||||||
let $soundbankselectionsave = $('#soundbankselectionsave');
|
|
||||||
let $soundbankselectionclose = $('#soundbankselectionclose');
|
|
||||||
|
|
||||||
// broadcast zone selection modal elements
|
// broadcast zone selection modal elements
|
||||||
let $broadcastzonemodal = $('#broadcastzonemodal');
|
|
||||||
let $broadcastzoneselection = $('#broadcastzoneselection');
|
|
||||||
let $broadcastzoneselectionsave = $('#broadcastzoneselectionsave');
|
|
||||||
let $broadcastzoneselectionclose = $('#broadcastzoneselectionclose');
|
|
||||||
|
|
||||||
// messagebank selection modal elements
|
|
||||||
let $messagebankmodal = $('#messagebankmodal');
|
|
||||||
let $messagebankselection = $('#messagebankselection');
|
|
||||||
let $messagebankselectionsave = $('#messagebankselectionsave');
|
|
||||||
let $messagebankselectionclose = $('#messagebankselectionclose');
|
|
||||||
|
|
||||||
$usertablebody.empty();
|
function fill_broadcastzonelist() {
|
||||||
$finduser.on('input', function () {
|
$('#broadcastzonelist').empty();
|
||||||
|
broadcastzones.forEach(desc => {
|
||||||
|
const row = `<div class="form-check">
|
||||||
|
<input class="form-check-input broadcastzonecheckbox" type="checkbox" value="${desc}" id="broadcastzone_${desc}">
|
||||||
|
<label class="form-check-label" for="broadcastzone_${desc}">
|
||||||
|
${desc}
|
||||||
|
</label>
|
||||||
|
</div>`;
|
||||||
|
$('#broadcastzonelist').append(row);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// messagebank selection modal elements
|
||||||
|
|
||||||
|
function fill_messagebanklist() {
|
||||||
|
$('#messagebanklist').empty();
|
||||||
|
messagebankids.forEach(id => {
|
||||||
|
let value = `${id.value} [${id.key}]`;
|
||||||
|
const row = `<div class="form-check">
|
||||||
|
<input class="form-check-input messagebankidcheckbox" type="checkbox" value="${id.key}" id="messagebankid_${id.key}">
|
||||||
|
<label class="form-check-label" for="messagebankid_${id.key}">
|
||||||
|
${value}
|
||||||
|
</label>
|
||||||
|
</div>`;
|
||||||
|
$('#messagebanklist').append(row);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$('#usertablebody').empty();
|
||||||
|
reloaduserDB();
|
||||||
|
$('#finduser').off('input').on('input', function () {
|
||||||
|
let searchTerm = $(this).val().toLowerCase();
|
||||||
|
if (searchTerm.length > 0) {
|
||||||
|
let filteredUsers = window.userdb.filter(user =>
|
||||||
|
user.username.toLowerCase().includes(searchTerm) ||
|
||||||
|
user.airline_tags.toLowerCase().includes(searchTerm) ||
|
||||||
|
user.city_tags.toLowerCase().includes(searchTerm)
|
||||||
|
//user.messagebank_ann_id.toLowerCase().includes(searchTerm) ||
|
||||||
|
//user.broadcastzones.toLowerCase().includes(searchTerm)
|
||||||
|
);
|
||||||
|
fill_usertablebody(filteredUsers);
|
||||||
|
} else {
|
||||||
|
fill_usertablebody(window.userdb);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show modal dialog for soundbank, messagebank, broadcastzone selection
|
||||||
|
* @param {boolean} editmode if true, edit mode, else add mode
|
||||||
|
* @param {number} index index of user to edit, default 0
|
||||||
|
*/
|
||||||
|
function modalshow(editmode = false, index=0) {
|
||||||
|
// event on click btnShowSoundbankModal
|
||||||
|
$('#btnShowSoundbankModal').off('click').on('click', function () {
|
||||||
|
$('#soundbankmodal').modal('show');
|
||||||
|
fill_citylist();
|
||||||
|
fill_airlinelist();
|
||||||
|
|
||||||
|
let airline = $('#modalairlinetags').val().trim();
|
||||||
|
let city = $('#modalcitytags').val().trim();
|
||||||
|
if (airline.length > 0) {
|
||||||
|
let airlinekeys = airline.split(";");
|
||||||
|
$('#airlinelist input[type=checkbox]').each(function () {
|
||||||
|
let tag = $(this).val();
|
||||||
|
if (airlinekeys.includes(tag)) {
|
||||||
|
$(this).prop('checked', true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (city.length > 0) {
|
||||||
|
let citykeys = city.split(";");
|
||||||
|
$('#citylist input[type=checkbox]').each(function () {
|
||||||
|
let tag = $(this).val();
|
||||||
|
if (citykeys.includes(tag)) {
|
||||||
|
$(this).prop('checked', true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#soundbankmodal').off('click.soundbankselectionsave').on('click.soundbankselectionsave', '#soundbankselectionsave', function () {
|
||||||
|
let selected_airlinetags = [];
|
||||||
|
$('#airlinelist input[type=checkbox]:checked').each(function () {
|
||||||
|
selected_airlinetags.push($(this).val());
|
||||||
|
});
|
||||||
|
let selected_citytags = [];
|
||||||
|
$('#citylist input[type=checkbox]:checked').each(function () {
|
||||||
|
selected_citytags.push($(this).val());
|
||||||
|
});
|
||||||
|
|
||||||
|
//console.log("Selected airline tags: ", selected_airlinetags);
|
||||||
|
//console.log("Selected city tags: ", selected_citytags);
|
||||||
|
|
||||||
|
if (selected_airlinetags.length == 0 || selected_citytags.length == 0) {
|
||||||
|
alert("Please select at least one airline tag and one city tag.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let airlinevalue = selected_airlinetags.join(";");
|
||||||
|
let cityvalue = selected_citytags.join(";");
|
||||||
|
$('#modalairlinetags').val(airlinevalue);
|
||||||
|
$('#modalcitytags').val(cityvalue);
|
||||||
|
|
||||||
|
|
||||||
|
$('#soundbankmodal').modal('hide');
|
||||||
|
});
|
||||||
|
$('#soundbankmodal').off('click.soundbankselectionclose').on('click.soundbankselectionclose', '#soundbankselectionclose', function () {
|
||||||
|
$('#soundbankmodal').modal('hide');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// event on click btnShowMessagebankModal
|
||||||
|
$('#btnShowMessagebankModal').off('click').on('click', function () {
|
||||||
|
$('#messagebankmodal').modal('show');
|
||||||
|
fill_messagebanklist();
|
||||||
|
let messagebank = $('#modalmessagebank').val().trim();
|
||||||
|
if (messagebank.length > 0) {
|
||||||
|
let messagebankkeys = messagebank.split(";");
|
||||||
|
$('#messagebanklist input[type=checkbox]').each(function () {
|
||||||
|
let id = $(this).val();
|
||||||
|
if (messagebankkeys.includes(id)) {
|
||||||
|
$(this).prop('checked', true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#messagebankmodal').off('click.messagebankselectionsave').on('click.messagebankselectionsave', '#messagebankselectionsave', function () {
|
||||||
|
let selected_messagebankids = [];
|
||||||
|
$('#messagebanklist input[type=checkbox]:checked').each(function () {
|
||||||
|
selected_messagebankids.push($(this).val());
|
||||||
|
});
|
||||||
|
//console.log("Selected message bank IDs: ", selected_messagebankids);
|
||||||
|
if (selected_messagebankids.length == 0) {
|
||||||
|
alert("Please select at least one message bank ID.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let messagebankvalue = selected_messagebankids.join(";");
|
||||||
|
$('#modalmessagebank').val(messagebankvalue);
|
||||||
|
|
||||||
|
$('#messagebankmodal').modal('hide');
|
||||||
|
});
|
||||||
|
$('#messagebankmodal').off('click.messagebankselectionclose').on('click.messagebankselectionclose', '#messagebankselectionclose', function () {
|
||||||
|
$('#messagebankmodal').modal('hide');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// event on click btnShowBroaadcastZoneModal
|
||||||
|
$('#btnShowBroaadcastZoneModal').off('click').on('click', function () {
|
||||||
|
$('#broadcastzonemodal').modal('show');
|
||||||
|
fill_broadcastzonelist();
|
||||||
|
let broadcastzones = $('#modalbroadcastzones').val().trim();
|
||||||
|
if (broadcastzones.length > 0) {
|
||||||
|
let broadcastzonesvalues = broadcastzones.split(";");
|
||||||
|
$('#broadcastzonelist input[type=checkbox]').each(function () {
|
||||||
|
let desc = $(this).val();
|
||||||
|
if (broadcastzonesvalues.includes(desc)) {
|
||||||
|
$(this).prop('checked', true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$('#broadcastzonemodal').off('click.broadcastzoneselectionsave').on('click.broadcastzoneselectionsave', '#broadcastzoneselectionsave', function () {
|
||||||
|
let selected_broadcastzones = [];
|
||||||
|
$('#broadcastzonelist input[type=checkbox]:checked').each(function () {
|
||||||
|
selected_broadcastzones.push($(this).val());
|
||||||
|
});
|
||||||
|
//console.log("Selected broadcast zones: ", selected_broadcastzones);
|
||||||
|
if (selected_broadcastzones.length == 0) {
|
||||||
|
alert("Please select at least one broadcast zone.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let broadcastzonesvalue = selected_broadcastzones.join(";");
|
||||||
|
$('#modalbroadcastzones').val(broadcastzonesvalue);
|
||||||
|
$('#broadcastzonemodal').modal('hide');
|
||||||
|
});
|
||||||
|
$('#broadcastzonemodal').off('click.broadcastzoneselectionclose').on('click.broadcastzoneselectionclose', '#broadcastzoneselectionclose', function () {
|
||||||
|
$('#broadcastzonemodal').modal('hide');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// event on Click save button
|
||||||
|
$('#addmodal').off('click.usermanagementsave').on('click.usermanagementsave', '#usermanagementsave', function () {
|
||||||
|
let username = $('#modalusername').val().trim();
|
||||||
|
let password = $('#modalpassword').val();
|
||||||
|
let verifypassword = $('#modalverifypassword').val();
|
||||||
|
let location = $('#modallocation').val().trim();
|
||||||
|
let airline_tags = $('#modalairlinetags').val().trim();
|
||||||
|
let city_tags = $('#modalcitytags').val().trim();
|
||||||
|
let messagebank_ann_id = $('#modalmessagebank').val().trim();
|
||||||
|
let broadcastzones = $('#modalbroadcastzones').val().trim();
|
||||||
|
|
||||||
|
if (username.length === 0) {
|
||||||
|
alert("Username cannot be empty");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (password.length === 0 || verifypassword.length === 0) {
|
||||||
|
alert("Password cannot be empty");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (password !== verifypassword) {
|
||||||
|
alert("Password and Verify Password do not match");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (airline_tags.length === 0) {
|
||||||
|
alert("Airline tags cannot be empty");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (city_tags.length === 0) {
|
||||||
|
alert("City tags cannot be empty");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (messagebank_ann_id.length === 0) {
|
||||||
|
alert("Message bank ANN_ID cannot be empty");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (broadcastzones.length === 0) {
|
||||||
|
alert("Broadcast zones cannot be empty");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {UserDB}
|
||||||
|
*/
|
||||||
|
let ll = {
|
||||||
|
index: index,
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
location: location,
|
||||||
|
airline_tags: airline_tags,
|
||||||
|
city_tags: city_tags,
|
||||||
|
messagebank_ann_id: messagebank_ann_id,
|
||||||
|
broadcastzones: broadcastzones
|
||||||
|
}
|
||||||
|
|
||||||
|
if (editmode) {
|
||||||
|
fetchAPI(APIURL + "UpdateByIndex/" + index, "PATCH", {}, ll, (okdata) => {
|
||||||
|
alert("Success update User : " + okdata.message);
|
||||||
|
reloaduserDB();
|
||||||
|
}, (errdata) => {
|
||||||
|
alert("Error update User : " + errdata.message);
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
fetchAPI(APIURL + "Add", "POST", {}, ll, (okdata) => {
|
||||||
|
alert("Success add User : " + okdata.message);
|
||||||
|
reloaduserDB();
|
||||||
|
}, (errdata) => {
|
||||||
|
alert("Error add User : " + errdata.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$('#addmodal').modal('hide');
|
||||||
|
});
|
||||||
|
// event on Click close button
|
||||||
|
$('#addmodal').off('click.usermanagementclose').on('click.usermanagementclose', '#usermanagementclose', function () {
|
||||||
|
$('#addmodal').modal('hide');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#btnClear').off('click').on('click', function () {
|
||||||
|
DoClear(APIURL, "UserManagement", (okdata) => {
|
||||||
|
reloaduserDB();
|
||||||
|
alert("Success clear user management : " + okdata.message);
|
||||||
|
}, (errdata) => {
|
||||||
|
alert("Error clear user management : " + errdata.message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
$('#btnAdd').off('click').on('click', () => {
|
||||||
|
$('#addmodal').modal('show');
|
||||||
|
clearAddModal();
|
||||||
|
modalshow(false,0);
|
||||||
|
});
|
||||||
|
$('#btnRemove').off('click').on('click', () => {
|
||||||
|
if (window.selecteduserrow) {
|
||||||
|
let cells = window.selecteduserrow.find('td');
|
||||||
|
/** @type {UserDB} */
|
||||||
|
let user = {
|
||||||
|
index: parseInt(cells.eq(0).text()),
|
||||||
|
username: cells.eq(1).text(),
|
||||||
|
password: cells.eq(2).text(),
|
||||||
|
airline_tags: cells.eq(3).text(),
|
||||||
|
city_tags: cells.eq(4).text(),
|
||||||
|
messagebank_ann_id: cells.eq(5).text(),
|
||||||
|
broadcastzones: cells.eq(6).text()
|
||||||
|
}
|
||||||
|
if (confirm(`Are you sure to delete user [${user.index}] Username=${user.username} ?`)) {
|
||||||
|
fetchAPI(APIURL + "DeleteByIndex/" + user.index, "DELETE", {}, null, (okdata) => {
|
||||||
|
reloaduserDB();
|
||||||
|
alert("Success delete user : " + okdata.message);
|
||||||
|
}, (errdata) => {
|
||||||
|
alert("Error delete user : " + errdata.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert("No user selected");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$('#btnEdit').off('click').on('click', () => {
|
||||||
|
if (window.selecteduserrow) {
|
||||||
|
let cells = window.selecteduserrow.find('td');
|
||||||
|
let index = parseInt(cells.eq(0).text());
|
||||||
|
if (isNaN(index) || index <= 0) {
|
||||||
|
alert("Invalid user index");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/** @type {UserDB} */
|
||||||
|
let user = window.userdb.find(u => u.index === index);
|
||||||
|
if (!user) {
|
||||||
|
alert("User not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (confirm(`Are you sure to edit user [${user.index}] Username=${user.username} ?`)) {
|
||||||
|
$('#addmodal').modal('show');
|
||||||
|
// fill modal with user data
|
||||||
|
$('#modalindex').val(user.index);
|
||||||
|
$('#modalusername').val(user.username);
|
||||||
|
$('#modalpassword').val(user.password);
|
||||||
|
$('#modalverifypassword').val(user.password);
|
||||||
|
$('#modallocation').val(user.location);
|
||||||
|
$('#modalairlinetags').val(user.airline_tags);
|
||||||
|
$('#modalcitytags').val(user.city_tags);
|
||||||
|
$('#modalmessagebank').val(user.messagebank_ann_id);
|
||||||
|
$('#modalbroadcastzones').val(user.broadcastzones);
|
||||||
|
modalshow(true, user.index);
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert("No user selected");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$('#btnExport').off('click').on('click', () => {
|
||||||
|
DoExport(APIURL, "user.xlsx", {});
|
||||||
|
});
|
||||||
|
$('#btnImport').off('click').on('click', () => {
|
||||||
|
DoImport(APIURL, (okdata) => {
|
||||||
|
reloaduserDB();
|
||||||
|
alert("Success import user : " + okdata.message);
|
||||||
|
}, (errdata) => {
|
||||||
|
alert("Error importing user from XLSX : " + errdata.message);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -4,8 +4,9 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||||
<title>AAS_NewGen_30Sept25</title>
|
<title>AAS_NewGen_17OKT25</title>
|
||||||
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||||
<link rel="stylesheet" href="assets/css/styles.css">
|
<link rel="stylesheet" href="assets/css/styles.css">
|
||||||
</head>
|
</head>
|
||||||
@@ -19,15 +20,15 @@
|
|||||||
<div class="modal fade" role="dialog" tabindex="-1" id="broadcastzonemodal">
|
<div class="modal fade" role="dialog" tabindex="-1" id="broadcastzonemodal">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header bg-header-modal">
|
||||||
<h4 class="modal-title">Add / Edit Broadcast Zones</h4><button class="btn-close" type="button" aria-label="Close" data-bs-dismiss="modal"></button>
|
<h4 class="modal-title">Add / Edit Broadcast Zones</h4><button class="btn-close" type="button" aria-label="Close" data-bs-dismiss="modal"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body bg-modal-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||||
<p class="text-add">Index</p>
|
<p class="text-add">Index</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input class="w-25 form-control input-add" type="text" id="broadcastzoneindex" placeholder="index"></div>
|
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input class="w-25 form-control input-add" type="text" id="broadcastzoneindex" placeholder="index" readonly=""></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||||
@@ -79,7 +80,7 @@
|
|||||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R08"><label class="form-check-label" for="formCheck-8">08</label></div>
|
<div class="form-check"><input class="form-check-input" type="checkbox" id="R08"><label class="form-check-label" for="formCheck-8">08</label></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-3">
|
<div class="col-3 invisible">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R09"><label class="form-check-label" for="formCheck-25">09</label></div>
|
<div class="form-check"><input class="form-check-input" type="checkbox" id="R09"><label class="form-check-label" for="formCheck-25">09</label></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -105,7 +106,7 @@
|
|||||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R16"><label class="form-check-label" for="formCheck-32">16</label></div>
|
<div class="form-check"><input class="form-check-input" type="checkbox" id="R16"><label class="form-check-label" for="formCheck-32">16</label></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-3">
|
<div class="col-3 invisible">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R17"><label class="form-check-label" for="formCheck-17">17</label></div>
|
<div class="form-check"><input class="form-check-input" type="checkbox" id="R17"><label class="form-check-label" for="formCheck-17">17</label></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -131,7 +132,7 @@
|
|||||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R24"><label class="form-check-label" for="formCheck-24">24</label></div>
|
<div class="form-check"><input class="form-check-input" type="checkbox" id="R24"><label class="form-check-label" for="formCheck-24">24</label></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-3">
|
<div class="col-3 invisible">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R25"><label class="form-check-label" for="formCheck-9">25</label></div>
|
<div class="form-check"><input class="form-check-input" type="checkbox" id="R25"><label class="form-check-label" for="formCheck-9">25</label></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -194,9 +195,9 @@
|
|||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="col-sm-1">No</th>
|
<th class="class05">No</th>
|
||||||
<th class="col-sm-3">Description</th>
|
<th class="class75">Description</th>
|
||||||
<th class="col">IP Address</th>
|
<th class="class20">IP Address</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="soundchanneltablebody"></tbody>
|
<tbody id="soundchanneltablebody"></tbody>
|
||||||
@@ -235,11 +236,11 @@
|
|||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="col-sm-1">No</th>
|
<th class="class05">No</th>
|
||||||
<th class="col-sm-2">Description</th>
|
<th class="class40">Description</th>
|
||||||
<th class="col-sm-2">SoundChannel</th>
|
<th class="class20">SoundChannel</th>
|
||||||
<th class="col-sm-2">ID</th>
|
<th class="class05">ID</th>
|
||||||
<th class="col">BP</th>
|
<th class="class30">BP</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="broadcastzonetablebody"></tbody>
|
<tbody id="broadcastzonetablebody"></tbody>
|
||||||
@@ -254,10 +255,10 @@
|
|||||||
<div class="modal fade" role="dialog" tabindex="-1" id="soundchannelmodal">
|
<div class="modal fade" role="dialog" tabindex="-1" id="soundchannelmodal">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header bg-header-modal">
|
||||||
<h4 class="modal-title">Edit Sound Channel</h4><button class="btn-close" type="button" aria-label="Close" data-bs-dismiss="modal"></button>
|
<h4 class="modal-title">Edit Sound Channel</h4><button class="btn-close" type="button" aria-label="Close" data-bs-dismiss="modal"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body bg-modal-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||||
<p class="text-add">Index</p>
|
<p class="text-add">Index</p>
|
||||||
@@ -268,13 +269,13 @@
|
|||||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||||
<p class="text-add">Description</p>
|
<p class="text-add">Description</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-8 col-md-8 col-xl-8 co-lg-8"><input class="w-100 form-control input-add" type="text" id="soundchanneldescription" placeholder="Description"></div>
|
<div class="col-8 col-sm-8 col-md-8 col-xl-8 co-lg-8"><input class="w-100 form-control input-add" type="text" id="soundchanneldescription" placeholder="Description" readonly=""></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||||
<p class="text-add">IP Address</p>
|
<p class="text-add">IP Address</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-8 col-md-8 col-xl-8 co-lg-8"><input class="w-100 form-control input-add" type="text" id="soundchannelip" placeholder="IP Address"></div>
|
<div class="col-8 col-sm-8 col-md-8 col-xl-8 co-lg-8"><input class="w-100 form-control input-add" type="text" id="soundchannelip" placeholder="IP Address" required="" inputmode="numeric" pattern="^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}$"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer"><button class="btn btn-round-basic color-edit class25" id="soundchannelclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-round-basic color-add class25" id="soundchannelsave" type="button">Save</button></div>
|
<div class="modal-footer"><button class="btn btn-round-basic color-edit class25" id="soundchannelclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-round-basic color-add class25" id="soundchannelsave" type="button">Save</button></div>
|
||||||
@@ -282,6 +283,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||||
|
<script src="assets/js/bs-init.js"></script>
|
||||||
<script src="assets/js/soundchannel.js"></script>
|
<script src="assets/js/soundchannel.js"></script>
|
||||||
<script src="assets/js/broadcastzones.js"></script>
|
<script src="assets/js/broadcastzones.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||||
<title>AAS NewGeneration 17092025</title>
|
<title>AAS NewGeneration 17092025</title>
|
||||||
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||||
|
<link rel="stylesheet" href="assets/css/select2.min.css">
|
||||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||||
<link rel="stylesheet" href="assets/css/styles.css">
|
<link rel="stylesheet" href="assets/css/styles.css">
|
||||||
</head>
|
</head>
|
||||||
@@ -97,9 +99,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="container w-100 pad-container" id="content"></div>
|
<div class="container w-100 pad-container" id="content"></div>
|
||||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
<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/jquery-3.7.1.min.js"></script>
|
||||||
<script src="assets/js/select2.js"></script>
|
|
||||||
<script src="assets/js/script.js"></script>
|
<script src="assets/js/script.js"></script>
|
||||||
|
<script src="assets/js/select2.min.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -4,8 +4,9 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||||
<title>AAS_NewGen_30Sept25</title>
|
<title>AAS_NewGen_17OKT25</title>
|
||||||
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||||
<link rel="stylesheet" href="assets/css/styles.css">
|
<link rel="stylesheet" href="assets/css/styles.css">
|
||||||
</head>
|
</head>
|
||||||
@@ -41,9 +42,9 @@
|
|||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="col-sm-1">No</th>
|
<th class="class05">No</th>
|
||||||
<th class="col-sm-2">TAG</th>
|
<th class="class20">TAG</th>
|
||||||
<th class="col">Languages</th>
|
<th class="class75">Languages</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="languagebanktablebody"></tbody>
|
<tbody id="languagebanktablebody"></tbody>
|
||||||
@@ -53,10 +54,10 @@
|
|||||||
<div class="modal fade" role="dialog" tabindex="-1" id="languagemodal">
|
<div class="modal fade" role="dialog" tabindex="-1" id="languagemodal">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header bg-header-modal">
|
||||||
<h4 class="modal-title">Add / Edit Language</h4><button class="btn-close" type="button" aria-label="Close" data-bs-dismiss="modal"></button>
|
<h4 class="modal-title">Add / Edit Language</h4><button class="btn-close" type="button" aria-label="Close" data-bs-dismiss="modal"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body bg-modal-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||||
<p class="text-add">Index</p>
|
<p class="text-add">Index</p>
|
||||||
@@ -88,6 +89,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
<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/languagelink.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||||
<title>AAS_NewGen_30Sept25</title>
|
<title>AAS_NewGen_17OKT25</title>
|
||||||
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||||
<link rel="stylesheet" href="assets/css/styles.css">
|
<link rel="stylesheet" href="assets/css/styles.css">
|
||||||
</head>
|
</head>
|
||||||
@@ -38,11 +39,11 @@
|
|||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="col-1 col-md-1">No</th>
|
<th class="class10">No</th>
|
||||||
<th class="col-2 col-md-2 col-lg-2">Date</th>
|
<th class="class15">Date</th>
|
||||||
<th class="col-2 col-md-2 col-lg-2">Time</th>
|
<th class="class15">Time</th>
|
||||||
<th class="col-2 col-md-2 col-lg-2">Machine</th>
|
<th class="class15">Machine</th>
|
||||||
<th>Description</th>
|
<th class="class45">Description</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="logtablebody"></tbody>
|
<tbody id="logtablebody"></tbody>
|
||||||
@@ -50,6 +51,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
<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/log.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||||
<title>AAS NewGeneration 17092025</title>
|
<title>AAS NewGeneration 17092025</title>
|
||||||
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||||
<link rel="stylesheet" href="assets/css/styles.css">
|
<link rel="stylesheet" href="assets/css/styles.css">
|
||||||
</head>
|
</head>
|
||||||
@@ -13,22 +14,21 @@
|
|||||||
<body>
|
<body>
|
||||||
<section class="position-relative py-4 py-xl-5">
|
<section class="position-relative py-4 py-xl-5">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-5">
|
<div class="row mb-4"></div>
|
||||||
<div class="col-md-8 col-xl-6 text-center mx-auto">
|
<div class="row d-flex justify-content-center mb-7">
|
||||||
<h2>Sign In</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row d-flex justify-content-center">
|
|
||||||
<div class="col-md-6 col-xl-4">
|
<div class="col-md-6 col-xl-4">
|
||||||
<div class="card mb-5 card-login">
|
<div class="card mb-5 card-login">
|
||||||
<div class="card-body d-flex flex-column align-items-center">
|
<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 bg-icon-login">
|
<div class="bs-icon-xl bs-icon-circle bs-icon-primary my-4 bs-icon bg-icon-login"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" class="bi bi-person bg-icon-login">
|
||||||
<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>
|
<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>
|
</svg></div>
|
||||||
<form class="text-center" method="post">
|
<h2 class="mb-3 h-login">Login</h2>
|
||||||
<div class="mb-3"><input class="form-control input-login" type="text" name="username" placeholder="Username"></div>
|
<form class="text-center py-2 bottom-signin" method="post">
|
||||||
<div class="mb-3"><input class="form-control input-login" type="password" name="password" placeholder="Password"></div>
|
<p class="p-login">Username</p>
|
||||||
<div class="mb-3"><button class="btn btn-primary d-block w-100 btn-login" type="submit">Sign In</button></div>
|
<div class="mb-3"><input class="form-control input-login" type="text" name="username" placeholder="Enter your username"></div>
|
||||||
|
<p class="p-login">Password</p>
|
||||||
|
<div class="mb-3"><input class="form-control input-login" type="password" name="password" placeholder="Enter your password"></div>
|
||||||
|
<div class="mb-3 py-2"><button class="btn btn-primary d-block w-100 btn-login" type="submit">Login</button></div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -37,6 +37,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||||
|
<script src="assets/js/bs-init.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -4,8 +4,9 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||||
<title>AAS_NewGen_30Sept25</title>
|
<title>AAS_NewGen_17OKT25</title>
|
||||||
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||||
<link rel="stylesheet" href="assets/css/styles.css">
|
<link rel="stylesheet" href="assets/css/styles.css">
|
||||||
</head>
|
</head>
|
||||||
@@ -41,13 +42,13 @@
|
|||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="col-sm-1">No</th>
|
<th class="class05">No</th>
|
||||||
<th class="col-sm-2">Description</th>
|
<th class="class15">Description</th>
|
||||||
<th class="col-sm-1">Language</th>
|
<th class="class10">Language</th>
|
||||||
<th class="col-sm-1">ANN ID</th>
|
<th class="class10">ANN ID</th>
|
||||||
<th class="col-sm-1">Type</th>
|
<th class="class10">Type</th>
|
||||||
<th class="col-sm-3">Message Details</th>
|
<th class="class35">Message Details</th>
|
||||||
<th class="col-sm-3">Message Tags</th>
|
<th class="class15">Message Tags</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="messagebanktablebody"></tbody>
|
<tbody id="messagebanktablebody"></tbody>
|
||||||
@@ -55,29 +56,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal fade" role="dialog" tabindex="-1" id="messagebankmodal">
|
<div class="modal fade" role="dialog" tabindex="-1" id="messagebankmodal">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog modal-xl" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header bg-header-modal">
|
||||||
<h4 class="modal-title">Add / Edit Message</h4>
|
<h4 class="modal-title">Add / Edit Message</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body bg-modal-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
<div class="col-4 col-sm-4 col-md-4 col-lg-3 col-xl-3">
|
||||||
<p class="text-add">Index</p>
|
<p class="text-add">Index</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input class="w-25 input-add form-control" type="text" id="messageindex" placeholder="Index" readonly=""></div>
|
<div class="col-8 col-sm-8 col-md-8 col-lg-9 col-xl-9"><input class="w-25 input-add form-control" type="text" id="messageindex" placeholder="Index" readonly=""></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
<div class="col-4 col-sm-4 col-md-4 col-lg-3 col-xl-3">
|
||||||
<p class="text-add">Description</p>
|
<p class="text-add">Description</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input type="text" id="messagedescription" class="input-add form-control" placeholder="Description"></div>
|
<div class="col-8 col-sm-8 col-md-8 col-lg-9 col-xl-9"><input type="text" id="messagedescription" class="input-add form-control" placeholder="Description"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
<div class="col-4 col-sm-4 col-md-4 col-lg-3 col-xl-3">
|
||||||
<p class="text-add">Language</p>
|
<p class="text-add">Language</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><select id="messagelanguage" class="input-add form-control">
|
<div class="col-8 col-sm-8 col-md-8 col-lg-9 col-xl-9"><select id="messagelanguage" class="input-add form-control">
|
||||||
<option value="INDONESIA">Indonesia</option>
|
<option value="INDONESIA">Indonesia</option>
|
||||||
<option value="LOCAL">Local</option>
|
<option value="LOCAL">Local</option>
|
||||||
<option value="ENGLISH">English</option>
|
<option value="ENGLISH">English</option>
|
||||||
@@ -87,40 +88,40 @@
|
|||||||
</select></div>
|
</select></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
<div class="col-4 col-sm-4 col-md-4 col-lg-3 col-xl-3">
|
||||||
<p class="text-add">ANN ID</p>
|
<p class="text-add">ANN ID</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input type="number" id="messageannid" class="input-add form-control" min="1" max="100" value="1" step="1" placeholder="Announcement ID"></div>
|
<div class="col-8 col-sm-8 col-md-8 col-lg-9 col-xl-9"><input type="number" id="messageannid" class="input-add form-control" min="1" max="100" value="1" step="1" placeholder="Announcement ID"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
<div class="col-4 col-sm-4 col-md-4 col-lg-3 col-xl-3">
|
||||||
<p class="text-add">Voice Type</p>
|
<p class="text-add">Voice Type</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><select id="messagevoicetype" class="input-add form-control">
|
<div class="col-8 col-sm-8 col-md-8 col-lg-9 col-xl-9"><select id="messagevoicetype" class="input-add form-control">
|
||||||
<option value="VOICE_1">Voice 1</option>
|
<option value="VOICE_1">Voice 1</option>
|
||||||
<option value="VOICE_2">Voice 2</option>
|
<option value="VOICE_2">Voice 2</option>
|
||||||
<option value="VOICE_3">Voice 3</option>
|
<option value="VOICE_3">Voice 3</option>
|
||||||
</select></div>
|
</select></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col bg-light">
|
||||||
<ul class="list-unstyled w-100 h-100" id="messageavailablevariables"></ul>
|
<ul class="list-unstyled w-100 h-100" id="messageavailablevariables"></ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-2">
|
<div class="col-3 col-sm-3 col-md-3 col-lg-2 col-xl-2">
|
||||||
<div class="row"><button class="btn btn-danger" id="btnclearlist" type="button"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" style="font-size: 32;">
|
<div class="row pad-row-btn"><button class="btn btn-round-basic color-remove" data-bs-toggle="tooltip" data-bss-tooltip="" id="btnclearlist" type="button" title="Clear List"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" style="font-size: 32;">
|
||||||
<!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc. -->
|
<!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc. -->
|
||||||
<path d="M256 48a208 208 0 1 1 0 416 208 208 0 1 1 0-416zm0 464A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM175 175c-9.4 9.4-9.4 24.6 0 33.9l47 47-47 47c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l47-47 47 47c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-47-47 47-47c9.4-9.4 9.4-24.6 0-33.9s-24.6-9.4-33.9 0l-47 47-47-47c-9.4-9.4-24.6-9.4-33.9 0z"></path>
|
<path d="M256 48a208 208 0 1 1 0 416 208 208 0 1 1 0-416zm0 464A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM175 175c-9.4 9.4-9.4 24.6 0 33.9l47 47-47 47c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l47-47 47 47c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-47-47 47-47c9.4-9.4 9.4-24.6 0-33.9s-24.6-9.4-33.9 0l-47 47-47-47c-9.4-9.4-24.6-9.4-33.9 0z"></path>
|
||||||
</svg></button></div>
|
</svg></button></div>
|
||||||
<div class="row"><button class="btn btn-warning" id="btnremovefromlist" type="button"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" style="font-size: 32;">
|
<div class="row pad-row-btn"><button class="btn btn-round-basic color-edit" data-bs-toggle="tooltip" data-bss-tooltip="" data-bs-placement="right" id="btnremovefromlist" type="button" title="Remove"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" style="font-size: 32;">
|
||||||
<!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc. -->
|
<!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc. -->
|
||||||
<path d="M48 256a208 208 0 1 1 416 0A208 208 0 1 1 48 256zm464 0A256 256 0 1 0 0 256a256 256 0 1 0 512 0zM217.4 376.9c4.2 4.5 10.1 7.1 16.3 7.1c12.3 0 22.3-10 22.3-22.3V304h96c17.7 0 32-14.3 32-32V240c0-17.7-14.3-32-32-32H256V150.3c0-12.3-10-22.3-22.3-22.3c-6.2 0-12.1 2.6-16.3 7.1L117.5 242.2c-3.5 3.8-5.5 8.7-5.5 13.8s2 10.1 5.5 13.8l99.9 107.1z"></path>
|
<path d="M48 256a208 208 0 1 1 416 0A208 208 0 1 1 48 256zm464 0A256 256 0 1 0 0 256a256 256 0 1 0 512 0zM217.4 376.9c4.2 4.5 10.1 7.1 16.3 7.1c12.3 0 22.3-10 22.3-22.3V304h96c17.7 0 32-14.3 32-32V240c0-17.7-14.3-32-32-32H256V150.3c0-12.3-10-22.3-22.3-22.3c-6.2 0-12.1 2.6-16.3 7.1L117.5 242.2c-3.5 3.8-5.5 8.7-5.5 13.8s2 10.1 5.5 13.8l99.9 107.1z"></path>
|
||||||
</svg></button></div>
|
</svg></button></div>
|
||||||
<div class="row"><button class="btn btn-success" id="btnaddtolist" type="button"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" style="font-size: 32;">
|
<div class="row pad-row-btn"><button class="btn btn-round-basic color-import" data-bs-toggle="tooltip" data-bss-tooltip="" data-bs-placement="right" id="btnaddtolist" type="button" title="Add"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" style="font-size: 32;">
|
||||||
<!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc. -->
|
<!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc. -->
|
||||||
<path d="M464 256A208 208 0 1 1 48 256a208 208 0 1 1 416 0zM0 256a256 256 0 1 0 512 0A256 256 0 1 0 0 256zM294.6 135.1c-4.2-4.5-10.1-7.1-16.3-7.1C266 128 256 138 256 150.3V208H160c-17.7 0-32 14.3-32 32v32c0 17.7 14.3 32 32 32h96v57.7c0 12.3 10 22.3 22.3 22.3c6.2 0 12.1-2.6 16.3-7.1l99.9-107.1c3.5-3.8 5.5-8.7 5.5-13.8s-2-10.1-5.5-13.8L294.6 135.1z"></path>
|
<path d="M464 256A208 208 0 1 1 48 256a208 208 0 1 1 416 0zM0 256a256 256 0 1 0 512 0A256 256 0 1 0 0 256zM294.6 135.1c-4.2-4.5-10.1-7.1-16.3-7.1C266 128 256 138 256 150.3V208H160c-17.7 0-32 14.3-32 32v32c0 17.7 14.3 32 32 32h96v57.7c0 12.3 10 22.3 22.3 22.3c6.2 0 12.1-2.6 16.3-7.1l99.9-107.1c3.5-3.8 5.5-8.7 5.5-13.8s-2-10.1-5.5-13.8L294.6 135.1z"></path>
|
||||||
</svg></button></div>
|
</svg></button></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col bg-light">
|
||||||
<ul class="list-unstyled w-100 h-100" id="messageselectedvariables"></ul>
|
<ul class="list-unstyled w-100 h-100" id="messageselectedvariables"></ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -130,6 +131,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
<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/messagebank.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||||
<title>AAS_NewGen_30Sept25</title>
|
<title>AAS_NewGen_17OKT25</title>
|
||||||
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||||
<link rel="stylesheet" href="assets/css/styles.css">
|
<link rel="stylesheet" href="assets/css/styles.css">
|
||||||
</head>
|
</head>
|
||||||
@@ -101,7 +102,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 pad-card">
|
||||||
<div class="card" id="streamercard-4">
|
<div class="card card-channel" id="streamercard-4">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle05">Channel 05</h4>
|
<h4 class="card-title" id="streamertitle05">Channel 05</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -120,7 +121,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 pad-card">
|
||||||
<div class="card" id="streamercard-5">
|
<div class="card card-channel" id="streamercard-5">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle06">Channel 06</h4>
|
<h4 class="card-title" id="streamertitle06">Channel 06</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -139,7 +140,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 pad-card">
|
||||||
<div class="card" id="streamercard-6">
|
<div class="card card-channel" id="streamercard-6">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle07">Channel 07</h4>
|
<h4 class="card-title" id="streamertitle07">Channel 07</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -158,7 +159,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 pad-card">
|
||||||
<div class="card" id="streamercard-7">
|
<div class="card card-channel" id="streamercard-7">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle08">Channel 08</h4>
|
<h4 class="card-title" id="streamertitle08">Channel 08</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -179,7 +180,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-8">
|
<div class="card card-channel" id="streamercard-8">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle09">Channel 09</h4>
|
<h4 class="card-title" id="streamertitle09">Channel 09</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -198,7 +199,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-9">
|
<div class="card card-channel" id="streamercard-9">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle10">Channel 10</h4>
|
<h4 class="card-title" id="streamertitle10">Channel 10</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -217,7 +218,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-10">
|
<div class="card card-channel" id="streamercard-10">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle11">Channel 11</h4>
|
<h4 class="card-title" id="streamertitle11">Channel 11</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -236,7 +237,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-11">
|
<div class="card card-channel" id="streamercard-11">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle12">Channel 12</h4>
|
<h4 class="card-title" id="streamertitle12">Channel 12</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -257,7 +258,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-12">
|
<div class="card card-channel" id="streamercard-12">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle13">Channel 13</h4>
|
<h4 class="card-title" id="streamertitle13">Channel 13</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -276,7 +277,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-13">
|
<div class="card card-channel" id="streamercard-13">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle14">Channel 14</h4>
|
<h4 class="card-title" id="streamertitle14">Channel 14</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -295,7 +296,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-14">
|
<div class="card card-channel" id="streamercard-14">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle15">Channel 15</h4>
|
<h4 class="card-title" id="streamertitle15">Channel 15</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -314,7 +315,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-15">
|
<div class="card card-channel" id="streamercard-15">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle16">Channel 16</h4>
|
<h4 class="card-title" id="streamertitle16">Channel 16</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -335,7 +336,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-16">
|
<div class="card card-channel" id="streamercard-16">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle17">Channel 17</h4>
|
<h4 class="card-title" id="streamertitle17">Channel 17</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -354,7 +355,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-17">
|
<div class="card card-channel" id="streamercard-17">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle18">Channel 18</h4>
|
<h4 class="card-title" id="streamertitle18">Channel 18</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -373,7 +374,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-18">
|
<div class="card card-channel" id="streamercard-18">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle19">Channel 19</h4>
|
<h4 class="card-title" id="streamertitle19">Channel 19</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -392,7 +393,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-19">
|
<div class="card card-channel" id="streamercard-19">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle20">Channel 20</h4>
|
<h4 class="card-title" id="streamertitle20">Channel 20</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -413,7 +414,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-20">
|
<div class="card card-channel" id="streamercard-20">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle21">Channel 21</h4>
|
<h4 class="card-title" id="streamertitle21">Channel 21</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -432,7 +433,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-21">
|
<div class="card card-channel" id="streamercard-21">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle22">Channel 22</h4>
|
<h4 class="card-title" id="streamertitle22">Channel 22</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -451,7 +452,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-22">
|
<div class="card card-channel" id="streamercard-22">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle23">Channel 23</h4>
|
<h4 class="card-title" id="streamertitle23">Channel 23</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -470,7 +471,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-23">
|
<div class="card card-channel" id="streamercard-23">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle24">Channel 24</h4>
|
<h4 class="card-title" id="streamertitle24">Channel 24</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -491,7 +492,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-24">
|
<div class="card card-channel" id="streamercard-24">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle25">Channel 25</h4>
|
<h4 class="card-title" id="streamertitle25">Channel 25</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -510,7 +511,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-25">
|
<div class="card card-channel" id="streamercard-25">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle26">Channel 26</h4>
|
<h4 class="card-title" id="streamertitle26">Channel 26</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -529,7 +530,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-26">
|
<div class="card card-channel" id="streamercard-26">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle27">Channel 27</h4>
|
<h4 class="card-title" id="streamertitle27">Channel 27</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -548,7 +549,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-27">
|
<div class="card card-channel" id="streamercard-27">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle28">Channel 28</h4>
|
<h4 class="card-title" id="streamertitle28">Channel 28</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -569,7 +570,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-28">
|
<div class="card card-channel" id="streamercard-28">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle29">Channel 29</h4>
|
<h4 class="card-title" id="streamertitle29">Channel 29</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -588,7 +589,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-29">
|
<div class="card card-channel" id="streamercard-29">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle30">Channel 30</h4>
|
<h4 class="card-title" id="streamertitle30">Channel 30</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -607,7 +608,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-30">
|
<div class="card card-channel" id="streamercard-30">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle31">Channel 31</h4>
|
<h4 class="card-title" id="streamertitle31">Channel 31</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -626,7 +627,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-31">
|
<div class="card card-channel" id="streamercard-31">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle32">Channel 32</h4>
|
<h4 class="card-title" id="streamertitle32">Channel 32</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -647,7 +648,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-32">
|
<div class="card card-channel" id="streamercard-32">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle33">Channel 33</h4>
|
<h4 class="card-title" id="streamertitle33">Channel 33</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -666,7 +667,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-33">
|
<div class="card card-channel" id="streamercard-33">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle34">Channel 34</h4>
|
<h4 class="card-title" id="streamertitle34">Channel 34</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -685,7 +686,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-34">
|
<div class="card card-channel" id="streamercard-34">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle35">Channel 35</h4>
|
<h4 class="card-title" id="streamertitle35">Channel 35</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -704,7 +705,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-35">
|
<div class="card card-channel" id="streamercard-35">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle36">Channel 36</h4>
|
<h4 class="card-title" id="streamertitle36">Channel 36</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -725,7 +726,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-36">
|
<div class="card card-channel" id="streamercard-36">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle37">Channel 37</h4>
|
<h4 class="card-title" id="streamertitle37">Channel 37</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -744,7 +745,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-37">
|
<div class="card card-channel" id="streamercard-37">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle38">Channel 38</h4>
|
<h4 class="card-title" id="streamertitle38">Channel 38</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -763,7 +764,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-38">
|
<div class="card card-channel" id="streamercard-38">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle39">Channel 39</h4>
|
<h4 class="card-title" id="streamertitle39">Channel 39</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -782,7 +783,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-39">
|
<div class="card card-channel" id="streamercard-39">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle40">Channel 40</h4>
|
<h4 class="card-title" id="streamertitle40">Channel 40</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -803,7 +804,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-40">
|
<div class="card card-channel" id="streamercard-40">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle41">Channel 41</h4>
|
<h4 class="card-title" id="streamertitle41">Channel 41</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -822,7 +823,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-41">
|
<div class="card card-channel" id="streamercard-41">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle42">Channel 42</h4>
|
<h4 class="card-title" id="streamertitle42">Channel 42</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -841,7 +842,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-42">
|
<div class="card card-channel" id="streamercard-42">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle43">Channel 43</h4>
|
<h4 class="card-title" id="streamertitle43">Channel 43</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -860,7 +861,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-43">
|
<div class="card card-channel" id="streamercard-43">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle44">Channel 44</h4>
|
<h4 class="card-title" id="streamertitle44">Channel 44</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -881,7 +882,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-44">
|
<div class="card card-channel" id="streamercard-44">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle-44">Channel 45</h4>
|
<h4 class="card-title" id="streamertitle-44">Channel 45</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -900,7 +901,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-45">
|
<div class="card card-channel" id="streamercard-45">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle46">Channel 46</h4>
|
<h4 class="card-title" id="streamertitle46">Channel 46</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -919,7 +920,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-46">
|
<div class="card card-channel" id="streamercard-46">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle47">Channel 47</h4>
|
<h4 class="card-title" id="streamertitle47">Channel 47</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -938,7 +939,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-47">
|
<div class="card card-channel" id="streamercard-47">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle48">Channel 48</h4>
|
<h4 class="card-title" id="streamertitle48">Channel 48</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -959,7 +960,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-48">
|
<div class="card card-channel" id="streamercard-48">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle49">Channel 49</h4>
|
<h4 class="card-title" id="streamertitle49">Channel 49</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -978,7 +979,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-49">
|
<div class="card card-channel" id="streamercard-49">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle50">Channel 50</h4>
|
<h4 class="card-title" id="streamertitle50">Channel 50</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -997,7 +998,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-50">
|
<div class="card card-channel" id="streamercard-50">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle51">Channel 51</h4>
|
<h4 class="card-title" id="streamertitle51">Channel 51</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -1016,7 +1017,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-51">
|
<div class="card card-channel" id="streamercard-51">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle52">Channel 52</h4>
|
<h4 class="card-title" id="streamertitle52">Channel 52</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -1037,7 +1038,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-52">
|
<div class="card card-channel" id="streamercard-52">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle53">Channel 53</h4>
|
<h4 class="card-title" id="streamertitle53">Channel 53</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -1056,7 +1057,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-53">
|
<div class="card card-channel" id="streamercard-53">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle54">Channel 54</h4>
|
<h4 class="card-title" id="streamertitle54">Channel 54</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -1075,7 +1076,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-54">
|
<div class="card card-channel" id="streamercard-54">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle55">Channel 55</h4>
|
<h4 class="card-title" id="streamertitle55">Channel 55</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -1094,7 +1095,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-55">
|
<div class="card card-channel" id="streamercard-55">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle56">Channel 56</h4>
|
<h4 class="card-title" id="streamertitle56">Channel 56</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -1115,7 +1116,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-56">
|
<div class="card card-channel" id="streamercard-56">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle57">Channel 57</h4>
|
<h4 class="card-title" id="streamertitle57">Channel 57</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -1134,7 +1135,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-57">
|
<div class="card card-channel" id="streamercard-57">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle58">Channel 58</h4>
|
<h4 class="card-title" id="streamertitle58">Channel 58</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -1153,7 +1154,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-58">
|
<div class="card card-channel" id="streamercard-58">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle59">Channel 59</h4>
|
<h4 class="card-title" id="streamertitle59">Channel 59</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -1172,7 +1173,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-59">
|
<div class="card card-channel" id="streamercard-59">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle60">Channel 60</h4>
|
<h4 class="card-title" id="streamertitle60">Channel 60</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -1193,7 +1194,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-60">
|
<div class="card card-channel" id="streamercard-60">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle61">Channel 61</h4>
|
<h4 class="card-title" id="streamertitle61">Channel 61</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -1212,7 +1213,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-61">
|
<div class="card card-channel" id="streamercard-61">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle62">Channel 62</h4>
|
<h4 class="card-title" id="streamertitle62">Channel 62</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -1231,7 +1232,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-62">
|
<div class="card card-channel" id="streamercard-62">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle63">Channel 63</h4>
|
<h4 class="card-title" id="streamertitle63">Channel 63</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -1250,7 +1251,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 col-xxl-3 pad-card">
|
||||||
<div class="card" id="streamercard-63">
|
<div class="card card-channel" id="streamercard-63">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" id="streamertitle64">Channel 64</h4>
|
<h4 class="card-title" id="streamertitle64">Channel 64</h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -1262,7 +1263,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="card-text" id="streamerstatus64">Status : Idle</p>
|
<p class="card-text" id="streamerstatus64">Status : Idle</p>
|
||||||
<div class="progress" id="streamervu-63">
|
<div class="progress" id="streamervu64">
|
||||||
<div class="progress-bar" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100" style="width: 50%;">50%</div>
|
<div class="progress-bar" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100" style="width: 50%;">50%</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1272,17 +1273,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-item pad-accordion">
|
<div class="accordion-item pad-accordion pad-2">
|
||||||
<h2 class="accordion-header" role="tab"><button class="accordion-button collapsed bg-heading2" type="button" data-bs-toggle="collapse" data-bs-target="#accordion-1 .item-2" aria-expanded="false" aria-controls="accordion-1 .item-2">Paging Queue</button></h2>
|
<h2 class="accordion-header" role="tab"><button class="accordion-button collapsed bg-heading2" type="button" data-bs-toggle="collapse" data-bs-target="#accordion-1 .item-2" aria-expanded="false" aria-controls="accordion-1 .item-2">Paging Queue</button></h2>
|
||||||
<div class="accordion-collapse collapse item-2" role="tabpanel" data-bs-parent="#accordion-1">
|
<div class="accordion-collapse collapse item-2 pad-2" role="tabpanel" data-bs-parent="#accordion-1">
|
||||||
<div class="accordion-body">
|
<div class="accordion-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Index</th>
|
<th class="class05">Index</th>
|
||||||
<th>Description</th>
|
<th class="class10">Date Time</th>
|
||||||
|
<th class="class15">Source</th>
|
||||||
|
<th class="class10">Type</th>
|
||||||
|
<th class="class30">Message</th>
|
||||||
|
<th class="class30">Broadcast Zones</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="pagingqueuetable"></tbody>
|
<tbody id="pagingqueuetable"></tbody>
|
||||||
@@ -1291,6 +1296,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col"><button class="btn w-100 pad-button btn-round-basic color-add" id="clearpagingqueue" type="button">Clear Queue</button></div>
|
<div class="col"><button class="btn w-100 pad-button btn-round-basic color-add" id="clearpagingqueue" type="button">Clear Queue</button></div>
|
||||||
|
<div class="col"><button class="btn w-100 pad-button btn-round-basic color-remove" id="removepagingqueue" type="button">Remove Queue</button></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1304,8 +1310,12 @@
|
|||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Index</th>
|
<th class="class05">Index</th>
|
||||||
<th>Description</th>
|
<th class="class10">Date Time</th>
|
||||||
|
<th class="class15">Source</th>
|
||||||
|
<th class="class10">Type</th>
|
||||||
|
<th class="class30">Message</th>
|
||||||
|
<th class="class30">Broadcast Zones</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="automaticqueuetable"></tbody>
|
<tbody id="automaticqueuetable"></tbody>
|
||||||
@@ -1314,12 +1324,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col"><button class="btn w-100 pad-button btn-round-basic color-add" id="clearautomatictable" type="button">Clear Queue</button></div>
|
<div class="col"><button class="btn w-100 pad-button btn-round-basic color-add" id="clearautomatictable" type="button">Clear Queue</button></div>
|
||||||
|
<div class="col"><button class="btn w-100 pad-button btn-round-basic color-remove" id="removeautomatictable" type="button">Remove Queue</button></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||||
|
<script src="assets/js/bs-init.js"></script>
|
||||||
|
<script src="assets/js/overview.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -4,8 +4,9 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||||
<title>AAS_NewGen_30Sept25</title>
|
<title>AAS_NewGen_17OKT25</title>
|
||||||
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||||
<link rel="stylesheet" href="assets/css/styles.css">
|
<link rel="stylesheet" href="assets/css/styles.css">
|
||||||
</head>
|
</head>
|
||||||
@@ -16,7 +17,70 @@
|
|||||||
<h2 style="text-align: center;">Setting</h2>
|
<h2 style="text-align: center;">Setting</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<div class="card card-setting">
|
||||||
|
<div class="card-body">
|
||||||
|
<h4 class="card-title"><strong>Upload Soundbank</strong></h4>
|
||||||
|
<hr>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-2 col-sm-2 col-md-2 col-lg-2 col-xl-2"><label class="col-form-label">Path</label></div>
|
||||||
|
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"><input class="w-100 form-control" type="text" id="setting_path"></div>
|
||||||
|
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-2"><button class="btn w-100 pad-button btn-round-basic color-add" id="save_directory" type="button">Save Directory</button></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6 col-sm-2 col-md-2 col-lg-2 col-xl-2"><label class="col-form-label">Category</label></div>
|
||||||
|
<div class="col-6 col-sm-10 col-md-2 col-lg-2 col-xl-2"><select id="setting_category" class="input-add form-select"></select></div>
|
||||||
|
<div class="col-6 col-sm-2 col-md-2 col-lg-2 col-xl-2"><label class="col-form-label">Language</label></div>
|
||||||
|
<div class="col-6 col-sm-10 col-md-2 col-lg-2 col-xl-2"><select id="setting_language" class="input-add form-select"></select></div>
|
||||||
|
<div class="col-6 col-sm-2 col-md-2 col-lg-2 col-xl-2"><label class="col-form-label">Voice</label></div>
|
||||||
|
<div class="col-6 col-sm-10 col-md-2 col-lg-2 col-xl-2"><select id="setting_voice" class="input-add form-select"></select></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-sm-12 col-md-6 col-lg-6 col-xl-6">
|
||||||
|
<div class="bg-white w-100" id="drop-area" multiple=""><input type="file" id="file-input"><label class="form-label d">Drop files here or click to select</label></div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-sm-12 col-md-6 col-lg-6 col-xl-6">
|
||||||
|
<div class="row"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row py-5">
|
||||||
|
<div class="col">
|
||||||
|
<div class="card card-setting">
|
||||||
|
<div class="card-body pad-accordion">
|
||||||
|
<h4 class="card-title"><strong>FIS CODE</strong></h4>
|
||||||
|
<hr>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-2 col-sm-2 col-md-2 col-lg-2 col-xl-2"><label class="col-form-label">GOP</label></div>
|
||||||
|
<div class="col-10 col-sm-10 col-md-10 col-lg-10 col-xl-10"><select id="input_GOP" class="input-add form-select"></select></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-2 col-sm-2 col-md-2 col-lg-2 col-xl-2"><label class="col-form-label">GBP</label></div>
|
||||||
|
<div class="col-10 col-sm-10 col-md-10 col-lg-10 col-xl-10"><select id="input_GBP" class="input-add form-select"></select></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-2 col-sm-2 col-md-2 col-lg-2 col-xl-2"><label class="col-form-label">GFC</label></div>
|
||||||
|
<div class="col-10 col-sm-10 col-md-10 col-lg-10 col-xl-10"><select id="input_GFC" class="input-add form-select"></select></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-2 col-sm-2 col-md-2 col-lg-2 col-xl-2"><label class="col-form-label">FLD</label></div>
|
||||||
|
<div class="col-10 col-sm-10 col-md-10 col-lg-10 col-xl-10"><select id="input_FLD" class="input-add form-select"></select></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-2 col-sm-2 col-md-2 col-lg-2 col-xl-2"><label class="col-form-label"></label></div>
|
||||||
|
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-2"><button class="btn w-100 pad-button btn-round-basic color-add" type="button">Save</button></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||||
|
<script src="assets/js/bs-init.js"></script>
|
||||||
|
<script src="assets/js/dragdrop.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -4,8 +4,9 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||||
<title>AAS_NewGen_30Sept25</title>
|
<title>AAS_NewGen_17OKT25</title>
|
||||||
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||||
<link rel="stylesheet" href="assets/css/styles.css">
|
<link rel="stylesheet" href="assets/css/styles.css">
|
||||||
</head>
|
</head>
|
||||||
@@ -41,13 +42,13 @@
|
|||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="col-sm-1">No</th>
|
<th class="class05">No</th>
|
||||||
<th>Description</th>
|
<th class="class20">Description</th>
|
||||||
<th class="col-sm-1">TAG</th>
|
<th class="class10">TAG</th>
|
||||||
<th class="col-sm-1">Category</th>
|
<th class="class15">Category</th>
|
||||||
<th class="col-sm-1">Language</th>
|
<th class="class15">Language</th>
|
||||||
<th class="col-sm-1">Type</th>
|
<th class="class10">Type</th>
|
||||||
<th class="col-sm-3">Filename</th>
|
<th class="class25">Filename</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="soundbanktablebody"></tbody>
|
<tbody id="soundbanktablebody"></tbody>
|
||||||
@@ -57,10 +58,10 @@
|
|||||||
<div class="modal fade border-0" role="dialog" tabindex="-1" id="soundbankmodal">
|
<div class="modal fade border-0" role="dialog" tabindex="-1" id="soundbankmodal">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header show">
|
<div class="modal-header show bg-header-modal">
|
||||||
<h4 class="modal-title align-content-center">Add / Edit Soundbank</h4>
|
<h4 class="modal-title align-content-center">Add / Edit Soundbank</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body bg-modal-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||||
<p class="text-add">Index</p>
|
<p class="text-add">Index</p>
|
||||||
@@ -97,11 +98,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><select id="modalvoicetype" class="input-add form-select"></select></div>
|
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><select id="modalvoicetype" class="input-add form-select"></select></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row" style="height: 100px;">
|
<div class="row">
|
||||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||||
<p class="text-add">Path</p>
|
<p class="text-add">Path</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><select class="w-100 js-example-basic-single input-add form-select" id="modalpath" name="modalpath"></select></div>
|
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><select id="modalpath" class="input-add form-select cw-100" name="modalpath" data-control="select2"></select></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer"><button class="btn btn-round-basic color-edit class25" id="soundbankclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-round-basic color-add class25" id="soundbanksave" type="button">Save</button></div>
|
<div class="modal-footer"><button class="btn btn-round-basic color-edit class25" id="soundbankclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-round-basic color-add class25" id="soundbanksave" type="button">Save</button></div>
|
||||||
@@ -109,6 +110,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
<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/soundbank.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||||
<title>AAS_NewGen_30Sept25</title>
|
<title>AAS_NewGen_17OKT25</title>
|
||||||
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||||
<link rel="stylesheet" href="assets/css/styles.css">
|
<link rel="stylesheet" href="assets/css/styles.css">
|
||||||
</head>
|
</head>
|
||||||
@@ -29,6 +30,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||||
|
<script src="assets/js/bs-init.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -4,8 +4,9 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||||
<title>AAS_NewGen_30Sept25</title>
|
<title>AAS_NewGen_17OKT25</title>
|
||||||
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||||
<link rel="stylesheet" href="assets/css/styles.css">
|
<link rel="stylesheet" href="assets/css/styles.css">
|
||||||
</head>
|
</head>
|
||||||
@@ -41,14 +42,15 @@
|
|||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="col-sm-1">No</th>
|
<th class="class05">No</th>
|
||||||
<th class="col-sm-2">Description</th>
|
<th class="class15">Description</th>
|
||||||
<th class="col-sm-1">Day</th>
|
<th class="class15">Day</th>
|
||||||
<th class="col-sm-1">Time</th>
|
<th class="class10">Time</th>
|
||||||
<th class="col-sm-2">Sound Path</th>
|
<th class="class15">Sound Path</th>
|
||||||
<th class="col-sm-1">Repeat</th>
|
<th class="class10">Repeat</th>
|
||||||
<th class="col-sm-1">Enable</th>
|
<th class="class05">Enable</th>
|
||||||
<th>Broadcast Zones</th>
|
<th class="class15">Broadcast Zones</th>
|
||||||
|
<th class="class10">Language</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="schedulebanktablebody"></tbody>
|
<tbody id="schedulebanktablebody"></tbody>
|
||||||
@@ -58,10 +60,10 @@
|
|||||||
<div class="modal fade" role="dialog" tabindex="-1" id="schedulemodal">
|
<div class="modal fade" role="dialog" tabindex="-1" id="schedulemodal">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header bg-header-modal">
|
||||||
<h4 class="modal-title">Add / Edit Schedule</h4>
|
<h4 class="modal-title">Add / Edit Schedule</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body bg-modal-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||||
<p class="text-add">Index</p>
|
<p class="text-add">Index</p>
|
||||||
@@ -86,37 +88,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row pad-day">
|
<div class="row pad-day">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="form-check"><input class="form-check-input" type="radio" id="schedulesunday" name="dayselection" value="Sunday"><label class="form-check-label" for="formCheck-8">Sunday</label></div>
|
<div class="form-check"><input class="form-check-input" type="radio" id="scheduleweekly"><label class="form-check-label" for="formCheck-1">Weekly</label></div><select class="w-100 input-add form-select" id="weeklyselect">
|
||||||
</div>
|
<optgroup label="This is a group">
|
||||||
</div>
|
<option value="12" selected="">This is item 1</option>
|
||||||
<div class="row pad-day">
|
<option value="13">This is item 2</option>
|
||||||
<div class="col">
|
<option value="14">This is item 3</option>
|
||||||
<div class="form-check"><input class="form-check-input" type="radio" id="schedulemonday" name="dayselection" value="Monday"><label class="form-check-label" for="formCheck-7">Monday</label></div>
|
</optgroup>
|
||||||
</div>
|
</select>
|
||||||
</div>
|
|
||||||
<div class="row pad-day">
|
|
||||||
<div class="col">
|
|
||||||
<div class="form-check"><input class="form-check-input" type="radio" id="scheduletuesday" name="dayselection" value="Tuesday"><label class="form-check-label" for="formCheck-6">Tuesday</label></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row pad-day">
|
|
||||||
<div class="col">
|
|
||||||
<div class="form-check"><input class="form-check-input" type="radio" id="schedulewednesday" name="dayselection" value="Wednesday"><label class="form-check-label" for="formCheck-5">Wednesday</label></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row pad-day">
|
|
||||||
<div class="col">
|
|
||||||
<div class="form-check"><input class="form-check-input" type="radio" id="schedulethursday" name="dayselection" value="Thursday"><label class="form-check-label" for="formCheck-4">Thursday</label></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row pad-day">
|
|
||||||
<div class="col">
|
|
||||||
<div class="form-check"><input class="form-check-input" type="radio" id="schedulefriday" name="dayselection" value="Friday"><label class="form-check-label" for="formCheck-3">Friday</label></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row pad-day">
|
|
||||||
<div class="col">
|
|
||||||
<div class="form-check"><input class="form-check-input" type="radio" id="schedulesaturday" name="dayselection" value="Saturday"><label class="form-check-label" for="formCheck-2">Saturday</label></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -133,12 +111,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="row w-100 h-100">
|
<div class="row w-100 h-100">
|
||||||
<div class="col-4 col-sm-4 col-md-4 col-lg-3 col-xl-3"><input type="number" id="schedulehour" class="input-add form-control class100" value="0" min="0" max="23" step="1"></div>
|
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"><input type="number" id="schedulehour" class="input-add form-control class100" value="0" min="0" max="23" step="1"></div>
|
||||||
<div class="col-2 col-sm-2 col-md-2 col-lg-3 col-xl-3">
|
<div class="col-2 col-sm-2 col-md-2 col-lg-2 col-xl-2">
|
||||||
<p class="pad-time">(H)</p>
|
<p class="pad-time">(H)</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4 col-sm-4 col-md-4 col-lg-3 col-xl-3"><input class="w-100 input-add form-control" type="number" id="scheduleminute" value="0" min="0" max="59" step="1"></div>
|
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"><input class="w-100 input-add form-control" type="number" id="scheduleminute" value="0" min="0" max="59" step="1"></div>
|
||||||
<div class="col-2 col-sm-2 col-md-2 col-lg-3 col-xl-3">
|
<div class="col-2 col-sm-2 col-md-2 col-lg-2 col-xl-2">
|
||||||
<p class="pad-time">(M)</p>
|
<p class="pad-time">(M)</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -146,9 +124,25 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||||
<p class="text-add">Sound Path</p>
|
<p class="text-add">Message</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><select id="schedulemessage" class="input-add form-select"></select></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||||
|
<p class="text-add">Language</p>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="row pad-day">
|
||||||
|
<div class="col"><select class="w-100 input-add form-select" id="languageselect">
|
||||||
|
<optgroup label="This is a group">
|
||||||
|
<option value="12" selected="">This is item 1</option>
|
||||||
|
<option value="13">This is item 2</option>
|
||||||
|
<option value="14">This is item 3</option>
|
||||||
|
</optgroup>
|
||||||
|
</select></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><select id="schedulesoundpath" class="input-add form-control"></select></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||||
@@ -166,7 +160,13 @@
|
|||||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||||
<p class="text-add">Broadcast Zones</p>
|
<p class="text-add">Broadcast Zones</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8 border" id="schedulezones"></div>
|
<div class="col-8 col-sm-8 col-md-8 col-lg-9 col-xl-8 border"><select class="w-100 input-add form-select" id="schedulezones">
|
||||||
|
<optgroup label="This is a group">
|
||||||
|
<option value="12" selected="">This is item 1</option>
|
||||||
|
<option value="13">This is item 2</option>
|
||||||
|
<option value="14">This is item 3</option>
|
||||||
|
</optgroup>
|
||||||
|
</select></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer"><button class="btn btn-round-basic color-edit class25" id="scheduleclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-round-basic class25 color-add" id="schedulesave" type="button">Save</button></div>
|
<div class="modal-footer"><button class="btn btn-round-basic color-edit class25" id="scheduleclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-round-basic class25 color-add" id="schedulesave" type="button">Save</button></div>
|
||||||
@@ -174,6 +174,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
<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/schedulebank.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||||
<title>AAS_NewGen_30Sept25</title>
|
<title>AAS_NewGen_17OKT25</title>
|
||||||
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||||
<link rel="stylesheet" href="assets/css/styles.css">
|
<link rel="stylesheet" href="assets/css/styles.css">
|
||||||
</head>
|
</head>
|
||||||
@@ -41,12 +42,13 @@
|
|||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="col-sm-1">No</th>
|
<th class="class05">No</th>
|
||||||
<th class="col-sm-1">Username</th>
|
<th class="class10">Username</th>
|
||||||
<th class="col-sm-1">Location</th>
|
<th class="class15">Location</th>
|
||||||
<th class="col">Soundbank</th>
|
<th class="class15">Airline</th>
|
||||||
<th class="col-sm-2">Messagebank</th>
|
<th class="class15">City</th>
|
||||||
<th class="col-sm-2">Broadcast Zones</th>
|
<th class="class20">Messagebank</th>
|
||||||
|
<th class="class20">Broadcast Zones</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="usertablebody"></tbody>
|
<tbody id="usertablebody"></tbody>
|
||||||
@@ -56,10 +58,10 @@
|
|||||||
<div class="modal fade border-0" role="dialog" tabindex="-1" id="addmodal">
|
<div class="modal fade border-0" role="dialog" tabindex="-1" id="addmodal">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header show">
|
<div class="modal-header text-start show bg-header-modal">
|
||||||
<h4 class="modal-title align-content-center">Add / Edit User</h4>
|
<h4 class="modal-title text-center align-content-center">Add / Edit User</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body bg-modal-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||||
<p class="text-add">Index</p>
|
<p class="text-add">Index</p>
|
||||||
@@ -86,21 +88,53 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||||
<p class="text-add">Sound Bank</p>
|
<p class="text-add">Location</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input type="text" id="modallocation" class="input-add form-control"></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||||
|
<p class="text-add">Airline Tags</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-8"><input type="text" id="modalairlinetags" class="form-control input-add"></div>
|
||||||
|
<div class="col-4"><button class="btn w-100 btn-select btn-round-basic color-import" id="btnShowSoundbankModal" type="button">Select</button></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||||
|
<p class="text-add">City Tags</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-8"><input type="text" id="modalcitytags" class="form-control input-add"></div>
|
||||||
|
<div class="col-4"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input type="text" id="modalsoundbank" class="input-add"><button class="btn btn-primary" id="btnShowSoundbankModal" type="button">Select</button></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||||
<p class="text-add">Message Bank</p>
|
<p class="text-add">Message Bank</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input type="text" id="modalmessagebank" class="input-add"><button class="btn btn-primary" id="btnShowMessagebankModal" type="button">Select</button></div>
|
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-8"><input type="text" id="modalmessagebank" class="form-control input-add"></div>
|
||||||
|
<div class="col-4"><button class="btn w-100 btn-round-basic color-import" id="btnShowMessagebankModal" type="button">Select</button></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row" style="height: 100px;">
|
<div class="row">
|
||||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||||
<p class="text-add">Broadcast Zones</p>
|
<p class="text-add">Broadcast Zones</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input type="text" id="modalbroadcastzones" class="input-add" name="modalpath"><button class="btn btn-primary" id="btnShowBroaadcastZoneModal" type="button">Select</button></div>
|
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input type="text" id="modalbroadcastzones" class="form-control input-add" name="modalpath"></div>
|
||||||
|
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"><button class="btn w-100 btn-round-basic color-import" id="btnShowBroaadcastZoneModal" type="button">Select</button></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer"><button class="btn btn-round-basic color-edit class25" id="usermanagementclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-round-basic color-add class25" id="usermanagementsave" type="button">Save</button></div>
|
<div class="modal-footer"><button class="btn btn-round-basic color-edit class25" id="usermanagementclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-round-basic color-add class25" id="usermanagementsave" type="button">Save</button></div>
|
||||||
@@ -110,43 +144,61 @@
|
|||||||
<div class="modal fade" role="dialog" tabindex="-1" id="soundbankmodal">
|
<div class="modal fade" role="dialog" tabindex="-1" id="soundbankmodal">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header form-control input-add">
|
||||||
<h4 class="modal-title">Sound Bank Selection</h4>
|
<h4 class="modal-title">Sound Bank Selection</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body bg-modal-body">
|
||||||
<p>(mon, ganti list checkbox, dengan id=soundbankselection</p>
|
<div class="accordion" role="tablist" id="accordion-1">
|
||||||
|
<div class="accordion-item pad-accordion">
|
||||||
|
<h2 class="accordion-header" role="tab"><button class="accordion-button bg-heading1" type="button" data-bs-toggle="collapse" data-bs-target="#accordion-1 .item-1" aria-expanded="true" aria-controls="accordion-1 .item-1">Airline</button></h2>
|
||||||
|
<div class="accordion-collapse collapse show item-1" role="tabpanel" data-bs-parent="#accordion-1">
|
||||||
|
<div class="accordion-body">
|
||||||
|
<ul id="airlinelist"></ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="accordion-item pad-accordion">
|
||||||
|
<h2 class="accordion-header" role="tab"><button class="accordion-button collapsed bg-heading2" type="button" data-bs-toggle="collapse" data-bs-target="#accordion-1 .item-2" aria-expanded="false" aria-controls="accordion-1 .item-2">City</button></h2>
|
||||||
|
<div class="accordion-collapse collapse item-2" role="tabpanel" data-bs-parent="#accordion-1">
|
||||||
|
<div class="accordion-body">
|
||||||
|
<ul id="citylist"></ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer"><button class="btn btn-light" id="soundbankselectionclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-primary" id="soundbankselectionsave" type="button">Save</button></div>
|
<div class="modal-footer"><button class="btn btn-round-basic color-edit class25" id="soundbankselectionclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-round-basic color-add class25" id="soundbankselectionsave" type="button">Save</button></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal fade" role="dialog" tabindex="-1" id="broadcastzonemodal">
|
<div class="modal fade" role="dialog" tabindex="-1" id="broadcastzonemodal">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header form-control input-add">
|
||||||
<h4 class="modal-title">Broadcast Zones Selection</h4>
|
<h4 class="modal-title">Broadcast Zones Selection</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body bg-modal-body">
|
||||||
<p>(mon, ganti list checkbox, dengan id=broadcastzoneselection</p>
|
<ul id="broadcastzonelist"></ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer"><button class="btn btn-light" id="broadcastzoneselectionclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-primary" id="broadcastzoneselectionsave" type="button">Save</button></div>
|
<div class="modal-footer"><button class="btn btn-round-basic color-edit class25" id="broadcastzoneselectionclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-round-basic color-add class25" id="broadcastzoneselectionsave" type="button">Save</button></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal fade" role="dialog" tabindex="-1" id="messagebankmodal">
|
<div class="modal fade" role="dialog" tabindex="-1" id="messagebankmodal">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header form-control input-add">
|
||||||
<h4 class="modal-title">Message Bank Selection</h4>
|
<h4 class="modal-title">Message Bank Selection</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body bg-modal-body">
|
||||||
<p>(mon, ganti list checkbox, dengan id=messagebankselection</p>
|
<ul id="messagebanklist"></ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer"><button class="btn btn-light" id="messagebankselectionclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-primary" id="messagebankselectionsave" type="button">Save</button></div>
|
<div class="modal-footer"><button class="btn btn-round-basic color-edit class25" id="messagebankselectionclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-round-basic color-add class25" id="messagebankselectionsave" type="button">Save</button></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||||
|
<script src="assets/js/bs-init.js"></script>
|
||||||
<script src="assets/js/usermanagement.js"></script>
|
<script src="assets/js/usermanagement.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
42
src/Main.kt
42
src/Main.kt
@@ -7,6 +7,7 @@ import barix.TCP_Barix_Command_Server
|
|||||||
import codes.Somecodes
|
import codes.Somecodes
|
||||||
import com.sun.jna.Platform
|
import com.sun.jna.Platform
|
||||||
import commandServer.TCP_Android_Command_Server
|
import commandServer.TCP_Android_Command_Server
|
||||||
|
import content.Category
|
||||||
import content.Language
|
import content.Language
|
||||||
import content.VoiceType
|
import content.VoiceType
|
||||||
import database.Log
|
import database.Log
|
||||||
@@ -21,6 +22,7 @@ import org.tinylog.provider.ProviderRegistry
|
|||||||
import oshi.util.GlobalConfig
|
import oshi.util.GlobalConfig
|
||||||
import web.WebApp
|
import web.WebApp
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Paths
|
||||||
import kotlin.concurrent.fixedRateTimer
|
import kotlin.concurrent.fixedRateTimer
|
||||||
import kotlin.io.path.absolutePathString
|
import kotlin.io.path.absolutePathString
|
||||||
|
|
||||||
@@ -29,7 +31,7 @@ lateinit var audioPlayer: AudioPlayer
|
|||||||
val StreamerOutputs: MutableMap<String, BarixConnection> = HashMap()
|
val StreamerOutputs: MutableMap<String, BarixConnection> = HashMap()
|
||||||
lateinit var udpreceiver: UDPReceiver
|
lateinit var udpreceiver: UDPReceiver
|
||||||
lateinit var tcpreceiver: TCPReceiver
|
lateinit var tcpreceiver: TCPReceiver
|
||||||
const val version = "0.0.2 (23/09/2025)"
|
const val version = "0.0.8 (16/10/2025)"
|
||||||
// AAS 64 channels
|
// AAS 64 channels
|
||||||
const val max_channel = 64
|
const val max_channel = 64
|
||||||
|
|
||||||
@@ -52,11 +54,17 @@ val contentCache = ContentCache()
|
|||||||
* Create necessary folders if not exist
|
* Create necessary folders if not exist
|
||||||
*/
|
*/
|
||||||
fun folder_preparation(){
|
fun folder_preparation(){
|
||||||
|
// sementara diset begini, nanti pake config file
|
||||||
|
Somecodes.Soundbank_directory = Paths.get("c:\\soundbank")
|
||||||
Files.createDirectories(Somecodes.SoundbankResult_directory)
|
Files.createDirectories(Somecodes.SoundbankResult_directory)
|
||||||
Files.createDirectories(Somecodes.PagingResult_directory)
|
Files.createDirectories(Somecodes.PagingResult_directory)
|
||||||
Files.createDirectories(Somecodes.Soundbank_directory)
|
Files.createDirectories(Somecodes.Soundbank_directory)
|
||||||
Somecodes.Soundbank_Languages_directory.forEach {
|
Language.entries.forEach { language ->
|
||||||
Files.createDirectories(it)
|
VoiceType.entries.forEach { voice ->
|
||||||
|
Category.entries.forEach { category ->
|
||||||
|
Files.createDirectories(Somecodes.SoundbankDirectory(language, voice, category) )
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -159,29 +167,49 @@ fun main() {
|
|||||||
|
|
||||||
val barixserver = TCP_Barix_Command_Server()
|
val barixserver = TCP_Barix_Command_Server()
|
||||||
barixserver.StartTcpServer { cmd ->
|
barixserver.StartTcpServer { cmd ->
|
||||||
//Logger.info { cmd }
|
val _tcp = barixserver.getSocket(cmd.ipaddress)
|
||||||
val _streamer = StreamerOutputs[cmd.ipaddress]
|
val _streamer = StreamerOutputs[cmd.ipaddress]
|
||||||
val _sc = db.soundchannelDB.List.find { it.ip == cmd.ipaddress }
|
val _sc = db.soundchannelDB.List.find { it.ip == cmd.ipaddress }
|
||||||
if (_streamer == null) {
|
if (_streamer == null) {
|
||||||
// belum create BarixConnection untuk ipaddress ini
|
// belum create BarixConnection untuk ipaddress ini
|
||||||
//Logger.info { "New Streamer Output connection from ${cmd.ipaddress}" }
|
//Logger.info { "New Streamer Output connection from ${cmd.ipaddress}" }
|
||||||
if (_sc != null) {
|
if (_sc != null) {
|
||||||
|
|
||||||
val _bc = BarixConnection(_sc.index, _sc.channel, cmd.ipaddress)
|
val _bc = BarixConnection(_sc.index, _sc.channel, cmd.ipaddress)
|
||||||
_bc.vu = cmd.vu
|
// cmd.vu 0 - 32767, kita convert ke 0 - 100
|
||||||
|
_bc.vu = ((1.0 * cmd.vu / 32767.0)* 100.0).toInt()
|
||||||
_bc.bufferRemain = cmd.buffremain
|
_bc.bufferRemain = cmd.buffremain
|
||||||
_bc.statusData = cmd.statusdata
|
_bc.statusData = cmd.statusdata
|
||||||
|
_bc.commandsocket = _tcp
|
||||||
|
|
||||||
StreamerOutputs[cmd.ipaddress] = _bc
|
StreamerOutputs[cmd.ipaddress] = _bc
|
||||||
Logger.info { "Created new Streamer Output for channel ${_sc.channel} with IP ${cmd.ipaddress}" }
|
Logger.info { "Created new Streamer Output for channel ${_sc.channel} with IP ${cmd.ipaddress}" }
|
||||||
} else Logger.warn { "soundChannelDB doesn't have soundchannel with IP ${cmd.ipaddress}" }
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// sudah ada, update data
|
// sudah ada, update data
|
||||||
if (_sc != null && _sc.channel != _streamer.channel) {
|
if (_sc != null && _sc.channel != _streamer.channel) {
|
||||||
_streamer.channel = _sc.channel
|
_streamer.channel = _sc.channel
|
||||||
}
|
}
|
||||||
_streamer.vu = cmd.vu
|
// cmd.vu 0 - 32767, kita convert ke 0 - 100
|
||||||
|
_streamer.vu = ((1.0 * cmd.vu / 32767.0)* 100.0).toInt()
|
||||||
_streamer.bufferRemain = cmd.buffremain
|
_streamer.bufferRemain = cmd.buffremain
|
||||||
_streamer.statusData = cmd.statusdata
|
_streamer.statusData = cmd.statusdata
|
||||||
|
|
||||||
|
// cek apakah koneksi TCP nya ganti
|
||||||
|
if (_streamer.commandsocket == null) {
|
||||||
|
_streamer.commandsocket = _tcp
|
||||||
|
} else {
|
||||||
|
if (_streamer.commandsocket != _tcp) {
|
||||||
|
// ganti koneksi
|
||||||
|
try {
|
||||||
|
_streamer.commandsocket?.close()
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
Logger.error(ex) { "Error closing previous TCP command socket for ${cmd.ipaddress}" }
|
||||||
|
}
|
||||||
|
_streamer.commandsocket = _tcp
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import audio.AudioFileInfo
|
import audio.AudioFileInfo
|
||||||
|
import barix.BarixConnection
|
||||||
import codes.Result_GetSoundbankFiles
|
import codes.Result_GetSoundbankFiles
|
||||||
import codes.Somecodes.Companion.Get_ANN_ID
|
import codes.Somecodes.Companion.Get_ANN_ID
|
||||||
import codes.Somecodes.Companion.IsAlphabethic
|
import codes.Somecodes.Companion.IsAlphabethic
|
||||||
@@ -89,6 +90,25 @@ class MainExtension01 {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find BarixConnection by BroadcastZones description
|
||||||
|
* @param zonename BroadcastZones description
|
||||||
|
* @return BarixConnection if found, null if not found or invalid
|
||||||
|
*/
|
||||||
|
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 ip = sc?.ip ?: ""
|
||||||
|
if (ValidIPV4(ip)){
|
||||||
|
// ketemu ip-nya
|
||||||
|
return StreamerOutputs[ip]
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* find SoundChannel IP from BroadcastZones description
|
* find SoundChannel IP from BroadcastZones description
|
||||||
* @param bz List of BroadcastZones description
|
* @param bz List of BroadcastZones description
|
||||||
@@ -541,7 +561,7 @@ class MainExtension01 {
|
|||||||
.filter { it.Type=="PAGING" }
|
.filter { it.Type=="PAGING" }
|
||||||
|
|
||||||
list.forEach { qp ->
|
list.forEach { qp ->
|
||||||
println("Processing $qp")
|
//println("Processing $qp")
|
||||||
if (qp.BroadcastZones.isNotBlank()){
|
if (qp.BroadcastZones.isNotBlank()){
|
||||||
if (ValidFile(qp.Message)){
|
if (ValidFile(qp.Message)){
|
||||||
val zz = qp.BroadcastZones.split(";")
|
val zz = qp.BroadcastZones.split(";")
|
||||||
@@ -550,19 +570,30 @@ class MainExtension01 {
|
|||||||
if (AllStreamerOutputIdle(ips)){
|
if (AllStreamerOutputIdle(ips)){
|
||||||
val afi = audioPlayer.LoadAudioFile(qp.Message)
|
val afi = audioPlayer.LoadAudioFile(qp.Message)
|
||||||
if (afi.isValid()){
|
if (afi.isValid()){
|
||||||
// file bisa di load, kirim ke masing masing Streamer Output by IP address
|
// file bisa di load, kirim ke masing-masing Streamer Output by IP address
|
||||||
ips.forEach { ip -> StreamerOutputs[ip]?.SendData(afi.bytes, { db.Add_Log("AAS", it) }, { db.Add_Log("AAS", it) }) }
|
Activate_Relays(zz)
|
||||||
|
ips.forEach { ip ->
|
||||||
|
// send byte array to streamer output
|
||||||
|
StreamerOutputs[ip]?.SendData(afi.bytes,
|
||||||
|
{
|
||||||
|
Deactivate_Relays(zz)
|
||||||
|
db.Add_Log("AAS", it) },
|
||||||
|
{
|
||||||
|
Deactivate_Relays(zz)
|
||||||
|
db.Add_Log("AAS", it) })
|
||||||
|
}
|
||||||
|
|
||||||
val logmessage =
|
val logmessage =
|
||||||
"Broadcast started PAGING with Filename '${qp.Message}' to zones: ${qp.BroadcastZones}"
|
"Broadcast started PAGING with Filename '${qp.Message}' to zones: ${qp.BroadcastZones}"
|
||||||
Logger.info { logmessage }
|
Logger.info { logmessage }
|
||||||
db.Add_Log("AAS", logmessage)
|
db.Add_Log("AAS", logmessage)
|
||||||
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
||||||
|
db.queuepagingDB.Resort()
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
// file tidak valid, delete from queue paging
|
// file tidak valid, delete from queue paging
|
||||||
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
||||||
|
db.queuepagingDB.Resort()
|
||||||
db.Add_Log(
|
db.Add_Log(
|
||||||
"AAS",
|
"AAS",
|
||||||
"Cancelled paging message with index ${qp.index} due to invalid audio file"
|
"Cancelled paging message with index ${qp.index} due to invalid audio file"
|
||||||
@@ -572,11 +603,13 @@ class MainExtension01 {
|
|||||||
} else {
|
} else {
|
||||||
// ada broadcast zone yang tidak valid, delete from queue paging
|
// ada broadcast zone yang tidak valid, delete from queue paging
|
||||||
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
||||||
|
db.queuepagingDB.Resort()
|
||||||
db.Add_Log("AAS", "Cancelled paging message with index ${qp.index} due to invalid broadcast zone")
|
db.Add_Log("AAS", "Cancelled paging message with index ${qp.index} due to invalid broadcast zone")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// file tidak valid, delete from queue paging
|
// file tidak valid, delete from queue paging
|
||||||
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
||||||
|
db.queuepagingDB.Resort()
|
||||||
db.Add_Log(
|
db.Add_Log(
|
||||||
"AAS",
|
"AAS",
|
||||||
"Cancelled paging message with index ${qp.index} due to invalid audio file"
|
"Cancelled paging message with index ${qp.index} due to invalid audio file"
|
||||||
@@ -585,6 +618,7 @@ class MainExtension01 {
|
|||||||
} else {
|
} else {
|
||||||
// invalid broadcast zone, delete from queue paging
|
// invalid broadcast zone, delete from queue paging
|
||||||
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
||||||
|
db.queuepagingDB.Resort()
|
||||||
db.Add_Log("AAS", "Cancelled paging message with index ${qp.index} due to empty broadcast zone")
|
db.Add_Log("AAS", "Cancelled paging message with index ${qp.index} due to empty broadcast zone")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -640,13 +674,25 @@ class MainExtension01 {
|
|||||||
// file siap broadcast
|
// file siap broadcast
|
||||||
val targetafi = audioPlayer.LoadAudioFile(targetfile)
|
val targetafi = audioPlayer.LoadAudioFile(targetfile)
|
||||||
if (targetafi.isValid()) {
|
if (targetafi.isValid()) {
|
||||||
ips.forEach { ip -> StreamerOutputs[ip]?.SendData(targetafi.bytes, { db.Add_Log("AAS", it) }, {db.Add_Log("AAS", it) }) }
|
// activate relays from broadcast zone
|
||||||
|
Activate_Relays(zz)
|
||||||
|
ips.forEach { ip ->
|
||||||
|
// send byte array to streamer output
|
||||||
|
StreamerOutputs[ip]?.SendData(targetafi.bytes,
|
||||||
|
{
|
||||||
|
Deactivate_Relays(zz)
|
||||||
|
db.Add_Log("AAS", it) },
|
||||||
|
{
|
||||||
|
Deactivate_Relays(zz)
|
||||||
|
db.Add_Log("AAS", it) })
|
||||||
|
}
|
||||||
|
|
||||||
val logmsg =
|
val logmsg =
|
||||||
"Broadcast started SHALAT message with generated file '$targetfile' to zones: ${qp.BroadcastZones}"
|
"Broadcast started SHALAT message with generated file '$targetfile' to zones: ${qp.BroadcastZones}"
|
||||||
Logger.info { logmsg }
|
Logger.info { logmsg }
|
||||||
db.Add_Log("AAS", logmsg)
|
db.Add_Log("AAS", logmsg)
|
||||||
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
||||||
|
db.queuepagingDB.Resort()
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
db.Add_Log(
|
db.Add_Log(
|
||||||
@@ -657,6 +703,7 @@ class MainExtension01 {
|
|||||||
}
|
}
|
||||||
} else{
|
} else{
|
||||||
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
||||||
|
db.queuepagingDB.Resort()
|
||||||
db.Add_Log("AAS", result.message)
|
db.Add_Log("AAS", result.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -665,6 +712,7 @@ class MainExtension01 {
|
|||||||
} else {
|
} else {
|
||||||
// tidak ada messagebank dengan ann_id ini, delete from queue paging
|
// tidak ada messagebank dengan ann_id ini, delete from queue paging
|
||||||
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
||||||
|
db.queuepagingDB.Resort()
|
||||||
db.Add_Log(
|
db.Add_Log(
|
||||||
"AAS",
|
"AAS",
|
||||||
"Cancelled Shalat message with index ${qp.index} due to ANN_ID $ann_id not found in Messagebank"
|
"Cancelled Shalat message with index ${qp.index} due to ANN_ID $ann_id not found in Messagebank"
|
||||||
@@ -675,16 +723,19 @@ class MainExtension01 {
|
|||||||
} else {
|
} else {
|
||||||
// invalid ann_id, delete from queue paging
|
// invalid ann_id, delete from queue paging
|
||||||
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
||||||
|
db.queuepagingDB.Resort()
|
||||||
db.Add_Log("AAS", "Cancelled shalat message with index ${qp.index} due to invalid ANN_ID")
|
db.Add_Log("AAS", "Cancelled shalat message with index ${qp.index} due to invalid ANN_ID")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// ada broadcast zone yang tidak valid, delete from queue paging
|
// ada broadcast zone yang tidak valid, delete from queue paging
|
||||||
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
||||||
|
db.queuepagingDB.Resort()
|
||||||
db.Add_Log("AAS", "Cancelled shalat message with index ${qp.index} due to invalid broadcast zone")
|
db.Add_Log("AAS", "Cancelled shalat message with index ${qp.index} due to invalid broadcast zone")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// invalid broadcast zone, delete from queue paging
|
// invalid broadcast zone, delete from queue paging
|
||||||
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
||||||
|
db.queuepagingDB.Resort()
|
||||||
db.Add_Log("AAS", "Cancelled shalat message with index ${qp.index} due to empty broadcast zone")
|
db.Add_Log("AAS", "Cancelled shalat message with index ${qp.index} due to empty broadcast zone")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -730,21 +781,35 @@ class MainExtension01 {
|
|||||||
// file siap broadcast
|
// file siap broadcast
|
||||||
val targetafi = audioPlayer.LoadAudioFile(targetfile)
|
val targetafi = audioPlayer.LoadAudioFile(targetfile)
|
||||||
if (targetafi.isValid()) {
|
if (targetafi.isValid()) {
|
||||||
ips.forEach { ip -> StreamerOutputs[ip]?.SendData(targetafi.bytes, { db.Add_Log("AAS", it) }, { db.Add_Log("AAS", it) }) }
|
// activate relays from broadcast zone
|
||||||
|
Activate_Relays(zz)
|
||||||
|
ips.forEach { ip ->
|
||||||
|
// send byte array to streamer output
|
||||||
|
StreamerOutputs[ip]?.SendData(targetafi.bytes,
|
||||||
|
{
|
||||||
|
Deactivate_Relays(zz)
|
||||||
|
db.Add_Log("AAS", it) },
|
||||||
|
{
|
||||||
|
Deactivate_Relays(zz)
|
||||||
|
db.Add_Log("AAS", it) })
|
||||||
|
}
|
||||||
|
|
||||||
val logmsg =
|
val logmsg =
|
||||||
"Broadcast started TIMER message with generated file '$targetfile' to zones: ${qa.BroadcastZones}"
|
"Broadcast started TIMER message with generated file '$targetfile' to zones: ${qa.BroadcastZones}"
|
||||||
Logger.info { logmsg }
|
Logger.info { logmsg }
|
||||||
db.Add_Log("AAS", logmsg)
|
db.Add_Log("AAS", logmsg)
|
||||||
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
||||||
|
db.queuetableDB.Resort()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
} else{
|
} else{
|
||||||
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
||||||
|
db.queuetableDB.Resort()
|
||||||
db.Add_Log("AAS", result.message)
|
db.Add_Log("AAS", result.message)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
||||||
|
db.queuetableDB.Resort()
|
||||||
db.Add_Log("AAS", result.message)
|
db.Add_Log("AAS", result.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -753,6 +818,7 @@ class MainExtension01 {
|
|||||||
} else {
|
} else {
|
||||||
// tidak ada messagebank dengan ann_id ini, delete from queue table
|
// tidak ada messagebank dengan ann_id ini, delete from queue table
|
||||||
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
||||||
|
db.queuetableDB.Resort()
|
||||||
db.Add_Log(
|
db.Add_Log(
|
||||||
"AAS",
|
"AAS",
|
||||||
"Cancelled TIMER with index ${qa.index} due to ANN_ID $ann_id not found in Messagebank"
|
"Cancelled TIMER with index ${qa.index} due to ANN_ID $ann_id not found in Messagebank"
|
||||||
@@ -763,27 +829,61 @@ class MainExtension01 {
|
|||||||
} else {
|
} else {
|
||||||
// invalid ann_id, delete from queue table
|
// invalid ann_id, delete from queue table
|
||||||
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
||||||
|
db.queuetableDB.Resort()
|
||||||
db.Add_Log("AAS", "Cancelled TIMER message with index ${qa.index} due to invalid ANN_ID")
|
db.Add_Log("AAS", "Cancelled TIMER message with index ${qa.index} due to invalid ANN_ID")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// ada broadcast zone yang tidak valid, delete from queue table
|
// ada broadcast zone yang tidak valid, delete from queue table
|
||||||
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
||||||
|
db.queuetableDB.Resort()
|
||||||
db.Add_Log("AAS", "Cancelled TIMER message with index ${qa.index} due to invalid broadcast zone")
|
db.Add_Log("AAS", "Cancelled TIMER message with index ${qa.index} due to invalid broadcast zone")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// invalid broadcast zone, delete from queue table
|
// invalid broadcast zone, delete from queue table
|
||||||
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
||||||
|
db.queuetableDB.Resort()
|
||||||
db.Add_Log("AAS", "Cancelled TIMER message with index ${qa.index} due to empty broadcast zone")
|
db.Add_Log("AAS", "Cancelled TIMER message with index ${qa.index} due to empty broadcast zone")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Activate_Relays(zz: List<String>){
|
||||||
|
zz.forEach { zonename ->
|
||||||
|
val bz = db.broadcastDB.List.find { it.description == zonename }
|
||||||
|
if (bz!=null){
|
||||||
|
val bc = Get_Barix_Connection_by_ZoneName(zonename)
|
||||||
|
if (bc!=null){
|
||||||
|
// ada barix connection
|
||||||
|
val relays = bz.bp.split(",")
|
||||||
|
.map { it.trim()}
|
||||||
|
.filter { it.isNotBlank() }
|
||||||
|
.filter { IsNumber(it) && it.toInt() in 1..8 }
|
||||||
|
.map{ it.toInt() }
|
||||||
|
if (relays.isNotEmpty()) bc.ActivateRelay(relays)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Deactivate_Relays(zz: List<String>){
|
||||||
|
zz.forEach { zonename ->
|
||||||
|
val bz = db.broadcastDB.List.find { it.description == zonename }
|
||||||
|
if (bz!=null){
|
||||||
|
val bc = Get_Barix_Connection_by_ZoneName(zonename)
|
||||||
|
bc?.DeactivateRelay()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun Read_Queue_Soundbank() : Boolean{
|
fun Read_Queue_Soundbank() : Boolean{
|
||||||
db.queuetableDB.Get()
|
db.queuetableDB.Get()
|
||||||
val list = db.queuetableDB.List.filter { it.Type=="SOUNDBANK" }
|
val list = db.queuetableDB.List.filter { it.Type=="SOUNDBANK" }
|
||||||
list.forEach { qa ->
|
list.forEach { qa ->
|
||||||
println("Processing $qa")
|
//println("Processing $qa")
|
||||||
if (qa.BroadcastZones.isNotEmpty()){
|
if (qa.BroadcastZones.isNotEmpty()){
|
||||||
val zz = qa.BroadcastZones.split(";")
|
val zz = qa.BroadcastZones.split(";")
|
||||||
if (AllBroadcastZonesValid(zz)){
|
if (AllBroadcastZonesValid(zz)){
|
||||||
@@ -820,6 +920,7 @@ class MainExtension01 {
|
|||||||
"Cancelled SOUNDBANK message with index ${qa.index} due to missing or invalid ANN_ID in SB_TAGS"
|
"Cancelled SOUNDBANK message with index ${qa.index} due to missing or invalid ANN_ID in SB_TAGS"
|
||||||
)
|
)
|
||||||
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
||||||
|
db.queuetableDB.Resort()
|
||||||
return@forEach
|
return@forEach
|
||||||
}
|
}
|
||||||
// sampe sini punya ann_id valid
|
// sampe sini punya ann_id valid
|
||||||
@@ -847,12 +948,14 @@ class MainExtension01 {
|
|||||||
} else {
|
} else {
|
||||||
db.Add_Log("AAS", result.message)
|
db.Add_Log("AAS", result.message)
|
||||||
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
||||||
|
db.queuetableDB.Resort()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listafi.isNotEmpty()){
|
if (listafi.isNotEmpty()){
|
||||||
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
||||||
|
db.queuetableDB.Resort()
|
||||||
// bikin nama file ada postifx nya dari SB_TAGS, tapi spasi diganti underscore
|
// bikin nama file ada postifx nya dari SB_TAGS, tapi spasi diganti underscore
|
||||||
// dan titik dua diganti dash
|
// dan titik dua diganti dash
|
||||||
val postfix = qa.SB_TAGS.replace(" ","_").replace(":","-")
|
val postfix = qa.SB_TAGS.replace(" ","_").replace(":","-")
|
||||||
@@ -864,7 +967,20 @@ class MainExtension01 {
|
|||||||
//println("Successfully wrote WAV file: $targetfile")
|
//println("Successfully wrote WAV file: $targetfile")
|
||||||
val targetafi = audioPlayer.LoadAudioFile(targetfile)
|
val targetafi = audioPlayer.LoadAudioFile(targetfile)
|
||||||
if (targetafi.isValid()) {
|
if (targetafi.isValid()) {
|
||||||
ips.forEach { ip -> StreamerOutputs[ip]?.SendData(targetafi.bytes, { db.Add_Log("AAS", it) }, { db.Add_Log("AAS", it) }) }
|
// activate relays from broadcast zone
|
||||||
|
Activate_Relays(zz)
|
||||||
|
ips.forEach { ip ->
|
||||||
|
// send byte array to streamer output
|
||||||
|
StreamerOutputs[ip]?.SendData(targetafi.bytes,
|
||||||
|
{
|
||||||
|
Deactivate_Relays(zz)
|
||||||
|
db.Add_Log("AAS", it)
|
||||||
|
|
||||||
|
}, {
|
||||||
|
Deactivate_Relays(zz)
|
||||||
|
db.Add_Log("AAS", it)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
val logmsg =
|
val logmsg =
|
||||||
"Broadcast started SOUNDBANK message with generated file '$targetfile' to zones: ${qa.BroadcastZones}"
|
"Broadcast started SOUNDBANK message with generated file '$targetfile' to zones: ${qa.BroadcastZones}"
|
||||||
@@ -876,6 +992,7 @@ class MainExtension01 {
|
|||||||
} else {
|
} else {
|
||||||
println("Failed to write WAV file: ${result.message}")
|
println("Failed to write WAV file: ${result.message}")
|
||||||
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
||||||
|
db.queuetableDB.Resort()
|
||||||
db.Add_Log("AAS", result.message)
|
db.Add_Log("AAS", result.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -883,6 +1000,7 @@ class MainExtension01 {
|
|||||||
} else {
|
} else {
|
||||||
// tidak ada messagebank dengan ann_id ini, delete from queue table
|
// tidak ada messagebank dengan ann_id ini, delete from queue table
|
||||||
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
||||||
|
db.queuetableDB.Resort()
|
||||||
db.Add_Log(
|
db.Add_Log(
|
||||||
"AAS",
|
"AAS",
|
||||||
"Cancelled SOUNDBANK message with index ${qa.index} due to ANN_ID $ann_id not found in Messagebank"
|
"Cancelled SOUNDBANK message with index ${qa.index} due to ANN_ID $ann_id not found in Messagebank"
|
||||||
@@ -894,11 +1012,13 @@ class MainExtension01 {
|
|||||||
} else {
|
} else {
|
||||||
// ada broadcast zone yang tidak valid, delete from queue table
|
// ada broadcast zone yang tidak valid, delete from queue table
|
||||||
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
||||||
|
db.queuetableDB.Resort()
|
||||||
db.Add_Log("AAS", "Cancelled SOUNDBANK message with index ${qa.index} due to invalid broadcast zone")
|
db.Add_Log("AAS", "Cancelled SOUNDBANK message with index ${qa.index} due to invalid broadcast zone")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// invalid broadcast zone, delete from queue table
|
// invalid broadcast zone, delete from queue table
|
||||||
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
||||||
|
db.queuetableDB.Resort()
|
||||||
db.Add_Log("AAS", "Cancelled SOUNDBANK message with index ${qa.index} due to empty broadcast zone")
|
db.Add_Log("AAS", "Cancelled SOUNDBANK message with index ${qa.index} due to empty broadcast zone")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,11 @@ import kotlinx.coroutines.CoroutineScope
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import org.tinylog.Logger
|
||||||
import java.net.DatagramPacket
|
import java.net.DatagramPacket
|
||||||
import java.net.DatagramSocket
|
import java.net.DatagramSocket
|
||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
|
import java.net.Socket
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
|
|
||||||
@@ -18,9 +20,9 @@ class BarixConnection(val index: UInt, var channel: String, val ipaddress: Strin
|
|||||||
private var _sd: Int = 0
|
private var _sd: Int = 0
|
||||||
private var _vu: Int = 0
|
private var _vu: Int = 0
|
||||||
private var _onlinecounter = 0
|
private var _onlinecounter = 0
|
||||||
private val udp = DatagramSocket(0)
|
|
||||||
private val inet = InetSocketAddress(ipaddress, port)
|
private val inet = InetSocketAddress(ipaddress, port)
|
||||||
private val maxUDPsize = 1000
|
private val maxUDPsize = 1000
|
||||||
|
private var _tcp: Socket? = null
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -53,6 +55,15 @@ class BarixConnection(val index: UInt, var channel: String, val ipaddress: Strin
|
|||||||
_onlinecounter = 5
|
_onlinecounter = 5
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TCP command socket for communication with this Barix device
|
||||||
|
*/
|
||||||
|
var commandsocket: Socket?
|
||||||
|
get() = _tcp
|
||||||
|
set(value){
|
||||||
|
_tcp = value
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decrement online counter, if counter reaches 0, the device is considered offline
|
* Decrement online counter, if counter reaches 0, the device is considered offline
|
||||||
*/
|
*/
|
||||||
@@ -93,24 +104,26 @@ class BarixConnection(val index: UInt, var channel: String, val ipaddress: Strin
|
|||||||
fun SendData(data: ByteArray, cbOK: Consumer<String>, cbFail: Consumer<String>) {
|
fun SendData(data: ByteArray, cbOK: Consumer<String>, cbFail: Consumer<String>) {
|
||||||
if (data.isNotEmpty()) {
|
if (data.isNotEmpty()) {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
DatagramSocket().use{ udp ->
|
||||||
val bb = ByteBuffer.wrap(data)
|
val bb = ByteBuffer.wrap(data)
|
||||||
while(bb.hasRemaining()){
|
while(bb.hasRemaining()){
|
||||||
try {
|
try {
|
||||||
val chunk = ByteArray(if (bb.remaining() > maxUDPsize) maxUDPsize else bb.remaining())
|
val chunk = ByteArray(if (bb.remaining() > maxUDPsize) maxUDPsize else bb.remaining())
|
||||||
bb.get(chunk)
|
bb.get(chunk)
|
||||||
while(bufferRemain<chunk.size){
|
//println("Buffer remain: $bufferRemain, sending chunk size: ${chunk.size}")
|
||||||
delay(10)
|
while(bufferRemain<chunk.size){
|
||||||
|
delay(10)
|
||||||
|
//println("Waiting until buffer enough..")
|
||||||
|
}
|
||||||
|
udp.send(DatagramPacket(chunk, chunk.size, inet))
|
||||||
|
delay(2)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
cbFail.accept("SendData to $ipaddress:$port failed, message: ${e.message}")
|
||||||
|
return@launch
|
||||||
}
|
}
|
||||||
udp.send(DatagramPacket(chunk, chunk.size, inet))
|
|
||||||
delay(1)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
cbFail.accept("SendData to $ipaddress:$port failed, message: ${e.message}")
|
|
||||||
return@launch
|
|
||||||
}
|
}
|
||||||
|
cbOK.accept("SendData to $channel ($ipaddress:$port) succeeded, ${data.size} bytes sent")
|
||||||
}
|
}
|
||||||
cbOK.accept("SendData to $channel ($ipaddress:$port) succeeded, ${data.size} bytes sent")
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else cbFail.accept("SendData to $ipaddress:$port failed, data is empty")
|
} else cbFail.accept("SendData to $ipaddress:$port failed, data is empty")
|
||||||
@@ -142,4 +155,76 @@ class BarixConnection(val index: UInt, var channel: String, val ipaddress: Strin
|
|||||||
fun toJsonString(): String {
|
fun toJsonString(): String {
|
||||||
return Somecodes.toJsonString(toJsonNode())
|
return Somecodes.toJsonString(toJsonNode())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activate relay on Barix device
|
||||||
|
* @param relays The relay numbers to activate (1-8)
|
||||||
|
* @return true if successful
|
||||||
|
*/
|
||||||
|
fun ActivateRelay(vararg relays: Int){
|
||||||
|
val command = StringBuilder("RELAY;")
|
||||||
|
var binary = 0
|
||||||
|
relays.forEach {
|
||||||
|
if (it in 1..8) {
|
||||||
|
binary = binary or (1 shl (it - 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
command.append(binary.toString()).append("@")
|
||||||
|
SendCommand(command.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ActivateRelay(relays: List<Int>){
|
||||||
|
val command = StringBuilder("RELAY;")
|
||||||
|
var binary = 0
|
||||||
|
relays.forEach {
|
||||||
|
if (it in 1..8) {
|
||||||
|
binary = binary or (1 shl (it - 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
command.append(binary.toString()).append("@")
|
||||||
|
SendCommand(command.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deactivate relay on Barix device
|
||||||
|
*/
|
||||||
|
fun DeactivateRelay(){
|
||||||
|
SendCommand("RELAY;0@")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send command to Barix device
|
||||||
|
* @param command The command to send
|
||||||
|
* @return true if successful
|
||||||
|
*/
|
||||||
|
fun SendCommand(command: String): Boolean {
|
||||||
|
try {
|
||||||
|
if (_tcp!=null){
|
||||||
|
if (!_tcp!!.isClosed){
|
||||||
|
val bb = command.toByteArray()
|
||||||
|
val size = bb.size + 4
|
||||||
|
val b4 = byteArrayOf(
|
||||||
|
(size shr 24 and 0xFF).toByte(),
|
||||||
|
(size shr 16 and 0xFF).toByte(),
|
||||||
|
(size shr 8 and 0xFF).toByte(),
|
||||||
|
(size and 0xFF).toByte()
|
||||||
|
)
|
||||||
|
val out = _tcp!!.getOutputStream()
|
||||||
|
out.write(b4)
|
||||||
|
out.write(bb)
|
||||||
|
out.flush()
|
||||||
|
Logger.info { "SendCommand to $ipaddress : $command" }
|
||||||
|
return true
|
||||||
|
}else {
|
||||||
|
Logger.error { "Socket to $ipaddress is not connected" }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Logger.error { "Socket to $ipaddress is null" }
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Logger.error { "Failed to SendCommand to $ipaddress, Message : ${e.message}" }
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,8 +3,10 @@ package barix
|
|||||||
import codes.Somecodes.Companion.ValidString
|
import codes.Somecodes.Companion.ValidString
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import org.tinylog.Logger
|
import org.tinylog.Logger
|
||||||
|
import java.io.DataInputStream
|
||||||
import java.net.ServerSocket
|
import java.net.ServerSocket
|
||||||
import java.net.Socket
|
import java.net.Socket
|
||||||
|
import java.nio.ByteBuffer
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
@@ -37,13 +39,19 @@ class TCP_Barix_Command_Server {
|
|||||||
val key : String = socket.inetAddress.hostAddress
|
val key : String = socket.inetAddress.hostAddress
|
||||||
socketMap[key] = socket
|
socketMap[key] = socket
|
||||||
Logger.info { "Start communicating with Streamer Output with IP : $key" }
|
Logger.info { "Start communicating with Streamer Output with IP : $key" }
|
||||||
val din = socket.getInputStream()
|
try{
|
||||||
while (isActive) {
|
|
||||||
if (din.available()>0){
|
val din = DataInputStream(socket.getInputStream())
|
||||||
val bb = ByteArray(din.available())
|
while (isActive) {
|
||||||
din.read(bb)
|
val length = ByteArray(4)
|
||||||
|
din.readFully(length)
|
||||||
|
val readlength = ByteBuffer.wrap(length).getInt()
|
||||||
|
//println("Read Length : $readlength")
|
||||||
|
val bb = ByteArray(readlength)
|
||||||
|
din.readFully(bb)
|
||||||
// B4A format, 4 bytes di depan adalah size
|
// B4A format, 4 bytes di depan adalah size
|
||||||
val str = String(bb, 4, bb.size - 4)
|
val str = String(bb)
|
||||||
|
//println("Received from $key : $str")
|
||||||
if (ValidString(str)) {
|
if (ValidString(str)) {
|
||||||
// Valid command from Barix is in format $"STATUSBARIX;VU;BuffRemain;StatusData"$
|
// Valid command from Barix is in format $"STATUSBARIX;VU;BuffRemain;StatusData"$
|
||||||
pattern.find(str)?.let { matchResult ->
|
pattern.find(str)?.let { matchResult ->
|
||||||
@@ -60,8 +68,12 @@ class TCP_Barix_Command_Server {
|
|||||||
Logger.warn { "Invalid command format from $key : $str" }
|
Logger.warn { "Invalid command format from $key : $str" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
} catch (ex:Exception){
|
||||||
|
Logger.error { "Error in communication with Streamer Output with IP $key, Message : ${ex.message}" }
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.info { "Finished communicating with Streamer Output with IP $key" }
|
Logger.info { "Finished communicating with Streamer Output with IP $key" }
|
||||||
socketMap.remove(key)
|
socketMap.remove(key)
|
||||||
|
|
||||||
@@ -102,4 +114,15 @@ class TCP_Barix_Command_Server {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Socket by IP address
|
||||||
|
* @param ip The IP address of the client
|
||||||
|
* @return Socket if found, null otherwise
|
||||||
|
*/
|
||||||
|
fun getSocket(ip: String): Socket? {
|
||||||
|
return socketMap[ip]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2,8 +2,11 @@ package codes
|
|||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode
|
import com.fasterxml.jackson.databind.JsonNode
|
||||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
|
import content.Category
|
||||||
import content.Language
|
import content.Language
|
||||||
|
import content.NetworkInformation
|
||||||
import content.ScheduleDay
|
import content.ScheduleDay
|
||||||
|
import content.VoiceType
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
@@ -13,6 +16,8 @@ import oshi.SystemInfo
|
|||||||
import oshi.hardware.CentralProcessor
|
import oshi.hardware.CentralProcessor
|
||||||
import oshi.hardware.GlobalMemory
|
import oshi.hardware.GlobalMemory
|
||||||
import oshi.hardware.NetworkIF
|
import oshi.hardware.NetworkIF
|
||||||
|
import oshi.hardware.Sensors
|
||||||
|
import oshi.software.os.OperatingSystem
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
@@ -25,21 +30,17 @@ import kotlin.io.path.name
|
|||||||
class Somecodes {
|
class Somecodes {
|
||||||
companion object {
|
companion object {
|
||||||
val current_directory : String = System.getProperty("user.dir")
|
val current_directory : String = System.getProperty("user.dir")
|
||||||
val Soundbank_directory : Path = Path.of(current_directory,"Soundbank")
|
var Soundbank_directory : Path = Path.of(current_directory,"Soundbank")
|
||||||
val Soundbank_Languages_directory : List<Path> = listOf(
|
|
||||||
Soundbank_directory.resolve(Language.INDONESIA.name),
|
|
||||||
Soundbank_directory.resolve(Language.LOCAL.name),
|
|
||||||
Soundbank_directory.resolve(Language.ENGLISH.name),
|
|
||||||
Soundbank_directory.resolve(Language.CHINESE.name),
|
|
||||||
Soundbank_directory.resolve(Language.JAPANESE.name),
|
|
||||||
Soundbank_directory.resolve(Language.ARABIC.name)
|
|
||||||
)
|
|
||||||
val SoundbankResult_directory : Path = Path.of(current_directory,"SoundbankResult")
|
val SoundbankResult_directory : Path = Path.of(current_directory,"SoundbankResult")
|
||||||
val PagingResult_directory : Path = Path.of(current_directory,"PagingResult")
|
val PagingResult_directory : Path = Path.of(current_directory,"PagingResult")
|
||||||
|
|
||||||
val si = SystemInfo()
|
val si = SystemInfo()
|
||||||
val processor: CentralProcessor = si.hardware.processor
|
val processor: CentralProcessor = si.hardware.processor
|
||||||
val memory : GlobalMemory = si.hardware.memory
|
val memory : GlobalMemory = si.hardware.memory
|
||||||
|
val NetworkInfoMap = mutableMapOf<String, NetworkInformation>()
|
||||||
|
val sensor : Sensors = si.hardware.sensors
|
||||||
|
val os : OperatingSystem = si.operatingSystem
|
||||||
|
|
||||||
val datetimeformat1: DateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss")
|
val datetimeformat1: DateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss")
|
||||||
val dateformat1: DateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy")
|
val dateformat1: DateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy")
|
||||||
@@ -56,6 +57,25 @@ class Somecodes {
|
|||||||
// regex for getting ann_id from Message, which is the number inside []
|
// regex for getting ann_id from Message, which is the number inside []
|
||||||
private val ann_id_regex = Regex("\\[(\\d+)]")
|
private val ann_id_regex = Regex("\\[(\\d+)]")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the directory path for a specific language, voice type, and category.
|
||||||
|
* @param language The language.
|
||||||
|
* @param voice The voice type.
|
||||||
|
* @param category The category.
|
||||||
|
* @return The path to the directory.
|
||||||
|
*/
|
||||||
|
fun SoundbankDirectory(language: Language, voice: VoiceType, category: Category) : Path{
|
||||||
|
return Soundbank_directory.resolve(language.name).resolve(voice.name).resolve(category.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun SoundbankDirectory(language: String, voice: String, category: String) : Path{
|
||||||
|
return SoundbankDirectory(
|
||||||
|
Language.valueOf(language),
|
||||||
|
VoiceType.valueOf(voice),
|
||||||
|
Category.valueOf(category)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun ExtractFilesFromClassPath(resourcePath: String, outputDir: Path) {
|
fun ExtractFilesFromClassPath(resourcePath: String, outputDir: Path) {
|
||||||
try {
|
try {
|
||||||
val resource = Somecodes::class.java.getResource(resourcePath)
|
val resource = Somecodes::class.java.getResource(resourcePath)
|
||||||
@@ -157,14 +177,13 @@ class Somecodes {
|
|||||||
/**
|
/**
|
||||||
* List all audio files (.mp3 and .wav) in the specified directory and its subdirectories.
|
* List all audio files (.mp3 and .wav) in the specified directory and its subdirectories.
|
||||||
* Only files larger than 1KB are included.
|
* Only files larger than 1KB are included.
|
||||||
* @param dir The directory to search in (default is the current working directory).
|
* @param p The directory Path to search in
|
||||||
* @return A list of absolute paths to the audio files found.
|
* @return A list of absolute paths to the audio files found.
|
||||||
*/
|
*/
|
||||||
fun ListAudioFiles(dir: String = current_directory) : List<String>{
|
fun ListAudioFiles(p: Path) : List<String>{
|
||||||
return try{
|
return try{
|
||||||
// find all files that ends with .mp3 or .wav
|
// find all files that ends with .mp3 or .wav
|
||||||
// and find them recursively
|
// and find them recursively
|
||||||
val p = Path.of(dir)
|
|
||||||
if (Files.exists(p) && Files.isDirectory(p)){
|
if (Files.exists(p) && Files.isDirectory(p)){
|
||||||
Files.walk(p)
|
Files.walk(p)
|
||||||
// cari file regular saja
|
// cari file regular saja
|
||||||
@@ -265,16 +284,43 @@ class Somecodes {
|
|||||||
, (usedMemory.toDouble() / totalMemory * 100))
|
, (usedMemory.toDouble() / totalMemory * 100))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun GetNetworkStatus(networkname: String) : String{
|
fun GetNetworkStatus(cb : Consumer<List<NetworkInformation>>) {
|
||||||
val networks: List<NetworkIF> = si.hardware.networkIFs.toList()
|
val networks: List<NetworkIF> = si.hardware.networkIFs.toList()
|
||||||
//TODO cari network yang sesuai dengan networkname
|
networks.forEach { net ->
|
||||||
// networks.forEach { net ->
|
|
||||||
//
|
if (net.ifOperStatus==NetworkIF.IfOperStatus.UP){
|
||||||
// println(net.name+" "+net.displayName+" ")
|
if (net.iPv4addr.size>0 || net.iPv6addr.size>0){
|
||||||
// net.updateAttributes()
|
var ni = NetworkInfoMap[net.name]
|
||||||
// println("upload: ${SizetoHuman(net.bytesSent)} sent, download: ${SizetoHuman(net.bytesRecv)} received")
|
if (ni == null){
|
||||||
// }
|
ni = NetworkInformation(net.name, net.displayName, net.macaddr)
|
||||||
return ""
|
NetworkInfoMap[net.name] = ni
|
||||||
|
}
|
||||||
|
ni.ipV4addr = net.iPv4addr.toMutableList()
|
||||||
|
ni.ipV6addr = net.iPv6addr.toMutableList()
|
||||||
|
ni.speed = net.speed
|
||||||
|
ni.packetsSent = net.packetsSent
|
||||||
|
ni.packetsRecv = net.packetsRecv
|
||||||
|
if (ni.updateStamp==0L){
|
||||||
|
ni.bytesSent = net.bytesSent
|
||||||
|
ni.bytesRecv = net.bytesRecv
|
||||||
|
ni.txSpeed = 0
|
||||||
|
ni.rxSpeed = 0
|
||||||
|
ni.updateStamp = System.currentTimeMillis()
|
||||||
|
} else {
|
||||||
|
// tx speed = (current bytesSent - previous bytesSent) / (current time - previous time) * 1000
|
||||||
|
val currentTime = System.currentTimeMillis()
|
||||||
|
ni.txSpeed = ((net.bytesSent - ni.bytesSent) * 1000 / (currentTime - ni.updateStamp))
|
||||||
|
ni.rxSpeed = ((net.bytesRecv - ni.bytesRecv) * 1000 / (currentTime - ni.updateStamp))
|
||||||
|
ni.bytesSent = net.bytesSent
|
||||||
|
ni.bytesRecv = net.bytesRecv
|
||||||
|
ni.updateStamp = currentTime
|
||||||
|
}
|
||||||
|
} else if (NetworkInfoMap.contains(net.name)) NetworkInfoMap.remove(net.name)
|
||||||
|
} else if (NetworkInfoMap.contains(net.name)) NetworkInfoMap.remove(net.name)
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
cb.accept(NetworkInfoMap.values.toList())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -286,6 +332,23 @@ class Somecodes {
|
|||||||
return value is String && value.isNotBlank()
|
return value is String && value.isNotBlank()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if all strings in a list are valid non-blank strings.
|
||||||
|
* @param values The list of strings to check.
|
||||||
|
* @return True if all strings in the list are valid non-blank strings, false otherwise.
|
||||||
|
*/
|
||||||
|
fun ValidStrings(values: List<String>) : Boolean{
|
||||||
|
if (values.isNotEmpty()){
|
||||||
|
for (v in values){
|
||||||
|
if (!ValidString(v)){
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a string is a valid file path and the file exists.
|
* Check if a string is a valid file path and the file exists.
|
||||||
* @param value The string to check.
|
* @param value The string to check.
|
||||||
@@ -433,6 +496,35 @@ class Somecodes {
|
|||||||
sb.append(".wav")
|
sb.append(".wav")
|
||||||
return sb.toString()
|
return sb.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get sensors information using OSHI library.
|
||||||
|
* @return A string representing the CPU temperature, fan speeds, and CPU voltage, or an empty string if not available.
|
||||||
|
*/
|
||||||
|
fun GetSensorsInfo() : String {
|
||||||
|
val cputemp = sensor.cpuTemperature
|
||||||
|
val cpuvolt = sensor.cpuVoltage
|
||||||
|
val fanspeed = sensor.fanSpeeds
|
||||||
|
return if (cpuvolt>0 && cputemp > 0 && fanspeed.isNotEmpty()){
|
||||||
|
String.format("CPU Temp: %.1f °C\nFan Speeds: %s RPM\nCPU Voltage: %.2f V",
|
||||||
|
sensor.cpuTemperature,
|
||||||
|
sensor.fanSpeeds.joinToString("/"),
|
||||||
|
sensor.cpuVoltage
|
||||||
|
)
|
||||||
|
} else ""
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun GetUptime() : String {
|
||||||
|
val value = os.systemUptime
|
||||||
|
return if (value>0){
|
||||||
|
// number of seconds since system boot
|
||||||
|
val hours = value / 3600
|
||||||
|
val minutes = (value % 3600) / 60
|
||||||
|
val seconds = value % 60
|
||||||
|
String.format("%02d:%02d:%02d", hours, minutes, seconds)
|
||||||
|
} else ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ class TCP_Android_Command_Server {
|
|||||||
din.read(bb)
|
din.read(bb)
|
||||||
// B4A format, 4 bytes di depan adalah size
|
// B4A format, 4 bytes di depan adalah size
|
||||||
val str = String(bb, 4, bb.size - 4)
|
val str = String(bb, 4, bb.size - 4)
|
||||||
println("Received command from $key : $str")
|
//println("Received command from $key : $str")
|
||||||
str.split("@").map { it.trim() }.filter { ValidString(it) }
|
str.split("@").map { it.trim() }.filter { ValidString(it) }
|
||||||
.forEach {
|
.forEach {
|
||||||
process_command(key,it) { reply ->
|
process_command(key,it) { reply ->
|
||||||
@@ -220,6 +220,7 @@ class TCP_Android_Command_Server {
|
|||||||
pj.broadcastzones
|
pj.broadcastzones
|
||||||
)
|
)
|
||||||
if (db.queuepagingDB.Add(qp)) {
|
if (db.queuepagingDB.Add(qp)) {
|
||||||
|
db.queuepagingDB.Resort()
|
||||||
logcb.accept("Paging audio inserted to queue paging table from Android $key, file ${pj.filePath.absolutePathString()}")
|
logcb.accept("Paging audio inserted to queue paging table from Android $key, file ${pj.filePath.absolutePathString()}")
|
||||||
cb.accept("PCMFILE_STOP;OK@")
|
cb.accept("PCMFILE_STOP;OK@")
|
||||||
return
|
return
|
||||||
@@ -274,6 +275,7 @@ class TCP_Android_Command_Server {
|
|||||||
pj.broadcastzones
|
pj.broadcastzones
|
||||||
)
|
)
|
||||||
if (db.queuepagingDB.Add(qp)){
|
if (db.queuepagingDB.Add(qp)){
|
||||||
|
db.queuepagingDB.Resort()
|
||||||
logcb.accept("Paging audio inserted to queue paging table from IPM $key, file ${pj.filePath.absolutePathString()}")
|
logcb.accept("Paging audio inserted to queue paging table from IPM $key, file ${pj.filePath.absolutePathString()}")
|
||||||
cb.accept("STOPPAGINGAND;OK@")
|
cb.accept("STOPPAGINGAND;OK@")
|
||||||
return
|
return
|
||||||
@@ -306,7 +308,7 @@ class TCP_Android_Command_Server {
|
|||||||
if (userlogin != null){
|
if (userlogin != null){
|
||||||
val userdb = db.userDB.List.find { it.username == username }
|
val userdb = db.userDB.List.find { it.username == username }
|
||||||
if (userdb != null){
|
if (userdb != null){
|
||||||
println("Sending initialization data to $key with username $username")
|
//println("Sending initialization data to $key with username $username")
|
||||||
val result = StringBuilder()
|
val result = StringBuilder()
|
||||||
// kirim Zone
|
// kirim Zone
|
||||||
result.append("ZONE")
|
result.append("ZONE")
|
||||||
@@ -627,6 +629,7 @@ class TCP_Android_Command_Server {
|
|||||||
lang
|
lang
|
||||||
)
|
)
|
||||||
if (db.queuetableDB.Add(qt)){
|
if (db.queuetableDB.Add(qt)){
|
||||||
|
db.queuetableDB.Resort()
|
||||||
logcb.accept("Broadcast request from Android $key username=${listUserLogin.find { it.ip==key }?.username ?: "UNKNOWN"} inserted. Message: $desc;$lang;$tags;$zone")
|
logcb.accept("Broadcast request from Android $key username=${listUserLogin.find { it.ip==key }?.username ?: "UNKNOWN"} inserted. Message: $desc;$lang;$tags;$zone")
|
||||||
cb.accept("BROADCASTAND;OK@")
|
cb.accept("BROADCASTAND;OK@")
|
||||||
return
|
return
|
||||||
|
|||||||
15
src/content/NetworkInformation.kt
Normal file
15
src/content/NetworkInformation.kt
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package content
|
||||||
|
|
||||||
|
class NetworkInformation(val name: String, val displayName: String, val macAddress: String) {
|
||||||
|
var ipV4addr: MutableList<String> = mutableListOf()
|
||||||
|
var ipV6addr: MutableList<String> = mutableListOf()
|
||||||
|
var bytesSent: Long = 0
|
||||||
|
var bytesRecv: Long = 0
|
||||||
|
var packetsSent: Long = 0
|
||||||
|
var packetsRecv: Long = 0
|
||||||
|
var speed: Long = 0 // in bits per second
|
||||||
|
var updateStamp : Long = 0 // epoch time in milliseconds, buat hitung speed
|
||||||
|
var txSpeed: Long = 0
|
||||||
|
var rxSpeed: Long = 0
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,4 +1,28 @@
|
|||||||
package database
|
package database
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
data class BroadcastZones(var index: UInt, var description: String, var SoundChannel: String, var id: String, var bp: String)
|
data class BroadcastZones(var index: UInt, var description: String, var SoundChannel: String, var id: String, var bp: String){
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if all fields are not empty
|
||||||
|
*/
|
||||||
|
fun isNotEmpty(): Boolean{
|
||||||
|
if (description.isNotEmpty()){
|
||||||
|
if (SoundChannel.isNotEmpty()){
|
||||||
|
if (id.isNotEmpty()){
|
||||||
|
if (bp.isNotEmpty()){
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a string representation of the BroadcastZones object.
|
||||||
|
*/
|
||||||
|
override fun toString(): String {
|
||||||
|
return "BroadcastZones(index=$index, description='$description', SoundChannel='$SoundChannel', id='$id', bp='$bp')"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
package database
|
|
||||||
|
|
||||||
data class BroadcastZonesHtml(var index: UInt, var description: String, var SoundChannel: String, var Box: String, var Relay: String)
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
package database
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
data class IpZones(var index:UInt, var description: String, var ip: String)
|
|
||||||
@@ -1,4 +1,22 @@
|
|||||||
package database
|
package database
|
||||||
|
|
||||||
@Suppress("unused")
|
data class LanguageLink(var index: UInt, var TAG: String, var Language: String){
|
||||||
data class LanguageLink(var index: UInt, var TAG: String, var Language: String)
|
|
||||||
|
/**
|
||||||
|
* Check if the LanguageLink object has non-empty TAG and Language fields.
|
||||||
|
* @return true if both TAG and Language are not empty, false otherwise.
|
||||||
|
*/
|
||||||
|
fun isNotEmpty() : Boolean {
|
||||||
|
return TAG.isNotEmpty() && Language.isNotEmpty()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representation of the LanguageLink object.
|
||||||
|
* @return A string in the format "LanguageLink(index=..., TAG='...', Language='...')".
|
||||||
|
*/
|
||||||
|
override fun toString(): String {
|
||||||
|
return "LanguageLink(index=$index, TAG='$TAG', Language='$Language')"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
package database
|
package database
|
||||||
|
|
||||||
@Suppress("unused")
|
data class LogSemiauto(val index: UInt, val date: String, val time: String, val source: String, val description: String){
|
||||||
data class LogSemiauto(val index: UInt, val date: String, val time: String, val source: String, val description: String)
|
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "$date $time [$source] $description"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import org.tinylog.Logger
|
|||||||
import java.sql.Connection
|
import java.sql.Connection
|
||||||
import java.sql.DriverManager
|
import java.sql.DriverManager
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
import kotlin.math.max
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class to manage a connection to a MariaDB database.
|
* A class to manage a connection to a MariaDB database.
|
||||||
@@ -105,7 +104,7 @@ class MariaDB(
|
|||||||
List.clear()
|
List.clear()
|
||||||
try {
|
try {
|
||||||
val statement = connection.createStatement()
|
val statement = connection.createStatement()
|
||||||
val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}")
|
val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName} ORDER BY Category, Language, VoiceType, TAG")
|
||||||
while (resultSet?.next() == true) {
|
while (resultSet?.next() == true) {
|
||||||
val soundbank = Soundbank(
|
val soundbank = Soundbank(
|
||||||
resultSet.getLong("index").toUInt(),
|
resultSet.getLong("index").toUInt(),
|
||||||
@@ -221,13 +220,15 @@ class MariaDB(
|
|||||||
override fun Resort(): Boolean {
|
override fun Resort(): Boolean {
|
||||||
try {
|
try {
|
||||||
val statement = connection.createStatement()
|
val statement = connection.createStatement()
|
||||||
|
val tempdb_name = "temp_${super.dbName}"
|
||||||
|
|
||||||
// use a temporary table to reorder the index
|
// use a temporary table to reorder the index
|
||||||
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}")
|
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
|
||||||
statement?.executeUpdate("INSERT INTO temp_${super.dbName} (Description, TAG, Category, Language, VoiceType, Path) SELECT Description, TAG, Category, Language, VoiceType, Path FROM ${super.dbName} ORDER BY Description ")
|
statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
|
||||||
|
statement?.executeUpdate("INSERT INTO $tempdb_name (Description, TAG, Category, Language, VoiceType, Path) SELECT Description, TAG, Category, Language, VoiceType, Path FROM ${super.dbName} ORDER BY Category, Language, VoiceType, TAG ")
|
||||||
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
||||||
statement?.executeUpdate("INSERT INTO ${super.dbName} (Description, TAG, Category, Language, VoiceType, Path) SELECT Description, TAG, Category, Language, VoiceType, Path FROM temp_${super.dbName}")
|
statement?.executeUpdate("INSERT INTO ${super.dbName} (Description, TAG, Category, Language, VoiceType, Path) SELECT Description, TAG, Category, Language, VoiceType, Path FROM $tempdb_name")
|
||||||
statement?.executeUpdate("DROP TABLE temp_${super.dbName}")
|
statement?.executeUpdate("DROP TABLE $tempdb_name")
|
||||||
Logger.info("${super.dbName} table resorted by Description" as Any)
|
Logger.info("${super.dbName} table resorted by Description" as Any)
|
||||||
// reload the local list
|
// reload the local list
|
||||||
Get()
|
Get()
|
||||||
@@ -421,12 +422,14 @@ class MariaDB(
|
|||||||
override fun Resort(): Boolean {
|
override fun Resort(): Boolean {
|
||||||
try {
|
try {
|
||||||
val statement = connection.createStatement()
|
val statement = connection.createStatement()
|
||||||
|
val tempdb_name = "temp_${super.dbName}"
|
||||||
// use a temporary table to reorder the index
|
// use a temporary table to reorder the index
|
||||||
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}")
|
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
|
||||||
statement?.executeUpdate("INSERT INTO temp_${super.dbName} (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) SELECT Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS FROM ${super.dbName} ORDER BY ANN_ID ")
|
statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
|
||||||
|
statement?.executeUpdate("INSERT INTO $tempdb_name (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) SELECT Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS FROM ${super.dbName} ORDER BY ANN_ID ")
|
||||||
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
||||||
statement?.executeUpdate("INSERT INTO ${super.dbName} (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) SELECT Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS FROM temp_${super.dbName}")
|
statement?.executeUpdate("INSERT INTO ${super.dbName} (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) SELECT Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS FROM $tempdb_name")
|
||||||
statement?.executeUpdate("DROP TABLE temp_${super.dbName}")
|
statement?.executeUpdate("DROP TABLE $tempdb_name")
|
||||||
Logger.info("${super.dbName} table resorted by Description" as Any)
|
Logger.info("${super.dbName} table resorted by Description" as Any)
|
||||||
// reload the local list
|
// reload the local list
|
||||||
Get()
|
Get()
|
||||||
@@ -616,12 +619,14 @@ class MariaDB(
|
|||||||
override fun Resort(): Boolean {
|
override fun Resort(): Boolean {
|
||||||
try {
|
try {
|
||||||
val statement = connection.createStatement()
|
val statement = connection.createStatement()
|
||||||
|
val tempdb_name = "temp_${super.dbName}"
|
||||||
// use a temporary table to reorder the index
|
// use a temporary table to reorder the index
|
||||||
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}")
|
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
|
||||||
statement?.executeUpdate("INSERT INTO temp_${super.dbName} (TAG, Language) SELECT TAG, Language FROM ${super.dbName} ORDER BY TAG")
|
statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
|
||||||
|
statement?.executeUpdate("INSERT INTO $tempdb_name (TAG, Language) SELECT TAG, Language FROM ${super.dbName} ORDER BY TAG")
|
||||||
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
||||||
statement?.executeUpdate("INSERT INTO ${super.dbName} (TAG, Language) SELECT TAG, Language FROM temp_${super.dbName}")
|
statement?.executeUpdate("INSERT INTO ${super.dbName} (TAG, Language) SELECT TAG, Language FROM $tempdb_name")
|
||||||
statement?.executeUpdate("DROP TABLE temp_${super.dbName}")
|
statement?.executeUpdate("DROP TABLE $tempdb_name")
|
||||||
Logger.info("${super.dbName} table resorted by TAG" as Any)
|
Logger.info("${super.dbName} table resorted by TAG" as Any)
|
||||||
// reload the local list
|
// reload the local list
|
||||||
Get()
|
Get()
|
||||||
@@ -834,12 +839,14 @@ class MariaDB(
|
|||||||
override fun Resort(): Boolean {
|
override fun Resort(): Boolean {
|
||||||
try {
|
try {
|
||||||
val statement = connection.createStatement()
|
val statement = connection.createStatement()
|
||||||
|
val tempdb_name = "temp_${super.dbName}"
|
||||||
// use a temporary table to reorder the index
|
// use a temporary table to reorder the index
|
||||||
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}")
|
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
|
||||||
statement?.executeUpdate("INSERT INTO temp_${super.dbName} (Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language) SELECT Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language FROM ${super.dbName} ORDER BY Day , Time ")
|
statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
|
||||||
|
statement?.executeUpdate("INSERT INTO $tempdb_name (Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language) SELECT Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language FROM ${super.dbName} ORDER BY Day , Time ")
|
||||||
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
||||||
statement?.executeUpdate("INSERT INTO ${super.dbName} (Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language) SELECT Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language FROM temp_${super.dbName}")
|
statement?.executeUpdate("INSERT INTO ${super.dbName} (Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language) SELECT Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language FROM $tempdb_name")
|
||||||
statement?.executeUpdate("DROP TABLE temp_${super.dbName}")
|
statement?.executeUpdate("DROP TABLE $tempdb_name")
|
||||||
Logger.info("${super.dbName} table resorted by Day and Time" as Any)
|
Logger.info("${super.dbName} table resorted by Day and Time" as Any)
|
||||||
// reload the local list
|
// reload the local list
|
||||||
Get()
|
Get()
|
||||||
@@ -988,7 +995,7 @@ class MariaDB(
|
|||||||
override fun Add(data: BroadcastZones): Boolean {
|
override fun Add(data: BroadcastZones): Boolean {
|
||||||
try {
|
try {
|
||||||
val statement =
|
val statement =
|
||||||
connection.prepareStatement("INSERT INTO ${super.dbName} (description, SoundChannel, Box, Relay) VALUES (?, ?, ?, ?)")
|
connection.prepareStatement("INSERT INTO ${super.dbName} (description, SoundChannel, id, bp) VALUES (?, ?, ?, ?)")
|
||||||
statement?.setString(1, data.description)
|
statement?.setString(1, data.description)
|
||||||
statement?.setString(2, data.SoundChannel)
|
statement?.setString(2, data.SoundChannel)
|
||||||
statement?.setString(3, data.id)
|
statement?.setString(3, data.id)
|
||||||
@@ -1010,7 +1017,7 @@ class MariaDB(
|
|||||||
try {
|
try {
|
||||||
connection.autoCommit = false
|
connection.autoCommit = false
|
||||||
val sql =
|
val sql =
|
||||||
"INSERT INTO ${super.dbName} (description, SoundChannel, Box, Relay) VALUES (?, ?, ?, ?)"
|
"INSERT INTO ${super.dbName} (description, SoundChannel, id, bp) VALUES (?, ?, ?, ?)"
|
||||||
val statement = connection.prepareStatement(sql)
|
val statement = connection.prepareStatement(sql)
|
||||||
for (bz in data) {
|
for (bz in data) {
|
||||||
statement.setString(1, bz.description)
|
statement.setString(1, bz.description)
|
||||||
@@ -1033,7 +1040,7 @@ class MariaDB(
|
|||||||
override fun UpdateByIndex(index: Int, data: BroadcastZones): Boolean {
|
override fun UpdateByIndex(index: Int, data: BroadcastZones): Boolean {
|
||||||
try {
|
try {
|
||||||
val statement =
|
val statement =
|
||||||
connection.prepareStatement("UPDATE ${super.dbName} SET description = ?, SoundChannel = ?, Box = ?, Relay = ? WHERE `index` = ?")
|
connection.prepareStatement("UPDATE ${super.dbName} SET description = ?, SoundChannel = ?, id = ?, bp = ? WHERE `index` = ?")
|
||||||
statement?.setString(1, data.description)
|
statement?.setString(1, data.description)
|
||||||
statement?.setString(2, data.SoundChannel)
|
statement?.setString(2, data.SoundChannel)
|
||||||
statement?.setString(3, data.id)
|
statement?.setString(3, data.id)
|
||||||
@@ -1055,12 +1062,14 @@ class MariaDB(
|
|||||||
override fun Resort(): Boolean {
|
override fun Resort(): Boolean {
|
||||||
try {
|
try {
|
||||||
val statement = connection.createStatement()
|
val statement = connection.createStatement()
|
||||||
|
val tempdb_name = "temp_${super.dbName}"
|
||||||
// use a temporary table to reorder the index
|
// use a temporary table to reorder the index
|
||||||
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}")
|
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
|
||||||
statement?.executeUpdate("INSERT INTO temp_${super.dbName} (description, SoundChannel, Box, Relay) SELECT description, SoundChannel, Box, Relay FROM ${super.dbName} ORDER BY description ")
|
statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
|
||||||
|
statement?.executeUpdate("INSERT INTO $tempdb_name (description, SoundChannel, id, bp) SELECT description, SoundChannel, id, bp FROM ${super.dbName} ORDER BY description ")
|
||||||
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
||||||
statement?.executeUpdate("INSERT INTO ${super.dbName} (description, SoundChannel, Box, Relay) SELECT description, SoundChannel, Box, Relay FROM temp_${super.dbName}")
|
statement?.executeUpdate("INSERT INTO ${super.dbName} (description, SoundChannel, id, bp) SELECT description, SoundChannel, id, bp FROM $tempdb_name")
|
||||||
statement?.executeUpdate("DROP TABLE temp_${super.dbName}")
|
statement?.executeUpdate("DROP TABLE $tempdb_name")
|
||||||
Logger.info("${super.dbName} table resorted by description" as Any)
|
Logger.info("${super.dbName} table resorted by description" as Any)
|
||||||
// reload the local list
|
// reload the local list
|
||||||
Get()
|
Get()
|
||||||
@@ -1076,7 +1085,7 @@ class MariaDB(
|
|||||||
val sheet = workbook.getSheet("BroadcastZones")
|
val sheet = workbook.getSheet("BroadcastZones")
|
||||||
?: throw Exception("No sheet named 'BroadcastZones' found")
|
?: throw Exception("No sheet named 'BroadcastZones' found")
|
||||||
val headerRow = sheet.getRow(0) ?: throw Exception("No header row found")
|
val headerRow = sheet.getRow(0) ?: throw Exception("No header row found")
|
||||||
val headers = arrayOf("Index", "description", "SoundChannel", "Box", "Relay")
|
val headers = arrayOf("Index", "description", "SoundChannel", "id", "bp")
|
||||||
for ((colIndex, header) in headers.withIndex()) {
|
for ((colIndex, header) in headers.withIndex()) {
|
||||||
val cell = headerRow.getCell(colIndex) ?: throw Exception("Header '$header' not found")
|
val cell = headerRow.getCell(colIndex) ?: throw Exception("Header '$header' not found")
|
||||||
if (cell.stringCellValue != header) throw Exception("Header '$header' not found")
|
if (cell.stringCellValue != header) throw Exception("Header '$header' not found")
|
||||||
@@ -1089,9 +1098,9 @@ class MariaDB(
|
|||||||
val row = sheet.getRow(rowIndex) ?: continue
|
val row = sheet.getRow(rowIndex) ?: continue
|
||||||
val description = row.getCell(1)?.stringCellValue ?: continue
|
val description = row.getCell(1)?.stringCellValue ?: continue
|
||||||
val soundChannel = row.getCell(2)?.stringCellValue ?: continue
|
val soundChannel = row.getCell(2)?.stringCellValue ?: continue
|
||||||
val box = row.getCell(3)?.stringCellValue ?: continue
|
val id = row.getCell(3)?.stringCellValue ?: continue
|
||||||
val relay = row.getCell(4)?.stringCellValue ?: continue
|
val bp = row.getCell(4)?.stringCellValue ?: continue
|
||||||
val broadcastZone = BroadcastZones(0u, description, soundChannel, box, relay)
|
val broadcastZone = BroadcastZones(0u, description, soundChannel, id, bp)
|
||||||
_broadcastZonesList.add(broadcastZone)
|
_broadcastZonesList.add(broadcastZone)
|
||||||
}
|
}
|
||||||
return AddAll(_broadcastZonesList)
|
return AddAll(_broadcastZonesList)
|
||||||
@@ -1108,7 +1117,7 @@ class MariaDB(
|
|||||||
val workbook = XSSFWorkbook()
|
val workbook = XSSFWorkbook()
|
||||||
val sheet = workbook.createSheet("BroadcastZones")
|
val sheet = workbook.createSheet("BroadcastZones")
|
||||||
val headerRow = sheet.createRow(0)
|
val headerRow = sheet.createRow(0)
|
||||||
val headers = arrayOf("Index", "description", "SoundChannel", "Box", "Relay")
|
val headers = arrayOf("Index", "description", "SoundChannel", "id", "bp")
|
||||||
for ((colIndex, header) in headers.withIndex()) {
|
for ((colIndex, header) in headers.withIndex()) {
|
||||||
val cell = headerRow.createCell(colIndex)
|
val cell = headerRow.createCell(colIndex)
|
||||||
cell.setCellValue(header)
|
cell.setCellValue(header)
|
||||||
@@ -1119,8 +1128,8 @@ class MariaDB(
|
|||||||
row.createCell(0).setCellValue(resultSet.getString("index"))
|
row.createCell(0).setCellValue(resultSet.getString("index"))
|
||||||
row.createCell(1).setCellValue(resultSet.getString("description"))
|
row.createCell(1).setCellValue(resultSet.getString("description"))
|
||||||
row.createCell(2).setCellValue(resultSet.getString("SoundChannel"))
|
row.createCell(2).setCellValue(resultSet.getString("SoundChannel"))
|
||||||
row.createCell(3).setCellValue(resultSet.getString("Box"))
|
row.createCell(3).setCellValue(resultSet.getString("id"))
|
||||||
row.createCell(4).setCellValue(resultSet.getString("Relay"))
|
row.createCell(4).setCellValue(resultSet.getString("bp"))
|
||||||
}
|
}
|
||||||
for (i in headers.indices) {
|
for (i in headers.indices) {
|
||||||
sheet.autoSizeColumn(i)
|
sheet.autoSizeColumn(i)
|
||||||
@@ -1237,12 +1246,14 @@ class MariaDB(
|
|||||||
override fun Resort(): Boolean {
|
override fun Resort(): Boolean {
|
||||||
try {
|
try {
|
||||||
val statement = connection.createStatement()
|
val statement = connection.createStatement()
|
||||||
|
val tempdb_name = "temp_${super.dbName}"
|
||||||
// use a temporary table to reorder the index
|
// use a temporary table to reorder the index
|
||||||
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}")
|
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
|
||||||
statement?.executeUpdate("INSERT INTO temp_${super.dbName} (Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, Repeat, Language) SELECT Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, Repeat, Language FROM ${super.dbName} ORDER BY `index` ")
|
statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
|
||||||
|
statement?.executeUpdate("INSERT INTO $tempdb_name (Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, `Repeat`, Language) SELECT Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, `Repeat`, Language FROM ${super.dbName} ORDER BY `index` ")
|
||||||
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
||||||
statement?.executeUpdate("INSERT INTO ${super.dbName} (Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, Repeat, Language) SELECT Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, Repeat, Language FROM temp_${super.dbName}")
|
statement?.executeUpdate("INSERT INTO ${super.dbName} (Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, `Repeat`, Language) SELECT Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, `Repeat`, Language FROM $tempdb_name")
|
||||||
statement?.executeUpdate("DROP TABLE temp_${super.dbName}")
|
statement?.executeUpdate("DROP TABLE $tempdb_name")
|
||||||
Logger.info("${super.dbName} table resorted by index" as Any)
|
Logger.info("${super.dbName} table resorted by index" as Any)
|
||||||
// reload the local list
|
// reload the local list
|
||||||
Get()
|
Get()
|
||||||
@@ -1396,12 +1407,14 @@ class MariaDB(
|
|||||||
override fun Resort(): Boolean {
|
override fun Resort(): Boolean {
|
||||||
try {
|
try {
|
||||||
val statement = connection.createStatement()
|
val statement = connection.createStatement()
|
||||||
|
val tempdb_name = "temp_${super.dbName}"
|
||||||
// use a temporary table to reorder the index
|
// use a temporary table to reorder the index
|
||||||
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}")
|
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
|
||||||
statement?.executeUpdate("INSERT INTO temp_${super.dbName} (Date_Time, Source, Type, Message, SB_TAGS) SELECT Date_Time, Source, Type, Message, SB_TAGS FROM ${super.dbName} ORDER BY `index` ")
|
statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
|
||||||
|
statement?.executeUpdate("INSERT INTO $tempdb_name (Date_Time, Source, Type, Message, BroadcastZones) SELECT Date_Time, Source, Type, Message, BroadcastZones FROM ${super.dbName} ")
|
||||||
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
||||||
statement?.executeUpdate("INSERT INTO ${super.dbName} (Date_Time, Source, Type, Message, SB_TAGS) SELECT Date_Time, Source, Type, Message, SB_TAGS FROM temp_${super.dbName}")
|
statement?.executeUpdate("INSERT INTO ${super.dbName} (Date_Time, Source, Type, Message, BroadcastZones) SELECT Date_Time, Source, Type, Message, BroadcastZones FROM $tempdb_name")
|
||||||
statement?.executeUpdate("DROP TABLE temp_${super.dbName}")
|
statement?.executeUpdate("DROP TABLE $tempdb_name")
|
||||||
Logger.info("${super.dbName} table resorted by index" as Any)
|
Logger.info("${super.dbName} table resorted by index" as Any)
|
||||||
// reload the local list
|
// reload the local list
|
||||||
Get()
|
Get()
|
||||||
@@ -1423,7 +1436,7 @@ class MariaDB(
|
|||||||
val workbook = XSSFWorkbook()
|
val workbook = XSSFWorkbook()
|
||||||
val sheet = workbook.createSheet("QueuePaging")
|
val sheet = workbook.createSheet("QueuePaging")
|
||||||
val headerRow = sheet.createRow(0)
|
val headerRow = sheet.createRow(0)
|
||||||
val headers = arrayOf("Index", "Date_Time", "Source", "Type", "Message", "SB_TAGS")
|
val headers = arrayOf("Index", "Date_Time", "Source", "Type", "Message", "BroadcastZones")
|
||||||
for ((colIndex, header) in headers.withIndex()) {
|
for ((colIndex, header) in headers.withIndex()) {
|
||||||
val cell = headerRow.createCell(colIndex)
|
val cell = headerRow.createCell(colIndex)
|
||||||
cell.setCellValue(header)
|
cell.setCellValue(header)
|
||||||
@@ -1436,7 +1449,7 @@ class MariaDB(
|
|||||||
row.createCell(2).setCellValue(resultSet.getString("Source"))
|
row.createCell(2).setCellValue(resultSet.getString("Source"))
|
||||||
row.createCell(3).setCellValue(resultSet.getString("Type"))
|
row.createCell(3).setCellValue(resultSet.getString("Type"))
|
||||||
row.createCell(4).setCellValue(resultSet.getString("Message"))
|
row.createCell(4).setCellValue(resultSet.getString("Message"))
|
||||||
row.createCell(5).setCellValue(resultSet.getString("SB_TAGS"))
|
row.createCell(5).setCellValue(resultSet.getString("BroadcastZones"))
|
||||||
}
|
}
|
||||||
for (i in headers.indices) {
|
for (i in headers.indices) {
|
||||||
sheet.autoSizeColumn(i)
|
sheet.autoSizeColumn(i)
|
||||||
@@ -1557,12 +1570,14 @@ class MariaDB(
|
|||||||
override fun Resort(): Boolean {
|
override fun Resort(): Boolean {
|
||||||
try {
|
try {
|
||||||
val statement = connection.createStatement()
|
val statement = connection.createStatement()
|
||||||
|
val tempdb_name = "temp_${super.dbName}"
|
||||||
// use a temporary table to reorder the index
|
// use a temporary table to reorder the index
|
||||||
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}")
|
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
|
||||||
statement?.executeUpdate("INSERT INTO temp_${super.dbName} (channel, ip) SELECT channel, ip FROM ${super.dbName} ORDER BY `index` ")
|
statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
|
||||||
|
statement?.executeUpdate("INSERT INTO $tempdb_name (channel, ip) SELECT channel, ip FROM ${super.dbName} ORDER BY `index` ")
|
||||||
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
||||||
statement?.executeUpdate("INSERT INTO ${super.dbName} (channel, ip) SELECT channel, ip FROM temp_${super.dbName}")
|
statement?.executeUpdate("INSERT INTO ${super.dbName} (channel, ip) SELECT channel, ip FROM $tempdb_name")
|
||||||
statement?.executeUpdate("DROP TABLE temp_${super.dbName}")
|
statement?.executeUpdate("DROP TABLE $tempdb_name")
|
||||||
Logger.info("${super.dbName} table resorted by index" as Any)
|
Logger.info("${super.dbName} table resorted by index" as Any)
|
||||||
// reload the local list
|
// reload the local list
|
||||||
Get()
|
Get()
|
||||||
@@ -1770,19 +1785,21 @@ class MariaDB(
|
|||||||
override fun Resort(): Boolean {
|
override fun Resort(): Boolean {
|
||||||
try {
|
try {
|
||||||
val statement = connection.createStatement()
|
val statement = connection.createStatement()
|
||||||
|
val tempdb_name = "temp_${super.dbName}"
|
||||||
// use a temporary table to reorder the index
|
// use a temporary table to reorder the index
|
||||||
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}")
|
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
|
||||||
|
statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
|
||||||
statement?.executeUpdate(
|
statement?.executeUpdate(
|
||||||
"INSERT INTO temp_${super.dbName} (datenya, timenya, machine, description) " +
|
"INSERT INTO $tempdb_name (datenya, timenya, machine, description) " +
|
||||||
"SELECT datenya, timenya, machine, description FROM ${super.dbName} " +
|
"SELECT datenya, timenya, machine, description FROM ${super.dbName} " +
|
||||||
"ORDER BY datenya , timenya , machine "
|
"ORDER BY datenya , timenya , machine "
|
||||||
)
|
)
|
||||||
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
||||||
statement?.executeUpdate(
|
statement?.executeUpdate(
|
||||||
"INSERT INTO ${super.dbName} (datenya, timenya, machine, description) " +
|
"INSERT INTO ${super.dbName} (datenya, timenya, machine, description) " +
|
||||||
"SELECT datenya, timenya, machine, description FROM temp_${super.dbName}"
|
"SELECT datenya, timenya, machine, description FROM $tempdb_name"
|
||||||
)
|
)
|
||||||
statement?.executeUpdate("DROP TABLE temp_${super.dbName}")
|
statement?.executeUpdate("DROP TABLE $tempdb_name")
|
||||||
Logger.info("${super.dbName} table resorted by datenya, timenya, machine" as Any)
|
Logger.info("${super.dbName} table resorted by datenya, timenya, machine" as Any)
|
||||||
// reload the local list
|
// reload the local list
|
||||||
Get()
|
Get()
|
||||||
@@ -1947,12 +1964,14 @@ class MariaDB(
|
|||||||
override fun Resort(): Boolean {
|
override fun Resort(): Boolean {
|
||||||
try {
|
try {
|
||||||
val statement = connection.createStatement()
|
val statement = connection.createStatement()
|
||||||
|
val tempdb_name = "temp_${super.dbName}"
|
||||||
// use a temporary table to reorder the index
|
// use a temporary table to reorder the index
|
||||||
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}")
|
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
|
||||||
statement?.executeUpdate("INSERT INTO temp_${super.dbName} (username, password, location, soundbank_tags, messagebank_ann_id, broadcastzones) SELECT username, password, location, soundbank_tags, messagebank_ann_id, broadcastzones FROM ${super.dbName} ORDER BY username ")
|
statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
|
||||||
|
statement?.executeUpdate("INSERT INTO $tempdb_name (username, password, location, airline_tags, city_tags, messagebank_ann_id, broadcastzones) SELECT username, password, location, airline_tags, city_tags, messagebank_ann_id, broadcastzones FROM ${super.dbName} ORDER BY username ")
|
||||||
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
||||||
statement?.executeUpdate("INSERT INTO ${super.dbName} (username, password, location, soundbank_tags, messagebank_ann_id, broadcastzones) SELECT username, password, location, soundbank_tags, messagebank_ann_id, broadcastzones FROM temp_${super.dbName}")
|
statement?.executeUpdate("INSERT INTO ${super.dbName} (username, password, location, airline_tags, city_tags, messagebank_ann_id, broadcastzones) SELECT username, password, location, airline_tags, city_tags, messagebank_ann_id, broadcastzones FROM $tempdb_name")
|
||||||
statement?.executeUpdate("DROP TABLE temp_${super.dbName}")
|
statement?.executeUpdate("DROP TABLE $tempdb_name")
|
||||||
Logger.info("${super.dbName} table resorted by index" as Any)
|
Logger.info("${super.dbName} table resorted by index" as Any)
|
||||||
// reload the local list
|
// reload the local list
|
||||||
Get()
|
Get()
|
||||||
@@ -2254,24 +2273,21 @@ class MariaDB(
|
|||||||
* @return a list of Soundbank with Category City and matching tag
|
* @return a list of Soundbank with Category City and matching tag
|
||||||
*/
|
*/
|
||||||
fun Find_Soundbank_City(tag: String) : List<Soundbank>{
|
fun Find_Soundbank_City(tag: String) : List<Soundbank>{
|
||||||
val lowerTag = tag.lowercase()
|
|
||||||
return soundDB.List
|
return soundDB.List
|
||||||
.filter{ it.Category== Category.City.name }
|
.filter{ it.Category== Category.City.name }
|
||||||
.filter { it.TAG.lowercase()==lowerTag}
|
.filter { it.TAG.equals(tag,true)}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Find_Soundbank_AirplaneName(tag: String) : List<Soundbank>{
|
fun Find_Soundbank_AirplaneName(tag: String) : List<Soundbank>{
|
||||||
val lowerTag = tag.lowercase()
|
|
||||||
return soundDB.List
|
return soundDB.List
|
||||||
.filter{ it.Category== Category.Airplane_Name.name }
|
.filter{ it.Category== Category.Airplane_Name.name }
|
||||||
.filter { it.TAG.lowercase()==lowerTag}
|
.filter { it.TAG.equals(tag,true)}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Find_Soundbank_Places(tag: String) : List<Soundbank>{
|
fun Find_Soundbank_Places(tag: String) : List<Soundbank>{
|
||||||
val lowerTag = tag.lowercase()
|
|
||||||
return soundDB.List
|
return soundDB.List
|
||||||
.filter{ it.Category== Category.Places.name }
|
.filter{ it.Category== Category.Places.name }
|
||||||
.filter { it.TAG.lowercase()==lowerTag}
|
.filter { it.TAG.equals(tag,true)}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Find_Soundbank_Shalat(tag: String) : List<Soundbank>{
|
fun Find_Soundbank_Shalat(tag: String) : List<Soundbank>{
|
||||||
@@ -2282,25 +2298,85 @@ class MariaDB(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun Find_Soundbank_Sequence(tag: String) : List<Soundbank>{
|
fun Find_Soundbank_Sequence(tag: String) : List<Soundbank>{
|
||||||
val lowerTag = tag.lowercase()
|
|
||||||
|
|
||||||
return soundDB.List
|
return soundDB.List
|
||||||
.filter{ it.Category== Category.Sequence.name }
|
.filter{ it.Category== Category.Sequence.name }
|
||||||
.filter { it.TAG.lowercase()==lowerTag}
|
.filter { it.TAG.equals(tag,true)}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Find_Soundbank_Reason(tag: String) : List<Soundbank>{
|
fun Find_Soundbank_Reason(tag: String) : List<Soundbank>{
|
||||||
val lowerTag = tag.lowercase()
|
|
||||||
return soundDB.List
|
return soundDB.List
|
||||||
.filter{ it.Category== Category.Reason.name }
|
.filter{ it.Category== Category.Reason.name }
|
||||||
.filter { it.TAG.lowercase()==lowerTag}
|
.filter { it.TAG.equals(tag,true)}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Find_Soundbank_Procedure(tag: String) : List<Soundbank> {
|
fun Find_Soundbank_Procedure(tag: String) : List<Soundbank> {
|
||||||
val lowerTag = tag.lowercase()
|
|
||||||
return soundDB.List
|
return soundDB.List
|
||||||
.filter { it.Category == Category.Procedure.name }
|
.filter { it.Category == Category.Procedure.name }
|
||||||
.filter { it.TAG.lowercase() == lowerTag }
|
.filter { it.TAG.equals(tag,true) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all distinct airline code tags from soundbank
|
||||||
|
* @return a list of distinct airline code tags sorted alphabetically
|
||||||
|
*/
|
||||||
|
fun Get_AirlineCode_Tags(): List<String> {
|
||||||
|
return soundDB.List
|
||||||
|
.filter { it.Category == Category.Airline_Code.name }
|
||||||
|
.map { it.TAG }
|
||||||
|
.distinct()
|
||||||
|
.sorted()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all distinct city tags from soundbank
|
||||||
|
* @return a list of distinct city tags sorted alphabetically
|
||||||
|
*/
|
||||||
|
fun Get_City_Tags(): List<String> {
|
||||||
|
return soundDB.List
|
||||||
|
.filter { it.Category == Category.City.name }
|
||||||
|
.map { it.TAG }
|
||||||
|
.distinct()
|
||||||
|
.sorted()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all distinct message ID from messagebank
|
||||||
|
* @return a list of distinct ANN_ID sorted numerically
|
||||||
|
*/
|
||||||
|
fun Get_MessageID_List(): List<UInt> {
|
||||||
|
return messageDB.List
|
||||||
|
.distinctBy { it.ANN_ID }
|
||||||
|
.map { it.ANN_ID }
|
||||||
|
.sorted()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all distinct broadcast zone descriptions from broadcastDB
|
||||||
|
* @return a list of distinct broadcast zone descriptions sorted alphabetically
|
||||||
|
*/
|
||||||
|
fun Get_BroadcastZone_List(): List<String> {
|
||||||
|
return broadcastDB.List
|
||||||
|
.distinctBy { it.description }
|
||||||
|
.map { it.description }
|
||||||
|
.sorted()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all distinct sound channel from soundchannelDB
|
||||||
|
* @return a list of distinct sound channel sorted alphabetically
|
||||||
|
*/
|
||||||
|
fun Get_SoundChannel_List(): List<String> {
|
||||||
|
return soundchannelDB.List
|
||||||
|
.distinctBy { it.channel }
|
||||||
|
.map { it.channel }
|
||||||
|
.sorted()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a username already exists in the userDB (case-insensitive)
|
||||||
|
*/
|
||||||
|
fun Username_exists(username: String): Boolean {
|
||||||
|
return userDB.List.any { it.username.equals(username, ignoreCase = true) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,4 +8,27 @@ data class Messagebank(
|
|||||||
var Voice_Type: String,
|
var Voice_Type: String,
|
||||||
var Message_Detail: String,
|
var Message_Detail: String,
|
||||||
var Message_TAGS: String
|
var Message_TAGS: String
|
||||||
)
|
) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if all fields are not empty and ANN_ID > 0
|
||||||
|
*/
|
||||||
|
fun isNotEmpty(): Boolean{
|
||||||
|
if (Description.isNotEmpty()){
|
||||||
|
if (Language.isNotEmpty()){
|
||||||
|
if (Voice_Type.isNotEmpty()){
|
||||||
|
if (Message_Detail.isNotEmpty()){
|
||||||
|
if (Message_TAGS.isNotEmpty()){
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "Messagebank(index=$index, Description='$Description', Language='$Language', ANN_ID=$ANN_ID, Voice_Type='$Voice_Type', Message_Detail='$Message_Detail', Message_TAGS='$Message_TAGS')"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,13 @@
|
|||||||
package database
|
package database
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
data class QueueFids(var index: UInt, var ALCODE: String, var FLNUM: String, var ORIGIN: String, var ETAD: String, var FREMARK: String)
|
data class QueueFids(var index: UInt, var ALCODE: String, var FLNUM: String, var ORIGIN: String, var ETAD: String, var FREMARK: String){
|
||||||
|
|
||||||
|
fun isNotEmpty(): Boolean {
|
||||||
|
return ALCODE.isNotEmpty() && FLNUM.isNotEmpty() && ORIGIN.isNotEmpty() && ETAD.isNotEmpty() && FREMARK.isNotEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "QueueFids(index=$index, ALCODE='$ALCODE', FLNUM='$FLNUM', ORIGIN='$ORIGIN', ETAD='$ETAD', FREMARK='$FREMARK')"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
package database
|
package database
|
||||||
|
|
||||||
data class QueuePaging(var index: UInt, var Date_Time: String, var Source: String, var Type: String, var Message: String, var BroadcastZones: String){
|
data class QueuePaging(var index: UInt, var Date_Time: String, var Source: String, var Type: String, var Message: String, var BroadcastZones: String){
|
||||||
|
fun isNotEmpty(): Boolean {
|
||||||
|
return Date_Time.isNotEmpty() && Source.isNotEmpty() && Type.isNotEmpty() && Message.isNotEmpty() && BroadcastZones.isNotEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "QueuePaging(index=$index, Date_Time='$Date_Time', Source='$Source', Type='$Type', Message='$Message', BroadcastZones='$BroadcastZones')"
|
return "QueuePaging(index=$index, Date_Time='$Date_Time', Source='$Source', Type='$Type', Message='$Message', BroadcastZones='$BroadcastZones')"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,12 @@ package database
|
|||||||
|
|
||||||
data class QueueTable(var index: UInt, var Date_Time: String, var Source: String, var Type: String, var Message: String, var SB_TAGS: String, var BroadcastZones: String, var Repeat: UInt, var Language: String){
|
data class QueueTable(var index: UInt, var Date_Time: String, var Source: String, var Type: String, var Message: String, var SB_TAGS: String, var BroadcastZones: String, var Repeat: UInt, var Language: String){
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if all fields are not empty
|
||||||
|
*/
|
||||||
|
fun isNotEmpty(): Boolean {
|
||||||
|
return Date_Time.isNotEmpty() && Source.isNotEmpty() && Type.isNotEmpty() && Message.isNotEmpty() && SB_TAGS.isNotEmpty() && BroadcastZones.isNotEmpty() && Language.isNotEmpty()
|
||||||
|
}
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "QueueTable(index=$index, Date_Time='$Date_Time', Source='$Source', Type='$Type', Message='$Message', SB_TAGS='$SB_TAGS', BroadcastZones='$BroadcastZones', Repeat=$Repeat, Language='$Language')"
|
return "QueueTable(index=$index, Date_Time='$Date_Time', Source='$Source', Type='$Type', Message='$Message', SB_TAGS='$SB_TAGS', BroadcastZones='$BroadcastZones', Repeat=$Repeat, Language='$Language')"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,4 +10,13 @@ data class ScheduleBank(
|
|||||||
var Repeat: UByte,
|
var Repeat: UByte,
|
||||||
var Enable: Boolean,
|
var Enable: Boolean,
|
||||||
var BroadcastZones: String,
|
var BroadcastZones: String,
|
||||||
var Language: String)
|
var Language: String) {
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "ScheduleBank(index=$index, Description='$Description', Day='$Day', Time='$Time', Soundpath='$Soundpath', Repeat=$Repeat, Enable=$Enable, BroadcastZones='$BroadcastZones', Language='$Language')"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isNotEmpty() : Boolean{
|
||||||
|
return Description.isNotEmpty() && Day.isNotEmpty() && Time.isNotEmpty() && Soundpath.isNotEmpty() && BroadcastZones.isNotEmpty() && Language.isNotEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,17 @@
|
|||||||
package database
|
package database
|
||||||
|
|
||||||
@Suppress("unused")
|
data class SoundChannel(val index: UInt, val channel: String, val ip: String) {
|
||||||
data class SoundChannel(val index: UInt, val channel: String, val ip: String)
|
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "SoundChannel(index=$index, channel='$channel', ip='$ip')"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if both channel and ip are not empty
|
||||||
|
* @return true if both channel and ip are not empty, false otherwise
|
||||||
|
*/
|
||||||
|
fun isNotEmpty() : Boolean {
|
||||||
|
return channel.isNotEmpty() && ip.isNotEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package database
|
package database
|
||||||
|
|
||||||
|
|
||||||
data class Soundbank(
|
data class Soundbank(
|
||||||
var index: UInt,
|
var index: UInt,
|
||||||
var Description: String,
|
var Description: String,
|
||||||
@@ -8,4 +9,32 @@ data class Soundbank(
|
|||||||
var Language: String,
|
var Language: String,
|
||||||
var VoiceType: String,
|
var VoiceType: String,
|
||||||
var Path: String,
|
var Path: String,
|
||||||
)
|
){
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if all fields are not empty
|
||||||
|
*/
|
||||||
|
fun isNotEmpty(): Boolean{
|
||||||
|
return Description.isNotEmpty() && TAG.isNotEmpty() && Category.isNotEmpty() && Language.isNotEmpty() && VoiceType.isNotEmpty() && Path.isNotEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if Category is valid
|
||||||
|
*/
|
||||||
|
fun ValidCategory() : Boolean{
|
||||||
|
return content.Category.entries.any{ it.name == this.Category}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if VoiceType is valid
|
||||||
|
*/
|
||||||
|
fun ValidVoiceType() : Boolean{
|
||||||
|
return content.VoiceType.entries.any{ it.name == this.VoiceType}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Check if Language is valid
|
||||||
|
*/
|
||||||
|
fun ValidLanguage() : Boolean{
|
||||||
|
return content.Language.entries.any{ it.name == this.Language}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,26 @@ data class UserDB(var index: UInt, var username: String, var password: String, v
|
|||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "UserDB(index=$index, username='$username', location='$location', airline_tags='$airline_tags', city_tags='$city_tags', messagebank_ann_id='$messagebank_ann_id', broadcastzones='$broadcastzones')"
|
return "UserDB(index=$index, username='$username', location='$location', airline_tags='$airline_tags', city_tags='$city_tags', messagebank_ann_id='$messagebank_ann_id', broadcastzones='$broadcastzones')"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isNotEmpty() : Boolean {
|
||||||
|
return username.isNotEmpty() && password.isNotEmpty() && location.isNotEmpty() && airline_tags.isNotEmpty() && city_tags.isNotEmpty() && messagebank_ann_id.isNotEmpty() && broadcastzones.isNotEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares this UserDB object with another UserDB object for equality.
|
||||||
|
* Two UserDB objects are considered equal if all their properties are equal, except for the index property.
|
||||||
|
*/
|
||||||
|
fun isEqual(other: UserDB?): Boolean {
|
||||||
|
if (other == null) return false
|
||||||
|
return this.username == other.username &&
|
||||||
|
this.password == other.password &&
|
||||||
|
this.location == other.location &&
|
||||||
|
this.airline_tags == other.airline_tags &&
|
||||||
|
this.city_tags == other.city_tags &&
|
||||||
|
this.messagebank_ann_id == other.messagebank_ann_id &&
|
||||||
|
this.broadcastzones == other.broadcastzones
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
266
src/toa/Vx3K.kt
Normal file
266
src/toa/Vx3K.kt
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
package toa
|
||||||
|
|
||||||
|
import codes.Somecodes
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.tinylog.Logger
|
||||||
|
import java.net.Inet4Address
|
||||||
|
import java.net.InetSocketAddress
|
||||||
|
import java.net.Socket
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
import java.nio.ByteOrder
|
||||||
|
import java.util.function.BiConsumer
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VX3K Protocol
|
||||||
|
* @param ipaddress IP address of the VX3K device, default to 192.168.14.1
|
||||||
|
* @param port Port number of the VX3K device, from 50050-50053 default to 50053
|
||||||
|
*/
|
||||||
|
class Vx3K(val ipaddress : String = "192.168.14.1", val port : Int = 50053) {
|
||||||
|
private val remotesocket : InetSocketAddress
|
||||||
|
init{
|
||||||
|
if (port !in 50050..50053){
|
||||||
|
throw IllegalArgumentException("Port number must be between 50050 and 50053")
|
||||||
|
}
|
||||||
|
try{
|
||||||
|
val inet = Inet4Address.getByName(ipaddress)
|
||||||
|
remotesocket = InetSocketAddress(inet, port)
|
||||||
|
} catch (_ : Exception){
|
||||||
|
throw IllegalArgumentException("Invalid IP address: $ipaddress")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to the VX3K device
|
||||||
|
* @param timeout Connection timeout in milliseconds, default to 30000 ms
|
||||||
|
*/
|
||||||
|
fun Connect(timeout: Int = 30000){
|
||||||
|
try{
|
||||||
|
val socket = Socket()
|
||||||
|
// read timeout 5 seconds
|
||||||
|
socket.soTimeout = 5000
|
||||||
|
socket.connect(remotesocket, timeout)
|
||||||
|
} catch (e : Exception){
|
||||||
|
Logger.error { "Failed to connect with ${remotesocket.hostName}:${remotesocket.port}, Message: ${e.message}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Contact Input (Commmand 0x1001)
|
||||||
|
* @param ID : Device ID for VX3K, range 0 - 31
|
||||||
|
* @param CIN : Contact Input Number, range 0 - 15 for normal terminal, 16 for emergency contact input1, 17 for emergency contact input2
|
||||||
|
* @param isON : true for ON, false for OFF
|
||||||
|
* @param cb : Callback function with parameters (success: Boolean, message: String)
|
||||||
|
*/
|
||||||
|
fun VirtualCIN(ID: Short, CIN: Short, isON: Boolean, cb : BiConsumer<Boolean, String>){
|
||||||
|
val commandID = 0x1001.toShort()
|
||||||
|
if (ID !in 0..31){
|
||||||
|
cb.accept(false, "ID must be between 0 and 31")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (CIN !in 0..17){
|
||||||
|
cb.accept(false, "CIN must be between 0 and 17")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val payload = ByteBuffer.allocate(6).order(ByteOrder.BIG_ENDIAN)
|
||||||
|
payload.putShort(ID)
|
||||||
|
payload.putShort(CIN)
|
||||||
|
payload.putShort(if (isON) 1 else 0)
|
||||||
|
val command = Make_Request_Command(commandID, payload.array())
|
||||||
|
Send_Receive(command,8){
|
||||||
|
success, reply ->
|
||||||
|
if (success){
|
||||||
|
val bb = ByteBuffer.wrap(reply).order(ByteOrder.BIG_ENDIAN)
|
||||||
|
val resp_commandID = bb.short
|
||||||
|
val resp_code = bb.short
|
||||||
|
if (resp_commandID==commandID){
|
||||||
|
if (resp_code.toInt() == 0){
|
||||||
|
cb.accept(true, "Virtual CIN command sent successfully to ${remotesocket.hostName}:${remotesocket.port}")
|
||||||
|
} else {
|
||||||
|
cb.accept(false, "Virtual CIN command failed with response code $resp_code from ${remotesocket.hostName}:${remotesocket.port}")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cb.accept(false, "Invalid response command ID $resp_commandID from ${remotesocket.hostName}:${remotesocket.port}")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cb.accept(false, "Failed to send Virtual CIN command to ${remotesocket.hostName}:${remotesocket.port}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open / Close Audio Input in Broadcast Pattern (Command 0x1003 for old firmware, 0x1101 for new firmware)
|
||||||
|
* @param NewFirmware Set to true if using new firmware version
|
||||||
|
* @param ID Device ID for VX3K, range 0 - 39 for new firmware, 0 - 31 for old firmware
|
||||||
|
* @param Channel Audio Input Channel, range 0 - 3
|
||||||
|
* @param BroadcastZones Vx3K_BroadcastZone object with selected broadcast zones
|
||||||
|
* @param cb Callback function with parameters (success: Boolean, message: String)
|
||||||
|
*/
|
||||||
|
fun AudioInput_BroadcastPattern(NewFirmware : Boolean, ID: Short, Channel: Short, BroadcastZones: Vx3K_BroadcastZone, cb: BiConsumer<Boolean, String>){
|
||||||
|
val CommandID = if (NewFirmware) 0x1101.toShort() else 0x1003.toShort()
|
||||||
|
if (NewFirmware){
|
||||||
|
if (ID !in 0..39){
|
||||||
|
cb.accept(false, "ID must be between 0 and 39")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ID !in 0..31){
|
||||||
|
cb.accept(false, "ID must be between 0 and 31")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Channel !in 0..3){
|
||||||
|
cb.accept(false, "Channel must be between 0 and 3")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val payload = ByteBuffer.allocate(if (NewFirmware) 86 else 70).order(ByteOrder.BIG_ENDIAN)
|
||||||
|
// Audio Input = 1
|
||||||
|
payload.putShort(1)
|
||||||
|
// VX3K ID
|
||||||
|
payload.putShort(ID)
|
||||||
|
// Audio Input Channel
|
||||||
|
payload.putShort(Channel)
|
||||||
|
// Broadcast Pattern Zones
|
||||||
|
payload.put(BroadcastZones.payload)
|
||||||
|
val command = Make_Request_Command(CommandID, payload.array())
|
||||||
|
Send_Receive(command,8){
|
||||||
|
success, reply ->
|
||||||
|
if (success){
|
||||||
|
val bb = ByteBuffer.wrap(reply).order(ByteOrder.BIG_ENDIAN)
|
||||||
|
val resp_commandID = bb.short
|
||||||
|
val resp_code = bb.short
|
||||||
|
if (resp_commandID==CommandID){
|
||||||
|
if (resp_code.toInt() == 0){
|
||||||
|
cb.accept(true, "Open Audio Input Broadcast command sent successfully to ${remotesocket.hostName}:${remotesocket.port}")
|
||||||
|
} else {
|
||||||
|
cb.accept(false, "Open Audio Input Broadcast command failed with response code $resp_code from ${remotesocket.hostName}:${remotesocket.port}")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cb.accept(false, "Invalid response command ID $resp_commandID from ${remotesocket.hostName}:${remotesocket.port}")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cb.accept(false, "Failed to send Open Audio Input Broadcast command to ${remotesocket.hostName}:${remotesocket.port}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open / Close Network Broadcast Pattern (Command 0x1102 for old firmware, 0x1104 for new firmware)
|
||||||
|
* @param NewFirmware Set to true if using new firmware version
|
||||||
|
* @param multicastIP Multicast IP address in string format, from 224.0.0.0 ~ 239.255.255.255, default to 224.0.0.1
|
||||||
|
* @param port UDP Port number, port 5000-5255 is invalid, default to 5300
|
||||||
|
* @param priority Broadcast Priority value, range 1 - 1024, default to 512 (to be checked later)
|
||||||
|
* @param isBGM true for BGM, false for Paging
|
||||||
|
* @param SSRC SSRC value, range 1 - 65535, default to 1 (to be checked later)
|
||||||
|
* @param payloadType RTP Payload Type, range 0 - 127, default to 0 (to be checked later)
|
||||||
|
* @param payloadSize RTP Payload Size, range 0 - 1500, default to 1000 (to be checked later)
|
||||||
|
* @param BroadcastZones Vx3K_BroadcastZone object with selected broadcast zones
|
||||||
|
* @param cb Callback function with parameters (success: Boolean, message: String)
|
||||||
|
*/
|
||||||
|
fun Network_BroadcastPattern(NewFirmware: Boolean,multicastIP: String = "224.0.0.1", port: Int = 5300, priority: Int = 512, isBGM: Boolean, SSRC: UShort = 1u, payloadType: Short = 0, payloadSize: Short = 1000, BroadcastZones: Vx3K_BroadcastZone, cb:BiConsumer<Boolean, String>){
|
||||||
|
val CommandID = if (NewFirmware) 0x1104.toShort() else 0x1102.toShort()
|
||||||
|
if (Somecodes.ValidIPV4(multicastIP)){
|
||||||
|
val inet = Inet4Address.getByName(multicastIP)
|
||||||
|
if (inet.isMulticastAddress){
|
||||||
|
if (port in 5256..65535){
|
||||||
|
if (priority in 1..1024){
|
||||||
|
if (SSRC in 1u..65535u){
|
||||||
|
if (payloadType in 0..127){
|
||||||
|
if (payloadSize in 0..1500){
|
||||||
|
val jitterbuffer = 1000 //TODO 32 signed integer, to be checked later
|
||||||
|
val payload = ByteBuffer.allocate(if (NewFirmware) 100 else 84)
|
||||||
|
payload.put(inet.address)
|
||||||
|
payload.putShort(port.toShort())
|
||||||
|
payload.putShort(priority.toShort())
|
||||||
|
payload.putShort(if (isBGM) 1 else 0)
|
||||||
|
payload.putShort(SSRC.toShort())
|
||||||
|
payload.putShort(payloadType)
|
||||||
|
payload.putShort(payloadSize)
|
||||||
|
payload.putInt(jitterbuffer)
|
||||||
|
payload.put(BroadcastZones.payload)
|
||||||
|
val command = Make_Request_Command(CommandID, payload.array())
|
||||||
|
Send_Receive(command,10){
|
||||||
|
success, reply ->
|
||||||
|
if (success){
|
||||||
|
val bb = ByteBuffer.wrap(reply).order(ByteOrder.BIG_ENDIAN)
|
||||||
|
val resp_commandID = bb.short
|
||||||
|
val resp_code = bb.short
|
||||||
|
// buang 2 short
|
||||||
|
bb.short
|
||||||
|
bb.short
|
||||||
|
val sound_source_number = bb.short
|
||||||
|
if (resp_commandID==CommandID){
|
||||||
|
if (resp_code.toInt() == 0){
|
||||||
|
cb.accept(true, "Open Network Broadcast command sent successfully to ${remotesocket.hostName}:${remotesocket.port}, Sound Source Number: $sound_source_number")
|
||||||
|
} else {
|
||||||
|
cb.accept(false, "Open Network Broadcast command failed with response code $resp_code from ${remotesocket.hostName}:${remotesocket.port}")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cb.accept(false, "Invalid response command ID $resp_commandID from ${remotesocket.hostName}:${remotesocket.port}")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cb.accept(false, "Failed to send Open Network Broadcast command to ${remotesocket.hostName}:${remotesocket.port}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else cb.accept(false, "Payload Size must be between 0 and 1500")
|
||||||
|
} else cb.accept(false, "Payload Type must be between 0 and 127")
|
||||||
|
} else cb.accept(false, "SSRC must be between 1 and 65535")
|
||||||
|
} else cb.accept(false, "Priority must be between 1 and 1024")
|
||||||
|
} else cb.accept(false, "Port must be greater than 5255 and less than 65536")
|
||||||
|
} else cb.accept(false, "multicastIP must between 224.0.0.0 ~ 239.255.255.255")
|
||||||
|
} else cb.accept(false, "Multicast IP address invalid")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send command and receive reply from VX3K device
|
||||||
|
* @param command Command byte array to send
|
||||||
|
* @param expectedlength Expected length of the reply byte array
|
||||||
|
* @param cb Callback function with parameters (success: Boolean, reply: ByteArray)
|
||||||
|
*/
|
||||||
|
private fun Send_Receive(command: ByteArray, expectedlength: Int, cb : BiConsumer<Boolean, ByteArray>){
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
try{
|
||||||
|
val tcp = Socket()
|
||||||
|
tcp.soTimeout = 5000
|
||||||
|
tcp.connect(remotesocket, 30000)
|
||||||
|
val outstream = tcp.getOutputStream()
|
||||||
|
val instream = tcp.getInputStream()
|
||||||
|
outstream.write(command)
|
||||||
|
outstream.flush()
|
||||||
|
val reply = ByteArray(expectedlength)
|
||||||
|
instream.read(reply)
|
||||||
|
outstream.close()
|
||||||
|
instream.close()
|
||||||
|
tcp.close()
|
||||||
|
cb.accept(true, reply)
|
||||||
|
} catch (_: Exception){
|
||||||
|
cb.accept(false, ByteArray(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap payload into VX3K Request Command format
|
||||||
|
* @param commandID Command ID
|
||||||
|
* @param Payload Payload data
|
||||||
|
* @return ByteArray of the complete command
|
||||||
|
*/
|
||||||
|
private fun Make_Request_Command(commandID: Short, Payload: ByteArray) : ByteArray {
|
||||||
|
val result = ByteBuffer.allocate(8+Payload.size).order(ByteOrder.BIG_ENDIAN)
|
||||||
|
// command ID (2 bytes)
|
||||||
|
result.putShort(commandID)
|
||||||
|
// Response code (2 bytes), set 0 for Request
|
||||||
|
result.putShort(0)
|
||||||
|
// command length = command header (8 bytes) + payload size
|
||||||
|
result.putShort((Payload.size+8).toShort())
|
||||||
|
// flag and reserver = 0 (2 bytes)
|
||||||
|
result.putShort(0)
|
||||||
|
result.put(Payload)
|
||||||
|
// read to byte array
|
||||||
|
return result.array()
|
||||||
|
}
|
||||||
|
}
|
||||||
67
src/toa/Vx3K_BroadcastZone.kt
Normal file
67
src/toa/Vx3K_BroadcastZone.kt
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package toa
|
||||||
|
|
||||||
|
import kotlin.experimental.and
|
||||||
|
import kotlin.experimental.or
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VX3K Broadcast Zone Configuration
|
||||||
|
* Old Firmware: Broadcast Zone 1 - 512
|
||||||
|
* New Firmware: Broadcast Zone 1 - 640
|
||||||
|
* @param NewFirmware Set to true if using new firmware version
|
||||||
|
*/
|
||||||
|
@Suppress("unused")
|
||||||
|
class Vx3K_BroadcastZone(val NewFirmware: Boolean = false) {
|
||||||
|
val payload: ByteArray = if (NewFirmware) ByteArray(80) else ByteArray(64)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a zone as active
|
||||||
|
* @param zonenumber Zone number to set (1 to 512 for old firmware, 1 to 640 for new firmware)
|
||||||
|
*/
|
||||||
|
fun SetZone(zonenumber: Int){
|
||||||
|
if (zonenumber<1) throw Exception("Minimum zone number is 1")
|
||||||
|
if (NewFirmware){
|
||||||
|
if (zonenumber>640) throw Exception("Maximum zone number is 640 for new firmware")
|
||||||
|
} else {
|
||||||
|
if (zonenumber>512) throw Exception("Maximum zone number is 512 for old firmware")
|
||||||
|
}
|
||||||
|
|
||||||
|
val byteIndex = (zonenumber - 1) / 8
|
||||||
|
val bitIndex = (zonenumber - 1) % 8
|
||||||
|
payload[byteIndex] = payload[byteIndex] or (1 shl bitIndex).toByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear a zone (set as inactive)
|
||||||
|
* @param zonenumber Zone number to clear (1 to 512 for old firmware, 1 to 640 for new firmware)
|
||||||
|
*/
|
||||||
|
fun ClearZone(zonenumber: Int){
|
||||||
|
if (zonenumber<1) throw Exception("Minimum zone number is 1")
|
||||||
|
if (NewFirmware){
|
||||||
|
if (zonenumber>640) throw Exception("Maximum zone number is 640 for new firmware")
|
||||||
|
} else {
|
||||||
|
if (zonenumber>512) throw Exception("Maximum zone number is 512 for old firmware")
|
||||||
|
}
|
||||||
|
|
||||||
|
val byteIndex = (zonenumber - 1) / 8
|
||||||
|
val bitIndex = (zonenumber - 1) % 8
|
||||||
|
payload[byteIndex] = payload[byteIndex] and ((1 shl bitIndex).inv().toByte())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set all zones as active
|
||||||
|
*/
|
||||||
|
fun SetAllZones(){
|
||||||
|
for (i in payload.indices){
|
||||||
|
payload[i] = 0xFF.toByte()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all zones (set all as inactive)
|
||||||
|
*/
|
||||||
|
fun ClearAllZones(){
|
||||||
|
for (i in payload.indices){
|
||||||
|
payload[i] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
4
src/web/KeyValueMessage.kt
Normal file
4
src/web/KeyValueMessage.kt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
class KeyValueMessage(val key: String, val value: String) {
|
||||||
|
}
|
||||||
9
src/web/StreamerOutputData.kt
Normal file
9
src/web/StreamerOutputData.kt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
class StreamerOutputData(val index: UInt, val channel: String, val ipaddress: String, val vu: Int, val bufferRemain: Int, var isPlaying: Boolean) {
|
||||||
|
companion object{
|
||||||
|
fun fromBarixConnection(bc: barix.BarixConnection): StreamerOutputData {
|
||||||
|
return StreamerOutputData(bc.index, bc.channel, bc.ipaddress, bc.vu, bc.bufferRemain, bc.isPlaying())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@ package web
|
|||||||
|
|
||||||
import StreamerOutputs
|
import StreamerOutputs
|
||||||
import codes.Somecodes
|
import codes.Somecodes
|
||||||
|
import codes.Somecodes.Companion.GetSensorsInfo
|
||||||
|
import codes.Somecodes.Companion.GetUptime
|
||||||
import codes.Somecodes.Companion.ListAudioFiles
|
import codes.Somecodes.Companion.ListAudioFiles
|
||||||
import codes.Somecodes.Companion.ValiDateForLogHtml
|
import codes.Somecodes.Companion.ValiDateForLogHtml
|
||||||
import codes.Somecodes.Companion.ValidFile
|
import codes.Somecodes.Companion.ValidFile
|
||||||
@@ -9,13 +11,21 @@ import codes.Somecodes.Companion.ValidIPV4
|
|||||||
import codes.Somecodes.Companion.ValidScheduleDay
|
import codes.Somecodes.Companion.ValidScheduleDay
|
||||||
import codes.Somecodes.Companion.ValidScheduleTime
|
import codes.Somecodes.Companion.ValidScheduleTime
|
||||||
import codes.Somecodes.Companion.ValidString
|
import codes.Somecodes.Companion.ValidString
|
||||||
|
import codes.Somecodes.Companion.ValidStrings
|
||||||
import com.fasterxml.jackson.databind.JsonNode
|
import com.fasterxml.jackson.databind.JsonNode
|
||||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
import content.Category
|
import content.Category
|
||||||
import content.Language
|
import content.Language
|
||||||
import content.ScheduleDay
|
import content.ScheduleDay
|
||||||
import content.VoiceType
|
import content.VoiceType
|
||||||
import database.*
|
import database.BroadcastZones
|
||||||
|
import database.LanguageLink
|
||||||
|
import database.MariaDB
|
||||||
|
import database.Messagebank
|
||||||
|
import database.ScheduleBank
|
||||||
|
import database.SoundChannel
|
||||||
|
import database.Soundbank
|
||||||
|
import database.UserDB
|
||||||
import db
|
import db
|
||||||
import io.javalin.Javalin
|
import io.javalin.Javalin
|
||||||
import io.javalin.apibuilder.ApiBuilder.before
|
import io.javalin.apibuilder.ApiBuilder.before
|
||||||
@@ -29,6 +39,7 @@ import io.javalin.http.Context
|
|||||||
import io.javalin.json.JavalinJackson
|
import io.javalin.json.JavalinJackson
|
||||||
import io.javalin.websocket.WsMessageContext
|
import io.javalin.websocket.WsMessageContext
|
||||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook
|
||||||
|
import java.nio.file.Files
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
@@ -78,7 +89,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
}
|
}
|
||||||
// Set user session
|
// Set user session
|
||||||
it.sessionAttribute("user", user.first)
|
it.sessionAttribute("user", user.first)
|
||||||
println("User ${user.first} logged in")
|
//println("User ${user.first} logged in")
|
||||||
// Redirect to home page
|
// Redirect to home page
|
||||||
it.redirect("home.html")
|
it.redirect("home.html")
|
||||||
}
|
}
|
||||||
@@ -88,26 +99,30 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
before { CheckUsers(it) }
|
before { CheckUsers(it) }
|
||||||
ws("/ws") { ws ->
|
ws("/ws") { ws ->
|
||||||
// WebSocket endpoint for home
|
// WebSocket endpoint for home
|
||||||
ws.onClose { wsCloseContext ->
|
//
|
||||||
// TODO Handle WebSocket close event
|
|
||||||
println("WebSocket closed: ${wsCloseContext.session.remoteAddress}")
|
|
||||||
}
|
|
||||||
ws.onMessage { wsMessageContext ->
|
ws.onMessage { wsMessageContext ->
|
||||||
try {
|
try {
|
||||||
val cmd =
|
val cmd =
|
||||||
objectmapper.readValue(wsMessageContext.message(), WebsocketCommand::class.java)
|
objectmapper.readValue(wsMessageContext.message(), WebsocketCommand::class.java)
|
||||||
when (cmd.command) {
|
when (cmd.command) {
|
||||||
"getSystemTime" -> {
|
"getSystemTime" -> {
|
||||||
|
val systemtime = LocalDateTime.now().format(Somecodes.datetimeformat1)
|
||||||
|
val uptime = GetUptime()
|
||||||
SendReply(
|
SendReply(
|
||||||
wsMessageContext,
|
wsMessageContext,
|
||||||
cmd.command,
|
cmd.command,
|
||||||
LocalDateTime.now().format(Somecodes.datetimeformat1)
|
if (uptime.isNotEmpty()) "Date & Time : $systemtime\nSystem Uptime : $uptime" else "Date & Time : $systemtime"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
"getCPUStatus" -> {
|
"getCPUStatus" -> {
|
||||||
Somecodes.getCPUUsage { vv ->
|
Somecodes.getCPUUsage { vv ->
|
||||||
SendReply(wsMessageContext, cmd.command, vv)
|
val sv = GetSensorsInfo()
|
||||||
|
if (sv.isNotEmpty()){
|
||||||
|
SendReply(wsMessageContext, cmd.command, vv+"\n"+sv)
|
||||||
|
} else {
|
||||||
|
SendReply(wsMessageContext, cmd.command, vv)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,9 +135,9 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
"getNetworkStatus" -> {
|
"getNetworkStatus" -> {
|
||||||
// TODO Get Network status
|
Somecodes.GetNetworkStatus { nn ->
|
||||||
Somecodes.GetNetworkStatus("")
|
SendReply(wsMessageContext, cmd.command, objectmapper.writeValueAsString(nn))
|
||||||
SendReply(wsMessageContext, cmd.command, "OK")
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"getPagingQueue" ->{
|
"getPagingQueue" ->{
|
||||||
@@ -134,7 +149,8 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
"getStreamerOutputs" -> {
|
"getStreamerOutputs" -> {
|
||||||
SendReply(wsMessageContext, cmd.command, objectmapper.writeValueAsString(StreamerOutputs.values.toList()))
|
val reply : List<StreamerOutputData> = StreamerOutputs.map { so -> StreamerOutputData.fromBarixConnection(so.value) }
|
||||||
|
SendReply(wsMessageContext, cmd.command, objectmapper.writeValueAsString(reply))
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
@@ -146,10 +162,9 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
ws.onConnect { wsConnectContext ->
|
// ws.onConnect { wsConnectContext ->
|
||||||
// TODO Handle WebSocket connect event
|
// println("WebSocket connected: ${wsConnectContext.session.remoteAddress}")
|
||||||
println("WebSocket connected: ${wsConnectContext.session.remoteAddress}")
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,43 +207,99 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
it.result(objectmapper.writeValueAsString(ScheduleDay.entries.map { day -> day.toString() }))
|
it.result(objectmapper.writeValueAsString(ScheduleDay.entries.map { day -> day.toString() }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
path("ListFiles/{Language}/{VoiceType}/{Category}") {
|
||||||
|
get { ctx ->
|
||||||
|
val language = ctx.pathParam("Language")
|
||||||
|
val voiceType = ctx.pathParam("VoiceType")
|
||||||
|
val category = ctx.pathParam("Category")
|
||||||
|
if (ValidString(language) && Language.entries.any { lang -> lang.name == language }) {
|
||||||
|
if (ValidString(voiceType) && VoiceType.entries.any { vt -> vt.name == voiceType }) {
|
||||||
|
if (ValidString(category) && Category.entries.any { cat -> cat.name == category }) {
|
||||||
|
val dir = Somecodes.SoundbankDirectory(
|
||||||
|
Language.valueOf(language),
|
||||||
|
VoiceType.valueOf(voiceType),
|
||||||
|
Category.valueOf(category)
|
||||||
|
)
|
||||||
|
if (Files.isDirectory(dir)){
|
||||||
|
val list = ListAudioFiles(dir)
|
||||||
|
ctx.result(objectmapper.writeValueAsString(list))
|
||||||
|
|
||||||
|
} else ctx.status(400)
|
||||||
|
.result(objectmapper.writeValueAsString(resultMessage("Directory does not exist")))
|
||||||
|
} else ctx.status(400)
|
||||||
|
.result(objectmapper.writeValueAsString(resultMessage("Invalid Category")))
|
||||||
|
} else ctx.status(400)
|
||||||
|
.result(objectmapper.writeValueAsString(resultMessage("Invalid VoiceType")))
|
||||||
|
} else ctx.status(400)
|
||||||
|
.result(objectmapper.writeValueAsString(resultMessage("Invalid Language")))
|
||||||
|
}
|
||||||
|
}
|
||||||
path("SoundBank") {
|
path("SoundBank") {
|
||||||
get("List") {
|
get("List") {
|
||||||
it.result(MariaDB.ArrayListtoString(db.soundDB.List))
|
it.result(MariaDB.ArrayListtoString(db.soundDB.List))
|
||||||
}
|
}
|
||||||
get("ListFiles"){
|
get("ListFiles") {
|
||||||
it.result(objectmapper.writeValueAsString(ListAudioFiles("C:\\soundbank")))
|
it.result(objectmapper.writeValueAsString(ListAudioFiles(Somecodes.Soundbank_directory)))
|
||||||
|
}
|
||||||
|
get("GetPhrases/{Language}/{VoiceType}"){
|
||||||
|
val language = it.pathParam("Language")
|
||||||
|
val voiceType = it.pathParam("VoiceType")
|
||||||
|
if (ValidString(language) && Language.entries.any { lang -> lang.name == language }) {
|
||||||
|
if (ValidString(voiceType) && VoiceType.entries.any { vt -> vt.name == voiceType }) {
|
||||||
|
val phrases = db.soundDB.List
|
||||||
|
.filter { sb -> sb.Language == language }
|
||||||
|
.filter {sb -> sb.VoiceType == voiceType}
|
||||||
|
.filter { sb -> sb.Category == Category.Phrase.name}
|
||||||
|
.distinctBy { sb -> sb.TAG }
|
||||||
|
.sortedBy { sb -> sb.TAG }
|
||||||
|
it.result(objectmapper.writeValueAsString(phrases))
|
||||||
|
} else it.status(400)
|
||||||
|
.result(objectmapper.writeValueAsString(resultMessage("Invalid VoiceType")))
|
||||||
|
} else it.status(400)
|
||||||
|
.result(objectmapper.writeValueAsString(resultMessage("Invalid Language")))
|
||||||
|
}
|
||||||
|
get("AirlineTags") { ctx ->
|
||||||
|
val value = db.soundDB.List
|
||||||
|
.filter { it.Category == Category.Airplane_Name.name }
|
||||||
|
.distinctBy { it.TAG }
|
||||||
|
.sortedBy { it.TAG }
|
||||||
|
.map { KeyValueMessage(it.TAG, it.Description) }
|
||||||
|
ctx.result(objectmapper.writeValueAsString(value))
|
||||||
|
}
|
||||||
|
get("CityTags") { ctx ->
|
||||||
|
val value = db.soundDB.List
|
||||||
|
.filter { it.Category == Category.City.name }
|
||||||
|
.distinctBy { it.TAG }
|
||||||
|
.sortedBy { it.TAG }
|
||||||
|
.map { KeyValueMessage(it.TAG, it.Description) }
|
||||||
|
ctx.result(objectmapper.writeValueAsString(value))
|
||||||
}
|
}
|
||||||
post("Add") {
|
post("Add") {
|
||||||
try {
|
try {
|
||||||
val addvalue = objectmapper.readValue(it.body(), Soundbank::class.java)
|
val addvalue = objectmapper.readValue(it.body(), Soundbank::class.java)
|
||||||
if (ValidString(addvalue.Description)) {
|
if (addvalue.isNotEmpty()){
|
||||||
if (ValidString(addvalue.TAG)) {
|
// check apakah TAG sudah ada untuk language, category dan voicetype yang sama
|
||||||
if (ValidString(addvalue.Category)) {
|
val exists = db.soundDB.List.any { sb ->
|
||||||
if (ValidString(addvalue.Language)) {
|
sb.TAG == addvalue.TAG && sb.Language == addvalue.Language && sb.Category == addvalue.Category && sb.VoiceType == addvalue.VoiceType
|
||||||
if (ValidString(addvalue.Path)) {
|
}
|
||||||
// check apakah TAG sudah ada untuk language dan category yang sama
|
if (!exists) {
|
||||||
val exists = db.soundDB.List.any { sb ->
|
val absolutepath = Somecodes.SoundbankDirectory(Language.valueOf(addvalue.Language), VoiceType.valueOf(addvalue.VoiceType), Category.valueOf(addvalue.Category)).resolve(addvalue.Path)
|
||||||
sb.TAG == addvalue.TAG && sb.Language == addvalue.Language && sb.Category == addvalue.Category
|
if (Files.isRegularFile(absolutepath)) {
|
||||||
}
|
addvalue.Path = absolutepath.toAbsolutePath().toString()
|
||||||
if (!exists) {
|
if (db.soundDB.Add(addvalue)) {
|
||||||
if (ValidFile(addvalue.Path)) {
|
db.soundDB.Resort()
|
||||||
if (db.soundDB.Add(addvalue)) {
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
db.soundDB.Resort()
|
db.Add_Log("AAS", "Added Sound Bank: $addvalue")
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
} else it.status(500)
|
||||||
} else it.status(500)
|
.result(objectmapper.writeValueAsString(resultMessage("Failed to add soundbank to database")))
|
||||||
.result(objectmapper.writeValueAsString(resultMessage("Failed to add soundbank to database")))
|
} else it.status(400)
|
||||||
} else it.status(400)
|
.result(objectmapper.writeValueAsString(resultMessage("Invalid Path, file does not exist")))
|
||||||
.result(objectmapper.writeValueAsString(resultMessage("Invalid Path, file does not exist")))
|
} else it.status(400)
|
||||||
} else it.status(400)
|
.result(objectmapper.writeValueAsString(resultMessage("TAG=${addvalue.TAG} already exists for the same Language=${addvalue.Language} and Category=${addvalue.Category}")))
|
||||||
.result(objectmapper.writeValueAsString(resultMessage("TAG=${addvalue.TAG} already exists for the same Language=${addvalue.Language} and Category=${addvalue.Category}")))
|
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Some fields are empty")))
|
||||||
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Path")))
|
|
||||||
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Language")))
|
} catch (e: Exception) {
|
||||||
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Category")))
|
it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid request body, Message: ${e.message}")))
|
||||||
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid TAG")))
|
|
||||||
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Description")))
|
|
||||||
} catch (_: Exception) {
|
|
||||||
it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid request body")))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete("List") {
|
delete("List") {
|
||||||
@@ -236,6 +307,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
if (db.soundDB.Clear()) {
|
if (db.soundDB.Clear()) {
|
||||||
db.soundDB.Get()
|
db.soundDB.Get()
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Clear Sound Bank table")
|
||||||
} else {
|
} else {
|
||||||
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate soundbank table")))
|
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate soundbank table")))
|
||||||
}
|
}
|
||||||
@@ -249,6 +321,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
if (db.soundDB.DeleteByIndex(index.toInt())) {
|
if (db.soundDB.DeleteByIndex(index.toInt())) {
|
||||||
db.soundDB.Resort()
|
db.soundDB.Resort()
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Deleted Sound Bank with index $index")
|
||||||
} else {
|
} else {
|
||||||
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to delete soundbank with index $index")))
|
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to delete soundbank with index $index")))
|
||||||
}
|
}
|
||||||
@@ -257,68 +330,79 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
patch("UpdateByIndex/{index}") {
|
patch("UpdateByIndex/{index}") {
|
||||||
// update by index
|
// update by index
|
||||||
val index = it.pathParam("index").toUIntOrNull()
|
val index = it.pathParam("index").toUIntOrNull()
|
||||||
if (index == null) {
|
if (index!=null){
|
||||||
// tidak ada path param index
|
|
||||||
it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid index")))
|
|
||||||
} else {
|
|
||||||
val sb = db.soundDB.List.find { xx -> xx.index == index }
|
val sb = db.soundDB.List.find { xx -> xx.index == index }
|
||||||
if (sb == null) {
|
if (sb!=null){
|
||||||
// soundbank dengan index tersebut tidak ditemukan
|
try{
|
||||||
it.status(404).result(objectmapper.writeValueAsString(resultMessage("Soundbank with index $index not found")))
|
//println("index=$index, body=${it.body()}")
|
||||||
} else {
|
val newsb = objectmapper.readValue(it.body(), Soundbank::class.java)
|
||||||
// soundbank dengan index tersebut ditemukan, sekarang update
|
//println("newsb=$newsb")
|
||||||
val json: JsonNode = objectmapper.readTree(it.body())
|
if (newsb.isNotEmpty()){
|
||||||
if (json.isEmpty) {
|
var varchanged = false
|
||||||
it.status(400).result(objectmapper.writeValueAsString(resultMessage("UpdateByIndex with index=$index has empty body")))
|
if (newsb.Description != sb.Description) {
|
||||||
} else {
|
sb.Description = newsb.Description
|
||||||
val _description = json.get("Description").asText("")
|
varchanged = true
|
||||||
val _tag = json.get("TAG").asText("")
|
}
|
||||||
val _category = json.get("Category").asText("")
|
if (newsb.TAG != sb.TAG) {
|
||||||
val _language = json.get("Language").asText("")
|
sb.TAG = newsb.TAG
|
||||||
val _path = json.get("Path").asText("")
|
varchanged = true
|
||||||
var changed = false
|
}
|
||||||
if (ValidString(_description) && _description != sb.Description) {
|
if (newsb.ValidCategory()) {
|
||||||
sb.Description = _description
|
if (newsb.Category != sb.Category){
|
||||||
changed = true
|
sb.Category = newsb.Category
|
||||||
}
|
varchanged = true
|
||||||
if (ValidString(_tag) && _tag != sb.TAG) {
|
}
|
||||||
sb.TAG = _tag
|
|
||||||
changed = true
|
|
||||||
}
|
|
||||||
if (ValidString(_category) && _category != sb.Category) {
|
|
||||||
if (Category.entries.any { cat ->
|
|
||||||
cat.name == _category
|
|
||||||
}) {
|
|
||||||
sb.Category = _category
|
|
||||||
changed = true
|
|
||||||
} else {
|
} else {
|
||||||
it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Category")))
|
it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Category")))
|
||||||
return@patch
|
return@patch
|
||||||
}
|
}
|
||||||
}
|
if (newsb.ValidLanguage()) {
|
||||||
if (ValidString(_language) && _language != sb.Language) {
|
if (newsb.Language != sb.Language){
|
||||||
sb.Language = _language
|
sb.Language = newsb.Language
|
||||||
changed = true
|
varchanged = true
|
||||||
}
|
}
|
||||||
if (ValidString(_path) && _path != sb.Path) {
|
|
||||||
if (ValidFile(_path)) {
|
|
||||||
sb.Path = _path
|
|
||||||
changed = true
|
|
||||||
} else {
|
} else {
|
||||||
it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Path, file does not exist")))
|
it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Language")))
|
||||||
return@patch
|
return@patch
|
||||||
}
|
}
|
||||||
}
|
if (newsb.ValidVoiceType()) {
|
||||||
if (changed) {
|
if (newsb.VoiceType != sb.VoiceType){
|
||||||
if (db.soundDB.UpdateByIndex(index.toInt(), sb)) {
|
sb.VoiceType = newsb.VoiceType
|
||||||
db.soundDB.Resort()
|
varchanged = true
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
}
|
||||||
} else it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to update soundbank with index $index")))
|
} else {
|
||||||
} else it.status(400)
|
it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid VoiceType")))
|
||||||
.result(objectmapper.writeValueAsString(resultMessage("Nothing has changed for soundbank with index $index")))
|
return@patch
|
||||||
|
}
|
||||||
|
|
||||||
|
val newpath = Somecodes.SoundbankDirectory(newsb.Language, newsb.VoiceType, newsb.Category).resolve(newsb.Path)
|
||||||
|
if (Files.isRegularFile(newpath)){
|
||||||
|
if (newpath.toAbsolutePath().toString() != sb.Path) {
|
||||||
|
sb.Path = newpath.toAbsolutePath().toString()
|
||||||
|
varchanged = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid path")))
|
||||||
|
return@patch
|
||||||
|
}
|
||||||
|
|
||||||
|
if (varchanged) {
|
||||||
|
//println("Some fields changed for index=$index, updating...")
|
||||||
|
if (db.soundDB.UpdateByIndex(index.toInt(), sb)) {
|
||||||
|
db.soundDB.Resort()
|
||||||
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Updated Soundbank $sb")
|
||||||
|
} else it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to update soundbank with index $index")))
|
||||||
|
} else it.status(400)
|
||||||
|
.result(objectmapper.writeValueAsString(resultMessage("Nothing has changed for soundbank with index $index")))
|
||||||
|
} else Exception("Some fields are empty")
|
||||||
|
} catch(e: Exception){
|
||||||
|
it.status(400).result(objectmapper.writeValueAsString(resultMessage("Unable to parse request body to Soundbank, Message: ${e.message}")))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
} else it.status(404).result(objectmapper.writeValueAsString(resultMessage("Soundbank with index $index not found")))
|
||||||
|
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid index")))
|
||||||
|
|
||||||
}
|
}
|
||||||
get("ExportXLSX") {
|
get("ExportXLSX") {
|
||||||
val xlsxdata = db.soundDB.Export_XLSX()
|
val xlsxdata = db.soundDB.Export_XLSX()
|
||||||
@@ -359,8 +443,15 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
// get messagebank list
|
// get messagebank list
|
||||||
it.result(MariaDB.ArrayListtoString(db.messageDB.List))
|
it.result(MariaDB.ArrayListtoString(db.messageDB.List))
|
||||||
}
|
}
|
||||||
post("Add"){
|
get("MessageIDs") { ctx ->
|
||||||
val json : JsonNode = objectmapper.readTree(it.body())
|
val value = db.messageDB.List
|
||||||
|
.distinctBy { it.ANN_ID }
|
||||||
|
.sortedBy { it.ANN_ID }
|
||||||
|
.map { KeyValueMessage(it.ANN_ID.toString(), it.Description) }
|
||||||
|
ctx.result(objectmapper.writeValueAsString(value))
|
||||||
|
}
|
||||||
|
post("Add") {
|
||||||
|
val json: JsonNode = objectmapper.readTree(it.body())
|
||||||
val description = json.get("Description")?.asText("") ?: ""
|
val description = json.get("Description")?.asText("") ?: ""
|
||||||
val language = json.get("Language")?.asText("") ?: ""
|
val language = json.get("Language")?.asText("") ?: ""
|
||||||
val ann_id = json.get("ANN_ID")?.asInt()?.toUInt() ?: 0u
|
val ann_id = json.get("ANN_ID")?.asInt()?.toUInt() ?: 0u
|
||||||
@@ -377,6 +468,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
if (db.messageDB.Add(mb)){
|
if (db.messageDB.Add(mb)){
|
||||||
db.messageDB.Resort()
|
db.messageDB.Resort()
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Added Messagebank: $mb")
|
||||||
} else it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to add messagebank to database")))
|
} else it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to add messagebank to database")))
|
||||||
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Message_TAGS")))
|
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Message_TAGS")))
|
||||||
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Message_Detail")))
|
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Message_Detail")))
|
||||||
@@ -390,6 +482,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
if (db.messageDB.Clear()) {
|
if (db.messageDB.Clear()) {
|
||||||
db.messageDB.Get()
|
db.messageDB.Get()
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Clear Message Bank table")
|
||||||
} else {
|
} else {
|
||||||
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate messagebank table")))
|
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate messagebank table")))
|
||||||
}
|
}
|
||||||
@@ -403,6 +496,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
if (db.messageDB.DeleteByIndex(index.toInt())) {
|
if (db.messageDB.DeleteByIndex(index.toInt())) {
|
||||||
db.messageDB.Resort()
|
db.messageDB.Resort()
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Deleted Message Bank with index $index")
|
||||||
} else {
|
} else {
|
||||||
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to delete messagebank with index $index")))
|
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to delete messagebank with index $index")))
|
||||||
}
|
}
|
||||||
@@ -463,6 +557,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
if (db.messageDB.UpdateByIndex(index.toInt(), mb)) {
|
if (db.messageDB.UpdateByIndex(index.toInt(), mb)) {
|
||||||
db.messageDB.Resort()
|
db.messageDB.Resort()
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Updated Messagebank $mb")
|
||||||
} else it.status(500)
|
} else it.status(500)
|
||||||
.result(objectmapper.writeValueAsString(resultMessage("Failed to update messagebank with index $index")))
|
.result(objectmapper.writeValueAsString(resultMessage("Failed to update messagebank with index $index")))
|
||||||
} else it.status(400)
|
} else it.status(400)
|
||||||
@@ -515,7 +610,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
val json: JsonNode = objectmapper.readTree(it.body())
|
val json: JsonNode = objectmapper.readTree(it.body())
|
||||||
val tag = json.get("tag").asText("")
|
val tag = json.get("tag").asText("")
|
||||||
val languages = json.get("language").asText("").split(";")
|
val languages = json.get("language").asText("").split(";")
|
||||||
println("Add Language Link, tag=$tag, languages=$languages")
|
//println("Add Language Link, tag=$tag, languages=$languages")
|
||||||
if (ValidString(tag)){
|
if (ValidString(tag)){
|
||||||
if (languages.all { xx -> Language.entries.any { yy -> yy.name.equals(xx,true)} }){
|
if (languages.all { xx -> Language.entries.any { yy -> yy.name.equals(xx,true)} }){
|
||||||
if (!db.languageDB.List.any { ll -> ll.TAG.equals(tag,true) }) {
|
if (!db.languageDB.List.any { ll -> ll.TAG.equals(tag,true) }) {
|
||||||
@@ -523,13 +618,14 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
if (db.languageDB.Add(newvalue)){
|
if (db.languageDB.Add(newvalue)){
|
||||||
db.languageDB.Resort()
|
db.languageDB.Resort()
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Added Language Link: $newvalue")
|
||||||
} else {
|
} else {
|
||||||
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to add language link to database")))
|
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to add language link to database")))
|
||||||
println("Failed to add language link to database")
|
//println("Failed to add language link to database")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
it.status(400).result(objectmapper.writeValueAsString(resultMessage("TAG=$tag already exists")))
|
it.status(400).result(objectmapper.writeValueAsString(resultMessage("TAG=$tag already exists")))
|
||||||
println("TAG=$tag already exists")
|
//println("TAG=$tag already exists")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
it.status(400).result(objectmapper.writeValueAsString(resultMessage("Contains unsupported language")))
|
it.status(400).result(objectmapper.writeValueAsString(resultMessage("Contains unsupported language")))
|
||||||
@@ -545,6 +641,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
if (db.languageDB.Clear()) {
|
if (db.languageDB.Clear()) {
|
||||||
db.languageDB.Resort()
|
db.languageDB.Resort()
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Clear Language Link table")
|
||||||
} else {
|
} else {
|
||||||
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate language link table")))
|
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate language link table")))
|
||||||
}
|
}
|
||||||
@@ -558,6 +655,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
if (db.languageDB.DeleteByIndex(index.toInt())) {
|
if (db.languageDB.DeleteByIndex(index.toInt())) {
|
||||||
db.languageDB.Resort()
|
db.languageDB.Resort()
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Deleted Language Link with index $index")
|
||||||
} else {
|
} else {
|
||||||
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to delete language link with index $index")))
|
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to delete language link with index $index")))
|
||||||
}
|
}
|
||||||
@@ -592,6 +690,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
if (db.languageDB.UpdateByIndex(index.toInt(), ll)) {
|
if (db.languageDB.UpdateByIndex(index.toInt(), ll)) {
|
||||||
db.languageDB.Resort()
|
db.languageDB.Resort()
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Updated Language Link $ll")
|
||||||
} else it.status(500)
|
} else it.status(500)
|
||||||
.result(objectmapper.writeValueAsString(resultMessage("Failed to update language link with index $index")))
|
.result(objectmapper.writeValueAsString(resultMessage("Failed to update language link with index $index")))
|
||||||
} else it.status(400)
|
} else it.status(400)
|
||||||
@@ -644,12 +743,12 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
if (db.scheduleDB.Clear()) {
|
if (db.scheduleDB.Clear()) {
|
||||||
db.scheduleDB.Get()
|
db.scheduleDB.Get()
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Clear Schedule Bank table")
|
||||||
} else {
|
} else {
|
||||||
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate schedulebank table")))
|
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate schedulebank table")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
post("Add"){
|
post("Add"){
|
||||||
|
|
||||||
// TODO add new schedule
|
// TODO add new schedule
|
||||||
// recheck lagi tambahan steph
|
// recheck lagi tambahan steph
|
||||||
val json: JsonNode = objectmapper.readTree(it.body())
|
val json: JsonNode = objectmapper.readTree(it.body())
|
||||||
@@ -670,10 +769,21 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
val zones = broadcast_zones.split(";")
|
val zones = broadcast_zones.split(";")
|
||||||
if (zones.all { zz -> db.broadcastDB.List.any { xx -> xx.description.equals(zz,true) } }){
|
if (zones.all { zz -> db.broadcastDB.List.any { xx -> xx.description.equals(zz,true) } }){
|
||||||
if (ValidString(language) && Language.entries.any{ lang -> lang.name == language }){
|
if (ValidString(language) && Language.entries.any{ lang -> lang.name == language }){
|
||||||
val newvalue = ScheduleBank(0u, description, day, time, soundpath, repeat, enable, broadcast_zones, language)
|
val newvalue = ScheduleBank(
|
||||||
|
0u,
|
||||||
|
description,
|
||||||
|
day,
|
||||||
|
time,
|
||||||
|
soundpath,
|
||||||
|
repeat,
|
||||||
|
enable,
|
||||||
|
broadcast_zones,
|
||||||
|
language
|
||||||
|
)
|
||||||
if (db.scheduleDB.Add(newvalue)){
|
if (db.scheduleDB.Add(newvalue)){
|
||||||
db.scheduleDB.Resort()
|
db.scheduleDB.Resort()
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Added Schedule Bank: $newvalue")
|
||||||
} else it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to add schedule to database")))
|
} else it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to add schedule to database")))
|
||||||
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Language")))
|
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Language")))
|
||||||
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Contains unsupported BroadcastZones")))
|
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Contains unsupported BroadcastZones")))
|
||||||
@@ -683,7 +793,6 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Time format, must be HH:mm")))
|
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Time format, must be HH:mm")))
|
||||||
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Day format")))
|
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Day format")))
|
||||||
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Description")))
|
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Description")))
|
||||||
|
|
||||||
}
|
}
|
||||||
delete("DeleteByIndex/{index}") {
|
delete("DeleteByIndex/{index}") {
|
||||||
// delete by index
|
// delete by index
|
||||||
@@ -694,6 +803,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
if (db.scheduleDB.DeleteByIndex(index.toInt())) {
|
if (db.scheduleDB.DeleteByIndex(index.toInt())) {
|
||||||
db.scheduleDB.Resort()
|
db.scheduleDB.Resort()
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Deleted Schedule Bank with index $index")
|
||||||
} else {
|
} else {
|
||||||
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to delete schedule with index $index")))
|
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to delete schedule with index $index")))
|
||||||
}
|
}
|
||||||
@@ -773,6 +883,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
if (db.scheduleDB.UpdateByIndex(index.toInt(), sb)) {
|
if (db.scheduleDB.UpdateByIndex(index.toInt(), sb)) {
|
||||||
db.scheduleDB.Resort()
|
db.scheduleDB.Resort()
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Updated Schedule : $sb")
|
||||||
} else it.status(500)
|
} else it.status(500)
|
||||||
.result(objectmapper.writeValueAsString(resultMessage("Failed to update schedule with index $index")))
|
.result(objectmapper.writeValueAsString(resultMessage("Failed to update schedule with index $index")))
|
||||||
} else it.status(400)
|
} else it.status(400)
|
||||||
@@ -808,12 +919,204 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
db.scheduleDB.Resort()
|
db.scheduleDB.Resort()
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
} else {
|
} else {
|
||||||
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to import schedulebank from XLSX")))
|
it.status(500)
|
||||||
|
.result(objectmapper.writeValueAsString(resultMessage("Failed to import schedulebank from XLSX")))
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
it.status(400)
|
||||||
|
.result(objectmapper.writeValueAsString(resultMessage("Invalid XLSX file")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
path("UserManagement") {
|
||||||
|
get("List") {
|
||||||
|
it.result(objectmapper.writeValueAsString(db.userDB.List))
|
||||||
|
}
|
||||||
|
delete("List") {
|
||||||
|
if (db.userDB.Clear()) {
|
||||||
|
db.userDB.Get()
|
||||||
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Clear User Management table")
|
||||||
|
} else {
|
||||||
|
it.status(500)
|
||||||
|
.result(objectmapper.writeValueAsString(resultMessage("Failed to truncate user table")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
post("Add") { ctx ->
|
||||||
|
val json: JsonNode = objectmapper.readTree(ctx.body())
|
||||||
|
val username = json.get("username").asText("")
|
||||||
|
val password = json.get("password").asText("")
|
||||||
|
val location = json.get("location").asText("")
|
||||||
|
val airline_tags = json.get("airline_tags").asText("")
|
||||||
|
val city_tags = json.get("city_tags").asText("")
|
||||||
|
val messagebank_ann_id = json.get("messagebank_ann_id").asText("")
|
||||||
|
val broadcastzones = json.get("broadcastzones").asText("")
|
||||||
|
if (ValidStrings(listOf(username, password, location, airline_tags, city_tags, messagebank_ann_id, broadcastzones))) {
|
||||||
|
if (!db.Username_exists(username)) {
|
||||||
|
// check apakah ada airline tag yang tidak ada di soundbank
|
||||||
|
val atags = airline_tags.split(";").map { it.trim() }
|
||||||
|
.filter { it.isNotEmpty() }.distinct()
|
||||||
|
val airlinetags = db.Get_AirlineCode_Tags()
|
||||||
|
if (atags.all { tag -> airlinetags.any { it == tag } }) {
|
||||||
|
// check apakah ada city tag yang tidak ada di soundbank
|
||||||
|
val ctags = city_tags.split(";").map { it.trim() }
|
||||||
|
.filter { it.isNotEmpty() }.distinct()
|
||||||
|
val citytags = db.Get_City_Tags()
|
||||||
|
if (ctags.all { tag -> citytags.any { it == tag } }) {
|
||||||
|
val bzdesc =
|
||||||
|
broadcastzones.split(";").map { it.trim() }
|
||||||
|
.filter { it.isNotEmpty() }.distinct()
|
||||||
|
val bzlist = db.Get_BroadcastZone_List()
|
||||||
|
if (bzdesc.all { desc -> bzlist.any { it == desc } }) {
|
||||||
|
val mbids = messagebank_ann_id.split(";")
|
||||||
|
.map { it.trim() }
|
||||||
|
.filter { it.isNotEmpty() }.distinct()
|
||||||
|
.mapNotNull { it.toUIntOrNull() }
|
||||||
|
val mbankids = db.Get_MessageID_List()
|
||||||
|
if (mbids.all { id -> mbankids.any { it == id } }) {
|
||||||
|
// semua valid, tambain ke database
|
||||||
|
val newuser = UserDB(
|
||||||
|
0u,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
location,
|
||||||
|
airline_tags,
|
||||||
|
city_tags,
|
||||||
|
messagebank_ann_id,
|
||||||
|
broadcastzones
|
||||||
|
)
|
||||||
|
if (db.userDB.Add(newuser)) {
|
||||||
|
db.userDB.Resort()
|
||||||
|
ctx.result(objectmapper.writeValueAsString(resultMessage("OK") ))
|
||||||
|
db.Add_Log("AAS", "Added User: $newuser")
|
||||||
|
} else ctx.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to add user to database")))
|
||||||
|
} else ctx.status(400).result(objectmapper.writeValueAsString(resultMessage("Some ANN_ID not found in Messagebank") ))
|
||||||
|
} else ctx.status(400).result(objectmapper.writeValueAsString(resultMessage("Some broadcast zone tags not found in soundbank")) )
|
||||||
|
} else ctx.status(400).result(objectmapper.writeValueAsString(resultMessage("Some city tags not found in soundbank")))
|
||||||
|
} else ctx.status(400).result(objectmapper.writeValueAsString(resultMessage("Some airline tags not found in soundbank")))
|
||||||
|
} else ctx.status(400).result(objectmapper.writeValueAsString("Username already exists"))
|
||||||
|
} else ctx.status(400).result(objectmapper.writeValueAsString(resultMessage("Not all fields have value")))
|
||||||
|
}
|
||||||
|
delete("DeleteByIndex/{index}") {
|
||||||
|
// delete by index
|
||||||
|
val index = it.pathParam("index").toUIntOrNull()
|
||||||
|
if (index == null) {
|
||||||
|
it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid index")))
|
||||||
|
} else {
|
||||||
|
if (db.userDB.DeleteByIndex(index.toInt())) {
|
||||||
|
db.userDB.Resort()
|
||||||
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Deleted User with index $index")
|
||||||
|
} else it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to delete user with index $index")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
patch("UpdateByIndex/{index}"){ ctx ->
|
||||||
|
// update by index
|
||||||
|
val index = ctx.pathParam("index").toUIntOrNull()
|
||||||
|
if (index!=null){
|
||||||
|
val user = db.userDB.List.find { xx -> xx.index == index }
|
||||||
|
if (user!=null){
|
||||||
|
val json : JsonNode = objectmapper.readTree(ctx.body())
|
||||||
|
if (!json.isEmpty){
|
||||||
|
val _username = json.get("username").asText("")
|
||||||
|
val _password = json.get("password").asText("")
|
||||||
|
val _location = json.get("location").asText("")
|
||||||
|
val _airline_tags = json.get("airline_tags").asText("")
|
||||||
|
val _city_tags = json.get("city_tags").asText("")
|
||||||
|
val _messagebank_ann_id = json.get("messagebank_ann_id").asText("")
|
||||||
|
val _broadcastzones = json.get("broadcastzones").asText("")
|
||||||
|
if (ValidStrings(listOf(_username, _password, _location, _airline_tags, _city_tags, _messagebank_ann_id, _broadcastzones))) {
|
||||||
|
val _otherusername = db.userDB.List.find { xx -> xx.username.equals(_username, true) && xx.index != index }
|
||||||
|
if (_otherusername == null) {
|
||||||
|
// belum ada user lain yang pakai username ini
|
||||||
|
val atags = _airline_tags.split(";").map { it.trim() }.filter { it.isNotEmpty() }.distinct()
|
||||||
|
val ctags = _city_tags.split(";").map { it.trim() }.filter { it.isNotEmpty() }.distinct()
|
||||||
|
val msgids = _messagebank_ann_id.split(";").map { it.trim() }.filter { it.isNotEmpty() }.distinct().mapNotNull { it.toUIntOrNull() }
|
||||||
|
val bzdesc = _broadcastzones.split(";").map { it.trim() }.filter { it.isNotEmpty() }.distinct()
|
||||||
|
|
||||||
|
val airline_tags = db.Get_AirlineCode_Tags()
|
||||||
|
val city_tags = db.Get_City_Tags()
|
||||||
|
val msgbankids = db.Get_MessageID_List()
|
||||||
|
val bzlist = db.Get_BroadcastZone_List()
|
||||||
|
if (atags.all { tag -> airline_tags.any { it == tag } }) {
|
||||||
|
if (ctags.all { tag -> city_tags.any { it == tag } }) {
|
||||||
|
if (msgids.all { tag -> msgbankids.any { it == tag } }) {
|
||||||
|
if (bzdesc.all { tag -> bzlist.any { it == tag } }) {
|
||||||
|
val editeduser = UserDB(index, _username, _password, _location, _airline_tags, _city_tags, _messagebank_ann_id, _broadcastzones)
|
||||||
|
if (!user.isEqual(editeduser)){
|
||||||
|
if (db.userDB.UpdateByIndex(index.toInt(), editeduser)){
|
||||||
|
db.userDB.Resort()
|
||||||
|
ctx.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Updated User : $editeduser")
|
||||||
|
} else ctx.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to update user with index $index")))
|
||||||
|
} else ctx.status(400).result(objectmapper.writeValueAsString(resultMessage("Nothing has changed for user with index $index")))
|
||||||
|
} else ctx.status(400).result(objectmapper.writeValueAsString(resultMessage("Some broadcast zone is not found in Broadcast Zone list")))
|
||||||
|
} else ctx.status(400).result(objectmapper.writeValueAsString(resultMessage("Some ANN_ID not found in Messagebank")))
|
||||||
|
} else ctx.status(400).result(objectmapper.writeValueAsString(resultMessage("Some city tags not found in soundbank")))
|
||||||
|
} else ctx.status(400).result(objectmapper.writeValueAsString("Some airline tags not found in soundbank"))
|
||||||
|
} else ctx.status(400).result(objectmapper.writeValueAsString(resultMessage("Username already exists for another user")))
|
||||||
|
} else ctx.status(400).result(objectmapper.writeValueAsString(resultMessage("Not all fiels have value")))
|
||||||
|
} else ctx.status(400).result(objectmapper.writeValueAsString(resultMessage("UpdateByIndex with index=$index has empty body")))
|
||||||
|
} else ctx.status(400).result(objectmapper.writeValueAsString(resultMessage("User with index $index not found")))
|
||||||
|
} else ctx.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid index")))
|
||||||
|
}
|
||||||
|
get("ExportXLSX") {
|
||||||
|
val xlsxdata = db.userDB.Export_XLSX()
|
||||||
|
if (xlsxdata != null) {
|
||||||
|
it.header(
|
||||||
|
"Content-Type",
|
||||||
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||||
|
)
|
||||||
|
it.header("Content-Disposition", "attachment; filename=\"userdb.xlsx\"")
|
||||||
|
it.outputStream().use { out ->
|
||||||
|
xlsxdata.write(out)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
it.status(500)
|
||||||
|
.result(objectmapper.writeValueAsString(resultMessage("Failed to export user table to XLSX")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
post("ImportXLSX") {
|
||||||
|
val uploaded = it.uploadedFile("file")
|
||||||
|
if (uploaded == null) {
|
||||||
|
it.status(400)
|
||||||
|
.result(objectmapper.writeValueAsString(resultMessage("No file uploaded")))
|
||||||
|
return@post
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
val xlsx = XSSFWorkbook(uploaded.content())
|
||||||
|
if (db.userDB.Import_XLSX(xlsx)) {
|
||||||
|
db.userDB.Resort()
|
||||||
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
} else {
|
||||||
|
it.status(500)
|
||||||
|
.result(objectmapper.writeValueAsString(resultMessage("Failed to import user table from XLSX")))
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid XLSX file")))
|
it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid XLSX file")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO kirim list message dan broadcast zones untuk ADD/Edit schedule
|
||||||
|
get("GetMessageAndBroadcastZones") {
|
||||||
|
val result = object {
|
||||||
|
val messages = db.messageDB.List
|
||||||
|
.filter { mb -> !mb.Message_Detail.contains("[") && !mb.Message_Detail.contains("]")}
|
||||||
|
.map { mb -> "${mb.Description} [${mb.ANN_ID}]" }
|
||||||
|
val broadcastzones = db.broadcastDB.List.map { it.description }
|
||||||
|
|
||||||
|
}
|
||||||
|
it.result(objectmapper.writeValueAsString(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kirim list language dari Messagebank berdasarkan ANN_ID
|
||||||
|
get("GetLanguageList/{ANN_ID}") { get1 ->
|
||||||
|
//kirim list language dari Messagebank
|
||||||
|
val langlist = db.messageDB.List
|
||||||
|
.filter { it.ANN_ID == get1.pathParam("ANN_ID").toInt().toUInt() }
|
||||||
|
.map { it.Language }.distinct()
|
||||||
|
get1.result(objectmapper.writeValueAsString(langlist))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
path("Log") {
|
path("Log") {
|
||||||
get("List") { get1 ->
|
get("List") { get1 ->
|
||||||
@@ -863,24 +1166,20 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
}
|
}
|
||||||
path("BroadcastZones"){
|
path("BroadcastZones"){
|
||||||
get("List") {
|
get("List") {
|
||||||
// TODO : temporary, convert BroadcastZones to BroadcastZonesHtml, karena harus revisi javascript di Bootstrap Studio
|
|
||||||
// val newlist: ArrayList<BroadcastZonesHtml> = db.broadcastDB.List.map { xx ->
|
|
||||||
// BroadcastZonesHtml(
|
|
||||||
// xx.index,
|
|
||||||
// xx.description,
|
|
||||||
// xx.SoundChannel,
|
|
||||||
// xx.id,
|
|
||||||
// xx.bp
|
|
||||||
// )
|
|
||||||
// } as ArrayList<BroadcastZonesHtml>
|
|
||||||
|
|
||||||
it.result(MariaDB.ArrayListtoString(db.broadcastDB.List))
|
it.result(MariaDB.ArrayListtoString(db.broadcastDB.List))
|
||||||
}
|
}
|
||||||
delete("List"){
|
get("BroadcastZoneDescriptions") { ctx ->
|
||||||
|
val value = db.broadcastDB.List
|
||||||
|
.distinctBy { it.description }
|
||||||
|
.map { it.description }
|
||||||
|
ctx.result(objectmapper.writeValueAsString(value))
|
||||||
|
}
|
||||||
|
delete("List") {
|
||||||
// truncate broadcast zones table
|
// truncate broadcast zones table
|
||||||
if (db.broadcastDB.Clear()) {
|
if (db.broadcastDB.Clear()) {
|
||||||
db.broadcastDB.Get()
|
db.broadcastDB.Get()
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Clear Broadcast Zones table")
|
||||||
} else {
|
} else {
|
||||||
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate broadcast zones table")))
|
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate broadcast zones table")))
|
||||||
}
|
}
|
||||||
@@ -899,6 +1198,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
if (db.broadcastDB.Add(newbp)){
|
if (db.broadcastDB.Add(newbp)){
|
||||||
db.broadcastDB.Resort()
|
db.broadcastDB.Resort()
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS","Added Broadcast Zone: $newbp")
|
||||||
} else it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to add broadcast zone to database")))
|
} else it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to add broadcast zone to database")))
|
||||||
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Relay")))
|
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Relay")))
|
||||||
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Box")))
|
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Box")))
|
||||||
@@ -914,6 +1214,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
if (db.broadcastDB.DeleteByIndex(index.toInt())) {
|
if (db.broadcastDB.DeleteByIndex(index.toInt())) {
|
||||||
db.broadcastDB.Resort()
|
db.broadcastDB.Resort()
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Deleted Broadcast Zone with index $index")
|
||||||
} else {
|
} else {
|
||||||
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to delete broadcast zone with index $index")))
|
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to delete broadcast zone with index $index")))
|
||||||
}
|
}
|
||||||
@@ -958,6 +1259,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
if (db.broadcastDB.UpdateByIndex(index.toInt(), bz)) {
|
if (db.broadcastDB.UpdateByIndex(index.toInt(), bz)) {
|
||||||
db.broadcastDB.Resort()
|
db.broadcastDB.Resort()
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Updated Broadcast Zone : $bz")
|
||||||
} else it.status(500)
|
} else it.status(500)
|
||||||
.result(objectmapper.writeValueAsString(resultMessage("Failed to update broadcast zone with index $index")))
|
.result(objectmapper.writeValueAsString(resultMessage("Failed to update broadcast zone with index $index")))
|
||||||
} else it.status(400)
|
} else it.status(400)
|
||||||
@@ -1006,11 +1308,15 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
get("List"){
|
get("List"){
|
||||||
it.result(MariaDB.ArrayListtoString(db.soundchannelDB.List))
|
it.result(MariaDB.ArrayListtoString(db.soundchannelDB.List))
|
||||||
}
|
}
|
||||||
delete("List"){
|
get("SoundChannelDescriptions") {
|
||||||
|
it.result(objectmapper.writeValueAsString(db.Get_SoundChannel_List()))
|
||||||
|
}
|
||||||
|
delete("List") {
|
||||||
// truncate sound channel table
|
// truncate sound channel table
|
||||||
if (db.soundchannelDB.Clear()) {
|
if (db.soundchannelDB.Clear()) {
|
||||||
db.soundchannelDB.Get()
|
db.soundchannelDB.Get()
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Clear Sound Channel table")
|
||||||
} else {
|
} else {
|
||||||
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate sound channel table")))
|
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate sound channel table")))
|
||||||
}
|
}
|
||||||
@@ -1044,23 +1350,26 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
} else {
|
} else {
|
||||||
|
|
||||||
// cek apakah ada soundchannel lain yang pakai ip dan channel yang sama
|
// cek apakah ada soundchannel lain yang pakai ip dan channel yang sama
|
||||||
if (db.soundchannelDB.List.any { xx -> xx.index != index && _ip.equals(xx.ip) && _channel.equals(xx.channel, true) }) {
|
val othersc = db.soundchannelDB.List.filter { sc -> sc.ip == _ip }.filter { sc -> sc.index != index }
|
||||||
println("Another sound channel already uses IP $_ip and channel $_channel")
|
if (othersc.isNotEmpty()){
|
||||||
it.status(400).result(objectmapper.writeValueAsString(resultMessage("Another sound channel already uses IP $_ip and channel $_channel")))
|
it.status(400).result(objectmapper.writeValueAsString(resultMessage("This IP address is already used by another sound channel")))
|
||||||
return@patch
|
} else {
|
||||||
|
// ada sesuatu yang ganti
|
||||||
|
val newsc = SoundChannel(0u, _channel, _ip)
|
||||||
|
if (db.soundchannelDB.UpdateByIndex(index.toInt(), newsc)) {
|
||||||
|
println("Updated sound channel with index $index")
|
||||||
|
|
||||||
|
db.soundchannelDB.Resort()
|
||||||
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Updated Sound Channel : $newsc")
|
||||||
|
} else {
|
||||||
|
println("Failed to update sound channel with index $index")
|
||||||
|
it.status(500)
|
||||||
|
.result(objectmapper.writeValueAsString(resultMessage("Failed to update sound channel with index $index")))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ada sesuatu yang ganti
|
|
||||||
val newsc = SoundChannel(0u, _channel, _ip)
|
|
||||||
if (db.soundchannelDB.UpdateByIndex(index.toInt(),newsc)){
|
|
||||||
println("Updated sound channel with index $index")
|
|
||||||
db.soundchannelDB.Resort()
|
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
|
||||||
} else {
|
|
||||||
println("Failed to update sound channel with index $index")
|
|
||||||
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to update sound channel with index $index")))
|
|
||||||
return@patch
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -1077,7 +1386,6 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
get("ExportXLSX"){
|
get("ExportXLSX"){
|
||||||
val xlsxdata = db.soundchannelDB.Export_XLSX()
|
val xlsxdata = db.soundchannelDB.Export_XLSX()
|
||||||
@@ -1109,7 +1417,8 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to import sound channel from XLSX")))
|
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to import sound channel from XLSX")))
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid XLSX file")))
|
it.status(400)
|
||||||
|
.result(objectmapper.writeValueAsString(resultMessage("Invalid XLSX file")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1124,6 +1433,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
if (db.queuepagingDB.Clear()) {
|
if (db.queuepagingDB.Clear()) {
|
||||||
db.queuepagingDB.Get()
|
db.queuepagingDB.Get()
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Clear queue paging table")
|
||||||
} else {
|
} else {
|
||||||
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate queue paging table")))
|
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate queue paging table")))
|
||||||
}
|
}
|
||||||
@@ -1137,6 +1447,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
if (db.queuepagingDB.DeleteByIndex(index.toInt())) {
|
if (db.queuepagingDB.DeleteByIndex(index.toInt())) {
|
||||||
db.queuepagingDB.Resort()
|
db.queuepagingDB.Resort()
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Deleted queue paging with index $index")
|
||||||
} else {
|
} else {
|
||||||
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to delete queue paging with index $index")))
|
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to delete queue paging with index $index")))
|
||||||
}
|
}
|
||||||
@@ -1145,6 +1456,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Steph : coba tambah untuk QueueTable Table. Belum ada di JS file(?)
|
// Steph : coba tambah untuk QueueTable Table. Belum ada di JS file(?)
|
||||||
path("QueueTable"){
|
path("QueueTable"){
|
||||||
get("List"){
|
get("List"){
|
||||||
@@ -1155,6 +1467,8 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
if (db.queuetableDB.Clear()) {
|
if (db.queuetableDB.Clear()) {
|
||||||
db.queuetableDB.Get()
|
db.queuetableDB.Get()
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Clear Automatic Queue Table")
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate queue sound table")))
|
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate queue sound table")))
|
||||||
}
|
}
|
||||||
@@ -1168,6 +1482,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
|||||||
if (db.queuetableDB.DeleteByIndex(index.toInt())) {
|
if (db.queuetableDB.DeleteByIndex(index.toInt())) {
|
||||||
db.queuetableDB.Resort()
|
db.queuetableDB.Resort()
|
||||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||||
|
db.Add_Log("AAS", "Deleted Automatic Queue Table with index $index")
|
||||||
} else {
|
} else {
|
||||||
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to delete queue sound with index $index")))
|
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to delete queue sound with index $index")))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user