diff --git a/.gitignore b/.gitignore
index 3332cf1..33406e3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -112,6 +112,4 @@ install_plugin_cad.sh
*#
.#*
\#*\#
-Cube3
-sdf-generation
-p.json
\ No newline at end of file
+out/
\ No newline at end of file
diff --git a/asp-review-app/ui/src/features/topology_ajax_preview/topology_ajax_preview.tsx b/asp-review-app/ui/src/features/topology_ajax_preview/topology_ajax_preview.tsx
index a08435f..d2a1584 100644
--- a/asp-review-app/ui/src/features/topology_ajax_preview/topology_ajax_preview.tsx
+++ b/asp-review-app/ui/src/features/topology_ajax_preview/topology_ajax_preview.tsx
@@ -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;
diff --git a/asp/helper/xmlformatter.py b/asp/helper/xmlformatter.py
new file mode 100644
index 0000000..cf41b29
--- /dev/null
+++ b/asp/helper/xmlformatter.py
@@ -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 += ""
+ 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"&", "&", str)
+ str = re.sub(r"<", "<", 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 += "" % 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 += "" % (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 += ""
+ return str
+
+ class NotationDecl(Token):
+ def __unicode__(self):
+ str = self.indent_create()
+ str += "" % (
+ 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 ""
+ 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 = " -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)
\ No newline at end of file
diff --git a/asp/main.py b/asp/main.py
index dba64a5..17783c9 100644
--- a/asp/main.py
+++ b/asp/main.py
@@ -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(
diff --git a/asp/mocks/urdf/model.urdf b/asp/mocks/urdf/model.urdf
index 56e19d8..74aec10 100644
--- a/asp/mocks/urdf/model.urdf
+++ b/asp/mocks/urdf/model.urdf
@@ -15,7 +15,7 @@
-
+
@@ -23,14 +23,14 @@
-
+
- 0.2
- 0.1
- 1 0 0
+ 0.2
+ 0.1
+ 1 0 0
diff --git a/asp/src/model/sdf_geometry.py b/asp/src/model/sdf_geometry.py
index 08b7e44..3fdaa8b 100644
--- a/asp/src/model/sdf_geometry.py
+++ b/asp/src/model/sdf_geometry.py
@@ -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
diff --git a/asp/src/usecases/formatter_usecase.py b/asp/src/usecases/formatter_usecase.py
index 9efa2e8..f1a9d97 100644
--- a/asp/src/usecases/formatter_usecase.py
+++ b/asp/src/usecases/formatter_usecase.py
@@ -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(
diff --git a/asp/src/usecases/urdf_sub_assembly_usecase.py b/asp/src/usecases/urdf_sub_assembly_usecase.py
index f035e5f..4d652f7 100644
--- a/asp/src/usecases/urdf_sub_assembly_usecase.py
+++ b/asp/src/usecases/urdf_sub_assembly_usecase.py
@@ -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)
diff --git a/cad_generation/env.json b/cad_generation/env.json
index 3945e7a..f9633a4 100644
--- a/cad_generation/env.json
+++ b/cad_generation/env.json
@@ -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"
}
\ No newline at end of file
diff --git a/cad_generation/scenarios/robossembler_freecad_export_scenario.py b/cad_generation/scenarios/robossembler_freecad_export_scenario.py
index 717a127..c5b74da 100644
--- a/cad_generation/scenarios/robossembler_freecad_export_scenario.py
+++ b/cad_generation/scenarios/robossembler_freecad_export_scenario.py
@@ -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):
diff --git a/cad_generation/usecases/get_sdf_geometry_usecase.py b/cad_generation/usecases/get_sdf_geometry_usecase.py
index 4a4ccb4..6ea2931 100644
--- a/cad_generation/usecases/get_sdf_geometry_usecase.py
+++ b/cad_generation/usecases/get_sdf_geometry_usecase.py
@@ -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) == '':
+ if str(el) == '':
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
-
-
-
-
-
diff --git a/cad_stability_check/.gitignore b/cad_stability_check/.gitignore
index 2b2f40c..e482277 100644
--- a/cad_stability_check/.gitignore
+++ b/cad_stability_check/.gitignore
@@ -92,4 +92,5 @@ ENV/
# Rope project settings
.ropeproject
-env.json
\ No newline at end of file
+env.json
+out/
\ No newline at end of file
diff --git a/cad_stability_check/main.py b/cad_stability_check/main.py
index d44593f..d0f91a2 100644
--- a/cad_stability_check/main.py
+++ b/cad_stability_check/main.py
@@ -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()
diff --git a/geometric_feasibility_predicate/env.json b/geometric_feasibility_predicate/env.json
index 4bc2d96..9c62939 100644
--- a/geometric_feasibility_predicate/env.json
+++ b/geometric_feasibility_predicate/env.json
@@ -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/"
}
\ No newline at end of file
diff --git a/geometric_feasibility_predicate/main.py b/geometric_feasibility_predicate/main.py
index 7e717fa..d8840ab 100644
--- a/geometric_feasibility_predicate/main.py
+++ b/geometric_feasibility_predicate/main.py
@@ -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()
+
\ No newline at end of file
diff --git a/pddl/helper/fs.py b/pddl/helper/fs.py
index 1bd3dbe..bcd33e6 100644
--- a/pddl/helper/fs.py
+++ b/pddl/helper/fs.py
@@ -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()
diff --git a/pddl/main.py b/pddl/main.py
index 9c05f1c..0856cf0 100644
--- a/pddl/main.py
+++ b/pddl/main.py
@@ -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()
diff --git a/stability_process_predicate/main.py b/stability_process_predicate/main.py
index 06028a9..89ab934 100644
--- a/stability_process_predicate/main.py
+++ b/stability_process_predicate/main.py
@@ -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()
+
\ No newline at end of file
diff --git a/stability_process_predicate/usecases/stability_check_usecase.py b/stability_process_predicate/usecases/stability_check_usecase.py
index 86d6004..da35575 100644
--- a/stability_process_predicate/usecases/stability_check_usecase.py
+++ b/stability_process_predicate/usecases/stability_check_usecase.py
@@ -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