How I controlled SVG elements in Thingworx
Thingworx에서는 Media Entity를 통해 PNG, JPG 등의 이미지를 업로드 하고, Mashup상에서 배치하여 공장 도면 등을 표현할 수 있다. 그리고 State Definition을 통해 Shape등과 연계하여 상태에 기반한 색상/이미지 제어를 할 수 있다.
하지만 Mashup상에서 일일히 바인딩하고 Media Entity들을 수작업해서 배치해야하는 수고로움이 있다.
SVG 이미지 컨트롤의 필요성
SVG 이미지는 고화질 벡터 이미지 이며 Thingworx Mashup에 삽입 했을 때, 특정 Element를 선택해서 제어할 수만 있다면 개별적인 CSS를 적용하여 표현의 자유도가 높아질 수 있다.
물론, State Definition등의 Thingworx Platform에서 제공하는 기능들을 건너뛰는 제어가 되기 때문에 남발하면 유지보수 측면에서 좋지는 않음을 유의해야한다.
다만 예상과는 다르게 Thingworx에서의 의도적인 조치인지는 모르겠지만 SVG 이미지를 Media Entity로 업로드가 불가능하고, Media Entity의 Configuration에서 URL을 주는 방식으로 삽입할 수는 있지만, IMG 태그로 url을 통해 통째로 들어가기 때문에 세부 컨트롤이 불가능하다.
하지만 내 경우에는 독특한 요구사항을 달성하기 위해서는 Elemenet 컨트롤을 기술검토할 수 밖에 없었는데 그 요구사항이란,
- 공장 도면에 로봇 Thing들을 표시하는데
- 가능하면 Thingworx의 Composer를 접속하지 않는 방식으로
- 로봇 Thing의 상태를 색상으로 구분하여 표시하고(가동/비가동)
- 해당 Thing에 해당하는 부분에 마우스를 오버하면 로봇 Thing의 이름이 툴팁으로 표시되었어야 했다.
SVG Element 컨트롤이 가능하다면,
공장 도면을 SVG로 만들고 로봇 Thing의 이름을 id 등으로 부여하는 작업만 한다면 요구사항을 달성할 수 있지 않을까 싶었다.
설계 수정 시에도 SVG를 별도로 생성할 필요 없이, 설계 파일에서는 보통 SVG Export기능을 지원하니까 id만 잘 설정해주면 수정된 형상대로 Thingworx의 Composer상에서의 별다른 변경 없이 적용이 가능할 것이라 생각해서 Thingworx에서 SVG 이미지를 컨트롤 하기 위한 방법을 찾기 시작했다.
Thingworx에서 SVG 이미지가 Element로 제어되지 않는 이유
SVG 이미지를 Media Entity로 업로드 할 수 없기 때문에, Thingworx에서는 화면에 SVG 이미지를 올리기 위해서는 아래의 순서를 따라야 한다.
해당 내용은 PTC Article: Is it possible to use SVG images in ThingWorx mashups에서도 확인할 수 있다.
- SVG 이미지를
<tomcat_home>\webapps\Thingworx\Runtime\images에 위치시킨다. - Media Entity를 생성하고, Configuration의 URL에 다음 규칙으로 URL을 준다.
http://host:port/Thingworx/Runtime/images/{file_name}.svg - Configuration에서 Dynamic Content 옵션을 활성화 한다.
- Media 속성에 해당 Entity를 할당한다.
그치만 해당 방식으로는 SVG 타입의 이미지가 업로드 된 Media 위젯은 IMG 태그로 들어가 SVG 내부 Element 제어가 불가능하다.
Thingworx에서 SVG Element로 제어하려면
결론부터 얘기하자면, SVG 이미지를 SVG 태그로 포함한 JSP 페이지를 만들어서 tomcat 하위에 배치시키고, Mashup 상에서는 iframe 방식으로 호출하면 제어가 가능하긴 하다.
이렇게 어거지로 작업하면 사실 독특한 요구사항을 전부 충족시킬 수 있기는한데, 유지보수 측면에서는 좋은 방법은 아닐 것 같아 고민은 된다.
하지만 리스크를 가져갈지 말지에 대한 선택은 고객의 몫으로 남겨두어야 하기에, 일단은 가능한 옵션은 모두 검토해 보는 편이다.
각 로봇들의 id는 robot_1, robot_2 … 이런 규칙으로 지정되어있다고 가정했고, 각 로봇들에 마우스오버 하면 로봇 ID 툴팁이 표시된다.
jsp 예제
아래 소스는, Tomcat의 Thingworx 폴더 하위에 svg 폴더를 따로 생성하고, testFactory.svg라는 이미지를 로드하는 jsp 예제이다.
<div id="svgContainer"></div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
<div id="svgContainer"></div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(document).ready(function () {
$.get("http://int.innofactory.net:36988/Thingworx/svg/testFactory.svg", function (data) {
let svg = $(data).find("svg"); // <svg> 요소만 추출
svg.attr("width", "100%"); // 너비 100% 설정
$("#svgContainer").append(svg); // DOM에 추가
}, "xml");
// SVG 요소 동적 로드 후에도 이벤트가 적용되도록 document 레벨에서 바인딩
$(document).on("mouseover", "[id*='robot']", function (event) {
$("text#robot_tooltip").remove(); // 기존 툴팁 제거
let svgElem = $(this).closest("svg")[0]; // SVG 요소 찾기
if (!svgElem) return;
let pt = svgElem.createSVGPoint(); // SVG 좌표 변환용 객체 생성
pt.x = event.clientX;
pt.y = event.clientY;
let matrix = svgElem.getScreenCTM();
if (!matrix) return; // getScreenCTM()이 null일 경우 대비
let cursorPoint = pt.matrixTransform(matrix.inverse()); // 화면 좌표 → SVG 좌표 변환
let tooltip = document.createElementNS("http://www.w3.org/2000/svg", "text");
$(tooltip)
.attr({
id: "robot_tooltip",
x: cursorPoint.x + 10, // 마우스 포인터 기준으로 약간 오른쪽
y: cursorPoint.y - 10, // 마우스 포인터 기준으로 약간 위쪽
"font-size": "12",
fill: "white",
stroke: "black",
"stroke-width": "0.5",
})
.text($(this).attr("id")); // 현재 요소의 ID 표시
$(this).append(tooltip);
});
// 마우스 아웃 시 툴팁 삭제
$(document).on("mouseout", "[id*='robot']", function () {
$("text#robot_tooltip").remove();
});
});
</script>testFactory.svg 예제
Mashup 상에서 해당 jsp 예제를 iframe으로 로드하여 의도한 대로 동작하는 것을 확인했다. 기술 검토용 예제이니 감안해서 보길 바란다.
다른 옵션(SvgViewerWidgetTWX)
기술 검토 당시에는 확인하지 못했던 옵션이지만, 해당 포스팅을 작성하기 위해 서칭하다 보니 유용한 Extension이 오픈소스로 올라와 있는 것을 확인했다.
SvgViewerWidgetTWX 라는 Extension인데, PTC Field Team에서 만들었다고 한다.
내가 직접 테스트 해보지는 못했지만 SVG용 SvgFileRepository에 SVG 파일을 업로드 해서 불러오는 방식이고, Extension 설치 시 SvgEnabledThingShape가 추가되어 해당 ThingShape를 Implement한 Thing을 생성하고, GetSvgOverrides를 오버라이드 하여 구현하는 방식으로 보인다.
Element 색상, 텍스트 변경, 툴팁에 관한 옵션도 있어보인다.
오피셜한 Extension은 아니라서 지원이나 보증은 못한다고는 하지만, 요구사항들이 Extension Import로 해결할 수 있는 문제라면 깔끔한 모양새의 해결책 일 것 같고, 꽤나 유용해 보인다.
한번 살펴보는 것을 추천한다.
마치며
안타깝게도 기술 검토를 통해 SVG Element를 컨트롤 할 수 있는 방법을 찾았지만, 내부 사정으로 해당 옵션은 채택되지 못했다.
기술 검토 기간에 SvgViewerWidgetTWX Extension을 함께 확인해보지 못해 아쉬움은 있지만, 다음에도 유사한 요구사항이 있거나 의사 결정 시에는 해당 Extension 또한 테스트 해보고 좋은 옵션으로 제공할 수 있을 것 같다.