기본 콘텐츠로 건너뛰기

명령행으로 웹서비스 배포하기

웹서비스를 단위 테스트하기 위하여 명령행으로 웹서비스를 배포하도록 작성했습니다. 일반적인 단위 테스트트는 프로젝트는 참조하여 테스트를 진행하면 되지만 웹서비스의 경우에는 먼저 서비스가 웹서버에 배포되어야 하는데 수작업으로 웹서버에 배포를 해야한다면 단위 테스트를 자동화할수 없습니다. 그래서 어플리케이션을 빌드할때 웹서비스를 컴파일하고 -> 명령행으로 자동으로 웹서버에 배포한 뒤 -> 단위 테스를 진행하도록 하였습니다. MSBuild.exe를 이용하여 웹서비스를 컴파일 한뒤 배포할 수 있습니다. WebApi 프로젝트를 Release로 컴파일 한뒤 CustomProfile을 이용하여 웹서버에 배포합니다. 1 %MSBUILD% WebApi.csproj  / t:WebPublish  / p:DeployBuild = true ;Configuration = Release  / p:PublishProfile = CustomProfile  / p:VisualStudioVersion = 15. 0   cs 컴파일에 앞서 배포할 Profile을 하나 만들어 줍니다.(참조 : https://docs.microsoft.com/ko-kr/aspnet/web-forms/overview/deployment/visual-studio-web-deployment/deploying-to-iis" ) 프로젝트 파일을 컴파일하는 경우에는 VS 버전을 명시해야 한다고 합니다. (/p:VisualStudioVersion=15.0 => VS 2017) 이렇게 웹배포가 끝난뒤에 xUnit을 이용하여 Web Api에 대한 단위 테스트를 진행할 수 있습니다.

[ARS] Clash Check

Piping Auto Routing을 개발하다 보면 파이프가 장치 혹은 구조물을 뚫고 지나가는 현상이 발생하게 됩니다. Routing을 돌리고 나서 무수히 많은 모델에서 이러한 간섭을 일일이 찾는 것은 힘들고도 시간 낭비입니다. 그래서 간섭이 일어난 부분을 찾는 Clash Check 기능이 필요합니다. Clash Check 기능은 모델의 OrientedBoundingBox를 구해 OrientedBoundingBox끼리 충돌 검사를 하여 결과를 리포트합니다. 리포트를 더블 클릭하면 충돌이 발생한 두 OrientedBoundingBox를 줌하여 화면에 표시합니다. 어떻게 말해야 할지 모르겠지만 일반적인 경우(두 OrientedBoundingBox의 크기가 비슷할 경우)는 충돌이 발생한 지점을 찾기가 쉽지만, 한 쪽이 다른 쪽에 비해 월등히 크다면 충돌이 일어나 지점을 정확히 찾기가 어렵게 됩니다. 좀 더 정확한 충돌 지점을 찾기 위해서는 충돌이 일어난 OrientedBoundingBox를 세분화하여 작은 OrientedBoundingBox들을 생성합니다. [하나의 OrientedBoundingBox를 여러 개의 작은 OrientedBoundingBox로 나눔] Small OrientedBoundingBox의 좌표는 아래 식으로 계산할 수 있습니다. $$(x,y,z) = \overrightarrow{X}*i + \overrightarrow{Y}*j + \overrightarrow{Z}*k,\ (i,j,k는 각각 \overrightarrow{X},\overrightarrow{Y},\overrightarrow{Z}상의 값)$$ 그리고 생성한 OrientedBoundingBox들 끼리 충돌 검사를 하여 충돌이 일어난 정확한 지점을 찾습니다.

DataBinding

뷰에 데이터를 연결하여 화면에 데이터를 표시할때 DataBinding을 많이 사용합니다. 일반적인 예가 GridView 에 데이터를 연결하는 것입니다. 1 this.radGridViewProject.DataSource = this.Projects; cs GridView가 아닌 일반 컨트롤, 예를들어 TextBox나 ComboBox에도 DataBinding을 사용할 수 있습니다. 1 this .radTextBoxTcpServerPort.DataBindings.Add( "Text" , Properties.Settings.Default,  "TcpServerPort" ); cs 첫번째 인자 Text는 TextBox의 속성 이름이고 두번째는 DataSource 그리고 마지막은 DataSource의 멤버 이름을 설정하면 됩니다. 즉 위 코드는 TextBox의 “Text” 속성에 Properties.Setting.Default의 “TcpServerPort”를 연결시킵니다. 이렇게 UI와 데이터를 연결시켜 놓으면 UI 변경 시 연결된 데이터도 함께 변경됩니다. 1 2 3 4 5 6 7 8 9 this .radTextBoxTcpServerPort.DataBindings.Add( "Text" , Properties.Settings.Default,  "TcpServerPort" ); /// DO SOMETHING(예: UI를 통한 TcpServerPort 수정) Properties.Settings.Default.Save(); DataBinding을 사용하지 않으면 UI를 통해 수정한 내용을 TcpServerPort에 적용 후에 저장해야 합니다.   this .radTextBoxTcpServerPort.DataBindings.Add( "Tex...

Host 어플리케이션과 AutoCAD 플러그인의 상호 연동

AutoCAD 플러그인을 개발하면서 항상 고민이 되는 부분이 어떻게 Host 어플리케이션과 연동하는냐하는 문제였습니다. 초기에는 윈도위 API인 SendMessage를 이용하여 서로 데이타를 주고 받도록 디자인을 했었는데 뭔가 밀접하게 연결이 되어 있지 않다는 느낌이 강했습니다. 그래서 Tcp/IP 통신을 이용하여 상호 연동하는 방식으로 변경하기로 했습니다. AutoCAD에 플러그인이 로딩됨과 동시에 Tcp/IP로 Host 어플리케이션이 접속하기를 기다립니다. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 public   void  DoAcceptTcpClient() {     _TcpListener.Start( 1 );        // Start to listen for connections from a client.      while  ( true )     {          // Accept the connection.           using  (TcpClient client  =  _TcpListener.AcceptTcpClient())         {  ...

White Space 검색

아래와 같이 주어진 도면에 Label을 도면 상의 다른 객체들과 겹쳐지지 않게 배치하고자 할때 White Space 검색이 필요합니다. 먼저 도면의 모든 객체들을 포함하는 Bounding Box를 구합니다. 이 Bounding Box 안에 Label을 배치한다고 가정하겠습니다. 도면 상의 객체의 Bounding Box를 구해 도면의 Bounding Box안에 배치시킵니다. 그리고 배치하려는 Label의 Bounding Box를 구한 다음 Label을 배치할 위치를 찾을 Search Bounding Box를 설정합니다. 일반적으로 Label을 가지는 객체의 Bounding Box의 비율로 Search Bounding Box를 구합니다.(물론 도면 전체 Bounding Box가 될 수도 있습니다.) 라인 넘버의 경우 Search Bounding Box를 파이프 길이 방향으로 길게 생성하면 될것 같습니다. Search Bounding Box안에서 Label을 X,Y로 조금씩 이동(δt)하면서 Score를 구합니다. Score = distance * distance factor + overlap area ∗ overlap area factor distance = 밸브의 원점과 Label 위치와의 거리 distance factor = distance가 score에 미치는 영향 overlap area = label의 bounding box가 도면 상의 객체와 겹치는 영역 overlap area factor = overlap area가 score에 미치는 영향 $\delta t$의 크기가 작을 수록 Label을 배치할 위치의 정밀도가 높아지지만 위치를 찾는데 걸리는 시간을 길어집니다. overlap area는 사각형들이(Label과 여러 객체들이 동시에 겹쳐질수 있음) 겹쳐지는 영역을 구하면 됩니다. Label을 회전할 필요가 있으면 회전한 Bounding Box를 구하여 Search Bounding ...

SQLite Bulk Insert

SQLite에는 데이터를 대량 삽입하는 특별한 방법이 없습니다. 데이터를 삽입하거나 업데이트할 때 최적의 성능을 얻으려면 다음을 수행해야 합니다. - 트랜잭션을 사용하세요. - 데이터베이스에 연결하는 횟수를 줄이세요.(Command를 하나만 생성하여 재사용) - 동일한 매개 변수를 사용하여 명령을 다시 사용하세요. using (var transaction = connection.BeginTransaction()) { var command = connection.CreateCommand(); command.CommandText = @" INSERT INTO Markup(UID, Color) VALUES ($UID, $Color) "; var paramUID = command.CreateParameter(); paramUID.ParameterName = "$UID"; var paramColor = command.CreateParameter(); paramColor.ParameterName = "$Color"; command.Parameters.Add(paramUID); // Insert a lot of data var random = new Random(); for (var i = 0; i 단일 건마다 Command를 생성하여 쿼리를 실행하는 것보다 엄청난 성능 향상을 볼 수 있습니다.

유용한 Visual Studio 확장 기능

유용한 Visutal Studio 확장 기능을 소개해 드리고자 합니다. 1. SonarLint      1. 정적 분석을 통해 잠재적인 코드 상의 오류 사항 표시 및 개선 코드를 제시해 줍니다.      2. 개선 코드 제시 var blk = ViewModelControl.Blocks.Where(param => param.Name.Equals((node.DataBoundItem as Wbs).UID)).FirstOrDefault(); if (blk != null) { ViewModelControl.Blocks[(node.DataBoundItem as Wbs).UID].Entities.SelectAll(); } var blk = ViewModelControl.Blocks.Where(param => param.Name.Equals((node.DataBoundItem as Wbs).UID)).FirstOrDefault(); if (blk != null) { ViewModelControl.Blocks[(node.DataBoundItem as Wbs).UID].Entities.SelectAll(); } 위 코드에 대해서 아래와 같은 개선 코드를 제시해 줍니다.(Where 대신에 FirstOrDefault를 사용하라고 함) 위 제안대로 바꾸면 같은 동작을 하는 좀더 간단한 코드로 바꿀 수 있습니다. var blk = ViewModelControl.Blocks.FirstOrDefault(param => param.Name.Equals((node.DataBoundItem as Wbs).UID)); if (blk != null) { ViewModelControl.Bl...

Shipping Box

  배관 자재를 운반하기 위한 컨테이너 박스에 운송비를 줄이기 위해 최대로 많은 배관 자재를 넣기 위한 방안입니다. 컨테이너 박스는 아시다시피 직육면체로 보시면 되고 배관 자재는 분기(TEE)가 없는 선형적인 구조라고 가정하겠습니다.(직관, ELBOW, REDUER, FLANGE로만 구성)   쉽게 생각해보면 " 컨테이너 박스에 차례로 배관 자재를 들어갈때 까지 넣으면 될것 같습니다. " 이게 끝일까요? 이렇게만 하고 코딩을 시작하면 될까요? 가슴 한 구석에서 " 이렇게만 하면 안될 것 같은데... "라고 이야기하는 것 같습니다.   여튼 시작해보겠습니다. 일반적으로 컨테이너 박스는 차량에 실려 운반되기 때문에 한쪽이 긴 직육면체입니다. 다행이 직관이 컨테이너의 길이 방향으로 놓여있다면 쉬울것 같은데 그렇지 않다면 문제가 됩니다.   그래서 직관을 컨테이너 길이 방향(각도 0)으로 회전시켜서 넣습니다.   코딩을 해봅시다. if angle == 0:     pass elif angle == 90:     직관을 -90도로 회전 elif angle == 270:     직관을 -270도로 회전 else:     pass 여러분들이 이런 코드를 보면 위험하다는 느낌을 가져야 합니다. 평면(360)을 4분면으로 나누어 처리했는데 일반 배관 라인은 직교하여 생성하기 때문에 큰 무리가 없어 보입니다만  사선으로 이동하는 배관 라인의 경우에 우리가 원하는 결과를 만족시킬수 없습니다.   그럼 45도를 코드에 넣어서 처리하면 되지 않을까 생각하시겠지만 if angle == 0:     pass elif angle == 45:     직관을 -45도로 회전 elif angle == 90:     직관을 -90도로 회전 elif angle == 270:   ...

[C#] 작업 프로그래스 바

 장시간(3초 이상)의 작업이 진행되는 경우 사용자가 멍하니 아무런 변화가 없는 모니터를 보게 하는 것 보다 작업 진행 상태를 보여주는 것이 좋습니다. 퍼센트로 작업 진행률을 보여주면 더할 나위 없이 좋겠지만 그렇지 못할 경우에는 진행 애니메이션이라도 보여주는 것이 좋습니다. 이렇게 작업 애니메이션을 보여주기 위해서 C#에서는 BackgroundWorker를 많이 사용합니다. 사용자에게 보여지는 화면은 Main Thread에서 생성하고 실제 작업은 BackgroundWorker에서 담당하게 됩니다. 작업이 진행되는 동안 이벤트를 받아 UI에서 진행률을 업데이트할 수 있습니다. BackgroundWorker는 아래 3개의 이벤트를 제공하고 있습니다. DoWork : 실제 작업을 담당하는 이벤트 ProgressChanged : 작업 진행률을 업데이트하는 이벤트 RunWorkerCompleted : 작업이 끝났을 때 호출되는 이벤트 BackgroundWorker에 대한 예제는 MSDN 에서 찾을 수 있습니다. 이렇게 개발을 진행해도 되지만 개발의 편의성을 위해 BackgroundWorker와 UI를 감싸는 클래스를 만들어 보았습니다. 이 클래스는 작업이 진행되는 동안에 부모 윈도우에 작업 애니메이션을 보여주고 작업이 끝나면 애니메이션을 멈추게 됩니다. 작업은 가상 함수를 override해서 수행할 수 있습니다. 작업 애니메이션은 Telerik의 RadWaitingBar를 이용하여 구현하였습니다.(GIF 애니메이션 파일을 따로 구할 필요가 없어 편리합니다.) class ARSWorker { protected System . Windows . Forms . Control _parent = null ; private BackgroundWorker worker = new BackgroundWorker ( ) { WorkerReportsProgress = true , WorkerSupportsCancellation = ...