Map in mongo & map loader & websocket update state & ctx state stablishment
This commit is contained in:
parent
047a3ffd5c
commit
44e28d8e6b
3
.env
3
.env
@ -15,3 +15,6 @@ POSTGRES_USER=moxitech
|
||||
POSTGRES_PASSWORD=moxitech
|
||||
POSTGRES_PORT=5432
|
||||
POSTGRES_HOST=postgres
|
||||
|
||||
SOCKET_BASE_ADDRESS=9091
|
||||
SOCKET_SERVICE_HOST=socket
|
7
DOCS.md
Normal file
7
DOCS.md
Normal file
@ -0,0 +1,7 @@
|
||||
|
||||
|
||||
SOCKET MESSAGING:
|
||||
// PORT CHANGE IN .env
|
||||
localhost:9091/storeEvent?client_id=1&message={"me": "update"}
|
||||
// CONNECTION:
|
||||
ws://localhost:9091/ws/1
|
@ -65,6 +65,18 @@ services:
|
||||
- dns_net
|
||||
restart: always
|
||||
|
||||
socket:
|
||||
container_name: dns-socket
|
||||
build:
|
||||
context: ./src/socket
|
||||
dockerfile: Dockerfile
|
||||
env_file: ".env"
|
||||
ports:
|
||||
- "${SOCKET_BASE_ADDRESS}:${SOCKET_BASE_ADDRESS}"
|
||||
networks:
|
||||
- dns_net
|
||||
restart: always
|
||||
|
||||
front:
|
||||
container_name: dns-ui
|
||||
build:
|
||||
|
49
src/frontend/src/Services/SocketMessaging/MessageHook.js
Normal file
49
src/frontend/src/Services/SocketMessaging/MessageHook.js
Normal file
@ -0,0 +1,49 @@
|
||||
import { useEffect, useState, useRef } from 'react';
|
||||
|
||||
const useWebSocket = (userToken) => {
|
||||
const [message, setMessage] = useState(null);
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
const ws = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!userToken) return;
|
||||
|
||||
const connectWebSocket = () => {
|
||||
const socketUrl = `ws://${process.env.SOCKET_SERVICE_HOST}:${process.env.SOCKET_BASE_ADDRESS}/ws/${userToken}`;
|
||||
ws.current = new WebSocket(socketUrl);
|
||||
|
||||
ws.current.onopen = () => {
|
||||
console.log('WebSocket connected');
|
||||
setIsConnected(true);
|
||||
};
|
||||
|
||||
ws.current.onmessage = (event) => {
|
||||
const data = event.data;
|
||||
console.log('Message received:', data);
|
||||
setMessage(data);
|
||||
};
|
||||
|
||||
ws.current.onclose = () => {
|
||||
console.log('WebSocket disconnected, attempting to reconnect...');
|
||||
setIsConnected(false);
|
||||
setTimeout(connectWebSocket, 3000); // Reconnect after 3 seconds
|
||||
};
|
||||
|
||||
ws.current.onerror = (error) => {
|
||||
console.error('WebSocket error:', error);
|
||||
};
|
||||
};
|
||||
|
||||
connectWebSocket();
|
||||
|
||||
return () => {
|
||||
if (ws.current) {
|
||||
ws.current.close();
|
||||
}
|
||||
};
|
||||
}, [userToken]);
|
||||
|
||||
return { message, isConnected };
|
||||
};
|
||||
|
||||
export default useWebSocket;
|
@ -100,7 +100,19 @@ const useWebsocketConnection = (userToken, roomHash) => {
|
||||
return { websocketStruct, sendMessage };
|
||||
};
|
||||
|
||||
export const spawnJsonSignal = (signal, name, coords) => {
|
||||
export const spawnJsonSignal =
|
||||
( signal,
|
||||
name,
|
||||
coords,
|
||||
antennaRadius,
|
||||
antennaDirections="0,0,0",
|
||||
modulation="",
|
||||
bandwidth=0,
|
||||
dataRate=0,
|
||||
wayPoints=[],
|
||||
speed=0,
|
||||
meshName=null
|
||||
) => {
|
||||
if (![1, 2].includes(signal)) {
|
||||
throw new Error('Invalid signal value. Expected 1 or 2.');
|
||||
}
|
||||
@ -115,11 +127,20 @@ export const spawnJsonSignal = (signal, name, coords) => {
|
||||
name,
|
||||
params: {
|
||||
coords,
|
||||
antennaRadius,
|
||||
antennaDirections,
|
||||
modulation,
|
||||
bandwidth,
|
||||
dataRate,
|
||||
wayPoints,
|
||||
speed,
|
||||
meshName
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
export const DeleteSignal = (name) => {
|
||||
const signal = 3;
|
||||
|
||||
|
@ -28,14 +28,23 @@ const Dashboard = () => {
|
||||
const [objectType, setObjectType] = useState(1); // 1 = Drone, 2 = Base Station
|
||||
const [formObjectData, setFormObjectData] = useState({
|
||||
name: "",
|
||||
type: 1,
|
||||
coordinates_x: 0,
|
||||
coordinates_y: 0,
|
||||
coordinates_z: 0,
|
||||
droneField1: "",
|
||||
droneField2: "",
|
||||
baseStationField1: "",
|
||||
baseStationField2: ""
|
||||
type: 0, // 1 = Drone, 2 = Base Station
|
||||
coordinates_x: 0, // Координата X на карте
|
||||
coordinates_y: 0, // Координата Y на карте
|
||||
coordinates_z: 0, // Координата Z на карте
|
||||
|
||||
antennaRadius: 0, // Радиус антенны
|
||||
antennaDirection_x: 0,// Направление антенны по X
|
||||
antennaDirection_y: 0,// Направление антенны по Y
|
||||
antennaDirection_z: 0,// Направление антенны по Z
|
||||
|
||||
modulation: "", // Модуляция
|
||||
bandwidth: 0, // Пропускная способность
|
||||
dataRate: 0, // Скорость передачи данных
|
||||
// DRONE FIELDS
|
||||
wayPoints: 0, // Точки следования
|
||||
speed: 0, // Скорость
|
||||
meshName: "", // Название сети
|
||||
});
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const handleSearch = () => {
|
||||
@ -76,18 +85,13 @@ const Dashboard = () => {
|
||||
alert("Объект с указанным именем не найден");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const handleTypeChange = (type) => {
|
||||
setObjectType(type);
|
||||
setFormData({ ...formData, type });
|
||||
};
|
||||
|
||||
const handleDeleteSignal = (name) => {
|
||||
sendMessage(DeleteSignal(name));
|
||||
};
|
||||
|
||||
|
||||
// Одно состояние для управления всеми модальными окнами
|
||||
const [modals, setModals] = useState({
|
||||
isModalOpen: false,
|
||||
@ -96,7 +100,6 @@ const Dashboard = () => {
|
||||
isModalSearchOpen: false,
|
||||
isModalMapOpen: false,
|
||||
});
|
||||
|
||||
// Функция открытия модального окна в зависимости от переданной строки
|
||||
const openModal = (modalName) => {
|
||||
setModals((prevModals) => ({
|
||||
@ -104,7 +107,6 @@ const Dashboard = () => {
|
||||
[modalName]: true,
|
||||
}));
|
||||
};
|
||||
|
||||
// Функция закрытия всех модальных окон
|
||||
const closeAllModals = () => {
|
||||
setModals({
|
||||
@ -114,7 +116,7 @@ const Dashboard = () => {
|
||||
isModalSearchOpen: false,
|
||||
});
|
||||
};
|
||||
|
||||
// Обработка websocket
|
||||
useEffect(() => {
|
||||
if (websocketStruct && websocketStruct.modulation) {
|
||||
const modulation = websocketStruct.modulation;
|
||||
@ -327,9 +329,34 @@ const containsObjectRecursively = (parent, target) => {
|
||||
sceneRef.current.add(drone.getObject());
|
||||
}
|
||||
};
|
||||
// Добавление дрона в сцену
|
||||
// Добавление базовой станции в сцену
|
||||
const handleAddBaseStation = () => {
|
||||
const modalAddDroneOrBaseStation = () => {
|
||||
//formObjectData
|
||||
alert(`Добавлено: ${JSON.stringify(formObjectData)}`);
|
||||
if (sceneRef.current) {
|
||||
if (objectType === 1) {
|
||||
const drone = new Drone(formObjectData.name);
|
||||
drone.setPosition(formObjectData.coordinates_x, formObjectData.coordinates_y, formObjectData.coordinates_z);
|
||||
setDrones((prevDrones) => [...prevDrones, drone]);
|
||||
let json_signal = spawnJsonSignal(
|
||||
1,
|
||||
formObjectData.name,
|
||||
`${formObjectData.coordinates_x},${formObjectData.coordinates_y},${formObjectData.coordinates_z}`,
|
||||
formObjectData.antennaRadius,
|
||||
`${formObjectData.antennaDirection_x},${formObjectData.antennaDirection_y},${formObjectData.antennaDirection_z}`,
|
||||
formObjectData.modulation,
|
||||
formObjectData.bandwidth,
|
||||
formObjectData.dataRate,
|
||||
formObjectData.wayPoints,
|
||||
formObjectData.speed,
|
||||
formObjectData.meshName
|
||||
);
|
||||
sendMessage(json_signal)
|
||||
sceneRef.current.add(drone.getObject());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Добавление базовой станции в сцену
|
||||
const handleAddBaseStation = () => {
|
||||
if (sceneRef.current) {
|
||||
const current = Date.now();
|
||||
const base = new BaseStation(current / 1000);
|
||||
@ -350,7 +377,7 @@ const handleAddBaseStation = () => {
|
||||
console.log(baseStation.map(b => b.getObject().children[0]));
|
||||
sceneRef.current.add(base.getObject());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Обработчик ввода на форму высот
|
||||
const handleInputChange = (event) => {
|
||||
@ -497,60 +524,120 @@ const handleAddBaseStation = () => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label>Параметры антенны:</label>
|
||||
<div className="input-group mb-2">
|
||||
<span className="input-group-text">Радиус антенны</span>
|
||||
<input
|
||||
type="number"
|
||||
className="form-control"
|
||||
value={formObjectData.antennaRadius}
|
||||
name='antennaRadius'
|
||||
onChange={handleAddInputChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="input-group mb-2">
|
||||
<span className="input-group-text">Направление X</span>
|
||||
<input
|
||||
type="number"
|
||||
className="form-control"
|
||||
value={formObjectData.antennaDirection_x}
|
||||
name='antennaDirection_x'
|
||||
onChange={handleAddInputChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="input-group mb-2">
|
||||
<span className="input-group-text">Направление Y</span>
|
||||
<input
|
||||
type="number"
|
||||
className="form-control"
|
||||
name='antennaDirection_y'
|
||||
value={formObjectData.antennaDirection_y}
|
||||
onChange={handleAddInputChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="input-group mb-2">
|
||||
<span className="input-group-text">Направление Z</span>
|
||||
<input
|
||||
type="number"
|
||||
className="form-control"
|
||||
name='antennaDirection_z'
|
||||
value={formObjectData.antennaDirection_z}
|
||||
onChange={handleAddInputChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="input-group mb-2">
|
||||
<span className="input-group-text">Модуляция</span>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
name='modulation'
|
||||
value={formObjectData.modulation}
|
||||
onChange={handleAddInputChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="input-group mb-2">
|
||||
<span className="input-group-text">Пропускная способность</span>
|
||||
<input
|
||||
type="number"
|
||||
className="form-control"
|
||||
name='bandwidth'
|
||||
value={formObjectData.bandwidth}
|
||||
onChange={handleAddInputChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="input-group mb-2">
|
||||
<span className="input-group-text">Скорость</span>
|
||||
<input
|
||||
type="number"
|
||||
className="form-control"
|
||||
name='dataRate'
|
||||
value={formObjectData.dataRate}
|
||||
onChange={handleAddInputChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{objectType === 1 && (
|
||||
<>
|
||||
<div className="mb-3">
|
||||
<label>Поле 1 для дрона:</label>
|
||||
<label>Скорость перемещения:</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
name='droneField1'
|
||||
value={formObjectData.droneField1}
|
||||
name='speed'
|
||||
value={formObjectData.speed}
|
||||
onChange={handleAddInputChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label>Поле 2 для дрона:</label>
|
||||
<label>Название Mesh сети:</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
name='droneField2'
|
||||
value={formObjectData.droneField2}
|
||||
name='meshName'
|
||||
value={formObjectData.meshName}
|
||||
onChange={handleAddInputChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label>Точки перемещения:</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
name='wayPoints'
|
||||
value={formObjectData.wayPoints}
|
||||
onChange={handleAddInputChange}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{objectType === 2 && (
|
||||
<>
|
||||
<div className="mb-3">
|
||||
<label>Поле 1 для базовой станции:</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
name='baseStationField1'
|
||||
value={formObjectData.baseStationField1}
|
||||
onChange={handleAddInputChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label>Поле 2 для базовой станции:</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
name='baseStationField2'
|
||||
value={formObjectData.baseStationField2}
|
||||
onChange={handleAddInputChange}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button type="button" className="btn btn-secondary" onClick={closeAllModals}>Закрыть</button>
|
||||
<button type="button" className="btn btn-primary" onClick={() => { alert(`Добавлено: ${JSON.stringify(formData)}`); }}>Добавить</button>
|
||||
<button type="button" className="btn btn-primary" onClick={() => { modalAddDroneOrBaseStation(); }}>Добавить</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import '../DeviceGroups.css';
|
||||
import '../css/DeviceGroups.css';
|
||||
|
||||
const DeviceGroups = () => {
|
||||
const [groups, setGroups] = useState([]);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import { QRCodeCanvas } from "qrcode.react";
|
||||
import '../Devices.css';
|
||||
import '../css/Devices.css';
|
||||
|
||||
const Devices = () => {
|
||||
return (
|
||||
|
@ -1,33 +0,0 @@
|
||||
|
||||
// Инициализация WebSocket соединения
|
||||
// const socket = io("ws://localhost:8080");
|
||||
|
||||
|
||||
// useEffect(() => {
|
||||
// // Получаем местоположение пользователя
|
||||
// if (navigator.geolocation) {
|
||||
// navigator.geolocation.getCurrentPosition(
|
||||
// (position) => {
|
||||
// const { latitude, longitude } = position.coords;
|
||||
// setUserLocation([latitude, longitude]); // Устанавливаем координаты пользователя
|
||||
// setLocationLoaded(true); // Флаг, что местоположение загружено
|
||||
// },
|
||||
// (error) => {
|
||||
// console.error("Ошибка при получении геолокации:", error);
|
||||
// setLocationLoaded(true); // Продолжаем даже при ошибке
|
||||
// }
|
||||
// );
|
||||
// } else {
|
||||
// console.error("Геолокация не поддерживается вашим браузером");
|
||||
// setLocationLoaded(true); // Продолжаем даже если геолокация недоступна
|
||||
// }
|
||||
|
||||
// // Получаем данные устройств в реальном времени
|
||||
// socket.on("deviceLocationUpdate", (data) => {
|
||||
// setDevices(data);
|
||||
// });
|
||||
|
||||
// return () => {
|
||||
// socket.off("deviceLocationUpdate");
|
||||
// };
|
||||
// }, []);
|
@ -1,9 +1,26 @@
|
||||
import * as THREE from 'three';
|
||||
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
|
||||
class BaseStation {
|
||||
constructor(name, scale = 10) {
|
||||
constructor(
|
||||
name,
|
||||
scale = 10,
|
||||
antennaRadius = 1,
|
||||
antennaDirection_x=0,
|
||||
antennaDirection_y=0,
|
||||
antennaDirection_z=0,
|
||||
modulation="QAM-16",
|
||||
bandwidth=0,
|
||||
dataRate=0,
|
||||
) {
|
||||
this.object = new THREE.Object3D();
|
||||
this.name = name;
|
||||
this.antennaRadius = antennaRadius;
|
||||
this.antennaDirection_x = antennaDirection_x;
|
||||
this.antennaDirection_y = antennaDirection_y;
|
||||
this.antennaDirection_z = antennaDirection_z;
|
||||
this.modulation = modulation;
|
||||
this.bandwidth = bandwidth;
|
||||
this.dataRate = dataRate;
|
||||
|
||||
// Инициализируем GLTFLoader
|
||||
const loader = new GLTFLoader();
|
||||
|
@ -1,10 +1,32 @@
|
||||
import * as THREE from 'three';
|
||||
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
|
||||
class Drone {
|
||||
constructor(name, scale = 0.5) {
|
||||
constructor(name,
|
||||
antennaRadius = 1,
|
||||
antennaDirection_x=0,
|
||||
antennaDirection_y=0,
|
||||
antennaDirection_z=0,
|
||||
modulation="QAM-16",
|
||||
bandwidth=0,
|
||||
dataRate=0,
|
||||
wayPoints=[],
|
||||
speed=0,
|
||||
meshName=null,
|
||||
scale = 0.5)
|
||||
{
|
||||
this.object = new THREE.Object3D();
|
||||
this.name = name;
|
||||
this.antennaRadius = antennaRadius;
|
||||
|
||||
this.antennaDirection_x = antennaDirection_x;
|
||||
this.antennaDirection_y = antennaDirection_y;
|
||||
this.antennaDirection_z = antennaDirection_z;
|
||||
this.modulation = modulation;
|
||||
this.bandwidth = bandwidth;
|
||||
this.dataRate = dataRate;
|
||||
this.wayPoints = wayPoints
|
||||
this.speed = speed;
|
||||
this.meshName = meshName;
|
||||
// Инициализируем GLTFLoader
|
||||
const loader = new GLTFLoader();
|
||||
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
@ -102,3 +103,65 @@ func (db *MongoDbInstance) InsertIntoSimulationsHistory(data interface{}) (primi
|
||||
|
||||
return insertedID, nil
|
||||
}
|
||||
|
||||
// MapUpload saves a [][]float64 map into the map_templates collection
|
||||
func (db *MongoDbInstance) MapUpload(mapData [][]float64) (primitive.ObjectID, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
collection := db.Db.Collection("map_templates")
|
||||
|
||||
mapDocument := bson.M{"map_data": mapData, "created_at": time.Now()}
|
||||
|
||||
result, err := collection.InsertOne(ctx, mapDocument)
|
||||
if err != nil {
|
||||
log.Printf("Error inserting map into collection 'map_templates': %v", err)
|
||||
return primitive.NilObjectID, err
|
||||
}
|
||||
|
||||
insertedID := result.InsertedID.(primitive.ObjectID)
|
||||
log.Printf("Map successfully inserted with ID: %v", insertedID)
|
||||
|
||||
return insertedID, nil
|
||||
}
|
||||
|
||||
// MapLoader loads a [][]float64 map from the map_templates collection
|
||||
func (db *MongoDbInstance) MapLoader(mapID primitive.ObjectID) ([][]float64, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
collection := db.Db.Collection("map_templates")
|
||||
|
||||
var result bson.M
|
||||
err := collection.FindOne(ctx, bson.M{"_id": mapID}).Decode(&result)
|
||||
if err != nil {
|
||||
log.Printf("Error loading map from collection 'map_templates': %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mapData, ok := result["map_data"].(primitive.A)
|
||||
if !ok {
|
||||
log.Printf("Error asserting map data type")
|
||||
return nil, mongo.ErrNoDocuments
|
||||
}
|
||||
|
||||
heightMap := make([][]float64, len(mapData))
|
||||
for i, row := range mapData {
|
||||
rowArray, ok := row.(primitive.A)
|
||||
if !ok {
|
||||
log.Printf("Error asserting row type")
|
||||
return nil, mongo.ErrNoDocuments
|
||||
}
|
||||
heightMap[i] = make([]float64, len(rowArray))
|
||||
for j, val := range rowArray {
|
||||
floatVal, ok := val.(float64)
|
||||
if !ok {
|
||||
log.Printf("Error asserting value type at row %d, column %d", i, j)
|
||||
return nil, mongo.ErrNoDocuments
|
||||
}
|
||||
heightMap[i][j] = floatVal
|
||||
}
|
||||
}
|
||||
|
||||
return heightMap, nil
|
||||
}
|
||||
|
@ -57,6 +57,11 @@ func (db *GormDbInstance) AutoMakeSuperUser() error {
|
||||
db.DB.Create(&database.User{Username: "admin", Password: "admin", IsAdmin: true})
|
||||
fmt.Println("Superuser has updated! :: \n Login: admin \n Password: admin")
|
||||
}
|
||||
db.DB.Where("username = ?", "user").First(&user)
|
||||
if user.ID == 0 {
|
||||
db.DB.Create(&database.User{Username: "user", Password: "user", IsAdmin: false})
|
||||
fmt.Println("User has added! :: \n Login: user \n Password: user")
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("DB is nil poiner")
|
||||
|
@ -1,10 +1,14 @@
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"log"
|
||||
"math"
|
||||
"math/rand"
|
||||
"moxitech/dns/internal/database"
|
||||
"moxitech/dns/package/math/distance"
|
||||
"sync"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type Map struct {
|
||||
@ -246,11 +250,18 @@ func CalculateDataRate(modulation string, bandwidth float64) float64 {
|
||||
|
||||
// MakeExampleMap создает статическую карту
|
||||
func MakeExampleMap() Map {
|
||||
mapID, err := primitive.ObjectIDFromHex("6709c34cdeb5f8e46f450c9f")
|
||||
if err != nil {
|
||||
log.Fatalf("Ошибка при преобразовании строки в ObjectID: %v", err)
|
||||
}
|
||||
|
||||
database.Instance.MapLoader(mapID)
|
||||
heightData := [][]float64{
|
||||
{0, 10, 15, 20},
|
||||
{5, 15, 25, 30},
|
||||
{10, 20, 35, 40},
|
||||
{15, 25, 40, 50},
|
||||
{20, 30, 25, 50},
|
||||
}
|
||||
|
||||
// Определяем карту высот
|
||||
@ -263,7 +274,7 @@ func MakeExampleMap() Map {
|
||||
return mapObj
|
||||
}
|
||||
|
||||
// Создает полностью случаную карту
|
||||
// Создает полностью случайную карту
|
||||
func MakeRandomMap() Map {
|
||||
// Задаём случайное количество высот
|
||||
numRows := rand.Intn(50) + 1 // Случайное количество строк (от 1 до 50)
|
||||
|
12
src/socket/Dockerfile
Normal file
12
src/socket/Dockerfile
Normal file
@ -0,0 +1,12 @@
|
||||
# Build stage
|
||||
FROM golang:1.22.7-alpine3.20 AS builder
|
||||
WORKDIR /var/socket
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY . .
|
||||
RUN go build -o main cmd/main.go
|
||||
|
||||
# Run stage
|
||||
FROM alpine:3.20.3
|
||||
COPY --from=builder /var/socket/main /main
|
||||
CMD ["./main"]
|
97
src/socket/cmd/main.go
Normal file
97
src/socket/cmd/main.go
Normal file
@ -0,0 +1,97 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
var (
|
||||
clients = make(map[int]*websocket.Conn) // Список активных клиентов
|
||||
events = make(map[int]string) // Локальная карта для хранения сообщений
|
||||
mutex sync.Mutex // Мьютекс для управления конкурентным доступом к карте
|
||||
upgrader = websocket.Upgrader{
|
||||
CheckOrigin: func(r *http.Request) bool { return true },
|
||||
}
|
||||
)
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/storeEvent", handleStoreEvent)
|
||||
http.HandleFunc("/ws/", handleWebSocket)
|
||||
|
||||
// Запуск горутины для обработки сообщений каждую секунду
|
||||
go processEvents()
|
||||
fmt.Printf("Server started on %v \n", os.Getenv("SOCKET_BASE_ADDRESS"))
|
||||
http.ListenAndServe(fmt.Sprintf("0.0.0.0:%v", os.Getenv("SOCKET_BASE_ADDRESS")), nil)
|
||||
}
|
||||
|
||||
// Обработчик для сохранения событий
|
||||
func handleStoreEvent(w http.ResponseWriter, r *http.Request) {
|
||||
clientID := r.URL.Query().Get("client_id")
|
||||
message := r.URL.Query().Get("message")
|
||||
|
||||
if clientID == "" || message == "" {
|
||||
http.Error(w, "client_id and message are required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var id int
|
||||
_, err := fmt.Sscanf(clientID, "%d", &id)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid client_id", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
mutex.Lock()
|
||||
events[id] = message
|
||||
mutex.Unlock()
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
// Обработчик WebSocket соединений
|
||||
func handleWebSocket(w http.ResponseWriter, r *http.Request) {
|
||||
clientIDStr := r.URL.Path[len("/ws/"):]
|
||||
var clientID int
|
||||
_, err := fmt.Sscanf(clientIDStr, "%d", &clientID)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid client ID", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to upgrade to WebSocket", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
mutex.Lock()
|
||||
clients[clientID] = conn
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
||||
// Функция для обработки событий и отправки сообщений подключенным клиентам
|
||||
func processEvents() {
|
||||
for {
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
mutex.Lock()
|
||||
for id, message := range events {
|
||||
if conn, ok := clients[id]; ok {
|
||||
err := conn.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("Client %d: %s", id, message)))
|
||||
if err != nil {
|
||||
fmt.Printf("Error sending message to client %d: %v\n", id, err)
|
||||
conn.Close()
|
||||
delete(clients, id)
|
||||
}
|
||||
// Удаляем событие после отправки
|
||||
delete(events, id)
|
||||
}
|
||||
}
|
||||
mutex.Unlock()
|
||||
}
|
||||
}
|
5
src/socket/go.mod
Normal file
5
src/socket/go.mod
Normal file
@ -0,0 +1,5 @@
|
||||
module moxitech/socket
|
||||
|
||||
go 1.22.7
|
||||
|
||||
require github.com/gorilla/websocket v1.5.3 // indirect
|
2
src/socket/go.sum
Normal file
2
src/socket/go.sum
Normal file
@ -0,0 +1,2 @@
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
Loading…
Reference in New Issue
Block a user