diff --git a/internal/common/pause.go b/internal/common/pause.go index 792710d..c94367d 100644 --- a/internal/common/pause.go +++ b/internal/common/pause.go @@ -2,6 +2,7 @@ package common import "sync" +// CtrlWait 待机暂停控制器 type CtrlWait struct { C chan int8 diff --git a/internal/routes/play.go b/internal/routes/play.go index 01fdc88..c44ffe3 100644 --- a/internal/routes/play.go +++ b/internal/routes/play.go @@ -19,15 +19,15 @@ func switchPoint(ctx context.Context, point int) leaf.HandlerFunc { switch point { case 2: // 镇水塔点位 return play.OnlyVideo + case 5: + // 登龙云台(激光秀) + return play.LaserShow case 10: // 10号点位(发卡机) return play.PushCard(ctx) case 11: // 11号点位(等待插卡) return play.WaitCard(ctx) - case 5: - // 登龙云台(激光秀) - return play.LaserShow default: return play.Default } diff --git a/internal/routes/standby/laser.go b/internal/routes/standby/laser.go new file mode 100644 index 0000000..ef60288 --- /dev/null +++ b/internal/routes/standby/laser.go @@ -0,0 +1,35 @@ +package standby + +import ( + "context" + "fmt" + "game-driver/config/game" + "game-driver/internal/schema" + "game-driver/pkg/oscx" + "go.uber.org/zap" +) + +func LaserShow(item schema.WaitItemModel) func(c context.Context) error { + cfg := (game.C).(game.LaserConfig) + + return func(c context.Context) error { + zap.S().Infoln("开始播放大型激光秀") + + o := oscx.New(cfg.Host, cfg.Port) + err := o.EnableLaserOutput() + if err != nil { + return fmt.Errorf("激光打开异常: %w", err) + } else { + defer zap.S().Infoln("大型激光秀播放结束:", item.Data) + defer o.DisableLaserOutput() + + err = o.StartCue(item.Data) + if err != nil { + return fmt.Errorf("播放大型激光节目异常: %w", err) + } + } + + <-c.Done() + return nil + } +} diff --git a/internal/routes/standby/tts.go b/internal/routes/standby/tts.go index 3539d90..56043df 100644 --- a/internal/routes/standby/tts.go +++ b/internal/routes/standby/tts.go @@ -7,7 +7,6 @@ import ( "game-driver/pkg/audio" "game-driver/pkg/tts" "go.uber.org/zap" - "time" ) func TTS(item schema.WaitItemModel) func(c context.Context) error { @@ -20,13 +19,8 @@ func TTS(item schema.WaitItemModel) func(c context.Context) error { zap.S().Infoln("播放待机 TTS 语音") defer zap.S().Infoln("结束待机 TTS 语音") - for { - audio.PlayWav(c, reader) - select { - case <-c.Done(): - return nil - case <-time.After(time.Duration(item.Interval) * time.Second): - } - } + audio.PlayWav(c, reader) + + return nil } } diff --git a/internal/routes/standby/video.go b/internal/routes/standby/video.go index d99d03d..eb2ca56 100644 --- a/internal/routes/standby/video.go +++ b/internal/routes/standby/video.go @@ -7,7 +7,6 @@ import ( "game-driver/pkg/utils" "game-driver/pkg/video" "go.uber.org/zap" - "time" ) func Video(item schema.WaitItemModel) func(c context.Context) error { @@ -23,16 +22,10 @@ func Video(item schema.WaitItemModel) func(c context.Context) error { utils.BlankOpen() defer utils.BlankClose() - for { - err := video.Play(c, path, local) - if err != nil { - return fmt.Errorf("视频播放异常: %w", err) - } - select { - case <-c.Done(): - return nil - case <-time.After(time.Duration(item.Interval) * time.Second): - } + err = video.Play(c, path, local) + if err != nil { + return fmt.Errorf("视频播放异常: %w", err) } + return nil } } diff --git a/internal/routes/standby_ctrl/time.go b/internal/routes/standby_ctrl/cron.go similarity index 95% rename from internal/routes/standby_ctrl/time.go rename to internal/routes/standby_ctrl/cron.go index 7a4e987..9907a02 100644 --- a/internal/routes/standby_ctrl/time.go +++ b/internal/routes/standby_ctrl/cron.go @@ -8,8 +8,8 @@ import ( "time" ) -// Time 时间控制器 -func Time(rootRules []cronrange.Rule, cron string, play func(c context.Context) error) func(c context.Context) error { +// Cron 时间控制器 +func Cron(rootRules []cronrange.Rule, cron string, play func(c context.Context) error) func(c context.Context) error { // 设定默认时间规则 if cron == "" { cron = "* * * *" diff --git a/internal/routes/standby_ctrl/device.go b/internal/routes/standby_ctrl/device.go new file mode 100644 index 0000000..fb41845 --- /dev/null +++ b/internal/routes/standby_ctrl/device.go @@ -0,0 +1,21 @@ +package standby_ctrl + +import ( + "context" + "game-driver/internal/common" + "go.uber.org/zap" +) + +func Device(d *common.Device, lock bool, play func(c context.Context) error) func(c context.Context) error { + return func(c context.Context) error { + if lock { + zap.S().Infoln("待机任务锁定设备") + defer zap.S().Infoln("待机任务解锁设备") + + d.Lock() + defer d.Unlock() + } + + return play(c) + } +} diff --git a/internal/routes/standby_ctrl/duration.go b/internal/routes/standby_ctrl/duration.go new file mode 100644 index 0000000..6006803 --- /dev/null +++ b/internal/routes/standby_ctrl/duration.go @@ -0,0 +1,23 @@ +package standby_ctrl + +import ( + "context" + "go.uber.org/zap" + "time" +) + +// Duration 持续时长控制器 +func Duration(duration int64, play func(c context.Context) error) func(c context.Context) error { + return func(c context.Context) error { + zap.S().Infoln("待机持续时长控制器: ", duration) + defer zap.S().Infoln("待机持续时长控制器结束: ", duration) + + if duration > 0 { + ctx, cancel := context.WithTimeout(c, time.Duration(duration)*time.Second) + defer cancel() + c = ctx + } + + return play(c) + } +} diff --git a/internal/routes/standby_ctrl/interval.go b/internal/routes/standby_ctrl/interval.go new file mode 100644 index 0000000..b1a6dba --- /dev/null +++ b/internal/routes/standby_ctrl/interval.go @@ -0,0 +1,34 @@ +package standby_ctrl + +import ( + "context" + "go.uber.org/zap" + "time" +) + +func Interval(interval int64, play func(c context.Context) error) func(c context.Context) error { + return func(c context.Context) error { + zap.S().Infoln("待机间隔控制器: ", interval) + defer zap.S().Infoln("待机间隔控制器结束: ", interval) + + for { + err := play(c) + if err != nil { + zap.S().Errorln("执行后续操作异常: ", err) + } + if interval > 0 { + select { + case <-c.Done(): + return nil + case <-time.After(time.Duration(interval) * time.Second): + } + } else { + select { + case <-c.Done(): + return nil + default: + } + } + } + } +} diff --git a/internal/routes/wait.go b/internal/routes/wait.go index a296c14..17b895e 100644 --- a/internal/routes/wait.go +++ b/internal/routes/wait.go @@ -13,7 +13,8 @@ import ( "sync" ) -func WaitAction(ctrl *common.CtrlWait) leaf.HandlerFunc { +// WaitAction 待机任务,支持音乐、TTS、继电器、视频、网页、投影仪、大型激光秀 ctrl +func WaitAction(ctrl *common.CtrlWait, device *common.Device) leaf.HandlerFunc { ps := common.NewPauseSub(ctrl) return func(c *leaf.Context) { @@ -49,7 +50,15 @@ func WaitAction(ctrl *common.CtrlWait) leaf.HandlerFunc { if f == nil { return } - f = standby_ctrl.Time(rules, item.Cron, f) + f = standby_ctrl.Duration(item.Duration, f) + if f == nil { + return + } + f = standby_ctrl.Device(device, item.Lock, f) + if f == nil { + return + } + f = standby_ctrl.Interval(item.Interval, f) if f == nil { return } @@ -57,6 +66,10 @@ func WaitAction(ctrl *common.CtrlWait) leaf.HandlerFunc { if f == nil { return } + f = standby_ctrl.Cron(rules, item.Cron, f) + if f == nil { + return + } e := f(c) if e != nil { zap.S().Errorf("%s异常: %s\n", title, e) @@ -75,9 +88,11 @@ func WaitAction(ctrl *common.CtrlWait) leaf.HandlerFunc { case schema.WaitVideo: handleItem("视频待机控制", item, standby.Video(item)) case schema.WaitWeb: - handleItem("视频待机控制", item, standby.Web(item)) + handleItem("网页待机控制", item, standby.Web(item)) case schema.WaitPJLink: - handleItem("视频待机控制", item, standby.PJLink(item)) + handleItem("投影仪待机控制", item, standby.PJLink(item)) + case schema.WaitLaserShow: + handleItem("大型激光秀控制", item, standby.LaserShow(item)) default: zap.S().Infof("不支持的类型: %d\n", item.Type) } diff --git a/internal/schema/wait.go b/internal/schema/wait.go index d1b8692..2d4f5d9 100644 --- a/internal/schema/wait.go +++ b/internal/schema/wait.go @@ -9,14 +9,17 @@ const ( WaitRelay WaitWeb WaitPJLink + WaitLaserShow ) type WaitItemModel struct { - Cron string `json:"cron"` - Type WaitType `json:"type"` - Data string `json:"data"` - Interval int64 `json:"interval"` - Pause bool `json:"pause"` + Cron string `json:"cron"` // 时间规则 + Type WaitType `json:"type"` // 类型 + Data string `json:"data"` // 执行数据 + Duration int64 `json:"duration"` // 持续时长 + Interval int64 `json:"interval"` // 间隔时间 + Pause bool `json:"pause"` // 是否暂停 + Lock bool `json:"lock"` // 是否锁定 } type WaitModel struct { diff --git a/internal/server.go b/internal/server.go index ef82b57..84eb41a 100644 --- a/internal/server.go +++ b/internal/server.go @@ -155,7 +155,7 @@ func Run() { middleware.PayloadJSON[schema.WaitModel](), middleware.Unique(common.GlobalBgStopper), middleware.EmergencyStop(common.GlobalBgStopper), - routes.WaitAction(common.PassCtrl), + routes.WaitAction(common.PassCtrl, device), ) // 处理指令 router.RegisterHandler(topicPrefix+"command", diff --git a/json.md b/json.md index c5434f9..cce0340 100644 --- a/json.md +++ b/json.md @@ -20,6 +20,24 @@ url: `server/wushan/0/play` } ``` +## 点位1(击缶台) + +### 待机 + +url: `server/wushan/1/wait` + +```json +{ + "cron": "08:00-22:00 * * *", + "items": [ + { + "type": 3, + "data": "/dev/ttyUSB0" + } + ] +} +``` + ### Game url: `server/wushan/1/play` @@ -140,7 +158,14 @@ url: `server/wushan/5/wait` "cron": "08:00-22:00 * * *", "items": [ { - "data": "file://./三峡龙脊BGM.mp3" + "data": "file://./三峡龙脊BGM.mp3", + "pause": true + }, + { + "cron": "12:05-12:10 * * *", + "type": 6, + "data": "wushan", + "lock": true } ] } diff --git a/readme.md b/readme.md index 6813eee..517a664 100644 --- a/readme.md +++ b/readme.md @@ -136,10 +136,14 @@ Payload: "cron": "17:20-21:35 1-5 * *", // 间隔时间(s), 类型>2时, 该项无效, default 0 "interval": 0, - // 事件类型(0: 音频; 1: 视频; 2: TTS; 3: 继电器; 4: 网页), default 0 + // 持续时长(s), 待机任务执行时持续的时长。为 0 表示 音频、视频、TTS 按播放时长,继电器、网页、投影仪、激光秀持续整个时间段。 default 0 + "duration": 0, + // 事件类型(0: 音频; 1: 视频; 2: TTS; 3: 继电器; 4: 网页; 5: 投影仪; 6: 激光秀;), default 0 "type": 2, // Game 指令执行时是否暂停(默认 false) "pause": true, + // 待机任务执行时,是否锁定设备(默认 false) + "lock": false, // 事件数据(TTS为文字, 继电器为端口号, 其他都为地址链接。支持 file:// 本地文件地址、 http(s):// 远程文件地址) "data": "", },