2011年9月27日 星期二

耳機擴大器DIY

由於公司客服部門並不是獨立的隔離單位,接電話時常常受到其他部門的聲音影響。

雖然是客服人員帶著客服耳機聽電話,但由於客服耳機既不是全罩式,也不是雙耳式,

所以客服人員老是抱怨客戶來電聲音聽不清楚,

然而,電話機音量已經調整到最高了,效果依然不理想。

後來想到利用耳機擴大器來增加話筒音量,

可是網路查了又查,即便是網路拍賣的最便宜的也要近千元,

況且又不能直接使用,因為客服專用耳機的接頭就不太一樣,所以一定要改裝,

這樣下來,十幾部的客服電話的花費就會增加許多,

天啊,我只要簡單的把耳機音量擴大啊!!?

所以找上了一個國外專門自己DIY耳機的網站
http://gilmore2.chem.northwestern.edu/projects/index.htm 的 A Pocket Headphone Amplifier 章節

這個網站的幾乎都是愛好者的聚落,文件資源豐富,解說也很清楚。

所以我就參考了其中一組大家常用的耳機擴大器來自製,首先把基本結構電路先建立起來:

圖1:OP2134基本耳機擴大電路(單路)

2011年9月24日 星期六

在Powerbuilder下使用API執行Ping指令

利用 Windows 現成的 API 來實現這個功能
1.定義需要使用的結構變數
str_wsadata
unsignedinteger  version
unsignedinteger  highversion
character  description[257]
character  systemstatus[129]
unsignedinteger  maxsockets
unsignedinteger  maxupddg
string  vendorinfo

str_inaddr
unsignedlong  s_addr

str_ip_option_information
character  ttl
character  tos
character  flags
character  optionssize
unsignedlong  optionsdata

str_icmp_echo_reply
unsignedlong  address
unsignedlong  status
unsignedlong  roundtriptime
unsignedlong  datasize
unsignedinteger  reserved
unsignedlong  ptrdata
str_ip_option_information  options
character  data[256]


2.定義 External Functions
//Open the socket connection.
FUNCTION Integer WSAStartup( uint UIVersionRequested, ref str_WSAData lpWSAData ) library "wsock32.dll" alias for "WSAStartup;ANSI"
//Clean up sockets.
FUNCTION Integer WSACleanup() library "wsock32.dll" alias for "WSACleanup;ANSI"
//Create a handle on which Internet Control Message Protocol (ICMP) requests can be issued.
FUNCTION Ulong IcmpCreateFile() Library "icmp.dll"
//Close an Internet Control Message Protocol (ICMP) handle that IcmpCreateFile opens.
FUNCTION Ulong IcmpCloseHandle(ULong IcmpHandle) Library "icmp.dll"
//Send an Internet Control Message Protocol (ICMP) echo request, and then return one or more replies.
FUNCTION Ulong IcmpSendEcho(ULong IcmpHandle,ULong DestinationAddress,String RequestData,ULong RequestSize,ULong RequestOptions,ref str_icmpechoreply ReplyBuffer,ULong ReplySize,ULong Timeout) Library "icmp.dll" alias for "IcmpSendEcho;ANSI"

// 轉換IP字串成IP資料位址Convert a string that contains an (Ipv4) Internet Protocol dotted address into a correct address.
FUNCTION ULong inet_addr(string ip_addr) library "ws2_32.dll" alias for "inet_addr;ANSI"


3.定義 Instance Variables 常數
Constant ULong WS_VERSION_REQD = 257

Constant Long ICMP_SUCCESS = 0
Constant Long ICMP_STATUS_BUFFER_TO_SMALL = 11001  //Buffer Too Small
Constant Long ICMP_STATUS_DESTINATION_NET_UNREACH = 11002  //Destination Net Unreachable
Constant Long ICMP_STATUS_DESTINATION_HOST_UNREACH = 11003  //Destination Host Unreachable
Constant Long ICMP_STATUS_DESTINATION_PROTOCOL_UNREACH = 11004  //Destination Protocol Unreachable
Constant Long ICMP_STATUS_DESTINATION_PORT_UNREACH = 11005  //Destination Port Unreachable
Constant Long ICMP_STATUS_NO_RESOURCE = 11006  //No Resources
Constant Long ICMP_STATUS_BAD_OPTION = 11007  //Bad Option
Constant Long ICMP_STATUS_HARDWARE_ERROR = 11008  //Hardware Error
Constant Long ICMP_STATUS_LARGE_PACKET = 11009  //Packet Too Big
Constant Long ICMP_STATUS_REQUEST_TIMED_OUT = 11010  //Request Timed Out
Constant Long ICMP_STATUS_BAD_REQUEST = 11011  //Bad Request
Constant Long ICMP_STATUS_BAD_ROUTE = 11012  //Bad Route
Constant Long ICMP_STATUS_TTL_EXPIRED_TRANSIT = 11013  //TimeToLive Expired Transit
Constant Long ICMP_STATUS_TTL_EXPIRED_REASSEMBLY = 11014  //TimeToLive Expired Reassembly
Constant Long ICMP_STATUS_PARAMETER = 11015  //Parameter Problem
Constant Long ICMP_STATUS_SOURCE_QUENCH = 11016  //Source Quench
Constant Long ICMP_STATUS_OPTION_TOO_BIG = 11017  //Option Too Big
Constant Long ICMP_STATUS_BAD_DESTINATION = 11018  //Bad Destination
Constant Long ICMP_STATUS_NEGOTIATING_IPSEC = 11032  //Negotiating IPSEC
Constant Long ICMP_STATUS_GENERAL_FAILURE = 11050  //General Failure


4.icmp處理訊息函數
public function string wf_icmpmsg(unsignedlong pingresponse)
String  ls_ret

Choose case PingResponse
  Case ICMP_SUCCESS
    ls_ret = "Success!"
  Case ICMP_STATUS_BUFFER_TO_SMALL
    ls_ret = "Buffer Too Small"
  Case ICMP_STATUS_DESTINATION_NET_UNREACH
    ls_ret = "Destination Net Unreachable"
  Case ICMP_STATUS_DESTINATION_HOST_UNREACH
    ls_ret = "Destination Host Unreachable"
  Case ICMP_STATUS_DESTINATION_PROTOCOL_UNREACH
    ls_ret = "Destination Protocol Unreachable"
  Case ICMP_STATUS_DESTINATION_PORT_UNREACH
    ls_ret = "Destination Port Unreachable"
  Case ICMP_STATUS_NO_RESOURCE
    ls_ret = "No Resources"
  Case ICMP_STATUS_BAD_OPTION
    ls_ret = "Bad Option"
  Case ICMP_STATUS_HARDWARE_ERROR
    ls_ret = "Hardware Error"
  Case ICMP_STATUS_LARGE_PACKET
    ls_ret = "Packet Too Big"
  Case ICMP_STATUS_REQUEST_TIMED_OUT
    ls_ret = "Request Timed Out"
  Case ICMP_STATUS_BAD_REQUEST
    ls_ret = "Bad Request"
  Case ICMP_STATUS_BAD_ROUTE
    ls_ret = "Bad Route"
  Case ICMP_STATUS_TTL_EXPIRED_TRANSIT
    ls_ret = "TimeToLive Expired Transit"
  Case ICMP_STATUS_TTL_EXPIRED_REASSEMBLY
    ls_ret = "TimeToLive Expired Reassembly"
  Case ICMP_STATUS_PARAMETER
    ls_ret = "Parameter Problem"
  Case ICMP_STATUS_SOURCE_QUENCH
    ls_ret = "Source Quench"
  Case ICMP_STATUS_OPTION_TOO_BIG
    ls_ret = "Option Too Big"
  Case ICMP_STATUS_BAD_DESTINATION
    ls_ret = "Bad Destination"
  Case ICMP_STATUS_NEGOTIATING_IPSEC
    ls_ret = "Negotiating IPSEC"
  Case ICMP_STATUS_GENERAL_FAILURE
    ls_ret = "General Failure"
  Case Else
    ls_ret = "Unknown Response"
End Choose

Return ls_ret


5.範例程式碼:
str_wsadata  lstr_wsadata
str_icmp_echo_reply  lstr_reply
Ulong  lul_timeout , lul_address , lul_hIcmp , lul_size
Integer  li_res
String  ls_pingIP , ls_send , ls_msg
n_sizeof  ln_size

ls_pingIP = "168.95.1.1" //中華電信DNS
ls_send = "hello"
lul_timeout = 1000 //(ms , 1 秒)

lstr_wsadata.vendorinfo = space(256)
//建立SOCKET
li_res = wsastartup (WS_VERSION_REQD, lstr_wsadata)
if li_res <> 0 then return
//轉換IP位址字串
lul_address = inet_addr(ls_pingIP)
if lul_address <> -1 and lul_address <> 0 then
  //建立ICMP請求
  lul_hIcmp = IcmpCreateFile()
  if isnull(lul_hIcmp) then
    messagebox("錯誤","ICMP請求建立失敗!")
  else
    lul_size = size.sizeof(lstr_reply)
    IcmpSendEcho(lul_hIcmp, lul_address, ls_send, Len(ls_send), 0, lstr_reply,lul_size,lul_timeout)

    //取得訊息
    ls_msg = wf_icmpmsg(lstr_reply.status)
    messagebox("Ping " + ls_pingIP + " :" , "Icmp code : " + String(lstr_reply.status) + "~r~n" + &
          "Response Message : " + ls_msg + "~r~n" + &
          "Time : " + String(lstr_reply.RoundTripTime) + " ms")

    //關閉ICMP請求
    IcmpCloseHandle(lul_hIcmp)

  end if
else
  messagebox("錯誤","IP位址不合法!")
end if
//關閉SOCKET
wsacleanup()


參考來源:
http://support.microsoft.com/kb/300197/zh-tw

2011年9月23日 星期五

sizeof in powerbuilder

PowerBuder 開發資料庫應用程式是相當的方便,

但是對於比較低階的資料處理,常常顯得捉襟見肘,

像是有使用到外部 API 時,需要傳遞變數,更多時候都需要一並傳遞該變數的長度,

此時,如果變數是很單純的,例如:char、integer、long 等,

而這些變數長度大致上都可由 help 來查得,

但是對於比較複雜的變數例如 structure 那就真的麻煩了,尤其是 structure 裡面又包 structure,

若是學過 C/C++ 的人都知道裡面有個 sizeof(var) 的函數,

但唯獨在 PowerBuilder 裡面就是沒有,

這也讓開發者真的挫折不少,每次都要自己算。

網路上我找了很久,終於找到可以實用的方法。

所以底下實做一個物件 nvo_sizeof (PB10) 來完成這個 PowerBuilder 所缺少的部份:

1.建立使用者物件 nvo_sizeof
2.將 AutoInstantiate 勾選
3.建立 Instance Variables
Private:
CONSTANT integer SIZE_BOOLEAN  = 1  // Boolean
CONSTANT integer SIZE_CHAR  = 1  // Char
CONSTANT integer SIZE_INT  = 2  // Signed integer
CONSTANT integer SIZE_UINT  = 2  // Unsigned integer
CONSTANT integer SIZE_LONG  = 4  // Signed Long
CONSTANT integer SIZE_ULONG  = 4  // Unsigned Long
CONSTANT integer SIZE_STRING  = 4  // Assume as string pointer
// Supported DataTypes
integer  INTEGER
uint  UINT
long  LONG
ulong   ULONG
char  CHAR
string  STRING
boolean  BOOLEAN


2011年9月22日 星期四

Javascript的Array中文排序

續上一篇

資料讀取到 Array 以後,偶爾使用者會需要排序這些列表資料,

當然 javascript array 有 sort 這個方法來排序,只是這個排序,

只比較合適於內容值為數值的排序,對於文字的排序就不怎麼靈光,不好用了。

因此 javascript 提供的另外一種排序方式 就比較合適文字/中文的排序方式:


//SORT data
function sortDW1(field,kind){
    dwset.sort(function(a,b){
        if (kind=="A"){
            return a[field].localeCompare(b[field])
        }else{
            return b[field].localeCompare(a[field])
        };
    });
};


關於 localeCompare 可以參考系列網頁資料:
http://www.w3school.com.cn/js/jsref_localeCompare.asp
http://www.southmaster.com/article/pub.php?page=red2.php&id=5408


2011年9月16日 星期五

將SQL讀出的資料暫存到JavaScript的Array以方便利用

有時候網頁需要讀取列表資料的時候,

通常我會把 SQL 執行語法交由另外一個 Coldfusion page 去執行,

然後取得回傳的內容並且儲存起來。

(這個方式當然是使用 ajax 方式,才不會動不動就要重新 RUN 一次版面)

為什麼要這麼做?

因為多數的時候,這個列表內容可能非常多(1~2千筆),

因此,這個列表就需要動用到分頁顯示,

然而,若是以前的作法是,每次點擊不同的分頁,就必須重新呼叫 SQL 查詢,

並只過濾出我需要顯示的範圍,而這種方式的處理,資料庫讀取就變得相當頻繁,

2011年9月10日 星期六

背景讀取 DataWindow 的方法

很多時候,DataWindow 的設計目的在於監視列表的資料,尤其是會自動定期更新的列表。

譬如說,我有一個紀錄狀態的 Table ,裡面紀錄的狀態一直在改變,

因此,我會需要一個 DataWindow 定時的去 Retrieve 這些紀錄資料,並將它顯示到畫面上,

讓管理人員可以一目了然。

當然,你會說這有什麼困難?用一個 DataWindow 加上 Timer 就可以解決啦!

但如果,這些資料多到螢幕容納不下時會發生什麼事?

如果你設定必需2秒更新一次又會怎麼樣?

事實是,當管理人員下拉(Scroll)該 DataWindow 以便檢視下方被遮住的資料時,

由於 Retrieve 的關係,所以畫面又跳回第一筆 Row 啦!!!

管理人員會落入不勝其擾的拉下又跳回、拉下又跳回的無間地獄了。

也許加大 Timer 間隔是個方法,不過對於資料的即時性就大打折扣了,

尤其是特別重要的資料更是如此,例如即時股票的變化.....

所以需要一點小技巧來達成這樣的運作模式,利用下面的程式碼技巧可以避開 DataWindow 的顯示畫面 reflash 動作:


DataStore lds_src
Long ll_row , ll_rcount
Integer li_col , li_ccount

//建立一個資料倉儲並使之與資料視窗的來源物件相同
lds_src = Create DataStore
lds_src.DataObject = dw_1.DataStore
lds_src.SetTransObject(SQLCA)

//由資料倉儲來讀取資料
lds_src.Retrieve()
ll_rcount = lds_src.RowCount()
li_ccount = integer(dw_1.Object.DataWindow.Column.Count)
for ll_row = 1 to ll_rcount
  for li_col = 1 to li_ccount
    //複製資料蓋掉畫面上的資料
    dw_1.Object.Data[ll_row , li_col] = lds_src.Object.Data[ll_row , li_col]
  next
next
//移除畫面上多餘的資料(如果有的話)
if ll_row <= dw_1.RowCount() then
  do while dw_1.RowCount() > lds_src.RowCount()
    dw_1.DeleteRow(dw_1.RowCount())
  loop
end if

Destroy lds_src

這段程式碼放在讀取的 function 裡面執行,

當然為了提高效率,你可以把淡藍色的部份放在該 window 的 open 事件裡面,
只是 lds_src 就必須定義在 Instance 變數區內以方便呼叫使用,
而粉色的部份就放到 window 的 close 事件去處理就好了。


2011年9月9日 星期五

Datawindow裡面資料Row的狀態變化

DataWindow 的 Row 在讀取、新增、修改後都有不同的狀態紀錄,

要取得該 Row 狀態只要使用下面這個指令就可以:
DWItemStatus ldwi_row

ldwi_row = dw_1.GetItemStatus(ll_row , li_column , Primary!)或
ldwi_row = dw_1.GetItemStatus(ll_row , "col_name", Primary!)

上面程式片碼中 li_column 如果為 0 ,代表取得整個 Row 狀態。

DWItemStatus 有四種狀態:
New! (新增 row)
NewModified! (新增 row 並修改內容)
DataModified! (資料內容被修改)
NotModified! (沒有任何修改)
但是要注意的是一個 Row 有兩種狀態 Flag (標籤)用來記錄其受影響的目標:
Row Flag (影響範圍屬於整列狀態,一列資料只有一個這樣的標籤紀錄)
Column Flag (影響範圍為單一欄位,每個欄位都有一個這樣的標籤紀錄)
而各狀態相關的範圍如下:

狀態受影響標籤
New!Rows
NewModified!Rows
DataModified!Rows 與 Columns
NotModified!Rows 與 Columns


2011年9月8日 星期四

Powerbuilder inet.PostURL用法

目前PB的開發我都是把它當成一個 Remote Client UI,

所以實際上它沒有連接資料庫,而完全是透過Coldfusion的服務頁面完成資料操作,

因此很多時候需要傳遞大量資料時,簡單的 inet.GetURL 函數就變得不好用了,

因此特地研究了inet.PostURL的用法,它的用法在Help上有兩個範例(我有修改過):

第一種是簡單的呼叫一個頁面,並取得頁面回傳的HTML內容:
(請注意 linet 和 n_internetresult 都是必須先建立的物件與類別,可參考這裡)

Blob lblb_args
String ls_headers , ls_url
Long ll_length
n_internetresult lir_html
inet linet

linet = Create inet
lir_html = CREATE n_internetresult
ls_url = "http://www.mydoamin.com/testpage.cfm"
lblb_args = blob("")
ll_length = Len(lblb_args)
ls_headers = "Content-Length: " + String(ll_length) + "~n~n"
linet.PostURL (ls_url, lblb_args, ls_headers, 8080, lir_html)

destroy lir_html
destroy linet

上面這種方式並沒有傳遞參數,其實效果如同:
linet.GetURL("http://www.mydoamin.com:8080/testpage.cfm",lir_html)

2011年9月7日 星期三

Powerbuilder如何建立網際網路服務

PowerBuilder 要建立網路服務,基本上需要兩個物件:
inet
internetResult
inet直接建立(instance)就可以了,例如:
inet linet
linet = Create inet
而接收inet傳回的資料就需要 internetResult 了,
只是這個物件不能直接建立,必須先建立Class (類別),
並且寫入處理回傳資料的程式碼(script),再建立(instance)成為物件才能使用:

2011年9月6日 星期二

jQuery.cookie 如何讀取 Coldfusion cfcookie 的設定值

我們都知道在Coldfusion下設定cookie只需要使用 cfcookie tag,
例如我們設定一個cookie名稱叫cid,它的值為123456,有效期為1天:

<cfcookie name="cid" value="123456" expires="1">

如果用coldfusion來讀取這個只需要下:

<cfset idvar = cookie.cid>



#cookie.cid#

就可以簡單讀取。
但是某些情況下,若是用到了javascript來讀取這個值,就好像不是這麼一回事了,