Stability process predicate evaluation module

This commit is contained in:
IDONTSUDO 2023-09-12 19:09:33 +00:00 committed by Igor Brylyov
parent 78e31ea49c
commit 03bc34539c
19 changed files with 1365 additions and 123 deletions

4
.gitignore vendored
View file

@ -112,6 +112,4 @@ install_plugin_cad.sh
*#
.#*
\#*\#
Cube3
sdf-generation
p.json
out/

View file

@ -6,7 +6,7 @@ import { HttpRepository, HttpMethod, HttpRoute } from '../../core/repository/htt
export const pathAjaxTopologyScreen = '/topology/adjax/usecase/'
export interface IAdjaxMatrix {
allPars: string[];
allParts: string[];
firstDetail: string;
matrix: StringMap;
matrixError: StringMap | null;

877
asp/helper/xmlformatter.py Normal file
View file

@ -0,0 +1,877 @@
"""
Format and compress XML documents
"""
import getopt
import re
import sys
import xml.parsers.expat
__version__ = "0.2.4"
DEFAULT_BLANKS = False
DEFAULT_COMPRESS = False
DEFAULT_SELFCLOSE = False
DEFAULT_CORRECT = True
DEFAULT_INDENT = 2
DEFAULT_INDENT_CHAR = " "
DEFAULT_INLINE = True
DEFAULT_ENCODING_INPUT = None
DEFAULT_ENCODING_OUTPUT = None
DEFAULT_EOF_NEWLINE = False
class Formatter:
# Use internal encoding:
encoding_internal = None
def __init__(
self,
indent=DEFAULT_INDENT,
preserve=[],
blanks=DEFAULT_BLANKS,
compress=DEFAULT_COMPRESS,
selfclose=DEFAULT_SELFCLOSE,
indent_char=DEFAULT_INDENT_CHAR,
encoding_input=DEFAULT_ENCODING_INPUT,
encoding_output=DEFAULT_ENCODING_OUTPUT,
inline=DEFAULT_INLINE,
correct=DEFAULT_CORRECT,
eof_newline=DEFAULT_EOF_NEWLINE,
):
# Minify the XML document:
self.compress = compress
# Use self-closing tags
self.selfclose = selfclose
# Correct text nodes
self.correct = correct
# Decode the XML document:
self.encoding_input = self.enc_normalize(encoding_input)
# Encode ouput by:
self.encoding_output = self.enc_normalize(encoding_output)
# Insert indent = indent*level*indent_char:
self.indent = int(indent)
# Indent by char:
self.indent_char = indent_char
# Format inline objects:
self.inline = inline
# Don't compress this elements and their descendants:
self.preserve = preserve
# Preserve blanks lines (collapse multiple into one)
self.blanks = blanks
# Always add a newline character at EOF
self.eof_newline = eof_newline
@property
def encoding_effective(self, enc=None):
if self.encoding_output:
return self.encoding_output
elif self.encoding_internal:
return self.encoding_internal
elif self.encoding_input:
return self.encoding_input
else:
return "UTF-8"
def enc_normalize(self, string):
""" Format an Encoding identifier to upper case. """
if isinstance(string, str):
return string.upper()
return None
def enc_encode(self, strg):
""" Encode a formatted XML document in target"""
if sys.version_info > (3, 0):
return strg.encode(self.encoding_effective) # v3
return strg.decode("utf-8").encode(self.encoding_effective) # v2
def enc_output(self, path, strg):
""" Output according to encoding """
fh = sys.stdout
if strg is not None:
if path is not None:
open(path, "w+b").write(strg)
elif sys.version_info > (3, 0):
fh.buffer.write(strg)
else:
fh.write(strg)
def format_string(self, xmldoc=""):
""" Format a XML document given by xmldoc """
token_list = Formatter.TokenList(self)
token_list.parser.Parse(xmldoc)
return self.enc_encode(str(token_list))
def format_file(self, file):
""" Format a XML document given by path name """
fh = open(file, "rb")
token_list = Formatter.TokenList(self)
token_list.parser.ParseFile(fh)
fh.close()
return self.enc_encode(str(token_list))
class TokenList:
# Being in a cdata section:
cdata_section = False
# Lock deletion of leading whitespace:
desc_mixed_level = None
# Lock indenting:
indent_level = None
# Reference the Formatter:
formatter = None
# Count levels:
level_counter = 0
# Lock deletion of whitespaces:
preserve_level = None
def __init__(self, formatter):
# Keep tokens in a list:
self._list = []
self.formatter = formatter
self.parser = xml.parsers.expat.ParserCreate(
encoding=self.formatter.encoding_input
)
self.parser.specified_attributes = 1
self.parser.buffer_text = True
# Push tokens to buffer:
for pattern in [
"XmlDecl%s",
"ElementDecl%s",
"AttlistDecl%s",
"EntityDecl%s",
"StartElement%s",
"EndElement%s",
"ProcessingInstruction%s",
"CharacterData%s",
"Comment%s",
"Default%s",
"StartDoctypeDecl%s",
"EndDoctypeDecl%s",
"StartCdataSection%s",
"EndCdataSection%s",
"NotationDecl%s",
]:
setattr(
self.parser, pattern % "Handler", self.xml_handler(pattern % "")
)
def __iter__(self):
return iter(self._list)
def __len__(self):
return len(self._list)
def __getitem__(self, pos):
if 0 <= pos < len(self._list):
return self._list[pos]
else:
raise IndexError
def __setitem__(self, pos, value):
if 0 <= pos < len(self._list):
self._list[pos] = value
else:
raise IndexError
def __str__(self):
""" Returns the formatted XML document in UTF-8. """
for step in ["configure", "pre_operate", "post_operate"]:
for tk in iter(self):
getattr(tk, step)()
result = ""
for tk in iter(self):
result += str(tk)
if self.formatter.eof_newline and not result.endswith("\n"):
result += "\n"
return result
def append(self, tk):
""" Add token to tokenlist. """
tk.pos = len(self._list)
self._list.append(tk)
def level_increment(self):
""" Increment level counter. """
self.level_counter += 1
def level_decrement(self):
""" Decrement level counter. """
self.level_counter -= 1
def token_descendant_mixed(self, tk):
""" Mark descendants of mixed content. """
if tk.name == "StartElement":
# Mark every descendant:
if tk.content_model in [2, 3] and self.desc_mixed_level is None:
self.desc_mixed_level = tk.level
return False
return self.desc_mixed_level is not None
elif tk.name == "EndElement":
# Stop marking every descendant:
if tk.level is self.desc_mixed_level:
self.desc_mixed_level = None
elif self.desc_mixed_level is not None:
return True
return False
elif self.desc_mixed_level is None:
return False
return self.desc_mixed_level >= tk.level - 1
def sequence(self, tk, scheme=None):
"""Returns sublist of token list.
None: next to last
EndElement: first to previous"""
if scheme == "EndElement" or (scheme is None and tk.end):
return reversed(self._list[: tk.pos])
return self._list[(tk.pos + 1) :]
def token_indent(self, tk):
if self.formatter.inline:
return self.token_indent_inline(tk)
""" Indent outside of text of mixed content. """
if tk.name == "StartElement":
# Block indenting for descendants of text and mixed content:
if tk.content_model in [2, 3] and self.indent_level is None:
self.indent_level = tk.level
elif self.indent_level is not None:
return False
return True
elif tk.name == "EndElement":
# Unblock indenting for descendants of text and mixed content:
if tk.level == self.indent_level:
self.indent_level = None
elif self.indent_level is None:
return True
return False
return self.indent_level is None
def token_indent_inline(self, tk):
""" Indent every element content - no matter enclosed by text or mixed content. """
for itk in iter(self.sequence(tk, "EndElement")):
if itk.level < tk.level and itk.name == "StartElement":
if itk.content_model == 1:
return True
return False
if (
itk.level == tk.level
and tk.name == "EndElement"
and itk.name == "StartElement"
):
if itk.content_model == 1:
return True
return False
return True
def token_model(self, tk):
"""Returns code for content model.
0: empty
1: element
2: text
3: mixed"""
eflag = tflag = 0
for itk in iter(self.sequence(tk)):
# Element boundary found:
if itk.level <= tk.level:
break
# Direct child found:
elif (itk.level - 1) == tk.level:
if itk.start:
eflag = 1
elif itk.not_empty:
tflag = 2
return eflag + tflag
def token_preserve(self, tk):
"""Preseve eyery descendant of an preserved element.
0: not locked
1: just (un)locked
2: locked"""
# Lock perserving for StartElements:
if tk.name == "StartElement":
if self.preserve_level is not None:
return 2
if tk.arg[0] in self.formatter.preserve:
self.preserve_level = tk.level
return 1
return 0
# Unlock preserving for EndElements:
elif tk.name == "EndElement":
if (
tk.arg[0] in self.formatter.preserve
and tk.level == self.preserve_level
):
self.preserve_level = None
return 1
elif self.preserve_level is None:
return 0
return 2
return self.preserve_level is not None
def whitespace_append_trailing(self, tk):
""" Add a trailing whitespace to previous character data. """
if self.formatter.correct and tk.leading and tk.not_empty:
self.whitespace_append(tk, "EndElement", "StartElement", True)
def whitespace_append_leading(self, tk):
""" Add a leading whitespace to previous character data. """
if self.formatter.correct and tk.trailing and tk.not_empty:
self.whitespace_append(tk)
def whitespace_append(
self, tk, start="StartElement", stop="EndElement", direct=False
):
""" Add a whitspace to token list. """
for itk in self.sequence(tk, start):
if (
itk.empty
or (itk.name == stop and itk.descendant_mixed is False)
or (itk.name == start and abs(tk - itk) == 1)
):
break
elif itk.not_empty or (itk.name == start and itk.descendant_mixed):
self.insert_empty(itk, direct)
break
def whitespace_delete_leading(self, tk):
""" Returns True, if no next token or all empty (up to next end element)"""
if (
self.formatter.correct
and tk.leading
and not tk.preserve
and not tk.cdata_section
):
for itk in self.sequence(tk, "EndElement"):
if itk.trailing:
return True
elif itk.name in ["EndElement", "CharacterData", "EndCdataSection"]:
return False
return True
return False
def whitespace_delete_trailing(self, tk):
"""Returns True, if no next token or all empty (up to next end element)"""
if (
self.formatter.correct
and tk.trailing
and not tk.preserve
and not tk.cdata_section
):
for itk in self.sequence(tk, "StartElement"):
if itk.end:
return True
elif (
itk.name in ["StartElement", "StartCdataSection"]
or itk.not_empty
):
return False
return True
return False
def insert_empty(self, tk, before=True):
""" Insert an Empty Token into token list - before or after tk. """
if not (0 < tk.pos < (len(self) - 1)):
return False
ptk = self[tk.pos - 1]
ntk = self.formatter.CharacterData(self, [" "])
ntk.level = max(ptk.level, tk.level)
ntk.descendant_mixed = tk.descendant_mixed
ntk.preserve = ptk.preserve * tk.preserve
ntk.cdata_section = ptk.cdata_section or tk.cdata_section
if before:
self._list.insert(tk.pos + 1, ntk)
else:
self._list.insert(tk.pos, ntk)
for i in range((tk.pos - 1), len(self._list)):
self._list[i].pos = i
def xml_handler(self, key):
""" Returns lambda function which adds token to token list"""
return lambda *arg: self.append(getattr(self.formatter, key)(self, arg))
class Token(object):
def __init__(self, tklist, arg):
# Reference Token List:
self.list = tklist
# Token datas:
self.arg = list(arg)
# Token is placed in an CDATA section:
self.cdata_section = False
# Token has content model:
self.content_model = None
# Remove trailing wihtespaces:
self.delete_trailing = False
# Remove leading whitespaces:
self.delete_leading = False
# Token is descendant of text or mixed content element:
self.descendant_mixed = False
# Reference to formatter:
self.formatter = tklist.formatter
# Insert indenting white spaces:
self.indent = False
# N-th generation of roots descendants:
self.level = self.list.level_counter
# Token class:
self.name = self.__class__.__name__
# Preserve white spaces within enclosed tokens:
self.preserve = False
# Position in token list:
self.pos = None
def __sub__(self, other):
return self.pos - other.pos
def __unicode__(self):
return ""
# Workaround, see http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python/:
if sys.version_info > (3, 0):
__str__ = lambda x: x.__unicode__()
else:
__str__ = lambda x: unicode(x).encode("utf-8")
@property
def end(self):
return self.name == "EndElement"
@property
def empty(self):
return self.name == "CharacterData" and re.match(
r"^[\t\s\n]*$", self.arg[0]
)
@property
def leading(self):
return self.name == "CharacterData" and re.search(
r"^[\t\s\n]+", self.arg[0]
)
@property
def not_empty(self):
return (
self.name == "CharacterData"
and not self.cdata_section
and not re.match(r"^[\t\s\n]+$", self.arg[0])
)
@property
def trailing(self):
return self.name == "CharacterData" and re.search(
r"[\t\s\n]+$", self.arg[0]
)
@property
def start(self):
return self.name == "StartElement"
@property
def correct(self):
return self.formatter.correct
def attribute(self, key, value):
if key and value:
return ' %s="%s"' % (key, value)
elif key:
return ' %s=""' % (key)
return ""
def indent_insert(self):
""" Indent token. """
# Child of root and no empty node
if (
self.level > 0 and not (self.end and self.list[self.pos - 1].start)
) or ( # not empty node:
self.end and not self.list[self.pos - 1].start
):
return self.indent_create(self.level)
return ""
def indent_create(self, times=1):
""" Returns indent string. """
if not self.formatter.compress and self.formatter.indent:
return "\n%s" % (
(times * self.formatter.indent) * self.formatter.indent_char
)
return ""
def identifier(self, systemid, publicid):
# TODO add base parameter:
if publicid and systemid:
return ' PUBLIC "%s" "%s"' % (publicid, systemid)
elif publicid:
return ' PUBLIC "%s"' % publicid
elif systemid:
return ' SYSTEM "%s"' % systemid
return ""
def configure(self):
""" Set token properties. """
self.descendant_mixed = self.list.token_descendant_mixed(self)
self.preserve = self.list.token_preserve(self)
self.cdata_section = self.list.cdata_section
def pre_operate(self):
pass
def post_operate(self):
pass
class AttlistDecl(Token):
def __unicode__(self):
str = self.indent_create()
str += "<!ATTLIST %s %s" % (self.arg[0], self.arg[1])
if self.arg[2] is not None:
str += " %s" % self.arg[2]
if self.arg[4] and not self.arg[3]:
str += " #REQUIRED"
elif self.arg[3] and self.arg[4]:
str += " #FIXED"
elif not self.arg[4] and not self.arg[3]:
str += " #IMPLIED"
if self.arg[3]:
str += ' "%s"' % self.arg[3]
str += ">"
return str
class CharacterData(Token):
def __unicode__(self):
str = self.arg[0]
if not self.preserve and not self.cdata_section:
# remove empty tokens always in element content!
if self.empty and not self.descendant_mixed:
if self.formatter.blanks and not self.formatter.compress and re.match(r"\s*\n\s*\n\s*", str):
str = "\n"
else:
str = ""
else:
if self.correct:
str = re.sub(r"\r\n", "\n", str)
str = re.sub(r"\r|\n|\t", " ", str)
str = re.sub(r"\s+", " ", str)
if self.delete_leading:
str = re.sub(r"^\s", "", str)
if self.delete_trailing:
str = re.sub(r"\s$", "", str)
if not self.cdata_section:
str = re.sub(r"&", "&amp;", str)
str = re.sub(r"<", "&lt;", str)
return str
def pre_operate(self):
self.list.whitespace_append_trailing(self)
self.list.whitespace_append_leading(self)
def post_operate(self):
self.delete_leading = self.list.whitespace_delete_leading(self)
self.delete_trailing = self.list.whitespace_delete_trailing(self)
class Comment(Token):
def __unicode__(self):
str = ""
if self.preserve in [0, 1] and self.indent:
str += self.indent_insert()
str += "<!--%s-->" % re.sub(
r"^[\r\n]+$", "\n", re.sub(r"^[\r\n]+", "\n", self.arg[0])
)
return str
def configure(self):
super(Formatter.Comment, self).configure()
self.indent = self.list.token_indent(self)
class Default(Token):
pass
class EndCdataSection(Token):
def __unicode__(self):
return "]]>"
def configure(self):
self.list.cdata_section = False
class ElementDecl(Token):
def __unicode__(self):
str = self.indent_create()
str += "<!ELEMENT %s%s>" % (self.arg[0], self.evaluate_model(self.arg[1]))
return str
def evaluate_model(self, model, modelStr="", concatStr=""):
childSeq = []
mixed = model[0] == xml.parsers.expat.model.XML_CTYPE_MIXED
hasChilds = len(model[3]) or mixed
if model[0] == xml.parsers.expat.model.XML_CTYPE_EMPTY: # 1
modelStr += " EMPTY"
elif model[0] == xml.parsers.expat.model.XML_CTYPE_ANY: # 2
modelStr += " ANY"
elif model[0] == xml.parsers.expat.model.XML_CTYPE_NAME: # 4
modelStr = "%s" % model[2] # new start
elif model[0] in (
xml.parsers.expat.model.XML_CTYPE_CHOICE,
xml.parsers.expat.model.XML_CTYPE_MIXED,
): # 5
concatStr = "|"
elif model[0] == xml.parsers.expat.model.XML_CTYPE_SEQ: # 6
concatStr = ","
if hasChilds:
modelStr += " ("
if mixed:
childSeq.append("#PCDATA")
for child in model[3]:
childSeq.append(self.evaluate_model(child))
modelStr += concatStr.join(childSeq)
if hasChilds:
modelStr += ")"
modelStr += {
xml.parsers.expat.model.XML_CQUANT_NONE: "",
xml.parsers.expat.model.XML_CQUANT_OPT: "?",
xml.parsers.expat.model.XML_CQUANT_PLUS: "+",
xml.parsers.expat.model.XML_CQUANT_REP: "*",
}[model[1]]
return modelStr
class EndDoctypeDecl(Token):
def __unicode__(self):
str = ""
if self.list[self.pos - 1].name != "StartDoctypeDecl":
str += self.indent_create(0)
str += "]"
str += ">"
str += self.indent_create(0)
return str
class EndElement(Token):
def __init__(self, list, arg):
list.level_decrement()
super(Formatter.EndElement, self).__init__(list, arg)
def __unicode__(self):
str = ""
# Don't close empty nodes on compression mode:
if (
not (self.formatter.compress or self.formatter.selfclose)
or self.list[self.pos - 1].name != "StartElement"
):
if self.preserve in [0] and self.indent:
str += self.indent_insert()
str += "</%s>" % self.arg[0]
return str
def configure(self):
self.descendant_mixed = self.list.token_descendant_mixed(self)
self.preserve = self.list.token_preserve(self)
self.indent = self.list.token_indent(self)
class EntityDecl(Token):
def __unicode__(self):
str = self.indent_create()
str += "<!ENTITY "
if self.arg[1]:
str += "% "
str += "%s " % self.arg[0]
if self.arg[2]:
str += '"%s"' % self.arg[2]
else:
str += "%s " % self.identifier(self.arg[4], self.arg[5])
if self.arg[6]:
str += "NDATA %s" % self.arg[6]
str += ">"
return str
class NotationDecl(Token):
def __unicode__(self):
str = self.indent_create()
str += "<!NOTATION %s%s>" % (
self.arg[0],
self.identifier(self.arg[2], self.arg[3]),
)
return str
class ProcessingInstruction(Token):
def __unicode__(self):
str = ""
if self.preserve in [0, 1] and self.indent:
str += self.indent_insert()
str += "<?%s %s?>" % (self.arg[0], self.arg[1])
return str
def configure(self):
super(Formatter.ProcessingInstruction, self).configure()
self.indent = self.list.token_indent(self)
class StartCdataSection(Token):
def __unicode__(self):
return "<![CDATA["
def configure(self):
self.list.cdata_section = True
class StartDoctypeDecl(Token):
def __unicode__(self):
str = "<!DOCTYPE %s" % (self.arg[0])
if self.arg[1]:
str += self.identifier(self.arg[1], self.arg[2])
if self.arg[3]:
str += " ["
return str
class StartElement(Token):
def __init__(self, list, arg):
super(Formatter.StartElement, self).__init__(list, arg)
self.list.level_increment()
def __unicode__(self):
str = ""
if self.preserve in [0, 1] and self.indent:
str += self.indent_insert()
str += "<%s" % self.arg[0]
for attr in sorted(self.arg[1].keys()):
str += self.attribute(attr, self.arg[1][attr])
if self.list[self.pos + 1].end and (self.formatter.compress or self.formatter.selfclose):
str += "/>"
else:
str += ">"
return str
def configure(self):
self.content_model = self.list.token_model(self)
self.descendant_mixed = self.list.token_descendant_mixed(self)
self.preserve = self.list.token_preserve(self)
self.indent = self.list.token_indent(self)
class XmlDecl(Token):
def __init__(self, list, arg):
super(Formatter.XmlDecl, self).__init__(list, arg)
if len(self.arg) > 1:
self.formatter.encoding_internal = self.arg[1]
def __unicode__(self):
str = "<?xml%s%s" % (
self.attribute("version", self.arg[0]),
self.attribute("encoding", self.formatter.encoding_effective),
)
if self.arg[2] > -1:
str += self.attribute("standalone", "yes")
str += "?>\n"
return str
def cli_usage(msg=""):
""" Output usage for command line tool. """
sys.stderr.write(msg + "\n")
sys.stderr.write(
'Usage: xmlformat [--preserve "pre,literal"] [--blanks]\
[--compress] [--selfclose] [--indent num] [--indent-char char]\
[--outfile file] [--encoding enc] [--outencoding enc]\
[--disable-inlineformatting] [--overwrite] [--disable-correction]\
[--eof-newline]\
[--help] <--infile file | file | - >\n'
)
sys.exit(2)
def cli():
""" Launch xmlformatter from command line. """
res = None
indent = DEFAULT_INDENT
indent_char = DEFAULT_INDENT_CHAR
outfile = None
overwrite = False
preserve = []
blanks = False
compress = DEFAULT_COMPRESS
selfclose = DEFAULT_SELFCLOSE
infile = None
encoding = DEFAULT_ENCODING_INPUT
outencoding = DEFAULT_ENCODING_OUTPUT
inline = DEFAULT_INLINE
correct = DEFAULT_CORRECT
eof_newline = DEFAULT_EOF_NEWLINE
try:
opts, args = getopt.getopt(
sys.argv[1:],
"",
[
"compress",
"selfclose",
"disable-correction",
"disable-inlineformatting",
"encoding=",
"help",
"infile=",
"indent=",
"indent-char=",
"outfile=",
"outencoding=",
"overwrite",
"preserve=",
"blanks",
"eof-newline"
],
)
except getopt.GetoptError as err:
cli_usage(str(err))
for key, value in opts:
if key in ["--indent"]:
indent = value
elif key in ["--preserve"]:
preserve = value.replace(",", " ").split()
elif key in ["--blanks"]:
blanks = True
elif key in ["--help"]:
cli_usage()
elif key in ["--compress"]:
compress = True
elif key in ["--selfclose"]:
selfclose = True
elif key in ["--outfile"]:
outfile = value
elif key in ["--infile"]:
infile = value
elif key in ["--encoding"]:
encoding = value
elif key in ["--outencoding"]:
outencoding = value
elif key in ["--indent-char"]:
indent_char = value
elif key in ["--disable-inlineformatting"]:
inline = False
elif key in ["--disable-correction"]:
correct = False
elif key in ["--overwrite"]:
overwrite = True
elif key in ["--eof-newline"]:
eof_newline = True
try:
formatter = Formatter(
indent=indent,
preserve=preserve,
blanks=blanks,
compress=compress,
selfclose=selfclose,
encoding_input=encoding,
encoding_output=outencoding,
indent_char=indent_char,
inline=inline,
correct=correct,
eof_newline=eof_newline,
)
input_file = None
if infile:
input_file = infile
res = formatter.format_file(input_file)
elif len(args) > 0:
if args[0] == "-":
res = formatter.format_string("".join(sys.stdin.readlines()))
else:
input_file = args[0]
res = formatter.format_file(input_file)
except xml.parsers.expat.ExpatError as err:
cli_usage("XML error: %s" % err)
except IOError as err:
cli_usage("IO error: %s" % err)
except:
cli_usage("Unkonwn error")
if overwrite:
formatter.enc_output(input_file, res)
else:
formatter.enc_output(outfile, res)

View file

@ -7,7 +7,6 @@ from src.usecases.sdf_sub_assembly_usecase import SdfSubAssemblyUseCase
import os
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('--generationFolder', help='FreeCad generation folder')
@ -27,8 +26,8 @@ if __name__ == "__main__":
for el in geometryFiles:
geometryModels.append(GeometryModel.from_dict(
FS.readJSON(args.generationFolder + '/assets/' + el)))
if os.path.exists(outPath + 'sdf-generation/'):
shutil.rmtree(path=outPath + 'sdf-generation/')
# if os.path.exists(outPath + 'sdf-generation/'):
# shutil.rmtree(path=outPath + 'sdf-generation/')
if (args.format == 'sdf'):
SdfSubAssemblyUseCase().call(

View file

@ -15,7 +15,7 @@
</inertial>
<visual>
<geometry>
<mesh filename="{stl}" scale="1 1 1" />
<mesh filename="{stl}" scale="0.001 0.001 0.001" />
</geometry>
<material name="white">
<color rgba="1. 1. 1. 1." />
@ -23,14 +23,14 @@
</visual>
<collision>
<geometry>
<mesh filename="{stl}" scale="1 1 1" />
<mesh filename="{stl}" scale="0.001 0.001 0.001" />
</geometry>
</collision>
<friction>
<ode>
<mu>0.2</mu>
<mu2>0.1</mu2>
<fdir1>1 0 0</fdir1>
<mu>0.2</mu>
<mu2>0.1</mu2>
<fdir1>1 0 0</fdir1>
</ode>
</friction>
</link>

View file

@ -30,6 +30,9 @@ def to_class(c, x):
return x.to_dict()
DELIMITER_SCALE = 10000
class GeometryModel:
def __init__(self, name, ixx, ixy, ixz, iyy, izz, massSDF, posX, posY, posZ, eulerX, eulerY, eulerZ, iyz, stl, link, friction, centerMassX, centerMassY, centerMassZ):
self.name = name

View file

@ -1,10 +1,10 @@
from helper.xmlformatter import Formatter
from src.model.enum import Enum
from helper.fs import FS
import xmlformatter
class FormatterUseCase:
def call(outPath: str, format: str):
formatter = xmlformatter(
formatter = Formatter(
indent="1", indent_char="\t", encoding_output="ISO-8859-1", preserve=["literal"])
files = FS.readFilesTypeFolder(

View file

@ -1,16 +1,11 @@
from typing import Optional
from helper.fs import FS
from src.model.enum import Enum
from src.model.asm import Assembly
from src.model.sdf_geometry import GeometryModel
from helper.fs import filterModels, listGetFirstValue
import json
import re
URDF_FILE_FORMAT = '.urdf'
URDF_GENERATOR_FILE = 'urdf-generation' + '.json'
@ -19,6 +14,6 @@ class UrdfSubAssemblyUseCase(Assembly):
dirPath = generationFolder + Enum.folderPath
asm = {}
for el in geometryModels:
asm[el.name] = el.toUrdf()
asm[el.name] = el.toUrdf()
FS.writeFile(data=json.dumps(asm,indent=4),
fileName=URDF_GENERATOR_FILE, filePath=dirPath)

View file

@ -1,6 +1,6 @@
{
"doc": "/home/idontsudo/t/framework/asp-review-app/server/public/cubes/cubes.FCStd",
"out": "/home/idontsudo/t/framework/cad_generation",
"doc": "/home/idontsudo/framework/asp/out/disk_and_axis_n.FCStd",
"out": "/home/idontsudo/framework/asp/out",
"resultURL": "http://localhost:3002/assembly/save/out",
"projectId": "cubes"
}

View file

@ -36,9 +36,9 @@ class RobossemblerFreeCadExportScenario:
self.geometry(directory)
ExportAssemblyThemAllUseCase().call(directoryExport)
shutil.make_archive(directory, 'zip', directory)
# shutil.make_archive(directory, 'zip', directory)
shutil.rmtree(directory)
# shutil.rmtree(directory)
return True
def geometry(self, outPutsPath: str):

View file

@ -1,38 +1,41 @@
import FreeCAD as App
from model.sdf_geometry_model import SdfGeometryModel
from helper.is_solid import is_object_solid
class SdfGeometryUseCase:
ShapePropertyCheck = ['Mass','MatrixOfInertia','Placement', ]
ShapePropertyCheck = ['Mass', 'MatrixOfInertia', 'Placement', ]
PartPropertyCheck = ['Shape']
def call(self, stlPaths:dict) -> list[SdfGeometryModel]:
def call(self, stlPaths: dict) -> list[SdfGeometryModel]:
materialSolid = {}
for el in App.ActiveDocument.Objects:
if str(el) == '<App::MaterialObjectPython object>':
if str(el) == '<App::MaterialObjectPython object>':
friction = el.Material.get('SlidingFriction')
for i in el.References:
materialSolid[i[0].Label] = friction
geometry = []
try:
for el in App.ActiveDocument.Objects:
if is_object_solid(el):
mass = el.Shape.Mass
inertia = el.Shape.MatrixOfInertia
pos = el.Shape.Placement
inertia = el.Shape.MatrixOfInertia
name = el.Label
ixx = str(inertia.A11 / 1000000)
ixy = str(inertia.A12 / 1000000)
ixz = str(inertia.A13 / 1000000)
iyy = str(inertia.A22 / 1000000)
iyz = str(inertia.A23 / 1000000)
izz = str(inertia.A33 / 1000000)
massSDF = str(mass / 1000000)
posX = str(pos.Base[0] / 1000000)
posY = str(pos.Base[1] / 1000000)
posZ = str(pos.Base[2] / 1000000)
delimiter = 1000000
ixx = str(inertia.A11 / delimiter)
ixy = str(inertia.A12 / delimiter)
ixz = str(inertia.A13 / delimiter)
iyy = str(inertia.A22 / delimiter)
iyz = str(inertia.A23 / delimiter)
izz = str(inertia.A33 / delimiter)
massSDF = str(mass / delimiter)
posX = str(pos.Base[0] / delimiter)
posY = str(pos.Base[1] / delimiter)
posZ = str(pos.Base[2] / delimiter)
eulerX = str(pos.Rotation.toEuler()[0])
eulerY = str(pos.Rotation.toEuler()[1])
eulerZ = str(pos.Rotation.toEuler()[2])
@ -42,10 +45,10 @@ class SdfGeometryUseCase:
geometry.append(
SdfGeometryModel(
stl=stlPaths.get(el.Label),
name=name,
name=name,
ixx=ixx,
ixz=ixz,
ixy=ixy,
ixz=ixz,
ixy=ixy,
iyy=iyy,
iyz=iyz,
izz=izz,
@ -53,8 +56,8 @@ class SdfGeometryUseCase:
posX=posX,
posY=posY,
posZ=posZ,
eulerX=eulerX,
eulerY=eulerY,
eulerX=eulerX,
eulerY=eulerY,
eulerZ=eulerZ,
friction=materialSolid.get(el.Label) or '',
centerMassX=centerMassX,
@ -64,10 +67,4 @@ class SdfGeometryUseCase:
)
except Exception as e:
print(e)
return geometry

View file

@ -92,4 +92,5 @@ ENV/
# Rope project settings
.ropeproject
env.json
env.json
out/

View file

@ -1,15 +1,266 @@
# Алгоритм генерации графа с помощью оценки стабильности подсборок в физическом движке PyBullet
from typing import Any, TypeVar, Type, cast
import FreeCAD as App
import json
import re
from pyquaternion import Quaternion
import importOBJ
import FreeCAD as App
import Draft
import os
import Part
import numpy as np
from typing import TypeAlias
import FreeCADGui as Gui
ISequencesUnion: TypeAlias = dict[str:dict[str:list[str]]]
def importObjAtPath(path: str):
import importOBJ
def importObjAtPath(path: str, inc: int):
importOBJ.insert(u"" + path, App.ActiveDocument.Label)
pass
mesh = App.ActiveDocument.Objects[inc]
shape = Part.Shape()
shape.makeShapeFromMesh(mesh.Mesh.Topology, 0.05)
solid = Part.makeSolid(shape)
Part.show(solid)
App.ActiveDocument.Objects[inc +
1].Label = App.ActiveDocument.Objects[inc].Name
App.ActiveDocument.removeObject(App.ActiveDocument.Objects[inc].Name)
return App.ActiveDocument.Objects[inc]
T = TypeVar("T")
def from_float(x: Any) -> float:
assert isinstance(x, (float, int)) and not isinstance(x, bool)
return float(x)
def to_float(x: Any) -> float:
assert isinstance(x, float)
return x
def from_str(x: Any) -> str:
assert isinstance(x, str)
return x
def to_class(c: Type[T], x: Any) -> dict:
assert isinstance(x, c)
return cast(Any, x).to_dict()
def euler_to_quaternion(yaw, pitch, roll):
qx = np.sin(roll/2) * np.cos(pitch/2) * np.cos(yaw/2) - \
np.cos(roll/2) * np.sin(pitch/2) * np.sin(yaw/2)
qy = np.cos(roll/2) * np.sin(pitch/2) * np.cos(yaw/2) + \
np.sin(roll/2) * np.cos(pitch/2) * np.sin(yaw/2)
qz = np.cos(roll/2) * np.cos(pitch/2) * np.sin(yaw/2) - \
np.sin(roll/2) * np.sin(pitch/2) * np.cos(yaw/2)
qw = np.cos(roll/2) * np.cos(pitch/2) * np.cos(yaw/2) + \
np.sin(roll/2) * np.sin(pitch/2) * np.sin(yaw/2)
return [qx, qy, qz, qw]
class Coords:
x: float
y: float
z: float
def __init__(self, x: float, y: float, z: float) -> None:
self.x = x
self.y = y
self.z = z
@staticmethod
def from_dict(obj: Any) -> 'Coords':
assert isinstance(obj, dict)
x = from_float(obj.get("x"))
y = from_float(obj.get("y"))
z = from_float(obj.get("z"))
return Coords(x, y, z)
def to_dict(self) -> dict:
result: dict = {}
result["x"] = to_float(self.x)
result["y"] = to_float(self.y)
result["z"] = to_float(self.z)
return result
def vector(self):
return App.Vector(self.x, self.y, self.z)
class MotionResultModel:
id: str
euler: Coords
position: Coords
def __init__(self, id: str, euler: Coords, position: Coords) -> None:
self.id = id
self.euler = euler
self.position = position
@staticmethod
def from_dict(obj: Any) -> 'MotionResultModel':
assert isinstance(obj, dict)
id = from_str(obj.get("id"))
euler = Coords.from_dict(obj.get("euler"))
position = Coords.from_dict(obj.get("position"))
return MotionResultModel(id, euler, position)
def to_dict(self) -> dict:
result: dict = {}
result["id"] = from_str(self.id)
result["euler"] = to_class(Coords, self.euler)
result["position"] = to_class(Coords, self.position)
return result
class SequencesEvaluation:
sequences: ISequencesUnion
assemblyDir: str
result: dict = {}
def __init__(self, sequences, assemblyDir) -> None:
self.sequences = sequences
self.assemblyDir = assemblyDir
pass
def assemblyComputed(self):
debug = True
for sequenceNumber, v in self.sequences.items():
for assemblyNumber, assemblySequenced in v.items():
# print(assemblyNumber)
# print()
# if(assemblyNumber == 1 and sequenceNumber == 4 or assemblyNumber == 1 and sequenceNumber == 0):
if(assemblyNumber == 1 and sequenceNumber == 1 and debug):
debug = False
if(sequenceNumber == 0):
sequenceNumber+=1
print(assemblySequenced)
self.comptedAssembly(
assemblySequenced, sequenceNumber, assemblyNumber)
pass
def comptedAssembly(self, assembly: list[str], sequenceNumber: int, assemblyNumber: int):
assemblyParts = []
for counter in range(len(assembly)):
importObjAtPath(
self.assemblyDir + 'sdf/meshes/' + assembly[counter] + '.obj',
counter
)
assemblyParts.append({
"part": App.ActiveDocument.Objects[counter],
"name": assembly[counter]
})
motionResult = json.loads((open(self.assemblyDir + 'stability' + '/' + str(
sequenceNumber + 1) + '/' + str(assemblyNumber) + '/' + 'motion_result.json')).read())
simulatorMotionResults: list['MotionResultModel'] = []
for _k, v in motionResult.items():
simulatorMotionResults.append(MotionResultModel.from_dict(v))
for el in simulatorMotionResults:
for e in assemblyParts:
# сопоставляем детали
if (el.id == e.get('name')):
# вычисляем центр детали для перемещения
center = e.get('part').Shape.CenterOfMass
# получаем центр деталей из симуляции
new_center = App.Vector(el.position.vector())
# вычисляем вектор смещения
offset = new_center - center
# перемещаем деталь на вектор смещения
e.get('part').Placement.Base += offset
# импортируем меш связанный с зоной обьекта
# zonePart = importObjAtPath(self.assemblyDir + "stability/zones/meshes/zone_sub_assembly" + str(
# assembly.__len__() - 1) + ".obj", len(App.ActiveDocument.Objects))
# получаем координаты зоны относительно детали за которой закреплена зона
print(assemblyNumber)
coords = json.loads(
(open(self.assemblyDir + "stability/zones/sub_assembly_coords_" + str(assemblyNumber - 1) + ".json")).read())
assemblyCounter = len(assemblyParts)
detailWhichZoneBindings = assemblyParts[assemblyCounter - 2].get(
'part')
detailStabilityComputed = assemblyParts[assemblyCounter - 1].get(
'part')
relativeCoordinates = coords.get('relativeCoordinates')
relativeEuler = coords.get('relativeEuler')
specificEuler = {
'yaw': detailWhichZoneBindings.Placement.Rotation.toEuler()[0] - relativeEuler.get('yaw'),
'pitch': detailWhichZoneBindings.Placement.Rotation.toEuler()[1] - relativeEuler.get('pitch'),
'roll': detailWhichZoneBindings.Placement.Rotation.toEuler()[2] - relativeEuler.get('roll')
}
quaternion = euler_to_quaternion(specificEuler.get(
'yaw'), specificEuler.get('pitch'), specificEuler.get('roll'))
rotation = App.Rotation(
quaternion[0], quaternion[1], quaternion[2], quaternion[3])
detailStabilityComputed.Placement.Rotation = rotation
centerVector = detailWhichZoneBindings.Shape.CenterOfMass
vector = App.Vector(relativeCoordinates.get(
'x'), relativeCoordinates.get('y'), relativeCoordinates.get('z'))
# current_center = zonePart.Shape.CenterOfMass
# move_vector = App.Vector(centerVector + vector) - current_center
# zonePart.Placement.move(move_vector)
# computedStabilityResult = computedStability(
# zonePart, detailStabilityComputed)
if sequenceNumber not in self.result.keys():
self.result[sequenceNumber] = []
# self.result[sequenceNumber].append({
# str(assemblyNumber): assembly,
# "result": computedStabilityResult
# })
# for part in App.ActiveDocument.Objects:
# App.ActiveDocument.removeObject(part.Name)
def get_part_center(part):
shape = None
if not hasattr(part, 'Shape'):
shape = part.Mesh
if hasattr(part, 'Shape'):
shape = part.Shape
center = shape.BoundBox.Center
return App.Vector(center[0],
center[1],
center[2])
def move_second_part_to_match_center(first_part, second_part):
first_center = get_part_center(first_part)
second_center = get_part_center(second_part)
offset = first_center - second_center
second_part.Placement.move(offset)
def create(part):
clone = Draft.make_clone([part], forcedraft=True)
clone.Scale = App.Vector(1.30, 1.30, 1.30)
clone_corr = (App.Vector(0.4476673941774023, -2.109332894191716, -0.5918687740295264) -
clone.Placement.Base).scale(*App.Vector(-0.25, -0.25, -0.25))
clone.Placement.move(clone_corr)
App.ActiveDocument.recompute()
return clone
def getFullPathObj(assemblyFolder: str, name: str):
@ -27,67 +278,128 @@ def computedStability(refElement, childElement):
childElement.Name).getLinkedObject(True).ViewObject, 'DisplayMode', App.ActiveDocument.getObject('Common').ViewObject.DisplayMode)
App.ActiveDocument.recompute()
obj = App.ActiveDocument.getObjectsByLabel('Common')[0]
shp = obj.Shape
bbox = shp.BoundBox
# Если после операции пересечения зона обьекта совпадает с зоной тестируемого обьекта то тест прошел успешно
# Если после операции пересечения зона обьекта совпадает с зоной тестируемого обьекта то тест прошел успешно
if bbox.XLength == rootElement.XLength and bbox.YLength == rootElement.YLength and rootElement.ZLength == bbox.ZLength:
return True
return False
def autoStabilityZoneComputed(stepFilesPaths: list[str], directoryStableZonesPath: str):
cadObjects = []
for count in range(len(stepFilesPaths)):
importObjAtPath(stepFilesPaths[count], count)
cadObjects.append(App.ActiveDocument.Objects[count])
assemblesBindings = []
for increment in range(len(cadObjects)):
if (increment != 0):
detailForEvaluationZ = cadObjects[increment]
zoneBindingDetailZ = cadObjects[increment-1]
assemblesBindings.append(
{'zoneBindingDetail': detailForEvaluationZ, 'detailForEvaluation': zoneBindingDetailZ, 'relativeCoordinates': None, 'zonePart': None})
for increment in range(len(assemblesBindings)):
el = assemblesBindings[increment]
zoneBindingDetail = el.get('zoneBindingDetail')
zoneBindingDetailCenterVector = zoneBindingDetail.Shape.CenterOfMass
zoneDetail = create(el.get('detailForEvaluation'))
move_second_part_to_match_center(
el.get('zoneBindingDetail'), zoneDetail)
zoneDetail.Label = 'zone_sub_assembly' + str(increment + 1)
zoneDetail.ViewObject.ShapeColor = (0.40, 0.74, 0.71)
zoneDetail.ViewObject.Transparency = 50
zoneDetailCenterVector = el.get(
'detailForEvaluation').Shape.CenterOfMass
el['relativeCoordinates'] = {
'x': zoneBindingDetailCenterVector.x - zoneDetailCenterVector.x,
'y': zoneBindingDetailCenterVector.y - zoneDetailCenterVector.y,
'z': zoneBindingDetailCenterVector.z - zoneDetailCenterVector.z
}
el['relativeEuler'] = {
'yaw': zoneBindingDetail.Placement.Rotation.toEuler()[0] - el.get('detailForEvaluation').Placement.Rotation.toEuler()[0],
'pitch': zoneBindingDetail.Placement.Rotation.toEuler()[1] - el.get('detailForEvaluation').Placement.Rotation.toEuler()[1],
'roll': zoneBindingDetail.Placement.Rotation.toEuler()[2] - el.get('detailForEvaluation').Placement.Rotation.toEuler()[2]
}
el['zonePart'] = zoneDetail
meshesPath = directoryStableZonesPath + 'meshes/'
if not os.path.exists(directoryStableZonesPath):
os.makedirs(directoryStableZonesPath)
if not os.path.exists(meshesPath):
os.makedirs(meshesPath)
zonesSaved = {}
for counter in range(len(assemblesBindings)):
zoneComputed = assemblesBindings[counter]
mesh = zoneComputed.get('zonePart')
zonesSavePath = meshesPath + mesh.Label + '.obj'
importOBJ.export([mesh], zonesSavePath)
zonesSaved[mesh.Label] = 'meshes/' + mesh.Label + '.obj'
for counter in range(len(assemblesBindings)):
el = assemblesBindings[counter]
savePath = zonesSaved[el.get('zonePart').Label]
el['zonePart'] = savePath
el['detailForEvaluation'] = el['detailForEvaluation'].Label
el['zoneBindingDetail'] = el['zoneBindingDetail'].Label
json_result = json.dumps(el)
file_to_open = directoryStableZonesPath + \
'sub_assembly_coords_' + str(counter) + '.json'
f = open(file_to_open, 'w', )
f.write(json_result)
f.close()
def main():
App.newDocument()
# загрузка env интерфейса
env = json.loads((open('./env.json')).read())
# получение пути координат файла
coordinatesFilePath = env.get('pathToTheSimulationCoordinatesFile')
assemblyDir = env.get('aspPath')
sequencesJSON = json.loads((open(assemblyDir + 'sequences.json')).read())
directoryStableZones = assemblyDir + 'stability/zones/'
# получение папки сборки
assemblyFolder = env.get('generationFolder')
buildNumber = int(re.findall(r'\d', coordinatesFilePath)[0])
sequences = sequencesJSON.get('sequences')
stepStructure = json.loads(
(open(assemblyDir + 'step-structure.json')).read())
# загрузка последовательности сборки ввиде списка строк деталей
assemblyStructure = json.loads(
(open(assemblyFolder + 'step-structure.json')).read())
# получение номера тестируемой сборки
assemblyNumber = int(buildNumber)
# получение тестируемой детали в сборке
activeDetail = assemblyStructure[assemblyNumber]
subassemblyNotParticipatingInMarkup = assemblyStructure[0:assemblyNumber - 1]
detailOfTheMarkingZoneOfWhich = assemblyStructure[assemblyNumber - 1]
importObjAtPath(getFullPathObj(assemblyFolder, activeDetail))
importObjAtPath(getFullPathObj(
assemblyFolder, detailOfTheMarkingZoneOfWhich))
meshMark = App.ActiveDocument.Objects[0]
meshDetailOfTheMarkZone = App.ActiveDocument.Objects[1]
meshMark.ViewObject.ShapeColor = (0.31, 0.77, 0.87)
meshDetailOfTheMarkZone.ViewObject.ShapeColor = (0.68, 0.66, 0.95)
for el in list(map(lambda el: getFullPathObj(assemblyFolder, el), subassemblyNotParticipatingInMarkup)):
importObjAtPath(el)
for el in App.ActiveDocument.Objects[2:App.ActiveDocument.Objects.__len__()]:
el.ViewObject.ShapeColor = (0.32, 0.05, 0.38)
# загрузка координат
coordinates = json.loads((open(coordinatesFilePath)).read())
inc = 1
# перемещение обьектов в документе на точки стабильность полученные из pybullet
for el in App.ActiveDocument.Objects:
pos = coordinates[inc]['position']
qua = coordinates[inc]['quaternion']
new_quaternion = Quaternion(qua[0], qua[1], qua[2], qua[3])
rotation_matrix = new_quaternion.rotation_matrix
current_position = el.Placement.Base
new_position = App.Vector(
current_position.x, current_position.y, current_position.z)
new_placement = App.Placement(new_position, rotation_matrix)
el.Placement = new_placement
App.ActiveDocument.recompute()
el.Placement.move(App.Vector(pos[0], pos[1], pos[2]))
# вычисление стабильность
computedStability(meshMark, meshDetailOfTheMarkZone)
pass
stepFilesPaths = []
for step in stepStructure:
stepFilesPaths.append(assemblyDir+'sdf/meshes/' + step + '.obj')
if not os.path.exists(directoryStableZones):
print('Zones not found automatic calculation started')
autoStabilityZoneComputed(stepFilesPaths, directoryStableZones)
sequencesJoin = {}
for arrayCounter in range(len(sequences)):
for indexCounter in range(len(sequences[arrayCounter])):
if (indexCounter != 0):
if (sequencesJoin.get(arrayCounter) == None):
sequencesJoin[arrayCounter] = {
indexCounter: sequences[arrayCounter][0:indexCounter+1]
}
else:
sequencesJoin[arrayCounter][indexCounter] = sequences[arrayCounter][0:indexCounter+1]
seqEvaluation = SequencesEvaluation(sequencesJoin, assemblyDir)
seqEvaluation.assemblyComputed()
print(seqEvaluation.result)
main()

View file

@ -1,4 +1,4 @@
{
"cadFilePath":"/home/idontsudo/framework/geometric_feasibility_predicate/cubes.FCStd",
"outPath":"/home/idontsudo/framework/geometric_feasibility_predicate/out/"
"cadFilePath":"/home/idontsudo/framework/asp/out/disk_and_axis_n (2).FCStd",
"outPath":"/home/idontsudo/framework/asp/out/"
}

View file

@ -189,7 +189,7 @@ class AdjacencyMatrix:
@staticmethod
def from_dict(obj: Any) -> 'AdjacencyMatrix':
assert isinstance(obj, dict)
all_pars = from_list(from_str, obj.get("allPars"))
all_pars = from_list(from_str, obj.get("allParts"))
first_detail = from_str(obj.get("firstDetail"))
matrix = from_dict(lambda x: from_list(from_str, x), obj.get("matrix"))
@ -197,7 +197,7 @@ class AdjacencyMatrix:
def to_dict(self) -> dict:
result: dict = {}
result["allPars"] = from_list(from_str, self.all_parts)
result["allParts"] = from_list(from_str, self.all_parts)
result["firstDetail"] = from_str(self.first_detail)
result["matrix"] = from_dict(
lambda x: from_list(from_str, x), self.matrix)
@ -449,13 +449,54 @@ class CadAdjacencyMatrix:
part.Shape.distToShape(nextPart.Shape)[0])
if (collisionResult == 0):
matrix[part.Label].append(nextPart.Label)
return AdjacencyMatrix(all_parts=GetAllPartsLabelsUseCase(
).call(), first_detail=GetFirstDetailUseCase().call(),
matrix=matrix
)
def reduce(function, iterable, initializer=None):
it = iter(iterable)
if initializer is None:
value = next(it)
else:
value = initializer
for element in it:
value = function(value, element)
return value
def to_ascii_hash(text):
ascii_values = [ord(character) for character in text]
return reduce(lambda x, y: x + y, ascii_values)
def matrixGetUniqueContact(matrix):
detailsToCheck = []
detailsHashCheck = {}
for k, v in matrix.items():
for el in v:
if (el != k):
hash = to_ascii_hash(k + el)
if (detailsHashCheck.get(hash) == None):
detailsHashCheck[hash] = hash
detailsToCheck.append({
'child': el,
'parent': k
})
return detailsToCheck
def intersectionComputed(parts):
App.activeDocument().addObject("Part::MultiCommon", "Common")
App.activeDocument().Common.Shapes = [parts[0], parts[1]]
App.activeDocument().getObject('Common').ViewObject.ShapeColor = getattr(parts[0].getLinkedObject(True).ViewObject, 'ShapeColor', App.activeDocument().getObject('Common').ViewObject.ShapeColor)
App.activeDocument().getObject('Common').ViewObject.DisplayMode = getattr(parts[0].getLinkedObject(True).ViewObject, 'DisplayMode', App.activeDocument().getObject('Common').ViewObject.DisplayMode)
App.ActiveDocument.recompute()
area = App.activeDocument().getObject('Common').Shape.Area
App.ActiveDocument.removeObject('Common')
return area
def main():
env = FS.readJSON('env.json')
cadFile = env['cadFilePath']
@ -464,14 +505,34 @@ def main():
return TypeError('CadFile not found env.json')
App.open(u'' + cadFile)
# Получение матрицы
topologyMatrix = CadAdjacencyMatrix().primitiveMatrix()
topologyMatrix = CadAdjacencyMatrix().matrixBySurfaces()
import json
sequences = json.dumps({"sequences": AllSequences(
topologyMatrix.matrix).adj_matrix_names}, ensure_ascii=False, indent=4)
matrix = topologyMatrix.matrix
contacts = matrixGetUniqueContact(matrix)
intersection_geometry = {
'status':True,
'recalculations':None
}
for el in contacts:
child = App.ActiveDocument.getObjectsByLabel(el.get('child'))[0]
parent = App.ActiveDocument.getObjectsByLabel(el.get('parent'))[0]
area = intersectionComputed([child,parent])
if(area != 0.0):
if(intersection_geometry.get('recalculations') == None):
intersection_geometry['status'] = False
intersection_geometry['recalculations'] = []
intersection_geometry['recalculations'].append({
'area':area,
'connect': el.get('child') + ' ' + el.get('parent')
})
FS.writeFile(json.dumps(intersection_geometry, ensure_ascii=False, indent=4), outPath, 'intersection_geometry.json')
FS.writeFile(sequences, outPath, 'sequences.json')
FS.writeFile(json.dumps(topologyMatrix.to_dict(),
ensure_ascii=False, indent=4), outPath, 'adjacency_matrix.json')
main()

View file

@ -3,15 +3,15 @@ import json
class FS:
def readJSON(path:str):
return json.loads((open(path)).read())
f = open(path)
return json.loads((f).read())
def writeFile(data, filePath, fileName):
file_to_open = filePath + fileName
f = open(file_to_open, 'w', )
f = open(file_to_open, 'w')
f.write(data)
f.close()
def readFile(path):
return open(path).read()

View file

@ -5,7 +5,8 @@ from src.model.asm4_structure import Asm4Structure
from src.usecases.assembly_to_pddl_use_case import AssemblyToPddlUseCase
# python3 main.py --stepStructurePath /Users/idontsudo/robo/step-structure.json --outPath /Users/idontsudo/robo/pddl/out/
# python3 main.py --stepStructurePath --outPath /Users/idontsudo/robo/pddl/out/
# python3 main.py --stepStructurePath /Users/idontsudo/framework/asp/out/step-structure.json --outPath /Users/idontsudo/robo/pddl/out/
if __name__ == "__main__":
parser = argparse.ArgumentParser()

View file

@ -1,8 +1,7 @@
import argparse
from usecases.stability_check_usecase import StabilityCheckUseCase
# python3 main.py --aspPath /Users/idontsudo/framework/asp/out/
# python3 main.py --aspPath /Users/idontsudo/framework/asp/out
def main():
parser = argparse.ArgumentParser()
@ -10,3 +9,4 @@ def main():
args = parser.parse_args()
StabilityCheckUseCase().call(args.aspPath)
main()

View file

@ -116,7 +116,6 @@ class StabilityCheckUseCase:
f.write(urdf)
f.close()
urdfs.append(os.path.abspath(f.name))
return urdfs
def executeSimulation(self, assembly: list[str], outPath: str, urdfGeneration: dict[str:str], duration: int) -> list['SimulatorStabilityResultModel']:
@ -133,7 +132,6 @@ class StabilityCheckUseCase:
for el in urdfs:
id = p.loadURDF(el)
bulletIds.append(id)
for i in range(duration):
if (i + 200 == duration):
inc = 0