最近客户有要求,要把WIFI排插的MAC地址打印成二维码。公司使用的是一台TSC的打印机。而WIFI设备的MAC地址会由它自己通过UDP广播出来。而且还要求要做一个界面。这就很头疼了。用C做好像很麻烦的样子,所以我决定使用我第二熟悉的Java来做,刚好IDEA也有做GUI的功能。这里做一下记录。
在网上寻找驱动,这个很简单,就不用说了吧,先让自己的电脑的打印机列表里出现这个打印机,而且可以打印测试页。
去这里寻找DLL下载下来,这里我直接给个链接吧=====》Windows TSC DLL
打开压缩包,会发现有两个文件夹,一个是X86,另一个是X64;将X86里的东西拷贝到C:\Windows\System32下,将X64里的东西拷贝到C:\Windows\SysWOW64下;
安装JDK环境。
创建TSPL命令的类
的在读了TSPL指令集后,首创建我需要的类。
首先TSPL的用户指南在这里下载=====》这里 首先,我需要设定我的标签大小。
SIZE
这里我就不把内容贴上来了。 简单说说,最主要的就是标签的长宽,文档中是使用Label width和Label length来表示的。那么就来吧。
package com.Aperture.TSPL;
public class SIZE {
private double mWidth, mHeight;
public SIZE(double Width, double Height) {
this.mWidth = Width;
this.mHeight = Height;
}
}
还可以注意到一个事情,文档中出现了两种单位,一种是mm,另一种是inch,这里新建一个枚举类,想了半天也想不到什么好名字,就用TSCSystem吧:
package com.Aperture.TSPL;
/**
* The TSC unit:
* <dl>
* <dt>English:</dt>
* <dd>inch</dd>
* <dt>Metric:</dt>
* <dd>mm</dd>
* </dl>
*/
public enum TSCSystem {
English, Metric
}
我手里的标签长宽很奇怪,不是整数值,把游标卡尺的单位转换到Inch后发现居然是个整数,那我就用Inch来做默认单位吧:
package com.Aperture.TSPL;
public class SIZE {
private double mWidth, mHeight;
private TSCSystem mTscSystem = TSCSystem.English;
/**
* @param Width Label width
* @param Height Label length
* @param tscSystem Length unit. See {@link TSCSystem}.
*/
public SIZE(double Width, double Height, TSCSystem tscSystem) {
this.mWidth = Width;
this.mHeight = Height;
this.mTscSystem = tscSystem;
}
/**
* This command defines the label width and length.
*
* @param Width Label width (inch)
* @param Height Label length (inch)
*/
public SIZE(double Width, double Height) {
this.mWidth = Width;
this.mHeight = Height;
}
}
需要将其转换为TSPL指令,所以再来一个方法:
/**
* @return TSPL command.
*/
public String getCOMMAND() {
switch (mTscSystem) {
case English:
return "SIZE " + String.valueOf(mWidth) + ',' + String.valueOf(mHeight);
case Metric:
return "SIZE " + String.valueOf(mWidth) + " mm" + ',' + String.valueOf(mHeight) + " mm";
default:
return null;
}
}
GAP
这个就是标签与标签之间的距离。 参照上一个很容易就能写出:
package com.Aperture.TSPL;
public class GAP {
private final double mGap;
private final double mOffset;
private TSCSystem mTscSystem = TSCSystem.English;
/**
* Defines the gap distance between two labels.
*
* @param mGap Label GAP
* @param mOffset Gap offset
* @param tscSystem Length unit. See {@link TSCSystem}.
*/
public GAP(double mGap, double mOffset, TSCSystem tscSystem) {
this.mGap = mGap;
this.mOffset = mOffset;
this.mTscSystem = tscSystem;
}
/**
* Defines the gap distance between two labels.
*
* @param mGap Label GAP (inch)
* @param mOffset Gap offset (inch)
*/
public GAP(double mGap, double mOffset) {
this.mGap = mGap;
this.mOffset = mOffset;
}
/**
* Defines the gap distance between two labels.
*
* @param mGap Label GAP (inch)
*/
public GAP(double mGap) {
this.mGap = mGap;
this.mOffset = 0;
}
/**
* @return TSPL command.
*/
public String getCOMMAND() {
switch (mTscSystem) {
case English:
return "SIZE " + String.valueOf(mGap) + ',' + String.valueOf(mOffset);
case Metric:
return "SIZE " + String.valueOf(mGap) + " mm" + ',' + String.valueOf(mOffset) + " mm";
default:
return null;
}
}
}
TEXT
这是比较复杂的一个,因为可变参数很多。
package com.Aperture.TSPL;
public class TEXT {
//TEXT x,y, " font ",rotation,x-multiplication,y-multiplication,[alignment,] " content "
private Integer mX;
private Integer mY;
private String mFont;
private Integer mRotation;
private Integer mXMultiplication, mYMultiplication;
private Integer mAlignment = null;
private String mContent;
public TEXT(int x, int y, String font, int rotation, int x_multiplication, int y_multiplication, String content) {
mX = x;
mY = y;
mFont = font;
this.mRotation = rotation;
this.mXMultiplication = x_multiplication;
this.mYMultiplication = y_multiplication;
this.mContent = content;
}
public TEXT(int x, int y, String font, int rotation, int x_multiplication, int y_multiplication, int alignment, String content) {
mX = x;
mY = y;
mFont = font;
this.mRotation = rotation;
this.mXMultiplication = x_multiplication;
this.mYMultiplication = y_multiplication;
this.mAlignment = alignment;
this.mContent = content;
}
public String getCOMMAND() {
if (mAlignment == null) {
return "TEXT " +
mX +
mY +
" \"" + mFont + "\"" +
mRotation +
mXMultiplication +
mYMultiplication +
"\"" + mContent + "\"";
}
return "TEXT " +
mX + ',' +
mY + ',' +
" \"" + mFont + "\"" + ',' +
mRotation + ',' +
mXMultiplication + ',' +
mYMultiplication + ',' +
mAlignment + ',' +
"\"" + mContent + "\"";
}
}
QRCODE
这个居然比TEXT简单。
package com.Aperture.TSPL;
public class QRCODE {
private double mX;
private double mY;
private char mEccLevel;
private double mCellWidth;
private char mMode;
private int mRotation;
private String mModule;
private String mMask;
private String mData;
public QRCODE(double x, double mY, char eccLevel, double cellWidth, char mode, int rotation, String module, String mask, String Data) {
this.mX = x;
this.mY = mY;
this.mEccLevel = eccLevel;
this.mCellWidth = cellWidth;
this.mMode = mode;
this.mRotation = rotation;
this.mModule = module;
this.mMask = mask;
this.mData = Data;
}
public String getCOMMAND() {
return "QRCODE " + mX + "," + mY + "," + mEccLevel + "," + mCellWidth + "," + mMode + "," + mRotation + "," + mModule + "," + mMask + ",\"" + mData + '\"';
}
}
其他
剩下的东西都放在这里吧,GUI没什么好讲的。 客户还有提到需要一个SN码,SN码要和MAC对应,完了还要做在报表里。通过不断的+1即可解决这个问题。读取UDP广播的话,这种东西最好不要做在主线程中,以免卡住界面。本来想用Thread的,但是这个线程一直读取UDP的时候会让我的CPU风扇狂转不止。所以我查到了定时任务TimerTask。
class UdpReceive extends TimerTask {
@Override
public void run() {
DatagramSocket socket;
try {
socket = new DatagramSocket(8080);
} catch (SocketException e) {
stateTextArea1.append("\tUdpReceive: 8080 已被占用!" + '\n');
stateTextArea1.append(e.getMessage() + '\n');
return;
}
DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);
try {
socket.receive(packet);
} catch (IOException e) {
stateTextArea1.append(e.getMessage() + '\n');
return;
}
byte[] packetData = packet.getData();
String data = new String(packetData, 0, packet.getLength()).replaceAll("\r\n", "");
stateTextArea1.append("\tUdpReceive: 获得数据: " + '\"' + data + '\"' + '\n');
macTextField.setText(data.split(",")[2]);
socket.close();
if (MAC_Records.contains(data)) {
snTextField.setText(SN_Records.get(MAC_Records.indexOf(data)));
stateTextArea1.append("\tUdpReceive: " + data + "已存在于记录中;SN :" +
SN_Records.get(MAC_Records.indexOf(data)) + '\n');
} else {
snTextField.setText("000" + todayRecordName + String.valueOf(100000 + Number)
.substring(1));
Number++;
MAC_Records.add(data);
SN_Records.add(snTextField.getText());
if (SaveNewData()) {
stateTextArea1.append("\tUdpReceive: 保存记录成功." + '\n');
}
// PrintLabel("000" + todayRecordName + String.valueOf(100000 + MAC_Records.size())
// .substring(1), data);
PrintLabel(SN_Records.get(MAC_Records.indexOf(data)), data);
mTable.updateUI();
}
}
}
通过调用
new Timer().schedule(new UdpReceive(), delay, period);
来启用这个定时线程。 避免UDP广播多次调用打印,所以,我使用暴力一点的方法,直接把所有的MAC全部存起来,每一个新的MAC都做一个判断,只有接收到的MAC是新的设备的时候才调用打印机。打印部分的的流程如下:
private void PrintLabel(String snData, String macData) {
stateTextArea1.append("\t\tPrintLabel: 打印 QRCODE " + macData + '\n');
TscLibDll.INSTANCE.openport("TSC TTP-342M Pro");
TscLibDll.INSTANCE.clearbuffer();
TscLibDll.INSTANCE.setup("83.4", "29", "2", "8",
"0", "2.54", "0");
TscLibDll.INSTANCE.sendcommand(new DIRECTION(1).getCOMMAND());
TscLibDll.INSTANCE.sendcommand(new SHIFT(10, -20).getCOMMAND());
TscLibDll.INSTANCE.sendcommand(new QRCODE(690, 40, 'L', 10, 'A',
0, "M2", "S7", macData).getCOMMAND());
TscLibDll.INSTANCE.sendcommand(new BARCODE(70, 135, "128", 50, 0,
0, 3, 7, 0, snData).getCOMMAND());
TscLibDll.INSTANCE.sendcommand(new TEXT(90, 185, "3", 0, 1,
1, 0, "SN: " + snData).getCOMMAND());
TscLibDll.INSTANCE.printlabel("1", "1");
TscLibDll.INSTANCE.closeport();
}
总结起来就是
TscLibDll.INSTANCE.openport(); // 打开端口
TscLibDll.INSTANCE.setup(); // 设置条码大小
TscLibDll.INSTANCE.sendcommand(); // 发送TSPL指令
TscLibDll.INSTANCE.printlabel(); // 打印
TscLibDll.INSTANCE.closeport(); // 关闭端口
而每打印的时候发去的指令它都会存下来,所以要打印新的东西就要清除缓存,再加上
TscLibDll.INSTANCE.clearbuffer(); // 清除缓存
就完美了。
两个项目的所有代码我都放在Github上====》Godenfreemans的GitHub