Skip to content

Commit

Permalink
fix:Eliminate duplicates
Browse files Browse the repository at this point in the history
  • Loading branch information
huyenngn committed Dec 21, 2023
1 parent 5df618b commit f4ee1a9
Show file tree
Hide file tree
Showing 7 changed files with 225 additions and 1,232 deletions.
6 changes: 3 additions & 3 deletions capella_ros_tools/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
@click.option(
"--exists-action",
"action",
type=click.Choice(["k", "o", "a"], case_sensitive=False),
type=click.Choice(["k", "o", "a", "c"], case_sensitive=False),
default="c" if sys.stdin.isatty() else "a",
help="Default action when an element already exists: (k)eep, (o)verwrite, (a)bort.",
help="Default action when an element already exists: (c)heck, (k)eep, (o)verwrite, (a)bort.",
)
@click.option("--port", "-p", type=int, help="Port for HTML display.")
@click.option(
Expand All @@ -43,7 +43,7 @@
nargs=2,
type=(
click.Choice(["capella", "messages"]),
click.Path(exists=True, path_type=Path),
click.Path(path_type=Path),
),
required=True,
help="Output file type and path.",
Expand Down
134 changes: 131 additions & 3 deletions capella_ros_tools/display/templates/class.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,137 @@ <h1>
</table>
</main>
<aside>
<div data-zoom-on-wheel data-pan-on-drag>
{{ element.tree_view.as_svg | safe }}
</div>
<div>{{ element.tree_view.as_svg | safe }}</div>
<script type="module">
import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm";
// Select the SVG container
const svg = d3.select(".ClassDiagramBlank");
svg.attr("width", "100%").attr("height", "100%");

// Create a new group element
const svgContainer = svg.append("g");

svgContainer.attr("id", "svgContainer");

// Select all children of the SVG container except the new group
const children = svg.selectAll(
":scope > *:not(#svgContainer)"
);

// Move all children into the group
children.each(function () {
svgContainer.node().appendChild(this);
});

svgContainer.select("rect").remove();

function escapeUUIDForCSSSelector(uuid) {
// Escape first character if it's a digit
let escapedUUID = uuid[0].match(/^\d/)
? "\\3" + uuid[0] + " " + uuid.slice(1)
: uuid;
// Escape hyphens
return escapedUUID.replace(/-/g, "\\-");
}

let groupedNodes = [];

svgContainer.selectAll(".Box").each(function () {
var depElements = d3.select(this).selectAll("g");

if (groupedNodes.includes(this.id)) {
return;
}
groupedNodes.push(this.id);

var contextGroup = svgContainer.append("g");
contextGroup.attr("class", "contextGroup");

if (depElements.nodes().length == 0) {
contextGroup
.node()
.appendChild(d3.select(this).node());
} else {
depElements.nodes()[0].classList.forEach((id) => {
groupedNodes.push(id);
let escapedUUID = escapeUUIDForCSSSelector(id);
contextGroup
.node()
.appendChild(
d3.select("#" + escapedUUID).node()
);
});
}
});

const draggableElements =
svgContainer.selectAll(".contextGroup");

// Zoom and pan
svg.call(
d3
.zoom()
.extent([
[0, 0],
[svg.node().clientWidth, svg.node().clientHeight],
])
.scaleExtent([0.1, 10])
.on("zoom", (event) => {
svgContainer.attr("transform", event.transform);
})
);

// Create an array of data with the same length as your selection
let data = Array.from(draggableElements.nodes(), () => ({
currentX: 0,
currentY: 0,
deltaX: 0,
deltaY: 0,
}));

// Bind the data to your elements
draggableElements.data(data);

// Apply the drag behavior to the selected elements
draggableElements.call(
d3
.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
);

function dragStarted(event, d) {
// Implement any custom behavior on drag start if needed
d.deltaX = event.x - d.currentX;
d.deltaY = event.y - d.currentY;
}

function dragged(event, d) {
// Implement any custom behavior on drag
d.currentX = event.x - d.deltaX;
d.currentY = event.y - d.deltaY;

d3.select(this).attr(
"transform",
`translate(${d.currentX},${d.currentY})`
);

var boundingBox = svgContainer.node().getBBox();
svgContainer
.attr("width", boundingBox.width)
.attr("height", boundingBox.height)
.attr(
"viewBox",
`${boundingBox.x} ${boundingBox.y} ${boundingBox.width} ${boundingBox.height}`
);
}

function dragEnded(event, d) {
d.deltaX = event.x;
d.deltaY = event.y;
}
</script>
</aside>
</body>
</html>
21 changes: 15 additions & 6 deletions capella_ros_tools/modules/capella/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ def create_classes(
overlap = []
for cls in classes:
try:
overlap.append(package.classes.by_name(cls.name))
overlap.append(
self.model.search("Class", below=package).by_name(cls.name)
)
logger.info("Class %s already exists.", cls.name)
except KeyError:
package.classes.create(
Expand Down Expand Up @@ -81,7 +83,11 @@ def create_enums(
overlap = []
for enum in enums:
try:
overlap.append(package.datatypes.by_name(enum.name))
overlap.append(
self.model.search("Enumeration", below=package).by_name(
enum.name
)
)
logger.info("Enum %s already exists.", enum.name)
except KeyError:
type = package.datatypes.create(
Expand Down Expand Up @@ -146,9 +152,7 @@ def create_properties(self, cls: ClassDef, package: t.Any):
try:
type_package = self.data.packages.by_name(prop.type_pkg_name)
except KeyError:
type_package = (
package.parent if package is not self.data else package
)
type_package = package

try:
partclass = self.model.search(
Expand Down Expand Up @@ -177,12 +181,17 @@ def create_properties(self, cls: ClassDef, package: t.Any):
continue
except KeyError:
pass

try:
type_package = package.parent.packages.by_name("types")
except KeyError:
type_package = package
try:
property_type = self.model.search(
"Enumeration", below=type_package
).by_name(prop.type_name)
except KeyError:
property_type = self._find_type(prop.type_name, type_package)
property_type = self._find_type(prop.type_name, package)

attribute = self._create_composition(
superclass, prop, property_type
Expand Down
31 changes: 22 additions & 9 deletions capella_ros_tools/modules/messages/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@


VALID_MESSAGE_NAME_PATTERN = "[A-Z][A-Za-z0-9]*"
VALID_CONSTANT_NAME_PATTERN = "[A-Z][A-Z0-9_]*?[A-Z0-9]*"
VALID_CONSTANT_NAME_PATTERN = "[A-Z][A-Z0-9_]*[A-Z0-9]*"
VALID_REF_COMMENT_PATTERN = re.compile(
r"cf\.\s*"
rf"({VALID_MESSAGE_NAME_PATTERN})"
r"(?:,\s*"
rf"({VALID_CONSTANT_NAME_PATTERN}_XXX))?"
rf"({VALID_CONSTANT_NAME_PATTERN}))?"
)


Expand Down Expand Up @@ -214,18 +214,27 @@ def _process_enums(msg):
for enum in msg.enums:
_rename_enum(enum)

to_delete = set()
to_delete = []
for i, enum in enumerate(msg.enums):
# combine enums with the same name
if i in to_delete:
# combine enums with the same name or have just 1 value
if enum in to_delete:
continue

try:
if len(enum.values) == 1:
msg.enums[i + 1].values = enum.values + msg.enums[i + 1].values
to_delete.append(enum)
continue
except IndexError:
pass

indeces = [
i
for i, other_enum in enumerate(msg.enums)
if other_enum.name is enum.name and other_enum is not enum
]
for i in indeces:
to_delete.add(i)
to_delete.append(msg.enums[i])
for value in msg.enums[i].values:
enum.values.append(
ConstantDef(
Expand All @@ -236,8 +245,8 @@ def _process_enums(msg):
)
)

for i in to_delete:
del msg.enums[i]
for enum in to_delete:
msg.enums.remove(enum)

for enum in msg.enums:
match_name = [
Expand Down Expand Up @@ -282,7 +291,11 @@ def _process_comments(instance):
# reference to enum
ref_file_name, ref_common_prefix = match.groups()
instance.type.name = (
_get_enum_identifier(ref_common_prefix[:-4])
_get_enum_identifier(
ref_common_prefix[:-4]
if ref_common_prefix.endswith("_XXX")
else ref_common_prefix
)
if ref_common_prefix
else ref_file_name
)
Expand Down
18 changes: 10 additions & 8 deletions capella_ros_tools/scripts/msg2capella.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,7 @@ def _add_objects(
):
packages = [p.name for p in current_pkg_def.packages]
self.model.create_packages(packages, current_root)
classes = [
ClassDef(c.name, [], "\n".join(c.annotations))
for c in current_pkg_def.messages
if c.fields
]
overlap = self.model.create_classes(classes, current_root)
self._resolve_overlap(overlap, self.model.delete_classes, current_root)
self.model.create_classes(classes, current_root)

enums = [
EnumDef(
e.name,
Expand All @@ -124,6 +117,15 @@ def _add_objects(
self._resolve_overlap(overlap, self.model.delete_enums, current_root)
self.model.create_enums(enums, current_root)

classes = [
ClassDef(c.name, [], "\n".join(c.annotations))
for c in current_pkg_def.messages
if c.fields
]
overlap = self.model.create_classes(classes, current_root)
self._resolve_overlap(overlap, self.model.delete_classes, current_root)
self.model.create_classes(classes, current_root)

for new_pkg_def in current_pkg_def.packages:
new_root = current_root.packages.by_name(new_pkg_def.name)
self._add_objects(new_pkg_def, new_root)
Expand Down
Loading

0 comments on commit f4ee1a9

Please sign in to comment.