佳博/芯桦打票机对接(含USB和网口)
记录一下最近实现的一个需求,用PHP远程连接芯桦、佳博打票机,代码还未进行优化,基本功能已经实现打印,具体打印指令需要查看佳博/芯桦的打票机指令,USB口打印质量需要其他语言配合,我这边用到了.net编写USB口的接收指令程序,有兴趣了解的可以联系一起学习探讨。
遇到一些雷点:
-
比如如何保证极端环境下,不漏单,可以使用佳博/芯桦打票机的一个回应的指令判断,但是值得注意的是,需要有一些判断网络连接的措施,比如可以有超时的机制,出现超时、指令异常的情况,放入队列等待重新发送指令。
-
内网穿透的问题,因为商家打票机处于一个IP内的局域网环境,所以需要使用到内网穿透,一开始我们采用的是NAT123,后面发现这个是个大坑啊!!! 我了个槽,不仅转发极其缓慢,偶尔还有时候会转发出错。
然后后面果断改变策略,使用了一个frp的工具,把转发交给了自家的服务器,果然快了很多!
基本上都是秒出单的(也有可能是暂时单少),frp搭建和使用可能后面有时间会写,暂时太忙了,没什么时间写,不过网上也一堆教程,有需要可以去看看。 -
网口和USB口的区别,网口的话芯桦和佳博默认端口都是9100,设备都会有网卡,只需要在frp做内网穿透的时候,绑定转发到这个端口就行了。USB口的话,需要编写接收程序去驱动这个USB串口,至于方法的话,有很多种,有其他语言基础的小伙伴可以看看佳博官方的demo,不过值得注意的就是demo仅供参考。。。最终的实现方案,还有容错机制还是要自己去实现的。。
-
商家设备问题,遇到一个客如云的商家,安卓系统,原本想要做成APP去做驱动USB串口,没想到客如云直接不让你安装第三方未授权APP(简直流氓!),然后此类的商家的话,只能是找客如云合作,或者购买其他的打票设备了。windows收银机的话,.net是可以编写接收指令和驱动串口的,开机直接后台启动,当然容错机制和LOG也是要有的,自己注意就好!不跑题了,官方的其他方法没试过,有条件的小伙伴可以自行尝试。
-
接收指令的问题,刚开始做打票机指令接收的时候,发现指令断断续续的,有时候还会有丢数据的情况出现,这个问题我不知道是不是网络的粘包问题,有时候接收的指令都是不全的,出现此问题可以尝试分段读取流,或者尝试流传输时带长度,然后接受时校验,然后容错那里也可以加上佳博/芯桦打票机的一个打印后返回指令,如果不正常,就通知服务器发送端,后面尝试重新发送指令。
最后说说:
-
这次需求比较着急,实现的也比较仓促,从0到1,代码还没时间去做深层的优化,写的不好不要喷我,哈哈哈。代码仅供参考!!!
-
有类似需求,但是不懂的,也可以私信联系,大家可以一起探讨,虽然不是最好的,但是也有一些自己的经验、见解。希望可以帮到大家。
-
还有就是希望看到这里的码友,在平常实现需求的过程中,不要轻言放弃,虽然问题会一个接着一个,但总会有方法破浪前行的,以此共勉!
<?phpclass Printer
{protected $ESC="\x1b";//设置protected $GS="\x1d";//页面设置 字体大小17倍数protected $HT="\x09";//跳格设置 9,17,25,33,41protected $CUT="\x1d";//切纸protected $DEL="\x10";//切纸protected $EOT="\x04";//切纸protected $LF="\x0a";//打印缓冲区并换行public $fp = '';public $errno = '';public $ip = '';public $port = '';public function __construct($print_ip,$print_port){$this->fp = @fsockopen($print_ip,$print_port,$errno,$errstr,30);$this->errno = $errno;$this->ip = $print_ip;$this->port = $print_port;}/*** 网口类型打票机60mm* @param $order*/public function printOrder($order) {fwrite($this->fp,$this->ESC."@");//初始化fwrite($this->fp,$this->GS."!".chr(17));fwrite($this->fp,$this->ESC."a".chr(1));//居中fwrite($this->fp,iconv("UTF-8","GBK//IGNORE",'取餐号:'.$order['meal_code']));fwrite($this->fp,$this->LF);fwrite($this->fp,iconv("UTF-8","GBK//IGNORE",$order['shop']['shop_title']));fwrite($this->fp,$this->LF);fwrite($this->fp,$this->GS."!".chr(0));fwrite($this->fp,"------------------------------------------------");fwrite($this->fp,$this->LF);fwrite($this->fp,$this->GS."!".chr(17));fwrite($this->fp,iconv("UTF-8","GBK//IGNORE","桌号:".$order['table'] ? $order['table']['number'] : ''));fwrite($this->fp,$this->LF);fwrite($this->fp,$this->GS."!".chr(0));fwrite($this->fp,iconv("UTF-8","GBK//IGNORE","结账时间:".date('Y-m-d H:i:s',$order['pay_time'])));fwrite($this->fp,$this->LF);fwrite($this->fp,iconv("UTF-8","GBK//IGNORE","收银员:顾客/系统"));fwrite($this->fp,$this->LF);fwrite($this->fp,iconv("UTF-8","GBK//IGNORE","单号:".$order['oid']));fwrite($this->fp,$this->LF);fwrite($this->fp,"------------------------------------------------");fwrite($this->fp,$this->ESC."a".chr(0));fwrite($this->fp,$this->HT);fwrite($this->fp,iconv("UTF-8","GBK//IGNORE","品项"));fwrite($this->fp,$this->HT);fwrite($this->fp,$this->HT);fwrite($this->fp,$this->HT);fwrite($this->fp,iconv("UTF-8","GBK//IGNORE","数量"));fwrite($this->fp,$this->HT);fwrite($this->fp,iconv("UTF-8","GBK//IGNORE","原价"));fwrite($this->fp,$this->HT);fwrite($this->fp,iconv("UTF-8","GBK//IGNORE","优惠价"));fwrite($this->fp,$this->LF);foreach ($order['product'] as $val) {$mc=$val['title'].'('.$val['sku_cn'].')';$str_length = mb_strlen($mc);$str_count = ceil($str_length / 7);if ($str_count > 0) {for ($i = 1;$i <= $str_count;$i ++) {fwrite($this->fp,iconv("UTF-8","GBK//IGNORE",mb_substr($mc,($i - 1) * 7,7)));if ($i == 1) {fwrite($this->fp,$this->HT);fwrite($this->fp,$this->HT);fwrite($this->fp,iconv("UTF-8","GBK//IGNORE",$val['number']));//数量fwrite($this->fp,$this->HT);fwrite($this->fp,iconv("UTF-8", "GBK//IGNORE",$val['money']));//价格fwrite($this->fp,$this->HT);fwrite($this->fp,iconv("UTF-8","GBK//IGNORE",$val['money']));//优惠价fwrite($this->fp,$this->HT);} else {fwrite($this->fp,$this->HT);fwrite($this->fp,$this->HT);fwrite($this->fp,$this->HT);fwrite($this->fp,$this->HT);fwrite($this->fp,$this->LF);}}}}fwrite($this->fp,"------------------------------------------------");fwrite($this->fp,$this->ESC."a".chr(0));fwrite($this->fp,iconv("UTF-8","GBK//IGNORE","菜品原价"));fwrite($this->fp,$this->HT);fwrite($this->fp,$this->HT);fwrite($this->fp,$this->HT);fwrite($this->fp,$this->HT);fwrite($this->fp,iconv("UTF-8","GBK//IGNORE",$order['sum_price']));fwrite($this->fp,$this->LF);fwrite($this->fp,iconv("UTF-8","GBK//IGNORE","订单原价"));fwrite($this->fp,$this->HT);fwrite($this->fp,$this->HT);fwrite($this->fp,$this->HT);fwrite($this->fp,$this->HT);fwrite($this->fp,iconv("UTF-8","GBK//IGNORE",$order['sum_price']));fwrite($this->fp,$this->LF);fwrite($this->fp,$this->GS."!".chr(0));fwrite($this->fp,"------------------------------------------------");$mc = "支付方式";$this->setHt($mc);fwrite($this->fp,$this->HT);fwrite($this->fp,$this->HT);$pay = $order['pay'];if (mb_strlen($pay) < 4) {fwrite($this->fp,$this->HT);}fwrite($this->fp,iconv("UTF-8","GBK//IGNORE",$pay));fwrite($this->fp,$this->LF);fwrite($this->fp,iconv("UTF-8","GBK//IGNORE","应付金额"));fwrite($this->fp,$this->HT);fwrite($this->fp,$this->HT);fwrite($this->fp,$this->HT);fwrite($this->fp,$this->HT);$price = $order['money'];fwrite($this->fp,iconv("UTF-8","GBK//IGNORE",$price));fwrite($this->fp,$this->LF);fwrite($this->fp,$this->GS."!".chr(0));fwrite($this->fp,"------------------------------------------------");fwrite($this->fp,$this->LF);fwrite($this->fp,iconv("UTF-8","GBK//IGNORE","下单人:顾客/系统"));fwrite($this->fp,$this->LF);fwrite($this->fp,iconv("UTF-8","GBK//IGNORE","下单时间:".date('Y-m-d H:i:s',$order['created_time'])));fwrite($this->fp,$this->LF);fwrite($this->fp,iconv("UTF-8","GBK//IGNORE","打印人:管理员"));fwrite($this->fp,$this->LF);fwrite($this->fp,iconv("UTF-8","GBK//IGNORE","打印时间:".date('Y-m-d H:i:s',time())));fwrite($this->fp,$this->LF);fwrite($this->fp,iconv("UTF-8","GBK//IGNORE","备注:".$order['remark']));fwrite($this->fp,$this->ESC."d".chr(5));//走行fwrite($this->fp,$this->CUT."V".chr(0).chr(2));//切纸fwrite($this->fp,$this->ESC."C".chr(3).chr(2).chr(3));fwrite($this->fp,$this->DEL.$this->EOT.chr(1));}/*** 网口标签机* @param $item* @param $usb_port*/public function printOrder2Label($item) {$list = ['SIZE 40 mm,30 mm', //设置纸张'GAP 3 mm,0 mm', //设置间距'CLS', //刷新缓冲区'DENSITY 7','REFERENCE 0,0','SHIFT 0','TEXT 20,20,"TSS24.BF2",0,1,1,"'.iconv("UTF-8","GBK//IGNORE",'美团外卖 #'.$item['table'].' '.$item['meal_code']).'"', //文字'TEXT 20,50,"TSS24.BF2",0,1,1,"'.iconv("UTF-8","GBK//IGNORE",$item['title']).'"', //文字'TEXT 20,80,"TSS24.BF2",0,1,1,"'.iconv("UTF-8","GBK//IGNORE",mb_substr($item['sku_cn'],0,13)).'"', //文字'TEXT 20,110,"TSS24.BF2",0,1,1,"'.iconv("UTF-8","GBK//IGNORE",mb_substr($item['sku_cn'],13,13)).'"', //文字'TEXT 20,140,"TSS24.BF2",0,1,1,"'.iconv("UTF-8","GBK//IGNORE",'价格:'.$item['price']).'"', //文字'TEXT 20,170,"TSS24.BF2",0,1,1,"'.iconv("UTF-8","GBK//IGNORE",$item['shop_title']).'"', //文字'TEXT 20,200,"TSS24.BF2",0,1,1,"'.date('Y-m-d H:i:s').'"', //文字'PRINT 1'];foreach ($list as &$val) {$val = $val.'\r\n';$val = str_replace("\\r\\n", "\r\n",$val);$val = str_replace("\\", "",$val);fwrite($this->fp,$val);}}/*** USB标签机* @param $item* @param $usb_port*/public function printOrder2($item) {$list = ['SIZE 40 mm,30 mm', //设置纸张'GAP 3 mm,0 mm', //设置间距'CLS', //刷新缓冲区'DENSITY 7','REFERENCE 0,0','SHIFT 0','TEXT 20,20,"TSS24.BF2",0,1,1,"美团外卖 #'.$item['meal_code'].'"', //文字'TEXT 20,50,"TSS24.BF2",0,1,1,"'.$item['title'].'"', //文字'TEXT 20,80,"TSS24.BF2",0,1,1,"'.mb_substr($item['sku_cn'],0,13).'"', //文字'TEXT 20,110,"TSS24.BF2",0,1,1,"'.mb_substr($item['sku_cn'],13,13).'"', //文字'TEXT 20,140,"TSS24.BF2",0,1,1,"价格:'.$item['price'].'"', //文字'TEXT 20,170,"TSS24.BF2",0,1,1,"'.$item['shop_title'].'"', //文字'TEXT 20,200,"TSS24.BF2",0,1,1,"'.date('Y-m-d H:i:s').'"', //文字'PRINT 1'];foreach ($list as &$val) {$val = $val.'\r\n';fwrite($this->fp,$val);}}/*** USB40mm打票机* @param $order* @param $usb_port*/public function printOrder3($order) {$add = [$this->ESC."@",$this->GS."!".chr(17),$this->ESC."a".chr(1),'取餐号:'.$order['meal_code'],$this->LF,$order['shop']['shop_title'],$this->LF,$this->GS."!".chr(0),"--------------------------------",$this->LF,$this->GS."!".chr(17),"桌号:".$order['table']['number'],$this->LF,$this->GS."!".chr(0),"结账时间:".date('Y-m-d H:i:s',$order['pay_time']),$this->LF,"收银员:顾客/系统",$this->LF,"单号:".$order['oid'],$this->LF,$this->GS."!".chr(0),"--------------------------------","品项",$this->HT,$this->HT,"数量","原价","优惠价",$this->LF];if ($order['product']) {foreach ($order['product'] as $val) {$mc=$val['title'].'('.$val['sku_cn'].')';$add[] = $mc;$add[] = $this->HT;$add[] = $val['number'];$add[] = $this->HT;$add[] = $val['money'];$add[] = $this->HT;}}$add[] = "--------------------------------";$add[] = "菜品原价";$add[] = $this->HT;$add[] = $this->HT;$add[] = $this->HT;$add[] = $order['sum_price'];$add[] = $this->LF;$add[] = "订单原价";$add[] = $this->HT;$add[] = $this->HT;$add[] = $this->HT;$add[] = $order['sum_price'];$add[] = $this->LF;$add[] = $this->GS."!".chr(0);$add[] = "--------------------------------";$add[] = $this->LF;$mc = "支付方式";$this->setHt1($mc,$add);$add[] = $this->HT;$pay = $order['pay'];if (mb_strlen($pay) < 4) {$add[] = $this->HT;}$add[] = $pay;$add[] = $this->LF;$add[] = "应付金额";$add[] = $this->HT;$add[] = $this->HT;$add[] = $this->HT;$price = $order['money'];if (mb_strlen($price) < 4) {$add[] = $this->HT;}$add[] = $price;$add[] = $this->LF;$add[] = $this->GS."!".chr(0);$add[] = "--------------------------------";$add[] = $this->LF;$add[] = "下单人:顾客/系统";$add[] = $this->LF;$add[] = "下单时间:".date('Y-m-d H:i:s',$order['created_time']);$add[] = $this->LF;$add[] = "打印人:管理员";$add[] = $this->LF;$add[] = "打印时间:".date('Y-m-d H:i:s',time());$add[] = $this->LF;$add[] = "备注:".$order['remark'];$add[] = $this->ESC."d".chr(5);$add[] = $this->CUT."V".chr(0).chr(8);$add[] = $this->ESC."C".chr(3).chr(2).chr(3);foreach ($add as &$val) {fwrite($this->fp,$val);}}/*** USB打印60mm格式* @param $order* @param $usb_port*/public function printOrder4($order,$usb_port = 0) {$add = [$this->ESC."@",$this->GS."!".chr(17),$this->ESC."a".chr(1),$this->LF,'取餐号:'.$order['meal_code'],$this->LF,$order['shop']['shop_title'],$this->LF,$this->GS."!".chr(0),$this->GS."!".chr(0),"------------------------------------------------",$this->LF,$this->GS."!".chr(17),"桌号:".$order['table']['number'],$this->LF,$this->GS."!".chr(0),"结账时间:".date('Y-m-d H:i:s',$order['pay_time']),$this->LF,"收银员:顾客/系统",$this->LF,"单号:".$order['oid'],$this->LF,$this->GS."!".chr(0),"------------------------------------------------",$this->LF,$this->ESC."a".chr(0),"品项",$this->HT,$this->HT,$this->HT,"数量",$this->HT,"原价",$this->HT,"优惠价",$this->LF];if ($order['product']) {foreach ($order['product'] as $val) {$mc=$val['title'].'('.$val['sku_cn'].')';$str_length = mb_strlen($mc);$str_count = ceil($str_length / 7);if ($str_count > 0) {for ($i = 1;$i <= $str_count;$i ++) {$add[] = mb_substr($mc,($i - 1) * 7,7);if ($i == 1) {$add[] = $this->HT;$add[] = $this->HT;if ($str_length < 7 && $str_count == 1) {$add[] = $this->HT;}$add[] = $val['number'];$add[] = $this->HT;$add[] = $val['money'];$add[] = $this->HT;$add[] = $val['money'];$add[] = $this->LF;} else {$add[] = $this->HT;$add[] = $this->HT;$add[] = $this->HT;$add[] = $this->HT;$add[] = $this->LF;}}}}}$add[] = "------------------------------------------------";$add[] = $this->LF;$add[] = $this->ESC."a".chr(0);$add[] = "菜品原价";// $add[] = $this->HT;$add[] = $this->HT;$add[] = $this->HT;$add[] = $this->HT;$add[] = $this->HT;$add[] = $order['sum_price'];$add[] = $this->LF;$add[] = "订单原价";$add[] = $this->HT;$add[] = $this->HT;$add[] = $this->HT;$add[] = $this->HT;// $add[] = $this->HT;$add[] = $order['sum_price'];$add[] = $this->LF;$add[] = $this->GS."!".chr(0);$add[] = "------------------------------------------------";$add[] = $this->LF;$mc = "支付方式";$add[] = $mc;$add[] = $this->HT;$add[] = $this->HT;$add[] = $this->HT;$pay = $order['pay'];if (mb_strlen($pay) <= 4) {$add[] = $this->HT;}$add[] = $pay;$add[] = $this->LF;$add[] = "应付金额";$add[] = $this->HT;$add[] = $this->HT;$add[] = $this->HT;$add[] = $this->HT;$price = $order['money'];$add[] = $price;$add[] = $this->LF;$add[] = $this->GS."!".chr(0);$add[] = "------------------------------------------------";$add[] = $this->LF;$add[] = "下单人:顾客/系统";$add[] = $this->LF;$add[] = "下单时间:".date('Y-m-d H:i:s',$order['created_time']);$add[] = $this->LF;$add[] = "打印人:管理员";$add[] = $this->LF;$add[] = "打印时间:".date('Y-m-d H:i:s',time());$add[] = $this->LF;$add[] = "备注:".$order['remark'];$add[] = $this->ESC."d".chr(5);$add[] = $this->CUT."V".chr(0).chr(2);$add[] = $this->ESC."C".chr(3).chr(2).chr(3);foreach ($add as $val) {fwrite($this->fp,$val);}}
}