:解決觸摸屏工控現(xiàn)場(chǎng)輸入難題)
做LabVIEW上位機(jī)的朋友十有八九都有過(guò)這種經(jīng)歷設(shè)備已經(jīng)拉到客戶現(xiàn)場(chǎng)操作面板是觸摸屏一體機(jī)界面上偏偏還有配方號(hào)、批次號(hào)、工號(hào)、IP地址這些必須手動(dòng)輸入的字段。讓客戶接USB鍵盤(pán)不現(xiàn)實(shí)很多產(chǎn)線工位根本沒(méi)地方放用系統(tǒng)自帶軟鍵盤(pán)界面風(fēng)格和自家程序完全不搭中文輸入還要去戳屏幕體驗(yàn)一言難盡。所以我花時(shí)間整理過(guò)一套LabVIEW中英文虛擬鍵盤(pán)源程序?qū)iT(mén)解決這類場(chǎng)景的輸入問(wèn)題。這篇把方案選型、核心實(shí)現(xiàn)、實(shí)操步驟和踩坑記錄全部攤開(kāi)講一遍做上位機(jī)、做HMI的朋友可以直接拿去改。1. 為什么LabVIEW項(xiàng)目里非要有這塊鍵盤(pán)不可1.1 觸摸屏工控現(xiàn)場(chǎng)的輸入難題寫(xiě)辦公軟件的團(tuán)隊(duì)可能很難理解產(chǎn)線工程師為什么會(huì)對(duì)屏幕上多一個(gè)鍵盤(pán)這種事較真。但真正到現(xiàn)場(chǎng)調(diào)試過(guò)就知道很多自動(dòng)化設(shè)備合同里明確寫(xiě)著人機(jī)界面采用觸摸屏默認(rèn)就是不配實(shí)體鍵盤(pán)鼠標(biāo)的。設(shè)備操作員每天站在屏前手上戴著手套環(huán)境里有粉塵、油污實(shí)體鍵盤(pán)既占空間又難清潔。可MES相關(guān)需求又把手動(dòng)輸入的字段堆到了界面上操作員編號(hào)、產(chǎn)品型號(hào)、生產(chǎn)批號(hào)、當(dāng)前工單、設(shè)備維護(hù)密碼……這些字段用下拉框很難覆蓋必須開(kāi)放自由輸入。這種情況下放任不管操作員有幾個(gè)應(yīng)對(duì)辦法。第一跟現(xiàn)場(chǎng)工程師抱怨然后被塞一個(gè)積灰的舊鍵盤(pán)用膠布貼在屏邊上第二靠系統(tǒng)自帶軟鍵盤(pán)湊合但自帶軟鍵盤(pán)按鈕很小戴手套戳不準(zhǔn)在英文系統(tǒng)上還輸不了中文第三干脆把設(shè)備晾在那等IT來(lái)處理。不管哪一種本質(zhì)上都是在消耗現(xiàn)場(chǎng)信任。虛擬鍵盤(pán)這個(gè)看似不起眼的小功能恰恰是讓上位機(jī)交付后能不能直接開(kāi)機(jī)干活的關(guān)鍵一環(huán)。1.2 現(xiàn)成軟鍵盤(pán)方案的三個(gè)坑有的朋友會(huì)說(shuō)Windows不是自帶屏幕鍵盤(pán)嗎直接調(diào)不就行了這個(gè)方案聽(tīng)起來(lái)節(jié)省成本實(shí)際用起來(lái)有三個(gè)繞不開(kāi)的坑。第一個(gè)是界面風(fēng)格問(wèn)題系統(tǒng)軟鍵盤(pán)的外殼、配色、字體跟LabVIEW做的界面完全是兩個(gè)世界的產(chǎn)物客戶看第一眼就會(huì)覺(jué)得這不是一套系統(tǒng)專業(yè)度大打折扣。第二個(gè)坑是可控性差。系統(tǒng)軟鍵盤(pán)的布局、按鍵大小、數(shù)字鍵盤(pán)是否常駐、中文輸入法如何切換通通不受程序控制。你真想限制操作員只能輸入數(shù)字或者強(qiáng)制使用特定數(shù)值范圍系統(tǒng)軟鍵盤(pán)完全配合不了。第三個(gè)坑更隱蔽——許多工廠的工控機(jī)是特定定制鏡像或精簡(jiǎn)版Windows系統(tǒng)軟鍵盤(pán)可能被策略限制部署到現(xiàn)場(chǎng)才發(fā)現(xiàn)在別的機(jī)器上能彈出來(lái)的鍵盤(pán)這臺(tái)機(jī)器上怎么都打不開(kāi)或者中文輸入法缺失求助IT又是一輪漫長(zhǎng)的扯皮。與其賭現(xiàn)場(chǎng)環(huán)境不如把鍵盤(pán)邏輯完全放進(jìn)LabVIEW程序里。1.3 一套自繪虛擬鍵盤(pán)源程序的價(jià)值點(diǎn)我自己最初決定寫(xiě)這套中英文虛擬鍵盤(pán)源程序目標(biāo)其實(shí)很樸素。第一在任何現(xiàn)場(chǎng)都能穩(wěn)定彈出來(lái)不受系統(tǒng)版本、精簡(jiǎn)鏡像影響。第二界面風(fēng)格跟主程序完全統(tǒng)一尺寸、配色、字體都能調(diào)。第三中英文輸入都能搞定中文不能只靠調(diào)用外部輸入法。第四能和主程序業(yè)務(wù)邏輯打通比如輸完數(shù)字按回車就觸發(fā)查詢這類事件在虛擬鍵盤(pán)內(nèi)部閉環(huán)處理掉。后來(lái)在實(shí)際項(xiàng)目里又驗(yàn)證出兩個(gè)額外收益。一是復(fù)用性鍵盤(pán)做成一個(gè)獨(dú)立子VI后多個(gè)項(xiàng)目之間互相拷貝只改字典和布局就能用。二是可維護(hù)性很多需求方會(huì)在驗(yàn)收前幾天突然提能不能加個(gè)中文輸入如果鍵盤(pán)邏輯分散在主程序各處這種改動(dòng)會(huì)非常痛苦而集中在一個(gè)源程序模塊里修改路徑很短。這也是我堅(jiān)持自繪而不是臨時(shí)拼湊的核心理由。2. 方案選型自繪虛擬鍵盤(pán)的開(kāi)發(fā)路線拆解2.1 三條主流實(shí)現(xiàn)路線對(duì)比動(dòng)手前我其實(shí)比較過(guò)三條路線這里直接列出對(duì)比結(jié)論。實(shí)現(xiàn)路線實(shí)現(xiàn)成本中英文支持定制化能力部署與兼容性維護(hù)成本調(diào)用系統(tǒng)屏幕鍵盤(pán)極低依賴系統(tǒng)輸入法幾乎不可定制精簡(jiǎn)鏡像可能缺組件無(wú)源程序可控第三方ActiveX軟鍵盤(pán)控件中等視控件而定有限需要注冊(cè)組件升級(jí)易出問(wèn)題依賴廠商維護(hù)純LabVIEW原生控件自繪較高完全自主實(shí)現(xiàn)完全可控打包簡(jiǎn)單運(yùn)行時(shí)隨程序下發(fā)源碼在手上隨時(shí)改表格里第三條路線前期投入最大但它換來(lái)的是整個(gè)鍵盤(pán)模塊完全透明。現(xiàn)場(chǎng)出任何問(wèn)題打開(kāi)LabVIEW就能改、能查、能重現(xiàn)而不是面對(duì)一個(gè)黑盒控件干瞪眼。工控項(xiàng)目里最怕的就是軟件在客戶那里出問(wèn)題而你無(wú)法定位原生自繪恰恰把這種風(fēng)險(xiǎn)壓到最低。2.2 為什么原生控件自繪更符合工控場(chǎng)景工控上位機(jī)有一個(gè)區(qū)別于普通Windows應(yīng)用的特點(diǎn)運(yùn)行環(huán)境不可控程度高。同樣是Win10有的現(xiàn)場(chǎng)是普通辦公版有的是各類定制精簡(jiǎn)鏡像第三方ActiveX控件在打包后往現(xiàn)場(chǎng)一裝經(jīng)常出現(xiàn)注冊(cè)表項(xiàng)缺失、運(yùn)行庫(kù)沒(méi)裝、控件未授權(quán)等一串連鎖反應(yīng)。系統(tǒng)軟鍵盤(pán)更不用說(shuō)連在不在都取決于鏡像策略。原生控件則沒(méi)這個(gè)問(wèn)題——它只是普通LabVIEW前面板控件隨VI一起打進(jìn)安裝包不需要額外注冊(cè)任何組件。另外LabVIEW原生控件的風(fēng)格可以和主程序用同一套主題色、字體大小和按鈕形狀。好的HMI界面之所以讓操作員覺(jué)得高級(jí)一個(gè)重要原因是視覺(jué)語(yǔ)言統(tǒng)一。界面上一堆混合風(fēng)格的控件跟Word里混了三種字體一個(gè)道理。原生自繪能直接把這層統(tǒng)一感做出來(lái)。2.3 整體架構(gòu)與數(shù)據(jù)流設(shè)計(jì)這套虛擬鍵盤(pán)源程序的架構(gòu)我拆成三層。界面層是前面板上一堆布爾按鈕負(fù)責(zé)呈現(xiàn)和接收點(diǎn)擊這一層只管哪些鍵被按下映射層是按鍵識(shí)別與狀態(tài)管理邏輯負(fù)責(zé)把按鈕點(diǎn)擊翻譯成字符或者功能命令這一層是鍵盤(pán)的靈魂所在中英文切換、Shift狀態(tài)、拼音組合都在這里完成交互層負(fù)責(zé)把結(jié)果送到目標(biāo)輸入控件并處理回車通知。數(shù)據(jù)流上一次完整的按鍵操作是這樣的操作員在觸摸屏按下字母鍵該按鈕的值變更事件觸發(fā)事件結(jié)構(gòu)收到控件引用和當(dāng)前值映射層根據(jù)標(biāo)簽文本判斷按的是哪一個(gè)鍵結(jié)合當(dāng)前處于英文、拼音還是數(shù)字模式得出本次要輸出的內(nèi)容如果內(nèi)容是需要上屏的字符就直接寫(xiě)入當(dāng)前目標(biāo)輸入控件然后把焦點(diǎn)切回目標(biāo)控件如果是回車、退格這類功能鍵則執(zhí)行相應(yīng)的通知或刪除操作。整體數(shù)據(jù)流單向、清晰出問(wèn)題時(shí)順著鏈路查就行不會(huì)出現(xiàn)這個(gè)字符到底是誰(shuí)寫(xiě)進(jìn)去的這種懸案。3. 核心實(shí)現(xiàn)細(xì)節(jié)按鍵識(shí)別、中英文切換與焦點(diǎn)管理3.1 用控件標(biāo)簽識(shí)別按鍵避免100個(gè)事件分支很多第一次寫(xiě)虛擬鍵盤(pán)的朋友第一反應(yīng)是在事件結(jié)構(gòu)里給每一個(gè)按鈕單獨(dú)建一個(gè)分支100個(gè)按鍵就是100個(gè)分支。這種做法不是說(shuō)不行但維護(hù)起來(lái)極其痛苦想改一個(gè)字的顯示標(biāo)簽要?jiǎng)右欢训胤?。我的做法是只建三五個(gè)分支用控件標(biāo)簽文本識(shí)別這一招把按鍵區(qū)分開(kāi)。具體來(lái)說(shuō)在前面板上每個(gè)按鍵控件的標(biāo)簽直接設(shè)為它代表的字符比如字母A鍵標(biāo)簽就叫AShift鍵標(biāo)簽就叫Shift。在事件結(jié)構(gòu)中把這100個(gè)按鍵的值變更事件全部綁定到同一個(gè)事件分支事件數(shù)據(jù)里的控件引用就是被點(diǎn)擊的那個(gè)鍵。通過(guò)一個(gè)屬性節(jié)點(diǎn)讀取這個(gè)引用的標(biāo)簽文本再用條件結(jié)構(gòu)Case根據(jù)標(biāo)簽文本決定分支邏輯。這里把處理邏輯用文字概括一下事件分支(多個(gè)按鍵值變更): 控件引用 - 屬性節(jié)點(diǎn)讀出標(biāo)簽文本 switch(標(biāo)簽文本): case A..Z: 輸出字母 case 0..9: 輸出數(shù)字 case Shift: 切換Shift狀態(tài) case Backspace: 刪除最后一個(gè)字符 case Enter: 觸發(fā)完成通知 default: 其他字符鍵同樣輸出用這種結(jié)構(gòu)新增一個(gè)字符鍵只需要在前面板放一個(gè)按鈕、把標(biāo)簽改成對(duì)應(yīng)字符程序框圖一行不用動(dòng)擴(kuò)展性比逐鍵建分支好太多。關(guān)鍵是LabVIEW的控件引用機(jī)制讓這件事變得非常簡(jiǎn)單事件結(jié)構(gòu)中拿到的不是字符串而是控件對(duì)象的引用讀寫(xiě)它的標(biāo)簽、值、焦點(diǎn)等屬性都暢通無(wú)阻。3.2 中文拼音輸入與候選字機(jī)制的實(shí)現(xiàn)這套源程序里中文輸入采用拼音加候選字方案不依賴操作系統(tǒng)任何輸入法程序?;舅悸肥钱?dāng)鍵盤(pán)切換到中文模式后字母鍵不再直接上屏而是先進(jìn)入一個(gè)拼音緩沖區(qū)緩沖區(qū)里拼出一個(gè)完整拼音后程序到內(nèi)置字典里檢索出對(duì)應(yīng)的候選漢字顯示在候選字列表控件里操作員點(diǎn)擊候選字這個(gè)字才寫(xiě)入目標(biāo)輸入控件。實(shí)現(xiàn)上字典通常用一個(gè)二維字符串?dāng)?shù)組常量來(lái)表達(dá)第一列是拼音第二列是逗號(hào)分隔的漢字候選比如zhong,中,鐘,種,眾,重這樣的結(jié)構(gòu)。檢索時(shí)把拼音緩沖區(qū)內(nèi)容和字典第一列做字符串匹配命中的一行就拆成候選列表顯示。這個(gè)字典可以按項(xiàng)目需要增補(bǔ)比如某工廠經(jīng)常要輸入產(chǎn)品批次用字、人員姓名的生僻字直接往數(shù)組里加行就行不用改任何程序結(jié)構(gòu)。這里有個(gè)細(xì)節(jié)值得多說(shuō)一句在設(shè)計(jì)拼音檢索邏輯時(shí)最好支持完整拼音匹配而不是前綴匹配否則操作員輸入帶聲調(diào)的數(shù)字或者拼音還沒(méi)輸完就急著看候選很容易造成誤判。我在實(shí)際項(xiàng)目里用的策略是完整拼音才匹配加候選列表動(dòng)態(tài)刷新屏幕下方還有一個(gè)拼音緩沖區(qū)顯示框讓操作員隨時(shí)知道當(dāng)前拼到了哪一步。中文輸入過(guò)程的體驗(yàn)順不順很大程度上看這個(gè)反饋?zhàn)龅玫讲坏轿弧?.3 焦點(diǎn)控制虛擬鍵盤(pán)不搶焦點(diǎn)的完整做法虛擬鍵盤(pán)最容易翻車的地方是真機(jī)一碰就暴露的焦點(diǎn)問(wèn)題。觸摸屏操作沒(méi)有鼠標(biāo)光標(biāo)只有一個(gè)當(dāng)前活動(dòng)控件的概念。正常情況下操作員先點(diǎn)擊文本輸入框文本框獲得鍵盤(pán)焦點(diǎn)此時(shí)實(shí)體鍵盤(pán)或掃碼槍輸入的字符會(huì)落在文本框里。問(wèn)題來(lái)了一旦操作員接著點(diǎn)擊屏幕上的虛擬鍵盤(pán)按鍵這個(gè)鍵盤(pán)按鈕本身變成了活動(dòng)控件鍵盤(pán)焦點(diǎn)從文本框跑到了虛擬鍵盤(pán)按鈕上之后再點(diǎn)其他虛擬鍵輸出的字符就不知道跑哪去了。解決辦法的核心是焦點(diǎn)強(qiáng)制回收。虛擬鍵盤(pán)每次處理完一個(gè)按鍵都要立刻把鍵盤(pán)焦點(diǎn)寫(xiě)回當(dāng)前目標(biāo)輸入控件。在程序框圖上就是保存一份目標(biāo)控件的引用處理完字符后通過(guò)屬性節(jié)點(diǎn)把該控件的Key Focus有的版本里叫鍵盤(pán)焦點(diǎn)設(shè)為T(mén)rue。這一步必須放在虛擬鍵盤(pán)按鈕事件分支的同一次執(zhí)行中完成不能留著下個(gè)循環(huán)再做否則操作員會(huì)肉眼可見(jiàn)地看到焦點(diǎn)閃了一下又跳走。另外還有一個(gè)經(jīng)驗(yàn)鍵盤(pán)子VI最好作為普通子VI嵌入到主程序前面板中不要讓鍵盤(pán)彈成獨(dú)立頂層窗口。原因很簡(jiǎn)單跨VI操作別的程序前面板控件的焦點(diǎn)在LabVIEW里有時(shí)會(huì)受到限制或需要額外引用處理而嵌入到同一前面板后所有控件引用都是同一VI下的正常關(guān)系焦點(diǎn)回收邏輯最穩(wěn)。3.4 回車觸發(fā)業(yè)務(wù)邏輯的實(shí)現(xiàn)方式工控界面上的輸入框經(jīng)常講究輸完按回車立即執(zhí)行下一步。比如掃碼槍掃完條碼自動(dòng)回車程序馬上查詢數(shù)據(jù)庫(kù)操作員在鍵盤(pán)上輸完工單號(hào)按回車界面直接跳轉(zhuǎn)。很多人直接掛目標(biāo)控件的值變更事件結(jié)果發(fā)現(xiàn)每敲一個(gè)字符界面就反應(yīng)一次輸入還沒(méi)結(jié)束邏輯已經(jīng)跑了好幾遍操作體驗(yàn)一塌糊涂。正確做法是這樣目標(biāo)輸入控件在生產(chǎn)運(yùn)行期間只做純文本接收不觸發(fā)業(yè)務(wù)邏輯虛擬鍵盤(pán)上的回車鍵被按下時(shí)不是簡(jiǎn)單把回車字符追加進(jìn)文本框而是通過(guò)用戶事件或者消息隊(duì)列向主程序發(fā)送輸入完成信號(hào)主程序收到信號(hào)后才從目標(biāo)控件讀取當(dāng)前值執(zhí)行查詢、校驗(yàn)或跳轉(zhuǎn)。這樣設(shè)計(jì)還有一個(gè)額外好處虛擬鍵盤(pán)、實(shí)體鍵盤(pán)、掃碼槍三種輸入路徑可用同一把回車邏輯。掃碼槍以USB鍵盤(pán)模式輸出時(shí)掃完碼自動(dòng)補(bǔ)一個(gè)回車這個(gè)回車天然觸發(fā)輸入完成信號(hào)數(shù)據(jù)和鍵盤(pán)輸入統(tǒng)一走同一條處理鏈。這套一致性在MES掃碼綁定場(chǎng)景下特別值錢(qián)鍵盤(pán)和掃碼槍兩條輸入鏈路不用寫(xiě)兩套業(yè)務(wù)代碼。4. 實(shí)操過(guò)程完整搭出一套中英文虛擬鍵盤(pán)4.1 前面板布局與按鈕機(jī)械動(dòng)作選擇動(dòng)手搭之前先規(guī)劃好按鍵布局。標(biāo)準(zhǔn)QWERTY三行字母區(qū)、一個(gè)數(shù)字符號(hào)區(qū)、一排功能鍵區(qū)這是大多數(shù)操作員最熟悉的排布不要為了省空間搞另類排列。觸摸屏對(duì)按鍵尺寸有硬性要求我習(xí)慣把字母鍵做成40像素以上對(duì)應(yīng)約10到12毫米物理尺寸間距至少2像素戴手套操作也不容易誤觸功能鍵可以用稍小尺寸但至少也要35像素。整體放在屏幕底部三分之一的區(qū)間避免遮擋主界面的數(shù)據(jù)展示區(qū)。每個(gè)按鍵控件的機(jī)械動(dòng)作建議統(tǒng)一設(shè)為L(zhǎng)atch類型按下觸發(fā)、讀取后自動(dòng)復(fù)位。這類鎖定式機(jī)械動(dòng)作的程序邏輯很簡(jiǎn)單每次按下產(chǎn)生一次True事件結(jié)構(gòu)讀取并處理完后按鈕自動(dòng)復(fù)位天然避免了長(zhǎng)按重復(fù)觸發(fā)。如果用瞬時(shí)動(dòng)作操作員手指一直按著屏幕值會(huì)持續(xù)保持True不小心就觸發(fā)N次邏輯調(diào)起來(lái)很惱火。視覺(jué)上功能鍵用不同底色區(qū)分會(huì)有幫助目前我的習(xí)慣是Shift用深灰、Backspace用紅、Enter用綠、中英文切換用藍(lán)其余字母數(shù)字鍵用淺灰布局邏輯一眼掃過(guò)去就很清楚。4.2 程序框圖的關(guān)鍵邏輯與狀態(tài)管理程序框圖主體是一個(gè)While循環(huán)包著事件結(jié)構(gòu)事件結(jié)構(gòu)里維護(hù)幾個(gè)核心狀態(tài)當(dāng)前模式英文、拼音、數(shù)字、Shift狀態(tài)、Caps狀態(tài)、拼音緩沖區(qū)字符串。這些狀態(tài)用移位寄存器保存每處理一次按鍵后更新?tīng)顟B(tài)再傳給下一輪事件。字符鍵的處理邏輯分四種情況。模式是英文時(shí)把標(biāo)簽文本按Shift和Caps狀態(tài)做大小寫(xiě)轉(zhuǎn)換后寫(xiě)入目標(biāo)控件模式是數(shù)字時(shí)只允許數(shù)字鍵輸出模式是拼音時(shí)字符進(jìn)入拼音緩沖區(qū)并刷新候選字列表不直接上屏拼音候選字被選定時(shí)把選中的漢字拼接進(jìn)目標(biāo)文本框同時(shí)清空拼音緩沖區(qū)。功能鍵處理相對(duì)獨(dú)立Backspace從目標(biāo)控件文本末尾刪一個(gè)字符Enter發(fā)送用戶事件觸發(fā)輸入完成Shift和Caps只改狀態(tài)不產(chǎn)生字符輸出。這里提醒一個(gè)新手容易漏的點(diǎn)事件結(jié)構(gòu)里的多個(gè)按鍵共享一個(gè)分支時(shí)注意事件數(shù)據(jù)里還要判斷觸發(fā)事件的是哪一個(gè)引用。尤其是在綁定動(dòng)態(tài)事件時(shí)如果不加區(qū)分就拿引用去讀標(biāo)簽文本后面Case結(jié)構(gòu)會(huì)亂。建議在事件分支開(kāi)始處就讀取引用并轉(zhuǎn)成局部變量后面各處統(tǒng)一使用同一個(gè)引用保證一致性。4.3 子VI封裝、數(shù)據(jù)交互與主程序?qū)渔I盤(pán)做完后以子VI形式供主程序調(diào)用。子VI對(duì)外暴露的接口我固定為三組目標(biāo)控件引用輸入、當(dāng)前模式輸入輸出、用戶事件輸出輸出。主程序里每個(gè)需要輸入的可編輯控件都綁定一個(gè)鼠標(biāo)按下或者鍵盤(pán)焦點(diǎn)事件在事件處理里把該控件的引用傳給鍵盤(pán)子VI的輸入引腳。這樣操作員點(diǎn)哪個(gè)輸入框虛擬鍵盤(pán)就自動(dòng)知道往哪個(gè)框里送字符不需要額外做選中目標(biāo)的操作。數(shù)據(jù)交互方式我推薦用戶事件加隊(duì)列的組合。用戶事件用來(lái)通知輸入完成這類異步業(yè)務(wù)信號(hào)隊(duì)列用來(lái)傳遞字符串消息主程序在其他循環(huán)里消費(fèi)。如果項(xiàng)目規(guī)模不大也可以簡(jiǎn)化成鍵盤(pán)子VI輸出一個(gè)字符串控件主程序輪詢字符串控件取值后清空。不過(guò)輪詢方案多多少少有點(diǎn)浪費(fèi)CPU而且處理不及時(shí)我建議能上事件就上事件。如果你用的是JKI狀態(tài)機(jī)之類的框架虛擬鍵盤(pán)的輸出消息還可以直接轉(zhuǎn)成消息幀塞進(jìn)狀態(tài)機(jī)隊(duì)列里實(shí)現(xiàn)主界面邏輯和鍵盤(pán)輸入模塊的徹底解耦。我有一個(gè)項(xiàng)目就是這么干的鍵盤(pán)子VI完全不知道主程序在做什么它只負(fù)責(zé)把人按的鍵翻譯成消息主狀態(tài)機(jī)收到消息后再?zèng)Q定跳轉(zhuǎn)、校驗(yàn)還是落庫(kù)維護(hù)起來(lái)非常清爽。4.4 與掃碼槍等外部鍵盤(pán)輸入的統(tǒng)一處理前面提到過(guò)很多掃碼槍工作在USB鍵盤(pán)模式相當(dāng)于一個(gè)只會(huì)敲字符串的實(shí)體鍵盤(pán)插入電腦后不需要裝驅(qū)動(dòng)焦點(diǎn)在哪個(gè)文本框條碼就進(jìn)哪個(gè)框。這種設(shè)計(jì)恰恰給了我們一個(gè)啟發(fā)虛擬鍵盤(pán)本質(zhì)上也是輸入設(shè)備它的輸出路徑跟掃碼槍沒(méi)有本質(zhì)區(qū)別都是往當(dāng)前焦點(diǎn)控件里塞字符。所以我在主程序中并不區(qū)分字符是從虛擬鍵盤(pán)來(lái)的還是掃碼槍來(lái)的統(tǒng)一目標(biāo)控件值變更、統(tǒng)一回車觸發(fā)邏輯數(shù)據(jù)源無(wú)關(guān)性讓業(yè)務(wù)層不用寫(xiě)一堆條件判斷。唯一需要留意的是掃碼槍的字符輸入速度很快一整個(gè)條碼在幾十毫秒內(nèi)全部灌進(jìn)文本框如果目標(biāo)控件的事件處理里做了重量級(jí)操作比如查數(shù)據(jù)庫(kù)就會(huì)把輸入卡在半路。做法是把數(shù)據(jù)接收和業(yè)務(wù)處理放在不同循環(huán)里接收循環(huán)只管把值收進(jìn)來(lái)放進(jìn)隊(duì)列處理循環(huán)再慢慢消費(fèi)兩者速率天然解耦。虛擬鍵盤(pán)手動(dòng)輸入雖然慢但在同一套架構(gòu)下也享受到了這個(gè)好處。5. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄5.1 點(diǎn)擊按鍵后目標(biāo)框的焦點(diǎn)被搶走這是虛擬鍵盤(pán)項(xiàng)目里出現(xiàn)頻率最高的問(wèn)題現(xiàn)象是操作員點(diǎn)一下虛擬鍵盤(pán)按鈕輸入框的光標(biāo)就不見(jiàn)了后面的字符要么沒(méi)反應(yīng)要么跑進(jìn)奇怪的地方。排查要點(diǎn)是把導(dǎo)致焦點(diǎn)變化的每一個(gè)事件都過(guò)一遍。首先確認(rèn)點(diǎn)擊虛擬鍵后目標(biāo)輸入控件是否被重新設(shè)置了焦點(diǎn)其次檢查目標(biāo)控件的屬性里有沒(méi)有禁用或者只讀開(kāi)關(guān)誤開(kāi)導(dǎo)致它根本拿不到焦點(diǎn)最后檢查鍵盤(pán)按鈕的機(jī)械動(dòng)作會(huì)不會(huì)造成值在多個(gè)循環(huán)間閃爍因?yàn)榻裹c(diǎn)切換跟控件狀態(tài)變化有關(guān)。如果焦點(diǎn)設(shè)置了但焦點(diǎn)還是丟多半是鍵盤(pán)子VI被當(dāng)成獨(dú)立頂層窗口彈出跨VI焦點(diǎn)控制受限。把鍵盤(pán)改成嵌入主程序前面板后這個(gè)問(wèn)題基本能解決。5.2 中文輸出亂碼與字體顯示異常中文亂碼分兩種情況處理。第一種是真正的編碼錯(cuò)亂出現(xiàn)原因是LabVIEW字符串在不同機(jī)器上的本地代碼頁(yè)不一致比如程序在中文系統(tǒng)開(kāi)發(fā)部署到英文系統(tǒng)后中文字符串變成問(wèn)號(hào)。解決思路是在涉及文件的場(chǎng)景把字符串統(tǒng)一轉(zhuǎn)成UTF-8字節(jié)寫(xiě)入讀取時(shí)反向轉(zhuǎn)換不要直接拿中文字符串當(dāng)落庫(kù)值在純界面顯示場(chǎng)景則要保證目標(biāo)文本框控件使用的字體支持中文常見(jiàn)做法是把字體設(shè)置為宋體或微軟雅黑。第二種是顯示問(wèn)題字符沒(méi)亂但顯示成方框。這種不是編碼問(wèn)題是字體不支持。設(shè)備部署機(jī)上如果沒(méi)有安裝對(duì)應(yīng)字體界面上的中文字符就會(huì)被替代字符占位。打包時(shí)把字體一并帶上或者干脆在設(shè)計(jì)時(shí)就指定系統(tǒng)自帶且支持中文的字體能省去不少現(xiàn)場(chǎng)救火的時(shí)間。5.3 觸摸屏上按鍵手感差、誤觸多觸摸屏操作和鼠標(biāo)點(diǎn)擊不一樣沒(méi)有懸停狀態(tài)也沒(méi)有精確的光標(biāo)。操作員如果戴手套手指接觸面積大40像素以下的按鍵非常容易誤觸相鄰鍵。經(jīng)驗(yàn)值是鍵間距至少2到4像素整鍵尺寸至少40像素如果屏是10.1寸以下的小屏寧可壓縮布局行數(shù)也不能無(wú)限縮小按鍵。另外我遇到過(guò)一種典型的誤觸按鍵值在觸摸屏的電容感應(yīng)上產(chǎn)生抖動(dòng)一次點(diǎn)擊被事件結(jié)構(gòu)抓到兩次True導(dǎo)致一個(gè)字符輸出兩遍。排查時(shí)在事件分支的True處理里加一個(gè)簡(jiǎn)單的時(shí)間過(guò)濾比如連續(xù)兩次按鍵間隔小于100毫秒則忽略基本能壓掉這類問(wèn)題。要記得這個(gè)過(guò)濾只防抖不要擋住操作員故意快速連輸?shù)那闆r所以閾值不要設(shè)得太大。5.4 打包部署后鍵盤(pán)顯示異常開(kāi)發(fā)環(huán)境里一切正常打包安裝到現(xiàn)場(chǎng)機(jī)器后鍵盤(pán)錯(cuò)位、字體變化、界面縮放異常這類問(wèn)題多半和高DPI、字體缺失有關(guān)。工控現(xiàn)場(chǎng)常見(jiàn)的觸摸屏一體機(jī)分辨率不低但縮放設(shè)置五花八門(mén)LabVIEW前面板如果沒(méi)做DPI感知設(shè)置高分屏下會(huì)模糊一片或者控件錯(cuò)位。打包前建議在項(xiàng)目屬性里確認(rèn)前面板縮放模式在目標(biāo)機(jī)器上實(shí)測(cè)不同分辨率下的顯示效果優(yōu)先保證最常使用的那個(gè)分辨率表現(xiàn)良好。還有一個(gè)容易踩的坑有些工控機(jī)安裝的是LabVIEW運(yùn)行引擎而不是完整開(kāi)發(fā)環(huán)境如果鍵盤(pán)VI用到了某個(gè)只在開(kāi)發(fā)環(huán)境默認(rèn)路徑下的字體或資源打包時(shí)沒(méi)設(shè)置好運(yùn)行環(huán)境里就會(huì)找不到。打包前用安裝程序向?qū)О堰\(yùn)行引擎一并帶上再把項(xiàng)目用到的字體手動(dòng)添加進(jìn)來(lái)能避免大部分這類問(wèn)題。5.5 幾個(gè)項(xiàng)目用過(guò)之后的整體體會(huì)這套虛擬鍵盤(pán)源程序我在實(shí)際交付里反復(fù)用了幾輪最大的體會(huì)是虛擬鍵盤(pán)不是一個(gè)錦上添花的玩具功能而是直接影響設(shè)備能否通過(guò)驗(yàn)收的基礎(chǔ)設(shè)施??蛻舨粫?huì)因?yàn)樗嗉臃值坏┲形妮斎敕ㄕ{(diào)不出來(lái)、焦點(diǎn)亂跳、回車不觸發(fā)減分是實(shí)打?qū)嵉摹K越ㄗh大家在項(xiàng)目計(jì)劃里就把鍵盤(pán)模塊當(dāng)成一等公民排期而不是最后一天臨時(shí)塞進(jìn)去的東西。我還總結(jié)出一個(gè)實(shí)用習(xí)慣鍵盤(pán)子VI做成通用模板后每個(gè)新項(xiàng)目先復(fù)制一份再按項(xiàng)目需求改三樣?xùn)|西——拼音字典加項(xiàng)目專用詞匯、功能鍵布局、配色主題。主界面業(yè)務(wù)邏輯完全不需要?jiǎng)印_@樣一套框架沉淀下來(lái)后面再遇到客戶臨時(shí)要加中文輸入這類需求改動(dòng)時(shí)間基本能控制在半天以內(nèi)現(xiàn)場(chǎng)應(yīng)對(duì)起來(lái)會(huì)從容很多。最后再提一個(gè)細(xì)節(jié)記得在鍵盤(pán)上給輸入完成留一個(gè)顯眼的確認(rèn)鍵并且讓這個(gè)鍵在觸摸屏上足夠大很多操作員輸完數(shù)據(jù)后習(xí)慣性地找綠色確認(rèn)按鈕這個(gè)位置做不好前面所有輸入體驗(yàn)都白搭。