pvblocks 0.1.6__py3-none-any.whl

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.
pvblocks/__init__.py ADDED
@@ -0,0 +1,2 @@
1
+ VERSION = '0.1.6'
2
+ __version__ = VERSION
pvblocks/__main__.py ADDED
@@ -0,0 +1,6 @@
1
+ import os
2
+ import argparse
3
+
4
+ from datetime import datetime
5
+
6
+ from . import VERSION
pvblocks/constants.py ADDED
@@ -0,0 +1,49 @@
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
+ SELF_RESET_CMD = 24
20
+ TRIGGERED_READ_COMMAND = 50
21
+ ALIVE = 100
22
+ LIST_MODULES = 101
23
+ OPEN_MODULE = 106
24
+ CLOSE_MODULE = 107
25
+ RESET_MODULE = 108
26
+ RESET_CONTROLLER = 109
27
+ TRIGGER_ALL = 110
28
+ BROADCAST_THRESHOLD_EXCEEDED = 111
29
+ ALIVE_RR1701 = 200
30
+ CURVE_RUNNING = 250
31
+ RR1700 = 0
32
+ RR1701 = 1
33
+
34
+
35
+ IVMPP_READ_IVPOINT = 20
36
+ IVMPP_APPLY_STATE = 21
37
+ PV_IRR_READ_IRRADIANCES = 30
38
+
39
+ VOC = 0
40
+ ISC = 1
41
+ MPP = 2
42
+ VOLTAGE_BIAS = 3
43
+
44
+ ISC_TO_VOC = 0
45
+ SWEEP_ISC_TO_VOC = 0
46
+ SWEEP_VOC_TO_ISC = 1
47
+ EXTENT_CURVE_DELAY = 2
48
+ SWEEP_VOC_ISC_VOC = 4
49
+ SWEEP_ISC_VOC_ISC = 8
pvblocks/exceptions.py ADDED
@@ -0,0 +1,36 @@
1
+ class NoResponseException(Exception):
2
+ '''No response from system.'''
3
+ def __str__(self):
4
+ return self.__doc__
5
+
6
+ class UnexpectedResponseException(Exception):
7
+ '''Unexpected response from system.'''
8
+ def __str__(self):
9
+ return self.__doc__
10
+
11
+ class NoReadDataImplementedException(Exception):
12
+ '''No read_data implemented for this blocktype.'''
13
+
14
+ def __str__(self):
15
+ return self.__doc__
16
+
17
+
18
+ class CannotOpenBlockException(Exception):
19
+ '''Cannot open PVBlock.'''
20
+
21
+ def __str__(self):
22
+ return self.__doc__
23
+
24
+
25
+ class MethodNotSupportedException(Exception):
26
+ '''Method not supported for PVBlock.'''
27
+
28
+ def __str__(self):
29
+ return self.__doc__
30
+
31
+
32
+ class PvBlocksIsNoneException(Exception):
33
+ '''PVBlock is None'''
34
+
35
+ def __str__(self):
36
+ return self.__doc__
@@ -0,0 +1,351 @@
1
+ from . import VERSION
2
+ from . import exceptions
3
+ from . import constants
4
+
5
+ import requests
6
+
7
+ EndOfLine = '\r\n'
8
+
9
+ def show_version():
10
+ return VERSION
11
+
12
+ def get_channel_number(usbNr, boardNr, channelNr):
13
+ return channelNr + 1 + (boardNr*8) + usbNr*32
14
+
15
+ def extract_hex_values(guid_string):
16
+ xx = guid_string[6:8]
17
+ yy = guid_string[21:23]
18
+ return int(xx, 16), int(yy, 16)
19
+
20
+
21
+ # (usb nr, board nr, channel nr), with slotnr = None for a temperature sensor
22
+ def GetPosition(guid_string):
23
+ (BlockNr, UsbNr) = extract_hex_values(guid_string)
24
+
25
+ if BlockNr > 100:
26
+ return (UsbNr, int((BlockNr -101)/8), (BlockNr -101)%8)
27
+
28
+ return (UsbNr, BlockNr-64, None)
29
+
30
+ def create_rr1741_sensors(input_array):
31
+ result = []
32
+ for md in input_array:
33
+ sens = md['sensors'][0]
34
+ result.append({'id': sens['id'], 'name': sens['name'], 'description': sens['description']})
35
+ return result
36
+
37
+ def create_rr1727_sensors(input_array):
38
+ result = []
39
+ for sens in input_array:
40
+ result.append({'id': sens['id'], 'name': sens['name'], 'description': sens['description']})
41
+ return result
42
+
43
+
44
+ class PvBlocksApi(object):
45
+ TYPES = {
46
+ 20: 'IV/MPP IV-Curve control and measure PvBlock',
47
+ 27: 'IV/MPP IV-Curve control and measure PvBlock',
48
+ 30: 'PV-IRR 4x analog voltage readout block',
49
+ 40: 'PV-TEMP 4x Pt100 readout block',
50
+ 41: 'PV-TEMP 2x Thermocouple T readout block',
51
+ 50: 'PV-MOD digital modbus module'
52
+ }
53
+
54
+ def __init__(self, host, api_key):
55
+ self.PVBlocksUrl = 'http://' + host
56
+ self.APIkey = api_key
57
+ self.Blocks = []
58
+ self.PvBaseSystemType = constants.RR1700
59
+ self.token = ''
60
+
61
+
62
+ def _url(self, path):
63
+ return self.PVBlocksUrl + '/v1' + path
64
+
65
+ def get_token(self):
66
+ resp = requests.post(self._url('/authentication/Login'), json={'key': self.APIkey})
67
+ if resp.status_code != 200:
68
+ # This means something went wrong.
69
+ raise Exception('POST /authentication/Login {}'.format(resp.status_code))
70
+ else:
71
+ return resp.json()['bearer']
72
+
73
+ def get(self, endpoint, expected_response_code=200, json_response=True):
74
+ resp = requests.get(self._url(endpoint), headers={'Authorization': 'Bearer ' + self.token})
75
+ if resp.status_code != expected_response_code:
76
+ self.token = self.get_token()
77
+ resp = requests.get(
78
+ self._url(endpoint), headers={'Authorization': 'Bearer ' + self.token})
79
+ if resp.status_code != expected_response_code:
80
+ raise Exception('GET /' + endpoint + '{}'.format(resp.status_code))
81
+ else:
82
+ if json_response:
83
+ return resp.json()
84
+ else:
85
+ pass
86
+ def post(self, endpoint, payload, expected_response_code=201, json_response=True):
87
+ resp = requests.post(self._url(endpoint), headers={'Authorization': 'Bearer ' + self.token}, json=payload)
88
+ if resp.status_code != expected_response_code:
89
+ self.token = self.get_token()
90
+ resp = requests.post(
91
+ self._url(endpoint), headers={'Authorization': 'Bearer ' + self.token}, json=payload)
92
+ if resp.status_code != expected_response_code:
93
+ raise Exception('POST /' + endpoint + '{}'.format(resp.status_code))
94
+ else:
95
+ if json_response:
96
+ return resp.json()
97
+ else:
98
+ pass
99
+
100
+ def put(self, endpoint, payload, expected_response_code=204):
101
+ resp = requests.put(self._url(endpoint), headers={'Authorization': 'Bearer ' + self.token}, json=payload)
102
+ if resp.status_code != expected_response_code:
103
+ self.token = self.get_token()
104
+ resp = requests.put(
105
+ self._url(endpoint), headers={'Authorization': 'Bearer ' + self.token}, json=payload)
106
+ if resp.status_code != expected_response_code:
107
+ raise Exception('PUT /' + endpoint + '{}'.format(resp.status_code))
108
+
109
+
110
+ def delete(self, endpoint, expected_response_code=204):
111
+ resp = requests.delete(self._url(endpoint), headers={'Authorization': 'Bearer ' + self.token})
112
+ if resp.status_code != expected_response_code:
113
+ self.token = self.get_token()
114
+ resp = requests.delete(
115
+ self._url(endpoint), headers={'Authorization': 'Bearer ' + self.token})
116
+ if resp.status_code != expected_response_code:
117
+ raise Exception('DEL /' + endpoint + '{}'.format(resp.status_code))
118
+
119
+
120
+
121
+ def get_api_version(self):
122
+ resp = requests.get(self._url('/info'))
123
+ if resp.status_code != 200:
124
+ # This means something went wrong.
125
+ raise Exception('GET /info {}'.format(resp.status_code))
126
+ else:
127
+ return resp.json()['version']
128
+
129
+ def Online(self):
130
+ return self.get_api_version() == 'v1'
131
+
132
+ def Init(self):
133
+ if self.Online():
134
+ count = self.scan_blocks()
135
+ print('Scanned {} blocks'.format(count))
136
+ else:
137
+ print('System not online')
138
+
139
+ def get_pvdevices(self):
140
+ endpoint = '/PvDevice'
141
+ return self.get(endpoint)
142
+
143
+ def create_pvdevice(self, label):
144
+ endpoint = '/PvDevice'
145
+ payload = {
146
+ "Name": label,
147
+ "Serial": "",
148
+ "Manufacturer": "",
149
+ "ManufacturerCode": "",
150
+ "Material": "",
151
+ "IsBiFacial": False,
152
+ "Voc": 0,
153
+ "Isc": 0,
154
+ "Power": 0,
155
+ "Alpha": 0,
156
+ "Beta": 0,
157
+ "TemperatureCoefficient": 0,
158
+ "Area": 0,
159
+ "CellCount": 0,
160
+ "StringCount": 0,
161
+ "TemperatureId": 0,
162
+ "IrradianceId": 0
163
+ }
164
+ return self.post(endpoint, payload)
165
+
166
+ def delete_pvdevice(self, id):
167
+ endpoint = '/PvDevice/{}'.format(id)
168
+ self.delete(endpoint)
169
+
170
+ def get_pvblocks(self):
171
+ endpoint = '/Block'
172
+ return self.get(endpoint)
173
+
174
+ def list_all_unique_identifiers(self):
175
+ blocks = self.get_pvblocks()
176
+ result = []
177
+ for b in blocks:
178
+ result.append((b['uniqueIdentifier']))
179
+ return result
180
+
181
+ def scan_blocks(self):
182
+ blks = self.get_pvblocks()
183
+ module_count = len(blks)
184
+ self.Blocks = []
185
+ for b in blks:
186
+ (usb, board, channel) = GetPosition(b['uniqueIdentifier'])
187
+ if b['type'] == 'RR-1727':
188
+ sensors = create_rr1727_sensors(b['measurementDevices'][0]['sensors'])
189
+ if b['type'] == 'RR-1741':
190
+ sensors = create_rr1741_sensors(b['measurementDevices'])
191
+
192
+ self.Blocks.append({ "label": b["label"], "id": b["id"],"guid": b['uniqueIdentifier'],
193
+ "usbNr": usb, "boardNr": board, "channelNr": channel,
194
+ "type": b['type'], "sensors": sensors, 'commands': b['availableCommands']})
195
+ return module_count
196
+
197
+ def reset_block(self, guid):
198
+ endpoint = '/Hardware/%s/reset' % (guid)
199
+ return self.get(endpoint, expected_response_code=204, json_response=False)
200
+
201
+ def write_block_label(self, id, label):
202
+ endpoint = '/Block/Label/{}'.format(id)
203
+ payload = {'position': 0, 'label': label}
204
+ return self.post(endpoint, payload, expected_response_code=200)
205
+
206
+
207
+ def write_rr1727_default_sweep(self, id, points, integration_cycles, sweepType ):
208
+ endpoint = '/Command/updateIvCurveParameters/%d' % (id)
209
+ payload = {'points': points, 'delay': integration_cycles, 'sweepstyle': sweepType}
210
+ self.post(endpoint, payload, expected_response_code=200, json_response=False)
211
+
212
+
213
+ def read_rr1727_calibration_values(self, guid):
214
+ endpoint = '/Hardware/%s/sendCommand' % (guid)
215
+ payload = {'CommandName': 'ReadFloatEeprom', 'Parameters': {'count': 4, 'address': 4}}
216
+ return self.post(endpoint, payload, expected_response_code=200)['1']
217
+
218
+ def write_rr1727_calibration_values(self, guid, A, B, C, D):
219
+ endpoint = '/Hardware/%s/sendCommand' % (guid)
220
+ payload = {'CommandName': 'WriteFloatEeprom', 'Parameters': {'flt': A, 'address': 4}}
221
+ self.post(endpoint, payload, expected_response_code=200, json_response=False)
222
+ payload = {'CommandName': 'WriteFloatEeprom', 'Parameters': {'flt': B, 'address': 8}}
223
+ self.post(endpoint, payload, expected_response_code=200, json_response=False)
224
+ payload = {'CommandName': 'WriteFloatEeprom', 'Parameters': {'flt': C, 'address': 12}}
225
+ self.post(endpoint, payload, expected_response_code=200, json_response=False)
226
+ payload = {'CommandName': 'WriteFloatEeprom', 'Parameters': {'flt': D, 'address': 16}}
227
+ self.post(endpoint, payload, expected_response_code=200, json_response=False)
228
+ self.reset_block(guid)
229
+
230
+ def get_schedules(self):
231
+ endpoint = '/Pipeline'
232
+ return self.get(endpoint)
233
+
234
+ def create_schedule(self, interval='* * * * *', daylightOnly=False):
235
+ if interval == 1:
236
+ crontab = '* * * * *'
237
+ else:
238
+ crontab ='*/%d * * * *' % (interval)
239
+
240
+ description = 'Execute every %d minutes' % (interval)
241
+ if daylightOnly:
242
+ description += ' during daylight'
243
+
244
+ endpoint = "/Pipeline"
245
+ payload = {'description': description, 'daylightOnly': daylightOnly, 'cronTabs': [crontab]}
246
+ return self.post(endpoint, payload, expected_response_code=201)
247
+
248
+ def delete_schedule(self, id):
249
+ endpoint = '/Pipeline/{}'.format(id)
250
+ self.delete(endpoint)
251
+
252
+ def enable_scheduler(self):
253
+ endpoint = '/Pipeline/enable'
254
+ self.post(endpoint, {}, expected_response_code=204, json_response=False)
255
+
256
+ def disable_scheduler(self):
257
+ endpoint = '/Pipeline/disable'
258
+ self.post(endpoint, {}, expected_response_code=204, json_response=False)
259
+
260
+ def update_sensor_description(self, id, label):
261
+ endpoint = '/Sensor/{}'.format(id)
262
+ original = self.get(endpoint)
263
+ sensor = {'description': label,
264
+ 'enabled': original['enabled'],
265
+ 'unit': original['unit'],
266
+ 'calibration': original['calibration'],
267
+ 'options': original['options'],
268
+ 'name': original['name'] }
269
+ self.put(endpoint, sensor)
270
+
271
+
272
+ def attach_sensor_to_pvdevice(self, sensor_id, pvdevice_id):
273
+ endpoint = '/Sensor/%d/attach/%d' % (sensor_id, pvdevice_id)
274
+ payload = {}
275
+ self.post(endpoint, payload, expected_response_code=201)
276
+
277
+ def add_command_to_schedule(self, schedule_id, blockId, command):
278
+ endpoint = '/Pipeline/%d/command' % (schedule_id)
279
+ payload = { 'pvBlockId': blockId, 'commandName': command['name'], 'parameters': command['defaultParameters'], 'withTrigger': command['defaultWithTrigger']}
280
+ self.post(endpoint, payload, expected_response_code=201)
281
+
282
+ # State definitions:
283
+ # Voc = 0,
284
+ # Isc = 1,
285
+ # Mpp = 2,
286
+ # Vbias = 3
287
+
288
+ def write_rr1727_state(self, guid, state, voltageBias=0, store=True):
289
+ endpoint = '/Hardware/%s/sendCommand' % (guid)
290
+ payload = {'CommandName': 'ApplyState', 'Parameters': {'state': state, 'voltageBias': voltageBias}}
291
+ self.post(endpoint, payload, expected_response_code=200)
292
+ if store:
293
+ endpoint = '/Hardware/%s/storeIvMppState' % (guid)
294
+ payload = {'guid': guid, 'state': state, 'vbias': voltageBias}
295
+ self.put(endpoint, payload, expected_response_code=201)
296
+
297
+ def ApplyVoc(self, guid):
298
+ self.write_rr1727_state(guid, 0, store=False)
299
+
300
+ def ApplyIsc(self, guid):
301
+ self.write_rr1727_state(guid, 1, store=False)
302
+
303
+ def write_rr1727_integration_time(self, guid, integration_time):
304
+ if integration_time not in [1,4,9,15]:
305
+ raise ValueError("Integration time must be in [1,4,9,15]")
306
+
307
+ endpoint = '/Hardware/%s/sendCommand' % (guid)
308
+ payload = {'CommandName': 'WriteEeprom', 'Parameters': {'data': str(integration_time), 'address': 116}}
309
+ self.post(endpoint, payload, expected_response_code=200)
310
+ self.reset_block(guid)
311
+
312
+ def read_rr1727_integration_time(self, guid):
313
+ endpoint = '/Hardware/%s/sendCommand' % (guid)
314
+ payload = {'CommandName': 'ReadEeprom', 'Parameters': {'length': 1, 'address': 116}}
315
+ return self.post(endpoint, payload, expected_response_code=200)['1'][0]
316
+
317
+ def read_rr1727_mpp_values(self, guid):
318
+ endpoint = '/Hardware/%s/sendCommand' % (guid)
319
+ payload = {'CommandName': 'ReadFloatEeprom', 'Parameters': {'count': 4, 'address': 64}}
320
+ return self.post(endpoint, payload, expected_response_code=200)['1']
321
+
322
+ def write_rr1727_mpp_values(self, guid, p1, p2, p3, p4):
323
+ endpoint = '/Hardware/%s/sendCommand' % (guid)
324
+ payload = {'CommandName': 'WriteFloatEeprom', 'Parameters': {'flt': p1, 'address': 64}}
325
+ self.post(endpoint, payload, expected_response_code=200, json_response=False)
326
+ payload = {'CommandName': 'WriteFloatEeprom', 'Parameters': {'flt': p2, 'address': 68}}
327
+ self.post(endpoint, payload, expected_response_code=200, json_response=False)
328
+ payload = {'CommandName': 'WriteFloatEeprom', 'Parameters': {'flt': p3, 'address': 72}}
329
+ self.post(endpoint, payload, expected_response_code=200, json_response=False)
330
+ payload = {'CommandName': 'WriteFloatEeprom', 'Parameters': {'flt': p4, 'address': 76}}
331
+ self.post(endpoint, payload, expected_response_code=200, json_response=False)
332
+ endpoint = '/Hardware/%s/refresh-eeprom' % (guid)
333
+ self.get(endpoint, expected_response_code=204, json_response=False)
334
+
335
+ def read_rr1741_temperatures(self, guid):
336
+ endpoint = '/Hardware/%s/sendCommand' % (guid)
337
+ payload = {'CommandName': 'GetTemperatures', 'Parameters': {'direct': True}}
338
+ result = self.post(endpoint, payload, expected_response_code=200)
339
+ return [result['1']['temperature'], result['2']['temperature']]
340
+
341
+ def read_rr1727_ivpoint(self, guid):
342
+ endpoint = '/Hardware/%s/sendCommand' % (guid)
343
+ payload = {'CommandName': 'MeasureDirectIvPoint'}
344
+ result = self.post(endpoint, payload, expected_response_code=200)
345
+ return [result['1']['ivpoint']['i'], result['1']['ivpoint']['v']]
346
+
347
+ def sweep_rr1727_ivcurve(self, guid, points, integration_cycles, sweepType):
348
+ endpoint = '/Hardware/%s/sendCommand' % (guid)
349
+ payload = {'CommandName': 'StartIvCurve', 'Parameters': {'points': points, 'delay': integration_cycles, 'sweepstyle': sweepType}}
350
+ result = self.post(endpoint, payload, expected_response_code=200)
351
+ return {'Voltages': result['1']['Voltages'], 'Currents': result['1']['Currents']}
@@ -0,0 +1,540 @@
1
+ from . import VERSION
2
+ from . import exceptions
3
+ from . import constants
4
+ import serial
5
+ import uuid
6
+ import struct
7
+ from time import sleep
8
+ from enum import IntEnum
9
+
10
+
11
+
12
+ def show_version():
13
+ return VERSION
14
+
15
+
16
+ def ReadSerial(ser):
17
+ out = []
18
+ while ser.inWaiting() > 0:
19
+ out.append(ser.read(1)[0])
20
+ return out
21
+
22
+
23
+ class PvBlocks(object):
24
+ TYPES = {
25
+ 20: 'IV/MPP IV-Curve control and measure PvBlock',
26
+ 27: 'IV/MPP IV-Curve control and measure PvBlock',
27
+ 30: 'PV-IRR 4x analog voltage readout block',
28
+ 40: 'PV-TEMP 4x Pt100 readout block',
29
+ 41: 'PV-TEMP 2x Thermocouple T readout block',
30
+ 50: 'PV-MOD digital modbus module'
31
+ }
32
+
33
+ def __init__(self, serialport):
34
+ # type: (UART_Adapter) -> None
35
+ self.uart = serial.Serial(serialport,
36
+ baudrate=115200,
37
+ bytesize=serial.EIGHTBITS,
38
+ parity=serial.PARITY_NONE,
39
+ stopbits=serial.STOPBITS_ONE,
40
+ timeout=1)
41
+ self.Blocks = []
42
+ self.IvMppBlocks = []
43
+ self.PvIrrBlocks = []
44
+ self.PvBaseSystemType = constants.RR1700
45
+
46
+ def init_system(self):
47
+ self.uart.write(serial.to_bytes([1, constants.ALIVE]))
48
+ sleep(0.5)
49
+ bts = ReadSerial(self.uart)
50
+ if len(bts) != 2:
51
+ raise exceptions.NoResponseException()
52
+
53
+ if bts[1] == constants.ALIVE_RR1701:
54
+ self.PvBaseSystemType = constants.RR1701
55
+
56
+ return bts[0] == 3 and bts[1] == constants.ALIVE or bts[1] == constants.ALIVE_RR1701
57
+
58
+
59
+ def close_system(self):
60
+ self.uart.close()
61
+
62
+
63
+ def scan_blocks(self):
64
+ self.uart.write(serial.to_bytes([1, constants.LIST_MODULES]))
65
+ sleep(2)
66
+ bts = ReadSerial(self.uart)
67
+
68
+ if (bts[0] != 3) or (bts[1] != constants.LIST_MODULES):
69
+ raise exceptions.UnexpectedResponseException()
70
+
71
+ module_count = bts[3]
72
+ self.Blocks = []
73
+ self.IvMppBlocks = []
74
+ self.PvIrrBlocks = []
75
+ for index in range(module_count):
76
+ match bts[(index * 9) + 4 + 8]:
77
+ case 20:
78
+ blck = IvMpp(bts[(index * 9) + 4: (index * 9) + 13], self.uart)
79
+ self.IvMppBlocks.append(blck)
80
+ case 27:
81
+ blck = IvMpp27(bts[(index * 9) + 4: (index * 9) + 13], self.uart)
82
+ self.IvMppBlocks.append(blck)
83
+ case 30:
84
+ blck = PvIrr(bts[(index * 9) + 4: (index * 9) + 13], self.uart)
85
+ self.PvIrrBlocks.append(blck)
86
+ case _:
87
+ blck = PvBlock(bts[(index * 9) + 4: (index * 9) + 13], self.uart)
88
+ self.Blocks.append(blck)
89
+
90
+
91
+ return module_count > 0
92
+
93
+ def reset_controller(self):
94
+ if not self.uart.is_open:
95
+ self.uart.open()
96
+
97
+ self.uart.write(serial.to_bytes([1, constants.RESET_CONTROLLER]))
98
+ sleep(3)
99
+
100
+
101
+
102
+
103
+ class PvBlock(object):
104
+ def __init__(self, bytes, uart):
105
+ self.bytes = bytes[0:8]
106
+ id = int.from_bytes(bytearray(self.bytes), 'little')
107
+ self.Guid = uuid.UUID(int=id)
108
+ self.Type = bytes[8]
109
+ self.uart = uart
110
+ self.node = 2
111
+
112
+
113
+ def reset_block(self):
114
+ self.uart.write(serial.to_bytes([1,
115
+ constants.RESET_MODULE,
116
+ self.bytes[0],
117
+ self.bytes[1],
118
+ self.bytes[2],
119
+ self.bytes[3],
120
+ self.bytes[4],
121
+ self.bytes[5],
122
+ self.bytes[6],
123
+ self.bytes[7]]))
124
+ sleep(0.5)
125
+
126
+
127
+ def open(self):
128
+ self.uart.write(serial.to_bytes([1,
129
+ constants.OPEN_MODULE,
130
+ 0,
131
+ self.bytes[0],
132
+ self.bytes[1],
133
+ self.bytes[2],
134
+ self.bytes[3],
135
+ self.bytes[4],
136
+ self.bytes[5],
137
+ self.bytes[6],
138
+ self.bytes[7]]))
139
+ sleep(0.5)
140
+ bts = ReadSerial(self.uart)
141
+
142
+ return len(bts) == 3
143
+
144
+ def close(self):
145
+ self.uart.write(serial.to_bytes([1,
146
+ constants.CLOSE_MODULE,
147
+ self.bytes[0],
148
+ self.bytes[1],
149
+ self.bytes[2],
150
+ self.bytes[3],
151
+ self.bytes[4],
152
+ self.bytes[5],
153
+ self.bytes[6],
154
+ self.bytes[7]]))
155
+ sleep(0.5)
156
+ bts = ReadSerial(self.uart)
157
+
158
+ return len(bts) == 3
159
+
160
+
161
+
162
+
163
+
164
+ def read_statusbyte(self):
165
+ self.open()
166
+ self.uart.write(serial.to_bytes([self.node, constants.GET_STATUS]))
167
+ sleep(0.5)
168
+ bts = ReadSerial(self.uart)
169
+ self.close()
170
+ if len(bts) < 10:
171
+ raise exceptions.UnexpectedResponseException()
172
+ return StatusByte(bts)
173
+
174
+ def get_info(self):
175
+ status = self.read_statusbyte()
176
+ d = {'firmware': status.firmware, 'hardware': status.hardware}
177
+ return d
178
+
179
+
180
+
181
+
182
+ class IvMpp(PvBlock):
183
+ def read_ivpoint(self):
184
+
185
+ self.open()
186
+ self.uart.write(serial.to_bytes([2, constants.READ_COMMAND]))
187
+ sleep(0.5)
188
+ bts = ReadSerial(self.uart)
189
+ self.close()
190
+ if len(bts) < 15:
191
+ raise exceptions.UnexpectedResponseException()
192
+
193
+ if bts[2] != 12:
194
+ raise exceptions.UnexpectedResponseException()
195
+
196
+ r1 = int.from_bytes(bts[3:7], "little") / 10000.0
197
+ r2 = int.from_bytes(bts[7:11], "little") / 100000.0
198
+ ivpoint = IvPoint(r1, r2)
199
+
200
+ return ivpoint
201
+
202
+ def ApplyVoc(self):
203
+ self.open()
204
+ self.uart.write(serial.to_bytes([2, constants.IDLE_COMMAND]))
205
+ sleep(0.5)
206
+ self.close()
207
+
208
+
209
+ def ApplyMpp(self):
210
+ self.open()
211
+ self.uart.write(serial.to_bytes([2, constants.MPP_COMMAND]))
212
+ sleep(0.5)
213
+ self.close()
214
+
215
+ def ApplyIsc(self):
216
+ voltage = 0.0;
217
+ self.open()
218
+ bytes = list(((int)(1000 * voltage)).to_bytes(4, "little"))
219
+ self.uart.write(serial.to_bytes([2, constants.VOLTAGE_COMMAND, bytes[0], bytes[1], bytes[2], bytes[3]]))
220
+ sleep(0.5)
221
+ self.close()
222
+
223
+ def ApplyVoltageBias(self, voltage):
224
+ self.open()
225
+ bytes = list(((int)(1000 * voltage)).to_bytes(4, "little"))
226
+ self.uart.write(
227
+ serial.to_bytes([2, constants.VOLTAGE_COMMAND, bytes[0], bytes[1], bytes[2], bytes[3]]))
228
+ sleep(0.5)
229
+ self.close()
230
+
231
+ def measure_ivcurve(self, points, delay_ms, sweepstyle):
232
+ self.open()
233
+
234
+ self.uart.write(
235
+ serial.to_bytes([2, constants.SET_TRIGGER_COMMAND, 0]))
236
+
237
+ sleep(0.5)
238
+
239
+ self.uart.write(
240
+ serial.to_bytes([2, constants.CURVE_COMMAND, points, delay_ms, 0, 0, 0, 0, sweepstyle]))
241
+
242
+ while self.uart.inWaiting() != 3:
243
+ sleep(0.01)
244
+
245
+ bts = ReadSerial(self.uart)
246
+
247
+ status = self.read_statusbyte()
248
+ while status.mode == 5:
249
+ sleep(0.5)
250
+ status = self.read_statusbyte()
251
+
252
+ self.close()
253
+ points_measured = status.statusbytes[0]
254
+ curve = self.transfer_curve(points_measured)
255
+
256
+ return curve
257
+
258
+ def transfer_curve(self, points):
259
+ self.open()
260
+ self.uart.write(serial.to_bytes([2, constants.TRANSFER_CURVE_COMMAND]))
261
+ sleep(0.5)
262
+ availablebytes = 8 + (points * 8) + 1
263
+ toread = self.uart.inWaiting()
264
+ while toread != availablebytes:
265
+ toread = self.uart.inWaiting()
266
+ print(toread)
267
+ sleep(0.1)
268
+ bts = ReadSerial(self.uart)
269
+ self.close()
270
+
271
+ voltages = []
272
+ currents = []
273
+
274
+ for i in range(1, int((availablebytes - 1)/8)):
275
+ index = (i * 8) + 1
276
+ voltages.append(int.from_bytes(bts[index:(index+4)], "little") / 10000.0)
277
+ index = index + 4
278
+ currents.append(int.from_bytes(bts[index:(index+4)], "little") / 100000.0)
279
+
280
+ return {'voltages': voltages, 'currents': currents}
281
+
282
+ def read_calibration(self):
283
+ c = {'A': 0.0, 'B': 0.0, 'C': 0.0, 'D': 0.0}
284
+ bts = self.read_eeprom(4, 16)
285
+ c['A'] = struct.unpack('<f', bytearray(bts[0:4]))
286
+ c['B'] = struct.unpack('<f', bytearray(bts[4:8]))
287
+ c['C'] = struct.unpack('<f', bytearray(bts[8:12]))
288
+ c['D'] = struct.unpack('<f', bytearray(bts[12:16]))
289
+
290
+ return c
291
+
292
+ def read_eeprom(self, address, length):
293
+ bts = list(address.to_bytes(2, 'little'))
294
+ self.open()
295
+ self.uart.write(
296
+ serial.to_bytes([2, constants.READ_EEPROM_COMMAND, length, bts[0], bts[1]]))
297
+
298
+ while self.uart.inWaiting() != length + 3:
299
+ sleep(0.01)
300
+
301
+ bts = ReadSerial(self.uart)
302
+ self.close()
303
+ return bts[3:]
304
+
305
+ def write_voltage_calibration(self, A, B):
306
+ ba = list(bytearray(struct.pack("<f", A)))
307
+ self.write_eeprom(ba, 4)
308
+ ba = list(bytearray(struct.pack("<f", B)))
309
+ self.write_eeprom(ba, 8)
310
+ self.reset_block()
311
+ sleep(3)
312
+
313
+
314
+ def write_current_calibration(self,C, D):
315
+ ba = list(bytearray(struct.pack("<f", C)))
316
+ self.write_eeprom(ba, 12)
317
+ ba = list(bytearray(struct.pack("<f", D)))
318
+ self.write_eeprom(ba, 16)
319
+ self.reset_block()
320
+ sleep(3)
321
+
322
+
323
+ def write_eeprom(self, data, address):
324
+ bts = list(address.to_bytes(self.node, 'little'))
325
+ tx = [self.node, constants.WRITE_EEPROM_COMMAND, len(data), bts[0], bts[1]]
326
+
327
+ self.open()
328
+ self.uart.write(serial.to_bytes(tx + data))
329
+ self.close()
330
+ sleep(0.5)
331
+
332
+
333
+ class IvMpp27(PvBlock):
334
+ def __init__(self, bytes, uart):
335
+ super().__init__(bytes, uart)
336
+ self.node = bytes[0]
337
+
338
+ def reset(self):
339
+ self.open()
340
+ self.uart.write(serial.to_bytes([self.node, constants.SELF_RESET_CMD]))
341
+ sleep(0.5)
342
+ self.close()
343
+
344
+ def read_ivpoint(self):
345
+
346
+ self.open()
347
+ self.uart.write(serial.to_bytes([self.node, constants.READ_COMMAND]))
348
+ sleep(0.5)
349
+ bts = ReadSerial(self.uart)
350
+ self.close()
351
+ if len(bts) < 15:
352
+ raise exceptions.UnexpectedResponseException()
353
+
354
+ if bts[2] != 12:
355
+ raise exceptions.UnexpectedResponseException()
356
+
357
+ r1 = int.from_bytes(bts[3:7], "little") / 10000.0
358
+ r2 = int.from_bytes(bts[7:11], "little") / 100000.0
359
+ ivpoint = IvPoint(r1, r2)
360
+
361
+ return ivpoint
362
+
363
+ def ApplyVoc(self):
364
+ self.open()
365
+ self.uart.write(serial.to_bytes([self.node, constants.IDLE_COMMAND]))
366
+ sleep(0.5)
367
+ self.close()
368
+
369
+
370
+ def ApplyMpp(self):
371
+ self.open()
372
+ self.uart.write(serial.to_bytes([self.node, constants.MPP_COMMAND]))
373
+ sleep(0.5)
374
+ self.close()
375
+
376
+ def ApplyIsc(self):
377
+ voltage = 0.0;
378
+ self.open()
379
+ bytes = list(((int)(1000 * voltage)).to_bytes(4, "little"))
380
+ self.uart.write(serial.to_bytes([self.node, constants.VOLTAGE_COMMAND, bytes[0], bytes[1], bytes[2], bytes[3]]))
381
+ sleep(0.5)
382
+ self.close()
383
+
384
+ def ApplyVoltageBias(self, voltage):
385
+ self.open()
386
+ bytes = list(((int)(1000 * voltage)).to_bytes(4, "little"))
387
+ self.uart.write(
388
+ serial.to_bytes([self.node, constants.VOLTAGE_COMMAND, bytes[0], bytes[1], bytes[2], bytes[3]]))
389
+ sleep(0.5)
390
+ self.close()
391
+
392
+ def measure_ivcurve(self, points, delay_ms, sweepstyle):
393
+ self.open()
394
+
395
+ self.uart.write(
396
+ serial.to_bytes([self.node, constants.SET_TRIGGER_COMMAND, 0]))
397
+
398
+ sleep(0.5)
399
+
400
+ self.uart.write(
401
+ serial.to_bytes([self.node, constants.CURVE_COMMAND, points, delay_ms, 0, 0, 0, 0, sweepstyle]))
402
+
403
+ while self.uart.inWaiting() != 3:
404
+ sleep(0.01)
405
+
406
+ bts = ReadSerial(self.uart)
407
+
408
+ status = self.read_statusbyte()
409
+ while status.mode == 5:
410
+ sleep(0.5)
411
+ status = self.read_statusbyte()
412
+
413
+ self.close()
414
+ points_measured = status.statusbytes[0]
415
+ curve = self.transfer_curve(points_measured)
416
+
417
+ return curve
418
+
419
+ def transfer_curve(self, points):
420
+ self.open()
421
+ self.uart.write(serial.to_bytes([self.node, constants.TRANSFER_CURVE_COMMAND]))
422
+ sleep(0.5)
423
+ availablebytes = 8 + (points * 8) + 1
424
+ toread = self.uart.inWaiting()
425
+ while toread != availablebytes:
426
+ toread = self.uart.inWaiting()
427
+ print(toread)
428
+ sleep(0.1)
429
+ bts = ReadSerial(self.uart)
430
+ self.close()
431
+
432
+ voltages = []
433
+ currents = []
434
+
435
+ for i in range(1, int((availablebytes - 1)/8)):
436
+ index = (i * 8) + 1
437
+ voltages.append(int.from_bytes(bts[index:(index+4)], "little") / 10000.0)
438
+ index = index + 4
439
+ currents.append(int.from_bytes(bts[index:(index+4)], "little") / 100000.0)
440
+
441
+ return {'voltages': voltages, 'currents': currents}
442
+
443
+ def read_calibration(self):
444
+ c = {'A': 0.0, 'B': 0.0, 'C': 0.0, 'D': 0.0}
445
+ bts = self.read_eeprom(4, 16)
446
+
447
+ c['A'] = struct.unpack('<f', bytearray(bts[0:4]))
448
+ c['B'] = struct.unpack('<f', bytearray(bts[4:8]))
449
+ c['C'] = struct.unpack('<f', bytearray(bts[8:12]))
450
+ c['D'] = struct.unpack('<f', bytearray(bts[12:16]))
451
+
452
+ return c
453
+
454
+ def write_voltage_calibration(self, A, B):
455
+ ba = list(bytearray(struct.pack("<f", A)))
456
+ self.write_eeprom(ba, 4)
457
+ ba = list(bytearray(struct.pack("<f", B)))
458
+ self.write_eeprom(ba, 8)
459
+ self.reset()
460
+ sleep(3)
461
+
462
+
463
+ def write_current_calibration(self,C, D):
464
+ ba = list(bytearray(struct.pack("<f", C)))
465
+ self.write_eeprom(ba, 12)
466
+ ba = list(bytearray(struct.pack("<f", D)))
467
+ self.write_eeprom(ba, 16)
468
+ self.reset()
469
+ sleep(3)
470
+
471
+ def read_eeprom(self, address, length):
472
+ bts = list(address.to_bytes(self.node, 'little'))
473
+ self.open()
474
+ self.uart.write(
475
+ serial.to_bytes([self.node, constants.READ_EEPROM_COMMAND, length, bts[0], bts[1]]))
476
+
477
+ while self.uart.inWaiting() != length + 3:
478
+ sleep(0.01)
479
+
480
+ bts = ReadSerial(self.uart)
481
+ self.close()
482
+ return bts[3:]
483
+
484
+ def write_eeprom(self, data, address):
485
+ bts = list(address.to_bytes(self.node, 'little'))
486
+ tx = [self.node, constants.WRITE_EEPROM_COMMAND, len(data), bts[0], bts[1]]
487
+
488
+ self.open()
489
+ self.uart.write(serial.to_bytes(tx + data))
490
+ self.close()
491
+ sleep(0.5)
492
+
493
+
494
+
495
+
496
+
497
+ class PvIrr(PvBlock):
498
+
499
+ def ReadIrradiances(self):
500
+ self.open()
501
+ self.uart.write(serial.to_bytes([2, constants.READ_COMMAND]))
502
+ sleep(0.5)
503
+ bts = ReadSerial(self.uart)
504
+ if len(bts) < 10:
505
+ raise exceptions.UnexpectedResponseException()
506
+
507
+ irradiances = []
508
+
509
+ r1 = int.from_bytes(bts[3:7], "little") / 1000.0
510
+ r2 = int.from_bytes(bts[7:11], "little") / 1000.0
511
+ irradiances.append(r1)
512
+ irradiances.append(r2)
513
+ if bts[2] == 16:
514
+ r3 = int.from_bytes(bts[11:15], "little") / 1000.0
515
+ r4 = int.from_bytes(bts[15:19], "little") / 1000.0
516
+ irradiances.append(r3)
517
+ irradiances.append(r4)
518
+
519
+ self.close()
520
+ return irradiances
521
+
522
+ class StatusByte(object):
523
+ def __init__(self, bytes):
524
+ self.blocktype = bytes[2]
525
+ self.token = bytes[3]
526
+ self.mode = bytes[4]
527
+ self.statusbytes = bytes[5:]
528
+ self.firmware = bytes[9]
529
+ self.hardware = bytes[8]
530
+
531
+
532
+ class IvPoint(object):
533
+ def __init__(self, voltage, current):
534
+ self.voltage = voltage
535
+ self.current = current
536
+ self.power = voltage * current
537
+
538
+ def __str__(self):
539
+ return "(%f, %f)" % (self.voltage, self.current)
540
+
@@ -0,0 +1,21 @@
1
+ Metadata-Version: 2.4
2
+ Name: pvblocks
3
+ Version: 0.1.6
4
+ Summary: Python package to directly control pvblocks modules
5
+ Author-email: Erik Haverkamp <erik@rera.nl>
6
+ Project-URL: Homepage, https://github.com/rerasolutions/pvblocks-python
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.8
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Dynamic: license-file
14
+
15
+ ### pvblocks
16
+
17
+ Package to control pvblocks modules directly
18
+
19
+ requires packages:
20
+
21
+ pyserial
@@ -0,0 +1,13 @@
1
+ test.py,sha256=HEBnuUicKLXo0hPGxfWnqHrRBd4EGFpRdnY7hAZQ9Jw,1576
2
+ test_api.py,sha256=3LTVZCHltluplbRMYdgIU3NBaq13rc9_-mCJbD_91bE,5747
3
+ pvblocks/__init__.py,sha256=deDwZUUNy-NL9BMy8q5Yj1y9Wsp8t_Y_IQ452_W7z_c,42
4
+ pvblocks/__main__.py,sha256=fhvwtf6NzUeUsjP_ARtvxcmYjAvfwqz9_K1bHFwEjUg,84
5
+ pvblocks/constants.py,sha256=E7LsCFWXIRHceIMWbGrGgE0fQqMnKZySIp_RtthU-xM,977
6
+ pvblocks/exceptions.py,sha256=we35JF-svaCDAFYqKFlDCp7ZwbCU0FQe7nksKpUfPnQ,850
7
+ pvblocks/pvblocks_api.py,sha256=XHfmDOrfPoLO05emOmKcTND4fBXy0tyIGc0NPV2eYKM,15113
8
+ pvblocks/pvblocks_system.py,sha256=-CarH4lrseNLvAqgUpWsug6WpAvQ3E8JPVDr2JPOq2M,17076
9
+ pvblocks-0.1.6.dist-info/licenses/LICENSE,sha256=eLQBGuCvsZgLd0xvjckcr-aG-1UB7JzD5VE52GsskPU,1091
10
+ pvblocks-0.1.6.dist-info/METADATA,sha256=-PMGbegokiqmAwPAkdCQGCTl6uWIkCKuYztEUEXMCyQ,593
11
+ pvblocks-0.1.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
12
+ pvblocks-0.1.6.dist-info/top_level.txt,sha256=Y8qcy__xcs9-JbDqIFw7FATtYTlhjqQ6rMpvigmGXAU,23
13
+ pvblocks-0.1.6.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 rerasolutions
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ pvblocks
2
+ test
3
+ test_api
test.py ADDED
@@ -0,0 +1,69 @@
1
+ from pvblocks import pvblocks_system
2
+ from pvlib.ivtools.utils import rectify_iv_curve
3
+ import numpy as np
4
+ import time
5
+ import sys
6
+
7
+ print("PV-Blocks version: " + pvblocks_system.show_version())
8
+
9
+ pvblocks = pvblocks_system.PvBlocks('COM8')
10
+ import matplotlib.pyplot as plt
11
+
12
+ if pvblocks.init_system():
13
+ print("init ok")
14
+ else:
15
+ print("failed")
16
+
17
+ print("scanning available blocks")
18
+
19
+ start_time = time.time()
20
+ if pvblocks.scan_blocks():
21
+ print("ok")
22
+ else:
23
+ print("failed")
24
+ end_time = time.time()
25
+ elapsed_time = end_time - start_time
26
+ print(f"Elapsed Time: {elapsed_time} seconds")
27
+
28
+
29
+ iv_mpp = None
30
+
31
+ for b in pvblocks.Blocks:
32
+ print(pvblocks_system.PvBlocks.TYPES[b.Type])
33
+
34
+ for b in pvblocks.IvMppBlocks:
35
+ print(pvblocks_system.PvBlocks.TYPES[b.Type])
36
+
37
+ for b in pvblocks.PvIrrBlocks:
38
+ print(pvblocks_system.PvBlocks.TYPES[b.Type])
39
+
40
+ if len(pvblocks.IvMppBlocks) > 0:
41
+ iv_mpp = pvblocks.IvMppBlocks[0]
42
+
43
+ #print(iv_mpp.get_info())
44
+ ivpoint = iv_mpp.read_ivpoint()
45
+ print(ivpoint)
46
+
47
+ print(iv_mpp.read_calibration())
48
+
49
+
50
+
51
+ #print('measure iv curve 200 points, 20ms')
52
+ #start_time = time.time()
53
+ #curve = iv_mpp.measure_ivcurve(200, 20, 0)
54
+ #end_time = time.time()
55
+ #elapsed_time = end_time - start_time
56
+ #print(f"Elapsed Time: {elapsed_time} seconds")
57
+ #(v, i) = rectify_iv_curve(curve['voltages'], curve['currents'])
58
+ #p = v * i
59
+ #voc = v[-1]
60
+ #isc = i[0]
61
+ #index_max = np.argmax(p)
62
+ #impp = i[index_max]
63
+ #vmpp = v[index_max]
64
+ #pmax = p[index_max]
65
+ #ff = pmax/(voc * isc)
66
+ #pmax = p[index_max]
67
+
68
+
69
+ pvblocks.close_system()
test_api.py ADDED
@@ -0,0 +1,148 @@
1
+
2
+ host = ''
3
+ apikey = ''
4
+
5
+ from pvblocks import pvblocks_api
6
+ from pvblocks import constants
7
+ print(pvblocks_api.show_version())
8
+ pvblocks = pvblocks_api.PvBlocksApi(host ,apikey)
9
+ pvblocks.Init()
10
+
11
+ def DeleteAllPvDevices():
12
+ for s in pvblocks.get_pvdevices():
13
+ pvblocks.delete_pvdevice(s['id'])
14
+
15
+ def DeleteAllSchedules():
16
+ for s in pvblocks.get_schedules():
17
+ pvblocks.delete_schedule(s['id'])
18
+
19
+ def RecreateSchedules():
20
+ TemperatureScheduleId = pvblocks.create_schedule(1, False)['id']
21
+ IvPointScheduleId = pvblocks.create_schedule(1, False)['id']
22
+ IvCurveScheduleId = pvblocks.create_schedule(5, False)['id']
23
+ return (TemperatureScheduleId, IvPointScheduleId, IvCurveScheduleId)
24
+
25
+ def AssignTemperatureToSchedule(scheduleId):
26
+ for b in pvblocks.Blocks:
27
+ if b['type'] == "RR-1741":
28
+ pvblocks.add_command_to_schedule(scheduleId, b['id'], b['commands'][0])
29
+
30
+ def AssignTIvCurveToSchedule(scheduleId):
31
+ for b in pvblocks.Blocks:
32
+ if b['type'] == "RR-1727":
33
+ for c in b['commands']:
34
+ if c['name'] == 'StartIvCurve':
35
+ pvblocks.add_command_to_schedule(scheduleId, b['id'], c)
36
+
37
+ def AssignTIvPointToSchedule(scheduleId):
38
+ for b in pvblocks.Blocks:
39
+ if b['type'] == "RR-1727":
40
+ for c in b['commands']:
41
+ if c['name'] == 'MeasureIvPoint':
42
+ pvblocks.add_command_to_schedule(scheduleId, b['id'], c)
43
+
44
+ def RecreateBlockLabels():
45
+ for b in pvblocks.Blocks:
46
+ if b['type'] == "RR-1727":
47
+ channel = pvblocks_api.get_channel_number(b['usbNr'], b['boardNr'], b['channelNr'])
48
+ label = "IVMPP-{}".format(channel)
49
+ print(label)
50
+ pvblocks.write_block_label(b['id'], label)
51
+ for s in b['sensors']:
52
+ if s['name'] == 'ivcurve':
53
+ pvblocks.update_sensor_description(s['id'], "ivcurve-{}".format(channel))
54
+ else:
55
+ pvblocks.update_sensor_description(s['id'], "ivpoint-{}".format(channel))
56
+
57
+ if b['type'] == "RR-1741":
58
+ location = 4* b['usbNr'] + b['boardNr'] + 1
59
+ label = "IVTEMP-{}".format(location)
60
+ print(label)
61
+ pvblocks.write_block_label(b['id'], label)
62
+ cnt = 1
63
+ for s in b['sensors']:
64
+ pvblocks.update_sensor_description(s['id'], "TC-{}".format(cnt))
65
+ cnt = cnt + 1
66
+
67
+
68
+ def RecreatePvDevices():
69
+ for b in pvblocks.Blocks:
70
+ if b['type'] == "RR-1727":
71
+ channel = pvblocks_api.get_channel_number(b['usbNr'], b['boardNr'], b['channelNr'])
72
+ label = "PvDevice-{}".format(channel)
73
+ pvblocks.create_pvdevice(label)
74
+
75
+ def RecreatePvDevicesAndAssign():
76
+ board_tc_sensor_ids = {}
77
+ for b in pvblocks.Blocks:
78
+ if b['type'] == "RR-1741":
79
+ board_tc_sensor_ids['boardNr{}'.format(b['boardNr'])] = [b['sensors'][0]['id'], b['sensors'][1]['id']]
80
+
81
+ for b in pvblocks.Blocks:
82
+ if b['type'] == "RR-1727":
83
+ channel = pvblocks_api.get_channel_number(b['usbNr'], b['boardNr'], b['channelNr'])
84
+ label = "PvDevice-{}".format(channel)
85
+ dev = pvblocks.create_pvdevice(label)
86
+ pvblocks.attach_sensor_to_pvdevice(b['sensors'][0]['id'] ,dev['id'])
87
+ pvblocks.attach_sensor_to_pvdevice(b['sensors'][1]['id'], dev['id'])
88
+ tc1_id = board_tc_sensor_ids['boardNr{}'.format(b['boardNr'])][0]
89
+ tc2_id = board_tc_sensor_ids['boardNr{}'.format(b['boardNr'])][1]
90
+ if b['channelNr'] < 4:
91
+ pvblocks.attach_sensor_to_pvdevice(tc1_id, dev['id'])
92
+ else:
93
+ pvblocks.attach_sensor_to_pvdevice(tc2_id, dev['id'])
94
+
95
+
96
+ def SetStateForAllRr1727(state, voltageBias = 0, block_list = None):
97
+ if block_list is None:
98
+ block_list = pvblocks.Blocks
99
+
100
+ for b in block_list:
101
+ if b['type'] == "RR-1727":
102
+ pvblocks.write_rr1727_state(b['guid'], state, voltageBias)
103
+
104
+ def SetSweepParametersForAllRr1727(points, integration_cycles, sweep_type, block_list = None):
105
+ if block_list is None:
106
+ block_list = pvblocks.Blocks
107
+
108
+ for b in block_list:
109
+ if b['type'] == "RR-1727":
110
+ pvblocks.write_rr1727_default_sweep(b['id'], points, integration_cycles, sweep_type)
111
+
112
+ def SetCalibrationValuesForAllRr1727(A, B, C, D, block_list = None):
113
+ if block_list is None:
114
+ block_list = pvblocks.Blocks
115
+
116
+ for b in block_list:
117
+ print(b['label'])
118
+ if b['type'] == "RR-1727":
119
+ pvblocks.write_rr1727_calibration_values(b['guid'], A, B, C, D)
120
+
121
+ def SetMppParametersForAllRr1727(p1, p2, p3, p4, block_list = None):
122
+ if block_list is None:
123
+ block_list = pvblocks.Blocks
124
+
125
+ for b in block_list:
126
+ print(b['label'])
127
+ if b['type'] == "RR-1727":
128
+ pvblocks.write_rr1727_mpp_values(b['guid'], p1, p2, p3, p4)
129
+
130
+ def ShowBlocks(block_list = None):
131
+ if block_list is None:
132
+ block_list = pvblocks.Blocks
133
+
134
+ for b in block_list:
135
+ print(b['label'])
136
+
137
+ # DeleteAllPvDevices()
138
+ # RecreateBlockLabels()
139
+ # RecreatePvDevicesAndAssign()
140
+ # DeleteAllSchedules()
141
+ # (TemperatureScheduleId, IvPointScheduleId, IvCurveScheduleId) = RecreateSchedules()
142
+ # AssignTemperatureToSchedule(TemperatureScheduleId)
143
+ # AssignTIvCurveToSchedule(IvCurveScheduleId)
144
+ # AssignTIvPointToSchedule(IvPointScheduleId)
145
+ # SetStateForAllRr1727(constants.MPP)
146
+ # SetSweepParametersForAllRr1727(200, 4, constants.SWEEP_ISC_TO_VOC)
147
+ # SetCalibrationValuesForAllRr1727(0.125, 0, 10, 0)
148
+ # SetMppParametersForAllRr1727(0.75, 0, 0.01, 100)