Files
jx-callback/business/jxstore/event/event_tcp.go
suyl fa0a934737 aa
2021-07-22 13:58:53 +08:00

656 lines
20 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"
"encoding/json"
"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"
"io"
"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 //离线
printerStatusOverFlow = -2 //流量超了
)
//标签
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 (
tcpClient = &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)
printMsgCallbackChan = make(chan string)
printMsgChanFail = make(chan *model.PrintMsg)
)
type PrintInfo struct {
C net.Conn
Status int // 2 //在线缺纸 1 //在线 -1 //离线
StatusTime time.Time
}
//连接的客户端,吧每个客户端都放进来
type TcpClient struct {
Clients map[string]*PrintInfo
s *sync.RWMutex
}
type GetPrintStatus struct {
PrintNo string //打印机编号
AppID int
}
func ListenTcp() {
tcpClient.Clients = make(map[string]*PrintInfo)
tcpClient.s = new(sync.RWMutex)
l, err := net.Listen("tcp", ":8000")
if err != nil {
fmt.Println("listen error:", err)
return
}
globals.SugarLogger.Debugf("begin listenTcp port 8000......")
go HandleTcpMessages()
go HandleCheckTcpHeart()
//go doPrint2(printMsgChan)
//go doPrint(printMsgChanFail)
for {
c, err := l.Accept()
if err != nil {
fmt.Println("accept error:", err)
break
}
go handleConn(c)
}
}
func handleConn(c net.Conn) {
var (
printNo string //打印机编号
printStatus *GetPrintStatus
)
if c == nil {
globals.SugarLogger.Debugf("conn is nil")
return
}
defer c.Close()
buffer := make([]byte, 1024)
for {
n, err := c.Read(buffer)
if err != nil {
if err == io.EOF {
fmt.Println("connection close")
} else {
fmt.Println("ReadString err", err)
}
return
}
//也可能是查状态的
if err = json.Unmarshal(buffer[:n], &printStatus); err == nil {
fmt.Println("handleConn msg: ", string(buffer[:n]))
if printStatus != nil {
var status int
if tcpClient.Clients[printStatus.PrintNo] != nil {
status = tcpClient.Clients[printStatus.PrintNo].Status
} else {
status = printerStatusOffline
}
globals.SugarLogger.Debugf("handleConn getstatus :%v", utils.Format4Output(tcpClient.Clients[printStatus.PrintNo], true))
c.Write([]byte(utils.Int2Str(status)))
c.Close()
}
}
//看是心跳还是打印返回
data := hex.EncodeToString(buffer[:n])
//证明是心跳
if strings.Contains(data, heartText) {
globals.SugarLogger.Debugf("handleConn heart: %v", data)
printNoData, _ := hex.DecodeString(data[len(heartText) : len(data)-8])
printNo = string(printNoData)
globals.SugarLogger.Debugf("handleConn printno :[%v]", printNo)
status := printStatus2JxStatus(data[len(data)-8 : len(data)-6])
if tcpClient.Clients[printNo] == nil {
printInfo := &PrintInfo{
C: c,
Status: printStatus2JxStatus(data[len(data)-8 : len(data)-6]),
StatusTime: time.Now(),
}
tcpClient.s.Lock()
tcpClient.Clients[printNo] = printInfo
tcpClient.s.Unlock()
changePrinterStatus(printNo, status)
if status > 0 {
//printFail()
}
} else {
//改变打印机状态
tcpClient.s.Lock()
changePrinterStatus(printNo, status)
if tcpClient.Clients[printNo] != nil {
if tcpClient.Clients[printNo].Status != status {
tcpClient.Clients[printNo].Status = status
}
tcpClient.Clients[printNo].StatusTime = time.Now()
}
tcpClient.s.Unlock()
}
} else if strings.Contains(data, printText) {
globals.SugarLogger.Debugf("handleConn print callback: %v", data)
printMsgCallbackChan <- data
//changePrintMsg(data)
}
}
}
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 printFail() (err error) {
//新开机的打印失败和错误的
var (
db = dao.GetDB()
)
prints, _ := dao.GetPrintMsgs(db, []int{printMsgWait, printMsgFail, printMsgErr}, time.Now().Add(-time.Hour*3), time.Now(), 0, 999)
for _, printMsg := range prints {
printMsgChanFail <- printMsg
printMsg.Status = printMsgAlreadyLoad
dao.UpdateEntity(db, printMsg, "Status")
}
return err
}
func doPrint(printMsgChanFail chan *model.PrintMsg) (err error) {
var (
db = dao.GetDB()
)
for {
select {
case printMsg, _ := <-printMsgChanFail:
var (
data []byte
c net.Conn
)
if printMsg != nil {
if err = checkPrintMsg(db, printMsg); err == nil {
tcpClient.s.RLock()
if tcpClient.Clients[printMsg.PrintNo] != nil {
if tcpClient.Clients[printMsg.PrintNo].Status == printerStatusOnline {
if tcpClient.Clients[printMsg.PrintNo].C != nil {
c = tcpClient.Clients[printMsg.PrintNo].C
data, err = buildMsg(printMsg)
}
}
}
tcpClient.s.RUnlock()
if c != nil {
if _, err = c.Write(data); err != nil {
globals.SugarLogger.Debugf("handleTcpMessages err [%v]", err)
delete(tcpClient.Clients, printMsg.PrintNo)
c.Close()
} else {
globals.SugarLogger.Debugf("handleTcpMessages success, data: %v", hex.EncodeToString(data))
printMsg.Status = printMsgAlreadySend
dao.UpdateEntity(db, printMsg, "Status", "Comment")
}
}
}
}
}
}
}
func printStatus2JxStatus(printStatus string) (status int) {
if printStatus == heartErrWithoutPaper {
return printerStatusOnlineWithoutPaper
} else if printStatus == heartErrNormal {
return printerStatusOnline
}
return status
}
func changePrintMsg(data string) (err error) {
var (
db = dao.GetDB()
printNo, comment string
orderNo int64
status int
)
//1、先找出打印机编号和订单序列号这两个确定唯一一条消息
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)
//2、打印成功改变打印表的状态
if strings.Contains(data, printSuccessText) {
//1e001802000150323032313036313530303030313000013c
status = printMsgSuccess
} else {
//打印失败也改变状态并更新失败原因
status = printMsgFail
comment = printErrMap[data[12:14]]
}
if tcpClient.Clients[printNo] == nil {
return err
}
if printMsgs, err := dao.GetPrintMsgNoPage(db, printNo, orderNo); err != nil {
globals.SugarLogger.Debugf("changePrintMsg err :[%v]", err)
return err
} else if len(printMsgs) == 0 {
globals.SugarLogger.Debugf("changePrintMsg err ,not found printMsg printNo:[%v], orderNo :[%v]", printNo, orderNo)
} else if len(printMsgs) > 0 {
for _, v := range printMsgs {
v.Comment = comment
v.Status = status
dao.UpdateEntity(db, v, "Comment", "Status")
}
}
return err
}
func HandleTcpMessages() {
var (
db = dao.GetDB()
offset, pageSize = 0, 1
)
for {
//一直读?
prints, _ := dao.GetPrintMsgs(db, []int{printMsgWait}, time.Now().Add(-time.Hour*3), time.Now(), offset, pageSize)
for _, printMsg := range prints {
printMsgChan <- printMsg
printMsg.Status = printMsgAlreadyLoad
dao.UpdateEntity(db, printMsg, "Status")
}
}
}
func doPrint2(printMsgChan chan *model.PrintMsg) (err error) {
var (
db = dao.GetDB()
)
for {
select {
case printMsg, _ := <-printMsgChan:
var (
data []byte
c net.Conn
)
if printMsg != nil {
if err = checkPrintMsg(db, printMsg); err == nil {
tcpClient.s.RLock()
if tcpClient.Clients[printMsg.PrintNo] != nil {
if tcpClient.Clients[printMsg.PrintNo].Status == printerStatusOnline {
if tcpClient.Clients[printMsg.PrintNo].C != nil {
c = tcpClient.Clients[printMsg.PrintNo].C
data, err = buildMsg(printMsg)
}
} else if tcpClient.Clients[printMsg.PrintNo].Status == printerStatusOffline {
err = fmt.Errorf("打印机离线!")
} else if tcpClient.Clients[printMsg.PrintNo].Status == printerStatusOnlineWithoutPaper {
err = fmt.Errorf("打印机缺纸!")
}
}
tcpClient.s.RUnlock()
}
} else {
err = fmt.Errorf("未查询到此printMsg")
}
if err != nil {
printMsg.Status = printMsgErr
printMsg.Comment = err.Error()
dao.UpdateEntity(db, printMsg, "Status", "Comment")
delete(tcpClient.Clients, printMsg.PrintNo)
if c != nil {
c.Close()
}
} else {
if c != nil {
if _, err = c.Write(data); err != nil {
globals.SugarLogger.Debugf("handleTcpMessages err [%v]", err)
delete(tcpClient.Clients, printMsg.PrintNo)
c.Close()
} else {
globals.SugarLogger.Debugf("handleTcpMessages success, data: %v", hex.EncodeToString(data))
printMsg.Status = printMsgAlreadySend
dao.UpdateEntity(db, printMsg, "Status", "Comment")
}
}
}
default:
select {
case data, _ := <-printMsgCallbackChan:
changePrintMsg(data)
}
}
}
return err
}
func HandleCheckTcpHeart() {
for {
keys := []string{}
for k, v := range tcpClient.Clients {
if time.Now().Sub(v.StatusTime) > time.Minute+time.Second {
v.Status = printerStatusOffline
keys = append(keys, k)
}
}
for _, v := range keys {
changePrinterStatus(v, printerStatusOffline)
if tcpClient.Clients[v] != nil {
delete(tcpClient.Clients, v)
}
}
}
}
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)
}