一个javascript条码打印机程序给我的惊喜

<br />

昨天为了一个“特殊”的需要,我跟一个BTP-2200E条码打印机“鼓捣”了一下午,
竟然搞出了一个我现在最为满意的程序。

额......当我第一次知道要写那个“特殊”程序的时候,其实我是拒绝的!因为我觉得……呀……你不能叫我写马上写,第一,我要试一下,我又不想说……你接一个需求写完以后加了很多框架,那程序跑啊……很快!很短!很小!结果客户出来一定骂我,根本没有这种程序!这证明上面那个是假的……我说先要给我试一下。后来我经过也知道他们是条码的,而且没有那种三帝成分的。那……写了这下午……这下午下来之后呢……起码我写了很舒服。现在呢……每天还在写!每天还在写呢.....我还给了我业务员用!来!来!来!大家试试看!那我跟老板讲︰「写的时候就写!写完之后,这个代码就是我的代码,就不要加,再加框架上去,加到没有就是这样子!我要给客户看到,我写完之后是这样子,你们写完之后,也是这样子!

说到打印机,多少让人联想到计算机的起源,比如磁带,比如移位。这个BTP-2200E是一个仓库用的条码打印机,一条长的黑色磁带,和一个长的白色底带,从盒子一端嘀嘀嘀的出来一张卡片似的条码。打出来是类似这样的:


+----------------------+
| 货号: 16C002-2       
| 颜色: 粉色  尺码: 236  
| 材质: 牛皮  等级: 一型  |
| QB/T1001-2006        |
| GB/T22969-2009       |
| ||||||||||||||||     |
|  16C002-2039136      |
+-----------------------+

<br />

源代码如下:

<br />


(function(global, mod) {
    if (typeof exports == "object" && typeof module == "object") { 
        return mod(exports); 
    } else if (typeof define == "function" && define.amd) {
        return define(["exports"], mod); 
    } else { 
        mod(global.BPLA || (global.BPLA = {})); 
    }
})(this, function(exports) {
    var BPLA_OK = 0, 
        port = -1, 
        errno = '',
        messages = {
            errorLoadBplaocx: '没有成功加载BPLAOCX插件',
            errorOpenPort: '打开端口错误,请检查连接或端口参数设置',
            errorTimeout: '超时配置未能正确设置,请检查连接或端口参数设置',
            errorPrint: '打印条形码错误'
        };

// 检测到错误,中断程序

    function error(m) {
        errno = messages[m]; 
    }

// 检查BPLAOCX插件加载

    function checkatx() {
        if (typeof BPLAOCX !== 'object' || BPLAOCX === null) {
            error('errorLoadBplaocx');
        }
    }

// 关闭端口

    function close() {
        BPLAOCX.BPLACloseUSB(port);
        port = -1;
        return "Port closed!";  
    }

// 打开端口

    function open() {
        if ((port = BPLAOCX.BPLAOpenUSB(0, 0)) === -1) {
            error('errorOpenPort');
        } else if(BPLAOCX.BPLASetTimeOut(port, iWrTime, iReTime) != BPLA_OK) {
            close();
            error('errorTimeout');
        }
    }

// 程序序列器,这个工具会顺序运行操作,直到出现一个错误,序列器会终止运行,
// 并返回错误。如果成功打印,序列器会返回一个成功信息。

    function sequence(/*fn1, fn2, ...*/) {
        var functions = Array.prototype.slice.call(arguments),
            i = 0,
            fn;
        while (typeof (fn = functions[i++]) === 'function') {
            fn();
            if (errno !== '') {
                return {msg:errno, state:0};
            }
        }
        return {msg:'成功打印条形码!', state:1};
    }

    function printer(method, args) {
        return function () {
            if (BPLAOCX[method].apply(BPLAOCX, args) !== BPLA_OK) {
                error('errorPrint');
            }
        };
    }

// 打印队列

    exports.print = function (data) {
    // data: {
    //     serial: String, 货号
    //     color: String, 颜色
    //     size: String, 尺码
    //     material: String, 材质
    //     level: String, 等级
    //     model: String, 型号
    //     qbnum: String, QB号
    //     gbnum: String, GB号
    //     hcode: String, 水平码
    //     vcode: String, 垂直码 
    // }
        return sequence(
            checkatx,
            open,
            printer('BPLASetPaperLength', [port, 550, 0]),
            printer('BPLAStartArea',      [port, 0, 1000, 0, 0, 0, 0]),
            printer('BPLAPrintBarcode',   [port, data.hcode, 150, 100, 1, 24, 80, 4, 2, "000", 0, 0]),
            printer('BPLAPrintBarcode',   [port, data.vcode,  860, 50, 4, 5, 80, 4, 3, "000", 0, 0]), 
            printer('BPLAPrintTruetype',  [port, data.hcode, 210, 25, "Arial", 40, 0, 1, 0, 0, 1]),
            printer('BPLAPrintTruetype',  [port, "(内部使用])", 330, 70, "Arial", 25, 0, 0, 0, 0, 1]),
            printer('BPLAPrintTruetype',  [port, data.gbnum, 120, 200, "Arial", 35, 0, 0, 0, 0, 1]), 
            printer('BPLAPrintTruetype',  [port, data.qbnum, 120, 260, "Arial", 35, 0, 0, 0, 0, 1]),
            printer('BPLAPrintTruetype',  [port, "型号: " + data.model, 460, 260, "黑体", 35, 0, 0, 0, 0, 1]),
            printer('BPLAPrintTruetype',  [port, "材质: " + data.material, 120, 320, "黑体", 35, 0, 0, 0, 0, 1]),
            printer('BPLAPrintTruetype',  [port, "等级: " + data.level, 460, 320, "黑体", 35, 0, 0, 0, 0, 1]),
            printer('BPLAPrintTruetype',  [port, "颜色:", 120, 380, "黑体", 35, 0, 0, 0, 0, 1]),
            printer('BPLAPrintTruetype',  [port, data.color, 250, 370, "黑体", 50, 0, 1, 0, 0, 1]),
            printer('BPLAPrintTruetype',  [port, "尺码:", 460, 380, "黑体", 35, 0, 1, 0, 0, 1]),
            printer('BPLAPrintTruetype',  [port, data.size, 600, 372, "黑体", 50, 0, 1, 0, 0, 1]),
            printer('BPLAPrintTruetype',  [port, "货号:", 120, 440, "黑体", 35, 0, 0, 0, 0, 1]),
            printer('BPLAPrintTruetype',  [port, data.serial, 250, 430, "黑体", 50, 0, 0, 0, 0, 1]),
            printer('BPLAPrint',          [port, 1, 0, 1]),
            close
        );
    };  
});

调用的方式是这样的:


<!DOCTYPE html>
<html>
<head>
<script src="/js/bpla.js"></script>
<!--其他html内容-->
<object codebase="/bpla/BPLAOCX.cab"></object>
<script type="text/javascript">
    document.getElementById('printer').onclick = function () {
        var state = BPLA.print({
            serial   : '16C002-2',
            color    : '粉色',
            size     : '236',
            material : '牛皮',
            level    : '合格',
            model    : '一型',
            qbnum    : 'QB/T1001-2006',
            gbnum    : 'GB/T22969-2009',
            hcode    : '6961996123006',
            vcode    : '16C002-2039136'
        });
        document.getElementById('state').innerHTML = state.msg;
    };
</script>
</html>

HTML页面有一个打印按钮,当点击的时候,打印机会打开端口,打印传送的数据。

这个程序的主要处理程序是exports.print(data),输入一个配置过的数据对象,
调用sequence序列器,运行一串操作。

BTP-2200E的开发库提供了一个BPLAOCX模块,包含了打印机可调用的接口。每一个BPLAOCX调用都会返回一个状态函数,-1或者是大于0的值,用来检测打印机的可用状态,这意味着,如果你不做一些处理,你的代码将会充满了大量的没有多少价值的if else:


if (BPLAOCX.BPLASetPaperLength(port, 550, 0) < 0) {
    // 一个错误
} else if (BPLAOCX.BPLAStartArea(port, 0, 1000, 0, 0, 0, 0) < 0) {
    // 一个错误
} else if (BPLAOCX.BPLAPrintBarcode(port, data.hcode, 150, 100, 1, 24, 80, 4, 2, "000", 0, 0) < 0) {
...

if else是算法逻辑所必需的,但是如果只是单纯的状态检查,而且是一大堆的时候,你就会有些崩溃了,尤其是以后需要修改内容的时候。在长长的if else代码中查找和修改代码都是很容易产生bug,而且这些代码很难有描述性。

所以,最终,一个printer(method, args)用来作为一个语法糖,封装了错误处理。

错误处理是通过一个全局的messages对象拾取,一个errno标识来访问。如果errno是空值,就表明操作没有错误,如果errno是非空值,就表明出现错误,操作需要终止。(这个手法其实是抄袭自C的标准库)

printer()调用BPLAOCX的方法,操作打印机,当出现错误的时候,就设置errno的对应错误值。

sequence()是一个流程处理函数,输入参数都是函数。sequence()会顺序处理序列,调用每一个进入的函数,运行完的时候检查当前的errno状态,出现错误的时候,立刻终止程序,并返回一个错误信息。当所有打印成功完成后,关闭打印端口,并且返回一个成功信息。

这个打印序列的操作过程:


⇀ 检测打印模块加载
⇀ 打开端口
⇀ 设置打印纸长
⇀ 调整条码参数
⇀ 调整条码参数
⇀ 调整文字参数
⇀ 调整文字参数
⇀ 调整文字参数
⇀ 调整文字参数
⇀ 调整文字参数
⇀ 调整文字参数
⇀ 调整文字参数
⇀ 调整文字参数
⇀ 调整文字参数
⇀ 调整文字参数
⇀ 调整文字参数
⇀ 调整文字参数
⇀ 调整文字参数
⇀ 开始打印
⇀ 关闭端口

而ta所对应的程序代码正是:

sequence(
      checkatx,
      open,
      printer('BPLASetPaperLength', [port, 550, 0]),
      printer('BPLAStartArea',      [port, 0, 1000, 0, 0, 0, 0]),
      printer('BPLAPrintBarcode',   [port, data.hcode, 150, 100, 1, 24, 80, 4, 2, "000", 0, 0]),
      printer('BPLAPrintBarcode',   [port, data.vcode,  860, 50, 4, 5, 80, 4, 3, "000", 0, 0]), 
      printer('BPLAPrintTruetype',  [port, data.hcode, 210, 25, "Arial", 40, 0, 1, 0, 0, 1]),
      printer('BPLAPrintTruetype',  [port, "(内部使用])", 330, 70, "Arial", 25, 0, 0, 0, 0, 1]),
      printer('BPLAPrintTruetype',  [port, data.gbnum, 120, 200, "Arial", 35, 0, 0, 0, 0, 1]), 
      printer('BPLAPrintTruetype',  [port, data.qbnum, 120, 260, "Arial", 35, 0, 0, 0, 0, 1]),
      printer('BPLAPrintTruetype',  [port, "型号: " + data.model, 460, 260, "黑体", 35, 0, 0, 0, 0, 1]),
      printer('BPLAPrintTruetype',  [port, "材质: " + data.material, 120, 320, "黑体", 35, 0, 0, 0, 0, 1]),
      printer('BPLAPrintTruetype',  [port, "等级: " + data.level, 460, 320, "黑体", 35, 0, 0, 0, 0, 1]),
      printer('BPLAPrintTruetype',  [port, "颜色:", 120, 380, "黑体", 35, 0, 0, 0, 0, 1]),
      printer('BPLAPrintTruetype',  [port, data.color, 250, 370, "黑体", 50, 0, 1, 0, 0, 1]),
      printer('BPLAPrintTruetype',  [port, "尺码:", 460, 380, "黑体", 35, 0, 1, 0, 0, 1]),
      printer('BPLAPrintTruetype',  [port, data.size, 600, 372, "黑体", 50, 0, 1, 0, 0, 1]),
      printer('BPLAPrintTruetype',  [port, "货号:", 120, 440, "黑体", 35, 0, 0, 0, 0, 1]),
      printer('BPLAPrintTruetype',  [port, data.serial, 250, 430, "黑体", 50, 0, 0, 0, 0, 1]),
      printer('BPLAPrint',          [port, 1, 0, 1]),
      close
)

在异步代码的时候,我会常常写一些sequence方式的语法糖,而在顺序的代码中很少去这样做。然而,这次偶然的经历,让我对代码的编写有了一些新的思考。即便是顺序的代码,也许仍有一些巧妙的方法可以变得更加灵活。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,681评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,710评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,623评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,202评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,232评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,368评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,795评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,461评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,647评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,476评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,525评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,226评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,785评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,857评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,090评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,647评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,215评论 2 341

推荐阅读更多精彩内容

  • error code(错误代码)=0是操作成功完成。error code(错误代码)=1是功能错误。error c...
    Heikki_阅读 3,344评论 1 9
  • 激光技术出现于60年代,真正投入实际应用始于70年代初期。最早的激光发射器是充有氦-氖(He-Ne)气体的电子激...
    我是嘻哈大哥阅读 2,993评论 1 6
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,841评论 6 13
  • error code(错误代码)=2000是无效的像素格式。error code(错误代码)=2001是指定的驱动...
    Heikki_阅读 1,752评论 0 4
  • 前段时间在简书上看到有个小青年称自己是独居小怪兽,最近各种微信公众号又说大城市的空巢青年有几千万,各种提醒独居女子...
    飞奔的镜子阅读 426评论 0 2