// // MapViewController.swift // LaserMethane // // Created by 203 on 2021/8/6. // import CoreBluetooth import KeychainAccess import SnapKit import UIKit let SCREEN_WIDTH = UIScreen.main.bounds.size.width let SCREEN_HEIGHT = UIScreen.main.bounds.size.height let BUTTON_WIDTH = CGFloat(36) let BUTTON_HEIGHT = CGFloat(36) let ASK_DEV_CODE_COMMAND: [UInt8] = [0x01, 0x0D, 0x0A] // 查询设备编号指令 let OPEN_TRANSFER_COMMAND: [UInt8] = [0x02, 0x0D, 0x0A] // 开启数据发送命令 let viewWidth = (SCREEN_WIDTH - 40) / 3 let viewHeight = (SCREEN_WIDTH - 40) / 3 class MapViewController: UIViewController, MAMapViewDelegate { private let manager = CLLocationManager() private var locationManager = AMapLocationManager() private var mapView: MAMapView! private var topTagView: UIView! @IBOutlet var deviceStatusLabel: UILabel! @IBOutlet var inspectNameLabel: UILabel! @IBOutlet var inspectTimeLabel: UILabel! @IBOutlet var stopButton: UIButton! @IBOutlet var refreshButton: UIButton! @IBOutlet var bluetoothButton: UIButton! @IBOutlet var expandButton: UIButton! @IBOutlet var minusButton: UIButton! @IBOutlet var addEventButton: UIButton! @IBOutlet var currentView: UIView! @IBOutlet var settingView: UIView! @IBOutlet var maxView: UIView! @IBOutlet var currentValueLabel: UILabel! @IBOutlet var settingValueLabel: UILabel! @IBOutlet var maxValueLabel: UILabel! private var valueDelegate: TransferValueDelegate! private var keychain: Keychain! private var centralManager: CBCentralManager! // 扫描到的设备的集合 private var scanDevices = [CBPeripheral]() private var writeCharacteristic: CBCharacteristic! private var deviceName: String! private var cbPeripheral: CBPeripheral! override func viewDidLoad() { super.viewDidLoad() // 设置导航栏背景和标题 title = "巡检" navigationController?.navigationBar.isTranslucent = false navigationController?.navigationBar.barTintColor = .systemBlue let dict: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: UIColor.white] navigationController?.navigationBar.titleTextAttributes = dict navigationController?.navigationBar.tintColor = .white // keychain = Keychain() // 蓝牙相关 centralManager = CBCentralManager() centralManager.delegate = self // 设置地图 setUpMap() // 在地图图层上面View topTagView = UIView(frame: CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: 55)) topTagView.backgroundColor = UIColor(red: 214 / 255, green: 238 / 255, blue: 254 / 255, alpha: 1.0) addTopTagSubview() addLeftSubview() addBottomSubview() view.addSubview(topTagView) } func setUpMap() { mapView = MAMapView(frame: view.bounds) // 开启缩放手势 mapView.isZoomEnabled = true // 地图的缩放级别的范围是[3-19] mapView.setZoomLevel(15, animated: true) // 禁用旋转手势 mapView.isRotateEnabled = false // 禁用倾斜手势 mapView.isRotateCameraEnabled = false mapView.isRotateEnabled = false // 显示定位蓝点 mapView.showsUserLocation = true mapView.userTrackingMode = .follow // 开启定位 locationManager.delegate = self // 设置定位最小更新距离 locationManager.distanceFilter = 5 // 开启持续定位 manager.requestWhenInUseAuthorization() manager.requestAlwaysAuthorization() locationManager.startUpdatingLocation() // 不显示罗盘 mapView.showsCompass = false mapView.delegate = self view.addSubview(mapView) // 放大 expandButton.layer.cornerRadius = 5 view.addSubview(expandButton) expandButton.snp.makeConstraints { (make) -> Void in make.size.equalTo(CGSize(width: BUTTON_WIDTH, height: BUTTON_HEIGHT)) make.right.equalTo(-5) make.top.equalTo((VIEW_HEIGHT - BUTTON_HEIGHT) / 3) } // 缩小 minusButton.layer.cornerRadius = 5 view.addSubview(minusButton) minusButton.snp.makeConstraints { (make) -> Void in make.size.equalTo(CGSize(width: BUTTON_WIDTH, height: BUTTON_HEIGHT)) make.right.equalTo(-5) make.top.equalTo((VIEW_HEIGHT - BUTTON_HEIGHT) / 3 + BUTTON_HEIGHT + 2) } } func addTopTagSubview() { topTagView.addSubview(deviceStatusLabel) deviceStatusLabel.snp.makeConstraints { (make) -> Void in make.size.equalTo(CGSize(width: topTagView.frame.width, height: 20)) make.top.equalTo(5) make.centerX.equalToSuperview() } topTagView.addSubview(inspectNameLabel) inspectNameLabel.snp.makeConstraints { (make) -> Void in make.size.equalTo(CGSize(width: topTagView.frame.width / 2, height: 20)) make.top.equalTo(30) make.left.equalTo(5) } topTagView.addSubview(inspectTimeLabel) inspectTimeLabel.snp.makeConstraints { (make) -> Void in make.size.equalTo(CGSize(width: topTagView.frame.width / 2, height: 20)) make.top.equalTo(30) make.right.equalTo(5) } } func addLeftSubview() { stopButton.layer.cornerRadius = 18 view.addSubview(stopButton) stopButton.snp.makeConstraints { (make) -> Void in // 设置视图大小 make.size.equalTo(CGSize(width: BUTTON_WIDTH, height: BUTTON_HEIGHT)) make.left.equalTo(10) make.top.equalTo(topTagView.frame.height + 10) } bluetoothButton.layer.cornerRadius = 18 view.addSubview(bluetoothButton) bluetoothButton.snp.makeConstraints { (make) -> Void in // 设置视图大小 make.size.equalTo(CGSize(width: BUTTON_WIDTH, height: BUTTON_HEIGHT)) make.left.equalTo(10) make.top.equalTo(topTagView.frame.height + 20 + BUTTON_HEIGHT) } refreshButton.layer.cornerRadius = 18 view.addSubview(refreshButton) refreshButton.snp.makeConstraints { (make) -> Void in // 设置视图大小 make.size.equalTo(CGSize(width: BUTTON_WIDTH, height: BUTTON_HEIGHT)) make.left.equalTo(10) make.top.equalTo(topTagView.frame.height + 30 + BUTTON_HEIGHT * 2) } } // 停止巡检 @IBAction func stopInspectAction(_ sender: Any) { locationManager.stopUpdatingLocation() if cbPeripheral == nil { return } centralManager.cancelPeripheralConnection(cbPeripheral) } // 搜索蓝牙 @IBAction func searchBluetoothAction(_ sender: Any) { if isBluetoothAvailable() { LoadingHub.shared.showLoading(text: "设备搜索中...") centralManager.scanForPeripherals(withServices: nil, options: nil) DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: { // 列表显示扫描到的设备 self.showTableView() }) } else { AlertHub.shared.showWaringAlert(controller: self, messge: "蓝牙不可用,请检查") } } func showTableView() { centralManager.stopScan() LoadingHub.shared.hideLoading() let actionSheetController = UIAlertController() for device in scanDevices { actionSheetController.addAction(UIAlertAction(title: device.name, style: .default, handler: { alertAction in // 连接点击的设备 LoadingHub.shared.showLoading(text: "设备连接中...") self.scanDevices.forEach { it in if alertAction.title == it.name { self.centralManager.connect(it, options: nil) } } })) } actionSheetController.addAction(UIAlertAction(title: "取消", style: .cancel)) present(actionSheetController, animated: true, completion: nil) } // 重新发送指令 @IBAction func refreshCmdAction(_ sender: Any) { if isBluetoothAvailable() { } else { AlertHub.shared.showWaringAlert(controller: self, messge: "请先连接设备") } } func addBottomSubview() { LayerShadowHub.shared.setShadow(view: currentView, sColor: UIColor(red: 211 / 255, green: 211 / 255, blue: 211 / 255, alpha: 1.0), offset: CGSize(width: 0, height: 0), alpha: 1.0, radius: CGFloat(5.0)) view.addSubview(currentView) currentView.snp.makeConstraints { (make) -> Void in make.size.equalTo(CGSize(width: viewWidth, height: viewHeight)) make.bottom.equalTo(-(SCREEN_HEIGHT * 0.1 + 10)) make.left.equalTo(10) } LayerShadowHub.shared.setShadow(view: settingView, sColor: UIColor(red: 211 / 255, green: 211 / 255, blue: 211 / 255, alpha: 1.0), offset: CGSize(width: 0, height: 0), alpha: 1.0, radius: CGFloat(5.0)) view.addSubview(settingView) settingView.snp.makeConstraints { (make) -> Void in make.size.equalTo(CGSize(width: viewWidth, height: viewHeight)) make.bottom.equalTo(-(SCREEN_HEIGHT * 0.1 + 10)) make.centerX.equalToSuperview() } LayerShadowHub.shared.setShadow(view: maxView, sColor: UIColor(red: 211 / 255, green: 211 / 255, blue: 211 / 255, alpha: 1.0), offset: CGSize(width: 0, height: 0), alpha: 1.0, radius: CGFloat(5.0)) view.addSubview(maxView) maxView.snp.makeConstraints { (make) -> Void in make.size.equalTo(CGSize(width: viewWidth, height: viewHeight)) make.bottom.equalTo(-(SCREEN_HEIGHT * 0.1 + 10)) make.right.equalTo(-10) } // 添加按钮 let buttonWidth = SCREEN_WIDTH * 0.8 addEventButton.layer.cornerRadius = 8 view.addSubview(addEventButton) addEventButton.snp.makeConstraints { (make) -> Void in make.top.equalTo(SCREEN_HEIGHT * 0.8) make.size.equalTo(CGSize(width: buttonWidth, height: 36)) make.centerX.equalToSuperview() } } @IBAction func addEventAction(_ sender: Any) { let lng = keychain[Constant.CurrentLongitude.rawValue]! as String let lat = keychain[Constant.CurrentLatitude.rawValue]! as String print("位置信息: [\(lng), \(lat)]") let newEventViewController = NewEventViewController(nibName: "NewEventViewController", bundle: nil) // 委托代理 valueDelegate = newEventViewController let selectController = UIAlertController(title: "选择事件类型", message: nil, preferredStyle: .alert) // 获取当前时间戳 let timeInterval: TimeInterval = Date().timeIntervalSince1970 let timeStamp = Int(timeInterval) let inspectButton = UIAlertAction(title: "报警事件", style: .default, handler: { _ in // 实现代理的方法,传值 self.valueDelegate.transfer(controller: self, dic: ["isWarning": true, "inspectionId": timeStamp.id(), "inspectionName": (self.inspectNameLabel?.text)!, "longitude": Double(lng)!, "latitude": Double(lat)!]) self.navigationController?.pushViewController(newEventViewController, animated: true) }) let eventLogButton = UIAlertAction(title: "自定义事件", style: .default, handler: { _ in // 实现代理的方法,传值 self.valueDelegate.transfer(controller: self, dic: ["isWarning": false, "inspectionId": timeStamp.id(), "inspectionName": (self.inspectNameLabel?.text)!, "longitude": Double(lng)!, "latitude": Double(lat)!]) self.navigationController?.pushViewController(newEventViewController, animated: true) }) selectController.addAction(inspectButton) selectController.addAction(eventLogButton) present(selectController, animated: true, completion: nil) } @IBAction func expandMap(_ sender: Any) { var zoomLevel = mapView.zoomLevel zoomLevel += 1 mapView.setZoomLevel(zoomLevel, animated: true) } @IBAction func minusMap(_ sender: Any) { var zoomLevel = mapView.zoomLevel zoomLevel -= 1 mapView.setZoomLevel(zoomLevel, animated: true) } } extension MapViewController: AMapLocationManagerDelegate { // 持续定位位置信息 func amapLocationManager(_ manager: AMapLocationManager!, didUpdate location: CLLocation!) { print("位置信息: {lat:\(location.coordinate.latitude); lon:\(location.coordinate.longitude)};") keychain[Constant.CurrentLongitude.rawValue] = location.coordinate.longitude.description keychain[Constant.CurrentLatitude.rawValue] = location.coordinate.latitude.description } } extension MapViewController: CBCentralManagerDelegate { func isBluetoothAvailable() -> Bool { if #available(iOS 10.0, *) { return centralManager.state == CBManagerState.poweredOn } else { return centralManager.state == .poweredOn } } func centralManagerDidUpdateState(_ central: CBCentralManager) { switch central.state { case .poweredOn: print("可用") case .resetting: print("重置中") case .unsupported: print("不支持") case .unauthorized: print("未验证") case .poweredOff: print("未启动") case .unknown: print("未知的") @unknown default: print("未知的") } } // 发现设备 func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) { deviceName = peripheral.name if deviceName != nil { if deviceName.isNumber() { // 如果不包含 就加入 if !scanDevices.contains(peripheral) { scanDevices.append(peripheral) } } } } // 设备已连接 func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { LoadingHub.shared.hideLoading() print("设备已连接,开始搜索设备特性服务") cbPeripheral = peripheral cbPeripheral.delegate = self cbPeripheral.discoverServices(nil) } // 设备连接失败 func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) { LoadingHub.shared.hideLoading() AlertHub.shared.showWaringAlert(controller: self, messge: "设备连接失败,请检查") } // 设备断开连接 func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) { print("设备断开连接") deviceStatusLabel.text = "设备编号:未连接" currentValueLabel.text = "--" settingValueLabel.text = "--" maxValueLabel.text = "--" } } extension MapViewController: CBPeripheralDelegate { // 发送指令到设备 func writeToPeripheral(_ bytes: [UInt8]) { if writeCharacteristic != nil { cbPeripheral.writeValue(Data(bytes: bytes, count: bytes.count), for: writeCharacteristic, type: .withResponse) } } func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { for service in peripheral.services! { print("发现服务: \(service.uuid.uuidString)") // 发现服务: 0003CDD0-0000-1000-8000-00805F9B0131 if Constant.SERVICE_UUID.rawValue == service.uuid.uuidString { peripheral.discoverCharacteristics(nil, for: service) } } } func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { for character in service.characteristics! { print("发现特征值: \(character.uuid.uuidString)") if Constant.READ_CHARACTERISTIC_UUID.rawValue == character.uuid.uuidString { // 发现特征值: 0003CDD1-0000-1000-8000-00805F9B0131 cbPeripheral.readValue(for: character) } if Constant.WRITE_CHARACTERISTIC_UUID.rawValue == character.uuid.uuidString { // 发现特征值: 0003CDD2-0000-1000-8000-00805F9B0131 writeCharacteristic = character } cbPeripheral.setNotifyValue(true, for: character) } // 查询设备编号 writeToPeripheral(ASK_DEV_CODE_COMMAND) } func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) { if error != nil { print("发送数据失败! error信息: \(String(describing: error))") } } // 特征的订阅状体发生变化 func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) { guard error == nil else { return } } // 所有的,不管是 read , notify 的特征的值都是在这里读取 func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { if error != nil { return } if Constant.READ_CHARACTERISTIC_UUID.rawValue == characteristic.uuid.uuidString { let data = characteristic.value! if data.count == 28 { // 51, 51, 50, 48, 50, 49, 48, 49, 48, 48, 48, 51, 13, 10, 170, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 13, 10 // 只解析前14位,后14位是测量值 var deviceCodeData = Data() for i in 0 ... 13 { deviceCodeData.append(data[i]) } deviceStatusLabel.text = "设备编号: \(NSString(data: deviceCodeData, encoding: String.Encoding.utf8.rawValue)! as String)" // 获取到编号之后再开启数据发送命令 writeToPeripheral(OPEN_TRANSFER_COMMAND) } else if data.count == 14 { /** * 170,0,0,0,100,1,0,100,0,0,0,100,13,10 * * 170是数据标头AA * 13,10是数据结束位 * */ // 浓度:4字节 0-99999 var potencyValue = 0 for i in 1 ... 4 { potencyValue += abs(Int(data[i])) } currentValueLabel.text = potencyValue.description // 报警值:2字节 0-65535 var alarmValue = 0 for i in 6 ... 7 { alarmValue += abs(Int(data[i])) } settingValueLabel.text = alarmValue.description // 5s内最大值: 4字节 0-99999 var maxPotencyValue = 0 for i in 8 ... 11 { maxPotencyValue += abs(Int(data[i])) } maxValueLabel.text = maxPotencyValue.description } else { print("错误数据") } } } }