From f37340d48599ed64e312e0d6a42d252169d7bac3 Mon Sep 17 00:00:00 2001 From: Satakun Utama Date: Sun, 28 Dec 2025 10:49:09 +0700 Subject: [PATCH 1/4] Forced Stylistic Changes --- Chapter1.typ | 22 +- Chapter2/CLanguage.typ | 275 +++-- Chapter2/Chapter2.typ | 8 + Chapter2/Flutter.typ | 569 +++++++---- Chapter2/HTTPS.typ | 214 +++- Chapter2/Intro.typ | 14 +- Chapter2/Microcontroller.typ | 163 ++- Chapter2/PIR.typ | 212 +++- Chapter2/X509.typ | 102 +- Chapter3.typ | 114 ++- Dracula.tmTheme | 940 ++++++++++++++++++ PageTemplate.typ | 43 +- References.yaml | 555 +++++------ main.typ | 19 +- ...i-technical-college-project-guidelines.csl | 406 ++++++++ 15 files changed, 2859 insertions(+), 797 deletions(-) create mode 100644 Dracula.tmTheme create mode 100644 nong-khai-technical-college-project-guidelines.csl diff --git a/Chapter1.typ b/Chapter1.typ index 19f414f..f4e5367 100644 --- a/Chapter1.typ +++ b/Chapter1.typ @@ -1,5 +1,5 @@ #import "PageTemplate.typ": * -#show: page-theme +#show: chapter-page #set enum( indent: 3em, numbering: n => context { @@ -14,11 +14,19 @@ == ที่มาและความสำคัญของปัญหา -#i ความปลอดภัยนั้นเป็นเรื่องสำคัญสำหรับทุกคนแต่องค์กรแต่ละองค์กรและคนแต่ละคนมักมีความต้องการด้านความปลอดภัยไม่เหมือนกัน แต่ในบางครั้ง เมื่อมีบุคคลหรือองค์กรที่ต้องการเทคโนโลยีด้านความปลอดภัยเหล่านี้ เทคโนโลยีความปลอดภัยนั้นอาจมีราคาสูงเกินกว่าจะเอื้อมถึงได้ ส่งผลให้อาจมีการลดระดับความปลอดภัยลงมา เพิ่มความเสี่ยงของชีวิต ทรัพย์สิน เอกสาร และข้อมูลต่าง ๆ ขององค์กรหรือบุคคลนั้น ๆ +#i +ความปลอดภัยนั้นเป็นเรื่องสำคัญสำหรับทุกคนแต่องค์กรแต่ละองค์กรและคนแต่ละคนมักมีความต้องการด้านความปลอดภัยไม่เหมือนกัน +แต่ในบางครั้ง เมื่อมีบุคคลหรือองค์กรที่ต้องการเทคโนโลยีด้านความปลอดภัยเหล่านี้ +เทคโนโลยีความปลอดภัยนั้นอาจมีราคาสูงเกินกว่าจะเอื้อมถึงได้ ส่งผลให้อาจมีการลดระดับความปลอดภัยลงมา +เพิ่มความเสี่ยงของชีวิต ทรัพย์สิน เอกสาร และข้อมูลต่าง ๆ ขององค์กรหรือบุคคลนั้น ๆ -#i ในโลกปัจจุบัน อินเทอร์เน็ตนั้นก็เป็นสิ่งที่สำคัญมากเช่นกัน และสถานที่ส่วนใหญ่มักจะมีอินเทอร์เน็ต จึงก่อให้เกิดการที่มีอุปกรณ์อินเทอร์เน็ตรอบตัวเพิ่มขึ้นทุกวัน และได้มีสิ่งที่เรียกว่า Internet of Things (IoT) เกิดขึ้น ซึ่งคืออุปกรณ์ที่ถูกปรับปรุงให้ใช้งานได้ดีขึ้นด้วยเทคโนโลยีไร้สายต่าง ๆ เช่น Wi-Fi, Bluetooth, Zigbee, และ Thread +#i ในโลกปัจจุบัน อินเทอร์เน็ตนั้นก็เป็นสิ่งที่สำคัญมากเช่นกัน และสถานที่ส่วนใหญ่มักจะมีอินเทอร์เน็ต +จึงก่อให้เกิดการที่มีอุปกรณ์อินเทอร์เน็ตรอบตัวเพิ่มขึ้นทุกวัน และได้มีสิ่งที่เรียกว่า Internet of Things +(IoT) เกิดขึ้น ซึ่งคืออุปกรณ์ที่ถูกปรับปรุงให้ใช้งานได้ดีขึ้นด้วยเทคโนโลยีไร้สายต่าง ๆ เช่น Wi-Fi, +Bluetooth, Zigbee, และ Thread -#i โครงงานนี้จึงมีเป้าหมายที่จะแก้ไขปัญหาที่กล่าวไปข้างต้น พร้อมศึกษาและเรียนรู้เกี่ยวกับเทคโนโลยีไร้สาย Wi-Fi และ NFC เพื่อสร้างอุปกรณ์ยืนยันตัวตนที่ต้นทุนไม่สูงมากและให้ราคาเข้าถึงได้ง่ายขึ้น +#i โครงงานนี้จึงมีเป้าหมายที่จะแก้ไขปัญหาที่กล่าวไปข้างต้น พร้อมศึกษาและเรียนรู้เกี่ยวกับเทคโนโลยีไร้สาย +Wi-Fi และ NFC เพื่อสร้างอุปกรณ์ยืนยันตัวตนที่ต้นทุนไม่สูงมากและให้ราคาเข้าถึงได้ง่ายขึ้น == วัตถุประสงค์ของโครงงาน @@ -40,9 +48,11 @@ == นิยามศัพท์เฉพาะ -#i เครื่องยืนยันตัวตนด้วย NFC คืออุปกรณ์ความปลอดภัยที่มีหน้าที่ในการยืนยันตัวตนบุคคลที่เข้าออกพื้นที่ โดยใช้เทคโนโลยี NFC เป็นระบบยืนยันตัวตนบุคคลและใช้เซนเซอร์ตรวจจับความเคลื่อนใหวในการตรวจสอบหากมีบุคคลเข้าไปโดยไม่ได้รับอนุญาต +#i เครื่องยืนยันตัวตนด้วย NFC คืออุปกรณ์ความปลอดภัยที่มีหน้าที่ในการยืนยันตัวตนบุคคลที่เข้าออกพื้นที่ +โดยใช้เทคโนโลยี NFC +เป็นระบบยืนยันตัวตนบุคคลและใช้เซนเซอร์ตรวจจับความเคลื่อนใหวในการตรวจสอบหากมีบุคคลเข้าไปโดยไม่ได้รับอนุญาต -#pagebreak() +#show: page-theme == ผลที่คาดว่าจะได้รับ diff --git a/Chapter2/CLanguage.typ b/Chapter2/CLanguage.typ index b42d558..d63608d 100644 --- a/Chapter2/CLanguage.typ +++ b/Chapter2/CLanguage.typ @@ -3,18 +3,31 @@ = ภาษาซี (C Programming Language) -#i ภาษาซีเป็นภาษาโปรแกรมสำหรับวัตถุประสงค์ทั่วไปสร้างขึ้นในช่วงทศวรรษ 1970 โดยเดนนิสริตชีและยังคงได้รับความนิยมและใช้งานอย่างกว้างขวางด้วยการออกแบบภาษาซีทำให้โปรแกรมเมอร์สามารถเข้าถึงคุณลักษณะต่างๆของสถาปัตยกรรมซีพียูทั่วไปได้โดยตรง ซึ่งปรับแต่งให้เหมาะกับชุดคำสั่ง เป้าหมาย ภาษาซี ถูกนำมาใช้และยังคงนำมาใช้ในการพัฒนาระบบปฏิบัติการไดรเวอร์อุปกรณ์และสแต็กโปรโตคอลแต่การใช้งานในซอฟต์แวร์แอปพลิเคชั่นกำลังลดลงภาษาซีถูกนำมาใช้ในคอมพิวเตอร์ตั้งแต่ซูเปอร์คอมพิวเตอร์ขนาดใหญ่ที่สุดไปจนถึงไมโครคอนโทรลเลอร์ขนาดเล็กที่สุดและระบบฝังตัว +#i ภาษาซีเป็นภาษาโปรแกรมสำหรับวัตถุประสงค์ทั่วไปสร้างขึ้นในช่วงทศวรรษ 1970 +โดยเดนนิสริตชีและยังคงได้รับความนิยมและใช้งานอย่างกว้างขวางด้วยการออกแบบภาษาซีทำให้โปรแกรมเมอร์สามารถเข้าถึงคุณลักษณะต่างๆของสถาปัตยกรรมซีพียูทั่วไปได้โดยตรง +ซึ่งปรับแต่งให้เหมาะกับชุดคำสั่ง เป้าหมาย ภาษาซี +ถูกนำมาใช้และยังคงนำมาใช้ในการพัฒนาระบบปฏิบัติการไดรเวอร์อุปกรณ์และสแต็กโปรโตคอลแต่การใช้งานในซอฟต์แวร์แอปพลิเคชั่นกำลังลดลงภาษาซีถูกนำมาใช้ในคอมพิวเตอร์ตั้งแต่ซูเปอร์คอมพิวเตอร์ขนาดใหญ่ที่สุดไปจนถึงไมโครคอนโทรลเลอร์ขนาดเล็กที่สุดและระบบฝังตัว #i -ภาษาซีเป็นภาษาเชิงกระบวนการที่จำเป็นรองรับการเขียนโปรแกรมแบบมีโครงสร้างขอบเขตตัวแปรเชิงศัพท์และการเรียกซ้ำด้วยระบบชนิดข้อมูลแบบคงที่ภาษาซีถูกออกแบบมาเพื่อการคอมไพล์เพื่อให้สามารถเข้าถึงหน่วยความจำ และโครงสร้างภาษา ในระดับต่ำซึ่งแมปกับคำสั่งเครื่องได้อย่างมีประสิทธิภาพ โดยทั้งหมดนี้รองรับรันไทม์ขั้นต่ำ แม้จะมีความสามารถในระดับต่ำ แต่ภาษาซีก็ถูกออกแบบมาเพื่อสนับสนุนการเขียนโปรแกรมข้ามแพลตฟอร์ม โปรแกรมซี ที่สอดคล้องกับมาตรฐานที่เขียนขึ้นโดยคำนึงถึงความสามารถในการพกพาสามารถคอมไพล์สำหรับแพลตฟอร์มคอมพิวเตอร์และระบบปฏิบัติการที่หลากหลาย โดยมีการเปลี่ยนแปลงซอร์สโคดเพียงเล็กน้อย +ภาษาซีเป็นภาษาเชิงกระบวนการที่จำเป็นรองรับการเขียนโปรแกรมแบบมีโครงสร้างขอบเขตตัวแปรเชิงศัพท์และการเรียกซ้ำด้วยระบบชนิดข้อมูลแบบคงที่ภาษาซีถูกออกแบบมาเพื่อการคอมไพล์เพื่อให้สามารถเข้าถึงหน่วยความจำ +และโครงสร้างภาษา ในระดับต่ำซึ่งแมปกับคำสั่งเครื่องได้อย่างมีประสิทธิภาพ โดยทั้งหมดนี้รองรับรันไทม์ขั้นต่ำ +แม้จะมีความสามารถในระดับต่ำ แต่ภาษาซีก็ถูกออกแบบมาเพื่อสนับสนุนการเขียนโปรแกรมข้ามแพลตฟอร์ม +โปรแกรมซี +ที่สอดคล้องกับมาตรฐานที่เขียนขึ้นโดยคำนึงถึงความสามารถในการพกพาสามารถคอมไพล์สำหรับแพลตฟอร์มคอมพิวเตอร์และระบบปฏิบัติการที่หลากหลาย +โดยมีการเปลี่ยนแปลงซอร์สโคดเพียงเล็กน้อย -#i แม้ว่าทั้งภาษาซีและไลบรารีมาตรฐานของภาษา ซีจะไม่ได้มีคุณสมบัติยอดนิยมบางอย่างที่พบในภาษาอื่น แต่ก็มีความยืดหยุ่นเพียงพอที่จะรองรับคุณสมบัติเหล่านั้นได้ ตัวอย่างเช่นการวางแนววัตถุและการเก็บขยะนั้นจัดทำโดยไลบรารีภายนอก GLib Object System และ Boehm garbage collector ตามลำดับ +#i แม้ว่าทั้งภาษาซีและไลบรารีมาตรฐานของภาษา ซีจะไม่ได้มีคุณสมบัติยอดนิยมบางอย่างที่พบในภาษาอื่น +แต่ก็มีความยืดหยุ่นเพียงพอที่จะรองรับคุณสมบัติเหล่านั้นได้ +ตัวอย่างเช่นการวางแนววัตถุและการเก็บขยะนั้นจัดทำโดยไลบรารีภายนอก GLib Object System และ +Boehm garbage collector ตามลำดับ -#i ตั้งแต่ปี 2000 เป็นต้นมาภาษาซี ได้รับการจัดอันดับอย่างต่อเนื่องให้อยู่ในอันดับสี่ภาษาสูงสุดในดัชนี TIOBE ซึ่งเป็นการวัดความนิยมของภาษาการเขียนโปรแกรม +#i ตั้งแต่ปี 2000 เป็นต้นมาภาษาซี ได้รับการจัดอันดับอย่างต่อเนื่องให้อยู่ในอันดับสี่ภาษาสูงสุดในดัชนี TIOBE +ซึ่งเป็นการวัดความนิยมของภาษาการเขียนโปรแกรม == ตัวอย่าง "hello, world" -#i โดยการเรียนภาษาเขียนโปรแกรมใหม่ ๆ ต้องเริ่มด้วยการเขียนโปรแกรมในภาษานั้น ๆ โดยโปรแกรมแรกที่จะเขียนนั้นเหมือน ๆ กันในทุกภาษา คือการพิมพ์ "hello, world" +#i โดยการเรียนภาษาเขียนโปรแกรมใหม่ ๆ ต้องเริ่มด้วยการเขียนโปรแกรมในภาษานั้น ๆ +โดยโปรแกรมแรกที่จะเขียนนั้นเหมือน ๆ กันในทุกภาษา คือการพิมพ์ "hello, world" ```c #include @@ -25,41 +38,64 @@ int main() } ``` -#i คุณสามารถบันทึกไฟล์นี้เป็นไฟล์ที่มีส่วนขยายไฟล์ `.c` เช่น `hello.c` ได้เลย แต่การจะรันโปรแกรมนี้นั้นขึ้นอยู่กับระบบปฏิบัติการของคุณ ตัวอย่างเช่นบนระบบที่มีชุดคอมไพเลอร์ GCC (หรือ MinGW สำหรับเวอร์ชันบน Windows) ติดตั้งอยู่สามารถใช้คำสั่ง +#i คุณสามารถบันทึกไฟล์นี้เป็นไฟล์ที่มีส่วนขยายไฟล์ `.c` เช่น `hello.c` ได้เลย +แต่การจะรันโปรแกรมนี้นั้นขึ้นอยู่กับระบบปฏิบัติการของคุณ ตัวอย่างเช่นบนระบบที่มีชุดคอมไพเลอร์ GCC (หรือ +MinGW สำหรับเวอร์ชันบน Windows) ติดตั้งอยู่สามารถใช้คำสั่ง ```bash cc hello.c ``` -เพื่อคอมไพล์ไฟล์ได้ หากคุณไม่ได้ทำอะไรผิดพลาดไป เช่นการพิมพ์ตกหรือการสะกดผิด การคอมไพล์จะดำเนินการไปอย่างเงียบ ๆ และสร้างไฟล์ไบนารีชื่อ `a.out` ออกมา คุณสามารถรันไฟล์นั้นบนเทอร์มินัลของคุณได้โดยการพิมพ์ `./a.out` แล้วจึงจะได้ข้อความดังต่อไปนี้ออกมา +เพื่อคอมไพล์ไฟล์ได้ หากคุณไม่ได้ทำอะไรผิดพลาดไป เช่นการพิมพ์ตกหรือการสะกดผิด +การคอมไพล์จะดำเนินการไปอย่างเงียบ ๆ และสร้างไฟล์ไบนารีชื่อ `a.out` ออกมา +คุณสามารถรันไฟล์นั้นบนเทอร์มินัลของคุณได้โดยการพิมพ์ `./a.out` แล้วจึงจะได้ข้อความดังต่อไปนี้ออกมา ``` hello, world ``` -#i โดยโปรแกรมภาษา C นั้น ไม่ว่าจะขนาดใด จะประกอบไปด้วยฟังก์ชันและตัวแปร โดยฟังก์ชันจะประกอบไปด้วยสเตตเมนต์ (statements) ที่ระบุสิ่งที่โปรแกรมจะต้องกระทำ และตัวแปรนั้นกำหนดค่าที่จะถูกใช้งานในการกระทำเหล่านั้น โดยในตัวอย่างมีฟังก์ชันชื่อ `main` ซึ่งปกติแล้วคุณมีอิสระในการตั้งชื่อฟังก์ชันว่าอะไรก็ได้ แต่ฟังก์ชัน `main` นั้นพิเศษ เพราะโปรแกรมของคุณนั้นมีจุดเริ่มต้นที่ `main` ดังนั้น โปรแกรมทุกโปรแกรมต้องมี `main` อยู่สักที่ +#i โดยโปรแกรมภาษา C นั้น ไม่ว่าจะขนาดใด จะประกอบไปด้วยฟังก์ชันและตัวแปร +โดยฟังก์ชันจะประกอบไปด้วยสเตตเมนต์ (statements) ที่ระบุสิ่งที่โปรแกรมจะต้องกระทำ +และตัวแปรนั้นกำหนดค่าที่จะถูกใช้งานในการกระทำเหล่านั้น โดยในตัวอย่างมีฟังก์ชันชื่อ `main` +ซึ่งปกติแล้วคุณมีอิสระในการตั้งชื่อฟังก์ชันว่าอะไรก็ได้ แต่ฟังก์ชัน `main` นั้นพิเศษ +เพราะโปรแกรมของคุณนั้นมีจุดเริ่มต้นที่ `main` ดังนั้น โปรแกรมทุกโปรแกรมต้องมี `main` อยู่สักที่ -#i โดยปกติแล้วฟังก์ชัน `main` นั้นจะเรียกใช้ฟังก์ชันอื่น ๆ เพื่อทำงานให้มัน โดยอาจเป็นฟังก์ชันที่คุณเขียน หรือฟังก์ชันที่มาจากไลบรารีที่คุณใช้งาน ในบรรทัดแรกของโปรแกรมตัวอย่าง +#i โดยปกติแล้วฟังก์ชัน `main` นั้นจะเรียกใช้ฟังก์ชันอื่น ๆ เพื่อทำงานให้มัน โดยอาจเป็นฟังก์ชันที่คุณเขียน +หรือฟังก์ชันที่มาจากไลบรารีที่คุณใช้งาน ในบรรทัดแรกของโปรแกรมตัวอย่าง ```c #include ``` -มีหน้าที่ในการนำเข้าข้อมูลเกี่ยวกับไลบรารีอินพุต/เอาต์พุตมาตรฐาน โดยบรรทัดนี้นั้นอยู่ในไฟล์ ภาษา C หลายไฟล์ เนื่องจากการแสดงผลข้อมูลนั้นเป็นการกระทำที่ถูกกระทำบ่อย +มีหน้าที่ในการนำเข้าข้อมูลเกี่ยวกับไลบรารีอินพุต/เอาต์พุตมาตรฐาน โดยบรรทัดนี้นั้นอยู่ในไฟล์ ภาษา C +หลายไฟล์ เนื่องจากการแสดงผลข้อมูลนั้นเป็นการกระทำที่ถูกกระทำบ่อย -#i หนึ่งในวิธีการโอนถ่ายข้อมูลระหว่างฟังก์ชันคือการมอบรายการของข้อมูลที่ต้องการมอบให้แก่ฟังก์ชัน โดยค่าที่มอบให้ฟังก์ชันเหล่านั้นมีชื่อเรียกว่า อาร์กิวเมนต์ (arguments) ซึ่งวงเล็บที่ตามหลังชื่อฟังก์ชันนั้นคือวงเล็บที่จะครอบรายการอาร์กิวเมนต์ โดยในตัวอย่างฟังก์ชัน `main` นั้นไม่หวังค่าอาร์กิวเมนต์ใด ๆ สังเกตได้จาก `()` ที่เป็นรายการที่ว่างปล่าว +#i หนึ่งในวิธีการโอนถ่ายข้อมูลระหว่างฟังก์ชันคือการมอบรายการของข้อมูลที่ต้องการมอบให้แก่ฟังก์ชัน +โดยค่าที่มอบให้ฟังก์ชันเหล่านั้นมีชื่อเรียกว่า อาร์กิวเมนต์ (arguments) +ซึ่งวงเล็บที่ตามหลังชื่อฟังก์ชันนั้นคือวงเล็บที่จะครอบรายการอาร์กิวเมนต์ โดยในตัวอย่างฟังก์ชัน `main` +นั้นไม่หวังค่าอาร์กิวเมนต์ใด ๆ สังเกตได้จาก `()` ที่เป็นรายการที่ว่างปล่าว -#i สเตตเมนต์ที่อยู่ภายในฟังก์ชันนั้นจะถูกครอบด้วยวงเล็บปีกกา `{}` ซึ่งในฟังก์ชัน `main` มีแค่ 1 สเตตเมนต์ คือ +#i สเตตเมนต์ที่อยู่ภายในฟังก์ชันนั้นจะถูกครอบด้วยวงเล็บปีกกา `{}` ซึ่งในฟังก์ชัน `main` มีแค่ 1 +สเตตเมนต์ คือ ```c printf("hello, world\n"); ``` -#i โดยฟังก์ชันนั้นจะถูกเรียกใช้ได้โดยการเรียกชื่อมัน ตามด้วยรายการอาร์กิวเมนต์ที่ถูกครอบด้วยวงเล็บ ดังนั้น สเตตเมนต์นี้จึงมีการเรียกใช้ฟังก์ชัน `printf` ด้วยอาร์กิวเมนต์ `"hello, world\n"` โดยที่ `printf` เป็นฟังก์ชันจากไลบรารีที่ทำการพรินต์ข้อมูล (ซึ่งการพรินต์ในที่นี้คือการแสดงผลข้อความบนหน้าจอในเทอร์มินัล) และข้อมูลที่มันแสดงนั้นก็คือรายการอักขระที่ถูกครอบอยู่ด้วยเครื่องหมายอัญประกาศนั่นเอง +#i โดยฟังก์ชันนั้นจะถูกเรียกใช้ได้โดยการเรียกชื่อมัน ตามด้วยรายการอาร์กิวเมนต์ที่ถูกครอบด้วยวงเล็บ +ดังนั้น สเตตเมนต์นี้จึงมีการเรียกใช้ฟังก์ชัน `printf` ด้วยอาร์กิวเมนต์ `"hello, world\n"` โดยที่ +`printf` เป็นฟังก์ชันจากไลบรารีที่ทำการพรินต์ข้อมูล +(ซึ่งการพรินต์ในที่นี้คือการแสดงผลข้อความบนหน้าจอในเทอร์มินัล) +และข้อมูลที่มันแสดงนั้นก็คือรายการอักขระที่ถูกครอบอยู่ด้วยเครื่องหมายอัญประกาศนั่นเอง -#i รายการอักขระที่ถูกครอบด้วยเครื่องหมายอัญประกาศ เช่น `"hello, world\n"` นั้นมีชื่อเรียกว่า character string หรือ string constant และในตัวอย่างนี้นั้น เราจะมีการใช้รายการอักขระนี้เป็นเพียงแค่อาร์กิวเมนต์ของ `printf` และฟังก์ชันอื่น ๆ +#i รายการอักขระที่ถูกครอบด้วยเครื่องหมายอัญประกาศ เช่น `"hello, world\n"` นั้นมีชื่อเรียกว่า +character string หรือ string constant และในตัวอย่างนี้นั้น +เราจะมีการใช้รายการอักขระนี้เป็นเพียงแค่อาร์กิวเมนต์ของ `printf` และฟังก์ชันอื่น ๆ -#i ลำดับตัวอักษร `\n` ในสตริงคือสัญกรณ์ภาษา C สำหรับ#emph[ตัวอักษรบรรทัดใหม่] ซึ่งเมื่อถูกพรินต์แล้วจะให้เอาต์พุตไปอยู่ทางด้านซ้ายของบรรทัดใหม่ โดยหากไม่ใส่ `\n` (ซึ่งคุณสามารถทดลองได้เลย) คุณจะพบว่าไม่มีการขึ้นบรรทัดใหม่ของข้อความ และคุณต้องใช้ `\n` ในการขึ้นบรรทัดใหม่ และหากคุณลองทำแบบนี้: +#i ลำดับตัวอักษร `\n` ในสตริงคือสัญกรณ์ภาษา C สำหรับ#emph[ตัวอักษรบรรทัดใหม่] +ซึ่งเมื่อถูกพรินต์แล้วจะให้เอาต์พุตไปอยู่ทางด้านซ้ายของบรรทัดใหม่ โดยหากไม่ใส่ `\n` +(ซึ่งคุณสามารถทดลองได้เลย) คุณจะพบว่าไม่มีการขึ้นบรรทัดใหม่ของข้อความ และคุณต้องใช้ `\n` +ในการขึ้นบรรทัดใหม่ และหากคุณลองทำแบบนี้: ```c printf("Hello, world @@ -68,7 +104,8 @@ printf("Hello, world คอมไพเลอร์ภาษา C นั้นจะแสดงข้อความแสดงข้อผิดพลาดขึ้นมา -#i `printf` นั้นจะไม่มีทางใส่ตัวอักษรขึ้นบรรทัดใหม่ให้โดยอัตโนมัติ ดังนั้นคุณสามารถเรียกใช้ฟังก์ชันหลาย ๆ ครั้งเพื่อค่อย ๆ สร้างเอาต์พุตออกมาได้ โดยที่โปรแกรมแรกของเราจะสามารถเขียนแบบนี้ได้ +#i `printf` นั้นจะไม่มีทางใส่ตัวอักษรขึ้นบรรทัดใหม่ให้โดยอัตโนมัติ ดังนั้นคุณสามารถเรียกใช้ฟังก์ชันหลาย +ๆ ครั้งเพื่อค่อย ๆ สร้างเอาต์พุตออกมาได้ โดยที่โปรแกรมแรกของเราจะสามารถเขียนแบบนี้ได้ ```c #include @@ -83,11 +120,17 @@ int main() แล้วข้อความที่แสดงออกมาจะยังคงเดิม -#i คุณสามารถสังเกตได้ว่า `\n` นั้นจะแทนตัวอักษรตัวเดียว โดยสัญกรณ์ _escape sequence_ เช่น `\n` คือรูปแบบในการเขียนตัวอักษรที่อาจพิมพ์ได้ยากหรือตัวอักษรล่องหน โดยสัญกรณ์อื่น ๆ ในประเภทเดียวกันมีตัวอย่างเช่น `\t` สำหรับตัวอักษรแท็บ, `\b` สำหรับ backspace, `\"` สำหรับการพิมพ์สัญลักษณ์อัญประกาศ (ไม่เช่นนั้นตัวอักษรอัญประกาศจะถูกถือว่าเป็นตัวอักษรในการเริ่มต้น/สิ้นสุดของสตริง), และ `\\` สำหรับการพิมพ์ตัวอักษร backslash เอง +#i คุณสามารถสังเกตได้ว่า `\n` นั้นจะแทนตัวอักษรตัวเดียว โดยสัญกรณ์ _escape sequence_ เช่น +`\n` คือรูปแบบในการเขียนตัวอักษรที่อาจพิมพ์ได้ยากหรือตัวอักษรล่องหน โดยสัญกรณ์อื่น ๆ +ในประเภทเดียวกันมีตัวอย่างเช่น `\t` สำหรับตัวอักษรแท็บ, `\b` สำหรับ backspace, `\"` +สำหรับการพิมพ์สัญลักษณ์อัญประกาศ +(ไม่เช่นนั้นตัวอักษรอัญประกาศจะถูกถือว่าเป็นตัวอักษรในการเริ่มต้น/สิ้นสุดของสตริง), และ `\\` +สำหรับการพิมพ์ตัวอักษร backslash เอง == ตัวแปร (Variables) -#i ตัวแปรในภาษา C เบื้องต้นแล้วประกอบไปด้วยประเภทของข้อมูล และชื่อตัวแปร โดยที่ชื่อตัวแปรนั้นสามารถเป็นรายการที่ถูกแบ่งด้วยเครื่องหมายจุลภาคได้ด้วยเช่นกัน ตัวอย่างคือ +#i ตัวแปรในภาษา C เบื้องต้นแล้วประกอบไปด้วยประเภทของข้อมูล และชื่อตัวแปร +โดยที่ชื่อตัวแปรนั้นสามารถเป็นรายการที่ถูกแบ่งด้วยเครื่องหมายจุลภาคได้ด้วยเช่นกัน ตัวอย่างคือ ```c int data; @@ -96,14 +139,20 @@ float a, b, c; == ประเภทข้อมูล (Data Types) -#i ข้อมูลที่เกี่ยวข้องกับตัวเลขมักมีประเภท *unsigned* และ *signed* โดยความแตกต่างหากอธิบายสั้น ๆ คือ +#i ข้อมูลที่เกี่ยวข้องกับตัวเลขมักมีประเภท *unsigned* และ *signed* โดยความแตกต่างหากอธิบายสั้น +ๆ คือ - *Signed (มีเครื่องหมาย):* ตัวเลขที่สามารถติดลบได้ ระยะข้อมูลตัวอย่างคือ -128 ถึง 127 - *Unsigned (ไม่มีเครื่องหมาย):* ตัวเลขที่ไม่สามารถติดลบได้ ระยะข้อมูลตัวอย่างคือ 0 ถึง 255 -#i จะสังเกตได้ว่า ข้อมูลประเภท unsigned นั้นสามารถเก็บตัวเลขบวกได้จำนวนมากกว่า คือสูงสุดที่ 255 แต่หากนำค่าสัมบูรณ์ (absolute value) ของระยะข้อมูลแบบ signed มาบวกกัน เช่น\ #math.equation($|-128| + |127|$, alt: "ค่าสัมบูรณ์ของ -128 บวกค่าสัมบูรณ์ของ 127") จะพบว่าได้ค่า 255 หมายความว่า จริง ๆ แล้วข้อมูลประเภท signed สามารถเก็บข้อมูลได้ 255 ตัวเลขเช่นกัน เพียงแต่ว่าครึ่งหนึ่งของตัวเลขที่สามารถเก็บได้เป็นตัวเลขติดลบ +#i จะสังเกตได้ว่า ข้อมูลประเภท unsigned นั้นสามารถเก็บตัวเลขบวกได้จำนวนมากกว่า คือสูงสุดที่ 255 +แต่หากนำค่าสัมบูรณ์ (absolute value) ของระยะข้อมูลแบบ signed มาบวกกัน เช่น\ +#math.equation($|-128| + |127|$, alt: "ค่าสัมบูรณ์ของ -128 บวกค่าสัมบูรณ์ของ 127") +จะพบว่าได้ค่า 255 หมายความว่า จริง ๆ แล้วข้อมูลประเภท signed สามารถเก็บข้อมูลได้ 255 +ตัวเลขเช่นกัน เพียงแต่ว่าครึ่งหนึ่งของตัวเลขที่สามารถเก็บได้เป็นตัวเลขติดลบ -*หมายเหตุ:* เลขคณิตจำนวนเต็มมีนิยามแตกต่างกันสำหรับชนิดจำนวนเต็มแบบ signed และ unsigned โปรดดูตัวดำเนินการเลขคณิต โดยเฉพาะอย่างยิ่งการโอเวอร์โฟลว์จำนวนเต็ม +*หมายเหตุ:* เลขคณิตจำนวนเต็มมีนิยามแตกต่างกันสำหรับชนิดจำนวนเต็มแบบ signed และ unsigned +โปรดดูตัวดำเนินการเลขคณิต โดยเฉพาะอย่างยิ่งการโอเวอร์โฟลว์จำนวนเต็ม === ประเภทบูลีน (Boolean) @@ -119,8 +168,10 @@ float a, b, c; - `short int` (หรืออีกชื่อหนึ่งคือ `short` และสามารถใช้คีย์เวิร์ด `signed` ได้) - `unsigned short int` (หรือ `unsigned short`) - `int` (หรือ `signed int`) \ - คือประเภทข้อมูลตัวเลขที่ปกติที่สุด และจะถูกการันตีว่าจะมีขนาดขั้นต่ำ 16 บิตเสมอ โดยระบบทั่วไปส่วนใหญ่ในปัจจุบันจะเป็น 32 บิต -- `unsigned int` (หรือเพียงแค่ `unsigned`): คือประเภท `int` ในแบบ `unsigned`, มี modulo arithmetic, และเหมาะสมสำหรับการเปลี่ยนแปลงบิต + คือประเภทข้อมูลตัวเลขที่ปกติที่สุด และจะถูกการันตีว่าจะมีขนาดขั้นต่ำ 16 บิตเสมอ + โดยระบบทั่วไปส่วนใหญ่ในปัจจุบันจะเป็น 32 บิต +- `unsigned int` (หรือเพียงแค่ `unsigned`): คือประเภท `int` ในแบบ `unsigned`, มี + modulo arithmetic, และเหมาะสมสำหรับการเปลี่ยนแปลงบิต - `long int` (หรือ `long`) - `unsigned long int` (หรือ `unsigned long`) @@ -130,10 +181,14 @@ float a, b, c; - `long long int` (หรือ `long long`) - `unsigned long long int` (หรือ `unsigned long long`) - มีเพิ่มตั้งแต่ C23: - - `_BitInt(n)` (หรือ `signed _BitInt(n)`): ประเภทข้อมูล signed แบบมีขนาดชัดเจน โดย n แทนด้วยจำนวนบิต (รวมถึงบิตเครื่องหมาย และ n จะต้องไม่มากกว่า `BITINT_MAXWIDTH` จากไฟล์ ``) - - `unsigned _BitInt(n)`: เหมือนข้างต้น เพียงแค่เป็นประเภท unsigned (และไม่มีบิตเครื่องหมาย) + - `_BitInt(n)` (หรือ `signed _BitInt(n)`): ประเภทข้อมูล signed แบบมีขนาดชัดเจน โดย + n แทนด้วยจำนวนบิต (รวมถึงบิตเครื่องหมาย และ n จะต้องไม่มากกว่า `BITINT_MAXWIDTH` + จากไฟล์ ``) + - `unsigned _BitInt(n)`: เหมือนข้างต้น เพียงแค่เป็นประเภท unsigned + (และไม่มีบิตเครื่องหมาย) -และเหมือนประเภทข้อมูลอื่น ๆ คุณสามารถเรียงคีย์เวิร์ดแบบใดก็ได้ เช่น `unsigned long long int` และ `long int unsigned long` นั้นเหมือนกัน +และเหมือนประเภทข้อมูลอื่น ๆ คุณสามารถเรียงคีย์เวิร์ดแบบใดก็ได้ เช่น `unsigned long long int` +และ `long int unsigned long` นั้นเหมือนกัน ตารางต่อไปนี้สรุปประเภทตัวเลขทั้งหมดและคุณสมบัติของมัน @@ -247,13 +302,21 @@ float a, b, c; และนอกจากค่าบิตขั้นต่ำ มาตรฐาน C นั้นการันตีว่า: -#i ```c 1``` == ```c sizeof(char)``` #sym.lt.eq ```c sizeof(short)``` #sym.lt.eq ```c sizeof(int)``` #sym.lt.eq ```c sizeof(long)``` #sym.lt.eq ```c sizeof(long long)``` +#i ```c 1``` == ```c sizeof(char)``` #sym.lt.eq ```c sizeof(short)``` #sym.lt.eq +```c sizeof(int)``` #sym.lt.eq ```c sizeof(long)``` #sym.lt.eq +```c sizeof(long long)``` -*หมายเหตุ:* เงื่อนไขนี้อนุญาตกรณีสุดขีดที่ทุกประเภทมีขนาด 64 บิตและ `sizeof` คืนค่า `1` สำหรับทุกประเภท +*หมายเหตุ:* เงื่อนไขนี้อนุญาตกรณีสุดขีดที่ทุกประเภทมีขนาด 64 บิตและ `sizeof` คืนค่า `1` +สำหรับทุกประเภท ==== รูปแบบข้อมูล (data model) -#i รูปแบบข้อมูล หรือ data model คือรูปแบบการเก็บข้อมูลของโปรแกรมซึ่งเป็นสิ่งที่กำหนดขนาดของตัวแปร โดยรูปแบบข้อมูลนั้นจะถูกกำหนดโดยแพลตฟอร์มเป้าหมาย ซึ่งมีหน่วยประมวลผลและระบบปฏิบัติการเป็นปัจจัยหลัก โดยตามตารางในหัวข้อก่อนหน้า หลัก ๆ แล้วมีรูปแบบข้อมูลอยู่ 4 รูปแบบ คือ LP32, ILP32, LLP64, และ LP64 ซึ่งหากต้องการหาความหาย L หมายถึง Long, P หมายถึง Pointer, และ I หมายถึง Integer (จำนวนเต็ม) แล้วตามด้วยเลขบิต ดังนั้น สรุปแล้วจึงจะมีความหมายดังนี้ +#i รูปแบบข้อมูล หรือ data model คือรูปแบบการเก็บข้อมูลของโปรแกรมซึ่งเป็นสิ่งที่กำหนดขนาดของตัวแปร +โดยรูปแบบข้อมูลนั้นจะถูกกำหนดโดยแพลตฟอร์มเป้าหมาย +ซึ่งมีหน่วยประมวลผลและระบบปฏิบัติการเป็นปัจจัยหลัก โดยตามตารางในหัวข้อก่อนหน้า หลัก ๆ +แล้วมีรูปแบบข้อมูลอยู่ 4 รูปแบบ คือ LP32, ILP32, LLP64, และ LP64 ซึ่งหากต้องการหาความหาย L +หมายถึง Long, P หมายถึง Pointer, และ I หมายถึง Integer (จำนวนเต็ม) แล้วตามด้วยเลขบิต +ดังนั้น สรุปแล้วจึงจะมีความหมายดังนี้ ระบบ 32 บิต: - LP32 หรือ 2/4/4: `long` และ Pointer มีขนาด 32 บิต @@ -268,7 +331,8 @@ float a, b, c; - LP64 หรือ 4/8/8: `long` และ Pointer มีขนาด 64 บิต - ระบบ Unix และเสมือน Unix (Linux, Mac OS X) -#i รูปแบบอื่น ๆ นั้นหาได้ยาก ตัวอย่างเช่น ILP64 (8/8/8: `int`, `long`, และ Pointer ขนาด 64 บิต) ที่มีการใช้งานแค่ในระบบ Unix 64 บิตช่วงเริ่มต้น (เช่น Unicos บน Cray) +#i รูปแบบอื่น ๆ นั้นหาได้ยาก ตัวอย่างเช่น ILP64 (8/8/8: `int`, `long`, และ Pointer ขนาด +64 บิต) ที่มีการใช้งานแค่ในระบบ Unix 64 บิตช่วงเริ่มต้น (เช่น Unicos บน Cray) และโปรดจำไว้ว่า ตัวเลขที่มีขนาดแน่นอนนั้นมีให้ใช้งานใน `` ตั้งแต่ C99 @@ -277,12 +341,20 @@ float a, b, c; ภาษา C นั้นมีประเภทข้อมูลสำหรับแทนตัวเลขทศนิยมจริง 3 (หรือ 6 ตั้งแต่ C23) ประเภท - `float`: จำนวนทศนิยมความแม่นยำเดี่ยว ตรงกับฟอร์แมตมาตรฐาน IEEE-754 binary32 หากรองรับ -- `double`: จำนวนทศนิยมความแม่นยำสองเท่า ตรงกับฟอร์แมตมาตรฐาน IEEE-754 binary64 หากรองรับ -- `long double`: จำนวนทศนิยมความแม่นยำเพิ่มเติม ตรงกับฟอร์แมตมาตรฐาน IEEE-754 binary128 หากรองรับ มิฉะนั้นจะตรงกับ IEEE-754 binary64-extended หากรองรับ มิฉะนั้นจะตรงกับรูปแบบจำนวนทศนิยมที่ไม่ตรงกับมาตรฐาน IEEE-754 รูปแบบใดก็ได้ตราบใดที่มีความแม่นยำกว่า binary64 และระยะข้อมูลนั้นอย่างน้อยก็ต้องดีเท่า binary64 และหากไม่รองรับทั้งหมดนั้น จะตรงกับฟอร์แมตมาตรฐาน IEEE-754 binary64 +- `double`: จำนวนทศนิยมความแม่นยำสองเท่า ตรงกับฟอร์แมตมาตรฐาน IEEE-754 binary64 + หากรองรับ +- `long double`: จำนวนทศนิยมความแม่นยำเพิ่มเติม ตรงกับฟอร์แมตมาตรฐาน IEEE-754 binary128 + หากรองรับ มิฉะนั้นจะตรงกับ IEEE-754 binary64-extended หากรองรับ + มิฉะนั้นจะตรงกับรูปแบบจำนวนทศนิยมที่ไม่ตรงกับมาตรฐาน IEEE-754 + รูปแบบใดก็ได้ตราบใดที่มีความแม่นยำกว่า binary64 และระยะข้อมูลนั้นอย่างน้อยก็ต้องดีเท่า binary64 + และหากไม่รองรับทั้งหมดนั้น จะตรงกับฟอร์แมตมาตรฐาน IEEE-754 binary64 - รูปแบบ binary128 นั้นถูกใช้โดยระบบ HP-UX, SPARC, MIPS, ARM64, และ z/OS บางระบบ - - รูปแบบ IEEE-754 binary64-extended ที่รู้จักกันอย่างแพร่หลายที่สุดคือรูปแบบความแม่นยำเพิ่มเติม 80 บิต x87 ซึ่งถูกใช้โดยสถาปัตยกรรม x86 และ x86-64 บางระบบ (การยกเว้นที่ควรพูดถึงคือ MSVC ที่กำหนดให้ `long double` อยู่ในรูปแบบเดียวกันกับ `double`, เช่น binary64) + - รูปแบบ IEEE-754 binary64-extended ที่รู้จักกันอย่างแพร่หลายที่สุดคือรูปแบบความแม่นยำเพิ่มเติม + 80 บิต x87 ซึ่งถูกใช้โดยสถาปัตยกรรม x86 และ x86-64 บางระบบ (การยกเว้นที่ควรพูดถึงคือ + MSVC ที่กำหนดให้ `long double` อยู่ในรูปแบบเดียวกันกับ `double`, เช่น binary64) -เมื่อใช้มาตรฐาน C ตั้งแต่ C23 เป็นต้นไปและหากแพลตฟอร์มของคุณใช้งานคอนแสตนต์มาโคร `__STDC_IEC_60559_DFP__` ข้อมูลประเภทตัวเลขทศนิยมดังต่อไปนี้จะถูกรองรับด้วย: +เมื่อใช้มาตรฐาน C ตั้งแต่ C23 เป็นต้นไปและหากแพลตฟอร์มของคุณใช้งานคอนแสตนต์มาโคร +`__STDC_IEC_60559_DFP__` ข้อมูลประเภทตัวเลขทศนิยมดังต่อไปนี้จะถูกรองรับด้วย: - `_Decimal32`: แทนรูปแบบมาตรฐาน IEEE-754 decimal32 - `_Decimal64`: แทนรูปแบบมาตรฐาน IEEE-754 decimal64 @@ -293,62 +365,93 @@ float a, b, c; ข้อมูลประเภททศนิยมอาจรองรับค่าพิเศษเพิ่มเติมได้แก่ - อนันต์ (Infinity, ทั้งบวกและลบ) -- ศูนย์ติดลบ, `-0.0` โดยมีค่าเท่ากับศูยน์ที่ติดบวก แต่อาจมีความหมายในบางสมการ เช่น `1.0 / 0.0 == INFINITY` แต่ `1.0 / -0.0 == -INFINITY` +- ศูนย์ติดลบ, `-0.0` โดยมีค่าเท่ากับศูยน์ที่ติดบวก แต่อาจมีความหมายในบางสมการ เช่น + `1.0 / 0.0 == INFINITY` แต่ `1.0 / -0.0 == -INFINITY` - ไม่ใช่ตัวเลข (not-a-number; NaN) ซึ่งไม่เท่ากับอะไรเลย (รวมถึงตัวมันเอง) -ทศนิยมจำนวนจริงสามารถถูกใช้กับตัวดำเนินการทางคณิตศาสตร์ได้ *+ - / \** และฟังก์ชันทางคณิตศาสตร์จาก `` โดยทั้งตัวดำเนินการและฟังก์ชันจากไลบรารีนั้นสามารถก่อให้เกิดการแสดงข้อผิดพลาดของจำนวนทศนิยมได้และจะตั้งค่า `errno` +ทศนิยมจำนวนจริงสามารถถูกใช้กับตัวดำเนินการทางคณิตศาสตร์ได้ *+ - / \** +และฟังก์ชันทางคณิตศาสตร์จาก `` +โดยทั้งตัวดำเนินการและฟังก์ชันจากไลบรารีนั้นสามารถก่อให้เกิดการแสดงข้อผิดพลาดของจำนวนทศนิยมได้และจะตั้งค่า +`errno` === ประเภทจำนวนทศนิยมซับซ้อน (Complex floating types) -#i ประเภทข้อมูลจำนวนทศนิยมซับซ้อนนั้นเป็นประเภทที่แทนตัวเลขเชิงซ้อน (complex number) นั้นคือ ตัวเลขที่สามารถถูกเขียนแทนเป็นผลรวมของจำนวนจริงและจำนวนจริงที่คูณด้วยจำนวนจินตภาพ: #math.equation($a + b i$, alt: "a บวก b i") +#i ประเภทข้อมูลจำนวนทศนิยมซับซ้อนนั้นเป็นประเภทที่แทนตัวเลขเชิงซ้อน (complex number) นั้นคือ +ตัวเลขที่สามารถถูกเขียนแทนเป็นผลรวมของจำนวนจริงและจำนวนจริงที่คูณด้วยจำนวนจินตภาพ: +#math.equation($a + b i$, alt: "a บวก b i") ประเภทจำนวนเชิงซ้อนมีอยู่สามประเภท ได้แก่ -- ```c float _Complex``` (และสามารถใช้ ```c float complex``` ได้เช่นกันหากนำเข้า ``) -- ```c double _Complex``` (และสามารถใช้ ```c double complex``` ได้เช่นกันหากนำเข้า ``) -- ```c long double _Complex``` (และสามารถใช้ ```c long double complex``` ได้เช่นกันหากนำเข้า ``) +- ```c float _Complex``` (และสามารถใช้ ```c float complex``` ได้เช่นกันหากนำเข้า + ``) +- ```c double _Complex``` (และสามารถใช้ ```c double complex``` ได้เช่นกันหากนำเข้า + ``) +- ```c long double _Complex``` (และสามารถใช้ ```c long double complex``` + ได้เช่นกันหากนำเข้า ``) -*หมายเหตุ:* เหมือนกับประเภทอื่น ๆ สามารถพิมพ์คีย์เวิร์ดในลำดับใดก็ได้ ```c long double complex```, ```c complex long double``` และแม้แต่ ```c double complex long``` นั้นคือประเภทข้อมูลเดียวกัน +*หมายเหตุ:* เหมือนกับประเภทอื่น ๆ สามารถพิมพ์คีย์เวิร์ดในลำดับใดก็ได้ +```c long double complex```, ```c complex long double``` และแม้แต่ +```c double complex long``` นั้นคือประเภทข้อมูลเดียวกัน === ประเภทจำนวนทศนิยมจินตภาพ (Imaginary floating types) -#i ประเภทข้อมูลจำนวนทศนิยมจินตภาพนั้นเป็นประเภทที่แทนตัวเลขจินตภาพ (imaginary number) นั้นคือ ตัวเลขที่สามารถถูกเขียนแทนเป็นจำนวนจริงที่คูณด้วยจำนวนจินตภาพ: #math.equation($b i$, alt: "b i") +#i ประเภทข้อมูลจำนวนทศนิยมจินตภาพนั้นเป็นประเภทที่แทนตัวเลขจินตภาพ (imaginary number) นั้นคือ +ตัวเลขที่สามารถถูกเขียนแทนเป็นจำนวนจริงที่คูณด้วยจำนวนจินตภาพ: #math.equation( + $b i$, + alt: "b i", +) ประเภทจำนวนเชิงซ้อนมีอยู่สามประเภท ได้แก่ -- ```c float _Imaginary``` (และสามารถใช้ ```c float imaginary``` ได้เช่นกันหากนำเข้า ``) -- ```c double _Imaginary``` (และสามารถใช้ ```c double imaginary``` ได้เช่นกันหากนำเข้า ``) -- ```c long double _Imaginary``` (และสามารถใช้ ```c long double imaginary``` ได้เช่นกันหากนำเข้า ``) +- ```c float _Imaginary``` (และสามารถใช้ ```c float imaginary``` ได้เช่นกันหากนำเข้า + ``) +- ```c double _Imaginary``` (และสามารถใช้ ```c double imaginary``` + ได้เช่นกันหากนำเข้า ``) +- ```c long double _Imaginary``` (และสามารถใช้ ```c long double imaginary``` + ได้เช่นกันหากนำเข้า ``) -*หมายเหตุ:* เหมือนกับประเภทอื่น ๆ สามารถพิมพ์คีย์เวิร์ดในลำดับใดก็ได้ ```c long double imaginary```, ```c imaginary long double``` และแม้แต่ ```c double imaginary long``` นั้นคือประเภทข้อมูลเดียวกัน +*หมายเหตุ:* เหมือนกับประเภทอื่น ๆ สามารถพิมพ์คีย์เวิร์ดในลำดับใดก็ได้ +```c long double imaginary```, ```c imaginary long double``` และแม้แต่ +```c double imaginary long``` นั้นคือประเภทข้อมูลเดียวกัน === ประเภทตัวอักษร (Character) - `signed char`: ประเภทสำหรับตัวอักษรแบบ signed - `unsigned char`: ประเภทสำหรับตัวอักษรแบบ unsigned -- `char`: ประเภทสำหรับตัวอักษรแบบไม่ระบุระยะข้อมูล ซึ่งสามารถเท่ากับ `signed char` หรือ `unsigned char` ก็ได้ขึ้นอยู่กับแพลตฟอร์มและคอมไพเลอร์ แต่อย่างไรก็ตาม `char` นั้นไม่ใช่เพียงแค่มาโครที่ลิงก์ไปยังประเภทอื่น ๆ แต่ `char` คือประเภทของมันเอง +- `char`: ประเภทสำหรับตัวอักษรแบบไม่ระบุระยะข้อมูล ซึ่งสามารถเท่ากับ `signed char` หรือ + `unsigned char` ก็ได้ขึ้นอยู่กับแพลตฟอร์มและคอมไพเลอร์ แต่อย่างไรก็ตาม `char` + นั้นไม่ใช่เพียงแค่มาโครที่ลิงก์ไปยังประเภทอื่น ๆ แต่ `char` คือประเภทของมันเอง === คีย์เวิร์ด -- `bool`, `true`, `false`, `char`, `int`, `short`, `long`, `signed`, `unsigned`, `float`, `double`. -- `_Bool`, `_BitInt`, `_Complex`, `_Imaginary`, `_Decimal32`, `_Decimal64`, `_Decimal128`. +- `bool`, `true`, `false`, `char`, `int`, `short`, `long`, `signed`, `unsigned`, + `float`, `double`. +- `_Bool`, `_BitInt`, `_Complex`, `_Imaginary`, `_Decimal32`, `_Decimal64`, + `_Decimal128`. === ระยะค่าที่เก็บได้ #i ตารางต่อไปนี้ให้ข้อมูลเกี่ยวกับขอบเขตของประเภทข้อมูลต่าง ๆ -#i ก่อนมาตรฐาน C23 มาตรฐาน C อนุญาตการแทนตัวเลขแบบใดก็ได้ และระยะขั้นต่ำของตัวเลข N บิตคือ #math.equation($-(2^(N-1)-1)$, alt: "ลบ 2 ยกกำลัง N ลบ 1 ทั้งหมดลบ 1") ถึง -#math.equation($+2^(N-1)-1$, alt: "บวก 2 ยกกำลัง N ลบ 1 ทั้งหมดลบ 1") (เช่น *-127* ถึง *127* สำหรับประเภทตัวเลข 8 บิต) ซึ่งตรงกับขอบเขตของส่วนเติมเต็มหนึ่ง (one's complement) หรือการแทนจำนวนมีเครื่องหมาย (sign-and-magnitude) +#i ก่อนมาตรฐาน C23 มาตรฐาน C อนุญาตการแทนตัวเลขแบบใดก็ได้ และระยะขั้นต่ำของตัวเลข N บิตคือ +#math.equation($-(2^(N-1)-1)$, alt: "ลบ 2 ยกกำลัง N ลบ 1 ทั้งหมดลบ 1") ถึง +#math.equation($+2^(N-1)-1$, alt: "บวก 2 ยกกำลัง N ลบ 1 ทั้งหมดลบ 1") (เช่น *-127* +ถึง *127* สำหรับประเภทตัวเลข 8 บิต) ซึ่งตรงกับขอบเขตของส่วนเติมเต็มหนึ่ง (one's complement) +หรือการแทนจำนวนมีเครื่องหมาย (sign-and-magnitude) -#i อย่างไรก็ตาม รูปแบบข้อมูลที่ใช้กันอย่างแพร่หลายทั้งหมด (รวมถึง ILP32, LP32, LP64, และ LLP64) และคอมไพเลอร์ C เกือบทั้งหมดใช้การแทนตัวเลขแบบส่วนเติมเต็มสอง (two's complement) (มีข้อยกเว้นที่ทราบแค่บางคอมไพเลอร์สำหรับระบบ UNISYS) และตั้งแต่มาตรฐาน C23 มันคือการแทนตัวเลขแบบเดียวที่ถูกอนุญาตให้ใช้โดยมาตรฐาน และมีขอบเขตที่แน่นอนระหว่าง -#math.equation($-2^(N-1)$, alt: "ลบ 2 ยกกำลัง N ลบ 1") ถึง -#math.equation($+2^(N-1)-1$, alt: "บวก 2 ยกกำลัง N ลบ 1 ทั้งหมดลบ 1") (เช่น *-128* ถึง *127* สำหรับประเภทตัวเลข 8 บิต) +#i อย่างไรก็ตาม รูปแบบข้อมูลที่ใช้กันอย่างแพร่หลายทั้งหมด (รวมถึง ILP32, LP32, LP64, และ +LLP64) และคอมไพเลอร์ C เกือบทั้งหมดใช้การแทนตัวเลขแบบส่วนเติมเต็มสอง (two's complement) +(มีข้อยกเว้นที่ทราบแค่บางคอมไพเลอร์สำหรับระบบ UNISYS) และตั้งแต่มาตรฐาน C23 +มันคือการแทนตัวเลขแบบเดียวที่ถูกอนุญาตให้ใช้โดยมาตรฐาน และมีขอบเขตที่แน่นอนระหว่าง +#math.equation($-2^(N-1)$, alt: "ลบ 2 ยกกำลัง N ลบ 1") ถึง #math.equation( + $+2^(N-1)-1$, + alt: "บวก 2 ยกกำลัง N ลบ 1 ทั้งหมดลบ 1", +) (เช่น *-128* ถึง *127* สำหรับประเภทตัวเลข 8 บิต) (มีการเพิ่มจุลภาคในทศนิยมเพื่อเพิ่มความสะดวกในการอ่าน) #show table.cell.where(x: 0): strong #show math.equation.where(block: true): set block(spacing: 0.6em) -#show math.equation: set text(font: "Noto Sans Math") #set list(indent: 0em) #figure( @@ -416,11 +519,20 @@ float a, b, c; table.cell( [ - min subnormal: - #math.equation($± 1.401,298,4 · 10^(-45)$, alt: "บวกลบ 1.4012984 คูณ 10 ยกกำลัง -45") + #math.equation( + $± 1.401,298,4 · 10^(-45)$, + alt: "บวกลบ 1.4012984 คูณ 10 ยกกำลัง -45", + ) - min normal: - #math.equation($± 1.175,494,3 · 10^(-38)$, alt: "บวกลบ 1.1754943 คูณ 10 ยกกำลัง -38") + #math.equation( + $± 1.175,494,3 · 10^(-38)$, + alt: "บวกลบ 1.1754943 คูณ 10 ยกกำลัง -38", + ) - max: \ - #math.equation($± 3.402,823,4 · 10^(38)$, alt: "บวกลบ 3.4028234 คูณ 10 ยกกำลัง 38") + #math.equation( + $± 3.402,823,4 · 10^(38)$, + alt: "บวกลบ 3.4028234 คูณ 10 ยกกำลัง 38", + ) ], align: left, ), @@ -467,8 +579,7 @@ float a, b, c; `±0x1p-1074` - min normal:\ `±0x1p-1022` - - max: - `±0x1` \ `.fffffffffffffp+1023` + - max: `±0x1` \ `.fffffffffffffp+1023` ], align: left, ), @@ -522,8 +633,7 @@ float a, b, c; `±0x1p-16445` - min normal: `±0x1p-16382` - - max: - `±0x1.ffffffff`\ `fffffffep+16383` + - max: `±0x1.ffffffff`\ `fffffffep+16383` ], align: left, ), @@ -559,8 +669,7 @@ float a, b, c; `±0x1p-16494` - min normal: `±0x1p-16382` - - max: - `±0x1.ffffffffffffff`\ `ffffffffffffffp+16383` + - max: `±0x1.ffffffffffffff`\ `ffffffffffffffp+16383` ], align: left, ), @@ -576,7 +685,10 @@ float a, b, c; - min normal:\ #math.equation($± 1 · 10^(-95)$, alt: "บวกลบ 1 คูณ 10 ยกกำลัง ลบ 95") - max:\ - #math.equation($± 9.999'999 · 10^96$, alt: "บวกลบ 9.999999 คูณ 10 ยกกำลัง 96") + #math.equation( + $± 9.999'999 · 10^96$, + alt: "บวกลบ 9.999999 คูณ 10 ยกกำลัง 96", + ) ], align: left, ), @@ -606,9 +718,15 @@ float a, b, c; table.cell( [ - min subnormal:\ - #math.equation($± 1 · 10^(-6176)$, alt: "บวกลบ 1 คูณ 10 ยกกำลัง ลบ 6176") + #math.equation( + $± 1 · 10^(-6176)$, + alt: "บวกลบ 1 คูณ 10 ยกกำลัง ลบ 6176", + ) - min normal:\ - #math.equation($± 1 · 10^(-6143)$, alt: "บวกลบ 1 คูณ 10 ยกกำลัง ลบ 6143") + #math.equation( + $± 1 · 10^(-6143)$, + alt: "บวกลบ 1 คูณ 10 ยกกำลัง ลบ 6143", + ) - max: #math.equation( $ ± 9.999'999'999'999'999'\ 999'999'999'999'999'999\ · 10^6144 $, @@ -626,10 +744,25 @@ float a, b, c; == ชุดแปลโปรแกรมของกนู (GNU Compiler Collection; GCC) -#i ในกระบวนการการพัฒนาโครงงานนี้ ชุดแปลโปรแกรมของกนูนั้นถูกใช้เป็นหลักเนื่องจากเป็นชุดแปลโปรแกรม (คอมไพเลอร์; Compiler) ที่ใช้เป็นหลักในการพัฒนาโคดที่สร้างบนพื้นฐาน Arduino และบอร์ดต่าง ๆ รวมถึงบอร์ด ESP32 +#i ในกระบวนการการพัฒนาโครงงานนี้ +ชุดแปลโปรแกรมของกนูนั้นถูกใช้เป็นหลักเนื่องจากเป็นชุดแปลโปรแกรม (คอมไพเลอร์; Compiler) +ที่ใช้เป็นหลักในการพัฒนาโคดที่สร้างบนพื้นฐาน Arduino และบอร์ดต่าง ๆ รวมถึงบอร์ด ESP32 -#i ชุดคอมไพเลอร์ GNU (GNU Compiler Collection; GCC) (เดิมชื่อ GNU C Compiler) คือชุดคอมไพเลอร์จากโครงการ GNU ที่รองรับภาษาโปรแกรม สถาปัตยกรรมฮาร์ดแวร์ และระบบปฏิบัติการต่าง ๆ มูลนิธิซอฟต์แวร์เสรี (FSF) เผยแพร่ GCC ในฐานะซอฟต์แวร์เสรีภายใต้สัญญาอนุญาตสถูกเรียกาธารณะทั่วไปของ GNU (GNU GPL) GCC เป็นองค์ประกอบสำคัญของชุดเครื่องมือ GNU ซึ่งใช้สำหรับโครงการส่วนใหญ่ที่เกี่ยวข้องกับ GNU และเคอร์เนล Linux ด้วยโคดประมาณ 15 ล้านบรรทัดในปี 2019 GCC จึงเป็นหนึ่งในโปรแกรมฟรีที่ใหญ่ที่สุดเท่าที่เคยมีมา GCC มีบทบาทสำคัญในการเติบโตของซอฟต์แวร์เสรี ทั้งในฐานะเครื่องมือและตัวอย่าง +#i ชุดคอมไพเลอร์ GNU (GNU Compiler Collection; GCC) (เดิมชื่อ GNU C Compiler) +คือชุดคอมไพเลอร์จากโครงการ GNU ที่รองรับภาษาโปรแกรม สถาปัตยกรรมฮาร์ดแวร์ +และระบบปฏิบัติการต่าง ๆ มูลนิธิซอฟต์แวร์เสรี (FSF) เผยแพร่ GCC +ในฐานะซอฟต์แวร์เสรีภายใต้สัญญาอนุญาตสถูกเรียกาธารณะทั่วไปของ GNU (GNU GPL) GCC +เป็นองค์ประกอบสำคัญของชุดเครื่องมือ GNU ซึ่งใช้สำหรับโครงการส่วนใหญ่ที่เกี่ยวข้องกับ GNU และเคอร์เนล +Linux ด้วยโคดประมาณ 15 ล้านบรรทัดในปี 2019 GCC จึงเป็นหนึ่งในโปรแกรมฟรีที่ใหญ่ที่สุดเท่าที่เคยมีมา +GCC มีบทบาทสำคัญในการเติบโตของซอฟต์แวร์เสรี ทั้งในฐานะเครื่องมือและตัวอย่าง -#i นอกจากจะเป็นคอมไพเลอร์อย่างเป็นทางการของระบบปฏิบัติการ GNU แล้ว GCC ยังได้รับการยอมรับให้เป็นคอมไพเลอร์มาตรฐานโดยระบบปฏิบัติการคอมพิวเตอร์สมัยใหม่ที่คล้ายกับ Unix อื่นๆ อีกมากมาย รวมถึงระบบปฏิบัติการ Linux ส่วนใหญ่ ระบบปฏิบัติการตระกูล BSD ส่วนใหญ่ก็เปลี่ยนมาใช้ GCC ไม่นานหลังจากเปิดตัว แม้ว่าหลังจากนั้น FreeBSD และ Apple macOS ได้เปลี่ยนมาใช้คอมไพเลอร์ Clang ส่วนใหญ่เป็นเพราะเหตุผลด้านลิขสิทธิ์ GCC ยังสามารถคอมไพเลอร์โคดสำหรับระบบปฏิบัติการ Windows, Android, iOS, Solaris, HP-UX, AIX และ MS-DOS ได้อีกด้วย +#i นอกจากจะเป็นคอมไพเลอร์อย่างเป็นทางการของระบบปฏิบัติการ GNU แล้ว GCC +ยังได้รับการยอมรับให้เป็นคอมไพเลอร์มาตรฐานโดยระบบปฏิบัติการคอมพิวเตอร์สมัยใหม่ที่คล้ายกับ Unix อื่นๆ +อีกมากมาย รวมถึงระบบปฏิบัติการ Linux ส่วนใหญ่ ระบบปฏิบัติการตระกูล BSD ส่วนใหญ่ก็เปลี่ยนมาใช้ GCC +ไม่นานหลังจากเปิดตัว แม้ว่าหลังจากนั้น FreeBSD และ Apple macOS ได้เปลี่ยนมาใช้คอมไพเลอร์ Clang +ส่วนใหญ่เป็นเพราะเหตุผลด้านลิขสิทธิ์ GCC ยังสามารถคอมไพเลอร์โคดสำหรับระบบปฏิบัติการ Windows, +Android, iOS, Solaris, HP-UX, AIX และ MS-DOS ได้อีกด้วย -#i GCC ได้รับการพอร์ตไปยังแพลตฟอร์มและสถาปัตยกรรมชุดคำสั่งต่าง ๆ มากกว่าคอมไพเลอร์อื่น ๆ และถูกนำไปใช้งานอย่างกว้างขวางในฐานะเครื่องมือในการพัฒนาซอฟต์แวร์ทั้งแบบฟรีและแบบที่เป็นกรรมสิทธิ์ นอกจากนี้ GCC ยังพร้อมใช้งานสำหรับระบบฝังตัวมากมาย รวมถึงชิปที่ใช้ ARM และ Power ISA +#i GCC ได้รับการพอร์ตไปยังแพลตฟอร์มและสถาปัตยกรรมชุดคำสั่งต่าง ๆ มากกว่าคอมไพเลอร์อื่น ๆ +และถูกนำไปใช้งานอย่างกว้างขวางในฐานะเครื่องมือในการพัฒนาซอฟต์แวร์ทั้งแบบฟรีและแบบที่เป็นกรรมสิทธิ์ +นอกจากนี้ GCC ยังพร้อมใช้งานสำหรับระบบฝังตัวมากมาย รวมถึงชิปที่ใช้ ARM และ Power ISA diff --git a/Chapter2/Chapter2.typ b/Chapter2/Chapter2.typ index 6ac7047..5540bf3 100644 --- a/Chapter2/Chapter2.typ +++ b/Chapter2/Chapter2.typ @@ -5,6 +5,11 @@ #show heading: i-figured.reset-counters.with(level: 3) #show figure: i-figured.show-figure.with(level: 3) +#show raw.where(block: false): set text(font: "Laksaman") +#show raw.where(block: true): set block(fill: rgb("#282A36"), inset: 1.5em) +#show raw.where(block: true): set raw(theme: "../Dracula.tmTheme") +#show raw.where(block: true): set text(fill: rgb("#F8F8F2")) + #set heading(numbering: "บทที่ 1") #include "Intro.typ" @@ -16,6 +21,9 @@ #include "HTTPS.typ" #include "TLS.typ" #include "NFC.typ" + +#pagebreak() + #include "Flutter.typ" #include "Git.typ" #include "CLanguage.typ" diff --git a/Chapter2/Flutter.typ b/Chapter2/Flutter.typ index f6134fd..73db069 100644 --- a/Chapter2/Flutter.typ +++ b/Chapter2/Flutter.typ @@ -9,8 +9,8 @@ สามารถใช้พัฒนาแอปพลิเคชันข้ามแพลตฟอร์มจากฐานโคดเดียวสำหรับเว็บ Fuchsia, Android, iOS, Linux, macOS และ Windows โดย Flutter ได้รับการพูดถึงครั้งแรกในปี 2015 และเปิดตัวในเดือนพฤษภาคม 2017 และ Flutter ถูกใช้งานภายในโดย Google ในแอปพลิเคชันต่างๆ -เช่น Google Pay และ Google Earth รวมถึงโดยนักพัฒนาซอฟต์แวร์รายอื่นๆ เช่น ByteDance -และ Alibaba +เช่น Google Pay และ Google Earth รวมถึงโดยนักพัฒนาซอฟต์แวร์รายอื่นๆ เช่น ByteDance และ +Alibaba #i Flutter จะสร้างแอปพลิเคชันที่มีเอ็นจิ้นการเรนเดอร์ของตัวเอง ซึ่งส่งข้อมูลพิกเซลไปยังหน้าจอโดยตรง ซึ่งแตกต่างจากเฟรมเวิร์ก UI อื่น ๆ อีกมากมายที่อาศัยแพลตฟอร์มเป้าหมายเพื่อจัดหาเอ็นจิ้นการเรนเดอร์ @@ -19,26 +19,30 @@ Linux, macOS และ Windows โดย Flutter ได้รับการพ ช่วยลดความยุ่งยากในการรองรับหลายแพลตฟอร์ม เนื่องจากสามารถใช้โคด UI ที่เหมือนกันได้กับทุกแพลตฟอร์มเป้าหมาย -#pagebreak() - == การติดตั้งโปรแกรมเขียนโคด -#i จริง ๆ แล้วนั้น Flutter สามารถทำงานกับโปรแกรมเขียนโคดใดก็ได้ แต่มีโปรแกรมเหล่านี้ที่อาจมีประสบการณ์การพัฒนาที่ดีกว่าโปรแกรมอื่น: +#i จริง ๆ แล้วนั้น Flutter สามารถทำงานกับโปรแกรมเขียนโคดใดก็ได้ +แต่มีโปรแกรมเหล่านี้ที่อาจมีประสบการณ์การพัฒนาที่ดีกว่าโปรแกรมอื่น: - Visual Studio Code (VS Code) - Android Studio - JetBrains IntelliJ - Firebase Studio -#i โครงงานนี้ใช้โปรแกรมเขียนโคด Android Studio เป็นหลักเนื่องจากแอพลิเคชันโครงงานมี Android เป็นเป้าหมายหลัก และ Android SDK สามารถจัดการได้ง่ายกว่าใน Android Studio +#i โครงงานนี้ใช้โปรแกรมเขียนโคด Android Studio เป็นหลักเนื่องจากแอพลิเคชันโครงงานมี Android +เป็นเป้าหมายหลัก และ Android SDK สามารถจัดการได้ง่ายกว่าใน Android Studio === Android Studio -#i Android Studio สามารถดาวน์โหลดได้ผ่าน https://developer.android.com/studio หรือสามารถถูกติดตั้งและจัดการผ่านแอพลิเคชัน JetBrains Toolbox ได้เช่นกัน (https://www.jetbrains.com/toolbox-app/) +#i Android Studio สามารถดาวน์โหลดได้ผ่าน https://developer.android.com/studio +หรือสามารถถูกติดตั้งและจัดการผ่านแอพลิเคชัน JetBrains Toolbox ได้เช่นกัน +(https://www.jetbrains.com/toolbox-app/) == การติดตั้ง Flutter -#i การติดตั้ง Flutter สามารถทำได้สองวิธีด้วยกัน คือการติดตั้งผ่าน Visual Studio Code (VS Code) และการติดตั้งด้วยตนเอง โดยหากต้องการใช้ VS Code เป็นโปรแกรมเขียนโคดอยู่แล้ว สามารถติดตั้งผ่าน VS Code ได้เลย +#i การติดตั้ง Flutter สามารถทำได้สองวิธีด้วยกัน คือการติดตั้งผ่าน Visual Studio Code (VS +Code) และการติดตั้งด้วยตนเอง โดยหากต้องการใช้ VS Code เป็นโปรแกรมเขียนโคดอยู่แล้ว +สามารถติดตั้งผ่าน VS Code ได้เลย #i แต่ก่อนอื่น ต้องทำการติดตั้งโปรแกรมและไลบรารีพื้นฐานที่จำเป็นสำหรับ Flutter ก่อน @@ -49,8 +53,15 @@ Linux, macOS และ Windows โดย Flutter ได้รับการพ #grid( columns: 2, column-gutter: 1em, - [#i ในการพัฒนาซอฟต์แวร์บน Windows ด้วย Flutter คุณจำเป็นต้องติดตั้ง Git สำหรับ Windows ซึ่งคุณสามารถดูขั้นตอนการติดตั้งได้โดยการสแกน QR code ด้านข้าง หรือที่ https://git-scm.com/install/windows หรือเพียงแค่ใช้ WinGet ในการติดตั้งโดยการใช้คำสั่งด้านล่าง], - tiaoma.qrcode("https://git-scm.com/install/windows", width: 1in, alt: "QR โคดสำหรับหน้าการติดตั้ง Git สำหรับ Windows"), + [#i ในการพัฒนาซอฟต์แวร์บน Windows ด้วย Flutter คุณจำเป็นต้องติดตั้ง Git สำหรับ Windows + ซึ่งคุณสามารถดูขั้นตอนการติดตั้งได้โดยการสแกน QR code ด้านข้าง หรือที่ + https://git-scm.com/install/windows หรือเพียงแค่ใช้ WinGet + ในการติดตั้งโดยการใช้คำสั่งด้านล่าง], + tiaoma.qrcode( + "https://git-scm.com/install/windows", + width: 1in, + alt: "QR โคดสำหรับหน้าการติดตั้ง Git สำหรับ Windows", + ), ) ```sh @@ -59,7 +70,8 @@ winget install --id Git.Git -e --source winget ==== Linux -#i Flutter ใช้ไลบรารีดังต่อไปนี้ในขั้นตอนการพัฒนาแอพลิเคชันบน Linux (development dependencies; ยังไม่รวมไลบรารีและโปรแกรมที่ต้องมีในการสร้างแอพลิเคชัน _สำหรับ_ Linux) +#i Flutter ใช้ไลบรารีดังต่อไปนี้ในขั้นตอนการพัฒนาแอพลิเคชันบน Linux (development +dependencies; ยังไม่รวมไลบรารีและโปรแกรมที่ต้องมีในการสร้างแอพลิเคชัน _สำหรับ_ Linux) #grid( columns: 2, @@ -80,7 +92,8 @@ winget install --id Git.Git -e --source winget ==== macOS -#i จำเป็นต้องทำการติดตั้งเครื่องมือ command-line Xcode เพื่อเข้าถึงเครื่องมือที่ Flutter จำเป็นต้องใช้ รวมถึง Git +#i จำเป็นต้องทำการติดตั้งเครื่องมือ command-line Xcode เพื่อเข้าถึงเครื่องมือที่ Flutter +จำเป็นต้องใช้ รวมถึง Git ในการดาวน์โหลดเครื่องมือ ใช้คำสั่งต่อไปนี้ในเทอร์มินัลที่คุณเลือก: @@ -88,40 +101,51 @@ winget install --id Git.Git -e --source winget xcode-select --install ``` -#i หากคุณไม่ได้ติดตั้งเครื่องมืออยู่แล้ว จะมีไดอะลอกเพื่อคอนเฟิร์มว่าคุณต้องการที่จะติดตั้งมัน กด *Install* และกด *Done* เมื่อทำการติดตั้งเสร็จสิ้นแล้ว - -=== การติดตั้งผ่าน Visual Studio Code - -1. เปิด VSCode -2. ติดตั้งส่วนขยาย Flutter \ - อยู่ภายใต้ ID `Dart-Code.flutter` ทั้งบน Visual Studio Marketplace และ OpenVSX -3. ติดตั้ง Flutter ด้วย VS Code - + เปิด Command Palette ด้วยเมนู *View* > *Command Palette* หรือกด Ctrl + Shift + P - + ใน Command Palette พิมพ์ `flutter`. - + เลือก *Flutter: New Project* - + VS Code จะให้คุณเลือก Flutter SDK บนคอมพิวเตอร์ของคุณ เลือก *Download SDK* - + เมือหน้าไดอะลอก *Select Folder for Flutter SDK* แสดงขึ้น เลือกสถานที่ที่คุณอยากติดตั้ง Flutter - + คลิก *Clone Flutter* \ - ในระหว่างการดาวน์โหลด VS Code จะแสดงการแจ้งเตือนนี้: - ``` - Downloading the Flutter SDK. This may take a few minutes. - ``` - การดาวน์โหลดนี้จะใช้เวลาสองสามนาที หากคุณเชื่อว่าการดาวน์โหลดหยุดชะงัก คุณสามารถคลิก *Cancel* แล้วเริ่มต้นการติดตั้งใหม่ได้ - + คลิก *Add SDK to PATH* \ - เมื่อเสร็จสิ้น จะมีการแจ้งเตือน - ``` - The Flutter SDK was added to your PATH - ``` - + VS Code อาจแสดงการแจ้งเตือนเกี่ยวกับการเก็บข้อมูลของ Google หากคุณยินยอม คลิก *OK* - + เพื่อความแน่ใจ กรุณาปิดเทอร์มินัลทุกหน้าต่างหรือรีสตาร์ท VS Code เพื่อให้แน่ใจว่า Flutter จะสามารถใช้ผ่านเทอร์มินัลได้ -4. เมื่อเสร็จสิ้น ใช้คำสั่ง `flutter doctor -v` ในเทอร์มินัลที่คุณเลือกเพื่อตรวจสอบการติดตั้ง Flutter ของคุณ \ - หากคำสั่งไม่เจอหรือเกิดข้อผิดพลาดขึ้น ตรวจสอบ https://docs.flutter.dev/install/troubleshoot สำหรับข้อมูลเพิ่มเติม +#i หากคุณไม่ได้ติดตั้งเครื่องมืออยู่แล้ว จะมีไดอะลอกเพื่อคอนเฟิร์มว่าคุณต้องการที่จะติดตั้งมัน กด +*Install* และกด *Done* เมื่อทำการติดตั้งเสร็จสิ้นแล้ว #pagebreak() +=== การติดตั้งผ่าน Visual Studio Code + +#[ + #set enum(indent: 3em) + + เปิด VSCode + + ติดตั้งส่วนขยาย Flutter \ + อยู่ภายใต้ ID `Dart-Code.flutter` ทั้งบน Visual Studio Marketplace และ OpenVSX + + ติดตั้ง Flutter ด้วย VS Code + + เปิด Command Palette ด้วยเมนู *View* > *Command Palette* หรือกด Ctrl + Shift + + P + + ใน Command Palette พิมพ์ `flutter`. + + เลือก *Flutter: New Project* + + VS Code จะให้คุณเลือก Flutter SDK บนคอมพิวเตอร์ของคุณ เลือก *Download SDK* + + เมือหน้าไดอะลอก *Select Folder for Flutter SDK* แสดงขึ้น เลือกสถานที่ที่คุณอยากติดตั้ง + Flutter + + คลิก *Clone Flutter* \ + ในระหว่างการดาวน์โหลด VS Code จะแสดงการแจ้งเตือนนี้: + ``` + Downloading the Flutter SDK. This may take a few minutes. + ``` + การดาวน์โหลดนี้จะใช้เวลาสองสามนาที หากคุณเชื่อว่าการดาวน์โหลดหยุดชะงัก คุณสามารถคลิก + *Cancel* แล้วเริ่มต้นการติดตั้งใหม่ได้ + + คลิก *Add SDK to PATH* \ + เมื่อเสร็จสิ้น จะมีการแจ้งเตือน + ``` + The Flutter SDK was added to your PATH + ``` + + VS Code อาจแสดงการแจ้งเตือนเกี่ยวกับการเก็บข้อมูลของ Google หากคุณยินยอม คลิก *OK* + + เพื่อความแน่ใจ กรุณาปิดเทอร์มินัลทุกหน้าต่างหรือรีสตาร์ท VS Code เพื่อให้แน่ใจว่า Flutter + จะสามารถใช้ผ่านเทอร์มินัลได้ + + เมื่อเสร็จสิ้น ใช้คำสั่ง `flutter doctor -v` ในเทอร์มินัลที่คุณเลือกเพื่อตรวจสอบการติดตั้ง + Flutter ของคุณ \ + หากคำสั่งไม่เจอหรือเกิดข้อผิดพลาดขึ้น ตรวจสอบ + https://docs.flutter.dev/install/troubleshoot สำหรับข้อมูลเพิ่มเติม +] + === การติดตั้งด้วยตนเอง -#i แนะนำให้ทำตาม https://docs.flutter.dev/install/manual#install-flutter เนื่องจากกระบวนการนี้ต้องใช้ข้อมูลที่ใหม่ล่าสุด +#i แนะนำให้ทำตาม https://docs.flutter.dev/install/manual#install-flutter +เนื่องจากกระบวนการนี้ต้องใช้ข้อมูลที่ใหม่ล่าสุด 1. ดาวน์โหลด Flutter (สามารถหาปุ่มดาวน์โหลดได้จากลิงก์ด้านบน) 2. สร้างโฟลเดอร์สำหรับเก็บ Flutter SDK @@ -131,29 +155,36 @@ xcode-select --install == Dart -#i Dart เป็นภาษาโปรแกรมที่ออกแบบโดย Lars Bak และ Kasper Lund และพัฒนาโดย -Google สามารถใช้พัฒนาแอปพลิเคชันบนเว็บ มือถือ เซิร์ฟเวอร์ และเดสก์ท็อปได้ และยังเป็นภาษาหลักที่ใช้ในการพัฒนาแอพลิเคชัน Flutter +#i Dart เป็นภาษาโปรแกรมที่ออกแบบโดย Lars Bak และ Kasper Lund และพัฒนาโดย Google +สามารถใช้พัฒนาแอปพลิเคชันบนเว็บ มือถือ เซิร์ฟเวอร์ และเดสก์ท็อปได้ +และยังเป็นภาษาหลักที่ใช้ในการพัฒนาแอพลิเคชัน Flutter -#i Dart เป็นภาษาเชิงวัตถุ อิงคลาส และรวบรวมขยะ (garbage-collection) ด้วยไวยากรณ์แบบ C สามารถคอมไพล์เป็นโค้ดเครื่อง JavaScript หรือ WebAssembly ได้ รองรับอินเทอร์เฟซ มิกซ์อิน คลาสนามธรรม เจเนอริกแบบรีไฟด์ และการอนุมานชนิดข้อมูล +#i Dart เป็นภาษาเชิงวัตถุ อิงคลาส และรวบรวมขยะ (garbage-collection) ด้วยไวยากรณ์แบบ C +สามารถคอมไพล์เป็นโค้ดเครื่อง JavaScript หรือ WebAssembly ได้ รองรับอินเทอร์เฟซ มิกซ์อิน +คลาสนามธรรม เจเนอริกแบบรีไฟด์ และการอนุมานชนิดข้อมูล == การสร้างโปรเจกต์ -#i ตั้งแต่หัวข้อนี้เป็นต้นไป จะเป็นข้อมูลสำหรับการทำงานกับ Android Studio เป็นหลักเนื่องจากเป็นโปรแกรมหลักที่ถูกใช้งานในการพัฒนาแอพลิเคชันโครงงานนี้ +#i ตั้งแต่หัวข้อนี้เป็นต้นไป จะเป็นข้อมูลสำหรับการทำงานกับ Android Studio +เป็นหลักเนื่องจากเป็นโปรแกรมหลักที่ถูกใช้งานในการพัฒนาแอพลิเคชันโครงงานนี้ -#i หากยังไม่ได้ติดตั้งปลั๊กอิน Flutter โปรดติดตั้งปลั๊กอินก่อน โดยหากอยู่ในหน้าต้อนรับ สามารถติดตั้งปลั๊กอินได้โดยการเข้าไปยังแท็บ *Plugins* หรือหากเปิดโปรเจกต์อื่นอยู่ สามารถเข้าถึงหน้าปลั๊กอินได้โดยการกดที่ไอคอนฟันเฟืองในแถบเครื่องมือ แล้วกด *Plugins...* หลังจากนั้น ในแท็บ *Marketplace* ของหน้าปลั๊กอิน ค้นหา *Flutter* (ผู้ผลิตปลั๊กอินคือ Google) แล้วกด *Install* +#i หากยังไม่ได้ติดตั้งปลั๊กอิน Flutter โปรดติดตั้งปลั๊กอินก่อน โดยหากอยู่ในหน้าต้อนรับ +สามารถติดตั้งปลั๊กอินได้โดยการเข้าไปยังแท็บ *Plugins* หรือหากเปิดโปรเจกต์อื่นอยู่ +สามารถเข้าถึงหน้าปลั๊กอินได้โดยการกดที่ไอคอนฟันเฟืองในแถบเครื่องมือ แล้วกด *Plugins...* +หลังจากนั้น ในแท็บ *Marketplace* ของหน้าปลั๊กอิน ค้นหา *Flutter* (ผู้ผลิตปลั๊กอินคือ Google) +แล้วกด *Install* #afigure( image("Flutter/homePage.png", width: 65%), - attr: "ส่วนหนึ่งของโครงงาน, ศตคุณ อุตมะ, ภายใต้ CC BY-SA 4.0", alt: "หน้ายินดีต้อนรับในแท็บ Projects ที่กำลังแสดงรายการโปรเจกต์และปุ่มในการสร้างโปรเจกต์ใหม่", caption: [หน้ายินดีต้อนรับใน Android Studio], ) -#i เมื่อคลิก *New Flutter Project* จะมีหน้าถามสถานที่ติดตั้ง Flutter SDK หลังจากนั้น กด *Next* แล้วจะมีหน้าต่อไปนี้ขึ้นมาเพื่อให้คุณกรอกรายละเอียดโปรเจกต์ +#i เมื่อคลิก *New Flutter Project* จะมีหน้าถามสถานที่ติดตั้ง Flutter SDK หลังจากนั้น กด +*Next* แล้วจะมีหน้าต่อไปนี้ขึ้นมาเพื่อให้คุณกรอกรายละเอียดโปรเจกต์ #afigure( image("Flutter/newProjectPage.png", width: 80%), - attr: "ส่วนหนึ่งของโครงงาน, ศตคุณ อุตมะ, ภายใต้ CC BY-SA 4.0", alt: "หน้ากรอกรายละเอียดโปรเจกต์ใหม่", caption: [หน้าโปรเจกต์ใหม่], ) @@ -163,16 +194,26 @@ Google สามารถใช้พัฒนาแอปพลิเคชั - *Project name:* ชื่อโปรเจกต์ - *Project location:* โฟลเดอร์ที่ต้องการเก็บโปรเจกต์ - *Description:* รายละเอียดโปรเจกต์ -- *Project type:* ประเภทโปรเจกต์ ในกรณีนี้เป็นค่า *Application* เนื่องจากเราต้องการสร้างแอพลิเคชัน -- *Organization:* โดเมนเนมย้อนหลังขององค์กรที่พัฒนา (Reverse domain name notation; Reverse-DNS) +- *Project type:* ประเภทโปรเจกต์ ในกรณีนี้เป็นค่า *Application* + เนื่องจากเราต้องการสร้างแอพลิเคชัน +- *Organization:* โดเมนเนมย้อนหลังขององค์กรที่พัฒนา (Reverse domain name notation; + Reverse-DNS) - *Android language:* เลือกระหว่าง Java และ Kotlin เป็นภาษาหลักที่ใช้ในแอพลิเคชัน Android -- *Platforms:* แพลตฟอร์มที่โปรเจกต์จะรองรับ อย่างไรก็ตาม การสร้างไฟล์ไบนารีสำหรับแอพลิเคชันขึ้นอยู่กับแพลตฟอร์มที่พัฒนาแอพลิเคชันเช่นกัน หมายความว่า ถึงแม้ตามทฤษฎีแล้วแอพลิเคชันของคุณจะรองรับ iOS คุณต้องมีอุปกรณ์ Mac ในการสร้างไฟล์แอพลิเคชัน iOS ออกมา +- *Platforms:* แพลตฟอร์มที่โปรเจกต์จะรองรับ อย่างไรก็ตาม + การสร้างไฟล์ไบนารีสำหรับแอพลิเคชันขึ้นอยู่กับแพลตฟอร์มที่พัฒนาแอพลิเคชันเช่นกัน หมายความว่า + ถึงแม้ตามทฤษฎีแล้วแอพลิเคชันของคุณจะรองรับ iOS คุณต้องมีอุปกรณ์ Mac ในการสร้างไฟล์แอพลิเคชัน + iOS ออกมา เมื่อทำการใส่รายละเอียดทั้งหมดแล้ว สามารถกด Create เพื่อสร้างโปรเจกต์ได้เลย +#pagebreak() + == แอพลิเคชันตัวอย่าง -เมื่อกดรันแอพลิเคชันด้วยไอคอน #box(image("Flutter/vscode_play.svg", alt: "Play"), baseline: 15%) (หรือ Shift+F10 ใน Android Studio) จะได้แอพลิเคชันดังรูปด้านล่างออกมา +เมื่อกดรันแอพลิเคชันด้วยไอคอน #box( + image("Flutter/vscode_play.svg", alt: "Play"), + baseline: 15%, +) (หรือ Shift+F10 ใน Android Studio) จะได้แอพลิเคชันดังรูปด้านล่างออกมา #afigure( grid( @@ -189,7 +230,6 @@ Google สามารถใช้พัฒนาแอปพลิเคชั alt: "โปรแกรมคอมพิวเตอร์ แถบหน้าต่างโปรแกรมสีดำ ในหน้าต่างประกอบด้วยส่วนประกอบคล้ายแอพลิเคชันบนโทรศัพท์", ), ), - attr: "ส่วนหนึ่งของโครงงาน, ศตคุณ อุตมะ, ภายใต้ CC BY-SA 4.0", caption: [แอพลิเคชันตัวอย่างบน Android 15 และ Arch Linux], ) @@ -201,166 +241,258 @@ _*หมายเหตุ:* โคดในห้วข้อนี้ถูก โดยในบรรทัดแรก จะมีการนำเข้า Material UI -```dart -import 'package:flutter/material.dart'; -``` +#set figure(kind: "image", supplement: "รูปที่") -และถัดมา จะมีฟังก์ชันหลักชื่อ `main` ที่ทำหน้าที่ในการรันแอพลิเคชัน โดยมีการรับอาร์กิวเมนต์เป็นวิดเจ็ท ซึ่งในกรณีนี้เป็นการสร้างวัตถุจากคลาส `MyApp` ที่เป็นวิดเจ็ท +#afigure( + ```dart + import 'package:flutter/material.dart'; + ```, + caption: [โคดนำเข้าแพคเกจ], +) -```dart -void main() { - runApp(const MyApp()); -} -``` +และถัดมา จะมีฟังก์ชันหลักชื่อ `main` ที่ทำหน้าที่ในการรันแอพลิเคชัน โดยมีการรับอาร์กิวเมนต์เป็นวิดเจ็ท +ซึ่งในกรณีนี้เป็นการสร้างวัตถุจากคลาส `MyApp` ที่เป็นวิดเจ็ท -ถัดมาจะมีคลาส `MyApp` ที่สืบทอดมาจาก `StatelessWidget` ซึ่งคือคลาสสำหรับวิดเจ็ทที่ไร้สถานะ ("Stateless") +#afigure( + ```dart + void main() { + runApp(const MyApp()); + } + ```, + caption: [ฟังก์ชัน main], +) -```dart -class MyApp extends StatelessWidget { -``` +ถัดมาจะมีคลาส `MyApp` ที่สืบทอดมาจาก `StatelessWidget` ซึ่งคือคลาสสำหรับวิดเจ็ทที่ไร้สถานะ +("Stateless") + +#afigure( + ```dart + class MyApp extends StatelessWidget { + ```, + caption: [], +) ซึ่งในคลาสจะมี constructor ที่สามารถรับค่าคีย์ได้: -```dart - const MyApp({super.key}); -``` +#afigure( + ```dart + const MyApp({super.key}); + ```, + caption: [], +) และจะมีฟังก์ชันในการสร้างวิดเจ็ท ซึ่งฟังก์ชันนี้เป็นการสืบทอดฟังก์ชันมา สังเกตได้จาก `@override` -```dart - @override - Widget build(BuildContext context) { -``` +#afigure( + ```dart + @override + Widget build(BuildContext context) { + ```, + caption: [], +) -และในฟังก์ชันจะมีการสร้างวัตถุ MaterialApp ซึ่งมีหน้าที่ในการเก็บข้อมูลเกี่ยวกับแอพลิเคชัน Material UI รวมถึงข้อมูลเช่น ชื่อแอพลิเคชัน (`title`) และธีม (`theme`) +และในฟังก์ชันจะมีการสร้างวัตถุ MaterialApp ซึ่งมีหน้าที่ในการเก็บข้อมูลเกี่ยวกับแอพลิเคชัน Material +UI รวมถึงข้อมูลเช่น ชื่อแอพลิเคชัน (`title`) และธีม (`theme`) -```dart - return MaterialApp( - title: 'Flutter Demo', -``` +#afigure( + ```dart + return MaterialApp( + title: 'Flutter Demo', + ```, + caption: [], +) โดยข้อมูลธีมนั้นถูกเก็บด้วยการสร้างวัตถุ `ThemeData` -```dart - theme: ThemeData( -``` +#afigure( + ```dart + theme: ThemeData( + ```, + caption: [], +) -และมีการสร้างธีมสีจากสีหลักโดยการใช้เมธอด `ColorScheme.fromSeed` แต่ในโคดด้านล่างนี้ -คลาส `ColorScheme` นั้นถูกอนุมานขึ้นมา โดยใน Dart 3.10 มีฟีเจอร์ "dot shorthands" ซึ่งเป็นทางลัดในการข้ามการเขียนชื่อคลาส ซึ่งชื่อคลาสสามารถถูกอนุมานขึ้นมาได้เนื่องจากอาร์กิวเมนต์ `colorScheme` นั้นคาดหวังค่าที่เป็นวัตถุจากคลาส `ColorScheme` อยู่แล้ว +และมีการสร้างธีมสีจากสีหลักโดยการใช้เมธอด `ColorScheme.fromSeed` แต่ในโคดด้านล่างนี้ คลาส +`ColorScheme` นั้นถูกอนุมานขึ้นมา โดยใน Dart 3.10 มีฟีเจอร์ "dot shorthands" +ซึ่งเป็นทางลัดในการข้ามการเขียนชื่อคลาส ซึ่งชื่อคลาสสามารถถูกอนุมานขึ้นมาได้เนื่องจากอาร์กิวเมนต์ +`colorScheme` นั้นคาดหวังค่าที่เป็นวัตถุจากคลาส `ColorScheme` อยู่แล้ว -```dart - colorScheme: .fromSeed(seedColor: Colors.deepPurple), - ), -``` +#afigure( + ```dart + colorScheme: .fromSeed(seedColor: Colors.deepPurple), + ), + ```, + caption: [], +) และต่อมา อาร์กิวเมนต์ `home` เป็นอาร์กิวเมนต์ที่รับค่าเป็นวิดเจ็ทซึ่งจะเป็นหน้าแรกของแอพลิเคชัน -```dart - home: const MyHomePage(title: 'Flutter Demo Home Page'), - ); +#afigure( + ```dart + home: const MyHomePage(title: 'Flutter Demo Home Page'), + ); + } } -} -``` + ```, + kind: "image", + supplement: "รูปที่", + caption: [], +) -ถัดมา มีการสร้างคลาส `MyHomePage` ที่ถูกใช้ด้านบน โดยคลาสเป็น `StatefulWidget` ซึ่งหมายความว่า เป็นคลาสที่มีสถานะ (State) +ถัดมา มีการสร้างคลาส `MyHomePage` ที่ถูกใช้ด้านบน โดยคลาสเป็น `StatefulWidget` +ซึ่งหมายความว่า เป็นคลาสที่มีสถานะ (State) -```dart -class MyHomePage extends StatefulWidget { -``` +#afigure( + ```dart + class MyHomePage extends StatefulWidget { + ```, + caption: [], +) โดยใน constructor มีการรับค่าพารามีเตอร์ `title` -```dart - const MyHomePage({super.key, required this.title}); -``` +#afigure( + ```dart + const MyHomePage({super.key, required this.title}); + ```, + caption: [], +) -โดยคลาสนี้เป็นคลาสที่เก็บการตั้งค่าวิดเจ็ท และเป็นคลาสที่เก็บค่าพารามิเตอร์จากวิดเจ็ทที่เหนือกว่า และตัวแปรในคลาสย่อยของ `StatefulWidget` นั้นจะมีคีย์เวิร์ด `final` เสมอ ซึ่งหมายถึงว่า ตัวแปรนี้เปลี่ยนแปลงไม่ได้หลังจากสร้างวัตถุจากคลาสแล้ว +โดยคลาสนี้เป็นคลาสที่เก็บการตั้งค่าวิดเจ็ท และเป็นคลาสที่เก็บค่าพารามิเตอร์จากวิดเจ็ทที่เหนือกว่า +และตัวแปรในคลาสย่อยของ `StatefulWidget` นั้นจะมีคีย์เวิร์ด `final` เสมอ ซึ่งหมายถึงว่า +ตัวแปรนี้เปลี่ยนแปลงไม่ได้หลังจากสร้างวัตถุจากคลาสแล้ว -```dart - final String title; -``` +#afigure( + ```dart + final String title; + ```, + caption: [], +) -ต่อมาจึงมีการสร้างวัตถุ State ซึ่งในฟังก์ชัน `createState` ที่ถูกสืบทอดมานั้น จะมีการสร้างวัตถุ State จากคลาส `_MyHomePageState` (โดยที่ `_` หมายถึงว่าคลาสนั้นเป็นส่วนตัวและไม่ควรถูกนำเข้าโดยไฟล์อื่น) +ต่อมาจึงมีการสร้างวัตถุ State ซึ่งในฟังก์ชัน `createState` ที่ถูกสืบทอดมานั้น จะมีการสร้างวัตถุ State +จากคลาส `_MyHomePageState` (โดยที่ `_` +หมายถึงว่าคลาสนั้นเป็นส่วนตัวและไม่ควรถูกนำเข้าโดยไฟล์อื่น) -```dart - @override - State createState() => _MyHomePageState(); -} -``` - -```dart -class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { -``` - -โดยในฟังก์ชัน `_incrementCounter` มีการเรียกใช้ฟังก์ชัน `setState` ซึ่งจะแจ้งเตือน Flutter ว่ามีการเปลี่ยนแปลงสถานะ ซึ่งจะก่อให้เกิดการเร็นเดอร์วิดเจ็ทใหม่ และในอาร์กิวเมนต์ของ `setState` คือฟังก์ชันไม่ระบุชื่อ (anonymous functions หรืออีกชื่อหนึ่งคือ lambda) ที่เปลี่ยนแปลงสถานะของวิดเจ็ท - -```dart - setState(() { - _counter++; - }); +#afigure( + ```dart + @override + State createState() => _MyHomePageState(); } -``` + ```, + caption: [], +) -โดยต่อมา มีฟังก์ชัน `build` เช่นเคยที่มีหน้าที่ในการสร้างวิดเจ็ท โดยฟังก์ชัน `build` ของวิดเจ็ทที่มีสถานะนั้นจะถูกรันใหม่ทุกครั้งที่มีการเปลี่ยนแปลงสถานะ +#afigure( + ```dart + class _MyHomePageState extends State { + int _counter = 0; -```dart - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( -``` + void _incrementCounter() { + ```, + caption: [], +) -*การทดลอง:* ในระหว่างที่รันแอพลิเคชันอยู่ ลองเปลี่ยนสีพื้นหลังของวิดเจ็ท `AppBar` (เป็นค่าอื่นเช่น `Colors.amber`) แล้วทำการ hot reload โดยการกดไอคอน#box(image("Flutter/flutterHotReload.svg", alt: "สายฟ้า", width: 1.5em), baseline: 20%)เพื่อเห็นการเปลี่ยนแปลงของสีแถบแอพลิเคชันในระหว่างที่สีของวิดเจ็ทอื่น ๆ ยังคงเดิม +โดยในฟังก์ชัน `_incrementCounter` มีการเรียกใช้ฟังก์ชัน `setState` ซึ่งจะแจ้งเตือน Flutter +ว่ามีการเปลี่ยนแปลงสถานะ ซึ่งจะก่อให้เกิดการเร็นเดอร์วิดเจ็ทใหม่ และในอาร์กิวเมนต์ของ `setState` +คือฟังก์ชันไม่ระบุชื่อ (anonymous functions หรืออีกชื่อหนึ่งคือ lambda) ที่เปลี่ยนแปลงสถานะของวิดเจ็ท -```dart - backgroundColor: Theme.of(context).colorScheme.inversePrimary, -``` +#afigure( + ```dart + setState(() { + _counter++; + }); + } + ```, + caption: [], +) -โดยในอาร์กิวเมนต์ `title` นี้มีการใช้ค่าที่รับมาจากวิดเจ็ทที่เหนือกว่า โดย `widget` ในที่นี้คือวัตถุของคลาส `MyHomePage` ที่ทำหน้าที่เก็บค่า `title` ของเรา แล้วเราจึงทำค่า `title` นั้นใส่ในวิดเจ็ทข้อความ (`Text`) +โดยต่อมา มีฟังก์ชัน `build` เช่นเคยที่มีหน้าที่ในการสร้างวิดเจ็ท โดยฟังก์ชัน `build` +ของวิดเจ็ทที่มีสถานะนั้นจะถูกรันใหม่ทุกครั้งที่มีการเปลี่ยนแปลงสถานะ -```dart - title: Text(widget.title), - ), -``` +#afigure( + ```dart + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + ```, + caption: [], +) + +*การทดลอง:* ในระหว่างที่รันแอพลิเคชันอยู่ ลองเปลี่ยนสีพื้นหลังของวิดเจ็ท `AppBar` (เป็นค่าอื่นเช่น +`Colors.amber`) แล้วทำการ hot reload โดยการกดไอคอน#box( + image("Flutter/flutterHotReload.svg", alt: "สายฟ้า", width: 1.5em), + baseline: 20%, +)เพื่อเห็นการเปลี่ยนแปลงของสีแถบแอพลิเคชันในระหว่างที่สีของวิดเจ็ทอื่น ๆ ยังคงเดิม + +#afigure( + ```dart + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + ```, + caption: [], +) + +โดยในอาร์กิวเมนต์ `title` นี้มีการใช้ค่าที่รับมาจากวิดเจ็ทที่เหนือกว่า โดย `widget` +ในที่นี้คือวัตถุของคลาส `MyHomePage` ที่ทำหน้าที่เก็บค่า `title` ของเรา แล้วเราจึงทำค่า `title` +นั้นใส่ในวิดเจ็ทข้อความ (`Text`) + +#afigure( + ```dart + title: Text(widget.title), + ), + ```, + caption: [], +) `Center` เป็นวิดเจ็ทจัดเลย์เอาต์ โดยจะทำให้ลูก 1 วิดเจ็ทของมัน (`child`) อยู่ตรงกลาง -```dart - body: Center( -``` +#afigure( + ```dart + body: Center( + ```, + caption: [], +) -และ `Column` เป็นวิดเจ็ทจัดเลย์เอาต์เช่นกัน มันรับลูกหลายคน (`children`) แล้วจัดเรียงมันในแนวตั้ง โดยค่าเริ่มต้นแล้ว มันจะจัดให้ตัวเองกว้างเท่ากับลูก ๆ ของมัน และพยายามจัดให้ตัวเองสูงเท่าวิดเจ็ทที่สูงกว่า +และ `Column` เป็นวิดเจ็ทจัดเลย์เอาต์เช่นกัน มันรับลูกหลายคน (`children`) แล้วจัดเรียงมันในแนวตั้ง +โดยค่าเริ่มต้นแล้ว มันจะจัดให้ตัวเองกว้างเท่ากับลูก ๆ ของมัน +และพยายามจัดให้ตัวเองสูงเท่าวิดเจ็ทที่สูงกว่า -วิดเจ็ทคอลัมน์มีหลายคุณสมบัติในการควบคุมขนาดของมันและการจัดวางลูก ๆ ของมัน โดยด้านล่างมีการใช้อาร์กิวเมนต์ `mainAxisAlignment` ในการจัดลูก ๆ ของมันให้อยู่ตรงกลางในแนวตั้ง โดย "main axis" หรือ แกนหลัก ในกรณีนี้คือแนวตั้ง เพราะคอลัมน์นั้นเป็นแนวตั้ง (และ "cross axis" หรือแกนไขว้ จะเป็นแนวนอน คือแนวตรงข้ามกับแนวหลัก) +วิดเจ็ทคอลัมน์มีหลายคุณสมบัติในการควบคุมขนาดของมันและการจัดวางลูก ๆ ของมัน +โดยด้านล่างมีการใช้อาร์กิวเมนต์ `mainAxisAlignment` ในการจัดลูก ๆ ของมันให้อยู่ตรงกลางในแนวตั้ง +โดย "main axis" หรือ แกนหลัก ในกรณีนี้คือแนวตั้ง เพราะคอลัมน์นั้นเป็นแนวตั้ง (และ "cross axis" +หรือแกนไขว้ จะเป็นแนวนอน คือแนวตรงข้ามกับแนวหลัก) -```dart - child: Column( - mainAxisAlignment: .center, - children: [ - const Text('You have pushed the button this many times:'), - Text( - '$_counter', -``` +#afigure( + ```dart + child: Column( + mainAxisAlignment: .center, + children: [ + const Text('You have pushed the button this many times:'), + Text( + '$_counter', + ```, + caption: [], +) -การเข้าถึงธีมของแอพลิเคชัน (```dart Theme.of(context)```) แล้วนำธีมข้อความ `headlineMedium` มาใช้: +การเข้าถึงธีมของแอพลิเคชัน (```dart Theme.of(context)```) แล้วนำธีมข้อความ +`headlineMedium` มาใช้: -```dart - style: Theme.of(context).textTheme.headlineMedium, - ), // Text - ], - ), // Column - ), // Center - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), - ); +#afigure( + ```dart + style: Theme.of(context).textTheme.headlineMedium, + ), // Text + ], + ), // Column + ), // Center + floatingActionButton: FloatingActionButton( + onPressed: _incrementCounter, + tooltip: 'Increment', + child: const Icon(Icons.add), + ), + ); + } } -} -``` + ```, + caption: [], +) #pagebreak() @@ -397,18 +529,23 @@ class _MyHomePageState extends State { == การนำเข้าแพคเกจ -#i ไฟล์ `pubspec.yaml` เป็นไฟล์ที่เก็บข้อมูลเกี่ยวกับโปรเจกต์ Flutter ของคุณ เช่น ชื่อ คำอธิบาย เวอร์ชัน และรวมถึงสิ่งที่จะกล่าวถึงในหัวข้อนี้ คือการติดตั้งและนำเข้าแพคเกจภายนอกมาใช้ในโปรเจกต์ +#i ไฟล์ `pubspec.yaml` เป็นไฟล์ที่เก็บข้อมูลเกี่ยวกับโปรเจกต์ Flutter ของคุณ เช่น ชื่อ คำอธิบาย +เวอร์ชัน และรวมถึงสิ่งที่จะกล่าวถึงในหัวข้อนี้ คือการติดตั้งและนำเข้าแพคเกจภายนอกมาใช้ในโปรเจกต์ -#i โดยหากไม่รวมรายละเอียดโปรเจกต์เบื้องต้นเช่นชื่อและคำอธิบายแล้ว ไฟล์ `pubspec.yaml` จะมีรายละเอียดดังนี้ +#i โดยหากไม่รวมรายละเอียดโปรเจกต์เบื้องต้นเช่นชื่อและคำอธิบายแล้ว ไฟล์ `pubspec.yaml` +จะมีรายละเอียดดังนี้ -`environment` นั้นจะกล่าวถึงสิ่งแวดล้อม ซึ่งในกรณีนี้มีเพียง `sdk` ที่ระบุเวอร์ชันของ Flutter SDK ในการคอมไพล์โปรเจกต์ +`environment` นั้นจะกล่าวถึงสิ่งแวดล้อม ซึ่งในกรณีนี้มีเพียง `sdk` ที่ระบุเวอร์ชันของ Flutter SDK +ในการคอมไพล์โปรเจกต์ ```yaml environment: sdk: ^3.10.1 ``` -`dependencies` จะกล่าวถึงไลบรารีที่โปรเจกต์พึ่งพา ซึ่งโดยค่าเริ่มต้นแล้ว ในโปรเจกต์ตัวอย่างจะมีการติดตั้งเซ็ทไอคอน Cupertino มาให้ (`cupertino_icons`; หรือเรียกอย่างง่ายว่า ไอคอน iOS) ซึ่งแน่นอนว่าหากคุณไม่ใช้ สามารถลบทิ้งได้ +`dependencies` จะกล่าวถึงไลบรารีที่โปรเจกต์พึ่งพา ซึ่งโดยค่าเริ่มต้นแล้ว +ในโปรเจกต์ตัวอย่างจะมีการติดตั้งเซ็ทไอคอน Cupertino มาให้ (`cupertino_icons`; +หรือเรียกอย่างง่ายว่า ไอคอน iOS) ซึ่งแน่นอนว่าหากคุณไม่ใช้ สามารถลบทิ้งได้ ```yaml dependencies: @@ -418,7 +555,10 @@ dependencies: cupertino_icons: ^1.0.8 ``` -`dev_dependencies` กล่าวถึงไลบรารีที่โปรเจกต์พึ่งพา#emph([ในการพัฒนา]) หมายความว่า ไลบรารีเหล่านี้จะไม่ถูกใส่เข้าไปยังแอพลิเคชันของคุณโดยตรง โดยตามค่าเริ่มต้นแล้ว จะมีแพคเกจ `flutter_test` ที่ถูกดึงมาจาก Flutter SDK โดยตรง และมี `flutter_lints` สำหรับการตรวจข้อผิดพลาดในโคด +`dev_dependencies` กล่าวถึงไลบรารีที่โปรเจกต์พึ่งพา#emph([ในการพัฒนา]) หมายความว่า +ไลบรารีเหล่านี้จะไม่ถูกใส่เข้าไปยังแอพลิเคชันของคุณโดยตรง โดยตามค่าเริ่มต้นแล้ว จะมีแพคเกจ +`flutter_test` ที่ถูกดึงมาจาก Flutter SDK โดยตรง และมี `flutter_lints` +สำหรับการตรวจข้อผิดพลาดในโคด ```yaml dev_dependencies: @@ -428,21 +568,25 @@ dev_dependencies: flutter_lints: ^6.0.0 ``` -`flutter` เป็นการตั้งค่าเกี่ยวกับ Flutter โดยที่ตามค่าเริ่มต้นแล้วจะมีค่า `uses-material-design` มาให้เป็น `true` ซึ่งจะเป็นการบอกกับ Flutter ว่าแอพลิเคชันนั้นใช้ Material design +`flutter` เป็นการตั้งค่าเกี่ยวกับ Flutter โดยที่ตามค่าเริ่มต้นแล้วจะมีค่า `uses-material-design` +มาให้เป็น `true` ซึ่งจะเป็นการบอกกับ Flutter ว่าแอพลิเคชันนั้นใช้ Material design ```yaml flutter: uses-material-design: true ``` -และนอกจากนั้นแล้ว ในส่วนของ `flutter` นี้จะเป็นส่วนที่ลิสต์รายการไฟล์เพิ่มเติมที่ต้องการใส่เข้าไปในแอพลิเคชันด้วยเช่นกัน ตัวอย่างเช่น โครงงานนี้มีการเพิ่มเติมในส่วน `flutter` ดังนี้: +และนอกจากนั้นแล้ว ในส่วนของ `flutter` +นี้จะเป็นส่วนที่ลิสต์รายการไฟล์เพิ่มเติมที่ต้องการใส่เข้าไปในแอพลิเคชันด้วยเช่นกัน ตัวอย่างเช่น +โครงงานนี้มีการเพิ่มเติมในส่วน `flutter` ดังนี้: ```yaml assets: - assets/certificates/rootCA.crt ``` -โดยจะเป็นการเพิ่มไฟล์ใบรับรอง `rootCA.crt` เข้าไปกับแอพลิเคชันเพื่อใช้ในการส่งคำขอ HTTPS ไปยังอุปกรณ์ของโครงงาน +โดยจะเป็นการเพิ่มไฟล์ใบรับรอง `rootCA.crt` เข้าไปกับแอพลิเคชันเพื่อใช้ในการส่งคำขอ HTTPS +ไปยังอุปกรณ์ของโครงงาน #pagebreak() @@ -453,15 +597,25 @@ flutter: + Material Design: การดีไซน์ของ Google สำหรับ Android + Cupertino Design: การดีไซน์ของ Apple สำหรับ iOS -*หมายเหตุ:* Cupertino Design ถูกแทนที่โดย Liquid Glass แล้ว โดยในปัจจุบันทีม Flutter กำลังทำการตรวจสอบและแก้ไขโครงสร้างระบบดีไซน์ ดังนั้น หากมีผู้พัฒนาต้องการใช้เอฟเฟกต์ Liquid Glass ในแอพลิเคชัน Flutter จึงจำเป็นต้องพึงพาแพคเกจบุคคลที่สามก่อนในขณะนี้ (Flutter เวอร์ชัน 3.38.3 ณ เวลาที่พิมพ์) +*หมายเหตุ:* Cupertino Design ถูกแทนที่โดย Liquid Glass แล้ว โดยในปัจจุบันทีม Flutter +กำลังทำการตรวจสอบและแก้ไขโครงสร้างระบบดีไซน์ ดังนั้น หากมีผู้พัฒนาต้องการใช้เอฟเฟกต์ Liquid +Glass ในแอพลิเคชัน Flutter จึงจำเป็นต้องพึงพาแพคเกจบุคคลที่สามก่อนในขณะนี้ (Flutter เวอร์ชัน +3.38.3 ณ เวลาที่พิมพ์) -#i แอพลิเคชันในโครงงานนี้ใช้ Material Design เนื่องจากมีเป้าหมายหลักเป็นแพลตฟอร์ม Android โดย Material Design คือภาษาการดีไซน์ที่ถูกพัฒนาโดย Google และถูกเปิดตัวครั้งแรก 25 มิถุนายน 2014 และมีเวอร์ชันหลัก 3 เวอร์ชันด้วยกัน โดยที่เวอร์ชันที่ 3 ถูกเปิดตัวในงาน Google I/O 2021 และมีชื่อว่า "Material You" (แต่ชื่อธรรมดา "Material Design 3" ก็ยังถูกใช้งานกันอย่างปกติ) และในงาน Google I/O 2025 มีการเปิดตัว "Material 3 Expressive" ซึ่งเป็นการปรับปรุงต่อจาก Material You เดิมสำหรับ Android 16 และ Wear OS 6 และสามารถดูรายละเอียดเพิ่มเติมเกี่ยวกับ Material 3 ได้ที่ https://m3.material.io/ +#i แอพลิเคชันในโครงงานนี้ใช้ Material Design เนื่องจากมีเป้าหมายหลักเป็นแพลตฟอร์ม Android +โดย Material Design คือภาษาการดีไซน์ที่ถูกพัฒนาโดย Google และถูกเปิดตัวครั้งแรก 25 มิถุนายน +2014 และมีเวอร์ชันหลัก 3 เวอร์ชันด้วยกัน โดยที่เวอร์ชันที่ 3 ถูกเปิดตัวในงาน Google I/O 2021 +และมีชื่อว่า "Material You" (แต่ชื่อธรรมดา "Material Design 3" ก็ยังถูกใช้งานกันอย่างปกติ) +และในงาน Google I/O 2025 มีการเปิดตัว "Material 3 Expressive" ซึ่งเป็นการปรับปรุงต่อจาก +Material You เดิมสำหรับ Android 16 และ Wear OS 6 และสามารถดูรายละเอียดเพิ่มเติมเกี่ยวกับ +Material 3 ได้ที่ https://m3.material.io/ == ข้อมูลเกี่ยวกับแพลตฟอร์ม === Android -#i ในการพัฒนาแอพลิเคชัน Android โดยใช้เฟรมเวิร์ก Flutter จำเป็นต้องใช้ส่วนประกอบเครื่องมือพัฒนา Android ดังนี้ +#i ในการพัฒนาแอพลิเคชัน Android โดยใช้เฟรมเวิร์ก Flutter +จำเป็นต้องใช้ส่วนประกอบเครื่องมือพัฒนา Android ดังนี้ - Android SDK (API Level 36 ณ เวลาที่พิมพ์) - Android SDK Build-Tools @@ -471,31 +625,59 @@ flutter: และแนะนำให้จัดการและติดตั้งเครื่องมือเหล่านี้ผ่าน Android Studio -#i ในการติดตั้ง Android SDK ควรติดตั้ง Android SDK ล่าสุดถึงแม้ว่าอุปกรณ์ของคุณจะใช้เวอร์ชันที่เก่ากว่านั้น เพื่อความมั่นใจว่าแอพลิเคชันจะสามารถใช้กับอุปกรณ์ที่ใหม่ล่าสุดได้ +#i ในการติดตั้ง Android SDK ควรติดตั้ง Android SDK +ล่าสุดถึงแม้ว่าอุปกรณ์ของคุณจะใช้เวอร์ชันที่เก่ากว่านั้น +เพื่อความมั่นใจว่าแอพลิเคชันจะสามารถใช้กับอุปกรณ์ที่ใหม่ล่าสุดได้ -#i แอพลิเคชัน Android จะมี SDK/API level เป้าหมาย (Target SDK/API level) และ SDK/API level ขั้นต่ำ (Minimum SDK/API level) โครงงานนี้ ณ เวลาที่พิมพ์ ใช้ API level เป้าหมาย 36 (Android 16) และ API level ขั้นต่ำ 24 (Android 7) ซึ่งรวมกันแล้ว แอพลิเคชัน Android จะสามารถติดตั้งได้บนระบบตั้งแต่ API level ขั้นต่ำจนถึง API level เป้าหมาย หรือก็คือ แอพลิเคชันในโครงงานนี้สามารถติดตั้งได้ตั้งแต่บนระบบ Android 7 ถึง Android 16 นั่นเอง +#i แอพลิเคชัน Android จะมี SDK/API level เป้าหมาย (Target SDK/API level) และ SDK/API +level ขั้นต่ำ (Minimum SDK/API level) โครงงานนี้ ณ เวลาที่พิมพ์ ใช้ API level เป้าหมาย 36 +(Android 16) และ API level ขั้นต่ำ 24 (Android 7) ซึ่งรวมกันแล้ว แอพลิเคชัน Android +จะสามารถติดตั้งได้บนระบบตั้งแต่ API level ขั้นต่ำจนถึง API level เป้าหมาย หรือก็คือ +แอพลิเคชันในโครงงานนี้สามารถติดตั้งได้ตั้งแต่บนระบบ Android 7 ถึง Android 16 นั่นเอง ==== Java/Kotlin -#i Java และ Kotlin เป็นภาษาสำคัญสำหรับการพัฒนาแอพลิเคชัน Android ถึงอย่างไรก็ตาม แอพลิเคชัน Flutter นั้นถูกเขียนด้วยภาษา Dart แต่ยังจำเป็นต้องมีโคด Java และ Kotlin เล็กน้อยเพื่อเริ่มต้นแอพลิเคชัน Flutter +#i Java และ Kotlin เป็นภาษาสำคัญสำหรับการพัฒนาแอพลิเคชัน Android ถึงอย่างไรก็ตาม แอพลิเคชัน +Flutter นั้นถูกเขียนด้วยภาษา Dart แต่ยังจำเป็นต้องมีโคด Java และ Kotlin +เล็กน้อยเพื่อเริ่มต้นแอพลิเคชัน Flutter -#i โดยปกติแล้ว Flutter จะสร้างโคดพื้นฐานขึ้นมาให้สำหรับการเริ่มแอพลิเคชันแบบพื้นฐาน (อยู่ภายในโฟลเดอร์ `java` และ `kotlin` ตามที่ถูกกล่าวถึงใน@flStructure) ดังนั้นจึงไม่จำเป็นต้องมีการเขียนโคด Java หรือ Kotlin เอง แต่ในบางกรณี อาจต้องเขียนโคดเพิ่มเองหากมีความต้องการเข้าถึงฟีเจอร์พื้นฐานระบบที่ Flutter ไม่มี API เพื่อให้เข้าถึงได้และไม่มีแพคเกจเพื่อรองรับฟีเจอร์ที่ต้องการ +#i โดยปกติแล้ว Flutter จะสร้างโคดพื้นฐานขึ้นมาให้สำหรับการเริ่มแอพลิเคชันแบบพื้นฐาน +(อยู่ภายในโฟลเดอร์ `java` และ `kotlin` ตามที่ถูกกล่าวถึงใน@flStructure) +ดังนั้นจึงไม่จำเป็นต้องมีการเขียนโคด Java หรือ Kotlin เอง แต่ในบางกรณี +อาจต้องเขียนโคดเพิ่มเองหากมีความต้องการเข้าถึงฟีเจอร์พื้นฐานระบบที่ Flutter ไม่มี API +เพื่อให้เข้าถึงได้และไม่มีแพคเกจเพื่อรองรับฟีเจอร์ที่ต้องการ -#i โครงการนี้ใช้ Java 21 (JetBrains Runtime/Azul Zulu OpenJDK) เป็นหลักในการทำงานกับ Gradle แต่แอพลิเคชัน Android ที่ผลิตออกมานั้น เพื่อให้เข้ากับเวอร์ชันที่เก่ากว่าของระบบปฏิบัติการได้ ใช้ Java 11 +#i โครงการนี้ใช้ Java 21 (JetBrains Runtime/Azul Zulu OpenJDK) เป็นหลักในการทำงานกับ +Gradle แต่แอพลิเคชัน Android ที่ผลิตออกมานั้น เพื่อให้เข้ากับเวอร์ชันที่เก่ากว่าของระบบปฏิบัติการได้ ใช้ +Java 11 ==== Gradle -#i Gradle เป็นเครื่องมือสร้างระบบอัตโนมัติสำหรับการพัฒนาซอฟต์แวร์หลายภาษา จัดการงานต่าง ๆ เช่น การคอมไพล์ การแพ็คเกจ การทดสอบ การปรับใช้ และการเผยแพร่ ภาษาที่รองรับ ได้แก่ Java (รวมถึงภาษา Kotlin, Groovy, Scala ที่ใช้ JDK), C/C++ และ JavaScript Gradle พัฒนาต่อยอดจากแนวคิดของ Apache Ant และ Apache Maven และนำเสนอภาษาเฉพาะโดเมนที่ใช้ Groovy และ Kotlin ซึ่งต่างจากการกำหนดค่าโครงการที่ใช้ XML ที่ Maven ใช้ Gradle ใช้กราฟแบบอะไซคลิกกำกับทิศทางเพื่อจัดการการอ้างอิง กราฟนี้ใช้เพื่อกำหนดลำดับของงานที่ควรดำเนินการ Gradle ทำงานบน Java Virtual Machine +#i Gradle เป็นเครื่องมือสร้างระบบอัตโนมัติสำหรับการพัฒนาซอฟต์แวร์หลายภาษา จัดการงานต่าง ๆ เช่น +การคอมไพล์ การแพ็คเกจ การทดสอบ การปรับใช้ และการเผยแพร่ ภาษาที่รองรับ ได้แก่ Java +(รวมถึงภาษา Kotlin, Groovy, Scala ที่ใช้ JDK), C/C++ และ JavaScript Gradle +พัฒนาต่อยอดจากแนวคิดของ Apache Ant และ Apache Maven และนำเสนอภาษาเฉพาะโดเมนที่ใช้ +Groovy และ Kotlin ซึ่งต่างจากการกำหนดค่าโครงการที่ใช้ XML ที่ Maven ใช้ Gradle +ใช้กราฟแบบอะไซคลิกกำกับทิศทางเพื่อจัดการการอ้างอิง กราฟนี้ใช้เพื่อกำหนดลำดับของงานที่ควรดำเนินการ +Gradle ทำงานบน Java Virtual Machine -#i Gradle คือเครื่องมือหลักที่ใช้ในการจัดการโปรเจกต์ Java ส่วนใหญ่ รวมถึงโปรเจกต์ Android โดยในโครงการนี้ จะใช้ Gradle เวอร์ชัน 8.14.3 เป็นหลัก +#i Gradle คือเครื่องมือหลักที่ใช้ในการจัดการโปรเจกต์ Java ส่วนใหญ่ รวมถึงโปรเจกต์ Android +โดยในโครงการนี้ จะใช้ Gradle เวอร์ชัน 8.14.3 เป็นหลัก -#i โดยปกติแล้ว ผู้พัฒนานั้นไม่มีความจำเป็นที่จะต้องแตะต้อง Gradle ด้วยตนเอง และ Flutter จะทำการจัดการเอง แต่หากมีความจำเป็นต้องใช้คำสั่ง Gradle ด้วยตนเอง จะมีสคริปต์ `gradlew` (หรือ `gradlew.bat` สำหรับผู้ใช้ Windows) ภายในโฟลเดอร์ `android` ของโปรเจกต์ Flutter เสมอเพื่อเรียกใช้ Gradle ที่ถูกดาวน์โหลดมาสำหรับโปรเจกต์นั้น ๆ +#i โดยปกติแล้ว ผู้พัฒนานั้นไม่มีความจำเป็นที่จะต้องแตะต้อง Gradle ด้วยตนเอง และ Flutter +จะทำการจัดการเอง แต่หากมีความจำเป็นต้องใช้คำสั่ง Gradle ด้วยตนเอง จะมีสคริปต์ `gradlew` (หรือ +`gradlew.bat` สำหรับผู้ใช้ Windows) ภายในโฟลเดอร์ `android` ของโปรเจกต์ Flutter +เสมอเพื่อเรียกใช้ Gradle ที่ถูกดาวน์โหลดมาสำหรับโปรเจกต์นั้น ๆ === Linux -#i เช่นเดียวกับ Android ที่กล่าวไปข้างต้น Flutter มีการสร้างโคดสำหรับการเปิดแอพลิเคชันแบบพื้นฐาน แต่สำหรับ Linux แล้วนั้น Flutter ใช้โคด C++ และเฟรมเวิร์ก CMake ในการสร้างรากฐานของแอพลิเคชัน +#i เช่นเดียวกับ Android ที่กล่าวไปข้างต้น Flutter มีการสร้างโคดสำหรับการเปิดแอพลิเคชันแบบพื้นฐาน +แต่สำหรับ Linux แล้วนั้น Flutter ใช้โคด C++ และเฟรมเวิร์ก CMake +ในการสร้างรากฐานของแอพลิเคชัน -#i ในการพัฒนาแอพลิเคชันสำหรับ Linux ต้องติดตั้งโปรแกรมเพิ่มเติม (build dependencies) ขยายความคือ ด้านบนคือสิ่งที่จำเป็นหากมีระบบอื่นเป็นเป้าหมาย แต่หากต้องการพัฒนาแอพลิเคชัน Linux ต้องติดตั้งโปรแกรมในรายการด้านล่างเพิ่ม +#i ในการพัฒนาแอพลิเคชันสำหรับ Linux ต้องติดตั้งโปรแกรมเพิ่มเติม (build dependencies) +ขยายความคือ ด้านบนคือสิ่งที่จำเป็นหากมีระบบอื่นเป็นเป้าหมาย แต่หากต้องการพัฒนาแอพลิเคชัน Linux +ต้องติดตั้งโปรแกรมในรายการด้านล่างเพิ่ม #grid( columns: 2, @@ -512,7 +694,8 @@ flutter: ], ) -#i การติดตั้งไลบรารีและโปรแกรมที่กล่าวไปข้างต้นจะแตกต่างกันไปแต่ละการแจกจ่าย Linux และ Flutter ใช้ไลบรารีพื้นฐานดังกล่าวในการทำงานของแอพลิเคชัน (runtime dependencies) +#i การติดตั้งไลบรารีและโปรแกรมที่กล่าวไปข้างต้นจะแตกต่างกันไปแต่ละการแจกจ่าย Linux และ +Flutter ใช้ไลบรารีพื้นฐานดังกล่าวในการทำงานของแอพลิเคชัน (runtime dependencies) - GTK 3 - blkid @@ -561,4 +744,6 @@ sudo pacman -S --needed util-linux-libs xz gtk3 === macOS/iOS -#i การพัฒนาแอพลิเคชันสำหรับ macOS และ iOS นั้นต้องทำบน macOS เท่านั้นและจำเป็นต้องพึ่งพาเครื่องมือ Xcode แต่เนื่องจากในโครงงานนี้ไม่มีผู้ใช้ macOS จึงไม่สามารถสร้างไบนารีสำหรับ macOS และ iOS ออกมาได้ และไม่ใช่เป้าหมายของโครงงานนี้เช่นกัน +#i การพัฒนาแอพลิเคชันสำหรับ macOS และ iOS นั้นต้องทำบน macOS +เท่านั้นและจำเป็นต้องพึ่งพาเครื่องมือ Xcode แต่เนื่องจากในโครงงานนี้ไม่มีผู้ใช้ macOS +จึงไม่สามารถสร้างไบนารีสำหรับ macOS และ iOS ออกมาได้ และไม่ใช่เป้าหมายของโครงงานนี้เช่นกัน diff --git a/Chapter2/HTTPS.typ b/Chapter2/HTTPS.typ index 8b38005..8ac182e 100644 --- a/Chapter2/HTTPS.typ +++ b/Chapter2/HTTPS.typ @@ -2,87 +2,237 @@ = เกณฑ์วิธีขนส่งข้อความหลายมิติแบบมั่นคง (Hypertext Transfer Protocol Secure; HTTPS) -#i เกณฑ์วิธีขนส่งข้อความหลายมิติแบบมั่นคง (Hypertext Transfer Protocol Secure; HTTPS) คือส่วนต่อขยายของโปรโตคอลเกณฑ์วิธีขนส่งข้อความหลายมิติ (Hypertext Transfer Protocol; HTTP) ซึ่งใช้การเข้ารหัสเพื่อการสื่อสารที่ปลอดภัยผ่านเครือข่ายคอมพิวเตอร์ และถูกใช้อย่างแพร่หลายบนอินเทอร์เน็ต โดยโปรโตคอลเครือข่าย HTTPS จะถูกเข้ารหัสด้วยเกณฑ์วิธีความมั่นคงของชั้นขนส่ง (Transport Layer Security; TLS) หรือก่อนหน้านี้คือเกณฑ์วิธีชั้นซ็อกเก็ตปลอดภัย (Secure Sockets Layer; SSL) ด้วยเหตุนั้น โปรโตคอลนี้สามารถเรียกด้วยชื่อ HTTP over TLS หรือ HTTP over SSL ได้เช่นกัน +#i เกณฑ์วิธีขนส่งข้อความหลายมิติแบบมั่นคง (Hypertext Transfer Protocol Secure; HTTPS) +คือส่วนต่อขยายของโปรโตคอลเกณฑ์วิธีขนส่งข้อความหลายมิติ (Hypertext Transfer Protocol; HTTP) +ซึ่งใช้การเข้ารหัสเพื่อการสื่อสารที่ปลอดภัยผ่านเครือข่ายคอมพิวเตอร์ และถูกใช้อย่างแพร่หลายบนอินเทอร์เน็ต +โดยโปรโตคอลเครือข่าย HTTPS จะถูกเข้ารหัสด้วยเกณฑ์วิธีความมั่นคงของชั้นขนส่ง (Transport Layer +Security; TLS) หรือก่อนหน้านี้คือเกณฑ์วิธีชั้นซ็อกเก็ตปลอดภัย (Secure Sockets Layer; SSL) +ด้วยเหตุนั้น โปรโตคอลนี้สามารถเรียกด้วยชื่อ HTTP over TLS หรือ HTTP over SSL ได้เช่นกัน -#i แรงจูงใจหลักของ HTTPS คือการยืนยันตัวตนของเว็บไซต์ที่เข้าถึง และการปกป้องความเป็นส่วนตัวและความสมบูรณ์ของข้อมูลที่แลกเปลี่ยนระหว่างการรับส่งข้อมูล HTTPS ป้องกันการโจมตีแบบ man-in-the-middle และการเข้ารหัสบล็อกไซเฟอร์แบบสองทิศทางในการสื่อสารระหว่างไคลเอนต์และเซิร์ฟเวอร์ ช่วยป้องกันการสื่อสารจากการดักฟังและการปลอมแปลง ประเด็นการพิสูจน์ตัวตนของ HTTPS จำเป็นต้องมีบุคคลที่สามที่เชื่อถือได้ลงนามในใบรับรองดิจิทัลฝั่งเซิร์ฟเวอร์ เดิมทีการดำเนินการนี้มีค่าใช้จ่ายสูง ซึ่งหมายความว่าการเชื่อมต่อ HTTPS ที่ผ่านการรับรองความถูกต้องอย่างสมบูรณ์มักจะพบได้เฉพาะในบริการธุรกรรมการชำระเงินที่ปลอดภัยและระบบสารสนเทศขององค์กรที่ปลอดภัยอื่นๆ บนเวิลด์ไวด์เว็บเท่านั้น ในปี 2016 แคมเปญโดยมูลนิธิพรมแดนอิเล็กทรอนิกส์ (Electronic Frontier Foundation; EFF) ด้วยการสนับสนุนจากนักพัฒนาเว็บเบราว์เซอร์ ทำให้โปรโตคอลนี้แพร่หลายมากขึ้น นับตั้งแต่ปี 2018 เป็นต้นมา HTTPS ถูกใช้โดยผู้ใช้เว็บบ่อยกว่า HTTP ดั้งเดิมที่ไม่ปลอดภัย โดยส่วนใหญ่เพื่อปกป้องความถูกต้องของหน้าเว็บบนเว็บไซต์ทุกประเภท รักษาความปลอดภัยบัญชี และรักษาความเป็นส่วนตัวของการสื่อสาร การระบุตัวตน และการท่องเว็บของผู้ใช้ +#i แรงจูงใจหลักของ HTTPS คือการยืนยันตัวตนของเว็บไซต์ที่เข้าถึง +และการปกป้องความเป็นส่วนตัวและความสมบูรณ์ของข้อมูลที่แลกเปลี่ยนระหว่างการรับส่งข้อมูล HTTPS +ป้องกันการโจมตีแบบ man-in-the-middle +และการเข้ารหัสบล็อกไซเฟอร์แบบสองทิศทางในการสื่อสารระหว่างไคลเอนต์และเซิร์ฟเวอร์ +ช่วยป้องกันการสื่อสารจากการดักฟังและการปลอมแปลง ประเด็นการพิสูจน์ตัวตนของ HTTPS +จำเป็นต้องมีบุคคลที่สามที่เชื่อถือได้ลงนามในใบรับรองดิจิทัลฝั่งเซิร์ฟเวอร์ เดิมทีการดำเนินการนี้มีค่าใช้จ่ายสูง +ซึ่งหมายความว่าการเชื่อมต่อ HTTPS +ที่ผ่านการรับรองความถูกต้องอย่างสมบูรณ์มักจะพบได้เฉพาะในบริการธุรกรรมการชำระเงินที่ปลอดภัยและระบบสารสนเทศขององค์กรที่ปลอดภัยอื่นๆ +บนเวิลด์ไวด์เว็บเท่านั้น ในปี 2016 แคมเปญโดยมูลนิธิพรมแดนอิเล็กทรอนิกส์ (Electronic Frontier +Foundation; EFF) ด้วยการสนับสนุนจากนักพัฒนาเว็บเบราว์เซอร์ ทำให้โปรโตคอลนี้แพร่หลายมากขึ้น +นับตั้งแต่ปี 2018 เป็นต้นมา HTTPS ถูกใช้โดยผู้ใช้เว็บบ่อยกว่า HTTP ดั้งเดิมที่ไม่ปลอดภัย +โดยส่วนใหญ่เพื่อปกป้องความถูกต้องของหน้าเว็บบนเว็บไซต์ทุกประเภท รักษาความปลอดภัยบัญชี +และรักษาความเป็นส่วนตัวของการสื่อสาร การระบุตัวตน และการท่องเว็บของผู้ใช้ == โดยรวม -#i รูปแบบ Uniform Resource Identifier (URI) ของ HTTPS มีรูปแบบการใช้งานที่เหมือนกันกับรูปแบบ HTTP อย่างไรก็ตาม HTTPS จะส่งสัญญาณให้เบราว์เซอร์ใช้ชั้นการเข้ารหัสเพิ่มเติมของ SSL/TLS เพื่อปกป้องการรับส่งข้อมูลซึ่ง SSL/TLS เหมาะอย่างยิ่งสำหรับ HTTP เนื่องจากสามารถให้การป้องกันได้แม้ว่าจะมีการตรวจสอบความถูกต้องเพียงด้านเดียวของการสื่อสาร ในกรณีนี้คือธุรกรรม HTTP บนอินเทอร์เน็ต ซึ่งโดยทั่วไปมีเพียงเซิร์ฟเวอร์เท่านั้นที่ได้รับการรับรองความถูกต้อง (โดยไคลเอนต์ตรวจสอบใบรับรองของเซิร์ฟเวอร์) +#i รูปแบบ Uniform Resource Identifier (URI) ของ HTTPS +มีรูปแบบการใช้งานที่เหมือนกันกับรูปแบบ HTTP อย่างไรก็ตาม HTTPS +จะส่งสัญญาณให้เบราว์เซอร์ใช้ชั้นการเข้ารหัสเพิ่มเติมของ SSL/TLS เพื่อปกป้องการรับส่งข้อมูลซึ่ง SSL/TLS +เหมาะอย่างยิ่งสำหรับ HTTP +เนื่องจากสามารถให้การป้องกันได้แม้ว่าจะมีการตรวจสอบความถูกต้องเพียงด้านเดียวของการสื่อสาร +ในกรณีนี้คือธุรกรรม HTTP บนอินเทอร์เน็ต +ซึ่งโดยทั่วไปมีเพียงเซิร์ฟเวอร์เท่านั้นที่ได้รับการรับรองความถูกต้อง +(โดยไคลเอนต์ตรวจสอบใบรับรองของเซิร์ฟเวอร์) -#i HTTPS สร้างช่องทางที่ปลอดภัยบนเครือข่ายที่ไม่ปลอดภัย วิธีนี้ช่วยให้มั่นใจได้ถึงการป้องกันที่เหมาะสมจากผู้ดักฟังและการโจมตีแบบ man-in-the-middle โดยมีเงื่อนไขว่ามีการใช้ชุดการเข้ารหัสที่เหมาะสม และใบรับรองเซิร์ฟเวอร์ได้รับการตรวจสอบและเชื่อถือได้ +#i HTTPS สร้างช่องทางที่ปลอดภัยบนเครือข่ายที่ไม่ปลอดภัย +วิธีนี้ช่วยให้มั่นใจได้ถึงการป้องกันที่เหมาะสมจากผู้ดักฟังและการโจมตีแบบ man-in-the-middle +โดยมีเงื่อนไขว่ามีการใช้ชุดการเข้ารหัสที่เหมาะสม และใบรับรองเซิร์ฟเวอร์ได้รับการตรวจสอบและเชื่อถือได้ -#i เนื่องจาก HTTPS เชื่อมโยง HTTP ทั้งหมดเข้ากับ TLS โดยตรงจึงสามารถเข้ารหัสโปรโตคอล HTTP พื้นฐานทั้งหมดได้ ซึ่งรวมถึง URL ของคำขอ พารามิเตอร์การค้นหา ส่วนหัว และคุกกี้ (ซึ่งมักจะมีข้อมูลระบุตัวตนของผู้ใช้) อย่างไรก็ตาม เนื่องจากที่อยู่เว็บไซต์และหมายเลขพอร์ตเป็นส่วนหนึ่งของโปรโตคอล TCP/IP พื้นฐาน HTTPS จึงไม่สามารถปกป้องการเปิดเผยข้อมูลเหล่านี้ได้ ในทางปฏิบัติ หมายความว่าแม้แต่บนเว็บเซิร์ฟเวอร์ที่กำหนดค่าอย่างถูกต้อง ผู้ดักฟังก็สามารถอนุมานที่อยู่ IP และหมายเลขพอร์ตของเว็บเซิร์ฟเวอร์ และบางครั้งอาจรวมถึงชื่อโดเมน (เช่น www.example.org แต่ไม่สามารถอนุมานส่วนที่เหลือของ URL) ที่ผู้ใช้กำลังสื่อสารด้วย รวมถึงปริมาณข้อมูลที่ถ่ายโอนและระยะเวลาของการสื่อสาร แต่อย่างไรก็ตามไม่รวมถึงเนื้อหาของการสื่อสาร +#i เนื่องจาก HTTPS เชื่อมโยง HTTP ทั้งหมดเข้ากับ TLS โดยตรงจึงสามารถเข้ารหัสโปรโตคอล HTTP +พื้นฐานทั้งหมดได้ ซึ่งรวมถึง URL ของคำขอ พารามิเตอร์การค้นหา ส่วนหัว และคุกกี้ +(ซึ่งมักจะมีข้อมูลระบุตัวตนของผู้ใช้) อย่างไรก็ตาม +เนื่องจากที่อยู่เว็บไซต์และหมายเลขพอร์ตเป็นส่วนหนึ่งของโปรโตคอล TCP/IP พื้นฐาน HTTPS +จึงไม่สามารถปกป้องการเปิดเผยข้อมูลเหล่านี้ได้ ในทางปฏิบัติ +หมายความว่าแม้แต่บนเว็บเซิร์ฟเวอร์ที่กำหนดค่าอย่างถูกต้อง ผู้ดักฟังก็สามารถอนุมานที่อยู่ IP +และหมายเลขพอร์ตของเว็บเซิร์ฟเวอร์ และบางครั้งอาจรวมถึงชื่อโดเมน (เช่น www.example.org +แต่ไม่สามารถอนุมานส่วนที่เหลือของ URL) ที่ผู้ใช้กำลังสื่อสารด้วย +รวมถึงปริมาณข้อมูลที่ถ่ายโอนและระยะเวลาของการสื่อสาร แต่อย่างไรก็ตามไม่รวมถึงเนื้อหาของการสื่อสาร -#i เว็บเบราว์เซอร์รู้วิธีเชื่อถือเว็บไซต์ HTTPS โดยอ้างอิงจากผู้ให้บริการออกใบรับรอง (Certificate Authority) ที่ติดตั้งไว้ล่วงหน้าในซอฟต์แวร์ ผู้สร้างเว็บเบราว์เซอร์จึงไว้วางใจผู้ให้บริการออกใบรับรองในการออกใบรับรองที่ถูกต้อง ดังนั้น ผู้ใช้ควรเชื่อถือการเชื่อมต่อ HTTPS ไปยังเว็บไซต์ก็ต่อเมื่อเป็นไปตามเงื่อนไขทั้งหมดต่อไปนี้: +#i เว็บเบราว์เซอร์รู้วิธีเชื่อถือเว็บไซต์ HTTPS โดยอ้างอิงจากผู้ให้บริการออกใบรับรอง (Certificate +Authority) ที่ติดตั้งไว้ล่วงหน้าในซอฟต์แวร์ +ผู้สร้างเว็บเบราว์เซอร์จึงไว้วางใจผู้ให้บริการออกใบรับรองในการออกใบรับรองที่ถูกต้อง ดังนั้น +ผู้ใช้ควรเชื่อถือการเชื่อมต่อ HTTPS ไปยังเว็บไซต์ก็ต่อเมื่อเป็นไปตามเงื่อนไขทั้งหมดต่อไปนี้: -- ผู้ใช้เชื่อมั่นว่าอุปกรณ์ของตน โฮสต์เบราว์เซอร์ และวิธีการเข้าถึงเบราว์เซอร์นั้นไม่ถูกบุกรุก (กล่าวคือ ไม่มีการโจมตีซัพพลายเชน) -- ผู้ใช้เชื่อมั่นว่าซอฟต์แวร์เบราว์เซอร์ใช้งาน HTTPS ได้อย่างถูกต้องพร้อมกับผู้ให้บริการออกใบรับรองที่ติดตั้งไว้ล่วงหน้าอย่างถูกต้อง -- ผู้ใช้เชื่อมั่นว่าผู้ให้บริการออกใบรับรองจะรับรองเฉพาะเว็บไซต์ที่ถูกต้องตามกฎหมายเท่านั้น (กล่าวคือ ผู้ให้บริการออกใบรับรองจะไม่ถูกบุกรุกและไม่มีการออกใบรับรองที่ผิดพลาด) +- ผู้ใช้เชื่อมั่นว่าอุปกรณ์ของตน โฮสต์เบราว์เซอร์ และวิธีการเข้าถึงเบราว์เซอร์นั้นไม่ถูกบุกรุก (กล่าวคือ + ไม่มีการโจมตีซัพพลายเชน) +- ผู้ใช้เชื่อมั่นว่าซอฟต์แวร์เบราว์เซอร์ใช้งาน HTTPS + ได้อย่างถูกต้องพร้อมกับผู้ให้บริการออกใบรับรองที่ติดตั้งไว้ล่วงหน้าอย่างถูกต้อง +- ผู้ใช้เชื่อมั่นว่าผู้ให้บริการออกใบรับรองจะรับรองเฉพาะเว็บไซต์ที่ถูกต้องตามกฎหมายเท่านั้น (กล่าวคือ + ผู้ให้บริการออกใบรับรองจะไม่ถูกบุกรุกและไม่มีการออกใบรับรองที่ผิดพลาด) - เว็บไซต์มีใบรับรองที่ถูกต้อง ซึ่งหมายความว่าได้รับการลงนามโดยผู้ให้บริการที่เชื่อถือได้ -- ใบรับรองระบุเว็บไซต์ได้อย่างถูกต้อง (เช่น เมื่อเบราว์เซอร์เข้าชม https://example.com ใบรับรองที่ได้รับนั้นถูกต้องสำหรับ example.com และไม่ใช่ของหน่วยงานอื่น) +- ใบรับรองระบุเว็บไซต์ได้อย่างถูกต้อง (เช่น เมื่อเบราว์เซอร์เข้าชม https://example.com + ใบรับรองที่ได้รับนั้นถูกต้องสำหรับ example.com และไม่ใช่ของหน่วยงานอื่น) - ผู้ใช้เชื่อมั่นว่าเลเยอร์การเข้ารหัสของโปรโตคอล (SSL/TLS) มีความปลอดภัยเพียงพอจากการดักฟัง -#i HTTPS มีความสำคัญอย่างยิ่งต่อเครือข่ายที่ไม่ปลอดภัยและเครือข่ายที่อาจถูกแทรกแซง เครือข่ายที่ไม่ปลอดภัย เช่น จุดเชื่อมต่อ Wi-Fi สาธารณะ ซึ่งเปิดโอกาสให้ทุกคนในเครือข่ายท้องถิ่นเดียวกันสามารถดักจับแพ็กเก็ตและค้นพบข้อมูลสำคัญที่ไม่ได้รับการป้องกันโดย HTTPS นอกจากนี้ ยังพบว่าเครือข่าย WLAN ทั้งแบบฟรีและแบบเสียเงินบางเครือข่ายได้แทรกแซงหน้าเว็บโดยการแทรกแพ็กเก็ตเพื่อแสดงโฆษณาของตนเองบนเว็บไซต์อื่น การกระทำเช่นนี้สามารถถูกนำไปใช้ในทางที่ผิดได้หลายวิธี เช่น การฉีดมัลแวร์ลงในหน้าเว็บและการขโมยข้อมูลส่วนบุคคลของผู้ใช้ +#i HTTPS มีความสำคัญอย่างยิ่งต่อเครือข่ายที่ไม่ปลอดภัยและเครือข่ายที่อาจถูกแทรกแซง +เครือข่ายที่ไม่ปลอดภัย เช่น จุดเชื่อมต่อ Wi-Fi สาธารณะ +ซึ่งเปิดโอกาสให้ทุกคนในเครือข่ายท้องถิ่นเดียวกันสามารถดักจับแพ็กเก็ตและค้นพบข้อมูลสำคัญที่ไม่ได้รับการป้องกันโดย +HTTPS นอกจากนี้ ยังพบว่าเครือข่าย WLAN +ทั้งแบบฟรีและแบบเสียเงินบางเครือข่ายได้แทรกแซงหน้าเว็บโดยการแทรกแพ็กเก็ตเพื่อแสดงโฆษณาของตนเองบนเว็บไซต์อื่น +การกระทำเช่นนี้สามารถถูกนำไปใช้ในทางที่ผิดได้หลายวิธี เช่น +การฉีดมัลแวร์ลงในหน้าเว็บและการขโมยข้อมูลส่วนบุคคลของผู้ใช้ -#i เมื่อมีข้อมูลมากขึ้นเกี่ยวกับการเฝ้าระวังมวลชนทั่วโลกและการขโมยข้อมูลส่วนบุคคลของอาชญากร การใช้ระบบรักษาความปลอดภัย HTTPS บนเว็บไซต์ทั้งหมดจึงมีความสำคัญเพิ่มมากขึ้นเรื่อยๆ โดยไม่คำนึงถึงประเภทของการเชื่อมต่ออินเทอร์เน็ตที่ใช้งาน แม้ว่าข้อมูลเมตาเกี่ยวกับหน้าเว็บแต่ละหน้าที่ผู้ใช้เข้าชมอาจไม่ถือว่ามีความละเอียดอ่อน แต่เมื่อนำมารวมกันแล้ว ข้อมูลเมตาเหล่านี้อาจเปิดเผยข้อมูลเกี่ยวกับผู้ใช้ได้มาก และกระทบต่อความเป็นส่วนตัวของผู้ใช้ +#i เมื่อมีข้อมูลมากขึ้นเกี่ยวกับการเฝ้าระวังมวลชนทั่วโลกและการขโมยข้อมูลส่วนบุคคลของอาชญากร +การใช้ระบบรักษาความปลอดภัย HTTPS บนเว็บไซต์ทั้งหมดจึงมีความสำคัญเพิ่มมากขึ้นเรื่อยๆ +โดยไม่คำนึงถึงประเภทของการเชื่อมต่ออินเทอร์เน็ตที่ใช้งาน +แม้ว่าข้อมูลเมตาเกี่ยวกับหน้าเว็บแต่ละหน้าที่ผู้ใช้เข้าชมอาจไม่ถือว่ามีความละเอียดอ่อน +แต่เมื่อนำมารวมกันแล้ว ข้อมูลเมตาเหล่านี้อาจเปิดเผยข้อมูลเกี่ยวกับผู้ใช้ได้มาก +และกระทบต่อความเป็นส่วนตัวของผู้ใช้ -#i การปรับใช้ HTTPS ยังอนุญาตให้ใช้ HTTP/2 และ HTTP/3 (และรุ่นก่อนหน้าอย่าง SPDY และ QUIC) ซึ่งเป็น HTTP เวอร์ชันใหม่ที่ออกแบบมาเพื่อลดเวลา ขนาด และความหน่วงในการโหลดหน้าเว็บ +#i การปรับใช้ HTTPS ยังอนุญาตให้ใช้ HTTP/2 และ HTTP/3 (และรุ่นก่อนหน้าอย่าง SPDY และ QUIC) +ซึ่งเป็น HTTP เวอร์ชันใหม่ที่ออกแบบมาเพื่อลดเวลา ขนาด และความหน่วงในการโหลดหน้าเว็บ -#i และมีการแนะนำให้ใช้ HTTP Strict Transport Security (HSTS) ร่วมกับ HTTPS เพื่อป้องกันผู้ใช้จากการโจมตีแบบ man-in-the-middle โดยเฉพาะอย่างยิ่ง SSL stripping +#i และมีการแนะนำให้ใช้ HTTP Strict Transport Security (HSTS) ร่วมกับ HTTPS +เพื่อป้องกันผู้ใช้จากการโจมตีแบบ man-in-the-middle โดยเฉพาะอย่างยิ่ง SSL stripping == ความปลอดภัย -#i ความปลอดภัยของ HTTPS อยู่ที่ TLS พื้นฐาน ซึ่งโดยทั่วไปจะใช้คีย์สาธารณะและคีย์ส่วนตัวระยะยาวเพื่อสร้างคีย์เซสชันระยะสั้น ซึ่งจะถูกนำไปใช้ในการเข้ารหัสการไหลของข้อมูลระหว่างไคลเอนต์และเซิร์ฟเวอร์ ใบรับรอง X.509 ถูกใช้เพื่อยืนยันตัวตนของเซิร์ฟเวอร์ (และบางครั้งรวมถึงไคลเอนต์ด้วย) ด้วยเหตุนี้ ผู้ให้บริการออกใบรับรองและใบรับรองคีย์สาธารณะจึงจำเป็นต่อการตรวจสอบความสัมพันธ์ระหว่างใบรับรองและเจ้าของ รวมถึงการสร้าง ลงนาม และดูแลความถูกต้องของใบรับรอง แม้ว่าวิธีนี้อาจมีประโยชน์มากกว่าการตรวจสอบตัวตนผ่านเครือข่ายที่เชื่อถือได้ แต่การเปิดเผยข้อมูลการเฝ้าระวังข้อมูลจำนวนมากในปี 2013 ได้ชี้ให้เห็นถึงผู้ให้บริการออกใบรับรองว่าเป็นจุดอ่อนที่อาจนำไปสู่การโจมตีแบบ man-in-the-middle คุณสมบัติที่สำคัญในบริบทนี้คือความลับแบบส่งต่อ (Forward Secrecy) ซึ่งรับประกันว่าการสื่อสารที่เข้ารหัสที่บันทึกไว้ในอดีตจะไม่สามารถดึงข้อมูลและถอดรหัสได้ หากคีย์ลับหรือรหัสผ่านระยะยาวถูกบุกรุกในอนาคต ไม่ใช่ทุกเว็บเซิร์ฟเวอร์ที่จะมีระบบความลับแบบส่งต่อ +#i ความปลอดภัยของ HTTPS อยู่ที่ TLS พื้นฐาน +ซึ่งโดยทั่วไปจะใช้คีย์สาธารณะและคีย์ส่วนตัวระยะยาวเพื่อสร้างคีย์เซสชันระยะสั้น +ซึ่งจะถูกนำไปใช้ในการเข้ารหัสการไหลของข้อมูลระหว่างไคลเอนต์และเซิร์ฟเวอร์ ใบรับรอง X.509 +ถูกใช้เพื่อยืนยันตัวตนของเซิร์ฟเวอร์ (และบางครั้งรวมถึงไคลเอนต์ด้วย) ด้วยเหตุนี้ +ผู้ให้บริการออกใบรับรองและใบรับรองคีย์สาธารณะจึงจำเป็นต่อการตรวจสอบความสัมพันธ์ระหว่างใบรับรองและเจ้าของ +รวมถึงการสร้าง ลงนาม และดูแลความถูกต้องของใบรับรอง +แม้ว่าวิธีนี้อาจมีประโยชน์มากกว่าการตรวจสอบตัวตนผ่านเครือข่ายที่เชื่อถือได้ +แต่การเปิดเผยข้อมูลการเฝ้าระวังข้อมูลจำนวนมากในปี 2013 +ได้ชี้ให้เห็นถึงผู้ให้บริการออกใบรับรองว่าเป็นจุดอ่อนที่อาจนำไปสู่การโจมตีแบบ man-in-the-middle +คุณสมบัติที่สำคัญในบริบทนี้คือความลับแบบส่งต่อ (Forward Secrecy) +ซึ่งรับประกันว่าการสื่อสารที่เข้ารหัสที่บันทึกไว้ในอดีตจะไม่สามารถดึงข้อมูลและถอดรหัสได้ +หากคีย์ลับหรือรหัสผ่านระยะยาวถูกบุกรุกในอนาคต ไม่ใช่ทุกเว็บเซิร์ฟเวอร์ที่จะมีระบบความลับแบบส่งต่อ -#i เพื่อให้ HTTPS มีประสิทธิภาพ เว็บไซต์จะต้องโฮสต์ผ่าน HTTPS ทั้งหมด หากเนื้อหาบางส่วนของเว็บไซต์ถูกโหลดผ่าน HTTP (เช่น สคริปต์หรือรูปภาพ) หรือหากโหลดเฉพาะหน้าที่มีข้อมูลละเอียดอ่อน เช่น หน้าเข้าสู่ระบบ ผ่าน HTTPS ขณะที่ส่วนอื่นๆ ของเว็บไซต์ผ่าน HTTP ธรรมดา ผู้ใช้จะเสี่ยงต่อการถูกโจมตีและการเฝ้าระวัง นอกจากนี้ คุกกี้บนเว็บไซต์ที่รันผ่าน HTTPS จะต้องเปิดใช้งานแอตทริบิวต์ secure ในเว็บไซต์ที่มีข้อมูลละเอียดอ่อน ผู้ใช้และเซสชันจะถูกเปิดเผยทุกครั้งที่เข้าถึงเว็บไซต์นั้นด้วย HTTP แทนที่จะเป็น HTTPS +#i เพื่อให้ HTTPS มีประสิทธิภาพ เว็บไซต์จะต้องโฮสต์ผ่าน HTTPS ทั้งหมด +หากเนื้อหาบางส่วนของเว็บไซต์ถูกโหลดผ่าน HTTP (เช่น สคริปต์หรือรูปภาพ) +หรือหากโหลดเฉพาะหน้าที่มีข้อมูลละเอียดอ่อน เช่น หน้าเข้าสู่ระบบ ผ่าน HTTPS ขณะที่ส่วนอื่นๆ +ของเว็บไซต์ผ่าน HTTP ธรรมดา ผู้ใช้จะเสี่ยงต่อการถูกโจมตีและการเฝ้าระวัง นอกจากนี้ +คุกกี้บนเว็บไซต์ที่รันผ่าน HTTPS จะต้องเปิดใช้งานแอตทริบิวต์ secure ในเว็บไซต์ที่มีข้อมูลละเอียดอ่อน +ผู้ใช้และเซสชันจะถูกเปิดเผยทุกครั้งที่เข้าถึงเว็บไซต์นั้นด้วย HTTP แทนที่จะเป็น HTTPS == รายละเอียดทางเทคนิค === ความแตกต่างจาก HTTP -#i URL แบบ HTTPS เริ่มต้นด้วย "https://" และใช้พอร์ต 443 ตามค่าเริ่มต้น ในขณะที่ URL แบบ HTTP เริ่มต้นด้วย "http://" และใช้พอร์ต 80 ตามค่าเริ่มต้น +#iiii URL แบบ HTTPS เริ่มต้นด้วย "https://" และใช้พอร์ต 443 ตามค่าเริ่มต้น ในขณะที่ URL แบบ +HTTP เริ่มต้นด้วย "http://" และใช้พอร์ต 80 ตามค่าเริ่มต้น -#i HTTP ไม่ได้เข้ารหัส จึงมีความเสี่ยงต่อการโจมตีแบบ man-in-the-middle และการดักฟังซึ่งอาจทำให้ผู้โจมตีสามารถเข้าถึงบัญชีเว็บไซต์และข้อมูลสำคัญ และแก้ไขหน้าเว็บเพื่อแทรกมัลแวร์หรือโฆษณาได้ HTTPS ได้รับการออกแบบมาให้ทนทานต่อการโจมตีประเภทนี้ และถือว่าปลอดภัย (ยกเว้นการใช้งาน HTTPS ที่ใช้ SSL เวอร์ชันที่ล้าสมัย) +#iiii HTTP ไม่ได้เข้ารหัส จึงมีความเสี่ยงต่อการโจมตีแบบ man-in-the-middle +และการดักฟังซึ่งอาจทำให้ผู้โจมตีสามารถเข้าถึงบัญชีเว็บไซต์และข้อมูลสำคัญ +และแก้ไขหน้าเว็บเพื่อแทรกมัลแวร์หรือโฆษณาได้ HTTPS ได้รับการออกแบบมาให้ทนทานต่อการโจมตีประเภทนี้ +และถือว่าปลอดภัย (ยกเว้นการใช้งาน HTTPS ที่ใช้ SSL เวอร์ชันที่ล้าสมัย) === ชั้นเครือข่าย -#i HTTP ทำงานที่เลเยอร์สูงสุดของโมเดล TCP/IP นั่นคือเลเยอร์แอปพลิเคชัน เช่นเดียวกับโปรโตคอลความปลอดภัย TLS (ซึ่งทำงานเป็นเลเยอร์ย่อยที่ต่ำกว่าของเลเยอร์เดียวกัน) ซึ่งเข้ารหัสข้อความ HTTP ก่อนส่ง และถอดรหัสเมื่อข้อความมาถึง โดยเคร่งครัดแล้ว HTTPS ไม่ใช่โปรโตคอลใหม่ที่แยกจากกัน แต่หมายถึงการใช้ HTTP ทั่วไปบนการเชื่อมต่อ SSL/TLS ที่เข้ารหัส (เป็นส่วนต่อขยายจาก HTTP อย่างที่กล่าวไปข้างต้น) +#iiii HTTP ทำงานที่เลเยอร์สูงสุดของโมเดล TCP/IP นั่นคือเลเยอร์แอปพลิเคชัน +เช่นเดียวกับโปรโตคอลความปลอดภัย TLS (ซึ่งทำงานเป็นเลเยอร์ย่อยที่ต่ำกว่าของเลเยอร์เดียวกัน) +ซึ่งเข้ารหัสข้อความ HTTP ก่อนส่ง และถอดรหัสเมื่อข้อความมาถึง โดยเคร่งครัดแล้ว HTTPS +ไม่ใช่โปรโตคอลใหม่ที่แยกจากกัน แต่หมายถึงการใช้ HTTP ทั่วไปบนการเชื่อมต่อ SSL/TLS ที่เข้ารหัส +(เป็นส่วนต่อขยายจาก HTTP อย่างที่กล่าวไปข้างต้น) -#i HTTPS เข้ารหัสเนื้อหาข้อความทั้งหมด รวมถึงส่วนหัว HTTP และข้อมูลคำขอ/การตอบกลับ ยกเว้นการโจมตีด้วยการเข้ารหัส CCA ที่อาจเกิดขึ้นตามที่อธิบายไว้ในส่วนข้อจำกัดด้านล่าง ผู้โจมตีควรจะสามารถตรวจพบการเชื่อมต่อระหว่างสองฝ่ายได้มากที่สุด รวมถึงชื่อโดเมนและที่อยู่ IP ของฝ่ายนั้นด้วย +#iiii HTTPS เข้ารหัสเนื้อหาข้อความทั้งหมด รวมถึงส่วนหัว HTTP และข้อมูลคำขอ/การตอบกลับ +ยกเว้นการโจมตีด้วยการเข้ารหัส CCA ที่อาจเกิดขึ้นตามที่อธิบายไว้ในส่วนข้อจำกัดด้านล่าง +ผู้โจมตีควรจะสามารถตรวจพบการเชื่อมต่อระหว่างสองฝ่ายได้มากที่สุด รวมถึงชื่อโดเมนและที่อยู่ IP +ของฝ่ายนั้นด้วย === การตั้งค่าเซิร์ฟเวอร์ -เพื่อเตรียมเว็บเซิร์ฟเวอร์ให้ยอมรับการเชื่อมต่อ HTTPS ผู้ดูแลระบบต้องสร้างใบรับรองคีย์สาธารณะสำหรับเว็บเซิร์ฟเวอร์ ใบรับรองนี้ต้องได้รับการลงนามโดยผู้ออกใบรับรองที่เชื่อถือได้เพื่อให้เว็บเบราว์เซอร์ยอมรับโดยไม่มีการแจ้งเตือน ผู้ออกใบรับรองจะรับรองว่าผู้ถือใบรับรองคือผู้ดำเนินการของเว็บเซิร์ฟเวอร์ที่นำเสนอใบรับรองนั้น โดยทั่วไปเว็บเบราว์เซอร์จะเผยแพร่รายชื่อใบรับรองการลงนามของผู้ออกใบรับรองหลักๆ เพื่อให้สามารถตรวจสอบใบรับรองที่ลงนามโดยผู้ออกใบรับรองเหล่านั้นได้ +#iiii เพื่อเตรียมเว็บเซิร์ฟเวอร์ให้ยอมรับการเชื่อมต่อ HTTPS +ผู้ดูแลระบบต้องสร้างใบรับรองคีย์สาธารณะสำหรับเว็บเซิร์ฟเวอร์ +ใบรับรองนี้ต้องได้รับการลงนามโดยผู้ออกใบรับรองที่เชื่อถือได้เพื่อให้เว็บเบราว์เซอร์ยอมรับโดยไม่มีการแจ้งเตือน +ผู้ออกใบรับรองจะรับรองว่าผู้ถือใบรับรองคือผู้ดำเนินการของเว็บเซิร์ฟเวอร์ที่นำเสนอใบรับรองนั้น +โดยทั่วไปเว็บเบราว์เซอร์จะเผยแพร่รายชื่อใบรับรองการลงนามของผู้ออกใบรับรองหลักๆ +เพื่อให้สามารถตรวจสอบใบรับรองที่ลงนามโดยผู้ออกใบรับรองเหล่านั้นได้ ==== การขอใบรับรอง -#i มีผู้ให้บริการออกใบรับรองเชิงพาณิชย์จำนวนหนึ่งที่เสนอใบรับรอง SSL/TLS แบบชำระเงินหลายประเภท รวมถึงใบรับรองการตรวจสอบขยาย +#iiiii มีผู้ให้บริการออกใบรับรองเชิงพาณิชย์จำนวนหนึ่งที่เสนอใบรับรอง SSL/TLS +แบบชำระเงินหลายประเภท รวมถึงใบรับรองการตรวจสอบขยาย -#i Let's Encrypt เปิดตัวในเดือนเมษายน 2559 ให้บริการใบรับรอง SSL/TLS พื้นฐานแบบอัตโนมัติฟรีแก่เว็บไซต์ มูลนิธิ Electronic Frontier Foundation ระบุว่า Let's Encrypt จะทำให้การเปลี่ยนจาก HTTP เป็น HTTPS "ง่ายดายเพียงแค่ออกคำสั่งหรือคลิกปุ่ม" ปัจจุบันผู้ให้บริการเว็บโฮสต์และผู้ให้บริการคลาวด์ส่วนใหญ่ใช้ประโยชน์จาก Let's Encrypt เพื่อมอบใบรับรองฟรีให้กับลูกค้า +#iiiii Let's Encrypt เปิดตัวในเดือนเมษายน 2559 ให้บริการใบรับรอง SSL/TLS +พื้นฐานแบบอัตโนมัติฟรีแก่เว็บไซต์ มูลนิธิ Electronic Frontier Foundation ระบุว่า Let's Encrypt +จะทำให้การเปลี่ยนจาก HTTP เป็น HTTPS "ง่ายดายเพียงแค่ออกคำสั่งหรือคลิกปุ่ม" +ปัจจุบันผู้ให้บริการเว็บโฮสต์และผู้ให้บริการคลาวด์ส่วนใหญ่ใช้ประโยชน์จาก Let's Encrypt +เพื่อมอบใบรับรองฟรีให้กับลูกค้า ==== ใช้เป็นการควบคุมการเข้าถึง -#i ระบบนี้ยังสามารถใช้สำหรับการตรวจสอบสิทธิ์ไคลเอ็นต์เพื่อจำกัดการเข้าถึงเว็บเซิร์ฟเวอร์เฉพาะผู้ใช้ที่ได้รับอนุญาตเท่านั้น ในการดำเนินการนี้ ผู้ดูแลระบบเว็บไซต์มักจะสร้างใบรับรองสำหรับผู้ใช้แต่ละราย ซึ่งผู้ใช้จะโหลดใบรับรองลงในเบราว์เซอร์ โดยปกติใบรับรองจะมีชื่อและที่อยู่อีเมลของผู้ใช้ที่ได้รับอนุญาต และจะถูกตรวจสอบโดยเซิร์ฟเวอร์โดยอัตโนมัติในแต่ละการเชื่อมต่อเพื่อยืนยันตัวตนของผู้ใช้ ซึ่งอาจไม่จำเป็นต้องใช้รหัสผ่านด้วยซ้ำ +#iiiii +ระบบนี้ยังสามารถใช้สำหรับการตรวจสอบสิทธิ์ไคลเอ็นต์เพื่อจำกัดการเข้าถึงเว็บเซิร์ฟเวอร์เฉพาะผู้ใช้ที่ได้รับอนุญาตเท่านั้น +ในการดำเนินการนี้ ผู้ดูแลระบบเว็บไซต์มักจะสร้างใบรับรองสำหรับผู้ใช้แต่ละราย +ซึ่งผู้ใช้จะโหลดใบรับรองลงในเบราว์เซอร์ โดยปกติใบรับรองจะมีชื่อและที่อยู่อีเมลของผู้ใช้ที่ได้รับอนุญาต +และจะถูกตรวจสอบโดยเซิร์ฟเวอร์โดยอัตโนมัติในแต่ละการเชื่อมต่อเพื่อยืนยันตัวตนของผู้ใช้ +ซึ่งอาจไม่จำเป็นต้องใช้รหัสผ่านด้วยซ้ำ ==== ในกรณีที่คีย์ลับถูกบุกรุก -#i คุณสมบัติที่สำคัญในบริบทนี้คือการเข้ารหัสแบบส่งต่อที่สมบูรณ์แบบ (PFS) การมีคีย์ลับแบบอสมมาตรระยะยาวตัวใดตัวหนึ่งที่ใช้สร้างเซสชัน HTTPS ไม่น่าจะทำให้การได้มาซึ่งคีย์เซสชันระยะสั้นเพื่อถอดรหัสการสนทนาทำได้ง่ายขึ้น แม้ในภายหลังก็ตาม ในปี 2013 มีเพียงการแลกเปลี่ยนคีย์ Diffie–Hellman (DHE) และการแลกเปลี่ยนคีย์ Diffie–Hellman แบบเส้นโค้งวงรี (ECDHE) เท่านั้นที่ทราบว่ามีคุณสมบัตินี้ ในปี 2013 มีเพียง 30% ของเซสชัน Firefox, Opera และ Chromium Browser เท่านั้นที่ใช้คุณสมบัตินี้ และเกือบ 0% ของเซสชัน Safari และ Microsoft Internet Explorer ของ Apple ที่ใช้คุณสมบัตินี้ TLS 1.3 ซึ่งเผยแพร่ในเดือนสิงหาคม 2018 ได้ยกเลิกการสนับสนุนการเข้ารหัสแบบไม่มีการเข้ารหัสแบบส่งต่อ ณ เดือนกุมภาพันธ์ พ.ศ. 2562 เว็บเซิร์ฟเวอร์ที่สำรวจ 96.6% รองรับการรักษาความลับแบบ Forward ในรูปแบบใดรูปแบบหนึ่ง และ 52.1% จะใช้การรักษาความลับแบบ Forward กับเบราว์เซอร์ส่วนใหญ่ ณ เดือนกรกฎาคม พ.ศ. 2566 เว็บเซิร์ฟเวอร์ที่สำรวจ 99.6% รองรับการรักษาความลับแบบ Forward ในรูปแบบใดรูปแบบหนึ่ง และ 75.2% จะใช้การรักษาความลับแบบ Forward กับเบราว์เซอร์ส่วนใหญ่ +#iiiii คุณสมบัติที่สำคัญในบริบทนี้คือการเข้ารหัสแบบส่งต่อที่สมบูรณ์แบบ (PFS) +การมีคีย์ลับแบบอสมมาตรระยะยาวตัวใดตัวหนึ่งที่ใช้สร้างเซสชัน HTTPS +ไม่น่าจะทำให้การได้มาซึ่งคีย์เซสชันระยะสั้นเพื่อถอดรหัสการสนทนาทำได้ง่ายขึ้น แม้ในภายหลังก็ตาม ในปี +2013 มีเพียงการแลกเปลี่ยนคีย์ Diffie--Hellman (DHE) และการแลกเปลี่ยนคีย์ Diffie--Hellman +แบบเส้นโค้งวงรี (ECDHE) เท่านั้นที่ทราบว่ามีคุณสมบัตินี้ ในปี 2013 มีเพียง 30% ของเซสชัน Firefox, +Opera และ Chromium Browser เท่านั้นที่ใช้คุณสมบัตินี้ และเกือบ 0% ของเซสชัน Safari และ +Microsoft Internet Explorer ของ Apple ที่ใช้คุณสมบัตินี้ TLS 1.3 ซึ่งเผยแพร่ในเดือนสิงหาคม +2018 ได้ยกเลิกการสนับสนุนการเข้ารหัสแบบไม่มีการเข้ารหัสแบบส่งต่อ ณ เดือนกุมภาพันธ์ พ.ศ. 2562 +เว็บเซิร์ฟเวอร์ที่สำรวจ 96.6% รองรับการรักษาความลับแบบ Forward ในรูปแบบใดรูปแบบหนึ่ง และ +52.1% จะใช้การรักษาความลับแบบ Forward กับเบราว์เซอร์ส่วนใหญ่ ณ เดือนกรกฎาคม พ.ศ. 2566 +เว็บเซิร์ฟเวอร์ที่สำรวจ 99.6% รองรับการรักษาความลับแบบ Forward ในรูปแบบใดรูปแบบหนึ่ง และ +75.2% จะใช้การรักษาความลับแบบ Forward กับเบราว์เซอร์ส่วนใหญ่ ===== การเพิกถอนใบรับรอง -ใบรับรองอาจถูกเพิกถอนก่อนหมดอายุได้ เช่น เนื่องจากความลับของคีย์ส่วนตัวถูกละเมิด เบราว์เซอร์ยอดนิยมเวอร์ชันที่ใหม่พอเช่น Firefox Opera และ Internet Explorer บน Windows Vista จะใช้ Online Certificate Status Protocol (OCSP) เพื่อตรวจสอบว่าไม่เป็นเช่นนั้น เบราว์เซอร์จะส่งหมายเลขซีเรียลของใบรับรองไปยังผู้ออกใบรับรองหรือผู้แทนผ่าน OCSP และผู้ออกใบรับรองจะตอบกลับ โดยแจ้งให้เบราว์เซอร์ทราบว่าใบรับรองยังคงใช้ได้อยู่หรือไม่ นอกจากนี้ CA อาจออกรายการเพิกถอนใบรับรอง (Certificate Revocation List; CRL) เพื่อแจ้งให้ผู้ใช้ทราบว่าใบรับรองเหล่านี้ถูกเพิกถอนแล้ว อย่างไรก็ตาม CRL ไม่จำเป็นสำหรับฟอรัม CA/Browser (“ฟอรัม CA/Browser” ดังกล่าวคือองค์กร) อีกต่อไป อย่างไรก็ตาม CA ยังคงใช้ CRL กันอย่างแพร่หลาย สถานะการเพิกถอนส่วนใหญ่บนอินเทอร์เน็ตจะหายไปในไม่ช้าหลังจากใบรับรองหมดอายุ +#iiiiii ใบรับรองอาจถูกเพิกถอนก่อนหมดอายุได้ เช่น เนื่องจากความลับของคีย์ส่วนตัวถูกละเมิด +เบราว์เซอร์ยอดนิยมเวอร์ชันที่ใหม่พอเช่น Firefox Opera และ Internet Explorer บน Windows +Vista จะใช้ Online Certificate Status Protocol (OCSP) เพื่อตรวจสอบว่าไม่เป็นเช่นนั้น +เบราว์เซอร์จะส่งหมายเลขซีเรียลของใบรับรองไปยังผู้ออกใบรับรองหรือผู้แทนผ่าน OCSP +และผู้ออกใบรับรองจะตอบกลับ โดยแจ้งให้เบราว์เซอร์ทราบว่าใบรับรองยังคงใช้ได้อยู่หรือไม่ นอกจากนี้ CA +อาจออกรายการเพิกถอนใบรับรอง (Certificate Revocation List; CRL) +เพื่อแจ้งให้ผู้ใช้ทราบว่าใบรับรองเหล่านี้ถูกเพิกถอนแล้ว อย่างไรก็ตาม CRL ไม่จำเป็นสำหรับฟอรัม +CA/Browser (“ฟอรัม CA/Browser” ดังกล่าวคือองค์กร) อีกต่อไป อย่างไรก็ตาม CA ยังคงใช้ CRL +กันอย่างแพร่หลาย สถานะการเพิกถอนส่วนใหญ่บนอินเทอร์เน็ตจะหายไปในไม่ช้าหลังจากใบรับรองหมดอายุ === ข้อจำกัด -#i การเข้ารหัส SSL (Secure Sockets Layer) และ TLS (Transport Layer Security) สามารถกำหนดค่าได้สองโหมด ได้แก่ โหมดธรรมดาและโหมด Mutual ในโหมดธรรมดา การตรวจสอบสิทธิ์จะดำเนินการโดยเซิร์ฟเวอร์เท่านั้น โหมด Mutual กำหนดให้ผู้ใช้ต้องติดตั้งใบรับรองไคลเอ็นต์ส่วนบุคคลในเว็บเบราว์เซอร์เพื่อการตรวจสอบสิทธิ์ผู้ใช้ ไม่ว่าในกรณีใด ระดับการป้องกันจะขึ้นอยู่กับความถูกต้องของการใช้งานซอฟต์แวร์และอัลกอริทึมการเข้ารหัสที่ใช้ +#iiii การเข้ารหัส SSL (Secure Sockets Layer) และ TLS (Transport Layer Security) +สามารถกำหนดค่าได้สองโหมด ได้แก่ โหมดธรรมดาและโหมด Mutual ในโหมดธรรมดา +การตรวจสอบสิทธิ์จะดำเนินการโดยเซิร์ฟเวอร์เท่านั้น โหมด Mutual +กำหนดให้ผู้ใช้ต้องติดตั้งใบรับรองไคลเอ็นต์ส่วนบุคคลในเว็บเบราว์เซอร์เพื่อการตรวจสอบสิทธิ์ผู้ใช้ +ไม่ว่าในกรณีใด +ระดับการป้องกันจะขึ้นอยู่กับความถูกต้องของการใช้งานซอฟต์แวร์และอัลกอริทึมการเข้ารหัสที่ใช้ -#i SSL/TLS ไม่ป้องกันการจัดทำดัชนีของเว็บไซต์โดยเว็บครอว์เลอร์ และในบางกรณี URI ของทรัพยากรที่เข้ารหัสสามารถอนุมานได้โดยการทราบขนาดคำขอ/การตอบสนองที่ถูกสกัดกั้นเท่านั้น วิธีนี้ช่วยให้ผู้โจมตีสามารถเข้าถึงข้อความธรรมดา (เนื้อหาคงที่ที่เปิดเผยต่อสาธารณะ) และข้อความที่เข้ารหัส (เนื้อหาคงที่เวอร์ชันเข้ารหัส) ทำให้สามารถโจมตีด้วยการเข้ารหัสได้ +#iiii SSL/TLS ไม่ป้องกันการจัดทำดัชนีของเว็บไซต์โดยเว็บครอว์เลอร์ และในบางกรณี URI +ของทรัพยากรที่เข้ารหัสสามารถอนุมานได้โดยการทราบขนาดคำขอ/การตอบสนองที่ถูกสกัดกั้นเท่านั้น +วิธีนี้ช่วยให้ผู้โจมตีสามารถเข้าถึงข้อความธรรมดา (เนื้อหาคงที่ที่เปิดเผยต่อสาธารณะ) และข้อความที่เข้ารหัส +(เนื้อหาคงที่เวอร์ชันเข้ารหัส) ทำให้สามารถโจมตีด้วยการเข้ารหัสได้ -#i เนื่องจาก TLS ทำงานที่ระดับโปรโตคอลที่ต่ำกว่า HTTP และไม่มีความรู้เกี่ยวกับโปรโตคอลระดับสูงกว่า เซิร์ฟเวอร์ TLS จึงสามารถแสดงใบรับรองได้เพียงใบเดียวสำหรับที่อยู่และพอร์ตที่กำหนดเท่านั้น ในอดีต นั่นหมายความว่าไม่สามารถใช้การโฮสต์เสมือนแบบอิงชื่อกับ HTTPS ได้ มีโซลูชันที่เรียกว่า Server Name Indication (SNI) ซึ่งส่งชื่อโฮสต์ไปยังเซิร์ฟเวอร์ก่อนเข้ารหัสการเชื่อมต่อ แม้ว่าเบราว์เซอร์รุ่นเก่าจะไม่รองรับส่วนขยายนี้ก็ตาม การรองรับ SNI มีให้ใช้งานตั้งแต่ Firefox 2, Opera 8, Apple Safari 2.1, Google Chrome 6 และ Internet Explorer 7 บน Windows Vista +#iiii เนื่องจาก TLS ทำงานที่ระดับโปรโตคอลที่ต่ำกว่า HTTP +และไม่มีความรู้เกี่ยวกับโปรโตคอลระดับสูงกว่า เซิร์ฟเวอร์ TLS +จึงสามารถแสดงใบรับรองได้เพียงใบเดียวสำหรับที่อยู่และพอร์ตที่กำหนดเท่านั้น ในอดีต +นั่นหมายความว่าไม่สามารถใช้การโฮสต์เสมือนแบบอิงชื่อกับ HTTPS ได้ มีโซลูชันที่เรียกว่า Server Name +Indication (SNI) ซึ่งส่งชื่อโฮสต์ไปยังเซิร์ฟเวอร์ก่อนเข้ารหัสการเชื่อมต่อ +แม้ว่าเบราว์เซอร์รุ่นเก่าจะไม่รองรับส่วนขยายนี้ก็ตาม การรองรับ SNI มีให้ใช้งานตั้งแต่ Firefox 2, +Opera 8, Apple Safari 2.1, Google Chrome 6 และ Internet Explorer 7 บน Windows +Vista -#i การโจมตีแบบ man-in-the-middle ที่ซับซ้อนประเภทหนึ่งที่เรียกว่า SSL stripping ถูกนำเสนอในงานประชุม Blackhat Conference ปี 2009 การโจมตีประเภทนี้ทำลายความปลอดภัยของ HTTPS โดยการเปลี่ยนลิงก์ https: ให้เป็นลิงก์ http: โดยใช้ประโยชน์จากข้อเท็จจริงที่ว่าผู้ใช้อินเทอร์เน็ตเพียงไม่กี่คนเท่านั้นที่พิมพ์ "https" ลงในอินเทอร์เฟซเบราว์เซอร์ พวกเขาจึงเข้าสู่เว็บไซต์ที่ปลอดภัยได้โดยการคลิกลิงก์ และถูกหลอกให้คิดว่ากำลังใช้ HTTPS ในขณะที่จริงๆ แล้วกำลังใช้ HTTP ผู้โจมตีจึงสื่อสารกับไคลเอ็นต์อย่างชัดเจน สิ่งนี้กระตุ้นให้เกิดการพัฒนามาตรการรับมือใน HTTP ที่เรียกว่า HTTP Strict Transport Security +#iiii การโจมตีแบบ man-in-the-middle ที่ซับซ้อนประเภทหนึ่งที่เรียกว่า SSL stripping +ถูกนำเสนอในงานประชุม Blackhat Conference ปี 2009 การโจมตีประเภทนี้ทำลายความปลอดภัยของ +HTTPS โดยการเปลี่ยนลิงก์ https: ให้เป็นลิงก์ http: +โดยใช้ประโยชน์จากข้อเท็จจริงที่ว่าผู้ใช้อินเทอร์เน็ตเพียงไม่กี่คนเท่านั้นที่พิมพ์ "https" +ลงในอินเทอร์เฟซเบราว์เซอร์ พวกเขาจึงเข้าสู่เว็บไซต์ที่ปลอดภัยได้โดยการคลิกลิงก์ +และถูกหลอกให้คิดว่ากำลังใช้ HTTPS ในขณะที่จริงๆ แล้วกำลังใช้ HTTP +ผู้โจมตีจึงสื่อสารกับไคลเอ็นต์อย่างชัดเจน สิ่งนี้กระตุ้นให้เกิดการพัฒนามาตรการรับมือใน HTTP ที่เรียกว่า +HTTP Strict Transport Security -#i HTTPS ได้รับการพิสูจน์แล้วว่ามีความเสี่ยงต่อการโจมตีวิเคราะห์ทราฟฟิกหลากหลายรูปแบบ การโจมตีวิเคราะห์ทราฟฟิกเป็นการโจมตีแบบ Side-Channel ประเภทหนึ่งที่อาศัยการเปลี่ยนแปลงเวลาและขนาดของทราฟฟิกเพื่ออนุมานคุณสมบัติของทราฟฟิกที่เข้ารหัส การวิเคราะห์ทราฟฟิกเป็นไปได้เนื่องจากการเข้ารหัส SSL/TLS เปลี่ยนแปลงเนื้อหาของทราฟฟิก แต่มีผลกระทบน้อยมากต่อขนาดและระยะเวลาของทราฟฟิก ในเดือนพฤษภาคม 2553 งานวิจัยโดยนักวิจัยจาก Microsoft Research และ Indiana University ค้นพบว่าข้อมูลผู้ใช้ที่ละเอียดอ่อนโดยละเอียดสามารถอนุมานได้จากช่องทางด้านข้าง เช่น ขนาดแพ็กเก็ต นักวิจัยพบว่าแม้จะมีการป้องกัน HTTPS ในแอปพลิเคชันเว็บชั้นนำที่มีชื่อเสียงหลายตัวในด้านการดูแลสุขภาพ ภาษี การลงทุน และการค้นหาเว็บ แต่ผู้แอบฟังสามารถอนุมานโรค/ยา/การผ่าตัดของผู้ใช้ รายได้ของครอบครัว และความลับในการลงทุนได้ +#iiii HTTPS ได้รับการพิสูจน์แล้วว่ามีความเสี่ยงต่อการโจมตีวิเคราะห์ทราฟฟิกหลากหลายรูปแบบ +การโจมตีวิเคราะห์ทราฟฟิกเป็นการโจมตีแบบ Side-Channel +ประเภทหนึ่งที่อาศัยการเปลี่ยนแปลงเวลาและขนาดของทราฟฟิกเพื่ออนุมานคุณสมบัติของทราฟฟิกที่เข้ารหัส +การวิเคราะห์ทราฟฟิกเป็นไปได้เนื่องจากการเข้ารหัส SSL/TLS เปลี่ยนแปลงเนื้อหาของทราฟฟิก +แต่มีผลกระทบน้อยมากต่อขนาดและระยะเวลาของทราฟฟิก ในเดือนพฤษภาคม 2553 งานวิจัยโดยนักวิจัยจาก +Microsoft Research และ Indiana University +ค้นพบว่าข้อมูลผู้ใช้ที่ละเอียดอ่อนโดยละเอียดสามารถอนุมานได้จากช่องทางด้านข้าง เช่น ขนาดแพ็กเก็ต +นักวิจัยพบว่าแม้จะมีการป้องกัน HTTPS ในแอปพลิเคชันเว็บชั้นนำที่มีชื่อเสียงหลายตัวในด้านการดูแลสุขภาพ +ภาษี การลงทุน และการค้นหาเว็บ แต่ผู้แอบฟังสามารถอนุมานโรค/ยา/การผ่าตัดของผู้ใช้ +รายได้ของครอบครัว และความลับในการลงทุนได้ -#i ความจริงที่ว่าเว็บไซต์สมัยใหม่ส่วนใหญ่ รวมถึง Google, Yahoo! และ Amazon ใช้ HTTPS ทำให้เกิดปัญหาสำหรับผู้ใช้จำนวนมากที่พยายามเข้าถึงจุดเชื่อมต่อ Wi-Fi สาธารณะ เนื่องจากหน้าเข้าสู่ระบบจุดเชื่อมต่อ Wi-Fi ของพอร์ทัลแบบแคปทีฟไม่สามารถโหลดได้หากผู้ใช้พยายามเปิดทรัพยากร HTTPS และเว็บไซต์หลายแห่ง เช่น NeverSSL รับประกันว่าเว็บไซต์เหล่านั้นจะสามารถเข้าถึงได้ผ่าน HTTP เสมอ +#iiii ความจริงที่ว่าเว็บไซต์สมัยใหม่ส่วนใหญ่ รวมถึง Google, Yahoo! และ Amazon ใช้ HTTPS +ทำให้เกิดปัญหาสำหรับผู้ใช้จำนวนมากที่พยายามเข้าถึงจุดเชื่อมต่อ Wi-Fi สาธารณะ +เนื่องจากหน้าเข้าสู่ระบบจุดเชื่อมต่อ Wi-Fi +ของพอร์ทัลแบบแคปทีฟไม่สามารถโหลดได้หากผู้ใช้พยายามเปิดทรัพยากร HTTPS และเว็บไซต์หลายแห่ง เช่น +NeverSSL รับประกันว่าเว็บไซต์เหล่านั้นจะสามารถเข้าถึงได้ผ่าน HTTP เสมอ diff --git a/Chapter2/Intro.typ b/Chapter2/Intro.typ index 26af356..9538d94 100644 --- a/Chapter2/Intro.typ +++ b/Chapter2/Intro.typ @@ -27,15 +27,17 @@ อุปกรณ์ควบคุมขนาดเล็ก ซึ่งบรรจุความสามารถที่คล้ายคลึงกับระบบคอมพิวเตอร์ โดยใน ไมโครคอนโทรลเลอร์ได้รวม เอาซีพียูหน่วยความจำและพอร์ต ซึ่งเป็นส่วนประกอบหลักสำคัญของ ระบบคอมพิวเตอร์เข้าไว้ด้วยกัน โดยทำการบรรจุเข้าไว้ในตัวถังเดียวกัน ไมโครคอนโทรลเลอร์ถ้าแปล -ความหมายแบบตรงตัวก็คือ ระบบคอนโทรลขนาดเล็กเรียกอีกอย่างหนึ่งคือเป็นระบบคอมพิวเตอร์ ขนาดเล็ก +ความหมายแบบตรงตัวก็คือ ระบบคอนโทรลขนาดเล็กเรียกอีกอย่างหนึ่งคือเป็นระบบคอมพิวเตอร์ ขนาดเล็ก#jb ที่สามารถนำมาประยุกต์ใช้งานได้หลากหลาย โดยผ่านการออกแบบวงจรให้เหมาะกับงาน ต่างๆ -และยังสามารถเขียนโปรแกรมคำสั่งเพื่อควบคุมขา Input/Output เพื่อสั่งงานให้ไป ควบคุม อุปกรณ์ต่างๆ -ได้อีกด้วย ซึ่งก็นับว่าเป็นระบบที่สามารถนำมาประยุกต์ใช้งานได้หลากหลาย ทั้ง ทางด้าน Digital และ -Analog ยกตัวอย่างเช่น ระบบสัญญาณตอบรับอัตโนมัติ, ระบบบัตรคิว, ระบบ ตอกบัตรพนักงาน และอื่นๆ +และยังสามารถเขียนโปรแกรมคำสั่งเพื่อควบคุมขา Input/Output เพื่อสั่งงานให้ไป ควบคุม อุปกรณ์ต่าง +ๆ#jb ได้อีกด้วย ซึ่งก็นับว่าเป็นระบบที่สามารถนำมาประยุกต์ใช้งานได้หลากหลาย ทั้ง ทางด้าน Digital +และ Analog ยกตัวอย่างเช่น ระบบสัญญาณตอบรับอัตโนมัติ, ระบบบัตรคิว, ระบบ ตอกบัตรพนักงาน และอื่นๆ ยิ่งระบบไมโครคอนโทรลเลอร์ในยุคปัจจุบันนั้นสามารถทำการเชื่อต่อกับ ระบบ Network ของคอมพิวเตอร์ทั่วไปได้อีกด้วย ดังนั้นการสั่งงานจึงไม่ใช่แค่หน้าแผงวงจร แต่ อาจจะเป็นการสั่งงานอยู่คนละ ซีกโลกผ่านเครือข่ายอินเตอร์เน็ตก็ได้โครงสร้างโดยทั่วไปของไมโครคอนโทรลเลอร์นั้น สามารถแบ่งออกมาได้เป็น 5 ส่วนใหญ่ ๆ ได้แก่ หน่วยประมวลผลกลาง หรือ ซีพียู, หน่วยความจำ, ส่วนติดต่อกับอุปกรณ์ภายนอก หรือพอร์ต, ช่องทางเดินของสัญญาณ หรือบัส และ วงจรกำเนิดสัญญาณนาฬิกา#jb -หน่วยความจำนั้น สามารถแบ่งออกเป็น 2 ส่วนคือ หน่วยความจำที่มีไว้สำหรับเก็บ โปรแกรมหลัก -เปรียบเสมือนฮาร์ดดิสก์และหน่วยความจำข้อมูล ใช้เป็นเหมือนกับ กระดาษทดในการ คำนวณของ#jb +หน่วยความจำนั้น สามารถแบ่งออกเป็น 2 ส่วนคือ หน่วยความจำที่มีไว้สำหรับเก็บ โปรแกรมหลัก#jb +เปรียบเสมือนฮาร์ดดิสก์และหน่วยความจำข้อมูล ใช้เป็นเหมือนกับ กระดาษทดในการ คำนวณของ#jb ซีพียู +โดย ESP32 เป็นไมโครคอนโทรลเลอร์แบบ System-on-a-Chip (SoC) ที่มีการรวมส่วนประกอบ#jb +ทั้งหมดที่จำเป็นสำหรับการประมวลผลและการสื่อสารไร้สายไว้ในชิปเดียว ที่มีคุณสมบัติเด่นด้านการ#jb diff --git a/Chapter2/Microcontroller.typ b/Chapter2/Microcontroller.typ index df27793..e5c8cbb 100644 --- a/Chapter2/Microcontroller.typ +++ b/Chapter2/Microcontroller.typ @@ -1,10 +1,7 @@ -#import "../PageTemplate.typ": i, jb +#import "../PageTemplate.typ": * -ซีพียู โดย ESP32 เป็นไมโครคอนโทรลเลอร์แบบ System-on-a-Chip (SoC) -ที่มีการรวมส่วนประกอบทั้งหมดที่จำเป็นสำหรับการประมวลผลและการสื่อสารไร้สายไว้ในชิปเดียว -ที่มีคุณสมบัติเด่นด้านการเชื่อมต่อ Wi-Fi และ Bluetooth ในตัว เป็นชิปไมโครคอนโทรลเลอร์แบบ 32 บิต -ที่มีความสามารถสูง พัฒนาและผลิตโดย บริษัท Espressif Systems จากประเทศจีน -ส่วนประกอบหลักของบอร์ด ESP32 +เชื่อมต่อ Wi-Fi และ Bluetooth ในตัว เป็นชิปไมโครคอนโทรลเลอร์แบบ 32 บิต ที่มีความสามารถสูง +พัฒนาและผลิตโดย บริษัท Espressif Systems จากประเทศจีน ส่วนประกอบหลักของบอร์ด ESP32 #i ESP32 คือ ไมโครคอนโทรลเลอร์ราคาประหยัดและประหยัดพลังงานที่ผสานรวมความสามารถทั้ง Wi-Fi และ Bluetooth ชิปเหล่านี้มีตัวเลือกการประมวลผลที่หลากหลาย รวมถึง ไมโครโปรเซสเซอร์#jb @@ -38,26 +35,30 @@ Tensilica Xtensa LX6 ที่มีทั้งแบบดูอัลคอ #let partition-table = csv("PartitionTable.csv") -#table( - columns: 6, - table.header([Name], [Type], [SubType], [Offset], [Size], [Flags]), - ..partition-table.flatten().slice(6), +#figure( + table( + columns: 6, + align: left, + table.header([Name], [Type], [SubType], [Offset], [Size], [Flags]), + ..partition-table.flatten().slice(6), + ), + caption: [รายการพาร์ทิชัน], ) ซึ่งคือตารางค่าเริ่มต้นของ ESP32 ใน Arduino platform อย่างไรก็ตามมีการเปลี่ยนแปลงระบบเก็บไฟล์จาก SPIFFS เป็น LittleFS โดยที่: -+ *Name:* ชื่อของพาร์ทิชัน ห้ามซ้ำกัน ชื่อนั้นไม่สำคัญต่อระบบและต้องขนาดไม่เกิน 16 ตัวอักษร ++ Name: ชื่อของพาร์ทิชัน ห้ามซ้ำกัน ชื่อนั้นไม่สำคัญต่อระบบและต้องขนาดไม่เกิน 16 ตัวอักษร (ไม่มีอักขระพิเศษ) -+ *Type:* ประเภทของพาร์ทัชัน สามารถเป็น `data` หรือ `app` ได้ - - `app` คือพาร์ทิชันที่ใช้ในการเก็บแอพลิเคชัน - - `data` คือพาร์ทิชันที่ใช้ในการเก็บข้อมูลทั่วไป -+ *SubType:* ประเภทย่อย ระบุการใช้งานของพาร์ทิชัน `app` และ `data` - - `data` - - `ota`: พาร์ทัชันเก็บข้อมูล OTA (สำหรับการอัพเดททางอากาศ, Over-the-air update) ++ Type: ประเภทของพาร์ทัชัน สามารถเป็น data หรือ app ได้ + - app คือพาร์ทิชันที่ใช้ในการเก็บแอพลิเคชัน + - data คือพาร์ทิชันที่ใช้ในการเก็บข้อมูลทั่วไป ++ SubType: ประเภทย่อย ระบุการใช้งานของพาร์ทิชัน app และ data + - data + - ota: พาร์ทัชันเก็บข้อมูล OTA (สำหรับการอัพเดททางอากาศ, Over-the-air update) โดยหากไม่ใช้งาน OTA สามารถนำออกได้ โดยขนาดของพาร์ทิชันนี้ควรจะมีขนาดที่แน่นอนอยู่ที่ 8 KiB (0x2000 ไบต์) - - `nvs`: พาร์ทิชันเก็บข้อมูลทั่วไปเช่น ข้อมูล Wi-Fi, ข้อมูลการสอบเทียบ PHY ของอุปกรณ์, + - nvs: พาร์ทิชันเก็บข้อมูลทั่วไปเช่น ข้อมูล Wi-Fi, ข้อมูลการสอบเทียบ PHY ของอุปกรณ์, และข้อมูลอื่น ๆ ที่ต้องถูกเก็บบนหน่วยความจำถาวร (Non-volatile memory) โดยพาร์ทิชันประเภทนี้เหมาะสมสำหรับการเก็บข้อมูลการตั้งค่าเล็กน้อย ใบรองรับคลาวด์ ฯลฯ และการใช้งาน NVS อีกอย่างคือการเก็บข้อมูลที่ละเอียดอ่อน เนื่องจาก NVS รองรับการเข้ารหัส @@ -65,64 +66,52 @@ Tensilica Xtensa LX6 ที่มีทั้งแบบดูอัลคอ และหากจำเป็น คุณสามารถขยายขนาดเพิ่มได้ โดยขนาดที่แนะนำนั้นอยู่ระหว่าง 12 KiB และ 64 KiB ถึงแม้ว่าคุณจะสามารถขยายให้มันใหญ่กว่านี้ได้ การใช้งานระบบไฟล์เช่น FAT หรือ SPIFFS นั้นจะเหมาะสมสำหรับข้อมูลที่ใหญ่กว่า - - `coredump`: ประเภทพาร์ทิชันย่อยนี้มีหน้าที่ในการเก็บข้อมูล core dump บนหน่วยความจำแฟลช - โดย core dump - นั้นคือข้อมูลที่ถูกใช้งานสำหรับการตรวจสอบข้อผิด-พลาดร้ายแรงเช่นการแครชและแพนิค + - coredump: ประเภทพาร์ทิชันย่อยนี้มีหน้าที่ในการเก็บข้อมูล core dump บนหน่วยความจำแฟลช โดย + core dump นั้นคือข้อมูลที่ถูกใช้งานสำหรับการตรวจสอบข้อผิด-พลาดร้ายแรงเช่นการแครชและแพนิค โดยฟังก์ชันนี้จะต้องถูกเปิดในการตั้งค่าโปรเจกต์และตั้งที่หมายในการแฟลช และพาร์ทิชันนี้มีขนาดที่แนะนำอยู่ที่ 64 KiB (0x10000) - - `nvs_keys`: พาร์ทิชันที่เป็นประเภทย่อยนี้เก็บคีย์การเข้ารหัสของพาร์ทัชัน NVS + - nvs_keys: พาร์ทิชันที่เป็นประเภทย่อยนี้เก็บคีย์การเข้ารหัสของพาร์ทัชัน NVS เมื่อการเข้ารหัสถูกใช้งาน โดยมีขนาดอยู่ที่ 4 KiB (0x1000) - - `fat`: กำหนดพาร์ทิชันสำหรับระบบไฟล์ FAT โดยที่จะเหมาะสมสำหรับข้อมูลใหญ่ ๆ + - fat: กำหนดพาร์ทิชันสำหรับระบบไฟล์ FAT โดยที่จะเหมาะสมสำหรับข้อมูลใหญ่ ๆ และหากข้อมูลนั้นถูกเปลี่ยนแปลงบ่อย โดยระบบไฟล์ FAT สามารถใช้ฟีเจอร์ wear leveling และการเข้ารหัสได้ - - `spiffs`: กำหนดพาร์ทิชันสำหรับระบบไฟล์ SPIFFS เหมาะสำหรับไฟล์ใหญ่เช่นกันและรองรับ - wear leveling อย่างไรก็ตาม ระบบไฟล์นี้ไม่รองรับการเข้ารหัส - - `app` - - `factory`: พาร์ทิชันเก็บแอพลิเคชันเริ่มต้น + - spiffs: กำหนดพาร์ทิชันสำหรับระบบไฟล์ SPIFFS เหมาะสำหรับไฟล์ใหญ่เช่นกันและรองรับ wear + leveling อย่างไรก็ตาม ระบบไฟล์นี้ไม่รองรับการเข้ารหัส + - app + - factory: พาร์ทิชันเก็บแอพลิเคชันเริ่มต้น โปรแกรมบูตโหลดเดอร์จะเลือกพาร์ทิชันนี้เป็นแอพลิเคชันเริ่มต้นหากไม่มีพาร์ทิชัน OTA หรือพาร์ทิชัน - OTA นั้นว่างเปล่า หากมีการใช้พาร์ทิชัน OTA พาร์ทิชัน `ota_0` - สามารถถูกใช้เป็นแอพลิเคชันเริ่มต้นได้และพาร์ทิชัน `factory` สามารถถูกนำออกได้ - - `ota_0` ถึง `ota_15`: พาร์ทิชัน ota_x นั้นถูกใช้สำหรับอัพเดท OTA โดยฟีเจอร์ OTA + OTA นั้นว่างเปล่า หากมีการใช้พาร์ทิชัน OTA พาร์ทิชัน ota_0 + สามารถถูกใช้เป็นแอพลิเคชันเริ่มต้นได้และพาร์ทิชัน factory สามารถถูกนำออกได้ + - ota_0 ถึง ota_15: พาร์ทิชัน ota_x นั้นถูกใช้สำหรับอัพเดท OTA โดยฟีเจอร์ OTA นั้นจำเป็นต้องใช้พาร์ทิชัน OTA อย่างน้อย 2 พาร์ทิชัน (โดยปกติคือ ota_0 และ ota_1) และจำเป็นต้องใช้พาร์ทิชัน ota ด้วยเช่นกันในการเก็บข้อมูลเกี่ยวกับ OTA โดยสามารถมีพาร์ทิชัน OTA ได้สูงสุด 16 พาร์ทิชัน แต่ 2 พาร์ทิชันคือจำนวนขั้นต่ำที่ต้องใช้สำหรับฟีเจอร์ OTA แบบเบสิค - - `test`: ใช้สำหรับการทดสอบในโรงงาน -+ *Offset:* กำหนดพื้นที่ที่พาร์ทิชันนั้น ๆ เริ่มต้น โดย Offset นั้นถูกกำหนดโดยการรวมค่า Offset + - test: ใช้สำหรับการทดสอบในโรงงาน ++ Offset: กำหนดพื้นที่ที่พาร์ทิชันนั้น ๆ เริ่มต้น โดย Offset นั้นถูกกำหนดโดยการรวมค่า Offset และขนาดของพาร์ทิชันก่อนหน้า \ - *หมายเหตุ:* Offset จะต้องเป็นทวีคูณของ 4 KiB (0x1000) + หมายเหตุ: Offset จะต้องเป็นทวีคูณของ 4 KiB (0x1000) และพาร์ทิชันแอพจะต้องจัดตำแหน่งให้มีขนาด 64 KiB (0x10000) โดยหากปล่อยให้ว่าง ค่า Offset จะถูกคำนวนโดยอัตโนมัติตามตำแหน่งท้ายของพาร์ทิชันก่อนหน้า รวมถึงการจัดตำแหน่งใด ๆ ที่จำเป็น - อย่างไรก็ตาม Offset ของพาร์ทิชันแรกนั้นจะต้องเป็น *0x9000* และ *0x10000* + อย่างไรก็ตาม Offset ของพาร์ทิชันแรกนั้นจะต้องเป็น 0x9000 และ 0x10000 สำหรับพาร์ทิชันแอพลิเคชันแรก -+ *Size:* ขนาดของพาร์ทิชัน โดยค่านี้สามารถเป็นเลขทศนิยม, ตัวเลข Hex (นำหน้าด้วย `0x`), ++ Size: ขนาดของพาร์ทิชัน โดยค่านี้สามารถเป็นเลขทศนิยม, ตัวเลข Hex (นำหน้าด้วย 0x), หรือใช้ตัวอักษรต่อท้ายเพื่อบ่งบอกหน่วย K (กิโล) หรือ M (เมกา) เช่น 4096 = 4K = 0x1000 -+ *Flags:* ในปัจจุบันคอลัมน์นี้ใช้เพียงแค่เพื่อบ่งบอกว่าพาร์ทิชันนั้น ๆ ถูกเข้ารหัสหรือไม่ ++ Flags: ในปัจจุบันคอลัมน์นี้ใช้เพียงแค่เพื่อบ่งบอกว่าพาร์ทิชันนั้น ๆ ถูกเข้ารหัสหรือไม่ -== LittleFS -#i LittleFS คือระบบไฟล์ขนาดเล็กที่ปลอดภัยต่อความล้มเหลวที่ออกแบบมาสำหรับ#jb +== littlefs +#i littlefs คือระบบไฟล์ขนาดเล็กที่ปลอดภัยต่อความล้มเหลวที่ออกแบบมาสำหรับ#jb ไมโครคอนโทรลเลอร์ -#show raw: set par(leading: 0.5em) - -``` - | | | .---._____ - .-----. | | ---|o |---| littlefs | ---| |---| | - '-----' '----------' - | | | -``` - -*ความยืดหยุ่นในการป้องกันการสูญเสียพลังงาน* littlefs ออกแบบมาเพื่อรับมือกับปัญหาไฟฟ้าดับแบบสุ่ม +ความยืดหยุ่นในการป้องกันการสูญเสียพลังงาน littlefs ออกแบบมาเพื่อรับมือกับปัญหาไฟฟ้าดับแบบสุ่ม การดำเนินการไฟล์ทั้งหมดมีการรับประกันการคัดลอกข้อมูลเมื่อเขียนข้อมูล (copy-on-write) ที่แข็งแกร่ง และหากไฟฟ้าดับ ระบบไฟล์จะกลับสู่สถานะปกติล่าสุดที่ทราบ -*การปรับระดับการสึกหรอแบบไดนามิก* littlefs ออกแบบมาเพื่อแฟลชโดยเฉพาะ +การปรับระดับการสึกหรอแบบไดนามิก littlefs ออกแบบมาเพื่อแฟลชโดยเฉพาะ และมอบการปรับระดับการสึกหรอบนบล็อกแบบไดนามิก นอกจากนี้ littlefs ยังสามารถตรวจจับบล็อกเสียและแก้ไขปัญหาได้ -*RAM/ROM แบบมีขอบเขต* littlefs ออกแบบมาเพื่อทำงานกับหน่วยความจำขนาดเล็ก การใช้งาน#jb -RAM ถูกจำกัดอย่างเข้มงวด ซึ่งหมายความว่าการใช้ RAM จะไม่เปลี่ยนแปลงเมื่อระบบไฟล์เติบโตขึ้น#jb +RAM/ROM แบบมีขอบเขต littlefs ออกแบบมาเพื่อทำงานกับหน่วยความจำขนาดเล็ก การใช้งาน#jb RAM +ถูกจำกัดอย่างเข้มงวด ซึ่งหมายความว่าการใช้ RAM จะไม่เปลี่ยนแปลงเมื่อระบบไฟล์เติบโตขึ้น#jb ระบบไฟล์ไม่มีการเรียกซ้ำแบบไม่มีขอบเขต และหน่วยความจำแบบไดนามิกถูกจำกัดให้อยู่ในบัฟเฟอร์ที่กำหนดค่าได้ซึ่งสามารถจัดเตรียมแบบคงที่ได้ @@ -140,33 +129,41 @@ RAM ถูกจำกัดอย่างเข้มงวด ซึ่งห โดยการจำกัดจำนวนการลบข้อมูลที่อนุญาตบนบล็อกต่อการจัดสรรแต่ละครั้ง ตัวจัดสรรจะปรับระดับการสึกหรอแบบไดนามิกทั่วทั้งระบบไฟล์ -``` - root - .--------.--------. - | A'| B'| | - | | |-> | - | | | | - '--------'--------' - .----' '--------------. - A v B v - .--------.--------. .--------.--------. - | C'| D'| | | E'|new| | - | | |-> | | | E'|-> | - | | | | | | | | - '--------'--------' '--------'--------' - .-' '--. | '------------------. - v v .-' v -.--------. .--------. v .--------. -| C | | D | .--------. write | new E | -| | | | | E | ==> | | -| | | | | | | | -'--------' '--------' | | '--------' - '--------' .-' | - .-' '-. .-------------|------' - v v v v - .--------. .--------. .--------. - | F | | G | | new F | - | | | | | | - | | | | | | - '--------' '--------' '--------' -``` +#show raw: set par(leading: 0.4em) +#show raw: set text(size: 8pt) + +#afigure( + ``` + root + .--------.--------. + | A'| B'| | + | | |-> | + | | | | + '--------'--------' + .----' '--------------. + A v B v + .--------.--------. .--------.--------. + | C'| D'| | | E'|new| | + | | |-> | | | E'|-> | + | | | | | | | | + '--------'--------' '--------'--------' + .-' '--. | '------------------. + v v .-' v + .--------. .--------. v .--------. + | C | | D | .--------. write | new E | + | | | | | E | ==> | | + | | | | | | | | + '--------' '--------' | | '--------' + '--------' .-' | + .-' '-. .-------------|------' + v v v v + .--------. .--------. .--------. + | F | | G | | new F | + | | | | | | + | | | | | | + '--------' '--------' '--------' + ```, + kind: "image", + supplement: "รูปที่", + caption: [แสดงการทำงานเบื้องต้นของ LittleFS], +) diff --git a/Chapter2/PIR.typ b/Chapter2/PIR.typ index f9e8403..108fa8c 100644 --- a/Chapter2/PIR.typ +++ b/Chapter2/PIR.typ @@ -5,79 +5,145 @@ = เซ็นเซอร์อินฟราเรดแบบพาสซีฟ (PIR sensor) -#i เซ็นเซอร์อินฟราเรดแบบพาสซีฟ (PIR sensor) คือ เซ็นเซอร์อิเล็กทรอนิกส์ที่วัดแสงอินฟราเรด (IR) ที่แผ่ออกมาจากวัตถุในระยะการมองเห็น เซ็นเซอร์ชนิดนี้มักใช้ในเครื่องตรวจจับความเคลื่อนไหว แบบ PIR เซ็นเซอร์ PIR มักใช้ในสัญญาณเตือนภัยและระบบไฟส่องสว่างอัตโนมัติ +#iii เซ็นเซอร์อินฟราเรดแบบพาสซีฟ (PIR sensor) คือ เซ็นเซอร์อิเล็กทรอนิกส์ที่วัดแสงอินฟราเรด +(IR) ที่แผ่ออกมาจากวัตถุในระยะการมองเห็น เซ็นเซอร์ชนิดนี้มักใช้ในเครื่องตรวจจับความเคลื่อนไหว แบบ +PIR เซ็นเซอร์ PIR มักใช้ในสัญญาณเตือนภัยและระบบไฟส่องสว่างอัตโนมัติ #afigure( image("PIR/Front-Fresnel_type.jpg", height: image-height), - attr: [Jack LaRosa, Public Domain, https://commons.wikimedia.org/w/index.php?curid=4479143], + attr: [Jack LaRosa, Public Domain, + https://commons.wikimedia.org/w/index.php?curid=4479143], alt: "เซนเซอร์ตรวจจับการเคลื่อนไหวทรงสี่เหลี่ยมผืนผ้าแนวตั้ง", caption: [เครื่องตรวจจับการเคลื่อนไหวแบบ PIR ทั่วไปสำหรับที่พักอาศัย/เชิงพาณิชย์], ) -#i เซ็นเซอร์ PIR ตรวจจับการเคลื่อนไหวทั่วไป แต่ไม่ได้ให้ข้อมูลว่าใครหรือสิ่งใดเคลื่อนไหว ดังนั้น จึงจำเป็นต้องใช้ เซ็นเซอร์ IR แบบสร้างภาพ เซ็นเซอร์ PIR มักเรียกสั้นๆ ว่า "PIR" หรือบางครั้งเรียกว่า "PID" ซึ่งย่อมาจาก "เครื่องตรวจจับอินฟราเรดแบบพาสซีฟ" เซ็นเซอร์ PIR ตรวจจับการเคลื่อนไหวทั่วไป แต่ไม่ได้ให้ข้อมูลว่าใครหรือสิ่งใดเคลื่อนไหว ดังนั้น จึงจำเป็นต้องใช้ เซ็นเซอร์ IR แบบสร้างภาพ เซ็นเซอร์ PIR มักเรียกสั้นๆ ว่า "PIR" หรือบางครั้งเรียกว่า "PID" ซึ่งย่อมาจาก "เครื่องตรวจจับอินฟราเรดแบบพาสซีฟ" คำว่าพาสซีฟหมายถึงข้อเท็จจริงที่ว่าอุปกรณ์ PIR ไม่ได้แผ่พลังงานเพื่อจุดประสงค์ในการตรวจจับ แต่ทำงานโดยการตรวจจับรังสีอินฟราเรด (ความร้อนจากการแผ่รังสี) ที่แผ่ออกมาจากหรือสะท้อนจากวัตถุ เท่านั้นซีฟ" คำว่าพาสซีฟหมายถึงข้อเท็จจริงที่ว่าอุปกรณ์ PIR ไม่ได้แผ่พลังงานเพื่อจุดประสงค์ในการตรวจจับ แต่ทำงานโดยการตรวจจับรังสีอินฟราเรด (ความร้อนจากการแผ่รังสี) ที่แผ่ออกมาจากหรือสะท้อนจากวัตถุ เท่านั้น +#i เซ็นเซอร์ PIR ตรวจจับการเคลื่อนไหวทั่วไป แต่ไม่ได้ให้ข้อมูลว่าใครหรือสิ่งใดเคลื่อนไหว ดังนั้น +จึงจำเป็นต้องใช้ เซ็นเซอร์ IR แบบสร้างภาพ เซ็นเซอร์ PIR มักเรียกสั้นๆ ว่า "PIR" +หรือบางครั้งเรียกว่า "PID" ซึ่งย่อมาจาก "เครื่องตรวจจับอินฟราเรดแบบพาสซีฟ" เซ็นเซอร์ PIR +ตรวจจับการเคลื่อนไหวทั่วไป แต่ไม่ได้ให้ข้อมูลว่าใครหรือสิ่งใดเคลื่อนไหว ดังนั้น จึงจำเป็นต้องใช้ เซ็นเซอร์ +IR แบบสร้างภาพ เซ็นเซอร์ PIR มักเรียกสั้นๆ ว่า "PIR" หรือบางครั้งเรียกว่า "PID" ซึ่งย่อมาจาก +"เครื่องตรวจจับอินฟราเรดแบบพาสซีฟ" คำว่าพาสซีฟหมายถึงข้อเท็จจริงที่ว่าอุปกรณ์ PIR +ไม่ได้แผ่พลังงานเพื่อจุดประสงค์ในการตรวจจับ แต่ทำงานโดยการตรวจจับรังสีอินฟราเรด +(ความร้อนจากการแผ่รังสี) ที่แผ่ออกมาจากหรือสะท้อนจากวัตถุ เท่านั้นซีฟ" +คำว่าพาสซีฟหมายถึงข้อเท็จจริงที่ว่าอุปกรณ์ PIR ไม่ได้แผ่พลังงานเพื่อจุดประสงค์ในการตรวจจับ +แต่ทำงานโดยการตรวจจับรังสีอินฟราเรด (ความร้อนจากการแผ่รังสี) ที่แผ่ออกมาจากหรือสะท้อนจากวัตถุ +เท่านั้น == หลักการทำงาน -#i วัตถุทุกชนิดที่มีอุณหภูมิสูงกว่าศูนย์องศาสัมบูรณ์จะปล่อยพลังงานความร้อนออกมาในรูปของรังสีแม่เหล็กไฟฟ้า โดยปกติแล้วรังสีนี้มองไม่เห็นด้วยตาเปล่าเนื่องจากแผ่รังสีในช่วงความยาวคลื่นอินฟราเรด แต่อุปกรณ์อิเล็กทรอนิกส์ที่ออกแบบมาเพื่อจุดประสงค์นี้ สามารถตรวจจับได้ +#iiii +วัตถุทุกชนิดที่มีอุณหภูมิสูงกว่าศูนย์องศาสัมบูรณ์จะปล่อยพลังงานความร้อนออกมาในรูปของรังสีแม่เหล็กไฟฟ้า +โดยปกติแล้วรังสีนี้มองไม่เห็นด้วยตาเปล่าเนื่องจากแผ่รังสีในช่วงความยาวคลื่นอินฟราเรด +แต่อุปกรณ์อิเล็กทรอนิกส์ที่ออกแบบมาเพื่อจุดประสงค์นี้ สามารถตรวจจับได้ == เครื่องตรวจจับการเคลื่อนไหวแบบ PIR #afigure( image("PIR/Motion_detector.jpg", height: image-height), alt: "เครื่องตรวจจับความเคลื่อนไหว ติดตั้งบนเพดาน", - attr: [CHG, Public Domain, https://commons.wikimedia.org/w/index.php?curid=6087132], - caption: [เครื่องตรวจจับความเคลื่อนไหว PIR - ใช้สำหรับควบคุมไฟภายนอกอาคารแบบอัตโนมัติ], + attr: [CHG, Public Domain, + https://commons.wikimedia.org/w/index.php?curid=6087132], + caption: [เครื่องตรวจจับความเคลื่อนไหว PIR ใช้สำหรับควบคุมไฟภายนอกอาคารแบบอัตโนมัติ], ) #afigure( image("PIR/Camera_trap,_fotopułapka,_kamera_leśna,_kamera_obserwacyjna.jpg"), - attr: [Dariusz Kowalczyk, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=96211951], + attr: [Dariusz Kowalczyk, CC BY-SA 4.0, + https://commons.wikimedia.org/w/index.php?curid=96211951], alt: "กล้องดักถ่ายที่ถูกพันรอบต้นไม้", caption: [กล้องดักถ่ายพร้อมระบบตรวจจับความเคลื่อนไหวแบบ PIR], ) #afigure( - image("PIR/Light_switch_with_passive_infrared_sensor.jpg", height: image-height), + image( + "PIR/Light_switch_with_passive_infrared_sensor.jpg", + height: image-height, + ), alt: "สวิตช์ไฟทรงสี่เหลี่ยมผืนผ้าแนวตั้งที่ติดตั้งเซนเซอร์ตรวจจับคน", - attr: [Z22, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=35183184], + attr: [Z22, CC BY-SA 4.0, + https://commons.wikimedia.org/w/index.php?curid=35183184], caption: [สวิตช์ไฟภายในอาคารที่ติดตั้งเซนเซอรตรวจจับการครอบครองแบบ PIR], ) -#i เครื่องตรวจจับความเคลื่อนไหวแบบ PIR ใช้เพื่อตรวจจับการเคลื่อนไหวของคน สัตว์ หรือวัตถุอื่นๆ มักใช้กับสัญญาณกันขโมยและระบบไฟส่องสว่างแบบอัตโนมัติ +#i เครื่องตรวจจับความเคลื่อนไหวแบบ PIR ใช้เพื่อตรวจจับการเคลื่อนไหวของคน สัตว์ หรือวัตถุอื่นๆ +มักใช้กับสัญญาณกันขโมยและระบบไฟส่องสว่างแบบอัตโนมัติ == การดำเนินการ -#i เซ็นเซอร์ PIR สามารถตรวจจับการเปลี่ยนแปลงของปริมาณรังสีอินฟราเรดที่กระทบกับวัตถุ ซึ่งจะแตกต่างกันไปขึ้นอยู่กับอุณหภูมิและลักษณะพื้นผิวของวัตถุที่อยู่ด้านหน้าเซ็นเซอร์เมื่อวัตถุ เช่น บุคคล ผ่านด้านหน้าพื้นหลัง เช่น กำแพง อุณหภูมิ ณ จุดนั้นในมุมมองของเซ็นเซอร์จะเพิ่มขึ้นจากอุณหภูมิห้องเป็นอุณหภูมิร่างกายแล้วกลับมาอีกครั้ง เซ็นเซอร์จะแปลงการเปลี่ยนแปลงที่เกิดขึ้นของรังสีอินฟราเรดที่เข้ามาเป็นการเปลี่ยนแปลงของแรงดันไฟฟ้าขาออก และสิ่งนี้จะกระตุ้นการตรวจจับ วัตถุที่มีอุณหภูมิใกล้เคียงกันแต่มีลักษณะพื้นผิวต่างกันอาจมีรูปแบบการปล่อยรังสีอินฟราเรดที่แตกต่างกัน ดังนั้นการเคลื่อนย้ายวัตถุเทียบกับพื้นหลังอาจกระตุ้นเครื่องตรวจจับได้เช่นกัน +#iiii เซ็นเซอร์ PIR สามารถตรวจจับการเปลี่ยนแปลงของปริมาณรังสีอินฟราเรดที่กระทบกับวัตถุ +ซึ่งจะแตกต่างกันไปขึ้นอยู่กับอุณหภูมิและลักษณะพื้นผิวของวัตถุที่อยู่ด้านหน้าเซ็นเซอร์เมื่อวัตถุ เช่น บุคคล +ผ่านด้านหน้าพื้นหลัง เช่น กำแพง อุณหภูมิ ณ +จุดนั้นในมุมมองของเซ็นเซอร์จะเพิ่มขึ้นจากอุณหภูมิห้องเป็นอุณหภูมิร่างกายแล้วกลับมาอีกครั้ง +เซ็นเซอร์จะแปลงการเปลี่ยนแปลงที่เกิดขึ้นของรังสีอินฟราเรดที่เข้ามาเป็นการเปลี่ยนแปลงของแรงดันไฟฟ้าขาออก +และสิ่งนี้จะกระตุ้นการตรวจจับ +วัตถุที่มีอุณหภูมิใกล้เคียงกันแต่มีลักษณะพื้นผิวต่างกันอาจมีรูปแบบการปล่อยรังสีอินฟราเรดที่แตกต่างกัน +ดังนั้นการเคลื่อนย้ายวัตถุเทียบกับพื้นหลังอาจกระตุ้นเครื่องตรวจจับได้เช่นกัน -#i PIR มีหลายรูปแบบการใช้งานที่หลากหลาย รุ่นที่นิยมใช้กันมากที่สุดมีเลนส์เฟรสเนลหรือส่วนกระจกจำนวนมาก ระยะการทำงานประมาณ 10 เมตร (30 ฟุต) และมุมมองภาพน้อยกว่า 180° มีรุ่นที่มีมุมมองภาพกว้างกว่า รวมถึง 360° ซึ่งโดยทั่วไปออกแบบมาเพื่อติดตั้งบนเพดาน PIR ขนาดใหญ่บางรุ่นผลิตด้วยกระจกส่วนเดียวและสามารถตรวจจับการเปลี่ยนแปลงของพลังงานอินฟราเรดได้ในระยะ 30 เมตร (100 ฟุต) จาก PIR นอกจากนี้ยังมี PIR ที่ออกแบบด้วยกระจกแบบปรับทิศทางได้ ซึ่งสามารถครอบคลุมพื้นที่ได้กว้าง (110°) หรือครอบคลุมพื้นที่แคบมากแบบ "ม่าน" หรือสามารถเลือกส่วนกระจกแยกแต่ละส่วนเพื่อ "ปรับแต่ง" พื้นที่ครอบคลุมได้ +#i PIR มีหลายรูปแบบการใช้งานที่หลากหลาย +รุ่นที่นิยมใช้กันมากที่สุดมีเลนส์เฟรสเนลหรือส่วนกระจกจำนวนมาก ระยะการทำงานประมาณ 10 เมตร (30 +ฟุต) และมุมมองภาพน้อยกว่า 180° มีรุ่นที่มีมุมมองภาพกว้างกว่า รวมถึง 360° +ซึ่งโดยทั่วไปออกแบบมาเพื่อติดตั้งบนเพดาน PIR +ขนาดใหญ่บางรุ่นผลิตด้วยกระจกส่วนเดียวและสามารถตรวจจับการเปลี่ยนแปลงของพลังงานอินฟราเรดได้ในระยะ +30 เมตร (100 ฟุต) จาก PIR นอกจากนี้ยังมี PIR ที่ออกแบบด้วยกระจกแบบปรับทิศทางได้ +ซึ่งสามารถครอบคลุมพื้นที่ได้กว้าง (110°) หรือครอบคลุมพื้นที่แคบมากแบบ "ม่าน" +หรือสามารถเลือกส่วนกระจกแยกแต่ละส่วนเพื่อ "ปรับแต่ง" พื้นที่ครอบคลุมได้ == การตรวจจับความแตกต่าง -#i เซ็นเซอร์หลายตัวอาจเชื่อมต่อเป็นอินพุตตรงข้ามกับเครื่องขยายสัญญาณดิฟเฟอเรนเชียล ในรูปแบบนี้ การวัดค่า PIR จะหักล้างกันเอง ทำให้อุณหภูมิเฉลี่ยของระยะการมองเห็นถูกตัดออกจากสัญญาณไฟฟ้า การเพิ่มขึ้นของพลังงานอินฟราเรดทั่วทั้งเซ็นเซอร์จะหักล้างตัวเองและจะไม่กระตุ้นอุปกรณ์ วิธีนี้ช่วยให้อุปกรณ์ต้านทานการเปลี่ยนแปลงที่ผิดพลาดในกรณีที่ได้รับแสงแฟลชสั้นๆ หรือแสงที่ส่องสว่างทั่วทั้งสนาม (การได้รับพลังงานสูงอย่างต่อเนื่องอาจทำให้วัสดุเซ็นเซอร์อิ่มตัวและทำให้เซ็นเซอร์ไม่สามารถบันทึกข้อมูลเพิ่มเติมได้) ในขณะเดียวกัน การจัดเรียงแบบดิฟเฟอเรนเชียลนี้ยังช่วยลดสัญญาณรบกวนโหมดทั่วไปทำให้อุปกรณ์ต้านทานการกระตุ้นเนื่องจากสนามไฟฟ้าใกล้เคียง อย่างไรก็ตาม เซ็นเซอร์แบบดิฟเฟอเรนเชียลคู่ไม่สามารถวัดอุณหภูมิได้ในรูปแบบนี้ ดังนั้นจึงมีประโยชน์เฉพาะสำหรับการตรวจจับการเคลื่อนไหวเท่านั้น +#iiii เซ็นเซอร์หลายตัวอาจเชื่อมต่อเป็นอินพุตตรงข้ามกับเครื่องขยายสัญญาณดิฟเฟอเรนเชียล ในรูปแบบนี้ +การวัดค่า PIR จะหักล้างกันเอง ทำให้อุณหภูมิเฉลี่ยของระยะการมองเห็นถูกตัดออกจากสัญญาณไฟฟ้า +การเพิ่มขึ้นของพลังงานอินฟราเรดทั่วทั้งเซ็นเซอร์จะหักล้างตัวเองและจะไม่กระตุ้นอุปกรณ์ +วิธีนี้ช่วยให้อุปกรณ์ต้านทานการเปลี่ยนแปลงที่ผิดพลาดในกรณีที่ได้รับแสงแฟลชสั้นๆ +หรือแสงที่ส่องสว่างทั่วทั้งสนาม +(การได้รับพลังงานสูงอย่างต่อเนื่องอาจทำให้วัสดุเซ็นเซอร์อิ่มตัวและทำให้เซ็นเซอร์ไม่สามารถบันทึกข้อมูลเพิ่มเติมได้) +ในขณะเดียวกัน +การจัดเรียงแบบดิฟเฟอเรนเชียลนี้ยังช่วยลดสัญญาณรบกวนโหมดทั่วไปทำให้อุปกรณ์ต้านทานการกระตุ้นเนื่องจากสนามไฟฟ้าใกล้เคียง +อย่างไรก็ตาม เซ็นเซอร์แบบดิฟเฟอเรนเชียลคู่ไม่สามารถวัดอุณหภูมิได้ในรูปแบบนี้ +ดังนั้นจึงมีประโยชน์เฉพาะสำหรับการตรวจจับการเคลื่อนไหวเท่านั้น == การปฏิบัติจริง -#i เมื่อเซ็นเซอร์ PIR ถูกกำหนดค่าในโหมดดิฟเฟอเรนเชียล เซ็นเซอร์จะสามารถใช้งานได้เฉพาะในฐานะอุปกรณ์ตรวจจับการเคลื่อนไหว ในโหมดนี้ เมื่อตรวจจับการเคลื่อนไหวภายใน "แนวสายตา" ของเซ็นเซอร์ พัลส์เสริมคู่หนึ่งจะถูกประมวลผลที่ขาเอาต์พุตของเซ็นเซอร์ เพื่อนำสัญญาณเอาต์พุตนี้ไปใช้งานจริงในการกระตุ้นโหลด เช่น รีเลย์หรือเครื่องบันทึกข้อมูลหรือสัญญาณเตือนสัญญาณดิฟเฟอเรน-เชียลจะถูกแก้ไขโดยใช้วงจรเรียงกระแสแบบบริดจ์และป้อนเข้าสู่วงจรขับรีเลย์แบบทรานซิสเตอร์ หน้าสัมผัสของรีเลย์นี้จะปิดและเปิดเพื่อตอบสนองต่อสัญญาณจาก PIR โดยกระตุ้นโหลดที่เชื่อมต่ออยู่ผ่านหน้าสัมผัสของมัน รับรู้ถึงการตรวจจับบุคคลภายในพื้นที่จำกัดที่กำหนดไว้ล่วงหน้า +#iiii เมื่อเซ็นเซอร์ PIR ถูกกำหนดค่าในโหมดดิฟเฟอเรนเชียล +เซ็นเซอร์จะสามารถใช้งานได้เฉพาะในฐานะอุปกรณ์ตรวจจับการเคลื่อนไหว ในโหมดนี้ +เมื่อตรวจจับการเคลื่อนไหวภายใน "แนวสายตา" ของเซ็นเซอร์ +พัลส์เสริมคู่หนึ่งจะถูกประมวลผลที่ขาเอาต์พุตของเซ็นเซอร์ +เพื่อนำสัญญาณเอาต์พุตนี้ไปใช้งานจริงในการกระตุ้นโหลด เช่น +รีเลย์หรือเครื่องบันทึกข้อมูลหรือสัญญาณเตือนสัญญาณดิฟเฟอเรน-เชียลจะถูกแก้ไขโดยใช้วงจรเรียงกระแสแบบบริดจ์และป้อนเข้าสู่วงจรขับรีเลย์แบบทรานซิสเตอร์ +หน้าสัมผัสของรีเลย์นี้จะปิดและเปิดเพื่อตอบสนองต่อสัญญาณจาก PIR +โดยกระตุ้นโหลดที่เชื่อมต่ออยู่ผ่านหน้าสัมผัสของมัน +รับรู้ถึงการตรวจจับบุคคลภายในพื้นที่จำกัดที่กำหนดไว้ล่วงหน้า == การออกแบบผลิตภัณฑ์ #afigure( image("PIR/PIR_Motion_Sensor-Sensinova_(SN-PR11).png", height: image-height), - attr: [Versatile Techno - http://www.sensinova.in/pir-motion-sensor/SNPR11.php, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=48377787], + attr: [Versatile Techno - + http://www.sensinova.in/pir-motion-sensor/SNPR11.php, CC BY-SA 4.0, + https://commons.wikimedia.org/w/index.php?curid=48377787], alt: "ผลิตภัณฑ์เซนเซอร์สีขาว มีเครื่องหมายแบรนด์ Sensinova", caption: [การออกแบบเซ็นเซอร์ตรวจจับการเคลื่อนไหว PIR], ) -#i โดยทั่วไปเซ็นเซอร์ PIR จะติดตั้งอยู่บนแผงวงจรพิมพ์ซึ่งมีอุปกรณ์อิเล็กทรอนิกส์ที่จำเป็นสำหรับการตีความสัญญาณจากตัวเซ็นเซอร์เอง โดยทั่วไปแล้วชุดประกอบทั้งหมดจะบรรจุอยู่ภายในตัวเรือน ซึ่งติดตั้งในตำแหน่งที่เซ็นเซอร์สามารถครอบคลุมพื้นที่ที่ต้องการตรวจสอบได้ ตัวเรือนมักจะมี "หน้าต่าง" พลาสติกที่พลังงานอินฟราเรดสามารถผ่านเข้ามาได้ แม้ว่ามักจะโปร่งแสงต่อแสงที่มองเห็น แต่พลังงานอินฟราเรดสามารถผ่านเข้ามายังเซ็นเซอร์ได้ผ่านหน้าต่าง เนื่องจากพลาสติกที่ใช้นั้นโปร่งใสต่อรังสีอินฟราเรด หน้าต่างพลาสติกช่วยลดโอกาสที่วัตถุแปลกปลอม (ฝุ่น แมลง ฝน ฯลฯ) จะบดบังมุมมองของเซ็นเซอร์ ทำให้กลไกเสียหาย และอาจทำให้เกิดสัญญาณเตือนที่ผิดพลาด หน้าต่างนี้สามารถใช้เป็นตัวกรองเพื่อจำกัดความยาวคลื่นให้อยู่ที่ 8-14 ไมโครเมตร ซึ่งใกล้เคียงกับรังสีอินฟราเรดที่มนุษย์ปล่อยออกมามากที่สุด นอกจากนี้ยังสามารถใช้เป็นกลไกโฟกัสได้อีกด้วย (ดูด้านล่าง) +#i โดยทั่วไปเซ็นเซอร์ PIR +จะติดตั้งอยู่บนแผงวงจรพิมพ์ซึ่งมีอุปกรณ์อิเล็กทรอนิกส์ที่จำเป็นสำหรับการตีความสัญญาณจากตัวเซ็นเซอร์เอง +โดยทั่วไปแล้วชุดประกอบทั้งหมดจะบรรจุอยู่ภายในตัวเรือน +ซึ่งติดตั้งในตำแหน่งที่เซ็นเซอร์สามารถครอบคลุมพื้นที่ที่ต้องการตรวจสอบได้ ตัวเรือนมักจะมี "หน้าต่าง" +พลาสติกที่พลังงานอินฟราเรดสามารถผ่านเข้ามาได้ แม้ว่ามักจะโปร่งแสงต่อแสงที่มองเห็น +แต่พลังงานอินฟราเรดสามารถผ่านเข้ามายังเซ็นเซอร์ได้ผ่านหน้าต่าง +เนื่องจากพลาสติกที่ใช้นั้นโปร่งใสต่อรังสีอินฟราเรด หน้าต่างพลาสติกช่วยลดโอกาสที่วัตถุแปลกปลอม (ฝุ่น +แมลง ฝน ฯลฯ) จะบดบังมุมมองของเซ็นเซอร์ ทำให้กลไกเสียหาย และอาจทำให้เกิดสัญญาณเตือนที่ผิดพลาด +หน้าต่างนี้สามารถใช้เป็นตัวกรองเพื่อจำกัดความยาวคลื่นให้อยู่ที่ 8-14 ไมโครเมตร +ซึ่งใกล้เคียงกับรังสีอินฟราเรดที่มนุษย์ปล่อยออกมามากที่สุด นอกจากนี้ยังสามารถใช้เป็นกลไกโฟกัสได้อีกด้วย +(ดูด้านล่าง) == การโฟกัส -#i สามารถใช้กลไกที่แตกต่างกันเพื่อโฟกัสพลังงานอินฟราเรดระยะไกลลงบนพื้นผิวเซ็นเซอร์ได้ +#iiii สามารถใช้กลไกที่แตกต่างกันเพื่อโฟกัสพลังงานอินฟราเรดระยะไกลลงบนพื้นผิวเซ็นเซอร์ได้ == เลนส์ -#i ม่านพลาสติกอาจหล่อขึ้นรูปหลายเหลี่ยมเพื่อรวมพลังงานอินฟราเรดไปยังเซ็นเซอร์ แต่ละเหลี่ยมคือเลนส์เฟรสเนล +#iiii ม่านพลาสติกอาจหล่อขึ้นรูปหลายเหลี่ยมเพื่อรวมพลังงานอินฟราเรดไปยังเซ็นเซอร์ +แต่ละเหลี่ยมคือเลนส์เฟรสเนล === เลนส์มัลติเฟรสเนลของ PIR @@ -85,63 +151,72 @@ image("PIR/FacetLensOfMotionDetector_animation2.gif", height: 2in), attr: [CC BY-SA 3.0, https://en.wikipedia.org/w/index.php?curid=14193664], alt: "เครื่องตรวจจับความเคลื่อนไหวทรงกระบอก", - caption: [ตัวเรือนเครื่องตรวจจับความเคลื่อนไหว PIR พร้อมช่องหน้าต่างทรงกระบอกเหลี่ยมโดยแต่ละเหลี่ยมเป็นเลนส์เฟรสเนล โฟกัสแสงไปที่ชิ้นส่วนเซ็นเซอร์ไพโรอิเล็กทริกที่อยู่ด้านล่าง], + caption: [ตัวเรือนเครื่องตรวจจับความเคลื่อนไหว PIR + พร้อมช่องหน้าต่างทรงกระบอกเหลี่ยมโดยแต่ละเหลี่ยมเป็นเลนส์เฟรสเนล + โฟกัสแสงไปที่ชิ้นส่วนเซ็นเซอร์ไพโรอิเล็กทริกที่อยู่ด้านล่าง], ) #afigure( image("PIR/Fresnel_only.jpg", height: 2in), - attr: [Jack LaRosa, Public Domain, https://commons.wikimedia.org/w/index.php?curid=4463018], + attr: [Jack LaRosa, Public Domain, + https://commons.wikimedia.org/w/index.php?curid=4463018], alt: "ฝาครอบของเซนเซอร์ตรวจจับความเคลื่อนไหว", - caption: [ฝาครอบด้านหน้า PIR เท่านั้น (ถอดอุปกรณ์อิเล็กทรอนิกส์ออก) โดยมีแหล่งกำเนิดแสงจุดอยู่ด้านหลัง เพื่อแสดงเลนส์แต่ละตัว], + caption: [ฝาครอบด้านหน้า PIR เท่านั้น (ถอดอุปกรณ์อิเล็กทรอนิกส์ออก) + โดยมีแหล่งกำเนิดแสงจุดอยู่ด้านหลัง เพื่อแสดงเลนส์แต่ละตัว], ) #afigure( image("PIR/Circuit_board_revealed.jpg", height: 2in), - attr: [Jack LaRosa, Public Domain, https://commons.wikimedia.org/w/index.php?curid=4478366], + attr: [Jack LaRosa, Public Domain, + https://commons.wikimedia.org/w/index.php?curid=4478366], alt: "แผงวงจร PIR ลูกศรสีเขียวชี้ไปยังเซนเซอร์ภายในบอร์ด", - caption: [PIR ที่ถอดฝาครอบด้านหน้าออก แสดงตำแหน่งของ - เซ็นเซอร์ไพโรอิเล็กทริก (ลูกศรสีเขียว)], + caption: [PIR ที่ถอดฝาครอบด้านหน้าออก แสดงตำแหน่งของ เซ็นเซอร์ไพโรอิเล็กทริก + (ลูกศรสีเขียว)], ) == กระจก PIR -#i บางรุ่นผลิตขึ้นโดยใช้กระจกพาราโบลา แบบแบ่งส่วนภายใน เพื่อรวมพลังงานอินฟราเรด ในกรณีที่ใช้กระจก ฝาครอบกระจกพลาสติกโดยทั่วไปจะไม่มีเลนส์เฟรสเนลหล่อขึ้นรูป +#iiii บางรุ่นผลิตขึ้นโดยใช้กระจกพาราโบลา แบบแบ่งส่วนภายใน เพื่อรวมพลังงานอินฟราเรด +ในกรณีที่ใช้กระจก ฝาครอบกระจกพลาสติกโดยทั่วไปจะไม่มีเลนส์เฟรสเนลหล่อขึ้นรูป === PIR ชนิดกระจกแบ่งส่วน #afigure( image("PIR/Front-(mirror_type).jpg", height: 2in), - attr: [Jack LaRosa, Public Domain, https://commons.wikimedia.org/w/index.php?curid=4501665], + attr: [Jack LaRosa, Public Domain, + https://commons.wikimedia.org/w/index.php?curid=4501665], alt: "เซนเซอร์ทรงคล้ายทรงกลม", - caption: [PID ทั่วไปสำหรับที่พักอาศัย/เชิงพาณิชย์ที่ - ใช้กระจกแบ่งส่วนภายในเพื่อการโฟกัส], + caption: [PID ทั่วไปสำหรับที่พักอาศัย/เชิงพาณิชย์ที่ ใช้กระจกแบ่งส่วนภายในเพื่อการโฟกัส], ) #afigure( image("PIR/Mirror_type_opened.jpg", height: 2in), - attr: [Deuxdad, Public Domain, https://commons.wikimedia.org/w/index.php?curid=4501724], + attr: [Deuxdad, Public Domain, + https://commons.wikimedia.org/w/index.php?curid=4501724], alt: "เซนเซอร์ก่อนหน้าเมื่อถูกถอดฝาครอบออก", - caption: [ถอดฝาครอบออกแล้ว กระจกแบ่งส่วน - ด้านล่างมีแผงวงจรพิมพ์ (PC) อยู่ด้านบน], + caption: [ถอดฝาครอบออกแล้ว กระจกแบ่งส่วน ด้านล่างมีแผงวงจรพิมพ์ (PC) อยู่ด้านบน], ) #afigure( image("PIR/Mirror_in_place.jpg", height: 2in), - attr: [Jack LaRosa, Public Domain, https://commons.wikimedia.org/w/index.php?curid=4502198], + attr: [Jack LaRosa, Public Domain, + https://commons.wikimedia.org/w/index.php?curid=4502198], alt: "เซนเซอร์ก่อนหน้าเมื่อถอดแผงวงจรให้เห็นกระจกแบ่งส่วนด้านใน", caption: [แผงวงจรพิมพ์ถูกถอดออกเพื่อแสดงกระจกแบบแบ่งส่วน], ) #afigure( image("PIR/Segmented-parabolic_mirror.jpg", height: 2in), - attr: [Deuxdad, Public Domain, https://commons.wikimedia.org/w/index.php?curid=4502224], + attr: [Deuxdad, Public Domain, + https://commons.wikimedia.org/w/index.php?curid=4502224], alt: "กระจกแบ่งส่วนที่ถูกถอดออก", caption: [กระจกพาราโบลาแบบแบ่งส่วนถอดออกจากตัวเครื่อง], ) #afigure( image("PIR/Rear_of_circuit_board2.jpg", height: 2in), - attr: [Jack LaRosa, Public Domain, https://commons.wikimedia.org/w/index.php?curid=4508036], + attr: [Jack LaRosa, Public Domain, + https://commons.wikimedia.org/w/index.php?curid=4508036], alt: "ด้านหลังของแผงวงจรก่อนหน้า ลูกศรสีเขียวชี้ไปยังเซนเซอร์", caption: [ด้านหลังของแผงวงจรที่หันเข้าหากระจกเมื่อติดตั้ง เซ็นเซอร์ไพโรอิเล็กทริกแสดงด้วยลูกศรสีเขียว], @@ -151,37 +226,78 @@ #afigure( image("PIR/Motion_Detector_with_Beam_Pattern.jpg", height: 2in), - attr: [AndreasCT, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=84066723], + attr: [AndreasCT, CC BY-SA 4.0, + https://commons.wikimedia.org/w/index.php?curid=84066723], alt: "กราฟิกแสดงลำแสงจำลองการทำงานของเครื่องตรวจจับความเคลื่อนไหว", - caption: [เครื่องตรวจจับความเคลื่อนไหวที่มีรูปแบบลำแสงซ้อนทับ ความยาวของลำแสงเป็นตัวชี้วัดความไวของเครื่องตรวจจับในทิศทางนั้น], + caption: [เครื่องตรวจจับความเคลื่อนไหวที่มีรูปแบบลำแสงซ้อนทับ + ความยาวของลำแสงเป็นตัวชี้วัดความไวของเครื่องตรวจจับในทิศทางนั้น], ) -#i จากการโฟกัส ทำให้มุมมองของเครื่องตรวจจับกลายเป็นรูปแบบลำแสง ภายใต้มุมบางมุม (โซน)\ เซ็นเซอร์ PIR แทบจะไม่ได้รับพลังงานรังสีใด ๆ และภายใต้มุมอื่น ๆ PIR จะได้รับพลังงานอินฟราเรดในปริมาณที่เข้มข้น การแยกนี้ช่วยให้เครื่องตรวจจับความเคลื่อนไหวสามารถแยกแยะระหว่างแสงสว่างที่กว้างและวัตถุที่กำลังเคลื่อนที่ได้ +#i จากการโฟกัส ทำให้มุมมองของเครื่องตรวจจับกลายเป็นรูปแบบลำแสง ภายใต้มุมบางมุม (โซน)\ +เซ็นเซอร์ PIR แทบจะไม่ได้รับพลังงานรังสีใด ๆ และภายใต้มุมอื่น ๆ PIR +จะได้รับพลังงานอินฟราเรดในปริมาณที่เข้มข้น +การแยกนี้ช่วยให้เครื่องตรวจจับความเคลื่อนไหวสามารถแยกแยะระหว่างแสงสว่างที่กว้างและวัตถุที่กำลังเคลื่อนที่ได้ -#i เมื่อบุคคลเดินจากมุมหนึ่ง (ลำแสง) ไปยังอีกมุมหนึ่ง เครื่องตรวจจับจะมองเห็นบุคคลที่กำลังเคลื่อนไหวเป็นระยะ ๆ เท่านั้น ส่งผลให้สัญญาณเซ็นเซอร์เปลี่ยนแปลงอย่างรวดเร็ว ซึ่งระบบอิเล็กทรอนิกส์จะใช้เพื่อส่งสัญญาณเตือนภัยหรือเปิดไฟ ระบบอิเล็กทรอนิกส์จะไม่สนใจสัญญาณที่เปลี่ยนแปลงช้า ๆ +#i เมื่อบุคคลเดินจากมุมหนึ่ง (ลำแสง) ไปยังอีกมุมหนึ่ง +เครื่องตรวจจับจะมองเห็นบุคคลที่กำลังเคลื่อนไหวเป็นระยะ ๆ เท่านั้น +ส่งผลให้สัญญาณเซ็นเซอร์เปลี่ยนแปลงอย่างรวดเร็ว +ซึ่งระบบอิเล็กทรอนิกส์จะใช้เพื่อส่งสัญญาณเตือนภัยหรือเปิดไฟ +ระบบอิเล็กทรอนิกส์จะไม่สนใจสัญญาณที่เปลี่ยนแปลงช้า ๆ -#i จำนวน รูปร่าง การกระจาย และความไวของโซนเหล่านี้ถูกกำหนดโดยเลนส์และกระจก ผู้ผลิตพยายามอย่างเต็มที่เพื่อสร้างรูปแบบลำแสงความไวที่เหมาะสมที่สุดสำหรับการใช้งานแต่ละประเภท +#i จำนวน รูปร่าง การกระจาย และความไวของโซนเหล่านี้ถูกกำหนดโดยเลนส์และกระจก +ผู้ผลิตพยายามอย่างเต็มที่เพื่อสร้างรูปแบบลำแสงความไวที่เหมาะสมที่สุดสำหรับการใช้งานแต่ละประเภท == การใช้งานระบบไฟอัตโนมัติ -#i เมื่อใช้เป็นส่วนหนึ่งของระบบไฟส่องสว่าง ระบบอิเล็กทรอนิกส์ใน PIR มักจะควบคุมรีเลย์ในตัวที่สามารถสลับแรงดันไฟฟ้าหลักได้ ซึ่งหมายความว่า PIR สามารถตั้งค่าให้เปิดไฟที่เชื่อมต่อกับ PIR เมื่อตรวจพบการเคลื่อนไหวได้ วิธีนี้มักใช้ในสถานการณ์กลางแจ้ง ทั้งเพื่อป้องกันอาชญากร (ไฟรักษาความปลอดภัย) หรือเพื่อการใช้งานจริง เช่น การเปิดไฟประตูหน้าบ้านเพื่อให้คุณหากุญแจเจอในความมืด\ การใช้งานเพิ่มเติมสามารถทำได้ในห้องน้ำสาธารณะ ห้องเตรียมอาหารแบบวอล์กอิน ทางเดิน หรือบริเวณใดก็ตามที่สามารถควบคุมไฟอัตโนมัติได้ วิธีนี้ช่วยประหยัดพลังงานได้ เพราะไฟจะเปิดเฉพาะเมื่อจำเป็นเท่านั้น และผู้ใช้ไม่จำเป็นต้องปิดไฟเมื่อออกจากพื้นที่ +#h(9.75em) เมื่อใช้เป็นส่วนหนึ่งของระบบไฟส่องสว่าง ระบบอิเล็กทรอนิกส์ใน PIR +มักจะควบคุมรีเลย์ในตัวที่สามารถสลับแรงดันไฟฟ้าหลักได้ ซึ่งหมายความว่า PIR +สามารถตั้งค่าให้เปิดไฟที่เชื่อมต่อกับ PIR เมื่อตรวจพบการเคลื่อนไหวได้ วิธีนี้มักใช้ในสถานการณ์กลางแจ้ง +ทั้งเพื่อป้องกันอาชญากร (ไฟรักษาความปลอดภัย) หรือเพื่อการใช้งานจริง เช่น +การเปิดไฟประตูหน้าบ้านเพื่อให้คุณหากุญแจเจอในความมืด\ +การใช้งานเพิ่มเติมสามารถทำได้ในห้องน้ำสาธารณะ ห้องเตรียมอาหารแบบวอล์กอิน ทางเดิน +หรือบริเวณใดก็ตามที่สามารถควบคุมไฟอัตโนมัติได้ วิธีนี้ช่วยประหยัดพลังงานได้ +เพราะไฟจะเปิดเฉพาะเมื่อจำเป็นเท่านั้น และผู้ใช้ไม่จำเป็นต้องปิดไฟเมื่อออกจากพื้นที่ == แอปพลิเคชั่นด้านความปลอดภัย -#i เมื่อใช้เป็นส่วนหนึ่งของระบบรักษาความปลอดภัย วงจรอิเล็กทรอนิกส์ใน PIR มักจะควบคุมรีเลย์ ขนาดเล็ก รีเลย์นี้จะทำหน้าที่เชื่อมต่อวงจรไฟฟ้าผ่านหน้า สัมผัสไฟฟ้าคู่หนึ่งที่เชื่อมต่อกับโซนอินพุตตรวจจับของแผงควบคุมสัญญาณกันขโมยโดยทั่วไประบบจะออกแบบให้หากไม่มีการเคลื่อนไหว หน้าสัมผัสรีเลย์จะปิดอยู่ ซึ่งเรียกว่ารีเลย์แบบ 'ปกติปิด' (NC) หากตรวจพบการเคลื่อนไหว รีเลย์จะเปิดวงจรเพื่อส่งสัญญาณเตือนภัย หรือหากสายไฟถูกตัดการเชื่อมต่อ สัญญาณเตือนภัยก็จะทำงานเช่นกัน +#h(9.75em) เมื่อใช้เป็นส่วนหนึ่งของระบบรักษาความปลอดภัย วงจรอิเล็กทรอนิกส์ใน PIR +มักจะควบคุมรีเลย์ ขนาดเล็ก รีเลย์นี้จะทำหน้าที่เชื่อมต่อวงจรไฟฟ้าผ่านหน้า +สัมผัสไฟฟ้าคู่หนึ่งที่เชื่อมต่อกับโซนอินพุตตรวจจับของแผงควบคุมสัญญาณกันขโมยโดยทั่วไประบบจะออกแบบให้หากไม่มีการเคลื่อนไหว +หน้าสัมผัสรีเลย์จะปิดอยู่ ซึ่งเรียกว่ารีเลย์แบบ 'ปกติปิด' (NC) หากตรวจพบการเคลื่อนไหว +รีเลย์จะเปิดวงจรเพื่อส่งสัญญาณเตือนภัย หรือหากสายไฟถูกตัดการเชื่อมต่อ สัญญาณเตือนภัยก็จะทำงานเช่นกัน == การจัดวาง -#i ผู้ผลิตแนะนำให้วางผลิตภัณฑ์อย่างระมัดระวังเพื่อป้องกันการแจ้งเตือนที่ผิดพลาด (เช่น การตรวจจับใดๆ ที่ไม่ได้เกิดจากผู้บุกรุก) +#h(9.75em) ผู้ผลิตแนะนำให้วางผลิตภัณฑ์อย่างระมัดระวังเพื่อป้องกันการแจ้งเตือนที่ผิดพลาด (เช่น +การตรวจจับใดๆ ที่ไม่ได้เกิดจากผู้บุกรุก) -#i พวกเขาแนะนำให้ติดตั้ง PIR ในลักษณะที่ PIR ไม่สามารถ "มองเห็น" ออกจากหน้าต่างได้ แม้ว่าความยาวคลื่นของรังสีอินฟราเรดที่ชิปมีความไวต่อแสงจะทะลุผ่านกระจกได้ไม่ดีนัก แต่แหล่งกำเนิดแสงอินฟราเรดที่แรง (เช่น จากไฟหน้ารถยนต์หรือแสงแดด) อาจทำให้เซ็นเซอร์รับภาพเกินพิกัดและทำให้เกิดสัญญาณเตือนภัยผิดพลาดได้ บุคคลที่เคลื่อนไหวอยู่อีกฝั่งของกระจกจะไม่ถูก PID "มองเห็น" ซึ่งอาจเป็นผลดีสำหรับหน้าต่างที่หันหน้าไปทางทางเท้าสาธารณะ หรือเป็นผลเสียสำหรับหน้าต่างในฉากกั้นภายใน +#i พวกเขาแนะนำให้ติดตั้ง PIR ในลักษณะที่ PIR ไม่สามารถ "มองเห็น" ออกจากหน้าต่างได้ +แม้ว่าความยาวคลื่นของรังสีอินฟราเรดที่ชิปมีความไวต่อแสงจะทะลุผ่านกระจกได้ไม่ดีนัก +แต่แหล่งกำเนิดแสงอินฟราเรดที่แรง (เช่น จากไฟหน้ารถยนต์หรือแสงแดด) +อาจทำให้เซ็นเซอร์รับภาพเกินพิกัดและทำให้เกิดสัญญาณเตือนภัยผิดพลาดได้ +บุคคลที่เคลื่อนไหวอยู่อีกฝั่งของกระจกจะไม่ถูก PID "มองเห็น" +ซึ่งอาจเป็นผลดีสำหรับหน้าต่างที่หันหน้าไปทางทางเท้าสาธารณะ +หรือเป็นผลเสียสำหรับหน้าต่างในฉากกั้นภายใน -#i ขอแนะนำว่าไม่ควรติดตั้ง PIR ในตำแหน่งที่ ช่องระบายอากาศ HVAC จะเป่าลมร้อนหรือเย็นลงบนพื้นผิวพลาสติกที่ปิดหน้าต่างของตัวบ้าน แม้ว่าอากาศจะมีค่าการแผ่รังสี ต่ำมาก (ปล่อยพลังงานอินฟราเรดในปริมาณน้อยมาก) แต่ลมที่พัดผ่านฝาครอบหน้าต่างพลาสติกอาจทำให้อุณหภูมิของพลาสติกเปลี่ยนแปลงจนทำให้เกิดสัญญาณเตือนที่ผิดพลาดได้ +#i ขอแนะนำว่าไม่ควรติดตั้ง PIR ในตำแหน่งที่ ช่องระบายอากาศ HVAC +จะเป่าลมร้อนหรือเย็นลงบนพื้นผิวพลาสติกที่ปิดหน้าต่างของตัวบ้าน แม้ว่าอากาศจะมีค่าการแผ่รังสี ต่ำมาก +(ปล่อยพลังงานอินฟราเรดในปริมาณน้อยมาก) +แต่ลมที่พัดผ่านฝาครอบหน้าต่างพลาสติกอาจทำให้อุณหภูมิของพลาสติกเปลี่ยนแปลงจนทำให้เกิดสัญญาณเตือนที่ผิดพลาดได้ -#i เซ็นเซอร์มักได้รับการออกแบบมาให้ "เพิกเฉย" สัตว์เลี้ยงในบ้าน เช่น สุนัขหรือแมว โดยการตั้งค่าความไวให้สูงขึ้น หรือทำให้แน่ใจว่าพื้นห้องจะไม่อยู่ในโฟกัส +#i เซ็นเซอร์มักได้รับการออกแบบมาให้ "เพิกเฉย" สัตว์เลี้ยงในบ้าน เช่น สุนัขหรือแมว +โดยการตั้งค่าความไวให้สูงขึ้น หรือทำให้แน่ใจว่าพื้นห้องจะไม่อยู่ในโฟกัส -#i เนื่องจากเซ็นเซอร์ PIR มีระยะการทำงานสูงสุด 10 เมตร (30 ฟุต) ดังนั้นการติดตั้งเครื่องตรวจจับเพียงตัวเดียวใกล้ทางเข้าจึงเพียงพอสำหรับห้องที่มีทางเข้าเพียงทางเดียว ระบบรักษาความปลอดภัยที่ใช้ PIR ยังใช้งานได้ดีกับระบบรักษาความปลอดภัยภายนอกอาคารและระบบไฟที่ไวต่อการเคลื่อนไหว ข้อดีอย่างหนึ่งคือใช้พลังงานต่ำ ซึ่งทำให้สามารถใช้พลังงานแสงอาทิตย์ได้ +#i เนื่องจากเซ็นเซอร์ PIR มีระยะการทำงานสูงสุด 10 เมตร (30 ฟุต) +ดังนั้นการติดตั้งเครื่องตรวจจับเพียงตัวเดียวใกล้ทางเข้าจึงเพียงพอสำหรับห้องที่มีทางเข้าเพียงทางเดียว +ระบบรักษาความปลอดภัยที่ใช้ PIR +ยังใช้งานได้ดีกับระบบรักษาความปลอดภัยภายนอกอาคารและระบบไฟที่ไวต่อการเคลื่อนไหว +ข้อดีอย่างหนึ่งคือใช้พลังงานต่ำ ซึ่งทำให้สามารถใช้พลังงานแสงอาทิตย์ได้ == เทอร์โมมิเตอร์แบบควบคุมระยะไกลด้วย PIR -#i มีการออกแบบวงจร PIR ที่ใช้วัดอุณหภูมิของวัตถุที่อยู่ห่างไกลในวงจรดังกล่าว จะใช้เอาต์พุต PIR แบบไม่มีค่าความแตกต่าง สัญญาณเอาต์พุตจะถูกประเมินตามการสอบเทียบสเปกตรัม IR ของสสารชนิดเฉพาะที่ต้องการตรวจวัด ด้วยวิธีนี้ การวัดอุณหภูมิจากระยะไกลจึงค่อนข้างแม่นยำและแม่นยำ หากไม่มีการสอบเทียบกับชนิดของวัสดุที่ตรวจวัด อุปกรณ์เทอร์โมมิเตอร์ PIR จะสามารถวัดการเปลี่ยนแปลงของการแผ่รังสี IR ซึ่งสอดคล้องกับการเปลี่ยนแปลงของอุณหภูมิโดยตรง แต่ไม่สามารถคำนวณค่าอุณหภูมิที่แท้จริงได้ +#h(9.75em) มีการออกแบบวงจร PIR ที่ใช้วัดอุณหภูมิของวัตถุที่อยู่ห่างไกลในวงจรดังกล่าว จะใช้เอาต์พุต +PIR แบบไม่มีค่าความแตกต่าง สัญญาณเอาต์พุตจะถูกประเมินตามการสอบเทียบสเปกตรัม IR +ของสสารชนิดเฉพาะที่ต้องการตรวจวัด ด้วยวิธีนี้ การวัดอุณหภูมิจากระยะไกลจึงค่อนข้างแม่นยำและแม่นยำ +หากไม่มีการสอบเทียบกับชนิดของวัสดุที่ตรวจวัด อุปกรณ์เทอร์โมมิเตอร์ PIR +จะสามารถวัดการเปลี่ยนแปลงของการแผ่รังสี IR ซึ่งสอดคล้องกับการเปลี่ยนแปลงของอุณหภูมิโดยตรง +แต่ไม่สามารถคำนวณค่าอุณหภูมิที่แท้จริงได้ diff --git a/Chapter2/X509.typ b/Chapter2/X509.typ index def512f..a26f4e3 100644 --- a/Chapter2/X509.typ +++ b/Chapter2/X509.typ @@ -2,15 +2,29 @@ == X.509 (รูปแบบใบรับรอง TLS/SSL) -#i ในการเข้ารหัส X.509 เป็นมาตรฐานของสหภาพโทรคมนาคมระหว่างประเทศ (ITU) ที่กำหนดรูปแบบของใบรับรองคีย์สาธารณะใบรับรอง X.509 ถูกใช้ในโปรโตคอลอินเทอร์เน็ตมากมายรวมถึง TLS/SSL ซึ่งเป็นพื้นฐานของ HTTPS โปรโตคอลที่ปลอดภัยสำหรับการท่องเว็บ นอกจากนี้ยังใช้ในแอปพลิเคชันออฟไลน์เช่น ลาย เซ็นอิเล็กทรอนิกส์ -ใบรับรอง X.509 เชื่อมโยงข้อมูลประจำตัวกับคีย์สาธารณะโดยใช้ลายเซ็นดิจิทัล ใบรับรองประกอบด้วยข้อมูลประจำตัว (ชื่อโฮสต์องค์กร หรือบุคคล) และคีย์สาธารณะ (RSA, DSA, ECDSA, ed25519 เป็นต้น) ซึ่งลงนามโดยผู้ออกใบรับรองหรือลงนามด้วยตนเอง เมื่อใบรับรองได้รับการลงนามโดยผู้ออกใบรับรองที่เชื่อถือได้หรือผ่านการตรวจสอบความถูกต้องด้วยวิธีอื่น ผู้ถือใบรับรองนั้นสามารถใช้คีย์สาธารณะที่มีอยู่เพื่อสร้างการสื่อสารที่ปลอดภัยกับบุคคลอื่น หรือตรวจสอบความถูกต้องของเอกสารที่ลงนามดิจิทัลด้วย คีย์ส่วนตัวที่เกี่ยวข้องได้ +#i ในการเข้ารหัส X.509 เป็นมาตรฐานของสหภาพโทรคมนาคมระหว่างประเทศ (ITU) +ที่กำหนดรูปแบบของใบรับรองคีย์สาธารณะใบรับรอง X.509 ถูกใช้ในโปรโตคอลอินเทอร์เน็ตมากมายรวมถึง +TLS/SSL ซึ่งเป็นพื้นฐานของ HTTPS โปรโตคอลที่ปลอดภัยสำหรับการท่องเว็บ +นอกจากนี้ยังใช้ในแอปพลิเคชันออฟไลน์เช่น ลาย เซ็นอิเล็กทรอนิกส์ ใบรับรอง X.509 +เชื่อมโยงข้อมูลประจำตัวกับคีย์สาธารณะโดยใช้ลายเซ็นดิจิทัล ใบรับรองประกอบด้วยข้อมูลประจำตัว +(ชื่อโฮสต์องค์กร หรือบุคคล) และคีย์สาธารณะ (RSA, DSA, ECDSA, ed25519 เป็นต้น) +ซึ่งลงนามโดยผู้ออกใบรับรองหรือลงนามด้วยตนเอง +เมื่อใบรับรองได้รับการลงนามโดยผู้ออกใบรับรองที่เชื่อถือได้หรือผ่านการตรวจสอบความถูกต้องด้วยวิธีอื่น +ผู้ถือใบรับรองนั้นสามารถใช้คีย์สาธารณะที่มีอยู่เพื่อสร้างการสื่อสารที่ปลอดภัยกับบุคคลอื่น +หรือตรวจสอบความถูกต้องของเอกสารที่ลงนามดิจิทัลด้วย คีย์ส่วนตัวที่เกี่ยวข้องได้ -#i X.509 ยังกำหนดรายการเพิกถอนใบรับรองซึ่งเป็นวิธีการแจกจ่ายข้อมูลเกี่ยวกับใบรับรองที่ถือว่าไม่ถูกต้องโดยผู้มีอำนาจลงนาม ตลอดจนอัลกอริทึมการตรวจสอบเส้นทางการรับรองซึ่งช่วยให้ใบรับรองได้รับการลงนามโดยใบรับรอง CA ตัวกลาง ซึ่งใบรับรองเหล่านี้จะได้รับการลงนามโดยใบรับรองอื่นๆ ต่อไปจนไปถึงจุดยึดที่เชื่อถือได้ในที่สุด +#i X.509 +ยังกำหนดรายการเพิกถอนใบรับรองซึ่งเป็นวิธีการแจกจ่ายข้อมูลเกี่ยวกับใบรับรองที่ถือว่าไม่ถูกต้องโดยผู้มีอำนาจลงนาม +ตลอดจนอัลกอริทึมการตรวจสอบเส้นทางการรับรองซึ่งช่วยให้ใบรับรองได้รับการลงนามโดยใบรับรอง CA +ตัวกลาง ซึ่งใบรับรองเหล่านี้จะได้รับการลงนามโดยใบรับรองอื่นๆ ต่อไปจนไปถึงจุดยึดที่เชื่อถือได้ในที่สุด -#i X.509 ถูกกำหนดโดย "Standardization Sector" ของ ITU (SG17 ของ ITU-T) ใน ITU-T Study Group 17 และมีพื้นฐานมาจาก Abstract Syntax Notation One (ASN.1) ซึ่งเป็นมาตรฐานอีกประการหนึ่งของ ITU-T +#i X.509 ถูกกำหนดโดย "Standardization Sector" ของ ITU (SG17 ของ ITU-T) ใน ITU-T +Study Group 17 และมีพื้นฐานมาจาก Abstract Syntax Notation One (ASN.1) +ซึ่งเป็นมาตรฐานอีกประการหนึ่งของ ITU-T === โครงสร้างของใบรับรอง -#i โครงสร้างที่กำหนดไว้โดยมาตรฐานจะแสดงอยู่ในภาษาทางการที่เรียกว่า Abstract Syntax Notation One (ASN.1) +#i โครงสร้างที่กำหนดไว้โดยมาตรฐานจะแสดงอยู่ในภาษาทางการที่เรียกว่า Abstract Syntax +Notation One (ASN.1) โครงสร้างของใบรับรองดิจิทัล X.509 v3 มีดังนี้: @@ -25,39 +39,75 @@ - ชื่อเรื่อง - ข้อมูลคีย์สาธารณะของเรื่อง - อัลกอริทึมคีย์สาธารณะ - - คีย์สาธารณะของเรื่อง - รหัสประจำตัวผู้ออก (ทางเลือก) - รหัสประจำตัวเฉพาะเรื่อง (ทางเลือก) + - คีย์สาธารณะของเรื่อง รหัสประจำตัวผู้ออก (ทางเลือก) รหัสประจำตัวเฉพาะเรื่อง (ทางเลือก) ส่วนขยาย (ทางเลือก) - อัลกอริทึมลายเซ็นใบรับรอง - ลายเซ็นใบรับรอง -#i ฟิลด์ส่วนขยาย (ถ้ามี) จะเป็นลำดับของส่วนขยายใบรับรองอย่างน้อยหนึ่งรายการ แต่ละส่วนขยายมีรหัสประจำตัวเฉพาะของตัวเอง ซึ่งแสดงเป็นตัวระบุวัตถุ (OID) ซึ่งเป็นชุดค่าพร้อมกับข้อบ่งชี้ที่สำคัญหรือไม่สำคัญ ระบบที่ใช้ใบรับรองต้องปฏิเสธใบรับรองหากพบส่วนขยายที่สำคัญที่ไม่รู้จักหรือส่วนขยายที่สำคัญซึ่งมีข้อมูลที่ไม่สามารถประมวลผลได้ ส่วนขยายที่ไม่สำคัญอาจถูกละเว้นหากไม่รู้จัก แต่จะต้องได้รับการประมวลผลหากรู้จักส่วนขยายใบรับรอง +#i ฟิลด์ส่วนขยาย (ถ้ามี) จะเป็นลำดับของส่วนขยายใบรับรองอย่างน้อยหนึ่งรายการ +แต่ละส่วนขยายมีรหัสประจำตัวเฉพาะของตัวเอง ซึ่งแสดงเป็นตัวระบุวัตถุ (OID) +ซึ่งเป็นชุดค่าพร้อมกับข้อบ่งชี้ที่สำคัญหรือไม่สำคัญ +ระบบที่ใช้ใบรับรองต้องปฏิเสธใบรับรองหากพบส่วนขยายที่สำคัญที่ไม่รู้จักหรือส่วนขยายที่สำคัญซึ่งมีข้อมูลที่ไม่สามารถประมวลผลได้ +ส่วนขยายที่ไม่สำคัญอาจถูกละเว้นหากไม่รู้จัก แต่จะต้องได้รับการประมวลผลหากรู้จักส่วนขยายใบรับรอง #i โครงสร้างของเวอร์ชัน 1 มีอยู่ ใน RFC 1422 -#i รูปแบบภายในของตัวระบุเฉพาะของผู้เผยแพร่และเรื่องที่ระบุไว้ใน X.520 ไดเร็กทอรี:คำแนะนำ ประเภทแอตทริบิวต์ที่เลือก +#i รูปแบบภายในของตัวระบุเฉพาะของผู้เผยแพร่และเรื่องที่ระบุไว้ใน X.520 ไดเร็กทอรี:คำแนะนำ +ประเภทแอตทริบิวต์ที่เลือก -#i ITU-T ได้นำตัวระบุเฉพาะของผู้ออกหลักทรัพย์และบุคคลมาใช้ในเวอร์ชัน 2 เพื่ออนุญาตให้นำชื่อผู้ออกหลักทรัพย์หรือบุคคลมาใช้ซ้ำได้หลังจากระยะเวลาหนึ่ง ตัวอย่างหนึ่งของการนำกลับมาใช้ซ้ำคือเมื่อ CA ล้มละลายและชื่อถูกลบออกจากรายชื่อสาธารณะของประเทศ หลังจากนั้น CA อื่นที่มีชื่อเดียวกันอาจลงทะเบียนตัวเองได้ แม้ว่าจะไม่เกี่ยวข้องกับ CA แรกก็ตาม อย่างไรก็ตาม IETF แนะนำว่าไม่ควรนำชื่อผู้ออกหลักทรัพย์และบุคคลมาใช้ซ้ำ ดังนั้นเวอร์ชัน 2 จึงยังไม่แพร่หลายในอินเทอร์เน็ต +#i ITU-T ได้นำตัวระบุเฉพาะของผู้ออกหลักทรัพย์และบุคคลมาใช้ในเวอร์ชัน 2 +เพื่ออนุญาตให้นำชื่อผู้ออกหลักทรัพย์หรือบุคคลมาใช้ซ้ำได้หลังจากระยะเวลาหนึ่ง +ตัวอย่างหนึ่งของการนำกลับมาใช้ซ้ำคือเมื่อ CA ล้มละลายและชื่อถูกลบออกจากรายชื่อสาธารณะของประเทศ +หลังจากนั้น CA อื่นที่มีชื่อเดียวกันอาจลงทะเบียนตัวเองได้ แม้ว่าจะไม่เกี่ยวข้องกับ CA แรกก็ตาม +อย่างไรก็ตาม IETF แนะนำว่าไม่ควรนำชื่อผู้ออกหลักทรัพย์และบุคคลมาใช้ซ้ำ ดังนั้นเวอร์ชัน 2 +จึงยังไม่แพร่หลายในอินเทอร์เน็ต -#i ส่วนขยายได้รับการแนะนำในเวอร์ชัน 3 CA สามารถใช้ส่วนขยายเพื่อออกใบรับรองได้เฉพาะสำหรับจุดประสงค์เฉพาะ (เช่น สำหรับการลงนามในวัตถุดิจิทัล เท่านั้น) +#i ส่วนขยายได้รับการแนะนำในเวอร์ชัน 3 CA +สามารถใช้ส่วนขยายเพื่อออกใบรับรองได้เฉพาะสำหรับจุดประสงค์เฉพาะ (เช่น +สำหรับการลงนามในวัตถุดิจิทัล เท่านั้น) -#i ในทุกเวอร์ชันหมายเลขซีเรียลจะต้องไม่ซ้ำกันสำหรับใบรับรองแต่ละใบที่ออกโดย CA เฉพาะ (ดังที่กล่าวถึงใน RFC 5280) +#i ในทุกเวอร์ชันหมายเลขซีเรียลจะต้องไม่ซ้ำกันสำหรับใบรับรองแต่ละใบที่ออกโดย CA เฉพาะ +(ดังที่กล่าวถึงใน RFC 5280) === นามสกุลไฟล์ใบรับรอง -#i นามสกุลไฟล์ที่ใช้กันทั่วไปสำหรับใบรับรอง X.509 มีหลายประเภทนามสกุลไฟล์เหล่านี้ยังใช้สำหรับข้อมูลอื่นๆ เช่น คีย์ส่วนตัวด้วย +#iiii นามสกุลไฟล์ที่ใช้กันทั่วไปสำหรับใบรับรอง X.509 +มีหลายประเภทนามสกุลไฟล์เหล่านี้ยังใช้สำหรับข้อมูลอื่นๆ เช่น คีย์ส่วนตัวด้วย -- `.pem` -- (อีเมลอิเล็กทรอนิกส์ที่เพิ่มความเป็นส่วนตัว) ใบรับรอง DER ที่เข้ารหัส Base64 แนบระหว่าง `-----BEGIN CERTIFICATE-----` และ `-----END CERTIFICATE-----` -- `.cer`, `.crt`, `.der` -- โดยปกติจะอยู่ในรูปแบบไบนารี DER แต่ใบรับรองที่เข้ารหัส Base64 ก็เป็นเรื่องปกติเช่นกัน (ดู `.pem` ด้านบน) -- `.p8`, `.p8e`, `.pk8` -- คีย์ส่วนตัวที่ส่งออกตามที่ระบุไว้ใน PKCS\#8 อาจอยู่ในรูปแบบ DER หรือ PEM ที่ขึ้นต้นด้วย `-----BEGIN PRIVATE KEY-----` คีย์ที่เข้ารหัสจะขึ้นต้นด้วย `-----BEGIN ENCRYPTED PRIVATE KEY-----` และอาจมี `.p8e` เป็นนามสกุลไฟล์ -- `.p10`, `.csr` -- PKCS\#10 เป็นคำขอลงนามใบรับรอง (CSR) ในรูปแบบ PEM ขึ้นต้นด้วย `-----BEGIN CERTIFICATE REQUEST-----` แบบฟอร์มเหล่านี้สร้างขึ้นเพื่อส่งไปยังผู้ออกใบรับรอง (CA) แบบฟอร์มประกอบด้วยรายละเอียดสำคัญของใบรับรองที่ร้องขอ เช่น ชื่อสามัญ (/CN), หัวเรื่อง, องค์กร, รัฐ, ประเทศ รวมถึงคีย์สาธารณะของใบรับรองที่ต้องการให้ลงนาม คีย์เหล่านี้จะได้รับการลงนามโดย CA และใบรับรองจะถูกส่งกลับคืน ใบรับรองที่ส่งคืนคือใบรับรอง สาธารณะ (ซึ่งมีคีย์สาธารณะแต่ไม่มีคีย์ส่วนตัว) ซึ่งตัวใบรับรองเองสามารถอยู่ในรูปแบบต่างๆ ได้หลายรูปแบบ แต่โดยปกติจะเป็น `.p7r` -- `.p7r` -- คำตอบ ของ PKCS\#7 ต่อ CSR ประกอบด้วยใบรับรองที่เพิ่งลงนาม และใบรับรองของ CA เอง -- `.p7s` -- ลายเซ็นดิจิทัล PKCS\#7 อาจมีไฟล์หรือข้อความที่ลงนามต้นฉบับ ใช้ใน S/MIME สำหรับการลงนามในอีเมลกำหนดไว้ใน RFC 2311 -- `.p7m` -- PKCS\#7 (SignedData, EnvelopedData) ข้อความ เช่น ไฟล์ที่เข้ารหัส ("enveloped") ข้อความ หรือจดหมายอีเมล MIME กำหนดไว้ใน RFC 2311 -- `.p7c` -- โครงสร้าง SignedData แบบ "certs-only" ของ PKCS\#7 ที่เสื่อมลง โดยไม่มีข้อมูลใดๆ ให้ลงนาม กำหนดไว้ใน RFC 2311 -- `.p7b` -- โครงสร้าง SignedData ของ PKCS\#7 ที่ไม่มีข้อมูล มีเพียงใบรับรองแบบบันเดิลหรือ CRL (ไม่ค่อยเกิดขึ้น) แต่ไม่มีคีย์ส่วนตัว ใช้รูปแบบ DER หรือ BER หรือ PEM ที่ขึ้นต้นด้วย `-----BEGIN PKCS7-----` รูปแบบที่ Windows ใช้สำหรับการแลกเปลี่ยนใบรับรอง รองรับโดย Java แต่มักใช้นามสกุล `.keystore` แทน ซึ่งแตกต่างจากใบรับรองแบบ `.pem` รูปแบบนี้มีวิธีที่กำหนดไว้สำหรับการรวมใบรับรองเส้นทางการรับรอง -- `.p12`, `.pfx`, `.pkcs12` -- PKCS\#12 อาจมีใบรับรอง (สาธารณะ) และคีย์ส่วนตัว (ป้องกันด้วยรหัสผ่าน) ในไฟล์เดียว `.pfx` - _Personal Information eXchange_ PFX ซึ่งเป็นรุ่นก่อนของ PKCS\#12 (โดยปกติจะมีข้อมูลในรูปแบบ PKCS\#12 เช่น ไฟล์ PFX ที่สร้างใน IIS) -- `.crl` -- รายการเพิกถอนใบรับรอง (CRL) หน่วยงานที่ออกใบรับรองจะจัดทำรายการเหล่านี้ขึ้นเพื่อใช้ในการเพิกถอนใบรับรองก่อนหมดอายุ +- `.pem` -- (อีเมลอิเล็กทรอนิกส์ที่เพิ่มความเป็นส่วนตัว) ใบรับรอง DER ที่เข้ารหัส Base64 + แนบระหว่าง `-----BEGIN CERTIFICATE-----` และ `-----END CERTIFICATE-----` +- `.cer`, `.crt`, `.der` -- โดยปกติจะอยู่ในรูปแบบไบนารี DER แต่ใบรับรองที่เข้ารหัส Base64 + ก็เป็นเรื่องปกติเช่นกัน (ดู `.pem` ด้านบน) +- `.p8`, `.p8e`, `.pk8` -- คีย์ส่วนตัวที่ส่งออกตามที่ระบุไว้ใน PKCS\#8 อาจอยู่ในรูปแบบ DER หรือ + PEM ที่ขึ้นต้นด้วย `-----BEGIN PRIVATE KEY-----` คีย์ที่เข้ารหัสจะขึ้นต้นด้วย + `-----BEGIN ENCRYPTED PRIVATE KEY-----` และอาจมี `.p8e` เป็นนามสกุลไฟล์ +- `.p10`, `.csr` -- PKCS\#10 เป็นคำขอลงนามใบรับรอง (CSR) ในรูปแบบ PEM ขึ้นต้นด้วย + `-----BEGIN CERTIFICATE REQUEST-----` แบบฟอร์มเหล่านี้สร้างขึ้นเพื่อส่งไปยังผู้ออกใบรับรอง + (CA) แบบฟอร์มประกอบด้วยรายละเอียดสำคัญของใบรับรองที่ร้องขอ เช่น ชื่อสามัญ (/CN), หัวเรื่อง, + องค์กร, รัฐ, ประเทศ รวมถึงคีย์สาธารณะของใบรับรองที่ต้องการให้ลงนาม + คีย์เหล่านี้จะได้รับการลงนามโดย CA และใบรับรองจะถูกส่งกลับคืน ใบรับรองที่ส่งคืนคือใบรับรอง + สาธารณะ (ซึ่งมีคีย์สาธารณะแต่ไม่มีคีย์ส่วนตัว) ซึ่งตัวใบรับรองเองสามารถอยู่ในรูปแบบต่างๆ + ได้หลายรูปแบบ แต่โดยปกติจะเป็น `.p7r` +- `.p7r` -- คำตอบ ของ PKCS\#7 ต่อ CSR ประกอบด้วยใบรับรองที่เพิ่งลงนาม และใบรับรองของ CA + เอง +- `.p7s` -- ลายเซ็นดิจิทัล PKCS\#7 อาจมีไฟล์หรือข้อความที่ลงนามต้นฉบับ ใช้ใน S/MIME + สำหรับการลงนามในอีเมลกำหนดไว้ใน RFC 2311 +- `.p7m` -- PKCS\#7 (SignedData, EnvelopedData) ข้อความ เช่น ไฟล์ที่เข้ารหัส + ("enveloped") ข้อความ หรือจดหมายอีเมล MIME กำหนดไว้ใน RFC 2311 +- `.p7c` -- โครงสร้าง SignedData แบบ "certs-only" ของ PKCS\#7 ที่เสื่อมลง + โดยไม่มีข้อมูลใดๆ ให้ลงนาม กำหนดไว้ใน RFC 2311 +- `.p7b` -- โครงสร้าง SignedData ของ PKCS\#7 ที่ไม่มีข้อมูล มีเพียงใบรับรองแบบบันเดิลหรือ CRL + (ไม่ค่อยเกิดขึ้น) แต่ไม่มีคีย์ส่วนตัว ใช้รูปแบบ DER หรือ BER หรือ PEM ที่ขึ้นต้นด้วย + `-----BEGIN PKCS7-----` รูปแบบที่ Windows ใช้สำหรับการแลกเปลี่ยนใบรับรอง รองรับโดย Java + แต่มักใช้นามสกุล `.keystore` แทน ซึ่งแตกต่างจากใบรับรองแบบ `.pem` + รูปแบบนี้มีวิธีที่กำหนดไว้สำหรับการรวมใบรับรองเส้นทางการรับรอง +- `.p12`, `.pfx`, `.pkcs12` -- PKCS\#12 อาจมีใบรับรอง (สาธารณะ) และคีย์ส่วนตัว + (ป้องกันด้วยรหัสผ่าน) ในไฟล์เดียว `.pfx` - _Personal Information eXchange_ PFX + ซึ่งเป็นรุ่นก่อนของ PKCS\#12 (โดยปกติจะมีข้อมูลในรูปแบบ PKCS\#12 เช่น ไฟล์ PFX ที่สร้างใน IIS) +- `.crl` -- รายการเพิกถอนใบรับรอง (CRL) + หน่วยงานที่ออกใบรับรองจะจัดทำรายการเหล่านี้ขึ้นเพื่อใช้ในการเพิกถอนใบรับรองก่อนหมดอายุ -#i PKCS\#7 เป็นมาตรฐานสำหรับการลงนามหรือเข้ารหัสข้อมูล (เรียกอย่างเป็นทางการว่า "enveloping") เนื่องจากจำเป็นต้องใช้ใบรับรองเพื่อตรวจสอบข้อมูลที่ลงนามแล้วจึงสามารถรวมใบรับรองไว้ในโครงสร้าง SignedData ได้ +#iiii PKCS\#7 เป็นมาตรฐานสำหรับการลงนามหรือเข้ารหัสข้อมูล (เรียกอย่างเป็นทางการว่า +"enveloping") +เนื่องจากจำเป็นต้องใช้ใบรับรองเพื่อตรวจสอบข้อมูลที่ลงนามแล้วจึงสามารถรวมใบรับรองไว้ในโครงสร้าง +SignedData ได้ diff --git a/Chapter3.typ b/Chapter3.typ index c4597ad..25f296c 100644 --- a/Chapter3.typ +++ b/Chapter3.typ @@ -117,6 +117,8 @@ [นำเสนอโครงงาน], [], [], [], [], [], [], [], [], [], [], [], ) +#show: page-theme + === ผังการดำเนินงาน #diagram( @@ -233,35 +235,58 @@ == ขั้นตอนการประกอบ +โครงงานแบ่งออกเป็น 3 โมดูล + ++ โมดูลเซนเซอร์ NFC ขาเข้าและบอร์ด ESP32 ++ โมดูลเซนเซอร์ NFC ขาออก ++ โมดูลเซนเซอร์ PIR + +#show heading: it => { + if it.level > 2 { + block( + it, + inset: (left: -3em), + ) + } else { + it + } +} + +=== โมดูลเซนเซอร์ NFC ขาออก + +#i ดำเนินการเจาะรูบริเวณตัวกล่องเพื่อใช้เป็นช่องสำหรับสายไฟ +จากนั้นนำสายไฟร้อยผ่านช่องดังกล่าวและต่อเข้ากับเซนเซอร์ NFC ให้เรียบร้อยตามขั้นตอน + === การติดตั้งอุปกรณ์ === การเขียนเฟิร์มแวร์ -โครงงานนี้ใช้ซอฟต์แวร์ PlatformIO ในการสร้างและจัดการโปรเจกต์เฟิร์มแวร์ +#i โครงงานนี้ใช้ซอฟต์แวร์ PlatformIO ในการสร้างและจัดการโปรเจกต์เฟิร์มแวร์ โดยหากต้องการเพียงแค่เขียนเฟิร์มแวร์ลงไปยังบอร์ด ESP32 คุณจำเป็นต้องใช้ซอฟต์แวร์หลัก ๆ คือ -PlatformIO Core และ Git (ไม่จำเป็น แต่เพื่อความสะดวกสบาย) +PlatformIO Core และ Git (ไม่จำเป็น แต่เพื่อความสะดวกสบาย) อย่างไรก็ตาม PlatformIO +จำเป็นต้องใช้ Python เวอร์ชัน 3.6 ขึ้นไปด้วยเช่นกัน ดังนั้นคุณจำเป็นต้องติดตั้ง Python +ด้วยหากคุณยังไม่มี -อย่างไรก็ตาม PlatformIO จำเป็นต้องใช้ Python เวอร์ชัน 3.6 ขึ้นไปด้วยเช่นกัน -ดังนั้นคุณจำเป็นต้องติดตั้ง Python ด้วยหากคุณยังไม่มี +#i ในขั้นตอนแรก โปรดเปิดเทอร์มินัลของคุณ ซึ่งโดยทั่วไปแล้วคุณสามารถค้นหาแอพลิเคชัน#jb "Terminal" +ได้เลย โดยบน Windows 10 เวอร์ชั่นใหม่ ๆ และ Windows 11 จะมาพร้อมกับแอพลิเคชัน Windows +Terminal อย่างไรก็ตาม เมื่อเปิดแล้ว โปรดตรวจสอบให้แน่ใจว่าคุณกำลังใช้ PowerShell และไม่ใช่ +Command Prompt -ในขั้นตอนแรก โปรดเปิดเทอร์มินัลของคุณ ซึ่งโดยทั่วไปแล้วคุณสามารถค้นหาแอพลิเคชัน "Terminal" ได้เลย -โดยบน Windows 10 เวอร์ชั่นใหม่ ๆ และ Windows 11 จะมาพร้อมกับแอพลิเคชัน Windows Terminal -อย่างไรก็ตาม เมื่อเปิดแล้ว โปรดตรวจสอบให้แน่ใจว่าคุณกำลังใช้ PowerShell และไม่ใช่ Command -Prompt - -โดยในปัจจุบัน Python เวอร์ชันล่าสุดคือ Python 3.14.2 โดยคุณสามารถติดตั้ง Python และ Git บน -Windows ได้ด้วยการใช้คำสั่งต่อไปนี้ +#i โดยในปัจจุบัน Python เวอร์ชันล่าสุดคือ Python 3.14.2 โดยคุณสามารถติดตั้ง Python และ Git +บน Windows ได้ด้วยการใช้คำสั่งต่อไปนี้ ```sh winget install Python.Python.3.14 Git.Git -e -s winget ``` -สำหรับระบบปฏิบัติการอื่นนั้น โดยปกติแล้วจะไม่ต้องติดตั้ง Python +#i สำหรับระบบปฏิบัติการอื่นนั้น โดยปกติแล้วจะไม่ต้องติดตั้ง Python เพิ่มเนื่องจากมีติดมากับระบบปฏิบัติการอยู่แล้ว อย่างไรก็ตาม บน Linux อาจต้องมีการติดตั้งการรองรับ Virtual Environment แยก โดยแต่ละระบบจะมีชื่อแพคเกจไม่เหมือนกัน โดยบน Debian, Fedora, และ Arch สามารถใช้คำสั่งต่อไปนี้ในการติดตั้งทั้ง Python Virtual Environment และ Git พร้อมกันได้ +#pagebreak() + ```sh # Debian sudo apt install python3-venv git @@ -271,13 +296,11 @@ sudo dnf install python3-virtualenv git sudo pacman -S python-virtualenv git ``` -#pagebreak() - #show raw.where(block: true): set block(below: 2em) ==== การติดตั้ง PlatformIO Core ผ่านแพคเกจ -หากคุณใช้ Fedora Linux หรือ Arch Linux (หรือลูก ๆ ของมัน) คุณสามารถติดตั้งแพคเกจ +#i หากคุณใช้ Fedora Linux หรือ Arch Linux (หรือลูก ๆ ของมัน) คุณสามารถติดตั้งแพคเกจ PlatformIO ได้โดยตรง โดยมีคำสั่งดังนี้: ```sh @@ -287,18 +310,16 @@ sudo dnf install platformio sudo pacman -S platformio-core ``` -หากคุณติดตั้งแพคเกจ Fedora นั้นแล้ว คุณไม่จำเป็นที่จะต้องติดตั้งกฎ udev ด้วยตนเอง +- หากคุณติดตั้งแพคเกจ Fedora นั้นแล้ว คุณไม่จำเป็นที่จะต้องติดตั้งกฎ udev ด้วยตนเอง (ที่จะถูกกล่าวถึงใน@pioudev) - -หากคุณใช้ Arch คุณสามารถติดตั้งแพคเกจกฎ udev ได้โดยตรงโดยไม่ต้องดาวน์โหลดเอง - -```sh -sudo pacman -S --asdeps platformio-core-udev -``` +- หากคุณใช้ Arch คุณสามารถติดตั้งแพคเกจกฎ udev ได้โดยตรงโดยไม่ต้องดาวน์โหลดเอง + ```sh + sudo pacman -S --asdeps platformio-core-udev + ``` ==== การติดตั้ง PlatformIO Core ผ่านสคริปต์ -ถัดไป ในการติดตั้ง PlatformIO Core สามารถทำได้โดยการใช้สคริปต์ติดตั้ง โดยสำหรับ `curl` +#i ถัดไป ในการติดตั้ง PlatformIO Core สามารถทำได้โดยการใช้สคริปต์ติดตั้ง โดยสำหรับ `curl` สามารถใช้คำสั่งนี้ได้: ```sh @@ -319,20 +340,22 @@ iwr -OutFile get-platformio.py -Uri https://raw.githubusercontent.com/platformio (มีการเว้นบรรทัดใหม่เนื่องจากพื้นที่ไม่เพียงพอ โปรดอย่าเว้นบรรทัดเมื่อพิมพ์คำสั่งจริง) -แล้วดังนั้นจึงใช้คำสั่ง `python3 get-platformio.py` ในการรันสคริปต์ติดตั้งที่ได้ทำการดาวน์โหลดมา - -โดยค่าเริ่มต้นแล้ว PlatformIO จะไม่เพิ่มตนเองเข้าไปยังตัวแปรสิ่งแวดล้อม PATH +#i แล้วดังนั้นจึงใช้คำสั่ง `python3 get-platformio.py` +ในการรันสคริปต์ติดตั้งที่ได้ทำการดาวน์โหลดมา โดยค่าเริ่มต้นแล้ว PlatformIO +จะไม่เพิ่มตนเองเข้าไปยังตัวแปรสิ่งแวดล้อม PATH ซึ่งจำเป็นในการใช้คำสั่งจากที่ใหนก็ได้โดยไม่ต้องกล่าวถึงไฟล์พาธ -โดยสำหรับ Linux แล้วนั้น คุณต้องเพิ่ม `$HOME/.local/bin/` เข้าไปยัง PATH ของคุณ โดยหากคุณใช้ -Bash คุณสามารถแก้ไข `~/.bash_profile` และเพิ่มบรรทัดนี้เข้าไปได้: +#pagebreak() + +#i โดยสำหรับ Linux แล้วนั้น คุณต้องเพิ่ม `$HOME/.local/bin/` เข้าไปยัง PATH ของคุณ +โดยหากคุณใช้ Bash คุณสามารถแก้ไข `~/.bash_profile` และเพิ่มบรรทัดนี้เข้าไปได้: ```sh export PATH=$PATH:$HOME/.local/bin ``` -หากคุณใช้ Zsh สามารถใช้โคดเดียวกันได้ เพียงแต่คุณต้องแก้ไขไฟล์ `~/.zprofile` หรือ `~/.zshrc` -แทน +#i หากคุณใช้ Zsh สามารถใช้โคดเดียวกันได้ เพียงแต่คุณต้องแก้ไขไฟล์ `~/.zprofile` หรือ +`~/.zshrc` แทน โดยบน Windows มีขั้นตอนดังนี้: @@ -346,14 +369,15 @@ export PATH=$PATH:$HOME/.local/bin ==== 99-platformio-udev.rules -ผู้ใช้ Linux จำเป็นที่จะต้องติดตั้งกฎ udev โดยสามารถดูไฟล์กฎ udev เวอร์ชันล่าสุดได้ที่\ +#i ผู้ใช้ Linux จำเป็นที่จะต้องติดตั้งกฎ udev โดยสามารถดูไฟล์กฎ udev เวอร์ชันล่าสุดได้ที่#jb https://raw.githubusercontent.com/platformio/platformio-core/develop/platformio/assets/system/99-platformio-udev.rules *หมายเหตุ:* โปรดตรวจสอบว่า PID และ VID ของบอร์ดคุณอยู่ในไฟล์กฎนั้น โดยคุณสามารถดู PID/VID ของบอร์ดคุณได้ผ่านคำสั่ง `pio device list` -โดยไฟล์นั้นต้องถูกวางอยู่ที่ `/etc/udev/rules.d/99-platformio-udev.rules` (ตำแหน่งที่ดีที่สุด) -หรือ `/lib/udev/rules.d/99-platformio-udev.rules` (อาจจำเป็นสำหรับบางระบบที่พัง) +#i โดยไฟล์นั้นต้องถูกวางอยู่ที่ `/etc/udev/rules.d/99-platformio-udev.rules` +(ตำแหน่งที่ดีที่สุด) หรือ `/lib/udev/rules.d/99-platformio-udev.rules` +(อาจจำเป็นสำหรับบางระบบที่พัง) โปรดใช้คำสั่งต่อไปนี้ในการดาวน์โหลดและวางไฟล์นั้นไว้ในสถานที่ที่ถูกต้อง: @@ -382,28 +406,30 @@ sudo udevadm trigger ==== การดาวน์โหลดโปรเจกต์ -สามารถใช้ Git ในการ clone โปรเจกต์ได้ด้วยคำสั่งต่อไปนี้: +#i สามารถใช้ Git ในการ clone โปรเจกต์ได้ด้วยคำสั่งต่อไปนี้: ```sh git clone https://gitskette.dailitation.xyz/linesofcodes/liteauth-firmware32.git ``` -โดย Git นั้นจะทำการโคลนโปรเจกต์ไปที่โฟลเดอร์ `liteauth-firmware32` เนื่องจากเป็นชื่อของ Git -repository - -หรือไปที่ https://gitskette.dailitation.xyz/linesofcodes/liteauth-firmware32 -และทำการคลิกปุ่ม *Code* แล้วกด *Download ZIP* หรือ *Download TAR.GZ* -แล้วทำการแตกไฟล์ได้ตามปกติ +#i โดย Git นั้นจะทำการโคลนโปรเจกต์ไปที่โฟลเดอร์ `liteauth-firmware32` เนื่องจากเป็นชื่อของ +Git repository หรือหากไม่ต้องการใช้ Git กรุณาไปที่ +https://gitskette.dailitation.xyz/linesofcodes/liteauth-firmware32 และทำการคลิกปุ่ม +*Code* แล้วกด *Download ZIP* หรือ *Download TAR.GZ* แล้วทำการแตกไฟล์ได้ตามปกติ หลังจากนั้น ไปที่โฟลเดอร์ของคุณในเทอร์มินัลโดยใช้คำสั่ง `cd` ==== คำสั่ง PlatformIO เบื้องต้น -- `pio run --list-targets`: ดูรายการเป้าหมายคำสั่งรัน -- `pio run upload`: รันเป้าหมายอัพโหลด ซึ่งนี่คือคำสั่งที่คุณควรจะใช้ในการเขียนเฟิร์มแวร์ลงบนบอร์ด -- `pio device monitor`: เปิด Serial Monitor +#[ + #set list(indent: 3em) + - `pio run --list-targets`: ดูรายการเป้าหมายคำสั่งรัน + - `pio run upload`: รันเป้าหมายอัพโหลด + ซึ่งนี่คือคำสั่งที่คุณควรจะใช้ในการเขียนเฟิร์มแวร์ลงบนบอร์ด + - `pio device monitor`: เปิด Serial Monitor +] -*หมายเหตุ:* โปรดใช้คำสั่งประเภท `pio run` ในโฟลเดอร์ของโปรเจกต์ +#i *หมายเหตุ:* โปรดใช้คำสั่งประเภท `pio run` ในโฟลเดอร์ของโปรเจกต์ == การทดสอบ diff --git a/Dracula.tmTheme b/Dracula.tmTheme new file mode 100644 index 0000000..c3434ec --- /dev/null +++ b/Dracula.tmTheme @@ -0,0 +1,940 @@ + + + + + + + + name + Dracula + settings + + + settings + + background + #282a36 + caret + #f8f8f0 + block_caret + #999a9e + foreground + #f8f8f2 + invisibles + #3B3A32 + lineHighlight + #44475a + selection + #44475a + findHighlight + #effb7b + findHighlightForeground + #000000 + selectionBorder + #222218 + activeGuide + #9D550FB0 + bracketsForeground + #F8F8F2A5 + bracketsOptions + underline + bracketContentsForeground + #F8F8F2A5 + bracketContentsOptions + underline + tagsOptions + stippled_underline + + + + name + Comment + scope + comment + settings + + foreground + #6272a4 + fontStyle + + + + + name + String + scope + string + settings + + foreground + #f1fa8c + + + + name + Number + scope + constant.numeric + settings + + foreground + #bd93f9 + + + + name + Built-in constant + scope + constant.language + settings + + foreground + #bd93f9 + + + + name + User-defined constant + scope + constant.character, constant.other + settings + + foreground + #bd93f9 + + + + name + Variable + scope + variable + settings + + fontStyle + + + + + name + Ruby's @variable + scope + variable.other.readwrite.instance + settings + + fontStyle + + foreground + #ffb86c + + + + name + String interpolation + scope + constant.character.escaped, constant.character.escape, string source, string source.ruby + settings + + fontStyle + + foreground + #ff79c6 + + + + name + Ruby Regexp + scope + source.ruby string.regexp.classic.ruby,source.ruby string.regexp.mod-r.ruby + settings + + fontStyle + + foreground + #ff5555 + + + + name + Keyword + scope + keyword + settings + + foreground + #ff79c6 + + + + name + Storage + scope + storage + settings + + fontStyle + + foreground + #ff79c6 + + + + name + Storage type + scope + storage.type + settings + + fontStyle + italic + foreground + #8be9fd + + + + name + Storage Type Namespace + scope + storage.type.namespace + settings + + fontStyle + italic + foreground + #8be9fd + + + + name + Storage Type Class + scope + storage.type.class + settings + + fontStyle + italic + foreground + #ff79c6 + + + + name + Class name + scope + entity.name.class + settings + + fontStyle + underline + foreground + #8be9fd + + + + name + Meta Path + scope + meta.path + settings + + fontStyle + underline + foreground + #66d9ef + + + + name + Inherited class + scope + entity.other.inherited-class + settings + + fontStyle + italic underline + foreground + #8be9fd + + + + name + Function name + scope + entity.name.function + settings + + fontStyle + + foreground + #50fa7b + + + + name + Function argument + scope + variable.parameter + settings + + fontStyle + italic + foreground + #ffb86c + + + + name + Tag name + scope + entity.name.tag + settings + + fontStyle + + foreground + #ff79c6 + + + + name + Tag attribute + scope + entity.other.attribute-name + settings + + fontStyle + + foreground + #50fa7b + + + + name + Library function + scope + support.function + settings + + fontStyle + + foreground + #8be9fd + + + + name + Library constant + scope + support.constant + settings + + fontStyle + + foreground + #6be5fd + + + + name + Library class/type + scope + support.type, support.class + settings + + fontStyle + italic + foreground + #66d9ef + + + + name + Library variable + scope + support.other.variable + settings + + fontStyle + + + + + name + Support Other Namespace + scope + support.other.namespace + settings + + fontStyle + italic + foreground + #66d9ef + + + + name + Invalid + scope + invalid + settings + + background + #ff79c6 + fontStyle + + foreground + #F8F8F0 + + + + name + Invalid deprecated + scope + invalid.deprecated + settings + + background + #bd93f9 + foreground + #F8F8F0 + + + + name + JSON String + scope + meta.structure.dictionary.json string.quoted.double.json + settings + + foreground + #CFCFC2 + + + + name + diff.header + scope + meta.diff, meta.diff.header + settings + + foreground + #6272a4 + + + + name + diff.deleted + scope + markup.deleted + settings + + foreground + #ff79c6 + + + + name + diff.inserted + scope + markup.inserted + settings + + foreground + #50fa7b + + + + name + diff.changed + scope + markup.changed + settings + + foreground + #E6DB74 + + + + scope + constant.numeric.line-number.find-in-files - match + settings + + foreground + #bd93f9 + + + + scope + entity.name.filename + settings + + foreground + #E6DB74 + + + + scope + message.error + settings + + foreground + #F83333 + + + + name + JSON Punctuation + scope + punctuation.definition.string.begin.json - meta.structure.dictionary.value.json, punctuation.definition.string.end.json - meta.structure.dictionary.value.json + settings + + foreground + #EEEEEE + + + + name + JSON Structure + scope + meta.structure.dictionary.json string.quoted.double.json + settings + + foreground + #8be9fd + + + + name + JSON String + scope + meta.structure.dictionary.value.json string.quoted.double.json + settings + + foreground + #f1fa8c + + + + name + JSON: 6 deep + scope + meta meta meta meta meta meta meta.structure.dictionary.value string + settings + + foreground + #50fa7b + + + + name + JSON: 5 deep + scope + meta meta meta meta meta meta.structure.dictionary.value string + settings + + foreground + #ffb86c + + + + name + JSON: 4 deep + scope + meta meta meta meta meta.structure.dictionary.value string + settings + + foreground + #ff79c6 + + + + name + JSON: 3 deep + scope + meta meta meta meta.structure.dictionary.value string + settings + + foreground + #bd93f9 + + + + name + JSON: 2 deep + scope + meta meta meta.structure.dictionary.value string + settings + + foreground + #50fa7b + + + + name + JSON: 1 deep + scope + meta meta.structure.dictionary.value string + settings + + foreground + #ffb86c + + + + + + name + Markup: strike + scope + markup.strike + settings + + fontStyle + italic + foreground + #FFB86C + + + + name + Markup: bold + scope + markup.bold + settings + + fontStyle + bold + foreground + #FFB86C + + + + name + Markup: italic + scope + markup.italic + settings + + fontStyle + italic + foreground + #FFB86C + + + + name + Markdown: heading + scope + markup.heading + settings + + foreground + #8BE9FD + + + + name + Markdown: List Items Punctuation + scope + punctuation.definition.list_item.markdown + settings + + foreground + #FF79C6 + + + + name + Markdown: Blockquote + scope + markup.quote + settings + + fontStyle + italic + foreground + #6272A4 + + + + name + Markdown: Blockquote Punctuation + scope + punctuation.definition.blockquote.markdown + settings + + fontStyle + italic + background + #6272A4 + foreground + #6272A4 + + + + name + Markdown: Separator + scope + meta.separator + settings + + foreground + #6272A4 + + + + name + Markup: raw inline + scope + text.html.markdown markup.raw.inline + settings + + foreground + #50FA7B + + + + name + Markup: underline + scope + markup.underline + settings + + fontStyle + underline + foreground + #BD93F9 + + + + name + Markup: Raw block + scope + markup.raw.block + settings + + foreground + #CFCFC2 + + + + name + Markdown: Raw Block fenced source + scope + markup.raw.block.fenced.markdown source + settings + + foreground + #F8F8F2 + + + + name + Markdown: Fenced Bode Block + scope + punctuation.definition.fenced.markdown, variable.language.fenced.markdown + settings + + fontStyle + italic + foreground + #6272A4 + + + + name + Markdown: Fenced Language + scope + variable.language.fenced.markdown + settings + + fontStyle + italic + foreground + #6272A4 + + + + name + Punctuation Accessor + scope + punctuation.accessor + settings + + foreground + #FF79C6 + + + + name + Meta Function Return Type + scope + meta.function.return-type + settings + + foreground + #FF79C6 + + + + name + Punctuation Section Block Begin + scope + punctuation.section.block.begin + settings + + foreground + #ffffff + + + + name + Punctuation Section Block End + scope + punctuation.section.block.end + settings + + foreground + #ffffff + + + + name + Punctuation Section Embedded Begin + scope + punctuation.section.embedded.begin + settings + + foreground + #ff79c6 + + + + name + Punctuation Section Embedded End + scope + punctuation.section.embedded.end + settings + + foreground + #ff79c6 + + + + name + Punctuation Separator Namespace + scope + punctuation.separator.namespace + settings + + foreground + #ff79c6 + + + + name + Variable Function + scope + variable.function + settings + + foreground + #50fa7b + + + + name + Variable Other + scope + variable.other + settings + + foreground + #ffffff + + + + name + Variable Language + scope + variable.language + settings + + foreground + #bd93f9 + + + + name + Entity Name Module Ruby + scope + entity.name.module.ruby + settings + + foreground + #8be9fd + + + + name + Entity Name Constant Ruby + scope + entity.name.constant.ruby + settings + + foreground + #bd93f9 + + + + name + Support Function Builtin Ruby + scope + support.function.builtin.ruby + settings + + foreground + #ffffff + + + + name + Storage Type Namespace CS + scope + storage.type.namespace.cs + settings + + foreground + #ff79c6 + + + + name + Entity Name Namespace CS + scope + entity.name.namespace.cs + settings + + foreground + #8be9fd + + + + uuid + 83091B89-765E-4F0D-9275-0EC6CB084126 + colorSpaceName + sRGB + semanticClass + theme.dracula + author + Zeno Rocha + + diff --git a/PageTemplate.typ b/PageTemplate.typ index 7bea5b0..9700b34 100644 --- a/PageTemplate.typ +++ b/PageTemplate.typ @@ -16,14 +16,28 @@ right: 1in, bottom: 1in, ), - header: context [ - #h(1fr) - #counter(page).display(thai-numbering) - ], + header: none, ) doc } +/// Sane indentation +#let i = h(3em) + +/// Insane shit forced upon by my teacher + +/// For indenting in 3rd level subheadings +#let iii = h(3em) +// #let iii = h(5.5em) + +/// For indenting in 4th level subheadings +#let iiii = h(6em) +// #let iiii = h(9.25em) + +#let iiiii = h(9em) + +#let iiiiii = h(12em) + #let page-theme(doc) = { set page( paper: "a4", @@ -51,12 +65,25 @@ leading: 1em, ) set list(indent: 1em) - show heading: set text(size: 10.5pt) + show heading: set text(size: 10.5pt, weight: "regular") show heading: set block(below: 1em) - show heading.where(level: 1): set text(size: 12pt) + show heading.where(level: 1): set text(size: 12pt, weight: "bold") show heading.where(level: 1): set align(center) + show heading.where(level: 2): set text(weight: "bold") + show heading: it => { + if it.level > 2 { + block( + it, + inset: (left: 3em * (it.level - 2)), + ) + } else { + it + } + } + show math.equation: set text(font: "Laksaman") show raw: set text(font: "Cascadia Code", size: 10pt) show table.cell.where(y: 0): strong + set enum(number-align: start + top) set ref(supplement: "หัวข้อ") doc } @@ -64,7 +91,7 @@ // Figure with attribution information #let afigure( body, - attr: "", + attr: none, ..args, ) = [ #figure(body, ..args) @@ -75,6 +102,4 @@ ) ] -#let i = h(3em) - #let jb = linebreak(justify: true) diff --git a/References.yaml b/References.yaml index 4ed6264..9d9272e 100644 --- a/References.yaml +++ b/References.yaml @@ -1,353 +1,354 @@ wkNodeMcu: - type: Web - title: NodeMCU - date: 2025-08-15 - language: en - publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) - url: - date: 2025-12-8 - value: https://en.wikipedia.org/w/index.php?title=NodeMCU&oldid=1306030712 + type: Web + title: NodeMCU + date: 2025-08-15 + language: en + publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) + url: + date: 2025-12-8 + value: https://en.wikipedia.org/w/index.php?title=NodeMCU&oldid=1306030712 wkEsp32: - type: Web - title: ESP32 - date: 2025-11-2 - language: en - publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) - url: - date: 2025-12-8 - value: https://en.wikipedia.org/w/index.php?title=ESP32&oldid=1320113248 + type: Web + title: ESP32 + date: 2025-11-2 + language: en + publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) + url: + date: 2025-12-8 + value: https://en.wikipedia.org/w/index.php?title=ESP32&oldid=1320113248 wkEspressif: - type: Web - title: Espressif Systems - date: 2025-10-6 - language: en - publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) - url: - date: 2025-12-8 - value: https://en.wikipedia.org/w/index.php?title=Espressif_Systems&oldid=1315427960 + type: Web + title: Espressif Systems + date: 2025-10-6 + language: en + publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) + url: + date: 2025-12-8 + value: https://en.wikipedia.org/w/index.php?title=Espressif_Systems&oldid=1315427960 wkFlutter: - type: Web - title: Flutter - date: 2025-11-12 - language: en - publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) - url: - date: 2025-11-30 - value: https://en.wikipedia.org/w/index.php?title=Flutter_(software)&oldid=1321794260 + type: Web + title: Flutter + date: 2025-11-12 + language: en + publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) + url: + date: 2025-11-30 + value: https://en.wikipedia.org/w/index.php?title=Flutter_(software)&oldid=1321794260 wkGit: - type: Web - title: Git - date: 2025-11-1 - language: en - publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) - url: - date: 2025-11-30 - value: https://en.wikipedia.org/w/index.php?title=Git&oldid=1319901866 + type: Web + title: Git + date: 2025-11-1 + language: en + publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) + url: + date: 2025-11-30 + value: https://en.wikipedia.org/w/index.php?title=Git&oldid=1319901866 wkGitea: - type: Web - title: Gitea - date: 2025-11-17 - language: en - publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) - url: - date: 2025-11-30 - value: https://en.wikipedia.org/w/index.php?title=Gitea&oldid=1322631603 + type: Web + title: Gitea + date: 2025-11-17 + language: en + publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) + url: + date: 2025-11-30 + value: https://en.wikipedia.org/w/index.php?title=Gitea&oldid=1322631603 wkHttps: - type: Web - title: HTTPS - date: 2025-11-30 - language: en - publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) - url: + type: Web + title: HTTPS date: 2025-11-30 - value: https://en.wikipedia.org/w/index.php?title=HTTPS&oldid=1324964055 + language: en + publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) + url: + date: 2025-11-30 + value: https://en.wikipedia.org/w/index.php?title=HTTPS&oldid=1324964055 wkTls: - type: Web - title: Transport Layer Security - date: 2025-11-24 - language: en - publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) - url: - date: 2025-11-30 - value: https://en.wikipedia.org/w/index.php?title=Transport_Layer_Security&oldid=1323879251 + type: Web + title: Transport Layer Security + date: 2025-11-24 + language: en + publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) + url: + date: 2025-11-30 + value: https://en.wikipedia.org/w/index.php?title=Transport_Layer_Security&oldid=1323879251 wkC: - type: Web - title: C (programming language) - date: 2025-11-26 - language: en - publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) - url: - date: 2025-11-30 - value: https://en.wikipedia.org/w/index.php?title=C_(programming_language)&oldid=1324211766 + type: Web + title: C (programming language) + date: 2025-11-26 + language: en + publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) + url: + date: 2025-11-30 + value: https://en.wikipedia.org/w/index.php?title=C_(programming_language)&oldid=1324211766 wkGcc: - type: Web - title: GNU Compiler Collection - date: 2025-11-30 - language: en - publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) - url: + type: Web + title: GNU Compiler Collection date: 2025-11-30 - value: https://en.wikipedia.org/w/index.php?title=GNU_Compiler_Collection&oldid=1324929423 + language: en + publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) + url: + date: 2025-11-30 + value: https://en.wikipedia.org/w/index.php?title=GNU_Compiler_Collection&oldid=1324929423 wkDart: - type: Web - title: Dart (programming language) - date: 2025-11-21 - language: en - publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) - url: - date: 2025-12-8 - value: https://en.wikipedia.org/w/index.php?title=Dart_(programming_language)&oldid=1323401675 + type: Web + title: Dart (programming language) + date: 2025-11-21 + language: en + publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) + url: + date: 2025-12-8 + value: https://en.wikipedia.org/w/index.php?title=Dart_(programming_language)&oldid=1323401675 wkX509: - type: Web - title: X.509 - date: 2025-11-11 - language: en - publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) - url: - date: 2025-11-29 - value: https://en.wikipedia.org/w/index.php?title=X.509&oldid=1321610537 + type: Web + title: X.509 + date: 2025-11-11 + language: en + publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) + url: + date: 2025-11-29 + value: https://en.wikipedia.org/w/index.php?title=X.509&oldid=1321610537 wkDerEncoding: - type: Web - title: X.690 - language: en - publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) - date: 2025-10-6 - url: - date: 2025-12-3 - value: https://en.wikipedia.org/w/index.php?title=X.690&oldid=1315457524#DER_encoding + type: Web + title: X.690 + language: en + publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) + date: 2025-10-6 + url: + date: 2025-12-3 + value: https://en.wikipedia.org/w/index.php?title=X.690&oldid=1315457524#DER_encoding wkOpenSSL: - type: Web - title: OpenSSL - date: 2025-12-1 - language: en - publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) - url: - date: 2025-12-3 - value: https://en.wikipedia.org/w/index.php?title=OpenSSL&oldid=1325138239 + type: Web + title: OpenSSL + date: 2025-12-1 + language: en + publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) + url: + date: 2025-12-3 + value: https://en.wikipedia.org/w/index.php?title=OpenSSL&oldid=1325138239 wkMaterial: - type: Web - title: Material Design - date: 2025-11-15 - language: en - publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) - url: - date: 2025-12-6 - value: https://en.wikipedia.org/w/index.php?title=Material_Design&oldid=1322252287 + type: Web + title: Material Design + date: 2025-11-15 + language: en + publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) + url: + date: 2025-12-6 + value: https://en.wikipedia.org/w/index.php?title=Material_Design&oldid=1322252287 wkBuzzer: - type: Web - title: Buzzer - date: 2025-11-13 - language: en - publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) - url: - date: 2025-12-10 - value: https://en.wikipedia.org/w/index.php?title=Buzzer&oldid=1321902450 + type: Web + title: Buzzer + date: 2025-11-13 + language: en + publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) + url: + date: 2025-12-10 + value: https://en.wikipedia.org/w/index.php?title=Buzzer&oldid=1321902450 wkNFC: - type: Web - title: Near-field communication - date: 2025-11-5 - language: en - publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) - url: - date: 2025-12-10 - value: https://en.wikipedia.org/w/index.php?title=Near-field_communication&oldid=1320616102 + type: Web + title: Near-field communication + date: 2025-11-5 + language: en + publisher: มูลนิธิวิกิมีเดีย (ภายใต้ CC BY-SA 4.0) + url: + date: 2025-12-10 + value: https://en.wikipedia.org/w/index.php?title=Near-field_communication&oldid=1320616102 androidJdkVersion: - type: Web - title: Java versions in Android builds - publisher: Android Developers (ภายใต้สัญญาอนุญาต Apache License 2.0) - language: en - # Publishing date/Last updated - date: 2025-11-21 - url: - value: https://developer.android.com/build/jdks - # Date of research - date: 2025-11-26 + type: Web + title: Java versions in Android builds + publisher: Android Developers (ภายใต้สัญญาอนุญาต Apache License 2.0) + language: en + # Publishing date/Last updated + date: 2025-11-21 + url: + value: https://developer.android.com/build/jdks + # Date of research + date: 2025-11-26 sofFlutterMinSdk: - type: Web - title: Where is the value of "flutter.minSdkVersion" in Flutter project initialized? - date: 2025-08-26 - language: en - publisher: Stackoverflow (ภายใต้ CC BY-SA 4.0) - url: - date: 2025-11-26 - value: https://stackoverflow.com/a/79746636 + type: Web + title: Where is the value of "flutter.minSdkVersion" in Flutter project initialized? + date: 2025-08-26 + language: en + publisher: Stackoverflow (ภายใต้ CC BY-SA 4.0) + url: + date: 2025-11-26 + value: https://stackoverflow.com/a/79746636 sofCDataModel: - type: Web - title: Are data models - ILP32 or LP64 decided by OS or the Hardware Architecture? - date: 2020-10-14 - language: en - publisher: Stackoverflow (ภายใต้ CC BY-SA 4.0) - url: - date: 2025-12-9 - value: https://stackoverflow.com/a/79746636 + type: Web + title: Are data models - ILP32 or LP64 decided by OS or the Hardware Architecture? + date: 2020-10-14 + language: en + publisher: Stackoverflow (ภายใต้ CC BY-SA 4.0) + url: + date: 2025-12-9 + value: https://stackoverflow.com/a/79746636 flInstallManually: - type: Web - title: Install Flutter manually - date: 2025-10-28 - language: en - publisher: Flutter (ภายใต้ CC BY 3.0 และ BSD License) - url: - date: 2025-12-6 - value: https://docs.flutter.dev/install/manual + type: Web + title: Install Flutter manually + date: 2025-10-28 + language: en + publisher: Flutter (ภายใต้ CC BY 3.0 และ BSD License) + url: + date: 2025-12-6 + value: https://docs.flutter.dev/install/manual flVsCode: - type: Web - title: Install Flutter using VS Code - date: 2025-10-28 - language: en - publisher: Flutter (ภายใต้ CC BY 3.0 และ BSD License) - url: - date: 2025-12-6 - value: https://docs.flutter.dev/install/with-vs-code + type: Web + title: Install Flutter using VS Code + date: 2025-10-28 + language: en + publisher: Flutter (ภายใต้ CC BY 3.0 และ BSD License) + url: + date: 2025-12-6 + value: https://docs.flutter.dev/install/with-vs-code flLinuxDev: - type: Web - title: Set up Linux development - date: 2025-9-25 - language: en - publisher: Flutter (ภายใต้ CC BY 3.0 และ BSD License) - url: - date: 2025-12-6 - value: https://docs.flutter.dev/platform-integration/linux/setup + type: Web + title: Set up Linux development + date: 2025-9-25 + language: en + publisher: Flutter (ภายใต้ CC BY 3.0 และ BSD License) + url: + date: 2025-12-6 + value: https://docs.flutter.dev/platform-integration/linux/setup flLinuxBuild: - type: Web - title: Build Linux apps with Flutter - date: 2025-9-5 - language: en - publisher: Flutter (ภายใต้ CC BY 3.0 และ BSD License) - url: - date: 2025-12-6 - value: https://docs.flutter.dev/platform-integration/linux/building + type: Web + title: Build Linux apps with Flutter + date: 2025-9-5 + language: en + publisher: Flutter (ภายใต้ CC BY 3.0 และ BSD License) + url: + date: 2025-12-6 + value: https://docs.flutter.dev/platform-integration/linux/building archPkgs: - type: Web - title: Arch Linux - Package Search - date: 2025-12-15 - language: en - url: + type: Web + title: Arch Linux - Package Search date: 2025-12-15 - value: https://archlinux.org/packages/ + language: en + url: + date: 2025-12-15 + value: https://archlinux.org/packages/ fedoraPkgs: - type: Web - title: Fedora Packages - date: 2025-12-15 - language: en - url: + type: Web + title: Fedora Packages date: 2025-12-15 - value: https://packages.fedoraproject.org/ + language: en + url: + date: 2025-12-15 + value: https://packages.fedoraproject.org/ debianPkgs: - type: Web - title: Debian -- Packages - date: 2025-12-15 - language: en - url: + type: Web + title: Debian -- Packages date: 2025-12-15 - value: https://www.debian.org/distrib/packages + language: en + url: + date: 2025-12-15 + value: https://www.debian.org/distrib/packages gitWindows: - type: Web - title: Git - Install for Windows - date: 2025-11-30 - language: en - url: - date: 2025-12-1 - value: https://git-scm.com/install/windows + type: Web + title: Git - Install for Windows + date: 2025-11-30 + language: en + url: + date: 2025-12-1 + value: https://git-scm.com/install/windows twMaterialYou: - type: Post - title: "The latest in Material Design is NOW available" - date: 2021-10-28 - author: Material Design [@materialdesign] - url: - date: 2025-12-1 - value: https://x.com/materialdesign/status/1453409331697885192 + type: Post + title: "The latest in Material Design is NOW available" + date: 2021-10-28 + author: Material Design [@materialdesign] + url: + date: 2025-12-1 + value: https://x.com/materialdesign/status/1453409331697885192 cpprefCBasics: - type: Web - title: Declarations - date: 2025-02-09 - publisher: cppreference.com (ภายใต้ CC BY-SA 3.0 Unported) - url: - date: 2025-12-08 - value: https://cppreference.com/w/c/language/declarations.html + type: Web + title: Declarations + date: 2025-02-09 + publisher: cppreference.com (ภายใต้ CC BY-SA 3.0 Unported) + url: + date: 2025-12-08 + value: https://cppreference.com/w/c/language/declarations.html cpprefATypes: - type: Web - title: Arithmetic types - date: 2025-02-09 - publisher: cppreference.com (ภายใต้ CC BY-SA 3.0 Unported) - url: - date: 2025-12-09 - value: https://cppreference.com/w/c/language/arithmetic_types.html + type: Web + title: Arithmetic types + date: 2025-02-09 + publisher: cppreference.com (ภายใต้ CC BY-SA 3.0 Unported) + url: + date: 2025-12-09 + value: https://cppreference.com/w/c/language/arithmetic_types.html ansiC: - type: Book - title: The ANSI C Programming Language - page-total: 238 - author: - - Brian W. Kernighan - - Dennis M. Ritchie - publisher: Prentice Hall - language: en - edition: ฉบับที่สอง (Second edition) - date: 1988 - url: https://archive.org/details/the-ansi-c-programming-language-by-brian-w.-kernighan-dennis-m.-ritchie.org + type: Book + title: The ANSI C Programming Language + page-total: 238 + author: + - Brian W. Kernighan + - Dennis M. Ritchie + publisher: Prentice Hall + language: en + edition: ฉบับที่สอง (Second edition) + date: 1988 + serial-number: + isbn: 0131101633 + url: https://archive.org/details/the-ansi-c-programming-language-by-brian-w.-kernighan-dennis-m.-ritchie.org ghEsp32Partition: - type: Web - title: default.csv - date: 2024-4-15 - url: - date: 2025-12-9 - value: https://github.com/espressif/arduino-esp32/blob/2ede5ac10923afd1e3a42ce1fb41930a9de05d16/tools/partitions/default.csv + type: Web + title: default.csv + date: 2024-4-15 + url: + date: 2025-12-9 + value: https://github.com/espressif/arduino-esp32/blob/2ede5ac10923afd1e3a42ce1fb41930a9de05d16/tools/partitions/default.csv pioPython: - type: Web - title: Install Python Interpreter - date: 2024-9-14 - publisher: PlatformIO Docs (ภายใต้ Apache License 2.0) - url: - date: 2025-12-15 - value: https://docs.platformio.org/en/latest/faq/install-python.html + type: Web + title: Install Python Interpreter + date: 2024-9-14 + publisher: PlatformIO Docs (ภายใต้ Apache License 2.0) + url: + date: 2025-12-15 + value: https://docs.platformio.org/en/latest/faq/install-python.html pioSysReq: - type: Web - date: 2022-5-30 - title: System Requirements - publisher: PlatformIO Docs (ภายใต้ Apache License 2.0) - url: - date: 2025-12-15 - value: https://docs.platformio.org/en/latest/core/installation/requirements.html + type: Web + date: 2022-5-30 + title: System Requirements + publisher: PlatformIO Docs (ภายใต้ Apache License 2.0) + url: + date: 2025-12-15 + value: https://docs.platformio.org/en/latest/core/installation/requirements.html pioInsScript: - type: Web - date: 2023-8-14 - title: Installer Script (Recommended) - publisher: PlatformIO Docs (ภายใต้ Apache License 2.0) - url: - date: 2025-12-15 - value: https://docs.platformio.org/en/latest/core/installation/methods/installer-script.html - + type: Web + date: 2023-8-14 + title: Installer Script (Recommended) + publisher: PlatformIO Docs (ภายใต้ Apache License 2.0) + url: + date: 2025-12-15 + value: https://docs.platformio.org/en/latest/core/installation/methods/installer-script.html diff --git a/main.typ b/main.typ index 06ae6b0..1972189 100644 --- a/main.typ +++ b/main.typ @@ -52,7 +52,12 @@ #include "Chapter4.typ" #include "Chapter5.typ" -#bibliography("References.yaml", title: "บรรณานุกรม", full: true, style: "chicago-author-date") +#bibliography( + "References.yaml", + title: "บรรณานุกรม", + full: true, + style: "nong-khai-technical-college-project-guidelines.csl", +) #pagebreak() @@ -61,16 +66,24 @@ #let image-credits() = context { let figures = query(figure.where(kind: "i-figured-image")) let attributions = query() + let offset = 0 for (idx, f) in figures.enumerate() { if (idx >= attributions.len()) { return } + if attributions.at(idx).body.body == [] { + offset += 1 + + if attributions.at(idx + offset).body.body == [] { + continue + } + } + f.caption - // f.counter.display(f.numbering) h(2.2em) - attributions.at(idx).body.body + attributions.at(idx + offset).body.body } } diff --git a/nong-khai-technical-college-project-guidelines.csl b/nong-khai-technical-college-project-guidelines.csl new file mode 100644 index 0000000..364c114 --- /dev/null +++ b/nong-khai-technical-college-project-guidelines.csl @@ -0,0 +1,406 @@ + + -- 2.54.0 From 1c627501dd3bfb6d545baaf7a7f3b0e5060df005 Mon Sep 17 00:00:00 2001 From: Satakun Utama Date: Wed, 31 Dec 2025 21:54:51 +0700 Subject: [PATCH 2/4] Finish Flutter heading --- Chapter2/Buzzer.typ | 20 +- Chapter2/CLanguage.typ | 105 +-- Chapter2/Chapter2.typ | 12 +- Chapter2/Flutter.typ | 845 ++++-------------- Chapter2/Flutter/app-anatomy.svg | 72 ++ Chapter2/Flutter/archdiagram.png | Bin 0 -> 109102 bytes Chapter2/Flutter/color-picker.png | Bin 0 -> 103138 bytes Chapter2/Flutter/desktopExampleApp.png | Bin 37840 -> 0 bytes Chapter2/Flutter/mobileExampleApp.png | Bin 63062 -> 0 bytes Chapter2/Flutter/newProjectPage.png | Bin 64692 -> 0 bytes Chapter2/Git.typ | 16 +- Chapter2/HTTPS.typ | 30 +- Chapter2/Intro.typ | 41 +- Chapter2/Microcontroller.typ | 262 +++++- .../Microcomputer_with_EPROM_(piggyback).jpg | Bin 0 -> 1384342 bytes Chapter2/Microcontroller/PIC16CxxxWIN.jpg | Bin 0 -> 1049719 bytes Chapter2/NFC.typ | 96 +- Chapter2/OpenSSL.typ | 29 +- Chapter2/PIR.typ | 36 +- Chapter2/Sensors.typ | 38 +- Chapter2/TLS.typ | 124 ++- Chapter2/X509.typ | 22 +- Chapter2/x690.typ | 24 +- Chapter3.typ | 436 --------- Chapter3/Chapter3.typ | 695 ++++++++++++++ PageTemplate.typ | 30 +- References.yaml | 32 +- main.typ | 14 +- 28 files changed, 1577 insertions(+), 1402 deletions(-) create mode 100644 Chapter2/Flutter/app-anatomy.svg create mode 100644 Chapter2/Flutter/archdiagram.png create mode 100644 Chapter2/Flutter/color-picker.png delete mode 100644 Chapter2/Flutter/desktopExampleApp.png delete mode 100644 Chapter2/Flutter/mobileExampleApp.png delete mode 100644 Chapter2/Flutter/newProjectPage.png create mode 100644 Chapter2/Microcontroller/Microcomputer_with_EPROM_(piggyback).jpg create mode 100644 Chapter2/Microcontroller/PIC16CxxxWIN.jpg delete mode 100644 Chapter3.typ create mode 100644 Chapter3/Chapter3.typ diff --git a/Chapter2/Buzzer.typ b/Chapter2/Buzzer.typ index 0be87a7..fae309d 100644 --- a/Chapter2/Buzzer.typ +++ b/Chapter2/Buzzer.typ @@ -1,20 +1,30 @@ -#import "../PageTemplate.typ": i +#import "../PageTemplate.typ": * = ลำโพงสัญญาณ (Buzzer) -#i Buzzer เป็นอุปกรณ์ส่งสัญญาณเสียงซึ่งอาจเป็น อุปกรณ์ เชิงกลเครื่องกลไฟฟ้าหรือเพียโซอิเล็กทริก (เรียกสั้น ๆ ว่าเพียโซ) การใช้งานทั่วไปของบัซเซอร์และบี๊บเปอร์ ได้แก่อุปกรณ์แจ้งเตือนตัวตั้งเวลาวงจรและการยืนยันการป้อนข้อมูลของผู้ใช้ เช่น การคลิกเมาส์หรือการกดแป้นพิมพ์ +#i Buzzer เป็นอุปกรณ์ส่งสัญญาณเสียงซึ่งอาจเป็น อุปกรณ์ เชิงกลเครื่องกลไฟฟ้าหรือเพียโซอิเล็กทริก +(เรียกสั้น ๆ ว่าเพียโซ) การใช้งานทั่วไปของบัซเซอร์และบี๊บเปอร์ +ได้แก่อุปกรณ์แจ้งเตือนตัวตั้งเวลาวงจรและการยืนยันการป้อนข้อมูลของผู้ใช้ เช่น +การคลิกเมาส์หรือการกดแป้นพิมพ์ ประเภทของ Buzzer มี 3 ชนิด คือ == ไฟฟ้าเชิงกล (Electromechanical) -#i อุปกรณ์ในยุคแรกๆ ใช้ระบบไฟฟ้าเครื่องกลแบบเดียวกับกระดิ่งไฟฟ้าโดยไม่มีฆ้องโลหะ ในทำนองเดียวกันรีเลย์อาจเชื่อมต่อเพื่อตัดกระแสไฟฟ้า ที่ทำหน้าที่สั่งการตัวเอง ซึ่งทำให้หน้าสัมผัสส่งเสียงหึ่งๆ (หน้าสัมผัสจะส่งเสียงหึ่งๆ ที่ความถี่สายหากใช้ไฟฟ้ากระแสสลับ) บ่อยครั้งที่อุปกรณ์เหล่านี้ถูกยึดไว้กับผนังหรือเพดานเพื่อใช้เป็นแผงเก็บเสียง คำว่า "buzzer" มาจากเสียงแหบๆ ของ buzzer ระบบไฟฟ้าเครื่องกล +#iii อุปกรณ์ในยุคแรกๆ ใช้ระบบไฟฟ้าเครื่องกลแบบเดียวกับกระดิ่งไฟฟ้าโดยไม่มีฆ้องโลหะ +ในทำนองเดียวกันรีเลย์อาจเชื่อมต่อเพื่อตัดกระแสไฟฟ้า ที่ทำหน้าที่สั่งการตัวเอง +ซึ่งทำให้หน้าสัมผัสส่งเสียงหึ่งๆ (หน้าสัมผัสจะส่งเสียงหึ่งๆ ที่ความถี่สายหากใช้ไฟฟ้ากระแสสลับ) +บ่อยครั้งที่อุปกรณ์เหล่านี้ถูกยึดไว้กับผนังหรือเพดานเพื่อใช้เป็นแผงเก็บเสียง คำว่า "buzzer" +มาจากเสียงแหบๆ ของ buzzer ระบบไฟฟ้าเครื่องกล == กลไก (Mechanical) -#i จอยบัซเซอร์เป็นตัวอย่างของบัซเซอร์แบบกลไกล้วนๆ และจำเป็นต้องมีไดรเวอร์ ตัวอย่างอื่นๆ ของบัซเซอร์ประเภทนี้คือกริ่งประตู +#iii จอยบัซเซอร์เป็นตัวอย่างของบัซเซอร์แบบกลไกล้วนๆ และจำเป็นต้องมีไดรเวอร์ ตัวอย่างอื่นๆ +ของบัซเซอร์ประเภทนี้คือกริ่งประตู == เพียโซอิเล็กทริก (Piezoelectric) -#i องค์ประกอบเพียโซอิเล็กทริกอาจถูกขับเคลื่อนด้วย วงจรอิเล็กทรอนิกส์ แบบสั่นหรือ แหล่ง สัญญาณเสียง อื่นๆ ซึ่งขับเคลื่อนด้วยเครื่องขยายเสียงเพียโซอิเล็กทริกเสียงที่มักใช้เพื่อระบุว่ามีการกดปุ่ม ได้แก่ เสียงคลิก เสียงกริ่ง หรือเสียงบี๊บ +#iii องค์ประกอบเพียโซอิเล็กทริกอาจถูกขับเคลื่อนด้วย วงจรอิเล็กทรอนิกส์ แบบสั่นหรือ แหล่ง สัญญาณเสียง +อื่นๆ ซึ่งขับเคลื่อนด้วยเครื่องขยายเสียงเพียโซอิเล็กทริกเสียงที่มักใช้เพื่อระบุว่ามีการกดปุ่ม ได้แก่ เสียงคลิก +เสียงกริ่ง หรือเสียงบี๊บ diff --git a/Chapter2/CLanguage.typ b/Chapter2/CLanguage.typ index d63608d..c749ef8 100644 --- a/Chapter2/CLanguage.typ +++ b/Chapter2/CLanguage.typ @@ -24,108 +24,31 @@ Boehm garbage collector ตามลำดับ #i ตั้งแต่ปี 2000 เป็นต้นมาภาษาซี ได้รับการจัดอันดับอย่างต่อเนื่องให้อยู่ในอันดับสี่ภาษาสูงสุดในดัชนี TIOBE ซึ่งเป็นการวัดความนิยมของภาษาการเขียนโปรแกรม -== ตัวอย่าง "hello, world" +== ประวัติ -#i โดยการเรียนภาษาเขียนโปรแกรมใหม่ ๆ ต้องเริ่มด้วยการเขียนโปรแกรมในภาษานั้น ๆ -โดยโปรแกรมแรกที่จะเขียนนั้นเหมือน ๆ กันในทุกภาษา คือการพิมพ์ "hello, world" +=== การพัฒนาช่วงแรก -```c -#include +==== ภาษา B -int main() -{ - printf("hello, world\n"); -} -``` +==== ภาษา B ใหม่และ C รุ่นแรก -#i คุณสามารถบันทึกไฟล์นี้เป็นไฟล์ที่มีส่วนขยายไฟล์ `.c` เช่น `hello.c` ได้เลย -แต่การจะรันโปรแกรมนี้นั้นขึ้นอยู่กับระบบปฏิบัติการของคุณ ตัวอย่างเช่นบนระบบที่มีชุดคอมไพเลอร์ GCC (หรือ -MinGW สำหรับเวอร์ชันบน Windows) ติดตั้งอยู่สามารถใช้คำสั่ง +==== โครงสร้างและการเขียน Unix kernel ใหม่ -```bash -cc hello.c -``` +=== K&R C -เพื่อคอมไพล์ไฟล์ได้ หากคุณไม่ได้ทำอะไรผิดพลาดไป เช่นการพิมพ์ตกหรือการสะกดผิด -การคอมไพล์จะดำเนินการไปอย่างเงียบ ๆ และสร้างไฟล์ไบนารีชื่อ `a.out` ออกมา -คุณสามารถรันไฟล์นั้นบนเทอร์มินัลของคุณได้โดยการพิมพ์ `./a.out` แล้วจึงจะได้ข้อความดังต่อไปนี้ออกมา +=== ANSI C และ ISO C -``` -hello, world -``` +=== C99 -#i โดยโปรแกรมภาษา C นั้น ไม่ว่าจะขนาดใด จะประกอบไปด้วยฟังก์ชันและตัวแปร -โดยฟังก์ชันจะประกอบไปด้วยสเตตเมนต์ (statements) ที่ระบุสิ่งที่โปรแกรมจะต้องกระทำ -และตัวแปรนั้นกำหนดค่าที่จะถูกใช้งานในการกระทำเหล่านั้น โดยในตัวอย่างมีฟังก์ชันชื่อ `main` -ซึ่งปกติแล้วคุณมีอิสระในการตั้งชื่อฟังก์ชันว่าอะไรก็ได้ แต่ฟังก์ชัน `main` นั้นพิเศษ -เพราะโปรแกรมของคุณนั้นมีจุดเริ่มต้นที่ `main` ดังนั้น โปรแกรมทุกโปรแกรมต้องมี `main` อยู่สักที่ +=== C11 -#i โดยปกติแล้วฟังก์ชัน `main` นั้นจะเรียกใช้ฟังก์ชันอื่น ๆ เพื่อทำงานให้มัน โดยอาจเป็นฟังก์ชันที่คุณเขียน -หรือฟังก์ชันที่มาจากไลบรารีที่คุณใช้งาน ในบรรทัดแรกของโปรแกรมตัวอย่าง +=== C17 -```c -#include -``` +=== C23 -มีหน้าที่ในการนำเข้าข้อมูลเกี่ยวกับไลบรารีอินพุต/เอาต์พุตมาตรฐาน โดยบรรทัดนี้นั้นอยู่ในไฟล์ ภาษา C -หลายไฟล์ เนื่องจากการแสดงผลข้อมูลนั้นเป็นการกระทำที่ถูกกระทำบ่อย +=== C2Y -#i หนึ่งในวิธีการโอนถ่ายข้อมูลระหว่างฟังก์ชันคือการมอบรายการของข้อมูลที่ต้องการมอบให้แก่ฟังก์ชัน -โดยค่าที่มอบให้ฟังก์ชันเหล่านั้นมีชื่อเรียกว่า อาร์กิวเมนต์ (arguments) -ซึ่งวงเล็บที่ตามหลังชื่อฟังก์ชันนั้นคือวงเล็บที่จะครอบรายการอาร์กิวเมนต์ โดยในตัวอย่างฟังก์ชัน `main` -นั้นไม่หวังค่าอาร์กิวเมนต์ใด ๆ สังเกตได้จาก `()` ที่เป็นรายการที่ว่างปล่าว - -#i สเตตเมนต์ที่อยู่ภายในฟังก์ชันนั้นจะถูกครอบด้วยวงเล็บปีกกา `{}` ซึ่งในฟังก์ชัน `main` มีแค่ 1 -สเตตเมนต์ คือ - -```c -printf("hello, world\n"); -``` - -#i โดยฟังก์ชันนั้นจะถูกเรียกใช้ได้โดยการเรียกชื่อมัน ตามด้วยรายการอาร์กิวเมนต์ที่ถูกครอบด้วยวงเล็บ -ดังนั้น สเตตเมนต์นี้จึงมีการเรียกใช้ฟังก์ชัน `printf` ด้วยอาร์กิวเมนต์ `"hello, world\n"` โดยที่ -`printf` เป็นฟังก์ชันจากไลบรารีที่ทำการพรินต์ข้อมูล -(ซึ่งการพรินต์ในที่นี้คือการแสดงผลข้อความบนหน้าจอในเทอร์มินัล) -และข้อมูลที่มันแสดงนั้นก็คือรายการอักขระที่ถูกครอบอยู่ด้วยเครื่องหมายอัญประกาศนั่นเอง - -#i รายการอักขระที่ถูกครอบด้วยเครื่องหมายอัญประกาศ เช่น `"hello, world\n"` นั้นมีชื่อเรียกว่า -character string หรือ string constant และในตัวอย่างนี้นั้น -เราจะมีการใช้รายการอักขระนี้เป็นเพียงแค่อาร์กิวเมนต์ของ `printf` และฟังก์ชันอื่น ๆ - -#i ลำดับตัวอักษร `\n` ในสตริงคือสัญกรณ์ภาษา C สำหรับ#emph[ตัวอักษรบรรทัดใหม่] -ซึ่งเมื่อถูกพรินต์แล้วจะให้เอาต์พุตไปอยู่ทางด้านซ้ายของบรรทัดใหม่ โดยหากไม่ใส่ `\n` -(ซึ่งคุณสามารถทดลองได้เลย) คุณจะพบว่าไม่มีการขึ้นบรรทัดใหม่ของข้อความ และคุณต้องใช้ `\n` -ในการขึ้นบรรทัดใหม่ และหากคุณลองทำแบบนี้: - -```c -printf("Hello, world -"); -``` - -คอมไพเลอร์ภาษา C นั้นจะแสดงข้อความแสดงข้อผิดพลาดขึ้นมา - -#i `printf` นั้นจะไม่มีทางใส่ตัวอักษรขึ้นบรรทัดใหม่ให้โดยอัตโนมัติ ดังนั้นคุณสามารถเรียกใช้ฟังก์ชันหลาย -ๆ ครั้งเพื่อค่อย ๆ สร้างเอาต์พุตออกมาได้ โดยที่โปรแกรมแรกของเราจะสามารถเขียนแบบนี้ได้ - -```c -#include - -int main() -{ - printf("hello, "); - printf("world"); - printf("\n"); -} -``` - -แล้วข้อความที่แสดงออกมาจะยังคงเดิม - -#i คุณสามารถสังเกตได้ว่า `\n` นั้นจะแทนตัวอักษรตัวเดียว โดยสัญกรณ์ _escape sequence_ เช่น -`\n` คือรูปแบบในการเขียนตัวอักษรที่อาจพิมพ์ได้ยากหรือตัวอักษรล่องหน โดยสัญกรณ์อื่น ๆ -ในประเภทเดียวกันมีตัวอย่างเช่น `\t` สำหรับตัวอักษรแท็บ, `\b` สำหรับ backspace, `\"` -สำหรับการพิมพ์สัญลักษณ์อัญประกาศ -(ไม่เช่นนั้นตัวอักษรอัญประกาศจะถูกถือว่าเป็นตัวอักษรในการเริ่มต้น/สิ้นสุดของสตริง), และ `\\` -สำหรับการพิมพ์ตัวอักษร backslash เอง +=== Embedded C == ตัวแปร (Variables) @@ -195,8 +118,6 @@ float a, b, c; #show table.cell.where(y: 1): strong #show table.cell: set par(justify: false, leading: 0.5em) -#show figure: i-figured.show-figure.with(level: 4) - #figure( table( columns: 7, diff --git a/Chapter2/Chapter2.typ b/Chapter2/Chapter2.typ index 5540bf3..fc7d729 100644 --- a/Chapter2/Chapter2.typ +++ b/Chapter2/Chapter2.typ @@ -2,14 +2,6 @@ #import "@preview/i-figured:0.2.4" // #show: page-theme -#show heading: i-figured.reset-counters.with(level: 3) -#show figure: i-figured.show-figure.with(level: 3) - -#show raw.where(block: false): set text(font: "Laksaman") -#show raw.where(block: true): set block(fill: rgb("#282A36"), inset: 1.5em) -#show raw.where(block: true): set raw(theme: "../Dracula.tmTheme") -#show raw.where(block: true): set text(fill: rgb("#F8F8F2")) - #set heading(numbering: "บทที่ 1") #include "Intro.typ" @@ -19,11 +11,9 @@ #include "Buzzer.typ" #include "HTTP.typ" #include "HTTPS.typ" +#pagebreak() #include "TLS.typ" #include "NFC.typ" - -#pagebreak() - #include "Flutter.typ" #include "Git.typ" #include "CLanguage.typ" diff --git a/Chapter2/Flutter.typ b/Chapter2/Flutter.typ index 73db069..dc70cbb 100644 --- a/Chapter2/Flutter.typ +++ b/Chapter2/Flutter.typ @@ -2,6 +2,7 @@ #import "@preview/treet:1.0.0": * #import "@preview/tiaoma:0.3.0" #set heading(numbering: "1.1", offset: 1) +#set figure(kind: image) = Flutter @@ -19,731 +20,225 @@ Alibaba ช่วยลดความยุ่งยากในการรองรับหลายแพลตฟอร์ม เนื่องจากสามารถใช้โคด UI ที่เหมือนกันได้กับทุกแพลตฟอร์มเป้าหมาย -== การติดตั้งโปรแกรมเขียนโคด - -#i จริง ๆ แล้วนั้น Flutter สามารถทำงานกับโปรแกรมเขียนโคดใดก็ได้ -แต่มีโปรแกรมเหล่านี้ที่อาจมีประสบการณ์การพัฒนาที่ดีกว่าโปรแกรมอื่น: - -- Visual Studio Code (VS Code) -- Android Studio -- JetBrains IntelliJ -- Firebase Studio - -#i โครงงานนี้ใช้โปรแกรมเขียนโคด Android Studio เป็นหลักเนื่องจากแอพลิเคชันโครงงานมี Android -เป็นเป้าหมายหลัก และ Android SDK สามารถจัดการได้ง่ายกว่าใน Android Studio - -=== Android Studio - -#i Android Studio สามารถดาวน์โหลดได้ผ่าน https://developer.android.com/studio -หรือสามารถถูกติดตั้งและจัดการผ่านแอพลิเคชัน JetBrains Toolbox ได้เช่นกัน -(https://www.jetbrains.com/toolbox-app/) - -== การติดตั้ง Flutter - -#i การติดตั้ง Flutter สามารถทำได้สองวิธีด้วยกัน คือการติดตั้งผ่าน Visual Studio Code (VS -Code) และการติดตั้งด้วยตนเอง โดยหากต้องการใช้ VS Code เป็นโปรแกรมเขียนโคดอยู่แล้ว -สามารถติดตั้งผ่าน VS Code ได้เลย - -#i แต่ก่อนอื่น ต้องทำการติดตั้งโปรแกรมและไลบรารีพื้นฐานที่จำเป็นสำหรับ Flutter ก่อน - -=== การติดตั้งโปรแกรมและไลบรารีที่จำเป็น - -==== Windows - -#grid( - columns: 2, - column-gutter: 1em, - [#i ในการพัฒนาซอฟต์แวร์บน Windows ด้วย Flutter คุณจำเป็นต้องติดตั้ง Git สำหรับ Windows - ซึ่งคุณสามารถดูขั้นตอนการติดตั้งได้โดยการสแกน QR code ด้านข้าง หรือที่ - https://git-scm.com/install/windows หรือเพียงแค่ใช้ WinGet - ในการติดตั้งโดยการใช้คำสั่งด้านล่าง], - tiaoma.qrcode( - "https://git-scm.com/install/windows", - width: 1in, - alt: "QR โคดสำหรับหน้าการติดตั้ง Git สำหรับ Windows", - ), -) - -```sh -winget install --id Git.Git -e --source winget -``` - -==== Linux - -#i Flutter ใช้ไลบรารีดังต่อไปนี้ในขั้นตอนการพัฒนาแอพลิเคชันบน Linux (development -dependencies; ยังไม่รวมไลบรารีและโปรแกรมที่ต้องมีในการสร้างแอพลิเคชัน _สำหรับ_ Linux) - -#grid( - columns: 2, - column-gutter: 2in, - [ - - curl - - git - - unzip - ], - [ - - xz - - zip - - glu - ], -) - -หากต้องการคำสั่งในการติดตั้งแพคเกจเหล่านี้ โปรดดู@flLinuxDetails - -==== macOS - -#i จำเป็นต้องทำการติดตั้งเครื่องมือ command-line Xcode เพื่อเข้าถึงเครื่องมือที่ Flutter -จำเป็นต้องใช้ รวมถึง Git - -ในการดาวน์โหลดเครื่องมือ ใช้คำสั่งต่อไปนี้ในเทอร์มินัลที่คุณเลือก: - -```sh -xcode-select --install -``` - -#i หากคุณไม่ได้ติดตั้งเครื่องมืออยู่แล้ว จะมีไดอะลอกเพื่อคอนเฟิร์มว่าคุณต้องการที่จะติดตั้งมัน กด -*Install* และกด *Done* เมื่อทำการติดตั้งเสร็จสิ้นแล้ว - -#pagebreak() - -=== การติดตั้งผ่าน Visual Studio Code - -#[ - #set enum(indent: 3em) - + เปิด VSCode - + ติดตั้งส่วนขยาย Flutter \ - อยู่ภายใต้ ID `Dart-Code.flutter` ทั้งบน Visual Studio Marketplace และ OpenVSX - + ติดตั้ง Flutter ด้วย VS Code - + เปิด Command Palette ด้วยเมนู *View* > *Command Palette* หรือกด Ctrl + Shift + - P - + ใน Command Palette พิมพ์ `flutter`. - + เลือก *Flutter: New Project* - + VS Code จะให้คุณเลือก Flutter SDK บนคอมพิวเตอร์ของคุณ เลือก *Download SDK* - + เมือหน้าไดอะลอก *Select Folder for Flutter SDK* แสดงขึ้น เลือกสถานที่ที่คุณอยากติดตั้ง - Flutter - + คลิก *Clone Flutter* \ - ในระหว่างการดาวน์โหลด VS Code จะแสดงการแจ้งเตือนนี้: - ``` - Downloading the Flutter SDK. This may take a few minutes. - ``` - การดาวน์โหลดนี้จะใช้เวลาสองสามนาที หากคุณเชื่อว่าการดาวน์โหลดหยุดชะงัก คุณสามารถคลิก - *Cancel* แล้วเริ่มต้นการติดตั้งใหม่ได้ - + คลิก *Add SDK to PATH* \ - เมื่อเสร็จสิ้น จะมีการแจ้งเตือน - ``` - The Flutter SDK was added to your PATH - ``` - + VS Code อาจแสดงการแจ้งเตือนเกี่ยวกับการเก็บข้อมูลของ Google หากคุณยินยอม คลิก *OK* - + เพื่อความแน่ใจ กรุณาปิดเทอร์มินัลทุกหน้าต่างหรือรีสตาร์ท VS Code เพื่อให้แน่ใจว่า Flutter - จะสามารถใช้ผ่านเทอร์มินัลได้ - + เมื่อเสร็จสิ้น ใช้คำสั่ง `flutter doctor -v` ในเทอร์มินัลที่คุณเลือกเพื่อตรวจสอบการติดตั้ง - Flutter ของคุณ \ - หากคำสั่งไม่เจอหรือเกิดข้อผิดพลาดขึ้น ตรวจสอบ - https://docs.flutter.dev/install/troubleshoot สำหรับข้อมูลเพิ่มเติม -] - -=== การติดตั้งด้วยตนเอง - -#i แนะนำให้ทำตาม https://docs.flutter.dev/install/manual#install-flutter -เนื่องจากกระบวนการนี้ต้องใช้ข้อมูลที่ใหม่ล่าสุด - -1. ดาวน์โหลด Flutter (สามารถหาปุ่มดาวน์โหลดได้จากลิงก์ด้านบน) -2. สร้างโฟลเดอร์สำหรับเก็บ Flutter SDK -3. ทำการแตกไฟล์ที่ดาวน์โหลดมา -4. เพิ่ม Flutter เข้าไปยัง PATH ของคุณ (วิธีการขึ้นอยู่กับระบบปฏิบัติการ) -5. ยืนยันความถูกต้องของการติดตั้งของคุณด้วยคำสั่ง `flutter doctor -v` - == Dart -#i Dart เป็นภาษาโปรแกรมที่ออกแบบโดย Lars Bak และ Kasper Lund และพัฒนาโดย Google +#iii Dart เป็นภาษาโปรแกรมที่ออกแบบโดย Lars Bak และ Kasper Lund และพัฒนาโดย Google สามารถใช้พัฒนาแอปพลิเคชันบนเว็บ มือถือ เซิร์ฟเวอร์ และเดสก์ท็อปได้ -และยังเป็นภาษาหลักที่ใช้ในการพัฒนาแอพลิเคชัน Flutter +และยังเป็นภาษาหลักที่ใช้ในการพัฒนาแอปพลิเคชัน Flutter -#i Dart เป็นภาษาเชิงวัตถุ อิงคลาส และรวบรวมขยะ (garbage-collection) ด้วยไวยากรณ์แบบ C +#iii Dart เป็นภาษาเชิงวัตถุ อิงคลาส และรวบรวมขยะ (garbage-collection) ด้วยไวยากรณ์แบบ C สามารถคอมไพล์เป็นโค้ดเครื่อง JavaScript หรือ WebAssembly ได้ รองรับอินเทอร์เฟซ มิกซ์อิน คลาสนามธรรม เจเนอริกแบบรีไฟด์ และการอนุมานชนิดข้อมูล -== การสร้างโปรเจกต์ +#pagebreak() -#i ตั้งแต่หัวข้อนี้เป็นต้นไป จะเป็นข้อมูลสำหรับการทำงานกับ Android Studio -เป็นหลักเนื่องจากเป็นโปรแกรมหลักที่ถูกใช้งานในการพัฒนาแอพลิเคชันโครงงานนี้ +== สถาปัตยกรรม -#i หากยังไม่ได้ติดตั้งปลั๊กอิน Flutter โปรดติดตั้งปลั๊กอินก่อน โดยหากอยู่ในหน้าต้อนรับ -สามารถติดตั้งปลั๊กอินได้โดยการเข้าไปยังแท็บ *Plugins* หรือหากเปิดโปรเจกต์อื่นอยู่ -สามารถเข้าถึงหน้าปลั๊กอินได้โดยการกดที่ไอคอนฟันเฟืองในแถบเครื่องมือ แล้วกด *Plugins...* -หลังจากนั้น ในแท็บ *Marketplace* ของหน้าปลั๊กอิน ค้นหา *Flutter* (ผู้ผลิตปลั๊กอินคือ Google) -แล้วกด *Install* +#iii Flutter ถูกออกแบบมาให้เป็นระบบแบบเลเยอร์ที่ต่อขยายได้ +ประกอบด้วยไลบรารีอิสระหลายชุดที่แต่ละชุดพึ่งพาเลเยอร์ที่อยู่ด้านล่าง +ไม่มีเลเยอร์ใดที่มีสิทธิ์พิเศษในการเข้าถึงเลเยอร์ด้านล่าง +และทุกส่วนของเฟรมเวิร์กถูกออกแบบมาให้เป็นตัวเลือกและสามารถทดแทนได้ #afigure( - image("Flutter/homePage.png", width: 65%), - alt: "หน้ายินดีต้อนรับในแท็บ Projects ที่กำลังแสดงรายการโปรเจกต์และปุ่มในการสร้างโปรเจกต์ใหม่", - caption: [หน้ายินดีต้อนรับใน Android Studio], + image("Flutter/archdiagram.png", width: 80%), + attr: [Flutter, ภายใต้ CC BY 4.0], + alt: "แผนผังสถาปัตยกรรม Flutter", + caption: [สถาปัตยกรรม Flutter], ) -#i เมื่อคลิก *New Flutter Project* จะมีหน้าถามสถานที่ติดตั้ง Flutter SDK หลังจากนั้น กด -*Next* แล้วจะมีหน้าต่อไปนี้ขึ้นมาเพื่อให้คุณกรอกรายละเอียดโปรเจกต์ +#iii สำหรับระบบปฏิบัติการที่อยู่ภายใต้ แอปพลิเคชัน Flutter +จะถูกบรรจุในลักษณะเดียวกับแอปพลิเคชันเนทีฟอื่น ๆ โดยตัวฝังตัว (Embedder) +เฉพาะแพลตฟอร์มจะทำหน้าที่เป็นจุดเริ่มต้น ประสานงานกับระบบปฏิบัติการที่อยู่ภายใต้เพื่อเข้าถึงบริการต่างๆ +เช่น พื้นผิวการแสดงผล การเข้าถึง และการป้อนข้อมูล และจัดการลูปเหตุการณ์ข้อความ +ตัวฝังตัวเขียนด้วยภาษาที่เหมาะสมกับแพลตฟอร์ม ปัจจุบันคือ Java และ C++ สำหรับ Android, Swift +และ Objective-C/Objective-C++ สำหรับ#jb iOS และ macOS และ C++ สำหรับ Windows และ +Linux การใช้ตัวฝังตัว โค้ด Flutter สามารถรวมเข้ากับแอปพลิเคชันที่มีอยู่แล้วในรูปแบบโมดูล +หรือโค้ดอาจเป็นเนื้อหาทั้งหมดของแอปพลิเคชันก็ได้ Flutter +มีตัวฝังตัวจำนวนมากสำหรับแพลตฟอร์มเป้าหมายทั่วไป แต่ก็ยังมีตัวฝังตัวอื่นๆ อีกด้วย -#afigure( - image("Flutter/newProjectPage.png", width: 80%), - alt: "หน้ากรอกรายละเอียดโปรเจกต์ใหม่", - caption: [หน้าโปรเจกต์ใหม่], -) +#iii หัวใจหลักของ Flutter คือ Flutter engine ซึ่งส่วนใหญ่เขียนด้วยภาษา C++ +และรองรับฟังก์ชันพื้นฐานที่จำเป็นต่อการทำงานของแอปพลิเคชัน Flutter ทั้งหมด +เอนจินนี้มีหน้าที่ในการแปลงฉากที่ประกอบขึ้นเป็นภาพแรสเตอร์ทุกครั้งที่จำเป็นต้องวาดเฟรมใหม่ +มันมีหน้าที่ให้การใช้งานระดับต่ำของ API หลักของ Flutter รวมถึงการจัดวางข้อความกราฟิก +การรับส่งข้อมูลไฟล์และเครือข่าย รันไทม์ Dart และเครื่องมือคอมไพล์ -รายละเอียดที่จำเป็นต้องกรอกในการสร้างโปรเจกต์ใหม่มีดังนี้: +#iii เอนจินนี้ถูกเปิดเผยสู่เฟรมเวิร์ก Flutter ผ่านทาง dart:ui ซึ่งห่อหุ้มโค้ด C++ +ที่อยู่เบื้องหลังด้วยคลาส Dart ไลบรารีนี้เปิดเผยส่วนประกอบพื้นฐานระดับต่ำสุด เช่น +คลาสสำหรับควบคุมระบบย่อยการรับข้อมูล กราฟิก และการแสดงผลข้อความ -- *Project name:* ชื่อโปรเจกต์ -- *Project location:* โฟลเดอร์ที่ต้องการเก็บโปรเจกต์ -- *Description:* รายละเอียดโปรเจกต์ -- *Project type:* ประเภทโปรเจกต์ ในกรณีนี้เป็นค่า *Application* - เนื่องจากเราต้องการสร้างแอพลิเคชัน -- *Organization:* โดเมนเนมย้อนหลังขององค์กรที่พัฒนา (Reverse domain name notation; - Reverse-DNS) -- *Android language:* เลือกระหว่าง Java และ Kotlin เป็นภาษาหลักที่ใช้ในแอพลิเคชัน Android -- *Platforms:* แพลตฟอร์มที่โปรเจกต์จะรองรับ อย่างไรก็ตาม - การสร้างไฟล์ไบนารีสำหรับแอพลิเคชันขึ้นอยู่กับแพลตฟอร์มที่พัฒนาแอพลิเคชันเช่นกัน หมายความว่า - ถึงแม้ตามทฤษฎีแล้วแอพลิเคชันของคุณจะรองรับ iOS คุณต้องมีอุปกรณ์ Mac ในการสร้างไฟล์แอพลิเคชัน - iOS ออกมา +#iii โดยทั่วไป นักพัฒนาจะโต้ตอบกับ Flutter ผ่านเฟรมเวิร์ก Flutter +ซึ่งเป็นเฟรมเวิร์กที่ทันสมัยและตอบสนองต่อสิ่งรอบข้าง เขียนด้วยภาษา Dart +เฟรมเวิร์กนี้ประกอบด้วยชุดไลบรารีแพลตฟอร์ม เลย์เอาต์ และพื้นฐานที่ครบครัน +ซึ่งประกอบด้วยเลเยอร์หลายชั้น เริ่มจากล่างขึ้นบน ได้แก่: -เมื่อทำการใส่รายละเอียดทั้งหมดแล้ว สามารถกด Create เพื่อสร้างโปรเจกต์ได้เลย +- คลาสพื้นฐานและบริการส่วนประกอบต่างๆ เช่น แอนิเมชัน การวาดภาพ และท่าทางสัมผัส + ซึ่งนำเสนอนามธรรมที่ใช้กันทั่วไปเหนือพื้นฐานที่อยู่เบื้องหลัง + +- เลเยอร์การเรนเดอร์ให้นามธรรมสำหรับการจัดการเลย์เอาต์ ด้วยเลเยอร์นี้ + คุณสามารถสร้างโครงสร้างแบบต้นไม้ของวัตถุที่เรนเดอร์ได้ คุณสามารถจัดการวัตถุเหล่านี้แบบไดนามิก + โดยโครงสร้างแบบต้นไม้จะอัปเดตเลย์เอาต์โดยอัตโนมัติเพื่อสะท้อนการเปลี่ยนแปลงของคุณ + +- เลเยอร์วิดเจ็ตเป็นนามธรรมของการประกอบ + วัตถุเรนเดอร์แต่ละชิ้นในเลเยอร์การเรนเดอร์จะมีคลาสที่สอดคล้องกันในเลเยอร์วิดเจ็ต นอกจากนี้ + เลเยอร์วิดเจ็ตยังช่วยให้คุณกำหนดการรวมกันของคลาสที่คุณสามารถนำกลับมาใช้ใหม่ได้ + นี่คือเลเยอร์ที่แนะนำโมเดลการเขียนโปรแกรมแบบตอบสนอง + +- ไลบรารี Material และ Cupertino + นำเสนอชุดควบคุมที่ครอบคลุมซึ่งใช้ส่วนประกอบพื้นฐานของเลเยอร์วิดเจ็ตเพื่อนำภาษาการออกแบบ + Material หรือ iOS ไปใช้ + +#iii เฟรมเวิร์ก Flutter มีขนาดค่อนข้างเล็ก +ฟีเจอร์ระดับสูงหลายอย่างที่นักพัฒนาอาจใช้ถูกพัฒนาขึ้นมาในรูปแบบของแพ็กเกจ +รวมถึงปลั๊กอินของแต่ละแพลตฟอร์ม เช่น กล้องและเว็บวิว ตลอดจนฟีเจอร์ที่ไม่ขึ้นกับแพลตฟอร์ม เช่น +ตัวอักษร, HTTP และแอนิเมชัน ซึ่งสร้างขึ้นจากไลบรารีหลักของ Dart และ Flutter +แพ็กเกจบางส่วนมาจากระบบนิเวศที่กว้างกว่า ครอบคลุมบริการต่างๆ เช่น การชำระเงินภายในแอป +การตรวจสอบสิทธิ์ของ Apple และแอนิเมชัน #pagebreak() -== แอพลิเคชันตัวอย่าง +== โครงสร้างของแอปพลิเคชัน -เมื่อกดรันแอพลิเคชันด้วยไอคอน #box( - image("Flutter/vscode_play.svg", alt: "Play"), - baseline: 15%, -) (หรือ Shift+F10 ใน Android Studio) จะได้แอพลิเคชันดังรูปด้านล่างออกมา +#iii แผนภาพต่อไปนี้แสดงภาพรวมของส่วนประกอบต่างๆ ที่ประกอบกันเป็นแอป Flutter +ทั่วไปที่สร้างขึ้นโดยคำสั่ง `flutter create` แผนภาพนี้แสดงตำแหน่งของ Flutter Engine +ในโครงสร้างนี้ เน้นขอบเขตของ API และระบุที่เก็บโค้ด (repository) ที่ส่วนประกอบแต่ละส่วนอยู่ +คำอธิบายด้านล่างจะอธิบายคำศัพท์บางคำที่ใช้กันทั่วไปในการอธิบายส่วนประกอบของแอป Flutter #afigure( - grid( - columns: 2, - align: horizon, - image( - "Flutter/mobileExampleApp.png", - height: 2.5in, - alt: "แอพลิเคชันมือถือ มีแถบสีม่วง ตัวเลขแสดงจำนวนครั้งที่กดปุ่มและปุ่มกดเพิ่มจำนวน", - ), - image( - "Flutter/desktopExampleApp.png", - width: 90%, - alt: "โปรแกรมคอมพิวเตอร์ แถบหน้าต่างโปรแกรมสีดำ ในหน้าต่างประกอบด้วยส่วนประกอบคล้ายแอพลิเคชันบนโทรศัพท์", - ), - ), - caption: [แอพลิเคชันตัวอย่างบน Android 15 และ Arch Linux], -) - -#show raw.where(block: true): set block(below: 2em) - -_*หมายเหตุ:* โคดในห้วข้อนี้ถูกนำ comment ออกเพื่อความรวบรัด_ - -ภายในโฟลเดอร์โปรเจกต์ใหม่ จะมีไฟล์ `lib/main.dart` พร้อมแอพลิเคชันตัวอย่าง - -โดยในบรรทัดแรก จะมีการนำเข้า Material UI - -#set figure(kind: "image", supplement: "รูปที่") - -#afigure( - ```dart - import 'package:flutter/material.dart'; - ```, - caption: [โคดนำเข้าแพคเกจ], -) - -และถัดมา จะมีฟังก์ชันหลักชื่อ `main` ที่ทำหน้าที่ในการรันแอพลิเคชัน โดยมีการรับอาร์กิวเมนต์เป็นวิดเจ็ท -ซึ่งในกรณีนี้เป็นการสร้างวัตถุจากคลาส `MyApp` ที่เป็นวิดเจ็ท - -#afigure( - ```dart - void main() { - runApp(const MyApp()); - } - ```, - caption: [ฟังก์ชัน main], -) - -ถัดมาจะมีคลาส `MyApp` ที่สืบทอดมาจาก `StatelessWidget` ซึ่งคือคลาสสำหรับวิดเจ็ทที่ไร้สถานะ -("Stateless") - -#afigure( - ```dart - class MyApp extends StatelessWidget { - ```, - caption: [], -) - -ซึ่งในคลาสจะมี constructor ที่สามารถรับค่าคีย์ได้: -#afigure( - ```dart - const MyApp({super.key}); - ```, - caption: [], -) - -และจะมีฟังก์ชันในการสร้างวิดเจ็ท ซึ่งฟังก์ชันนี้เป็นการสืบทอดฟังก์ชันมา สังเกตได้จาก `@override` - -#afigure( - ```dart - @override - Widget build(BuildContext context) { - ```, - caption: [], -) - -และในฟังก์ชันจะมีการสร้างวัตถุ MaterialApp ซึ่งมีหน้าที่ในการเก็บข้อมูลเกี่ยวกับแอพลิเคชัน Material -UI รวมถึงข้อมูลเช่น ชื่อแอพลิเคชัน (`title`) และธีม (`theme`) - -#afigure( - ```dart - return MaterialApp( - title: 'Flutter Demo', - ```, - caption: [], -) - -โดยข้อมูลธีมนั้นถูกเก็บด้วยการสร้างวัตถุ `ThemeData` - -#afigure( - ```dart - theme: ThemeData( - ```, - caption: [], -) - -และมีการสร้างธีมสีจากสีหลักโดยการใช้เมธอด `ColorScheme.fromSeed` แต่ในโคดด้านล่างนี้ คลาส -`ColorScheme` นั้นถูกอนุมานขึ้นมา โดยใน Dart 3.10 มีฟีเจอร์ "dot shorthands" -ซึ่งเป็นทางลัดในการข้ามการเขียนชื่อคลาส ซึ่งชื่อคลาสสามารถถูกอนุมานขึ้นมาได้เนื่องจากอาร์กิวเมนต์ -`colorScheme` นั้นคาดหวังค่าที่เป็นวัตถุจากคลาส `ColorScheme` อยู่แล้ว - -#afigure( - ```dart - colorScheme: .fromSeed(seedColor: Colors.deepPurple), - ), - ```, - caption: [], -) - -และต่อมา อาร์กิวเมนต์ `home` เป็นอาร์กิวเมนต์ที่รับค่าเป็นวิดเจ็ทซึ่งจะเป็นหน้าแรกของแอพลิเคชัน - -#afigure( - ```dart - home: const MyHomePage(title: 'Flutter Demo Home Page'), - ); - } - } - ```, - kind: "image", - supplement: "รูปที่", - caption: [], -) - -ถัดมา มีการสร้างคลาส `MyHomePage` ที่ถูกใช้ด้านบน โดยคลาสเป็น `StatefulWidget` -ซึ่งหมายความว่า เป็นคลาสที่มีสถานะ (State) - -#afigure( - ```dart - class MyHomePage extends StatefulWidget { - ```, - caption: [], -) - -โดยใน constructor มีการรับค่าพารามีเตอร์ `title` - -#afigure( - ```dart - const MyHomePage({super.key, required this.title}); - ```, - caption: [], -) - -โดยคลาสนี้เป็นคลาสที่เก็บการตั้งค่าวิดเจ็ท และเป็นคลาสที่เก็บค่าพารามิเตอร์จากวิดเจ็ทที่เหนือกว่า -และตัวแปรในคลาสย่อยของ `StatefulWidget` นั้นจะมีคีย์เวิร์ด `final` เสมอ ซึ่งหมายถึงว่า -ตัวแปรนี้เปลี่ยนแปลงไม่ได้หลังจากสร้างวัตถุจากคลาสแล้ว - -#afigure( - ```dart - final String title; - ```, - caption: [], -) - -ต่อมาจึงมีการสร้างวัตถุ State ซึ่งในฟังก์ชัน `createState` ที่ถูกสืบทอดมานั้น จะมีการสร้างวัตถุ State -จากคลาส `_MyHomePageState` (โดยที่ `_` -หมายถึงว่าคลาสนั้นเป็นส่วนตัวและไม่ควรถูกนำเข้าโดยไฟล์อื่น) - -#afigure( - ```dart - @override - State createState() => _MyHomePageState(); - } - ```, - caption: [], -) - -#afigure( - ```dart - class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - ```, - caption: [], -) - -โดยในฟังก์ชัน `_incrementCounter` มีการเรียกใช้ฟังก์ชัน `setState` ซึ่งจะแจ้งเตือน Flutter -ว่ามีการเปลี่ยนแปลงสถานะ ซึ่งจะก่อให้เกิดการเร็นเดอร์วิดเจ็ทใหม่ และในอาร์กิวเมนต์ของ `setState` -คือฟังก์ชันไม่ระบุชื่อ (anonymous functions หรืออีกชื่อหนึ่งคือ lambda) ที่เปลี่ยนแปลงสถานะของวิดเจ็ท - -#afigure( - ```dart - setState(() { - _counter++; - }); - } - ```, - caption: [], -) - -โดยต่อมา มีฟังก์ชัน `build` เช่นเคยที่มีหน้าที่ในการสร้างวิดเจ็ท โดยฟังก์ชัน `build` -ของวิดเจ็ทที่มีสถานะนั้นจะถูกรันใหม่ทุกครั้งที่มีการเปลี่ยนแปลงสถานะ - -#afigure( - ```dart - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - ```, - caption: [], -) - -*การทดลอง:* ในระหว่างที่รันแอพลิเคชันอยู่ ลองเปลี่ยนสีพื้นหลังของวิดเจ็ท `AppBar` (เป็นค่าอื่นเช่น -`Colors.amber`) แล้วทำการ hot reload โดยการกดไอคอน#box( - image("Flutter/flutterHotReload.svg", alt: "สายฟ้า", width: 1.5em), - baseline: 20%, -)เพื่อเห็นการเปลี่ยนแปลงของสีแถบแอพลิเคชันในระหว่างที่สีของวิดเจ็ทอื่น ๆ ยังคงเดิม - -#afigure( - ```dart - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - ```, - caption: [], -) - -โดยในอาร์กิวเมนต์ `title` นี้มีการใช้ค่าที่รับมาจากวิดเจ็ทที่เหนือกว่า โดย `widget` -ในที่นี้คือวัตถุของคลาส `MyHomePage` ที่ทำหน้าที่เก็บค่า `title` ของเรา แล้วเราจึงทำค่า `title` -นั้นใส่ในวิดเจ็ทข้อความ (`Text`) - -#afigure( - ```dart - title: Text(widget.title), - ), - ```, - caption: [], -) - -`Center` เป็นวิดเจ็ทจัดเลย์เอาต์ โดยจะทำให้ลูก 1 วิดเจ็ทของมัน (`child`) อยู่ตรงกลาง - -#afigure( - ```dart - body: Center( - ```, - caption: [], -) - -และ `Column` เป็นวิดเจ็ทจัดเลย์เอาต์เช่นกัน มันรับลูกหลายคน (`children`) แล้วจัดเรียงมันในแนวตั้ง -โดยค่าเริ่มต้นแล้ว มันจะจัดให้ตัวเองกว้างเท่ากับลูก ๆ ของมัน -และพยายามจัดให้ตัวเองสูงเท่าวิดเจ็ทที่สูงกว่า - -วิดเจ็ทคอลัมน์มีหลายคุณสมบัติในการควบคุมขนาดของมันและการจัดวางลูก ๆ ของมัน -โดยด้านล่างมีการใช้อาร์กิวเมนต์ `mainAxisAlignment` ในการจัดลูก ๆ ของมันให้อยู่ตรงกลางในแนวตั้ง -โดย "main axis" หรือ แกนหลัก ในกรณีนี้คือแนวตั้ง เพราะคอลัมน์นั้นเป็นแนวตั้ง (และ "cross axis" -หรือแกนไขว้ จะเป็นแนวนอน คือแนวตรงข้ามกับแนวหลัก) - -#afigure( - ```dart - child: Column( - mainAxisAlignment: .center, - children: [ - const Text('You have pushed the button this many times:'), - Text( - '$_counter', - ```, - caption: [], -) - -การเข้าถึงธีมของแอพลิเคชัน (```dart Theme.of(context)```) แล้วนำธีมข้อความ -`headlineMedium` มาใช้: - -#afigure( - ```dart - style: Theme.of(context).textTheme.headlineMedium, - ), // Text - ], - ), // Column - ), // Center - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), - ); - } - } - ```, - caption: [], + image("Flutter/app-anatomy.svg", width: 3in), + attr: [Flutter, ภายใต้ CC BY 4.0], + alt: "แผนผังสถาปัตยกรรม Flutter", + caption: [สถาปัตยกรรม Flutter], ) +- แอปพลิเคชัน Dart (Dart app) + - ประกอบวิดเจ็ตเข้าด้วยกันเพื่อสร้าง UI ที่ต้องการ + - ดำเนินการตามตรรกะทางธุรกิจ + - นักพัฒนาแอปเป็นเจ้าของ +- เฟรมเวิร์ก (Framework) + - ให้ API ระดับสูงสำหรับการสร้างแอปคุณภาพสูง (ตัวอย่างเช่น วิดเจ็ต การทดสอบการกด + การตรวจจับท่าทาง การเข้าถึงได้ และการอินพุตข้อความ) + - ประกอบต้นวิดเจ็ตของแอปพลิเคชันเป็นฉาก #pagebreak() - -== โครงสร้างโปรเจกต์ Flutter - -ในโครงงานนี้ โปรเจกต์ Flutter มีโครงสร้างคร่าว ๆ ดังกล่าว - -#tree-list[ - - android - - app - - src - - main - - java: โคด Java - - kotlin: โคด Kotlin - - res: โฟลเดอร์ทรัพยากร เช่น ไอคอนแอพลิเคชัน - - AndroidManifest.xml - - build.gradle.kts - - settings.gradle.kts - - assets - - certificates - - rootCA.crt: ใบรับรอง Root (ดู@x509 สำหรับรายละเอียด) - - build: โฟลเดอร์สำหรับเก็บไฟล์ไบนารี - - ios - - lib: ซอร์สโคดของแอพลิเคชัน - - linux - - macos - - test - - windows - - l10n.yaml: ไฟล์ตั้งค่าฟีเจอร์แปลภาษา - - pubspec.yaml: ไฟล์ข้อมูลโปรเจกต์ Flutter -] - -(รายการข้างต้นรวมถึงแค่ไฟล์ที่สำคัญที่จะถูกกล่าวถึงใน@flutter นี้) - -== การนำเข้าแพคเกจ - -#i ไฟล์ `pubspec.yaml` เป็นไฟล์ที่เก็บข้อมูลเกี่ยวกับโปรเจกต์ Flutter ของคุณ เช่น ชื่อ คำอธิบาย -เวอร์ชัน และรวมถึงสิ่งที่จะกล่าวถึงในหัวข้อนี้ คือการติดตั้งและนำเข้าแพคเกจภายนอกมาใช้ในโปรเจกต์ - -#i โดยหากไม่รวมรายละเอียดโปรเจกต์เบื้องต้นเช่นชื่อและคำอธิบายแล้ว ไฟล์ `pubspec.yaml` -จะมีรายละเอียดดังนี้ - -`environment` นั้นจะกล่าวถึงสิ่งแวดล้อม ซึ่งในกรณีนี้มีเพียง `sdk` ที่ระบุเวอร์ชันของ Flutter SDK -ในการคอมไพล์โปรเจกต์ - -```yaml -environment: - sdk: ^3.10.1 -``` - -`dependencies` จะกล่าวถึงไลบรารีที่โปรเจกต์พึ่งพา ซึ่งโดยค่าเริ่มต้นแล้ว -ในโปรเจกต์ตัวอย่างจะมีการติดตั้งเซ็ทไอคอน Cupertino มาให้ (`cupertino_icons`; -หรือเรียกอย่างง่ายว่า ไอคอน iOS) ซึ่งแน่นอนว่าหากคุณไม่ใช้ สามารถลบทิ้งได้ - -```yaml -dependencies: - flutter: - sdk: flutter - - cupertino_icons: ^1.0.8 -``` - -`dev_dependencies` กล่าวถึงไลบรารีที่โปรเจกต์พึ่งพา#emph([ในการพัฒนา]) หมายความว่า -ไลบรารีเหล่านี้จะไม่ถูกใส่เข้าไปยังแอพลิเคชันของคุณโดยตรง โดยตามค่าเริ่มต้นแล้ว จะมีแพคเกจ -`flutter_test` ที่ถูกดึงมาจาก Flutter SDK โดยตรง และมี `flutter_lints` -สำหรับการตรวจข้อผิดพลาดในโคด - -```yaml -dev_dependencies: - flutter_test: - sdk: flutter - - flutter_lints: ^6.0.0 -``` - -`flutter` เป็นการตั้งค่าเกี่ยวกับ Flutter โดยที่ตามค่าเริ่มต้นแล้วจะมีค่า `uses-material-design` -มาให้เป็น `true` ซึ่งจะเป็นการบอกกับ Flutter ว่าแอพลิเคชันนั้นใช้ Material design - -```yaml -flutter: - uses-material-design: true -``` - -และนอกจากนั้นแล้ว ในส่วนของ `flutter` -นี้จะเป็นส่วนที่ลิสต์รายการไฟล์เพิ่มเติมที่ต้องการใส่เข้าไปในแอพลิเคชันด้วยเช่นกัน ตัวอย่างเช่น -โครงงานนี้มีการเพิ่มเติมในส่วน `flutter` ดังนี้: - -```yaml - assets: - - assets/certificates/rootCA.crt -``` - -โดยจะเป็นการเพิ่มไฟล์ใบรับรอง `rootCA.crt` เข้าไปกับแอพลิเคชันเพื่อใช้ในการส่งคำขอ HTTPS -ไปยังอุปกรณ์ของโครงงาน - -#pagebreak() +- เอนจิน (Engine) + - มีหน้าที่แปลงฉากเป็นรูปแบบแรสเตอร์ + - ให้การทำงานระดับต่ำของแกนกลางของ Flutter API (เช่น กราฟิก การจัดข้อความ และรันไทม์ + Dart) + - เปิดเผยฟังก์ชันระดับนี้ให้แก่เฟรมเวิร์กผ่าน API `dart:ui` + - บูรณาการกับแพลตฟอร์มต่าง ๆ ด้วย API ตัวฝังตัว +- ตัวฝังตัว (Embedder) + - ประสานงานกับระบบปฏิบัติการภายใต้สำหรับการเข้าถึงบริการต่าง ๆ เช่น พื้นผิวการเรนเดอร์ + การเข้าถึง และการป้อนข้อมูล + - จัดการลูปอิเวนต์ + - เปิดเผย API เฉพาะแพลตฟอร์มเพื่อบูรณาการตัวฝังตัวเข้าไปยังแอป +- ตัวรัน (Runner) + - ประกอบชิ้นส่วนที่ถูกเปิดเผยโดยตัวฝังตัวเข้าเป็นแพคเกจแอปพลิเคชันที่สามารถใช้งานได้บนแพลตฟอร์มเป้าหมาย + - บางส่วนถูกสร้างขึ้นโดย `flutter create` และมีเจ้าของเป็นผู้พัฒนาแอป == ระบบการดีไซน์ -โดยใน Flutter แล้วนั้น ไม่รวมแพคเกจบุคคลที่สาม จะมีระบบการดีไซน์อยู่สองแบบคือ: +#iii โดยใน Flutter แล้วนั้น ไม่รวมแพคเกจบุคคลที่สาม จะมีระบบการดีไซน์อยู่สองแบบคือ: -+ Material Design: การดีไซน์ของ Google สำหรับ Android -+ Cupertino Design: การดีไซน์ของ Apple สำหรับ iOS +#[ + #set enum(indent: 5.5em) + + Material Design: การดีไซน์ของ Google สำหรับ Android + + Cupertino Design: การดีไซน์ของ Apple สำหรับ iOS +] -*หมายเหตุ:* Cupertino Design ถูกแทนที่โดย Liquid Glass แล้ว โดยในปัจจุบันทีม Flutter -กำลังทำการตรวจสอบและแก้ไขโครงสร้างระบบดีไซน์ ดังนั้น หากมีผู้พัฒนาต้องการใช้เอฟเฟกต์ Liquid -Glass ในแอพลิเคชัน Flutter จึงจำเป็นต้องพึงพาแพคเกจบุคคลที่สามก่อนในขณะนี้ (Flutter เวอร์ชัน -3.38.3 ณ เวลาที่พิมพ์) +#iii *หมายเหตุ:* Cupertino Design ถูกแทนที่โดย Liquid Glass แล้ว โดยในปัจจุบันทีม Flutter +กำลังทำการตรวจสอบและแก้ไขโครงสร้างระบบดีไซน์ ดังนั้น หากมีผู้พัฒนาต้องการใช้เอฟเฟกต์#jb Liquid +Glass ในแอปพลิเคชัน Flutter จึงจำเป็นต้องพึงพาแพคเกจบุคคลที่สามก่อนในขณะนี้ (Flutter เวอร์ชัน +3.38.5 ณ เวลาที่พิมพ์) -#i แอพลิเคชันในโครงงานนี้ใช้ Material Design เนื่องจากมีเป้าหมายหลักเป็นแพลตฟอร์ม Android -โดย Material Design คือภาษาการดีไซน์ที่ถูกพัฒนาโดย Google และถูกเปิดตัวครั้งแรก 25 มิถุนายน +#iii Material Design คือภาษาการดีไซน์ที่ถูกพัฒนาโดย Google และถูกเปิดตัวครั้งแรก 25 มิถุนายน 2014 และมีเวอร์ชันหลัก 3 เวอร์ชันด้วยกัน โดยที่เวอร์ชันที่ 3 ถูกเปิดตัวในงาน Google I/O 2021 -และมีชื่อว่า "Material You" (แต่ชื่อธรรมดา "Material Design 3" ก็ยังถูกใช้งานกันอย่างปกติ) -และในงาน Google I/O 2025 มีการเปิดตัว "Material 3 Expressive" ซึ่งเป็นการปรับปรุงต่อจาก -Material You เดิมสำหรับ Android 16 และ Wear OS 6 และสามารถดูรายละเอียดเพิ่มเติมเกี่ยวกับ -Material 3 ได้ที่ https://m3.material.io/ +และมีชื่อว่า "Material You" (แต่ชื่อธรรมดา "Material Design 3" ก็ยังถูกใช้งานกันอย่างปกติ)#jb +และในงาน Google I/O 2025 มีการเปิดตัว "Material 3 Expressive" +ซึ่งเป็นการปรับปรุงต่อจาก#jb Material You เดิมสำหรับ Android 16 และ Wear OS 6 +และสามารถดูรายละเอียดเพิ่มเติมเกี่ยวกับ Material 3 ได้ที่ https://m3.material.io/ -== ข้อมูลเกี่ยวกับแพลตฟอร์ม +== ส่วนติดต่อผู้ใช้ที่มีปฏิกิริยา -=== Android +#iii บนพื้นผิว Flutter เป็นเฟรมเวิร์ก UI แบบ reactive และ declarative +ซึ่งนักพัฒนาเป็นผู้จัดเตรียมการแมปจากสถานะแอปพลิเคชันไปยังสถานะอินเทอร์เฟซ +และเฟรมเวิร์กจะทำหน้าที่อัปเดตอินเทอร์เฟซขณะรันไทม์เมื่อสถานะของแอปพลิเคชันเปลี่ยนแปลง +โมเดลนี้ได้รับแรงบันดาลใจจากงานที่มาจาก Facebook สำหรับเฟรมเวิร์ก React ของพวกเขา +ซึ่งรวมถึงการทบทวนหลักการออกแบบแบบดั้งเดิมหลายประการ -#i ในการพัฒนาแอพลิเคชัน Android โดยใช้เฟรมเวิร์ก Flutter -จำเป็นต้องใช้ส่วนประกอบเครื่องมือพัฒนา Android ดังนี้ +#iii ในเฟรมเวิร์ก UI แบบดั้งเดิมส่วนใหญ่ สถานะเริ่มต้นของอินเทอร์เฟซผู้ใช้จะถูกอธิบายเพียงครั้งเดียว +จากนั้นจึงอัปเดตแยกกันด้วยโคดผู้ใช้ ณ รันไทม์ เพื่อตอบสนองต่อเหตุการณ์ +ความท้าทายประการหนึ่งของแนวทางนี้คือ เมื่อแอปพลิเคชันมีความซับซ้อนมากขึ้น +นักพัฒนาจำเป็นต้องทราบว่าสถานะเปลี่ยนแปลงไปอย่างไรตลอดทั้ง UI ตัวอย่างเช่น พิจารณา UI ต่อไปนี้: -- Android SDK (API Level 36 ณ เวลาที่พิมพ์) -- Android SDK Build-Tools -- Android SDK Command-line Tools -- Android SDK Platform-Tools -- Android Emulator (ไม่บังคับ) - -และแนะนำให้จัดการและติดตั้งเครื่องมือเหล่านี้ผ่าน Android Studio - -#i ในการติดตั้ง Android SDK ควรติดตั้ง Android SDK -ล่าสุดถึงแม้ว่าอุปกรณ์ของคุณจะใช้เวอร์ชันที่เก่ากว่านั้น -เพื่อความมั่นใจว่าแอพลิเคชันจะสามารถใช้กับอุปกรณ์ที่ใหม่ล่าสุดได้ - -#i แอพลิเคชัน Android จะมี SDK/API level เป้าหมาย (Target SDK/API level) และ SDK/API -level ขั้นต่ำ (Minimum SDK/API level) โครงงานนี้ ณ เวลาที่พิมพ์ ใช้ API level เป้าหมาย 36 -(Android 16) และ API level ขั้นต่ำ 24 (Android 7) ซึ่งรวมกันแล้ว แอพลิเคชัน Android -จะสามารถติดตั้งได้บนระบบตั้งแต่ API level ขั้นต่ำจนถึง API level เป้าหมาย หรือก็คือ -แอพลิเคชันในโครงงานนี้สามารถติดตั้งได้ตั้งแต่บนระบบ Android 7 ถึง Android 16 นั่นเอง - -==== Java/Kotlin - -#i Java และ Kotlin เป็นภาษาสำคัญสำหรับการพัฒนาแอพลิเคชัน Android ถึงอย่างไรก็ตาม แอพลิเคชัน -Flutter นั้นถูกเขียนด้วยภาษา Dart แต่ยังจำเป็นต้องมีโคด Java และ Kotlin -เล็กน้อยเพื่อเริ่มต้นแอพลิเคชัน Flutter - -#i โดยปกติแล้ว Flutter จะสร้างโคดพื้นฐานขึ้นมาให้สำหรับการเริ่มแอพลิเคชันแบบพื้นฐาน -(อยู่ภายในโฟลเดอร์ `java` และ `kotlin` ตามที่ถูกกล่าวถึงใน@flStructure) -ดังนั้นจึงไม่จำเป็นต้องมีการเขียนโคด Java หรือ Kotlin เอง แต่ในบางกรณี -อาจต้องเขียนโคดเพิ่มเองหากมีความต้องการเข้าถึงฟีเจอร์พื้นฐานระบบที่ Flutter ไม่มี API -เพื่อให้เข้าถึงได้และไม่มีแพคเกจเพื่อรองรับฟีเจอร์ที่ต้องการ - -#i โครงการนี้ใช้ Java 21 (JetBrains Runtime/Azul Zulu OpenJDK) เป็นหลักในการทำงานกับ -Gradle แต่แอพลิเคชัน Android ที่ผลิตออกมานั้น เพื่อให้เข้ากับเวอร์ชันที่เก่ากว่าของระบบปฏิบัติการได้ ใช้ -Java 11 - -==== Gradle - -#i Gradle เป็นเครื่องมือสร้างระบบอัตโนมัติสำหรับการพัฒนาซอฟต์แวร์หลายภาษา จัดการงานต่าง ๆ เช่น -การคอมไพล์ การแพ็คเกจ การทดสอบ การปรับใช้ และการเผยแพร่ ภาษาที่รองรับ ได้แก่ Java -(รวมถึงภาษา Kotlin, Groovy, Scala ที่ใช้ JDK), C/C++ และ JavaScript Gradle -พัฒนาต่อยอดจากแนวคิดของ Apache Ant และ Apache Maven และนำเสนอภาษาเฉพาะโดเมนที่ใช้ -Groovy และ Kotlin ซึ่งต่างจากการกำหนดค่าโครงการที่ใช้ XML ที่ Maven ใช้ Gradle -ใช้กราฟแบบอะไซคลิกกำกับทิศทางเพื่อจัดการการอ้างอิง กราฟนี้ใช้เพื่อกำหนดลำดับของงานที่ควรดำเนินการ -Gradle ทำงานบน Java Virtual Machine - -#i Gradle คือเครื่องมือหลักที่ใช้ในการจัดการโปรเจกต์ Java ส่วนใหญ่ รวมถึงโปรเจกต์ Android -โดยในโครงการนี้ จะใช้ Gradle เวอร์ชัน 8.14.3 เป็นหลัก - -#i โดยปกติแล้ว ผู้พัฒนานั้นไม่มีความจำเป็นที่จะต้องแตะต้อง Gradle ด้วยตนเอง และ Flutter -จะทำการจัดการเอง แต่หากมีความจำเป็นต้องใช้คำสั่ง Gradle ด้วยตนเอง จะมีสคริปต์ `gradlew` (หรือ -`gradlew.bat` สำหรับผู้ใช้ Windows) ภายในโฟลเดอร์ `android` ของโปรเจกต์ Flutter -เสมอเพื่อเรียกใช้ Gradle ที่ถูกดาวน์โหลดมาสำหรับโปรเจกต์นั้น ๆ - -=== Linux - -#i เช่นเดียวกับ Android ที่กล่าวไปข้างต้น Flutter มีการสร้างโคดสำหรับการเปิดแอพลิเคชันแบบพื้นฐาน -แต่สำหรับ Linux แล้วนั้น Flutter ใช้โคด C++ และเฟรมเวิร์ก CMake -ในการสร้างรากฐานของแอพลิเคชัน - -#i ในการพัฒนาแอพลิเคชันสำหรับ Linux ต้องติดตั้งโปรแกรมเพิ่มเติม (build dependencies) -ขยายความคือ ด้านบนคือสิ่งที่จำเป็นหากมีระบบอื่นเป็นเป้าหมาย แต่หากต้องการพัฒนาแอพลิเคชัน Linux -ต้องติดตั้งโปรแกรมในรายการด้านล่างเพิ่ม - -#grid( - columns: 2, - column-gutter: 1in, - [ - - GTK 3 (ไลบรารีสำหรับการพัฒนา) - - pkg-config - - ไลบรารี GNU Standard C++ v3 - ], - [ - - Clang - - CMake - - Ninja - ], +#afigure( + image("Flutter/color-picker.png", width: 70%), + alt: "หน้าต่างเลือกสี", + caption: [หน้าต่างเลือกสี], ) -#i การติดตั้งไลบรารีและโปรแกรมที่กล่าวไปข้างต้นจะแตกต่างกันไปแต่ละการแจกจ่าย Linux และ -Flutter ใช้ไลบรารีพื้นฐานดังกล่าวในการทำงานของแอพลิเคชัน (runtime dependencies) +#iii มีหลายที่ที่สามารถเปลี่ยนสถานะได้: กล่องสี, แถบเลื่อนเฉดสี, ​​ปุ่มตัวเลือก เมื่อผู้ใช้โต้ตอบกับ UI +การเปลี่ยนแปลงจะต้องสะท้อนให้เห็นในทุกที่ ที่เลวร้ายยิ่งกว่านั้น เว้นแต่จะได้รับการดูแล +การเปลี่ยนแปลงเล็กน้อยในส่วนใดส่วนหนึ่งของอินเทอร์เฟซผู้ใช้อาจทำให้เกิดเอฟเฟกต์คลื่นส่งผลกระทบกับโค้ดที่ดูเหมือนจะไม่เกี่ยวข้องกัน -- GTK 3 -- blkid -- LZMA +#iii วิธีแก้ปัญหาอย่างหนึ่งคือแนวทางเช่น MVC +โดยที่คุณส่งข้อมูลการเปลี่ยนแปลงไปยังโมเดลผ่านคอนโทรลเลอร์ +จากนั้นโมเดลจะพุชสถานะใหม่ไปยังมุมมองผ่านคอนโทรลเลอร์ อย่างไรก็ตาม#jb สิ่งนี้ก็เป็นปัญหาเช่นกัน +เนื่องจากการสร้างและการอัปเดตองค์ประกอบ UI +เป็นขั้นตอนสองขั้นตอนที่แยกจากกันซึ่งอาจไม่ซิงค์กันได้อย่างง่ายดาย -แต่โดยทั่วไปแล้ว ไลบรารีเหล่านี้ควรถูกติดตั้งมาอยู่แล้วหากคุณใช้ graphical desktop ทั่วไป +#iii Flutter พร้อมด้วยเฟรมเวิร์กเชิงโต้ตอบอื่น ๆ ใช้แนวทางอื่นในการแก้ไขปัญหานี้ +โดยแยกอินเทอร์เฟซผู้ใช้ออกจากสถานะพื้นฐานอย่างชัดเจน ด้วย API สไตล์ React +คุณจะสร้างเฉพาะคำอธิบาย UI เท่านั้น +และเฟรมเวิร์กจะดูแลการใช้การกำหนดค่านั้นเพื่อสร้างหรืออัปเดตอินเทอร์เฟซผู้ใช้ตามความเหมาะสม -==== Debian +#iii ใน Flutter วิดเจ็ต (คล้ายกับส่วนประกอบใน React) +จะแสดงด้วยคลาสที่ไม่เปลี่ยนรูปซึ่งใช้ในการกำหนดค่าแผนผังของวัตถุ +วิดเจ็ตเหล่านี้ถูกใช้เพื่อจัดการแผนผังวัตถุที่แยกจากกันสำหรับโครงร่าง +ซึ่งจากนั้นจะใช้ในการจัดการแผนผังวัตถุที่แยกจากกันสำหรับการประกอบ หัวใจหลักของ Flutter +คือชุดของกลไกในการอัพเดทส่วนที่ดัดแปลงของแผนผังวิดเจ็ตอย่างมีประสิทธิภาพ +การแปลงแผนผังหลายแผนผังของวัตถุให้เป็นแผนผังระดับล่างของวัตถุ +และการแพร่กระจายการเปลี่ยนแปลงไปยังแผนผังวิดเจ็ตเหล่านี้ -```sh -# Development dependencies: -sudo apt install curl git unzip xz-utils zip libglu1-mesa +#iii วิดเจ็ตประกาศส่วนติดต่อผู้ใช้โดยการเขียนทับเมธอด `build()` ซึ่งเป็นฟังก์ชันที่แปลงสถานะเป็น +UI: -# Linux build dependencies: -sudo apt install clang cmake ninja-build pkg-config libgtk-3-dev libstdc++-12-dev +#afigure( + ``` + UI = f(state) + ```, + kind: image, + caption: [สูตรแสดงการทำงานอย่างคร่าว], +) -# Runtime dependencies: -sudo apt install libgtk-3-0 libblkid1 liblzma5 -``` +#iii เมธอด `build()` นั้นตามการออกแบบแล้วเป็นเมธอดที่เร็วและควรที่จะไม่มีผลข้างเคียง +ทำให้เมธอดนั้นสามารถถูกเรียกใช้โดยเฟรมเวิร์กเมื่อไหร่ก็ได้ที่จำเป็น +(เป็นไปได้ที่จะบ่อยมากและมีการเรียกใช้หนึ่งครั้งต่อหนึ่งเฟรม) -==== Fedora Linux +#iii วิธีนี้พึ่งพาลักษณะเฉพาะรันไทม์ภาษา (หากเจาะจงคือการสร้างและทำลายวัตถุอย่างรวดเร็ว) ซึ่ง +Dart นั้นเหมาะสำหรับงานนี้เป็นพิเศษ -```sh -# Development dependencies: -sudo dnf install curl git unzip xz zip mesa-libglu +== ประวัติ -# Linux build dependencies: -sudo dnf install clang cmake ninja-build pkgconf gtk3 +#iii Flutter เวอร์ชันแรกรู้จักกันในชื่อ "Sky" และทำงานบนระบบปฏิบัติการ Android +มีการเปิดเผยในการประชุมสุดยอดนักพัฒนา Dart ประจำปี 2015 +โดยมีจุดประสงค์ที่ระบุไว้คือสามารถแสดงผลได้อย่างสม่ำเสมอที่ 120 เฟรมต่อวินาที เมื่อวันที่ 4 ธันวาคม +2018 Flutter 1.0 เปิดตัวในการประชุม Flutter ที่ลอนดอน -# Runtime dependencies: -sudo dnf install gtk3 libblkid xz -``` +#iii ในวันที่ 6 พฤษภาคม 2020 ชุดพัฒนาซอฟต์แวร์ (SDK) Dart เวอร์ชัน 2.8 และ Flutter 1.17.0 +ได้รับการเผยแพร่ โดยเพิ่มการรองรับ Metal API -==== Arch Linux +#iii เมื่อวันที่ 3 มีนาคม 2021 Google ได้เปิดตัว Flutter 2 ระหว่างกิจกรรม Flutter Engage +ออนไลน์ ได้เพิ่มตัวเรนเดอร์ที่ใช้ Canvas สำหรับเว็บ นอกเหนือจากตัวเรนเดอร์ที่ใช้ HTML +และการสนับสนุนแอปพลิเคชันเดสก์ท็อปแบบทดลองสำหรับ Windows, macOS, และ Linux +นอกจากนี้ยังมาพร้อมกับ Dart 2.0 ซึ่งรวมถึงการสนับสนุนด้านความปลอดภัยแบบ null (null-safety) +ความปลอดภัยแบบ null เป็นทางเลือกในตอนแรกเนื่องจากเป็นการเปลี่ยนแปลงครั้งใหญ่และบังคับใช้ใน +Dart 3 ที่เปิดตัวในปี 2023 -```sh -# Development dependencies: -sudo pacman -S --needed curl git unzip xz zip glu +#iii ในวันที่ 12 พฤษภาคม 2022 Flutter 3 และ Dart 2.17 +ได้รับการเผยแพร่โดยมีการรองรับแพลตฟอร์มเดสก์ท็อปทั้งหมดอย่างเสถียร -# Linux build dependencies: -sudo pacman -S --needed clang cmake ninja pkgconf gtk3 +#iii เมื่อวันที่ 27 ตุลาคม 2024 นักพัฒนาชุมชน Flutter จำนวนหนึ่งได้ประกาศเปิดตัว Flock +ซึ่งเป็นเวอร์ชันแยกของ Flutter ที่มีจุดประสงค์เพื่อให้ง่ายต่อการร่วมพัฒนา +ในขณะเดียวกันก็ยังคงรักษาความสอดคล้องกับทุกการเปลี่ยนแปลงที่เกิดขึ้นในโค้ดต้นทาง -# Runtime dependencies: -sudo pacman -S --needed util-linux-libs xz gtk3 -``` - -=== macOS/iOS - -#i การพัฒนาแอพลิเคชันสำหรับ macOS และ iOS นั้นต้องทำบน macOS -เท่านั้นและจำเป็นต้องพึ่งพาเครื่องมือ Xcode แต่เนื่องจากในโครงงานนี้ไม่มีผู้ใช้ macOS -จึงไม่สามารถสร้างไบนารีสำหรับ macOS และ iOS ออกมาได้ และไม่ใช่เป้าหมายของโครงงานนี้เช่นกัน +#iii ในปี 2025 Google ยังคงพัฒนา Flutter ต่อไปด้วยสถาปัตยกรรมแบบโมดูลาร์ที่ได้รับการปรับปรุง +การรองรับอุปกรณ์พับได้ และการเพิ่มประสิทธิภาพ ARM IoT ตามที่ระบุไว้ในแผนงานฉบับปรับปรุง diff --git a/Chapter2/Flutter/app-anatomy.svg b/Chapter2/Flutter/app-anatomy.svg new file mode 100644 index 0000000..d18ea14 --- /dev/null +++ b/Chapter2/Flutter/app-anatomy.svg @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Chapter2/Flutter/archdiagram.png b/Chapter2/Flutter/archdiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..2d34fe8f2b876f7f6b452d653496a1fb91b80592 GIT binary patch literal 109102 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맼iyPn?zr!jlW_F@;jmLbXKZ*PxA*-8$I=T&yFTAGUkx`w{^_N+e^M4htug;ir#qsO;n7;>3&wu!H@1MBJYfEia-#hJGmB60GD?Cj- z-kyhj=f5AHi?81G|6JYsUgy1qvgW={mtI7dzqm%$Lq6i z3cj8^eKT%l&9evx_enj|zx+0LvkuOlS(q&oTO#Z>O1?h z`~Ji4+gdGa1?nD(%}&2-bLqisp=r0)>8V)oF6HnpEh&gu+@vgW((lgCHLY5D7W`I| zO`Ia8?SId5ptxm0>SX7>!!n{xqGuT&T5yQ1*z>o+abo$L(&xX5pUhyf^xjt1=TP-} z=asp!|H@5@=KJE*eeM%fl;&_>Jz5sPeZ%o^i)yGuY2}~Yr+W{s;d!`+ z<3VdQ&!iWIni0z!lh*e5Bp&~ME~LgxOCV?Z9otJ^br$STHj}>V{aszX0ExE_`6i`Lz{lr>N`YS z`c)d;y5=?Cd)af*mrnk_bn>oo?^dty&fhB>^uABo(Bjb0acY=B;gL8`_^ZapuhzI~fvZt`(k__j1LDZuwbP`8X2qu6P|8RloY|wNg#bh()hA z=e{wYovY&+Qg!CVzq2h*OAeJ?STJ=JSLf_E+jk2u{JBKQfC4O|y;1-0Jn z{%!p9?ebq&7cbrO*eUE8$M^bM9NrwUf>x$2`&O65(Z#7v2{L~z!?n@}?CXE_7MJy8`MMVX)9Q)bIv>)2Mn!V@_^KB2{ zlNIcQpGf7-kLL6iJ=bZLb0frx|ITlpAbI~R-*fwvRys%MM;%WrU+J+hxS?xJy5(Ql z4L7AS(sUKFIhQRuzFTNo)jt7_8y4s0r#IgBdFw0Xac9Zd=R3mhn6HZvefv-==}ldw$EJlv9pOBE z=)3HNstDPI+o})l>bx9!dQMo5PkPY%ZJ7&;zb;N$r?s%#+5Psdf`ri30YT^F&pW^R z{QtvOOVy7%_-*(%mOowl^7qbrEl&%*V~ z&Nx%e#(n3?O>dg4-M;=jvFP@Z>r*7BSNWj=QH;(QK$c_4M{kAl8#g?P@%zE!${L8!~-|@EehUE*t zdw3Qlx%SRqnz~77^DZ^y2$4?t<*&3uA#TcqgN7#*wFP9>H6$N(3!h_lP()~&h3c%A z6834kc%Ms}_ew=S@?+xL$u)IC;h{}OYTv(m@c;U{&r><38mxW&@xf>BL?P4keSKEl zzE4y|Pw)SoS!(h4pxCDH+cNeik_5H@F)5d8f8@`YRniU%N_U@I`{e&*7n6rK-ncWhq%!`MRnW<=Px`d0 zL{(?!`lzcajPI|7?G)&lRr9r`DM0K()?V=zP{?jr-hY-gjo-`k7T^1f`_=DCo;9gy zF7jWzPqZmk$VtoXP)1Ytw$iiT)46V*Z;1CzpYbE7?poy{`L!zQviw%>L#?`g@v8jQ zc#Bt*hi*t}iaW!1;FSdo8IO$F6m`Ft26V70q$_)34}jbABH(ou;DWIVa23 zEO)nh=%Iao8yfHVzbSgMCv)|6{zI2G$eLU@{NnwEJBeQya@GI;(k`C-t+GbXX7>Lj zuiJLLzcO>8*ORTkf_Kl`>%g>JE&kbW6)-LpuPtXJDIb?qbjvYF3+w)F76@#Ybp2I;08 zZb@bMzv=R;Q>Go8wpzHG-L}fHdl2Ec;7i|zJ0JHNuj~n1>A}-%X5T4UTR5rrr%+U= zgu0IRo$v4X{uRjZn{1DLJ^S0$_hrm3Dxw~YiSK+C+_DT?sr`P^xvJNyiJoq?UuJPm zdLa{Ny@KzGlx$N&8>q$roP8_J>9tR z+#3Gy%9n)~o0aCIKY!f|szueescc`}qo(C}!S9z%tVEX6(KjJN)2t>J6<*mRcFQ#V zyYK3G+^e#gukPH~68$W4`u4qrhm0O*KZ-v&Z&aDzNx^#h0nhyg^?kZMYJdKl#J%`E#zCg_Q+dKgK7e zro+i|nQ!Y^yM@Ass<=K>u63Fta;N!2SIqiXm0z6SzAxcEu__^8LI3RUu}{Blzmc;q zeAb2~my{-LFy5G}bXPIUY4!C3mfq6)?1Vsdm?Nudaar`q)5?1CW@j||dT0IEKKI$x z|3OCl_UyHj-Y6CPd#lW$R}vR^Xw#OzN7kR4`+lEAR%ATmw^b^MoqwypwV#em$Yr|j zrkZf+oKE-r#h<6{TpqPna&LNwQ1&Ii%GEr50)!%-U zPhb6R5np~ihj-uIi8J2c=sM9Db+|>fC2{VXV413yNn59VTBvO1ucA|aTjcirKOr2a z@BcaSDqEpocHU_*rcSk8@3q$NKbpp^7~QaeCuA{)UBQhfwR6sWU9#H!+^)^fjS_Fx z-{6x{IdCC$$--2fe;F&hPZ@IVf0UMayVmuv-TYHK%@$r3h%MUSJ0bEe$LUYsg8#hA z`7ao&ziqidVx--&g*zVf1!ku+<$mv4o4?NKt)5Ew)a`rka(GXgb2VP%q3yBlkM~af z8>WubL-a2E6cyEFu)5JVZ{HD4Z<*Ei1g9CT=5$xqiphT!lW7_(>~Y}D=ViTdvG+FJ zSMZAv+xz|}-#=?UnUHMFls>Uq_td(RanJSC=HC5(E4TE)v{%ozipZR^6P#wl9NYQx zOO6RBFV5PKQYU^@%hV(K={L#52ZyZPuSRj{o>3Q^W<7tZe}vY;<)V{T)iWQ^d%bV< zmVbOV-n>-Tnc02s&MNcQ`(|x;W1qv$`)zK>Idu+!i`7n_PR0dlJ$dxG*^?_(Us}-a zI8Xf-zwkqw_WU(TT=~E<<8_?F)r8m$nX_bW20r;YBk54kt{Vd099(x7%t(~9*oKr| zcpg-yz2BO$&M0^yZ&{R2#Hz=IpT0Ub7CDc9aW6wAGf651f`0} zj(gISblJESVj53ZuhHe*-OX0^+c`q$#4}RjX_TvBArdwQFJtwDS1uon- z?bEwv_NSbuTk2XjoQ*tRoaZY%?djL@{hZ!SGfEdGJDuBgUU$ZZH~Km3y+1fOTk0>L ze|UN2pTpPVFDh@jduF!3PxXz+oZdHzt|>8GE^+(pgYVVN+R(GT`?}=ZH>Dr4w@lq2 z^yse3{H1aN(>~QExdvX`_8z5uV7+g8rJU|(S+l&iXB0xr0#~vf^jsZ~Hs#Kz8xofi zcdO}?)@GU(f74Zd)@YjcMJQt}`@@LUyDqeCeLc&-T}|$fPUZd2vNcSncg4N_@~&`x z#OXJ$WD2f`9`1A$y>(>S=9=A$`CFbw7|&-8Sg3S^Zx!>_#2FiwFe$Fi-L`Gw@}u$J zKkNEO*ts9jT|6n|;6In^oA>;CXP@}A4z3>!f!l94e|xw9eS@KIz}$FjKb$ zYj>UBZ96xhBQEpRl%~A*kA#cw)wHJsetW8w^!n4^nV-I0Zb>zs(SIxB{}r=M|2=*? zel+b`%-*tej{3LRzt86L9`_ABDZA?~auPq#!*@3&D^2mUSg68kHP&8<8~u~F>=mBI zb?sZSd+xQ*SGU={a?rdf%(N(5h`(;y-uI5|@%GZ{XSo`7M&JJ}xn%P;(XQr{22a12 zZ~fQA?Vh|&`rEV(Pb~X|r`=*R{Fj$yZ3s%_#}W?(tztJlsVeeE>GMAJmZj_18z1ZC zUW+{6@Xsu<@~-ph_s@TNd1x#-&75?o=v9Js#)8?euV!z#cE*lPHdCqY!R&9cIeYKl zo^gx4%s(bsp6le%jUidmx0o@q>tRSCnrR$os2eyy>qKsUVl5Y?ze&t^U0M~ z%XeLVA&N8x;As%lav&({SZapz8U=;4Lq@&4`IVmMXEqqtGg~+<+^NU)ci$wRj}OvM zf)e%Ph`i48%Qcy9eOr}t$M$^U-}SNT!qbFyDE=@mZBcD1zWYgbroV`q&dhMBebfBq zw@1}qu(|+>>YoOQo(J(SW|I)A_MeNJASK&-MZY0KneZBL0W%ti&4b@A& z5q{D+?7TA@d)6~X&M5B*Zfa2tkak`4{Nr^V=|4B#{kXk8&-TwnBitcmhYo;#WKqD&Ga5C1*CZdbcX#Z&p?NImgMil=y31vx*Q>^c2IO}2e1 z&#|vcI;nlJm4`mnw)3r!TCg?HbpM8>zs~w^`EU2yLUD)Z>Zv9RB@dL>?^@&MWAh{O z-@et&@`V>7T(5&FMLrT*x;@S4!SJQu7s&*?2kX>QAt#JN_d(LcAPvyItle%wPq{Ck(?pyu4=-E+oHM@8FGB@t})oA!TQ1+z2SKAM* zpZ-Ad2SdDd=~Uh&%1m~NJG7aU160Hm%619%v^IWNvYPqiqu1UMZ1Xt3d8s56+I*9J zBY*4M({g*$+r1s;J`s@NY98Tf^X&^aPx2Dp93)?t__h6HTkdb(P2WTMG(95Dbsye! zeP{Q2t;EjH(c;e*Y?1xG;dGd_eTw?E|9!!Gb&VD8)qYqIw9q!)=11g*ecrz#rpEuh z@#e_$u&41%x|JMtEV2jRBrCSZNmXClVOINohVL!6E<0fb*GTU^SRk+V~O?w$=_KYut|3Ce0xvfnt3@+Yiv;#hvo>(|GiHQWrIQc0gr z3r`c8F}0z*y6O7ut-FH2g^g;W=2hKY^A#_ywp^LlcA%zp#)dg@H~6Gpe3`R%hFaE& znx{`%H`uE+ojdXB%BO>eZ-1ZFUtb>i{mjmPK7pWQ76S#Ju@Y!xBqh^`;&y8<5Y0uyACc#nl&6ZxEH2Ri9cpA%_ zFL#r!ZLRA&=S}H;&%uw6b_I_)>%!QfFv)0U-T*IE}ntDS;-szm+ z?5Xe1PuV_SO^12McgZ?+%@Va!dDUHSs_Q-*yR+)lttz>unODuc&tS9F;m6(+R=<=hMDI zRVG7cY4a0eX8&C|PcOTcdPiL*_*&-Dpyom_O&Rt z=G;lPv*Ta=p*DPv#d*t|*wPcFm)l-)uB=OZ=5e?xAkr(8jZdjhB6Qu!YNp6NVFx8+ zr8~TvO!9B$2kd>lfN|~ODGDwE9^a1LS13?)ddio(*uzd?#pzo02h4%fpGJJzer2am zMR-Nqq$NsE^v~-)Km6HQz*(#H6Q_6KzMXD;yb}+dIkl{TnXwR*vO$9^+6G<>-uC79 z-Y)iPWiV)rdiMKMUGn0%?cZy@-YB1@E%>GWrR21gEP5rGMw#53KCa(y)y}(6t4WJ_ ztKzPCPrsJibDq9nc2%!7wUp8wEeVOSgtxD@=n>DbuFnO3SDm(tm~CE@riJHmAQed`r!WTtJcNhpIZ}V zs=O3Y(GmT>u{-SSlh-D(=Rf`0{k%o>hQ?e8CZ|b4Q}xw!mP>4FKIWDiK1-k9Zof$N zcJos_9xqu0@;}bfS7|T%oPO%|`G3OG5>Kw5^hbW)T$@eNj*n7&7A-D6bvy3BZ&ObN zy{)H=_Q?ks+1lN|j8td6N}Hkh=HdOtMZIS(EeJ?-D2?| z;h~x&cChfJ?c&_|iT~_>+OhoVI%Tx){>k~8`S!jM&&pGj6pf$PynHeF_5Vci%5T?t zH!O)N-y@=+v$WLkNpi@Nqx+B=;5p@aH~vgIS$Q#5dCs~W$r|TgTxAi8uIqkf+}D5k zidnQv`^EU*4|ZJM72r7i)&%`>HJ#{NA3A~s3LF&eR(|Q2$Nn~LNBhJBtIq7~*(!VY zp{%~>{`W6$PC2{w(U#Up+rBNd^-ZzOI2``S_Q_3!57Gi!(`M|QQCjovBx`xkB;AfJ zbMGZi*F7b3_T-OCCvU#zl01{$p)QoW<^uVAJ(?>y;)!tAl=OA zed|+d?ffoL-dkp@4J-9B6<=%+z9E$JfE=ntR_> z;^D;Fb6aY2C5|pSzwDjpwX@&;{r`S8BdAeriROo=H;%U?`R>l;`mw`J(>mkOB?F$r z20jr|`+seW>=1A2%IUkg!I-^KtE0es>ZJv{8XxGW$bbHRP4RaB)&xGzgXT>8EjTCG zrWksu6>OXAyH!E**p`aKzlz+P{|fK!-TU6cRiHa%ol}+2mje^~*RiinicH>QJ=6X0 z&aBf*oEGfWF4kVDr@ElwGp~ohgZmsk<+alk9?x8P>}%oN6wcte+?m&3{(Lqw@oeFW z#qUqOudA9CIoGD#=JpwVB@f|K4(Cs5J==3`S0&5J0NY=4RkhZyD7y6KlV$_+*0#od z&*rAGUFYTRSDe40lEpZ4@iNEzD;b}=E_nOrw)+dIFOrN_C3DVbNAv&972puPw1?dw zhyAU#QMP>1>uJZ2z3Kd!|E=rIdCxW;8FiCx+1Oi||NHJH^;vU@{4js>c&6hC(<$H6 zf92ToTFGvxKOi4fS$D(!&Av>*`B_ruXWDXho)wt)#?j*)m+j>Vdw=}dQ*Sf>M}3U> z@f#m*{ZD?+XR)MfkwwFIR;C;Lf((pJ4J;ClOB7lhnmjggincI`9C8cvkal8Ow2foS z4c(kuBG#9EjE!$stG}ztetz@gx3#R--+%bcR6gVI-~H8oZ2OlV2uk2&VDe^|#dNx7 z#|qXLOZely$*IM^FxzjV$p1@$ca`BPSIfVfvfueQ|KMD}&!xlYkigk+;F7EEfxkxs zZ#@j#JI$s{)z>~_Z~mLpQ4ei4FXG?!e1A%MZA_+|BQJ^uhuZrkw_?jzT(-$tAC`G$ z|K~F^>^u(txOVaj*FKYFmu{LceQ|75W#G_Z6o}Zhf_2B=9FeVFyKT~YihOqOKQ`Mi z)Kzll`;$)cYc6_V423ip%Z(B=}`N-1wBzq z1wK7(_l&8x>G9_Of9%lhLN257)z;_TXU-S?mC(Amoc*@7miy!zTm8;_pDT*u)C2cq z_ulFLx1s0nmFM|C->-C*Um_QAZXNsj?JeFf|+yVrX329o2Anx%s2Zul8pI zcY9BMTRZ(l^RER5J{|qtX$<#`#Sg>A?ET$8u4+#%KfQDRqQXG#g2yKfdOPNZrtbW@ z>eH3AI_`Fh_%9{Ai!qnmD|!6Wccp#*bdpND#(wXS61)F5Hz!LmFfy<;aU2wG``E&&Qu4$(&w|(Y zSJ?$VCX^*!2FwiB8jx$x`=S^&G&5X4S@rgY`2cLeFTIgF*@6sp;8 zkR9g8@Zvp+!yM{S%x3t8s`*Pf>dL=8C=LTJ%{-tG!Q{{&%W{D4mLr<4(G3SLngg$g zByAmL1g-bYO_;N`Lg z9Mi5dGsJVqY%LRDu;yU+i$5;QNs7z;sBu}1s`(e*M3%4#S@R4=ys7^iBTCK^XgG+R z$P(lkP*VSIh7HJOa~wd?ynz)pTh^ilBj`4Pnwy)MxfmE2IvW2*>mPeiAURK-!TO*O z19+`#J*tL3sBZhfj^YzXh9AvKk@N8eAstq41_2$#Z|4{zigdURM?cfPuAQ6M`{)+< zB<<*A(B`DHXwP>ytxhlbd||Gf%FOrce;vF0ZqDq_Pwz=*-g`G?`dd%snR_OFOYRB+ z^_5v0+;kbHT&(u;)QSH0_$%Mf8}`q=@A-P4{5q%o{KjuD|6ad4=fj?7)|cBh`nU$L zGcuUHDX=kpa>0G_#L739>8N)3&P}nd{^! zaXN&hRDeNYrs<<|6Z_qrBd*=u63x0=-P_!1&+;o!!#C}E7PY-o^`+s;$|xTFQ@>+B ze>%_~xpKd{hcVy0+=;7SYz@2Bt9Pn+ZBfyy)0&nq*y45{o4sD@{u@^N1-$|HpTC*a zZ)|%1o%P($`)>c;WA1zXgu~)($8Jf^S^odT$DiE>Re!EjP0^Rs>^D?eIY+Q|zJHZw z>jswyqiGBIlyqkQJU8X~32D2tEpG}B{mfsgE%&%YGG1t|Yu(A}%-Z_OzkCddKXXpc zF`3e(|AeVU^$;k7C$3~N%Gk2$PIxn!C@glmJ2I*c$Wt8v&3ArtGMW5o+o{y0R;`&t!sDtMFXiKEvWHn>DE zF)%3TG#}secyr6uxjfmuOjPp>p8Hcr14qcjU-*zS=7yXLxyG}{ za-nbd&pZc5zPgUd_o7--PYWKj_m?iS;5hAeLab*;aD8}_a7fEi_2B8Qo)L#W>MuEB zcq!q_`SSFhoI5W))bgdK&hj?1;P7sFI#2#IL#ct;bEbP+R%P{Y?ecB5ek>NXPx zEvfytKr;~Z&ENd*p6&HJ`=9LH@?z7r```3m{C$z@?iZou>V4Bryi|$d+?+`a!Nv(cci*2USNHJ5&W8)9U);z2C;wN*oLqhhwe?ofpEujyNMB35 z)x7h7_2+l>8z1|8Z(RCY?##V%2NnmG$8rpx-tA5M`}#_|>>Q81Z|-fq{w}W~rSQl0 z-Df1eulQ#A<9+#D$xUsW*#8`;+xw7j{+y`)dwvUiUUz)nR8!&G$L3wEKl{dFgNyvV zP2awMJbU-nvevp3-(zx#%lUWLBzD~Y@zS~aO}RunqYm#rslOlgPJZ#)?yr-4@7Ld( z#r`}HnRh?kXJhy@+tPH?hE#^r7r=mF+M}8mZ};Z}eA&LwZf!#VEBn;5b!E-{#ST`A zB746rJi0xrIx&di(Pi}wFFKdoY|8gRzczK&t+mRpw@tgU@UWs%!lJDSrFV?Y z3vTUcP<^laSdb&BPL6TustfW<4@}zVctCRb9zTisEX#iWInMVg^ta59B&pSF_*rsH zbdycjCYu%C#y`pxpx+B=1+3%&$(8sEwJLB96M*vflHgF7v5-|`MYoH;&a~$4LbMl zl)8}q=A)g4fHKq8O5TJ_i=LnF|69+t`EvB&p-buVbw>}KFzqv}{dKnXvQ6Th&C^rf z$0+~lab~_6(!&22|0ax+1|P%VR3&CB)y&0V{pxX*S`Mx z>nstwe*e?otGUld&1_M-rKIrH`Sm{2Sx50&hB`0OA`c2Ju^(SqmWPO>v@9lQ6 z*m+NE1IvNX)`nu)Ga(CtmWLki+O~nknsZMd7lTGkO3jMRQA+%Ob2RI#edpR2e*2NP zhrKV%k70^~ciz?rJAY}ZThi@*w{_V?3l7CLJS|?Y%j~pe?pgJ#Pu)}^l)pr*XV?^po)4BIq#le00iu?rZb7o_{HC6xh+>kjrrT)|AER_pL5i zE;?}O?zUIff*ID2pZ!^MqUv;K#XLuY8%ivyYZ+e%Dekc9J1oD(+@q3VZ;YjbqrgTN zyw(r0F4^vV(_Z$>7 zWIX-FZTIg3LHSVv6SA2jK3S`NO3s_Kl~qSoAW8JWj4#u@I2S0_$~R=$I;~o?k=fht zd{K4C^Ila^p@TuZ9hd%H`}Sb-mfVFOtdAbpwB&4In&fJ?g?lrugHlCoL+Uv*MxDL6 ztM>`d-^%q`cf%$JkIOcjdUxDinp7ixZQC&c!>w!+!g6|&xdpc5MKf;LcBA0e#*iIH zZ@S$VIJ2dhJ@&UXr%vtngtG@5ZaoGWd7O2>MNZUR^Ve0`yXNxUFkDyo>yOHvU*UGWoe8<{D#j6i| zS}8tlXKIt0`IT(S(%`x}@W= z54>*69N((B;mYO7J(kC$N&|e%&42Ki3s{t%JbNv~pv9!{WkdF-*d2$mu!oY3s|i`O96_acMFYpPw|_@``}+jC{6fXC6x#oIRV9rz@GqAla~Vi{kmm zDdzjj@4ana!|Z)KAbLua`yPKKQ{}C(t8&lm3g11?AWi%6tdj0e{{&a?CTJ=dJl!X+ zZ@66~qUvT?t@>Q~HStTb)Le7;HVJyf{Iz-CwASP7#9QAUmIoD`e%$?q{h-ISgr*Q^4E6u;F4gic`|2HhsQTi!9`j>t_XGKA zSvT+`+}v?({<+9wi{Cvi{+$!}XYZ4`*_B7;&Hq?)w6=TKwPo4D`Rjh~5b z?MB;?KMUfXnME4Uoc~5U@T>oi+OMl`h;L!liQK%Z=um0x@@*gAyI(rCjHRCOho7w5 zy_p-`cdIM@3}0q0v!P~}ko3dnKX06vA{!xb6VHOXXq-`X4x_|4+3!!?Z28`R32_x_WgDYpjLcr^;78 zFRcyson;KqbH3W^ETfTY-!wt)fRKZxsq*FrF|9kZSf*KYY<>AOXukSYo!2hG37)o- zudKa3i@`jheaivM8(s;A?cD5Rs-n!9wleBSCUaWvem=ulENT zdQb8QXD-ltF9a%Wm|34?@J9XRX%||x>8OFbXbywinryw#%XXRn@182MNNLUPlj$3$ zd3iEkyO^7IzM*oN)I?ndZ@%y)#^RM*db915)HnXrl3CzsH+_<@xy`1GO*hvvc?a^P z-AYzYOE_t_QbSg|PNyNV#WLsK?n~D=@6X=Ruq1MN*~@io8+Z~VrNH38q!I-!sf19) z6^qUXYAjKgEw@R%$DqUO7vkQaI{#wA7Pg+-4AU5{1fEyzT{$l!M>^uu-+w=4Hw~CD~)Booj)d~KuW*0JhGky3}$He1e`Eu3rgrDWw8=i#qG>Fdm_vY3i z>syUW_s-K>{w8$C&x{;*mgnV%8dDj(uf^vnMNVxt>6snV&{{a3?@Hy(Yk#a0)!&xN zG5Tz>>(+Ymo4=LGgM$AC|5EAa?h;_}i_PDld1Wm9a&9`nRORUw%nJw)FM7ls!>d z-K*tlL+e*;syu(QlX=>Ys8{?(+lzn48YRD!NaT6)ZQYsAfz=oH-*tVn@JOcBDn=dV zh&C_)m4wTc)W2Jb`oH2$;9PL{OH9!G(5Hv2jT;p?SLLN7cveaK{aYr&-nhjwy7%Sp zKaESR*niH}$lP#QZo{7Ne21>MuMO8aCmHH=B`;-s<+DrSSc65_S3k*I#g!Y5@#zHm zD!Kinh+*hvBYWB)25BbJ>Pk%Y6Xw}QfNGCT0Z+?j zZS`BX?6Li`SNra=NiJZ1&G1^VooS|WRuUj!g8oYo6`Il~K57?SV;B zQAM-1EopnbtKsS4Xt(v52i&H{2smdSpSaDeA$29&2AdC)Og4OxH%_~K%3Ab%B(G1F zRaz*YM+mD#$Y0Bsk3tyKe$6q;Ft%fLVcK;mY5i4?-{G#==Fgk99&pV&P<*)geXjk9 z#Kv2@+`qh+_IZ5D^FEaUc z-G0xte>T6)1f|*M-u_)^yj`n$&TfGNLc9q#6L}#Z!875i+>d;QC=bOHhG`5_-Z6U@ zws{<0dT@$X0x&ev%wnjR}}CkN%V zWOXUCCuo{SzRH&@VqUFf(er1wXhc!fHm&WpfnqL<3e%aqgYW1ZLh2&^YS-Mn{p#5?=;7TGxuz^tXrrt|N87!y|+JqYBm*AEpTi-FiXxV zD0<<7?JGCw&AQyV?00|V1F3>bA*&I}D% zBhxN;d}8n|mLC?e9giL6a+u9yaRc=rgaes$mR@uD-l+Pv;?LGv&ZMmzI&U+hITAc~ zEiY?SJvUD_BB*|i<%Sd=bMeLoz5}0_5|*%87%4nj&v2kDVdd7$8TVE1Gd4u?==}fv z=BHA^$&-H>rY)5^AoTm_550h6y=)PiR_+hoo4b0C@K$N>MpeyUQViaEV=u?-D_C-) zl>O+siN~Z~%$YQQ-<~;JMc3T)J21)2^zXbrLDp&WYKxyt%|6cRczM$Mngd6<@3K!j zSG{5FrvKWkUqFS}t;gRrSA`C7-o-oy*TV`Pg0K z$EP@BYi6Cu<6kbc)ZgCmMOpJ<_jk8dZ%(po@Z;G~^XaGQd*5pD_rm3<^MS_sDto8b%$fz zwtBK z^}i2X%I80}C#UQ}zo9fJ3GEEJrx5wPdwnxmQlQJ#3RP)yba!{UUSqnZ(Tr z+NFBCZ*QK~p!y-AHq!U!%x^3I-afWkUh{q6)XjOi`Zo{$m{Qd#%&fCiPj}^;?(bn* z-}bJ)d%j0{S_T7joS`q9;q>JE7tfpKv%Jy%%E%PKG*M{lFOwZ7duQ-5oo1M}z~ zJw9CE+`eG_jjnf$-sksSypp3H6V9UZbVBdBiwx7GKBV4{ys&RiI>Tv(X}VWGiyv65 z^&zi&<1vFz&A};oC+FBbJ!Y4DH)gpY$E!VQ+xT?KZIkbnXixigVaxJ=xsNaP-&nq= z$FS|&!wF}PFnHG-x%543s;#J*E@<$CVH!A7EM(OA{MDm??ThghU#}%Lsmh*RHbE61 zt69=lgqu_z2>Rt3bi0Hjf^9=cyI1q~e^GooJ2^uHgTAdfr~g-u;Z#t<$~$1nbnl(<(QftUr6F`5ST2I(6Z(zi&qS z@N8JJR@>?9t4np0Ma~xG8I zv6~DG4+I4reNq0Xx=%DB@4MnH-G-&=8?Jo+lm06;naTT@vr)#r&zYZRT4pd$yKhtH z7hEv4hTW4>?0^xd2`hc;njeEV@4h@w2i5$e^Y(pUJLt1^dP;a}@|MZ_*M4A~R#&^Y z*ymN9)R#TC+4Bxe+I!HNF=EsGRj1ndH{7>N`^%9ad0l{-XZaX6l?UJF z-1+Ze(GQodlueUf)Lgr=rS3mne zP~AGSD+`uPSbOS>%{O&Wz#?K?^}|Wet0pCvq^2`HxHq@fZuXr{mT5&*vJ3aM^F~PJ zr7T)7vrjd}d{&VD> z#pf~?%so8oCU1i0{*~{w+eQE0KDJQaGGK}MpDX|Rg8tSX+j_VC>-WEN8&y+Vqz+{M zt+l)$0xB9{`Ilju#e8#{O%@?H|K9m|=h>vH=xO^M{~p`cpn89?X+mdQ+53bQpV;qv zGRv7g4procsbXGOo3OI>dTqkW?E(_a*A{4Nb``jJPF|uuX>Y^R<9t!CPk32>mTgoF zOPKj~@^YT9k(v7Qu9qgPEZDXa95U-cA+sz$d~;azKIP)Rt?#ZK7vCv+@x=uO^&i@Y ztS)G%>X@6YwzvOuY9Cwn>&;qMuFI#bW!dnH+57rF!5xYA9~L*}&t3e9PxDd;f&}JG1Qg)l3m=8@6b#G(Y;>U`^qoHI9q(T>oG7GhSlPer)r769!*PAN2)2 zwQ66lZ=Aka+w8&hebeqA-{`l}_gkDXYs9L%dAt#e=G$#a?-2g6t=HYYD8ic4gm)GD z6p8g^^4IQv4^Pmny(AIw@0$7Yb}jiE>(kfWTvyHPHH!t*IXPfd(gDsvY#aXEj!kQ0 z`5yXXyU2uvZDFAdhCBB=`ZB+lnl@>bhir#gqw0O`b`8-dAx9nct=9hVj-R)zF7Wk% zAV={}-tjZv*=@LDB|WoYk5-@bq64QzBNoj*!YRJvG1-FUjqve)b+l zD;efY%rq(0Vc)=$kh$LZ-kIY;KbD$a_20j9{yN);g64U)2LJmez1pl5?#X*>id@~% zsM%+D{hpf!zujFoKde#JYgU!5%%;D`?|s^?UiY`7KVx0}Ds!1o59jR9iGOSBL%(TV z;`41(U1g}UZ~d9j&Ae$kz6OxMgf@&-)rD?`WPez)>*dlDb!vY*-q|+TKO4C6u}A#XzMHn^*0OEb&94)U}jUI@_%L?bB=((@k%-`->_p*~$}Kof!N6Yrwqiwu}1~ zaazRJtbbD}#o&F@ z-PrQevW329hRsuQ%|*~+@f=PHAY zy8OgkUY){up&rcT&vcs~vX=8P^s!v{)0nE(;c5S{BEz7e-aqF_VVvbcuLGRR9hpjW znY=AiS8n(B+p*4{OYTxznN-B1AD@gxe@%^2V2Ph8zvQ=S#R**@QKsYn4g}3Dn9Kdk z`so3ozZd=$?TTPpTJztf+Hk!!s}AQR)dM=`>{o8_zP1vYuJwhxH=p&>X@0HD`Fq7>CnWy=zMHlqb4|6*X&F|I@Lgp&>mD{5NMekWRq%wG~F7ohyz9H@LMy;84S=al0r#f>mIjv|twio&&O)M=Te-X4iR}Ab8-- zeY3j19rI^RS{?o^>WD~%|39;R-4b$&U5EXcy)9Xz^=@6icR=Xrb0v!jXB(EX{d;@p zEZ>~RevGH>{~qkg+$U)Qs=j4&vbQ&=GNv(wJw0?w=io6muZDGv1&0?-I%E)Bzb3$B z$1{GNzrWdT_wCiMU&kBq>D`;NQXbbERUdC$@&AY3o8{oLS!?@~OPU+IMeTD!YU>qd zT$P)3YL-l4>)m~;49ahBNaxQN>{4agap&5BNoAoKZS#-q|GmA}-=68j$3Sn_S8-xY z-u3t6la)8EKi!aSyy3}&X(@L#C!d#{SCPkX8Z?j$X^}HWY&xmxdaC1>&?*+!Zig4g z`Cjd0^1jM1e<52sVpjJ5r3!}T<~>ep1)6+xnY>rpU#d-*xjNcOTAXv`s)CNcjj6lz z=Ji~Eb|5Hgz2Khn99tj%u*vpa-k^GF?P`!EvzficZ{Jc)==`LUw@mzf@Ru+3&Z;L) z6nnSM)>Yf>|LX6h=kXQG+3aE)Ql)E~YZL7QSz0CUAG17_{`sx$%b@#$2ZCNj-usea zR&>U$Z0orSkkg+zPuF_$f0j+~Tb`vnCR;Ur?-WYxU9mBIP3xU!T9cghgKs?Co#yqo z=HB+wgq2>qLZ&LOfAf2LZ?Zj84C9BTMHf_m#cbw({qFZ_&~$7>clm=G4x0^^e`8*0buG01$pdfk8##Sb+~-FsdANRXWArn+5G3kX zV3r}y&|OIT?xwIP0u=uKPhzfJ~cl6f{P+oUGhVm9&C<;uU8-=ELO-8v=Vxk^OF zmWNLd2ral8b-%!%&slqBg7NQQyVGZRvt_MCINEtnrBy3roLS4qIPF;NO4$gjw+DR| zG@I;vqrb6zkNiuMo7-#N-q?BdcEFqEB{JZ4%S$$ieJO|2gHG#Jd-QFrx){qmx!(Jf z=ixgBjwYJ&mu}7a$vuDOqBU(co(hS@2`3q6FS;K-8?Hoeee+k0?dMJ}uZ)_*pZ=DB(gSSr?XaV%hNEPY-j$n& zejZ$@w4mgND0fxY2Hm48ABHF%Xq-KL(y`oR?e~5gCWg$i;QPLAj^3U(_ZaWHcK_bK zPAXwR9P4&LOU0ktcb{33FHsgF`IyDSL;uGEFNFyQOd6-!o{L>!X)3j=HA?-ZNW$C; z@xO1GJf2nQDbIbP(;+vbJU5%WD>lJyBA@sKH(hO}NS3`8HA?1^f2Y(kwyUp*^t>>| z%zfv_!}|pzvbo$giaKcLSxkDxAMNPIkQL$^b$>&i&>t3|OXte%wz}*!Xxf^5b8(?F zyY-nl4}WgA+jk;nQC-Fw{Y!qKm49|F<+#!&duK=8Q9t85K522AORv0|gU82U{9mO#5)4}WiYv)BS6;u1`>axFo$K5wS`KO$5EEZrW zhAa1Hwhu-7er5%9V4((!viJjd_E@L0!2Wc=G| znLQIJv=|~FCmVy#8Fg^un8uJ0$j$Iffx!?Oj|@6lpmTLWizrzXUbA>JYyclb+Ass+ z2u=nu@LAU2wW$+In^d9ZIh>J!E`II?A0F$lK}3h~gw;VIsCf!$umzv4;A4%!=f0-Q zO@NsvV9X8)$^(nUK&J_V&z9Zfw}FKLVqV8g=!#INbHS-QQaysH0bnuvOVQS&SQ2n1ak=^=1Gar^_G!T5m6+!^i+ir3V&WlVLdkN`{~%;fx@U9t_&R z!oUDZ0^L!tqKW}5VWbK~OD7F<=CNVUCBtSj|`&n!&L&O!>S--?uc#KjX3W4(g0|O`n z>7WGZiQGH#>nbeg8G*tz!QS|MxLpwBP(AulZqT`KpC6 zybllOzfPPh{CVZR?9iOjOV$3Gd+flm4i1Z^)L-BBY!S$--Di|*JNs+-#qF~{%WL@lI{vNe z_^Gq2Rl=>*?uVW6KG(cHJUw*7o12U5d&;)i*IWGkKKxykz;uOSqs3u=`fhQo&nxg&STnschHJ{jL)J()LeU=^cl^7t6oCxNrS@-=#HN z(@sw9xf<1RZW8;l`lXq_*7qlPE}x>6cu%YMh z(`(Or>O#-`TK+8h-%XV{PtD%WFWug<_uI;A(U0qX7Tf;)J`LnNrHHaSoZCeY&yK$` zEAIE2X=h9iPU@_yOPLQUJV8l@YudERoBY#0W|}xfyxnmzU-T6lNSx)m+zI=Sm`^5L%n-n69E}rK<<9KjWH3P`N zwt`c#OE)}EZC&ZHZpG%1m-iB8wj4LC&-uP{k$_(14w<+gD{d8aU!1mD-TX}4gdF3D zri7Vllb-wbS@-+zTP)%>hco@hCHB&9VHcZJn^YkgvB7oi(m4q;|H#a^vj6&K*ZPXD zmv=doQ}4gG_cg8VkjnG(+yRe$cwQ|YFq z37rX@37QdQ><5Lwl?wwXRR1Tumq~c@Vv)taqcw&5R$M%`Lh>3{7Pv{1tF3eB*0$Rl z<@~?JSyEPtsiNU zvf0t}c>3{dX=`@zBfr@h{y zQf~7#cDb^SChO^}#iCMM&o1B<*EKY|t#|3fb={uAi7h{q-zD*_->>kD|KBm0Bc^Sv zr&XF*z2mDpU-x0+#I|^Q_ zS?3o2kL6@Chqf7S$HZd3*@tdM&D#4{Zi$ufl#Wv@$KTD)chox9q_`+~yUefkrHbw! zI9lh`^96afZP~?RM@v6;~W}*>I%%VclxWugtsiZXD%g z+A;0?SN6J=!-_h$masVQ5)iU^sT%(8-cO!`mv86Y+q&Skm+gD&+I2-+V^(ZjJnj2I zmV-^Ht^17T?s%Kvt8;OK+xs19&)-grD-afVwl2F@|Gclo+vyqWDiS`ZImi6|wq4;& z_B3bLN2$EiCP!^JV%2TC+$6Y1Aa9lC#`$%pSik;V+;+&S*<}9ftn4MBkACqAe%&4N ztCHvW;!ib~YwiVJo~nPY>Z#`YtJQVkHs|F;nm-==V0WS^Rp_t*&*%QVn;Ry|+}!k{ z>tpxTMfy^PI$YCc&a9j<6Asd6?c8mktr?bnvGVk~f*rc+wM*xh*p;N-(!2e1!@bV$ zha{4F438bUbjV0{s%6Oii94nQB(4;l8W^bgF@Dk0d~bPS&sd=d7Zb+s#`UKSPd@Kn z8m>9hZBdU)%1MWqpvIeAiJVn>b$>scoT!pzrk38mbRFmY=zVnpr;g?7pPedpSibH) zzh+^t+9Pd_(~I+G^4_fYx%u9n^8t>Nd*5H@SrNsl+Wac`W*YZ5{Q%L^_bYEEEIJ^o zpPp~9^ZV_t)}@lniJb*WGTM?J>+X2U^%(D(YrcQdNo}UdvCFKTMa%w2Up#3sQ6Qva zdr*Xtn&C$^)fsb^9&hk?b24pR^|CuVIM&x*yzCj?8f2on;~aPCfgL|vFB(2O_WYFW zW*>vakL*8xKK1?hj>m-;4qXBVgG6#q>(bVxUVAnj5;8ntsM@Nw_tEi|0&Uq7lUtG( zok~3T+1d3)jD85`D*oFFI;MsmLB-vzs#m2~e!SzZ^pGX$x7rD#wNG-QB8ucDJ@)qx zaJ<*`mwD>J(0}uO_O&i;O+6eDap~9AtS7!#7Hat?e?Q$7U2OL;PCw(%vx$@c7$ ze|)vv4<4h8*`JqN>aCJDjp$yrVQbXAjRCGs)wb8TQ+(~Oai8|i;%l$!oh3dkBslBC z`>U;~bxXCbw`QN+y71My6~Bu9XLkud;5fS{_x|@P^9^_RuAjr1e*gYNFJaGh<{CO* zEj-~ZS*ET%OpHWg3Ot^XRAe->*OCqlr1x!-Z=69|IWYaO~;(` zOS!*0Gle!EiM*NYG~-{%^q>3RdzPmORZd#reDBO;yXb6bYoV&2#~qXWnxD*_H$A7d zOYNS(zW?(N2}Q1P+T@qGvnn`IX`-op@$IM2LW?%MOT71YW6JM8x&I#)PF6AedM7~e z@{SEp%AFLRgd8o%cU>iOdt<%I;$tQ1+iO)Oq@?c`n6S}M=w+rS- zEx&6#J394j#OmZ3VY06_PygB`ZgtOm-<$RvnWDcwukZH?H0_GDV)!7VH%sQo^RGA0 zoG*EMEN9OTPyH&LU1iUj^ZWK?-~AV4?(emETKT(g|9)LrdFPzq|Dtmx%!iCV{>!jO zPGvoyj0V_&X~|p^(NBe%+oDxy1#5r zZpgSQ=@~h-|9qWRyht?*2=l+lY(Y@Rdqh=Hi(+S~iXd?#guN3OZBDBohshAkURe7U?+f>)j@ zeYjZuSKv&x==tWdCYy{zBA0ENQkZq8Wocim?aZ8DlZnpFhGFWFFMnR%=y#vdWJms{ z?9NxkKiW+{Zmc}{ZT1$f`R6#qrg2Jedvkk(3P4cgi%q-ATqrozGI;aRMIP7JyYL0F z9xu!675mNiWr7Op~*k` z+%;7l%j+txpBEJMxyWyt_G*XCPFGgn*h8Pp`!&>)GXE}HWc<7M$PAan)cFq5)>Gf| za-8mv5TExi;fuuk`o*^w{Z%$KeYp1O$)ho!zsv#U?aY7(qe3-l`I_tc=F{G6w9Lra z`ubVWd7fh)^Db99^5MI%h!I&bU(j;b%IW{U0zjP-omV~ zebJSFp4?S&-w+kCEJ;U4SEttylH4OAmbq`27oQg88ex3q!Sz>%Ul;~XoF1^#^Y*qE zg*@#+4+SF6_h-5O^m*B0lx=WLRcCd3*OFIq8%DB-Dv+KQ@$i!=`FXj zgCd@7y$~$$duGDi9bs?lmv=A!JYTp_-~l-OIwr0Zy=)z461$bv=CzM_*b8H^l*gM> zYv=YX7Ir!26%p3zsd6(x*5~h;)$_l5m9Mz)ZTFUW=l{)lJ*oG({kz3>6jY+ z{;}KkoUi5zj^6cGZ_e2u!?*O*X12u2CucY9J)-jaz0BYG>r+hkY}%NY6!9x2{_mAJ z_a66hoxbB0*I0IDrbFBItp96Lr}IvF$$5PmCllB|jEOr_X8SreUHh=5>aEwpq{`jgljiMTW4mthH65MH z1|X9Ve&M{e?e3>TF`0U`I=lACzrLg@FP zelc}kZ`tssT$rceLk!bu!DSm#++; z_|fj%%kL#6O;cN!ZUp64sNWvD0_Sd|KeNKbQ7|9>hifyxBUJ5eG;$G4bAJ{ zI=@$e!j3l~ap&LG%L~sX?CJ1zyR^zDmc9H=tLodrmrN6+1QSoP7}feu`1@&|P~>c* z=dr3^zb{_9V_WEUP%09GC8EtQHf-3k;m+qSN#7N{ts7JVBa#)KWC>5(eD_H1RehbM zU50x_H+UHeJu+qUECV^h{)3x&ij&sH$9ul41O=UNM1;|^Ym)u@F87|W%C%gT{HB8Y zbi8GW)9u6I;?q8UtyvoYk!~KRdcGjICU4eDPitPRUpzai!A*_=N=J%h!j&Ajpd)y!Al z*Ts~3hF!XJ?a-!!f8|T=zBnJA@3s5*uD=P1nTa z!t zdC`CRH(ed+JOA}%S6sa`XK`oC*Ylu)W%hwXLXl=YYqBHQ*D$YnFWtZY_T{f_k+Umu zTAymq|N3d(Y{QDO)f;@QzA>+x%X!`2+Dgstf8j^#v;5g?nZk8S_xHb>xs$c^Y4Ce- zx$eHuvm7X42dZC#CNB%|Keq34^>H7)S<^ObshX|M^?%K&SVB>u$MxVdJW4neE%LYQe|$<6DCxvO*JVKCaNcwaph2w6E*EWOu8zd#;O$SoVItS-sR2E}d!eL5(S(B*Gy!t^KZr zu*pA@dzP?-w^SiF7{Z6MZ>iZ`m|qMJ->LBk%ing-|X0T>gOz< z+ie$pd0GC8*1gAK(>8pq42o0@4Y_l_vYvnLI#5v=91(HI?76?VpTf*rcL5o6rpy03HRy0>!*Ddp4p;o@b1~ZdV_CsYwO#8H+X7# z94ZQnw-vlp7tv2V~d3{H}#jzWe3HMF!$sc>XpmiyWgC z5u3fCrFq?K&d_Eq_HBlJhm33mZUwb!PtaMmX_1$LujedKYK_0H?cF&$eunbO98h9a z5S!M}u|~>c`wioc1l>cMLJNC-?k|dX7u6kd>hqpU6SjT&%6WQ~V|c@vvdHy@e`VEU zBa|G9RUf^MpOW8u%0;nnrPqf1cSjfH7RPG0abfmPE*%p3_pfK=1feynm#jS1Ubb-q%e7FSUT*n$@th~6rZgwsk~-Rxf9uuUQ*OC}>qNV|JtK_Pol&hw z+{F^}p+G${;!yJYvgTv+=S?W{i^w-By~KZg&6(X#&T@X2k`8mS{xD`!?JcvFU8ke#J&&HcXnvl}u7As!WBRfdfq!Rg;o6j!sHywm zUb^pT4~64fcys0+Dta7Z-J*K@tMzf8wym?)iXK;`;`bl7*WG`8cVd=IpQ&{8{Pe^~*8XO( zX&eWxLp#!d-=VfqSkAv{5f4~)9*4RYTi|; zSU%xGmUq;2rR>c2QFXjtJNrKA1wM^Ym|uCm%XiZwDP_gQ*CVg%U-LS#Xshx33Vq** zYh~yqVw;O_r{$!O`MR&$&G)@w*V^!HPm){e)kSTu_FR59lUGIE{>QrhKaT4rE?N4h z#Abc|60eWu{d0J>urE=1{yCL-()~aGb@zQ!3CfJExb0p1@a+0ujbE=uEPgx5-8$ss zMYnaV&yW4tkmfQ?;rrYbUoCDJuq|}{s^m+1O}e z*LhpL`4hcvdgCo7tK*SN}0Xi z>3px5(6!vsx_Z}2=EK#^QMCdc{P$I^O8e(r+aHqr-~A#xD`>)lLx-_Z_2aD@6{rl1RSXPF-VO;T|IT0H^!U`1 z)eqVGjeYIQj0uB=%p^(xLLJwSUy4l{>N^eVf}T_3-K zPpo2KP}so2GVRN}P#)Qpwd#{De7IKG9Mzbr-guzs(yE~7e15oz2G!1%bG$;GOWx#| z&O7$Gc4xEg$7D4btE;~JgoDNHi-`LK~&Cnph z(e$_cZ$s|Gr)?` zO9lo((Dd0HMhtC|3=AkUZw;bgtH9bA(I(d<6hRYYD7Kvzh0T5wjsBw*BT1(3hN z`oJ@kAQlHG$Ak5OCp1Aqq!|jC9|G%3%{Hj>-tcEGGl<8)aBx$U)c(l{me(c!Wh^wd z3*w*l_S=iEET_Y=e`_B6WWxkiRrJ|s!<^SXRjLtY)3?Rhyg&G9HakR0<6zM~>t7F@ z%1eGsPS_dAJ#BW=(|b^DtNy)sTUPxejdNP}-ro0Hq7GmEd2YjQ3kg&24K}ix^J5}s z+H5h7Fk?A=7h)EJQLN={$^RAmY=0iC$*)=Qihuvztln@Toz+WE)+{%kc}r4f`7*yd zd!HXP;y=-Bv}q>$bM8;BKM&P>JacNJc-7vw*NYak{@ZXjkAHf!_v5e668`Lcv(Y#A z?VP&KJw9AH$K1-TV&m?!LM+o;GnZFCR=-w%uKwQqg~oQ1{!QnbAvZ(sMi%e$OPrUv zFZ1%t+^H~-Ei{n0TNT%oDmMdaTU`F*&YM?zpD+I{&3^0c$2WHp?#z1W$96M%cHxDz z`X7JO(qCu9DTxHw0ZQfxM<<0LS?j!G` z?xXKxen(R0`t|Mk7WxN`_|J*^_~+j5^1G^ffMd_6cRv z`@(0%{fe`SSJSrdn;N}(<~koFPx{#Vxcm6ql)MnXZq6rrYj?s-_NR@N1)+2I?7V4H zcl^$;jn&hqwehFR=SFfzFVpef;*)r`>c$=iy$3#fd}^UF_icU2-Y<-wtU zpL{FoPmScBT{X+wR9t*}n%9;y78!i=y3Ice&D?c{n-Sub-{H$NFMq%E^V082e=q&N z#8_OOx+~$=gF}Zt%q;tTB>CyTQwL_0UMCcvP1%+7E6M7y9Lwq8=}#S>%AMXMw#_H<*{3sa zmgrtIQZ;=%|7PUuf|-YA9X|dj;fhZwQdoIB_I~gE-TVJ((}^z|Rl}AEFB4xTy-a?Y za^1Ior-Gl?)TjQ835(~Rt$F&&#k9}-cDqg+DbG`lN_%6xJFED@%y&Pg%2-X^dU>YL zQ`N4p>|!a1n{UK>o%g=){rvTJb#_PyU1s)P3samscLD%IZ_V7TA-td3Qvg!NGHy6EW z-LkJ_|CjwG2fiFAIr!yZ$)PWYN)CTHJooa{m))tmimo5rWS2T~?q+%S)8Fm7rfDvd z{5;3<$L8NbfyD%HRmAXe-!@Y?Quy8F^ML2JVm0*=vmAEVMSK`jyMemnsdvCa7m>StXO>?Q_`!jyGu3VPU$xbRh@$%{0-^C9i zB4^r&RB}R0OsLn2)%SXR+9dT?yj21u_N*SxJbCJu)(zWP>&`r%WR&{T^`g=4jN-;L zzLH&A?u9){1!eQ8&hBnbkVMz;cfH9zkI&i@jcg5K4QdVMZjjTyEPPq?yGGcwgr7$* z8kI+_`)d}nZhij!fJdpEGb9;%W&S*Z=w+C4On z{vLU3>d#Ltr)5;0emQHteckDYWo&1<#UU;aU`k!WZsxT@CwOY%GDWj?pWyXq#0S$;Um0a38x`~IpGcU;%pd0i0MKjY}iZfNmw z;l|5LD|RkC?yNVy|D{HkYyb z(*62=P4VkpTZIf>KP+0m2ww3wr7r(``Zinh#kCuE{raaHTKef3sK8N8m{t*a=&87? z_rXtdKyCuHPApAJAHAC~@7|v5va;9b(mwx?RZUWhn^rJ$>c)5>U471jpVq*X?%ZTl z`QpIAUiRg`9!j;;qPoI5{%^BR!M(1XIRDL8 zvw164U3?6x8uWB|<^2wTGn|m#?6dYelrK-a`>xvjs=98uX{si)9n+9t3ChTz8WU8J zg_>4cF>bzV_c7v7>M}j!!kL+;T~7N%sYTV*EPwiO^{K2I#j`evfR=_aFfcf5c=L5z z{Oou6GBaNG#m|)QiTBzQ6Cq(YxnO4JX`g7b*@vr*GG~6>u=8?JuKqF|@bWSSP(v5g zVAyRtZ|$m&(xCQ?IosiIVLeb$JT+sc=jk==XXj1>1!n(p9sbzK2{ScM7wD7m)dv7^Yb>mLv%&Qw_ZigB%~EE;)0Q(o%Z=pz{cLvh^{dwpaZO8y z7Sb%IuU)FxyXqKw^xBW7a^Ab_2ZczQZO*$bI>FP5Umt&Wcc)lc`1c4{Qd5X9V}JZ! z_nX)coU@}Ttd`i$+gcDoybI{uu~(xI(9meW7qy=Xss?%Ktl z_kIMZ>28)YD~~$Idu(l4wT<=N`xX9XX1i;G1IH&?2>PBc!vna4kUv)Hz)Qs=%_-CnO6@vTbQAJ*92u$<-e zwS+&{-n`71sd;)$+uC1;^QUj!@Ko;f%&FhwUX;}Rd~)j4lZ7m&FW1-3PS}|ZiUZKf zs<6KoZ@>Na;jB;OGrQAkT64Fyt>&E8-5j|2uH4>xF}JEVvYbvUuIPtFTeb6s7^(e} zHBSrai0_*G{+ZnAo#)c>7w+E~(3HA*>(48fE`4}7_h6APXh9;V3BFe$;#_H){VCgN z^I1-xoS9&0FR}khRxUTRiC$pGIqk9W!J=*Di{>ZX%*yqHOE_{)3pK5b+OWst@Av)x z4!8gR`Fj7ap8x;v*WdYgKk)xgk>{^}fBJm>^XpH)e=YlS22xZUEGjgpGCx?f&G=v8 z=FO!c8~)gSd0t=k=dj!2kCOjCp0D5ec>m&mpEb{2|Niv({O8x7e*gM)9wWp4#P@s_ zYft>Q==(qa!2h2t9$Ol}a1;eIq;Rid$Q4+1KsV=L-gX78#x7laDq0lwEg&d{J*Rg!viah18GIFETFZ^ zYe3pSD?QD#7!QVEl^ZWnU zGb9wXKX6<7nW5pe0z=NR(tr(T(zE?Q%b5=fG2B_f%5dEgydd>p5fiFuOA~&6{M-Hi z$M5s^|NZ^*Illgk+`avFy9?j`d2{F5%A1|Z?%)2`zdv_%>BSGHm+!rK?_INw^v!>N zcJE(z_xbI}_ZbI`ZvTGsaqZu)@%t*$?83HfGzhy}GvDaipNccbyZ-z=JX<}vZ|jE} z`>XdF|H@Z8u6bPh^?3_P{m19dNB8|aanR_srA68`LwV53qKKz|Qg5ESU()yd;NxFc z^HqPF%cpxG`+RA_P6aE~H)ohH1w}qfc+k_b`^dU|@6|$hIi>~PIyQs-+O>)qxoh79 zr|f)JyW0Kmn#FeKHr)3)7vB_{dS6IbIF+64^r@Y%f6gsET^%LTA?tSEH zQf1f?$jz`0vOKyo=&FTyq?w`S#$3DXwK}KOIo3(&2rGPkf9l@8TPLedZ;HQre9^Xi zOSQMjQ1@u8^nV}md1n7s=p&G>zke)bKbzh z07+~+pO%?&q->2No0+0btp z=Y6jJ+hWy-w3&a*_>NuaQI6>2MfU1-g$Tb799MoNN1awQyqNv6nn%2KTg|qt4R88l z?@rvZp+9n;zzMGYNxJiz_xvt){BU;HuVb&~{=Ff}7|yj{w$b!?{?vq>8YOAAcYjAz zPsvfr*j7F@VWx$qL7@gGN>pBxPn6saUb-`S!4T+umoesiLM2wPjn|Hf4R# zVp+qo;gNEfwTM$nnuS|R^)eSQ&sskrCV>9C|YMTNA#cLTTCahspo zZ1Va0seJ9*zyGo*2^KOQBP>08>1U<>{`lt_ zn)xi{TW-(sX=y)SRjz5dqR43YJH+z(_jfU|FDvZ!)Yo39;JkRbT?W;_n9B)~^>Z>3 zV{cBM_ubk|gXQ#|=8Yo!x6@0)(yX7=&iuJYaniSW|NFkT_h~=g`e)lRcI3>p&T)g! zo2;o4+wBf+`qy{d%jWbhtM8dLoVCvVwTCNnEYjaT-(T?0ZSwafu4xJpOb6QT!Ah>> z{L>C!-)nbpll6gZ{nn8i&P2@JtaLlw+HK$UYB^!=<5hd|w*41iIW3^$2u_R3*&ALj zc&z{$PSlLJ_I;yW-oLr8CT^&-$$tH@d)nhE$6xeGeD=CMe}AQl4r(l{i{0S!L0UUP z?TN3+VWVUn+i%ae`E8wZyk`B+i5n_I-gP}rZOcB)<9$$w#hamEu^2-PJEOw}5gqd% zveU}*<0E&iTUML(Kz3T|=Wz9irUXugyeoeE4A&j6J2J3%9}L>ChJ8hm&FM7SD%(&raS#TH^~1Dup)nH!q2;Gr=_o-Tfb#~yxrTg zelpgVcCM>2i+}gy;=I%@yFHag)!(bs@2@`pBd4b_a$l9)=K8f0w+JW|@AGACS-W>LmoV$<+fyCPE6P%@OkdA_0bLg3R$v>zaSEwl~XsneoubST^l=>)|swHfPS9Q8}q#;F)H} zWZWIO%W3ko^0cyDaWxNxf2aKmx1AepZTjlv*`6P~*1n;!u~TQ(r>5l{3i4dI4p z*;%kg;iXSzu;STO9^Q5O-nQQJBX1u!UFG4e#Pik0b;=*_T>0ng{+(R@)u#4;8k4v{ zrPZ1*oBblJWI)*W_>-ysP0!pdugj3%qg3K|Pt@vo9@ppF`;Xn9ebKG`uz{wulK2(xX#MM!`|TI;s&#AM#|J;r+M)Md_|uGPqqNiK z9+#`@gqwgIB{r|lq-D|Oj+4CqH?GVrUGvj?VQogP#^i4kS0CTg%hzt5CEIs0yH2~r z@1CU7cF|qJ)rZc^{co`(+xnLKx|f${K9l$Wc7S6C1joQ6WTJ753+iqKSZhbG# zC@Guxaz;dT-i-VIzA^-!nGtl5;n<70)ejyOq@A^N-o5^??ACv8|K7e}w`HHd{EVG{ z_iWi&{GDHH{l!~9=Wd))o@7-0-CFwJJ>ilg&C}a|9^WsMwtY{hWksuX!OPv;PCjaS z(wo1YeE50+v-Gn3eY2~72!Hz=TW+v$>+|D@?;5=iU0G&daOl&T!`EHgg{S#NSS51a zR84t$plXVXQkt@M>y|TuUn8#C{ZckhT>0KkIx@2U(4~h3hm3U2w1z}hq*)94t!r7E zaH#0ny70C|#*#23mc=x=cZ-9r_YLU5; ziXlM{3*6Tx|GRjeRkWnN(|h`}gjq5F<)2#5mFwoUJo8>F*!)kZ@V#6&*^L}8I)bnN zec4s|!0-N#?TnJIU&OSpZ|2Ln`y%a3%h|$p))K7Mvv~bZ&(oi~;Y;bohb>D%9$DtX z!F6-$T#uGDPyQ^uqusjTZqa9d@AI1{Y+Clh@8$6?-`ATuy?tOgHK***rH2`Ii7Wk9 zep^_&u4Apkp-C@hgdEI}zFzj(FLYUcWn=$;pO>wjcYH<4vgTiv(!H{UHR0Ly^|@De z7rL*tmc8x1E>hA-ofA?*DdHBy=PVdEAzWu8?YE>YqZF?v}YIb`0533gw zOdV3*9qd<$J&o8x2Q5q2clV`OB)$Z>yPHKm(`|K!cZZ&bk#(g+zW(_n zRV`1=*+1j%v?s6rb&}=R@}?b2=D&P@blc7Q^>gMtt>v#O|Mc&PlwZU;1B*EuMB1;M z51Vl5X^W~$z|0N7Ld=_I*h(HcRHGqScCIh=wkNB`u`T_71tq^rJv=J0_`hUvs!{E2 z?{nwOKb=_q+w)iOZ0YZHcLHDgxhuUm*15swuU;ot#@pYbf?tkRTPAA0{@Ydh;9Op{ zbaK0)N!$7NA4)GAj1ryv?#jWK?+W@z@qV=PnpRZ%?(p9o`v2$p{E+H6 zy|4PyyNdQd=Jo7$`=q(sg{LivTe8nV z&z`#|3yJ&Pm+d__cgLeUAB{J?vyKl|?tgJ_o>_cdnmH4tR z%3W@Ga{0FkWlnGRroJ7IXD=*%#CI?IcQRi$4Es=e!Et4~qtVWL9!qwnJk4aqDQ5C^)pKk?qCvW{$p7uRhED z^a)5Te|kfBn!oLwhjx9f%jGUfvbC3gt@OVq9)O@l zp_RWWZ$-)ZHzn-rlIJ{ly`b4wLiFNed!xA1rR}Zi@6G3$m;Q}+$dfX%vPgUhD*juR z9)4J0A>$XpwZeNzi__=sW!8zE2d7FTa?DJ*Y52NjX|qS3>d~`;2VB#mIzGl*7(VK| zWm9lS=-I@KN>C~H#`$^tdTuS@?Eh=63uKd-Tf85^3A_Ro=Wlf5(?O4!bVzWY^TWYgg_U=mNzsG)`dmnLLHFvqin)FwTJRbYR%b4$pU-Yv` zXxilP|L5=5u*cVbv)=K&_KlpM>ffig_8x%k>bKlcs|wS39@(VK*(efzm7u_ZO^ucm%t=e;GLf1G*y{OY~N(^j49n&y33 zv+w)V206=(W{0EFugnHjcn=~YrZ3B{;*&0(X599pZiif^gywUBi@cx;bw+$Ot8~9< zd!=sp6ZzLFTPKO<-l)DWzbx+u_q*u$SbgS1&J%|&D^ICiVZ(lVT4~$XdA(PQ`RgQp z&;6>gI5$-K(4?|WZaTlTPjs0SAFuqy}>1b+*&FSny~2!rCWNrhZd59AfZM)CpW?SYx8( zGhh19(peHw#e9Dm4?Oa_va{Wu(NH4fR{OQ(g45E=zQsR3an;*Oo_S`Ks_MBko?ZLO zBC;2YZC3wxzp{Aw;^PS|FSb^??>XFYWZhYZtA)4AeJ|J*<-W|z<*BH>uXT5&v&2E~ zdz^OPFC2F^{Qu-Z_WYZo%gp|*n=`+%N6n#p>*FQn_1mTYDKF6~=Hp{KZE4Y>!n@Y% z;O^E9vM+ixhBLzFJ5_YdHY_K zxf{+{#4oFv9_2q>L#mE>?cdcutPcp@C|_rhvRWpkrn>g|W0})y?R&d6u)Ij$%jB3= z^1$o=>bzv7<-gA*D+gUoGe2gQ?cW&?pIi7hM}Eha_rFwEe)!!t^X4ZuTeUNHzRud0 zEb;k|Y*AdP`N_P4#eA18NK?6Tdim5-vP z-nX`jY~{Vpwbs_2Q+PV($1q_~bbH31EV8xz5OK{xRB?{tLig2k?x2hh_9N})xxb}!JE+TB?U35zT%M^9&hXk2|38H z^-Y_~sZ%w+^Ag|f`)v5*%wNmOtN%lqHeb3Bu*sz?^qah0twqVdufG;-@;M}w>^Z;t zh4|r}o+=i8A6h#097@qiYI($>A-s26Xi{d;OXZ%oSrfmwugU*WZ5cAul4|HwV4wIn~Suh4z1@U%7BhDNIn zRBqOqyWxy!yx#ZSDVP1%>&&^g>xVSULJny)oxfgFW?Kbse(m4&$Lf*tDrG6z9gDg< z^Ck;r-nZMb?M;MI!3EYczvL#G-o5#Fq5GoRb$ls{YQIR7xU0Gr)J3x}1d<&iY+ zrq+u#xx$@?IKA1AET}W@p4f3?PVFXx{5^S3s_*Y_J1hDvza-JGV`J5eDoYOUU(wHZ z-;laF_lB&NtC{7-X-~JWy~OElmUuDl?u;d~y-b9zD!mAs^>)F}*GcETSDk#tunYy$oQ69j1Q#(WPW6j;HdDV?Xxf4d+vHcw ztwIxB@}6Cv@8%Ve6{1laX?|#u#!Zvj^|#MGnBm&B)Xx3o9v`+Ik(YD!tbT6Ar>;|b z_wYU0fZLNgHi#UW^i}idwU!;LrmmZ=dg#-+`jF`0lU{F{CMbOIPP?r&cSFmBw3?NB zxehKnzKuUdEU4vmwp;tN_1!yfJuf)4DGXjAEqbwPlX>my?nkF~YTR8Csrr>?t7QS7 zo=u^D$H_Og@3#p~o3yKW7u%BB%(uVtJh|pDJ3%j7I*3`c{ zwf_Ba34f5_?Ab1&^40&2fcB0&(ZrQbN2E0M3mZQ11#x;eT#UH&{Ysq-|Dm9z3E$OH zlz038UUzZsjl=gHjx^~0sFpWL<$tdHYR&PDyq12uf`guxE$?sX=ty=tCUovoMAx;; zw>_RprM<7z|E}q+exlrHmEo)%o1>O09Lm-(EuYbx#H4tz{M?_z=gPCb9rbv9?ql?? zb?we`zVj!YW0O_S`LN>f4R?v(d!ow(gcrKLdOo8-w*1PvkmGCn7&-n+pS{1YbHf>f z_+|Ce%??ef3<%Di^~$=<>1*qsUza&JPj>{*-*voHNvAO5(y(o16Ty4KM?m8c6n_eWJsr<7qL%i7j?bg?_eb0oa@m)Q>BGMw` zosw@3SK>;iXG~^N2Hy?#8@G4(YDw~DEqeV_K_~Kab;}F0pMTi{ji&!Q?3TD!@o$Fb zs*p?F&$g_c#Nln0`Y3;bo9kP~XD?=4i2EYJ5q~T&GE+LIY~AHQrH19dW&9$pb!{jC zbu|xjdar%UI8k}`^+jz#k-3d8qvtKxd{ALm79hpx&C{~oeSPo6_9KfVMI@9bc36ts zSrK^IZdajai>jc4(whl7p;dv$Tm{w7MzJPl7Q`&mzWK%`wAt(O*#|NzUimT{-j|pi zL!y7NE5~Qg2)ou7)X1RSpHN%>Wp}46_e+`F)3@rg_nNVkT{E1;@bY!if@JnbPC*8@ z%wtM!d!2a1prIOF@MYDM-olP^aWCiGiaEZ~U!J2=p}0cdQC~XvPT=bn)z^J}f-(!r z4jKKgnfWjF7KisU`H81I3ZD7+R?dyDm(or2T&phlYgJf_>g?{28PC=A?5`Wt8Cj&2 z=Eljczj!7})!jY&=E)fT&d7DkbHoznuJM+tk{<$vv)SEaH3S->BVa zf410WS96|0sPwX*uank+ikOl^o5Y|+j7;;S7*~d_&vWl~O)V{4r1`9@PTehHR=HQ! zl{P8AEQOUNez_AL^R#5_u{TU>ZP@rW>1apj%v}%lby`$+dlyT7c`CSU(joTaifwVc zoZdV#)vp$_9NJV{(Y(xbZP}K0zvc-Yn7B*#xr@gkci(g6qC7X(B%8=eyjnJ8k416D z?2X^V&PD8XD!rmsyU$>~${|oG)U0-$|Bv3!xsOYj+pV+aZjgDgX7=4*X~Ky!>-U}U zk6`jJS*vXMA$tBD>8cjHbIa{}=WS5oX7!h?pI)k{Q&@5Oj@y-W3v+MEZth9E>!Qk1 zI(6Sk3saZb9&5dwzxW=0*s`?wXSDKfpU=vHxhq?qo{3$*rYqulh8@SMKfCU|;PifG z^z0Ew<1eN<_9YLlnuXmcIx}0JLurfk^4!$8_aE$CILvQFTFIUItqy z*N?sd)tNKr!c(%0jD=~E!d>1a4O*WJkF$C0;s~r%Yh`QNpmerRV4Bq2gzh5?bgRz% zNsjMY()V}!v@7Wr@!BG(A|JSw1O=YYShv27r|&w~A)&uUfbTUEc8Odvmbje~H*Q7kz2Io~^YV zRqJ|oO|errI{9VflKYBJj!)jOWwPX?PePg!{qI(k9ui{cw3}9W*dgNC9R1J2(_}tt z`X02qkh$W%@ayurznMQtr*>(3<#fcyP2z=vf{ew=2g<6vob#S-kvkNrd-~(X-)YS*0~F- z9#3yr6LjYD5?xnzZ_mx$d#}I>k2G~1Sx}#$;E<84$LSTB1tyO+%%~4~muvRSAZS$& z)2q-K9{j$E(N9a3zH`=^D5|KVSf*=z&$os7e3)azrRjgB$=;b=ey(~W$1zV{59xW+ z_ou7qTvqgp?+puCEWLQ@{@I@9bKYHfo)B(+r1+nq)tnV^`%25EhI?&a`ZAXN>bFJw zYgG20Ojv81xYHorY|G61FVAoKavKz`lX~SJrKm(*a)?L+2W-^jOPeIw)R(&D_xRKc zJ-H%yRC8D7>#Lm`mi)_N%2bek@W~=gSB3HX?fd2Z^-HxLwQp3t4r<}8Si$WPGW}sh z@2bth(}Jb{AJ})z_f6Q{{cOAD`IdW3TcrFt)9P0HwN|->d}(pFS0(LrE>Dn>TvTT-NASZW+Y7hc4t$jQ zYm(NYn*2ZS^w!oB)81c~-Rc(+c4c2`aYN&VGdA(h)O8q3tGB+(e`0Z|KU3r=$JgZ- zg{RFG-d?=+mf*Bs`#gSbGpSvF;^B;^wYSYUWcP=j^ym!xH$O&qT3G)IDVbM6@6SGL zS<2kxwMb3ZYO_E>*`c7|8PZ-8GT!c$dQm0GdAhao+UJ;GJg;5iZhPJ9owmvGefU8) z$pjbxf0)x-T_>9fGze;u7};`dU%X?;tl)Jz;a65FrOe2E{&W@Bp&*(6 zP8PEkJn5WwUhiTer|H_34OhNhOmg$SUX0$l@+q6?w?_8#baNJisf6_|rW>A>SoDC%j)|tKgyQ*Gr~fYmuH4F(k6PdI)FVf`(p)R^ zz0anY2|?3&ou-_0+`d4!C%r_lM8#}s<`jjN)X&$C-T@bcv*86{(Efn#75oB5UJ+q} zElUifmt{nWs_NYIoaYnq>b+=Reev$A7asm{7MZi1ne%k(&s`cTC)}2pcag*5-rEWnj_emuz4`p~pO&R(=HK1sGUH$2eZwn4uSMr%|NOfAq2M%&TJd>% z?E5a?E?^6051hMatFq44YoSiI*;#$51!rcJavkf-WDd*z-v8s)n$>20Z$)3Wlpea| z=*A~{O|fLNkcZ2>Gr!pmZ7Sjw@`?Sud)B9|;5?bIII!j~!*|waTdv)kA6S0DhCNVw zww;g2HO|SoQ|E6Gu|IwF+gFaDzTzO3Lz{lSPI?Ebkk23nKhB&q+4wEyZec{^ow+Ag zC30~k)*ZU^;KzD~wzc1uFYA$=_Tb7I-NelMxu@^X@{17Ksc?R~5Br~4K5nJ+46TYD z{Bd|x%)<6IH?hOX>(xeOotvq1zr4-5Yg>NBDpB)-uiu0U;py|a3zx0@@aA*sTTbtp z(T_@PwuXsDa=5Lt*Su8fVx8p~ac=IkUCZmYPDyVskY_(6^x)63^_BClZLL1Kw7c}s zB}Pkgo=o|j*Zd+T8ClydRo|AwTVi{8>xHxJyFEYt+AnaZVrI+JSyBSif}%HN8E?3j z?n*X$- zN!6jEvzAM4PPtX9=1{ge@qWgkNp@YFyKOqHRDUr}XzR>=DIvsO^D`n!JK0q5o8c_^ zEuAagt8UNjT~^BV=El3il>b_CPp9-1D(grVdwUC)Wz79@b1f)^7;y#dH&Xj56WOe& zV|;%4Vs)M6`Bi?IA}cLsT}*acZEu;Vxw5>xJx=jzQq`|7Rl?JL^`5o=B>AjB^#bqf zq{$CjmOAIIP~veFd&b+cbm9!532D*Z`cJNZ&GLv?_ip3PquZAANBypI?20hs4LmL^ zyQpcyne6D7yCj0W1-8e zLq?YtS=Xk#;W*v0F<))&qsKoE7OD4xT3sR!RJta1I7Ot*(|7leV4A18ty!KuYyFmQ z|7;fjd(x8n{D`bq#HqdSU;Y34;+U%IH-*O@5n3HJd2fGunyji!db|JWjAMs6z1g=; zXx9+vRy!nAZB}a=c1Uyox^VGBLUZFU81Uwj{Yr8i2w$w$lcHwCjcU@MnrYkO0*mkUGgHD=1 zi%P4;%za;7r?xB=5W2EiO8d~Jt*@*9+gol*%e;QTVzcnHj~;QXnwGD_C4>W<-Yy7v3AY9FW(cty(?dpm@TvTXXC}SrW);@5v!QzOqp7$qO-a8Z%fHu zuR~{S3pzmoxn{-7KT}+qY}U4<{`*<>mHo-lj_lxF;+KkU#<7s zZO4y4+uNkq{fV1Vzi3uL%alJS%k3rq%EkTc*MF&OlE44m@rQ@S_vps_>(+1WOfIjQ z-_JW|1Iv;W;l$%JQ^jXl)@pn;GuWWuoL(?(jht+v=aFWaWVI(ZzQ(&oI5n(VwC1h0 z^Y0H+1*Dd2KB8fz-J;4DCi+W9S9Q&`AD1|!e}6SpdgT?-#XKQHVfNzkMbH$f%qsDznVqa^g(v`j`Lr-quq!5sl*Jakn@(Y3FbK5ckPK z%FhaoUTy4u5V3K~%(5WqPmSVR>iYlwDSN}8l z)}s1rF3+62Alo!molZFsL(Apr>woIXEjrg1Ij>cu*=6RYhwWF^Ps?`dlQz>a<@SrP z>ewS3ICmpcO@92f%C5>4pQ_!hj^}N=zJAKi_D!5WOZWZ~oVI46Rk-Su?uHF#@}qBF zx9;1x=6_1ug2}$5!vV?w)sN+e`PY_0NKT=kwM74zEA>`542; zS4$4=IK&uu(|q3kZ>`;0Cx3_Uc~iOc{$cxlk6gQt|NG=V|LyO$yzBqEzyG|kNqCyZ z+X)r6P9aIIN18Q`PTjiRYSo4Qo((RFq6aHh?lEiA4RVa&dYJCNz4>t<%cGPhEvgS{ zy9Hvi%9gDD8=! zoo|v&7EYVcm;PDr%ejrSo4DRyioJG7YlHSJqkq2d=NHDB-fsx(s}(G}6fS*xwaF{r zU?XtLraH<`Rp;~h>74hM&fT!(yzu!Pe%nK)Pv_+Px;#U8np4VaHnHE&tgg8qcH5k^ zptwHi?*6ME!=euvX>2S!U(I`y_nSw{=j&4T`z;e+X1x8kFgJEZM0dzDxg9qgB5r+6 z%`#+Dbq#-h{cDzI#Jro5{%fQ7jI>-XpLn)??VH!K&lj+Vrp#Puw^XJ$q|fk${i0}- zx>y-s(5OV;ybXIwAM-1UtuFlf{qVNib&}s~YD5DgdsTFHr@!OPuRW|?dF`0sG`{1; z?(aFQ=KcTT|Gy$(*5m&AAF(p&N;>mD&)I+VzNg~r+W2sb#7y}IP2B0Cw4q+?X6ye`H#T0Gzh$DvV)@^%I2zCB zsyh6LZ8bdrYK})OIA;!yl9%^WtrAyWG}CHbS8e%qxtqW=i#w@rrhH?a*xGUQ z=ssie*%sAe@0E4CyWao3w%|~4+O@A)-Vt6cidDxBeC_-i>|&OZwqY&L>$w}}X=x^; z?yHM*Sfjb;<7EZ5-zzS@PWlI`9c642ITIJEG;_-D{P8yN_}|ykt`UrF zYwLeX1y1cgzW-b3dXB0;!R`5<_pP^|vtdopvu;0=Uz|)1ttS{S=}$5eN}ZsgShjGI zo$J!#N8bdmY`Eg|ZNIel??%P0gAGRA7uyxpO-+1SRNE4J9xU;H$^NUkN@GcDv&K;l zV~L43IlUDp%XRHeO!sfQ_Ahiom4u&!j70OE&qB^0!vob#9(%GRv$jj|MU+`5J?IeL zJg+77_lso;Of#>GTOKJtaHDIxyX=QqRU0}i)eJUyC?DDJAX?cq;Y-9N&1Z6pIU8PR zyzOsYF3-F6u06-*==(BhX8YwH&$B8t%3HO#a0TyTr`iwu0PWZZYtRx-7GZi=kwEF-W$!` z@WlF2bPwn2^gnxaeg!LYc+WYi^`bz=U;cu4j?=}CI7J=b%&n<;OD-$F-;{9Y$}Pjw zJ34sEel7G*U-th6bME44E(I2eHyswJOPg&}Szo$jfs9W?*9Nh(d9~RyBC7KgmcID2 zc)t4C@{)hawx`#;zB+BgoV%Zvop@x`o_9gK^wcu$uaO#wGw*Km+Tgfz`kvkUI(&0B zocO~x@pNHr%Bo3sy&}x&W6#{(XV?sytJm81^Qm{og;lDFKYR6Kg*+-b&DQ*ky82OI zQtkQQN6*iB&(gR!@W;XJcK6JWUH|t=`)S*OW%6HTfA1^bJ8l2${{LT&-+NsfS8S1( zdH(5r%gXR80xmDT1|1?lT4v0e
qIQF}du}CA}-_&a8QTb=C%!yDOP9cQpx2 zlg!cnU6rnW>E?vXsU3qE)G!k$ADK)JAeGD{vn8>;B!^_XFJtDr{{k=C|-e+5b zf6b4(wa?$wz0gwUkm)_V&wU-|^_Y)m3renkW$yp|aQp9*XLslQ$t>l)!Mwet_w_#6 zZTENQ<@`SWuthcFeZ26>)E`Y5N0KhiLKIQyz9!XSla2R%uAEvK#T;iV~WZ@a$YYpSR$=;)g5Q*YZ`& z%RHIf+2OL+@15bSrkAtl9p3G$^e)PJfAY%dn!DHQ22HshaCz2N*W(U7J#}w{IroYf z&9I!)tMzV$Z-el(C5uc1rbRyY{;RiHc-rRg`}9|YypWar0xIGfI;wtGg_L+cjCGC3 z@-<*~jA^-9eQ#~4SN7`}PiIP;_m*++T4gxf#-=LZI;QKil zN0Ys=T#LAOhULYs|JI`VS|)o{=!^6>OsYDcZ--@?XEqx0 znluzWU~0<+7D}s-aTX{S|d| zT_#^+>{^-;edndu*{-Eu)HO{#^CJ}nwuEY$dEQlh?$hhSs9Gv?(U>hUT}iX!%yl)Z zyagL0O2o6ZqdaDRo0$Epcgb)4Gu{!W@@{$^E=rsiwIJ|tShm0Bviv{MC!D$u-kTpM z_(Xp3X}0%WKbJ*u-Sd3fnZqkXGfx}T<6Jxx_IZZGfoet zFAtbDrQOR56kl31`~^PWN@V!9fa~JBr!Hrj*N6%%omyP;{fu8w{A=6KFRCO<*uOmI znVr5fFZFiElBM&v{(ru`-ga~D)P46hE)3!8(?q+`Qd>ZK7O?#`WVn-CWe!M7fOF zSolu;JiebP)5@&PUQl)IUVDD-e|uQ^#qKL}XT9o3*GZJve0lbl_8!$&^8(om=N#Agdw6?C zVfW{m?X+~C=UVV;sl_;P}tT-ttm*6*};7Wxld($~$t z^@Dxp@qM-F=5LeumOYd87yrlPs=N8a7Q+aW({GXLwv)~^!oC`f;sc&;k!`VF^f z+#T;Ox~8=*nqDj4UbooJG+Rb8NbGvyoWEB#I=7!Ljt4Ej@_r&wY*8g%DtJ?)<6qx7 z_Lqmg&y2q=o&QNPTKIN*W$(Ym@|pJ8>2q^d^E~+L*szIjliDKr5A$vwNjdHGwf?3x z_cNy2Up>5gx5H6%U05ln`T0`*^(LuGn(t!oy!e(o^LXD}AoMtC(B*7HKnBJSeu+w6^#@;}ZIy!Vaub9F)LuAi{ zAd|Fze;{)WBLCjWl^y+z)F zQ}@+ICcYHiH`h*Yx=WwQvbWMjLLRr6C*FFWH*fLtrCp2Oty#jm$o**byK}8y7j6wv z-E;bMT>qW-KQ7uHKAt^axX&d=L{QyPM(dNVw*7j|Tho=q_rAThe$JHrY7_H*dp5Nc zoGNB&dD_j(z@VVR$iTCzKdUa}_v!8PX1_Asef&Fje#NI7*F@MJ$gs4i7U&t-J4D#s zJ!E8F>ChMuVNjfrleqH4scmcHKlQqB3x##boQa6=nltUn^k=2N4r~fY%HS~ZD;H0#R!W>RZ%vOayWxiT;|3EWfzICg^ z*ZBU6H$Ri|z8x~UYsq*>h=D<18YrO!AAWVRS=!J3Tk?~`mrvi>E4%mZU45&a?C$B7 zwHI{073ZH1xUJ9dTtPvnv3TN7#zUW8icK>sUGc;{CNcB!sji32+IzN2ikdEev1-wx zmrwSovVJl)U+J|{qgB-TSFA!5x3uA_y>{id7CicF+~mJMT-Td(@9gyJT8qwymQ<{E zy=kEJWmu3xz1$whtf+g2-`wi?%FTW|aId3|p6)rt^X|F`A6 zk)N-|>24Db{cXHyLx@+z`?~FcD{kfAdp&K{y|-&O_*D9dIYclqFdPy}{1neU zKhOEUzti{h(Z$ehb6;6+i)ep#Y-Quc8O!3A=Jx66=$c(EjCdQw8LO>xdA=|xwK61j zZrH-W5HtTL@Ans{{KTI>fB5q0ws)e)DPX1Ov@Vg&7rUJ{`Cm`w^mcuxDdcgk{Fnh{arG={WV*Z3@yWQZfe-4#qxB24Sz^pc{*sQvGO{vO&{e6*Kr>{I0 z;R8p_j{t6lg5-vwXs^)jLF7=%#)#cBU8T?n|{y~H5ypr3zEbXt)m4uKr6TW_1y=D2ol^KgC@7>^2@7Dm@%=*Atmw|!dxdH>j9tQ;- z!D(5)i&o#?y>F_#r-o+f@?RRU5wmu+3e_t1$ak8aB~GPe)W!-29SpaN^Fin?IV$c6x?x^UYvc z`Td{&?dkUBhk{lXZeKI|;kGq%Ztwq=j&1Pd1GCX3!f*bC`0G&$AJr7U`Bi2MhtAx)VakV3+oTOO zB3n}TpX#5_oH$c2olmd0csYMqRG{`gomGn^WfE7`&IoFG+U0sxH89@%+Haq$TXbH0 zKi8UWf9O)Z-vmKWjly7eg^7WofR71O{|QdJ?q@0%BmUN2qqx0XPfKd&@~A#z^{|z9 zjpGg%?OB`HxuK`?-q~AEg}20;Ogm7X`~BgRjkUom6Ej~2s2zNFRQbFkFUSjSSiE4S zBN<*0yV6`fE|crc=YLCkBe}h=-rFqX?PU6Cm(Qw~u?7DZMIIKIcJlj7-`iR1Z_k=J z(V{wICnyE`hF2fmw^~TjY{BO3@qZ&btJZhMoC%9qX0Hw|-9Ic8V_;|~7GPjdVC3*_ zdAgoE^NNNJ$2ve z+2%o)d?xN#_`T*wp7d-nvw)0w_M40@MYM&L@=YnYdU&^Z%zU$b^-JnP{}&~d<}d$S z^Z%1tb*$3H1^2hDjqlpC|4U!U`OV?m`9bYlV^P#h^+1M&fx!b*18MGjymy^cYvN1G z-6dJ!T^mHys*GoDm}8wA&^>E5|GJ2eSA;xe&ewKbFwPa2W~$7knvfS1`oh*$>)z*k zi?VjDxmIfYFaOY|S0~wQ zZ(q9~@?_3?+pWd!tLDaAMYJqEzwd%^=>99~|1M2loqJ`~qEBz^ws_3=z3fvc%Q7W~ zzfU*SN8KpDy_K(rD|X|GT}LDG=49^7XZ!X#;(}Z8oqdYWZ@+){%u@H4?biCtWrvLZ zLYg%Vpdz1PPBQ}o11G2ztvc&Vb#(Tp8}Dv4RZaBe4US#jf4bCm+J-+b_HMhk%}Y}= zQ^Rv{Rh7^T!Pe9g#+B(A^Thksomp;vcW>poUIvj>H&?C*Y+34gK! zy)S%o;*?k}`?t3NTjD)^w3CkR2%I{peb)c_%csq>_O6lKyz=&}iMT@7+wg=-Ol9yI zuHU~c4qbBF6j?NxT}5cxqD6PWD=Y6f-|GMi`w~mgo zQl+@XJx|lU-Y>)7YZz#Sf0gon8Gh*drHxl_a-aV49Nf`B%B?C)iJcolxFT%LH*JXs zwRW3yf$Q`Kjnh+FmIhtrTl-2TI%uxlhBIGUH*9%xV}|j>X$Ldjb(QhlZojPl<@-5n z5m#3?$@gnCnICcOs8Puim@U0L@~5oY$KIGTJ`v08&@&c8;!hb4?@sC2UdFnKD{r;b zX*wM5f49=iF|c%!OxO$0AlANz52vb`uUzJT(N6ps*K7B0@2}cz+OTC!ykX#LaSQgf zUw5p3R{8l&)Lr4ohuhVv@FT0i;6C(Zo z$UQ61U{BTmC$_bCyz-c^^4~rC>h0yq?4dgc;C0Jx>OX=4Qd3hJtB(Acam9fWtf6SXNF`YjIo3&!j zcD&2`7=Hfdrm|gv4?j-GSi4^OwTPdJt$lQ9y<^4A{CrdLO(CAqt zwqbSXnFWEXN|Jc~+Be*2@GJ3uuI5)LR&jRv9%;}Xdv{%i8wVK}-YiJq-XqP8UK!M?mwMGO}6L{-#AU8vq0K+6yferi&)*7H)rl4)L5EU%m2aDK1 zT2vVl*cmr8mkEHBAQ@u70~UB)RyVKj_x9hrc5$O<76Ki>06N$Nax4Vsqy*>@I4sZu za2{|ssX`Pfz|RS3NPwPv!vQ;*0d)HnL>1ylIM{(3pbh%agWE_m3ba8VY^VVI1QrKS z3j-tuy<-S`QUp%7JAh7U0I6mG9bX7$ARE=g)Nnd4mF(C$)(+mu66hNT? z3Zte}A&}wVTgg}$4sPOSK(fb@8Ei82{KapK3G9p@SAs46&9I>vX2G5tyV)f&`z;y{ zZ`Ai&>?Ur$@5U;@Ydw>Xt&qI3ZSk$HTj#gs-i~YE?+@&CAaKfwvD?!yb6Lh~9Ts2)Z3c;gb zsV^BpegOwRL|ik1si8Yc%lcpMiB;mF)A!}X?RtCswbJTmdg~m+tWx|y3Fn{@BP3FN z8eZ*tS@=_9ZO!F9B{x6DukY7(TU(L4|Jirj!bj_+A04r;EqufN`TPmceS!&`44vQr znKZj3JY?JdtAR_ezVF|hpcyf*bJEdPf48?UR&`XVO1Xvgui0UL>a|_>Ggf9u} zSg+gjZ*|`sMfHC#H=h?;A7A-*`m-ayKn}mF9KqC(;Q98%rx#yNoN_-NxIskcv$0&? znpx6k_&oQsJ8XEf!ni*0?#jYvGn3{ojo!a-zF>s>99WuYQsrz)t$e%O@JeA?{E|8S zw0U8)(a{}0^4|u<|4W-X`5R}`QxDE*!S6FanVwknb4Sgb%5~h+ znx017Y_@SkL!+Lt*y3DsPUNP3^yuEN} zN6i)gJW-v(KrC*iyU=5?^1c#JTU5K*%P(z93ku;@A>?o-=o4e^WLrKTU}Ll zbJC^fXALv_Z`*zpUswGp|6Z2qjw{z+m2_sW`~7J9`RDd4IhGvN{2yoc@W(6no!7p- zFP!6D^zD9Z_5Jg^j;%Uh;-BVKcW&zWo!6e_7tLK<|6tPR|BtQQ>+WyeR>wVmt5Kq5 zPRyw=&w2ljuP{6Exrrs?%KN-MKVPof`&>Py=-K?0uWEip?(aQse)Uk%yQBQOzpayh z_<7fpH~V*f+c&?s*K~ex)*B8^Z_d+iWKQ0jd1n5}pa09|GVebVW4vv@;WdG0^{Xyh z+P%$}&adlw{^PQ?j^+1f%hs3Ab>4sSYr@;-zc%>wz5cx6-~Q6Qk>dRS`Cc!X9sBcN z)Vb47-}T>Qd+;H@_^jmXbvygsn~U}wTy?#Wzo!4m=TiUw%)fJs?`PYmZQJm5KM>S6z?W`hFv))AX1h`&S$hTK{y;rMk<#U5Ssove*2Zv*NYar0fqLI<$MX zysx@`zhLv}={s(UCW}q;_uE~0aBFv^{YfjKZwC)eYsvclB))%N-{E=EckYMJ_@csj zQi5^PI<+6)dG=LeZO%-#oY%X>eELtztFI>O=?FTM{oOE8t$%{=nx}izv-ZA~`NccE>|JkM ze){>Fd$%PndY=0J@5edD{9ZRp&zE^V*d4nxdBYv6Jb8u_fA0NztNUm7Ynh*S@0`E6 z`%}`}<2~0LK_ToB0SaiLX^WT=J(=2{_(v>JnatIH_vZiZn@g@-ogQ;*-`kvs#7toy zmC4S^Z|xkmu!e?Ixh&=gG?Lwj59jpCD2 zx%YaXj`iV@n=kS6-@T(t9iRI%mB$7AeI=aF9{kRlP9o+a3Aymho{9+x*Y9)I}DooqIpNjwftO-)%mrnbXbJ z@A}t!^<0bKw6Ai;QS7Y(P8^Dfnk@oO9uZw!EvlU>ZYVSgPmsW^YblCQ6w7VmtS z?SA4^zw*Z?j}Ge}`cYZJGkK%h;>`5yrd4dkzNeh8uC)Dr!QcB-?!zit8Z%*1%-tB;$KE-dBJ%315(uBVo<)wcJL&`y(_yCvLR ziB6(#PCWk1Aox91apJc3H-1}YelFrn^jtFW`tIXjHSF26BaXkF(e-l<$LU|al53lt zAeq96Ls3P?_3NQaD#xqB!;}uJSfZ12+G>ui^o%*hW`Vx9L$plSY*;c&RsUpbLXyAR zH?bsnV=a*tJSTFa!#~U{*7mqu_js*sciBIM>e7p1S9xb&kBOVGMk{N1c*xhAyZ@<) zdc3*$lI?q0q@m5b6qBY+9$z+^-;B^I$}mrFu6E3hduRK&%`EHA=5*z=Arp6M+K+-}Ch+ENaJ~0w8$XNnUf#c>ydwO%H>A|tS|4hTQk$h!{cRC< zRX}UyWIl$sd+l9Ar@X!&F?F}x*Zka$xYrr^-qt=*moLRteR#3rH0QIGXH1{w&-%&X zW5AyNOy|}L|8svo$4`HFhxJ{7`SRh(g$2LAe4U>poIaC9!02+=;R#C~ zrHf5ksxmW2f&FdvO17=0c#(=6XT-A5kvd7`KEW1lJ ze<%vxy6m^4V4LoB+qLdzD-}wczAL2ko}3)od)oV|Ly9>hX-o$@f-FR~06_xpiwM4Cd z*Pr|y$=2_Gi!1QA)VGUUL}%Z7td{=o=Ba(@N0cvt>6u{qf?n?eFzvdGpi~J&(?vJAXpTzslTqd-nR@ zVb@+;)bid``U!b)4R7TBI#=(h z>c{l%q*pfge`lOG*Y_}~p5(lY>3F^0za^Uz-%aiRr1tH`{z=m|schL0xcL0qEfkTnoay(4Nqf~l-q&p?J;-u(i?Fr+birwg_Z{CfyRVhKFK*4YI{vur zp-JCX?MuF1b|yns1d^t~RW^MUb+ znO5vKD$CSf?QRydU+#Xsx_sNuKFg@M`TyEG=T7(L@Q(kgpm4P(;@{~Th7o_Q-U&{t zDZ5|z+6JwC@I^T>GSpPLQ~tf3+n!_By{8{t>T>Qek*<kygu~v z;++puKYt6X{@-zA-~4aymp?!A^~5^iw{z_!N^6d_XNFtN z1eNtO%jDZ)yQ8Iz-#@;id_3Xn>mBY9pKMQlx6DTO>ly}5Z!Pu1lcxTz(~FAu4^wBv zew;J4^^NB9wRM}ECT>libMfBy&1Gw&1AbpNsZ42ks_xgk!Ns9kE1CCR*Uft;N}Ikt zH$1+)>w?Ed&biYcw=Vs8?@6=itu0>vW(y^6_*Hs`%RAaPcjf;X7p*i}1e`cHy+Q4e zLz{$Dbao&Azr`x?=Le6Ht8Or|Ll zpO1o(ajxXVJ&$=(Qtq~-{(skJq4@Wgdeyu9yAw7{+3;n{8k?`>pX>YPm^3|}xZP@1 zP6^+iPZrm#&XsCu>HPiOS?7M_cky+m`{x%eSo>?w1I9(1pcHqy-94!#^(WWqSJyw* zBwD`sW&eCjuO#Q`U&`QC0;I9zbZFA58HH>@df&@$bR9A}RP_G*l&UOwHC4`Qw%?AQ zWBdQ@UG}s-%ZoQ_1MCwktBZH0Y+Gw4C^YS>*tD{HY4NAeoWJ?_T=+?U(?1f;?4Bw* z%29XC%xm8=U0ZwQjdau&za<|(Wu7*_xpmR6%F17@sb61L?Ab8qoS|}JWIa-d1$ji6 znb)P(srpssEbaek>n~GeZ29~Bfx5Uk`kiL|8=lPBZ{@c^@>$&96(3KxEcIS7!!TF; z@P@bf)z{{u7yP$^6|4iKHVg1Qx`X{E-~`2m7KoL^1?G7 z5!d9wF$!(F3r$n`r!;R|v~}b9(;f+~hDj$+LfzJJY@ubR9Zn zUwuC(Xv@x*uU~8F2wvTIXwu#vUZHE1xJ=4DBm5?BV7aCJEHu!}yk@85|LLDn4_~x5 znEL9vijMZ4`sUxiHx)mdI@R#~^G1oy<;BTIzHTU9J^$WRapU*t_Ag&OfBI5<+ST=s zKOQRjb*!f)^{*hf?F4H6ae6BzavFa-S)aa&<@CgFvu~cS{*=#jKU_uU^!(zY!nB-| zFP@ySx7YTc?#6Zcw6@#5?TN3e!hGiZJMJkwZOil?k5$Jwyp?(<3I4UdeP&;zwJFH4 zvo|bRy5gq&ySQ~ZFJ=C0tzq2uQ`&d;(RnSYso(o8i)V@+d3x{uj!8c)kI$ChWB2UG zx4$6|!tC|^_itEJC)+PRE!Z+mMdx)txRi#pPPsU}7iby$sC6s5#MJY>-2b2H+OWO3 z7go&sqW0)|{mT>6HaJ`r5RATQvAc$Isb=BH7aeA`E=B+9Pi<}4`)ks~MgPCN`DrIV z3C@_*-UPTROy`@Vee-pp&S{8!e+%`-~O1eI){wEPN7S}x=J+qT*9YoC4R zw@JQ}Duo}dJ$wI&@0m#)+Z|^+E1$7h|8dS|iOCl;e{VBBXg7P~_p5JmpG{mO{!c$f zph{@fo9NWVAAhOOx?Z_z^QC_$PH`^rytPwWTW4|Wkq7tWW;bm0OjJyaOip~6dGg1P zC9}o1D1QC3_sypI<2F|Wt)Fkarqi@x%hliYwkyA%-*UuE*C_qZ`{e!U(qTFu|N9p3 zA3C@C_knkYdw+Z`(rsB<&l9%4?>P7Amomme)4sYRwZ>apRCQvV4xQh^?o-iZw?kIZ z@zb5lMm<`LlNXq7x}>tYTT3TBJfQH>_LF6ks{dVE{j_yy_`D*$FdM0it0%bM{`Ro5 zo4u#H1K9zuK4p6s$Lx4lQtYxs%TUB?3R9b z+bU6^X_r-WKz%9W#DDwt<$n2ny=I>A!DiNNQFhKn|Mt)2z1H=rb!lw$x?6wWs(t%6 z)Ar|?JvGPA?)?;a^z*H=rF|Tcx7qpmpK`3oUGwpe&G$sn-EOz5L;voHiBEhf-we$- z$9r0q3OI2DCadVQLW=D~&b4#zzyH*v5?KAc|C6QXyuhO#Mkl%S4xQcm<6*k$!>{IZ zIfe3WdYxVxQu$A3g_s zPEY&&a*S5HV_bNuJ&gN;|L%ziPL#9)Wu~wE)hyUZY}4uj`e)Spq6wAFOWgFHn ze7Zuk{+a1R|J#>OT@hB<(S2J~N@}&;KA{)&b<_mu)AN^5Q%<&~2yp0{A>>`eLRiJFf;hfWvYrn*M9{Lbz@ z2GvOuIr_w=oyZp2s?wHgZ7Y3PefiDl{>31#a-5!EdF_;&mfGU;)z8*?Wj}M9@ABgQ zy{3}YDQmffrfoB=n3v(%F08JTo2vcx;7sei*MGnBi$40S)ywZ%;CcyZHJgg0{il7N z?8$l8AA9)me&M6skYsi4R_?va2J0`*n79Gd=FjQ_XI{lbO_tE!5dTYB(@q^c*v!1H zJXN*Y>6Tf;K@;=3rLX7xkrryov#9>|((wE4dsnB&RNp`UYc<2)4<>fi>nA=?wLDh8 zS%1m@8tnxif2n`k8B_GH{?3DYa_8FryKxoq-W1*vbLrdF{-pWF-xd@%yB}Y9AmRLl z<^R7;4|y~tGrwD9Ay-R_GSjr`$7?TSz5QV~ea_!B<$eWTf(Il%DtA zN&J_*ZpGEFX>$|KrNk}~db@V(w{q|G!XBzo+b-KSdeyyC=bunuf4jQA?f&}8O2Oj~ z+;41lmS$OGz|s6PPNj~Xo2~N(P4uO3-W9gO9Nuw zh+|qa(kv3kGzI~}t>=q|O zB0DVn1a#O@X17p1r-j>dZy-}-Pwg03P8%~aSaTrzbqz!kDfsH&+`U?q>?zh32wFdF zES0)(`15-gG{V8B<$QPt|M->IF57L*Q;}vpRd|r^BwFW1_sp# zy)yQEy;An|{{(clYac9%-Y`cGBDY|tO~v}V@Ad~br7r&dc7D{_f5(!R-_Kpc^GY?M zPxt2L+kZunyh(a_)%ey+SNUw@l>zRtI{^(NDQ*XL|)d(AoR?A41&%Z;lt zjUx7ax%4r5xtz}D#u^Rf**^>ZDb!D~KO=vBZT;Vn_2rL_%4B5jye}tyZR@pH1*`WN z{qerH(Kq*PkN(8dOAe&vlw8`er>d&z*a{(?tw)PuEm#W{7R5AMn^Kkkl zBk_|fGCHTb>TyBrky&%;{iV-dE6${(R>kj%{}pfbz$txqc>M2W6YXaB&4`xDgFG}+Wh>QxjQyrwk)mRY*|%O znEJyLcBx1U>*dd%54cc$x{i48MPBiX)XcgdbhUoL&W^wTKvXY86g zH&2wLLa!`>UVmhjRP{)EeNAQR+o|Ep#C1L|tTB+!Gy7kgyZ7H&U+(Dm`}=m?+Hd~; z;8hPW&Wu2=zCS}i{_jDbd zz0+lxUa=y~l2azBp!>$4;h0bszKnYrzn<<_`Rwie_5t6*`yM~#z58zY^(ES2O;3OB z*N^afvoR+6_p?`ftY;UV+nHRyf3Z)bUJl>98SlT&Kl?B!#F%Z?_Uz3C#_i|xPDi}x zL0r?tH~$ODHC=9t?WfDl(3@cwu}u3i@9E+Vk;XIE&Db~NoJDKG&T2=ykZ%!?;F*^G z%=ea%Zg7zCznSLQ!g|3G#XmCE@Vr{N4&sRe52vmZ&#ZY_`?Bt3eWdVAxrn4x*gaHM zkN&?~f9LPn-`c0&H@(`tVdf?u*|pdG=if}7DR^E;*ZAAa?ODYacE0cn;Z=XFA^>9YRzQ+DMF%Y?2hzS}u` za&NxwtJP@lFPvJt0Dfm9SW$bCl*L2;CwLJRr zl#q`4G9B%UMoUc|&#jzn_SJ;_wDCsECueSTS{FZvh?l=B47)O_z+&l3(WPHgXBmOR z!XVb5)?jWy{-#ZzMBZ#!rv2G^y-(!7h%INLw?rrOmW zV}5p#6%yK093)>hZWYox9g_OS^vJD#dsgea89SqAZu_GcH|^wUnUYJ-CPGv)Tv1#4 z*EO>-b&=5}?$y@X%Vo6dRieU{>1u;6>FPIvs9LaPf7TM6g`2EGBYpmt-|KHbeRl=R z86|eRSrGY#tpBUqPH7%JrP*@Y>xfQSzYauvf!WJHD=gM0-LHG@dh{nm?7;uMpBFtk zQ1=`fY0uX<{}eWY`k3Y9|4r=dDj=0j|Mus(FE|9fn+qfY#SU)|GJ@7BH7xkQ{ d8>lcjl)&ZGmDfq{Xuz$3Dlfk8|agc&`9 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 U^76fmbRWoaPgg&ebxsLQ0B6du*Z=?k literal 0 HcmV?d00001 diff --git a/Chapter2/Flutter/desktopExampleApp.png b/Chapter2/Flutter/desktopExampleApp.png deleted file mode 100644 index 5eb21c79205c925a3f74ba24d5d810a4a6b006f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37840 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 diff --git a/Chapter2/Flutter/mobileExampleApp.png b/Chapter2/Flutter/mobileExampleApp.png deleted file mode 100644 index 83ed148e9503611d87eee5c9380c2762a899de4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 63062 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

t)=4PB?*_M-~U~( za%*459KGXDPhIYRXDd`R>Eg$S$?5l2W$u|aaZ+``d5~We*)3up%g_Ip`rzTTOSb** zY!_YrdCY66TB_*t{6AkWhUeW|rJ@u$v439OWApx+^diPP1#_RzGdy-B|Jo}q$1Wv* zx$ct^#nmz5+J0*4X)}DoN)Pd;-+5XTv&N#b;CylLyvV?m(m=&lkz+sho~>YH*pYv7 z)Arr#YjbL5bUaE97BO7!6x|i@#(8fobF53sRu{FS%g;V{KY1V_KfXRZFRd`AWYJln zm3rrHq`!alD!04WFJ^mMc2&{2wbN#OTlZ|vnMGe+qwmUHkG;ykvHs%6huqiuTHnms z^8I`!mvT1Md~JQNc4XNKe(qxL@9(RAEzkeAx9Y!x%XDpj-RBE7WoRy4x;Aca zKv`8{S=GTBKT%1+&H2xRi!C>;`qpIiY(huc4CkQB1zUH$o3v~0^w2#^OZ1gz?kp^54Byn7qcPpvk&v$JmX(rJeCW=@;E^RwsA zOS_HFgkMaXI^)ipIqlyC13khd-@cl5S8jRjgpQ>-y3x{eg7)w|zgqtz(Z@0G*1Ftl zOIY+gZ~y=D{oLQ_|FeHyXLJrwY%kXNAew0wIj`=C_hj)dBS~HM?>684|Npyxr9ISC zT<+%QoD*L<7cajTu>17vsMq%@{7Y2L=0USLA6UiM>XPtyvi!H>)jMH}|W{ zmeRxbj{Mm2bB^z{y>svCPS^Gq4h*YG%qclk)6{kQ@vC>)^5CM`%kxZ#y7|35p*PJh z%$&PVO;z)<-Iegr>Xlpf9(-E1Of7Zk@)UR1V=E0aKhE9zb;d3z^Jy|C?eJ*B^m zB2G=?eH~{bG%3jM@5%btdh5H+olKgOZnNXEh;*)Jhi=`+o2TmQj+#E0-MZBVGzfC@ z@5fmkT*Y3?XIspeGC@vQG++Le@}k1$H*Vb6ux;5ir75@CvK!m~e#`qE5T6v2mC!Hn z$m{9L1BYv^=ks`qYjxlJ_F&ubd;fl@zdC$s7Plg!a?JCY{F5&V35%wiMIM)3{B7aA zXx*(FFLo-%XUVPKy?+1Yms0ufv-m12ql(Jn>z7|l;NmQD>NxgHnz`B5CB@X;#r>#c z@3Vrfk_->(7HFJm*z3NQD|pI&!@5hppYP7gP05L{jczfm`p3Pn0W8H$K^!eED$jG*?llnwn?R z8yDrWf0w)1^LhRvz8RO_29&>0-Fq`d)M-WeN3qGibJssA=qc1|y`m%L^z+T9<9ge> zjki_5UOZ87W0%rpnO6;GXN7iXq-@lXbapcdQ^c-biIOf7WLW|>b^zLhE7zT2dSGO&E_;236b9c@>`twHUTxEX!@~Cr1w5x66w)