佳博打印机demo

uGprinterNet类
基于delphi12

点击查看代码
unit uGprinterNet;

interface

uses
  System.SysUtils, System.Classes, System.Types, System.DateUtils, IdTCPClient,
  IdGlobal, Winapi.Windows, System.Math, IdException;

type
  // 打印机状态枚举
  TPrinterStatus = (psNormal, psPaperOut, psCoverOpen, psUnknownError);

type
  // 二维码纠错等级枚举,对应手册n值
  TQRErrorLevel = (qrL = 48, qrM = 49, qrQ = 50, qrH = 51);

  TGpNetPrinter = class(TObject)
  private
    FTcp: TIdTCPClient;
    FSendBuffer: TIdBytes;
    FGBEnc: TEncoding; // 固定GB2312编码缓存
    FStatusRetryInterval: Cardinal; // 状态轮询间隔(ms)
    FStatusTimeout: Cardinal;        // 状态检测总超时(ms)

    procedure AppendByte(AByte: Byte);
    procedure AppendBytes(const Data: TBytes);
    procedure FlushSendBuffer;
    function ReadPrinterStatus: TPrinterStatus; // 读取硬件状态
    function WaitPrinterReady: Boolean;         // 阻塞等待打印机就绪
    function QueryPrinterInfo(QueryCode: Byte): string;
  public
    constructor Create;
    destructor Destroy; override;

    // 网络连接
    procedure Connect(const AHost: string; APort: Word = 9100);
    procedure Disconnect;
    function IsConnected: Boolean;
    function GetPrintBrand: string;
    function GetPrintModel: string;
    // 原有兼容接口
    procedure SendCmd(const ACmd: string);
    procedure SendBytes(const Data: TBytes);

    // 打印样式扩展
    procedure SetCharScale(W, H: Integer);
    procedure SetBold(Enable: Boolean);
    procedure SetDoubleWidth(Enable: Boolean);
    procedure SetDoubleHeight(Enable: Boolean);
    procedure SetQuadBigFont(Enable: Boolean);
    procedure AlignCenter;
    procedure AlignLeft;
    procedure FeedLines(ALines: Integer);
    procedure PrintLine;
    procedure CutPaperFull;
    procedure CutPaperHalf;
    procedure Beep(const ANumber: Integer = 2; TimeCoeff: Byte = 2);
     ///步骤1:设置QR码单个模块点阵大小 <param name="DotSize">模块尺寸 1~9,默认3</param>
    procedure SetQRModuleSize(DotSize: Byte = 3);
   /// 步骤2:设置QR码纠错等级
    procedure SetQRErrorLevel(Level: TQRErrorLevel = qrM);
   /// 步骤3+4:写入内容并打印二维码(整合一键调用) <param name="QRText">二维码存储文本内容</param>
    procedure PrintQRCode(const QRText: string);
    // 状态配置
    procedure SetStatusWaitParam(ARetryMs, ATimeoutMs: Cardinal);
    //查询状态
    function GetPrinterCurrentStatus: TPrinterStatus;
    // 打印主入口:自动检测状态,异常阻塞等待恢复后再打印
    function ExecutePrint: Boolean;
    function ExecutePrintSimple: Boolean; //不检测状态直接打
  end;

implementation

{ TGpNetPrinter }

constructor TGpNetPrinter.Create;
begin
  inherited;
  FTcp := TIdTCPClient.Create(nil);
  SetLength(FSendBuffer, 0);   //初始化空打印缓冲
  FGBEnc := TEncoding.GetEncoding('GB2312');
  // 默认等待参数:间隔300ms,总超时10秒
  FStatusRetryInterval := 300;          //默认轮询间隔
  FStatusTimeout := 10000;
end;

destructor TGpNetPrinter.Destroy;
begin
  Disconnect;
  FTcp.Free;
  FGBEnc.Free;
  SetLength(FSendBuffer, 0);
  inherited;
end;

procedure TGpNetPrinter.SetStatusWaitParam(ARetryMs, ATimeoutMs: Cardinal);
begin
  FStatusRetryInterval := ARetryMs;
  FStatusTimeout := ATimeoutMs;
end;

procedure TGpNetPrinter.Connect(const AHost: string; APort: Word = 9100);
begin
  Disconnect;
  FTcp.Host := AHost;
  FTcp.Port := APort;
  FTcp.Connect;

  FTcp.IOHandler.ReadTimeout := 1000;
end;

procedure TGpNetPrinter.Disconnect;
begin
  if FTcp.Connected then
    FTcp.Disconnect;
end;

function TGpNetPrinter.IsConnected: Boolean;
begin
  Result := FTcp.Connected;
end;

procedure TGpNetPrinter.PrintLine;
begin
  SendCmd(#10 + '------------------------------------------------');   //#10换行
end;

procedure TGpNetPrinter.PrintQRCode(const QRText: string);
var
  DataBuf: TBytes;
  TotalLen, pL, pH: Word;
  I: Integer;
begin
 //转字节数组
  DataBuf := TEncoding.UTF8.GetBytes(QRText);
  // k = 总长度 -3;总长度 = 文本字节数 + 3
  TotalLen := Length(DataBuf) + 3;
  pL := TotalLen and $FF;       // 低位
  pH := (TotalLen shr 8) and $FF; // 高位
    // ====== 步骤3:Function180 存储二维码数据 GS ( K pL pH 49 80 48 + 数据 ======
  AppendByte($1D);
  AppendByte($28);
  AppendByte($6B);
  AppendByte(Byte(pL));
  AppendByte(Byte(pH));
  AppendByte($31); // cn=49
  AppendByte($50); // fn=80
  AppendByte($30); // m=48
  // 追加全部文本字节
  for I := Low(DataBuf) to High(DataBuf) do
    AppendByte(DataBuf[I]);

  // ====== 步骤4:Function181 打印已存储的二维码 ======
  AppendByte($1D);
  AppendByte($28);
  AppendByte($6B);
  AppendByte($03);
  AppendByte($00);
  AppendByte($31);
  AppendByte($51); // fn=81
  AppendByte($30); // m=48
end;

function TGpNetPrinter.QueryPrinterInfo(QueryCode: Byte): string;
var
  RecvBuf: TIdBytes;
begin
  Result := '';
  if not IsConnected then
 exit  ;
  SetLength(FSendBuffer, 0);
    // 下发 GS I n  1D 49 n
  AppendByte($1D);
  AppendByte($49);
  AppendByte(QueryCode);
  FTcp.IOHandler.Write(FSendBuffer);

    //读取
  FTCP.IOHandler.ReadBytes(RecvBuf, -1, False);
  Result := Trim(TEncoding.ASCII.GetString(RecvBuf));
  SetLength(FSendBuffer, 0);
end;

procedure TGpNetPrinter.AppendByte(AByte: Byte);
var
  OldLen: Integer;
begin
  OldLen := Length(FSendBuffer);
  SetLength(FSendBuffer, OldLen + 1);
  FSendBuffer[OldLen] := AByte;
end;

procedure TGpNetPrinter.AppendBytes(const Data: TBytes);
var
  SrcIdBuf: TIdBytes;
  OldLen: Integer;
begin
  if Length(Data) = 0 then
    Exit;
  SrcIdBuf := TIdBytes(Data);
  OldLen := Length(FSendBuffer);
  SetLength(FSendBuffer, OldLen + Length(SrcIdBuf));
  Move(SrcIdBuf[0], FSendBuffer[OldLen], Length(SrcIdBuf));
end;

procedure TGpNetPrinter.Beep(const ANumber: Integer = 2; TimeCoeff: Byte = 2);
var
  bTimes, bCoeff: Byte;
begin
  if (ANumber < 1) or (ANumber > 9) then
    bTimes := 2
  else
    bTimes := Byte(ANumber);

  // 限制时长系数 1~9
  if (TimeCoeff < 1) or (TimeCoeff > 9) then
    bCoeff := 2
  else
    bCoeff := TimeCoeff;
  AppendByte($1B);
  AppendByte($42);
  AppendByte(bTimes);
  AppendByte(bCoeff);

end;

procedure TGpNetPrinter.FlushSendBuffer;
begin

  if not IsConnected then
    Exit;
  if Length(FSendBuffer) = 0 then
    Exit;
  FTcp.IOHandler.Write(FSendBuffer);
  SetLength(FSendBuffer, 0);
end;

function TGpNetPrinter.GetPrintBrand: string;
begin
  Result := QueryPrinterInfo(66);
end;

function TGpNetPrinter.GetPrinterCurrentStatus: TPrinterStatus;
begin
  result := ReadPrinterStatus();
end;

function TGpNetPrinter.GetPrintModel: string;
begin
  Result := QueryPrinterInfo(67);
end;

procedure TGpNetPrinter.SendCmd(const ACmd: string);
var
  TempBuf: TBytes;
begin
  if ACmd.IsEmpty then
    Exit;
  TempBuf := FGBEnc.GetBytes(ACmd);
  AppendBytes(TempBuf);
end;

procedure TGpNetPrinter.SendBytes(const Data: TBytes);
begin
  AppendBytes(Data);
end;

// 加粗 1B 45 n
procedure TGpNetPrinter.SetBold(Enable: Boolean);
begin
  AppendByte($1B);
  AppendByte($45);
  AppendByte(Ord(Enable));
end;

procedure TGpNetPrinter.SetCharScale(W, H: Integer);
var
  n: Byte;
begin
  // 边界限制,防止超出手册1~8范围
  if W < 1 then
    W := 1;
  if W > 8 then
    W := 8;
  if H < 1 then
    H := 1;
  if H > 8 then
    H := 8;
  // 计算掩码n
  n := (W shl 4) or H;
  // 发送GS ! 指令
  AppendByte($1D);
  AppendByte($21);
  AppendByte(n);

end;

// 倍宽
procedure TGpNetPrinter.SetDoubleWidth(Enable: Boolean);
begin
  AppendByte($1D);
  AppendByte($21);
  AppendByte(IfThen(Enable, $11, $00));
end;

procedure TGpNetPrinter.SetQRErrorLevel(Level: TQRErrorLevel);
var
  n: Byte;
begin
  n := Byte(Level);
  AppendByte($1D);
  AppendByte($28);
  AppendByte($6B);
  AppendByte($03);
  AppendByte($00);
  AppendByte($31);
  AppendByte($45); // fn=69
  AppendByte(n);
end;

procedure TGpNetPrinter.SetQRModuleSize(DotSize: Byte);
var
  n: Byte;
begin
  if DotSize < 1 then
    n := 1
  else if DotSize > 9 then
    n := 9
  else
    n := DotSize;
  AppendByte($1D);
  AppendByte($28);
  AppendByte($6B);
  AppendByte($03);
  AppendByte($00);
  AppendByte($31);
  AppendByte($43);  //FN=67
  AppendByte(n);
end;

procedure TGpNetPrinter.SetQuadBigFont(Enable: Boolean);
var
  n: Byte;
begin
  if Enable then
    n := 1
  else
    n := 0;
  AppendByte($1C);
  AppendByte($57);
  AppendByte(n);
end;

// 倍高
procedure TGpNetPrinter.SetDoubleHeight(Enable: Boolean);
begin
  AppendByte($1D);
  AppendByte($21);
  AppendByte(IfThen(Enable, $10, $00));
end;

// 居中
procedure TGpNetPrinter.AlignCenter;
begin
  AppendByte($1B);
  AppendByte($61);
  AppendByte(1);
end;

// 左对齐
procedure TGpNetPrinter.AlignLeft;
begin
  AppendByte($1B);
  AppendByte($61);
  AppendByte(0);
end;

// 走纸N行
procedure TGpNetPrinter.FeedLines(ALines: Integer);
var
  I: Integer;
begin
  for I := 1 to ALines do
    AppendByte($0A);
end;

// 全切
procedure TGpNetPrinter.CutPaperFull;
begin
  AppendByte($1D);
  AppendByte($56);
  AppendByte($41);
  AppendByte(0);
end;

// 半切
procedure TGpNetPrinter.CutPaperHalf;
begin
  AppendByte($1D);
  AppendByte($56);
  AppendByte($42);
  AppendByte(0);
end;

{ 核心:读取打印机硬件状态 GS r 0 / GS r 1 }
function TGpNetPrinter.ReadPrinterStatus: TPrinterStatus;
var
  SendCmd: TIdBytes;
  RecvBuf: TIdBytes; // 专门用来接收返回字节
  RespByte: Byte;
begin
  Result := psNormal;      //超时放行打印。。
  if not IsConnected then
    Exit;
  try
    // 1. 清空旧打印残留缓冲(关键修复)
    FTcp.IOHandler.InputBuffer.Clear;
       // 发送读取打印机状态指令:GS r 0 ($1D $72 $00)
    SetLength(SendCmd, 3);
    SendCmd[0] := $10; // DLE
    SendCmd[1] := $04; // EOT
    SendCmd[2] := $02; // n=2 脱机/错误状态
    FTcp.IOHandler.Write(SendCmd);
    Sleep(50); // 微小延时,等待打印机回传字节

    // 读取1字节状态返回
    SetLength(RecvBuf, 0);
    FTcp.IOHandler.ReadBytes(TIdBytes(RecvBuf), 1, False);

    if Length(RecvBuf) >= 1 then
    begin
      RespByte := RecvBuf[0];
      // Bit2=4:纸仓开盖  1=开盖
      if (RespByte and 4) <> 0 then
      begin
        Result := psCoverOpen;
        Exit;

      end;
     // Bit5=32:缺纸 1=缺纸
      if (RespByte and 32) <> 0 then
      begin
        Result := psPaperOut;
        Exit;

      end;
      // 无异常
      Result := psNormal;
    end;
  except
    // 捕获所有Indy网络超时/断连异常
    on E: EIdException do
    begin
      Result := psUnknownError;
    end;

  end;

end;

{ 阻塞等待打印机就绪,超时返回False }
function TGpNetPrinter.WaitPrinterReady: Boolean;
var
  StartTick: Cardinal;
  CurStatus: TPrinterStatus;
begin
  Result := False;
  if not IsConnected then
    Exit;

  StartTick := GetTickCount;
  while GetTickCount - StartTick < FStatusTimeout do
  begin
    CurStatus := ReadPrinterStatus;
    case CurStatus of
      psNormal:
        begin
          Result := True;
          Break;
        end;
      psPaperOut, psCoverOpen:
        begin
          // 缺纸/开盖,等待指定间隔后重试
          Sleep(FStatusRetryInterval);
        end;
      psUnknownError:
        begin
          Sleep(FStatusRetryInterval);
        end;
    end;
  end;
end;

{ 打印主流程:先等设备就绪,再一次性发送缓冲 }
function TGpNetPrinter.ExecutePrint: Boolean;
begin
  Result := False;
  if not IsConnected then
    Exit;
  if Length(FSendBuffer) = 0 then
  begin
    Result := True;
    Exit;
  end;

  // 阻塞等待打印机恢复正常(缺纸/开盖会循环等待,直到超时)
  if not WaitPrinterReady then
    Exit;

  // 状态正常,执行发送
  FlushSendBuffer;
  Result := True;
end;

function TGpNetPrinter.ExecutePrintSimple: Boolean;
begin
  Result := False;
  if not IsConnected then
    Exit;
  if Length(FSendBuffer) = 0 then
  begin
    Result := True;
    Exit;
  end;
  try
    FlushSendBuffer;
    Result := True;
  except
    Result := False;
  end;
end;

end.

窗口设计:

点击查看代码
unit uJiaboDemo;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  Vcl.StdCtrls, uGprinterNet, IdBaseComponent, IdComponent, IdTCPConnection,
  IdTCPClient, IdGlobal;

type
  TForm2 = class(TForm)
    Edit1: TEdit;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    Memo1: TMemo;
    Button5: TButton;
    Label1: TLabel;
    Label2: TLabel;
    Edit2: TEdit;
    procedure FormDestroy(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;
  AGprinterNet: TGpNetPrinter;

implementation

{$R *.dfm}

procedure TForm2.Button1Click(Sender: TObject);
var
  Sta: TPrinterStatus;
  Tip: string;
begin
  if not Assigned(AGprinterNet) then
  begin
    AGprinterNet := TGpNetPrinter.Create;
  end;
  AGprinterNet.Connect(Edit1.Text, 9100);
  if not AGprinterNet.IsConnected then
  begin
    ShowMessage('未连接打印机');
    Exit;
  end;
  Sta := AGprinterNet.GetPrinterCurrentStatus;
  case Sta of
    psNormal:
      Tip := '打印机状态:正常,有纸、盖子闭合';
    psPaperOut:
      Tip := '打印机状态:缺纸,请加装打印纸';
    psCoverOpen:
      Tip := '打印机状态:纸仓盖子未关好';
    psUnknownError:
      Tip := '打印机无响应/网络断开';
  end;

  AGprinterNet.Beep;
  AGprinterNet.ExecutePrint;
  ShowMessage(Tip);
  AGprinterNet.Disconnect;
end;

procedure TForm2.Button2Click(Sender: TObject);
begin
  if not Assigned(AGprinterNet) then
  begin
    AGprinterNet := TGpNetPrinter.Create;
  end;
   // 3. 没连接才连接(不要每次打印都重连)
  if not AGprinterNet.IsConnected then
  begin
    try
      AGprinterNet.Connect(edit1.Text, 9100);
    except
      on E: Exception do
      begin
        ShowMessage('连接打印机失败:' + E.Message);
        Exit;
      end;

    end;
  end;

  if Trim(Edit2.Text) = '' then
  begin
    ShowMessage('请输入打印内容');
    Exit;
  end;

  if not AGprinterNet.IsConnected then
  begin
    ShowMessage('打印机连接失败,请检查IP与网线');
    Exit;
  end;

//  ShowMessage('连接成功,正在打印...');

  try
    //1. 拼接打印内容(仅存入本地缓冲,不发包)
    AGprinterNet.SetCharScale(4, 4);   //倍宽
    AGprinterNet.SendCmd(Edit2.Text + '四倍超大' + #10);
    AGprinterNet.SetCharScale(3, 3);   //倍宽
    AGprinterNet.SendCmd(Edit2.Text + '3倍超大' + #10);
    AGprinterNet.SetCharScale(2, 2);   //倍宽
    AGprinterNet.SendCmd(Edit2.Text + '二倍超大' + #10);
    AGprinterNet.SetCharScale(1, 1);
    AGprinterNet.PrintLine;
    AGprinterNet.SetCharScale(1, 4);   //倍宽
    AGprinterNet.SendCmd(Edit2.Text + '四倍超大' + #10);
    AGprinterNet.SetCharScale(1, 3);   //倍宽
    AGprinterNet.SendCmd(Edit2.Text + '3倍超大' + #10);
    AGprinterNet.SetCharScale(1, 2);   //倍宽
    AGprinterNet.SendCmd(Edit2.Text + '二倍超大' + #10);
    AGprinterNet.SetCharScale(1, 1);
    AGprinterNet.PrintLine;
    AGprinterNet.SetCharScale(4, 1);   //倍宽
    AGprinterNet.SendCmd(Edit2.Text + '四倍超大' + #10);
    AGprinterNet.SetCharScale(3, 1);   //倍宽
    AGprinterNet.SendCmd(Edit2.Text + '3倍超大' + #10);
    AGprinterNet.SetCharScale(2, 1);   //倍宽
    AGprinterNet.SendCmd(Edit2.Text + '二倍超大' + #10);
    AGprinterNet.SetCharScale(1, 1);

    AGprinterNet.PrintLine;
    AGprinterNet.SetDoubleWidth(false);   //倍宽
    AGprinterNet.SetDoubleWidth(True);   //倍宽
    AGprinterNet.SendCmd(Edit2.Text + '倍宽' + #10);
    AGprinterNet.SetDoubleWidth(false);   //倍宽
    AGprinterNet.SetDoubleHeight(True); //倍高
    AGprinterNet.SendCmd(Edit2.Text + '倍高' + #10);
    AGprinterNet.SetDoubleHeight(false); //倍高
    AGprinterNet.SetBold(True);         //加粗
    AGprinterNet.SendCmd(Edit2.Text + '加粗' + #10);
    AGprinterNet.SetBold(false);         //加粗
    AGprinterNet.SendCmd(Edit2.Text + '正常' + #10);
    AGprinterNet.Beep;

    // 2. 执行打印:内部自动检测打印机状态、发送全部数据
    if AGprinterNet.ExecutePrint then

    else
      ShowMessage('打印机异常(缺纸/开盖/超时),打印失败');

    // 关键:延迟断开,给TCP足够时间把数据发给打印机
    Sleep(500);
  except
    on E: Exception do
    begin
      ShowMessage('打印出错:' + E.Message);
    end;
  end;

  // 全部流程结束后再断开
  AGprinterNet.Disconnect;

end;

procedure TForm2.Button3Click(Sender: TObject);
begin
  if not Assigned(AGprinterNet) then
  begin
    AGprinterNet := TGpNetPrinter.Create;
  end;
   // 3. 没连接才连接(不要每次打印都重连)
  if not AGprinterNet.IsConnected then
  begin
    try
      AGprinterNet.Connect(Edit1.Text, 9100);
    except
      on E: Exception do
      begin
        ShowMessage('连接打印机失败:' + E.Message);
        Exit;
      end;

    end;
  end;

  AGprinterNet.CutPaperFull;   //只做本地拼接,不发送。
  AGprinterNet.ExecutePrint;   //必须加这行,本地拼接完成一并发送。
  AGprinterNet.Disconnect;
end;

procedure TForm2.Button4Click(Sender: TObject);
begin
  if not Assigned(AGprinterNet) then
  begin
    AGprinterNet := TGpNetPrinter.Create;
  end;
   // 3. 没连接才连接(不要每次打印都重连)
  if not AGprinterNet.IsConnected then
  begin
    try
      AGprinterNet.Connect('192.168.0.55', 9100);
    except
      on E: Exception do
      begin
        ShowMessage('连接打印机失败:' + E.Message);
        Exit;
      end;

    end;
  end;
  AGprinterNet.SetCharScale(2, 2);
  AGprinterNet.SendCmd('订单二维码');
  AGprinterNet.SetCharScale(1, 1);
  AGprinterNet.FeedLines(1);
 // 2. 配置二维码参数(只需修改时调用,全局生效)
  AGprinterNet.SetQRModuleSize(6);    // 模块6点阵,中等大小
  AGprinterNet.SetQRErrorLevel(qrM); // 纠错M级,平衡容量与容错
// 3. 写入并打印二维码内容
  AGprinterNet.PrintQRCode(edit2.Text);
// 走纸+切纸
  AGprinterNet.FeedLines(3);
  AGprinterNet.CutPaperFull;
  AGprinterNet.ExecutePrint;
end;

procedure TForm2.Button5Click(Sender: TObject);
var
  Brand, Model: string;
begin
  if not Assigned(AGprinterNet) then
  begin
    AGprinterNet := TGpNetPrinter.Create;
  end;
   // 3. 没连接才连接(不要每次打印都重连)
  if not AGprinterNet.IsConnected then
  begin
    try
      AGprinterNet.Connect(Edit1.Text, 9100);
    except
      on E: Exception do
      begin
        ShowMessage('连接打印机失败:' + E.Message);
        Exit;
      end;

    end;
  end;
  Brand := AGprinterNet.GetPrintBrand;
  Model := AGprinterNet.GetPrintModel;

  ShowMessage('打印机品牌:'+Brand + #10+'型号: ' + Model);
end;

procedure TForm2.FormDestroy(Sender: TObject);
begin
  AGprinterNet.Free;
end;

end.

posted @ 2026-06-30 16:15  沂水蓝海  阅读(5)  评论(0)    收藏  举报