지난번에 1:1 소켓프로그래밍으로 채팅 프로그램을 만들어보았습니다.


참고 : http://yeolco.tistory.com/31


이번 시간에는 1:n 비동기 방식 채팅프로그램을 만들어보겠습니다.


각 클라이언트 소켓마다 쓰레드를 만들어서 통신하는 방법입니다.






실행화면


3명의 사용자가 동시에 사용했을때




잘 작동하는 화면입니다.





사용자 1명이 나가고 새로운 사용자가 서버에 접속하는 화면입니다.




◎ Server쪽 소스코드입니다.


using System;

using System.Collections.Generic;

using System.Net;

using System.Net.Sockets;

using System.Text;

using System.Threading;

using System.Windows.Forms;


namespace SIMT_CHATTING_SERVER

{

    public partial class Form1 : Form

    {

        TcpListener server = null; // 서버

        TcpClient clientSocket = null; // 소켓

        static int counter = 0; // 사용자 수

        string date; // 날짜 

        // 각 클라이언트 마다 리스트에 추가

        public Dictionary<TcpClient, string> clientList = new Dictionary<TcpClient, string>();


        public Form1()

        {

            InitializeComponent();

            // 쓰레드 생성

            Thread t = new Thread(InitSocket);

            t.IsBackground = true;

            t.Start();

        }


        private void InitSocket()

        {

            server = new TcpListener(IPAddress.Any, 9999); // 서버 접속 IP, 포트

            clientSocket = default(TcpClient); // 소켓 설정

            server.Start(); // 서버 시작

            DisplayText(">> Server Started");


            while (true)

            {

                try

                {

                    counter++; // Client 수 증가

                    clientSocket = server.AcceptTcpClient(); // client 소켓 접속 허용

                    DisplayText(">> Accept connection from client");


                    NetworkStream stream = clientSocket.GetStream();

                    byte[] buffer = new byte[1024]; // 버퍼

                    int bytes = stream.Read(buffer, 0, buffer.Length);

                    string user_name = Encoding.Unicode.GetString(buffer, 0, bytes);

                    user_name = user_name.Substring(0, user_name.IndexOf("$")); // client 사용자 명


                    clientList.Add(clientSocket, user_name); // cleint 리스트에 추가


                    SendMessageAll(user_name + " 님이 입장하셨습니다.", "", false); // 모든 client에게 메세지 전송


                    handleClient h_client = new handleClient(); // 클라이언트 추가

                    h_client.OnReceived += new handleClient.MessageDisplayHandler(OnReceived);

                    h_client.OnDisconnected += new handleClient.DisconnectedHandler(h_client_OnDisconnected);

                    h_client.startClient(clientSocket, clientList); 

                }

                catch (SocketException se) { break; }

                catch (Exception ex) { break; }

            }


            clientSocket.Close(); // client 소켓 닫기

            server.Stop(); // 서버 종료

        }


        void h_client_OnDisconnected(TcpClient clientSocket) // cleint 접속 해제 핸들러

        {

            if (clientList.ContainsKey(clientSocket))

                clientList.Remove(clientSocket);

        }


        private void OnReceived(string message, string user_name) // cleint로 부터 받은 데이터

        {

            if(message.Equals("leaveChat")) {

                string displayMessage = "leave user : " + user_name;

                DisplayText(displayMessage);

                SendMessageAll("leaveChat", user_name, true);

            }

            else {

                string displayMessage = "From client : " + user_name + " : " + message;

                DisplayText(displayMessage); // Server단에 출력

                SendMessageAll(message, user_name, true); // 모든 Client에게 전송

            }

        }


        public void SendMessageAll(string message, string user_name, bool flag)

        {

            foreach (var pair in clientList)

            {

                date = DateTime.Now.ToString("yyyy.MM.dd. HH:mm:ss"); // 현재 날짜 받기


                TcpClient client = pair.Key as TcpClient;

                NetworkStream stream = client.GetStream();

                byte[] buffer = null;


                if (flag)

                {

                    if(message.Equals("leaveChat"))

                        buffer = Encoding.Unicode.GetBytes(user_name + " 님이 대화방을 나갔습니다.");

                    else

                        buffer = Encoding.Unicode.GetBytes("[ " + date + " ] " + user_name + " : " + message);

                }

                else

                {

                    buffer = Encoding.Unicode.GetBytes(message);

                }


                stream.Write(buffer, 0, buffer.Length); // 버퍼 쓰기

                stream.Flush();

            }

        }


        private void DisplayText(string text) // Server 화면에 출력

        {

            if (textBox1.InvokeRequired)

            {

                textBox1.BeginInvoke(new MethodInvoker(delegate

                {

                    textBox1.AppendText(text + Environment.NewLine);

                }));

            }

            else

                textBox1.AppendText(text + Environment.NewLine);

        }

    }

}




◎ Client쪽 소스코드입니다.


using System;

using System.Net.Sockets;

using System.Text;

using System.Threading;

using System.Windows.Forms;


namespace Chatting

{

    public partial class Form2 : Form

    {

        TcpClient clientSocket = new TcpClient(); // 소켓

        NetworkStream stream = default(NetworkStream); 

        string message = string.Empty;


        public Form2()

        {

            InitializeComponent();

        }


        private void Form2_Load(object sender, EventArgs e)

        {

            try

            {

                clientSocket.Connect("192.168.0.31", 9999); // 접속 IP 및 포트

                stream = clientSocket.GetStream();

            }

            catch (Exception e2)

            {

                MessageBox.Show("서버가 실행중이 아닙니다.", "연결 실패!");

                Application.Exit();

            }


            message = "채팅 서버에 연결 되었습니다.";

            DisplayText(message);


            byte[] buffer = Encoding.Unicode.GetBytes(Form1.nickname + "$");

            stream.Write(buffer, 0, buffer.Length);

            stream.Flush();


            Thread t_handler = new Thread(GetMessage);

            t_handler.IsBackground = true;

            t_handler.Start();

        }


        private void button1_Click(object sender, EventArgs e) // 메세지 보내기

        {

            textBox2.Focus();

            byte[] buffer = Encoding.Unicode.GetBytes(textBox2.Text + "$");

            stream.Write(buffer, 0, buffer.Length);

            stream.Flush();

            textBox2.Text = "";

        }


        private void GetMessage() // 메세지 받기

        {

            while (true)

            {

                stream = clientSocket.GetStream();

                int BUFFERSIZE = clientSocket.ReceiveBufferSize;

                byte[] buffer = new byte[BUFFERSIZE];

                int bytes = stream.Read(buffer, 0, buffer.Length);


                string message = Encoding.Unicode.GetString(buffer, 0, bytes);

                DisplayText(message);

            }

        }


        private void DisplayText(string text) // Server에 메세지 출력

        {

            if (textBox1.InvokeRequired)

            {

                textBox1.BeginInvoke(new MethodInvoker(delegate

                {

                    textBox1.AppendText(text + Environment.NewLine);

                }));

            }

            else

                textBox1.AppendText(text + Environment.NewLine);

        }


        private void textBox2_KeyUp(object sender, KeyEventArgs e)

        {

            if (e.KeyCode == Keys.Enter) // 엔터키 눌렀을 때

                button1_Click(this, e);

        }


        private void Form2_FormClosing(object sender, FormClosingEventArgs e) // 폼 닫을 때 실행

        {

            byte[] buffer = Encoding.Unicode.GetBytes("leaveChat" + "$");

            stream.Write(buffer, 0, buffer.Length);

            stream.Flush();

            Application.ExitThread();

            Environment.Exit(0);

        }

    }

}





정보가 유익하셨다면 아래 공감버튼 눌러주시면 감사하겠습니다.

질문사항은 댓글로 달아주시면 성의껏 답변해드리겠습니다.




안녕하세요 열코입니다!

OpenCV로 영상처리 및 패턴인식을 공부하는 동안 가장 기본적인 자동차 번호판 인식 프로그램을 

간단하게 제작해 보았습니다.


* 개발 환경

개발 툴 : Visual Studio 2017

개발 언어 : C#


* 기능

- IplImage Load(불러오기) 및 Save(저장)

- Web Image Load 및 Save

- Image GrayScale(흑백)

- Image Binary(이진화)

- Image CannyEdge(에지 검출)

- Templet Match 및 ROI(관심 영역 추출)

- Mouse Drag Event로 ROI

- Tesseract-OCR(API)

- OpenALPR(API)


* 소스 코드

전체 소스코드는 300줄가량 되며, 부분 소스코드만 공개하고 설명합니다.


- Binary 함수


1
2
3
4
5
6
7
8
9
IplImage gray = new IplImage(src.Size, BitDepth.U8, 1); // GrayScale 수행
 
Cv.CvtColor(src, gray, ColorConversion.BgrToGray);
 
IplImage bina = new IplImage(src.Size, BitDepth.U8, 1);
 
int temp = trackBar1.Value * 20// track bar 값을 받아와 적절한 수치로 변환
 
Binarizer.SauvolaFast(gray, bina, temp, 0.264); // Sauvola 방법으로 이진화 수행
cs



- Tesseract 함수


1
2
3
4
5
6
7
8
9
Bitmap img = new Bitmap(pictureBoxIpl2.Image);  
 
var ocr = new TesseractEngine("./tessdata""kor", EngineMode.Default);  
 
// ./tessdata : tesseract 문자 인식 훈련 데이터, kor : 한글 or eng : 영문
 
var texts = ocr.Process(img); 
 
textBox1.AppendText(texts.GetText());
cs




- OpenALPR


1
2
3
4
5
6
7
8
9
10
11
12
13
Task<string> recognizeTask = Task.Run(() => ProcessImage(savePath));
 
recognizeTask.Wait();
 
string task_result = recognizeTask.Result;
 
string result = task_result.Split(':')[11].Split(',')[0];
 
result = result.Replace("\"""");
 
result = result.Trim();  // 결과 값을 적절히 파싱
 
textBox1.AppendText(result);
cs





* 실행 화면

프로그램을 실행하면 아래 사진과 같은 화면이 실행됩니다.



Load Image를 눌러 로컬 컴퓨터 내 이미지를 불러오거나 Capture를 눌러 연결된 카메라로 사진을 불러올 수 있습니다. 먼저 Capture를 눌러 사진을 찍어보겠습니다.




제 책상 위의 아이폰을 한번 찍어봤습니다...

캡처된 이미지를 불러오기 성공했다는 로그가 뜨고 화면에 이미지를 불러옵니다.

이 사진은 딱히 패턴인식할 사진이 아니므로 Load Image로 자동차 사진을 불러오겠습니다.



자동차 이미지를 불러왔습니다.

자동차 번호판을 인식해야 하는데 먼저 전처리 과정을 거쳐야 인식률이 많이 높아집니다.

(사진 선명도 및 노이즈를 제거하기 위해 처리하는 과정입니다.)



- GrayScale은 RGB 컬러사진은 흑백 사진으로 변환해주는 버튼입니다.

- CannyEdge는 사진의 Edge(모서리) 영역만 감지하여 나타내주는 버튼입니다.

- Init은 작업한 사진을 초기화하는 버튼입니다.

- Binary Degree는 Track Bar로 구현했습니다. 수치의 degree( 정도) 값에 의해 Binary(이진화) 처리를 해줍니다.



120 degree로 Binary 처리한 결과입니다.

번호판 부분이 선명하게 처리되었습니다.



마우스 이벤트를 이용하여 마우스 드래그를 하여 마우스 X, Y좌표를 받아온 후 좌표 크기만큼 잘라서 따로 표시해줍니다. (패턴 인식 알고리즘 속도 향상을 위해)



패턴인식 알고리즘은 2가지를 사용했습니다.

Logic 1은 Open Library인 Tesseract 알고리즘을 사용했고,

Logic 2는 OpenALPR API를 사용했습니다.

(OpenALPR은 유료 라이브러리로 2000회 무료로 사용 가능합니다.) 

두 라이브러리 모두 인식률은 상용적으로 사용할 정도로 높진 않았습니다.



하지만 전처리 과정과 알고리즘 수정을 통해 어느 정도 인식률을 높이는 것이 가능합니다.

Logic 1 버튼을 눌러 결과를 확인하니 번호판을 정확히 인식하는 모습입니다.

실행 시간은 두 로직 모두 1~3초 정도 소요합니다. (더 많은 최소화 작업이 필요)  

Templet Match는 미리 저장해 둔 Templet 사진을 통해 기존 사진과 비교하여 번호판을 찾아내는 방법입니다. 번호판 모양이 제각각이기 때문에 인식률이 많이 낮았습니다.



Logic 2(OpenALPR) 실행 결과입니다.

실행 시간은 Logic 1(Tesseract) 조금(0.5초~1초) 더 걸리는 정도였습니다.

OpenALPR 홈페이지에서 데모 프로그램 실행 시 한글 인식이 잘 되는 걸로 확인했는데

소스코드로 구현하니 한글 인식이 안되는 걸로 나옵니다... (아시는 분 댓글로 부탁드립니다)


* 수정

- OpenALPR 한글지원 가능합니다.

PostAsync에서 API주소에 country를 us에서 kr로 변경하면 한글도 인식됩니다.

한글을 unicode로 처리하기 때문에 한글이 \ud638 이런식으로 출력됩니다.

유니코드를 한글로 변경하는 알고리즘을 따로 구현하여 처리하였습니다.


유니코드 한글로 변환하기


또 다른 차량 번호판 인식 결과입니다.



다른 차량 역시 번호판을 잘 인식하는 모습입니다.

위 사진처럼 깨끗하고 깔끔하게 번호판이 나온 경우 따로 전처리 작업을 해주지 않아도 인식하는 모습입니다.


알고리즘 및 전처리 과정을 수정하여 실시간 CCTV에서 번호판 인식이 가능하고, 자율 주행 자동차에서 도로 인식 및 표지판, 신호등을 인식하는 알고리즘을 만들어 볼 수 있겠네요




질문 또는 오타나 잘못된 정보가 있는 경우 댓글로 달아주세요!

공감♡ 버튼을 눌러주시면 더욱 유용하고 좋은 포스팅으로 찾아 뵙겠습니다.







TCP/IP 는 패킷 통신 방식으로 IP(인터넷 프로토콜)와 TCP(전송제어 프로토콜)로 구성되어있습니다.

이번에는 C#으로 TCP/IP 통신 프로그램을 이용하여 간단한 채팅프로그램을 만들어보겠습니다.




* 개발 환경

개발 툴 : Visual Studio 15.0(2017)

개발 언어 : C#


* 소스 코드(서버)


TcpListener Server; // 소켓 서버

TcpClient Client; // 클라이언트

StreamReader Reader; 

StreamWriter Writer; 

NetworkStream stream; // 네트워크 스트림 연결

Thread ReceiveThread; 

bool Connected;

private delegate void AddTextDelegate(string strText); // 크로스 쓰레드 호출


public Form1()

{

     InitializeComponent();

}


private void Form1_Load(object sender, EventArgs e)

{

     Thread ListenThread = new Thread(new ThreadStart(Listen)); // 서버 시작

     ListenThread.Start();

}


private void button1_Click(object sender, EventArgs e) // 보내기 버튼

{

     textBox1.AppendText("Me : " + textBox2.Text + "\r\n"); // 화면에 출력

     Writer.WriteLine(textBox2.Text); // 보내버리기

     Writer.Flush();

     textBox2.Clear(); 

}


private void Form1_FormClosing(object sender, FormClosingEventArgs e) // 폼 종료

{

     Connected = false;

     if (Reader != null) Reader.Close();

     if (Writer != null) Writer.Close();

     if (Server != null) Server.Stop();

     if (Client != null) Client.Close();

     if (ReceiveThread != null) ReceiveThread.Abort(); // 사용한 객체를 모두 닫아준다

}


private void Listen() // 클라이언트와 연결하기

{

     AddTextDelegate AddText = new AddTextDelegate(textBox1.AppendText);

     IPAddress addr = new IPAddress(0); // 서버 ip

     int port = 8080; // 서버 포트

     Server = new TcpListener(addr, port);

     Server.Start(); // 서버 시작

     Invoke(AddText, "Server Start!" + "\r\n");

     Client = Server.AcceptTcpClient(); // 클라이언트 연결 수락

     Connected = true;

     Invoke(AddText, "Connected to Client!" + "\r\n");

     stream = Client.GetStream(); // 클라이언트 스트림 값 받아오기

     Reader = new StreamReader(stream);

     Writer = new StreamWriter(stream);

     ReceiveThread = new Thread(new ThreadStart(Receive)); // 값을 받기 위한 쓰레드

     ReceiveThread.Start();

}


private void Receive() // 클라이언트에게 받기

{

     AddTextDelegate AddText = new AddTextDelegate(textBox1.AppendText);

     while(Connected)

     {     

          Thread.Sleep(1);

          if(stream.CanRead) // 받아온 데이터가 있다면 출력

          {

               string tempStr = Reader.ReadLine();

               if(tempStr.Length > 0)

               {

                    Invoke(AddText, "You : " + tempStr + "\r\n");

               }

          }

     }

}



* 소스 코드(클라이언트) - 서버와 거의 비슷함


TcpListener Server;

TcpClient Client;

StreamReader Reader;

StreamWriter Writer;

NetworkStream stream;

Thread ReceiveThread;

bool Connected;


private delegate void AddTextDelegate(string strText);


public Form1()

{

     InitializeComponent();

}


private void Form1_FormClosing(object sender, FormClosingEventArgs e) // 폼 종료

{

     Connected = false;

     if (Reader != null) Reader.Close();

     if (Writer != null) Writer.Close();

     if (Server != null) Server.Stop();

     if (Client != null) Client.Close();

     if (ReceiveThread != null) ReceiveThread.Abort();

}


private void button1_Click(object sender, EventArgs e) // 보내기 버튼

{

     textBox1.AppendText("Me : " + textBox2.Text + "\r\n");

     Writer.WriteLine(textBox2.Text); // 보내버리기

     Writer.Flush();

     textBox2.Clear();

}


private void Form1_Load(object sender, EventArgs e) // 폼 실행

{

     String IP = "192.168.x.x"; // 접속 할 서버 아이피를 입력

     int port = 8080; // 포트

     Client = new TcpClient();

     Client.Connect(IP, port);

     stream = Client.GetStream();

     Connected = true;

     textBox1.AppendText("Connected to Server!" + "\r\n");

     Reader = new StreamReader(stream);

     Writer = new StreamWriter(stream);

     ReceiveThread = new Thread(new ThreadStart(Receive));

     ReceiveThread.Start();

}


private void Receive() // 서버로 부터 값 받아오기

{

     AddTextDelegate AddText = new AddTextDelegate(textBox1.AppendText);

     while (Connected)

     {

          Thread.Sleep(1);

          if (stream.CanRead)

          {

               string tempStr = Reader.ReadLine();

               if (tempStr.Length > 0)

               {

                    Invoke(AddText, "You : " + tempStr + "\r\n");

               }

          }

     }

}



* 실행 화면


서버(좌측), 클라이언트(우측) 실행화면




오류나 질문사항은 댓글로 달아주세요!!





* 개발환경
개발 툴 : Visual Studio 15.0 (2017)
개발 언어 : C#
데이터베이스 : MySQL 5.7

* 데이터베이스 테이블
Table 1 : User - Num(PK), ID, PW, Manager
Table 2 : Data - Code, Date, User(FK)

* 기능
- 데이터 추가, 삭제, 수정, 검색(JDBC를 통한 Java-MySQL Database연동)
- 사용자 추가, 삭제, 목록(DB연동)
- 파일 입출력(Local)

* 주요 소스코드
- 기본 설명은 주석으로 처리했습니다.

1) 로그인
private void button_Login_Click(object sender, EventArgs e) { 
            String id = textBox4.Text; 
            String password = textBox3.Text; 
 // 입력받은 ID와 PASSWORD를 변수에 저장 
            int logInCheck = 0; 
            scon = new MySqlConnection(url); 
            scom = new MySqlCommand(); 
            scom.Connection = scon; 
 // SELECT 쿼리문(입력받은 ID,PASSWORD를 DB내에서 검색) 
            scom.CommandText = "SELECT * FROM user"; 
            try   { 
                scon.Open(); 
                sdr = scom.ExecuteReader(); 
                while (sdr.Read())   { 
                    String getId = sdr["id"].ToString(); 
                    String getPassword = sdr["password"].ToString(); 
                    String getManager = sdr["manager"].ToString(); 
 // 입력한 ID와 PASSWORD가 일치하는 내용이 있을 때 
                    if (id.Equals(getId) && password.Equals(getPassword))   { 
                        if (getManager.Equals("True")) 
   {   // 관리자가 로그인 할 경우
                            MessageBox.Show("[관리자] " + id + " 님 환영합니다.", "로그인 성공"); 
                        } 
                        else  

                        { // 사용자가 로그인 할 경우 
                            MessageBox.Show("[사용자] " + id + " 님 환영합니다.", "로그인 성공"); 
                        } 
                        logInCheck = 1; 
                        nowId = id; 
                       
 // 저장 경로 설정 
                        saveFolder = @"D:\BarcodeDataManager\" + textBox4.Text + @"\" 
                                     + nowTime.Split('.')[0] + @"년\" + nowTime.Split('.')[1] + @"월\"; 
                        saveRoute = saveFolder + nowTime.Split('.')[2].Split(' ')[0] + "일.txt"; 
                        textBox2.Text = saveRoute; 
                        dataGridView1.Columns[0].HeaderText = "시간"; 
                        dataGridView1.Columns[1].HeaderText = "바코드"; 
                        break; 
                    } 
                } 
                if (logInCheck != 1) 
// 입력받은 ID와 PASSWORD가 일치하지 않는 경우 
                { 
                    MessageBox.Show("ID 또는 PASSWORD를 확인해주세요", "로그인 실패"); 
                    textBox3.Clear(); 
                    textBox4.Clear(); 
                    textBox4.Focus(); 
                } 
            } 
            catch (Exception ex) 
            { 
                MessageBox.Show(ex.Message, "오류발생!"); 
            } 
            finally 
            { 
                scon.Close(); 
                textBox1.Focus(); 
            } 
 }

2) 바코드 입력
private void textBox_Barcode_KeyUp(object sender, KeyEventArgs e)   { 
            
// 엔터키 입력 시 
            if (e.KeyCode == Keys.Enter) 
            { 
                
// 입력 란이 비어있을 경우 
                if (textBox1.Text.Equals("")) 
                { 
                  
  // 함수 종료 
                    return; 
                } 
                String barcode = textBox1.Text; 
                textBox1.Text = ""; 
               
 // Database 내 중복검사 함수 호출 
                if (isOverlap(barcode)) 
                { 
                    
// Database 내 데이터 입력 함수 호출 
                    insertDatabase(barcode); 
                  
  // 로컬 컴퓨터 내 txt파일 저장 
                    dataFileWriter(barcode); 
                    if (checkBox1.Checked) // 기록여부 체크박스 확인 
                    { 
                        String[] Row = { nowTime, barcode }; 
                        dataGridView1.Rows.Add(Row); 
                        dataGridView1.FirstDisplayedScrollingRowIndex = 
                        dataGridView1.Rows.Count - 1;
 // DataGridView 자동 스크롤 
                    } 
                } 
                
// Data 중복 시 
                else 
                { 
                   
 // 함수 종료 
                    return; 
                } 
            } 
}


3) 데이터 추가(관리자용)
private void button_Data_Insert_Click(object sender, EventArgs e)  { 
            String barcode = textBox1.Text; 
            if(barcode.Equals("")) 
            { 
                MessageBox.Show("입력을 확인해주세요"); 
                return; 
            } 
            String time; 
            String user; 
           
 // 현재 시간 사용 체크 시 
            if(checkBox1.Checked) 
            { 
                time = Form1.nowTime; 
            } 
            
// 아닐 시 입력된 dateTimePicker에서 값을 받아온다. 
            else 
            { 
                time = dateTimePicker1.Value.ToString("yyyy.MM.dd HH:mm:ss"); 
            } 
            
// 현재 사용자 사용 체크시 
            if(checkBox2.Checked) 
            { 
                user = Form1.GetId(); 
            } 
           
 // 아닐 시 입력된 콤보박스에서 값을 받아온다. 
            else 
            { 
                user = comboBox1.SelectedItem.ToString(); 
            } 
            
// 데이터 중복 확인 
            if(Form1.isOverlap(barcode)) 
            { 
                Form1.scon = new MySqlConnection(Form1.url); 
                Form1.scom = new MySqlCommand(); 
                Form1.scom.Connection = Form1.scon; 
               
 // INSERT 쿼리문 
                Form1.scom.CommandText = "INSERT INTO datalist (code, serial, user) VALUES ('" 
                    + barcode + "','" + time + "','" + user + "');"; 
                try 
                { 
                    Form1.scon.Open(); 
                    Form1.sdr = Form1.scom.ExecuteReader(); 
                    MessageBox.Show("데이터가 입력되었습니다."); 
                } 
                catch (Exception ex) 
                { 
                    MessageBox.Show(ex.Message); 
                } 
                finally 
                { 
                    Form1.scon.Close(); 
                    this.Close(); 
                } 
            } 
            
// 데이터 중복 시 
            else 
            { 
               
 // 함수 종료 
                textBox1.Focus(); 
                return; 
            } 
 }


4) 데이터 중복검사
public static bool isOverlap(String inputBarcode)   { 
            scon = new MySqlConnection(url); 
            scom = new MySqlCommand(); 
            scom.Connection = scon; 
           
 // SELECT 쿼리문(입력받은 Barcode를 DB내에서 검색) 
            scom.CommandText = "SELECT * FROM datalist"; 
            try 
            { 
                scon.Open(); 
                sdr = scom.ExecuteReader(); 
                while (sdr.Read()) 
                { 
                    String getSerial = sdr["serial"].ToString(); 
                    String getCode = sdr["code"].ToString(); 
                    if (inputBarcode.Equals(getCode)) 
                    { 
                        MessageBox.Show("[ " + getSerial + " ] " + inputBarcode + 
                                        "은 이미 중복되었습니다.", "데이터 중복"); 
                        return false; 
                    } 
                } 
            } 
            catch (Exception ex) 
            { 
                MessageBox.Show(ex.Message, "오류발생!"); 
            } 
            finally 
            { 
                scon.Close(); 
            } 
            return true; 
 }


5) 파일쓰기(로컬 컴퓨터에 따로 저장)
 private void dataFileWriter(String barcode) { 
            if(checkBox2.Checked == true) 
// 저장 여부 체크박스 확인 
            { 
                DirectoryInfo di = new DirectoryInfo(saveFolder); 
                if (di.Exists == false)
 // 지정된 경로에 폴더가 있는 지 확인 
                { 
                    di.Create(); 
// 폴더 생성 
                } 
                
// 파일쓰기 (true 옵션 : 파일 맨 끝에 덧붙여 쓰기) 
                using (StreamWriter outputFile = new StreamWriter(saveRoute, true)) 
                { 
                    outputFile.WriteLine("[" + nowTime + "] " + barcode); 
                } 
            }   
}

* 실행화면


- 화면구성 
기록 란 리스트 ) DataGridView
* 다음 함수를 통해 DataGridView PostHeader에 index를 추가
        private void dataGridView1_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e) 
        { 
            if (e.RowIndex >= 0) 
            { 
                string NumberingText = (e.RowIndex + 1).ToString(); 

               
 // 글자 사이즈 구하기. 
                SizeF stringSize = e.Graphics.MeasureString(NumberingText, Font); 

               
 // 글자에 맞춰 좌표계산.  
                PointF StringPoint = new PointF 
                ( 
                    Convert.ToSingle(dataGridView1.RowHeadersWidth - 3 - stringSize.Width), 
                    Convert.ToSingle(e.RowBounds.Y) + dataGridView1[0, e.RowIndex].ContentBounds.Height * 0.3f 
                ); 

                
// 문자열 그리기. 
                e.Graphics.DrawString 
                ( 
                    NumberingText, 
                    Font, 
                    Brushes.Black, 
                    StringPoint.X, 
                    StringPoint.Y 
                ); 
            } 
        }

바코드 입력 란) TextBox - USB를 통해 바코드 입력 시 바코드 입력 후 자동 Enter키 입력
현재시간) Label - 쓰레드를 통해 1초마다 현재시간 갱신(바코드 입력 시간 저장)

** 바코드 입력 란은 항상 포커스가 유지되어야 하기 때문에 쓰레드 또는 버튼 이벤트에 포커스를 유지하는 함수를 호출 해 주어야 합니다 **

질문 사항은 댓글로 남겨주세요





to Top