2008年12月25日 星期四

TclDocTest

自從玩過 python 的 doctest,我就一直覺得這概念實在很優秀,讓人勤於寫範例,又兼顧一些簡單的測試。因此,我也用 Tcl 實作了一個 TclDocTest。

不過目前只有一些簡單的比對,仍有許多需要調整修正的地方,整套我是用 XOTcl 實作的,這是我第一隻 XOTcl 程式。

仍需要修正的部分做些簡單的紀錄 ...

1. 應該以一個 Example 為單位,一個 Example 只要一個步驟錯誤便可以 return 出去。
2. 加入 puts 的測試
3. 計算每個 Example 的測試時間,可以了解每個測試所花費的時間。
4. 仍需可以印出 Example 的標題(標題有可能是一個 proc 的名字,或是一個較為完整的測試)
5. 每個測試最好可以用一個獨立的 interp
6. 可以實作一些結果的巨集,讓人寫測試結果不需要這麼斤斤計較(這的確會讓人感到寫範例很厭煩)
# % set x 100.1 ;# 我只想檢查它是否為浮點數
# <<float>>
# % set y 123 ;# 根本不想檢查這行
# <<pass>>
# % add $x $y ;# 這才是我想檢查的部分
# 223.1


7. 總測試個數
8. 總測試時間
9. 總錯誤個數

有空的時候就開始修正這些 ...

2008年12月18日 星期四

什麼是重構?

其實大師們三兩句就講出重構的精神,以下擷取於
重構 - 改善既有代碼的設計 (Refactoring: Improving the Design of Existing Code) 一書
Martin Fowler 著
侯捷 熊節 / 合譯


「程式碼被閱讀和修改的次數遠遠多於它被編寫的次數。保持程式碼易讀、易修改的關鍵,就是重構。」

「在不改變程式碼外在行為的前提下,對程式碼作出修改,以改進程式的內部結構。」

如果上面那句是用來形容重構的關鍵,那底下這句就是平實的呈現重構的過程。我相信這兩句話已經可以表達出90% 的重構精神了。

2008年12月12日 星期五

Eclipse & DLTK 的環境設置

主要是介紹如何設置一個 Tcl 的 IDE 環境。

打開 Eclipse 之後,進入 Window / References



接著來到 Tcl / Editor



將設定更改為。
這樣設置可以讓 Tab 縮排為四格空白,符合網路上標準縮排,避免 Tab 與 空白格之間的排版有問題。(這不是絕對要求,事實上遇到排版有問題的情況也不多,不過以防萬一我是會這樣設置的。)



接著來到 interpreters ,將設定更改為。
建置好這樣才可以直接執行 Tcl Script,並且將 Tcl/lib 全部載入進來。



建置 debug 環境。
去下載頁面。



下載 Tcl 的 Windows 版本



將路徑指向 dbgp_tcldebug.exe



這樣就完成了 debug 環境建置。

它還有一些好用的設置,好比說 Tcl / Editor / Template ,可以自行研究看看!

2008年12月11日 星期四

設置 Breakpoint 的盲點

這篇很短,不過解決這個問題花了我大概半個小時。

在測試 breakpoint 的時候,我隨手點一個 breakpoint ,怎麼執行都沒有停在 breakpoint 。我以為是我設置 debugmode 的時候出錯了。

找了老半天,才發現我把 breakpoint 設置在一個不會為真的 if 判斷式內 ...

切記!不要再把 breakpoint 設置在根本不會執行的地方來做測試了!

Eclipse & DLTK 的安裝方法

Eclipse 是一套 Open Source 的 IDE 。

而 DLTK 是它的一套 plugin ,讓 Dynamic Language 可以在 Eclipse 上有 IDE 。

安裝 Eclipse 的方法可以輕易的在網上搜尋到,所以這邊是介紹 DLTK 的安裝方法。
(若無法順利執行 eclipse.exe 請參考這個網站)


首先,必須先完成 Eclipse 的安裝。


接著進入此網站下載並安裝以下的 plugin
RSE-runtime-core-3.0.2.zip
emf-runtime-2.4.1.zip
Dynamic Languages Toolkit Bundles - SDK

Eclipse 安裝 plugin 的方法很簡單,只要解壓縮時指定放置在 Eclipse 安裝路徑底下,並執行解壓縮即可。

Ex : C:\Eclipse\

完成以上動作,便可以開始使用 Eclipse & DLTK 了。

下一篇再紀錄如何設置環境。

利用 ruby 的 net/http 套件將網路上的圖片抓下來

底下是使用ruby獨有的寫作方式來抓取圖片,可以與 Tcl 跟 Python 做些比較
這是 Tcl 版本
這是 Python 版本

require 'net/http'

# 開啟一個二進制檔案,將檔案物件放入 f 這個變數內
open('yahoo_logo.gif', 'wb') do |f|
    
# 將底下網址回傳的內容放入 image 這個變數內
    
image = Net::HTTP.get('l.yimg.com', '/tw.yimg.com/i/tw/hp/spirit/yahoo_logo.gif')
    
    
# 將內容寫入檔案內
    
f.write(image)
    
    
# 關閉檔案
    
f.close()
end



這樣就會在執行目錄底下產生一個叫做 yahoo_logo.gif 的圖片了

2008年11月26日 星期三

簡單的 Server & Client for windows

這是從http://nxforce.blogspot.com/2008/07/cc-socket-connection.html看來的,非常的清楚!除了提供檔案下載,還逐行解釋,甚至連怎麼編譯都有交代。

不過對我這個菜鳥來說,看到gcc ..... -lwsock32 這個參數倒是挺陌生的 ...

原來這是 link 到 WindowAPI 的方法,我們可以在他提供的兩個檔案中看到
#pragma comment(lib,"wsock32.lib")


而這行就需要這個參數!雖然我還是不太了解 #pragma comme .. 的意思啦 ...


這裡可以看到許多 C code ,裡面有 echo server / client !
http://src.wtgstudio.com/list_user.php?uid=jackal

2008年11月25日 星期二

製作 sha1c.so on arm

本來是想 build vfs1.3 與 Mk4tcl 在 arm 上,讓 arm 可以直接 source critcl2.kit ,但無奈功力不足 ...

先是成功 build vfs1.3,但 tclsh critcl2.kit 時依舊有錯誤訊息,查了一下才發現它在 package require Mk4tcl 時出錯了。後來嘗試 build Mk4tcl ,還真是困難重重,編譯成功了,但卻無法 package require Mk4tcl 進來 ...

再來我就換個方向,換編譯 vqtcl ,原先 tclsh critcl2.kit 時,可以在 source code 裡面看到若是 Mk4tcl 有錯,就換 package require vfs::mkcl ,而 vqtcl 這個套件就內涵 vfs::mkcl,而這次比較順利,直接就編譯過去了,本想說終於可以在 arm 上玩 kit 檔案,卻還是出現這樣的錯誤訊息 ...

couldn't read file "/home/root/critcl2.kit/main.tcl": Success
while executing
"source /home/root/critcl2.kit/main.tcl"
("uplevel" body line 1)
invoked from within
"uplevel [list source [file join $self main.tcl]]"


看起來應該是 vqtcl 處理的不夠完整,無法直接 source .kit 內的檔案 ...

看來要在 arm 上玩 .kit 檔案要擇日再研究了!




回到正題,雖然無法在 arm 上把玩 kit 檔案,但依舊是可以用一些偷吃步的方法 ...!

必須要先有相關環境

在這邊下載 - sdx.bat、sdx.kit 兩個檔案
http://www.equi4.com/pub/sk/

在這邊下載 - Tclkitsh (for windows) (有些防毒軟體會認為它是病毒,但其實只是雙重副檔名造成誤判,因此只要記得下載時把副檔名 exe 拿掉即可)
http://www.equi4.com/pub/tk/downloads.html
此執行檔下載回來之後,記得更名為 tclkitsh.exe (配合 sdx.bat)


環境都有之後
先在 windows 底下,下達這樣的指令
sdx unwrap critcl2.kit


這時候就會出現 critcl2.vfs 的資料夾

裡面我們會用到的有

critcl2.vfs/lib/critcl/ -> 將其複製到 arm 上的 /usr/lib/tcl8.4 底下
critcl2.vfs/lib/platform/ -> 將其複製到 arm 上的 /usr/lib/tcl8.4 底下
critcl2.vfs/lib/app-critcl/critcl.tcl -> 複製到 arm 上任意資料夾,此為主要執行檔案
critcl2.vfs/lib/app-critcl/runtime.tcl -> 與上述檔案放在同一個資料夾內
(以上兩隻程式我是放在 /home/root/critcl4arm/ 底下)

到了這個階段,可以試試看執行 tclsh /home/root/critcl4arm/critcl.tcl ,應該會跳出 help 資訊。

有了以上環境,有了 gcc ,就可以開始編譯 .so 檔了!

sha1c 的 source code 在 tcllib 裡面有。
http://sourceforge.net/projects/tcllib/

抓下 tcllib-1.11.tar.gz 之後,tcllib-1.11\modules\sha1\ 裡面存放著許多檔案,但我們需要用到的只有 sha1c.tcl、sha1.c、sha1.h 這三個檔案,將這三個檔案放置在同一個資料夾,並進入此資料夾內下達這樣的指令

tclsh /home/root/critcl4arm/critcl.tcl -pkg sha1c.tcl


等待數秒,就可以看到此資料夾底下出現一個 lib/ ,而裡面所放的 sha1c/ 資料夾就是 sha1c 的 .so 套件了!將其移動到 /usr/lib/tcl8.4/ 裡,接下來就可以享受在 arm 上用 c 來演算 sha1 的神速了!

Tcl - sha1::sha1 -bin [string repeat a 500000]
約 20 秒

C - sha1::sha1 -bin [string repeat a 500000]
約 0.17 秒

2008年11月24日 星期一

Tcl extension 實作

這算是我開始學 C 的原因之一吧?雖然完全沒有用 C 實作一個成品出來,但至少對於 gcc 稍微有點感覺了。

說真的,Tcl extension 實作還真的可以完全不用研究 C ... 但花了兩天學了 C 之後,對於它的錯誤訊息卻非常有勇氣去處理跟閱讀,因此,我解決了它的錯誤 ... 如今,我也可以輕鬆的利用 Critcl 來用 C 寫出 Tcl 的 shared library 了!

首先必須先有 gcc 的 runtime,這其實只要安裝 dev-cpp,然後在環境變數裡新增 C:\Dev-CPP\bin 就可以了。

之後可以參考這個網站
http://www.garyfeng.com/wordpress/2002/11/20/critcl-builds-c-extensions-on-the-fly/

但照著這個網站去作,在 windows 上還是可能會出問題,由於 critcl 會自動在 ~ 目錄建立一個 .critcl 資料夾,而它會將需要編譯的檔案都放在裡面,開始編譯的階段,它會嘗試給 gcc 這些檔案的完整路徑,而問題就是在這邊發生的。

像這樣
gcc C:\Documents and Settings\user\......


可想而知是一定會出錯的 ...


解決的方法,一是將它指定的目錄改為非空白路徑,二是幫它處理好這段。這兩種方式都不會很困難。

之後就可以在 windows 上正常編譯出 .dll 檔案了!

2008年11月22日 星期六

C 語言學習 Part II

我好像都跳著記錄 ... 通常有疑惑的部分我才會自己寫個測試看看,所以感覺是測試筆記 ...

說不定沒測試的部分雖然看懂了但卻沒有記下來 ... !不過不管怎樣就繼續往下吧!



這一樣是從良葛格學習筆記繼續的。

這是直接從某位址放進字串的方法
#include <stdio.h>

int main(void) {
    
// 在 *str1 這個位址放入 h,然後依序放入 ello
    char *str1 = "hello";
    
    
// 偏移量為 0 即為 h 
    printf("%c\n", str1[0]);
    
    
// 偏移量加 1 即為 e 
    printf("%c\n", *(str1 + 1));
    
    
// 偏移量加 5 也是放了一個 '\0',與陣列字串一樣? 
    if (str1[5] == '\0') {
        
puts("如果偏移量加 5 的位址放了 '\\0' 就會看到這行");
    
}
}



釐清底下兩個例子
#include <stdio.h>

int main(void) {
    
// 這個例子要證明底下兩個是不同的
    
    
// 例子一
    char *str1[] = {"abc12", "def", "ghi", "jkl", "mno", "pqr", "stu", "vwx", "xy1", "234"};
    
    
// 例子二
    char str2[10][4] = {"abc", "def", "ghi", "jkl", "mno", "pqr", "stu", "vwx", "xy1", "234"};
    
    
printf("str1\t\t指向這個位址\t\t%X\n", str1);
    
    
printf("str1[0]\t\t的位址是\t\t%X\n", &str1[0]);
    
printf("str1[0]\t\t這個位址所存放的是\t%s\n", str1[0]);
    
    
printf("str1[1]\t\t的位址是\t\t%X\n", &str1[1]);
    
printf("str1[1]\t\t這個位址所存放的是\t%s\n", str1[1]);
    
    
// 這邊可以看的出來,它的空間會配合宣告而變動 
    printf("str1[0][6]\t這個位址所存放的是\t%c\n", str1[0][6]);
    
    
puts("");
    
    
printf("str2\t\t指向這個位址\t\t%X\n", str2);
    
    
printf("str2[0]\t\t的位址是\t\t%X\n", &str2[0]);
    
printf("str2[0]\t\t這個位址所存放的是\t%s\n", str2[0]);
    
    
printf("str2[1]\t\t的位址是\t\t%X\n", &str2[1]);
    
printf("str2[1]\t\t這個位址所存放的是\t%s\n", str2[1]);
    
    
printf("str2[0][4]\t這個位址所存放的是\t%c\n", str2[0][4]);
}

2008年11月21日 星期五

Hello World!

希望可以將基礎打好,因此還是從頭來過!

底下是參考了良葛格的學習筆記,並稍微修改成自己較容易記得的寫法。

這應該是最簡短的寫法了。
# include <stdio.h>

int main() {
    
printf("Hello World!\n");
}



雖然我指定 name 為 char ,但仍可以用 %d 將它印出來,只是會變為 66('A' = 65, 'B' = 66)
#include <stdio.h>

int main(void) {    
    
int age;
    
char name = 'B';
    
    
printf("%d\t%d\n", age, name);
}



甚至可以這樣做都不會出錯 ... 所以是否只是宣告記憶體配置空間而已呢?
#include <stdio.h>

int main(void) {    
    
int age = 'A';
    
char name = 65;
    
    
printf("%d\t%c\n", age, name);
}



這也令我非常困惑!
#include <stdio.h>

int main(void) {
    
// 照理說是宣告一個大小為 5 的陣列?但似乎有些隱藏的涵義? 
    char str[5];
    
printf("輸入 a 或是 b,不然就無法寫入 str:");
    
    
// 正規表示法? 
    scanf("%[a-b]", str);
    
printf("輸入的字元為 %s\n", str);    
    
    
// 不把暫存器中的東西清除的話,下次遇到 scanf 就會直接把暫存器中的東西拿去用而跳過去了 ... 
    fflush(stdin);
    
    
// 如果覆寫失敗,那 str 就會維持此行之前的內容 
    printf("輸入 1 或是 2,不然就無法覆寫 str:");
    
scanf("%[1-2]", str);
    
printf("輸入的字元為 %s\n", str);
    
    
// 為什麼這行不會出錯?而且若是上頭的 scanf 輸入了超過 10 個字以上,第 11 個字還會被取代成 a! 
    str[10] = 'a';
    
    
printf("%s\n", str);
    
    
// 厲害的是 size 還是 5? 
    printf("%d\n", sizeof(str));
    
    
return 0;
}



這裡要特別注意換行符號!
#include <stdio.h>

int main(void) {
    
// 一個字元 c 
    char c;
    
    
// 輸入超過一個字元,會將剩下的字元放進暫存區中 ... 等待下次的 getchar 或是 scanf 
    printf("請輸入一個字元:");
    
c = getchar();
    
    
// 若輸入了 a 並按下 Enter, 暫存器中會進行這樣的動作
    // 暫存器的值 = 'a\n', 取出第一個字元放進 c 並進行清除
    // 所以 c 將會存放 'a',且暫存器中仍有 '\n' 
    
    
    
// 直接印出 c 
    putchar(c);
    
putchar('\n');
    
    
// 若第一次輸入時輸入了超過一個字元,則這邊會將暫存器中的值再取出一個並清除
    // 若第一次輸入只輸入一個字元,這邊則會抓到換行符號! 
    c = getchar();
    
putchar(c);
    
putchar('\n');
    
    
return 0;
}



身為 Tcler ,這東西真的讓我感到非常親切 ......
#include <stdio.h>

int main(void) {
    
char c;
    
char str[20];
    
    
// 會直接加上換行符號 
    puts("請輸入字串:");
    
    
// 輸入一行字串且去掉換行符號 
    gets(str);
    
puts("輸入的字串為:");
    
puts(str);
    
    
// 這邊可以測試的出來 gets 會確實的將輸入的東西完全從暫存器中移除
    // 不會殘留換行符號在內,而是取出之後才處理!
    puts("如果這邊有停止的話,則暫存器內已無字元!請隨意輸入字元:"); 
    
c = getchar();
    
    
return 0;
}



今天先告一段落 ...

C 語言學習開始!

總是得起頭的,不是嗎?

儘管腳本語言在解決問題上迅速確實,但在面對效能問題時,還是得依靠 C 才有辦法克服。

Tcl 版本的 md5 演算法,在演算 500000 個 a 的時候,需要花費 20 秒(環境是在 arm9 上)
C 版本的 md5 演算法卻只要花費 0.17 秒 ...(環境是在 arm9 上)


更糟糕的是,雖然 Tcl 可以將 C source code 動態載入,但我依舊不知道該如何進行才好!

因為我根本不懂它們之間的關係 ...!真的只懂皮毛是不夠的 ...

2008年11月20日 星期四

Glade 的第一個 hello world

還真是辛苦 ...

網路上充斥著不能用的範例,大概是版本不同的關係 ... 搞了好久才弄出第一個 hello world ...

hello.py
# coding=utf-8

import gtk
import gtk.glade

class HellowWorld:
    
def __init__(self):
        
self.wTree = gtk.glade.XML('hello.glade') 
        
        
# 抓 glade 檔案裡面的 id="hello"
        
self.window = self.wTree.get_widget("hello")
        
# 如果有抓到這個物件
        
if (self.window):
            
# 關閉視窗時,執行 gtk.main_quit (其實就是把 GUI 關掉)
            
self.window.connect("destroy", gtk.main_quit)
            
            
# 就是少了這個害我畫面都出不來!
            
self.window.show()

if __name__ == "__main__":
    
hwg = HellowWorld()
    
gtk.main()



這是hello.glade,可以自己用glade3拉拉看
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--Generated with glade3 3.4.3 on Thu Nov 20 00:42:25 2008 -->
<glade-interface>
  
<widget class="GtkWindow" id="hello">
    
<child>
      
<widget class="GtkLabel" id="lab1">
        
<property name="visible">True</property>
        
<property name="label" translatable="yes">hello</property>
      
</widget>
    
</child>
  
</widget>
</glade-interface>

2008年11月18日 星期二

安裝 PyGtk 與其他相關套件

本來我是打算挑選 wxPython 做為 Python 語言的主力 GUI 工具,原因我已經忘了 ... 在沒有這個網誌之前就綜合網路上意見所決定的。

但還沒有開始研究 wxPython ,就突然因為工作關係必須開始學習 Gtk 了。雖然我已經忘記為什麼當初決定使用 wxPython ,但隱約記得 Gtk 給我的感覺並不好,但不是在開發階段,而是安裝階段 ...

不過不管怎樣,現在回頭來看 Gtk ,似乎感覺也是不賴的,當然我並沒有能力評斷兩種工具撰寫 code 的優劣,畢竟我只有用這兩種工具寫過小量的例子。





這邊做一些網上找回來的紀錄,都還只是網路上的片段句子,有空再回來証實並整理。


PyOpenGL 的安裝必須依靠 setuptools ,可以參考這個網站。
http://www.ibm.com/developerworks/cn/linux/l-cppeak3.html


PyOpenGL 的官方網站就在這囉 ...
http://pyopengl.sourceforge.net/


要讓 pyGtk 可以使用 OpenGL 的一些函式,就必須安裝 PyGtkGlExt (Python Gtk GL Extension?)才可以。底下這個官網也有 all-in-one ,可以一口氣安裝 PyGtk 、 PyOpenGL ... etc
http://www.k-3d.org/gtkglext/Main_Page


主角之一的 Gtk 官方網站
http://www.gtk.org/


其實真正要動只要安裝這個應該就可以動了 ... (可以在 Python 中 import gtk)
Gtk runtime++


glade 是可以為 Gtk 產生拖拉點放碼的工具,已經附在gtk-dev-2.12.9-win32-2.exe,但要更改一下它的捷徑 ... 要指向 C:\GTK\bin\glade-3.exe (不知道為什麼他原本是C:\GTK\bin\glade-2.exe)



這是可以參考的網站之一
http://vpoohtw.blogspot.com/2008/05/pygtk.html


至於 pyWin32 是可以利用 python 與 windows 的一些應用程式(如 outlook)溝通的套件。


pyCairo 是可以讓 Gtk 擁有比較炫的圖案


ATK(Accessibility Tool Kit) 讓程式能提供更友善、更易於操作的介面。


Pango 則是處理多國語言的好幫手 ...


這是一個 C 的 Gtk 的教學網站,有踩地雷的實例
http://debian.linux.org.tw/~kevpeng/




先這樣,有空再來整理 ...

2008年11月16日 星期日

破解無名小站

我其實可以理解想破解無名小站的心態,但我卻無法理解那些明明知道無名小站可以破解,卻還是要把裸照放在網路上的人 ...

身為一般使用者,想破解相簿自然是想看一些好康的,又或是與人結怨,想破解相簿讓他難堪,這是很容易理解的。

而身為技術人,骨子裡多少都會有一些不安分因子,總是會想要嘗試看看自己是否有辦法破解。

沒想到的是,無名小站的防護機制竟是如此的薄弱,連我這種菜鳥都可以輕易的破解自己跟朋友的相簿(朋友的相簿當然是經過他同意的) ...

奉勸大家真的不要再把隱私照放在網路上了!

2008年11月13日 星期四

用 Tcl 實作發送 Snmptrap

其實我還不是很熟 Snmptrap 啦 ... 只是在網路上看到了一篇 PPT ,就試著實作看看。

參考的網址如下:
http://www.chu.edu.tw/~hsiang/91_Class/NM2.ppt


符合 BER 格式的封包內容像這樣 ... (這算是補充 PTT 沒有的部份)



底下就是一份實作的範例

snmptrap.tcl
# Snmptrap 封包內容共三段
# Version + Community + Data(SNMP Protocol Data Unit (SNMP PDU))

# ------------------------------------
#            |Type
# ------------------------------------
# Version    |INTEGER {version-1(0)}
# Community  |OCTET STRING
# Data       |ANY (Sequence)
# ------------------------------------


# 其中 Data 又分為兩段
# PDU Header + PDU Data


# PDU Header 又分五段
# Enterprise + Agent address + Generic trap type + Specific trap code + Time stamp

# Enterprise—Identifies the type of managed object that generates the trap.
# Agent address—Provides the address of the managed object that generates the trap.
# Generic trap type—Indicates one of a number of generic trap types.
# Specific trap code—Indicates one of a number of specific trap codes.
# Time stamp—Provides the amount of time that has elapsed between the last network reinitialization and generation of the trap.

# ------------------------------------
#                       |Type
# ------------------------------------
# Enterprise            |ObjectIdentifier
# Agent address         |Hex
# Generic trap type     |INTEGER
# Specific trap code    |INTEGER
# Time stamp            |IntegerOrEnum
# ------------------------------------



# PDU Data 為一份序列(Sequence),序列內容可以包含一個以上的序列,結構如下
# [[OID + Value][OID + Value][OID + Value] ... [OID + Value]]



# ------------------------
# For demo send trap ...

# ----------------------
# import package
# ----------------------


package require asn
package require udp
package require ip


# -------------------------
# utility procedures
# -------------------------

# Figure out what this machine's IP address is.
# Refernce http:
//wiki.tcl.tk/3015 to rewrite this proc
proc myip {} {
    
set myip "127.0.0.1"
    
switch $::tcl_platform(platform) {
        
unix {
            
set buffer [exec /sbin/ifconfig]
            
set scan "inet addr:"
            
foreach line [split $buffer \n] {
                
if ![regexp $scan $line] continue
                
set s [expr [string first $scan $line 0] + [string length $scan]]
                
set f [expr [string first " " $line $s] - 1]
                
set addr [string range $line $s $f]
                
if { $addr != "127.0.0.1" } {
                    
set myip $addr
                    
break
                
}
            
}
        
}
        
windows {
            
catch { exec ipconfig } data
            
set buffer [split $data \n]
            
foreach line $buffer {
                   
if [regexp -nocase {IP Address} $line] {
                    
set myip [string trim [lindex [split $line :] end]]
                    
break
                
}
            
}
        
}
    
}
    
return $myip
}

proc myHexIP {} {
    
return [string range [ip::toHex [myip]] 2 end]
}


proc send_trap {args} {
    
# ---------------------
    # Configuration
    # ---------------------

    
# send trap to ... ?
    set server 127.0.0.1
    
set port 162

    
# 0 | 1
    set version 0

    
# public | password
    set community public

    
# 1.3.6.1.4.1.23041
    set enterprise "1 3 6 1 4 1 23041"

    
set agent_address [myHexIP]

    
set generic_trap_type 6

    
set specific_trap_code 0

    
# --------------------------------
    # start for encoding

    
set timestamp [clock second]


    
# Version
    set packet [asn::asnInteger $version]

    
# Version + Community
    append packet [asn::asnOctetString $community]

    
# PDU Header = Enterprise + Agent address + Generic trap type + Specific trap code + Time stamp
    set PDU_Header [asn::asnObjectIdentifier $enterprise]
    
append PDU_Header [binary format H* "4004$agent_address"]
    
append PDU_Header [asn::asnInteger $generic_trap_type]
    
append PDU_Header [asn::asnInteger $specific_trap_code]
    
append PDU_Header [asn::asnIntegerOrEnum 43 [expr ([clock seconds] - $timestamp) * 100]]

    
# PDU Data = [[OID + Value][OID + Value][OID + Value] ... [OID + Value]]
    set PDU_Data ""

    
foreach {OID value} $args {
        
set sequence [asn::asnObjectIdentifier $OID]
        
append sequence [asn::asnOctetString [encoding convertto $value]]
        
        
# 將 OID + Value 編成一個序列
        set sequence [asn::asnSequence $sequence]
        
        
# 放入 PDU_Data 主序列中
        append PDU_Data $sequence
    
}

    
# 把 PDU_Data 正式編成一個序列
    set PDU_Data [asn::asnSequence $PDU_Data]


    
# SNMP PDU = PDU Header + PDU Data
    set SNMP_PDU [format {%s%s} $PDU_Header $PDU_Data]

    
# OCTET STRING (0x04)
    set SNMP_PDU [asn::asnChoiceConstr 4 $SNMP_PDU]

    
# Version + Community + Data(SNMP Protocol Data Unit (SNMP PDU))
    append packet $SNMP_PDU

    
# 把 packet 正式編成一個序列
    set packet [asn::asnSequence $packet]




    
# 發送 trap / udp
    set s [udp_open]
    
fconfigure $s -blocking 0 -remote [list $server $port] -translation binary -encoding binary
    
puts -nonewline $s $packet
    
flush $s
    
close $s
}


接下來只要利用 send_trap 這個 proc 並傳兩個參數(OID、value),就可以打開接收器看成果了!

2008年11月11日 星期二

Ruby 的 Class

Ruby 非常的靈活,以下的範例可以輕鬆添加 Fixnum (應該像是其他語言的 int 類別)這個類別的功能


test1.rb
# reference : Beginning Ruby From Novice to Professional

# 讓我們在 Fixnum 這個類別中添加一些功能 ...
class Fixnum
    
def sec
        
self
    
end
    
    
def min
        
self * 60
    
end
    
    
def hour
        
self.min * 60
    
end
    
    
def day
        
self.hour * 24
    
end
end

puts "這是 1 這個物件的類別 = #{1.class}"
puts "1.sec  = #{1.sec}"
puts "1.min  = #{1.min}"
puts "1.hour = #{1.hour}"
puts "1.day  = #{1.day}"




test1.rb的output會像這樣
這是 1 這個物件的類別 = Fixnum
1.sec = 1
1.min = 60
1.hour = 3600
1.day = 86400



可想而知 Ruby 的生產速度定是非常優秀的了

Ruby

其實不管從哪個層面來看,我都是個不合格的 Programmer ... 在我的觀念中,一個 Programmer 要會的基本語言就是 C ,但我卻只會 C 的皮毛而已。

第一個學習的語言就是腳本語言,這讓我很難回頭靜下心來學習 C ,我一直都是個目標導向的人,因此都是需要什麼才學習什麼,而這個原因幾乎也就是腳本語言誕生的目的 ...

我想大概哪天需要寫高效率的東西才會讓我靜下心好好研究 C 的奧妙吧 ...

離題了,標題是 Ruby ,會研究起 Ruby 是因為想多了解幾種腳本語言 ... 好吧,其實有很大原因是因為很好奇 Python 與 Ruby 的戰爭中,那些神人們所說論點,讓我想知道這兩種語言在根本上的差異。

因此 Ruby 也是我日後會嘗試接觸的語言之一了!

Ruby 與 Python

Ruby 與 Python 的戰爭我想是永無止息的一天了 ...

對我這個菜鳥來說,其實我根本不知道兩者都優劣在哪,網路上充斥著似乎兩種語言都精通的高手們(還有精通超過五種語言的高手呢),總是可以一直斥責對方,真神人境界也 ...

不知道何年何月何日我才可以有資格評斷兩種以上的語言呢 ...

目前台灣在網路上擁戴 Python 與 Ruby 的,我感覺就好像台灣政治的藍與綠,在氣質有根本上的不同 ...

藍 - Python
綠 - Ruby


說不定做個調查,還真的是如此呢!

不過常常看到某方的擁護者提出一個觀點-「寫程式是因人而異,所以那不是我們程式語言的錯」

這個論點其實是非常有問題的,工具的使用當然是因人而異,但卻不影響這個工具的客觀立場。

自行車給選手級的人騎著,當然是有機會可以超越機車,不過如果要環島一周,是那個工具比較優秀呢?

2008年11月6日 星期四

object & return

Python 的 class 功能真的比我想像中的強大多了 ...

這是一個範例,它定義了 object 各種需求的 return function
class a:
    
def __int__(self):
        
return 123456789
    
    
def __str__(self):
        
return 'this is a string'
    
    
def __unicode__(self):
        
return 'unicode here'



使用起來會像這樣
>>> s = a()
>>> int(s)
123456789
>>> str(s)
'this is a string'
>>> unicode(s)
u'unicode here'



可以應用在蠻多場合的!

2008年11月5日 星期三

class處理未宣告的method

class 有一個很神奇的 method,只要你在 class 中有宣告 def __getattr__(self, name),它便會把所有無法處理的優先丟給它處理。


這是一個範例
class controlMethod:
    
def otherMethod(self, name):
        
def method(*args, **kwargs):
            
print name, args, kwargs
        
return method
    
    
def __getattr__(self, name):
        
return self.otherMethod(name)
    
    
def hello(self):
        
print 'hello world'




使用起來會像這樣...
>>> c = controlMethod()
>>> c.hello()
hello world
>>> c.nono(1,2,a='die')
nono (1,2) {'a': 'die'}




在不同的應用上,也可以這樣設計
def method(*args, **kwargs):
    
print args, kwargs

class controlMethod:
    
def __getattr__(self, name):
        
return method
    
    
def hello(self):
        
print 'hello world'

Django的escape

由於 escape 通常都會大量的使用,因此在Django 1.0 的 template 中,已經不需要設置 filter escape 了,因為它已經是預設值了

所以又多了一個 on/off 功能..

這樣才會停止 escape 的功能
{% autoescape off %}
    {{ variable }}
{% endautoescape %}


當然也可以這樣使用,variable1 將會停用 escape 功能,variable2 就啟用 escape 功能
{% autoescape off %}
    {{ variable1 }}
    {% autoescape on %}
        {{ variable2 }}
    {% endautoescape %}
{% endautoescape %}


這樣子也會啟用
{% autoescape off %}
    {{ variable|escape }}
{% endautoescape %}

2008年10月30日 星期四

開發軟體時的自我要求

RD與業務的戰爭


這應該是個永遠的議題




其實我也染了許多業務的惡習,就是凡事容易求快,習慣先求成果,卻少了嚴謹的規劃。雖然資歷上我還是個菜鳥Programmer,但短短時間內卻也因為這樣的陋習多了不少困擾

1. 新人進來時,什麼都要親自下海教授
當然不是說就不需要教育訓練了,只是如果能整理一個好的教學文件,這樣的困擾可以減低不少,也可以增加新人學習效率

2. 移交部分開發工作給同事
反過來想,若是我必須接手另一個同事開發到一半的工作,我會希望有完整的文件可以瀏覽,並且coding style良好

3. 隨著code體積龐大,測試時間就只能等比往上加
這我想我連一般的programmer的資格都不夠,若是照著TDD來走,就不會有現在這樣的窘境了
可以參考這個網站

4. 使用者使用不便
這可能還是需要時間來磨練,現階段的我大概只能靠提供完整一點的文件來解決這個問題


要解決以上的問題,可能需要好好制定一些自我要求的流程了




除了事前完整的SA,在coding階段還是要有以下動作

1. 功能基本流程圖
若SA階段切不夠細,這邊就必須再設計出來

2. 功能概念簡報
盡量可以將每個功能切割成5張簡報以內可以呈現

3. Test code

4. 命名方式整理
不管是變數還是函式,都需要統一其命名格式,當然如果該程式語言有一個標準更好

5. coding style整理
其實上述應該算在這條之中,但刻意獨立出來重視命名方式

6. 邏輯性註解以及TODO

7. 功能使用方式簡報

8. 版本Release的更動紀錄


目前先這樣,有想到再補


可能會有些矯枉過正,但先這樣實行看看吧!不做做看怎麼會知道呢!

2008年10月29日 星期三

Django的urls.py

在urls.py裡面,你會看到這樣的描述
(r'^admin/doc/', include('django.contrib.admindocs.urls'))


了解這樣的描述之前,要先了解urls.py的行為

其實講白了就是對照網址,看要給誰處理罷了。對照的方式為正規表示法,而且是由上到下,比對成功就丟給後面的function處理了,所以要小心設計,不然會有一些function苦無用武之地....

但為什麼上面的描述要加上include的字樣?

可以注意到前端的正規表示並未加上$(在正規表示法中,$代表結尾),因此它可以比對大範圍的admin/doc/....網址,比較有趣的是,include就是把....部份丟給另一個urls.py處理,因此每個app也可以擁有自己的urls.py,不一定要將所有的「轉址」放在專案根目錄的urls.py裡面

2008年10月26日 星期日

設置Django的Admin功能

其實我還不清楚這功能可以幹麼,哈!不過我看很多人都在想辦法設置它,所以我也先設置起來玩玩看!

1. 更改settings.py

共要改三個地方
DATABASE_ENGINE = 'sqlite3'
DATABASE_NAME = 'c:/django_www/data.db'


INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
)




2. 配置sqlite3與data.db

打開DOS Prompt,下達
manage.py syncdb


然後照著上面的提示輸入


3. 更改urls.py

直接把內容換成這樣
from django.conf.urls.defaults import *

# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    
# Example:
    
# (r'^www/', include('www.foo.urls')),
    
# (r'^$', 'www.helloworld.index'),
    
# Uncomment the admin/doc line below and add 'django.contrib.admindocs' 
    
# to INSTALLED_APPS to enable admin documentation:
    
# (r'^admin/doc/', include('django.contrib.admindocs.urls')),

    
# Uncomment the next line to enable the admin:
    
(r'^admin/(.*)', admin.site.root),
)


然後就可以到http://localhost/admin/看看admin頁面了!

2008年10月24日 星期五

Python+Django+Apache

這篇文章講的非常清楚
http://thinkhole.org/wp/2006/04/03/django-on-windows-howto/


把四個需要安裝的整理出來

1. 安裝Python - version 2.5.2.2
http://www.activestate.com/store/download_file.aspx?binGUID=4656928e-d642-4aec-815e-b8d819b7cdbb

若無特殊需求,使用default一直下一步就可以了


2. 安裝Django - version 1.0
http://www.djangoproject.com/download/1.0/tarball/

下載完成之後,解壓縮後進入資料夾,打開cmd.exe,執行
python setup.py install

即可安裝完成


3. 安裝Apache,請到下載頁面選擇最新版本(xxx為版本)apache_xxx-win32-x86-no_ssl.msi
http://apache.ntu.edu.tw/httpd/binaries/win32/
(我下載的為apache_2.2.10-win32-x86-no_ssl.msi)

原本的路徑應為 - C:\Program File\....\Apache2.2
但為了修改方便建議安裝至 - C:\Apache


4. 安裝mod_python,請選擇與Python版本與Apache版本相符的最新版本,此為讓Apache支援python的django模組
http://apache.stu.edu.tw/httpd/modpython/win/3.3.1/
(我下載的為mod_python-3.3.1.win32-py2.5-Apache2.2.exe)

在安裝mod_python的時候,他會要你選擇一個資料夾,請指定你Apache的安裝目錄


全都安裝好之後,http://localhost/應該就可以看到apache的首頁了


但我們想要看的應該是django架構的網站,而不是apache內建的首頁,因此要開始做一些設定

1. 先用django建立一個專案
安裝django成功之後,可以到這裡C:\Python25\scripts看到一個叫django-admin.py的檔案(當然實際還是得看安裝位置),它其實是django為了方便使用者所製作出來的一隻可帶參數執行檔。因此當我們要用django在C:\目錄底下建立一個新的專案,就打開DOS Prompt,並輸入這兩行

cd c:\
c:\Python25\scripts\django-admin.py startproject django_www


就可以看到在c:\產生了一個django_www的目錄了!這時候我們可以用django內建的web server來看看畫面,打開DOS Prompt,輸入
c:\django_www\manage.py runserver


然後再到這裡http://localhost:8000/就可以看到django所產生的首頁了!看完之後可以把django內建的web server停止,因為接下來我們就要開始使用Apache當作web server了!(不過不停止也不會影響就是了)



2. 跟Apache說我要開始使用django了!
首先要先把mod_python載入apache的服務,因此需要打開C:\Apache\conf\httpd.conf來編輯,先隨便找一個地方加入這一行
LoadModule python_module modules/mod_python.so


然後也是隨便找一個地方貼上
MaxRequestsPerChild 1
<Location "/">
    SetHandler python-program
    PythonPath "['C:/'] + sys.path"
    PythonHandler django.core.handlers.modpython
    SetEnv DJANGO_SETTINGS_MODULE django_www.settings
    PythonAutoReload Off
    PythonDebug On
</Location>
Alias /site_media C:/django_www/media
Alias /media C:/Python25/Lib/site-packages/django/contrib/admin/media
<Location "/site_media">
    SetHandler None
</Location>
<Location "/media>
    Order Deny,Allow
    Allow from all
    SetHandler None
</Location>


然後重新啟動Apache就可以在http://localhost/看到django的首頁了!