From f2e9c6f38fbac4aa21f77db809b3afe7139bf9f5 Mon Sep 17 00:00:00 2001 From: moxitech Date: Sat, 5 Oct 2024 22:02:41 +0700 Subject: [PATCH] Insert, update, exampling, delete over websocket --- .env | 1 + src/server/entity/websocket/models.go | 95 ++++++- src/server/internal/server/server.go | 24 +- src/server/internal/server/websocket.go | 237 ++++++++++++------ .../package/math/simulator/simulator.go | 27 ++ 5 files changed, 300 insertions(+), 84 deletions(-) diff --git a/.env b/.env index a8a9ea4..c8f6974 100644 --- a/.env +++ b/.env @@ -6,6 +6,7 @@ MONGO_INITDB_ROOT_PASSWORD=moxitech MONGO_INITDB_DATABASE=drone-network-simulator MONGO_INITDB_SIM_COLLECTION=simulations MONGO_INITDB_SIM_HIST_COLLECTION=simulations_history +MONGO_INITDB_MAP_COLLECTION=map_templates POSTGRES_DB=moxitech POSTGRES_USER=moxitech diff --git a/src/server/entity/websocket/models.go b/src/server/entity/websocket/models.go index 13457cd..edc248d 100644 --- a/src/server/entity/websocket/models.go +++ b/src/server/entity/websocket/models.go @@ -1,6 +1,10 @@ package websocket -import "moxitech/dns/package/math/simulator" +import ( + "moxitech/dns/package/math/simulator" + "moxitech/dns/package/utils/randomizer" + "time" +) type Modulation struct { Map *MapPartial `json:"map"` @@ -21,6 +25,91 @@ type SystemObject struct { Params *map[string]string `json:"params"` } -func (o *SystemObject) Delete() { - +// DeleteObjectIfExists удаляет объект если он найден +func (o *Modulation) DeleteObjectIfExists(name string) { + if o.Objects == nil { + return + } + for i, obj := range o.Objects { + if obj.Name == name { + o.Objects = append(o.Objects[:i], o.Objects[i+1:]...) + return + } + } +} + +// SpawnExampleData создает случайные данные и перезаписывает карту +func (o *Modulation) SpawnExampleData() bool { + o.Map = &MapPartial{ + Map: simulator.MakeRandomMap(), + Name: "example starter map", + Changed: false, + } + o.Objects = make([]*SystemObject, 0) + o.Ts_update = time.Now().Unix() + return true +} + +// AddObject добавляет объект станции или дрона +func (o *Modulation) AddObject(name string, obj_type int, coords [3]int, params map[string]string) { + if obj_type != 1 && obj_type != 2 { + // Значит объект не станция или не дрон + return + } + if o.Objects == nil { + o.Objects = make([]*SystemObject, 0) + } + if o.ObjectExists(name) { + name = name + " " + randomizer.GenerateRandomString() + } + obj := &SystemObject{ + Name: name, + Type: obj_type, + Coords: coords, + Params: ¶ms, + } + o.Objects = append(o.Objects, obj) +} + +// UpdateObject обновляет объект станции или дрона по имени +func (o *Modulation) UpdateObject(name string, obj_type int, coords [3]int, params map[string]string) { + if obj_type != 1 && obj_type != 2 { + // Значит объект не станция или не дрон + return + } + if o.Objects == nil { + o.Objects = make([]*SystemObject, 0) + } + ok := o.ObjectExists(name) + if !ok { + o.AddObject(name, obj_type, coords, params) + return + } + obj := &SystemObject{ + Name: name, + Type: obj_type, + Coords: coords, + Params: ¶ms, + } + o.DeleteObjectIfExists(name) + o.Objects = append(o.Objects, obj) +} + +// GetObjects вернет все объекты карты +func (o *Modulation) GetObjects() []*SystemObject { + return o.Objects +} + +// ObjectExists проверит есть ли объект с заданным (именем | id) на карте +func (o *Modulation) ObjectExists(name string) bool { + if o.Objects == nil { + o.Objects = make([]*SystemObject, 0) + return false + } + for _, obj := range o.Objects { + if obj.Name == name { + return true + } + } + return false } diff --git a/src/server/internal/server/server.go b/src/server/internal/server/server.go index 7cfaf0e..74829f8 100644 --- a/src/server/internal/server/server.go +++ b/src/server/internal/server/server.go @@ -91,18 +91,24 @@ func CreateOrConnectSocket(c *websocket.Conn) { } // LOGGING : fmt.Printf("Received message from user %s: %s\n", userToken, msg) - room.UpdateGroupUptime() - // Обрабатываем сообщение - - data, err := json.Marshal(Rooms) + err, ok = room.WebsocketAction(msg) if err != nil { - fmt.Printf("Error marshal json: %v\n", err) + err = c.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("%v", err))) + if err != nil { + fmt.Printf("Error writing message: %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) + if ok { + // Обрабатываем сообщение + data, err := json.Marshal(Rooms[groupHash]) + 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) + } } - } // Закрываем соединение при выходе diff --git a/src/server/internal/server/websocket.go b/src/server/internal/server/websocket.go index 4da1f6e..d51b89c 100644 --- a/src/server/internal/server/websocket.go +++ b/src/server/internal/server/websocket.go @@ -5,7 +5,8 @@ import ( "fmt" "moxitech/dns/entity/websocket" "moxitech/dns/package/math/simulator" - u_sorting "moxitech/dns/package/utils/sorting" + "strconv" + "strings" "sync" "time" ) @@ -79,102 +80,193 @@ func (room *WebsocketRoom) FlushGroupRequest() { // ... ДОБАВИТЬ ЛОГИКУ ИЗ WEBSOCKET ACTION ... // -// FlushGroupRequest сохраняет результат вычислений группы -// @GroupEntity : WS entity -> сущность группы с данными и вычислениями -// func (room *WebsocketRoom) DeleteMapObjectRequest(name string) { -// roomsMutex.Lock() -// defer roomsMutex.Unlock() -// room.Modulation.Objects.DeleteObject(name) +// DeleteMapObjectRequest удаляет определенный объект +func (room *WebsocketRoom) DeleteMapObjectRequest(message websocket.SignalMessage) { + var parsedData struct { + Name string `json:"name"` + } + err := json.Unmarshal(message.Data, &parsedData) + if err != nil { + fmt.Printf("[delete] error parsing data: %v", err) + } -// } + roomsMutex.Lock() + defer roomsMutex.Unlock() + room.Modulation.DeleteObjectIfExists(parsedData.Name) +} + +// InsertObject вставляет базовые случайные данные в симуляцию +func (room *WebsocketRoom) InsertObject(message websocket.SignalMessage) error { + // Тип объекта (тип сигнала) + type_obj := message.Signal + // Определяем структуру для данных в поле `data` + var parsedData struct { + Name string `json:"name"` + Params map[string]string `json:"params"` + } + + // Парсим `data` в структуру + err := json.Unmarshal(message.Data, &parsedData) + if err != nil { + return fmt.Errorf("[insert] error parsing data: %v", err) + } + + // Получаем значения name и params + name := parsedData.Name + params := parsedData.Params + + // Проверяем наличие координат в параметрах + if params["coords"] == "" { + return fmt.Errorf("[insert] coords is empty") + } + + // Разделяем строку координат на отдельные элементы + coordinateStrings := strings.Split(params["coords"], ",") + + // Инициализируем массив для хранения координат + coordinates := make([]int, len(coordinateStrings)) + + // Преобразуем каждую координату в целое число + for i, coord := range coordinateStrings { + parsedCoord, err := strconv.Atoi(strings.TrimSpace(coord)) + if err != nil { + return fmt.Errorf("[insert] invalid coordinate: %s", coord) + } + coordinates[i] = parsedCoord + } + params["coords"] = "" + // Добавляем объект в комнату + roomsMutex.Lock() + defer roomsMutex.Unlock() + room.Modulation.AddObject(name, type_obj, [3]int{coordinates[0], coordinates[1], coordinates[2]}, params) + + return nil +} + +// UpdateObjectRequest вставляет базовые случайные данные в симуляцию +func (room *WebsocketRoom) UpdateObjectRequest(message websocket.SignalMessage) error { + // Тип объекта (тип сигнала) + type_obj := message.Signal - 20 + // Определяем структуру для данных в поле `data` + var parsedData struct { + Name string `json:"name"` + Params map[string]string `json:"params"` + } + + // Парсим `data` в структуру + err := json.Unmarshal(message.Data, &parsedData) + if err != nil { + return fmt.Errorf("[insert] error parsing data: %v", err) + } + + // Получаем значения name и params + name := parsedData.Name + params := parsedData.Params + + // Проверяем наличие координат в параметрах + if params["coords"] == "" { + return fmt.Errorf("[insert] coords is empty") + } + + // Разделяем строку координат на отдельные элементы + coordinateStrings := strings.Split(params["coords"], ",") + + // Инициализируем массив для хранения координат + coordinates := make([]int, len(coordinateStrings)) + + // Преобразуем каждую координату в целое число + for i, coord := range coordinateStrings { + parsedCoord, err := strconv.Atoi(strings.TrimSpace(coord)) + if err != nil { + return fmt.Errorf("[insert] invalid coordinate: %s", coord) + } + coordinates[i] = parsedCoord + } + params["coords"] = "" + // Добавляем объект в комнату + roomsMutex.Lock() + defer roomsMutex.Unlock() + + room.Modulation.AddObject(name, type_obj, [3]int{coordinates[0], coordinates[1], coordinates[2]}, params) + + return nil +} + +// InsertBasicData вставляет базовые случайные данные в симуляцию +func (room *WebsocketRoom) InsertExampleData() { + roomsMutex.Lock() + defer roomsMutex.Unlock() + room.Modulation.SpawnExampleData() +} + +// InsertMapFromJson вставляет высоты и прочие данные карты в симуляцию, по шаблону +func (room *WebsocketRoom) InsertMapFromJson(template string) { + // TODO + roomsMutex.Lock() + defer roomsMutex.Unlock() + room.Modulation.DeleteObjectIfExists(template) +} + +// InsertMapFromMongo вставляет высоты и прочие данные карты в симуляцию, из mongo по названию +func (room *WebsocketRoom) InsertMapFromMongo(name string) { + // TODO + roomsMutex.Lock() + defer roomsMutex.Unlock() + room.Modulation.DeleteObjectIfExists(name) +} + +// RunSimulation запускает симуляцию в горутине +func (room *WebsocketRoom) RunSimulation(name string) { + // TODO +NormalizeDataForSimulation() + roomsMutex.Lock() + defer roomsMutex.Unlock() + room.Modulation.DeleteObjectIfExists(name) +} // NormalizeDataForSimulation - Преобразование данных для отправки в симуляцию -func (room *WebsocketRoom) 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) { +func (room *WebsocketRoom) WebsocketAction(message []byte) (error, bool) { var Signal websocket.SignalMessage err := json.Unmarshal(message, &Signal) if err != nil { fmt.Printf("[WebsocketAction] %v", err) + return fmt.Errorf("error parsing occured: %v \n waiting for signal struct", err), false } + room.UpdateGroupUptime() switch Signal.Signal { case 0: room.UpdateGroupUptime() + return nil, false case 1: + room.InsertObject(Signal) + return nil, true case 2: + room.InsertObject(Signal) + return nil, true case 3: - + room.DeleteMapObjectRequest(Signal) + return nil, true case 21: + // Update Base Station + room.UpdateObjectRequest(Signal) + return nil, true + case 22: + // Update Drone + room.UpdateObjectRequest(Signal) + return nil, true case 30: + // Init example data + room.InsertExampleData() case 31: case 32: @@ -197,4 +289,5 @@ func (room *WebsocketRoom) WebsocketAction(message []byte) { // Запуск симуляции :: 100 // Пользователь исключен :: 301 + return nil, false } diff --git a/src/server/package/math/simulator/simulator.go b/src/server/package/math/simulator/simulator.go index 5193069..b54b5f7 100644 --- a/src/server/package/math/simulator/simulator.go +++ b/src/server/package/math/simulator/simulator.go @@ -2,6 +2,7 @@ package simulator import ( "math" + "math/rand" "moxitech/dns/package/math/distance" "sync" ) @@ -259,3 +260,29 @@ func MakeExampleMap() Map { } return mapObj } + +// Создает полностью случаную карту +func MakeRandomMap() Map { + // Задаём случайное количество высот + numRows := rand.Intn(50) + 1 // Случайное количество строк (от 1 до 50) + numCols := rand.Intn(50) + 1 // Случайное количество столбцов (от 1 до 50) + + // Инициализируем данные высот + heightData := make([][]float64, numRows) + for i := range heightData { + heightData[i] = make([]float64, numCols) + for j := range heightData[i] { + // Случайные значения высот + heightData[i][j] = rand.Float64() * 100 // высоты от 0 до 100 + } + } + + // Определяем карту высот + mapObj := Map{ + Name: "RandomGeneratedMap", + MinBound: [3]float64{-2000, -2000, 0}, // Минимальные координаты + MaxBound: [3]float64{2000, 2000, 500}, // Максимальные координаты + HeightData: heightData, // Случайные высоты + } + return mapObj +}