기본 콘텐츠로 건너뛰기

3월, 2024의 게시물 표시

메타 퀘스트 3

2월 달 연말 정산 환급받은 돈으로 메타 퀘스트 3를 3개월 할부로 샀습니다. 제가 유일하게 이용하는 쿠팡에서 케이스와 같이 판매하는 놈으로 신용카드 결제하고 나서 며칠 뒤에 물건을 받았습니다. 퇴근하자마자 WIFI를 잡고 이것저것 세팅을 하고 있으니까 아내가 아이들과 학원을 마치고 함께 집에 왔습니다. 제가 하는 것을 물끄러미 보더니만 이거 얼마 주고 샀어? 응 쿠팡에서 00주고 샀어 그리고는 아내는 옷을 갈아입으러 안방으로 들어가고 저는 나머지 설정을 계속했습니다. 설정을 다 끝마치고 뒷정리를 하고 있는데 아내가 제 쪽으로 다가오더니 00 사이트를 보니까 이것보다 10만이나 싸던데? 가격 비교는 하고 산거지? 라고 물었습니다. 물론 쿠팡에서만 검색했지 다른 사이트와 가격 비교는 하지 않았습니다. 음.. 저는 말을 얼버무렸습니다. 아내는 한심하다는 듯 저를 쳐다보더니 10만 원이면 애들 학원을 하나 더 보낼 수 있는데 앞으로 이런 것 살 때 나한테 얘기해.. 저는 아무 말 없이 마저 뒷정리했습니다. 아내는 그 뒤로도 2번이나 같은 말을 저에게 했습니다. 물론 믿었던 쿠팡에서 10만 원이나 비싸게 주고 산 것에 마음이 쓰라렸지만, 가격 비교하고 검색하고 이것저것 비교한 후에 구매하는 것에 익숙하지 않아 이런 일들이 심심치 않게 일어나곤 합니다. 이번에도 제가 검색만 조금 했더라면 156GB가 아닌 526GB를 샀을 겁니다. 애들이 퀘스트 2에서 해보고 재미있었던지 엘리베이터를 타는 것을 설치해 달라고 해서 기기를 구매하면 제공하는 3만 크레딧으로 구매했습니다. ISLANDERS VR, Smash Drums 등을 추가로 구매하고 나니 3만 크레딧은 금방 사라지고 추가로 돈을 내야 했습니다. 3D로 구현되어서 그런지 퀘스트용 앱들은 크기가 모두 10GB 이상이 되어 앱을 몇 개 다운받으니 용량이 90%가 찼습니다. 배터리 시간도 완충하고 2시간 정도 사용하고 나면 충전을 해야 합니다. 저 같은 경우에는 게임을

[윈도우즈 서비스] Console 실행 - #1

윈도우즈 서비스에서 특정 작업을 수행할 경우, 작업이 한꺼번에 많이 몰릴 때를 대비하여 프로세스를 실행하여 작업을 처리하려고 합니다. (기존에는 서비스 내부에서 스레드(Thread)를 생성하여 처리했었는데 문제가 많이 발생했었습니다.) 이렇게 프로세스로 처리하면 작업 중에 오류가 발생하더라도 해당 프로세스만 종료되고 윈도우즈 서비스에는 아무런 영향도 미치지 않게 됩니다. 윈도우즈 서비스는 이 글 참조하여 생성하면 됩니다. 위 글에서도 나와있지만 관리자 권한으로 Command를 실행하여 서비스를 등록해야 합니다. 그렇지 않으면 아래와 같은 오류 메시지를 뿌리고 등록에 실패하게 됩니다. 서비스에 등록했으면 WIN 키( ) + R키를 눌러 services.msc 를 입력하여 윈도우즈 서비스 창을 엽니다. 하지만 서비스를 바로 실행하면 아마 아래와 같은 메시지가 뜨면서 서비스가 실행되지 않습니다. 서비스 화면에서 해당 서비스의 속성 창으로 들어가서 로그온 탭에서 계정을 로컬 시스템 계정으로 변경합니다.( '서비스와 데스크탑 상호 작용 허용' 을 체크해주세요.)  ->  다시 서비스를 실행하면 서비스가 제대로 실행됩니다. 서비스에서 실행할 Console 프로그램 서비스에서 Timer를 돌려 특정 시간마다 Console 프로그램을 실행하려고 합니다. 실행 중인 Console 프로그램의 Instance의 수를 얻어 Console 프로그램 실행 여부를 판단할 수 있습니다. string ProcessName = "ConsoleApp1"; // Get all instances of ConsoleApp1 running on the local computer. // This will return an empty array if ConsoleApp1 isn't running. Process[] ProcessByName = Process.GetProcessesByName(Proces

[윈도우즈 이벤트 로그] System.Security.SecurityException

프로그램에서 발생하는 에러를 이벤트 로그를 남기려고 아래와 같이 이벤트 로그에 접근하니 windowEventLog = Log; windowEventLog.Source = SourceName; windowEventLog.Log = LogName; if (!EventLog.SourceExists(SourceName)) EventLog.CreateEventSource(SourceName, "Application"); System.Security.SecurityException 오류가 발생했습니다. 위 5 번째 줄에서 원본 여부를 존재 여부를 확인할때 오류가 발생합니다. 검색해보니 EventLog.SourceExists 함수에서 EventLog\Security 에 접근하게 되는데 관리자 권한 이 없을 경우에 오류가 발생한다고 합니다. 이 경우에는 레지스트리에 SourceName 으로 키를 생성해주면 오류가 발생하지 않는다고 합니다. Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\Application\SourceName] 위 내용을 .reg 파일로 생성해서 실행시켜주면 됩니다.

IronPython 활용

예전부터 응용 프로그램에 Python을 결합하여 즐겨 사용했는데, 어떤 사용자의 요구사항 때문이었습니다. 그 사용자가 사용하는 프로그램에 계산식이 있는데 계산식이 프로젝트의 특성때문에 조금씩 수정해야 한다는 것이었습니다. 프로젝트마다 계산식을 수정해서 프로그램을 배포하기가 어려워 계산식 자체를 Python 코드로 외부 파일로 빼내는 방식으로 처리했습니다. 어느날 차를 몰고 가는데 전화가 와서 이번 프로젝트에서는 factor 3을 3.5로 바꿔야 하는데 어떻게 하나요? 라고 문의가 왔습니다. Python 파일을 열어 숫자 3을 3.5로 바꾸면 된다고 답변을 해줬습니다. 이렇게 사용자는 프로젝트마다 Python에서 계산식을 수정하여 사용하면 되기 때문에 프로그램을 수정할 필요가 없어집니다. 이전 글 에서도 이야기했듯이 IronPython은 C#과 궁합이 아주 좋아 이번 프로젝트에서도 IronPython을 프로그램에 포함시켰습니다. 자동으로 생성된 배관 라인을 수정하거나 새로 생성하는 기능을 위해 IronPython을 활용했습니다. 먼저 앱 실행 경로를 IronPython 검색 경로에 추가해줍니다. 1 2 3 4 5 6 #region IronPython의 검색 경로에 앱 실행 경로를 추가 var paths  =  _IronPython.GetSearchPaths(); var ExecutePath  =   System .IO.Path.GetDirectoryName(Application.ExecutablePath); paths.Add(ExecutePath); _IronPython.SetSearchPaths(paths); #endregion Colored by Color Scripter cs 아래와 같이 C# Class를 정의하고 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public   class  MoveTo {      public  MoveTo(PythonTuple value)     {          int

[EYESHOT] Ver 2022에서 Ver 2023으로 업그레이드

기존 2022버전에서 2023으로 업그레드하기 위해 GIT에서 2023 브랜치를 새로 만들었습니다. devDept 사이트 에서 최신 2023 버전을 다운받습니다. 오늘 들어가 보니 2023.3.630 이 최신 버전입니다. 설치한 후 Activate 툴의 Convert 탭에서 기존 프로젝트를 Eyeshot 2023프로젝트로 변환할 수 있습니다. 아래 Got It! 버튼을 눌러 기존 프로젝트 폴더를 선택하면 선택한 프로젝트의 Eyeshot 버전이 표시됩니다. 2023으로 업그레이드한 화면을 캡쳐했네요.. Upgrade 버튼을 누르면 자동으로 코드를 변경합니다. 이때 레퍼런스까지 자동으로 변경됩니다. 레퍼런스가 자동으로 추가된 모습 프로젝트를 열어 컴파일해서 자동으로 변경하지 못한 부분에서 발생한 오류나 프로그램 동작 시 이상한 부분들을 수정해줍니다. 2023 변경 사항 에서 2023 버전에서의 변경 사항을 확인할 수 있습니다. 실제 프로젝트를 변경해보니 Designer쪽에 넣어놓은 컨트롤들은 거의 변경되지 않았습니다. Workspace 및 ProgressBar의 namespace가 devDept.Eyeshot에서 devDept.Eyeshot.Control로 변경되어서 Designer쪽에서 변경해주고나면 코드 쪽에서는 거의 대부분 자동으로 변환됩니다.

[EYESHOT] LinearDim 생성하기

Eyeshot에서 LinearDim을 생성하는 방법을 찾아봐도 자세한 설명이 나오지 않아 조금의 시행 착오를 거쳤습니다. Eyeshot 도움말을 찾아보면 LinearDim의 생성자는 아래와 같습니다. 1 2 3 4 5 6 7 public  LinearDim(     Plane dimPlane,    Point3D extLine1,    Point3D extLine2,    Point3D dimLinePos,     double  textHeight ) cs Parameters dimPlane     The dimension plane extLine1     First extension line point extLine2     Second extension line point dimLinePos     Dimension line position. Affects text position when the text cannot fit between extension lines. textHeight     Text height dimPlane은 Dimension이 놓여지는 평면입니다 음... 백문이불여일견이라 아무래도 말이나 글로 설명하는 것보다 그림으로 보여주는 것이 이해하기가 더 쉬울 것 같습니다 하지만 Eyeshot에서는 그림을 제공하지 않아 제가 그려봤습니다. 여기서 dimPlane을 변경하면 같은 위치의 extLine1, extLine2라도 표시되는 Dimension이 바뀌게 됩니다. dimPlane 변경 extLine1과 extLine2를 dimPlane의 X축으로 Projection한 거리가 Dimension의 값이 되고, dimPlane의 Y축 방향으로 extLine1, extLine2에서 지시선이 생깁니다. Dimension Text는 dimPlane의 법선 방향으로 dimLinePos의 위치에 표시됩니다. 만일 dimPlane의 법선 방향이 반대 방향으로 되면 Dimension Text

Shipping Box - #2

이전 글 에서 미처 고려하지 못했던 사항이 있어 이 글을 작성하게 되었습니다. 길이가 가장 긴 직선 구간을 선택해 그와 붙어 있는 직선 구간을 선택해 두 벡터를 외적하여 폭 벡터를 구한다 위 부분은 두 직선 구간이 수직이라는 가정으로 작성되었습니다. 어제 동료와 이야기하다 45도로 연결된 구간을 어떻게 처리해야 하는지 고민이 된다는 이야기를 들었을 때 이전 글에서 위 부분이 문제가 있다는 것을 알았습니다. 수학 시간에 우리가 배웠던 모든 축 벡터를 서로 수직입니다. 한번 기억을 더듬어 보시기 바랍니다. 따라서 우리가 구해야 하는 축 벡터도 서로 수직이어야 합니다. 또 다른 말로 서로 독립적이어야 합니다. 그런데 두 직선 구간이 45로 연결되어 있다면 두 구간을 독립적이지 않게 됩니다.(한 축의 좌표가 다른 축 좌표 값을 포함하게 됩니다.) A, B 벡터가 있다고 가정하고 두 벡터가 서로 수직이지 않다고 하면, A, B 벡터 외적으로 A, B에 수직인 C를 구할 수 있습니다. $$\vec{C} = \vec{A}\times\vec{B}$$ 그리고 A, C 벡터 외적을 통하여 B 벡터를 구할 수 있습니다. 이때 B 벡터는 A, C 벡터에 수직이 됩니다. 이로써 세 벡터 A, B, C는 수직이며 서로 독립적입니다. $$\vec{B}=\vec{A}\times\vec{C}$$ 이로써 이전 글의 문제점을 보완할 수 있습니다. 하지만 복잡해 보입니다. 단순함이 복잡함보다 아름답습니다. 이전 글에서의 목적을 간단히 되짚어 보면 점들을 포함하는 OrientedBoundingBox를 구하는 것입니다. 위 점들에서 가장 멀리 떨어진 두 점을 선택하고 두 점을 잇는 선(A)을 구합니다. 이 선에서 가장 멀리 떨어진 점을 하나 선택해 선에 수직인 다른 선(B)을 구합니다. A, B 두 선의 외적을 이용하여 두 선에 수직인 벡터 C를 구할 수 있습니다. 점들을 A, B, C 축으로 변환하여 점들을 포

GITHUB 블로그를 Google 검색 엔진에 노출하기

요즘 GITHUB에 블로그를 생성하여 블로그 작성에 의욕을 불태우고 있는데, 아무래도 혼자만 만족하는 것보다 여러 사람들과 공유하는 것이 좋을 것 같아서 Google 검색 엔진에 노출하는 방법을 찾아 보았습니다. 저는 Chripy JekyII 테마를 이용하여 블로그를 만들었는데, Google 검색 엔진에 노출하는 방법은 찾아보면 많이 나와있어 여기서는 언급하지 않겠습니다. [Github Blog] Github blog를 Google 검색 엔진에 노출시키기 다만 제가 따라하면서 겪었던 오류에 대해서 이야기 해보겠습니다. 저는 아래와 같이 수작업으로 sitemap.xml을 생성하여 root폴더에 두었습니다. --- layout: null --- {% for post in site.posts %} {{ site.url }}{{ post.url }} {% if post.lastmod == null %} {{ post.date | date_to_xmlschema }} {% else %} {{ post.lastmod | date_to_xmlschema }} {% endif %} {% if post.sitemap.changefreq == null %} weekly {% else %} {{ post.sitemap.changefreq }} {% endif %} {% if post.sitemap.priority == null %} 0.5 {% else %} {{ post.sitemap.priority }} {% endif %} {% endfor %} 로컬에서 테스트하기 위해 http://localhost:4000/sitemap.xml을 호출하니 아래와 같은 오류가 발생했습니다.

[C#] Class를 Json으로 저장하기

JsonConverter.Serialize를 사용하면 Class를 Json으로 변환할 수 있습니다. 하지만 Json으로 변환할때 Class를 명시해줘야 하기 때문에 아래와 같은 경우는 처리하기가 어렵습니다. 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 class  A {      public   virtual   string  Type { get; set; }  =   "A" ;      public   string  Parent { get; set; }  =   "Parent" ; }   class  B : A {      public   override   string  Type { get; set; }  =   "B" ;      public   string  Child { get; set; }  =   "Child" ; }   List < A >  list  =   new   List (){ new  A(),  new  B()}   foreach (var tmp  in  list) {      if (tmp  is  A a)     {         JsonConverter.Serialize < A > (a);     }      else   if (tmp  is  B b)     {         JsonConverter.Serialize < B > (b);     } } Colored by Color Scripter cs 위와 같은 코드는 상속받는 클래스가 늘어나면 해당 코드를 변경해야 하기 때문에 관리가 힘듭니다. 아래와 같이 바꾸면 되겠지만 이때 상속 받은 B 클래스가 Json으로 제대로 변환되지 않습니다. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class  A {      public   virtual

Ellipse의 Start, End Point 구하기

포맷마다 프리미티브를 정의하는 것이 달라 데이터 교환을 위해서 프리미티브 변환을 해줘야 합니다. Ellipse는 아래와 같이 정의할 수 있습니다.($\alpha$=시작 각도, $\beta$=회전 각도) 위 그림에서 Ellipse상의 점 $A$는 아래 식으로 구할 수 있습니다. $x^\prime = a*cos(\theta)\\y^\prime = b*sin(\theta)$ 위 식은 2D일때의 식이고 그럼 3D일때는 어떻게 식을 구해야 할까요? $a, b$ 는 각각  $\vec{U}$축, $\vec{V}$축상의 거리이기 때문에 위 식은 아래 식으로 변환할 수 있습니다. $x^\prime = a*\vec{U}*cos(\theta)\\y^\prime = b*\vec{V}*sin(\theta)$ 위 식이 Ellipse의 일반식이 됩니다. $\vec{U}, \vec{V}$에 축의 벡터를 입력하면 원하는 좌표를 구할 수 있습니다. 예를 들어 $\vec{U}$에 $\vec{X}([1, 0, 0])$을 $\vec{V}$에 $\vec{Y}([0,1,0])$을 대입하면 아래와 같습니다. $$x^\prime = a*[1_x,0_y,0_z]*cos(\theta)\\y^\prime = b*[0_x,1_y,0_z]*sin(\theta)$$ $\vec{X}$축의 $y,z$ 요소는 0이고, $\vec{Y}$축의 $x,z$ 요소는 0이기 때문에 이것을 풀어보면 첫번째 식과 같게 됩니다. $\vec{U}$, $\vec{V}$축이 임의의 3D 축이라고 가정해보면 $$x^\prime = a*[u_x,u_y,u_z]*cos(\theta)\\y^\prime = b*[v_x,v_y,v_z]*sin(\theta)$$ 월드 좌표 $(x,y,z)$는 아래 식으로 구할 수 있습니다. $$x = a*u_x*cos(\theta)+b*v_x*sin(\theta)\\y = a*u_y*cos(\theta)+b*v_y*sin(\theta)\\z = a*u_z*cos(\theta)+b*v_z*sin(\theta)$$