153 lines
6.8 KiB
Python
153 lines
6.8 KiB
Python
from math import ceil
|
|
|
|
from helix.calculators.bom_helper import add_parts_to_list
|
|
from helix.constants import ebom_parts
|
|
from helix.constants.ebom_parts import *
|
|
from helix.constants.parts import wire_clip_large, cable_support, cable_support_lid, channel_nut, sunshade
|
|
from helix.constants.system_type import SystemType
|
|
from helix.constants.inverter_brand import InverterBrand
|
|
from helix.forms.ebom_form import InverterBrandForm
|
|
|
|
|
|
class EbomCalculator(object):
|
|
def __init__(self, user_values, row_count, column_count, modules_count = None):
|
|
self.values = user_values
|
|
self.row_count = row_count
|
|
self.column_count = column_count
|
|
self.modules_count = modules_count
|
|
|
|
def resolve_power_monitor_type(self):
|
|
module_type = self.values.module_type()
|
|
|
|
thresholds = {
|
|
ModuleType.Cell96: 306,
|
|
ModuleType.Cell128: 230,
|
|
ModuleType.PSeries: 286
|
|
}
|
|
|
|
if (not self.modules_count) or self.modules_count >= thresholds[module_type]:
|
|
return monitor_controller_480_v
|
|
else:
|
|
return monitor_controller_240_v
|
|
|
|
def compute_ebom(self):
|
|
part_list = {}
|
|
|
|
power_stations = self.values.power_stations()
|
|
standalone_inverters = self.values.standalone_inverters()
|
|
monitors = self.values.power_monitors()
|
|
module_type = self.values.module_type()
|
|
system_type = self.values.system_type()
|
|
|
|
is_delta=None
|
|
try:
|
|
is_delta=(self.values.inverter_brands()[0]['inverter_brand_id']==InverterBrand.DELTA.value)
|
|
except IndexError :
|
|
#Some tests are calculating bom without providing inverter brand so inverter_brands is empty
|
|
#for those tests, inverter brand is irrelevant
|
|
is_delta=False
|
|
|
|
inverter_count = 0
|
|
total_ac_run_length = 0
|
|
panel_board_counts = [0, 0]
|
|
proper_monitor_controller = self.resolve_power_monitor_type()
|
|
|
|
try:
|
|
is_delta = (self.values.inverter_brands()[0]['inverter_brand_id']==InverterBrand.DELTA.value)
|
|
except IndexError:
|
|
is_delta = False
|
|
|
|
for power_station in power_stations:
|
|
power_station_count = power_station['power_station_quantity']
|
|
total_ac_run_length += power_station['ac_run_length']
|
|
inverter_quantity = power_station['inverter_quantity'] + self.get_standalone_inverters(power_station)
|
|
if inverter_quantity <= 2:
|
|
panel_board_counts[0] += power_station_count
|
|
else:
|
|
panel_board_counts[1] += power_station_count
|
|
|
|
if self.power_station_has_monitor(power_station, monitors):
|
|
panel_board_parts_to_use = panel_board_parts_with_monitor(inverter_quantity, proper_monitor_controller)
|
|
else:
|
|
panel_board_parts_to_use = panel_board_parts(inverter_quantity, with_aux=False)
|
|
add_parts_to_list(part_list, panel_board_parts_to_use, power_station_count)
|
|
add_parts_to_list(part_list, shared_panel_board_parts(module_type, system_type), power_station_count)
|
|
add_parts_to_list(part_list, {channel_nut: 4}, power_station_count)
|
|
|
|
for inverter in power_station['inverters']:
|
|
inverter_count += power_station_count
|
|
self.add_parts_for_inverter(part_list, inverter, power_station_count)
|
|
add_parts_to_list(part_list, inverter_parts(inverter, module_type), power_station_count)
|
|
|
|
for inverter in standalone_inverters:
|
|
inverter_count += 1
|
|
total_ac_run_length += inverter['ac_run_length']
|
|
self.add_parts_for_inverter(part_list, inverter)
|
|
add_parts_to_list(part_list, standalone_inverter_parts(inverter, system_type, module_type), 1)
|
|
add_parts_to_list(part_list, inverter_parts(inverter, module_type), 1)
|
|
if inverter['attachment_point'][1]:
|
|
add_parts_to_list(part_list, standalone_inverter_attached_to_panel_board_parts, 1)
|
|
|
|
for monitor in monitors:
|
|
if monitor['power_source'][0] == 'Switch Gear/External':
|
|
add_parts_to_list(part_list, {proper_monitor_controller: 1}, 1)
|
|
if (is_delta):
|
|
add_parts_to_list(part_list, {ethernet_plug: 2},1)
|
|
|
|
add_parts_to_list(part_list, {wire_clip_large: inverter_count}, self.row_count)
|
|
|
|
add_parts_to_list(part_list, {stump: 1}, ceil(total_ac_run_length / 4.0))
|
|
cable_supports = self.calculate_cable_supports(panel_board_counts, len(standalone_inverters))
|
|
add_parts_to_list(part_list, {cable_support: 1, cable_support_lid: 1}, cable_supports)
|
|
add_parts_to_list(part_list, {rear_skirt: -1}, ceil(cable_supports*.38))
|
|
|
|
dependent_part_list = {}
|
|
|
|
for part, quantity in part_list.items():
|
|
dependent_parts = ebom_parts.dependent_parts(module_type, system_type,is_delta).get(part)
|
|
if dependent_parts:
|
|
add_parts_to_list(dependent_part_list, dependent_parts, quantity)
|
|
|
|
add_parts_to_list(part_list, dependent_part_list)
|
|
|
|
return part_list
|
|
|
|
def add_parts_for_inverter(self, part_list, inverter, multiplier=1):
|
|
strings_per_inverter = inverter_strings_parts.get(inverter['strings_per_inverter'], {})
|
|
add_parts_to_list(part_list, inverter_model_parts[inverter['model']], multiplier)
|
|
add_parts_to_list(part_list, strings_per_inverter, multiplier)
|
|
if inverter['sunshade']:
|
|
add_parts_to_list(part_list, {sunshade: 1, sunshade_bolt: 2, sunshade_washer: 2}, multiplier)
|
|
if inverter['dc_switch']:
|
|
add_parts_to_list(part_list, dc_switch_parts, multiplier)
|
|
|
|
def calculate_cable_supports(self, panel_board_counts, standalone_inverter_count):
|
|
if sum(panel_board_counts) == 0:
|
|
return 0
|
|
|
|
if self.values.system_type() == SystemType.dualTilt:
|
|
dimension1 = self.column_count
|
|
dimension2 = self.row_count
|
|
else:
|
|
dimension1 = self.row_count
|
|
dimension2 = self.column_count
|
|
|
|
result = (standalone_inverter_count + panel_board_counts[0] + (2 * panel_board_counts[1])) / sum(panel_board_counts)
|
|
result *= dimension1 * max(dimension1 / dimension2, 1)
|
|
result += dimension1
|
|
return ceil(result)
|
|
|
|
def get_standalone_inverters(self, power_station):
|
|
standalone_inverters = self.values.standalone_inverters()
|
|
count = 0
|
|
for inverter in standalone_inverters:
|
|
if inverter['attachment_point'][1] == power_station['power_station_id']:
|
|
count += 1
|
|
return count
|
|
|
|
def power_station_has_monitor(self, power_station, monitors):
|
|
for monitor in monitors:
|
|
if monitor['power_source'][1] == power_station['power_station_id']:
|
|
return True
|
|
return False
|