퇴근5분전

 늦은(?) 나이에 분가를 하게 되었다. 

 

프로젝트 끝나고 쉬는 중에 부모님의 분가요청으로... 나오게 되었다. 

 

그나마 본가에서 가까운 곳 오피스텔을 매매해서 자리를 잡았다. 

 

잔금 마치고 10일정도는 짐 실어나르면서 청소를 하였다. 

 

나름 짐 없이 산다했는데도 많이 들어 날렀다. 

 

벽한쪽에 TV놓는 자리처럼 휑한곳이 있길래... 컴퓨터를 놓기위해 

'고릴라랙'을 짜서 올려놓고 모니터 암을 달고 컴퓨터를 모니터 뒤에 두었다. 

보조 책상(ONA-804)으로 키보드 트레이( APL-KT65 )를 부착했다. 

 

# 컴퓨터 

 

 동향에 가까운 남동향이라 해가 아침부터 밝게 들어오고, 앞이 탁 트여서 답답하지도 않다. 

 

역앞이라 밤에 빛이 많이 들어와서 암막커튼도 했다.

 

층간소음은 그럭저럭... 조용한편인데, 발소리가 쿵쾅쿵쾅 들릴때가 있는데... 그래도 일찍 자는지 밤엔 조용하다. 

 

 

이제 혼자 생활하면서 어떻게 지내야 하는지 하나 하나 체크해보고 있는데... 

1. 식사시간? 밥을 차려서 먹고 치우고 하는데 대략 30~40분 가량 소요가 된다. 

   그래서 아침을 어떻게 먹을지?... 여러가지 시도중...

2. 세번정도 세탁기 돌려봤는데... 세탁기가 작아서 자주 돌려야 할 듯...

3. 음식쓰레기?... 

    아예 많이 나오면 자주 버리면 될 듯 하지만, 과일깍아먹으니 버리기도 애매하고 봉다리에 넣어놓았더니 곰팡이 피면서 썩더라... 3l짜리도 다 안찬거 버릴수밖에... 

4. 혼자 있는 시간에 익숙해져야 할듯 하다. 

   TV를 보는게 아니고... 본가에서는 귀로 듣기만 했는데... 너무 조용하니 이상하긴 하다.

 막 끝난 프로젝트에서 {설비 ---- mes서버}간에 데이타 통신을 ActiveMQ라는 것으로 구현이 되어 있었다.

메세지 전문을 만들면서 '보내는건 되어도 결과를 받을 순 없다.' 라는 답변을 받았다.

그런게 어딧냐고... 기간계 통신이 일방적으로 주기만 하는게 어디있느냐... 주고 받고 해야지... 라며 얘기를 했었다.

 

 프로젝트 끝나고 집에서 ActiveMQ관련해서 찾아보다 보니 RabbitMQ나 Kafka 같은 것들까지 나오길래...

구글링을 하면서 이게 뭔가 찾아봤더니 MSMQ같은 큐였다. 

 내 기억엔 MSMQ에서 양방향으로 비동기 통신을 했었던 걸로 기억하는데? 

이런것들은 한쪽에서 보내는 것만 된다고?

 

 몇 일을 고민도해보고 찾아보고 샘플도 만들어보고 테스트도 해보니... 내가 알던 방식이 맞다.

큐라는걸 사용할 뿐이지.. 주고 받고 해야지... 

기본 메세지 전송은 [프로듀서]라는게 [큐]에 등록(Send)하면 [컨슈머]가 꺼내서(Listner) 처리를 한다.

 

 Q. 서버.컨슈머가 메세지 처리 후 결과를 클라이언트에 돌려주는 방법이 있나?

 전 프로젝트 소스에서도 클라이언트.프로듀서가 전송 후 결과를 받는 것으로 처리 되어 있는 걸로 봐서는... 있지만?

서버쪽 소스는 내가 볼수 없었기에... 추측만 해볼 수 밖에...

 계기 ::

  설비에서 부자재 사용을 알릴때 서버에 요청하면 사용승인난 대기하던 부자재를 사용처리하면서 

 등록 ID를 설비에 돌려줘야 하는데... 이게 안되는게 아니고 안하는거였다? 왜? 귀찮아서 였을까??

 

간단하게 두가지 방법이 있었다. 

첫번째는 임시 큐를 사용하는 방법.

클라이언트.컨슈머 생성시 tempQueueDestination = session.CreateTemporaryQueue() 로 임시큐 사용

요청메세지에 NMSReplyTo = tempQueueDestination 을 할당하여 .Send

서버.컨슈머가 메세지를 수신하고 처리시 수신메세지.NMSReplyTo를 이용해서 서버.Producer를 만들고 Send하면

클라이언트.컨슈머에서 Receive 할 수 있다. - 테스트 결과 잘 된다.

mporaryQueue()

 

두번째 방법은 응답 컨슈머 생성시 selector를 지정

1. 클라이언트.컨슈머 생성시 selector를 res=producerID를 지정

2. 클라이언트.프로듀서의 요청메세지.Properties["req"] = null; 을 지정

3. 클라이언트.프로듀서의 요청메세지.Properties["pid"] = producerID; 을 지정

4. Send

 

1. 서버.컨슈머 생성시 selector를 req is null을 지정한다. 

2. 서버.컨슈머의 수신된 메세지 처리시

   서버.프로듀서의 응답메세지.Properties["res"] = 수신된메세지. Properties["pid"] 지정

3. Send

※ res, req를 등록하는 이유는 하나의 큐 JSFW.Queue에 등록하게되므로

자기가 올린 메세지를 자기가 수신하지 않도록 구분해주는 역할로 selector를 지정한다.

 

 클라이언트 프로그램을 여러개 띄워서 동시간에 동시 요청 가능하도록 만들고 테스트해보니 

원하던 동작을 잘 했다. ( 아래는 두개만 찍었지만.. 네개정도 띄우고 10초단위로 확인 해봤다. )

 

결론 : 잘 된다. 

 

예외 : 내 샘플 코드가 문제인지? 

서버.Consumer 프로그램을 수신을 멈춰놓고 긴시간 대기하면

클라이언트.프로듀서 프로그램은 10초마다 등록을 한다. 동기로 구현해놔서 Receive가 안되어 지정시간 동안 대기...

나중에 서버.Consumer 프로그램에서 다시 수신시작을 했을때 한번에 밀려들어오는데... 

 

클라이언트.프로듀서 프로그램에서 Thread로 10초마다 Send걸어놔서 그런지.. 

껐다 켰다 하면 같은 데이타로 Send가 여러번 일어나는 현상이??? 이건 샘플이 잘못된걸꺼야... _)_;;; 

1. ActiveMQ 

          -프로듀서:컨슈머=1:1, 1:n 

          -토픽, 큐

 

2. MSMQ (netMSMQBinding : two-way)

 

4. SQL MQ? 이런게 있어?

 

3. 코파일럿 AI로 ppt 생성해보기 

          -이전 프로젝트에서 만들었던 PPT내용을 ai로 재현이 되는지... 확인해보기.

         :: 맨날 말로만 된다고 하는 건 봤는데... 진짜 필요한 화면은 어떻게 만들수 있나?

 

5. 골프?...

 

--- 모르던게 생겼으니... 또 공부를 해야 다음 일도 하는거고... 

          

  DAT 프로그래밍을 하면서... 

챠트도 그리고, 각종 로그도 남기면서... 쓰레드 현재 실시간으로 값을 보는 컨트롤도 만들고...

또 프로그램 원격 재시작, 모니터링등... PLC 신호에 맞춰 대응하는 프로그래밍이 재미있긴 했다.

다만 그 신호가 원치 않을때도 왔었다는게....

 

 암튼 플젝하면서 일정관리, 소스정리, PLC연동기획서 현행화, 이슈관리 등... 

좀 할게 있었지만... 초반엔 현장 대응하느라 시간이 안나서... 못했던걸 마지막 달에 주말엔 집에서 문서정리하면서 보냈다. 

 

 연동기획서를 재정리 할때는 PPT 헬퍼가 Office2021에서 동작을 이상하게 하는 바람에... 제대로 못 써먹었다. 

 

이번에 office 2007버전과 office2021버전에서 모두 사용이 가능하도록 수정하였다. 

 

일정관리 툴은 Todo를 써야 하는데... 무슨 이유였을까? 소스의 MainForm.cs 제일 위 상단에 회의록 겸, 일정을 적으면서 사용했다. git에 올라가니 기록은 유지되었을 것이다. 

 

Visual Studio Extention 프로그램에 함수목록 보기 프로그램을 수정하여 사용했다. 

함수명만 보이니까 함수의 역할이나 기능에 대한 코멘트를 함수목록에서 보고 싶어서.... 

 음... 좀 아쉬운건 회의록 정리하는게... 부족했다?

아침 10시 반이면 회의를 1시간 반이나 하는데... 딱히 이렇다할 기록이 없으니. 

회의록 정리하는 걸 고민해봐야겠다. 

 

이후 후속 DAT개발자 분이 드디어 출근을 했다하니... 잊어버려도 될 듯....

 

 

  와우.. 드디어 지난 금요일에 프로젝트 계약기간이 끝나고 탈출?했다. 

계약기간은 3개월! 연장은 안했다. 1달짜리 단가가 그대로라.. 굳이 할 이유가 없더라.

업무는 제조 생산 설비에서 PLC 신호에 대응하여 데이타 생성 및 수집이었다. 

 

 5월에 이미 오픈한 프로젝트였지만, 6월 초에 들어갔더니 퍼질러 놓은 똥이 너무도 커서... 

소스를 싹 지우듯이 하면서 다시 코드를 작성했다. 가장 빠르게 해야 하는 것이 "발행", "인쇄", "배출" 신호를 잡아서 

처리해야 했다. 두달은 밤 늦게까지 또 주말에도 나가서 일하며, 1년 중 공장이 가장 바쁘게 돌아가는 기간에 

프로그램이 동작하도록 만들었고. 남은 한달은 남은 과제 23가지들을 처리를 했다. 

 설비는 88대로... 일일이 껐다 켰다 하면서 돌아 다녀야 하고... 두달 미친듯이 다니니 다리가 망가졌다.

집에 도착하면 어그적 어그적 걸어다닐 정도로... 발바닥과 왼쪽 무릎이 아프다.

 그래도 세달을 채워야 하니 그냥 최대한 설비를 안돌아다니면서 일을 했다.

그래도 배포를 하려면 설비를 끄고 켜고 하러 다녀야 했고...

한번 하면 두세번은 한듯 하다. 돌아오지 않은 내 다리.. ㅠㅠ

 

 좀 쉬면서 나를 좀 돌보고... 해야 하는데... 하~! 집은 또 집대로... 편하지 않네. 

 

 간만에 이 프로그램을 추가 수정...

SI플젝하면서 메소드에 옆에 설명문구를 넣고 싶은 생각이 들었는데... 

미루다가 이제 넣었다. 

 

 

색상 지정은 그대로고

F2 키를 누르면 힌트입력 컨트롤이 앞으로 나온다. 

위와 같이 

일자 및 시간을  보여주는 컨트롤을 만들었다. 

설비에서 데이타 입력하는 터치에서 화상키보드 열어서 년 월일을 입력하고 있는 걸 보고... 

와우... 이런 컨트롤이 있으면 그냥 넣을 수 있을 듯 해서... 잠깐 만들어봤다. 

UP, DN 버튼은 눌러도 되고, 화면을 밀어서 올리고 내리고...

 

설정할 수 있는 속성은 위와 같다. 

 

표시 갯수설정.

값이 계속 이어서 돌려서 보여줄지 말지

몇개씩 건너뛸지...

현재 선택된 값...

값 표시 포맷...

박스세로 간격...

 

컨트롤을 만들면서 직접 그려야 하는 부분이 있는데

영역내에 그려야 하는 박스 갯수에 따라 중심에서 그려져야 할때의 계산... 

 

5개 짜리
1개짜리
4개짜리

top 0 

중심 250 box H=100 

높이 500
//중심점을 기준으로 display되는 갯수로 높이값을 빼주면 시작지점이 된다. 
//중심이 절반이니까 = display / 2

1개면  250 - ( 100 * 1/2 ) = 250 -  50 box 1개 : 200-300
2개면  250 - ( 100 * 2/2 ) = 250 - 100 box 2개 : 150-250, 250-350
3개면  250 - ( 100 * 3/2 ) = 250 - 150 box 3개 : 100-200, 200-300, 300-400
...

간격값을 주면? 10씩 넣으면
offset = 10 // 중간에 display -1 개씩 끼워넣어지므로...

1개면  250 - ( 100 * 1/2 ) - (10 * (1-1/2)) = 250 -  50 -  0 box 1개 : 200-300
2개면  250 - ( 100 * 2/2 ) - (10 * (2-1/2)) = 250 - 100 -  5 box 2개 : 145-245,+10, 255-355
3개면  250 - ( 100 * 3/2 ) - (10 * (3-1/2)) = 250 - 150 - 10 box 3개 : 90-190, +10, 200-300, +10, 310-410

 

구현..

 


        int offset = 15;

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
 
    // 시작 지점 = 박스 높이 *  디스플레이 카운트 / 2 - (간격 * (디스플레이 카운트 - 1) /2 );
    int top = this.Height/2 - (BoxHeight * DisplayCount / 2 ) - (offset * (DisplayCount-1) / 2);
    e.Graphics.DrawLine(Pens.Black, 0, this.Height / 2f, this.Width, this.Height / 2f);
    for (int loop = 0; loop < DisplayCount; loop++)
    {
        e.Graphics.DrawRectangle(Pens.Red, 10, top, BoxWidth, BoxHeight);
        top += BoxHeight + offset; 
    }
}

  설비가 90여대가 가까운데 프로그램 업데이트가 자동이 아니다. 

할때마다 배포서버에 배포 후 프로그램을 각 설비마다 가서 껐다 켜줘야 한다. 

이때 구형pc는 켜지는게 느리다. 그래서 프로그램이 꺼지면 설비가 일정시간 동안 신호를 받지 못하면 멈춘다. 

 

 그래서... 지금까지는 시간에 쫒기다보니... 재시작 프로그램을 만들 시간을 낼 수가 없어서 몸으로 때웠다.

이제 재시작을 만들었다. 

 추후엔 업데이트할 내역이 있으면 자동 재시작도 가능할거라 생각한다. 

샘플프로그램이다. ( 설비에서 제대로 동작하는 것을 확인 하였다. )

 

1. 버튼을 누르면... 해당 프로그램은 죽으면서 cmd로 Restart 프로그램을 띄운다. 

2. 파라미터로 원래 프로그램의 프로세스명과 재시작해줄 프로그램 실행경로를 넘겨준다. 

3. 재시작 프로그램이 떠서 원 프로그램이 죽는지 체크하고... 죽고 나면 다시 실행시켜준다. 

 -- 이때 설비에 시그널을 던져주기 위해... 별도의 쓰레드를 잠깐 돌려준다. 

+++++++++++++++++++++++++++++++++

추가. 

재시작 프로그램을 도입했는데... 시행착오? 같은게 있었다. 

재시작 할 프로그램도 업데이트 항목에 넣으면... 재시작되는 도중에 업데이트가 안된다. 

 

 업데이트 파일은 ReStart00.exe로 00을 붙여서 배포하고

재시작 하는 이벤트에서 ReStart00.exe를 복사해서 ReStart.exe로 덮어쓰기.

그 후 ReStart.exe를 실행하도록 바꾸었다. 

+++++++++++++++++++++++++++++++++

하나더...

설비별로 별도의 테스트가 필요한 경우가 있어서... 

 

재시작 프로그램이 실행중에 'TestFile' 이라는 폴더에 테스트용 실행파일이 있다면 

파일이 존재하면 옮겨서 붙여넣기 해준다.

+++++++++++++++++++++++++++++++++

또 하나...

이래 저래 시행착오를 거치다가...

ReStart 바로가기 만드는 콘솔프로그램을 만들었다. 

 

 이유는 재시작 프로그램배포와 함께 맞물려서 깔끔하게 안돌아간다... 88대를 배포하다보니...

전체 배포를 했음에도... 신뢰가 안되네... 멈출까봐... 불안한 느낌?

 처음 배포 후 재시작눌렀을때 일부 pc에서 재시작 안하고 콘솔이 종료되는 현상이 있었더랬다... 

수정 후... 잘 되긴 하는데... 구형PC는 좀 불안하긴 함... 

배포할때 ReStart.exe를 붙여넣고 바로가기 생성프로그램을 돌려서

이걸 실행해놓은 상태에서 프로그램을 끈다. 

그러면 정해진 프로세스대로 동작하면서 업데이트 된다. 

 전체 배포가 잘 진행된다 싶을때까지... 구형PC들은 이걸로 사용할 예정이다. 

****************************************************

마지막으로.. 

오늘 모두 배포 완료했다. 시작하자마자 콘솔이 꺼지는 현상도 잡혔고... 

업데이트 중 설비가 멈추지는 않을 듯 하다. 

구형설비의 프로그램이 죽고 되살아 나는데 시간이 참 오래 걸리네... 

 

 이번 플젝에서 설비88대에 대한 모니터링을 만들예정이다. 

딱히 이건 요구사항 정의도 없는 거고... 그냥 설비마다 돌아다니면서 보는게 구찮아.

한달되었는데... 하루 쉬었다. 게다가 새벽에 퇴근도 몇번하고... 차 끊겨서 태워다 주기도 하고...

 

 TCP/IP통신을 할까? gRPC를 할까? WCF로 할까? ... 다 알아봤는데...

다 구찮아.. gRPC는 .net framework로도 안된다.. 4.8에서 된다고는 하는데... 뭐가 복잡한건지... 지원이 된다 안된다... 

인증서 만들어야 한다거나... 포트열어줘야 한다거나... 등등 귀찮아.... 

 

그냥 DB에 넣고 빼자. 

 

그래서 만든 거다. 

큰 프로그램은 88대의 설비를 보여줄 화면이고 1개만 우선 테스트 중이다. 

작은 화면은 설비에서 보낼 설비 정보를 테스트용으로 만든 프로그램이다. 

DB를 통해 전달하고 DB를 읽어서 보여준다. 

 

10분 이상이면

 

장비가 가동 중일때 신호를 받아서 찍어준 것.

 

 이번 플젝에서 사용한 기술이지만... 간략히 메모하자면... 

챠트를 직접 쓰자니... 저거 그리자고 챠트를? 

저런건 간단히 그리자!!!

 /~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/  3일치 데이타

 /~~~~~~~~~~~~~~~/ 그려질 컨트롤 가로폭

두개의 데이타로  그려질 컨트롤의 1px에 3일치 데이타의 갯수를 구한다. 

1px에 데이타갯수 =  3일치 데이타 갯수 / 그려질 컨트롤 가로폭

for( loop < 그려질 컨트롤 가로폭 )

    for( loop2 < 1px에 데이타갯수 )

            idx = ( loop * 1px에 그려질 데이타 갯수 ) + loop2

            이렇게 하면 3일치 데이타[ idx ]로 접근이 가능.

            1px에 그려질 데이타의 존재여부에 따라 있으면 loop2 의 for문 탈출....

 

위처럼 하면 1px에 그려질 데이타가 모두 보여지는 것은 아니나... 존재여부에 따라 신호를 그려준다. 

손실률이 있지만... 지금 현재 보고 싶은 그래프 그리는 것엔 충분하다. 

  

 

이렇게 3일치의 데이타를 챠트로 발행/인쇄/배출 신호를 잡아서 뿌려주면 

설비에서 어느 시점에 어떤 신호들이 안왔는지 알 수 있다.

저기에 해당하는 지점의 로그를 살펴서 어떤 이유인지 살펴보게 된다.