Files
PVPlant/Mechanical/Frame/PVPlantFrame.py
2025-06-15 23:10:17 +02:00

1319 lines
53 KiB
Python

# /**********************************************************************
# * *
# * Copyright (c) 2021 Javier Braña <javier.branagutierrez@gmail.com> *
# * *
# * This program is free software; you can redistribute it and/or modify*
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * as published by the Free Software Foundation; either version 2 of *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * This program is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307*
# * USA *
# * *
# ***********************************************************************
import math
import ArchComponent
import FreeCAD
import Part
import PVPlantSite
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtCore
from PySide.QtCore import QT_TRANSLATE_NOOP
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
__title__ = "PVPlant Frames"
__author__ = "Javier Braña"
__url__ = "http://www.sogos-solar.com"
import os
import PVPlantResources
from PVPlantResources import DirIcons as DirIcons
class FrameSetup:
"A Base Frame Setup Class"
def __init__(self, obj):
# Definición de Variables:
self.obj = obj
def setProperties(self, obj):
''' Definición de Propiedades: '''
pl = obj.PropertiesList
# Modulo: ------------------------------------------------------------------------------------------------------
if not "ModuleThick" in pl:
obj.addProperty("App::PropertyLength",
"ModuleThick",
"Module",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).ModuleThick = 40
if not "ModuleWidth" in pl:
obj.addProperty("App::PropertyLength",
"ModuleWidth",
"Module",
QT_TRANSLATE_NOOP("App::Property", "The width of this object")
).ModuleWidth = 1130
if not "ModuleHeight" in pl:
obj.addProperty("App::PropertyLength",
"ModuleHeight",
"Module",
QT_TRANSLATE_NOOP("App::Property", "The Length of this object")
).ModuleHeight = 2250
if not "PoleCableLength" in pl:
obj.addProperty("App::PropertyLength",
"PoleCableLength",
"Module",
QT_TRANSLATE_NOOP("App::Property", "The Length of this object")
).PoleCableLength = 1200
if not "ModulePower" in pl:
obj.addProperty("App::PropertyQuantity",
"ModulePower",
"Module",
QT_TRANSLATE_NOOP("App::Property", "The Length of this object")
).ModulePower = 600
# Array de modulos: -------------------------------------------------------------------------------------------
if not "ModuleColumns" in pl:
obj.addProperty("App::PropertyQuantity",
"ModuleColumns",
"ModuleArray",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).ModuleColumns = 2
if not "ModuleRows" in pl:
obj.addProperty("App::PropertyQuantity",
"ModuleRows",
"ModuleArray",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).ModuleRows = 2
if not "ModuleColGap" in pl:
obj.addProperty("App::PropertyDistance",
"ModuleColGap",
"ModuleArray",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).ModuleColGap = 20
if not "ModuleRowGap" in pl:
obj.addProperty("App::PropertyDistance",
"ModuleRowGap",
"ModuleArray",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).ModuleRowGap = 20
if not "ModuleOffsetX" in pl:
obj.addProperty("App::PropertyDistance",
"ModuleOffsetX",
"ModuleArray",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).ModuleOffsetX = 0
if not "ModuleOffsetY" in pl:
obj.addProperty("App::PropertyDistance",
"ModuleOffsetY",
"ModuleArray",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).ModuleOffsetY = 0
if not "ModuleOrientation" in pl:
obj.addProperty("App::PropertyEnumeration",
"ModuleOrientation",
"ModuleArray",
QT_TRANSLATE_NOOP("App::Property",
"The facemaker type to use to build the profile of this object")
).ModuleOrientation = ["Portrait", "Landscape"]
if not "ModuleViews" in pl:
obj.addProperty("App::PropertyBool",
"ModuleViews",
"ModuleArray",
QT_TRANSLATE_NOOP("App::Property",
"The facemaker type to use to build the profile of this object")
).ModuleViews = True
if not "TotalPower" in pl:
obj.addProperty("App::PropertyQuantity",
"TotalPower",
"ModuleArray",
QT_TRANSLATE_NOOP("App::Property",
"The facemaker type to use to build the profile of this object")
)
# Poles --------------------------------------------------------------------------------------------------------
if not "PoleType" in pl:
obj.addProperty("App::PropertyLinkList",
"PoleType",
"Poles",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
)
# Frame --------------------------------------------------------------------------------------------------------
if not "MaxLengthwiseTilt" in pl:
obj.addProperty("App::PropertyAngle",
"MaxLengthwiseTilt",
"Frame",
QT_TRANSLATE_NOOP("App::Property", "Máxima inclinación longitudinal")
).MaxLengthwiseTilt = 15
if not "Width" in pl:
obj.addProperty("App::PropertyDistance",
"Width",
"Frame",
QT_TRANSLATE_NOOP("App::Property",
"Largo de la estructura")
)
obj.setEditorMode("Width", 1)
if not "Length" in pl:
obj.addProperty("App::PropertyDistance",
"Length",
"Frame",
QT_TRANSLATE_NOOP("App::Property",
"Ancho de la estructura")
)
obj.setEditorMode("Length", 1)
if not "TotalAreaShape" in pl:
obj.addProperty("App::PropertyDistance",
"TotalAreaShape",
"Frame",
QT_TRANSLATE_NOOP("Part::PropertyPartShape",
"Total Area de los Paneles")
)
obj.setEditorMode("TotalAreaShape", 1)
''' ------------------------------------------- Fixed Structure --------------------------------------------------- '''
def makeRack(name="Rack"):
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
obj.Label = name
FixedRack(obj)
ViewProviderFixedRack(obj.ViewObject)
return obj
class FixedRack(FrameSetup):
"A Fixed Rack Obcject"
def __init__(self, obj):
#FrameSetup.__init__(self, obj)
super(FixedRack, self).__init__(obj)
self.setProperties(obj)
obj.ModuleColumns = 6
obj.ModuleRows = 2
obj.ModuleColGap = 20
obj.ModuleRowGap = 20
#obj.Tilt = 30
# Does a IfcType exist?
# obj.IfcType = "Fence"
# obj.MoveWithHost = False
def setProperties(self, obj):
FrameSetup.setProperties(self, obj)
pl = obj.PropertiesList
if not "ModuleElevation" in pl:
obj.addProperty("App::PropertyDistance",
"ModuleElevation",
"ModuleArray",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).ModuleElevation = 500
# Frame: ------------------------------------------------------------------------------------------------------
if not "Tilt" in pl:
obj.addProperty("App::PropertyAngle",
"Tilt",
"Frame",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).Tilt = 0
if not "FrameType" in pl:
obj.addProperty("App::PropertyEnumeration",
"FrameType",
"Frame",
QT_TRANSLATE_NOOP("App::Property",
"The facemaker type to use to build the profile of this object")
).FrameType = ["Simple", "West-Lest"]
# Pole: ------------------------------------------------------------------------------------------------------
if not "BackPostWidth" in pl:
obj.addProperty("App::PropertyLength",
"BackPostWidth",
"Pole",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).BackPostWidth = 80
if not "BackPostHeight" in pl:
obj.addProperty("App::PropertyLength",
"BackPostHeight",
"Pole",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).BackPostHeight = 160
if not "BackPostLength" in pl:
obj.addProperty("App::PropertyLength",
"BackPostLength",
"Pole",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).BackPostLength = 3200
if not "FrontPostWidth" in pl:
obj.addProperty("App::PropertyLength",
"FrontPostWidth",
"Pole",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).FrontPostWidth = 40
if not "FrontPostHeight" in pl:
obj.addProperty("App::PropertyLength",
"FrontPostHeight",
"Pole",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).FrontPostHeight = 80
if not "FrontPostLength" in pl:
obj.addProperty("App::PropertyLength",
"FrontPostLength",
"Pole",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).FrontPostLength = 2300
# Array of Posts: ------------------------------------------------------------------------------------------------------
if not "NumberPostsX" in pl:
obj.addProperty("App::PropertyQuantity",
"NumberPostsX",
"Poles",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).NumberPostsX = 3
if not "DistancePostsX" in pl:
obj.addProperty("App::PropertyLength",
"DistancePostsX",
"Poles",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).DistancePostsX = 3000
if not "FrontPost" in pl:
obj.addProperty("App::PropertyBool",
"FrontPost",
"Poles",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).FrontPost = False
if not "DistancePostsY" in pl:
obj.addProperty("App::PropertyLength",
"DistancePostsY",
"Poles",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).DistancePostsY = 2000
if not "RammingDeep" in pl:
obj.addProperty("App::PropertyLength",
"RammingDeep",
"Poles",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).RammingDeep = 1500
# Correas: ----------------------------------------------------------------------------------------------------
if not "BeamHeight" in pl:
obj.addProperty("App::PropertyLength",
"BeamHeight",
"Beam",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).BeamHeight = 80
if not "BeamWidth" in pl:
obj.addProperty("App::PropertyLength",
"BeamWidth",
"Beam",
QT_TRANSLATE_NOOP("App::Property", "The width of this object")
).BeamWidth = 50
if not "BeamOffset" in pl:
obj.addProperty("App::PropertyLength",
"BeamOffset",
"Beam",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).BeamOffset = 50
if not "BeamSpacing" in pl:
obj.addProperty("App::PropertyLength",
"BeamSpacing",
"Beam",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).BeamSpacing = 1000
self.Type = "Fixed Rack"
obj.Proxy = self
def onDocumentRestored(self, obj):
#ArchComponent.Component.onDocumentRestored(self, obj)
super(FixedRack, self).onDocumentRestored(obj)
self.setProperties(obj)
def __getstate__(self):
return self.Type
def __setstate__(self, state):
if state:
self.Type = state
def onChanged(self, fp, prop):
'''Do something when a property has changed'''
def generate_modules(self, obj, h, w):
modulos=[]
for y in range(int(obj.ModuleRows.Value)):
for x in range(int(obj.ModuleColumns.Value)):
mod = Part.makeBox(w, h, obj.ModuleThick.Value)
mod.Placement.Base = FreeCAD.Vector(
x * (w + obj.ModuleColGap.Value),
y * (h + obj.ModuleRowGap.Value),
0
)
modulos.append(mod)
compound = Part.makeCompound(modulos)
'''center = FreeCAD.Vector(compound.BoundBox.Center)
for mod in modulos:
mod.Placement.Base -= FreeCAD.Vector(center.x, center.y, 0)
compound = Part.makeCompound(modulos)'''
return compound
def generate_posts(self, obj):
postBack = Part.makeBox(obj.BackPostWidth.Value, obj.BackPostHeight.Value, obj.BackPostLength.Value)
postFront = Part.makeBox(obj.FrontPostWidth.Value, obj.FrontPostHeight.Value, obj.FrontPostLength.Value)
post_back = []
post_front = []
for x in range(int(obj.NumberPostsX.Value)):
postCopy = postBack.copy()
postCopy.Placement.Base = FreeCAD.Vector(x * obj.DistancePostsX.Value, obj.DistancePostsY.Value, 0)
post_back.append(postCopy)
if obj.FrontPost:
postCopy = postFront.copy()
postCopy.Placement.Base = FreeCAD.Vector(x * obj.DistancePostsX.Value, 0, 0)
post_front.append(postCopy)
return Part.makeCompound([Part.makeCompound(post_back), Part.makeCompound(post_front)])
def correct_placement(self, obj):
center = FreeCAD.Vector(obj.BoundBox.Center)
obj.Placement.Base -= FreeCAD.Vector(center.x, center.y, 0)
def execute(self, obj):
# obj.Shape: compound
# |- Modules and Beams: compound
# |-- Modules array: compound
# |--- Modules: solid
# |-- Beams: compound
# |--- MainBeam: solid
# |--- Secundary Beams: solid (if exist)
# |- Poles array: compound
# |-- Poles: solid
pl = obj.Placement
if obj.ModuleOrientation == "Portrait":
w = obj.ModuleWidth.Value
h = obj.ModuleHeight.Value
else:
h = obj.ModuleWidth.Value
w = obj.ModuleHeight.Value
totalh = h * obj.ModuleRows + obj.ModuleRowGap.Value * (obj.ModuleRows - 1)
totalw = w * obj.ModuleColumns + obj.ModuleColGap.Value * (obj.ModuleColumns - 1)
modules = self.generate_modules(obj, h, w)
modules.Placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(1, 0, 0), obj.Tilt.Value)
self.correct_placement(modules)
modules.Placement.Base += FreeCAD.Vector(0, 0, obj.ModuleElevation.Value)
posts = self.generate_posts(obj)
self.correct_placement(posts)
posts.Placement.Base -= FreeCAD.Vector(0, 0, obj.RammingDeep.Value)
compound = Part.makeCompound([modules, posts])
obj.Shape = compound
obj.Placement = pl
angle = obj.Placement.Rotation.toEuler()[1]
if angle > obj.MaxLengthwiseTilt:
obj.ViewObject.ShapeColor = (1.0, 0.0, 0.0)
obj.Width = totalw
obj.Length = totalh
class ViewProviderFixedRack:
"A View Provider for the Pipe object"
def __init__(self, vobj):
vobj.Proxy = self
def getIcon(self):
""" Return the path to the appropriate icon. """
return str(os.path.join(DirIcons, "solar-fixed.svg"))
def setEdit(self, vobj, mode):
if (mode == 0) and hasattr(self, "Object"):
taskd = _FixedRackTaskPanel(self.Object)
taskd.obj = self.Object
FreeCADGui.Control.showDialog(taskd)
return True
return False
class _FixedRackTaskPanel:
def __init__(self, obj=None):
self.obj = obj
# -------------------------------------------------------------------------------------------------------------
# Module widget form
# -------------------------------------------------------------------------------------------------------------
self.form = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "Mechanical/Frame/PVPlantFixedFrame.ui")
def selectionchange(self, i):
vis = False
if i == 1:
vis = True
self.formRack.widgetTracker.setVisible(vis)
def editBreadthwaysNumOfPostChange(self):
self.formPiling.tableBreadthwaysPosts.insertRow(self.formPiling.tableBreadthwaysPosts.rowCount)
def editAlongNumOfPostChange(self):
self.l1.setText("current value:" + str(self.sp.value()))
def getValues(self):
d = {}
d["ModuleFrame"] = self.ModuleFrame
d["ModuleHeight"] = self.ModuleHeight
d["ModuleWidth"] = self.ModuleWidth
d["ModuleThick"] = self.ModuleThick
return d
def accept(self):
FreeCADGui.Control.closeDialog()
return True
def reject(self):
FreeCAD.ActiveDocument.removeObject(self.obj.Name)
FreeCADGui.Control.closeDialog()
return True
''' ------------------------------------------- Tracker Structure --------------------------------------------------- '''
def makeTrackerSetup(name="TrackerSetup"):
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "TrackerSetup")
obj.Label = name
TrackerSetup(obj)
ViewProviderTrackerSetup(obj.ViewObject)
FreeCAD.ActiveDocument.recompute()
try:
site = PVPlantSite.get()
frame_list = site.Frames
frame_list.append(obj)
site.Frames = frame_list
except:
pass
return obj
def getarray(array, numberofpoles):
if len(array) == 0:
newarray = [0] * numberofpoles
return newarray
elif len(array) == 1:
newarray = [array[0]] * numberofpoles
return newarray
elif len(array) == 2:
newarray = [array[0]] * numberofpoles
half = int(numberofpoles / 2)
newarray[half] = array[1]
if numberofpoles % 2 == 0:
newarray[half - 1] = array[1]
return newarray
elif len(array) == 3:
half = int(numberofpoles / 2)
newarray = [array[0]] * half + [array[1]] + [array[2]] * half
if numberofpoles % 2 == 0:
newarray[half] = array[1]
return newarray
elif len(array) == numberofpoles:
return array
elif len(array) > numberofpoles:
return array[0: numberofpoles]
else:
newarray = [array[0]] * numberofpoles
return newarray
class TrackerSetup(FrameSetup):
"A 1 Axis Tracker Obcject"
def __init__(self, obj):
FrameSetup.__init__(self, obj)
self.setProperties(obj)
obj.ModuleColumns = 45
obj.ModuleRows = 2
obj.ModuleColGap = 20
obj.ModuleRowGap = 20
#obj.Tilt = 0
def setProperties(self, obj):
FrameSetup.setProperties(self, obj)
pl = obj.PropertiesList
# Array de modulos: -------------------------------------------------------------------------------------------
if not "MotorGap" in pl:
obj.addProperty("App::PropertyDistance",
"MotorGap",
"ModuleArray",
QT_TRANSLATE_NOOP("App::Property", "Thse height of this object")
).MotorGap = 550
if not "UseGroupsOfModules" in pl:
obj.addProperty("App::PropertyBool",
"UseGroupsOfModules",
"GroupsOfModules",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).UseGroupsOfModules = False
# Array of Posts: ------------------------------------------------------------------------------------------------------
'''movido a la clase madre
if not "PoleType" in pl:
obj.addProperty("App::PropertyLinkList",
"PoleType",
"Poles",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
)'''
if not "PoleSequence" in pl:
obj.addProperty("App::PropertyIntegerList",
"PoleSequence",
"Poles",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
)
if not "NumberPole" in pl:
obj.addProperty("App::PropertyQuantity",
"NumberPole",
"Poles",
"The total number of poles"
).NumberPole = 7
if not "DistancePole" in pl:
obj.addProperty("App::PropertyIntegerList", # No list of Lenght so I use float list
"DistancePole",
"Poles",
"Distance between poles starting from the left and from the first photovoltaic module "
"without taking into account the offsets"
).DistancePole = [7000, 7000, 7000, 7000, 7000, 7000, 7000]
if not "AerialPole" in pl:
obj.addProperty("App::PropertyIntegerList",
"AerialPole",
"Poles",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).AerialPole = [1050]
# Correas: ----------------------------------------------------------------------------------------------------
# 1. MainBeam: -------------------------------------------------------------------------------------------------
if not "MainBeamProfile" in pl:
obj.addProperty("App::PropertyEnumeration",
"MainBeamProfile",
"Beam",
"The height of this object"
).MainBeamProfile = ["Square", "Octagon", "Circle"]
if not "MainBeamHeight" in pl:
obj.addProperty("App::PropertyLength",
"MainBeamHeight",
"Beam",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).MainBeamHeight = 120
if not "MainBeamWidth" in pl:
obj.addProperty("App::PropertyLength",
"MainBeamWidth",
"Beam",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).MainBeamWidth = 120
if not "MainBeamAxisPosition" in pl:
obj.addProperty("App::PropertyLength",
"MainBeamAxisPosition",
"Beam",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).MainBeamAxisPosition = 1278
# 2. Costillas: ----------------------------------------------------------------------------------------------------
if not "ShowBeams" in pl:
obj.addProperty("App::PropertyBool",
"ShowBeams",
"Beam",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).ShowBeams = False
if not "BeamHeight" in pl:
obj.addProperty("App::PropertyLength",
"BeamHeight",
"Beam",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).BeamHeight = 80
if not "BeamWidth" in pl:
obj.addProperty("App::PropertyLength",
"BeamWidth",
"Beam",
QT_TRANSLATE_NOOP("App::Property", "The width of this object")
).BeamWidth = 83.2
if not "BeamOffset" in pl:
obj.addProperty("App::PropertyLength",
"BeamOffset",
"Beam",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).BeamOffset = 50
if not "BeamSpacing" in pl:
obj.addProperty("App::PropertyLength",
"BeamSpacing",
"Beam",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).BeamSpacing = 1000
# Tracker --------------------------------------------------------------------------------------------------------
if not "MaxPhi" in pl:
obj.addProperty("App::PropertyAngle",
"MaxPhi",
"Frame",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).MaxPhi = 60
if not "MinPhi" in pl:
obj.addProperty("App::PropertyAngle",
"MinPhi",
"Frame",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).MinPhi = -60
if not "MaxNegativeLengthwiseTilt" in pl:
obj.addProperty("App::PropertyAngle",
"MaxNegativeLengthwiseTilt",
"Frame",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).MaxNegativeLengthwiseTilt = 6
self.Type = "1 Axis Tracker"
obj.Proxy = self
def onDocumentRestored(self, obj):
self.setProperties(obj)
def onChanged(self, obj, prop):
'''Do something when a property has changed'''
'''if prop == "NumberPole":
obj.AerialPole = self.calculateAerialArray(obj)
obj.DistancePole = self.calculateDistanceArray(obj)
obj.PoleSequence = self.calculatePostSequence(obj)'''
if prop == "AerialPole":
obj.AerialPole = self.calculateAerialArray(obj)
if prop == "DistancePole":
obj.DistancePole = self.calculateDistanceArray(obj)
if prop == "PoleSequence":
obj.PoleSequence = self.calculatePostSequence(obj)
if prop == "UseGroupsOfModules":
if obj.getPropertyByName(prop) == True:
if not "ColumnsPerGroup" in obj.PropertiesList:
obj.addProperty("App::PropertyIntegerList",
"ColumnsPerGroup",
"GroupsOfModules",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
)
if not "GroupGaps" in obj.PropertiesList:
obj.addProperty("App::PropertyIntegerList",
"GroupGaps",
"GroupsOfModules",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
)
else:
if "ColumnsPerGroup" in obj.PropertiesList:
obj.removeProperty("ColumnsPerGroup")
if "GroupGaps" in obj.PropertiesList:
obj.removeProperty("GroupGaps")
def calculatePostSequence(self, obj):
return getarray(obj.PoleSequence, int(obj.NumberPole.Value))
def calculateAerialArray(self, obj):
return getarray(obj.AerialPole, int(obj.NumberPole.Value))
def calculateDistanceArray(self, obj):
return getarray(obj.DistancePole, int(obj.NumberPole.Value))
def CalculateModuleArray(self, obj, totalh, totalw, moduleh, modulew):
module = Part.makeBox(modulew, moduleh, obj.ModuleThick.Value)
compound = Part.makeCompound([])
offsetx = -totalw / 2
offsety = -totalh / 2
offsetz = obj.MainBeamHeight.Value + obj.BeamHeight.Value
if obj.ModuleViews:
mid = int(obj.ModuleColumns.Value / 2)
for row in range(int(obj.ModuleRows.Value)):
for col in range(int(obj.ModuleColumns.Value)):
xx = offsetx + (modulew + obj.ModuleColGap.Value) * col
if col >= mid:
xx += obj.MotorGap.Value - obj.ModuleColGap.Value
yy = offsety + (moduleh + obj.ModuleRowGap.Value) * row
zz = offsetz
moduleCopy = module.copy()
moduleCopy.Placement.Base = FreeCAD.Vector(xx, yy, zz)
compound.add(moduleCopy)
else:
totalArea = Part.makePlane(totalw, totalh)
totalArea.Placement.Base = FreeCAD.Vector(offsetx, offsety, offsetz)
compound.add(totalArea)
return compound
def calculateBeams(self, obj, totalh, totalw, moduleh, modulew):
''' make mainbeam and modules beams '''
compound = Part.makeCompound([])
if obj.MainBeamProfile == "Square":
mainbeam = Part.makeBox(totalw + obj.ModuleOffsetX.Value * 2,
obj.MainBeamWidth.Value,
obj.MainBeamHeight.Value)
mainbeam.Placement.Base.y = -obj.MainBeamWidth.Value / 2
elif obj.MainBeamProfile == "Octagon":
# base on Draft.polygon:
radius = obj.MainBeamWidth.Value / 2
angle = (math.pi * 2) / 8
angleoffset = angle/2
delta = radius / math.cos(angle / 2.0)
pts = []
for i in range(8):
ang = (i + 1) * angle + angleoffset
point = FreeCAD.Vector(0,
delta * math.cos(ang),
delta * math.sin(ang))
point = point.add(FreeCAD.Vector(0, 0, radius))
pts.append(point)
pts.append(pts[0])
profile = Part.makePolygon(pts)
profile = Part.Face(profile)
mainbeam = profile.extrude(FreeCAD.Vector(totalw + obj.ModuleOffsetX.Value * 2, 0, 0))
else:
radius = obj.MainBeamWidth.Value / 2
profile = Part.Face(Part.Wire(Part.makeCircle(radius, FreeCAD.Vector(0,0,radius),
FreeCAD.Vector(1,0,0))))
mainbeam = profile.extrude(FreeCAD.Vector(totalw + obj.ModuleOffsetX.Value * 2, 0, 0))
mainbeam.Placement.Base.x = -totalw / 2 - obj.ModuleOffsetX.Value
compound.add(mainbeam)
# Correa profile:
if obj.ShowBeams: # TODO: make it in another function
mid = int(obj.ModuleColumns.Value / 2)
up = 27.8 # todo
thi = 3.2 # todo
p1 = FreeCAD.Vector(obj.BeamWidth.Value / 2 - up, 0, thi)
p2 = FreeCAD.Vector(p1.x, 0, obj.BeamHeight.Value)
p3 = FreeCAD.Vector(obj.BeamWidth.Value / 2, 0, p2.z)
p4 = FreeCAD.Vector(p3.x, 0, obj.BeamHeight.Value - thi)
p5 = FreeCAD.Vector(p4.x - up + thi, 0, p4.z)
p6 = FreeCAD.Vector(p5.x, 0, 0)
p7 = FreeCAD.Vector(-p6.x, 0, p6.z)
p8 = FreeCAD.Vector(-p5.x, 0, p5.z)
p9 = FreeCAD.Vector(-p4.x, 0, p4.z)
p10 = FreeCAD.Vector(-p3.x, 0, p3.z)
p11 = FreeCAD.Vector(-p2.x, 0, p2.z)
p12 = FreeCAD.Vector(-p1.x, 0, p1.z)
p = Part.makePolygon([p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p1])
p = Part.Face(p)
profile = p.extrude(FreeCAD.Vector(0, 428, 0))
for col in range(int(obj.ModuleColumns.Value)):
xx = totalArea.Placement.Base.x + (modulew + obj.ModuleColGap.Value) * col
if col >= mid:
xx += float(obj.MotorGap.Value) - obj.ModuleColGap.Value
correaCopy = profile.copy()
correaCopy.Placement.Base.x = xx
correaCopy.Placement.Base.y = -428 / 2
correaCopy.Placement.Base.z = obj.MainBeamHeight.Value
self.ListModules.append(correaCopy)
compound.add(correaCopy)
return compound
def CalculatePosts(self, obj, totalh, totalw):
# Temp: utilizar el uso de versiones:
ver = 1
if ver == 0:
# versión 0:
'''posttmp = Part.makeBox(obj.PoleWidth.Value, obj.PoleHeight.Value, obj.PoleLength.Value)
linetmp = Part.LineSegment(FreeCAD.Vector(0), FreeCAD.Vector(0, obj.PoleHeight.Value / 2, 0)).toShape()
compoundPoles = Part.makeCompound([])
compoundAxis = Part.makeCompound([])
offsetX = - totalw / 2
offsetY = -obj.PoleHeight.Value / 2
arrayDistance = getarray(obj.DistancePole, int(obj.NumberPole.Value))
arrayAerial = getarray(obj.AerialPole, int(obj.NumberPole.Value))
for x in range(int(obj.NumberPole.Value)):
offsetX += arrayDistance[x] - obj.PoleWidth.Value / 2
postCopy = posttmp.copy()
postCopy.Placement.Base = FreeCAD.Vector(offsetX, offsetY, -(obj.PoleLength.Value - arrayAerial[x]))
compoundPoles.add(postCopy)
axis = linetmp.copy()
axis.Placement.Base = FreeCAD.Vector(offsetX + obj.PoleWidth.Value / 2, offsetY, arrayAerial[x])
compoundAxis.add(axis)
return compoundPoles, compoundAxis'''
elif ver == 1:
# versión 1:
linetmp = Part.LineSegment(FreeCAD.Vector(0), FreeCAD.Vector(0, 10, 0)).toShape()
compoundPoles = Part.makeCompound([])
compoundAxis = Part.makeCompound([])
offsetX = - totalw / 2
arrayDistance = obj.DistancePole
arrayAerial = obj.AerialPole
arrayPost = obj.PoleSequence
for x in range(int(obj.NumberPole.Value)):
postCopy = obj.PoleType[arrayPost[x]].Shape.copy()
offsetX += arrayDistance[x]
postCopy.Placement.Base = FreeCAD.Vector(offsetX, 0, -(postCopy.BoundBox.ZLength - arrayAerial[x]))
compoundPoles.add(postCopy)
axis = linetmp.copy()
axis.Placement.Base = FreeCAD.Vector(offsetX, 0, arrayAerial[x])
compoundAxis.add(axis)
return compoundPoles, compoundAxis
def execute(self, obj):
# obj.Shape: compound
# |- Modules and Beams: compound
# |-- Modules array: compound
# |--- Modules: solid
# |-- Beams: compound
# |--- MainBeam: solid
# |--- Secundary Beams: solid (if exist)
# |- Poles array: compound
# |-- Poles: solid
# |-- Axis: Edge/line (if exist)
if obj.ModuleOrientation == "Portrait":
w = obj.ModuleWidth.Value
h = obj.ModuleHeight.Value
else:
h = obj.ModuleWidth.Value
w = obj.ModuleHeight.Value
totalh = h * obj.ModuleRows + obj.ModuleRowGap.Value * (obj.ModuleRows - 1)
totalw = w * obj.ModuleColumns + obj.ModuleColGap.Value * (obj.ModuleColumns - 1) + \
(obj.MotorGap.Value - obj.ModuleColGap.Value) if obj.MotorGap.Value > 0 else 0
modules = self.CalculateModuleArray(obj, totalh, totalw, h, w)
beams = self.calculateBeams(obj, totalh, totalw, h, w)
poles, poleaxis = self.CalculatePosts(obj, totalh, totalw)
compound = Part.makeCompound([modules, beams])
compound.Placement.Base.z = obj.MainBeamAxisPosition.Value - (obj.MainBeamHeight.Value / 2)
obj.Shape = Part.makeCompound([compound, Part.makeCompound([poles, poleaxis])])
obj.Width = min(obj.Shape.BoundBox.XLength, obj.Shape.BoundBox.YLength)
obj.Length = max(obj.Shape.BoundBox.XLength, obj.Shape.BoundBox.YLength)
obj.TotalPower = obj.ModulePower.Value * obj.ModuleRows * obj.ModuleColumns
class ViewProviderTrackerSetup:
"A View Provider for the TrackerSetup object"
def __init__(self, obj):
'''Set this object to the proxy object of the actual view provider'''
obj.Proxy = self
def getIcon(self):
return str(os.path.join(DirIcons, "trackersetup.svg"))
def setEdit(self, vobj, mode):
if (mode == 0) and hasattr(self, "Object"):
taskd = _TrackerTaskPanel(self.Object)
taskd.obj = self.Object
FreeCADGui.Control.showDialog(taskd)
return True
return False
def makeTracker(name = "Tracker", setup = None):
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Tracker")
obj.Label = name
Tracker(obj)
ViewProviderTracker(obj.ViewObject)
if setup is not None:
obj.Setup = setup
return obj
class Tracker(ArchComponent.Component):
"A 1 Axis single row Tracker Obcject"
def __init__(self, obj):
# Definición de Variables:
ArchComponent.Component.__init__(self, obj)
self.Type = None
self.oldTilt = 0
self.oldRotation = None
self.setProperties(obj)
def setProperties(self, obj):
pl = obj.PropertiesList
if not ("Setup" in pl):
obj.addProperty("App::PropertyLink",
"Setup",
"Frame",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
)
if not ("Tilt" in pl):
obj.addProperty("App::PropertyAngle",
"Tilt",
"Frame",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).Tilt = 0
if not ("AngleX" in pl):
obj.addProperty("App::PropertyAngle",
"AngleX",
"Outputs",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).AngleX = 0
if not ("AngleY" in pl):
obj.addProperty("App::PropertyAngle",
"AngleY",
"Outputs",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).AngleX = 0
if not ("AngleZ" in pl):
obj.addProperty("App::PropertyAngle",
"AngleZ",
"Outputs",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).AngleX = 0
self.Type = "Tracker"
#obj.Type = self.Type
obj.Proxy = self
def onDocumentRestored(self, obj):
ArchComponent.Component.onDocumentRestored(self, obj)
self.setProperties(obj)
def onBeforeChange(self, obj, prop):
if prop == "Tilt":
self.oldTilt = obj.Tilt.Value
if prop == "Placement":
self.oldRotation = obj.Placement.Rotation
def onChanged(self, obj, prop):
if prop.startswith("Angle"):
base = obj.Placement.Base
angles = obj.Placement.Rotation.toEulerAngles("XYZ")
if prop == "AngleX":
rot = FreeCAD.Rotation(angles[2], angles[1], obj.AngleX.Value)
elif prop == "AngleY":
rot = FreeCAD.Rotation(angles[2], obj.AngleY.Value, angles[0])
elif prop == "AngleZ":
rot = FreeCAD.Rotation(obj.AngleZ.Value, angles[1], angles[0])
obj.Placement = FreeCAD.Placement(base, rot, FreeCAD.Vector(0,0,0))
if hasattr(FreeCAD.ActiveDocument, "FramesChecking"):
from PVPlantRackChecking import checkSingleTracker
checkSingleTracker(obj, FreeCAD.ActiveDocument.FramesChecking)
def execute(self, obj):
# obj.Shape: compound
# |- Modules and Beams: compound
# |-- Modules array: compound
# |--- Modules: solid
# |-- Beams: compound
# |--- MainBeam: solid
# |--- Secundary Beams: solid (if exist)
# |- Poles array: compound
# |-- Poles array: compound
# |--- Pole: solid
# |-- PoleAxes: Edge
if obj.Setup is None:
return
pl = obj.Placement
shape = obj.Setup.Shape.copy()
p1 = shape.SubShapes[0].SubShapes[1].SubShapes[0].CenterOfMass
p2 = min(shape.SubShapes[0].SubShapes[1].SubShapes[0].Faces, key=lambda face: face.Area).CenterOfMass
axis = p1 - p2
modules = shape.SubShapes[0].rotate(p1, axis, obj.Tilt.Value)
angle = obj.Placement.Rotation.toEuler()[1]
newpoles = Part.makeCompound([])
for i in range(len(shape.SubShapes[1].SubShapes[0].SubShapes)):
pole = shape.SubShapes[1].SubShapes[0].SubShapes[i]
axis = shape.SubShapes[1].SubShapes[1].SubShapes[i]
base = axis.Vertexes[0].Point
axis = axis.Vertexes[1].Point - axis.Vertexes[0].Point
newpoles.add(pole.rotate(base, axis, -angle))
poles = Part.makeCompound([newpoles, shape.SubShapes[1].SubShapes[1].copy()])
obj.Shape = Part.makeCompound([modules, poles])
obj.Placement = pl
obj.AngleX, obj.AngleY, obj.AngleZ = obj.Placement.Rotation.toEulerAngles("XYZ")
class ViewProviderTracker(ArchComponent.ViewProviderComponent):
"A View Provider for the Pipe object"
def __init__(self, vobj):
ArchComponent.ViewProviderComponent.__init__(self, vobj)
self.Object = vobj.Object
vobj.Proxy = self
def getIcon(self):
return str(os.path.join(DirIcons, "solar-tracker.svg"))
def setEdit(self, vobj, mode):
"""Method called when the document requests the object to enter edit mode.
Edit mode is entered when a user double clicks on an object in the tree
view, or when they use the menu option [Edit -> Toggle Edit Mode].
Just display the standard FRAME SETUP task panel.
Parameters
----------
mode: int or str
The edit mode the document has requested. Set to 0 when requested via
a double click or [Edit -> Toggle Edit Mode].
Returns
-------
bool
If edit mode was entered.
"""
'''if (mode == 0) and hasattr(self, "Object"):
taskd = _TrackerTaskPanel(self.Object)
taskd.obj = self.Object
FreeCADGui.Control.showDialog(taskd)
return True'''
return False
class _TrackerTaskPanel:
def __init__(self, obj=None):
if not (obj is None):
self.new = True
self.obj = makeRack()
else:
self.new = False
self.obj = obj
# -------------------------------------------------------------------------------------------------------------
# Module widget form
# -------------------------------------------------------------------------------------------------------------
self.formRack = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "/PVPlantFrame.ui")
self.formRack.widgetTracker.setVisible(False)
self.formRack.comboFrameType.currentIndexChanged.connect(self.selectionchange)
self.formPiling = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "/PVPlantRackFixedPiling.ui")
self.formPiling.editBreadthwaysNumOfPost.valueChanged.connect(self.editBreadthwaysNumOfPostChange)
self.formPiling.editAlongNumOfPost.valueChanged.connect(self.editAlongNumOfPostChange)
self.form = [self.formRack, self.formPiling]
def selectionchange(self, i):
vis = False
if i == 1:
vis = True
self.formRack.widgetTracker.setVisible(vis)
def editBreadthwaysNumOfPostChange(self):
self.formPiling.tableBreadthwaysPosts.insertRow(self.formPiling.tableBreadthwaysPosts.rowCount)
def editAlongNumOfPostChange(self):
self.l1.setText("current value:" + str(self.sp.value()))
def getValues(self):
d = {}
d["ModuleFrame"] = self.ModuleFrame
d["ModuleHeight"] = self.ModuleHeight
d["ModuleWidth"] = self.ModuleWidth
d["ModuleThick"] = self.ModuleThick
return d
def accept(self):
FreeCADGui.Control.closeDialog()
return True
def reject(self):
if self.new:
FreeCAD.ActiveDocument.removeObject(self.obj.Name)
FreeCADGui.Control.closeDialog()
return True
class _FrameTaskPanel:
def __init__(self, obj=None):
if not (obj is None):
self.new = True
self.ojb = makeRack()
else:
self.new = False
self.obj = obj
self.formRack = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "/PVPlantFrame.ui")
self.formRack.widgetTracker.setVisible(False)
self.formRack.comboFrameType.setEnable(self.new)
self.formRack.comboFrameType.currentIndexChanged.connect(self.selectionchange)
self.formPiling = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "/PVPlantRackFixedPiling.ui")
self.formPiling.editBreadthwaysNumOfPost.valueChanged.connect(self.editBreadthwaysNumOfPostChange)
self.formPiling.editAlongNumOfPost.valueChanged.connect(self.editAlongNumOfPostChange)
self.form = [self.formRack, self.formPiling]
def selectionchange(self, i):
vis = False
if i == 1:
vis = True
self.formRack.widgetTracker.setVisible(vis)
def editBreadthwaysNumOfPostChange(self):
self.formPiling.tableBreadthwaysPosts.insertRow(self.formPiling.tableBreadthwaysPosts.rowCount)
def editAlongNumOfPostChange(self):
self.l1.setText("current value:" + str(self.sp.value()))
def getValues(self):
d = {}
d["ModuleFrame"] = self.ModuleFrame
d["ModuleHeight"] = self.ModuleHeight
d["ModuleWidth"] = self.ModuleWidth
d["ModuleThick"] = self.ModuleThick
return d
def accept(self):
FreeCADGui.Control.closeDialog()
return True
def reject(self):
FreeCAD.ActiveDocument.removeObject(self.obj.Name)
if self.new:
FreeCADGui.Control.closeDialog()
return True
class CommandFixedRack:
"the Arch Building command definition"
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "solar-fixed.svg")),
'MenuText': QtCore.QT_TRANSLATE_NOOP("PVPlantRack", "Fixed Rack"),
'Accel': "R, F",
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PVPlantRack",
"Creates a Fixed Rack object from setup dialog.")}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
def Activated(self):
obj = makeRack()
#self.TaskPanel = _FixedRackTaskPanel(obj)
#FreeCADGui.Control.showDialog(self.TaskPanel)
return
class CommandTrackerSetup:
"the Arch Building command definition"
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "trackersetup.svg")),
'MenuText': QtCore.QT_TRANSLATE_NOOP("PVPlantTracker", "TrackerSetup"),
'Accel': "R, F",
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PVPlanTracker",
"Creates a TrackerSetup object from setup dialog.")}
def IsActive(self):
return True
return (not FreeCAD.ActiveDocument is None and
not FreeCAD.ActiveDocument.getObject("Site") is None)
def Activated(self):
obj = makeTrackerSetup()
self.TaskPanel = _FixedRackTaskPanel(obj)
FreeCADGui.Control.showDialog(self.TaskPanel)
return
class CommandTracker:
"the Arch Building command definition"
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "solar-tracker.svg")),
'MenuText': QtCore.QT_TRANSLATE_NOOP("PVPlantTracker", "Tracker"),
'Accel': "R, F",
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PVPlanTracker",
"Creates a Tracker object from setup dialog.")}
def IsActive(self):
return ((not (FreeCAD.ActiveDocument is None)) and
(not (FreeCAD.ActiveDocument.getObject("Site")) is None) and
(not (FreeCAD.ActiveDocument.getObject("TrackerSetup")) is None))
def Activated(self):
sel = FreeCADGui.Selection.getSelection()
setupobj = None
for obj in sel:
if obj.Name[:12] == "TrackerSetup":
setupobj = obj
break
makeTracker(setup=setupobj)
return