修复投影仪控制
This commit is contained in:
36
go.mod
36
go.mod
@@ -3,26 +3,27 @@ module game-driver
|
||||
go 1.23.2
|
||||
|
||||
require (
|
||||
github.com/adrg/libvlc-go/v3 v3.1.6
|
||||
github.com/aliyun/alibabacloud-nls-go-sdk v1.1.1
|
||||
github.com/eclipse/paho.golang v0.22.0
|
||||
github.com/go-pkgz/cronrange v0.2.0
|
||||
github.com/go-rod/rod v0.116.2
|
||||
github.com/gopxl/beep/v2 v2.1.0
|
||||
github.com/grid-x/modbus v0.0.0-20241004123532-f6c6fb5201b3
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/gopxl/beep/v2 v2.1.1
|
||||
github.com/grid-x/modbus v0.0.0-20250219144522-2b18d136199f
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/spf13/viper v1.19.0
|
||||
github.com/tencentcloud/tencentcloud-cls-sdk-go v1.0.11
|
||||
github.com/warthog618/go-gpiocdev v0.9.1
|
||||
go.uber.org/zap v1.27.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/adrg/libvlc-go/v3 v3.1.6 // indirect
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.76 // indirect
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.92 // indirect
|
||||
github.com/ebitengine/oto/v3 v3.3.2 // indirect
|
||||
github.com/ebitengine/purego v0.8.1 // indirect
|
||||
github.com/ebitengine/purego v0.8.2 // indirect
|
||||
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/grid-x/serial v0.0.0-20211107191517-583c7356b3aa // indirect
|
||||
github.com/hajimehoshi/go-mp3 v0.3.4 // indirect
|
||||
@@ -30,7 +31,7 @@ require (
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.17.2 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/magiconair/properties v1.8.9 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
@@ -39,27 +40,26 @@ require (
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/sagikazarmark/locafero v0.6.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.7.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/satori/go.uuid v1.2.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/afero v1.12.0 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/tencentcloud/tencentcloud-cls-sdk-go v1.0.11 // indirect
|
||||
github.com/ysmood/fetchup v0.2.4 // indirect
|
||||
github.com/ysmood/goob v0.4.0 // indirect
|
||||
github.com/ysmood/got v0.40.0 // indirect
|
||||
github.com/ysmood/gson v0.7.3 // indirect
|
||||
github.com/ysmood/leakless v0.9.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250228200357-dead58393ab7 // indirect
|
||||
golang.org/x/net v0.35.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
33
go.sum
33
go.sum
@@ -7,9 +7,12 @@ github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1376/go.mod h1:9CMdKNL3ynIGPpfTcdwTvIm8SGuAZYYC4jFVSSvE1YQ=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.76 h1:mg/+23+/gAw6zdxv9I5dPCj666WJPLk8S1nXm0dOumQ=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.76/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.92 h1:qespx4b6EexlXkvQUow9x0v1GnWUJYGU5FWYw3a4Wlg=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.92/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
|
||||
github.com/aliyun/alibabacloud-nls-go-sdk v1.1.1 h1:LjItoNZuu5xHlsByFo+kr3nGa4LRIESCGWhfurayxBg=
|
||||
github.com/aliyun/alibabacloud-nls-go-sdk v1.1.1/go.mod h1:4BDMUKpEaP/Ct79w0ozR0nbnEj49g1k3mrgX/IKG5I4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -19,6 +22,8 @@ github.com/ebitengine/oto/v3 v3.3.2 h1:VTWBsKX9eb+dXzaF4jEwQbs4yWIdXukJ0K40KgkpY
|
||||
github.com/ebitengine/oto/v3 v3.3.2/go.mod h1:MZeb/lwoC4DCOdiTIxYezrURTw7EvK/yF863+tmBI+U=
|
||||
github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE=
|
||||
github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
|
||||
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/eclipse/paho.golang v0.22.0 h1:JhhUngr8TBlyUZDZw/L6WVayPi9qmSmdWeki48i5AVE=
|
||||
github.com/eclipse/paho.golang v0.22.0/go.mod h1:9ZiYJ93iEfGRJri8tErNeStPKLXIGBHiqbHV74t5pqI=
|
||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
@@ -37,6 +42,8 @@ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGw
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
@@ -46,11 +53,15 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopxl/beep/v2 v2.1.0 h1:Jv95iHw3aNWoAa/J78YyXvOvMHH2ZGeAYD5ug8tVt8c=
|
||||
github.com/gopxl/beep/v2 v2.1.0/go.mod h1:sQvj2oSsu8fmmDWH3t0DzIe0OZzTW6/TJEHW4Ku+22o=
|
||||
github.com/gopxl/beep/v2 v2.1.1 h1:6FYIYMm2qPAdWkjX+7xwKrViS1x0Po5kDMdRkq8NVbU=
|
||||
github.com/gopxl/beep/v2 v2.1.1/go.mod h1:ZAm9TGQ9lvpoiFLd4zf5B1IuyxZhgRACMId1XJbaW0E=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grid-x/modbus v0.0.0-20241004123532-f6c6fb5201b3 h1:TfBJ561lUg0i0GLsxKeRaWoBGN8nyCLNt0OMGRx7R2M=
|
||||
github.com/grid-x/modbus v0.0.0-20241004123532-f6c6fb5201b3/go.mod h1:WpbUAyptAAi0VAriSRopZa6uhiJOJCTz7KFvgGtNRXc=
|
||||
github.com/grid-x/modbus v0.0.0-20250219144522-2b18d136199f h1:i5NSZj4IehIvyDSIa2CLbqSeglX8Ngre8Qck64Wr63Q=
|
||||
github.com/grid-x/modbus v0.0.0-20250219144522-2b18d136199f/go.mod h1:WpbUAyptAAi0VAriSRopZa6uhiJOJCTz7KFvgGtNRXc=
|
||||
github.com/grid-x/serial v0.0.0-20211107191517-583c7356b3aa h1:Rsn6ARgNkXrsXJIzhkE4vQr5Gbx2LvtEMv4BJOK4LyU=
|
||||
github.com/grid-x/serial v0.0.0-20211107191517-583c7356b3aa/go.mod h1:kdOd86/VGFWRrtkNwf1MPk0u1gIjc4Y7R2j7nhwc7Rk=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.4 h1:NUP7pBYH8OguP4diaTZ9wJbUbk3tC0KlfzsEpWmYj68=
|
||||
@@ -73,6 +84,8 @@ github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E
|
||||
github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
|
||||
github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
@@ -112,6 +125,8 @@ github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/f
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk=
|
||||
github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0=
|
||||
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
|
||||
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
@@ -122,12 +137,18 @@ github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9yS
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
|
||||
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
|
||||
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -163,6 +184,8 @@ github.com/ysmood/leakless v0.9.0 h1:qxCG5VirSBvmi3uynXFkcnLMzkphdh3xx5FtrORwDCU
|
||||
github.com/ysmood/leakless v0.9.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ=
|
||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
@@ -178,6 +201,8 @@ golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo=
|
||||
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
||||
golang.org/x/exp v0.0.0-20250228200357-dead58393ab7 h1:aWwlzYV971S4BXRS9AmqwDLAD85ouC6X+pocatKY58c=
|
||||
golang.org/x/exp v0.0.0-20250228200357-dead58393ab7/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
|
||||
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
@@ -188,6 +213,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -195,9 +222,13 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
@@ -214,6 +245,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
|
||||
@@ -3,13 +3,10 @@ package common
|
||||
import "sync"
|
||||
|
||||
type CtrlWait struct {
|
||||
// 用于暂停的chan
|
||||
P chan struct{}
|
||||
// 用于恢复的chan
|
||||
R chan struct{}
|
||||
C chan int8
|
||||
|
||||
// 状态
|
||||
s bool
|
||||
|
||||
m sync.RWMutex
|
||||
}
|
||||
|
||||
@@ -18,7 +15,7 @@ func (c *CtrlWait) Pause() {
|
||||
c.m.RLock()
|
||||
defer c.m.RUnlock()
|
||||
if c.s {
|
||||
c.P <- struct{}{}
|
||||
c.C <- 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +24,7 @@ func (c *CtrlWait) Resume() {
|
||||
c.m.RLock()
|
||||
defer c.m.RUnlock()
|
||||
if c.s {
|
||||
c.R <- struct{}{}
|
||||
c.C <- 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +43,7 @@ func (c *CtrlWait) Close() {
|
||||
// NewCtrlWait 创建一个控制等待
|
||||
func NewCtrlWait() *CtrlWait {
|
||||
return &CtrlWait{
|
||||
P: make(chan struct{}),
|
||||
R: make(chan struct{}),
|
||||
C: make(chan int8),
|
||||
s: false,
|
||||
}
|
||||
}
|
||||
|
||||
53
internal/common/pause_sub.go
Normal file
53
internal/common/pause_sub.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package common
|
||||
|
||||
import "sync"
|
||||
|
||||
type PauseSub struct {
|
||||
ctrl *CtrlWait
|
||||
// 回调函数
|
||||
items []chan int8
|
||||
m sync.RWMutex
|
||||
}
|
||||
|
||||
// Add 添加一个暂停项
|
||||
func (p *PauseSub) Add(item chan int8) {
|
||||
p.m.Lock()
|
||||
defer p.m.Unlock()
|
||||
p.items = append(p.items, item)
|
||||
}
|
||||
|
||||
// Remove 移除一个暂停项
|
||||
func (p *PauseSub) Remove(item chan int8) {
|
||||
p.m.Lock()
|
||||
defer p.m.Unlock()
|
||||
for i, v := range p.items {
|
||||
if v == item {
|
||||
p.items = append(p.items[:i], p.items[i+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run 开始监听
|
||||
func (p *PauseSub) Run() {
|
||||
p.ctrl.Open()
|
||||
defer p.ctrl.Close()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-p.ctrl.C:
|
||||
go func() {
|
||||
p.m.RLock()
|
||||
defer p.m.RUnlock()
|
||||
for _, item := range p.items {
|
||||
item <- 1
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func NewPauseSub(c *CtrlWait) *PauseSub {
|
||||
return &PauseSub{
|
||||
ctrl: c,
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"game-driver/internal/common"
|
||||
"game-driver/leaf"
|
||||
"go.uber.org/zap"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func Pause(ctrl *common.CtrlWait) leaf.HandlerFunc {
|
||||
return func(c *leaf.Context) {
|
||||
var cancel context.CancelFunc
|
||||
|
||||
// 保存原始的 Context
|
||||
originalCtx := c.Context
|
||||
|
||||
// 获取锚点
|
||||
holdPoint := c.Hold()
|
||||
|
||||
// 等待组
|
||||
var wait sync.WaitGroup
|
||||
defer wait.Wait()
|
||||
|
||||
run := true
|
||||
|
||||
wait.Add(1)
|
||||
go func() {
|
||||
defer wait.Done()
|
||||
zap.S().Infoln("待机控制器")
|
||||
|
||||
ctrl.Open()
|
||||
defer ctrl.Close()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-originalCtx.Done():
|
||||
cancel()
|
||||
zap.S().Infoln("待机控制器监听结束")
|
||||
return
|
||||
case <-ctrl.R:
|
||||
{
|
||||
zap.S().Infoln("待机控制器 Resume 触发")
|
||||
c.Context = originalCtx
|
||||
run = true
|
||||
}
|
||||
case <-ctrl.P:
|
||||
{
|
||||
zap.S().Infoln("待机控制器 Pause 触发")
|
||||
run = false
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-originalCtx.Done():
|
||||
return
|
||||
default:
|
||||
if run {
|
||||
cancel = leaf.WithCancel(c)
|
||||
c.Resume(holdPoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
internal/routes/standby/audio.go
Normal file
40
internal/routes/standby/audio.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package standby
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"game-driver/internal/schema"
|
||||
"game-driver/pkg/audio"
|
||||
"game-driver/pkg/utils"
|
||||
"github.com/gopxl/beep/v2/speaker"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func Audio(item schema.WaitItemModel) func(c context.Context) error {
|
||||
return func(c context.Context) error {
|
||||
data, err := utils.LinkAudio(item.Data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("音频数据获取异常: %w", err)
|
||||
}
|
||||
if data == nil {
|
||||
return fmt.Errorf("音频数据获取为空")
|
||||
}
|
||||
|
||||
zap.S().Infoln("播放待机音乐")
|
||||
defer zap.S().Infoln("结束待机音乐")
|
||||
|
||||
ctrl, closer, e := audio.PlayBgmMP3(data)
|
||||
defer closer()
|
||||
if e != nil {
|
||||
return fmt.Errorf("播放待机音乐异常: %w", e)
|
||||
}
|
||||
|
||||
<-c.Done()
|
||||
|
||||
speaker.Lock()
|
||||
ctrl.Streamer = nil
|
||||
speaker.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
35
internal/routes/standby/pjlink.go
Normal file
35
internal/routes/standby/pjlink.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package standby
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"game-driver/config/wait"
|
||||
"game-driver/internal/schema"
|
||||
"game-driver/pkg/pjlink"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func PJLink(_ schema.WaitItemModel) func(c context.Context) error {
|
||||
return func(c context.Context) error {
|
||||
cfg := (wait.C).(wait.PJLink)
|
||||
pjc := pjlink.NewClient(cfg.Ip, cfg.Port, cfg.Password, cfg.Id)
|
||||
|
||||
zap.S().Infoln("打开待机投影仪")
|
||||
resp, err := pjc.PowerOn()
|
||||
if err != nil {
|
||||
return fmt.Errorf("打开投影仪异常: %w", err)
|
||||
}
|
||||
zap.S().Infoln("投影仪返回报文:", resp)
|
||||
|
||||
<-c.Done()
|
||||
|
||||
zap.S().Infoln("关闭待机投影仪")
|
||||
resp, err = pjc.PowerOff()
|
||||
if err != nil {
|
||||
return fmt.Errorf("关闭投影仪异常: %w", err)
|
||||
}
|
||||
zap.S().Infoln("投影仪返回报文:", resp)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
28
internal/routes/standby/relay.go
Normal file
28
internal/routes/standby/relay.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package standby
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"game-driver/internal/schema"
|
||||
"game-driver/pkg/relay"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func Relay(item schema.WaitItemModel) func(c context.Context) error {
|
||||
return func(c context.Context) error {
|
||||
r, err := relay.New(item.Data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("继电器初始化异常: %w", err)
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
zap.S().Infoln("待机继电器供电")
|
||||
defer zap.S().Infoln("待机继电器断电")
|
||||
|
||||
_ = r.On(0)
|
||||
<-c.Done()
|
||||
_ = r.Off(0)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
32
internal/routes/standby/tts.go
Normal file
32
internal/routes/standby/tts.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package standby
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"game-driver/internal/schema"
|
||||
"game-driver/pkg/audio"
|
||||
"game-driver/pkg/tts"
|
||||
"go.uber.org/zap"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TTS(item schema.WaitItemModel) func(c context.Context) error {
|
||||
return func(c context.Context) error {
|
||||
reader, err := tts.DefaultTTS.Get(item.Data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("语音合成异常: %w", err)
|
||||
}
|
||||
|
||||
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):
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
38
internal/routes/standby/video.go
Normal file
38
internal/routes/standby/video.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package standby
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"game-driver/internal/schema"
|
||||
"game-driver/pkg/utils"
|
||||
"game-driver/pkg/video"
|
||||
"go.uber.org/zap"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Video(item schema.WaitItemModel) func(c context.Context) error {
|
||||
return func(c context.Context) error {
|
||||
local, err := utils.LinkVideo(item.Data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("视频文件获取异常: %w", err)
|
||||
}
|
||||
|
||||
zap.S().Infoln("播放待机视频")
|
||||
defer zap.S().Infoln("结束待机视频")
|
||||
|
||||
utils.BlankOpen()
|
||||
defer utils.BlankClose()
|
||||
|
||||
for {
|
||||
err := video.Play(c, local)
|
||||
if err != nil {
|
||||
return fmt.Errorf("视频播放异常: %w", err)
|
||||
}
|
||||
select {
|
||||
case <-c.Done():
|
||||
return nil
|
||||
case <-time.After(time.Duration(item.Interval) * time.Second):
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
22
internal/routes/standby/web.go
Normal file
22
internal/routes/standby/web.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package standby
|
||||
|
||||
import (
|
||||
"context"
|
||||
"game-driver/internal/schema"
|
||||
"game-driver/pkg/browser"
|
||||
"game-driver/pkg/utils"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func Web(item schema.WaitItemModel) func(c context.Context) error {
|
||||
return func(c context.Context) error {
|
||||
zap.S().Infoln("打开待机网页")
|
||||
|
||||
// 控制背光
|
||||
utils.BlankOpen()
|
||||
defer utils.BlankClose()
|
||||
|
||||
browser.OpenApp(c, item.Data)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
66
internal/routes/standby_ctrl/pause.go
Normal file
66
internal/routes/standby_ctrl/pause.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package standby_ctrl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"game-driver/internal/common"
|
||||
"go.uber.org/zap"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func Pause(ps *common.PauseSub, isPause bool, play func(c context.Context) error) func(c context.Context) error {
|
||||
return func(c context.Context) error {
|
||||
var cancel context.CancelFunc
|
||||
run := true
|
||||
|
||||
if isPause {
|
||||
p := make(chan int8)
|
||||
defer close(p)
|
||||
|
||||
ps.Add(p)
|
||||
defer ps.Remove(p)
|
||||
|
||||
zap.S().Infoln("待机控制器")
|
||||
defer zap.S().Infoln("待机控制器结束")
|
||||
|
||||
// 等待组
|
||||
var wait sync.WaitGroup
|
||||
defer wait.Wait()
|
||||
|
||||
wait.Add(1)
|
||||
go func() {
|
||||
defer wait.Done()
|
||||
for {
|
||||
select {
|
||||
case <-c.Done():
|
||||
return
|
||||
case v := <-p:
|
||||
if v == 1 {
|
||||
zap.S().Infoln("待机控制器 Pause 触发")
|
||||
run = false
|
||||
cancel()
|
||||
} else {
|
||||
zap.S().Infoln("待机控制器 Resume 触发")
|
||||
run = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-c.Done():
|
||||
return nil
|
||||
default:
|
||||
if run {
|
||||
nc, cc := context.WithCancel(c)
|
||||
cancel = cc
|
||||
err := play(nc)
|
||||
if err != nil {
|
||||
zap.S().Infoln("执行后续操作异常: ", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
91
internal/routes/standby_ctrl/time.go
Normal file
91
internal/routes/standby_ctrl/time.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package standby_ctrl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/go-pkgz/cronrange"
|
||||
"go.uber.org/zap"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Time 时间控制器
|
||||
func Time(rootRules []cronrange.Rule, cron string, play func(c context.Context) error) func(c context.Context) error {
|
||||
// 设定默认时间规则
|
||||
if cron == "" {
|
||||
cron = "* * * *"
|
||||
}
|
||||
|
||||
rules, err := cronrange.Parse(cron)
|
||||
if err != nil {
|
||||
zap.S().Errorln("解析时间规则异常: ", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return func(c context.Context) error {
|
||||
a := make(chan bool)
|
||||
defer close(a)
|
||||
|
||||
// 等待组
|
||||
var waitGroup sync.WaitGroup
|
||||
defer waitGroup.Wait()
|
||||
|
||||
ticker := time.NewTicker(time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
waitGroup.Add(1)
|
||||
go func() {
|
||||
defer waitGroup.Done()
|
||||
for {
|
||||
select {
|
||||
case <-c.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
if cronrange.Match(rules, time.Now()) && cronrange.Match(rootRules, time.Now()) {
|
||||
a <- true
|
||||
} else {
|
||||
a <- false
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
var cancel context.CancelFunc
|
||||
var m sync.Mutex
|
||||
for {
|
||||
select {
|
||||
case <-c.Done():
|
||||
if cancel != nil {
|
||||
cancel()
|
||||
cancel = nil
|
||||
}
|
||||
return nil
|
||||
case r := <-a:
|
||||
if r {
|
||||
if ok := m.TryLock(); ok {
|
||||
ctx, cc := context.WithCancel(context.TODO())
|
||||
cancel = cc
|
||||
waitGroup.Add(1)
|
||||
go func() {
|
||||
defer waitGroup.Done()
|
||||
defer m.Unlock()
|
||||
defer func() { cancel = nil }()
|
||||
|
||||
err := play(ctx)
|
||||
if err != nil {
|
||||
zap.S().Errorln("执行动作异常: ", err)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-time.After(time.Minute):
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
} else if cancel != nil {
|
||||
cancel()
|
||||
cancel = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,291 +2,85 @@ package routes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"game-driver/config/wait"
|
||||
"game-driver/internal/common"
|
||||
"game-driver/internal/middleware"
|
||||
"game-driver/internal/routes/standby"
|
||||
"game-driver/internal/routes/standby_ctrl"
|
||||
"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"
|
||||
"game-driver/pkg/video"
|
||||
"github.com/go-pkgz/cronrange"
|
||||
"github.com/gopxl/beep/v2/speaker"
|
||||
"go.uber.org/zap"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
func runAction(c *leaf.Context, item schema.WaitItemModel, rootRules []cronrange.Rule, play func(c context.Context, item schema.WaitItemModel) error) {
|
||||
// 设定默认时间规则
|
||||
if item.Cron == "" {
|
||||
item.Cron = "* * * *"
|
||||
}
|
||||
func WaitAction(ctrl *common.CtrlWait) leaf.HandlerFunc {
|
||||
ps := common.NewPauseSub(ctrl)
|
||||
|
||||
rules, err := cronrange.Parse(item.Cron)
|
||||
if err != nil {
|
||||
zap.S().Errorln("解析时间规则异常: ", err)
|
||||
return
|
||||
}
|
||||
return func(c *leaf.Context) {
|
||||
payload := leaf.Value[*schema.WaitModel](c, middleware.PayloadJSONKey)
|
||||
|
||||
a := make(chan bool)
|
||||
defer close(a)
|
||||
|
||||
// 等待组
|
||||
var wait sync.WaitGroup
|
||||
defer wait.Wait()
|
||||
|
||||
ticker := time.NewTicker(time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
wait.Add(1)
|
||||
go func() {
|
||||
defer wait.Done()
|
||||
for {
|
||||
select {
|
||||
case <-c.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
if cronrange.Match(rules, time.Now()) && cronrange.Match(rootRules, time.Now()) {
|
||||
a <- true
|
||||
} else {
|
||||
a <- false
|
||||
}
|
||||
}
|
||||
// 设定默认时间规则,ctrl
|
||||
if payload.Cron == "" {
|
||||
payload.Cron = "* * * *"
|
||||
}
|
||||
}()
|
||||
|
||||
var cancel context.CancelFunc
|
||||
var m sync.Mutex
|
||||
for {
|
||||
select {
|
||||
case <-c.Done():
|
||||
if cancel != nil {
|
||||
cancel()
|
||||
}
|
||||
return
|
||||
case r := <-a:
|
||||
if r {
|
||||
if ok := m.TryLock(); ok {
|
||||
ctx, cc := context.WithCancel(context.TODO())
|
||||
cancel = cc
|
||||
wait.Add(1)
|
||||
go func() {
|
||||
defer wait.Done()
|
||||
defer m.Unlock()
|
||||
defer func() { cancel = nil }()
|
||||
|
||||
err := play(ctx, item)
|
||||
if err != nil {
|
||||
zap.S().Errorln("执行动作异常: ", err)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-time.After(time.Minute):
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
} else if cancel != nil {
|
||||
cancel()
|
||||
cancel = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func WaitAction(c *leaf.Context) {
|
||||
payload := leaf.Value[*schema.WaitModel](c, middleware.PayloadJSONKey)
|
||||
|
||||
// 设定默认时间规则
|
||||
if payload.Cron == "" {
|
||||
payload.Cron = "* * * *"
|
||||
}
|
||||
|
||||
rules, err := cronrange.Parse(payload.Cron)
|
||||
if err != nil {
|
||||
zap.S().Errorln("解析时间规则异常: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 等待组
|
||||
var wait sync.WaitGroup
|
||||
defer wait.Wait()
|
||||
for _, item := range payload.Items {
|
||||
switch item.Type {
|
||||
case schema.WaitAudio:
|
||||
// 执行音乐播放
|
||||
wait.Add(1)
|
||||
go func() {
|
||||
defer wait.Done()
|
||||
runAction(c, item, rules, audioAction)
|
||||
}()
|
||||
case schema.WaitTTS:
|
||||
// 执行TTS播放
|
||||
wait.Add(1)
|
||||
go func() {
|
||||
defer wait.Done()
|
||||
runAction(c, item, rules, ttsAction)
|
||||
}()
|
||||
case schema.WaitRelay:
|
||||
// 执行继电器供电
|
||||
wait.Add(1)
|
||||
go func() {
|
||||
defer wait.Done()
|
||||
runAction(c, item, rules, relayAction)
|
||||
}()
|
||||
case schema.WaitVideo:
|
||||
// 执行视频播放
|
||||
wait.Add(1)
|
||||
go func() {
|
||||
defer wait.Done()
|
||||
runAction(c, item, rules, videoAction)
|
||||
}()
|
||||
case schema.WaitWeb:
|
||||
// 执行网页打开
|
||||
wait.Add(1)
|
||||
go func() {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func audioAction(c context.Context, item schema.WaitItemModel) error {
|
||||
data, err := utils.LinkAudio(item.Data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("音频数据获取异常: %w", err)
|
||||
}
|
||||
if data == nil {
|
||||
return fmt.Errorf("音频数据获取为空")
|
||||
}
|
||||
|
||||
zap.S().Infoln("播放待机音乐")
|
||||
defer zap.S().Infoln("结束待机音乐")
|
||||
|
||||
ctrl, closer, e := audio.PlayBgmMP3(data)
|
||||
defer closer()
|
||||
if e != nil {
|
||||
return fmt.Errorf("播放待机音乐异常: %w", e)
|
||||
}
|
||||
|
||||
<-c.Done()
|
||||
|
||||
speaker.Lock()
|
||||
ctrl.Streamer = nil
|
||||
speaker.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ttsAction(c context.Context, item schema.WaitItemModel) error {
|
||||
reader, err := tts.DefaultTTS.Get(item.Data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("语音合成异常: %w", err)
|
||||
}
|
||||
|
||||
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):
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func relayAction(c context.Context, item schema.WaitItemModel) error {
|
||||
r, err := relay.New(item.Data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("继电器初始化异常: %w", err)
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
zap.S().Infoln("待机继电器供电")
|
||||
defer zap.S().Infoln("待机继电器断电")
|
||||
|
||||
_ = r.On(0)
|
||||
<-c.Done()
|
||||
_ = r.Off(0)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func videoAction(c context.Context, item schema.WaitItemModel) error {
|
||||
local, err := utils.LinkVideo(item.Data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("视频文件获取异常: %w", err)
|
||||
}
|
||||
|
||||
zap.S().Infoln("播放待机视频")
|
||||
defer zap.S().Infoln("结束待机视频")
|
||||
|
||||
utils.BlankOpen()
|
||||
defer utils.BlankClose()
|
||||
|
||||
for {
|
||||
err := video.Play(c, local)
|
||||
rules, err := cronrange.Parse(payload.Cron)
|
||||
if err != nil {
|
||||
return fmt.Errorf("视频播放异常: %w", err)
|
||||
zap.S().Errorln("解析时间规则异常: ", err)
|
||||
return
|
||||
}
|
||||
select {
|
||||
case <-c.Done():
|
||||
return nil
|
||||
case <-time.After(time.Duration(item.Interval) * time.Second):
|
||||
|
||||
// 等待组
|
||||
var waitGroup sync.WaitGroup
|
||||
defer waitGroup.Wait()
|
||||
|
||||
// 开启暂停监听
|
||||
waitGroup.Add(1)
|
||||
go func() {
|
||||
defer waitGroup.Done()
|
||||
ps.Run()
|
||||
}()
|
||||
|
||||
// 处理每个待机控制
|
||||
handleItem := func(title string, item schema.WaitItemModel, f func(c context.Context) error) {
|
||||
waitGroup.Add(1)
|
||||
go func() {
|
||||
defer waitGroup.Done()
|
||||
if f == nil {
|
||||
return
|
||||
}
|
||||
f = standby_ctrl.Time(rules, item.Cron, f)
|
||||
if f == nil {
|
||||
return
|
||||
}
|
||||
f = standby_ctrl.Pause(ps, item.Pause, f)
|
||||
if f == nil {
|
||||
return
|
||||
}
|
||||
e := f(c)
|
||||
if e != nil {
|
||||
zap.S().Errorf("%s异常: %s\n", title, e)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
for _, item := range payload.Items {
|
||||
switch item.Type {
|
||||
case schema.WaitAudio:
|
||||
handleItem("音乐待机控制", item, standby.Audio(item))
|
||||
case schema.WaitTTS:
|
||||
handleItem("TTS待机控制", item, standby.TTS(item))
|
||||
case schema.WaitRelay:
|
||||
handleItem("继电器待机控制", item, standby.Relay(item))
|
||||
case schema.WaitVideo:
|
||||
handleItem("视频待机控制", item, standby.Video(item))
|
||||
case schema.WaitWeb:
|
||||
handleItem("视频待机控制", item, standby.Web(item))
|
||||
case schema.WaitPJLink:
|
||||
handleItem("视频待机控制", item, standby.PJLink(item))
|
||||
default:
|
||||
zap.S().Infof("不支持的类型: %d\n", item.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func webAction(c context.Context, item schema.WaitItemModel) error {
|
||||
zap.S().Infoln("打开待机网页")
|
||||
|
||||
// 控制背光
|
||||
utils.BlankOpen()
|
||||
defer utils.BlankClose()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ type WaitItemModel struct {
|
||||
Type WaitType `json:"type"`
|
||||
Data string `json:"data"`
|
||||
Interval int64 `json:"interval"`
|
||||
Pause bool `json:"pause"`
|
||||
}
|
||||
|
||||
type WaitModel struct {
|
||||
|
||||
@@ -154,8 +154,7 @@ func Run() {
|
||||
middleware.PayloadJSON[schema.WaitModel](),
|
||||
middleware.Unique(common.GlobalBgStopper),
|
||||
middleware.EmergencyStop(common.GlobalBgStopper),
|
||||
middleware.Pause(common.PassCtrl),
|
||||
routes.WaitAction,
|
||||
routes.WaitAction(common.PassCtrl),
|
||||
)
|
||||
// 处理指令
|
||||
router.RegisterHandler(topicPrefix+"command",
|
||||
|
||||
@@ -32,11 +32,11 @@ func NewClient(host, port, password, id string) *Client {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Connect() error {
|
||||
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
|
||||
return fmt.Errorf("连接异常: %w", err)
|
||||
}
|
||||
c.conn = conn
|
||||
|
||||
@@ -44,14 +44,14 @@ func (c *Client) Connect() error {
|
||||
reader := bufio.NewReader(c.conn)
|
||||
response, err := reader.ReadString('\r')
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return err
|
||||
c.close()
|
||||
return fmt.Errorf("读取异常: %w", err)
|
||||
}
|
||||
|
||||
// Handle authentication
|
||||
if strings.HasPrefix(response, "PJLINK 1") {
|
||||
if c.Password == "" {
|
||||
c.Close()
|
||||
c.close()
|
||||
return ErrAuthFailed
|
||||
}
|
||||
|
||||
@@ -62,13 +62,13 @@ func (c *Client) Connect() error {
|
||||
|
||||
_, err = fmt.Fprintf(c.conn, "%s\r", authHash)
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return err
|
||||
c.close()
|
||||
return fmt.Errorf("写入异常: %w", err)
|
||||
}
|
||||
|
||||
authResponse, err := reader.ReadString('\r')
|
||||
if err != nil || !strings.Contains(authResponse, "OK") {
|
||||
c.Close()
|
||||
c.close()
|
||||
return ErrAuthFailed
|
||||
}
|
||||
}
|
||||
@@ -104,36 +104,54 @@ func (c *Client) sendCommand(command string) (string, error) {
|
||||
return "", ErrAuthFailed
|
||||
} else if result == "ERR2" {
|
||||
return "", ErrCommandError
|
||||
} else if result == "ERR3" {
|
||||
return "YES", nil
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *Client) PowerOn() error {
|
||||
func (c *Client) PowerOn() (string, error) {
|
||||
err := c.connect()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("连接异常: %w", err)
|
||||
}
|
||||
defer c.close()
|
||||
|
||||
response, err := c.sendCommand("POWR 1")
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
if response != "OK" {
|
||||
return fmt.Errorf("unexpected response: %s", response)
|
||||
if response == "YES" {
|
||||
return response, nil
|
||||
} else if response != "OK" {
|
||||
return response, fmt.Errorf("unexpected response: %s", response)
|
||||
}
|
||||
return nil
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (c *Client) PowerOff() error {
|
||||
func (c *Client) PowerOff() (string, error) {
|
||||
err := c.connect()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("连接异常: %w", err)
|
||||
}
|
||||
defer c.close()
|
||||
|
||||
response, err := c.sendCommand("POWR 0")
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
if response != "OK" {
|
||||
return fmt.Errorf("unexpected response: %s", response)
|
||||
if response == "YES" {
|
||||
return response, nil
|
||||
} else if response != "OK" {
|
||||
return response, fmt.Errorf("unexpected response: %s", response)
|
||||
}
|
||||
return nil
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (c *Client) Close() {
|
||||
func (c *Client) close() {
|
||||
if c.conn != nil {
|
||||
c.conn.Close()
|
||||
c.conn = nil
|
||||
|
||||
44
todo.md
44
todo.md
@@ -1,22 +1,34 @@
|
||||
# 技术点记录
|
||||
|
||||
### ubuntu 24 开机慢优化
|
||||
|
||||
```bash
|
||||
# 在 systemd-networkd-wait-online.service Service 加入 TImeoutStartSec=2sec
|
||||
sudo vim /etc/systemd/system/network-online.target.wants/systemd-networkd-wait-online.service
|
||||
```
|
||||
|
||||
## linux 下播放音频
|
||||
|
||||
```bash
|
||||
sudo apt install libasound2-dev alsa-utils
|
||||
```
|
||||
|
||||
## linux 下播放视频
|
||||
|
||||
```bash
|
||||
sudo apt install ffmpeg
|
||||
sudo apt install libvlc-dev vlc
|
||||
```
|
||||
|
||||
显示安装
|
||||
## 显示安装
|
||||
|
||||
```bash
|
||||
sudo apt install xorg
|
||||
```
|
||||
|
||||
### 当前用户加入播放音频与视频的组中
|
||||
|
||||
```bash
|
||||
sudo usermod -aG audio,video $USER
|
||||
sudo usermod -aG audio,video,dialout $USER
|
||||
```
|
||||
|
||||
### 关闭背光
|
||||
@@ -28,12 +40,8 @@ xset dpms force off
|
||||
xset dpms force on
|
||||
```
|
||||
|
||||
### 播放视频
|
||||
```bash
|
||||
ffplay -autoexit -fs -i video.mp4
|
||||
```
|
||||
|
||||
### 注册为 service ,并开机启动
|
||||
|
||||
```bash
|
||||
sudo cp /script/game-driver.service /etc/systemd/system/
|
||||
sudo systemctl enable game-driver
|
||||
@@ -41,11 +49,13 @@ sudo systemctl start game-driver
|
||||
```
|
||||
|
||||
## 编译 arm64 架构
|
||||
|
||||
```bash
|
||||
CC=aarch64-linux-gnu-gcc CGO_ENABLED=1 GOOS=linux GOARCH=arm64 go build -o game-driver-arm64 .
|
||||
```
|
||||
|
||||
## J8引脚
|
||||
|
||||
```bash
|
||||
J8:
|
||||
3V3 (1) (2) 5V
|
||||
@@ -73,6 +83,7 @@ GPIO26 (37) (38) GPIO20
|
||||
## 极简桌面环境并自动登录
|
||||
|
||||
### 安装 xorg i3
|
||||
|
||||
```bash
|
||||
sudo apt install xorg i3-wm
|
||||
```
|
||||
@@ -80,6 +91,7 @@ sudo apt install xorg i3-wm
|
||||
### 自动启动 Xorg 和窗口管理器
|
||||
|
||||
编辑 `.bashrc`文件,在文件的末尾添加以下行:
|
||||
|
||||
```bash
|
||||
if [ -z "$DISPLAY" ] && [ "$(tty)" = "/dev/tty1" ]; then
|
||||
startx
|
||||
@@ -90,16 +102,20 @@ fi
|
||||
|
||||
### 自动登录
|
||||
|
||||
编辑 `/etc/systemd/system/getty.target.wants/getty@tty1.service` 文件,将 `ExecStart` 行修改为:
|
||||
编辑 `/etc/systemd/system/getty.target.wants/getty@tty1.service` 文件,将 `ExecStart` 行修改为:
|
||||
|
||||
```bash
|
||||
ExecStart=-/sbin/agetty --autologin <your_username> --noclear %I $TERM
|
||||
```
|
||||
|
||||
其中:
|
||||
- <your_username>:替换为你想自动登录的用户名。
|
||||
|
||||
- <your_username>:替换为你想自动登录的用户名。
|
||||
|
||||
### 禁用 i3bar 状态栏
|
||||
|
||||
编辑 `~/.config/i3/config`,将如下行注释掉:
|
||||
|
||||
```bash
|
||||
# bar {
|
||||
# status_command i3status
|
||||
@@ -111,11 +127,13 @@ ExecStart=-/sbin/agetty --autologin <your_username> --noclear %I $TERM
|
||||
### 配置 i3
|
||||
|
||||
安装 `unclutter`:
|
||||
|
||||
```bash
|
||||
sudo apt install unclutter
|
||||
```
|
||||
|
||||
编辑 `~/.config/i3/config`,添加如下行:
|
||||
|
||||
```bash
|
||||
exec --no-startup-id unclutter -root # 隐藏鼠标
|
||||
exec --no-startup-id xset dpms 0 0 0 # 关闭屏幕自动关闭
|
||||
@@ -136,13 +154,17 @@ sudo apt install fonts-noto-cjk fonts-noto-color-emoji
|
||||
|
||||
```bash
|
||||
sudo add-apt-repository ppa:xtradeb/apps
|
||||
sudo apt update
|
||||
sudo apt install ungoogled-chromium
|
||||
```
|
||||
|
||||
### 设置默认启动页面
|
||||
|
||||
编辑 `~/.config/i3/config`,添加如下行:
|
||||
|
||||
```bash
|
||||
exec --no-startup-id ungoogled-chromium --kiosk --disable-extensions --disable-translate --app=<your_url>
|
||||
```
|
||||
|
||||
### 设置系统默认音量
|
||||
|
||||
`alsamixer` `sudo alsactl store`
|
||||
|
||||
Reference in New Issue
Block a user