Skip to content

Commit

Permalink
corrected which udp messages to wait for.
Browse files Browse the repository at this point in the history
  • Loading branch information
jollyjinx committed Jul 10, 2023
1 parent b3f3fda commit 7261f68
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 106 deletions.
2 changes: 1 addition & 1 deletion Sources/sma2mqttLibrary/DataObjects/PublishedValue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public struct PublishedValue: Encodable

public func encode(to encoder: Encoder) throws
{
enum CodingKeys: String, CodingKey { case unit, value, scale, id, prio, write, event,date }
enum CodingKeys: String, CodingKey { case unit, value, scale, id, prio, write, event, date }
var container = encoder.container(keyedBy: CodingKeys.self)

let objectDefinition = tagTranslator.smaObjectDefinitions[objectID]
Expand Down
153 changes: 79 additions & 74 deletions Sources/sma2mqttLibrary/SMADevice.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import AsyncHTTPClient
import Foundation
import JLog
import NIOCore
import NIOFoundationCompat
import NIOHTTP1
import RegexBuilder
Expand All @@ -22,7 +23,7 @@ public actor SMADevice
struct QueryObject: Hashable
{
let objectid: String
let path:String
let path: String
let interval: Int
}

Expand All @@ -40,7 +41,8 @@ public actor SMADevice
var objectsToQueryContinously = [String: QueryObject]()
var objectsToQueryNext = [QueryElement]()
var lastRequestSentDate = Date.distantPast
let minimumRequestInterval = 1.0 / 20.0 // 1 / maximumRequestsPerSecond
let udpMinimumRequestInterval = 1.0 / 20.0 // 1 / maximumRequestsPerSecond
let udpRequestTimeout = 1.5

let requestAllObjects: Bool

Expand All @@ -49,6 +51,7 @@ public actor SMADevice
var scheme = "https"
let httpClient: HTTPClient
private var sessionid: String?
let httpTimeout: NIOCore.TimeAmount = .seconds(5)

let udpReceiver: UDPReceiver
let udpEmitter: UDPEmitter?
Expand All @@ -64,7 +67,7 @@ public actor SMADevice
private var refreshTask: Task<Void, Error>?
private var tagTranslator = SMATagTranslator.shared

public init(address: String, userright: UserRight = .user, password: String = "00000", publisher: SMAPublisher? = nil, refreshInterval: Int = 30, interestingPaths: [String: Int] = [:], requestAllObjects: Bool = false, bindAddress:String = "0.0.0.0", udpEmitter: UDPEmitter? = nil) async throws
public init(address: String, userright: UserRight = .user, password: String = "00000", publisher: SMAPublisher? = nil, refreshInterval: Int = 30, interestingPaths: [String: Int] = [:], requestAllObjects: Bool = false, bindAddress: String = "0.0.0.0", udpEmitter: UDPEmitter? = nil) async throws
{
self.address = address
self.userright = userright
Expand All @@ -77,8 +80,7 @@ public actor SMADevice
self.requestAllObjects = requestAllObjects
self.udpEmitter = udpEmitter

self.udpReceiver = try UDPReceiver(bindAddress: bindAddress, listenPort: 0)

udpReceiver = try UDPReceiver(bindAddress: bindAddress, listenPort: 0)

name = address
hasDeviceName = false
Expand All @@ -101,7 +103,7 @@ public actor SMADevice
{
JLog.error("\(address): Failed to query interesting objects: \(error)")
errorcounter += 1
try? await Task.sleep(for: .seconds(1))
// try? await Task.sleep(for: .seconds(1))
}
}
JLog.error("\(address): too many erros")
Expand All @@ -124,79 +126,74 @@ public extension SMADevice

JLog.debug("\(address):received udp packet:\(data.hexDump)")

let smaPacket: SMAPacket
guard let smaPacket = try? SMAPacket(data: data) else { return nil }

do
return await receivedSMAPacket(smaPacket)
}

func receivedSMAPacket(_ smaPacket: SMAPacket) async -> SMAPacket?
{
if let netPacket = smaPacket.netPacket
{
smaPacket = try SMAPacket(data: data)
udpLoggedIn = netPacket.isLoggedIn
udpSystemId = netPacket.header.sourceSystemId
udpSerial = netPacket.header.sourceSerial

if let netPacket = smaPacket.netPacket
if netPacket.header.u16command == 0xFFFD
{
udpLoggedIn = netPacket.isLoggedIn
udpSystemId = netPacket.header.sourceSystemId
udpSerial = netPacket.header.sourceSerial
return nil
}

if netPacket.header.u16command == 0xFFFD
{
return nil
}
let objectIDs = netPacket.values.map { String(format: "%04X_%02X%04X00", netPacket.header.u16command, $0.type, $0.address) }

let objectIDs = netPacket.values.map { String(format: "%04X_%02X%04X00", netPacket.header.u16command, $0.type, $0.address) }
if let objectID = objectIDs.first(where: { tagTranslator.objectsAndPaths[$0] != nil }),
let simpleObject = tagTranslator.objectsAndPaths[objectID]
{
JLog.debug("\(address): objectid:\(objectID) name:\(simpleObject.json)")
justRetrievedObject(objectID: objectID)

if let objectID = objectIDs.first(where: { tagTranslator.objectsAndPaths[$0] != nil }),
let simpleObject = tagTranslator.objectsAndPaths[objectID]
{
JLog.trace("\(address): objectid:\(objectID) name:\(simpleObject.json)")
justRetrievedObject(objectID: objectID)
let path = name + "/\(simpleObject.path)"
var resultValues = [GetValuesResult.Value]()

let path = name + "/\(simpleObject.path)"
var resultValues = [GetValuesResult.Value]()
for value in netPacket.values
{
JLog.trace("\(address): objectid:\(objectID)")

for value in netPacket.values
switch value.value
{
JLog.trace("\(address): objectid:\(objectID)")

switch value.value
{
case let .uint(value):
if let firstValue = value.first as? UInt32
{
let resultValue = GetValuesResult.Value.intValue(Int(firstValue))
resultValues.append(resultValue)
}
case let .int(value):
if let firstValue = value.first as? Int32
{
let resultValue = GetValuesResult.Value.intValue(Int(firstValue))
resultValues.append(resultValue)
}

case let .string(string):
let resultValue = GetValuesResult.Value.stringValue(string)
case let .uint(value):
if let firstValue = value.first as? UInt32
{
let resultValue = GetValuesResult.Value.intValue(Int(firstValue))
resultValues.append(resultValue)

case let .tags(tags):
let resultValue = GetValuesResult.Value.tagValues(tags.map { Int($0) })
}
case let .int(value):
if let firstValue = value.first as? Int32
{
let resultValue = GetValuesResult.Value.intValue(Int(firstValue))
resultValues.append(resultValue)
}

default:
try? await publisher?.publish(to: path + ".\(value.number)", payload: value.json, qos: .atMostOnce, retain: false)
}
}
case let .string(string):
let resultValue = GetValuesResult.Value.stringValue(string)
resultValues.append(resultValue)

let singleValue = PublishedValue(objectID: objectID, values: resultValues, tagTranslator: tagTranslator)
try? await publisher?.publish(to: path, payload: singleValue.json, qos: .atMostOnce, retain: false)
}
else if !objectIDs.isEmpty
{
JLog.error("\(address): objectIDs not known \(objectIDs)")
case let .tags(tags):
let resultValue = GetValuesResult.Value.tagValues(tags.map { Int($0) })
resultValues.append(resultValue)

default:
try? await publisher?.publish(to: path + ".\(value.number)", payload: value.json, qos: .atMostOnce, retain: false)
}
}

let singleValue = PublishedValue(objectID: objectID, values: resultValues, tagTranslator: tagTranslator)
try? await publisher?.publish(to: path, payload: singleValue.json, qos: .atMostOnce, retain: false)
}
else if !objectIDs.isEmpty
{
JLog.error("\(address): objectIDs not known \(objectIDs)")
}
}
catch
{
JLog.error("\(address):did not decode :\(error) \(data.hexDump)")
return nil
}

JLog.trace("\(address): received \(smaPacket)")
Expand Down Expand Up @@ -244,7 +241,7 @@ public extension SMADevice
{
let objectToQuery = try getNextRequest()

let nextReadDate = max( objectToQuery.nextReadDate , lastRequestSentDate + minimumRequestInterval )
let nextReadDate = max(objectToQuery.nextReadDate, lastRequestSentDate + udpMinimumRequestInterval)

let timeToWait = nextReadDate.timeIntervalSinceNow

Expand All @@ -253,7 +250,7 @@ public extension SMADevice
try await Task.sleep(for: .seconds(timeToWait))
}
lastRequestSentDate = Date()

try await udpQueryObject(objectID: objectToQuery.objectid)
}

Expand All @@ -279,9 +276,17 @@ public extension SMADevice
}

JLog.trace("\(address): sending udp packet:\(packetToSend)")
let packet = try await udpReceiver.sendReceivePacket(data: [UInt8](packetToSend.hexStringToData()), address: address, port: 9522, receiveTimeout: 1.0)
let packets = try await udpReceiver.sendReceivePacket(data: [UInt8](packetToSend.hexStringToData()), packetcounter: packetcounter, address: address, port: 9522, receiveTimeout: udpRequestTimeout)

let _ = await receivedUDPData(packet.data)
if !packets.isEmpty
{
lastSeen = Date()
}

for packet in packets
{
_ = await receivedSMAPacket(packet)
}
}
}

Expand Down Expand Up @@ -451,34 +456,34 @@ extension SMADevice
return nil
}

func objectIdIsInteresting(_ objectid: String) -> (path:String,interval:Int?)
func objectIdIsInteresting(_ objectid: String) -> (path: String, interval: Int?)
{
let path = "/" + (tagTranslator.objectsAndPaths[objectid]?.path ?? "unkown-id-\(objectid)")

let interval = pathIsInteresting(path)

JLog.debug("\(address):\(objectid) \(path) interval:\( interval ?? -1 )")
JLog.trace("\(address):\(objectid) \(path) interval:\(interval ?? -1)")

return (path:path,interval:interval)
return (path: path, interval: interval)
}

@discardableResult
func addObjectToQueryContinouslyIfNeeded(objectid: String) -> Bool
{
JLog.trace("\(address):working on objectId:\(objectid)")

let (path,interval) = objectIdIsInteresting(objectid)
let (path, interval) = objectIdIsInteresting(objectid)

if let interval
{
if let inuse = objectsToQueryContinously.values.first(where:{ $0.path == path })
if let inuse = objectsToQueryContinously.values.first(where: { $0.path == path })
{
JLog.notice("\(address): Won't query objectid:\(objectid) - object with same path:\(inuse.objectid) path:\(inuse.path)")
return false
}
JLog.debug("\(address): adding to objectsToQueryContinously objectid:\(objectid) path:\(path) interval:\(interval)")

let queryObject = objectsToQueryContinously[objectid] ?? QueryObject(objectid: objectid, path:path, interval: interval)
let queryObject = objectsToQueryContinously[objectid] ?? QueryObject(objectid: objectid, path: path, interval: interval)

if interval <= queryObject.interval
{
Expand Down Expand Up @@ -575,7 +580,7 @@ extension SMADevice
request.body = .bytes(requestBody)
}

let response = try await httpClient.execute(request, timeout: .seconds(5))
let response = try await httpClient.execute(request, timeout: httpTimeout)

JLog.trace("\(address):url:\(url) got response: \(response)")
lastSeen = Date()
Expand Down
2 changes: 1 addition & 1 deletion Sources/sma2mqttLibrary/SMALighthouse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public actor SMALighthouse
guard Date().timeIntervalSince(lastDiscoveryRequestDate) > disoveryRequestInterval else { return }

let dicoveryPacket = SMAPacketGenerator.generateDiscoveryPacket()
await mcastReceiver.sendPacket(data: [UInt8](dicoveryPacket.hexStringToData()), address: mcastAddress, port: mcastPort)
await mcastReceiver.sendPacket(data: [UInt8](dicoveryPacket.hexStringToData()), packetcounter: 0, address: mcastAddress, port: mcastPort)
lastDiscoveryRequestDate = Date()
}

Expand Down
4 changes: 2 additions & 2 deletions Sources/sma2mqttLibrary/SMAPacket/SMAPacketGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ extension SMAPacketGenerator
return try generateCommandPacket(packetcounter: packetcounter, command: command, dstSystemId: dstSystemId, dstSerial: dstSerial)
}

private static let localhostname = Host.current().names.filter({ $0 != "localhost" }).sorted(by: { $0.count < $1.count }).first ?? "localhost"
private static let localhostname = Host.current().names.filter { $0 != "localhost" }.sorted(by: { $0.count < $1.count }).first ?? "localhost"

static func generateCommandPacket(packetcounter: Int, command: String, dstSystemId: UInt16 = 0xFFFF, dstSerial: UInt32 = 0xFFFF_FFFF) throws -> String
{
Expand All @@ -32,7 +32,7 @@ extension SMAPacketGenerator
let dstSysidString = String(format: "%02x%02x", dstSystemId & 0xFF, (dstSystemId & 0xFF00) >> 8)
let dstSerialString = String(format: "%02x%02x%02x%02x", dstSerial & 0xFF, (dstSerial >> 8) & 0xFF, (dstSerial >> 16) & 0xFF, (dstSerial >> 24) & 0xFF)

let ownidString = String(format: "%04x", Self.localhostname.hashValue & 0xFFFF )
let ownidString = String(format: "%04x", Self.localhostname.hashValue & 0xFFFF)
let header = """
534d 4100
0004 02a0 0000 0001
Expand Down
4 changes: 2 additions & 2 deletions Sources/sma2mqttLibrary/Tools/MutlicastReceiver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import JLog

public protocol UDPEmitter
{
func sendPacket(data: [UInt8], address: String, port: UInt16) async
func sendPacket(data: [UInt8], packetcounter: Int, address: String, port: UInt16) async
}

public struct Packet
Expand Down Expand Up @@ -154,7 +154,7 @@ actor MulticastReceiver: UDPEmitter
}
}

func sendPacket(data: [UInt8], address: String, port: UInt16)
func sendPacket(data: [UInt8], packetcounter _: Int, address: String, port: UInt16)
{
var destinationAddress = sockaddr_in()

Expand Down
Loading

0 comments on commit 7261f68

Please sign in to comment.