# /********************************************************************** # * * # * Copyright (c) 2021 Javier BraƱa * # * * # * 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 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 try: _fromUtf8 = QtCore.QString.fromUtf8 except AttributeError: def _fromUtf8(s): return s import os __dir__ = os.path.join(FreeCAD.getUserAppDataDir(), "Mod", "PVPlant") import PVPlantResources from PVPlantResources import DirIcons as DirIcons Dir3dObjects = os.path.join(PVPlantResources.DirResources, "3dObjects") def makeStringSetup(): obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "StringSetup") _StringSetup(obj) _ViewProviderStringSetup(obj.ViewObject) try: if FreeCAD.ActiveDocument.StringsSetup: FreeCAD.ActiveDocument.StringsSetup.addObject(obj) except: pass return obj class _StringSetup: def __init__(self, obj): self.setCommonProperties(obj) self.obj = obj self.StringCount = 1 def setCommonProperties(self, obj): pl = obj.PropertiesList if not ("NumberOfStrings" in pl): obj.addProperty("App::PropertyInteger", "NumberOfStrings", "Setup", QT_TRANSLATE_NOOP("App::Property", "The height of this object") ).NumberOfStrings = 0 obj.setEditorMode("NumberOfStrings", 1) self.Type = "StringSetup" obj.Proxy = self def onDocumentRestored(self, obj): self.setProperties(obj) def addString(self, modulelist): stringName = "String" + str(self.StringCount) self.obj.addProperty("App::PropertyIntegerList", stringName, "Setup", "String: " + stringName ) setattr(self.obj, stringName, modulelist) ''' self.obj.addProperty("App::PropertyInteger", stringName + "_Power", "Outputs", QT_TRANSLATE_NOOP("App::Property", "The height of this object") ) setattr(self.obj, stringName + "_Power", len(modulelist) * 450) ''' self.obj.NumberOfStrings = self.StringCount self.StringCount += 1 class _ViewProviderStringSetup: def __init__(self, vobj): ''' Set view properties. ''' self.Object = vobj.Object vobj.Proxy = self def attach(self, vobj): ''' Create Object visuals in 3D view. ''' self.Object = vobj.Object return def getIcon(self): ''' Return object treeview icon. ''' return str(os.path.join(DirIcons, "stringsetup.svg")) ''' def claimChildren(self): """ Provides object grouping """ return self.Object.Group ''' def setEdit(self, vobj, mode=0): """ Enable edit """ return True def unsetEdit(self, vobj, mode=0): """ Disable edit """ return False def doubleClicked(self, vobj): """ Detect double click """ pass def setupContextMenu(self, obj, menu): """ Context menu construction """ pass def edit(self): """ Edit callback """ pass def __getstate__(self): """ Save variables to file. """ return None def __setstate__(self,state): """ Get variables from file. """ return None class _SelObserver: def __init__(self, form): self.form = form def addSelection(self, doc, obj, sub, pnt): rack = FreeCAD.ActiveDocument.getObjectsByLabel(obj)[0].Shape modules = rack.SubShapes[0].SubShapes[0].SubShapes if sub[0:4] == 'Face': numFace = int(sub.replace('Face', '')) - 1 selFace = rack.Faces[numFace] for module in modules: for num, face in enumerate(module.Faces): if selFace.isSame(face): self.form.setModule(modules.index(module)) FreeCADGui.Selection return True elif sub[0:4] == 'Edge': numEdge = int(sub.replace('Edge', '')) - 1 selEdge = rack.Edge[numEdge] for module in modules: for num, edge in enumerate(module.Edges): if selEdge.isSame(edge): print("Encontrado: ", modules.index(module)) return True return True def removeSelection(self, doc, obj, sub): # Delete the selected object '''print("FSO-RemSel:" + str(obj) + ":" + str(sub) + "\n")''' return True def setSelection(self, doc): # Selection in ComboView '''print("FSO-SetSel:" + "\n")''' return True def clearSelection(self, doc): # If click on the screen, clear the selection '''print("FSO-ClrSel:" + "\n")''' return True class _StringSetupPanel: def __init__(self, obj=None): self.obj = obj self.new = False if obj is None: self.new = True self.obj = makeStringSetup() self.form = FreeCADGui.PySideUic.loadUi(__dir__ + "/PVPlantStringSetup.ui") self.form.buttonSelFrame.clicked.connect(self.selFrame) self.form.buttonAddString.clicked.connect(self.addString) self.form.listStrings.currentRowChanged.connect(self.listStringsCurrentRowChanged) self.form.editName.editingFinished.connect(self.setStringName) self.selobserver = _SelObserver(self) self.stringslist = [] self.currentstring = 0 self.totalModules = 0 self.left = 0 self.frameColor = None def selFrame(self): sel = FreeCADGui.Selection.getSelection() frame = None if len(sel) == 0: # TODO: lanzar un error return elif len(sel) == 1: frame = sel[0] else: for obj in sel: if not hasattr(obj, 'Proxy'): continue print(obj.Proxy.__class__) print(issubclass(obj.Proxy.__class__, PVPlantRack._Frame)) if issubclass(obj.Proxy.__class__, PVPlantRack._Frame): frame = obj break if frame == None: # TODO: lanzar un error print("No frame selected") return self.frame = frame self.form.editFrame.setText(frame.Label) self.totalModules = frame.ModuleCols * frame.ModuleRows self.left = self.totalModules self.form.editTotal.setText(str(int(self.totalModules))) self.form.editUsed.setText("0") self.form.editLeft.setText(str(int(self.left))) FreeCADGui.Selection.removeObserver(self.selobserver) FreeCADGui.Selection.addObserver(self.selobserver) self.frameColor = self.obj.ViewObject.ShapeColor self.colorlist = [] for face in self.frame.Shape.Faces: self.colorlist.append((1.0, 0.50, 0.40, 0.25)) self.frame.ViewObject.DiffuseColor = self.colorlist def addString(self): FreeCADGui.Selection.clearSelection() self.stringslist.append([]) self.currentstring = len(self.stringslist) - 1 self.form.listStrings.addItem("String" + str(self.form.listStrings.count())) self.form.listStrings.setCurrentRow(self.form.listStrings.count() - 1) def listStringsCurrentRowChanged(self, row): self.currentstring = row self.form.listModules.clear() for num in self.stringslist[row]: self.form.listModules.addItem(str(num)) def setModule(self, numModule): if len(self.stringslist) == 0: return if self.left == 0: return if numModule in self.stringslist[self.currentstring]: '''remove module ?? ''' self.stringslist[self.currentstring].remove(int(numModule)) item = self.form.listModules.findItems(str(numModule), QtCore.Qt.MatchExactly)[0] self.form.listModules.takeItem(self.form.listModules.row(item)) for moduleFace in self.frame.Shape.Solids[numModule].Faces: for i, face in enumerate(self.frame.Shape.Faces): if moduleFace.isEqual(face): self.colorlist[i] = (1.0, 0.50, 0.40, 0.25) self.frame.ViewObject.DiffuseColor = self.colorlist break else: self.stringslist[self.currentstring].append(int(numModule)) self.form.listModules.addItem(str(numModule)) for moduleFace in self.frame.Shape.Solids[numModule].Faces: for i, face in enumerate(self.frame.Shape.Faces): if moduleFace.isEqual(face): self.colorlist[i] = (0.0, 0.0, 1.0, 0.0) self.frame.ViewObject.DiffuseColor = self.colorlist break self.calculateModules() def calculateModules(self): num = 0 for string in self.stringslist: num += len(string) self.left = int(self.totalModules - num) self.form.editUsed.setText(str(num)) self.form.editLeft.setText(str(self.left)) def setStringName(self): self.obj.Label = self.form.editName.text() def accept(self): for string in self.stringslist: self.obj.Proxy.addString(string.copy()) self.FormClosing() return True def reject(self): FreeCAD.ActiveDocument.removeObject(self.obj.Name) self.FormClosing() return True def FormClosing(self): FreeCADGui.Selection.removeObserver(self.selobserver) FreeCADGui.Control.closeDialog() self.frame.ViewObject.DiffuseColor = self.frameColor def makeString(base=None): if base is None: return obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "String") _String(obj) obj.Frame = base _ViewProviderString(obj.ViewObject) if FreeCAD.ActiveDocument.Strings: FreeCAD.ActiveDocument.Strings.addObject(obj) FreeCAD.ActiveDocument.recompute() return obj class _String(ArchComponent.Component): def __init__(self, obj): ArchComponent.Component.__init__(self, obj) self.setProperties(obj) obj.Proxy = self obj.IfcType = "Cable Segment" obj.setEditorMode("IfcType", 1) def setProperties(self, obj): pl = obj.PropertiesList if not ("Frame" in pl): obj.addProperty("App::PropertyLink", "Frame", "Setup", QT_TRANSLATE_NOOP("App::Property", "The height of this object") ).Frame = None if not ("StringSetup" in pl): obj.addProperty("App::PropertyLink", "StringSetup", "Setup", QT_TRANSLATE_NOOP("App::Property", "The height of this object") ).StringSetup = None if not ("StringPoles" in pl): obj.addProperty("App::PropertyVectorList", "StringPoles", "StringOutput", QT_TRANSLATE_NOOP("App::Property", "The height of this object") ).StringPoles = [] obj.setEditorMode("StringPoles", 1) self.Type = "String" def onDocumentRestored(self, obj): ArchComponent.Component.onDocumentRestored(self, obj) self.setProperties(obj) obj.Proxy = self def __getstate__(self): return self.Type def __setstate__(self, state): if state: self.Type = state def onBeforeChange(self, obj, prop): '''''' def onChanged(self, obj, prop): '''Do something when a property has changed''' def getDownFace(module): area_max = max([face.Area for face in module.Faces]) faces = [] for face in module.Faces: if face.Area == area_max: faces.append(face) return faces[0] if faces[0].Placement.Base.z > faces[1].Placement.Base.z else faces[1] if (prop == "Frame") or (prop == "StringSetup"): if not (obj.Frame is None) and not (obj.StringSetup is None): JuntionBoxPosition = 200 portrait = obj.Frame.ModuleOrientation == "Portrait" cableLength = 1200 if hasattr(obj.Frame, "PoleCableLength"): cableLength = obj.Frame.PoleCableLength.Value moduleWidth = obj.Frame.ModuleWidth.Value moduleHeight = obj.Frame.ModuleHeight.Value dist_x = JuntionBoxPosition + obj.Frame.ModuleColGap.Value + ( moduleWidth if portrait else moduleHeight) / 2 dist_y = obj.Frame.ModuleRowGap.Value + (moduleHeight if portrait else moduleWidth) PolePosition = 0 if portrait: PolePosition = moduleWidth / 2 - JuntionBoxPosition FrameModules = obj.Frame.Shape.SubShapes[0].SubShapes[0].SubShapes cableProfile = Part.Face(Part.Wire(Part.Circle(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(1, 0, 0), 6).toShape())) positiveMC4 = Part.Shape() positiveMC4.read(os.path.join(Dir3dObjects, "MC4 POSITIVE.IGS")) negativeMC4 = Part.Shape() negativeMC4.read(os.path.join(Dir3dObjects, "MC4 NEGATIVE.IGS")) vec = None if obj.Frame.Route: vertexes = obj.Frame.Route.Shape.Vertexes vec = vertexes[1].Point - vertexes[0].Point else: poles = obj.Frame.Shape.SubShapes[1].SubShapes vec = poles[1].BoundBox.Center - poles[0].BoundBox.Center vecp= FreeCAD.Vector(-vec.y, vec.x, vec.z) # V1: for stringnum in range(obj.StringSetup.NumberOfStrings): '''''' return # V0: for stringnum in range(obj.StringSetup.NumberOfStrings): string = obj.StringSetup.getPropertyByName("String" + str(stringnum + 1)) total = len(string) - 1 dir = 0 ndir = 0 # dirvec = FrameModules(string[-]).Placement.Base - FrameModules(string[0]).Placement.Base for i, num in enumerate(string): face = getDownFace(FrameModules[num]) if i < total: dir = string[i + 1] - num dir /= abs(dir) ndir = -dir positivePolePosition = FreeCAD.Vector(0, ndir * PolePosition, 0) + face.CenterOfMass negativePolePosition = FreeCAD.Vector(0, dir * PolePosition, 0) + face.CenterOfMass # dibujar +: p1 = positivePolePosition p2 = p1 + ndir * FreeCAD.Vector(0, 50, 0) p3 = p2 + ndir * FreeCAD.Vector(cableLength - dist_x, 0, 0) p4 = p3 + ndir * FreeCAD.Vector(0, dist_x - 50, 0) w = Part.makePolygon([p1, p2, p3, p4]) #cableProfile.Placement.Base = p1 #cable = w.makePipeShell([cableProfile], True, False, 2) Part.show(w) mc4copy = positiveMC4.copy() mc4copy.Placement.Base = p4 + FreeCAD.Vector(0, mc4copy.BoundBox.XLength/2 + 1.3, 0) positiveMC4.Placement.Rotation.Angle = math.radians(ndir * 90) Part.show(mc4copy) # dibujar -: p1 = negativePolePosition p2 = p1 + dir * FreeCAD.Vector(0, 50, 0) p3 = p2 + ndir * FreeCAD.Vector(cableLength - dist_x, 0, 0) p4 = p3 + dir * FreeCAD.Vector(0, dist_x - 50, 0) w = Part.makePolygon([p1, p2, p3, p4]) Part.show(w) mc4copy = negativeMC4.copy() mc4copy.Placement.Base = p4 - FreeCAD.Vector(0, mc4copy.BoundBox.XLength/2 + 1.3, 0) mc4copy.Placement.Rotation.Angle = math.radians(dir * 90) Part.show(mc4copy) if i == 0 or i == total: list = obj.StringPoles.copy() list.append(p3) obj.StringPoles = list break def execute(self, obj): '''Do something when recompute''' class _ViewProviderString(ArchComponent.ViewProviderComponent): def __init__(self, vobj): ArchComponent.ViewProviderComponent.__init__(self, vobj) def getIcon(self): return str(os.path.join(DirIcons, "string.svg")) class _CommandStringSetup: def GetResources(self): return {'Pixmap': str(os.path.join(DirIcons, "stringsetup.svg")), 'Accel': "E, C", 'MenuText': "String Setup", 'ToolTip': "Configure strings of a Frame"} def IsActive(self): if FreeCAD.ActiveDocument: return True else: return False def Activated(self): self.TaskPanel = _StringSetupPanel() FreeCADGui.Control.showDialog(self.TaskPanel) return class _CommandStringing: def GetResources(self): return {'Pixmap': str(os.path.join(DirIcons, "string.svg")), 'Accel': "E, S", 'MenuText': QT_TRANSLATE_NOOP("Placement", "String"), 'ToolTip': QT_TRANSLATE_NOOP("Placement", "Make string on a Frame")} def IsActive(self): if FreeCAD.ActiveDocument: return True else: return False def Activated(self): # issubclass(Car, Vehicles) # isinstance(Car, Vehicles) sel = FreeCADGui.Selection.getSelection() if len(sel) > 0: for obj in sel: if obj.Name.__contains__("Tracker"): makeString(obj) if FreeCAD.GuiUp: class _CommandStringingGroup: def GetCommands(self): return tuple(['PVPlantStringSetup', 'PVPlantStringing' ]) def GetResources(self): return {'MenuText': 'Stringing', 'ToolTip': 'Tools to setup and make strings' } def IsActive(self): return not FreeCAD.ActiveDocument is None FreeCADGui.addCommand('PVPlantStringSetup', _CommandStringSetup()) FreeCADGui.addCommand('PVPlantStringing', _CommandStringing()) FreeCADGui.addCommand('Stringing', _CommandStringingGroup())