BCB 收銀機系統(POS)開發 |
|
bruce0211
版主 ![]() ![]() ![]() ![]() ![]() 發表:157 回覆:668 積分:279 註冊:2002-06-13 發送簡訊給我 |
由收銀機系統談 Windows 程式開發風格 - 解決方案 本解決方案由來請參考"由收銀機系統談 Windows 程式開發風格"一文 ■原理
1.宣告一 Global 變數 inkey 以便儲存 Keydown 抓到的按鍵值 , 另宣告一 Global 變數 LoopID 以便紀錄機能層識別 , 當 LoopID 為 0 時表交易READY 狀態 , 即尚未進入任一機能層中
2.Keydown 抓到按鍵值時不是立刻針對按鍵值做處理 , 而是先看 LoopID 這個旗標代表要到哪個副程序或哪個機能層去 (這跟一般 Windows 程式習慣不太相同)
3.當流程到達 LoopID 所指向的機能層後 , 再回頭將原先儲存在 inkey 的按鍵值取出處理
4.每一機能層都會將 LoopID 值改變 , 以便下一個 KeyDown 事件發生時知道該直接進入哪一機能層處理
5.以 LoopID 控制每一機能層進入時機 , 程式不必使用多個 Form 或 PageControl 或 Notebook , 來管理每一機能層按鍵所代表不同意義
6.這是筆者目前想到最適合以 Windows 模擬 DOS 線性程序結構的方法 , 如有其它高明方式歡迎討論 ■程式解說
步驟(1). 按 "F1" 鍵代表進入退貨交易程序 , 按其他鍵代表錯誤 步驟(2).進入退貨交易第一層程序 "請輸入退貨交易序號" 後 , 若輸入完整 8 位數字在按 Enter , 則進入下一層(步驟 3) , 若輸入數字長度不對則產生錯誤提示 ; 而按 "ESC" 鍵則有兩個意義 , 當已有輸入部分交易序號時 , 按 "ESC" 表清空交易序號重來 ; 當完全未輸入任何交易序號時 , 按 "ESC" 表放棄退貨交易程序 , 回到交易 READY 狀態 , 等待其它程序起始 .... 步驟(3).進入退貨交易第二層程序 "請輸入 1.部份退貨 2.全部退貨" 後 , 若輸入 "1" 或 "2" 再按 Enter , 則執行真正退貨執行程序 ; 若輸入數字不對則產生錯誤提示 ; 而按 "ESC" 鍵有兩個意義 , 當已有輸入"1" 或 "2" 時 , 按 "ESC" 表清空選擇重來 ; 當完全未輸入任何數字時 , 按 "ESC" 表放棄退貨交易第二層程序 , 回到退貨交易第一層程序 "請輸入退貨交易序號" , 程式流程又退回到前一層 ■測試程式
1.真實的收銀機程式由於不使用標準鍵盤 , 所以不使用 VK_XXX 虛擬鍵 , 本例只在說明模擬
2.先 NEW 一個 Application Form , 放入兩個元件 Panel1 & Panel2 , 位置在 Form 左下角的位置 , 當作收銀機系統畫面的提示攔及輸入欄 (完全不使用能被 Focused 的 Edit 元件) , 不用替畫面上任何元件設定屬性
3.完整程式碼如下 , 程式執行後按 "F1" 即可進入退貨程序模擬 ...
//--------------------------------------------------------------------------- #include "vcl.h" #pragma hdrstop #include "Unit1.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; int LoopID=0; WORD inkey; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- void __fastcall TForm1::FormActivate(TObject *Sender) { Panel1->Caption="掃描/輸入(Ready)"; } //--------------------------------------------------------------------------- void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift) { inkey=Key; if (LoopID==0) { switch (inkey) { case VK_ESCAPE : break; case VK_F1 : LoopID_111(0); break; default : ShowMessage("操作錯誤"); } inkey=0; } else { LoopManager(); } } //--------------------------------------------------------------------------- // InitFlag == 0 功能初始化 ; InitFlag <> 0 功能執行 //--------------------------------------------------------------------------- void __fastcall TForm1::LoopID_0(int InitFlag) { if (InitFlag==0) { LoopID=0; Panel1->Caption="掃描/輸入(Ready)"; Panel2->Caption=""; return; } } //--------------------------------------------------------------------------- void __fastcall TForm1::LoopManager() //用來分派旗標所指定之執行工作 { if (LoopID==0) return; switch (LoopID) { case 111 : LoopID_111(1); break; case 112 : LoopID_112(1); break; } } //--------------------------------------------------------------------------- // InitFlag == 0 功能初始化 ; InitFlag <> 0 功能執行 //--------------------------------------------------------------------------- void __fastcall TForm1::LoopID_111(int InitFlag) //請輸入退貨交易序號 { if (InitFlag==0) { LoopID=111; Panel1->Caption="請輸入退貨交易序號 (8 位數字)"; Panel2->Caption=""; return; } switch (inkey) { case VK_ESCAPE : if (Trim(Panel2->Caption)=="") { LoopID_0(0);//回前一層(初始層) } else { LoopID_111(0); //回本層 } break; case VK_RETURN : if (Trim(Panel2->Caption).Length()!=8) { ShowMessage("輸入錯誤"); LoopID_111(0); //回本層 } else { LoopID_112(0); //至下一層 } break; case 48 : case 49 : case 50 : case 51 : case 52 : case 53 : case 54 : case 55 : case 56 : case 57 : Panel2->Caption=Panel2->Caption (String)char(inkey); break; default : ShowMessage("操作錯誤"); } inkey=0; return; } //--------------------------------------------------------------------------- // InitFlag == 0 功能初始化 ; InitFlag <> 0 功能執行 //--------------------------------------------------------------------------- void __fastcall TForm1::LoopID_112(int InitFlag) //請輸入 1.部份退貨 2.全部退貨 { if (InitFlag==0) { LoopID=112; Panel1->Caption="請輸入 1.部份退貨 2.全部退貨"; Panel2->Caption=""; return; } switch (inkey) { case VK_ESCAPE : if (Trim(Panel2->Caption)=="") { LoopID_111(0); //回前一層 } else { LoopID_112(0); //回本層 } break; case VK_RETURN : if (StrToInt(Panel2->Caption)>2) { ShowMessage("輸入錯誤"); LoopID_112(0); //回本層 } else { //至下一層 LoopID_11_Launch(); // 退貨機能執行 LoopID_0(0);//回初始層 } break; case 49 : case 50 : Panel2->Caption=Panel2->Caption (String)char(inkey); break; default : ShowMessage("操作錯誤"); } inkey=0; return; } //--------------------------------------------------------------------------- void __fastcall TForm1::LoopID_11_Launch() // 退貨機能執行 { //真正退貨執行程序 //...... //...... ShowMessage("退貨執行完畢"); } //---------------------------------------------------------------------------by BCB 源碼任務 (http://home.kimo.com.tw/bruce0829/) 發表人 - bruce0211 於 2002/08/25 20:42:58 |
bruce0211
版主 ![]() ![]() ![]() ![]() ![]() 發表:157 回覆:668 積分:279 註冊:2002-06-13 發送簡訊給我 |
由收銀機系統談 Windows 程式開發風格 - 解決方案之二 本解決方案由來請參考"由收銀機系統談 Windows 程式開發風格"一文 ■原理
1.本方案採用迴圈攔截 KeyDown 事件的方式 , 每個機能層都用迴圈控制 , 比較類似 DOS 的做法 , 但訊息攔截不是控制的很好 , 在進入迴圈後無法關閉程式 (或是其他還沒發現到的限制...)
2.每個迴圈的出入口都要初始化畫面上的指示訊息 , 故撰寫指示訊息的程式碼較多 , 且有重複
3.目前並未實際拿來撰寫真正的程式 , 不曉得訊息攔截上是否會有其他問題(如進入無窮迴圈 , GUI 畫面未 Refresh , 或程式當在迴圈理不理你) , 在 Windows 中使用 do ... while 直覺不是一個理想的程式架構 , 也不曉得會不會降低系統 performace ??? ■程式解說
步驟(1). 按 "F1" 鍵代表進入退貨交易程序 , 按其他鍵代表錯誤 步驟(2).進入退貨交易第一層程序 "請輸入退貨交易序號" 後 , 若輸入完整 8 位數字在按 Enter , 則進入下一層(步驟 3) , 若輸入數字長度不對則產生錯誤提示 ; 而按 "ESC" 鍵則有兩個意義 , 當已有輸入部分交易序號時 , 按 "ESC" 表清空交易序號重來 ; 當完全未輸入任何交易序號時 , 按 "ESC" 表放棄退貨交易程序 , 回到交易 READY 狀態 , 等待其它程序起始 .... 步驟(3).進入退貨交易第二層程序 "請輸入 1.部份退貨 2.全部退貨" 後 , 若輸入 "1" 或 "2" 再按 Enter , 則執行真正退貨執行程序 ; 若輸入數字不對則產生錯誤提示 ; 而按 "ESC" 鍵有兩個意義 , 當已有輸入"1" 或 "2" 時 , 按 "ESC" 表清空選擇重來 ; 當完全未輸入任何數字時 , 按 "ESC" 表放棄退貨交易第二層程序 , 回到退貨交易第一層程序 "請輸入退貨交易序號" , 程式流程又退回到前一層 ■測試程式
1.真實的收銀機程式由於不使用標準鍵盤 , 所以不使用 VK_XXX 虛擬鍵 , 本例只在說明模擬
2.先 NEW 一個 Application Form , 放入兩個元件 Panel1 & Panel2 , 位置在 Form 左下角的位置 , 當作收銀機系統畫面的提示攔及輸入欄 (完全不使用能被 Focused 的 Edit 元件) , 不用替畫面上任何元件設定屬性
3.完整程式碼如下 , 程式執行後按 "F1" 即可進入退貨程序模擬 ...
//--------------------------------------------------------------------------- #include "vcl.h" #pragma hdrstop #include "Unit1.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; MSG msg; bool end_flag; WORD inkey; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- void __fastcall TForm1::FormActivate(TObject *Sender) { Panel1->Caption="掃描/輸入 (Ready)"; Panel2->Caption=""; } //--------------------------------------------------------------------------- void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift) { inkey=Key; end_flag=false; switch (inkey) { case VK_ESCAPE : break; case VK_F1 : LoopID_111(); break; default : ShowMessage("操作錯誤1"); } } //--------------------------------------------------------------------------- //退貨機能選項第一層 : 請輸入退貨交易序號 (按 ESC 回前一層) //--------------------------------------------------------------------------- void __fastcall TForm1::LoopID_111() { Panel1->Caption="請輸入退貨交易序號 (8 位數字)"; Panel2->Caption=""; while (true) { //攔截 Keydown 事件 , 其它事件則放行通過 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message != WM_KEYDOWN) { TranslateMessage(&msg); DispatchMessage(&msg); continue; } else { inkey=msg.wParam; switch (inkey) { case VK_ESCAPE : if (Trim(Panel2->Caption)=="") { //回前一層(初始層) Panel1->Caption="掃描/輸入 (Ready)"; return; } else { //回本層 Panel1->Caption="請輸入退貨交易序號 (8 位數字)"; Panel2->Caption=""; } break; case VK_RETURN : if (Trim(Panel2->Caption).Length()!=8) { ShowMessage("輸入錯誤2"); //回本層 Panel1->Caption="請輸入退貨交易序號 (8 位數字)"; Panel2->Caption=""; } else { LoopID_112(); //至下一層 if (end_flag) return; } break; case 48 : case 49 : case 50 : case 51 : case 52 : case 53 : case 54 : case 55 : case 56 : case 57 : Panel2->Caption=Panel2->Caption (String)char(inkey); break; default : ShowMessage("操作錯誤3"); } } } } } //--------------------------------------------------------------------------- // 退貨機能選項第二層 : 請輸入 1.部份退貨 2.全部退貨 (按 ESC 回前一層) //--------------------------------------------------------------------------- void __fastcall TForm1::LoopID_112() { Panel1->Caption="請輸入 1.部份退貨 2.全部退貨"; Panel2->Caption=""; while (true) { //攔截 Keydown 事件 , 其它事件則放行通過 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message != WM_KEYDOWN) { TranslateMessage(&msg); DispatchMessage(&msg); continue; } else { inkey=msg.wParam; switch (inkey) { case VK_ESCAPE : if (Trim(Panel2->Caption)=="") { //回前一層 Panel1->Caption="請輸入退貨交易序號 (8 位數字)"; Panel2->Caption=""; return; } else { //回本層 Panel1->Caption="請輸入 1.部份退貨 2.全部退貨"; Panel2->Caption=""; } break; case VK_RETURN : if (StrToInt(Panel2->Caption)>2) { ShowMessage("輸入錯誤4"); //回本層 Panel1->Caption="請輸入 1.部份退貨 2.全部退貨"; Panel2->Caption=""; } else { //至下一層 LoopID_11_Launch(); // 退貨機能執行 //回初始層 if (end_flag) return; } break; case 48 : case 49 : case 50 : case 51 : case 52 : case 53 : case 54 : case 55 : case 56 : case 57 : Panel2->Caption=Panel2->Caption (String)char(inkey); break; default : ShowMessage("操作錯誤5"); } } } } } //--------------------------------------------------------------------------- // 實際退貨機能執行 //--------------------------------------------------------------------------- void __fastcall TForm1::LoopID_11_Launch() { //真正退貨執行程序 //...... //...... ShowMessage("退貨執行完畢"); Panel1->Caption="掃描/輸入 (Ready)"; Panel2->Caption=""; end_flag=true; } //---------------------------------------------------------------------------by BCB 源碼任務 (http://home.kimo.com.tw/bruce0829/) 發表人 - bruce0211 於 2002/08/27 13:12:09 |
jcjroc
高階會員 ![]() ![]() ![]() ![]() 發表:21 回覆:279 積分:115 註冊:2002-09-18 發送簡訊給我 |
|
thomas0728
中階會員 ![]() ![]() ![]() 發表:112 回覆:260 積分:89 註冊:2002-03-12 發送簡訊給我 |
|
bruce0211
版主 ![]() ![]() ![]() ![]() ![]() 發表:157 回覆:668 積分:279 註冊:2002-06-13 發送簡訊給我 |
|
koncon
一般會員 ![]() ![]() 發表:0 回覆:7 積分:1 註冊:2003-01-06 發送簡訊給我 |
|
bruce0211
版主 ![]() ![]() ![]() ![]() ![]() 發表:157 回覆:668 積分:279 註冊:2002-06-13 發送簡訊給我 |
|
aamihcom
一般會員 ![]() ![]() 發表:57 回覆:40 積分:19 註冊:2003-10-02 發送簡訊給我 |
引言: 再補充描述一下DOS vs. Windows 風格的不同: □ Dos 程式以程序為主,事件(鍵盤事件)為輔,即進入某一程序後就一直等在那邊隨時監測你的鍵盤輸入,沒有符合這個程序離開的條件絕不會離開此程序 □ Windows 程式以事件(鍵盤事件)為主,程序為輔 , 按了鍵(OnKeyDown)後,再看看(依旗標控制)要進到哪個程序(而且不能一直卡在那個程序中 , 否則將接不到下一次鍵盤觸發事件) 程序的完成是分週期的 (OnKeyDown -> 旗標決定 -> 程序甲 -> 執行完成否 ? 完成則改變旗標值(下次 KeyDown 就不會再進來程序甲),未完成則 return 繼續等待下一個 KeyDown ; 換句話說 : 副程序在執行期間 , 程式流程仍要返回 KeyDown 事件以等待下一個 KeyDown 事件 , 且要確保 下一個 KeyDown 事件發生後流程會回到原先未完成的程序 , 而不會被別的程序搶走 □ POS 系統畫面不能有可以被 Focus 的元件 , 所以也不能用 Edit 來等待使用者按鍵的輸入 , 況且 POS 鍵盤是所謂的"可程式化鍵盤" , 按下 "1" 這個數字並不是標準 "1" 的 ASCII 碼 , 所以根本不能用一般編輯元件來接收鍵盤碼 , 而要直接抓 ScanCode 來處理 POS 架構比較適合用 DOS 的模式喔 ... 版主大人您好~ 看了這個討論主題 ,我有一些心得 1.一個pos系統(如版本所說的專業的pos收銀系統),其開發上,的確是不容易, 但這是指真正有寫過的人才會這麼覺得,尤其是windows版,為何會說真正 有寫過的人才會這麼覺得呢.以我來說,我個人也是學了一年多的pos系統 程式維護超市及大賣場的,先講我第一次接觸到的感覺 ,一個畫面 ,功能 也才不過十幾二十個,主要資料表格也不多,很簡單啊(這是我去年差不多 七月份的感覺),起初是維護公司dos版的pos,第一家上線,客戶總共有接近 六十台pos在同一天上線,我改了一些小小的部份 ,結果沒出什麼問題 ,那 時還是覺得很順利 ,後來維護windows版的pos ,起初以為應該會比dos的 簡單,因為windows的開發工具很方便,不像dos那樣,連個字串加淢都要處 理,不過事實上並非如此,很多問題反而更難去處理,像版主所說鍵盤控制 的部作的確不好處理,pos鍵盤不是一般的鍵盤裡面所燒的值各家都不一 樣,光要靠一個edit來做輸入控制那是不可能的,因為你要考慮的不只是 單一個pos鍵盘,而是要都能同,一支pos收銀程式,不可能一種硬體一個板 本必須要能自由去設定以搭配各種不同的硬體,,windows本身對鍵盤就一 些訊息存在,需要去把它防掉,這就是一個沒寫過的人不會了解的固難處, 另外流程及狀態控制,一個好的pos系統在收銀員的操作需加入一些指示的 訊息,如現在要輸入什麼等,在操作錯誤時又要能回應使用者,為何操作錯誤 回復上一動作,或是重來,雖然只有十幾種功能,但在收銀員的操作下出現的 狀況,差不多會有一百個以上,這些錯誤要如何去控制及做訊息回應,這又是 一項不好處理的地方,再來硬體週邊的部份,各種不同的硬體你都要能支援, 而且配來配去,各硬體又不一定都一樣的控制,比如說錢箱好了,有的是從發 票機接出來的,有的則是由com port接出來的,這看起來好像沒什麼,但在同 一支程式加入這些控制又是讓程式更難控制,再來資料索引的問題 ,收銀前 台必須能夠及時收到訊息自動變價(各式各樣的變價),程式在不離開的前提 及要資料都正確,在更新上也不好處理,在來是促銷部份,促銷在現行的windows 所開發的系統下,促銷是一項很重要的功能,所以客戶一直會要求,要怎樣促 銷,在主流程己經都完成了,加入一項新的促銷,是很難做的,怎麼說呢,比如 你加了一項a群組配b群組折扣,這時又要考慮,刪除,查價,折讓,折扣,加價, 退貨時的功能,幾乎大部份的功能都要動到了,這個好處理嗎?,真的不好處 理,pos程式是很小但很細,不好改那也是為什麼,一直都改不好,bug永遠改 不完的原因,其它還有很多...也都是層層想扣的. 自從維護pos系統的這二年來,有一個感觸,大家只是認為好像很容易,想起 來容易,但做起來,真的不容易,個人是認為一個專業的pos系統的開發倒不 一定比ERP來來簡單,ERP重資料庫的概念,系統很大沒錯,但細節絶對不會比 pos來的細,又現今pos系統己經有點成熟,價格上也變的很低,但需要維護嗎 幾個人呢這點實在是一般的小公司怎麼算都不合理的地方. ps: 在下是認為上面那個keyboard的問題,BCB裡可以將keyboard及滑屬的訊息全部 攔下來,然後重新自行定義,這樣會比其它方法好做. Application->NULL 然後再指定SCANCODE給Application->OnMessage 發表人 - bruce0211 於 2002/08/01 17:02:31 |
tonyhome
一般會員 ![]() ![]() 發表:3 回覆:13 積分:3 註冊:2002-07-12 發送簡訊給我 |
「程式設計是基本功,業別的 Know how 才是關鍵!」 如果學會程式設計就能大小通吃....就沒有人哭喊著去賣香雞排。 如果學會程式設計就覺得其他沒什麼大不了....程式一改再改,為什麼不能結案? 「程式」對業者而言只是工具(Tools)而不是工藝(Technology),因為他不懂程式設計,如果你不能懂得業者為什麼要使用 POS?而 POS 能夠幫業者做什麼?那對業者而言也只能說 POS 只是換一台「較高級的收銀機」。 同業加油吧!不僅是程式設計,還要加強相關業態的 Know how 才能讓你的程式更專業、更有價值。
|
ANDY8C
資深會員 ![]() ![]() ![]() ![]() ![]() 發表:114 回覆:582 積分:299 註冊:2006-10-29 發送簡訊給我 |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |