美文网首页NodeJS in LNNM前端
一个javascript条码打印机程序给我的惊喜

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

作者: Tulayang | 来源:发表于2015-04-01 16:09 被阅读1897次

    <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方式的语法糖,而在顺序的代码中很少去这样做。然而,这次偶然的经历,让我对代码的编写有了一些新的思考。即便是顺序的代码,也许仍有一些巧妙的方法可以变得更加灵活。

    相关文章

      网友评论

        本文标题:一个javascript条码打印机程序给我的惊喜

        本文链接:https://www.haomeiwen.com/subject/yoyoxttx.html