[2012.02.27] make: del: Command not found 하드웨어 프로그래밍

얼마전에 설치한 Yagarto 와 Eclipse 환경에서
make clean 을 했을 때 다음과 같은 에러가 나왔다.

del obj\*.o
make: del: Command not found
make: *** [clean] Error 127

그래서 make 파일을 열어서 clean 을 찾아보았다.

clean:
 del obj\*.o
 del $(BUILDNAME).elf

흠...맞는데...왜 안되는 것이지...;;

언제나 그렇듯이 구글님을 찾았다.
역시나...

http://stackoverflow.com/questions/2463037/calling-windows-commands-e-g-del-from-a-gnu-makefile

다음과 같이 하니 에러가 사라졌다.

clean:
 cmd /C del obj\\\*.o
 cmd /C del $(BUILDNAME).elf

obj\\\*.o 은 \가 escape 문자로 처리된다고 해서 수정한 것이다.
실행해보니 잘 지워진다.

그런데 cmd 는 인식하면서 왜 del 은 인식하지 못할까...Path 가 안 잡혀있나...
명령어가 존재하지 않나...
그래서 검색해보았다.
...
없다. 허허...그래서 그랬구나.
del.exe 는 cmd.exe 의 Builtin command (빌트인 커맨드, 내장 명령어) 였던 것이다.

여튼 해결.

[2012.02.24] Yagarto 설치하기. 하드웨어 프로그래밍

GNU ARM Toolchain 을 알아보는 중에 Yagarto (Yet another GNU ARM toolchain : 아직도 또 다른 GNU ARM toochain 이야 라는 느낌 http://www.yagarto.de/) 라는 것을 발견하여 성급한 설치를 시도하였다.
물론 '성급한' 설치였던 만큼 이틀의 시간을 수업료로 지불하고 박살났다.
역시 공개용 툴은 쉽게 설치되는 것이 없다.

여튼 이번에는 성급함을 버리고 천천히 살펴보기로 했다.

그리고 발견하였다...이름하여 하우투(How-to http://www.yagarto.de/howto.html)

How to?

Some tutorials about installation and use of the software can be found at the "how to" page.


슬프다...왜 내 눈에는 다운로드만 보였을까...ㅜㅜ

"How to" 페이지로 가보면

  1. GDB Server for J-Link EDU
  2. YAGARTO, native GNU ARM toolchain for windows
  3. YAGARTO and Eclipse

라고 나온다.

그럼 순서대로 진행해볼까나...


1. J-Link EDU 용 GDB 서버 구축 (http://www.yagarto.de/howto/gdbserver/index.html)
Introduction 을 지나가면

Download and install

For our GDB Server we need the following components here:

  1. J-Link "Software and documention pack"
  2. YAGARTO Tools (like make, sh, rm, cp and mkdir)

라고 나온다. 앞의 1. GDB Server for J-Link EDU 에 부속된 것이니 각각을 1-1, 1-2 라고 하자.

1-1. J-Link "Software and documention pack"
http://www.segger.com/cms/jlink-software.html 에서 최신버전을 받아 설치한다.(제품 시리얼 번호가 필요함) 

1-2. YAGARTO Tools
http://www.yagarto.de/index.html#download 의 Download  쪽에 있는 YAGARTO Tools 를 다운로드 받는다.
2012.02.24. 현재 최신버전은 http://www.yagarto.de/download/yagarto/yagarto-tools-20100703-setup.exe 이다.

음...그런데 내 컴퓨터에는 Embarcadero Technologies, Inc. Delphi 가 깔려 있어서 이놈의 make 가 먼저 반응한다. ㅜㅜ
Command Prompt 창에서 make --version 이라고 치면 다음과 같이 나온다. ㅠㅠ

C:\Documents and Settings\hwdev>make --version
MAKE Version 5.4  Copyright (c) 1987, 2010 Embarcadero Technologies, Inc.
Incorrect command line argument: --version

Syntax: MAKE [options ...] target[s]
    -B                Builds all targets regardless of dependency dates
    -Dsymbol[=string] Defines symbol [equal to string]
    -Idirectory       Names an include directory
    -K                Keeps (does not erase) temporary files created by MAKE
    -N                Increases MAKE's compatibility with NMAKE
    -Wfilename        Writes MAKE to filename updating all non-string options
    -Usymbol          Undefine symbol
    -ffilename        Uses filename as the MAKEFILE
    -a                Performs auto-dependency checks for include files
    -c                Caches auto-dependency information
    -e                Ignores redefinition of environment variable macros
    -i                Ignores errors returned by commands
    -l+               Enables use of long command lines
    -m                Displays the date and time stamp of each file
    -n                Prints commands but does not do them
    -p                Displays all macro definitions and implicit rules
    -q                Returns zero if target is up-to-date and nonzero
                      if it is not (for use in batch files)
    -r                Ignores rules and macros defined in BUILTINS.MAK
    -s                Silent, does not print commands before doing them
    -? or -h          Prints this message
      Options marked with '+' are on by default. To turn off a default
      option follow it by a '-', for example: -a-

찾아보니 C:\Program Files\Embarcadero\RAD Studio\9.0\bin\make.exe 가 반응한 것이다.

PATH 를 변경하면 다음과 같이 나온다.

C:\Documents and Settings\hwdev>make -version
GNU Make 3.81
Copyright (C) 2006  Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.

This program built for i686-pc-mingw32

PATH 순서를 바꾸면 해결되지만 그러면 또 Delphi 의 동작이 이상해질 수 있으므로 이 방법은 쓰지 않을 것이다..
어차피 이클립스에서 개발하지 커맨드 프롬프트 창에서 개발할 것도 아니므로 PATH를 수정하는 대신 이클립스의 설정을 변경하여 이 문제를 해결한다.


2. Native GNU ARM toolchain for windows(http://www.yagarto.de/howto/yagarto1/index.html)
http://www.yagarto.de/index.html#download 의 Download  쪽에 있는 YAGARTO GNU ARM toolchain 을 다운로드 받는다.
2012.02.24. 현재 최신버전은 http://www.yagarto.de/download/yagarto/yagarto-bu-2.21_gcc-4.6.2-c-c++_nl-1.19.0_gdb-7.3.1_eabi_20111119.html 이다.
근데 링크를 따라가보면 정작 소스포지(http://sourceforge.net)의 YAGARTO 프로젝트의 Files (http://sourceforge.net/projects/yagarto/files/YAGARTO%20for%20Windows/20111119/) 로 연결된다.


3. YAGARTO 와 Eclipse(http://www.yagarto.de/howto/yagarto2/index.html)
페이지에 다음과 필요한 것이 무엇인지 나와있다.

What do you need?

To use Eclipse as an Integrated Development Environment for your toolchain you need:

  • a Java Runtime Environment (JRE) by Sun,
  • Eclipse Platform Runtime Binary,
  • and the Eclipse C/C++ Development Tooling - CDT.

3-1. Java Runtime Environment (JRE)
남궁성님의 자바 설치하기를 참고하여 설치한다.

3-2. Eclipse Platform Runtime Binary 
문서는 Eclipse Classic 3.6.2 를 기준으로 작성되어 있으나 뭐...그냥 최신 버전으로 설치해도 된다.
문제는 CDT 플러그 인 설치 때 버전 차이로 오는 부분을 극복해야 한다는 것이다.

3-3. Eclipse C/C++ Development Tooling - CDT
3-2 를 최신버전으로 했을 때 버전 차이로 오는 문제를 극복해야 한다...

음...3-2 와 3-3 은 Eclipse IDE for C/C++ Developers (includes Incubating components) 를 설치하여 해결할 수도 있다.
http://www.eclipse.org/cdt/downloads.php 에 가면 다음과 같은 글이 있다.

The CDT can either be installed as part of the Eclipse C/C++ IDE packaged zip file or installed into an existing Eclipse using the "Install New Software..." dialog and entering the p2 repository URLs listed below.


"as part of the Eclipse C/C++ IDE packaged zip file" 에 주목하자.

이전에 Java 공부한다고 설치한 Eclipse IDE for Java Developers 가 있어서 CDT만 추가로 설치하기도 했는데...
버전 차이로 인해 조금 고생을 하기는 했다.
CDT Main Features는 모두 설치해야 한다.
TCF 는 뭐가 뭔지 몰라서 설치하지는 않았다.
문제는 CDT Optional Features 인데...아래 그림을 참고하여 설치하기 바란다.

여튼 이클립스를 용도별(JAVA용, 안드로이드용, CDT 등등)로 따로 사용할 경우에는 그냥 Eclipse IDE for C/C++ Developers (includes Incubating components) 를 설치하는 것이 맘 편하다.

이제 남은 것은 Yagarto 용 이클립스 설정인데 설명하기 좀 그러니 http://www.yagarto.de/howto/yagarto2/index.html 에 가서 찬찬히 읽어보고 따라해보기 바란다.

내 경우 1-2. YAGARTO Tools 에서 이야기 했듯이 Embarcadero Technologies, Inc. Delphi 가 깔려 있어서 이놈의 make 가 먼저 반응하는데 별도의 조치를 취하지 않으면 이건 이클립스에서도 동일하다.
Project 메뉴의 Build 나 Clean 등의 make 를 사용하는 메뉴를 실행하면 제대로 동작하지 않는다.
Yagarto 의 Make 가 반응하지 않는 것이다.

PATH 순서를 바꾸면 해결되지만 그러면 또 Delphi 의 동작이 이상해질 수 있으므로 이 방법은 쓰지 않을 것이다..
어차피 이클립스에서 개발하지 커맨드 프롬프트 창에서 개발할 것도 아니므로 PATH를 수정하는 대신 이클립스의 설정을 변경하여 이 문제를 해결한다.

Project - Properties 클릭한다.

C/C++ Build - Builder Settings 에서 Build command 를 아래 그림과 같이 수정한다.
그럼 적어도 이클립스 상에서는 Embarcadero Technologies, Inc. Delphi 의 make(내 경우에는 C:\Program Files\Embarcadero\RAD Studio\9.0\bin\make.exe)가 먼저 반응하는 일은 없다.

[2012.01.27] 델파이 프로젝트 조건부 컴파일(Conditional compilation) 델파이(Delphi)

Project - Options - Directories/Conditionals - Conditionals - Conditional defines

이전부터 델파이에서 조건부 컴파일(Conditional compilation)을 사용하면서 각 소스마다 {$define TESTMODE} 를 추가하고 삭제하고 하는 것이 번거로웠고 어떻게 프로젝트 전반에 걸쳐 한번에 처리할 방법이 없을까 하고 고민한 적이 있었다.
C 언어에서는 헤더 파일 하나 #include 하고 그 헤더 파일을 수정하면 되었었는데 델파이는 그런 방법이 안되는 것이었다.

이전에 델파이에 그런 것은 없다는 소리를 들어서 그냥저냥 번거로워도 일일이 각 소스마다 $define 부분을 수정해왔었다.

그러다 오늘 문득 이렇게 쓰기 불편한 것이 이상하다는 생각이 들었고 만약 조건부 컴파일 기능이 IDE 에 있다면 어느 구석에 둘까 하는 생각을 해보았다.
답은 Project Options 메뉴...들어가서 보니 Conditionals 가 눈에 띄었다.
그래서 얼씨구나 하고 Conditional defines 항목에 TESTMODE 라고 입력하고 모든 소스의 {$define TESTMODE} 들을 주석처리하고 F9 를 눌렀다.
...안된다.
그래서 F1 키를 누르고 도움말을 읽어보고 도움말 링크를 따라다니고 하면서
-TESTMODE, $TESTMODE, -$TESTMODE, DTESTMODE, -DTESTMODE 등등을 입력하면서 실행해보았지만...역시...

방황의 시간을 거쳐 결국 구글신에 도달하였다. 역시 구글님은 전지하시다.

Delphi Conditional Define 라는 검색어로 검색하니 답이 나왔다.

Project Options 에서 조건부 컴파일 항목을 수정한 후에는 Build All Projects 를 해야 한다.
만약 소스가 수정되었다면 그냥 F9 를 눌러도 적용되지만 소스가 수정되지 않은 경우에는 다시 Build 를 해야 적용이 되는 것이다.
즉, 앞의 경우 Conditional defines 항목에 TESTMODE 로 설정하고 Build All Projects 한 후 Run 하면 적용되는 것이다.


[2012.01.11] Indy10 TIdTCPClient 객체에서 연결 끊어짐 처리. 델파이(Delphi)

Indy9 과 Indy10 의 차이인지 델파이7 과 델파이 XE2(델파이2009 부터...) 의 차이인지 모르겠지만...

연결 끊어짐을 확인하는데 보통 CheckForGracefullyDisconnect, CheckForDisconnect 과 CheckForDataOnSource 을 사용했었다.

Indy9에서 쓰던 방식을 Indy10 에서 수정해서 다음과 같이 사용했었다.

try
    FConnectionToView.IOHandler.CheckForDisconnect(True, True);
    FConnectionToView.IOHandler.CheckForDataOnSource(100);
except
end;
try
    FConnectionToView.Host := ConnectionInfo^.Host;
    FConnectionToView.Port := ConnectionInfo^.Port;
    if (Not FConnectionToView.Connected) then FConnectionToView.Connect;

    // 이런저런 패킷 던지기
except
    // FConnectionToView.Connect 에 대한 예외 처리.
end;

그런데 서버가 켜져있는 상태에서 클라이언트가 접속하여 이런저런 데이터를 주고 받은 후, 서버를 강제 종료시켰다가 다시 켠 뒤 클라이언트에서 위의 동작을 하면 안되는 것이었다.
여튼 아래와 같이 해서 해결하기는 했는데 좀 찜찜하다.

try
    if FConnectionToView.IOHandler <> nil then
    begin
        FConnectionToView.IOHandler.CheckForDisconnect(True, True);
        FConnectionToView.IOHandler.CheckForDataOnSource(100);
    end;
except
    FConnectionToView.IOHandler.Free;    // 핵심은 바로 여기
end;
try
    FConnectionToView.Host := ConnectionInfo^.Host;
    FConnectionToView.Port := ConnectionInfo^.Port;
    if (Not FConnectionToView.Connected) then FConnectionToView.Connect;

    // 이런저런 패킷 던지기
except
    // FConnectionToView.Connect 에 대한 예외 처리.
end;

핵심은 FConnectionToView.IOHandler.Free; 이다.
메모리 누수가 생길까 걱정스러워 프로젝트 소스에

ReportMemoryLeaksOnShutdown := True;

를 추가하고 실행해봤는데 문제가 없었다.


[2011.12.20] Windows COMMTIMEOUTS structure 델파이(Delphi)

최근에 델파이로 필테라호출기 제어 프로그램을 수정한 적이 있다.
그런데 동작이 이상해서 한참 이런저런 삽질하다 시간이 모잘라서 그냥 내보냈다.
아무래도 맘에 걸리고 결국에는 다시 수정작업을 하게 될 듯 하여 자료를 찾아봤다.

원본은 아래의 링크를 참고한다.
http://msdn.microsoft.com/en-us/library/windows/desktop/aa363190(v=vs.85).aspx


멤버는 아래와 같다.

ReadIntervalTimeout
ReadTotalTimeoutMultiplier
ReadTotalTimeoutConstant
WriteTotalTimeoutMultiplier
WriteTotalTimeoutConstant

내 경우 호출기로 송신한 후 호출기가 응답하는 패킷을 읽어야 하는데, 분명 호출기가 있는데 응답 패킷을 읽지 못하고 그냥 나오는 것이었다.

다음과 같은 원인때문일 것으로 추측하는데
첫 번째는 호출기로 송신한 패킷을 호출기가 수신하지 못한 경우이고
두 번째는 호출기가 응답하는 패킷을 수신하지 못한 경우이다.

통신선과 장치에 문제가 없다면 둘다 타임아웃 설정과 관련이 있다.
첫 번째는 WriteTotalTimeoutMultiplier 와 WriteTotalTimeoutConstant 설정 때문이고
두 번째는 ReadIntervalTimeout, ReadTotalTimeoutMultiplier, ReadTotalTimeoutConstant 설정 때문이다.

우선 이야기를 더 진행하기 전에 각 Timeout 들의 내용을 살펴본다.

----------------------------------------------------------------------------------------------------

ReadIntervalTimeout

The maximum time allowed to elapse between the arrival of two bytes on the communications line, in milliseconds. During a ReadFile operation, the time period begins when the first byte is received. If the interval between the arrival of any two bytes exceeds this amount, the ReadFile operation is completed and any buffered data is returned. A value of zero indicates that interval time-outs are not used.

A value of MAXDWORD, combined with zero values for both the ReadTotalTimeoutConstant and ReadTotalTimeoutMultiplier members, specifies that the read operation is to return immediately with the bytes that have already been received, even if no bytes have been received.

읽기간격타임아웃
도착하는 2 바이트 사이의 최대 시간 간격을 1000분의1초 단위로 설정할 수 있게 한다. ReadFile 동작시, 첫 번째 바이트가 수신되면서 타임아웃 간격이 시작된다. 만약 1 바이트가 수신된 후 지정한 시간이 지나도 다음 1 바이트가 수신되지 않으면 ReadFile 동작은 완료되고 버퍼에 저장된 값이 반환된다. 0 값으로 설정하면 읽기 간격 타임아웃을 사용하지 않는다는 것을 나타낸다.
ReadTotalTimeoutMultiplier 과 ReadTotalTimeoutConstant 을 0으로 설정했을 때, ReadIntervalTimeout를 MAXDWORD 값 (2^32-1 = 4294967295) 으로 설정하면 읽기 동작은 수신한 데이터가 있으면 수신한 데이터를 가지고, 수신한 데이터가 없다면 없는대로 곧바로 리턴한다.

ReadTotalTimeoutMultiplier

The multiplier used to calculate the total time-out period for read operations, in milliseconds. For each read operation, this value is multiplied by the requested number of bytes to be read.

읽기전체타임아웃곱하기계수
읽기동작의 전체 타임아웃 간격을 계산하는데 사용하는 곱하기계수로 단위는 1000분의 1초 이다. 각각의 읽기 동작시, 읽기 요청된 바이트 수에 곱해진다.

ReadTotalTimeoutConstant

A constant used to calculate the total time-out period for read operations, in milliseconds. For each read operation, this value is added to the product of the ReadTotalTimeoutMultiplier member and the requested number of bytes.

A value of zero for both the ReadTotalTimeoutMultiplier and ReadTotalTimeoutConstant members indicates that total time-outs are not used for read operations.

읽기전체타임아웃상수
전체 타임아웃 간격을 계산하는데 사용되는 상수로 단위는 1000분의 1초이다. 각각의 읽기 동작시, 이 값은 읽기 요청된 바이트 수와 ReadTotalTimeoutMultiplier의 곱에 더해진다.
ReadTotalTimeoutMultiplier와 ReadTotalTimeoutConstant을 0으로 설정하면, 읽기 동작시 전체 타임아웃을 사용하지 않는다.

Remarks

If an application sets ReadIntervalTimeout and ReadTotalTimeoutMultiplier to MAXDWORD and sets ReadTotalTimeoutConstant to a value greater than zero and less than MAXDWORD, one of the following occurs when the ReadFile function is called:

  • If there are any bytes in the input buffer, ReadFile returns immediately with the bytes in the buffer.
  • If there are no bytes in the input buffer, ReadFile waits until a byte arrives and then returns immediately.
  • If no bytes arrive within the time specified by ReadTotalTimeoutConstant, ReadFile times out.

주의
애플리케이션에서 ReadIntervalTimeout과 ReadTotalTimeoutMultiplier를  MAXDWORD 값 (2^32-1 = 4294967295) 으로 설정하고 ReadTotalTimeoutConstant 값을 0보다 크고 MAXDWORD 값보다 작게 ( 0 < ReadTotalTimeoutConstant < MAXDWORD ) 설정하면 ReadFile 함수가 호출되었을 때 다음의 경우들 중 하나가 된다.

  • 만약 입력 버퍼에 수신 데이터가 있으면, ReadFile은 즉시 입력 버퍼의 데이터를 반환한다.
  • 만약 입력 버퍼에 수신 데이터가 없으면, ReadFile은 1바이트 수신할 때까지 기다렸다가 수신되면 즉시 입력 버퍼의 데이터를 반환한다.
  • 만약 ReadTotalTimeoutConstant로 지정한 시간 안에 데이터가 수신되지 않으면, ReadFile 는 타임아웃 된다.


끝.
---------------------------------------------------------------------------------------------------------------------

각 타임아웃 값들의 의미를 몰라 많이 헷갈렸는데 현재 대충 정리한 것은 이렇다.
ReadIntervalTimeout 은 하나의 데이터가 수신된 후 다음 데이터가 수신될 때까지의 타임아웃이다.
ReadTotalTimeoutConstant 과 ReadTotalTimeoutMultiplier 는 Read 동작 전체에 걸쳐 적용되는 타임아웃.
그리고 2개의 타임아웃은 각각 동작. ReadIntervalTimeout 이든 ReadTotalTimeout 이든 두 타임아웃 조건 중 어느 것이라도 만족한다면 타임아웃이 발생.
대략 9600 bps 라면 1 바이트 수신하는데 소모되는 시간은 1ms 정도인데 여유를 봐서 10 ms 정도가 최대값이라고 생각되면 ReadIntervalTimeout은 10ms 로 설정.
그리고 호출기가 최대 50 ms 이내에 응답한다고 한다면 ReadTotalTimeoutConstant 은 50 ms 로 설정.
ReadTotalTimeoutMultiplier 는 1 ms ~ 10 ms 사이의 값으로 설정. 

만약 4바이트를 읽기 한다면...타임아웃이 발생하는 경우는

ReadTotalTimeoutConstant +  ReadTotalTimeoutMultiplier * 4 밀리초 동안 4바이트가 수신되지 못했을 경우 수신된 데이터까지 반환하고 타임아웃.

ReadTotalTimeoutConstant +  ReadTotalTimeoutMultiplier * 4 밀리초 전에
첫 번째 바이트가 수신된 후 ReadIntervalTimeout 동안 두 번째 바이트가 수신되지 않으면 수신된 데이터까지 반환하고 타임아웃.
(이 경우 반환되는 값은 첫 번째 바이트 까지일 것이다.)

ReadTotalTimeoutConstant +  ReadTotalTimeoutMultiplier * 4 밀리초 전에
두 번째 바이트가 수신된 후 ReadIntervalTimeout 동안 세 번째 바이트가 수신되지 않으면 수신된 데이터까지 반환하고 타임아웃.
(이 경우 반환되는 값은 두 번째 바이트 까지일 것이다.)

ReadTotalTimeoutConstant +  ReadTotalTimeoutMultiplier * 4 밀리초 전에
세 번째 바이트가 수신된 후 ReadIntervalTimeout 동안 네 번째 바이트가 수신되지 않으면 수신된 데이터까지 반환하고 타임아웃.
(이 경우 반환되는 값은 세 번째 바이트 까지일 것이다.)

이런 식일 것이다.

아! 타임아웃과 직접 관련 있는 내용은 아니지만 추가한다.
간혹 내 애플리케이션에서 뭔가 하느라고 그 사이 수신된 데이터를 받지 못하면 어쩌지 하는 경우가 있는데...
드라이버가 알아서 하드웨어 버퍼에서 열심히 애플리케이션에서 설정한 입력버퍼로 퍼 나르므로 그리 걱정할 필요가 없다.
버퍼만 넉넉하게 잡으면...그런 일은 없겠지만 만약 그 퍼나르는 속도보다 빠르게 들어온다면?
음...우선 Read / Write 동작은 오버헤드가 있다...는 것을 기억하기 바란다.
1 Byte 단위로 읽기 / 쓰기를 하는 것보다는 Byte Block 단위로 읽기 / 쓰기를 하는 것이 부하가 적다는 것이다.
프로토콜과 애플리케이션 구조를 이런 생각을 바탕으로 작성한다.
...뭔 짓을 해도 소용없다면?
.
.
.
...하드웨어의 한계...업그레이드 뿐...(먼산)




2011.12.21.수요일.
필테라 호출기 제어 프로그램의 문제를 알아냈다. 좀 어이없었는데...
읽기 함수를 호출할 때 사용하는 버퍼의 형(Type)이 문제였다.

ReadBuffer: array of Byte;

는 괜찮은데

ReadBuffer: array of Integer;

는 문제를 일으킨다.
...원래 알고 있었던 문제라서 내가 작성한 경우에는 전부 Byte 배열로 선언하고 있었다.
다른 개발자가 만든 프로그램을 수정한 것이라서 함수들 속에 이런 부분들이 좀 있다.
그런데 어떤 함수에서는 Byte 로 되어 있는데 어떤 함수들에서는 Integer 로 되어 있다. 이거 뭔가...;;
...여튼 수정하고 나니 잘 된다.
단 ReadTotalConstant 를 25ms 로 했더니 좀 놓치는 것이 있어서 고민하다가 50 ms 로 바꾸고 나니 놓치는 응답이 없었다.
반응성도 나쁘지 않아서 굳이 25ms 에서 50ms 사이의 값을 시행착오로 최적화 시킬 필요가 없다는 생각이 들어 빌드하고 릴리즈 한 뒤 봉인했다.


[2011.12.13] 이벤트 구동 방식의 프로그래밍 - 이벤트 연결 테스트 델파이(Delphi)

정리는 아직 못하고 테스트만 했다. 뭐라고 설명해야할지 잘 몰라서이다. ;;


program Project1;

uses
    FastMM4, FastMM4Messages, SyncObjs,
    Forms,
    uMain in 'uMain.pas' {fmMain},
    uTest1 in 'uTest1.pas';

{$R *.res}

begin
    //RegisterExpectedMemoryLeak(TCriticalSection ,1);  // TCriticalSection 이 SyncObjs 안에 있음.

    Application.Initialize;
    Application.CreateForm(TfmMain, fmMain);
    Application.Run;
end.



unit uMain;

interface

uses
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
    Dialogs, StdCtrls, uTest1;

type
    TfmMain = class(TForm)
        Memo1: TMemo;
        Button1: TButton;
        Button2: TButton;
        Button3: TButton;
        Button4: TButton;
        procedure FormCreate(Sender: TObject);
        procedure Button1Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    private
        { Private declarations }
        FTest1List: array of TTest1;
        procedure DoTest(Sender: TObject);
        procedure DoTestButtonClick(Sender: TObject);
    public
        { Public declarations }
    end;

var
    fmMain: TfmMain;

implementation

{$R *.dfm}

procedure TfmMain.FormCreate(Sender: TObject);
var
    iTmp1: Integer;
    iLoop1: Integer;
begin
    Left := 0;
    Top := 0;

    iTmp1 := 3;
    SetLength(FTest1List, iTmp1);
    for iLoop1 := 0 to iTmp1 - 1 do
    begin
        FTest1List[iLoop1] := TTest1.Create;
        FTest1List[iLoop1].TestIndex := iLoop1 + 1;
        FTest1List[iLoop1].OnTest := DoTest;
    end;
end;

procedure TfmMain.FormDestroy(Sender: TObject);
var
  iTmp1: Integer;
  iLoop1: Integer;
begin
  for iLoop1 := Length(FTest1List) - 1 downto 0 do
  begin
    FTest1List[iLoop1].Free;
    FTest1List[iLoop1] := nil;
  end;
end;

procedure TfmMain.Button1Click(Sender: TObject);
begin
    DoTestButtonClick(Sender);
end;

procedure TfmMain.DoTestButtonClick(Sender: TObject);
begin
    try
        if Assigned(FTest1List[TButton(Sender).tag]) then
        begin
            FTest1List[TButton(Sender).tag].Test;
        end;
    except
        on E: Exception do
        begin
            Memo1.Lines.Add('TfmMain.DoTestButtonClick Exception ' + E.Message);
        end;
    end;
end;

procedure TfmMain.DoTest(Sender: TObject);
begin
    if (Sender is TTest1) then
    begin
        Memo1.Lines.Add('TTest1, TestIndex is ' + IntToStr(TTest1(Sender).TestIndex));
    end;
end;

end.



unit uTest1;

interface

uses
    SysUtils, Classes, Dialogs
    ;

type
    TTest1 = class
    private
        FOnTest: TNotifyEvent;
    public
        TestIndex: Integer;
        procedure Test;
        property OnTest: TNotifyEvent read FOnTest write FOnTest;
    end;

implementation

{ TTest1 }

procedure TTest1.Test;
begin
    ShowMessage('TTest1, TestIndex is ' + IntToStr(TestIndex));

    if Assigned(FOnTest) then
    begin
        FOnTest(Self);
    end;
end;

end.


TTest1 객체를 여러 개 생성한 후 TTest1 의 테스트 이벤트 OnTest 에 이 TTest1 의 객체를 생성하고 사용하는 fmMain 의 DoTest 프로시져를 연결한 경우 어떤 TTest1 의 객체에서라도 OnTest 이벤트가 발생하면 DoTest 프로시져가 실행되는 것이다.
보면 알겠지만 TTest1 의 이벤트와 fmMain의 프로시져를 연결한 것이라 TTest1 이 fmMain을 직접 알고 있지 않아도, TTest1 이 있는 pas 파일의 Uses 에 uMain 이 없어도 된다는 것이다. 이래야 나중에 fmMain 이 바뀌거나 할 때도 TTest1 의 소스는 수정하지 않을 수 있다. 특히나 fmMain 에 TMemo 객체 하나 올려놓고 로그를 찍는 경우라면 더 말할 나위 없을 것이다. TMemo 객체 이름이 바뀌거나 대상이 바뀌거나 하면 이곳저곳 수정하기 바쁜데 수정할 곳을 fmMain 안에 모아둘 수 있다.

...이걸 어디에 써먹으려고 한 것이가하면...Indy 의 OnConnect 와 OnDisconnect 이벤트를 이벤트 방식으로 처리하기 위해서 시험한 것이다.


[2011.12.06] 근황?

어제부터 묘하다 했다.
12월 5일 오전에 전체회의가 일정에 잡혀있었다.
나는 모르고 안 나간 상태.
그러니...

아침에 불려갔다.
인사를 하란다...과장인데 그러면 쓰냐고...그런데 누구한테 해야하지...
...목소리 크게 하고 소리치라는 이야긴가...
힘들다.

그리고는 연차를 낸 이유를 알려달라고 한다.
...뭔가 해명하는 분위기였다. 허허...
장모님 대장 내시경 검사, 남은 하나는 가족 회의라고 이야기했다.
전자는 진짜고 뒤는 둘러댄 것이지만...

여튼 슬슬 힘들다. 군대처럼 하고 싶은가본데...
해병대 출신 프로그래머를 뽑으라고 말하고 나는 나가고 싶다.


Access restriction: Class is not accessible due to restriction on required library 자바(Java)

자바로 시리얼 통신을 하려고 하였다.
http://java.sun.com/products/javacomm
를 이용하면 된다고 하는데 속도가 느리다고 한다. 이보다 속도가 빠른 것이 있으니..
http://www.jcontrol.org/download/readme_rxtx_en.html

여튼 그래서 RXTX 를 설치하였다.

처음에는 에디트 플러스에서 하려고 했는데 import 해야할 패키지가 많아서 그냥 이클립스에서 Java 프로젝트를 만들고 소스를 추가하였다.

그런데 다음과 같은 에러를 만나게되었다.

Access restriction: Class is not accessible due to restriction on required library

뭔가...이건...여튼 검색해보니 해결책이 있었다.

  • Windows -> Preferences -> Java -> Compiler -> Errors/Warnings
  • (Project) Properties -> Java Compiler -> Errors/Warnings
두 경로 중 한곳에서, Deprecated and restricted API 항목을 열고,
"Forbidden reference (access rules)" 에서 Error로 선택된것을 Ingnore Warning 으로 변경해주면 된다.

두 번째 경로에서 변경할 경우 잘 안되는 경우도 있다고 한다.

여튼 이건 해당 모듈이 완벽한 호환을 보장하지 않을 경우이므로 되도록이면 다른 모듈로 대체하는 것이 좋다고 한다.

음...Sun 에서 제공하는 것을 해야 하나...그런데 이전에 읽은 글에서는 MS 가 뭔가 시리얼 통신 쪽을 요상하게 바꿔서 지원하기 힘들다고 했다고도 하고 지원을 중단한다고도 했다고 하는데...그럼 어쩌라고...ㅜㅜ

여튼 끝.


Java 주석 속에 Unicode escape 가 있을 때 에러 발생. 자바(Java)

아래의 소스를 컴파일 하면 다음과 같은 에러 내용을 만나게 된다.

SpecialChar.java:19: error: illegal unicode escape
유니코드(16진수)문자 \u유니코드 (예: char a='\u0041';) 
               ^
1 error

에러가 발생한 원인은 에러 메시지대로 \u 때문인지 \u 를 지우면 에러가 없어진다.
그러나 정확한 원인과 결과의 인과관계...이유는 모르겠다.
JDK 1.7 에서 발견했고 Java의 정석의 저자 남궁성님에 따르면 JDK 1.6 에서도 발생한다고 한다.

http://cafe.naver.com/ArticleRead.nhn?clubid=10286641&page=1&menuid=17&boardtype=L&articleid=63341&referrerAllArticles=false


/-*
Date  : 2011.11.23.
Source  : SpecialChar.java

!! 주석 작성시 주의점
문자열을 의미하는 큰따옴표(") 안에 주석이 있을 때는 주석이 아닌 문자열로 인식된다.

특수문자
tab      \t
backspace    \b
form feed    \f
new line    \n
carriage return   \r
역슬래쉬(\)    \\
작은따옴표    \'
큰따옴표    \"
유니코드(16진수)문자 \u유니코드 (예: char a='\u0041';) 
*-

class  SpecialChar
{
 public static void main(String[] args)
 {
  char single = '\'';    // single = '''; 와 같이 할 수 없다.
  String dblQuote = "\"Hello\""; // 큰 따옴표를 출력하려면 이렇게 한다.
  String root = "C:\\";

  System.out.println(single);
  System.out.println(dblQuote);
  System.out.println(root);

  
  { // !---테스트하기 위해 개인적으로 추가한 블럭---!
  System.out.println();

  System.out.println((int)single);
  
  }
 }
/- 실행 결과
'
"Hello"
C:\

39
*-
}


Java 를 위한 EditPlus 사용자 도구 구성 자바(Java)

JDK 를 C:\jdk1.7\ 에 설치했다.
 

마지막의 Package Run 은 약간의 설명이 필요할 듯 하다.
인수를 보면 $(CurSel).$(FileNameNoExt) 라고 되어 있는 것을 볼 수 있다.
Java 의 Package Run 은 Java.exe 패키지명.클래스명 이다.
Java 는 소스 파일 이름이 클래스명과 동일하니까 클래스명 자리에는 $(FileNameNoExt) 매크로를 사용하였다.
그런데 EditPlus 에서 자동으로 패키지명을 넣을 수 있는 인수 매크로가 존재하지 않았다.
$(Prompt) 매크로를사용하면 사용자가 인수를 입력할 수 있도록 대화상자를 표시하도록 하고 사용자가 직접 입력할 수 있지만...
불편하다는 생각이 들었다. 그래서 차선으로 생각한 것이...$(CurSel) 매크로였다.
$(CurSel) 매크로는 선택한 텍스트를 삽입한다.
따라서 패키지명을 선택한 후 Package Run 을 하면 $(CurSel).$(FileNameNoExt) 은 패키지명.클래스명 으로 대체될 것이다.
결과적으로 Java.exe 패키지명.클래스명 으로 실행되는 것이다. 
 
예를 들어 다음과 같은 자바 코드가 있다면

package test.hello;

class  HelloWorld
{
    public static void main(String[] args) 
    {
        System.out.println("Hello World!");
    }
}

javac.exe -d . HelloWorld 로 컴파일 후 java.exe test.hello.HelloWorld 로 실행하면 된다.
test.hello 를 선택하고 Package Run  을 실행하면 인수 $(CurSel).$(FileNameNoExt) 은 test.hello.HelloWorld 로 대체될 것이다.
결국 java.exe test.hello.HelloWorld 로 실행하게 된다.

2011.11.26.
Run 과 Package Run 의 인수 설정 뒤에 공백 + $(Prompt) 를 추가하면
실행시 인수(args)를 묻는 창이 나온다.

음...이런저런 테스트를 위해 나는 인수를 입력할 수 있도록 $(Prompt)를 추가하여 사용중이다.

나중에 귀찮으면 안쓰겠지. 이제 막 Java 공부 시작하였으니 할 것은 다 해봐야지.
...어차피 결국 이클립스를 쓰게 될 듯 하지만...


1 2 3 4 5