โปรแกรมบล็อกและ Git สำหรับงานทีม
UAT 104 การเขียนโปรแกรมคอมพิวเตอร์
บทเรียน
เมื่อเรียนจบโมดูลนี้ ผู้เรียนจะสามารถ
- แปลงโปรแกรมบล็อกของโดรน Hula เป็นโค้ด Python ที่มีโครงสร้างเดียวกัน
- เขียนคลาสจำลองโดรนและใช้คำนวณเส้นทางรูปหลายเหลี่ยมและรูปดาว
- อธิบายการทำงานคู่ขนานด้วย thread การส่งสัญญาณ และการป้องกันตัวแปรร่วม
- ใช้ Git บันทึกประวัติงาน แตก branch และรวมงานตาม GitHub flow
- เขียนข้อความ commit ตาม Conventional Commits และไม่ส่งข้อมูลลับขึ้น repository
ทำไมต้องรู้
นักศึกษาหลายคนเริ่มเขียนโปรแกรมโดรนด้วย โปรแกรมบล็อก เช่น แอป Hula ของ HighGreat ซึ่งลากบล็อก “ขึ้นบิน” “บินไปข้างหน้า” “เลี้ยว” มาต่อกัน โปรแกรมบล็อกช่วยให้เห็นโครงสร้างของโปรแกรมชัดเจน แต่เมื่องานใหญ่ขึ้น ต้องใช้ภาษาที่ทดสอบได้ นำกลับมาใช้ได้ และทำงานร่วมกันเป็นทีมได้ ซึ่งคือ Python กับ Git
ข่าวดีคือแนวคิดเหมือนกันแทบทั้งหมด บล็อกหนึ่งบล็อกคือคำสั่งหนึ่งบรรทัด บล็อก Repeat คือ for บล็อก if คือ if สิ่งที่ได้เพิ่มคือเครื่องมือทดสอบ การจำลอง และประวัติการแก้ไขที่ตรวจย้อนได้
จากบล็อกสู่ Python
| บล็อก Hula | Python | หมายเหตุ |
|---|---|---|
| when ▶ clicked | ส่วนหลักของโปรแกรม | จุดเริ่มทำงาน |
| Take off / Landing | drone.take_off() / drone.land() | ต้องขึ้นบินก่อนและลงจอดเสมอ |
| Fly to Front with 60 cm | drone.fly_forward(60) | |
| Turn to Clockwise with 90 degree | drone.turn_clockwise(90) | |
| Repeat 4 times | for _ in range(4): | |
| repeat until [เงื่อนไข] | while not เงื่อนไข: | |
| Set / change [ตัวแปร] | count = 0 / count += 1 | |
| broadcast / when I receive | threading.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 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
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 ระดับในคลังความรู้โดรน
- เลือกตัวอย่าง Hula ระดับ 2–4 หนึ่งตัวอย่าง แปลงเป็น Python ด้วย
SimDroneและเขียนการทดสอบที่ยืนยันว่าเส้นทางปิดวงหรือเลี้ยวครบตามที่ตั้งใจ - สร้าง repository บน GitHub ของกลุ่ม ใส่
.gitignoreตั้งแต่ commit แรก และรวมไฟล์จากโมดูล 2 ถึง 4 - แต่ละคนแตก branch ของตนเอง เปิด pull request และ review งานของเพื่อนอย่างน้อยหนึ่งชิ้น
- ถ้ามีโดรน Hula ให้บินโปรแกรมบล็อกต้นฉบับในพื้นที่ปลอดภัยภายใต้การดูแลของอาจารย์ แล้วเปรียบเทียบกับผลจำลอง
ข้อผิดพลาดที่พบบ่อย
ระวัง
- ลืม Take off หรือ Landing ในโปรแกรมบล็อก
- เชื่อว่าเปอร์เซ็นต์ความเร็วในแอปเท่ากับ m/s ทั้งที่ต้องสอบเทียบก่อน
- ให้สอง thread แก้ตัวแปรเดียวกันโดยไม่มี Lock
- commit โฟลเดอร์
.venvหรือไฟล์ข้อมูลลับ - ทำงานบน main โดยตรง จนงานของหลายคนชนกัน
- เขียนข้อความ commit ว่า “update” ซึ่งไม่บอกอะไรเลย
สรุป
- บล็อกของ Hula แปลงเป็น Python ได้ตรง ๆ: Repeat คือ
forrepeat until คือwhilebroadcast คือthreading.Event - คลาสจำลองโดรนช่วยตรวจตรรกะก่อนบิน มุมเลี้ยวของรูป คือ และปิดวงหลัง ช่วง
- thread ที่ใช้ตัวแปรร่วมต้องมี Lock และต้องรอให้ thread จบด้วย
join() - Git มีสี่พื้นที่ และ GitHub flow คือ branch → commit → pull request → review → merge
- ข้อความ commit ตาม Conventional Commits และห้าม commit ข้อมูลลับ
แบบฝึกตรวจความเข้าใจ
- บล็อก Repeat 5 times ตรงกับคำสั่ง Python ใด
- รูปหกเหลี่ยมด้านเท่า ด้านละ 100 cm ต้องเลี้ยวครั้งละกี่องศา และบินรวมกี่ cm
- รูป จะกลับจุดเริ่มหลังบินกี่ช่วง
- คำสั่ง Git ใดย้ายการเปลี่ยนแปลงจากโฟลเดอร์งานไปพื้นที่เตรียม
- เผลอ commit API key ขึ้น GitHub แล้ว สิ่งแรกที่ต้องทำคืออะไร
เฉลย
for _ in range(5):- และบินรวม cm
- ช่วง
git add- เปลี่ยนหรือยกเลิก API key นั้นทันที เพราะการลบจากประวัติภายหลังไม่รับประกันว่ายังไม่มีใครคัดลอกไป
สรุปสูตรสำคัญ
| มุมเลี้ยวของรูปหลายเหลี่ยมและรูปดาว {n/k} | |
| จำนวนช่วงก่อนกลับจุดเริ่ม |
แหล่งอ้างอิงหลัก
- Shenzhen HighGreat Innovation Technology Development Co. HULA all-in-1 drone and Hula app user manual. link
- Python Software Foundation. The Python tutorial (Python 3.14 documentation). link
- Chacon, S., & Straub, B. (2014). Pro Git (2nd ed.). Apress. link
- GitHub. GitHub flow. GitHub Docs. link
- Conventional Commits. Conventional Commits 1.0.0. link
- GitHub. Removing sensitive data from a repository. GitHub Docs. link
- OWASP Foundation. Secrets management cheat sheet. OWASP Cheat Sheet Series. link
อ่านเพิ่มเติม
ศึกษาหน่วยความรู้ที่กำหนดล่วงหน้า ดูสื่อประกอบ และทำ quiz ประจำโมดูล
คู่มืออ้างอิงบล็อกคำสั่ง Hula
ตัวอย่างการเขียนโปรแกรมโดรน Hula 5 ระดับ
Git และการทำงานร่วมกันแบบมีเวอร์ชัน
ในชั้นเรียน / ภาคสนาม
ปฏิบัติการในห้องแล็บหรือภาคสนามตามใบงาน พร้อม checklist ความปลอดภัย
หลักฐานการเรียนรู้: ใบงานที่ผ่านการตรวจและผล quiz