diff --git a/backend/Makefile b/backend/Makefile index 37943bc6..2f8de3ad 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -7,7 +7,7 @@ pull: start: pull up -pb: statesync speedControl ats +pb: statesync speedControl ats block statesync: statesync_internal statesync_external statesync_positioning @@ -76,3 +76,14 @@ ats_go: ats_python: cd auto_operation && \ ./protoc-gen.sh + +block: block_external + +block_external: + protoc \ + --go_out=external \ + --go_opt=Mproto/block.proto=./spec \ + --go-grpc_out=external/spec \ + --go-grpc_opt=Mproto/block.proto=. \ + -I./ \ + proto/block.proto diff --git a/backend/compose.debug.yml b/backend/compose.debug.yml index 7fbe01ab..0e166caf 100644 --- a/backend/compose.debug.yml +++ b/backend/compose.debug.yml @@ -13,6 +13,7 @@ services: environment: - "CLIENTSIDESERVER_PORT=8080" - "INTERNALSERVER_ADDR=internal:54321" + - "CLIENTSIDESERVER_ATSADDRESS=internal:54321" internal: build: context: ./internal diff --git a/backend/external/cmd/main.go b/backend/external/cmd/main.go index 320f5b02..9289356b 100644 --- a/backend/external/cmd/main.go +++ b/backend/external/cmd/main.go @@ -1,64 +1,16 @@ package main import ( + "log" "ueckoken/plarail2022-external/internal" - "ueckoken/plarail2022-external/pkg/envStore" - "ueckoken/plarail2022-external/pkg/syncController" - "github.com/prometheus/client_golang/prometheus" + "go.uber.org/zap" ) -const namespace = "plarailexternal" - func main() { - clientHandler2syncController := make(chan syncController.StationState, 16) - syncController2clientHandler := make(chan syncController.StationState, 64) - initEspStatus2syncController := make(chan syncController.StationState) - - envVal := envStore.GetEnv() - - clientConn := prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Namespace: namespace, - Name: "clients_connections_seconds", - Help: "Number of connections handling websocket", - }, - []string{}, - ) - - clientConnTotal := prometheus.NewCounterVec( - prometheus.CounterOpts{ - Namespace: namespace, - Name: "clients_connections_total", - Help: "Total client connection", - }, - []string{}, - ) - - controlCommandTotal := prometheus.NewCounterVec( - prometheus.CounterOpts{ - Namespace: namespace, - Name: "client_commands_total", - Help: "Total client commands", - }, - []string{}, - ) - - httpServer := internal.HTTPServer{ - ClientHandler2syncController: clientHandler2syncController, - SyncController2clientHandler: syncController2clientHandler, - Environment: envVal, - NumberOfClientConnection: clientConn, - TotalClientConnection: clientConnTotal, - TotalCLientCommands: controlCommandTotal, - Clients: &internal.ClientsCollection{}, - } - syncController := syncController.SyncController{ - ClientHandler2syncController: clientHandler2syncController, - SyncController2clientHandler: syncController2clientHandler, - Environment: envVal, - InitServoRoute: initEspStatus2syncController, + logger, err := zap.NewDevelopment() + if err != nil { + log.Fatalln("failed to initialize zap") } - go httpServer.StartServer() - syncController.StartSyncController() + internal.Run(logger) } diff --git a/backend/external/go.mod b/backend/external/go.mod index d921b465..bf4f3f14 100644 --- a/backend/external/go.mod +++ b/backend/external/go.mod @@ -9,6 +9,7 @@ require ( github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/prometheus/client_golang v1.14.0 github.com/vrischmann/envconfig v1.3.0 + go.uber.org/zap v1.23.0 google.golang.org/grpc v1.50.1 google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v2 v2.4.0 @@ -25,6 +26,8 @@ require ( github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/multierr v1.6.0 // indirect golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect diff --git a/backend/external/go.sum b/backend/external/go.sum index bbacd0f4..1e44a9c1 100644 --- a/backend/external/go.sum +++ b/backend/external/go.sum @@ -38,6 +38,7 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -178,6 +179,7 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -186,16 +188,11 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= -github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= -github.com/prometheus/client_golang v1.13.1 h1:3gMjIY2+/hzmqhtUC/aQNYldJA6DtH3CgQvwS+02K1c= -github.com/prometheus/client_golang v1.13.1/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= @@ -225,8 +222,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/vrischmann/envconfig v1.3.0 h1:4XIvQTXznxmWMnjouj0ST5lFo/WAYf5Exgl3x82crEk= github.com/vrischmann/envconfig v1.3.0/go.mod h1:bbvxFYJdRSpXrhS63mBFtKJzkDiNkyArOLXtY6q0kuI= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -237,6 +234,13 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= +go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -486,8 +490,6 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.50.0 h1:fPVVDxY9w++VjTZsYvXWqEf9Rqar/e+9zYfxKK+W+YU= -google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -521,8 +523,8 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/backend/external/internal/blocksync.go b/backend/external/internal/blocksync.go new file mode 100644 index 00000000..a7b72104 --- /dev/null +++ b/backend/external/internal/blocksync.go @@ -0,0 +1,14 @@ +package internal + +import ( + "go.uber.org/zap" + "ueckoken/plarail2022-external/pkg/synccontroller" + "ueckoken/plarail2022-external/spec" +) + +// startBlockSync starts sync controller for block state. +func startBlockSync(logger *zap.Logger, syncInput chan synccontroller.KV[spec.Blocks_BlockId, spec.NotifyStateRequest_State], syncOutput chan<- synccontroller.KV[spec.Blocks_BlockId, spec.NotifyStateRequest_State]) { + s := synccontroller.NewSyncController(logger, syncInput, syncOutput) + + go s.Run() +} diff --git a/backend/external/pkg/syncController/embed/stationInit.yml b/backend/external/internal/embed/stationInit.yml similarity index 100% rename from backend/external/pkg/syncController/embed/stationInit.yml rename to backend/external/internal/embed/stationInit.yml diff --git a/backend/external/pkg/syncController/embed/stationRule.yml b/backend/external/internal/embed/stationRule.yml similarity index 100% rename from backend/external/pkg/syncController/embed/stationRule.yml rename to backend/external/internal/embed/stationRule.yml diff --git a/backend/external/internal/grpc.go b/backend/external/internal/grpc.go new file mode 100644 index 00000000..b3dc6411 --- /dev/null +++ b/backend/external/internal/grpc.go @@ -0,0 +1,123 @@ +package internal + +import ( + "context" + "fmt" + "net" + "ueckoken/plarail2022-external/pkg/envStore" + "ueckoken/plarail2022-external/pkg/synccontroller" + "ueckoken/plarail2022-external/spec" + + "go.uber.org/zap" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +// GrpcStateHandler is a handler for gRPC. +type GrpcStateHandler struct { + logger *zap.Logger + env *envStore.Env + spec.UnimplementedControlServer + stateOutput chan<- synccontroller.KV[spec.Stations_StationId, spec.Command2InternalRequest_State] + stateInput <-chan synccontroller.KV[spec.Stations_StationId, spec.Command2InternalRequest_State] +} + +// NewGrpcHandler creates gRPC handler. +func NewGrpcHandler(logger *zap.Logger, env *envStore.Env, stateOutput chan<- synccontroller.KV[spec.Stations_StationId, spec.Command2InternalRequest_State], stateInput <-chan synccontroller.KV[spec.Stations_StationId, spec.Command2InternalRequest_State]) *GrpcStateHandler { + return &GrpcStateHandler{logger: logger, env: env, stateOutput: stateOutput, stateInput: stateInput} +} + +// Command2Internal handles requests from ATS. +func (g GrpcStateHandler) Command2Internal(_ context.Context, req *spec.RequestSync) (*spec.ResponseSync, error) { + s := synccontroller.KV[spec.Stations_StationId, spec.Command2InternalRequest_State]{ + Key: req.GetStation().GetStationId(), + Value: spec.Command2InternalRequest_State(req.GetState()), + } + g.stateOutput <- s + return &spec.ResponseSync{Response: spec.ResponseSync_SUCCESS}, nil +} + +// / handleInput transmits changes received in channel to ATS. +func (g GrpcStateHandler) handleInput(ctx context.Context) { + con, err := grpc.DialContext(ctx, g.env.ClientSideServer.ATSAddress.String(), + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + g.logger.Error("failed to connect ATS", zap.Error(err)) + } + defer con.Close() + for d := range g.stateInput { + client := spec.NewControlClient(con) + req := &spec.RequestSync{ + Station: &spec.Stations{StationId: d.Key}, + State: spec.RequestSync_State(d.Value), + } + res, err := client.Command2Internal(ctx, req) + if err != nil { + g.logger.Error("failed to send data to ATS", zap.Any("payload", req), zap.Error(err)) + } + if res.GetResponse() != spec.ResponseSync_SUCCESS { + g.logger.Error("ATS response seems to be unsuccessfull", zap.Any("payload", res.GetResponse())) + } + } +} + +type GrpcBlockHandler struct { + env *envStore.Env + logger *zap.Logger + spec.UnimplementedBlockStateSyncServer + stateOutput chan<- synccontroller.KV[spec.Blocks_BlockId, spec.NotifyStateRequest_State] + stateInput <-chan synccontroller.KV[spec.Blocks_BlockId, spec.NotifyStateRequest_State] +} + +// NewGrpcBlockHandler creates gRPC handler. +func NewGrpcBlockHandler(logger *zap.Logger, env *envStore.Env, stateOutput chan<- synccontroller.KV[spec.Blocks_BlockId, spec.NotifyStateRequest_State], stateInput <-chan synccontroller.KV[spec.Blocks_BlockId, spec.NotifyStateRequest_State]) *GrpcBlockHandler { + return &GrpcBlockHandler{logger: logger, env: env, stateOutput: stateOutput, stateInput: stateInput} +} + +// NotifyState handles requests from ATS. +func (g GrpcBlockHandler) NotifyState(_ context.Context, req *spec.NotifyStateRequest) (*spec.NotifyStateResponse, error) { + g.stateOutput <- synccontroller.KV[spec.Blocks_BlockId, spec.NotifyStateRequest_State]{Key: req.GetBlock().GetBlockId(), Value: req.GetState()} + return &spec.NotifyStateResponse{Response: spec.NotifyStateResponse_SUCCESS}, nil +} + +// handleInput transmits changes received in channel to ATS. +func (g GrpcBlockHandler) handleInput(ctx context.Context) { + con, err := grpc.DialContext(ctx, g.env.ClientSideServer.ATSAddress.String(), + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + g.logger.Error("failed to connect ATS", zap.Error(err)) + } + defer con.Close() + for d := range g.stateInput { + client := spec.NewBlockStateSyncClient(con) + req := &spec.NotifyStateRequest{ + Block: &spec.Blocks{BlockId: d.Key}, + State: d.Value, + } + res, err := client.NotifyState(ctx, req) + if err != nil { + g.logger.Error("failed to send data to ATS", zap.Any("payload", req), zap.Error(err)) + } + if res.GetResponse() != spec.NotifyStateResponse_SUCCESS { + g.logger.Error("ATS response seems to be unsuccessfull", zap.Any("payload", res.GetResponse())) + } + } +} + +// GRPCListenAndServe listens and serve. +func GRPCListenAndServe(ctx context.Context, logger *zap.Logger, port uint, handler *GrpcStateHandler, blockhandler *GrpcBlockHandler) { + lis, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) + if err != nil { + logger.Panic("failed to listen", zap.Error(err)) + } + go handler.handleInput(ctx) + go blockhandler.handleInput(ctx) + s := grpc.NewServer() + spec.RegisterControlServer(s, handler) + spec.RegisterBlockStateSyncServer(s, blockhandler) + if err := s.Serve(lis); err != nil { + logger.Panic("failed to server", zap.Error(err)) + } +} diff --git a/backend/external/internal/http.go b/backend/external/internal/http.go deleted file mode 100644 index a665ffab..00000000 --- a/backend/external/internal/http.go +++ /dev/null @@ -1,128 +0,0 @@ -package internal - -import ( - "fmt" - "log" - "net/http" - "sync" - "time" - "ueckoken/plarail2022-external/pkg/clientHandler" - "ueckoken/plarail2022-external/pkg/envStore" - "ueckoken/plarail2022-external/pkg/syncController" - - "github.com/gorilla/mux" - "github.com/gorilla/websocket" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promhttp" -) - -type HTTPServer struct { - ClientHandler2syncController chan syncController.StationState - SyncController2clientHandler chan syncController.StationState - Environment *envStore.Env - NumberOfClientConnection *prometheus.GaugeVec - TotalClientConnection *prometheus.CounterVec - TotalCLientCommands *prometheus.CounterVec - Clients *ClientsCollection -} - -type ClientsCollection struct { - Clients []clientHandler.ClientChannel - mtx sync.Mutex -} - -func (h *HTTPServer) StartServer() { - clientChannelSend := make(chan clientHandler.ClientChannel) - go h.registerClient(clientChannelSend) - go h.handleChanges() - go h.unregisterClient() - r := mux.NewRouter() - prometheus.MustRegister(h.NumberOfClientConnection) - prometheus.MustRegister(h.TotalClientConnection) - prometheus.MustRegister(h.TotalCLientCommands) - r.HandleFunc("/", clientHandler.HandleStatic) - var upgrader = websocket.Upgrader{ - ReadBufferSize: 1024, - WriteBufferSize: 1024, - CheckOrigin: func(r *http.Request) bool { - return true - }, - } - r.Handle("/ws", clientHandler.ClientHandler{Upgrader: upgrader, ClientCommand: h.ClientHandler2syncController, ClientChannelSend: clientChannelSend}) - r.Handle("/metrics", promhttp.Handler()) - srv := &http.Server{ - Handler: r, - Addr: fmt.Sprintf("0.0.0.0:%d", h.Environment.ClientSideServer.Port), - ReadHeaderTimeout: 5 * time.Second, - ReadTimeout: 5 * time.Second, - WriteTimeout: 5 * time.Second, - } - - log.Fatal(srv.ListenAndServe()) -} - -func (h *HTTPServer) handleChanges() { - for d := range h.SyncController2clientHandler { - h.Clients.mtx.Lock() - h.TotalCLientCommands.With(prometheus.Labels{}).Inc() - for _, c := range h.Clients.Clients { - select { - case c.ClientSync <- d: - default: - log.Println("buffer is full when send...") - continue - } - } - h.Clients.mtx.Unlock() - } - time.Sleep(1 * time.Second) -} - -func (h *HTTPServer) registerClient(cn chan clientHandler.ClientChannel) { - for n := range cn { - func(h *HTTPServer, n clientHandler.ClientChannel) { - h.Clients.mtx.Lock() - defer h.Clients.mtx.Unlock() - h.TotalClientConnection.With(prometheus.Labels{}).Inc() - h.Clients.Clients = append(h.Clients.Clients, n) - }(h, n) - } -} - -func (h *HTTPServer) unregisterClient() { - for { - h.Clients.mtx.Lock() - var deletionList []int - for i, c := range h.Clients.Clients { - select { - case <-c.Done: - deletionList = append(deletionList, i) - default: - continue - } - } - h.Clients.deleteClient(deletionList) - h.Clients.mtx.Unlock() - h.NumberOfClientConnection.With(prometheus.Labels{}).Set(float64(len(h.Clients.Clients))) - time.Sleep(1 * time.Second) - } -} - -func (cl *ClientsCollection) deleteClient(deletion []int) { - var tmp []clientHandler.ClientChannel - for i, c := range cl.Clients { - if !contain(deletion, i) { - tmp = append(tmp, c) - } - } - cl.Clients = tmp -} - -func contain(list []int, data int) bool { - for _, l := range list { - if l == data { - return true - } - } - return false -} diff --git a/backend/external/internal/root.go b/backend/external/internal/root.go new file mode 100644 index 00000000..c2d63a68 --- /dev/null +++ b/backend/external/internal/root.go @@ -0,0 +1,68 @@ +package internal + +import ( + "context" + "ueckoken/plarail2022-external/pkg/envStore" + "ueckoken/plarail2022-external/pkg/httphandler" + "ueckoken/plarail2022-external/pkg/synccontroller" + "ueckoken/plarail2022-external/spec" + + "go.uber.org/zap" +) + +// Run runs external server. +func Run(logger *zap.Logger) { + ctx := context.Background() + synccontrollerInput := make(chan synccontroller.KV[spec.Stations_StationId, spec.Command2InternalRequest_State]) + synccontrollerOutput := make(chan synccontroller.KV[spec.Stations_StationId, spec.Command2InternalRequest_State]) + grpcHandlerInput := make(chan synccontroller.KV[spec.Stations_StationId, spec.Command2InternalRequest_State]) + main2grpcHandler := make(chan synccontroller.KV[spec.Stations_StationId, spec.Command2InternalRequest_State]) + httpInputKV := make(chan synccontroller.KV[spec.Stations_StationId, spec.Command2InternalRequest_State]) + httpInput := make(chan *spec.Command2InternalRequest) + httpOutput := make(chan *spec.Command2InternalRequest) + + go func() { + for c := range synccontrollerOutput { + select { + case main2grpcHandler <- c: + default: + logger.Info("buffer full", zap.String("buffer", "main2grpcHandler")) + } + select { + case httpInputKV <- c: + default: + logger.Info("buffer full", zap.String("buffer", "httpInputKV")) + } + } + }() + go func() { + for c := range httpInputKV { + httpInput <- &spec.Command2InternalRequest{Station: &spec.Stations{StationId: c.Key}, State: c.Value} + } + }() + + go func() { + for c := range httpOutput { + synccontrollerInput <- synccontroller.KV[spec.Stations_StationId, spec.Command2InternalRequest_State]{Key: c.GetStation().GetStationId(), Value: c.GetState()} + } + }() + + envVal := envStore.GetEnv() + + httpServer := httphandler.NewHTTPServer( + logger.Named("sync-controller"), + httpOutput, + httpInput, + envVal, + ) + StartStationSync(logger.Named("station-sync"), synccontrollerInput, synccontrollerOutput) + grpcHandler := NewGrpcHandler(logger.Named("grpc-handler"), envVal, main2grpcHandler, grpcHandlerInput) + + client2blocksync := make(chan synccontroller.KV[spec.Blocks_BlockId, spec.NotifyStateRequest_State]) + blocksync2client := make(chan synccontroller.KV[spec.Blocks_BlockId, spec.NotifyStateRequest_State]) + startBlockSync(logger.Named("blocksync"), client2blocksync, blocksync2client) + grpcBlockHandl := NewGrpcBlockHandler(logger.Named("grpc-block-handler"), envVal, client2blocksync, blocksync2client) + + go GRPCListenAndServe(ctx, logger, uint(envVal.ClientSideServer.GrpcPort), grpcHandler, grpcBlockHandl) + httpServer.StartServer() +} diff --git a/backend/external/pkg/syncController/initStation.go b/backend/external/internal/stationinit.go similarity index 85% rename from backend/external/pkg/syncController/initStation.go rename to backend/external/internal/stationinit.go index fb56911a..44be9300 100644 --- a/backend/external/pkg/syncController/initStation.go +++ b/backend/external/internal/stationinit.go @@ -1,6 +1,7 @@ -package syncController +package internal import ( + // embed for rule. _ "embed" "gopkg.in/yaml.v2" "log" @@ -17,7 +18,7 @@ type StationInitRule struct { //go:embed embed/stationInit.yml var initRuleFile []byte -func NewInitializeRule() *InitRule { +func NewRule() *InitRule { r := new(InitRule) err := yaml.Unmarshal(initRuleFile, r) if err != nil { diff --git a/backend/external/internal/stationsync.go b/backend/external/internal/stationsync.go new file mode 100644 index 00000000..96dcbeba --- /dev/null +++ b/backend/external/internal/stationsync.go @@ -0,0 +1,35 @@ +package internal + +import ( + "time" + "ueckoken/plarail2022-external/pkg/synccontroller" + "ueckoken/plarail2022-external/spec" + + "go.uber.org/zap" +) + +// StartStationSync starts sync controller for station state. +func StartStationSync(logger *zap.Logger, syncInput chan synccontroller.KV[spec.Stations_StationId, spec.Command2InternalRequest_State], syncOutput chan<- synccontroller.KV[spec.Stations_StationId, spec.Command2InternalRequest_State]) { + s := synccontroller.NewSyncController(logger, syncInput, syncOutput) + + go s.Run() + initStationSync(logger.Named("initialization"), NewRule(), syncInput) +} + +// InitStationSync initalize state. +func initStationSync(logger *zap.Logger, r *InitRule, initializer chan<- synccontroller.KV[spec.Stations_StationId, spec.Command2InternalRequest_State]) { + for _, sta := range r.Stations { + id, ok := spec.Stations_StationId_value[sta.Name] + if !ok { + logger.Error("unknown station name", zap.String("station", sta.Name)) + continue + } + state, ok := spec.RequestSync_State_value[sta.State] + if !ok { + logger.Error("unknown state", zap.String("state", sta.State)) + continue + } + initializer <- synccontroller.KV[spec.Stations_StationId, spec.Command2InternalRequest_State]{Key: spec.Stations_StationId(id), Value: spec.Command2InternalRequest_State(state)} + time.Sleep(500 * time.Millisecond) + } +} diff --git a/backend/external/pkg/clientHandler/clientHandler.go b/backend/external/pkg/clientHandler/clientHandler.go deleted file mode 100644 index a45b3460..00000000 --- a/backend/external/pkg/clientHandler/clientHandler.go +++ /dev/null @@ -1,140 +0,0 @@ -package clientHandler - -import ( - "context" - _ "embed" - "encoding/json" - "fmt" - "log" - "net/http" - "time" - "ueckoken/plarail2022-external/pkg/servo" - "ueckoken/plarail2022-external/pkg/syncController" - pb "ueckoken/plarail2022-external/spec" - - "github.com/gorilla/websocket" -) - -type ClientHandler struct { - Upgrader websocket.Upgrader - ClientCommand chan syncController.StationState - ClientChannelSend chan ClientChannel -} - -type ClientChannel struct { - ClientSync chan syncController.StationState - Done chan struct{} -} -type clientSendData struct { - StationName string `json:"station_name"` - State string `json:"state"` -} - -func (m ClientHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", r.RemoteAddr) - w.Header().Set("Access-Control-Allow-Credentials", "true") - w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization") - w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") - c, err := m.Upgrader.Upgrade(w, r, nil) - if err != nil { - log.Println(err) - return - } - defer c.Close() - ctx, cancel := context.WithCancel(context.Background()) - var cSync = make(chan syncController.StationState, 64) - var cDone = make(chan struct{}) - var cChannel = ClientChannel{cSync, cDone} - m.ClientChannelSend <- cChannel - c.SetPongHandler(func(string) error { - return c.SetReadDeadline(time.Now().Add(20 * time.Second)) - }) - c.SetCloseHandler(func(code int, text string) error { - log.Println("connection closed") - cancel() - return nil - }) - go handleClientCommand(ctx, c, &m) - go handleClientPing(ctx, c) - for cChan := range cChannel.ClientSync { - dat := clientSendData{StationName: pb.Stations_StationId_name[cChan.StationID], State: pb.RequestSync_State_name[cChan.State]} - err := c.WriteJSON(dat) - if err != nil { - log.Println("err", err) - cDone <- struct{}{} - cancel() - break - } - } -} - -func handleClientPing(ctx context.Context, c *websocket.Conn) { - ticker := time.NewTicker(10 * time.Second) - defer ticker.Stop() - for { - select { - case <-ticker.C: - if err := c.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(1*time.Second)); err != nil { - log.Printf("err occured in clientHandler.handleClientPing, err=%s", err) - } - case <-ctx.Done(): - ticker.Stop() - return - } - } -} - -func handleClientCommand(ctx context.Context, c *websocket.Conn, m *ClientHandler) { - for { - select { - case <-ctx.Done(): - return - default: - r, err := unpackClientSendData(c) - if err != nil { - log.Println(err) - return - } - m.ClientCommand <- *r - } - } -} - -func unpackClientSendData(c *websocket.Conn) (*syncController.StationState, error) { - _, msg, err := c.ReadMessage() - if err != nil { - return nil, fmt.Errorf("websocket read failed: %e", err) - } - var ud clientSendData - err = json.Unmarshal(msg, &ud) - if err != nil { - return nil, fmt.Errorf("bad json format: %e", err) - } - - station, ok := pb.Stations_StationId_value[ud.StationName] - if !ok { - return nil, fmt.Errorf("bad station format: %s", ud.StationName) - } - - state, ok := pb.RequestSync_State_value[ud.State] - if !ok { - return nil, fmt.Errorf("bad state format: %s", ud.State) - } - log.Printf("Received: StationID:%d, State:%d\n", station, state) - return &syncController.StationState{ - StationState: servo.StationState{ - StationID: station, - State: state, - }, - }, nil -} - -//go:embed embed/index.html -var IndexHTML []byte - -func HandleStatic(w http.ResponseWriter, r *http.Request) { - _, err := w.Write(IndexHTML) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - } -} diff --git a/backend/external/pkg/envStore/envStore.go b/backend/external/pkg/envStore/envStore.go index ca4a879a..4b872218 100644 --- a/backend/external/pkg/envStore/envStore.go +++ b/backend/external/pkg/envStore/envStore.go @@ -37,7 +37,9 @@ func (p *Port) Unmarshal(s string) error { type Env struct { ClientSideServer struct { - Port Port `envconfig:"default=54321"` + Port Port `envconfig:"default=54321"` + GrpcPort Port `envconfig:"default=9000"` + ATSAddress hostnamePort } InternalServer struct { Addr hostnamePort diff --git a/backend/external/pkg/httphandler/httphandler.go b/backend/external/pkg/httphandler/httphandler.go new file mode 100644 index 00000000..0c4fac15 --- /dev/null +++ b/backend/external/pkg/httphandler/httphandler.go @@ -0,0 +1,172 @@ +package httphandler + +import ( + "fmt" + "net/http" + "sync" + "time" + "ueckoken/plarail2022-external/pkg/envStore" + "ueckoken/plarail2022-external/pkg/websockethandler" + + "github.com/gorilla/mux" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + "go.uber.org/zap" + "google.golang.org/protobuf/proto" +) + +// HTTPServer is a server managing websockethandler and websocket clients. +type HTTPServer[T proto.Message] struct { + logger *zap.Logger + httpOutput chan<- T + httpInput <-chan T + environment *envStore.Env + numberOfClientConnection *prometheus.GaugeVec + totalClientConnection *prometheus.CounterVec + totalCLientCommands *prometheus.CounterVec + clients *ClientsCollection[T] +} + +// NewHTTPServer creates HTTP server. +func NewHTTPServer[T proto.Message](logger *zap.Logger, httpOutput chan<- T, httpInput <-chan T, env *envStore.Env) *HTTPServer[T] { + const namespace = "plarailexternal" + clientConn := prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: namespace, + Name: "clients_connections_seconds", + Help: "Number of connections handling websocket", + }, + []string{}, + ) + + clientConnTotal := prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: namespace, + Name: "clients_connections_total", + Help: "Total client connection", + }, + []string{}, + ) + + controlCommandTotal := prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: namespace, + Name: "client_commands_total", + Help: "Total client commands", + }, + []string{}, + ) + return &HTTPServer[T]{ + logger: logger, + httpOutput: httpOutput, + httpInput: httpInput, + environment: env, + numberOfClientConnection: clientConn, + totalClientConnection: clientConnTotal, + totalCLientCommands: controlCommandTotal, + clients: &ClientsCollection[T]{}, + } +} + +// ClientsCollection is a set of clients. +// When client is disconnected, this will remove it and stop sending data. +type ClientsCollection[T proto.Message] struct { + clients []websockethandler.ClientChannel[T] + mtx sync.Mutex +} + +// StartServer starts HTTP server and websocket server. +func (h *HTTPServer[T]) StartServer() { + clientChannelSend := make(chan websockethandler.ClientChannel[T]) + handlerInput := make(chan T) + go h.registerClient(clientChannelSend) + go h.broadcastChanges() + go h.unregisterClients() + r := mux.NewRouter() + prometheus.MustRegister(h.numberOfClientConnection) + prometheus.MustRegister(h.totalClientConnection) + prometheus.MustRegister(h.totalCLientCommands) + r.HandleFunc("/", websockethandler.HandleStatic) + r.Handle("/ws", websockethandler.NewClientHandler(h.logger.Named("ws-handler"), handlerInput, clientChannelSend)) + r.Handle("/metrics", promhttp.Handler()) + srv := &http.Server{ + Handler: r, + Addr: fmt.Sprintf("0.0.0.0:%d", h.environment.ClientSideServer.Port), + ReadHeaderTimeout: 5 * time.Second, + ReadTimeout: 5 * time.Second, + WriteTimeout: 5 * time.Second, + } + + h.logger.Info("start listening") + if err := srv.ListenAndServe(); err != nil { + h.logger.Panic("failed to serve", zap.Error(err)) + } +} + +// broadcastChanges receives the change from main channel and broadcast it to clients. +func (h *HTTPServer[T]) broadcastChanges() { + for d := range h.httpInput { + h.clients.mtx.Lock() + h.totalCLientCommands.With(prometheus.Labels{}).Inc() + for _, c := range h.clients.clients { + select { + case c.SyncToClient <- d: + default: + h.logger.Info("client buffer is full...") + continue + } + } + h.clients.mtx.Unlock() + } +} + +// registerClient registers a client that listening changes. +func (h *HTTPServer[T]) registerClient(cn <-chan websockethandler.ClientChannel[T]) { + for n := range cn { + func(h *HTTPServer[T], n websockethandler.ClientChannel[T]) { + h.clients.mtx.Lock() + defer h.clients.mtx.Unlock() + h.totalClientConnection.With(prometheus.Labels{}).Inc() + h.clients.clients = append(h.clients.clients, n) + }(h, n) + } +} + +// unregisterClients remove clients that seems to be disconnected. +func (h *HTTPServer[T]) unregisterClients() { + for { + h.clients.mtx.Lock() + var deletionList []int + for i, c := range h.clients.clients { + select { + case <-c.Done: + deletionList = append(deletionList, i) + default: + continue + } + } + h.clients.deleteClient(deletionList) + h.clients.mtx.Unlock() + h.numberOfClientConnection.With(prometheus.Labels{}).Set(float64(len(h.clients.clients))) + } +} + +// deleteClient deletes clients specified with arg. +func (cl *ClientsCollection[T]) deleteClient(deletion []int) { + var tmp []websockethandler.ClientChannel[T] + for i, c := range cl.clients { + if !contain(deletion, i) { + tmp = append(tmp, c) + } + } + cl.clients = tmp +} + +func contain(list []int, data int) bool { + for _, l := range list { + if l == data { + return true + } + } + return false +} diff --git a/backend/external/pkg/syncController/syncController.go b/backend/external/pkg/syncController/syncController.go deleted file mode 100644 index d3c172b5..00000000 --- a/backend/external/pkg/syncController/syncController.go +++ /dev/null @@ -1,160 +0,0 @@ -package syncController - -import ( - "errors" - "log" - "sync" - "time" - "ueckoken/plarail2022-external/pkg/envStore" - "ueckoken/plarail2022-external/pkg/servo" - "ueckoken/plarail2022-external/pkg/stationNameId" - "ueckoken/plarail2022-external/spec" -) - -type StationState struct { - servo.StationState -} - -type stationKVS struct { - stations []StationState - mtx sync.Mutex - validator IValidator -} - -func newStationKvs() *stationKVS { - v := NewRouteValidator() - skvs := stationKVS{validator: v} - return &skvs -} -func (skvs *stationKVS) update(u StationState) error { - skvs.mtx.Lock() - defer skvs.mtx.Unlock() - // err := skvs.validator.Validate(u, skvs.stations) - var err error - if err != nil { - return err - } - log.Printf("validation passed u=`%v`\n", u) - for i, s := range skvs.stations { - if s.StationID == u.StationID { - skvs.stations[i].State = u.State - return nil - } - } - skvs.stations = append(skvs.stations, u) - return nil -} - -// forceUpdate differs from update for ignore route validation. -func (skvs *stationKVS) forceUpdate(u StationState) { - skvs.mtx.Lock() - defer skvs.mtx.Unlock() - for i, s := range skvs.stations { - if s.StationID == u.StationID { - skvs.stations[i].State = u.State - return - } - } - skvs.stations = append(skvs.stations, u) -} -func (skvs *stationKVS) get(stationID int32) (station StationState, err error) { - skvs.mtx.Lock() - defer skvs.mtx.Unlock() - for _, s := range skvs.stations { - if s.StationID == stationID { - return s, nil - } - } - return StationState{}, errors.New("Not found") -} - -func (skvs *stationKVS) retrieve() []StationState { - return skvs.stations -} - -type SyncController struct { - ClientHandler2syncController chan StationState - SyncController2clientHandler chan StationState - InitServoRoute chan StationState - Environment *envStore.Env -} - -func (s *SyncController) StartSyncController() { - kvs := newStationKvs() - - go s.Init(NewInitializeRule()) - s.initNode(s.Environment, kvs) - - go s.periodicallySync(kvs) - s.triggeredSync(s.Environment, kvs) -} - -func (s *SyncController) initNode(e *envStore.Env, kvs *stationKVS) { - for c := range s.InitServoRoute { - kvs.forceUpdate(c) - c2i := servo.NewCommand2Internal(c.StationState, e) - log.Println("initNode: ", c2i.String()) - err := c2i.Send() - if err != nil { - log.Fatalf("initNode Send err: `%v`\n", err) - return - } - } -} - -func (s *SyncController) triggeredSync(e *envStore.Env, kvs *stationKVS) { - for c := range s.ClientHandler2syncController { - err := kvs.update(c) - if err != nil { - log.Println("syncController validator err: ", err) - continue - } - c2i := servo.NewCommand2Internal(c.StationState, e) - err = c2i.Send() - if err != nil { - log.Println("syncController send err: ", err) - continue - } - s.SyncController2clientHandler <- c - } -} - -func (s *SyncController) periodicallySync(kvs *stationKVS) { - ch := time.Tick(2 * time.Second) - for range ch { - kvs.mtx.Lock() - k := kvs.retrieve() - for _, st := range k { - select { - case s.SyncController2clientHandler <- st: - default: - log.Println("buffer full for:") - } - } - kvs.mtx.Unlock() - } -} - -func (s SyncController) Init(r *InitRule) { - for _, sta := range r.Stations { - id, err := stationNameId.Name2Id(sta.Name) - if err != nil { - log.Fatalln(err) - } - state, ok := spec.RequestSync_State_value[sta.State] - if !ok { - log.Fatalln(sta.State, "is incorrect") - } - s.InitServoRoute <- StationState{ - struct { - StationID int32 - State int32 - }{ - StationID: id, - State: state, - }, - } - time.Sleep(500 * time.Millisecond) - } - close(s.InitServoRoute) -} diff --git a/backend/external/pkg/syncController/syncController_test.go b/backend/external/pkg/syncController/syncController_test.go deleted file mode 100644 index ae3e9457..00000000 --- a/backend/external/pkg/syncController/syncController_test.go +++ /dev/null @@ -1,120 +0,0 @@ -package syncController - -import ( - "sync" - "testing" - "ueckoken/plarail2022-external/pkg/servo" -) - -func (skvs *stationKVS) contain(ss StationState) bool { - for _, s := range skvs.stations { - if s == ss { - return true - } - } - return false -} - -type ValidatorMock struct{} - -func (vm *ValidatorMock) Validate(_ StationState, _ []StationState) error { - return nil -} - -func TestSyncController_update(t *testing.T) { - station1 := StationState{servo.StationState{StationID: 1, State: 1}} - station2 := StationState{servo.StationState{StationID: 2, State: 1}} - kvs := stationKVS{ - stations: nil, - mtx: sync.Mutex{}, - validator: &ValidatorMock{}, - } - err := kvs.update(station1) - if err != nil { - t.Errorf("validate failed. `%v`", err) - } - if !kvs.contain(station1) { - t.Errorf("append failed") - } - - // new station append - err = kvs.update(station2) - if err != nil { - t.Errorf("validate failed. `%v`", err) - } - if !kvs.contain(station2) { - t.Errorf("station add failed") - } - if len(kvs.stations) != 2 { - t.Errorf("append failed") - } - if !kvs.contain(station1) { - t.Errorf("stations before update are not keeping") - } - if !kvs.contain(station1) && !kvs.contain(station2) { - t.Errorf("station2 is not append with `update` method") - } - - // update exist station data - station1 = StationState{servo.StationState{StationID: 1, State: 0}} - err = kvs.update(station1) - if err != nil { - t.Errorf("validate failed. `%v`", err) - } - if len(kvs.stations) != 2 { - t.Errorf("append failed") - } - if !kvs.contain(station1) { - t.Errorf("not update station data") - } -} -func TestSyncController_get(t *testing.T) { - station1 := StationState{servo.StationState{StationID: 1, State: 1}} - station2 := StationState{servo.StationState{StationID: 2, State: 1}} - skvs := stationKVS{ - stations: nil, - mtx: sync.Mutex{}, - } - // member is not exist - station, err := skvs.get(0) - if station != (StationState{}) { - t.Errorf("'station' is expect for empty but not empty") - } - - if err == nil { - t.Errorf("'err' is expect not nil") - } else if err.Error() != "Not found" { - t.Errorf("err.Error() expect 'Not found' but return %e", err) - } - - skvs = stationKVS{ - stations: []StationState{station1, station2}, - mtx: sync.Mutex{}, - } - station, err = skvs.get(1) - if station != station1 { - t.Errorf("'station1' is expect but called station%d", station.StationID) - } - if err != nil { - t.Errorf("return err is not nil: %e", err) - } - - station, err = skvs.get(2) - if station != station2 { - t.Errorf("'station2' is expect but called station%d", station.StationID) - } - if err != nil { - t.Errorf("return err is not nil: %e", err) - } - - // test for call 'get' not exist record - station, err = skvs.get(3) - if station != (StationState{}) { - t.Errorf("'station' is expect for empty but called station%d", station.StationID) - } - if err == nil { - t.Errorf("expect err but return nil") - } else if err.Error() != "Not found" { - t.Errorf("err.Error() expect 'Not found' but return %e", err) - } -} diff --git a/backend/external/pkg/syncController/validate.go b/backend/external/pkg/syncController/validate.go deleted file mode 100644 index c359b62e..00000000 --- a/backend/external/pkg/syncController/validate.go +++ /dev/null @@ -1,174 +0,0 @@ -package syncController - -import ( - _ "embed" - "fmt" - "reflect" - "ueckoken/plarail2022-external/pkg/servo" - "ueckoken/plarail2022-external/pkg/stationNameId" - "ueckoken/plarail2022-external/spec" - - "gopkg.in/yaml.v2" -) - -type IValidator interface { - Validate(state StationState, ss []StationState) error -} -type Validator struct { - Stations []Station `yaml:"stations"` -} -type Station struct { - Station EachStation `yaml:"station"` -} -type EachStation struct { - Name string `yaml:"name"` - Points []string `yaml:"points"` - Rules []Rule `yaml:"rules"` -} -type Rule struct { - On []string `yaml:"on,omitempty"` - Off []string `yaml:"off,omitempty"` -} - -type RuleSuite struct { - On int - Off int -} - -const ( - UNDEFINED = 0 - ALLOW = 1 - DENY = 2 -) - -//go:embed embed/stationRule.yml -var confFile []byte - -func NewRouteValidator() IValidator { - v := new(Validator) - err := yaml.Unmarshal(confFile, v) - if err != nil { - panic(err) - } - return v -} - -func (v *Validator) Validate(u StationState, ss []StationState) error { - targetSta, err := v.getValidateTarget(u) - if err != nil { - return err - } - // getValidateTarget は引数に取った駅がvalidateの対象外のときに空の構造体を返す - if reflect.DeepEqual(targetSta, &Station{}) { - return nil - } - beforeAfter := struct { - before bool - after bool - }{} - // 置き替え前 - allRuleRes, err := searchAllRules(targetSta.Station.Rules, ss) - if err != nil { - return err - } - beforeAfter.before = allRuleOk(allRuleRes) - - // 置き替え後 - id, err := searchIndex(u.StationID, ss) - if err != nil { - return err - } - ss[id] = StationState{servo.StationState{StationID: u.StationID, State: u.State}} - - allRuleRes, err = searchAllRules(targetSta.Station.Rules, ss) - if err != nil { - return err - } - beforeAfter.after = allRuleOk(allRuleRes) - if beforeAfter.before && !beforeAfter.after { - n, _ := stationNameId.ID2Name(u.StationID) - return fmt.Errorf("validation %s error ", n) - } - return nil -} -func (v *Validator) getValidateTarget(u StationState) (*Station, error) { - targetSta := new(Station) - for _, s := range v.Stations { - for _, pointName := range s.Station.Points { - id, err := stationNameId.Name2Id(pointName) - if err != nil { - return nil, err - } - if u.StationID == id { - *targetSta = s - break - } - } - } - return targetSta, nil -} - -func (r *RuleSuite) isOk() bool { - return r.On == ALLOW && r.Off == ALLOW -} -func allRuleOk(rules []RuleSuite) bool { - for _, r := range rules { - if r.isOk() { - return true - } - } - return false -} -func searchIndex(id int32, ss []StationState) (int, error) { - for i, s := range ss { - if s.StationID == id { - return i, nil - } - } - return -1, fmt.Errorf("index error") -} - -// matchRule. -func matchRule(rules []string, ss []StationState, state int32) (status int, err error) { - isSuiteRule := UNDEFINED - if rules == nil { - isSuiteRule = ALLOW - } - for _, rule := range rules { - id, err := stationNameId.Name2Id(rule) - if err != nil { - return -1, err - } - for _, kvsSta := range ss { - if isSuiteRule == DENY { - break - } - if kvsSta.StationID != id { - continue - } - // ルール合致 - if kvsSta.State == state { - isSuiteRule = ALLOW - } else { - isSuiteRule = DENY - } - break - } - } - return isSuiteRule, nil -} - -func searchAllRules(rules []Rule, ss []StationState) (ok []RuleSuite, err error) { - for _, rule := range rules { - isOnOk, err := matchRule(rule.On, ss, int32(spec.RequestSync_ON)) - if err != nil { - return nil, err - } - isOffOk, err := matchRule(rule.Off, ss, int32(spec.RequestSync_OFF)) - if err != nil { - return nil, err - } - ok = append(ok, RuleSuite{On: isOnOk, Off: isOffOk}) - } - return ok, nil -} diff --git a/backend/external/pkg/syncController/validate_test.go b/backend/external/pkg/syncController/validate_test.go deleted file mode 100644 index 811d39c7..00000000 --- a/backend/external/pkg/syncController/validate_test.go +++ /dev/null @@ -1,199 +0,0 @@ -package syncController - -import ( - "testing" - "ueckoken/plarail2022-external/pkg/servo" - "ueckoken/plarail2022-external/spec" -) - -const ( - ChofuP1 = int32(spec.Stations_chofu_p1) - ChofuP2 = int32(spec.Stations_chofu_p2) - ChofuS1 = int32(spec.Stations_chofu_s1) - ChofuS2 = int32(spec.Stations_chofu_s2) -) -const ( - ON = 1 - OFF = 2 -) - -func TestValidator_Validate(t *testing.T) { - type fields struct { - Stations []Station - } - type args struct { - u StationState - ss []StationState - } - rules := []Station{{ - EachStation{ - Name: "chofu_kudari", - Points: []string{"chofu_s1", "chofu_s2", "chofu_p1", "chofu_p2"}, - Rules: []Rule{ - { - On: nil, - Off: []string{"chofu_s1", "chofu_s2", "chofu_p1", "chofu_p2"}, - }, - { - On: []string{"chofu_s1"}, - Off: nil, - }, - { - On: []string{"chofu_s2"}, - Off: nil, - }, - }, - }, - }, - } - tests := []struct { - name string - fields fields - args args - wantErr bool - }{ - { - name: "ルールの1つ目に従う", - fields: fields{Stations: rules}, - args: args{ - u: StationState{servo.StationState{StationID: ChofuP1, State: OFF}}, - ss: []StationState{ - {servo.StationState{StationID: ChofuP1, State: ON}}, // chofu_b1,on - {servo.StationState{StationID: ChofuP2, State: OFF}}, // chofu_b2,off - {servo.StationState{StationID: ChofuS1, State: OFF}}, // chofu_s1,off - {servo.StationState{StationID: ChofuS2, State: OFF}}, // chofu_s2,off - }, - }, - wantErr: false, - }, - { - name: "ルールの2つ目に従う", - fields: fields{Stations: rules}, - args: args{ - u: StationState{servo.StationState{StationID: ChofuS1, State: ON}}, - ss: []StationState{ - {servo.StationState{StationID: ChofuP1, State: ON}}, - {servo.StationState{StationID: ChofuP2, State: ON}}, - {servo.StationState{StationID: ChofuS1, State: OFF}}, - {servo.StationState{StationID: ChofuS2, State: OFF}}, - }, - }, - wantErr: false, - }, - { - name: "ルールの3つ目に従う", - fields: fields{Stations: rules}, - args: args{ - u: StationState{servo.StationState{StationID: ChofuS2, State: ON}}, - ss: []StationState{ - {servo.StationState{StationID: ChofuP1, State: ON}}, - {servo.StationState{StationID: ChofuP2, State: ON}}, - {servo.StationState{StationID: ChofuS1, State: OFF}}, - {servo.StationState{StationID: ChofuS2, State: OFF}}, - }, - }, - wantErr: false, - }, - { - name: "2,3つ目のルールはONが複数あっても良い", - fields: fields{Stations: rules}, - args: args{ - u: StationState{servo.StationState{StationID: ChofuS2, State: ON}}, - ss: []StationState{ - {servo.StationState{StationID: ChofuP1, State: ON}}, - {servo.StationState{StationID: ChofuP2, State: ON}}, - {servo.StationState{StationID: ChofuS1, State: ON}}, - {servo.StationState{StationID: ChofuS2, State: OFF}}, - }, - }, - wantErr: false, - }, - { - name: "バリデートの対象外", - fields: fields{Stations: rules}, - args: args{u: StationState{servo.StationState{StationID: 10, State: OFF}}}, - wantErr: false, - }, - { - name: "3つ目のルール違反", - fields: fields{Stations: rules}, - args: args{ - u: StationState{servo.StationState{StationID: ChofuP1, State: ON}}, - ss: []StationState{ - {servo.StationState{StationID: ChofuP1, State: OFF}}, - {servo.StationState{StationID: ChofuP2, State: OFF}}, - {servo.StationState{StationID: ChofuS1, State: OFF}}, - {servo.StationState{StationID: ChofuS2, State: OFF}}, - }, - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - v := &Validator{ - Stations: tt.fields.Stations, - } - if err := v.Validate(tt.args.u, tt.args.ss); (err != nil) != tt.wantErr { - t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} - -func Test_matchRule(t *testing.T) { - type args struct { - rules []string - ss []StationState - state int32 - } - tests := []struct { - name string - args args - wantStatus int - wantErr bool - }{ - { - name: "rule not found", - args: args{ - rules: nil, - ss: []StationState{ - {servo.StationState{StationID: ChofuP1, State: OFF}}, - {servo.StationState{StationID: ChofuP2, State: OFF}}, - {servo.StationState{StationID: ChofuS1, State: OFF}}, - {servo.StationState{StationID: ChofuS2, State: OFF}}, - }, - state: OFF, - }, - wantStatus: ALLOW, - wantErr: false, - }, - { - name: "異常系", - args: args{ - rules: []string{"chofu_p1", "chofu_p2", "chofu_s1", "chofu_s2"}, - ss: []StationState{ - {servo.StationState{StationID: ChofuP1, State: OFF}}, - {servo.StationState{StationID: ChofuP2, State: OFF}}, - {servo.StationState{StationID: ChofuS1, State: ON}}, - {servo.StationState{StationID: ChofuS2, State: OFF}}, - }, - state: OFF, - }, - wantStatus: DENY, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotStatus, err := matchRule(tt.args.rules, tt.args.ss, tt.args.state) - if (err != nil) != tt.wantErr { - t.Errorf("matchRule() error = %v, wantErr %v", err, tt.wantErr) - return - } - if gotStatus != tt.wantStatus { - t.Errorf("matchRule() gotStatus = %v, want %v", gotStatus, tt.wantStatus) - } - }) - } -} diff --git a/backend/external/pkg/synccontroller/syncController.go b/backend/external/pkg/synccontroller/syncController.go new file mode 100644 index 00000000..3821e291 --- /dev/null +++ b/backend/external/pkg/synccontroller/syncController.go @@ -0,0 +1,102 @@ +package synccontroller + +import ( + "fmt" + "log" + "sync" + "time" + + "go.uber.org/zap" +) + +// KV is a pair of key and value. +type KV[T, U comparable] struct { + Key T + Value U +} + +// stationKVS is a primitive KVS to store KV. +type stationKVS[T, U comparable] struct { + values map[T]*U + mtx sync.Mutex +} + +func newStationKVS[T, U comparable]() *stationKVS[T, U] { + skvs := stationKVS[T, U]{values: make(map[T]*U, 0)} + return &skvs +} + +func (skvs *stationKVS[T, U]) update(kv KV[T, U]) error { + skvs.mtx.Lock() + defer skvs.mtx.Unlock() + skvs.values[kv.Key] = &kv.Value + return nil +} + +func (skvs *stationKVS[T, U]) get(key T) (value *U, err error) { + skvs.mtx.Lock() + defer skvs.mtx.Unlock() + dat, ok := skvs.values[key] + if !ok { + return nil, fmt.Errorf("not found such key=%+v", key) + } + return dat, nil + +} + +func (skvs *stationKVS[T, U]) retrieve() map[T]*U { + return skvs.values +} + +// SyncController is a struct that stores KVs. +type SyncController[T, U comparable] struct { + logger *zap.Logger + stateInput <-chan KV[T, U] + stateOutput chan<- KV[T, U] + kvs *stationKVS[T, U] +} + +// NewSyncController creates new sync controller. +// This update KVs when a KV arrives in stateInput, if runs triggeredSync. +func NewSyncController[T, U comparable](logger *zap.Logger, stateInput <-chan KV[T, U], stateOutput chan<- KV[T, U]) *SyncController[T, U] { + return &SyncController[T, U]{logger: logger, stateInput: stateInput, stateOutput: stateOutput, kvs: newStationKVS[T, U]()} +} + +// Run runs sync controller, and this will block forever. +// This receives and saves state from stateInput. +// This emits state when received, or periodically. +func (s *SyncController[T, U]) Run() { + go s.periodicallySync() + s.triggeredSync() +} + +func (s *SyncController[T, U]) triggeredSync() { + for c := range s.stateInput { + s.logger.Info("state input received", zap.Any("state", c)) + err := s.kvs.update(c) + if err != nil { + log.Println("syncController validator err: ", err) + continue + } + s.stateOutput <- c + } +} + +func (s *SyncController[T, U]) periodicallySync() { + ch := time.Tick(2 * time.Second) + for range ch { + func() { + s.kvs.mtx.Lock() + d := s.kvs.retrieve() + for key, value := range d { + select { + case s.stateOutput <- KV[T, U]{Key: key, Value: *value}: + default: + s.logger.Info("buffer full") + } + } + s.kvs.mtx.Unlock() + s.logger.Info("unlocked") + }() + } +} diff --git a/backend/external/pkg/synccontroller/syncController_test.go b/backend/external/pkg/synccontroller/syncController_test.go new file mode 100644 index 00000000..c713ca65 --- /dev/null +++ b/backend/external/pkg/synccontroller/syncController_test.go @@ -0,0 +1,110 @@ +package synccontroller + +import ( + "strings" + "testing" + "ueckoken/plarail2022-external/spec" +) + +func (skvs *stationKVS[T, U]) contain(dat KV[T, U]) bool { + d, ok := skvs.values[dat.Key] + if !ok { + return false + } + if *d != dat.Value { + return false + } + return true +} + +type ValidatorMock struct{} + +func TestSyncController_update(t *testing.T) { + station1 := KV[spec.Stations_StationId, spec.Command2InternalRequest_State]{Key: spec.Stations_StationId(1), Value: spec.Command2InternalRequest_State(1)} + station2 := KV[spec.Stations_StationId, spec.Command2InternalRequest_State]{Key: spec.Stations_StationId(2), Value: spec.Command2InternalRequest_State(1)} + kvs := newStationKVS[spec.Stations_StationId, spec.Command2InternalRequest_State]() + err := kvs.update(station1) + if err != nil { + t.Errorf("validate failed. `%v`", err) + } + if !kvs.contain(station1) { + t.Errorf("append failed") + } + + // new station append + err = kvs.update(station2) + if err != nil { + t.Errorf("validate failed. `%v`", err) + } + if !kvs.contain(station2) { + t.Errorf("station add failed") + } + if len(kvs.values) != 2 { + t.Errorf("append failed") + } + if !kvs.contain(station1) { + t.Errorf("stations before update are not keeping") + } + if !kvs.contain(station1) && !kvs.contain(station2) { + t.Errorf("station2 is not append with `update` method") + } + + // update exist station data + station1 = KV[spec.Stations_StationId, spec.Command2InternalRequest_State]{Key: spec.Stations_StationId(1), Value: spec.Command2InternalRequest_State(0)} + err = kvs.update(station1) + if err != nil { + t.Errorf("validate failed. `%v`", err) + } + if len(kvs.values) != 2 { + t.Errorf("append failed") + } + if !kvs.contain(station1) { + t.Errorf("not update station data") + } +} +func TestSyncController_get(t *testing.T) { + station1 := KV[spec.Stations_StationId, spec.Command2InternalRequest_State]{Key: spec.Stations_StationId(1), Value: spec.Command2InternalRequest_State(1)} + station2 := KV[spec.Stations_StationId, spec.Command2InternalRequest_State]{Key: spec.Stations_StationId(2), Value: spec.Command2InternalRequest_State(1)} + kvs := newStationKVS[spec.Stations_StationId, spec.Command2InternalRequest_State]() + // member is not exist + _, err := kvs.get(0) + if err == nil { + t.Errorf("'err' is expect not nil") + } else if !strings.Contains(err.Error(), "not found") { + t.Errorf("err.Error() expect 'Not found' but return %e", err) + } + + kvs = newStationKVS[spec.Stations_StationId, spec.Command2InternalRequest_State]() + err = kvs.update(station1) + if err != nil { + t.Errorf("return err is not nil: %e", err) + } + station, err := kvs.get(spec.Stations_StationId(1)) + if err != nil { + t.Errorf("return err is not nil: %e", err) + } + if *station != station1.Value { + t.Errorf("'station1' is expect but called station%d", station) + } + + err = kvs.update(station2) + if err != nil { + t.Errorf("return err is not nil: %e", err) + } + station, err = kvs.get(2) + if err != nil { + t.Errorf("return err is not nil: %e", err) + } + if *station != station2.Value { + t.Errorf("'station2' is expect but called station%d", station) + } + + // test for call 'get' not exist record + _, err = kvs.get(3) + if err == nil { + t.Errorf("expect err but return nil") + } + if !strings.Contains(err.Error(), "not found") { + t.Errorf("err.Error() expect 'Not found' but return %e", err) + } +} diff --git a/backend/external/pkg/clientHandler/embed/index.html b/backend/external/pkg/websockethandler/embed/index.html similarity index 81% rename from backend/external/pkg/clientHandler/embed/index.html rename to backend/external/pkg/websockethandler/embed/index.html index 87140c15..d9b2b711 100644 --- a/backend/external/pkg/clientHandler/embed/index.html +++ b/backend/external/pkg/websockethandler/embed/index.html @@ -11,10 +11,10 @@ p1.innerHTML = data; }; window.onload = function () { - sock = new WebSocket("wss://" + location.host + "/ws"); + sock = new WebSocket("ws://" + location.host + "/ws"); sock.onmessage = function (event) { var data = JSON.parse(event.data); - sock.send(`{"station_name": "chofu_b1", "state": "ON"}`); + sock.send(`{"station": {"stationId":26}, "state": 1}`); console.log(data); update(); }; diff --git a/backend/external/pkg/websockethandler/websockethandler.go b/backend/external/pkg/websockethandler/websockethandler.go new file mode 100644 index 00000000..9733f1a7 --- /dev/null +++ b/backend/external/pkg/websockethandler/websockethandler.go @@ -0,0 +1,134 @@ +package websockethandler + +import ( + "context" + _ "embed" + "encoding/json" + "fmt" + "net/http" + "time" + + "github.com/gorilla/websocket" + "go.uber.org/zap" + "google.golang.org/protobuf/proto" +) + +type ClientHandler[T proto.Message] struct { + logger *zap.Logger + upgrader websocket.Upgrader + commandFromClient chan<- T + channelClient chan ClientChannel[T] +} + +// NewClientHandler creates client handler. +func NewClientHandler[T proto.Message](logger *zap.Logger, clientHandlerOutput chan<- T, channelClient chan ClientChannel[T]) ClientHandler[T] { + return ClientHandler[T]{ + logger: logger, + upgrader: websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, + CheckOrigin: func(r *http.Request) bool { + return true + }}, + commandFromClient: clientHandlerOutput, + channelClient: channelClient, + } +} + +type ClientChannel[T proto.Message] struct { + SyncToClient chan T + Done chan struct{} +} + +func (m ClientHandler[T]) ServeHTTP(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Origin", r.RemoteAddr) + w.Header().Set("Access-Control-Allow-Credentials", "true") + w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization") + w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") + c, err := m.upgrader.Upgrade(w, r, nil) + if err != nil { + m.logger.Error("failed to upgrade", zap.Error(err)) + return + } + defer c.Close() + ctx, cancel := context.WithCancel(context.Background()) + var cSync = make(chan T) + var cDone = make(chan struct{}) + var cChannel = ClientChannel[T]{cSync, cDone} + m.channelClient <- cChannel + c.SetPongHandler(func(string) error { + return c.SetReadDeadline(time.Now().Add(20 * time.Second)) + }) + c.SetCloseHandler(func(code int, text string) error { + m.logger.Info("connection closed") + cancel() + return nil + }) + go handleClientCommand(ctx, m.logger.Named("client-handler"), c, m.commandFromClient) + go handleClientPing(ctx, m.logger.Named("ping-handler"), c) + for cChan := range cChannel.SyncToClient { + err := c.WriteJSON(cChan) + if err != nil { + m.logger.Info("failed to send data to client, closing connection...", zap.Error(err)) + cDone <- struct{}{} + cancel() + break + } + } +} + +func handleClientPing(ctx context.Context, logger *zap.Logger, c *websocket.Conn) { + ticker := time.NewTicker(10 * time.Second) + defer ticker.Stop() + for { + select { + case <-ticker.C: + if err := c.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(1*time.Second)); err != nil { + logger.Error("failed to send ping", zap.Error(err)) + } + case <-ctx.Done(): + ticker.Stop() + return + } + } +} + +func handleClientCommand[T proto.Message](ctx context.Context, logger *zap.Logger, c *websocket.Conn, ch chan<- T) { + for { + select { + case <-ctx.Done(): + return + default: + r, err := readClientData[T](c) + if err != nil { + logger.Error("failed to read client packet", zap.Error(err)) + return + } + ch <- *r + } + } +} + +func readClientData[T proto.Message](c *websocket.Conn) (*T, error) { + _, msg, err := c.ReadMessage() + if err != nil { + return nil, fmt.Errorf("websocket read failed: %e", err) + } + var ud T + err = json.Unmarshal(msg, &ud) + if err != nil { + return nil, fmt.Errorf("bad json format: %e", err) + } + + return &ud, nil +} + +//go:embed embed/index.html +var IndexHTML []byte + +func HandleStatic(w http.ResponseWriter, r *http.Request) { + _, err := w.Write(IndexHTML) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + } +} diff --git a/backend/external/spec/block.pb.go b/backend/external/spec/block.pb.go new file mode 100644 index 00000000..1632db3a --- /dev/null +++ b/backend/external/spec/block.pb.go @@ -0,0 +1,541 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.20.3 +// source: proto/block.proto + +package spec + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type NotifyStateRequest_State int32 + +const ( + NotifyStateRequest_UNKNOWN NotifyStateRequest_State = 0 + NotifyStateRequest_OPEN NotifyStateRequest_State = 1 + NotifyStateRequest_CLOSE NotifyStateRequest_State = 2 +) + +// Enum value maps for NotifyStateRequest_State. +var ( + NotifyStateRequest_State_name = map[int32]string{ + 0: "UNKNOWN", + 1: "OPEN", + 2: "CLOSE", + } + NotifyStateRequest_State_value = map[string]int32{ + "UNKNOWN": 0, + "OPEN": 1, + "CLOSE": 2, + } +) + +func (x NotifyStateRequest_State) Enum() *NotifyStateRequest_State { + p := new(NotifyStateRequest_State) + *p = x + return p +} + +func (x NotifyStateRequest_State) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (NotifyStateRequest_State) Descriptor() protoreflect.EnumDescriptor { + return file_proto_block_proto_enumTypes[0].Descriptor() +} + +func (NotifyStateRequest_State) Type() protoreflect.EnumType { + return &file_proto_block_proto_enumTypes[0] +} + +func (x NotifyStateRequest_State) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use NotifyStateRequest_State.Descriptor instead. +func (NotifyStateRequest_State) EnumDescriptor() ([]byte, []int) { + return file_proto_block_proto_rawDescGZIP(), []int{0, 0} +} + +type NotifyStateResponse_Response int32 + +const ( + NotifyStateResponse_UNKNOWN NotifyStateResponse_Response = 0 + NotifyStateResponse_SUCCESS NotifyStateResponse_Response = 1 + NotifyStateResponse_FAILED NotifyStateResponse_Response = 2 +) + +// Enum value maps for NotifyStateResponse_Response. +var ( + NotifyStateResponse_Response_name = map[int32]string{ + 0: "UNKNOWN", + 1: "SUCCESS", + 2: "FAILED", + } + NotifyStateResponse_Response_value = map[string]int32{ + "UNKNOWN": 0, + "SUCCESS": 1, + "FAILED": 2, + } +) + +func (x NotifyStateResponse_Response) Enum() *NotifyStateResponse_Response { + p := new(NotifyStateResponse_Response) + *p = x + return p +} + +func (x NotifyStateResponse_Response) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (NotifyStateResponse_Response) Descriptor() protoreflect.EnumDescriptor { + return file_proto_block_proto_enumTypes[1].Descriptor() +} + +func (NotifyStateResponse_Response) Type() protoreflect.EnumType { + return &file_proto_block_proto_enumTypes[1] +} + +func (x NotifyStateResponse_Response) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use NotifyStateResponse_Response.Descriptor instead. +func (NotifyStateResponse_Response) EnumDescriptor() ([]byte, []int) { + return file_proto_block_proto_rawDescGZIP(), []int{1, 0} +} + +type Blocks_BlockId int32 + +const ( + Blocks_unknown Blocks_BlockId = 0 + Blocks_shinjuku_sakurajosui_up Blocks_BlockId = 10 + Blocks_shinjuku_sakurajosui_down Blocks_BlockId = 11 + Blocks_sakurajosui_chofu_up Blocks_BlockId = 20 + Blocks_sakurajosui_chofu_down Blocks_BlockId = 21 + Blocks_chofu_hachioji_up Blocks_BlockId = 30 + Blocks_chofu_hachioji_down Blocks_BlockId = 31 + Blocks_chofu_hashimoto_up Blocks_BlockId = 40 + Blocks_chofu_hashimoto_down Blocks_BlockId = 41 + Blocks_shinjuku_b1 Blocks_BlockId = 100 + Blocks_shinjuku_b2 Blocks_BlockId = 101 + Blocks_sakurajosui_b1 Blocks_BlockId = 110 + Blocks_sakurajosui_b2 Blocks_BlockId = 111 + Blocks_sakurajosui_b3 Blocks_BlockId = 120 + Blocks_sakurajosui_b4 Blocks_BlockId = 121 + Blocks_chofu_b1 Blocks_BlockId = 130 + Blocks_chofu_b2 Blocks_BlockId = 131 + Blocks_chofu_b3 Blocks_BlockId = 132 + Blocks_chofu_b4 Blocks_BlockId = 133 + Blocks_hashimoto_b1 Blocks_BlockId = 140 + Blocks_hashimoto_b2 Blocks_BlockId = 141 + Blocks_hachioji_b1 Blocks_BlockId = 150 + Blocks_hachioji_b2 Blocks_BlockId = 151 +) + +// Enum value maps for Blocks_BlockId. +var ( + Blocks_BlockId_name = map[int32]string{ + 0: "unknown", + 10: "shinjuku_sakurajosui_up", + 11: "shinjuku_sakurajosui_down", + 20: "sakurajosui_chofu_up", + 21: "sakurajosui_chofu_down", + 30: "chofu_hachioji_up", + 31: "chofu_hachioji_down", + 40: "chofu_hashimoto_up", + 41: "chofu_hashimoto_down", + 100: "shinjuku_b1", + 101: "shinjuku_b2", + 110: "sakurajosui_b1", + 111: "sakurajosui_b2", + 120: "sakurajosui_b3", + 121: "sakurajosui_b4", + 130: "chofu_b1", + 131: "chofu_b2", + 132: "chofu_b3", + 133: "chofu_b4", + 140: "hashimoto_b1", + 141: "hashimoto_b2", + 150: "hachioji_b1", + 151: "hachioji_b2", + } + Blocks_BlockId_value = map[string]int32{ + "unknown": 0, + "shinjuku_sakurajosui_up": 10, + "shinjuku_sakurajosui_down": 11, + "sakurajosui_chofu_up": 20, + "sakurajosui_chofu_down": 21, + "chofu_hachioji_up": 30, + "chofu_hachioji_down": 31, + "chofu_hashimoto_up": 40, + "chofu_hashimoto_down": 41, + "shinjuku_b1": 100, + "shinjuku_b2": 101, + "sakurajosui_b1": 110, + "sakurajosui_b2": 111, + "sakurajosui_b3": 120, + "sakurajosui_b4": 121, + "chofu_b1": 130, + "chofu_b2": 131, + "chofu_b3": 132, + "chofu_b4": 133, + "hashimoto_b1": 140, + "hashimoto_b2": 141, + "hachioji_b1": 150, + "hachioji_b2": 151, + } +) + +func (x Blocks_BlockId) Enum() *Blocks_BlockId { + p := new(Blocks_BlockId) + *p = x + return p +} + +func (x Blocks_BlockId) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Blocks_BlockId) Descriptor() protoreflect.EnumDescriptor { + return file_proto_block_proto_enumTypes[2].Descriptor() +} + +func (Blocks_BlockId) Type() protoreflect.EnumType { + return &file_proto_block_proto_enumTypes[2] +} + +func (x Blocks_BlockId) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Blocks_BlockId.Descriptor instead. +func (Blocks_BlockId) EnumDescriptor() ([]byte, []int) { + return file_proto_block_proto_rawDescGZIP(), []int{2, 0} +} + +type NotifyStateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + State NotifyStateRequest_State `protobuf:"varint,2,opt,name=state,proto3,enum=NotifyStateRequest_State" json:"state,omitempty"` + Block *Blocks `protobuf:"bytes,3,opt,name=block,proto3" json:"block,omitempty"` +} + +func (x *NotifyStateRequest) Reset() { + *x = NotifyStateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_block_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NotifyStateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NotifyStateRequest) ProtoMessage() {} + +func (x *NotifyStateRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_block_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NotifyStateRequest.ProtoReflect.Descriptor instead. +func (*NotifyStateRequest) Descriptor() ([]byte, []int) { + return file_proto_block_proto_rawDescGZIP(), []int{0} +} + +func (x *NotifyStateRequest) GetState() NotifyStateRequest_State { + if x != nil { + return x.State + } + return NotifyStateRequest_UNKNOWN +} + +func (x *NotifyStateRequest) GetBlock() *Blocks { + if x != nil { + return x.Block + } + return nil +} + +type NotifyStateResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Response NotifyStateResponse_Response `protobuf:"varint,1,opt,name=response,proto3,enum=NotifyStateResponse_Response" json:"response,omitempty"` +} + +func (x *NotifyStateResponse) Reset() { + *x = NotifyStateResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_block_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NotifyStateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NotifyStateResponse) ProtoMessage() {} + +func (x *NotifyStateResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_block_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NotifyStateResponse.ProtoReflect.Descriptor instead. +func (*NotifyStateResponse) Descriptor() ([]byte, []int) { + return file_proto_block_proto_rawDescGZIP(), []int{1} +} + +func (x *NotifyStateResponse) GetResponse() NotifyStateResponse_Response { + if x != nil { + return x.Response + } + return NotifyStateResponse_UNKNOWN +} + +type Blocks struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + BlockId Blocks_BlockId `protobuf:"varint,3,opt,name=blockId,proto3,enum=Blocks_BlockId" json:"blockId,omitempty"` +} + +func (x *Blocks) Reset() { + *x = Blocks{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_block_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Blocks) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Blocks) ProtoMessage() {} + +func (x *Blocks) ProtoReflect() protoreflect.Message { + mi := &file_proto_block_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Blocks.ProtoReflect.Descriptor instead. +func (*Blocks) Descriptor() ([]byte, []int) { + return file_proto_block_proto_rawDescGZIP(), []int{2} +} + +func (x *Blocks) GetBlockId() Blocks_BlockId { + if x != nil { + return x.BlockId + } + return Blocks_unknown +} + +var File_proto_block_proto protoreflect.FileDescriptor + +var file_proto_block_proto_rawDesc = []byte{ + 0x0a, 0x11, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x22, 0x8f, 0x01, 0x0a, 0x12, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x05, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x4e, 0x6f, 0x74, 0x69, + 0x66, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x05, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x73, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x29, 0x0a, 0x05, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, + 0x12, 0x08, 0x0a, 0x04, 0x4f, 0x50, 0x45, 0x4e, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x43, 0x4c, + 0x4f, 0x53, 0x45, 0x10, 0x02, 0x22, 0x82, 0x01, 0x0a, 0x13, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, + 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x1d, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, + 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x30, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, + 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x01, 0x12, 0x0a, + 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x22, 0x98, 0x04, 0x0a, 0x06, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x29, 0x0a, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0f, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2e, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, + 0x22, 0xe2, 0x03, 0x0a, 0x07, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x0b, 0x0a, 0x07, + 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x73, 0x68, 0x69, + 0x6e, 0x6a, 0x75, 0x6b, 0x75, 0x5f, 0x73, 0x61, 0x6b, 0x75, 0x72, 0x61, 0x6a, 0x6f, 0x73, 0x75, + 0x69, 0x5f, 0x75, 0x70, 0x10, 0x0a, 0x12, 0x1d, 0x0a, 0x19, 0x73, 0x68, 0x69, 0x6e, 0x6a, 0x75, + 0x6b, 0x75, 0x5f, 0x73, 0x61, 0x6b, 0x75, 0x72, 0x61, 0x6a, 0x6f, 0x73, 0x75, 0x69, 0x5f, 0x64, + 0x6f, 0x77, 0x6e, 0x10, 0x0b, 0x12, 0x18, 0x0a, 0x14, 0x73, 0x61, 0x6b, 0x75, 0x72, 0x61, 0x6a, + 0x6f, 0x73, 0x75, 0x69, 0x5f, 0x63, 0x68, 0x6f, 0x66, 0x75, 0x5f, 0x75, 0x70, 0x10, 0x14, 0x12, + 0x1a, 0x0a, 0x16, 0x73, 0x61, 0x6b, 0x75, 0x72, 0x61, 0x6a, 0x6f, 0x73, 0x75, 0x69, 0x5f, 0x63, + 0x68, 0x6f, 0x66, 0x75, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x10, 0x15, 0x12, 0x15, 0x0a, 0x11, 0x63, + 0x68, 0x6f, 0x66, 0x75, 0x5f, 0x68, 0x61, 0x63, 0x68, 0x69, 0x6f, 0x6a, 0x69, 0x5f, 0x75, 0x70, + 0x10, 0x1e, 0x12, 0x17, 0x0a, 0x13, 0x63, 0x68, 0x6f, 0x66, 0x75, 0x5f, 0x68, 0x61, 0x63, 0x68, + 0x69, 0x6f, 0x6a, 0x69, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x10, 0x1f, 0x12, 0x16, 0x0a, 0x12, 0x63, + 0x68, 0x6f, 0x66, 0x75, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x6d, 0x6f, 0x74, 0x6f, 0x5f, 0x75, + 0x70, 0x10, 0x28, 0x12, 0x18, 0x0a, 0x14, 0x63, 0x68, 0x6f, 0x66, 0x75, 0x5f, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x6d, 0x6f, 0x74, 0x6f, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x10, 0x29, 0x12, 0x0f, 0x0a, + 0x0b, 0x73, 0x68, 0x69, 0x6e, 0x6a, 0x75, 0x6b, 0x75, 0x5f, 0x62, 0x31, 0x10, 0x64, 0x12, 0x0f, + 0x0a, 0x0b, 0x73, 0x68, 0x69, 0x6e, 0x6a, 0x75, 0x6b, 0x75, 0x5f, 0x62, 0x32, 0x10, 0x65, 0x12, + 0x12, 0x0a, 0x0e, 0x73, 0x61, 0x6b, 0x75, 0x72, 0x61, 0x6a, 0x6f, 0x73, 0x75, 0x69, 0x5f, 0x62, + 0x31, 0x10, 0x6e, 0x12, 0x12, 0x0a, 0x0e, 0x73, 0x61, 0x6b, 0x75, 0x72, 0x61, 0x6a, 0x6f, 0x73, + 0x75, 0x69, 0x5f, 0x62, 0x32, 0x10, 0x6f, 0x12, 0x12, 0x0a, 0x0e, 0x73, 0x61, 0x6b, 0x75, 0x72, + 0x61, 0x6a, 0x6f, 0x73, 0x75, 0x69, 0x5f, 0x62, 0x33, 0x10, 0x78, 0x12, 0x12, 0x0a, 0x0e, 0x73, + 0x61, 0x6b, 0x75, 0x72, 0x61, 0x6a, 0x6f, 0x73, 0x75, 0x69, 0x5f, 0x62, 0x34, 0x10, 0x79, 0x12, + 0x0d, 0x0a, 0x08, 0x63, 0x68, 0x6f, 0x66, 0x75, 0x5f, 0x62, 0x31, 0x10, 0x82, 0x01, 0x12, 0x0d, + 0x0a, 0x08, 0x63, 0x68, 0x6f, 0x66, 0x75, 0x5f, 0x62, 0x32, 0x10, 0x83, 0x01, 0x12, 0x0d, 0x0a, + 0x08, 0x63, 0x68, 0x6f, 0x66, 0x75, 0x5f, 0x62, 0x33, 0x10, 0x84, 0x01, 0x12, 0x0d, 0x0a, 0x08, + 0x63, 0x68, 0x6f, 0x66, 0x75, 0x5f, 0x62, 0x34, 0x10, 0x85, 0x01, 0x12, 0x11, 0x0a, 0x0c, 0x68, + 0x61, 0x73, 0x68, 0x69, 0x6d, 0x6f, 0x74, 0x6f, 0x5f, 0x62, 0x31, 0x10, 0x8c, 0x01, 0x12, 0x11, + 0x0a, 0x0c, 0x68, 0x61, 0x73, 0x68, 0x69, 0x6d, 0x6f, 0x74, 0x6f, 0x5f, 0x62, 0x32, 0x10, 0x8d, + 0x01, 0x12, 0x10, 0x0a, 0x0b, 0x68, 0x61, 0x63, 0x68, 0x69, 0x6f, 0x6a, 0x69, 0x5f, 0x62, 0x31, + 0x10, 0x96, 0x01, 0x12, 0x10, 0x0a, 0x0b, 0x68, 0x61, 0x63, 0x68, 0x69, 0x6f, 0x6a, 0x69, 0x5f, + 0x62, 0x32, 0x10, 0x97, 0x01, 0x32, 0x4c, 0x0a, 0x0e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x3a, 0x0a, 0x0b, 0x4e, 0x6f, 0x74, 0x69, 0x66, + 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x13, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x4e, 0x6f, + 0x74, 0x69, 0x66, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2e, 0x2f, 0x73, 0x70, 0x65, 0x63, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_proto_block_proto_rawDescOnce sync.Once + file_proto_block_proto_rawDescData = file_proto_block_proto_rawDesc +) + +func file_proto_block_proto_rawDescGZIP() []byte { + file_proto_block_proto_rawDescOnce.Do(func() { + file_proto_block_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_block_proto_rawDescData) + }) + return file_proto_block_proto_rawDescData +} + +var file_proto_block_proto_enumTypes = make([]protoimpl.EnumInfo, 3) +var file_proto_block_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_proto_block_proto_goTypes = []interface{}{ + (NotifyStateRequest_State)(0), // 0: NotifyStateRequest.State + (NotifyStateResponse_Response)(0), // 1: NotifyStateResponse.Response + (Blocks_BlockId)(0), // 2: Blocks.BlockId + (*NotifyStateRequest)(nil), // 3: NotifyStateRequest + (*NotifyStateResponse)(nil), // 4: NotifyStateResponse + (*Blocks)(nil), // 5: Blocks +} +var file_proto_block_proto_depIdxs = []int32{ + 0, // 0: NotifyStateRequest.state:type_name -> NotifyStateRequest.State + 5, // 1: NotifyStateRequest.block:type_name -> Blocks + 1, // 2: NotifyStateResponse.response:type_name -> NotifyStateResponse.Response + 2, // 3: Blocks.blockId:type_name -> Blocks.BlockId + 3, // 4: BlockStateSync.NotifyState:input_type -> NotifyStateRequest + 4, // 5: BlockStateSync.NotifyState:output_type -> NotifyStateResponse + 5, // [5:6] is the sub-list for method output_type + 4, // [4:5] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name +} + +func init() { file_proto_block_proto_init() } +func file_proto_block_proto_init() { + if File_proto_block_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_proto_block_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NotifyStateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_block_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NotifyStateResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_block_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Blocks); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_proto_block_proto_rawDesc, + NumEnums: 3, + NumMessages: 3, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_proto_block_proto_goTypes, + DependencyIndexes: file_proto_block_proto_depIdxs, + EnumInfos: file_proto_block_proto_enumTypes, + MessageInfos: file_proto_block_proto_msgTypes, + }.Build() + File_proto_block_proto = out.File + file_proto_block_proto_rawDesc = nil + file_proto_block_proto_goTypes = nil + file_proto_block_proto_depIdxs = nil +} diff --git a/backend/external/spec/block_grpc.pb.go b/backend/external/spec/block_grpc.pb.go new file mode 100644 index 00000000..2bad95d1 --- /dev/null +++ b/backend/external/spec/block_grpc.pb.go @@ -0,0 +1,105 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.2.0 +// - protoc v3.20.3 +// source: proto/block.proto + +package spec + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// BlockStateSyncClient is the client API for BlockStateSync service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type BlockStateSyncClient interface { + NotifyState(ctx context.Context, in *NotifyStateRequest, opts ...grpc.CallOption) (*NotifyStateResponse, error) +} + +type blockStateSyncClient struct { + cc grpc.ClientConnInterface +} + +func NewBlockStateSyncClient(cc grpc.ClientConnInterface) BlockStateSyncClient { + return &blockStateSyncClient{cc} +} + +func (c *blockStateSyncClient) NotifyState(ctx context.Context, in *NotifyStateRequest, opts ...grpc.CallOption) (*NotifyStateResponse, error) { + out := new(NotifyStateResponse) + err := c.cc.Invoke(ctx, "/BlockStateSync/NotifyState", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// BlockStateSyncServer is the server API for BlockStateSync service. +// All implementations must embed UnimplementedBlockStateSyncServer +// for forward compatibility +type BlockStateSyncServer interface { + NotifyState(context.Context, *NotifyStateRequest) (*NotifyStateResponse, error) + mustEmbedUnimplementedBlockStateSyncServer() +} + +// UnimplementedBlockStateSyncServer must be embedded to have forward compatible implementations. +type UnimplementedBlockStateSyncServer struct { +} + +func (UnimplementedBlockStateSyncServer) NotifyState(context.Context, *NotifyStateRequest) (*NotifyStateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method NotifyState not implemented") +} +func (UnimplementedBlockStateSyncServer) mustEmbedUnimplementedBlockStateSyncServer() {} + +// UnsafeBlockStateSyncServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to BlockStateSyncServer will +// result in compilation errors. +type UnsafeBlockStateSyncServer interface { + mustEmbedUnimplementedBlockStateSyncServer() +} + +func RegisterBlockStateSyncServer(s grpc.ServiceRegistrar, srv BlockStateSyncServer) { + s.RegisterService(&BlockStateSync_ServiceDesc, srv) +} + +func _BlockStateSync_NotifyState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(NotifyStateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BlockStateSyncServer).NotifyState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/BlockStateSync/NotifyState", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BlockStateSyncServer).NotifyState(ctx, req.(*NotifyStateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// BlockStateSync_ServiceDesc is the grpc.ServiceDesc for BlockStateSync service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var BlockStateSync_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "BlockStateSync", + HandlerType: (*BlockStateSyncServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "NotifyState", + Handler: _BlockStateSync_NotifyState_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "proto/block.proto", +} diff --git a/docker-bake.hcl b/docker-bake.hcl index 718948f9..74df0a9d 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -1,5 +1,5 @@ group "default" { - targets = ["auto-operation", "external", "internal", "multicaster", "positioning", "webrtc-sender", "frontend", "receiver-test"] + targets = ["auto-operation", "external", "internal", "multicaster", "positioning", "webrtc-sender", "frontend", "receiver-test", "logviewer"] } variable "PREFIX" { @@ -70,3 +70,10 @@ target "receiver-test" { GET_TAG("receiver-test") ] } + +target "logviewer" { + context = "./infra/logviewer" + tags = [ + GET_TAG("logviewer") + ] +} diff --git a/infra/logviewer/Dockerfile b/infra/logviewer/Dockerfile new file mode 100644 index 00000000..b9d12b3e --- /dev/null +++ b/infra/logviewer/Dockerfile @@ -0,0 +1,10 @@ +FROM golang:1.19.3-bullseye AS builder + +ENV CGO_ENABLED=0 +WORKDIR /app +COPY . /app +RUN GOARCH=amd64 CGO_ENABLED=0 go build -a -tags "netgo" -installsuffix netgo -ldflags="-s -w -extldflags \"-static\"" -o app main.go + +FROM gcr.io/distroless/static-debian11:nonroot AS runner +COPY --from=builder --chown=nonroot:nonroot /app/app /app +ENTRYPOINT [ "/app" ] diff --git a/manifests/imageautomation/logviewer/imagepolicy.yaml b/manifests/imageautomation/logviewer/imagepolicy.yaml new file mode 100644 index 00000000..d8eee8cd --- /dev/null +++ b/manifests/imageautomation/logviewer/imagepolicy.yaml @@ -0,0 +1,14 @@ +apiVersion: image.toolkit.fluxcd.io/v1beta1 +kind: ImagePolicy +metadata: + name: logviewer + namespace: plarail2021 +spec: + imageRepositoryRef: + name: logviewer + filterTags: + pattern: '[a-f0-9]+-(?P[0-9]+)' + extract: '$ts' + policy: + numerical: + order: asc diff --git a/manifests/imageautomation/logviewer/kustomization.yaml b/manifests/imageautomation/logviewer/kustomization.yaml new file mode 100644 index 00000000..473a6b48 --- /dev/null +++ b/manifests/imageautomation/logviewer/kustomization.yaml @@ -0,0 +1,3 @@ +resources: + - ./repository.yaml + - ./imagepolicy.yaml diff --git a/manifests/imageautomation/logviewer/repository.yaml b/manifests/imageautomation/logviewer/repository.yaml new file mode 100644 index 00000000..66efb64f --- /dev/null +++ b/manifests/imageautomation/logviewer/repository.yaml @@ -0,0 +1,8 @@ +apiVersion: image.toolkit.fluxcd.io/v1beta1 +kind: ImageRepository +metadata: + name: logviewer + namespace: plarail2021 +spec: + image: ghcr.io/ueckoken/plarail2022-logviewer + interval: 1m0s