加入 pjlink 控制
This commit is contained in:
16
cmd/root.go
16
cmd/root.go
@@ -7,6 +7,7 @@ import (
|
||||
"errors"
|
||||
"game-driver/config"
|
||||
"game-driver/config/game"
|
||||
"game-driver/config/wait"
|
||||
"game-driver/internal"
|
||||
"io/fs"
|
||||
"log"
|
||||
@@ -70,11 +71,20 @@ func initConfig() {
|
||||
}
|
||||
|
||||
// 初始化游戏节点配置
|
||||
game.G = game.NewConfig(config.C.Point)
|
||||
if game.G != nil { // 如果需要游戏配置
|
||||
err = viper.UnmarshalKey("game", &game.G)
|
||||
game.C = game.NewConfig(config.C.Point)
|
||||
if game.C != nil { // 如果需要游戏配置
|
||||
err = viper.UnmarshalKey("game", &game.C)
|
||||
if err != nil {
|
||||
log.Panicln("unmarshal game config failed: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化游戏节点待机时配置
|
||||
wait.C = wait.NewConfig(config.C.Point)
|
||||
if wait.C != nil { // 如果需要游戏配置
|
||||
err = viper.UnmarshalKey("wait", &wait.C)
|
||||
if err != nil {
|
||||
log.Panicln("unmarshal wait config failed: ", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
config.yml
10
config.yml
@@ -4,6 +4,11 @@ relay:
|
||||
maxTimeout: 60 # 单位 s,必须大于 0
|
||||
log:
|
||||
level: debug
|
||||
tencentCLS:
|
||||
endpoint:
|
||||
secretID:
|
||||
secretKey:
|
||||
topicID:
|
||||
file:
|
||||
filename: logs/app.log
|
||||
maxSize: 10
|
||||
@@ -20,6 +25,11 @@ aliyun:
|
||||
volume: 100 # 音量,取值范围:0~100
|
||||
voice: zhifeng_emo # 发音人
|
||||
speechRate: 50 # 语速,取值范围:-500~500
|
||||
wait:
|
||||
ip:
|
||||
port:
|
||||
password:
|
||||
id:
|
||||
#game:
|
||||
# addr: /dev/ttyUSB0 # 点位 11 的串口地址
|
||||
# pushGroups: # 点位 10 的发卡器配置
|
||||
|
||||
@@ -13,4 +13,4 @@ func NewConfig(point int) Config {
|
||||
}
|
||||
}
|
||||
|
||||
var G Config
|
||||
var C Config
|
||||
|
||||
8
config/wait/pjlink.go
Normal file
8
config/wait/pjlink.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package wait
|
||||
|
||||
type PJLink struct {
|
||||
Ip string
|
||||
Port string
|
||||
Password string
|
||||
Id string
|
||||
}
|
||||
14
config/wait/wait.go
Normal file
14
config/wait/wait.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package wait
|
||||
|
||||
type Config any
|
||||
|
||||
func NewConfig(point int) Config {
|
||||
switch point {
|
||||
case 2:
|
||||
return PJLink{}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var C Config
|
||||
@@ -28,7 +28,7 @@ type ResponseBody struct {
|
||||
}
|
||||
|
||||
func PushCard(ctx context.Context) leaf.HandlerFunc {
|
||||
g := (game.G).(game.ConfigPush)
|
||||
g := (game.C).(game.ConfigPush)
|
||||
|
||||
readers := make([]card_reader.Reader, len(g.PushGroups))
|
||||
devices := make([]*card_pusher.Device, len(g.PushGroups))
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
func WaitCard(ctx context.Context) leaf.HandlerFunc {
|
||||
g := (game.G).(game.ConfigWait)
|
||||
g := (game.C).(game.ConfigWait)
|
||||
|
||||
reader, err := card_reader.NewReader(g.Addr)
|
||||
if err != nil {
|
||||
|
||||
@@ -3,11 +3,13 @@ package routes
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"game-driver/config/wait"
|
||||
"game-driver/internal/middleware"
|
||||
"game-driver/internal/schema"
|
||||
"game-driver/leaf"
|
||||
"game-driver/pkg/audio"
|
||||
"game-driver/pkg/browser"
|
||||
"game-driver/pkg/pjlink"
|
||||
"game-driver/pkg/relay"
|
||||
"game-driver/pkg/tts"
|
||||
"game-driver/pkg/utils"
|
||||
@@ -151,6 +153,13 @@ func WaitAction(c *leaf.Context) {
|
||||
defer wait.Done()
|
||||
runAction(c, item, rules, webAction)
|
||||
}()
|
||||
case schema.WaitPJLink:
|
||||
// 执行投影仪打开
|
||||
wait.Add(1)
|
||||
go func() {
|
||||
defer wait.Done()
|
||||
runAction(c, item, rules, pjlinkAction)
|
||||
}()
|
||||
default:
|
||||
zap.S().Infof("不支持的类型: %d\n", item.Type)
|
||||
}
|
||||
@@ -255,3 +264,29 @@ func webAction(c context.Context, item schema.WaitItemModel) error {
|
||||
browser.OpenApp(c, item.Data)
|
||||
return nil
|
||||
}
|
||||
|
||||
func pjlinkAction(c context.Context, _ schema.WaitItemModel) error {
|
||||
cfg := (wait.C).(wait.PJLink)
|
||||
pjc := pjlink.NewClient(cfg.Ip, cfg.Port, cfg.Password, cfg.Id)
|
||||
err := pjc.Connect()
|
||||
if err != nil {
|
||||
return fmt.Errorf("连接 PJLink 设备异常: %w", err)
|
||||
}
|
||||
defer pjc.Close()
|
||||
|
||||
zap.S().Infoln("打开待机投影仪")
|
||||
err = pjc.PowerOn()
|
||||
if err != nil {
|
||||
return fmt.Errorf("打开投影仪异常: %w", err)
|
||||
}
|
||||
|
||||
<-c.Done()
|
||||
|
||||
zap.S().Infoln("关闭待机投影仪")
|
||||
err = pjc.PowerOff()
|
||||
if err != nil {
|
||||
return fmt.Errorf("关闭投影仪异常: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ const (
|
||||
WaitTTS
|
||||
WaitRelay
|
||||
WaitWeb
|
||||
WaitPJLink
|
||||
)
|
||||
|
||||
type WaitItemModel struct {
|
||||
|
||||
38
json.md
38
json.md
@@ -5,7 +5,7 @@ url: `server/wushan/2/wait`
|
||||
|
||||
```json
|
||||
{
|
||||
"cron": "* * * *",
|
||||
"cron": "08:00-22:00 * * *",
|
||||
"items": [
|
||||
{
|
||||
"data": "file://./三峡龙脊BGM.mp3"
|
||||
@@ -35,18 +35,36 @@ url: `server/wushan/2/play`
|
||||
}
|
||||
```
|
||||
|
||||
### STOP 待机
|
||||
## 点位5(登龙云台)
|
||||
### 待机
|
||||
|
||||
url: `server/wushan/2/command`
|
||||
url: `server/wushan/5/wait`
|
||||
|
||||
```text
|
||||
stop-bg
|
||||
```json
|
||||
{
|
||||
"cron": "08:00-22:00 * * *",
|
||||
"items": [
|
||||
{
|
||||
"data": "file://./三峡龙脊BGM.mp3"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### STOP GAME
|
||||
### Game
|
||||
|
||||
url: `server/wushan/2/command`
|
||||
url: `server/wushan/5/play`
|
||||
|
||||
```text
|
||||
stop
|
||||
```
|
||||
```json
|
||||
{
|
||||
"bgm": "file://./青云龙台.mp3",
|
||||
"power": true,
|
||||
"volume":1,
|
||||
"tts": {
|
||||
"start": "刘佳勇者,恭喜你成功通关!"
|
||||
},
|
||||
"game": {
|
||||
"wait": 10
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
141
pkg/pjlink/pjlink.go
Normal file
141
pkg/pjlink/pjlink.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package pjlink
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/md5"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrAuthFailed = errors.New("授权验证失败")
|
||||
ErrCommandError = errors.New("命令执行异常")
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
Host string
|
||||
Port string
|
||||
Password string
|
||||
ID string
|
||||
conn net.Conn
|
||||
}
|
||||
|
||||
func NewClient(host, port, password, id string) *Client {
|
||||
return &Client{
|
||||
Host: host,
|
||||
Port: port,
|
||||
Password: password,
|
||||
ID: id,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Connect() error {
|
||||
address := net.JoinHostPort(c.Host, c.Port)
|
||||
conn, err := net.DialTimeout("tcp", address, 5*time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.conn = conn
|
||||
|
||||
// Read challenge
|
||||
reader := bufio.NewReader(c.conn)
|
||||
response, err := reader.ReadString('\r')
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
// Handle authentication
|
||||
if strings.HasPrefix(response, "PJLINK 1") {
|
||||
if c.Password == "" {
|
||||
c.Close()
|
||||
return ErrAuthFailed
|
||||
}
|
||||
|
||||
challenge := strings.TrimSpace(strings.Split(response, " ")[2])
|
||||
authString := fmt.Sprintf("%s%s", challenge, c.Password)
|
||||
hashed := md5.Sum([]byte(authString))
|
||||
authHash := fmt.Sprintf("%x", hashed)
|
||||
|
||||
_, err = fmt.Fprintf(c.conn, "%s\r", authHash)
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
authResponse, err := reader.ReadString('\r')
|
||||
if err != nil || !strings.Contains(authResponse, "OK") {
|
||||
c.Close()
|
||||
return ErrAuthFailed
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) sendCommand(command string) (string, error) {
|
||||
if c.conn == nil {
|
||||
return "", errors.New("not connected")
|
||||
}
|
||||
|
||||
fullCommand := fmt.Sprintf("%%%s%s\r", c.ID, command)
|
||||
_, err := c.conn.Write([]byte(fullCommand))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(c.conn)
|
||||
response, err := reader.ReadString('\r')
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Remove prefix and parse response
|
||||
parts := strings.SplitN(response, "=", 2)
|
||||
if len(parts) < 2 {
|
||||
return "", ErrCommandError
|
||||
}
|
||||
|
||||
result := strings.TrimSpace(parts[1])
|
||||
if result == "ERR1" {
|
||||
return "", ErrAuthFailed
|
||||
} else if result == "ERR2" {
|
||||
return "", ErrCommandError
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *Client) PowerOn() error {
|
||||
response, err := c.sendCommand("POWR 1")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if response != "OK" {
|
||||
return fmt.Errorf("unexpected response: %s", response)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) PowerOff() error {
|
||||
response, err := c.sendCommand("POWR 0")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if response != "OK" {
|
||||
return fmt.Errorf("unexpected response: %s", response)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) Close() {
|
||||
if c.conn != nil {
|
||||
c.conn.Close()
|
||||
c.conn = nil
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user