가상함수에 대한 말씀도 드렸고, 앞으로 통신 라이브러리를 어떻게 구성할 지에 대한 말씀도 올렸습니다. 그래서 이번에는 제가 평소에 사용하고 있는 rs232c 통신 라이브러리를 올려 드리면서 설명을 아울러 올립니다. 동영상으로 모든 내용을 담으려 했습니다만 시간이 너무 길어 둘로 나누었습니다.
이번 시간에는 라이브러리를 다운 받으실 수 있도록 준비하고 설명은 예제 파일에 대해서만 올립니다. 그러나 예제 파일을 먼저 보시면 rs232c 통신 라이브러리에 대한 전체적인 이해를 하실 수 있고, 또한 라이브러리에 대해 먼저 설명을 드리면 자칫 재미도 없고 도통 복잡하고 시끄럽게만 보여 드릴 것 같은 생각에서 입니다.
이번에 올려 드리는 파일은 rs232c 통신 뿐이지만 설계 자체가 tcp/ip, udp/ip, uds 모두 포함할 수 있도록 디자인 되었기 때문에 이 라이브러리를 이해 하시면 앞으로 추가되는 부분을 쉽게 이해하실 수 있습니다.
sample.c
파일을 내려 받으시면 여러 개의 파일이 나옵니다만 sample.c 를 동영상과 함께 이해를 하시면 그다지 어려운 내용이 아니라고 생각됩니다. "니가 만들었으니까 그렇지!!" 라고 하시면 드릴 말씀은 없지만( ^^ ) 다음 시간에 나머지 파일을 아울러 자세히 올리 도록 하겠습니다.
sample.c 를 보시면 main() 함수 밑에 rs232_t 에 대한 포인터 변수가 선언되어 있습니다.
int main( int argc, char **argv) { rs232_t *rs232;
강좌에도 객체라는 말을 사용했습니다만, C++의 OOP와 같은 객체는 못되지만 rs232c 통신을 위한 모든 준비가 되어 있는 단위라 객체라는 말을 사용했습니다. 이점 양해를 부탁드립니다.
rs232_t는 struct, 즉 레코드일 뿐이지만 필요 변수 외에도 처리에 필요한 함수를 포인터로 가지고 있습니다. 내용을 볼까요? rs232.h 에 있습니다.
typedef struct rs232_t_ rs232_t; struct rs232_t_ { int poll_ndx; int fd; int type; // tcp/udp/uds/serial/ int tag; void (*on_poll_in )( rs232_t *); void (*on_poll_out)( rs232_t *); void (*on_poll_err)( rs232_t *); void (*on_poll_hup)( rs232_t *); char devname[20]; // 장치명을 담을 버퍼 int baud; int databit; int stopbit; int parity; void (*on_read )( rs232_t *); void (*on_writable)( rs232_t *); void (*on_error )( rs232_t *, int); };
아직까지는 자세한 설명을 드리지 못해 이해가 어려우시겠지만 rs232 통신을 위한 필요 변수가 준비되어 있고, poll 을 운영하기 위한 가상함수가 준비되어 있습니다.
프로그램 소스를 보도록 하겠습니다. include 문을 제외한 알짜베기 부분만 담겠습니다.
char sample_buf[MAX_BUF_SIZE]; ////////////////////////////////////////////////////////////////////// 소켓에서 에러가 발생 void on_error( rs232_t *sender, int reopen_ok) { // 이벤트 핸들이 발생하기 전에 // 소켓을 재 오픈합니다. // 즉, rs 에 대해 file descriptor 를 다시 생성합니다. // 이후 on_errror() 이벤트가 발생하며, // reopen_ok = 0 : 다시 오픈에 성공했다면 // reopen_ok = -1 : 다시 오픈에 실패 // 이벤트 핸들러에서 복귀되면, 자동으로 // rs232_t 포인터는 소멸되며 NULL 값을 가지게 됩니다. // 잠시 후에 rs232_t * 객체를 생성하십시오. if ( 0 == reopen_ok) { printf( "232 포트에 에러가 발생했으나n"); printf( "%s 포트를 다시 열었습니다.n", sender->devname); } else { printf( "232 포트에 에러가 발생했고,n"); printf( "다시 포트 열기에 실패했습니다.n"); printf( "통신이 중단됩니다.n"); printf( "또한 sender 은 소멸되며, NULL 이되므로n"); printf( "이후로 통신 객체를 사용하시면 안됩니다!!n"); } } ////////////////////////////////////////////////////////////////////// 클라이언트로부터 자료 수신 void on_read( rs232_t *sender) { int sz_read; sz_read = rs232_read( sender, sample_buf, MAX_BUF_SIZE); if ( 0 < sz_read) // 반드시 0 < 임을 확인한다. { printf( "-> %sn", sample_buf); rs232_write( sender, sample_buf, sz_read); // 상대 PC 로 자료 전송 } else { // 읽어 들인 데이터의 크기가 0 또는 음수가되면 // on_error() 이벤트 핸들러가 호출됩니다. // on_error() 이벤트 핸들러의 reopen_ok 값이 // 음수가 되면, 이 on_read() 이벤트는 더 이상 // 발생하지 않습니다. printf( "read errorn"); } } ////////////////////////////////////////////////////////////////////// main process int main( int argc, char **argv) { rs232_t *rs232; char *text = "abcdefghijklmnopqrstuvwxyz"; printf( "****************************n"); printf( "rs232 samplen"); printf( "%sn", argv[0]); printf( "****************************n"); poll_init(); // poll 메니저를 초기화 // 서버 소켓 준비 rs232 = rs232_open( "/dev/ttyS0", 115200, 8, 'n', 1); if ( NULL == rs232) // 소켓 생성에 실패했다면 { printf( "error = %dn", neterr_no); exit(1); } rs232->on_read = on_read; rs232->on_error = on_error; // 루프 테스트 rs232_write( rs232, text, strlen( text)); // 다른 rs232 포트(rs232 용 파일)로 전송 가능 while( 1) { poll_loop(1000); } }
프로그램이 실행되면, 제일 먼저 poll 을 위한 poll_init()를 호출합니다. 제 톧신 라이브러리는 마치 Event Drivn 프로그램처럼 어떤 이벤트를 감시하고, 발생하면 해당 함수를 호출해 준다는 개념입니다. 이를 위해 poll을 운영합니다.
poll 준비가 완료되면 rs232_open() 함수를 이용하여 rs232 통신 객체 하나를 생성합니다. 그리고 하단에 폴 메니저가 통신 객체에 대해서 자료가 수신 되었는지, 혹은 에러가 발생하지 않았는지 검색하고 발생했다면 poll 이 해당 가상함수를 호출해 줍니다.
rs232 통신 객체는 poll로부터 호출될 때, 어떻게 처리할 지를 함수로 구성해서 가상함수에 지정해 줍니다.
rs232->on_read = on_read; rs232->on_error = on_error;
이제부터는 자료가 수신 되었을 때에는 on_read()함수에서 처리하시고, 자료 전송은 rs232_write( rs232, text, strlen( text));를 이용하시면 됩니다.