在自动化生产线中,标签打印系统是不可或缺的一环。想象一下,每天有成千上万个产品需要贴上包含生产日期、批次号、条形码等信息的标签,如果全靠人工操作,不仅效率低下,还容易出错。这就是为什么我们需要开发一个智能的标签打印解决方案。
我遇到过这样一个真实案例:某汽车零部件厂商的生产线上,工人需要手动输入产品序列号并打印标签,经常出现打印位置偏移、内容错误等问题。后来我们改用自动化标签打印系统后,错误率直接降为零,生产效率提升了3倍。
这种系统通常需要满足几个核心需求:
开发可视化设计器,我推荐基于开源的Smart.FormDesigner进行二次开发。这个框架已经实现了基础的拖拽功能,我们可以在此基础上扩展标签打印所需的特殊控件。
首先创建一个WinForm项目,引用必要的DLL。核心类需要继承FormDesignerControl:
csharp复制public class LabelDesigner : FormDesignerControl
{
public LabelDesigner()
{
// 初始化设计器
this.DesignerHost.AddService(typeof(IMenuCommandService),
new MenuCommandService(this.DesignerHost));
}
}
标签打印需要一些特殊控件,这些是标准WinForm没有的:
csharp复制public class BarcodeControl : Control
{
public BarcodeType Type { get; set; } = BarcodeType.Code128;
public string BarcodeValue { get; set; }
protected override void OnPaint(PaintEventArgs e)
{
// 使用ZXing.Net等库生成条形码图像
var writer = new BarcodeWriter {
Format = BarcodeFormat.CODE_128,
Options = new EncodingOptions { Height = this.Height - 10 }
};
var barcodeBitmap = writer.Write(BarcodeValue);
e.Graphics.DrawImage(barcodeBitmap, new Point(0, 5));
}
}
csharp复制public class DynamicLabel : Label
{
public string FieldName { get; set; }
// 其他自定义属性...
}
为了让设计体验更流畅,我们需要添加一些实用功能:
这些细节看似不起眼,但实际使用中能大幅提升设计效率。我曾在项目中忽略了撤销功能,结果用户误操作后无法回退,不得不重新设计整个模板,教训深刻。
设计好的模板需要保存为结构化数据。XML是个不错的选择,既方便阅读又能完整保存控件属性:
xml复制<LabelTemplate PageSize="A4" Orientation="Portrait">
<Controls>
<BarcodeControl Name="barcode1" X="50" Y="20"
Width="200" Height="80" BarcodeType="Code128"/>
<DynamicLabel Name="prodName" X="50" Y="110"
Text="产品名称" Font="Arial,12" />
</Controls>
</LabelTemplate>
对应的序列化代码:
csharp复制public void SaveTemplate(string filePath)
{
var serializer = new XmlSerializer(typeof(LabelTemplate));
using (var writer = new StreamWriter(filePath))
{
serializer.Serialize(writer, this.CurrentTemplate);
}
}
打印的核心是PrintDocument类,关键是要在PrintPage事件中精确绘制每个元素:
csharp复制private void PrintDocument_PrintPage(object sender, PrintPageEventArgs e)
{
float dpiX = e.Graphics.DpiX;
float dpiY = e.Graphics.DpiY;
// 计算缩放比例(设计时是96dpi)
float scaleX = dpiX / 96;
float scaleY = dpiY / 96;
foreach (var control in Template.Controls)
{
var bounds = new Rectangle(
(int)(control.X * scaleX),
(int)(control.Y * scaleY),
(int)(control.Width * scaleX),
(int)(control.Height * scaleY));
if (control is BarcodeControl barcode)
{
DrawBarcode(e.Graphics, barcode, bounds);
}
// 其他控件类型...
}
}
这里有个坑要注意:不同打印机的DPI可能不同,必须进行缩放计算,否则打印位置会偏移。我曾经因此浪费了上百张标签纸才找到原因。
要让LabVIEW调用我们的打印功能,最简单的方式是将核心逻辑封装成类库:
csharp复制[ComVisible(true)]
[Guid("...")]
public class LabelPrinter
{
public void Print(string templatePath, Dictionary<string, string> fields)
{
// 加载模板并替换动态字段
// 执行打印...
}
}
注册DLL后,LabVIEW可以通过"调用库函数节点"直接调用:
对于更松耦合的系统,可以采用TCP通信。C#端创建Socket服务:
csharp复制TcpListener listener = new TcpListener(IPAddress.Any, 8080);
listener.Start();
while (true)
{
var client = listener.AcceptTcpClient();
ThreadPool.QueueUserWorkItem(HandleClient, client);
}
void HandleClient(object state)
{
var client = (TcpClient)state;
using (var stream = client.GetStream())
{
// 接收JSON格式的打印请求
var reader = new StreamReader(stream);
var request = JsonConvert.DeserializeObject<PrintRequest>(
reader.ReadToEnd());
// 执行打印...
}
}
LabVIEW端使用"TCP Open Connection"和"TCP Write"发送打印指令。这种方式的好处是打印服务可以独立部署在任何机器上。
动态标签的关键是数据替换,我推荐使用类似Mustache的模板语法:
csharp复制public string ReplaceFields(string text, Dictionary<string, string> data)
{
var regex = new Regex(@"\{\{(\w+)\}\}");
return regex.Replace(text, match =>
{
var key = match.Groups[1].Value;
return data.TryGetValue(key, out var value) ? value : match.Value;
});
}
这样在模板中可以这样定义动态字段:"{{productName}}",调用时传入对应的字典即可。
在多个项目中,我总结了一些典型问题及解决方案:
打印位置偏移:
中文乱码:
打印速度慢:
对于更复杂的场景,可以考虑以下扩展:
数据库集成:
批量打印优化:
csharp复制public void BatchPrint(string templatePath, IEnumerable<Dictionary<string, string>> dataList)
{
var template = LoadTemplate(templatePath);
var printDoc = new PrintDocument();
printDoc.PrintPage += (sender, e) =>
{
if (!dataList.MoveNext())
{
e.HasMorePages = false;
return;
}
DrawTemplate(e.Graphics, template, dataList.Current);
e.HasMorePages = true;
};
printDoc.Print();
}
模板版本控制:
在我的压力测试中(配置:i5-8250U,8GB内存):
| 场景 | 平均耗时 | 内存占用 |
|---|---|---|
| 简单标签(纯文本) | 23ms/页 | 15MB |
| 复杂标签(含3个条形码) | 87ms/页 | 22MB |
| 连续100页打印 | 4.2秒 | 28MB |
关键发现:条形码生成是性能瓶颈,预生成可以提升30%速度。