pvblocks 0.1.0__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pvblocks might be problematic. Click here for more details.
- {pvblocks-0.1.0 → pvblocks-0.1.2}/PKG-INFO +1 -1
- {pvblocks-0.1.0 → pvblocks-0.1.2}/pyproject.toml +1 -1
- pvblocks-0.1.2/src/pvblocks/__init__.py +2 -0
- pvblocks-0.1.2/src/pvblocks/constants.py +43 -0
- {pvblocks-0.1.0 → pvblocks-0.1.2}/src/pvblocks/pvblocks_system.py +19 -55
- {pvblocks-0.1.0 → pvblocks-0.1.2}/src/pvblocks.egg-info/PKG-INFO +1 -1
- {pvblocks-0.1.0 → pvblocks-0.1.2}/src/pvblocks.egg-info/SOURCES.txt +2 -1
- {pvblocks-0.1.0 → pvblocks-0.1.2}/tests/test.py +17 -4
- pvblocks-0.1.2/tests/testui.py +395 -0
- pvblocks-0.1.0/src/pvblocks/__init__.py +0 -2
- pvblocks-0.1.0/src/pvblocks/constants.py +0 -42
- {pvblocks-0.1.0 → pvblocks-0.1.2}/LICENSE +0 -0
- {pvblocks-0.1.0 → pvblocks-0.1.2}/README.md +0 -0
- {pvblocks-0.1.0 → pvblocks-0.1.2}/setup.cfg +0 -0
- {pvblocks-0.1.0 → pvblocks-0.1.2}/src/pvblocks/__main__.py +0 -0
- {pvblocks-0.1.0 → pvblocks-0.1.2}/src/pvblocks/exceptions.py +0 -0
- {pvblocks-0.1.0 → pvblocks-0.1.2}/src/pvblocks.egg-info/dependency_links.txt +0 -0
- {pvblocks-0.1.0 → pvblocks-0.1.2}/src/pvblocks.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
IDLE_COMMAND = 0
|
|
2
|
+
BLINK_COMMAND = 1
|
|
3
|
+
VOLTAGE_COMMAND = 2
|
|
4
|
+
MPP_COMMAND = 3
|
|
5
|
+
READ_COMMAND = 4
|
|
6
|
+
CURVE_COMMAND = 5
|
|
7
|
+
TRANSFER_CURVE_COMMAND = 6
|
|
8
|
+
EXTERNAL_MPP_COMMAND = 7
|
|
9
|
+
TRIGGERED_CURVE_COMMAND = 8
|
|
10
|
+
GET_STATUS = 13
|
|
11
|
+
WRITE_EEPROM_COMMAND = 14
|
|
12
|
+
SET_TRIGGER_COMMAND = 15
|
|
13
|
+
READ_EEPROM_COMMAND = 16
|
|
14
|
+
UPDATE_CONFIG_COMMAND = 17
|
|
15
|
+
GET_CONFIG_COMMAND = 18
|
|
16
|
+
START_FIRMWARE_UPDATE = 19
|
|
17
|
+
ENABLE_FAST_COMMUNICATIONS = 20
|
|
18
|
+
DISABLE_BROADCAST = 21
|
|
19
|
+
TRIGGERED_READ_COMMAND = 50
|
|
20
|
+
ALIVE = 100
|
|
21
|
+
LIST_MODULES = 101
|
|
22
|
+
OPEN_MODULE = 106
|
|
23
|
+
CLOSE_MODULE = 107
|
|
24
|
+
RESET_MODULE = 108
|
|
25
|
+
RESET_CONTROLLER = 109
|
|
26
|
+
TRIGGER_ALL = 110
|
|
27
|
+
BROADCAST_THRESHOLD_EXCEEDED = 111
|
|
28
|
+
CURVE_RUNNING = 250
|
|
29
|
+
|
|
30
|
+
IVMPP_READ_IVPOINT = 20
|
|
31
|
+
IVMPP_APPLY_STATE = 21
|
|
32
|
+
PV_IRR_READ_IRRADIANCES = 30
|
|
33
|
+
|
|
34
|
+
VOC = 0
|
|
35
|
+
ISC = 1
|
|
36
|
+
MPP = 2
|
|
37
|
+
VOLTAGE_BIAS = 3
|
|
38
|
+
|
|
39
|
+
ISC_TO_VOC = 0,
|
|
40
|
+
SWEEP_VOC_TO_ISC = 1
|
|
41
|
+
EXTENT_CURVE_DELAY = 2
|
|
42
|
+
SWEEP_VOC_ISC_VOC = 4
|
|
43
|
+
SWEEP_ISC_VOC_ISC = 8
|
|
@@ -41,12 +41,12 @@ class PvBlocks(object):
|
|
|
41
41
|
self.PvIrrBlocks = []
|
|
42
42
|
|
|
43
43
|
def init_system(self):
|
|
44
|
-
self.uart.write(serial.to_bytes([1, constants.
|
|
44
|
+
self.uart.write(serial.to_bytes([1, constants.ALIVE]))
|
|
45
45
|
sleep(0.5)
|
|
46
46
|
bts = ReadSerial(self.uart)
|
|
47
47
|
if len(bts) != 2:
|
|
48
48
|
raise exceptions.NoResponseException()
|
|
49
|
-
return bts[0] == 3 and bts[1] == constants.
|
|
49
|
+
return bts[0] == 3 and bts[1] == constants.ALIVE
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
def close_system(self):
|
|
@@ -54,11 +54,11 @@ class PvBlocks(object):
|
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
def scan_blocks(self):
|
|
57
|
-
self.uart.write(serial.to_bytes([1, constants.
|
|
57
|
+
self.uart.write(serial.to_bytes([1, constants.LIST_MODULES]))
|
|
58
58
|
sleep(2)
|
|
59
59
|
bts = ReadSerial(self.uart)
|
|
60
60
|
|
|
61
|
-
if (bts[0] != 3) or (bts[1] != constants.
|
|
61
|
+
if (bts[0] != 3) or (bts[1] != constants.LIST_MODULES):
|
|
62
62
|
raise exceptions.UnexpectedResponseException()
|
|
63
63
|
|
|
64
64
|
module_count = bts[3]
|
|
@@ -84,37 +84,10 @@ class PvBlocks(object):
|
|
|
84
84
|
if not self.uart.is_open:
|
|
85
85
|
self.uart.open()
|
|
86
86
|
|
|
87
|
-
self.uart.write(serial.to_bytes([1, constants.
|
|
87
|
+
self.uart.write(serial.to_bytes([1, constants.RESET_CONTROLLER]))
|
|
88
88
|
sleep(3)
|
|
89
89
|
|
|
90
90
|
|
|
91
|
-
def read_irradiances(self, pvblock):
|
|
92
|
-
if self.open_block(pvblock):
|
|
93
|
-
self.uart.write(serial.to_bytes([2, constants.Rr1700Command.ReadCommand]))
|
|
94
|
-
sleep(0.5)
|
|
95
|
-
bts = ReadSerial(self.uart)
|
|
96
|
-
if len(bts) < 10:
|
|
97
|
-
raise exceptions.UnexpectedResponseException()
|
|
98
|
-
|
|
99
|
-
r1 = int.from_bytes(bts[3:7], "little") / 1000.0
|
|
100
|
-
r2 = int.from_bytes(bts[7:11], "little") / 1000.0
|
|
101
|
-
if bts[2] == 16:
|
|
102
|
-
r3 = int.from_bytes(bts[11:15], "little") / 1000.0
|
|
103
|
-
r4 = int.from_bytes(bts[15:19], "little") / 1000.0
|
|
104
|
-
return r1, r2, r3, r4
|
|
105
|
-
else:
|
|
106
|
-
return r1, r2
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
else:
|
|
110
|
-
raise exceptions.CannotOpenBlockException()
|
|
111
|
-
|
|
112
|
-
return (0, 0, 0, 0)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
91
|
class PvBlock(object):
|
|
119
92
|
def __init__(self, bytes, uart):
|
|
120
93
|
self.bytes = bytes[0:8]
|
|
@@ -125,7 +98,7 @@ class PvBlock(object):
|
|
|
125
98
|
|
|
126
99
|
def open(self):
|
|
127
100
|
self.uart.write(serial.to_bytes([1,
|
|
128
|
-
constants.
|
|
101
|
+
constants.OPEN_MODULE,
|
|
129
102
|
0,
|
|
130
103
|
self.bytes[0],
|
|
131
104
|
self.bytes[1],
|
|
@@ -142,7 +115,7 @@ class PvBlock(object):
|
|
|
142
115
|
|
|
143
116
|
def close(self):
|
|
144
117
|
self.uart.write(serial.to_bytes([1,
|
|
145
|
-
constants.
|
|
118
|
+
constants.CLOSE_MODULE,
|
|
146
119
|
self.bytes[0],
|
|
147
120
|
self.bytes[1],
|
|
148
121
|
self.bytes[2],
|
|
@@ -159,7 +132,7 @@ class PvBlock(object):
|
|
|
159
132
|
|
|
160
133
|
def read_statusbyte(self):
|
|
161
134
|
self.open()
|
|
162
|
-
self.uart.write(serial.to_bytes([2, constants.
|
|
135
|
+
self.uart.write(serial.to_bytes([2, constants.GET_STATUS]))
|
|
163
136
|
sleep(0.5)
|
|
164
137
|
bts = ReadSerial(self.uart)
|
|
165
138
|
self.close()
|
|
@@ -177,7 +150,7 @@ class IvMpp(PvBlock):
|
|
|
177
150
|
def read_ivpoint(self):
|
|
178
151
|
|
|
179
152
|
self.open()
|
|
180
|
-
self.uart.write(serial.to_bytes([2, constants.
|
|
153
|
+
self.uart.write(serial.to_bytes([2, constants.READ_COMMAND]))
|
|
181
154
|
sleep(0.5)
|
|
182
155
|
bts = ReadSerial(self.uart)
|
|
183
156
|
self.close()
|
|
@@ -195,14 +168,14 @@ class IvMpp(PvBlock):
|
|
|
195
168
|
|
|
196
169
|
def ApplyVoc(self):
|
|
197
170
|
self.open()
|
|
198
|
-
self.uart.write(serial.to_bytes([2, constants.
|
|
171
|
+
self.uart.write(serial.to_bytes([2, constants.IDLE_COMMAND]))
|
|
199
172
|
sleep(0.5)
|
|
200
173
|
self.close()
|
|
201
174
|
|
|
202
175
|
|
|
203
176
|
def ApplyMpp(self):
|
|
204
177
|
self.open()
|
|
205
|
-
self.uart.write(serial.to_bytes([2, constants.
|
|
178
|
+
self.uart.write(serial.to_bytes([2, constants.MPP_COMMAND]))
|
|
206
179
|
sleep(0.5)
|
|
207
180
|
self.close()
|
|
208
181
|
|
|
@@ -210,7 +183,7 @@ class IvMpp(PvBlock):
|
|
|
210
183
|
voltage = 0.0;
|
|
211
184
|
self.open()
|
|
212
185
|
bytes = list(((int)(1000 * voltage)).to_bytes(4, "little"))
|
|
213
|
-
self.uart.write(serial.to_bytes([2, constants.
|
|
186
|
+
self.uart.write(serial.to_bytes([2, constants.VOLTAGE_COMMAND, bytes[0], bytes[1], bytes[2], bytes[3]]))
|
|
214
187
|
sleep(0.5)
|
|
215
188
|
self.close()
|
|
216
189
|
|
|
@@ -218,7 +191,7 @@ class IvMpp(PvBlock):
|
|
|
218
191
|
self.open()
|
|
219
192
|
bytes = list(((int)(1000 * voltage)).to_bytes(4, "little"))
|
|
220
193
|
self.uart.write(
|
|
221
|
-
serial.to_bytes([2, constants.
|
|
194
|
+
serial.to_bytes([2, constants.VOLTAGE_COMMAND, bytes[0], bytes[1], bytes[2], bytes[3]]))
|
|
222
195
|
sleep(0.5)
|
|
223
196
|
self.close()
|
|
224
197
|
|
|
@@ -226,12 +199,12 @@ class IvMpp(PvBlock):
|
|
|
226
199
|
self.open()
|
|
227
200
|
|
|
228
201
|
self.uart.write(
|
|
229
|
-
serial.to_bytes([2, constants.
|
|
202
|
+
serial.to_bytes([2, constants.SET_TRIGGER_COMMAND, 0]))
|
|
230
203
|
|
|
231
204
|
sleep(0.5)
|
|
232
205
|
|
|
233
206
|
self.uart.write(
|
|
234
|
-
serial.to_bytes([2, constants.
|
|
207
|
+
serial.to_bytes([2, constants.CURVE_COMMAND, points, delay_ms, 0, 0, 0, 0, sweepstyle]))
|
|
235
208
|
|
|
236
209
|
while self.uart.inWaiting() != 3:
|
|
237
210
|
sleep(0.01)
|
|
@@ -244,7 +217,6 @@ class IvMpp(PvBlock):
|
|
|
244
217
|
status = self.read_statusbyte()
|
|
245
218
|
|
|
246
219
|
self.close()
|
|
247
|
-
|
|
248
220
|
points_measured = status.statusbytes[0]
|
|
249
221
|
curve = self.transfer_curve(points_measured)
|
|
250
222
|
|
|
@@ -252,7 +224,7 @@ class IvMpp(PvBlock):
|
|
|
252
224
|
|
|
253
225
|
def transfer_curve(self, points):
|
|
254
226
|
self.open()
|
|
255
|
-
self.uart.write(serial.to_bytes([2, constants.
|
|
227
|
+
self.uart.write(serial.to_bytes([2, constants.TRANSFER_CURVE_COMMAND]))
|
|
256
228
|
sleep(0.5)
|
|
257
229
|
availablebytes = 8 + (points * 8) + 1
|
|
258
230
|
toread = self.uart.inWaiting()
|
|
@@ -266,8 +238,7 @@ class IvMpp(PvBlock):
|
|
|
266
238
|
voltages = []
|
|
267
239
|
currents = []
|
|
268
240
|
|
|
269
|
-
|
|
270
|
-
for i in range(int((availablebytes - 1)/8)):
|
|
241
|
+
for i in range(1, int((availablebytes - 1)/8)):
|
|
271
242
|
index = (i * 8) + 1
|
|
272
243
|
voltages.append(int.from_bytes(bts[index:(index+4)], "little") / 10000.0)
|
|
273
244
|
index = index + 4
|
|
@@ -289,7 +260,7 @@ class IvMpp(PvBlock):
|
|
|
289
260
|
bts = list(address.to_bytes(2, 'little'))
|
|
290
261
|
self.open()
|
|
291
262
|
self.uart.write(
|
|
292
|
-
serial.to_bytes([2, constants.
|
|
263
|
+
serial.to_bytes([2, constants.READ_EEPROM_COMMAND, length, bts[0], bts[1]]))
|
|
293
264
|
|
|
294
265
|
while self.uart.inWaiting() != length + 3:
|
|
295
266
|
sleep(0.01)
|
|
@@ -303,7 +274,7 @@ class PvIrr(PvBlock):
|
|
|
303
274
|
|
|
304
275
|
def ReadIrradiances(self):
|
|
305
276
|
self.open()
|
|
306
|
-
self.uart.write(serial.to_bytes([2, constants.
|
|
277
|
+
self.uart.write(serial.to_bytes([2, constants.READ_COMMAND]))
|
|
307
278
|
sleep(0.5)
|
|
308
279
|
bts = ReadSerial(self.uart)
|
|
309
280
|
if len(bts) < 10:
|
|
@@ -343,10 +314,3 @@ class IvPoint(object):
|
|
|
343
314
|
def __str__(self):
|
|
344
315
|
return "(%f, %f)" % (self.voltage, self.current)
|
|
345
316
|
|
|
346
|
-
|
|
347
|
-
class SweepStyle(IntEnum):
|
|
348
|
-
ISC_TO_VOC = 0,
|
|
349
|
-
SWEEP_VOC_TO_ISC = 1,
|
|
350
|
-
EXTENT_CURVE_DELAY = 2,
|
|
351
|
-
SWEEP_VOC_ISC_VOC = 4,
|
|
352
|
-
SWEEP_ISC_VOC_ISC = 8
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
from pvblocks import pvblocks_system
|
|
2
2
|
from time import sleep
|
|
3
|
+
from pvlib.ivtools.utils import rectify_iv_curve
|
|
4
|
+
import numpy as np
|
|
3
5
|
|
|
4
6
|
print("PV-Blocks version: " + pvblocks_system.show_version())
|
|
5
7
|
|
|
6
8
|
pvblocks = pvblocks_system.PvBlocks('COM16')
|
|
7
|
-
|
|
9
|
+
import matplotlib.pyplot as plt
|
|
8
10
|
|
|
9
11
|
if pvblocks.init_system():
|
|
10
12
|
print("init ok")
|
|
@@ -37,6 +39,17 @@ if len(pvblocks.IvMppBlocks) > 0:
|
|
|
37
39
|
#print(iv_mpp.get_info())
|
|
38
40
|
ivpoint = iv_mpp.read_ivpoint()
|
|
39
41
|
print(ivpoint)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
curve = iv_mpp.measure_ivcurve(100, 20, 0)
|
|
43
|
+
(v, i) = rectify_iv_curve(curve['voltages'], curve['currents'])
|
|
44
|
+
p = v * i
|
|
45
|
+
voc = v[-1]
|
|
46
|
+
isc = i[0]
|
|
47
|
+
index_max = np.argmax(p)
|
|
48
|
+
impp = i[index_max]
|
|
49
|
+
vmpp = v[index_max]
|
|
50
|
+
pmax = p[index_max]
|
|
51
|
+
ff = pmax/(voc * isc)
|
|
52
|
+
pmax = p[index_max]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
pvblocks.close_system()
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import pathlib
|
|
3
|
+
import ttkbootstrap as ttk
|
|
4
|
+
from ttkbootstrap.constants import *
|
|
5
|
+
from ttkbootstrap import utility
|
|
6
|
+
from matplotlib.figure import Figure
|
|
7
|
+
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)
|
|
8
|
+
import serial.tools.list_ports
|
|
9
|
+
import pickle
|
|
10
|
+
import os
|
|
11
|
+
from pvblocks import pvblocks_system
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class MainApp(ttk.Frame):
|
|
15
|
+
|
|
16
|
+
def __init__(self, master):
|
|
17
|
+
super().__init__(master, padding=15)
|
|
18
|
+
self.pack(fill=BOTH, expand=YES)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
self.master = master
|
|
22
|
+
|
|
23
|
+
self.online = False
|
|
24
|
+
self.is_active = False
|
|
25
|
+
self.voltages = []
|
|
26
|
+
self.currents = []
|
|
27
|
+
self.pvblocks = None
|
|
28
|
+
self.iv_mpp = None
|
|
29
|
+
self.load_settings()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
self.fig = Figure((6, 4), dpi=100)
|
|
34
|
+
# application variables
|
|
35
|
+
self.points_var = ttk.StringVar(value=self.settings['points'])
|
|
36
|
+
self.delay_var = ttk.StringVar(value=self.settings['delay_ms'])
|
|
37
|
+
self.mode_var = ttk.StringVar(value='voc')
|
|
38
|
+
self.sweepstyle_var = ttk.StringVar(value=self.settings['sweepstyle'])
|
|
39
|
+
self.ivpoint_var = ttk.StringVar(value='(,)')
|
|
40
|
+
|
|
41
|
+
self.isc_var = ttk.StringVar(value='0.000')
|
|
42
|
+
self.voc_var = ttk.StringVar(value='0.000')
|
|
43
|
+
self.impp_var = ttk.StringVar(value='0.000')
|
|
44
|
+
self.vmpp_var = ttk.StringVar(value='0.000')
|
|
45
|
+
self.ff_var = ttk.StringVar(value='0.000')
|
|
46
|
+
self.power_var = ttk.StringVar(value='0.000')
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
self.create_menu()
|
|
50
|
+
# header and labelframe option container
|
|
51
|
+
option_text = "IV/MPP Load control"
|
|
52
|
+
self.option_lf = ttk.Labelframe(self, text=option_text, padding=15)
|
|
53
|
+
self.option_lf.pack(fill=X, expand=YES, anchor=N)
|
|
54
|
+
|
|
55
|
+
data_text = "Measurement"
|
|
56
|
+
self.data_lf = ttk.Labelframe(self, text=data_text, padding=15)
|
|
57
|
+
self.data_lf.pack(fill=X, expand=YES, anchor=N)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
self.create_mode_row()
|
|
61
|
+
self.create_curveinput_row()
|
|
62
|
+
self.create_ivpoint_row()
|
|
63
|
+
|
|
64
|
+
self.create_graph()
|
|
65
|
+
self.create_parameters_table('Isc:', self.isc_var)
|
|
66
|
+
self.create_parameters_table('Voc:', self.voc_var)
|
|
67
|
+
self.create_parameters_table('Impp:', self.impp_var)
|
|
68
|
+
self.create_parameters_table('Vmpp:', self.vmpp_var)
|
|
69
|
+
self.create_parameters_table('Power:', self.power_var)
|
|
70
|
+
self.create_parameters_table('FF:', self.ff_var)
|
|
71
|
+
self.create_save_button()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
self.draw_empty_figure()
|
|
75
|
+
self.update_controls()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def create_menu(self):
|
|
81
|
+
self.menu = ttk.Menu(self.master)
|
|
82
|
+
self.master.config(menu=self.menu)
|
|
83
|
+
self.connect_menu = ttk.Menu(self.menu)
|
|
84
|
+
self.menu.add_cascade(label='File', menu=self.connect_menu)
|
|
85
|
+
self.connect_menu.add_command(label='Connect...', command=self.connect)
|
|
86
|
+
self.connect_menu.add_command(label='Disconnect...', command=self.disconnect, state='disabled')
|
|
87
|
+
self.connect_menu.add_separator()
|
|
88
|
+
self.connect_menu.add_command(label='Exit', command=self.exit_app)
|
|
89
|
+
self.configmenu = ttk.Menu(self.menu)
|
|
90
|
+
self.menu.add_cascade(label='Configure', menu=self.configmenu)
|
|
91
|
+
self.configmenu.add_command(label='Select serialport', command=self.open_comport_window)
|
|
92
|
+
self.helpmenu = ttk.Menu(self.menu)
|
|
93
|
+
self.menu.add_cascade(label='Help', menu=self.helpmenu)
|
|
94
|
+
self.helpmenu.add_command(label='About')
|
|
95
|
+
|
|
96
|
+
def create_parameters_table(self, caption, variable):
|
|
97
|
+
isc_row = ttk.Frame(self.data_lf)
|
|
98
|
+
isc_row.pack(side=TOP, expand=YES, pady=1)
|
|
99
|
+
lbl = ttk.Label(master=isc_row, text=caption, width=10)
|
|
100
|
+
lbl.pack(side=LEFT, padx=5)
|
|
101
|
+
isc_ent = ttk.Entry(master=isc_row, textvariable=variable)
|
|
102
|
+
isc_ent.pack(side=LEFT, padx=5, fill=X, expand=YES)
|
|
103
|
+
|
|
104
|
+
def create_save_button(self):
|
|
105
|
+
isc_row = ttk.Frame(self.data_lf)
|
|
106
|
+
isc_row.pack(side=TOP, fill=X, pady=1)
|
|
107
|
+
self.save_btn = ttk.Button(
|
|
108
|
+
master=isc_row,
|
|
109
|
+
text="Save data",
|
|
110
|
+
command=self.save_data
|
|
111
|
+
)
|
|
112
|
+
self.save_btn.pack(side=TOP, fill=X, padx=5)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def create_ivpoint_row(self):
|
|
117
|
+
"""Add path row to labelframe"""
|
|
118
|
+
ivpoint_row = ttk.Frame(self.option_lf)
|
|
119
|
+
ivpoint_row.pack(fill=X, expand=YES)
|
|
120
|
+
ivpoint_lbl = ttk.Label(ivpoint_row, text="IV-Point", width=8)
|
|
121
|
+
ivpoint_lbl.pack(side=LEFT, padx=(15, 0))
|
|
122
|
+
ivpoint_ent = ttk.Entry(ivpoint_row, textvariable=self.ivpoint_var)
|
|
123
|
+
ivpoint_ent.pack(side=LEFT, fill=X, expand=YES, padx=5)
|
|
124
|
+
self.measure_ivpoint_btn = ttk.Button(
|
|
125
|
+
master=ivpoint_row,
|
|
126
|
+
text="Measure point",
|
|
127
|
+
command = self.measure_ivpoint,
|
|
128
|
+
width=18
|
|
129
|
+
)
|
|
130
|
+
self.measure_ivpoint_btn.pack(side=LEFT, padx=5)
|
|
131
|
+
|
|
132
|
+
def create_curveinput_row(self):
|
|
133
|
+
"""Add term row to labelframe"""
|
|
134
|
+
term_row = ttk.Frame(self.option_lf)
|
|
135
|
+
term_row.pack(fill=X, expand=YES, pady=15)
|
|
136
|
+
points_lbl = ttk.Label(term_row, text="Points", width=8)
|
|
137
|
+
points_lbl.pack(side=LEFT, padx=(15, 0))
|
|
138
|
+
points_ent = ttk.Entry(term_row, textvariable=self.points_var)
|
|
139
|
+
points_ent.pack(side=LEFT, fill=X, expand=YES, padx=5)
|
|
140
|
+
delay_lbl = ttk.Label(term_row, text="Delay [ms]", width=8)
|
|
141
|
+
delay_lbl.pack(side=LEFT, padx=(15, 0))
|
|
142
|
+
delay_ent = ttk.Entry(term_row, textvariable=self.delay_var)
|
|
143
|
+
delay_ent.pack(side=LEFT, fill=X, expand=YES, padx=5)
|
|
144
|
+
|
|
145
|
+
sweepstyle_ent = ttk.Combobox(term_row, state="readonly", textvariable=self.sweepstyle_var,
|
|
146
|
+
values=("Isc to Voc", "Voc to Isc", "Isc to Voc to Isc", "Voc to Isc to Voc"))
|
|
147
|
+
sweepstyle_ent.pack(side=LEFT, fill=X, expand=YES)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
self.measureCurveBtn = ttk.Button(
|
|
152
|
+
master=term_row,
|
|
153
|
+
text="Measure Curve",
|
|
154
|
+
command = self.system_measure_curve,
|
|
155
|
+
width=18
|
|
156
|
+
)
|
|
157
|
+
self.measureCurveBtn.pack(side=LEFT, padx=5)
|
|
158
|
+
|
|
159
|
+
def create_mode_row(self):
|
|
160
|
+
"""Add type row to labelframe"""
|
|
161
|
+
type_row = ttk.Frame(self.option_lf)
|
|
162
|
+
type_row.pack(fill=X, expand=YES, pady=15)
|
|
163
|
+
type_lbl = ttk.Label(type_row, text="Mode", width=8)
|
|
164
|
+
type_lbl.pack(side=LEFT, padx=(15, 0))
|
|
165
|
+
|
|
166
|
+
voc_opt = ttk.Radiobutton(
|
|
167
|
+
master=type_row,
|
|
168
|
+
text="Voc mode",
|
|
169
|
+
variable=self.mode_var,
|
|
170
|
+
value="voc"
|
|
171
|
+
)
|
|
172
|
+
voc_opt.pack(side=LEFT)
|
|
173
|
+
|
|
174
|
+
isc_opt = ttk.Radiobutton(
|
|
175
|
+
master=type_row,
|
|
176
|
+
text="Isc mode",
|
|
177
|
+
variable=self.mode_var,
|
|
178
|
+
value="isc"
|
|
179
|
+
)
|
|
180
|
+
isc_opt.pack(side=LEFT, padx = 5)
|
|
181
|
+
voc_opt.invoke()
|
|
182
|
+
|
|
183
|
+
self.apply_btn = ttk.Button(
|
|
184
|
+
master=type_row,
|
|
185
|
+
text="Apply",
|
|
186
|
+
command=self.on_apply,
|
|
187
|
+
width=8
|
|
188
|
+
)
|
|
189
|
+
self.apply_btn.pack(side=LEFT, padx=5)
|
|
190
|
+
|
|
191
|
+
def create_graph(self):
|
|
192
|
+
print("Create graph")
|
|
193
|
+
canvas = FigureCanvasTkAgg(self.fig, master=self.data_lf)
|
|
194
|
+
canvas.get_tk_widget().pack(side=LEFT)
|
|
195
|
+
|
|
196
|
+
def on_apply(self):
|
|
197
|
+
print(self.mode_var.get())
|
|
198
|
+
if self.mode_var.get() == 'isc':
|
|
199
|
+
self.iv_mpp.ApplyIsc()
|
|
200
|
+
else:
|
|
201
|
+
self.iv_mpp.ApplyVoc()
|
|
202
|
+
|
|
203
|
+
def system_measure_curve(self):
|
|
204
|
+
|
|
205
|
+
print('measure curve')
|
|
206
|
+
self.save_btn.configure(state=ttk.DISABLED)
|
|
207
|
+
self.is_active = True
|
|
208
|
+
self.update_controls()
|
|
209
|
+
self.voltages = []
|
|
210
|
+
self.currents = []
|
|
211
|
+
self.draw_empty_figure()
|
|
212
|
+
self.master.update()
|
|
213
|
+
self.save_settings()
|
|
214
|
+
|
|
215
|
+
sweepstl = 0
|
|
216
|
+
|
|
217
|
+
if self.sweepstyle_var.get() == 'Isc to Voc':
|
|
218
|
+
sweepstl = 0
|
|
219
|
+
if self.sweepstyle_var.get() == 'Voc to Isc':
|
|
220
|
+
sweepstl = 1
|
|
221
|
+
if self.sweepstyle_var.get() == 'Isc to Voc to Isc':
|
|
222
|
+
sweepstl = 8
|
|
223
|
+
if self.sweepstyle_var.get() == 'Voc to Isc to Voc':
|
|
224
|
+
sweepstl = 4
|
|
225
|
+
|
|
226
|
+
curve = self.iv_mpp.measure_ivcurve(int(self.points_var.get()), int(self.delay_var.get()), sweepstl)
|
|
227
|
+
|
|
228
|
+
self.voltages = curve['voltages']
|
|
229
|
+
self.currents = curve['currents']
|
|
230
|
+
self.refresh_figure()
|
|
231
|
+
self.is_active = False
|
|
232
|
+
self.update_controls()
|
|
233
|
+
self.save_btn.configure(state=ttk.NORMAL)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def measure_ivpoint(self):
|
|
237
|
+
print('measure point')
|
|
238
|
+
self.is_active = True
|
|
239
|
+
self.update_controls()
|
|
240
|
+
ivpoint = self.iv_mpp.read_ivpoint()
|
|
241
|
+
voltage = ivpoint.voltage
|
|
242
|
+
current = ivpoint.current
|
|
243
|
+
self.ivpoint_var.set('Voltage: %f V, Current: %f A, Power: %f W' % (voltage, current, voltage * current))
|
|
244
|
+
self.is_active = False
|
|
245
|
+
self.update_controls()
|
|
246
|
+
pass
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def system_connect(self):
|
|
250
|
+
if self.online:
|
|
251
|
+
self.disconnect()
|
|
252
|
+
else:
|
|
253
|
+
self.connect()
|
|
254
|
+
|
|
255
|
+
def exit_app(self):
|
|
256
|
+
print('exit app')
|
|
257
|
+
if self.online:
|
|
258
|
+
self.disconnect()
|
|
259
|
+
self.master.quit()
|
|
260
|
+
|
|
261
|
+
def connect(self):
|
|
262
|
+
self.online = False
|
|
263
|
+
print('connect pvblocks using: ' + self.settings['serialport'])
|
|
264
|
+
self.pvblocks = pvblocks_system.PvBlocks(self.settings['serialport'])
|
|
265
|
+
if self.pvblocks.init_system():
|
|
266
|
+
print("init ok")
|
|
267
|
+
print("scanning available blocks")
|
|
268
|
+
|
|
269
|
+
if self.pvblocks.scan_blocks():
|
|
270
|
+
print("scan_blocks returns ok")
|
|
271
|
+
self.iv_mpp = None
|
|
272
|
+
if len(self.pvblocks.IvMppBlocks) > 0:
|
|
273
|
+
self.iv_mpp = self.pvblocks.IvMppBlocks[0]
|
|
274
|
+
self.online = True
|
|
275
|
+
print("Found IV-MPP block")
|
|
276
|
+
else:
|
|
277
|
+
print("scan_blocks failed")
|
|
278
|
+
|
|
279
|
+
else:
|
|
280
|
+
print("failed")
|
|
281
|
+
|
|
282
|
+
self.update_controls()
|
|
283
|
+
|
|
284
|
+
def disconnect(self):
|
|
285
|
+
print('disconnect pvblocks')
|
|
286
|
+
self.pvblocks.close_system()
|
|
287
|
+
self.online = False
|
|
288
|
+
self.update_controls()
|
|
289
|
+
|
|
290
|
+
def refresh_figure(self):
|
|
291
|
+
self.fig.clear()
|
|
292
|
+
ax = self.fig.add_subplot(111)
|
|
293
|
+
ax.scatter(self.voltages, self.currents)
|
|
294
|
+
self.fig.canvas.draw_idle()
|
|
295
|
+
|
|
296
|
+
def draw_empty_figure(self):
|
|
297
|
+
y = []
|
|
298
|
+
x = []
|
|
299
|
+
ax = self.fig.add_subplot(111)
|
|
300
|
+
ax.scatter(x, y)
|
|
301
|
+
self.fig.canvas.draw()
|
|
302
|
+
|
|
303
|
+
def save_data(self):
|
|
304
|
+
pass
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def open_comport_window(self):
|
|
308
|
+
# Create a new top-level window
|
|
309
|
+
comport_window = ttk.Toplevel(self.master)
|
|
310
|
+
comport_window.title("Select Serial Port")
|
|
311
|
+
comport_window.geometry("300x150")
|
|
312
|
+
|
|
313
|
+
# Make the window modal
|
|
314
|
+
comport_window.grab_set()
|
|
315
|
+
|
|
316
|
+
# Label instructing the user
|
|
317
|
+
label = ttk.Label(comport_window, text="Select a serial port:")
|
|
318
|
+
label.pack(pady=10)
|
|
319
|
+
|
|
320
|
+
# List of COM ports - customizable
|
|
321
|
+
com_ports = []
|
|
322
|
+
for c in serial.tools.list_ports.comports():
|
|
323
|
+
com_ports.append(c.device)
|
|
324
|
+
|
|
325
|
+
# Variable to hold the selected port
|
|
326
|
+
selected_port = ttk.StringVar()
|
|
327
|
+
|
|
328
|
+
# Combobox for selecting COM port
|
|
329
|
+
combo = ttk.Combobox(comport_window, values=com_ports, textvariable=selected_port)
|
|
330
|
+
combo.pack(pady=5)
|
|
331
|
+
combo.current(0) # Set default selection
|
|
332
|
+
|
|
333
|
+
# Function to handle selection
|
|
334
|
+
def select_port():
|
|
335
|
+
self.settings['serialport'] = selected_port.get()
|
|
336
|
+
self.save_settings()
|
|
337
|
+
comport_window.grab_release()
|
|
338
|
+
comport_window.destroy()
|
|
339
|
+
|
|
340
|
+
# Button to confirm selection
|
|
341
|
+
select_button = ttk.Button(comport_window, text="Select", command=select_port)
|
|
342
|
+
select_button.pack(pady=10)
|
|
343
|
+
|
|
344
|
+
def update_controls(self):
|
|
345
|
+
if self.online:
|
|
346
|
+
self.connect_menu.entryconfig(0, state=ttk.DISABLED)
|
|
347
|
+
self.connect_menu.entryconfig(1, state=ttk.NORMAL)
|
|
348
|
+
else:
|
|
349
|
+
self.connect_menu.entryconfig(0, state=ttk.NORMAL)
|
|
350
|
+
self.connect_menu.entryconfig(1, state=ttk.DISABLED)
|
|
351
|
+
self.save_btn.configure(state=ttk.DISABLED)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
if self.is_active:
|
|
355
|
+
self.measureCurveBtn.configure(state=ttk.DISABLED)
|
|
356
|
+
self.measure_ivpoint_btn.configure(state=ttk.DISABLED)
|
|
357
|
+
self.apply_btn.configure(state=ttk.DISABLED)
|
|
358
|
+
else:
|
|
359
|
+
if self.online:
|
|
360
|
+
self.measureCurveBtn.configure(state=ttk.NORMAL)
|
|
361
|
+
self.measure_ivpoint_btn.configure(state=ttk.NORMAL)
|
|
362
|
+
self.apply_btn.configure(state=ttk.NORMAL)
|
|
363
|
+
else:
|
|
364
|
+
self.measureCurveBtn.configure(state=ttk.DISABLED)
|
|
365
|
+
self.measure_ivpoint_btn.configure(state=ttk.DISABLED)
|
|
366
|
+
self.apply_btn.configure(state=ttk.DISABLED)
|
|
367
|
+
|
|
368
|
+
self.master.update()
|
|
369
|
+
|
|
370
|
+
def save_settings(self):
|
|
371
|
+
self.settings['points'] = self.points_var.get()
|
|
372
|
+
self.settings['delay_ms'] = self.delay_var.get()
|
|
373
|
+
self.settings['sweepstyle'] = self.sweepstyle_var.get()
|
|
374
|
+
with open('settings.pkl', 'wb') as f:
|
|
375
|
+
pickle.dump(self.settings, f)
|
|
376
|
+
|
|
377
|
+
def load_settings(self):
|
|
378
|
+
self.settings = {'serialport': 'COM1', 'points': '100', 'delay_ms': '20', 'sweepstyle': 'Isc to Voc'}
|
|
379
|
+
|
|
380
|
+
if os.path.isfile('settings.pkl'):
|
|
381
|
+
with open('settings.pkl', 'rb') as f:
|
|
382
|
+
self.settings = pickle.load(f)
|
|
383
|
+
else:
|
|
384
|
+
with open('settings.pkl', 'wb') as f:
|
|
385
|
+
pickle.dump(self.settings, f)
|
|
386
|
+
|
|
387
|
+
for key in self.settings.keys():
|
|
388
|
+
print(self.settings[key])
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
if __name__ == '__main__':
|
|
392
|
+
|
|
393
|
+
app = ttk.Window("PVBlocks IV/MPP Control", "journal")
|
|
394
|
+
MainApp(app)
|
|
395
|
+
app.mainloop()
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
from enum import IntEnum
|
|
2
|
-
|
|
3
|
-
class Rr1700Command(IntEnum):
|
|
4
|
-
IdleCommand = 0,
|
|
5
|
-
BlinkCommand = 1,
|
|
6
|
-
VoltageCommand = 2,
|
|
7
|
-
MppCommand = 3,
|
|
8
|
-
ReadCommand = 4,
|
|
9
|
-
CurveCommand = 5,
|
|
10
|
-
TransferCurveCommand = 6,
|
|
11
|
-
ExternalMppCommand = 7,
|
|
12
|
-
TriggeredCurveCommand = 8,
|
|
13
|
-
GetStatus = 13,
|
|
14
|
-
WriteEepromCommand = 14,
|
|
15
|
-
SetTriggerCommand = 15,
|
|
16
|
-
ReadEepromCommand = 16,
|
|
17
|
-
UpdateConfigCommand = 17,
|
|
18
|
-
GetConfigCommand = 18,
|
|
19
|
-
StartFirmwareUpdate = 19,
|
|
20
|
-
EnableFastCommunications = 20,
|
|
21
|
-
DisableBroadcast = 21,
|
|
22
|
-
TriggeredReadCommand = 50,
|
|
23
|
-
Alive = 100,
|
|
24
|
-
ListModules = 101,
|
|
25
|
-
OpenModule = 106,
|
|
26
|
-
CloseModule = 107,
|
|
27
|
-
ResetModule = 108,
|
|
28
|
-
ResetController = 109,
|
|
29
|
-
TriggerAll = 110,
|
|
30
|
-
BroadcastThresholdExceeded = 111,
|
|
31
|
-
CurveRunning = 250
|
|
32
|
-
|
|
33
|
-
class Rr1700Function(IntEnum):
|
|
34
|
-
IvMppReadIVPoint = 20,
|
|
35
|
-
IvMppApplyState = 21,
|
|
36
|
-
PvIrrReadIrradiances = 30
|
|
37
|
-
|
|
38
|
-
class IvMppState(IntEnum):
|
|
39
|
-
Voc = 0,
|
|
40
|
-
Isc = 1,
|
|
41
|
-
Mpp = 2,
|
|
42
|
-
VoltageBias = 3
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|