gsd 4 months ago
parent
commit
25d2ece326
  1. 3
      service.py
  2. 44
      tileManager.py
  3. 4
      ui/angular.json
  4. 25
      ui/package-lock.json
  5. 2
      ui/package.json
  6. 2
      ui/src/app/app-routing.module.ts
  7. 1
      ui/src/app/app.component.ts
  8. 4
      ui/src/app/app.module.ts
  9. 69
      ui/src/app/components/nodes/nodes-map.component.ts
  10. 21
      ui/src/app/components/nodes/nodes.styles.scss
  11. 6
      ui/src/app/entities/NodeDTO.ts
  12. 7
      ui/src/app/entities/node/DeviceMetricsDTO.ts
  13. 6
      ui/src/app/entities/node/PositionDTO.ts
  14. 17
      webExtensions/publicEndpoints.py

3
service.py

@ -126,6 +126,9 @@ class MongoDriver(MeshArgsParse):
sys.exit(1)
self.dbStore = self.dbClient[self.args.mongo_db]
self.dbService = DbService(self.dbStore)
from tileManager import TileManager
self.tileManager = TileManager(self)
async def dbSaveRadio(self, new_from_radio):
'''try:

44
tileManager.py

@ -0,0 +1,44 @@
import aiohttp
from pymongo.asynchronous.database import AsyncDatabase
from logger import logger
from time import time
class TileManager:
domain = 'a.tile.openstreetmap.org'
format = "png"
def __init__(self, core):
self.core = core
self.dbStore:AsyncDatabase = self.core.dbStore
def generateHeaders(self):
return {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36",
"Referer": "http://localhost:4200/",
"Accept": "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"
}
async def grabTile(self, z:int, x:int, y:int):
#grab from db
collection = self.dbStore['openstreetmap']
query = {"x":x, "y":y, "z": z}
t = await collection.find_one(query)
if t:
return t["img"]
else:
#ищем картинку чтож поделать
url = f"https://{self.domain}/{z}/{x}/{y}.{self.format}"
async with aiohttp.ClientSession() as session:
async with session.get(url, ssl=False, headers=self.generateHeaders()) as resp:
# Read the entire response body as bytes
img = await resp.read()
logger.info(url, resp.status)
if resp.status == 200:
query['ts'] = time()
query['img'] = img
query['format'] = self.format
await collection.insert_one(query)
return img
else:
raise Exception("cannot get img")

4
ui/angular.json

@ -29,7 +29,8 @@
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"src/styles.scss"
"src/styles.scss",
"./node_modules/leaflet/dist/leaflet.css"
],
"scripts": []
},
@ -101,6 +102,7 @@
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"./node_modules/leaflet/dist/leaflet.css",
"src/styles.scss"
],
"scripts": []

25
ui/package-lock.json

@ -19,6 +19,7 @@
"@angular/platform-browser-dynamic": "^14.2.0",
"@angular/router": "^14.2.0",
"chart.js": "^4.5.1",
"leaflet": "^1.9.4",
"rxjs": "~7.5.0",
"tslib": "^2.3.0",
"zone.js": "~0.11.4"
@ -28,6 +29,7 @@
"@angular/cli": "~14.2.13",
"@angular/compiler-cli": "^14.2.0",
"@types/jasmine": "~4.0.0",
"@types/leaflet": "^1.9.21",
"jasmine-core": "~4.3.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.1.0",
@ -3343,6 +3345,13 @@
"@types/send": "*"
}
},
"node_modules/@types/geojson": {
"version": "7946.0.16",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
"integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/http-errors": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
@ -3374,6 +3383,16 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/leaflet": {
"version": "1.9.21",
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.21.tgz",
"integrity": "sha512-TbAd9DaPGSnzp6QvtYngntMZgcRk+igFELwR2N99XZn7RXUdKgsXMR+28bUO0rPsWp8MIu/f47luLIQuSLYv/w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/geojson": "*"
}
},
"node_modules/@types/mime": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
@ -8099,6 +8118,12 @@
"node": ">= 8"
}
},
"node_modules/leaflet": {
"version": "1.9.4",
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz",
"integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==",
"license": "BSD-2-Clause"
},
"node_modules/less": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz",

2
ui/package.json

@ -21,6 +21,7 @@
"@angular/platform-browser-dynamic": "^14.2.0",
"@angular/router": "^14.2.0",
"chart.js": "^4.5.1",
"leaflet": "^1.9.4",
"rxjs": "~7.5.0",
"tslib": "^2.3.0",
"zone.js": "~0.11.4"
@ -30,6 +31,7 @@
"@angular/cli": "~14.2.13",
"@angular/compiler-cli": "^14.2.0",
"@types/jasmine": "~4.0.0",
"@types/leaflet": "^1.9.21",
"jasmine-core": "~4.3.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.1.0",

2
ui/src/app/app-routing.module.ts

@ -4,8 +4,10 @@ import {NodesListComponent} from "./components/nodes/nodes-list.component";
import {BotCommandsComponent} from "./components/botCommands/BotCommands.component";
import {MessageHistoryComponent} from "./components/messages/MessageHistory.component";
import {NetworkStatusComponent} from "./components/packet/NetworkStatus.component";
import {NodesMapComponent} from "./components/nodes/nodes-map.component";
const routes: Routes = [
{path: "nodes/map", component: NodesMapComponent},
{path: "nodes/:type", component: NodesListComponent},
{path: "messages", component: MessageHistoryComponent},
{path: "network/status/:num", component: NetworkStatusComponent},

1
ui/src/app/app.component.ts

@ -26,6 +26,7 @@ export class AppComponent implements OnInit {
{name: "История сообщений", url:"messages"},
{name: "Прямые ноды", url:"nodes/direct"},
{name: "Все ноды", url:"nodes/list"},
{name: "Карта сети", url: "nodes/map"}
//{name: "Все ноды", url:""},
]

4
ui/src/app/app.module.ts

@ -25,6 +25,7 @@ import {MatSelectModule} from "@angular/material/select";
import {NetworkStatusComponent} from "./components/packet/NetworkStatus.component";
import {MatRadioModule} from "@angular/material/radio";
import {DatePipe} from "@angular/common";
import {NodesMapComponent} from "./components/nodes/nodes-map.component";
@NgModule({
declarations: [
@ -33,7 +34,8 @@ import {DatePipe} from "@angular/common";
MessageHistoryComponent,
NodesListComponent,
AuthDialog,
NetworkStatusComponent
NetworkStatusComponent,
NodesMapComponent
],
imports: [
BrowserModule,

69
ui/src/app/components/nodes/nodes-map.component.ts

@ -0,0 +1,69 @@
import {Component, OnInit} from "@angular/core";
import * as L from 'leaflet';
import {HttpClient} from "@angular/common/http";
import {NodeDTO} from "../../entities/NodeDTO";
import {Subscription} from "rxjs";
@Component({
selector: "app-nodes-map",
styleUrls: ['nodes.styles.scss'],
template: `
<div style="width:80%">
<div class="map-container">
<div class="map-frame">
<div id="map"></div>
</div>
</div>
</div>
`
})
export class NodesMapComponent implements OnInit {
map!: L.Map;
nodes: NodeDTO[] = []
constructor(private http: HttpClient) {
}
ngOnInit(): void {
this.http.get(`api/nodes/list?p=true`).subscribe(
(obj) => {
this.nodes = (obj as NodeDTO[])
}
).add(
() => {
this.createMap(this.convertPosition(this.nodes.filter((node) => node.havePosition).pop())).add(
() => {
this.nodes.filter((node) => node.havePosition).forEach(
(node) => {
L.marker(this.convertPosition(node)).addTo(this.map)
}
)
}
)
}
)
}
convertPosition(node:NodeDTO|undefined):[number, number] {
if (node) {
let lat = node.position.latitude_i / 10000000;
let lng = node.position.longitude_i / 10000000;
return [lat, lng];
} else return [0,0];
}
createMap(center:[number, number]) {
this.map = L.map("map",{
center: center,
zoom: 11
});
const tiles = L.tileLayer('api/tile/{z}/{x}/{y}.png', {
maxZoom: 18,
minZoom: 3,
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
});
tiles.addTo(this.map)
return Subscription.EMPTY;
}
}

21
ui/src/app/components/nodes/nodes.styles.scss

@ -0,0 +1,21 @@
.map-container {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: 30px;
}
.map-frame {
border: 2px solid black;
height: 100%;
}
#map {
height: 100%;
}
:host ::ng-deep .leaflet-control-attribution {
display: none;
}

6
ui/src/app/entities/NodeDTO.ts

@ -1,7 +1,13 @@
import {NodeMiniDTO} from "./NodeMiniDTO";
import {PositionDTO} from "./node/PositionDTO";
import {DeviceMetricsDTO} from "./node/DeviceMetricsDTO";
export interface NodeDTO extends NodeMiniDTO {
snr: number,
hops_away: number,
ts: number
havePosition:boolean,
position: PositionDTO,
haveMetrics:boolean,
device_metrics: DeviceMetricsDTO
}

7
ui/src/app/entities/node/DeviceMetricsDTO.ts

@ -0,0 +1,7 @@
export interface DeviceMetricsDTO {
battery_level: number,
voltage: number,
channel_utilization: number,
air_util_tx: number,
uptime_seconds: number
}

6
ui/src/app/entities/node/PositionDTO.ts

@ -0,0 +1,6 @@
export interface PositionDTO {
latitude_i: number,
longitude_i: number,
time: number,
location_source: number
}

17
webExtensions/publicEndpoints.py

@ -1,8 +1,9 @@
from fastapi import FastAPI
from fastapi.requests import Request
from fastapi.responses import Response, JSONResponse
from fastapi.responses import Response, JSONResponse, StreamingResponse
from fastapi.exceptions import HTTPException
from extra.NodeDTO import NodeDTO
import traceback
class WebExtension:
app: FastAPI
@ -59,4 +60,16 @@ class WebExtension:
if userNum is not None:
return NodeDTO(userNode)
else:
return {}
return {}
@self.app.get(self.core.context+"/tile/{z}/{x}/{y}.png")
@self.core.authManager.authRequest()
async def grabTile(z:int, x:int, y:int):
try:
img = await self.core.tileManager.grabTile(z, x, y)
print(type(img))
#return StreamingResponse(img, media_type="image/png")
return Response(content=img, media_type="image/png")
except:
traceback.print_exc()
raise HTTPException(status_code=404, detail="not found tile")
Loading…
Cancel
Save