mirror of
https://gitlab.silvrtree.co.uk/martind2000/WolkSense-Hexiwear.git
synced 2025-01-30 17:40:13 +00:00
412 lines
8.6 KiB
Swift
412 lines
8.6 KiB
Swift
|
//
|
||
|
// CocoaMQTTFrame.swift
|
||
|
// CocoaMQTT
|
||
|
//
|
||
|
// Created by Feng Lee<feng@eqmtt.io> on 14/8/3.
|
||
|
// Copyright (c) 2015 emqtt.io. All rights reserved.
|
||
|
//
|
||
|
|
||
|
import Foundation
|
||
|
|
||
|
/**
|
||
|
* Encode and Decode big-endian UInt16
|
||
|
*/
|
||
|
extension UInt16 {
|
||
|
|
||
|
//Most Significant Byte (MSB)
|
||
|
var highByte: UInt8 { return UInt8( (self & 0xFF00) >> 8) }
|
||
|
|
||
|
//Least Significant Byte (LSB)
|
||
|
var lowByte: UInt8 { return UInt8(self & 0x00FF) }
|
||
|
|
||
|
var hlBytes: [UInt8] { return [highByte, lowByte] }
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* String with two bytes length
|
||
|
*/
|
||
|
extension String {
|
||
|
|
||
|
//ok?
|
||
|
var bytesWithLength: [UInt8] { return UInt16(utf8.count).hlBytes + utf8 }
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Bool to bit
|
||
|
*/
|
||
|
extension Bool {
|
||
|
|
||
|
var bit: UInt8 { return self ? 1 : 0}
|
||
|
|
||
|
init(bit: UInt8) {
|
||
|
self = (bit == 0) ? false : true
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* read bit
|
||
|
*/
|
||
|
extension UInt8 {
|
||
|
|
||
|
func bitAt(offset: UInt8) -> UInt8 {
|
||
|
return (self >> offset) & 0x01
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* MQTT Frame Type
|
||
|
*/
|
||
|
enum CocoaMQTTFrameType: UInt8 {
|
||
|
|
||
|
case RESERVED = 0x00
|
||
|
|
||
|
case CONNECT = 0x10
|
||
|
|
||
|
case CONNACK = 0x20
|
||
|
|
||
|
case PUBLISH = 0x30
|
||
|
|
||
|
case PUBACK = 0x40
|
||
|
|
||
|
case PUBREC = 0x50
|
||
|
|
||
|
case PUBREL = 0x60
|
||
|
|
||
|
case PUBCOMP = 0x70
|
||
|
|
||
|
case SUBSCRIBE = 0x80
|
||
|
|
||
|
case SUBACK = 0x90
|
||
|
|
||
|
case UNSUBSCRIBE = 0xA0
|
||
|
|
||
|
case UNSUBACK = 0xB0
|
||
|
|
||
|
case PINGREQ = 0xC0
|
||
|
|
||
|
case PINGRESP = 0xD0
|
||
|
|
||
|
case DISCONNECT = 0xE0
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* MQTT Frame
|
||
|
*/
|
||
|
class CocoaMQTTFrame {
|
||
|
|
||
|
|
||
|
/**
|
||
|
* |--------------------------------------
|
||
|
* | 7 6 5 4 | 3 | 2 1 | 0 |
|
||
|
* | Type | DUP flag | QoS | RETAIN |
|
||
|
* |--------------------------------------
|
||
|
*/
|
||
|
var header: UInt8 = 0
|
||
|
|
||
|
var type: UInt8 { return UInt8(header & 0xF0) }
|
||
|
|
||
|
var dup: Bool {
|
||
|
|
||
|
get { return ((header & 0x08) >> 3) == 0 ? false : true }
|
||
|
|
||
|
set { header |= (newValue.bit << 3) }
|
||
|
|
||
|
}
|
||
|
|
||
|
var qos: UInt8 {
|
||
|
|
||
|
//#define GETQOS(HDR) ((HDR & 0x06) >> 1)
|
||
|
get { return (header & 0x06) >> 1 }
|
||
|
|
||
|
//#define SETQOS(HDR, Q) (HDR | ((Q) << 1))
|
||
|
set { header |= (newValue << 1) }
|
||
|
|
||
|
}
|
||
|
|
||
|
var retain: Bool {
|
||
|
|
||
|
get { return (header & 0x01) == 0 ? false : true }
|
||
|
|
||
|
set { header |= newValue.bit }
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Variable Header
|
||
|
*/
|
||
|
var variableHeader: [UInt8] = []
|
||
|
|
||
|
/*
|
||
|
* Payload
|
||
|
*/
|
||
|
var payload: [UInt8] = []
|
||
|
|
||
|
init(header: UInt8) {
|
||
|
self.header = header
|
||
|
}
|
||
|
|
||
|
init(type: CocoaMQTTFrameType, payload: [UInt8] = []) {
|
||
|
self.header = type.rawValue
|
||
|
self.payload = payload
|
||
|
}
|
||
|
|
||
|
func data() -> [UInt8] {
|
||
|
self.pack()
|
||
|
return [UInt8]([header]) + encodeLength() + variableHeader + payload
|
||
|
}
|
||
|
|
||
|
func encodeLength() -> [UInt8] {
|
||
|
var bytes: [UInt8] = []
|
||
|
var digit: UInt8 = 0
|
||
|
var len: UInt32 = UInt32(variableHeader.count+payload.count)
|
||
|
repeat {
|
||
|
digit = UInt8(len % 128)
|
||
|
len = len / 128
|
||
|
// if there are more digits to encode, set the top bit of this digit
|
||
|
if len > 0 { digit = digit | 0x80 }
|
||
|
bytes.append(digit)
|
||
|
} while len > 0
|
||
|
return bytes
|
||
|
}
|
||
|
|
||
|
func pack() { return; } //do nothing
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* MQTT CONNECT Frame
|
||
|
*/
|
||
|
class CocoaMQTTFrameConnect: CocoaMQTTFrame {
|
||
|
|
||
|
let PROTOCOL_LEVEL = UInt8(4)
|
||
|
|
||
|
let PROTOCOL_VERSION: String = "MQTT/3.1.1"
|
||
|
|
||
|
let PROTOCOL_MAGIC: String = "MQTT"
|
||
|
|
||
|
/**
|
||
|
* |----------------------------------------------------------------------------------
|
||
|
* | 7 | 6 | 5 | 4 3 | 2 | 1 | 0 |
|
||
|
* | username | password | willretain | willqos | willflag | cleansession | reserved |
|
||
|
* |----------------------------------------------------------------------------------
|
||
|
*/
|
||
|
var flags: UInt8 = 0
|
||
|
|
||
|
var flagUsername: Bool {
|
||
|
//#define FLAG_USERNAME(F, U) (F | ((U) << 7))
|
||
|
get { return Bool(bit: (flags >> 7) & 0x01) }
|
||
|
|
||
|
set { flags |= (newValue.bit << 7) }
|
||
|
}
|
||
|
|
||
|
var flagPasswd: Bool {
|
||
|
//#define FLAG_PASSWD(F, P) (F | ((P) << 6))
|
||
|
get { return Bool(bit:(flags >> 6) & 0x01) }
|
||
|
|
||
|
set { flags |= (newValue.bit << 6) }
|
||
|
}
|
||
|
|
||
|
var flagWillRetain: Bool {
|
||
|
//#define FLAG_WILLRETAIN(F, R) (F | ((R) << 5))
|
||
|
get { return Bool(bit: (flags >> 5) & 0x01) }
|
||
|
|
||
|
set { flags |= (newValue.bit << 5) }
|
||
|
}
|
||
|
|
||
|
var flagWillQOS: UInt8 {
|
||
|
//#define FLAG_WILLQOS(F, Q) (F | ((Q) << 3))
|
||
|
get { return (flags >> 3) & 0x03 }
|
||
|
|
||
|
set { flags |= (newValue << 3) }
|
||
|
}
|
||
|
|
||
|
var flagWill: Bool {
|
||
|
//#define FLAG_WILL(F, W) (F | ((W) << 2))
|
||
|
get { return Bool(bit:(flags >> 2) & 0x01) }
|
||
|
|
||
|
set { flags |= ((newValue.bit) << 2) }
|
||
|
}
|
||
|
|
||
|
var flagCleanSess: Bool {
|
||
|
//#define FLAG_CLEANSESS(F, C) (F | ((C) << 1))
|
||
|
get { return Bool(bit: (flags >> 1) & 0x01) }
|
||
|
|
||
|
set { flags |= ((newValue.bit) << 1) }
|
||
|
}
|
||
|
|
||
|
var client: CocoaMQTTClient
|
||
|
|
||
|
init(client: CocoaMQTT) {
|
||
|
self.client = client
|
||
|
super.init(type: CocoaMQTTFrameType.CONNECT)
|
||
|
}
|
||
|
|
||
|
override func pack() {
|
||
|
|
||
|
//variable header
|
||
|
variableHeader += PROTOCOL_MAGIC.bytesWithLength
|
||
|
variableHeader.append(PROTOCOL_LEVEL)
|
||
|
|
||
|
//payload
|
||
|
payload += client.clientId.bytesWithLength
|
||
|
|
||
|
if let will = client.willMessage {
|
||
|
flagWill = true
|
||
|
flagWillQOS = will.qos.rawValue
|
||
|
flagWillRetain = will.retain
|
||
|
payload += will.topic.bytesWithLength
|
||
|
payload += will.payload
|
||
|
}
|
||
|
if let username = client.username {
|
||
|
flagUsername = true
|
||
|
payload += username.bytesWithLength
|
||
|
}
|
||
|
if let password = client.password {
|
||
|
flagPasswd = true
|
||
|
payload += password.bytesWithLength
|
||
|
}
|
||
|
|
||
|
//flags
|
||
|
flagCleanSess = client.cleanSess
|
||
|
variableHeader.append(flags)
|
||
|
variableHeader += client.keepAlive.hlBytes
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* MQTT PUBLISH Frame
|
||
|
*/
|
||
|
class CocoaMQTTFramePublish: CocoaMQTTFrame {
|
||
|
|
||
|
var msgid: UInt16?
|
||
|
|
||
|
var topic: String?
|
||
|
|
||
|
var data: [UInt8]?
|
||
|
|
||
|
init(msgid: UInt16, topic: String, payload: [UInt8]) {
|
||
|
super.init(type: CocoaMQTTFrameType.PUBLISH, payload: payload)
|
||
|
self.msgid = msgid
|
||
|
self.topic = topic
|
||
|
}
|
||
|
|
||
|
init(header: UInt8, data: [UInt8]) {
|
||
|
super.init(header: header)
|
||
|
self.data = data
|
||
|
}
|
||
|
|
||
|
func unpack() {
|
||
|
//topic
|
||
|
var msb = data![0], lsb = data![1]
|
||
|
let len = UInt16(msb) << 8 + UInt16(lsb)
|
||
|
var pos: Int = 2 + Int(len)
|
||
|
topic = NSString(bytes: [UInt8](data![2...(pos-1)]), length: Int(len), encoding: NSUTF8StringEncoding) as? String
|
||
|
|
||
|
//msgid
|
||
|
if qos == 0 {
|
||
|
msgid = 0
|
||
|
} else {
|
||
|
msb = data![pos]; lsb = data![pos+1]
|
||
|
msgid = UInt16(msb) << 8 + UInt16(lsb)
|
||
|
pos += 2
|
||
|
}
|
||
|
|
||
|
//payload
|
||
|
let end = data!.count - 1
|
||
|
|
||
|
if (end - pos >= 0) {
|
||
|
payload = [UInt8](data![pos...end])
|
||
|
//receives an empty message
|
||
|
} else {
|
||
|
payload = []
|
||
|
}
|
||
|
}
|
||
|
|
||
|
override func pack() {
|
||
|
variableHeader += topic!.bytesWithLength
|
||
|
if qos > 0 {
|
||
|
variableHeader += msgid!.hlBytes
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* MQTT PUBACK Frame
|
||
|
*/
|
||
|
class CocoaMQTTFramePubAck: CocoaMQTTFrame {
|
||
|
|
||
|
var msgid: UInt16?
|
||
|
|
||
|
init(type: CocoaMQTTFrameType, msgid: UInt16) {
|
||
|
super.init(type: type)
|
||
|
if type == CocoaMQTTFrameType.PUBREL {
|
||
|
qos = CocoaMQTTQOS.QOS1.rawValue
|
||
|
}
|
||
|
self.msgid = msgid
|
||
|
}
|
||
|
|
||
|
override func pack() {
|
||
|
variableHeader += msgid!.hlBytes
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* MQTT SUBSCRIBE Frame
|
||
|
*/
|
||
|
class CocoaMQTTFrameSubscribe: CocoaMQTTFrame {
|
||
|
|
||
|
var msgid: UInt16?
|
||
|
|
||
|
var topic: String?
|
||
|
|
||
|
var reqos: UInt8 = CocoaMQTTQOS.QOS0.rawValue
|
||
|
|
||
|
init(msgid: UInt16, topic: String, reqos: UInt8) {
|
||
|
super.init(type: CocoaMQTTFrameType.SUBSCRIBE)
|
||
|
self.msgid = msgid
|
||
|
self.topic = topic
|
||
|
self.reqos = reqos
|
||
|
self.qos = CocoaMQTTQOS.QOS1.rawValue
|
||
|
}
|
||
|
|
||
|
override func pack() {
|
||
|
variableHeader += msgid!.hlBytes
|
||
|
payload += topic!.bytesWithLength
|
||
|
payload.append(reqos)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* MQTT UNSUBSCRIBE Frame
|
||
|
*/
|
||
|
class CocoaMQTTFrameUnsubscribe: CocoaMQTTFrame {
|
||
|
|
||
|
var msgid: UInt16?
|
||
|
|
||
|
var topic: String?
|
||
|
|
||
|
init(msgid: UInt16, topic: String) {
|
||
|
super.init(type: CocoaMQTTFrameType.UNSUBSCRIBE)
|
||
|
self.msgid = msgid
|
||
|
self.topic = topic
|
||
|
qos = CocoaMQTTQOS.QOS1.rawValue
|
||
|
}
|
||
|
|
||
|
override func pack() {
|
||
|
variableHeader += msgid!.hlBytes
|
||
|
payload += topic!.bytesWithLength
|
||
|
}
|
||
|
|
||
|
}
|