From 54afa206d09d9f3128788d0f3a45641852d19796 Mon Sep 17 00:00:00 2001 From: phl0 Date: Thu, 24 Aug 2017 11:05:31 +0200 Subject: [PATCH 001/244] Add ambed binary to make clean --- ambed/makefile | 4 ++-- ambed/readme | 2 +- ambedtest/makefile | 2 +- src/makefile | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ambed/makefile b/ambed/makefile index 0aa514e..c556a28 100644 --- a/ambed/makefile +++ b/ambed/makefile @@ -14,9 +14,9 @@ $(EXECUTABLE): $(OBJECTS) $(CC) $(CFLAGS) $< -o $@ clean: - $(RM) *.o + $(RM) $(EXECUTABLE) *.o install: mkdir -p /ambed - cp ./ambed /ambed/ + cp $(EXECUTABLE) /ambed/ cp ./run /ambed/ diff --git a/ambed/readme b/ambed/readme index 3c6713a..6da694a 100644 --- a/ambed/readme +++ b/ambed/readme @@ -61,8 +61,8 @@ Follow FTDI provided documentation for installation and testing of the drivers. # git clone https://github.com/LX3JL/xlxd.git # cd xlxd/ambed/ -# make # make clean +# make # make install 3) configuring ambed startup script diff --git a/ambedtest/makefile b/ambedtest/makefile index 9e795b1..bc1a804 100644 --- a/ambedtest/makefile +++ b/ambedtest/makefile @@ -14,4 +14,4 @@ $(EXECUTABLE): $(OBJECTS) $(CC) $(CFLAGS) $< -o $@ clean: - $(RM) *.o + $(RM) $(EXECUTABLE) *.o diff --git a/src/makefile b/src/makefile index 6ec8420..f0f1378 100644 --- a/src/makefile +++ b/src/makefile @@ -14,11 +14,11 @@ $(EXECUTABLE): $(OBJECTS) $(CC) $(CFLAGS) $< -o $@ clean: - $(RM) xlxd *.o + $(RM) $(EXECUTABLE) *.o install: mkdir -p /xlxd - cp ./xlxd /xlxd/ + cp $(EXECUTABLE) /xlxd/ cp -i ../config/xlxd.blacklist /xlxd/ cp -i ../config/xlxd.whitelist /xlxd/ cp -i ../config/xlxd.interlink /xlxd/ From be2a5ee2bb2c884da1d086f9b32b384030a598dc Mon Sep 17 00:00:00 2001 From: phl0 Date: Thu, 24 Aug 2017 22:37:04 +0200 Subject: [PATCH 002/244] Fix .gitignore file --- .gitignore | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index d5b9992..dffdf1c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ *.o -xlxd -ambed -ambedtest +src/xlxd +ambed/ambed +ambedtest/ambedtest From a248c0ef39303c594a50700e4edf7fc90d8fbeff Mon Sep 17 00:00:00 2001 From: Luiz Amaral Date: Sun, 27 Aug 2017 18:09:08 -0300 Subject: [PATCH 003/244] Dashboard 2 updates for xlxd 2.x.x --- dashboard2/index.php | 326 ++++++++++++++++------------- dashboard2/pgs/class.node.php | 9 +- dashboard2/pgs/class.parsexml.php | 21 +- dashboard2/pgs/class.reflector.php | 37 +++- dashboard2/pgs/class.station.php | 5 +- dashboard2/pgs/config.inc.php | 5 +- dashboard2/pgs/peers.php | 44 +++- dashboard2/pgs/repeaters.php | 2 +- dashboard2/pgs/users.php | 183 ++++++++++++---- 9 files changed, 411 insertions(+), 221 deletions(-) diff --git a/dashboard2/index.php b/dashboard2/index.php index 9f15fa0..1769c4f 100644 --- a/dashboard2/index.php +++ b/dashboard2/index.php @@ -6,15 +6,23 @@ * The dashboard is based of the Bootstrap dashboard template. */ -if (file_exists("./pgs/functions.php")) { require_once("./pgs/functions.php"); } else { die("functions.php does not exist."); } -if (file_exists("./pgs/config.inc.php")) { require_once("./pgs/config.inc.php"); } else { die("config.inc.php does not exist."); } +if (file_exists("./pgs/functions.php")) { + require_once("./pgs/functions.php"); +} else { + die("functions.php does not exist."); +} +if (file_exists("./pgs/config.inc.php")) { + require_once("./pgs/config.inc.php"); +} else { + die("config.inc.php does not exist."); +} -if (!class_exists('ParseXML')) require_once("./pgs/class.parsexml.php"); -if (!class_exists('Node')) require_once("./pgs/class.node.php"); +if (!class_exists('ParseXML')) require_once("./pgs/class.parsexml.php"); +if (!class_exists('Node')) require_once("./pgs/class.node.php"); if (!class_exists('xReflector')) require_once("./pgs/class.reflector.php"); -if (!class_exists('Station')) require_once("./pgs/class.station.php"); -if (!class_exists('Peer')) require_once("./pgs/class.peer.php"); -if (!class_exists('Interlink')) require_once("./pgs/class.interlink.php"); +if (!class_exists('Station')) require_once("./pgs/class.station.php"); +if (!class_exists('Peer')) require_once("./pgs/class.peer.php"); +if (!class_exists('Interlink')) require_once("./pgs/class.interlink.php"); $Reflector = new xReflector(); $Reflector->SetFlagFile("./pgs/country.csv"); @@ -23,176 +31,198 @@ $Reflector->SetXMLFile($Service['XMLFile']); $Reflector->LoadXML(); -if ($CallingHome['Active']) { - - $CallHomeNow = false; - if (!file_exists($CallingHome['HashFile'])) { - $Hash = CreateCode(16); - $LastSync = 0; - $Ressource = @fopen($CallingHome['HashFile'],"w"); - if ($Ressource) { - @fwrite($Ressource, "'); - @fclose($Ressource); - @exec("chmod 777 ".$CallingHome['HashFile']); - $CallHomeNow = true; - } - } - else { - include($CallingHome['HashFile']); - if ($LastSync < (time() - $CallingHome['PushDelay'])) { - $Ressource = @fopen($CallingHome['HashFile'],"w"); - if ($Ressource) { +if ($CallingHome['Active']) { + + $CallHomeNow = false; + if (!file_exists($CallingHome['HashFile'])) { + $Hash = CreateCode(16); + $LastSync = 0; + $Ressource = @fopen($CallingHome['HashFile'], "w"); + if ($Ressource) { @fwrite($Ressource, "'); + @fwrite($Ressource, "\n" . '$LastSync = 0;'); + @fwrite($Ressource, "\n" . '$Hash = "' . $Hash . '";'); + @fwrite($Ressource, "\n\n" . '?>'); @fclose($Ressource); - } - $CallHomeNow = true; - } - } - - if ($CallHomeNow || isset($_GET['callhome'])) { - $Reflector->SetCallingHome($CallingHome, $Hash); - $Reflector->ReadInterlinkFile(); - $Reflector->PrepareInterlinkXML(); - $Reflector->PrepareReflectorXML(); - $Reflector->CallHome(); - } -} -else { - $Hash = ""; + @exec("chmod 777 " . $CallingHome['HashFile']); + $CallHomeNow = true; + } + } else { + include($CallingHome['HashFile']); + if ($LastSync < (time() - $CallingHome['PushDelay'])) { + $Ressource = @fopen($CallingHome['HashFile'], "w"); + if ($Ressource) { + @fwrite($Ressource, "'); + @fclose($Ressource); + } + $CallHomeNow = true; + } + } + + if ($CallHomeNow || isset($_GET['callhome'])) { + $Reflector->SetCallingHome($CallingHome, $Hash); + $Reflector->ReadInterlinkFile(); + $Reflector->PrepareInterlinkXML(); + $Reflector->PrepareReflectorXML(); + $Reflector->CallHome(); + } +} else { + $Hash = ""; } ?> - - - - - - - - - - - - <?php echo $Reflector->GetReflectorName(); ?> Reflector Dashboard - - - - - - - - - - - - + + + + + + + + + + + <?php echo $Reflector->GetReflectorName(); ?> Reflector Dashboard + + + + + + + + + + + + + function ReloadPage() { document.location.href = "./index.php'; - if (isset($_GET['show'])) { - echo '?show='.$_GET['show']; - } - echo '"; + if (isset($_GET['show'])) { + echo '?show=' . $_GET['show']; + } + echo '"; }'; - if (!isset($_GET['show']) || (($_GET['show'] != 'liveircddb') && ($_GET['show'] != 'reflectors') && ($_GET['show'] != 'interlinks'))) { - echo ' - setTimeout(ReloadPage, '.$PageOptions['PageRefreshDelay'].');'; - } - echo ' + if (!isset($_GET['show']) || (($_GET['show'] != 'liveircddb') && ($_GET['show'] != 'reflectors') && ($_GET['show'] != 'interlinks'))) { + echo ' + setTimeout(ReloadPage, ' . $PageOptions['PageRefreshDelay'] . ');'; + } + echo ' '; - } - if (!isset($_GET['show'])) $_GET['show'] = ""; -?> + } + if (!isset($_GET['show'])) $_GET['show'] = ""; + ?> - - -
-
+
+
- - your private hash in '.$CallingHome['HashFile'].' could not be created, please check your config file and the permissions for the defined folder. + your private hash in ' . $CallingHome['HashFile'] . ' could not be created, please check your config file and the permissions for the defined folder.
'; - } - } - - switch ($_GET['show']) { - case 'users' : require_once("./pgs/users.php"); break; - case 'repeaters' : require_once("./pgs/repeaters.php"); break; - case 'liveircddb' : require_once("./pgs/liveircddb.php"); break; - case 'peers' : require_once("./pgs/peers.php"); break; - case 'reflectors' : require_once("./pgs/reflectors.php"); break; - default : require_once("./pgs/users.php"); - } + } + } + + switch ($_GET['show']) { + case 'users' : + require_once("./pgs/users.php"); + break; + case 'repeaters' : + require_once("./pgs/repeaters.php"); + break; + case 'liveircddb' : + require_once("./pgs/liveircddb.php"); + break; + case 'peers' : + require_once("./pgs/peers.php"); + break; + case 'reflectors' : + require_once("./pgs/reflectors.php"); + break; + default : + require_once("./pgs/users.php"); + } + + ?> -?> - -
-
+
+ -
-
-

-
-
- - - - - - - - +
+
+

+

+
+
+ + + + + + + + diff --git a/dashboard2/pgs/class.node.php b/dashboard2/pgs/class.node.php index 523e0ba..30a19d3 100644 --- a/dashboard2/pgs/class.node.php +++ b/dashboard2/pgs/class.node.php @@ -10,8 +10,9 @@ class Node { private $LastHeardTime; private $Suffix; private $Prefix; + private $RandomID; - public function __construct($Callsign, $IP, $LinkedModule, $Protocol, $ConnectTime, $LastHeardTime) { + public function __construct($Callsign, $IP, $LinkedModule, $Protocol, $ConnectTime, $LastHeardTime, $RandomID) { $this->IP = $IP; @@ -34,8 +35,8 @@ class Node { $this->Prefix = ""; } - - $this->LinkedModule = trim($LinkedModule); + $this->LinkedModule = trim($LinkedModule); + $this->RandomID = $RandomID; } public function GetFullCallsign() { return $this->FullCallsign; } @@ -47,6 +48,8 @@ class Node { public function GetLastHeardTime() { return $this->LastHeardTime; } public function GetSuffix() { return $this->Suffix; } public function GetPrefix() { return $this->Prefix; } + public function GetRandomID() { return $this->RandomID; } + } ?> diff --git a/dashboard2/pgs/class.parsexml.php b/dashboard2/pgs/class.parsexml.php index 700ee87..1d4069e 100644 --- a/dashboard2/pgs/class.parsexml.php +++ b/dashboard2/pgs/class.parsexml.php @@ -1,32 +1,29 @@ ") === false) return false; if (strpos($InputString, "") === false) return false; - + $Element = substr($InputString, strpos($InputString, "<".$ElementName.">")+strlen($ElementName)+2, strpos($InputString, "")-strpos($InputString, "<".$ElementName.">")-strlen($ElementName)-2); return $Element; - + } - + public function GetAllElements($InputString, $ElementName) { $Elements = array(); while (strpos($InputString, $ElementName) !== false) { $Elements[] = $this->GetElement($InputString, $ElementName); - $InputString = substr($InputString, strpos($InputString, "")+strlen($ElementName)+3, strlen($InputString)); + $InputString = substr($InputString, strpos($InputString, "")+strlen($ElementName)+3, strlen($InputString)); } return $Elements; } - - -} - +} -?> \ No newline at end of file +?> diff --git a/dashboard2/pgs/class.reflector.php b/dashboard2/pgs/class.reflector.php index 67fab68..f15f092 100644 --- a/dashboard2/pgs/class.reflector.php +++ b/dashboard2/pgs/class.reflector.php @@ -59,14 +59,14 @@ class xReflector { $tmpNodes = $XML->GetAllElements($AllNodesString, "NODE"); for ($i=0;$iGetElement($tmpNodes[$i], 'Callsign'), $XML->GetElement($tmpNodes[$i], 'IP'), $XML->GetElement($tmpNodes[$i], 'LinkedModule'), $XML->GetElement($tmpNodes[$i], 'Protocol'), $XML->GetElement($tmpNodes[$i], 'ConnectTime'), $XML->GetElement($tmpNodes[$i], 'LastHeardTime')); + $Node = new Node($XML->GetElement($tmpNodes[$i], 'Callsign'), $XML->GetElement($tmpNodes[$i], 'IP'), $XML->GetElement($tmpNodes[$i], 'LinkedModule'), $XML->GetElement($tmpNodes[$i], 'Protocol'), $XML->GetElement($tmpNodes[$i], 'ConnectTime'), $XML->GetElement($tmpNodes[$i], 'LastHeardTime'), CreateCode(16)); $this->AddNode($Node); } $AllStationsString = $XML->GetElement($this->XMLContent, $LinkedUsersName); $tmpStations = $XML->GetAllElements($AllStationsString, "STATION"); for ($i=0;$iGetElement($tmpStations[$i], 'Callsign'), $XML->GetElement($tmpStations[$i], 'Via node'), $XML->GetElement($tmpStations[$i], 'Via peer'), $XML->GetElement($tmpStations[$i], 'LastHeardTime')); + $Station = new Station($XML->GetElement($tmpStations[$i], 'Callsign'), $XML->GetElement($tmpStations[$i], 'Via node'), $XML->GetElement($tmpStations[$i], 'Via peer'), $XML->GetElement($tmpStations[$i], 'LastHeardTime'), $XML->GetElement($tmpStations[$i], 'On module')); $this->AddStation($Station, false); } @@ -213,7 +213,7 @@ class xReflector { } } } - + public function GetSuffixOfRepeater($Repeater, $LinkedModul, $StartWithIndex = 0) { $suffix = ""; $found = false; @@ -230,6 +230,19 @@ class xReflector { return $suffix; } + public function GetCallsignAndSuffixByID($RandomId) { + $suffix = ""; + $callsign = ""; + $i = 0; + while ($i < $this->NodeCount()) { + if ($this->Nodes[$i]->GetRandomID() == $RandomId) { + return $this->Nodes[$i]->GetCallSign().'-'.$this->Nodes[$i]->GetSuffix(); + } + $i++; + } + return 'N/A'; + } + public function StationCount() { return count($this->Stations); } @@ -264,7 +277,6 @@ class xReflector { } $Letters--; } - return array(strtolower($Image), $Name); } @@ -287,6 +299,7 @@ class xReflector { } public function GetModuleOfNode($Node) { + die("FUNCTION DEPRECATED..."); $Node = trim(str_replace(" ", "-", $Node)); $Node = trim(str_replace(" ", "-", $Node)); $Node = trim(str_replace(" ", "-", $Node)); @@ -314,6 +327,16 @@ class xReflector { return $out; } + public function GetNodesInModulesById($Module) { + $out = array(); + for ($i=0;$i<$this->NodeCount();$i++) { + if ($this->Nodes[$i]->GetLinkedModule() == $Module) { + $out[] = $this->Nodes[$i]->GetRandomID(); + } + } + return $out; + } + public function SetCallingHome($CallingHomeVariables, $Hash) { if (!isset($CallingHomeVariables['Active'])) { $CallingHomeVariables['Active'] = false; } @@ -345,11 +368,6 @@ class xReflector { public function PushCallingHome() { $CallingHome = @fopen($this->CallingHomeServerURL."?ReflectorName=".$this->ReflectorName."&ReflectorUptime=".$this->ServiceUptime."&ReflectorHash=".$this->CallingHomeHash."&DashboardURL=".$this->CallingHomeDashboardURL."&Country=".urlencode($this->CallingHomeCountry)."&Comment=".urlencode($this->CallingHomeComment)."&OverrideIP=".$this->CallingHomeOverrideIP, "r"); - - - - - //debug($this->CallingHomeServerURL."?ReflectorName=".$this->ReflectorName."&ReflectorUptime=".$this->ServiceUptime."&ReflectorHash=".$this->CallingHomeHash."&DashboardURL=".$this->CallingHomeDashboardURL."&Country=".urlencode($this->CallingHomeCountry)."&Comment=".urlencode($this->CallingHomeComment)); } public function ReadInterlinkFile() { @@ -402,6 +420,7 @@ class xReflector { '.$this->CallingHomeCountry.' '.$this->CallingHomeComment.' '.$this->CallingHomeOverrideIP.' + '.$this->Version.' '; } diff --git a/dashboard2/pgs/class.station.php b/dashboard2/pgs/class.station.php index 5541e02..533601c 100644 --- a/dashboard2/pgs/class.station.php +++ b/dashboard2/pgs/class.station.php @@ -8,8 +8,9 @@ class Station { private $Suffix; private $CallsignOnly; private $Peer; + private $OnModule; - public function __construct($Callsign, $Via, $Peer, $LastHeardTime) { + public function __construct($Callsign, $Via, $Peer, $LastHeardTime, $OnModule) { $this->Callsign = trim($Callsign); $this->Via = trim($Via); $this->Peer = trim($Peer); @@ -23,6 +24,7 @@ class Station { $tmp = explode(" ", $Callsign); $this->CallsignOnly = $tmp[0]; + $this->OnModule = $OnModule; } public function GetCallsign() { return $this->Callsign; } @@ -31,6 +33,7 @@ class Station { public function GetLastHeardTime() { return $this->LastHeardTime; } public function GetSuffix() { return $this->Suffix; } public function GetCallsignOnly() { return $this->CallsignOnly; } + public function GetModule() { return $this->OnModule; } } diff --git a/dashboard2/pgs/config.inc.php b/dashboard2/pgs/config.inc.php index b282b2d..c841796 100644 --- a/dashboard2/pgs/config.inc.php +++ b/dashboard2/pgs/config.inc.php @@ -16,7 +16,7 @@ $PageOptions = array(); $PageOptions['ContactEmail'] = 'your_email'; // Support E-Mail address -$PageOptions['DashboardVersion'] = '2.3.1'; // Dashboard Version +$PageOptions['DashboardVersion'] = '2.3.7'; // Dashboard Version $PageOptions['PageRefreshActive'] = true; // Activate automatic refresh $PageOptions['PageRefreshDelay'] = '10000'; // Page refresh time in miliseconds @@ -33,6 +33,7 @@ $PageOptions['PeerPage']['LimitTo'] = 99; // Numb $PageOptions['PeerPage']['IPModus'] = 'ShowFullIP'; // See possible options above $PageOptions['PeerPage']['MasqueradeCharacter'] = '*'; // Character used for masquerade +$PageOptions['LastHeardPage']['LimitTo'] = 39; // Number of stations to show $PageOptions['ModuleNames'] = array(); // Module nomination $PageOptions['ModuleNames']['A'] = 'Int.'; @@ -47,7 +48,7 @@ $PageOptions['MetaAuthor'] = 'LX1IQ'; $PageOptions['MetaRevisit'] = 'After 30 Days'; // Meta Tag Values, usefull for Search Engine $PageOptions['MetaRobots'] = 'index,follow'; // Meta Tag Values, usefull for Search Engine - +$PageOptions['UserPage']['ShowFilter'] = true; // Show Filter on Users page $Service['PIDFile'] = '/var/log/xlxd.pid'; $Service['XMLFile'] = '/var/log/xlxd.xml'; diff --git a/dashboard2/pgs/peers.php b/dashboard2/pgs/peers.php index 9271818..1f45c13 100644 --- a/dashboard2/pgs/peers.php +++ b/dashboard2/pgs/peers.php @@ -1,3 +1,23 @@ +GetElement($INPUT, "reflectorlist"); + $Reflectors = $XML->GetAllElements($Reflectorlist, "reflector"); +} + +fclose($Result); +?> + @@ -22,15 +42,28 @@ for ($i=0;$i<$Reflector->PeerCount();$i++) { echo ' - - + '; + + $Name = $Reflector->Peers[$i]->GetCallSign(); + $URL = ''; + + for ($j=1;$jGetElement($Reflectors[$j], "name")) { + $URL = $XML->GetElement($Reflectors[$j], "dashboardurl"); + } + } + if ($Result && (trim($URL) != "")) { + echo ''; + } else { + echo ''; + } + echo ' '; if ($PageOptions['PeerPage']['IPModus'] != 'HideIP') { - echo ' - '; } - echo ' - '; + echo ''; if ($i == $PageOptions['PeerPage']['LimitTo']) { $i = $Reflector->PeerCount()+1; } } diff --git a/dashboard2/pgs/repeaters.php b/dashboard2/pgs/repeaters.php index 3e1f643..a7562b9 100644 --- a/dashboard2/pgs/repeaters.php +++ b/dashboard2/pgs/repeaters.php @@ -49,7 +49,7 @@ for ($i=0;$i<$Reflector->NodeCount();$i++) { case 'C' : echo '2m'; break; case 'D' : echo 'Dongle'; break; case 'G' : echo 'Internet-Gateway'; break; - default : + default : echo ''; } } echo ' diff --git a/dashboard2/pgs/users.php b/dashboard2/pgs/users.php index bff6a75..bc8f92f 100644 --- a/dashboard2/pgs/users.php +++ b/dashboard2/pgs/users.php @@ -1,6 +1,86 @@ + +
#
'.($i+1).''.$Reflector->Peers[$i]->GetCallSign().''.($i+1).''.$Name.''.$Name.''.date("d.m.Y H:i", $Reflector->Peers[$i]->GetLastHeardTime()).' '.FormatSeconds(time()-$Reflector->Peers[$i]->GetConnectTime()).' s '.$Reflector->Peers[$i]->GetProtocol().' '.$Reflector->Peers[$i]->GetLinkedModule().''; + echo ''; $Bytes = explode(".", $Reflector->Peers[$i]->GetIP()); if ($Bytes !== false && count($Bytes) == 4) { switch ($PageOptions['PeerPage']['IPModus']) { @@ -42,8 +75,7 @@ for ($i=0;$i<$Reflector->PeerCount();$i++) { } echo '
+ + +'; +} +?> @@ -15,36 +95,59 @@ $Reflector->LoadFlags(); for ($i=0;$i<$Reflector->StationCount();$i++) { - echo ' - - - - - - - - - + $ShowThisStation = true; + if ($PageOptions['UserPage']['ShowFilter']) { + $CS = true; + if ($_SESSION['FilterCallSign'] != null) { + if (!fnmatch($_SESSION['FilterCallSign'], $Reflector->Stations[$i]->GetCallSign(), FNM_CASEFOLD)) { + $CS = false; + } + } + $MO = true; + if ($_SESSION['FilterModule'] != null) { + if (trim(strtolower($_SESSION['FilterModule'])) != strtolower($Reflector->Stations[$i]->GetModule())) { + $MO = false; + } + } + + $ShowThisStation = ($CS && $MO); + } + + if ($ShowThisStation) { + + echo ' + + + + + + + + + '; - if ($i == 39) { $i = $Reflector->StationCount()+1; } + } + if ($i == $PageOptions['LastHeardPage']['LimitTo']) { + $i = $Reflector->StationCount() + 1; + } } ?> @@ -56,11 +159,16 @@ for ($i=0;$i<$Reflector->StationCount();$i++) { GetModules(); +sort($Modules, SORT_STRING); echo ''; for ($i=0;$i'.$PageOptions['ModuleNames'][$Modules[$i]].' '.$Modules[$i].''; + echo ''; } else { echo ' @@ -71,20 +179,17 @@ for ($i=0;$i'; +$GlobalPositions = array(); + for ($i=0;$iGetCallSignsInModules($Modules[$i]); + $Users = $Reflector->GetNodesInModulesByID($Modules[$i]); echo '
+ + + '; + if (($_SESSION['FilterModule'] != null) || ($_SESSION['FilterCallSign'] != null)) { + echo ' + '; + } + echo ' + +
+
+ + + +
+
Disable filters +
+ + + +
+
+
# Flag
'; - echo ($i==0 ? '' : $i+1); - - - echo ''; - - list ($Flag, $Name) = $Reflector->GetFlag($Reflector->Stations[$i]->GetCallSign()); - if (file_exists("./img/flags/".$Flag.".png")) { - echo ''.$Name.''.$Name.''; - } - echo ''.$Reflector->Stations[$i]->GetCallsignOnly().''.$Reflector->Stations[$i]->GetSuffix().''.$Reflector->Stations[$i]->GetVia(); - if ($Reflector->Stations[$i]->GetPeer() != $Reflector->GetReflectorName()) { - echo ' / '.$Reflector->Stations[$i]->GetPeer(); - } - echo ''.@date("d.m.Y H:i", $Reflector->Stations[$i]->GetLastHeardTime()).''; - if ($Reflector->Stations[$i]->GetPeer() == $Reflector->GetReflectorName()) { - echo trim($Reflector->GetModuleOfNode($Reflector->Stations[$i]->GetVia())); - } - echo '
'; + if ($i == 0 && $Reflector->Stations[$i]->GetLastHeardTime() > (time() - 60)) { + echo ''; + } else { + echo($i + 1); + } + + + echo ''; + + list ($Flag, $Name) = $Reflector->GetFlag($Reflector->Stations[$i]->GetCallSign()); + if (file_exists("./img/flags/" . $Flag . ".png")) { + echo '' . $Name . '' . $Name . ''; + } + echo '' . $Reflector->Stations[$i]->GetCallsignOnly() . '' . $Reflector->Stations[$i]->GetSuffix() . '' . $Reflector->Stations[$i]->GetVia(); + if ($Reflector->Stations[$i]->GetPeer() != $Reflector->GetReflectorName()) { + echo ' / ' . $Reflector->Stations[$i]->GetPeer(); + } + echo '' . @date("d.m.Y H:i", $Reflector->Stations[$i]->GetLastHeardTime()) . '' . $Reflector->Stations[$i]->GetModule() . '
'.$PageOptions['ModuleNames'][$Modules[$i]]; + if (trim($PageOptions['ModuleNames'][$Modules[$i]]) != "") { + echo '
'; + } + echo $Modules[$i].'
'; + $UserCheckedArray = array(); for ($j=0;$jGetSuffixOfRepeater($Users[$j], $Modules[$i], max($CurrentPositions)+1); - } - else { - $Displayname = $Users[$j].'-'.$Reflector->GetSuffixOfRepeater($Users[$j], $Modules[$i]); - } + $Displayname = $Reflector->GetCallsignAndSuffixByID($Users[$j]); echo ' @@ -99,4 +204,4 @@ echo ''; ?>
'.$Displayname.'
- + \ No newline at end of file From c909cfbea8ee64bc67d23b365ac72975a5b64e99 Mon Sep 17 00:00:00 2001 From: Yngwie Chou Date: Tue, 29 Aug 2017 07:16:01 +0800 Subject: [PATCH 004/244] Make DMRmmdvm and DMRplus node module Id configurable --- src/cdmrmmdvmprotocol.cpp | 14 +++++++------- src/cdmrmmdvmprotocol.h | 3 +++ src/cdmrplusprotocol.cpp | 6 +++--- src/cdmrplusprotocol.h | 3 +++ 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/cdmrmmdvmprotocol.cpp b/src/cdmrmmdvmprotocol.cpp index 3e8868e..0e67704 100644 --- a/src/cdmrmmdvmprotocol.cpp +++ b/src/cdmrmmdvmprotocol.cpp @@ -511,7 +511,7 @@ bool CDmrmmdvmProtocol::IsValidKeepAlivePacket(const CBuffer &Buffer, CCallsign { uint32 uiRptrId = MAKEDWORD(MAKEWORD(Buffer.data()[10],Buffer.data()[9]),MAKEWORD(Buffer.data()[8],Buffer.data()[7])); callsign->SetDmrid(uiRptrId, true); - callsign->SetModule('B'); + callsign->SetModule(MMDVM_MODULE_ID); valid = callsign->IsValid(); } return valid; @@ -526,7 +526,7 @@ bool CDmrmmdvmProtocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *c { uint32 uiRptrId = MAKEDWORD(MAKEWORD(Buffer.data()[7],Buffer.data()[6]),MAKEWORD(Buffer.data()[5],Buffer.data()[4])); callsign->SetDmrid(uiRptrId, true); - callsign->SetModule('B'); + callsign->SetModule(MMDVM_MODULE_ID); valid = callsign->IsValid(); if ( !valid) { @@ -545,7 +545,7 @@ bool CDmrmmdvmProtocol::IsValidAuthenticationPacket(const CBuffer &Buffer, CCall { uint32 uiRptrId = MAKEDWORD(MAKEWORD(Buffer.data()[7],Buffer.data()[6]),MAKEWORD(Buffer.data()[5],Buffer.data()[4])); callsign->SetDmrid(uiRptrId, true); - callsign->SetModule('B'); + callsign->SetModule(MMDVM_MODULE_ID); valid = callsign->IsValid(); } return valid; @@ -560,7 +560,7 @@ bool CDmrmmdvmProtocol::IsValidDisconnectPacket(const CBuffer &Buffer, CCallsign { uint32 uiRptrId = MAKEDWORD(MAKEWORD(Buffer.data()[7],Buffer.data()[6]),MAKEWORD(Buffer.data()[5],Buffer.data()[4])); callsign->SetDmrid(uiRptrId, true); - callsign->SetModule('B'); + callsign->SetModule(MMDVM_MODULE_ID); valid = callsign->IsValid(); } return valid; @@ -575,7 +575,7 @@ bool CDmrmmdvmProtocol::IsValidConfigPacket(const CBuffer &Buffer, CCallsign *ca { uint32 uiRptrId = MAKEDWORD(MAKEWORD(Buffer.data()[7],Buffer.data()[6]),MAKEWORD(Buffer.data()[5],Buffer.data()[4])); callsign->SetDmrid(uiRptrId, true); - callsign->SetModule('B'); + callsign->SetModule(MMDVM_MODULE_ID); valid = callsign->IsValid(); } return valid; @@ -590,7 +590,7 @@ bool CDmrmmdvmProtocol::IsValidOptionPacket(const CBuffer &Buffer, CCallsign *ca { uint32 uiRptrId = MAKEDWORD(MAKEWORD(Buffer.data()[7],Buffer.data()[6]),MAKEWORD(Buffer.data()[5],Buffer.data()[4])); callsign->SetDmrid(uiRptrId, true); - callsign->SetModule('B'); + callsign->SetModule(MMDVM_MODULE_ID); valid = callsign->IsValid(); } return valid; @@ -673,7 +673,7 @@ bool CDmrmmdvmProtocol::IsValidDvHeaderPacket(const CBuffer &Buffer, CDvHeaderPa // build DVHeader CCallsign csMY = CCallsign("", uiSrcId); CCallsign rpt1 = CCallsign("", uiRptrId); - rpt1.SetModule('B'); + rpt1.SetModule(MMDVM_MODULE_ID); CCallsign rpt2 = m_ReflectorCallsign; rpt2.SetModule(DmrDstIdToModule(uiDstId)); diff --git a/src/cdmrmmdvmprotocol.h b/src/cdmrmmdvmprotocol.h index 2f221c5..25dfbd3 100644 --- a/src/cdmrmmdvmprotocol.h +++ b/src/cdmrmmdvmprotocol.h @@ -43,6 +43,9 @@ #define MMDVM_SLOTTYPE_HEADER 1 #define MMDVM_SLOTTYPE_TERMINATOR 2 +// DMRMMDVM Module ID +#define MMDVM_MODULE_ID "B" + //////////////////////////////////////////////////////////////////////////////////////// // class diff --git a/src/cdmrplusprotocol.cpp b/src/cdmrplusprotocol.cpp index fb4f270..76bfd43 100644 --- a/src/cdmrplusprotocol.cpp +++ b/src/cdmrplusprotocol.cpp @@ -414,7 +414,7 @@ bool CDmrplusProtocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *ca sz[8] = 0; uint32 dmrid = atoi(sz); callsign->SetDmrid(dmrid, true); - callsign->SetModule('B'); + callsign->SetModule(DMRPLUS_MODULE_ID); ::memcpy(sz, &Buffer.data()[8], 4); sz[4] = 0; *reflectormodule = DmrDstIdToModule(atoi(sz)); @@ -437,7 +437,7 @@ bool CDmrplusProtocol::IsValidDisconnectPacket(const CBuffer &Buffer, CCallsign sz[8] = 0; uint32 dmrid = atoi(sz); callsign->SetDmrid(dmrid, true); - callsign->SetModule('B'); + callsign->SetModule(DMRPLUS_MODULE_ID); *reflectormodule = Buffer.data()[11] - '0' + 'A'; valid = (callsign->IsValid() && std::isupper(*reflectormodule)); } @@ -467,7 +467,7 @@ bool CDmrplusProtocol::IsValidDvHeaderPacket(const CIp &Ip, const CBuffer &Buffe // build DVHeader CCallsign csMY = CCallsign("", uiSrcId); CCallsign rpt1 = CCallsign("", uiSrcId); - rpt1.SetModule('B'); + rpt1.SetModule(DMRPLUS_MODULE_ID); CCallsign rpt2 = m_ReflectorCallsign; rpt2.SetModule(DmrDstIdToModule(uiDstId)); uint32 uiStreamId = IpToStreamId(Ip); diff --git a/src/cdmrplusprotocol.h b/src/cdmrplusprotocol.h index 430f741..1c8707d 100644 --- a/src/cdmrplusprotocol.h +++ b/src/cdmrplusprotocol.h @@ -34,6 +34,9 @@ //////////////////////////////////////////////////////////////////////////////////////// // define +// DMR Plus Module ID +#define DMRPLUS_MODULE_ID "B" + //////////////////////////////////////////////////////////////////////////////////////// // class From 8cdd27e80956491b93b42369488dff9931d54e1a Mon Sep 17 00:00:00 2001 From: Yngwie Chou Date: Tue, 29 Aug 2017 07:25:56 +0800 Subject: [PATCH 005/244] Make DMRmmdvm and DMRplus repeater module ID configurable --- src/cdmrmmdvmprotocol.h | 2 +- src/cdmrplusprotocol.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdmrmmdvmprotocol.h b/src/cdmrmmdvmprotocol.h index 25dfbd3..52b5e64 100644 --- a/src/cdmrmmdvmprotocol.h +++ b/src/cdmrmmdvmprotocol.h @@ -44,7 +44,7 @@ #define MMDVM_SLOTTYPE_TERMINATOR 2 // DMRMMDVM Module ID -#define MMDVM_MODULE_ID "B" +#define MMDVM_MODULE_ID 'B' //////////////////////////////////////////////////////////////////////////////////////// // class diff --git a/src/cdmrplusprotocol.h b/src/cdmrplusprotocol.h index 1c8707d..e142a72 100644 --- a/src/cdmrplusprotocol.h +++ b/src/cdmrplusprotocol.h @@ -35,7 +35,7 @@ // define // DMR Plus Module ID -#define DMRPLUS_MODULE_ID "B" +#define DMRPLUS_MODULE_ID 'B' //////////////////////////////////////////////////////////////////////////////////////// // class From b46e5f5a4f9c5841317487101adc4006c6beacd0 Mon Sep 17 00:00:00 2001 From: yngwiechou Date: Wed, 30 Aug 2017 10:51:28 +0800 Subject: [PATCH 006/244] Improve log messages for DMRMmdvm and DMRPlus protocol connect/auth/config packets with source IP address --- src/cdmrmmdvmprotocol.cpp | 28 +++++++++++++++++++--------- src/cdmrmmdvmprotocol.h | 6 +++--- src/cdmrplusprotocol.cpp | 6 +++--- src/cdmrplusprotocol.h | 2 +- 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/cdmrmmdvmprotocol.cpp b/src/cdmrmmdvmprotocol.cpp index 3e8868e..2f6c84c 100644 --- a/src/cdmrmmdvmprotocol.cpp +++ b/src/cdmrmmdvmprotocol.cpp @@ -130,8 +130,8 @@ void CDmrmmdvmProtocol::Task(void) //std::cout << "DMRmmdvm DV last frame" << std::endl; OnDvLastFramePacketIn(LastFrame, &Ip); - } - else if ( IsValidConnectPacket(Buffer, &Callsign) ) + } + else if ( IsValidConnectPacket(Buffer, &Callsign, Ip) ) { std::cout << "DMRmmdvm connect packet from " << Callsign << " at " << Ip << std::endl; @@ -150,7 +150,7 @@ void CDmrmmdvmProtocol::Task(void) } } - else if ( IsValidAuthenticationPacket(Buffer, &Callsign) ) + else if ( IsValidAuthenticationPacket(Buffer, &Callsign, Ip) ) { std::cout << "DMRmmdvm authentication packet from " << Callsign << " at " << Ip << std::endl; @@ -203,7 +203,7 @@ void CDmrmmdvmProtocol::Task(void) } g_Reflector.ReleaseClients(); } - else if ( IsValidConfigPacket(Buffer, &Callsign) ) + else if ( IsValidConfigPacket(Buffer, &Callsign, Ip) ) { std::cout << "DMRmmdvm configuration packet from " << Callsign << " at " << Ip << std::endl; @@ -517,7 +517,7 @@ bool CDmrmmdvmProtocol::IsValidKeepAlivePacket(const CBuffer &Buffer, CCallsign return valid; } -bool CDmrmmdvmProtocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *callsign) +bool CDmrmmdvmProtocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *callsign, const CIp &Ip) { uint8 tag[] = { 'R','P','T','L' }; @@ -530,13 +530,13 @@ bool CDmrmmdvmProtocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *c valid = callsign->IsValid(); if ( !valid) { - std::cout << "DMRmmdvm connect packet from unrecognized id " << (int)callsign->GetDmrid() << std::endl; + std::cout << "DMRmmdvm connect packet from IP address " << Ip << " / unrecognized id " << (int)callsign->GetDmrid() << std::endl; } } return valid; } -bool CDmrmmdvmProtocol::IsValidAuthenticationPacket(const CBuffer &Buffer, CCallsign *callsign) +bool CDmrmmdvmProtocol::IsValidAuthenticationPacket(const CBuffer &Buffer, CCallsign *callsign, const CIp &Ip) { uint8 tag[] = { 'R','P','T','K' }; @@ -547,6 +547,11 @@ bool CDmrmmdvmProtocol::IsValidAuthenticationPacket(const CBuffer &Buffer, CCall callsign->SetDmrid(uiRptrId, true); callsign->SetModule('B'); valid = callsign->IsValid(); + if ( !valid) + { + std::cout << "DMRmmdvm authnetication packet from IP address " << Ip << " / unrecognized id " << (int)callsign->GetDmrid() << std::endl; + } + } return valid; } @@ -566,7 +571,7 @@ bool CDmrmmdvmProtocol::IsValidDisconnectPacket(const CBuffer &Buffer, CCallsign return valid; } -bool CDmrmmdvmProtocol::IsValidConfigPacket(const CBuffer &Buffer, CCallsign *callsign) +bool CDmrmmdvmProtocol::IsValidConfigPacket(const CBuffer &Buffer, CCallsign *callsign, const CIp &Ip) { uint8 tag[] = { 'R','P','T','C' }; @@ -577,6 +582,11 @@ bool CDmrmmdvmProtocol::IsValidConfigPacket(const CBuffer &Buffer, CCallsign *ca callsign->SetDmrid(uiRptrId, true); callsign->SetModule('B'); valid = callsign->IsValid(); + if ( !valid) + { + std::cout << "DMRmmdvm config packet from IP address " << Ip << " / unrecognized id " << (int)callsign->GetDmrid() << std::endl; + } + } return valid; } @@ -671,7 +681,7 @@ bool CDmrmmdvmProtocol::IsValidDvHeaderPacket(const CBuffer &Buffer, CDvHeaderPa } // build DVHeader - CCallsign csMY = CCallsign("", uiSrcId); + CCallsign csMY = CCallsign("", uiSrcId); CCallsign rpt1 = CCallsign("", uiRptrId); rpt1.SetModule('B'); CCallsign rpt2 = m_ReflectorCallsign; diff --git a/src/cdmrmmdvmprotocol.h b/src/cdmrmmdvmprotocol.h index 2f221c5..c7acd98 100644 --- a/src/cdmrmmdvmprotocol.h +++ b/src/cdmrmmdvmprotocol.h @@ -86,10 +86,10 @@ protected: bool OnDvHeaderPacketIn(CDvHeaderPacket *, const CIp &, uint8, uint8); // packet decoding helpers - bool IsValidConnectPacket(const CBuffer &, CCallsign *); - bool IsValidAuthenticationPacket(const CBuffer &, CCallsign *); + bool IsValidConnectPacket(const CBuffer &, CCallsign *, const CIp &); + bool IsValidAuthenticationPacket(const CBuffer &, CCallsign *, const CIp &); bool IsValidDisconnectPacket(const CBuffer &, CCallsign *); - bool IsValidConfigPacket(const CBuffer &, CCallsign *); + bool IsValidConfigPacket(const CBuffer &, CCallsign *, const CIp &); bool IsValidOptionPacket(const CBuffer &, CCallsign *); bool IsValidKeepAlivePacket(const CBuffer &, CCallsign *); bool IsValidRssiPacket(const CBuffer &, CCallsign *, int *); diff --git a/src/cdmrplusprotocol.cpp b/src/cdmrplusprotocol.cpp index fb4f270..4106471 100644 --- a/src/cdmrplusprotocol.cpp +++ b/src/cdmrplusprotocol.cpp @@ -125,7 +125,7 @@ void CDmrplusProtocol::Task(void) delete Header; } } - else if ( IsValidConnectPacket(Buffer, &Callsign, &ToLinkModule) ) + else if ( IsValidConnectPacket(Buffer, &Callsign, &ToLinkModule, Ip) ) { //std::cout << "DMRplus keepalive/connect packet for module " << ToLinkModule << " from " << Callsign << " at " << Ip << std::endl; @@ -404,7 +404,7 @@ void CDmrplusProtocol::HandleKeepalives(void) //////////////////////////////////////////////////////////////////////////////////////// // packet decoding helpers -bool CDmrplusProtocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *callsign, char *reflectormodule) +bool CDmrplusProtocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *callsign, char *reflectormodule, const CIp &Ip) { bool valid = false; if ( Buffer.size() == 31 ) @@ -421,7 +421,7 @@ bool CDmrplusProtocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *ca valid = (callsign->IsValid() && (std::isupper(*reflectormodule) || (*reflectormodule == ' ')) ); if ( !valid) { - std::cout << "DMRplus connect packet from unrecognized id " << (int)callsign->GetDmrid() << std::endl; + std::cout << "DMRplus connect packet from IP address " << Ip << " / unrecognized id " << (int)callsign->GetDmrid() << std::endl; } } return valid; diff --git a/src/cdmrplusprotocol.h b/src/cdmrplusprotocol.h index 430f741..4b9bb52 100644 --- a/src/cdmrplusprotocol.h +++ b/src/cdmrplusprotocol.h @@ -78,7 +78,7 @@ protected: bool OnDvHeaderPacketIn(CDvHeaderPacket *, const CIp &); // packet decoding helpers - bool IsValidConnectPacket(const CBuffer &, CCallsign *, char *); + bool IsValidConnectPacket(const CBuffer &, CCallsign *, char *, const CIp &); bool IsValidDisconnectPacket(const CBuffer &, CCallsign *, char *); bool IsValidDvHeaderPacket(const CIp &, const CBuffer &, CDvHeaderPacket **); bool IsValidDvFramePacket(const CIp &, const CBuffer &, CDvFramePacket **); From 5ba8e770dc01c40520f1c46b6c273674dab2075b Mon Sep 17 00:00:00 2001 From: phl0 Date: Thu, 31 Aug 2017 09:30:30 +0200 Subject: [PATCH 007/244] Redo changes of PR #29 previously overwritten with commit 83278d13 --- src/cclients.cpp | 4 ++-- src/cdextraclient.h | 2 +- src/cdplusclient.h | 2 +- src/cpeers.cpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cclients.cpp b/src/cclients.cpp index 3818865..837802a 100644 --- a/src/cclients.cpp +++ b/src/cclients.cpp @@ -85,7 +85,7 @@ void CClients::AddClient(CClient *client) // and append m_Clients.push_back(client); std::cout << "New client " << client->GetCallsign() << " at " << client->GetIp() - << " added with protocol " << client->GetProtocol(); + << " added with protocol " << client->GetProtocolName(); if ( client->GetReflectorModule() != ' ' ) { std::cout << " on module " << client->GetReflectorModule(); @@ -110,7 +110,7 @@ void CClients::RemoveClient(CClient *client) { // remove it std::cout << "Client " << m_Clients[i]->GetCallsign() << " at " << m_Clients[i]->GetIp() - << " removed with protocol " << client->GetProtocol(); + << " removed with protocol " << client->GetProtocolName(); if ( client->GetReflectorModule() != ' ' ) { std::cout << " on module " << client->GetReflectorModule(); diff --git a/src/cdextraclient.h b/src/cdextraclient.h index 132ff37..453c5f4 100644 --- a/src/cdextraclient.h +++ b/src/cdextraclient.h @@ -48,7 +48,7 @@ public: // identity int GetProtocol(void) const { return PROTOCOL_DEXTRA; } int GetProtocolRevision(void) const { return m_ProtRev; } - const char *GetProtocolName(void) const { return "Dextra"; } + const char *GetProtocolName(void) const { return "DExtra"; } int GetCodec(void) const { return CODEC_AMBEPLUS; } bool IsNode(void) const { return true; } diff --git a/src/cdplusclient.h b/src/cdplusclient.h index f4b165f..d615e12 100644 --- a/src/cdplusclient.h +++ b/src/cdplusclient.h @@ -47,7 +47,7 @@ public: // identity int GetProtocol(void) const { return PROTOCOL_DPLUS; } - const char *GetProtocolName(void) const { return "Dplus"; } + const char *GetProtocolName(void) const { return "DPlus"; } int GetCodec(void) const { return CODEC_AMBEPLUS; } bool IsNode(void) const { return true; } bool IsDextraDongle(void) const { return m_bDextraDongle; } diff --git a/src/cpeers.cpp b/src/cpeers.cpp index 3a27282..35c5547 100644 --- a/src/cpeers.cpp +++ b/src/cpeers.cpp @@ -85,7 +85,7 @@ void CPeers::AddPeer(CPeer *peer) // append peer to reflector peer list m_Peers.push_back(peer); std::cout << "New peer " << peer->GetCallsign() << " at " << peer->GetIp() - << " added with protocol " << peer->GetProtocol() << std::endl; + << " added with protocol " << peer->GetProtocolName() << std::endl; // and append all peer's client to reflector client list // it is double lock safe to lock Clients list after Peers list CClients *clients = g_Reflector.GetClients(); From 5010b5fef899b114401ea6c96cfd97b30c0e1a90 Mon Sep 17 00:00:00 2001 From: phl0 Date: Thu, 12 Oct 2017 15:02:27 +0200 Subject: [PATCH 008/244] Add filter function to repeaters page --- dashboard/pgs/repeaters.php | 229 ++++++++++++++++++++++++++++-------- 1 file changed, 180 insertions(+), 49 deletions(-) diff --git a/dashboard/pgs/repeaters.php b/dashboard/pgs/repeaters.php index 124d59d..bdf8d6f 100755 --- a/dashboard/pgs/repeaters.php +++ b/dashboard/pgs/repeaters.php @@ -1,4 +1,110 @@ - + + +
+ +'; +} + + +?> @@ -22,61 +128,86 @@ $odd = ""; $Reflector->LoadFlags(); for ($i=0;$i<$Reflector->NodeCount();$i++) { - - if ($odd == "#FFFFFF") { $odd = "#F1FAFA"; } else { $odd = "#FFFFFF"; } - - echo ' - - - - - - - - - '; - if ($PageOptions['RepeatersPage']['IPModus'] != 'HideIP') { + + if ($ShowThisStation) { + if ($odd == "#FFFFFF") { $odd = "#F1FAFA"; } else { $odd = "#FFFFFF"; } + echo ' - + + + + '; + else { + switch ($Reflector->Nodes[$i]->GetSuffix()) { + case 'A' : echo '23cm'; break; + case 'B' : echo '70cm'; break; + case 'C' : echo '2m'; break; + case 'D' : echo 'Dongle'; break; + case 'G' : echo 'Internet-Gateway'; break; + default : echo ''; + } + } + echo ' + + + + '; + if ($PageOptions['RepeatersPage']['IPModus'] != 'HideIP') { + echo ' + '; + } + echo ' + '; } - echo ' - '; if ($i == $PageOptions['RepeatersPage']['LimitTo']) { $i = $Reflector->NodeCount()+1; } } From b870d0b91a92c63869428736d57580081ec9bb74 Mon Sep 17 00:00:00 2001 From: LX3JL Date: Sun, 5 Nov 2017 18:18:48 +0100 Subject: [PATCH 009/244] amber 1.2.0 * added USB-3006 support * added DF2ET-3003 support * reworked CVocodec::Init() for more flexible device pairing * rewored CVocodecInterface instantiation for easier new device addition --- ambed/cftdidevicedescr.cpp | 494 ++++++++++++++++++++++++++++++++++++ ambed/cftdidevicedescr.h | 29 ++- ambed/cusb3000interface.cpp | 2 +- ambed/cusb3003interface.cpp | 48 +++- ambed/cusb3xxxinterface.cpp | 2 +- ambed/cvocodecs.cpp | 312 ++++------------------- ambed/cvocodecs.h | 8 +- ambed/main.cpp | 8 +- ambed/main.h | 4 +- ambed/readme | 33 ++- 10 files changed, 638 insertions(+), 302 deletions(-) diff --git a/ambed/cftdidevicedescr.cpp b/ambed/cftdidevicedescr.cpp index 71cbfbf..88411a2 100644 --- a/ambed/cftdidevicedescr.cpp +++ b/ambed/cftdidevicedescr.cpp @@ -24,6 +24,10 @@ #include "main.h" #include +#include "cusb3000interface.h" +#include "cusb3003interface.h" +#include "cusb3003hrinterface.h" +#include "cusb3003df2etinterface.h" #include "cftdidevicedescr.h" @@ -56,3 +60,493 @@ CFtdiDeviceDescr::CFtdiDeviceDescr(const CFtdiDeviceDescr &descr) ::memcpy(m_szDescription, descr.m_szDescription, sizeof(m_szDescription)); ::memcpy(m_szSerial, descr.m_szSerial, sizeof(m_szSerial)); } + +//////////////////////////////////////////////////////////////////////////////////////// +// interface factory + +int CFtdiDeviceDescr::CreateInterface(CFtdiDeviceDescr *descr, std::vector*channels) +{ + int iNbChs = 0; + + // single channel devices cannot be created alone + // three channels devices + if ( (::strcmp(descr->GetDescription(), "USB-3003") == 0) || // DVSI's USB-3003 + (::strcmp(descr->GetDescription(), "DF2ET-3003") == 0) || // DF2ET's USB-3003 opensource device + (::strcmp(descr->GetDescription(), "ThumbDV-3") == 0) ) // ThumbDV-3 + { + iNbChs = CreateUsb3003(descr, channels); + } + // six channels devices + else if ( (::strcmp(descr->GetDescription(), "USB-3006 A") == 0) ) // LX3JL's USB-3006 opensource device + { + iNbChs = CreateUsb3006(descr, channels); + } + // twelves channels devices + else if ( (::strcmp(descr->GetDescription(), "USB-3012 A") == 0) ) // DVSI's USB-3012 + { + iNbChs = CreateUsb3012(descr, channels); + } + // done + return iNbChs; +} + +int CFtdiDeviceDescr::CreateInterfacePair(CFtdiDeviceDescr *descr1, CFtdiDeviceDescr *descr2, std::vector*channels) +{ + int iNbChs = 0; + + // create interface objects + if ( (descr1->GetNbChannels() == 1) && (descr1->GetNbChannels() == 1) ) + { + // create 3000-3000 pair + CUsb3000Interface *Usb3000A = InstantiateUsb3000(descr1); + CUsb3000Interface *Usb3000B = InstantiateUsb3000(descr2); + iNbChs = CreatePair(Usb3000A, Usb3000B, channels); + } + else if ( (descr1->GetNbChannels() == 3) && (descr1->GetNbChannels() == 1) ) + { + // create 3003-3000 pair + CUsb3003Interface *Usb3003 = InstantiateUsb3003(descr1); + CUsb3000Interface *Usb3000 = InstantiateUsb3000(descr2); + iNbChs = CreatePair(Usb3003, Usb3000, channels); + } + else if ( (descr1->GetNbChannels() == 1) && (descr1->GetNbChannels() == 3) ) + { + // create 3000-3003 pair + CUsb3000Interface *Usb3000 = InstantiateUsb3000(descr1); + CUsb3003Interface *Usb3003 = InstantiateUsb3003(descr2); + iNbChs = CreatePair(Usb3003, Usb3000, channels); + } + else if ( (descr1->GetNbChannels() == 3) && (descr1->GetNbChannels() == 3) ) + { + // create 3003-3003 pair + CUsb3003Interface *Usb3003A = InstantiateUsb3003(descr1); + CUsb3003Interface *Usb3003B = InstantiateUsb3003(descr2); + iNbChs = CreatePair(Usb3003A, Usb3003B, channels); + } + + // done + return iNbChs; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// get + +int CFtdiDeviceDescr::GetNbChannels(void) const +{ + int iNbChs = 0; + + // single channel devices + if ( (::strcmp(m_szDescription, "USB-3000") == 0) || // DVSI's USB-3000 + (::strcmp(m_szDescription, "ThumbDV") == 0) ) // ThumbDV + { + iNbChs = 1; + } + // three channels devices + else if ( (::strcmp(m_szDescription, "USB-3003") == 0) || // DVSI's USB-3003 + (::strcmp(m_szDescription, "DF2ET-3003") == 0) || // DF2ET's USB-3003 opensource device + (::strcmp(m_szDescription, "ThumbDV-3") == 0) ) // ThumbDV-3 + { + iNbChs = 3; + } + // six channels devices + else if ( (::strcmp(m_szDescription, "USB-3006 A") == 0) ) // LX3JL's USB-3006 opensource device + { + iNbChs = 6; + } + // twelves channels devices + else if ( (::strcmp(m_szDescription, "USB-3012 A") == 0) ) // DVSI's USB-3012 + { + iNbChs = 12; + } + + // done + return iNbChs; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// DVSI's USB-3012 factory helper +// +// This device uses 3 AMBE3003 connected on a single FTDI 4 channels +// USB to serial interface. The reset mechanism is based +// on DTR and SetBreak. Baudrate is 921600 +// + +int CFtdiDeviceDescr::CreateUsb3012(CFtdiDeviceDescr *descr, std::vector*channels) +{ + int nStreams = 0; + + // create the interfaces for the four 3003 chips + CUsb3003HRInterface *Usb3003A = + new CUsb3003HRInterface(descr->GetVid(), descr->GetPid(), "USB-3012_A", descr->GetSerialNumber()); + CUsb3003HRInterface *Usb3003B = + new CUsb3003HRInterface(descr->GetVid(), descr->GetPid(), "USB-3012_B", descr->GetSerialNumber()); + CUsb3003HRInterface *Usb3003C = + new CUsb3003HRInterface(descr->GetVid(), descr->GetPid(), "USB-3012_C", descr->GetSerialNumber()); + CUsb3003HRInterface *Usb3003D = + new CUsb3003HRInterface(descr->GetVid(), descr->GetPid(), "USB-3012_D", descr->GetSerialNumber()); + + // init the interfaces + if ( Usb3003A->Init(CODEC_AMBEPLUS) && Usb3003B->Init(CODEC_AMBE2PLUS) && + Usb3003C->Init(CODEC_AMBEPLUS) && Usb3003D->Init(CODEC_AMBE2PLUS) ) + { + CVocodecChannel *Channel; + // create all channels + { + // ch1 + Channel = new CVocodecChannel(Usb3003A, 0, Usb3003A, 1, CODECGAIN_AMBEPLUS); + channels->push_back(Channel); + Usb3003A->AddChannel(Channel); + // ch2 + Channel = new CVocodecChannel(Usb3003A, 1, Usb3003A, 0, CODECGAIN_AMBE2PLUS); + channels->push_back(Channel); + Usb3003A->AddChannel(Channel); + // ch3 + Channel = new CVocodecChannel(Usb3003B, 0, Usb3003B, 1, CODECGAIN_AMBEPLUS); + channels->push_back(Channel); + Usb3003B->AddChannel(Channel); + // ch4 + Channel = new CVocodecChannel(Usb3003B, 1, Usb3003B, 0, CODECGAIN_AMBE2PLUS); + channels->push_back(Channel); + Usb3003B->AddChannel(Channel); + // ch5 + Channel = new CVocodecChannel(Usb3003C, 0, Usb3003C, 1, CODECGAIN_AMBEPLUS); + channels->push_back(Channel); + Usb3003C->AddChannel(Channel); + // ch6 + Channel = new CVocodecChannel(Usb3003C, 1, Usb3003C, 0, CODECGAIN_AMBE2PLUS); + channels->push_back(Channel); + Usb3003C->AddChannel(Channel); + // ch7 + Channel = new CVocodecChannel(Usb3003D, 0, Usb3003D, 1, CODECGAIN_AMBEPLUS); + channels->push_back(Channel); + Usb3003D->AddChannel(Channel); + // ch8 + Channel = new CVocodecChannel(Usb3003D, 1, Usb3003D, 0, CODECGAIN_AMBE2PLUS); + channels->push_back(Channel); + Usb3003D->AddChannel(Channel); + // ch9 + Channel = new CVocodecChannel(Usb3003A, 2, Usb3003B, 2, CODECGAIN_AMBEPLUS); + channels->push_back(Channel); + Usb3003A->AddChannel(Channel); + Usb3003B->AddChannel(Channel); + // ch10 + Channel = new CVocodecChannel(Usb3003B, 2, Usb3003A, 2, CODECGAIN_AMBE2PLUS); + channels->push_back(Channel); + Usb3003A->AddChannel(Channel); + Usb3003B->AddChannel(Channel); + // ch11 + Channel = new CVocodecChannel(Usb3003C, 2, Usb3003D, 2, CODECGAIN_AMBEPLUS); + channels->push_back(Channel); + Usb3003C->AddChannel(Channel); + Usb3003D->AddChannel(Channel); + // ch12 + Channel = new CVocodecChannel(Usb3003D, 2, Usb3003C, 2, CODECGAIN_AMBE2PLUS); + channels->push_back(Channel); + Usb3003C->AddChannel(Channel); + Usb3003D->AddChannel(Channel); + //done + nStreams = 12; + } + } + else + { + // cleanup + delete Usb3003A; + delete Usb3003B; + delete Usb3003C; + delete Usb3003D; + } + + // done + return nStreams; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// LX3JL's USB-3006 factory helper +// +// This device uses 2 AMBE3003 connected on a single FTDI 2 channels +// USB to serial interface. The reset mechanism is based +// on DTR and software reset. Baudrate is 921600 +// +int CFtdiDeviceDescr::CreateUsb3006(CFtdiDeviceDescr *descr, std::vector*channels) +{ + int nStreams = 0; + + // create the interfaces for the four 3003 chips + CUsb3003Interface *Usb3003A = + new CUsb3003Interface(descr->GetVid(), descr->GetPid(), "USB-3006_A", descr->GetSerialNumber()); + CUsb3003Interface *Usb3003B = + new CUsb3003Interface(descr->GetVid(), descr->GetPid(), "USB-3006_B", descr->GetSerialNumber()); + + // init the interfaces + if ( Usb3003A->Init(CODEC_AMBEPLUS) && Usb3003B->Init(CODEC_AMBE2PLUS) ) + { + CVocodecChannel *Channel; + // create all channels + { + // ch1 + Channel = new CVocodecChannel(Usb3003A, 0, Usb3003A, 1, CODECGAIN_AMBEPLUS); + channels->push_back(Channel); + Usb3003A->AddChannel(Channel); + // ch2 + Channel = new CVocodecChannel(Usb3003A, 1, Usb3003A, 0, CODECGAIN_AMBE2PLUS); + channels->push_back(Channel); + Usb3003A->AddChannel(Channel); + // ch3 + Channel = new CVocodecChannel(Usb3003B, 0, Usb3003B, 1, CODECGAIN_AMBEPLUS); + channels->push_back(Channel); + Usb3003B->AddChannel(Channel); + // ch4 + Channel = new CVocodecChannel(Usb3003B, 1, Usb3003B, 0, CODECGAIN_AMBE2PLUS); + channels->push_back(Channel); + Usb3003B->AddChannel(Channel); + // ch5 + Channel = new CVocodecChannel(Usb3003A, 2, Usb3003B, 2, CODECGAIN_AMBEPLUS); + channels->push_back(Channel); + Usb3003A->AddChannel(Channel); + Usb3003B->AddChannel(Channel); + // ch6 + Channel = new CVocodecChannel(Usb3003B, 2, Usb3003A, 2, CODECGAIN_AMBE2PLUS); + channels->push_back(Channel); + Usb3003A->AddChannel(Channel); + Usb3003B->AddChannel(Channel); + //done + nStreams = 6; + } + } + else + { + // cleanup + delete Usb3003A; + delete Usb3003B; + } + + // done + return nStreams; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// USB-3003 factory helpers +// DVSI +// DF2ET +// ThumbDV +// +// These devices uses a AMBE3003 connected on a single FTDI +// USB to serial interface. The reset mechanism is based +// on DTR and SetBreak, or software. Baudrate is 921600 +// +int CFtdiDeviceDescr::CreateUsb3003(CFtdiDeviceDescr *descr, std::vector*channels) +{ + int nStreams = 0; + + // create the interfaces for the 3003 chip + CUsb3003Interface *Usb3003 = InstantiateUsb3003(descr); + + // init the interface + if ( (Usb3003 != NULL) && Usb3003->Init(CODEC_NONE) ) + { + CVocodecChannel *Channel; + // create all channels + { + // ch1 + Channel = new CVocodecChannel(Usb3003, 0, Usb3003, 1, CODECGAIN_AMBEPLUS); + channels->push_back(Channel); + Usb3003->AddChannel(Channel); + // ch2 + Channel = new CVocodecChannel(Usb3003, 1, Usb3003, 0, CODECGAIN_AMBE2PLUS); + channels->push_back(Channel); + Usb3003->AddChannel(Channel); + // done + nStreams = 2; + } + } + else + { + // cleanup + delete Usb3003; + } + + // done + return nStreams; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// 1 ch + 1 ch pair creation + +int CFtdiDeviceDescr::CreatePair(CUsb3000Interface *Usb3000A, CUsb3000Interface *Usb3000B, std::vector*channels) +{ + int nStreams = 0; + + // init the interfaces + if ( Usb3000A->Init(CODEC_AMBEPLUS) && Usb3000B->Init(CODEC_AMBE2PLUS) ) + { + CVocodecChannel *Channel; + // create all channels + { + // ch1 + Channel = new CVocodecChannel(Usb3000A, 0, Usb3000B, 0, CODECGAIN_AMBEPLUS); + channels->push_back(Channel); + Usb3000A->AddChannel(Channel); + Usb3000B->AddChannel(Channel); + // ch2 + Channel = new CVocodecChannel(Usb3000A, 0, Usb3000B, 0, CODECGAIN_AMBE2PLUS); + channels->push_back(Channel); + Usb3000A->AddChannel(Channel); + Usb3000B->AddChannel(Channel); + // done + nStreams = 2; + } + } + else + { + // cleanup + delete Usb3000A; + delete Usb3000B; + } + + // done + return nStreams; + +} + +//////////////////////////////////////////////////////////////////////////////////////// +// 3 ch + 3 ch pair creation + +int CFtdiDeviceDescr::CreatePair(CUsb3003Interface *Usb3003A, CUsb3003Interface *Usb3003B, std::vector*channels) +{ + int nStreams = 0; + + // init the interfaces + if ( Usb3003A->Init(CODEC_AMBEPLUS) && Usb3003B->Init(CODEC_AMBE2PLUS) ) + { + CVocodecChannel *Channel; + // create all channels + { + // ch1 + Channel = new CVocodecChannel(Usb3003A, 0, Usb3003A, 1, CODECGAIN_AMBEPLUS); + channels->push_back(Channel); + Usb3003A->AddChannel(Channel); + // ch2 + Channel = new CVocodecChannel(Usb3003A, 1, Usb3003A, 0, CODECGAIN_AMBE2PLUS); + channels->push_back(Channel); + Usb3003A->AddChannel(Channel); + // ch3 + Channel = new CVocodecChannel(Usb3003B, 0, Usb3003B, 1, CODECGAIN_AMBEPLUS); + channels->push_back(Channel); + Usb3003B->AddChannel(Channel); + // ch4 + Channel = new CVocodecChannel(Usb3003B, 1, Usb3003B, 0, CODECGAIN_AMBE2PLUS); + channels->push_back(Channel); + Usb3003B->AddChannel(Channel); + // ch5 + Channel = new CVocodecChannel(Usb3003A, 2, Usb3003B, 2, CODECGAIN_AMBEPLUS); + channels->push_back(Channel); + Usb3003A->AddChannel(Channel); + Usb3003B->AddChannel(Channel); + // ch6 + Channel = new CVocodecChannel(Usb3003B, 2, Usb3003A, 2, CODECGAIN_AMBE2PLUS); + channels->push_back(Channel); + Usb3003A->AddChannel(Channel); + Usb3003B->AddChannel(Channel); + // done + nStreams = 6; + } + } + else + { + // cleanup + delete Usb3003A; + delete Usb3003B; + } + + // done + return nStreams; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// 3 ch + 1 ch pair creation + +int CFtdiDeviceDescr::CreatePair(CUsb3003Interface *Usb3003A, CUsb3000Interface *Usb3000B, std::vector*channels) +{ + int nStreams = 0; + + // init the interfaces + if ( Usb3003A->Init(CODEC_AMBEPLUS) && Usb3000B->Init(CODEC_AMBE2PLUS) ) + { + CVocodecChannel *Channel; + // create all channels + { + // ch1 + Channel = new CVocodecChannel(Usb3003A, 0, Usb3003A, 1, CODECGAIN_AMBEPLUS); + channels->push_back(Channel); + Usb3003A->AddChannel(Channel); + // ch2 + Channel = new CVocodecChannel(Usb3003A, 1, Usb3003A, 0, CODECGAIN_AMBE2PLUS); + channels->push_back(Channel); + Usb3003A->AddChannel(Channel); + // ch5 + Channel = new CVocodecChannel(Usb3003A, 2, Usb3000B, 0, CODECGAIN_AMBEPLUS); + channels->push_back(Channel); + Usb3003A->AddChannel(Channel); + Usb3000B->AddChannel(Channel); + // done + nStreams = 6; + } + } + else + { + // cleanup + delete Usb3003A; + delete Usb3000B; + } + + // done + return nStreams; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// interface instantiation helpers + +CUsb3003Interface *CFtdiDeviceDescr::InstantiateUsb3003(CFtdiDeviceDescr *descr) +{ + CUsb3003Interface *Usb3003 = NULL; + + // intstantiate the proper version of USB-3003 + if ( (::strcmp(descr->GetDescription(), "USB-3003") == 0) ) // DVSI's USB-3003 + { + // hardware reset, 921600 bps + Usb3003 = new CUsb3003HRInterface + (descr->GetVid(), descr->GetPid(), descr->GetDescription(), descr->GetSerialNumber()); + } + else if ( (::strcmp(descr->GetDescription(), "DF2ET-3003") == 0) ) // DF2ET's USB-3003 opensource device + { + // specific hardware reset, 921600 bps + Usb3003 = new CUsb3003DF2ETInterface + (descr->GetVid(), descr->GetPid(), descr->GetDescription(), descr->GetSerialNumber()); + } + else if ( (::strcmp(descr->GetDescription(), "ThumbDV-3") == 0) ) // ThumbDV-3 + { + // software reset, 921600 bps + Usb3003 = new CUsb3003Interface + (descr->GetVid(), descr->GetPid(), descr->GetDescription(), descr->GetSerialNumber()); + } + // done + return Usb3003; +} + +CUsb3000Interface *CFtdiDeviceDescr::InstantiateUsb3000(CFtdiDeviceDescr *descr) +{ + CUsb3000Interface *Usb3000 = NULL; + + // intstantiate the proper version of USB-3000 + if ( (::strcmp(descr->GetDescription(), "USB-3000") == 0) || // DVSI's USB-3000 + (::strcmp(descr->GetDescription(), "ThumbDV") == 0) ) // ThumbDV + { + Usb3000 = new CUsb3000Interface + (descr->GetVid(), descr->GetPid(), descr->GetDescription(), descr->GetSerialNumber()); + } + // done + return Usb3000; +} diff --git a/ambed/cftdidevicedescr.h b/ambed/cftdidevicedescr.h index acbc1bd..c01b485 100644 --- a/ambed/cftdidevicedescr.h +++ b/ambed/cftdidevicedescr.h @@ -27,6 +27,7 @@ #include #include "ftd2xx.h" +#include "cvocodecchannel.h" //////////////////////////////////////////////////////////////////////////////////////// // define @@ -38,6 +39,9 @@ //////////////////////////////////////////////////////////////////////////////////////// // class +class CUsb3000Interface; +class CUsb3003Interface; + class CFtdiDeviceDescr { public: @@ -48,12 +52,14 @@ public: // destructor virtual ~CFtdiDeviceDescr() {} - + + // interface factory + static int CreateInterface(CFtdiDeviceDescr *, std::vector*); + static int CreateInterfacePair(CFtdiDeviceDescr *, CFtdiDeviceDescr *, std::vector*); + // get - bool IsUsed(void) const {return m_bUsed; } - bool IsUsb3000(void) const { return (::strcmp(m_szDescription, "USB-3000") == 0); } - bool IsUsb3003(void) const { return (::strcmp(m_szDescription, "USB-3003") == 0); } - bool IsUsb3012(void) const { return (::strcmp(m_szDescription, "USB-3012 A") == 0); } + bool IsUsed(void) const { return m_bUsed; } + int GetNbChannels(void) const; uint32 GetVid(void) const { return m_uiVid; } uint32 GetPid(void) const { return m_uiPid; } const char *GetDescription(void) const { return m_szDescription; } @@ -61,7 +67,18 @@ public: // set void SetUsed(bool used) { m_bUsed = used; } - + +protected: + // factory helper + static int CreateUsb3012(CFtdiDeviceDescr *, std::vector*); + static int CreateUsb3006(CFtdiDeviceDescr *, std::vector*); + static int CreateUsb3003(CFtdiDeviceDescr *, std::vector*); + static int CreatePair(CUsb3003Interface *, CUsb3003Interface *, std::vector*); + static int CreatePair(CUsb3003Interface *, CUsb3000Interface *, std::vector*); + static int CreatePair(CUsb3000Interface *, CUsb3000Interface *, std::vector*); + static CUsb3003Interface *InstantiateUsb3003(CFtdiDeviceDescr *); + static CUsb3000Interface *InstantiateUsb3000(CFtdiDeviceDescr *); + protected: // status bool m_bUsed; diff --git a/ambed/cusb3000interface.cpp b/ambed/cusb3000interface.cpp index 3cd0754..b340cba 100644 --- a/ambed/cusb3000interface.cpp +++ b/ambed/cusb3000interface.cpp @@ -254,7 +254,7 @@ bool CUsb3000Interface::ResetDevice(void) { FTDI_write_packet(m_FtdiHandle, zeropacket, sizeof(zeropacket)); } - + // write soft-reset packet if ( FTDI_write_packet(m_FtdiHandle, txpacket, sizeof(txpacket)) ) diff --git a/ambed/cusb3003interface.cpp b/ambed/cusb3003interface.cpp index e10361d..309680c 100644 --- a/ambed/cusb3003interface.cpp +++ b/ambed/cusb3003interface.cpp @@ -249,22 +249,46 @@ bool CUsb3003Interface::OpenDevice(void) bool CUsb3003Interface::ResetDevice(void) { bool ok = false; - FT_STATUS ftStatus; int len; char rxpacket[100]; + char zeropacket[10] = + { + 0,0,0,0,0,0,0,0,0,0 + }; + char txpacket[7] = + { + PKT_HEADER, + 0, + 3, + 0, + PKT_RESET, + PKT_PARITYBYTE, + 3 ^ PKT_RESET ^ PKT_PARITYBYTE + }; - //if the device is a USB-3003, it supports reset via UART break signal - //printf("reset via uart break...\n"); - ftStatus = FT_SetBreakOn( m_FtdiHandle ); - CTimePoint::TaskSleepFor(10); - ftStatus = FT_SetBreakOff( m_FtdiHandle ); - //CTimePoint::TaskSleepFor(10); - - len = FTDI_read_packet( m_FtdiHandle, rxpacket, sizeof(rxpacket) ); - ok = ((len == 7) && (rxpacket[4] == PKT_READY)); - if ( !ok ) + + //the chip might be in a state where it is waiting to receive bytes from a prior incomplete packet. + //first send 350 zeros in case, the chip's receive state is still waiting for characters + //if we send more than needed, the exta characters will just get discarded since they do not match the header byte + //after that we send PKT_RESET to reset the device + //As long as the AMBE3000 is able to receive via uart, this method will succeed in resetting it. + + for ( int i = 0; i < 35 ; i++ ) { - std::cout << "USB-3003 hard reset failed" << std::endl; + FTDI_write_packet(m_FtdiHandle, zeropacket, sizeof(zeropacket)); + } + + + // write soft-reset packet + if ( FTDI_write_packet(m_FtdiHandle, txpacket, sizeof(txpacket)) ) + { + // read reply + len = FTDI_read_packet( m_FtdiHandle, rxpacket, sizeof(rxpacket) ); + ok = ((len == 7) && (rxpacket[4] == PKT_READY)); + if ( !ok ) + { + std::cout << "USB-3003 soft reset failed" << std::endl; + } } // done diff --git a/ambed/cusb3xxxinterface.cpp b/ambed/cusb3xxxinterface.cpp index b59e3fe..7bf3fdd 100644 --- a/ambed/cusb3xxxinterface.cpp +++ b/ambed/cusb3xxxinterface.cpp @@ -65,7 +65,7 @@ bool CUsb3xxxInterface::Init(void) std::cout << "Opening " << m_szDeviceName << ":" << m_szDeviceSerial << " device" << std::endl; if ( ok &= OpenDevice() ) { - // reset + // reset //std::cout << "Reseting " << m_szDeviceName << "device" << std::endl; if ( ok &= ResetDevice() ) { diff --git a/ambed/cvocodecs.cpp b/ambed/cvocodecs.cpp index ca6ec71..56f4bb4 100644 --- a/ambed/cvocodecs.cpp +++ b/ambed/cvocodecs.cpp @@ -24,8 +24,6 @@ #include "main.h" #include -#include "cusb3000interface.h" -#include "cusb3003interface.h" #include "cvocodecs.h" //////////////////////////////////////////////////////////////////////////////////////// @@ -89,73 +87,80 @@ bool CVocodecs::Init(void) DiscoverFtdiDevices(); // and create interfaces for the discovered devices + // first handle all even number of channels devices for ( int i = 0; i < m_FtdiDeviceDescrs.size(); i++ ) { - // create relevant interface - if ( m_FtdiDeviceDescrs[i]->IsUsb3012() ) + CFtdiDeviceDescr *descr = m_FtdiDeviceDescrs[i]; + if ( !descr->IsUsed() && IsEven(descr->GetNbChannels()) ) { - iNbCh += InitUsb3012(*m_FtdiDeviceDescrs[i]); - m_FtdiDeviceDescrs[i]->SetUsed(true); + // create the object + iNbCh += CFtdiDeviceDescr::CreateInterface(descr, &m_Channels); + // and flag as used + descr->SetUsed(true); } - else if ( m_FtdiDeviceDescrs[i]->IsUsb3003() && !m_FtdiDeviceDescrs[i]->IsUsed() ) + } + // next handle all single channel devices. + // they must be handeled in pair, or in pair with another + // even number of channels device. + for ( int i = 0; i < m_FtdiDeviceDescrs.size(); i++ ) + { + CFtdiDeviceDescr *descr1 = m_FtdiDeviceDescrs[i]; + CFtdiDeviceDescr *descr2 = NULL; + if ( !descr1->IsUsed() && (descr1->GetNbChannels() == 1) ) { - // another unsed USB-3003 avaliable for a pair ? + // any other single channel device to pair with ? bool found = false; int j = i+1; while ( !found && (j < m_FtdiDeviceDescrs.size()) ) { - if ( m_FtdiDeviceDescrs[j]->IsUsb3003() && !m_FtdiDeviceDescrs[i]->IsUsed() ) - { - found = true; - } - else - { - j++; - } + descr2 = m_FtdiDeviceDescrs[j]; + found = (!descr2->IsUsed() && (descr2->GetNbChannels() == 1)); } - - // pair ? + // found one ? if ( found ) { - // yes! - iNbCh += InitUsb3003Pair(*m_FtdiDeviceDescrs[i], *m_FtdiDeviceDescrs[j]); - m_FtdiDeviceDescrs[i]->SetUsed(true); - m_FtdiDeviceDescrs[j]->SetUsed(true); - } - else - { - // just single - iNbCh += InitUsb3003(*m_FtdiDeviceDescrs[i]); - m_FtdiDeviceDescrs[i]->SetUsed(true); + // yes, create and pairboth interfaces + iNbCh += CFtdiDeviceDescr::CreateInterfacePair(descr1, descr2, &m_Channels); + // and flag as used + descr1->SetUsed(true); + descr2->SetUsed(true); } } - else if ( m_FtdiDeviceDescrs[i]->IsUsb3000() && !m_FtdiDeviceDescrs[i]->IsUsed() ) + } + // now we should have only remaining the 3 chennels device(s) + // and possibly an unique single channel device + for ( int i = 0; i < m_FtdiDeviceDescrs.size(); i++ ) + { + CFtdiDeviceDescr *descr1 = m_FtdiDeviceDescrs[i]; + CFtdiDeviceDescr *descr2 = NULL; + if ( !descr1->IsUsed() && (descr1->GetNbChannels() == 3) ) { - // another unsed USB-3000 avaliable for a pair ? + // any other odd channel device to pair with ? + // any other single channel device to pair with ? bool found = false; int j = i+1; while ( !found && (j < m_FtdiDeviceDescrs.size()) ) { - if ( m_FtdiDeviceDescrs[j]->IsUsb3000() && !m_FtdiDeviceDescrs[i]->IsUsed() ) - { - found = true; - } - else - { - j++; - } + descr2 = m_FtdiDeviceDescrs[j]; + found = (!descr2->IsUsed() && IsOdd(descr2->GetNbChannels())); } - - // pair ? + // found one ? if ( found ) { - // yes! - iNbCh += InitUsb3000Pair(*m_FtdiDeviceDescrs[i], *m_FtdiDeviceDescrs[j]); - m_FtdiDeviceDescrs[i]->SetUsed(true); - m_FtdiDeviceDescrs[j]->SetUsed(true); + // yes, create and pairboth interfaces + iNbCh += CFtdiDeviceDescr::CreateInterfacePair(descr1, descr2, &m_Channels); + // and flag as used + descr1->SetUsed(true); + descr2->SetUsed(true); + } + else + { + // no, just create a standalone 3003 interface + iNbCh += CFtdiDeviceDescr::CreateInterface(descr1, &m_Channels); + // and flag as used + descr1->SetUsed(true); } - // otherwise anonther unused USB-3003 for a pair ? - } + } } if ( ok ) @@ -222,221 +227,6 @@ bool CVocodecs::DiscoverFtdiDevices(void) return ok; } -int CVocodecs::InitUsb3012(const CFtdiDeviceDescr &descr) -{ - int nStreams = 0; - - // create the interfaces for the four 3003 chips - CUsb3003Interface *Usb3003A = new CUsb3003Interface(descr.GetVid(), descr.GetPid(), "USB-3012_A", descr.GetSerialNumber()); - CUsb3003Interface *Usb3003B = new CUsb3003Interface(descr.GetVid(), descr.GetPid(), "USB-3012_B", descr.GetSerialNumber()); - CUsb3003Interface *Usb3003C = new CUsb3003Interface(descr.GetVid(), descr.GetPid(), "USB-3012_C", descr.GetSerialNumber()); - CUsb3003Interface *Usb3003D = new CUsb3003Interface(descr.GetVid(), descr.GetPid(), "USB-3012_D", descr.GetSerialNumber()); - - // init the interfaces - if ( Usb3003A->Init(CODEC_AMBEPLUS) && Usb3003B->Init(CODEC_AMBE2PLUS) && - Usb3003C->Init(CODEC_AMBEPLUS) && Usb3003D->Init(CODEC_AMBE2PLUS) ) - { - CVocodecChannel *Channel; - // create all channels - { - // ch1 - Channel = new CVocodecChannel(Usb3003A, 0, Usb3003A, 1, CODECGAIN_AMBEPLUS); - m_Channels.push_back(Channel); - Usb3003A->AddChannel(Channel); - // ch2 - Channel = new CVocodecChannel(Usb3003A, 1, Usb3003A, 0, CODECGAIN_AMBE2PLUS); - m_Channels.push_back(Channel); - Usb3003A->AddChannel(Channel); - // ch3 - Channel = new CVocodecChannel(Usb3003B, 0, Usb3003B, 1, CODECGAIN_AMBEPLUS); - m_Channels.push_back(Channel); - Usb3003B->AddChannel(Channel); - // ch4 - Channel = new CVocodecChannel(Usb3003B, 1, Usb3003B, 0, CODECGAIN_AMBE2PLUS); - m_Channels.push_back(Channel); - Usb3003B->AddChannel(Channel); - // ch5 - Channel = new CVocodecChannel(Usb3003C, 0, Usb3003C, 1, CODECGAIN_AMBEPLUS); - m_Channels.push_back(Channel); - Usb3003C->AddChannel(Channel); - // ch6 - Channel = new CVocodecChannel(Usb3003C, 1, Usb3003C, 0, CODECGAIN_AMBE2PLUS); - m_Channels.push_back(Channel); - Usb3003C->AddChannel(Channel); - // ch7 - Channel = new CVocodecChannel(Usb3003D, 0, Usb3003D, 1, CODECGAIN_AMBEPLUS); - m_Channels.push_back(Channel); - Usb3003D->AddChannel(Channel); - // ch8 - Channel = new CVocodecChannel(Usb3003D, 1, Usb3003D, 0, CODECGAIN_AMBE2PLUS); - m_Channels.push_back(Channel); - Usb3003D->AddChannel(Channel); - // ch9 - Channel = new CVocodecChannel(Usb3003A, 2, Usb3003B, 2, CODECGAIN_AMBEPLUS); - m_Channels.push_back(Channel); - Usb3003A->AddChannel(Channel); - Usb3003B->AddChannel(Channel); - // ch10 - Channel = new CVocodecChannel(Usb3003B, 2, Usb3003A, 2, CODECGAIN_AMBE2PLUS); - m_Channels.push_back(Channel); - Usb3003A->AddChannel(Channel); - Usb3003B->AddChannel(Channel); - // ch11 - Channel = new CVocodecChannel(Usb3003C, 2, Usb3003D, 2, CODECGAIN_AMBEPLUS); - m_Channels.push_back(Channel); - Usb3003C->AddChannel(Channel); - Usb3003D->AddChannel(Channel); - // ch12 - Channel = new CVocodecChannel(Usb3003D, 2, Usb3003C, 2, CODECGAIN_AMBE2PLUS); - m_Channels.push_back(Channel); - Usb3003C->AddChannel(Channel); - Usb3003D->AddChannel(Channel); - //done - nStreams = 12; - } - } - else - { - // cleanup - delete Usb3003A; - delete Usb3003B; - delete Usb3003C; - delete Usb3003D; - } - - // done - return nStreams; -} - -int CVocodecs::InitUsb3003(const CFtdiDeviceDescr &descr) -{ - int nStreams = 0; - - // create the interfaces for the 3003 chip - CUsb3003Interface *Usb3003 = new CUsb3003Interface(descr.GetVid(), descr.GetPid(), "USB-3003", descr.GetSerialNumber()); - - // init the interface - if ( Usb3003->Init(CODEC_NONE) ) - { - CVocodecChannel *Channel; - // create all channels - { - // ch1 - Channel = new CVocodecChannel(Usb3003, 0, Usb3003, 1, CODECGAIN_AMBEPLUS); - m_Channels.push_back(Channel); - Usb3003->AddChannel(Channel); - // ch2 - Channel = new CVocodecChannel(Usb3003, 1, Usb3003, 0, CODECGAIN_AMBE2PLUS); - m_Channels.push_back(Channel); - Usb3003->AddChannel(Channel); - // done - nStreams = 2; - } - } - else - { - // cleanup - delete Usb3003; - } - - // done - return nStreams; -} - -int CVocodecs::InitUsb3003Pair(const CFtdiDeviceDescr &descr1, const CFtdiDeviceDescr &descr2) -{ - int nStreams = 0; - - // create the interfaces for the two 3003 chips - CUsb3003Interface *Usb3003A = new CUsb3003Interface(descr1.GetVid(), descr1.GetPid(), "USB-3003", descr1.GetSerialNumber()); - CUsb3003Interface *Usb3003B = new CUsb3003Interface(descr2.GetVid(), descr2.GetPid(), "USB-3003", descr2.GetSerialNumber()); - - // init the interfaces - if ( Usb3003A->Init(CODEC_AMBEPLUS) && Usb3003B->Init(CODEC_AMBE2PLUS) ) - { - CVocodecChannel *Channel; - // create all channels - { - // ch1 - Channel = new CVocodecChannel(Usb3003A, 0, Usb3003A, 1, CODECGAIN_AMBEPLUS); - m_Channels.push_back(Channel); - Usb3003A->AddChannel(Channel); - // ch2 - Channel = new CVocodecChannel(Usb3003A, 1, Usb3003A, 0, CODECGAIN_AMBE2PLUS); - m_Channels.push_back(Channel); - Usb3003A->AddChannel(Channel); - // ch3 - Channel = new CVocodecChannel(Usb3003B, 0, Usb3003B, 1, CODECGAIN_AMBEPLUS); - m_Channels.push_back(Channel); - Usb3003B->AddChannel(Channel); - // ch4 - Channel = new CVocodecChannel(Usb3003B, 1, Usb3003B, 0, CODECGAIN_AMBE2PLUS); - m_Channels.push_back(Channel); - Usb3003B->AddChannel(Channel); - // ch5 - Channel = new CVocodecChannel(Usb3003A, 2, Usb3003B, 2, CODECGAIN_AMBEPLUS); - m_Channels.push_back(Channel); - Usb3003A->AddChannel(Channel); - Usb3003B->AddChannel(Channel); - // ch6 - Channel = new CVocodecChannel(Usb3003B, 2, Usb3003A, 2, CODECGAIN_AMBE2PLUS); - m_Channels.push_back(Channel); - Usb3003A->AddChannel(Channel); - Usb3003B->AddChannel(Channel); - // done - nStreams = 6; - } - } - else - { - // cleanup - delete Usb3003A; - delete Usb3003B; - } - - // done - return nStreams; -} - -int CVocodecs::InitUsb3000Pair(const CFtdiDeviceDescr &descr1, const CFtdiDeviceDescr &descr2) -{ - int nStreams = 0; - - // create the interfaces for the two 3000 chips - CUsb3000Interface *Usb3000A = new CUsb3000Interface(descr1.GetVid(), descr1.GetPid(), "USB-3000", descr1.GetSerialNumber()); - CUsb3000Interface *Usb3000B = new CUsb3000Interface(descr2.GetVid(), descr2.GetPid(), "USB-3000", descr2.GetSerialNumber()); - - // init the interfaces - if ( Usb3000A->Init(CODEC_AMBEPLUS) && Usb3000B->Init(CODEC_AMBE2PLUS) ) - { - CVocodecChannel *Channel; - // create all channels - { - // ch1 - Channel = new CVocodecChannel(Usb3000A, 0, Usb3000B, 0, CODECGAIN_AMBEPLUS); - m_Channels.push_back(Channel); - Usb3000A->AddChannel(Channel); - Usb3000B->AddChannel(Channel); - // ch2 - Channel = new CVocodecChannel(Usb3000B, 0, Usb3000A, 0, CODECGAIN_AMBE2PLUS); - m_Channels.push_back(Channel); - Usb3000A->AddChannel(Channel); - Usb3000B->AddChannel(Channel); - // done - nStreams = 2; - } - } - else - { - // cleanup - delete Usb3000A; - delete Usb3000B; - } - - // done - return nStreams; -} - //////////////////////////////////////////////////////////////////////////////////////// // manage channels diff --git a/ambed/cvocodecs.h b/ambed/cvocodecs.h index 6688ebb..62da1f6 100644 --- a/ambed/cvocodecs.h +++ b/ambed/cvocodecs.h @@ -56,10 +56,10 @@ public: protected: // initialisation helpers bool DiscoverFtdiDevices(void); - int InitUsb3012(const CFtdiDeviceDescr &); - int InitUsb3003(const CFtdiDeviceDescr &); - int InitUsb3003Pair(const CFtdiDeviceDescr &, const CFtdiDeviceDescr &); - int InitUsb3000Pair(const CFtdiDeviceDescr &, const CFtdiDeviceDescr &); + + // helpers + bool IsEven(int i) const { return ((i % 2) == 0); } + bool IsOdd(int i) const { return !IsEven(i); } protected: // array of interfaces diff --git a/ambed/main.cpp b/ambed/main.cpp index 6892471..078174f 100644 --- a/ambed/main.cpp +++ b/ambed/main.cpp @@ -114,8 +114,12 @@ int main(int argc, const char * argv[]) } #else // wait any key - for (;;); - //std::cin.get(); + for (;;) + { + // sleep 60 seconds + CTimePoint::TaskSleepFor(60000); + //std::cin.get(); + } #endif // and wait for end diff --git a/ambed/main.h b/ambed/main.h index 4c14244..36a04ed 100644 --- a/ambed/main.h +++ b/ambed/main.h @@ -48,8 +48,8 @@ // version ----------------------------------------------------- #define VERSION_MAJOR 1 -#define VERSION_MINOR 1 -#define VERSION_REVISION 1 +#define VERSION_MINOR 2 +#define VERSION_REVISION 0 // global ------------------------------------------------------ diff --git a/ambed/readme b/ambed/readme index 3c6713a..ddfc4c9 100644 --- a/ambed/readme +++ b/ambed/readme @@ -3,7 +3,7 @@ // ambed // // Created by Jean-Luc Deltombe (LX3JL) on 09/07/2017. -// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2017 Jean-Luc Deltombe (LX3JL). All rights reserved. // // ---------------------------------------------------------------------------- // This file is part of ambed. @@ -25,23 +25,30 @@ Hardware compatibility. ====================== -This version of ambed is compatible with DVSI's USB-3000, USB-3003, USB-3012 device -and ThumbDV +This version of ambed is compatible with: +- DF2ET's AMBE3003USB opensource device (https://github.com/phl0/AMBE3003USB) +- LX3JL's USB-3006 opensource device (https://github.com/lx3jl/usb-3006) +- DVSI's USB-3000 device +- DVSI's USB-3003 device +- DVSI's USB-3012 device +- NWDR's ThumbDV device +- NWDR's ThumbDV-3 device Available transcoding channels per device: device DMR->DSTAR DSTAR->DMR Nb Of concurrent channels ---------------------------------------------------------------------- -USB-3000(pair) 1 1 2 -USB-3003 1 1 1 -USB-3003(pair) 3 3 not tested -USB-3012 6 6 8 - -Multiple USB-3xxx can be used at the same time. -You need to use USB-3000 by pairs. -ThumbDV is recognized as USB-3000. -ThumbDV must be programed with FT-Prog (Provided by FTDI) by modifying device description as "USB-3000". - +3000(pair) 1 1 2 +3003 1 1 1 +3003(pair) 3 3 not tested +3003-3000(pair) 2 2 not tested +3006 3 3 4 +3012 6 6 8 + +Multiple devices can be used at the same time. +You need to use 3000 by pairs or paired with a 3003 +Do not to use USB hubs as they have proven making +system behaviour unreliable. Instructions: ============= From c3657078c4b90d2c5bf634b8ab828c3a07373df5 Mon Sep 17 00:00:00 2001 From: LX3JL Date: Sun, 5 Nov 2017 20:39:15 +0100 Subject: [PATCH 010/244] Add files via upload --- ambed/cusb3003df2etinterface.cpp | 164 +++++++++++++++++++++++++++++++ ambed/cusb3003df2etinterface.h | 57 +++++++++++ ambed/cusb3003hrinterface.cpp | 66 +++++++++++++ ambed/cusb3003hrinterface.h | 55 +++++++++++ 4 files changed, 342 insertions(+) create mode 100644 ambed/cusb3003df2etinterface.cpp create mode 100644 ambed/cusb3003df2etinterface.h create mode 100644 ambed/cusb3003hrinterface.cpp create mode 100644 ambed/cusb3003hrinterface.h diff --git a/ambed/cusb3003df2etinterface.cpp b/ambed/cusb3003df2etinterface.cpp new file mode 100644 index 0000000..29f41e3 --- /dev/null +++ b/ambed/cusb3003df2etinterface.cpp @@ -0,0 +1,164 @@ +// +// cusb3003df2etinterface.cpp +// ambed +// +// Created by Jean-Luc Deltombe (LX3JL) and Florian Wolters (DF2ET) on 03/11/2017. +// Copyright © 2017 Jean-Luc Deltombe (LX3JL) and Florian Wolters (DF2ET). +// All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of ambed. +// +// xlxd 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 of the License, or +// (at your option) any later version. +// +// xlxd 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 Foobar. If not, see . +// ---------------------------------------------------------------------------- +// Created by Florian Wolters (DF2ET) on 03/11/2017. +// Copyright © 2017 Florian Wolters (DF2ET). All rights reserved. + +#include "main.h" +#include "ctimepoint.h" +#include "cambepacket.h" +#include "cusb3003df2etinterface.h" +#include "cvocodecs.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + +CUsb3003DF2ETInterface::CUsb3003DF2ETInterface(uint32 uiVid, uint32 uiPid, const char *szDeviceName, const char *szDeviceSerial) +: CUsb3003Interface(uiVid, uiPid, szDeviceName, szDeviceSerial) +{ +} + +//////////////////////////////////////////////////////////////////////////////////////// +// low level + +bool CUsb3003DF2ETInterface::OpenDevice(void) +{ + FT_STATUS ftStatus; + int baudrate = 921600; + + //sets serial VID/PID for a Standard Device NOTE: This is for legacy purposes only. This can be ommitted. + ftStatus = FT_SetVIDPID(m_uiVid, m_uiPid); + if (ftStatus != FT_OK) {FTDI_Error((char *)"FT_SetVIDPID", ftStatus ); return false; } + + ftStatus = FT_OpenEx((PVOID)m_szDeviceSerial, FT_OPEN_BY_SERIAL_NUMBER, &m_FtdiHandle); + if (ftStatus != FT_OK) { FTDI_Error((char *)"FT_OpenEx", ftStatus ); return false; } + + CTimePoint::TaskSleepFor(50); + FT_Purge(m_FtdiHandle, FT_PURGE_RX | FT_PURGE_TX ); + CTimePoint::TaskSleepFor(50); + + ftStatus = FT_SetDataCharacteristics(m_FtdiHandle, FT_BITS_8, FT_STOP_BITS_1, FT_PARITY_NONE); + if ( ftStatus != FT_OK ) { FTDI_Error((char *)"FT_SetDataCharacteristics", ftStatus ); return false; } + + ftStatus = FT_SetFlowControl(m_FtdiHandle, FT_FLOW_RTS_CTS, 0x11, 0x13); + if (ftStatus != FT_OK) { FTDI_Error((char *)"FT_SetFlowControl", ftStatus ); return false; } + + ftStatus = FT_SetRts (m_FtdiHandle); + if (ftStatus != FT_OK) { FTDI_Error((char *)"FT_SetRts", ftStatus ); return false; } + + // for DF2ET-3003 interface pull DTR low to take AMBE3003 out of reset. + ftStatus = FT_SetDtr( m_FtdiHandle ); + CTimePoint::TaskSleepFor(50); + if (ftStatus != FT_OK) { FTDI_Error((char *)"FT_SetDtr", ftStatus); return false; } + + ftStatus = FT_SetBaudRate(m_FtdiHandle, baudrate ); + if (ftStatus != FT_OK) { FTDI_Error((char *)"FT_SetBaudRate", ftStatus ); return false; } + + ftStatus = FT_SetLatencyTimer(m_FtdiHandle, 4); + if (ftStatus != FT_OK) { FTDI_Error((char *)"FT_SetLatencyTimer", ftStatus ); return false; } + + ftStatus = FT_SetUSBParameters(m_FtdiHandle, USB3XXX_MAXPACKETSIZE, 0); + if (ftStatus != FT_OK) { FTDI_Error((char *)"FT_SetUSBParameters", ftStatus ); return false; } + + ftStatus = FT_SetTimeouts(m_FtdiHandle, 200, 200 ); + if (ftStatus != FT_OK) { FTDI_Error((char *)"FT_SetTimeouts", ftStatus ); return false; } + + // done + return true; +} + +bool CUsb3003DF2ETInterface::ResetDevice(void) +{ + bool ok = false; + FT_STATUS ftStatus; + int len, i; + char rxpacket[100]; + + std::cout << "Trying AMBE3003USB soft reset" << std::endl; + + DWORD n, b; + char txpacket[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + char reset_packet[7] = { PKT_HEADER, 0, 3, 0, PKT_RESET, PKT_PARITYBYTE, 3 ^ PKT_RESET ^ PKT_PARITYBYTE }; + char *p; + + for (i = 0; i < 35; i++) + { + p = &txpacket[0]; + n = 10; + do + { + ftStatus = FT_Write( m_FtdiHandle, p, n, &b); + if (FT_OK != ftStatus) + { + return 1; + } + n -= b; + p += b; + } while (n > 0); + } + + p = &reset_packet[0]; + n = 7; + do + { + ftStatus = FT_Write( m_FtdiHandle, p, n, &b); + if (FT_OK != ftStatus) + { + return 1; + } + n -= b; + p += b; + } while (n > 0); + + len = FTDI_read_packet( m_FtdiHandle, rxpacket, sizeof(rxpacket) ); + ok = ((len == 7) && (rxpacket[4] == PKT_READY)); + if ( ok ) + { + std::cout << "AMBE3003USB soft reset succeeded" << std::endl; + } + else + { + std::cout << "AMBE3003USB soft reset failed" << std::endl; + + std::cout << "Trying AMBE3003USB hard reset" << std::endl; + + ftStatus = FT_ClrDtr( m_FtdiHandle ); + CTimePoint::TaskSleepFor(10); + ftStatus = FT_SetDtr( m_FtdiHandle ); + CTimePoint::TaskSleepFor(10); + + len = FTDI_read_packet( m_FtdiHandle, rxpacket, sizeof(rxpacket) ); + ok = ((len == 7) && (rxpacket[4] == PKT_READY)); + if ( ok ) + { + std::cout << "AMBE3003USB hard reset succeeded" << std::endl; + } + else + { + std::cout << "AMBE3003USB hard reset failed" << std::endl; + } + } + return ok; +} + diff --git a/ambed/cusb3003df2etinterface.h b/ambed/cusb3003df2etinterface.h new file mode 100644 index 0000000..ad11cc3 --- /dev/null +++ b/ambed/cusb3003df2etinterface.h @@ -0,0 +1,57 @@ +// +// cusb3003df2etinterface.h +// ambed +// +// Created by Jean-Luc Deltombe (LX3JL) and Florian Wolters (DF2ET) on 03/11/2017. +// Copyright © 2017 Jean-Luc Deltombe (LX3JL) and Florian Wolters (DF2ET). +// All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of ambed. +// +// xlxd 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 of the License, or +// (at your option) any later version. +// +// xlxd 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 Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cusb3003df2etinterface_h +#define cusb3003df2etinterface_h + + +#include "ftd2xx.h" +#include "cbuffer.h" +#include "cusb3003interface.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CUsb3003DF2ETInterface : public CUsb3003Interface +{ +public: + // constructors + CUsb3003DF2ETInterface(uint32, uint32, const char *, const char *); + + // destructor + virtual ~CUsb3003DF2ETInterface() {} + +protected: + // low level + bool OpenDevice(void); + bool ResetDevice(void); +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cusb3003df2etinterface_h */ diff --git a/ambed/cusb3003hrinterface.cpp b/ambed/cusb3003hrinterface.cpp new file mode 100644 index 0000000..f096d85 --- /dev/null +++ b/ambed/cusb3003hrinterface.cpp @@ -0,0 +1,66 @@ +// +// cusb3003hrinterface.cpp +// ambed +// +// Created by Jean-Luc Deltombe (LX3JL) on 30/10/2017. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of ambed. +// +// xlxd 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 of the License, or +// (at your option) any later version. +// +// xlxd 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 Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "main.h" +#include "ctimepoint.h" +#include "cambepacket.h" +#include "cusb3003hrinterface.h" +#include "cvocodecs.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + +CUsb3003HRInterface::CUsb3003HRInterface(uint32 uiVid, uint32 uiPid, const char *szDeviceName, const char *szDeviceSerial) +: CUsb3003Interface(uiVid, uiPid, szDeviceName, szDeviceSerial) +{ +} + +//////////////////////////////////////////////////////////////////////////////////////// +// low level + +bool CUsb3003HRInterface::ResetDevice(void) +{ + bool ok = false; + FT_STATUS ftStatus; + int len; + char rxpacket[100]; + + //if the device is a USB-3003, it supports reset via UART break signal + //printf("reset via uart break...\n"); + ftStatus = FT_SetBreakOn( m_FtdiHandle ); + CTimePoint::TaskSleepFor(10); + ftStatus = FT_SetBreakOff( m_FtdiHandle ); + //CTimePoint::TaskSleepFor(10); + + len = FTDI_read_packet( m_FtdiHandle, rxpacket, sizeof(rxpacket) ); + ok = ((len == 7) && (rxpacket[4] == PKT_READY)); + if ( !ok ) + { + std::cout << "USB-3003 hard reset failed" << std::endl; + } + + // done + return ok; +} + diff --git a/ambed/cusb3003hrinterface.h b/ambed/cusb3003hrinterface.h new file mode 100644 index 0000000..b045e14 --- /dev/null +++ b/ambed/cusb3003hrinterface.h @@ -0,0 +1,55 @@ +// +// cusb3003hrinterface.h +// ambed +// +// Created by Jean-Luc Deltombe (LX3JL) on 30/10/2017. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of ambed. +// +// xlxd 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 of the License, or +// (at your option) any later version. +// +// xlxd 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 Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cusb3003hrinterface_h +#define cusb3003hrinterface_h + + +#include "ftd2xx.h" +#include "cbuffer.h" +#include "cusb3003interface.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CUsb3003HRInterface : public CUsb3003Interface +{ +public: + // constructors + CUsb3003HRInterface(uint32, uint32, const char *, const char *); + + // destructor + virtual ~CUsb3003HRInterface() {} + +protected: + // low level + bool ResetDevice(void); +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cusb3003hrinterface_h */ From 5419ef64b86f3a1395372a80358f9f1bfa0e73e5 Mon Sep 17 00:00:00 2001 From: phl0 Date: Sun, 5 Nov 2017 20:48:08 +0100 Subject: [PATCH 011/244] Align device names --- ambed/cusb3003df2etinterface.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ambed/cusb3003df2etinterface.cpp b/ambed/cusb3003df2etinterface.cpp index 29f41e3..94cd357 100644 --- a/ambed/cusb3003df2etinterface.cpp +++ b/ambed/cusb3003df2etinterface.cpp @@ -95,7 +95,7 @@ bool CUsb3003DF2ETInterface::ResetDevice(void) int len, i; char rxpacket[100]; - std::cout << "Trying AMBE3003USB soft reset" << std::endl; + std::cout << "Trying DF2ET-3003 soft reset" << std::endl; DWORD n, b; char txpacket[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -135,13 +135,13 @@ bool CUsb3003DF2ETInterface::ResetDevice(void) ok = ((len == 7) && (rxpacket[4] == PKT_READY)); if ( ok ) { - std::cout << "AMBE3003USB soft reset succeeded" << std::endl; + std::cout << "DF2ET-3003 soft reset succeeded" << std::endl; } else { - std::cout << "AMBE3003USB soft reset failed" << std::endl; + std::cout << "DF2ET-3003 soft reset failed" << std::endl; - std::cout << "Trying AMBE3003USB hard reset" << std::endl; + std::cout << "Trying DF2ET-3003 hard reset" << std::endl; ftStatus = FT_ClrDtr( m_FtdiHandle ); CTimePoint::TaskSleepFor(10); @@ -152,11 +152,11 @@ bool CUsb3003DF2ETInterface::ResetDevice(void) ok = ((len == 7) && (rxpacket[4] == PKT_READY)); if ( ok ) { - std::cout << "AMBE3003USB hard reset succeeded" << std::endl; + std::cout << "DF2ET-3003 hard reset succeeded" << std::endl; } else { - std::cout << "AMBE3003USB hard reset failed" << std::endl; + std::cout << "DF2ET-3003 hard reset failed" << std::endl; } } return ok; From 8482f85478a7d320899dbddbc07ada0902a5e94c Mon Sep 17 00:00:00 2001 From: phl0 Date: Mon, 6 Nov 2017 12:56:27 +0100 Subject: [PATCH 012/244] Fix typo --- src/ccodecstream.cpp | 2 +- src/creflector.h | 2 +- src/ctranscoder.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ccodecstream.cpp b/src/ccodecstream.cpp index ca4ac1e..7d8e0c8 100644 --- a/src/ccodecstream.cpp +++ b/src/ccodecstream.cpp @@ -95,7 +95,7 @@ bool CCodecStream::Init(uint16 uiPort) m_bStopThread = false; // create server's IP - m_Ip = g_Reflector.GetTrasncoderIp(); + m_Ip = g_Reflector.GetTranscoderIp(); m_uiPort = uiPort; // create our socket diff --git a/src/creflector.h b/src/creflector.h index 8c9a9d0..1424453 100644 --- a/src/creflector.h +++ b/src/creflector.h @@ -60,7 +60,7 @@ public: void SetListenIp(const CIp &ip) { m_Ip = ip; } void SetTranscoderIp(const CIp &ip) { m_AmbedIp = ip; } const CIp &GetListenIp(void) const { return m_Ip; } - const CIp &GetTrasncoderIp(void) const { return m_AmbedIp; } + const CIp &GetTranscoderIp(void) const { return m_AmbedIp; } // operation bool Start(void); diff --git a/src/ctranscoder.cpp b/src/ctranscoder.cpp index e8082bd..ff378c7 100644 --- a/src/ctranscoder.cpp +++ b/src/ctranscoder.cpp @@ -94,7 +94,7 @@ bool CTranscoder::Init(void) m_bStopThread = false; // create server's IP - m_Ip = g_Reflector.GetTrasncoderIp(); + m_Ip = g_Reflector.GetTranscoderIp(); // create our socket ok = m_Socket.Open(TRANSCODER_PORT); From 35b8fd1261cb63d643001f5faf29c38fff74cc4b Mon Sep 17 00:00:00 2001 From: LX3JL Date: Mon, 6 Nov 2017 15:29:53 +0100 Subject: [PATCH 013/244] amber 1.2.0 corrected bug when pairing 2 even devices --- ambed/cftdidevicedescr.cpp | 10 +++++----- ambed/cusb3003df2etinterface.cpp | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ambed/cftdidevicedescr.cpp b/ambed/cftdidevicedescr.cpp index 88411a2..cc94c9d 100644 --- a/ambed/cftdidevicedescr.cpp +++ b/ambed/cftdidevicedescr.cpp @@ -95,28 +95,28 @@ int CFtdiDeviceDescr::CreateInterfacePair(CFtdiDeviceDescr *descr1, CFtdiDeviceD int iNbChs = 0; // create interface objects - if ( (descr1->GetNbChannels() == 1) && (descr1->GetNbChannels() == 1) ) + if ( (descr1->GetNbChannels() == 1) && (descr2->GetNbChannels() == 1) ) { // create 3000-3000 pair CUsb3000Interface *Usb3000A = InstantiateUsb3000(descr1); CUsb3000Interface *Usb3000B = InstantiateUsb3000(descr2); iNbChs = CreatePair(Usb3000A, Usb3000B, channels); } - else if ( (descr1->GetNbChannels() == 3) && (descr1->GetNbChannels() == 1) ) + else if ( (descr1->GetNbChannels() == 3) && (descr2->GetNbChannels() == 1) ) { // create 3003-3000 pair CUsb3003Interface *Usb3003 = InstantiateUsb3003(descr1); CUsb3000Interface *Usb3000 = InstantiateUsb3000(descr2); iNbChs = CreatePair(Usb3003, Usb3000, channels); } - else if ( (descr1->GetNbChannels() == 1) && (descr1->GetNbChannels() == 3) ) + else if ( (descr1->GetNbChannels() == 1) && (descr2->GetNbChannels() == 3) ) { // create 3000-3003 pair CUsb3000Interface *Usb3000 = InstantiateUsb3000(descr1); CUsb3003Interface *Usb3003 = InstantiateUsb3003(descr2); iNbChs = CreatePair(Usb3003, Usb3000, channels); } - else if ( (descr1->GetNbChannels() == 3) && (descr1->GetNbChannels() == 3) ) + else if ( (descr1->GetNbChannels() == 3) && (descr2->GetNbChannels() == 3) ) { // create 3003-3003 pair CUsb3003Interface *Usb3003A = InstantiateUsb3003(descr1); @@ -390,7 +390,7 @@ int CFtdiDeviceDescr::CreatePair(CUsb3000Interface *Usb3000A, CUsb3000Interface Usb3000A->AddChannel(Channel); Usb3000B->AddChannel(Channel); // ch2 - Channel = new CVocodecChannel(Usb3000A, 0, Usb3000B, 0, CODECGAIN_AMBE2PLUS); + Channel = new CVocodecChannel(Usb3000B, 0, Usb3000A, 0, CODECGAIN_AMBE2PLUS); channels->push_back(Channel); Usb3000A->AddChannel(Channel); Usb3000B->AddChannel(Channel); diff --git a/ambed/cusb3003df2etinterface.cpp b/ambed/cusb3003df2etinterface.cpp index 94cd357..29f41e3 100644 --- a/ambed/cusb3003df2etinterface.cpp +++ b/ambed/cusb3003df2etinterface.cpp @@ -95,7 +95,7 @@ bool CUsb3003DF2ETInterface::ResetDevice(void) int len, i; char rxpacket[100]; - std::cout << "Trying DF2ET-3003 soft reset" << std::endl; + std::cout << "Trying AMBE3003USB soft reset" << std::endl; DWORD n, b; char txpacket[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -135,13 +135,13 @@ bool CUsb3003DF2ETInterface::ResetDevice(void) ok = ((len == 7) && (rxpacket[4] == PKT_READY)); if ( ok ) { - std::cout << "DF2ET-3003 soft reset succeeded" << std::endl; + std::cout << "AMBE3003USB soft reset succeeded" << std::endl; } else { - std::cout << "DF2ET-3003 soft reset failed" << std::endl; + std::cout << "AMBE3003USB soft reset failed" << std::endl; - std::cout << "Trying DF2ET-3003 hard reset" << std::endl; + std::cout << "Trying AMBE3003USB hard reset" << std::endl; ftStatus = FT_ClrDtr( m_FtdiHandle ); CTimePoint::TaskSleepFor(10); @@ -152,11 +152,11 @@ bool CUsb3003DF2ETInterface::ResetDevice(void) ok = ((len == 7) && (rxpacket[4] == PKT_READY)); if ( ok ) { - std::cout << "DF2ET-3003 hard reset succeeded" << std::endl; + std::cout << "AMBE3003USB hard reset succeeded" << std::endl; } else { - std::cout << "DF2ET-3003 hard reset failed" << std::endl; + std::cout << "AMBE3003USB hard reset failed" << std::endl; } } return ok; From 034a4ac8c99f6e48a54ac928049d3cca25ed8c25 Mon Sep 17 00:00:00 2001 From: phl0 Date: Tue, 7 Nov 2017 01:33:31 +0100 Subject: [PATCH 014/244] realign devicenames after accidental rename --- ambed/cusb3003df2etinterface.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ambed/cusb3003df2etinterface.cpp b/ambed/cusb3003df2etinterface.cpp index 29f41e3..94cd357 100644 --- a/ambed/cusb3003df2etinterface.cpp +++ b/ambed/cusb3003df2etinterface.cpp @@ -95,7 +95,7 @@ bool CUsb3003DF2ETInterface::ResetDevice(void) int len, i; char rxpacket[100]; - std::cout << "Trying AMBE3003USB soft reset" << std::endl; + std::cout << "Trying DF2ET-3003 soft reset" << std::endl; DWORD n, b; char txpacket[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -135,13 +135,13 @@ bool CUsb3003DF2ETInterface::ResetDevice(void) ok = ((len == 7) && (rxpacket[4] == PKT_READY)); if ( ok ) { - std::cout << "AMBE3003USB soft reset succeeded" << std::endl; + std::cout << "DF2ET-3003 soft reset succeeded" << std::endl; } else { - std::cout << "AMBE3003USB soft reset failed" << std::endl; + std::cout << "DF2ET-3003 soft reset failed" << std::endl; - std::cout << "Trying AMBE3003USB hard reset" << std::endl; + std::cout << "Trying DF2ET-3003 hard reset" << std::endl; ftStatus = FT_ClrDtr( m_FtdiHandle ); CTimePoint::TaskSleepFor(10); @@ -152,11 +152,11 @@ bool CUsb3003DF2ETInterface::ResetDevice(void) ok = ((len == 7) && (rxpacket[4] == PKT_READY)); if ( ok ) { - std::cout << "AMBE3003USB hard reset succeeded" << std::endl; + std::cout << "DF2ET-3003 hard reset succeeded" << std::endl; } else { - std::cout << "AMBE3003USB hard reset failed" << std::endl; + std::cout << "DF2ET-3003 hard reset failed" << std::endl; } } return ok; From 384b6d41b1545a2d08e725550ff585671ec52b8a Mon Sep 17 00:00:00 2001 From: LX3JL Date: Wed, 8 Nov 2017 10:36:19 +0100 Subject: [PATCH 015/244] amber 1.2.1 corrected bug when pairing 3003+3000 added version reporting at startup --- ambed/cftdidevicedescr.cpp | 9 +++++++-- ambed/main.cpp | 2 +- ambed/main.h | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/ambed/cftdidevicedescr.cpp b/ambed/cftdidevicedescr.cpp index cc94c9d..f3d12b7 100644 --- a/ambed/cftdidevicedescr.cpp +++ b/ambed/cftdidevicedescr.cpp @@ -485,13 +485,18 @@ int CFtdiDeviceDescr::CreatePair(CUsb3003Interface *Usb3003A, CUsb3000Interface Channel = new CVocodecChannel(Usb3003A, 1, Usb3003A, 0, CODECGAIN_AMBE2PLUS); channels->push_back(Channel); Usb3003A->AddChannel(Channel); - // ch5 + // ch3 Channel = new CVocodecChannel(Usb3003A, 2, Usb3000B, 0, CODECGAIN_AMBEPLUS); channels->push_back(Channel); Usb3003A->AddChannel(Channel); Usb3000B->AddChannel(Channel); + // ch4 + Channel = new CVocodecChannel(Usb3000B, 2, Usb3003A, 2, CODECGAIN_AMBE2PLUS); + channels->push_back(Channel); + Usb3003A->AddChannel(Channel); + Usb3000B->AddChannel(Channel); // done - nStreams = 6; + nStreams = 4; } } else diff --git a/ambed/main.cpp b/ambed/main.cpp index 078174f..c9363e2 100644 --- a/ambed/main.cpp +++ b/ambed/main.cpp @@ -97,7 +97,7 @@ int main(int argc, const char * argv[]) g_AmbeServer.SetListenIp(CIp(argv[1])); // and let it run - std::cout << "Starting AMBEd" << std::endl << std::endl; + std::cout << "Starting AMBEd " << VERSION_MAJOR << "." << VERSION_MINOR << "." << VERSION_REVISION << std::endl << std::endl; if ( !g_AmbeServer.Start() ) { std::cout << "Error starting AMBEd" << std::endl; diff --git a/ambed/main.h b/ambed/main.h index 36a04ed..08f3221 100644 --- a/ambed/main.h +++ b/ambed/main.h @@ -49,7 +49,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 2 -#define VERSION_REVISION 0 +#define VERSION_REVISION 1 // global ------------------------------------------------------ From ddc52640df6bbeb8be61dac659a88b61c0a99c93 Mon Sep 17 00:00:00 2001 From: LX3JL Date: Wed, 8 Nov 2017 11:11:30 +0100 Subject: [PATCH 016/244] amber 1.2.1 --- ambed/cftdidevicedescr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ambed/cftdidevicedescr.cpp b/ambed/cftdidevicedescr.cpp index f3d12b7..6b68821 100644 --- a/ambed/cftdidevicedescr.cpp +++ b/ambed/cftdidevicedescr.cpp @@ -491,7 +491,7 @@ int CFtdiDeviceDescr::CreatePair(CUsb3003Interface *Usb3003A, CUsb3000Interface Usb3003A->AddChannel(Channel); Usb3000B->AddChannel(Channel); // ch4 - Channel = new CVocodecChannel(Usb3000B, 2, Usb3003A, 2, CODECGAIN_AMBE2PLUS); + Channel = new CVocodecChannel(Usb3000B, 0, Usb3003A, 2, CODECGAIN_AMBE2PLUS); channels->push_back(Channel); Usb3003A->AddChannel(Channel); Usb3000B->AddChannel(Channel); From 24c97c6988fa1c2314c2f300003b02c8026f2887 Mon Sep 17 00:00:00 2001 From: Stefan Saraev Date: Sun, 12 Nov 2017 00:22:54 +0200 Subject: [PATCH 017/244] makefile: force overwrite target binary fixes "cp: cannot create regular file '/xlxd/xlxd': Text file busy --- src/makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/makefile b/src/makefile index f0f1378..691c14e 100644 --- a/src/makefile +++ b/src/makefile @@ -18,7 +18,7 @@ clean: install: mkdir -p /xlxd - cp $(EXECUTABLE) /xlxd/ + cp -f $(EXECUTABLE) /xlxd/ cp -i ../config/xlxd.blacklist /xlxd/ cp -i ../config/xlxd.whitelist /xlxd/ cp -i ../config/xlxd.interlink /xlxd/ From 6f940fcc31af6d28443c41afe214c041f0d0eda1 Mon Sep 17 00:00:00 2001 From: Stefan Saraev Date: Sun, 12 Nov 2017 00:24:22 +0200 Subject: [PATCH 018/244] makefile: dont ask to overwrite configfiles. install .sample configs instead --- src/makefile | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/makefile b/src/makefile index 691c14e..de1e0db 100644 --- a/src/makefile +++ b/src/makefile @@ -19,6 +19,12 @@ clean: install: mkdir -p /xlxd cp -f $(EXECUTABLE) /xlxd/ - cp -i ../config/xlxd.blacklist /xlxd/ - cp -i ../config/xlxd.whitelist /xlxd/ - cp -i ../config/xlxd.interlink /xlxd/ + [ -f /xlxd/xlxd.blacklist ] && \ + cp ../config/xlxd.blacklist /xlxd/xlxd.blacklist.sample || \ + cp ../config/xlxd.blacklist /xlxd/xlxd.blacklist + [ -f /xlxd/xlxd.whitelist ] && \ + cp ../config/xlxd.whitelist /xlxd/xlxd.whitelist.sample || \ + cp ../config/xlxd.whitelist /xlxd/xlxd.whitelist + [ -f /xlxd/xlxd.interlink ] && \ + cp ../config/xlxd.interlink /xlxd/xlxd.interlink.sample || \ + cp ../config/xlxd.interlink /xlxd/xlxd.interlink From 18a5cd95448391119e70b2a9702b68d7d43738a0 Mon Sep 17 00:00:00 2001 From: LX3JL Date: Mon, 13 Nov 2017 08:42:32 +0100 Subject: [PATCH 019/244] ambed 1.3.0 Improved performances. Most device configuration can now run all available channels concurrently. See readme for details. --- ambed/cambepacket.h | 3 + ambed/cpacket.h | 12 ++- ambed/cusb3003interface.h | 1 + ambed/cusb3xxxinterface.cpp | 175 ++++++++++++++++++++++++------------ ambed/cusb3xxxinterface.h | 26 +++--- ambed/cvoicepacket.h | 5 +- ambed/main.h | 4 +- ambed/readme | 11 +-- ambedtest/ccodecstream.cpp | 28 +++--- ambedtest/ccodecstream.h | 1 + ambedtest/makefile | 4 +- 11 files changed, 178 insertions(+), 92 deletions(-) diff --git a/ambed/cambepacket.h b/ambed/cambepacket.h index 13cd124..f6ae00a 100644 --- a/ambed/cambepacket.h +++ b/ambed/cambepacket.h @@ -48,6 +48,9 @@ public: // destructor virtual ~CAmbePacket(); + // identity + bool IsAmbe(void) const { return true; } + // get uint8 GetCodec(void) const { return m_uiCodec; } uint8 *GetAmbe(void) { return m_uiAmbe; } diff --git a/ambed/cpacket.h b/ambed/cpacket.h index cd30d6a..a7768c0 100644 --- a/ambed/cpacket.h +++ b/ambed/cpacket.h @@ -40,13 +40,17 @@ public: // destructor virtual ~CPacket() {}; + // identity + virtual bool IsVoice(void) const { return false; } + virtual bool IsAmbe(void) const { return false; } + // get - int GetChannel(void) const { return m_iCh; } - uint8 GetPid(void) const { return m_uiPid; } + int GetChannel(void) const { return m_iCh; } + uint8 GetPid(void) const { return m_uiPid; } // set - void SetChannel(int i) { m_iCh = i; } - void SetPid(uint8 ui) { m_uiPid = ui; } + void SetChannel(int i) { m_iCh = i; } + void SetPid(uint8 ui) { m_uiPid = ui; } protected: // data diff --git a/ambed/cusb3003interface.h b/ambed/cusb3003interface.h index 2be4f57..984130e 100644 --- a/ambed/cusb3003interface.h +++ b/ambed/cusb3003interface.h @@ -70,6 +70,7 @@ protected: bool OpenDevice(void); bool ResetDevice(void); bool ConfigureDevice(void); + int GetDeviceFifoSize(void) const { return 2; } // data uint8 m_uiChCodecs[USB3003_NB_CH]; diff --git a/ambed/cusb3xxxinterface.cpp b/ambed/cusb3xxxinterface.cpp index 7bf3fdd..c8eb461 100644 --- a/ambed/cusb3xxxinterface.cpp +++ b/ambed/cusb3xxxinterface.cpp @@ -39,12 +39,12 @@ CUsb3xxxInterface::CUsb3xxxInterface(uint32 uiVid, uint32 uiPid, const char *szDeviceName, const char *szDeviceSerial) { m_FtdiHandle = NULL; - m_iDeviceFifoLevel = 0; - m_iActiveQueue = QUEUE_CHANNEL; m_uiVid = uiVid; m_uiPid = uiPid; ::strcpy(m_szDeviceName, szDeviceName); ::strcpy(m_szDeviceSerial, szDeviceSerial); + m_iSpeechFifolLevel = 0; + m_iChannelFifolLevel = 0; } //////////////////////////////////////////////////////////////////////////////////////// @@ -52,6 +52,19 @@ CUsb3xxxInterface::CUsb3xxxInterface(uint32 uiVid, uint32 uiPid, const char *szD CUsb3xxxInterface::~CUsb3xxxInterface() { + // delete m_SpeechQueues + for ( int i = 0; i < m_SpeechQueues.size(); i++ ) + { + delete m_SpeechQueues[i]; + } + m_SpeechQueues.clear(); + + // delete m_ChannelQueues + for ( int i = 0; i < m_ChannelQueues.size(); i++ ) + { + delete m_ChannelQueues[i]; + } + m_ChannelQueues.clear(); } //////////////////////////////////////////////////////////////////////////////////////// @@ -82,6 +95,13 @@ bool CUsb3xxxInterface::Init(void) } std::cout << std::endl; + // create our queues + for ( int i = 0; i < GetNbChannels(); i++ ) + { + m_SpeechQueues.push_back(new CPacketQueue); + m_ChannelQueues.push_back(new CPacketQueue); + } + // base class if ( ok ) { @@ -118,11 +138,10 @@ void CUsb3xxxInterface::Task(void) { if ( IsValidSpeechPacket(Buffer, &iCh, &VoicePacket) ) { -#ifdef DEBUG_DUMPFILE - g_AmbeServer.m_DebugFile << m_szDeviceName << "\t" << "->Sp" << iCh << std::endl; std::cout.flush(); -#endif - // update fifo status - m_iDeviceFifoLevel = MAX(m_iDeviceFifoLevel-1, 0); + // update fifo level + // as we get a speech packet, it means that the device + // channel fifo input decreased by 1 + m_iChannelFifolLevel = MAX(0, m_iChannelFifolLevel-1); // push back to relevant channel voice queue // our incoming channel packet has now been through the decoder @@ -140,13 +159,14 @@ void CUsb3xxxInterface::Task(void) } else if ( IsValidChannelPacket(Buffer, &iCh, &AmbePacket) ) { -#ifdef DEBUG_DUMPFILE - g_AmbeServer.m_DebugFile << m_szDeviceName << "\t" << "->Ch" << iCh << std::endl; std::cout.flush(); -#endif - // update fifo status - m_iDeviceFifoLevel = MAX(m_iDeviceFifoLevel-1, 0); - + // update fifo level + // as we get a channel packet, it means that the device + // speech fifo input decreased by 1 + m_iSpeechFifolLevel = MAX(0, m_iSpeechFifolLevel-1); + // push back to relevant channel outcoming queue + // we are done with this packet transcoding + // it's final step Channel = GetChannelWithChannelOut(iCh); if ( Channel != NULL ) { @@ -178,9 +198,12 @@ void CUsb3xxxInterface::Task(void) // get packet CVoicePacket *Packet = (CVoicePacket *)Queue->front(); Queue->pop(); + // this is second step of transcoding + // we just received from hardware a decoded speech packet // post it to relevant channel encoder - Packet->SetChannel(Channel->GetChannelOut()); - m_SpeechQueue.push(Packet); + int i = Channel->GetChannelOut(); + Packet->SetChannel(i); + m_SpeechQueues[i]->push(Packet); // done done = false; } @@ -196,9 +219,12 @@ void CUsb3xxxInterface::Task(void) // get packet CAmbePacket *Packet = (CAmbePacket *)Queue->front(); Queue->pop(); + // this is first step of transcoding + // a fresh new packet to be transcoded is showing up // post it to relevant channel decoder - Packet->SetChannel(Channel->GetChannelIn()); - m_ChannelQueue.push(Packet); + int i = Channel->GetChannelIn(); + Packet->SetChannel(i); + m_ChannelQueues[i]->push(Packet); // done done = false; } @@ -207,57 +233,94 @@ void CUsb3xxxInterface::Task(void) } } while (!done); - // process device incoming queues + // process device incoming queues (aka to device) // interlace speech and channels packets - // and make sure that the fifo is always - // fed. - unsigned long iQueueLevel = m_SpeechQueue.size() + m_ChannelQueue.size(); - if ( ((m_iDeviceFifoLevel == 0) && (iQueueLevel >= 2)) || (m_iDeviceFifoLevel == 1) ) + // and post to final device queue + do { - if ( m_iActiveQueue == QUEUE_CHANNEL ) + done = true; + // loop on all channels + for ( int i = 0; i < GetNbChannels(); i++ ) { - // post next channel packet - if ( !m_ChannelQueue.empty() ) + // speech + if ( !m_SpeechQueues[i]->empty() ) { // get packet - CAmbePacket *Packet = (CAmbePacket *)m_ChannelQueue.front(); - m_ChannelQueue.pop(); - //Post it - EncodeChannelPacket(&Buffer, Packet->GetChannel(), Packet); - WriteBuffer(Buffer); - m_iDeviceFifoLevel++; - // and delete it -#ifdef DEBUG_DUMPFILE - g_AmbeServer.m_DebugFile << m_szDeviceName << "\t" << "Ch" << Packet->GetChannel() << "->" << std::endl; std::cout.flush(); -#endif - delete Packet; - } - // and interlace - m_iActiveQueue = QUEUE_SPEECH; + CPacket *Packet = m_SpeechQueues[i]->front(); + m_SpeechQueues[i]->pop(); + // and push to device queue + m_DeviceQueue.push(Packet); + // next + done = false; + } + // ambe + if ( !m_ChannelQueues[i]->empty() ) + { + // get packet + CPacket *Packet = m_ChannelQueues[i]->front(); + m_ChannelQueues[i]->pop(); + // and push to device queue + m_DeviceQueue.push(Packet); + // done = false; + } } - else + + } while (!done); + + // process device queue to feed hardware + // make sure that device fifo is fed all the time + int fifoSize = GetDeviceFifoSize(); + do + { + done = true; + // if device fifo level is zero (device idle) + // wait that at least 3 packets are in incoming + // queue before restarting + if ( ((m_iSpeechFifolLevel+m_iChannelFifolLevel) > 0) || (m_DeviceQueue.size() >= (fifoSize+1)) ) { - // post next speech packet - if ( !m_SpeechQueue.empty() ) + // any packet to send ? + if ( m_DeviceQueue.size() > 0 ) { - // get packet - CVoicePacket *Packet = (CVoicePacket *)m_SpeechQueue.front(); - m_SpeechQueue.pop(); - //Post it - EncodeSpeechPacket(&Buffer, Packet->GetChannel(), Packet); - WriteBuffer(Buffer); - m_iDeviceFifoLevel++; - // and delete it + // yes, get it + CPacket *Packet = m_DeviceQueue.front(); + if ( Packet->IsVoice() && (m_iSpeechFifolLevel < fifoSize) ) + { + // encode & post + EncodeSpeechPacket(&Buffer, Packet->GetChannel(), (CVoicePacket *)Packet); + WriteBuffer(Buffer); + // remove from queue + m_DeviceQueue.pop(); + // and delete it + delete Packet; + // update fifo level + m_iSpeechFifolLevel++; + // next + done = false; #ifdef DEBUG_DUMPFILE - g_AmbeServer.m_DebugFile << m_szDeviceName << "\t" << "Sp" << Packet->GetChannel() << "->" << std::endl; std::cout.flush(); + g_AmbeServer.m_DebugFile << m_szDeviceName << "\t" << "Sp" << Packet->GetChannel() << "->" << std::endl; std::cout.flush(); #endif - delete Packet; + } + else if ( Packet->IsAmbe() && (m_iChannelFifolLevel < fifoSize) ) + { + // encode & post + EncodeChannelPacket(&Buffer, Packet->GetChannel(), (CAmbePacket *)Packet); + WriteBuffer(Buffer); + // remove from queue + m_DeviceQueue.pop(); + // and delete it + delete Packet; + // update fifo level + m_iChannelFifolLevel++; + // next + done = false; +#ifdef DEBUG_DUMPFILE + g_AmbeServer.m_DebugFile << m_szDeviceName << "\t" << "Ch" << Packet->GetChannel() << "->" << std::endl; std::cout.flush(); +#endif + } + } - // and interlace - m_iActiveQueue = QUEUE_CHANNEL; } - } - + } while (!done); // and wait a bit CTimePoint::TaskSleepFor(2); diff --git a/ambed/cusb3xxxinterface.h b/ambed/cusb3xxxinterface.h index 31bf3c4..d7dd3cf 100644 --- a/ambed/cusb3xxxinterface.h +++ b/ambed/cusb3xxxinterface.h @@ -98,12 +98,13 @@ protected: virtual void EncodeSpeechPacket(CBuffer *, int, CVoicePacket *) {} // low level - virtual bool OpenDevice(void) { return false; } - virtual bool ResetDevice(void) { return false; } + virtual bool OpenDevice(void) { return false; } + virtual bool ResetDevice(void) { return false; } bool ReadDeviceVersion(void); bool DisableParity(void); - virtual bool ConfigureDevice(void) { return false; } + virtual bool ConfigureDevice(void) { return false; } bool ConfigureChannel(uint8, const uint8 *, int, int); + virtual int GetDeviceFifoSize(void) const { return 1; } // io level bool ReadBuffer(CBuffer *); @@ -117,17 +118,18 @@ protected: protected: // data - uint32 m_uiVid; - uint32 m_uiPid; - char m_szDeviceName[FTDI_MAX_STRINGLENGTH]; - char m_szDeviceSerial[FTDI_MAX_STRINGLENGTH]; - FT_HANDLE m_FtdiHandle; + uint32 m_uiVid; + uint32 m_uiPid; + char m_szDeviceName[FTDI_MAX_STRINGLENGTH]; + char m_szDeviceSerial[FTDI_MAX_STRINGLENGTH]; + FT_HANDLE m_FtdiHandle; // queue - CPacketQueue m_SpeechQueue; - CPacketQueue m_ChannelQueue; - int m_iDeviceFifoLevel; - int m_iActiveQueue; + std::vector m_SpeechQueues; + std::vector m_ChannelQueues; + CPacketQueue m_DeviceQueue; + int m_iSpeechFifolLevel; + int m_iChannelFifolLevel; }; //////////////////////////////////////////////////////////////////////////////////////// diff --git a/ambed/cvoicepacket.h b/ambed/cvoicepacket.h index 7189d01..7dbefc3 100644 --- a/ambed/cvoicepacket.h +++ b/ambed/cvoicepacket.h @@ -47,7 +47,10 @@ public: // destructor virtual ~CVoicePacket(); - + + // identity + bool IsVoice(void) const { return true; } + // get uint8 *GetVoice(void) { return m_uiVoice; } int GetVoiceSize(void) const { return m_iSize; } diff --git a/ambed/main.h b/ambed/main.h index 08f3221..3c7bd19 100644 --- a/ambed/main.h +++ b/ambed/main.h @@ -48,8 +48,8 @@ // version ----------------------------------------------------- #define VERSION_MAJOR 1 -#define VERSION_MINOR 2 -#define VERSION_REVISION 1 +#define VERSION_MINOR 3 +#define VERSION_REVISION 0 // global ------------------------------------------------------ diff --git a/ambed/readme b/ambed/readme index 9d74345..4224cca 100644 --- a/ambed/readme +++ b/ambed/readme @@ -22,6 +22,7 @@ // along with Foobar. If not, see . // ---------------------------------------------------------------------------- +VERSION: 1.3.0 Hardware compatibility. ====================== @@ -37,13 +38,13 @@ This version of ambed is compatible with: Available transcoding channels per device: device DMR->DSTAR DSTAR->DMR Nb Of concurrent channels ----------------------------------------------------------------------- +------------------------------------------------------------------------- 3000(pair) 1 1 2 -3003 1 1 1 -3003(pair) 3 3 not tested +3003 1 1 2 +3003(pair) 3 3 4 3003-3000(pair) 2 2 not tested -3006 3 3 4 -3012 6 6 8 +3006 3 3 6 +3012 6 6 12 Multiple devices can be used at the same time. You need to use 3000 by pairs or paired with a 3003 diff --git a/ambedtest/ccodecstream.cpp b/ambedtest/ccodecstream.cpp index 5910f8d..5c5fcf3 100644 --- a/ambedtest/ccodecstream.cpp +++ b/ambedtest/ccodecstream.cpp @@ -45,6 +45,7 @@ CCodecStream::CCodecStream(uint16 uiId, uint8 uiCodecIn, uint8 uiCodecOut) m_bConnected = false; m_iAmbeSrcPtr = 0; m_iAmbeDestPtr = 0; + m_uiNbTotalPacketSent = 0; m_uiNbPacketSent = 0; m_uiNbPacketReceived = 0; m_uiNbPacketBad = 0; @@ -120,6 +121,7 @@ bool CCodecStream::Init(uint16 uiPort) m_pThread = new std::thread(CCodecStream::Thread, this); m_bConnected = true; m_FrameTimer.Now(); + m_uiNbTotalPacketSent = 0; ResetStats(); } else @@ -181,11 +183,12 @@ void CCodecStream::Task(void) // and increment pointer m_iAmbeSrcPtr = (m_iAmbeSrcPtr + 1) % m_AmbeSrc.size(); + m_uiNbTotalPacketSent++; m_uiNbPacketSent++; }*/ // any packt to send to trancoder ? - uint32 uiNbPacketToSend = (uint32)(m_FrameTimer.DurationSinceNow() * 50.0) - m_uiNbPacketSent; + uint32 uiNbPacketToSend = (uint32)(m_FrameTimer.DurationSinceNow() * 50.0) - m_uiNbTotalPacketSent; if ( uiNbPacketToSend > 0 ) { for ( int i = 0; i < uiNbPacketToSend; i++ ) @@ -196,6 +199,7 @@ void CCodecStream::Task(void) // and increment pointer m_iAmbeSrcPtr = (m_iAmbeSrcPtr + 1) % m_AmbeSrc.size(); + m_uiNbTotalPacketSent++; m_uiNbPacketSent++; } } @@ -283,15 +287,19 @@ void CCodecStream::ResetStats(void) void CCodecStream::DisplayStats(void) { - //double fps = (double)m_uiNbPacketSent / m_StatsTimer.DurationSinceNow(); - double fps = (double)m_uiNbPacketReceived / m_StatsTimer.DurationSinceNow(); + // get stats + uint32 uiSent = m_uiNbPacketSent; + uint32 uiReceived = m_uiNbPacketReceived; + uint32 uiBad = m_uiNbPacketBad; + double fps = (double)uiReceived / m_StatsTimer.DurationSinceNow(); - std::cout << "Stream " << (int)m_uiStreamId << " (" << (int)m_uiCodecIn << "->" << (int)m_uiCodecOut << ") : "; - std::cout << m_uiNbPacketSent << " / " << m_uiNbPacketReceived << " / " << m_uiNbPacketTimeout; - //std::cout << " / " << m_uiNbPacketBad; - std::cout << " ; " << fps << " fps"; + // resets + ResetStats(); - std::cout << std::endl; - - m_uiNbPacketBad = 0; + // displays + char sz[256]; + sprintf(sz, "Stream %d (%d->%d) : %u / %u / %u : %.1f fps", + m_uiStreamId, m_uiCodecIn, m_uiCodecOut, + uiSent, uiReceived, uiBad, fps); + std::cout << sz << std::endl; } diff --git a/ambedtest/ccodecstream.h b/ambedtest/ccodecstream.h index 568e466..4619648 100644 --- a/ambedtest/ccodecstream.h +++ b/ambedtest/ccodecstream.h @@ -99,6 +99,7 @@ protected: std::thread *m_pThread; CTimePoint m_TimeoutTimer; CTimePoint m_FrameTimer; + uint32 m_uiNbTotalPacketSent; // stats CTimePoint m_StatsTimer; diff --git a/ambedtest/makefile b/ambedtest/makefile index bc1a804..c85fe4c 100644 --- a/ambedtest/makefile +++ b/ambedtest/makefile @@ -8,10 +8,10 @@ EXECUTABLE=ambedtest all: $(SOURCES) $(EXECUTABLE) $(EXECUTABLE): $(OBJECTS) - $(CC) $(LDFLAGS) $(OBJECTS) -Wl,-rpath,/usr/local/lib -o $@ + $(CC) $(LDFLAGS) $(OBJECTS) -lftd2xx -Wl,-rpath,/usr/local/lib -o $@ .cpp.o: $(CC) $(CFLAGS) $< -o $@ clean: - $(RM) $(EXECUTABLE) *.o + $(RM) *.o From 3153515d334a2ec90e4aa73e13ca3c07ef03d542 Mon Sep 17 00:00:00 2001 From: phl0 Date: Mon, 13 Nov 2017 08:58:59 +0100 Subject: [PATCH 020/244] Redo overwritten changes --- ambedtest/makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ambedtest/makefile b/ambedtest/makefile index c85fe4c..bc1a804 100644 --- a/ambedtest/makefile +++ b/ambedtest/makefile @@ -8,10 +8,10 @@ EXECUTABLE=ambedtest all: $(SOURCES) $(EXECUTABLE) $(EXECUTABLE): $(OBJECTS) - $(CC) $(LDFLAGS) $(OBJECTS) -lftd2xx -Wl,-rpath,/usr/local/lib -o $@ + $(CC) $(LDFLAGS) $(OBJECTS) -Wl,-rpath,/usr/local/lib -o $@ .cpp.o: $(CC) $(CFLAGS) $< -o $@ clean: - $(RM) *.o + $(RM) $(EXECUTABLE) *.o From c9778017f8929433df45cadc1744f5edbbfd058a Mon Sep 17 00:00:00 2001 From: Stefan Saraev Date: Sun, 26 Nov 2017 14:34:23 +0200 Subject: [PATCH 021/244] allow including an extra config file outside of db/ root --- dashboard/pgs/config.inc.php | 10 ++++++++++ dashboard2/pgs/config.inc.php | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/dashboard/pgs/config.inc.php b/dashboard/pgs/config.inc.php index 2e3df5c..180c778 100755 --- a/dashboard/pgs/config.inc.php +++ b/dashboard/pgs/config.inc.php @@ -63,4 +63,14 @@ $CallingHome['HashFile'] = "/tmp/callinghome.php"; $CallingHome['OverrideIPAddress'] = ""; // Insert your IP address here. Leave blank for autodetection. No need to enter a fake address. $CallingHome['InterlinkFile'] = "/xlxd/xlxd.interlink"; // Path to interlink file + +/* + include an extra config file for people who dont like to mess with shipped config.ing.php + this makes updating dashboard from git a little bit easier +*/ + +if (file_exists("../config.inc.php")) { + include ("../config.inc.php"); +} + ?> diff --git a/dashboard2/pgs/config.inc.php b/dashboard2/pgs/config.inc.php index c841796..b17f3fb 100644 --- a/dashboard2/pgs/config.inc.php +++ b/dashboard2/pgs/config.inc.php @@ -63,4 +63,14 @@ $CallingHome['HashFile'] = "/tmp/callinghome.php"; $CallingHome['OverrideIPAddress'] = ""; // Insert your IP address here. Leave blank for autodetection. No need to enter a fake address. $CallingHome['InterlinkFile'] = "/xlxd/xlxd.interlink"; // Path to interlink file + +/* + include an extra config file for people who dont like to mess with shipped config.ing.php + this makes updating dashboard from git a little bit easier +*/ + +if (file_exists("../config.inc.php")) { + include ("../config.inc.php"); +} + ?> From 8ea9385d560cf08553566837cac82c55bcddd7b8 Mon Sep 17 00:00:00 2001 From: LX1IQ Date: Tue, 19 Dec 2017 11:16:04 +0100 Subject: [PATCH 022/244] v2.3.8 add support for network traffic statistics via vnstat. --- dashboard/changes.txt | 12 ++++++++- dashboard/index.php | 63 ++++++++++++++++++++++--------------------- 2 files changed, 43 insertions(+), 32 deletions(-) diff --git a/dashboard/changes.txt b/dashboard/changes.txt index 727e0b5..2bba828 100755 --- a/dashboard/changes.txt +++ b/dashboard/changes.txt @@ -1,6 +1,16 @@ +xlx db v2.3.8 + +add support for network traffic statistics via vnstat. + +- "config.inc.php" +- "index.php" +- "functions.php" + +add traffic.php + xlx db v2.3.7 -add background button color change on active page. +add background color change on active page. - "config.inc.php" - "layout.css" diff --git a/dashboard/index.php b/dashboard/index.php index 6509be5..f64d1ab 100755 --- a/dashboard/index.php +++ b/dashboard/index.php @@ -2,8 +2,8 @@ session_start(); -if (file_exists("./pgs/functions.php")) { require_once("./pgs/functions.php"); } else { die("functions.php does not exist."); } -if (file_exists("./pgs/config.inc.php")) { require_once("./pgs/config.inc.php"); } else { die("config.inc.php does not exist."); } +if (file_exists("./pgs/functions.php")) { require_once("./pgs/functions.php"); } else { die("functions.php does not exist."); } +if (file_exists("./pgs/config.inc.php")) { require_once("./pgs/config.inc.php"); } else { die("config.inc.php does not exist."); } if (!class_exists('ParseXML')) require_once("./pgs/class.parsexml.php"); if (!class_exists('Node')) require_once("./pgs/class.node.php"); @@ -12,6 +12,8 @@ if (!class_exists('Station')) require_once("./pgs/class.station.php"); if (!class_exists('Peer')) require_once("./pgs/class.peer.php"); if (!class_exists('Interlink')) require_once("./pgs/class.interlink.php"); +setlocale(LC_ALL, $VNStat['Locale']); + $Reflector = new xReflector(); $Reflector->SetFlagFile("./pgs/country.csv"); $Reflector->SetPIDFile($Service['PIDFile']); @@ -19,38 +21,23 @@ $Reflector->SetXMLFile($Service['XMLFile']); $Reflector->LoadXML(); -if ($CallingHome['Active']) { - +if ($CallingHome['Active']) { + $CallHomeNow = false; if (!file_exists($CallingHome['HashFile'])) { $Hash = CreateCode(16); $LastSync = 0; - $Ressource = @fopen($CallingHome['HashFile'],"w"); - if ($Ressource) { - @fwrite($Ressource, "'); - @fclose($Ressource); - @exec("chmod 777 ".$CallingHome['HashFile']); - $CallHomeNow = true; - } + UpdateHashFile($CallingHome['HashFile'], $LastSync, $Hash); + $CallHomeNow = true; } else { include($CallingHome['HashFile']); - if ($LastSync < (time() - $CallingHome['PushDelay'])) { - $Ressource = @fopen($CallingHome['HashFile'],"w"); - if ($Ressource) { - @fwrite($Ressource, "'); - @fclose($Ressource); - } + if ($LastSync < (time() - $CallingHome['PushDelay'])) { + UpdateHashFile($CallingHome['HashFile'], time(), $Hash); $CallHomeNow = true; } } - + if ($CallHomeNow || isset($_GET['callhome'])) { $Reflector->SetCallingHome($CallingHome, $Hash); $Reflector->ReadInterlinkFile(); @@ -58,14 +45,13 @@ if ($CallingHome['Active']) { $Reflector->PrepareReflectorXML(); $Reflector->CallHome(); } + } else { $Hash = ""; } - - ?> @@ -76,7 +62,7 @@ else { - + <?php echo $Reflector->GetReflectorName(); ?> Reflector Dashboard @@ -85,8 +71,8 @@ else { if ($PageOptions['PageRefreshActive']) { echo ' '; } - + if (!isset($_GET['show'])) $_GET['show'] = ""; ?> @@ -118,7 +104,21 @@ else {
+ + + '; + if (($_SESSION['FilterModule'] != null) || ($_SESSION['FilterCallSign'] != null) || ($_SESSION['FilterProtocol'] != null)) { + echo ' + '; + } + echo ' + + +
+
+ + + +
+
Disable filters +
+ + + +
+
+
+ + + +
+
+
# Flag
'.($i+1).''; - list ($Flag, $Name) = $Reflector->GetFlag($Reflector->Nodes[$i]->GetCallSign()); - if (file_exists("./img/flags/".$Flag.".png")) { - echo ''.$Name.''.$Name.''; - } - echo 'Nodes[$i]->GetSuffix(); - echo '" class="pl" target="_blank">'.$Reflector->Nodes[$i]->GetCallSign(); - if ($Reflector->Nodes[$i]->GetSuffix() != "") { echo '-'.$Reflector->Nodes[$i]->GetSuffix(); } - echo ''; - if (($Reflector->Nodes[$i]->GetPrefix() == 'REF') || ($Reflector->Nodes[$i]->GetPrefix() == 'XRF')) { - switch ($Reflector->Nodes[$i]->GetPrefix()) { - case 'REF' : echo 'REF-Link'; break; - case 'XRF' : echo 'XRF-Link'; break; + $ShowThisStation = true; + if ($PageOptions['UserPage']['ShowFilter']) { + $CS = true; + if ($_SESSION['FilterCallSign'] != null) { + if (!fnmatch($_SESSION['FilterCallSign'], $Reflector->Nodes[$i]->GetCallSign(), FNM_CASEFOLD)) { + $CS = false; + } } - } - else { - switch ($Reflector->Nodes[$i]->GetSuffix()) { - case 'A' : echo '23cm'; break; - case 'B' : echo '70cm'; break; - case 'C' : echo '2m'; break; - case 'D' : echo 'Dongle'; break; - case 'G' : echo 'Internet-Gateway'; break; - default : echo ''; + $MO = true; + if ($_SESSION['FilterModule'] != null) { + if (trim(strtolower($_SESSION['FilterModule'])) != strtolower($Reflector->Nodes[$i]->GetLinkedModule())) { + $MO = false; + } + } + $PR = true; + if ($_SESSION['FilterProtocol'] != null) { + if (trim(strtolower($_SESSION['FilterProtocol'])) != strtolower($Reflector->Nodes[$i]->GetProtocol())) { + $PR = false; + } } + + $ShowThisStation = ($CS && $MO && $PR); } - echo ''.date("d.m.Y H:i", $Reflector->Nodes[$i]->GetLastHeardTime()).''.FormatSeconds(time()-$Reflector->Nodes[$i]->GetConnectTime()).' s'.$Reflector->Nodes[$i]->GetProtocol().''.$Reflector->Nodes[$i]->GetLinkedModule().''; - $Bytes = explode(".", $Reflector->Nodes[$i]->GetIP()); - if ($Bytes !== false && count($Bytes) == 4) { - switch ($PageOptions['RepeatersPage']['IPModus']) { - case 'ShowLast1ByteOfIP' : echo $PageOptions['RepeatersPage']['MasqueradeCharacter'].'.'.$PageOptions['RepeatersPage']['MasqueradeCharacter'].'.'.$PageOptions['RepeatersPage']['MasqueradeCharacter'].'.'.$Bytes[3]; break; - case 'ShowLast2ByteOfIP' : echo $PageOptions['RepeatersPage']['MasqueradeCharacter'].'.'.$PageOptions['RepeatersPage']['MasqueradeCharacter'].'.'.$Bytes[2].'.'.$Bytes[3]; break; - case 'ShowLast3ByteOfIP' : echo $PageOptions['RepeatersPage']['MasqueradeCharacter'].'.'.$Bytes[1].'.'.$Bytes[2].'.'.$Bytes[3]; break; - default : echo $Reflector->Nodes[$i]->GetIP(); +
'.($i+1).''; + list ($Flag, $Name) = $Reflector->GetFlag($Reflector->Nodes[$i]->GetCallSign()); + if (file_exists("./img/flags/".$Flag.".png")) { + echo ''.$Name.''.$Name.''; + } + echo 'Nodes[$i]->GetSuffix(); + echo '" class="pl" target="_blank">'.$Reflector->Nodes[$i]->GetCallSign(); + if ($Reflector->Nodes[$i]->GetSuffix() != "") { echo '-'.$Reflector->Nodes[$i]->GetSuffix(); } + echo ''; + if (($Reflector->Nodes[$i]->GetPrefix() == 'REF') || ($Reflector->Nodes[$i]->GetPrefix() == 'XRF')) { + switch ($Reflector->Nodes[$i]->GetPrefix()) { + case 'REF' : echo 'REF-Link'; break; + case 'XRF' : echo 'XRF-Link'; break; } } - echo ''.date("d.m.Y H:i", $Reflector->Nodes[$i]->GetLastHeardTime()).''.FormatSeconds(time()-$Reflector->Nodes[$i]->GetConnectTime()).' s'.$Reflector->Nodes[$i]->GetProtocol().''.$Reflector->Nodes[$i]->GetLinkedModule().''; + $Bytes = explode(".", $Reflector->Nodes[$i]->GetIP()); + if ($Bytes !== false && count($Bytes) == 4) { + switch ($PageOptions['RepeatersPage']['IPModus']) { + case 'ShowLast1ByteOfIP' : echo $PageOptions['RepeatersPage']['MasqueradeCharacter'].'.'.$PageOptions['RepeatersPage']['MasqueradeCharacter'].'.'.$PageOptions['RepeatersPage']['MasqueradeCharacter'].'.'.$Bytes[3]; break; + case 'ShowLast2ByteOfIP' : echo $PageOptions['RepeatersPage']['MasqueradeCharacter'].'.'.$PageOptions['RepeatersPage']['MasqueradeCharacter'].'.'.$Bytes[2].'.'.$Bytes[3]; break; + case 'ShowLast3ByteOfIP' : echo $PageOptions['RepeatersPage']['MasqueradeCharacter'].'.'.$Bytes[1].'.'.$Bytes[2].'.'.$Bytes[3]; break; + default : echo $Reflector->Nodes[$i]->GetIP(); + } + } + echo '
- + + + + + + Traffic'; + } + + ?>
Users / ModulesRepeaters / Nodes (NodeCount(); ?>)Peers (PeerCount(); ?>)ReflectorlistD-Star liveUsers / ModulesRepeaters / Nodes (NodeCount(); ?>)Peers (PeerCount(); ?>)ReflectorlistircDDB live
@@ -141,6 +141,7 @@ else { case 'liveircddb' : require_once("./pgs/liveircddb.php"); break; case 'peers' : require_once("./pgs/peers.php"); break; case 'reflectors' : require_once("./pgs/reflectors.php"); break; + case 'traffic' : require_once("./pgs/traffic.php"); break; default : require_once("./pgs/users.php"); } From f696a5f7fde2fd23fdd08103d36dddde58ca0869 Mon Sep 17 00:00:00 2001 From: LX1IQ Date: Tue, 19 Dec 2017 11:17:06 +0100 Subject: [PATCH 023/244] v2.3.8 add support for network traffic statistics via vnstat. --- dashboard/pgs/config.inc.php | 47 ++++++------- dashboard/pgs/functions.php | 129 +++++++++++++++++++++++++++++++++++ dashboard/pgs/traffic.php | 102 +++++++++++++++++++++++++++ 3 files changed, 252 insertions(+), 26 deletions(-) create mode 100644 dashboard/pgs/traffic.php diff --git a/dashboard/pgs/config.inc.php b/dashboard/pgs/config.inc.php index 180c778..2c69ed5 100755 --- a/dashboard/pgs/config.inc.php +++ b/dashboard/pgs/config.inc.php @@ -13,35 +13,33 @@ ShowLast3ByteOfIP $Service = array(); $CallingHome = array(); $PageOptions = array(); +$VNStat = array(); -$PageOptions['ContactEmail'] = 'your_email'; // Support E-Mail address +$PageOptions['ContactEmail'] = 'your_mail'; // Support E-Mail address -$PageOptions['DashboardVersion'] = '2.3.7'; // Dashboard Version - -$PageOptions['PageRefreshActive'] = true; // Activate automatic refresh -$PageOptions['PageRefreshDelay'] = '10000'; // Page refresh time in miliseconds +$PageOptions['DashboardVersion'] = '2.3.8'; // Dashboard Version +$PageOptions['PageRefreshActive'] = true; // Activate automatic refresh +$PageOptions['PageRefreshDelay'] = '10000'; // Page refresh time in miliseconds $PageOptions['RepeatersPage'] = array(); -$PageOptions['RepeatersPage']['LimitTo'] = 99; // Number of Repeaters to show -$PageOptions['RepeatersPage']['IPModus'] = 'ShowFullIP'; // See possible options above -$PageOptions['RepeatersPage']['MasqueradeCharacter'] = '*'; // Character used for masquerade - +$PageOptions['RepeatersPage']['LimitTo'] = 99; // Number of Repeaters to show +$PageOptions['RepeatersPage']['IPModus'] = 'ShowFullIP'; // See possible options above +$PageOptions['RepeatersPage']['MasqueradeCharacter'] = '*'; // Character used for masquerade $PageOptions['PeerPage'] = array(); -$PageOptions['PeerPage']['LimitTo'] = 99; // Number of peers to show -$PageOptions['PeerPage']['IPModus'] = 'ShowFullIP'; // See possible options above -$PageOptions['PeerPage']['MasqueradeCharacter'] = '*'; // Character used for masquerade +$PageOptions['PeerPage']['LimitTo'] = 99; // Number of peers to show +$PageOptions['PeerPage']['IPModus'] = 'ShowFullIP'; // See possible options above +$PageOptions['PeerPage']['MasqueradeCharacter'] = '*'; // Character used for masquerade $PageOptions['LastHeardPage']['LimitTo'] = 39; // Number of stations to show -$PageOptions['ModuleNames'] = array(); // Module nomination +$PageOptions['ModuleNames'] = array(); // Module nomination $PageOptions['ModuleNames']['A'] = 'Int.'; $PageOptions['ModuleNames']['B'] = 'Regional'; $PageOptions['ModuleNames']['C'] = 'National'; $PageOptions['ModuleNames']['D'] = ''; - $PageOptions['MetaDescription'] = 'XLX is a D-Star Reflector System for Ham Radio Operators.'; // Meta Tag Values, usefull for Search Engine $PageOptions['MetaKeywords'] = 'Ham Radio, D-Star, XReflector, XLX, XRF, DCS, REF, '; // Meta Tag Values, usefull forSearch Engine $PageOptions['MetaAuthor'] = 'LX1IQ'; // Meta Tag Values, usefull for Search Engine @@ -50,27 +48,24 @@ $PageOptions['MetaRobots'] = 'index,follow'; $PageOptions['UserPage']['ShowFilter'] = true; // Show Filter on Users page +$PageOptions['Traffic']['Show'] = false; // Enable vnstat traffic statistics + $Service['PIDFile'] = '/var/log/xlxd.pid'; $Service['XMLFile'] = '/var/log/xlxd.xml'; -$CallingHome['Active'] = false; // xlx phone home, true or false -$CallingHome['MyDashBoardURL'] = 'http://your_dashboard'; // dashboard url +$CallingHome['Active'] = false; // xlx phone home, true or false +$CallingHome['MyDashBoardURL'] = 'http://your_dashboard'; // dashboard url $CallingHome['ServerURL'] = 'http://xlxapi.rlx.lu/api.php'; // database server, do not change !!!! $CallingHome['PushDelay'] = 600; // push delay in seconds $CallingHome['Country'] = "your_country"; // Country -$CallingHome['Comment'] = "your_comment"; // Comment. Max 100 character +$CallingHome['Comment'] = "your_comment"; // Comment. Max 100 character $CallingHome['HashFile'] = "/tmp/callinghome.php"; // Make sure the apache user has read and write permissions in this folder. $CallingHome['OverrideIPAddress'] = ""; // Insert your IP address here. Leave blank for autodetection. No need to enter a fake address. $CallingHome['InterlinkFile'] = "/xlxd/xlxd.interlink"; // Path to interlink file - -/* - include an extra config file for people who dont like to mess with shipped config.ing.php - this makes updating dashboard from git a little bit easier -*/ - -if (file_exists("../config.inc.php")) { - include ("../config.inc.php"); -} +$VNStat['Interfaces'] = array(); +$VNStat['Interfaces'][0]['Name'] = 'eth0'; +$VNStat['Interfaces'][0]['Address'] = 'eth0'; +$VNStat['Binary'] = '/usr/bin/vnstat'; ?> diff --git a/dashboard/pgs/functions.php b/dashboard/pgs/functions.php index 353b2d3..ea68472 100755 --- a/dashboard/pgs/functions.php +++ b/dashboard/pgs/functions.php @@ -60,4 +60,133 @@ function CreateCode ($laenge) { return $out; } + +function UpdateHashFile($HashFile, $newLastSync, $newHash) { + if (version_compare(phpversion(), "5.6", ">=")) { + $Ressource = @fopen($HashFile, "c"); + if ($Ressource) { + if (flock($Ressource, LOCK_EX)) { + @fwrite($Ressource, "'); + + @fflush($Ressource); + @ftruncate($Ressource, ftell($Ressource)); + @flock($Ressource, LOCK_UN); + } + @fclose($Ressource); + @chmod($HashFile, 0777); + return true; + } + } + else { + $Ressource = @fopen($HashFile, "w"); + if ($Ressource) { + @fwrite($Ressource, "'); + @fclose($Ressource); + @exec("chmod 777 ".$CallingHome['HashFile']); + $CallHomeNow = true; + } + } + + return false; +} + +function VNStatLocalize($str) { + global $L; + if (isset($L[$str])) { + return $L[$str]; + } + else { + return $str; + } +} + +function VNStatGetData($iface, $vnstat_bin) { + $vnstat_data = array(); + + $fd = @popen("$vnstat_bin --dumpdb -i $iface", "r"); + if (is_resource($fd)) { + $buffer = ''; + while (!feof($fd)) { + $buffer .= fgets($fd); + } + $vnstat_data = explode("\n", $buffer); + pclose($fd); + } + + $day = array(); + $hour = array(); + $month = array(); + $top = array(); + + if (isset($vnstat_data[0]) && strpos($vnstat_data[0], 'Error') !== false) { + return; + } + + foreach($vnstat_data as $line) { + $d = explode(';', trim($line)); + if ($d[0] == 'd') { + $day[$d[1]]['time'] = $d[2]; + $day[$d[1]]['rx'] = $d[3] * 1024 + $d[5]; + $day[$d[1]]['tx'] = $d[4] * 1024 + $d[6]; + $day[$d[1]]['act'] = $d[7]; + $day[$d[1]]['rx2'] = $d[5]; + $day[$d[1]]['tx2'] = $d[6]; + } + else if ($d[0] == 'm') { + $month[$d[1]]['time'] = $d[2]; + $month[$d[1]]['rx'] = $d[3] * 1024 + $d[5]; + $month[$d[1]]['tx'] = $d[4] * 1024 + $d[6]; + $month[$d[1]]['act'] = $d[7]; + $month[$d[1]]['rx2'] = $d[5]; + $month[$d[1]]['tx2'] = $d[6]; + } + else if ($d[0] == 'h') { + $hour[$d[1]]['time'] = $d[2]; + $hour[$d[1]]['rx'] = $d[3]; + $hour[$d[1]]['tx'] = $d[4]; + $hour[$d[1]]['act'] = 1; + } + else if ($d[0] == 't') { + $top[$d[1]]['time'] = $d[2]; + $top[$d[1]]['rx'] = $d[3] * 1024 + $d[5]; + $top[$d[1]]['tx'] = $d[4] * 1024 + $d[6]; + $top[$d[1]]['act'] = $d[7]; + } + else { + $summary[$d[0]] = isset($d[1]) ? $d[1] : ''; + } + } + + rsort($day); + rsort($month); + rsort($hour); + + return array($day, $month, $hour, $day, $month, $top, $summary); +} + + +function kbytes_to_string($kb) { + $byte_notation = null; + $units = array('TB','GB','MB','KB'); + $scale = 1024*1024*1024; + $ui = 0; + $custom_size = isset($byte_notation) && in_array($byte_notation, $units); + + while ((($kb < $scale) && ($scale > 1)) || $custom_size) { + $ui++; + $scale = $scale / 1024; + if ($custom_size && $units[$ui] == $byte_notation) { + break; + } + } + return sprintf("%0.2f %s", ($kb/$scale),$units[$ui]); +} + + ?> \ No newline at end of file diff --git a/dashboard/pgs/traffic.php b/dashboard/pgs/traffic.php new file mode 100644 index 0000000..7b57ff2 --- /dev/null +++ b/dashboard/pgs/traffic.php @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + '; + +for ($i=0;$i 0) { + echo ' + + + + + + + '; + } +} + +echo '
Network interfacesStatistics
'.$VNStat['Interfaces'][$i]['Name'].''; + if ($i < count($VNStat['Interfaces'])) { + echo '
'; + } + } + + ?>
+
DayRXTXAvg RxAvg TX
'.date("d.m.Y", $Data[0][$i]['time']).''.kbytes_to_string($Data[0][$i]['rx']).''.kbytes_to_string($Data[0][$i]['tx']).''.kbytes_to_string($Data[0][$i]['rx2']).''.kbytes_to_string($Data[0][$i]['tx2']).'
'; + + + + +echo ' +
+ + + + + + + '; + +for ($i=0;$i 0) { + echo ' + + + + + + + '; + } +} + +echo '
MonthRXTXAvg RxAvg TX
'.date("F", $Data[1][$i]['time']).''.kbytes_to_string($Data[1][$i]['rx']).''.kbytes_to_string($Data[1][$i]['tx']).''.kbytes_to_string($Data[1][$i]['rx2']).''.kbytes_to_string($Data[1][$i]['tx2']).'
'; +?> + + +
\ No newline at end of file From 146dbc70f28081c0b493da91840afc932975c5b0 Mon Sep 17 00:00:00 2001 From: phl0 Date: Wed, 20 Dec 2017 00:00:48 +0100 Subject: [PATCH 024/244] Fix obiously missing slashes to table tags --- dashboard/pgs/traffic.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dashboard/pgs/traffic.php b/dashboard/pgs/traffic.php index 7b57ff2..46f62e2 100644 --- a/dashboard/pgs/traffic.php +++ b/dashboard/pgs/traffic.php @@ -67,7 +67,7 @@ for ($i=0;$i'; +echo '
'; @@ -95,8 +95,8 @@ for ($i=0;$i'; +echo ''; ?> - \ No newline at end of file + From ed24a5b8cb15aa42fc9ce89cc421014ff8c9ff21 Mon Sep 17 00:00:00 2001 From: LX1IQ Date: Wed, 20 Dec 2017 11:24:38 +0100 Subject: [PATCH 025/244] v2.3.8 put back the support for a second config file in the root, removed by mistake. 73, Luc --- dashboard/pgs/config.inc.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/dashboard/pgs/config.inc.php b/dashboard/pgs/config.inc.php index 2c69ed5..6c8e513 100755 --- a/dashboard/pgs/config.inc.php +++ b/dashboard/pgs/config.inc.php @@ -15,7 +15,7 @@ $CallingHome = array(); $PageOptions = array(); $VNStat = array(); -$PageOptions['ContactEmail'] = 'your_mail'; // Support E-Mail address +$PageOptions['ContactEmail'] = 'your_email'; // Support E-Mail address $PageOptions['DashboardVersion'] = '2.3.8'; // Dashboard Version @@ -68,4 +68,14 @@ $VNStat['Interfaces'][0]['Name'] = 'eth0'; $VNStat['Interfaces'][0]['Address'] = 'eth0'; $VNStat['Binary'] = '/usr/bin/vnstat'; +/* + include an extra config file for people who dont like to mess with shipped config.ing.php + this makes updating dashboard from git a little bit easier +*/ + + if (file_exists("../config.inc.php")) { + include ("../config.inc.php"); + } + + ?> From 32a437c138ddf97440790bd67a70e9a7b15d6698 Mon Sep 17 00:00:00 2001 From: LX3JL Date: Fri, 12 Jan 2018 09:30:59 +0100 Subject: [PATCH 026/244] xlxd 2.2.1 * auto-reload of deride DB * interlink protocol v2.0 --- src/ccallsign.cpp | 49 +++++-- src/ccallsignlist.cpp | 2 +- src/cdmriddir.cpp | 274 ++++++++++--------------------------- src/cdmriddir.h | 37 +++-- src/cdmriddirfile.cpp | 165 ++++++++++++++++++++++ src/cdmriddirfile.h | 59 ++++++++ src/cdmriddirhttp.cpp | 182 ++++++++++++++++++++++++ src/cdmriddirhttp.h | 52 +++++++ src/cdvframepacket.cpp | 14 ++ src/cdvframepacket.h | 1 + src/cdvlastframepacket.cpp | 10 ++ src/cdvlastframepacket.h | 1 + src/cpacket.cpp | 10 ++ src/cpacket.h | 1 + src/cpeer.cpp | 5 +- src/cpeer.h | 4 +- src/creflector.cpp | 5 +- src/ctranscoder.cpp | 6 - src/ctranscoder.h | 3 + src/cversion.cpp | 87 ++++++++++++ src/cversion.h | 64 +++++++++ src/cxlxclient.cpp | 26 +++- src/cxlxclient.h | 12 +- src/cxlxpeer.cpp | 28 +++- src/cxlxpeer.h | 10 +- src/cxlxprotocol.cpp | 190 +++++++++++++++++++++---- src/cxlxprotocol.h | 26 ++-- src/main.cpp | 3 + src/main.h | 18 ++- 29 files changed, 1059 insertions(+), 285 deletions(-) create mode 100644 src/cdmriddirfile.cpp create mode 100644 src/cdmriddirfile.h create mode 100644 src/cdmriddirhttp.cpp create mode 100644 src/cdmriddirhttp.h create mode 100644 src/cversion.cpp create mode 100644 src/cversion.h diff --git a/src/ccallsign.cpp b/src/ccallsign.cpp index af04e58..23cdb3e 100644 --- a/src/ccallsign.cpp +++ b/src/ccallsign.cpp @@ -25,7 +25,8 @@ #include "main.h" #include #include -#include "cdmriddir.h" +#include "cdmriddirfile.h" +#include "cdmriddirhttp.h" #include "ccallsign.h" //////////////////////////////////////////////////////////////////////////////////////// @@ -60,16 +61,24 @@ CCallsign::CCallsign(const char *sz, uint32 dmrid) // dmrid ok ? if ( m_uiDmrid == 0 ) { - m_uiDmrid = g_DmridDir.FindDmrid(*this); + g_DmridDir.Lock(); + { + m_uiDmrid = g_DmridDir.FindDmrid(*this); + } + g_DmridDir.Unlock(); } } else if ( m_uiDmrid != 0 ) { - const CCallsign *callsign = g_DmridDir.FindCallsign(m_uiDmrid); - if ( callsign != NULL ) - { - ::memcpy(m_Callsign, callsign->m_Callsign, sizeof(m_Callsign)); - } + g_DmridDir.Lock(); + { + const CCallsign *callsign = g_DmridDir.FindCallsign(m_uiDmrid); + if ( callsign != NULL ) + { + ::memcpy(m_Callsign, callsign->m_Callsign, sizeof(m_Callsign)); + } + } + g_DmridDir.Unlock(); } } @@ -151,7 +160,11 @@ void CCallsign::SetCallsign(const char *sz, bool UpdateDmrid) // and update dmrid if ( UpdateDmrid ) { - m_uiDmrid = g_DmridDir.FindDmrid(*this); + g_DmridDir.Lock(); + { + m_uiDmrid = g_DmridDir.FindDmrid(*this); + } + g_DmridDir.Unlock(); } } @@ -174,7 +187,11 @@ void CCallsign::SetCallsign(const uint8 *buffer, int len, bool UpdateDmrid) } if ( UpdateDmrid ) { - m_uiDmrid = g_DmridDir.FindDmrid(*this); + g_DmridDir.Lock(); + { + m_uiDmrid = g_DmridDir.FindDmrid(*this); + } + g_DmridDir.Unlock(); } } @@ -183,11 +200,15 @@ void CCallsign::SetDmrid(uint32 dmrid, bool UpdateCallsign) m_uiDmrid = dmrid; if ( UpdateCallsign ) { - const CCallsign *callsign = g_DmridDir.FindCallsign(dmrid); - if ( callsign != NULL ) - { - ::memcpy(m_Callsign, callsign->m_Callsign, sizeof(m_Callsign)); - } + g_DmridDir.Lock(); + { + const CCallsign *callsign = g_DmridDir.FindCallsign(dmrid); + if ( callsign != NULL ) + { + ::memcpy(m_Callsign, callsign->m_Callsign, sizeof(m_Callsign)); + } + } + g_DmridDir.Unlock(); } } diff --git a/src/ccallsignlist.cpp b/src/ccallsignlist.cpp index 5d68b93..dfd9d35 100644 --- a/src/ccallsignlist.cpp +++ b/src/ccallsignlist.cpp @@ -34,7 +34,7 @@ CCallsignList::CCallsignList() { m_Filename = NULL; - ::memset(&m_LastModTime, 0, sizeof(CCallsignList)); + ::memset(&m_LastModTime, 0, sizeof(time_t)); } //////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/cdmriddir.cpp b/src/cdmriddir.cpp index 21f9347..5d62db3 100644 --- a/src/cdmriddir.cpp +++ b/src/cdmriddir.cpp @@ -26,246 +26,120 @@ #include "main.h" #include "creflector.h" #include "cdmriddir.h" - - -CDmridDir g_DmridDir; +#include "cdmriddirfile.h" +#include "cdmriddirhttp.h" //////////////////////////////////////////////////////////////////////////////////////// -// find +// constructor & destructor -const CCallsign *CDmridDir::FindCallsign(uint32 dmrid) +CDmridDir::CDmridDir() { - auto found = m_CallsignMap.find(dmrid); - if ( found != m_CallsignMap.end() ) + m_bStopThread = false; + m_pThread = NULL; +} + +CDmridDir::~CDmridDir() +{ + // kill threads + m_bStopThread = true; + if ( m_pThread != NULL ) { - return &(found->second); + m_pThread->join(); + delete m_pThread; } - return NULL; } -uint32 CDmridDir::FindDmrid(const CCallsign &callsign) + +//////////////////////////////////////////////////////////////////////////////////////// +// init & close + +bool CDmridDir::Init(void) { - auto found = m_DmridMap.find(callsign); - if ( found != m_DmridMap.end() ) + // load content + Reload(); + + // reset stop flag + m_bStopThread = false; + + // start thread; + m_pThread = new std::thread(CDmridDir::Thread, this); + + return true; +} + +void CDmridDir::Close(void) +{ + m_bStopThread = true; + if ( m_pThread != NULL ) { - return (found->second); + m_pThread->join(); + delete m_pThread; + m_pThread = NULL; } - return 0; } //////////////////////////////////////////////////////////////////////////////////////// -// refresh +// thread -#if (DMRIDDB_USE_RLX_SERVER == 1) -bool CDmridDir::RefreshContent(void) +void CDmridDir::Thread(CDmridDir *This) { - bool ok = true; - CBuffer buffer; - - // get file from xlxapi server - if ( (ok = HttpGet("xlxapi.rlx.lu", "api/exportdmr.php", 80, &buffer)) ) + while ( !This->m_bStopThread ) { - // clear directory - m_CallsignMap.clear(); - m_DmridMap.clear(); + // Wait 30 seconds + CTimePoint::TaskSleepFor(DMRIDDB_REFRESH_RATE * 60000); - // scan file - if ( buffer.size() > 0 ) + // have lists files changed ? + if ( This->NeedReload() ) { - char *ptr1 = (char *)buffer.data(); - char *ptr2; - // get next line - while ( (ptr2 = ::strchr(ptr1, '\n')) != NULL ) - { - *ptr2 = 0; - // get items - char *dmrid; - char *callsign; - if ( ((dmrid = ::strtok(ptr1, ";")) != NULL) && IsValidDmrid(dmrid) ) - { - if ( ((callsign = ::strtok(NULL, ";")) != NULL) ) - { - // new entry - uint32 ui = atoi(dmrid); - CCallsign cs(callsign, ui); - if ( cs.IsValid() ) - { - m_CallsignMap.insert(std::pair(ui, cs)); - m_DmridMap.insert(std::pair(cs,ui)); - } - } - } - // next line - ptr1 = ptr2+1; - } - // done - ok = true; + This->Reload(); } - } - - // report - std::cout << "Read " << m_DmridMap.size() << " DMR id from online database " << std::endl; - - // done - return ok; + } } -#else -bool CDmridDir::RefreshContent(void) + +//////////////////////////////////////////////////////////////////////////////////////// +// Reload + +bool CDmridDir::Reload(void) { - bool ok = true; CBuffer buffer; - std::ifstream file; - std::streampos size; + bool ok = false; - // open file - file.open(DMRIDDB_PATH, std::ios::in | std::ios::binary | std::ios::ate); - if ( file.is_open() ) + if ( LoadContent(&buffer) ) { - // clear directory - m_CallsignMap.clear(); - m_DmridMap.clear(); - - // scan file - size = file.tellg(); - if ( size > 0 ) + Lock(); { - // read file into buffer - buffer.resize((int)size+1); - file.seekg (0, std::ios::beg); - file.read((char *)buffer.data(), (int)size); - - // close file - file.close(); - - // crack it - char *ptr1 = (char *)buffer.data(); - char *ptr2; - // get next line - while ( (ptr2 = ::strchr(ptr1, '\n')) != NULL ) - { - *ptr2 = 0; - // get items - char *dmrid; - char *callsign; - if ( ((dmrid = ::strtok(ptr1, ";")) != NULL) && IsValidDmrid(dmrid) ) - { - if ( ((callsign = ::strtok(NULL, ";")) != NULL) ) - { - // new entry - uint32 ui = atoi(dmrid); - CCallsign cs(callsign, ui); - if ( cs.IsValid() ) - { - m_CallsignMap.insert(std::pair(ui, cs)); - m_DmridMap.insert(std::pair(cs,ui)); - } - } - } - // next line - ptr1 = ptr2+1; - } - // done - ok = true; + ok = RefreshContent(buffer); } + Unlock(); } - - // report - std::cout << "Read " << m_DmridMap.size() << " DMR id from online database " << std::endl; - - // done return ok; } -#endif -//////////////////////////////////////////////////////////////////////////////////////// -// httpd helpers -#define DMRID_HTTPGET_SIZEMAX (256) -#define DMRID_TEXTFILE_SIZEMAX (10*1024*1024) +//////////////////////////////////////////////////////////////////////////////////////// +// find -bool CDmridDir::HttpGet(const char *hostname, const char *filename, int port, CBuffer *buffer) +const CCallsign *CDmridDir::FindCallsign(uint32 dmrid) { - bool ok = false; - int sock_id; - - // open socket - if ( (sock_id = ::socket(AF_INET, SOCK_STREAM, 0)) >= 0 ) + auto found = m_CallsignMap.find(dmrid); + if ( found != m_CallsignMap.end() ) { - // get hostname address - struct sockaddr_in servaddr; - struct hostent *hp; - ::memset(&servaddr,0,sizeof(servaddr)); - if( (hp = gethostbyname(hostname)) != NULL ) - { - // dns resolved - ::memcpy((char *)&servaddr.sin_addr.s_addr, (char *)hp->h_addr, hp->h_length); - servaddr.sin_port = htons(port); - servaddr.sin_family = AF_INET; - - // connect - if ( ::connect(sock_id, (struct sockaddr *)&servaddr, sizeof(servaddr)) == 0) - { - // send the GET request - char request[DMRID_HTTPGET_SIZEMAX]; - ::sprintf(request, "GET /%s HTTP/1.0\r\nFrom: %s\r\nUser-Agent: xlxd\r\n\r\n", - filename, (const char *)g_Reflector.GetCallsign()); - ::write(sock_id, request, strlen(request)); - - // config receive timeouts - fd_set read_set; - struct timeval timeout; - timeout.tv_sec = 5; - timeout.tv_usec = 0; - FD_ZERO(&read_set); - FD_SET(sock_id, &read_set); - - // get the reply back - buffer->clear(); - bool done = false; - do - { - char buf[1440]; - ssize_t len = 0; - select(sock_id+1, &read_set, NULL, NULL, &timeout); - //if ( (ret > 0) || ((ret < 0) && (errno == EINPROGRESS)) ) - //if ( ret >= 0 ) - //{ - usleep(5000); - len = read(sock_id, buf, 1440); - if ( len > 0 ) - { - buffer->Append((uint8 *)buf, (int)len); - ok = true; - } - //} - done = (len <= 0); - - } while (!done); - buffer->Append((uint8)0); - - // and disconnect - close(sock_id); - } - else - { - std::cout << "Cannot establish connection with host " << hostname << std::endl; - } - } - else - { - std::cout << "Host " << hostname << " not found" << std::endl; - } - + return &(found->second); } - else + return NULL; +} + +uint32 CDmridDir::FindDmrid(const CCallsign &callsign) +{ + auto found = m_DmridMap.find(callsign); + if ( found != m_DmridMap.end() ) { - std::cout << "Failed to open wget socket" << std::endl; + return (found->second); } - - // done - return ok; + return 0; } + //////////////////////////////////////////////////////////////////////////////////////// // syntax helpers diff --git a/src/cdmriddir.h b/src/cdmriddir.h index 83f25b1..1e0d9ed 100644 --- a/src/cdmriddir.h +++ b/src/cdmriddir.h @@ -32,7 +32,6 @@ #include "cbuffer.h" #include "ccallsign.h" - // compare function for std::map::find struct CallsignCompare @@ -48,31 +47,49 @@ class CDmridDir { public: // constructor - CDmridDir() {} + CDmridDir(); // destructor - ~CDmridDir() {} + ~CDmridDir(); + + // init & close + virtual bool Init(void); + virtual void Close(void); + + // locks + void Lock(void) { m_Mutex.lock(); } + void Unlock(void) { m_Mutex.unlock(); } // refresh - bool RefreshContent(void); + virtual bool LoadContent(CBuffer *) { return false; } + virtual bool RefreshContent(const CBuffer &) { return false; } // find const CCallsign *FindCallsign(uint32); uint32 FindDmrid(const CCallsign &); protected: - // httpd helpers - bool HttpGet(const char *, const char *, int, CBuffer *); - - // syntax helpers + // thread + static void Thread(CDmridDir *); + + // reload helpers + bool Reload(void); + virtual bool NeedReload(void) { return false; } bool IsValidDmrid(const char *); protected: - // directory + // data std::map m_CallsignMap; std::map m_DmridMap; + + // Lock() + std::mutex m_Mutex; + + // thread + bool m_bStopThread; + std::thread *m_pThread; + }; //////////////////////////////////////////////////////////////////////////////////////// - #endif /* cdmriddir_h */ diff --git a/src/cdmriddirfile.cpp b/src/cdmriddirfile.cpp new file mode 100644 index 0000000..0efb26e --- /dev/null +++ b/src/cdmriddirfile.cpp @@ -0,0 +1,165 @@ +// +// cdmriddirfile.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 29/12/2017. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd 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 of the License, or +// (at your option) any later version. +// +// xlxd 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 Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include +#include +#include +#include "main.h" +#include "cdmriddirfile.h" + + +#if (DMRIDDB_USE_RLX_SERVER == 0) +CDmridDirFile g_DmridDir; +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor & destructor + +CDmridDirFile::CDmridDirFile() +{ + ::memset(&m_LastModTime, 0, sizeof(time_t)); +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// init & close + +bool CDmridDirFile::Init(void) +{ + return CDmridDir::Init(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// refresh + +bool CDmridDirFile::NeedReload(void) +{ + bool needReload = false; + + time_t time; + if ( GetLastModTime(&time) ) + { + needReload = time != m_LastModTime; + } + return needReload; +} + +bool CDmridDirFile::LoadContent(CBuffer *buffer) +{ + bool ok = false; + std::ifstream file; + std::streampos size; + + // open file + file.open(DMRIDDB_PATH, std::ios::in | std::ios::binary | std::ios::ate); + if ( file.is_open() ) + { + // read file + size = file.tellg(); + if ( size > 0 ) + { + // read file into buffer + buffer->resize((int)size+1); + file.seekg (0, std::ios::beg); + file.read((char *)buffer->data(), (int)size); + + // close file + file.close(); + + // update time + GetLastModTime(&m_LastModTime); + + // done + ok = true; + } + } + + // done + return ok; +} + +bool CDmridDirFile::RefreshContent(const CBuffer &buffer) +{ + bool ok = false; + + // clear directory + m_CallsignMap.clear(); + m_DmridMap.clear(); + + // scan buffer + if ( buffer.size() > 0 ) + { + // crack it + char *ptr1 = (char *)buffer.data(); + char *ptr2; + + // get next line + while ( (ptr2 = ::strchr(ptr1, '\n')) != NULL ) + { + *ptr2 = 0; + // get items + char *dmrid; + char *callsign; + if ( ((dmrid = ::strtok(ptr1, ";")) != NULL) && IsValidDmrid(dmrid) ) + { + if ( ((callsign = ::strtok(NULL, ";")) != NULL) ) + { + // new entry + uint32 ui = atoi(dmrid); + CCallsign cs(callsign, ui); + if ( cs.IsValid() ) + { + m_CallsignMap.insert(std::pair(ui, cs)); + m_DmridMap.insert(std::pair(cs,ui)); + } + } + } + // next line + ptr1 = ptr2+1; + } + + // done + ok = true; + } + + // report + std::cout << "Read " << m_DmridMap.size() << " DMR id from file " << DMRIDDB_PATH << std::endl; + + // done + return ok; +} + + +bool CDmridDirFile::GetLastModTime(time_t *time) +{ + bool ok = false; + + struct stat fileStat; + if( ::stat(DMRIDDB_PATH, &fileStat) != -1 ) + { + *time = fileStat.st_mtime; + ok = true; + } + return ok; +} diff --git a/src/cdmriddirfile.h b/src/cdmriddirfile.h new file mode 100644 index 0000000..8466068 --- /dev/null +++ b/src/cdmriddirfile.h @@ -0,0 +1,59 @@ +// +// cdmrididirfile.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 29/12/2017. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd 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 of the License, or +// (at your option) any later version. +// +// xlxd 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 Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cdmrididirfile_h +#define cdmrididirfile_h + +#include "cdmriddir.h" + +//////////////////////////////////////////////////////////////////////////////////////// + +class CDmridDirFile : public CDmridDir +{ +public: + // constructor + CDmridDirFile(); + + // destructor + ~CDmridDirFile() {} + + // init & close + bool Init(void); + + // refresh + bool LoadContent(CBuffer *); + bool RefreshContent(const CBuffer &); + +protected: + // reload helpers + bool NeedReload(void); + bool GetLastModTime(time_t *); + +protected: + // data + time_t m_LastModTime; + }; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cdmrididirfile_h */ diff --git a/src/cdmriddirhttp.cpp b/src/cdmriddirhttp.cpp new file mode 100644 index 0000000..12f21a0 --- /dev/null +++ b/src/cdmriddirhttp.cpp @@ -0,0 +1,182 @@ +// +// cdmriddirhttp.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 29/12/2017. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd 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 of the License, or +// (at your option) any later version. +// +// xlxd 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 Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include +#include "main.h" +#include "creflector.h" +#include "cdmriddirhttp.h" + +#if (DMRIDDB_USE_RLX_SERVER == 1) +CDmridDirHttp g_DmridDir; +#endif + + + + +//////////////////////////////////////////////////////////////////////////////////////// +// refresh + +bool CDmridDirHttp::LoadContent(CBuffer *buffer) +{ + // get file from xlxapi server + return HttpGet("xlxapi.rlx.lu", "api/exportdmr.php", 80, buffer); +} + +bool CDmridDirHttp::RefreshContent(const CBuffer &buffer) +{ + bool ok = false; + + // clear directory + m_CallsignMap.clear(); + m_DmridMap.clear(); + + // scan file + if ( buffer.size() > 0 ) + { + char *ptr1 = (char *)buffer.data(); + char *ptr2; + // get next line + while ( (ptr2 = ::strchr(ptr1, '\n')) != NULL ) + { + *ptr2 = 0; + // get items + char *dmrid; + char *callsign; + if ( ((dmrid = ::strtok(ptr1, ";")) != NULL) && IsValidDmrid(dmrid) ) + { + if ( ((callsign = ::strtok(NULL, ";")) != NULL) ) + { + // new entry + uint32 ui = atoi(dmrid); + CCallsign cs(callsign, ui); + if ( cs.IsValid() ) + { + m_CallsignMap.insert(std::pair(ui, cs)); + m_DmridMap.insert(std::pair(cs,ui)); + } + } + } + // next line + ptr1 = ptr2+1; + } + // done + ok = true; + } + + // report + std::cout << "Read " << m_DmridMap.size() << " DMR id from xlxapi.rlx.lu database " << std::endl; + + // done + return ok; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// httpd helpers + +#define DMRID_HTTPGET_SIZEMAX (256) +#define DMRID_TEXTFILE_SIZEMAX (10*1024*1024) + +bool CDmridDirHttp::HttpGet(const char *hostname, const char *filename, int port, CBuffer *buffer) +{ + bool ok = false; + int sock_id; + + // open socket + if ( (sock_id = ::socket(AF_INET, SOCK_STREAM, 0)) >= 0 ) + { + // get hostname address + struct sockaddr_in servaddr; + struct hostent *hp; + ::memset(&servaddr,0,sizeof(servaddr)); + if( (hp = gethostbyname(hostname)) != NULL ) + { + // dns resolved + ::memcpy((char *)&servaddr.sin_addr.s_addr, (char *)hp->h_addr, hp->h_length); + servaddr.sin_port = htons(port); + servaddr.sin_family = AF_INET; + + // connect + if ( ::connect(sock_id, (struct sockaddr *)&servaddr, sizeof(servaddr)) == 0) + { + // send the GET request + char request[DMRID_HTTPGET_SIZEMAX]; + ::sprintf(request, "GET /%s HTTP/1.0\r\nFrom: %s\r\nUser-Agent: xlxd\r\n\r\n", + filename, (const char *)g_Reflector.GetCallsign()); + ::write(sock_id, request, strlen(request)); + + // config receive timeouts + fd_set read_set; + struct timeval timeout; + timeout.tv_sec = 5; + timeout.tv_usec = 0; + FD_ZERO(&read_set); + FD_SET(sock_id, &read_set); + + // get the reply back + buffer->clear(); + bool done = false; + do + { + char buf[1440]; + ssize_t len = 0; + select(sock_id+1, &read_set, NULL, NULL, &timeout); + //if ( (ret > 0) || ((ret < 0) && (errno == EINPROGRESS)) ) + //if ( ret >= 0 ) + //{ + usleep(5000); + len = read(sock_id, buf, 1440); + if ( len > 0 ) + { + buffer->Append((uint8 *)buf, (int)len); + ok = true; + } + //} + done = (len <= 0); + + } while (!done); + buffer->Append((uint8)0); + + // and disconnect + close(sock_id); + } + else + { + std::cout << "Cannot establish connection with host " << hostname << std::endl; + } + } + else + { + std::cout << "Host " << hostname << " not found" << std::endl; + } + + } + else + { + std::cout << "Failed to open wget socket" << std::endl; + } + + // done + return ok; +} diff --git a/src/cdmriddirhttp.h b/src/cdmriddirhttp.h new file mode 100644 index 0000000..1d90ec1 --- /dev/null +++ b/src/cdmriddirhttp.h @@ -0,0 +1,52 @@ +// +// cdmriddirhttp.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 29/12/2017. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd 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 of the License, or +// (at your option) any later version. +// +// xlxd 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 Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cdmriddirhttp_h +#define cdmriddirhttp_h + +#include "cdmriddir.h" + +//////////////////////////////////////////////////////////////////////////////////////// + +class CDmridDirHttp : public CDmridDir +{ +public: + // constructor + CDmridDirHttp() {} + + // destructor + ~CDmridDirHttp() {} + + // refresh + bool LoadContent(CBuffer *); + bool RefreshContent(const CBuffer &); + +protected: + // reload helpers + bool NeedReload(void) { return true; } + bool HttpGet(const char *, const char *, int, CBuffer *); +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cdmriddirhttp_h */ diff --git a/src/cdvframepacket.cpp b/src/cdvframepacket.cpp index aa0e7bf..113aba4 100644 --- a/src/cdvframepacket.cpp +++ b/src/cdvframepacket.cpp @@ -61,6 +61,20 @@ CDvFramePacket::CDvFramePacket(const uint8 *ambe, const uint8 *sync, uint16 sid, ::memset(m_uiDvData, 0, sizeof(m_uiDvData)); } +// dstar + dmr constructor + +CDvFramePacket::CDvFramePacket + (uint16 sid, + uint8 dstarpid, const uint8 *dstarambe, const uint8 *dstardvdata, + uint8 dmrpid, uint8 dprspid, const uint8 *dmrambe, const uint8 *dmrsync) +: CPacket(sid, dstarpid, dmrpid, dprspid) +{ + ::memcpy(m_uiAmbe, dstarambe, sizeof(m_uiAmbe)); + ::memcpy(m_uiDvData, dstardvdata, sizeof(m_uiDvData)); + ::memcpy(m_uiAmbePlus, dmrambe, sizeof(m_uiAmbePlus)); + ::memcpy(m_uiDvSync, dmrsync, sizeof(m_uiDvSync)); +} + // copy constructor CDvFramePacket::CDvFramePacket(const CDvFramePacket &DvFrame) diff --git a/src/cdvframepacket.h b/src/cdvframepacket.h index 555770d..69917b7 100644 --- a/src/cdvframepacket.h +++ b/src/cdvframepacket.h @@ -55,6 +55,7 @@ public: CDvFramePacket(); CDvFramePacket(const struct dstar_dvframe *, uint16, uint8); CDvFramePacket(const uint8 *, const uint8 *, uint16, uint8, uint8); + CDvFramePacket(uint16, uint8, const uint8 *, const uint8 *, uint8, uint8, const uint8 *, const uint8 *); CDvFramePacket(const CDvFramePacket &); // destructor diff --git a/src/cdvlastframepacket.cpp b/src/cdvlastframepacket.cpp index 46673f3..5908303 100644 --- a/src/cdvlastframepacket.cpp +++ b/src/cdvlastframepacket.cpp @@ -47,6 +47,16 @@ CDvLastFramePacket::CDvLastFramePacket(const uint8 *ambe, const uint8 *sync, uin { } +// dstar + dmr constructor + +CDvLastFramePacket::CDvLastFramePacket + (uint16 sid, + uint8 dstarpid, const uint8 *dstarambe, const uint8 *dstardvdata, + uint8 dmrpid, uint8 dprspid, const uint8 *dmrambe, const uint8 *dmrsync) + : CDvFramePacket(sid, dstarpid, dstarambe, dstardvdata, dmrpid, dprspid, dmrambe, dmrsync) +{ +} + // copy constructor CDvLastFramePacket::CDvLastFramePacket(const CDvLastFramePacket &DvFrame) diff --git a/src/cdvlastframepacket.h b/src/cdvlastframepacket.h index d561fc6..685e9d4 100644 --- a/src/cdvlastframepacket.h +++ b/src/cdvlastframepacket.h @@ -42,6 +42,7 @@ public: CDvLastFramePacket(); CDvLastFramePacket(const struct dstar_dvframe *, uint16, uint8); CDvLastFramePacket(const uint8 *, const uint8 *, uint16, uint8, uint8); + CDvLastFramePacket(uint16, uint8, const uint8 *, const uint8 *, uint8, uint8, const uint8 *, const uint8 *); CDvLastFramePacket(const CDvLastFramePacket &); // destructor diff --git a/src/cpacket.cpp b/src/cpacket.cpp index b93d603..02b8931 100644 --- a/src/cpacket.cpp +++ b/src/cpacket.cpp @@ -59,6 +59,16 @@ CPacket::CPacket(uint16 sid, uint8 dmrpid, uint8 dmrspid) m_uiOriginId = ORIGIN_LOCAL; }; +CPacket::CPacket(uint16 sid, uint8 dstarpid, uint8 dmrpid, uint8 dmrsubpid) +{ + m_uiStreamId = sid; + m_uiDstarPacketId = dstarpid; + m_uiDmrPacketId = dmrpid; + m_uiDmrPacketSubid = dmrsubpid; + m_uiModuleId = ' '; + m_uiOriginId = ORIGIN_LOCAL; +} + //////////////////////////////////////////////////////////////////////////////////////// // virtual duplication diff --git a/src/cpacket.h b/src/cpacket.h index b762896..30280b5 100644 --- a/src/cpacket.h +++ b/src/cpacket.h @@ -42,6 +42,7 @@ public: CPacket(); CPacket(uint16 sid, uint8 dstarpid); CPacket(uint16 sid, uint8 dmrpid, uint8 dmrsubpid); + CPacket(uint16 sid, uint8 dstarpid, uint8 dmrpid, uint8 dmrsubpid); // destructor virtual ~CPacket() {}; diff --git a/src/cpeer.cpp b/src/cpeer.cpp index d1db483..037325e 100644 --- a/src/cpeer.cpp +++ b/src/cpeer.cpp @@ -40,12 +40,13 @@ CPeer::CPeer() m_LastHeardTime = std::time(NULL); } -CPeer::CPeer(const CCallsign &callsign, const CIp &ip, char *modules) +CPeer::CPeer(const CCallsign &callsign, const CIp &ip, char *modules, const CVersion &version) { m_Callsign = callsign; m_Ip = ip; ::memset(m_ReflectorModules, 0, sizeof(m_ReflectorModules)); ::strncpy(m_ReflectorModules, modules, sizeof(m_ReflectorModules)-1); + m_Version = version; m_LastKeepaliveTime.Now(); m_ConnectTime = std::time(NULL); m_LastHeardTime = std::time(NULL); @@ -56,6 +57,7 @@ CPeer::CPeer(const CPeer &peer) m_Callsign = peer.m_Callsign; m_Ip = peer.m_Ip; ::memcpy(m_ReflectorModules, peer.m_ReflectorModules, sizeof(m_ReflectorModules)); + m_Version = peer.m_Version; m_LastKeepaliveTime = peer.m_LastKeepaliveTime; m_ConnectTime = peer.m_ConnectTime; m_LastHeardTime = peer.m_LastHeardTime; @@ -82,6 +84,7 @@ bool CPeer::operator ==(const CPeer &peer) const same &= (peer.m_Callsign == m_Callsign); same &= (peer.m_Ip == m_Ip); + same &= (peer.m_Version == m_Version); for ( int i = 0; (i < m_Clients.size()) && same ; i++ ) { same &= (peer.m_Clients[i] == m_Clients[i]); diff --git a/src/cpeer.h b/src/cpeer.h index 92dcdc6..ffb68a5 100644 --- a/src/cpeer.h +++ b/src/cpeer.h @@ -25,6 +25,7 @@ #ifndef cpeer_h #define cpeer_h +#include "cversion.h" #include "ctimepoint.h" #include "cip.h" #include "ccallsign.h" @@ -41,7 +42,7 @@ class CPeer public: // constructors CPeer(); - CPeer(const CCallsign &, const CIp &, char *); + CPeer(const CCallsign &, const CIp &, char *, const CVersion &); CPeer(const CPeer &); // destructor @@ -82,6 +83,7 @@ protected: CCallsign m_Callsign; CIp m_Ip; char m_ReflectorModules[NB_MODULES_MAX+1]; + CVersion m_Version; std::vector m_Clients; // status diff --git a/src/creflector.cpp b/src/creflector.cpp index 9ede4fc..ed6ba58 100644 --- a/src/creflector.cpp +++ b/src/creflector.cpp @@ -26,7 +26,8 @@ #include #include "creflector.h" #include "cgatekeeper.h" -#include "cdmriddir.h" +#include "cdmriddirfile.h" +#include "cdmriddirhttp.h" #include "ctranscoder.h" //////////////////////////////////////////////////////////////////////////////////////// @@ -102,7 +103,7 @@ bool CReflector::Start(void) ok &= g_GateKeeper.Init(); // init dmrid directory - g_DmridDir.RefreshContent(); + g_DmridDir.Init(); // init the transcoder g_Transcoder.Init(); diff --git a/src/ctranscoder.cpp b/src/ctranscoder.cpp index ff378c7..6b810c9 100644 --- a/src/ctranscoder.cpp +++ b/src/ctranscoder.cpp @@ -188,12 +188,6 @@ void CTranscoder::Task(void) } - // handle end of streaming timeout - //CheckStreamsTimeout(); - - // handle queue from reflector - //HandleQueue(); - // keep client alive if ( m_LastKeepaliveTime.DurationSinceNow() > TRANSCODER_KEEPALIVE_PERIOD ) { diff --git a/src/ctranscoder.h b/src/ctranscoder.h index b45b727..7d54c58 100644 --- a/src/ctranscoder.h +++ b/src/ctranscoder.h @@ -55,6 +55,9 @@ public: void Lock(void) { m_Mutex.lock(); } void Unlock(void) { m_Mutex.unlock(); } + // status + bool IsConnected(void) const { return m_bConnected; } + // manage streams CCodecStream *GetStream(CPacketStream *, uint8); void ReleaseStream(CCodecStream *); diff --git a/src/cversion.cpp b/src/cversion.cpp new file mode 100644 index 0000000..8452e0b --- /dev/null +++ b/src/cversion.cpp @@ -0,0 +1,87 @@ +// +// cversion.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 05/01/2018. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd 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 of the License, or +// (at your option) any later version. +// +// xlxd 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 Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "main.h" +#include "cversion.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + +CVersion::CVersion() +{ + m_iMajor = 0; + m_iMinor = 0; + m_iRevision = 0; +} + +CVersion::CVersion(int iMajor, int iMinor, int iRevision) +{ + m_iMajor = iMajor; + m_iMinor = iMinor; + m_iRevision = iRevision; +} + +CVersion::CVersion(const CVersion &Version) +{ + m_iMajor = Version.m_iMajor; + m_iMinor = Version.m_iMinor; + m_iRevision = Version.m_iRevision; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// comparaison + +bool CVersion::IsEqualOrHigherTo(const CVersion &version) const +{ + if ( m_iMajor > version.m_iMajor ) + { + return true; + } + else if ( m_iMajor == version.m_iMajor ) + { + if ( m_iMinor > version.m_iMinor ) + { + return true; + } + else if ( m_iMinor == version.m_iMinor ) + { + if ( m_iRevision >= version.m_iRevision ) + { + return true; + } + } + } + return false; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// operator + +bool CVersion::operator ==(const CVersion &Version) const +{ + return ( (Version.m_iMajor == m_iMajor) && + (Version.m_iMinor == m_iMinor) && + (Version.m_iRevision == m_iRevision )) ; +} + diff --git a/src/cversion.h b/src/cversion.h new file mode 100644 index 0000000..a5e931a --- /dev/null +++ b/src/cversion.h @@ -0,0 +1,64 @@ +// +// cversion.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 05/01/2018. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd 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 of the License, or +// (at your option) any later version. +// +// xlxd 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 Foobar. If not, see . +// ---------------------------------------------------------------------------- + + +#ifndef cversion_h +#define cversion_h + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CVersion +{ +public: + // constructor + CVersion(); + CVersion(int, int, int); + CVersion(const CVersion &); + + // destructor + virtual ~CVersion() {} + + // get + int GetMajor(void) const { return m_iMajor; } + int GetMinor(void) const { return m_iMinor; } + int GetRevision(void) const { return m_iRevision; } + + // comparaison + bool IsEqualOrHigherTo(const CVersion &) const; + + // operator + bool operator ==(const CVersion &) const; + +protected: + // data + int m_iMajor; + int m_iMinor; + int m_iRevision; +}; + + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cversion_h */ diff --git a/src/cxlxclient.cpp b/src/cxlxclient.cpp index 63d51d8..2d32b29 100644 --- a/src/cxlxclient.cpp +++ b/src/cxlxclient.cpp @@ -32,16 +32,40 @@ CXlxClient::CXlxClient() { + m_ProtRev = XLX_PROTOCOL_REVISION_0; } -CXlxClient::CXlxClient(const CCallsign &callsign, const CIp &ip, char reflectorModule) +CXlxClient::CXlxClient(const CCallsign &callsign, const CIp &ip, char reflectorModule, int protRev) : CClient(callsign, ip, reflectorModule) { + m_ProtRev = protRev; } CXlxClient::CXlxClient(const CXlxClient &client) : CClient(client) { + m_ProtRev = client.m_ProtRev; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// identity + +int CXlxClient::GetCodec(void) const +{ + int codec; + + switch ( GetProtocolRevision() ) + { + case XLX_PROTOCOL_REVISION_0: + case XLX_PROTOCOL_REVISION_1: + default: + codec = CODEC_AMBEPLUS; + break; + case XLX_PROTOCOL_REVISION_2: + codec = CODEC_NONE; + break; + } + return codec; } //////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/cxlxclient.h b/src/cxlxclient.h index 503df40..ae15045 100644 --- a/src/cxlxclient.h +++ b/src/cxlxclient.h @@ -31,6 +31,10 @@ //////////////////////////////////////////////////////////////////////////////////////// // define +#define XLX_PROTOCOL_REVISION_0 0 // AMBE only, original connect mechanism +#define XLX_PROTOCOL_REVISION_1 1 // AMBE only, revised connect mechanism +#define XLX_PROTOCOL_REVISION_2 2 // Transcoded AMBE+AMBE2 interlink + //////////////////////////////////////////////////////////////////////////////////////// // class @@ -40,7 +44,7 @@ class CXlxClient : public CClient public: // constructors CXlxClient(); - CXlxClient(const CCallsign &, const CIp &, char = ' '); + CXlxClient(const CCallsign &, const CIp &, char = ' ', int = XLX_PROTOCOL_REVISION_0); CXlxClient(const CXlxClient &); // destructor @@ -48,7 +52,9 @@ public: // identity int GetProtocol(void) const { return PROTOCOL_XLX; } + int GetProtocolRevision(void) const { return m_ProtRev; } const char *GetProtocolName(void) const { return "XLX"; } + int GetCodec(void) const; bool IsPeer(void) const { return true; } // status @@ -56,6 +62,10 @@ public: // reporting void WriteXml(std::ofstream &) {} + +protected: + // data + int m_ProtRev; }; //////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/cxlxpeer.cpp b/src/cxlxpeer.cpp index d96fbaa..aa214c4 100644 --- a/src/cxlxpeer.cpp +++ b/src/cxlxpeer.cpp @@ -37,14 +37,18 @@ CXlxPeer::CXlxPeer() { } -CXlxPeer::CXlxPeer(const CCallsign &callsign, const CIp &ip, char *modules) -: CPeer(callsign, ip, modules) +CXlxPeer::CXlxPeer(const CCallsign &callsign, const CIp &ip, char *modules, const CVersion &version) +: CPeer(callsign, ip, modules, version) { + // get protocol revision + int protrev = GetProtocolRevision(version); + //std::cout << "Adding XLX peer with protocol revision " << protrev << std::endl; + // and construct all xlx clients for ( int i = 0; i < ::strlen(modules); i++ ) { // create - CXlxClient *client = new CXlxClient(callsign, ip, modules[i]); + CXlxClient *client = new CXlxClient(callsign, ip, modules[i], protrev); // and append to vector m_Clients.push_back(client); } @@ -82,3 +86,21 @@ bool CXlxPeer::IsAlive(void) const return (m_LastKeepaliveTime.DurationSinceNow() < XLX_KEEPALIVE_TIMEOUT); } +//////////////////////////////////////////////////////////////////////////////////////// +// revision helper + +int CXlxPeer::GetProtocolRevision(const CVersion &version) +{ + int protrev = XLX_PROTOCOL_REVISION_0; + + if ( version.IsEqualOrHigherTo(CVersion(2,2,0)) ) + { + protrev = XLX_PROTOCOL_REVISION_2; + } + else if ( version.IsEqualOrHigherTo(CVersion(1,4,0)) ) + { + protrev = XLX_PROTOCOL_REVISION_1; + } + return protrev; +} + diff --git a/src/cxlxpeer.h b/src/cxlxpeer.h index 1ced2ad..482f3ec 100644 --- a/src/cxlxpeer.h +++ b/src/cxlxpeer.h @@ -26,9 +26,11 @@ #define cxlxpeer_h #include "cpeer.h" +#include "cxlxclient.h" //////////////////////////////////////////////////////////////////////////////////////// -// +// define + //////////////////////////////////////////////////////////////////////////////////////// // class @@ -38,7 +40,7 @@ class CXlxPeer : public CPeer public: // constructors CXlxPeer(); - CXlxPeer(const CCallsign &, const CIp &, char *); + CXlxPeer(const CCallsign &, const CIp &, char *, const CVersion &); CXlxPeer(const CXlxPeer &); // destructor @@ -46,9 +48,13 @@ public: // status bool IsAlive(void) const; + // identity int GetProtocol(void) const { return PROTOCOL_XLX; } const char *GetProtocolName(void) const { return "XLX"; } + + // revision helper + static int GetProtocolRevision(const CVersion &); }; //////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/cxlxprotocol.cpp b/src/cxlxprotocol.cpp index c6f645a..0a6dd7c 100644 --- a/src/cxlxprotocol.cpp +++ b/src/cxlxprotocol.cpp @@ -67,10 +67,10 @@ void CXlxProtocol::Task(void) CIp Ip; CCallsign Callsign; char Modules[NB_MODULES_MAX+1]; + CVersion Version; CDvHeaderPacket *Header; CDvFramePacket *Frame; CDvLastFramePacket *LastFrame; - uint8 Major, Minor, Revision; // any incoming packet ? if ( m_Socket.Receive(&Buffer, &Ip, 20) != -1 ) @@ -106,34 +106,41 @@ void CXlxProtocol::Task(void) // handle it OnDvLastFramePacketIn(LastFrame, &Ip); } - else if ( IsValidConnectPacket(Buffer, &Callsign, Modules, &Major, &Minor, &Revision) ) + else if ( IsValidConnectPacket(Buffer, &Callsign, Modules, &Version) ) { - std::cout << "XLX (" << (int)Major << "." << (int)Minor << "." << (int)Revision + std::cout << "XLX (" + << Version.GetMajor() << "." << Version.GetMinor() << "." << Version.GetRevision() << ") connect packet for modules " << Modules << " from " << Callsign << " at " << Ip << std::endl; // callsign authorized? if ( g_GateKeeper.MayLink(Callsign, Ip, PROTOCOL_XLX, Modules) ) { + // acknowledge connecting request // following is version dependent - // for backward compatibility, only send ACK once - if ( (Major == 1) && (Minor < 4) ) + switch ( CXlxPeer::GetProtocolRevision(Version) ) { - // already connected ? - CPeers *peers = g_Reflector.GetPeers(); - if ( peers->FindPeer(Callsign, Ip, PROTOCOL_XLX) == NULL ) - { + case XLX_PROTOCOL_REVISION_0: + { + // already connected ? + CPeers *peers = g_Reflector.GetPeers(); + if ( peers->FindPeer(Callsign, Ip, PROTOCOL_XLX) == NULL ) + { + // acknowledge the request + EncodeConnectAckPacket(&Buffer, Modules); + m_Socket.Send(Buffer, Ip); + } + g_Reflector.ReleasePeers(); + + } + break; + case XLX_PROTOCOL_REVISION_1: + case XLX_PROTOCOL_REVISION_2: + default: // acknowledge the request EncodeConnectAckPacket(&Buffer, Modules); m_Socket.Send(Buffer, Ip); - } - g_Reflector.ReleasePeers(); - } - else - { - // acknowledge the request - EncodeConnectAckPacket(&Buffer, Modules); - m_Socket.Send(Buffer, Ip); + break; } } else @@ -143,7 +150,7 @@ void CXlxProtocol::Task(void) m_Socket.Send(Buffer, Ip); } } - else if ( IsValidAckPacket(Buffer, &Callsign, Modules) ) + else if ( IsValidAckPacket(Buffer, &Callsign, Modules, &Version) ) { std::cout << "XLX ack packet for modules " << Modules << " from " << Callsign << " at " << Ip << std::endl; @@ -156,8 +163,8 @@ void CXlxProtocol::Task(void) { // create the new peer // this also create one client per module - CXlxPeer *peer = new CXlxPeer(Callsign, Ip, Modules); - + CXlxPeer *peer = new CXlxPeer(Callsign, Ip, Modules, Version); + // append the peer to reflector peer list // this also add all new clients to reflector client list peers->AddPeer(peer); @@ -253,6 +260,13 @@ void CXlxProtocol::HandleQueue(void) CBuffer buffer; if ( EncodeDvPacket(*packet, &buffer) ) { + // encode revision dependent version + CBuffer bufferLegacy = buffer; + if ( packet->IsDvFrame() && (bufferLegacy.size() == 45) ) + { + bufferLegacy.resize(27); + } + // and push it to all our clients linked to the module and who are not streaming in CClients *clients = g_Reflector.GetClients(); int index = -1; @@ -263,7 +277,25 @@ void CXlxProtocol::HandleQueue(void) if ( !client->IsAMaster() && (client->GetReflectorModule() == packet->GetModuleId()) ) { // no, send the packet - m_Socket.Send(buffer, client->GetIp()); + // this is protocol revision dependent + switch ( client->GetProtocolRevision() ) + { + case XLX_PROTOCOL_REVISION_0: + case XLX_PROTOCOL_REVISION_1: + m_Socket.Send(bufferLegacy, client->GetIp()); + break; + case XLX_PROTOCOL_REVISION_2: + default: + if ( g_Transcoder.IsConnected() ) + { + m_Socket.Send(buffer, client->GetIp()); + } + else + { + m_Socket.Send(bufferLegacy, client->GetIp()); + } + break; + } } } g_Reflector.ReleaseClients(); @@ -459,7 +491,7 @@ bool CXlxProtocol::IsValidKeepAlivePacket(const CBuffer &Buffer, CCallsign *call } -bool CXlxProtocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *callsign, char *modules, uint8 *major, uint8 *minor, uint8 *rev) +bool CXlxProtocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *callsign, char *modules, CVersion *version) { bool valid = false; if ((Buffer.size() == 39) && (Buffer.data()[0] == 'L') && (Buffer.data()[38] == 0)) @@ -467,9 +499,7 @@ bool CXlxProtocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *callsi callsign->SetCallsign((const uint8 *)&(Buffer.data()[1]), 8); ::strcpy(modules, (const char *)&(Buffer.data()[12])); valid = callsign->IsValid(); - *major = Buffer.data()[9]; - *minor = Buffer.data()[10]; - *rev = Buffer.data()[11]; + *version = CVersion(Buffer.data()[9], Buffer.data()[10], Buffer.data()[11]); for ( int i = 0; i < ::strlen(modules); i++ ) { valid &= IsLetter(modules[i]); @@ -489,7 +519,7 @@ bool CXlxProtocol::IsValidDisconnectPacket(const CBuffer &Buffer, CCallsign *cal return valid; } -bool CXlxProtocol::IsValidAckPacket(const CBuffer &Buffer, CCallsign *callsign, char *modules) +bool CXlxProtocol::IsValidAckPacket(const CBuffer &Buffer, CCallsign *callsign, char *modules, CVersion *version) { bool valid = false; if ((Buffer.size() == 39) && (Buffer.data()[0] == 'A') && (Buffer.data()[38] == 0)) @@ -497,6 +527,7 @@ bool CXlxProtocol::IsValidAckPacket(const CBuffer &Buffer, CCallsign *callsign, callsign->SetCallsign((const uint8 *)&(Buffer.data()[1]), 8); ::strcpy(modules, (const char *)&(Buffer.data()[12])); valid = callsign->IsValid(); + *version = CVersion(Buffer.data()[9], Buffer.data()[10], Buffer.data()[11]); for ( int i = 0; i < ::strlen(modules); i++ ) { valid &= IsLetter(modules[i]); @@ -516,6 +547,74 @@ bool CXlxProtocol::IsValidNackPacket(const CBuffer &Buffer, CCallsign *callsign) return valid; } +CDvFramePacket *CXlxProtocol::IsValidDvFramePacket(const CBuffer &Buffer) +{ + CDvFramePacket *dvframe = NULL; + + // base class first (protocol revision 1 and lower) + dvframe = CDextraProtocol::IsValidDvFramePacket(Buffer); + + // otherwise try protocol revision 2 + if ( (dvframe == NULL) && + (Buffer.size() == 45) && (Buffer.Compare((uint8 *)"DSVT", 4) == 0) && + (Buffer.data()[4] == 0x20) && (Buffer.data()[8] == 0x20) && + ((Buffer.data()[14] & 0x40) == 0) ) + { + // create packet + dvframe = new CDvFramePacket( + // sid + *((uint16 *)&(Buffer.data()[12])), + // dstar + Buffer.data()[14], &(Buffer.data()[15]), &(Buffer.data()[24]), + // dmr + Buffer.data()[27], Buffer.data()[28], &(Buffer.data()[29]), &(Buffer.data()[38])); + + // check validity of packet + if ( !dvframe->IsValid() ) + { + delete dvframe; + dvframe = NULL; + } + } + + // done + return dvframe; +} + +CDvLastFramePacket *CXlxProtocol::IsValidDvLastFramePacket(const CBuffer &Buffer) +{ + CDvLastFramePacket *dvframe = NULL; + + // base class first (protocol revision 1 and lower) + dvframe = CDextraProtocol::IsValidDvLastFramePacket(Buffer); + + // otherwise try protocol revision 2 + if ( (dvframe == NULL) && + (Buffer.size() == 45) && (Buffer.Compare((uint8 *)"DSVT", 4) == 0) && + (Buffer.data()[4] == 0x20) && (Buffer.data()[8] == 0x20) && + ((Buffer.data()[14] & 0x40) != 0) ) + { + // create packet + dvframe = new CDvLastFramePacket( + // sid + *((uint16 *)&(Buffer.data()[12])), + // dstar + Buffer.data()[14], &(Buffer.data()[15]), &(Buffer.data()[24]), + // dmr + Buffer.data()[27], Buffer.data()[28], &(Buffer.data()[29]), &(Buffer.data()[38])); + + // check validity of packet + if ( !dvframe->IsValid() ) + { + delete dvframe; + dvframe = NULL; + } + } + + // done + return dvframe; +} + //////////////////////////////////////////////////////////////////////////////////////// // packet encoding helpers @@ -584,3 +683,42 @@ void CXlxProtocol::EncodeConnectNackPacket(CBuffer *Buffer) Buffer->Append((uint8)0); } +bool CXlxProtocol::EncodeDvFramePacket(const CDvFramePacket &Packet, CBuffer *Buffer) const +{ + uint8 tag[] = { 'D','S','V','T',0x20,0x00,0x00,0x00,0x20,0x00,0x01,0x02 }; + + Buffer->Set(tag, sizeof(tag)); + Buffer->Append(Packet.GetStreamId()); + Buffer->Append((uint8)(Packet.GetDstarPacketId() % 21)); + Buffer->Append((uint8 *)Packet.GetAmbe(), AMBE_SIZE); + Buffer->Append((uint8 *)Packet.GetDvData(), DVDATA_SIZE); + + Buffer->Append((uint8)Packet.GetDmrPacketId()); + Buffer->Append((uint8)Packet.GetDmrPacketSubid()); + Buffer->Append((uint8 *)Packet.GetAmbePlus(), AMBEPLUS_SIZE); + Buffer->Append((uint8 *)Packet.GetDvSync(), DVSYNC_SIZE); + + return true; + +} + +bool CXlxProtocol::EncodeDvLastFramePacket(const CDvLastFramePacket &Packet, CBuffer *Buffer) const +{ + uint8 tag[] = { 'D','S','V','T',0x20,0x00,0x00,0x00,0x20,0x00,0x01,0x02 }; + uint8 dstarambe[] = { 0x55,0xC8,0x7A,0x00,0x00,0x00,0x00,0x00,0x00 }; + uint8 dstardvdata[] = { 0x25,0x1A,0xC6 }; + + Buffer->Set(tag, sizeof(tag)); + Buffer->Append(Packet.GetStreamId()); + Buffer->Append((uint8)((Packet.GetPacketId() % 21) | 0x40)); + Buffer->Append(dstarambe, sizeof(dstarambe)); + Buffer->Append(dstardvdata, sizeof(dstardvdata)); + + + Buffer->Append((uint8)Packet.GetDmrPacketId()); + Buffer->Append((uint8)Packet.GetDmrPacketSubid()); + Buffer->Append((uint8 *)Packet.GetAmbePlus(), AMBEPLUS_SIZE); + Buffer->Append((uint8 *)Packet.GetDvSync(), DVSYNC_SIZE); + + return true; +} diff --git a/src/cxlxprotocol.h b/src/cxlxprotocol.h index 795fa06..1d23c50 100644 --- a/src/cxlxprotocol.h +++ b/src/cxlxprotocol.h @@ -25,7 +25,7 @@ #ifndef cxlxprotocol_h #define cxlxprotocol_h - +#include "cversion.h" #include "ctimepoint.h" #include "cdextraprotocol.h" #include "cclients.h" @@ -65,18 +65,22 @@ protected: void OnDvLastFramePacketIn(CDvLastFramePacket *, const CIp * = NULL); // packet decoding helpers - bool IsValidKeepAlivePacket(const CBuffer &, CCallsign *); - bool IsValidConnectPacket(const CBuffer &, CCallsign *, char *, uint8 *, uint8 *, uint8 *); - bool IsValidDisconnectPacket(const CBuffer &, CCallsign *); - bool IsValidAckPacket(const CBuffer &, CCallsign *, char *); - bool IsValidNackPacket(const CBuffer &, CCallsign *); + bool IsValidKeepAlivePacket(const CBuffer &, CCallsign *); + bool IsValidConnectPacket(const CBuffer &, CCallsign *, char *, CVersion *); + bool IsValidDisconnectPacket(const CBuffer &, CCallsign *); + bool IsValidAckPacket(const CBuffer &, CCallsign *, char *, CVersion *); + bool IsValidNackPacket(const CBuffer &, CCallsign *); + CDvFramePacket *IsValidDvFramePacket(const CBuffer &); + CDvLastFramePacket *IsValidDvLastFramePacket(const CBuffer &); // packet encoding helpers - void EncodeKeepAlivePacket(CBuffer *); - void EncodeConnectPacket(CBuffer *, const char *); - void EncodeDisconnectPacket(CBuffer *); - void EncodeConnectAckPacket(CBuffer *, const char *); - void EncodeConnectNackPacket(CBuffer *); + void EncodeKeepAlivePacket(CBuffer *); + void EncodeConnectPacket(CBuffer *, const char *); + void EncodeDisconnectPacket(CBuffer *); + void EncodeConnectAckPacket(CBuffer *, const char *); + void EncodeConnectNackPacket(CBuffer *); + bool EncodeDvFramePacket(const CDvFramePacket &, CBuffer *) const; + bool EncodeDvLastFramePacket(const CDvLastFramePacket &, CBuffer *) const; protected: // time diff --git a/src/main.cpp b/src/main.cpp index 830f74e..154a53b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -94,6 +94,9 @@ int main(int argc, const char * argv[]) return 1; } + // splash + std::cout << "Starting xlxd " << VERSION_MAJOR << "." << VERSION_MINOR << "." << VERSION_REVISION << std::endl << std::endl; + // initialize reflector g_Reflector.SetCallsign(argv[1]); g_Reflector.SetListenIp(CIp(argv[2])); diff --git a/src/main.h b/src/main.h index 566d986..ac77c96 100644 --- a/src/main.h +++ b/src/main.h @@ -48,8 +48,8 @@ // version ----------------------------------------------------- #define VERSION_MAJOR 2 -#define VERSION_MINOR 0 -#define VERSION_REVISION 0 +#define VERSION_MINOR 2 +#define VERSION_REVISION 1 // global ------------------------------------------------------ @@ -127,8 +127,9 @@ // DMRid database ----------------------------------------------- -#define DMRIDDB_USE_RLX_SERVER 0 -#define DMRIDDB_PATH "/xlxd/dmrid.dat" +#define DMRIDDB_USE_RLX_SERVER 1 // 1 = use http, 0 = use local file +#define DMRIDDB_PATH "/xlxd/dmrid.dat" // local file path +#define DMRIDDB_REFRESH_RATE 180 // in minutes // xml & json reporting ----------------------------------------- @@ -180,8 +181,13 @@ extern CReflector g_Reflector; class CGateKeeper; extern CGateKeeper g_GateKeeper; -class CDmridDir; -extern CDmridDir g_DmridDir; +#if (DMRIDDB_USE_RLX_SERVER == 1) + class CDmridDirHttp; + extern CDmridDirHttp g_DmridDir; +#else + class CDmridDirFile; + extern CDmridDirFile g_DmridDir; +#endif class CTranscoder; extern CTranscoder g_Transcoder; From 6d5055a927829b688ed5aa716a0e0720c948d04b Mon Sep 17 00:00:00 2001 From: LX1IQ Date: Mon, 15 Jan 2018 12:14:59 +0100 Subject: [PATCH 027/244] bugfix apache2: PHP Notice: Undefined index: Locale in /var/www/db/index.php on line 15 --- dashboard/index.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/dashboard/index.php b/dashboard/index.php index f64d1ab..a71586a 100755 --- a/dashboard/index.php +++ b/dashboard/index.php @@ -12,8 +12,6 @@ if (!class_exists('Station')) require_once("./pgs/class.station.php"); if (!class_exists('Peer')) require_once("./pgs/class.peer.php"); if (!class_exists('Interlink')) require_once("./pgs/class.interlink.php"); -setlocale(LC_ALL, $VNStat['Locale']); - $Reflector = new xReflector(); $Reflector->SetFlagFile("./pgs/country.csv"); $Reflector->SetPIDFile($Service['PIDFile']); From 8b52495cf24aa907c4d6a0432f7c2d5751e8eacd Mon Sep 17 00:00:00 2001 From: LX1IQ Date: Fri, 19 Jan 2018 10:15:04 +0100 Subject: [PATCH 028/244] db v2.3.9 redesign for the callinghome.php --- dashboard/changes.txt | 8 ++++++ dashboard/index.php | 66 ++++++++++++++++++++++++++++++------------- 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/dashboard/changes.txt b/dashboard/changes.txt index 2bba828..2eb06ce 100755 --- a/dashboard/changes.txt +++ b/dashboard/changes.txt @@ -1,3 +1,11 @@ +xlx db v2.3.9 + +redesign for the callinghome.php + +- "config.inc.php" +- "index.php" +- "functions.php" + xlx db v2.3.8 add support for network traffic statistics via vnstat. diff --git a/dashboard/index.php b/dashboard/index.php index a71586a..a8021ab 100755 --- a/dashboard/index.php +++ b/dashboard/index.php @@ -2,8 +2,8 @@ session_start(); -if (file_exists("./pgs/functions.php")) { require_once("./pgs/functions.php"); } else { die("functions.php does not exist."); } -if (file_exists("./pgs/config.inc.php")) { require_once("./pgs/config.inc.php"); } else { die("config.inc.php does not exist."); } +if (file_exists("./pgs/functions.php")) { require_once("./pgs/functions.php"); } else { die("functions.php does not exist."); } +if (file_exists("./pgs/config.inc.php")) { require_once("./pgs/config.inc.php"); } else { die("config.inc.php does not exist."); } if (!class_exists('ParseXML')) require_once("./pgs/class.parsexml.php"); if (!class_exists('Node')) require_once("./pgs/class.node.php"); @@ -19,23 +19,49 @@ $Reflector->SetXMLFile($Service['XMLFile']); $Reflector->LoadXML(); -if ($CallingHome['Active']) { - +if ($CallingHome['Active']) { + $CallHomeNow = false; + $LastSync = 0; + $Hash = ""; + if (!file_exists($CallingHome['HashFile'])) { - $Hash = CreateCode(16); - $LastSync = 0; - UpdateHashFile($CallingHome['HashFile'], $LastSync, $Hash); - $CallHomeNow = true; + $Ressource = fopen($CallingHome['HashFile'], "w+"); + if ($Ressource) { + $Hash = CreateCode(16); + @fwrite($Ressource, "'); + @fflush($Ressource); + @fclose($Ressource); + @chmod($HashFile, 0777); + } } else { - include($CallingHome['HashFile']); - if ($LastSync < (time() - $CallingHome['PushDelay'])) { - UpdateHashFile($CallingHome['HashFile'], time(), $Hash); - $CallHomeNow = true; + require_once($CallingHome['HashFile']); + } + + if (@file_exists($CallingHome['LastCallHomefile'])) { + if (@is_readable($CallingHome['LastCallHomefile'])) { + $tmp = @file($CallingHome['LastCallHomefile']); + if (isset($tmp[0])) { + $LastSync = $tmp[0]; + } + unset($tmp); } } - + + if ($LastSync < (time() - $CallingHome['PushDelay'])) { + $CallHomeNow = true; + $Ressource = @fopen($CallingHome['LastCallHomefile'], "w+"); + if ($Ressource) { + @fwrite($Ressource, time()); + @fflush($Ressource); + @fclose($Ressource); + @chmod($HashFile, 0777); + } + } + if ($CallHomeNow || isset($_GET['callhome'])) { $Reflector->SetCallingHome($CallingHome, $Hash); $Reflector->ReadInterlinkFile(); @@ -43,7 +69,7 @@ if ($CallingHome['Active']) { $Reflector->PrepareReflectorXML(); $Reflector->CallHome(); } - + } else { $Hash = ""; @@ -60,7 +86,7 @@ else { - + <?php echo $Reflector->GetReflectorName(); ?> Reflector Dashboard @@ -69,8 +95,8 @@ else { if ($PageOptions['PageRefreshActive']) { echo ' '; } - + if (!isset($_GET['show'])) $_GET['show'] = ""; ?> @@ -106,14 +132,14 @@ else { Repeaters / Nodes (NodeCount(); ?>) Peers (PeerCount(); ?>) Reflectorlist - ircDDB live + D-Star live Traffic'; + echo '">Traffic statistics'; } ?> From 74cede01af8b8f3ac486af9fb54bfc04c8ba068b Mon Sep 17 00:00:00 2001 From: LX1IQ Date: Fri, 19 Jan 2018 10:15:55 +0100 Subject: [PATCH 029/244] db v2.3.9 redesign for the callinghome.php --- dashboard/pgs/config.inc.php | 16 +++------------- dashboard/pgs/functions.php | 36 ------------------------------------ 2 files changed, 3 insertions(+), 49 deletions(-) diff --git a/dashboard/pgs/config.inc.php b/dashboard/pgs/config.inc.php index 6c8e513..5e33629 100755 --- a/dashboard/pgs/config.inc.php +++ b/dashboard/pgs/config.inc.php @@ -17,7 +17,7 @@ $VNStat = array(); $PageOptions['ContactEmail'] = 'your_email'; // Support E-Mail address -$PageOptions['DashboardVersion'] = '2.3.8'; // Dashboard Version +$PageOptions['DashboardVersion'] = '2.3.9'; // Dashboard Version $PageOptions['PageRefreshActive'] = true; // Activate automatic refresh $PageOptions['PageRefreshDelay'] = '10000'; // Page refresh time in miliseconds @@ -47,7 +47,6 @@ $PageOptions['MetaRevisit'] = 'After 30 Days'; $PageOptions['MetaRobots'] = 'index,follow'; // Meta Tag Values, usefull for Search Engine $PageOptions['UserPage']['ShowFilter'] = true; // Show Filter on Users page - $PageOptions['Traffic']['Show'] = false; // Enable vnstat traffic statistics $Service['PIDFile'] = '/var/log/xlxd.pid'; @@ -60,6 +59,7 @@ $CallingHome['PushDelay'] = 600; $CallingHome['Country'] = "your_country"; // Country $CallingHome['Comment'] = "your_comment"; // Comment. Max 100 character $CallingHome['HashFile'] = "/tmp/callinghome.php"; // Make sure the apache user has read and write permissions in this folder. +$CallingHome['LastCallHomefile'] = "/tmp/lastcallhome.php"; // lastcallhome.php can remain in the tmp folder $CallingHome['OverrideIPAddress'] = ""; // Insert your IP address here. Leave blank for autodetection. No need to enter a fake address. $CallingHome['InterlinkFile'] = "/xlxd/xlxd.interlink"; // Path to interlink file @@ -68,14 +68,4 @@ $VNStat['Interfaces'][0]['Name'] = 'eth0'; $VNStat['Interfaces'][0]['Address'] = 'eth0'; $VNStat['Binary'] = '/usr/bin/vnstat'; -/* - include an extra config file for people who dont like to mess with shipped config.ing.php - this makes updating dashboard from git a little bit easier -*/ - - if (file_exists("../config.inc.php")) { - include ("../config.inc.php"); - } - - -?> +?> \ No newline at end of file diff --git a/dashboard/pgs/functions.php b/dashboard/pgs/functions.php index ea68472..74be9ee 100755 --- a/dashboard/pgs/functions.php +++ b/dashboard/pgs/functions.php @@ -60,42 +60,6 @@ function CreateCode ($laenge) { return $out; } - -function UpdateHashFile($HashFile, $newLastSync, $newHash) { - if (version_compare(phpversion(), "5.6", ">=")) { - $Ressource = @fopen($HashFile, "c"); - if ($Ressource) { - if (flock($Ressource, LOCK_EX)) { - @fwrite($Ressource, "'); - - @fflush($Ressource); - @ftruncate($Ressource, ftell($Ressource)); - @flock($Ressource, LOCK_UN); - } - @fclose($Ressource); - @chmod($HashFile, 0777); - return true; - } - } - else { - $Ressource = @fopen($HashFile, "w"); - if ($Ressource) { - @fwrite($Ressource, "'); - @fclose($Ressource); - @exec("chmod 777 ".$CallingHome['HashFile']); - $CallHomeNow = true; - } - } - - return false; -} - function VNStatLocalize($str) { global $L; if (isset($L[$str])) { From 85b8b6075397000b5c8817432eaa94c10b1e0cc0 Mon Sep 17 00:00:00 2001 From: LX1IQ Date: Fri, 19 Jan 2018 12:50:06 +0100 Subject: [PATCH 030/244] db v2.3.9 --- dashboard/pgs/config.inc.php | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/dashboard/pgs/config.inc.php b/dashboard/pgs/config.inc.php index 5e33629..ff17a52 100755 --- a/dashboard/pgs/config.inc.php +++ b/dashboard/pgs/config.inc.php @@ -19,20 +19,20 @@ $PageOptions['ContactEmail'] = 'your_email'; // Support $PageOptions['DashboardVersion'] = '2.3.9'; // Dashboard Version -$PageOptions['PageRefreshActive'] = true; // Activate automatic refresh -$PageOptions['PageRefreshDelay'] = '10000'; // Page refresh time in miliseconds +$PageOptions['PageRefreshActive'] = true; // Activate automatic refresh +$PageOptions['PageRefreshDelay'] = '10000'; // Page refresh time in miliseconds $PageOptions['RepeatersPage'] = array(); -$PageOptions['RepeatersPage']['LimitTo'] = 99; // Number of Repeaters to show +$PageOptions['RepeatersPage']['LimitTo'] = 99; // Number of Repeaters to show $PageOptions['RepeatersPage']['IPModus'] = 'ShowFullIP'; // See possible options above -$PageOptions['RepeatersPage']['MasqueradeCharacter'] = '*'; // Character used for masquerade +$PageOptions['RepeatersPage']['MasqueradeCharacter'] = '*'; // Character used for masquerade $PageOptions['PeerPage'] = array(); -$PageOptions['PeerPage']['LimitTo'] = 99; // Number of peers to show +$PageOptions['PeerPage']['LimitTo'] = 99; // Number of peers to show $PageOptions['PeerPage']['IPModus'] = 'ShowFullIP'; // See possible options above $PageOptions['PeerPage']['MasqueradeCharacter'] = '*'; // Character used for masquerade -$PageOptions['LastHeardPage']['LimitTo'] = 39; // Number of stations to show +$PageOptions['LastHeardPage']['LimitTo'] = 39; // Number of stations to show $PageOptions['ModuleNames'] = array(); // Module nomination $PageOptions['ModuleNames']['A'] = 'Int.'; @@ -53,7 +53,7 @@ $Service['PIDFile'] = '/var/log/xlxd.pid'; $Service['XMLFile'] = '/var/log/xlxd.xml'; $CallingHome['Active'] = false; // xlx phone home, true or false -$CallingHome['MyDashBoardURL'] = 'http://your_dashboard'; // dashboard url +$CallingHome['MyDashBoardURL'] = 'http://your_dashboard'; // dashboard url $CallingHome['ServerURL'] = 'http://xlxapi.rlx.lu/api.php'; // database server, do not change !!!! $CallingHome['PushDelay'] = 600; // push delay in seconds $CallingHome['Country'] = "your_country"; // Country @@ -68,4 +68,13 @@ $VNStat['Interfaces'][0]['Name'] = 'eth0'; $VNStat['Interfaces'][0]['Address'] = 'eth0'; $VNStat['Binary'] = '/usr/bin/vnstat'; +/* +include an extra config file for people who dont like to mess with shipped config.ing.php +this makes updating dashboard from git a little bit easier +*/ + +if (file_exists("../config.inc.php")) { + include ("../config.inc.php"); +} + ?> \ No newline at end of file From 247057a4ee15b5136df8ed014d9599d1e7b232e7 Mon Sep 17 00:00:00 2001 From: LX1IQ Date: Fri, 19 Jan 2018 12:57:18 +0100 Subject: [PATCH 031/244] db v2.3.9 --- dashboard/pgs/config.inc.php | 58 ++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/dashboard/pgs/config.inc.php b/dashboard/pgs/config.inc.php index ff17a52..2616de8 100755 --- a/dashboard/pgs/config.inc.php +++ b/dashboard/pgs/config.inc.php @@ -15,53 +15,53 @@ $CallingHome = array(); $PageOptions = array(); $VNStat = array(); -$PageOptions['ContactEmail'] = 'your_email'; // Support E-Mail address +$PageOptions['ContactEmail'] = 'your_email'; // Support E-Mail address -$PageOptions['DashboardVersion'] = '2.3.9'; // Dashboard Version +$PageOptions['DashboardVersion'] = '2.3.9'; // Dashboard Version -$PageOptions['PageRefreshActive'] = true; // Activate automatic refresh -$PageOptions['PageRefreshDelay'] = '10000'; // Page refresh time in miliseconds +$PageOptions['PageRefreshActive'] = true; // Activate automatic refresh +$PageOptions['PageRefreshDelay'] = '10000'; // Page refresh time in miliseconds $PageOptions['RepeatersPage'] = array(); -$PageOptions['RepeatersPage']['LimitTo'] = 99; // Number of Repeaters to show -$PageOptions['RepeatersPage']['IPModus'] = 'ShowFullIP'; // See possible options above -$PageOptions['RepeatersPage']['MasqueradeCharacter'] = '*'; // Character used for masquerade +$PageOptions['RepeatersPage']['LimitTo'] = 99; // Number of Repeaters to show +$PageOptions['RepeatersPage']['IPModus'] = 'ShowFullIP'; // See possible options above +$PageOptions['RepeatersPage']['MasqueradeCharacter'] = '*'; // Character used for masquerade $PageOptions['PeerPage'] = array(); -$PageOptions['PeerPage']['LimitTo'] = 99; // Number of peers to show -$PageOptions['PeerPage']['IPModus'] = 'ShowFullIP'; // See possible options above -$PageOptions['PeerPage']['MasqueradeCharacter'] = '*'; // Character used for masquerade +$PageOptions['PeerPage']['LimitTo'] = 99; // Number of peers to show +$PageOptions['PeerPage']['IPModus'] = 'ShowFullIP'; // See possible options above +$PageOptions['PeerPage']['MasqueradeCharacter'] = '*'; // Character used for masquerade -$PageOptions['LastHeardPage']['LimitTo'] = 39; // Number of stations to show +$PageOptions['LastHeardPage']['LimitTo'] = 39; // Number of stations to show -$PageOptions['ModuleNames'] = array(); // Module nomination +$PageOptions['ModuleNames'] = array(); // Module nomination $PageOptions['ModuleNames']['A'] = 'Int.'; $PageOptions['ModuleNames']['B'] = 'Regional'; $PageOptions['ModuleNames']['C'] = 'National'; $PageOptions['ModuleNames']['D'] = ''; -$PageOptions['MetaDescription'] = 'XLX is a D-Star Reflector System for Ham Radio Operators.'; // Meta Tag Values, usefull for Search Engine -$PageOptions['MetaKeywords'] = 'Ham Radio, D-Star, XReflector, XLX, XRF, DCS, REF, '; // Meta Tag Values, usefull forSearch Engine -$PageOptions['MetaAuthor'] = 'LX1IQ'; // Meta Tag Values, usefull for Search Engine -$PageOptions['MetaRevisit'] = 'After 30 Days'; // Meta Tag Values, usefull for Search Engine -$PageOptions['MetaRobots'] = 'index,follow'; // Meta Tag Values, usefull for Search Engine +$PageOptions['MetaDescription'] = 'XLX is a D-Star Reflector System for Ham Radio Operators.'; // Meta Tag Values, usefull for Search Engine +$PageOptions['MetaKeywords'] = 'Ham Radio, D-Star, XReflector, XLX, XRF, DCS, REF, '; // Meta Tag Values, usefull forSearch Engine +$PageOptions['MetaAuthor'] = 'LX1IQ'; // Meta Tag Values, usefull for Search Engine +$PageOptions['MetaRevisit'] = 'After 30 Days'; // Meta Tag Values, usefull for Search Engine +$PageOptions['MetaRobots'] = 'index,follow'; // Meta Tag Values, usefull for Search Engine -$PageOptions['UserPage']['ShowFilter'] = true; // Show Filter on Users page -$PageOptions['Traffic']['Show'] = false; // Enable vnstat traffic statistics +$PageOptions['UserPage']['ShowFilter'] = true; // Show Filter on Users page +$PageOptions['Traffic']['Show'] = false; // Enable vnstat traffic statistics $Service['PIDFile'] = '/var/log/xlxd.pid'; $Service['XMLFile'] = '/var/log/xlxd.xml'; -$CallingHome['Active'] = false; // xlx phone home, true or false -$CallingHome['MyDashBoardURL'] = 'http://your_dashboard'; // dashboard url -$CallingHome['ServerURL'] = 'http://xlxapi.rlx.lu/api.php'; // database server, do not change !!!! -$CallingHome['PushDelay'] = 600; // push delay in seconds -$CallingHome['Country'] = "your_country"; // Country -$CallingHome['Comment'] = "your_comment"; // Comment. Max 100 character -$CallingHome['HashFile'] = "/tmp/callinghome.php"; // Make sure the apache user has read and write permissions in this folder. -$CallingHome['LastCallHomefile'] = "/tmp/lastcallhome.php"; // lastcallhome.php can remain in the tmp folder -$CallingHome['OverrideIPAddress'] = ""; // Insert your IP address here. Leave blank for autodetection. No need to enter a fake address. -$CallingHome['InterlinkFile'] = "/xlxd/xlxd.interlink"; // Path to interlink file +$CallingHome['Active'] = false; // xlx phone home, true or false +$CallingHome['MyDashBoardURL'] = 'http://your_dashboard'; // dashboard url +$CallingHome['ServerURL'] = 'http://xlxapi.rlx.lu/api.php'; // database server, do not change !!!! +$CallingHome['PushDelay'] = 600; // push delay in seconds +$CallingHome['Country'] = "your_country"; // Country +$CallingHome['Comment'] = "your_comment"; // Comment. Max 100 character +$CallingHome['HashFile'] = "/tmp/callinghome.php"; // Make sure the apache user has read and write permissions in this folder. +$CallingHome['LastCallHomefile'] = "/tmp/lastcallhome.php"; // lastcallhome.php can remain in the tmp folder +$CallingHome['OverrideIPAddress'] = ""; // Insert your IP address here. Leave blank for autodetection. No need to enter a fake address. +$CallingHome['InterlinkFile'] = "/xlxd/xlxd.interlink"; // Path to interlink file $VNStat['Interfaces'] = array(); $VNStat['Interfaces'][0]['Name'] = 'eth0'; From c3920519872deb15cf7afc3673221d0541085cdf Mon Sep 17 00:00:00 2001 From: LX3JL Date: Tue, 23 Jan 2018 12:22:19 +0100 Subject: [PATCH 032/244] xlxd 2.2.2 BM interlinking --- src/cbmclient.cpp | 54 ++++++++++++++++++++++++++ src/cbmclient.h | 65 +++++++++++++++++++++++++++++++ src/cbmpeer.cpp | 91 ++++++++++++++++++++++++++++++++++++++++++++ src/cbmpeer.h | 62 ++++++++++++++++++++++++++++++ src/cxlxprotocol.cpp | 46 +++++++++++++++++++++- src/cxlxprotocol.h | 5 +++ src/main.h | 2 +- 7 files changed, 322 insertions(+), 3 deletions(-) create mode 100644 src/cbmclient.cpp create mode 100644 src/cbmclient.h create mode 100644 src/cbmpeer.cpp create mode 100644 src/cbmpeer.h diff --git a/src/cbmclient.cpp b/src/cbmclient.cpp new file mode 100644 index 0000000..8dc4b0c --- /dev/null +++ b/src/cbmclient.cpp @@ -0,0 +1,54 @@ +// +// cbmclient.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 20/01/2017. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd 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 of the License, or +// (at your option) any later version. +// +// xlxd 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 Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include +#include "main.h" +#include "cbmclient.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructors + +CBmClient::CBmClient() +{ +} + +CBmClient::CBmClient(const CCallsign &callsign, const CIp &ip, char reflectorModule) + : CClient(callsign, ip, reflectorModule) +{ +} + +CBmClient::CBmClient(const CBmClient &client) + : CClient(client) +{ +} + +//////////////////////////////////////////////////////////////////////////////////////// +// status + +bool CBmClient::IsAlive(void) const +{ + return (m_LastKeepaliveTime.DurationSinceNow() < XLX_KEEPALIVE_TIMEOUT); +} + diff --git a/src/cbmclient.h b/src/cbmclient.h new file mode 100644 index 0000000..fc47778 --- /dev/null +++ b/src/cbmclient.h @@ -0,0 +1,65 @@ +// +// cbmclient.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 20/01/2017. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd 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 of the License, or +// (at your option) any later version. +// +// xlxd 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 Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cbmclient_h +#define cbmclient_h + + +#include "cclient.h" +#include "cxlxclient.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CBmClient : public CClient +{ +public: + // constructors + CBmClient(); + CBmClient(const CCallsign &, const CIp &, char = ' '); + CBmClient(const CBmClient &); + + // destructor + virtual ~CBmClient() {}; + + // identity + int GetProtocol(void) const { return PROTOCOL_XLX; } + int GetProtocolRevision(void) const { return XLX_PROTOCOL_REVISION_2; } + const char *GetProtocolName(void) const { return "XLX"; } + int GetCodec(void) const { return CODEC_AMBE2PLUS; } + bool IsPeer(void) const { return true; } + + // status + bool IsAlive(void) const; + + // reporting + void WriteXml(std::ofstream &) {} +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cbmclient_h */ diff --git a/src/cbmpeer.cpp b/src/cbmpeer.cpp new file mode 100644 index 0000000..36c38fb --- /dev/null +++ b/src/cbmpeer.cpp @@ -0,0 +1,91 @@ +// +// cbmpeer.cpp +// xlxd +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd 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 of the License, or +// (at your option) any later version. +// +// xlxd 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 Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "main.h" +#include +#include "creflector.h" +#include "cbmpeer.h" +#include "cbmclient.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + + +CBmPeer::CBmPeer() +{ +} + +CBmPeer::CBmPeer(const CCallsign &callsign, const CIp &ip, char *modules, const CVersion &version) +: CPeer(callsign, ip, modules, version) +{ + std::cout << "Adding BM peer" << std::endl; + + // and construct all xlx clients + for ( int i = 0; i < ::strlen(modules); i++ ) + { + // create + CBmClient *client = new CBmClient(callsign, ip, modules[i]); + // and append to vector + m_Clients.push_back(client); + } +} + +CBmPeer::CBmPeer(const CBmPeer &peer) +: CPeer(peer) +{ + for ( int i = 0; i < peer.m_Clients.size(); i++ ) + { + CBmClient *client = new CBmClient((const CBmClient &)*(peer.m_Clients[i])); + // grow vector capacity if needed + if ( m_Clients.capacity() == m_Clients.size() ) + { + m_Clients.reserve(m_Clients.capacity()+10); + } + // and append + m_Clients.push_back(client); + + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// destructors + +CBmPeer::~CBmPeer() +{ +} + +//////////////////////////////////////////////////////////////////////////////////////// +// status + +bool CBmPeer::IsAlive(void) const +{ + return (m_LastKeepaliveTime.DurationSinceNow() < XLX_KEEPALIVE_TIMEOUT); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// revision helper + +int CBmPeer::GetProtocolRevision(const CVersion &version) +{ + return XLX_PROTOCOL_REVISION_2; +} + diff --git a/src/cbmpeer.h b/src/cbmpeer.h new file mode 100644 index 0000000..b92d238 --- /dev/null +++ b/src/cbmpeer.h @@ -0,0 +1,62 @@ +// +// cbmpeer.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 20/01/2017. +// Copyright © 2016 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd 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 of the License, or +// (at your option) any later version. +// +// xlxd 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 Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cbmpeer_h +#define cbmpeer_h + + +#include "cpeer.h" +#include "cbmclient.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CBmPeer : public CPeer +{ +public: + // constructors + CBmPeer(); + CBmPeer(const CCallsign &, const CIp &, char *, const CVersion &); + CBmPeer(const CBmPeer &); + + // destructor + ~CBmPeer(); + + // status + bool IsAlive(void) const; + + // identity + int GetProtocol(void) const { return PROTOCOL_XLX; } + const char *GetProtocolName(void) const { return "XLX"; } + + // revision helper + static int GetProtocolRevision(const CVersion &); +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cbmpeer_h */ diff --git a/src/cxlxprotocol.cpp b/src/cxlxprotocol.cpp index 0a6dd7c..f01fb9a 100644 --- a/src/cxlxprotocol.cpp +++ b/src/cxlxprotocol.cpp @@ -25,6 +25,7 @@ #include "main.h" #include #include "cxlxpeer.h" +#include "cbmpeer.h" #include "cxlxprotocol.h" #include "creflector.h" #include "cgatekeeper.h" @@ -118,7 +119,7 @@ void CXlxProtocol::Task(void) { // acknowledge connecting request // following is version dependent - switch ( CXlxPeer::GetProtocolRevision(Version) ) + switch ( GetConnectingPeerProtocolRevision(Callsign, Version) ) { case XLX_PROTOCOL_REVISION_0: { @@ -163,7 +164,7 @@ void CXlxProtocol::Task(void) { // create the new peer // this also create one client per module - CXlxPeer *peer = new CXlxPeer(Callsign, Ip, Modules, Version); + CPeer *peer = CreateNewPeer(Callsign, Ip, Modules, Version); // append the peer to reflector peer list // this also add all new clients to reflector client list @@ -722,3 +723,44 @@ bool CXlxProtocol::EncodeDvLastFramePacket(const CDvLastFramePacket &Packet, CBu return true; } + +//////////////////////////////////////////////////////////////////////////////////////// +// protocol revision helper + +int CXlxProtocol::GetConnectingPeerProtocolRevision(const CCallsign &Callsign, const CVersion &Version) +{ + int protrev; + + // BM ? + if ( Callsign.HasSameCallsignWithWildcard(CCallsign("BM*")) ) + { + protrev = CBmPeer::GetProtocolRevision(Version); + } + // otherwise, assume native xlx + else + { + protrev = CXlxPeer::GetProtocolRevision(Version); + } + + // done + return protrev; +} + +CPeer *CXlxProtocol::CreateNewPeer(const CCallsign &Callsign, const CIp &Ip, char *Modules, const CVersion &Version) +{ + CPeer *peer = NULL; + + // BM ? + if ( Callsign.HasSameCallsignWithWildcard(CCallsign("BM*")) ) + { + peer = new CBmPeer(Callsign, Ip, Modules, Version); + } + else + { + peer = new CXlxPeer(Callsign, Ip, Modules, Version); + } + + // done + return peer; +} + diff --git a/src/cxlxprotocol.h b/src/cxlxprotocol.h index 1d23c50..1b8d8d9 100644 --- a/src/cxlxprotocol.h +++ b/src/cxlxprotocol.h @@ -32,6 +32,7 @@ //////////////////////////////////////////////////////////////////////////////////////// +class CPeer; //////////////////////////////////////////////////////////////////////////////////////// // class @@ -82,6 +83,10 @@ protected: bool EncodeDvFramePacket(const CDvFramePacket &, CBuffer *) const; bool EncodeDvLastFramePacket(const CDvLastFramePacket &, CBuffer *) const; + // protocol revision helper + int GetConnectingPeerProtocolRevision(const CCallsign &, const CVersion &); + CPeer *CreateNewPeer(const CCallsign &, const CIp &, char *, const CVersion &); + protected: // time CTimePoint m_LastKeepaliveTime; diff --git a/src/main.h b/src/main.h index ac77c96..fa7acab 100644 --- a/src/main.h +++ b/src/main.h @@ -49,7 +49,7 @@ #define VERSION_MAJOR 2 #define VERSION_MINOR 2 -#define VERSION_REVISION 1 +#define VERSION_REVISION 2 // global ------------------------------------------------------ From d595746671111a3c6f9e7538555bb127139df5e1 Mon Sep 17 00:00:00 2001 From: LX1IQ Date: Fri, 2 Feb 2018 13:34:20 +0100 Subject: [PATCH 033/244] db v2.3.9 update ja flag --- dashboard/img/flags/jp.png | Bin 160 -> 18117 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/dashboard/img/flags/jp.png b/dashboard/img/flags/jp.png index fd1ce056a5dbcb17f68c7eca726c476ade3c19e6..c1b7aacce4c137aecac1126316026b1f3d160b0c 100755 GIT binary patch literal 18117 zcmeI4c|4Ts+rS@3$G){_lg^-o%x;@$j2LT37@>X4JToTDjG4*SNmQ1Wlcci6(P~Xa zDWQ<4glI=Og`^JsPLwvh&mi&UC;f!ulsuLna}5$ohz2x zDJ#rX002PQ!QR>pde?%Uv*cu;*QgrZHt0>BXYVZl0LAH|rxcKKcs2kixUead6)SwX zLaxA<%R@L&C(%Vhh501(og=FXzJch58*c+zj>7#nfGk?W=|hj6pn9Wf(b*FZ~N zZCR|=kw@yT6;o|&rf6Kxh?U;EH)4;vtD({^g@>{oT1WS8KNh>|=BKBdkNMp%9e7?h zbp8RkE3YrDutBz5PU(<6-qkolDcfrP)W5dZR8>FiKrM|_Sil3+3jjGK0OJ}Pr4LNm3~acrqZ0z`NCsxuzVRgevp8R^ z%K##kTtF&bK(L8iEr$%0_4Gu|*QBF7S-f6X8|JuT)VaBsI!%x!WHe=w-MzYlAcx2XEy7W88MQUEc@@E48@TP=YaaT`H zI>LAR(}v*T*A|0z84H03ro;X~09a?e07bo7Xi*~v0M;3i24^fa`YUD|mdnhm__eh{ z@$&|g9aamfDy`J46c`cn_#5=|wpr;#*PYQc*nqyfRMV*3)i*kUr-G{VxTs>zQ~gMj zORvx?jg+5ad0lQgO{+Op%4e6C&R)4$$x-jMD(t6B+r0pBBX)+Xwqv}pJ=Sd(72#p8 z`No|83n6BW?WOo7*8$wN&(WoJ*aMLTuI6`@&QprBOy};fJ*s${A;hM{&CE!>%|AES ze2>YIuG_i3s?Bj^N)4v`#cWx8l|Kemebwr$nW20Fz4okjo>Ff0+%qflq)T?br8k@n zo-!@crd-}+O0X4zHs8|T%gJl`L;Ebmyah&6o8?XArFXbiF49iOay+MdY3hfn**-XI zjF-Kk&i!d|)6r_1V<}ap3#RR);1(6{`^Dkxaca}FCfg>Cn@+c3cB+y6T%Cj)%?!T+ zS;7HB#Nz8w9cdj`JMbME9a?WzE2Q}>4Lttd{h6NI&5g_EbgFe4b;^KAI7^RXDQ=Im zj!>r>$E~tDb)?Acv};V(bfZlvgu3lB9UM=bI(qQVT;Ex~1MJf;gqNm1J~)4Q;%(2S z+HSiSi7%x4gO!w{)wkEgvh?Pp&X%3cniD+xbduA_Id^u?si9&_v9FT4l2wzr9$p4T z2EVJv-&8ob?4a&J4FfbaJLhE1lN?2lK95>zT=qKmE%~+{c=z^f`MZa5J#x}kymHrY zXJs$RipgPRaj6!^!@UQ0%6MOOy6QqX?wWCSeS2v&LXkeFFvL*HPdPVwQ>1Z~?%BFW zzXutVBn@ho>^9HIK0=`2m*G7N-TDqNEo9r10^c7NE|>4p%S`MG-}Pk1YyD^X8YhjM zim~7tznnvfhrDv_b6s=oUX_{T_hhD>EVC)`{uHTVS+i`>nniZ3xcXnyy4)YRKh9Lh zoat;%e4CHT@jIb$q{KS-3MaESDgE-gh;%1z=9AE}u8;G!pa-dg0UtBp+9`R-6e=x{ zX;G@5O}b_r$R%l_Yg+zsrL4WWlyZW0ke2v#M|^Q3>DWEnKWtU^u&dX!FK7>_4yZn+ zE2FEW>*)N_xi`hlx%j01$t6xpoNDTB)g7(7pKP3Jj(vdZO6^L0klJ=(!>W)~hDBLL z7eR-Dj#VeUdy7grQL6~vC%imXtu3@Ym3C_3{gP89nNu^1mnSUGO~22&dgh;^8%5a{ z=A7w#DAST(6i~E<#`2lPx^ON3YHvbW!j?;0{unfuRgQV2lB>B&GhA?vKZCfE_#~zN zaIeLdYyF41(JZe9wT6fI_M+*v`c>>S_tZOftXZsL!S=q`-8&t()uIx~s8o&W$Jfg` zmI>bmu6-KT^foQEFtxHLtL1dBNiVxMruENTMt|z3d*cPSuU@D8<=WY3q4?)Pj9*(` zxC_N4l6AgI2Te!Ed!Bl5=E8~KoB`VFS>$(Q{-5pSc9Q86+!OEhw@k|~T)$B3a+RCD zKB|qfRcDwn95&ntWJWBMVoC3ew2WN+%cu8YziVCDYe8%Q-xWOSvD}kUY^iP;x9!xn z&Wcl7zSsZWcS>uZE2%!do~;tKBK*bu+p@IhwB1+3l@>+CM0rMA?B&IJ&27Xc8V5JF zQUq<*AFU5rKS^a_T?nU<%Zv@kIjD`KX46YXmn^DLOz&IwywbcJ{2k{JPzL!X9RJI0 zpL_aZo~1ogY~MU934MJnFZ4)#Wq(xW%xu&&L*k*OPO;0)4*bhf%^+*$o`r9w2QS>K z5L&5O$p3&1HV=#{7`n#UeE~iHpzIC5!hLzadLtEztWLh5 zuF`v8AX~r!J(*YPcHSf$%1vKf*K@1zX3IX)!>|7w?wM9O?dkra6VZ;INKLQjWqBJ3 z1*E{x7KL{8Ul&KOl*uDq+*M={aL*;T$1^cMyi7OD*)MWk#m#LM=cW8!DS9Y$Ms0c3 z`k-g6ix=9v?L6&cbeaZPedn(H87;FC)we#>YYpkth~eJ6lH7XWTy}0%F{6Rm#O!|l z1ev~C7pfc@O;L@TqXXB7bL2_xnSi z0@j{d9uU}5Bi$rDD66AV^q~0eJ}DCpVgOKZjl@BzkCw**{PDW{3+16~SWl7*R5MOmIr6_HMZIf$V6>#7>a)zr%L%XE;+6?S~>bvx*3^3cxJ&L?EaidX<#(y zrq3EWI#ioKYbHAU{k>NY>Kl_9k0icHOt=#8`-#Dq<*(+PZmF)Uo`3m92p+qxf90p1 zis05@&DY1;R9!lLM{Ly__Wf}7t^4iFPYH7p5{9QpC-r@%dI`OporrX9 z0E))o`hcjA03Ngv1ptyo2#-eh1BD15kj3VhYrihJqK#lP%(Xp@oiWZl3h2wW595RG zVauuXFh4qhp>07{AcYVi2LYgvh6oAp=Lm=)=Gr5EiO^?JGg=!l(naWJu5Bp_i12b= zfuL~tAi@}Bf}~?{c!Vhdg*7%Kn3@?Pa2Tu!8iPY)@kk7oh=m>m#Mq%tR)9W{_zWh| z&DwS>93+`*`wE3TA{reW9E=Laqquw)8cQG$L~d|6B-8^b2;~TAAxMruXVl3TKh~gt z&S&$4Y%T{O@=NpKZW5YnYl{MnA7kSR;Ef032*%h!6wx6x9vX|npnqWG>@40aAmF=} z0-;S1bS7gX`))u1HIxUU-9Q0%6Q2&+1c4l(&i77&K_BtgADD?Wqh=$iFgimdDF28QK1dUC z`BW~~pFEmn6W^a~#s-SO8lx}>Jx?}=!3`GZlhAO(iCLfU30l*HAQ{R^Gb9EFJ*Zd$ z5r-ifnJmT-h#1TyC!;-KKAgD>HZ$~lK5#@lX0ne-J>fo}Rm7kPX+PyLYreVCe{ScG{b@; zL;l*?H-Q{D0wIk<2cbX^WE2|;Vuq#D@iPM?usvG%!Bf8q#C({fnT?N zQTVe*stS+B7l5M0YOeidwT_pe@hT&-8Cmp18eLS6$aGOkgA5Y-tJaCr8n+xXV}Hxs z1dB7m5sX1310sQBGK~mGf-wd};!Lncrc5l2Ze#{!@TmVu=0A=R>BSlYIOij#WSl`u7s==N9|FnRt^c z=>K=(O{`wJFO9kQN}Y+uiI}AE zJR?%L{#-tExdF*|68d}1-?f5%g2__~;911(x&7^*l{D?02 zM5xL_D}H1FkRh9I+Ti`YMAg!N(?~Tm#bKZWjTOC)nNC*2O})jNPFBNBCoDZUY#|vp z77f-s$x5^<8HMT&ZJe}mv}xftppbgh63+*wmEeK_5id$`iRS~;N^rq|h!-We#Pfk^CAeTf#ETMK;`zX| z5?nAK;zbEA@qA!f2`(5A@uCEmcs?+#1Q!g5cu|5&JRg`=f(r&jyePpXo)1hb!36^% zUXPq{3NXD~fi;kq( zGE%cQO<9(LP+!wu2Zj`OB6cMWoxGu>efeE~VAVQX^S@R4YQYWA!5iATv#2q$R_b_; zR?F0`!)`KU+U2&byK=NlQfy)_>+E?~r`5c|tx&&lubR+EV_u`>9@QKAdG;tQ?NA1b z!Z*y!>dm;@)M!Y2-!lD7(UVvedFUL0;bCbY=!Jg4zcj+2`-B0&!DhL2zSV|p{{?aG B-q-*D literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!VDxcS|@x5QW60^A+Gxv)Q(76pL2-0=#&0* z*2*7GU;h91@Bjb*TMB}7fohyRT^vIsE+=cav+|^TJUxfi_tL~4jyya(t2pXrGEF!? zh41-vgN6p{Iq^KtW*`39ny}Bp&u&*$$>$4ioy+;#s!x8mXOO&IxG`nvihiJR44$rj JF6*2UngBnIJ{14} From 7ec97b2c9e5d11d2c1c00105653a56b12fec1884 Mon Sep 17 00:00:00 2001 From: narspt Date: Fri, 2 Feb 2018 20:26:33 +0000 Subject: [PATCH 034/244] use location.reload() to keep page scroll position use location.reload() to keep page scroll position on page refreshes, but keep using location.href method for POST requests to avoid browser asking to resubmit data on reloads --- dashboard/index.php | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/dashboard/index.php b/dashboard/index.php index a8021ab..9f1d9ab 100755 --- a/dashboard/index.php +++ b/dashboard/index.php @@ -97,12 +97,19 @@ else { '; } if (!isset($_GET['show'])) $_GET['show'] = ""; From 90fe828fd1163a5a7bbaa754fd440afedd45afda Mon Sep 17 00:00:00 2001 From: narspt Date: Tue, 6 Feb 2018 08:03:23 +0000 Subject: [PATCH 037/244] don't use location.reload() if there is a 'do' GET request --- dashboard/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dashboard/index.php b/dashboard/index.php index 9f1d9ab..8a23f81 100755 --- a/dashboard/index.php +++ b/dashboard/index.php @@ -98,7 +98,7 @@ else { var PageRefresh; function ReloadPage() {'; - if ($_SERVER['REQUEST_METHOD'] === 'POST') { + if (($_SERVER['REQUEST_METHOD'] === 'POST') || isset($_GET['do'])) { echo ' document.location.href = "./index.php'; if (isset($_GET['show'])) { From e4b196e72af2caf1f94c1ae81eb9f59d5647f48c Mon Sep 17 00:00:00 2001 From: narspt Date: Tue, 6 Feb 2018 08:12:24 +0000 Subject: [PATCH 038/244] db2: don't use location.reload() if there is a 'do' GET request --- dashboard2/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dashboard2/index.php b/dashboard2/index.php index cf29a95..25fea68 100644 --- a/dashboard2/index.php +++ b/dashboard2/index.php @@ -110,7 +110,7 @@ if ($CallingHome['Active']) { var PageRefresh; function ReloadPage() {'; - if ($_SERVER['REQUEST_METHOD'] === 'POST') { + if (($_SERVER['REQUEST_METHOD'] === 'POST') || isset($_GET['do'])) { echo ' document.location.href = "./index.php'; if (isset($_GET['show'])) { From 2e9ff8166f4c82f448a52b653467f54b8d693020 Mon Sep 17 00:00:00 2001 From: LX1IQ Date: Mon, 5 Mar 2018 10:30:17 +0100 Subject: [PATCH 039/244] Add flags for missing countries Add flags for missing countries --- dashboard/img/flags/aq.png | Bin 0 -> 16344 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 dashboard/img/flags/aq.png diff --git a/dashboard/img/flags/aq.png b/dashboard/img/flags/aq.png new file mode 100644 index 0000000000000000000000000000000000000000..df8948755f8dd0b8f5afa76a7b6c1da9acd7abea GIT binary patch literal 16344 zcmeI3dpuNmAIFc7OKzn$c4?=nEyb9N!5Cw#jIgELaw*c7Ihf4N%*fjp`)m^MWi82YsmtoqWXrFhy7XRsb^g>j3~|F2~bH;^VTE z$`bHQnQTEIXe#9iAvFNdY^6dbD-@Jq0>NMo-v--#wi1isux+s3<}L&mp*l}au1=Fup#(fo!o)~e z#$F%-G3KTgI2O^2g0ZwRC7N4NNMsWXi9oc#6Rhw=GLAr`60E2MB4*@;rK>{`O~ekO zx-%A!l!N}W!G=gALMk2~5fNb;VP+~21>=cUR#tcd2~Q&7AO%jmo-biaaeVQd;Uu5( zFhDUTq30zO8bml-Gsm)Kyj@c_k4Bipp^3!o4jY(HnbFmXNU$} zE7e{EG9?0$r$E4^53e%UPY5yg_VQI_i81o#@Y#Y0@d6rtyk%^@$FhM8rUaxzP701- zh9j7J5(!l3&zxc`Uzh|~l40ez94-PjCuscyIY{PIq6It$OgTOWw13!43G**yfooxT z*eq(0K*VE8=o}t17{m+t!8H6>BTPBEs;Kq?u0RA03#6OT@Dnw|Tth33>dY5Qn0yxK z%%DRJrW_8N$|hNWEK4#0M_`d4ghVn47Z}7M;4H`>nZz`=AQH@=>|fZwNX-zi!sWYV zBsKeQriR(SNWEOdflfLm7p9Sq{_mEUAk7!0oH$}=eXfVCTWIwTon;ni!4!dZ!O)ucD|>x7>*u!d>1lZR>CfeSJ`uyMpU=;y z75n9U(&UF46xR^t8K&0WnXdA0MtoM=6DK~sbZ%HCRFFaBHIbwx(kGJ~%xfR|$ z-a6)7!RJWmq>*alnqe;a$vb>3ctAG^Al<^;+{~PYAKyIQ;mVPMTptDp+P`A?s+8~e zA1*d@+qqq#D)Qp1xtBEGEi1`Oy3u>oc*W{n3cJAG$0rNe zH0iUT3ktEnIz6Hc)EQPcC|zk6KRtXyRlAOznth@E$1_dd41G7H1iKYpEz?UWw&f+O zW-66lFUj4ZYBcHM8f@+9wY*|n#tzm!3x{b<2DS&5^eg*5?{m%&0rd?1){sC_+BRhw z|NC%e#?grPiVwI8+g_J`*Slju0Y57=j2{?!*z0}W}p z!wjeLjiKc}k0muZ=S)?hrP$iK?*pBsmtf?s%dR0jatULH@2FuwLO(IKFI zu**20w=};a>QL-M%0D`RRB;Bg?38-!T8AS>sW<4%-LACh zXZ`MrJ(M1L&m!Pz7GG?fuM+(-W_`>{Hdd8Td*NiKUs;*|s@mYg^F4agcO@&wSCpOf z80Zl^ST=uAE1;_>3%{5Orp$fiUfOfX>R7UNv*c{!L+?E^RkyylB|X^Q6tgAUDYia|wcFp0|7sDoYLbe{EdAvS z#o6f#?ns?)?R-KaJLH!UZPHD=GB&=IWnOmSaqn2Sd{i`9T$-;kNp(((Cx!s{u`#s| z0`5sWGi_fiq}n|SuQ+^!`m8@$V~fg%Sq9bLMi=4>tPQjQRShZmV)TnDYl6PIql$uX z)*tk?mJeay%woqHS#EEB-gB{2)|pklA&h3||L*36pUQQ$i&IYPu=gz)EXz#RG_~2V z3;QZ`aCKqHC2hLTho|2US{zI&C{ugx7}JY^4oAi`UWKDayzzNUeK%kKhbSJv4_*+`cEVndc~4)FJTag})6yZJ%!f@uST(Lbv4 znrlkI#)KBku($TS@Gz?=~^lIOh&^^cT4^8rM3VUy#^eC>3YA=t#pEFRCMdfg< zuV3iN&U*i@Eo*h9v@f)-j8k-?=>$VRG9D-9{NA=s%wKR8rY`nqMbYtB`GCpIu)Ex+np zDxD8Ah|7y#U3sdJ^5*8x**g4I#InAOJKAYSjdyEkS1WhqbR}uJkh`X1>fC Date: Mon, 5 Mar 2018 10:30:54 +0100 Subject: [PATCH 040/244] Add flags for missing countries Add flags for missing countries --- dashboard2/img/flags/aq.png | Bin 0 -> 16344 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 dashboard2/img/flags/aq.png diff --git a/dashboard2/img/flags/aq.png b/dashboard2/img/flags/aq.png new file mode 100644 index 0000000000000000000000000000000000000000..df8948755f8dd0b8f5afa76a7b6c1da9acd7abea GIT binary patch literal 16344 zcmeI3dpuNmAIFc7OKzn$c4?=nEyb9N!5Cw#jIgELaw*c7Ihf4N%*fjp`)m^MWi82YsmtoqWXrFhy7XRsb^g>j3~|F2~bH;^VTE z$`bHQnQTEIXe#9iAvFNdY^6dbD-@Jq0>NMo-v--#wi1isux+s3<}L&mp*l}au1=Fup#(fo!o)~e z#$F%-G3KTgI2O^2g0ZwRC7N4NNMsWXi9oc#6Rhw=GLAr`60E2MB4*@;rK>{`O~ekO zx-%A!l!N}W!G=gALMk2~5fNb;VP+~21>=cUR#tcd2~Q&7AO%jmo-biaaeVQd;Uu5( zFhDUTq30zO8bml-Gsm)Kyj@c_k4Bipp^3!o4jY(HnbFmXNU$} zE7e{EG9?0$r$E4^53e%UPY5yg_VQI_i81o#@Y#Y0@d6rtyk%^@$FhM8rUaxzP701- zh9j7J5(!l3&zxc`Uzh|~l40ez94-PjCuscyIY{PIq6It$OgTOWw13!43G**yfooxT z*eq(0K*VE8=o}t17{m+t!8H6>BTPBEs;Kq?u0RA03#6OT@Dnw|Tth33>dY5Qn0yxK z%%DRJrW_8N$|hNWEK4#0M_`d4ghVn47Z}7M;4H`>nZz`=AQH@=>|fZwNX-zi!sWYV zBsKeQriR(SNWEOdflfLm7p9Sq{_mEUAk7!0oH$}=eXfVCTWIwTon;ni!4!dZ!O)ucD|>x7>*u!d>1lZR>CfeSJ`uyMpU=;y z75n9U(&UF46xR^t8K&0WnXdA0MtoM=6DK~sbZ%HCRFFaBHIbwx(kGJ~%xfR|$ z-a6)7!RJWmq>*alnqe;a$vb>3ctAG^Al<^;+{~PYAKyIQ;mVPMTptDp+P`A?s+8~e zA1*d@+qqq#D)Qp1xtBEGEi1`Oy3u>oc*W{n3cJAG$0rNe zH0iUT3ktEnIz6Hc)EQPcC|zk6KRtXyRlAOznth@E$1_dd41G7H1iKYpEz?UWw&f+O zW-66lFUj4ZYBcHM8f@+9wY*|n#tzm!3x{b<2DS&5^eg*5?{m%&0rd?1){sC_+BRhw z|NC%e#?grPiVwI8+g_J`*Slju0Y57=j2{?!*z0}W}p z!wjeLjiKc}k0muZ=S)?hrP$iK?*pBsmtf?s%dR0jatULH@2FuwLO(IKFI zu**20w=};a>QL-M%0D`RRB;Bg?38-!T8AS>sW<4%-LACh zXZ`MrJ(M1L&m!Pz7GG?fuM+(-W_`>{Hdd8Td*NiKUs;*|s@mYg^F4agcO@&wSCpOf z80Zl^ST=uAE1;_>3%{5Orp$fiUfOfX>R7UNv*c{!L+?E^RkyylB|X^Q6tgAUDYia|wcFp0|7sDoYLbe{EdAvS z#o6f#?ns?)?R-KaJLH!UZPHD=GB&=IWnOmSaqn2Sd{i`9T$-;kNp(((Cx!s{u`#s| z0`5sWGi_fiq}n|SuQ+^!`m8@$V~fg%Sq9bLMi=4>tPQjQRShZmV)TnDYl6PIql$uX z)*tk?mJeay%woqHS#EEB-gB{2)|pklA&h3||L*36pUQQ$i&IYPu=gz)EXz#RG_~2V z3;QZ`aCKqHC2hLTho|2US{zI&C{ugx7}JY^4oAi`UWKDayzzNUeK%kKhbSJv4_*+`cEVndc~4)FJTag})6yZJ%!f@uST(Lbv4 znrlkI#)KBku($TS@Gz?=~^lIOh&^^cT4^8rM3VUy#^eC>3YA=t#pEFRCMdfg< zuV3iN&U*i@Eo*h9v@f)-j8k-?=>$VRG9D-9{NA=s%wKR8rY`nqMbYtB`GCpIu)Ex+np zDxD8Ah|7y#U3sdJ^5*8x**g4I#InAOJKAYSjdyEkS1WhqbR}uJkh`X1>fC Date: Mon, 7 May 2018 09:24:23 +0200 Subject: [PATCH 041/244] amber 1.3.1 added compatibility with DVMEGAs --- ambed/cftdidevicedescr.cpp | 16 ++++++++++++++-- ambed/main.h | 2 +- ambed/readme | 4 +++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/ambed/cftdidevicedescr.cpp b/ambed/cftdidevicedescr.cpp index 6b68821..90aaa4d 100644 --- a/ambed/cftdidevicedescr.cpp +++ b/ambed/cftdidevicedescr.cpp @@ -72,6 +72,7 @@ int CFtdiDeviceDescr::CreateInterface(CFtdiDeviceDescr *descr, std::vectorGetDescription(), "USB-3003") == 0) || // DVSI's USB-3003 (::strcmp(descr->GetDescription(), "DF2ET-3003") == 0) || // DF2ET's USB-3003 opensource device + (::strcmp(descr->GetDescription(), "DVstick-33") == 0) || // DVMEGA USB-3003 device (::strcmp(descr->GetDescription(), "ThumbDV-3") == 0) ) // ThumbDV-3 { iNbChs = CreateUsb3003(descr, channels); @@ -136,14 +137,16 @@ int CFtdiDeviceDescr::GetNbChannels(void) const int iNbChs = 0; // single channel devices - if ( (::strcmp(m_szDescription, "USB-3000") == 0) || // DVSI's USB-3000 - (::strcmp(m_szDescription, "ThumbDV") == 0) ) // ThumbDV + if ( (::strcmp(m_szDescription, "USB-3000") == 0) || // DVSI's USB-3000 + (::strcmp(m_szDescription, "DVstick-30") == 0) || // DVMEGA AMBE3000 device + (::strcmp(m_szDescription, "ThumbDV") == 0) ) // ThumbDV { iNbChs = 1; } // three channels devices else if ( (::strcmp(m_szDescription, "USB-3003") == 0) || // DVSI's USB-3003 (::strcmp(m_szDescription, "DF2ET-3003") == 0) || // DF2ET's USB-3003 opensource device + (::strcmp(m_szDescription, "DVstick-33") == 0) || // DVMEGA AMBE 3003 device (::strcmp(m_szDescription, "ThumbDV-3") == 0) ) // ThumbDV-3 { iNbChs = 3; @@ -330,6 +333,7 @@ int CFtdiDeviceDescr::CreateUsb3006(CFtdiDeviceDescr *descr, std::vectorGetVid(), descr->GetPid(), descr->GetDescription(), descr->GetSerialNumber()); } + else if ( (::strcmp(descr->GetDescription(), "DVstick-33") == 0) ) // DVMEGA AMBE3003 device + { + // specific fardware reset, 921600 bps + Usb3003 = new CUsb3003DF2ETInterface + (descr->GetVid(), descr->GetPid(), descr->GetDescription(), descr->GetSerialNumber()); + } + // done return Usb3003; } @@ -547,6 +558,7 @@ CUsb3000Interface *CFtdiDeviceDescr::InstantiateUsb3000(CFtdiDeviceDescr *descr) // intstantiate the proper version of USB-3000 if ( (::strcmp(descr->GetDescription(), "USB-3000") == 0) || // DVSI's USB-3000 + (::strcmp(descr->GetDescription(), "DVstick-30")== 0) || // DVMEGA AMBE3000 device (::strcmp(descr->GetDescription(), "ThumbDV") == 0) ) // ThumbDV { Usb3000 = new CUsb3000Interface diff --git a/ambed/main.h b/ambed/main.h index 3c7bd19..689d43c 100644 --- a/ambed/main.h +++ b/ambed/main.h @@ -49,7 +49,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 3 -#define VERSION_REVISION 0 +#define VERSION_REVISION 1 // global ------------------------------------------------------ diff --git a/ambed/readme b/ambed/readme index 4224cca..52305d4 100644 --- a/ambed/readme +++ b/ambed/readme @@ -22,7 +22,7 @@ // along with Foobar. If not, see . // ---------------------------------------------------------------------------- -VERSION: 1.3.0 +VERSION: 1.3.1 Hardware compatibility. ====================== @@ -34,6 +34,8 @@ This version of ambed is compatible with: - DVSI's USB-3012 device - NWDR's ThumbDV device - NWDR's ThumbDV-3 device +- DVMEGA AMBE3000 device +- DVMEGA AMBE3003 device Available transcoding channels per device: From 10d56ae843b889b029233f5652ab5dd75a8dd40d Mon Sep 17 00:00:00 2001 From: Adrian Fewster Date: Sun, 3 Jun 2018 23:38:54 +1000 Subject: [PATCH 042/244] Update cvocodecs.cpp 'availables' correctly changed to 'available' for correct English --- ambed/cvocodecs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ambed/cvocodecs.cpp b/ambed/cvocodecs.cpp index 56f4bb4..3981e0a 100644 --- a/ambed/cvocodecs.cpp +++ b/ambed/cvocodecs.cpp @@ -165,7 +165,7 @@ bool CVocodecs::Init(void) if ( ok ) { - std::cout << "Codec interfaces initialized successfully : " << iNbCh << " channels availables" << std::endl; + std::cout << "Codec interfaces initialized successfully : " << iNbCh << " channels available" << std::endl; } else { From 71cddc2f5a2e9f01172255d0190f90dd9f7b9ce5 Mon Sep 17 00:00:00 2001 From: LX3JL Date: Mon, 25 Jun 2018 18:41:24 +0200 Subject: [PATCH 043/244] amber 1.3.2 fixed problem while running multiple 3006 or 3012 devices adjusted load sharing when running multiple devices --- ambed/cftdidevicedescr.cpp | 69 ++++++++++++++++++++++++++----------- ambed/cftdidevicedescr.h | 13 ++++--- ambed/cusb3003interface.cpp | 4 +-- ambed/cvocodecs.cpp | 47 ++++++++++++++++++++++--- ambed/main.h | 2 +- ambed/readme | 2 +- 6 files changed, 102 insertions(+), 35 deletions(-) diff --git a/ambed/cftdidevicedescr.cpp b/ambed/cftdidevicedescr.cpp index 90aaa4d..e17f4e8 100644 --- a/ambed/cftdidevicedescr.cpp +++ b/ambed/cftdidevicedescr.cpp @@ -132,6 +132,33 @@ int CFtdiDeviceDescr::CreateInterfacePair(CFtdiDeviceDescr *descr1, CFtdiDeviceD //////////////////////////////////////////////////////////////////////////////////////// // get +const char * CFtdiDeviceDescr::GetChannelDescription(int ch) const +{ + static char descr[FTDI_MAX_STRINGLENGTH]; + char tag[3] = "_X"; + + ::strcpy(descr, GetDescription()); + if ( ::strlen(descr) >= 2 ) + { + descr[::strlen(descr)-2] = 0x00; + tag[1] = (char)ch + 'A'; + ::strcat(descr, tag); + } + return descr; +} + +const char * CFtdiDeviceDescr::GetChannelSerialNumber(int ch) const +{ + static char serial[FTDI_MAX_STRINGLENGTH]; + + ::strcpy(serial, GetSerialNumber()); + if ( ::strlen(serial) >= 1 ) + { + serial[::strlen(serial)-1] = (char)ch + 'A'; + } + return serial; +} + int CFtdiDeviceDescr::GetNbChannels(void) const { int iNbChs = 0; @@ -180,13 +207,13 @@ int CFtdiDeviceDescr::CreateUsb3012(CFtdiDeviceDescr *descr, std::vectorGetVid(), descr->GetPid(), "USB-3012_A", descr->GetSerialNumber()); + new CUsb3003HRInterface(descr->GetVid(), descr->GetPid(), descr->GetChannelDescription(0), descr->GetChannelSerialNumber(0)); CUsb3003HRInterface *Usb3003B = - new CUsb3003HRInterface(descr->GetVid(), descr->GetPid(), "USB-3012_B", descr->GetSerialNumber()); + new CUsb3003HRInterface(descr->GetVid(), descr->GetPid(), descr->GetChannelDescription(1), descr->GetChannelSerialNumber(1)); CUsb3003HRInterface *Usb3003C = - new CUsb3003HRInterface(descr->GetVid(), descr->GetPid(), "USB-3012_C", descr->GetSerialNumber()); + new CUsb3003HRInterface(descr->GetVid(), descr->GetPid(), descr->GetChannelDescription(2), descr->GetChannelSerialNumber(2)); CUsb3003HRInterface *Usb3003D = - new CUsb3003HRInterface(descr->GetVid(), descr->GetPid(), "USB-3012_D", descr->GetSerialNumber()); + new CUsb3003HRInterface(descr->GetVid(), descr->GetPid(), descr->GetChannelDescription(3), descr->GetChannelSerialNumber(3)); // init the interfaces if ( Usb3003A->Init(CODEC_AMBEPLUS) && Usb3003B->Init(CODEC_AMBE2PLUS) && @@ -212,31 +239,31 @@ int CFtdiDeviceDescr::CreateUsb3012(CFtdiDeviceDescr *descr, std::vectorpush_back(Channel); Usb3003B->AddChannel(Channel); // ch5 + Channel = new CVocodecChannel(Usb3003A, 2, Usb3003B, 2, CODECGAIN_AMBEPLUS); + channels->push_back(Channel); + Usb3003A->AddChannel(Channel); + Usb3003B->AddChannel(Channel); + // ch6 + Channel = new CVocodecChannel(Usb3003B, 2, Usb3003A, 2, CODECGAIN_AMBE2PLUS); + channels->push_back(Channel); + Usb3003A->AddChannel(Channel); + Usb3003B->AddChannel(Channel); + // ch7 Channel = new CVocodecChannel(Usb3003C, 0, Usb3003C, 1, CODECGAIN_AMBEPLUS); channels->push_back(Channel); Usb3003C->AddChannel(Channel); - // ch6 + // ch8 Channel = new CVocodecChannel(Usb3003C, 1, Usb3003C, 0, CODECGAIN_AMBE2PLUS); channels->push_back(Channel); Usb3003C->AddChannel(Channel); - // ch7 + // ch9 Channel = new CVocodecChannel(Usb3003D, 0, Usb3003D, 1, CODECGAIN_AMBEPLUS); channels->push_back(Channel); Usb3003D->AddChannel(Channel); - // ch8 + // ch10 Channel = new CVocodecChannel(Usb3003D, 1, Usb3003D, 0, CODECGAIN_AMBE2PLUS); channels->push_back(Channel); Usb3003D->AddChannel(Channel); - // ch9 - Channel = new CVocodecChannel(Usb3003A, 2, Usb3003B, 2, CODECGAIN_AMBEPLUS); - channels->push_back(Channel); - Usb3003A->AddChannel(Channel); - Usb3003B->AddChannel(Channel); - // ch10 - Channel = new CVocodecChannel(Usb3003B, 2, Usb3003A, 2, CODECGAIN_AMBE2PLUS); - channels->push_back(Channel); - Usb3003A->AddChannel(Channel); - Usb3003B->AddChannel(Channel); // ch11 Channel = new CVocodecChannel(Usb3003C, 2, Usb3003D, 2, CODECGAIN_AMBEPLUS); channels->push_back(Channel); @@ -273,13 +300,13 @@ int CFtdiDeviceDescr::CreateUsb3012(CFtdiDeviceDescr *descr, std::vector*channels) { - int nStreams = 0; + int nStreams = 0; - // create the interfaces for the four 3003 chips + // create the interfaces for the two 3003 chips CUsb3003Interface *Usb3003A = - new CUsb3003Interface(descr->GetVid(), descr->GetPid(), "USB-3006_A", descr->GetSerialNumber()); + new CUsb3003Interface(descr->GetVid(), descr->GetPid(), descr->GetChannelDescription(0), descr->GetChannelSerialNumber(0)); CUsb3003Interface *Usb3003B = - new CUsb3003Interface(descr->GetVid(), descr->GetPid(), "USB-3006_B", descr->GetSerialNumber()); + new CUsb3003Interface(descr->GetVid(), descr->GetPid(), descr->GetChannelDescription(1), descr->GetChannelSerialNumber(1)); // init the interfaces if ( Usb3003A->Init(CODEC_AMBEPLUS) && Usb3003B->Init(CODEC_AMBE2PLUS) ) diff --git a/ambed/cftdidevicedescr.h b/ambed/cftdidevicedescr.h index c01b485..15fa362 100644 --- a/ambed/cftdidevicedescr.h +++ b/ambed/cftdidevicedescr.h @@ -58,12 +58,15 @@ public: static int CreateInterfacePair(CFtdiDeviceDescr *, CFtdiDeviceDescr *, std::vector*); // get - bool IsUsed(void) const { return m_bUsed; } + bool IsUsed(void) const { return m_bUsed; } int GetNbChannels(void) const; - uint32 GetVid(void) const { return m_uiVid; } - uint32 GetPid(void) const { return m_uiPid; } - const char *GetDescription(void) const { return m_szDescription; } - const char *GetSerialNumber(void) const { return m_szSerial; } + uint32 GetVid(void) const { return m_uiVid; } + uint32 GetPid(void) const { return m_uiPid; } + const char *GetDescription(void) const { return m_szDescription; } + const char *GetSerialNumber(void) const { return m_szSerial; } + const char * GetChannelDescription(int) const; + const char * GetChannelSerialNumber(int) const; + // set void SetUsed(bool used) { m_bUsed = used; } diff --git a/ambed/cusb3003interface.cpp b/ambed/cusb3003interface.cpp index 309680c..471dc39 100644 --- a/ambed/cusb3003interface.cpp +++ b/ambed/cusb3003interface.cpp @@ -209,9 +209,9 @@ bool CUsb3003Interface::OpenDevice(void) ftStatus = FT_SetVIDPID(m_uiVid, m_uiPid); if (ftStatus != FT_OK) {FTDI_Error((char *)"FT_SetVIDPID", ftStatus ); return false; } - ftStatus = FT_OpenEx((PVOID)m_szDeviceName, FT_OPEN_BY_DESCRIPTION, &m_FtdiHandle); + ftStatus = FT_OpenEx((PVOID)m_szDeviceSerial, FT_OPEN_BY_SERIAL_NUMBER, &m_FtdiHandle); if (ftStatus != FT_OK) { FTDI_Error((char *)"FT_OpenEx", ftStatus ); return false; } - + CTimePoint::TaskSleepFor(50); FT_Purge(m_FtdiHandle, FT_PURGE_RX | FT_PURGE_TX ); CTimePoint::TaskSleepFor(50); diff --git a/ambed/cvocodecs.cpp b/ambed/cvocodecs.cpp index 3981e0a..aa28707 100644 --- a/ambed/cvocodecs.cpp +++ b/ambed/cvocodecs.cpp @@ -88,13 +88,14 @@ bool CVocodecs::Init(void) // and create interfaces for the discovered devices // first handle all even number of channels devices + std::vector Multi3003DevicesChs; for ( int i = 0; i < m_FtdiDeviceDescrs.size(); i++ ) { CFtdiDeviceDescr *descr = m_FtdiDeviceDescrs[i]; if ( !descr->IsUsed() && IsEven(descr->GetNbChannels()) ) { // create the object - iNbCh += CFtdiDeviceDescr::CreateInterface(descr, &m_Channels); + iNbCh += CFtdiDeviceDescr::CreateInterface(descr, &Multi3003DevicesChs); // and flag as used descr->SetUsed(true); } @@ -102,6 +103,7 @@ bool CVocodecs::Init(void) // next handle all single channel devices. // they must be handeled in pair, or in pair with another // even number of channels device. + std::vector PairsOf3000DevicesChs; for ( int i = 0; i < m_FtdiDeviceDescrs.size(); i++ ) { CFtdiDeviceDescr *descr1 = m_FtdiDeviceDescrs[i]; @@ -120,15 +122,16 @@ bool CVocodecs::Init(void) if ( found ) { // yes, create and pairboth interfaces - iNbCh += CFtdiDeviceDescr::CreateInterfacePair(descr1, descr2, &m_Channels); + iNbCh += CFtdiDeviceDescr::CreateInterfacePair(descr1, descr2, &PairsOf3000DevicesChs); // and flag as used descr1->SetUsed(true); descr2->SetUsed(true); } } } - // now we should have only remaining the 3 chennels device(s) + // now we should have only remaining the 3 channels device(s) // and possibly an unique single channel device + std::vector Single3003DeviceChannels; for ( int i = 0; i < m_FtdiDeviceDescrs.size(); i++ ) { CFtdiDeviceDescr *descr1 = m_FtdiDeviceDescrs[i]; @@ -148,7 +151,7 @@ bool CVocodecs::Init(void) if ( found ) { // yes, create and pairboth interfaces - iNbCh += CFtdiDeviceDescr::CreateInterfacePair(descr1, descr2, &m_Channels); + iNbCh += CFtdiDeviceDescr::CreateInterfacePair(descr1, descr2, &Multi3003DevicesChs); // and flag as used descr1->SetUsed(true); descr2->SetUsed(true); @@ -156,13 +159,47 @@ bool CVocodecs::Init(void) else { // no, just create a standalone 3003 interface - iNbCh += CFtdiDeviceDescr::CreateInterface(descr1, &m_Channels); + iNbCh += CFtdiDeviceDescr::CreateInterface(descr1, &Single3003DeviceChannels); // and flag as used descr1->SetUsed(true); } } } + // now agregate channels by order of priority + // for proper load sharing + // pairs of 300 devices first + { + for ( int i = 0; i < PairsOf3000DevicesChs.size(); i++ ) + { + m_Channels.push_back(PairsOf3000DevicesChs.at(i)); + } + PairsOf3000DevicesChs.clear(); + } + // next the left-over single 3003 device + { + for ( int i = 0; i < Single3003DeviceChannels.size(); i++ ) + { + m_Channels.push_back(Single3003DeviceChannels.at(i)); + } + Single3003DeviceChannels.clear(); + } + // and finally interlace multi-3003 and pairs of 3003 devices which always + // results to 6 channels per pair of 3003 + { + int n = (int)Multi3003DevicesChs.size() / 6; + for ( int i = 0; i < 6; i++ ) + { + for ( int j = 0; j < n; j++ ) + { + m_Channels.push_back(Multi3003DevicesChs.at((j*6) + i)); + } + } + Multi3003DevicesChs.clear(); + } + + + // done if ( ok ) { std::cout << "Codec interfaces initialized successfully : " << iNbCh << " channels available" << std::endl; diff --git a/ambed/main.h b/ambed/main.h index 689d43c..a432f36 100644 --- a/ambed/main.h +++ b/ambed/main.h @@ -49,7 +49,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 3 -#define VERSION_REVISION 1 +#define VERSION_REVISION 2 // global ------------------------------------------------------ diff --git a/ambed/readme b/ambed/readme index 52305d4..bfd8583 100644 --- a/ambed/readme +++ b/ambed/readme @@ -22,7 +22,7 @@ // along with Foobar. If not, see . // ---------------------------------------------------------------------------- -VERSION: 1.3.1 +VERSION: 1.3.2 Hardware compatibility. ====================== From 2a3cd63d53a856fba0fd69e812e146335dd96cf3 Mon Sep 17 00:00:00 2001 From: narspt Date: Fri, 20 Jul 2018 22:23:29 +0100 Subject: [PATCH 044/244] make sure buttons text never word-wrap to avoid them getting fully disarranged in some specific cases... --- dashboard/css/layout.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dashboard/css/layout.css b/dashboard/css/layout.css index 3a80aff..26af1ba 100755 --- a/dashboard/css/layout.css +++ b/dashboard/css/layout.css @@ -79,6 +79,7 @@ a.tip:hover span { .menulink { font-size : 14pt; text-decoration : none; + white-space : nowrap; border : 1px #000000 solid; padding-left : 10px; padding-top : 3px; @@ -92,6 +93,7 @@ a.tip:hover span { .menulinkactive { font-size : 14pt; text-decoration : none; + white-space : nowrap; border : 1px #000000 solid; padding-left : 10px; padding-top : 3px; From 767fed2a87fdf958fbccfaa4fde374f074764c34 Mon Sep 17 00:00:00 2001 From: Eddie Date: Sun, 22 Jul 2018 05:18:44 +0800 Subject: [PATCH 045/244] logout session logout session --- dashboard2/log/logout.php | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 dashboard2/log/logout.php diff --git a/dashboard2/log/logout.php b/dashboard2/log/logout.php new file mode 100644 index 0000000..a369a6c --- /dev/null +++ b/dashboard2/log/logout.php @@ -0,0 +1,28 @@ + + + + + + + +
+ +
+ + From b189220eb8c220e4fabcb07f371c733a52ec6ea4 Mon Sep 17 00:00:00 2001 From: narspt Date: Sat, 13 Oct 2018 18:40:18 +0100 Subject: [PATCH 046/244] dashboard performance enhancement Use an array indexed by dxcc prefix for faster lookups instead of scanning whole Flagarray and it's dxcc nested arrays (done lots of times when there are many last heard entries), which is very slow and resource hungry... before this change my server was taking ~360ms to generate users page (with 80 last heard entries), after this small change it takes ~18ms instead. --- dashboard/pgs/class.reflector.php | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/dashboard/pgs/class.reflector.php b/dashboard/pgs/class.reflector.php index 33d77f7..d63ca4b 100755 --- a/dashboard/pgs/class.reflector.php +++ b/dashboard/pgs/class.reflector.php @@ -6,6 +6,7 @@ class xReflector { public $Stations = null; public $Peers = null; private $Flagarray = null; + private $Flagarray_DXCC = null; private $Flagfile = null; private $CallingHomeActive = null; private $CallingHomeHash = null; @@ -125,6 +126,7 @@ class xReflector { public function LoadFlags() { if ($this->Flagfile != null) { $this->Flagarray = array(); + $this->Flagarray_DXCC = array(); $handle = fopen($this->Flagfile,"r"); if ($handle) { $i = 0; @@ -134,11 +136,12 @@ class xReflector { if (isset($tmp[0])) { $this->Flagarray[$i]['Country'] = $tmp[0]; } else { $this->Flagarray[$i]['Country'] = 'Undefined'; } if (isset($tmp[1])) { $this->Flagarray[$i]['ISO'] = $tmp[1]; } else { $this->Flagarray[$i]['ISO'] = "Undefined"; } - $this->Flagarray[$i]['DXCC'] = array(); + //$this->Flagarray[$i]['DXCC'] = array(); if (isset($tmp[2])) { $tmp2 = explode("-", $tmp[2]); for ($j=0;$jFlagarray[$i]['DXCC'][] = $tmp2[$j]; + //$this->Flagarray[$i]['DXCC'][] = $tmp2[$j]; + $this->Flagarray_DXCC[ trim($tmp2[$j]) ] = $i; } } $i++; @@ -256,25 +259,15 @@ class xReflector { public function GetFlag($Callsign) { $Image = ""; - $FoundFlag = false; $Letters = 4; $Name = ""; - while (($Letters >= 2) && (!$FoundFlag)) { - $j = 0; - $Prefix = substr($Callsign, 0, $Letters); - while (($j < count($this->Flagarray)) && (!$FoundFlag)) { - - $z = 0; - while (($z < count($this->Flagarray[$j]['DXCC'])) && (!$FoundFlag)) { - if (trim($Prefix) == trim($this->Flagarray[$j]['DXCC'][$z])) { - $Image = $this->Flagarray[$j]['ISO']; - $Name = $this->Flagarray[$j]['Country']; - $FoundFlag = true; + while ($Letters >= 2) { + $Prefix = substr(trim($Callsign), 0, $Letters); + if (isset($this->Flagarray_DXCC[$Prefix])) { + $Image = $this->Flagarray[ $this->Flagarray_DXCC[$Prefix] ]['ISO']; + $Name = $this->Flagarray[ $this->Flagarray_DXCC[$Prefix] ]['Country']; + break; } - $z++; - } - $j++; - } $Letters--; } return array(strtolower($Image), $Name); From 070f16fdb7aa4a942379c48b8606df32c2586a7d Mon Sep 17 00:00:00 2001 From: narspt Date: Sat, 13 Oct 2018 18:53:37 +0100 Subject: [PATCH 047/244] dashboard2 performance enhancement Use an array indexed by dxcc prefix for faster lookups instead of scanning whole Flagarray and it's dxcc nested arrays (done lots of times when there are many last heard entries), which is very slow and resource hungry... before this change my server was taking ~360ms to generate users page (with 80 last heard entries), after this small change it takes ~18ms instead. --- dashboard2/pgs/class.reflector.php | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/dashboard2/pgs/class.reflector.php b/dashboard2/pgs/class.reflector.php index f15f092..d6505f7 100644 --- a/dashboard2/pgs/class.reflector.php +++ b/dashboard2/pgs/class.reflector.php @@ -6,6 +6,7 @@ class xReflector { public $Stations = null; public $Peers = null; private $Flagarray = null; + private $Flagarray_DXCC = null; private $Flagfile = null; private $CallingHomeActive = null; private $CallingHomeHash = null; @@ -125,6 +126,7 @@ class xReflector { public function LoadFlags() { if ($this->Flagfile != null) { $this->Flagarray = array(); + $this->Flagarray_DXCC = array(); $handle = fopen($this->Flagfile,"r"); if ($handle) { $i = 0; @@ -134,11 +136,12 @@ class xReflector { if (isset($tmp[0])) { $this->Flagarray[$i]['Country'] = $tmp[0]; } else { $this->Flagarray[$i]['Country'] = 'Undefined'; } if (isset($tmp[1])) { $this->Flagarray[$i]['ISO'] = $tmp[1]; } else { $this->Flagarray[$i]['ISO'] = "Undefined"; } - $this->Flagarray[$i]['DXCC'] = array(); + //$this->Flagarray[$i]['DXCC'] = array(); if (isset($tmp[2])) { $tmp2 = explode("-", $tmp[2]); for ($j=0;$jFlagarray[$i]['DXCC'][] = $tmp2[$j]; + //$this->Flagarray[$i]['DXCC'][] = $tmp2[$j]; + $this->Flagarray_DXCC[ trim($tmp2[$j]) ] = $i; } } $i++; @@ -256,25 +259,15 @@ class xReflector { public function GetFlag($Callsign) { $Image = ""; - $FoundFlag = false; $Letters = 4; $Name = ""; - while (($Letters >= 2) && (!$FoundFlag)) { - $j = 0; - $Prefix = substr($Callsign, 0, $Letters); - while (($j < count($this->Flagarray)) && (!$FoundFlag)) { - - $z = 0; - while (($z < count($this->Flagarray[$j]['DXCC'])) && (!$FoundFlag)) { - if (trim($Prefix) == trim($this->Flagarray[$j]['DXCC'][$z])) { - $Image = $this->Flagarray[$j]['ISO']; - $Name = $this->Flagarray[$j]['Country']; - $FoundFlag = true; + while ($Letters >= 2) { + $Prefix = substr(trim($Callsign), 0, $Letters); + if (isset($this->Flagarray_DXCC[$Prefix])) { + $Image = $this->Flagarray[ $this->Flagarray_DXCC[$Prefix] ]['ISO']; + $Name = $this->Flagarray[ $this->Flagarray_DXCC[$Prefix] ]['Country']; + break; } - $z++; - } - $j++; - } $Letters--; } return array(strtolower($Image), $Name); From dbc05c9b632fcb0e7479544ac37263cd267e1ca2 Mon Sep 17 00:00:00 2001 From: narspt Date: Tue, 16 Oct 2018 19:27:59 +0100 Subject: [PATCH 048/244] smooth page refresh with jquery dynamically reload page body contents with jquery instead of just asking browser to reload whole page, this provides a fully smooth page refresh and keeps page scroll position on all browsers --- dashboard/index.php | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/dashboard/index.php b/dashboard/index.php index 8a23f81..2048783 100755 --- a/dashboard/index.php +++ b/dashboard/index.php @@ -94,32 +94,32 @@ else { if ($PageOptions['PageRefreshActive']) { echo ' + '; } From 7ea08fe865f5c9ce73bc6382d12b6f19c73db909 Mon Sep 17 00:00:00 2001 From: narspt Date: Tue, 16 Oct 2018 19:29:54 +0100 Subject: [PATCH 049/244] include jquery library 1.x to ensure old browsers compatibility --- dashboard/js/jquery-1.12.4.min.js | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 dashboard/js/jquery-1.12.4.min.js diff --git a/dashboard/js/jquery-1.12.4.min.js b/dashboard/js/jquery-1.12.4.min.js new file mode 100644 index 0000000..e836475 --- /dev/null +++ b/dashboard/js/jquery-1.12.4.min.js @@ -0,0 +1,5 @@ +/*! jQuery v1.12.4 | (c) jQuery Foundation | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=a.document,e=c.slice,f=c.concat,g=c.push,h=c.indexOf,i={},j=i.toString,k=i.hasOwnProperty,l={},m="1.12.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return e.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:e.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a){return n.each(this,a)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(e.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:g,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(n.isPlainObject(c)||(b=n.isArray(c)))?(b?(b=!1,f=a&&n.isArray(a)?a:[]):f=a&&n.isPlainObject(a)?a:{},g[d]=n.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray||function(a){return"array"===n.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){var b=a&&a.toString();return!n.isArray(a)&&b-parseFloat(b)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;try{if(a.constructor&&!k.call(a,"constructor")&&!k.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(!l.ownFirst)for(b in a)return k.call(a,b);for(b in a);return void 0===b||k.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?i[j.call(a)]||"object":typeof a},globalEval:function(b){b&&n.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(s(a)){for(c=a.length;c>d;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):g.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(h)return h.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,g=0,h=[];if(s(a))for(d=a.length;d>g;g++)e=b(a[g],g,c),null!=e&&h.push(e);else for(g in a)e=b(a[g],g,c),null!=e&&h.push(e);return f.apply([],h)},guid:1,proxy:function(a,b){var c,d,f;return"string"==typeof b&&(f=a[b],b=a,a=f),n.isFunction(a)?(c=e.call(arguments,2),d=function(){return a.apply(b||this,c.concat(e.call(arguments)))},d.guid=a.guid=a.guid||n.guid++,d):void 0},now:function(){return+new Date},support:l}),"function"==typeof Symbol&&(n.fn[Symbol.iterator]=c[Symbol.iterator]),n.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){i["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=!!a&&"length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ga(),z=ga(),A=ga(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+M+"))|)"+L+"*\\]",O=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+N+")*)|.*)\\)|)",P=new RegExp(L+"+","g"),Q=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),R=new RegExp("^"+L+"*,"+L+"*"),S=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),T=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),U=new RegExp(O),V=new RegExp("^"+M+"$"),W={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M+"|[*])"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},X=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Z=/^[^{]+\{\s*\[native \w/,$=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,_=/[+~]/,aa=/'|\\/g,ba=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),ca=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},da=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(ea){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fa(a,b,d,e){var f,h,j,k,l,o,r,s,w=b&&b.ownerDocument,x=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==x&&9!==x&&11!==x)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==x&&(o=$.exec(a)))if(f=o[1]){if(9===x){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(w&&(j=w.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(o[2])return H.apply(d,b.getElementsByTagName(a)),d;if((f=o[3])&&c.getElementsByClassName&&b.getElementsByClassName)return H.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==x)w=b,s=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(aa,"\\$&"):b.setAttribute("id",k=u),r=g(a),h=r.length,l=V.test(k)?"#"+k:"[id='"+k+"']";while(h--)r[h]=l+" "+qa(r[h]);s=r.join(","),w=_.test(a)&&oa(b.parentNode)||b}if(s)try{return H.apply(d,w.querySelectorAll(s)),d}catch(y){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(Q,"$1"),b,d,e)}function ga(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ha(a){return a[u]=!0,a}function ia(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ja(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function ka(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function la(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function na(a){return ha(function(b){return b=+b,ha(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function oa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=fa.support={},f=fa.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fa.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ia(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ia(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Z.test(n.getElementsByClassName),c.getById=ia(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return"undefined"!=typeof b.getElementsByClassName&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=Z.test(n.querySelectorAll))&&(ia(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ia(function(a){var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Z.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ia(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",O)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Z.test(o.compareDocumentPosition),t=b||Z.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return ka(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?ka(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},fa.matches=function(a,b){return fa(a,null,null,b)},fa.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(T,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fa(b,n,null,[a]).length>0},fa.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fa.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fa.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fa.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fa.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fa.selectors={cacheLength:50,createPseudo:ha,match:W,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ba,ca),a[3]=(a[3]||a[4]||a[5]||"").replace(ba,ca),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fa.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fa.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return W.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&U.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ba,ca).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fa.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(P," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fa.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ha(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ha(function(a){var b=[],c=[],d=h(a.replace(Q,"$1"));return d[u]?ha(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ha(function(a){return function(b){return fa(a,b).length>0}}),contains:ha(function(a){return a=a.replace(ba,ca),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ha(function(a){return V.test(a||"")||fa.error("unsupported lang: "+a),a=a.replace(ba,ca).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Y.test(a.nodeName)},input:function(a){return X.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:na(function(){return[0]}),last:na(function(a,b){return[b-1]}),eq:na(function(a,b,c){return[0>c?c+b:c]}),even:na(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:na(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:na(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:na(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function ra(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j,k=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(j=b[u]||(b[u]={}),i=j[b.uniqueID]||(j[b.uniqueID]={}),(h=i[d])&&h[0]===w&&h[1]===f)return k[2]=h[2];if(i[d]=k,k[2]=a(b,c,g))return!0}}}function sa(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ta(a,b,c){for(var d=0,e=b.length;e>d;d++)fa(a,b[d],c);return c}function ua(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function va(a,b,c,d,e,f){return d&&!d[u]&&(d=va(d)),e&&!e[u]&&(e=va(e,f)),ha(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ta(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ua(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ua(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ua(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function wa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ra(function(a){return a===b},h,!0),l=ra(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[ra(sa(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return va(i>1&&sa(m),i>1&&qa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(Q,"$1"),c,e>i&&wa(a.slice(i,e)),f>e&&wa(a=a.slice(e)),f>e&&qa(a))}m.push(c)}return sa(m)}function xa(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=F.call(i));u=ua(u)}H.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&fa.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ha(f):f}return h=fa.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xa(e,d)),f.selector=a}return f},i=fa.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ba,ca),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=W.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ba,ca),_.test(j[0].type)&&oa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qa(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||_.test(a)&&oa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ia(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ia(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ja("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ia(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ja("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ia(function(a){return null==a.getAttribute("disabled")})||ja(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fa}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.uniqueSort=n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},v=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},w=n.expr.match.needsContext,x=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,y=/^.[^:#\[\.,]*$/;function z(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(y.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return n.inArray(a,b)>-1!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;e>b;b++)if(n.contains(d[b],this))return!0}));for(b=0;e>b;b++)n.find(a,d[b],c);return c=this.pushStack(e>1?n.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(z(this,a||[],!1))},not:function(a){return this.pushStack(z(this,a||[],!0))},is:function(a){return!!z(this,"string"==typeof a&&w.test(a)?n(a):a||[],!1).length}});var A,B=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=n.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||A,"string"==typeof a){if(e="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:B.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),x.test(e[1])&&n.isPlainObject(b))for(e in b)n.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}if(f=d.getElementById(e[2]),f&&f.parentNode){if(f.id!==e[2])return A.find(a);this.length=1,this[0]=f}return this.context=d,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof c.ready?c.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};C.prototype=n.fn,A=n(d);var D=/^(?:parents|prev(?:Until|All))/,E={children:!0,contents:!0,next:!0,prev:!0};n.fn.extend({has:function(a){var b,c=n(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(n.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=w.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?n.inArray(this[0],n(a)):n.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.uniqueSort(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function F(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return u(a,"parentNode")},parentsUntil:function(a,b,c){return u(a,"parentNode",c)},next:function(a){return F(a,"nextSibling")},prev:function(a){return F(a,"previousSibling")},nextAll:function(a){return u(a,"nextSibling")},prevAll:function(a){return u(a,"previousSibling")},nextUntil:function(a,b,c){return u(a,"nextSibling",c)},prevUntil:function(a,b,c){return u(a,"previousSibling",c)},siblings:function(a){return v((a.parentNode||{}).firstChild,a)},children:function(a){return v(a.firstChild)},contents:function(a){return n.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(E[a]||(e=n.uniqueSort(e)),D.test(a)&&(e=e.reverse())),this.pushStack(e)}});var G=/\S+/g;function H(a){var b={};return n.each(a.match(G)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?H(a):n.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),h>=c&&h--}),this},has:function(a){return a?n.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=!0,c||j.disable(),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().progress(c.notify).done(c.resolve).fail(c.reject):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=e.call(arguments),d=c.length,f=1!==d||a&&n.isFunction(a.promise)?d:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?e.call(arguments):d,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(d>1)for(i=new Array(d),j=new Array(d),k=new Array(d);d>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().progress(h(b,j,i)).done(h(b,k,c)).fail(g.reject):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(d,[n]),n.fn.triggerHandler&&(n(d).triggerHandler("ready"),n(d).off("ready"))))}});function J(){d.addEventListener?(d.removeEventListener("DOMContentLoaded",K),a.removeEventListener("load",K)):(d.detachEvent("onreadystatechange",K),a.detachEvent("onload",K))}function K(){(d.addEventListener||"load"===a.event.type||"complete"===d.readyState)&&(J(),n.ready())}n.ready.promise=function(b){if(!I)if(I=n.Deferred(),"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll)a.setTimeout(n.ready);else if(d.addEventListener)d.addEventListener("DOMContentLoaded",K),a.addEventListener("load",K);else{d.attachEvent("onreadystatechange",K),a.attachEvent("onload",K);var c=!1;try{c=null==a.frameElement&&d.documentElement}catch(e){}c&&c.doScroll&&!function f(){if(!n.isReady){try{c.doScroll("left")}catch(b){return a.setTimeout(f,50)}J(),n.ready()}}()}return I.promise(b)},n.ready.promise();var L;for(L in n(l))break;l.ownFirst="0"===L,l.inlineBlockNeedsLayout=!1,n(function(){var a,b,c,e;c=d.getElementsByTagName("body")[0],c&&c.style&&(b=d.createElement("div"),e=d.createElement("div"),e.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(e).appendChild(b),"undefined"!=typeof b.style.zoom&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",l.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(e))}),function(){var a=d.createElement("div");l.deleteExpando=!0;try{delete a.test}catch(b){l.deleteExpando=!1}a=null}();var M=function(a){var b=n.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b},N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(O,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}n.data(a,b,c)}else c=void 0; +}return c}function Q(a){var b;for(b in a)if(("data"!==b||!n.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function R(a,b,d,e){if(M(a)){var f,g,h=n.expando,i=a.nodeType,j=i?n.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||n.guid++:h),j[k]||(j[k]=i?{}:{toJSON:n.noop}),"object"!=typeof b&&"function"!=typeof b||(e?j[k]=n.extend(j[k],b):j[k].data=n.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[n.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[n.camelCase(b)])):f=g,f}}function S(a,b,c){if(M(a)){var d,e,f=a.nodeType,g=f?n.cache:a,h=f?a[n.expando]:n.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){n.isArray(b)?b=b.concat(n.map(b,n.camelCase)):b in d?b=[b]:(b=n.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!Q(d):!n.isEmptyObject(d))return}(c||(delete g[h].data,Q(g[h])))&&(f?n.cleanData([a],!0):l.deleteExpando||g!=g.window?delete g[h]:g[h]=void 0)}}}n.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?n.cache[a[n.expando]]:a[n.expando],!!a&&!Q(a)},data:function(a,b,c){return R(a,b,c)},removeData:function(a,b){return S(a,b)},_data:function(a,b,c){return R(a,b,c,!0)},_removeData:function(a,b){return S(a,b,!0)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=n.data(f),1===f.nodeType&&!n._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));n._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){n.data(this,a)}):arguments.length>1?this.each(function(){n.data(this,a,b)}):f?P(f,a,n.data(f,a)):void 0},removeData:function(a){return this.each(function(){n.removeData(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=n._data(a,b),c&&(!d||n.isArray(c)?d=n._data(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return n._data(a,c)||n._data(a,c,{empty:n.Callbacks("once memory").add(function(){n._removeData(a,b+"queue"),n._removeData(a,c)})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},Z=/^(?:checkbox|radio)$/i,$=/<([\w:-]+)/,_=/^$|\/(?:java|ecma)script/i,aa=/^\s+/,ba="abbr|article|aside|audio|bdi|canvas|data|datalist|details|dialog|figcaption|figure|footer|header|hgroup|main|mark|meter|nav|output|picture|progress|section|summary|template|time|video";function ca(a){var b=ba.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}!function(){var a=d.createElement("div"),b=d.createDocumentFragment(),c=d.createElement("input");a.innerHTML="
a",l.leadingWhitespace=3===a.firstChild.nodeType,l.tbody=!a.getElementsByTagName("tbody").length,l.htmlSerialize=!!a.getElementsByTagName("link").length,l.html5Clone="<:nav>"!==d.createElement("nav").cloneNode(!0).outerHTML,c.type="checkbox",c.checked=!0,b.appendChild(c),l.appendChecked=c.checked,a.innerHTML="",l.noCloneChecked=!!a.cloneNode(!0).lastChild.defaultValue,b.appendChild(a),c=d.createElement("input"),c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),a.appendChild(c),l.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,l.noCloneEvent=!!a.addEventListener,a[n.expando]=1,l.attributes=!a.getAttribute(n.expando)}();var da={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:l.htmlSerialize?[0,"",""]:[1,"X
","
"]};da.optgroup=da.option,da.tbody=da.tfoot=da.colgroup=da.caption=da.thead,da.th=da.td;function ea(a,b){var c,d,e=0,f="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||n.nodeName(d,b)?f.push(d):n.merge(f,ea(d,b));return void 0===b||b&&n.nodeName(a,b)?n.merge([a],f):f}function fa(a,b){for(var c,d=0;null!=(c=a[d]);d++)n._data(c,"globalEval",!b||n._data(b[d],"globalEval"))}var ga=/<|&#?\w+;/,ha=/r;r++)if(g=a[r],g||0===g)if("object"===n.type(g))n.merge(q,g.nodeType?[g]:g);else if(ga.test(g)){i=i||p.appendChild(b.createElement("div")),j=($.exec(g)||["",""])[1].toLowerCase(),m=da[j]||da._default,i.innerHTML=m[1]+n.htmlPrefilter(g)+m[2],f=m[0];while(f--)i=i.lastChild;if(!l.leadingWhitespace&&aa.test(g)&&q.push(b.createTextNode(aa.exec(g)[0])),!l.tbody){g="table"!==j||ha.test(g)?""!==m[1]||ha.test(g)?0:i:i.firstChild,f=g&&g.childNodes.length;while(f--)n.nodeName(k=g.childNodes[f],"tbody")&&!k.childNodes.length&&g.removeChild(k)}n.merge(q,i.childNodes),i.textContent="";while(i.firstChild)i.removeChild(i.firstChild);i=p.lastChild}else q.push(b.createTextNode(g));i&&p.removeChild(i),l.appendChecked||n.grep(ea(q,"input"),ia),r=0;while(g=q[r++])if(d&&n.inArray(g,d)>-1)e&&e.push(g);else if(h=n.contains(g.ownerDocument,g),i=ea(p.appendChild(g),"script"),h&&fa(i),c){f=0;while(g=i[f++])_.test(g.type||"")&&c.push(g)}return i=null,p}!function(){var b,c,e=d.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(l[b]=c in a)||(e.setAttribute(c,"t"),l[b]=e.attributes[c].expando===!1);e=null}();var ka=/^(?:input|select|textarea)$/i,la=/^key/,ma=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,na=/^(?:focusinfocus|focusoutblur)$/,oa=/^([^.]*)(?:\.(.+)|)/;function pa(){return!0}function qa(){return!1}function ra(){try{return d.activeElement}catch(a){}}function sa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)sa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=qa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return n().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=n.guid++)),a.each(function(){n.event.add(this,b,e,d,c)})}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=n.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return"undefined"==typeof n||a&&n.event.triggered===a.type?void 0:n.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(G)||[""],h=b.length;while(h--)f=oa.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=n.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=n.event.special[o]||{},l=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},i),(m=g[o])||(m=g[o]=[],m.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,l):m.push(l),n.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n.hasData(a)&&n._data(a);if(r&&(k=r.events)){b=(b||"").match(G)||[""],j=b.length;while(j--)if(h=oa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=m.length;while(f--)g=m[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(m.splice(f,1),g.selector&&m.delegateCount--,l.remove&&l.remove.call(a,g));i&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(k)&&(delete r.handle,n._removeData(a,"events"))}},trigger:function(b,c,e,f){var g,h,i,j,l,m,o,p=[e||d],q=k.call(b,"type")?b.type:b,r=k.call(b,"namespace")?b.namespace.split("."):[];if(i=m=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!na.test(q+n.event.triggered)&&(q.indexOf(".")>-1&&(r=q.split("."),q=r.shift(),r.sort()),h=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=r.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:n.makeArray(c,[b]),l=n.event.special[q]||{},f||!l.trigger||l.trigger.apply(e,c)!==!1)){if(!f&&!l.noBubble&&!n.isWindow(e)){for(j=l.delegateType||q,na.test(j+q)||(i=i.parentNode);i;i=i.parentNode)p.push(i),m=i;m===(e.ownerDocument||d)&&p.push(m.defaultView||m.parentWindow||a)}o=0;while((i=p[o++])&&!b.isPropagationStopped())b.type=o>1?j:l.bindType||q,g=(n._data(i,"events")||{})[b.type]&&n._data(i,"handle"),g&&g.apply(i,c),g=h&&i[h],g&&g.apply&&M(i)&&(b.result=g.apply(i,c),b.result===!1&&b.preventDefault());if(b.type=q,!f&&!b.isDefaultPrevented()&&(!l._default||l._default.apply(p.pop(),c)===!1)&&M(e)&&h&&e[q]&&!n.isWindow(e)){m=e[h],m&&(e[h]=null),n.event.triggered=q;try{e[q]()}catch(s){}n.event.triggered=void 0,m&&(e[h]=m)}return b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,d,f,g,h=[],i=e.call(arguments),j=(n._data(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())a.rnamespace&&!a.rnamespace.test(g.namespace)||(a.handleObj=g,a.data=g.data,d=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==d&&(a.result=d)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&("click"!==a.type||isNaN(a.button)||a.button<1))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>-1:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]","i"),va=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,wa=/\s*$/g,Aa=ca(d),Ba=Aa.appendChild(d.createElement("div"));function Ca(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function Da(a){return a.type=(null!==n.find.attr(a,"type"))+"/"+a.type,a}function Ea(a){var b=ya.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Fa(a,b){if(1===b.nodeType&&n.hasData(a)){var c,d,e,f=n._data(a),g=n._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)n.event.add(b,c,h[c][d])}g.data&&(g.data=n.extend({},g.data))}}function Ga(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!l.noCloneEvent&&b[n.expando]){e=n._data(b);for(d in e.events)n.removeEvent(b,d,e.handle);b.removeAttribute(n.expando)}"script"===c&&b.text!==a.text?(Da(b).text=a.text,Ea(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),l.html5Clone&&a.innerHTML&&!n.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&Z.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}}function Ha(a,b,c,d){b=f.apply([],b);var e,g,h,i,j,k,m=0,o=a.length,p=o-1,q=b[0],r=n.isFunction(q);if(r||o>1&&"string"==typeof q&&!l.checkClone&&xa.test(q))return a.each(function(e){var f=a.eq(e);r&&(b[0]=q.call(this,e,f.html())),Ha(f,b,c,d)});if(o&&(k=ja(b,a[0].ownerDocument,!1,a,d),e=k.firstChild,1===k.childNodes.length&&(k=e),e||d)){for(i=n.map(ea(k,"script"),Da),h=i.length;o>m;m++)g=k,m!==p&&(g=n.clone(g,!0,!0),h&&n.merge(i,ea(g,"script"))),c.call(a[m],g,m);if(h)for(j=i[i.length-1].ownerDocument,n.map(i,Ea),m=0;h>m;m++)g=i[m],_.test(g.type||"")&&!n._data(g,"globalEval")&&n.contains(j,g)&&(g.src?n._evalUrl&&n._evalUrl(g.src):n.globalEval((g.text||g.textContent||g.innerHTML||"").replace(za,"")));k=e=null}return a}function Ia(a,b,c){for(var d,e=b?n.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||n.cleanData(ea(d)),d.parentNode&&(c&&n.contains(d.ownerDocument,d)&&fa(ea(d,"script")),d.parentNode.removeChild(d));return a}n.extend({htmlPrefilter:function(a){return a.replace(va,"<$1>")},clone:function(a,b,c){var d,e,f,g,h,i=n.contains(a.ownerDocument,a);if(l.html5Clone||n.isXMLDoc(a)||!ua.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(Ba.innerHTML=a.outerHTML,Ba.removeChild(f=Ba.firstChild)),!(l.noCloneEvent&&l.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(d=ea(f),h=ea(a),g=0;null!=(e=h[g]);++g)d[g]&&Ga(e,d[g]);if(b)if(c)for(h=h||ea(a),d=d||ea(f),g=0;null!=(e=h[g]);g++)Fa(e,d[g]);else Fa(a,f);return d=ea(f,"script"),d.length>0&&fa(d,!i&&ea(a,"script")),d=h=e=null,f},cleanData:function(a,b){for(var d,e,f,g,h=0,i=n.expando,j=n.cache,k=l.attributes,m=n.event.special;null!=(d=a[h]);h++)if((b||M(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)m[e]?n.event.remove(d,e):n.removeEvent(d,e,g.handle);j[f]&&(delete j[f],k||"undefined"==typeof d.removeAttribute?d[i]=void 0:d.removeAttribute(i),c.push(f))}}}),n.fn.extend({domManip:Ha,detach:function(a){return Ia(this,a,!0)},remove:function(a){return Ia(this,a)},text:function(a){return Y(this,function(a){return void 0===a?n.text(this):this.empty().append((this[0]&&this[0].ownerDocument||d).createTextNode(a))},null,a,arguments.length)},append:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.appendChild(a)}})},prepend:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&n.cleanData(ea(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&n.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return Y(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(ta,""):void 0;if("string"==typeof a&&!wa.test(a)&&(l.htmlSerialize||!ua.test(a))&&(l.leadingWhitespace||!aa.test(a))&&!da[($.exec(a)||["",""])[1].toLowerCase()]){a=n.htmlPrefilter(a);try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(ea(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return Ha(this,arguments,function(b){var c=this.parentNode;n.inArray(this,a)<0&&(n.cleanData(ea(this)),c&&c.replaceChild(b,this))},a)}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=0,e=[],f=n(a),h=f.length-1;h>=d;d++)c=d===h?this:this.clone(!0),n(f[d])[b](c),g.apply(e,c.get());return this.pushStack(e)}});var Ja,Ka={HTML:"block",BODY:"block"};function La(a,b){var c=n(b.createElement(a)).appendTo(b.body),d=n.css(c[0],"display");return c.detach(),d}function Ma(a){var b=d,c=Ka[a];return c||(c=La(a,b),"none"!==c&&c||(Ja=(Ja||n("