2022-02-01 03:43:34 +00:00
// Copyright 2017, 2018, 2019, 2020, 2021, 2022 Max H. Parke KA1RBI
// Copyright 2020, 2021, 2022 Michael Rose
//
// This file is part of OP25
//
// OP25 is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3, or (at your option)
// any later version.
//
// OP25 is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with OP25; see the file COPYING. If not, write to the Free
// Software Foundation, Inc., 51 Franklin Street, Boston, MA
// 02110-1301, USA.
var lastUpdated = "09-Jan-2022" ;
var d _debug = 0 ;
var http _req = new XMLHttpRequest ( ) ;
var counter1 = 0 ;
var error _val = null ;
var current _tgid = null ;
var active _tgid = null ;
var active _nac = null ;
var send _busy = 0 ;
var send _qfull = 0 ;
var send _queue = [ ] ;
var req _cb _count = 0 ;
var request _count = 0 ;
var nfinal _count = 0 ;
var n200 _count = 0 ;
var r200 _count = 0 ;
var SEND _QLIMIT = 5 ;
var summary _mode = true ;
var enable _changed = false ;
var enable _status = [ ] ;
var last _srcaddr = [ ] ;
var last _srctag = [ ] ;
var last _alg = [ ] ;
var last _algid = [ ] ;
var last _keyid = [ ] ;
var tgid _files = { } ;
var srcid _files = { } ;
var channel _id = { } ;
var freqTable = true ;
var chsize = 1 ;
var evsize = 1 ;
var ersize = 1 ;
var event _source = null ; // must be in global scope for Babysitter to work.
window . g _change _freq = [ ] ;
window . g _cc _event = [ ] ;
window . src = [ ] ;
var intvAlias = null ;
var intvCss = null ;
localStorage . AliasTableUpdated == true ;
localStorage . ColorsTableUpdated == true ;
var encsym = "∅" ;
const zeroPad = ( num , places ) => String ( num ) . padStart ( places , '0' ) ; // leading zeros for single digit time values
function do _onload ( ) {
$ ( '#div_status' ) . show ( window . animateSpeed ) ;
$ ( '#babysitter' ) . hide ( ) ;
$ ( '#b1' ) . addClass ( 'nav-button-active' ) ;
$ ( '#uiupdated' ) . html ( lastUpdated ) ;
document . documentElement . setAttribute ( 'data-theme' , 'dark' ) ;
window . siteAlias = null ;
intvAlias = setInterval ( getSiteAlias , 5000 ) ;
intvCss = setInterval ( generateCSS , 4000 ) ;
resetFileReload = setInterval ( rstFileReload , 12000 )
connect ( ) ;
generateCSS ( ) ;
getSiteAlias ( ) ;
beginJsonSettings ( ) ;
accColorSel ( ) ;
for ( i = 1 ; i < 100 ; i ++ ) {
$ ( '#unk_default' ) . append ( new Option ( i , i ) ) ;
}
window . animateSpeed = $ ( '#ani_speed' ) . val ( ) ;
}
// vars set when saving things...
// localStorage.AliasTableUpdated == true;
// localStorage.ColorsTableUpdated == true;
$ ( document ) . ready ( function ( ) {
// // populate url into oplog url field
// var x = window.location.origin.split( ':' );
// var y = x[0] + ':' + x[1] + ':5000';
// $('#oplogUrl').val(y);
loadHelp ( ) ;
} ) ;
function connect ( ) {
event _source = new EventSource ( '/stream' ) ;
event _source . addEventListener ( 'message' , eventsource _listener ) ;
event _source . onerror = function ( ) {
event _source . close ( ) ;
}
setReconnect ( ) ;
}
function eventsource _listener ( event ) {
dispatch _commands ( event . data ) ;
}
// Babysitter - watches /stream, reacts when lost/restored
function setReconnect ( ) {
// readyState values: 0 = connecting, 1 = open, 2 = closed
var reconnecting = false ;
// do we need to setInterval with a var here since we call setReconnect again?
setInterval ( ( ) => {
if ( event _source . readyState == 2 ) {
reconnecting = true ;
$ ( '#babysitter' ) . show ( ) ;
$ ( '#estat' ) . html ( event _source . readyState ) ;
connect ( ) ;
} else if ( reconnecting ) {
reconnecting = false
$ ( '#estat' ) . html ( event _source . readyState ) ;
if ( ! event _source . readyState == 0 )
$ ( '#estat' ) . html ( 'OK' ) ;
$ ( '#babysitter' ) . hide ( ) ;
}
} , 3000 ) ;
}
function rstFileReload ( ) {
if ( event _source == null || event _source . readyState != 1 )
return ;
// forces the alias and colors info to reload in case user
// makes changes to them from another browser.
localStorage . AliasTableUpdated == true ;
localStorage . ColorsTableUpdated == true ;
}
function navOplog ( ) {
window . open ( $ ( '#oplogUrl' ) . val ( ) ) ;
}
function phaseType ( nac ) {
// reset the UI to a Phase 1 display (untested)
window [ nac + 'flavor' ] = null ;
}
function nav _update ( command ) {
var names = [
'b1' ,
'b2' ,
'b3' ,
'b4' ,
'b5' ,
'b7'
] ;
var bmap = {
'status' : 'b1' ,
'settings' : 'b2' ,
'rx' : 'b3' ,
'help' : 'b4' ,
'view' : 'b5' ,
'about' : 'b7'
} ;
var id = bmap [ command ] ;
for ( var id1 in names ) {
b = document . getElementById ( names [ id1 ] ) ;
if ( names [ id1 ] == id ) {
b . className = 'nav-button-active' ;
} else {
b . className = 'nav-button' ;
}
}
}
function f _select ( command ) {
var div _list = [
'status' ,
'settings' ,
'rx' ,
'help' ,
'about'
] ;
var orig _command = command ;
if ( command == 'rx' ) {
$ ( '#div_logs' ) . show ( window . animateSpeed ) ;
command = 'status' ;
summary _mode = false ;
} else {
summary _mode = true ;
$ ( '#div_logs' ) . hide ( window . animateSpeed ) ;
}
for ( var i = 0 ; i < div _list . length ; i ++ ) {
( command == div _list [ i ] ) ? $ ( '#div_' + div _list [ i ] ) . show ( window . animateSpeed ) : $ ( '#div_' + div _list [ i ] ) . hide ( window . animateSpeed ) ;
}
( command == 'status' && summary _mode == true ) ? $ ( '#div_images' ) . show ( window . animateSpeed ) : $ ( '#div_images' ) . hide ( window . animateSpeed ) ;
( command == 'status' ) ? $ ( '#controls' ) . show ( ) : $ ( '#controls' ) . hide ( ) ;
nav _update ( orig _command ) ;
if ( command == 'settings' )
f _list ( ) ;
}
function rx _update ( d ) {
if ( d [ 'files' ] . length > 0 ) {
for ( var i = 0 ; i < d [ 'files' ] . length ; i ++ ) {
var img = document . getElementById ( 'img' + i ) ;
if ( img [ 'src' ] != d [ 'files' ] [ i ] ) {
img [ 'src' ] = d [ 'files' ] [ i ] ;
img . style [ 'display' ] = '' ;
}
}
}
error _val = d [ 'error' ] ;
fine _tune = d [ 'fine_tune' ] ;
}
// frequency, system, and talkgroup display
function change _freq ( d ) { // d json_type = change_freq
t = 'TDMA' ;
var t = 'FDMA' ;
if ( ( d [ 'tdma' ] == 0 ) || ( d [ 'tdma' ] == 1 ) ) {
t = '<span style="color: lime;">TDMA ' + d [ 'tdma' ] + '</span>' ;
}
$ ( '#stx' ) . html ( t ) ;
var displayTgid = '—' ;
var displayTag = ' ' ;
var display _src = '—' ;
var display _alg = '—' ;
var display _keyid = '—' ;
var display _srctag = '—' ;
var e _class = 'value' ;
var trunc = $ ( '#valTruncate' ) . val ( ) ;
last _srcaddr [ d [ 'nac' ] ] = d [ 'srcaddr' ] ;
last _alg [ d [ 'nac' ] ] = d [ 'alg' ] ;
last _algid [ d [ 'nac' ] ] = d [ 'algid' ] ;
last _keyid [ d [ 'nac' ] ] = d [ 'keyid' ] ;
last _srctag [ d [ 'nac' ] ] = d [ 'srcaddr.tag' ] ;
if ( d [ 'tgid' ] != null ) {
displayTgid = d [ 'tgid' ] ;
displayTag = d [ 'tag' ] . substring ( 0 , trunc ) ;
if ( d [ 'srcaddr' ] != null && d [ 'srcaddr' ] > 0 ) {
display _src = d [ 'srcaddr' ] ;
display _srctag = d [ 'srcaddr_tag' ] ;
}
display _alg = d [ 'alg' ] ;
if ( d [ 'algid' ] != 128 ) {
display _keyid = hex ( d [ 'keyid' ] ) ;
e _class = 'red_value' ;
}
}
// main display - system, talkgroup, encryption, keyid, source addr display
var d _sys = 'system' in d ? d [ 'system' ] . substring ( 0 , trunc ) : 'Undefined' ;
var html = '<table style="width: 510px; height: 168px;">' ;
html += '<tr>' ;
html += '<td style="width: 422px;" colspan=2><span class="systgid" id="dSys">' + d _sys + '</span></td>' ;
html += '<td align="center" style="width: 88px;">' ;
html += '<span class="label-sm">Frequency</span><br><span class="value">' + freqDisplay ( d [ 'freq' ] / 1000000 ) + '</span></td>' ;
html += '</tr>' ;
html += '<tr>' ;
html += '<td style="width: 422px;" colspan=2><span class="systgid" id="dTag">' + displayTag + '</span></td>' ;
html += '<td align="center" style="width: 88px;">' ;
html += '<span class="label-sm">Talkgroup</span><br><span class="value" id="dTgid">' + displayTgid + '</span>' ;
html += '</td>' ;
html += '</tr>' ;
html += '<tr>' ;
html += '<td align="left">' ;
html += '<span class="label-sm">Encryption</span><br><span class="' + e _class + '" id="dAlg">' + display _alg + '</span>' ;
html += '</td>' ;
html += '<td align="center" style="width: 88px;">' ;
html += '<span class="label-sm">Key ID</span><br><span class="value" id="dKey">' + display _keyid + '</span>' ;
html += '</td>' ;
html += '<td align="center" style="width: 88px;">' ;
html += '<span class="label-sm">Source Addr</span><br><span class="value" id="dSrc">' + display _src + '</span>' ;
html += '</td>' ;
html += '</tr>' ;
html += '</table>' ;
$ ( '#div_s2' ) . html ( html ) . show ( ) ;
active _nac = d [ 'nac' ] ;
active _tgid = d [ 'tgid' ] ;
if ( d [ 'tgid' ] != null ) {
current _tgid = d [ 'tgid' ] ;
}
// color/style for main display
var fontStyle = $ ( '#valFontStyle' ) . val ( ) ;
var tgSize = parseInt ( $ ( '#valTagFont' ) . val ( ) ) ;
var sysSize = parseInt ( $ ( '#valSystemFont' ) . val ( ) ) ;
var defColor = $ ( '#sysColor' ) . val ( ) ;
var sysColor = "" ;
var tagColor = "" ;
var clr = getProperty ( '.c' + d . tag _color , 'color' , 'tgcolors' ) ;
var ani = getProperty ( '.c' + d . tag _color , 'animation' , 'tgcolors' ) ;
var bg = getProperty ( '.c' + d . tag _color , 'backgroundColor' , 'tgcolors' ) ;
sysColor = ( cbState ( 'color_main_sys' ) && d [ 'tag_color' ] ) ? clr : defColor ;
tagColor = ( cbState ( 'color_main_tag' ) && d [ 'tag_color' ] ) ? clr : defColor ;
$ ( '#dSys' ) . css ( { "color" : sysColor , "font-size" : sysSize , "font-weight" : fontStyle } ) ;
$ ( '#dTag' ) . css ( { "color" : tagColor , "font-size" : tgSize , "font-weight" : fontStyle , "animation" : ani , "background-color" : bg } ) ;
} // end change_freq
function trunk _summary ( d ) { // d json_type = trunk_update
var nacs = [ ] ;
for ( var nac in d ) {
if ( ! is _digit ( nac . charAt ( 0 ) ) )
continue ;
nacs [ nac ] = 1 ;
}
var html = '' ;
html += '<br><div class="summary">' ;
html += '<form>' ;
html += '<table border=1 width=732 border width=0 cellpadding=0 cellspacing=0>' ;
html += '<tr><th>Enabled</th><th>NAC</th><th>System</th><th>Last TSBK</th><th>TSBK Count</th><th>Alias TSV</tr>' ;
for ( nac in d ) {
if ( ! is _digit ( nac . charAt ( 0 ) ) )
continue ;
last _srcaddr [ nac ] = d [ nac ] [ 'srcaddr' ] ;
last _srctag [ nac ] = d [ nac ] [ 'srcaddr_tag' ] ;
last _alg [ nac ] = d [ nac ] [ 'alg' ] ;
last _algid [ nac ] = d [ nac ] [ 'algid' ] ;
last _keyid [ nac ] = d [ nac ] [ 'keyid' ] ;
if ( ! ( nac in enable _status ) )
enable _status [ nac ] = true ;
var times = [ ] ;
var last _tsbk = d [ nac ] [ 'last_tsbk' ] * 1000 ;
var display _last _tsbk = getTime ( last _tsbk ) ;
times . push ( display _last _tsbk ) ;
var min _t = 0 ;
if ( times . length ) {
for ( var i = 0 ; i < times . length ; i ++ ) {
if ( i == 0 || times [ i ] < min _t )
min _t = times [ i ] ;
}
times = min _t ;
} else {
times = ' ' ;
}
var ns = parseInt ( nac ) . toString ( 16 ) ;
html += '<tr>' ;
var tf = d [ nac ] [ 'tgid_tags_file' ] ;
var sf = d [ nac ] [ 'unit_id_tags_file' ] ;
var sysid = d [ nac ] [ 'sysid' ] ;
var checked = enable _status [ nac ] ? 'checked' : '' ;
html += '<td align=center><span class="value"><input type="checkbox" id="enabled-' + nac + '" ' + checked + ' onchange="javascript: f_enable_changed(this, ' + nac + ');">' ;
html += '<label for="enabled-' + nac + '"><span></span> ' + '</label></span></td>' ;
// checkbox styling nonsense above
html += '<td align=center><span class="value">' + ns . toUpperCase ( ) + '</span></td>' ;
html += '<td align=center><span class="value">' + d [ nac ] [ 'sysname' ] + '</span></td>' ;
html += '<td align=center><span class="value">' + times + '</span></td>' ;
html += '<td align=center><span class="value">' + comma ( d [ nac ] [ 'tsbks' ] ) + '</span></td>' ;
html += '<td align=center> ' ;
if ( tf )
html += '<a title="Talkgroups" style="text-decoration: none;" href="tsv-edit.html?file=' + tf + '&mode=tg&css=' + displayMode ( ) + '&nac=' + nac + '" target="_new">TG</a>' ;
if ( sf )
html += '  <a title="Source IDs" style="text-decoration: none;" href="tsv-edit.html?file=' + sf + '&mode=src&css=' + displayMode ( ) + '&nac=' + nac + '" target="_blank">SRC</a>' ;
html += '  <a title="System Site Aliases" style="text-decoration: none;" href="alias-edit.html?file=site-alias.json&mode=alias&sys=' + ( sysid ) + '"&css=' + displayMode ( ) + ' target="_blank">SA</a>' ;
html += '</td>'
html += '</tr>' ;
}
var display = '' ;
if ( ! enable _changed )
display = 'none' ;
html += '<tr id="save_list_row" style="display: ' + display + ';"><td colspan=99>' ;
html += '<input type="button" name="save_list" value="Apply Settings" onclick="javascript:f_save_list(this);"></input>' ;
html += '</td></tr>' ;
html += '</table></form></div>' ;
return html ;
} // end trunk_summary()
// additional system info: wacn, sysID, rfss, site id, freq table, adjc sites, secondary control channels, freq error
function trunk _detail ( d ) { // d json_type = trunk_update
var html = '' ;
var alias , sysid , rfss , site ;
var error _val = fine _tune = "—" ;
for ( var nac in d ) {
if ( ! is _digit ( nac . charAt ( 0 ) ) )
continue ;
var p2 = window [ nac + 'flavor' ] ; // if phase 2 was detected, add phase 2 layouts
var flavor = "Phase 1" ;
if ( p2 )
flavor = "Phase 2" ;
last _srcaddr [ nac ] = d [ nac ] [ 'srcaddr' ] ;
last _srctag [ nac ] = d [ nac ] [ 'srcaddr_tag' ] ;
last _alg [ nac ] = d [ nac ] [ 'alg' ] ;
last _algid [ nac ] = d [ nac ] [ 'algid' ] ;
last _keyid [ nac ] = d [ nac ] [ 'keyid' ] ;
error _val = sessionStorage . errorVal ;
fine _tune = sessionStorage . fineTune ;
sysid = d [ nac ] [ 'sysid' ] ;
rfss = d [ nac ] [ 'rfid' ] ;
site = d [ nac ] [ 'stid' ] ;
// use the Site Alias if defined, otherwise use d/nac/sysname
if ( window . siteAlias != null && window . siteAlias [ sysid ] && window . siteAlias [ sysid ] [ rfss ] && window . siteAlias [ sysid ] [ rfss ] [ site ] ) {
alias = window . siteAlias [ sysid ] [ rfss ] [ site ] [ 'alias' ] ;
} else {
alias = d [ nac ] [ 'sysname' ] ;
}
html += '<br><div>' ;
html += '<table class="rxsys" border=1 border width=0 cellpadding=0 cellspacing=0 width=100%">' ;
html += '<col width="120px">' ; // 1
html += '<col width="120px">' ; // 2
html += '<col width="120px">' ; // 3
html += '<col width="120px">' ; // 4
html += '<col width="120px">' ; // 5
html += '<col width="120px">' ; // 6
html += '<th colspan="6">' + alias + '</th><tr>' ;
// 1
html += '<td align="center">' ;
html += 'System ID<br> <span class="value">' + '0x' + parseInt ( d [ nac ] [ 'sysid' ] ) . toString ( 16 ) . toUpperCase ( ) ;
html += '</td>' ;
// 2
html += '<td align="center">' ;
html += 'NAC<br> <span class="value">' + '0x' + parseInt ( nac ) . toString ( 16 ) . toUpperCase ( ) ;
html += '</td>' ;
// 3
html += '<td align="center">' ;
html += 'WACN<br> <span class="value">' + '0x' + parseInt ( d [ nac ] [ 'wacn' ] ) . toString ( 16 ) . toUpperCase ( ) ;
html += '</td>' ;
// 4
html += '<td align="center">' ;
html += 'RFSS ID<br> <span class="value">' + d [ nac ] [ 'rfid' ]
html += '</td>' ;
// 5
html += '<td align="center">' ;
html += 'Site ID<br> <span class="value">' + d [ nac ] [ 'stid' ]
html += '</td>' ;
// 6
html += '<td align="center" id="ptype" ondblclick="phaseType(' + nac + ')">' ;
html += '<span>Type<br> <span class="value">' + flavor + '</span></span>' ;
html += '</td>' ;
html += '</tr>' ;
// row 2
html += '<tr>' ;
// 1
html += '<td align="center" style="white-space: nowrap;">' ;
html += 'Control Channel<br> <span class="value">' + freqDisplay ( d [ nac ] [ 'rxchan' ] / 1000000 ) ;
html += '</td>' ;
// 2, 3, 4
scc = '' ;
for ( i = 0 ; i < d [ nac ] [ 'secondary' ] . length ; i ++ ) {
scc += freqDisplay ( d [ nac ] [ 'secondary' ] [ i ] / 1000000 ) ;
if ( ( i + 1 ) != d [ nac ] [ 'secondary' ] . length )
scc += ' ' ; // space between freqs
}
html += '<td title="' + scc + '" colspan="3" align="center" style="max-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">' ;
html += 'Secondary Control Channels</span><br><span class="value"> ' ;
if ( d [ nac ] [ 'secondary' ] . length ) {
html += scc ;
} else {
html += '<span class="value">None' ;
}
html += '</td></span>' ;
// 5
html += '<td align="center">' ;
html += 'TSBK<br><span class="value">' + comma ( d [ nac ] [ 'tsbks' ] ) ;
html += '</td>' ;
// 6
var last _tsbk = d [ nac ] [ 'last_tsbk' ] * 1000 ;
var display _last _tsbk = getTime ( last _tsbk ) ;
html += '<td align="center">' ;
html += 'Last TSBK<br> <span class="value">' + display _last _tsbk + '</span>' ;
html += '</td>' ;
html += '</tr>' ;
if ( cbState ( 'showBandPlan' ) ) {
var zsys = d [ nac ] [ 'sysid' ] ;
html += '<tr><td colspan="6">' ;
html += '<table id="bandplan" title="Channel ID / Band Plan"><tr>' ;
html += '<th>ID</th>' ;
html += '<th>Type</th>' ;
html += '<th>Base Frequency</th>' ;
html += '<th>Tx Offset</th>' ;
html += '<th>Spacing (kHz)</th>' ;
html += '<th>Slots</th></tr>' ;
for ( p in channel _id [ d [ nac ] [ 'sysid' ] ] ) {
html += '<tr>' ;
html += '<td style="text-align: center;">' + channel _id [ zsys ] [ p ] [ 'iden' ] + '</td>' ;
html += '<td style="text-align: center;">' + channel _id [ zsys ] [ p ] [ 'type' ] + '</td>' ;
html += '<td style="text-align: center;">' + freqDisplay ( channel _id [ zsys ] [ p ] [ 'freq' ] ) + '</td>' ;
html += '<td style="text-align: center;">' + freqDisplay ( channel _id [ zsys ] [ p ] [ 'offset' ] ) + '</td>' ;
html += '<td style="text-align: center;">' + ( channel _id [ zsys ] [ p ] [ 'step' ] * 100 ) + '</td>' ;
html += '<td style="text-align: center;">' + channel _id [ zsys ] [ p ] [ 'slots' ] + '</td>' ;
html += '</tr>' ;
}
html += '</table>' ;
html += '</td></tr>' ;
}
html += '</table></div>' ;
// system frequencies table // d json_type = trunk_update
html += '<br>' ;
if ( freqTable == true ) { // show/hide table with F key
html += '<table class="fixed" id="sysfreq">' ;
html += '<col width="100px">' ; // 1 Freq
if ( cbState ( 'showLast' ) )
html += '<col width=" 75px">' ; // 2 Last
html += '<col width=" 80px">' ; // 3 tgid
html += '<col width="175px">' ; // 4 tgtag
html += '<col width=" 25px">' ; // 5 enc
html += '<col width=" 80px">' ; // 6 srcaddr
html += '<col width="195px">' ; // 7 srctag
html += '<tr>' ;
html += '<th>Frequency </th>' ; // 1
if ( cbState ( 'showLast' ) )
html += '<th>Last </th>' ; // 2
html += '<th colspan="3">Talkgroup</th>' ; // 3 / 4 / 5
html += '<th colspan="2">Source</th>' ; // 6 / 7
html += '</tr>' ;
var c , src _c , sc _src , sf _freq , sf _last , sf _hits , sf _timeout ;
// save keystrokes!
var fd = 'frequency_data' ;
var ft = 'frequency_tracking' ;
var td = 'talkgroup_data' ;
var ts = 'time_slot' ;
var calls = 'calls' ;
sf _timeout = 0 ; // delay before clearing the tg info
// #frequency#
//calls
var sf _count = [ ] ; // "count"
var sf _lastactive = [ ] ; // "last_active"
var sf _protected = [ ] ; // "protected"
var sf _starttime = [ ] ; // "start_time"
var sf _endtime = [ ] ; // "end_time"
//tgid
var sf _tgid = [ ] ; // "tg_id"
var sf _tgtag = [ ] ; // "tag"
var sf _tgcolor = [ ] ; // "color"
//srcaddr
var sf _srcaddr = [ ] ; // "unit_id"
var sf _srctag = [ ] ; // "tag"
var sf _srccolor = [ ] ; // "color"
var slot , tgid , x , y ;
var sf _enc = [ ] ;
var sf _la = [ ] ;
var sf _last = 0 ;
sf _tgid [ 0 ] = " " ;
sf _tgtag [ 0 ] = " " ;
sf _srcaddr [ 0 ] = " " ;
sf _srctag [ 0 ] = " " ;
sf _enc [ 0 ] = " " ;
sf _tgcolor [ 0 ] = 0 ;
sf _srccolor [ 0 ] = 0 ;
sf _la [ 0 ] = 0 ;
sf _tgid [ 1 ] = " " ;
sf _tgtag [ 1 ] = " " ;
sf _srcaddr [ 1 ] = " " ;
sf _srctag [ 1 ] = " " ;
sf _enc [ 1 ] = " " ;
sf _tgcolor [ 1 ] = 0 ;
sf _srccolor [ 1 ] = 0 ;
sf _la [ 1 ] = 0 ;
for ( var freq in d [ nac ] [ ft ] ) {
// temp for testing -- semi-perm?
if ( d [ nac ] [ ft ] [ freq ] [ 'tdma' ] == true ) {
zt = "<span style='color: #070;'>T</span>" ;
} else {
zt = " " ;
}
// end temp
slot = 0 ;
for ( var call of d [ nac ] [ ft ] [ freq ] [ calls ] ) {
if ( call == null ) {
sf _tgid [ slot ] = " " ;
sf _tgtag [ slot ] = " " ;
sf _srcaddr [ slot ] = " " ;
sf _srctag [ slot ] = " " ;
sf _enc [ slot ] = " " ;
slot ++ ;
continue ;
}
if ( call . end _time == 0 ) { // if end_time != 0 then the call is dead and should not be displayed in Active Freq table
sf _tgid [ slot ] = call . tgid . tg _id ;
if ( call . tgid . tag ) {
sf _tgtag [ slot ] = call . tgid . tag ;
} else {
sf _tgtag [ slot ] = "Talkgroup " + call . tgid . tg _id ;
call . tgid . color = $ ( '#unk_default' ) . val ( ) ;
}
sf _tgcolor [ slot ] = call . tgid . color ;
sf _srcaddr [ slot ] = call . srcaddr . unit _id ? call . srcaddr . unit _id : " " ;
sf _srctag [ slot ] = call . srcaddr . tag ? call . srcaddr . tag : " " ;
sf _srccolor [ slot ] = call . srcaddr . color ;
sf _protected [ slot ] = call [ 'protected' ] ; // protected is a reserved word in JS so can't use dot notation here
sf _enc [ slot ] = ( sf _protected [ slot ] == true ) ? encsym : " " ;
} // end if call.end_time
sf _count [ slot ] = call . count ;
sf _lastactive [ slot ] = call . last _active ;
sf _la [ slot ] = parseInt ( d . time - sf _lastactive [ slot ] , 10 ) ;
slot ++ ;
} // end for var call in calls
sf _freq = freqDisplay ( parseInt ( freq ) / 1000000 ) ;
sf _last = d [ nac ] [ ft ] [ freq ] [ 'last_active' ] ;
sf _last = parseFloat ( sf _last ) . toFixed ( 0 ) ;
if ( sf _la [ 0 ] > sf _timeout ) {
sf _tgid [ 0 ] = " " ;
sf _tgtag [ 0 ] = " " ;
sf _srcaddr [ 0 ] = " " ;
sf _srctag [ 0 ] = " " ;
sf _enc [ 0 ] = " " ;
}
if ( sf _la [ 1 ] > sf _timeout ) {
sf _tgid [ 1 ] = " " ;
sf _tgtag [ 1 ] = " " ;
sf _srcaddr [ 1 ] = " " ;
sf _srctag [ 1 ] = " " ;
sf _enc [ 1 ] = " " ;
}
for ( slot = 0 ; slot < 2 ; slot ++ ) {
var c = 0 ;
var src _c = 0 ;
if ( sf _tgcolor [ slot ] ) {
c = sf _tgcolor [ slot ] ;
} else {
c = smartColor ( sf _tgtag [ slot ] )
}
if ( sf _srccolor [ slot ] ) {
src _c = sf _srccolor [ slot ] ;
} else {
src _c = smartColor ( sf _srctag [ slot ] )
}
sc _src = "<span class=\"c" + src _c + "\">" ;
sc = "<span class=\"c" + c + "\">" ;
var tfile = d [ nac ] [ 'tgid_tags_file' ] ;
var sfile = d [ nac ] [ 'unit_id_tags_file' ] ;
// Active Frequencies Table
html += '<tr>' ;
if ( slot == 0 )
html += '<td style="cursor: crosshair;" title="' + sf _freq + ' Hits: ' + sf _count [ slot ] + '">' + sf _freq + ' ' + zt + '</td>' ; // 1
if ( slot == 1 )
html += '<td align="right" style="cursor: crosshair;" title="' + sf _freq + ' Hits: ' + sf _count [ slot ] + '"> <span class="label-sm">/</span> 2 </td>' ; // 1
if ( cbState ( 'showLast' ) )
html += '<td>' + sf _la [ slot ] + '</td>' ; // 2
html += '<td name="tgid" ondblclick="editTsv(this, 1, \'' + tfile + '\', ' + nac + ');">' + sc + sf _tgid [ slot ] + '</td>' ; // 3
html += '<td name="tag" ondblclick="editTsv(this, 1, \'' + tfile + '\', ' + nac + ');">' + sc + sf _tgtag [ slot ] + '</td>' ; // 4
html += '<td><span class="enc">' + sf _enc [ slot ] + '</span></td>' ; // 5
html += '<td name="srcid" ondblclick="editTsv(this, 1, \'' + sfile + '\', ' + nac + ');">' + sc _src + sf _srcaddr [ slot ] + '</td>' ; // 6
html += '<td name="srctag" ondblclick="editTsv(this, 1, \'' + sfile + '\', ' + nac + ');">' + sc _src + sf _srctag [ slot ] + '</td>' ; // 7
html += '</tr>' ;
// if (!p2 || zt == " " ) break;
if ( ! p2 || zt == " " || ( sf _la [ slot ] > 120 && cbState ( 'colSlot2' ) ) ) break ;
} // end for slot
} // end for freq
html += '</table>' ;
} // end (freqTable == true)
if ( cbState ( 'show_adj' ) )
html += adjacent _data ( d [ nac ] [ 'adjacent_data' ] ) ;
} // end for nac in d
return html ;
} // end trunk_detail()
function editTsv ( click , source , file , nac ) { // dbl click the ui to access the TSV editor
if ( ! click )
return ;
// source
// 1 = Active Frequencies
// 2 = Call History
window . getSelection ( ) . removeAllRanges ( ) ; // unselect the text that was double-clicked on
var cname = click . attributes . name ;
cname = ( cname . value ) ;
if ( source == 2 && ( cname == "tgid" || cname == "tag" ) ) { // Call History Talkground dbl clicked
var sid = file ;
file = tgid _files [ sid ] ;
}
if ( source == 2 && ( cname == "srcid" || cname == "srctag" ) ) { // Call History Source dbl clicked
var sid = file ;
file = srcid _files [ sid ] ;
}
if ( ( cname == "tgid" || cname == "tag" ) && ( ! file || file == "null" ) ) { // null is being stored as a string in window.tgid_files
jAlert ( "Talkgroup tag TSV file not specified in config file. Cannot edit." , "Error" ) ;
return ;
}
if ( ( cname == "srcid" || cname == "srctag" ) && ( ! file || file == "null" ) ) {
jAlert ( "Source tag TSV file not specified in config file. Cannot edit." , "Error" ) ;
return ;
}
var id = 0 ;
var url , x , y , tsv ;
// Active Frequencies // Call History
// cellIndex[2] = Talkgroup ID // cellIndex[3] = Talkgroup ID
// cellIndex[3] = Talkgroup Tag // cellIndex[4] = Talkgroup Tag
// cellIndex[5] = Source ID // cellIndex[5] = Source ID
// cellIndex[6] = Source Tag // cellIndex[6] = Source Tag
switch ( source ) {
case 1 : // sysfreq Table TD
x = 2 ; // the cell index of tgid
y = 5 ; // the cell index of srcid
switch ( cname ) {
case 'tgid' :
id = click . innerText ;
url = 'tsv-edit.html?file=' + file + '&css=' + displayMode ( ) + '&mode=tg&id=' + id + '&nac=' + nac ;
tsv = window . open ( url , 'tsv' ) ;
break ;
case 'tag' :
id = click . parentElement . cells [ x ] . innerText ;
url = 'tsv-edit.html?file=' + file + '&css=' + displayMode ( ) + '&mode=tg&id=' + id + '&nac=' + nac ;
tsv = window . open ( url , 'tsv' ) ;
break ;
case 'srcid' :
id = click . innerText ;
url = 'tsv-edit.html?file=' + file + '&css=' + displayMode ( ) + '&mode=src&id=' + id + '&nac=' + nac ;
tsv = window . open ( url , 'tsv' ) ;
break ;
case 'srctag' :
id = click . parentElement . cells [ y ] . innerText ;
url = 'tsv-edit.html?file=' + file + '&css=' + displayMode ( ) + '&mode=src&id=' + id + '&&nac=' + nac ;
tsv = window . open ( url , 'tsv' ) ;
break ;
}
break ;
case 2 : // Call History Table SPAN
x = 3 ; // the cell index of tgid
y = 5 ; // the cell index of srcid
switch ( cname ) {
case 'tgid' :
id = click . parentElement . parentElement . cells [ x ] . innerText ;
url = 'tsv-edit.html?file=' + file + '&css=' + displayMode ( ) + '&mode=tg&id=' + id + '&nac=' + nac ;
tsv = window . open ( url , 'tsv' ) ;
break ;
case 'tag' :
id = click . parentElement . parentElement . cells [ x ] . innerText ;
url = 'tsv-edit.html?file=' + file + '&css=' + displayMode ( ) + '&mode=tg&id=' + id + '&nac=' + nac ; ;
tsv = window . open ( url , 'tsv' ) ;
break ;
case 'srcid' :
id = click . parentElement . parentElement . cells [ y ] . innerText ;
url = 'tsv-edit.html?file=' + file + '&css=' + displayMode ( ) + '&mode=src&id=' + id + '&nac=' + nac ;
tsv = window . open ( url , 'tsv' ) ;
break ;
case 'srctag' :
id = click . parentElement . parentElement . cells [ y ] . innerText ;
url = 'tsv-edit.html?file=' + file + '&css=' + displayMode ( ) + '&mode=src&id=' + id + '&nac=' + nac ;
tsv = window . open ( url , 'tsv' ) ;
break ;
} // end switch cname
break ;
} // end switch source
} // end editTsv()
// adjacent sites table
function adjacent _data ( d ) {
// d json_type = trunk_update
if ( Object . keys ( d ) . length < 1 )
return '' ;
// var html = "<div class=\"adjacent\">"; // open div-adjacent
var html = '<br><table class="fixed" id="adjacent-sites" width=100%>' ;
html += '<col width="220px">' ;
html += '<col width="130px">' ;
html += '<col width="100px">' ;
html += '<col width=" 75px">' ;
html += '<col width=" 75px">' ;
html += '<col width="130px">' ;
html += '<th>Adjacent Sites</th><th>Frequency</th><th>System ID</th><th>RFSS ID</th><th>Site ID</th><th>Uplink</th>' ;
var ct = 0 ;
var alias , sysid , rfss , site ;
for ( var freq in d ) {
sysid = d [ freq ] [ 'sysid' ] ;
rfss = d [ freq ] [ 'rfid' ] ;
site = d [ freq ] [ 'stid' ] ;
alias = "-" ;
if ( window . siteAlias != null && window . siteAlias [ sysid ] && window . siteAlias [ sysid ] [ rfss ] && window . siteAlias [ sysid ] [ rfss ] [ site ] ) {
alias = window . siteAlias [ sysid ] [ rfss ] [ site ] [ 'alias' ] ;
} else {
alias = 'Site ' + d [ freq ] [ 'stid' ] ;
}
html += '<tr><td>' + alias + '</td><td align="center">' + freqDisplay ( freq / 1000000 ) + '</td><td align="center">' + d [ freq ] [ 'sysid' ] . toString ( 16 ) . toUpperCase ( ) + '</td>' ;
html += '<td align="center">' + d [ freq ] [ 'rfid' ] + '</td><td align="center">' + d [ freq ] [ 'stid' ] + '</td>' ;
html += '<td align="center">' + freqDisplay ( d [ freq ] [ 'uplink' ] / 1000000 ) + '</td></tr>' ;
}
html += '</table>' ;
return html ; // end adjacent sites table
} // end adjacent_data()
function trunk _update ( d ) {
var html ;
if ( summary _mode ) {
html = trunk _summary ( d ) ; // home screen
} else {
html = trunk _detail ( d ) ; // RX screen
}
$ ( '#div_s1' ) . html ( html ) ;
if ( ! summary _mode )
sortTable ( 'adjacent-sites' , 4 ) ;
// display hold indicator
if ( d [ 'data' ] [ 'hold_mode' ] ) {
$ ( '#holdIndicator' ) . show ( ) ;
} else {
$ ( '#holdIndicator' ) . hide ( ) ;
}
// display last command unless it was more than 10 seconds ago
x2 = d [ 'data' ] [ 'last_command' ] ;
if ( x2 && d [ 'data' ] [ 'last_command_time' ] > - 10 ) {
$ ( '#lastCommand' ) . html ( 'Last Command<br><b>' + x2 . toUpperCase ( ) + '</b><br>' + ' ' + d [ 'data' ] [ 'last_command_time' ] * - 1 + ' secs ago' ) ;
} else {
$ ( '#lastCommand' ) . html ( '' ) ;
}
update _data ( d ) ;
} // end trunk_update()
function update _data ( d ) { // d json type = trunk_update
if ( active _nac == null || active _tgid == null )
return ;
var display _src = '—' ;
var display _srctag = '—' ;
var display _alg = '—' ;
var display _keyid = '—' ;
var e _class = 'value' ;
if ( last _srcaddr [ active _nac ] != null ) {
display _src = last _srcaddr [ active _nac ] ;
var ele = document . getElementById ( 'dSrc' ) ;
if ( ele != null )
ele . innerHTML = display _src ;
}
if ( last _srctag [ active _nac ] != null ) {
display _srctag = last _srctag [ active _nac ] ;
var ele = document . getElementById ( 'dSrctag' ) ;
if ( ele != null )
ele . innerHTML = display _srctag ;
}
if ( last _algid [ active _nac ] == null || last _alg [ active _nac ] == null || last _keyid [ active _nac ] == null )
return ;
display _alg = last _alg [ active _nac ] ;
if ( last _algid [ active _nac ] != 128 ) {
display _keyid = last _keyid [ active _nac ] ;
e _class = 'red_value' ;
}
ele = document . getElementById ( 'dAlg' ) ;
if ( ele != null ) {
ele . innerHTML = display _alg ;
ele . className = e _class ;
}
ele = document . getElementById ( 'dKey' ) ;
if ( ele != null )
ele . innerHTML = display _keyid ;
} // end update_data()
function error _tracking ( ) {
return ; // empty right now, handled in dispatch_commands switch block
} // end error_tracking
function dispatch _commands ( txt ) {
if ( txt == '[]' ) {
return ;
}
var dl = JSON . parse ( txt ) ;
var dispatch = {
'trunk_update' : trunk _update ,
'change_freq' : change _freq ,
'rx_update' : rx _update ,
'config_data' : config _data ,
'config_list' : config _list ,
'cc_event' : cc _event ,
'freq_error_tracking' : error _tracking
} ;
for ( var i = 0 ; i < dl . length ; i ++ ) {
var d = dl [ i ] ;
if ( ! ( 'json_type' in d ) )
continue ;
if ( ! ( d [ 'json_type' ] in dispatch ) )
continue ;
var j _type = d [ 'json_type' ] ;
var time = getTime ( new Date ( ) ) ;
if ( d . time )
time = getTime ( d . time * 1000 ) ;
switch ( j _type ) {
case 'freq_error_tracking' :
var ele , bx , cx , dx , ex , fx , gx ;
$ ( '#error_tracking' ) . show ( ) ;
bx = d . device ;
cx = d . name ;
dx = d . error _band ;
ex = d . freq _correction ;
fx = d . freq _error ;
gx = d . tuning _error ;
appendErrorTable ( time , bx , cx , dx , ex , fx , gx , 'errors' ) ;
break ;
case 'change_freq' :
cb = cbState ( 'log_cf' ) ;
time = getTime ( d [ 'effective_time' ] * 1000 ) ;
if ( d . tgid && ! d . tag ) {
d . tag = 'Talkgroup ' + d . tgid + '' ; // talkgroup tag isn't in the tsv
d . tag _color = $ ( '#unk_default' ) . val ( ) ;
}
sysid = d . sysid ? hex ( d . sysid ) . toUpperCase ( ) : "—"
var freq = d [ 'freq' ] / 1000000 ;
var srctag = "—" ;
var color , srccolor , srcaddr ;
if ( d [ 'tag_color' ] ) {
color = d [ 'tag_color' ] ;
} else {
color = smartColor ( d . tag ) ;
d [ 'tag_color' ] = color ;
}
if ( d [ 'srcaddr_color' ] ) {
srccolor = d [ 'srcaddr_color' ] ;
} else {
srccolor = smartColor ( d . tag ) ;
}
var tag = '<span class="c' + color + '">' + d . tag + '</span>' ;
var tgid = '<span class="c' + color + '">' + d . tgid + '</span>' ;
// TODO - cb not defined keeps coming up a couple times in the console, right at start up.
if ( cb && d . tgid ) {
appendJsonTable ( time , j _type , sysid , tag , tgid , 'OP25' , freq , '--' , 'history' ) ;
}
if ( d . tgid ) {
srctag = ( window [ 'srct' + d . srcaddr ] ) ? '<span class="c' + srccolor + '">' + window [ 'srct' + d . srcaddr ] : "—" ;
srcaddr = '<span class="c' + srccolor + '">' + d . srcaddr ;
}
window . g _change _freq = d ;
break ;
case 'trunk_update' :
cb = cbState ( 'log_tu' ) ;
var tf , sf , sid , a , z ;
var sysid , site , rfid , color ;
var grpaddr , c _grpaddr , srcaddr , c _srcaddr , talkgroup , c _talkgroup , srctag , c _srctag ;
for ( nac in d ) {
if ( Number . isInteger ( parseInt ( nac ) ) ) {
sysid = d [ nac ] [ 'sysid' ] ;
sid = sysid ;
sysid = hex ( sysid ) . toUpperCase ( ) ;
site = d [ nac ] [ 'stid' ] ;
rfid = d [ nac ] [ 'rfid' ] ;
grpaddr = d [ nac ] [ 'grpaddr' ] ;
srcaddr = d [ nac ] [ 'srcaddr' ] ;
tf = d [ nac ] [ 'tgid_tags_file' ] ;
tgid _files [ sid ] = tf ;
sf = d [ nac ] [ 'unit_id_tags_file' ] ;
srcid _files [ sid ] = sf ;
if ( window [ 'tgidt' + grpaddr ] )
talkgroup = window [ 'tgidt' + grpaddr ] ;
srctag = ( window [ 'srct' + srcaddr ] ) ? ( window [ 'srct' + srcaddr ] ) : " " ;
color = smartColor ( talkgroup ) ;
srccolor = smartColor ( talkgroup ) ;
if ( window [ 'tgidc' + grpaddr ] )
color = window [ 'tgidc' + grpaddr ] ;
c _grpaddr = '<span class="c' + color + '">' + grpaddr + '</span>' ;
c _talkgroup = '<span class="c' + color + '">' + talkgroup + '</span>' ;
color = ( window [ 'srcc' + srcaddr ] ) ? window [ 'srcc' + srcaddr ] : "0" ;
c _srcaddr = '<span class="c' + srccolor + '">' + srcaddr + '</span>' ;
c _srctag = '<span class="c' + srccolor + '">' + srctag + '</span>' ;
var sr = "Site: " + site + " RFID: " + rfid ;
var col _f = "OP25" ; // empty for now
if ( cb ) {
if ( grpaddr ) { // do not append the table if no grpaddr is present - TU into Events table is pretty much useless anyway
appendJsonTable ( time , j _type , sysid , sr , c _grpaddr , col _f , "Update" , "--" , "history" ) ;
}
} //end if cb
} // end if nac is number
} // end for nac in d
sources ( d ) ;
window . g _trunk _update = d ;
break ;
case 'rx_update' :
cb = cbState ( 'log_rx' ) ;
if ( d [ 'files' ] [ 0 ] ) {
var ps = "Plots: True" ;
} else {
var ps = "Plots: False"
}
if ( d [ 'fine_tune' ] ) {
var ft = d [ 'fine_tune' ] ;
} else {
var ft = "n/a" ;
}
if ( cb ) {
appendJsonTable ( time , j _type , ps , 'Fine Tune: ' + ft , 'Tune Err: ' + d [ 'error' ] , 'OP25' , 'RX Update' , '--' , 'history' ) ;
} // this Events table entry doesn't add much value either.
sessionStorage . fineTune = d [ 'fine_tune' ] ? d [ 'fine_tune' ] : "—" ;
sessionStorage . errorVal = d [ 'error' ] ? d [ 'error' ] : "—" ;
break ;
case 'cc_event' :
time = getTime ( d [ 'time' ] * 1000 ) ;
cb = cbState ( 'log_cc' ) ;
var opcode , n _opcode , sysid , tag , target , source , srctag , src _c , color ;
var logCall = 0 ;
var noLog = 0 ;
var xp = 0 ;
if ( d . opcode !== null ) {
opcode = d [ 'opcode' ] . toString ( 16 ) ;
if ( g _opcode [ opcode ] ) {
n _opcode = g _opcode [ opcode ] ;
} else {
n _opcode = 'Opcode Not Found: ' + opcode ;
}
}
tag = target = source = '—' ;
srctag = " " ; // used
// [group] does not appear in every cc_event!
if ( d . group ) {
if ( d . group && d . group . tag ) {
tag = d . group . tag ;
} else {
d . group . tag = 'Talkgroup ' + d . group . tg _id ;
d . group . color = $ ( '#unk_default' ) . val ( ) ;
}
} // end if d.group
if ( d . reason ) {
tag = getReason ( hex ( d . reason ) ) ;
}
n _opcode = g _opcode [ opcode ] ? g _opcode [ opcode ] : opcode ;
if ( d [ 'srcaddr' ] ) {
source = ( d . srcaddr . unit _id ) ? d . srcaddr . unit _id : "— No ID" ; // This condition is reached when there is traffic
srctag = ( d . srcaddr . tag ) ? d . srcaddr . tag : " " ; // but no source unit id is present. on ebrcs, this
// happens when an old u/vhf system is patched onto ebrcs.
}
// handle manufacturer opcodes:
// 0x10 = Relm/BK
// 0x68 = Kenwood
// 144(10) 0x90 = Motorola
// 164(10) 0xA4 = Harris
// 0xD8 = Tait
// 0xF8 = Vertex Standard
// trunking.py sends the following opcodes:
// -1, 0x00, 01, 02, 03, 09, 20, 27, 28, 2a, 2b, 2c, 2d, 2f, 31, 33, 34, 3d
switch ( opcode ) {
case "-1" : // end call (not a P25 standard)
tag = d . tgid . tag ;
target = d . tgid . tg _id ;
n _opcode = "End Call" ;
noLog = 1 ; // Events
// TODO - maybe nothing?
break ;
case "0" :
if ( d . mfrid != null ) {
switch ( d . mfrid ) {
case 0 :
n _opcode = "Call" ; // GRP_V_CH_GRANT
target = d [ 'group' ] [ 'tg_id' ] ;
srctag = d [ 'srcaddr' ] [ 'tag' ] ;
noLog = cbState ( 'je_calls' ) ? 0 : 1 ; //events
logCall = 1 ; // callHistory
break ;
case 144 : // MOTOROLA
n _opcode = "XP Adds" ; //mot_grg_add_cmd 0x00"
tag = d . sg . tag ;
target = d . sg . tg _id ;
noLog = 1 ; // chatty af
logCall = 0 ; // callHistory
break ;
case "default" :
n _opcode = "Call" ;
target = d [ 'group' ] [ 'tg_id' ] ;
srctag = d [ 'srcaddr' ] [ 'tag' ] ;
noLog = cbState ( 'je_calls' ) ? 0 : 1 ;
logCall = 1 ; // callHistory
break ;
} // end switch
} // end if
break ;
case "1" : // 0x01 - RESERVED
if ( d . mfrid != null ) {
switch ( d . mfrid ) {
case 0 :
n _opcode = "Reserved 0x01" ;
break ;
case 144 : // MOTOROLA
n _opcode = "XP Drops" ;
tag = d . sg . tag ;
target = d . sg . tg _id ;
break ;
} // end switch
} // end if
break ;
case "2" : // 0x02 - grp_v_ch_grant_updt
if ( d . mfrid != null ) {
switch ( d . mfrid ) {
case 0 :
n _opcode = "grp_v_ch_grant_updt 0x02" ;
noLog = cbState ( 'je_calls' ) ? 0 : 1 ;
break ;
case 144 : // MOTOROLA
d [ 'group' ] = d [ 'sg' ] ;
d [ 'srcaddr' ] = d [ 'sa' ] ;
srctag = d . sa . tag ;
n _opcode = "XP Call" ; // MOT XP Call mot_grg_cn_grant 0x02
tag = d . sg . tag ;
target = d . sg . tg _id ;
source = d . sa . unit _id ;
noLog = cbState ( 'je_calls' ) ? 0 : 1 ;
logCall = 1 ; // callHistory
xp = 1 ;
break ;
} // end switch
} // end if
break ;
case "3" : // 0x03 - GRP_V_CH_GRANT_UPDT_EXP
if ( d . mfrid != null ) {
switch ( d . mfrid ) {
case 0 :
n _opcode = "grp_v_ch_grant_updt_exp 0x03" ;
break ;
case 144 : // MOTOROLA
n _opcode = "mot_grg_cn_grant_updt 0x03" ;
tag = d . sg1 . tag ;
target = d . sg1 . tg _id ;
noLog = 1 ; // used for late entry, very chatty, probably should not log to JSON Events
break ;
} // end switch
} // end if
break ;
case "9" : // MOT System Load ? Could be "Motorola Scan Marker"
if ( d . mfrid != null ) {
switch ( d . mfrid ) {
case 0 :
n _opcode = "Opcode 0x03" ;
break ;
case 144 : // MOTOROLA
n _opcode = "System Load " + d . test1 ;
noLog = 1 ;
break ;
} // end switch
} // end if
break ;
case "20" : // 0x20 - ACK_RSP_FNE
noLog = 1 ;
break ;
case "24" : // 0x24 - Extended Function Command (inhibit) TODO: get reason code, log it
var efclass = d . efclass ;
var efoperand = d . efoperand ;
var efargs = d . efargs ;
var target = d . target ;
n _opcode = "Ext Fnct Cmd: " + efoperand ;
source = efargs ;
noLog = 0 ;
break ;
case "27" : // 0x27 - DENY_RSP
source = d . target . unit _id ;
srctag = d . target . tag ;
noLog = cbState ( 'je_deny' ) ? 0 : 1 ;
break ;
case "28" : // 0x28 - GRP_AFF_RSP
source = d . target . unit _id ;
target = d . group . tg _id ;
noLog = cbState ( 'je_joins' ) ? 0 : 1 ;
break ;
case "2a" : // 0x2A - GRP_AFF_Q - Group Affiliate Query
noLog = 1 ;
break ;
case "2b" : // 0x2B - LOC_REG_RSP - Location Registration Response
if ( d [ 'rv' ] != null ) {
switch ( d [ 'rv' ] ) {
case 0 :
n _opcode = "Joins" ;
target = d . group . tg _id ;
source = d . target . unit _id ;
srctag = d . target . tag ;
noLog = cbState ( 'je_joins' ) ? 0 : 1 ;
break ;
case 1 :
n _opcode = "Reg Fail" ;
target = d . group . tg _id ;
source = d . target . unit _id ;
srctag = d . target . tag ;
noLog = cbState ( 'je_deny' ) ? 0 : 1 ;
break ;
case 2 :
n _opcode = "Reg Denied" ;
target = d . group . tg _id ;
source = d . target . unit _id ;
srctag = d . target . tag ;
noLog = cbState ( 'je_deny' ) ? 0 : 1 ;
break ;
case 3 :
n _opcode = "Reg Refused" ;
target = d . group . tg _id ;
source = d . target . unit _id ;
srctag = d . target . tag ;
noLog = cbState ( 'je_deny' ) ? 0 : 1 ;
break ;
} // end rv switch
} // end if
break ;
case "2c" : // 0x2C - U_REG_RSP - Login TODO: source and target are the same from the json
source = d . source . unit _id ;
srctag = d . source . tag ;
// target = d.target.unit_id;
// tag = d.target.tag;
noLog = cbState ( 'je_log' ) ? 0 : 1 ;
break ;
case "2d" : // 0x2D - U_REG_CMD - Force SU Registration
source = d . source . unit _id ;
srctag = d . source . tag ;
target = d . target . unit _id ;
tag = d . target . tag ;
noLog = cbState ( 'je_log' ) ? 0 : 1 ;
break ;
case "2f" : // 0x2F - U_DE_REG_ACK - Logout
source = d . source . unit _id ;
srctag = d . source . tag ;
noLog = cbState ( 'je_log' ) ? 0 : 1 ;
break ;
case "31" : // 0x31 - AUTH_DMD - Authentication Demand
source = d . target _id . unit _id ;
srctag = d . target _id . tag ;
target = d . target _address . unit _id ;
tag = d . target _address . tag ;
break ;
case "33" : // 0x33 - iden up tdma
var sysid = d . sysid ;
var type = 'TDMA' ;
var iden = d . iden ;
var freq = d . freq / 1000000 ;
var offset = d . offset / 1000000 ;
var step = d . step / 100000 ;
var slots = d . slots ;
channelId ( sysid , iden , type , freq , offset , step , slots ) ;
noLog = 1 ;
break ;
case "34" : // 0x34 - iden up vhf/uhf
// TODO - test this
var sysid = d . sysid ;
var type = 'FDMA' ;
var iden = d . iden ;
var freq = d . freq / 1000000 ;
var offset = d . offset / 1000000 ;
var step = d . step / 100000 ;
var slots = 1 ;
channelId ( sysid , iden , type , freq , offset , step , slots ) ;
noLog = 1 ;
break ;
case "3d" : // 0x3D - iden up (7/800)
var sysid = d . sysid ;
var type = 'FDMA' ;
var iden = d . iden ;
var freq = d . freq / 1000000 ;
var offset = d . offset / 1000000 ;
var step = d . step / 100000 ;
var slots = 1 ;
channelId ( sysid , iden , type , freq , offset , step , slots ) ;
noLog = 1 ;
break ;
} // end switch - end handle manf opcodes
if ( d . options ) {
n _opcode = ( ( d . options >> 6 ) & 1 ) ? n _opcode = n _opcode + " ∅" : n _opcode ; //encrypted
}
c = smartColor ( tag ) ;
src _c = 0 ;
if ( d [ 'group' ] )
c = ( d [ 'group' ] [ 'color' ] ) ? d [ 'group' ] [ 'color' ] : c ;
if ( d [ 'sg' ] )
c = ( d [ 'sg' ] [ 'color' ] ) ? d [ 'sg' ] [ 'color' ] : c ;
tag = "<span class=\"c" + c + "\">" + tag ;
target = "<span class=\"c" + c + "\">" + target ;
if ( d [ 'group' ] ) {
src _c = d [ 'group' ] [ 'color' ] ;
}
if ( d [ 'source' ] ) {
src _c = d [ 'source' ] [ 'color' ] ;
}
if ( d [ 'srcaddr' ] ) { // present for voice calls, not present for other cc_events
src _c = ( d [ 'srcaddr' ] [ 'color' ] ) ? d [ 'srcaddr' ] [ 'color' ] : src _c ;
}
source = "<span class=\"c" + src _c + "\">" + source ;
srctag = "<span class=\"c" + src _c + "\">" + srctag ;
if ( cb && noLog == 0 ) {
appendJsonTable ( time , j _type , n _opcode , tag , target , source , srctag , opcode , 'history' ) ;
}
if ( target != "—" && logCall ) {
target = "<span name=\"tgid\" ondblclick=\"editTsv(this, 2, " + d . sysid + ", " + d . nac + ");\" class=\"c" + c + "\">" + target ;
tag = "<span name=\"tag\" ondblclick=\"editTsv(this, 2, " + d . sysid + ", " + d . nac + ");\" class=\"c" + c + "\">" + tag ;
source = "<span name=\"srcid\" ondblclick=\"editTsv(this, 2, " + d . sysid + ", " + d . nac + ");\" class=\"c" + src _c + "\">" + source ;
srctag = "<span name=\"srctag\" ondblclick=\"editTsv(this, 2, " + d . sysid + ", " + d . nac + ");\" class=\"c" + src _c + "\">" + srctag ;
var tdma _slot = d . tdma _slot ; // s/b null if not running tdma
appendCallHistory ( time , "--" , target , tag , source , srctag , 'callHistory' , d . options , xp , d . sysid , d . nac , tdma _slot ) ;
}
window . g _cc _event = d ;
break ;
} // end switch
dispatch [ d [ 'json_type' ] ] ( d ) ; // correct function is called based on json type in the dataset
} // end for
f _debug ( d ) ;
} // end dispatch_commands()
function cc _event ( d ) {
// does nothing right now
return ;
} // end cc_event()
function do _update ( ) {
f _debug ( ) ;
} // do_update()
function f _scan _button ( command ) {
if ( current _tgid == null )
send _command ( command , - 1 ) ;
else
send _command ( command , current _tgid ) ;
}
function f _goto _button ( command ) {
var _tgid = 0 ;
if ( command == 'goto' ) {
command = 'hold' ;
if ( current _tgid != null )
_tgid = current _tgid ;
_tgid = parseInt ( prompt ( 'Enter TGID to hold.' , _tgid ) ) ;
if ( isNaN ( _tgid ) || _tgid < 0 || _tgid > 65535 ) {
return ; // Cancel was pressed or invalid entry
}
// it's necessary to release current hold before trying to hold on a new tg
if ( $ ( '#holdIndicator' ) . is ( ':visible' ) ) {
send _command ( 'skip' , - 1 ) ;
setTimeout ( function ( ) {
send _command ( command , _tgid ) ;
} , 500 ) ;
return ;
}
send _command ( command , _tgid ) ;
}
}
function f _debug ( d ) {
if ( ! d _debug )
return ;
var html = "<div class='label'>Debug:<br>" ;
// html += 'window.g_nac = ' + window.g_nac;
html += '<br>' ;
html += 'json type: ' + d [ 'json_type' ] ;
html += '<br>' ;
html += "busy " + send _busy ;
html += " qfull " + send _qfull ;
html += " sendq size " + send _queue . length ;
html += " requests " + request _count ;
html += " <br>callbacks: " ;
html += " total=" + req _cb _count ;
html += " incomplete=" + nfinal _count ;
html += " error=" + n200 _count ;
html += " OK=" + r200 _count ;
html += "</div>" ;
$ ( '#div_debug' ) . html ( html ) ;
}
function popOut ( ) {
var myWindow = window . open ( window . location . href , '' , 'width=760,height=400' ) ;
}
function toggleCSS ( ) {
$ ( document . documentElement ) . attr ( 'data-theme' ) == 'light' ?
$ ( document . documentElement ) . attr ( 'data-theme' , 'dark' ) :
$ ( document . documentElement ) . attr ( 'data-theme' , 'light' ) ;
sdmode ( ) ;
}
function comma ( x ) {
// add comma formatting to whatever you give it (xx,xxxx,xxxx)
return x . toString ( ) . replace ( /\B(?=(\d{3})+(?!\d))/g , ',' ) ;
}
document . onkeydown = function ( evt ) {
console . log ( evt . altKey ) ;
// keyboard shortcuts
evt = evt || window . event ;
var x = document . activeElement . tagName ;
if ( x == "INPUT" || evt . altKey == true || evt . ctrlKey == true )
return ; // don't do anything if user is typing in an input field or if alt, ctrl key is bing used
switch ( evt . keyCode ) {
case 70 :
// 'f' key - show/hide frequency table
$ ( '#lastCommand' ) . html ( 'F - Freq Tbl<br><br>' ) . show ( ) ;
freqTable = ! freqTable ;
setTimeout ( function ( ) { $ ( '#lastCommand' ) . html ( '' ) . hide ( ) ; } , 2000 ) ;
break ;
case 71 :
// 'g' key - GOTO
$ ( '#lastCommand' ) . html ( 'G - GOTO<br><br>' ) . show ( ) ;
f _goto _button ( 'goto' ) ;
break ;
case 76 :
// 'l' key - LOCKOUT
$ ( '#lastCommand' ) . html ( 'L - LOCKOUT<br><br>' ) . show ( ) ;
f _scan _button ( 'lockout' ) ;
break ;
case 72 :
// 'h' key - HOLD
$ ( '#lastCommand' ) . html ( 'H - HOLD<br><br>' ) . show ( ) ;
f _scan _button ( 'hold' ) ;
break ;
case 80 :
// 'p' show/hide plots
$ ( '#lastCommand' ) . html ( 'P - Plots<br><br>' ) . show ( ) ;
minify ( 'div_images' ) ;
break ;
case 83 :
// 's' key - SKIP
$ ( '#lastCommand' ) . html ( 'S - Skip<br><br>' ) . show ( ) ;
f _scan _button ( 'skip' ) ;
break ;
case 86 :
// 'v' key - VIEW (light/dark)
$ ( '#lastCommand' ) . html ( 'V - View<br><br>' ) . show ( ) ;
toggleCSS ( ) ;
break ;
case 82 :
// 'r' key - RX screen
f _select ( 'rx' ) ;
break ;
case 74 :
// 'j' key - HOME screen
f _select ( 'status' ) ;
break ;
case 77 :
// 'm' key - MINIFY
minify ( 'nav-bar' ) ;
minify ( 'div_images' ) ;
minify ( 'div_s1' ) ;
break ;
case 48 : // '0' key - show/hide Main Display
minify ( 'controlsDisplay' ) ;
break ;
case 49 : // '1' key - show/hide callHistory
$ ( '#lastCommand' ) . html ( '1 - Calls<br><br>' ) . show ( ) ;
minify ( 'log_container_1' ) ;
break ;
case 50 :
$ ( '#lastCommand' ) . html ( '2 - Events<br><br>' ) . show ( ) ;
// '2' key - show/hide history (json log)
minify ( 'log_container_2' ) ;
break ;
case 51 :
// '3' key - show/hide logs block
$ ( '#lastCommand' ) . html ( '3 - Logs<br><br>' ) . show ( ) ;
if ( $ ( '#div_logs' ) . is ( ":hidden" ) ) {
$ ( '#div_logs' ) . show ( window . animateSpeed ) ;
} else {
$ ( '#div_logs' ) . hide ( window . animateSpeed ) ;
}
break ; // showBandPlan
case 52 :
// '4' key - show/hide Band Plan
$ ( '#lastCommand' ) . html ( '4 - Bandplan<br><br>' ) . show ( ) ;
$ ( '#showBandPlan' ) . trigger ( 'click' ) ;
setTimeout ( function ( ) { $ ( '#lastCommand' ) . html ( '' ) . hide ( ) ; } , 2000 ) ;
break ;
case 65 :
$ ( '#lastCommand' ) . html ( 'A - Neighbors<br><br>' ) . show ( ) ;
$ ( '#show_adj' ) . trigger ( 'click' ) ;
break ;
case 66 :
// 'b' key - bold
$ ( '#valFontStyle' ) . val ( 'bold' ) ;
break ;
case 78 :
// 'n' key - normal
$ ( '#valFontStyle' ) . val ( 'normal' ) ;
break ;
} // end switch
} ; // end onkeydown
function minify ( div ) {
$ ( '#' + div ) . toggle ( window . animateSpeed ) ;
}
function appendJsonTable ( a , b , c , d , e , f , srctag , opcode , target ) {
var numRows = document . getElementById ( target ) . rows . length ;
var size = parseInt ( $ ( '#log_len' ) . val ( ) ) ;
if ( ! Number . isInteger ( size ) ) {
// entry in Config / Display Options must be a number
size = 1500 ;
}
// shorter friendly view
var fv = {
'cc_event' : "<font color='#ff3300'>CC</font>" ,
'rx_update' : 'RX' ,
'change_freq' : 'CF' ,
'trunk_update' : 'TU'
} ;
if ( numRows > size )
document . getElementById ( target ) . deleteRow ( - 1 ) ;
var table = document . getElementById ( target ) ;
var lastRowIndex = table . rows . length - 1 ;
$ ( '#eh-count' ) . html ( ' ' ) ;
if ( d _debug == 1 )
$ ( '#eh-count' ) . html ( '   ' + comma ( table . rows . length ) ) ;
var skip = 0 ;
var prevTime = nohtml ( table . rows [ 1 ] . cells [ 0 ] . innerHTML ) ; // time
// do not duplicate history enteries - uses window object to store previous enteries and compares them with current data
// seems to work...
if ( target == 'history' && b == 'trunk_update' && window . g _trunk _update _c && window . g _trunk _update _d ) {
if ( c == window . g _trunk _update _c && d == window . g _trunk _update _d && a == prevTime ) {
skip = 1 ;
}
}
if ( target == 'history' && b . includes ( 'cc_event' ) && window . g _cc _event _c && window . g _cc _event _e ) {
if ( c == window . g _cc _event _c && ( e == window . g _cc _event _e || f == window . g _cc _event _f ) && a == prevTime ) {
skip = 1 ;
}
}
if ( target == 'history' && b . includes ( 'change_freq' ) && window . g _change _freq _c && window . g _change _freq _e ) {
if ( c == window . g _change _freq _c && e == window . g _change _freq _e && a == prevTime ) {
skip = 1 ;
}
}
if ( ! skip ) {
var row = table . insertRow ( 1 ) ; // 2nd row insert
var cell0 = row . insertCell ( 0 ) ;
var cell1 = row . insertCell ( 1 ) ;
var cell2 = row . insertCell ( 2 ) ;
var cell3 = row . insertCell ( 3 ) ;
var cell4 = row . insertCell ( 4 ) ;
var cell5 = row . insertCell ( 5 ) ;
var cell6 = row . insertCell ( 6 ) ;
var cell7 = row . insertCell ( 7 ) ;
opcode = opcode . toString ( ) ;
opcode = opcode . length == 1 ? "0" + opcode : opcode ;
cell0 . innerHTML = a ; //time
cell1 . innerHTML = ( target == 'history' ) ? '<div align="center">' + fv [ b ] + '</div>' : '<div align="center">' + b + '</div>' ; // type
cell2 . innerHTML = f ; // source id
cell3 . innerHTML = srctag ;
cell4 . innerHTML = '<div align="center">' + opcode . toUpperCase ( ) + '</div>' ; // opcode
cell5 . innerHTML = c ; // n_coode (event)
cell6 . innerHTML = e ; // target
cell7 . innerHTML = d ; // tag
// only update the window globals if we haven't skipped, so do this inside if !skip
if ( target == 'history' && b == 'trunk_update' ) {
window . g _trunk _update _c = c ;
window . g _trunk _update _d = d ;
}
if ( target == 'history' && b . includes ( 'cc_event' ) ) {
window . g _cc _event _c = c ;
window . g _cc _event _e = e ;
window . g _cc _event _f = f ;
}
if ( target == 'history' && b . includes ( 'change_freq' ) ) {
window . g _change _freq _c = c ;
window . g _change _freq _e = e ;
}
} // end if !skip
} // end appendJsonTable
function appendCallHistory ( a , b , c , d , e , f , target , options , xp , sysid , nac , tdma _slot ) {
var numRows = document . getElementById ( target ) . rows . length ;
// var size = document.getElementById('log_len').value;
var size = parseInt ( $ ( '#log_len' ) . val ( ) ) ;
if ( ! Number . isInteger ( size ) ) {
// entry in Config / Display Options must be a number
size = 1500 ;
}
if ( numRows > size )
$ ( '#' + target + ' tr:last' ) . remove ( ) ;
var table = document . getElementById ( target ) ;
$ ( '#ch-count' ) . html ( ' ' ) ;
if ( d _debug == 1 )
$ ( '#ch-count' ) . html ( '   ' + comma ( ( table . rows . length ) ) ) ;
var lastRowIndex = table . rows . length - 1 ;
var skip = 0 ;
var pri , enc , xpatch , x , y ;
b = ( options ) ? options : " " ;
enc = ( ( b >> 6 ) & 1 ) ? "∅ " : "" ;
pri = ( ( b >> 2 ) & 1 ) . toString ( ) + ( ( b >> 1 ) & 1 ) . toString ( ) + ( ( b >> 0 ) & 1 ) . toString ( ) ;
pri = parseInt ( pri , 2 ) ;
pri = ( xp ) ? "XP " : pri ;
var prevTime , prevTg , prevSrc ;
// avoid duplicate channel grant enteries into Call History table.
// Search previous 9 rows, compares current data with existing tgid, srcaddr, and time
// if tgid and srcaddr are equal and time is within 2 seconds, skip the entry,
search : {
for ( x = 1 ; x < 8 ; x ++ ) {
if ( ! table . rows [ x ] ) {
break search ;
}
prevTime = nohtml ( table . rows [ x ] . cells [ 0 ] . innerHTML ) ; // time
prevTg = nohtml ( table . rows [ x ] . cells [ 3 ] . innerHTML ) ; // tgid
prevSrc = nohtml ( table . rows [ x ] . cells [ 5 ] . innerHTML ) ; // source addr
var psec = prevTime . slice ( - 1 ) ;
var asec = a . slice ( - 1 ) ;
var diff = Math . abs ( psec - asec ) ;
if ( nohtml ( c ) == prevTg && nohtml ( e ) == prevSrc && ( a == prevTime || diff <= 2 ) ) {
skip = 1 ;
} // end if
} // end for
} // end search
if ( ( ( b >> 6 ) & 1 ) && cbState ( 'hide_enc' ) ) { // hide encrypted calls if selected in Config
skip = 1 ;
}
// do not append the Call History table if not enabled on Home tab
if ( enable _status [ nac ] == false ) {
skip = 1 ;
}
var tslot = '' ;
if ( cbState ( 'showSlot' ) == true && tdma _slot != null ) {
tslot = 'S' + ( tdma _slot + 1 ) + ' ' ;
}
if ( ! skip ) {
var row = table . insertRow ( 1 ) ; // 2nd row insert
var cell0 = row . insertCell ( 0 ) ;
var cell1 = row . insertCell ( 1 ) ;
var cell2 = row . insertCell ( 2 ) ;
var cell3 = row . insertCell ( 3 ) ;
var cell4 = row . insertCell ( 4 ) ;
var cell5 = row . insertCell ( 5 ) ;
var cell6 = row . insertCell ( 6 ) ;
cell0 . innerHTML = a ;
cell1 . innerHTML = '<div align="center">' + hex ( sysid ) . toUpperCase ( ) + '</div>' ;
cell2 . innerHTML = '<div align="center">' + tslot + enc + pri + '</div>' ;
cell3 . innerHTML = c ;
cell4 . innerHTML = d ;
cell5 . innerHTML = e ;
cell6 . innerHTML = f ;
} // end if !skip
} // end appendCallHistory
function appendErrorTable ( ax , bx , cx , dx , ex , fx , gx , target ) {
var numRows = document . getElementById ( target ) . rows . length ;
var size = parseInt ( $ ( '#log_len' ) . val ( ) ) ;
if ( ! Number . isInteger ( size ) ) {
// entry in Config / Display Options must be a number
size = 1500 ;
}
if ( numRows > size )
document . getElementById ( target ) . deleteRow ( - 1 ) ;
var table = document . getElementById ( target ) ;
var lastRowIndex = table . rows . length - 1 ;
var skip = 0 ;
if ( ! skip ) {
var row = table . insertRow ( 1 ) ; // 2nd row insert
var cell0 = row . insertCell ( 0 ) ;
var cell1 = row . insertCell ( 1 ) ;
var cell2 = row . insertCell ( 2 ) ;
var cell3 = row . insertCell ( 3 ) ;
var cell4 = row . insertCell ( 4 ) ;
var cell5 = row . insertCell ( 5 ) ;
var cell6 = row . insertCell ( 6 ) ;
cell0 . innerHTML = ax ; //time
cell1 . innerHTML = bx ;
cell2 . innerHTML = cx ;
cell3 . innerHTML = dx ;
cell4 . innerHTML = ex ;
cell5 . innerHTML = fx ;
cell6 . innerHTML = gx ;
} // end if !skip
} // end appendErrorTable
function update _freq ( d ) {
return ; // not currently used
}
function channelId ( sysid , iden , type , freq , offset , step , slots ) {
! ( sysid in channel _id ) && ( channel _id [ sysid ] = { } ) ;
! ( iden in channel _id [ sysid ] ) && ( channel _id [ sysid ] [ iden ] = { } ) ;
channel _id [ sysid ] [ iden ] = {
'iden' : iden , 'type' : type , 'freq' : freq , 'offset' : offset , 'step' : step , 'slots' : slots
} ;
}
function smartColor ( t ) {
// searches string t for items in sc1 and sc2, returns a color if found.
var z = 4 ; // number of smart colors - TOTO add the UI elements in index.html
if ( ! t || ! cbState ( 'smartcolors' ) )
return 0 ; // do nothing if there is no talkgroup name passed, or if the box is not checked (cb default is false!)
var tag = t . toString ( ) . toUpperCase ( ) ; // throws an error if t is an int
var color = 0 ;
var ele , x , i , sc , alen ;
for ( i = 1 ; i < ( z + 1 ) ; i ++ ) {
ele = document . getElementById ( 'sc' + i ) . value ;
sc = ele . split ( " " ) ;
for ( var x = 0 ; x < sc . length ; x ++ ) {
if ( tag . includes ( sc [ x ] . toUpperCase ( ) ) ) {
color = i ;
return color ;
}
}
}
return color ;
} // end smartColor()
function divExpand ( div ) {
console . log ( div ) ;
var h = $ ( '#' + div ) . height ( ) ;
console . log ( typeof ( h ) ) ;
switch ( true ) {
case ( h < 2 ) :
console . log ( 'case 1:' + h ) ;
$ ( '#' + div ) . height ( 301 ) ;
$ ( '#' + div ) . show ( ) ;
break ;
case ( h <= 301 ) :
console . log ( 'case 2:' + h ) ;
$ ( '#' + div ) . height ( 702 ) ;
break ;
case ( h < 9999 ) :
console . log ( 'case 3:' + h ) ;
$ ( '#' + div ) . height ( 1 ) ;
$ ( '#' + div ) . hide ( ) ;
break ;
default :
console . log ( h < 2 ) ;
console . log ( h < 301 ) ;
console . log ( h < 9999 ) ;
}
}
function openTable ( div , ref ) {
// popout window for review/search of log tables
var divText = $ ( '#' + div ) . prop ( 'outerHTML' ) ;
divText = divText . replace ( 'table id="history"' , 'table id="searchTable"' ) ;
divText = divText . replace ( 'table id="callHistory"' , 'table id="searchTable"' ) ;
divText = divText . replace ( 'table id="errors"' , 'table id="searchTable"' ) ;
var myWindow = window . open ( '' , '' , 'width=900,height=600' ) ;
var view = document . documentElement . getAttribute ( 'data-theme' ) ;
var doc = myWindow . document ;
doc . open ( ) ;
doc . write ( '<script src="main.js"></script>' ) ;
doc . write ( '<script src="jquery.js"></script>' ) ;
doc . write ( '<script src="editor.js"></script>' ) ;
doc . write ( '<script>window.tgid_files = window.opener.tgid_files;</script>' ) ;
doc . write ( '<script>window.srcid_files = window.opener.srcid_files;</script>' ) ;
doc . write ( '<script>generateCSS(true);</script>' ) ;
// search icon 🔎
doc . write ( '<span class="nac">' + ref . id + '</span><br><br>' ) ;
doc . write ( '<input type="text" id="searchInput" onkeyup="searchTable()" placeholder="Search" title="Search">' ) ;
doc . write ( '<div align="right"><a href="#" style="text-decoration: none;" onclick="csvTable(\'searchTable\');">' ) ;
doc . write ( '<img src="csv.png" width="30" title="Download CSV"></a></div><br>' ) ;
doc . write ( '<script>document.getElementById("searchInput").focus();</script>' ) ;
doc . write ( '<link rel="stylesheet" type="text/css" id="style" href="main.css">' ) ;
if ( view == 'dark' )
doc . documentElement . setAttribute ( 'data-theme' , 'dark' ) ;
doc . write ( divText ) ;
doc . close ( ) ;
}
function searchTable ( ) {
var input , filter , table , tr , i , x , s , cols ;
var td = [ ] ;
cols = document . getElementById ( 'searchTable' ) . rows [ 1 ] . cells . length ;
input = document . getElementById ( 'searchInput' ) ;
filter = input . value . toUpperCase ( ) ;
table = document . getElementById ( 'searchTable' ) ;
tr = table . getElementsByTagName ( 'tr' ) ;
for ( i = 1 ; i < tr . length ; i ++ ) {
var s = undefined ;
for ( x = 0 ; x < cols ; x ++ ) {
td [ x ] = tr [ i ] . getElementsByTagName ( 'td' ) [ x ] ;
s = s + ' ' + nohtml ( td [ x ] . innerHTML ) . toUpperCase ( ) ;
}
if ( s . includes ( filter ) ) {
tr [ i ] . style . display = '' ;
} else {
tr [ i ] . style . display = 'none'
}
} // end for
} // end function
function searchTsvTable ( ) {
var input , filter , table , tr , i , x , s , y , cols ;
var td = [ ] ;
table = document . getElementById ( 'talkgroups' ) ;
cols = table . rows [ 1 ] . cells . length ;
input = document . getElementById ( 'searchInput' ) ;
filter = input . value . toUpperCase ( ) ;
tr = table . getElementsByTagName ( 'tr' ) ;
for ( i = 1 ; i < tr . length ; i ++ ) {
var s = undefined ;
for ( x = 1 ; x < cols ; x ++ ) { // don't search the first column [0]
td [ x ] = tr [ i ] . getElementsByTagName ( 'td' ) [ x ] ;
s += ' ' + td [ x ] . firstChild . value . toUpperCase ( ) ;
}
if ( s . includes ( filter ) ) {
tr [ i ] . style . display = '' ;
} else {
tr [ i ] . style . display = 'none'
}
} // end for
} // end function
function hex ( dec ) {
if ( ! dec ) return ;
return dec . toString ( 16 ) ;
}
function dec ( hex ) {
if ( ! hex ) return ;
return parseInt ( hex , 16 ) ;
}
function nohtml ( str ) {
if ( typeof str != 'string' ) return str ;
var x = str . replace ( /(<([^>]+)>)/gi , "" ) ;
return x ;
}
function freqDisplay ( f ) {
var freq ;
if ( ! f ) return ;
if ( cbState ( 'trailing_zeros' ) ) {
freq = f . toFixed ( 5 ) ;
} else {
if ( Number . isInteger ( f ) ) {
freq = f + ".0" ;
} else {
freq = f ;
}
}
return freq ;
}
function displayMode ( ) { // just returns the display mode
var x , y ;
x = document . documentElement . getAttribute ( 'data-theme' ) ;
y = ( x == "dark" ) ? "dark" : "light" ;
return y ;
}
function sdmode ( ) {
$ ( '#selDispMode' ) . val ( displayMode ( ) ) ;
}
function sdmodeChange ( ) {
var y = document . getElementById ( 'selDispMode' ) ;
document . documentElement . setAttribute ( 'data-theme' , y . value ) ;
}
function is _digit ( s ) {
return ( ( s >= '0' && s <= '9' ) ) ;
}
function getTime ( x ) {
//expects Unix timestamp, returns 24 hour time, hrs:min:sec
date = new Date ( parseInt ( x ) ) ;
var time = zeroPad ( date . getHours ( ) , 2 ) + ':' + zeroPad ( date . getMinutes ( ) , 2 ) + ':' + zeroPad ( date . getSeconds ( ) , 2 ) ;
return time ;
}
function cbState ( x ) {
// returns the state (true / false) of whatever checkbox you ask it to
return $ ( '#' + x ) . is ( ':checked' ) ;
}
function csvTable ( table _id , separator = ',' ) { // Quick and simple export target #table_id into a csv
// Select rows from table_id
var rows = document . querySelectorAll ( 'table#' + table _id + ' tr' ) ;
// Construct csv
var csv = [ ] ;
for ( var i = 0 ; i < rows . length ; i ++ ) {
var row = [ ] ,
cols = rows [ i ] . querySelectorAll ( 'td, th' ) ;
for ( var j = 0 ; j < cols . length ; j ++ ) {
// Clean innertext to remove multiple spaces and jumpline (break csv)
var data = cols [ j ] . innerText . replace ( /(\r\n|\n|\r)/gm , '' ) . replace ( /(\s\s)/gm , ' ' ) ;
// Escape double-quote with double-double-quote
data = data . replace ( /"/g , '""' ) ;
// Push escaped string
row . push ( '"' + data + '"' ) ;
}
csv . push ( row . join ( separator ) ) ;
}
var csv _string = csv . join ( '\n' ) ;
// Download it
var filename = 'export_' + table _id + '_' + new Date ( ) . toLocaleDateString ( ) + '.csv' ;
var link = document . createElement ( 'a' ) ;
link . style . display = 'none' ;
link . setAttribute ( 'target' , '_blank' ) ;
link . setAttribute ( 'href' , 'data:text/csv;charset=utf-8,' + encodeURIComponent ( csv _string ) ) ;
link . setAttribute ( 'download' , filename ) ;
document . body . appendChild ( link ) ;
link . click ( ) ;
document . body . removeChild ( link ) ;
}
function exportToCsv ( filename , rows ) { // another csv export tool, but this one accepts an array.
var processRow = function ( row ) {
var finalVal = '' ;
for ( var j = 0 ; j < row . length ; j ++ ) {
var innerValue = row [ j ] === null ? '' : row [ j ] . toString ( ) ;
if ( row [ j ] instanceof Date ) {
innerValue = row [ j ] . toLocaleString ( ) ;
} ;
var result = innerValue . replace ( /"/g , '""' ) ;
if ( result . search ( /("|,|\n)/g ) >= 0 )
result = '"' + result + '"' ;
if ( j > 0 )
finalVal += ',' ;
finalVal += result ;
}
return finalVal + '\n' ;
} ;
var csvFile = '' ;
for ( var i = 0 ; i < rows . length ; i ++ ) {
csvFile += processRow ( rows [ i ] ) ;
}
var blob = new Blob ( [ csvFile ] , { type : 'text/csv;charset=utf-8;' } ) ;
if ( navigator . msSaveBlob ) { // IE 10+
navigator . msSaveBlob ( blob , filename ) ;
} else {
var link = document . createElement ( "a" ) ;
if ( link . download !== undefined ) { // feature detection
// Browsers that support HTML5 download attribute
var url = URL . createObjectURL ( blob ) ;
link . setAttribute ( "href" , url ) ;
link . setAttribute ( "download" , filename ) ;
link . style . visibility = 'hidden' ;
document . body . appendChild ( link ) ;
link . click ( ) ;
document . body . removeChild ( link ) ;
}
}
}
function sortTable ( t , c ) { // table id, column index
if ( ! cbState ( 'show_adj' ) )
return ;
if ( ! t ) return ;
var table , rows , switching , i , x , y , shouldSwitch ;
table = document . getElementById ( t ) ;
switching = true ;
while ( switching ) {
switching = false ;
rows = table . rows ;
// (skip the first row (0), which contains table headers)
for ( i = 1 ; i < ( rows . length - 1 ) ; i ++ ) {
shouldSwitch = false ;
x = rows [ i ] . getElementsByTagName ( "TD" ) [ c ] ;
y = rows [ i + 1 ] . getElementsByTagName ( "TD" ) [ c ] ;
if ( x . innerHTML . toLowerCase ( ) > y . innerHTML . toLowerCase ( ) ) {
shouldSwitch = true ;
break ;
}
}
if ( shouldSwitch ) {
rows [ i ] . parentNode . insertBefore ( rows [ i + 1 ] , rows [ i ] ) ;
switching = true ;
}
}
} // end sort table
function sources ( d ) { // d json type = trunk_update
// stores source IDs and tags and colors in a global object
// USAGE:
// window['tgidc' + tgid] = tgid color
// window['tgidt' + tgid] = tgid tag "talkgroup"
// window['srct' + srcaddr] = srcaddr tag
// window['srcc' + srcaddr] = srcaddr color
if ( Object . keys ( d ) . length < 1 )
return ;
var f , nac , srcaddr , srcaddr _tag ;
var ft = 'frequency_tracking' ;
var calls = 'calls' ;
for ( nac in d ) {
if ( Number . isInteger ( parseInt ( nac ) ) ) {
for ( f in d [ nac ] [ ft ] ) {
srcaddr _tag = d [ nac ] [ ft ] [ f ] [ 'calls' ] [ 0 ] [ 'srcaddr' ] [ 'tag' ] ? d [ nac ] [ ft ] [ f ] [ 'calls' ] [ 0 ] [ 'srcaddr' ] : null ;
srcaddr = d [ nac ] [ ft ] [ f ] [ 'calls' ] [ 0 ] [ 'srcaddr' ] [ 'unit_id' ] ? d [ nac ] [ ft ] [ f ] [ 'calls' ] [ 0 ] [ 'srcaddr' ] : null ;
if ( srcaddr && srcaddr _tag ) {
window [ 'tgidc' + d [ nac ] [ ft ] [ f ] [ 'calls' ] [ 0 ] [ 'tgid' ] [ 'tg_id' ] ] = d [ nac ] [ ft ] [ f ] [ 'calls' ] [ 0 ] [ 'tgid' ] [ 'color' ] ;
window [ 'tgidt' + d [ nac ] [ ft ] [ f ] [ 'calls' ] [ 0 ] [ 'tgid' ] [ 'tg_id' ] ] = d [ nac ] [ ft ] [ f ] [ 'calls' ] [ 0 ] [ 'tgid' ] [ 'tag' ] ;
window [ 'srct' + d [ nac ] [ ft ] [ f ] [ 'calls' ] [ 0 ] [ 'srcaddr' ] [ 'unit_id' ] ] = d [ nac ] [ ft ] [ f ] [ 'calls' ] [ 0 ] [ 'srcaddr' ] [ 'tag' ] ;
window [ 'srcc' + d [ nac ] [ ft ] [ f ] [ 'calls' ] [ 0 ] [ 'srcaddr' ] [ 'unit_id' ] ] = d [ nac ] [ ft ] [ f ] [ 'calls' ] [ 0 ] [ 'srcaddr' ] [ 'color' ] ;
}
if ( d [ nac ] [ ft ] [ f ] [ 'tdma' ] == true ) {
window [ nac + 'flavor' ] = "2" ; // set it as p2 and leave it that way for the duration
}
} // end for f
} // end if number
} // end for x
} // end function
function getReason ( r ) {
// denial reason lookup
if ( g _reason [ r ] ) {
result = '0x' + r + ' - ' + g _reason [ r ] ;
} else {
result = '0x' + r ;
}
return result ;
}
function saveDisplaySettings ( ) {
var settings = { } ;
$ ( '#saveSettings' ) . html ( 'Saved' ) ;
setTimeout ( ( ) => { $ ( '#saveSettings' ) . html ( 'Save Display Settings' ) ; } , 2000 ) ;
var s = [
"valSystemFont" ,
"valTagFont" ,
"valTruncate" ,
"valFontStyle" ,
"sc1" ,
"sc2" ,
"sc3" ,
"sc4" ,
"log_len" ,
"color_main_tag" ,
"color_main_sys" ,
"smartcolors" ,
"log_cc" ,
"log_cf" ,
"log_tu" ,
"log_rx" ,
"show_adj" ,
"je_joins" ,
"je_calls" ,
"je_deny" ,
"je_log" ,
"hide_enc" ,
"trailing_zeros" ,
"selDispMode" ,
"acc1" ,
"acc2" ,
"sysColor" ,
"valColor" ,
"sysColor" ,
"btnColor" ,
"unk_default" ,
"ani_speed" ,
"showBandPlan" ,
"showSlot" ,
"oplogip" ,
"oplogport" ,
"showLast" ,
"colSlot2" ] ;
for ( r in s ) {
if ( $ ( '#' + s [ r ] ) . attr ( 'type' ) == "checkbox" ) {
settings [ s [ r ] ] = $ ( '#' + s [ r ] ) . is ( ':checked' ) ? true : false ;
} else {
settings [ s [ r ] ] = $ ( '#' + s [ r ] ) . val ( ) ;
}
} // end for
var settingsJSON = JSON . stringify ( settings , null , 2 ) ;
send _command ( 'config-savesettings' , settingsJSON ) ;
}
function loadHelp ( ) { // this is also called from editor.js
f = 'help.html' ;
$ . ajax ( {
url : f ,
type : 'GET' ,
success : popHelp ,
error : function ( XMLHttpRequest , textStatus , errorThrown ) { alert ( 'Settings File Acces Error: \n\nFile:' + f + '\n\n' + errorThrown + '\n\n' ) ; }
} ) ;
}
function popHelp ( h ) {
$ ( '#div_help' ) . html ( h )
}
function beginJsonSettings ( ) { // this is also called from editor.js
f = 'ui-settings.json' ;
$ . ajax ( {
url : f ,
type : 'GET' ,
success : loadJsonDisplaySettings ,
error : function ( XMLHttpRequest , textStatus , errorThrown ) { alert ( 'Settings File Acces Error: \n\nFile:' + f + '\n\n' + errorThrown + '\n\n' ) ; }
} ) ;
}
function loadJsonDisplaySettings ( settings ) {
$ ( '#loadSettings' ) . html ( 'Loaded' ) ;
setTimeout ( ( ) => { $ ( '#loadSettings' ) . html ( 'Load Settings' ) ; } , 2000 ) ;
var ele , m ;
for ( item in settings ) {
ele = document . getElementById ( item ) ;
if ( ele ) {
if ( ele . type == "checkbox" ) {
ele . checked = settings [ item ] == true ? true : false ;
continue ;
}
if ( ele . type == "select" ) {
ele . value = settings [ item ] ;
continue ;
}
ele . value = settings [ item ] ;
}
if ( item == "selDispMode" ) {
m = settings [ item ] ;
document . documentElement . setAttribute ( 'data-theme' , m ) ;
sdmode ( ) ;
}
// populate url into oplog url field
var x = window . location . origin . split ( ':' ) ;
// var y = x[0] + ':' + x[1] + ':5000';
// $('#oplogUrl').val(y);
$ ( '#oplogip' ) . val ( x [ 1 ] . substring ( 2 ) ) ;
$ ( '#oplogport' ) . val ( '5000' ) ;
if ( item == "oplogip" ) {
$ ( '#oplogip' ) . val ( settings [ item ] ) ;
}
if ( item == "oplogport" ) {
$ ( '#oplogport' ) . val ( settings [ item ] ) ;
}
$ ( '#oplogUrl' ) . val ( 'http://' + $ ( '#oplogip' ) . val ( ) + ':' + $ ( '#oplogport' ) . val ( ) ) ;
} // end for item
uiColorRefresh ( ) ;
}
function uiColorRefresh ( ) { // this is also called from editor.js
$ ( '#acc1' ) . trigger ( 'change' ) ;
$ ( '#valColor' ) . trigger ( 'change' ) ;
$ ( '#sysColor' ) . trigger ( 'change' ) ;
$ ( '#btnColor' ) . trigger ( 'change' ) ;
$ ( '#ani_speed' ) . trigger ( 'change' ) ;
}
function changeCss ( className , classValue ) {
// we need invisible container to store additional css definitions
var cssMainContainer = $ ( '#css-modifier-container' ) ;
if ( cssMainContainer . length == 0 ) {
cssMainContainer = $ ( '<div id="css-modifier-container"></div>' ) ;
cssMainContainer . hide ( ) ;
cssMainContainer . appendTo ( $ ( 'body' ) ) ;
}
// and we need one div for each class
var classContainer = cssMainContainer . find ( 'div[data-class="' + className + '"]' ) ;
if ( classContainer . length == 0 ) {
classContainer = $ ( '<div data-class="' + className + '"></div>' ) ;
classContainer . appendTo ( cssMainContainer ) ;
}
// append additional style
classContainer . html ( '<style>' + className + ' {' + classValue + '}</style>' ) ;
}
function getSiteAlias ( ) {
if ( localStorage . AliasTableUpdated == false )
return ;
if ( event _source == null || event _source . readyState != 1 ) // the server has gone out to lunch
return ;
$ . ajax ( {
url : 'site-alias.json' ,
type : 'GET' ,
success : loadSiteAlias ,
error : function ( XMLHttpRequest , textStatus , errorThrown ) { console . log ( 'site-alias.json file not found. \n\nFile:' + url + '\n\n' + errorThrown + '\n\n' ) ; }
} ) ;
}
function loadSiteAlias ( json ) {
window . siteAlias = json ;
localStorage . AliasTableUpdated == false ;
}
function accColorSel ( ) { // this is also called from editor.js
$ ( '.accColor' ) . on ( 'change' , function ( ) {
this . blur ( ) ;
var c1 = $ ( '#acc1' ) . val ( ) ;
var c2 = $ ( '#acc2' ) . val ( ) ;
var z = 'linear-gradient(' + c1 + ', ' + c2 + ')' ;
$ ( '.controlsDisplay' ) . css ( 'background' , z ) ;
} ) ;
$ ( '#valColor' ) . on ( 'change' , function ( ) {
this . blur ( ) ;
var c1 = $ ( '#valColor' ) . val ( ) ;
changeCss ( '.value' , 'color: ' + c1 + ';' ) ;
changeCss ( '.nac' , 'color: ' + c1 + ';' ) ;
} ) ;
$ ( '#sysColor' ) . on ( 'change' , function ( ) {
this . blur ( ) ;
var c1 = $ ( '#sysColor' ) . val ( ) ;
changeCss ( '.systgid' , 'color: ' + c1 + ';' ) ;
} ) ;
$ ( '#btnColor' ) . on ( 'change' , function ( ) {
this . blur ( ) ;
var c1 = $ ( '#btnColor' ) . val ( ) ;
changeCss ( 'button' , 'color: ' + c1 + ';' ) ;
changeCss ( '.nav-button-active' , 'color: ' + c1 + ';' ) ;
changeCss ( '.nav-button:hover' , 'color: ' + c1 + ';' ) ;
changeCss ( '.nav-button-active:hover' , 'color: ' + c1 + ';' ) ;
changeCss ( '.btn' , 'color: ' + c1 + ';' ) ;
changeCss ( '.control-button' , 'color: ' + c1 + ';' ) ;
changeCss ( '.control-button:hover' , 'color: ' + c1 + ';' ) ;
} ) ;
$ ( '#ani_speed' ) . on ( 'change' , function ( ) {
this . blur ( ) ;
window . animateSpeed = parseInt ( this . value ) ;
} ) ;
}
// color, animation, backgroundColor - used for main talkgroup/system display
// returns the property value of the selector supplied. sheet is document.styleSheets[index].title
function getProperty ( selector , property , sheet ) {
for ( y in document . styleSheets ) {
if ( document . styleSheets [ y ] . title == sheet ) // this title is set in the json color map builder func
break ;
}
rules = document . styleSheets [ y ] . cssRules ;
for ( i in rules ) {
if ( rules [ i ] . selectorText == selector )
return rules [ i ] [ 'style' ] [ property ] ;
}
return false ;
}
function getStyle ( className ) { //test, not used
var cssText = "" ;
var classes = document . styleSheets [ 1 ] . rules || document . styleSheets [ 0 ] . cssRules ;
for ( var x = 0 ; x < classes . length ; x ++ ) {
if ( classes [ x ] . selectorText == className ) {
cssText += classes [ x ] . cssText || classes [ x ] . style . cssText ;
}
}
return cssText ;
}
var g _opcode = { // global opcodes
'0' : 'Call' , // Group Voice Grant grp_v_ch_grant dec=0
'1' : 'Reserved' , // REserved 0x01
'2' : 'Update 0x02' ,
'3' : 'Update 0x03' ,
'4' : 'UnitVoiceGrant' ,
'5' : 'UU_ANS_RSP' , // unit to unit answer response
'6' : 'UnitVoiceUpdate' ,
'8' : 'PhoneGrant' ,
'9' : 'TELE_INT_PSTN_AEQ' ,
'0a' : 'Phone Alert' , // tele interconnect
'0b' : 'Reserved' ,
'10' : 'UnitDataGrant' ,
'11' : 'GroupDataGrant' ,
'12' : 'GroupDataUpdate' ,
'13' : 'GroupDataUpdateExplicit' ,
'18' : 'UnitStatusUpdate' ,
'1a' : 'UnitStatusQuery' ,
'1c' : 'UnitShortMessage' ,
'1d' : 'UnitMonitor' ,
'1f' : 'UnitCallAlert' , // Call Alert
'20' : 'Ack Response' , // AckResponse - ACK_RESP_U - This is the generic response supplied by a unit to acknowledge an action when there is no other expected response. Response from radio to system poll ("are you still there?")
'21' : 'QueuedResponse' ,
'24' : 'ExtFunctionCommand' ,
'27' : 'Denied' , // DenyResponse
'28' : 'Joins' , // GroupAffiliationResponse - GRP_AFF_RSP - This is the response to the request for group affiliation by a unit. This will present the necessary information to the requesting unit to allow it to perform group operations for the indicated group identity.
'29' : 'Ack Response FNE' , // This is the generic response supplied to a unit to acknowledge an action when there is no other expected response. This response is sent to a subscriber unit in response to an earlier action or service request.
'2a' : 'Group Aff Q' , // GRP_AFF_Q - This transaction is to be used to determine what a targeted subscriber unit maintains as the group affiliation data for the unit. The Query will usually originate in the system, but the standard enables other originators.
'2b' : 'Joins' , // LocRegResponse - This transaction is to be used to respond to a Location Registration Request. The response indicates that the subscriber is registered in the new location area.
'2c' : 'Login' , // UnitRegResponse
'2d' : 'Force SU Reg' , // UnitRegCommand - U_REG_CMD - This transaction is to be used to force an SU to initiate Unit Registration
'2e' : 'UnitAuthCommand' ,
'2f' : 'Logout' , // UnitDeregAck
'31' : 'Auth Demand' ,
'36' : 'RoamingAddrCommand' ,
'37' : 'RoamingAddrUpdate' ,
'38' : 'SystemServiceBroadcast' , // This broadcast will inform the subscriber units of the current system services supported and currently offered on the Primary control channel of this site.
'39' : 'AltControlChannel' ,
'3a' : 'RfssStatusBroadcast' ,
'3b' : 'NetworkStatusBroadcast' ,
'3c' : 'AdjacentSite' , // Adjacent Site Broadcast
'3d' : 'ChannelParamsUpdate' , // IDEN_UP
'3e' : 'ProtectionParamBroadcast' ,
'3f' : 'ProtectionParamUpdate'
} ;
var g _reason = { // TIA-10.AABC-B Annex B Deny Response Reason Codes
'10' : 'Unit not valid' ,
'11' : 'Unit not authoirized' ,
'20' : 'Target unit not valid' ,
'21' : 'Target unit not authorized' ,
'2f' : 'Target unit refused' ,
'30' : 'Target group invalid' ,
'31' : 'Target group not authoirzed' ,
'40' : 'Invalid dialing' ,
'41' : 'Telephone number not authroized' ,
'42' : 'PSTN address invalid' ,
'50' : 'Call time-out' ,
'51' : 'Call terminated by landline' ,
'52' : 'Call terminated by subscriber' ,
'5f' : 'Call pre-empted' ,
'60' : 'Site access denial' ,
'61' : 'User/system def' , // 0x61 - 0xEF per standard
'67' : 'User/sys def' ,
'77' : 'User/sys def' ,
'c0' : 'User/sys def' , // seen on MOT system
'f0' : 'Call options invalid' ,
'f1' : 'Protection service option invalid' ,
'f2' : 'Duples service option invalid' ,
'f3' : 'circuit/packet mode service option invalid' ,
'ff' : 'Service not supported by system'
} ;
var g _moto _opcode = { // opcodes when mfrid is MOT (0x90, 144)
'0' : 'MOT Add Patch Group' ,
'1' : 'MOT Del Patch Group' ,
'3' : 'MOT Patch Voice Channel Grant Update' ,
'4' : 'MOT Unknown' ,
'5' : 'MOT Traffic Chan Stn ID' ,
'6' : 'MOT Unknown' ,
'7' : 'MOT Unknown' ,
'8' : 'MOT Unknown' ,
'9' : 'MOT System Load' ,
'0a' : 'MOT Unknown' ,
'0b' : 'MOT Control Chan Base Stn ID' ,
'0c' : 'MOT Unknown' ,
'0d' : 'MOT Unknown' ,
'0e' : 'MOT Planned Control Channel Shutdown' ,
// 0f through 3f = unknown
} ;
var g _serviceOption = {
// TODO - populate this, not used currently.
// bits 0-2 priority
'0' : 'reserved' ,
'1' : 'Lowest' ,
'2' : 'User/system def' ,
'3' : 'User/system def' ,
'4' : 'Default' ,
'5' : 'User/system def' ,
'6' : 'User/system def' ,
'7' : 'Highest' ,
// bit 3 - reserved
// bit 4 - mode
} ;
// const value_string MFIDS[] = {
// { 0x00, "Standard MFID (pre-2001)" },
// { 0x01, "Standard MFID (post-2001)" },
// { 0x09, "Aselsan Inc." },
// { 0x10, "Relm / BK Radio" },
// { 0x18, "EADS Public Safety Inc." },
// { 0x20, "Cycomm" },
// { 0x28, "Efratom Time and Frequency Products, Inc" },
// { 0x30, "Com-Net Ericsson" },
// { 0x34, "Etherstack" },
// { 0x38, "Datron" },
// { 0x40, "Icom" },
// { 0x48, "Garmin" },
// { 0x50, "GTE" },
// { 0x55, "IFR Systems" },
// { 0x5A, "INIT Innovations in Transportation, Inc" },
// { 0x60, "GEC-Marconi" },
// { 0x64, "Harris Corp." },
// { 0x68, "Kenwood Communications" },
// { 0x70, "Glenayre Electronics" },
// { 0x74, "Japan Radio Co." },
// { 0x78, "Kokusai" },
// { 0x7C, "Maxon" },
// { 0x80, "Midland" },
// { 0x86, "Daniels Electronics Ltd." },
// { 0x90, "Motorola" },
// { 0xA0, "Thales" },
// { 0xA4, "M/A-COM" },
// { 0xB0, "Raytheon" },
// { 0xC0, "SEA" },
// { 0xC8, "Securicor" },
// { 0xD0, "ADI" },
// { 0xD8, "Tait Electronics" },
// { 0xE0, "Teletec" },
// { 0xF0, "Transcrypt International" },
// { 0xF8, "Vertex Standard" },
// { 0xFC, "Zetron, Inc" },
// };
// const value_string ALGIDS[] = {
// /* Type I */
// { 0x00, "ACCORDION 1.3" },
// { 0x01, "BATON (Auto Even)" },
// { 0x02, "FIREFLY Type 1" },
// { 0x03, "MAYFLY Type 1" },
// { 0x04, "SAVILLE" },
// { 0x05, "Motorola Assigned - PADSTONE" },
// { 0x41, "BATON (Auto Odd)" },
// /* Type III */
// { 0x80, "Unencrypted" },
// { 0x81, "DES-OFB, 56 bit key" },
// { 0x83, "3 key Triple DES, 168 bit key" },
// { 0x84, "AES-256-OFB" },
// { 0x85, "AES-128-ECB"},
// { 0x88, "AES-CBC"},
// { 0x89, "AES-128-OFB"},
// /* Motorola proprietary - some of these have been observed over the air,
// some have been taken from firmware dumps on various devices, others
// have come from the TIA's FTP website while it was still public,
// from document "ALGID Guide 2015-04-15.pdf", and others have been
// have been worked out with a little bit of "guesswork" ;) */
// { 0x9F, "Motorola DES-XL 56-bit key" },
// { 0xA0, "Motorola DVI-XL" },
// { 0xA1, "Motorola DVP-XL" },
// { 0xA2, "Motorola DVI-SPFL"},
// { 0xA3, "Motorola HAYSTACK" },
// { 0xA4, "Motorola Assigned - Unknown" },
// { 0xA5, "Motorola Assigned - Unknown" },
// { 0xA6, "Motorola Assigned - Unknown" },
// { 0xA7, "Motorola Assigned - Unknown" },
// { 0xA8, "Motorola Assigned - Unknown" },
// { 0xA9, "Motorola Assigned - Unknown" },
// { 0xAA, "Motorola ADP (40 bit RC4)" },
// { 0xAB, "Motorola CFX-256" },
// { 0xAC, "Motorola Assigned - Unknown" },
// { 0xAD, "Motorola Assigned - Unknown" },
// { 0xAE, "Motorola Assigned - Unknown" },
// { 0xAF, "Motorola AES-256-GCM (possibly)" },
// { 0xB0, "Motorola DVP"},
// };