Visual Studio 2017 Community의 프롬포트 도구모음 스크립트 버그

현재 2018년 4월 19일 겪어서 하루 삽질한 내용.

부스트 라이브러리 경로를 INCLUDE, LIB 환경변수에 설정하고 Qt Creator에서 아무리 들고볶아도(최신버전을 받아서 업데이트 포함) LIB변수는 변하는데 INCLUDE 변수는 전혀 변하지않고 환경변수 값이 반영 안되는 것이였다.

구글링도해보고 뭘 해봐도 다른건 되는데(CL, _CL_변수) 이게 안되어서 IDE에서 헤더를 인식 못하였다. 그렇게 오늘 수 시간을 삽질한 결과 답을 얻었다.

일단 문제의 스크립트 내용은 아래와같다.

1분만에 눈에 들어오는것이 있다면 매의눈이라고 할 수 있겠다. __tmpwinsdk_include 환경변수를 초기화하고 INCLUDE 변수 내용을 담고 그것을 다시 환경변수에 설정하고 __tmpwinsdk_include 변수를 비우는데… 문제는 INCLUDE 환경변수를 수정하는 라인에 있었다. 그쪽만 혼자 __tmp_include변수에 할당하고 그 변수는 그냥 쓰레기가 되어버렸다.

이건 스크립트 흐름으로 보아 분명 버그임에 틀림없으니… 일단 내가 바꿔서 사용해야겠다. -_-;; 이것때문에 오전부터 지금까지 네시간 넘게 내가 왜 삽질을 해야했을까… 암튼 이 포스팅은 이것으로 끝!

아래 스크린샷은 수정한 후의 “C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\vsdevcmd\core\winsdk.bat” 파일의 내용이다.

eclipse에서 spring, maven 사용시 ClassNotFoundException 및 LOC Header 등 에러 발생시 확인해볼 사항

오랜만에 스프링 사용해볼겸 boot를 써보았다.

이전엔 maven – update project를 해보면 Invalid LOC Header (bad signature) 문제가 발생하였다며 파일이름이 나타났기때문에 바로 찾아서 지운 후 update project를 하면 되었는데…
이번엔 war이나 jar파일로 패키징까지 다 되었지만 함께 패키징 된 jar 파일이 문제있어 예외가 발생하였다.
디버그 모드로 실행하면 예외가 발생하고 해당 예외의 객체 이름부분을 콘솔 출력에서 클릭하면 Break Point를 활성화 할 수 있어 ZipException을 등록해두었더니 아래와같이 스택에서 파일명을 확인할 수 있었다.

그리고 콘솔에서 해당 경로를 보면 항상 보이는 파일이 있다.

바로 sha1-in-progress 확장자를 가진 파일이다. jar파일을 지운 후 update project를하여 jar 파일이 다 받아지면 sha1-in-progress파일이 사라진다.

이런점을 활용하면 탐색기를 띄운 후 경로표시줄에 %userprofile%\.m2 를 넣은 다음 검색에 sha1-in-progress를 입력하면 아래와같이 죽죽 뜬다. (많이도 실패했네 -_-)

콘솔이 편하면 콘솔에서, 탐색기가 편하면 탐색기에서 해당 경로에서 jar 파일을 지운 후 update project를 한번 해주자.

jar 파일을 지우고 update project를 해서 jar 파일이 완전하게 받아져도 sha1-in-progress파일이 남는경우가있다. (아마 남는경우가 더 많은거같다.) 그냥 폴더를 지우는것도 깔끔한 방법이다.

P.S.
하… 전혀 힌트도 얻지 못할 콘솔 메시지… 다운로드가 도중에 끊기는건 대체 어느시대에 일어나는 일이란 말인가… 덕분에 하루 반나절을 구글링으로 -_-;;;
수년도 더 지난 이런 문제 이제 좀 알아서 sha1 해쉬를해서 안맞는 파일은 다운받는 메뉴 좀 넣어주지. -_-;;

libmodbus 3.1.4 분석내용

보통의 plc 기반 modbus rtu는 2 wire RS-485 방식을 사용하는것으로 보인다.

RS-485가 병렬연결 방식이라 모든 장치에 메시지를 전송할 수 있다는것이 장점인데 modbus 프로토콜은 명확하게 구분되는 STX와 ETX가 없다. 이렇다보니 master가되는 서버가 1번 아이디의 장치로 데이터를 요청하여 1번 아이디의 장치가 응답을 하였을경우 2번 아이디의 장치의 시리얼에 수신되는 데이터는 1번 아이디의 장치 요청데이터, 1번 아이디 장치의 응답데이터이다.

즉, 1번 장치에 요청되고 응답 되었을경우 아래처럼 2번장치에 수신된다. (read holding registers 기준)
[SLAVE ID][FUNCTION CODE][START][QUANTITY][CHECK SUM][SLAVE ID][FUNCTION CODE][BYTES][UINT16 DATA 1][UINT16 DATA 2]…[UINT16 DATA n][CHECK SUM]

STX와 ETX가 없다보니 2번 장치에서 자신의 요청이 아닌 1번 응답을 무시하더라도 바로 뒤에 붙어오는 데이터를 읽게되면 ‘1번 장치의 holding register의 데이터 x주소에서부터 y만큼의 데이터 요청’으로 해석되어진다. 하지만 응답되는 데이터의 길이가 더 크기때문에 이후에 또 읽게되면 UINT16 DATA 부분이 읽혀지게되고 이후 파싱이 안된다.

libmodbus는 rtu일경우 기본적으로 요청 -> 응답 순으로 데이터가 수신되는 조건으로 만들어져있다. 즉, 처음 modbus_receive(3)를 호출하면 데이터 요청, 그다음 modbus_receive(3)을 호출하면 데이터 응답인것으로 수신한다. 따라서 처음 modbus_receive(3) 호출 시 [SLAVE ID][FUNCTION CODE][START][QUANTITY][CHECK SUM]으로 데이터를 읽고 자신에 대한 요청이 아닐경우 데이터 응답을 읽도록 상태가 변경되어 다음 modbus_receive(3) 호출 시 응답 데이터인 [SLAVE ID][FUNCTION CODE][BYTES][UINT16 DATA 1][UINT16 DATA 2]…[UINT16 DATA n][CHECK SUM] 형식으로 읽게되어 완전하게 읽은 후 데이터 요청을 읽도록 상태를 변경한 후 읽은 데이터는 버리도록 한다.

modbus_receive(3)을 호출하면 처음 [SLAVE ID][FUNCTION CODE]를 읽기까지 receive timeout값(기본 0.5초)만큼 버퍼의 데이터를 대기하고 이후 나머지 데이터는 byte timeout값(기본 0.5초)만큼 버퍼의 데이터를 대기한다. 만약 receive timeout값을 5초로 주어지고 slave id가 2번으로 설정되어있을 때 1번 장치에 대한 요청이 modbus_receive(3) 호출하여 읽혀지면 응답 데이터를 읽도록 상태가 변경되어있기때문에 5초 이내 master에서 2번, 또는 다른 장치에 대한 데이터 요청이 발생하면 데이터 응답이 수신되지 않기때문에 에러 즉, -1이 반환된다. (이후 다시 modbus_receive(3)을 호출하면 정상으로 다시 데이터 요청을 읽을지 테스트 하지는 않았다.)

이런 상태를 보면 modbus rtu는 master와 slave간의 요청 응답의 타임아웃을 서로 여유를 두고 잘 정해야 할것으로 보인다.

예전 클라이언트를 만들었을 때 로직은 시리얼 데이터를 읽는 스레드 내 데이터가 수신되면 마지막으로 버퍼에 데이터가 읽힌 시간을 확인하여 0.2초(예시값)이상 지났을경우 버퍼를 비우고 수신된 데이터를 버퍼에 쌓은 후 파싱하고 자신에 대한 요청이 아니면 무시, 자신에 대한 요청이면 응답하는 단순한 구조였다. 첫 바이트가 자신의 아이디와 동일하고 Function Code가 유효하더라도 Check Sum 값이 동일할 경우는 희박하고 데이터 요청 개수(quantity)의 최소, 최대가 이미 정해져있으므로 문제가 발생하지 않았다.

범용적인 상황에서는 libmodbus의 구현이 더 맞겠지만 요청 데이터의 address와 개수의 범위가 이미 정해진 상태에서는 직접 만들었던 방식으로 충분하다고 생각되기때문에 무엇이 더 좋은 방법인지는 모르겠지만 나중에 참고가 필요할 때 참고하기 위해 분석 내용을 기록한다.

여담이지만 그냥 modbus ascii 프로토콜은 STX, ETX가 명확해 보여서 이걸 쓰면 간단할텐데…(?) 아쉽게도 이건 잘 안쓰는거같다.

암튼 이쯤에서 끝!

Qt Multimedia의 QMediaPlayer가 동작 안할 때

여기에서 말하는 문제는 윈도에서의 Qt 문제.

먼저 Backends 별 사용할 수 있는 기능(?)의 표는 아래 링크에서 확인가능.
https://wiki.qt.io/Qt_Multimedia_Backends

Qt 홈페이지에서 받을 수 있는 바이너리 라이브러리는 Multimedia Backends가 DirectShow로 되어있는것으로 보인다. 구글링해도 뾰족한 답을 찾기 힘들었는데 결국 이것도 몇시간동안 삽질해본 결과 재생 안되면 적절한 코덱을 깔아야하고(…) 아니면 Media Foundation을 사용하는 것이였다.

Media Foundation을 사용하려면 Multimedia 라이브러리를 빌드할 때 Media Foundation을 사용하도록 해야한다. 아래는 내가 사용한 qt-everywhere-opensource 패키지의 configure 옵션.

configure -opensource -confirm-license -prefix D:\OpenSource\Qt5.9.0.x64 -nomake examples -nomake tests -force-debug-info -platform win32-msvc -mp -opengl dynamic -mediaplayer-backend wmf

핵심은 -mediaplayer-backend wmf 옵션이다. 이렇게 빌드된것과 기본인 DirectShow를 사용하도록 빌드했을 때 대략 아래와같이 차이가 난다.

– DirectShow

dsengine.dll은 211KB, wmfengine.dll은 42KB

– Media Foundation

dsengine.dll은 63KB, wmfengine.dll은 167KB (qtmedia_audioengined.pdb 파일이 약간 차이있지만 코드에서 backend 별로 메크로 차이가 있는지는 확인 안해보았다.)

Media Foundation은 범용적으로 사용하는 코덱을 지원하므로 프로그램이 윈 7 이상이라면 이쪽을 사용하도록하는것이 더 나아보인다. (자세한 정보는 https://msdn.microsoft.com/ko-kr/library/windows/desktop/dd757927 이곳에서.) 물론 기존의 DirectShow가 더 다양한 코덱이 있을것으로 보기때문에 특정 포맷만 지원할것이 아니라면 DirectShow를 사용하는것이 더 나을 수 있다.

빌드 자체를 남기는 기록이 아니기때문에 여기까지. 만약 빌드 방법을 찾는다면 http://rette.iruis.net/2017/05/qt-5-8-0-webengine-%EB%AA%A8%EB%93%88-%EB%B9%8C%EB%93%9C/ 이 글을 참조.