반응형

[python] 버전에 대한 논의와 라이브러리 설치

버전에 대한 (의미없는) 논쟁

 필자가 처음 파이썬을 접하게 된것은 기록시점으로부터 약 4년전인 2014년 말 혹은 2015년 초 쯤 이었다. 당시 필자는 순수 물리학으로 졸업해서 대학원을 컴퓨터 공학으로 전과 지원을 해서 공부를 했기 때문에 이제 막 C/C++와 자료구조를 한참 익히고 있을 때였다.


 그러던 중 멀티미디어 강의를 하고 있던 선배의 부탁으로 실습준비를 도와주면서 파이썬을 처음으로 접하게 되었다. 당시 python3.1이 나오던 시기였기 때문에 대부분 자료는 python2.7을 기준으로 하는 자료가 많았다. 게다가 대부분읜 자료는 윈도우보다는 리눅스나 OS X 기반환경에 대한 설명이 주를 이루었었다. 이는 당시 윈도우 환경에서는 오류가 나는 일이 많았던 것으로 기억이 난다.


 때문에 당시에 규모 있는 커뮤니티에서 2.7 버전과 3.x 버전 중 어느 버전을 사용해야 하느냐는 질문이 나오면 2.x 버전을 사용하는 사람을 게으름뱅이(?)로 취급하는 사람들도 꽤 있었기 때문에 논쟁이 격해지기도 했는데, 의미가 없는 짓이다. 일단 당시 수치계산에 중요한 numpy 라는 라이브러리가 아직 3.x를 완벽하게 지원되지 않았기 때문에 이는 자신이 하려는 일에 지원이 되는지 여부를 확인하고 선택해야 한다.


 물론 지금은 시간이 많이 지나서 기존 2.7에서 지원되는 라이브러리가 3.x에서도 많이 되는것 같다. 특히 pip 같은 라이브러리 관리 툴의 경우 윈도우 환경에서 사용하기 위해서는 별도로 설치 방법을 찾아서 설치를 해주었어야 했으나 지금은 Python 설치시 같이 설치가 된다.



라이브러리 설치

 라이브러리 설치는 이미 Python이 설치되어 있다는 전제하에서 진행을 한다.

 라이브러리를 설치하는 방법은 직접 파일을 다운로드 받아서 설치하는 방법이 있지만, 오프라인 환경에서 작업해야 하는 것이 아닌 이상 pip를 통해서 설치하면 된다. 만약 python2.7 과 python3.x를 같이 혼용해서 사용한다면, -m 옵션을 사용해서 명령어를 실행하면 된다.


예시로 numpy를 설치한다.


# 한가지 버전만 사용하는 경우
pip install numpy

# 두가지 버전을 같이 사용하는 경우 만약 3.x의 경우
python3 -m pip install numpy


 위의 명령어를 powershell 이나 cmd 창에서 실행하면 된다. MS에서는 최근 powershell을 밀고 있으니 powershell을 사용하는 것도 권장한다.

 만약 opencv를 설치할 경우에도 pip를 통해서 설치가 가능하다. 과거에는 python 패키지 관리격인 anaconda를 통해서 설치가 가능했지만, 필자가 이 글을 적는 시점에 확인한 결과 opencv도 설치가 가능했다.


# OpenCV 를 설치하는 경우
# 여기서 matplotlib는 그래프 같은 새창을 띄울때 사용되는 라이브러리이다
python3 -m pip install matplotlib opencv-python

# python3 인터프리터를 호출한후 라이브러리 호출을 확인한다
>>>import cv2
>>>print(cv2.__version__)


대부분 설치 확인을 이미지를 띄워보지만, 버전만 확인해봐도 호출이 제대로 되는지 확인이 된다.


 위의 방식으로 설치할 경우 버전이 최신으로 설치가 되니 버전을 선택을 하고 싶다면, 버전도 추가로 입력을 해줘야 한다. 이처럼 과거보다 python 프로그램의 진입장벽이 많이 낮아 진것이 체감된다. 특히 OpenCV의 경우 C++로 개발하는 것이 아니라면, 이제는 소스를 받아서 CMake로 메타 빌드까지 안해도 되는 시대가 되었다.


참조자료

python opencv 설치 방법



반응형

'Python > Python3' 카테고리의 다른 글

[python3]타입검사(Type Check)  (0) 2019.04.23
[Python3]Str(문자열)의 편리한 기능들  (0) 2019.04.20
[Python3]List의 편리한 기능들  (0) 2019.04.19
[python]CSV 데이터 포멧 입출력  (0) 2018.10.22
[python]기본 Shell 활용  (0) 2018.10.20
반응형

[라즈베리 파이]비공식 멀티카메라 모듈 후기(ivport v2)

배경

 많은 수의 카메라 컨트롤을 해야 하는 프로젝트를 진행하고 있었기 때문에 카메라 수를 줄이고 싶었다.

 비공식적으로 터키에 소재하는 ivmech 업체에서 Ivport v2 라는 이름으로 단일 카메라 포트를 여러개의 pi camera로 확장하는 모듈을 제작했다. 기존에도 Ivport v1으로 pi camera v1에 대해서 멀티카메라 모듈을 제공한 적 있는 업체였다.

 ivport v2 중첩으로 연결이 가능하고 최대 4번 중첩하여 16개의 카메라 연결이 이론적으로 가능하다(실제로는 여러 에러사항 때문에 중첩하여 사용하지 않을 것을 권장한다).



모듈 드라이버

 제조사에서 드라이버를 제공하는데, Python으로 작성되어 있다(ivport 드라이버 링크). CSI(Camera Serial Interface)포트 하나와 GPIO 핀과 연결이 되어 있어야 한다. 드라이버의 역할은 GPIO의 신호를 바꿔서 Ivport v2 모듈에 신호를 줘서 연결되어 있는 카메라를 바꾸는 방식이다.


 만약 Python 이외에 다른 언어로 구현을 해야 하는 경우라면 직접 드라이버를 만들어야 할 것이다. 그렇다면 드라이버 코드를 보고 GPIO 신호를 바꿔주는 것을 구현해야 한다. 이에 적합한 건 C 라이브러인 Wiring이 있다.


 그 외 이미지 데이터를 얻는 방법은 공식 프로그램인 raspistill 과 같은 프로그램을 사용하거나 동일하게 제공는 라이브러리르 사용해서 얻을 수 있다.



원리

 드라이버에 대해서 분석이 되면, 눈치가 빠른 사람은 모듈이 한번에 하나씩 카메라에 접근하는 방식으로 동시에 카메라에 접근 할 수 없다는 것을 알게 될 것이다. 이는 카메라로 부터 얻는 이미지 데이터의 크기가 커질 수록 느릴 수 밖에 없다.



성능

 제조사 측에서는 스테레오 카메라로 라이브 스트리밍을 시연 동영상을 유투브에 올려놨는데, 중간에 일시 정지 해보면 0.03 ~0.05초 정도 차이가 나서 준수해 보이지만, 역시 화질과 이미지 사이즈를 언급이 안되어 있다(모니터에 보이는 큰 화면은 출력해서 resize한다면 큰 문제가 아니다).


 하드웨어 조건 고려해서 Pi Camera v2 의 최대 고화질 카메라 이미지 수신에 걸리는 시간을 계산했을 때 최대 약 0.05초 정도 걸린다는 걸 계산했었다. 이는 이론 값이니 실제로는 더 걸릴 것이다.


 해당 내용을 PM에게 보고 하자 제조사측에도 공식 문의를 하기로 결정했고, 그 결과 얻는 답변은 Low 단으로 수정했을 경우 2대의 카메라간 동기오차는 0.10 ~ 0.15초의 오차를 얻을 수 있을 것이라고 했다. 즉, 동기가 중요한 경우 그냥 여러대의 라즈베리 파이를 구매해서 신호 동기화를 시키는 게 낫다.


 또 테스트를 직접 했을 때 재미있는 현상이 몇 가지 있었는데, 1번 카메라가 없으면 모듈이 아예 인식이 안된다.



평가

 해당 모듈은 라즈베리 파이 재단에서 제작하지 않은 비공식적인 모듈이다. 해당 모듈은 라즈베리 파이 본체가 있어야 사용할 수 있는 모듈인데, 라즈베리 파이 본체보다 금액이 좀 더 비싸다. 물론 카메라 4대를 전부 연결해서 본체를 덜 구매 한다면 이득이지만, 동기화에 한계가 있다.

 심지어 ivport v2 모듈이 소모하는 전력이 의외로 많은지 4개의 카메라를 전부 연결하면 1개는 인식이 안되었다.


 취미로 하는게 아니라면, 오히려 제약이 많기 때문에 가능하면 사용하지 않는 것이 좋다.

반응형
반응형

C# : using문의 기능과 활용

알게 된 배경

 C++에서 개발을 하다가 C#을 접하게 되었을 때 익숙하면서도 생소한 문법이 using문이었다. C++에서도 namespace를 선언할 경우 'using namespace [이름];' 형태의 문법을 사용하고 C#에서도 마찬가지로 최상단에 'using System;' 형태로 기본적으로 작성이 되어 있기 때문이다.


 하지만, 입문을 제대로 하지 않은 상태에서 파일, 네트워크 등 IO(입출력)에 해당되는 MSDN 문서를 보면 코드의 메서드 안에서 사용되는 using문을 쉽게 볼 수 있다. 하지만, 기존에 using 사용되던 키워드(?)와 다른 기능이라서 처음에 쉽게 사용하기 어렵다.


 특히나 예외처리 문법의 finally 문의 기능과 유사했기 때문에 필자의 경우 예제를 보기 전까지는 제대로 활용을 하지 못 했었다.


using문의 의미

 키보드와 콘솔 화면 출력을 제외한 대부분의 IO(입출력)의 경우 실제로 stream이나 unmanagement 메모리 공간을 할당하고 해제 하는 패턴 혹은 Stream Open 과 Stream Close 하는 패턴으로 코드를 작성해야 한다.


 C++의 경우 소멸자가 유용하게 사용되는데, C# 역시 소멸자를 제공하지만, IDisposeable 인터패이스를 상속하여 Dispose라는 메서드를 override하여 Unmanagement 메모리 해제나 Stream Close를 구현하도록 권장을 하고 있다. 그리고 이렇게 구현된 Dispose 메서드는 using문의 Scope에서 벗어나면, 호출이 하도록 하여 자연스럽게 C++의 소멸자와 비슷한 기능을 수행한다.


using문 예시


using System;
using System.IO;

class FileRead
{
    var imgFilePath = @".\img.jpg";
    using(var imgFileStream = new FileStream(imgFilePath, FileMode.Open))
    {
        var tmpImg = Image.FromStream(imgFileStream);
        // ... 중략
    }
}



예외처리의 finally문과의 관계

 IO처리에 있어서 항상 염두해야 하는 문법은 예외 처리이다. 이는 파일 입출력의 경우 파일을 읽을 때 파일이 없는 경우, 통신중에 네트워크 연결이 끊어진 경우 등 비정상적인 동작을 대비해서 작성하는 문법이다.


 여기서 finally 문의 경우 예외가 발생하던 안하던 간에 반드시 실행되는 내용을 작성을 하도록 되어 있다. 가령 Stream Open 한뒤에 작업중에 예외가 발생해서 파일을 닫아야 하지만, 예외 발생 하지 않은 경우에도 닫아야 하는 경우에 사용된다.


try-catch-finally문 예시

using System;
using System.IO;

class FileRead
{
    var imgFilePath = @".\img.jpg";
    try
    {
        var imgFileStream = new FileStream(imgFilePath, FileMode.Open);
        var tmpImg = Image.FromStream(imgFileStream);
        // ... 중략
    }
    catch(Exception ex)
    {
        // ... 예외 처리
    }
    finally
    {
        imgFileStream.Close();
        // ... 혹은 imgFileStream.Dispose() 호출
    }
}




 여기서 생각을 가만히 해보면 finally문이 using문과 역할의 유사점을 느낄 수 있다. 오히려 finally문의 경우 Close 같은 메서드를 직접 호출하는 반면, using문은 IDisposeable을 상속 받은 객체의 경우 자동으로 Scope를 벗어나면 Dispose() 메서드를 호출한다.


 그리고 MS의 대부분 Stream에 관련된 클래스들은 Dispose() 메서드에 이러한 기능들이 정의 되어 있다. 따라서 IO 구현시 가능하면 using문을 사용하는 것이 실수를 예방하기에 좋은 편이다.



using문/try-catch 패턴 예시


using System;
using System.IO;

class FileRead
{
    var imgFilePath = @".\img.jpg";
    using(var imgFileStream = new FileStream(imgFilePath, FileMode.Open))
    {
        try
        {
            var tmpImg = Image.FromStream(imgFileStream);
            // ... 중략
        }
        catch(Exception ex)
        {
            // ... 예외 처리
        }
    }
}


 위의 패턴으로만 보았을 때 using문-try-catch 패턴이 깔끔해보인다. 하지만, 처음 using문에서 사용할 객체를 만드는 순간에 발생하는 예외의 경우 처리가 안되는 패턴이기 때문에 이를 피하기 위해서 파일 존재 여부 검사와 같은 추가 코드가 필요하다.



참고자료

도서: C# 7과 닷넷 코어 2.0


반응형

'C#' 카테고리의 다른 글

C# .NET: Dispose (소멸자) 패턴  (0) 2018.05.14
C# .NET : SQLLocalDB (1) 개요와 오류사례  (0) 2018.03.23
C# : this와 base  (0) 2018.02.20
C# Winform : Detect Resize (크기 변화 감지)  (0) 2018.02.07
C# : Queue VS ConcurrentQueue  (0) 2018.02.02
반응형

C# .NET: Dispose (소멸자) 패턴

알게된 배경

 클래스가 프로그래밍 언어에서 등장할 때 가장 먼저 배우는 것이 생성자이다. 이후 생성자의 반대 개념인 소멸자(혹은 파괴자, 제거자 등 여러 이름으로 부를 수 있는 개념)개념이다.

 이는 기존 C++ 같은 언어에서 힙(요즘에는 이 힙을 자유공간(Free Space)라고 부르자고 제안되고 있음)에 할당된 메모리를 관리(할당과 해제)를하기 위해서였다.


 그러나 시간이 지나고 나서 GC(Garbage Collection)가 등장하고 나서 부터는 소멸자의 존재 의미가 애매 해졌다. C# DotNet Core 에서부터는 소멸자가 사라지게 되었지만, 기존의 DotNet FrameWork의 경우 아직도 소멸자가 건재하다.


 DotNet FrameWork에서는 메모리 관리 방법이 Management와 Unmanagement로 2가지 방법이 있다. Management는 말그대로 CLR이 관리를 해준다(이는 GC의 동작도 포함). 반면, Unmanagement는 CLR이 관여를 하지 않는다. 이렇게 Unmanagement한 영역은 Stream을 사용하는 부분과 소켓 프로그래밍을 위한 버퍼 등이 포함된다. 이러한 영역은 개발자가 직접 해제하는 코드를 작성해줘야 하는데, 이럴때 사용되는 것이 소멸자였다.


 그러나 DotNet FrameWork에서는 Dispose라는 인터페이스를 제공을 해주고 있는데, 이 인터페이스는 GC가 해당 Class의 인스턴스를 제거할 때 해당 작업을 먼저 하도록 되어 있다. 문제는 앞에서 언급된 Dispose 인터페이스와 소멸자의 역할이 유사하다 보니 오작동 될 가능성이 있다. 때문에 이러한 패턴이 생겨나게 되었다.


 Dispose 메서드는 IDisposeable 인터페이스를 상속 받아서 구현하면, using() 으로 호출하면 해당 스코프(혹은 스택)을 벗어나면, 호출이 된다.



패턴 예제



using System.Net.Sockets;

class UdpListenClass : IDisposeable
{
    private UdpClient Listener { get; private set; }
    // .. 중략
    // Start Destroyer Pattern
    private bool _disposed = false;
    private void Dispose(bool calledDestroyer)
    {
        if(false == _disposed)
        {
            // 소켓을 닫거나 Unmamagement 메모리를 여기서 해제하면 된다
            Listener.Close();
            _disposed = true;
        }
        if(false == calledDestroyer)
        {
            GC.SuppressFinalize(this);
        }
    }
    public void Dispose()
    {
        Dispose(false);
    }
    ~UdpSocket()
    {
        Dispose(true);
    }
    // End Destroyer Pattern
    // ... 중략
}


 혹시나 오해가 될까 추가로 적자면, 소멸자는 ~[클래스명]으로 선언 정의 된다. Dispose는 소멸자가 아니지만, using문 패턴에서 호출되는 인터패이스다. 그리고 최근에 나오고 있는 DotNet Core에서는 소멸자를 더 이상 사용하지 않는다.


 물론 DotNet FrameWork랑 독립적으로 업데이트가 되고 있지만, 새로운 기능을 2017년 8월부터 DotNet Core에 먼저 추가가 되고 이후에 DotNet FrameWork에 추가 할 것이라고 MS에서 발표를 했으니 참조만 하자


참고 자료

도서: 시작하세요. C# 6.0

도서: C# 7과 닷넷 코어 2.0

반응형

'C#' 카테고리의 다른 글

C# : using문의 기능과 활용  (0) 2018.05.15
C# .NET : SQLLocalDB (1) 개요와 오류사례  (0) 2018.03.23
C# : this와 base  (0) 2018.02.20
C# Winform : Detect Resize (크기 변화 감지)  (0) 2018.02.07
C# : Queue VS ConcurrentQueue  (0) 2018.02.02
반응형

라즈비안 : Pi Camera v2 에러 메시지


배경

 라즈베리 파이에서 공식적으로 제공하는 카메라의 경우 라즈비안의 raspistill 등 공개 표준 프로그램과 라이브러리가 함께 제공된다. 공식적으로는 Python 라이브러리만 제공되지만, C 라이브러리도 존재한다.


 라즈베리 파이와 같이 PC에 비해서 저사양의 불안정한 기기에서는 종종 소프트웨어에서 문제인지 아니면, 하드웨어에서 문제인지 판단해야 한다. 사실 오류(혹은 에러) 메시지를 잘 확인 하기만 해도 대부분의 문제의 원인을 알 수 있다. 따라서 필자가 경험한 오류 메시지에 대해서 사례로 기록을 한다.


 여기서 언급한 에러 메시지는 raspistill을 실행하다가 반환되는 에러 메시지를 적어 놓았다.



카메라 인식 문제

 에러 메시지1

mmal: Cannot read camera info, keeping the defaults for OV5647
mmal: mmal vc component create: failed to create component 'vc_ril.camera'(1:ENOMEM)
mmal: mmal component create core: could not create component 'vc.ril.camera'(1)
mmal: Failed to create camera component
mmal: main: Failed to create camera component
mmal: Camera is not detected. Please check carefully the camera module is installed correctly


 이 경우는 인식 아예 못하는 경우다.

 에러 메시지에서 확인할 수 있듯이 운영체제에서는 카메라에 접근하기 위해서 단계적으로 카메라에 관련되 드라이버와 컴포넌트를 생성을 하는데 이 모든 단계가 실패를 하게 된다. 대부분 이 경우 선이 연결이 잘못되었다던가 선이 문제가 있는 경우이다.


 아주 드문 경우로 만약 정상적으로 선으로 연결이 되었음에도 이 메시지가 뜬다면, 카메라 하드웨어 모듈내의 회선이나 끊어진 경우일 수 있다.



카메라 회신 문제(고장)

에러 메시지2

mmal: No data received from sensor. Check all connections, including the Sunny one on the camera board



 이 경우는 약 5초간 응답이 없다가 보게 되는 에러 메시지이다.

 이 경우는 하드웨어적으로 연결은 잘 된 상태이다. 그리고 드라이버도 잘 인식이 된 것이다. 하지만, 이미지 데이터를 요청을 해도 그 요청에 대해서 응답이 없는 상태이다. 이 경우 대부분 카메라 하드웨어 모듈에 문제(고장)이 있는 상태이다.


 아주 드물게 카메라 하드웨어 모듈의 점퍼 부분이 느슨하게 연결이 되거나 선이 연결이 되었지만, 느슨하게 연결되서 생길 수도 있지만, 말 그대로 아주 드물다.



참조자료

겸험과 오류 메시지 내용

반응형
반응형

C# .NET : SQLLocalDB

배경

 인터넷 네트워크는 아니지만, 응용프로그램에서 내부적인 DB를 저장하고 사용해야 하는 경우가 종종 있다. 이 경우 DB의 비중은 비교적 낮기 때문에 DBMS의 크기가 작으며, 많은 기능이 필요로 하지않는다.

 최근 임베이디드 환경의 프로젝트를 진행을 하고 있다. 임베이디드 같이 근거리 통신에 디바이스간 통신에 대해서 다룰 경우에는 이 처럼 작은 DBMS가 더 적합하다.



SQLLocalDB 설치 방법

 간단하게 말하면 SQL Server를 설치하면 된다.


 MS SQL Server 2008 부터 express 버전에서부터 사용할 수 있게 해 놓았다(혹시 필자가 잘못 알고 있다면, 알려 주기 바람). 이 글을 기록하는 시기에서는 MS SQL Server 2017 Express 버전을 설치하면, 무료로 사용이 가능하다.


 다만, 설치가 옵션으로 설치되는 것이라서 설치과정중 구성요소에서 설치 체크를 해야한다.


 SQL Server 2017이 리눅스와 OS X 등에서 크로스 플랫폼으로 사용이 가능하다고 홍보를 하고 있지만, exe 파일로 제공되니 차후 재 빌드 파일이 나오지 않는다면, DOTNET Core로 실행해야 할 가능성이 있다.


 설치가 정상적으로 이루어지면, Visual Studio 2017 등 에서 바로 사용이 가능하다.



SQLLocalDB의 호환성

 당연한 이야기이지만, SQL Server와 달리 닷넷 환경에서 사용이 가능하고 현재로써는 Windows 에 국한이 되어 있다. 그리고 우리가 모르는 사이에 Windows에서 이미 SQLLocalDB를 사용하고 있다. 때문에 배포 환경이 Window 10 이라면, 웬만하면 따로 프로그램을 설치 하지 않아도 사용이 가능하다.


 여기 까지는 MS사의 설명이지만, 실제로는 배포하는 프로그램의 경우 가능 하면 사용을 자제하는 것이 좋다. 이유는 배포 환경에 대해서 완전한 독립성을 보장을 해주지 않는다. 이는 다시 말하자면, 어떤 PC에서는 정상적으로 DB인스턴스가 생성되어 사용이 가능하지만, 어떤 PC에서는 적절한 DB인스턴스가 생성되지 않는다.


 이를 해결하기 위해서는 MS Sql Server 를 설치해야 하는데, 배포하는 프로그램에서 갑자기 Sql Server를 설치하라고 하는 것은 다소 무리가 있다. 연습용으로만 사용하자.



필수도구

 쉽게 관리하기 위한 툴로 SSMS(SQL Server Management Studio)을 사용할 수 있다. 초기 시작할때 "(LocalDB)\MSSQLLocalDB" 로 접속을 하면, PC에 있는 mdf 확장자를 가진 데이터베이스를 확인할 수 있다. 수정에는 제약이 있지만(아마도 Express 버전이라서), 대부분의 내용을 확인이 가능하다.


 "sqllocaldb.exe"라고 하는 유틸리티가 대부분 기본적으로 Windows에 설치되어 있다. 못 믿겠다면, 당장 powershell 이나 cmd 에서 명령어를 사용하면, 도움말 정보를 띄우는 것을 확인 할 수 있다. 이 유틸리티를 통해서 응용프로그램에서 사용하는 mdf 파일에 접근하는데 필요한 디비 인스턴스를 관리 할 수 있다.



오류 사례

다른 PC에서 Access 거부

 개발 PC가 아닌 다른 PC에서 mdf 파일에 접근이 안되는 경우가 있다. 필자도 이 문제로 많은 시간을 버렸다. 일단 필자의 경우 sqllocaldb.exe 를 통해서 확인 해본 결과 실행이 안되는 PC에서 생성된 "MSSQLLocalDB" 인스턴스 버전은 v13.0 으로 생성된것에 반해 개발 PC에서는 v14.0 으로 생성이 되었었다. 그리고 개발 PC에서만 실행이 되었다.


 하지만, 자택에 있는 다른 PC에서 같은 소스코드로 mdf 만 새로 생성해서 다시 개발환경이 아닌 PC에서 실행을 해본 결과 실행이 잘되었다(역시 sqllocaldb.exe 로 "MSSQLLocalDB" 인스턴스 정보를 확인해보니 v13.01.. 으로 생성된 것을 확인 할 수 있었다).


(상단이 초기 개발 PC, 하단이 자택 PC)



 필자의 기억에 의하면, 먼저 잘 안된 PC 환경에서는 Visual Studio 2017 installer 에서 sql server 관련 패키지를 설치한 것 외에는 차이가 없었다. 앞으로는 개선이 되겠지만, 배포를 위해서라도 sql 관련 패키지를 visual studio 2017 installer 로 설치하는 것은 자제하자.



Git에서 mdf파일의 커밋제외

 그리고 git 을 사용할 경우 gitignore 파일 목록중에 mdf 확장자는 커밋에서 제외가 되니 이부분을 주의 해야한다. visual studio의 솔루션 창에는 보이지만 실제로는 존재하지 않는 파일이 되어 버린다.



참고자료




반응형

'C#' 카테고리의 다른 글

C# : using문의 기능과 활용  (0) 2018.05.15
C# .NET: Dispose (소멸자) 패턴  (0) 2018.05.14
C# : this와 base  (0) 2018.02.20
C# Winform : Detect Resize (크기 변화 감지)  (0) 2018.02.07
C# : Queue VS ConcurrentQueue  (0) 2018.02.02
반응형

Xamarin.Form : 빠른 시작(첫 앱 만들기)

작성 배경

 Xamarin.Form가 나온지 시간이 괘 지났음에도 한글문서가 많지 않은 것같다. 사실 튜토리얼에 있는 영문문서가 읽기에 어려운 편은 아니다. 그러나 단의 영어로 되어 있다는 점때문에 진입장벽이 생기는 것 같아서 틈틈히 정리를 한다. 물론 정리하다가 중단하게 될 수 도 있으리라...


 본문의 내용은 Xamarin.form 공식 웹페이지의 Getting Started -> Hello, Xamarin.Form -> part 1: Quickstart를 약간의 번역과 의역을 첨가하여 작성한다.



Xamain.Forms 빠른 시작


프로젝트 만들기

새프로젝트 항목 중 Visual C# 하위 목록중에서 Cross-platform를 선택을 하면 우측 괄호에 Xamarin.Form이 보이는 항목을 클릭한다.


프로젝트 이름은 "Phoneword" 로 한다.


실제 여러개의 프로젝트가 하나의 앱을 만듣는 것이기 때문에 가급적 솔루션용 폴더를 체크하도록 한다.


확인 버튼을 클릭을 하면, 크로스플래폼 앱에 대해서 세부 설정을 할 수 있다. "Blank App"을 선택하고 .NET Standard를 선택을 하고 프로젝트를 생성한다.


(먼저 Androd 프로젝트의 MainActivity.cs 파일을 열어보고 에러 메시지가 뜨는지 확인을 해야한다. Xamarin.Form이 메이저 버전이 업데이트 되면서 일부 호환이 되지 않아서 에러가 나는 경우가 있는데, 이때는 미련없이 재설치를 하자.)


여러개의 프로젝트중에서 Phoneword 프로젝트내의 MainPage.xaml을 열고 다음과 같이 수정한다.


<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                   xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                   x:Class="Phoneword.MainPage">
    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness">
            <On Platform="iOS" Value="20, 40, 20, 20" />
            <On Platform="Android, WinPhone, Windows" Value="20" />
        </OnPlatform>
    </ContentPage.Padding>
    <StackLayout>
      <Label Text="Enter a Phoneword:" />
      <Entry x:Name="phoneNumberText" Text="1-855-XAMARIN" />
      <Button x:Name="translateButon" Text="Translate" Clicked="OnTranslate" />
      <Button x:Name="callButton" Text="Call" IsEnabled="false" Clicked="OnCall" />
    </StackLayout>
</ContentPage>


변경이 끝나면, 저장(Ctrl + s)을 한다.


이제 솔루션 탐색기(Solution Explorer)의 (+)버튼을 누르면 같은 이름의 MainPage.xaml.cs 파일이 보인다(혹은 소스코드 열기로 확인할 수 있음). 해당 파일을 수정한다.


 단, 주의 해야 할점이 에러가 발생하는 코드가 있는데, 이는 진행하면서 해결하니 조급해하지 말자.


MainPage.xaml.cs 입력받은 숫자문자를 숫자로 변환을 해주는 기능이다.


using System;
using Xamarin.Forms;

namespace Phoneword
{
    public partial class MainPage : ContentPage
    {
        string translatedNumber;

        public MainPage ()
        {
            InitializeComponent ();
        }

        void OnTranslate (object sender, EventArgs e)
        {
            translatedNumber = Core.PhonewordTranslator.ToNumber (phoneNumberText.Text);
            if (!string.IsNullOrWhiteSpace (translatedNumber)) {
                callButton.IsEnabled = true;
                callButton.Text = "Call " + translatedNumber;
            } else {
                callButton.IsEnabled = false;
                callButton.Text = "Call";
            }
        }

        async void OnCall (object sender, EventArgs e)
        {
            if (await this.DisplayAlert (
                    "Dial a Number",
                    "Would you like to call " + translatedNumber + "?",
                    "Yes",
                    "No")) {
                var dialer = DependencyService.Get<IDialer> ();
                if (dialer != null)
                    dialer.Dial (translatedNumber);
            }
        }
    }
}


에러 메시지에 겁먹지 말고 Ctrl + s 를 클릭하여 변경사항을 저장한다.


 같은 솔루션 탐색기에서 같은 프로젝트의 App.xaml 하위에 있는 App.xaml.cs를 열고 아래와 같이 수정을 한다.


using Xamarin.Forms;
using Xamarin.Forms.Xaml;

[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace Phoneword
{
    public partial class App : Application
    {
        public App()
        {
            InitializeComponent();
            MainPage = new MainPage();
        }

        protected override void OnStart()
        {
            // Handle when your app starts
        }

        protected override void OnSleep()
        {
            // Handle when your app sleeps
        }

        protected override void OnResume()
        {
            // Handle when your app resumes
        }
    }
}


역시 변경사항을 저장한다.


솔루션 탐색기에서 Phoneword 프로젝트에서 마우스 우클릭을 하여 추가(Add)에서 새항목(New Item)을 선택한다.


 Visual C#의 code 항목에서 클래스(Class)를 선택하고 이름을 "PhoneTranslator"로 작성하고 추가(Add)한다.


생성된 PhoneTranslator.cs를 다음과 같이 수정한다.

(이때 주의 할 점이 namespace 이름을 바꾼다는 것을 간과 하지 말자)


using System.Text;

namespace Core
{
    public static class PhonewordTranslator
    {
        public static string ToNumber(string raw)
        {
            if (string.IsNullOrWhiteSpace(raw))
                return null;

            raw = raw.ToUpperInvariant();

            var newNumber = new StringBuilder();
            foreach (var c in raw)
            {
                if (" -0123456789".Contains(c))
                    newNumber.Append(c);
                else
                {
                    var result = TranslateToNumber(c);
                    if (result != null)
                        newNumber.Append(result);
                    // Bad character?
                    else
                        return null;
                }
            }
            return newNumber.ToString();
        }

        static bool Contains(this string keyString, char c)
        {
            return keyString.IndexOf(c) >= 0;
        }

        static readonly string[] digits = {
            "ABC", "DEF", "GHI", "JKL", "MNO", "PQRS", "TUV", "WXYZ"
        };

        static int? TranslateToNumber(char c)
        {
            for (int i = 0; i < digits.Length; i++)
            {
                if (digits[i].Contains(c))
                    return 2 + i;
            }
            return null;
        }
    }
}


작성이 완료되었으면, 역시 저장한다.


Phoneword 프로젝트에 이번에는 Interface를 추가한다. 방법은 방금전과 마찬가지로 솔루션 탐색기에서 Phoneword 프로젝트에서 "우클릭 >> 추가 >> 새 항목" Visual C#의 code 인터페이스(interface)를 선택한뒤 IDialer로 이름을 적고 생성한다.


코드는 다음과 같이 수정한다.


namespace Phoneword
{
    public interface IDialer
    {
        bool Dial(string number);
    }
}


이 코드는 나중에 다른 프로젝트들에서 상속한다. 역시 저장을 한다.



이제, Dialer를 상속하자. 솔루션 탐색기에서 Phoneword.iOS 프로젝트에 우클릭을 한뒤에 "추가"에서 "새 항목"을 선택한다.


 새 항목 화면에서 Apple의 하위의 code를 선택하고, 클래스(Class)를 선택한다. 이때 클래스가 여러개 보이는데, 처음 보이는 것을 선택하면 된다.


 클래스 명은 "PhoneDialer"로 하여 생성한다.


코드는 다음과 같이 수정한다.


using Foundation;
using Phoneword.iOS;
using UIKit;
using Xamarin.Forms;

[assembly: Dependency(typeof(PhoneDialer))]
namespace Phoneword.iOS
{
    public class PhoneDialer : IDialer
    {
        public bool Dial(string number)
        {
            return UIApplication.SharedApplication.OpenUrl (
                new NSUrl ("tel:" + number));
        }
    }
}


변경이 완료되었으면, 저장을 한다.


이번에는 안드로이드 쪽이다. Phoneword.Android 프로젝트에 우클릭 하고 추가의 새 항목을 선택한다.


추가 창에서 visual C#하위의 Android를 선택한뒤 클래스(Class)를 선택하여 이름을 아까와 동일한 PhoneDialer로 작성하고 생성한다.


아래와 같이 코드를 수정한다(이때 임포트 되는 모듈이 많으므로 빼먹지 않도록 주의한다).


using Android.Content;
using Android.Telephony;
using Phoneword.Droid;
using System.Linq;
using Xamarin.Forms;
using Uri = Android.Net.Uri;

[assembly: Dependency(typeof(PhoneDialer))]
namespace Phoneword.Droid
{
    public class PhoneDialer : IDialer
    {
        public bool Dial(string number)
        {
            var context = MainActivity.Instance;
            if (context == null)
                return false;

            var intent = new Intent (Intent.ActionCall);
            intent.SetData (Uri.Parse ("tel:" + number));

            if (IsIntentAvailable (context, intent)) {
                context.StartActivity (intent);
                return true;
            }

            return false;
        }

        public static bool IsIntentAvailable(Context context, Intent intent)
        {
            var packageManager = context.PackageManager;

            var list = packageManager.QueryIntentServices (intent, 0)
                .Union (packageManager.QueryIntentActivities (intent, 0));

            if (list.Any ())
                return true;

            var manager = TelephonyManager.FromContext (context);
            return manager.PhoneType != PhoneType.None;
        }
    }
}


변경이 완료되었으면 저장을 한다.



솔루션 탐색기에서 Phoneword.Android 프로젝트를 더블 클릭하여 MainActivity.cs 파일을 열어서 다음과 같이 수정한다(원문은 더블클릭하라고 했지만, 해당되는 이름의 파일을 클릭해도 된다).


using Android.App;
using Android.Content.PM;
using Android.OS;

namespace Phoneword.Droid
{
    [Activity(Label = "Phoneword", Icon = "@drawable/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        internal static MainActivity Instance { get; private set; }

        protected override void OnCreate(Bundle bundle)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(bundle);

            Instance = this;
            global::Xamarin.Forms.Forms.Init(this, bundle);
            LoadApplication(new App());
        }
    }
}


작성이 완료되면 저장을 한다.


Phoneword.Android 프로젝트를 우클릭하여 속성을 선택한다. 속성에서 "Android 매니페스트"(Android Manifest)항목을 선택하고 하단의 Required permissions: 항목중 "CALL_PHONE"항목을 체크한다.


역시 변경되었으면 저장한다.



이제 UWP를 수정하자.

솔루션 탐색기에서 Phoneword.UWP 프로젝트에 우클릭을 한뒤 추가(Add)에 새 항목(New Item)을 선택한다. 선택화면에서는 Visual C# 에서 Code를 선택하여 클래스(Class)를 선택한다. 이름은 PhoneDialer로 작성하여 생성한다.


생성된 클래스는 다음과 같이 작성한다(아직 어셈블리를 추가하지 않았기 때문에 에러가 보이는 경우도 있으나 조급할 필요가 없다).


using Phoneword.UWP;
using System;
using System.Threading.Tasks;
using Windows.ApplicationModel.Calls;
using Windows.UI.Popups;
using Xamarin.Forms;

[assembly: Dependency(typeof(PhoneDialer))]
namespace Phoneword.UWP
{
    public class PhoneDialer : IDialer
    {
        bool dialled = false;

        public bool Dial(string number)
        {
            DialNumber(number);
            return dialled;
        }

        async Task DialNumber(string number)
        {
            var phoneLine = await GetDefaultPhoneLineAsync();
            if (phoneLine != null)
            {
                phoneLine.Dial(number, number);
                dialled = true;
            }
            else
            {
                var dialog = new MessageDialog("No line found to place the call");
                await dialog.ShowAsync();
                dialled = false;
            }
        }

        async Task<PhoneLine> GetDefaultPhoneLineAsync()
        {
            var phoneCallStore = await PhoneCallManager.RequestStoreAsync();
            var lineId = await phoneCallStore.GetDefaultLineAsync();
            return await PhoneLine.FromIdAsync(lineId);
        }
    }
}


변경이 완료되었으면 저장한다.


 프로젝트 탐색기에서 참조(Reference)를 우클릭한뒤 "참조 추가"(Add Reference)를 클릭한다. 

좌특의 Universal Windows의 하위 확장(Extensions)을 클릭한다.


 항목들중 Windows Mobile Extensions for the UWP 모듈 최신(작성시점 10.0.162..)을 체크하고, 확인 버튼을 클릭한다.

(종종 체크가 이미 되어 있는 경우가 있는데 이 때는 변경할 것이 없다.)



Phoneword.UWP 프로젝트 하위의 "Package.appxmanifest" 항목을 연다.

상위 탭버튼 중에 "기능"(Capabilities)를 선택한뒤에 좌측 기능중 "전화 통화"(Phone Call)를 체크 하고 저장한다.



이제 빌드할 시간이다. 먼저 지금까지 작성한 코드중에 에러가 있는지 확인을 해본다. 종종 IDailer를 찾지 못한다는 에러가 보일 수 있는데 이 경우 Phoneword 프로젝트를 빌드를 해주면 된다. Phoneword 프로젝트에 우클릭하여 빌드를 클릭하여 빌드하자.


Phoneword 빌드가 끝나면, 솔루션 탐색기에서 Phoneword.UWP 프로젝트를 우클릭을 한뒤 "시작프로젝트로 설정"(Set as StartUp Project)를 클릭한다. 이제 상단의 녹색화살표가 있는 "로컬 컴퓨터"(Local Machine)를 클릭한다(혹은 단축키 F5).


기다리면, UWP로 빌드된 프로그램이 실행된것을 확인할 수 있다.



참조자료

자바린 공식 페이지(영문)



반응형
반응형

C++11 :  범위기반 for 문

배경

 이미 C++11에서부터 존재했지만, 신경을 안쓰면 잘 모르는 for문의 기능이다. 이미 다른 언어들은 기존부터 foreach라는 키워드로 제공했던 기능이다.


 for문에서 직접 범위기반으로 반복하는 것이 가능 해짐으로써 STL 컨터이너 사용에 좀 더 편리 해졌다.



사용방법

#include <iostream>
#include <vector>

int main(const int argc, const char * argv[])
{
    std::vector<int> vec{ 1, 2, 3, 4};
    // 기존 반복자 사용방법
    auto itVec = vec.begin();
    for(; itVec != vec.end(); ++itVec)
    {
        std::cout << (*itVec) << std::endl;
    }

    // 범위 for문
    for(auto valVec : vec)
    {
        std::cout << valVec << std::endl;
    }
    return 0;
}


 첫 for문은 반복자를 이용한 원소 출력인 반면, 두번째 for문은 범위를 이용해서 원소를 확인하는 방법이다. auto 키워드는 C++11에서 가장 먼저 사용하게 되기때문에 자세한 설명은 생략한다.(auto 링크)



주요 특징

 1. 기존 반복자를 사용하지 않고 시퀀스(메모리가 연결된 자료형)형 컨테이너의 원소를 확인을 할 수 있다.
 2. 읽는 원소는 시퀀스 내부 원소의 형으로 변환된다.

 3. 범위 for문을 사용하여 읽는 시퀀스내부의 원소 값을 수정할 수 없다.



참조자료

MSDN 범위기반 for문




반응형
반응형

C++11 : STL std::array 정적배열

배경

 C++11에서 등장한 array는 기존의 정적 배열(이제는 C Style array 라고 불림)을 개선하기 위해서 등장을 했다. C/C++ 외의 다른 프로그래밍 언어를 조금 훝어보면 알 수 있다.


 JAVA, JS, C# 등에서의 배열을 선언/정의 할때 부가 정보(Length 등)가 같이 있는 것에 비해서 C++11이전의 C Style static array은 단순히 연속된 메모리 공간의 앞부분을 가르키는 포인터에 불과 했었다. 이로 포인터를 사용할 경우 잘못된 메모리 참조 또는 이를 개선하기 위한 추가 코딩으로 인한 생산성 저하로 이어지는 문제가 있다.


 그래서 C++11이전에서는 std::vector를 사용하기도 했다. 그러나 C Style Array에 비해서 성능 저하의 부담이 크며, 오버플로워가 일어나면, 동적으로 용량을 늘려 버리는 바람에 정해진 크기를 버퍼로 사용하기에는 잠재적인 문제가 있을 수 있다(물론 좋은 프로그래머는 이런 문제를 예방을 할 것이다).


 std::array는 vector보다 경량이며, 기존의 정적 배열의 성질을 가졌으며, 반복자등 일부 STL 컨테이너를 사용할 수 있게 만들어졌다.



선언 및 초기화 방법

 array표준헤더파일을 포함해야 사용할 수 있다. 타입과 배열크기를 입력하고 기존 컨테이너처럼 선언하고 사용할 수 있다. <>에 첫인자는 자료형(type), 두번째 인자는 크기를 입력해야 한다.

#include <array>

int main(const int argc, const char * argv[])
{
    std::array<int, 5> arr1 = {1, 2, 3};
    std::array<int, 5> arr2(1, 2, 3);
    std::array<int, 5> arr3{1, 2, 3};

    return 0;
}


주요 특징

 정의된 array의 남는 원소에는 0으로 초기화가 된다.

 array도 vector와 같이 객체명(혹은 변수명) 뒤에 대괄호로 배열처럼 사용할 수 있다.

 vector처럼 반복자를 사용할 수 있다.


예시

// arrayEx.cpp
#include <array>
#include <iostream>

int main(cosnt int argc, const char * argv[])
{
    std::array<int, 5> arr1{1, 2, 3};
    // 네번째 int에 접근하여 값을 대입함
    arr1[3] = 4;
    // 반복자처럼 사용 가능
    auto itArr = arr1.begin();
    for(; itArr != arr1.end(); ++itArr)
    {
        std::cout << (*itArr) << std::endl;
    }
    // 출력결과 arr1[4](5번째)는 0으로 초기화 되어 있음
    return 0;
}


 size() 함수로 배열의 크기를 알 수 있다. 단, capacity()가 없다. 이는 vector와 가장 큰 차이 중 하나다. 이는 array경우 size()와 capacity()가 따로 있을 필요가 없기 때문이다.

 array로 선언된 객체명은 array라는 컨테이너의 포인터이기이다. 이 때문에 기존의 정적 배열의 첫번째 포인터가 필요할 경우 vector와 같이 data()를 호출해서 사용해야 한다. 이는 오래된 라이브러리를 호환해야 한다던가 버퍼로 사용하기에 적절하다.

 fill()로 특정 원소값으로 모든 원소를 초기화 할 수 있다.


이 밖의 자세한 특징은 cpprefernce 문서를 참조하면 된다.



참조자료

cppReference 문서




반응형
반응형

CMake 익히기 : 기초문법 (2)

작성 배경

 CMake의 가장 큰 장점은 C/C++ 크로스 플랫폼에 빌드를 할 수 있다. 그러나 임베이디드의 대부분을 차지하는 리눅스 환경의 경우 적당한 빌드 시스템이 CMake만한게 없다는 것도 함정이다. 또한 다른 사람이 만들어 놓은 것을 사용하기는 쉽지만, 막상 규모가 조금 되는 프로그램을 만들려고 하면, 쉽지 않다. 이 때문에 간단하게나마 문법을 조금씩 정리한다.



CMake 기초문법


end가 붙은 키워드의 arg

 end키워드가 붙은 경우(endif, endfunction, endmacro, while, 등)에 앞의 arg를 동일하게 적어 줘서 여러 키워드가 중첩이 될 경우 구분할 수 있는 역할을 할 수 있다. 하지만 붙이지 않아도 된다.



매크로(macro)

 CMake에서 매크로는 문법상 함수와 동일하다. 다만, 동작 방식에서 차이가 있다. 함수의 경우 메모리에 동적으로 생성되어 작동하는 반면 매크로는 C의 매크로처럼 호출 부분에 작성되어 있는 절차를 삽입하는 방식이다.

macro(hello_macro)
    message("hello world")
endmacro(hello_macro)

hello_macro()



조건문(if)

 조건문 if()도 마지막에는 endif()로 끝이 나야 한다. if()조건 외 실행할 경우 else()가 사용된다. if()외에 다른 조건을 정의 할때는 elseif()를 사용한다.


 조건문이 참이 되는 경우는 ON, YES, Y, TRUE, 또는 0이 아닌 값이다. 반면 거짓이 되는 경우는 0, OFF, NO, FALSE, N, IGNORE, ""이다.


 또한 조건문에서 함께 사용되는 비교, 논리 연산자의 경우 기호가 아닌 대문자 키워드로 표현을 한다. 비교 연산자 : EQUAL, LESS, GREATER

논리 연산자 : AND, OR, NOT

기타 매칭 :  MATCHES [REGEX]

set(VAL 1)

if(${VAL})
    message("${VAL} is TRUE")
else()
    message("${VAL} is FALSE")
endif()



include()

 확장자가 cmake로 작성된 다른 cmake 파일을 포함시킬 수가 있다. C/C++의 include와 유사하다. 다만, 표준라이브러리가 없어서 로컬에서 포함을 해야 한다. 대부분 오픈소스에는 cmake라는 폴더에 .cmake 파일을 모아 놓는다.

 include하게 되면, include된 파일에 있는 매크로나 함수, 변수를 사용할 수 있다.




연습예제

 앞에서 간단하게 살펴본 내용으로 간단하게 실습 예제를 작성해보자. 스크립트를 2개로 만들어서 실행해보자

# cmake폴더의 macro.cmake 파일
macro(check_val_macro val)
    if(${val})
        message("${val} is true")
    else()
        message("${val} is false")
    endif()
endmacro(check_val_macro val)
# 작업 폴더의 CMakeLists.txt 파일
cmake_minimum_required(VERSION 2.6)

include(cmake/macro.cmake)

set(FOO 1)
check_val_macro(FOO)

set(FOO FALSE)
check_val_macro(FOO)


결과는 다음과 같이 나온다





참고자료

OpenCV 소스코드의 CMake스크립트

Mastering CMake A Cross-Platform Build System 서적

반응형

'각종 툴' 카테고리의 다른 글

[PyCharm]기본 키 맵 해제  (0) 2020.12.20
CMake 익히기 : 기초문법 (1)  (0) 2018.02.19
CMake : 공식예제 step1 해보기  (0) 2017.12.11

+ Recent posts