Stability process predicate evaluation module
This commit is contained in:
parent
78e31ea49c
commit
03bc34539c
19 changed files with 1365 additions and 123 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -112,6 +112,4 @@ install_plugin_cad.sh
|
||||||
*#
|
*#
|
||||||
.#*
|
.#*
|
||||||
\#*\#
|
\#*\#
|
||||||
Cube3
|
out/
|
||||||
sdf-generation
|
|
||||||
p.json
|
|
|
@ -6,7 +6,7 @@ import { HttpRepository, HttpMethod, HttpRoute } from '../../core/repository/htt
|
||||||
|
|
||||||
export const pathAjaxTopologyScreen = '/topology/adjax/usecase/'
|
export const pathAjaxTopologyScreen = '/topology/adjax/usecase/'
|
||||||
export interface IAdjaxMatrix {
|
export interface IAdjaxMatrix {
|
||||||
allPars: string[];
|
allParts: string[];
|
||||||
firstDetail: string;
|
firstDetail: string;
|
||||||
matrix: StringMap;
|
matrix: StringMap;
|
||||||
matrixError: StringMap | null;
|
matrixError: StringMap | null;
|
||||||
|
|
877
asp/helper/xmlformatter.py
Normal file
877
asp/helper/xmlformatter.py
Normal 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"&", "&", 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 += "<!--%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)
|
|
@ -7,7 +7,6 @@ from src.usecases.sdf_sub_assembly_usecase import SdfSubAssemblyUseCase
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('--generationFolder', help='FreeCad generation folder')
|
parser.add_argument('--generationFolder', help='FreeCad generation folder')
|
||||||
|
@ -27,8 +26,8 @@ if __name__ == "__main__":
|
||||||
for el in geometryFiles:
|
for el in geometryFiles:
|
||||||
geometryModels.append(GeometryModel.from_dict(
|
geometryModels.append(GeometryModel.from_dict(
|
||||||
FS.readJSON(args.generationFolder + '/assets/' + el)))
|
FS.readJSON(args.generationFolder + '/assets/' + el)))
|
||||||
if os.path.exists(outPath + 'sdf-generation/'):
|
# if os.path.exists(outPath + 'sdf-generation/'):
|
||||||
shutil.rmtree(path=outPath + 'sdf-generation/')
|
# shutil.rmtree(path=outPath + 'sdf-generation/')
|
||||||
|
|
||||||
if (args.format == 'sdf'):
|
if (args.format == 'sdf'):
|
||||||
SdfSubAssemblyUseCase().call(
|
SdfSubAssemblyUseCase().call(
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
</inertial>
|
</inertial>
|
||||||
<visual>
|
<visual>
|
||||||
<geometry>
|
<geometry>
|
||||||
<mesh filename="{stl}" scale="1 1 1" />
|
<mesh filename="{stl}" scale="0.001 0.001 0.001" />
|
||||||
</geometry>
|
</geometry>
|
||||||
<material name="white">
|
<material name="white">
|
||||||
<color rgba="1. 1. 1. 1." />
|
<color rgba="1. 1. 1. 1." />
|
||||||
|
@ -23,14 +23,14 @@
|
||||||
</visual>
|
</visual>
|
||||||
<collision>
|
<collision>
|
||||||
<geometry>
|
<geometry>
|
||||||
<mesh filename="{stl}" scale="1 1 1" />
|
<mesh filename="{stl}" scale="0.001 0.001 0.001" />
|
||||||
</geometry>
|
</geometry>
|
||||||
</collision>
|
</collision>
|
||||||
<friction>
|
<friction>
|
||||||
<ode>
|
<ode>
|
||||||
<mu>0.2</mu>
|
<mu>0.2</mu>
|
||||||
<mu2>0.1</mu2>
|
<mu2>0.1</mu2>
|
||||||
<fdir1>1 0 0</fdir1>
|
<fdir1>1 0 0</fdir1>
|
||||||
</ode>
|
</ode>
|
||||||
</friction>
|
</friction>
|
||||||
</link>
|
</link>
|
||||||
|
|
|
@ -30,6 +30,9 @@ def to_class(c, x):
|
||||||
return x.to_dict()
|
return x.to_dict()
|
||||||
|
|
||||||
|
|
||||||
|
DELIMITER_SCALE = 10000
|
||||||
|
|
||||||
|
|
||||||
class GeometryModel:
|
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):
|
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
|
self.name = name
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
|
from helper.xmlformatter import Formatter
|
||||||
from src.model.enum import Enum
|
from src.model.enum import Enum
|
||||||
from helper.fs import FS
|
from helper.fs import FS
|
||||||
import xmlformatter
|
|
||||||
|
|
||||||
class FormatterUseCase:
|
class FormatterUseCase:
|
||||||
def call(outPath: str, format: str):
|
def call(outPath: str, format: str):
|
||||||
formatter = xmlformatter(
|
formatter = Formatter(
|
||||||
indent="1", indent_char="\t", encoding_output="ISO-8859-1", preserve=["literal"])
|
indent="1", indent_char="\t", encoding_output="ISO-8859-1", preserve=["literal"])
|
||||||
|
|
||||||
files = FS.readFilesTypeFolder(
|
files = FS.readFilesTypeFolder(
|
||||||
|
|
|
@ -1,16 +1,11 @@
|
||||||
from typing import Optional
|
|
||||||
from helper.fs import FS
|
from helper.fs import FS
|
||||||
from src.model.enum import Enum
|
from src.model.enum import Enum
|
||||||
from src.model.asm import Assembly
|
from src.model.asm import Assembly
|
||||||
from src.model.sdf_geometry import GeometryModel
|
from src.model.sdf_geometry import GeometryModel
|
||||||
from helper.fs import filterModels, listGetFirstValue
|
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
URDF_FILE_FORMAT = '.urdf'
|
URDF_FILE_FORMAT = '.urdf'
|
||||||
URDF_GENERATOR_FILE = 'urdf-generation' + '.json'
|
URDF_GENERATOR_FILE = 'urdf-generation' + '.json'
|
||||||
|
|
||||||
|
@ -19,6 +14,6 @@ class UrdfSubAssemblyUseCase(Assembly):
|
||||||
dirPath = generationFolder + Enum.folderPath
|
dirPath = generationFolder + Enum.folderPath
|
||||||
asm = {}
|
asm = {}
|
||||||
for el in geometryModels:
|
for el in geometryModels:
|
||||||
asm[el.name] = el.toUrdf()
|
asm[el.name] = el.toUrdf()
|
||||||
FS.writeFile(data=json.dumps(asm,indent=4),
|
FS.writeFile(data=json.dumps(asm,indent=4),
|
||||||
fileName=URDF_GENERATOR_FILE, filePath=dirPath)
|
fileName=URDF_GENERATOR_FILE, filePath=dirPath)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"doc": "/home/idontsudo/t/framework/asp-review-app/server/public/cubes/cubes.FCStd",
|
"doc": "/home/idontsudo/framework/asp/out/disk_and_axis_n.FCStd",
|
||||||
"out": "/home/idontsudo/t/framework/cad_generation",
|
"out": "/home/idontsudo/framework/asp/out",
|
||||||
"resultURL": "http://localhost:3002/assembly/save/out",
|
"resultURL": "http://localhost:3002/assembly/save/out",
|
||||||
"projectId": "cubes"
|
"projectId": "cubes"
|
||||||
}
|
}
|
|
@ -36,9 +36,9 @@ class RobossemblerFreeCadExportScenario:
|
||||||
self.geometry(directory)
|
self.geometry(directory)
|
||||||
ExportAssemblyThemAllUseCase().call(directoryExport)
|
ExportAssemblyThemAllUseCase().call(directoryExport)
|
||||||
|
|
||||||
shutil.make_archive(directory, 'zip', directory)
|
# shutil.make_archive(directory, 'zip', directory)
|
||||||
|
|
||||||
shutil.rmtree(directory)
|
# shutil.rmtree(directory)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def geometry(self, outPutsPath: str):
|
def geometry(self, outPutsPath: str):
|
||||||
|
|
|
@ -1,38 +1,41 @@
|
||||||
import FreeCAD as App
|
import FreeCAD as App
|
||||||
from model.sdf_geometry_model import SdfGeometryModel
|
from model.sdf_geometry_model import SdfGeometryModel
|
||||||
|
|
||||||
from helper.is_solid import is_object_solid
|
from helper.is_solid import is_object_solid
|
||||||
|
|
||||||
|
|
||||||
class SdfGeometryUseCase:
|
class SdfGeometryUseCase:
|
||||||
ShapePropertyCheck = ['Mass','MatrixOfInertia','Placement', ]
|
ShapePropertyCheck = ['Mass', 'MatrixOfInertia', 'Placement', ]
|
||||||
PartPropertyCheck = ['Shape']
|
PartPropertyCheck = ['Shape']
|
||||||
def call(self, stlPaths:dict) -> list[SdfGeometryModel]:
|
|
||||||
|
def call(self, stlPaths: dict) -> list[SdfGeometryModel]:
|
||||||
materialSolid = {}
|
materialSolid = {}
|
||||||
for el in App.ActiveDocument.Objects:
|
for el in App.ActiveDocument.Objects:
|
||||||
if str(el) == '<App::MaterialObjectPython object>':
|
if str(el) == '<App::MaterialObjectPython object>':
|
||||||
friction = el.Material.get('SlidingFriction')
|
friction = el.Material.get('SlidingFriction')
|
||||||
for i in el.References:
|
for i in el.References:
|
||||||
materialSolid[i[0].Label] = friction
|
materialSolid[i[0].Label] = friction
|
||||||
geometry = []
|
geometry = []
|
||||||
try:
|
try:
|
||||||
for el in App.ActiveDocument.Objects:
|
for el in App.ActiveDocument.Objects:
|
||||||
|
|
||||||
if is_object_solid(el):
|
if is_object_solid(el):
|
||||||
mass = el.Shape.Mass
|
mass = el.Shape.Mass
|
||||||
inertia = el.Shape.MatrixOfInertia
|
inertia = el.Shape.MatrixOfInertia
|
||||||
pos = el.Shape.Placement
|
pos = el.Shape.Placement
|
||||||
inertia = el.Shape.MatrixOfInertia
|
inertia = el.Shape.MatrixOfInertia
|
||||||
name = el.Label
|
name = el.Label
|
||||||
ixx = str(inertia.A11 / 1000000)
|
delimiter = 1000000
|
||||||
ixy = str(inertia.A12 / 1000000)
|
ixx = str(inertia.A11 / delimiter)
|
||||||
ixz = str(inertia.A13 / 1000000)
|
ixy = str(inertia.A12 / delimiter)
|
||||||
iyy = str(inertia.A22 / 1000000)
|
ixz = str(inertia.A13 / delimiter)
|
||||||
iyz = str(inertia.A23 / 1000000)
|
iyy = str(inertia.A22 / delimiter)
|
||||||
izz = str(inertia.A33 / 1000000)
|
iyz = str(inertia.A23 / delimiter)
|
||||||
massSDF = str(mass / 1000000)
|
izz = str(inertia.A33 / delimiter)
|
||||||
posX = str(pos.Base[0] / 1000000)
|
massSDF = str(mass / delimiter)
|
||||||
posY = str(pos.Base[1] / 1000000)
|
posX = str(pos.Base[0] / delimiter)
|
||||||
posZ = str(pos.Base[2] / 1000000)
|
posY = str(pos.Base[1] / delimiter)
|
||||||
|
posZ = str(pos.Base[2] / delimiter)
|
||||||
eulerX = str(pos.Rotation.toEuler()[0])
|
eulerX = str(pos.Rotation.toEuler()[0])
|
||||||
eulerY = str(pos.Rotation.toEuler()[1])
|
eulerY = str(pos.Rotation.toEuler()[1])
|
||||||
eulerZ = str(pos.Rotation.toEuler()[2])
|
eulerZ = str(pos.Rotation.toEuler()[2])
|
||||||
|
@ -42,10 +45,10 @@ class SdfGeometryUseCase:
|
||||||
geometry.append(
|
geometry.append(
|
||||||
SdfGeometryModel(
|
SdfGeometryModel(
|
||||||
stl=stlPaths.get(el.Label),
|
stl=stlPaths.get(el.Label),
|
||||||
name=name,
|
name=name,
|
||||||
ixx=ixx,
|
ixx=ixx,
|
||||||
ixz=ixz,
|
ixz=ixz,
|
||||||
ixy=ixy,
|
ixy=ixy,
|
||||||
iyy=iyy,
|
iyy=iyy,
|
||||||
iyz=iyz,
|
iyz=iyz,
|
||||||
izz=izz,
|
izz=izz,
|
||||||
|
@ -53,8 +56,8 @@ class SdfGeometryUseCase:
|
||||||
posX=posX,
|
posX=posX,
|
||||||
posY=posY,
|
posY=posY,
|
||||||
posZ=posZ,
|
posZ=posZ,
|
||||||
eulerX=eulerX,
|
eulerX=eulerX,
|
||||||
eulerY=eulerY,
|
eulerY=eulerY,
|
||||||
eulerZ=eulerZ,
|
eulerZ=eulerZ,
|
||||||
friction=materialSolid.get(el.Label) or '',
|
friction=materialSolid.get(el.Label) or '',
|
||||||
centerMassX=centerMassX,
|
centerMassX=centerMassX,
|
||||||
|
@ -64,10 +67,4 @@ class SdfGeometryUseCase:
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
return geometry
|
return geometry
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
3
cad_stability_check/.gitignore
vendored
3
cad_stability_check/.gitignore
vendored
|
@ -92,4 +92,5 @@ ENV/
|
||||||
|
|
||||||
# Rope project settings
|
# Rope project settings
|
||||||
.ropeproject
|
.ropeproject
|
||||||
env.json
|
env.json
|
||||||
|
out/
|
|
@ -1,15 +1,266 @@
|
||||||
# Алгоритм генерации графа с помощью оценки стабильности подсборок в физическом движке PyBullet
|
# Алгоритм генерации графа с помощью оценки стабильности подсборок в физическом движке PyBullet
|
||||||
|
from typing import Any, TypeVar, Type, cast
|
||||||
import FreeCAD as App
|
import FreeCAD as App
|
||||||
import json
|
import json
|
||||||
import re
|
import importOBJ
|
||||||
from pyquaternion import Quaternion
|
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):
|
def importObjAtPath(path: str, inc: int):
|
||||||
import importOBJ
|
|
||||||
importOBJ.insert(u"" + path, App.ActiveDocument.Label)
|
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):
|
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)
|
childElement.Name).getLinkedObject(True).ViewObject, 'DisplayMode', App.ActiveDocument.getObject('Common').ViewObject.DisplayMode)
|
||||||
App.ActiveDocument.recompute()
|
App.ActiveDocument.recompute()
|
||||||
obj = App.ActiveDocument.getObjectsByLabel('Common')[0]
|
obj = App.ActiveDocument.getObjectsByLabel('Common')[0]
|
||||||
|
|
||||||
shp = obj.Shape
|
shp = obj.Shape
|
||||||
bbox = shp.BoundBox
|
bbox = shp.BoundBox
|
||||||
# Если после операции пересечения зона обьекта совпадает с зоной тестируемого обьекта то тест прошел успешно
|
# Если после операции пересечения зона обьекта совпадает с зоной тестируемого обьекта то тест прошел успешно
|
||||||
if bbox.XLength == rootElement.XLength and bbox.YLength == rootElement.YLength and rootElement.ZLength == bbox.ZLength:
|
if bbox.XLength == rootElement.XLength and bbox.YLength == rootElement.YLength and rootElement.ZLength == bbox.ZLength:
|
||||||
return True
|
return True
|
||||||
return False
|
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():
|
def main():
|
||||||
App.newDocument()
|
App.newDocument()
|
||||||
# загрузка env интерфейса
|
|
||||||
env = json.loads((open('./env.json')).read())
|
env = json.loads((open('./env.json')).read())
|
||||||
|
|
||||||
# получение пути координат файла
|
assemblyDir = env.get('aspPath')
|
||||||
coordinatesFilePath = env.get('pathToTheSimulationCoordinatesFile')
|
sequencesJSON = json.loads((open(assemblyDir + 'sequences.json')).read())
|
||||||
|
directoryStableZones = assemblyDir + 'stability/zones/'
|
||||||
|
|
||||||
# получение папки сборки
|
sequences = sequencesJSON.get('sequences')
|
||||||
assemblyFolder = env.get('generationFolder')
|
stepStructure = json.loads(
|
||||||
buildNumber = int(re.findall(r'\d', coordinatesFilePath)[0])
|
(open(assemblyDir + 'step-structure.json')).read())
|
||||||
|
|
||||||
# загрузка последовательности сборки ввиде списка строк деталей
|
stepFilesPaths = []
|
||||||
assemblyStructure = json.loads(
|
for step in stepStructure:
|
||||||
(open(assemblyFolder + 'step-structure.json')).read())
|
stepFilesPaths.append(assemblyDir+'sdf/meshes/' + step + '.obj')
|
||||||
# получение номера тестируемой сборки
|
if not os.path.exists(directoryStableZones):
|
||||||
assemblyNumber = int(buildNumber)
|
print('Zones not found automatic calculation started')
|
||||||
# получение тестируемой детали в сборке
|
autoStabilityZoneComputed(stepFilesPaths, directoryStableZones)
|
||||||
activeDetail = assemblyStructure[assemblyNumber]
|
|
||||||
|
sequencesJoin = {}
|
||||||
subassemblyNotParticipatingInMarkup = assemblyStructure[0:assemblyNumber - 1]
|
|
||||||
detailOfTheMarkingZoneOfWhich = assemblyStructure[assemblyNumber - 1]
|
for arrayCounter in range(len(sequences)):
|
||||||
importObjAtPath(getFullPathObj(assemblyFolder, activeDetail))
|
for indexCounter in range(len(sequences[arrayCounter])):
|
||||||
importObjAtPath(getFullPathObj(
|
if (indexCounter != 0):
|
||||||
assemblyFolder, detailOfTheMarkingZoneOfWhich))
|
if (sequencesJoin.get(arrayCounter) == None):
|
||||||
meshMark = App.ActiveDocument.Objects[0]
|
sequencesJoin[arrayCounter] = {
|
||||||
meshDetailOfTheMarkZone = App.ActiveDocument.Objects[1]
|
indexCounter: sequences[arrayCounter][0:indexCounter+1]
|
||||||
meshMark.ViewObject.ShapeColor = (0.31, 0.77, 0.87)
|
}
|
||||||
meshDetailOfTheMarkZone.ViewObject.ShapeColor = (0.68, 0.66, 0.95)
|
else:
|
||||||
for el in list(map(lambda el: getFullPathObj(assemblyFolder, el), subassemblyNotParticipatingInMarkup)):
|
sequencesJoin[arrayCounter][indexCounter] = sequences[arrayCounter][0:indexCounter+1]
|
||||||
importObjAtPath(el)
|
|
||||||
for el in App.ActiveDocument.Objects[2:App.ActiveDocument.Objects.__len__()]:
|
|
||||||
el.ViewObject.ShapeColor = (0.32, 0.05, 0.38)
|
|
||||||
# загрузка координат
|
seqEvaluation = SequencesEvaluation(sequencesJoin, assemblyDir)
|
||||||
coordinates = json.loads((open(coordinatesFilePath)).read())
|
seqEvaluation.assemblyComputed()
|
||||||
inc = 1
|
print(seqEvaluation.result)
|
||||||
# перемещение обьектов в документе на точки стабильность полученные из 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
|
|
||||||
|
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{
|
{
|
||||||
"cadFilePath":"/home/idontsudo/framework/geometric_feasibility_predicate/cubes.FCStd",
|
"cadFilePath":"/home/idontsudo/framework/asp/out/disk_and_axis_n (2).FCStd",
|
||||||
"outPath":"/home/idontsudo/framework/geometric_feasibility_predicate/out/"
|
"outPath":"/home/idontsudo/framework/asp/out/"
|
||||||
}
|
}
|
|
@ -189,7 +189,7 @@ class AdjacencyMatrix:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_dict(obj: Any) -> 'AdjacencyMatrix':
|
def from_dict(obj: Any) -> 'AdjacencyMatrix':
|
||||||
assert isinstance(obj, dict)
|
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"))
|
first_detail = from_str(obj.get("firstDetail"))
|
||||||
matrix = from_dict(lambda x: from_list(from_str, x), obj.get("matrix"))
|
matrix = from_dict(lambda x: from_list(from_str, x), obj.get("matrix"))
|
||||||
|
|
||||||
|
@ -197,7 +197,7 @@ class AdjacencyMatrix:
|
||||||
|
|
||||||
def to_dict(self) -> dict:
|
def to_dict(self) -> dict:
|
||||||
result: 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["firstDetail"] = from_str(self.first_detail)
|
||||||
result["matrix"] = from_dict(
|
result["matrix"] = from_dict(
|
||||||
lambda x: from_list(from_str, x), self.matrix)
|
lambda x: from_list(from_str, x), self.matrix)
|
||||||
|
@ -449,13 +449,54 @@ class CadAdjacencyMatrix:
|
||||||
part.Shape.distToShape(nextPart.Shape)[0])
|
part.Shape.distToShape(nextPart.Shape)[0])
|
||||||
if (collisionResult == 0):
|
if (collisionResult == 0):
|
||||||
matrix[part.Label].append(nextPart.Label)
|
matrix[part.Label].append(nextPart.Label)
|
||||||
|
|
||||||
return AdjacencyMatrix(all_parts=GetAllPartsLabelsUseCase(
|
return AdjacencyMatrix(all_parts=GetAllPartsLabelsUseCase(
|
||||||
).call(), first_detail=GetFirstDetailUseCase().call(),
|
).call(), first_detail=GetFirstDetailUseCase().call(),
|
||||||
matrix=matrix
|
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():
|
def main():
|
||||||
env = FS.readJSON('env.json')
|
env = FS.readJSON('env.json')
|
||||||
cadFile = env['cadFilePath']
|
cadFile = env['cadFilePath']
|
||||||
|
@ -464,14 +505,34 @@ def main():
|
||||||
return TypeError('CadFile not found env.json')
|
return TypeError('CadFile not found env.json')
|
||||||
App.open(u'' + cadFile)
|
App.open(u'' + cadFile)
|
||||||
|
|
||||||
# Получение матрицы
|
topologyMatrix = CadAdjacencyMatrix().matrixBySurfaces()
|
||||||
topologyMatrix = CadAdjacencyMatrix().primitiveMatrix()
|
|
||||||
import json
|
import json
|
||||||
sequences = json.dumps({"sequences": AllSequences(
|
sequences = json.dumps({"sequences": AllSequences(
|
||||||
topologyMatrix.matrix).adj_matrix_names}, ensure_ascii=False, indent=4)
|
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(sequences, outPath, 'sequences.json')
|
||||||
FS.writeFile(json.dumps(topologyMatrix.to_dict(),
|
FS.writeFile(json.dumps(topologyMatrix.to_dict(),
|
||||||
ensure_ascii=False, indent=4), outPath, 'adjacency_matrix.json')
|
ensure_ascii=False, indent=4), outPath, 'adjacency_matrix.json')
|
||||||
|
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -3,15 +3,15 @@ import json
|
||||||
|
|
||||||
class FS:
|
class FS:
|
||||||
def readJSON(path:str):
|
def readJSON(path:str):
|
||||||
return json.loads((open(path)).read())
|
f = open(path)
|
||||||
|
return json.loads((f).read())
|
||||||
|
|
||||||
def writeFile(data, filePath, fileName):
|
def writeFile(data, filePath, fileName):
|
||||||
|
|
||||||
file_to_open = filePath + fileName
|
file_to_open = filePath + fileName
|
||||||
|
f = open(file_to_open, 'w')
|
||||||
f = open(file_to_open, 'w', )
|
|
||||||
|
|
||||||
f.write(data)
|
f.write(data)
|
||||||
|
f.close()
|
||||||
|
|
||||||
def readFile(path):
|
def readFile(path):
|
||||||
return open(path).read()
|
return open(path).read()
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,8 @@ from src.model.asm4_structure import Asm4Structure
|
||||||
from src.usecases.assembly_to_pddl_use_case import AssemblyToPddlUseCase
|
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__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import argparse
|
import argparse
|
||||||
from usecases.stability_check_usecase import StabilityCheckUseCase
|
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():
|
def main():
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
|
@ -10,3 +9,4 @@ def main():
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
StabilityCheckUseCase().call(args.aspPath)
|
StabilityCheckUseCase().call(args.aspPath)
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -116,7 +116,6 @@ class StabilityCheckUseCase:
|
||||||
f.write(urdf)
|
f.write(urdf)
|
||||||
f.close()
|
f.close()
|
||||||
urdfs.append(os.path.abspath(f.name))
|
urdfs.append(os.path.abspath(f.name))
|
||||||
|
|
||||||
return urdfs
|
return urdfs
|
||||||
|
|
||||||
def executeSimulation(self, assembly: list[str], outPath: str, urdfGeneration: dict[str:str], duration: int) -> list['SimulatorStabilityResultModel']:
|
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:
|
for el in urdfs:
|
||||||
id = p.loadURDF(el)
|
id = p.loadURDF(el)
|
||||||
bulletIds.append(id)
|
bulletIds.append(id)
|
||||||
|
|
||||||
for i in range(duration):
|
for i in range(duration):
|
||||||
if (i + 200 == duration):
|
if (i + 200 == duration):
|
||||||
inc = 0
|
inc = 0
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue