Newer
Older
LaserMethane / LaserMethane / ViewController / Map / MapViewController.swift
//
//  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("错误数据")
            }
        }
    }
}