zhangzengfei
2024-10-17 b4a86051e9bf4888fd5d01c12232a26f3874e03b
service/nvcs.go
@@ -2,6 +2,9 @@
import (
   "bytes"
   "context"
   "encoding/binary"
   "encoding/hex"
   "encoding/json"
   "fmt"
   "io/ioutil"
@@ -13,14 +16,39 @@
   "gat1400Exchange/config"
   "gat1400Exchange/models"
   "gat1400Exchange/pkg/logger"
   "gat1400Exchange/rfid"
   "gat1400Exchange/util"
   "github.com/gin-gonic/gin"
   "github.com/google/uuid"
   "golang.org/x/text/encoding/simplifiedchinese"
   "golang.org/x/text/transform"
)
type ElevatorStatus struct {
type A1SetFloorFrame struct {
   Header     [16]byte // Fixed 16-byte header
   Command    byte     // 1 byte command
   MAC        [6]byte  // 6 bytes MAC address
   DataLength uint16   // 2 bytes data length
   Data       []byte   // Data area, length defined by DataLength
   Checksum   uint16   // 2 bytes CRC16 checksum
}
// Convert frame to byte slice
func (f *A1SetFloorFrame) toBytes(includeChecksum bool) []byte {
   buf := new(bytes.Buffer)
   binary.Write(buf, binary.LittleEndian, f.Header)
   binary.Write(buf, binary.LittleEndian, f.Command)
   binary.Write(buf, binary.LittleEndian, f.MAC)
   binary.Write(buf, binary.LittleEndian, f.DataLength)
   buf.Write(f.Data)
   if includeChecksum {
      binary.Write(buf, binary.LittleEndian, f.Checksum)
   }
   return buf.Bytes()
}
type A1ElevatorStatus struct {
   TotalFloors int    `json:"TotalFloors"`
   Floor       int    `json:"Floor"`
   FloorName   string `json:"FloorName"`
@@ -28,15 +56,15 @@
   Speed       string `json:"Speed"`
}
type Elevator struct {
   Name   string         `json:"Name"`
   IP     string         `json:"IP"`
   Status ElevatorStatus `json:"Status"`
   Alarm  []interface{}  `json:"Alarm"` // You might want to define a specific type for alarms
type A1Elevator struct {
   Name   string           `json:"Name"`
   IP     string           `json:"IP"`
   Status A1ElevatorStatus `json:"Status"`
   Alarm  []interface{}    `json:"Alarm"` // You might want to define a specific type for alarms
}
type A1ElevatorData struct {
   Elevator []Elevator `json:"Elevator"`
   Elevator []A1Elevator `json:"Elevator"`
}
const (
@@ -44,6 +72,98 @@
   RunUp
   RunDown
)
var ElevatorRunState int
var ElevatorRunFloor string
var RunningCorrectTaskId string
func A1CorrectFloor() {
   if RunningCorrectTaskId != "" || config.RFIDConf.EPC == "" {
      return
   }
   taskId := uuid.New().String()
   RunningCorrectTaskId = taskId
   rfidReader := rfid.NewReader(config.RFIDConf.DevName, 115200, 5)
   defer rfidReader.CloseSerial()
   err := rfidReader.OpenSerial()
   if err != nil {
      logger.Error(err.Error())
      return
   }
   isFind, err := rfidReader.ScanSpecificEPC(config.RFIDConf.EPC, 5)
   if isFind && RunningCorrectTaskId == taskId {
      frame := NewA1SetFloorFrame(config.NVCSConf.Mac, config.RFIDConf.Position)
      address := "192.168.10.253:50000"
      err := A1SendFrame(frame, address)
      if err != nil {
         logger.Warn(err.Error())
      } else {
         logger.Debug("The floor has been calibrated.")
      }
   }
}
func calculateCRC16(data []byte) uint16 {
   var crc16 uint16 = 0xFFFF
   for i := 0; i < len(data); i++ {
      crc16 ^= uint16(data[i])
      for j := 0; j < 8; j++ {
         if crc16&0x0001 != 0 {
            crc16 >>= 1
            crc16 ^= 0xA001
         } else {
            crc16 >>= 1
         }
      }
   }
   return crc16
}
// Create a new frame based on provided data
func NewA1SetFloorFrame(macAddr string, floor uint8) *A1SetFloorFrame {
   b, err := hex.DecodeString(macAddr)
   if err != nil {
      return nil
   }
   if len(b) != 6 {
      return nil
   }
   var mac [6]byte
   copy(mac[:], b)
   //b, err = hex.DecodeString(floor)
   frame := &A1SetFloorFrame{
      Header:     [16]byte{0x45, 0x4c, 0x45, 0x56, 0x41, 0x54, 0x4f, 0x52, 0x2d, 0x53, 0x45, 0x4e, 0x53, 0x4f, 0x52, 0x00},
      Command:    0x0c,
      MAC:        mac,
      DataLength: 1,
      Data:       []byte{floor},
   }
   frame.Checksum = calculateCRC16(frame.toBytes(false)) // Calculate CRC without including the checksum itself
   return frame
}
func A1SendFrame(frame *A1SetFloorFrame, address string) error {
   conn, err := net.Dial("udp", address)
   if err != nil {
      return err
   }
   defer conn.Close()
   // Send frame
   _, err = conn.Write(frame.toBytes(true))
   return err
}
// 对接网络视频字符叠加器,接收udp发送的楼层信息, 更新device地址
func NVCSA1UDPServer() {
@@ -67,7 +187,28 @@
   logger.Info("UDP server listening on port %s...", port)
   var runState string
   rfidReader := rfid.NewReader(config.RFIDConf.DevName, 115200, 5)
   if config.RFIDConf.ReadFloor {
      defer rfidReader.CloseSerial()
      err := rfidReader.OpenSerial()
      if err != nil {
         logger.Error("open rfid. %s", err.Error())
         return
      }
      ctx, _ := context.WithCancel(context.Background())
      go func() {
         err := rfidReader.ReadEPCData(ctx)
         if err != nil {
            logger.Error("rfid read. %s", err.Error())
         }
      }()
   }
   var lastFloor int
   var lastSaveTime int64
   // 无限循环等待接收数据
   for {
      // 创建一个缓冲区来存储接收的数据
@@ -97,51 +238,71 @@
         continue
      }
      // 记录电梯运行状态, 只记录上行和下行
      if data.Elevator[0].Status.RunDir == RunUp {
         runState = "上"
      } else if data.Elevator[0].Status.RunDir == RunDown {
         runState = "下"
      elevator := data.Elevator[0]
      var runStateStr string
      if config.NVCSConf.RunState {
         if elevator.Status.RunDir == RunUp {
            runStateStr = "上"
         } else if elevator.Status.RunDir == RunDown {
            runStateStr = "下"
         }
      }
      // 已到最下层
      if data.Elevator[0].Status.Floor == 0 {
         runState = "上"
      }
      if data.Elevator[0].Status.Floor == data.Elevator[0].Status.TotalFloors {
         runState = "下"
      if !config.NVCSConf.RunState {
         runStateStr = ""
      }
      // 设置osd  格式 "1F上 固 枪"
      if config.NVCSConf.OSD != "" {
         floorText := fmt.Sprintf("%s%s %s", data.Elevator[0].Status.FloorName, runState, config.NVCSConf.OSD)
      // correct floor when elevator stopped.
      //if elevator.Status.RunDir == 0 {
      //   go A1CorrectFloor()
      //} else {
      //   RunningCorrectTaskId = ""
      //}
         // 调用hik api 将文字添加到osd的左下角
         AddFloorToOSD(floorText)
      }
      if data.Elevator[0].Status.RunDir > 0 {
      if lastFloor == elevator.Status.Floor && (time.Now().Unix()-lastSaveTime < 2) {
         continue
      }
      elevator := data.Elevator[0]
      lastFloor = elevator.Status.Floor
      lastSaveTime = time.Now().Unix()
      // 程序部署在设备端, 字符叠加器上报的名称允许为空. 在云端, 名称必须与摄像机相同
      if elevator.Name == "" {
         elevator.Name = "1"
      }
      var d = models.Positions{
         DeviceId:   elevator.Name,
         Pos:        elevator.Status.FloorName,
         CreateTime: time.Now().Unix(),
         TimeString: time.Now().Format("2006-01-02 15:04:05"),
      ElevatorRunState = elevator.Status.RunDir
      ElevatorRunFloor = elevator.Status.FloorName
      if config.RFIDConf.ReadFloor && rfidReader.EPCData != "" {
         ElevatorRunFloor, _ = rfid.Parse2Floor(rfidReader.EPCData)
         logger.Debug("rfid read epc floor %s", ElevatorRunFloor)
         if elevator.Status.RunDir == RunStop && ElevatorRunFloor != elevator.Status.FloorName {
            logger.Warn("A floor error has occurred rfid epc %s, nvcs floor %s", ElevatorRunFloor, elevator.Status.FloorName)
         }
      }
      err = d.Save()
      if err != nil {
         logger.Warn("Device position update error:%s", err.Error())
      // 设置osd  格式 "1F上 固 枪"
      if config.NVCSConf.OSD != "" {
         floorText := fmt.Sprintf("%s%s %s", ElevatorRunFloor, runStateStr, config.NVCSConf.OSD)
         // 调用hik api 将文字添加到osd的左下角
         AddFloorToOSD(floorText)
      }
      go func() {
         var d = models.Positions{
            DeviceId:   elevator.Name,
            Pos:        ElevatorRunFloor,
            RunDir:     ElevatorRunState,
            CreateTime: time.Now().Unix(),
            TimeString: time.Now().Format("2006-01-02 15:04:05"),
         }
         err = d.Save()
         if err != nil {
            logger.Warn("Device position update error:%s", err.Error())
         }
      }()
   }
}
@@ -211,13 +372,15 @@
func NVCSA2WebServer() {
   // 先获取总楼层数, 记录最高层
   GetA2ElevatorConfig()
   var runState string
   //GetA2ElevatorConfig()
   r := gin.Default()
   r.POST("/", func(c *gin.Context) {
      var req A2ElevatorData
      var runState string
      var iRunState int
      err := c.BindJSON(&req)
      if err != nil {
         c.JSON(http.StatusBadRequest, nil)
@@ -226,19 +389,30 @@
      logger.Debug("Received A2 report data %+v", req)
      // 记录电梯运行状态, 只记录上行和下行
      // 记录电梯运行状态
      if strings.Contains(req.State.Status, "上") {
         runState = "上"
         iRunState = RunUp
      } else if strings.Contains(req.State.Status, "下") {
         runState = "下"
         iRunState = RunDown
      }
      // 已到最下层
      if req.State.Floor == A2BottomFloor {
         runState = "上"
      }
      if req.State.Floor == A2TopFloor {
         runState = "下"
      ElevatorRunState = iRunState
      ElevatorRunFloor = req.State.Floor
      //// 已到最下层
      //if req.State.Floor == A2BottomFloor {
      //   runState = "上"
      //   iRunState = RunUp
      //}
      //if req.State.Floor == A2TopFloor {
      //   runState = "下"
      //   iRunState = RunDown
      //}
      if !config.NVCSConf.RunState {
         runState = ""
      }
      if config.NVCSConf.OSD != "" {
@@ -251,6 +425,74 @@
      var d = models.Positions{
         DeviceId:   req.Id,
         Pos:        req.State.Floor,
         RunDir:     iRunState,
         CreateTime: time.Now().Unix(),
         TimeString: time.Now().Format("2006-01-02 15:04:05"),
      }
      err = d.Save()
      if err != nil {
         logger.Warn("Device position update error:%s", err.Error())
      }
      c.JSON(http.StatusOK, "ok")
   })
   err := r.Run(fmt.Sprintf(":%s", config.NVCSConf.Port))
   if err != nil {
      logger.Warn("Start NVCS WebServer error, %s", err.Error())
   }
}
type A3ElevatorData struct {
   Id     string `json:"id"`
   Time   string `json:"time"`
   Total  int    `json:"total"`
   Status int    `json:"status"`
   Floor  int    `json:"floor"`
}
func NVCSA3WebServer() {
   r := gin.Default()
   r.POST("/", func(c *gin.Context) {
      var req A3ElevatorData
      var runState string
      var iRunState int
      err := c.BindJSON(&req)
      if err != nil {
         c.JSON(http.StatusBadRequest, nil)
         return
      }
      iRunState = req.Status
      ElevatorRunState = req.Status
      ElevatorRunFloor = fmt.Sprintf("%dF", req.Floor)
      logger.Debug("Received A2 report data %+v", req)
      // 记录电梯运行状态
      if iRunState == RunUp {
         runState = "上"
      } else if iRunState == RunDown {
         runState = "下"
      }
      if !config.NVCSConf.RunState {
         runState = ""
      }
      if config.NVCSConf.OSD != "" {
         floorText := fmt.Sprintf("%dF%s %s", req.Floor, runState, config.NVCSConf.OSD)
         // 调用hik api 将文字添加到osd的左下角
         AddFloorToOSD(floorText)
      }
      var d = models.Positions{
         DeviceId:   req.Id,
         Pos:        fmt.Sprintf("%dF", req.Floor),
         RunDir:     iRunState,
         CreateTime: time.Now().Unix(),
         TimeString: time.Now().Format("2006-01-02 15:04:05"),
      }
@@ -273,7 +515,12 @@
   if config.NVCSConf.Model == "A1" {
      go NVCSA1UDPServer()
   }
   if config.NVCSConf.Model == "A2" {
      go NVCSA2WebServer()
   }
   if config.NVCSConf.Model == "A3" {
      go NVCSA3WebServer()
   }
}