2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
20 |
21 |
--------------------------------------------------------------------------------
/docs/gatsby-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | siteMetadata: {
3 | title: `DroneSym - Documentation`,
4 | description: `Project Documentation about project DroneSym`,
5 | author: `@gatsbyjs`,
6 | },
7 | plugins: [
8 | `gatsby-plugin-react-helmet`,
9 | `gatsby-plugin-mdx`,
10 | {
11 | resolve: `gatsby-source-filesystem`,
12 | options: {
13 | name: `images`,
14 | path: `${__dirname}/src/images`,
15 | },
16 | },
17 | `gatsby-transformer-sharp`,
18 | `gatsby-plugin-sharp`,
19 | {
20 | resolve: `gatsby-plugin-manifest`,
21 | options: {
22 | name: `gatsby-starter-default`,
23 | short_name: `starter`,
24 | start_url: `/`,
25 | background_color: `#663399`,
26 | theme_color: `#663399`,
27 | display: `minimal-ui`,
28 | icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site.
29 | },
30 | },
31 | // this (optional) plugin enables Progressive Web App + Offline functionality
32 | // To learn more, visit: https://gatsby.dev/offline
33 | // `gatsby-plugin-offline`,
34 | ],
35 | }
36 |
--------------------------------------------------------------------------------
/docs/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 gatsbyjs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, async } from '@angular/core/testing';
2 |
3 | import { AppComponent } from './app.component';
4 |
5 | describe('AppComponent', () => {
6 | beforeEach(async(() => {
7 | TestBed.configureTestingModule({
8 | declarations: [
9 | AppComponent
10 | ],
11 | }).compileComponents();
12 | }));
13 |
14 | it('should create the app', async(() => {
15 | const fixture = TestBed.createComponent(AppComponent);
16 | const app = fixture.debugElement.componentInstance;
17 | expect(app).toBeTruthy();
18 | }));
19 |
20 | it(`should have as title 'app'`, async(() => {
21 | const fixture = TestBed.createComponent(AppComponent);
22 | const app = fixture.debugElement.componentInstance;
23 | expect(app.title).toEqual('app');
24 | }));
25 |
26 | it('should render title in a h1 tag', async(() => {
27 | const fixture = TestBed.createComponent(AppComponent);
28 | fixture.detectChanges();
29 | const compiled = fixture.debugElement.nativeElement;
30 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!!');
31 | }));
32 | });
33 |
--------------------------------------------------------------------------------
/dronesym-python/flask-api/src/node.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 | import json
3 | import time
4 | import requests
5 |
6 |
7 | apiUrl = 'http://localhost:3000/dronesym/api/node'
8 |
9 |
10 | def update_drone(id, status):
11 | try:
12 | response = requests.post(
13 | apiUrl + '/update/' + str(id),
14 | json=status,
15 | headers={
16 | 'Content-Type': 'application/json'})
17 | return response.json()
18 | except requests.ConnectionError:
19 | print("Retrying...")
20 | time.sleep(0.1)
21 | return update_drone(id, status)
22 |
23 |
24 | def get_drone_by_id(id):
25 | try:
26 | response = requests.get(apiUrl + '/get/' + id)
27 | return response.json()
28 | except requests.ConnectionError:
29 | print("Retrying...")
30 | time.sleep(1)
31 | return get_drone_by_id(id)
32 |
33 |
34 | def get_drones():
35 | try:
36 | response = requests.get(apiUrl + '/get')
37 | return response.json()
38 | except requests.ConnectionError:
39 | print("Retrying...")
40 | time.sleep(1)
41 | return get_drones()
42 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | # Prerequisites
2 |
3 | Please answer the following questions for yourself before submitting an issue. **YOU MAY DELETE THE PREREQUISITES SECTION.**
4 |
5 | - [ ] I am running the latest version
6 | - [ ] I checked the documentation and found no answer
7 | - [ ] I checked to make sure that this issue has not already been filed
8 | - [ ] I'm reporting the issue to the correct repository (for multi-repository projects)
9 |
10 | # Expected Behavior
11 |
12 | Please describe the behavior you are expecting
13 |
14 | # Current Behavior
15 |
16 | What is the current behavior?
17 |
18 | # Failure Information (for bugs)
19 |
20 | Please help provide information about the failure if this is a bug. If it is not a bug, please remove the rest of this template.
21 |
22 | ## Steps to Reproduce
23 |
24 | Please provide detailed steps for reproducing the issue.
25 |
26 | 1. step 1
27 | 2. step 2
28 | 3. you get it...
29 |
30 | ## Context
31 |
32 | Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions.
33 |
34 |
35 | ## Failure Logs
36 |
37 | Please include any relevant log snippets or files here.
38 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/confirm-dialog/confirm-dialog.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ message }}
4 |
5 |
6 |
22 |
23 |
27 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/long-stack-trace-zone';
4 | import 'zone.js/dist/proxy.js';
5 | import 'zone.js/dist/sync-test';
6 | import 'zone.js/dist/jasmine-patch';
7 | import 'zone.js/dist/async-test';
8 | import 'zone.js/dist/fake-async-test';
9 | import { getTestBed } from '@angular/core/testing';
10 | import {
11 | BrowserDynamicTestingModule,
12 | platformBrowserDynamicTesting
13 | } from '@angular/platform-browser-dynamic/testing';
14 |
15 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
16 | declare const __karma__: any;
17 | declare const require: any;
18 |
19 | // Prevent Karma from running prematurely.
20 | __karma__.loaded = function () {};
21 |
22 | // First, initialize the Angular testing environment.
23 | getTestBed().initTestEnvironment(
24 | BrowserDynamicTestingModule,
25 | platformBrowserDynamicTesting()
26 | );
27 | // Then we find all the tests.
28 | const context = require.context('./', true, /\.spec\.ts$/);
29 | // And load the modules.
30 | context.keys().map(context);
31 | // Finally, start Karma to run the tests.
32 | __karma__.start();
33 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/login/login.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | import { MaterializeAction } from 'angular2-materialize';
4 | import { Router } from '@angular/router';
5 | import { UserService } from '../user-service/user.service';
6 |
7 | declare var Materialize: any;
8 |
9 | @Component({
10 | selector: 'app-login',
11 | templateUrl: './login.component.html',
12 | styleUrls: ['./login.component.css']
13 | })
14 | export class LoginComponent {
15 | private user: any = {};
16 |
17 | constructor(private router: Router, private userService: UserService) {
18 | this.user.username = '';
19 | this.user.password = '';
20 | }
21 |
22 | setUsername($event) {
23 | this.user.username = $event.target.value;
24 | }
25 |
26 | setPassword($event) {
27 | this.user.password = $event.target.value;
28 | }
29 |
30 | onLogin($event) {
31 | $event.preventDefault();
32 | this.userService.login(this.user.username, this.user.password)
33 | .then((res) => {
34 | if (res.status === 'OK') {
35 | this.router.navigate(['dashboard/map']);
36 | } else {
37 | Materialize.toast(res.msg, 4000);
38 | }
39 | });
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # Typescript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # dotenv environment variables file
55 | .env
56 |
57 | # gatsby files
58 | .cache/
59 | public
60 |
61 | # Mac files
62 | .DS_Store
63 |
64 | # Yarn
65 | yarn-error.log
66 | .pnp/
67 | .pnp.js
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
--------------------------------------------------------------------------------
/docs/src/components/layout.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Layout component that queries for data
3 | * with Gatsby's useStaticQuery component
4 | *
5 | * See: https://www.gatsbyjs.org/docs/use-static-query/
6 | */
7 |
8 | import React from "react"
9 | import PropTypes from "prop-types"
10 | import { useStaticQuery, graphql } from "gatsby"
11 |
12 | import Header from "./header"
13 | import "./layout.css"
14 |
15 | const Layout = ({ children }) => {
16 | const data = useStaticQuery(graphql`
17 | query SiteTitleQuery {
18 | site {
19 | siteMetadata {
20 | title
21 | }
22 | }
23 | }
24 | `)
25 |
26 | return (
27 | <>
28 |
29 |
37 |
{children}
38 |
39 | © {new Date().getFullYear()}, Built with
40 | {` `}
41 | Gatsby
42 |
43 |
44 | >
45 | )
46 | }
47 |
48 | Layout.propTypes = {
49 | children: PropTypes.node.isRequired,
50 | }
51 |
52 | export default Layout
53 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dronesym-frontend",
3 | "short_name": "dronesym-frontend",
4 | "theme_color": "#1976d2",
5 | "background_color": "#fafafa",
6 | "display": "standalone",
7 | "scope": "/",
8 | "start_url": "/",
9 | "icons": [
10 | {
11 | "src": "assets/icons/icon-72x72.png",
12 | "sizes": "72x72",
13 | "type": "image/png"
14 | },
15 | {
16 | "src": "assets/icons/icon-96x96.png",
17 | "sizes": "96x96",
18 | "type": "image/png"
19 | },
20 | {
21 | "src": "assets/icons/icon-128x128.png",
22 | "sizes": "128x128",
23 | "type": "image/png"
24 | },
25 | {
26 | "src": "assets/icons/icon-144x144.png",
27 | "sizes": "144x144",
28 | "type": "image/png"
29 | },
30 | {
31 | "src": "assets/icons/icon-152x152.png",
32 | "sizes": "152x152",
33 | "type": "image/png"
34 | },
35 | {
36 | "src": "assets/icons/icon-192x192.png",
37 | "sizes": "192x192",
38 | "type": "image/png"
39 | },
40 | {
41 | "src": "assets/icons/icon-384x384.png",
42 | "sizes": "384x384",
43 | "type": "image/png"
44 | },
45 | {
46 | "src": "assets/icons/icon-512x512.png",
47 | "sizes": "512x512",
48 | "type": "image/png"
49 | }
50 | ]
51 | }
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/reset-code-dialog/reset-code-dialog.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, EventEmitter, Output, Input } from '@angular/core';
2 | import { MaterializeAction } from 'angular2-materialize';
3 |
4 | @Component({
5 | selector: 'app-reset-code-dialog',
6 | templateUrl: './reset-code-dialog.component.html',
7 | styleUrls: ['./reset-code-dialog.component.css']
8 | })
9 | export class ResetCodeDialogComponent implements OnInit {
10 | modalActions = new EventEmitter
();
11 | code: string;
12 | codes:string;
13 | @Output('onResponse') onResponse = new EventEmitter();
14 | @Input('message') message: string;
15 | @Input('inputEnabled') inputEnabled: boolean;
16 | @Input()
17 |
18 | set show(show: boolean) {
19 | if (show) {
20 | this.modalActions.emit({ action: 'modal', params: ['open']});
21 | } else {
22 | this.modalActions.emit({ action: 'modal', params: ['close']});
23 | }
24 | }
25 |
26 | constructor() {
27 | this.code = '';
28 | }
29 |
30 | ngOnInit() {
31 | this.code = '';
32 | }
33 |
34 | public setCode($event) {
35 | this.code = $event.target.value;
36 | }
37 | public cancel() {
38 | this.onResponse.emit('DIALOG_CANCEL');
39 | }
40 |
41 | public confirm() {
42 | this.onResponse.emit({'message' : 'DIALOG_CONFIRM', 'code' : this.code});
43 | }
44 |
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/dronesym-node/Models/user.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 | const bcrypt = require('bcrypt');
3 |
4 | const userSchema = new mongoose.Schema({
5 | uname: {
6 | type: String,
7 | required: true,
8 | },
9 |
10 | password: {
11 | type: String,
12 | required: true,
13 | },
14 | role: {
15 | type: String,
16 | enum: ['user', 'admin'],
17 | lowercase: true,
18 | required: true,
19 | default: 'user',
20 | },
21 | email: {
22 | type: String,
23 | required: true,
24 | },
25 | groups: {
26 | type: [{
27 | groupId: String,
28 | groupName: String,
29 | }],
30 | default: [],
31 | },
32 |
33 | }, {timestamps: true});
34 |
35 | userSchema.pre('save', function(next) {
36 | const user = this;
37 | const SALT_FACTOR = 5;
38 |
39 | bcrypt.genSalt(SALT_FACTOR, function(err, salt) {
40 | if (err) {
41 | return next(err);
42 | }
43 |
44 | bcrypt.hash(user.password, salt, null, function(err, hash) {
45 | if (err) {
46 | return next(err);
47 | }
48 |
49 | user.password = hash;
50 | next();
51 | });
52 | });
53 | });
54 |
55 | userSchema.methods.comparePassword = function(password, callBack) {
56 | bcrypt.compare(password, this.password, function(err, isMatch) {
57 | if (err) {
58 | callBack(err, null);
59 | return;
60 | }
61 | callBack(null, isMatch);
62 | });
63 | };
64 |
65 | module.exports = mongoose.model('User', userSchema);
66 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gatsby-starter-default",
3 | "private": true,
4 | "description": "A simple starter to get up and developing quickly with Gatsby",
5 | "version": "0.1.0",
6 | "author": "Kyle Mathews ",
7 | "dependencies": {
8 | "@mdx-js/mdx": "^1.1.5",
9 | "@mdx-js/react": "^1.1.5",
10 | "gatsby": "^2.13.31",
11 | "gatsby-image": "^2.2.6",
12 | "gatsby-plugin-manifest": "^2.2.3",
13 | "gatsby-plugin-mdx": "^1.0.22",
14 | "gatsby-plugin-offline": "^2.2.4",
15 | "gatsby-plugin-react-helmet": "^3.1.2",
16 | "gatsby-plugin-sharp": "^2.2.8",
17 | "gatsby-source-filesystem": "^2.1.5",
18 | "gatsby-transformer-sharp": "^2.2.4",
19 | "prop-types": "^15.7.2",
20 | "react": "^16.8.6",
21 | "react-dom": "^16.8.6",
22 | "react-helmet": "^5.2.1"
23 | },
24 | "devDependencies": {
25 | "prettier": "^1.18.2"
26 | },
27 | "keywords": [
28 | "gatsby"
29 | ],
30 | "license": "MIT",
31 | "scripts": {
32 | "build": "gatsby build",
33 | "develop": "gatsby develop",
34 | "format": "prettier --write src/**/*.{js,jsx}",
35 | "start": "npm run develop",
36 | "serve": "gatsby serve",
37 | "test": "echo \"Write tests! -> https://gatsby.dev/unit-testing\""
38 | },
39 | "repository": {
40 | "type": "git",
41 | "url": "https://github.com/gatsbyjs/gatsby-starter-default"
42 | },
43 | "bugs": {
44 | "url": "https://github.com/gatsbyjs/gatsby/issues"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/forgot-password/forgot-password.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
Reset Password
16 |
17 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/new-password-dialog/new-password-dialog.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, EventEmitter, Output, Input } from '@angular/core';
2 | import { MaterializeAction } from 'angular2-materialize';
3 |
4 | @Component({
5 | selector: 'app-new-password-dialog',
6 | templateUrl: './new-password-dialog.component.html',
7 | styleUrls: ['./new-password-dialog.component.css']
8 | })
9 | export class NewPasswordDialogComponent implements OnInit {
10 | modalActions = new EventEmitter();
11 | pass: any;
12 | @Output('onResponse') onResponse = new EventEmitter();
13 | @Input('message') message: string;
14 | @Input('inputEnabled') inputEnabled: boolean;
15 | @Input()
16 |
17 | set show(show: boolean) {
18 | if (show) {
19 | this.modalActions.emit({ action: 'modal', params: ['open']});
20 | } else {
21 | this.modalActions.emit({ action: 'modal', params: ['close']});
22 | }
23 | }
24 |
25 | constructor() {
26 | this.pass = { password: '', retype: ''};
27 | }
28 |
29 | ngOnInit() {
30 | }
31 | public setPassword($event) {
32 | this.pass.password = $event.target.value;
33 | }
34 |
35 | public setRetype($event) {
36 | this.pass.retype = $event.target.value;
37 | }
38 |
39 | public cancel() {
40 | this.onResponse.emit('DIALOG_CANCEL');
41 | }
42 |
43 | public confirm() {
44 | if (this.pass.password === this.pass.retype) {
45 | this.onResponse.emit({'message' : 'DIALOG_CONFIRM', 'pass' : this.pass.password, 'retype' : this.pass.retype});
46 | }
47 |
48 | }
49 |
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/login/login.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
Login
14 |
15 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/drone-option-box/drone-option-box.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Output, Input, EventEmitter } from '@angular/core';
2 | import { DecimalPipe } from '@angular/common';
3 | import { MaterializeAction } from 'angular2-materialize';
4 |
5 | @Component({
6 | selector: 'app-drone-option-box',
7 | templateUrl: './drone-option-box.component.html',
8 | styleUrls: ['./drone-option-box.component.css']
9 | })
10 | export class DroneOptionBoxComponent implements OnInit {
11 |
12 | droneState: string;
13 | droneHeading: number;
14 | droneSpeed: number;
15 | droneAlt: number;
16 |
17 | @Output('onSelected') onSelected = new EventEmitter();
18 | @Input('name') droneName: string;
19 | @Input('description') droneDescription: string;
20 |
21 | @Input()
22 | set state(state: string) {
23 | this.droneState = state || 'FINISHED';
24 | }
25 | @Input()
26 | set altitude(value: number) {
27 | this.droneAlt = value || 0;
28 | }
29 | @Input()
30 | set heading(value: number) {
31 | this.droneHeading = value || 0;
32 | }
33 | @Input()
34 | set airspeed(value: number) {
35 | this.droneSpeed = value || 0;
36 | }
37 |
38 | constructor() {
39 | this.droneState = 'FINISHED';
40 | }
41 |
42 | ngOnInit() {
43 | }
44 |
45 | public onWaypoints() {
46 | this.onSelected.emit('SELECT_WAYPOINTS');
47 | }
48 |
49 | public onTakeoff() {
50 | this.onSelected.emit('SELECT_TAKEOFF');
51 | }
52 |
53 | public onResume() {
54 | this.onSelected.emit('SELECT_RESUME');
55 | }
56 |
57 | public onCancel() {
58 | this.onSelected.emit('SELECT_CANCEL');
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/drones-box/drones-box.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input, Output, EventEmitter } from '@angular/core';
2 | import { MaterializeAction } from 'angular2-materialize';
3 |
4 | @Component({
5 | selector: 'app-drones-box',
6 | templateUrl: './drones-box.component.html',
7 | styleUrls: ['./drones-box.component.css']
8 | })
9 | export class DronesBoxComponent {
10 |
11 | modalActions = new EventEmitter();
12 | selectedItems: any;
13 |
14 | @Input()
15 | set show(show: boolean) {
16 | if (show) {
17 | this.modalActions.emit({ action: 'modal', params: ['open']});
18 | } else {
19 | this.modalActions.emit({ action: 'modal', params: ['close']});
20 | }
21 | }
22 |
23 | @Input('drones') drones: any;
24 | @Input('message') message: string;
25 | @Output('onResponse') response = new EventEmitter();
26 |
27 | constructor() {
28 |
29 | this.selectedItems = [];
30 | }
31 |
32 | toggleDrone(droneId) {
33 | // console.log(droneId);
34 | // console.log(this.drones);
35 | if (this.selectedItems.indexOf(droneId) === -1) {
36 | this.selectedItems.push(droneId);
37 | } else {
38 | this.selectedItems = this.selectedItems.filter((id) => id !== droneId);
39 | }
40 | }
41 |
42 | isSelected(droneId) {
43 | return this.selectedItems.indexOf(droneId) > -1;
44 | }
45 |
46 | confirm() {
47 | this.response.emit({ action : 'DRONES_BOX_CONFIRM' , items : this.selectedItems });
48 | this.selectedItems = [];
49 | }
50 |
51 | cancel() {
52 | this.selectedItems = [];
53 | this.response.emit({ actions : 'DRONES_BOX_CANCEL'});
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/auth-http/auth-http.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Router } from '@angular/router';
3 | import { Http, Headers, RequestOptions } from '@angular/http';
4 | import { Observable , Operator } from 'rxjs';
5 | // import 'rxjs/operator/map';
6 | import 'rxjs/operator/share';
7 | import { map, filter, catchError, mergeMap } from 'rxjs/operators';
8 | import { from } from 'rxjs';
9 | @Injectable()
10 | export class AuthHttpService {
11 |
12 | constructor(private http: Http, private router: Router) { }
13 |
14 | private getAuthHeader(): Headers {
15 | const token = localStorage.getItem('token');
16 | const headers = new Headers();
17 | headers.append('Authorization', token);
18 |
19 | return headers;
20 | }
21 |
22 | private checkAuthorization(request: Observable): Observable {
23 | request.pipe(map((res) => {
24 | const json = res.json();
25 | if (json === 'Unauthorized') {
26 | this.router.navigate(['login']);
27 | return request;
28 | } else {
29 | return request;
30 | }
31 | }, (err) => {
32 | console.log(err);
33 | this.router.navigate(['login']);
34 | return request;
35 | }));
36 |
37 | return request;
38 | }
39 |
40 | public get(url: string): Observable {
41 | const authHeader = this.getAuthHeader();
42 | const request = this.http.get(url, { 'headers': authHeader });
43 | return this.checkAuthorization(request);
44 | }
45 |
46 | public post(url: string, data: any): Observable {
47 | const authHeader = this.getAuthHeader();
48 | const request = this.http.post(url, data, { 'headers': authHeader });
49 | return this.checkAuthorization(request);
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/confirm-dialog/confirm-dialog.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
2 | import { MaterializeAction } from 'angular2-materialize';
3 |
4 | @Component({
5 | selector: 'app-confirm-dialog',
6 | templateUrl: './confirm-dialog.component.html',
7 | styleUrls: ['./confirm-dialog.component.css']
8 | })
9 | export class ConfirmDialogComponent implements OnInit {
10 |
11 | modalActions = new EventEmitter();
12 | name: string;
13 | description: string;
14 | flying_time: string;
15 |
16 | @Output('onResponse') onResponse = new EventEmitter();
17 | @Input('message') message: string;
18 | @Input('inputEnabled') inputEnabled: boolean;
19 | @Input('newDrone') newDrone: boolean;
20 | @Input()
21 | set show(show: boolean) {
22 | if (show) {
23 | this.modalActions.emit({ action: 'modal', params: ['open']});
24 | } else {
25 | this.modalActions.emit({ action: 'modal', params: ['close']});
26 | }
27 | }
28 |
29 | constructor() {
30 | this.name = '';
31 | this.description = '';
32 | this.flying_time = '';
33 | }
34 |
35 | ngOnInit() {
36 | }
37 |
38 | public setName($event) {
39 | this.name = $event.target.value;
40 | }
41 | public setDescription($event) {
42 | this.description = $event.target.value;
43 | }
44 | public setFlyingTime($event) {
45 | this.flying_time = $event.target.value;
46 | }
47 | public cancel() {
48 | this.onResponse.emit('DIALOG_CANCEL');
49 | }
50 |
51 | public confirm() {
52 | this.onResponse.emit({'message' : 'DIALOG_CONFIRM', 'name' :
53 | this.name , 'description': this.description, 'flying_time': this.flying_time});
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/user-management/user-management.component.html:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
add Add Groups
24 |
25 |
0" class="collection">
26 | {{ group.groupName }}clear
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/dronesym-python/flask-api/src/mavparser.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 | from dronekit import Vehicle, Command
3 | from pymavlink import mavutil
4 |
5 |
6 | def create_mission(drone, waypoints):
7 | cmds = drone.commands
8 | cmds.wait_ready()
9 | cmds.clear()
10 |
11 | cmds.add(
12 | Command(
13 | 0,
14 | 0,
15 | 0,
16 | mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT,
17 | mavutil.mavlink.MAV_CMD_NAV_WAYPOINT,
18 | 0,
19 | 0,
20 | 0,
21 | 0,
22 | 0,
23 | 0,
24 | waypoints[0]['lat'],
25 | waypoints[0]['lon'],
26 | 10))
27 |
28 | for (i, wp) in enumerate(waypoints):
29 | cmds.add(
30 | Command(
31 | 0,
32 | 0,
33 | 0,
34 | mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT,
35 | mavutil.mavlink.MAV_CMD_NAV_WAYPOINT,
36 | 0,
37 | 0,
38 | 0,
39 | 0,
40 | 0,
41 | 0,
42 | wp['lat'],
43 | wp['lon'],
44 | 10))
45 |
46 | cmds.add(Command(0,
47 | 0,
48 | 0,
49 | mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT,
50 | mavutil.mavlink.MAV_CMD_NAV_WAYPOINT,
51 | 0,
52 | 0,
53 | 0,
54 | 0,
55 | 0,
56 | 0,
57 | waypoints[-1]['lat'],
58 | waypoints[-1]['lon'],
59 | 10))
60 | print('uploading mission...')
61 |
62 | cmds.upload()
63 |
64 | print('mission uploaded')
65 |
66 | return
67 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/user-signup/user-signup.component.html:
--------------------------------------------------------------------------------
1 |
48 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/signup/signup.component.css:
--------------------------------------------------------------------------------
1 | .login-background{
2 | background-color: #f5f5f5;
3 | }
4 |
5 | .login-card{
6 | margin-top: 2%;
7 | margin-bottom: 20%;
8 | }
9 |
10 | .logo-container{
11 | height: 200px;
12 | width: 200px;
13 | margin-left: 26%;
14 | margin-right: auto;
15 | }
16 |
17 | .footer-label{
18 | font-style: italic;
19 | color: #5e5e5e;
20 | }
21 | .card .card-image img {
22 | width:100%;
23 | align-items: center;
24 | }
25 | .card {
26 | min-width: 310px;
27 | }
28 | @media screen and (min-width:1500px) and (max-width: 2561px) {
29 | .card .card-image img {
30 | width: 100%;
31 | margin-left:100px;
32 | }
33 | }
34 | @media screen and (max-width: 1024px) {
35 | .card .card-image img {
36 | padding-right: 30px;
37 | }
38 | }
39 |
40 | @media screen and (max-width: 780px) {
41 | .row .col.offset-s4 {
42 | margin-left: 29%;
43 | }
44 | .card .card-image img {
45 | padding-right: 40px;
46 | }
47 | }
48 |
49 | .footer-label[_ngcontent-c1] {
50 | margin-left:70px;
51 | width:200px;
52 | }
53 | @media screen and (max-width: 560px) {
54 | .row .col.offset-s4 {
55 | margin-left: 18%;
56 | }
57 | }
58 | @media screen and (max-width: 530px) {
59 | .row .col.offset-s4 {
60 | margin-left: 16%;
61 | }
62 | .card .card-image img {
63 | padding-right: 30px;
64 | }
65 | }
66 |
67 | @media screen and (max-width: 470px) {
68 | .row .col.offset-s4 {
69 | margin-left: 10%;
70 | }
71 | .card .card-image img {
72 | padding-right: 35px;
73 | }
74 | }
75 | @media screen and (max-width: 400px) {
76 | .row .col.offset-s4 {
77 | margin-left: 6%;
78 | }
79 | .card .card-image img {
80 | padding-right: 40px;
81 | }
82 | }
83 |
84 | @media screen and (max-width: 330px) {
85 | .row .col.offset-s4 {
86 | margin-left: 4%;
87 | }
88 | .card {
89 | min-width: 270px;
90 | }
91 | .card .card-image img {
92 | padding-right: 50px;
93 | }
94 | }
95 | .btnn{
96 | text-align: center
97 | }
--------------------------------------------------------------------------------
/dronesym-node/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let express = require('express');
4 | let logger = require('morgan');
5 | let mongoose = require('mongoose');
6 | let passport = require('passport');
7 | let bodyParser = require('body-parser');
8 | let cors = require('cors');
9 | let app = express();
10 | let http = require('http');
11 | require('dotenv').config();
12 | // You can change the port to any other, if 3000 is busy or being used by any other service.
13 | let port = 3000;
14 |
15 | http = http.Server(app);
16 | let sockConn = require('./websocket').init(http);
17 |
18 | let droneRouter = require('./Routers/droneRouter');
19 | let userRouter = require('./Routers/userRouter');
20 | let mongoConfig = require('./config/example.mongoconfig');
21 |
22 | app.use(logger('dev'));
23 | app.use(bodyParser.json());
24 | app.use(bodyParser.urlencoded({extended: true}));
25 | app.use(cors());
26 |
27 | // passport configuration
28 | let passportConfig = require('./config/passportconfig')(passport);
29 | app.use(passport.initialize());
30 |
31 | // mongodb connection
32 | mongoose.connect(process.env.MONGO_URL, {useNewUrlParser: true});
33 |
34 | mongoose.connection.on('error', function(err) {
35 | console.log(err);
36 | });
37 |
38 | mongoose.connection.on('open', function() {
39 | console.log('Userbase connected...');
40 | });
41 |
42 |
43 | app.use('/dronesym/api/node', droneRouter);
44 | app.use('/dronesym/api/node/user', userRouter);
45 |
46 | // catch 404 and forward to error handler
47 | app.use(function(req, res, next) {
48 | let err = new Error('Not Found');
49 | err.status = 404;
50 | next(err);
51 | });
52 |
53 | app.use(function(err, req, res, next) {
54 | res.status(err.status || 500);
55 | res.json({
56 | message: err.message,
57 | error: err,
58 | });
59 | });
60 |
61 |
62 | http.listen(port, function() {
63 | // eslint-disable-next-line no-console
64 | console.log('Listening on ' + port + '..');
65 | });
66 |
67 | module.exports = app;
68 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/drone-groups/drone-groups.component.html:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
add Add Drones
25 |
26 |
0" class="collection">
27 | {{ getName(drone) }}clear
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/dronesym-node/tests/drone_tests.js:
--------------------------------------------------------------------------------
1 |
2 | const assert = require('assert');
3 | const randomstring = require('randomstring');
4 | const {createUser} = require('../Controllers/userCtrl');
5 | const user = ['', '', ''];// User Array
6 | // eslint-disable-next-line require-jsdoc
7 | function newUser(leng = 10, user) {
8 | // takes variable argument length, default is 10
9 | user[0] = randomstring.generate(leng);// Username
10 | user[1] = randomstring.generate(leng);// Password
11 | user[2] = 'User';// User
12 | user[3] = 'test@test.com'; // Email
13 | }
14 |
15 | describe('USER CONTROLLER', () => {
16 | describe('New User', () => {
17 | describe('Successfully Created', () => {
18 | before(() => {
19 | newUser(10, user);
20 | });
21 | it('No Missing Requirements. Success.', (done) => {
22 | createUser(user[0], user[3], user[1], user[2], function(response) {
23 | assert.strictEqual(response.status, 'OK');
24 | done();
25 | });
26 | });
27 | });
28 | describe('Throws Errors', () => {
29 | newUser(10, user);
30 | it('Username and Password Missing', (done) => {
31 | createUser('', user[3], '', user[2], function(response) {
32 | assert.strictEqual(response.status, 'ERROR');
33 | done();
34 | });
35 | });
36 | it('Username Missing', (done) => {
37 | createUser('', user[3], user[1], user[2], function(response) {
38 | assert.strictEqual(response.status, 'ERROR');
39 | done();
40 | });
41 | });
42 | it('Password Missing', (done) => {
43 | createUser(user[0], user[3], '', user[2], function(response) {
44 | assert.strictEqual(response.status, 'ERROR');
45 | done();
46 | });
47 | it('Email Missing', (done) => {
48 | createUser(user[0], '', user[1], user[2], function(response) {
49 | assert.strictEqual(response.status, 'ERROR');
50 | done();
51 | });
52 | });
53 | });
54 | });
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/login/login.component.css:
--------------------------------------------------------------------------------
1 | .login-background{
2 | background-color: #f5f5f5;
3 | }
4 |
5 | .login-card{
6 | margin-top: 2%;
7 | margin-bottom: 20%;
8 | }
9 |
10 | .logo-container{
11 | height: 200px;
12 | width: 200px;
13 | margin-left: 26%;
14 | margin-right: auto;
15 | }
16 |
17 | .footer-label{
18 | font-style: italic;
19 | color: #5e5e5e;
20 | }
21 | .card .card-image img {
22 | width:100%;
23 | align-items: center;
24 | }
25 | .card {
26 | min-width: 310px;
27 | }
28 | @media screen and (min-width:1500px) and (max-width: 2561px) {
29 | .card .card-image img {
30 | width: 100%;
31 | margin-left:100px;
32 | }
33 | }
34 | @media screen and (max-width: 1024px) {
35 | .card .card-image img {
36 | padding-right: 30px;
37 | }
38 | }
39 |
40 | @media screen and (max-width: 780px) {
41 | .row .col.offset-s4 {
42 | margin-left: 29%;
43 | }
44 | .card .card-image img {
45 | padding-right: 40px;
46 | }
47 | }
48 |
49 | .footer-label[_ngcontent-c1] {
50 | margin-left:70px;
51 | width:200px;
52 | }
53 |
54 | #forgetLink {
55 | margin-top: 25px;
56 | }
57 |
58 | @media screen and (max-width: 560px) {
59 | .row .col.offset-s4 {
60 | margin-left: 18%;
61 | }
62 | }
63 | @media screen and (max-width: 530px) {
64 | .row .col.offset-s4 {
65 | margin-left: 16%;
66 | }
67 | .card .card-image img {
68 | padding-right: 30px;
69 | }
70 | }
71 |
72 | @media screen and (max-width: 470px) {
73 | .row .col.offset-s4 {
74 | margin-left: 10%;
75 | }
76 | .card .card-image img {
77 | padding-right: 35px;
78 | }
79 | }
80 | @media screen and (max-width: 400px) {
81 | .row .col.offset-s4 {
82 | margin-left: 6%;
83 | }
84 | .card .card-image img {
85 | padding-right: 40px;
86 | }
87 | }
88 |
89 | @media screen and (max-width: 330px) {
90 | .row .col.offset-s4 {
91 | margin-left: 4%;
92 | }
93 | .card {
94 | min-width: 270px;
95 | }
96 | .card .card-image img {
97 | padding-right: 50px;
98 | }
99 | }
100 | .btnn{
101 | text-align: center
102 | }
--------------------------------------------------------------------------------
/dronesym-node/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "drone-sym",
3 | "version": "1.0.0",
4 | "description": "DroneSym NodeJS API",
5 | "keywords": [
6 | "npm",
7 | "nodejs",
8 | "mongodb",
9 | "firebase",
10 | "drone",
11 | "python",
12 | "angularjs",
13 | "googlemaps"
14 | ],
15 | "main": "index.js",
16 | "scripts": {
17 | "test": "better-npm-run test",
18 | "start": "nodemon index.js"
19 | },
20 | "betterScripts": {
21 | "test": {
22 | "command": "mocha ./tests",
23 | "env": {
24 | "NODE_ENV": "test"
25 | }
26 | }
27 | },
28 | "repository": {
29 | "type": "git",
30 | "url": "git+https://github.com/hasa93/DroneSym.git"
31 | },
32 | "author": "SCoRe Community",
33 | "contributors": [
34 | "dilinade",
35 | "hasa93",
36 | "rhperera",
37 | "malithsen",
38 | "charithccmc",
39 | "iammosespaulr"
40 | ],
41 | "license": "ISC",
42 | "bugs": {
43 | "url": "https://github.com/hasa93/DroneSym/issues"
44 | },
45 | "homepage": "https://github.com/hasa93/DroneSym#readme",
46 | "engines": {},
47 | "dependencies": {
48 | "bcrypt": "^3.0.6",
49 | "body-parser": "^1.18.3",
50 | "cookie-parser": "^1.4.4",
51 | "cors": "^2.8.5",
52 | "dotenv": "^8.1.0",
53 | "eslint": "^5.16.0",
54 | "eslint-config-google": "^0.13.0",
55 | "eslint-utils": "^1.4.2",
56 | "express": "^4.16.4",
57 | "http": "0.0.0",
58 | "is-ci": "^2.0.0",
59 | "jsonwebtoken": "^8.5.0",
60 | "mixin-deep": "^2.0.1",
61 | "mongoose": "^5.5.12",
62 | "morgan": "^1.9.1",
63 | "nodemailer": "^6.4.11",
64 | "nodemon": "^1.18.10",
65 | "passport": "^0.4.0",
66 | "passport-jwt": "^4.0.0",
67 | "request": "^2.88.0",
68 | "socket.io": "^2.2.0",
69 | "sinon": "^7.1.1",
70 | "sinon-mongoose": "^2.2.1",
71 | "socketio-jwt": "^4.5.0"
72 | },
73 | "devDependencies": {
74 | "better-npm-run": "^0.1.1",
75 | "chai": "^4.2.0",
76 | "mocha": "^6.0.2",
77 | "random-location": "^1.0.12",
78 | "randomstring": "^1.1.5"
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/drone-option-box/drone-option-box.component.html:
--------------------------------------------------------------------------------
1 |
2 | {{ droneName }} - {{ droneDescription}}
3 | terrain Altitude - {{ droneAlt | number:'1.2' }}
4 | navigation Heading - {{ droneHeading | number: '1.0-2' }}
5 | play_arrow Airspeed - {{ droneSpeed | number: '1.2'}}
6 |
7 |
8 |
9 |
14 |
15 |
19 |
20 |
24 |
25 |
29 |
30 |
--------------------------------------------------------------------------------
/docs/src/components/seo.js:
--------------------------------------------------------------------------------
1 | /**
2 | * SEO component that queries for data with
3 | * Gatsby's useStaticQuery React hook
4 | *
5 | * See: https://www.gatsbyjs.org/docs/use-static-query/
6 | */
7 |
8 | import React from "react"
9 | import PropTypes from "prop-types"
10 | import Helmet from "react-helmet"
11 | import { useStaticQuery, graphql } from "gatsby"
12 |
13 | function SEO({ description, lang, meta, title }) {
14 | const { site } = useStaticQuery(
15 | graphql`
16 | query {
17 | site {
18 | siteMetadata {
19 | title
20 | description
21 | author
22 | }
23 | }
24 | }
25 | `
26 | )
27 |
28 | const metaDescription = description || site.siteMetadata.description
29 |
30 | return (
31 |
72 | )
73 | }
74 |
75 | SEO.defaultProps = {
76 | lang: `en`,
77 | meta: [],
78 | description: ``,
79 | }
80 |
81 | SEO.propTypes = {
82 | description: PropTypes.string,
83 | lang: PropTypes.string,
84 | meta: PropTypes.arrayOf(PropTypes.object),
85 | title: PropTypes.string.isRequired,
86 | }
87 |
88 | export default SEO
89 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/drone-list/drone-list.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { DroneDataService } from '../drone-service/drone-data.service';
3 |
4 | declare var Materialize;
5 |
6 | @Component({
7 | selector: 'app-drone-list',
8 | templateUrl: './drone-list.component.html',
9 | styleUrls: ['./drone-list.component.css']
10 | })
11 |
12 |
13 | export class DroneListComponent {
14 |
15 | drones: any;
16 | showRenameConfirmation: boolean;
17 | showDeleteConfirmation: boolean;
18 |
19 | currDrone: any;
20 |
21 | constructor(private droneFeed: DroneDataService) {
22 | this.showDeleteConfirmation = false;
23 | this.showRenameConfirmation = false;
24 |
25 | this.drones = [];
26 |
27 | this.droneFeed.getDroneFeed()
28 | .subscribe((drones) => {
29 | // console.log(drones);
30 | if (this.drones.length !== drones.length) {
31 | this.drones = drones;
32 | }
33 | });
34 | }
35 |
36 | showDeleteConfirmationDialog(drone) {
37 | this.currDrone = drone;
38 | // console.log(drone._id);
39 | this.showDeleteConfirmation = true;
40 | }
41 |
42 | showRenameConfirmationDialog(drone) {
43 | this.currDrone = drone;
44 | this.showRenameConfirmation = true;
45 | }
46 |
47 | deleteResponse($event) {
48 | if ($event.message === 'DIALOG_CONFIRM') {
49 | this.droneFeed.removeDrone(this.currDrone._id, this.currDrone.status)
50 | .then((res) => {
51 | console.log(res);
52 |
53 | if (res.status === 'ERROR') {
54 | Materialize.toast(`Can't Delete : ${res.msg}`, 4000);
55 | return;
56 | } else if (res.status === 'OK') {
57 | Materialize.toast('Drone removed from fleet', 4000);
58 | }
59 | })
60 | .catch((err) => {
61 | console.log(err);
62 | });
63 | }
64 |
65 |
66 | this.showDeleteConfirmation = false;
67 | }
68 |
69 | renameResponse($event) {
70 | if ($event.message === 'DIALOG_CONFIRM') {
71 | this.droneFeed.updateName(this.currDrone._id, $event.name)
72 | .then((res) => {
73 | console.log(res);
74 | this.currDrone.name = res.update.name;
75 | });
76 | }
77 |
78 | this.showRenameConfirmation = false;
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/signup/signup.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
Signup
14 |
15 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/forgot-password/forgot-password.component.css:
--------------------------------------------------------------------------------
1 | .login-background{
2 | background-color: #f5f5f5;
3 | }
4 |
5 | .login-card{
6 | margin-top: 2%;
7 | margin-bottom: 20%;
8 | }
9 |
10 | .logo-container{
11 | height: 200px;
12 | width: 200px;
13 | margin-left: 26%;
14 | margin-right: auto;
15 | }
16 |
17 | .footer-label{
18 | font-style: italic;
19 | color: #5e5e5e;
20 | }
21 | .card .card-image img {
22 | width:100%;
23 | align-items: center;
24 | }
25 | .card {
26 | min-width: 310px;
27 | }
28 | @media screen and (min-width:1500px) and (max-width: 2561px) {
29 | .card .card-image img {
30 | width: 100%;
31 | margin-left:100px;
32 | }
33 | }
34 | @media screen and (max-width: 1024px) {
35 | .card .card-image img {
36 | padding-right: 30px;
37 | }
38 | }
39 |
40 | @media screen and (max-width: 780px) {
41 | .row .col.offset-s4 {
42 | margin-left: 29%;
43 | }
44 | .card .card-image img {
45 | padding-right: 40px;
46 | }
47 | }
48 | @media screen and (max-width: 660px) {
49 | .row .col.offset-s4 {
50 | margin-left: 25%;
51 | }
52 | .row .btn {
53 | float:left;
54 | margin-right:34%
55 | }
56 | }
57 | @media screen and (max-width: 630px) {
58 | .row .col.offset-s4 {
59 | margin-left: 24%;
60 | }
61 | .row .btn {
62 | float:right;
63 | margin-right:34%
64 | }
65 | }
66 | @media screen and (max-width: 630px) {
67 | .row .col.offset-s4 {
68 | margin-left: 22%;
69 | }
70 | .row .btn {
71 | float:right;
72 | margin-right:33%
73 | }
74 | .footer-label[_ngcontent-c1] {
75 | margin-left:70px;
76 | width:200px;
77 | }
78 | }
79 | @media screen and (max-width: 560px) {
80 | .row .col.offset-s4 {
81 | margin-left: 18%;
82 | }
83 | }
84 | @media screen and (max-width: 530px) {
85 | .row .col.offset-s4 {
86 | margin-left: 16%;
87 | }
88 | .card .card-image img {
89 | padding-right: 30px;
90 | }
91 | }
92 |
93 | @media screen and (max-width: 470px) {
94 | .row .col.offset-s4 {
95 | margin-left: 10%;
96 | }
97 | .card .card-image img {
98 | padding-right: 35px;
99 | }
100 | }
101 | @media screen and (max-width: 400px) {
102 | .row .col.offset-s4 {
103 | margin-left: 6%;
104 | }
105 | .card .card-image img {
106 | padding-right: 40px;
107 | }
108 | }
109 |
110 | @media screen and (max-width: 330px) {
111 | .row .col.offset-s4 {
112 | margin-left: 4%;
113 | }
114 | .card {
115 | min-width: 270px;
116 | }
117 | .card .card-image img {
118 | padding-right: 50px;
119 | }
120 | }
121 | .btnn{
122 | text-align: center
123 | }
124 |
--------------------------------------------------------------------------------
/dronesym-frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dronesym-frontend",
3 | "version": "0.0.0",
4 | "license": "MIT",
5 | "scripts": {
6 | "ng": "ng",
7 | "start": "ng serve",
8 | "build": "ng build",
9 | "test": "ng test",
10 | "lint": "ng lint",
11 | "e2e": "ng e2e"
12 | },
13 | "description": "Application For Controlling Drone Systems Remotely",
14 | "keywords": [
15 | "npm",
16 | "nodejs",
17 | "mongodb",
18 | "firebase",
19 | "drone",
20 | "python",
21 | "angularjs",
22 | "googlemaps"
23 | ],
24 | "bugs": "https://github.com/scorelab/DroneSym/issues",
25 | "author": "SCoRe Community",
26 | "contributors": [
27 | "dilinade",
28 | "hasa93",
29 | "rhperera",
30 | "malithsen",
31 | "charithccmc",
32 | "iammosespaulr"
33 | ],
34 | "repository": "https://github.com/scorelab/DroneSym",
35 | "homepage": "http://www.scorelab.org/",
36 | "engines": {},
37 | "private": true,
38 | "dependencies": {
39 | "@agm/core": "^1.0.0-beta.5",
40 | "@angular/animations": "^7.2.8",
41 | "@angular/common": "^7.2.8",
42 | "@angular/compiler": "^7.2.8",
43 | "@angular/core": "^7.2.8",
44 | "@angular/forms": "^7.2.8",
45 | "@angular/http": "^7.2.8",
46 | "@angular/platform-browser": "^7.2.8",
47 | "@angular/platform-browser-dynamic": "^7.2.8",
48 | "@angular/pwa": "^0.12.4",
49 | "@angular/router": "^7.2.8",
50 | "@angular/service-worker": "^7.2.8",
51 | "angular2-materialize": "^15.1.10",
52 | "core-js": "^2.6.5",
53 | "hammerjs": "^2.0.8",
54 | "jquery": "^3.3.1",
55 | "materialize-css": "^0.100.2",
56 | "mixin-deep": "^2.0.1",
57 | "rxjs": "^6.4.0",
58 | "rxjs-compat": "^6.4.0",
59 | "socket.io-client": "^2.2.0",
60 | "tslib": "^1.9.3",
61 | "tslint": "^5.16.0",
62 | "zone.js": "^0.8.29"
63 | },
64 | "devDependencies": {
65 | "@angular-devkit/build-angular": "^0.13.5",
66 | "@angular/cli": "^7.3.5",
67 | "@angular/compiler-cli": "^7.2.8",
68 | "@angular/language-service": "^7.2.8",
69 | "@types/jasmine": "^3.3.9",
70 | "@types/node": "^11.10.5",
71 | "codelyzer": "^4.5.0",
72 | "jasmine-core": "^3.3.0",
73 | "jasmine-spec-reporter": "^4.2.1",
74 | "karma": "^4.0.1",
75 | "karma-chrome-launcher": "^2.2.0",
76 | "karma-cli": "^2.0.0",
77 | "karma-coverage-istanbul-reporter": "^2.0.5",
78 | "karma-jasmine": "^2.0.1",
79 | "karma-jasmine-html-reporter": "^1.4.0",
80 | "protractor": "^5.4.2",
81 | "ts-node": "^8.0.3",
82 | "typescript": "3.1.6"
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/dronesym-node/tests/user_models.js:
--------------------------------------------------------------------------------
1 | var sinon = require('sinon');
2 | var chai = require('chai');
3 | var expect = chai.expect;
4 |
5 | var mongoose = require('mongoose');
6 | require('sinon-mongoose');
7 | var Todo = require('../Models/user');
8 |
9 | /*-----GET ALL GROUPS-------------*/
10 | describe("Get all User", function(){
11 | it("should return all user field", function(done){
12 | var TodoMock = sinon.mock(Todo);
13 | var expectedResult = {status: true, todo: []};
14 | TodoMock.expects('find').yields(null, expectedResult);
15 | Todo.find(function (err, result) {
16 | TodoMock.verify();
17 | TodoMock.restore();
18 | expect(result.status).to.be.true;
19 | done();
20 | });
21 | });
22 |
23 | it("should return error", function(done){
24 | var TodoMock = sinon.mock(Todo);
25 | var expectedResult = {status: false, error: "Something went wrong"};
26 | TodoMock.expects('find').yields(expectedResult, null);
27 | Todo.find(function (err, result) {
28 | TodoMock.verify();
29 | TodoMock.restore();
30 | expect(err.status).to.not.be.true;
31 | done();
32 | });
33 | });
34 | });
35 |
36 | /*----POSTING NEW GROUP------------*/
37 | describe("Post a new user of drones", function(){
38 | it("should create new user", function(done){
39 | var TodoMock = sinon.mock(new Todo({ todo: 'Save new user from mock'}));
40 | var todo = TodoMock.object;
41 | var expectedResult = { status: true };
42 | TodoMock.expects('save').yields(null, expectedResult);
43 | todo.save(function (err, result) {
44 | TodoMock.verify();
45 | TodoMock.restore();
46 | expect(result.status).to.be.true;
47 | done();
48 | });
49 | });
50 | // Test will pass if the todo is not saved
51 | it("should return error, if user not saved", function(done){
52 | var TodoMock = sinon.mock(new Todo({ todo: 'Save new user from mock'}));
53 | var todo = TodoMock.object;
54 | var expectedResult = { status: false };
55 | TodoMock.expects('save').yields(expectedResult, null);
56 | todo.save(function (err, result) {
57 | TodoMock.verify();
58 | TodoMock.restore();
59 | expect(err.status).to.not.be.true;
60 | done();
61 | });
62 | });
63 | });
64 |
--------------------------------------------------------------------------------
/dronesym-node/tests/group_models.js:
--------------------------------------------------------------------------------
1 | var sinon = require('sinon');
2 | var chai = require('chai');
3 | var expect = chai.expect;
4 |
5 | var mongoose = require('mongoose');
6 | require('sinon-mongoose');
7 | var Todo = require('../Models/group');
8 |
9 | /*-----GET ALL GROUPS-------------*/
10 | describe("Get all Groups", function(){
11 | it("should return all groups field", function(done){
12 | var TodoMock = sinon.mock(Todo);
13 | var expectedResult = {status: true, todo: []};
14 | TodoMock.expects('find').yields(null, expectedResult);
15 | Todo.find(function (err, result) {
16 | TodoMock.verify();
17 | TodoMock.restore();
18 | expect(result.status).to.be.true;
19 | done();
20 | });
21 | });
22 |
23 | it("should return error", function(done){
24 | var TodoMock = sinon.mock(Todo);
25 | var expectedResult = {status: false, error: "Something went wrong"};
26 | TodoMock.expects('find').yields(expectedResult, null);
27 | Todo.find(function (err, result) {
28 | TodoMock.verify();
29 | TodoMock.restore();
30 | expect(err.status).to.not.be.true;
31 | done();
32 | });
33 | });
34 | });
35 |
36 | /*----POSTING NEW GROUP------------*/
37 | describe("Post a new group of drones", function(){
38 | it("should create new post", function(done){
39 | var TodoMock = sinon.mock(new Todo({ todo: 'Save new group from mock'}));
40 | var todo = TodoMock.object;
41 | var expectedResult = { status: true };
42 | TodoMock.expects('save').yields(null, expectedResult);
43 | todo.save(function (err, result) {
44 | TodoMock.verify();
45 | TodoMock.restore();
46 | expect(result.status).to.be.true;
47 | done();
48 | });
49 | });
50 | // Test will pass if the todo is not saved
51 | it("should return error, if post not saved", function(done){
52 | var TodoMock = sinon.mock(new Todo({ todo: 'Save new group from mock'}));
53 | var todo = TodoMock.object;
54 | var expectedResult = { status: false };
55 | TodoMock.expects('save').yields(expectedResult, null);
56 | todo.save(function (err, result) {
57 | TodoMock.verify();
58 | TodoMock.restore();
59 | expect(err.status).to.not.be.true;
60 | done();
61 | });
62 | });
63 | });
64 |
--------------------------------------------------------------------------------
/dronesym-python/flask-api/tests/test_dronepool.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import mocks
3 | from dronekit import VehicleMode
4 | import unittest
5 | from inspect import getsourcefile
6 | import os.path
7 | import sys
8 |
9 | current_path = os.path.abspath(getsourcefile(lambda:0))
10 | current_dir = os.path.dirname(current_path)
11 | parent_dir = current_dir[:current_dir.rfind(os.path.sep)]
12 | source_dir = os.path.join(parent_dir, 'src')
13 |
14 | sys.path.insert(0, source_dir)
15 |
16 | import dronepool
17 |
18 | class TestDronePool(unittest.TestCase):
19 | def setUp(self):
20 | dronepool.env_test = True
21 | dronepool.instance_count = 0
22 |
23 | def tearDown(self):
24 | dronepool.instance_count = 0
25 | dronepool.drone_pool = {}
26 |
27 | def test_create_new_drone(self):
28 | res = dronepool.create_new_drone( { "db_key" : "test" })
29 | self.assertEqual(res['status'], "OK")
30 | self.assertEqual(res['id'], "test")
31 | self.assertEqual("test" in dronepool.drone_pool, True)
32 |
33 | def test_remove_drone(self):
34 | vehicle = mocks.MockVehicle()
35 | dronepool.drone_pool["test"] = vehicle
36 |
37 | res = dronepool.remove_drone({ "drone_id" : "test" })
38 | self.assertEqual(res['status'], "OK")
39 | self.assertEqual(res['id'], "test")
40 | self.assertEqual("test" in dronepool.drone_pool, False)
41 |
42 | def test_remove_in_flight_drone(self):
43 | vehicle = mocks.MockVehicle()
44 | vehicle.mode = VehicleMode('AUTO')
45 |
46 | dronepool.drone_pool["test"] = vehicle
47 | res = dronepool.remove_drone({ "drone_id" : "test" })
48 | self.assertEqual(res['status'], "ERROR")
49 | self.assertEqual("test" in dronepool.drone_pool, True)
50 |
51 | def test_remove_drone_with_invalid_key(self):
52 | res = dronepool.remove_drone({ "drone_id" : "test" })
53 | self.assertEqual(res['status'], "ERROR")
54 |
55 | def test_land_drone_armed(self):
56 | vehicle = mocks.MockVehicle()
57 | vehicle.armed = True
58 | dronepool.drone_pool = { "test" : vehicle }
59 | res = dronepool.land_drone({ "drone_id" : "test" })
60 | self.assertEqual(res, True)
61 | self.assertEqual(vehicle.mode, VehicleMode('LAND'))
62 |
63 | def test_land_drone_unarmed(self):
64 | vehicle = mocks.MockVehicle()
65 | vehicle.armed = False
66 | dronepool.drone_pool = { "test" : vehicle}
67 | res = dronepool.land_drone({ "drone_id" : "test" })
68 | self.assertEqual(res, False)
69 |
70 | def test_run_mission(self):
71 | vehicle = mocks.MockVehicle()
72 | mavparser = mocks.MockMavParser()
73 | vehicle.setAltitude(10)
74 | dronepool.mavparser = mavparser
75 | dronepool.run_mission(vehicle, 10, waypoints=[])
76 | self.assertEqual(vehicle.mode, VehicleMode('AUTO'))
77 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/signup/signup.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Output, Input, EventEmitter } from '@angular/core';
2 | import { Router } from '@angular/router';
3 | import { UserService } from '../user-service/user.service';
4 | import { MaterializeAction } from 'angular2-materialize';
5 |
6 | declare var Materialize;
7 |
8 | @Component({
9 | selector: 'app-signup',
10 | templateUrl: './signup.component.html',
11 | styleUrls: ['./signup.component.css']
12 | })
13 | export class SignupComponent implements OnInit {
14 |
15 | private user: any;
16 | /** Regular expression for email validation */
17 | regexp = new RegExp(/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/);
18 |
19 | constructor(private userService: UserService, private router: Router) {
20 | this.user = { uname: '', email: '', password: '', retype: '', role: '' };
21 | }
22 |
23 | ngOnInit() {
24 | }
25 | public setUsername($event) {
26 | this.user.uname = $event.target.value;
27 | }
28 | public setEmail($event) {
29 | this.user.email = $event.target.value;
30 | }
31 |
32 | public setPassword($event) {
33 | this.user.password = $event.target.value;
34 | }
35 |
36 | public setRetype($event) {
37 | this.user.retype = $event.target.value;
38 | }
39 | public onRoleSelect($event) {
40 | const role = $event.target.value.toLowerCase();
41 | this.user.role = role;
42 | }
43 | public onSignup($event) {
44 | $event.preventDefault();
45 |
46 | if (this.user.uname === '' || this.user.password === '' || this.user.retype === '') {
47 | Materialize.toast('All fields must be specified', 3000);
48 | return;
49 | }
50 |
51 | if (!this.regexp.test(this.user.email)) {
52 | Materialize.toast('Please enter a valid Email Address', 3000);
53 | return;
54 | }
55 |
56 | if (this.user.role === '') {
57 | Materialize.toast('Please specify a user role', 3000);
58 | return;
59 | }
60 |
61 | if (this.user.password !== this.user.retype) {
62 | Materialize.toast('Retype password does not match', 3000);
63 | return;
64 | }
65 |
66 | this.userService.createUserFromSignup(this.user.uname, this.user.password, this.user.role, this.user.email)
67 | .then((status) => {
68 | if (status.status === 'OK') {
69 | Materialize.toast('User created successfully', 4000);
70 |
71 | } else if (status.status === 'ERROR') {
72 | Materialize.toast(status.msg, 4000);
73 | }
74 | }, (err) => {
75 | Materialize.toast('Oops something went wrong...', 4000);
76 | console.log(err);
77 | });
78 | // console.log(this.user);
79 | }
80 |
81 | }
82 |
83 |
--------------------------------------------------------------------------------
/dronesym-node/Routers/userRouter.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const router = new express.Router();
3 | const userCtrl = require('../Controllers/userCtrl');
4 | const passport = require('passport');
5 |
6 | const authorize = userCtrl.authorizeUser;
7 | const authenticate = passport.authenticate('jwt', {session: false});
8 |
9 | router.post('/login', function(req, res) {
10 | userCtrl.loginUser(req.body.uname, req.body.password, function(status) {
11 | res.json(status);
12 | });
13 | });
14 | router.post('/sendEmail', function(req, res) {
15 | userCtrl.sendEmail(req.body.code, function(status) {
16 | res.json(status);
17 | });
18 | });
19 | router.post('/updatePass', function(req, res) {
20 | userCtrl.updatePass(req.body.uname, req.body.password, function(status) {
21 | res.json(status);
22 | });
23 | });
24 | router.post('/check', function(req, res) {
25 | userCtrl.check(req.body.uname, function(status) {
26 | res.json(status);
27 | });
28 | });
29 | router.post('/create', authenticate, authorize(['admin']), function(req, res) {
30 | userCtrl.createUser(req.body.uname, req.body.email,
31 | req.body.password, req.body.role,
32 | function(status) {
33 | res.json(status);
34 | });
35 | });
36 | router.post('/createuser', function(req, res) {
37 | userCtrl.createUserFromSignup(req.body.uname, req.body.password,
38 | req.body.role, req.body.email, function(status) {
39 | res.json(status);
40 | });
41 | });
42 |
43 | router.get('/role', authenticate, function(req, res) {
44 | res.json({status: 'OK', role: req.user.role});
45 | });
46 |
47 | router.get('/authenticate', authenticate, function(req, res) {
48 | res.json('Authorized');
49 | });
50 |
51 | router.post('/:groupId/add', authenticate, authorize(['admin']),
52 | function(req, res) {
53 | userCtrl.updateUserGroups(req.body.userId, req.params.groupId,
54 | insert=true, function(status) {
55 | res.json(status);
56 | });
57 | });
58 |
59 | router.post('/:groupId/updategroup', authenticate, authorize(['admin']),
60 | function(req, res) {
61 | userCtrl.updateUserInGroup(req.body.userId, req.params.groupId,
62 | insert=true,
63 | function(status) {
64 | res.json(status);
65 | });
66 | });
67 |
68 | router.post('/:groupId/remove', authenticate, authorize(['admin']),
69 | function(req, res) {
70 | userCtrl.updateUserGroups(req.body.userId, req.params.groupId,
71 | insert=false, function(status) {
72 | res.json(status);
73 | });
74 | });
75 |
76 | router.get('/list', authenticate, authorize(['admin']), function(req, res) {
77 | userCtrl.getUserList(req.user.id, function(status) {
78 | res.json(status);
79 | });
80 | });
81 |
82 | module.exports = router;
83 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/
22 | // import 'core-js/es6/symbol';
23 | // import 'core-js/es6/object';
24 | // import 'core-js/es6/function';
25 | // import 'core-js/es6/parse-int';
26 | // import 'core-js/es6/parse-float';
27 | // import 'core-js/es6/number';
28 | // import 'core-js/es6/math';
29 | // import 'core-js/es6/string';
30 | // import 'core-js/es6/date';
31 | // import 'core-js/es6/array';
32 | // import 'core-js/es6/regexp';
33 | // import 'core-js/es6/map';
34 | // import 'core-js/es6/weak-map';
35 | // import 'core-js/es6/set';
36 |
37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
38 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
39 |
40 | /** IE10 and IE11 requires the following to support `@angular/animation`. */
41 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
42 |
43 |
44 | /** Evergreen browsers require these. **/
45 | import 'core-js/es6/reflect';
46 |
47 |
48 | /** ALL Firefox browsers require the following to support `@angular/animation`. **/
49 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
50 |
51 |
52 |
53 | /***************************************************************************************************
54 | * Zone JS is required by Angular itself.
55 | */
56 | import 'zone.js/dist/zone'; // Included with Angular CLI.
57 |
58 |
59 |
60 | /***************************************************************************************************
61 | * APPLICATION IMPORTS
62 | */
63 |
64 | /**
65 | * Date, currency, decimal and percent pipes.
66 | * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10
67 | */
68 | // import 'intl'; // Run `npm install --save intl`.
69 | /**
70 | * Need to import at least one locale-data with intl.
71 | */
72 | // import 'intl/locale-data/jsonp/en';
73 |
--------------------------------------------------------------------------------
/dronesym-frontend/e2e/test.ts:
--------------------------------------------------------------------------------
1 | describe('End to End test for Login feature and check for possible vulnerabilities in DroneSym', function() {
2 | browser.driver.get('http:localhost:4200/dashboard');
3 | console.log('\x1b[32m', 'E2E for Login & URL redirection vulnerability Started!');
4 | it('Checking Login vulnerabilities', function() {
5 | browser.sleep(2000);
6 | expect(browser.driver.getCurrentUrl()).toMatch('http://localhost:4200/login');
7 | console.log('\x1b[36m', '[Status: PASS!] No URL redirection vulnerability found');
8 | });
9 | it('Confirming page rendering', function() {
10 | browser.driver.get('http:localhost:4200');
11 | // Checking the current url
12 | const path = browser.driver.getCurrentUrl();
13 | expect(path).toMatch('http://localhost:4200/');
14 | console.log('\x1b[32m', '[Status: PASS!] Server Accessibility is present.');
15 | });
16 | it('Confirming Sign In through Admin\'s account [submition through clicking the button]', function() {
17 |
18 | // Find page elements
19 | const NameField = browser.driver.findElement(By.id('username'));
20 | const PassField = browser.driver.findElement(By.id('password'));
21 | const LoginBtn = browser.driver.findElement(By.css('.btn'));
22 |
23 | // Fill input fields
24 | NameField.sendKeys('admin');
25 | PassField.sendKeys('admin');
26 |
27 | // Ensure fields contain what we've entered
28 | expect(NameField.getAttribute('value')).toEqual('admin');
29 | expect(PassField.getAttribute('value')).toEqual('admin');
30 |
31 | // Click to sign in - waiting for Angular as it is manually bootstrapped.
32 | LoginBtn.click().then(function() {
33 | browser.waitForAngular();
34 | expect(browser.driver.getCurrentUrl()).toMatch('http://localhost:4200/dashboard/map');
35 | });
36 | });
37 | console.log('\x1b[32m', '[Status: PASS!] Login activity using correct admin credentials is redirected to Maps page');
38 | it('Confirming Login through user\'s account [submition through Enter button]', function() {
39 | browser.driver.get('http:localhost:4200/login');
40 | // Checking the current url
41 | const path = browser.driver.getCurrentUrl();
42 | expect(path).toMatch('http://localhost:4200/');
43 | console.log('\x1b[36m', '[Status: PASS!] Login system through User\'s account is working properly');
44 | const NameField = browser.driver.findElement(By.id('username'));
45 | const PassField = browser.driver.findElement(By.id('password'));
46 | const LoginBtn = browser.driver.findElement(By.css('.btn'));
47 | NameField.sendKeys('icarus');
48 | PassField.sendKeys('icarus');
49 | expect(NameField.getAttribute('value')).toEqual('icarus');
50 | expect(PassField.getAttribute('value')).toEqual('icarus');
51 | browser.actions().sendKeys(protractor.Key.ENTER).perform();
52 | });
53 | });
54 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/app-router.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { RouterModule, Routes, RouterStateSnapshot } from '@angular/router';
3 | import { RouteGuardService } from './route-guard/route-guard.service';
4 | import { AdminAuthorizeService } from './admin-authorize/admin-authorize.service';
5 |
6 | import { AppComponent } from './app.component';
7 | import { LoginComponent } from './login/login.component';
8 | import { DashboardComponent } from './dashboard/dashboard.component';
9 | import { UserSignupComponent } from './user-signup/user-signup.component';
10 | import { UserViewComponent } from './user-view/user-view.component';
11 | import { UserDashboardComponent } from './user-dashboard/user-dashboard.component';
12 | import { DroneGroupsComponent } from './drone-groups/drone-groups.component';
13 | import { DroneListComponent } from './drone-list/drone-list.component';
14 | import { UserManagementComponent } from './user-management/user-management.component';
15 | import { SignupComponent } from './signup/signup.component';
16 | import { ForgotPasswordComponent } from './forgot-password/forgot-password.component';
17 |
18 | const appRoutes: Routes = [
19 | {
20 | path: 'login',
21 | component: LoginComponent
22 | },
23 | {
24 | path: 'signup',
25 | component: SignupComponent
26 | },
27 | {
28 | path: 'resetpassword',
29 | component: ForgotPasswordComponent
30 | },
31 | {
32 | path: 'dashboard',
33 | data: {
34 | breadcrumb: 'Dashboard'
35 | },
36 | component: UserViewComponent,
37 | canActivate: [
38 | RouteGuardService
39 | ],
40 | children: [
41 | {
42 | path: 'map',
43 | data: {
44 | breadcrumb: 'Map'
45 | },
46 | component: DashboardComponent,
47 | canActivate: [RouteGuardService]
48 | },
49 | {
50 | path: 'user',
51 | data: {
52 | breadcrumb: 'User'
53 | },
54 | component: UserDashboardComponent,
55 | canActivate: [RouteGuardService],
56 | children: [
57 | {
58 | path: 'groups',
59 | data: {
60 | breadcrumb: 'Manage Groups'
61 | },
62 | component: DroneGroupsComponent,
63 | canActivate: [RouteGuardService]
64 | },
65 | {
66 | path: 'list',
67 | data: {
68 | breadcrumb: 'Manage Drones'
69 | },
70 | component: DroneListComponent,
71 | canActivate: [RouteGuardService]
72 | },
73 | {
74 | path: 'users',
75 | data: {
76 | breadcrumb: 'Manage Users'
77 | },
78 | component: UserManagementComponent,
79 | canActivate: [RouteGuardService]
80 | }
81 | ]
82 | }
83 | ]
84 | },
85 | {
86 | path: '',
87 | component: LoginComponent
88 | },
89 | {
90 | path: '**',
91 | component: LoginComponent
92 | }
93 | ];
94 |
95 | @NgModule({
96 | imports: [
97 | RouterModule.forRoot(appRoutes)
98 | ],
99 | exports: [
100 | RouterModule
101 | ]
102 | })
103 |
104 | export class AppRouter {}
105 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at info@scorelab.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/dronesym-python/flask-api/src/main.py:
--------------------------------------------------------------------------------
1 | """
2 | Main entry point for the Flask API. The API will provide
3 | """
4 | # an interface to communicate with Dronekit instances
5 | from flask import jsonify, Flask
6 | from flask import abort, request
7 | from flask import make_response
8 | import dronepool
9 | import threadrunner
10 |
11 | app = Flask(__name__)
12 | api_base_url = '/dronesym/api/flask'
13 |
14 |
15 | # response.headers['X-Content-Type-Options'] = 'nosniff'
16 | # response.headers['X-Frame-Options'] = 'SAMEORIGIN'
17 | @app.after_request
18 | def apply_caching(response):
19 | response.headers["X-Frame-Options"] = "SAMEORIGIN"
20 | response.headers['X-Content-Type-Options'] = 'nosniff'
21 | return response
22 |
23 | @app.errorhandler(404)
24 | def send_not_found(error):
25 | return make_response(jsonify({"message": "Resource not found"}), 404)
26 |
27 |
28 | @app.errorhandler(400)
29 | def send_bad_request(error):
30 | return make_response(jsonify({"message": "Bad request"}), 400)
31 |
32 |
33 | @app.route(api_base_url + '/spawn', methods=['POST'])
34 | def create_new_drone():
35 |
36 | # This routes creates a new Dronekit SITL in the Drone Pool.
37 | # The initial position needs to be send along the request as a JSON
38 | global q
39 | # response.headers['X-Content-Type-Options'] = 'nosniff'
40 | # response.headers['X-Frame-Options'] = 'SAMEORIGIN'
41 | if not request.json or not 'location'in request.json or 'droneId' not in request.json:
42 | abort(400)
43 | print(request.json)
44 | home = request.json['location']
45 | drone_id = request.json['droneId']
46 | q.put((dronepool.create_new_drone, {"db_key": drone_id, "home": home}))
47 |
48 | return jsonify({"status": "OK", "message": "Created new drone"})
49 |
50 |
51 | @app.route(api_base_url + '/remove/', methods=['POST'])
52 | def remove_drone(drone_id):
53 | global q
54 | q.put((dronepool.remove_drone, {"drone_id": drone_id}))
55 | return jsonify({"status": "OK", "message": "Removed drone"})
56 |
57 |
58 | @app.route(api_base_url + '//takeoff', methods=['POST'])
59 | def send_takeoff(drone_id):
60 | # This route issues a takeoff command to a specific drone
61 | global q
62 | if request.json and request.json['waypoints'] and len(
63 | request.json['waypoints']) > 0:
64 | q.put(
65 | (dronepool.takeoff_drone, {
66 | "drone_id": drone_id, "waypoints": request.json['waypoints']}))
67 | else:
68 | q.put((dronepool.takeoff_drone, {"drone_id": drone_id}))
69 | return jsonify({"status": "taking_off", "drone_id": drone_id})
70 |
71 |
72 | @app.route(api_base_url + '//land', methods=['POST'])
73 | def send_land(drone_id):
74 | global q
75 | q.put((dronepool.land_drone, {"drone_id": drone_id}))
76 | return jsonify({"status": "landing", "drone_id": drone_id})
77 |
78 |
79 | @app.route(api_base_url + '//resume', methods=['POST'])
80 | def send_resume(drone_id):
81 | global q
82 | q.put((dronepool.resume_flight, {"drone_id": drone_id}))
83 | return jsonify({"status": "resuming", "drone_id": drone_id})
84 |
85 |
86 | if __name__ == '__main__':
87 | threadrunner.initialize()
88 | q = threadrunner.q
89 | dronepool.initialize()
90 | app.run(debug=True, use_reloader=False)
91 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/drone-groups/drone-groups.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { DroneDataService } from '../drone-service/drone-data.service';
3 | import { UserService } from '../user-service/user.service';
4 | import { DronesBoxComponent } from '../drones-box/drones-box.component';
5 |
6 | declare var Materialize: any;
7 |
8 | @Component({
9 | selector: 'app-drone-groups',
10 | templateUrl: './drone-groups.component.html',
11 | styleUrls: ['./drone-groups.component.css']
12 | })
13 | export class DroneGroupsComponent {
14 | userRole: string;
15 | groups: any;
16 | showGroupDialog: boolean;
17 | showDronesDialog: boolean;
18 | drones: any;
19 | currGroup: string;
20 |
21 | constructor(private droneFeed: DroneDataService, private userService: UserService) {
22 | this.userRole = '';
23 | this.showGroupDialog = false;
24 | this.showDronesDialog = false;
25 | this.drones = [];
26 |
27 | this.initialize();
28 | }
29 |
30 | private initialize() {
31 | this.userService.getUserRole()
32 | .then((role) => this.userRole = role)
33 | .catch((err) => console.log(err));
34 |
35 | this.droneFeed.getGroups()
36 | .then((res) => {
37 | this.groups = res.groups;
38 | // console.log(this.groups);
39 | })
40 | .catch((err) => console.log(err));
41 |
42 | this.droneFeed.getDroneFeed()
43 | .subscribe((drones) => { this.drones = drones; });
44 | }
45 |
46 |
47 | createGroup($event) {
48 | if ($event.message === 'DIALOG_CONFIRM') {
49 | this.droneFeed.createGroup($event.name)
50 | .then((res) => {
51 | if (res.status === 'ERROR') {
52 | Materialize.toast(res.msg, 4000);
53 | return;
54 | }
55 | this.groups.push(res.group);
56 | });
57 | }
58 | this.showGroupDialog = false;
59 | }
60 |
61 | showCreateGroupDialog() {
62 | this.showGroupDialog = true;
63 | }
64 |
65 | showSelectDronesDialog(groupId) {
66 | this.showDronesDialog = true;
67 | this.currGroup = groupId;
68 | }
69 |
70 | addDronesToGroup($event) {
71 | if ($event.action === 'DRONES_BOX_CONFIRM' && $event.items.length > 0) {
72 | this.droneFeed.addToGroup(this.currGroup, $event.items)
73 | .then((res) => {
74 | // console.log(res.group);
75 | const newGroup = res.group;
76 | this.groups = this.groups.map((group) => group._id === newGroup._id ? newGroup : group);
77 | });
78 | }
79 | this.showDronesDialog = false;
80 | this.currGroup = '';
81 | }
82 |
83 | removeGroup(groupId) {
84 | this.droneFeed.removeGroup(groupId)
85 | .then((res) => {
86 | console.log(res);
87 | Materialize.toast(`Deleted group ${res.group.name}`, 4000);
88 | this.groups = this.groups.filter((group) => group._id !== res.group._id);
89 | })
90 | .catch((err) => {
91 | console.log(err);
92 | });
93 | }
94 |
95 | removeFromGroup(groupId, droneId) {
96 | this.droneFeed.removeFromGroup(groupId, droneId)
97 | .then((res) => {
98 | console.log(res);
99 | const newGroup = res.group;
100 | this.groups = this.groups.map((group) => group._id === newGroup._id ? newGroup : group);
101 | });
102 | }
103 |
104 | getName(droneId) {
105 | return this.drones.filter((drone) => drone._id === droneId)
106 | .map((drone) => drone.name);
107 | }
108 |
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { NgModule } from '@angular/core';
3 | import { environment } from '../environments/environment';
4 |
5 | import { MaterializeModule } from 'angular2-materialize';
6 | import { HttpModule } from '@angular/http';
7 | import { AuthHttpService } from './auth-http/auth-http.service';
8 | import { RouterModule, Routes} from '@angular/router';
9 | import { AppRouter } from './app-router';
10 |
11 | import { AppComponent } from './app.component';
12 | import { AgmCoreModule } from '@agm/core';
13 | import { DroneDataService } from './drone-service/drone-data.service';
14 | import { UserService } from './user-service/user.service';
15 | import { RouteGuardService } from './route-guard/route-guard.service';
16 | import { AdminAuthorizeService } from './admin-authorize/admin-authorize.service';
17 | import { CursorTooltipComponent } from './cursor-tooltip/cursor-tooltip.component';
18 | import { ConfirmDialogComponent } from './confirm-dialog/confirm-dialog.component';
19 | import { DroneOptionBoxComponent } from './drone-option-box/drone-option-box.component';
20 | import { DashboardComponent } from './dashboard/dashboard.component';
21 | import { LoginComponent } from './login/login.component';
22 | import { UserSignupComponent } from './user-signup/user-signup.component';
23 | import { UserViewComponent } from './user-view/user-view.component';
24 | import { UserDashboardComponent } from './user-dashboard/user-dashboard.component';
25 | import { DroneGroupsComponent } from './drone-groups/drone-groups.component';
26 | import { DroneListComponent } from './drone-list/drone-list.component';
27 | import { DronesBoxComponent } from './drones-box/drones-box.component';
28 | import { UserManagementComponent } from './user-management/user-management.component';
29 | import { SignupComponent } from './signup/signup.component';
30 | import { BreadcrumbComponent } from './breadcrumb/breadcrumb.component';
31 | import { ForgotPasswordComponent } from './forgot-password/forgot-password.component';
32 | import { ResetCodeDialogComponent } from './reset-code-dialog/reset-code-dialog.component';
33 | import { NewPasswordDialogComponent } from './new-password-dialog/new-password-dialog.component';
34 | import { ServiceWorkerModule } from '@angular/service-worker';
35 |
36 | @NgModule({
37 | declarations: [
38 | AppComponent,
39 | CursorTooltipComponent,
40 | ConfirmDialogComponent,
41 | DroneOptionBoxComponent,
42 | DashboardComponent,
43 | LoginComponent,
44 | UserSignupComponent,
45 | UserViewComponent,
46 | UserDashboardComponent,
47 | DroneGroupsComponent,
48 | DroneListComponent,
49 | DronesBoxComponent,
50 | UserManagementComponent,
51 | SignupComponent,
52 | BreadcrumbComponent,
53 | ForgotPasswordComponent,
54 | ResetCodeDialogComponent,
55 | NewPasswordDialogComponent
56 | ],
57 | imports: [
58 | BrowserModule,
59 | MaterializeModule,
60 | HttpModule,
61 | AgmCoreModule.forRoot({
62 | apiKey: environment.mapsApiKey
63 | }),
64 | AppRouter,
65 | ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production })
66 | ],
67 | providers: [
68 | DroneDataService,
69 | UserService,
70 | AuthHttpService,
71 | RouteGuardService,
72 | AdminAuthorizeService
73 | ],
74 | bootstrap: [AppComponent]
75 | })
76 | export class AppModule { }
77 |
--------------------------------------------------------------------------------
/dronesym-frontend/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "arrow-return-shorthand": true,
7 | "callable-types": true,
8 | "class-name": true,
9 | "comment-format": [
10 | true,
11 | "check-space"
12 | ],
13 | "curly": true,
14 | "eofline": true,
15 | "forin": true,
16 | "import-blacklist": [
17 | true
18 | ],
19 | "import-spacing": true,
20 | "indent": [
21 | true,
22 | "spaces"
23 | ],
24 | "interface-over-type-literal": true,
25 | "label-position": true,
26 | "max-line-length": [
27 | true,
28 | 140
29 | ],
30 | "member-access": false,
31 | "member-ordering": [
32 | true,
33 | "static-before-instance"
34 | ],
35 | "no-arg": true,
36 | "no-bitwise": true,
37 | "no-console": [
38 | true,
39 | "debug",
40 | "info",
41 | "time",
42 | "timeEnd",
43 | "trace"
44 | ],
45 | "no-construct": true,
46 | "no-debugger": true,
47 | "no-duplicate-super": true,
48 | "no-empty": false,
49 | "no-empty-interface": true,
50 | "no-eval": true,
51 | "no-inferrable-types": [
52 | true,
53 | "ignore-params"
54 | ],
55 | "no-misused-new": true,
56 | "no-non-null-assertion": true,
57 | "no-shadowed-variable": true,
58 | "no-string-literal": false,
59 | "no-string-throw": true,
60 | "no-switch-case-fall-through": true,
61 | "no-trailing-whitespace": true,
62 | "no-unnecessary-initializer": true,
63 | "no-unused-expression": true,
64 | "no-use-before-declare": true,
65 | "no-var-keyword": true,
66 | "object-literal-sort-keys": false,
67 | "one-line": [
68 | true,
69 | "check-open-brace",
70 | "check-catch",
71 | "check-else",
72 | "check-whitespace"
73 | ],
74 | "prefer-const": true,
75 | "quotemark": [
76 | true,
77 | "single"
78 | ],
79 | "radix": true,
80 | "semicolon": [
81 | true
82 | ],
83 | "triple-equals": [
84 | true,
85 | "allow-null-check"
86 | ],
87 | "typedef-whitespace": [
88 | true,
89 | {
90 | "call-signature": "nospace",
91 | "index-signature": "nospace",
92 | "parameter": "nospace",
93 | "property-declaration": "nospace",
94 | "variable-declaration": "nospace"
95 | }
96 | ],
97 | "typeof-compare": true,
98 | "unified-signatures": true,
99 | "variable-name": false,
100 | "whitespace": [
101 | true,
102 | "check-branch",
103 | "check-decl",
104 | "check-operator",
105 | "check-separator",
106 | "check-type"
107 | ],
108 | "directive-selector": [
109 | true,
110 | "attribute",
111 | "app",
112 | "camelCase"
113 | ],
114 | "component-selector": [
115 | true,
116 | "element",
117 | "app",
118 | "kebab-case"
119 | ],
120 | "use-input-property-decorator": true,
121 | "use-output-property-decorator": true,
122 | "use-host-property-decorator": true,
123 | "no-input-rename": false,
124 | "no-output-rename": false,
125 | "use-life-cycle-interface": true,
126 | "use-pipe-transform-interface": true,
127 | "component-class-suffix": true,
128 | "directive-class-suffix": true,
129 | "no-access-missing-member": true,
130 | "templates-use-public": true,
131 | "invoke-injectable": true
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/user-management/user-management.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { UserSignupComponent } from '../user-signup/user-signup.component';
3 | import { UserService } from '../user-service/user.service';
4 | import { DroneDataService } from '../drone-service/drone-data.service';
5 | import { DronesBoxComponent } from '../drones-box/drones-box.component';
6 |
7 | @Component({
8 | selector: 'app-user-management',
9 | templateUrl: './user-management.component.html',
10 | styleUrls: ['./user-management.component.css']
11 | })
12 | export class UserManagementComponent implements OnInit {
13 |
14 | public showUserSignUpDialog: boolean;
15 | public showDroneGroupsDialog: boolean;
16 | public userRole;
17 | public users;
18 | public groups;
19 | public currUser;
20 |
21 | constructor(private userService: UserService, private droneService: DroneDataService) {
22 | this.showUserSignUpDialog = false;
23 | this.showDroneGroupsDialog = false;
24 | this.groups = [];
25 | this.users = [];
26 | this.currUser = '';
27 |
28 | userService.getUserRole().then((role) => {
29 | this.userRole = role;
30 | });
31 |
32 | userService.getUserList().then((users) => {
33 | this.users = users.users;
34 | // console.log(this.users);
35 | });
36 |
37 | droneService.getGroups().then((groups) => {
38 | // console.log(groups);
39 | this.groups = groups.groups.map((group) => {
40 | return {
41 | name : group.name,
42 | _id : group._id
43 | };
44 | });
45 | });
46 | }
47 |
48 | ngOnInit() {
49 | }
50 |
51 | private updateUser(userData) {
52 | this.users = this.users.map((user) => {
53 | // console.log(user);
54 | // console.log(userData);
55 | if (user._id === userData.id) {
56 | return userData;
57 | } else {
58 | return user;
59 | }
60 | });
61 | }
62 | private updateGroup(groupData) {
63 | this.groups = this.groups.map((groups) => {
64 | if (groups._id === groupData.id) {
65 | return groupData;
66 | } else {
67 | return groups;
68 | }
69 | });
70 | }
71 |
72 | public showCreateUserDialog() {
73 | this.showUserSignUpDialog = true;
74 | }
75 |
76 | public showGroupsDialog(userId) {
77 | this.showDroneGroupsDialog = true;
78 | this.currUser = userId;
79 | }
80 |
81 | public onDronesBoxResponse($event) {
82 | const groups = $event.items;
83 | // console.log(groups);
84 |
85 | groups.forEach((groupId) => {
86 | this.userService.addUserToGroup(this.currUser, groupId)
87 | .then((res) => {
88 | // console.log(res);
89 | this.updateUser(res.user);
90 | });
91 | });
92 | groups.forEach((groupId) => {
93 | this.userService.updateUserToGroup(this.currUser, groupId)
94 | .then((res) => {
95 | // console.log(res);
96 | this.updateGroup(res.user);
97 | });
98 | });
99 |
100 | this.showDroneGroupsDialog = false;
101 | this.currUser = '';
102 | }
103 |
104 | public onUserSignupResponse() {
105 | this.userService.getUserList().then((users) => {
106 | this.users = users.users;
107 | });
108 | this.showUserSignUpDialog = false;
109 | }
110 |
111 | public removeFromGroup(userId, groupId) {
112 | this.userService.removeUserFromGroup(userId, groupId)
113 | .then((res) => {
114 | this.updateUser(res.user);
115 | });
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/dashboard/dashboard.component.html:
--------------------------------------------------------------------------------
1 |
3 |
5 |
6 | 1" strokeColor="blue" strokeOpacity="0.8">
7 |
8 |
9 |
10 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
51 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/breadcrumb/breadcrumb.component.ts:
--------------------------------------------------------------------------------
1 |
2 | import { Component, OnInit } from '@angular/core';
3 | import { ActivatedRoute, Router, NavigationEnd } from '@angular/router';
4 |
5 | /** rxjs Imports */
6 | import { filter } from 'rxjs/operators';
7 |
8 | /** Custom Model */
9 | import { Breadcrumb } from './breadcrumb';
10 |
11 |
12 | const routeDataBreadcrumb = 'breadcrumb';
13 |
14 | const routeParamBreadcrumb = 'routeParamBreadcrumb';
15 |
16 | const routeResolveBreadcrumb = 'routeResolveBreadcrumb';
17 |
18 | const routeAddBreadcrumbLink = 'addBreadcrumbLink';
19 |
20 | @Component({
21 | selector: 'app-breadcrumb',
22 | templateUrl: './breadcrumb.component.html',
23 | styleUrls: ['./breadcrumb.component.css'],
24 |
25 | })
26 | export class BreadcrumbComponent implements OnInit {
27 |
28 | /** Array of breadcrumbs. */
29 | breadcrumbs: Breadcrumb[];
30 |
31 |
32 | constructor(private activatedRoute: ActivatedRoute,
33 | private router: Router) {
34 | this.generateBreadcrumbs();
35 | }
36 |
37 | ngOnInit() {
38 | }
39 |
40 | /**
41 | * Generates the array of breadcrumbs for the visited route.
42 | */
43 | generateBreadcrumbs() {
44 | const onNavigationEnd = this.router.events.pipe(filter(event => event instanceof NavigationEnd));
45 |
46 | onNavigationEnd.subscribe(() => {
47 | this.breadcrumbs = [];
48 | let currentRoute = this.activatedRoute.root;
49 | let currentUrl = '';
50 |
51 | while (currentRoute.children.length > 0) {
52 | const childrenRoutes = currentRoute.children;
53 | let breadcrumbLabel: any;
54 | let url: any;
55 |
56 | childrenRoutes.forEach(route => {
57 | currentRoute = route;
58 | breadcrumbLabel = false;
59 |
60 | if (route.outlet !== 'primary') {
61 | return;
62 | }
63 |
64 | const routeURL = route.snapshot.url.map(segment => segment.path).join('/');
65 | currentUrl += `/${routeURL}`;
66 |
67 | if (currentUrl === '/') {
68 | breadcrumbLabel = 'Home';
69 | }
70 |
71 | const hasData = (route.routeConfig && route.routeConfig.data);
72 |
73 | if (hasData) {
74 | if (route.snapshot.data.hasOwnProperty(routeResolveBreadcrumb) && route.snapshot.data[routeResolveBreadcrumb]) {
75 | breadcrumbLabel = route.snapshot.data;
76 | route.snapshot.data[routeResolveBreadcrumb].forEach((property: any) => {
77 | breadcrumbLabel = breadcrumbLabel[property];
78 | });
79 | } else if (route.snapshot.data.hasOwnProperty(routeParamBreadcrumb) &&
80 | route.snapshot.paramMap.get(route.snapshot.data[routeParamBreadcrumb])) {
81 | breadcrumbLabel = route.snapshot.paramMap.get(route.snapshot.data[routeParamBreadcrumb]);
82 | } else if (route.snapshot.data.hasOwnProperty(routeDataBreadcrumb)) {
83 | breadcrumbLabel = route.snapshot.data[routeDataBreadcrumb];
84 | }
85 |
86 | if (route.snapshot.data.hasOwnProperty(routeAddBreadcrumbLink)) {
87 | url = route.snapshot.data[routeAddBreadcrumbLink];
88 | } else {
89 | url = currentUrl;
90 | }
91 | }
92 |
93 | const breadcrumb: Breadcrumb = {
94 | label: breadcrumbLabel,
95 | url: url
96 | };
97 | if (breadcrumbLabel) {
98 | this.breadcrumbs.push(breadcrumb);
99 | }
100 | });
101 | }
102 | });
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/forgot-password/forgot-password.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { map } from 'rxjs/operators';
3 | import { UserService } from '../user-service/user.service';
4 | import { Router } from '@angular/router';
5 |
6 | declare var Materialize: any;
7 | @Component({
8 | selector: 'app-forgot-password',
9 | templateUrl: './forgot-password.component.html',
10 | styleUrls: ['./forgot-password.component.css']
11 | })
12 | export class ForgotPasswordComponent implements OnInit {
13 | private user: any = {};
14 | http: any;
15 | baseUrl: any;
16 | userRole: any;
17 | genCode: string;
18 | dialogParams = {
19 | codeDialog: { show: false },
20 | passDialog: {show: false}
21 | };
22 | validUser: boolean;
23 |
24 | constructor(private router: Router, private userService: UserService) {
25 | this.user.username = '';
26 | }
27 | setUsername($event) {
28 | this.user.username = $event.target.value;
29 | }
30 | generateCode() {
31 | // const min = 0;
32 | // const max = 9;
33 | // let rand;
34 | // let num = '';
35 | const length = 10;
36 | let result = '';
37 | const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
38 | const charactersLength = characters.length;
39 | for ( let i = 0; i < length; i++ ) {
40 | result += characters.charAt(Math.floor(Math.random() * charactersLength));
41 | }
42 | console.log(result);
43 | return result;
44 |
45 |
46 | }
47 | ngOnInit() {
48 | }
49 |
50 | sendEmail(code){
51 | this.userService.sendEmail(code)
52 | .then((res) => {
53 |
54 | });
55 | }
56 | checkUser($event) {
57 | this.genCode = this.generateCode();
58 | $event.preventDefault();
59 | this.userService.checkUser(this.user.username)
60 | .then((res) => {
61 | if (res.status === 'OK') {
62 | this.validUser = true;
63 | this.sendEmail(this.genCode);
64 | this.dialogParams.codeDialog.show = true;
65 | } else {
66 | Materialize.toast(res.msg, 4000);
67 | }
68 | });
69 | }
70 | public processDialogResponse($data) {
71 | console.log('Generated :' + this.genCode);
72 | // ($data);
73 | if (this.validUser === true) {
74 | if ($data.message === 'DIALOG_CONFIRM') {
75 | if ($data.code === this.genCode) {
76 | this.dialogParams.passDialog.show = true;
77 | } else {
78 | Materialize.toast('Invalid code', 4000);
79 | }
80 | } else {
81 | Materialize.toast('Dialog canceled!', 4000);
82 | this.router.navigate(['/resetpassword']);
83 | }
84 | this.dialogParams.codeDialog.show = false;
85 | }
86 | }
87 | public processPassDialogResponse($data) {
88 | console.log($data);
89 | if ($data.pass !== $data.retype) {
90 | Materialize.toast('Retype password does not match', 3000);
91 | return;
92 | } else {
93 | console.log($data.pass);
94 | this.userService.updatePass(this.user.username, $data.pass)
95 | .then((status) => {
96 | if (status.status === 'OK') {
97 | Materialize.toast('Password updated successfully', 4000);
98 | this.router.navigate(['/login']);
99 | } else if (status.status === 'ERROR') {
100 | Materialize.toast(status.msg, 4000);
101 | }
102 | }, (err) => {
103 | Materialize.toast('Oops something went wrong...', 4000);
104 | console.log(err);
105 | });
106 | }
107 |
108 |
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/user-signup/user-signup.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Output, Input, EventEmitter } from '@angular/core';
2 | import { Router } from '@angular/router';
3 | import { UserService } from '../user-service/user.service';
4 | import { MaterializeAction } from 'angular2-materialize';
5 |
6 | declare var Materialize;
7 |
8 | @Component({
9 | selector: 'app-user-signup',
10 | templateUrl: './user-signup.component.html',
11 | styleUrls: ['./user-signup.component.css']
12 | })
13 | export class UserSignupComponent implements OnInit {
14 |
15 | modalActions = new EventEmitter();
16 |
17 | /** Regular expression for email validation */
18 | // tslint:disable-next-line: max-line-length
19 | regexp = new RegExp(/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/);
20 |
21 | @Output('onResponse') onResponse = new EventEmitter();
22 | @Input()
23 | set show(show: boolean) {
24 | if (show) {
25 | this.modalActions.emit({ action: 'modal', params: ['open']});
26 | } else {
27 | this.modalActions.emit({ action: 'modal', params: ['close']});
28 | this.onResponse.emit({ status : 'CLOSED' });
29 | }
30 | }
31 |
32 |
33 | private user: any;
34 |
35 | constructor(private userService: UserService, private router: Router) {
36 | this.user = { uname: '', email: '', password: '', retype: '', role: '' };
37 | }
38 |
39 | ngOnInit() { }
40 |
41 | public setUsername($event) {
42 | this.user.uname = $event.target.value;
43 | }
44 |
45 | public setEmail($event) {
46 | this.user.email = $event.target.value;
47 | }
48 |
49 | public setPassword($event) {
50 | this.user.password = $event.target.value;
51 | }
52 |
53 | public setRetype($event) {
54 | this.user.retype = $event.target.value;
55 | }
56 |
57 | public onSignup($event) {
58 | $event.preventDefault();
59 |
60 | if (this.user.uname === '' || this.user.password === '' || this.user.retype === '') {
61 | Materialize.toast('All fields must be specified', 3000);
62 | return;
63 | }
64 |
65 | if (!this.regexp.test(this.user.email)) {
66 | Materialize.toast('Please enter a valid Email Address', 3000);
67 | return;
68 | }
69 |
70 | if (this.user.role === '') {
71 | Materialize.toast('Please specify a user role', 3000);
72 | return;
73 | }
74 |
75 | if (this.user.password !== this.user.retype) {
76 | Materialize.toast('Retype password does not match', 3000);
77 | return;
78 | }
79 |
80 | this.userService.createUser(this.user.uname, this.user.email, this.user.password, this.user.role)
81 | .then((status) => {
82 | if (status.status === 'OK') {
83 | Materialize.toast('User created successfully', 4000);
84 | this.modalActions.emit({ action: 'modal', params: ['close']});
85 | this.onResponse.emit({ status : 'CLOSED' });
86 | } else if (status.status === 'ERROR') {
87 | Materialize.toast(status.msg, 4000);
88 | this.onResponse.emit({ status : 'ERROR', msg : status.msg });
89 | }
90 | }, (err) => {
91 | Materialize.toast('Oops something went wrong...', 4000);
92 | this.onResponse.emit({ status : 'ERROR', msg : err });
93 | console.log(err);
94 | });
95 | // console.log(this.user);
96 | }
97 |
98 | public onRoleSelect($event) {
99 | const role = $event.target.value.toLowerCase();
100 | this.user.role = role;
101 | // console.log(this.user);
102 | }
103 |
104 | public cancel() {
105 | this.onResponse.emit({ status : 'CANCEL' });
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/dronesym-node/Routers/droneRouter.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const passport = require('passport');
3 | const drones = require('../Controllers/droneCtrl');
4 | const userCtrl = require('../Controllers/userCtrl');
5 | const router = new express.Router();
6 |
7 | const authenticate = passport.authenticate('jwt', {session: false});
8 | const authorize = userCtrl.authorizeUser;
9 |
10 | router.post('/create', authenticate, authorize(['admin']), function(req, res) {
11 | const drone = req.body;
12 | drones.createDrone(drone.name, drone.description,
13 | drone.flying_time, drone.location, req.user.id, function(response) {
14 | res.json(response);
15 | });
16 | });
17 |
18 | router.post('/remove/:id', authenticate, authorize(['admin']),
19 | function(req, res) {
20 | const drone = req.body;
21 | // console.log(drone);
22 |
23 | drones.removeDrone(req.params.id, drone.status, function(status) {
24 | res.json(status);
25 | });
26 | });
27 |
28 | router.get('/get', function(req, res) {
29 | drones.getDroneIds(function(ids) {
30 | res.json({'status': 'OK', 'drones': ids});
31 | });
32 | });
33 |
34 | router.get('/get/:id', function(req, res) {
35 | drones.getDroneById(req.params.id, function(drone) {
36 | // console.log(drone);
37 | res.json(drone);
38 | });
39 | });
40 |
41 | router.post('/update/:id', function(req, res) {
42 | drones.updateDroneStatus(req.params.id, req.body, function(status) {
43 | res.json(status);
44 | });
45 | });
46 |
47 | router.post('/takeoff/:id', authenticate, authorize(['admin', 'user']),
48 | function(req, res) {
49 | const waypoints = req.body.waypoints;
50 |
51 | drones.takeoffDrone(req.params.id, waypoints, function(status) {
52 | res.json(status);
53 | });
54 | });
55 |
56 | router.post('/land/:id', authenticate, authorize(['admin', 'user']),
57 | function(req, res) {
58 | const droneId = req.params.id;
59 |
60 | drones.landDrone(droneId, function(status) {
61 | res.json(status);
62 | });
63 | });
64 |
65 | router.post('/update/waypoints/:id', authenticate, authorize(['admin', 'user']),
66 | function(req, res) {
67 | const droneId = req.params.id;
68 | const waypoints = req.body.waypoints;
69 |
70 | drones.updateWaypoints(droneId, waypoints, function(status) {
71 | // console.log(status);
72 | res.json(status);
73 | });
74 | });
75 |
76 | router.post('/resume/:id', authenticate, authorize(['admin', 'user']),
77 | function(req, res) {
78 | const droneId = req.params.id;
79 | drones.resumeFlight(droneId, function(status) {
80 | res.json(status);
81 | });
82 | });
83 |
84 | router.post('/groups/create', authenticate, authorize(['admin']),
85 | function(req, res) {
86 | drones.createGroup(req.body.name, req.user.id, function(status) {
87 | res.json(status);
88 | });
89 | });
90 |
91 | router.post('/groups/remove/:id', authenticate, authorize(['admin']),
92 | function(req, res) {
93 | drones.removeGroup(req.params.id, function(status) {
94 | res.json(status);
95 | });
96 | });
97 |
98 | router.get('/groups', authenticate, function(req, res) {
99 | drones.getGroups(req.user.id, function(status) {
100 | res.json(status);
101 | });
102 | });
103 |
104 |
105 | router.post('/groups/:id/add', authenticate, function(req, res) {
106 | drones.addToGroup(req.params.id, req.body.drones, function(status) {
107 | res.json(status);
108 | });
109 | });
110 |
111 | router.post('/groups/:id/remove/:droneId', authenticate, function(req, res) {
112 | drones.removeFromGroup(req.params.id, req.params.droneId, function(status) {
113 | res.json(status);
114 | });
115 | });
116 |
117 | module.exports = router;
118 |
--------------------------------------------------------------------------------
/dronesym-frontend/src/app/drone-service/drone-data.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { AuthHttpService } from '../auth-http/auth-http.service';
3 | import { environment } from '../../environments/environment';
4 | import * as io from 'socket.io-client';
5 |
6 | import { Observable } from 'rxjs';
7 | import { map } from 'rxjs/operators';
8 |
9 | import 'rxjs/add/operator/toPromise';
10 |
11 | @Injectable()
12 | export class DroneDataService {
13 |
14 | private baseUrl: string;
15 | private drones: any;
16 | private droneObserver: any;
17 |
18 | feed: any;
19 |
20 | constructor(private http: AuthHttpService) {
21 | this.baseUrl = environment.nodeApiURL;
22 | this.drones = [];
23 | }
24 |
25 | public createDrone(name: string, description: string, flying_time: string, location: any): Promise {
26 | return this.http.post(`${this.baseUrl}/create`,
27 | { 'location' : location, 'name' : name, 'description': description, 'flying_time': flying_time})
28 | .pipe(map((res) => res.json()))
29 | .toPromise();
30 | }
31 |
32 | public removeDrone(droneId: string, droneStatus: string) {
33 | return this.http.post(`${this.baseUrl}/remove/${droneId}`, { 'status' : droneStatus })
34 | .pipe(map((res) => res.json()))
35 | .toPromise();
36 | }
37 |
38 | public getDroneFeed(): Observable {
39 | const feedObservable = new Observable((observer) => {
40 | const token = localStorage.getItem('token').slice(4);
41 | this.feed = io(environment.feedURL, { 'query' : `token=${token}`});
42 | this.droneObserver = observer;
43 |
44 | this.feed.on('SOCK_FEED_UPDATE', (data) => {
45 | // console.log(data);
46 | this.drones = data;
47 | observer.next(this.drones);
48 | });
49 |
50 | return () => {
51 | this.feed.disconnect();
52 | };
53 | });
54 |
55 | return feedObservable;
56 | }
57 |
58 | public updateDroneWaypoints(droneId: string, waypoints: [any]) {
59 | return this.http.post(`${this.baseUrl}/update/waypoints/${droneId}`, { 'waypoints': waypoints})
60 | .pipe(map((res) => res.json()))
61 | .toPromise();
62 | }
63 |
64 | public takeOffDrone(droneId: string, waypoints: [any]) {
65 | return this.http.post(`${this.baseUrl}/takeoff/${droneId}`, {'waypoints': waypoints})
66 | .pipe(map((res) => res.json()))
67 | .toPromise();
68 | }
69 |
70 | public landDrone(droneId: string) {
71 | return this.http.post(`${this.baseUrl}/land/${droneId}`, {})
72 | .pipe(map((res) => res.json()))
73 | .toPromise();
74 | }
75 |
76 | public resumeFlight(droneId: string) {
77 | return this.http.post(`${this.baseUrl}/resume/${droneId}`, {})
78 | .pipe(map((res) => res.json()))
79 | .toPromise();
80 | }
81 |
82 | public createGroup(name: string) {
83 | return this.http.post(`${this.baseUrl}/groups/create`, { 'name' : name })
84 | .pipe(map((res) => res.json()))
85 | .toPromise();
86 | }
87 |
88 | public getGroups() {
89 | return this.http.get(`${this.baseUrl}/groups`)
90 | .pipe(map((res) => res.json()))
91 | .toPromise();
92 | }
93 |
94 | public addToGroup(groupId: string, drones: [string]) {
95 | return this.http.post(`${this.baseUrl}/groups/${groupId}/add`, { 'drones' : drones })
96 | .pipe(map((res) => res.json()))
97 | .toPromise();
98 | }
99 |
100 | public removeFromGroup(groupId: string, droneId: string) {
101 | return this.http.post(`${this.baseUrl}/groups/${groupId}/remove/${droneId}`, {})
102 | .pipe(map((res) => res.json()))
103 | .toPromise();
104 | }
105 |
106 | public removeGroup(groupId: string) {
107 | return this.http.post(`${this.baseUrl}/groups/remove/${groupId}`, {})
108 | .pipe(map((res) => res.json()))
109 | .toPromise();
110 | }
111 |
112 | public updateName(droneId: string, newName: string) {
113 | return this.http.post(`${this.baseUrl}/update/${droneId}`, { 'name' : newName })
114 | .pipe(map((res) => res.json()))
115 | .toPromise();
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | When contributing to this repository, please first discuss the change you wish to make via issue,
4 | email, or any other method with the owners of this repository before making a change.
5 |
6 | Please note we have a code of conduct, please follow it in all your interactions with the project.
7 |
8 | ## Pull Request Process
9 |
10 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a
11 | build.
12 | 2. Update the README.md with details of changes to the interface, this includes new environment
13 | variables, exposed ports, useful file locations and container parameters.
14 | 3. Increase the version numbers in any examples files and the README.md to the new version that this
15 | Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
16 | 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you
17 | do not have permission to do that, you may request the second reviewer to merge it for you.
18 |
19 | ## Code of Conduct
20 |
21 | ### Our Pledge
22 |
23 | In the interest of fostering an open and welcoming environment, we as
24 | contributors and maintainers pledge to making participation in our project and
25 | our community a harassment-free experience for everyone, regardless of age, body
26 | size, disability, ethnicity, gender identity and expression, level of experience,
27 | nationality, personal appearance, race, religion, or sexual identity and
28 | orientation.
29 |
30 | ### Our Standards
31 |
32 | Examples of behavior that contributes to creating a positive environment
33 | include:
34 |
35 | * Using welcoming and inclusive language
36 | * Being respectful of differing viewpoints and experiences
37 | * Gracefully accepting constructive criticism
38 | * Focusing on what is best for the community
39 | * Showing empathy towards other community members
40 |
41 | Examples of unacceptable behavior by participants include:
42 |
43 | * The use of sexualized language or imagery and unwelcome sexual attention or
44 | advances
45 | * Trolling, insulting/derogatory comments, and personal or political attacks
46 | * Public or private harassment
47 | * Publishing others' private information, such as a physical or electronic
48 | address, without explicit permission
49 | * Other conduct which could reasonably be considered inappropriate in a
50 | professional setting
51 |
52 | ### Our Responsibilities
53 |
54 | Project maintainers are responsible for clarifying the standards of acceptable
55 | behavior and are expected to take appropriate and fair corrective action in
56 | response to any instances of unacceptable behavior.
57 |
58 | Project maintainers have the right and responsibility to remove, edit, or
59 | reject comments, commits, code, wiki edits, issues, and other contributions
60 | that are not aligned to this Code of Conduct, or to ban temporarily or
61 | permanently any contributor for other behaviors that they deem inappropriate,
62 | threatening, offensive, or harmful.
63 |
64 | ### Scope
65 |
66 | This Code of Conduct applies both within project spaces and in public spaces
67 | when an individual is representing the project or its community. Examples of
68 | representing a project or community include using an official project e-mail
69 | address, posting via an official social media account, or acting as an appointed
70 | representative at an online or offline event. Representation of a project may be
71 | further defined and clarified by project maintainers.
72 |
73 | ### Enforcement
74 |
75 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
76 | reported by contacting the project team at [INSERT EMAIL ADDRESS]. All
77 | complaints will be reviewed and investigated and will result in a response that
78 | is deemed necessary and appropriate to the circumstances. The project team is
79 | obligated to maintain confidentiality with regard to the reporter of an incident.
80 | Further details of specific enforcement policies may be posted separately.
81 |
82 | Project maintainers who do not follow or enforce the Code of Conduct in good
83 | faith may face temporary or permanent repercussions as determined by other
84 | members of the project's leadership.
85 |
86 | ### Attribution
87 |
88 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
89 | available at [http://contributor-covenant.org/version/1/4][version]
90 |
91 | [homepage]: http://contributor-covenant.org
92 | [version]: http://contributor-covenant.org/version/1/4/
93 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/git,node,linux,python,angular,mongo
3 |
4 | ### Angular ###
5 | ## Angular ##
6 | # compiled output
7 | /dist
8 | /tmp
9 | /app/**/*.js
10 | /app/**/*.js.map
11 |
12 | # dependencies
13 | /node_modules
14 | /bower_components
15 |
16 | # IDEs and editors
17 | /.idea
18 |
19 | # misc
20 | /.sass-cache
21 | /connect.lock
22 | /coverage/*
23 | /libpeerconnection.log
24 | npm-debug.log
25 | testem.log
26 | /typings
27 |
28 | # e2e
29 | /e2e/*.js
30 | /e2e/*.map
31 |
32 | #System Files
33 | .DS_Store
34 |
35 | ### Git ###
36 | *.orig
37 |
38 | ### Linux ###
39 | *~
40 |
41 | # temporary files which can be created if a process still has a handle open of a deleted file
42 | .fuse_hidden*
43 |
44 | # KDE directory preferences
45 | .directory
46 |
47 | # Linux trash folder which might appear on any partition or disk
48 | .Trash-*
49 |
50 | # .nfs files are created when an open file is removed but is still being accessed
51 | .nfs*
52 |
53 | ### Node ###
54 | # Logs
55 | logs
56 | *.log
57 | npm-debug.log*
58 | yarn-debug.log*
59 | yarn-error.log*
60 |
61 | # Runtime data
62 | pids
63 | *.pid
64 | *.seed
65 | *.pid.lock
66 |
67 | # Directory for instrumented libs generated by jscoverage/JSCover
68 | lib-cov
69 |
70 | # Coverage directory used by tools like istanbul
71 | coverage
72 |
73 | # nyc test coverage
74 | .nyc_output
75 |
76 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
77 | .grunt
78 |
79 | # Bower dependency directory (https://bower.io/)
80 | bower_components
81 |
82 | # node-waf configuration
83 | .lock-wscript
84 |
85 | # Compiled binary addons (http://nodejs.org/api/addons.html)
86 | build/Release
87 |
88 | # Dependency directories
89 | node_modules
90 | jspm_packages
91 |
92 | # Typescript v1 declaration files
93 | typings/
94 |
95 | # Optional npm cache directory
96 | .npm
97 |
98 | # Optional eslint cache
99 | .eslintcache
100 |
101 | # Optional REPL history
102 | .node_repl_history
103 |
104 | # Output of 'npm pack'
105 | *.tgz
106 |
107 | # Yarn Integrity file
108 | .yarn-integrity
109 |
110 | ### Python ###
111 | # Byte-compiled / optimized / DLL files
112 | __pycache__/
113 | *.py[cod]
114 | *$py.class
115 |
116 | # C extensions
117 | *.so
118 |
119 | # Distribution / packaging
120 | .Python
121 | build/
122 | develop-eggs/
123 | dist/
124 | downloads/
125 | eggs/
126 | .eggs/
127 | lib/
128 | lib64/
129 | parts/
130 | sdist/
131 | var/
132 | wheels/
133 | *.egg-info/
134 | .installed.cfg
135 | *.egg
136 |
137 | # PyInstaller
138 | # Usually these files are written by a python script from a template
139 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
140 | *.manifest
141 | *.spec
142 |
143 | # Installer logs
144 | pip-log.txt
145 | pip-delete-this-directory.txt
146 |
147 | # Unit test / coverage reports
148 | htmlcov/
149 | .tox/
150 | .coverage
151 | .coverage.*
152 | .cache
153 | nosetests.xml
154 | coverage.xml
155 | *.cover
156 | .hypothesis/
157 |
158 | # Translations
159 | *.mo
160 | *.pot
161 |
162 | # Django stuff:
163 | local_settings.py
164 |
165 | # Flask stuff:
166 | instance/
167 | .webassets-cache
168 |
169 | # Scrapy stuff:
170 | .scrapy
171 |
172 | # Sphinx documentation
173 | docs/_build/
174 |
175 | # PyBuilder
176 | target/
177 |
178 | # Jupyter Notebook
179 | .ipynb_checkpoints
180 |
181 | # pyenv
182 | .python-version
183 |
184 | # celery beat schedule file
185 | celerybeat-schedule
186 |
187 | # SageMath parsed files
188 | *.sage.py
189 |
190 | # Environments
191 | .venv
192 | env/
193 | venv/
194 | ENV/
195 | env.bak/
196 | venv.bak/
197 |
198 | # Spyder project settings
199 | .spyderproject
200 | .spyproject
201 |
202 | # Rope project settings
203 | .ropeproject
204 |
205 | # mkdocs documentation
206 | /site
207 |
208 | # mypy
209 | .mypy_cache/
210 |
211 | #Python virtualenv directories
212 | *.pyc
213 | dronesym-python/flask-api/bin
214 | dronesym-python/flask-api/include
215 | dronesym-python/flask-api/lib
216 | dronesym-python/flask-api/pip-selfcheck.json
217 |
218 | #Config files
219 | dronesym-frontend/src/environments/environment.ts
220 | dronesym-node/db.js
221 | dronesym-node/config/mongoconfig.js
222 | dronesym-node/dronesym-d5898-5e6e2d94c38d.json
223 | dronesym-node/example.db.js
224 | #py.test caches
225 | dronesym-python/flask-api/tests/__pycache__
226 |
227 | #.bak python
228 | *.bak
229 |
230 | # End of https://www.gitignore.io/api/git,node,linux,python,angular,firebase
231 |
--------------------------------------------------------------------------------
/dronesym-frontend/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "dronesym-frontend": {
7 | "root": "",
8 | "sourceRoot": "src",
9 | "projectType": "application",
10 | "architect": {
11 | "build": {
12 | "builder": "@angular-devkit/build-angular:browser",
13 | "options": {
14 | "outputPath": "dist",
15 | "index": "src/index.html",
16 | "main": "src/main.ts",
17 | "tsConfig": "src/tsconfig.app.json",
18 | "polyfills": "src/polyfills.ts",
19 | "assets": [
20 | "src/assets",
21 | "src/favicon.ico",
22 | "src/manifest.json"
23 | ],
24 | "styles": [
25 | "src/styles.css",
26 | "./node_modules/materialize-css/dist/css/materialize.css"
27 | ],
28 | "scripts": [
29 | "node_modules/jquery/dist/jquery.js",
30 | "node_modules/hammerjs/hammer.js",
31 | "./node_modules/materialize-css/dist/js/materialize.js"
32 | ]
33 | },
34 | "configurations": {
35 | "production": {
36 | "optimization": true,
37 | "outputHashing": "all",
38 | "sourceMap": false,
39 | "extractCss": true,
40 | "namedChunks": false,
41 | "aot": true,
42 | "extractLicenses": true,
43 | "vendorChunk": false,
44 | "buildOptimizer": true,
45 | "fileReplacements": [
46 | {
47 | "replace": "src/environments/environment.ts",
48 | "with": "src/environments/environment.prod.ts"
49 | }
50 | ],
51 | "serviceWorker": true,
52 | "ngswConfigPath": "src/ngsw-config.json"
53 | }
54 | }
55 | },
56 | "serve": {
57 | "builder": "@angular-devkit/build-angular:dev-server",
58 | "options": {
59 | "browserTarget": "dronesym-frontend:build"
60 | },
61 | "configurations": {
62 | "production": {
63 | "browserTarget": "dronesym-frontend:build:production"
64 | }
65 | }
66 | },
67 | "extract-i18n": {
68 | "builder": "@angular-devkit/build-angular:extract-i18n",
69 | "options": {
70 | "browserTarget": "dronesym-frontend:build"
71 | }
72 | },
73 | "test": {
74 | "builder": "@angular-devkit/build-angular:karma",
75 | "options": {
76 | "main": "src/test.ts",
77 | "karmaConfig": "./karma.conf.js",
78 | "polyfills": "src/polyfills.ts",
79 | "tsConfig": "src/tsconfig.spec.json",
80 | "scripts": [
81 | "node_modules/jquery/dist/jquery.js",
82 | "node_modules/hammerjs/hammer.js",
83 | "node_modules/materialize-css/dist/js/materialize.js"
84 | ],
85 | "styles": [
86 | "src/styles.css",
87 | "node_modules/materialize-css/dist/css/materialize.css"
88 | ],
89 | "assets": [
90 | "src/assets",
91 | "src/favicon.ico",
92 | "src/manifest.json"
93 | ]
94 | }
95 | },
96 | "lint": {
97 | "builder": "@angular-devkit/build-angular:tslint",
98 | "options": {
99 | "tsConfig": [
100 | "src/tsconfig.app.json",
101 | "src/tsconfig.spec.json"
102 | ],
103 | "exclude": []
104 | }
105 | }
106 | }
107 | },
108 | "dronesym-frontend-e2e": {
109 | "root": "e2e",
110 | "sourceRoot": "e2e",
111 | "projectType": "application",
112 | "architect": {
113 | "e2e": {
114 | "builder": "@angular-devkit/build-angular:protractor",
115 | "options": {
116 | "protractorConfig": "./protractor.conf.js",
117 | "devServerTarget": "dronesym-frontend:serve"
118 | }
119 | },
120 | "lint": {
121 | "builder": "@angular-devkit/build-angular:tslint",
122 | "options": {
123 | "tsConfig": [
124 | "e2e/tsconfig.e2e.json"
125 | ],
126 | "exclude": []
127 | }
128 | }
129 | }
130 | }
131 | },
132 | "defaultProject": "dronesym-frontend",
133 | "schematics": {
134 | "@schematics/angular:component": {
135 | "prefix": "app",
136 | "styleext": "css"
137 | },
138 | "@schematics/angular:directive": {
139 | "prefix": "app"
140 | }
141 | }
142 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # DroneSym
4 |
5 | [](https://travis-ci.org/scorelab/DroneSym)
6 | [](https://www.codacy.com/app/hcktheheaven/DroneSym?utm_source=github.com&utm_medium=referral&utm_content=scorelab/DroneSym&utm_campaign=Badge_Grade)
7 | [](https://gitter.im/scorelab/DroneSym)
8 |
9 | ### Setting Up the Project
10 |
11 | ### Prerequisites
12 |
13 | 1. Install Node.js 6.x (or higher)
14 | 2. Python version 2.7 (or higher)
15 | 3. MongoDB version 3.6 (or higher)
16 |
17 | ### Part 1 - Setting up the Node environment
18 |
19 | 1. After cloning the repo navigate to `dronesym-node` folder
20 | 2. Run `npm install` to pull the dependencies
21 | 3. To avoid the error : `exception in initAndListen: NonExistentPath: Data directory C:\data\db\ not found., terminating"(Windows) or "Error: dbpath (/data/db) doesn't exist"(Linux)`, create the dbpath as follows before running mongodb with replica set.
22 | **For Windows Users :**
23 | Create a folder C:\data\db
24 | **For Linux Users :**
25 | Run the following commands :
26 | To create the directory **/data/db** directory where **data** directory is in the **root** directory run:
27 | ```sh
28 | $ sudo mkdir -p /data/db
29 | ```
30 | As the owner and the group of '/data' directory are root, we need to change ownership of the directory to the current user to access it.
31 | We can change the ownership of the directory as follows:
32 | ```sh
33 | $ sudo chown -R username:group/data
34 | $ sudo chmod 0755 /data/db
35 | ```
36 | If one wants to change the MongoDB default data path i.e. "C:\data\db" (Windows) or "/data/db" (Linux) to some other directory then use:
37 | `mongod --dbpath new_dbpath`
38 | where **new_dbpath** is the path of the new data store directory. Ex : new_dbpath = /usr/local/var/mongodb-data
39 | 4. Run `mongod --replSet rs` to start running Mongo with a Replica Set.
40 | 5. Open another terminal without disturbing the terminal running mongod, then import the database with `mongorestore --db dronesym dronedb/dronesym`
41 | 6. Run `npm start` to start the Node server
42 | **Note: Make sure you have an admin account in the database under user collection. (Refer the schema in Models folder)**
43 |
44 | ### Part 2 - Setting up Python environment
45 |
46 | 1. After cloning the repo, navigate to the folder dronesym-python
47 | 2. Run `sudo pip install -r requirements.txt` to pull the dependencies
48 | 3. Navigate to `dronsym-python/flask-api/src` folder
49 | 4. Run `python main.py` to start the Flask server
50 | **Note: Node server should be running when starting up the Flask server**
51 |
52 | ### Part 3 - Setting up the Angular front-end
53 |
54 | **Make sure that you have Node6.x or higher version installed**
55 |
56 | Install AngularCLI
57 |
58 | ```sh
59 | $ npm install -g @angular/cli
60 | ```
61 |
62 | Set environmental variable in `./dronesym-frontend/src/environments/environment.ts`
63 |
64 | **_Note:_** You will have to rename the `example.environment.ts` to `environment.ts` or create new file, for example by copying the example file:
65 |
66 | ```sh
67 | $ cp src/environments/example.environment.ts src/environments/environment.ts`
68 | ```
69 | Edit the environment.ts as follows
70 | ```sh
71 | mapsApiKey: 'YOUR_GOOGLE_MAPS_API_KEY',
72 | nodeApiURL: 'http://localhost:3000/dronesym/api/node',
73 | feedURL: 'http://localhost:3000/feed'
74 | ```
75 |
76 | **Note: Dronesym Node server (`./dronesym-node/`) and DroneSym Flask server (`./dronesym-python/flask-api/src`) should be running before starting the frontend server\_**
77 |
78 | **Note: You should enable Google Maps JavaScript API before using API key**
79 |
80 | Starting the Angular development server
81 |
82 | ```sh
83 | $ npm install
84 | $ ng serve
85 | ```
86 | ### Runing tests
87 | 1.Navigate to `dronesym-node` folder and run `npm install http`
88 |
89 | 2.Then run yarn test
90 |
91 | ### Default login credentials
92 |
93 | #### Admin
94 |
95 | ```
96 | username: admin
97 | password: admin
98 | ```
99 |
100 | #### User
101 |
102 | ```
103 | username: icarus
104 | password: icarus
105 | ```
106 |
107 | ### Part 4 - Running with Docker (Optional)
108 |
109 | Checkout to docker branch
110 |
111 | ```sh
112 | $ git checkout docker
113 | ```
114 |
115 | Navigate to the root folder
116 |
117 | Run
118 |
119 | ```sh
120 | $ docker-compose up
121 | ```
122 |
123 | ### Run Unit Tests Node
124 |
125 | For node unit tests - **both the flask server and node server have to be running.**
126 |
127 | Navigate to `dronesym-node`
128 | Run `npm test`
--------------------------------------------------------------------------------