248 lines
5.5 KiB
Go
248 lines
5.5 KiB
Go
package main
|
||
|
||
import (
|
||
"bytes"
|
||
"errors"
|
||
"fmt"
|
||
"log/slog"
|
||
"net"
|
||
"strings"
|
||
"sync"
|
||
"time"
|
||
"unicode"
|
||
|
||
"golang.org/x/text/encoding/japanese"
|
||
)
|
||
|
||
const (
|
||
STX = 0x02
|
||
ETX = 0x03
|
||
ACK = 0x06
|
||
NAK = 0x15
|
||
)
|
||
|
||
type Client struct {
|
||
host string
|
||
conn net.Conn
|
||
mtx sync.Mutex
|
||
}
|
||
|
||
func NewClient(host string) *Client {
|
||
return &Client{
|
||
host: host,
|
||
}
|
||
}
|
||
|
||
func main() {
|
||
c := NewClient("192.168.2.33:1024")
|
||
|
||
err := c.SetText(1, "123456789A")
|
||
if err != nil {
|
||
slog.Error(err.Error())
|
||
return
|
||
}
|
||
slog.Info("OK")
|
||
|
||
resp, err := c.GetText(1)
|
||
if err != nil {
|
||
slog.Error(err.Error())
|
||
return
|
||
}
|
||
//0:1:A2001000010:023312332233323342335233623372338:
|
||
//0:1:A2001000010:282508251825282538254825582568257:
|
||
slog.Info(resp)
|
||
}
|
||
|
||
func (c *Client) sendCommand(cmd string) (string, error) {
|
||
c.mtx.Lock()
|
||
defer c.mtx.Unlock()
|
||
|
||
conn, err := net.DialTimeout("tcp", c.host, 3*time.Second)
|
||
if err != nil {
|
||
return "", fmt.Errorf("喷码机连接失败: %w", err)
|
||
}
|
||
defer conn.Close()
|
||
|
||
// 构造完整指令: STX + CMD + parameters + ETX
|
||
var buffer bytes.Buffer
|
||
buffer.WriteByte(STX)
|
||
buffer.WriteString(cmd)
|
||
buffer.WriteByte(ETX)
|
||
|
||
// 计算校验和
|
||
checksum := calcChecksum(buffer.Bytes()[1:]) // 排除STX的data部分
|
||
|
||
// 添加校验和到buffer
|
||
buffer.WriteString(fmt.Sprintf("%02X", checksum))
|
||
|
||
message := buffer.Bytes()
|
||
|
||
// 发送指令
|
||
_, err = conn.Write(message)
|
||
if err != nil {
|
||
return "", fmt.Errorf("喷码机发送指令失败: %w", err)
|
||
}
|
||
|
||
// 接收响应
|
||
resp, err := readResponse(conn)
|
||
if err != nil {
|
||
return "", fmt.Errorf("喷码机接收响应失败: %w", err)
|
||
}
|
||
|
||
// 解析响应
|
||
return parseResponse(resp)
|
||
}
|
||
|
||
func readResponse(conn net.Conn) ([]byte, error) {
|
||
_ = conn.SetReadDeadline(time.Now().Add(5 * time.Second))
|
||
|
||
buffer := make([]byte, 256)
|
||
n, err := conn.Read(buffer)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return buffer[:n], nil
|
||
}
|
||
|
||
func parseResponse(resp []byte) (body string, err error) {
|
||
if len(resp) == 0 {
|
||
return "", errors.New("empty response")
|
||
}
|
||
|
||
switch resp[0] {
|
||
case ACK:
|
||
// 处理带数据的响应(例如查询命令的响应)
|
||
if len(resp) > 3 && resp[1] == STX && resp[len(resp)-1] == ETX {
|
||
// 返回有效数据(示例处理)
|
||
return string(resp[2 : len(resp)-1]), nil
|
||
}
|
||
return "", nil
|
||
|
||
case NAK:
|
||
if len(resp) >= 7 && resp[1] == STX && resp[len(resp)-1] == ETX {
|
||
errorCode := string(resp[2:6])
|
||
msg, exists := errorCodeMap[errorCode]
|
||
if !exists {
|
||
msg = "Unknown error"
|
||
}
|
||
return "", fmt.Errorf("NAK error %s: %s", errorCode, msg)
|
||
}
|
||
return "", errors.New("NAK received without error details")
|
||
}
|
||
return
|
||
}
|
||
|
||
func calcChecksum(data []byte) byte {
|
||
var sum byte
|
||
for _, b := range data {
|
||
sum += b
|
||
}
|
||
return sum
|
||
}
|
||
|
||
func (c *Client) SetText(model int, text string) error {
|
||
|
||
//0:JIS、1:ASCII、2:SHIFT JIS、
|
||
//3:ASCII+SHIFT JIS、4:UNICODE、6:GB、
|
||
//7:ASCII+GB、8:KS、9:ASCII+KS、
|
||
//A:BIG5、B:ASCII+BIG5
|
||
bb, err := japanese.ShiftJIS.NewEncoder().Bytes([]byte(text))
|
||
if err != nil {
|
||
return fmt.Errorf("编码失败: %w", err)
|
||
}
|
||
|
||
//纵点数 (1位)
|
||
//1:5点、 2:7点、 3:9点、
|
||
//4:10点、 5:12点、 6:16点、
|
||
//7:20点、 8:22点、 9:24点、
|
||
//A:26点、 E:11点、 F:15点
|
||
//字体尺寸 (1位)
|
||
//2:24x24、 3:24x18、 4:16x16、 5:16x12、
|
||
//6:12x10、 7:10x8、 8:9x9、 9:9x7、
|
||
//A:7x8、 B:7x5、 C:5x5、 D:5xN、
|
||
//E:7xN
|
||
//字体底部位置 (2位)
|
||
// 00~25:配置于最下行开始0~25点
|
||
//倍角 (1位)
|
||
// 1~8:1~8倍
|
||
//文字方向 (1位)
|
||
// 0:↑、 1:←、 2:→、 3:↓、 4:镜面
|
||
//文字间点 (1位)
|
||
// 00~31:0~31点
|
||
//最终文字的文字间 (1位)
|
||
// 0:无效、 1:有效
|
||
//文字间点的倍角处理 (1位)
|
||
// 0:无效、 1:有效
|
||
//黑白反转 (1位)
|
||
// 0:无效、 1:有效
|
||
cmd := fmt.Sprintf("STM:0:%d:A2001000010:3%s:", model, bb)
|
||
|
||
_, err = c.sendCommand(cmd)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// 0:JIS、2:shift JIS、3:ASCII+shift JIS、
|
||
// 4:UNICODE、6:GB、7:ASCII+GB、8:KS、
|
||
// 9:ASCII+KS、A:BIG5、B:ASCII+BIG5
|
||
func (c *Client) GetText(model int) (text string, err error) {
|
||
conn, err := net.DialTimeout("tcp", "192.168.2.33:1024", 5*time.Second)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
defer conn.Close()
|
||
|
||
cmd := fmt.Sprintf("GTM:0:%d:%d:", model, 3)
|
||
|
||
response, err := c.sendCommand(cmd)
|
||
if err != nil {
|
||
return
|
||
}
|
||
|
||
fields := strings.Split(response, ":")
|
||
content := fields[3]
|
||
|
||
//coding, _ := strconv.Atoi(string([]rune(content)[0]))
|
||
//var re string
|
||
//switch coding {
|
||
//case 0, 3:
|
||
//case 1:
|
||
// re = content[1:]
|
||
//case 2:
|
||
// re, err = japanese.ShiftJIS.NewDecoder().String(content[1:])
|
||
//case 4:
|
||
// bb, _ := hex.DecodeString(content[1:])
|
||
// re, err = unicodex.UTF16(unicodex.LittleEndian, unicodex.IgnoreBOM).NewDecoder().String(string(bb))
|
||
//}
|
||
|
||
re, err := japanese.ShiftJIS.NewDecoder().String(content[1:])
|
||
if err != nil {
|
||
return "", fmt.Errorf("解码失败: %w", err)
|
||
}
|
||
re = strings.ToLowerSpecial(numConv, re)
|
||
return re, nil
|
||
}
|
||
|
||
var numConv = unicode.SpecialCase{
|
||
{
|
||
Lo: 0x3002, // Lo 全角句号
|
||
Hi: 0x3002, // Hi 全角句号
|
||
Delta: [unicode.MaxCase]rune{
|
||
0, // UpperCase
|
||
0x002e - 0x3002, // LowerCase 转成半角句号
|
||
0, // TitleCase
|
||
},
|
||
},
|
||
{
|
||
Lo: 0xff01, // Lo: 全角!
|
||
Hi: 0xFF19, // Hi:到全角 9
|
||
Delta: [unicode.MaxCase]rune{
|
||
0, // UpperCase
|
||
0x0021 - 0xff01, // LowerCase 转成半角
|
||
0, // TitleCase
|
||
},
|
||
},
|
||
}
|