primera subida
This commit is contained in:
80
Export/PVPlantBOQCivil.py
Normal file
80
Export/PVPlantBOQCivil.py
Normal file
@@ -0,0 +1,80 @@
|
||||
# /**********************************************************************
|
||||
# * *
|
||||
# * 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 FreeCAD, Draft
|
||||
import PVPlantSite
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
from DraftTools import translate
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import draftguitools.gui_trackers as DraftTrackers
|
||||
|
||||
import Part
|
||||
import pivy
|
||||
from pivy import coin
|
||||
import os
|
||||
else:
|
||||
# \cond
|
||||
def translate(ctxt, txt):
|
||||
return txt
|
||||
|
||||
|
||||
def QT_TRANSLATE_NOOP(ctxt, txt):
|
||||
return txt
|
||||
# \endcond
|
||||
|
||||
__title__ = "PVPlant Trench"
|
||||
__author__ = "Javier Braña"
|
||||
__url__ = "http://www.sogos-solar.com"
|
||||
|
||||
from PVPlantResources import DirIcons as DirIcons
|
||||
from PVPlantResources import DirDocuments as DirDocuments
|
||||
import openpyxl
|
||||
|
||||
|
||||
def makeBOQCivil():
|
||||
''' create a excel '''
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class _CommandBOQCivil:
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "boqc.svg")),
|
||||
'Accel': "R, C",
|
||||
'MenuText': "BOQ Civil",
|
||||
'ToolTip': ""}
|
||||
|
||||
def Activated(self):
|
||||
makeBOQCivil()
|
||||
|
||||
def IsActive(self):
|
||||
if FreeCAD.ActiveDocument:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('BOQCivil', _CommandBOQCivil())
|
||||
104
Export/PVPlantBOQElectrical.py
Normal file
104
Export/PVPlantBOQElectrical.py
Normal file
@@ -0,0 +1,104 @@
|
||||
# /**********************************************************************
|
||||
# * *
|
||||
# * 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 FreeCAD, Draft
|
||||
import copy
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
from DraftTools import translate
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import draftguitools.gui_trackers as DraftTrackers
|
||||
|
||||
import Part
|
||||
import pivy
|
||||
from pivy import coin
|
||||
import os
|
||||
else:
|
||||
# \cond
|
||||
def translate(ctxt, txt):
|
||||
return txt
|
||||
|
||||
|
||||
def QT_TRANSLATE_NOOP(ctxt, txt):
|
||||
return txt
|
||||
# \endcond
|
||||
|
||||
__title__ = "PVPlant Trench"
|
||||
__author__ = "Javier Braña"
|
||||
__url__ = "http://www.sogos-solar.com"
|
||||
|
||||
from PVPlantResources import DirIcons as DirIcons
|
||||
|
||||
|
||||
def makeBOQElectrical():
|
||||
''' create a excel '''
|
||||
print("makeBOQElectrical")
|
||||
import sys
|
||||
sys.path.append(r"/")
|
||||
funct = 0
|
||||
if funct == 0:
|
||||
print(" ------- Prueba generar layout en excel: ")
|
||||
# export layout to Excel:
|
||||
from Export import layoutToExcel
|
||||
layoutToExcel.generateLayout()
|
||||
return
|
||||
else:
|
||||
print(" ------- Prueba dibujar contorno de los objetos seleccionados: ")
|
||||
import numpy as np
|
||||
import MeshTools.MeshGetBoundary as mgb
|
||||
# contorno:
|
||||
maxdist = 6000
|
||||
if FreeCADGui.Selection.hasSelection():
|
||||
sel = FreeCADGui.Selection.getSelection()
|
||||
pts = []
|
||||
for obj in sel:
|
||||
for panel in obj.Shape.SubShapes[0].SubShapes[0].SubShapes:
|
||||
zm = panel.BoundBox.ZMax
|
||||
for i in range(8):
|
||||
pt = panel.BoundBox.getPoint(i)
|
||||
if pt.z == zm:
|
||||
pts.append(pt)
|
||||
import PVPlantCreateTerrainMesh
|
||||
m = PVPlantCreateTerrainMesh.Triangulate(np.array(pts), MaxlengthLE=maxdist, use3d=False)
|
||||
b = mgb.get_boundary(m)
|
||||
Part.show(b)
|
||||
|
||||
class _CommandBOQElectrical:
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "boqe.svg")),
|
||||
'Accel': "R, E",
|
||||
'MenuText': "BOQ Electrical",
|
||||
'ToolTip': ""}
|
||||
|
||||
def Activated(self):
|
||||
makeBOQElectrical()
|
||||
|
||||
def IsActive(self):
|
||||
if FreeCAD.ActiveDocument:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('BOQElectrical', _CommandBOQElectrical())
|
||||
344
Export/PVPlantBOQMechanical.py
Normal file
344
Export/PVPlantBOQMechanical.py
Normal file
@@ -0,0 +1,344 @@
|
||||
# /**********************************************************************
|
||||
# * *
|
||||
# * 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 FreeCAD
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui, os
|
||||
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 openpyxl
|
||||
from openpyxl.styles import Alignment, Border, Side, PatternFill, Font
|
||||
import PVPlantResources
|
||||
import PVPlantSite
|
||||
|
||||
# Estilos:
|
||||
thin = Side(border_style="thin", color="7DA4B8")
|
||||
double = Side(border_style="double", color="ff0000")
|
||||
border_thin = Border(top=thin, left=thin, right=thin, bottom=thin)
|
||||
border_fat = Border(top=thin, left=thin, right=thin, bottom=thin)
|
||||
# fill = PatternFill("solid", fgColor="DDDDDD")
|
||||
# fill = GradientFill(stop=("000000", "FFFFFF"))
|
||||
|
||||
scale = 0.001 # milimeters to meter
|
||||
|
||||
def style_range(ws, cell_range, border=Border(), fill=None, font=None, alignment=None):
|
||||
"""
|
||||
Apply styles to a range of cells as if they were a single cell.
|
||||
|
||||
:param ws: Excel worksheet instance
|
||||
:param range: An excel range to style (e.g. A1:F20)
|
||||
:param border: An openpyxl Border
|
||||
:param fill: An openpyxl PatternFill or GradientFill
|
||||
:param font: An openpyxl Font object
|
||||
"""
|
||||
|
||||
top = Border(top=border.top)
|
||||
left = Border(left=border.left)
|
||||
right = Border(right=border.right)
|
||||
bottom = Border(bottom=border.bottom)
|
||||
|
||||
first_cell = ws[cell_range.split(":")[0]]
|
||||
if alignment:
|
||||
first_cell.alignment = alignment
|
||||
|
||||
rows = ws[cell_range]
|
||||
if font:
|
||||
first_cell.font = font
|
||||
|
||||
for cell in rows[0]:
|
||||
cell.border = cell.border + top
|
||||
for cell in rows[-1]:
|
||||
cell.border = cell.border + bottom
|
||||
|
||||
for row in rows:
|
||||
l = row[0]
|
||||
r = row[-1]
|
||||
l.border = l.border + left
|
||||
r.border = r.border + right
|
||||
if fill:
|
||||
for c in row:
|
||||
c.fill = fill
|
||||
|
||||
def spreadsheetBOQFrames(sheet, sel):
|
||||
sheet['A1'] = 'Index'
|
||||
sheet['B1'] = 'Frame'
|
||||
sheet['C1'] = 'Frame Type'
|
||||
sheet['D1'] = 'X'
|
||||
sheet['E1'] = 'Y'
|
||||
sheet['F1'] = 'Z'
|
||||
sheet['G1'] = 'Angle N-S'
|
||||
sheet['H1'] = 'Angle L-W'
|
||||
sheet['I1'] = 'Nº Poles'
|
||||
|
||||
sheet.column_dimensions['A'].width = 8
|
||||
sheet.column_dimensions['B'].width = 30
|
||||
sheet.column_dimensions['C'].width = 20
|
||||
sheet.column_dimensions['D'].width = 20
|
||||
sheet.column_dimensions['E'].width = 20
|
||||
sheet.column_dimensions['F'].width = 20
|
||||
sheet.column_dimensions['G'].width = 15
|
||||
sheet.column_dimensions['H'].width = 15
|
||||
sheet.column_dimensions['I'].width = 15
|
||||
sheet.row_dimensions[1].height = 40
|
||||
|
||||
style_range(sheet, 'A1:I1',
|
||||
border=Border(top=thin, left=thin, right=thin, bottom=thin),
|
||||
fill=PatternFill("solid", fgColor="7DA4B8"),
|
||||
font=Font(name='Quicksand', size=10, b=True, color="FFFFFF"),
|
||||
alignment=Alignment(horizontal="center", vertical="center"))
|
||||
|
||||
for ind in range(0, len(sel)):
|
||||
row = ind + 2
|
||||
sheet['A{0}'.format(row)] = ind + 1
|
||||
sheet['B{0}'.format(row)] = sel[ind].Label
|
||||
sheet['C{0}'.format(row)] = sel[ind].Setup.Label
|
||||
sheet['D{0}'.format(row)] = sel[ind].Placement.Base.x * scale
|
||||
sheet['E{0}'.format(row)] = sel[ind].Placement.Base.y * scale
|
||||
sheet['R{0}'.format(row)] = sel[ind].Placement.Base.z * scale
|
||||
sheet['G{0}'.format(row)] = sel[ind].Placement.Rotation.toEuler()[0]
|
||||
sheet['H{0}'.format(row)] = sel[ind].Placement.Rotation.toEuler()[1]
|
||||
sheet['I{0}'.format(row)] = sel[ind].Setup.NumberPole.Value
|
||||
style_range(sheet, 'A' + str(row) + ':I' + str(row),
|
||||
border=Border(top=thin, left=thin, right=thin, bottom=thin),
|
||||
font=Font(name='Quicksand', size=10),
|
||||
alignment=Alignment(horizontal="center", vertical="center"))
|
||||
|
||||
def spreadsheetBOQPoles(sheet, sel):
|
||||
import MeshPart as mp
|
||||
from Mechanical.Frame import PVPlantFrame
|
||||
|
||||
# Data:
|
||||
terrain = PVPlantSite.get().Terrain.Mesh # Shape
|
||||
|
||||
# Headers:
|
||||
sheet['A1'] = 'Frame'
|
||||
sheet['B1'] = 'Pole'
|
||||
sheet['C1'] = 'Pole Type'
|
||||
sheet['D1'] = 'X'
|
||||
sheet['E1'] = 'Y'
|
||||
sheet['F1'] = 'Z frame attach'
|
||||
sheet['G1'] = 'Z aerial head'
|
||||
sheet['H1'] = 'Pole length'
|
||||
sheet['I1'] = 'Pole aerial length'
|
||||
sheet['J1'] = 'Pole terrain enter length'
|
||||
|
||||
sheet.column_dimensions['A'].width = 30
|
||||
sheet.column_dimensions['B'].width = 8
|
||||
sheet.column_dimensions['C'].width = 20
|
||||
sheet.column_dimensions['D'].width = 20
|
||||
sheet.column_dimensions['E'].width = 20
|
||||
sheet.column_dimensions['F'].width = 20
|
||||
sheet.column_dimensions['G'].width = 20
|
||||
sheet.column_dimensions['H'].width = 20
|
||||
sheet.column_dimensions['I'].width = 20
|
||||
sheet.column_dimensions['J'].width = 20
|
||||
sheet.row_dimensions[1].height = 40
|
||||
style_range(sheet, 'A1:J1',
|
||||
border=Border(top=thin, left=thin, right=thin, bottom=thin),
|
||||
fill=PatternFill("solid", fgColor="7DA4B8"),
|
||||
font=Font(name='Quicksand', size=11, b=True, color="FFFFFF"),
|
||||
alignment=Alignment(horizontal="center", vertical="center"))
|
||||
sheet['A2'] = ""
|
||||
sheet.row_dimensions[2].height = 5
|
||||
|
||||
data = {"Frame": [],
|
||||
#"FrameType": [],
|
||||
"Pole": [],
|
||||
"PoleType": [],
|
||||
"PoleLength": [],
|
||||
"Center": [],
|
||||
"Head": []}
|
||||
|
||||
cnt = 0
|
||||
for frame_ind, frame in enumerate(sel):
|
||||
poles = frame.Shape.SubShapes[1].SubShapes[0].SubShapes
|
||||
numpoles = int(frame.Setup.NumberPole.Value)
|
||||
seq = frame.Setup.PoleSequence
|
||||
if len(seq) < numpoles:
|
||||
seq = PVPlantFrame.getarray(frame.Setup.PoleSequence, numpoles)
|
||||
for pole_ind in range(numpoles):
|
||||
pole = poles[pole_ind]
|
||||
poletype = frame.Setup.PoleType[seq[pole_ind]]
|
||||
data["Frame"].append(frame.Label)
|
||||
#data["FrameType"].append(frame.Setup.Label)
|
||||
data["Pole"].append(pole_ind + 1)
|
||||
data["PoleType"].append(poletype.Label)
|
||||
data["PoleLength"].append(int(poletype.Height))
|
||||
data["Center"].append(pole.BoundBox.Center)
|
||||
data["Head"].append(pole.BoundBox.ZMax)
|
||||
cnt += 1
|
||||
|
||||
pts = mp.projectPointsOnMesh(data["Center"], terrain, FreeCAD.Vector(0, 0, 1))
|
||||
#if cnt == len(pts):
|
||||
data["Soil"] = pts
|
||||
|
||||
row = 3
|
||||
group_from = row
|
||||
f = data["Frame"][0]
|
||||
for i in range(0, len(data["Frame"])):
|
||||
if f != data["Frame"][i]:
|
||||
style_range(sheet, 'A' + str(group_from) + ':F' + str(row - 1),
|
||||
border=Border(top=thin, left=thin, right=thin, bottom=thin),
|
||||
font=Font(name='Quicksand', size=11, ),
|
||||
alignment=Alignment(horizontal="center", vertical="center"))
|
||||
sheet.merge_cells('A' + str(group_from) + ':A' + str(row - 1))
|
||||
style_range(sheet, 'A' + str(group_from) + ':A' + str(row - 1),
|
||||
border=Border(top=thin, left=thin, right=thin, bottom=thin),
|
||||
font=Font(name='Quicksand', size=11, ),
|
||||
alignment=Alignment(horizontal="center", vertical="center"))
|
||||
#sheet['A{0}'.format(row)] = ""
|
||||
sheet.row_dimensions[row].height = 5
|
||||
row += 1
|
||||
f = data["Frame"][i]
|
||||
group_from = row
|
||||
|
||||
sheet['A{0}'.format(row)] = data['Frame'][i]
|
||||
sheet['B{0}'.format(row)] = data['Pole'][i]
|
||||
sheet['C{0}'.format(row)] = data['PoleType'][i]
|
||||
sheet['D{0}'.format(row)] = round(data['Center'][i].x, 0) * scale
|
||||
sheet['D{0}'.format(row)].number_format = "0.000"
|
||||
sheet['E{0}'.format(row)] = round(data['Center'][i].y, 0) * scale
|
||||
sheet['E{0}'.format(row)].number_format = "0.000"
|
||||
try:
|
||||
sheet['F{0}'.format(row)] = round(data['Soil'][i].z, 0) * scale
|
||||
sheet['F{0}'.format(row)].number_format = "0.000"
|
||||
except:
|
||||
pass
|
||||
sheet['G{0}'.format(row)] = round(data['Head'][i]) * scale
|
||||
sheet['G{0}'.format(row)].number_format = "0.000"
|
||||
sheet['H{0}'.format(row)] = data["PoleLength"][i] * scale
|
||||
sheet['H{0}'.format(row)].number_format = "0.000"
|
||||
sheet['I{0}'.format(row)] = '=G{0}-F{0}'.format(row)
|
||||
sheet['I{0}'.format(row)].number_format = "0.000"
|
||||
sheet['J{0}'.format(row)] = '=H{0}-I{0}'.format(row)
|
||||
sheet['J{0}'.format(row)].number_format = "0.000"
|
||||
|
||||
style_range(sheet, 'A' + str(row) + ':J' + str(row),
|
||||
border=Border(top=thin, left=thin, right=thin, bottom=thin),
|
||||
font=Font(name='Quicksand', size=11,),
|
||||
alignment=Alignment(horizontal="center", vertical="center"))
|
||||
row += 1
|
||||
|
||||
def spreadsheetBOQPanelCollision(sheet, sel):
|
||||
# Headers:
|
||||
sheet['A1'] = 'Frame'
|
||||
sheet['B1'] = 'Nombre'
|
||||
sheet['C1'] = 'X'
|
||||
sheet['D1'] = 'Y'
|
||||
sheet['E1'] = 'Z'
|
||||
sheet['G1'] = 'Ángulo E-O'
|
||||
sheet['H1'] = 'Nº Hincas'
|
||||
|
||||
sheet.column_dimensions['A'].width = 30
|
||||
sheet.column_dimensions['B'].width = 8
|
||||
sheet.column_dimensions['C'].width = 20
|
||||
sheet.column_dimensions['D'].width = 20
|
||||
sheet.column_dimensions['E'].width = 20
|
||||
sheet.column_dimensions['F'].width = 20
|
||||
sheet.column_dimensions['G'].width = 20
|
||||
sheet.column_dimensions['H'].width = 20
|
||||
sheet.column_dimensions['I'].width = 20
|
||||
sheet.row_dimensions[1].height = 40
|
||||
style_range(sheet, 'A1:I1',
|
||||
border=Border(top=thin, left=thin, right=thin, bottom=thin),
|
||||
fill=PatternFill("solid", fgColor="7DA4B8"),
|
||||
font=Font(name='Quicksand', size=11, b=True, color="FFFFFF"),
|
||||
alignment=Alignment(horizontal="center", vertical="center"))
|
||||
sheet['A2'] = ""
|
||||
sheet.row_dimensions[2].height = 5
|
||||
|
||||
# Data:
|
||||
for frame_ind in range(0, len(sel)):
|
||||
frame = sel[frame_ind]
|
||||
sheet['A{0}'.format(ind + 2)] = ind
|
||||
sheet['B{0}'.format(ind + 2)] = sel[ind].Label
|
||||
sheet['C{0}'.format(ind + 2)] = sel[ind].Placement.Base.x * scale
|
||||
sheet['D{0}'.format(ind + 2)] = sel[ind].Placement.Base.y * scale
|
||||
sheet['E{0}'.format(ind + 2)] = sel[ind].Placement.Base.z * scale
|
||||
sheet['F{0}'.format(ind + 2)] = sel[ind].Placement.Rotation.toEuler()[0]
|
||||
sheet['G{0}'.format(ind + 2)] = sel[ind].Placement.Rotation.toEuler()[1]
|
||||
sheet['H{0}'.format(ind + 2)] = sel[ind].NumberPole.Value
|
||||
|
||||
class _CommandBOQMechanical:
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "boqm.svg")),
|
||||
'Accel': "R, M",
|
||||
'MenuText': QT_TRANSLATE_NOOP("Placement", "BOQ Mecánico"),
|
||||
'ToolTip': QT_TRANSLATE_NOOP("Placement", "Calcular el BOQ de la")}
|
||||
|
||||
def Activated(self):
|
||||
# make file global:
|
||||
#sel = FreeCAD.ActiveDocument.findObjects(Name="Tracker")
|
||||
sel = []
|
||||
for obj in FreeCAD.ActiveDocument.Objects:
|
||||
'''if not hasattr(obj, "Proxy"):
|
||||
continue
|
||||
if issubclass(obj.Proxy.__class__, PVPlantRack.Frame):
|
||||
objects.append(obj)'''
|
||||
if obj.Name.startswith("Tracker") and not obj.Name.startswith("TrackerSetup"):
|
||||
sel.append(obj)
|
||||
sel = sorted(sel, key=lambda x: x.Label)
|
||||
if len(sel) > 0:
|
||||
path = os.path.dirname(FreeCAD.ActiveDocument.FileName)
|
||||
filename = os.path.join(path, "BOQMechanical.xlsx")
|
||||
mywb = openpyxl.Workbook()
|
||||
sheet = mywb.active
|
||||
sheet.title = 'Frames information'
|
||||
spreadsheetBOQFrames(sheet, sel)
|
||||
|
||||
sheet = mywb.create_sheet("Poles information")
|
||||
spreadsheetBOQPoles(sheet, sel)
|
||||
mywb.save(filename)
|
||||
|
||||
print("Se ha generado el BOQMechanical: ")
|
||||
print(filename)
|
||||
'''import sys
|
||||
path = r'C:\Program Files (x86)\IronPython 2.7\Lib'
|
||||
sys.path.append(path)'''
|
||||
|
||||
import subprocess
|
||||
subprocess.Popen('explorer ' + path)
|
||||
|
||||
def IsActive(self):
|
||||
if FreeCAD.ActiveDocument:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('BOQMechanical', _CommandBOQMechanical())
|
||||
466
Export/exportDXF.py
Normal file
466
Export/exportDXF.py
Normal file
@@ -0,0 +1,466 @@
|
||||
import math
|
||||
import FreeCAD
|
||||
from Utils.PVPlantUtils import findObjects
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
from PySide import QtCore, QtGui
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
import os
|
||||
else:
|
||||
# \cond
|
||||
def translate(ctxt, txt):
|
||||
return txt
|
||||
|
||||
|
||||
def QT_TRANSLATE_NOOP(ctxt, txt):
|
||||
return txt
|
||||
# \endcond
|
||||
|
||||
__title__ = "PVPlant Export to DXF"
|
||||
__author__ = "Javier Braña"
|
||||
__url__ = "http://www.sogos-solar.com"
|
||||
|
||||
import PVPlantResources
|
||||
from PVPlantResources import DirIcons as DirIcons
|
||||
|
||||
field = {"name": "", "width": 0, "heigth": 0}
|
||||
|
||||
|
||||
class exportDXF:
|
||||
|
||||
def __init__(self, filename):
|
||||
''' '''
|
||||
self.doc = None
|
||||
self.msp = None
|
||||
self.filename = filename
|
||||
|
||||
'''
|
||||
doc.linetypes.add("GRENZE2",
|
||||
# linetype definition in acad.lin:
|
||||
# A,.25,-.1,[BOX,ltypeshp.shx,x=-.1,s=.1],-.1,1
|
||||
# replacing BOX by shape index 132 (got index from an AutoCAD file),
|
||||
# ezdxf can't get shape index from ltypeshp.shx
|
||||
pattern="A,.25,-.1,[132,ltypeshp.shx,x=-.1,s=.1],-.1,1",
|
||||
description="Grenze eckig ----[]-----[]----[]-----[]----[]--",
|
||||
length=1.45, # required for complex line types
|
||||
})
|
||||
|
||||
doc.linetypes.add("GRENZE2",
|
||||
# linetype definition in acad.lin:
|
||||
# A,.25,-.1,[BOX,ltypeshp.shx,x=-.1,s=.1],-.1,1
|
||||
# replacing BOX by shape index 132 (got index from an AutoCAD file),
|
||||
# ezdxf can't get shape index from ltypeshp.shx
|
||||
pattern = "A,.25,-.1,[132,ltypeshp.shx,x=-.1,s=.1],-.1,1",
|
||||
description = "Límite1 ----0-----0----0-----0----0-----0--",
|
||||
length = 1.45, # required for complex line types
|
||||
})
|
||||
'''
|
||||
|
||||
def createFile(self, version='R2018'):
|
||||
import ezdxf
|
||||
from ezdxf.tools.standards import linetypes
|
||||
|
||||
# 1. Create a new document
|
||||
self.doc = ezdxf.new(version)
|
||||
# 2. Setup document:
|
||||
self.doc.header["$INSUNITS"] = 6
|
||||
self.doc.header['$MEASUREMENT'] = 1
|
||||
self.doc.header['$LUNITS'] = 2
|
||||
self.doc.header['$AUNITS'] = 0
|
||||
|
||||
# 3. Add new entities to the modelspace:
|
||||
self.msp = self.doc.modelspace()
|
||||
|
||||
print("linetypes: ", self.doc.linetypes)
|
||||
for name, desc, pattern in linetypes():
|
||||
if name not in self.doc.linetypes:
|
||||
self.doc.linetypes.add(name=name,
|
||||
pattern=pattern,
|
||||
description=desc,
|
||||
)
|
||||
|
||||
|
||||
self.doc.linetypes.add(name="FENCELINE1",
|
||||
pattern="A,.25,-.1,[132,ltypeshp.shx,x=-.1,s=.1],-.1,1",
|
||||
description="Límite1 ----0-----0----0-----0----0-----0--",
|
||||
length=1.45) # required for complex line types
|
||||
|
||||
def save(self):
|
||||
from os.path import exists
|
||||
file_exists = exists(self.filename)
|
||||
self.doc.saveas(self.filename)
|
||||
self.doc.save()
|
||||
|
||||
def createLayer(self, layerName="newLayer", layerColor=(0,0,0), layerLineType='CONTINUOUS'):
|
||||
layer = self.doc.layers.add(name=layerName, linetype=layerLineType)
|
||||
layer.rgb = layerColor
|
||||
return layer
|
||||
|
||||
def createBlock(self, blockName='newBlock'):
|
||||
# Create a block
|
||||
block = self.doc.blocks.new(name=blockName)
|
||||
return block
|
||||
|
||||
def insertBlock(self, name, point=(0.0, 0.0), rotation=0.0, xscale=0.0, yscale=0.0):
|
||||
if name == "":
|
||||
return None
|
||||
|
||||
return self.msp.add_blockref(name, point, dxfattribs={
|
||||
'xscale': xscale,
|
||||
'yscale': yscale,
|
||||
'rotation': rotation
|
||||
})
|
||||
|
||||
def createPolyline(self, wire):
|
||||
data = getWire(wire.Shape)
|
||||
lwp = self.msp.add_lwpolyline(data)
|
||||
return lwp
|
||||
|
||||
|
||||
def getWire(wire, nospline=False, width=.0):
|
||||
"""Return a list of DXF ready points and bulges from a wire.
|
||||
|
||||
It builds a list of points from the edges of a `wire`.
|
||||
If the edges are circular arcs, the "bulge" of that edge is calculated,
|
||||
for other cases, the bulge is considered zero.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
wire : Part::TopoShape ('Wire')
|
||||
A shape representing a wire.
|
||||
|
||||
nospline : bool, optional
|
||||
It defaults to `False`.
|
||||
If it is `True`, the edges of the wire are not considered as
|
||||
being one of `'BSplineCurve'`, `'BezierCurve'`, or `'Ellipse'`,
|
||||
and a simple point is added to the list.
|
||||
Otherwise, `getSplineSegs(edge)` is used to extract
|
||||
the points and add them to the list.
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of tuples
|
||||
It returns a list of tuples ``[(...), (...), ...]``
|
||||
where each tuple indicates a point with additional information
|
||||
besides the coordinates.
|
||||
Two types of tuples may be returned.
|
||||
|
||||
[(float, float, float, None, None, float), ...]
|
||||
When `lw` is `True` (`'lwpolyline'`)
|
||||
the first three values represent the coordinates of the point,
|
||||
the next two are `None`, and the last value is the bulge.
|
||||
|
||||
[((float, float, float), None, [None, None], float), ...]
|
||||
When `lw` is `False` (`'polyline'`)
|
||||
the first element is a tuple of three values that indicate
|
||||
the coordinates of the point, the next element is `None`,
|
||||
the next element is a list of two `None` values,
|
||||
and the last element is the value of the bulge.
|
||||
|
||||
See also
|
||||
--------
|
||||
calcBulge
|
||||
"""
|
||||
import Part
|
||||
import DraftGeomUtils
|
||||
import math
|
||||
|
||||
def fmt(vec, b=0.0):
|
||||
return (vec.x * 0.001, vec.y * 0.001, width, width, b)
|
||||
|
||||
points = []
|
||||
edges = Part.__sortEdges__(wire.Edges)
|
||||
for edge in edges:
|
||||
v1 = edge.Vertexes[0].Point
|
||||
if DraftGeomUtils.geomType(edge) == "Circle":
|
||||
# polyline bulge -> negative makes the arc go clockwise
|
||||
angle = edge.LastParameter - edge.FirstParameter
|
||||
bul = math.tan(angle / 4)
|
||||
if edge.Curve.Axis.dot(FreeCAD.Vector(0, 0, 1)) < 0:
|
||||
bul = -bul
|
||||
points.append(fmt(v1, bul))
|
||||
elif (DraftGeomUtils.geomType(edge) in ["BSplineCurve",
|
||||
"BezierCurve",
|
||||
"Ellipse"]) and (not nospline):
|
||||
spline = getSplineSegs(edge)
|
||||
spline.pop()
|
||||
for p in spline:
|
||||
points.append(fmt(p))
|
||||
else:
|
||||
points.append(fmt(v1))
|
||||
|
||||
v = edges[-1].Vertexes[-1].Point
|
||||
points.append(fmt(v))
|
||||
|
||||
return points
|
||||
|
||||
|
||||
def getArcData(edge):
|
||||
"""Return center, radius, start, and end angles of a circle-based edge.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
edge : Part::TopoShape ('Edge')
|
||||
An edge representing a circular arc, either open or closed.
|
||||
|
||||
Returns
|
||||
-------
|
||||
(tuple, float, float, float)
|
||||
It returns a tuple of four values; the first value is a tuple
|
||||
with the coordinates of the center `(x, y, z)`;
|
||||
the other three represent the magnitude of the radius,
|
||||
and the start and end angles in degrees that define the arc.
|
||||
|
||||
(tuple, float, 0, 0)
|
||||
If the number of vertices in the `edge` is only one, only the center
|
||||
point exists, so it's a full circumference; in this case, both
|
||||
angles are zero.
|
||||
"""
|
||||
ce = edge.Curve.Center
|
||||
radius = edge.Curve.Radius
|
||||
if len(edge.Vertexes) == 1:
|
||||
# closed circle
|
||||
return DraftVecUtils.tup(ce), radius, 0, 0
|
||||
else:
|
||||
# new method: recalculate ourselves as we cannot trust edge.Curve.Axis
|
||||
# or XAxis
|
||||
p1 = edge.Vertexes[0].Point
|
||||
p2 = edge.Vertexes[-1].Point
|
||||
v1 = p1.sub(ce)
|
||||
v2 = p2.sub(ce)
|
||||
# print(v1.cross(v2))
|
||||
# print(edge.Curve.Axis)
|
||||
# print(p1)
|
||||
# print(p2)
|
||||
# we can use Z check since arcs getting here will ALWAYS be in XY plane
|
||||
# Z can be 0 if the arc is 180 deg
|
||||
# if (v1.cross(v2).z >= 0) or (edge.Curve.Axis.z > 0):
|
||||
# Calculates the angles of the first and last points
|
||||
# in the circular arc, with respect to the global X axis.
|
||||
if edge.Curve.Axis.z > 0:
|
||||
# clockwise
|
||||
ang1 = -DraftVecUtils.angle(v1)
|
||||
ang2 = -DraftVecUtils.angle(v2)
|
||||
else:
|
||||
# counterclockwise
|
||||
ang2 = -DraftVecUtils.angle(v1)
|
||||
ang1 = -DraftVecUtils.angle(v2)
|
||||
|
||||
# obsolete method - fails a lot
|
||||
# if round(edge.Curve.Axis.dot(Vector(0, 0, 1))) == 1:
|
||||
# ang1, ang2 = edge.ParameterRange
|
||||
# else:
|
||||
# ang2, ang1 = edge.ParameterRange
|
||||
# if edge.Curve.XAxis != Vector(1, 0, 0):
|
||||
# ang1 -= DraftVecUtils.angle(edge.Curve.XAxis)
|
||||
# ang2 -= DraftVecUtils.angle(edge.Curve.XAxis)
|
||||
|
||||
return (DraftVecUtils.tup(ce), radius,
|
||||
math.degrees(ang1), math.degrees(ang2))
|
||||
|
||||
|
||||
class _PVPlantExportDXF(QtGui.QWidget):
|
||||
'''The editmode TaskPanel to select what you want to export'''
|
||||
|
||||
def __init__(self):
|
||||
# super(_PVPlantExportDXF, self).__init__()
|
||||
QtGui.QWidget.__init__(self)
|
||||
|
||||
import os
|
||||
# self.form:
|
||||
self.form = FreeCADGui.PySideUic.loadUi(
|
||||
os.path.join(os.path.dirname(os.path.realpath(__file__)), "exportDXF.ui"))
|
||||
# setWindowIcon(QtGui.QIcon(os.path.join(PVPlantResources.DirIcons, "convert.svg")))
|
||||
# self.form.buttonTo.clicked.connect(self.addTo)
|
||||
|
||||
self.layout = QtGui.QHBoxLayout(self)
|
||||
self.layout.setContentsMargins(4, 4, 4, 4)
|
||||
self.layout.addWidget(self.form)
|
||||
|
||||
self.form.buttonAcept.clicked.connect(self.onAceptClick)
|
||||
|
||||
path = os.path.join(os.path.dirname(FreeCAD.ActiveDocument.FileName), "outputs", "autocad")
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
self.filename = os.path.join(path, FreeCAD.ActiveDocument.Name) + ".dxf"
|
||||
|
||||
def createLayers(self):
|
||||
''' '''
|
||||
self.exporter.createLayer("Areas_Boundary", layerColor=(0, 125, 125), layerLineType="FENCELINE1")
|
||||
self.exporter.createLayer("Areas_Exclusion", layerColor=(255, 0, 0))
|
||||
self.exporter.createLayer("Areas_Offsets", layerColor=(128, 128, 255))
|
||||
|
||||
self.exporter.createLayer("Internal_Roads", layerColor=(128, 128, 128))
|
||||
self.exporter.createLayer("Internal_Roads_Axis", layerColor=(255, 255, 255), layerLineType="DASHEDX2")
|
||||
|
||||
|
||||
def writeArea(self):
|
||||
for area in FreeCAD.ActiveDocument.Boundary.Group:
|
||||
pol = self.exporter.createPolyline(area)
|
||||
pol.dxf.layer = "Areas_Boundary"
|
||||
|
||||
for area in FreeCAD.ActiveDocument.Exclusion.Group:
|
||||
pol = self.exporter.createPolyline(area)
|
||||
pol.dxf.layer = "Areas_Exclusion"
|
||||
|
||||
for area in FreeCAD.ActiveDocument.Offsets.Group:
|
||||
self.exporter.createPolyline(area)
|
||||
pol.dxf.layer = "Areas_Offsets"
|
||||
|
||||
def writeFrameSetups(self):
|
||||
import Part
|
||||
# 1. Profiles:
|
||||
profilelist = list()
|
||||
for ts in FreeCAD.ActiveDocument.Site.Frames:
|
||||
for poletype in ts.PoleType:
|
||||
if not (poletype in profilelist):
|
||||
profilelist.append(poletype)
|
||||
block = self.exporter.createBlock(poletype.Label)
|
||||
w = poletype.Base.Shape.Wires[0]
|
||||
w.Placement.Base = FreeCAD.Vector(w.Placement.Base).sub(w.BoundBox.Center)
|
||||
block.add_lwpolyline(getWire(w))
|
||||
|
||||
block.add_circle((0, 0), 0.2, dxfattribs={'color': 2})
|
||||
p = math.sin(math.radians(45)) * 0.2
|
||||
|
||||
block.add_line((-p, -p), (p, p), dxfattribs={"layer": "MyLines"})
|
||||
block.add_line((-p, p), (p, -p), dxfattribs={"layer": "MyLines"})
|
||||
|
||||
# 2. Frames
|
||||
for ts in FreeCAD.ActiveDocument.Site.Frames:
|
||||
w = max(ts.Shape.SubShapes[0].SubShapes[0].SubShapes[0].Faces, key=lambda x: x.Area)
|
||||
pts = [w.BoundBox.getPoint(i) for i in range(4)]
|
||||
pts.append(FreeCAD.Vector(pts[0]))
|
||||
w = Part.makePolygon(pts)
|
||||
w.Placement.Base = w.Placement.Base.sub(w.BoundBox.Center)
|
||||
mblockname = "Trina_TSM-DEG21C-20-6XXWp Vertex"
|
||||
mblock = self.exporter.createBlock(mblockname)
|
||||
mblock.add_lwpolyline(getWire(w))
|
||||
|
||||
rblock = self.exporter.createBlock(ts.Label)
|
||||
w = max(ts.Shape.SubShapes[0].SubShapes[1].SubShapes[0].Faces, key=lambda x: x.Placement.Base.z).Wires[0]
|
||||
w.Placement.Base = w.Placement.Base.sub(w.BoundBox.Center)
|
||||
rblock.add_lwpolyline(getWire(w))
|
||||
for module in ts.Shape.SubShapes[0].SubShapes[0].SubShapes:
|
||||
point = FreeCAD.Vector(module.BoundBox.Center) * 0.001
|
||||
point = point[:2]
|
||||
rblock.add_blockref(mblockname, point, dxfattribs={
|
||||
'xscale': .0,
|
||||
'yscale': .0,
|
||||
'rotation': 0})
|
||||
for ind in range(int(ts.NumberPole.Value)):
|
||||
point = ts.Shape.SubShapes[1].SubShapes[0].SubShapes[ind].Placement.Base * 0.001
|
||||
point = point[:2]
|
||||
name = ts.PoleType[ts.PoleSequence[ind]].Label
|
||||
rblock.add_blockref(name, point, dxfattribs={
|
||||
'xscale': .0,
|
||||
'yscale': .0,
|
||||
'rotation': 0})
|
||||
|
||||
def writeFrames(self):
|
||||
objects = findObjects('Tracker')
|
||||
for frame in objects:
|
||||
if hasattr(frame, "Setup"):
|
||||
point = frame.Placement.Base * 0.001
|
||||
point = point[:2]
|
||||
self.exporter.insertBlock(frame.Setup.Label, point=point, rotation=frame.AngleZ)
|
||||
|
||||
def writeRoads(self):
|
||||
objects = findObjects("Road")
|
||||
#rblock = self.exporter.createBlock("Internal_roads")
|
||||
for road in objects:
|
||||
base = self.exporter.createPolyline(road.Base)
|
||||
base.dxf.const_width = road.Width.Value * 0.001
|
||||
base.dxf.layer = "Internal_Roads"
|
||||
|
||||
axis = self.exporter.createPolyline(road.Base)
|
||||
axis.dxf.const_width = .2
|
||||
axis.dxf.layer = "Internal_Roads_Axis"
|
||||
#my_lines = doc.layers.get('MyLines')
|
||||
|
||||
def writeTrenches(self):
|
||||
objects = findObjects("Trench")
|
||||
# rblock = self.exporter.createBlock("Internal_roads")
|
||||
for obj in objects:
|
||||
base = self.exporter.createPolyline(obj.Base)
|
||||
base.dxf.const_width = obj.Width.Value * 0.001
|
||||
base.dxf.layer = "Trench"
|
||||
|
||||
def setup_layout4(self, doc):
|
||||
layout2 = doc.layouts.new("scale 1-1")
|
||||
# The default paperspace scale is 1:1
|
||||
# 1 mm printed is 1 drawing unit in paperspace
|
||||
# For most use cases this is the preferred scaling and important fact:
|
||||
# the paperspace scaling has no influence on the VIEWPORT scaling - this is
|
||||
# a total different topic, see example "viewports_in_paperspace.py"
|
||||
|
||||
layout2.page_setup(size=(297, 210),
|
||||
margins=(10, 10, 10, 10),
|
||||
units="mm",
|
||||
scale=(1, 1),
|
||||
#offset=(50, 50),
|
||||
)
|
||||
layout2.add_viewport(
|
||||
# center of viewport in paperspace units
|
||||
center=(100, 100),
|
||||
# viewport size in paperspace units
|
||||
size=(50, 50),
|
||||
# modelspace point to show in center of viewport in WCS
|
||||
view_center_point=(60, 40),
|
||||
# how much modelspace area to show in viewport in drawing units
|
||||
view_height=20,
|
||||
#status=2,
|
||||
)
|
||||
lower_left, upper_right = layout2.get_paper_limits()
|
||||
x1, y1 = lower_left
|
||||
x2, y2 = upper_right
|
||||
center = lower_left.lerp(upper_right)
|
||||
|
||||
# Add DXF entities to the "Layout1" in paperspace coordinates:
|
||||
layout2.add_line((x1, center.y), (x2, center.y)) # horizontal center line
|
||||
layout2.add_line((center.x, y1), (center.x, y2)) # vertical center line
|
||||
layout2.add_circle((0, 0), radius=5) # plot origin
|
||||
|
||||
def onAceptClick(self):
|
||||
self.exporter = exportDXF(self.filename)
|
||||
if self.exporter:
|
||||
self.exporter.createFile()
|
||||
self.createLayers()
|
||||
self.writeArea()
|
||||
self.writeFrameSetups()
|
||||
self.writeFrames()
|
||||
self.writeRoads()
|
||||
self.writeTrenches()
|
||||
self.setup_layout4(self.exporter.doc)
|
||||
|
||||
self.exporter.save()
|
||||
print(self.filename)
|
||||
|
||||
self.close()
|
||||
|
||||
|
||||
class _CommandExportDXF:
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "dxf.svg")),
|
||||
'Accel': "E, A",
|
||||
'MenuText': "Export to DXF",
|
||||
'ToolTip': QT_TRANSLATE_NOOP("Placement", "Export choosed layers to dxf")}
|
||||
|
||||
def Activated(self):
|
||||
taskd = _PVPlantExportDXF()
|
||||
taskd.setParent(FreeCADGui.getMainWindow())
|
||||
taskd.setWindowFlags(QtCore.Qt.Dialog or QtCore.Qt.Dialog)
|
||||
taskd.setWindowModality(QtCore.Qt.WindowModal)
|
||||
taskd.show()
|
||||
|
||||
def IsActive(self):
|
||||
if FreeCAD.ActiveDocument:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('exportDXF', _CommandExportDXF())
|
||||
77
Export/exportDXF.ui
Normal file
77
Export/exportDXF.ui
Normal file
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>formPVSyst</class>
|
||||
<widget class="QDialog" name="formPVSyst">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>715</width>
|
||||
<height>520</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Export to PVSyst</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab">
|
||||
<attribute name="title">
|
||||
<string>Tab 1</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_2">
|
||||
<attribute name="title">
|
||||
<string>Tab 2</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonAcept">
|
||||
<property name="text">
|
||||
<string>Aceptar</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="Accept">
|
||||
<normaloff>../../../../../.designer/backup</normaloff>../../../../../.designer/backup</iconset>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonCancel">
|
||||
<property name="text">
|
||||
<string>Cancelar</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
15
Export/exportGeoJson.py
Normal file
15
Export/exportGeoJson.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import geojson
|
||||
from geojson import Point, Feature, FeatureCollection, dump
|
||||
|
||||
point = Point((-115.81, 37.24))
|
||||
|
||||
features = []
|
||||
features.append(Feature(geometry=point, properties={"country": "Spain"}))
|
||||
|
||||
# add more features...
|
||||
# features.append(...)
|
||||
|
||||
feature_collection = FeatureCollection(features)
|
||||
|
||||
with open('myfile.geojson', 'w') as f:
|
||||
dump(feature_collection, f)
|
||||
470
Export/exportPF.py
Normal file
470
Export/exportPF.py
Normal file
@@ -0,0 +1,470 @@
|
||||
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
path_PF = r"C:\Program Files\DIgSILENT\PowerFactory 2021 SP2\Python\3.10"
|
||||
os.environ['PATH'] = path_PF + ";" + os.environ['PATH']
|
||||
os.add_dll_directory(path_PF)
|
||||
sys.path.append(path_PF)
|
||||
|
||||
import powerfactory as pf
|
||||
|
||||
app = pf.GetApplication()
|
||||
|
||||
print(app)
|
||||
|
||||
user = app.GetCurrentuser()
|
||||
project = app.ActivateProject('Nine-bus System') # Nombre del proyecto
|
||||
prj = app.GetActiveProject()
|
||||
|
||||
|
||||
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
path = r'C:\Program Files\DIgSILENT\PowerFactory 2021 SP2\Python\3.10'
|
||||
os.environ['PATH'] += (";" + path)
|
||||
sys.path.append(path)
|
||||
|
||||
import powerfactory as pf
|
||||
|
||||
app = pf.GetApplication()
|
||||
if app is None:
|
||||
raise Exception('getting Powerfactory application failed')
|
||||
|
||||
# app.Show()
|
||||
|
||||
user = app.GetCurrentUser()
|
||||
projects = user.GetContents('*.IntPrj', 0)
|
||||
names = [pro.GetAttribute("loc_name") for pro in projects]
|
||||
|
||||
res = app.ActivateProject("ProyectoPrueba")
|
||||
if res == 1: # no existe y hay crearlo
|
||||
proj = app.CreateProject("ProyectoPrueba", "GridNueva")
|
||||
else:
|
||||
proj = app.GetActiveProject()
|
||||
|
||||
#########
|
||||
# MODEL #
|
||||
#########
|
||||
# Network model
|
||||
netmod = app.GetProjectFolder('netmod')
|
||||
# Network data
|
||||
netdata = app.GetProjectFolder('netdat')
|
||||
# Diagrams
|
||||
diag_fold = app.GetProjectFolder('dia')
|
||||
# Variations
|
||||
var_fold = app.GetProjectFolder('scheme')
|
||||
|
||||
###########
|
||||
# LIBRARY #
|
||||
###########
|
||||
# Equipment library
|
||||
equip = app.GetProjectFolder('equip')
|
||||
# lines_fold = equip.GetContents('Lines')[0][0]
|
||||
# User defined models
|
||||
main_lib = equip.GetParent()
|
||||
# udm = main_lib.GetContents('User Defined Models*')[0][0]
|
||||
# Script library
|
||||
script_lib = app.GetProjectFolder('script')
|
||||
# Template library
|
||||
templ_lib = app.GetProjectFolder('templ')
|
||||
# Operational library
|
||||
oplib = app.GetProjectFolder('oplib')
|
||||
# Characteristics
|
||||
# opchar = oplib.GetContents('Characteristics')[0][0]
|
||||
# Thermal ratings
|
||||
therm_fold = app.GetProjectFolder('therm')
|
||||
# MVAr limit curves
|
||||
mvar_fold = app.GetProjectFolder('mvar')
|
||||
|
||||
##############
|
||||
# STUDY CASE #
|
||||
##############
|
||||
# Study cases
|
||||
op_scen = app.GetProjectFolder('scen')
|
||||
# Operational scenarios
|
||||
sc_fold = app.GetProjectFolder('study')
|
||||
|
||||
# Diagramas:
|
||||
schemas = diag_fold.GetContents('*.IntGrfnet', 0)
|
||||
sch = None
|
||||
for obj in schemas:
|
||||
if obj.loc_name == "miEsquema":
|
||||
sch = obj
|
||||
break
|
||||
|
||||
if sch is None:
|
||||
sch = diag_fold.CreateObject("IntGrfnet", "miEsquema")
|
||||
|
||||
# redes:
|
||||
print(netdata)
|
||||
grids = netdata.GetContents('*.ElmNet', 0)
|
||||
grid = grids[0]
|
||||
ln = grid.GetContents('*.ElmLne', 0)[0]
|
||||
print(ln)
|
||||
# print(ln.type_id)
|
||||
print(f"Terminal 1 de la línea {ln.loc_name}: {ln.bus1}")
|
||||
print(f"Terminal 2 de la línea {ln.loc_name}: {ln.bus2}")
|
||||
|
||||
|
||||
def createCable(name):
|
||||
''''''
|
||||
cable = equip.CreateObject("TypCab", name)
|
||||
cable.uline
|
||||
cable.typCon = "cmp" # Forma (cmp: Macizo, hol: orificio, seg: sermento)
|
||||
cable.diaCon = 5.0 # diámetro externo (double) (mm)
|
||||
|
||||
# Capas condutoras: conductor, funda, blindaje
|
||||
cable.cHasEl = [1, 1, 1] # Existe
|
||||
cable.materialCond = [] # Material ()
|
||||
cable.crho = [] # Resistividad 20ºC (double) (uOhm*cm)
|
||||
cable.my = [] # Premeabilidad relativa (double)
|
||||
cable.cThEl = [] # Espesor (dobule) (mm)
|
||||
cable.Cf = [] # Factor de Relleno (double) (%)
|
||||
cable.rpha = [] # Resistenca DC 20ºC Ohm/km
|
||||
|
||||
# Capas de aislamiento: Aislamiento, Funda Exterior, Cubierta
|
||||
cable.cHasIns = [0, 0, 0] # Existe
|
||||
cable.materialIns = [] # Material
|
||||
cable.ctand = [] # Factor de Pérdidas Dieléctricas
|
||||
cable.cepsr = [] # Permeavilidad relativa
|
||||
cable.thlns = [] # Espesor (mm)
|
||||
|
||||
# Capas semicoductoras: Nucleo, Aislamiento
|
||||
cable.cHasSc = [] # Existe
|
||||
cable.thSc = [] # Espesor (mm)
|
||||
cable.cAdvSc = [] # Avanzado
|
||||
cable.rhoSc = [] # Resistividad uOhm*cm
|
||||
cable.mySc = [] # Permeabilidad relativa
|
||||
cable.epsrSC = [] # Premitividad relativa
|
||||
|
||||
cable.manuf = "" # fabricante
|
||||
|
||||
# 1 Flujo de carga
|
||||
cable.tmax = 90.0 # Máx. temperatura (ºC)
|
||||
|
||||
# 2. Cortocircuito VDE/IEC
|
||||
cable.rtemp = 80.0 # temperatura final máxima (ºC)
|
||||
cable.Ithr = 0.0 # Corriente de corta duración 1s (kA)
|
||||
|
||||
# 3. Análisis de Cables
|
||||
cable.tmax_screen = 70.0 # max. temparatura operación (ºC)
|
||||
cable.eps_emiss = 0.9 # coeficiente de emisión en la superficie
|
||||
cable.iopt_treated = True # Secado e impregnado
|
||||
cable.tshc = 1.0 # Cálculo de cortocircuito adiabático - Duración de la falla (s)
|
||||
|
||||
return cable
|
||||
|
||||
|
||||
def createLine(name):
|
||||
elmlne = p.CreateObject("ElmPvsys", name)
|
||||
elmlne.type_id = "" # Tipo
|
||||
elmlne.bus1 = "" # terminal i
|
||||
elmlne.bus2 = "" # terminal j
|
||||
elmlne.nlnum = 1 # líneas en paralelo
|
||||
elmlne.dline = 0.8 # longitud de la línea (double) (km)
|
||||
elmlne.fline = 1.0 # factor de reduccion (double)
|
||||
elmlne.inAir = 0 # Medio (0: Tierra, 1: Aérea)
|
||||
elmlne.i_dist = 0 # Modelo de la línea (0: Parámetros Concentrados, 1: Parámetros Distribuidos)
|
||||
|
||||
return elmlne
|
||||
|
||||
|
||||
def createPVPanel(name):
|
||||
typpvpanel = equip.CreateObject("TypPvPanel", name)
|
||||
typpvpanel.Ppk = 500.0 # Potencia Pico MPP (double) (W)
|
||||
typpvpanel.Umpp = 80.0 # Tensión nominal MPP (double) (V)
|
||||
typpvpanel.Impp = 6.0 # Corriente nominal MPP (double) (A)
|
||||
typpvpanel.Uoc = 90.0 # Tensión de Circuito Abierto (double) (V)
|
||||
typpvpanel.Isc = 7.0 # Corriente de cortocircuito (double) (A)
|
||||
typpvpanel.material = 0 # Material: 0:mono-SI, 1:poli-Si, 2:samor-SI, 3: CIS, 4:CdTe, 5: otro
|
||||
typpvpanel.usetval = 1 # Usar valores típicos
|
||||
typpvpanel.cT = -0.4 # Coeficiente de tenperatura P (double) (%/Cº)
|
||||
typpvpanel.noct = 45.0 # SINTC (double) (Cº)
|
||||
|
||||
typpvpanel.manuf = "" # Fabricante
|
||||
typpypanel.chr_name = "" # Nombre característico
|
||||
typpypanel.appr_status = 1 # Estado: 0: No aprobado, 1: aprobado
|
||||
|
||||
return typpvpanel
|
||||
|
||||
|
||||
def createPV(name):
|
||||
elmpvsys = grid.CreateObject("ElmPvsys", name)
|
||||
elmpvsys.typ_id = None
|
||||
elmpvsys.bus1 = None # terminal ()
|
||||
elmpvsys.mode_pgi = 0 # modelo (0:Entrada de potencia Activa, 1: Cálculo solar)
|
||||
elmpvsys.phtech = 0 # tecnología (0: 3F, 1: 3F-T, 2:1F F-T, 3:1F F-N)
|
||||
elmpvsys.ngnum = 1 # Inversores en paralelo (int)
|
||||
elmpvsys.npnum = 1 # Paneles por incersro (int)
|
||||
elmpvsys.sgn = 1.0 # Potencia aparente (double) (kVA)
|
||||
elmpvsys.cosn = 0.8 # Factor de Potencia nominal (double)
|
||||
# elmpvsys.
|
||||
return elmpvsys
|
||||
|
||||
|
||||
def createBarra(name, uknom=33.0):
|
||||
typbar = equip.CreateObject("TypBar", name)
|
||||
typbar.uknow = uknom
|
||||
|
||||
# 1. Cortocircuito VDE/IEC
|
||||
typbar.Ithlim = 0 # Corriente Nominal de Corto tiempo (kA)
|
||||
typbar.Tkr = 1 # tiempo Corriente Nominal de Corto tiempo (s)
|
||||
typbar.Iplim = 0 # Corriente Pico de cortocircuito (kA)
|
||||
|
||||
return typbar
|
||||
|
||||
|
||||
def createTerminal(name):
|
||||
elmterm = grid.CreateObject("ElmTerm", name)
|
||||
elmterm.systype = 0 # Tipo de sistema (0: AC, 1: DC, 2: AC/BI)
|
||||
elmterm.iUsage = 0 # Uso (0:Busbar, 1:Junction, Node 2: Internal node)
|
||||
elmterm.phtech = "ABC" # Tecnología de Fases: ABC, ABC-N, BI, BI-N, 2F, 2F-N, 1F, 1F-N, N
|
||||
elmterm.uknom = 33.0 # Tensión Nominal Línea-Línea (double)
|
||||
elmterm.iEarth = False # Aterrizado (bool)
|
||||
# Load Flow:
|
||||
# 1. Control de tensión:
|
||||
elmterm.vtarget = 1.0 # Tensión destino (double) (p.u.)
|
||||
elmterm.Vtarget = 0.0 # Tensión destino (double) (kV)
|
||||
elmterm.dvmax = 0.0 # Max delta V (double) (%)
|
||||
elmterm.dvmin = 0.0 # Min delta V (double) (%)
|
||||
elmterm.ivpriority = -1 # prioriddad
|
||||
# 2. Límite de tensión del estado estable
|
||||
elmterm.vmax = 1.05 # Límite superior de tensión (p.u.)
|
||||
elmterm.vmin = 0.0 # Límite inferior de tensión (p.u.)
|
||||
# 3. Límites del cambio del paso de tensión
|
||||
elmterm.vstep_change = True # Límites del cambio del paso de tensión (bool)
|
||||
elmterm.vstep_n1 = 6.0 # n-1 (%)
|
||||
elmterm.vstep_n2 = 12.0 # n-2 (%)
|
||||
elmterm.vstep_bus = 12.0 # falla de barra (%)
|
||||
# Análisis de Arco Eléctrico:
|
||||
elmterm.iAccessLoc = False # Ubicación Accesible (bool)
|
||||
elmterm.isSoftConstr = False # Restrinción blanda (bool)
|
||||
|
||||
crearCelda()
|
||||
|
||||
return elmterm
|
||||
|
||||
|
||||
def createCubic(parent, name):
|
||||
stacubic = parent.CreateObject("StaCubic", name)
|
||||
stacubic.cterm = parent
|
||||
# stacubic.obj_id =
|
||||
# stacubic.plntObjs =
|
||||
return stacubic
|
||||
|
||||
|
||||
def createSwitch(name):
|
||||
staswitch = ""
|
||||
|
||||
|
||||
def createTransformer(name):
|
||||
|
||||
|
||||
#
|
||||
|
||||
def createTransformerType(name):
|
||||
typTr3 = equip.CreateObject("TypTr3", name)
|
||||
typTr3.nt3ph = 3 # Tecnología (2: Monofásico, 3: trifásico)
|
||||
|
||||
# Potencia Nominal:
|
||||
typTr3.strn3_h = 1.0 # Lado AT (double) (MVA)
|
||||
typTr3.strn3_m = 1.0 # Lado MT (double) (MVA)
|
||||
typTr3.strn3_l = 1.0 # Lado BT (double) (MVA)
|
||||
|
||||
# Tensión nominal:
|
||||
typTr3.utrn3_h = 0.0 # Lado AT (double) (kV)
|
||||
typTr3.utrn3_m = 0.0 # Lado MT (double) (kV)
|
||||
typTr3.utrn3_l = 0.0 # Lado BT (double) (kV)
|
||||
|
||||
# Grupo Vectorial:
|
||||
typTr3.tr3cn_h = "YN" # Lado AT (Y, YN, Z, ZN, D)
|
||||
typTr3.nt3ag_h = 0.0 # Lado AT - Ángulo de desfase (double) (*30deg)
|
||||
typTr3.tr3cn_m = "YN" # Lado MT (Y, YN, Z, ZN, D)
|
||||
typTr3.nt3ag_m = 0.0 # Lado MT - Ángulo de desfase (double) (*30deg)
|
||||
typTr3.tr3cn_l = "YN" # Lado BT (Y, YN, Z, ZN, D)
|
||||
typTr3.nt3ag_l = 0.0 # Lado BT - Ángulo de desfase (double) (*30deg)
|
||||
|
||||
# Impedancia de secuencia positiva
|
||||
# Tensión de Cortocircuito uk:
|
||||
typTr3.uktr3_h = 3.0 # AT-MT (double) (%)
|
||||
typTr3.uktr3_m = 3.0 # MT-BT (double) (%)
|
||||
typTr3.uktr3_l = 3.0 # BT-AT (double) (%)
|
||||
|
||||
# Pérdidas en el Cobre
|
||||
typTr3.pcut3_h = 0.0 # AT-MT (double) (kW)
|
||||
typTr3.pcut3_m = 0.0 # MT-BT (double) (kW)
|
||||
typTr3.pcut3_l = 0.0 # BT-AT (double) (kW)
|
||||
|
||||
# Impedancia de secuencia cero
|
||||
# Tensión de Cortocircuito uk0:
|
||||
typTr3.uk0hm = 3.0 # AT-MT (double) (%)
|
||||
typTr3.uk0ml = 3.0 # MT-BT (double) (%)
|
||||
typTr3.uk0hl = 3.0 # BT-AT (double) (%)
|
||||
|
||||
# Pérdidas en el Cobre
|
||||
typTr3.ur0hm = 0.0 # AT-MT (double) (kW)
|
||||
typTr3.ur0ml = 0.0 # MT-BT (double) (kW)
|
||||
typTr3.ur0hl = 0.0 # BT-AT (double) (kW)
|
||||
|
||||
|
||||
# print(dir(app))
|
||||
# print(dir(app.CreateProject))
|
||||
|
||||
|
||||
cable = equip.GetContents('*.TypCab', 0)[0]
|
||||
print(dir(cable))
|
||||
print(cable.cHasEl)
|
||||
print(cable.materialCond)
|
||||
print(cable.tab_con)
|
||||
|
||||
'''
|
||||
Run a load flow
|
||||
|
||||
#define project name and study case
|
||||
projName = '_TSI_Nine-bus System'
|
||||
study_case = '01_Study_Case.IntCase'
|
||||
|
||||
#activate project
|
||||
project = app.ActivateProject(projName)
|
||||
proj = app.GetActiveProject()
|
||||
|
||||
#get the study case folder and activate project
|
||||
oFolder_studycase = app.GetProjectFolder('study')
|
||||
oCase = oFolder_studycase.GetContents(study_case)[0]
|
||||
oCase.Activate()
|
||||
|
||||
#get load flow object and execute
|
||||
oLoadflow=app.GetFromStudyCase('ComLdf') #get load flow object
|
||||
oLoadflow.Execute() #execute load flow
|
||||
'''
|
||||
|
||||
'''
|
||||
Print the results for generators, lines, and buses
|
||||
|
||||
|
||||
#get the generators and their active/reactive power and loading
|
||||
Generators = app.GetCalcRelevantObjects('*.ElmSym')
|
||||
for gen in Generators: #loop through list
|
||||
name = getattr(gen, 'loc_name') # get name of the generator
|
||||
actPower = getattr(gen,'c:p') #get active power
|
||||
reacPower = getattr(gen,'c:q') #get reactive power
|
||||
genloading = getattr(gen,'c:loading') #get loading
|
||||
#print results
|
||||
print('%s: P = %.2f MW, Q = %.2f MVAr, loading = %.0f percent' %(name,actPower,reacPower,genloading))
|
||||
|
||||
print('-----------------------------------------')
|
||||
|
||||
#get the lines and print their loading
|
||||
Lines=app.GetCalcRelevantObjects('*.ElmLne')
|
||||
for line in Lines: #loop through list
|
||||
name = getattr(line, 'loc_name') # get name of the line
|
||||
value = getattr(line, 'c:loading') #get value for the loading
|
||||
#print results
|
||||
print('Loading of the line: %s = %.2f percent' %(name,value))
|
||||
|
||||
print('-----------------------------------------')
|
||||
|
||||
#get the buses and print their voltage
|
||||
Buses=app.GetCalcRelevantObjects('*.ElmTerm')
|
||||
for bus in Buses: #loop through list
|
||||
name = getattr(bus, 'loc_name') # get name of the bus
|
||||
amp = getattr(bus, 'm:u1') #get voltage magnitude
|
||||
phase = getattr(bus, 'm:phiu') #get voltage angle
|
||||
#print results
|
||||
print('Voltage at %s = %.2f pu %.2f deg' %(name,amp,phase))
|
||||
'''
|
||||
|
||||
'''
|
||||
Run an RMS simulation
|
||||
|
||||
#define project name and study case
|
||||
projName = '_TSI_nine_bus_system'
|
||||
study_case = '01_Study_Case.IntCase'
|
||||
|
||||
#activate project
|
||||
project = app.ActivateProject(projName)
|
||||
proj = app.GetActiveProject()
|
||||
|
||||
#get the study case folder and activate project
|
||||
oFolder_studycase = app.GetProjectFolder('study')
|
||||
oCase = oFolder_studycase.GetContents(study_case)[0]
|
||||
oCase.Activate()
|
||||
|
||||
# calculate initial conditions
|
||||
oInit = app.GetFromStudyCase('ComInc') #get initial condition calculation object
|
||||
oInit.Execute()
|
||||
|
||||
#run RMS-simulation
|
||||
oRms = app.GetFromStudyCase('ComSim') #get RMS-simulation object
|
||||
oRms.Execute()
|
||||
'''
|
||||
|
||||
'''
|
||||
Retrieve RMS results from PowerFactory in Python
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
def getResults():
|
||||
#get result file
|
||||
elmRes = app.GetFromStudyCase('*.ElmRes')
|
||||
app.ResLoadData(elmRes)
|
||||
|
||||
#Get number of rows and columns
|
||||
NrRow = app.ResGetValueCount(elmRes,0)
|
||||
|
||||
#get objects of interest
|
||||
oSG1 = app.GetCalcRelevantObjects('G1.ElmSym')[0]
|
||||
oBus1 = app.GetCalcRelevantObjects('Bus 1.ElmTerm')[0]
|
||||
oLine4_5 = app.GetCalcRelevantObjects('Line 4-5.ElmLne')[0]
|
||||
|
||||
#Get index of variable of interest
|
||||
ColIndex_time = app.ResGetIndex(elmRes,elmRes,'b:tnow')
|
||||
ColIndex_ut = app.ResGetIndex(elmRes,oSG1,'s:ut')
|
||||
ColIndex_P = app.ResGetIndex(elmRes,oSG1,'s:P1')
|
||||
ColIndex_Q = app.ResGetIndex(elmRes,oSG1,'s:Q1')
|
||||
ColIndex_speed = app.ResGetIndex(elmRes,oSG1,'s:xspeed')
|
||||
ColIndex_u_bus1 = app.ResGetIndex(elmRes,oBus1,'m:u')
|
||||
ColIndex_loading_line_4_5 = app.ResGetIndex(elmRes,oLine4_5,'c:loading')
|
||||
|
||||
#pre-allocate result variables
|
||||
result_time = np.zeros((NrRow,))
|
||||
result_ut = np.zeros((NrRow))
|
||||
result_P = np.zeros((NrRow))
|
||||
result_Q = np.zeros((NrRow))
|
||||
result_speed = np.zeros((NrRow))
|
||||
result_u_bus1 = np.zeros((NrRow))
|
||||
result_loading_line_4_5 = np.zeros((NrRow))
|
||||
|
||||
#get results for each time step
|
||||
for i in range(NrRow):
|
||||
result_time[i] = app.ResGetData(elmRes,i,ColIndex_time)[1]
|
||||
result_ut[i] = app.ResGetData(elmRes,i,ColIndex_ut)[1]
|
||||
result_P[i] = app.ResGetData(elmRes,i,ColIndex_P)[1]
|
||||
result_Q[i] = app.ResGetData(elmRes,i,ColIndex_Q)[1]
|
||||
result_speed[i] = app.ResGetData(elmRes,i,ColIndex_speed)[1]
|
||||
result_u_bus1[i] = app.ResGetData(elmRes,i,ColIndex_u_bus1)[1]
|
||||
result_loading_line_4_5[i] = app.ResGetData(elmRes,i,ColIndex_loading_line_4_5)[1]
|
||||
|
||||
results = pd.DataFrame()
|
||||
results['time'] = result_time
|
||||
results['P'] = result_P
|
||||
results['Q'] = result_Q
|
||||
results['ut'] = result_ut
|
||||
results['speed'] = result_speed
|
||||
results['u_bus1'] = result_u_bus1
|
||||
results['loading_line_4_5'] = result_loading_line_4_5
|
||||
return results
|
||||
|
||||
#query results
|
||||
RES = getResults()
|
||||
'''
|
||||
|
||||
## Graphical objects:
|
||||
|
||||
|
||||
|
||||
864
Export/exportPVSyst.py
Normal file
864
Export/exportPVSyst.py
Normal file
@@ -0,0 +1,864 @@
|
||||
# /**********************************************************************
|
||||
# * *
|
||||
# * 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 Arch
|
||||
import Draft
|
||||
import FreeCAD
|
||||
import Mesh
|
||||
import MeshPart
|
||||
import Part
|
||||
import numpy
|
||||
import os
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
from PySide import QtCore
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
from DraftTools import translate
|
||||
else:
|
||||
def translate(ctxt, txt):
|
||||
return txt
|
||||
|
||||
try:
|
||||
_fromUtf8 = QtCore.QString.fromUtf8
|
||||
except AttributeError:
|
||||
def _fromUtf8(s):
|
||||
return s
|
||||
|
||||
from PVPlantResources import DirIcons as DirIcons
|
||||
|
||||
## @package importDAE
|
||||
# \ingroup ARCH
|
||||
# \brief DAE (Collada) file format importer and exporter
|
||||
#
|
||||
# This module provides tools to import and export Collada (.dae) files.
|
||||
|
||||
__title__ = "FreeCAD Collada importer"
|
||||
__author__ = "Yorik van Havre"
|
||||
__url__ = "http://www.freecadweb.org"
|
||||
|
||||
try:
|
||||
# Python 2 forward compatibility
|
||||
range = xrange
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
|
||||
def checkCollada():
|
||||
"checks if collada if available"
|
||||
|
||||
global collada
|
||||
COLLADA = None
|
||||
try:
|
||||
import collada
|
||||
except ImportError:
|
||||
FreeCAD.Console.PrintError(translate("Arch", "pycollada not found, collada support is disabled.") + "\n")
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
# from ARCH:
|
||||
def triangulate(shape):
|
||||
"triangulates the given face"
|
||||
|
||||
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch")
|
||||
mesher = p.GetInt("ColladaMesher", 0)
|
||||
tessellation = p.GetFloat("ColladaTessellation", 1.0)
|
||||
grading = p.GetFloat("ColladaGrading", 0.3)
|
||||
segsperedge = p.GetInt("ColladaSegsPerEdge", 1)
|
||||
segsperradius = p.GetInt("ColladaSegsPerRadius", 2)
|
||||
secondorder = p.GetBool("ColladaSecondOrder", False)
|
||||
optimize = p.GetBool("ColladaOptimize", True)
|
||||
allowquads = p.GetBool("ColladaAllowQuads", False)
|
||||
|
||||
if mesher == 0:
|
||||
return shape.tessellate(tessellation)
|
||||
elif mesher == 1:
|
||||
return MeshPart.meshFromShape(Shape=shape, MaxLength=tessellation).Topology
|
||||
else:
|
||||
return MeshPart.meshFromShape(Shape=shape, GrowthRate=grading, SegPerEdge=segsperedge,
|
||||
SegPerRadius=segsperradius, SecondOrder=secondorder, Optimize=optimize,
|
||||
AllowQuad=allowquads).Topology
|
||||
|
||||
def export(exportList, filename, tessellation=1, colors=None):
|
||||
"""export(exportList,filename,tessellation=1,colors=None) -- exports FreeCAD contents to a DAE file.
|
||||
colors is an optional dictionary of objName:shapeColorTuple or objName:diffuseColorList elements
|
||||
to be used in non-GUI mode if you want to be able to export colors. Tessellation is used when breaking
|
||||
curved surfaces into triangles."""
|
||||
|
||||
if not checkCollada(): return
|
||||
|
||||
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch")
|
||||
scale = p.GetFloat("ColladaScalingFactor", 1.0)
|
||||
scale = scale * 0.001 # from millimeters (FreeCAD) to meters (Collada)
|
||||
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View")
|
||||
c = p.GetUnsigned("DefaultShapeColor", 4294967295)
|
||||
defaultcolor = (float((c >> 24) & 0xFF) / 255.0, float((c >> 16) & 0xFF) / 255.0, float((c >> 8) & 0xFF) / 255.0)
|
||||
colmesh = collada.Collada()
|
||||
colmesh.assetInfo.upaxis = collada.asset.UP_AXIS.Z_UP
|
||||
|
||||
# authoring info
|
||||
cont = collada.asset.Contributor()
|
||||
try:
|
||||
author = FreeCAD.ActiveDocument.CreatedBy
|
||||
except UnicodeEncodeError:
|
||||
author = FreeCAD.ActiveDocument.CreatedBy.encode("utf8")
|
||||
author = author.replace("<", "")
|
||||
author = author.replace(">", "")
|
||||
cont.author = author
|
||||
ver = FreeCAD.Version()
|
||||
appli = "PVPlant for FreeCAD" + ver[0] + "." + ver[1] + " build" + ver[2] + "\n"
|
||||
cont.authoring_tool = appli
|
||||
|
||||
colmesh.assetInfo.contributors.append(cont)
|
||||
colmesh.assetInfo.unitname = "meter"
|
||||
colmesh.assetInfo.unitmeter = 1.0
|
||||
|
||||
defaultmat = None
|
||||
objind = 0
|
||||
scenenodes = []
|
||||
|
||||
# TODO: cambiar lo de objeclist. Buscar los elementos que se necesitan exportar
|
||||
objectlist = Draft.get_group_contents(exportList, walls=True, addgroups=True)
|
||||
objectlist = Arch.pruneIncluded(objectlist)
|
||||
for obj in objectlist:
|
||||
findex = numpy.array([])
|
||||
m = None
|
||||
if obj.isDerivedFrom("Part::Feature"):
|
||||
print("exporting object ", obj.Name, obj.Shape)
|
||||
new_shape = obj.Shape.copy()
|
||||
new_shape.Placement = obj.getGlobalPlacement()
|
||||
m = Mesh.Mesh(triangulate(new_shape))
|
||||
elif obj.isDerivedFrom("Mesh::Feature"):
|
||||
print("exporting object ", obj.Name, obj.Mesh)
|
||||
m = obj.Mesh
|
||||
elif obj.isDerivedFrom("App::Part"):
|
||||
for child in obj.OutList:
|
||||
objectlist.append(child)
|
||||
continue
|
||||
else:
|
||||
continue
|
||||
|
||||
if m:
|
||||
Topology = m.Topology
|
||||
Facets = m.Facets
|
||||
|
||||
# vertex indices
|
||||
vindex = numpy.empty(len(Topology[0]) * 3)
|
||||
for i in range(len(Topology[0])):
|
||||
v = Topology[0][i]
|
||||
vindex[list(range(i * 3, i * 3 + 3))] = (v.x * scale, v.y * scale, v.z * scale)
|
||||
|
||||
# normals
|
||||
nindex = numpy.empty(len(Facets) * 3)
|
||||
for i in range(len(Facets)):
|
||||
n = Facets[i].Normal
|
||||
nindex[list(range(i * 3, i * 3 + 3))] = (n.x, n.y, n.z)
|
||||
|
||||
# face indices
|
||||
findex = numpy.empty(len(Topology[1]) * 6, numpy.int64)
|
||||
for i in range(len(Topology[1])):
|
||||
f = Topology[1][i]
|
||||
findex[list(range(i * 6, i * 6 + 6))] = (f[0], i, f[1], i, f[2], i)
|
||||
|
||||
print(len(vindex), " vert indices, ", len(nindex), " norm indices, ", len(findex), " face indices.")
|
||||
vert_src = collada.source.FloatSource("cubeverts-array" + str(objind), vindex, ('X', 'Y', 'Z'))
|
||||
normal_src = collada.source.FloatSource("cubenormals-array" + str(objind), nindex, ('X', 'Y', 'Z'))
|
||||
geom = collada.geometry.Geometry(colmesh, "geometry" + str(objind), obj.Name, [vert_src, normal_src])
|
||||
input_list = collada.source.InputList()
|
||||
input_list.addInput(0, 'VERTEX', "#cubeverts-array" + str(objind))
|
||||
input_list.addInput(1, 'NORMAL', "#cubenormals-array" + str(objind))
|
||||
matnode = None
|
||||
matref = "materialref"
|
||||
if hasattr(obj, "Material"):
|
||||
if obj.Material:
|
||||
if hasattr(obj.Material, "Material"):
|
||||
if "DiffuseColor" in obj.Material.Material:
|
||||
kd = tuple([float(k) for k in obj.Material.Material["DiffuseColor"].strip("()").split(",")])
|
||||
effect = collada.material.Effect("effect_" + obj.Material.Name, [], "phong", diffuse=kd,
|
||||
specular=(1, 1, 1))
|
||||
mat = collada.material.Material("mat_" + obj.Material.Name, obj.Material.Name, effect)
|
||||
colmesh.effects.append(effect)
|
||||
colmesh.materials.append(mat)
|
||||
matref = "ref_" + obj.Material.Name
|
||||
matnode = collada.scene.MaterialNode(matref, mat, inputs=[])
|
||||
|
||||
if not matnode:
|
||||
if colors:
|
||||
if obj.Name in colors:
|
||||
color = colors[obj.Name]
|
||||
if color:
|
||||
if isinstance(color[0], tuple):
|
||||
# this is a diffusecolor. For now, use the first color - #TODO: Support per-face colors
|
||||
color = color[0]
|
||||
# print("found color for obj",obj.Name,":",color)
|
||||
kd = color[:3]
|
||||
effect = collada.material.Effect("effect_" + obj.Name, [], "phong", diffuse=kd,
|
||||
specular=(1, 1, 1))
|
||||
mat = collada.material.Material("mat_" + obj.Name, obj.Name, effect)
|
||||
colmesh.effects.append(effect)
|
||||
colmesh.materials.append(mat)
|
||||
matref = "ref_" + obj.Name
|
||||
matnode = collada.scene.MaterialNode(matref, mat, inputs=[])
|
||||
elif FreeCAD.GuiUp:
|
||||
if hasattr(obj.ViewObject, "ShapeColor"):
|
||||
kd = obj.ViewObject.ShapeColor[:3]
|
||||
effect = collada.material.Effect("effect_" + obj.Name, [], "phong", diffuse=kd, specular=(1, 1, 1))
|
||||
mat = collada.material.Material("mat_" + obj.Name, obj.Name, effect)
|
||||
colmesh.effects.append(effect)
|
||||
colmesh.materials.append(mat)
|
||||
matref = "ref_" + obj.Name
|
||||
matnode = collada.scene.MaterialNode(matref, mat, inputs=[])
|
||||
|
||||
if not matnode:
|
||||
if not defaultmat:
|
||||
effect = collada.material.Effect("effect_default", [], "phong", diffuse=defaultcolor,
|
||||
specular=(1, 1, 1))
|
||||
defaultmat = collada.material.Material("mat_default", "default_material", effect)
|
||||
colmesh.effects.append(effect)
|
||||
colmesh.materials.append(defaultmat)
|
||||
matnode = collada.scene.MaterialNode(matref, defaultmat, inputs=[])
|
||||
|
||||
triset = geom.createTriangleSet(findex, input_list, matref)
|
||||
geom.primitives.append(triset)
|
||||
colmesh.geometries.append(geom)
|
||||
geomnode = collada.scene.GeometryNode(geom, [matnode])
|
||||
node = collada.scene.Node("node" + str(objind), children=[geomnode])
|
||||
scenenodes.append(node)
|
||||
objind += 1
|
||||
|
||||
myscene = collada.scene.Scene("PVScene", scenenodes)
|
||||
colmesh.scenes.append(myscene)
|
||||
colmesh.scene = myscene
|
||||
colmesh.write(filename)
|
||||
|
||||
FreeCAD.Console.PrintMessage(translate("Arch", "file %s successfully created.") % filename)
|
||||
|
||||
def exportToDAE(path):
|
||||
filename = path + ".dae"
|
||||
|
||||
def exportToPVC(path, exportTerrain = False):
|
||||
filename = path + ".pvc"
|
||||
scale = 0.001 # from millimeters (FreeCAD) to meters (Collada)
|
||||
|
||||
from xml.etree.ElementTree import Element, SubElement
|
||||
import datetime
|
||||
|
||||
generated_on = str(datetime.datetime.now())
|
||||
|
||||
site = None
|
||||
tmp = FreeCAD.ActiveDocument.getObjectsByLabel('Site')
|
||||
for obj in tmp:
|
||||
if obj.Name.startswith("Site"):
|
||||
site = obj
|
||||
break
|
||||
terrain = None
|
||||
center = None
|
||||
if not(site.Terrain is None):
|
||||
terrain = site.Terrain.Mesh
|
||||
center = terrain.BoundBox.Center
|
||||
else:
|
||||
center = FreeCAD.Vector()
|
||||
|
||||
try:
|
||||
author = FreeCAD.ActiveDocument.CreatedBy
|
||||
except UnicodeEncodeError:
|
||||
author = FreeCAD.ActiveDocument.CreatedBy.encode("utf8")
|
||||
|
||||
author = author.replace("<", "")
|
||||
author = author.replace(">", "")
|
||||
ver = FreeCAD.Version()
|
||||
appli = "PVPlant for FreeCAD" + ver[0] + "." + ver[1] + " build" + ver[2] + "\n"
|
||||
|
||||
# xml: Configure one attribute with set()
|
||||
root = Element('COLLADA')
|
||||
root.set('xmlns:xsd', 'http://www.w3.org/2001/XMLSchema')
|
||||
root.set('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance')
|
||||
root.set('version', '1.4.1')
|
||||
root.set('xmlns', 'http://www.collada.org/2005/11/COLLADASchema')
|
||||
#root.append(Comment('Generated by ElementTree_csv_to_xml.py for PyMOTW'))
|
||||
|
||||
# xml: 1. Asset:
|
||||
asset = SubElement(root, 'asset')
|
||||
asset_contributor = SubElement(asset, 'contributor')
|
||||
asset_contributor_autor = SubElement(asset_contributor, 'autor')
|
||||
#asset_contributor_autor.text = author
|
||||
asset_contributor_authoring_tool = SubElement(asset_contributor, 'authoring_tool')
|
||||
#asset_contributor_authoring_tool.text = appli
|
||||
asset_contributor_comments = SubElement(asset_contributor, 'comments')
|
||||
asset_keywords = SubElement(asset, 'keywords')
|
||||
asset_revision = SubElement(asset, 'revision')
|
||||
asset_subject = SubElement(asset, 'subject')
|
||||
asset_tittle = SubElement(asset, 'title')
|
||||
#asset_tittle.text = FreeCAD.ActiveDocument.Name
|
||||
asset_unit = SubElement(asset, 'unit')
|
||||
asset_unit.set('meter', '0.001')
|
||||
asset_unit.set('name', 'millimeter')
|
||||
|
||||
# xml: 2. library_materials:
|
||||
library_materials = SubElement(root, 'library_materials')
|
||||
buf = ['Frames', 'Tree_trunk', 'Tree_crown', 'Topography_mesh']
|
||||
|
||||
for i in range(0, len(buf)):
|
||||
material = SubElement(library_materials, 'material')
|
||||
material.set('id', 'Material{0}'.format(i))
|
||||
material.set('name', buf[i])
|
||||
material_effect = SubElement(material, 'instance_effect')
|
||||
material_effect.set('url', '#Material{0}-fx'.format(i))
|
||||
|
||||
# xml: 3. library_effects:
|
||||
library_effects = SubElement(root, 'library_effects')
|
||||
buf = ['0.250000 0.500000 0.000000 1.000000',
|
||||
'0.500000 0.375000 0.250000 1.000000',
|
||||
'0.250000 1.000000 0.000000 1.000000',
|
||||
'0.250000 1.000000 0.000000 1.000000']
|
||||
|
||||
for i in range(0, len(buf)):
|
||||
effect = SubElement(library_effects, 'effect')
|
||||
effect.set('id', 'Material{0}-fx'.format(i))
|
||||
effect.set('name', 'Material{0}'.format(i))
|
||||
profile_COMMON = SubElement(effect, 'profile_COMMON')
|
||||
library_effects_effect_technique = SubElement(profile_COMMON, 'technique')
|
||||
library_effects_effect_technique.set('sid', 'standard')
|
||||
library_effects_effect_technique_lambert = SubElement(library_effects_effect_technique, 'lambert')
|
||||
library_effects_effect_technique_lambert_emission = SubElement(library_effects_effect_technique_lambert,
|
||||
'emission')
|
||||
library_effects_effect_technique_lambert_emission_color = SubElement(
|
||||
library_effects_effect_technique_lambert_emission, 'color')
|
||||
library_effects_effect_technique_lambert_emission_color.set('sid', 'emission')
|
||||
library_effects_effect_technique_lambert_emission_color.text = '0.000000 0.000000 0.000000 1.000000'
|
||||
ambient = SubElement(library_effects_effect_technique_lambert, 'ambient')
|
||||
ambient_color = SubElement(ambient, 'color')
|
||||
ambient_color.set('sid', 'ambient')
|
||||
ambient_color.text = '0.200000 0.200000 0.200000 1.000000'
|
||||
diffuse = SubElement(library_effects_effect_technique_lambert, 'diffuse')
|
||||
diffuse_color = SubElement(diffuse, 'color')
|
||||
diffuse_color.set('sid', 'diffuse')
|
||||
diffuse_color.text = buf[i]
|
||||
transparent = SubElement(library_effects_effect_technique_lambert, 'transparent')
|
||||
transparent.set('opaque', 'RGB_ZERO')
|
||||
transparent_color = SubElement(transparent, 'color')
|
||||
transparent_color.set('sid', 'transparent')
|
||||
transparent_color.text = '0.000000 0.000000 0.000000 1.000000'
|
||||
transparency = SubElement(library_effects_effect_technique_lambert, 'transparency')
|
||||
transparency_value = SubElement(transparency, 'float')
|
||||
transparency_value.set('sid', 'transparency')
|
||||
transparency_value.text = '0'
|
||||
|
||||
# xml: 4. library_geometries:
|
||||
library_geometries = SubElement(root, 'library_geometries')
|
||||
def add_geometry(objtype, vindex, findex, objind = 0, centers = None):
|
||||
|
||||
isFrame = False
|
||||
if objtype == 0:
|
||||
geometryName = 'Frame'
|
||||
referenceSTR = 'frame'
|
||||
isFrame = True
|
||||
elif objtype == 1:
|
||||
geometryName = 'ShadowMesh' ## --> ???
|
||||
referenceSTR = 'ShadowMesh' ## --> ???
|
||||
elif objtype == 2:
|
||||
geometryName = 'TerrainMesh'
|
||||
referenceSTR = 'TerrainMesh'
|
||||
|
||||
geometry = SubElement(library_geometries, 'geometry')
|
||||
geometry.set('id', geometryName + '{0}'.format(objind))
|
||||
mesh = SubElement(geometry, 'mesh')
|
||||
|
||||
source = SubElement(mesh, 'source')
|
||||
source.set('id', referenceSTR + '{0}MeshSource'.format(objind))
|
||||
float_array = SubElement(source, 'float_array')
|
||||
float_array.set('id', referenceSTR + '{0}FloatArray'.format(objind))
|
||||
float_array.set('count', '{0}'.format(len(vindex)))
|
||||
float_array.text = "" # vindex
|
||||
for ver in vindex:
|
||||
if len(float_array.text) > 0:
|
||||
float_array.text += ' '
|
||||
float_array.text += '{0:.6f}'.format(ver)
|
||||
|
||||
technique_common = SubElement(source, 'technique_common')
|
||||
accessor = SubElement(technique_common, 'accessor')
|
||||
accessor.set('count', '{0}'.format(len(vindex)))
|
||||
accessor.set('source', '#' + referenceSTR + '{0}FloatArray'.format(objind))
|
||||
accessor.set('stride', '3')
|
||||
param = SubElement(accessor, 'param')
|
||||
param.set('name', 'X')
|
||||
param.set('type', 'float')
|
||||
param = SubElement(accessor, 'param')
|
||||
param.set('name', 'Y')
|
||||
param.set('type', 'float')
|
||||
param = SubElement(accessor, 'param')
|
||||
param.set('name', 'Z')
|
||||
param.set('type', 'float')
|
||||
|
||||
vertices = SubElement(mesh, 'vertices')
|
||||
vertices.set('id', referenceSTR + '{0}VerticesSource'.format(objind))
|
||||
input = SubElement(vertices, "input")
|
||||
input.set('semantic', 'POSITION')
|
||||
input.set('source', '#' + referenceSTR + '{0}MeshSource'.format(objind))
|
||||
|
||||
triangles = SubElement(mesh, 'triangles')
|
||||
triangles.set('count', '0')
|
||||
triangles.set('material', 'Material0')
|
||||
input = SubElement(triangles, "input")
|
||||
input.set('offset', '0')
|
||||
input.set('semantic', 'VERTEX')
|
||||
input.set('source', '#' + referenceSTR + '{0}VerticesSource'.format(objind))
|
||||
p = SubElement(triangles, "p")
|
||||
p.text = ''
|
||||
for f in findex:
|
||||
if len(p.text) > 0:
|
||||
p.text += ' '
|
||||
p.text += '{0}'.format(f)
|
||||
|
||||
if isFrame:
|
||||
frame = SubElement(mesh, 'tracker_parameters' if isTracker else 'frame_parameters')
|
||||
module_width = SubElement(frame, "module_width")
|
||||
module_width.text = '{0}'.format(int(obj.Setup.ModuleWidth.Value))
|
||||
module_height = SubElement(frame, "module_height")
|
||||
module_height.text = '{0}'.format(int(obj.Setup.ModuleHeight.Value))
|
||||
module_x_spacing = SubElement(frame, "module_x_spacing")
|
||||
module_x_spacing.text = '{0}'.format(int(obj.Setup.ModuleColGap.Value))
|
||||
module_y_spacing = SubElement(frame, "module_y_spacing")
|
||||
module_y_spacing.text = '{0}'.format(int(obj.Setup.ModuleRowGap.Value))
|
||||
module_manufacturer = SubElement(frame, "module_manufacturer")
|
||||
module_manufacturer.text = 'generic'
|
||||
module_name = SubElement(frame, "module_name")
|
||||
module_name.text = 'generic'
|
||||
if isTracker:
|
||||
tracker_type = SubElement(frame, 'tracker_type')
|
||||
tracker_type.text = 'single_axis_trackers'
|
||||
axis = SubElement(frame, 'axis_vertices')
|
||||
for ind in range(0, len(centers)):
|
||||
array = SubElement(axis, 'float_array')
|
||||
array.set('id', 'tracker{0}AxisFloatArray1'.format(ind))
|
||||
array.set('count', '3')
|
||||
array.text = '{0:.6f} {1:.6f} {2:.6f}'.format(centers[i].x, centers[i].y, centers[i].z)
|
||||
|
||||
min_phi = SubElement(frame, 'min_phi')
|
||||
min_phi.text = '{0}'.format(int(obj.Setup.MinPhi.Value))
|
||||
max_phi = SubElement(frame, 'max_phi')
|
||||
max_phi.text = '{0}'.format(int(obj.Setup.MaxPhi.Value))
|
||||
min_theta = SubElement(frame, 'min_theta')
|
||||
min_theta.text = '{0}'.format(0)
|
||||
max_theta = SubElement(frame, 'max_theta')
|
||||
max_theta.text = '{0}'.format(0)
|
||||
|
||||
# xml: 5. library_visual_scenes:
|
||||
instance_geometry = SubElement(node, 'instance_geometry')
|
||||
instance_geometry.set('url', geometryName + '{0}'.format(objind))
|
||||
|
||||
bind_material = SubElement(instance_geometry, 'bind_material')
|
||||
technique_common = SubElement(bind_material, 'technique_common')
|
||||
instance_material = SubElement(technique_common, 'instance_material')
|
||||
instance_material.set('symbol', 'Material0')
|
||||
instance_material.set('target', '#Material0')
|
||||
instance_material = SubElement(technique_common, 'instance_material')
|
||||
instance_material.set('symbol', 'Material1')
|
||||
instance_material.set('target', '#Material1')
|
||||
instance_material = SubElement(technique_common, 'instance_material')
|
||||
instance_material.set('symbol', 'Material2')
|
||||
instance_material.set('target', '#Material2')
|
||||
|
||||
# xml: 5. library_visual_scenes:
|
||||
library_visual_scenes = SubElement(root, 'library_visual_scenes')
|
||||
visual_scene = SubElement(library_visual_scenes, 'visual_scene')
|
||||
visual_scene.set('id', '')
|
||||
visual_scene.set('name', '')
|
||||
|
||||
node = SubElement(visual_scene, 'node')
|
||||
node.set('id', 'Fbx_Root')
|
||||
node.set('name', 'Fbx_Root')
|
||||
node.set('sid', 'Fbx_Root')
|
||||
matrix = SubElement(node, 'matrix')
|
||||
matrix.set('sid', 'matrix')
|
||||
matrix.text = '1.000000 0.000000 0.000000 0.000000 0.000000 0.000000 -1.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 0.000000 0.000000 1.000000'
|
||||
node = SubElement(node, 'node')
|
||||
node.set('id', 'PVcase52')
|
||||
node.set('name', 'PVcase52')
|
||||
node.set('sid', 'PVcase52')
|
||||
matrix = SubElement(node, 'matrix')
|
||||
matrix.set('sid', 'matrix')
|
||||
matrix.text = '1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000'
|
||||
|
||||
extra = SubElement(visual_scene, 'extra')
|
||||
technique = SubElement(extra, 'technique')
|
||||
technique.set('profile', 'MAX3D')
|
||||
frame_rate = SubElement(technique, 'frame_rate')
|
||||
frame_rate.text = '30.000000'
|
||||
technique = SubElement(extra, 'technique')
|
||||
technique.set('profile', 'FCOLLADA')
|
||||
start_time = SubElement(technique, 'start_time')
|
||||
start_time.text = '0.000000'
|
||||
end_time = SubElement(technique, 'end_time')
|
||||
end_time.text = '1.000000'
|
||||
|
||||
# xml: 6. scene:
|
||||
scene = SubElement(root, 'scene')
|
||||
instance = SubElement(scene, 'instance_visual_scene')
|
||||
instance.set('url', '#')
|
||||
|
||||
full_list_of_objects = FreeCAD.ActiveDocument.Objects
|
||||
|
||||
# CASO 1 - FRAMES:
|
||||
frameType = site.Frames
|
||||
frame_setup = {"type": [],
|
||||
"footprint": []}
|
||||
for obj in frameType:
|
||||
frame_setup["type"] = obj
|
||||
frame_setup["footprint"] = ""
|
||||
|
||||
objind = 0
|
||||
# TODO: revisar
|
||||
for type in frameType:
|
||||
isTracker = "tracker" in type.Proxy.Type.lower()
|
||||
#TODO: Sólo para los proyectos de NAcho. Borrar
|
||||
isTracker = False
|
||||
|
||||
objectlist = FreeCAD.ActiveDocument.findObjects(Name="Tracker")
|
||||
tmp = []
|
||||
for obj in objectlist:
|
||||
if obj.Name.startswith("TrackerSetup"):
|
||||
continue
|
||||
else:
|
||||
tmp.append(obj)
|
||||
objectlist = tmp.copy()
|
||||
|
||||
for obj in objectlist:
|
||||
if obj.Setup == type:
|
||||
findex = numpy.array([])
|
||||
|
||||
modules = obj.Setup.Shape.SubShapes[0].SubShapes[0]
|
||||
pts = []
|
||||
for i in range(4):
|
||||
pts.append(modules.BoundBox.getPoint(i))
|
||||
|
||||
# temp -----
|
||||
if obj.Tilt.Value != 0:
|
||||
zz = int(pts[0].z) - (obj.Setup.MainBeamHeight.Value / 2 + obj.Setup.BeamHeight.Value)
|
||||
for p in pts:
|
||||
p.z -= zz
|
||||
new_shape = Part.Face(Part.makePolygon(pts))
|
||||
new_shape.Placement.Rotation.setEulerAngles("XYZ", obj.Tilt.Value, 0, 0)
|
||||
pts = []
|
||||
for ver in new_shape.Vertexes:
|
||||
p = ver.Point
|
||||
p.z += zz
|
||||
pts.append(p)
|
||||
# end temp -----
|
||||
|
||||
new_shape = Part.Face(Part.makePolygon(pts))
|
||||
new_shape.Placement = obj.Placement.copy()
|
||||
m = Mesh.Mesh(triangulate(new_shape))
|
||||
centers = []
|
||||
|
||||
if isTracker:
|
||||
minLengths = []
|
||||
for ed in new_shape.Edges:
|
||||
minLengths.append(ed.Length)
|
||||
minLength = min(minLengths)
|
||||
for ed in new_shape.Edges:
|
||||
if ed.Length == minLength:
|
||||
centers.append(ed.CenterOfMass)
|
||||
if m:
|
||||
Topology = m.Topology
|
||||
|
||||
# 1. vertex indices
|
||||
vindex = numpy.empty(len(Topology[0]) * 3)
|
||||
for i in range(len(Topology[0])):
|
||||
v = Topology[0][i]
|
||||
vindex[list(range(i * 3, i * 3 + 3))] = (-(v.x - center.x) * scale, (v.z - center.z) * scale,
|
||||
(v.y - center.y) * scale)
|
||||
|
||||
# 2. face indices
|
||||
findex = numpy.empty(len(Topology[1]) * 3, numpy.int64)
|
||||
for i in range(len(Topology[1])):
|
||||
f = Topology[1][i]
|
||||
findex[list(range(i * 3, i * 3 + 3))] = (f[0], f[1], f[2])
|
||||
|
||||
add_geometry(0, vindex, findex, objind, centers if isTracker else None)
|
||||
objind += 1
|
||||
|
||||
# CASE 2: Shadow objects
|
||||
#objectlist = FreeCAD.ActiveDocument.findObjects(Label = "Tree") # TODO: Cambiar label por name
|
||||
objectlist=[]
|
||||
for obj in FreeCAD.ActiveDocument.Objects:
|
||||
if obj.Name.startswith("Tracker"):
|
||||
continue
|
||||
else:
|
||||
objectlist.append(obj)
|
||||
objind = 0
|
||||
|
||||
for obj in objectlist:
|
||||
findex = numpy.array([])
|
||||
m = None
|
||||
if obj.isDerivedFrom("Part::Feature"):
|
||||
new_shape = obj.Shape.copy()
|
||||
new_shape.Placement = obj.getGlobalPlacement()
|
||||
m = Mesh.Mesh(triangulate(new_shape))
|
||||
elif obj.isDerivedFrom("Mesh::Feature"):
|
||||
m = obj.Mesh
|
||||
elif obj.isDerivedFrom("App::Part"):
|
||||
for child in obj.OutList:
|
||||
objectlist.append(child)
|
||||
continue
|
||||
else:
|
||||
continue
|
||||
|
||||
if m:
|
||||
Topology = m.Topology
|
||||
# vertex indices
|
||||
vindex = numpy.empty(len(Topology[0]) * 3)
|
||||
for i in range(len(Topology[0])):
|
||||
v = Topology[0][i]
|
||||
vindex[list(range(i * 3, i * 3 + 3))] = (-(v.x - center.x) * scale, (v.z - center.z) * scale,
|
||||
(v.y - center.y) * scale)
|
||||
|
||||
# face indices
|
||||
findex = numpy.empty(len(Topology[1]) * 3, numpy.int64)
|
||||
for i in range(len(Topology[1])):
|
||||
f = Topology[1][i]
|
||||
findex[list(range(i * 3, i * 3 + 3))] = (f[0], f[1], f[2])
|
||||
|
||||
add_geometry(1, vindex, findex, objind)
|
||||
objind += 1
|
||||
|
||||
|
||||
# CASE 3: Terrain
|
||||
# TODO: ver si se puede partir en varias mesh para que trabaje más rápido
|
||||
if exportTerrain:
|
||||
m = terrain
|
||||
if m:
|
||||
Topology = m.Topology
|
||||
# Facets = m.Facets
|
||||
|
||||
# vertex indices
|
||||
vindex = numpy.empty(len(Topology[0]) * 3)
|
||||
for i in range(len(Topology[0])):
|
||||
v = Topology[0][i]
|
||||
vindex[list(range(i * 3, i * 3 + 3))] = (-v.x * scale, v.z * scale, v.y * scale)
|
||||
|
||||
# face indices
|
||||
findex = numpy.empty(len(Topology[1]) * 3, numpy.int64)
|
||||
for i in range(len(Topology[1])):
|
||||
f = Topology[1][i]
|
||||
findex[list(range(i * 3, i * 3 + 3))] = (f[0], f[1], f[2])
|
||||
|
||||
add_geometry(2, vindex, findex)
|
||||
|
||||
# xml: 5. library_visual_scenes: ¿¿¿¿¿???????
|
||||
'''
|
||||
instance_geometry = SubElement(node, 'instance_geometry')
|
||||
instance_geometry.set('url', 'TerrainMesh{0}'.format(0))
|
||||
|
||||
bind_material = SubElement(instance_geometry, 'bind_material')
|
||||
technique_common = SubElement(bind_material, 'technique_common')
|
||||
instance_material = SubElement(technique_common, 'instance_material')
|
||||
instance_material.set('symbol', 'Material0')
|
||||
instance_material.set('target', '#Material0')
|
||||
instance_material = SubElement(technique_common, 'instance_material')
|
||||
instance_material.set('symbol', 'Material1')
|
||||
instance_material.set('target', '#Material1')
|
||||
instance_material = SubElement(technique_common, 'instance_material')
|
||||
instance_material.set('symbol', 'Material2')
|
||||
instance_material.set('target', '#Material2')
|
||||
'''
|
||||
|
||||
extra = SubElement(node, 'extra')
|
||||
technique = SubElement(extra, 'technique')
|
||||
technique.set('profile', 'FCOLLADA')
|
||||
visibility = SubElement(technique, 'visibility')
|
||||
visibility.text = '1.000000'
|
||||
|
||||
|
||||
# save the file:
|
||||
st = prettify(root)
|
||||
#print(st)
|
||||
f = open(filename, "w")
|
||||
f.write(st)
|
||||
f.close()
|
||||
FreeCAD.Console.PrintMessage("Se ha generado correctamente el archivo PVC: ", filename)
|
||||
return True
|
||||
|
||||
def prettify(elem):
|
||||
""" Return a pretty-printed XML string for the Element. """
|
||||
|
||||
from xml.etree import ElementTree
|
||||
from xml.dom import minidom
|
||||
|
||||
rough_string = ElementTree.tostring(elem, 'utf-8')
|
||||
reparsed = minidom.parseString(rough_string)
|
||||
return reparsed.toprettyxml(indent=" ")
|
||||
|
||||
def exportToH2P(path): # sólo válido para mesas
|
||||
filename = path + ".h2p"
|
||||
f2 = '{:.2f}'
|
||||
f3 = '{:.3f}'
|
||||
|
||||
st = 'START\n'
|
||||
# TODO: hacer un bucle para cada tipo-tamaño de estructura.
|
||||
# posible solucción: un primer bucle para identificar los tipos-tamaños de estructura
|
||||
#FreeCAD.ActiveDocument.findObjects
|
||||
#FreeCAD.ActiveDocument.Objects
|
||||
|
||||
objects = FreeCAD.ActiveDocument.findObjects(Name="Tracker")
|
||||
grouptype = []
|
||||
#for obj in objects:
|
||||
grouptype.append(objects[0])
|
||||
|
||||
for type in grouptype:
|
||||
st += 'TABLE\n' \
|
||||
'10\n'
|
||||
st += f3.format(type.Width.Value) + ',' + f3.format(type.Length.Value) + ',' + \
|
||||
f3.format(0) + ',' + f3.format(0) + ',' + f3.format(0) + ',' + f3.format(0) + "\n"
|
||||
#'#{ f3 %pvsyst.ilb.to_mm },#{f3 %pvsyst.irb.to_mm},#{f3 %pvsyst.itb.to_mm},' \
|
||||
#'#{f3 %pvsyst.ibb.to_mm}\n'
|
||||
st += '20\n'
|
||||
st += str(int(type.ModulesCols.Value)) + ',' + str(int(type.ModulesRows.Value)) + ',' + \
|
||||
str(type.ModuleColGap.Value) + ',' + str(type.ModuleRowGap.Value) + ',' + '30\n'
|
||||
st += '30\n'
|
||||
st += '1,' + f3.format(type.ModuleWidth.Value) + ',' + f3.format(type.ModuleHeight.Value) + ',' + \
|
||||
f3.format(type.ModuleThick.Value) + ',' + f2.format(450) + '\n' #f2.format(type.ModulePower.Value) + '\n'
|
||||
|
||||
# cornerdown = find_component_sizes(group.cdef)[1]
|
||||
# pvorigin = Geom::Point3d.new(cornerdown.x, cornerdown.y, 0)
|
||||
# group.instances.each{ | ins | str += pvsyst_insert(ins, pvorigin)}
|
||||
|
||||
for obj in objects:
|
||||
if obj.CloneOf == type:
|
||||
st += H2PInsert(obj)
|
||||
|
||||
## TODO: Bucle para buscar objetos que den sombra y el terreno. Todos llaman a H2PMesh
|
||||
mesh = FreeCAD.ActiveDocument.getObjectsByLabel('Surface')[0].Mesh
|
||||
st += H2PMesh(mesh, False)
|
||||
st += "END\n"
|
||||
|
||||
# save the file:
|
||||
f = open(filename, "w")
|
||||
f.write(st)
|
||||
f.close()
|
||||
FreeCAD.Console.PrintMessage("Se ha generado el archivo PVC: ", filename)
|
||||
return True
|
||||
|
||||
def H2PInsert(obj):
|
||||
f3 = '{:.3f}'
|
||||
f2 = '{:.2f}'
|
||||
scale = 0.001 ## ver como se puede hacer para que sea general. Pasar de mm a m
|
||||
|
||||
st = 'INSERT\n' \
|
||||
'10\n'
|
||||
st += f3.format(obj.Placement.Base.x * scale) + ',' + f3.format(obj.Placement.Base.y * scale) + ',' + \
|
||||
f3.format((obj.Placement.Base.z + obj.PoleLength.Value - obj.RammingDeep.Value + 1000) * scale) + '\n'
|
||||
st += '50\n'
|
||||
st += f2.format(-obj.Placement.Rotation.toEuler()[0]) + '\n'
|
||||
st += '55\n'
|
||||
st += f2.format(obj.Placement.Rotation.toEuler()[1]) + '\n'
|
||||
st += '56\n'
|
||||
st += f2.format(obj.Placement.Rotation.toEuler()[2]) + '\n'
|
||||
|
||||
return st
|
||||
|
||||
def H2PMesh(mesh, type):
|
||||
scale = 0.001 ## ver como se puede hacer para que sea general. Pasar de mm a m
|
||||
|
||||
f3 = '{:.3f}'
|
||||
st = ''
|
||||
if type:
|
||||
st = 'ShadowObject\nFence\n'
|
||||
else:
|
||||
st = 'DGM\n'
|
||||
|
||||
for face in mesh.Facets:
|
||||
p1 = face.Points[0]
|
||||
p2 = face.Points[1]
|
||||
p3 = face.Points[2]
|
||||
|
||||
st += f3.format(p1[0] * scale) + "," + f3.format(p1[1] * scale) + "," + f3.format(p1[2] * scale) + ";"
|
||||
st += f3.format(p2[0] * scale) + "," + f3.format(p2[1] * scale) + "," + f3.format(p2[2] * scale) + ";"
|
||||
st += f3.format(p3[0] * scale) + "," + f3.format(p3[1] * scale) + "," + f3.format(p3[2] * scale) + "\n"
|
||||
|
||||
return st
|
||||
|
||||
class _PVSystTaskPanel:
|
||||
|
||||
def __init__(self):
|
||||
self.form = FreeCADGui.PySideUic.loadUi(os.path.dirname(__file__) + "/exportPVSyst.ui")
|
||||
self.form = self.form
|
||||
|
||||
def show(self):
|
||||
# self.form.setWindowModality(Qt.WindowModal)
|
||||
self.form.show()
|
||||
|
||||
def accept(self):
|
||||
import datetime
|
||||
x = datetime.datetime.now()
|
||||
date = x.strftime("%y%m%d%H%M%S")
|
||||
overwrite = True
|
||||
|
||||
path = os.path.join(os.path.dirname(FreeCAD.ActiveDocument.FileName), "outputs", "PVSyst")
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
|
||||
name = FreeCAD.ActiveDocument.Label
|
||||
if not overwrite:
|
||||
name = date + "-" + name
|
||||
filename = os.path.join(path, name)
|
||||
|
||||
#if self.form.cbDAE.isChecked():
|
||||
# exportToDAE(filename)
|
||||
|
||||
if self.form.cbPVC.isChecked():
|
||||
exportToPVC(filename, self.form.cbTerrain.isChecked())
|
||||
|
||||
if self.form.cbH2P.isChecked():
|
||||
exportToH2P(filename)
|
||||
|
||||
FreeCADGui.Control.closeDialog()
|
||||
return True
|
||||
|
||||
def reject(self):
|
||||
FreeCADGui.Control.closeDialog()
|
||||
return True
|
||||
|
||||
class _CommandExportToPVSyst:
|
||||
"Export to PVSyst"
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "PVsyst.png")),
|
||||
'Accel': "E, P",
|
||||
'MenuText': QT_TRANSLATE_NOOP("Outputs", "Export to PVSyst"),
|
||||
'ToolTip': QT_TRANSLATE_NOOP("Outputs", "Exportar a PVSyst")}
|
||||
|
||||
def Activated(self):
|
||||
taskd = _PVSystTaskPanel()
|
||||
# taskd.show()
|
||||
FreeCADGui.Control.showDialog(taskd)
|
||||
|
||||
def IsActive(self):
|
||||
if FreeCAD.ActiveDocument:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('ExportToPVSyst', _CommandExportToPVSyst())
|
||||
84
Export/exportPVSyst.ui
Normal file
84
Export/exportPVSyst.ui
Normal file
@@ -0,0 +1,84 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>formPVSyst</class>
|
||||
<widget class="QDialog" name="formPVSyst">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>301</width>
|
||||
<height>123</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Export to PVSyst</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Export format:</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbH2P">
|
||||
<property name="text">
|
||||
<string>Helios 3D (H2P) - PVSyst 6.0 or higher</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbPVC">
|
||||
<property name="text">
|
||||
<string>PVCase (PVC) - PVSyst 7.0 or higher</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbTerrain">
|
||||
<property name="text">
|
||||
<string>Exportar terreno</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
159
Export/layoutToExcel.py
Normal file
159
Export/layoutToExcel.py
Normal file
@@ -0,0 +1,159 @@
|
||||
# /**********************************************************************
|
||||
# * *
|
||||
# * 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 FreeCAD, Draft
|
||||
import PVPlantSite
|
||||
import copy
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
from DraftTools import translate
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
import Part
|
||||
import pivy
|
||||
from pivy import coin
|
||||
import os
|
||||
else:
|
||||
# \cond
|
||||
def translate(ctxt, txt):
|
||||
return txt
|
||||
|
||||
|
||||
def QT_TRANSLATE_NOOP(ctxt, txt):
|
||||
return txt
|
||||
# \endcond
|
||||
|
||||
__title__ = "PVPlant Trench"
|
||||
__author__ = "Javier Braña"
|
||||
__url__ = "http://www.sogos-solar.com"
|
||||
|
||||
from PVPlantResources import DirIcons as DirIcons
|
||||
from PVPlantResources import DirDocuments as DirDocuments
|
||||
|
||||
|
||||
'''import os
|
||||
import platform
|
||||
import subprocess
|
||||
|
||||
def open_file(path):
|
||||
if platform.system() == "Windows":
|
||||
os.startfile(path)
|
||||
elif platform.system() == "Darwin":
|
||||
subprocess.Popen(["open", path])
|
||||
else:
|
||||
subprocess.Popen(["xdg-open", path])'''
|
||||
|
||||
|
||||
from PVPlantPlacement import getCols
|
||||
|
||||
def generateLayout(numcells = 3, gapc = 3):
|
||||
# TODO: hacerlo por zonas
|
||||
|
||||
objects = []
|
||||
for obj in FreeCAD.ActiveDocument.Objects:
|
||||
if not hasattr(obj, "Proxy"):
|
||||
continue
|
||||
'''if issubclass(obj.Proxy.__class__, PVPlantRack.Frame):
|
||||
objects.append(obj)'''
|
||||
if obj.Name.startswith("Tracker") and not obj.Name.startswith("TrackerSetup"):
|
||||
objects.append(obj)
|
||||
|
||||
if len(objects) == 0:
|
||||
FreeCAD.Console.PrintError("No hay trackes" + "\n")
|
||||
return False
|
||||
|
||||
columns = getCols(objects.copy())
|
||||
|
||||
MaxY = max(objects, key=lambda obj: obj.Placement.Base.y).Placement.Base.y
|
||||
long_max = max(objects, key=lambda obj: obj.Setup.Length.Value).Setup.Length.Value
|
||||
num_pole_max = max(objects, key=lambda obj: obj.Setup.NumberPole.Value).Setup.NumberPole.Value
|
||||
num_module_max = max(objects, key=lambda obj: obj.Setup.ModuleColumns.Value).Setup.ModuleColumns.Value
|
||||
# TODO: calcular la serparación entre trackers.
|
||||
gapy = 500
|
||||
|
||||
import openpyxl
|
||||
path = os.path.dirname(FreeCAD.ActiveDocument.FileName)
|
||||
filename = os.path.join(path, "layout.xlsx")
|
||||
mywb = openpyxl.Workbook()
|
||||
|
||||
# 1. Hincas
|
||||
sheet = mywb.active
|
||||
sheet.title = 'Poles'
|
||||
m = -(numcells * num_pole_max + gapc) / (long_max + gapy)
|
||||
line = lambda x: int(m * (x - MaxY))
|
||||
drawLayout(sheet, columns, line, 0)
|
||||
|
||||
# 2. Paneles
|
||||
sheet = mywb.create_sheet("Modules")
|
||||
m = -(numcells * num_module_max + gapc) / (long_max + gapy)
|
||||
line = lambda x: int(m * (x - MaxY) + 1)
|
||||
#drawLayout(sheet, columns, line, 1)
|
||||
|
||||
mywb.save(filename)
|
||||
print("Se ha generado el lyout en excel satisfactoriamente en: ")
|
||||
print(filename)
|
||||
os.startfile(path)
|
||||
|
||||
#from os import startfile
|
||||
#startfile("archivo.txt")
|
||||
|
||||
return True
|
||||
|
||||
def drawLayout(sheet, cols, line, cnt = 0, numcell = 3, gap = 3):
|
||||
from openpyxl.styles import Alignment, Border, Side, PatternFill, GradientFill, Font
|
||||
thin = Side(border_style="thin", color="000000")
|
||||
|
||||
for i, col in enumerate(cols):
|
||||
colnum = i * 3 + 1
|
||||
for g, group in enumerate(col):
|
||||
rownum = int(line(group[0].Placement.Base.y)) + 1
|
||||
if rownum < 1:
|
||||
continue
|
||||
for frame in group:
|
||||
num = int(frame.Setup.NumberPole.Value if cnt == 0 else frame.Setup.ModuleColumns.Value)
|
||||
sheet.merge_cells(start_row=rownum, start_column=colnum,
|
||||
end_row=rownum, end_column=colnum + 1)
|
||||
titlecell = sheet.cell(row=rownum, column=colnum)
|
||||
titlecell.value = frame.Label
|
||||
titlecell.border = Border(top=thin, left=thin, right=thin, bottom=thin)
|
||||
titlecell.font = Font(b=True, color="FF0000")
|
||||
titlecell.alignment = Alignment(horizontal="center", vertical="center")
|
||||
rownum += 1
|
||||
for ind in range(num):
|
||||
sheet.merge_cells(start_row=rownum, start_column=colnum,
|
||||
end_row=rownum + numcell - 1, end_column=colnum)
|
||||
polecell = sheet.cell(row=rownum, column=colnum)
|
||||
polecell.value = ind + 1
|
||||
polecell.alignment = Alignment(horizontal="center", vertical="center")
|
||||
|
||||
sheet.merge_cells(start_row=rownum, start_column=colnum + 1,
|
||||
end_row=rownum + numcell - 1, end_column=colnum + 1)
|
||||
polecell = sheet.cell(row=rownum, column=colnum + 1)
|
||||
polecell.value = "Celda-" + str(ind + 1)
|
||||
polecell.value = frame.Label
|
||||
polecell.border = Border(top=thin, left=thin, right=thin, bottom=thin)
|
||||
polecell.font = Font(b=True, color="FF0000")
|
||||
polecell.alignment = Alignment(horizontal="center", vertical="center")
|
||||
|
||||
rownum += numcell
|
||||
rownum += 1
|
||||
|
||||
Reference in New Issue
Block a user