Skip to content

Commit b3f3fda

Browse files
committed
added UDPReceiver class being used to query one inverter and waiting for the result
1 parent 1410f63 commit b3f3fda

File tree

11 files changed

+269
-21
lines changed

11 files changed

+269
-21
lines changed

Package.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,12 @@ let package = Package(
3535
.target(
3636
name: "sma2mqttLibrary",
3737
dependencies: [
38+
"CNative",
3839
.product(name: "BinaryCoder", package: "BinaryCoder"),
3940
.product(name: "AsyncHTTPClient", package: "async-http-client"),
4041
.product(name: "MQTTNIO", package: "mqtt-nio"),
4142
.product(name: "JLog", package: "JLog"),
43+
4244
],
4345
resources: [
4446
.copy("Obis/Resources/obisdefinition.json"),
@@ -47,6 +49,10 @@ let package = Package(
4749
.copy("SMAPacket/Resources/SMANetPacketDefinitions.json"),
4850
]
4951
),
52+
.target(
53+
name: "CNative",
54+
dependencies: []
55+
),
5056
.testTarget(
5157
name: "sma2mqttTests",
5258
dependencies: [

Sources/CNative/include/select.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#include <sys/select.h>
2+
3+
__BEGIN_DECLS
4+
5+
extern void SWIFT_FD_SET(int d, fd_set *set);
6+
7+
__END_DECLS

Sources/CNative/select.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#include "select.h"
2+
3+
void SWIFT_FD_SET(int d, fd_set *set) {
4+
FD_SET(d, &(*set));
5+
}

Sources/sma2mqtt/sma2mqtt.swift

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,10 @@ extension JLog.Level: ExpressibleByArgument {}
5151
"ac-side/grid-measurements/power:1",
5252
"ac-side/measured-values/daily-yield:30",
5353

54-
"immediate/feedin:1",
55-
"immediate/usage:1",
56-
5754
"battery/state-of-charge:20",
5855
"battery/battery/temperature:30",
59-
"battery/battery/battery-charge/battery-charge:20",
60-
// "*:0", // all once
56+
"battery/battery-charge/battery-charge:20",
57+
// "*:600", // all once
6158
]
6259
func run() async throws
6360
{

Sources/sma2mqttLibrary/DataObjects/PublishedValue.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@ public struct PublishedValue: Encodable
2323

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

2929
let objectDefinition = tagTranslator.smaObjectDefinitions[objectID]
3030
let compacted = values.compactMap { $0 }
31+
try container.encode(objectID, forKey: .id)
32+
// try container.encode(Date(), forKey: .date)
3133

3234
switch compacted.first
3335
{

Sources/sma2mqttLibrary/SMADevice.swift

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public actor SMADevice
2222
struct QueryObject: Hashable
2323
{
2424
let objectid: String
25+
let path:String
2526
let interval: Int
2627
}
2728

@@ -38,6 +39,8 @@ public actor SMADevice
3839

3940
var objectsToQueryContinously = [String: QueryObject]()
4041
var objectsToQueryNext = [QueryElement]()
42+
var lastRequestSentDate = Date.distantPast
43+
let minimumRequestInterval = 1.0 / 20.0 // 1 / maximumRequestsPerSecond
4144

4245
let requestAllObjects: Bool
4346

@@ -47,6 +50,7 @@ public actor SMADevice
4750
let httpClient: HTTPClient
4851
private var sessionid: String?
4952

53+
let udpReceiver: UDPReceiver
5054
let udpEmitter: UDPEmitter?
5155
var udpSystemId: UInt16 = 0xFFFF
5256
var udpSerial: UInt32 = 0xFFFF_FFFF
@@ -60,7 +64,7 @@ public actor SMADevice
6064
private var refreshTask: Task<Void, Error>?
6165
private var tagTranslator = SMATagTranslator.shared
6266

63-
public init(address: String, userright: UserRight = .user, password: String = "00000", publisher: SMAPublisher? = nil, refreshInterval: Int = 30, interestingPaths: [String: Int] = [:], requestAllObjects: Bool = false, udpEmitter: UDPEmitter? = nil) async throws
67+
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
6468
{
6569
self.address = address
6670
self.userright = userright
@@ -72,6 +76,10 @@ public actor SMADevice
7276
self.interestingPaths = interestingPaths
7377
self.requestAllObjects = requestAllObjects
7478
self.udpEmitter = udpEmitter
79+
80+
self.udpReceiver = try UDPReceiver(bindAddress: bindAddress, listenPort: 0)
81+
82+
7583
name = address
7684
hasDeviceName = false
7785

@@ -114,7 +122,7 @@ public extension SMADevice
114122
return nil
115123
}
116124

117-
JLog.trace("received udp packet:\(data.hexDump)")
125+
JLog.debug("\(address):received udp packet:\(data.hexDump)")
118126

119127
let smaPacket: SMAPacket
120128

@@ -199,7 +207,7 @@ public extension SMADevice
199207
{
200208
if obisvalue.mqtt != .invisible
201209
{
202-
try? await publisher?.publish(to: name + "/" + obisvalue.topic, payload: obisvalue.json, qos: .atLeastOnce, retain: obisvalue.mqtt == .retained)
210+
try? await publisher?.publish(to: name + "/" + obisvalue.topic, payload: obisvalue.json, qos: .atMostOnce, retain: obisvalue.mqtt == .retained)
203211
}
204212
}
205213

@@ -236,12 +244,16 @@ public extension SMADevice
236244
{
237245
let objectToQuery = try getNextRequest()
238246

239-
let timeToWait = objectToQuery.nextReadDate.timeIntervalSinceNow
247+
let nextReadDate = max( objectToQuery.nextReadDate , lastRequestSentDate + minimumRequestInterval )
248+
249+
let timeToWait = nextReadDate.timeIntervalSinceNow
240250

241251
if timeToWait > 0
242252
{
243253
try await Task.sleep(for: .seconds(timeToWait))
244254
}
255+
lastRequestSentDate = Date()
256+
245257
try await udpQueryObject(objectID: objectToQuery.objectid)
246258
}
247259

@@ -267,7 +279,9 @@ public extension SMADevice
267279
}
268280

269281
JLog.trace("\(address): sending udp packet:\(packetToSend)")
270-
await udpEmitter?.sendPacket(data: [UInt8](packetToSend.hexStringToData()), address: address, port: 9522)
282+
let packet = try await udpReceiver.sendReceivePacket(data: [UInt8](packetToSend.hexStringToData()), address: address, port: 9522, receiveTimeout: 1.0)
283+
284+
let _ = await receivedUDPData(packet.data)
271285
}
272286
}
273287

@@ -335,12 +349,13 @@ extension SMADevice
335349
name = deviceName
336350
}
337351

352+
try await getInformationDictionary(atPath: "/dyn/getDashValues.json")
353+
try await getInformationDictionary(atPath: "/dyn/getAllOnlValues.json")
354+
338355
for objectid in tagTranslator.smaObjectDefinitions.keys
339356
{
340357
addObjectToQueryContinouslyIfNeeded(objectid: objectid)
341358
}
342-
// try await getInformationDictionary(atPath: "/dyn/getDashValues.json")
343-
// try await getInformationDictionary(atPath: "/dyn/getAllOnlValues.json")
344359

345360
try? await logout()
346361
}
@@ -436,21 +451,34 @@ extension SMADevice
436451
return nil
437452
}
438453

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

443-
return pathIsInteresting(path)
458+
let interval = pathIsInteresting(path)
459+
460+
JLog.debug("\(address):\(objectid) \(path) interval:\( interval ?? -1 )")
461+
462+
return (path:path,interval:interval)
444463
}
445464

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

451-
if let interval = objectIdIsInteresting(objectid)
470+
let (path,interval) = objectIdIsInteresting(objectid)
471+
472+
if let interval
452473
{
453-
let queryObject = objectsToQueryContinously[objectid] ?? QueryObject(objectid: objectid, interval: interval)
474+
if let inuse = objectsToQueryContinously.values.first(where:{ $0.path == path })
475+
{
476+
JLog.notice("\(address): Won't query objectid:\(objectid) - object with same path:\(inuse.objectid) path:\(inuse.path)")
477+
return false
478+
}
479+
JLog.debug("\(address): adding to objectsToQueryContinously objectid:\(objectid) path:\(path) interval:\(interval)")
480+
481+
let queryObject = objectsToQueryContinously[objectid] ?? QueryObject(objectid: objectid, path:path, interval: interval)
454482

455483
if interval <= queryObject.interval
456484
{
@@ -498,7 +526,7 @@ extension SMADevice
498526
if hasDeviceName,
499527
addObjectToQueryContinouslyIfNeeded(objectid: objectId.key)
500528
{
501-
try await publisher?.publish(to: mqttPath, payload: singleValue.json, qos: .atMostOnce, retain: true)
529+
try await publisher?.publish(to: mqttPath, payload: singleValue.json, qos: .atMostOnce, retain: false)
502530
}
503531
}
504532
catch

Sources/sma2mqttLibrary/SMALighthouse.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public actor SMALighthouse
8585

8686
JLog.debug("Got new SMA Device with remoteAddress:\(remoteAddress)")
8787

88-
let task = Task { try await SMADevice(address: remoteAddress, userright: .user, password: password, publisher: mqttPublisher, interestingPaths: interestingPaths, udpEmitter: mcastReceiver) }
88+
let task = Task { try await SMADevice(address: remoteAddress, userright: .user, password: password, publisher: mqttPublisher, interestingPaths: interestingPaths, bindAddress: bindAddress, udpEmitter: mcastReceiver) }
8989
smaDeviceCache[remoteAddress] = .inProgress(task)
9090

9191
do

Sources/sma2mqttLibrary/SMAPacket/SMAPacketGenerator.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ extension SMAPacketGenerator
2121
return try generateCommandPacket(packetcounter: packetcounter, command: command, dstSystemId: dstSystemId, dstSerial: dstSerial)
2222
}
2323

24+
private static let localhostname = Host.current().names.filter({ $0 != "localhost" }).sorted(by: { $0.count < $1.count }).first ?? "localhost"
25+
2426
static func generateCommandPacket(packetcounter: Int, command: String, dstSystemId: UInt16 = 0xFFFF, dstSerial: UInt32 = 0xFFFF_FFFF) throws -> String
2527
{
2628
let jobid = String(format: "%02x", 1)
@@ -30,7 +32,7 @@ extension SMAPacketGenerator
3032
let dstSysidString = String(format: "%02x%02x", dstSystemId & 0xFF, (dstSystemId & 0xFF00) >> 8)
3133
let dstSerialString = String(format: "%02x%02x%02x%02x", dstSerial & 0xFF, (dstSerial >> 8) & 0xFF, (dstSerial >> 16) & 0xFF, (dstSerial >> 24) & 0xFF)
3234

33-
let ownid = String(format: "%04x", generateRandomNumber())
35+
let ownidString = String(format: "%04x", Self.localhostname.hashValue & 0xFFFF )
3436
let header = """
3537
534d 4100
3638
0004 02a0 0000 0001
@@ -41,7 +43,7 @@ extension SMAPacketGenerator
4143
A0
4244
\(dstSysidString) \(dstSerialString) 00
4345
01
44-
1234 \(ownid) 4321 00
46+
1234 \(ownidString) 4321 00
4547
\(jobid)
4648
\(result)
4749
\(remainingpackets)

Sources/sma2mqttLibrary/Tools/Extensions.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ public extension UInt32 { var ipv4String: String { "\(self >> 24).\(self >> 16 &
88

99
#if os(Linux)
1010
public let NSEC_PER_SEC: Int64 = 1_000_000_000
11+
public let USEC_PER_SEC: Int64 = 1_000_000
1112
#endif
1213

1314
public extension Data

Sources/sma2mqttLibrary/Tools/MQTTPublisher.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public actor MQTTPublisher: SMAPublisher
4848
{
4949
_ = self.mqttClient.connect()
5050
}
51+
JLog.debug("publish:\(topic) payload:\(payload)")
5152
_ = self.mqttClient.publish(to: topic, payload: byteBuffer, qos: qos, retain: retain)
5253
}
5354
}

0 commit comments

Comments
 (0)