打印机池的设计与实现

打印机池的设计与实现

先说一个应用场景——中厨。

服务员先在点菜电脑上点菜,点好的菜单信息要在各出品部的“出品印机”上打印出来,各出品部的师傅再按照菜单来做菜。

不同的菜,要在不同的出品打印机上打印,有的菜要在多个出品打印机上打印。

整个中厨大概有十来个出品部门,有的出品部门有一台出品打印机,有的出口部门有几台。

笔者把这些打印机统称为“打印机池”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// 单元功用:打印相关
// 单元设计:cxg
// 设计日期:2014-05-12
// 每一台打印机都有自己的任务队列和处理任务队列的线程
// 快餐只有一个出品部门
 
unit untPrintTask;
 
interface
 
uses
  System.SysUtils, System.Classes,
  Datasnap.DBClient, frxclass,
  System.Generics.Collections;
 
type
  TBillContent = record // 小票内容
    machineNo: string; // POS机号
    skyName: string; // 收款员姓名
    saleNo: string; // 小票号
    saleTime: TDateTime; // 销售时间
    amount: Currency; // 应收
    pay: Currency; // 支付
    change: Currency; // 找零
    prnData: OleVariant; // 小票明细:商品名称、单价、数量、金额。。。。。。
    deskNo:string; // 台号
    payType: string; // 支付方式
  end;
 
type
  TPrinterInfo = record // 打印机信息
    prnNo: Integer; // 印机编号
    prnName: string; // 印机名称
    prnType: string; // 结账、厨打
    prnWidth: Integer; // 50mm\76mm\80mm
    remark: string; // 备注
    prnModel: string; // 打印模版
  end;
 
type
  TOneTimePrint = record // 一次打印
    printerInfo: TPrinterInfo; // 打印机信息
    billContent: TBillContent; // 小票内容
  end;
 
type
  TPrintTaskThread = class(TThread)  // 打印任务线程
  private
    FPrintQueue: TQueue<TOneTimePrint>;   // 打印队列
  protected
    procedure Execute; override;
  public
    constructor Create; overload;
    destructor Destroy; override;
    property PrintQueue: TQueue<TOneTimePrint> read FPrintQueue
      write FPrintQueue;
  end;
 
var
  g_PrintTasks: TDictionary<string, TPrintTaskThread>;  // <打印机名字, TPrintTask>
 
implementation
 
{ TPrintTask }
 
uses untFastReport, UntSysConst;
 
constructor TPrintTaskThread.Create;
begin
  Create(False);
  FreeOnTerminate := False;
  // 创建打印队列
  FPrintQueue := TQueue<TOneTimePrint>.Create;
end;
 
destructor TPrintTaskThread.Destroy;
begin
  // 释放打印队列
  FreeAndNil(FPrintQueue);
  inherited;
end;
 
procedure TPrintTaskThread.Execute;
var
  OneTimePrint: TOneTimePrint;
  dm: TdmFastReport;
  c: TfrxComponent;
begin
  while not Self.Terminated do
  begin
    if Assigned(FPrintQueue) and (FPrintQueue.Count > 0) then
    begin
      // 从任务队列中提取一个任务
      OneTimePrint := FPrintQueue.Dequeue;
      dm := TdmFastReport.Create(nil);
      try
        try
          // 小票明细数据
          dm.cds.Data := OneTimePrint.billContent.prnData;
          // 小票模板
          dm.report.LoadFromFile(OneTimePrint.printerInfo.prnModel);
          // 哪个打印机
          dm.report.PrintOptions.Printer := OneTimePrint.printerInfo.prnName;
          // 变量赋值
 
          c:=dm.report.FindObject('mmShopName');
          if c<>nil then
            TfrxMemoView(c).Memo.Text := UserInfo.ShopName;
 
          c := dm.report.FindObject('mmMachineNo');
          if c<>nil then
            TfrxMemoView(c).Memo.Text := OneTimePrint.billContent.machineNo;
 
          c:=dm.report.FindObject('mmSKY');
          if c<>nil then
            TfrxMemoView(c).Memo.Text := OneTimePrint.billContent.skyName;
 
          c:= dm.report.FindObject('mmBillNo');
          if c<>nil then
            TfrxMemoView(c).Memo.Text := OneTimePrint.billContent.saleNo;
 
          c:=dm.report.FindObject('mmSaleTime');
          if c<>nil then
            TfrxMemoView(c).Memo.Text := FormatDateTime('yyyy-mm-dd hh:nn',OneTimePrint.billContent.saleTime);
 
          c:= dm.report.FindObject('mmDeskNo');
          if c<>nil then
            TfrxMemoView(c).Memo.Text := OneTimePrint.billContent.deskNo;  // 台号
 
          c:= dm.report.FindObject('mmPayType');
          if c<>nil then
            TfrxMemoView(c).Memo.Text := OneTimePrint.billContent.payType;
 
          c:= dm.report.FindObject('mmAmount');
          if c<> nil then
            TfrxMemoView(c).Memo.Text := FormatCurr('0.00', OneTimePrint.billContent.amount);
 
          c:=dm.report.FindObject('mmPay');
          if c<>nil then
            TfrxMemoView(c).Memo.Text := FormatCurr('0.00', OneTimePrint.billContent.pay);
 
          c:= dm.report.FindObject('mmGiveChange');
          if c<> nil then
            TfrxMemoView(c).Memo.Text := FormatCurr('0.00', OneTimePrint.billContent.change);
 
          // 开始打印
          dm.report.PrepareReport();
          dm.report.Print;
        except
          // 打印失败,重新加入任务队列
          Self.FPrintQueue.Enqueue(OneTimePrint);
        end;
      finally
        FreeAndNil(dm);
      end;
    end;
    // 线程休眠
    Sleep(1000);
  end;
end;
 
end.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
procedure TfrmSettleAccount.PrintBill;
var
  OneTimePrint: TOneTimePrint;
  p: TPrintTaskThread;
begin
  // 结帐打印机和出品打印机都打印相同的模板
  frmPos.cdsPrinter.First;
  while not frmPos.cdsPrinter.Eof do
  begin
    // 打印机信息
    OneTimePrint.printerInfo.prnNo := frmPos.cdsPrinter.FieldByName('prnNo')
      .AsInteger;
    OneTimePrint.printerInfo.prnName := frmPos.cdsPrinter.FieldByName
      ('prnName').Text;
    OneTimePrint.printerInfo.prnType := frmPos.cdsPrinter.FieldByName
      ('prnType').Text;
    OneTimePrint.printerInfo.prnWidth := UserInfo.PaperWidth;
    OneTimePrint.printerInfo.remark := frmPos.cdsPrinter.FieldByName
      ('remark').Text;
    // 不同宽度的打印机加载不同的打印模板
    case OneTimePrint.printerInfo.prnWidth of
      58:
        OneTimePrint.printerInfo.prnModel :=
          ExtractFilePath(Application.ExeName) + 'report\pos58.fr3';
      76:
        OneTimePrint.printerInfo.prnModel :=
          ExtractFilePath(Application.ExeName) + 'report\pos76.fr3';
      80:
        OneTimePrint.printerInfo.prnModel :=
          ExtractFilePath(Application.ExeName) + 'report\pos80.fr3';
    end;
    // 小票内容
    OneTimePrint.billContent.machineNo := UserInfo.MachineId;
    OneTimePrint.billContent.skyName := UserInfo.UserName;
    OneTimePrint.billContent.saleNo := UserInfo.saleNo;
    OneTimePrint.billContent.saleTime := Now;
    OneTimePrint.billContent.amount := self.Total.totalAmount;
    OneTimePrint.billContent.pay := self.Total.totalAmount;
    OneTimePrint.billContent.Change := StrToCurr(edtChange.Text);
    OneTimePrint.billContent.prnData := frmPos.cdsSaleD.Data;
    OneTimePrint.billContent.payType := frmPos.cdsSaleM.FieldByName
      ('payType').Text;
    OneTimePrint.billContent.deskNo := frmPos.edtDeskNo.Text;
    // 加入打印队列
    if g_PrintTasks.TryGetValue(frmPos.cdsPrinter.FieldByName('prnName').Text, p)
    then
    begin
      p.PrintQueue.Enqueue(OneTimePrint);
    end;
    frmPos.cdsPrinter.Next;
  end;
end;

  

posted @   delphi中间件  阅读(1112)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示