Subversion Repositories Mobile Apps.GyroMouse

Rev

Rev 1 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

//
//  ClientHandler.swift
//  GyroMouse
//
//  Created by Matteo Riva on 28/08/15.
//  Copyright © 2015 Matteo Riva. All rights reserved.
//

import Foundation
import CocoaAsyncSocket


class ClientHandler: NSObject, NetServiceDelegate, NetServiceBrowserDelegate, GCDAsyncSocketDelegate {
    
    private var socket: GCDAsyncSocket?
    private var serviceBrowser: NetServiceBrowser?
    
    private(set) var services = [NetService]()
    
    deinit {
        socket?.setDelegate(nil, delegateQueue: nil)
        socket = nil
        
        serviceBrowser?.delegate = nil
        serviceBrowser = nil
    }
    
    //MARK: - Publics
    
    func startBrowsing() {
        
        services = []
        
        // Initialize Service Browser
        serviceBrowser = NetServiceBrowser()
        
        // Configure Service Browser
        serviceBrowser!.delegate = self
        serviceBrowser!.searchForServices(ofType: "_gyroserver._tcp.", inDomain:"local.")
    }
    
    func stopBrowsing() {
        serviceBrowser?.stop()
        serviceBrowser?.delegate = nil
        serviceBrowser = nil
        services.removeAll()
    }
    
    func connectToLocalService(_ service: NetService) {
        
        // Resolve Service
        service.delegate = self
        service.resolve(withTimeout: 30)
    }
    
    func sendPacket(_ type: String, keyTapped: String, isButtonDown: Bool, isRightButton: Bool, roll: Double, scrollVelocity: Double, grav: (X: Double, Y: Double, Z: Double), rotat: (X: Double, Z: Double), acc: (X: Double, Y: Double, Z: Double), moveVelocity: Double) {
        let buffer = NSMutableData()
        var int32var: Int32
        var uint32var: UInt32
        var boolvar: Bool
        var doublevar: Double
        int32var = 1; buffer.append(&int32var, length: MemoryLayout<Int32>.size) // protocol version
        if (type == "key") { // key tapped
            if (keyTapped.isEmpty) { return } // consistency check: happens when we hit a backspace
            int32var = 1; buffer.append(&int32var, length: MemoryLayout<Int32>.size) // "key" packet type
            uint32var = keyTapped.unicodeScalars.first!.value; buffer.append(&uint32var, length: MemoryLayout<UInt32>.size)
        }
        else if (type == "backspace") { // backspace tapped
            int32var = 2; buffer.append(&int32var, length: MemoryLayout<Int32>.size) // "backspace" packet type
            // nothing to add
        }
        else if (type == "enter") { // enter tapped
            int32var = 3; buffer.append(&int32var, length: MemoryLayout<Int32>.size) // "enter" packet type
            // nothing to add
        }
        else if (type == "click") { // click
            int32var = 4; buffer.append(&int32var, length: MemoryLayout<Int32>.size) // "click" packet type
            boolvar = isRightButton; buffer.append(&boolvar, length: MemoryLayout<Bool>.size)
            boolvar = isButtonDown;  buffer.append(&boolvar, length: MemoryLayout<Bool>.size)
        }
        else if (type == "scroll") { // page scroll
            int32var = 5; buffer.append(&int32var, length: MemoryLayout<Int32>.size) // "scroll" packet type
            doublevar = roll;           buffer.append(&doublevar, length: MemoryLayout<Double>.size)
            doublevar = scrollVelocity; buffer.append(&doublevar, length: MemoryLayout<Double>.size)
        }
        else if (type == "move") { // movement
            int32var = 6; buffer.append(&int32var, length: MemoryLayout<Int32>.size) // "move" packet type
            doublevar = grav.X;       buffer.append(&doublevar, length: MemoryLayout<Double>.size)
            doublevar = grav.Y;       buffer.append(&doublevar, length: MemoryLayout<Double>.size)
            doublevar = grav.Z;       buffer.append(&doublevar, length: MemoryLayout<Double>.size)
            doublevar = rotat.X;      buffer.append(&doublevar, length: MemoryLayout<Double>.size)
            doublevar = rotat.Z;      buffer.append(&doublevar, length: MemoryLayout<Double>.size)
            doublevar = acc.X;        buffer.append(&doublevar, length: MemoryLayout<Double>.size)
            doublevar = acc.Y;        buffer.append(&doublevar, length: MemoryLayout<Double>.size)
            doublevar = acc.Z;        buffer.append(&doublevar, length: MemoryLayout<Double>.size)
            doublevar = moveVelocity; buffer.append(&doublevar, length: MemoryLayout<Double>.size)
        }
        else if (type == "reset") { // reset pointer
            int32var = 7; buffer.append(&int32var, length: MemoryLayout<Int32>.size) // "click" packet type
            // nothing to add
        }
        
        // Write Buffer
        let bufferData = buffer as Data
        print ("Sending \(bufferData.count) bytes packet:", terminator: "")
        for i in 0...(bufferData.count - 1) {
            let byte: UInt8 = bufferData.withUnsafeBytes { rawBuffer in rawBuffer.load(fromByteOffset: i * MemoryLayout<UInt8>.size, as: UInt8.self) }
            print (" " + String(format: "%02X", byte), terminator: "")
        }
        print ("")
        socket?.write(bufferData, withTimeout: -1, tag: 0)
    }
    
    func endConnection() {
        socket?.disconnect()
        socket?.setDelegate(nil, delegateQueue: nil)
        socket = nil
    }
    
    //MARK: - NSNetServiceBrowserDelegate
    
    func netServiceBrowser(_ browser: NetServiceBrowser, didFind service: NetService, moreComing: Bool) {
        // Update Services
        services.append(service)
        
        if !moreComing {
            NotificationCenter.default.post(Notification(name: GyroMouseShouldRefreshServerListNotification, object: self, userInfo: nil))
        }
    }
    
    func netServiceBrowser(_ browser: NetServiceBrowser, didRemove service: NetService, moreComing: Bool) {
        // Update Services
        services.remove(at: services.firstIndex(of: service)!)
        
        if !moreComing {
            NotificationCenter.default.post(Notification(name: GyroMouseShouldRefreshServerListNotification, object: self, userInfo: nil))
        }
    }
    
    func netServiceBrowserDidStopSearch(_ browser: NetServiceBrowser) {
        stopBrowsing()
    }
    
    func netServiceBrowser(_ browser: NetServiceBrowser, didNotSearch errorDict: [String : NSNumber]) {
        stopBrowsing()
    }
    
    //MARK: - NSNetServiceDelegate
    
    func netService(_ sender: NetService, didNotResolve errorDict: [String : NSNumber]) {
        sender.delegate = nil
    }
    
    func netServiceDidResolveAddress(_ sender: NetService) {
        // Connect With Service
        var isConnected = false
        
        if ((socket == nil) || (socket?.isDisconnected ?? false)) {
            // Initialize Socket
            socket = GCDAsyncSocket(delegate: self, delegateQueue: DispatchQueue.main)
            
            // Connect
            while (!isConnected && (sender.addresses!.count != 0)) {
                do {
                    try socket!.connect(toAddress: sender.addresses!.first!)
                    isConnected = true
                } catch {
                    isConnected = false
                    print("Unable to connect to address. Error \(error) with user info \(error.localizedDescription).", terminator: "\n")
                }
            }
        } else {
            isConnected = socket?.isConnected ?? false
        }
  
        if (isConnected) {
            print("Did Connect with Service: domain(\(sender.domain)) type(\(sender.type)) name(\(sender.name)) port(\(sender.port))", terminator: "\n")
        } else {
            print("Unable to Connect with Service: domain(\(sender.domain)) type(\(sender.type)) name(\(sender.name)) port(\(sender.port))", terminator: "\n")
            NotificationCenter.default.post(Notification(name: GyroMouseConnectionFailedNotification, object: self, userInfo: nil))
        }
    }
    
    //MARK: - GCDAsyncSocketDelegate
    
    func socket(_ sock: GCDAsyncSocket, didConnectToHost host: String, port: UInt16) {
        print("Socket Did Connect to Host: \(host) Port: \(port)", terminator: "\n")
        
        stopBrowsing()
        NotificationCenter.default.post(Notification(name: GyroMouseConnectionSuccessNotification, object: self, userInfo: nil))
    }
    
    func socketDidDisconnect(_ sock: GCDAsyncSocket, withError err: Error?) {
        socket?.delegate = nil
        socket = nil
        
        startBrowsing()
        NotificationCenter.default.post(Notification(name: GyroMouseDisconnectedNotification, object: self, userInfo: nil))
        
        if err != nil {
            print("Socket Did Disconnect with Error \(err!) with user info \(err!.localizedDescription).", terminator: "\n")
            //NotificationCenter.default.post(Notification(name: GyroMouseConnectionFailedNotification, object: self, userInfo: nil))
        } else {
            print("Socket Did Disconnect", terminator: "\n")
        }
    }

}