배상현

프론트 백엔드 코드 회사 래파지토리에서 가져오기

Signed-off-by: bsh <bsh01111@naver.com>
Showing 176 changed files with 10569 additions and 0 deletions
1 +module.exports = {
2 + parser: '@typescript-eslint/parser',
3 + parserOptions: {
4 + project: 'tsconfig.json',
5 + sourceType: 'module',
6 + },
7 + plugins: ['@typescript-eslint/eslint-plugin'],
8 + extends: [
9 + 'plugin:@typescript-eslint/recommended',
10 + 'plugin:prettier/recommended',
11 + ],
12 + root: true,
13 + env: {
14 + node: true,
15 + jest: true,
16 + },
17 + ignorePatterns: ['.eslintrc.js'],
18 + rules: {
19 + '@typescript-eslint/interface-name-prefix': 'off',
20 + '@typescript-eslint/explicit-function-return-type': 'off',
21 + '@typescript-eslint/explicit-module-boundary-types': 'off',
22 + '@typescript-eslint/no-explicit-any': 'off',
23 + },
24 +};
1 +# compiled output
2 +/dist
3 +/node_modules
4 +/output
5 +
6 +# config env
7 +.env
8 +.envrc
9 +
10 +# Logs
11 +logs
12 +*.log
13 +npm-debug.log*
14 +yarn-debug.log*
15 +yarn-error.log*
16 +lerna-debug.log*
17 +
18 +# OS
19 +.DS_Store
20 +
21 +# Tests
22 +/coverage
23 +/.nyc_output
24 +
25 +# IDEs and editors
26 +/.idea
27 +.project
28 +.classpath
29 +.c9/
30 +*.launch
31 +.settings/
32 +*.sublime-workspace
33 +builds/*
34 +nohup.out
35 +
36 +# IDE - VSCode
37 +.vscode/*
38 +!.vscode/settings.json
39 +!.vscode/tasks.json
40 +!.vscode/launch.json
41 +!.vscode/extensions.json
42 +
43 +ormconfig.json
...\ No newline at end of file ...\ No newline at end of file
1 +cache:
2 + untracked: true
3 + key: '$CI_BUILD_REF_NAME'
4 + paths:
5 + - node_modules/
6 +
7 +build:
8 + stage: build
9 + script:
10 + - npm install
11 + - npm run build
12 + only:
13 + - master
14 + tags:
15 + - build
16 +
17 +deploy:
18 + stage: deploy
19 + script:
20 + - source ./env/production.sh
21 + - pm2 start --exp-backoff-restart-delay=200
22 + only:
23 + - master
24 + tags:
25 + - deploy
1 +{
2 + "singleQuote": true,
3 + "trailingComma": "all"
4 +}
...\ No newline at end of file ...\ No newline at end of file
1 +## Description
2 +
3 +드.론.
4 +
5 +## Installation
6 +
7 +```bash
8 +$ npm install
9 +```
10 +
11 +## Running the app
12 +
13 +```bash
14 +# development
15 +$ npm run start
16 +
17 +# watch mode
18 +$ npm run start:dev
19 +
20 +# production mode
21 +$ npm run start:prod
22 +```
23 +
24 +## Test
25 +
26 +```bash
27 +# unit tests
28 +$ npm run test
29 +
30 +# e2e tests
31 +$ npm run test:e2e
32 +
33 +# test coverage
34 +$ npm run test:cov
35 +```
File mode changed
1 +module.exports = {
2 + apps: [
3 + {
4 + name: 'backend',
5 + script: 'npm',
6 + args: 'start',
7 + },
8 + ],
9 +};
1 +export DATABASE_USER=db
2 +export DATABASE_PASSWORD=1234
3 +export DATABASE_PORT=20208
4 +export DATABASE_HOST=14.33.35.148
5 +export DATABASE_NAME=dronedb
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "collection": "@nestjs/schematics",
3 + "sourceRoot": "src"
4 +}
This diff could not be displayed because it is too large.
1 +{
2 + "name": "web-api",
3 + "version": "0.0.1",
4 + "description": "",
5 + "author": "",
6 + "private": true,
7 + "license": "UNLICENSED",
8 + "scripts": {
9 + "prebuild": "rimraf dist",
10 + "build": "nest build",
11 + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
12 + "start": "nest start",
13 + "start:dev": "nest start --watch",
14 + "start:debug": "nest start --debug --watch",
15 + "start:prod": "node dist/main",
16 + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
17 + "test": "jest",
18 + "test:watch": "jest --watch",
19 + "test:cov": "jest --coverage",
20 + "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
21 + "test:e2e": "jest --config ./test/jest-e2e.json",
22 + "typeorm": "ts-node ./node_modules/typeorm/cli.js"
23 + },
24 + "dependencies": {
25 + "@nestjs/common": "^7.6.13",
26 + "@nestjs/config": "^0.6.3",
27 + "@nestjs/core": "^7.6.13",
28 + "@nestjs/platform-express": "^7.6.13",
29 + "@nestjs/platform-socket.io": "^7.6.15",
30 + "@nestjs/platform-ws": "^7.6.15",
31 + "@nestjs/typeorm": "^7.1.5",
32 + "@nestjs/websockets": "^7.6.15",
33 + "class-transformer": "^0.4.0",
34 + "class-validator": "^0.13.1",
35 + "pg": "^8.5.1",
36 + "reflect-metadata": "^0.1.13",
37 + "rimraf": "^3.0.2",
38 + "rxjs": "^6.6.6",
39 + "typeorm": "^0.2.32"
40 + },
41 + "devDependencies": {
42 + "@nestjs/cli": "^7.5.6",
43 + "@nestjs/schematics": "^7.2.7",
44 + "@nestjs/testing": "^7.6.13",
45 + "@types/express": "^4.17.11",
46 + "@types/jest": "^26.0.20",
47 + "@types/node": "^14.14.31",
48 + "@types/socket.io": "^2.1.13",
49 + "@types/supertest": "^2.0.10",
50 + "@typescript-eslint/eslint-plugin": "^4.15.2",
51 + "@typescript-eslint/parser": "^4.15.2",
52 + "eslint": "^7.20.0",
53 + "eslint-config-prettier": "^8.1.0",
54 + "eslint-plugin-prettier": "^3.3.1",
55 + "jest": "^26.6.3",
56 + "prettier": "^2.2.1",
57 + "supertest": "^6.1.3",
58 + "ts-jest": "^26.5.2",
59 + "ts-loader": "^8.0.17",
60 + "ts-node": "^9.1.1",
61 + "tsconfig-paths": "^3.9.0",
62 + "typescript": "^4.1.5"
63 + },
64 + "jest": {
65 + "moduleFileExtensions": [
66 + "js",
67 + "json",
68 + "ts"
69 + ],
70 + "rootDir": "src",
71 + "testRegex": ".*\\.spec\\.ts$",
72 + "transform": {
73 + "^.+\\.(t|j)s$": "ts-jest"
74 + },
75 + "collectCoverageFrom": [
76 + "**/*.(t|j)s"
77 + ],
78 + "coverageDirectory": "../coverage",
79 + "testEnvironment": "node"
80 + }
81 +}
1 +import { Module } from '@nestjs/common';
2 +import { ConfigModule, ConfigService } from '@nestjs/config';
3 +import { TypeOrmModule } from '@nestjs/typeorm';
4 +import configuration from './config/configuration';
5 +import { DroneModule } from './drone/drone.module';
6 +import { DroneEntity } from './entities/drone.entity';
7 +import { DroneLogEntity } from './entities/drone.log.entity';
8 +import { ScheduleEntity } from './entities/schedule.entity';
9 +import { MemberEntity } from './entities/member.entity';
10 +import { CodeEntity } from './entities/code.entity';
11 +import { DroneScheduleMappingEntity } from './entities/drone.schedule.mapping.entity';
12 +
13 +@Module({
14 + imports: [
15 + ConfigModule.forRoot({
16 + isGlobal: true,
17 + load: [configuration],
18 + }),
19 + TypeOrmModule.forRootAsync({
20 + imports: [ConfigModule],
21 + inject: [ConfigService],
22 + useFactory: (configService: ConfigService) => ({
23 + type: 'postgres',
24 + host: configService.get('database.host'),
25 + username: configService.get('database.user'),
26 + password: configService.get('database.password'),
27 + database: configService.get('database.name'),
28 + port: configService.get('database.port'),
29 + entities: [`${__dirname}/**/*.entity.{ts,js}`],
30 + synchronize: false,
31 + }),
32 + }),
33 + DroneModule,
34 + ],
35 + controllers: [],
36 + providers: [],
37 +})
38 +export class AppModule {}
1 +import 'dotenv/config';
2 +
3 +export default () => ({
4 + database: {
5 + host: process.env.DATABASE_HOST || '',
6 + user: process.env.DATABASE_USER || '',
7 + name: process.env.DATABASE_NAME || '',
8 + password: process.env.DATABASE_PASSWORD || '',
9 + port: process.env.DATABASE_PORT || '',
10 + },
11 +});
...\ No newline at end of file ...\ No newline at end of file
1 +import {
2 + Body,
3 + Controller,
4 + Delete,
5 + Get,
6 + Param,
7 + Post,
8 + Put,
9 + Query,
10 + UsePipes,
11 + ValidationPipe,
12 +} from '@nestjs/common';
13 +import { DroneService } from './drone.service';
14 +import { DroneEntity } from 'src/entities/drone.entity';
15 +import { MemberEntity } from 'src/entities/member.entity';
16 +import { DroneLogEntity } from 'src/entities/drone.log.entity';
17 +import { ScheduleEntity } from 'src/entities/schedule.entity';
18 +import { CodeEntity } from 'src/entities/code.entity';
19 +import { DroneApiDto } from 'src/drone/dto/drone.api.dto';
20 +
21 +@Controller()
22 +export class DroneController {
23 + constructor(private droneService: DroneService) {
24 + this.droneService = droneService;
25 + }
26 +
27 + @Get('/member/list')
28 + async findMemberAll(): Promise<MemberEntity[]> {
29 + const memberList = await this.droneService.findMemberAll();
30 + return Object.assign({
31 + data: {
32 + members: memberList,
33 + },
34 + statusCode: 200,
35 + statusMsg: '완료',
36 + });
37 + }
38 +
39 + @Get('/member/page')
40 + async findMemberPagination(@Query() queries): Promise<MemberEntity[]> {
41 + const pageNo = queries.pageNo;
42 + const pageSize = queries.pageSize;
43 +
44 + const begin = (pageNo - 1) * pageSize;
45 + const end = pageNo * pageSize;
46 +
47 + const memberList = await (await this.droneService.findMemberAll()).slice(
48 + begin,
49 + end,
50 + );
51 + const totalElement = (await this.droneService.findMemberAll()).length;
52 + return Object.assign({
53 + data: {
54 + pageNo: pageNo,
55 + pageSize: pageSize,
56 + totalElement: totalElement,
57 + members: memberList,
58 + },
59 + statusCode: 200,
60 + statusMsg: '완료',
61 + });
62 + }
63 +
64 + @Get('/member/:id')
65 + async findMemberOne(@Param('id') id: number): Promise<MemberEntity> {
66 + const foundMember = await this.droneService.findMemberOne(id);
67 + return Object.assign({
68 + data: foundMember,
69 + statusCode: 200,
70 + statusMsg: '완료',
71 + });
72 + }
73 +
74 + @Get('/drone/list')
75 + async findDroneAll(@Query() queries): Promise<DroneEntity[]> {
76 + // Params 변수
77 + const modelName = queries.modelName;
78 + const maker = queries.maker;
79 + const usageName = queries.usageName;
80 + const minWeight = queries.minWeight;
81 + const maxWeight = queries.maxWeight;
82 +
83 + const droneList = await this.droneService.findDroneAll({
84 + modelName,
85 + maker,
86 + usageName,
87 + minWeight,
88 + maxWeight,
89 + });
90 +
91 + return Object.assign({
92 + data: {
93 + drones: droneList,
94 + },
95 + statusCode: 200,
96 + statusMsg: '완료',
97 + });
98 + }
99 +
100 + @Get('/drone/page')
101 + async findDronePagination(@Query() queries): Promise<DroneEntity[]> {
102 + const modelName = queries.modelName;
103 + const maker = queries.maker;
104 + const usageName = queries.usageName;
105 + const minWeight = queries.minWeight;
106 + const maxWeight = queries.maxWeight;
107 + const pageNo = queries.pageNo;
108 + const pageSize = queries.pageSize;
109 +
110 + const droneList = await this.droneService.findDroneAll({
111 + modelName,
112 + maker,
113 + usageName,
114 + minWeight,
115 + maxWeight,
116 + pageNo,
117 + pageSize,
118 + });
119 + const totalElement = await this.droneService.findDroneTotalElement({
120 + modelName,
121 + maker,
122 + usageName,
123 + minWeight,
124 + maxWeight,
125 + });
126 +
127 + return Object.assign({
128 + data: {
129 + pageNo: pageNo,
130 + pageSize: pageSize,
131 + totalElement: totalElement,
132 + drones: droneList,
133 + },
134 + statusCode: 200,
135 + statusMsg: '완료',
136 + });
137 + }
138 +
139 + @Get('/drone/:id')
140 + async findDroneOne(@Param('id') id: number): Promise<DroneEntity> {
141 + const foundDrone = await this.droneService.findDroneOne(id);
142 + return Object.assign({
143 + data: foundDrone,
144 + statusCode: 200,
145 + statusMsg: '완료',
146 + });
147 + }
148 +
149 + @Get('/map/drone/:id')
150 + async findMapDroneOne(@Param('id') id: number) {
151 + const foundDrone = await this.droneService.findDroneOne(id);
152 + const foundSchedule = await this.droneService.findScheduleByDroneId(id);
153 +
154 + if (!foundSchedule) {
155 + return Object.assign({
156 + data: {
157 + foundDrone,
158 + foundSchedule: null,
159 + droneLogs: [],
160 + },
161 + statusCode: 200,
162 + statusMsg: '완료',
163 + });
164 + }
165 +
166 + const logList = await this.droneService.findLogListByScheduleId(
167 + foundSchedule.scheduleId,
168 + );
169 +
170 + return Object.assign({
171 + data: {
172 + foundDrone,
173 + foundSchedule,
174 + droneLogs: logList,
175 + },
176 + statusCode: 200,
177 + statusMsg: '완료',
178 + });
179 + }
180 +
181 + @Get('/log/list')
182 + async findLogAll(@Query() queries): Promise<DroneLogEntity[]> {
183 + const scheduleId = queries.scheduleId;
184 + const latitude = queries.latitude;
185 + const longitude = queries.longitude;
186 + const minVerticalSpeed = queries.minVerticalSpeed;
187 + const maxVerticalSpeed = queries.maxVerticalSpeed;
188 + const minHorizontalSpeed = queries.minHorizontalSpeed;
189 + const maxHorizontalSpeed = queries.maxHorizontalSpeed;
190 + const minAboveSeaLevel = queries.minAboveSeaLevel;
191 + const maxAboveSeaLevel = queries.maxAboveSeaLevel;
192 + const minAboveGroundLevel = queries.minAboveGroundLevel;
193 + const maxAboveGroundLevel = queries.maxAboveGroundLevel;
194 +
195 + const logList = await this.droneService.findLogAll({
196 + scheduleId,
197 + latitude,
198 + longitude,
199 + minVerticalSpeed,
200 + maxVerticalSpeed,
201 + minHorizontalSpeed,
202 + maxHorizontalSpeed,
203 + minAboveSeaLevel,
204 + maxAboveSeaLevel,
205 + minAboveGroundLevel,
206 + maxAboveGroundLevel,
207 + });
208 +
209 + return Object.assign({
210 + data: {
211 + droneLogs: logList,
212 + },
213 + statusCode: 200,
214 + statusMsg: '완료',
215 + });
216 + }
217 +
218 + @Get('/log/page')
219 + async findLogPagination(@Query() queries): Promise<DroneLogEntity[]> {
220 + const scheduleId = queries.scheduleId;
221 + const latitude = queries.latitude;
222 + const longitude = queries.longitude;
223 + const minVerticalSpeed = queries.minVerticalSpeed;
224 + const maxVerticalSpeed = queries.maxVerticalSpeed;
225 + const minHorizontalSpeed = queries.minHorizontalSpeed;
226 + const maxHorizontalSpeed = queries.maxHorizontalSpeed;
227 + const minAboveSeaLevel = queries.minAboveSeaLevel;
228 + const maxAboveSeaLevel = queries.maxAboveSeaLevel;
229 + const minAboveGroundLevel = queries.minAboveGroundLevel;
230 + const maxAboveGroundLevel = queries.maxAboveGroundLevel;
231 + const pageNo = queries.pageNo;
232 + const pageSize = queries.pageSize;
233 +
234 + const logList = await this.droneService.findLogAll({
235 + scheduleId,
236 + latitude,
237 + longitude,
238 + minVerticalSpeed,
239 + maxVerticalSpeed,
240 + minHorizontalSpeed,
241 + maxHorizontalSpeed,
242 + minAboveSeaLevel,
243 + maxAboveSeaLevel,
244 + minAboveGroundLevel,
245 + maxAboveGroundLevel,
246 + pageNo,
247 + pageSize,
248 + });
249 + const totalElement = await this.droneService.findLogTotalElement({
250 + scheduleId,
251 + latitude,
252 + longitude,
253 + minVerticalSpeed,
254 + maxVerticalSpeed,
255 + minHorizontalSpeed,
256 + maxHorizontalSpeed,
257 + minAboveSeaLevel,
258 + maxAboveSeaLevel,
259 + minAboveGroundLevel,
260 + maxAboveGroundLevel,
261 + });
262 +
263 + return Object.assign({
264 + data: {
265 + pageNo: pageNo,
266 + pageSize: pageSize,
267 + totalElement: totalElement,
268 + droneLogs: logList,
269 + },
270 + statusCode: 200,
271 + statusMsg: '완료',
272 + });
273 + }
274 +
275 + @Get('/log/:id')
276 + async findLogOne(@Param('id') id: number): Promise<DroneLogEntity> {
277 + const foundLog = await this.droneService.findLogOne(id);
278 + return Object.assign({
279 + data: foundLog,
280 + statusCode: 200,
281 + statusMsg: '완료',
282 + });
283 + }
284 +
285 + @Get('/schedule/list')
286 + async findScheduleAll(@Query() queries): Promise<ScheduleEntity[]> {
287 + const droneId = queries.droneId;
288 + const startTime = queries.startTime;
289 + const terminateTime = queries.terminateTime;
290 +
291 + const scheduleList = await this.droneService.findScheduleAll({
292 + droneId,
293 + startTime,
294 + terminateTime,
295 + });
296 + return Object.assign({
297 + data: {
298 + schedules: scheduleList,
299 + },
300 + statusCode: 200,
301 + statusMsg: '완료',
302 + });
303 + }
304 +
305 + @Get('/schedule/page')
306 + async findSchedulePagination(@Query() queries): Promise<ScheduleEntity[]> {
307 + const droneId = queries.droneId;
308 + const startTime = queries.startTime;
309 + const terminateTime = queries.terminateTime;
310 + const pageNo = queries.pageNo;
311 + const pageSize = queries.pageSize;
312 +
313 + const scheduleList = await this.droneService.findScheduleAll({
314 + droneId,
315 + startTime,
316 + terminateTime,
317 + pageNo,
318 + pageSize,
319 + });
320 + const totalElement = await this.droneService.findScheduleTotalElement({
321 + droneId,
322 + startTime,
323 + terminateTime,
324 + });
325 +
326 + return Object.assign({
327 + data: {
328 + pageNo: pageNo,
329 + pageSize: pageSize,
330 + totalElement: totalElement,
331 + schedules: scheduleList,
332 + },
333 + statusCode: 200,
334 + statusMsg: '완료',
335 + });
336 + }
337 +
338 + @Get('/schedule/:id')
339 + async findScheduleOne(@Param('id') id: number): Promise<ScheduleEntity> {
340 + const foundSchedule = await this.droneService.findScheduleOne(id);
341 + return Object.assign({
342 + data: foundSchedule,
343 + statusCode: 200,
344 + statusMsg: '완료',
345 + });
346 + }
347 +
348 + @Get('/code/list')
349 + async findCodeAll(): Promise<CodeEntity[]> {
350 + const codeList = await this.droneService.findCodeAll();
351 + return Object.assign({
352 + data: {
353 + codes: codeList,
354 + },
355 + statusCode: 200,
356 + statusMsg: '완료',
357 + });
358 + }
359 +
360 + @Get('/code/page')
361 + async findCodePagination(@Query() queries): Promise<CodeEntity[]> {
362 + const pageNo = queries.pageNo;
363 + const pageSize = queries.pageSize;
364 +
365 + const begin = (pageNo - 1) * pageSize;
366 + const end = pageNo * pageSize;
367 +
368 + const codeList = await (await this.droneService.findCodeAll()).slice(
369 + begin,
370 + end,
371 + );
372 +
373 + const totalElement = (await this.droneService.findCodeAll()).length;
374 + return Object.assign({
375 + data: {
376 + pageNo: pageNo,
377 + pageSize: pageSize,
378 + totalElement: totalElement,
379 + codes: codeList,
380 + },
381 + statusCode: 200,
382 + statusMsg: '완료',
383 + });
384 + }
385 +
386 + @Get('/code/:id')
387 + async findCodeOne(@Param('id') id: number): Promise<CodeEntity> {
388 + const foundCode = await this.droneService.findCodeOne(id);
389 + return Object.assign({
390 + data: foundCode,
391 + statusCode: 200,
392 + statusMsg: '완료',
393 + });
394 + }
395 +
396 + @Post()
397 + async saveMember(@Body() memberEntity: MemberEntity): Promise<string> {
398 + await this.droneService.saveMember(memberEntity);
399 + return Object.assign({
400 + statusCode: 201,
401 + data: { ...memberEntity },
402 + statusMsg: '완료',
403 + });
404 + }
405 +
406 + @Post()
407 + async saveDrone(@Body() droneEntity: DroneEntity): Promise<string> {
408 + await this.droneService.saveDrone(droneEntity);
409 + return Object.assign({
410 + statusCode: 201,
411 + data: { ...droneEntity },
412 + statusMsg: '완료',
413 + });
414 + }
415 +
416 + @Post('drone/list')
417 + @UsePipes(new ValidationPipe({ transform: true }))
418 + async saveDroneList(@Body() saveDroneListDto: DroneApiDto.SaveDroneListDto) {
419 + const affectedRows = await this.droneService.saveDroneList(
420 + saveDroneListDto.droneList,
421 + );
422 + return {
423 + statusCode: 201,
424 + data: { affectedRows },
425 + statusMessage: '완료',
426 + };
427 + }
428 +
429 + @Put('drone/list')
430 + @UsePipes(new ValidationPipe({ transform: true }))
431 + async updateDroneList(
432 + @Body() updateDroneListDto: DroneApiDto.UpdateDroneListDto,
433 + ) {
434 + const affectedRows = await this.droneService.updateDroneList(
435 + updateDroneListDto.droneList,
436 + );
437 + return {
438 + statusCode: 201,
439 + data: { affectedRows },
440 + statusMessage: '완료',
441 + };
442 + }
443 +
444 + @Post()
445 + async saveLog(@Body() dronelogEntity: DroneLogEntity): Promise<string> {
446 + await this.droneService.saveLog(dronelogEntity);
447 + return Object.assign({
448 + statusCode: 201,
449 + data: { ...dronelogEntity },
450 + statusMsg: '완료',
451 + });
452 + }
453 +
454 + @Post()
455 + async saveSchedule(@Body() ScheduleEntity: ScheduleEntity): Promise<string> {
456 + await this.droneService.saveSchedule(ScheduleEntity);
457 + return Object.assign({
458 + statusCode: 201,
459 + data: { ...ScheduleEntity },
460 + statusMsg: '완료',
461 + });
462 + }
463 +
464 + @Post('schdule/list')
465 + @UsePipes(new ValidationPipe({ transform: true }))
466 + async saveSchduleList(
467 + @Body() saveSchduleListDto: DroneApiDto.SaveSchduleListDto,
468 + ) {
469 + const affectedRows = await this.droneService.saveScheduleList(
470 + saveSchduleListDto.schduleList,
471 + );
472 + return {
473 + statusCode: 200,
474 + data: { affectedRows },
475 + statusMessage: '완료',
476 + };
477 + }
478 +
479 + @Put('schdule/list')
480 + @UsePipes(new ValidationPipe({ transform: true }))
481 + async updateSchduleList(
482 + @Body() updateSchduleListDto: DroneApiDto.UpdateSchduleListDto,
483 + ) {
484 + const affectedRows = await this.droneService.updateSchduleList(
485 + updateSchduleListDto.schduleList,
486 + );
487 + return {
488 + statusCode: 201,
489 + data: { affectedRows },
490 + statusMessage: '완료',
491 + };
492 + }
493 +
494 + @Post()
495 + async saveCode(@Body() CodeEntity: CodeEntity): Promise<string> {
496 + await this.droneService.saveCode(CodeEntity);
497 + return Object.assign({
498 + statusCode: 201,
499 + data: { ...CodeEntity },
500 + statusMsg: '완료',
501 + });
502 + }
503 +
504 + @Delete('/member/:id')
505 + async deleteMember(@Param('id') id: number): Promise<string> {
506 + await this.droneService.deleteMember(id);
507 + return Object.assign({
508 + statusCode: 201,
509 + data: {
510 + id: id,
511 + },
512 + statusMsg: '완료',
513 + });
514 + }
515 +
516 + @Delete('/drone/:id')
517 + async deleteDrone(@Param('id') id: number): Promise<string> {
518 + await this.droneService.deleteDrone(id);
519 + return Object.assign({
520 + statusCode: 201,
521 + data: {
522 + id: id,
523 + },
524 + statusMsg: '완료',
525 + });
526 + }
527 +
528 + @Delete('/log/:id')
529 + async deleteLog(@Param('id') id: number): Promise<string> {
530 + await this.droneService.deleteLog(id);
531 + return Object.assign({
532 + statusCode: 201,
533 + data: {
534 + id: id,
535 + },
536 + statusMsg: '완료',
537 + });
538 + }
539 +
540 + @Delete('/schedule/:id')
541 + async deleteSchedule(@Param('id') id: number): Promise<string> {
542 + await this.droneService.deleteSchedule(id);
543 + return Object.assign({
544 + statusCode: 201,
545 + data: {
546 + id: id,
547 + },
548 + statusMsg: '완료',
549 + });
550 + }
551 +
552 + @Delete('/code/:id')
553 + async deleteCode(@Param('id') id: number): Promise<string> {
554 + await this.droneService.deleteCode(id);
555 + return Object.assign({
556 + statusCode: 201,
557 + data: {
558 + id: id,
559 + },
560 + statusMsg: '완료',
561 + });
562 + }
563 +}
1 +import {
2 + Body,
3 + Controller,
4 + Post,
5 + UsePipes,
6 + ValidationPipe,
7 +} from '@nestjs/common';
8 +import { DroneDataService } from 'src/drone/drone.data.service';
9 +import { DroneApiDto } from 'src/drone/dto/drone.api.dto';
10 +
11 +@Controller()
12 +export class DroneDataController {
13 + constructor(private droneDataService: DroneDataService) {
14 + this.droneDataService = droneDataService;
15 + }
16 +
17 + @Post('/droneLog/list')
18 + @UsePipes(new ValidationPipe({ transform: true }))
19 + async saveDroneLogList(
20 + @Body() saveDroneLogListDto: DroneApiDto.SaveDroneLogListDto,
21 + ) {
22 + await this.droneDataService.saveDroneLogList(
23 + saveDroneLogListDto.droneLogList,
24 + );
25 + return {
26 + statusCode: 200,
27 + statusMsg: '완료',
28 + };
29 + }
30 +}
1 +import { Injectable } from '@nestjs/common';
2 +import { InjectRepository } from '@nestjs/typeorm';
3 +import { DroneLogEntity } from 'src/entities/drone.log.entity';
4 +import { DroneGateway } from 'src/drone/drone.gateway';
5 +import { Repository } from 'typeorm/index';
6 +import { DroneApiDto } from 'src/drone/dto/drone.api.dto';
7 +
8 +@Injectable()
9 +export class DroneDataService {
10 + constructor(
11 + @InjectRepository(DroneLogEntity)
12 + private dronelogRepository: Repository<DroneLogEntity>,
13 + private droneGateway: DroneGateway,
14 + ) {
15 + this.dronelogRepository = dronelogRepository;
16 + this.droneGateway = droneGateway;
17 + }
18 +
19 + async saveDroneLogList(droneLogList: DroneApiDto.SaveDroneLogDto[]) {
20 + // 드론 데이터 전송
21 + this.sendDroneLogList(droneLogList.map((log) => new DroneLogEntity(log)));
22 +
23 + // 드론로그 생성
24 + for (const droneLog of droneLogList) {
25 + this.dronelogRepository.save({
26 + droneId: droneLog.droneId,
27 + scheduleId: droneLog.scheduleId,
28 + latitude: droneLog.latitude,
29 + longitude: droneLog.longitude,
30 + verticalSpeed: droneLog.verticalSpeed,
31 + horizontalSpeed: droneLog.horizontalSpeed,
32 + aboveSeaLevel: droneLog.aboveSeaLevel,
33 + aboveGroundLevel: droneLog.aboveGroundLevel,
34 + });
35 + }
36 + }
37 +
38 + sendDroneLogList(droneLogList) {
39 + // 드론데이터 전송
40 + this.droneGateway.sendToClientsDroneLogList(droneLogList);
41 + for (const droneLog of droneLogList) {
42 + let droneLogEntity = new DroneLogEntity(droneLog);
43 + this.dronelogRepository.save(droneLogEntity);
44 + }
45 + }
46 +
47 + async saveLog(droneLogEntity: DroneLogEntity): Promise<void> {
48 + await this.dronelogRepository.save(droneLogEntity);
49 + }
50 +}
1 +import {
2 + SubscribeMessage,
3 + WebSocketGateway,
4 + WebSocketServer,
5 +} from '@nestjs/websockets';
6 +import { Server, Socket } from 'ws';
7 +import { DroneService } from './drone.service';
8 +import { DroneLogEntity } from 'src/entities/drone.log.entity';
9 +
10 +async function sleep(ms) {
11 + return new Promise((resolve) => {
12 + setTimeout(resolve, ms);
13 + });
14 +}
15 +
16 +@WebSocketGateway(20206, { path: '/drone' })
17 +export class DroneGateway {
18 + constructor(private readonly droneService: DroneService) {}
19 +
20 + @WebSocketServer()
21 + server: Server;
22 +
23 + private wsClients = [];
24 +
25 + async afterInit() {
26 + let globalCircleStep = 0;
27 + while (1) {
28 + if (globalCircleStep === 3600) {
29 + globalCircleStep = 0;
30 + } else {
31 + globalCircleStep += 1;
32 + }
33 + this.wsClients.forEach((client) => {
34 + const droneTestData = this.droneService.getDroneTestData({
35 + globalCircleStep,
36 + });
37 + client.send(
38 + JSON.stringify(
39 + Object.assign({
40 + data: { droneLog: droneTestData },
41 + statusCode: 200,
42 + }),
43 + ),
44 + );
45 + });
46 + await sleep(1000);
47 + }
48 + }
49 +
50 + async handleConnection(client: Socket) {
51 + let clientId = this.wsClients.push(client);
52 + }
53 +
54 + handleDisconnect(client: Socket) {
55 + let clientIndex = this.wsClients.find((wsClient) => wsClient === client);
56 + if (clientIndex !== -1) {
57 + this.wsClients.splice(clientIndex, 1);
58 + }
59 + }
60 +
61 + @SubscribeMessage('')
62 + handleMessages(client: Socket, payload: { name: string; text: string }) {
63 + client.send(
64 + JSON.stringify({
65 + number: 10,
66 + member: 'tony',
67 + }),
68 + );
69 + }
70 +
71 + sendToClientsDroneLogList(droneLogList: Array<DroneLogEntity>) {
72 + this.wsClients.forEach((client) => {
73 + client.send(
74 + JSON.stringify(
75 + Object.assign({
76 + data: { droneLog: droneLogList },
77 + statusCode: 200,
78 + }),
79 + ),
80 + );
81 + });
82 + }
83 +}
1 +import { Module } from '@nestjs/common';
2 +import { TypeOrmModule } from '@nestjs/typeorm';
3 +import { DroneController } from './drone.controller';
4 +import { DroneService } from './drone.service';
5 +import { DroneDataController } from 'src/drone/drone.data.controller';
6 +import { DroneDataService } from './drone.data.service';
7 +import { DroneGateway } from './drone.gateway';
8 +import { DroneEntity } from 'src/entities/drone.entity';
9 +import { ScheduleEntity } from 'src/entities/schedule.entity';
10 +import { DroneLogEntity } from 'src/entities/drone.log.entity';
11 +import { MemberEntity } from 'src/entities/member.entity';
12 +import { CodeEntity } from 'src/entities/code.entity';
13 +import { DroneScheduleMappingEntity } from 'src/entities/drone.schedule.mapping.entity';
14 +
15 +@Module({
16 + imports: [
17 + TypeOrmModule.forFeature([
18 + MemberEntity,
19 + DroneEntity,
20 + DroneLogEntity,
21 + ScheduleEntity,
22 + CodeEntity,
23 + DroneScheduleMappingEntity,
24 + ]),
25 + ],
26 + controllers: [DroneController, DroneDataController],
27 + providers: [DroneGateway, DroneService, DroneDataService],
28 +})
29 +export class DroneModule {}
1 +import { Injectable, HttpException } from '@nestjs/common';
2 +import { InjectRepository } from '@nestjs/typeorm';
3 +import { DroneEntity } from 'src/entities/drone.entity';
4 +import { DroneLogEntity } from 'src/entities/drone.log.entity';
5 +import { ScheduleEntity } from 'src/entities/schedule.entity';
6 +import { MemberEntity } from 'src/entities/member.entity';
7 +import { DroneApiDto } from 'src/drone/dto/drone.api.dto';
8 +import { DroneLogDto } from 'src/drone/dto/droneLog.dto';
9 +import { getRepository, Repository } from 'typeorm/index';
10 +import { DroneScheduleMappingEntity } from 'src/entities/drone.schedule.mapping.entity';
11 +import { CodeEntity } from 'src/entities/code.entity';
12 +
13 +@Injectable()
14 +export class DroneService {
15 + constructor(
16 + @InjectRepository(MemberEntity)
17 + private memberRepository: Repository<MemberEntity>,
18 + @InjectRepository(DroneEntity)
19 + private droneRepository: Repository<DroneEntity>,
20 + @InjectRepository(DroneLogEntity)
21 + private dronelogRepository: Repository<DroneLogEntity>,
22 + @InjectRepository(ScheduleEntity)
23 + private scheduleRepository: Repository<ScheduleEntity>,
24 + @InjectRepository(CodeEntity)
25 + private codeRepository: Repository<CodeEntity>,
26 + @InjectRepository(DroneScheduleMappingEntity)
27 + private droneschedulemappingRepository: Repository<DroneScheduleMappingEntity>,
28 + ) {
29 + this.memberRepository = memberRepository;
30 + this.droneRepository = droneRepository;
31 + this.dronelogRepository = dronelogRepository;
32 + this.scheduleRepository = scheduleRepository;
33 + this.codeRepository = codeRepository;
34 + this.droneschedulemappingRepository = droneschedulemappingRepository;
35 + }
36 +
37 + droneMap = {};
38 +
39 + // test Drone Data
40 + droneCount: number = 1200;
41 +
42 + getRandomArbitrary(min: number, max: number): number {
43 + return Math.random() * (max - min) + min;
44 + }
45 +
46 + circleMove(
47 + x: number,
48 + y: number,
49 + radius: number,
50 + max: number,
51 + circleStep: number,
52 + ) {
53 + return {
54 + x: x + radius * Math.cos((2 * Math.PI * circleStep) / max),
55 + y: y + radius * Math.sin((2 * Math.PI * circleStep) / max),
56 + };
57 + }
58 +
59 + logData: Array<{ x: number; y: number; id: number }> = Array.from(
60 + { length: this.droneCount },
61 + (v, i) => {
62 + return {
63 + x:
64 + this.getRandomArbitrary(37200000000000, 37300000000000) /
65 + 1000000000000,
66 + y:
67 + this.getRandomArbitrary(126900000000000, 127100000000000) /
68 + 1000000000000,
69 + id: i,
70 + };
71 + },
72 + );
73 +
74 + getDroneTestData({ globalCircleStep }) {
75 + const cordData = [];
76 + for (let i = 0; i < this.droneCount; i += 1) {
77 + const circleCord: { x: number; y: number } = this.circleMove(
78 + this.logData[i].x,
79 + this.logData[i].y,
80 + 0.05,
81 + 3600,
82 + globalCircleStep,
83 + );
84 + cordData.push({
85 + droneId: this.logData[i].id,
86 + scheduleId: this.logData[i].id,
87 + latitude: circleCord.x,
88 + longitude: circleCord.y,
89 + verticalSpeed: Math.round(Math.random() * 100),
90 + horizontalSpeed: Math.round(Math.random() * 100),
91 + aboveSeaLevel: 22,
92 + aboveGroundLevel: 33,
93 + });
94 + }
95 +
96 + return cordData;
97 + }
98 +
99 + // 드론ID로 스케줄 찾기
100 + async findScheduleByDroneId(droneId: number) {
101 + const scheduleList = getRepository(DroneScheduleMappingEntity)
102 + .createQueryBuilder('droneSchedule')
103 + .select('droneSchedule.scheduleId', 'scheduleId')
104 + .addSelect('schedule.startTime', 'startTime')
105 + .addSelect('schedule.terminateTime', 'terminateTime')
106 + .addSelect('schedule.startLatitude', 'startLatitude')
107 + .addSelect('schedule.startLongitude', 'startLongitude')
108 + .addSelect('schedule.terminateLatitude', 'terminateLatitude')
109 + .addSelect('schedule.terminateLongitude', 'terminateLongitude')
110 + .innerJoin(
111 + ScheduleEntity,
112 + 'schedule',
113 + 'droneSchedule.scheduleId = schedule.id',
114 + )
115 + .where('droneSchedule.droneId = :droneId', {
116 + droneId: droneId,
117 + })
118 + .andWhere('schedule.startTime <= NOW()')
119 + .andWhere('schedule.terminateTime >= NOW()')
120 + .orderBy('droneSchedule.scheduleId', 'DESC')
121 + .getRawOne();
122 +
123 + return scheduleList;
124 + }
125 +
126 + // 스케줄ID로 로그 찾기
127 + async findLogListByScheduleId(scheduleId: number) {
128 + const logList = getRepository(DroneLogEntity)
129 + .createQueryBuilder('droneLog')
130 + .where('droneLog.scheduleId = :scheduleId', {
131 + scheduleId: scheduleId,
132 + })
133 + .orderBy('droneLog.id', 'DESC')
134 + .getMany();
135 +
136 + return logList;
137 + }
138 +
139 + findMemberAll(): Promise<MemberEntity[]> {
140 + return this.memberRepository.find();
141 + }
142 +
143 + async findDroneAll({
144 + modelName,
145 + maker,
146 + usageName,
147 + minWeight,
148 + maxWeight,
149 + pageNo,
150 + pageSize,
151 + }: {
152 + modelName?: string;
153 + maker?: string;
154 + usageName?: string;
155 + minWeight?: number;
156 + maxWeight?: number;
157 + pageNo?: number;
158 + pageSize?: number;
159 + }): Promise<DroneEntity[]> {
160 + const droneList = getRepository(DroneEntity).createQueryBuilder('drone');
161 +
162 + if (modelName) {
163 + droneList.where('drone.modelName like :modelName', {
164 + modelName: `%${modelName}%`,
165 + });
166 + }
167 +
168 + if (maker) {
169 + droneList.andWhere('drone.maker like :maker', { maker: `%${maker}%` });
170 + }
171 +
172 + if (usageName) {
173 + droneList.andWhere('drone.usageName = :usageName', {
174 + usageName: usageName,
175 + });
176 + }
177 +
178 + if (minWeight) {
179 + droneList.andWhere('drone.weight >= :minWeight', {
180 + minWeight: minWeight,
181 + });
182 + }
183 +
184 + if (maxWeight) {
185 + droneList.andWhere('drone.weight <= :maxWeight', {
186 + maxWeight: maxWeight,
187 + });
188 + }
189 +
190 + droneList.orderBy('drone.id', 'DESC');
191 +
192 + if (pageNo && pageSize) {
193 + droneList.offset((pageNo - 1) * pageSize).limit(pageSize);
194 + }
195 + return droneList.getMany();
196 + }
197 +
198 + async findDroneTotalElement({
199 + modelName,
200 + maker,
201 + usageName,
202 + minWeight,
203 + maxWeight,
204 + }: {
205 + modelName?: string;
206 + maker?: string;
207 + usageName?: string;
208 + minWeight?: number;
209 + maxWeight?: number;
210 + }) {
211 + const droneList = getRepository(DroneEntity).createQueryBuilder('drone');
212 +
213 + if (modelName) {
214 + droneList.where('drone.modelName like :modelName', {
215 + modelName: `%${modelName}%`,
216 + });
217 + }
218 +
219 + if (maker) {
220 + droneList.andWhere('drone.maker like :maker', { maker: `%${maker}%` });
221 + }
222 +
223 + if (usageName) {
224 + droneList.andWhere('drone.usageName = :usageName', {
225 + usageName: usageName,
226 + });
227 + }
228 +
229 + if (minWeight) {
230 + droneList.andWhere('drone.weight >= :minWeight', {
231 + minWeight: minWeight,
232 + });
233 + }
234 +
235 + if (maxWeight) {
236 + droneList.andWhere('drone.weight <= :maxWeight', {
237 + maxWeight: maxWeight,
238 + });
239 + }
240 +
241 + return droneList.getCount();
242 + }
243 +
244 + async findLogAll({
245 + scheduleId,
246 + latitude,
247 + longitude,
248 + minVerticalSpeed,
249 + maxVerticalSpeed,
250 + minHorizontalSpeed,
251 + maxHorizontalSpeed,
252 + minAboveSeaLevel,
253 + maxAboveSeaLevel,
254 + minAboveGroundLevel,
255 + maxAboveGroundLevel,
256 + pageNo,
257 + pageSize,
258 + }: {
259 + scheduleId?: number;
260 + latitude?: number;
261 + longitude?: string;
262 + minVerticalSpeed?: number;
263 + maxVerticalSpeed?: number;
264 + minHorizontalSpeed?: number;
265 + maxHorizontalSpeed?: number;
266 + minAboveSeaLevel?: number;
267 + maxAboveSeaLevel?: number;
268 + minAboveGroundLevel?: number;
269 + maxAboveGroundLevel?: number;
270 + pageNo?: number;
271 + pageSize?: number;
272 + }): Promise<any[]> {
273 + const logList = getRepository(DroneLogEntity)
274 + .createQueryBuilder('droneLog')
275 + .select('droneLog.id', 'id')
276 + .addSelect('drone.modelName', 'modelName')
277 + .addSelect('droneLog.droneId', 'droneId')
278 + .addSelect('droneLog.scheduleId', 'scheduleId')
279 + .addSelect('droneLog.latitude', 'latitude')
280 + .addSelect('droneLog.longitude', 'longitude')
281 + .addSelect('droneLog.verticalSpeed', 'verticalSpeed')
282 + .addSelect('droneLog.horizontalSpeed', 'horizontalSpeed')
283 + .addSelect('droneLog.aboveSeaLevel', 'aboveSeaLevel')
284 + .addSelect('droneLog.aboveGroundLevel', 'aboveGroundLevel')
285 + .addSelect('droneLog.createdAt', 'createdAt')
286 + .innerJoin(DroneEntity, 'drone', 'drone.id = droneLog.droneId');
287 +
288 + if (scheduleId) {
289 + logList.where('droneLog.scheduleId = :scheduleId', {
290 + scheduleId: scheduleId,
291 + });
292 + }
293 +
294 + if (latitude) {
295 + logList.andWhere('cast(droneLog.latitude as varchar) like :latitude', {
296 + latitude: `%${latitude}%`,
297 + });
298 + }
299 +
300 + if (longitude) {
301 + logList.andWhere('cast(droneLog.longitude as varchar) like :longitude', {
302 + longitude: `%${longitude}%`,
303 + });
304 + }
305 +
306 + if (minVerticalSpeed) {
307 + logList.andWhere('droneLog.verticalSpeed >= :minVerticalSpeed', {
308 + minVerticalSpeed: minVerticalSpeed,
309 + });
310 + }
311 +
312 + if (maxVerticalSpeed) {
313 + logList.andWhere('droneLog.verticalSpeed <= :maxVerticalSpeed', {
314 + maxVerticalSpeed: maxVerticalSpeed,
315 + });
316 + }
317 +
318 + if (minHorizontalSpeed) {
319 + logList.andWhere('droneLog.horizontalSpeed >= :minHorizontalSpeed', {
320 + minHorizontalSpeed: minHorizontalSpeed,
321 + });
322 + }
323 +
324 + if (maxHorizontalSpeed) {
325 + logList.andWhere('droneLog.horizontalSpeed <= :maxHorizontalSpeed', {
326 + maxHorizontalSpeed: maxHorizontalSpeed,
327 + });
328 + }
329 +
330 + if (minAboveSeaLevel) {
331 + logList.andWhere('droneLog.aboveSeaLevel >= :minAboveSeaLevel', {
332 + minAboveSeaLevel: minAboveSeaLevel,
333 + });
334 + }
335 +
336 + if (maxAboveSeaLevel) {
337 + logList.andWhere('droneLog.aboveSeaLevel <= :maxAboveSeaLevel', {
338 + maxAboveSeaLevel: maxAboveSeaLevel,
339 + });
340 + }
341 +
342 + if (minAboveGroundLevel) {
343 + logList.andWhere('droneLog.aboveGroundLevel >= :minAboveGroundLevel', {
344 + minAboveGroundLevel: minAboveGroundLevel,
345 + });
346 + }
347 +
348 + if (maxAboveGroundLevel) {
349 + logList.andWhere('droneLog.aboveGroundLevel <= :maxAboveGroundLevel', {
350 + maxAboveGroundLevel: maxAboveGroundLevel,
351 + });
352 + }
353 +
354 + logList.orderBy('id', 'DESC');
355 +
356 + if (pageNo && pageSize) {
357 + logList.offset((pageNo - 1) * pageSize).limit(pageSize);
358 + }
359 + return logList.getRawMany();
360 + }
361 +
362 + async findLogTotalElement({
363 + scheduleId,
364 + latitude,
365 + longitude,
366 + minVerticalSpeed,
367 + maxVerticalSpeed,
368 + minHorizontalSpeed,
369 + maxHorizontalSpeed,
370 + minAboveSeaLevel,
371 + maxAboveSeaLevel,
372 + minAboveGroundLevel,
373 + maxAboveGroundLevel,
374 + }: {
375 + scheduleId?: number;
376 + latitude?: string;
377 + longitude?: string;
378 + minVerticalSpeed?: number;
379 + maxVerticalSpeed?: number;
380 + minHorizontalSpeed?: number;
381 + maxHorizontalSpeed?: number;
382 + minAboveSeaLevel?: number;
383 + maxAboveSeaLevel?: number;
384 + minAboveGroundLevel?: number;
385 + maxAboveGroundLevel?: number;
386 + }) {
387 + const logList = getRepository(DroneLogEntity)
388 + .createQueryBuilder('droneLog')
389 + .select('droneLog.id', 'id')
390 + .addSelect('drone.modelName', 'modelName')
391 + .addSelect('droneLog.droneId', 'droneId')
392 + .addSelect('droneLog.scheduleId', 'scheduleId')
393 + .addSelect('droneLog.latitude', 'latitude')
394 + .addSelect('droneLog.longitude', 'longitude')
395 + .addSelect('droneLog.verticalSpeed', 'verticalSpeed')
396 + .addSelect('droneLog.horizontalSpeed', 'horizontalSpeed')
397 + .addSelect('droneLog.aboveSeaLevel', 'aboveSeaLevel')
398 + .addSelect('droneLog.aboveGroundLevel', 'aboveGroundLevel')
399 + .addSelect('droneLog.createdAt', 'createdAt')
400 + .innerJoin(DroneEntity, 'drone', 'drone.id = droneLog.droneId');
401 +
402 + if (scheduleId) {
403 + logList.where('droneLog.scheduleId = :scheduleId', {
404 + scheduleId: scheduleId,
405 + });
406 + }
407 +
408 + if (latitude) {
409 + logList.andWhere('cast(droneLog.latitude as varchar) like :latitude', {
410 + latitude: `%${latitude}%`,
411 + });
412 + }
413 +
414 + if (longitude) {
415 + logList.andWhere('cast(droneLog.longitude as varchar) like :longitude', {
416 + longitude: `%${longitude}%`,
417 + });
418 + }
419 +
420 + if (minVerticalSpeed) {
421 + logList.andWhere('droneLog.verticalSpeed >= :minVerticalSpeed', {
422 + minVerticalSpeed: minVerticalSpeed,
423 + });
424 + }
425 +
426 + if (maxVerticalSpeed) {
427 + logList.andWhere('droneLog.verticalSpeed <= :maxVerticalSpeed', {
428 + maxVerticalSpeed: maxVerticalSpeed,
429 + });
430 + }
431 +
432 + if (minHorizontalSpeed) {
433 + logList.andWhere('droneLog.horizontalSpeed >= :minHorizontalSpeed', {
434 + minHorizontalSpeed: minHorizontalSpeed,
435 + });
436 + }
437 +
438 + if (maxHorizontalSpeed) {
439 + logList.andWhere('droneLog.horizontalSpeed <= :maxHorizontalSpeed', {
440 + maxHorizontalSpeed: maxHorizontalSpeed,
441 + });
442 + }
443 +
444 + if (minAboveSeaLevel) {
445 + logList.andWhere('droneLog.aboveSeaLevel >= :minAboveSeaLevel', {
446 + minAboveSeaLevel: minAboveSeaLevel,
447 + });
448 + }
449 +
450 + if (maxAboveSeaLevel) {
451 + logList.andWhere('droneLog.aboveSeaLevel <= :maxAboveSeaLevel', {
452 + maxAboveSeaLevel: maxAboveSeaLevel,
453 + });
454 + }
455 +
456 + if (minAboveGroundLevel) {
457 + logList.andWhere('droneLog.aboveGroundLevel >= :minAboveGroundLevel', {
458 + minAboveGroundLevel: minAboveGroundLevel,
459 + });
460 + }
461 +
462 + if (maxAboveGroundLevel) {
463 + logList.andWhere('droneLog.aboveGroundLevel <= :maxAboveGroundLevel', {
464 + maxAboveGroundLevel: maxAboveGroundLevel,
465 + });
466 + }
467 +
468 + return logList.getCount();
469 + }
470 +
471 + async findScheduleAll({
472 + droneId,
473 + startTime,
474 + terminateTime,
475 + pageNo,
476 + pageSize,
477 + }: {
478 + droneId?: number;
479 + startTime?: Date;
480 + terminateTime?: Date;
481 + pageNo?: number;
482 + pageSize?: number;
483 + }): Promise<ScheduleEntity[]> {
484 + const scheduleList = getRepository(ScheduleEntity)
485 + .createQueryBuilder('schedule')
486 + .select('schedule.id', 'id')
487 + .addSelect('drone.id', 'droneId')
488 + .addSelect('drone.modelName', 'modelName')
489 + .addSelect('schedule.startTime', 'startTime')
490 + .addSelect('schedule.terminateTime', 'terminateTime')
491 + .addSelect('schedule.startLatitude', 'startLatitude')
492 + .addSelect('schedule.terminateLatitude', 'terminateLatitude')
493 + .addSelect('schedule.startLongitude', 'startLongitude')
494 + .addSelect('schedule.terminateLongitude', 'terminateLongitude')
495 + .innerJoin(
496 + DroneScheduleMappingEntity,
497 + 'mapping',
498 + 'schedule.id = mapping.scheduleId',
499 + )
500 + .innerJoin(DroneEntity, 'drone', 'drone.id = mapping.droneId');
501 +
502 + if (droneId) {
503 + scheduleList.where('mapping.droneId = :droneId', { droneId: droneId });
504 + }
505 +
506 + if (startTime) {
507 + scheduleList.andWhere('schedule.startTime >= :startTime', {
508 + startTime: startTime,
509 + });
510 + }
511 + if (terminateTime) {
512 + scheduleList.andWhere('schedule.terminateTime <= :terminateTime', {
513 + terminateTime: terminateTime,
514 + });
515 + }
516 +
517 + scheduleList.orderBy('id', 'DESC');
518 +
519 + if (pageNo && pageSize) {
520 + scheduleList.offset((pageNo - 1) * pageSize).limit(pageSize);
521 + }
522 +
523 + return scheduleList.getRawMany();
524 + }
525 +
526 + async findScheduleTotalElement({
527 + droneId,
528 + startTime,
529 + terminateTime,
530 + }: {
531 + droneId?: number;
532 + startTime?: string;
533 + terminateTime?: string;
534 + }) {
535 + const scheduleList = getRepository(ScheduleEntity)
536 + .createQueryBuilder('schedule')
537 + .select('schedule.id', 'id')
538 + .addSelect('drone.id', 'droneId')
539 + .addSelect('drone.modelName', 'modelName')
540 + .addSelect('schedule.startTime', 'startTime')
541 + .addSelect('schedule.terminateTime', 'terminateTime')
542 + .addSelect('schedule.startLatitude', 'startLatitude')
543 + .addSelect('schedule.terminateLatitude', 'terminateLatitude')
544 + .addSelect('schedule.startLongitude', 'startLongitude')
545 + .addSelect('schedule.terminateLongitude', 'terminateLongitude')
546 + .innerJoin(
547 + DroneScheduleMappingEntity,
548 + 'mapping',
549 + 'schedule.id = mapping.scheduleId',
550 + )
551 + .innerJoin(DroneEntity, 'drone', 'drone.id = mapping.droneId');
552 +
553 + if (droneId) {
554 + scheduleList.where('mapping.droneId = :droneId', { droneId: droneId });
555 + }
556 +
557 + if (startTime) {
558 + scheduleList.andWhere('schedule.startTime >= :startTime', {
559 + startTime: startTime,
560 + });
561 + }
562 +
563 + if (terminateTime) {
564 + scheduleList.andWhere('schedule.terminateTime <= :terminateTime', {
565 + terminateTime: terminateTime,
566 + });
567 + }
568 +
569 + return scheduleList.getCount();
570 + }
571 +
572 + findCodeAll(): Promise<CodeEntity[]> {
573 + return this.codeRepository.find();
574 + }
575 +
576 + // 각 테이블별 detail 처리
577 + findMemberOne(id: number): Promise<MemberEntity> {
578 + return this.memberRepository.findOne({ id: id });
579 + }
580 +
581 + findDroneOne(id: number): Promise<DroneEntity> {
582 + return this.droneRepository.findOne({ id: id });
583 + }
584 +
585 + findLogOne(id: number): Promise<DroneLogEntity> {
586 + return this.dronelogRepository.findOne({ id: id });
587 + }
588 +
589 + findScheduleOne(id: number): Promise<ScheduleEntity> {
590 + const foundschedule = getRepository(ScheduleEntity)
591 + .createQueryBuilder('schedule')
592 + .select('schedule.id', 'id')
593 + .addSelect('mapping.droneId', 'droneId')
594 + .addSelect('schedule.startTime', 'startTime')
595 + .addSelect('schedule.terminateTime', 'terminateTime')
596 + .addSelect('schedule.startLatitude', 'startLatitude')
597 + .addSelect('schedule.terminateLatitude', 'terminateLatitude')
598 + .addSelect('schedule.startLongitude', 'startLongitude')
599 + .addSelect('schedule.terminateLongitude', 'terminateLongitude')
600 + .innerJoin(
601 + DroneScheduleMappingEntity,
602 + 'mapping',
603 + 'schedule.id = mapping.scheduleId',
604 + )
605 + .where('schedule.id = :id', { id: id });
606 +
607 + return foundschedule.getRawOne();
608 + }
609 +
610 + findCodeOne(id: number): Promise<CodeEntity> {
611 + return this.codeRepository.findOne({ id: id });
612 + }
613 +
614 + // 각 테이블별 insert
615 + async saveMember(memberEntity: MemberEntity): Promise<void> {
616 + await this.memberRepository.save(memberEntity);
617 + }
618 +
619 + async saveDrone(droneEntity: DroneEntity): Promise<void> {
620 + await this.droneRepository.save(droneEntity);
621 + }
622 +
623 + async saveDroneList(droneEntityList: DroneEntity[]): Promise<number> {
624 + const ret = await this.droneRepository.save(droneEntityList);
625 + return ret.length;
626 + }
627 +
628 + async updateDroneList(
629 + droneList: DroneApiDto.UpdateDroneDto[],
630 + ): Promise<number> {
631 + let affectedRows = 0;
632 + for (const drone of droneList) {
633 + const ret = await this.droneRepository
634 + .createQueryBuilder()
635 + .update('drone')
636 + .set({
637 + maker: drone.maker,
638 + usage: drone.usage,
639 + specification: drone.specification,
640 + weight: drone.weight,
641 + })
642 + .where('id = :id', { id: drone.id })
643 + .execute();
644 +
645 + affectedRows += ret.affected || 0;
646 + }
647 + return affectedRows;
648 + }
649 +
650 + async saveLog(droneLogEntity: DroneLogEntity): Promise<void> {
651 + await this.dronelogRepository.save(droneLogEntity);
652 + }
653 +
654 + async saveSchedule(ScheduleEntity: ScheduleEntity): Promise<void> {
655 + await this.scheduleRepository.save(ScheduleEntity);
656 + }
657 +
658 + async saveScheduleList(
659 + scheduleList: DroneApiDto.SaveSchduleDto[],
660 + ): Promise<number> {
661 + // 스케쥴 중복여부 확인
662 + for (const schedule of scheduleList) {
663 + const ret = await this.droneRepository
664 + .createQueryBuilder('drone')
665 + .select('drone.id', 'droneId')
666 + .addSelect('schedule.id', 'scheduleId')
667 + .innerJoin(
668 + DroneScheduleMappingEntity,
669 + 'mapping',
670 + 'drone.id = mapping.droneId',
671 + )
672 + .innerJoin(
673 + ScheduleEntity,
674 + 'schedule',
675 + 'schedule.id = mapping.scheduleId',
676 + )
677 + .where('drone.id = :id', { id: schedule.droneId })
678 + .andWhere('schedule.start_time <= :startTime', {
679 + startTime: schedule.startTime,
680 + })
681 + .andWhere('schedule.terminate_time >= :terminateTime', {
682 + terminateTime: schedule.terminateTime,
683 + })
684 + .getRawOne();
685 +
686 + if (ret) {
687 + // 시간이 중복된 스케쥴이 존제함.
688 + throw new HttpException(
689 + `해당 기간에 설정된 스케쥴이 이미 존제합니다. droneId: ${ret.droneId}, scheduleId: ${ret.scheduleId}`,
690 + 400,
691 + );
692 + }
693 + }
694 +
695 + let affectedRows = 0;
696 +
697 + // 스케쥴 생성
698 + for (const schedule of scheduleList) {
699 + const ret = await this.scheduleRepository.save({
700 + startTime: schedule.startTime,
701 + terminateTime: schedule.terminateTime,
702 + startLatitude: schedule.startLatitude,
703 + startLongitude: schedule.startLongitude,
704 + terminateLatitude: schedule.terminateLatitude,
705 + terminateLongitude: schedule.terminateLongitude,
706 + });
707 +
708 + const scheduleId = ret.id;
709 +
710 + await this.droneschedulemappingRepository.save({
711 + droneId: schedule.droneId,
712 + scheduleId,
713 + });
714 +
715 + affectedRows += 1;
716 + }
717 + return affectedRows;
718 + }
719 +
720 + async updateSchduleList(
721 + scheduleList: DroneApiDto.UpdateSchduleDto[],
722 + ): Promise<number> {
723 + // 스케쥴 중복여부 확인
724 + for (const schedule of scheduleList) {
725 + const ret = await this.droneschedulemappingRepository
726 + .createQueryBuilder('mapping')
727 + .select('drone.id', 'droneId')
728 + .addSelect('schedule.id', 'scheduleId')
729 + .innerJoin(DroneEntity, 'drone', 'drone.id = mapping.droneId')
730 + .innerJoin(
731 + DroneScheduleMappingEntity,
732 + 'mapping2',
733 + 'drone.id = mapping2.droneId',
734 + )
735 + .innerJoin(
736 + ScheduleEntity,
737 + 'schedule',
738 + 'schedule.id = mapping2.scheduleId',
739 + )
740 + .where('mapping.schedule_id = :id', {
741 + id: schedule.id,
742 + })
743 + .andWhere('mapping2.schedule_id != :id', {
744 + id: schedule.id,
745 + })
746 + .andWhere('schedule.start_time <= :startTime', {
747 + startTime: schedule.startTime,
748 + })
749 + .andWhere('schedule.terminate_time >= :terminateTime', {
750 + terminateTime: schedule.terminateTime,
751 + })
752 + .getRawOne();
753 +
754 + if (ret) {
755 + // 시간이 중복된 스케쥴이 존제함.
756 + throw new HttpException(
757 + `해당 기간에 설정된 스케쥴이 이미 존제합니다. droneId: ${ret.droneId}, scheduleId: ${ret.scheduleId}`,
758 + 400,
759 + );
760 + }
761 + }
762 +
763 + let affectedRows = 0;
764 +
765 + for (const schedule of scheduleList) {
766 + const ret = await this.scheduleRepository
767 + .createQueryBuilder()
768 + .update('schedule')
769 + .set({
770 + startTime: schedule.startTime,
771 + terminateTime: schedule.terminateTime,
772 + startLatitude: schedule.startLatitude,
773 + startLongitude: schedule.startLongitude,
774 + terminateLatitude: schedule.terminateLatitude,
775 + terminateLongitude: schedule.terminateLongitude,
776 + })
777 + .where('id = :id', { id: schedule.id })
778 + .execute();
779 +
780 + affectedRows += ret.affected || 0;
781 + }
782 + return affectedRows;
783 + }
784 +
785 + updateDroneMap(droneLogList: DroneLogDto[]) {
786 + droneLogList.forEach((droneLog) => {
787 + this.droneMap[`${droneLog.droneId}`] = droneLog;
788 + });
789 + }
790 +
791 + async saveCode(CodeEntity: CodeEntity): Promise<void> {
792 + await this.codeRepository.save(CodeEntity);
793 + }
794 +
795 + // 각 테이블별 Delete
796 + async deleteMember(id: number): Promise<void> {
797 + await this.memberRepository.delete({ id: id });
798 + }
799 +
800 + async deleteDrone(id: number): Promise<void> {
801 + await this.droneRepository.delete({ id: id });
802 + }
803 +
804 + async deleteLog(id: number): Promise<void> {
805 + await this.dronelogRepository.delete({ id: id });
806 + }
807 +
808 + async deleteSchedule(id: number): Promise<void> {
809 + await this.scheduleRepository.delete({ id: id });
810 + }
811 +
812 + async deleteCode(id: number): Promise<void> {
813 + await this.codeRepository.delete({ id: id });
814 + }
815 +
816 + async findFirstDroneLogList() {
817 + const droneList = await getRepository(DroneEntity)
818 + .createQueryBuilder('drone')
819 + .select('drone.id', 'droneId')
820 + .addSelect('schedule.id', 'scheduleId')
821 + .addSelect('log.latitude')
822 + .addSelect('log.longitude')
823 + .addSelect('log.vertical_speed', 'verticalSpeed')
824 + .addSelect('log.horizontal_speed', 'horizontalSpeed')
825 + .addSelect('log.above_sea_level', 'aboveSeaLevel')
826 + .addSelect('log.above_sea_level', 'aboveSeaLevel')
827 + .addSelect('log.above_ground_level', 'above_GroundLevel')
828 + .leftJoin(
829 + DroneScheduleMappingEntity,
830 + 'mapping',
831 + 'drone.id = mapping.droneId',
832 + )
833 + .leftJoin(
834 + ScheduleEntity,
835 + 'schedule',
836 + 'mapping.scheduleId = schedule.id AND NOW() between schedule.start_time AND schedule.terminate_time',
837 + )
838 + .leftJoin(DroneLogEntity, 'log', 'log.scheduleId = schedule.id')
839 + .where('log.id IS NULL')
840 + .orWhere(
841 + `log.id IN (
842 + SELECT MAX(log2.id) FROM drone_log log2 WHERE log2.schedule_id = schedule.id GROUP BY log2.schedule_id
843 + )`,
844 + )
845 + .orderBy('drone.id', 'DESC')
846 + .getRawMany();
847 + return droneList;
848 + }
849 +}
1 +import {
2 + IsArray,
3 + IsNotEmpty,
4 + IsOptional,
5 + IsNumber,
6 + IsString,
7 + ValidateNested,
8 + IsDateString,
9 +} from 'class-validator';
10 +import { Type } from 'class-transformer';
11 +import { DroneEntity } from 'src/entities/drone.entity';
12 +
13 +export namespace DroneApiDto {
14 + export class SaveDroneListDto {
15 + @IsArray()
16 + @ValidateNested({ each: true })
17 + @Type(() => DroneEntity)
18 + droneList: DroneEntity[];
19 + }
20 +
21 + export class UpdateDroneListDto {
22 + @IsArray()
23 + @ValidateNested({ each: true })
24 + @Type(() => UpdateDroneDto)
25 + droneList: UpdateDroneDto[];
26 + }
27 +
28 + export class UpdateDroneDto {
29 + @IsNotEmpty()
30 + @IsNumber()
31 + id: number;
32 +
33 + @IsString()
34 + @IsOptional()
35 + maker: string;
36 +
37 + @IsString()
38 + @IsOptional()
39 + usage: string;
40 +
41 + @IsNumber()
42 + @IsOptional()
43 + specification: number;
44 +
45 + @IsNumber()
46 + @IsOptional()
47 + weight: number;
48 + }
49 +
50 + export class SaveSchduleListDto {
51 + @IsArray()
52 + @ValidateNested({ each: true })
53 + @Type(() => SaveSchduleDto)
54 + schduleList: SaveSchduleDto[];
55 + }
56 +
57 + export class SaveSchduleDto {
58 + @IsNotEmpty()
59 + @IsNumber()
60 + droneId: number;
61 +
62 + @IsDateString()
63 + @IsNotEmpty()
64 + startTime: string;
65 +
66 + @IsDateString()
67 + @IsNotEmpty()
68 + terminateTime: string;
69 +
70 + @IsNumber()
71 + @IsNotEmpty()
72 + startLatitude: number;
73 +
74 + @IsNumber()
75 + @IsNotEmpty()
76 + startLongitude: number;
77 +
78 + @IsNumber()
79 + @IsNotEmpty()
80 + terminateLatitude: number;
81 +
82 + @IsNumber()
83 + @IsNotEmpty()
84 + terminateLongitude: number;
85 + }
86 +
87 + export class UpdateSchduleListDto {
88 + @IsArray()
89 + @ValidateNested({ each: true })
90 + @Type(() => UpdateSchduleDto)
91 + schduleList: UpdateSchduleDto[];
92 + }
93 +
94 + export class UpdateSchduleDto {
95 + @IsNotEmpty()
96 + @IsNumber()
97 + id: number;
98 +
99 + @IsDateString()
100 + @IsNotEmpty()
101 + startTime: string;
102 +
103 + @IsDateString()
104 + @IsNotEmpty()
105 + terminateTime: string;
106 +
107 + @IsNumber()
108 + @IsNotEmpty()
109 + startLatitude: number;
110 +
111 + @IsNumber()
112 + @IsNotEmpty()
113 + startLongitude: number;
114 +
115 + @IsNumber()
116 + @IsNotEmpty()
117 + terminateLatitude: number;
118 +
119 + @IsNumber()
120 + @IsNotEmpty()
121 + terminateLongitude: number;
122 + }
123 +
124 + export class SaveDroneLogListDto {
125 + @IsArray()
126 + @ValidateNested({ each: true })
127 + @Type(() => SaveDroneLogDto)
128 + droneLogList: SaveDroneLogDto[];
129 + }
130 +
131 + export class SaveDroneLogDto {
132 + @IsNotEmpty()
133 + @IsNumber()
134 + droneId: number;
135 +
136 + @IsNotEmpty()
137 + @IsNumber()
138 + scheduleId: number;
139 +
140 + @IsNotEmpty()
141 + @IsNumber()
142 + latitude: number;
143 +
144 + @IsNotEmpty()
145 + @IsNumber()
146 + longitude: number;
147 +
148 + @IsNotEmpty()
149 + @IsNumber()
150 + verticalSpeed: number;
151 +
152 + @IsNotEmpty()
153 + @IsNumber()
154 + horizontalSpeed: number;
155 +
156 + @IsNotEmpty()
157 + @IsNumber()
158 + aboveSeaLevel: number;
159 +
160 + @IsNotEmpty()
161 + @IsNumber()
162 + aboveGroundLevel: number;
163 + }
164 +}
1 +export class DroneDto {
2 + private _id: number;
3 + private _model_name: string;
4 + private _maker: string;
5 + private _usage: string;
6 + private _picture: string;
7 + private _specification: number;
8 + private _weight: number;
9 +
10 + constructor(
11 + id: number,
12 + model_name: string,
13 + maker: string,
14 + usage: string,
15 + picture: string,
16 + specification: number,
17 + weight: number,
18 + ) {
19 + this._id = id;
20 + this._model_name = model_name;
21 + this._maker = maker;
22 + this._usage = usage;
23 + this._picture = picture;
24 + this._specification = specification;
25 + this._weight = weight;
26 + }
27 +
28 + get id(): number {
29 + return this._id;
30 + }
31 + set id(value: number) {
32 + this._id = value;
33 + }
34 +
35 + get model_name(): string {
36 + return this._model_name;
37 + }
38 + set model_name(value: string) {
39 + this._model_name = value;
40 + }
41 +
42 + get maker(): string {
43 + return this._maker;
44 + }
45 + set maker(value: string) {
46 + this._maker = value;
47 + }
48 +
49 + get usage(): string {
50 + return this._usage;
51 + }
52 + set usage(value: string) {
53 + this._usage = value;
54 + }
55 +
56 + get picture(): string {
57 + return this._picture;
58 + }
59 + set picture(value: string) {
60 + this._picture = value;
61 + }
62 +
63 + get specification(): number {
64 + return this._specification;
65 + }
66 + set specification(value: number) {
67 + this._specification = value;
68 + }
69 +
70 + get weight(): number {
71 + return this._weight;
72 + }
73 + set weight(value: number) {
74 + this._weight = value;
75 + }
76 +}
1 +export class DroneLogDto {
2 + constructor(props) {
3 + // props.id = props.id ? parseInt(props.id) : props.id;
4 + props.droneId = props.droneId ? parseInt(props.droneId) : props.droneId;
5 + props.scheduleId = props.scheduleId
6 + ? parseInt(props.scheduleId)
7 + : props.scheduleId;
8 + Object.assign(this, props);
9 + }
10 + id: number;
11 + droneId: number;
12 + scheduleId: number;
13 + latitude: number;
14 + longitude: number;
15 + verticalSpeed: number;
16 + horizontalSpeed: number;
17 + aboveSeaLevel: number;
18 + aboveGroundLevel: number;
19 +}
1 +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm/index';
2 +
3 +@Entity({ name: 'code', schema: 'public' })
4 +export class CodeEntity {
5 + @PrimaryGeneratedColumn({ name: 'id' })
6 + id: number;
7 +
8 + @Column({ length: 20, name: 'code_group' })
9 + code_Group: string;
10 +
11 + @Column({ length: 20, name: 'code_group_name' })
12 + codeGroupName: string;
13 +
14 + @Column({ length: 20, name: 'code_text' })
15 + codeText: string;
16 +
17 + @Column({ length: 20, name: 'code_value' })
18 + codeValue: string;
19 +
20 + @Column({ length: 20, name: 'code_value_name' })
21 + codeValueName: string;
22 +}
1 +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm/index';
2 +
3 +@Entity({ name: 'drone', schema: 'public' })
4 +export class DroneEntity {
5 + @PrimaryGeneratedColumn({ name: 'id' })
6 + id: number;
7 +
8 + @Column({ length: 20, name: 'model_name' })
9 + modelName: string;
10 +
11 + @Column({ length: 20 })
12 + maker: string;
13 +
14 + @Column({ length: 20 })
15 + usage: string;
16 +
17 + @Column({ length: 20, name: 'usagename' })
18 + usageName: string;
19 +
20 + @Column({ length: 20 })
21 + picture: string;
22 +
23 + @Column()
24 + specification: number;
25 +
26 + @Column()
27 + weight: number;
28 +}
1 +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm/index';
2 +
3 +@Entity({ name: 'drone_log', schema: 'public' })
4 +export class DroneLogEntity {
5 + @PrimaryGeneratedColumn({ name: 'id' })
6 + id: number;
7 +
8 + @Column({ name: 'drone_id' })
9 + droneId: number;
10 +
11 + @Column({ name: 'schedule_id' })
12 + scheduleId: number;
13 +
14 + @Column()
15 + latitude: number;
16 +
17 + @Column()
18 + longitude: number;
19 +
20 + @Column({ name: 'vertical_speed' })
21 + verticalSpeed: number;
22 +
23 + @Column({ name: 'horizontal_speed' })
24 + horizontalSpeed: number;
25 +
26 + @Column({ name: 'above_sea_level' })
27 + aboveSeaLevel: number;
28 +
29 + @Column({ name: 'above_ground_level' })
30 + aboveGroundLevel: number;
31 +
32 + @Column({ type: 'timestamp', name: 'created_at' })
33 + createdAt: Date;
34 +
35 + constructor(props) {
36 + Object.assign(this, props);
37 + }
38 +}
1 +import { Entity, PrimaryGeneratedColumn } from 'typeorm/index';
2 +
3 +@Entity({ name: 'drone_schedule_mapping', schema: 'public' })
4 +export class DroneScheduleMappingEntity {
5 + @PrimaryGeneratedColumn({ name: 'drone_id' })
6 + droneId: number;
7 +
8 + @PrimaryGeneratedColumn({ name: 'schedule_id' })
9 + scheduleId: number;
10 +}
1 +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm/index';
2 +
3 +@Entity({ name: 'member', schema: 'public' })
4 +export class MemberEntity {
5 + @PrimaryGeneratedColumn({ name: 'id' })
6 + id: number;
7 +
8 + @Column({ length: 15 })
9 + name: string;
10 +
11 + @Column({ length: 20, name:'tel_number' })
12 + telNumber: string;
13 +
14 + @Column({ length: 20 })
15 + affiliation: string;
16 +}
1 +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm/index';
2 +
3 +@Entity({ name: 'schedule', schema: 'public' })
4 +export class ScheduleEntity {
5 + @PrimaryGeneratedColumn({ name: 'id' })
6 + id: number;
7 +
8 + @Column({ type: 'timestamp', name: 'start_time' })
9 + startTime: Date;
10 +
11 + @Column({ type: 'timestamp', name: 'terminate_time' })
12 + terminateTime: Date;
13 +
14 + @Column({ name: 'start_latitude' })
15 + startLatitude: number;
16 +
17 + @Column({ name: 'start_longitude' })
18 + startLongitude: number;
19 +
20 + @Column({ name: 'terminate_latitude' })
21 + terminateLatitude: number;
22 +
23 + @Column({ name: 'terminate_longitude' })
24 + terminateLongitude: number;
25 +}
1 +import { NestFactory } from '@nestjs/core';
2 +import { WsAdapter } from '@nestjs/platform-ws';
3 +import { AppModule } from './app.module';
4 +
5 +async function bootstrap() {
6 + const app = await NestFactory.create(AppModule);
7 + app.useWebSocketAdapter(new WsAdapter(app));
8 + await app.listen(20205);
9 +}
10 +bootstrap();
1 +import { Test, TestingModule } from '@nestjs/testing';
2 +import { INestApplication } from '@nestjs/common';
3 +import * as request from 'supertest';
4 +import { AppModule } from './../src/app.module';
5 +
6 +describe('AppController (e2e)', () => {
7 + let app: INestApplication;
8 +
9 + beforeEach(async () => {
10 + const moduleFixture: TestingModule = await Test.createTestingModule({
11 + imports: [AppModule],
12 + }).compile();
13 +
14 + app = moduleFixture.createNestApplication();
15 + await app.init();
16 + });
17 +
18 + it('/ (GET)', () => {
19 + return request(app.getHttpServer())
20 + .get('/')
21 + .expect(200)
22 + .expect('Hello World!');
23 + });
24 +});
1 +{
2 + "moduleFileExtensions": ["js", "json", "ts"],
3 + "rootDir": ".",
4 + "testEnvironment": "node",
5 + "testRegex": ".e2e-spec.ts$",
6 + "transform": {
7 + "^.+\\.(t|j)s$": "ts-jest"
8 + }
9 +}
1 +{
2 + "extends": "./tsconfig.json",
3 + "exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
4 +}
1 +{
2 + "compilerOptions": {
3 + "module": "commonjs",
4 + "declaration": true,
5 + "removeComments": true,
6 + "emitDecoratorMetadata": true,
7 + "experimentalDecorators": true,
8 + "allowSyntheticDefaultImports": true,
9 + "target": "es2017",
10 + "sourceMap": true,
11 + "outDir": "./dist",
12 + "baseUrl": "./",
13 + "incremental": true
14 + }
15 +}
1 +# editorconfig.org
2 +root = true
3 +
4 +[*]
5 +indent_style = space
6 +indent_size = 2
7 +end_of_line = lf
8 +charset = utf-8
9 +trim_trailing_whitespace = true
10 +insert_final_newline = true
11 +
12 +[*.md]
13 +trim_trailing_whitespace = false
1 +BASE_I18N_LOCALE=ko
2 +BASE_I18N_FALLBACK_LOCALE=ko
3 +BASE_API_URL=http://14.33.35.148:20205
4 +BASE_APP_URL=http://localhost:3000
1 +BASE_API_URL=http://14.33.35.148:20205
2 +BASE_APP_URL=http://14.33.35.148:20201
3 +
1 +node_modules
2 +lib/api-client
3 +**/*.ts
1 +module.exports = {
2 + root: true,
3 + env: {
4 + browser: true,
5 + node: true,
6 + },
7 + parserOptions: {
8 + parser: 'babel-eslint',
9 + },
10 + extends: [
11 + 'plugin:vue/essential',
12 + '@vue/airbnb',
13 + ],
14 + plugins: [
15 + ],
16 + // add your custom rules here
17 + rules: {
18 + 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
19 + 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
20 + 'no-unused-vars': 'warn',
21 + 'comma-dangle': ['error', 'always-multiline'],
22 + 'linebreak-style': 0,
23 + 'import/no-extraneous-dependencies': 0,
24 + 'no-shadow': 0,
25 + 'import/prefer-default-export': 0,
26 + 'max-len': ['warn', { code: 200 }],
27 + 'import/extensions': ['error', 'always', {
28 + js: 'never',
29 + jsx: 'never',
30 + vue: 'never',
31 + }],
32 + indent: [2, 2],
33 + },
34 + settings: {
35 + 'import/extensions': ['.js', '.jsx', '.vue'],
36 + 'import/resolver': {
37 + alias: {
38 + map: [
39 + ['@', './src'],
40 + ],
41 + extensions: ['.js', '.vue', '.jsx'],
42 + },
43 + node: {
44 + extensions: ['.js', '.vue', '.jsx'],
45 + },
46 + },
47 + },
48 +};
1 +# Created by .ignore support plugin (hsz.mobi)
2 +### Node template
3 +# Logs
4 +/logs
5 +*.log
6 +npm-debug.log*
7 +yarn-debug.log*
8 +yarn-error.log*
9 +
10 +# Runtime data
11 +pids
12 +*.pid
13 +*.seed
14 +*.pid.lock
15 +
16 +# Directory for instrumented libs generated by jscoverage/JSCover
17 +lib-cov
18 +
19 +# Coverage directory used by tools like istanbul
20 +coverage
21 +
22 +# nyc test coverage
23 +.nyc_output
24 +
25 +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
26 +.grunt
27 +
28 +# Bower dependency directory (https://bower.io/)
29 +bower_components
30 +
31 +# node-waf configuration
32 +.lock-wscript
33 +
34 +# Compiled binary addons (https://nodejs.org/api/addons.html)
35 +build/Release
36 +
37 +# Dependency directories
38 +node_modules/
39 +jspm_packages/
40 +
41 +# TypeScript v1 declaration files
42 +typings/
43 +
44 +# Optional npm cache directory
45 +.npm
46 +
47 +# Optional eslint cache
48 +.eslintcache
49 +
50 +# Optional REPL history
51 +.node_repl_history
52 +
53 +# Output of 'npm pack'
54 +*.tgz
55 +
56 +# Yarn Integrity file
57 +.yarn-integrity
58 +
59 +# parcel-bundler cache (https://parceljs.org/)
60 +.cache
61 +
62 +# next.js build output
63 +.next
64 +
65 +# nuxt.js build output
66 +.nuxt
67 +
68 +# Nuxt generate
69 +dist
70 +
71 +# vuepress build output
72 +.vuepress/dist
73 +
74 +# Serverless directories
75 +.serverless
76 +
77 +# IDE / Editor
78 +.idea
79 +
80 +# Service worker
81 +sw.*
82 +
83 +# macOS
84 +.DS_Store
85 +
86 +# Vim swap files
87 +*.swp
88 +.DS_Store
1 +stages:
2 + - others
3 + - build
4 + - deploy
5 +
6 +.build:
7 + cache:
8 + paths:
9 + - node_modules/
10 +
11 +.deploy:
12 + variables:
13 + GIT_STRATEGY: none
14 +
15 +dev_build:
16 + stage: build
17 + extends: .build
18 + script:
19 + - yarn install
20 + - yarn build
21 + only:
22 + - develop
23 + tags:
24 + - front
25 +
26 +dev_deploy:
27 + stage: deploy
28 + extends: .deploy
29 + script:
30 + - pm2 start --exp-backoff-restart-delay=100
31 + only:
32 + - develop
33 + tags:
34 + - front
35 +#(DEV) mockserver:
36 +# stage: deploy
37 +# extends: .deploy
38 +# image: node:15
39 +# script: yarn serve
40 +# only:
41 +# - develop
42 +# artifacts:
43 +# paths:
44 +# - ./
45 +# tags:
46 +# - ws-server
47 +#
48 +#(DEV) websocket:
49 +# stage: deploy
50 +# extends: .deploy
51 +# image: docker:latest
52 +# script: yarn ws
53 +# only:
54 +# - develop
55 +# artifacts:
56 +# paths:
57 +# - ./
58 +# tags:
59 +# - api-server
1 +{
2 + "tabWidth": 2,
3 + "semi": true,
4 + "singleQuote": true,
5 + "trailingComma": "es5",
6 + "bracketSpacing": false,
7 + "arrowParens": "always"
8 +}
1 +# drone-web-nuxt
2 +
3 +## Build Setup
4 +
5 +```bash
6 +# install dependencies
7 +$ yarn install
8 +
9 +# serve with hot reload at localhost:3000
10 +$ yarn dev
11 +
12 +# build for production and launch server
13 +$ yarn build
14 +$ yarn start
15 +
16 +# generate static project
17 +$ yarn generate
18 +```
19 +
20 +For detailed explanation on how things work, check out [Nuxt.js docs](https://nuxtjs.org).
1 +#!/bin/bash
2 +
3 +echo "Running server in the background"
4 +sudo systemctl restart hello-react
1 +module.exports = {
2 + apps: [
3 + {
4 + name: 'drone-front',
5 + // package.json에 정의된 npm run start를 실행하게 해서 PM2로 관리하게 한다.
6 + script: 'yarn',
7 + args: 'run start',
8 + instances: '1',
9 + max_memory_restart: '1G',
10 + error_file: 'err.log',
11 + out_file: 'out.log',
12 + log_file: 'combined.log',
13 + },
14 + ],
15 +};
1 +[Unit]
2 +Description=Drone front service
3 +After=network.target
4 +StartLimitIntervalSec=0
5 +
6 +[Service]
7 +Type=simple
8 +Restart=always
9 +RestartSec=1
10 +User=gitlab-runner
11 +ExecStart=/home/gitlab-runner/builds/4hhEfxWU/0/khu-oz-wizard/drone-monitoring-web-ui yarn start
12 +
13 +[Install]
14 +WantedBy=multi-user.target
This diff could not be displayed because it is too large.
1 +map $sent_http_content_type $expires {
2 + "text/html" epoch;
3 + "text/html; charset=utf-8" epoch;
4 + default off;
5 +}
6 +
7 +server {
8 + listen 20205;
9 + server_name localhost;
10 +
11 + gzip on;
12 + gzip_types text/plain application/xml text/css application/javascript;
13 + gzip_min_length 1000;
14 +
15 + location / {
16 + expires $expires;
17 +
18 + proxy_redirect off;
19 + proxy_set_header Host $host;
20 + proxy_set_header X-Real-IP $remote_addr;
21 + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
22 + proxy_set_header X-Forwarded-Proto $scheme;
23 + proxy_read_timeout 1m;
24 + proxy_connect_timeout 1m;
25 + proxy_pass http://127.0.0.1:3000;
26 + }
27 +}
1 +/* eslint-disable no-unused-vars */
2 +import api from './nuxtConfig/api';
3 +import build from './nuxtConfig/build';
4 +import theme from './nuxtConfig/theme';
5 +import nuxtConfigModule from './nuxtConfig/module';
6 +import io from './nuxtConfig/ioConfig';
7 +import extendRouter from './nuxtConfig/extendRouter';
8 +
9 +// 설정 내용이 짧은 것 및 구조화 하기 애매한 것은 별도 파일로 관리하지 않음.
10 +export default {
11 + // Global page headers: https://go.nuxtjs.dev/config-head
12 + head: {
13 + title: 'drone-web-nuxt',
14 + htmlAttrs: {
15 + lang: 'en',
16 + },
17 + meta: [
18 + { charset: 'utf-8' },
19 + { name: 'viewport', content: 'width=device-width, initial-scale=1' },
20 + { hid: 'description', name: 'description', content: '' },
21 + ],
22 + link: [
23 + { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
24 + ],
25 + },
26 +
27 + // Auto import components: https://go.nuxtjs.dev/config-components
28 + components: false,
29 + // source Directory
30 + srcDir: 'src/',
31 +
32 + /* middleware */
33 + serverMiddleware: [
34 + './serverMiddleWare/index',
35 + ],
36 + router: {
37 + // router middleware
38 + middleware: 'router',
39 + // router extend
40 + // extendRoutes: extendRouter,
41 + },
42 +
43 + // module, plugin, alias, robots
44 + ...nuxtConfigModule,
45 + // axios, proxy, auth
46 + ...api,
47 + // env, runtimeConfig, build
48 + ...build,
49 + // loading, transition, css
50 + ...theme,
51 +
52 + // vue Global Config
53 + vue: {
54 + config: {
55 + productionTip: true,
56 + devtools: process.env.NODE_ENV === 'development',
57 + // silent: process.env.NODE_ENV !== 'development',
58 + // performance: process.env.NODE_ENV === 'development',
59 + },
60 + },
61 +
62 + // robots Setting
63 + robots: {
64 + UserAgent: '*',
65 + Disallow: '/',
66 + },
67 + // socket io Setting
68 + io,
69 +};
1 +import { version } from '../package.json';
2 +
3 +/**
4 + * api 관련된 nuxt 옵션을 정리합니다.
5 + * 해당 옵션은 아래와 같습니다.
6 + * auth, axios, proxy,
7 + */
8 +export default {
9 + // Axios module configuration: https://go.nuxtjs.dev/config-axios
10 + axios: {
11 + proxy: true,
12 + retry: { retries: 3 },
13 + // baseUrl: 'http://localhost:5555',
14 + headers: {
15 + common: {
16 + Accept: 'application/json, text/plain, */*',
17 + AppVersion: version,
18 + },
19 + delete: {},
20 + get: {},
21 + head: {},
22 + post: {},
23 + put: {},
24 + patch: {},
25 + },
26 + },
27 + proxy: {
28 + '/api': {
29 + target: process.env.BASE_API_URL || 'http://14.33.35.148:20205',
30 + pathRewrite: {
31 + '^/api': '',
32 + },
33 + changeOrigin: true,
34 + },
35 + },
36 + auth: {
37 + // Options
38 + },
39 +};
1 +/**
2 + * 빌드에 관련된 nuxt 옵션을 정리합니다.
3 + * 해당 옵션은 아래와 같습니다.
4 + * env, build,
5 + */
6 +export default {
7 + // modern property https://ko.nuxtjs.org/docs/2.x/configuration-glossary/configuration-modern
8 + modern: false,
9 + /* env Setting */
10 + env: {
11 + BASE_API_URL: process.env.BASE_API_URL,
12 + BASE_APP_URL: process.env.BASE_APP_URL,
13 + BASE_I18N_LOCALE: process.env.BASE_I18N_LOCALE,
14 + BASE_I18N_FALLBACK_LOCALE: process.env.BASE_I18N_FALLBACK_LOCALE,
15 + },
16 + // public nuxt.context config variables
17 + publicRuntimeConfig: {
18 + BASE_API_URL: process.env.BASE_API_URL,
19 + BASE_APP_URL: process.env.BASE_APP_URL,
20 + BASE_I18N_LOCALE: process.env.BASE_I18N_LOCALE,
21 + BASE_I18N_FALLBACK_LOCALE: process.env.BASE_I18N_FALLBACK_LOCALE,
22 + },
23 + // private nuxt.context config variables
24 + privateRuntimeConfig: {
25 +
26 + },
27 +
28 + // Build Configuration: https://go.nuxtjs.dev/config-build
29 + build: {
30 + loaders: {
31 + vue: {
32 + transformAssetUrls: {
33 + 'vl-style-icon': 'src',
34 + },
35 + },
36 + // for Antdv CustomTheme Setting
37 + less: {
38 + lessOptions: {
39 + javascriptEnabled: true,
40 + math: 'always',
41 + },
42 + },
43 + },
44 + devtool: true,
45 + analyze: true,
46 + },
47 + // Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules
48 + buildModules: [
49 + // https://go.nuxtjs.dev/eslint
50 + '@nuxt/image',
51 + ],
52 + image: {
53 + staticFilename: '[publicPath]/images/[name]-[hash][ext]',
54 + presets: {
55 + avatar: {
56 + modifiers: {
57 + format: 'jpg',
58 + width: 50,
59 + height: 50,
60 + },
61 + },
62 + },
63 + },
64 +};
1 +export default function extendRoutes(routes, resolve) {
2 + routes.push({
3 + name: '404Page',
4 + path: '*',
5 + redirect: '/auth/404',
6 + component: resolve(__dirname, '../src/pages/auth/404.vue'),
7 + });
8 +}
1 +export default {
2 + sockets: [
3 + {
4 + name: 'main',
5 + url: 'http://localhost:8888',
6 + default: true,
7 + },
8 + ],
9 +};
1 +/**
2 + * 모듈에 관련된 nuxt 옵션을 정리합니다.
3 + * 해당 옵션은 아래와 같습니다.
4 + * module, plugin, alias
5 + */
6 +import { resolve } from 'path';
7 +
8 +export default {
9 +// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
10 + plugins: [
11 + resolve(__dirname, '../src/plugins/ApiClient/index'),
12 + resolve(__dirname, '../src/plugins/antDesign'),
13 + resolve(__dirname, '../src/plugins/Dayjs/index'),
14 + { src: '@/plugins/client-only.client.js' },
15 + { src: '@/plugins/globalMixins' },
16 + { src: '@/plugins/vuelayers.js', ssr: false },
17 + ],
18 +
19 + // Modules: https://go.nuxtjs.dev/config-modules
20 + modules: [
21 + // https://go.nuxtjs.dev/axios
22 + '@nuxtjs/axios',
23 + '@nuxtjs/style-resources',
24 + '@nuxtjs/auth-next',
25 + '@nuxtjs/sitemap',
26 + '@nuxtjs/robots',
27 + 'nuxt-socket-io',
28 + 'nuxt-leaflet',
29 + resolve(__dirname, '../src/modules/vuelayers.js'),
30 + ],
31 + // alias
32 + alias: {
33 + '@': resolve(__dirname, '../src/'),
34 + images: resolve(__dirname, '../src/assets/images'),
35 + styles: resolve(__dirname, '../src/assets/styles'),
36 + },
37 +};
1 +/**
2 + * 테마에 관련된 nuxt 옵션을 정리합니다.
3 + * 해당 옵션은 아래와 같습니다.
4 + * trainsition, css, loading
5 + */
6 +import { resolve } from 'path';
7 +
8 +export default {
9 +// Theme Animation
10 + loading: {
11 + color: '#1890ff',
12 + height: '4px',
13 + },
14 + layoutTransition: {
15 + name: 'default-layout',
16 + mode: 'out-in',
17 + },
18 + pageTransition: {
19 + name: 'default-page',
20 + mode: 'out-in',
21 + },
22 + // Global CSS: https://go.nuxtjs.dev/config-css
23 + css: [
24 + resolve(__dirname, '../src/assets/styles/less/index'),
25 + resolve(__dirname, '../src/assets/styles/scss/index'),
26 + ],
27 +};
1 +{
2 + "name": "drone-web-nuxt",
3 + "version": "1.0.3",
4 + "private": true,
5 + "scripts": {
6 + "dev": "nuxt",
7 + "analyze-dev": "nuxt --analyze",
8 + "build": "nuxt build",
9 + "start": "nuxt start",
10 + "build-mo": "nuxt build --modern=server",
11 + "analyze-build": "nuxt build --analyze",
12 + "start-mo": "nuxt start --modern=server",
13 + "generate": "nuxt generate",
14 + "lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .",
15 + "lint": "yarn lint:js",
16 + "serve": "json-server --watch lib/db.json --port 5555",
17 + "socket": "node ./serverMiddleWare/socket.js",
18 + "ws": "node ./serverMiddleWare/ws.js",
19 + "wsm": "node ./serverMiddleWare/multWs.js",
20 + "wsmp": "node ./serverMiddleWare/multPWs.js",
21 + "k6": "k6 run ./serverMiddleWare/k6.js",
22 + "k6m": "k6 run ./serverMiddleWare/k6mult.js"
23 + },
24 + "dependencies": {
25 + "@nuxtjs/auth-next": "5.0.0-1613647907.37b1156",
26 + "@nuxtjs/axios": "^5.13.1",
27 + "@nuxtjs/robots": "^2.5.0",
28 + "@nuxtjs/sitemap": "^2.4.0",
29 + "ant-design-vue": "^1.7.2",
30 + "core-js": "^3.8.3",
31 + "cors": "^2.8.5",
32 + "dayjs": "^1.10.4",
33 + "express": "^4.17.1",
34 + "highcharts": "^9.0.1",
35 + "highcharts-vue": "^1.3.5",
36 + "nuxt": "^2.14.12",
37 + "nuxt-leaflet": "^0.0.25",
38 + "nuxt-socket-io": "^1.1.14",
39 + "socket.io": "^4.0.0",
40 + "store2": "^2.12.0",
41 + "vuelayers": "^0.11.35",
42 + "vuex": "^3.6.2",
43 + "ws": "^7.4.4"
44 + },
45 + "devDependencies": {
46 + "@nuxt/image": "^0.4.13",
47 + "@nuxtjs/eslint-config": "^5.0.0",
48 + "@nuxtjs/eslint-module": "^3.0.2",
49 + "@nuxtjs/style-resources": "^1.0.0",
50 + "@vue/cli-service": "^4.5.11",
51 + "@vue/eslint-config-airbnb": "^5.3.0",
52 + "babel-eslint": "^10.1.0",
53 + "eslint": "^7.18.0",
54 + "eslint-import-resolver-alias": "^1.1.2",
55 + "eslint-plugin-nuxt": "^2.0.0",
56 + "eslint-plugin-vue": "^7.5.0",
57 + "fibers": "^5.0.0",
58 + "json-server": "^0.16.3",
59 + "less": "^4.1.1",
60 + "less-loader": "7",
61 + "sass": "^1.32.8",
62 + "sass-loader": "10"
63 + }
64 +}
1 +// <project root>/api/index.js
2 +// const express = require('express');
3 +const app = require('express')();
4 +const http = require('http').createServer(app);
5 +const io = require('socket.io')(http);
6 +
7 +// 실제로는 /api 라우트를 처리하는 메소드가 된다.
8 +app.get('/', (req, res) => {
9 + console.log('hi');
10 + io.emit('connection');
11 + res.send('API root');
12 +});
13 +
14 +io.of('/analytics').on('connect', (socket) => {
15 + console.log('클라이언트 접속');
16 +
17 + socket.on('disconnect', () => {
18 + console.log('클라이언트 접속 종료');
19 + });
20 + setInterval(() => {
21 + socket.emit('message', '메세지');
22 + }, 3000);
23 +});
24 +
25 +// 모듈로 사용할 수 있도록 export
26 +// 앱의 /api/* 라우트로 접근하는 모든 요청은 모두 app 인스턴스에게 전달된다.
27 +module.exports = {
28 + path: '/socket',
29 + handler: app,
30 +};
1 +import ws from 'k6/ws';
2 +import { check } from 'k6';
3 +
4 +export const options = {
5 + // vus: 100,
6 + // duration: '30s',
7 + stages: [
8 + { duration: '60s', target: 10 },
9 + ],
10 +};
11 +
12 +export default function () {
13 + const url = 'ws://localhost:8080';
14 + const params = { tags: { my_tag: 'hello' } };
15 +
16 + const res = ws.connect(url, params, (socket) => {
17 + socket.on('open', () => console.log('connected'));
18 + socket.on('message', (data) => console.log('Message received: '));
19 + socket.on('close', () => console.log('disconnected'));
20 + socket.setTimeout(() => {
21 + console.log('60 seconds passed, closing the socket');
22 + socket.close();
23 + }, 30000);
24 + });
25 +
26 + check(res, { 'status is 101': (r) => r && r.status === 101 });
27 +}
1 +import ws from 'k6/ws';
2 +import { check } from 'k6';
3 +
4 +export const options = {
5 + // vus: 100,
6 + // duration: '30s',
7 + stages: [
8 + { duration: '60s', target: 10 },
9 + // { duration: '10s', target: 30 },
10 + // { duration: '20s', target: 50 },
11 + ],
12 +};
13 +
14 +export default function () {
15 + const url = 'ws://localhost:8080';
16 + const params = { tags: { my_tag: 'hello' } };
17 + const res = {};
18 +
19 + for (let i = 0; i < 10; i += 1) {
20 + res[i] = ws.connect(`${url}/${i + 1}`, params, (socket) => {
21 + socket.on('open', () => console.log('connected', i));
22 + socket.on('message', (data) => console.log('Message received: ', i));
23 + socket.on('close', () => console.log('disconnected'));
24 + socket.setTimeout(() => {
25 + console.log('60 seconds passed, closing the socket');
26 + socket.close();
27 + }, 60000);
28 + });
29 + }
30 + console.log(res);
31 + check(res, {
32 + 'status1 is 101': (r) => r && r[0].status === 101,
33 + 'status2 is 101': (r) => r && r[1].status === 101,
34 + 'status3 is 101': (r) => r && r[2].status === 101,
35 + 'status4 is 101': (r) => r && r[3].status === 101,
36 + });
37 +}
38 +//
39 +// const res1 = ws.connect(`${url}/${1}`, params, (socket) => {
40 +// socket.on('open', () => console.log('connected'));
41 +// socket.on('message', (data) => console.log('Message received: 1'));
42 +// socket.on('close', () => console.log('disconnected'));
43 +// socket.setTimeout(() => {
44 +// console.log('60 seconds passed, closing the socket');
45 +// socket.close();
46 +// }, 60000);
47 +// });
48 +//
49 +// const res2 = ws.connect(`${url}/${2}`, params, (socket) => {
50 +// socket.on('open', () => console.log('connected'));
51 +// socket.on('message', (data) => console.log('Message received: 2'));
52 +// socket.on('close', () => console.log('disconnected'));
53 +// socket.setTimeout(() => {
54 +// console.log('60 seconds passed, closing the socket');
55 +// socket.close();
56 +// }, 60000);
57 +// });
58 +//
59 +// const res3 = ws.connect(`${url}/${3}`, params, (socket) => {
60 +// socket.on('open', () => console.log('connected'));
61 +// socket.on('message', (data) => console.log('Message received: 3'));
62 +// socket.on('close', () => console.log('disconnected'));
63 +// socket.setTimeout(() => {
64 +// console.log('60 seconds passed, closing the socket');
65 +// socket.close();
66 +// }, 60000);
67 +// });
68 +//
69 +// const res4 = ws.connect(`${url}/${4}`, params, (socket) => {
70 +// socket.on('open', () => console.log('connected'));
71 +// socket.on('message', (data) => console.log('Message received: 4'));
72 +// socket.on('close', () => console.log('disconnected'));
73 +// socket.setTimeout(() => {
74 +// console.log('60 seconds passed, closing the socket');
75 +// socket.close();
76 +// }, 60000);
77 +// });
78 +//
79 +// const res5 = ws.connect(`${url}/${5}`, params, (socket) => {
80 +// socket.on('open', () => console.log('connected'));
81 +// socket.on('message', (data) => console.log('Message received: 5'));
82 +// socket.on('close', () => console.log('disconnected'));
83 +// socket.setTimeout(() => {
84 +// console.log('60 seconds passed, closing the socket');
85 +// socket.close();
86 +// }, 60000);
87 +// });
88 +//
89 +// const res6 = ws.connect(`${url}/${6}`, params, (socket) => {
90 +// socket.on('open', () => console.log('connected'));
91 +// socket.on('message', (data) => console.log('Message received: 6'));
92 +// socket.on('close', () => console.log('disconnected'));
93 +// socket.setTimeout(() => {
94 +// console.log('60 seconds passed, closing the socket');
95 +// socket.close();
96 +// }, 60000);
97 +// });
98 +//
99 +// const res7 = ws.connect(`${url}/${7}`, params, (socket) => {
100 +// socket.on('open', () => console.log('connected'));
101 +// socket.on('message', (data) => console.log('Message received: 7'));
102 +// socket.on('close', () => console.log('disconnected'));
103 +// socket.setTimeout(() => {
104 +// console.log('60 seconds passed, closing the socket');
105 +// socket.close();
106 +// }, 60000);
107 +// });
108 +//
109 +// const res8 = ws.connect(`${url}/${8}`, params, (socket) => {
110 +// socket.on('open', () => console.log('connected'));
111 +// socket.on('message', (data) => console.log('Message received: 8'));
112 +// socket.on('close', () => console.log('disconnected'));
113 +// socket.setTimeout(() => {
114 +// console.log('60 seconds passed, closing the socket');
115 +// socket.close();
116 +// }, 60000);
117 +// });
118 +//
119 +// const res9 = ws.connect(`${url}/${9}`, params, (socket) => {
120 +// socket.on('open', () => console.log('connected'));
121 +// socket.on('message', (data) => console.log('Message received: 9'));
122 +// socket.on('close', () => console.log('disconnected'));
123 +// socket.setTimeout(() => {
124 +// console.log('60 seconds passed, closing the socket');
125 +// socket.close();
126 +// }, 60000);
127 +// });
128 +//
129 +// const res10 = ws.connect(`${url}/${10}`, params, (socket) => {
130 +// socket.on('open', () => console.log('connected'));
131 +// socket.on('message', (data) => console.log('Message received: 10'));
132 +// socket.on('close', () => console.log('disconnected'));
133 +// socket.setTimeout(() => {
134 +// console.log('60 seconds passed, closing the socket');
135 +// socket.close();
136 +// }, 60000);
137 +// });
138 +// check([res1, res2, res3, res10], { 'status is 101': (r) => r && r.status === 101 });
1 +/* eslint-disable prefer-arrow-callback,consistent-return,no-param-reassign,no-mixed-operators,no-use-before-define */
2 +const http = require('http');
3 +const WebSocket = require('ws');
4 +const url = require('url');
5 +
6 +const server = http.createServer();
7 +const wss = {};
8 +
9 +const dataNum = process.argv[2];
10 +const wsServerCnt = process.argv[3];
11 +const port = process.argv[4];
12 +const dataPerWsServer = dataNum / wsServerCnt;
13 +
14 +console.log(process.argv);
15 +
16 +let pingInterval = null;
17 +let sendInterval = null;
18 +
19 +const logData = [];
20 +for (let i = 0; i < dataPerWsServer; i += 1) {
21 + logData.push({
22 + latitude: getRandomArbitrary(37200000000000, 37300000000000) / 1000000000000,
23 + longitude: getRandomArbitrary(126900000000000, 127100000000000) / 1000000000000,
24 + id: i,
25 + });
26 +}
27 +
28 +function getRandomArbitrary(min, max) {
29 + return parseInt((Math.random() * (max - min) + min), 10);
30 +}
31 +function circleMove(x, y, radius, max, circleStep) {
32 + return {
33 + latitude: x + radius * Math.cos(2 * Math.PI * circleStep / max),
34 + longitude: y + radius * Math.sin(2 * Math.PI * circleStep / max),
35 + };
36 +}
37 +function makeCoordData(log, circleStep) {
38 + if (circleStep == null) {
39 + circleStep = 0;
40 + }
41 + if (circleStep === 3600) {
42 + circleStep = 0;
43 + } else circleStep += 1;
44 +
45 + // console.log('step', circleStep);
46 +
47 + return Array.from(
48 + { length: dataPerWsServer },
49 + (v, i) => ({
50 + id: i,
51 + ...circleMove(log[i].latitude, log[i].longitude, 0.05, 3600, circleStep),
52 + time: new Date(),
53 + }),
54 + );
55 +}
56 +function heartbeat() {
57 + this.isAlive = true;
58 + console.log('client Heartbeat');
59 +}
60 +function noop() {}
61 +
62 +for (let i = 0; i < wsServerCnt; i += 1) {
63 + wss[i + 1] = new WebSocket.Server({ noServer: true });
64 +}
65 +
66 +Object.entries(wss).forEach(([key, wss]) => {
67 + wss.on('connection', (ws) => {
68 + console.log('connected', key);
69 + ws.isAlive = true;
70 + ws.on('pong', heartbeat);
71 + ws.on('message', function incoming(message) {
72 + console.log('received: %s', message);
73 + });
74 + let circleStep = 0;
75 + sendInterval = setInterval(() => {
76 + if (ws.readyState === WebSocket.OPEN) {
77 + const coordData = makeCoordData(logData, circleStep);
78 + ws.send(JSON.stringify(coordData));
79 + }
80 + circleStep += 1;
81 + }, 1000);
82 +
83 + ws.on('close', function close() {
84 + console.log('websocket Closed');
85 + clearInterval(pingInterval);
86 + clearInterval(sendInterval);
87 + // sendInterval = null;
88 + });
89 +
90 + /* ping check */
91 + pingInterval = setInterval(function ping() {
92 + wss.clients.forEach(function each(ws) {
93 + if (ws.isAlive === false) return ws.terminate();
94 + ws.isAlive = false;
95 + ws.ping(noop);
96 + });
97 + }, 30000);
98 +
99 + wss.on('close', function close() {
100 + console.log('server closed');
101 + clearInterval(pingInterval);
102 + clearInterval(sendInterval);
103 + });
104 + });
105 +});
106 +
107 +server.on('upgrade', (request, socket, head) => {
108 + const { pathname } = url.parse(request.url);
109 +
110 + Object.entries(wss).forEach(([key, wss]) => {
111 + if (`/${key}` === pathname) {
112 + wss.handleUpgrade(request, socket, head, (ws) => {
113 + wss.emit('connection', ws, request);
114 + });
115 + }
116 + });
117 +});
118 +
119 +server.listen(port);
1 +/* eslint-disable prefer-arrow-callback,consistent-return,no-param-reassign,no-mixed-operators,no-use-before-define */
2 +const http = require('http');
3 +const WebSocket = require('ws');
4 +const url = require('url');
5 +
6 +const server = http.createServer();
7 +const wss = {};
8 +
9 +const dataNum = 20000;
10 +const wsServerCnt = process.argv[2];
11 +console.log(wsServerCnt);
12 +const dataPerWsServer = dataNum / wsServerCnt;
13 +
14 +let pingInterval = null;
15 +let sendInterval = null;
16 +
17 +const logData = [];
18 +for (let i = 0; i < dataPerWsServer; i += 1) {
19 + logData.push({
20 + latitude: getRandomArbitrary(37200000000000, 37300000000000) / 1000000000000,
21 + longitude: getRandomArbitrary(126900000000000, 127100000000000) / 1000000000000,
22 + id: i,
23 + });
24 +}
25 +
26 +function getRandomArbitrary(min, max) {
27 + return parseInt((Math.random() * (max - min) + min), 10);
28 +}
29 +function circleMove(x, y, radius, max, circleStep) {
30 + return {
31 + latitude: x + radius * Math.cos(2 * Math.PI * circleStep / max),
32 + longitude: y + radius * Math.sin(2 * Math.PI * circleStep / max),
33 + };
34 +}
35 +function makeCoordData(log, circleStep) {
36 + if (circleStep == null) {
37 + circleStep = 0;
38 + }
39 + if (circleStep === 3600) {
40 + circleStep = 0;
41 + } else circleStep += 1;
42 +
43 + // console.log('step', circleStep);
44 +
45 + return Array.from(
46 + { length: dataPerWsServer },
47 + (v, i) => ({
48 + id: i,
49 + ...circleMove(log[i].latitude, log[i].longitude, 0.05, 3600, circleStep),
50 + time: new Date(),
51 + }),
52 + );
53 +}
54 +function heartbeat() {
55 + this.isAlive = true;
56 + console.log('client Heartbeat');
57 +}
58 +function noop() {}
59 +
60 +for (let i = 0; i < wsServerCnt; i += 1) {
61 + wss[i + 1] = new WebSocket.Server({ noServer: true });
62 +}
63 +
64 +Object.entries(wss).forEach(([key, wss]) => {
65 + wss.on('connection', (ws) => {
66 + console.log('connected', key);
67 + ws.isAlive = true;
68 + ws.on('pong', heartbeat);
69 + ws.on('message', function incoming(message) {
70 + console.log('received: %s', message);
71 + });
72 + let circleStep = 0;
73 + sendInterval = setInterval(() => {
74 + if (ws.readyState === WebSocket.OPEN) {
75 + const coordData = makeCoordData(logData, circleStep);
76 + ws.send(JSON.stringify(coordData));
77 + }
78 + circleStep += 1;
79 + }, 1000);
80 +
81 + ws.on('close', function close() {
82 + console.log('websocket Closed');
83 + clearInterval(pingInterval);
84 + clearInterval(sendInterval);
85 + // sendInterval = null;
86 + });
87 +
88 + /* ping check */
89 + pingInterval = setInterval(function ping() {
90 + wss.clients.forEach(function each(ws) {
91 + if (ws.isAlive === false) return ws.terminate();
92 + ws.isAlive = false;
93 + ws.ping(noop);
94 + });
95 + }, 30000);
96 +
97 + wss.on('close', function close() {
98 + console.log('server closed');
99 + clearInterval(pingInterval);
100 + clearInterval(sendInterval);
101 + });
102 + });
103 +});
104 +
105 +server.on('upgrade', (request, socket, head) => {
106 + const { pathname } = url.parse(request.url);
107 +
108 + Object.entries(wss).forEach(([key, wss]) => {
109 + if (`/${key}` === pathname) {
110 + wss.handleUpgrade(request, socket, head, (ws) => {
111 + wss.emit('connection', ws, request);
112 + });
113 + }
114 + });
115 +});
116 +
117 +server.listen(20203);
1 +/* eslint-disable prefer-arrow-callback */
2 +// <project root>/api/index.js
3 +// const express = require('express');
4 +
5 +function getRandomArbitrary(min, max) {
6 + return parseInt((Math.random() * (max - min) + min), 10);
7 +}
8 +const app = require('express')();
9 +const http = require('http').createServer(app);
10 +const io = require('socket.io')(http, {
11 + cors: true,
12 + origins: ['http://127.0.0.1:3000', 'http://127.0.0.1:8888', 'http://localhost:3000'],
13 +});
14 +const cors = require('cors');
15 +
16 +app.use(cors());
17 +
18 +// 실제로는 /api 라우트를 처리하는 메소드가 된다.
19 +app.get('/', (req, res) => {
20 + console.log('hi');
21 + io.of('/testSoc').emit('connection', { data: '1234' });
22 + res.send('API root');
23 +});
24 +
25 +io.of('/testSoc').on('connect', (socket) => {
26 + console.log('클라이언트 접속');
27 +
28 + socket.on('getMessage', (data) => {
29 + console.log('fromClient', data);
30 + });
31 + socket.on('disconnect', () => {
32 + console.log('클라이언트 접속 종료');
33 + });
34 + setInterval(() => {
35 + socket.emit('receiveLog', { num: getRandomArbitrary(10, 100), time: new Date() });
36 + }, 1000);
37 +});
38 +
39 +http.listen(8888, () => {
40 + console.log('Socket IO server listening on port 8888');
41 +});
1 +import ws from 'k6/ws';
2 +import { check } from 'k6';
3 +
4 +export default function () {
5 + const url = 'ws://echo.websocket.org';
6 + const params = { tags: { my_tag: 'hello' } };
7 +
8 + const res = ws.connect(url, params, (socket) => {
9 + socket.on('open', () => {
10 + console.log('connected');
11 +
12 + socket.setInterval(() => {
13 + socket.ping();
14 + console.log('Pinging every 1sec (setInterval test)');
15 + }, 1000);
16 + });
17 +
18 + socket.on('ping', () => {
19 + console.log('PING!');
20 + });
21 +
22 + socket.on('pong', () => {
23 + console.log('PONG!');
24 + });
25 +
26 + socket.on('close', () => {
27 + console.log('disconnected');
28 + });
29 +
30 + socket.setTimeout(() => {
31 + console.log('2 seconds passed, closing the socket');
32 + socket.close();
33 + }, 2000);
34 + });
35 +
36 + check(res, {
37 + 'status is 101': (r) => r && r.status === 101,
38 + 'Homepage body size is 11026 bytes': (r) => r.body && r.body.length === 11026,
39 + test: (r) => r,
40 + });
41 +}
1 +/* eslint-disable prefer-arrow-callback,consistent-return,no-param-reassign,no-mixed-operators,no-use-before-define */
2 +let pingInterval = null;
3 +let sendInterval = null;
4 +const logData = [];
5 +const dataNum = 20000;
6 +for (let i = 0; i < dataNum; i += 1) {
7 + logData.push({
8 + latitude: getRandomArbitrary(37200000000000, 37300000000000) / 1000000000000,
9 + longitude: getRandomArbitrary(126900000000000, 127100000000000) / 1000000000000,
10 + id: i,
11 + });
12 +}
13 +
14 +function getRandomArbitrary(min, max) {
15 + return parseInt((Math.random() * (max - min) + min), 10);
16 +}
17 +function circleMove(x, y, radius, max, circleStep) {
18 + return {
19 + latitude: x + radius * Math.cos(2 * Math.PI * circleStep / max),
20 + longitude: y + radius * Math.sin(2 * Math.PI * circleStep / max),
21 + };
22 +}
23 +function makeCoordData(log, circleStep) {
24 + if (circleStep == null) {
25 + circleStep = 0;
26 + }
27 + if (circleStep === 3600) {
28 + circleStep = 0;
29 + } else circleStep += 1;
30 +
31 + console.log('step', circleStep);
32 +
33 + return Array.from(
34 + { length: dataNum },
35 + (v, i) => ({
36 + id: i,
37 + ...circleMove(log[i].latitude, log[i].longitude, 0.05, 3600, circleStep),
38 + time: new Date(),
39 + }),
40 + );
41 +}
42 +function heartbeat() {
43 + this.isAlive = true;
44 + console.log('client Heartbeat');
45 +}
46 +function noop() {}
47 +
48 +const WebSocket = require('ws');
49 +
50 +const wss = new WebSocket.Server({
51 + port: 20202,
52 + perMessageDeflate: {
53 + zlibDeflateOptions: {
54 + // See zlib defaults.
55 + chunkSize: 1024,
56 + memLevel: 7,
57 + level: 3,
58 + },
59 + zlibInflateOptions: {
60 + chunkSize: 10 * 1024,
61 + },
62 + // Other options settable:
63 + clientNoContextTakeover: true, // Defaults to negotiated value.
64 + serverNoContextTakeover: true, // Defaults to negotiated value.
65 + serverMaxWindowBits: 10, // Defaults to negotiated value.
66 + // Below options specified as default values.
67 + concurrencyLimit: 10, // Limits zlib concurrency for perf.
68 + threshold: 1024, // Size (in bytes) below which messages
69 + // should not be compressed.
70 + },
71 +});
72 +
73 +wss.on('connection', function connection(ws) {
74 + ws.isAlive = true;
75 + ws.on('pong', heartbeat);
76 + ws.on('message', function incoming(message) {
77 + console.log('received: %s', message);
78 + });
79 + let circleStep = 0;
80 + sendInterval = setInterval(() => {
81 + if (ws.readyState === WebSocket.OPEN) {
82 + const coordData = makeCoordData(logData, circleStep);
83 + ws.send(JSON.stringify(coordData));
84 + }
85 + circleStep += 1;
86 + }, 1000);
87 +
88 + ws.on('close', function close() {
89 + console.log('websocket Closed');
90 + clearInterval(pingInterval);
91 + clearInterval(sendInterval);
92 + // sendInterval = null;
93 + });
94 +});
95 +
96 +/* ping check */
97 +pingInterval = setInterval(function ping() {
98 + wss.clients.forEach(function each(ws) {
99 + if (ws.isAlive === false) return ws.terminate();
100 + ws.isAlive = false;
101 + ws.ping(noop);
102 + });
103 +}, 30000);
104 +
105 +wss.on('close', function close() {
106 + console.log('server closed');
107 + clearInterval(pingInterval);
108 + clearInterval(sendInterval);
109 +});
1 +# ASSETS
2 +
3 +**This directory is not required, you can delete it if you don't want to use it.**
4 +
5 +This directory contains your un-compiled assets such as LESS, SASS, or JavaScript.
6 +
7 +More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked).
1 +@import 'node_modules/ant-design-vue/dist/antd.less';
2 +@import '@/assets/styles/less/partials/antdesignCustom.less';
1 +/* default Variable */
2 +@primary-color: #1890ff; // primary color for all components
3 +@link-color: #1890ff; // link color
4 +@success-color: #52c41a; // success state color
5 +@warning-color: #faad14; // warning state color
6 +@error-color: #f5222d; // error state color
7 +@font-size-base: 14px; // major text font size
8 +@heading-color: rgba(0, 0, 0, 0.85); // heading text color
9 +@text-color: rgba(0, 0, 0, 0.65); // major text color
10 +@text-color-secondary: rgba(0, 0, 0, 0.45); // secondary text color
11 +@disabled-color: rgba(0, 0, 0, 0.25); // disable state color
12 +@border-radius-base: 4px; // major border radius
13 +@border-color-base: #d9d9d9; // major border color
14 +@box-shadow-base: 0 2px 8px rgba(0, 0, 0, 0.15); // major shadow for layers
1 +// Colors
2 +$white: #fff;
3 +$black: #001529;
4 +$blue: var(--kit-color-primary);
5 +$blue-light: #3d6ee7;
6 +$blue-dark: #103daf;
7 +$gray-1: #f2f4f8;
8 +$gray-2: #e4e9f0;
9 +$gray-3: #dde2ec;
10 +$gray-4: #c3bedc;
11 +$gray-5: #aca6cc;
12 +$gray-6: #786fa4;
13 +$yellow: #ff0;
14 +$orange: #f2a654;
15 +$red: #b52427;
16 +$pink: #fd3995;
17 +$purple: #652eff;
18 +$green: #41b883;
19 +$kdis-color: #0c7037;
20 +$antblue: #1890ff;
21 +
22 +$text: $gray-6;
23 +$border: $gray-2;
24 +
25 +// Accent colors
26 +$default: $gray-4;
27 +$primary: $kdis-color;
28 +$secondary: $gray-5;
29 +$success: $green;
30 +$info: $blue-light;
31 +$warning: $orange;
32 +$danger: $red;
33 +$light: $gray-1;
34 +$dark: $black;
35 +
36 +// dark theme
37 +$dark-gray-1: #aeaee0;
38 +$dark-gray-2: #7575a3;
39 +$dark-gray-3: #4f4f7a;
40 +$dark-gray-4: #2a274d;
41 +$dark-gray-5: #161537;
42 +$dark-gray-6: #100f28;
43 +
44 +// Font Family
45 +$base-font-family: 'Noto Sans KR', sans-serif;
46 +
47 +// Font Size
48 +$base-font-size: 15 !default;
1 +@import '@/assets/styles/scss/partials/transition.scss';
2 +@import '@/assets/styles/scss/partials/page.scss';
3 +@import '@/assets/styles/scss/partials/layouts.scss';
4 +@import '@/assets/styles/scss/partials/description.scss';
5 +@import '@/assets/styles/scss/partials/box.scss';
6 +@import '@/assets/styles/scss/partials/test.scss';
7 +@import '@/assets/styles/scss/partials/pagination.scss';
8 +@import '@/assets/styles/scss/partials/alert.scss';
1 +.filter-alert{
2 + padding-top: 5px;
3 + padding-bottom: 5px;
4 + margin-top: -10px;
5 + .ant-alert-icon{
6 + top: 9px;
7 + left: 14px;
8 + }
9 +}
1 +
2 +.mapBox {
3 + ::-webkit-scrollbar {
4 + width: 7px;
5 + border-radius: 10px;
6 + }
7 +
8 + ::-webkit-scrollbar-thumb {
9 + background-clip: padding-box;
10 + background-color: #47749E;
11 + border: 2px solid transparent;
12 + width: 5px;
13 + border-radius: 2px;
14 + }
15 + ::-webkit-scrollbar-track {
16 + background-color: rgb(236, 236, 236);
17 + border-radius: 0px 2px 2px 0px;
18 + }
19 +
20 +
21 + .search-box {
22 + position: absolute;
23 + right: 10px;
24 + top: 10px;
25 +
26 + .searchBtn{
27 + width: 50px;
28 + height: 50px;
29 + padding: 7px;
30 + background: white;
31 + border: 2px solid rgba(0,0,0,0.2);
32 + border-radius: 4px;
33 + cursor: pointer;
34 + }
35 +
36 + .ant-modal-content {
37 + position: absolute;
38 + right: 60px;
39 + top: 0px;
40 + width: 400px;
41 + max-height: calc(100vh - 120px);
42 +
43 + .ant-modal-header {
44 + background: #47749e;
45 + .ant-modal-title {
46 + color: white;
47 + text-align: center;
48 + }
49 + }
50 +
51 + .ant-modal-body {
52 + padding: 20px;
53 + }
54 +
55 + .ant-input:hover {
56 + border-color: #47749e;
57 + border-right-width: 1px !important;
58 + }
59 +
60 + .ant-input:focus {
61 + border-color: #47749e;
62 + }
63 +
64 + .ant-btn-primary {
65 + background-color: #47749e;
66 + border-color: #47749e;
67 + }
68 +
69 + .ant-list-header {
70 + color: white;
71 + background: #47749e;
72 + }
73 +
74 + .ant-input-search {
75 + width: 100%;
76 + margin-bottom: 10px;
77 + }
78 +
79 + .ant-list-items {
80 + max-height: calc(100vh - 300px);
81 + overflow-y: scroll;
82 + }
83 +
84 + .ant-list-item {
85 + padding: 10px;
86 + background: white;
87 + width: 100%;
88 + }
89 +
90 + .ant-list-item:hover {
91 + color: #47749e;
92 + font-weight: 600;
93 + }
94 + }
95 + }
96 +}
97 +.filter-feature-box {
98 + .ant-modal-content {
99 + position: absolute;
100 + right: 70px;
101 + top: 10px;
102 + width: 300px;
103 +
104 + .ant-modal-header {
105 + background: #47749e;
106 + .ant-modal-title {
107 + color: white;
108 + text-align: center;
109 + }
110 + }
111 + }
112 +
113 + .label {
114 + font-size: 13px;
115 + font-weight: 700;
116 + margin-bottom: 10px;
117 + margin-top: 20px;
118 +
119 + span {
120 + display: inline-block;
121 + border: 2px solid #47749e;
122 + border-radius: 3px;
123 + padding: 2px 5px;
124 + }
125 +
126 + }
127 +}
128 +
129 +.boxBtn {
130 + display: flex;
131 + justify-content: center;
132 + align-items: center;
133 + width: 50px;
134 + height: 50px;
135 + padding: 7px;
136 + background: white;
137 + border: 2px solid rgba(0,0,0,0.2);
138 + border-radius: 4px;
139 + cursor: pointer;
140 + font-size: 30px;
141 +}
142 +
143 +.bottom-tool-box {
144 + display: flex;
145 + gap: 10px;
146 + position: absolute;
147 + bottom: 10px;
148 + right: calc(50% - 25px);
149 + .filterBox {
150 + .filterBtn {
151 + width: 50px;
152 + height: 50px;
153 + padding: 7px;
154 + background: white;
155 + border: 2px solid rgba(0,0,0,0.2);
156 + border-radius: 4px;
157 + cursor: pointer;
158 + }
159 + }
160 +}
...\ No newline at end of file ...\ No newline at end of file
1 +@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@500&family=Roboto:wght@500&display=swap');
2 +
3 +.ant-descriptions-bordered .ant-descriptions-item-label {
4 + background-color: #47749e;
5 + color: white;
6 +}
7 +
8 +.ant-descriptions-item {
9 + display: inline-flex;
10 + margin-right: 10px;
11 +}
12 +
13 +.ant-descriptions-row {
14 + font-family: 'Noto Sans KR', sans-serif;
15 + .ant-descriptions-item-content {
16 + min-width: 150px;
17 + font-size: 13px;
18 + background: white;
19 + }
20 +}
21 +
22 +.ant-descriptions-item > span {
23 + align-self: center;
24 +}
25 +
26 +.ant-descriptions-bordered .ant-descriptions-item-label,
27 +.ant-descriptions-bordered .ant-descriptions-item-content {
28 + padding: 10px 10px;
29 +}
30 +.description-box .ant-descriptions-item {
31 + display: table-cell;
32 +}
33 +
34 +.drone-detail {
35 + .ant-descriptions-view {
36 + max-height: calc(100vh - 120px);
37 + overflow: auto;
38 + }
39 +
40 + .ant-descriptions-item-label.ant-descriptions-item-colon {
41 + font-size: 16px;
42 + }
43 +}
44 +
45 +.ant-page-header-content {
46 + .search-box .ant-descriptions-row .ant-descriptions-item-content {
47 + min-width: 0;
48 + }
49 + .search-box .ant-descriptions-row {
50 + font-family: none;
51 + }
52 +}
1 +
2 +.mt-size-default{
3 + margin-top: 8px;
4 +}
5 +.mt-size-sm {
6 + margin-top: 4px;
7 +}
8 +.mt-size-md {
9 + margin-top: 12px;
10 +}
11 +.mt-size-lg {
12 + margin-top: 20px;
13 +}
14 +
15 +.ml-size-default{
16 + margin-left: 8px;
17 +}
18 +.ml-size-sm {
19 + margin-left: 4px;
20 +}
21 +.ml-size-md {
22 + margin-left: 12px;
23 +}
24 +.ml-size-lg {
25 + margin-left: 20px;
26 +}
27 +
28 +.mr-size-default{
29 + margin-right: 8px;
30 +}
31 +.mr-size-sm {
32 + margin-right: 4px;
33 +}
34 +.mr-size-md {
35 + margin-right: 12px;
36 +}
37 +.mr-size-lg {
38 + margin-right: 20px;
39 +}
40 +
41 +.mb-size-default{
42 + margin-bottom: 8px;
43 +}
44 +.mb-size-sm {
45 + margin-bottom: 4px;
46 +}
47 +.mb-size-md {
48 + margin-bottom: 12px;
49 +}
50 +.mb-size-lg {
51 + margin-bottom: 20px;
52 +}
53 +
54 +.margin-size-default{
55 + margin: 8px;
56 +}
57 +.margin-size-sm {
58 + margin: 4px;
59 +}
60 +.margin-size-md {
61 + margin: 12px;
62 +}
63 +.margin-size-lg {
64 + margin: 20px;
65 +}
66 +
67 +
68 +.pt-size-default{
69 + padding-top: 8px;
70 +}
71 +.pt-size-sm {
72 + padding-top: 4px;
73 +}
74 +.pt-size-md {
75 + padding-top: 12px;
76 +}
77 +.pt-size-lg {
78 + padding-top: 20px;
79 +}
80 +
81 +.pl-size-default{
82 + padding-left: 8px;
83 +}
84 +.pl-size-sm {
85 + padding-left: 4px;
86 +}
87 +.pl-size-md {
88 + padding-left: 12px;
89 +}
90 +.pl-size-lg {
91 + padding-left: 20px;
92 +}
93 +
94 +.pr-size-default{
95 + padding-right: 8px;
96 +}
97 +.pr-size-sm {
98 + padding-right: 4px;
99 +}
100 +.pr-size-md {
101 + padding-right: 12px;
102 +}
103 +.pr-size-lg {
104 + padding-right: 20px;
105 +}
106 +
107 +.pb-size-default{
108 + padding-bottom: 8px;
109 +}
110 +.pb-size-sm {
111 + padding-bottom: 4px;
112 +}
113 +.pb-size-md {
114 + padding-bottom: 12px;
115 +}
116 +.pb-size-lg {
117 + padding-bottom: 20px;
118 +}
119 +
120 +.padding-size-default{
121 + padding: 8px;
122 +}
123 +.padding-size-sm {
124 + padding: 4px;
125 +}
126 +.padding-size-md {
127 + padding: 12px;
128 +}
129 +.padding-size-lg {
130 + padding: 20px;
131 +}
132 +.r-flex{
133 + display: flex;
134 + &.center{
135 + justify-content: center;
136 + align-items: center;
137 + }
138 + &.space-between{
139 + justify-content: space-between;
140 + align-items: center;
141 + }
142 + &.space-around{
143 + justify-content: space-around;
144 + align-items: center;
145 + }
146 + &.space-evenly{
147 + justify-content: space-evenly;
148 + align-items: center;
149 + }
150 + &.start{
151 + justify-content: start;
152 + align-items: center;
153 + }
154 + &.end{
155 + justify-content: flex-end;
156 + align-items: center;
157 + }
158 + &.gap-1 {
159 + gap: 4px
160 + }
161 + &.gap-2 {
162 + gap: 8px
163 + }
164 + &.gap-3 {
165 + gap: 12px
166 + }
167 + &.gap-4 {
168 + gap: 16px
169 + }
170 + &.gap-5 {
171 + gap: 20px
172 + }
173 + &.gap-6 {
174 + gap: 24px
175 + }
176 + &.gap-default{
177 + gap: 8px
178 + }
179 + &.gap-sm {
180 + gap: 6px
181 + }
182 + &.gap-md {
183 + gap: 12px
184 + }
185 + &.gap-lg {
186 + gap: 20px
187 + }
188 +}
1 +@import '@/assets/styles/mixins.scss';
2 +
3 +.page-header {
4 + background-color: white;
5 + border: $gray-3 1px solid;
6 + border-radius: 6px;
7 +}
8 +
9 +.search-input {
10 + width: 50%;
11 + min-width: 200px;
12 +}
13 +
14 +.page-main {
15 + margin-top: 20px;
16 + padding: 20px 20px 20px 20px;
17 + background-color: white;
18 + border-radius: 6px;
19 + border: $gray-2 1px solid;
20 +}
21 +
22 +.page-main-without-header {
23 + padding: 20px 20px 20px 20px;
24 + background-color: white;
25 + border-radius: 6px;
26 + border: $gray-2 1px solid;
27 +}
1 +.ant-table-pagination.ant-pagination {
2 + float: none;
3 + text-align: center;
4 +}
5 +
6 +.ant-pagination-prev .ant-pagination-item-link,
7 +.ant-pagination-next .ant-pagination-item-link,
8 +.ant-pagination-item {
9 + border: none;
10 + outline: none;
11 +}
12 +
13 +.ant-pagination-item {
14 + font-size: 1rem;
15 +}
16 +
17 +.ant-pagination-item-active a {
18 + font-weight: bolder;
19 +}
1 +@import '@/assets/styles/mixins.scss';
1 +.default-layout-enter-active,
2 +.default-layout-leave-active {
3 + transition: opacity 0.5s;
4 +}
5 +.default-layout-enter,
6 +.default-layout-leave-active {
7 + opacity: 0;
8 +}
9 +//
10 +.default-page-enter-active,
11 +.default-page-leave-active {
12 + transition: opacity 0.3s;
13 +}
14 +.default-page-enter,
15 +.default-page-leave-active {
16 + opacity: 0;
17 +}
1 +<template>
2 + <pie-chart :chart-data="chartData"
3 + :chart-settings="chartSettings"
4 + />
5 +</template>
6 +
7 +<script>
8 +import PieChart from '@/components/_Common/Chart/pieChart';
9 +
10 +export default {
11 + name: 'droneCategoryPieChart',
12 + components: {
13 + PieChart,
14 + },
15 + props: {
16 + chartData: {
17 + type: Array,
18 + default: null,
19 + },
20 + },
21 + data() {
22 + return {
23 + chartSettings: {
24 + chart: {
25 + height: 400,
26 + },
27 + title: {
28 + text: '카테고리별 드론 기체 수',
29 + style: {
30 + fontSize: '20px',
31 + },
32 + },
33 + width: '50%',
34 + plotOptions: {
35 + pie: {
36 + shadow: true,
37 + allowPointSelect: true,
38 + cursor: 'pointer',
39 + dataLabels: {
40 + useHTML: true,
41 + distance: -50,
42 + formatter() {
43 + if (this.percentage.toFixed(0) < 6) return '';
44 + return `<div style="padding: 6px 4px 4px 6px;
45 + background-color: rgba(0, 0, 0, 0.5);
46 + border: 2px solid #f2f4f8;
47 + border-radius: 6px;
48 + ">${this.percentage.toFixed(1)}%</div>`;
49 + },
50 + style: {
51 + fontSize: '16px',
52 + color: 'white',
53 + },
54 + },
55 + showInLegend: true,
56 + },
57 + },
58 + tooltip: {
59 + formatter() {
60 + return `
61 + <span style="font-size:16px; color:${this.color}">${this.key}</span>
62 + <table>
63 + <tr>
64 + <td style="padding:0">${this.series.name}</td>
65 + <td style="padding:0">: <b>${this.y}</b></td>
66 + </tr>
67 + <tr>
68 + <td style="padding:0">점유율</td>
69 + <td style="color:{series.color};padding:0">: <b>${this.percentage.toFixed(1)}%</b></td>
70 + </tr>
71 + </table>
72 + `;
73 + },
74 + },
75 + },
76 + };
77 + },
78 +};
79 +</script>
80 +
81 +<style scoped>
82 +
83 +</style>
1 +<template>
2 + <live-line-chart :chart-data="chartData"
3 + :chart-settings="chartSettings"
4 + />
5 +</template>
6 +
7 +<script>
8 +import LiveLineChart from '@/components/_Common/Chart/liveLineChart';
9 +import dayjs from 'dayjs';
10 +
11 +export default {
12 + components: {
13 + LiveLineChart,
14 + },
15 + props: {
16 + chartData: {
17 + type: Array,
18 + default: null,
19 + },
20 + },
21 + data() {
22 + return {
23 + chartSettings: {
24 + chart: {
25 + height: 400,
26 + },
27 + title: {
28 + text: '시간별 드론 기체 수',
29 + style: {
30 + fontSize: '20px',
31 + },
32 + },
33 + width: '100%',
34 + xAxis: {
35 + type: 'datetime',
36 + },
37 + yAxis: {
38 + title: {
39 + text: null,
40 + },
41 + min: 0,
42 + },
43 + tooltip: {
44 + formatter() {
45 + const trList = this.points.map((elem) => `
46 + <tr>
47 + <td style="font-size: 16px; padding:0; color:${elem.color}">${elem.series.name}</td>
48 + <td style="font-size: 16px; padding:0; color:${elem.color}">: <b>${elem.y}</b></td>
49 + </tr>
50 + `);
51 + return `
52 + <span style="font-size:12px; color:${this.color}">${dayjs(this.x).format('YYYY-MM-DD HH:mm:ss')}</span>
53 + <table>
54 + ${trList}
55 + </table>
56 + `;
57 + },
58 + },
59 + data: {
60 + enablePolling: true,
61 + dataRefreshRate: 2,
62 + },
63 + },
64 + };
65 + },
66 +};
67 +</script>
68 +
69 +<style scoped lang="scss">
70 +
71 +</style>
1 +<template>
2 + <pie-chart :chart-data="chartData"
3 + :chart-settings="chartSettings"
4 + />
5 +</template>
6 +
7 +<script>
8 +import PieChart from '@/components/_Common/Chart/pieChart';
9 +
10 +export default {
11 + name: 'makerPieChart',
12 + components: {
13 + PieChart,
14 + },
15 + props: {
16 + chartData: {
17 + type: Array,
18 + default: null,
19 + },
20 + },
21 + data() {
22 + return {
23 + chartSettings: {
24 + chart: {
25 + height: 400,
26 + },
27 + title: {
28 + text: '제조사별 드론 기체 수',
29 + style: {
30 + fontSize: '20px',
31 + },
32 + },
33 + width: '50%',
34 + plotOptions: {
35 + pie: {
36 + shadow: true,
37 + allowPointSelect: true,
38 + cursor: 'pointer',
39 + dataLabels: {
40 + useHTML: true,
41 + distance: -50,
42 + formatter() {
43 + if (this.percentage.toFixed(0) < 6) return '';
44 + return `<div style="padding: 6px 4px 4px 6px;
45 + background-color: rgba(0, 0, 0, 0.5);
46 + border: 2px solid #f2f4f8;
47 + border-radius: 6px;
48 + ">${this.percentage.toFixed(1)}%</div>`;
49 + },
50 + style: {
51 + fontSize: '16px',
52 + color: 'white',
53 + },
54 + },
55 + showInLegend: true,
56 + },
57 + },
58 + tooltip: {
59 + formatter() {
60 + return `
61 + <span style="font-size:16px; color:${this.color}">${this.key}</span>
62 + <table>
63 + <tr>
64 + <td style="padding:0">${this.series.name}</td>
65 + <td style="padding:0">: <b>${this.y}</b></td>
66 + </tr>
67 + <tr>
68 + <td style="padding:0">점유율</td>
69 + <td style="color:{series.color};padding:0">: <b>${this.percentage.toFixed(1)}%</b></td>
70 + </tr>
71 + </table>
72 + `;
73 + },
74 + },
75 + },
76 + };
77 + },
78 +};
79 +</script>
80 +
81 +<style scoped>
82 +
83 +</style>
1 +<template>
2 + <column-chart :chart-data="chartData"
3 + :chart-settings="chartSettings"/>
4 +</template>
5 +
6 +<script>
7 +import ColumnChart from '@/components/_Common/Chart/columnChart';
8 +
9 +export default {
10 + name: 'timeCategoryColumnChart',
11 + components: {
12 + ColumnChart,
13 +
14 + },
15 + props: {
16 + chartData: {
17 + type: Array,
18 + default: () => [],
19 + },
20 + },
21 + data() {
22 + return {
23 + chartSettings: {
24 + chart: {
25 + height: 400,
26 + },
27 + title: {
28 + text: '시간-드론 타입별 드론 기체 수',
29 + style: {
30 + fontSize: '20px',
31 + },
32 + },
33 + width: '100%',
34 + xAxis: {
35 + categories: [
36 + '00:00 ~ 04:00',
37 + '04:00 ~ 08:00',
38 + '08:00 ~ 12:00',
39 + '12:00 ~ 16:00',
40 + '16:00 ~ 20:00',
41 + '20:00 ~ 24:00',
42 + ],
43 + },
44 + yAxis: {
45 + title: {
46 + text: null,
47 + },
48 + min: 0,
49 + },
50 + },
51 + };
52 + },
53 +};
54 +</script>
55 +
56 +<style scoped lang="scss">
57 +
58 +</style>
1 +<template>
2 + <a-page-header
3 + class="page-header"
4 + title="Analytics"
5 + sub-title="Chart for Realtime Drone Data"
6 + >
7 + <template slot="extra">
8 + <a-button key="1" type="primary">
9 + action
10 + </a-button>
11 + </template>
12 + </a-page-header>
13 +</template>
14 +
15 +<script>
16 +
17 +export default {
18 + name: 'AnalyticsHeader',
19 + components: {},
20 + data() {
21 + return {
22 + };
23 + },
24 +};
25 +</script>
26 +
27 +<style scoped lang="scss">
28 +
29 +</style>
1 +<template>
2 + <a-page-header
3 + class="page-header"
4 + title="드론 정보"
5 + sub-title="Drone-Schedule"
6 + @back="$router.go(-1)"
7 + >
8 + <div :style="{display: 'flex'}">
9 + <div :style="{width: '30%'}">
10 + <img :src="droneInfo.picture" :style="{width: '90%'}" />
11 + </div>
12 +
13 + <div :style="{width: '70%', marginLeft: '20px'}">
14 + <div class="label-modelName">{{ droneInfo.modelName }}</div>
15 + <div :style="{display: 'flex', marginBottom: '15px'}">
16 + <div class="label-info">info</div>
17 +
18 + <a-descriptions
19 + :column="{xxl: 4, xl: 3, lg: 3, md: 3, sm: 2, xs: 1}"
20 + class="description-box"
21 + >
22 + <a-descriptions-item label="모델명">
23 + {{ droneInfo.modelName }}
24 + </a-descriptions-item>
25 + <a-descriptions-item label="제조사">
26 + {{ droneInfo.maker == null ? 'None' : droneInfo.maker }}
27 + </a-descriptions-item>
28 + <a-descriptions-item label="종류">
29 + {{ droneInfo.usage == null ? 'None' : droneInfo.usage }}
30 + </a-descriptions-item>
31 + <a-descriptions-item label="제원">
32 + {{
33 + droneInfo.specification == null
34 + ? 'None'
35 + : droneInfo.specification
36 + }}
37 + </a-descriptions-item>
38 + <a-descriptions-item label="무게">
39 + {{ droneInfo.weight == null ? '?' : droneInfo.weight }} kg
40 + </a-descriptions-item>
41 + <a-descriptions-item label="No">
42 + {{ droneInfo.id }}
43 + </a-descriptions-item>
44 + </a-descriptions>
45 + </div>
46 + </div>
47 + </div>
48 + <a-divider />
49 + </a-page-header>
50 +</template>
51 +
52 +<script>
53 +import {mapGetters} from 'vuex';
54 +
55 +export default {
56 + name: 'DatabaseDetailHeader',
57 + components: {},
58 + data() {
59 + return {
60 + droneInfo: {},
61 + };
62 + },
63 + computed: {
64 + ...mapGetters('Drone/detail', {
65 + getDetailData: 'getDetailData',
66 + }),
67 + ...mapGetters('Code', {
68 + getCodes: 'getCodes',
69 + }),
70 + droneCategory() {
71 + return (data) => {
72 + switch (parseInt(data, 10)) {
73 + case 1:
74 + return '촬영용';
75 + case 2:
76 + return '레이싱용';
77 + case 3:
78 + return '완구용';
79 + default:
80 + return null;
81 + }
82 + };
83 + },
84 + },
85 + created() {
86 + console.log(this.getDetailData);
87 + this.droneInfo = this.getDetailData;
88 + },
89 +};
90 +</script>
91 +
92 +<style scoped lang="scss">
93 +@import '@/assets/styles/mixins.scss';
94 +
95 +.label-modelName {
96 + font-size: 30px;
97 + color: $antblue;
98 +}
99 +.label-info {
100 + padding: 0 10px;
101 + border-right: 1px solid #777777;
102 + min-width: 20%;
103 +}
104 +.description-box {
105 + padding-left: 10px;
106 +}
107 +</style>
1 +<template>
2 + <a-page-header
3 + class="page-header"
4 + title="Database"
5 + sub-title="Table - Drone list"
6 + >
7 + <template slot="extra">
8 + <a-button key="1" type="primary" form="form" html-type="submit">
9 + 검색
10 + </a-button>
11 + </template>
12 +
13 + <a-form id="form" @submit.prevent="searchData" class="form-wrapper">
14 + <a-form-item label="모델명" class="form-item">
15 + <a-input v-model="searchParams.modelName" class="search-input" />
16 + </a-form-item>
17 + <a-form-item label="제조사" class="form-item">
18 + <a-input v-model="searchParams.maker" class="search-input" />
19 + </a-form-item>
20 + <a-form-item label="무게" class="form-item">
21 + <a-button
22 + v-if="searchOpenFlag"
23 + class="search-input"
24 + @click="searchOpenFlag = !searchOpenFlag"
25 + >{{ sliderBegin }} - {{ sliderEnd == 50 ? '50+' : sliderEnd }} kg
26 + <a-icon type="up" />
27 + </a-button>
28 + <a-button
29 + v-else
30 + class="search-input"
31 + @click="searchOpenFlag = !searchOpenFlag"
32 + >{{ sliderBegin }} - {{ sliderEnd == 50 ? '50+' : sliderEnd }} kg
33 + <a-icon type="down" />
34 + </a-button>
35 + <div v-if="searchOpenFlag" class="slider-box">
36 + <a-slider
37 + range
38 + :marks="marks"
39 + :max="50"
40 + :step="1"
41 + :default-value="[sliderBegin, sliderEnd]"
42 + class="search-input"
43 + @change="onSliderChange"
44 + @afterChange="onSliderAfterChange"
45 + />
46 + </div>
47 + </a-form-item>
48 + <a-form-item label="종류" has-feedback class="form-item">
49 + <a-select
50 + class="search-input"
51 + default-value=""
52 + @change="handleSelectChange"
53 + >
54 + <a-select-option value="">
55 + 선택 안 함
56 + </a-select-option>
57 + <a-select-option value="촬영용">
58 + 촬영용
59 + </a-select-option>
60 + <a-select-option value="산업용">
61 + 산업용
62 + </a-select-option>
63 + <a-select-option value="군사용">
64 + 군사용
65 + </a-select-option>
66 + <a-select-option value="레이싱용">
67 + 레이싱용
68 + </a-select-option>
69 + </a-select>
70 + </a-form-item>
71 + </a-form>
72 + </a-page-header>
73 +</template>
74 +
75 +<script>
76 +import {droneCategory} from '@/utils/CommonData/selectOptions';
77 +import {mapActions, mapGetters} from 'vuex';
78 +
79 +export default {
80 + name: 'DatabaseSearchFilter',
81 + components: {},
82 + data() {
83 + return {
84 + searchOpenFlag: false,
85 + categoryOptions: droneCategory,
86 + searchParams: {},
87 + marks: {
88 + 0: '0kg',
89 + 50: '50+kg',
90 + },
91 + sliderBegin: 0,
92 + sliderEnd: 50,
93 + };
94 + },
95 + computed: {
96 + ...mapGetters('Drone/page', {
97 + getPageParams: 'getPageParams',
98 + }),
99 + },
100 + created() {
101 + this.searchParams = JSON.parse(JSON.stringify(this.getPageParams));
102 + },
103 + methods: {
104 + ...mapActions('Drone/page', {
105 + setPageParams: 'setPageParams',
106 + }),
107 + searchData() {
108 + console.log(this.searchParams);
109 + this.setPageParams(this.searchParams);
110 + this.$emit('loadData');
111 + },
112 + handleSelectChange(value) {
113 + this.searchParams.usageName = value;
114 + this.searchData();
115 + },
116 + onSliderChange(value) {
117 + this.sliderBegin = value[0];
118 + this.sliderEnd = value[1];
119 + },
120 + onSliderAfterChange(value) {
121 + this.searchParams.minWeight = value[0];
122 + this.searchParams.maxWeight = value[1];
123 + if (value[1] == 50) {
124 + this.searchParams.maxWeight = null;
125 + }
126 + this.searchOpenFlag = !this.searchOpenFlag;
127 + },
128 + },
129 +};
130 +</script>
131 +
132 +<style scoped lang="scss">
133 +@import '@/assets/styles/mixins.scss';
134 +
135 +.form-wrapper {
136 + height: 40px;
137 +}
138 +.form-wrapper,
139 +.form-item {
140 + display: flex;
141 + margin-left: 10px;
142 + margin-bottom: 0;
143 +}
144 +
145 +.slider-box {
146 + background: white;
147 + padding: 10px 20px;
148 + border-radius: 20px;
149 + border: solid 1px $antblue;
150 + position: fixed;
151 + z-index: 10;
152 +}
153 +</style>
1 +<template>
2 + <a-table
3 + rowKey="id"
4 + bordered
5 + :loading="childLoading"
6 + :columns="columns"
7 + :data-source="getPageData"
8 + :scroll="{x: 1000}"
9 + :pagination="pagination"
10 + @change="changePage"
11 + >
12 + <a slot="modelName" slot-scope="data, row" @click="goDetail(row)">
13 + {{ data }}
14 + </a>
15 + <div slot="usageName" slot-scope="data">
16 + {{ data == null ? '?' : data }}
17 + </div>
18 + <div slot="weight" slot-scope="data">
19 + {{ data == null ? '?' : data + 'kg' }}
20 + </div>
21 + <div slot="specification" slot-scope="data">
22 + {{ data == null ? '?' : data }}
23 + </div>
24 + <div slot="droneCategory" slot-scope="data">
25 + {{ data }}
26 + </div>
27 + </a-table>
28 +</template>
29 +
30 +<script>
31 +import databaseColumn from '@/utils/ColumnData/databaseColumn';
32 +import { mapActions, mapGetters } from 'vuex';
33 +
34 +export default {
35 + name: 'DatabaseTable',
36 + props: {
37 + childLoading: {
38 + type: Boolean,
39 + default: false,
40 + },
41 + },
42 + data() {
43 + return {
44 + columns: databaseColumn,
45 + };
46 + },
47 + computed: {
48 + ...mapGetters('Drone/page', {
49 + getPageData: 'getPageData',
50 + getPagination: 'getPagination',
51 + getPageParams: 'getPageParams',
52 + }),
53 + pagination: {
54 + get() {
55 + return this.getPagination;
56 + },
57 + set(e) {
58 + this.setPagination({
59 + size: e.size,
60 + page: e.page,
61 + });
62 + },
63 + },
64 + },
65 + methods: {
66 + ...mapActions('Drone/page', {
67 + setPagination: 'setPagination',
68 + fetchPageData: 'fetchPageData',
69 + }),
70 + changePage(e) {
71 + this.pagination = {
72 + size: e.pageSize,
73 + page: e.current,
74 + };
75 + this.fetchPageData(this.getPageParams);
76 + },
77 + goDetail(row) {
78 + this.$router.push({
79 + path: `/database/drone/${row.id}`,
80 + });
81 + },
82 + },
83 +};
84 +</script>
85 +
86 +<style scoped lang="scss"></style>
1 +<template>
2 + <a-page-header
3 + class="page-header"
4 + title="Database"
5 + sub-title="Table - Drone Log"
6 + >
7 + <template slot="extra">
8 + <a-button
9 + key="1"
10 + type="primary"
11 + form="form"
12 + html-type="submit"
13 + :style="{padding: '0 15px'}"
14 + >
15 + 검색
16 + </a-button>
17 + </template>
18 +
19 + <a-form id="form" @submit.prevent="searchData" class="form-wrapper">
20 + <a-descriptions
21 + :column="{xs: 1, sm: 3, md: 3, lg: 6, xl: 7, xxl: 7}"
22 + class="search-box"
23 + >
24 + <a-descriptions-item label="Schedule ID" class="form-item">
25 + <a-input v-model="searchParams.scheduleId" class="search-input-log" />
26 + </a-descriptions-item>
27 + <a-descriptions-item label="수평 속도" class="form-item">
28 + <a-button v-if="hsOpenFlag" @click="hsOpenFlag = !hsOpenFlag"
29 + >{{ minHorizontalSpeed }} -
30 + {{ maxHorizontalSpeed == 100 ? '100+' : maxHorizontalSpeed }} km/s
31 + <a-icon type="up" />
32 + </a-button>
33 + <a-button v-else @click="hsOpenFlag = !hsOpenFlag"
34 + >{{ minHorizontalSpeed }} -
35 + {{ maxHorizontalSpeed == 100 ? '100+' : maxHorizontalSpeed }} km/s
36 + <a-icon type="down" />
37 + </a-button>
38 + <div v-if="hsOpenFlag" class="slider-box">
39 + <a-slider
40 + range
41 + :marks="speedMarks"
42 + :max="100"
43 + :step="1"
44 + :default-value="[minHorizontalSpeed, maxHorizontalSpeed]"
45 + class="search-input"
46 + @change="onHorizontalSpeedChange"
47 + @afterChange="onHorizontalSpeedAfterChange"
48 + />
49 + </div>
50 + </a-descriptions-item>
51 + <a-descriptions-item label="수직 속도" class="form-item">
52 + <a-button v-if="vsOpenFlag" @click="vsOpenFlag = !vsOpenFlag"
53 + >{{ minVerticalSpeed }} -
54 + {{ maxVerticalSpeed == 100 ? '100+' : maxVerticalSpeed }} km/s
55 + <a-icon type="up" />
56 + </a-button>
57 + <a-button v-else @click="vsOpenFlag = !vsOpenFlag"
58 + >{{ minVerticalSpeed }} -
59 + {{ maxVerticalSpeed == 100 ? '100+' : maxVerticalSpeed }} km/s
60 + <a-icon type="down" />
61 + </a-button>
62 + <div v-if="vsOpenFlag" class="slider-box">
63 + <a-slider
64 + range
65 + :marks="speedMarks"
66 + :max="100"
67 + :step="1"
68 + :default-value="[minVerticalSpeed, maxVerticalSpeed]"
69 + class="search-input"
70 + @change="onVerticalSpeedChange"
71 + @afterChange="onVerticalSpeedAfterChange"
72 + />
73 + </div>
74 + </a-descriptions-item>
75 + <a-descriptions-item label="지면 고도" class="form-item">
76 + <a-button v-if="aglOpenFlag" @click="aglOpenFlag = !aglOpenFlag"
77 + >{{ minAboveGroundLevel }} -
78 + {{ maxAboveGroundLevel == 200 ? '200+' : maxAboveGroundLevel }} m
79 + <a-icon type="up" />
80 + </a-button>
81 + <a-button v-else @click="aglOpenFlag = !aglOpenFlag"
82 + >{{ minAboveGroundLevel }} -
83 + {{ maxAboveGroundLevel == 200 ? '200+' : maxAboveGroundLevel }} m
84 + <a-icon type="down" />
85 + </a-button>
86 + <div v-if="aglOpenFlag" class="slider-box">
87 + <a-slider
88 + range
89 + :marks="levelMarks"
90 + :max="200"
91 + :step="1"
92 + :default-value="[minAboveGroundLevel, maxAboveGroundLevel]"
93 + class="search-input"
94 + @change="onAboveGroundLevelChange"
95 + @afterChange="onAboveGroundLevelAfterChange"
96 + />
97 + </div>
98 + </a-descriptions-item>
99 + <a-descriptions-item label="해발 고도" class="form-item">
100 + <a-button v-if="aslOpenFlag" @click="aslOpenFlag = !aslOpenFlag"
101 + >{{ minAboveSeaLevel }} -
102 + {{ maxAboveSeaLevel == 200 ? '200+' : maxAboveSeaLevel }} m
103 + <a-icon type="up" />
104 + </a-button>
105 + <a-button v-else @click="aslOpenFlag = !aslOpenFlag"
106 + >{{ minAboveSeaLevel }} -
107 + {{ maxAboveSeaLevel == 200 ? '200+' : maxAboveSeaLevel }} m
108 + <a-icon type="down" />
109 + </a-button>
110 + <div v-if="aslOpenFlag" class="slider-box">
111 + <a-slider
112 + range
113 + :marks="levelMarks"
114 + :max="200"
115 + :step="1"
116 + :default-value="[minAboveSeaLevel, maxAboveSeaLevel]"
117 + class="search-input"
118 + @change="onAboveSeaLevelChange"
119 + @afterChange="onAboveSeaLevelAfterChange"
120 + />
121 + </div>
122 + </a-descriptions-item>
123 + <a-descriptions-item label="위도" class="form-item">
124 + <a-input v-model="searchParams.latitude" class="search-input-log" />
125 + </a-descriptions-item>
126 + <a-descriptions-item label="경도" class="form-item">
127 + <a-input v-model="searchParams.longitude" class="search-input-log" />
128 + </a-descriptions-item>
129 + </a-descriptions>
130 + </a-form>
131 + </a-page-header>
132 +</template>
133 +
134 +<script>
135 +/* eslint-disable prefer-destructuring */
136 +import {mapActions, mapGetters} from 'vuex';
137 +
138 +export default {
139 + name: 'LogSearchFilter',
140 + components: {},
141 + data() {
142 + return {
143 + hsOpenFlag: false,
144 + vsOpenFlag: false,
145 + aslOpenFlag: false,
146 + aglOpenFlag: false,
147 + searchParams: {},
148 + speedMarks: {
149 + 0: '0km/h',
150 + 100: '100+km/h',
151 + },
152 + levelMarks: {
153 + 0: '0m',
154 + 200: '200+m',
155 + },
156 + minVerticalSpeed: 0,
157 + maxVerticalSpeed: 100,
158 + minHorizontalSpeed: 0,
159 + maxHorizontalSpeed: 100,
160 + minAboveSeaLevel: 0,
161 + maxAboveSeaLevel: 200,
162 + minAboveGroundLevel: 0,
163 + maxAboveGroundLevel: 200,
164 + };
165 + },
166 + computed: {
167 + ...mapGetters('Log/page', {
168 + getPageParams: 'getPageParams',
169 + }),
170 + },
171 + created() {
172 + this.searchParams = JSON.parse(JSON.stringify(this.getPageParams));
173 + },
174 + methods: {
175 + ...mapActions('Log/page', {
176 + setPageParams: 'setPageParams',
177 + }),
178 + searchData() {
179 + console.log(this.searchParams);
180 + this.setPageParams(this.searchParams);
181 + this.$emit('loadData');
182 + },
183 + onHorizontalSpeedChange(value) {
184 + this.minHorizontalSpeed = value[0];
185 + this.maxHorizontalSpeed = value[1];
186 + },
187 + onHorizontalSpeedAfterChange(value) {
188 + this.hsOpenFlag = !this.hsOpenFlag;
189 + this.searchParams.minHorizontalSpeed = this.minHorizontalSpeed;
190 + this.searchParams.maxHorizontalSpeed = this.maxHorizontalSpeed;
191 + if (this.maxHorizontalSpeed == 100) {
192 + this.searchParams.maxHorizontalSpeed = null;
193 + }
194 + },
195 + onVerticalSpeedChange(value) {
196 + this.minVerticalSpeed = value[0];
197 + this.maxVerticalSpeed = value[1];
198 + },
199 + onVerticalSpeedAfterChange(value) {
200 + this.vsOpenFlag = !this.vsOpenFlag;
201 + this.searchParams.minVerticalSpeed = this.minVerticalSpeed;
202 + this.searchParams.maxVerticalSpeed = this.maxVerticalSpeed;
203 + if (this.maxVerticalSpeed == 100) {
204 + this.searchParams.maxVerticalSpeed = null;
205 + }
206 + },
207 + onAboveSeaLevelChange(value) {
208 + this.minAboveSeaLevel = value[0];
209 + this.maxAboveSeaLevel = value[1];
210 + },
211 + onAboveSeaLevelAfterChange(value) {
212 + this.aslOpenFlag = !this.aslOpenFlag;
213 + this.searchParams.minAboveSeaLevel = this.minAboveSeaLevel;
214 + this.searchParams.maxAboveSeaLevel = this.maxAboveSeaLevel;
215 + if (this.maxAboveSeaLevel == 200) {
216 + this.searchParams.maxAboveSeaLevel = null;
217 + }
218 + },
219 + onAboveGroundLevelChange(value) {
220 + this.minAboveGroundLevel = value[0];
221 + this.maxAboveGroundLevel = value[1];
222 + },
223 + onAboveGroundLevelAfterChange(value) {
224 + this.aglOpenFlag = !this.aglOpenFlag;
225 + this.searchParams.minAboveGroundLevel = this.minAboveGroundLevel;
226 + this.searchParams.maxAboveGroundLevel = this.maxAboveGroundLevel;
227 + if (this.maxAboveGroundLevel == 200) {
228 + this.searchParams.maxAboveGroundLevel = null;
229 + }
230 + },
231 + },
232 +};
233 +</script>
234 +
235 +<style scoped lang="scss">
236 +@import '@/assets/styles/mixins.scss';
237 +.form-wrapper {
238 + //height: 40px;
239 + justify-content: space-between;
240 +}
241 +.form-wrapper,
242 +.form-item {
243 + display: flex;
244 + margin-right: 10px;
245 + margin-bottom: 0;
246 +}
247 +
248 +.slider-box {
249 + background: white;
250 + padding: 10px 23px;
251 + border-radius: 20px;
252 + border: solid 1px $antblue;
253 + position: fixed;
254 + z-index: 9;
255 +}
256 +
257 +.search-input-log {
258 + width: 100px;
259 +}
260 +
261 +.ant-btn {
262 + padding: 0 10px;
263 +}
264 +</style>
1 +<template>
2 + <a-table
3 + rowKey="id"
4 + bordered
5 + :loading="childLoading"
6 + :columns="columns"
7 + :data-source="getPageData"
8 + :scroll="{x: 1000}"
9 + :pagination="pagination"
10 + @change="changePage"
11 + >
12 + <a slot="modelName" slot-scope="data, row" @click="goDetail(row)">
13 + {{ data }}
14 + </a>
15 +
16 + <div slot="horizontalSpeed" slot-scope="data">{{ data }} km/h</div>
17 + <div slot="verticalSpeed" slot-scope="data">{{ data }} km/h</div>
18 + <div slot="aboveGroundLevel" slot-scope="data">{{ data }} m</div>
19 + <div slot="aboveSeaLevel" slot-scope="data">{{ data }} m</div>
20 + </a-table>
21 +</template>
22 +
23 +<script>
24 +import databaseLogColumn from '@/utils/ColumnData/databaseLogColumn';
25 +import { mapActions, mapGetters } from 'vuex';
26 +
27 +export default {
28 + name: 'LogTable',
29 + props: {
30 + childLoading: {
31 + type: Boolean,
32 + default: false,
33 + },
34 + },
35 + data() {
36 + return {
37 + columns: databaseLogColumn,
38 + };
39 + },
40 + computed: {
41 + ...mapGetters('Log/page', {
42 + getPageData: 'getPageData',
43 + getPagination: 'getPagination',
44 + getPageParams: 'getPageParams',
45 + }),
46 + pagination: {
47 + get() {
48 + return this.getPagination;
49 + },
50 + set(e) {
51 + this.setPagination({
52 + size: e.size,
53 + page: e.page,
54 + });
55 + },
56 + },
57 + },
58 + methods: {
59 + ...mapActions('Log/page', {
60 + setPagination: 'setPagination',
61 + fetchPageData: 'fetchPageData',
62 + }),
63 + ...mapActions('Drone/detail', {
64 + fetchDroneDetailData: 'fetchDetailData',
65 + }),
66 + ...mapActions('Schedule/detail', {
67 + fetchScheduleDetailData: 'fetchDetailData',
68 + }),
69 + changePage(e) {
70 + this.pagination = {
71 + size: e.pageSize,
72 + page: e.current,
73 + };
74 + this.fetchPageData(this.getPageParams);
75 + },
76 + goDetail(row) {
77 + this.fetchDroneDetailData(row.droneId);
78 + this.fetchScheduleDetailData(row.scheduleId);
79 + this.$router.push({
80 + path: `/database/schedule/${row.scheduleId}`,
81 + });
82 + },
83 + },
84 +};
85 +</script>
86 +
87 +<style scoped lang="scss"></style>
1 +<template>
2 + <a-page-header
3 + class="page-header"
4 + title="드론 정보"
5 + sub-title="Drone-Schedule-Log"
6 + @back="$router.go(-1)"
7 + >
8 + <div :style="{display: 'flex'}">
9 + <div :style="{width: '30%'}">
10 + <img :src="droneInfo.picture" :style="{width: '90%'}" />
11 + </div>
12 +
13 + <div :style="{width: '70%', marginLeft: '20px'}">
14 + <div class="label-modelName">{{ droneInfo.modelName }}</div>
15 + <div :style="{display: 'flex', marginBottom: '15px'}">
16 + <div class="label-info">info</div>
17 +
18 + <a-descriptions
19 + :column="{xxl: 4, xl: 3, lg: 3, md: 3, sm: 2, xs: 1}"
20 + class="description-box"
21 + >
22 + <a-descriptions-item label="모델명">
23 + {{ droneInfo.modelName }}
24 + </a-descriptions-item>
25 + <a-descriptions-item label="제조사">
26 + {{ droneInfo.maker == null ? 'None' : droneInfo.maker }}
27 + </a-descriptions-item>
28 + <a-descriptions-item label="종류">
29 + {{ droneInfo.usage == null ? 'None' : droneInfo.usageName }}
30 + </a-descriptions-item>
31 + <a-descriptions-item label="제원">
32 + {{
33 + droneInfo.specification == null
34 + ? 'None'
35 + : droneInfo.specification
36 + }}
37 + </a-descriptions-item>
38 + <a-descriptions-item label="무게">
39 + {{ droneInfo.weight == null ? '?' : droneInfo.weight }} kg
40 + </a-descriptions-item>
41 + <a-descriptions-item label="No">
42 + {{ droneInfo.id }}
43 + </a-descriptions-item>
44 + </a-descriptions>
45 + </div>
46 + <div :style="{display: 'flex'}">
47 + <div class="label-info">Schedule</div>
48 +
49 + <a-descriptions
50 + :column="{xxl: 4, xl: 3, lg: 3, md: 3, sm: 2, xs: 1}"
51 + class="description-box"
52 + >
53 + <a-descriptions-item label="시작 시간">
54 + {{ mc_dateTime(ScheduleInfo.startTime) }}
55 + </a-descriptions-item>
56 + <a-descriptions-item label="시작 위도">
57 + {{ ScheduleInfo.startLatitude }}
58 + </a-descriptions-item>
59 + <a-descriptions-item label="시작 경도">
60 + {{ ScheduleInfo.startLongitude }}
61 + </a-descriptions-item>
62 + <a-descriptions-item label="종료 시간">
63 + {{ mc_dateTime(ScheduleInfo.terminateTime) }}
64 + </a-descriptions-item>
65 + <a-descriptions-item label="종료 위도">
66 + {{ ScheduleInfo.terminateLatitude }}
67 + </a-descriptions-item>
68 + <a-descriptions-item label="종료 경도">
69 + {{ ScheduleInfo.terminateLongitude }}
70 + </a-descriptions-item>
71 + </a-descriptions>
72 + </div>
73 + </div>
74 + </div>
75 + <a-divider />
76 + </a-page-header>
77 +</template>
78 +
79 +<script>
80 +import {mapGetters} from 'vuex';
81 +
82 +export default {
83 + name: 'DatabaseDetailHeader',
84 + components: {},
85 + data() {
86 + return {
87 + droneInfo: {},
88 + ScheduleInfo: {},
89 + };
90 + },
91 + created() {
92 + this.droneInfo = this.getDetailData;
93 + this.ScheduleInfo = this.getScheduleDetailData;
94 + },
95 + computed: {
96 + ...mapGetters('Drone/detail', {
97 + getDetailData: 'getDetailData',
98 + }),
99 + ...mapGetters('Schedule/detail', {
100 + getScheduleDetailData: 'getDetailData',
101 + }),
102 + ...mapGetters('Code', {
103 + getCodes: 'getCodes',
104 + }),
105 + droneCategory() {
106 + return (data) => {
107 + switch (parseInt(data, 10)) {
108 + case 1:
109 + return '촬영용';
110 + case 2:
111 + return '레이싱용';
112 + case 3:
113 + return '완구용';
114 + default:
115 + return null;
116 + }
117 + };
118 + },
119 + },
120 +};
121 +</script>
122 +
123 +<style scoped lang="scss">
124 +@import '@/assets/styles/mixins.scss';
125 +
126 +.label-modelName {
127 + font-size: 30px;
128 + color: $antblue;
129 +}
130 +.label-info {
131 + padding: 0 10px;
132 + border-right: 1px solid #777777;
133 + min-width: 20%;
134 +}
135 +.description-box {
136 + padding-left: 10px;
137 +}
138 +</style>
1 +<template>
2 + <a-page-header
3 + class="page-header"
4 + title="Database"
5 + sub-title="Table - DroneSchedule"
6 + >
7 + <template slot="extra">
8 + <a-button key="1" type="primary" form="form" html-type="submit">
9 + 검색
10 + </a-button>
11 + </template>
12 +
13 + <a-form id="form" @submit.prevent="searchData" class="form-wrapper">
14 + <a-form-item label="검색 범위" class="form-item">
15 + <div>
16 + <a-date-picker
17 + v-model="startValue"
18 + :disabled-date="disabledStartDate"
19 + show-time
20 + format="YYYY-MM-DD HH:mm:ss"
21 + placeholder="Start Date"
22 + @openChange="handleStartOpenChange"
23 + />
24 + <span :style="{padding: '0 10px'}">-</span>
25 + <a-date-picker
26 + v-model="endValue"
27 + :disabled-date="disabledEndDate"
28 + show-time
29 + format="YYYY-MM-DD HH:mm:ss"
30 + placeholder="End Date"
31 + :open="endOpen"
32 + @openChange="handleEndOpenChange"
33 + />
34 + </div>
35 + </a-form-item>
36 + </a-form>
37 + </a-page-header>
38 +</template>
39 +
40 +<script>
41 +import {mapActions, mapGetters} from 'vuex';
42 +
43 +export default {
44 + name: 'DatabaseSearchFilter',
45 + components: {},
46 + data() {
47 + return {
48 + searchParams: {},
49 + startValue: null,
50 + endValue: null,
51 + endOpen: false,
52 + };
53 + },
54 + watch: {
55 + startValue(val) {
56 + if (val != null) {
57 + this.searchParams.startTime = val.format('YYYY-MM-DD HH:mm:ss');
58 + } else {
59 + this.searchParams.startTime = null;
60 + }
61 + },
62 + endValue(val) {
63 + if (val !== null) {
64 + this.searchParams.terminateTime = val.format('YYYY-MM-DD HH:mm:ss');
65 + } else {
66 + this.searchParams.terminateTime = null;
67 + }
68 + },
69 + },
70 + computed: {
71 + ...mapGetters('Schedule/page', {
72 + getPageParams: 'getPageParams',
73 + }),
74 + },
75 + created() {
76 + this.searchParams = JSON.parse(JSON.stringify(this.getPageParams));
77 + },
78 + methods: {
79 + ...mapActions('Schedule/page', {
80 + setPageParams: 'setPageParams',
81 + }),
82 + searchData() {
83 + console.log(this.searchParams);
84 + this.setPageParams(this.searchParams);
85 + this.$emit('loadData');
86 + },
87 + disabledStartDate(startValue) {
88 + const endValue = this.endValue;
89 + if (!startValue || !endValue) {
90 + return false;
91 + }
92 + return startValue.valueOf() > endValue.valueOf();
93 + },
94 + disabledEndDate(endValue) {
95 + const startValue = this.startValue;
96 + if (!endValue || !startValue) {
97 + return false;
98 + }
99 + return startValue.valueOf() >= endValue.valueOf();
100 + },
101 + handleStartOpenChange(open) {
102 + if (!open) {
103 + this.endOpen = true;
104 + }
105 + },
106 + handleEndOpenChange(open) {
107 + this.endOpen = open;
108 + },
109 + },
110 +};
111 +</script>
112 +
113 +<style scoped lang="scss">
114 +@import '@/assets/styles/mixins.scss';
115 +.form-wrapper {
116 + height: 40px;
117 +}
118 +.form-wrapper,
119 +.form-item {
120 + display: flex;
121 + margin-left: 10px;
122 + margin-bottom: 0;
123 +}
124 +.slider-box {
125 + background: white;
126 + padding: 10px 15px;
127 + border-radius: 20px;
128 + border: solid 1px $antblue;
129 + position: fixed;
130 + z-index: 9;
131 +}
132 +</style>
1 +<template>
2 + <a-table
3 + rowKey="id"
4 + bordered
5 + :loading="childLoading"
6 + :columns="columns"
7 + :data-source="getPageData"
8 + :scroll="{x: 1000}"
9 + :pagination="pagination"
10 + @change="changePage"
11 + >
12 + <a slot="modelName" slot-scope="data, row" @click="goDetail(row)">
13 + {{ data }}
14 + </a>
15 + <div slot="startTime" slot-scope="data">
16 + {{ mc_dateTime(data) }}
17 + </div>
18 + <div slot="terminateTime" slot-scope="data">
19 + {{ mc_dateTime(data) }}
20 + </div>
21 + </a-table>
22 +</template>
23 +
24 +<script>
25 +import databaseScheduleColumn from '@/utils/ColumnData/databaseScheduleColumn';
26 +import {mapActions, mapGetters} from 'vuex';
27 +
28 +export default {
29 + name: 'DatabaseTable',
30 + props: {
31 + childLoading: {
32 + type: Boolean,
33 + default: false,
34 + },
35 + },
36 + data() {
37 + return {
38 + columns: databaseScheduleColumn,
39 + };
40 + },
41 + computed: {
42 + ...mapGetters('Schedule/page', {
43 + getPageData: 'getPageData',
44 + getPagination: 'getPagination',
45 + getPageParams: 'getPageParams',
46 + }),
47 + pagination: {
48 + get() {
49 + return this.getPagination;
50 + },
51 + set(e) {
52 + this.setPagination({
53 + size: e.size,
54 + page: e.page,
55 + });
56 + },
57 + },
58 + },
59 + methods: {
60 + ...mapActions('Schedule/page', {
61 + setPagination: 'setPagination',
62 + fetchPageData: 'fetchPageData',
63 + }),
64 + ...mapActions('Drone/detail', {
65 + fetchDetailData: 'fetchDetailData',
66 + }),
67 + changePage(e) {
68 + this.pagination = {
69 + size: e.pageSize,
70 + page: e.current,
71 + };
72 + this.fetchPageData(this.getPageParams);
73 + },
74 + goDetail(row) {
75 + this.fetchDetailData(row.droneId);
76 + this.$router.push({
77 + path: `/database/schedule/${row.id}`,
78 + });
79 + },
80 + },
81 +};
82 +</script>
83 +
84 +<style scoped lang="scss"></style>
1 +<template>
2 + <div>
3 + <h4 class="footer-font">
4 + Drone Simulation Map
5 + </h4>
6 + <h5 class="footer-font">
7 + @2021 TwentyOz & KHU
8 + </h5>
9 + </div>
10 +</template>
11 +
12 +<script>
13 +export default {
14 + name: 'LayoutFooter',
15 + data() {
16 + return {
17 +
18 + };
19 + },
20 + computed: {
21 +
22 + },
23 + watch: {
24 +
25 + },
26 + created() {
27 +
28 + },
29 + method: {
30 +
31 + },
32 +};
33 +</script>
34 +
35 +<style scoped lang="scss">
36 +@import '@/assets/styles/mixins.scss';
37 +
38 +.footer-font {
39 + color: $gray-3
40 +}
41 +</style>
1 +<template>
2 + <div>
3 + <div class="bottom-tool-box">
4 + <div class="boxBtn" @click="clickWeatherBtn">
5 + <a-icon type="cloud"/>
6 + </div>
7 + <FilterBtnBox
8 + class="filterBox"
9 + @clickClose="clickFilterBoxClose"
10 + @changeFilterMode="e => this.$emit('changeFilterMode', e)"
11 + @changeSearchMode="e => this.$emit('changeSearchMode', e)"
12 + />
13 + <div class="boxBtn" @click="clickBookMarkBtn">
14 + <a-icon type="star"/>
15 + </div>
16 + </div>
17 + </div>
18 +</template>
19 +<script>
20 +import FilterBtnBox from '../FilterBox/filterBtnBox';
21 +
22 +export default {
23 + head() {
24 + return {
25 + title: 'Drone',
26 + meta: [
27 + {
28 + hid: 'database',
29 + name: 'Descriptions',
30 + content: 'DroneWeb-Content',
31 + },
32 + ],
33 + };
34 + },
35 + components: {
36 + FilterBtnBox
37 + },
38 + props: {
39 + },
40 + data() {
41 + return {
42 + searchMode: false,
43 + filterMode: false,
44 + };
45 + },
46 + computed: {
47 + },
48 + created() {
49 + },
50 + methods: {
51 + clickWeatherBtn() {
52 + console.log('click')
53 + this.$notification['warning']({
54 + message: '날씨 기능은 추후 추가될 예정입니다.',
55 + duration: 3,
56 + })
57 + },
58 + clickBookMarkBtn() {
59 + this.$notification['warning']({
60 + message: '즐겨찾기 기능은 추후 추가될 예정입니다.',
61 + duration: 3,
62 + })
63 + },
64 + toggleFilterMode() {
65 + this.$emit('toggleFilterMode')
66 + },
67 + toggleSearchMode() {
68 + this.$emit('toggleSearchMode')
69 + },
70 + clickSearchBoxClose() {
71 + this.searchMode = false;
72 + },
73 + clickFilterBoxClose() {
74 + this.filterMode = false;
75 + },
76 + clickSearchBtn() {
77 + this.filterMode = false;
78 + this.searchMode = true;
79 + },
80 + clickFilterBtn() {
81 + this.searchMode = false;
82 + this.filterMode = true;
83 + },
84 +
85 + },
86 +};
87 +</script>
88 +<style lang="scss">
89 +</style>
1 +<template>
2 + <div>
3 + <a-descriptions layout="vertical" bordered>
4 + <a-descriptions-item :span="4">
5 + <template v-slot:label>
6 + <div>{{ foundDrone.modelName }}</div>
7 + </template>
8 + <img
9 + :src="foundDrone.picture || require('@/assets/images/drone-image.jpg')"
10 + :style="{width: '300px'}"
11 + />
12 + </a-descriptions-item>
13 + <a-descriptions-item label="실시간 정보" :span="4">
14 + <div class="des-sub-title">현재 위치</div>
15 + <div class="des-sub-cont-grid-4" style="margin-bottom: 10px;">
16 + <div>
17 + <div>경도</div>
18 + <div>{{ selectedLastLog.longitude ? selectedLastLog.longitude.toFixed(3) : '?' }}</div>
19 + </div>
20 + <div>
21 + <div>위도</div>
22 + <div>{{ selectedLastLog.latitude ? selectedLastLog.latitude.toFixed(3) : '?' }}</div>
23 + </div>
24 + <div>
25 + <div>이동거리</div>
26 + <div>{{ Math.floor(getDetailData.distance + selectedLastLog.distance) }}m</div>
27 + </div>
28 + <div>
29 + <div>운용시간</div>
30 + <div>{{ getTimeDiff(foundSchedule.startTime) }}</div>
31 + </div>
32 + </div>
33 + <div class="des-sub-cont-grid-2" style="margin-bottom: 10px;">
34 + <div>
35 + <div class="des-sub-title">현재 속도</div>
36 + <div class="des-sub-cont-grid-2">
37 + <div>
38 + <div>수평 속도</div>
39 + <div>{{ selectedLastLog.horizontalSpeed }}km/h</div>
40 + </div>
41 + <div>
42 + <div>수직 속도</div>
43 + <div>{{ selectedLastLog.verticalSpeed }}km/h</div>
44 + </div>
45 + </div>
46 + </div>
47 + <div>
48 + <div class="des-sub-title">현재 고도</div>
49 + <div class="des-sub-cont-grid-2">
50 + <div>
51 + <div>지면고도</div>
52 + <div>{{ selectedLastLog.aboveGroundLevel }}m</div>
53 + </div>
54 + <div>
55 + <div>해발고도</div>
56 + <div>{{ selectedLastLog.aboveSeaLevel }}m</div>
57 + </div>
58 + </div>
59 + </div>
60 + </div>
61 + </a-descriptions-item>
62 + <a-descriptions-item label="스케쥴" :span="4" :style="{padding: '0px'}">
63 + <div class="des-sub-cont-grid-2" style="margin-bottom: 10px;">
64 + <div>
65 + <div class="des-sub-title">시작 스케쥴</div>
66 + <div>
67 + <div>날짜/시간</div>
68 + <div style="margin-bottom: 5px;">{{ mc_dateTime(foundSchedule.startTime) || '?' }}</div>
69 + <div>경도</div>
70 + <div style="margin-bottom: 5px;">{{ foundSchedule.startLongitude || '?' }}</div>
71 + <div>위도</div>
72 + <div style="margin-bottom: 5px;">{{ foundSchedule.startLatitude || '?' }}</div>
73 + </div>
74 + </div>
75 + <div>
76 + <div class="des-sub-title">실제 시작</div>
77 + <div>
78 + <div>날짜/시간</div>
79 + <div style="margin-bottom: 5px;">{{ droneLogs.length !== 0 ? mc_dateTime(droneLogs[0].createdAt) : "?" }}</div>
80 + <div>경도</div>
81 + <div style="margin-bottom: 5px;">{{ droneLogs.length !== 0 ? droneLogs[0].longitude : "?" }}</div>
82 + <div>위도</div>
83 + <div style="margin-bottom: 5px;">{{ droneLogs.length !== 0 ? droneLogs[0].latitude : "?" }}</div>
84 + </div>
85 + </div>
86 + </div>
87 + <div class="des-sub-title">종료 스케쥴</div>
88 + <div style="display: grid; grid-template-columns: 2fr 1fr 1fr">
89 + <div>
90 + <div>날짜/시간</div>
91 + <div>{{ mc_dateTime(foundSchedule.terminateTime) || '?' }}</div>
92 + </div>
93 + <div>
94 + <div>경도</div>
95 + <div>{{ foundSchedule.terminateLongitude || '?' }}</div>
96 + </div>
97 + <div>
98 + <div>위도</div>
99 + <div>{{ foundSchedule.terminateLatitude || '?' }}</div>
100 + </div>
101 + </div>
102 + </a-descriptions-item>
103 + <a-descriptions-item label="드론 정보" :span="6">
104 + <div class="des-sub-cont-grid-4">
105 + <div>
106 + <div class="des-sub-title">제조사</div>
107 + <div>{{ foundDrone.maker || '?' }}</div>
108 + </div>
109 + <div>
110 + <div class="des-sub-title">종류</div>
111 + <div>{{ foundDrone.usageName || '?' }}</div>
112 + </div>
113 + <div>
114 + <div class="des-sub-title">제원</div>
115 + <div>{{ foundDrone.specification || '?' }}</div>
116 + </div>
117 + <div>
118 + <div class="des-sub-title">무게</div>
119 + <div>{{ foundDrone.weight }}g</div>
120 + </div>
121 + </div>
122 + </a-descriptions-item>
123 + </a-descriptions>
124 + </div>
125 +</template>
126 +
127 +<script>
128 +import { mapActions, mapGetters } from 'vuex';
129 +
130 +export default {
131 + head() {
132 + return {
133 + title: 'Drone',
134 + meta: [
135 + {
136 + hid: 'database',
137 + name: 'Descriptions',
138 + content: 'DroneWeb-Content',
139 + },
140 + ],
141 + };
142 + },
143 + watch: {
144 + getSelectedLastLog: {
145 + deep: true,
146 + handler(newVal) {
147 + this.selectedLastLog = newVal;
148 + },
149 + },
150 + },
151 + data() {
152 + return {
153 + selectedLastLog: {},
154 + };
155 + },
156 + computed: {
157 + ...mapGetters('Drone/drone', {
158 + getDetailData: 'getDetailData',
159 + getSelectedLastLog: 'getSelectedLastLog',
160 + }),
161 + foundSchedule() {
162 + return this.getDetailData.foundSchedule || {};
163 + },
164 + foundDrone() {
165 + return this.getDetailData.foundDrone || {};
166 + },
167 + droneLogs() {
168 + return this.getDetailData.droneLogs || [];
169 + },
170 + selectedLastLog() {
171 + return this.getSelectedLastLog || {};
172 + },
173 + },
174 + methods: {
175 + getTimeDiff(startTime) {
176 + const totalSeconds = this.$dayjs().diff(this.$dayjs(startTime), 's');
177 + const seconds = totalSeconds % 60;
178 + const minutes = Math.floor(totalSeconds / 60) % 60;
179 + const hours = Math.floor(totalSeconds / 3600);
180 +
181 + return `${hours}:${minutes}:${seconds}`;
182 + },
183 + },
184 +};
185 +</script>
186 +<style lang="scss">
187 +.des-sub-title {
188 + font-size: 16px;
189 + font-weight: 900;
190 +}
191 +
192 +.des-sub-cont-grid-2 {
193 + display: grid;
194 + grid-template-columns: 1fr 1fr;
195 +}
196 +
197 +.des-sub-cont-grid-4 {
198 + display: grid;
199 + grid-template-columns: 1fr 1fr 1fr 1fr;
200 +}
201 +
202 +.des-sub-cont {
203 + display: flex;
204 + gap: 20px;
205 + margin-bottom: 20px;
206 +}
207 +
208 +</style>
1 +<template>
2 + <div>
3 + <img
4 + @click="clickFilterBtn"
5 + class="filterBtn"
6 + :src="require('@/static/img/filter.png')"/>
7 + </div>
8 +</template>
9 +<script>
10 +
11 +import CloseBox from '@/components/_Common/CloseBox/closeBox';
12 +import { mapActions, mapGetters } from 'vuex';
13 +
14 +export default {
15 + head() {
16 + return {
17 + title: 'Drone',
18 + meta: [
19 + {
20 + hid: 'database',
21 + name: 'Descriptions',
22 + content: 'DroneWeb-Content',
23 + },
24 + ],
25 + };
26 + },
27 + components: {
28 + CloseBox,
29 + },
30 + props: {},
31 + data() {
32 + return {
33 + filterMode: false,
34 + filteredDroneList: [],
35 + manufacturerValue: [],
36 + weightValue: [0, 50],
37 + weightMarks: {
38 + 0: '0kg',
39 + 50: '50kg+',
40 + },
41 + altitudeValue: [0, 200],
42 + altitudeMarks: {
43 + 0: '0m',
44 + 200: '200m+',
45 + },
46 + speedValue: [0, 100],
47 + speedMarks: {
48 + 0: '0km/h',
49 + 100: '100km/h+',
50 + },
51 + filmingOptions: ['True', 'False'],
52 + filmingValue: 'True',
53 + };
54 + },
55 + computed: {
56 + ...mapGetters('Etc', {
57 + getMakers: 'getMakers',
58 + }),
59 + ...mapGetters('Drone', {
60 + getLogFilter: 'drone/getLogFilter',
61 + getFixedDroneList: 'list/getFixedDroneList',
62 + }),
63 + },
64 + created() {
65 + },
66 + methods: {
67 + ...mapActions('Drone/drone', {
68 + setLogFilter: 'setLogFilter',
69 + clearLogFilter: 'clearLogFilter',
70 + }),
71 + clickFilterBtn() {
72 + this.$emit('changeSearchMode', false)
73 + this.$emit('changeFilterMode', true)
74 + },
75 + clickClose() {
76 + this.$emit('clickClose');
77 + },
78 + changeManufacturer(value) {
79 + this.manufacturerValue = value;
80 + this.filteredDroneList = this.getFixedDroneList.filter((v) => !!this.manufacturerValue.find((e) => v.maker === e));
81 + },
82 + changeWeight(weight) {
83 + this.weightValue = weight;
84 + },
85 + changeAltitude(altitude) {
86 + this.altitudeValue = altitude;
87 + },
88 + changeSpeed(speed) {
89 + this.speedValue = speed;
90 + },
91 + applyFilter() {
92 + this.setLogFilter({
93 + checkFilter: true,
94 + maker: this.manufacturerValue,
95 + filteredDroneList: this.filteredDroneList,
96 + weight: this.weightValue,
97 + altitude: this.altitudeValue,
98 + speed: this.speedValue,
99 + filming: this.filmingValue,
100 + });
101 + },
102 + clickReset() {
103 + this.clearLogFilter();
104 + this.filteredDroneList = [];
105 + this.manufacturerValue = [];
106 + this.weightValue = [0, 50];
107 + this.altitudeValue = [0, 200];
108 + this.speedValue = [0, 100];
109 + this.filmingValue = [];
110 + },
111 + },
112 +};
113 +</script>
114 +<style lang="scss">
115 +</style>
1 +<template>
2 + <div>
3 + <CloseBox
4 + @clickClose="clickClose"
5 + >
6 + <template v-slot:header>
7 + 드론 필터
8 + </template>
9 + <template v-slot:body>
10 + <a-alert
11 + v-if="getLogFilter.checkFilter"
12 + message="필터가 적용된 상태입니다."
13 + type="info" show-icon
14 + class="filter-alert"
15 + />
16 + <div :style="{padding: '0 10px 0 5px', marginTop: getLogFilter.checkFilter ? '-5px' : '-20px'}">
17 + <div class="label">
18 + <span>제조사</span>
19 + </div>
20 + <a-select
21 + :value="manufacturerValue"
22 + mode="tags"
23 + style="width: 100%;"
24 + placeholder="제조사"
25 + @change="changeManufacturer">
26 +
27 + <a-select-option v-for="(maker,index) in getMakers"
28 + :key="index"
29 + :value="maker"
30 + >
31 + {{ maker }}
32 + </a-select-option>
33 + </a-select>
34 + <div class="label"><span>무게</span></div>
35 + <a-slider
36 + :value="weightValue"
37 + range
38 + :marks="weightMarks"
39 + :max="50"
40 + :min="0"
41 + @change="changeWeight"
42 + />
43 + <div class="label"><span>고도</span></div>
44 + <a-slider
45 + :value="altitudeValue"
46 + range
47 + :marks="altitudeMarks"
48 + :max="200"
49 + :min="0"
50 + @change="changeAltitude"/>
51 + <div class="label"><span>속력</span></div>
52 + <a-slider
53 + :value="speedValue"
54 + range
55 + :marks="speedMarks"
56 + :max="100"
57 + :min="0"
58 + @change="changeSpeed"/>
59 +
60 + <div style="display: flex; justify-content: space-between; text-align: right; margin-top: 40px">
61 + <a-button @click="applyFilter">
62 + <a-icon type="check"/>
63 + 필터 적용
64 + </a-button>
65 + <a-button @click="clickReset">
66 + <a-icon type="reload"/>
67 + 필터 리셋
68 + </a-button>
69 + </div>
70 + </div>
71 + </template>
72 + </CloseBox>
73 + </div>
74 +</template>
75 +<script>
76 +
77 +import CloseBox from '@/components/_Common/CloseBox/closeBox';
78 +import { mapActions, mapGetters } from 'vuex';
79 +
80 +export default {
81 + head() {
82 + return {
83 + title: 'Drone',
84 + meta: [
85 + {
86 + hid: 'database',
87 + name: 'Descriptions',
88 + content: 'DroneWeb-Content',
89 + },
90 + ],
91 + };
92 + },
93 + components: {
94 + CloseBox,
95 + },
96 + props: {},
97 + data() {
98 + return {
99 + filteredDroneList: [],
100 + manufacturerValue: [],
101 + weightValue: [0, 50],
102 + weightMarks: {
103 + 0: '0kg',
104 + 50: '50kg+',
105 + },
106 + altitudeValue: [0, 200],
107 + altitudeMarks: {
108 + 0: '0m',
109 + 200: '200m+',
110 + },
111 + speedValue: [0, 100],
112 + speedMarks: {
113 + 0: '0km/h',
114 + 100: '100km/h+',
115 + },
116 + filmingOptions: ['True', 'False'],
117 + filmingValue: 'True',
118 + };
119 + },
120 + computed: {
121 + ...mapGetters('Etc', {
122 + getMakers: 'getMakers',
123 + }),
124 + ...mapGetters('Drone', {
125 + getLogFilter: 'drone/getLogFilter',
126 + getFixedDroneList: 'list/getFixedDroneList',
127 + }),
128 + },
129 + created() {
130 + },
131 + methods: {
132 + ...mapActions('Drone/drone', {
133 + setLogFilter: 'setLogFilter',
134 + clearLogFilter: 'clearLogFilter',
135 + }),
136 + clickClose() {
137 + this.$emit('clickClose');
138 + },
139 + changeManufacturer(value) {
140 + this.manufacturerValue = value;
141 + this.filteredDroneList = this.getFixedDroneList.filter((v) => !!this.manufacturerValue.find((e) => v.maker === e));
142 + },
143 + changeWeight(weight) {
144 + this.weightValue = weight;
145 + },
146 + changeAltitude(altitude) {
147 + this.altitudeValue = altitude;
148 + },
149 + changeSpeed(speed) {
150 + this.speedValue = speed;
151 + },
152 + applyFilter() {
153 + this.setLogFilter({
154 + checkFilter: true,
155 + maker: this.manufacturerValue,
156 + filteredDroneList: this.filteredDroneList,
157 + weight: this.weightValue,
158 + altitude: this.altitudeValue,
159 + speed: this.speedValue,
160 + filming: this.filmingValue,
161 + });
162 + },
163 + clickReset() {
164 + this.clearLogFilter();
165 + this.filteredDroneList = [];
166 + this.manufacturerValue = [];
167 + this.weightValue = [0, 50];
168 + this.altitudeValue = [0, 200];
169 + this.speedValue = [0, 100];
170 + this.filmingValue = [];
171 + },
172 + },
173 +};
174 +</script>
175 +<style lang="scss">
176 +</style>
1 +<template>
2 + <div>
3 + <img
4 + class="searchBtn"
5 + @click="clickSearchBtn"
6 + :src="require('@/static/img/search.png')"/>
7 + </div>
8 +</template>
9 +<script>
10 +
11 +import CloseBox from '@/components/_Common/CloseBox/closeBox';
12 +import { mapGetters } from 'vuex';
13 +
14 +export default {
15 + head() {
16 + return {
17 + title: 'Drone',
18 + meta: [
19 + {
20 + hid: 'database',
21 + name: 'Descriptions',
22 + content: 'DroneWeb-Content',
23 + },
24 + ],
25 + };
26 + },
27 + components: {
28 + CloseBox,
29 + },
30 + props: {},
31 + data() {
32 + return {
33 + searchValue: null,
34 + searchType: 'modelName',
35 + searchData: [],
36 + dataSource: [],
37 + searchMode: false,
38 + };
39 + },
40 + computed: {
41 + ...mapGetters('Etc', {
42 + getMakerDroneList: 'getMakerDroneList',
43 + }),
44 + ...mapGetters('Drone', {
45 + getLogFilter: 'drone/getLogFilter',
46 + getDroneLogs: 'drone/getDroneLogs',
47 + }),
48 + },
49 + created() {
50 + },
51 + methods: {
52 + async onSearch(value) {
53 + const params = {};
54 + params[this.searchType] = value;
55 + const result = await this.$store.dispatch('Drone/list/fetchListContents', params);
56 + const objectDrone = {};
57 + this.getDroneLogs.forEach((e) => {
58 + objectDrone[Number(e.droneId)] = true;
59 + });
60 + this.searchData = [...result.drones.filter((v) => objectDrone[Number(v.id)])];
61 + },
62 + clickSearchBtn() {
63 + this.$emit('changeFilterMode', false)
64 + this.$emit('changeSearchMode', true)
65 + },
66 + clickClose() {
67 + this.$emit('clickClose');
68 + this.searchMode = false;
69 + },
70 + clickListItem(item) {
71 + console.log('click', item);
72 + },
73 + },
74 +};
75 +</script>
76 +<style lang="scss">
77 +</style>
1 +<template>
2 + <div>
3 + <CloseBox
4 + @clickClose="clickClose"
5 + >
6 + <template v-slot:header>
7 + 드론 기체 검색
8 + </template>
9 + <template v-slot:body>
10 + <a-alert
11 + v-if="getLogFilter.checkFilter"
12 + message="필터가 적용된 상태입니다."
13 + type="info" show-icon
14 + class="filter-alert"
15 + />
16 + <div class="r-flex gap-default"
17 + :style="{marginTop: getLogFilter.checkFilter ? '10px' : '0px'}"
18 + >
19 + <a-select
20 + v-model="searchType"
21 + class="search-input"
22 + style="min-width: 80px"
23 + >
24 + <a-select-option value="modelName">
25 + 모델명
26 + </a-select-option>
27 + <a-select-option value="maker">
28 + 제조사
29 + </a-select-option>
30 + <a-select-option value="usageName">
31 + 용도
32 + </a-select-option>
33 + </a-select>
34 + <a-input-search
35 + placeholder="검색어"
36 + enter-button
37 + @search="onSearch"
38 + />
39 + </div>
40 +
41 + <a-list
42 + v-if="searchData.length"
43 + bordered
44 + :data-source="searchData">
45 + <a-list-item
46 + slot="renderItem"
47 + slot-scope="item"
48 + @click="() => clickListItem(item)"
49 + >
50 + {{ `${item.modelName} / ${item.maker} / ${item.usage || '*'}` }}
51 + </a-list-item>
52 + </a-list>
53 + </template>
54 + </CloseBox>
55 + </div>
56 +</template>
57 +<script>
58 +
59 +import CloseBox from '@/components/_Common/CloseBox/closeBox';
60 +import { mapGetters } from 'vuex';
61 +
62 +export default {
63 + head() {
64 + return {
65 + title: 'Drone',
66 + meta: [
67 + {
68 + hid: 'database',
69 + name: 'Descriptions',
70 + content: 'DroneWeb-Content',
71 + },
72 + ],
73 + };
74 + },
75 + components: {
76 + CloseBox,
77 + },
78 + props: {},
79 + data() {
80 + return {
81 + searchValue: null,
82 + searchType: 'modelName',
83 + searchData: [],
84 + dataSource: [],
85 + };
86 + },
87 + computed: {
88 + ...mapGetters('Etc', {
89 + getMakerDroneList: 'getMakerDroneList',
90 + }),
91 + ...mapGetters('Drone', {
92 + getLogFilter: 'drone/getLogFilter',
93 + getWholeDroneLog: 'drone/getWholeDroneLog',
94 + }),
95 + },
96 + created() {
97 +
98 + },
99 + methods: {
100 + onSearch(value) {
101 + const params = {};
102 + params[this.searchType] = value;
103 + this.$store.dispatch('Drone/list/fetchListContents', params).then((r) => {
104 + this.searchData = r.drones;
105 + });
106 + },
107 + handleSearch(value) {
108 +
109 + },
110 + clickClose() {
111 + this.$emit('clickClose');
112 + },
113 + clickListItem(item) {
114 + let checkValid = false;
115 + this.getWholeDroneLog.forEach((log) => {
116 + if (Number(log.droneId) === Number(item.id)) {
117 + this.$emit('focusChange', log.latitude, log.longitude);
118 + this.$emit('clickDrone', Number(item.id));
119 + checkValid = true;
120 + }
121 + });
122 + if (!checkValid) {
123 + this.$warning({
124 + title: '선택하신 드론은 현재 가동 중이지 않은 드론입니다.',
125 + });
126 + }
127 + },
128 + },
129 +};
130 +</script>
131 +<style lang="scss">
132 +</style>
1 +<template>
2 + <div>
3 + <l-marker
4 + ref="markersRef"
5 + :lat-lng="[latitude, longitude]"
6 + @click="clickDrone"
7 + >
8 + <l-icon
9 + :icon-size="iconSize"
10 + :icon-anchor="[20, 20]"
11 + :icon-url="icon"
12 + ></l-icon>
13 + </l-marker>
14 + <l-polyline :lat-lngs="drone.polyline.latlngs" :color="drone.polyline.color"></l-polyline>
15 + </div>
16 +</template>
17 +
18 +<script>
19 +
20 +import { mapGetters } from 'vuex';
21 +
22 +export default {
23 + head() {
24 + return {
25 + title: 'Drone',
26 + meta: [
27 + {
28 + hid: 'database',
29 + name: 'Descriptions',
30 + content: 'DroneWeb-Content',
31 + },
32 + ],
33 + };
34 + },
35 + props: {
36 + latitude: {
37 + type: Number,
38 + default: 0,
39 + },
40 + longitude: {
41 + type: Number,
42 + default: 0,
43 + },
44 + icon: {
45 + default: null,
46 + },
47 + },
48 + data() {
49 + return {
50 + drone: {
51 + latitude: 37.2430125,
52 + longitude: 127.0811054,
53 + polyline: {
54 + latlngs: [],
55 + color: 'red',
56 + },
57 + },
58 + };
59 + },
60 + mounted() {
61 + this.$nextTick(() => {
62 + // console.log('hi', this.$refs.markersRef)
63 + // this.markerObjects = this.$refs.markersRef.map(ref => ref.mapObject);
64 + // console.log(this.markerObjects)
65 + });
66 + },
67 + computed: {
68 + ...mapGetters('Drone/Map', {
69 + getZoomLevel: 'getZoomLevel',
70 + }),
71 + iconSize() {
72 + if (this.getZoomLevel == null) return [40, 40];
73 + return [this.getZoomLevel * 2.2, this.getZoomLevel * 2.2];
74 + },
75 + },
76 + created() {
77 + // this.makeDronePath();
78 + },
79 + methods: {
80 + clickDrone() {
81 + this.$emit('clickDrone');
82 + },
83 + makeDronePath() {
84 + // setTimeout(() => {
85 + // this.drone.latitude += 0.00005;
86 + // // this.drone.longitude += 0.00005;
87 + // this.drone.polyline.latlngs.push([this.drone.latitude, this.drone.longitude]);
88 + // this.makeDronePath();
89 + // }, 500);
90 + },
91 + },
92 +};
93 +</script>
1 +<template>
2 + <a-page-header
3 + class="page-header"
4 + title="RT-Map"
5 + sub-title="Realtime Drone Simulation Map"
6 + >
7 + <template slot="extra">
8 + <a-tooltip placement="bottom">
9 + <template slot="title">
10 + <div>맵이 중앙에 위치하도록 </div>
11 + <div>스크롤을 이동합니다.</div>
12 + </template>
13 + <a-button type="primary" icon="select"
14 + @click="moveScroll"
15 + >
16 + Move Scroll
17 + </a-button>
18 + </a-tooltip>
19 + </template>
20 + </a-page-header>
21 +</template>
22 +
23 +<script>
24 +
25 +export default {
26 + name: 'MainHeader',
27 + components: {},
28 + data() {
29 + return {
30 + };
31 + },
32 + methods: {
33 + moveScroll() {
34 + window.scrollTo(0, 105);
35 + },
36 + },
37 +};
38 +</script>
39 +
40 +<style scoped lang="scss">
41 +
42 +</style>
1 +# COMPONENTS
2 +
3 +**This directory is not required, you can delete it if you don't want to use it.**
4 +
5 +The components directory contains your Vue.js Components.
6 +
7 +_Nuxt.js doesn't supercharge these components._
1 +<template>
2 + <div v-if="chartData != null">
3 + <highcharts :options="options"
4 + :callback="onChartLoaded"
5 + :class="chartClass"
6 + :style="borderStyle"
7 + />
8 + </div>
9 +</template>
10 +
11 +<script>
12 +import { Chart } from 'highcharts-vue';
13 +
14 +export default {
15 + name: 'barChart',
16 + components: {
17 + highcharts: Chart,
18 + },
19 + props: {
20 + chartData: {
21 + type: Array,
22 + default: null,
23 + },
24 + chartSettings: {
25 + type: Object,
26 + default: null,
27 + },
28 + chartClass: {
29 + type: String,
30 + default: null,
31 + },
32 + bordered: {
33 + type: Boolean,
34 + default: null,
35 + },
36 + },
37 + data() {
38 + return {
39 + chart: null,
40 + options: null,
41 + borderStyle: {},
42 + };
43 + },
44 + watch: {
45 + chartData: {
46 + deep: true,
47 + handler(val) {
48 + if (val == null) return;
49 + this.options.series = val;
50 +
51 + if (this.chart == null) return;
52 + this.chart.redraw();
53 + this.chart.setSize(null);
54 + },
55 + },
56 + chartSettings: {
57 + deep: true,
58 + handler(val) {
59 + if (val == null) return;
60 + this.setCustomChartSetting(val);
61 +
62 + if (this.chart == null) return;
63 + this.chart.redraw();
64 + this.chart.setSize(null);
65 + },
66 + },
67 + },
68 + created() {
69 + this.options.series = this.chartData;
70 + this.setCustomChartSetting(this.chartSettings);
71 +
72 + if (this.bordered) {
73 + this.borderStyle = {
74 + border: '2px solid #dde2ec',
75 + };
76 + }
77 + },
78 + methods: {
79 + onChartLoaded(chart) {
80 + if (this.chart == null) {
81 + this.chart = chart;
82 + }
83 +
84 + this.chart.zoomOut(false);
85 + this.chart.redraw();
86 + this.chart.setSize(null);
87 + },
88 + /**
89 + * Label Formatter로 Html로 레이블 설정 가능
90 + * 자세한 설정은 https://api.highcharts.com/highcharts/plotOptions
91 + * @param settings
92 + */
93 + setCustomChartSetting(settings) {
94 + this.options.chart = {
95 + type: 'bar',
96 + };
97 + if (settings == null) return;
98 + this.options = { ...settings, series: this.chartData };
99 + this.options.chart.type = 'bar';
100 + /*
101 + * dataLabel 설정
102 + */
103 + // if (settings.dataLabelSuffix) {
104 + // this.options.plotOptions.column.dataLabels.formatter = function () {
105 + // return `${this.key}: ${this.y.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,')}${settings.dataLabelSuffix}`;
106 + // };
107 + // }
108 + },
109 + },
110 +};
111 +</script>
112 +
113 +<style scoped>
114 +
115 +</style>
1 +<template>
2 + <div v-if="chartData != null">
3 + <highcharts :options="options"
4 + :callback="onChartLoaded"
5 + :class="chartClass"
6 + :style="borderStyle"
7 + />
8 + </div>
9 +</template>
10 +
11 +<script>
12 +import { Chart } from 'highcharts-vue';
13 +
14 +export default {
15 + name: 'columnChart',
16 + components: {
17 + highcharts: Chart,
18 + },
19 + props: {
20 + chartData: {
21 + type: Array,
22 + default: null,
23 + },
24 + chartSettings: {
25 + type: Object,
26 + default: null,
27 + },
28 + chartClass: {
29 + type: String,
30 + default: null,
31 + },
32 + bordered: {
33 + type: Boolean,
34 + default: null,
35 + },
36 + },
37 + data() {
38 + return {
39 + chart: null,
40 + options: {},
41 + borderStyle: {},
42 + };
43 + },
44 + watch: {
45 + chartData: {
46 + deep: true,
47 + handler(val) {
48 + if (val == null) return;
49 + this.options.series = val;
50 +
51 + if (this.chart == null) return;
52 + this.chart.redraw();
53 + this.chart.setSize(null);
54 + },
55 + },
56 + chartSettings: {
57 + deep: true,
58 + handler(val) {
59 + if (val == null) return;
60 + this.setCustomChartSetting(val);
61 +
62 + if (this.chart == null) return;
63 + this.chart.redraw();
64 + this.chart.setSize(null);
65 + },
66 + },
67 + },
68 + created() {
69 + this.setCustomChartSetting(this.chartSettings);
70 + if (this.bordered) {
71 + this.borderStyle = {
72 + border: '2px solid #dde2ec',
73 + };
74 + }
75 + },
76 + methods: {
77 + onChartLoaded(chart) {
78 + if (this.chart == null) { this.chart = chart; }
79 +
80 + this.chart.zoomOut(false);
81 + this.chart.redraw();
82 + this.chart.setSize(null);
83 + },
84 + /**
85 + * Label Formatter로 Html로 레이블 설정 가능
86 + * 자세한 설정은 https://api.highcharts.com/highcharts/plotOptions
87 + * @param settings
88 + */
89 + setCustomChartSetting(settings) {
90 + this.options.chart = {
91 + type: 'column',
92 + };
93 + if (settings == null) return;
94 + this.options = { ...settings, series: this.chartData };
95 + this.options.chart.type = 'column';
96 + },
97 + },
98 +};
99 +</script>
100 +
101 +<style scoped>
102 +
103 +</style>
1 +<template>
2 + <div v-if="chartData != null">
3 + <highcharts :options="options"
4 + :callback="onChartLoaded"
5 + :class="chartClass"
6 + :style="borderStyle"
7 + />
8 + </div>
9 +</template>
10 +
11 +<script>
12 +import { Chart } from 'highcharts-vue';
13 +
14 +export default {
15 + name: 'lineChart',
16 + components: {
17 + highcharts: Chart,
18 + },
19 + props: {
20 + chartData: {
21 + type: Array,
22 + default: null,
23 + },
24 + chartSettings: {
25 + type: Object,
26 + default: null,
27 + },
28 + chartClass: {
29 + type: String,
30 + default: null,
31 + },
32 + bordered: {
33 + type: Boolean,
34 + default: null,
35 + },
36 + },
37 + data() {
38 + return {
39 + chart: null,
40 + options: {},
41 + borderStyle: {},
42 + };
43 + },
44 + watch: {
45 + chartData: {
46 + deep: true,
47 + handler(val) {
48 + if (val == null) return;
49 + this.options.series = val;
50 +
51 + if (this.chart == null) return;
52 + this.chart.redraw();
53 + this.chart.setSize(null);
54 + },
55 + },
56 + chartSettings: {
57 + deep: true,
58 + handler(val) {
59 + if (val == null) return;
60 + this.setCustomChartSetting(val);
61 +
62 + if (this.chart == null) return;
63 + this.chart.redraw();
64 + this.chart.setSize(null);
65 + },
66 + },
67 + },
68 + created() {
69 + this.options.series = this.chartData;
70 + this.setCustomChartSetting(this.chartSettings);
71 + if (this.bordered) {
72 + this.borderStyle = {
73 + border: '2px solid #dde2ec',
74 + };
75 + }
76 + },
77 + methods: {
78 + onChartLoaded(chart) {
79 + if (this.chart == null) { this.chart = chart; }
80 +
81 + this.chart.zoomOut(false);
82 + this.chart.redraw();
83 + this.chart.setSize(null);
84 + },
85 + /**
86 + * Label Formatter로 Html로 레이블 설정 가능
87 + * 자세한 설정은 https://api.highcharts.com/highcharts/plotOptions
88 + * @param settings
89 + */
90 + setCustomChartSetting(settings) {
91 + this.options.chart = {
92 + type: 'line',
93 + };
94 + if (settings == null) return;
95 + this.options = { ...settings, series: this.chartData };
96 + this.options.chart.type = 'line';
97 + },
98 + },
99 +};
100 +</script>
101 +
102 +<style scoped>
103 +
104 +</style>
1 +<template>
2 + <div v-if="chartData != null">
3 + <highcharts :options="options"
4 + :callback="onChartLoaded"
5 + :class="chartClass"
6 + :style="borderStyle"
7 + />
8 + </div>
9 +</template>
10 +
11 +<script>
12 +import { Chart } from 'highcharts-vue';
13 +
14 +export default {
15 + name: 'liveLineChart',
16 + components: {
17 + highcharts: Chart,
18 + },
19 + props: {
20 + chartData: {
21 + type: Array,
22 + default: null,
23 + },
24 + chartSettings: {
25 + type: Object,
26 + default: null,
27 + },
28 + chartClass: {
29 + type: String,
30 + default: null,
31 + },
32 + bordered: {
33 + type: Boolean,
34 + default: null,
35 + },
36 + },
37 + data() {
38 + return {
39 + chart: null,
40 + options: {},
41 + borderStyle: {},
42 + };
43 + },
44 + watch: {
45 + chartSettings: {
46 + deep: true,
47 + handler(val) {
48 + if (val == null) return;
49 + this.setCustomChartSetting(val);
50 +
51 + if (this.chart == null) return;
52 + this.chart.redraw();
53 + this.chart.setSize(null);
54 + },
55 + },
56 + },
57 + created() {
58 + this.options.series = this.chartData;
59 + this.setCustomChartSetting(this.chartSettings);
60 + if (this.bordered) {
61 + this.borderStyle = {
62 + border: '2px solid #dde2ec',
63 + };
64 + }
65 + },
66 + methods: {
67 + onChartLoaded(chart) {
68 + if (this.chart == null) { this.chart = chart; }
69 +
70 + this.chart.zoomOut(false);
71 + this.chart.redraw();
72 + this.chart.setSize(null);
73 + },
74 + /**
75 + * Label Formatter로 Html로 레이블 설정 가능
76 + * 자세한 설정은 https://api.highcharts.com/highcharts/plotOptions
77 + * @param settings
78 + */
79 + setCustomChartSetting(settings) {
80 + this.options.chart = {
81 + type: 'line',
82 + };
83 + if (settings == null) return;
84 + this.options = { ...settings, series: this.chartData };
85 + this.options.chart.type = 'line';
86 + },
87 + },
88 +};
89 +</script>
90 +
91 +<style scoped>
92 +
93 +</style>
1 +<template>
2 + <div v-if="chartData != null">
3 + <highcharts :options="options"
4 + :callback="onChartLoaded"
5 + :class="chartClass"
6 + :style="borderStyle"
7 + />
8 + </div>
9 +</template>
10 +
11 +<script>
12 +import { Chart } from 'highcharts-vue';
13 +
14 +export default {
15 + name: 'pieChart',
16 + components: {
17 + highcharts: Chart,
18 + },
19 + props: {
20 + chartData: {
21 + type: Array,
22 + default: null,
23 + },
24 + chartSettings: {
25 + type: Object,
26 + default: null,
27 + },
28 + chartClass: {
29 + type: String,
30 + default: null,
31 + },
32 + bordered: {
33 + type: Boolean,
34 + default: false,
35 + },
36 + donut: {
37 + type: Boolean,
38 + default: false,
39 + },
40 + },
41 + data() {
42 + return {
43 + chart: null,
44 + options: {},
45 + isDataLabelAdded: false,
46 + borderStyle: {},
47 + };
48 + },
49 + watch: {
50 + chartData: {
51 + deep: true,
52 + handler(val) {
53 + if (val == null) return;
54 + this.options.series = val;
55 + if (this.donut && val.length > 0) {
56 + this.options.series[0].innerSize = '50%';
57 + }
58 +
59 + if (this.chart == null) return;
60 + this.chart.redraw();
61 + this.chart.setSize(null);
62 + },
63 + },
64 + chartSettings: {
65 + deep: true,
66 + handler(val) {
67 + if (val == null) return;
68 + this.setCustomChartSetting(val);
69 +
70 + if (this.chart == null) return;
71 + this.chart.redraw();
72 + this.chart.setSize(null);
73 + },
74 + },
75 + 'chartSettings.subtitle': {
76 + deep: true,
77 + handler(val) {
78 + if (val == null) return;
79 + this.setCustomChartSetting(val);
80 +
81 + if (this.chart == null) return;
82 + this.chart.redraw();
83 + this.chart.setSize(null);
84 + },
85 + },
86 + },
87 + created() {
88 + this.setCustomChartSetting(this.chartSettings);
89 + if (this.bordered) {
90 + this.borderStyle = {
91 + border: '2px solid #dde2ec',
92 + };
93 + }
94 + },
95 + methods: {
96 + onChartLoaded(chart) {
97 + if (this.chart == null) {
98 + this.chart = chart;
99 + }
100 + this.chart.redraw();
101 + this.chart.setSize(null);
102 + },
103 + /**
104 + * Label Formatter로 Html로 레이블 설정 가능
105 + * 자세한 설정은 https://api.highcharts.com/highcharts/plotOptions
106 + * @param settings
107 + */
108 + setCustomChartSetting(settings) {
109 + this.options.chart = {
110 + type: 'pie',
111 + };
112 + if (settings == null) return;
113 + this.options = { ...settings, series: this.chartData };
114 + this.options.chart.type = 'pie';
115 + },
116 + },
117 +};
118 +</script>
119 +
120 +<style scoped>
121 +
122 +</style>
1 +<template>
2 + <div class="ant-modal-content">
3 + <button
4 + type="button"
5 + aria-label="Close"
6 + class="ant-modal-close"
7 + @click="clickClose"
8 + >
9 + <span class="ant-modal-close-x">
10 + <a-icon style="color: white" type="close" />
11 + </span>
12 + </button>
13 + <div class="ant-modal-header">
14 + <div class="ant-modal-title">
15 + <slot name="header"></slot>
16 + </div>
17 + </div>
18 + <div class="ant-modal-body">
19 + <slot name="body"></slot>
20 + </div>
21 + </div>
22 +</template>
23 +<script>
24 +
25 +export default {
26 + head() {
27 + return {
28 + title: 'Drone',
29 + meta: [
30 + {
31 + hid: 'database',
32 + name: 'Descriptions',
33 + content: 'DroneWeb-Content',
34 + },
35 + ],
36 + };
37 + },
38 + props: {
39 + },
40 + data() {
41 + return {
42 + };
43 + },
44 + computed: {
45 + },
46 + created() {
47 + },
48 + methods: {
49 + clickClose() {
50 + this.$emit('clickClose')
51 + },
52 + },
53 +};
54 +</script>
55 +<style lang="scss">
56 +</style>
1 +# _COMMON
2 +
3 +- 두개 이상의 페이지에서 사용되는 공통 컴포넌트를 저장하는 폴더입니다.
4 +- 하나의 페이지에서만 사용하는 컴포넌트는 src/component 하위에 폴더로 작성해주시기 바랍니다.
1 +<template>
2 + <svg class="NuxtLogo" width="245" height="180" viewBox="0 0 452 342" xmlns="http://www.w3.org/2000/svg">
3 + <path
4 + d="M139 330l-1-2c-2-4-2-8-1-13H29L189 31l67 121 22-16-67-121c-1-2-9-14-22-14-6 0-15 2-22 15L5 303c-1 3-8 16-2 27 4 6 10 12 24 12h136c-14 0-21-6-24-12z"
5 + fill="#00C58E"
6 + />
7 + <path
8 + d="M447 304L317 70c-2-2-9-15-22-15-6 0-15 3-22 15l-17 28v54l39-67 129 230h-49a23 23 0 0 1-2 14l-1 1c-6 11-21 12-23 12h76c3 0 17-1 24-12 3-5 5-14-2-26z"
9 + fill="#108775"
10 + />
11 + <path
12 + d="M376 330v-1l1-2c1-4 2-8 1-12l-4-12-102-178-15-27h-1l-15 27-102 178-4 12a24 24 0 0 0 2 15c4 6 10 12 24 12h190c3 0 18-1 25-12zM256 152l93 163H163l93-163z"
13 + fill="#2F495E"
14 + />
15 + </svg>
16 +</template>
17 +
18 +<script>
19 +export default {
20 + name: 'Logo',
21 +};
22 +</script>
23 +
24 +<style>
25 +.NuxtLogo {
26 + animation: 1s appear;
27 + margin: auto;
28 +}
29 +
30 +@keyframes appear {
31 + 0% {
32 + opacity: 0;
33 + }
34 +}
35 +</style>
1 +# LAYOUTS
2 +
3 +**This directory is not required, you can delete it if you don't want to use it.**
4 +
5 +This directory contains your Application Layouts.
6 +
7 +More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/views#layouts).
1 +<template>
2 + <div>
3 + <span>error</span>
4 + <Nuxt />
5 + </div>
6 +</template>
1 +<template>
2 + <a-config-provider>
3 + <a-layout :style="{height: '100%'}">
4 + <a-layout-header class="header-style">
5 + <div class="r-flex gap-2 space-between">
6 + <div class="r-flex start">
7 + <div style="margin-right: 20px">
8 + <a-icon class="logo-title" type="compass" />
9 + <span class="logo-title">Drone Web</span>
10 + </div>
11 + <a-menu
12 + theme="dark"
13 + mode="horizontal"
14 + v-model="tabKey"
15 + :style="{lineHeight: '40px'}"
16 + >
17 + <a-menu-item :key="1" class="router-menu-item">
18 + <nuxt-link to="/">
19 + <a-icon type="environment" />
20 + RT-Map
21 + </nuxt-link>
22 + </a-menu-item>
23 + <a-menu-item :key="2" class="router-menu-item router-menu-margin">
24 + <a-dropdown>
25 + <a
26 + class="ant-dropdown-link"
27 + @click="(e) => e.preventDefault()"
28 + >
29 + <a-icon type="database" />
30 + DataBase
31 + </a>
32 + <a-menu
33 + v-model="dbMenu"
34 + slot="overlay"
35 + @click="hoverClick"
36 + :style="{textAlign: 'center'}"
37 + class="router-database"
38 + >
39 + <a-menu-item :key="1">
40 + Drone
41 + </a-menu-item>
42 + <a-menu-item :key="2">
43 + Schedule
44 + </a-menu-item>
45 + <a-menu-item :key="3">
46 + Log
47 + </a-menu-item>
48 + </a-menu>
49 + </a-dropdown>
50 + </a-menu-item>
51 + <a-menu-item :key="3" class="router-menu-item" disabled>
52 + <!-- <nuxt-link to="/analytics">
53 + <a-icon type="dashboard" />
54 + Analytics
55 + </nuxt-link> -->
56 + <div @click="info()">
57 + <a-icon type="dashboard" />
58 + Analytics
59 + </div>
60 + </a-menu-item>
61 + <!-- <a-menu-item :key="4" class="router-menu-item">
62 + <nuxt-link to="/websocket">
63 + websocket
64 + </nuxt-link>
65 + </a-menu-item> -->
66 + </a-menu>
67 + </div>
68 + <div class="nav-etc">
69 + <a-dropdown>
70 + <a-button
71 + type="dash"
72 + icon="setting"
73 + style="background-color: rgba(245, 245, 245, 0.9); border: 2px solid #e4e9f0; color: #001529"
74 + />
75 + <a-menu
76 + slot="overlay"
77 + @click="clickEtc"
78 + class="router-database"
79 + >
80 + <a-menu-item-group style="text-align: center">
81 + WebSocket
82 + </a-menu-item-group>
83 + <a-menu-divider style="margin-top: 8px" />
84 + <a-menu-item key="connect">
85 + <a-icon type="link"/>Connect
86 + </a-menu-item>
87 + <a-menu-item key="disconnect">
88 + <a-icon type="disconnect"/>Disconnect
89 + </a-menu-item>
90 + </a-menu>
91 + </a-dropdown>
92 + </div>
93 + </div>
94 + </a-layout-header>
95 + <a-layout-content :style="{padding: '0 20px', marginTop: '84px'}">
96 + <div :style="{minHeight: 'calc(100vh - 84px - 89px)', height: '100%'}">
97 + <nuxt keep-alive />
98 + </div>
99 + </a-layout-content>
100 + <a-layout-footer class="footer-style">
101 + <layout-footer />
102 + </a-layout-footer>
103 + </a-layout>
104 + </a-config-provider>
105 +</template>
106 +
107 +<script>
108 +import LayoutFooter from '@/components/Layout/footer';
109 +import { mapActions, mapGetters } from 'vuex';
110 +
111 +export default {
112 + components: { LayoutFooter },
113 + /* vue-meta -> 각 페이지의 meta */
114 + head() {
115 + return {
116 + titleTemplate: 'Drone Web - %s',
117 + meta: [
118 + {
119 + hid: 'defaultLayout',
120 + name: 'Descriptions',
121 + content: 'Content',
122 + },
123 + ],
124 + };
125 + },
126 + data() {
127 + return {
128 + tabKey: [1],
129 + dbMenu: [0],
130 + etcAction: 'connect',
131 + };
132 + },
133 + watch: {
134 + '$route.fullPath': {
135 + handler() {
136 + this.changeMenuRoute();
137 + },
138 + },
139 + },
140 + created() {
141 + this.changeMenuRoute();
142 + this.$store.dispatch('Drone/list/fetchListContents').then((r) => {
143 + const makerList = {};
144 + const { drones } = r;
145 + for (let i = 0; i < drones.length; i += 1) {
146 + if (makerList[drones[i].maker]) makerList[drones[i].maker].push(drones[i]);
147 + else makerList[drones[i].maker] = [drones[i]];
148 + }
149 + this.$store.dispatch('Drone/list/setFixedDroneList', drones);
150 + this.$store.dispatch('Etc/setMakers', makerList);
151 + });
152 + },
153 + computed: {
154 + ...mapGetters('Schedule/page', {
155 + getSchdulePageParams: 'getPageParams',
156 + }),
157 + ...mapGetters('Log/page', {
158 + getLogPageParams: 'getPageParams',
159 + }),
160 + },
161 + methods: {
162 + changeMenuRoute() {
163 + const pageRoute = this.$route.fullPath.split('/')[1];
164 + switch (pageRoute) {
165 + case '':
166 + this.tabKey = [1];
167 + this.dbMenu = [0];
168 + break;
169 + case 'database':
170 + this.tabKey = [2];
171 + break;
172 + case 'analytics':
173 + this.tabKey = [3];
174 + this.dbMenu = [0];
175 + break;
176 + default:
177 + this.tabKey = [999];
178 + this.dbMenu = [0];
179 + break;
180 + }
181 + },
182 + hoverClick(data) {
183 + const { key } = data;
184 + this.tabKey = [2];
185 +
186 + switch (key) {
187 + case 1:
188 + this.dbMenu = [1];
189 + this.$router.push('/database/drone');
190 + break;
191 + case 2:
192 + this.clearSchedulePageParams();
193 + this.fetchSchedulePageData(this.getSchdulePageParams);
194 + this.dbMenu = [2];
195 + this.$router.push('/database/schedule');
196 + break;
197 + case 3:
198 + this.clearLogPageParams();
199 + this.fetchLogPageData(this.getLogPageParams);
200 + this.dbMenu = [3];
201 + this.$router.push('/database/log');
202 + break;
203 + default:
204 + break;
205 + }
206 + },
207 + ...mapActions('Schedule/page', {
208 + fetchSchedulePageData: 'fetchPageData',
209 + clearSchedulePageParams: 'clearPageParams',
210 + }),
211 + ...mapActions('Log/page', {
212 + fetchLogPageData: 'fetchPageData',
213 + clearLogPageParams: 'clearPageParams',
214 + }),
215 + info() {
216 + const h = this.$createElement;
217 + this.$info({
218 + title: 'Notification',
219 + content: h('div', {}, [h('p', '개발 예정인 기능입니다.')]),
220 + onOk() {},
221 + });
222 + },
223 + clickEtc(e) {
224 + if (e === 'connect') this.connect();
225 + else this.disconnect();
226 + },
227 + },
228 +};
229 +</script>
230 +
231 +<style lang="scss" scoped>
232 +@import '@/assets/styles/mixins.scss';
233 +.logo-title {
234 + color: white;
235 + font-size: 20px;
236 + margin-left: 20px;
237 +}
238 +.router-menu-item {
239 + border-radius: 8px;
240 +}
241 +.router-menu-margin {
242 + margin: 0 10px;
243 +}
244 +.header-style {
245 + position: fixed;
246 + z-index: 100;
247 + width: 100%;
248 + padding: 0px;
249 +}
250 +.nav-etc {
251 + margin-right: 20px;
252 + line-height: 30px;
253 +}
254 +.footer-style {
255 + background-color: #001529;
256 + text-align: center;
257 + margin: 20px 15px 0px 15px;
258 + border-radius: 6px 6px 0px 0px;
259 + padding: 12px 0px 6px 0px;
260 +}
261 +
262 +.ant-dropdown-menu-item-selected,
263 +.ant-dropdown-menu-submenu-title-selected,
264 +.ant-dropdown-menu-item-selected > a,
265 +.ant-dropdown-menu-submenu-title-selected > a {
266 + background: none;
267 +}
268 +</style>
1 +<template>
2 + <div class="error-main">
3 + <a-result
4 + style="margin-top: -50px"
5 + :status="this.error.statusCode.toString()"
6 + :title="this.error.statusCode.toString()"
7 + :sub-title="this.error.message"
8 + >
9 + <template #extra>
10 + <a-button type="primary" @click="$router.push('/')">
11 + Go to Main
12 + </a-button>
13 + </template>
14 + </a-result>
15 + </div>
16 +</template>
17 +
18 +<script>
19 +export default {
20 + props: {
21 + error: {
22 + type: Object,
23 + default: () => ({}),
24 + },
25 + },
26 +};
27 +</script>
28 +
29 +<style lang="scss" scoped>
30 +.error-main {
31 + background-color: white;
32 + height: calc(100vh - 84px - 69px);
33 + display: flex;
34 + align-items: center;
35 + justify-content: center;
36 +}
37 +</style>
1 +# MIDDLEWARE
2 +
3 +**This directory is not required, you can delete it if you don't want to use it.**
4 +
5 +This directory contains your application middleware.
6 +Middleware let you define custom functions that can be run before rendering either a page or a group of pages.
7 +
8 +More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware).
1 +/* eslint-disable func-names */
2 +export default function ({
3 + store, route, redirect,
4 +}) {
5 + // console.log('in middleware', store, route, redirect);
6 +}
1 +export default function (moduleOptions) {
2 + this.options.css.push('vuelayers/lib/style.css');
3 +}
1 +# PAGES
2 +
3 +This directory contains your Application Views and Routes.
4 +The framework reads all the `*.vue` files inside this directory and creates the router of your application.
5 +
6 +More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing).
1 +<template>
2 + <div>
3 + <analytics-header/>
4 + <div class="chart-div">
5 + <live-log-chart :chart-data="liveDroneData"/>
6 + </div>
7 + <div class="r-flex">
8 + <div class="pie-chart-div" style="margin-right: 20px">
9 + <maker-pie-chart :chart-data="makerData"/>
10 + </div>
11 + <div class="pie-chart-div">
12 + <drone-category-pie-chart :chart-data="categoryData" />
13 + </div>
14 + </div>
15 + <div class="chart-div">
16 + <time-category-column-chart :chart-data="timeDroneData"/>
17 + </div>
18 +
19 + <div class="page-main">
20 + <div>
21 + 소켓 테스트
22 + </div>
23 + <a-button @click="getMessage">emit</a-button>
24 + <a-button @click="onM">on</a-button>
25 + </div>
26 + </div>
27 +</template>
28 +
29 +<script>
30 +import AnalyticsHeader from '@/components/Analytics/header';
31 +import MakerPieChart from '@/components/Analytics/Chart/makerPieChart';
32 +import DroneCategoryPieChart from '@/components/Analytics/Chart/droneCategoryPieChart';
33 +import TimeCategoryColumnChart from '@/components/Analytics/Chart/timeCategoryColumnChart';
34 +import LiveLogChart from '@/components/Analytics/Chart/liveLogChart';
35 +
36 +export default {
37 + components: {
38 + LiveLogChart,
39 + TimeCategoryColumnChart,
40 + AnalyticsHeader,
41 + DroneCategoryPieChart,
42 + MakerPieChart,
43 + },
44 + data() {
45 + return {
46 + socket: null,
47 + makerData: [],
48 + categoryData: [],
49 + timeDroneData: [],
50 + liveDroneData: [{
51 + name: '드론 기체 수',
52 + data: [],
53 + }],
54 + };
55 + },
56 + computed: {
57 + ranNum() {
58 + return (min, max) => parseInt((Math.random() * (max - min) + min), 10);
59 + },
60 + },
61 + created() {
62 + this.makeChartData();
63 + },
64 + mounted() {
65 + this.socket = this.$nuxtSocket({
66 + // nuxt-socket-io opts:
67 + name: 'main', // Use socket "home"
68 + channel: '/testSoc',
69 + // socket.io-client opts:
70 + reconnection: false,
71 + });
72 + this.socket
73 + .on('receiveLog', (msg, cb) => {
74 + /* Handle event */
75 + console.log(msg);
76 + this.liveDroneData[0].data.push({
77 + x: this.$dayjs(msg.time).valueOf(), y: msg.num,
78 + });
79 + });
80 + this.socket
81 + .on('connection', (msg, cb) => {
82 + /* Handle event */
83 + console.log(msg, cb);
84 + });
85 + },
86 + methods: {
87 + getMessage() {
88 + this.socket.emit('getMessage', { id: 'abc123' }, (resp) => {
89 + this.messageRxd = resp;
90 + });
91 + },
92 + onM() {
93 + this.$axios.get('http://localhost:8888')
94 + .then((r) => {
95 + console.log('apiCall');
96 + });
97 + },
98 + makeChartData() {
99 + this.makerData = [{
100 + name: '기체수',
101 + colorByPoint: true,
102 + data: [
103 + {
104 + name: 'DJI', y: 6234,
105 + },
106 + {
107 + name: '3DRobotics', y: 1248,
108 + },
109 + {
110 + name: 'SYMA', y: 248,
111 + },
112 + {
113 + name: 'Parrot', y: 783,
114 + },
115 + {
116 + name: 'Cheerson', y: 563,
117 + },
118 + ],
119 + }];
120 + this.categoryData = [{
121 + name: '기체수',
122 + colorByPoint: true,
123 + data: [
124 + {
125 + name: 'TriCopter', y: 3452,
126 + },
127 + {
128 + name: 'QuadCopter', y: 4032,
129 + },
130 + {
131 + name: 'HexaCopter', y: 1395,
132 + },
133 + {
134 + name: 'OctaCopter', y: 474,
135 + },
136 + ],
137 + }];
138 +
139 + this.timeDroneData = [
140 + {
141 + name: 'Toy',
142 + data: [
143 + this.ranNum(1, 100),
144 + this.ranNum(1, 100),
145 + this.ranNum(100, 300),
146 + this.ranNum(100, 300),
147 + this.ranNum(50, 200),
148 + this.ranNum(1, 100),
149 + ],
150 + },
151 + {
152 + name: 'Racing',
153 + data: [
154 + this.ranNum(1, 100),
155 + this.ranNum(1, 100),
156 + this.ranNum(100, 300),
157 + this.ranNum(100, 300),
158 + this.ranNum(50, 200),
159 + this.ranNum(1, 100),
160 + ],
161 + },
162 + {
163 + name: 'Mission',
164 + data: [
165 + this.ranNum(100, 1000),
166 + this.ranNum(100, 1000),
167 + this.ranNum(1, 200),
168 + this.ranNum(1, 200),
169 + this.ranNum(1, 200),
170 + this.ranNum(100, 1000),
171 + ],
172 + },
173 + ];
174 + },
175 + },
176 +};
177 +</script>
178 +
179 +<style scoped lang="scss">
180 +@import '@/assets/styles/mixins.scss';
181 +
182 +.pie-chart-div {
183 + margin-top: 20px;
184 + height: 440px;
185 + width: calc(50% - 10px);
186 + padding: 20px 20px 20px 20px;
187 + background-color: white;
188 + border-radius: 6px;
189 + border: $gray-2 1px solid;
190 +}
191 +.chart-div {
192 + margin-top: 20px;
193 + height: 440px;
194 + padding: 20px 20px 20px 20px;
195 + background-color: white;
196 + border-radius: 6px;
197 + border: $gray-2 1px solid;
198 +}
199 +</style>
1 +<template>
2 + <div>
3 + 403 Error Page
4 + </div>
5 +</template>
6 +
7 +<script>
8 +export default {
9 + layout: 'auth',
10 + data() {
11 + return {
12 +
13 + };
14 + },
15 + computed: {
16 +
17 + },
18 + watch: {
19 +
20 + },
21 + created() {
22 +
23 + },
24 + method: {
25 +
26 + },
27 +};
28 +</script>
1 +<template>
2 + <div>
3 + 404 Error Page
4 + </div>
5 +</template>
6 +
7 +<script>
8 +export default {
9 + layout: 'auth',
10 + data() {
11 + return {
12 +
13 + };
14 + },
15 + computed: {
16 +
17 + },
18 + watch: {
19 +
20 + },
21 + created() {
22 +
23 + },
24 + method: {
25 +
26 + },
27 +};
28 +</script>
1 +<template>
2 + <div>
3 + 500 Error Page
4 + </div>
5 +</template>
6 +
7 +<script>
8 +export default {
9 + layout: 'auth',
10 + data() {
11 + return {
12 +
13 + };
14 + },
15 + computed: {
16 +
17 + },
18 + watch: {
19 +
20 + },
21 + created() {
22 +
23 + },
24 + method: {
25 +
26 + },
27 +};
28 +</script>
1 +<template>
2 + <div>
3 + <database-detail-header />
4 + <div class="page-main-without-header ">
5 + <div class="table-title">Schedule</div>
6 + <database-table :childLoading="parentLoading" @loadData="loadData" />
7 + </div>
8 + </div>
9 +</template>
10 +
11 +<script>
12 +import DatabaseDetailHeader from '@/components/Database/Drone/Detail/header';
13 +import DatabaseTable from '@/components/Database/Schedule/table';
14 +import {mapActions, mapGetters} from 'vuex';
15 +
16 +export default {
17 + components: {
18 + DatabaseDetailHeader,
19 + DatabaseTable,
20 + },
21 + async asyncData({params, store}) {
22 + const {id} = params;
23 + await store.dispatch('Drone/detail/fetchDetailData', id);
24 + return {id};
25 + },
26 + data() {
27 + return {
28 + parentLoading: false,
29 + searchParams: {},
30 + };
31 + },
32 + computed: {
33 + ...mapGetters('Drone/detail', {
34 + getDetailData: 'getDetailData',
35 + }),
36 + ...mapGetters('Schedule/page', {
37 + getPageParams: 'getPageParams',
38 + }),
39 + },
40 + watch: {},
41 + created() {
42 + this.searchParams = JSON.parse(JSON.stringify(this.getPageParams));
43 + this.searchParams.droneId = this.getDetailData.id;
44 + this.setPageParams(this.searchParams);
45 + this.loadData();
46 + },
47 + beforeDestroy() {
48 + this.clearPageParams();
49 + },
50 + methods: {
51 + ...mapActions('Schedule/page', {
52 + fetchPageData: 'fetchPageData',
53 + clearPageParams: 'clearPageParams',
54 + setPageParams: 'setPageParams',
55 + }),
56 + loadData() {
57 + this.parentLoading = true;
58 +
59 + this.fetchPageData(this.getPageParams).finally(() => {
60 + this.parentLoading = false;
61 + });
62 + },
63 + },
64 +};
65 +</script>
66 +
67 +<style scoped lang="scss">
68 +.table-title {
69 + font-size: 30px;
70 +}
71 +</style>
1 +<template>
2 + <div>
3 + <database-search-filter @loadData="loadData" />
4 +
5 + <div class="page-main">
6 + <database-table :childLoading="parentLoading" @loadData="loadData" />
7 + </div>
8 + </div>
9 +</template>
10 +
11 +<script>
12 +import { mapActions, mapGetters } from 'vuex';
13 +import DatabaseSearchFilter from '@/components/Database/Drone/searchFilter';
14 +import DatabaseTable from '@/components/Database/Drone/table';
15 +
16 +export default {
17 + head() {
18 + return {
19 + title: 'Database-Drone',
20 + meta: [
21 + {
22 + hid: 'database',
23 + name: 'Descriptions',
24 + content: 'db-Content',
25 + },
26 + ],
27 + };
28 + },
29 + components: {
30 + DatabaseTable,
31 + DatabaseSearchFilter,
32 + },
33 + data() {
34 + return {
35 + parentLoading: false,
36 + };
37 + },
38 + computed: {
39 + ...mapGetters('Drone/page', {
40 + getPageParams: 'getPageParams',
41 + }),
42 + },
43 + created() {
44 + this.loadData();
45 + },
46 + beforeDestroy() {
47 + this.clearPageParams();
48 + },
49 + methods: {
50 + ...mapActions('Drone/page', {
51 + fetchPageData: 'fetchPageData',
52 + clearPageParams: 'clearPageParams',
53 + }),
54 + loadData() {
55 + this.parentLoading = true;
56 + this.fetchPageData(this.getPageParams).finally(() => {
57 + this.parentLoading = false;
58 + });
59 + },
60 + },
61 +};
62 +</script>
63 +
64 +<style scoped lang="scss"></style>
1 +<template>
2 + <div>
3 + <log-search-filter @loadData="loadData"/>
4 +
5 + <div class="page-main">
6 + <log-table :childLoading="parentLoading" @loadData="loadData"/>
7 + </div>
8 + </div>
9 +</template>
10 +
11 +<script>
12 +import { mapActions, mapGetters } from 'vuex';
13 +import LogSearchFilter from '@/components/Database/Log/SearchFilter';
14 +import LogTable from '@/components/Database/Log/table';
15 +
16 +export default {
17 + head() {
18 + return {
19 + title: 'Database-Log',
20 + meta: [
21 + {
22 + hid: 'database',
23 + name: 'Descriptions',
24 + content: 'db-Content',
25 + },
26 + ],
27 + };
28 + },
29 + components: {
30 + LogTable,
31 + LogSearchFilter,
32 + },
33 + data() {
34 + return {
35 + parentLoading: false,
36 + };
37 + },
38 + computed: {
39 + ...mapGetters('Log/page', {
40 + getPageParams: 'getPageParams',
41 + }),
42 + },
43 + created() {
44 + this.loadData();
45 + },
46 + beforeDestroy() {
47 + this.clearPageParams();
48 + },
49 + methods: {
50 + ...mapActions('Log/page', {
51 + fetchPageData: 'fetchPageData',
52 + clearPageParams: 'clearPageParams',
53 + }),
54 + loadData() {
55 + this.parentLoading = true;
56 + this.fetchPageData(this.getPageParams).finally(() => {
57 + this.parentLoading = false;
58 + });
59 + },
60 + },
61 +};
62 +</script>
63 +
64 +<style scoped lang="scss"></style>
1 +<template>
2 + <div>
3 + <database-detail-header />
4 + <div class="page-main-without-header ">
5 + <div class="table-title">Log</div>
6 + <database-table :childLoading="parentLoading" @loadData="loadData" />
7 + </div>
8 + </div>
9 +</template>
10 +
11 +<script>
12 +import DatabaseDetailHeader from '@/components/Database/Schedule/Detail/header';
13 +import DatabaseTable from '@/components/Database/Log/table';
14 +import {mapActions, mapGetters} from 'vuex';
15 +
16 +export default {
17 + components: {
18 + DatabaseDetailHeader,
19 + DatabaseTable,
20 + },
21 + async asyncData({params, store}) {
22 + const {id} = params;
23 + await store.dispatch('Schedule/detail/fetchDetailData', id);
24 + return {id};
25 + },
26 + data() {
27 + return {
28 + parentLoading: false,
29 + searchParams: {},
30 + };
31 + },
32 + computed: {
33 + ...mapGetters('Schedule/detail', {
34 + getScheduleDetailData: 'getDetailData',
35 + }),
36 + ...mapGetters('Log/page', {
37 + getPageParams: 'getPageParams',
38 + }),
39 + },
40 + watch: {},
41 + created() {
42 + this.searchParams = JSON.parse(JSON.stringify(this.getPageParams));
43 + this.searchParams.scheduleId = this.getScheduleDetailData.id;
44 + this.setPageParams(this.searchParams);
45 + this.loadData();
46 + },
47 + beforeDestroy() {
48 + this.clearPageParams();
49 + },
50 + methods: {
51 + ...mapActions('Log/page', {
52 + fetchPageData: 'fetchPageData',
53 + clearPageParams: 'clearPageParams',
54 + setPageParams: 'setPageParams',
55 + }),
56 + loadData() {
57 + this.parentLoading = true;
58 +
59 + this.fetchPageData(this.getPageParams).finally(() => {
60 + this.parentLoading = false;
61 + });
62 + },
63 + },
64 +};
65 +</script>
66 +
67 +<style scoped lang="scss">
68 +.table-title {
69 + font-size: 30px;
70 +}
71 +</style>
1 +<template>
2 + <div>
3 + <database-search-filter @loadData="loadData" />
4 + <div class="page-main">
5 + <database-table :childLoading="parentLoading" @loadData="loadData" />
6 + </div>
7 + </div>
8 +</template>
9 +
10 +<script>
11 +import {mapActions, mapGetters} from 'vuex';
12 +import DatabaseSearchFilter from '@/components/Database/Schedule/searchFilter';
13 +import DatabaseTable from '@/components/Database/Schedule/table';
14 +
15 +export default {
16 + head() {
17 + return {
18 + title: 'Database',
19 + meta: [
20 + {
21 + hid: 'database',
22 + name: 'Descriptions',
23 + content: 'db-Content',
24 + },
25 + ],
26 + };
27 + },
28 + components: {
29 + DatabaseTable,
30 + DatabaseSearchFilter,
31 + },
32 + data() {
33 + return {
34 + parentLoading: false,
35 + };
36 + },
37 + computed: {
38 + ...mapGetters('Schedule/page', {
39 + getPageParams: 'getPageParams',
40 + }),
41 + },
42 + created() {
43 + this.loadData();
44 + },
45 + beforeDestroy() {
46 + this.clearPageParams();
47 + },
48 + methods: {
49 + ...mapActions('Schedule/page', {
50 + fetchPageData: 'fetchPageData',
51 + clearPageParams: 'clearPageParams',
52 + }),
53 + loadData() {
54 + this.parentLoading = true;
55 + this.fetchPageData(this.getPageParams).finally(() => {
56 + this.parentLoading = false;
57 + });
58 + },
59 + },
60 +};
61 +</script>
62 +
63 +<style scoped lang="scss"></style>
1 +<template>
2 + <div>
3 + <main-header/>
4 + <div class="page-main">
5 + <div id="map-wrap">
6 + <client-only>
7 + <div class="mapBox">
8 + <l-map
9 + ref="map"
10 + class="map"
11 + :center="[currentLatitude, currentLongitude]"
12 + :options="{zoomControl: false}"
13 + :zoom="zoom"
14 + :max-zoom="18"
15 + :min-zoom="8"
16 + @update:zoom="zoomUpdate"
17 + @update:bounds="boundsUpdate"
18 + @click="clickMap"
19 + >
20 + <l-tile-layer url="http://{s}.tile.osm.org/{z}/{x}/{y}.png"></l-tile-layer>
21 +
22 + <l-control position="bottomright">
23 + <img
24 + @click="clickGpsBtn"
25 + class="gpsBtn"
26 + :src="require('@/static/img/gps.png')"
27 + />
28 + </l-control>
29 +
30 + <l-control-zoom position="bottomright"></l-control-zoom>
31 + <template v-for="(drone) in getDroneLogs">
32 + <drone
33 + :key="drone.id"
34 + :latitude="drone.latitude"
35 + :longitude="drone.longitude"
36 + :icon="icon"
37 + @clickDrone=clickDrone(drone.droneId)
38 + />
39 + </template>
40 +
41 + <l-polyline :lat-lngs="selectedDroneRoute" :color="'green'"></l-polyline>
42 +
43 + </l-map>
44 + <DroneDetail
45 + class="drone-detail"
46 + v-if="showDroneDetail"
47 + />
48 + <SearchBtnBox
49 + class="search-box"
50 + @changeFilterMode="e => filterMode = e"
51 + @changeSearchMode="e => searchMode = e"
52 + />
53 + <SearchFeatureBox
54 + v-if="searchMode"
55 + @clickClose="searchMode = false"
56 + @focusChange="(lat, lng) => focusChange(lat, lng)"
57 + @clickDrone="(id) => clickDrone(id)"
58 + class="search-box"
59 + />
60 + <BottomToolBox
61 + class="bottom-tool-box"
62 + @changeFilterMode="e => filterMode = e"
63 + @changeSearchMode="e => searchMode = e"
64 + />
65 + <FilterFeatureBox
66 + v-if="filterMode"
67 + @clickClose="filterMode = false"
68 + class="filter-feature-box"
69 + />
70 +
71 + </div>
72 + </client-only>
73 + </div>
74 + </div>
75 + </div>
76 +</template>
77 +
78 +<script>
79 +/* eslint-disable no-underscore-dangle */
80 +import { mapActions, mapGetters } from 'vuex';
81 +import MainHeader from '@/components/Main/header';
82 +import Drone from '@/components/Main/drone';
83 +import DroneDetail from '@/components/Main/Box/DetailBox/detailBox';
84 +import SearchBtnBox from '@/components/Main/Box/SearchBox/searchBtnBox';
85 +import SearchFeatureBox from '@/components/Main/Box/SearchBox/searchFeatureBox';
86 +import BottomToolBox from '@/components/Main/Box/BottomToolBox/bottomToolBox';
87 +import FilterFeatureBox from '@/components/Main/Box/FilterBox/filterFeatureBox';
88 +
89 +import droneImg from '@/static/img/drone.svg';
90 +
91 +export default {
92 + components: {
93 + MainHeader, Drone, DroneDetail, SearchBtnBox, SearchFeatureBox, BottomToolBox, FilterFeatureBox,
94 + },
95 + head() {
96 + return {
97 + title: 'DroneWeb',
98 + meta: [
99 + {
100 + hid: 'database',
101 + name: 'Descriptions',
102 + content: 'DroneWeb-Content',
103 + },
104 + ],
105 + };
106 + },
107 + data() {
108 + return {
109 + center: [0, 0],
110 + rotation: 0,
111 + geolocPosition: undefined,
112 + map: null,
113 + selectedDroneRoute: [],
114 + selectedDroneData: {},
115 + selectedDroneId: null,
116 + currentLatitude: 37.2430125,
117 + currentLongitude: 127.0811054,
118 + showDroneDetail: false,
119 + filterMode: false,
120 + searchMode: false,
121 + icon: droneImg,
122 +
123 + zoom: 15,
124 + socketCheckInterval: null,
125 + };
126 + },
127 + computed: {
128 + ...mapGetters('Drone/drone', {
129 + getDetailData: 'getDetailData',
130 + getDroneLogs: 'getDroneLogs',
131 + getSelectedLogList: 'getSelectedLogList',
132 + getSelectedLastLog: 'getSelectedLastLog',
133 + getSelectedDroneId: 'getSelectedDroneId',
134 + }),
135 + },
136 + watch: {
137 + getDroneLogs: {
138 + deep: true,
139 + handler(newVal) {
140 + // console.log('드론 소켓 로그 getDroneLogs', newVal);
141 + },
142 + },
143 + getSelectedLastLog: {
144 + deep: true,
145 + handler(newVal) {
146 + this.selectedDroneRoute.push([newVal.latitude, newVal.longitude]);
147 + // console.log('page 속도 등 계산한 정보 selectedDroneLog', newVal);
148 + },
149 + },
150 + },
151 + created() {
152 + if (process.client) {
153 + // Websocket 연결
154 + this.connect();
155 + this.socketCheckInterval = setInterval(() => {
156 + console.log('socketState', window.clientSocket.readyState);
157 + this.connect();
158 + }, 30000);
159 + setTimeout(() => {
160 + this.zoom = 16;
161 + }, 2000);
162 +
163 + navigator.geolocation.getCurrentPosition((position) => {
164 + this.currentLatitude = position.coords.latitude;
165 + this.currentLongitude = position.coords.longitude;
166 + }, (e) => {
167 + console.log('err', e);
168 + },
169 + { enableHighAccuracy: true, maximumAge: 0, timeout: 2000 });
170 + }
171 + },
172 + methods: {
173 + ...mapActions('Drone/drone', {
174 + fetchDetailInfo: 'fetchDetailInfo',
175 + setSelectedDroneId: 'setSelectedDroneId',
176 + }),
177 + ...mapActions('Drone/Map', {
178 + setBoundary: 'setBoundary',
179 + setZoomLevel: 'setZoomLevel',
180 + }),
181 + zoomUpdate(zoom) {
182 + this.setZoomLevel(zoom);
183 + },
184 + boundsUpdate(bounds) {
185 + this.setBoundary(bounds);
186 + },
187 + focusChange(lat, lng) {
188 + this.map = this.$refs.map.mapObject.panTo([lat, lng]);
189 + },
190 + clickGpsBtn() {
191 + if (process.client) {
192 + navigator.geolocation.getCurrentPosition((position) => {
193 + this.currentLatitude = position.coords.latitude;
194 + this.currentLongitude = position.coords.longitude;
195 + this.focusChange(this.currentLatitude, this.currentLongitude);
196 + }, (e) => {
197 + console.log('err', e);
198 + }, { enableHighAccuracy: true, maximumAge: 0, timeout: 2000 });
199 + }
200 + },
201 + clickDrone(droneId) {
202 + if (this.getSelectedDroneId === droneId) return;
203 + this.selectedDroneRoute = [];
204 + this.setSelectedDroneId(droneId);
205 + this.accumulatedDistance = 0; // 미터
206 +
207 + this.fetchDetailInfo(this.getSelectedDroneId)
208 + .then((r) => {
209 + console.log('선택한 드론', r);
210 + r.droneLogs.forEach((droneLog) => {
211 + this.selectedDroneRoute.push([droneLog.latitude, droneLog.longitude]);
212 + });
213 + this.selectedDroneData = this.getDetailData;
214 + this.showDroneDetail = true;
215 + });
216 + },
217 + clickMap() {
218 + this.showDroneDetail = false;
219 + this.selectedDroneRoute = [];
220 + this.setSelectedDroneId(null);
221 + },
222 + clickSearchBtn() {
223 + console.log('click btn');
224 + },
225 + clickFilterBtn() {
226 + console.log('click btn');
227 + },
228 + },
229 +};
230 +</script>
231 +<style lang="scss" scoped>
232 +.mapBox {
233 + height: calc(100vh - 100px);
234 + position: relative;
235 +}
236 +
237 +.map {
238 + z-index: 0;
239 +}
240 +
241 +.drone-detail {
242 + position: absolute;
243 + z-index: 10;
244 + top: 10px;
245 + left: 10px;
246 +}
247 +
248 +.tool-box {
249 + position: absolute;
250 + z-index: 10;
251 + top: 10px;
252 + right: 10px;
253 +}
254 +
255 +.gpsBtn {
256 + width: 35px;
257 + height: 35px;
258 + padding: 5px;
259 + border: 2px solid rgba(0, 0, 0, 0.2);
260 + border-radius: 4px;
261 + background: white;
262 + cursor: pointer;
263 +}
264 +
265 +.gpsBtn:hover {
266 + background-color: #f4f4f4;
267 +}
268 +
269 +</style>
1 +<template>
2 + <div>
3 + <client-only>
4 + <vl-map :load-tiles-while-animating="true" :load-tiles-while-interacting="true"
5 + data-projection="EPSG:4326" style="height: 400px">
6 + <vl-view :min-zoom="2" :zoom.sync="zoom" :center.sync="center" :rotation.sync="rotation"></vl-view>
7 +
8 + <vl-geoloc @update:position="geolocPosition = $event">
9 + <template slot-scope="geoloc">
10 + <!-- <vl-feature v-if="geoloc.position" id="position-feature">
11 + {{ geoloc.position }}
12 + <vl-geom-point :coordinates="geoloc.position"></vl-geom-point>
13 + <vl-style-box>
14 + <vl-style-icon src="@/assets/images/drone.png" :scale="0.1" :anchor="[0.5, 1]"></vl-style-icon>
15 + </vl-style-box>
16 + </vl-feature> -->
17 +
18 + <vl-feature>
19 + <vl-geom-point :coordinates="[126.986606,37.310334]"></vl-geom-point>
20 + <vl-style-box>
21 + <vl-style-icon src="@/assets/images/drone.png" :scale="0.2" :anchor="[0.5, 1]"></vl-style-icon>
22 + </vl-style-box>
23 + </vl-feature>
24 +
25 + <vl-feature v-for="(drone) in droneLogs" :key="drone.id">
26 + <vl-geom-point :coordinates="[drone.longitude,drone.latitude]"></vl-geom-point>
27 + <vl-style-box>
28 + <vl-style-icon src="@/assets/images/drone.png" :scale="0.1" :anchor="[0.5, 1]"></vl-style-icon>
29 + </vl-style-box>
30 + </vl-feature>
31 + </template>
32 + </vl-geoloc>
33 +
34 + <vl-layer-tile id="osm">
35 + <vl-source-osm></vl-source-osm>
36 + </vl-layer-tile>
37 + </vl-map>
38 + <div style="padding: 20px">
39 + Zoom: {{ zoom }}<br>
40 + Center: {{ center }}<br>
41 + Rotation: {{ rotation }}<br>
42 + My geolocation: {{ geolocPosition }}
43 + </div>
44 + <a-button @click="connect">connect</a-button>
45 +<!-- <a-button @click="connectMultiClient">connectMultiClient</a-button>-->
46 + <a-button @click="disconnect">disconnect</a-button>
47 + </client-only>
48 + </div>
49 +</template>
50 +
51 +<script>
52 +import { mapActions, mapGetters } from 'vuex';
53 +import MainHeader from '@/components/Main/header';
54 +import Drone from '@/components/Main/drone';
55 +import DroneDetail from '@/components/Main/Box/DetailBox/detailBox';
56 +
57 +export default {
58 + components: {
59 + MainHeader, Drone, DroneDetail, ToolBox,
60 + },
61 + head() {
62 + return {
63 + title: 'DroneWeb',
64 + meta: [
65 + {
66 + hid: 'database',
67 + name: 'Descriptions',
68 + content: 'DroneWeb-Content',
69 + },
70 + ],
71 + };
72 + },
73 + data() {
74 + return {
75 + zoom: 2,
76 + center: [0, 0],
77 + rotation: 0,
78 + geolocPosition: undefined,
79 + map: null,
80 + drones: [],
81 + testDrone: {
82 + latitude: 37.2430125,
83 + longitude: 127.0811054,
84 + polyline: {
85 + latlngs: [],
86 + color: 'green',
87 + },
88 + },
89 + currentLatitude: 37.2430125,
90 + currentLongitude: 127.0811054,
91 + showDroneDetail: false,
92 + };
93 + },
94 + computed: {
95 + ...mapGetters('Drone/drone', {
96 + getDetailData: 'getDetailData',
97 + }),
98 + },
99 + watch: {
100 + droneLogs: {
101 + deep: true,
102 + handler(newVal) {
103 + console.log('드론 소켓 로그 droneLogs', newVal);
104 + },
105 + },
106 + },
107 + created() {
108 + /**
109 + * getDetailData와 fetchDetailInfo로 상세 정보 조회
110 + * 속도, 이동거리, 방향은 모두 계산해서 출력함.
111 + */
112 + if (process.browser) {
113 + navigator.geolocation.getCurrentPosition((position) => {
114 + this.currentLatitude = position.coords.latitude;
115 + this.currentLongitude = position.coords.longitude;
116 + }, () => {
117 + }, { enableHighAccuracy: true, maximumAge: 0 });
118 + }
119 + },
120 + methods: {
121 + ...mapActions('Drone/drone', {
122 + fetchDetailInfo: 'fetchDetailInfo',
123 + clearAccumulatedDistance: 'clearAccumulatedDistance',
124 + setSelectedDroneId: 'setSelectedDroneId',
125 + }),
126 + clickGpsBtn() {
127 + if (process.browser) {
128 + navigator.geolocation.getCurrentPosition((position) => {
129 + this.currentLatitude = position.coords.latitude;
130 + this.currentLongitude = position.coords.longitude;
131 + this.map = this.$refs.map.mapObject.panTo([this.currentLatitude, this.currentLongitude]);
132 + }, () => {
133 + }, { enableHighAccuracy: true, maximumAge: 0 });
134 + }
135 + },
136 + clickDrone() {
137 + /**
138 + * id가 1일때 가정하고 만듦. 추후에 수정 필.
139 + * distance 거리 단위는 미터
140 + */
141 + this.setSelectedDroneId(1);
142 + this.clearAccumulatedDistance();
143 + this.fetchDetailInfo(1)
144 + .then((r) => {
145 + this.showDroneDetail = true;
146 + console.log('선택한 드론', r);
147 + });
148 + console.log('drone click');
149 + },
150 + clickMap() {
151 + console.log('click Map');
152 + this.showDroneDetail = false;
153 + },
154 +
155 + clickSearchBtn() {
156 + console.log('click btn');
157 + },
158 + clickFilterBtn() {
159 + console.log('click btn');
160 + },
161 + },
162 +};
163 +</script>
164 +<style lang="scss" scoped>
165 +.map {
166 + z-index: 0;
167 +}
168 +
169 +.gpsBtn {
170 + width: 35px;
171 + height: 35px;
172 + padding: 5px;
173 + border: 2px solid rgba(0, 0, 0, 0.2);
174 + border-radius: 4px;
175 + background: white;
176 + cursor: pointer;
177 +}
178 +
179 +.gpsBtn:hover {
180 + background-color: #f4f4f4;
181 +}
182 +
183 +</style>
1 +<template>
2 + <div>
3 + <analytics-header/>
4 + <div class="chart-div">
5 + <live-log-chart :chart-data="liveDroneData"/>
6 + </div>
7 +
8 + <div class="page-main">
9 + <div>
10 + 웹 소켓 테스트
11 + </div>
12 + <a-input v-model="socketClientCnt"/>
13 + <a-button @click="multConnect">multConnect</a-button>
14 + <a-button @click="connect">connect</a-button>
15 + <a-button @click="sendMessage">sendMessage</a-button>
16 + <a-button @click="disconnectMult">disconnectMult</a-button>
17 + <a-button @click="disconnectSol">disconnectSol</a-button>
18 + <div>
19 + <a-button @click="multConnectDynamic">multConnectDynamic</a-button>
20 + </div>
21 + </div>
22 + </div>
23 +</template>
24 +
25 +<script>
26 +import AnalyticsHeader from '@/components/Analytics/header';
27 +import LiveLogChart from '@/components/Analytics/Chart/liveLogChart';
28 +import { mapActions, mapGetters } from 'vuex';
29 +import calcGeoLocation from '@/utils/Mixins/Component/calcGeolocation';
30 +
31 +export default {
32 + components: {
33 + LiveLogChart,
34 + AnalyticsHeader,
35 + },
36 + mixins: [calcGeoLocation],
37 + data() {
38 + return {
39 + socketClientCnt: 10,
40 + liveDroneData: [
41 + {
42 + name: '드론 기체 수',
43 + data: [],
44 + },
45 + {
46 + name: '활성화된 드론 수',
47 + data: [],
48 + },
49 + ],
50 + angle: {},
51 + velocity: {},
52 + };
53 + },
54 + computed: {
55 + ...mapGetters('GeoLocation', {
56 + getCoord: 'getCoord',
57 + getAngle: 'getAngle',
58 + getVelocity: 'getVelocity',
59 + }),
60 + ranNum() {
61 + return (min, max) => parseInt((Math.random() * (max - min) + min), 10);
62 + },
63 + },
64 + created() {
65 + },
66 + mounted() {
67 + },
68 + methods: {
69 + ...mapActions('GeoLocation', {
70 + saveCoord: 'saveCoord',
71 + }),
72 + connect() {
73 + // window.webSocketSol = new WebSocket('ws://localhost:20202');
74 + window.webSocketSol = new WebSocket('ws://14.33.35.148:20202');
75 + window.webSocketSol.onopen = () => {
76 + console.log('socket on open');
77 + };
78 + window.webSocketSol.onerror = () => {
79 + console.log('socket on open');
80 + };
81 + window.webSocketSol.onmessage = (data) => {
82 + // console.log(data);
83 + const logData = JSON.parse(data.data);
84 + // this.saveCoord(logData);
85 + console.log(`message ${this.$dayjs().format('HH:mm:ss')} : `, logData);
86 + };
87 + window.webSocketSol.onclose = () => {
88 + console.log('socket on open');
89 + };
90 + },
91 + multConnect() {
92 + for (let i = 0; i < 10; i += 1) {
93 + if (window[`wsSocket${i}`] == null || window[`wsSocket${i}`].readyState === 3) {
94 + // window[`wsSocket${i}`] = new WebSocket(`ws://localhost:20203/${i + 1}`);
95 + window[`wsSocket${i}`] = new WebSocket(`ws://14.33.35.148:20203/${i + 1}`);
96 + window[`wsSocket${i}`].onopen = () => {
97 + console.log(`socket${i} on open`);
98 + };
99 + window[`wsSocket${i}`].onerror = () => {
100 + console.log(`socket${i} on error`);
101 + };
102 + window[`wsSocket${i}`].onmessage = (data) => {
103 + // console.log(data);
104 + const logData = JSON.parse(data.data);
105 + // this.saveCoord(logData);
106 + console.log(`message${i} ${this.$dayjs().format('HH:mm:ss')} : `, logData);
107 + };
108 + window[`wsSocket${i}`].onclose = () => {
109 + console.log(`socket${i} on close`);
110 + };
111 + }
112 + }
113 + },
114 + sendMessage() {
115 + window.wsSocket.send('client sent message');
116 + },
117 + multConnectDynamic() {
118 + for (let i = 0; i < this.socketClientCnt; i += 1) {
119 + if (window[`wsSocket${i}`] == null || window[`wsSocket${i}`].readyState === 3) {
120 + // window[`wsSocket${i}`] = new WebSocket(`ws://localhost:20203/${i + 1}`);
121 + window[`wsSocket${i}`] = new WebSocket(`ws://14.33.35.148:20204/${i + 1}`);
122 + window[`wsSocket${i}`].onopen = () => {
123 + console.log(`socket${i} on open`);
124 + };
125 + window[`wsSocket${i}`].onerror = () => {
126 + console.log(`socket${i} on error`);
127 + };
128 + window[`wsSocket${i}`].onmessage = (data) => {
129 + // console.log(data);
130 + const logData = JSON.parse(data.data);
131 + // this.saveCoord(logData);
132 + console.log(`message${i} ${this.$dayjs().format('HH:mm:ss')} : `, logData);
133 + };
134 + window[`wsSocket${i}`].onclose = () => {
135 + console.log(`socket${i} on close`);
136 + };
137 + }
138 + }
139 + },
140 + disconnectMult() {
141 + for (let i = 0; i < this.socketClientCnt; i += 1) {
142 + if (window[`wsSocket${i}`].readyState === 1) {
143 + window[`wsSocket${i}`].close();
144 + window[`wsSocket${i}`] = null;
145 + }
146 + }
147 + },
148 + disconnectMult2() {
149 + for (let i = 0; i < this.socketClientCnt; i += 1) {
150 + if (window[`wsSocket${i}`].readyState === 1) {
151 + window[`wsSocket${i}`].close();
152 + window[`wsSocket${i}`] = null;
153 + }
154 + }
155 + },
156 + disconnectSol() {
157 + if (window.webSocketSol.readyState === 1) {
158 + window.webSocketSol.close();
159 + window.webSocketSol = null;
160 + }
161 + },
162 + },
163 +};
164 +</script>
165 +
166 +<style scoped lang="scss">
167 +@import '@/assets/styles/mixins.scss';
168 +
169 +.pie-chart-div {
170 + margin-top: 20px;
171 + height: 440px;
172 + width: calc(50% - 10px);
173 + padding: 20px 20px 20px 20px;
174 + background-color: white;
175 + border-radius: 6px;
176 + border: $gray-2 1px solid;
177 +}
178 +
179 +.chart-div {
180 + margin-top: 20px;
181 + height: 440px;
182 + padding: 20px 20px 20px 20px;
183 + background-color: white;
184 + border-radius: 6px;
185 + border: $gray-2 1px solid;
186 +}
187 +</style>
1 +/* eslint-disable func-names,no-param-reassign */
2 +import store from 'store2';
3 +import { Modal } from 'ant-design-vue';
4 +
5 +export default function ({ $axios, redirect }) {
6 + $axios.onRequest((config) => {
7 + /* header AppVersion 체크 */
8 + if (!store.get('appVersion')) {
9 + store.set('appVersion', config.headers.common.AppVersion);
10 + } else if (config.headers.common.AppVersion !== store.get('appVersion')) {
11 + store.set('appVersion', config.headers.common.AppVersion);
12 + Modal.info({
13 + title: '새로운 콘텐츠가 감지되었습니다.',
14 + content: '확인을 눌러 새로고침 해주세요.',
15 + onOk: () => {
16 + window.location.reload(true);
17 + },
18 + });
19 + }
20 + return config;
21 + });
22 + $axios.onResponse((response) => response.data);
23 + $axios.onError((error) => {
24 + if (error.response.status === 404) {
25 + redirect('/auth/404');
26 + } else {
27 + Modal.error({
28 + title: '서버와의 통신에 에러가 발생했습니다.',
29 + onOk: () => {
30 + redirect('/auth/500');
31 + },
32 + });
33 + }
34 + });
35 +}
1 +import Vue from 'vue';
2 +import dayjs from './partials/implement';
3 +
4 +const options = {
5 + // locale: process.env.VUE_APP_I18N_LOCALE || 'ko',
6 + locale: 'ko',
7 + localeObject: {
8 + name: 'ko',
9 + weekdays: ['월', '화', '수', '목', '금', '토', '일'],
10 + months: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],
11 + formats: {
12 + L: 'YYYY-MM-DD',
13 + LL: 'YYYY-MM-DD(ddd)',
14 + LLL: 'YYYY-MM-DD HH:mm',
15 + LLLL: 'YYYY-MM-DD HH:mm:sss',
16 + },
17 + },
18 +};
19 +
20 +Vue.use(dayjs, options);
21 +Vue.filter('dayjs', (value, format) => {
22 + if (value == null || format == null || value === 'Invalid Date') {
23 + return '';
24 + }
25 + return dayjs.day(value).format(format);
26 +});
1 +/* eslint-disable no-param-reassign */
2 +import dayjs from 'dayjs';
3 +import 'dayjs/locale/ko';
4 +
5 +export default {
6 + install(Vue, options) {
7 + dayjs.locale(options.locale, options.localeObject);
8 + Vue.prototype.$dayjs = dayjs;
9 + },
10 +};
11 +
12 +export const day = dayjs;
1 +# PLUGINS
2 +
3 +**This directory is not required, you can delete it if you don't want to use it.**
4 +
5 +This directory contains Javascript plugins that you want to run before mounting the root Vue.js application.
6 +
7 +More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/plugins).
1 +import Vue from 'vue';
2 +
3 +import {
4 + Breadcrumb, Icon, PageHeader, Alert, Result,
5 + Button, Layout, Table, Radio, Dropdown, Menu,
6 + Input, Calendar, Form, Tooltip, Select, Switch,
7 + Spin, Checkbox, Tabs, Pagination, notification,
8 + DatePicker, TimePicker, Divider, Card, Avatar,
9 + Row, Col, Modal, ConfigProvider, Descriptions, Badge,
10 + List, Progress, Slider, AutoComplete,
11 +} from 'ant-design-vue';
12 +
13 +Vue.use(notification);
14 +Vue.use(Badge);
15 +Vue.use(AutoComplete);
16 +Vue.use(List);
17 +Vue.use(PageHeader);
18 +Vue.use(Result);
19 +Vue.use(Alert);
20 +Vue.use(Modal);
21 +Vue.use(Icon);
22 +Vue.use(Divider);
23 +Vue.use(Row);
24 +Vue.use(Col);
25 +Vue.use(Card);
26 +Vue.use(Button);
27 +Vue.use(Breadcrumb);
28 +Vue.use(Layout);
29 +Vue.use(Table);
30 +Vue.use(Radio);
31 +Vue.use(Dropdown);
32 +Vue.use(Menu);
33 +Vue.use(Input);
34 +Vue.use(Calendar);
35 +Vue.use(Form);
36 +Vue.use(Tooltip);
37 +Vue.use(Select);
38 +Vue.use(Spin);
39 +Vue.use(Checkbox);
40 +Vue.use(Tabs);
41 +Vue.use(Pagination);
42 +Vue.use(Switch);
43 +Vue.use(DatePicker);
44 +Vue.use(TimePicker);
45 +Vue.use(ConfigProvider);
46 +Vue.use(Descriptions);
47 +Vue.use(Avatar);
48 +Vue.use(Progress);
49 +Vue.use(Slider);
50 +
51 +Vue.prototype.$notification = notification;
52 +Vue.prototype.$confirm = Modal.confirm;
53 +Vue.prototype.$info = Modal.info;
54 +Vue.prototype.$success = Modal.success;
55 +Vue.prototype.$error = Modal.error;
56 +Vue.prototype.$warning = Modal.warning;
1 +import Vue from 'vue';
2 +import mixins from '@/utils/Mixins/index';
3 +
4 +Vue.mixin(mixins);
1 +import Vue from 'vue';
2 +import Highcharts from 'highcharts';
3 +import HighchartsVue from 'highcharts-vue';
4 +import HighchartsBoost from 'highcharts/modules/boost';
5 +import HighchartsBoostCanvas from 'highcharts/modules/boost-canvas';
6 +// import HighchartsExporting from 'highcharts/modules/exporting';
7 +// import HighchartsOfflineExporting from 'highcharts/modules/offline-exporting';
8 +// import HighchartsExportData from 'highcharts/modules/export-data';
9 +import HighchartsMore from 'highcharts/highcharts-more';
10 +
11 +HighchartsBoost(Highcharts);
12 +HighchartsBoostCanvas(Highcharts);
13 +// HighchartsExporting(Highcharts);
14 +// HighchartsOfflineExporting(Highcharts);
15 +// HighchartsExportData(Highcharts);
16 +HighchartsMore(Highcharts);
17 +
18 +const options = {
19 + time: {
20 + timezoneOffset: -540,
21 + },
22 + colors: [
23 + '#C08B9C', '#F5D1B7', '#E99C65', '#7A9A82',
24 + '#28B3BE', '#A6A6A6', '#F4C96B', '#C04176',
25 + '#8885A4', '#454C88', '#94A4BE', '#E9DF84',
26 + '#7EAFB2',
27 + ],
28 + credits: {
29 + enabled: false,
30 + style: {
31 + color: '#FFFFFF',
32 + },
33 + },
34 + chart: {
35 + style: {
36 + fontFamily: 'Noto Sans Kr, sans-serif',
37 + fontWeight: 500,
38 + },
39 + animation: true,
40 + },
41 + title: {
42 + style: {
43 + color: '#2a274d',
44 + },
45 + },
46 + plotOptions: {
47 + column: {
48 + pointPadding: 0.2,
49 + borderWidth: 0,
50 + },
51 + },
52 + xAxis: {
53 + labels: {
54 + style: {
55 + color: '#4f4f7a',
56 + fontSize: '14px',
57 + },
58 + shared: true,
59 + useHTML: true,
60 + },
61 + },
62 + yAxis: {
63 + labels: {
64 + style: {
65 + color: '#4f4f7a',
66 + fontSize: '14px',
67 + },
68 + shared: true,
69 + useHTML: true,
70 + },
71 + },
72 + labels: {
73 + style: {
74 + color: '#4f4f7a',
75 + },
76 + shared: true,
77 + useHTML: true,
78 + },
79 + tooltip: {
80 + headerFormat: '<span style="font-size:16px;">{point.key}</span><table>',
81 + pointFormat: '<tr><td style="color:{series.color};padding:0">{series.name}</td>'
82 + + '<td style="padding:0"><b>: {point.y}</b></td></tr>',
83 + footerFormat: '</table>',
84 + shared: true,
85 + useHTML: true,
86 + style: {
87 + fontSize: '14px',
88 + },
89 + borderWidth: 2,
90 + borderRadius: 6,
91 + },
92 + legend: {
93 + useHTML: true,
94 + enabled: true,
95 + itemStyle: {
96 + color: '#4f4f7a',
97 + fontSize: '14px',
98 + },
99 + itemHoverStyle: {
100 + color: '#100f28',
101 + fontSize: '14px',
102 + },
103 + itemHiddenStyle: {
104 + color: '#dde2ec',
105 + fontSize: '14px',
106 + },
107 + },
108 + loading: {
109 + labelStyle: {
110 + color: '#013477',
111 + },
112 + style: {
113 + backgroundColor: 'gray',
114 + },
115 + },
116 + exporting: {
117 + sourceWidth: 1000,
118 + sourceHeight: 500,
119 + buttons: {
120 + contextButton: {
121 + theme: {
122 + fill: 'transparent',
123 + },
124 + x: -2,
125 + y: 0,
126 + menuItems: [
127 + 'viewFullscreen',
128 + 'printChart',
129 + 'separator',
130 + 'downloadPDF',
131 + 'downloadPNG',
132 + 'downloadXLS',
133 + ],
134 + },
135 + },
136 + fallbackToExportServer: false,
137 + },
138 + mapNavigation: {
139 + enableMouseWheelZoom: true,
140 + },
141 +};
142 +
143 +Highcharts.setOptions({
144 + lang: {
145 + thousandsSep: ',',
146 + },
147 + ...options,
148 +});
149 +
150 +Vue.use(HighchartsVue, {
151 + highcharts: Highcharts,
152 +});
1 +import Vue from 'vue'
2 +import VueLayers from 'vuelayers'
3 +import 'vuelayers/lib/style.css' // needs css-loader
4 +
5 +Vue.use(VueLayers)
...\ No newline at end of file ...\ No newline at end of file
1 +# STATIC
2 +
3 +**This directory is not required, you can delete it if you don't want to use it.**
4 +
5 +This directory contains your static files.
6 +Each file inside this directory is mapped to `/`.
7 +Thus you'd want to delete this README.md before deploying to production.
8 +
9 +Example: `/static/robots.txt` is mapped as `/robots.txt`.
10 +
11 +More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#static).
No preview for this file type
1 +<?xml version="1.0" standalone="no"?>
2 +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
3 + "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
4 +<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
5 + width="340.000000pt" height="340.000000pt" viewBox="0 0 340.000000 340.000000"
6 + preserveAspectRatio="xMidYMid meet">
7 +
8 +<g transform="translate(0.000000,340.000000) scale(0.100000,-0.100000)"
9 +fill="#000000" stroke="none">
10 +<path d="M610 3386 c-162 -36 -269 -95 -386 -211 -77 -76 -98 -105 -137 -185
11 +-58 -118 -77 -189 -84 -310 -4 -76 -1 -113 16 -185 57 -237 200 -414 419 -517
12 +116 -55 201 -71 342 -65 119 5 182 20 284 65 l59 26 39 -44 c53 -62 68 -119
13 +68 -260 0 -141 -15 -198 -68 -260 l-39 -44 -61 27 c-116 53 -212 70 -347 64
14 +-133 -6 -192 -20 -303 -75 -297 -147 -470 -505 -393 -812 52 -206 144 -347
15 +298 -456 120 -86 293 -144 431 -144 74 0 216 34 304 73 192 85 343 261 409
16 +477 45 145 25 377 -45 522 l-26 53 33 28 c17 16 57 40 87 55 51 25 64 27 190
17 +27 127 0 138 -2 190 -28 30 -15 69 -39 87 -55 l32 -27 -29 -66 c-50 -111 -64
18 +-182 -64 -309 0 -140 16 -210 80 -338 77 -154 230 -295 381 -352 172 -64 294
19 +-74 438 -36 115 31 172 56 256 112 159 106 267 274 314 489 54 249 -62 542
20 +-280 710 -216 166 -494 201 -749 95 l-77 -32 -23 21 c-77 72 -111 247 -81 411
21 +8 44 50 122 81 151 l23 22 58 -26 c117 -51 169 -62 313 -62 154 0 234 19 350
22 +82 202 110 324 273 381 507 26 110 24 203 -5 319 -38 148 -93 245 -201 353
23 +-76 76 -105 97 -185 137 -227 111 -446 114 -665 7 -196 -95 -356 -298 -400
24 +-508 -31 -146 -12 -331 48 -465 l31 -70 -44 -39 c-63 -54 -131 -73 -260 -73
25 +-131 0 -200 20 -261 73 l-43 39 26 59 c44 100 59 165 65 280 7 140 -12 234
26 +-72 359 -146 302 -495 480 -805 411z m268 -161 c62 -15 162 -60 215 -97 l28
27 +-20 -43 -28 c-24 -15 -99 -84 -167 -153 -118 -119 -127 -126 -173 -132 -83
28 +-12 -138 -78 -138 -166 0 -23 -5 -28 -37 -34 -21 -4 -49 -13 -63 -20 -60 -32
29 +-146 -154 -177 -252 -9 -29 -19 -53 -22 -53 -8 0 -53 73 -85 137 -82 165 -71
30 +377 29 549 69 119 220 230 365 268 72 19 189 19 268 1z m1963 -17 c98 -36 157
31 +-74 229 -147 139 -142 197 -338 156 -532 -15 -68 -67 -184 -104 -229 l-20 -25
32 +-10 25 c-5 14 -73 91 -149 170 -131 136 -141 148 -147 193 -5 35 -17 59 -40
33 +84 -27 30 -40 36 -91 42 l-59 6 -18 62 c-15 50 -27 69 -65 102 -57 51 -135 96
34 +-204 120 l-52 17 34 26 c50 39 150 86 216 103 86 22 240 14 324 -17z m-1573
35 +-287 c48 -90 65 -170 60 -291 -4 -97 -32 -217 -57 -243 -4 -4 -46 22 -93 57
36 +-48 36 -128 93 -180 126 -51 34 -96 69 -100 78 -15 39 -9 49 40 62 26 6 61 20
37 +78 31 59 36 163 210 164 274 l1 30 29 -34 c16 -18 42 -59 58 -90z m948 74 c5
38 +-13 72 -89 149 -167 173 -176 172 -170 38 -257 -50 -32 -131 -89 -179 -125
39 +-48 -37 -91 -63 -95 -59 -3 5 -17 35 -29 68 -43 115 -38 308 11 423 23 54 78
40 +142 88 142 4 0 11 -11 17 -25z m-1462 -495 c7 0 34 -34 61 -76 26 -42 83 -124
41 +126 -182 43 -58 76 -108 74 -112 -3 -4 -37 -18 -77 -31 -61 -20 -94 -24 -193
42 +-24 -138 0 -199 15 -298 75 -69 42 -79 57 -45 65 13 3 89 71 168 150 84 84
43 +151 143 158 140 8 -3 19 -5 26 -5z m1952 -27 c3 -21 17 -56 29 -79 38 -66 210
44 +-173 280 -174 l30 -1 -33 -28 c-47 -41 -146 -88 -222 -107 -88 -22 -251 -15
45 +-335 15 -33 12 -63 25 -67 29 -5 5 21 47 56 94 36 48 93 128 127 180 66 100
46 +73 108 106 108 18 0 23 -7 29 -37z m-1818 -1158 c35 -8 78 -23 97 -32 l34 -18
47 +-45 -60 c-25 -33 -82 -114 -126 -180 -44 -66 -88 -123 -97 -127 -38 -16 -49
48 +-8 -61 40 -6 26 -20 61 -31 78 -36 59 -210 163 -274 164 l-30 1 35 30 c43 37
49 +140 86 206 104 66 18 215 18 292 0z m1964 -17 c66 -25 168 -84 168 -97 0 -4
50 +-11 -11 -24 -17 -14 -5 -90 -72 -169 -149 -166 -162 -177 -166 -223 -87 -25
51 +43 -141 210 -200 287 l-24 32 49 22 c81 34 120 40 241 37 93 -3 128 -8 182
52 +-28z m319 -283 c100 -203 88 -413 -36 -595 -186 -274 -556 -340 -829 -147
53 +l-28 20 28 15 c16 8 92 76 169 152 132 131 143 139 189 146 82 11 136 77 136
54 +165 0 23 5 28 37 34 21 4 49 13 63 20 61 32 148 157 176 253 15 52 16 53 35
55 +36 11 -10 38 -54 60 -99z m-2707 -95 c113 -112 136 -140 136 -164 0 -44 35
56 +-109 70 -129 16 -10 52 -21 79 -24 47 -5 50 -7 56 -41 10 -54 29 -85 80 -130
57 +51 -45 163 -106 213 -117 32 -7 33 -7 16 -26 -52 -58 -256 -129 -369 -129
58 +-349 0 -628 325 -577 672 12 82 56 194 103 260 l22 31 18 -34 c10 -19 79 -95
59 +153 -169z m852 -39 c23 -102 15 -242 -18 -333 -24 -65 -84 -168 -97 -168 -3 0
60 +-13 14 -21 30 -8 16 -76 92 -151 168 -164 167 -164 168 -55 236 39 24 120 81
61 +181 125 l110 81 17 -33 c9 -17 25 -65 34 -106z m956 36 c64 -47 142 -99 172
62 +-117 36 -20 56 -38 56 -50 0 -10 2 -25 5 -32 4 -11 -9 -18 -52 -27 -48 -11
63 +-65 -21 -103 -61 -56 -59 -130 -195 -130 -241 l-1 -34 -30 35 c-121 141 -155
64 +398 -77 583 14 32 24 44 32 38 6 -5 64 -47 128 -94z"/>
65 +</g>
66 +</svg>
1 +export const state = () => ({
2 + codes: [],
3 +});
4 +
5 +export const getters = {
6 + getCodes: (state) => state.codes,
7 +};
8 +
9 +export const actions = {
10 + fetchCodes(context) {
11 + return new Promise((resolve, reject) => {
12 + context.commit('DEL_CODES');
13 + this.$axios.get('/api/code/list')
14 + .then((r) => {
15 + const result = r.data;
16 + context.commit('SET_CODES', result);
17 + resolve(result);
18 + })
19 + .catch((e) => {
20 + reject(e);
21 + });
22 + });
23 + },
24 +};
25 +
26 +export const mutations = {
27 + SET_CODES(state, payload) {
28 + state.pageData = payload;
29 + },
30 + DEL_CODES(state) {
31 + state.pageData = null;
32 + },
33 +};
1 +/* eslint-disable no-underscore-dangle */
2 +export const state = () => ({
3 + zoomLevel: 16,
4 + boundary: {
5 + northEast: {},
6 + southWest: {},
7 + },
8 + stateChange: false,
9 +});
10 +
11 +export const getters = {
12 + getZoomLevel: (state) => state.zoomLevel,
13 + getBoundary: (state) => state.boundary,
14 + getStateChange: (state) => state.stateChange,
15 + isBoundary: (state) => (drone) => {
16 + const checkLatBound = drone.latitude >= state.boundary.southWest.lat && drone.latitude <= state.boundary.northEast.lat;
17 + const checkLngBound = drone.longitude >= state.boundary.southWest.lng && drone.longitude <= state.boundary.northEast.lng;
18 + return checkLatBound && checkLngBound;
19 + },
20 + zoomToActivateTime: (state) => {
21 + switch (state.zoomLevel) {
22 + case 8:
23 + return 30;
24 + case 9:
25 + return 15;
26 + case 10:
27 + return 10;
28 + case 11:
29 + return 6;
30 + case 12:
31 + return 4;
32 + case 13:
33 + return 3;
34 + case 14:
35 + return 2;
36 + default:
37 + return 1;
38 + }
39 + },
40 +};
41 +
42 +export const actions = {
43 + setZoomLevel(context, data) {
44 + context.commit('SET_ZOOM_LEVEL', data);
45 + },
46 + setBoundary(context, data) {
47 + context.commit('SET_BOUNDARY', data);
48 + },
49 + clearZoomLevel(context) {
50 + context.commit('DEL_ZOOM_LEVEL');
51 + },
52 + clearBoundary(context) {
53 + context.commit('DEL_BOUNDARY');
54 + },
55 + clearStateChange(context) {
56 + context.commit('RESET_STATE_CHANGE');
57 + },
58 +};
59 +
60 +export const mutations = {
61 + SET_ZOOM_LEVEL(state, payload) {
62 + state.zoomLevel = payload;
63 + state.stateChange = true;
64 + },
65 + SET_BOUNDARY(state, payload) {
66 + state.boundary = { northEast: payload?._northEast, southWest: payload?._southWest };
67 + state.stateChange = true;
68 + },
69 + DEL_ZOOM_LEVEL(state) {
70 + state.zoomLevel = 16;
71 + },
72 + DEL_BOUNDARY(state) {
73 + state.boundary = {
74 + northEast: {},
75 + southWest: {},
76 + };
77 + },
78 + RESET_STATE_CHANGE(state) {
79 + state.stateChange = false;
80 + },
81 +};
1 +export const state = () => ({
2 + detailData: null,
3 +});
4 +
5 +export const getters = {
6 + getDetailData: (state) => state.detailData,
7 +};
8 +
9 +export const actions = {
10 + fetchDetailData(context, id) {
11 + return new Promise((resolve, reject) => {
12 + context.commit('CLEAR_DETAIL_DATA');
13 + this.$axios.get(`/api/drone/${id}`)
14 + .then((r) => {
15 + context.commit('SET_DETAIL_DATA', r.data);
16 + resolve(r.data);
17 + })
18 + .catch((e) => {
19 + reject(e);
20 + });
21 + });
22 + },
23 +};
24 +
25 +export const mutations = {
26 + SET_DETAIL_DATA(state, payload) {
27 + state.detailData = payload;
28 + },
29 + CLEAR_DETAIL_DATA(state) {
30 + state.detailData = null;
31 + },
32 +};
1 +import calcDistanceFromCoord from '@/utils/CommonFunction/calcDistanceFromCoord';
2 +
3 +export const state = () => ({
4 + detailData: {},
5 +
6 + droneLogs: [],
7 + wholeDroneLogs: [],
8 + selectedLogList: [],
9 + selectedDroneId: null,
10 + accumulatedDistance: 0,
11 + logFilter: {
12 + checkFilter: false,
13 + maker: [],
14 + filteredDroneList: [],
15 + weight: [0, 50],
16 + altitude: [0, 200],
17 + speed: [0, 100],
18 + },
19 +});
20 +
21 +export const getters = {
22 + getDetailData: (state) => state.detailData,
23 + getDroneLogs: (state) => state.droneLogs,
24 + getWholeDroneLog: (state) => state.wholeDroneLogs,
25 + getSelectedLogList: (state) => state.selectedLogList,
26 + getSelectedLastLog: (state) => {
27 + if (state.selectedLogList.length === 0) return null;
28 + return state.selectedLogList[state.selectedLogList.length - 1];
29 + },
30 + getAccumulatedDistance: (state) => state.accumulatedDistance,
31 + getSelectedDroneId: (state) => state.selectedDroneId,
32 + getLogFilter: (state) => state.logFilter,
33 +};
34 +
35 +export const actions = {
36 + fetchDetailInfo(context, id) {
37 + return new Promise((resolve, reject) => {
38 + // context.commit('DEL_DETAIL_DATA');
39 + this.$axios.get(`/api/map/drone/${id}`)
40 + .then((r) => {
41 + const result = r.data;
42 +
43 + let tempLog = null;
44 + let distance = 0;
45 + for (let i = 0; i < result.droneLogs.length; i += 1) {
46 + const currentLog = result.droneLogs[i];
47 + distance += calcDistanceFromCoord(tempLog, currentLog) || 0;
48 + tempLog = currentLog;
49 + }
50 + result.distance = distance;
51 + context.commit('SET_DETAIL_DATA', result);
52 + resolve(result);
53 + })
54 + .catch((e) => {
55 + reject(e);
56 + });
57 + });
58 + },
59 + setDroneLogs(context, data) {
60 + const filter = context.getters.getLogFilter;
61 + const filteredLogs = data.logs.filter((v) => {
62 + let isMaker = true;
63 + if (filter.maker.length !== 0) {
64 + isMaker = !!filter.filteredDroneList.find((e) => Number(e.id) === Number(v.droneId));
65 + }
66 + const isSpeed = (v?.horizontalSpeed >= filter.speed[0] && (filter.speed[1] === 100 ? true : v?.horizontalSpeed <= filter.speed[1]))
67 + || (v?.verticalSpeed >= filter.speed[0] && (filter.speed[1] === 100 ? true : v?.verticalSpeed <= filter.speed[1]));
68 + const isAltitude = (v?.aboveGroundLevel >= filter.altitude[0] && (filter.altitude[1] === 200 ? true : v?.aboveGroundLevel <= filter.altitude[1]))
69 + || (v?.aboveSeaLevel >= filter.altitude[0] && (filter.altitude[1] === 200 ? true : v?.aboveSeaLevel <= filter.altitude[1]));
70 + const isWeight = v?.weight >= filter.weight[0] && (filter.weight[1] === 50 ? true : v?.weight <= filter.weight[1]);
71 + return isAltitude && isSpeed && isMaker;
72 + });
73 + context.commit(data.mutation, filteredLogs);
74 + },
75 + clearDroneLogs(context) {
76 + context.commit('CLEAR_DRONE_LOGS');
77 + },
78 + setSelectedLogList(context, data) {
79 + context.commit('SET_SELECTED_LOG_LIST', data);
80 + },
81 + clearSelectedLogList(context) {
82 + context.commit('CLEAR_SELECTED_LOG_LIST');
83 + },
84 + setSelectedDroneId(context, data) {
85 + context.commit('SET_SELECTED_DRONE_ID', data);
86 + },
87 + clearSelectedDroneId(context) {
88 + context.commit('CLEAR_SELECTED_DRONE_ID');
89 + },
90 + setAccumulatedDistance(context, data) {
91 + context.commit('SET_ACCUMULATED_DISTANCE', data);
92 + },
93 + clearAccumulatedDistance(context) {
94 + context.commit('CLEAR_ACCUMULATED_DISTANCE');
95 + },
96 + setLogFilter(context, data) {
97 + context.commit('SET_LOG_FILTER', data);
98 + },
99 + clearLogFilter(context) {
100 + context.commit('DEL_LOG_FILTER');
101 + },
102 +};
103 +
104 +export const mutations = {
105 + SET_LOG_FILTER(state, payload) {
106 + state.logFilter = payload;
107 + },
108 + DEL_LOG_FILTER(state) {
109 + state.logFilter = {
110 + checkFilter: false,
111 + filteredDroneList: [],
112 + maker: [],
113 + weight: [0, 50],
114 + altitude: [0, 200],
115 + speed: [0, 100],
116 + };
117 + },
118 + //
119 + SET_DETAIL_DATA(state, payload) {
120 + state.detailData = payload;
121 + },
122 + DEL_DETAIL_DATA(state) {
123 + state.detailData = null;
124 + },
125 + //
126 + SET_DRONE_LOGS(state, payload) {
127 + state.droneLogs = payload;
128 + },
129 + CLEAR_DRONE_LOGS(state) {
130 + state.droneLogs = [];
131 + },
132 + //
133 + SET_WHOLE_DRONE_LOGS(state, payload) {
134 + state.wholeDroneLogs = payload;
135 + },
136 + CLEAR_WHOLE_DRONE_LOGS(state) {
137 + state.wholeDroneLogs = [];
138 + },
139 + //
140 + SET_SELECTED_LOG_LIST(state, payload) {
141 + state.selectedLogList.push(payload);
142 + },
143 + CLEAR_SELECTED_LOG_LIST(state) {
144 + state.selectedLogList = [];
145 + },
146 + //
147 + SET_SELECTED_DRONE_ID(state, payload) {
148 + state.selectedDroneId = payload;
149 + },
150 + CLEAR_SELECTED_DRONE_ID(state) {
151 + state.selectedDroneId = null;
152 + },
153 + //
154 + SET_ACCUMULATED_DISTANCE(state, payload) {
155 + state.accumulatedDistance += payload;
156 + },
157 + CLEAR_ACCUMULATED_DISTANCE(state) {
158 + state.accumulatedDistance = 0;
159 + },
160 +};
1 +export const state = () => ({
2 + listContents: [],
3 + fixedDroneList: [],
4 +});
5 +
6 +export const getters = {
7 + getListContents: (state) => state.listContents,
8 + getFixedDroneList: (state) => state.fixedDroneList,
9 +};
10 +
11 +export const actions = {
12 + /* params는 page.js의 pageParams 프로퍼티 중 size, no, total을 제외한 것 */
13 + fetchListContents(context, params) {
14 + return new Promise((resolve, reject) => {
15 + context.commit('CLEAR_LIST_DATA');
16 + this.$axios.get('/api/drone/list', {
17 + params,
18 + })
19 + .then((r) => {
20 + context.commit('SET_LIST_DATA', r.data);
21 + resolve(r.data);
22 + })
23 + .catch((e) => {
24 + console.log(e.response);
25 + reject(e);
26 + });
27 + });
28 + },
29 + setFixedDroneList(context, data) {
30 + context.commit('SET_FIXED_DRONE_LIST', data);
31 + },
32 +};
33 +
34 +export const mutations = {
35 + SET_FIXED_DRONE_LIST(state, payload) {
36 + state.fixedDroneList = payload;
37 + },
38 + SET_LIST_DATA(state, payload) {
39 + state.listContents = payload.drones;
40 + },
41 + CLEAR_LIST_DATA(state) {
42 + state.listContents = [];
43 + },
44 +};
1 +/* eslint-disable no-param-reassign */
2 +export const state = () => ({
3 + pageData: [],
4 + pageParams: {
5 + modelName: null,
6 + maker: null,
7 + minWeight: null,
8 + maxWeight: null,
9 + usageName: null,
10 + pageNo: 1,
11 + pageSize: 10,
12 + total: 0,
13 + },
14 +});
15 +
16 +export const getters = {
17 + getPageData: (state) => state.pageData,
18 + getPageParams: (state) => state.pageParams,
19 + getPagination: (state) => {
20 + const { total, pageSize } = state.pageParams;
21 + const page = state.pageParams.pageNo;
22 + return { total, pageSize, page };
23 + },
24 +};
25 +
26 +export const actions = {
27 + fetchPageData(context, params) {
28 + return new Promise((resolve, reject) => {
29 + context.commit('DEL_PAGE_DATA');
30 + this.$axios
31 + .get('/api/drone/page', {
32 + params,
33 + })
34 + .then((r) => {
35 + const result = r.data;
36 + result.drones.forEach((elem, idx) => {
37 + elem.no = result.totalElement - (result.pageNo * result.pageSize + idx);
38 + });
39 + context.commit('SET_PAGE_DATA', result);
40 + resolve(result);
41 + })
42 + .catch((e) => {
43 + reject(e);
44 + });
45 + });
46 + },
47 + setPageParams(context, data) {
48 + context.commit('SET_PAGE_PARAMS', data);
49 + },
50 + clearPageParams(context) {
51 + context.commit('DEL_PAGE_PARAMS');
52 + },
53 + setPagination(context, params) {
54 + context.commit('SET_PAGINATION', params);
55 + },
56 +};
57 +
58 +export const mutations = {
59 + SET_PAGE_DATA(state, payload) {
60 + state.pageData = payload.drones;
61 + state.pageParams.total = Number(payload.totalElement);
62 + state.pageParams.pageNo = Number(payload.pageNo);
63 + state.pageParams.pageSize = Number(payload.pageSize);
64 + },
65 + DEL_PAGE_DATA(state) {
66 + state.pageData = [];
67 + },
68 + SET_PAGE_PARAMS(state, params) {
69 + state.pageParams = JSON.parse(JSON.stringify(params));
70 + },
71 + SET_PAGINATION: (state, data) => {
72 + state.pageParams.pageNo = data.page;
73 + state.pageParams.pageSize = data.size;
74 + },
75 + DEL_PAGE_PARAMS(state) {
76 + state.pageParams = {
77 + modelName: null,
78 + maker: null,
79 + usage: null,
80 + pageNo: 1,
81 + pageSize: 20,
82 + total: 0,
83 + };
84 + },
85 +};
1 +export const state = () => ({
2 + makers: [],
3 + makerDroneList: [],
4 + // modelNameList: [],
5 +});
6 +
7 +export const getters = {
8 + getMakers: (state) => state.makers,
9 + getMakerDroneList: (state) => state.makerDroneList,
10 + // getModelNameList: (state) => state.modelNameList,
11 +};
12 +
13 +export const actions = {
14 + setMakers(context, data) {
15 + context.commit('DEL_MAKERS');
16 + context.commit('SET_MAKERS', data);
17 + },
18 +};
19 +
20 +export const mutations = {
21 + SET_MAKERS(state, payload) {
22 + state.makers = Object.keys(payload);
23 + Object.entries(payload).forEach(([key, value]) => {
24 + state.makerDroneList.push({ maker: key, children: value });
25 + // state.modelNameList = [...state.modelNameList, ...value];
26 + });
27 + },
28 + DEL_MAKERS(state) {
29 + state.makers = [];
30 + state.makerDroneList = [];
31 + // state.modelNameList = [];
32 + },
33 +};
1 +export const state = () => ({
2 + detailData: null,
3 +});
4 +
5 +export const getters = {
6 + getDetailData: (state) => state.detailData,
7 +};
8 +
9 +export const actions = {
10 + fetchDetailData(context, id) {
11 + return new Promise((resolve, reject) => {
12 + context.commit('CLEAR_DETAIL_DATA');
13 + this.$axios.get(`/api/log/${id}`)
14 + .then((r) => {
15 + context.commit('SET_DETAIL_DATA', r.data);
16 + resolve(r.data);
17 + })
18 + .catch((e) => {
19 + reject(e);
20 + });
21 + });
22 + },
23 +};
24 +
25 +export const mutations = {
26 + SET_DETAIL_DATA(state, payload) {
27 + state.detailData = payload;
28 + },
29 + CLEAR_DETAIL_DATA(state) {
30 + state.detailData = null;
31 + },
32 +};
1 +export const state = () => ({
2 + listContents: [],
3 +});
4 +
5 +export const getters = {
6 + getListContents: (state) => state.listContents,
7 +};
8 +
9 +export const actions = {
10 + /* params는 page.js의 pageParams 프로퍼티 중 size, no, total을 제외한 것 */
11 + fetchListContents(context, params) {
12 + return new Promise((resolve, reject) => {
13 + context.commit('CLEAR_LIST_DATA');
14 + this.$axios.get('/api/log/list', {
15 + params,
16 + })
17 + .then((r) => {
18 + context.commit('SET_LIST_DATA', r.data);
19 + resolve(r.data);
20 + })
21 + .catch((e) => {
22 + console.log(e.response);
23 + reject(e);
24 + });
25 + });
26 + },
27 +};
28 +
29 +export const mutations = {
30 + SET_LIST_DATA(state, payload) {
31 + state.listContents = payload;
32 + },
33 + CLEAR_LIST_DATA(state) {
34 + state.listContents = [];
35 + },
36 + N_MGR_LIST(state) {
37 + },
38 +};
1 +/* eslint-disable no-param-reassign */
2 +export const state = () => ({
3 + pageData: [],
4 + pageParams: {
5 + scheduleId: null,
6 + latitude: null,
7 + longitude: null,
8 + minVerticalSpeed: null,
9 + maxVerticalSpeed: null,
10 + minHorizontalSpeed: null,
11 + maxHorizontalSpeed: null,
12 + minAboveSeaLevel: null,
13 + maxAboveSeaLevel: null,
14 + minAboveGroundLevel: null,
15 + maxAboveGroundLevel: null,
16 + pageNo: 1,
17 + pageSize: 10,
18 + total: 0,
19 + },
20 +});
21 +
22 +export const getters = {
23 + getPageData: (state) => state.pageData,
24 + getPageParams: (state) => state.pageParams,
25 + getPagination: (state) => {
26 + const {total, pageSize} = state.pageParams;
27 + const page = state.pageParams.pageNo;
28 + return {total, pageSize, page};
29 + },
30 +};
31 +
32 +export const actions = {
33 + fetchPageData(context, params) {
34 + return new Promise((resolve, reject) => {
35 + context.commit('DEL_PAGE_DATA');
36 + this.$axios
37 + .get('/api/log/page', {
38 + params,
39 + })
40 + .then((r) => {
41 + const result = r.data;
42 + result.droneLogs.forEach((elem, idx) => {
43 + elem.no =
44 + result.totalElement - (result.pageNo * result.pageSize + idx);
45 + });
46 + context.commit('SET_PAGE_DATA', result);
47 + resolve(result);
48 + })
49 + .catch((e) => {
50 + reject(e);
51 + });
52 + });
53 + },
54 + setPageParams(context, data) {
55 + context.commit('SET_PAGE_PARAMS', data);
56 + },
57 + clearPageParams(context) {
58 + context.commit('DEL_PAGE_PARAMS');
59 + },
60 + setPagination(context, params) {
61 + context.commit('SET_PAGINATION', params);
62 + },
63 +};
64 +
65 +export const mutations = {
66 + SET_PAGE_DATA(state, payload) {
67 + state.pageData = payload.droneLogs;
68 + state.pageParams.total = Number(payload.totalElement);
69 + state.pageParams.pageNo = Number(payload.pageNo);
70 + state.pageParams.pageSize = Number(payload.pageSize);
71 + },
72 + DEL_PAGE_DATA(state) {
73 + state.pageData = [];
74 + },
75 + SET_PAGE_PARAMS(state, params) {
76 + state.pageParams = JSON.parse(JSON.stringify(params));
77 + },
78 + SET_PAGINATION: (state, data) => {
79 + state.pageParams.pageNo = data.page;
80 + state.pageParams.pageSize = data.size;
81 + },
82 + DEL_PAGE_PARAMS(state) {
83 + state.pageParams = {
84 + scheduleId: null,
85 + minSpeed: null,
86 + maxSpeed: null,
87 + minAltitude: null,
88 + maxAltitude: null,
89 + latitude: null,
90 + longitude: null,
91 + pageNo: 1,
92 + pageSize: 10,
93 + total: 0,
94 + };
95 + },
96 +};
1 +export const state = () => ({
2 + detailData: null,
3 +});
4 +
5 +export const getters = {
6 + getDetailData: (state) => state.detailData,
7 +};
8 +
9 +export const actions = {
10 + fetchDetailData(context, id) {
11 + return new Promise((resolve, reject) => {
12 + context.commit('CLEAR_DETAIL_DATA');
13 + this.$axios.get(`/api/member/${id}`)
14 + .then((r) => {
15 + context.commit('SET_DETAIL_DATA', r.data);
16 + resolve(r.data);
17 + })
18 + .catch((e) => {
19 + reject(e);
20 + });
21 + });
22 + },
23 +};
24 +
25 +export const mutations = {
26 + SET_DETAIL_DATA(state, payload) {
27 + state.detailData = payload;
28 + },
29 + CLEAR_DETAIL_DATA(state) {
30 + state.detailData = null;
31 + },
32 +};
1 +export const state = () => ({
2 + listContents: [],
3 +});
4 +
5 +export const getters = {
6 + getListContents: (state) => state.listContents,
7 +};
8 +
9 +export const actions = {
10 + /* params는 page.js의 pageParams 프로퍼티 중 size, no, total을 제외한 것 */
11 + fetchListContents(context, params) {
12 + return new Promise((resolve, reject) => {
13 + context.commit('CLEAR_LIST_DATA');
14 + this.$axios.get('/api/member/list', {
15 + params,
16 + })
17 + .then((r) => {
18 + context.commit('SET_LIST_DATA', r.data);
19 + resolve(r.data);
20 + })
21 + .catch((e) => {
22 + console.log(e.response);
23 + reject(e);
24 + });
25 + });
26 + },
27 +};
28 +
29 +export const mutations = {
30 + SET_LIST_DATA(state, payload) {
31 + state.listContents = payload;
32 + },
33 + CLEAR_LIST_DATA(state) {
34 + state.listContents = [];
35 + },
36 + N_MGR_LIST(state) {
37 + },
38 +};
1 +/* eslint-disable no-param-reassign */
2 +export const state = () => ({
3 + pageData: [],
4 + pageParams: {
5 + pageNo: 1,
6 + pageSize: 20,
7 + total: 0,
8 + },
9 +});
10 +
11 +export const getters = {
12 + getPageData: (state) => state.pageData,
13 + getPageParams: (state) => state.pageParams,
14 + getPagination: (state) => {
15 + const { total, pageSize } = state.pageParams;
16 + const page = state.pageParams.pageNo;
17 + return { total, pageSize, page };
18 + },
19 +};
20 +
21 +export const actions = {
22 + fetchPageData(context, params) {
23 + return new Promise((resolve, reject) => {
24 + context.commit('DEL_PAGE_DATA');
25 + this.$axios.get('/api/member/page', {
26 + params,
27 + })
28 + .then((r) => {
29 + const result = r.data;
30 + result.members.forEach((elem, idx) => {
31 + elem.no = result.totalElement - (result.pageNo * result.pageSize + idx);
32 + });
33 + context.commit('SET_PAGE_DATA', result);
34 + resolve(result);
35 + })
36 + .catch((e) => {
37 + reject(e);
38 + });
39 + });
40 + },
41 + setPageParams(context, data) {
42 + context.commit('SET_PAGE_PARAMS', data);
43 + },
44 + clearPageParams(context) {
45 + context.commit('DEL_PAGE_PARAMS');
46 + },
47 + setPagination(context, params) {
48 + context.commit('SET_PAGINATION', params);
49 + },
50 +};
51 +
52 +export const mutations = {
53 + SET_PAGE_DATA(state, payload) {
54 + state.pageData = payload.members;
55 + state.pageParams.total = Number(payload.totalElement);
56 + state.pageParams.pageNo = Number(payload.pageNo);
57 + state.pageParams.pageSize = Number(payload.pageSize);
58 + },
59 + DEL_PAGE_DATA(state) {
60 + state.pageData = [];
61 + },
62 + SET_PAGE_PARAMS(state, params) {
63 + state.pageParams = JSON.parse(JSON.stringify(params));
64 + },
65 + SET_PAGINATION: (state, data) => {
66 + state.pageParams.pageNo = data.page;
67 + state.pageParams.pageSize = data.size;
68 + },
69 + DEL_PAGE_PARAMS(state) {
70 + state.pageParams = {
71 + pageNo: 1,
72 + pageSize: 20,
73 + total: 0,
74 + };
75 + },
76 +};
1 +# STORE
2 +
3 +**This directory is not required, you can delete it if you don't want to use it.**
4 +
5 +This directory contains your Vuex Store files.
6 +Vuex Store option is implemented in the Nuxt.js framework.
7 +
8 +Creating a file in this directory automatically activates the option in the framework.
9 +
10 +More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store).
1 +export const state = () => ({
2 + detailData: null,
3 +});
4 +
5 +export const getters = {
6 + getDetailData: (state) => state.detailData,
7 +};
8 +
9 +export const actions = {
10 + fetchDetailData(context, id) {
11 + return new Promise((resolve, reject) => {
12 + context.commit('CLEAR_DETAIL_DATA');
13 + this.$axios.get(`/api/schedule/${id}`)
14 + .then((r) => {
15 + context.commit('SET_DETAIL_DATA', r.data);
16 + resolve(r.data);
17 + })
18 + .catch((e) => {
19 + reject(e);
20 + });
21 + });
22 + },
23 +};
24 +
25 +export const mutations = {
26 + SET_DETAIL_DATA(state, payload) {
27 + state.detailData = payload;
28 + },
29 + CLEAR_DETAIL_DATA(state) {
30 + state.detailData = null;
31 + },
32 +};
1 +export const state = () => ({
2 + listContents: [],
3 +});
4 +
5 +export const getters = {
6 + getListContents: (state) => state.listContents,
7 +};
8 +
9 +export const actions = {
10 + /* params는 page.js의 pageParams 프로퍼티 중 size, no, total을 제외한 것 */
11 + fetchListContents(context, params) {
12 + return new Promise((resolve, reject) => {
13 + context.commit('CLEAR_LIST_DATA');
14 + this.$axios.get('/api/schedule/list', {
15 + params,
16 + })
17 + .then((r) => {
18 + context.commit('SET_LIST_DATA', r.data);
19 + resolve(r.data);
20 + })
21 + .catch((e) => {
22 + console.log(e.response);
23 + reject(e);
24 + });
25 + });
26 + },
27 +};
28 +
29 +export const mutations = {
30 + SET_LIST_DATA(state, payload) {
31 + state.listContents = payload;
32 + },
33 + CLEAR_LIST_DATA(state) {
34 + state.listContents = [];
35 + },
36 + N_MGR_LIST(state) {
37 + },
38 +};
1 +/* eslint-disable no-param-reassign */
2 +export const state = () => ({
3 + pageData: [],
4 + pageParams: {
5 + droneId: null,
6 + startTime: null,
7 + terminateTime: null,
8 + pageNo: 1,
9 + pageSize: 10,
10 + total: 0,
11 + },
12 +});
13 +
14 +export const getters = {
15 + getPageData: (state) => state.pageData,
16 + getPageParams: (state) => state.pageParams,
17 + getPagination: (state) => {
18 + const {total, pageSize} = state.pageParams;
19 + const page = state.pageParams.pageNo;
20 + return {total, pageSize, page};
21 + },
22 +};
23 +
24 +export const actions = {
25 + fetchPageData(context, params) {
26 + return new Promise((resolve, reject) => {
27 + context.commit('DEL_PAGE_DATA');
28 + this.$axios
29 + .get('/api/schedule/page', {
30 + params,
31 + })
32 + .then((r) => {
33 + const result = r.data;
34 + result.schedules.forEach((elem, idx) => {
35 + elem.no =
36 + result.totalElement - (result.pageNo * result.pageSize + idx);
37 + });
38 + context.commit('SET_PAGE_DATA', result);
39 + resolve(result);
40 + })
41 + .catch((e) => {
42 + reject(e);
43 + });
44 + });
45 + },
46 + setPageParams(context, data) {
47 + context.commit('SET_PAGE_PARAMS', data);
48 + },
49 + clearPageParams(context) {
50 + context.commit('DEL_PAGE_PARAMS');
51 + },
52 + setPagination(context, params) {
53 + context.commit('SET_PAGINATION', params);
54 + },
55 +};
56 +
57 +export const mutations = {
58 + SET_PAGE_DATA(state, payload) {
59 + state.pageData = payload.schedules;
60 + state.pageParams.total = Number(payload.totalElement);
61 + state.pageParams.pageNo = Number(payload.pageNo);
62 + state.pageParams.pageSize = Number(payload.pageSize);
63 + },
64 + DEL_PAGE_DATA(state) {
65 + state.pageData = [];
66 + },
67 + SET_PAGE_PARAMS(state, params) {
68 + state.pageParams = JSON.parse(JSON.stringify(params));
69 + },
70 + SET_PAGINATION: (state, data) => {
71 + state.pageParams.pageNo = data.page;
72 + state.pageParams.pageSize = data.size;
73 + },
74 + DEL_PAGE_PARAMS(state) {
75 + state.pageParams = {
76 + droneId: null,
77 + startTime: null,
78 + terminateTime: null,
79 + pageNo: 1,
80 + pageSize: 10,
81 + total: 0,
82 + };
83 + },
84 +};
1 +export const state = () => ({
2 + settings: {},
3 +});
4 +
5 +export const getters = {
6 + getSettings: (state) => state.settings,
7 +};
8 +
9 +export const actions = {
10 + setSettings(context, params) {
11 + context.commit('CHANGE_SETTINGS', params);
12 + },
13 + async nuxtServerInit({ dispatch }, ctx) {
14 + dispatch('Code/fetchCodes');
15 + },
16 +};
17 +
18 +export const mutations = {
19 + CHANGE_SETTINGS(state, payload) {
20 + state.settings = payload;
21 + },
22 +};
1 +export default [
2 + {
3 + title: 'No',
4 + dataIndex: 'id',
5 + key: 'id',
6 + scopedSlots: {
7 + customRender: 'id',
8 + },
9 + align: 'center',
10 + ellipsis: true,
11 + width: 50,
12 + },
13 + {
14 + title: '모델명',
15 + dataIndex: 'modelName',
16 + key: 'modelName',
17 + scopedSlots: {
18 + customRender: 'modelName',
19 + },
20 + align: 'center',
21 + ellipsis: true,
22 + width: 200,
23 + },
24 + {
25 + title: '제조사',
26 + dataIndex: 'maker',
27 + key: 'maker',
28 + scopedSlots: {
29 + customRender: 'maker',
30 + },
31 + align: 'center',
32 + ellipsis: true,
33 + width: 120,
34 + },
35 + {
36 + title: '종류',
37 + dataIndex: 'usageName',
38 + key: 'usageName',
39 + scopedSlots: {
40 + customRender: 'usageName',
41 + },
42 + align: 'center',
43 + ellipsis: true,
44 + width: 60,
45 + },
46 + {
47 + title: '무게',
48 + dataIndex: 'weight',
49 + key: 'weight',
50 + scopedSlots: {
51 + customRender: 'weight',
52 + },
53 + align: 'center',
54 + ellipsis: true,
55 + width: 60,
56 + },
57 + {
58 + title: '제원',
59 + dataIndex: 'specification',
60 + key: 'specification',
61 + scopedSlots: {
62 + customRender: 'specification',
63 + },
64 + align: 'center',
65 + ellipsis: true,
66 + width: 60,
67 + },
68 +];
1 +export default [
2 + {
3 + title: 'No',
4 + dataIndex: 'id',
5 + key: 'id',
6 + scopedSlots: {
7 + customRender: 'id',
8 + },
9 + align: 'center',
10 + ellipsis: true,
11 + width: 50,
12 + },
13 + {
14 + title: '모델명',
15 + dataIndex: 'modelName',
16 + key: 'modelName',
17 + scopedSlots: {
18 + customRender: 'modelName',
19 + },
20 + align: 'center',
21 + ellipsis: true,
22 + width: 150,
23 + },
24 + {
25 + title: 'Schedule ID',
26 + dataIndex: 'scheduleId',
27 + key: 'scheduleId',
28 + scopedSlots: {
29 + customRender: 'scheduleId',
30 + },
31 + align: 'center',
32 + ellipsis: true,
33 + width: 60,
34 + },
35 + {
36 + title: '수평 속도',
37 + dataIndex: 'horizontalSpeed',
38 + key: 'horizontalSpeed',
39 + scopedSlots: {
40 + customRender: 'horizontalSpeed',
41 + },
42 + align: 'center',
43 + ellipsis: true,
44 + width: 60,
45 + },
46 + {
47 + title: '수직 속도',
48 + dataIndex: 'verticalSpeed',
49 + key: 'verticalSpeed',
50 + scopedSlots: {
51 + customRender: 'verticalSpeed',
52 + },
53 + align: 'center',
54 + ellipsis: true,
55 + width: 60,
56 + },
57 + {
58 + title: '지면 고도',
59 + dataIndex: 'aboveGroundLevel',
60 + key: 'aboveGroundLevel',
61 + scopedSlots: {
62 + customRender: 'aboveGroundLevel',
63 + },
64 + align: 'center',
65 + ellipsis: true,
66 + width: 60,
67 + },
68 + {
69 + title: '해발 고도',
70 + dataIndex: 'aboveSeaLevel',
71 + key: 'aboveSeaLevel',
72 + scopedSlots: {
73 + customRender: 'aboveSeaLevel',
74 + },
75 + align: 'center',
76 + ellipsis: true,
77 + width: 60,
78 + },
79 + {
80 + title: '위도',
81 + dataIndex: 'latitude',
82 + key: 'latitude',
83 + scopedSlots: {
84 + customRender: 'latitude',
85 + },
86 + align: 'center',
87 + ellipsis: true,
88 + width: 60,
89 + },
90 + {
91 + title: '경도',
92 + dataIndex: 'longitude',
93 + key: 'longitude',
94 + scopedSlots: {
95 + customRender: 'longitude',
96 + },
97 + align: 'center',
98 + ellipsis: true,
99 + width: 60,
100 + },
101 +];
1 +export default [
2 + {
3 + title: 'No',
4 + dataIndex: 'id',
5 + key: 'id',
6 + scopedSlots: {
7 + customRender: 'id',
8 + },
9 + align: 'center',
10 + ellipsis: true,
11 + width: 50,
12 + },
13 + {
14 + title: '모델명',
15 + dataIndex: 'modelName',
16 + key: 'modelName',
17 + scopedSlots: {
18 + customRender: 'modelName',
19 + },
20 + align: 'center',
21 + ellipsis: true,
22 + width: 200,
23 + },
24 + {
25 + title: '시작 날짜 / 시간',
26 + dataIndex: 'startTime',
27 + key: 'startTime',
28 + scopedSlots: {
29 + customRender: 'startTime',
30 + },
31 + align: 'center',
32 + ellipsis: true,
33 + width: 120,
34 + },
35 + {
36 + title: '시작 위도',
37 + dataIndex: 'startLatitude',
38 + key: 'startLatitude',
39 + scopedSlots: {
40 + customRender: 'startLatitude',
41 + },
42 + align: 'center',
43 + ellipsis: true,
44 + width: 60,
45 + },
46 + {
47 + title: '시작 경도',
48 + dataIndex: 'startLongitude',
49 + key: 'startLongitude',
50 + scopedSlots: {
51 + customRender: 'startLongitude',
52 + },
53 + align: 'center',
54 + ellipsis: true,
55 + width: 60,
56 + },
57 + {
58 + title: '종료 날짜 / 시간',
59 + dataIndex: 'terminateTime',
60 + key: 'terminateTime',
61 + scopedSlots: {
62 + customRender: 'terminateTime',
63 + },
64 + align: 'center',
65 + ellipsis: true,
66 + width: 120,
67 + },
68 + {
69 + title: '종료 위도',
70 + dataIndex: 'terminateLatitude',
71 + key: 'terminateLatitude',
72 + scopedSlots: {
73 + customRender: 'terminateLatitude',
74 + },
75 + align: 'center',
76 + ellipsis: true,
77 + width: 60,
78 + },
79 + {
80 + title: '종료 경도',
81 + dataIndex: 'terminateLongitude',
82 + key: 'terminateLongitude',
83 + scopedSlots: {
84 + customRender: 'terminateLongitude',
85 + },
86 + align: 'center',
87 + ellipsis: true,
88 + width: 60,
89 + },
90 +];
1 +export const droneCategory = [
2 + { label: '전체', value: 0 },
3 + { label: '촬영용', value: 1 },
4 + { label: '레이싱용', value: 2 },
5 + { label: '완구용', value: 3 },
6 +];
1 +/* eslint-disable no-mixed-operators */
2 +export default function calcAngleFromCoord(oldVal, newVal) {
3 + if ((oldVal == null || oldVal.latitude == null) || (newVal == null || newVal.latitude == null)) return undefined;
4 +
5 + const x = newVal.latitude - oldVal.latitude;
6 + const y = newVal.longitude - oldVal.longitude;
7 +
8 + const radian = Math.atan2(y, x);
9 + return radian * 180 / Math.PI;
10 +}
1 +function toRad(deg) {
2 + return deg * (Math.PI / 180);
3 +}
4 +// use Haversine Formula
5 +export default function calcDistanceFromCoord(oldVal, newVal) {
6 + if ((oldVal == null || oldVal.latitude == null) || (newVal == null || newVal.latitude == null)) return undefined;
7 +
8 + const R = 6371000;
9 +
10 + const φ1 = toRad(oldVal.latitude);
11 + const φ2 = toRad(newVal.latitude);
12 + const Δφ = toRad(Number(newVal.latitude) - Number(oldVal.latitude));
13 + const Δλ = toRad(Number(newVal.longitude) - Number(oldVal.longitude));
14 +
15 + const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2)
16 + + Math.cos(φ1) * Math.cos(φ2)
17 + * Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
18 + const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
19 +
20 + const result = R * c;
21 +
22 + return Number(result.toFixed(1)); // Distance in meter
23 +}
1 +import calcDistanceFromCoord from '@/utils/CommonFunction/calcDistanceFromCoord';
2 +import calcAngleFromCoord from '@/utils/CommonFunction/calcAngleFromCoord';
3 +
4 +export default {
5 + data() {
6 + return {
7 + };
8 + },
9 + computed: {
10 + calcAngle() {
11 + return (oldVal, newVal) => calcAngleFromCoord(oldVal, newVal);
12 + },
13 + calcVelocity() {
14 + return (oldVal, newVal) => `${calcDistanceFromCoord(oldVal, newVal)}m/s`;
15 + },
16 + calcDistance() {
17 + return (logs) => {
18 + let sum = 0;
19 + for (let i = 1; i < logs.length; i += 1) {
20 + const oldVal = logs[i - 1];
21 + const newVal = logs[i];
22 + sum += calcDistanceFromCoord(oldVal, newVal);
23 + }
24 + return sum;
25 + };
26 + },
27 + },
28 + watch: {
29 +
30 + },
31 + created() {
32 +
33 + },
34 + mounted() {
35 +
36 + },
37 + beforeDestroy() {
38 +
39 + },
40 + methods: {
41 + },
42 +};
1 +/*
2 + * 전역으로 사용되는 mixin 지정입니다.
3 + */
4 +import calcDistanceFromCoord from '@/utils/CommonFunction/calcDistanceFromCoord';
5 +import calcAngleFromCoord from '@/utils/CommonFunction/calcAngleFromCoord';
6 +import { mapActions, mapGetters } from 'vuex';
7 +
8 +export default {
9 + data() {
10 + return {
11 + socketServerCnt: 5,
12 + droneCnt: 1000,
13 + selectedDroneLog: {},
14 + accumulatedDistance: 0,
15 + socketOnGoing: 0,
16 + };
17 + },
18 + computed: {
19 + ...mapGetters('Drone/drone', {
20 + getDroneLogs: 'getDroneLogs',
21 + getSelectedLogList: 'getSelectedLogList',
22 + getSelectedDroneId: 'getSelectedDroneId',
23 + getSelectedLastLog: 'getSelectedLastLog',
24 + getAccumulatedDistance: 'getAccumulatedDistance',
25 + }),
26 + ...mapGetters('Drone/Map', {
27 + getZoomLevel: 'getZoomLevel',
28 + getBoundary: 'getBoundary',
29 + isBoundary: 'isBoundary',
30 + zoomToActivateTime: 'zoomToActivateTime',
31 + getStateChange: 'getStateChange',
32 + }),
33 + mc_dateTime() {
34 + return (dateTime) => {
35 + if (dateTime == null || dateTime === 'Invalid Date' || dateTime === '') return '';
36 + return this.$dayjs(dateTime).format('YYYY-MM-DD HH:mm:ss');
37 + };
38 + },
39 + mc_date() {
40 + return (date) => {
41 + if (date == null || date === 'Invalid Date' || date === '') return '';
42 + return this.$dayjs(date).format('YYYY-MM-DD');
43 + };
44 + },
45 + zoomToSee() {
46 + return (index) => {
47 + const div = index % 10;
48 + switch (this.zoom) {
49 + case 8:
50 + return div < 2;
51 + case 9:
52 + return div < 4;
53 + case 10:
54 + return div < 6;
55 + case 11:
56 + return div < 8;
57 + default:
58 + return true;
59 + }
60 + };
61 + },
62 + },
63 + methods: {
64 + ...mapActions('Drone/drone', {
65 + setDroneLogs: 'setDroneLogs',
66 + setSelectedLogList: 'setSelectedLogList',
67 + setAccumulatedDistance: 'setAccumulatedDistance',
68 + setSelectedDroneId: 'setSelectedDroneId',
69 + }),
70 + ...mapActions('Drone/Map', {
71 + clearStateChange: 'clearStateChange',
72 + }),
73 + connect() {
74 + if (window.clientSocket == null || window.clientSocket?.readyState === 3) {
75 + window.clientSocket = new WebSocket('ws://14.33.35.148:20206/drone');
76 +
77 + window.clientSocket.onopen = () => {
78 + console.log('socket on open');
79 + };
80 + window.clientSocket.onerror = () => {
81 + console.log('socket on error');
82 + };
83 + window.clientSocket.onmessage = (data) => {
84 + const logData = JSON.parse(data.data);
85 +
86 + let logInfo = null;
87 + const filterDroneLogs = logData.data.droneLog.filter((v, idx) => {
88 + if (this.getSelectedDroneId && v.droneId === this.getSelectedDroneId) logInfo = v;
89 + return this.isBoundary(v) && this.zoomToSee(idx);
90 + });
91 +
92 + if (this.socketOnGoing % this.zoomToActivateTime === 0 || this.getStateChange) {
93 + this.setDroneLogs({ logs: filterDroneLogs, mutation: 'SET_DRONE_LOGS' });
94 + this.setDroneLogs({ logs: logData.data.droneLog, mutation: 'SET_WHOLE_DRONE_LOGS' });
95 + this.clearStateChange();
96 + }
97 +
98 + if (logInfo) {
99 + this.setAccumulatedDistance(calcDistanceFromCoord(this.getSelectedLastLog, logInfo) || 0);
100 + logInfo.angle = calcAngleFromCoord(this.getSelectedLastLog, logInfo) || 0;
101 + logInfo.distance = this.getAccumulatedDistance;
102 + this.setSelectedLogList(logInfo);
103 + }
104 +
105 + this.socketOnGoing += 1;
106 + };
107 + window.clientSocket.onclose = () => {
108 + console.log('socket on close');
109 + };
110 + }
111 + },
112 + // connectMultiClient() {
113 + // for (let j = 0; j < this.droneCnt * this.socketServerCnt; j += 1) {
114 + // this.drones.push({ x: 0, y: 0 });
115 + // }
116 + // for (let i = 0; i < this.socketServerCnt; i += 1) {
117 + // if (
118 + // window[`wsSocket${i}`] == null
119 + // || window[`wsSocket${i}`].readyState === 3
120 + // ) {
121 + // window[`wsSocket${i}`] = new WebSocket(
122 + // 'ws://14.33.35.148:20206/drone',
123 + // );
124 + // window[`wsSocket${i}`].onopen = () => {
125 + // console.log(`socket${i} on open`);
126 + // };
127 + // window[`wsSocket${i}`].onerror = (e) => {
128 + // console.log(`socket${i} on error occur ${e}`);
129 + // };
130 + // window[`wsSocket${i}`].onmessage = (data) => {
131 + // const logData = JSON.parse(data.data);
132 + // console.log(
133 + // `message${i} ${this.$dayjs().format('HH:mm:ss')} : `,
134 + // logData.droneData,
135 + // );
136 + // setTimeout(() => {
137 + // for (let k = 0; k < this.droneCnt; k += 1) {
138 + // this.drones[i * (k + 1) + k].x = logData.droneData[k].x;
139 + // this.drones[i * (k + 1) + k].y = logData.droneData[k].y;
140 + // }
141 + // }, 0);
142 + // };
143 + // window[`wsSocket${i}`].onclose = () => {
144 + // console.log(`socket${i} closed`);
145 + // };
146 + // }
147 + // }
148 + // },
149 + disconnect() {
150 + if (window.clientSocket && window.clientSocket.readyState === 1) {
151 + window.clientSocket.close();
152 + window.clientSocket = null;
153 + }
154 + },
155 + },
156 +};
1 +config:
2 + target: "ws://localhost:8080/1"
3 + phases:
4 + - duration: 20
5 + arrivalRate: 10
6 +scenarios:
7 + - engine: "ws"
8 + flow:
9 + - think: 1
This diff could not be displayed because it is too large.