func init() { var err error log.SetLevel(log.ErrorLevel) common.Band, err = band.GetConfig(band.EU_863_870) if err != nil { panic(err) } }
// GetTestConfig returns the test configuration. func GetTestConfig() *TestConfig { var err error log.SetLevel(log.ErrorLevel) Band, err = band.GetConfig(band.EU_863_870) if err != nil { panic(err) } c := &TestConfig{ RedisURL: "redis://localhost:6379", } if v := os.Getenv("TEST_REDIS_URL"); v != "" { c.RedisURL = v } if v := os.Getenv("TEST_POSTGRES_DSN"); v != "" { c.PostgresDSN = v } return c }
// Get the frequency plan for the given region func Get(region string) (frequencyPlan FrequencyPlan, err error) { switch region { case pb_lorawan.Region_EU_863_870.String(): frequencyPlan.Band, err = lora.GetConfig(lora.EU_863_870, false, lorawan.DwellTimeNoLimit) // TTN uses SF9BW125 in RX2 frequencyPlan.RX2DataRate = 3 // TTN frequency plan includes extra channels next to the default channels: frequencyPlan.UplinkChannels = []lora.Channel{ lora.Channel{Frequency: 868100000, DataRates: []int{0, 1, 2, 3, 4, 5}}, lora.Channel{Frequency: 868300000, DataRates: []int{0, 1, 2, 3, 4, 5, 6}}, // Also SF7BW250 lora.Channel{Frequency: 868500000, DataRates: []int{0, 1, 2, 3, 4, 5}}, lora.Channel{Frequency: 868800000, DataRates: []int{7}}, // FSK 50kbps lora.Channel{Frequency: 867100000, DataRates: []int{0, 1, 2, 3, 4, 5}}, lora.Channel{Frequency: 867300000, DataRates: []int{0, 1, 2, 3, 4, 5}}, lora.Channel{Frequency: 867500000, DataRates: []int{0, 1, 2, 3, 4, 5}}, lora.Channel{Frequency: 867700000, DataRates: []int{0, 1, 2, 3, 4, 5}}, lora.Channel{Frequency: 867900000, DataRates: []int{0, 1, 2, 3, 4, 5}}, } frequencyPlan.DownlinkChannels = frequencyPlan.UplinkChannels frequencyPlan.CFList = &lorawan.CFList{867100000, 867300000, 867500000, 867700000, 867900000} case pb_lorawan.Region_US_902_928.String(): frequencyPlan.Band, err = lora.GetConfig(lora.US_902_928, false, lorawan.DwellTime400ms) case pb_lorawan.Region_CN_779_787.String(): err = errors.NewErrInternal("China 779-787 MHz band not supported") case pb_lorawan.Region_EU_433.String(): err = errors.NewErrInternal("Europe 433 MHz band not supported") case pb_lorawan.Region_AU_915_928.String(): frequencyPlan.Band, err = lora.GetConfig(lora.AU_915_928, false, lorawan.DwellTime400ms) case pb_lorawan.Region_CN_470_510.String(): frequencyPlan.Band, err = lora.GetConfig(lora.CN_470_510, false, lorawan.DwellTimeNoLimit) case pb_lorawan.Region_AS_923.String(): frequencyPlan.Band, err = lora.GetConfig(lora.AS_923, false, lorawan.DwellTime400ms) case pb_lorawan.Region_KR_920_923.String(): frequencyPlan.Band, err = lora.GetConfig(lora.KR_920_923, false, lorawan.DwellTimeNoLimit) default: err = errors.NewErrInvalidArgument("Frequency Band", "unknown") } if err != nil { return } return }
func run(c *cli.Context) error { ctx := context.Background() ctx, cancel := context.WithCancel(ctx) defer cancel() // parse the NetID var netID lorawan.NetID if err := netID.UnmarshalText([]byte(c.String("net-id"))); err != nil { log.Fatalf("NetID parse error: %s", err) } // get the band config if c.String("band") == "" { log.Fatalf("--band is undefined, valid options are: %s", strings.Join(bands, ", ")) } bandConfig, err := band.GetConfig(band.Name(c.String("band"))) if err != nil { log.Fatal(err) } common.Band = bandConfig log.WithFields(log.Fields{ "version": version, "net_id": netID.String(), "band": c.String("band"), "docs": "https://docs.loraserver.io/", }).Info("starting LoRa Server") // connect to the database log.Info("connecting to postgresql") db, err := storage.OpenDatabase(c.String("postgres-dsn")) if err != nil { log.Fatalf("database connection error: %s", err) } // setup redis pool log.Info("setup redis connection pool") rp := storage.NewRedisPool(c.String("redis-url")) // setup gateway backend gw, err := gateway.NewBackend(c.String("gw-mqtt-server"), c.String("gw-mqtt-username"), c.String("gw-mqtt-password")) if err != nil { log.Fatalf("gateway-backend setup failed: %s", err) } // setup application backend app, err := application.NewBackend(rp, c.String("app-mqtt-server"), c.String("app-mqtt-username"), c.String("app-mqtt-password")) if err != nil { log.Fatalf("application-backend setup failed: %s", err) } // setup controller backend ctrl, err := controller.NewBackend(rp, c.String("controller-mqtt-server"), c.String("controller-mqtt-username"), c.String("controller-mqtt-password")) if err != nil { log.Fatalf("controller-backend setup failed: %s", err) } // auto-migrate the database if c.Bool("db-automigrate") { log.Info("applying database migrations") m := &migrate.AssetMigrationSource{ Asset: migrations.Asset, AssetDir: migrations.AssetDir, Dir: "", } n, err := migrate.Exec(db.DB, "postgres", m, migrate.Up) if err != nil { log.Fatalf("applying migrations failed: %s", err) } log.WithField("count", n).Info("migrations applied") } lsCtx := loraserver.Context{ DB: db, RedisPool: rp, Gateway: gw, Application: app, Controller: ctrl, NetID: netID, } // start the loraserver server := loraserver.NewServer(lsCtx) if err := server.Start(); err != nil { log.Fatal(err) } // setup the grpc api go func() { server := api.GetGRPCServer(ctx, lsCtx) list, err := net.Listen("tcp", c.String("grpc-bind")) if err != nil { log.Fatalf("error creating gRPC listener: %s", err) } log.WithField("bind", c.String("grpc-bind")).Info("starting gRPC server") log.Fatal(server.Serve(list)) }() // setup the http server r := mux.NewRouter() // setup json api jsonHandler, err := api.GetJSONGateway(ctx, lsCtx, c.String("grpc-bind")) if err != nil { log.Fatalf("get json gateway error: %s", err) } log.WithField("path", "/api/v1").Info("registering api handler and documentation endpoint") r.HandleFunc("/api/v1", api.SwaggerHandlerFunc).Methods("get") r.PathPrefix("/api/v1/").Handler(jsonHandler) // setup static file server (for the gui) log.WithField("path", "/").Info("registering gui handler") r.PathPrefix("/").Handler(http.FileServer(&assetfs.AssetFS{ Asset: static.Asset, AssetDir: static.AssetDir, AssetInfo: static.AssetInfo, Prefix: "", })) // start the http server go func() { log.WithField("bind", c.String("http-bind")).Info("starting rest api / gui server") log.Fatal(http.ListenAndServe(c.String("http-bind"), r)) }() sigChan := make(chan os.Signal) exitChan := make(chan struct{}) signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) log.WithField("signal", <-sigChan).Info("signal received") go func() { log.Warning("stopping loraserver") if err := server.Stop(); err != nil { log.Fatal(err) } exitChan <- struct{}{} }() select { case <-exitChan: case s := <-sigChan: log.WithField("signal", s).Info("signal received, stopping immediately") } return nil }
func run(c *cli.Context) { // parse the NetID var netID lorawan.NetID if err := netID.UnmarshalText([]byte(c.String("net-id"))); err != nil { log.Fatalf("NetID parse error: %s", err) } // get the band config if c.String("band") == "" { log.Fatalf("--band is undefined, valid options are: %s", strings.Join(bands, ", ")) } bandConfig, err := band.GetConfig(band.Name(c.String("band"))) if err != nil { log.Fatal(err) } loraserver.Band = bandConfig log.WithFields(log.Fields{ "version": version, "net_id": netID.String(), "band": c.String("band"), }).Info("starting LoRa Server") // connect to the database log.Info("connecting to postgresql") db, err := loraserver.OpenDatabase(c.String("postgres-dsn")) if err != nil { log.Fatalf("database connection error: %s", err) } // setup redis pool log.Info("setup redis connection pool") rp := loraserver.NewRedisPool(c.String("redis-url")) // setup gateway backend gw, err := gateway.NewBackend(c.String("gw-mqtt-server"), c.String("gw-mqtt-username"), c.String("gw-mqtt-password")) if err != nil { log.Fatalf("gateway-backend setup failed: %s", err) } // setup application backend app, err := application.NewBackend(rp, c.String("app-mqtt-server"), c.String("app-mqtt-username"), c.String("app-mqtt-password")) if err != nil { log.Fatalf("application-backend setup failed: %s", err) } // auto-migrate the database if c.Bool("db-automigrate") { log.Info("applying database migrations") m := &migrate.AssetMigrationSource{ Asset: migrations.Asset, AssetDir: migrations.AssetDir, Dir: "", } n, err := migrate.Exec(db.DB, "postgres", m, migrate.Up) if err != nil { log.Fatalf("applying migrations failed: %s", err) } log.WithField("count", n).Info("migrations applied") } ctx := loraserver.Context{ DB: db, RedisPool: rp, Gateway: gw, Application: app, NetID: netID, } // start the loraserver server := loraserver.NewServer(ctx) if err := server.Start(); err != nil { log.Fatal(err) } // setup json-rpc api handler apiHandler, err := loraserver.NewJSONRPCHandler( loraserver.NewApplicationAPI(ctx), loraserver.NewNodeAPI(ctx), loraserver.NewNodeSessionAPI(ctx), loraserver.NewChannelListAPI(ctx), loraserver.NewChannelAPI(ctx), ) if err != nil { log.Fatal(err) } log.WithField("path", "/rpc").Info("registering json-rpc handler") http.Handle("/rpc", apiHandler) // setup static file server (for the gui) log.WithField("path", "/").Info("registering gui handler") http.Handle("/", http.FileServer(&assetfs.AssetFS{ Asset: static.Asset, AssetDir: static.AssetDir, AssetInfo: static.AssetInfo, Prefix: "", })) // start the http server go func() { log.WithField("bind", c.String("http-bind")).Info("starting http server") log.Fatal(http.ListenAndServe(c.String("http-bind"), nil)) }() sigChan := make(chan os.Signal) exitChan := make(chan struct{}) signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) log.WithField("signal", <-sigChan).Info("signal received") go func() { log.Warning("stopping loraserver") if err := server.Stop(); err != nil { log.Fatal(err) } exitChan <- struct{}{} }() select { case <-exitChan: case s := <-sigChan: log.WithField("signal", s).Info("signal received, stopping immediately") } }