def fixed_pf(self, params=None): """ Get/set fixed power factor control settings. Params: Ena - Enabled (True/False) PF - Power Factor set point WinTms - Randomized start time delay in seconds RmpTms - Ramp time in seconds to updated output level RvrtTms - Reversion time in seconds :param params: Dictionary of parameters to be updated. :return: Dictionary of active settings for fixed factor. """ if self.inv is None: der.DERError('DER not initialized') try: if params is not None: pf = params.get('PF') # Configuring Grid Management Services Control with Sunny Explorer # Cos phi (if supported by the device): Read Modbus register 30825. If the value 1075 can be read # from this register, the power factor is specified via system control. if pf is not None: if pf > 0: reg = 1042 # leading else: reg = 1041 # lagging self.inv.write(40025, util.u32_to_data(int(reg))) reg = int(abs(round(pf, 4) * 10000)) self.inv.write(40024, util.u16_to_data(int(reg))) ena = params.get('Ena') if ena is not None: if ena is True: reg = 1075 # 1075 = cos phi, specified by PV system control # reg = 1074 # 1075 = cos phi, direct specific. else: reg = 303 if reg != util.data_to_u32(self.inv.read(40200, 2)): self.inv.write(40200, util.u32_to_data(int(reg))) else: params = {} reg = self.inv.read(40200, 2) if util.data_to_u32(reg) == 1075: params['Ena'] = True else: params['Ena'] = False pf = None params['PF'] = pf except Exception, e: der.DERError(str(e))
def limit_max_power(self, params=None): """ Get/set max active power control settings. Params: Ena - Enabled (True/False) WMaxPct - Active power maximum as percentage of WMax WinTms - Randomized start time delay in seconds RmpTms - Ramp time in seconds to updated output level RvrtTms - Reversion time in seconds :param params: Dictionary of parameters to be updated. :return: Dictionary of active settings for limit max power. """ if self.inv is None: raise der.DERError('DER not initialized') ##### UNTESTED #### try: if params is not None: ena = params.get('Ena') if ena is not None: if ena is True: self.inv.write(40151, util.u32_to_data(802)) else: self.inv.write(40151, util.u32_to_data(803)) power = int(params.get('WMaxPct')) self.inv.write( 40016, util.s16_to_data(int(power)) ) # Active power setpoint P, in % of the maximum active power (PMAX) of the inverter # self.inv.write(40023, util.s16_to_data(int(power))) # Normalized active power limitation by PV system ctrl, in % # self.inv.write(40143, util.s32_to_data(int(power))) # Active power setpoint for the operating mode "Active power limitation P via PV system control" (A) # self.inv.write(40147, util.u32_to_data(int(power))) # Generator active power limitation for the operating mode "Active power limitation P via system control" (A) # self.inv.write(40149, util.s32_to_data(int(power))) # Active power setpoint for the operating mode "Active power limitation P via system control" (W) else: params = {} if util.data_to_u32(self.inv.read(40151, 2)) == 803: params['Ena'] = False else: params['Ena'] = True params['WMaxPct'] = util.data_to_s16(self.inv.read(40016, 1)) # params['WMaxPct'] = util.data_to_s16(self.inv.read(40023, 1)) # params['WMaxPct'] = util.data_to_s32(self.inv.read(40143, 2)) # params['WMaxPct'] = util.data_to_u32(self.inv.read(40147, 2)) # params['WMaxPct'] = util.data_to_s32(self.inv.read(40149, 2)) except Exception, e: raise der.DERError(str(e))
def connect(self, params=None): """ Get/set connect/disconnect function settings. Params: Conn - Connected (True/False) WinTms - Randomized start time delay in seconds RvrtTms - Reversion time in seconds :param params: Dictionary of parameters to be updated. :return: Dictionary of active settings for connect. """ if self.inv is None: raise der.DERError('DER not initialized') try: if params is not None: conn = params.get('Conn') if conn is not None: if conn is True: reg = 1467 # start else: reg = 1749 # Full stop (AC and DC side) # reg = 381 # Stop (AC side) self.inv.write(40018, util.u32_to_data(int(reg))) else: params = {} reg = self.inv.read(40018, 2) if util.data_to_u32(reg) == 1467: params['Conn'] = True else: params['Conn'] = False except Exception, e: raise der.DERError(str(e))
def gridguard(self, new_gg=None): """ Read/Write SMA Grid Guard. Params: Grid Guard :return: 0 or 1 for GG off or on. """ data = self.inv.read(43090, 2) gg = util.data_to_u32(data) if int(gg) == 1: self.ts.log("Grid Guard is already set") return True if new_gg is not None: self.ts.log('Writing new Grid Guard: %d' % new_gg) self.inv.write(43090, util.u32_to_data(int(new_gg))) self.ts.sleep(1) data = self.inv.read(43090, 2) gg = util.data_to_u32(data) self.ts.log("gg %s" % gg) if gg == 0: print('Grid guard was not enabled') return False else: print('Grid guard was enabled') return True
def test_run(): result = script.RESULT_FAIL gsim = None ts.log_debug(' ') ts.log_debug( 'Note: Please remove the Grid Guard Code from the test configuration after ' 'it has been to run to avoid exposing your Grid Guard Code to others.') ts.log_debug(' ') try: ipaddr = ts.param_value('gg.ipaddr') new_gg = ts.param_value('gg.code') # register = ts.param_value('gg.register') # port = ts.param_value('gg.port') # comm_id = ts.param_value('gg.id') register = 43090 port = 502 comm_id = 3 if not new_gg or new_gg == 'None': raise script.ScriptFail('No Grid Guard Code specified.') device = client.ModbusClientDeviceTCP(comm_id, ipaddr, port) data = device.read(register, 2) gg = util.data_to_u32(data) ts.log('Current grid guard code: %s' % hex(gg)) if gg == 0: ts.log('Original grid guard was not enabled') else: ts.log('Original grid guard was enabled') device.write(register, util.u32_to_data(int(new_gg))) data = device.read(register, 2) gg = util.data_to_u32(data) ts.log('Updated grid guard code: = %s' % hex(gg)) if gg == 0: ts.log_warning('Current grid guard is not enabled') result = script.RESULT_FAIL else: ts.log('Current grid guard is enabled') result = script.RESULT_PASS except script.ScriptFail, e: reason = str(e) if reason: ts.log_error(reason)
def gridguard(self, new_gg=None): """ Read/Write SMA Grid Guard. Params: Grid Guard :return: 0 or 1 for GG off or on. """ if new_gg is not None: print('Writing new Grid Guard: %d' % new_gg) self.inv.write(43090, util.u32_to_data(int(new_gg))) data = self.inv.read(43090, 2) gg = util.data_to_u32(data) if gg == 0: print('Grid guard was not enabled') return False else: print('Grid guard was enabled') return True
def writeAdvancedPwrControlEn(control=True): if control: device.write(0xf142, util.u32_to_data(1)) # write power factor else: device.write(0xf142, util.u32_to_data(0)) # write power factor return readAdvancedPwrControlEn()
def active_power(self, params=None): """ Get/set active power of EUT Params: Ena - Enabled (True/False) P - Active power in %Wmax (positive is exporting (discharging), negative is importing (charging) power) WinTms - Randomized start time delay in seconds RmpTms - Ramp time in seconds to updated output level RvrtTms - Reversion time in seconds :param params: Dictionary of parameters to be updated. :return: Dictionary of active settings for HFRT control. """ if self.inv is None: raise der.DERError('DER not initialized') try: if params is not None: ena = params.get('Ena') if ena is not None: if ena is True: # self.inv.write(40151, util.u32_to_data(802)) self.inv.write(40210, util.u32_to_data(1078)) else: # self.inv.write(40151, util.u32_to_data(803)) self.inv.write(40210, util.u32_to_data(303)) # Operating mode of active power limitation # 303 = Off # 1077 = Active power limitation P in W # 1078 = Act. power lim. as % of Pmax # 1079 = Act. power lim. via PV system ctrl # 1390 = Active power limitation P via analogue input # 1391 = Active power limitation P via digital inputs power = int(params.get('P')) # self.inv.write(40016, util.s16_to_data(int(power))) # Active power setpoint P, in % of the maximum active power (PMAX) of the inverter # self.inv.write(40023, util.s16_to_data(int(power))) # Normalized active power limitation by PV system ctrl, in % # self.inv.write(40143, util.s32_to_data(int(power))) # Active power setpoint for the operating mode "Active power limitation P via PV system control" (A) # self.inv.write(40147, util.u32_to_data(int(power))) # Generator active power limitation for the operating mode "Active power limitation P via system control" (A) # self.inv.write(40149, util.s32_to_data(int(power))) # Active power setpoint for the operating mode "Active power limitation P via system control" (W) # self.inv.write(40212, util.u32_to_data(int(power))) # Active power setpoint (W) self.inv.write(40214, util.u32_to_data( int(power))) # Active power setpoint (%) else: params = {} # enabled = util.data_to_u32(self.inv.read(40151, 1)) == 803 enabled = util.data_to_u32(self.inv.read(40210, 1)) == 1078 if enabled: params['Ena'] = False else: params['Ena'] = True # params['P'] = util.data_to_s16(self.inv.read(40016, 1)) # params['P'] = util.data_to_s16(self.inv.read(40023, 1)) # params['P'] = util.data_to_s32(self.inv.read(40143, 2)) # params['P'] = util.data_to_u32(self.inv.read(40147, 2)) # params['P'] = util.data_to_s32(self.inv.read(40149, 2)) params['P'] = util.data_to_u32(self.inv.read(40214, 2)) except Exception, e: raise der.DERError(str(e))
def reactive_power(self, params=None): """ Set the reactive power Params: Ena - Enabled (True/False) Q - Reactive power as %Qmax (positive is overexcited, negative is underexcited) WinTms - Randomized start time delay in seconds RmpTms - Ramp time in seconds to updated output level RvrtTms - Reversion time in seconds :param params: Dictionary of parameters to be updated. :return: Dictionary of active settings for Q control. """ # reactive_power_dept_ref = { # 'None': 0, # 'WMax': 1, # 'VArMax': 2, # 'VArAval': 3, # 0: 'None', # 1: 'WMax', # 2: 'VArMax', # 3: 'VArAval' # } if self.inv is None: raise der.DERError('DER not initialized') ##### UNTESTED #### try: if params is not None: ena = params.get('Ena') if ena is not None: if ena is True: # self.inv.write(40151, util.u32_to_data(802)) self.inv.write(40200, util.u32_to_data(1070)) else: # self.inv.write(40151, util.u32_to_data(803)) self.inv.write(40200, util.u32_to_data(303)) #wanbin var_pct_mod = params.get('VArPct_Mod') if var_pct_mod is not None: if var_pct_mod == 'WMax': var_w_max_pct = int(params.get('VArWMaxPct')) self.inv.write(40015, util.s16_to_data(int(var_w_max_pct))) # self.inv.write(40153, util.s32_to_data(int(var_w_max_pct))) else: raise der.DERError( 'DER reactive power mode not supported') else: params = {} # enabled = util.data_to_u32(self.inv.read(40151, 1)) == 803 enabled = util.data_to_u32(self.inv.read( 40200, 2)) == 1070 # Reactive power Q, direct spec. # enabled = util.data_to_u32(self.inv.read(40200, 2)) == 1071 # React. power const. Q in kvar if enabled: params['Ena'] = False else: params['Ena'] = True params['VArPct_Mod'] = 'WMax' params['VArWMaxPct'] = util.data_to_s16(self.inv.read( 40015, 1)) # params['VArWMaxPct'] = util.data_to_s32(self.inv.read(40153, 2)) # params['VArWMaxPct'] = util.data_to_s32(self.inv.read(40202, 2)) # Reactive power setpoint (VAr) params['VArWMaxPct'] = util.data_to_s32( self.inv.read(40204, 2)) # Reactive power setpoint (%) except Exception, e: raise der.DERError(str(e))
def volt_var_curve(self, id=1, params=None): """ Get/set volt/var curve v [] - List of voltage curve points var [] - List of var curve points based on DeptRef DeptRef - Dependent reference type: 'VAR_MAX_PCT', 'VAR_AVAL_PCT', 'VA_MAX_PCT', 'W_MAX_PCT' RmpTms - Ramp timer RmpDecTmm - Ramp decrement timer RmpIncTmm - Ramp increment timer :param params: Dictionary of parameters to be updated. :return: Dictionary of active settings for volt/var curve control. """ if self.inv is None: raise der.DERError('DER not initialized') #x1 = range(40282, 40306, 2) # X values 1 to 12 of the characteristic curve 1 #y1 = range(40306, 40330, 2) # Y values 1 to 12 of the characteristic curve 1 x1 = [41077, 41081, 41085, 41089] #wanbin y1 = [41079, 41083, 41087, 41091] #wanbin ''' x2 = range(40330, 40354, 2) # X values 1 to 12 of the characteristic curve 2 y2 = range(40354, 40378, 2) # Y values 1 to 12 of the characteristic curve 2 x3 = range(40378, 40402, 2) # X values 1 to 12 of the characteristic curve 3 y3 = range(40402, 40426, 2) # Y values 1 to 12 of the characteristic curve 3 ''' volt_var_dept_ref = { 'W_MAX_PCT': 1, 'VAR_MAX_PCT': 2, 'VAR_AVAL_PCT': 3, 1: 'W_MAX_PCT', 2: 'VAR_MAX_PCT', 3: 'VAR_AVAL_PCT' } try: if int(id) > 3: raise der.DERError('Curve id out of range: %s' % id) if params is not None: # self.ts.log_debug('Writing VV Curve to SMA....') # set voltage points v = params.get('v') if v is not None: v_len = len(v) # if v_len > n_pt: # raise der.DERError('Voltage point count out of range: %d' % (v_len)) for i in xrange(v_len): # SunSpec point index starts at 1 if id == 1: v_val = int( util.data_to_s32(self.inv.read(x1[i], 2))) # read self.ts.log_debug('Voltage point %s is %s' % (i, v_val)) if int(v_val) == int(round(v[i], 3) * 1000): self.ts.log_debug( 'V points : Skip Writing: Same setting') else: self.ts.log_debug( 'Writing v point %s to reg %s with value %s' % (i, x1[i], v[i])) self.inv.write( x1[i], util.s32_to_data(int( round(v[i], 3) * 1000))) self.ts.log_debug('Wrote V points') ''' elif id == 2: self.inv.write(x2[i], util.s32_to_data(int(round(v[i], 3)*1000))) else: self.inv.write(x3[i], util.s32_to_data(int(round(v[i], 3)*1000))) ''' # set var points var = params.get('var') if var is not None: var_len = len(var) # if var_len > n_pt: # raise der.DERError('VAr point count out of range: %d' % (var_len)) self.inv.write(41075, util.u32_to_data( 1977)) # Var in percentages of Pmax #wanbin for i in xrange( var_len): # SunSpec point index starts at 1 if id == 1: var_val = int( util.data_to_s32(self.inv.read(y1[i], 2))) # read if int(var_val) == int(round(var[i], 3) * 1000): self.ts.log_debug( 'VAR : Skip Writing: Same Setting') else: self.inv.write( y1[i], util.s32_to_data( int(round(var[i], 3) * 1000))) self.ts.log_debug('Wrote Var points') ''' elif id == 2: self.inv.write(y2[i], util.s32_to_data(int(round(var[i], 3)*1000))) self.inv.write(40979, util.u32_to_data(1977)) # Var in percentages of Pmax else: self.inv.write(y3[i], util.s32_to_data(int(round(var[i], 3)*1000))) self.inv.write(40981, util.u32_to_data(1977)) # Var in percentages of Pmax ''' else: self.ts.log_debug('Reading VV curve in SMA') params = {} v = [] var = [] if id == 1: n_pt = int(util.data_to_u32(self.inv.read(41071, 2))) #wanbin for i in xrange(int(4)): self.ts.log('Getting V%s' % i) v.append( util.data_to_s32(self.inv.read(x1[i], 2)) / 1000.) self.ts.log('Getting Q%s' % i) var.append( util.data_to_s32(self.inv.read(y1[i], 2)) / 1000.) ''' elif id == 2: n_pt = int(util.data_to_u32(self.inv.read(40264, 2))) self.ts.log_debug('n_pt %s' % n_pt) if n_pt < 1 or n_pt > 12: raise der.DERError('Unsupported number of VV points. n_pt: %s' % n_pt) for i in xrange(int(n_pt)): v.append(util.data_to_s32(self.inv.read(x2[i], 2))/1000) var.append(util.data_to_s32(self.inv.read(y2[i], 2))/1000) else: n_pt = int(util.data_to_u32(self.inv.read(40266, 2))) if n_pt < 1 or n_pt > 12: raise der.DERError('Unsupported number of VV points. n_pt: %s' % n_pt) for i in xrange(int(n_pt)): v.append(util.data_to_s32(self.inv.read(x3[i], 2))/1000) var.append(util.data_to_s32(self.inv.read(y3[i], 2))/1000) ''' dept_ref = volt_var_dept_ref.get(1) # 'W_MAX_PCT' params['DeptRef'] = dept_ref params['id'] = id # also store the curve number params['v'] = v params['var'] = var except Exception, e: raise der.DERError(str(e))
def volt_var(self, params=None): """ Get/set volt/var control Params: Ena - Enabled (True/False) ActCrv - Active curve number (0 - no active curve) NCrv - Number of curves supported NPt - Number of points supported per curve WinTms - Randomized start time delay in seconds RmpTms - Ramp time in seconds to updated output level RvrtTms - Reversion time in seconds :param params: Dictionary of parameters to be updated. :return: Dictionary of active settings for volt/var control. """ if self.inv is None: raise der.DERError('DER not initialized') try: if params is not None: curve = params.get( 'curve' ) ## Must write curve first because there is a read() in volt_var_curve act_crv = params.get('ActCrv') if curve is not None: self.volt_var_curve(id=act_crv, params=curve) ena = params.get('Ena') if ena is not None: if ena is True: reg = 2269 # React. power/volt. char. Q(U) #wanbin else: reg = 303 if reg != util.data_to_u32(self.inv.read(40200, 2)): self.inv.write(40200, util.u32_to_data(int(reg))) # Activation of the characteristic curve, configuration of characteristic curve mode if ena is True: reg = 308 # on else: reg = 303 # off if reg != util.data_to_u32(self.inv.read(41063, 2)): self.inv.write(41063, util.u32_to_data( reg)) # Curve 1, 303 = off, 308 = on if int(util.data_to_u32(self.inv.read(41061, 2))) != 2: self.inv.write(41061, util.u32_to_data( 2)) # Curve 1, 303 = off, 308 = on ''' if act_crv == 1: if reg != util.data_to_u32(self.inv.read(40937, 2)): self.inv.write(40937, util.u32_to_data(reg)) # Curve 1, 303 = off, 308 = on if act_crv == 2: if reg != util.data_to_u32(self.inv.read(40939, 2)): self.inv.write(40939, util.u32_to_data(reg)) # Curve 2, 303 = off, 308 = on if act_crv == 3: if reg != util.data_to_u32(self.inv.read(40941, 2)): self.inv.write(40941, util.u32_to_data(reg)) # Curve 3, 303 = off, 308 = on ''' ''' if act_crv is not None: # Characteristic curve number, configuration of the active power/voltage # characteristic curve P(V). 0 = function is switched off. if act_crv in [1, 2, 3]: if act_crv != util.data_to_u32(self.inv.read(40260, 2)): self.inv.write(40260, util.u32_to_data(act_crv)) else: raise der.DERError('Unsupported characteristic curve number.') else: # Q(U) programmed into curve 2 by default act_crv = 2 if act_crv != util.data_to_u32(self.inv.read(40260, 2)): self.inv.write(40260, util.u32_to_data(act_crv)) ''' else: params = {} self.ts.sleep(1) reg = self.inv.read(40200, 2) if int(util.data_to_u32(reg)) == 2269: params['Ena'] = True else: params['Ena'] = False self.ts.log(util.data_to_u32(reg)) params['ActCrv'] = 2 ''' if util.data_to_u32(self.inv.read(40937, 2)) == 303: if util.data_to_u32(self.inv.read(40977, 2)) == 1977 and \ util.data_to_u32(self.inv.read(40957, 2)) == 1976: # Voltage in %Vnom and Var in %Pmax params['ActCrv'] = 1 elif util.data_to_u32(self.inv.read(40937, 2)) == 303: if util.data_to_u32(self.inv.read(40979, 2)) == 1977 and \ util.data_to_u32(self.inv.read(40959, 2)) == 1976: # Voltage in %Vnom and Var in %Pmax params['ActCrv'] = 2 elif util.data_to_u32(self.inv.read(40937, 2)) == 303: if util.data_to_u32(self.inv.read(40981, 2)) == 1977 and \ util.data_to_u32(self.inv.read(40961, 2)) == 1976: # Voltage in %Vnom and Var in %Pmax params['ActCrv'] = 3 else: params['ActCrv'] = None params['NCrv'] = 3 # SMA supports 3 curves if params['ActCrv'] is not None: params['curve'] = self.volt_var_curve(id=params['ActCrv']) ''' except Exception, e: der.DERError(str(e))
def test_u32_to_data(self): self.assertEqual(util.u32_to_data(int(305419896)), b'\x12\x34\x56\x78') self.assertEqual(util.u32_to_data(int(2452903544)), b'\x92\x34\x56\x78')