forked from jbuehl/solaredge
-
Notifications
You must be signed in to change notification settings - Fork 2
/
semonitor.py
143 lines (133 loc) · 6.03 KB
/
semonitor.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#!/usr/bin/python
# SolarEdge inverter performance monitoring using the SolarEdge protocol
import time
import threading
from seConf import *
from seFiles import *
from seMsg import *
from seData import *
from seCommands import *
# global variables
threadLock = threading.Lock() # lock to synchronize reads and writes
masterEvent = threading.Event() # event to signal RS485 master release
running = True
# process the input data
def readData(dataFile, recFile, outFile):
if updateFileName != "": # create an array of zeros for the firmware update file
updateBuf = list('\x00'*updateSize)
if passiveMode:
msg = readMsg(dataFile, recFile) # skip data until the start of the first complete message
while running:
msg = readMsg(dataFile, recFile)
if msg == "": # end of file
# eof from network means connection was broken, wait for a reconnect and continue
if networkDevice:
closeData(dataFile)
dataFile = openDataSocket()
else: # all finished
if updateFileName != "": # write the firmware update file
writeUpdate()
return
if msg == "\x00"*len(msg): # ignore messages containing all zeros
if debugData: logData(msg)
else:
with threadLock:
try:
processMsg(msg, dataFile, recFile, outFile)
except Exception as ex:
debug("debugEnable", "Exception:", ex.args[0])
if haltOnException:
logData(msg)
raise
# process a received message
def processMsg(msg, dataFile, recFile, outFile):
# parse the message
(msgSeq, fromAddr, toAddr, function, data) = parseMsg(msg)
if function == 0:
# message could not be processed
debug("debugData", "Ignoring this message")
logData(data)
else:
msgData = parseData(function, data)
if (function == PROT_CMD_SERVER_POST_DATA) and (data != ""): # performance data
# write performance data to output file
writeData(msgData, outFile)
elif (updateFileName != "") and function == PROT_CMD_UPGRADE_WRITE: # firmware update data
updateBuf[msgData["offset"]:msgData["offset"]+msgData["length"]] = msgData["data"]
if (networkDevice or masterMode): # send reply
replyFunction = ""
if function == PROT_CMD_SERVER_POST_DATA: # performance data
# send ack
replyFunction = PROT_RESP_ACK
replyData = ""
elif function == PROT_CMD_SERVER_GET_GMT: # time request
# set time
replyFunction = PROT_RESP_SERVER_GMT
replyData = formatTime(int(time.time()), (time.localtime().tm_hour-time.gmtime().tm_hour)*60*60)
elif function == PROT_RESP_POLESTAR_MASTER_GRANT_ACK: # RS485 master release
masterEvent.set()
if replyFunction != "":
msg = formatMsg(msgSeq, toAddr, fromAddr, replyFunction, replyData)
sendMsg(dataFile, msg, recFile)
# write firmware image to file
def writeUpdate():
updateBuf = "".join(updateBuf)
debug("debugFiles", "writing", updateFileName)
with open(updateFileName, "w") as updateFile:
updateFile.write(updateBuf)
# RS485 master commands thread
def masterCommands(dataFile, recFile):
while running:
for slaveAddr in slaveAddrs:
with threadLock:
# grant control of the bus to the slave
sendMsg(dataFile, formatMsg(nextSeq(), masterAddr, int(slaveAddr, 16), PROT_CMD_POLESTAR_MASTER_GRANT), recFile)
# wait for slave to release the bus
masterEvent.clear()
masterEvent.wait()
time.sleep(masterMsgInterval)
# perform the specified commands
def doCommands(dataFile, commands, recFile):
slaveAddr = int(slaveAddrs[0], 16)
for command in commands:
# format the command parameters
function = int(command[0],16)
format = "<"+"".join(c[0] for c in command[1:])
params = [int(p[1:],16) for p in command[1:]]
seq = nextSeq()
# send the command
sendMsg(dataFile, formatMsg(seq, masterAddr, slaveAddr, function, struct.pack(format, *tuple(params))), recFile)
# wait for the response
msg = readMsg(dataFile, recFile)
(msgSeq, fromAddr, toAddr, response, data) = parseMsg(msg)
msgData = parseData(response, data)
# write response to output file
writeData({"command": function, "response": response, "sequence": seq, "data": msgData}, outFile)
# wait a bit before sending the next one
time.sleep(commandDelay)
if __name__ == "__main__":
# initialization
dataFile = openData(inFileName)
(recFile, outFile) = openOutFiles(recFileName, outFileName)
if passiveMode: # only reading from file or serial device
# read until eof then terminate
readData(dataFile, recFile, outFile)
else: # reading and writing to network or serial device
if commandAction: # commands were specified
# perform commands then terminate
doCommands(dataFile, commands, recFile)
else: # network or RS485
# start a thread for reading
readThread = threading.Thread(name=readThreadName, target=readData, args=(dataFile, recFile, outFile))
readThread.start()
debug("debugFiles", "starting", readThreadName)
if masterMode: # send RS485 master commands
# start a thread to poll for data
masterThread = threading.Thread(name=masterThreadName, target=masterCommands, args=(dataFile, recFile))
masterThread.start()
debug("debugFiles", "starting", masterThreadName)
# wait for termination
running = waitForEnd()
# cleanup
closeData(dataFile)
closeOutFiles(recFile, outFile)