forked from stik79/DeltaPVOutput
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdeltaInv.py
executable file
·170 lines (152 loc) · 7.42 KB
/
deltaInv.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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
import crc,struct,sys
from crc import CRC16
from struct import *
import datetime
class DeltaInverter:
inverterNum=0;
#Known Commands
##StrValue, Format, divisor, units
cmds = {'\x10\x01': ('DC Cur1',0,10.0,'A'),
'\x10\x02': ('DC Volts1',0,1,'V'),
'\x10\x03': ('DC Pwr1',0,1,'W'),
'\x10\x04': ('DC Cur2',0,10.0,'A'),
'\x10\x05': ('DC Volts2',0,1,'V'),
'\x10\x06': ('DC Pwr2',0,1,'W'),
'\x10\x07': ('AC Current',0,10.0,'A'),
'\x10\x08': ('AC Volts',0,1,'V'),
'\x10\x09': ('AC Power',0,1,'W'),
'\x11\x07': ('AC I Avg',0,10.0,'A'),
'\x11\x08': ('AC V Avg',0,1,'V'),
'\x11\x09': ('AC P Avg',0,1,'W'),
'\x13\x03': ('Day Wh',0,1,'Wh'),
'\x13\x04': ('Uptime',0,1,'min'),
'\x00\x00': ('Inverter Type',9,0,''),
'\x00\x01': ('Serial',1,0,''),
'\x00\x08': ('Part',1,0,''),
'\x00\x40': ('FW Version',10,0,''),
'\x20\x05': ('AC Temp',0,1,'o'),
'\x21\x08': ('DC Temp',0,1,'o')
};
#Constructor takes inverter number (incase you have more than 1)
def __init__(self,inverter=1):
self.inverterNum=inverter
self.crcCalc = CRC16()
#private to do the binary packing of the protocol
def __buildCmd(self, cmd):
l = len(cmd)
crc = self.crcCalc.calcString( struct.pack('BBB%ds'%l,5,self.inverterNum,l,cmd))
lo = crc & (0xff);
high = (crc>>8) & 0xff;
return struct.pack('BBBB%dsBBB' %len(cmd),2,5,self.inverterNum,len(cmd),cmd,lo,high,3);
#retrieves the instruction for the given human readable command
def __findCmd(self,strValue):
for k,v in self.cmds.iteritems():
if(v[0]==strValue):
return k
#unpacks the given command into an {Instruction} {Value} {Units} string
def __unpackFormatted(self,cmd):
if not self.isValidResponse(cmd):
return "Invalid Response"
cmdcontents = cmd[1:-3]
lendata = ord(cmdcontents[2])-2
try:
stringName,fmt,divisor,unit = self.cmds[cmdcontents[3:5]]
if fmt==0: ##General Numbers
resp,invNum,size,instruction,value = struct.unpack('>BBB2sH',cmdcontents)
value = value / divisor
elif fmt==1: ##ascii string
resp,invNum,size,instruction,value = struct.unpack('>BBB2s%ds' %lendata,cmdcontents)
elif fmt==9: ##Model
resp,invNum,size,instruction,typeof,model,value = struct.unpack('>BBB2sBB%ds' % (lendata-2),cmdcontents)
return self.cmds[instruction][0]+": Type:" + str(typeof) + " Model:" +value
elif fmt==10: ##FWVersion #
resp,invNum,size,instruction,ver,major,minor = struct.unpack('>BBB2sBBB',cmdcontents)
return self.cmds[instruction][0]+": " + str(ver) +"." + str(major)+ "."+ str(minor)
else:
resp,invNum,size,instruction,value = struct.unpack('>BBB2s%ds' % lendata,cmdcontents)
return self.cmds[instruction][0] + ": " + str(value) + " "+unit
except:
return "Error parsing string, perhaps unknown instruction"
#Returns the packed command to be sent over serail,
#Command includes STX, inverter number, CRC, ETX
def getCmdStringFor(self,cmd):
return self.__buildCmd(self.__findCmd(cmd))
# Returns the packed command to set the inverter's system date
# command inludes STX, inverter number, CRC, EXT
# the input parameter dt is a datetime.datetime object
# and if not passed in the current date/time is used
def getCmdStringSetDate(self,dt=None):
if dt is None:
dt = datetime.datetime.now()
cmd = struct.pack('BBBBBB', 0x00, 0xa0, 0x06, dt.day, dt.month, dt.year % 100)
return self.__buildCmd(cmd)
# Returns the packed command to set the inverter's system time
# command inludes STX, inverter number, CRC, EXT
# the input parameter dt is a datetime.datetime object
# and if not passed in the current date/time is used
def getCmdStringSetTime(self,dt=None):
if dt is None:
dt = datetime.datetime.now()
cmd = struct.pack('BBBBB', 0x00, 0xa1, dt.hour, dt.minute, dt.second)
return self.__buildCmd(cmd)
# returns two packets constructed by getCmdStringSetDate and
# getCmdStringSetTime. dt is a datetime.datetime object and if
# not passed in the current system time is used.
def getCmdsSetClock(self,dt=None):
if dt is None:
dt = datetime.datetime.now()
cmdSetDate = self.getCmdStringSetDate(dt)
cmdSetTime = self.getCmdStringSetTime(dt)
return cmdSetDate, cmdSetTime
#Returns a formatted human readble form of a response
def getFormattedResponse(self,cmd):
return self.__unpackFormatted(cmd)
#Returns a raw value from a response
def getValueFromResponse(self,cmd):
return self.__unpackData(cmd)
#prints out hex values of a command string and the related instruction
def debugRequestString(self,cmdString):
cmd = cmdString[4:6]
strCmd = self.cmds[cmd][0]
inverter = ord(cmdString[2])
print "%s on inverter %d:" % (strCmd,inverter)
for ch in cmdString:
sys.stdout.write("%02X " % ord(ch))
print ""
#checks for a valid STX, ETX and CRC
def isValidResponse(self,cmd):
if ord(cmd[1])<> 0x06 or ord(cmd[0])!=0x02 or ord(cmd[len(cmd)-1])!=0x03:
return False
cmdcontents = cmd[1:-3]
crc = self.crcCalc.calcString(cmdcontents)
lo = crc & (0xff)
high = (crc>>8) & 0xff
crcByte = len(cmd)-3
if ord(cmd[crcByte])!=lo or ord(cmd[crcByte+1])!=high:
return False
return True
#Returns a raw value from a response
def __unpackData(self,cmd):
if not self.isValidResponse(cmd):
return "Invalid Response"
cmdcontents = cmd[1:-3]
lendata = ord(cmdcontents[2])-2
try:
stringName,fmt,divisor,unit = self.cmds[cmdcontents[3:5]]
if fmt==0: ##General Numbers
resp,invNum,size,instruction,value = struct.unpack('>BBB2sH',cmdcontents)
value = value / divisor
elif fmt==1: ##ascii string
resp,invNum,size,instruction,value = struct.unpack('>BBB2s%ds' %lendata,cmdcontents)
# return self.cmds[instruction][0] + ": " + str(value) + " "+unit
elif fmt==9: ##Model
resp,invNum,size,instruction,typeof,model,value = struct.unpack('>BBB2sBB%ds' % (lendata-2),cmdcontents)
return ": Type:" + str(typeof) + " Model:" +value
elif fmt==10: ##FWVersion #
resp,invNum,size,instruction,ver,major,minor = struct.unpack('>BBB2sBBB',cmdcontents)
return str(ver) +"." + str(major)+ "."+ str(minor)
else:
resp,invNum,size,instruction,value = struct.unpack('>BBB2s%ds' % lendata,cmdcontents)
return str(value)
except:
return "Error parsing string, perhaps unknown instruction"