본문 바로가기
Angular.js/프로젝트

농장 온도변화 모니터 (plotly.js, RxJS)

by 찬찬2 2024. 5. 20.

캡쳐: 2024.05.20

 

설명:

- 농장에 설치된 약 30개의 센서에서 감지하는 온도수치를 시각적으로 표현.

- 5초 간격으로 서버에서 디바이스에 매핑된 약 30개의 온도에 대한 수치값을 가져와 최저/최고/평균 값을 동별로 보여준다.

- Ploty를 활요해 농장을 3D 모델로 만들고, 실제 센서의 위치를 생성된 모델 안에 위치시킴으로써 최대한 현실과 비슷하게 만들어 사용자의 집중도를 높힘.

- 왼쪽 위에 있는 항목들을 클릭하면 가운데 보이는 3D 농장의 레이어들이 토글되어 보이고 숨겨지고 한다.

 

특징: 

1. blender로 3D 모델을 만든 뒤 gometry 를 뽑아냈다. (blener에서 geometry 뽑아내는 script 보기)

2. 3D 모델(농장 inner/outer, 동/서/남/북, 센서위치)을 구성하는 geometry는 모두 csv 파일로 구성되어있고, HttpClient "get"으로 받아온다.

 

중심점 위치를 기반으로 x, y, z 좌표가 뽑아지기 떄문에 중심점의 위치가 중요하다. (사진에서 보이는 주황색 점)

 


 

특이사항 #1: 차트에서 사용자가 이동/회전/줌인/줌아웃 인터렉션을 하는 중 5초 간격의 인터벌 로직에 의해 차트의 카메라가 원 상태로 복귀된다. 즉, 사용자가 인터렉션 하기 전의 모습으로 되돌아간다.

 

문제점: 센서의 위치에 따른 온도수치를 보기위한 행위를 무용지물로 만들게됨으로써  사용자의 집중도를 저하시킨다.

 

발생 이유: 5초 간격으로 받아오는 데이터를 Plotly에게 넣어주고, Plotly는 새로운 데이터를 받을때 마다 자신의 상태를 원래의 설정상태(layout - scene - camera)로 되돌린다. 차트를 보고있던 시점이 원상태로 돌아간다. (카메라의 좌표값 x, y, z 원 위치) 기껏 줌 인 해서 자세히 보려고 했다가 카메라가 줌 아웃해버리는...

 

해결 방법: 사용자의 인터렉션을 감지하고, 인터렉션이 일어나고 있을 때(마우스 인) 인터벌 로직을 잠시 중지시키고, 인터렉션이 종료되었을때(마우스 아웃) 다시 인터벌 로직을 시작하는 것.

 

<plotly
    (relayout)="onCameraChange($event)"
    (relayouting) ="onCameraChange($event)"
    [useResizeHandler]="true"
    [updateOnDataChange]="true"
    [style]="style"
    [data]="data"
    [layout]="layout"
    [config]="config">
</plotly>

 

Plotly의 relayout, relayouting 이벤트가 사용자의 인터렉션을 감지한다.

 

  onCameraChange(event?: any){
    this.pauseIntervals$.next(true);
    this.cameraState$.next(true);
  }

 

onCameraChange 함수로 사용자의 인터렉션이 일어날때(relayout, relayouting의 호출) 마다 pauseIntervals$에 값을 발행해 takeUntil 연산자에 의해 인터벌 옵저버블을 complete 시켜 인터벌을 멈춘다.

 

  ngAfterViewInit(){
    // subscribe camera
    this.cameraSubscription = this.cameraState$.pipe(
      debounceTime(1000)
    ).subscribe(_ => {
      // cameraState에 값이 전달될때 마다 기존에 있던 dataSourceSubscription에 대한 구독을
      // 취소하고, 다시 구독한다.
      this.dataSourceSubscription = this.dataSourceTimer$.subscribe();
    });
    
    // subscribe device data
    this.dataSourceTimer$ = this.dataSource$.pipe(
      filter(data => data.size > 0),
      tap(devices => {
        this.updateMeshIntensity(devices);
        this.updateScatterData(devices);
        this.updateCurrentSeq(devices);
        this.updateTempHum();
      }),
      takeUntil(this.pauseIntervals$) // 구독을 완료상태(complete)로 만들어 더이상 위에 있는 update 함수들이 실행되지 않는다.
    );
    this.dataSourceSubscription = this.dataSourceTimer$.subscribe(); // 컴포넌트가 마운트된 시점에 인터벌 로직이 실행되어야 한다.(1회성)
  }
  
  ngOnChanges(changes: SimpleChanges){
    if('dataSource' in changes && changes['dataSource'].currentValue){
      dataSource = changes['dataSource'].currentValue;
      this.dataSource$.next(dataSource); // timer(0, 5000)... 5초간격으로 온도값을 받아 전달한다.
    };
  }

 

 

제어하고자 하는 인터렉션이 마우스 이벤트이므로, 마우스의 좌표이동에 따라 콜백함수가 엄청나게 불려지게된다.

성능차원에서 deboundTime으로, 사용자의 인터렉션이 끝난 시점에 onCameraChange가 실행되도록 했다.

 

takeUntil 연산자로 업데이트 로직들이 모여있는 dataSource$ 옵저버블을 구독완료상태로 만들어 중지시켜준다.

 

요청사항에 "농장처럼 보였으면 좋겠다" 때문에 3D 모델에 대해 알아보았고, 이를 Plotly에서 구현하기 위해 blender를 사용해보았다. 다행히 복작합 3D 모델이 아니라서 다행이었다.

프로젝트를 하면서 3D에 대해 관심이 생겼고, 지금은 three.js 를 공부해보고 있다. three.js로 간단한 웹페이지를 만들 계획이다.

'Angular.js > 프로젝트' 카테고리의 다른 글

Angular project, Plotly.js & WebGL is not supported issue  (0) 2024.05.17

댓글