Files
jx-callback/business/jxstore/event/event_tcp_utils.go
suyl e55d4dfd4f aa
2021-07-30 18:42:17 +08:00

498 lines
15 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package event
import (
"encoding/hex"
"fmt"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/globals"
"net"
"regexp"
"strconv"
"strings"
"sync"
"time"
)
const (
heartText = "1e000f02000151"
printText = "1e00180200"
printSuccessText = "1e001802000150"
printErrWithoutPaper = "05"
printMsgAlreadySend = 2 //已经发出打印消息
printMsgSuccess = 1 //打印成功
printMsgWait = 0 //待打印
printMsgFail = -1 //打印失败(打印机报出)
printMsgErr = -2 //京西报出
printMsgAlreadyLoad = 3 //已放入队列
heartErrNormal = "00" //正常
heartErrWithoutPaper = "04" //心跳错,缺纸
heartErrHot = "08" //过热
printerStatusOnlineWithoutPaper = 2 //在线缺纸
printerStatusOnline = 1 //在线
printerStatusOffline = -1 //离线
)
//标签
const (
signBR = "<br>" //换行
signCenter = "<center>" //居中
signLeft = "<left>" //居左
signRight = "<right>" //居右
signBig = "<b>" //字体放大
signHighBig = "<hb>" //字体纵向放大
signWideBig = "<wb>" //字体横向放大
signQrCenter = "<qrc>" //二维码居中
signQrLeft = "<qrl>" //二维码居左
signQrRight = "<qrr>" //二维码居右
signSound = "<sound>" //声音
hexSignBROrEXE = "0a"
hexSignCenter = "1b6101"
hexSignLeft = "1b6100"
hexSignRight = "1b6102"
hexSignNormal = "1b2100"
hexSignBig = "1b2130"
hexSignHighBig = "1b2110"
hexSignWideBig = "1b2120"
hexSignQrCenter = "1d5802"
hexSignQrLeft = "1d5800"
hexSignQrRight = "1d5804"
hexSignQr = "1b5a000106" //"1b5a000106" 0600 : 后面二维码的字节数
hexSignQrEnd = "000a1b40" //000a0a0a1b40
hexSignSound = "1d6b40"
byteSignBR = "3c62723e" //换行
byteSignCenter = "3c63656e7465723e" //居中
byteSignLeft = "3c6c6566743e" //居左
byteSignRight = "3c72696768743e" //居右
byteSignBig = "3c623e" //字体放大
byteSignHighBig = "3c68623e" //字体纵向放大
byteSignWideBig = "3c77623e" //字体横向放大
byteSignQrCenter = "3c7172633e"
byteSignQrLeft = "3c71726c3e"
byteSignQrRight = "3c7172723e"
byteSignSound = "3c736f756e643e"
byteSignCenterE = "3c2f63656e7465723e" //居中
byteSignLeftE = "3c2f6c6566743e" //居左
byteSignRightE = "3c2f72696768743e" //居右
byteSignBigE = "3c2f623e" //字体放大
byteSignHighBigE = "3c2f68623e" //字体纵向放大
byteSignWideBigE = "3c2f77623e" //字体横向放大
byteSignQrCenterE = "3c2f7172633e"
byteSignQrLeftE = "3c2f71726c3e"
byteSignQrRightE = "3c2f7172723e"
byteSignSoundE = "3c2f736f756e643e"
)
var (
//t = &TcpClient{}
printErrMap = map[string]string{
printErrWithoutPaper: "打印机缺纸!",
}
signMap = map[string]string{
byteSignBR: hexSignBROrEXE,
}
regexpQrc = regexp.MustCompile(byteSignQrCenter + "(.*?)" + byteSignQrCenterE)
regexpQrl = regexp.MustCompile(byteSignQrLeft + "(.*?)" + byteSignQrLeftE)
regexpQrr = regexp.MustCompile(byteSignQrRight + "(.*?)" + byteSignQrRightE)
regexpSound = regexp.MustCompile(byteSignSound + "(.*?)" + byteSignSoundE)
//printMsgChan = make(chan *model.PrintMsg, 1024)
//printMsgCallbackMap = make(map[string]chan string, 1024)
//printMsgChanFail = make(chan *model.PrintMsg, 1024)
timeoutChan = make(chan int, 10)
)
type PrintInfo struct {
C net.Conn
Status int // 2 //在线缺纸 1 //在线 -1 //离线
StatusTime time.Time
}
//连接的客户端,吧每个客户端都放进来
type TcpClient struct {
Clients map[string]*PrintInfo //放tcp连接的printNo 为key
MsgMap map[string]chan *model.PrintMsg //放打印信息的printNo为key
CallBackMap map[string]chan string //放打印信息回调信息的printNo为key
*sync.RWMutex
}
type GetPrintStatus struct {
PrintNo string //打印机编号
AppID int
}
//从连接池删除,并关闭连接
func (t *TcpClient) delConn(key string) {
t.Lock()
defer t.Unlock()
if t.Clients[key].C != nil {
t.Clients[key].C.Close()
}
delete(t.Clients, key)
}
//添加到连接池中
func (t *TcpClient) addConn(c net.Conn, key string, status int) {
t.Lock()
defer t.Unlock()
globals.SugarLogger.Debugf("addConn key: %s", key)
t.Clients[key] = &PrintInfo{
C: c,
Status: status,
StatusTime: time.Now(),
}
}
func (t *TcpClient) buildMsgMap(key string) {
t.Lock()
defer t.Unlock()
dataChan := make(chan *model.PrintMsg, 1024)
t.MsgMap[key] = dataChan
}
func (t *TcpClient) buildCallBackMap(key string) {
t.Lock()
defer t.Unlock()
dataChan := make(chan string, 1024)
t.CallBackMap[key] = dataChan
}
func (t *TcpClient) getPrintStatus(key string) int {
t.RLock()
defer t.RUnlock()
if t.isExist(key) {
return t.Clients[key].Status
} else {
return printerStatusOffline
}
}
func (t *TcpClient) getPrintConn(key string) net.Conn {
t.RLock()
defer t.RUnlock()
if t.isExist(key) {
return t.Clients[key].C
} else {
return nil
}
}
func (t *TcpClient) isExistMsg(key string) bool {
t.RLock()
defer t.RUnlock()
if t.MsgMap[key] == nil {
return false
} else {
return true
}
}
func (t *TcpClient) isExistCallback(key string) bool {
t.RLock()
defer t.RUnlock()
if t.CallBackMap[key] == nil {
return false
} else {
return true
}
}
func (t *TcpClient) isExist(key string) bool {
t.RLock()
defer t.RUnlock()
if t.Clients[key] == nil {
return false
} else {
return true
}
}
func (t *TcpClient) setPrintStatus(key string, status int) {
t.Lock()
defer t.Unlock()
if t.isExist(key) {
t.Clients[key].Status = status
t.Clients[key].StatusTime = time.Now()
}
}
func (t *TcpClient) addMsgChan(printMsg *model.PrintMsg) {
t.Lock()
defer t.Unlock()
if t.MsgMap[printMsg.PrintNo] == nil {
dataChan := make(chan *model.PrintMsg, 1024)
t.MsgMap[printMsg.PrintNo] = dataChan
}
globals.SugarLogger.Debugf("addMsgChan msgID: %s", printMsg.MsgID)
t.MsgMap[printMsg.PrintNo] <- printMsg
}
func (t *TcpClient) addCallbackChan(key, data string) {
t.Lock()
defer t.Unlock()
if !t.isExistCallback(key) {
t.buildCallBackMap(key)
}
t.CallBackMap[key] <- data
}
func NewTcpClient() *TcpClient {
t := &TcpClient{
Clients: make(map[string]*PrintInfo),
CallBackMap: make(map[string]chan string),
MsgMap: make(map[string]chan *model.PrintMsg),
}
t.RWMutex = new(sync.RWMutex)
return t
}
func printStatus2JxStatus(printStatus string) (status int) {
if printStatus == heartErrWithoutPaper {
return printerStatusOnlineWithoutPaper
} else if printStatus == heartErrNormal {
return printerStatusOnline
}
return status
}
func getCallbackMsgInfo(data string) (orderNo int64, printNo string) {
orderNo = h8l82int(data[len(data)-6:len(data)-4], data[len(data)-4:len(data)-2])
printNoData, _ := hex.DecodeString(data[len(printSuccessText) : len(data)-6])
printNo = string(printNoData)
return orderNo, printNo
}
func changePrinterStatus(printNo string, status int) {
var (
db = dao.GetDB()
)
if printer, err := dao.GetPrinter(db, printNo); err == nil && printer != nil {
feilds := []string{}
if printer.Status != status {
printer.Status = status
feilds = append(feilds, "Status")
}
isOnline := 0
if status == printerStatusOnline || status == printerStatusOnlineWithoutPaper {
isOnline = model.YES
} else {
isOnline = model.NO
}
if isOnline != printer.IsOnline {
printer.IsOnline = isOnline
feilds = append(feilds, "IsOnline")
}
if len(feilds) > 0 {
dao.UpdateEntity(db, printer, feilds...)
}
}
}
func buildMsg(printMsg *model.PrintMsg) (data []byte, err error) {
var (
content = printMsg.Content
orderNo = printMsg.OrderNo
str = "1e"
const1 = "0200ff50"
printInit = "1b40" //打印机初始化
//voice = "1d6b401dfd001a01015b7631365d736f756e64622cc4fad3d0d0c2b6a9b5a5c0b1" //语音,中国
//qr = "1d58021b5a0001061600747470733a2f2f7777772e62616964752e636f6d2f1b000A0A0A1B40"
orderNoHexH, orderNoHexL, printData string
)
//写入数据
orderNoHexH, orderNoHexL = int2h8l8(int64(orderNo))
printDataGBK, _ := jxutils.Utf8ToGbk([]byte(replaceContentOther(content)))
printData = hex.EncodeToString(printDataGBK)
printData = replaceContent(printData, printMsg)
lenData := int64(len(str) + len(const1) + len(orderNoHexH) + len(orderNoHexL) + len(printInit) + 2 + 4 + len(printData))
x1, x2 := int2h8l8(lenData / 2)
dataStr := str + x1 + x2 + const1 + orderNoHexH + orderNoHexL + printInit + printData
check := getCheckSum(dataStr)
return jxutils.Hextob(dataStr + check), err
}
func replaceContentOther(content string) string {
return strings.ReplaceAll(content, "⃣️", " ")
}
func getCheckSum(str string) (check string) {
var sum int64
for i := 0; i < len(str); i = i + 2 {
b := string(str[i]) + string(str[i+1])
bt, _ := strconv.ParseInt(b, 16, 32)
sum += bt
}
_, check = int2h8l8(sum)
return check
}
//内容中的标签替换成指令
func replaceContent(content string, printMsg *model.PrintMsg) (result string) {
var (
lenqr int
hexLenqr string
)
result = content
for k, v := range signMap {
if strings.Contains(result, k) {
result = strings.ReplaceAll(result, k, v)
}
}
if strings.Contains(result, byteSignCenter) && strings.Contains(result, byteSignCenterE) {
result = strings.ReplaceAll(result, byteSignCenter, hexSignCenter)
result = strings.ReplaceAll(result, byteSignCenterE, hexSignBROrEXE+hexSignLeft)
}
if strings.Contains(result, byteSignLeft) && strings.Contains(result, byteSignLeftE) {
result = strings.ReplaceAll(result, byteSignLeft, hexSignLeft)
result = strings.ReplaceAll(result, byteSignLeftE, hexSignBROrEXE+hexSignLeft)
}
if strings.Contains(result, byteSignRight) && strings.Contains(result, byteSignRightE) {
result = strings.ReplaceAll(result, byteSignRight, hexSignRight)
result = strings.ReplaceAll(result, byteSignRightE, hexSignBROrEXE+hexSignLeft)
}
if strings.Contains(result, byteSignBig) && strings.Contains(result, byteSignBigE) {
result = strings.ReplaceAll(result, byteSignBig, hexSignBig)
result = strings.ReplaceAll(result, byteSignBigE, hexSignBROrEXE+hexSignNormal)
}
if strings.Contains(result, byteSignHighBig) && strings.Contains(result, byteSignHighBigE) {
result = strings.ReplaceAll(result, byteSignHighBig, hexSignHighBig)
result = strings.ReplaceAll(result, byteSignHighBigE, hexSignBROrEXE+hexSignNormal)
}
if strings.Contains(result, byteSignWideBig) && strings.Contains(result, byteSignWideBigE) {
result = strings.ReplaceAll(result, byteSignWideBig, hexSignWideBig)
result = strings.ReplaceAll(result, byteSignWideBigE, hexSignBROrEXE+hexSignNormal)
}
if strings.Contains(result, byteSignQrCenter) && strings.Contains(result, byteSignQrCenterE) {
if qrs := regexpQrc.FindStringSubmatch(result); len(qrs) > 0 {
lenqr = len(qrs[1]) / 2
hexLenqr = fmt.Sprintf("%x", lenqr)
if len(hexLenqr) < 2 {
hexLenqr = "0" + hexLenqr
}
}
result = strings.ReplaceAll(result, byteSignQrCenter, hexSignQrCenter+hexSignQr+hexLenqr+"00")
result = strings.ReplaceAll(result, byteSignQrCenterE, hexSignQrEnd)
}
if strings.Contains(result, byteSignQrLeft) && strings.Contains(result, byteSignQrLeftE) {
if qrs := regexpQrl.FindStringSubmatch(result); len(qrs) > 0 {
lenqr = len(qrs[1]) / 2
hexLenqr = fmt.Sprintf("%x", lenqr)
if len(hexLenqr) < 2 {
hexLenqr = "0" + hexLenqr
}
}
result = strings.ReplaceAll(result, byteSignQrLeft, hexSignQrLeft+hexSignQr+hexLenqr+"00")
result = strings.ReplaceAll(result, byteSignQrLeftE, hexSignQrEnd)
}
if strings.Contains(result, byteSignQrRight) && strings.Contains(result, byteSignQrRightE) {
if qrs := regexpQrr.FindStringSubmatch(result); len(qrs) > 0 {
lenqr = len(qrs[1])
hexLenqr = fmt.Sprintf("%x", lenqr)
if len(hexLenqr) < 2 {
hexLenqr = "0" + hexLenqr
}
}
result = strings.ReplaceAll(result, byteSignQrRight, hexSignQrRight+hexSignQr+hexLenqr+"00")
result = strings.ReplaceAll(result, byteSignQrRightE, hexSignQrEnd)
}
if strings.Contains(result, byteSignSound) && strings.Contains(result, byteSignSoundE) {
if sounds := regexpSound.FindStringSubmatch(result); len(sounds) > 0 {
sound := sounds[1]
if printer, _ := dao.GetPrinter(dao.GetDB(), printMsg.PrintNo); printer != nil {
//先把结束标签消了
result = strings.ReplaceAll(result, byteSignSoundE, "")
//fd 固定
//001a (声音数据长度高八位低八位)
//0101 固定
soundPrefix := ""
if printer.Sound != "" {
soundPrefix = "[v" + utils.Int2Str(printer.Volume*2) + "]" + printer.Sound
} else {
soundPrefix = "[v" + utils.Int2Str(printer.Volume*2) + "]"
}
hexPrefix, _ := jxutils.Utf8ToGbk([]byte(soundPrefix))
hexPrefixStr := hex.EncodeToString(hexPrefix)
realSound := hexPrefixStr + sound
allLen := fmt.Sprintf("%x", (len("fd001a0101")+len(realSound))/2)
if len(allLen) < 2 {
allLen = "0" + allLen
}
soundLenH, soundLenX := int2h8l8(int64((len(realSound) + len("0101")) / 2))
result = strings.ReplaceAll(result, byteSignSound, hexSignSound+allLen+"fd"+soundLenH+soundLenX+"0100"+hexPrefixStr)
}
}
}
return result
}
func checkPrintMsg(db *dao.DaoDB, printMsg *model.PrintMsg) (err error) {
if printMsg.Content == "" {
return fmt.Errorf("此打印信息内容为空printMsg printNo:[%v], orderNo :[%v]", printMsg.PrintNo, printMsg.OrderNo)
}
if printMsg.PrintNo == "" {
return fmt.Errorf("此打印信息打印机编号为空printMsg printNo:[%v], orderNo :[%v]", printMsg.PrintNo, printMsg.OrderNo)
}
if printMsg.OrderNo == 0 {
return fmt.Errorf("此打印信息订单序号为空printMsg printNo:[%v], orderNo :[%v]", printMsg.PrintNo, printMsg.OrderNo)
}
if printer, err := dao.GetPrinter(db, printMsg.PrintNo); err == nil {
if printer != nil {
if printer.FlowFlag == 1 {
return fmt.Errorf("此打印机当月流量已用完请及时充值printNo:[%v]", printMsg.PrintNo)
}
}
}
return err
}
func int2h8l8(i int64) (h, l string) {
origin2 := fmt.Sprintf("%b", i)
flag := 16 - len(origin2)
for i := 0; i < flag; i++ {
origin2 = "0" + origin2
}
begin8 := origin2[:8]
end8 := origin2[8:]
r1, _ := strconv.ParseInt(begin8, 2, 32)
r2, _ := strconv.ParseInt(end8, 2, 32)
h = fmt.Sprintf("%x", r1)
l = fmt.Sprintf("%x", r2)
if len(h) < 2 {
h = "0" + h
}
if len(l) < 2 {
l = "0" + l
}
return h, l
}
func h8l82int(h, l string) (i int64) {
s1, s2 := xtob(h), xtob(l)
flag1 := 8 - len(s1)
flag2 := 8 - len(s2)
for m := 0; m < flag1; m++ {
s1 = "0" + s1
}
for j := 0; j < flag2; j++ {
s2 = "0" + s2
}
i, _ = strconv.ParseInt(s1+s2, 2, 10)
return i
}
func xtob(x string) string {
base, _ := strconv.ParseInt(x, 16, 10)
return strconv.FormatInt(base, 2)
}