โมดูล 5/5 · สัปดาห์ 13–15 · 27 ชม.

โปรแกรมบล็อกและ Git สำหรับงานทีม

UAT 104 การเขียนโปรแกรมคอมพิวเตอร์

เวลาเรียนประมาณ 90 นาทีร่าง รอตรวจปรับปรุงล่าสุด 26 กันยายน 2569

บทเรียน

เมื่อเรียนจบโมดูลนี้ ผู้เรียนจะสามารถ

  1. แปลงโปรแกรมบล็อกของโดรน Hula เป็นโค้ด Python ที่มีโครงสร้างเดียวกัน
  2. เขียนคลาสจำลองโดรนและใช้คำนวณเส้นทางรูปหลายเหลี่ยมและรูปดาว
  3. อธิบายการทำงานคู่ขนานด้วย thread การส่งสัญญาณ และการป้องกันตัวแปรร่วม
  4. ใช้ Git บันทึกประวัติงาน แตก branch และรวมงานตาม GitHub flow
  5. เขียนข้อความ commit ตาม Conventional Commits และไม่ส่งข้อมูลลับขึ้น repository

ความรู้พื้นฐานที่ควรมี: UAT 104 โมดูล 1–2

ทำไมต้องรู้

นักศึกษาหลายคนเริ่มเขียนโปรแกรมโดรนด้วย โปรแกรมบล็อก เช่น แอป Hula ของ HighGreat ซึ่งลากบล็อก “ขึ้นบิน” “บินไปข้างหน้า” “เลี้ยว” มาต่อกัน โปรแกรมบล็อกช่วยให้เห็นโครงสร้างของโปรแกรมชัดเจน แต่เมื่องานใหญ่ขึ้น ต้องใช้ภาษาที่ทดสอบได้ นำกลับมาใช้ได้ และทำงานร่วมกันเป็นทีมได้ ซึ่งคือ Python กับ Git

ข่าวดีคือแนวคิดเหมือนกันแทบทั้งหมด บล็อกหนึ่งบล็อกคือคำสั่งหนึ่งบรรทัด บล็อก Repeat คือ for บล็อก if คือ if สิ่งที่ได้เพิ่มคือเครื่องมือทดสอบ การจำลอง และประวัติการแก้ไขที่ตรวจย้อนได้

จากบล็อกสู่ Python

บล็อก HulaPythonหมายเหตุ
when ▶ clickedส่วนหลักของโปรแกรมจุดเริ่มทำงาน
Take off / Landingdrone.take_off() / drone.land()ต้องขึ้นบินก่อนและลงจอดเสมอ
Fly to Front with 60 cmdrone.fly_forward(60)
Turn to Clockwise with 90 degreedrone.turn_clockwise(90)
Repeat 4 timesfor _ in range(4):
repeat until [เงื่อนไข]while not เงื่อนไข:
Set / change [ตัวแปร]count = 0 / count += 1
broadcast / when I receivethreading.Eventทำงานคู่ขนาน

คลาสต่อไปนี้จำลองโดรนในระนาบ ไม่ได้สั่งโดรนจริง แต่ช่วยตรวจตรรกะของโปรแกรมก่อนนำไปบิน ความเร็วในแอป Hula เป็นเปอร์เซ็นต์ของคำสั่ง ไม่ใช่ m/s คลาสนี้จึงไม่จำลองเวลาบิน

import math


class SimDrone:
    """A flat-plane drone model for checking block programs before flight."""

    def __init__(self):
        self.x_cm = 0.0
        self.y_cm = 0.0
        self.heading_deg = 0.0
        self.flying = False
        self.distance_cm = 0.0
        self.turned_deg = 0.0

    def take_off(self):
        self.flying = True

    def land(self):
        self.flying = False

    def _require_flying(self):
        if not self.flying:
            raise RuntimeError("take off first")

    def fly_forward(self, cm):
        self._require_flying()
        self.x_cm += cm * math.sin(math.radians(self.heading_deg))
        self.y_cm += cm * math.cos(math.radians(self.heading_deg))
        self.distance_cm += cm

    def turn_clockwise(self, deg):
        self._require_flying()
        self.heading_deg = (self.heading_deg + deg) % 360
        self.turned_deg += deg

    def back_at_start(self):
        return math.isclose(self.x_cm, 0, abs_tol=1e-6) and math.isclose(self.y_cm, 0, abs_tol=1e-6)


drone = SimDrone()
try:
    drone.fly_forward(100)
except RuntimeError as err:
    print("error:", err)
error: take off first

เมธอด _require_flying() บังคับกฎ “ต้องขึ้นบินก่อน” ของ Hula ในโค้ด ถ้าลืมบล็อก Take off โปรแกรมจำลองจะแจ้งทันที แทนที่จะไปพบปัญหาตอนบินจริง

รูปหลายเหลี่ยมและรูปดาว

ตัวอย่างที่ 1 สี่เหลี่ยมและดาวห้าแฉก (ตัวอย่างที่ 2 และ 3 ของ Hula)

ถ้าเดินเส้นตรงยาวเท่ากันแล้วเลี้ยวทิศเดียวกันทุกครั้ง มุมเลี้ยวของรูปที่มีจุดยอด จุดและก้าวข้ามทีละ จุดคือ สี่เหลี่ยมคือ เลี้ยว 90° ดาวห้าแฉกคือ เลี้ยว 144°

def fly_polygon(n, k, side_cm):
    drone = SimDrone()
    drone.take_off()
    for _ in range(n):
        drone.fly_forward(side_cm)
        drone.turn_clockwise(360 * k / n)
    drone.land()
    return drone


for n, k, side in [(4, 1, 60), (5, 2, 50)]:
    d = fly_polygon(n, k, side)
    print(f"{{{n}/{k}}}: turn {360 * k / n:.0f} deg, flown {d.distance_cm:.0f} cm, "
          f"turned {d.turned_deg:.0f} deg, back at start: {d.back_at_start()}")
{4/1}: turn 90 deg, flown 240 cm, turned 360 deg, back at start: True
{5/2}: turn 144 deg, flown 250 cm, turned 720 deg, back at start: True

ดาวห้าแฉกเลี้ยวรวม 720° ไม่ใช่ 360° เพราะเส้นทางไขว้ตัวเอง

ตัวอย่างที่ 2 เมื่อรูปดาวปิดวงเร็วกว่าที่คิด

ถ้าใช้ ตั้งใจให้เป็นดาวหกแฉก โดรนจะกลับจุดเริ่มหลังบินเพียง ช่วง ได้สามเหลี่ยม แล้ววนซ้ำรอบเดิม

def legs_until_back(n, k, side_cm=100):
    drone = SimDrone()
    drone.take_off()
    for leg in range(1, n + 1):
        drone.fly_forward(side_cm)
        drone.turn_clockwise(360 * k / n)
        if drone.back_at_start():
            return leg
    return None


for n, k in [(5, 2), (6, 2), (7, 3)]:
    print(f"{{{n}/{k}}}: back after {legs_until_back(n, k)} legs, n / gcd = {n // math.gcd(n, k)}")
{5/2}: back after 5 legs, n / gcd = 5
{6/2}: back after 3 legs, n / gcd = 3
{7/3}: back after 7 legs, n / gcd = 7

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

ทำงานคู่ขนาน

ตัวอย่างระดับ 5 ของ Hula แยกเป็นสองสคริปต์ที่ทำงานพร้อมกัน สคริปต์แรกบินสำรวจ สคริปต์ที่สองค้นหาเป้าหมาย QR ทั้งสองสื่อสารผ่านบล็อก broadcast และตัวแปรร่วม ใน Python ใช้ thread สองตัว threading.Event แทน broadcast และ threading.Lock ป้องกันไม่ให้สอง thread แก้ตัวแปรร่วมพร้อมกันจนค่าผิด

import threading

mission_started = threading.Event()
lock = threading.Lock()
targets_marked = 0
QR_FOUND_AT_SCAN = {2, 5, 6}


def search_thread():
    global targets_marked
    mission_started.wait()
    scan = 0
    while targets_marked < 3:
        scan += 1
        if scan in QR_FOUND_AT_SCAN:
            with lock:
                targets_marked += 1
        threading.Event().wait(0.002)


searcher = threading.Thread(target=search_thread)
searcher.start()
mission_started.set()
while targets_marked < 3:
    threading.Event().wait(0.002)
searcher.join()
print("targets marked:", targets_marked)
targets marked: 3

searcher.join() รอให้ thread ค้นหาจบก่อนลงจอด ตรงกับบล็อก stop all scripts ใน Hula ที่กันไม่ให้มีสคริปต์ค้างทำงานต่อ การเขียนโปรแกรมคู่ขนานผิดพลาดง่าย ถ้าไม่จำเป็นให้เขียนแบบลำดับเดียวก่อน

Git: บันทึกประวัติงาน

Git บันทึกภาพของโค้ดแต่ละช่วง (commit) พร้อมข้อความอธิบาย ย้อนกลับได้ และให้หลายคนทำงานในโค้ดเดียวกันได้ หนังสือ Pro Git อ่านได้ฟรีที่ git-scm.com

สี่พื้นที่ของ Git โฟลเดอร์งาน พื้นที่เตรียม คลังในเครื่อง และคลังกลางบน GitHub คำสั่ง git add ย้ายจากโฟลเดอร์งานไปพื้นที่เตรียม git commit ย้ายไปคลังในเครื่อง git push ส่งไปคลังกลาง และ git pull ดึงจากคลังกลางกลับมาที่โฟลเดอร์งาน
ภาพที่ 1 พื้นที่ทั้งสี่ของ Git
git config --global init.defaultBranch main   # Git ยังตั้งชื่อ branch เริ่มต้นเป็น master
git init
git add mission_tools.py test_mission_tools.py
git commit -m "feat: add battery_status and distance_m"
git log --oneline

ไฟล์ .gitignore บอก Git ว่าไม่ต้องติดตามไฟล์ใด ต้องมีตั้งแต่ commit แรก

.venv/
__pycache__/
*.tlog
.env

ห้ามส่งข้อมูลลับขึ้น Git

รหัสผ่าน API key และไฟล์ .env ห้าม commit เด็ดขาด ประวัติของ Git เก็บทุกเวอร์ชัน การลบในภายหลังไม่ได้ลบจากประวัติหรือจาก fork ที่คนอื่นคัดลอกไปแล้ว GitHub แนะนำว่าถ้าข้อมูลลับหลุด ให้ เปลี่ยนหรือยกเลิกข้อมูลลับนั้นก่อน แล้วจึงล้างประวัติ ส่วน OWASP แนะนำให้เก็บข้อมูลลับในระบบจัดการข้อมูลลับหรือตัวแปรสภาพแวดล้อม ไม่ฝังในโค้ด

GitHub flow และข้อความ commit

เส้น main สีฟ้า มี branch สีชมพูชื่อ feature/battery-check แตกออกไป มี commit สามจุดบน branch จากนั้นเปิด pull request และ review แล้ว merge กลับเข้า main
ภาพที่ 2 GitHub flow: แตก branch ทำงาน เปิด pull request แล้ว merge

GitHub flow มีขั้นตอนง่าย ๆ ได้แก่ แตก branch จาก main → แก้และ commit → เปิด pull request → เพื่อนในทีม review → merge แล้วลบ branch

git switch -c feature/battery-check
# แก้โค้ด รันการทดสอบให้ผ่าน
git add mission_tools.py test_mission_tools.py
git commit -m "fix: treat 15% as LOW, not OK"
git push -u origin feature/battery-check

Conventional Commits 1.0.0 กำหนดรูปแบบ ชนิด(ขอบเขต): คำอธิบาย เช่น feat: สำหรับความสามารถใหม่ fix: สำหรับแก้บั๊ก และ docs: test: refactor: สำหรับงานอื่น ข้อความที่ดีบอก ว่าเปลี่ยนอะไร ไม่ใช่ “แก้งาน” หรือ “update”

ปฏิบัติการประจำโมดูล

ปฏิบัติการ: โครงงานย่อยเป็นทีม

ใช้คู่มือบล็อกคำสั่ง Hula และตัวอย่าง 5 ระดับในคลังความรู้โดรน

  1. เลือกตัวอย่าง Hula ระดับ 2–4 หนึ่งตัวอย่าง แปลงเป็น Python ด้วย SimDrone และเขียนการทดสอบที่ยืนยันว่าเส้นทางปิดวงหรือเลี้ยวครบตามที่ตั้งใจ
  2. สร้าง repository บน GitHub ของกลุ่ม ใส่ .gitignore ตั้งแต่ commit แรก และรวมไฟล์จากโมดูล 2 ถึง 4
  3. แต่ละคนแตก branch ของตนเอง เปิด pull request และ review งานของเพื่อนอย่างน้อยหนึ่งชิ้น
  4. ถ้ามีโดรน Hula ให้บินโปรแกรมบล็อกต้นฉบับในพื้นที่ปลอดภัยภายใต้การดูแลของอาจารย์ แล้วเปรียบเทียบกับผลจำลอง

ข้อผิดพลาดที่พบบ่อย

ระวัง

  • ลืม Take off หรือ Landing ในโปรแกรมบล็อก
  • เชื่อว่าเปอร์เซ็นต์ความเร็วในแอปเท่ากับ m/s ทั้งที่ต้องสอบเทียบก่อน
  • ให้สอง thread แก้ตัวแปรเดียวกันโดยไม่มี Lock
  • commit โฟลเดอร์ .venv หรือไฟล์ข้อมูลลับ
  • ทำงานบน main โดยตรง จนงานของหลายคนชนกัน
  • เขียนข้อความ commit ว่า “update” ซึ่งไม่บอกอะไรเลย

สรุป

  • บล็อกของ Hula แปลงเป็น Python ได้ตรง ๆ: Repeat คือ for repeat until คือ while broadcast คือ threading.Event
  • คลาสจำลองโดรนช่วยตรวจตรรกะก่อนบิน มุมเลี้ยวของรูป คือ และปิดวงหลัง ช่วง
  • thread ที่ใช้ตัวแปรร่วมต้องมี Lock และต้องรอให้ thread จบด้วย join()
  • Git มีสี่พื้นที่ และ GitHub flow คือ branch → commit → pull request → review → merge
  • ข้อความ commit ตาม Conventional Commits และห้าม commit ข้อมูลลับ

แบบฝึกตรวจความเข้าใจ

  1. บล็อก Repeat 5 times ตรงกับคำสั่ง Python ใด
  2. รูปหกเหลี่ยมด้านเท่า ด้านละ 100 cm ต้องเลี้ยวครั้งละกี่องศา และบินรวมกี่ cm
  3. รูป จะกลับจุดเริ่มหลังบินกี่ช่วง
  4. คำสั่ง Git ใดย้ายการเปลี่ยนแปลงจากโฟลเดอร์งานไปพื้นที่เตรียม
  5. เผลอ commit API key ขึ้น GitHub แล้ว สิ่งแรกที่ต้องทำคืออะไร
เฉลย
  1. for _ in range(5):
  2. และบินรวม cm
  3. ช่วง
  4. git add
  5. เปลี่ยนหรือยกเลิก API key นั้นทันที เพราะการลบจากประวัติภายหลังไม่รับประกันว่ายังไม่มีใครคัดลอกไป

สรุปสูตรสำคัญ

มุมเลี้ยวของรูปหลายเหลี่ยมและรูปดาว {n/k}
จำนวนช่วงก่อนกลับจุดเริ่ม

แหล่งอ้างอิงหลัก

  1. Shenzhen HighGreat Innovation Technology Development Co. HULA all-in-1 drone and Hula app user manual. link
  2. Python Software Foundation. The Python tutorial (Python 3.14 documentation). link
  3. Chacon, S., & Straub, B. (2014). Pro Git (2nd ed.). Apress. link
  4. GitHub. GitHub flow. GitHub Docs. link
  5. Conventional Commits. Conventional Commits 1.0.0. link
  6. GitHub. Removing sensitive data from a repository. GitHub Docs. link
  7. OWASP Foundation. Secrets management cheat sheet. OWASP Cheat Sheet Series. link

อ่านเพิ่มเติม

ศึกษาหน่วยความรู้ที่กำหนดล่วงหน้า ดูสื่อประกอบ และทำ quiz ประจำโมดูล

พร้อมใช้D02

คู่มืออ้างอิงบล็อกคำสั่ง Hula

คู่มืออ้างอิงบล็อกคำสั่งของแอป Hula (HighGreat) ครบทั้ง 10 หมวด 66 บล็อก — Flight order, Laser, Sensor, Multimedia, AI-Recognition, Control, Events, Operators, Variables, My Blocks — พร้อมหน้าที่ เงื่อนไข ตัวอย่าง และ 5 โปรแกรมตัวอย่างระดับเริ่มต้นถึงซับซ้อน
พร้อมใช้D02

ตัวอย่างการเขียนโปรแกรมโดรน Hula 5 ระดับ

5 โปรแกรมตัวอย่างสำหรับแอป Hula ตั้งแต่บินตรงแล้วลงจอด สี่เหลี่ยมด้วยลูป Repeat ดาว 5 แฉกพร้อมไฟ เลี่ยงสิ่งกีดขวางด้วยเซนเซอร์และตัวแปร ไปจนถึงภารกิจค้นหา QR Code แบบหลายเธรดขนาน พร้อมคำอธิบายทีละบรรทัด
อยู่ระหว่างพัฒนาD02

Git และการทำงานร่วมกันแบบมีเวอร์ชัน

commit branch pull request และการจัดการโค้ดโครงงาน

ในชั้นเรียน / ภาคสนาม

ปฏิบัติการในห้องแล็บหรือภาคสนามตามใบงาน พร้อม checklist ความปลอดภัย

หลักฐานการเรียนรู้: ใบงานที่ผ่านการตรวจและผล quiz

แบบทดสอบประจำโมดูล

แบบทดสอบนี้ใช้ตรวจความเข้าใจ (formative) ไม่ใช่การสอบเก็บคะแนน

โดเมนความรู้: การเขียนโปรแกรมและเทคโนโลยีดิจิทัล