Websocket connection, signaling, streaming & mongo driver & upd simulator

This commit is contained in:
moxitech 2024-10-05 12:19:35 +07:00
parent 83cb06d968
commit fa4be8d5ff
14 changed files with 635 additions and 26 deletions

2
.env
View File

@ -5,7 +5,7 @@ MONGO_INITDB_ROOT_USERNAME=moxitech
MONGO_INITDB_ROOT_PASSWORD=moxitech
MONGO_INITDB_DATABASE=drone-network-simulator
MONGO_INITDB_SIM_COLLECTION=simulations
MONGO_INITDB_SIM_HIST_COLLECTION=simulations_history
POSTGRES_DB=moxitech
POSTGRES_USER=moxitech

View File

@ -60,6 +60,7 @@ services:
- "8080:8080"
depends_on:
- postgres
- mongo
networks:
- dns_net
restart: always

View File

@ -74,6 +74,13 @@ import (
// }
func main() {
// JSON_L1_TEST
// data := json.T_CreateApproximateJsonStruct_l1()
// fmt.Println(data)
// RANDOM_STRING_TEST
// fmt.Println(randomizer.GenerateRandomString())
D_EXPORT_VARS()
err := database.NewDBConnection()
if err != nil {

View File

@ -0,0 +1,17 @@
package dto
type ServerState struct {
Gorutines int `json:"go"`
MemAllocation uint64 `json:"mem_alloc"`
TotalMemAllocation uint64 `json:"total_mem_alloc"`
NumGC uint32 `json:"numgc"`
}
type ActiveRoomsDTO struct {
Rooms []ActiveRoomDTO `json:"rooms_ids"`
}
type ActiveRoomDTO struct {
Name string `json:"name"`
Uuid string `json:"uuid"`
}

View File

@ -0,0 +1,12 @@
package websocket
import "encoding/json"
type SignalMessage struct {
Signal int `json:"signal"`
Data json.RawMessage `json:"data"`
}
type SignalDeleteMessage struct {
Signal int `json:"signal"`
Data string `json:"data"`
}

View File

@ -0,0 +1,26 @@
package websocket
import "moxitech/dns/package/math/simulator"
type Modulation struct {
Map *MapPartial `json:"map"`
Objects []*SystemObject `json:"objects"`
Ts_update int64 `json:"ts_update"`
}
type MapPartial struct {
Map simulator.Map `json:"map"`
Name string `json:"name"`
Changed bool `json:"changed"`
}
type SystemObject struct {
Name string `json:"name"`
Type int `json:"type"` // 0: drone, 1: base_station
Coords [3]int `json:"coords"`
Params *map[string]string `json:"params"`
}
func (o *SystemObject) Delete() {
}

View File

@ -12,6 +12,7 @@ require (
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/fasthttp/websocket v1.5.3 // indirect
github.com/gofiber/websocket/v2 v2.2.1 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
@ -23,12 +24,18 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/montanaflynn/stats v0.7.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
github.com/stretchr/testify v1.9.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.56.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
go.mongodb.org/mongo-driver v1.17.1 // indirect
golang.org/x/crypto v0.27.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.25.0 // indirect

View File

@ -9,6 +9,8 @@ github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yG
github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
github.com/gofiber/websocket/v2 v2.2.1 h1:C9cjxvloojayOp9AovmpQrk8VqvVnT8Oao3+IUygH7w=
github.com/gofiber/websocket/v2 v2.2.1/go.mod h1:Ao/+nyNnX5u/hIFPuHl28a+NIkrqK7PRimyKaj4JxVU=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
@ -32,6 +34,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
@ -50,16 +54,50 @@ github.com/valyala/fasthttp v1.56.0 h1:bEZdJev/6LCBlpdORfrLu/WOZXXxvrUQSiyniuaoW
github.com/valyala/fasthttp v1.56.0/go.mod h1:sReBt3XZVnudxuLOx4J/fMrJVorWRiWY2koQKgABiVI=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM=
go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@ -1 +1,104 @@
package database
import (
"context"
"log"
"os"
"sync"
"time"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
// MongoDbInstance представляет структуру для хранения подключения к MongoDB
type MongoDbInstance struct {
Client *mongo.Client
Db *mongo.Database
}
var instance *MongoDbInstance
var once sync.Once
// giveMeMongoConnectionString возвращает строку подключения к MongoDB
func giveMeMongoConnectionString() string {
return "mongodb://moxitech:moxitech@localhost:27017/" // Замените строку на свою строку подключения
}
// NewDbConnection создает подключение к базе данных MongoDB и возвращает единственный экземпляр MongoDbInstance
func NewDbConnection() *MongoDbInstance {
once.Do(func() {
clientOptions := options.Client().ApplyURI(giveMeMongoConnectionString())
// Устанавливаем таймаут для подключения
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// Подключаемся к MongoDB
client, err := mongo.Connect(ctx, clientOptions)
if err != nil {
log.Fatalf("Ошибка подключения к MongoDB: %v", err)
}
// Проверяем соединение
err = client.Ping(ctx, nil)
if err != nil {
log.Fatalf("Ошибка пинга MongoDB: %v", err)
}
log.Println("Успешное подключение к MongoDB")
instance = &MongoDbInstance{
Client: client,
Db: client.Database(os.Getenv("MONGO_INITDB_DATABASE")),
}
})
return instance
}
// InsertIntoSimulations вставляет данные в коллекцию "simulations"
func (db *MongoDbInstance) InsertIntoSimulations(data interface{}) (primitive.ObjectID, error) {
// Устанавливаем контекст с таймаутом
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
collection := db.Db.Collection(os.Getenv("MONGO_INITDB_SIM_COLLECTION"))
// Вставляем данные в коллекцию
result, err := collection.InsertOne(ctx, data)
if err != nil {
log.Printf("Ошибка при вставке данных в коллекцию '%v': %v", os.Getenv("MONGO_INITDB_SIM_COLLECTION"), err)
return primitive.NilObjectID, err
}
// Возвращаем ID вставленного документа
insertedID := result.InsertedID.(primitive.ObjectID)
log.Printf("Данные успешно вставлены с ID: %v", insertedID)
return insertedID, nil
}
// InsertIntoSimulations вставляет данные в коллекцию историй симуляций
func (db *MongoDbInstance) InsertIntoSimulationsHistory(data interface{}) (primitive.ObjectID, error) {
// Устанавливаем контекст с таймаутом
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// Получаем коллекцию "simulations_history"
collection := db.Db.Collection("simulations_history")
// Вставляем данные в коллекцию
result, err := collection.InsertOne(ctx, data)
if err != nil {
log.Printf("Ошибка при вставке данных в коллекцию 'simulations': %v", err)
return primitive.NilObjectID, err
}
// Возвращаем ID вставленного документа
insertedID := result.InsertedID.(primitive.ObjectID)
log.Printf("Данные успешно вставлены с ID: %v", insertedID)
return insertedID, nil
}

View File

@ -1,28 +1,47 @@
package server
import (
"encoding/json"
"fmt"
"moxitech/dns/entity/dto"
"moxitech/dns/internal/server/handlers/authorization"
"os"
"runtime"
"strconv"
"time"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/websocket/v2"
)
var (
Rooms map[string]*WebsocketRoom
)
func SpawnServer() error {
app := fiber.New()
go DispatchRooms()
Rooms = make(map[string]*WebsocketRoom)
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Welcome to Drone Network Simulator server-side API")
})
app.Get("/auth/:username/:password", authorization.AuthUser)
// GET http://localhost:8080/auth/:username/:password
app.Post("/auth/:username/:password", authorization.AuthUser)
// GET http://localhost:8080/simulations/active
app.Get("/simulations/active", GetActiveRooms)
// GET http://localhost:8080/simulations/from/history
app.Get("/simulations/from/history", authorization.AuthUser)
// GET http://localhost:8080/get/server/state
app.Get("/get/server/state", GetServerState)
// WebSocket маршрут
app.Get("/ws/connect/", websocket.New(CreateSocket))
app.Get("/ws/connect/", websocket.New(CreateOrConnectSocket))
return app.Listen(os.Getenv("SERVER_BASE_ADDRESS"))
}
// WS :: ws://localhost:8080/ws/connect?userToken=?&groupId=?
func CreateSocket(c *websocket.Conn) {
// WS :: ws://localhost:8080/ws/connect?userToken=%1&groupHash=%2
func CreateOrConnectSocket(c *websocket.Conn) {
// Получаем токен пользователя из query параметров
userToken := c.Query("userToken")
if userToken == "" {
@ -30,20 +49,37 @@ func CreateSocket(c *websocket.Conn) {
return
}
// Получаем группу к которой коннектиться user из query параметров
groupId := c.Query("groupId")
if groupId == "" {
c.WriteMessage(websocket.ClosePolicyViolation, []byte("groupId is required"))
groupHash := c.Query("groupHash")
if groupHash == "" {
c.WriteMessage(websocket.ClosePolicyViolation, []byte("groupHash is required"))
return
}
user_id, err := strconv.Atoi(userToken)
if err != nil {
c.WriteMessage(websocket.ClosePolicyViolation, []byte("UserToken is bad!"))
return
}
_, ok := Rooms[groupHash]
if !ok {
// TODO: прокинуть шаблон
Rooms[groupHash] = CreateGroupRequest(user_id, groupHash, "")
} else {
room := Rooms[groupHash]
val := room.ConnectGroupRequest(user_id)
err = c.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("%v", string(val))))
if err != nil {
fmt.Printf("Error writing message: %v\n", err)
}
}
room := Rooms[groupHash]
// Логика обработки подключения
fmt.Printf("User connected with token: %s\n", userToken)
fmt.Printf("[I] User connected with token or id: %s\n", userToken)
for {
// Читаем сообщения от клиента
messageType, msg, err := c.ReadMessage()
if messageType != websocket.TextMessage {
err = c.WriteMessage(websocket.BinaryMessage, []byte("Please use text message instead"))
err = c.WriteMessage(websocket.BinaryMessage, []byte("PLS USE JSON TEXT STRUCTURE!"))
if err != nil {
fmt.Printf("Error writing message: %v\n", err)
break
@ -52,15 +88,19 @@ func CreateSocket(c *websocket.Conn) {
}
if err != nil {
fmt.Printf("Error reading message: %v\n", err)
break
}
// LOGGING :
fmt.Printf("Received message from user %s: %s\n", userToken, msg)
room.UpdateGroupUptime()
// Обрабатываем сообщение
// Отправляем обратно сообщение клиенту
err = c.WriteMessage(websocket.TextMessage, msg)
data, err := json.Marshal(Rooms)
if err != nil {
fmt.Printf("Error marshal json: %v\n", err)
}
err = c.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("%v", string(data))))
if err != nil {
fmt.Printf("Error writing message: %v\n", err)
break
}
}
@ -68,3 +108,65 @@ func CreateSocket(c *websocket.Conn) {
// Закрываем соединение при выходе
c.Close()
}
// GetServerState => получает системное состояние сервера на go
func GetServerState(c *fiber.Ctx) error {
numGoroutines := runtime.NumGoroutine()
memStats := &runtime.MemStats{}
runtime.ReadMemStats(memStats)
state := dto.ServerState{
Gorutines: numGoroutines,
MemAllocation: memStats.Alloc,
TotalMemAllocation: memStats.TotalAlloc,
NumGC: memStats.NumGC,
}
data, err := json.Marshal(state)
if err != nil {
c.WriteString("ERROR" + err.Error())
return err
}
c.WriteString(string(data))
return nil
}
// GetActiveRooms получает активные комнаты
func GetActiveRooms(c *fiber.Ctx) error {
result := dto.ActiveRoomsDTO{
Rooms: make([]dto.ActiveRoomDTO, 0),
}
for uuid := range Rooms {
if uuid == "" {
continue
}
result.Rooms = append(result.Rooms, dto.ActiveRoomDTO{Name: fmt.Sprintf("Room %v", uuid), Uuid: uuid})
}
res, err := json.Marshal(result)
if err != nil {
c.Status(500).WriteString(fmt.Sprintf("Error! %v", res))
return err
}
c.Write(res)
return nil
}
// DispatchRooms => в бесконечном цикле раз в 50 минут проверяет состояние комнат
// если активность закончилась:
// 1. Сохраняет в mongodb
// 2. Отрубает сессию
func DispatchRooms() {
ticker := time.NewTicker(50 * time.Minute) // Устанавливаем таймер с интервалом 50 минут
defer ticker.Stop() // Останавливаем таймер после завершения
for range ticker.C {
now := time.Now().Unix() // Текущее время в формате Unix
for _, room := range Rooms {
// Проверяем, если прошло больше 20 минут с последнего обновления комнаты
if room.Ts_update+20*60 < now {
// Удаление комнаты из списка по ID
delete(Rooms, room.UUID)
fmt.Printf("Room %s is inactive for more than 20 minutes.\n", room.UUID)
}
}
}
}

View File

@ -1,20 +1,200 @@
package server
// CreateGroupRequest создает группу пользователей
// @UserDTO -> трансфер пользовательских данных {id}
func CreateGroupRequest() {
import (
"encoding/json"
"fmt"
"moxitech/dns/entity/websocket"
"moxitech/dns/package/math/simulator"
u_sorting "moxitech/dns/package/utils/sorting"
"sync"
"time"
)
var roomsMutex sync.RWMutex
type WebsocketRoom struct {
UUID string `json:"uuid"` // Строка подключения
Connections map[int]bool `json:"connections"` // Подключения: для каждого пользователя есть свое подключение
Modulation websocket.Modulation `json:"modulation"`
EndModulation *simulator.NetworkSimulation `json:"ended_modulation"` // Последняя проведенная симуляция
Ts_start int64 `json:"ts_start"`
Ts_update int64 `json:"ts_update"`
}
// CreateGroupRequest [@FrontendInitiator:makeSimulation] создает группу пользователей
// @UserDTO -> трансфер пользовательских данных {id}
func CreateGroupRequest(user_id int, groupHash string, mapTemplateOrTemplateName string) *WebsocketRoom {
if mapTemplateOrTemplateName != "" {
fmt.Println("TODO! загрузка шаблонов карты")
}
room := WebsocketRoom{
UUID: groupHash,
Connections: make(map[int]bool), // Инициализируем карту Connections
Modulation: websocket.Modulation{},
Ts_start: time.Now().Unix(),
Ts_update: time.Now().Unix(),
}
mod := websocket.Modulation{
Map: &websocket.MapPartial{
Map: simulator.MakeExampleMap(),
Name: "New Map",
Changed: false,
},
Objects: make([]*websocket.SystemObject, 0),
Ts_update: time.Now().Unix(),
}
room.Connections[user_id] = true
room.Modulation = mod
return &room
}
// ConnectGroupRequest [@FrontendInitiator:connectSimulation] подключается к группе пользователей
// @UserDTO -> трансфер пользовательских данных {id}
func (room *WebsocketRoom) ConnectGroupRequest(user_id int) string {
roomsMutex.Lock()
defer roomsMutex.Unlock()
// TODO: пробросить структуру Modulation
room.Connections[user_id] = true
// TODO: localhost -> nginx provide configuration OR ip
return fmt.Sprintf("ws://localhost:8080/ws/connect?userToken=%vgroupHash=%v", user_id, room.UUID)
}
// UpdateGroupUptime [@WebsocketInitiator:любая передача данных] обновляет uptime группы
func (room *WebsocketRoom) UpdateGroupUptime() {
room.Ts_update = time.Now().Unix()
}
// FlushGroupRequest сохраняет результат вычислений группы
// @GroupEntity : WS entity -> сущность группы с данными и вычислениями
// @IsUserRequest : boolean -> если пользовательский запрос - сейвим с именем группы, иначе создаем произвольную запись с Username<UserId:Creator> + timestamp
func FlushGroupRequest() {
func (room *WebsocketRoom) FlushGroupRequest() {
}
// ObserveGroupHandler проверяет массив групп пользователей и если в группе нет активных пользователей - закрываем соединение
// Запускать в горутине после старта сервера!
func ObserveGroupHandler() {
//
// ... ДОБАВИТЬ ЛОГИКУ ИЗ WEBSOCKET ACTION ...
//
// FlushGroupRequest сохраняет результат вычислений группы
// @GroupEntity : WS entity -> сущность группы с данными и вычислениями
// func (room *WebsocketRoom) DeleteMapObjectRequest(name string) {
// roomsMutex.Lock()
// defer roomsMutex.Unlock()
// room.Modulation.Objects.DeleteObject(name)
// }
// NormalizeDataForSimulation - Преобразование данных для отправки в симуляцию
func (room *WebsocketRoom) NormalizeDataForSimulation() {
}
// InitBroadCast - Инициализация раздачи контента участникам комнаты
func (room *WebsocketRoom) InitBroadCast() {
}
func SpawnSimulation(heightData [][]float64) {
// Пример карты высот, замените на настоящие данные
// heightData := [][]float64{
// {0, 10, 15, 20},
// {5, 15, 25, 30},
// {10, 20, 35, 40},
// {15, 25, 40, 50},
// }
// Определяем карту высот
mapObj := &simulator.Map{
Name: "Example Map",
MinBound: [3]float64{-1000, -1000, 0},
MaxBound: [3]float64{1000, 1000, 500},
HeightData: heightData,
}
// Определяем дронов
drones := []*simulator.Drone{
{
ID: 1,
Name: "Drone 1",
Coords: [3]float64{100, 100, 50},
Params: simulator.DroneParams{
AntennaRadius: 500,
AntennaDirection: [3]float64{1, 0, 0},
Waypoints: [][3]float64{{200, 200, 50}},
Speed: 10,
MeshName: "MeshA",
},
},
}
// Определяем базовые станции
baseStations := []*simulator.BaseStation{
{
ID: 1,
Name: "BaseStation1",
Coords: [3]float64{0, 0, 0},
Params: simulator.BaseStationParams{
AntennaRadius: 2000,
AntennaDirection: [3]float64{1, 0, 0},
},
},
}
// Создаем симуляцию
sim := &simulator.NetworkSimulation{
Map: mapObj,
Drones: drones,
BaseStations: baseStations,
TimeStep: 2,
}
// Запуск симуляции на 300 секунд
result_sim := sim.Simulate(300)
result := u_sorting.SortMap(result_sim)
for time, state := range result {
for localstate, f := range state {
fmt.Printf("%v| %v sec :: %v\n", time, localstate, f)
}
}
}
func (room *WebsocketRoom) WebsocketAction(message []byte) {
var Signal websocket.SignalMessage
err := json.Unmarshal(message, &Signal)
if err != nil {
fmt.Printf("[WebsocketAction] %v", err)
}
switch Signal.Signal {
case 0:
room.UpdateGroupUptime()
case 1:
case 2:
case 3:
case 21:
case 22:
case 30:
case 31:
case 32:
case 100:
case 301:
}
// Нахождение на странице симуляции :: 0 - просто обновляет uptime
// Добавление объекта :: 1 - базовая станция, 2 - дрон
// Удаление объекта :: 3
// Обновление объекта :: 21 - базовая станция, 22 - дрон
// Инициализация базовых данных :: 30
// Инициализация загрузки карты в ПО :: 31
// Инициализация загрузки карты из mongoDB :: 32
// Запуск симуляции :: 100
// Пользователь исключен :: 301
}

View File

@ -48,10 +48,10 @@ type BaseStation struct {
}
type NetworkSimulation struct {
Map *Map
Drones []*Drone
BaseStations []*BaseStation
TimeStep int
Map *Map `json:"map"`
Drones []*Drone `json:"drones"`
BaseStations []*BaseStation `json:"base_station"`
TimeStep int `json:"time_step"`
}
// Simulate запускает симуляцию и возвращает состояние сети на каждую секунду
@ -241,3 +241,21 @@ func CalculateDataRate(modulation string, bandwidth float64) float64 {
return spectralEfficiency * bandwidth // скорость передачи данных в Mbps
}
func MakeExampleMap() Map {
heightData := [][]float64{
{0, 10, 15, 20},
{5, 15, 25, 30},
{10, 20, 35, 40},
{15, 25, 40, 50},
}
// Определяем карту высот
mapObj := Map{
Name: "ExampleServerMap",
MinBound: [3]float64{-1000, -1000, 0},
MaxBound: [3]float64{1000, 1000, 500},
HeightData: heightData,
}
return mapObj
}

View File

@ -0,0 +1,77 @@
package json
import (
"encoding/json"
"moxitech/dns/package/math/simulator"
)
func Loader() {
}
// Dumper - генерик, задача которого вернуть json строку любого объекта
func Dumper[T any](x any) (string, error) {
data, err := json.Marshal(x)
return string(data), err
}
// T_CreateApproximateJsonStruct -> функция которая вернет JSON строку которая приблизительно получиться в layer 1 программы (в бекэнде)
func T_CreateApproximateJsonStruct_l1() string {
heightData := [][]float64{
{0, 10, 15, 20},
{5, 15, 25, 30},
{10, 20, 35, 40},
{15, 25, 40, 50},
}
mapObj := &simulator.Map{
Name: "Example Map",
MinBound: [3]float64{-1000, -1000, 0},
MaxBound: [3]float64{1000, 1000, 500},
HeightData: heightData,
}
drones := []*simulator.Drone{
{
ID: 1,
Name: "Drone 1",
Coords: [3]float64{100, 100, 50},
Params: simulator.DroneParams{
AntennaRadius: 500,
AntennaDirection: [3]float64{1, 0, 0},
Waypoints: [][3]float64{{200, 200, 50}},
Speed: 10,
MeshName: "MeshA",
},
},
}
baseStations := []*simulator.BaseStation{
{
ID: 1,
Name: "BaseStation 1",
Coords: [3]float64{0, 0, 0},
Params: simulator.BaseStationParams{
AntennaRadius: 2000,
AntennaDirection: [3]float64{1, 0, 0},
},
},
{
ID: 2,
Name: "BaseStation 2",
Coords: [3]float64{100, 50, 30},
Params: simulator.BaseStationParams{
AntennaRadius: 100,
AntennaDirection: [3]float64{1, 1, 0},
},
},
}
sim := &simulator.NetworkSimulation{
Map: mapObj,
Drones: drones,
BaseStations: baseStations,
TimeStep: 2,
}
data, err := json.Marshal(sim)
if err != nil {
panic(err)
}
return string(data)
}

View File

@ -0,0 +1,21 @@
package randomizer
import (
"math/rand"
"strings"
)
func GenerateRandomString() string {
// Создание строки символов, которые могут быть использованы для генерации случайной строки
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
// Генерация случайной строки длиной 10 символов
result := make([]byte, 10)
for i := range result {
result[i] = chars[rand.Intn(len(chars))]
}
// Преобразование среза в строку и возвращение результата
return strings.TrimSpace(string(result))
}