mirror of
https://github.com/jeremysrand/ListenerApp.git
synced 2024-06-10 19:29:31 +00:00
Compare commits
2 Commits
c61b9c0729
...
109213a577
Author | SHA1 | Date | |
---|---|---|---|
|
109213a577 | ||
|
b732007d68 |
|
@ -17,6 +17,21 @@
|
||||||
</dict>
|
</dict>
|
||||||
<key>SuppressBuildableAutocreation</key>
|
<key>SuppressBuildableAutocreation</key>
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>9D5155EE26A1EF7B0075EBC7</key>
|
||||||
|
<dict>
|
||||||
|
<key>primary</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<key>9D5155FF26A1EF7C0075EBC7</key>
|
||||||
|
<dict>
|
||||||
|
<key>primary</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<key>9D51560A26A1EF7C0075EBC7</key>
|
||||||
|
<dict>
|
||||||
|
<key>primary</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
<key>9D62FC5F27C494D700AEE01F</key>
|
<key>9D62FC5F27C494D700AEE01F</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>primary</key>
|
<key>primary</key>
|
||||||
|
|
|
@ -68,8 +68,8 @@ class GSConnection : ObservableObject {
|
||||||
private let writeQueue = OperationQueue()
|
private let writeQueue = OperationQueue()
|
||||||
private var mainQueue = OperationQueue.main
|
private var mainQueue = OperationQueue.main
|
||||||
|
|
||||||
private var condition = NSCondition()
|
|
||||||
private var canSend = true
|
private var canSend = true
|
||||||
|
private var lastSent = ""
|
||||||
|
|
||||||
private func changeState(newState : GSConnectionState)
|
private func changeState(newState : GSConnectionState)
|
||||||
{
|
{
|
||||||
|
@ -123,64 +123,61 @@ class GSConnection : ObservableObject {
|
||||||
logger.debug("Connected to \(self.destination)")
|
logger.debug("Connected to \(self.destination)")
|
||||||
}
|
}
|
||||||
|
|
||||||
private func doConnect() {
|
|
||||||
logger.debug("Attempting to connect to \(self.destination)")
|
|
||||||
client = TCPClient(address: destination, port: Int32(GSConnection.port))
|
|
||||||
guard let client = client else {
|
|
||||||
mainQueue.addOperation { self.connectionFailed() }
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch client.connect(timeout: 10) {
|
|
||||||
case .success:
|
|
||||||
mainQueue.addOperation { self.connectionSuccessful() }
|
|
||||||
case .failure(let error):
|
|
||||||
client.close()
|
|
||||||
self.client = nil
|
|
||||||
logger.error("Failed to connect to \(self.destination): \(String(describing: error))")
|
|
||||||
mainQueue.addOperation { self.connectionFailed() }
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
guard let byteArray = client.read(2) else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if (byteArray.count != 2) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
let data = Data(byteArray)
|
|
||||||
do {
|
|
||||||
let unpacked = try unpack("<h", data)
|
|
||||||
if (unpacked[0] as? Int == GSConnection.LISTEN_SEND_MORE) {
|
|
||||||
condition.lock()
|
|
||||||
canSend = true
|
|
||||||
condition.broadcast()
|
|
||||||
condition.unlock()
|
|
||||||
} else {
|
|
||||||
logger.error("Unexpected message on socket from \(self.destination)")
|
|
||||||
errorOccurred(title: "Protocol Error", message: "Unexpected message from the GS")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
logger.error("Unable to unpack message on socket from \(self.destination)")
|
|
||||||
errorOccurred(title: "Protocol Error", message: "Unexpected message from the GS")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
client.close()
|
|
||||||
self.client = nil
|
|
||||||
mainQueue.addOperation { self.disconnect() }
|
|
||||||
}
|
|
||||||
|
|
||||||
func connect(destination : String) {
|
func connect(destination : String) {
|
||||||
self.destination = destination
|
self.destination = destination
|
||||||
changeState(newState: .connecting)
|
changeState(newState: .connecting)
|
||||||
readQueue.addOperation {
|
readQueue.addOperation { [weak self, destination] in
|
||||||
self.doConnect()
|
self?.logger.debug("Attempting to connect to \(destination)")
|
||||||
|
let client = TCPClient(address: destination, port: Int32(GSConnection.port))
|
||||||
|
switch client.connect(timeout: 10) {
|
||||||
|
case .success:
|
||||||
|
self?.mainQueue.addOperation {
|
||||||
|
self?.client = client
|
||||||
|
self?.connectionSuccessful()
|
||||||
|
}
|
||||||
|
case .failure(let error):
|
||||||
|
client.close()
|
||||||
|
self?.logger.error("Failed to connect to \(destination): \(String(describing: error))")
|
||||||
|
self?.mainQueue.addOperation {
|
||||||
|
self?.connectionFailed()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
guard let byteArray = client.read(2) else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (byteArray.count != 2) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let self = self else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
let data = Data(byteArray)
|
||||||
|
do {
|
||||||
|
let unpacked = try unpack("<h", data)
|
||||||
|
if (unpacked[0] as? Int == GSConnection.LISTEN_SEND_MORE) {
|
||||||
|
self.mainQueue.addOperation {
|
||||||
|
self.canSend = true
|
||||||
|
self.trySend()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.logger.error("Unexpected message on socket from \(destination)")
|
||||||
|
self.errorOccurred(title: "Protocol Error", message: "Unexpected message from the GS")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
self.logger.error("Unable to unpack message on socket from \(destination)")
|
||||||
|
self.errorOccurred(title: "Protocol Error", message: "Unexpected message from the GS")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self?.mainQueue.addOperation { self?.disconnect() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,13 +190,10 @@ class GSConnection : ObservableObject {
|
||||||
stopListening()
|
stopListening()
|
||||||
}
|
}
|
||||||
|
|
||||||
condition.lock()
|
|
||||||
if (client != nil) {
|
if (client != nil) {
|
||||||
client!.close()
|
client!.close()
|
||||||
self.client = nil
|
self.client = nil
|
||||||
}
|
}
|
||||||
condition.broadcast()
|
|
||||||
condition.unlock()
|
|
||||||
|
|
||||||
waitForWriteQueue()
|
waitForWriteQueue()
|
||||||
waitForReadQueue()
|
waitForReadQueue()
|
||||||
|
@ -212,12 +206,11 @@ class GSConnection : ObservableObject {
|
||||||
speechForwarder.stopListening()
|
speechForwarder.stopListening()
|
||||||
self.speechForwarder = nil
|
self.speechForwarder = nil
|
||||||
}
|
}
|
||||||
condition.lock()
|
|
||||||
if (state == .listening) {
|
if (state == .listening) {
|
||||||
changeState(newState: .stoplistening)
|
changeState(newState: .stoplistening)
|
||||||
condition.broadcast()
|
trySend()
|
||||||
}
|
}
|
||||||
condition.unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func sendListenMsg(isListening: Bool) -> Bool {
|
private func sendListenMsg(isListening: Bool) -> Bool {
|
||||||
|
@ -236,6 +229,7 @@ class GSConnection : ObservableObject {
|
||||||
|
|
||||||
func listen(speechForwarder: SpeechForwarderProtocol) {
|
func listen(speechForwarder: SpeechForwarderProtocol) {
|
||||||
textHeard = ""
|
textHeard = ""
|
||||||
|
lastSent = ""
|
||||||
writeQueue.addOperation {
|
writeQueue.addOperation {
|
||||||
if (!self.sendListenMsg(isListening: true)) {
|
if (!self.sendListenMsg(isListening: true)) {
|
||||||
self.errorOccurred(title: "Write Error", message: "Unable to send data to the GS")
|
self.errorOccurred(title: "Write Error", message: "Unable to send data to the GS")
|
||||||
|
@ -252,57 +246,39 @@ class GSConnection : ObservableObject {
|
||||||
}
|
}
|
||||||
self.speechForwarder = speechForwarder
|
self.speechForwarder = speechForwarder
|
||||||
}
|
}
|
||||||
|
}
|
||||||
self.send()
|
}
|
||||||
|
|
||||||
_ = self.sendListenMsg(isListening: false)
|
private func trySend() {
|
||||||
|
if (textHeard == lastSent) {
|
||||||
self.mainQueue.addOperation {
|
if (state == .stoplistening) {
|
||||||
if (self.state == .stoplistening) {
|
writeQueue.addOperation {
|
||||||
self.changeState(newState: .connected)
|
_ = self.sendListenMsg(isListening: false)
|
||||||
|
}
|
||||||
|
changeState(newState: .connected)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!canSend) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
canSend = false
|
||||||
|
let stringToSend = textHeard
|
||||||
|
writeQueue.addOperation {
|
||||||
|
if self.send(latestText: stringToSend, lastSent: self.lastSent) {
|
||||||
|
self.mainQueue.addOperation {
|
||||||
|
self.lastSent = stringToSend
|
||||||
|
self.trySend()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func set(text:String)
|
func set(text:String) {
|
||||||
{
|
|
||||||
condition.lock()
|
|
||||||
textHeard = text
|
textHeard = text
|
||||||
condition.broadcast()
|
trySend()
|
||||||
condition.unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func send() {
|
|
||||||
var stringLastSent = ""
|
|
||||||
var stringToSend = ""
|
|
||||||
|
|
||||||
while true {
|
|
||||||
condition.lock()
|
|
||||||
guard client != nil else {
|
|
||||||
condition.unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if ((stringLastSent == textHeard) && (state == .stoplistening)) {
|
|
||||||
condition.unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if ((!canSend) ||
|
|
||||||
(stringLastSent == textHeard)) {
|
|
||||||
condition.wait()
|
|
||||||
condition.unlock()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
stringToSend = textHeard
|
|
||||||
condition.unlock()
|
|
||||||
|
|
||||||
if send(latestText: stringToSend, lastSent: stringLastSent) {
|
|
||||||
stringLastSent = stringToSend
|
|
||||||
condition.lock()
|
|
||||||
canSend = false
|
|
||||||
condition.unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func send(latestText : String, lastSent: String) -> Bool {
|
private func send(latestText : String, lastSent: String) -> Bool {
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.0</string>
|
<string>1.0</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>598</string>
|
<string>645</string>
|
||||||
<key>LSApplicationCategoryType</key>
|
<key>LSApplicationCategoryType</key>
|
||||||
<string>public.app-category.utilities</string>
|
<string>public.app-category.utilities</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
|
|
@ -80,7 +80,6 @@ struct Webview: UIViewRepresentable {
|
||||||
decisionHandler(WKNavigationActionPolicy.cancel)
|
decisionHandler(WKNavigationActionPolicy.cancel)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
print("no link")
|
|
||||||
decisionHandler(WKNavigationActionPolicy.allow)
|
decisionHandler(WKNavigationActionPolicy.allow)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@ class SpeechForwarder : SpeechForwarderProtocol {
|
||||||
audioEngine.stop()
|
audioEngine.stop()
|
||||||
audioEngine.inputNode.removeTap(onBus: 0)
|
audioEngine.inputNode.removeTap(onBus: 0)
|
||||||
recognitionTask?.cancel()
|
recognitionTask?.cancel()
|
||||||
|
recognitionTask?.finish()
|
||||||
|
|
||||||
recognitionRequest = nil
|
recognitionRequest = nil
|
||||||
recognitionTask = nil
|
recognitionTask = nil
|
||||||
|
@ -84,12 +85,15 @@ class SpeechForwarder : SpeechForwarderProtocol {
|
||||||
|
|
||||||
// Create a recognition task for the speech recognition session.
|
// Create a recognition task for the speech recognition session.
|
||||||
// Keep a reference to the task so that it can be canceled.
|
// Keep a reference to the task so that it can be canceled.
|
||||||
recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest) { result, error in
|
recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest) { [weak connection] result, error in
|
||||||
var isFinal = false
|
var isFinal = false
|
||||||
|
|
||||||
if let result = result {
|
if let result = result {
|
||||||
// Update the text view with the results.
|
// Update the text view with the results.
|
||||||
OperationQueue.main.addOperation { connection.set(text: result.bestTranscription.formattedString) }
|
OperationQueue.main.addOperation {
|
||||||
|
guard let connection = connection else { return }
|
||||||
|
connection.set(text: result.bestTranscription.formattedString)
|
||||||
|
}
|
||||||
isFinal = result.isFinal
|
isFinal = result.isFinal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,6 +103,7 @@ class SpeechForwarder : SpeechForwarderProtocol {
|
||||||
|
|
||||||
if error != nil || isFinal {
|
if error != nil || isFinal {
|
||||||
OperationQueue.main.addOperation {
|
OperationQueue.main.addOperation {
|
||||||
|
guard let connection = connection else { return }
|
||||||
connection.stopListening()
|
connection.stopListening()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,7 +116,7 @@ open class UDPClient: Socket {
|
||||||
guard let fd = self.fd else {
|
guard let fd = self.fd else {
|
||||||
return (nil, "no ip", 0)
|
return (nil, "no ip", 0)
|
||||||
}
|
}
|
||||||
var buff: [Byte] = [Byte](repeating: 0x0, count: expectlen)
|
let buff: [Byte] = [Byte](repeating: 0x0, count: expectlen)
|
||||||
var remoteipbuff: [Int8] = [Int8](repeating: 0x0, count: 16)
|
var remoteipbuff: [Int8] = [Int8](repeating: 0x0, count: 16)
|
||||||
var remoteport: Int32 = 0
|
var remoteport: Int32 = 0
|
||||||
let readLen: Int32 = c_yudpsocket_recive(fd, buff: buff, len: Int32(expectlen), ip: &remoteipbuff, port: &remoteport)
|
let readLen: Int32 = c_yudpsocket_recive(fd, buff: buff, len: Int32(expectlen), ip: &remoteipbuff, port: &remoteport)
|
||||||
|
@ -154,7 +154,7 @@ open class UDPServer: Socket {
|
||||||
//TODO add multycast and boardcast
|
//TODO add multycast and boardcast
|
||||||
open func recv(_ expectlen: Int) -> ([Byte]?, String, Int) {
|
open func recv(_ expectlen: Int) -> ([Byte]?, String, Int) {
|
||||||
if let fd = self.fd {
|
if let fd = self.fd {
|
||||||
var buff: [Byte] = [Byte](repeating: 0x0,count: expectlen)
|
let buff: [Byte] = [Byte](repeating: 0x0,count: expectlen)
|
||||||
var remoteipbuff: [Int8] = [Int8](repeating: 0x0,count: 16)
|
var remoteipbuff: [Int8] = [Int8](repeating: 0x0,count: 16)
|
||||||
var remoteport: Int32 = 0
|
var remoteport: Int32 = 0
|
||||||
let readLen: Int32 = c_yudpsocket_recive(fd, buff: buff, len: Int32(expectlen), ip: &remoteipbuff, port: &remoteport)
|
let readLen: Int32 = c_yudpsocket_recive(fd, buff: buff, len: Int32(expectlen), ip: &remoteipbuff, port: &remoteport)
|
||||||
|
|
|
@ -90,7 +90,7 @@ int yudpsocket_close(int socket_fd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//return socket fd
|
//return socket fd
|
||||||
int yudpsocket_client() {
|
int yudpsocket_client(void) {
|
||||||
//create socket
|
//create socket
|
||||||
int socketfd = socket(AF_INET, SOCK_DGRAM, 0);
|
int socketfd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
int reuseon = 1;
|
int reuseon = 1;
|
||||||
|
|
|
@ -103,9 +103,10 @@ class ListenerGSTests: XCTestCase {
|
||||||
XCTAssertEqual(connection.textHeard, "Hello, everyone!")
|
XCTAssertEqual(connection.textHeard, "Hello, everyone!")
|
||||||
|
|
||||||
XCTAssert(server.sendMore())
|
XCTAssert(server.sendMore())
|
||||||
|
connection.waitForMain()
|
||||||
|
connection.waitForWriteQueue()
|
||||||
XCTAssertEqual(server.getText(), "\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}everyone!")
|
XCTAssertEqual(server.getText(), "\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{7f}everyone!")
|
||||||
|
|
||||||
connection.waitForWriteQueue()
|
|
||||||
connection.waitForMain()
|
connection.waitForMain()
|
||||||
XCTAssert(server.getListenState(isListening: false))
|
XCTAssert(server.getListenState(isListening: false))
|
||||||
|
|
||||||
|
@ -336,8 +337,73 @@ class ListenerGSTests: XCTestCase {
|
||||||
XCTAssertEqual(connection.textHeard, "Hello, everyone!")
|
XCTAssertEqual(connection.textHeard, "Hello, everyone!")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Other tests:
|
func testDestructWhileConnected() throws {
|
||||||
// - Connection deconstruction - When I remove the reference to the connection, it stays up. I think self has references because of closures that are still running in the queues.
|
let server = GSServerMock()
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
let connection = GSConnection()
|
||||||
|
connection.setMainQueueForTest()
|
||||||
|
|
||||||
|
XCTAssertEqual(connection.state, .disconnected)
|
||||||
|
XCTAssertNil(connection.errorMessage)
|
||||||
|
XCTAssertEqual(connection.textHeard, "")
|
||||||
|
|
||||||
|
connection.connect(destination: "127.0.0.1")
|
||||||
|
XCTAssertEqual(connection.state, .connecting)
|
||||||
|
XCTAssert(server.accept())
|
||||||
|
|
||||||
|
XCTAssert(server.hasClient())
|
||||||
|
connection.waitForMain()
|
||||||
|
waitForConnection(connection: connection)
|
||||||
|
|
||||||
|
XCTAssertEqual(connection.state, .connected)
|
||||||
|
XCTAssertNil(connection.errorMessage)
|
||||||
|
XCTAssertEqual(connection.textHeard, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssert(server.getDisconnect())
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This test hangs at the getDisconnect() line at the end. Something is holding a connection reference.
|
||||||
|
func testDestructWhileListening() throws {
|
||||||
|
let server = GSServerMock()
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
let connection = GSConnection()
|
||||||
|
connection.setMainQueueForTest()
|
||||||
|
|
||||||
|
XCTAssertEqual(connection.state, .disconnected)
|
||||||
|
XCTAssertNil(connection.errorMessage)
|
||||||
|
XCTAssertEqual(connection.textHeard, "")
|
||||||
|
|
||||||
|
connection.connect(destination: "127.0.0.1")
|
||||||
|
XCTAssertEqual(connection.state, .connecting)
|
||||||
|
XCTAssert(server.accept())
|
||||||
|
|
||||||
|
XCTAssert(server.hasClient())
|
||||||
|
connection.waitForMain()
|
||||||
|
waitForConnection(connection: connection)
|
||||||
|
|
||||||
|
XCTAssertEqual(connection.state, .connected)
|
||||||
|
XCTAssertNil(connection.errorMessage)
|
||||||
|
XCTAssertEqual(connection.textHeard, "")
|
||||||
|
|
||||||
|
let speechForwarder = SpeechForwarderMock()
|
||||||
|
XCTAssert(!speechForwarder.isListening)
|
||||||
|
|
||||||
|
connection.listen(speechForwarder: speechForwarder)
|
||||||
|
XCTAssert(server.getListenState(isListening: true))
|
||||||
|
connection.waitForMain()
|
||||||
|
|
||||||
|
XCTAssert(speechForwarder.isListening)
|
||||||
|
XCTAssertEqual(connection.state, .listening)
|
||||||
|
XCTAssertNil(connection.errorMessage)
|
||||||
|
XCTAssertEqual(connection.textHeard, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssert(server.getDisconnect())
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
func testPerformanceExample() throws {
|
func testPerformanceExample() throws {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user