diff --git a/src/frontend/src/Services/WebsocketHook.js b/src/frontend/src/Services/WebsocketHook.js index 807cb93..ad2aa59 100644 --- a/src/frontend/src/Services/WebsocketHook.js +++ b/src/frontend/src/Services/WebsocketHook.js @@ -156,6 +156,16 @@ export const DeleteSignal = (name) => { }; }; +export const RunSimulatorSignal = (id) => { + const signal = 100; + + return { + signal, + data: { + id, + }, + }; +}; diff --git a/src/frontend/src/pages/Dashboard.js b/src/frontend/src/pages/Dashboard.js index a5f1cf2..8ba9860 100644 --- a/src/frontend/src/pages/Dashboard.js +++ b/src/frontend/src/pages/Dashboard.js @@ -7,7 +7,7 @@ import HeightPoint from './model/HeightPoint'; import { RandomHeightPoint } from './helpers/generateRandomHeightPoints' import ModalWindow from './components/Modal/Modal'; import { getCustomCookie } from '../Services/Local'; -import useWebsocketConnection, { DeleteSignal, spawnJsonSignal } from '../Services/WebsocketHook'; +import useWebsocketConnection, { DeleteSignal, spawnJsonSignal, RunSimulatorSignal } from '../Services/WebsocketHook'; import './Dashboard.css'; @@ -781,7 +781,10 @@ const containsObjectRecursively = (parent, target) => {
diff --git a/src/server/entity/websocket/models.go b/src/server/entity/websocket/models.go index ec53d79..efe9405 100644 --- a/src/server/entity/websocket/models.go +++ b/src/server/entity/websocket/models.go @@ -3,7 +3,10 @@ package websocket import ( "fmt" "moxitech/dns/package/math/simulator" + sim_queue "moxitech/dns/package/simulation" "moxitech/dns/package/utils/randomizer" + "strconv" + "strings" "time" ) @@ -49,75 +52,112 @@ func (o *Modulation) MirrorPayloadToSimulationStruct() Modulation { return shadow_sim_copy } -func (o *Modulation) RunSimulator() { - // // Пример карты высот, замените на настоящие данные - // heightData := [][]float64{ - // {0, 10, 15, 20}, - // {5, 15, 25, 30}, - // {10, 20, 35, 40}, - // {15, 25, 40, 50}, - // } +// StartNewSimulation - инициирует парсинг и добавление в очередь для обработки симуляции +func (o *Modulation) StartNewSimulation(timestep int, user_id string) { + mapObj := o.Map + drones := []*simulator.Drone{} + stations := []*simulator.BaseStation{} - // // Определяем карту высот - // - // mapObj := &simulator.Map{ - // Name: "Example Map", - // MinBound: [3]float64{-1000, -1000, 0}, - // MaxBound: [3]float64{1000, 1000, 500}, - // HeightData: heightData, - // } - // - // ? mapObj := o.Map + for i, v := range o.Objects { + if v.Params == nil { + continue + } - // // Определяем дронов - // - // 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) - // } - // } + params := *v.Params + if v.Type == 1 { + // Парсинг параметров для дронов + antennaDirection := [3]float64{1, 0, 0} // значение по умолчанию + if dir, ok := params["antennaDirections"]; ok && dir != "" { + directionValues := strings.Split(dir, ",") + if len(directionValues) == 3 { + antennaDirection[0], _ = strconv.ParseFloat(directionValues[0], 64) + antennaDirection[1], _ = strconv.ParseFloat(directionValues[1], 64) + antennaDirection[2], _ = strconv.ParseFloat(directionValues[2], 64) + } + } + + antennaRadius := 100.0 // значение по умолчанию + if radius, ok := params["antennaRadius"]; ok && radius != "" { + antennaRadius, _ = strconv.ParseFloat(radius, 64) + } + + waypoints := [][3]float64{} + if wp, ok := params["wayPoints"]; ok && wp != "" { + // Парсинг waypoints, предполагаем, что это строка вида "x1,y1,z1;x2,y2,z2" + waypointStrings := strings.Split(wp, ";") + for _, waypointStr := range waypointStrings { + coords := strings.Split(waypointStr, ",") + if len(coords) == 3 { + x, _ := strconv.ParseFloat(coords[0], 64) + y, _ := strconv.ParseFloat(coords[1], 64) + z, _ := strconv.ParseFloat(coords[2], 64) + waypoints = append(waypoints, [3]float64{x, y, z}) + } + } + } + + speed := 0.0 + if spd, ok := params["speed"]; ok && spd != "" { + speed, _ = strconv.ParseFloat(spd, 64) + } + + meshName := "" + if mesh, ok := params["meshName"]; ok { + meshName = mesh + } + + drone := simulator.Drone{ + ID: i, + Name: v.Name, + Coords: [3]float64{float64(v.Coords[0]), float64(v.Coords[1]), float64(v.Coords[2])}, + Params: simulator.DroneParams{ + AntennaRadius: antennaRadius, + AntennaDirection: antennaDirection, + Waypoints: waypoints, + Speed: speed, + MeshName: meshName, + }, + } + drones = append(drones, &drone) + } + + if v.Type == 2 { + // Парсинг параметров для базовых станций + antennaDirection := [3]float64{1, 0, 0} // значение по умолчанию + if dir, ok := params["antennaDirections"]; ok && dir != "" { + directionValues := strings.Split(dir, ",") + if len(directionValues) == 3 { + antennaDirection[0], _ = strconv.ParseFloat(directionValues[0], 64) + antennaDirection[1], _ = strconv.ParseFloat(directionValues[1], 64) + antennaDirection[2], _ = strconv.ParseFloat(directionValues[2], 64) + } + } + + antennaRadius := 200.0 // значение по умолчанию + if radius, ok := params["antennaRadius"]; ok && radius != "" { + antennaRadius, _ = strconv.ParseFloat(radius, 64) + } + + baseStation := simulator.BaseStation{ + ID: i, + Name: v.Name, + Coords: [3]float64{float64(v.Coords[0]), float64(v.Coords[1]), float64(v.Coords[2])}, + Params: simulator.BaseStationParams{ + AntennaRadius: antennaRadius, + AntennaDirection: antennaDirection, + }, + } + stations = append(stations, &baseStation) + } + } + + sim := &simulator.NetworkSimulation{ + Map: &mapObj.Map, + Drones: drones, + BaseStations: stations, + TimeStep: timestep, + } + sim_queue.AddSimulationTask(*sim, user_id) } // SpawnExampleData создает случайные данные и перезаписывает карту diff --git a/src/server/internal/server/server.go b/src/server/internal/server/server.go index 75c12c6..852851e 100644 --- a/src/server/internal/server/server.go +++ b/src/server/internal/server/server.go @@ -5,6 +5,7 @@ import ( "fmt" "moxitech/dns/entity/dto" "moxitech/dns/internal/server/handlers/authorization" + sim_queue "moxitech/dns/package/simulation" "os" "runtime" "strconv" @@ -20,6 +21,8 @@ var ( ) func SpawnServer() error { + sim_queue.Start() + go sim_queue.InvokeComplete() app := fiber.New() app.Use(cors.New(cors.Config{ AllowHeaders: "*", diff --git a/src/server/internal/server/websocket.go b/src/server/internal/server/websocket.go index fce4708..c2da07f 100644 --- a/src/server/internal/server/websocket.go +++ b/src/server/internal/server/websocket.go @@ -230,12 +230,22 @@ func (room *WebsocketRoom) InsertMapFromMongo(name string) { } // RunSimulation запускает симуляцию в горутине -func (room *WebsocketRoom) RunSimulation(name string) (int, error) { - // TODO +NormalizeDataForSimulation() +func (room *WebsocketRoom) RunSimulation(message websocket.SignalMessage) (int, error) { + var parsedData struct { + UserInvoked string `json:"id"` + } + // Парсим `data` в структуру + err := json.Unmarshal(message.Data, &parsedData) + if err != nil { + return -1, fmt.Errorf("[insert] error parsing data: %v", err) + } + if parsedData.UserInvoked == "" { + return -1, fmt.Errorf("[insert] Error parse userID: %v", err) + } roomsMutex.Lock() defer roomsMutex.Unlock() - room.Modulation.MirrorPayloadToSimulationStruct() - return -1, nil + room.Modulation.StartNewSimulation(100, parsedData.UserInvoked) + return 0, nil } // NormalizeDataForSimulation - Преобразование данных для отправки в симуляцию @@ -255,6 +265,7 @@ func (room *WebsocketRoom) WebsocketAction(message []byte) (error, bool) { fmt.Printf("[WebsocketAction] %v", err) return fmt.Errorf("error parsing occured: %v \n waiting for signal struct", err), false } + fmt.Println("Run collector") room.UpdateGroupUptime() switch Signal.Signal { case 0: @@ -264,6 +275,7 @@ func (room *WebsocketRoom) WebsocketAction(message []byte) (error, bool) { room.InsertObject(Signal) return nil, true case 2: + fmt.Println("Начата вставка объекта") room.InsertObject(Signal) return nil, true case 3: @@ -287,6 +299,8 @@ func (room *WebsocketRoom) WebsocketAction(message []byte) (error, bool) { case 33: // SYNC SIGNAL case 100: + fmt.Println("Запуск симуляции : ", Signal) + room.RunSimulation(Signal) case 101: // room.StoreMongo(Signal) diff --git a/src/server/package/simulation/sim_queue.go b/src/server/package/simulation/sim_queue.go new file mode 100644 index 0000000..b70780d --- /dev/null +++ b/src/server/package/simulation/sim_queue.go @@ -0,0 +1,60 @@ +package sim_queue + +import ( + "encoding/json" + "fmt" + "moxitech/dns/package/math/simulator" + "net/http" +) + +var ( + AddChannel = make(chan simulator.NetworkSimulation) + CompleteChannel = make(chan *simulationResult) +) + +type simulationResult struct { + Sim *simulator.NetworkSimulation + UserID string +} + +// AddSimulationTask adds a network simulation to the queue and runs it in a goroutine. +func AddSimulationTask(sim simulator.NetworkSimulation, userID string) { + AddChannel <- sim + CompleteChannel <- &simulationResult{Sim: &sim, UserID: userID} +} + +// InvokeComplete listens for completed simulations, serializes them to JSON, and sends to a specified endpoint. +func InvokeComplete() { + for result := range CompleteChannel { + go func(result *simulationResult) { + jsonData, err := json.Marshal(result.Sim) + if err != nil { + fmt.Println("Error serializing simulation result:", err) + return + } + url := fmt.Sprintf("http://socket:9091/storeEvent?client_id=%s&message=%s", result.UserID, string(jsonData)) + resp, err := http.Get(url) + if err != nil { + fmt.Println("Error sending simulation result:", err) + return + } + // database.Instance.InsertIntoSimulations(jsonData) + defer resp.Body.Close() + }(result) + } +} + +// Start starts listening on the channels to manage simulations. +func Start() { + go func() { + for sim := range AddChannel { + go runSimulation(sim) + } + }() +} + +// runSimulation runs a simulation and then sends it to the CompleteChannel. +func runSimulation(sim simulator.NetworkSimulation) { + sim.Simulate(sim.TimeStep) // Run the simulation for 300 iterations (as an example) + CompleteChannel <- &simulationResult{Sim: &sim} +}