佳博/芯桦打票机对接(含USB和网口)
记录一下最近实现的一个需求,用PHP远程连接芯桦、佳博打票机,代码还未进行优化,基本功能已经实现打印,具体打印指令需要查看佳博/芯桦的打票机指令,USB口打印质量需要其他语言配合,我这边用到了.net编写USB口的接收指令程序,有兴趣了解的可以联系一起学习探讨。
遇到一些雷点:
-
比如如何保证极端环境下,不漏单,可以使用佳博/芯桦打票机的一个回应的指令判断,但是值得注意的是,需要有一些判断网络连接的措施,比如可以有超时的机制,出现超时、指令异常的情况,放入队列等待重新发送指令。
-
内网穿透的问题,因为商家打票机处于一个IP内的局域网环境,所以需要使用到内网穿透,一开始我们采用的是NAT123,后面发现这个是个大坑啊!!! 我了个槽,不仅转发极其缓慢,偶尔还有时候会转发出错。
然后后面果断改变策略,使用了一个frp的工具,把转发交给了自家的服务器,果然快了很多!
基本上都是秒出单的(也有可能是暂时单少),frp搭建和使用可能后面有时间会写,暂时太忙了,没什么时间写,不过网上也一堆教程,有需要可以去看看。 -
网口和USB口的区别,网口的话芯桦和佳博默认端口都是9100,设备都会有网卡,只需要在frp做内网穿透的时候,绑定转发到这个端口就行了。USB口的话,需要编写接收程序去驱动这个USB串口,至于方法的话,有很多种,有其他语言基础的小伙伴可以看看佳博官方的demo,不过值得注意的就是demo仅供参考。。。最终的实现方案,还有容错机制还是要自己去实现的。。
-
商家设备问题,遇到一个客如云的商家,安卓系统,原本想要做成APP去做驱动USB串口,没想到客如云直接不让你安装第三方未授权APP(简直流氓!),然后此类的商家的话,只能是找客如云合作,或者购买其他的打票设备了。windows收银机的话,.net是可以编写接收指令和驱动串口的,开机直接后台启动,当然容错机制和LOG也是要有的,自己注意就好!不跑题了,官方的其他方法没试过,有条件的小伙伴可以自行尝试。
-
接收指令的问题,刚开始做打票机指令接收的时候,发现指令断断续续的,有时候还会有丢数据的情况出现,这个问题我不知道是不是网络的粘包问题,有时候接收的指令都是不全的,出现此问题可以尝试分段读取流,或者尝试流传输时带长度,然后接受时校验,然后容错那里也可以加上佳博/芯桦打票机的一个打印后返回指令,如果不正常,就通知服务器发送端,后面尝试重新发送指令。
最后说说:
-
这次需求比较着急,实现的也比较仓促,从0到1,代码还没时间去做深层的优化,写的不好不要喷我,哈哈哈。代码仅供参考!!!
-
有类似需求,但是不懂的,也可以私信联系,大家可以一起探讨,虽然不是最好的,但是也有一些自己的经验、见解。希望可以帮到大家。
-
还有就是希望看到这里的码友,在平常实现需求的过程中,不要轻言放弃,虽然问题会一个接着一个,但总会有方法破浪前行的,以此共勉!
<?php
class Printer
{
protected $ESC="\x1b";//设置
protected $GS="\x1d";//页面设置 字体大小17倍数
protected $HT="\x09";//跳格设置 9,17,25,33,41
protected $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);
}
}
}