primera subida

This commit is contained in:
2025-01-28 00:04:13 +01:00
commit a91237c3e1
577 changed files with 457418 additions and 0 deletions

80
Export/PVPlantBOQCivil.py Normal file
View 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())

View 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())

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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