หน้าเว็บ

วันอาทิตย์ที่ 9 มกราคม พ.ศ. 2554

วิธีทำให้ Python เรียก script อื่นทำงาน (execute external script)

ถ้าเรามี script ไฟล์ python (.py) อยู่ไฟล์นึง แล้วเราอยากให้ script ไฟล์ python อีกฝ่ายมาเรียกไฟล์แรกทำงาน(คล้ายๆการ include ใน php) ทำได้ดังนี้

สมมติว่ามีไฟล์ hello.py ซึ่งมีโค้ดดังนี้อยู่
print 'hello world'


คราวนี้ในไฟล์ที่สองชื่อ run.py จะเรียกใช้ hello.py

วิธีแรก
ในไฟล์ run.py ให้ใช้คำสั่ง execfile

...
execfile('hello.py')
...


กรณีแรกนี้คือสั่งเรียกไฟล์ python อื่น แต่ว่าถ้าเราต้องการเรียก script หรือ shell command ใดๆนอกจาก python ก็ทำได้เหมือนกัน โดยใช้วิธีที่สอง

วิธีที่สอง
ในไฟล์ run.py ให้ import โมดูลชื่อ os แล้วเรียก os.system(command) เช่น

import os
...
os.system('python hello.py')
...


หรือ

import os
...
os.system('cat hello.py | grep hello')
...


สำหรับวิธีแรกจะใช้ได้กับ python-2.x แต่ถ้าเป็น python-3.x ให้ใช้วิธีตาม link นี้แทน http://stackoverflow.com/questions/436198/what-is-an-alternative-to-execfile-in-python-3-0

ระบบที่ทดสอบ
Python: 2.6.6
OS: Ubunut 10.10

ที่มา
http://stackoverflow.com/questions/1027714/how-to-execute-a-file-within-the-python-interpreter

วันจันทร์ที่ 9 สิงหาคม พ.ศ. 2553

ใช้ printf library กับ TinyOS ผ่าน SerialForwarder

วันนี้ผมลองใช้ MoteLab ของ Harvard เพื่อจะทำการทดลองโปรแกรม TinyOS บนอุปกรณ์จริงจำนวนมาก ซึ่งผมลองดูตัวอย่างโค้ดของทาง MoteLab นั้นจะแสดงวิธีการเรียกค่าเก็บไว้ใน Message ที่ส่งผ่าน Master Serial Port ของทาง MoteLab

แต่ปัญหาต่อมาของผมคือตัวอย่างเหล่านั้นเป็นการส่งค่าประเภทที่เป็นตัวเลข ไม่ใช่ array และไม่ใช่ string ซึ่งที่ผมต้องการนั้นผมอยากจะใช้ library printf ของ TinyOS เพื่อแสดง String (ซึ่งเป็น array ของ char) แล้วส่งผ่านออกมาทาง Serial Port

ดังนั้นวิธีการของผมคือลองแกะโค้ดตัวอย่างของเค้าก่อนว่ามันต้องใช้ utility อะไรมาช่วยบ้าง ซึ่งโค้ดเค้านั้นเป็นภาษา python และมีการเรียกใช้ TestSerialMsg.py ซึ่งเป็น class ที่เก็บโครงสร้าง Message ที่ต้องการเอาไว้ ดังนั้นทำให้ผมพอจะเดาได้ว่าเราต้องมีการสร้าง PrintfMsg.py ขึ้นมาเพื่อใช้จัดการกับ Message ของไลบรารี printf ที่จะถูกส่งออกมา

ผมลองดู TestSerialMsg.py แล้วพบว่า class นั้นถูกสร้างมาจาก MIG ซึ่งใช้ในการ generate คลาสสำหรับโครงสร้าง Message นั้น แต่ว่าที่ผมเคยลองทำคือเอามา generate ออกมาเป็น .class ของภาษา java เท่านั้น ซึ่งตอนนั้นทำใน Makefile

ผมเลยลองสั่ง mig ดิบๆใน shell ดูก็พบว่ามันมี option ที่รองรับภาษา python ด้วย ดังนั้นผมจึงเริ่มไปไฟล์ที่เป็น structure ของ Message เริ่มต้นที่เป็นภาษา nesC ก่อน ก็พบว่ามันอยู่ที่ $(TOSROOT)/tos/lib/printf/printf.h

ดังนั้นก็ให้ไปที่ $(TOSROOT)/tos/lib/printf แล้วสั่งคำสั่งดังนี้

mig python -python-classname=PrintfMsg printf.h printf_msg -o PrintfMsg.py

จะได้ไฟล์ PrintfMsg.py ซึ่งไฟล์คลาสที่เก็บโครงสร้างของ Message แบบภาษา python ออกมา

ซึ่ง String นั้น Message นี้จะเก็บอยู่ตัวแปรอาเรย์ที่ชื่อ buffer ดังนั้นผมจึงลองไปดูที่ฟังก์ชัน getString_buffer() ที่ generate ออกมา และพบว่ามัน generate ผิด ไม่สามารถเรียกใช้ได้เพราะจะเกิด index out of bound นอกจากนั้นค่าที่ได้ยังไม่ใช่ String อีกด้วย โดยผมทำการแก้ไข 3 จุด คือ 1. เปลี่ยนขอบเขตเป็น 28 (จริงๆแล้วขึ้นค่า PRINTF_MSG_LENGTH ที่ define ไว้ใน printf.h) 2. ทำการจัดรูปเป็นเลขฐาน 16 แบบ 2 หลัก 3. ทำการแปลงเลขฐาน 16 ให้เป็น ascii character ซึ่งสุดท้ายแล้วฟังก์ชัน getString_buffer จะได้เป็นแบบนี้

import tinyos.message.Message
import binascii
...
...
def getString_buffer(self):
carr = "";
#for i in range(0, 4000):
for i in range(0, 28):
if self.getElement_buffer(i) == chr(0):
break
c = "%x" % (self.getElement_buffer(i) & 0xFF)
if (len(c) < 2):
carr += "0" + c
else:
carr += c
return binascii.unhexlify(carr)

*หมายเหตุ blog ผม indent มันเพี้ยน เวลาใช้จริงอย่าลืมจัด indent ให้ถูกต้อง

คราวนี้เวลานำไปใช้ก็ให้ลองดูตามตัวอย่าง TestSerial.py ของเค้า แล้วให้เพ่ิม

import PrintfMsg

แล้วเพิ่ม

self.mif.addListener(self, PrintfMsg.PrintfMsg)

และเวลาเรียกค่าออกมาให้ใช้

s = m.getString_buffer()

ก็จะได้ String ที่เกิดจากไลบรารี printf แล้วครับ แต่ว่าจะมีปัญหาเล็กน้อยคือถึงแม้เรา printf ออกมาเป็น String ยาวๆ แต่ว่าการส่งข้อมูลออกมาแต่ละครั้งจะเท่ากับความยาวที่กำหนดไว้เท่านั้น (ค่า default คือ 28) ดังนั้นมันจะตัดส่งออกมาทีละ 28 ตัวอักษร ถ้าต้องการให้มากกว่านั้นก็ลองไป compile ไลบรารี Printf กันใหม่ได้ครับ

ถ้าใครยังไม่เคยใช้ MoteLab เดี๋ยวว่างๆผมจะมาเขียนวิธีไว้ด้วยครับ

ระบบที่ทดสอบ
OS: Ubuntu 10.04
TinyOS: 2.1.1
Device: Harvard MoteLab

วันอาทิตย์ที่ 21 มิถุนายน พ.ศ. 2552

compile TinyOS-2.1.0 แบบ TOSSIM ไม่ได้

อันนี้อาจมีหลายสาเหตุ แต่สาเหตุที่ผมเพิ่งเจอล่าสุดคือ upgrade เป็น ubuntu 9.04 แล้วจากที่เคย compile ได้ก็ไม่ได้ซะงั้น
โดยอาการแรกของผมคือ มันฟ้อง หา g++ ไม่ได้
อันนี้แก้ง่ายมาก
sudo apt-get install g++
เป็นอันเรียบร้อย (g++ เป็น GNU C++ Compiler)

พอพ้น error แรกไปได้ติดปัญหาต่อไปคือ มันงอแงว่า หาไฟล์ Python.h ไม่เจอ
อันดับแรกเลยลองเช็คว่าเรามี file นี้อยู่ในเครื่องรึเปล่า โดยลองสั่ง
sudo find / -name Python.h
เพื่อใช้หา find ดูก็ได้ ถ้าเกิดมันบอกว่าไม่เจอ ซึ่งอาจเกิดจากการที่เราไม่มี python development package หรือไม่ก็เคยมี แแต่เป็นเวอร์ชันเก่า พอ upgrade เป็น 9.04 แล้วมันจะเปลี่ยน python เป็น 2.6 แล้วทำการ remove package ของเวอร์ชันเก่าทิ้ง ก็ให้แก้ download มาลงก่อนด้วย
sudo apt-get install python-dev
หรือถ้าต้องการระบุ version ของ python ก็ใช้เป็น
sudo apt-get install python2.5-dev
โดยเปลี่ยนเลข version ตามอัธยาศัย (ของผมคือลง python2.5 เพิ่มเข้าไปด้วย)

หลังจากลองใช้คำสั่งเดิมในการหาว่า Python.h อยู่ที่ไหนใหม่อีกครั้ง แบบของผมจะไปอยู่ที่
/usr/include/python2.5
ซึ่งให้จำ path นี้ไว้ แล้วไปแก้ในไฟล์ sim.extra ที่อยู่ใน $TOSROOT/support/make โดยให้เติมบรรทัดนี้เข้าไปใต้บรรทัด PFLAGS
CFLAGS += -I/usr/include/python2.5
และเปลี่ยนค่าของ PYTHON_VERSION ให้เป็นตัวที่เราใช้อยู่ ทั้งในไฟล์ sim.extra และ sim-fast.extra
PYTHON_VERSION=2.5
ซึ่งถ้าใครไม่รู้ว่า python ตัวเองเป็น version อะไรสามารถดูได้ด้วยคำสั่ง python -V

แล้วคราวนี้ลอง compile โปรแกรมให้เป็น tossim ใหม่อีกครั้ง ก็น่าจะผ่าน

ระบบที่ทดสอบ

TinyOS: 2.1.0 (via apt-get)
OS: Ubuntu 9.04

ที่มา
http://docs.tinyos.net/index.php/TOSSIM#Appendix_A:_Troubleshooting_TOSSIM_compilation

apt-get /usr/bin/python does not match the python default version. It must be reset to point to python2.6

ถ้าเกิดว่าใช้คำสั่ง apt-get upgrade หรือ apt-get install แล้วใน error มีคำว่า
/usr/bin/python does not match the python default version. It must be reset to point to python2.6
และลงท้ายว่า E: Sub-process /usr/bin/dpkg returned an error code (1)

อาจเกิดจากการที่เราสั่งเปลี่ยน version ของ python ในระบบเรา ซึ่งถึงแม้เราเปลี่ยนกลับแล้วก็แก้ไม่ได้

ให้แก้โดย ลบ link python เดิมออก แล้วสร้าง softlink ใหม่ดังนี้
sudo rm /usr/bin/python && ln -s /usr/bin/python2.6 /usr/bin/python

ระบบที่ทดสอบ
OS: Ubuntu 9.04
Python: 2.6

ที่มา
http://www.linuxforums.org/forum/ubuntu-help/146571-solved-upgrade-doesn-t-work.html

switch python version

ในกรณีถ้าในเครื่องมี python หลาย version เช่น 2.5 และ 2.6 สามารถทำการสลับ version ได้ด้วยวิธีต่อไปนี้

ก่อนสร้าง option สำหรับการสลับ version ก่อน
sudo update-alternatives --install /usr/bin/python python /usr/bin/python2.5 1
sudo update-alternatives --install /usr/bin/python python /usr/bin/python2.6 2

หลังจากนั้นเมื่อต้องการสลับให้ใช้คำสั่ง
sudo update-alternatives --config python
แล้วเลือกหมายเลขที่ตรงกับ version ของ python ที่ต้องการ

เสร็จแล้วให้ทดสอบด้วยคำสั่ง
python -V
หมายเหตุ ถ้าใช้แล้วเกิด side effect คือทำให้ apt-get เกิด error มีคำว่า /usr/bin/python does not match the python default version. It must be reset to point to python2.6 และลงท้ายด้วย E: Sub-process /usr/bin/dpkg returned an error code (1)
ให้แก้ด้วยวิธีนี้ http://ping2p.blogspot.com/2009/06/apt-get-usrbinpython-does-not-match.html

ระบบที่ทดสอบ
OS: Ubuntu 9.04
Python: 2.3, 2.4, 2.5, 2.6

ที่มา
http://pythonide.blogspot.com/2007/03/how-to-switch-between-python24-and.html

วันศุกร์ที่ 12 ธันวาคม พ.ศ. 2551

ใช้ python อ่านไฟล์ csv

วันนี้ต้องเขียนโปรแกรมอ่านค่าจากไฟล์ csv ซึ่งแปลงมาจากโปรแกรม spreadsheet (เช่น OOo Calc หรือ MS Excel) ก็เลยทดลองเขียนด้วย python ดู และไปเจอว่า python มี built-in module สำหรับจัดการ csv โดยเฉพาะเลย

สมมติว่าในไฟล์ filename.csv มีเนื้อหาไฟล์ดังนี้
3, a, xyz
8, t, www
4, x, abc

สามารถเขียนโปรแกรมได้แบบนี้
import csv

file = open("path/to/filename.csv", "r")
parser = csv.reader(file)

for field1, field2, field3 in parser:
print 'FIELD1=%s, FIELD2=%s, FIELD3=%s\n' % (field1, field2, field3)

file.close()

ผลลัพธ์ ได้ดังนี้

FIELD1=3, FIELD2=a, FIELD3=xyz
FIELD1= 8, FIELD2=t, FIELD3=www
FIELD1=4, FIELD2=x, FIELD3=abc

หรือว่าตรง for loop เราอาจเขียนแค่
for row in parser: แค่นี้ก็ได้ แล้วเวลาเรียกค่าก็ใช้ row[0], row[1], row[2] แบบนี้แทน

อ้างอิง
http://www.python.org/dev/peps/pep-0305/