วัดอุณหภูมิและความชื้นด้วย C# ตอน 2 (ตอนจบ)

ไปหน้าแรก | สารบัญ | Laploy.comระเบียนบทความ | บทความจากลาภลอย

วัดอุณหภูมิและความชื้นด้วย C# ตอน 2 (ตอนจบ)

เขียนโปรแกรมภาษา C# ใน .NET Framework นิยามคลาสวัดอุณหภูมิและความชื้นที่นำไปใช้ได้ทั้งใน WinForm และ WebForm ตอนจบ

 

ที่ท่านกำลังอ่านอยู่นี้คือตอนที่สองซึ่งเป็นตอนสุดท้ายของบทความนี้ ในตอนที่แล้วผู้เขียนพูดถึงฮาร์ดแวร์ที่ใช้ในโครงงานวิธีนำอุปกรณ์มาเชื่อมต่อกัน วิธีที่อุปกรณ์ดังกล่าวกับคอมพิวเตอร์สื่อสารกัน และวิธีเขียนคำสั่งควบคุมอุปกรณ์ นอกจากนั้นผู้เขียนยังอธิบายวิธีนิยามคลาส TempReader เพื่อห่อหุ้มอุปกรณ์และพอร์ทอนุกรมไว้เป็นก้อนเดียวกัน แต่อธิบายไปได้แค่สมาชิกแบบ field ก็หมดหน้ากระดาษ ดังนั้นในบทความตอนนี้เราจะมาดูนิยามส่วนที่เหลือทั้งหมด และปิดท้ายด้วยวิธีนำคลาส TempReader ไปใช้สร้าง object เพื่อทำหน้าที่วัดอุณหภูมิและความชื้นที่นำไปใช้ได้ทั้งใน WinForm และ WebForm

 

กระประกาศสมาชิก property

สมาชิกแบบ property ทำให้ object และ client class รับ-ส่งข้อมูลกันได้ อันที่จริงเราอาจเขียนโปรแกรมให้ client class อ่านและเขียนค่ากับฟิลด์สมาชิกของ object โดยตรงก็ได้ แต่ไม่สู้เป็นที่นิยม เพราะการทำเช่นนั้นผิดหลักการ encapsulation ซึ่งไม่ต้องการให้ client class เข้าถึงข้อมูลภายใน object โดยตรง แต่ให้เข้าถึงทางอ้อมผ่านส่วนกันชน

คลาส TempReader มีประกาศสมาชิก property เพียงตัวเดียวคือ TemppComPort ทำหน้าที่ให้ client class สามารถกำหนด และตรวจดูหมายเลขพอร์ทอนุกรมได้ เมื่อ client class ต้องการกำหนดค่าพอร์ท มันจะส่งค่าให้แก่ property นี้ ซึ่งจะนำค่าที่ได้รับมาไปกำหนดให้กับ ฟิลด์สมาชิก temppComPort อีกทอดหนึ่ง เมื่อ client class ต้องการอ่านค่าพอร์ท มันจะอ่านค่าจาก property นี้ ซึ่งจะนำค่าของฟิลด์สมาชิก temppComPort ส่งกลับไปให้

 

นิยามส่วน method สมาชิก

Method ในภาษา C# คล้ายฟังก์ชันในภาษาซีหรือภาษา JavaScript คือเป็นกลุ่มโค้ดทำหน้าที่ประมวลผลบางอย่าง ในภาษา C# ไม่มี method ที่อยู่อย่างเอกเทศเหมือนภาษาซี method ทุก method ต้องเป็นสมาชิกของคลาสหรือ struct เท่านั้น

หากท่านดูโค้ดส่วนนิยาม method สมาชิกของคลาส TempReader จะเห็นว่ามีสี่ตัวดังนี้

• GetTempp : ทำหน้าที่ส่งคำสั่งไปยัง AP170
• CloseCom : ทำหน้าที่ปิดพอร์ท
• CreateTemppConnection : ทำหน้าที่กำหนดค่าเริ่มต้นของพอร์ท
• myTemppComPort_DataReceived : ทำงานเมื่อพอร์ทได้รับข้อมูลจาก AP170

ต่อไปนี้ผู้เขียนจะอธิบายโค้ดของ method แต่ละตัว

 

GetTempp

Method GetTempp ทำหน้าที่ส่งคำสั่งไปยัง AP170 เพราะเมื่อเราต้องการอ่านค่าอุณหภูมิและความชื้น เราจะต้องส่งคำสั่งไปยัง AP170 การส่งคำสั่งทำได้โดยส่งกลุ่มตัวอักษรคำสั่งออกไปทางพอร์ทอนุกรม ยกตัวอย่างเช่น หากต้องการอ่านค่าอุณหภูมิและความชื้น เราต้องส่ง string ต่อไปนี้ “:1\r” ไปที่พอร์ทอนุกรม

เนื่องจากเราสร้าง object พอร์ทอนุกรมไว้แล้ว การส่งกลุ่มตัวอักษรคำสั่งออกไปทางพอร์ทอนุกรมจึงทำได้ง่ายมาก เพียงเรียกใช้ method ชื่อ Write โปรดสังเกตว่า method ชื่อ Write เป็น method ที่เราไม่ได้นิยามเอง แต่เป็น method สมาชิกของคลาส SerialPort ที่ .NET Framework จัดเตรียมไว้ให้

คำสั่ง if (!myTemppComPort.IsOpen) myTemppComPort.Open(); ทำหน้าที่ตรวจสอบว่าขณะนี้พอร์ทอนุกรมเปิดอยู่หรือไม่ หากพอร์ทยังไม่เปิดแล้วเราเรียก method Write จะเกิด run-time error เราจึงต้องเปิดพอร์ทเสียก่อน เมื่อเปิดแล้วและส่งคำสั่งเสร็จแล้ว เราจะไม่ปิดพอร์ท เราจำเป็นต้องเปิดพอร์ททิ้งไว้เพื่อรอรับข้อมูลที่ AP170 จะส่งกลับมา

 

CloseCom

Method CloseCom ทำหน้าที่ปิดพอร์ท มีสองเหตุการณ์ที่เราจำเป็นต้องปิดพอร์ทอนุกรม เหตุการณ์แรกคือเมื่อผู้ใช้เปลี่ยนค่ากำหนดของพอร์ทสื่อสาร เช่นหากผู้ใช้ต้องการเปลี่ยนความเร็ว จำนวนพาริตีบิต หรือเปลี่ยนหมายเลขพอร์ท เราจะต้องปิดพอร์ทเสียก่อน ในอีกกรณีหนึ่งคือเมื่อจบการทำงานของโปรแกรม เราควรปิดพอร์ทอนุกรมเพื่อให้โปรแกรมอื่นๆ สามารถเรียกใช้พอร์ทได้

คำสั่ง myTemppComPort.Close();ทำหน้าที่เรียก method Close ซึ่งเป็น method สมาชิกของคลาส SerialPort เช่นเดียวกับ method Write

 

CreateTemppConnection

Method CreateTemppConnection ทำหน้าที่กำหนดค่าเริ่มต้นของพอร์ท การกำหนดทำได้โดยเซท property ต่างๆ ของ object พอร์ทอนุกรม ค่าที่เราควรกำหนดก่อนใช้งานพอร์ทอนุกรมมีดังต่อไปนี้

  • BaudRate: ทำหน้าที่กำหนดความเร็วในการสื่อสาร เนื่องจาก AP170 ทำงานที่ความเร็ว 9600 เราจึงต้องตั้งค่า baud rate ให้ตรงตามนั้น
  • PortName: เราต้องกำหนดชื่อพอร์ทที่จะใช้งานในรูปแบบข้อความดังนี้ “COM1” คำว่า “COM” เป็นข้อความตายตัวจึงใส่ไว้ในโค้ดได้เลย ส่วนตัวเลขที่ตามหลังอาจเป็นไปได้ตั้งแต่ 1 ถึง 6 ซึ่งเราต้องการให้ผู้ใช้กำหนดได้ จึงทำเป็น property ชื่อ TemppComPort ไว้ดังที่อธิบายไปแล้ว เพื่อป้องกันไม่ให้เกิด error หาก client class เรียก Method CreateTemppConnection โดยไม่ได้เซทค่า property TemppComPort ไว้ก่อน เราจึงต้องกำหนดค่าโดยปริยายของฟิลด์สมาชิก temppComPort ให้เป็น 1 ไว้ตอนประกาศที่ส่วนหัวของโปรแกรม
  • Parity: พาริตี เป็นกลไกเพื่อตรวจสอบความถูกต้องของข้อมูลโดยแทรกบิตที่เป็นหนึ่งหรือศูนย์เข้าไปในข้อมูล เนื่องจาก AP170 ไม่ใช้งานพาริตี เราจึงกำหนดให้เป็น None คือไม่แทรกบิต
  • DataBits: ทำหน้าที่กำหนดจำนวนบิตของข้อมูลหนึ่งไบต์ AP170 มีมาตรฐานการรับ-ส่งข้อมูล 8 บิตต่อหนึ่งไบต์ เราจึงต้องกำหนดให้เป็นตามนั้น
  • StopBits: ทำหน้าที่กำหนดบิตปิดท้ายข้อมูลที่รับ-ส่งแต่ละไบต์ AP170 มีมาตรฐานการรับ-ส่งแบบ 1 stop bit เราจึงต้องกำหนดให้เป็นตามนั้น
  • DataReceived: ทำหน้ากำหนด method ที่เราต้องการให้โปรแกรมทำงานเมื่อพอร์ทสื่อสารได้รับข้อมูล ในที่นี้เรากำหนดว่าให้ไปทำงานที่ method ชื่อ myTemppComPort_DataReceived

ขอให้สังเกตว่าก่อนที่เราจะกำหนดค่าเหล่านี้เราต้องปิดพอร์ทเสียก่อน โดยเรียก method CloseCom

 

myTemppComPort_DataReceived

โค้ดส่วนสุดท้ายในคลาส TempReader คือ method myTemppComPort_DataReceived เป็น method ทำหน้าที่ให้บริการ event ซึ่งจะทำงานเมื่อพอร์ทอนุกรมได้รับข้อมูลจาก AP170 คำสั่ง rxString += myTemppComPort.ReadExisting(); ทำหน้าที่เรียก method ชื่อ ReadExisting() ซึ่งเป็น method สมาชิกของคลาส SerialPort ทำหน้าที่นำข้อมูลซึ่งอยู่ใน input buffer ทั้งหมดในขณะนั้นมาให้เรา ซึ่งเรานำมาเก็บพักไว้ในฟิลด์สมาชิก rxString

คำสั่งบรรทัดต่อมา if (rxString[rxString.Length-1] == ‘\r’ ) ทำหน้าที่ตรวจสอบว่าข้อมูลที่ได้รับมาครบแล้วหรือยัง ซึ่งตรวจสอบโดยดูว่าตัวอักษรสุดท้ายของข้อมูลเป็นรหัส return code หรือไม่ หากไม่ใช่แสดงว่าข้อมูลยังมาไม่ครบ โปรแกรมจะออกจาก method นี้เพื่อไปรอรับข้อมูลเพิ่ม

เมื่อข้อมูลมาครบดีแล้ว โปรแกรมจะทำงานคำสั่ง TemppReaderFire(rxString); ซึ่งเป็น anonymous method ที่เราประกาศไว้โดย delegate และ event ที่หัวโปรแกรม เนื่องจาก method นี้ไม่ได้มีอยู่จริง เมื่อโปรแกรมทำงานบรรทัดนี้ CLR (Common Language Runtime ของ .NET Framework) จะส่ง event ที่จะทำให้ method ใน client class (ที่เราต้องเขียนเตรียมไว้) ทำงานแทน

โค้ดของคลาส TemppReader ซึ่งมีหน้าที่เป็นพิมพ์เขียวของ object ห่อหุ้มการทำงานของพอร์ทอนุกรมและ AP170 มีเพียงเท่านี้ ต่อไปเป็นโค้ดส่วน client class ซึ่ง ทำหน้าที่ทดสอบใช้งาน object นี้

 

วิธีใช้งาน object

ต่อไปเราจะสร้าง WinForm เพื่อสาทิตการทำงานของ object ห่อหุ้มการทำงานของพอร์ทอนุกรมและ AP170 ที่เรานิยามคลาสไปแล้วในหัวข้อก่อน หน้า WinForm ที่เราจะสร้างเพื่อทดสอบนี้เรียบง่าย มี control เพียงสองตัวคือ Label ทำหน้าที่แสดงข้อมูล และปุ่มทำหน้าที่ส่งคำสั่งอ่านข้อมูลไปยัง AP170

 

หน้า WinForm ที่สร้างเพื่อทดสอบมี control เพียงสองตัวคือ Label ทำหน้าที่แสดงข้อมูล และปุ่มทำหน้าที่ส่งคำสั่งอ่านข้อมูลไปยัง AP170

 

หน้า WinForm ที่เราจะสร้างมีฐานะเป็นคลาส มีโค้ดทั้งหมดดังนี้

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace Laploy.TempHumReader
{
    public partial class test : Form
    {
        delegate void SetCallback(string text);
        private TemppReader myTemppReader1;

        public test()
        {
            InitializeComponent();
        }
        private void test_Load(object sender, EventArgs e)
        {
            myTemppReader1 = new TemppReader();
            myTemppReader1.TemppComPort = 1;
            myTemppReader1.TemppReaderFire += new
            TemppReader.TemppReaderEventHandler(myTemppReader1_TemppReaderFire);
            myTemppReader1.CreateTemppConnection();
        }
        private void myTemppReader1_TemppReaderFire(string tempp)
        {
            ShowText(tempp);
        }
        private void ShowText(string text)
        {
            if (label1.InvokeRequired)
            {
                SetCallback myCallBack = new SetCallback(ShowText);
                Invoke(myCallBack, new object[] { text });
            }
            else
            {
                label1.Text = text;
            }
        }
        private void button1_Click(object sender, EventArgs e)
        {
            myTemppReader1.GetTempp(":011\r");
        }
    }
} 

 
คลาสไดอะแกรมของ test 
 

จากคลาสไดอะแกรมของคลาส test จะเห็นว่ามีฟิลด์สมาชิก สี่ตัว button1, components และ label1 เป็น control ตัวอักษรมีสีอ่อนเพื่อบอกให้รู้ว่าเป็นโค้ดที่ถูกสร้างโดยโปรแกรม Visual Studio .NET ส่วน myTemppReader1 เป็นฟิลด์ที่เรานิยามขึ้นเอง

คลาส test มี method สมาชิกรวมทั้งสิ้น 7 ตัว สองตัวคือ Dispose และ InitializerComponent เป็น method ที่ถูกสร้างขึ้นโดยอัตโนมัติจากโปรแกรม Visual Studio .NET

โปรดสังเกตว่าคลาส test ไม่มีสมาชิกแบบ property เพราะเป็นคลาสแบบ WinForm ไม่ใชjคลาสที่เรานิยามขึ้นเพื่อสร้าง object

โปรแกรมใน .NET Framework นอกจากจะมีคุณสมบัติ type safety แล้ว ยังมีคุณสมบัติ thread safety อีกด้วย ในโปรแกรมของเรา object ที่เราสร้างจากคลาส SerialPort จะวิ่งอยู่ในอีก thread หนึ่งแยกต่างหากจาก thread ของตัว WinForm ทำให้เรานำข้อมูลจาก SerialPort มาแสดงใน label ใน WinForm โดยตรงไม่ได้ เพราะเป็นการทำงานข้าม thread ผู้เขียนแก้ปัญหานี้โดยใช้ delegate และ Invoke

คำสั่ง delegate void SetCallback(string text); ประกาศ method ที่จะใช้ในการ call back โปรดสังเกตว่า delegate ตัวนี้ไม่มีอะไรเกี่ยวข้องกับ delegate ในคลาส TemppReader ที่เราใช้ delegate ร่วมกับ event เพื่อสร้างการ call back กับ client class (คือหน้า WinForm นี้) แต่ในคลาส test นี้เราใช้ delegate ร่วมกับคำสั่ง Invoke เพื่อแก้ปัญหาการทำงานข้าม thread

คำสั่ง private TemppReader myTemppReader1; ทำหน้าที่สร้าง object ที่ห่อหุ้มการทำงานของพอร์ทอนุกรมและ AP170 ดังที่อธิบายไปแล้ว

ถัดมาคือ public test() เป็น constructor ทำหน้าที่กำหนดค่าเริ่มต้นต่างๆ ให้แก่ WinForm บรรทัดต่อมาคำสั่ง private void test_Load(object sender, EventArgs e) คือนิยาม method บริการ event Load ซึ่งจะเกิดขึ้นเมื่อหน้า WinForm ถูกเปิดขึ้น ผู้เขียนใส่คำสั่งกำหนดค่าต่างๆ ให้แก่ myTemppReader1

คำสั่งสำคัญที่ควรสังเกตคือ คำสั่งบรรทัดนี้

 

myTemppReader1.TemppReaderFire += new TemppReader.TemppReaderEventHandler(myTemppReader1_TemppReaderFire);

 

คำสั่งนี้ทำหน้าที่กำหนดให้โปรแกรมไปทำงานที่ method ชื่อ myTemppReader1_TemppReaderFire เมื่อ myTemppReader1 ส่ง event ชื่อ TemppReaderFire ซึ่งจะเกิดขึ้นเมื่อเราได้รับข้อมูลจาก AP170

นี่คือผลลัพธ์ของการที่เราสร้าง delegate และ event ไว้ในคลาส TemppReader นี่คือวิธีการที่ object เรียก method ใน client class ให้ทำงานได้เหมือนการทำ function pointer ในภาษา C++ เป็นวิธีสร้างและใช้งาน call back ในสไตล์ภาษา C#

ผู้เขียนอยากจะอธิบายโค้ดภายในคลาส test ละเอียดกว่านี้ แต่ทำไม่ได้เพราะหมดพื้นที่หน้ากระดาษเสียแล้ว อย่างไรก็ดีเท่าที่อธิบายมาโดยสังเขปนี้ ท่านน่าจะได้รับข้อมูลเพียงพอที่ไปทดลองใช้งานดูได้แล้ว หากมีข้อสงสัยใดๆ กรุณาโพสคำถามของท่านไว้ที่ wwwlaploy.com ผู้เขียนจะตอบทุกคำถามอย่างดีที่สุด

บทความนี้นำมาจาก นิตยสาร Windows IT Pro เขียนโดย ลาภลอย วานิชอังกูร (laploy.com)

Post a comment or leave a trackback: Trackback URL.

ใส่ความเห็น

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / เปลี่ยนแปลง )

Twitter picture

You are commenting using your Twitter account. Log Out / เปลี่ยนแปลง )

Facebook photo

You are commenting using your Facebook account. Log Out / เปลี่ยนแปลง )

Google+ photo

You are commenting using your Google+ account. Log Out / เปลี่ยนแปลง )

Connecting to %s

%d bloggers like this: