无法在swift 3

时间:2017-12-04 20:22:33

标签: ios swift core-bluetooth

我有这段代码:

//
//  Measurement.swift
//  BicycleSpeed

    import Foundation
    import CoreBluetooth

    // CSC Measurement
    // https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.csc_measurement.xml
    //
    //  Flags : 1 byte.  Bit 0: Wheel. Bit 1: Crank
    //  Cumulative Wheel revolutions: 4 bytes uint32
    //  Last wheel event time: 2 bytes. uint16 (1/1024s)
    //  Cumulative Crank revolutions: 2 bytes uint16
    //  Last cranck event time: 2 bytes. uint16 (1/1024s)


    struct Measurement : CustomDebugStringConvertible {

      let hasWheel:Bool
      let hasCrank:Bool
      let cumulativeWheel:UInt32
      let lastWheelEventTime:TimeInterval
      let cumulativeCrank:UInt16
      let lastCrankEventTime:TimeInterval
      let wheelSize:UInt32


      init(data:Data, wheelSize:UInt32) {

        self.wheelSize = wheelSize
        // Flags
        var flags:UInt8=0
        (data as NSData).getBytes(&flags, range: NSRange(location: 0, length: 1))

        hasWheel = ((flags & BTConstants.WheelFlagMask) > 0)
        hasCrank = ((flags & BTConstants.CrankFlagMask) > 0)

        var wheel:UInt32=0
        var wheelTime:UInt16=0
        var crank:UInt16=0
        var crankTime:UInt16=0

        var currentOffset = 1
        var length = 0

        if ( hasWheel ) {

          length = MemoryLayout<UInt32>.size
          (data as NSData).getBytes(&wheel, range: NSRange(location: currentOffset, length: length))
          currentOffset += length

          length = MemoryLayout<UInt16>.size
          (data as NSData).getBytes(&wheelTime, range: NSRange(location: currentOffset, length: length))
          currentOffset += length
        }

        if ( hasCrank ) {

          length = MemoryLayout<UInt16>.size
          (data as NSData).getBytes(&crank, range: NSRange(location: currentOffset, length: length))
          currentOffset += length

          length = MemoryLayout<UInt16>.size
          (data as NSData).getBytes(&crankTime, range: NSRange(location: currentOffset, length: length))
          currentOffset += length
        }

        cumulativeWheel     = CFSwapInt32LittleToHost(wheel)
        lastWheelEventTime  = TimeInterval( Double(CFSwapInt16LittleToHost(wheelTime))/BTConstants.TimeScale)
        cumulativeCrank     = CFSwapInt16LittleToHost(crank)
        lastCrankEventTime  = TimeInterval( Double(CFSwapInt16LittleToHost(crankTime))/BTConstants.TimeScale)

      }

      func timeIntervalForCurrentSample( _ current:TimeInterval, previous:TimeInterval ) -> TimeInterval {
        var timeDiff:TimeInterval = 0
        if( current >= previous ) {
            timeDiff = current - previous
        }
        else {
          // passed the maximum value
          timeDiff =  ( TimeInterval((Double( UINT16_MAX) / BTConstants.TimeScale)) - previous) + current
        }
        return timeDiff

      }

      func valueDiffForCurrentSample<T:UnsignedInteger>( _ current:T, previous:T , max:T) -> T {

        var diff:T = 0
        if  ( current >= previous ) {
          diff = current - previous
        }
        else {
           diff = ( max - previous ) + current
        }
        return diff
      }


      func valuesForPreviousMeasurement( _ previousSample:Measurement? ) -> ( cadenceinRPM:Double?, distanceinMeters:Double?, speedInMetersPerSecond:Double?)? {


        var distance:Double?, cadence:Double?, speed:Double?
        guard let previousSample = previousSample else {
          return nil
        }
        if ( hasWheel && previousSample.hasWheel ) {
          let wheelTimeDiff = timeIntervalForCurrentSample(lastWheelEventTime, previous: previousSample.lastWheelEventTime)
          let valueDiff = valueDiffForCurrentSample(cumulativeWheel, previous: previousSample.cumulativeWheel, max: UInt32.max)

          distance = Double( valueDiff * wheelSize) / 1000.0 // distance in meters
          if  distance != nil  &&  wheelTimeDiff > 0 {
            speed = (wheelTimeDiff == 0 ) ? 0 : distance! / wheelTimeDiff // m/s
          }
        }

        if( hasCrank && previousSample.hasCrank ) {
          let crankDiffTime = timeIntervalForCurrentSample(lastCrankEventTime, previous: previousSample.lastCrankEventTime)
          let valueDiff = Double(valueDiffForCurrentSample(cumulativeCrank, previous: previousSample.cumulativeCrank, max: UInt16.max))

          cadence = (crankDiffTime == 0) ? 0 : Double(60.0 * valueDiff / crankDiffTime) // RPM

        }
        print( "Cadence: \(String(describing: cadence)) RPM. Distance: \(String(describing: distance)) meters. Speed: \(String(describing: speed)) Km/h" )
        return ( cadenceinRPM:cadence, distanceinMeters:distance, speedInMetersPerSecond:speed)



      }

      var debugDescription:String {
        get {
            return "Wheel Revs: \(cumulativeWheel). Last wheel event time: \(lastWheelEventTime). Crank Revs: \(cumulativeCrank). Last Crank event time: \(lastCrankEventTime)"
        }
      }

        var myMeasurement = ((Measurement?) -> (cadenceinRPM: Double?, DistanceinMeters: Double?, speedinMetersPerSecond: Double?)).self


        struct dataVariables {

            static var mySpeed = myMeasurement.speedInMetersPerSecond


            static var myCadence : Double?
            static var miDistance : Double?

            static var myLastWheelEventTime : Double? = Measurement.valuesForPreviousMeasurement(lastWheelEventTime)

            static var myLastCrankEventTime : Double? = Measurement.valuesForPreviousMeasurement(lastCrankEventTime)


        }
    }


    }

我试图在struct dataVariables中分配静态变量返回值cadenceinRPM,distanceinMeters,speedinMetersPerSecond
以及lastWheelEventTime和lastCrankEventTime,所以我可以在另一个类中访问它们,但是我遇到以下错误: on var mySpeed:Instance member'myMeasurement'不能用于'Measurement'类型 on var myLastWheelEventTime和var myLastCrankEventTime:实例成员'valuesForPreviousMeasurement'不能用于'Measurement'类型;你的意思是使用这种类型的值吗? 如何引用那些返回值比? 有人可以解释这个错误吗?我搜索了其他类似的问题,但我还没有找到解决方案 我试图将var myVariable更改为

var myMeasurement = Measurement.self

但错误保持不变。

和其他代码

    //
//  CadenceSensor.swift

import Foundation
import CoreBluetooth

/*
 // Bluetooth  "Cycling Speed and Cadence"
 https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.cycling_speed_and_cadence.xml

 Service Cycling Speed and Cadence. Characteristic [2A5B]  // Measurement
 Service Cycling Speed and Cadence. Characteristic [2A5C]  // Supported Features
 Service Cycling Speed and Cadence. Characteristic [2A5D]  // Sensor location
 Service Cycling Speed and Cadence. Characteristic [2A55]  // Control Point

 */

public struct BTConstants {
    static let CadenceService         = "1816"
    static let CSCMeasurementUUID     = "2a5b"
    static let CSCFeatureUUID         = "2a5c"
    static let SensorLocationUUID     = "2a5d"
    static let ControlPointUUID       = "2a55"
    static let WheelFlagMask:UInt8    = 0b01
    static let CrankFlagMask:UInt8    = 0b10
    static let DefaultWheelSize:UInt32   = UInt32(myVariables.circonferenzaRuota!)  // In millimiters. 700x30 (by default my bike's wheels) :)
    static let TimeScale              = 1024.0
}

protocol CadenceSensorDelegate {

    func errorDiscoveringSensorInformation(_ error:NSError)
    func sensorReady()
    func sensorUpdatedValues( speedInMetersPerSecond speed:Double?, cadenceInRpm cadence:Double?, distanceInMeters distance:Double? )
}

class CadenceSensor: NSObject {

    let peripheral:CBPeripheral
    var sensorDelegate:CadenceSensorDelegate?
    var measurementCharasteristic:CBCharacteristic?
    var lastMeasurement:Measurement?
    let wheelCircunference:UInt32


    init(peripheral:CBPeripheral , wheel:UInt32=BTConstants.DefaultWheelSize) {
        self.peripheral = peripheral
        wheelCircunference = wheel
    }

    func start() {
        self.peripheral.discoverServices(nil)
        self.peripheral.delegate = self
    }


    func stop() {
        if let measurementCharasteristic = measurementCharasteristic {
            peripheral.setNotifyValue(false, for: measurementCharasteristic)
        }

    }

    func handleValueData( _ data:Data ) {

        let measurement = Measurement(data: data, wheelSize: wheelCircunference)
        print("\(measurement)")

        let values = measurement.valuesForPreviousMeasurement(lastMeasurement)
        lastMeasurement = measurement

        sensorDelegate?.sensorUpdatedValues(speedInMetersPerSecond: values?.speedInMetersPerSecond, cadenceInRpm: values?.cadenceinRPM, distanceInMeters: values?.distanceinMeters)
    }
}



extension CadenceSensor : CBPeripheralDelegate {


    func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
        guard error == nil else {
            sensorDelegate?.errorDiscoveringSensorInformation(NSError(domain: CBErrorDomain, code: 0, userInfo: [NSLocalizedDescriptionKey:NSLocalizedString("Error receiving measurements updates", comment:"")]))

            return
        }
        print("notification status changed for [\(characteristic.uuid)]...")
    }

    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {

        print("Updated [\(characteristic.uuid)]...")

        guard error == nil  , let data = characteristic.value  else {

            return
        }

        handleValueData(data)

    }

    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        guard error == nil  else {
            sensorDelegate?.errorDiscoveringSensorInformation(error! as NSError)
            return
        }
        // Find the cadence service
        guard let cadenceService =  peripheral.services?.filter({ (service) -> Bool in
            return service.uuid == CBUUID(string: BTConstants.CadenceService)
        }).first else {

            sensorDelegate?.errorDiscoveringSensorInformation(NSError(domain: CBErrorDomain, code: NSNotFound, userInfo: [NSLocalizedDescriptionKey:NSLocalizedString("Cadence service not found for this peripheral", comment:"")]))
            return
        }
        // Discover the cadence service characteristics
        peripheral.discoverCharacteristics(nil, for:cadenceService )
        print("Cadence service discovered")

    }

    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {

        guard let characteristics = service.characteristics else {
            sensorDelegate?.errorDiscoveringSensorInformation(NSError(domain: CBErrorDomain, code: NSNotFound, userInfo: [NSLocalizedDescriptionKey:NSLocalizedString("No characteristics found for the cadence service", comment:"")]))
            return

        }

        print("Received characteristics");

        // Enable notifications for the measurement characteristic
        for characteristic in characteristics {

            print("Service \(service.uuid). Characteristic [\(characteristic.uuid)]")

            if characteristic.uuid == CBUUID(string: BTConstants.CSCMeasurementUUID) {

                print("Found measurement characteristic. Subscribing...")
                peripheral.setNotifyValue(true, for: characteristic)
                measurementCharasteristic  = characteristic

            }
        }
        sensorDelegate?.sensorReady()

    }

}

和其他代码

    //
//  MainViewController.swift
//  BicycleSpeed


import UIKit
import CoreBluetooth

class MainViewController: UIViewController {


  struct Constants {

    static let ScanSegue = "ScanSegue"
    static let SensorUserDefaultsKey = "lastsensorused"
  }

  var bluetoothManager:BluetoothManager!
  var sensor:CadenceSensor?
  weak var scanViewController:ScanViewController?
  var infoViewController:InfoTableViewController?
  var accumulatedDistance:Double?

  lazy var distanceFormatter:LengthFormatter = {

    let formatter = LengthFormatter()
    formatter.numberFormatter.maximumFractionDigits = 1

    return formatter
  }()

  //@IBOutlet var labelBTStatus:UILabel!
  @IBOutlet var scanItem:UIBarButtonItem!
  @IBOutlet weak var idLabel: UILabel!


  override func viewDidLoad() {

      bluetoothManager = BluetoothManager()
      bluetoothManager.bluetoothDelegate = self
      scanItem.isEnabled = false

  }

  deinit {
    disconnectSensor()
  }

  @IBAction func unwindSegue( _ segue:UIStoryboardSegue ) {
      bluetoothManager.stopScan()
    guard let sensor = (segue as? ScanUnwindSegue)?.sensor else {
      return
    }
    print("Need to connect to sensor \(sensor.peripheral.identifier)")
    connectToSensor(sensor)

  }

  func disconnectSensor( ) {
    if sensor != nil  {
      bluetoothManager.disconnectSensor(sensor!)
      sensor = nil
    }
    accumulatedDistance = nil
  }

  func connectToSensor(_ sensor:CadenceSensor) {

    self.sensor  = sensor
    bluetoothManager.connectToSensor(sensor)
    // Save the sensor ID
    UserDefaults.standard.set(sensor.peripheral.identifier.uuidString, forKey: Constants.SensorUserDefaultsKey)
    UserDefaults.standard.synchronize()

  }
  // TODO: REconnect. Try this every X seconds
  func checkPreviousSensor() {

    guard let sensorID = UserDefaults.standard.object(forKey: Constants.SensorUserDefaultsKey)  as? String else {
      return
    }
    guard let sensor = bluetoothManager.retrieveSensorWithIdentifier(sensorID) else {
      return
    }
    self.sensor = sensor
    connectToSensor(sensor)

  }

  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let infoVC = segue.destination as? InfoTableViewController {
        infoViewController = infoVC
    }
    if segue.identifier == Constants.ScanSegue {

        // Scan segue
      bluetoothManager.startScan()
      scanViewController  = (segue.destination as? UINavigationController)?.viewControllers.first as? ScanViewController
    }

  }

}

extension MainViewController : CadenceSensorDelegate {

  func errorDiscoveringSensorInformation(_ error: NSError) {
      print("An error ocurred disconvering the sensor services/characteristics: \(error)")
  }

  func sensorReady() {
    print("Sensor ready to go...")
    accumulatedDistance = 0.0
  }

  func updateSensorInfo() {
    let name = sensor?.peripheral.name ?? ""
    let uuid = sensor?.peripheral.identifier.uuidString ?? ""

    OperationQueue.main.addOperation { () -> Void in
      self.infoViewController?.showDeviceName(name , uuid:uuid )
    }
  }


  func sensorUpdatedValues( speedInMetersPerSecond speed:Double?, cadenceInRpm cadence:Double?, distanceInMeters distance:Double? ) {

    accumulatedDistance? += distance ?? 0
    let distanceText = (accumulatedDistance != nil && accumulatedDistance! >= 1.0) ? distanceFormatter.string(fromMeters: accumulatedDistance!) : "N/A"
    let speedText = (speed != nil) ? distanceFormatter.string(fromValue: speed!*3.6, unit: .kilometer) + NSLocalizedString("/h", comment:"(km) Per hour") : "N/A"
    let cadenceText = (cadence != nil) ? String(format: "%.2f %@",  cadence!, NSLocalizedString("RPM", comment:"Revs per minute") ) : "N/A"

    OperationQueue.main.addOperation { () -> Void in

      self.infoViewController?.showMeasurementWithSpeed(speedText , cadence: cadenceText, distance: distanceText )
    }
  }


}

extension MainViewController : BluetoothManagerDelegate {

  func stateChanged(_ state: CBCentralManagerState) {
    print("State Changed: \(state)")
    var enabled = false
    var title = ""
    switch state {
    case .poweredOn:
        title = "Bluetooth ON"
        enabled = true
        // When the bluetooth changes to ON, try to reconnect to the previous sensor
        checkPreviousSensor()

    case .resetting:
        title = "Reseeting"
    case .poweredOff:
      title = "Bluetooth Off"
    case .unauthorized:
      title = "Bluetooth not authorized"
    case .unknown:
      title = "Unknown"
    case .unsupported:
      title = "Bluetooth not supported"
    }
    infoViewController?.showBluetoothStatusText( title )
    scanItem.isEnabled = enabled
  }


  func sensorConnection( _ sensor:CadenceSensor, error:NSError?) {
      print("")
    guard error == nil else {
      self.sensor = nil
      print("Error connecting to sensor: \(sensor.peripheral.identifier)")
      updateSensorInfo()
      accumulatedDistance = nil
      return
    }
    self.sensor = sensor
    self.sensor?.sensorDelegate = self
    print("Sensor connected. \(String(describing: sensor.peripheral.name)). [\(sensor.peripheral.identifier)]")
    updateSensorInfo()

    sensor.start()
  }


  func sensorDisconnected( _ sensor:CadenceSensor, error:NSError?) {
    print("Sensor disconnected")
    self.sensor = nil
  }

  func sensorDiscovered( _ sensor:CadenceSensor ) {
      scanViewController?.addSensor(sensor)
  }



}

在MainViewController.swift中,有这个func sensorUpdatedValues将我想要的三个值转换为字符串并初始化InfoTableViewController.swift中的func showMeasurementWithSpeed。

我可以在函数sensorUpdateValues中返回三个值,而不是将它们存储到新变量中吗?

3 个答案:

答案 0 :(得分:1)

在更好的方式来解决,这是通过将传感器对象到InfoTableViewController在制备(forSegue :),然后内部InfoTableViewControlleryou可以调用sensor.lastMeasurement.speedInMetersPerSecond或任何其他变种是在里面。由于类是通过引用传递的,因此即使转换到新的ViewController,它也会保留数据。

  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let infoVC = segue.destination as? InfoTableViewController {
    infoVC.sensor = self.sensor
}
if segue.identifier == Constants.ScanSegue {

    // Scan segue
  bluetoothManager.startScan()
  scanViewController  = (segue.destination as? UINavigationController)?.viewControllers.first as? ScanViewController
}

} 那么当然你可以用新VC中的这些数据做任何你想做的事情(将值分配给标签或诸如此类别)

答案 1 :(得分:0)

我相信它是因为你将func声明为实例级函数而不是类/结构级函数。你应该能够简单地添加&#34;静态&#34;关键字使您可以在示例代码中使用它。 ie&#34;静态函数值ForPreviousMeasurement ...&#34;

**更新 - 添加了一个简单的示例来显示类和实例函数之间的区别。

// This is an instance function being used.  It's called such because
// you need an actual object instance in order to call the func.
var myCar: Car = Car()
myCar.startEngine()

// This is a class level function being used.  It's called such because
// you don't actually need an object instance: It's simply part of the class.
Car.PriceForModel("HondaCivic")

答案 2 :(得分:0)

终于解决了..它比想象的容易...因为我理解了我感兴趣的三个值,传递了一些我想要摆脱的infoTableViewController。它们被传递,已经使用MainView控制器内的函数sensorUpdatedValues转换为String

  func sensorUpdatedValues( speedInMetersPerSecond speed:Double?, cadenceInRpm cadence:Double?, distanceInMeters distance:Double? ) {

    accumulatedDistance? += distance ?? 0
    let distanceText = (accumulatedDistance != nil && accumulatedDistance! >= 1.0) ? distanceFormatter.string(fromMeters: accumulatedDistance!) : "N/A"
    let speedText = (speed != nil) ? distanceFormatter.string(fromValue: speed!*3.6, unit: .kilometer) + NSLocalizedString("/h", comment:"(km) Per hour") : "N/A"
    let cadenceText = (cadence != nil) ? String(format: "%.2f %@",  cadence!, NSLocalizedString("RPM", comment:"Revs per minute") ) : "N/A"



    OperationQueue.main.addOperation { () -> Void in

      self.infoViewController?.showMeasurementWithSpeed(speedText , cadence: cadenceText, distance: distanceText )
    }
  }

所以我追溯了我的InfospeedoViewController中的函数(因为xCode要求它,因为它存在于InfoTableViewController中并重新路由到InfoSpeedoViewController使其成为必要)并且在该函数体内我将值连接到标签。

 func showMeasurementWithSpeed( _ speed:String, cadence:String, distance:String  ) {

          speedDisplayLabel.text = speed
          cadenceDisplayLabel.text = cadence
          distanceDisplayLabel.text = distance


//        showDetailText(speed, atSection: Constants.MeasurementsSection, row:Constants.SpeedRow)
//        showDetailText(cadence, atSection: Constants.MeasurementsSection, row:Constants.CadenceRow)
//        showDetailText(distance, atSection: Constants.MeasurementsSection, row:Constants.DistanceRow)

    }

注释掉的部分是用于填充单元格的旧InfoTableViewController指示。 非常感谢您的帮助。我学到了一些东西并巩固了其他东西。我想我们努力在错误的地方捕捉这些值,但我想这只是因为我打印了所有项目的文件,我可以更容易地跟踪数据流。不完全理解代码,因为我对swift的了解不足使得它更难以理解,但我有这种感觉,这种解决方案是逻辑的,因为通常简单的方法是最好的方式,通常只能看不到它。 再次感谢 我还有两个我想要的价值观。一个新的冒险开始..我应该发布另一个问题还是继续这个?