updates
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
|
||||
import FreeCAD
|
||||
import ArchComponent
|
||||
import Part
|
||||
import os
|
||||
import zipfile
|
||||
import re
|
||||
@@ -48,17 +49,18 @@ import PVPlantResources
|
||||
from PVPlantResources import DirIcons as DirIcons
|
||||
Dir3dObjects = os.path.join(PVPlantResources.DirResources, "3dObjects")
|
||||
|
||||
vector = ["Y", "YN", "Z", "ZN", "D"]
|
||||
|
||||
def makePCS():
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "StringInverter")
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "PowerConversionSystem")
|
||||
PowerConverter(obj)
|
||||
ViewProviderStringInverter(obj.ViewObject)
|
||||
ViewProviderPowerConverter(obj.ViewObject)
|
||||
|
||||
try:
|
||||
folder = FreeCAD.ActiveDocument.StringInverters
|
||||
folder = FreeCAD.ActiveDocument.PowerConversionSystemGroup
|
||||
except:
|
||||
folder = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'StringInverters')
|
||||
folder.Label = "StringInverters"
|
||||
folder = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'PowerConversionSystemGroup')
|
||||
folder.Label = "PowerConversionSystemGroup"
|
||||
folder.addObject(obj)
|
||||
return obj
|
||||
|
||||
@@ -67,9 +69,6 @@ class PowerConverter(ArchComponent.Component):
|
||||
def __init__(self, obj):
|
||||
''' Initialize the Area object '''
|
||||
ArchComponent.Component.__init__(self, obj)
|
||||
|
||||
self.oldMPPTs = 0
|
||||
|
||||
self.Type = None
|
||||
self.obj = None
|
||||
self.setProperties(obj)
|
||||
@@ -77,36 +76,69 @@ class PowerConverter(ArchComponent.Component):
|
||||
def setProperties(self, obj):
|
||||
pl = obj.PropertiesList
|
||||
|
||||
if not "File" in pl:
|
||||
obj.addProperty("App::PropertyFile",
|
||||
"File",
|
||||
"Inverter",
|
||||
"The base file this component is built upon")
|
||||
|
||||
if not ("MPPTs" in pl):
|
||||
obj.addProperty("App::PropertyQuantity",
|
||||
"MPPTs",
|
||||
"Inverter",
|
||||
"Points that define the area"
|
||||
).MPPTs = 0
|
||||
|
||||
if not ("Generator" in pl):
|
||||
# Transformer properties
|
||||
if not "Technology" in pl:
|
||||
obj.addProperty("App::PropertyEnumeration",
|
||||
"Generator",
|
||||
"Inverter",
|
||||
"Points that define the area"
|
||||
).Generator = ["Generic", "Library"]
|
||||
obj.Generator = "Generic"
|
||||
"Technology",
|
||||
"Transformer",
|
||||
"Number of phases and type of transformer"
|
||||
).Technology = ["Single Phase Transformer", "Three Phase Transformer"]
|
||||
|
||||
if not ("Type" in pl):
|
||||
obj.addProperty("App::PropertyString",
|
||||
"Type",
|
||||
"Base",
|
||||
"Points that define the area"
|
||||
).Type = "PowerConverter"
|
||||
obj.setEditorMode("Type", 1)
|
||||
if not "PowerPrimary" in pl:
|
||||
obj.addProperty("App::PropertyPower",
|
||||
"PowerPrimary",
|
||||
"Transformer",
|
||||
"The base file this component is built upon").PowerPrimary = 6000000000
|
||||
|
||||
self.Type = obj.Type
|
||||
if not "PowerSecundary1" in pl:
|
||||
obj.addProperty("App::PropertyPower",
|
||||
"PowerSecundary1",
|
||||
"Transformer",
|
||||
"The base file this component is built upon").PowerSecundary1 = 3000000000
|
||||
|
||||
if not "PowerSecundary2" in pl:
|
||||
obj.addProperty("App::PropertyPower",
|
||||
"PowerSecundary2",
|
||||
"Transformer",
|
||||
"The base file this component is built upon").PowerSecundary2 = 3000000000
|
||||
|
||||
if not "VoltagePrimary" in pl:
|
||||
obj.addProperty("App::PropertyElectricPotential",
|
||||
"VoltagePrimary",
|
||||
"Transformer",
|
||||
"The base file this component is built upon").VoltagePrimary = 33000000000
|
||||
|
||||
if not "VoltageSecundary1" in pl:
|
||||
obj.addProperty("App::PropertyElectricPotential",
|
||||
"VoltageSecundary1",
|
||||
"Transformer",
|
||||
"The base file this component is built upon").VoltageSecundary1 = 11000000000
|
||||
|
||||
if not "VoltageSecundary2" in pl:
|
||||
obj.addProperty("App::PropertyElectricPotential",
|
||||
"VoltageSecundary2",
|
||||
"Transformer",
|
||||
"The base file this component is built upon").VoltageSecundary2 = 11000000000
|
||||
|
||||
if not "VectorPrimary" in pl:
|
||||
obj.addProperty("App::PropertyEnumeration",
|
||||
"VectorPrimary",
|
||||
"Transformer",
|
||||
"The base file this component is built upon").VectorPrimary = vector
|
||||
|
||||
if not "VectorSecundary1" in pl:
|
||||
obj.addProperty("App::PropertyEnumeration",
|
||||
"VectorSecundary1",
|
||||
"Transformer",
|
||||
"The base file this component is built upon").VectorSecundary1 = vector
|
||||
|
||||
if not "VectorSecundary2" in pl:
|
||||
obj.addProperty("App::PropertyEnumeration",
|
||||
"VectorSecundary2",
|
||||
"Transformer",
|
||||
"The base file this component is built upon").VectorSecundary2 = vector
|
||||
|
||||
self.Type = "PowerConverter"
|
||||
obj.Proxy = self
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
@@ -114,263 +146,34 @@ class PowerConverter(ArchComponent.Component):
|
||||
self.setProperties(obj)
|
||||
|
||||
def onBeforeChange(self, obj, prop):
|
||||
|
||||
if prop == "MPPTs":
|
||||
self.oldMPPTs = int(obj.MPPTs)
|
||||
''' '''
|
||||
# This method is called before a property is changed.
|
||||
# It can be used to validate the property value or to update other properties.
|
||||
# If the property is not valid, you can raise an exception.
|
||||
# If you want to prevent the change, you can return False.
|
||||
# Otherwise, return True to allow the change.
|
||||
return True
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
''' '''
|
||||
|
||||
if prop == "Generator":
|
||||
if obj.Generator == "Generic":
|
||||
obj.setEditorMode("MPPTs", 0)
|
||||
else:
|
||||
obj.setEditorMode("MPPTs", 1)
|
||||
|
||||
if prop == "MPPTs":
|
||||
''' '''
|
||||
if self.oldMPPTs > obj.MPPTs:
|
||||
''' borrar sobrantes '''
|
||||
obj.removeProperty()
|
||||
|
||||
elif self.oldMPPTs < obj.MPPTs:
|
||||
''' crear los faltantes '''
|
||||
for i in range(self.oldMPPTs, int(obj.MPPTs)):
|
||||
''' '''
|
||||
print(i)
|
||||
else:
|
||||
pass
|
||||
|
||||
if (prop == "File") and obj.File:
|
||||
''' '''
|
||||
|
||||
def execute(self, obj):
|
||||
''' '''
|
||||
# obj.Shape: compound
|
||||
# |- body: compound
|
||||
# |-- inverter: solid
|
||||
# |-- door: solid
|
||||
# |-- holder: solid
|
||||
|
||||
# |- connectors: compound
|
||||
# |-- DC: compound
|
||||
# |--- MPPT 1..x: compound
|
||||
# |---- positive: compound
|
||||
# |----- connector 1..y: ??
|
||||
# |---- negative 1..y: compound
|
||||
# |----- connector 1..y: ??
|
||||
# |-- AC: compound
|
||||
# |--- R,S,T,: ??
|
||||
# |-- Communication
|
||||
# |- transformer: solid
|
||||
# |- primary switchgear: compound
|
||||
# |- secundary 1 switchgear: compound
|
||||
# |- secundary 2 switchgear: compound
|
||||
|
||||
pl = obj.Placement
|
||||
filename = self.getFile(obj)
|
||||
if filename:
|
||||
parts = self.getPartsList(obj)
|
||||
if parts:
|
||||
zdoc = zipfile.ZipFile(filename)
|
||||
if zdoc:
|
||||
f = zdoc.open(parts[list(parts.keys())[-1]][1])
|
||||
shapedata = f.read()
|
||||
f.close()
|
||||
shapedata = shapedata.decode("utf8")
|
||||
shape = self.cleanShape(shapedata, obj, parts[list(parts.keys())[-1]][2])
|
||||
obj.Shape = shape
|
||||
if not pl.isIdentity():
|
||||
obj.Placement = pl
|
||||
obj.MPPTs = len(shape.SubShapes[1].SubShapes[0].SubShapes)
|
||||
obj.Shape = Part.makeBox(6058, 2438, 2591) # Placeholder for the shape
|
||||
obj.Placement = pl
|
||||
|
||||
def cleanShape(self, shapedata, obj, materials):
|
||||
"cleans the imported shape"
|
||||
|
||||
import Part
|
||||
shape = Part.Shape()
|
||||
shape.importBrepFromString(shapedata)
|
||||
'''if obj.FuseArch and materials:
|
||||
# separate lone edges
|
||||
shapes = []
|
||||
for edge in shape.Edges:
|
||||
found = False
|
||||
for solid in shape.Solids:
|
||||
for soledge in solid.Edges:
|
||||
if edge.hashCode() == soledge.hashCode():
|
||||
found = True
|
||||
break
|
||||
if found:
|
||||
break
|
||||
if found:
|
||||
break
|
||||
else:
|
||||
shapes.append(edge)
|
||||
print("solids:",len(shape.Solids),"mattable:",materials)
|
||||
for key,solindexes in materials.items():
|
||||
if key == "Undefined":
|
||||
# do not join objects with no defined material
|
||||
for solindex in [int(i) for i in solindexes.split(",")]:
|
||||
shapes.append(shape.Solids[solindex])
|
||||
else:
|
||||
fusion = None
|
||||
for solindex in [int(i) for i in solindexes.split(",")]:
|
||||
if not fusion:
|
||||
fusion = shape.Solids[solindex]
|
||||
else:
|
||||
fusion = fusion.fuse(shape.Solids[solindex])
|
||||
if fusion:
|
||||
shapes.append(fusion)
|
||||
shape = Part.makeCompound(shapes)
|
||||
try:
|
||||
shape = shape.removeSplitter()
|
||||
except Exception:
|
||||
print(obj.Label,": error removing splitter")'''
|
||||
return shape
|
||||
|
||||
def getFile(self, obj, filename=None):
|
||||
"gets a valid file, if possible"
|
||||
|
||||
if not filename:
|
||||
filename = obj.File
|
||||
if not filename:
|
||||
return None
|
||||
if not filename.lower().endswith(".fcstd"):
|
||||
return None
|
||||
if not os.path.exists(filename):
|
||||
# search for the file in the current directory if not found
|
||||
basename = os.path.basename(filename)
|
||||
currentdir = os.path.dirname(obj.Document.FileName)
|
||||
altfile = os.path.join(currentdir,basename)
|
||||
if altfile == obj.Document.FileName:
|
||||
return None
|
||||
elif os.path.exists(altfile):
|
||||
return altfile
|
||||
else:
|
||||
# search for subpaths in current folder
|
||||
altfile = None
|
||||
subdirs = self.splitall(os.path.dirname(filename))
|
||||
for i in range(len(subdirs)):
|
||||
subpath = [currentdir]+subdirs[-i:]+[basename]
|
||||
altfile = os.path.join(*subpath)
|
||||
if os.path.exists(altfile):
|
||||
return altfile
|
||||
return None
|
||||
return filename
|
||||
|
||||
def getPartsList(self, obj, filename=None):
|
||||
|
||||
"returns a list of Part-based objects in a FCStd file"
|
||||
|
||||
parts = {}
|
||||
materials = {}
|
||||
filename = self.getFile(obj,filename)
|
||||
if not filename:
|
||||
return parts
|
||||
zdoc = zipfile.ZipFile(filename)
|
||||
with zdoc.open("Document.xml") as docf:
|
||||
name = None
|
||||
label = None
|
||||
part = None
|
||||
materials = {}
|
||||
writemode = False
|
||||
for line in docf:
|
||||
line = line.decode("utf8")
|
||||
if "<Object name=" in line:
|
||||
n = re.findall('name=\"(.*?)\"',line)
|
||||
if n:
|
||||
name = n[0]
|
||||
elif "<Property name=\"Label\"" in line:
|
||||
writemode = True
|
||||
elif writemode and "<String value=" in line:
|
||||
n = re.findall('value=\"(.*?)\"',line)
|
||||
if n:
|
||||
label = n[0]
|
||||
writemode = False
|
||||
elif "<Property name=\"Shape\" type=\"Part::PropertyPartShape\"" in line:
|
||||
writemode = True
|
||||
elif writemode and "<Part file=" in line:
|
||||
n = re.findall('file=\"(.*?)\"',line)
|
||||
if n:
|
||||
part = n[0]
|
||||
writemode = False
|
||||
elif "<Property name=\"MaterialsTable\" type=\"App::PropertyMap\"" in line:
|
||||
writemode = True
|
||||
elif writemode and "<Item key=" in line:
|
||||
n = re.findall('key=\"(.*?)\"',line)
|
||||
v = re.findall('value=\"(.*?)\"',line)
|
||||
if n and v:
|
||||
materials[n[0]] = v[0]
|
||||
elif writemode and "</Map>" in line:
|
||||
writemode = False
|
||||
elif "</Object>" in line:
|
||||
if name and label and part:
|
||||
parts[name] = [label,part,materials]
|
||||
name = None
|
||||
label = None
|
||||
part = None
|
||||
materials = {}
|
||||
writemode = False
|
||||
return parts
|
||||
|
||||
def getColors(self,obj):
|
||||
|
||||
"returns the DiffuseColor of the referenced object"
|
||||
|
||||
filename = self.getFile(obj)
|
||||
if not filename:
|
||||
return None
|
||||
part = obj.Part
|
||||
if not obj.Part:
|
||||
return None
|
||||
zdoc = zipfile.ZipFile(filename)
|
||||
if not "GuiDocument.xml" in zdoc.namelist():
|
||||
return None
|
||||
colorfile = None
|
||||
with zdoc.open("GuiDocument.xml") as docf:
|
||||
writemode1 = False
|
||||
writemode2 = False
|
||||
for line in docf:
|
||||
line = line.decode("utf8")
|
||||
if ("<ViewProvider name=" in line) and (part in line):
|
||||
writemode1 = True
|
||||
elif writemode1 and ("<Property name=\"DiffuseColor\"" in line):
|
||||
writemode1 = False
|
||||
writemode2 = True
|
||||
elif writemode2 and ("<ColorList file=" in line):
|
||||
n = re.findall('file=\"(.*?)\"',line)
|
||||
if n:
|
||||
colorfile = n[0]
|
||||
break
|
||||
if not colorfile:
|
||||
return None
|
||||
if not colorfile in zdoc.namelist():
|
||||
return None
|
||||
colors = []
|
||||
cf = zdoc.open(colorfile)
|
||||
buf = cf.read()
|
||||
cf.close()
|
||||
for i in range(1,int(len(buf)/4)):
|
||||
colors.append((buf[i*4+3]/255.0,buf[i*4+2]/255.0,buf[i*4+1]/255.0,buf[i*4]/255.0))
|
||||
if colors:
|
||||
return colors
|
||||
return None
|
||||
|
||||
def splitall(self,path):
|
||||
|
||||
"splits a path between its components"
|
||||
|
||||
allparts = []
|
||||
while 1:
|
||||
parts = os.path.split(path)
|
||||
if parts[0] == path: # sentinel for absolute paths
|
||||
allparts.insert(0, parts[0])
|
||||
break
|
||||
elif parts[1] == path: # sentinel for relative paths
|
||||
allparts.insert(0, parts[1])
|
||||
break
|
||||
else:
|
||||
path = parts[0]
|
||||
allparts.insert(0, parts[1])
|
||||
return allparts
|
||||
|
||||
class ViewProviderStringInverter(ArchComponent.ViewProviderComponent):
|
||||
class ViewProviderPowerConverter(ArchComponent.ViewProviderComponent):
|
||||
def __init__(self, vobj):
|
||||
ArchComponent.ViewProviderComponent.__init__(self, vobj)
|
||||
|
||||
@@ -381,12 +184,12 @@ class CommandPowerConverter:
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "Inverter.svg")),
|
||||
'Accel': "E, I",
|
||||
'MenuText': "String Inverter",
|
||||
'ToolTip': "String Placement",}
|
||||
'Accel': "E, P",
|
||||
'MenuText': "Power Converter",
|
||||
'ToolTip': "Power Converter",}
|
||||
|
||||
def Activated(self):
|
||||
sinverter = makeStringInverter()
|
||||
sinverter = makePCS()
|
||||
|
||||
def IsActive(self):
|
||||
active = not (FreeCAD.ActiveDocument is None)
|
||||
|
||||
Reference in New Issue
Block a user