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!
    // 弹窗里面的输入框
    private var inputTextField: UITextField!
    private var inspectionId: String!

    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
        // 新建巡检弹窗
        creatNewInspection()
        // 延时加载地图
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [self] in
            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)
        }

        inspectTimeLabel.textAlignment = .right
        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] in
                // 列表显示扫描到的设备
                showTableView()
            })
        } else {
            AlertHub.shared.showWaringAlert(controller: self, message: "蓝牙不可用,请检查")
        }
    }

    func showTableView() {
        centralManager.stopScan()
        LoadingHub.shared.hideLoading()

        if scanDevices.count == 0 {
            AlertHub.shared.showWaringAlert(controller: self, message: "无可用设备,请确认设备是否已开启")
        } else {
            let actionSheetController = UIAlertController()
            for device in scanDevices {
                actionSheetController.addAction(UIAlertAction(title: device.name, style: .default, handler: { [self] alertAction in
                    // 连接点击的设备
                    LoadingHub.shared.showLoading(text: "设备连接中...")
                    scanDevices.forEach { it in
                        if alertAction.title == it.name {
                            centralManager.connect(it, options: nil)
                        }
                    }
                }))
            }
            actionSheetController.addAction(UIAlertAction(title: "取消", style: .cancel))
            present(actionSheetController, animated: true, completion: nil)
        }
    }

    // 重新发送指令
    @IBAction func refreshCmdAction(_ sender: Any) {
        if isBluetoothAvailable() {
            writeToPeripheral(OPEN_TRANSFER_COMMAND)
        } else {
            AlertHub.shared.showWaringAlert(controller: self, message: "请先连接设备")
        }
    }

    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 + 20))
            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 + 20))
            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 + 20))
            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()
        }
    }

    func creatNewInspection() {
        let msgAlertCtr = UIAlertController(title: nil, message: "请输入巡检标签", preferredStyle: .alert)
        msgAlertCtr.addTextField { [self] textField in
            textField.placeholder = "如:xxx区间巡检"
            inputTextField = textField
            inputTextField.delegate = self
        }
        let actionOK = UIAlertAction(title: "开始巡检", style: .default) { [self] (_: UIAlertAction) -> Void in
            if inputTextField.text == "" {
                AlertHub.shared.showWaringAlert(controller: self, message: "巡检标签不能为空")
                return
            }
            // 获取当前时间戳
            let timeInterval: TimeInterval = Date().timeIntervalSince1970
            let timeStamp = Int(timeInterval)
            // 新建巡检,inspectionId必为空
            inspectionId = timeStamp.id()
            print("巡检id: \(inspectionId!)")
            // 设置标题和时间
            inspectNameLabel.text = inputTextField.text!
            let dateformatter = DateFormatter()
            dateformatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
            inspectTimeLabel.text = dateformatter.string(from: Date(timeIntervalSince1970: timeInterval))

            //开始巡检
            startInspection()
        }
        let actionCancel = UIAlertAction(title: "放弃", style: .cancel) { [self] (_: UIAlertAction) -> Void in
            navigationController?.popViewController(animated: true)
        }
        // 设置取消按钮颜色为红色
        actionCancel.setValue(UIColor(red: 255 / 255, green: 0 / 255, blue: 0 / 255, alpha: 1), forKey: "titleTextColor")
        msgAlertCtr.addAction(actionOK)
        msgAlertCtr.addAction(actionCancel)
        present(msgAlertCtr, animated: true, completion: nil)
    }

    func startInspection() {
        //巡检主要就是记录巡检员的位置
    }

    @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 inspectButton = UIAlertAction(title: "报警事件", style: .default, handler: { [self] _ in
            // 实现代理的方法,传值
            valueDelegate.transfer(controller: self,
                    dic: ["isWarning": true,
                          "inspectionId": inspectionId!,
                          "inspectionName": inspectNameLabel.text!,
                          "longitude": Double(lng)!,
                          "latitude": Double(lat)!])
            navigationController?.pushViewController(newEventViewController, animated: true)
        })
        let eventLogButton = UIAlertAction(title: "自定义事件", style: .default, handler: { [self] _ in
            // 实现代理的方法,传值
            valueDelegate.transfer(controller: self,
                    dic: ["isWarning": false,
                          "inspectionId": inspectionId!,
                          "inspectionName": inspectNameLabel.text!,
                          "longitude": Double(lng)!,
                          "latitude": Double(lat)!])
            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, message: "设备连接失败,请检查")
    }

    // 设备断开连接
    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("错误数据")
            }
        }
    }
}

extension MapViewController: UITextFieldDelegate {
}