diff --git a/client/py/audio_manager.py b/client/py/audio_manager.py
new file mode 100644
index 0000000..de90baf
--- /dev/null
+++ b/client/py/audio_manager.py
@@ -0,0 +1,64 @@
+import vlc
+import time
+
+#instance = vlc.Instance("-vvv", "--no-video", "--repeat")
+#player = instance.media_player_new()
+#media = instance.media_new("rtsp://piscan-arbor:8554/audio", "network-caching=25")
+#player.set_media(media)
+#player.play()
+#while(True):
+# time.sleep(1)
+
+class AudioManager:
+ vlc = None
+ player = None
+ media = None
+
+ def __init__(self):
+ self.vlc = vlc.Instance('--no-video', '--repeat', '-v')
+
+ def close(self):
+
+ if self.player:
+ self.player.stop()
+ self.player.release()
+
+ self.vlc.release()
+
+ def startRtspAudioStream(self, host, port):
+ print('create media')
+ self.media = self.vlc.media_new('rtsp://' + host + ':' + port + '/audio', 'network-caching=50')
+ print('create new player')
+ self.player = self.media.player_new_from_media()
+ print('start player')
+ self.player.play()
+
+ def stopRtspAudioStream(self):
+ print('stopping audio')
+ if self.player:
+ self.player.stop()
+ self.player.release()
+ self.player = None
+ self.media.release()
+ self.media = None
+
+ def setAudioVolume(self, level : int):
+ self.player.audio_set_volume(level)
+
+ def setAudioMute(self, mute : bool):
+ self.player.audio_set_mute(mute)
+
+if __name__ == '__main__':
+ audio = AudioManager()
+
+ audio.startRtspAudioStream("pibox-airglow", "8554")
+
+ try:
+ while(True):
+ time.sleep(1)
+ except KeyboardInterrupt:
+ pass
+
+ audio.stopRtspAudioStream()
+
+ audio.close()
diff --git a/client/py/client.py b/client/py/client.py
index 0c43874..8822dd4 100644
--- a/client/py/client.py
+++ b/client/py/client.py
@@ -12,6 +12,7 @@ from threading import Thread
from time import sleep
+import audio_manager
import common
import constants
import connect
@@ -27,6 +28,7 @@ import google.protobuf.message as proto
class PiScanClient(QWidget, common.AppInterface):
dataReceived = Signal(bytes)
+ manualDisconnectInitiated = False
def __init__(self, parent=None, address=None, port=None):
super(PiScanClient, self).__init__(parent)
@@ -59,6 +61,8 @@ class PiScanClient(QWidget, common.AppInterface):
self.inmsg = messages_pb2.ServerToClient()
+ self.audio = audio_manager.AudioManager()
+
def mainWidget(self):
return self.window
@@ -72,16 +76,28 @@ class PiScanClient(QWidget, common.AppInterface):
self.setWindowMode(common.WindowMode.DIALOG)
def receive(self):
+ disconnectMessage = ''
+
while True:
try:
data = self.sock.recv(2048)
+ if not data:
+ disconnectMessage = 'Connection closed by host'
+ break
self.dataReceived.emit(data)
+ except ConnectionAbortedError:
+ if not self.manualDisconnectInitiated:
+ disconnectMessage = 'Connection aborted'
+ break
except:
e = sys.exc_info()[0]
- self.showConnectDialog(repr(e))
+ disconnectMessage = 'Unhandled exception: ' + str(e)
break
- print("Closing connection")
+ print("Closing connection. Reason: " + disconnectMessage)
+ self.audio.stopRtspAudioStream()
+ self.manualDisconnectInitiated = False
+ self.showConnectDialog(disconnectMessage)
def handleReceived(self, data):
self.inmsg.ParseFromString(data)
@@ -110,18 +126,13 @@ class PiScanClient(QWidget, common.AppInterface):
def closeEvent(self, event):
print('Exiting...')
- try:
- if self.sock:
- self.sock.close()
- #self.rcv_thread.join()
- except:
- pass
+ self.disconnect()
## AppInterface methods
#def attemptConnection(self, sock):
- def completeConnection(self, sock):
+ def completeConnection(self, sock, host, use_audio, audio_port):
self.sock = sock
self.rcv_thread = Thread(target=self.receive)
self.rcv_thread.start()
@@ -141,7 +152,12 @@ class PiScanClient(QWidget, common.AppInterface):
message3 = messages_pb2.ClientToServer()
message3.type = messages_pb2.ClientToServer.Type.Value('GENERAL_REQUEST')
message3.generalRequest.type = request_pb2.GeneralRequest.RequestType.Value('SYSTEM_INFO')
- self.sock.send(message3.SerializeToString())
+ self.sock.send(message3.SerializeToString())
+
+ if use_audio:
+ self.audio.startRtspAudioStream(host, audio_port)
+ self.setAudioVolume(50)
+ self.scanner.setVolumeVisible(use_audio)
def scan(self):
message = messages_pb2.ClientToServer()
@@ -216,6 +232,21 @@ class PiScanClient(QWidget, common.AppInterface):
else:
self.setWindowTitle('PiScan')
+ def disconnect(self):
+ self.manualDisconnectInitiated = True
+ try:
+ if self.sock:
+ self.sock.close()
+ #self.rcv_thread.join()
+ except:
+ pass
+
+ def setAudioVolume(self, level):
+ self.audio.setAudioVolume(level)
+
+ def setAudioMute(self, mute):
+ self.audio.setAudioMute(mute)
+
class HostWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None, address=None, port=None):
super(HostWindow, self).__init__(parent)
diff --git a/client/py/common.py b/client/py/common.py
index 95f58ce..389ce71 100644
--- a/client/py/common.py
+++ b/client/py/common.py
@@ -31,7 +31,7 @@ class AppInterface:
#def attemptConnection(self, sock):
# pass
- def completeConnection(self, sock):
+ def completeConnection(self, sock, host, use_audio, audio_port):
pass
def scan(self):
@@ -75,3 +75,12 @@ class AppInterface:
def clearWindowTitleInfo(self):
pass
+
+ def disconnect(self):
+ pass
+
+ def setAudioVolume(self, level):
+ pass
+
+ def setAudioMute(self, mute):
+ pass
diff --git a/client/py/connect.py b/client/py/connect.py
index 633635e..38086ec 100644
--- a/client/py/connect.py
+++ b/client/py/connect.py
@@ -1,7 +1,7 @@
import sys
from PySide2.QtUiTools import QUiLoader
-from PySide2.QtWidgets import QWidget, QLabel, QPushButton, QLineEdit
+from PySide2.QtWidgets import QWidget, QLabel, QPushButton, QLineEdit, QCheckBox
from PySide2.QtCore import QObject
from PySide2.QtGui import QMovie, QPixmap
@@ -21,6 +21,9 @@ class ConnectDialog:
self.logo = parentWindow.findChild(QLabel, 'connect_logoImage')
self.hostLabel = parentWindow.findChild(QLabel, 'hostLabel')
self.portLabel = parentWindow.findChild(QLabel, 'hostPortLabel')
+ self.audioCheckBox = parentWindow.findChild(QCheckBox, 'connect_audioCheckBox')
+ self.rtspPortPanel = parentWindow.findChild(QWidget, 'connect_rtspPortPanel')
+ self.rtspPortLineEdit = parentWindow.findChild(QLineEdit, 'connect_rtspPortLineEdit')
self.logo.setPixmap(QPixmap("resources/icon-256.png"))
self.logo.setVisible(False)
@@ -39,6 +42,8 @@ class ConnectDialog:
self.hostLineEdit.returnPressed.connect(self.onConfirm)
self.portLineEdit.returnPressed.connect(self.onConfirm)
+ self.rtspPortPanel.setVisible(False)
+
def onConfirm(self):
host = self.hostLineEdit.text()
port = int(self.portLineEdit.text())
@@ -62,11 +67,19 @@ class ConnectDialog:
self.connectIndicator.setVisible(False)
- common.getApp().completeConnection(sock)
+ use_audio = self.audioCheckBox.isChecked()
+ audio_port = self.rtspPortLineEdit.text()
+ common.getApp().completeConnection(sock, address, use_audio, audio_port)
+ except ConnectionRefusedError:
+ self.connectFailed('Connect failed - Connection refused')
+ except gaierror as err:
+ self.connectFailed('Connect failed - ' + str(err))
+ except TimeoutError:
+ self.connectFailed('Connect failed - Timed out')
except:
e = sys.exc_info()[0]
- self.connectFailed(repr(e))
+ self.connectFailed('Connect failed - Unhandled exception: ' + str(e))
def contextWait(self):
diff --git a/client/py/scan_client.ui b/client/py/scan_client.ui
index 23a2f3f..95c21c2 100644
--- a/client/py/scan_client.ui
+++ b/client/py/scan_client.ui
@@ -481,10 +481,47 @@
-
- 1
+ 0
+
-
+
+
+
+
+
+ Qt::RichText
+
+
+ :/indicator/loader-small.gif
+
+
+ Qt::AlignHCenter|Qt::AlignTop
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 12
+
+
+
+ Connect
+
+
+ Connect
+
+
+
-
@@ -967,6 +1004,9 @@
12
+
+ Host address
+
localhost
@@ -1439,6 +1479,9 @@
12
+
+ TCP Port
+
1234
@@ -1447,24 +1490,541 @@
- -
-
+
-
+
-
+
0
0
-
-
- 12
-
-
-
- Connect
-
+
+
+ 0
+
+
+ 9
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 12
+
+
+
+ Connect with audio
+
+
+ Audio
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 12
+
+
+
+ Audio Port
+
+
+
+ -
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+ 127
+ 127
+ 127
+
+
+
+
+
+
+ 170
+ 170
+ 170
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+ 255
+ 255
+ 220
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+ 127
+ 127
+ 127
+
+
+
+
+
+
+ 170
+ 170
+ 170
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+ 255
+ 255
+ 220
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+
+
+ 127
+ 127
+ 127
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+ 127
+ 127
+ 127
+
+
+
+
+
+
+ 170
+ 170
+ 170
+
+
+
+
+
+
+ 127
+ 127
+ 127
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+ 127
+ 127
+ 127
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+ 255
+ 255
+ 220
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+
+
+ 12
+
+
+
+ RTSP port
+
+
+ 8554
+
+
+
+
+
+
+
+ -
+
+
-
@@ -1523,61 +2083,6 @@
- -
-
-
-
- 0
- 0
-
-
-
-
- 0
-
-
- 9
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 12
-
-
-
- Audio
-
-
-
-
-
-
- -
-
-
-
-
-
- Qt::RichText
-
-
- :/indicator/loader-small.gif
-
-
- Qt::AlignHCenter|Qt::AlignTop
-
-
-
- -
-
-
@@ -1623,8 +2128,11 @@
-
+
+ Disconnect
+
-
+ -X-
@@ -1642,7 +2150,10 @@
-
-
+
+
+ Settings
+
...
@@ -1662,7 +2173,10 @@
-
-
+
+
+ Connection info
+
...
@@ -1726,6 +2240,15 @@
0
+
+ Volume
+
+
+ 100
+
+
+ 50
+
Qt::Vertical
@@ -1733,8 +2256,11 @@
-
+
+ Mute
+
-
+ M
true
@@ -1810,6 +2336,9 @@
16
+
+ Scan mode
+
S
@@ -1827,6 +2356,9 @@
-
+
+ Edit entry
+
@@ -1845,6 +2377,9 @@
16
+
+ Entry index
+
0-0
@@ -1884,6 +2419,9 @@
5
+
+ Signal level
+
24
@@ -1908,6 +2446,9 @@
20
+
+ System tag
+
System Tag
@@ -1923,6 +2464,9 @@
20
+
+ Entry tag
+
Entry Tag
@@ -1955,45 +2499,6 @@
0
-
-
-
-
-
- 0
- 0
-
-
-
-
- 16
-
-
-
- 000.0000 MHz
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 16
-
-
-
- Modulation
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
-
-
@@ -2015,23 +2520,10 @@
0
-
-
-
-
-
- 0
- 0
-
-
-
-
-
-
-
-
-
+
0
0
@@ -2042,14 +2534,50 @@
- LO: N
+ LO:
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ Cycle lockout options
+
+
+ None
- -
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 16
+
+
+
+ Current frequency
+
+
+ 000.0000 MHz
+
+
+
+ -
@@ -2062,6 +2590,9 @@
16
+
+ Scan delay
+
D:0.0s
@@ -2070,6 +2601,30 @@
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 16
+
+
+
+ Modulation
+
+
+ Modulation
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
@@ -2147,8 +2702,17 @@
-
+
+
+ 75
+ true
+
+
+
+ Open/close sidebar
+
-
+ <
true
@@ -2226,6 +2790,9 @@
0
+
+ Set squelch level
+
-100
@@ -2268,6 +2835,9 @@
0
+
+ Set tuner gain
+
-1
@@ -2325,6 +2895,9 @@
true
+
+ Scan
+
Scan
@@ -2339,6 +2912,9 @@
true
+
+ Hold entry
+
Hold
@@ -2353,6 +2929,9 @@
true
+
+ Manual entry
+
Manual
@@ -2367,6 +2946,9 @@
true
+
+ Browse entries
+
Browser
@@ -2850,6 +3432,9 @@
0
+
+ Back
+
Back
@@ -2876,7 +3461,7 @@
-
- 2
+ 0
@@ -3380,6 +3965,9 @@
12
+
+ Frequency in megahertz
+
000.0000 MHz
@@ -3849,6 +4437,9 @@
12
+
+ Modulation
+
-
@@ -4300,6 +4891,9 @@
12
+
+ Tune
+
Tune
@@ -4323,7 +4917,7 @@
0
0
- 443
+ 179
223
@@ -7813,5 +8407,38 @@
-
+
+
+ scanner_sidebarToggle
+ toggled(bool)
+ scanner_sidebarPanel
+ setVisible(bool)
+
+
+ 390
+ 126
+
+
+ 439
+ 125
+
+
+
+
+ connect_audioCheckBox
+ toggled(bool)
+ connect_rtspPortPanel
+ setVisible(bool)
+
+
+ 147
+ 137
+
+
+ 239
+ 167
+
+
+
+
diff --git a/client/py/scanner.py b/client/py/scanner.py
index f2ad3d4..d3eb963 100644
--- a/client/py/scanner.py
+++ b/client/py/scanner.py
@@ -26,7 +26,8 @@ class Scanner:
self.modulationLabel = parentWindow.findChild(QLabel, 'scanner_modulationLabel')
self.systemTagLabel = parentWindow.findChild(QLabel, 'scanner_systemTagLabel')
self.entryNumLabel = parentWindow.findChild(QLabel, 'scanner_entryNumLabel')
- self.lockoutCheckbox = parentWindow.findChild(QCheckBox, 'scanner_lockoutCheckbox')
+ self.lockoutDurationLabel = parentWindow.findChild(QLabel, 'scanner_lockoutDurationLabel')
+ self.lockoutDurationButton = parentWindow.findChild(QPushButton, 'scanner_lockoutDurationButton')
self.scanIndicator = parentWindow.findChild(QLabel, 'scanner_scanIndicator')
self.gainSlider = parentWindow.findChild(QSlider, 'scanner_gainSlider')
##self.gainLabel = parentWindow.findChild(QLabel, 'scanner_gainLabel')
@@ -42,6 +43,16 @@ class Scanner:
self.sidebarToggleButton = parentWindow.findChild(QToolButton, 'scanner_sidebarToggle')
self.sidebarPanel = parentWindow.findChild(QWidget, 'scanner_sidebarPanel')
+ self.disconnectButton = parentWindow.findChild(QToolButton, 'scanner_disconnectButton')
+ self.settingsButton = parentWindow.findChild(QToolButton, 'scanner_settingsButton')
+ self.connectInfoButton = parentWindow.findChild(QToolButton, 'scanner_connectInfoButton')
+
+ self.volumeControlPanel = parentWindow.findChild(QWidget, 'scanner_volumeControl')
+ self.volumeSlider = parentWindow.findChild(QWidget, 'scanner_volumeSlider')
+ self.muteButton = parentWindow.findChild(QWidget, 'scanner_volumeMute')
+
+ self.entryEditButton = parentWindow.findChild(QToolButton, 'scanner_entryEditButton')
+
self.fnButton1.clicked.connect(self.onFnButton1)
self.fnButton2.clicked.connect(self.onFnButton2)
self.fnButton3.clicked.connect(self.onFnButton3)
@@ -50,10 +61,22 @@ class Scanner:
self.squelchSlider.valueChanged.connect(self.onsquelchSlider)
self.sidebarToggleButton.clicked.connect(self.onSidebarToggle)
- self.sidebarOpen = True
+ self.sidebarOpen = False
+ self.sidebarPanel.setVisible(False)
+
+ self.disconnectButton.clicked.connect(self.onDisconnectButton)
+
+ self.volumeSlider.valueChanged.connect(self.onVolumeSlider)
+ self.muteButton.toggled.connect(self.onMuteButton)
#temporary since settins dialog is not yet implemented
- self.fnButton4.setEnabled(False)
+ self.volumeControlPanel.setVisible(False)
+ self.lockoutDurationButton.setVisible(False)
+ self.lockoutDurationLabel.setVisible(False)
+ self.fnButton4.setVisible(False)
+ self.settingsButton.setVisible(False)
+ self.connectInfoButton.setVisible(False)
+ self.entryEditButton.setVisible(False)
movie = QMovie("resources/bar-scan.gif")
movie.start()
@@ -178,9 +201,11 @@ class Scanner:
def onSidebarToggle(self):
if self.sidebarOpen:
+ self.sidebarToggleButton.setText('<')
self.sidebarPanel.setVisible(False)
self.sidebarOpen = False
else:
+ self.sidebarToggleButton.setText('>')
self.sidebarPanel.setVisible(True)
self.sidebarOpen = True
@@ -190,4 +215,17 @@ class Scanner:
def setGainRange(self, minimum, maximum):
self.gainSlider.setMinimum(minimum)
- self.gainSlider.setMaximum(maximum)
\ No newline at end of file
+ self.gainSlider.setMaximum(maximum)
+
+ def onDisconnectButton(self):
+ common.getApp().disconnect()
+
+ def onVolumeSlider(self, value):
+ common.getApp().setAudioVolume(value)
+
+ def onMuteButton(self, value):
+ self.volumeSlider.setEnabled(not value)
+ common.getApp().setAudioMute(value)
+
+ def setVolumeVisible(self, visible):
+ self.volumeControlPanel.setVisible(visible)