Bläddra i källkod

优化抽象接口结构

longan 11 månader sedan
förälder
incheckning
6495003f93
8 ändrade filer med 147 tillägg och 125 borttagningar
  1. 1 0
      config.yaml
  2. 27 29
      lc/IDevice.go
  3. 1 1
      lc/event.go
  4. 8 4
      lc/loudspeaker.go
  5. 92 73
      lc/screen.go
  6. 11 13
      lc/server.go
  7. 6 5
      main.go
  8. 1 0
      util/config.go

+ 1 - 0
config.yaml

@@ -11,5 +11,6 @@ outputDevices:
   -
     name: "主路1输出设备"
     screen_ip: "192.168.110.200"
+    screen_port: "5000"
     loudspeaker_ip: "...地址"
     branch: 1

+ 27 - 29
lc/IDevice.go

@@ -1,12 +1,10 @@
 package lc
 
 import (
-	"github.com/sirupsen/logrus"
 	"lc-smartX/util"
-	"net"
-	"time"
 )
 
+// CorrectTimer 校时器接口
 type CorrectTimer interface {
 	Correct()
 }
@@ -17,44 +15,44 @@ func DoCorrectTime(a any) {
 	}
 }
 
-// IDevice 抽象设备,包含屏和喇叭
+// ReConnector 重连器接口
+type ReConnector interface {
+	Reconnect()
+}
+
+func DoReconnect(a any) {
+	if c, ok := a.(ReConnector); ok {
+		c.Reconnect()
+	}
+}
+
+// IDevice 抽象设备,包含屏和扬声器
 type IDevice interface {
 	// Call 通知输出设备,输出信息。屏显示来车警告信息,扬声器语音提示
 	Call()
-	// Back 屏显示原来的无车提醒信息
-	Back()
-	ReConnect()
-	// Correct time 校时
+	// Rollback 屏回滚到初始状态
+	Rollback()
+	ReConnector
 	CorrectTimer
 }
 
 type IntersectionDevice struct {
 	Info util.OutputDevice
-	S    Screen
-	L    IpCast
+	S    Screener
+	L    Loudspeaker
 }
 
 func (id *IntersectionDevice) Call() {
 	id.L.Speak()
-	id.S.Display()
-}
-func (id *IntersectionDevice) Back() {
-	id.S.Back()
-}
-
-func (id *IntersectionDevice) ReConnect() {
-	//重连屏
-	if !id.S.IsLive {
-		conn, err := net.DialTimeout("tcp", id.Info.ScreenIp+":5000", 5*time.Second)
-		if err != nil {
-			logrus.WithFields(map[string]interface{}{"设备名": id.Info.Name, "IP": id.Info.ScreenIp, "error": err}).Error("屏重连接失败!")
-			return
-		}
-		logrus.WithFields(map[string]interface{}{"设备名": id.Info.Name, "IP": id.Info.ScreenIp}).Info("屏重连接成功!")
-		id.S.Conn = conn
-		id.S.IsLive = true
-	}
-	//todo 重连loudspeaker
+	id.S.Display("P001")
+}
+func (id *IntersectionDevice) Rollback() {
+	id.S.Display("P000")
+}
+
+func (id *IntersectionDevice) Reconnect() {
+	DoReconnect(id.S)
+	DoReconnect(id.L)
 }
 
 func (id *IntersectionDevice) Correct() {

+ 1 - 1
lc/event.go

@@ -54,7 +54,7 @@ func (s *Server) Callback(ip string) {
 		return
 	}
 	notifier.Notify()
-	logrus.Debugf("camera %s Callback", ip)
+	logrus.Debugf("camera [%s] Callback", ip)
 }
 
 func handler(w http.ResponseWriter, r *http.Request) {

+ 8 - 4
lc/loudspeaker.go

@@ -4,6 +4,7 @@ import (
 	"fmt"
 )
 
+// Loudspeaker 扬声器接口
 type Loudspeaker interface {
 	Speak()
 }
@@ -11,10 +12,13 @@ type Loudspeaker interface {
 type IpCast struct {
 }
 
+func NewIpCast() *IpCast {
+	return &IpCast{}
+}
+
 func (ip IpCast) Speak() {
-	fmt.Println("语音")
+	fmt.Println("来车了来车了!")
 }
 
-//func (ip IpCast) CorrectTime(t time.Time) {
-//
-//}
+//func (ip IpCast) CorrectTime() {}
+//func (ip IpCast)Reconnect(){}

+ 92 - 73
lc/screen.go

@@ -10,66 +10,92 @@ import (
 	"time"
 )
 
+// Screener 屏接口
 type Screener interface {
-	Display()
+	Display(string)
 }
 
 type Screen struct {
 	Name      string
-	Conn      net.Conn
-	IsLive    bool
+	Addr      string
+	conn      net.Conn
+	liveState bool
 	StateInfo *bx.StateInfo //状态信息
 	Params    *bx.Params    //屏参
 }
 
-func NewScreen(name string, conn net.Conn) Screen {
-	s := Screen{
+func NewScreen(name string, ip, port string) *Screen {
+	s := &Screen{
 		Name:      name,
-		Conn:      conn,
-		IsLive:    true,
+		Addr:      fmt.Sprintf("%s:%s", ip, port),
 		StateInfo: &bx.StateInfo{},
 		Params:    &bx.Params{},
 	}
-	state := s.State()
-	s.StateInfo.Parse(state.Data)
-	params := s.Param()
-	s.Params.Parse(params.Data)
+	s.Reconnect()
 	return s
 }
 
-func (s *Screen) Display() {
-	if !s.IsLive {
+func (s *Screen) Display(str string) {
+	if !s.getLiveState() {
 		return
 	}
-	s.Lock(1, "P001")
-}
-
-func (s *Screen) Back() {
-	s.Lock(1, "P000")
+	s.Lock(1, str)
 }
 
 // Correct 校正时间
 func (s *Screen) Correct() {
-	now := time.Now()
-	if !s.IsLive {
+	if !s.getLiveState() {
 		return
 	}
+	now := time.Now()
 	cmd := bx.NewBxCmdSystemClockCorrect(now)
 	data := bx.NewBxDataPackCmd(cmd)
 	s.send(data.Pack())
 }
 
+// Reconnect 重连
+func (s *Screen) Reconnect() {
+	if s.getLiveState() {
+		return
+	}
+	conn, err := net.DialTimeout("tcp", s.Addr, 5*time.Second)
+	if err != nil {
+		logrus.Error(s.Name, "-", s.Addr, "[屏]重连接失败! error:", err)
+		return
+	}
+	logrus.Info(s.Name, "-", s.Addr, "[屏]连接成功!")
+	s.setConn(conn)
+	//读取屏信息
+	state := s.State()
+	s.StateInfo.Parse(state.Data)
+	params := s.Param()
+	s.Params.Parse(params.Data)
+}
+
+func (s *Screen) getLiveState() bool {
+	return s.liveState
+}
+
+func (s *Screen) setConn(conn net.Conn) {
+	s.conn = conn
+	s.liveState = true
+}
+
+// 给屏发送数据
 func (s *Screen) send(data []byte) {
-	if !s.IsLive {
+	if !s.getLiveState() {
 		return
 	}
-	_, err := s.Conn.Write(data)
+	_, err := s.conn.Write(data)
 	if err != nil {
 		logrus.WithFields(map[string]interface{}{"设备名": s.Name}).Error("tcp write error:", err)
-		s.IsLive = false
+		s.liveState = false
 	}
 }
 
+//以下对协议进行封装
+//↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
+
 type Color byte
 
 const (
@@ -81,24 +107,17 @@ const (
 	LightBlue
 	LightPurple
 	White
-	None
 )
 
-// TextRam dispMode显示方式
-// 0x01——静止显示
-// 0x02——快速打出
-// 0x03——向左移动
-// 0x04——向右移动
-// 0x05——向上移动
-// 0x06——向下移动
+// TextRam 发送动态区节目
 func (s *Screen) TextRam(ff FlashFile) {
-	if !s.IsLive {
+	if !s.getLiveState() {
 		return
 	}
 	var areas []bx.BxArea
 	encoder := simplifiedchinese.GB18030.NewEncoder()
 	var bytes []byte
-	if ff.color == None || ff.color == Default {
+	if ff.color == Default {
 		bytes, _ = encoder.Bytes([]byte(ff.msg))
 	} else {
 		bytes, _ = encoder.Bytes([]byte("\\C" + strconv.Itoa(int(ff.color)) + ff.msg))
@@ -117,7 +136,7 @@ func (s *Screen) TextRam(ff FlashFile) {
 
 // DelRamText 删除动态区,不传删除所有
 func (s *Screen) DelRamText(numbers ...byte) {
-	if !s.IsLive {
+	if !s.getLiveState() {
 		return
 	}
 	cmd := bx.NewCmdDelDynamicArea(numbers)
@@ -143,24 +162,24 @@ const (
 	DefaultRunMode    RunMode = Loop //默认
 )
 
-type DispMode byte
+type DisplayMode byte
 
 const (
-	_               DispMode = iota
-	Static                   //0x01——静止显示
-	QuickPunch               //0x02——快速打出
-	MoveLeft                 //0x03——向左移动
-	MoveRight                //0x04——向右移动
-	MoveUp                   //0x05——向上移动
-	MoveDown                 //0x06——向下移动
-	DefaultDispMode DispMode = Static
+	_                  DisplayMode = iota
+	Static                         //0x01——静止显示
+	QuickPunch                     //0x02——快速打出
+	MoveLeft                       //0x03——向左移动
+	MoveRight                      //0x04——向右移动
+	MoveUp                         //0x05——向上移动
+	MoveDown                       //0x06——向下移动
+	DefaultDisplayMode DisplayMode = Static
 )
 
 type FlashFile struct {
 	msg      string
 	color    Color
 	runMode  RunMode
-	dispMode DispMode
+	dispMode DisplayMode
 	originX  uint16
 	originY  uint16
 	width    uint16
@@ -171,9 +190,9 @@ func (ft *FlashFile) SetMsg(msg string, color Color) {
 	ft.msg = msg
 	ft.color = color
 }
-func (ft *FlashFile) SetMode(runMode RunMode, dispMode DispMode) {
+func (ft *FlashFile) SetMode(runMode RunMode, displayMode DisplayMode) {
 	ft.runMode = runMode
-	ft.dispMode = dispMode
+	ft.dispMode = displayMode
 }
 
 // SetOrigin xIsPixel=true表示x坐标为像素单位, =false表示以字节(8像素)为单位;y只有像素单位
@@ -196,19 +215,19 @@ func (ft *FlashFile) SetArea(w uint16, yIsPixel bool, h uint16) {
 	ft.width = w
 }
 
+// TextFlash 发送静态文件节目,掉电保存,文件名格式"P000","P001"
 func (s *Screen) TextFlash(ft FlashFile, isLogo bool) {
-	if !s.IsLive {
+	if !s.getLiveState() {
 		return
 	}
 	encoder := simplifiedchinese.GB18030.NewEncoder()
 	var bytes []byte
-	if ft.color == None || ft.color == Default {
+	if ft.color == Default {
 		bytes, _ = encoder.Bytes([]byte(ft.msg))
 	} else {
 		bytes, _ = encoder.Bytes([]byte("\\C" + strconv.Itoa(int(ft.color)) + ft.msg))
 	}
 	area := bx.NewBxAreaDynamic(0xff, byte(ft.runMode), byte(ft.dispMode), ft.originX, ft.originY, ft.width, ft.height, bytes, false)
-	//if s.FileNum
 	name := fmt.Sprintf("P%03d", s.StateInfo.ProgramNum)
 	if isLogo {
 		name = "LOGO"
@@ -232,6 +251,7 @@ func (s *Screen) TextFlash(ft FlashFile, isLogo bool) {
 	}
 }
 
+// Bitmap 发送自定义位图节目
 func (s *Screen) Bitmap(name string, bitmap []byte) {
 	file := bx.NewBitmapFile(name, bitmap)
 	cmd := file.NewCmd()
@@ -260,10 +280,7 @@ func (s *Screen) Lock(flag byte, name string) {
 	s.send(pack.Pack())
 }
 
-func (s *Screen) QueryFile() {
-
-}
-
+// DelFile 删除静态文件节目
 func (s *Screen) DelFile(delFiles ...string) {
 	cmd := bx.NewCmdDeleteFile(delFiles)
 	pack := bx.NewBxDataPackCmd(cmd)
@@ -276,6 +293,7 @@ func (s *Screen) DelFile(delFiles ...string) {
 	}
 }
 
+// DelText 删除动态区域节目
 func (s *Screen) DelText(delIds []byte) {
 	cmd := bx.NewBxCmdSendDynamicArea(nil)
 	cmd.SetDelAreaIds(delIds)
@@ -284,7 +302,7 @@ func (s *Screen) DelText(delIds []byte) {
 }
 
 func (s *Screen) TurnOnOff(onOff bool) {
-	if !s.IsLive {
+	if !s.getLiveState() {
 		return
 	}
 	cmd := bx.NewBxCmdTurnOnOff(onOff)
@@ -299,7 +317,7 @@ func (s *Screen) TurnOnOff(onOff bool) {
 //			{1349, 1355},
 //		}
 func (s *Screen) TimingSwitch(onOffSet [][2]uint64) {
-	if !s.IsLive {
+	if !s.getLiveState() {
 		return
 	}
 	cmd := bx.NewCmdTimingSwitch(onOffSet)
@@ -308,7 +326,7 @@ func (s *Screen) TimingSwitch(onOffSet [][2]uint64) {
 }
 
 func (s *Screen) CancelTimingSwitch() {
-	if !s.IsLive {
+	if !s.getLiveState() {
 		return
 	}
 	cmd := bx.NewCmdCancelTimingSwitch()
@@ -317,7 +335,7 @@ func (s *Screen) CancelTimingSwitch() {
 }
 
 func (s *Screen) State() *bx.BxResp {
-	if !s.IsLive {
+	if !s.getLiveState() {
 		return nil
 	}
 	cmd := bx.NewCmdState()
@@ -328,7 +346,7 @@ func (s *Screen) State() *bx.BxResp {
 }
 
 func (s *Screen) Param() *bx.BxResp {
-	if !s.IsLive {
+	if !s.getLiveState() {
 		return nil
 	}
 	cmd := bx.NewCmdReadParams()
@@ -342,28 +360,29 @@ func (s *Screen) Info() {
 	s.StateInfo.Print(s.Name)
 }
 
+// ReadResp 读取响应
 func (s *Screen) ReadResp() bx.BxResp {
 	var resp = make([]byte, 1024)
-	read, err := s.Conn.Read(resp)
+	read, err := s.conn.Read(resp)
 	if err != nil {
 		logrus.Error("读数据错误:", err)
-		s.IsLive = false
+		s.liveState = false
 		return bx.BxResp{}
 	}
 	var bxResp = bx.BxResp{}
 	parse := bxResp.Parse(resp, read)
-	if parse.IsAck() {
-		fmt.Println("response ACK")
-		fmt.Printf("原始响应数据:% 0x\n", resp[:read])
-		fmt.Println("解析响应数据:", parse)
-	} else if parse.IsInfo() {
-		fmt.Println("state ACK")
-		fmt.Printf("原始响应数据:% 0x\n", resp[:read])
-		fmt.Println("解析响应数据:", parse)
-	} else {
-		fmt.Println("response")
-		fmt.Printf("原始响应数据:% 0x\n", resp[:read])
-		fmt.Println("解析响应数据:", parse)
-	}
+	//if parse.IsAck() {
+	//	fmt.Println("response ACK")
+	//	fmt.Printf("原始响应数据:% 0x\n", resp[:read])
+	//	fmt.Println("解析响应数据:", parse)
+	//} else if parse.IsInfo() {
+	//	fmt.Println("state ACK")
+	//	fmt.Printf("原始响应数据:% 0x\n", resp[:read])
+	//	fmt.Println("解析响应数据:", parse)
+	//} else {
+	//	fmt.Println("response")
+	//	fmt.Printf("原始响应数据:% 0x\n", resp[:read])
+	//	fmt.Println("解析响应数据:", parse)
+	//}
 	return *parse
 }

+ 11 - 13
lc/server.go

@@ -1,7 +1,6 @@
 package lc
 
 import (
-	"github.com/sirupsen/logrus"
 	"lc-smartX/util"
 	"lc-smartX/util/gopool"
 	"time"
@@ -41,11 +40,11 @@ func (m MainNotifier) Notify() {
 type SubNotifier struct{ s *IntersectionServer }
 
 // Notify 支路来车,通知主路设备
-func (s SubNotifier) Notify() {
-	s.s.Sub.Reset(5 * time.Second)
-	if s.s.SubState != 1 {
-		s.s.SubState = 1
-		for _, v := range s.s.MainDevices {
+func (sub SubNotifier) Notify() {
+	sub.s.Sub.Reset(5 * time.Second)
+	if sub.s.SubState != 1 {
+		sub.s.SubState = 1
+		for _, v := range sub.s.MainDevices {
 			gopool.Go(v.Call)
 		}
 	}
@@ -58,41 +57,40 @@ func (is *IntersectionServer) Serve() {
 	for _, v := range util.Config.OutputDevices {
 		iDevice := &IntersectionDevice{
 			Info: v,
+			S:    NewScreen(v.Name, v.ScreenIp, v.ScreenPort),
+			L:    NewIpCast(),
 		}
-		iDevice.ReConnect() //初次连接
 		if iDevice.Info.Branch == 1 {
 			is.MainDevices = append(is.MainDevices, iDevice)
 		} else {
 			is.SubDevices = append(is.SubDevices, iDevice)
 		}
 	}
-	logrus.Info("主路输出设备列表:", is.MainDevices)
-	logrus.Info("支路输出设备列表:", is.SubDevices)
 	for {
 		select {
 		case <-is.Main.C: //检查主路状态->支路输出设备回到初始状态
 			for _, v := range is.SubDevices {
 				if is.MainState == 1 {
-					gopool.Go(v.Back)
+					gopool.Go(v.Rollback)
 				}
 			}
 			is.MainState = 0
 		case <-is.Sub.C: //检查支路状态->主路输出设备作出响应
 			for _, v := range is.MainDevices {
 				if is.SubState == 1 {
-					gopool.Go(v.Back)
+					gopool.Go(v.Rollback)
 				}
 			}
 			is.SubState = 0
 		case <-is.ReTicker.C: //每19s检查并尝试重连
 			gopool.Go(func() {
 				for _, v := range is.MainDevices {
-					v.ReConnect()
+					gopool.Go(v.Reconnect)
 				}
 			})
 			gopool.Go(func() {
 				for _, v := range is.SubDevices {
-					v.ReConnect()
+					gopool.Go(v.Reconnect)
 				}
 			})
 		}

+ 6 - 5
main.go

@@ -12,12 +12,13 @@ func main() {
 	//等事件服务先启动
 	time.Sleep(1 * time.Second)
 	is := &lc.IntersectionServer{
-		Main:     time.NewTicker(5 * time.Second),
-		Sub:      time.NewTicker(5 * time.Second),
-		ReTicker: time.NewTicker(19 * time.Second),
+		Main:     time.NewTicker(5 * time.Second),  //主路状态回滚
+		Sub:      time.NewTicker(5 * time.Second),  //支路状态回滚
+		ReTicker: time.NewTicker(19 * time.Second), //重连
 	}
 	lc.StartSmartXServer(is)
 
+	//先用以下示例将静态节目发送至屏,P000为初始节目,P001为来车警示节目,LOGO为开机节目
 	//单个屏设备连接不能并发发送数据,未作实现
 	//conn, err := net.Dial("tcp", "192.168.110.200:5000")
 	//if err != nil {
@@ -39,7 +40,7 @@ func main() {
 	//发送flash文件节目
 	//file := lc.FlashFile{}
 	//file.SetMsg("龙弛智慧", lc.Green)
-	//file.SetMode(lc.DefaultRunMode, lc.DefaultDispMode)
+	//file.SetMode(lc.DefaultRunMode, lc.DefaultDisplayMode)
 	//file.SetOrigin(0, true, 0)
 	//file.SetArea(64, true, 16)
 	//client.TextFlash(file, true)
@@ -57,7 +58,7 @@ func main() {
 	//发送动态区
 	//file1 := lc.FlashFile{}
 	//file1.SetMsg("动态", lc.Blue)
-	//file1.SetMode(lc.DefaultRunMode, lc.DefaultDispMode)
+	//file1.SetMode(lc.DefaultRunMode, lc.DefaultDisplayMode)
 	//file1.SetOrigin(32, true, 0)
 	//file1.SetArea(32, true, 16)
 	//client.TextFlash(file1)

+ 1 - 0
util/config.go

@@ -35,6 +35,7 @@ type hikServer struct {
 type OutputDevice struct {
 	Name          string `yaml:"name"`
 	ScreenIp      string `yaml:"screen_ip"`
+	ScreenPort    string `yaml:"screen_port"`
 	LoudspeakerIp string `yaml:"loudspeaker_ip"`
 	Branch        byte   `yaml:"branch"`
 }