migrate to angular2
Esse commit está contido em:
@@ -0,0 +1,3 @@
|
|||||||
|
Language: JavaScript
|
||||||
|
BasedOnStyle: Google
|
||||||
|
ColumnLimit: 100
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# http://editorconfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
max_line_length = 0
|
||||||
|
trim_trailing_whitespace = false
|
||||||
+28
-3
@@ -1,3 +1,28 @@
|
|||||||
.idea
|
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||||
node_modules
|
|
||||||
npm-debug.log
|
# compiled output
|
||||||
|
/dist
|
||||||
|
/tmp
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/bower_components
|
||||||
|
|
||||||
|
# IDEs and editors
|
||||||
|
/.idea
|
||||||
|
|
||||||
|
# misc
|
||||||
|
/.sass-cache
|
||||||
|
/connect.lock
|
||||||
|
/coverage/*
|
||||||
|
/libpeerconnection.log
|
||||||
|
npm-debug.log
|
||||||
|
testem.log
|
||||||
|
/typings
|
||||||
|
|
||||||
|
# e2e
|
||||||
|
/e2e/*.js
|
||||||
|
/e2e/*.map
|
||||||
|
|
||||||
|
#System Files
|
||||||
|
.DS_Store
|
||||||
+3
-3
@@ -13,15 +13,15 @@ This project is under development, this is just a first draft.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install
|
npm install
|
||||||
node start
|
npm run visualize
|
||||||
```
|
```
|
||||||
|
|
||||||
* Go to: http://localhost:3060
|
* Go to: http://localhost:4200
|
||||||
|
|
||||||
## Simulating data
|
## Simulating data
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
node run simulate
|
npm run simulate
|
||||||
```
|
```
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|||||||
externo
+21
@@ -0,0 +1,21 @@
|
|||||||
|
/* global require, module */
|
||||||
|
|
||||||
|
var Angular2App = require('angular-cli/lib/broccoli/angular2-app');
|
||||||
|
|
||||||
|
module.exports = function(defaults) {
|
||||||
|
return new Angular2App(defaults, {
|
||||||
|
vendorNpmFiles: [
|
||||||
|
'socket.io-client/socket.io.js',
|
||||||
|
'smoothie/smoothie.js',
|
||||||
|
'chart.js/Chart.js',
|
||||||
|
'ng2-charts/bundles/ng2-charts.js',
|
||||||
|
'systemjs/dist/system-polyfills.js',
|
||||||
|
'systemjs/dist/system.src.js',
|
||||||
|
'zone.js/dist/*.js',
|
||||||
|
'es6-shim/es6-shim.js',
|
||||||
|
'reflect-metadata/*.js',
|
||||||
|
'rxjs/**/*.js',
|
||||||
|
'@angular/**/*.js'
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"project": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"name": "clitest"
|
||||||
|
},
|
||||||
|
"apps": [
|
||||||
|
{"main": "src/main.ts", "tsconfig": "src/tsconfig.json"}
|
||||||
|
],
|
||||||
|
"addons": [],
|
||||||
|
"packages": [],
|
||||||
|
"e2e": {
|
||||||
|
"protractor": {
|
||||||
|
"config": "config/protractor.conf.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"karma": {
|
||||||
|
"config": "config/karma.conf.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaults": {
|
||||||
|
"prefix": "bci",
|
||||||
|
"sourceDir": "src"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<section ng-class="{ loading: !$ctrl.data }">
|
|
||||||
<h2 class="capitalize">{{ $ctrl.type }}</h2>
|
|
||||||
<canvas id="frequency-band-{{ $ctrl.type }}"
|
|
||||||
class="chart chart-bar"
|
|
||||||
chart-data="$ctrl.data"
|
|
||||||
chart-labels="$ctrl.channels"
|
|
||||||
chart-series="$ctrl.channels"
|
|
||||||
chart-options="$ctrl.options"
|
|
||||||
chart-colours="$ctrl.colors">
|
|
||||||
</canvas>
|
|
||||||
</section>
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
|
|
||||||
(function () {
|
|
||||||
|
|
||||||
var BCIFrequencyBand = {
|
|
||||||
templateUrl: 'components/frequency-band/frequency-band.html',
|
|
||||||
bindings: {
|
|
||||||
type: '@',
|
|
||||||
color: '@',
|
|
||||||
eventName: '@'
|
|
||||||
},
|
|
||||||
controller: function ($timeout) {
|
|
||||||
|
|
||||||
var $ctrl = this;
|
|
||||||
|
|
||||||
var socket = io();
|
|
||||||
|
|
||||||
$ctrl.colors = [
|
|
||||||
{ fillColor: 'rgba(112,185,252,1)' },
|
|
||||||
{ fillColor: 'rgba(116,150,161,1)' },
|
|
||||||
{ fillColor: 'rgba(162,86,178,1)' },
|
|
||||||
{ fillColor: 'rgba(144,132,246,1)' },
|
|
||||||
{ fillColor: 'rgba(138,219,229,1)' },
|
|
||||||
{ fillColor: 'rgba(232,223,133,1)' },
|
|
||||||
{ fillColor: 'rgba(148,159,177,1)' },
|
|
||||||
{ fillColor: 'rgba(182,224,53,1)' }
|
|
||||||
].filter(function (color, index) {
|
|
||||||
return parseInt($ctrl.color) === index;
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log($ctrl.type, $ctrl.colors);
|
|
||||||
|
|
||||||
$ctrl.channels = ['CH1','CH2','CH3','CH4','CH5','CH6','CH7','CH8'];
|
|
||||||
|
|
||||||
$ctrl.options = {
|
|
||||||
responsive: true,
|
|
||||||
animation: true,
|
|
||||||
animationSteps: 15
|
|
||||||
};
|
|
||||||
|
|
||||||
socket.on($ctrl.eventName, function (data) {
|
|
||||||
$timeout(function () {
|
|
||||||
$ctrl.labels = data.labels;
|
|
||||||
$ctrl.data = data[$ctrl.type || 'data'];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$ctrl.$onDestroy = function () {
|
|
||||||
socket.removeListener($ctrl.eventName);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
angular
|
|
||||||
.module('bciDashboard')
|
|
||||||
.component('bciFrequencyBand', BCIFrequencyBand);
|
|
||||||
|
|
||||||
})();
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<section ng-class="{ loading: !$ctrl.data }">
|
|
||||||
<h2>Frequency <span class="frequency-type">{{ $ctrl.type }}</span></h2>
|
|
||||||
<canvas id="frequency"
|
|
||||||
class="chart-base"
|
|
||||||
chart-type="$ctrl.type"
|
|
||||||
chart-data="$ctrl.data"
|
|
||||||
chart-labels="$ctrl.labels"
|
|
||||||
chart-series="$ctrl.channels"
|
|
||||||
chart-options="$ctrl.options"
|
|
||||||
chart-colours="$ctrl.colors">
|
|
||||||
</canvas>
|
|
||||||
</section>
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
|
|
||||||
(function () {
|
|
||||||
|
|
||||||
var BCIFrequency = {
|
|
||||||
templateUrl: 'components/frequency/frequency.html',
|
|
||||||
bindings: {
|
|
||||||
type: '@',
|
|
||||||
eventName: '@'
|
|
||||||
},
|
|
||||||
controller: function ($timeout) {
|
|
||||||
|
|
||||||
var $ctrl = this;
|
|
||||||
|
|
||||||
// Default chart type as fallback
|
|
||||||
$ctrl.type = $ctrl.type || 'Line';
|
|
||||||
|
|
||||||
var socket = io();
|
|
||||||
|
|
||||||
$ctrl.colors = [
|
|
||||||
{ strokeColor: 'rgba(112,185,252,1)' },
|
|
||||||
{ strokeColor: 'rgba(116,150,161,1)' },
|
|
||||||
{ strokeColor: 'rgba(162,86,178,1)' },
|
|
||||||
{ strokeColor: 'rgba(144,132,246,1)' },
|
|
||||||
{ strokeColor: 'rgba(138,219,229,1)' },
|
|
||||||
{ strokeColor: 'rgba(232,223,133,1)' },
|
|
||||||
{ strokeColor: 'rgba(148,159,177,1)' },
|
|
||||||
{ strokeColor: 'rgba(182,224,53,1)' }
|
|
||||||
];
|
|
||||||
|
|
||||||
$ctrl.options = {
|
|
||||||
responsive: true,
|
|
||||||
animation: true,
|
|
||||||
animationSteps: 15
|
|
||||||
};
|
|
||||||
|
|
||||||
$ctrl.series = ['CH1','CH2','CH3','CH4','CH5','CH6','CH7','CH8'];
|
|
||||||
|
|
||||||
socket.on($ctrl.eventName, function (data) {
|
|
||||||
$timeout(function () {
|
|
||||||
$ctrl.data = data.data;
|
|
||||||
$ctrl.labels = data.labels;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$ctrl.$onDestroy = function () {
|
|
||||||
socket.removeListener($ctrl.eventName);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
angular
|
|
||||||
.module('bciDashboard')
|
|
||||||
.component('bciFrequency', BCIFrequency);
|
|
||||||
|
|
||||||
})();
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
|
|
||||||
(function () {
|
|
||||||
|
|
||||||
angular
|
|
||||||
.module('bciDashboard')
|
|
||||||
.directive('bciTimeSeries', bciTimeSeries);
|
|
||||||
|
|
||||||
function bciTimeSeries() {
|
|
||||||
|
|
||||||
var timeSeries = new SmoothieChart({
|
|
||||||
millisPerLine: 3000,
|
|
||||||
grid: {
|
|
||||||
fillStyle: '#333333',
|
|
||||||
strokeStyle: 'rgba(0,0,0,0.1)',
|
|
||||||
sharpLines: false,
|
|
||||||
verticalSections: 8,
|
|
||||||
borderVisible: true
|
|
||||||
},
|
|
||||||
labels: {
|
|
||||||
disabled: true
|
|
||||||
},
|
|
||||||
maxValue: 8 * 2,
|
|
||||||
minValue: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
templateUrl: 'components/time-series/time-series.html',
|
|
||||||
scope: {
|
|
||||||
eventName: '@'
|
|
||||||
},
|
|
||||||
bindToController: true,
|
|
||||||
controllerAs: '$ctrl',
|
|
||||||
controller: function ($timeout) {
|
|
||||||
|
|
||||||
var $ctrl = this;
|
|
||||||
|
|
||||||
var socket = io();
|
|
||||||
|
|
||||||
$ctrl.colors = [
|
|
||||||
{ strokeColor: 'rgba(112,185,252,1)' },
|
|
||||||
{ strokeColor: 'rgba(116,150,161,1)' },
|
|
||||||
{ strokeColor: 'rgba(162,86,178,1)' },
|
|
||||||
{ strokeColor: 'rgba(144,132,246,1)' },
|
|
||||||
{ strokeColor: 'rgba(138,219,229,1)' },
|
|
||||||
{ strokeColor: 'rgba(232,223,133,1)' },
|
|
||||||
{ strokeColor: 'rgba(148,159,177,1)' },
|
|
||||||
{ strokeColor: 'rgba(182,224,53,1)' }
|
|
||||||
];
|
|
||||||
|
|
||||||
$ctrl.channels = ['CH1','CH2','CH3','CH4','CH5','CH6','CH7','CH8'];
|
|
||||||
|
|
||||||
// Construct time series array with 8 lines
|
|
||||||
var lines = Array(8).fill().map(function () {
|
|
||||||
return new TimeSeries();
|
|
||||||
});
|
|
||||||
|
|
||||||
lines.forEach(function (line, index) {
|
|
||||||
timeSeries.addTimeSeries(line, { strokeStyle: $ctrl.colors[index].strokeColor });
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on($ctrl.eventName, function (data) {
|
|
||||||
|
|
||||||
$timeout(function () {
|
|
||||||
$ctrl.amplitudes = data.amplitudes;
|
|
||||||
$ctrl.timeline = data.timeline;
|
|
||||||
});
|
|
||||||
|
|
||||||
lines.forEach(function (line, index) {
|
|
||||||
data.data[index].forEach(function (amplitude) {
|
|
||||||
line.append(new Date().getTime(), amplitude);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
$ctrl.$onDestroy = function () {
|
|
||||||
socket.removeListener($ctrl.eventName);
|
|
||||||
};
|
|
||||||
|
|
||||||
},
|
|
||||||
link: function (scope, element) {
|
|
||||||
// 200 = 50 samples * 4 milliseconds (sample rate)
|
|
||||||
timeSeries.streamTo(element[0].querySelector('canvas'), 40);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
})();
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.topoplot-wrapper {
|
|
||||||
width: 300px;
|
|
||||||
height: 300px;
|
|
||||||
border: 3px solid black;
|
|
||||||
border-radius: 50%;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
[class*='topoplot-c'] {
|
|
||||||
z-index: 2;
|
|
||||||
background-color: black;
|
|
||||||
border: 1px solid black;
|
|
||||||
width: 5%;
|
|
||||||
height: 5%;
|
|
||||||
border-radius: 50%;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top {
|
|
||||||
top: 2%
|
|
||||||
}
|
|
||||||
.middle {
|
|
||||||
top: calc(50% - 50px);
|
|
||||||
}
|
|
||||||
.third {
|
|
||||||
top: 70%;
|
|
||||||
}
|
|
||||||
.bottom {
|
|
||||||
bottom: 2%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top.left,
|
|
||||||
.bottom.left {
|
|
||||||
left: 32%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top.right,
|
|
||||||
.bottom.right {
|
|
||||||
right: 32%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.middle.left {
|
|
||||||
left: 25%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.middle.right {
|
|
||||||
right: 25%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.third.left {
|
|
||||||
left: 8%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.third.right {
|
|
||||||
right: 8%;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Grid */
|
|
||||||
|
|
||||||
.topoplot-grid {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 1;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
-webkit-clip-path: circle(50%);
|
|
||||||
border-radius: 50%;
|
|
||||||
-webkit-filter: blur(10px);
|
|
||||||
}
|
|
||||||
|
|
||||||
[class*='topoplot-u'] {
|
|
||||||
float: left;
|
|
||||||
/*border: 1px solid lightgray;*/
|
|
||||||
width: 9.09%;
|
|
||||||
height: 9.09%;
|
|
||||||
/*background: lightblue;*/
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<section ng-class="{ loading: !$ctrl.grid }">
|
|
||||||
<h2>Topo</h2>
|
|
||||||
<section class="topoplot-wrapper">
|
|
||||||
|
|
||||||
<article class="topoplot-c1 top left"></article>
|
|
||||||
<article class="topoplot-c2 top right"></article>
|
|
||||||
<article class="topoplot-c3 middle left"></article>
|
|
||||||
<article class="topoplot-c4 middle right"></article>
|
|
||||||
<article class="topoplot-c5 third left"></article>
|
|
||||||
<article class="topoplot-c6 third right"></article>
|
|
||||||
<article class="topoplot-c7 bottom left"></article>
|
|
||||||
<article class="topoplot-c8 bottom right"></article>
|
|
||||||
|
|
||||||
<aside class="topoplot-grid">
|
|
||||||
<div ng-repeat="pixel in $ctrl.grid track by $index" ng-style="$ctrl.getColor($index,pixel,$ctrl.grid)" ng-class="$ctrl.getClass($index)"></div>
|
|
||||||
</aside>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
</section>
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
|
|
||||||
(function () {
|
|
||||||
|
|
||||||
var BCITopo = {
|
|
||||||
templateUrl: 'components/topo/topo.html',
|
|
||||||
bindings: {
|
|
||||||
eventName: '@'
|
|
||||||
},
|
|
||||||
controller: function ($timeout) {
|
|
||||||
|
|
||||||
var $ctrl = this;
|
|
||||||
|
|
||||||
var socket = io();
|
|
||||||
|
|
||||||
$ctrl.getClass = function(index){
|
|
||||||
return 'topoplot-u' + index
|
|
||||||
};
|
|
||||||
|
|
||||||
$ctrl.getColor = function(index,pixel,grid){
|
|
||||||
var min = Math.min.apply(Math,grid);
|
|
||||||
var max = Math.max.apply(Math,grid);
|
|
||||||
var f = chroma.scale('Spectral').domain([min,max]);
|
|
||||||
return {'background-color': f(pixel)}
|
|
||||||
};
|
|
||||||
|
|
||||||
socket.on($ctrl.eventName, function (data) {
|
|
||||||
$timeout(function () {
|
|
||||||
$ctrl.grid = data.data;
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$ctrl.$onDestroy = function () {
|
|
||||||
socket.removeListener($ctrl.eventName);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
angular
|
|
||||||
.module('bciDashboard')
|
|
||||||
.component('bciTopo', BCITopo);
|
|
||||||
|
|
||||||
})();
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html ng-app="bciDashboard">
|
|
||||||
<head>
|
|
||||||
<title>BCI Dashboard</title>
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="node_modules/angular-chart.js/dist/angular-chart.css">
|
|
||||||
<link rel="stylesheet" href="visualizer.css">
|
|
||||||
<link rel="stylesheet" href="components/topo/topo.css">
|
|
||||||
|
|
||||||
<script src="node_modules/socket.io-client/socket.io.js"></script>
|
|
||||||
<script src="node_modules/smoothie/smoothie.js"></script>
|
|
||||||
|
|
||||||
<script src="node_modules/angular/angular.js"></script>
|
|
||||||
<script src="node_modules/chart.js/Chart.js"></script>
|
|
||||||
<script src="node_modules/angular-chart.js/dist/angular-chart.js"></script>
|
|
||||||
|
|
||||||
<script src="visualizer.js"></script>
|
|
||||||
|
|
||||||
<script src="lib/chroma.min.js"></script>
|
|
||||||
|
|
||||||
<script src="components/frequency/frequency.js"></script>
|
|
||||||
<script src="components/frequency-band/frequency-band.js"></script>
|
|
||||||
<script src="components/time-series/time-series.js"></script>
|
|
||||||
<script src="components/topo/topo.js"></script>
|
|
||||||
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
|
|
||||||
<h1>BCI Dashboard</h1>
|
|
||||||
|
|
||||||
<nav ng-init="mode = 'timeSeries'">
|
|
||||||
<button ng-click="mode = 'timeSeries'">Time Series</button>
|
|
||||||
<button ng-click="mode = 'frequency'">Frequency</button>
|
|
||||||
<button ng-click="mode = 'frequencyRadar'">Frequency Radar</button>
|
|
||||||
<button ng-click="mode = 'frequencyBands'">Frequency Bands</button>
|
|
||||||
<button ng-click="mode = 'topo'">Topo</button>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<section class="row" ng-if="mode == 'timeSeries'">
|
|
||||||
<bci-time-series event-name="bci:time" class="block block-75"></bci-time-series>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="row" ng-if="mode == 'frequency'">
|
|
||||||
<bci-frequency type="Line" event-name="bci:fft" class="block block-75"></bci-frequency>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="row" ng-if="mode == 'frequencyRadar'">
|
|
||||||
<bci-frequency type="Radar" event-name="bci:fft" class="block block-75"></bci-frequency>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="row" ng-if="mode == 'frequencyBands'">
|
|
||||||
<bci-frequency-band event-name="bci:fft" type="delta" color="1" class="block block-33"></bci-frequency-band>
|
|
||||||
<bci-frequency-band event-name="bci:fft" type="theta" color="2" class="block block-33"></bci-frequency-band>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="row" ng-if="mode == 'frequencyBands'">
|
|
||||||
<bci-frequency-band event-name="bci:fft" type="alpha" color="3" class="block block-33"></bci-frequency-band>
|
|
||||||
<bci-frequency-band event-name="bci:fft" type="beta" color="4" class="block block-33"></bci-frequency-band>
|
|
||||||
<bci-frequency-band event-name="bci:fft" type="gamma" color="5" class="block block-33"></bci-frequency-band>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="row" ng-if="mode == 'topo'">
|
|
||||||
<bci-topo event-name="bci:topo" class="block block-50"></bci-topo>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</main>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
externo
-33
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
@@ -1,28 +0,0 @@
|
|||||||
|
|
||||||
(function () {
|
|
||||||
|
|
||||||
angular
|
|
||||||
.module('bciDashboard', ['chart.js'])
|
|
||||||
.config(bciDashboardConfig);
|
|
||||||
|
|
||||||
function bciDashboardConfig (ChartJsProvider) {
|
|
||||||
ChartJsProvider.setOptions({
|
|
||||||
animation: false,
|
|
||||||
responsive: true,
|
|
||||||
datasetStrokeWidth: 1,
|
|
||||||
pointDot: false,
|
|
||||||
pointDotRadius: 1,
|
|
||||||
pointDotStrokeWidth: 0,
|
|
||||||
datasetFill: false,
|
|
||||||
scaleOverride: true,
|
|
||||||
scaleStartValue: -2,
|
|
||||||
scaleStepWidth: 1,
|
|
||||||
scaleSteps: 6,
|
|
||||||
barShowStroke: false,
|
|
||||||
barValueSpacing: 1,
|
|
||||||
barStrokeWidth: 1,
|
|
||||||
strokeColor: 'rgba(116,150,161,1)'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
})();
|
|
||||||
Arquivo binário não exibido.
|
Antes Largura: | Altura: | Tamanho: 356 KiB |
Arquivo binário não exibido.
|
Antes Largura: | Altura: | Tamanho: 3.6 MiB |
Arquivo binário não exibido.
|
Antes Largura: | Altura: | Tamanho: 1.9 MiB |
Arquivo binário não exibido.
|
Antes Largura: | Altura: | Tamanho: 3.6 MiB |
@@ -0,0 +1,3 @@
|
|||||||
|
export const environment = {
|
||||||
|
production: false
|
||||||
|
};
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
/* jshint node: true */
|
||||||
|
|
||||||
|
module.exports = function(environment) {
|
||||||
|
return {
|
||||||
|
environment: environment,
|
||||||
|
baseURL: '/',
|
||||||
|
locationType: 'auto'
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export const environment = {
|
||||||
|
production: true
|
||||||
|
};
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/*global jasmine, __karma__, window*/
|
||||||
|
Error.stackTraceLimit = Infinity;
|
||||||
|
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
|
||||||
|
|
||||||
|
__karma__.loaded = function () {
|
||||||
|
};
|
||||||
|
|
||||||
|
var distPath = '/base/dist/';
|
||||||
|
var appPath = distPath + 'app/';
|
||||||
|
|
||||||
|
function isJsFile(path) {
|
||||||
|
return path.slice(-3) == '.js';
|
||||||
|
}
|
||||||
|
|
||||||
|
function isSpecFile(path) {
|
||||||
|
return path.slice(-8) == '.spec.js';
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAppFile(path) {
|
||||||
|
return isJsFile(path) && (path.substr(0, appPath.length) == appPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
var allSpecFiles = Object.keys(window.__karma__.files)
|
||||||
|
.filter(isSpecFile)
|
||||||
|
.filter(isAppFile);
|
||||||
|
|
||||||
|
// Load our SystemJS configuration.
|
||||||
|
System.config({
|
||||||
|
baseURL: distPath
|
||||||
|
});
|
||||||
|
|
||||||
|
System.import('system-config.js').then(function() {
|
||||||
|
// Load and configure the TestComponentBuilder.
|
||||||
|
return Promise.all([
|
||||||
|
System.import('@angular/core/testing'),
|
||||||
|
System.import('@angular/platform-browser-dynamic/testing')
|
||||||
|
]).then(function (providers) {
|
||||||
|
var testing = providers[0];
|
||||||
|
var testingBrowser = providers[1];
|
||||||
|
|
||||||
|
testing.setBaseTestProviders(testingBrowser.TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS,
|
||||||
|
testingBrowser.TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS);
|
||||||
|
});
|
||||||
|
}).then(function() {
|
||||||
|
// Finally, load all spec files.
|
||||||
|
// This will run the tests directly.
|
||||||
|
return Promise.all(
|
||||||
|
allSpecFiles.map(function (moduleName) {
|
||||||
|
return System.import(moduleName);
|
||||||
|
}));
|
||||||
|
}).then(__karma__.start, __karma__.error);
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
module.exports = function (config) {
|
||||||
|
config.set({
|
||||||
|
basePath: '..',
|
||||||
|
frameworks: ['jasmine'],
|
||||||
|
plugins: [
|
||||||
|
require('karma-jasmine'),
|
||||||
|
require('karma-chrome-launcher')
|
||||||
|
],
|
||||||
|
customLaunchers: {
|
||||||
|
// chrome setup for travis CI using chromium
|
||||||
|
Chrome_travis_ci: {
|
||||||
|
base: 'Chrome',
|
||||||
|
flags: ['--no-sandbox']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
files: [
|
||||||
|
{ pattern: 'dist/vendor/es6-shim/es6-shim.js', included: true, watched: false },
|
||||||
|
{ pattern: 'dist/vendor/zone.js/dist/zone.js', included: true, watched: false },
|
||||||
|
{ pattern: 'dist/vendor/reflect-metadata/Reflect.js', included: true, watched: false },
|
||||||
|
{ pattern: 'dist/vendor/systemjs/dist/system-polyfills.js', included: true, watched: false },
|
||||||
|
{ pattern: 'dist/vendor/systemjs/dist/system.src.js', included: true, watched: false },
|
||||||
|
{ pattern: 'dist/vendor/zone.js/dist/async-test.js', included: true, watched: false },
|
||||||
|
|
||||||
|
{ pattern: 'config/karma-test-shim.js', included: true, watched: true },
|
||||||
|
|
||||||
|
// Distribution folder.
|
||||||
|
{ pattern: 'dist/**/*', included: false, watched: true }
|
||||||
|
],
|
||||||
|
exclude: [
|
||||||
|
// Vendor packages might include spec files. We don't want to use those.
|
||||||
|
'dist/vendor/**/*.spec.js'
|
||||||
|
],
|
||||||
|
preprocessors: {},
|
||||||
|
reporters: ['progress'],
|
||||||
|
port: 9876,
|
||||||
|
colors: true,
|
||||||
|
logLevel: config.LOG_INFO,
|
||||||
|
autoWatch: true,
|
||||||
|
browsers: ['Chrome'],
|
||||||
|
singleRun: false
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*global jasmine */
|
||||||
|
var SpecReporter = require('jasmine-spec-reporter');
|
||||||
|
|
||||||
|
exports.config = {
|
||||||
|
allScriptsTimeout: 11000,
|
||||||
|
specs: [
|
||||||
|
'../e2e/**/*.e2e.ts'
|
||||||
|
],
|
||||||
|
capabilities: {
|
||||||
|
'browserName': 'chrome'
|
||||||
|
},
|
||||||
|
directConnect: true,
|
||||||
|
baseUrl: 'http://localhost:4200/',
|
||||||
|
framework: 'jasmine',
|
||||||
|
jasmineNodeOpts: {
|
||||||
|
showColors: true,
|
||||||
|
defaultTimeoutInterval: 30000,
|
||||||
|
print: function() {}
|
||||||
|
},
|
||||||
|
useAllAngular2AppRoots: true,
|
||||||
|
beforeLaunch: function() {
|
||||||
|
require('ts-node').register({
|
||||||
|
project: 'e2e'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onPrepare: function() {
|
||||||
|
jasmine.getEnv().addReporter(new SpecReporter());
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { ClitestPage } from './app.po';
|
||||||
|
|
||||||
|
describe('clitest App', function() {
|
||||||
|
let page: ClitestPage;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
page = new ClitestPage();
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should display message saying app works', () => {
|
||||||
|
page.navigateTo();
|
||||||
|
expect(page.getParagraphText()).toEqual('clitest works!');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
export class ClitestPage {
|
||||||
|
navigateTo() {
|
||||||
|
return browser.get('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
getParagraphText() {
|
||||||
|
return element(by.css('clitest-app h1')).getText();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"compileOnSave": false,
|
||||||
|
"compilerOptions": {
|
||||||
|
"declaration": false,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"mapRoot": "",
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"noEmitOnError": true,
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"rootDir": ".",
|
||||||
|
"sourceMap": true,
|
||||||
|
"sourceRoot": "/",
|
||||||
|
"target": "es5"
|
||||||
|
}
|
||||||
|
}
|
||||||
externo
+1
@@ -0,0 +1 @@
|
|||||||
|
/// <reference path="../typings/main.d.ts" />
|
||||||
+44
-27
@@ -1,42 +1,59 @@
|
|||||||
{
|
{
|
||||||
"name": "openbci-dashboard",
|
"name": "clitest",
|
||||||
"private": false,
|
"version": "0.0.0",
|
||||||
"version": "0.0.1",
|
|
||||||
"description": "A fullstack javascript app for capturing and visualizing OpenBCI EEG data",
|
|
||||||
"main": "visualizer.js",
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"keywords": [
|
"angular-cli": {},
|
||||||
"openbci",
|
|
||||||
"eeg",
|
|
||||||
"neurojs",
|
|
||||||
"dashboard"
|
|
||||||
],
|
|
||||||
"author": "Alex Castillo",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node visualizer",
|
"start": "ng server",
|
||||||
"simulate": "node visualizer simulate"
|
"postinstall": "typings install",
|
||||||
},
|
"lint": "tslint \"src/**/*.ts\"",
|
||||||
"repository": {
|
"format": "clang-format -i -style=file --glob=src/**/*.ts",
|
||||||
"type": "git",
|
"visualize": "concurrently \"ng serve\" \"node visualizer\" ",
|
||||||
"url": "git@github.com:NeuroJS/openbci-dashboard.git"
|
"simulate": "concurrently \"ng serve\" \"node visualizer simulate\" ",
|
||||||
},
|
"pree2e": "webdriver-manager update",
|
||||||
"bugs": {
|
"e2e": "protractor"
|
||||||
"url": "https://github.com/NeuroJS/openbci-dashboard/issues"
|
|
||||||
},
|
},
|
||||||
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"angular": "^1.5.5",
|
"@angular/common": "2.0.0-rc.1",
|
||||||
"angular-chart.js": "^0.10.2",
|
"@angular/compiler": "2.0.0-rc.1",
|
||||||
"chart.js": "^1.1.1",
|
"@angular/core": "2.0.0-rc.1",
|
||||||
|
"@angular/platform-browser": "2.0.0-rc.1",
|
||||||
|
"@angular/platform-browser-dynamic": "2.0.0-rc.1",
|
||||||
|
"@angular/router": "2.0.0-rc.1",
|
||||||
|
"chart.js": "^1.0.2",
|
||||||
"dsp.js": "neurojs/dsp.js",
|
"dsp.js": "neurojs/dsp.js",
|
||||||
|
"es6-shim": "^0.35.0",
|
||||||
"express": "^4.13.4",
|
"express": "^4.13.4",
|
||||||
"fili": "^1.2.1",
|
"fili": "^1.2.1",
|
||||||
"jstat": "^1.5.2",
|
"jstat": "^1.5.2",
|
||||||
|
"ng2-charts": "^1.0.3",
|
||||||
"nodemon": "^1.9.1",
|
"nodemon": "^1.9.1",
|
||||||
"openbci-sdk": "^0.3.4",
|
"openbci-sdk": "^0.3.4",
|
||||||
|
"reflect-metadata": "0.1.3",
|
||||||
|
"rxjs": "5.0.0-beta.6",
|
||||||
"smoothie": "^1.27.0",
|
"smoothie": "^1.27.0",
|
||||||
"socket.io": "^1.4.5",
|
"socket.io": "^1.4.6",
|
||||||
"socket.io-client": "^1.4.5",
|
"socket.io-client": "^1.4.6",
|
||||||
|
"systemjs": "0.19.26",
|
||||||
"topogrid": "^1.0.6",
|
"topogrid": "^1.0.6",
|
||||||
"yargs": "^4.3.2"
|
"yargs": "^4.3.2",
|
||||||
|
"zone.js": "^0.6.12"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"angular-cli": "0.0.*",
|
||||||
|
"clang-format": "^1.0.35",
|
||||||
|
"codelyzer": "0.0.14",
|
||||||
|
"ember-cli-inject-live-reload": "^1.4.0",
|
||||||
|
"jasmine-core": "^2.4.1",
|
||||||
|
"jasmine-spec-reporter": "^2.4.0",
|
||||||
|
"karma": "^0.13.15",
|
||||||
|
"karma-chrome-launcher": "^0.2.3",
|
||||||
|
"karma-jasmine": "^0.3.8",
|
||||||
|
"protractor": "^3.3.0",
|
||||||
|
"ts-node": "^0.5.5",
|
||||||
|
"tslint": "^3.6.0",
|
||||||
|
"typescript": "^1.8.10",
|
||||||
|
"typings": "^0.8.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
|
|
||||||
@import url(https://fonts.googleapis.com/css?family=Roboto:400,700,300);
|
@import url(https://fonts.googleapis.com/css?family=Roboto:400,700,300);
|
||||||
|
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
:host {
|
||||||
font-family: 'Roboto', sans-serif;
|
display: block;
|
||||||
font-weight: 300;
|
height: 100vh;
|
||||||
color: #ffffff;
|
|
||||||
background-color: #222222;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
margin: 20px;
|
padding: 20px;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
|
margin: 0;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.capitalize {
|
.capitalize {
|
||||||
@@ -32,57 +31,6 @@ h1 {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav {
|
|
||||||
margin-left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
button {
|
|
||||||
appearance: none;
|
|
||||||
-webkit-appearance: none;
|
|
||||||
border: 0;
|
|
||||||
background: transparent;
|
|
||||||
color: #ffffff;
|
|
||||||
padding: 8px 20px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-family: 'Roboto', sans-serif;
|
|
||||||
font-weight: 300;
|
|
||||||
font-size: 12px;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
margin-right: 2px;
|
|
||||||
cursor: pointer;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav button:nth-child(1) {
|
|
||||||
background-color: rgba(112,185,252,1);
|
|
||||||
}
|
|
||||||
|
|
||||||
nav button:nth-child(2) {
|
|
||||||
background-color: rgba(138,219,229,1);
|
|
||||||
}
|
|
||||||
|
|
||||||
nav button:nth-child(3) {
|
|
||||||
background-color: rgba(162,86,178,1);
|
|
||||||
}
|
|
||||||
|
|
||||||
nav button:nth-child(4) {
|
|
||||||
background-color: rgba(144,132,246,1);
|
|
||||||
}
|
|
||||||
|
|
||||||
nav button:nth-child(5) {
|
|
||||||
background-color: rgba(232,223,133,1);
|
|
||||||
color: #000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav button:nth-child(6) {
|
|
||||||
background-color: rgba(148,159,177,1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
@@ -131,64 +79,55 @@ nav button:nth-child(6) {
|
|||||||
min-height: 580px;
|
min-height: 580px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.time-series canvas {
|
|
||||||
margin: 40px 80px 40px 40px;
|
nav {
|
||||||
|
margin-left: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.time-series-channels,
|
|
||||||
.time-series-amplitudes {
|
nav a {
|
||||||
position: absolute;
|
appearance: none;
|
||||||
top: 60px;
|
-webkit-appearance: none;
|
||||||
height: 450px;
|
border: 0;
|
||||||
color: rgba(102,102,102,1);
|
background: transparent;
|
||||||
|
color: #ffffff;
|
||||||
|
padding: 8px 20px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
font-weight: 300;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
margin-right: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.time-series-channels {
|
nav a:hover {
|
||||||
width: 40px;
|
opacity: 0.8;
|
||||||
left: 20px;
|
}
|
||||||
text-align: left;
|
|
||||||
|
nav a:nth-child(1) {
|
||||||
|
background-color: rgba(112,185,252,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a:nth-child(2) {
|
||||||
|
background-color: rgba(138,219,229,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a:nth-child(3) {
|
||||||
|
background-color: rgba(162,86,178,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a:nth-child(4) {
|
||||||
|
background-color: rgba(144,132,246,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a:nth-child(5) {
|
||||||
|
background-color: rgba(232,223,133,1);
|
||||||
color: #000000;
|
color: #000000;
|
||||||
font-weight: 500;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.time-series-amplitudes {
|
nav a:nth-child(6) {
|
||||||
right: 20px;
|
background-color: rgba(148,159,177,1);
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.time-series-channels ul {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.time-series-channels li {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
padding-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.time-series-channels ul,
|
|
||||||
.time-series-amplitudes ul {
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-end;
|
|
||||||
justify-content: space-around;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100%;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.time-series-duration {
|
|
||||||
position: absolute;
|
|
||||||
left: 50px;
|
|
||||||
width: 98%;
|
|
||||||
bottom: 22px;
|
|
||||||
display: flex;
|
|
||||||
color: rgba(102,102,102,1);
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.time-series-duration time {
|
|
||||||
width: 20%;
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<main>
|
||||||
|
<h1>{{title}}</h1>
|
||||||
|
<nav>
|
||||||
|
<a [routerLink]="['/time-series']">Time Series</a>
|
||||||
|
<a [routerLink]="['/frequency']">Frequency</a>
|
||||||
|
<a ng-click="">Frequency Radar</a>
|
||||||
|
<a ng-click="">Frequency Bands</a>
|
||||||
|
<a ng-click="">Topo</a>
|
||||||
|
</nav>
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
</main>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import {
|
||||||
|
beforeEachProviders,
|
||||||
|
describe,
|
||||||
|
expect,
|
||||||
|
it,
|
||||||
|
inject
|
||||||
|
} from '@angular/core/testing';
|
||||||
|
import { DashboardComponent } from '../app/dashboard.component';
|
||||||
|
|
||||||
|
beforeEachProviders(() => [DashboardComponent]);
|
||||||
|
|
||||||
|
describe('App: DashboardComponent', () => {
|
||||||
|
it('should create the app',
|
||||||
|
inject([DashboardComponent], (app: DashboardComponent) => {
|
||||||
|
expect(app).toBeTruthy();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should have as title \'clitest works!\'',
|
||||||
|
inject([DashboardComponent], (app: DashboardComponent) => {
|
||||||
|
expect(app.title).toEqual('clitest works!');
|
||||||
|
}));
|
||||||
|
});
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { TimeSeriesComponent } from './time-series';
|
||||||
|
import { FrequencyComponent } from './frequency';
|
||||||
|
import { Routes, Router, ROUTER_PROVIDERS, ROUTER_DIRECTIVES } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
moduleId: module.id,
|
||||||
|
selector: 'bci-dashboard',
|
||||||
|
templateUrl: 'dashboard.component.html',
|
||||||
|
styleUrls: ['dashboard.component.css'],
|
||||||
|
directives: [ROUTER_DIRECTIVES],
|
||||||
|
providers: [ROUTER_PROVIDERS]
|
||||||
|
})
|
||||||
|
|
||||||
|
@Routes([
|
||||||
|
{ path: '/time-series', component: TimeSeriesComponent },
|
||||||
|
{ path: '/frequency', component: FrequencyComponent }
|
||||||
|
])
|
||||||
|
|
||||||
|
export class DashboardComponent implements OnInit {
|
||||||
|
title = 'BCI Dashboard';
|
||||||
|
|
||||||
|
constructor (private router: Router) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit () {
|
||||||
|
this.router.navigate(['/time-series']);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// The file for the current environment will overwrite this one during build
|
||||||
|
// Different environments can be found in config/environment.{dev|prod}.ts
|
||||||
|
// The build system defaults to the dev environment
|
||||||
|
|
||||||
|
export const environment = {
|
||||||
|
production: false
|
||||||
|
};
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
:host,
|
||||||
|
.chart {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<base-chart class="chart"
|
||||||
|
[data]="chartData"
|
||||||
|
[labels]="chartLabels"
|
||||||
|
[options]="chartOptions"
|
||||||
|
[colours]="chartColors"
|
||||||
|
[series]="chartSeries"
|
||||||
|
[chartType]="chartType">
|
||||||
|
</base-chart>
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import {
|
||||||
|
beforeEach,
|
||||||
|
beforeEachProviders,
|
||||||
|
describe,
|
||||||
|
expect,
|
||||||
|
it,
|
||||||
|
inject,
|
||||||
|
} from '@angular/core/testing';
|
||||||
|
import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing';
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { FrequencyComponent } from './frequency.component';
|
||||||
|
|
||||||
|
describe('Component: Frequency', () => {
|
||||||
|
let builder: TestComponentBuilder;
|
||||||
|
|
||||||
|
beforeEachProviders(() => [FrequencyComponent]);
|
||||||
|
beforeEach(inject([TestComponentBuilder], function (tcb: TestComponentBuilder) {
|
||||||
|
builder = tcb;
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should inject the component', inject([FrequencyComponent],
|
||||||
|
(component: FrequencyComponent) => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should create the component', inject([], () => {
|
||||||
|
return builder.createAsync(FrequencyComponentTestController)
|
||||||
|
.then((fixture: ComponentFixture<any>) => {
|
||||||
|
let query = fixture.debugElement.query(By.directive(FrequencyComponent));
|
||||||
|
expect(query).toBeTruthy();
|
||||||
|
expect(query.componentInstance).toBeTruthy();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'test',
|
||||||
|
template: `
|
||||||
|
<bci-frequency></bci-frequency>
|
||||||
|
`,
|
||||||
|
directives: [FrequencyComponent]
|
||||||
|
})
|
||||||
|
class FrequencyComponentTestController {
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { Component, ElementRef, OnInit } from '@angular/core';
|
||||||
|
import * as io from 'socket.io-client';
|
||||||
|
import { ChartService } from '../shared';
|
||||||
|
import { CHART_DIRECTIVES } from '../shared/ng2-charts';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
moduleId: module.id,
|
||||||
|
selector: 'bci-frequency',
|
||||||
|
templateUrl: 'frequency.component.html',
|
||||||
|
styleUrls: ['frequency.component.css'],
|
||||||
|
directives: [CHART_DIRECTIVES],
|
||||||
|
providers: [ChartService]
|
||||||
|
})
|
||||||
|
|
||||||
|
export class FrequencyComponent implements OnInit {
|
||||||
|
|
||||||
|
socket: any;
|
||||||
|
constructor(private view: ElementRef, private chartService: ChartService) {
|
||||||
|
this.view = view;
|
||||||
|
this.socket = io('http://localhost:8080');
|
||||||
|
}
|
||||||
|
|
||||||
|
private chartType:string = 'Line';
|
||||||
|
private chartData:Array<any> = [[]];
|
||||||
|
private chartLabels:Array<any> = [];
|
||||||
|
private chartColors:Array<any> = this.chartService.getColors();
|
||||||
|
private chartSeries:Array<string> = this.chartService.getChannels();
|
||||||
|
|
||||||
|
private chartOptions:any = {
|
||||||
|
responsive: true,
|
||||||
|
animation: false,
|
||||||
|
animationSteps: 15,
|
||||||
|
datasetStrokeWidth: 1,
|
||||||
|
pointDot: false,
|
||||||
|
pointDotRadius: 1,
|
||||||
|
pointDotStrokeWidth: 0,
|
||||||
|
datasetFill: false,
|
||||||
|
scaleOverride: true,
|
||||||
|
scaleStartValue: -2,
|
||||||
|
scaleStepWidth: 1,
|
||||||
|
scaleSteps: 6,
|
||||||
|
barShowStroke: false,
|
||||||
|
barValueSpacing: 1,
|
||||||
|
barStrokeWidth: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.socket.on('bci:fft', (data) => {
|
||||||
|
this.chartData = data.data;
|
||||||
|
this.chartLabels = data.labels;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { FrequencyComponent } from './frequency.component';
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export {environment} from './environment';
|
||||||
|
export {DashboardComponent} from './dashboard.component';
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import {
|
||||||
|
beforeEachProviders,
|
||||||
|
it,
|
||||||
|
describe,
|
||||||
|
expect,
|
||||||
|
inject
|
||||||
|
} from '@angular/core/testing';
|
||||||
|
import { ChartService } from './chart.service';
|
||||||
|
|
||||||
|
describe('Chart Service', () => {
|
||||||
|
beforeEachProviders(() => [ChartService]);
|
||||||
|
|
||||||
|
it('should ...',
|
||||||
|
inject([ChartService], (service: ChartService) => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
}));
|
||||||
|
});
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ChartService {
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
getChannels (): Array<string> {
|
||||||
|
return Array(8).fill('CH').map((item, index) => item + (index + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
getColors (): Array<any> {
|
||||||
|
return [
|
||||||
|
{ strokeColor: 'rgba(112,185,252,1)' },
|
||||||
|
{ strokeColor: 'rgba(116,150,161,1)' },
|
||||||
|
{ strokeColor: 'rgba(162,86,178,1)' },
|
||||||
|
{ strokeColor: 'rgba(144,132,246,1)' },
|
||||||
|
{ strokeColor: 'rgba(138,219,229,1)' },
|
||||||
|
{ strokeColor: 'rgba(232,223,133,1)' },
|
||||||
|
{ strokeColor: 'rgba(148,159,177,1)' },
|
||||||
|
{ strokeColor: 'rgba(77,83,96,1)' }
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './chart.service';
|
||||||
|
export * from './ng2-charts';
|
||||||
@@ -0,0 +1,265 @@
|
|||||||
|
import {
|
||||||
|
Component, OnDestroy, OnInit, OnChanges,
|
||||||
|
EventEmitter, ElementRef, Input
|
||||||
|
} from '@angular/core';
|
||||||
|
import {CORE_DIRECTIVES, FORM_DIRECTIVES, NgClass} from '@angular/common';
|
||||||
|
|
||||||
|
declare var Chart:any;
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'chart',
|
||||||
|
template: `<canvas></canvas>`,
|
||||||
|
directives: [CORE_DIRECTIVES, NgClass]
|
||||||
|
})
|
||||||
|
export class ChartsComponent {}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'base-chart',
|
||||||
|
properties: [
|
||||||
|
'data',
|
||||||
|
'labels',
|
||||||
|
'series',
|
||||||
|
'colours',
|
||||||
|
'chartType',
|
||||||
|
'legend',
|
||||||
|
'options'
|
||||||
|
],
|
||||||
|
events: ['chartClick', 'chartHover'],
|
||||||
|
template: `
|
||||||
|
<canvas style="width: 100%; height: 100%;" (click)="click($event)" (mousemove)="hover($event)"></canvas>
|
||||||
|
`,
|
||||||
|
directives: [CORE_DIRECTIVES, FORM_DIRECTIVES, NgClass]
|
||||||
|
})
|
||||||
|
export class BaseChartComponent implements OnInit, OnDestroy, OnChanges {
|
||||||
|
@Input() public data:Array<any> = [];
|
||||||
|
@Input() public labels:Array<any> = [];
|
||||||
|
@Input() public options:any = {responsive: true};
|
||||||
|
@Input() public chartType:string;
|
||||||
|
@Input() public series:Array<any> = [];
|
||||||
|
@Input() public colours:Array<any> = [];
|
||||||
|
@Input() public legend:boolean;
|
||||||
|
|
||||||
|
private ctx:any;
|
||||||
|
private cvs:any;
|
||||||
|
private parent:any;
|
||||||
|
private chart:any;
|
||||||
|
private legendTemplate:any;
|
||||||
|
private initFlag:boolean = false;
|
||||||
|
private chartClick:EventEmitter<any> = new EventEmitter();
|
||||||
|
private chartHover:EventEmitter<any> = new EventEmitter();
|
||||||
|
private defaultsColours:Array<any> = [
|
||||||
|
{
|
||||||
|
fillColor: 'rgba(151,187,205,0.2)',
|
||||||
|
strokeColor: 'rgba(151,187,205,1)',
|
||||||
|
pointColor: 'rgba(151,187,205,1)',
|
||||||
|
pointStrokeColor: '#fff',
|
||||||
|
pointHighlightFill: '#fff',
|
||||||
|
pointHighlightStroke: 'rgba(151,187,205,0.8)',
|
||||||
|
color: 'rgba(151,187,205,1)',
|
||||||
|
highlight: 'rgba(151,187,205,0.8)'
|
||||||
|
}, {
|
||||||
|
fillColor: 'rgba(220,220,220,0.2)',
|
||||||
|
strokeColor: 'rgba(220,220,220,1)',
|
||||||
|
pointColor: 'rgba(220,220,220,1)',
|
||||||
|
pointStrokeColor: '#fff',
|
||||||
|
pointHighlightFill: '#fff',
|
||||||
|
pointHighlightStroke: 'rgba(220,220,220,0.8)',
|
||||||
|
color: 'rgba(220,220,220,1)',
|
||||||
|
highlight: 'rgba(220,220,220,0.8)'
|
||||||
|
}, {
|
||||||
|
fillColor: 'rgba(247,70,74,0.2)',
|
||||||
|
strokeColor: 'rgba(247,70,74,1)',
|
||||||
|
pointColor: 'rgba(247,70,74,1)',
|
||||||
|
pointStrokeColor: '#fff',
|
||||||
|
pointHighlightFill: '#fff',
|
||||||
|
pointHighlightStroke: 'rgba(247,70,74,0.8)',
|
||||||
|
color: 'rgba(247,70,74,1)',
|
||||||
|
highlight: 'rgba(247,70,74,0.8)'
|
||||||
|
}, {
|
||||||
|
fillColor: 'rgba(70,191,189,0.2)',
|
||||||
|
strokeColor: 'rgba(70,191,189,1)',
|
||||||
|
pointColor: 'rgba(70,191,189,1)',
|
||||||
|
pointStrokeColor: '#fff',
|
||||||
|
pointHighlightFill: '#fff',
|
||||||
|
pointHighlightStroke: 'rgba(70,191,189,0.8)',
|
||||||
|
color: 'rgba(70,191,189,1)',
|
||||||
|
highlight: 'rgba(70,191,189,0.8)'
|
||||||
|
}, {
|
||||||
|
fillColor: 'rgba(253,180,92,0.2)',
|
||||||
|
strokeColor: 'rgba(253,180,92,1)',
|
||||||
|
pointColor: 'rgba(253,180,92,1)',
|
||||||
|
pointStrokeColor: '#fff',
|
||||||
|
pointHighlightFill: '#fff',
|
||||||
|
pointHighlightStroke: 'rgba(253,180,92,0.8)',
|
||||||
|
color: 'rgba(253,180,92,1)',
|
||||||
|
highlight: 'rgba(253,180,92,0.8)'
|
||||||
|
}, {
|
||||||
|
fillColor: 'rgba(148,159,177,0.2)',
|
||||||
|
strokeColor: 'rgba(148,159,177,1)',
|
||||||
|
pointColor: 'rgba(148,159,177,1)',
|
||||||
|
pointStrokeColor: '#fff',
|
||||||
|
pointHighlightFill: '#fff',
|
||||||
|
pointHighlightStroke: 'rgba(148,159,177,0.8)',
|
||||||
|
color: 'rgba(148,159,177,1)',
|
||||||
|
highlight: 'rgba(148,159,177,0.8)'
|
||||||
|
}, {
|
||||||
|
fillColor: 'rgba(77,83,96,0.2)',
|
||||||
|
strokeColor: 'rgba(77,83,96,1)',
|
||||||
|
pointColor: 'rgba(77,83,96,1)',
|
||||||
|
pointStrokeColor: '#fff',
|
||||||
|
pointHighlightFill: '#fff',
|
||||||
|
pointHighlightStroke: 'rgba(77,83,96,0.8)',
|
||||||
|
color: 'rgba(77,83,96,1)',
|
||||||
|
highlight: 'rgba(77,83,96,0.8)'
|
||||||
|
}];
|
||||||
|
|
||||||
|
private element:ElementRef;
|
||||||
|
public constructor(element:ElementRef) {
|
||||||
|
this.element = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnInit():any {
|
||||||
|
this.ctx = this.element.nativeElement.children[0].getContext('2d');
|
||||||
|
this.cvs = this.element.nativeElement.children[0];
|
||||||
|
this.parent = this.element.nativeElement;
|
||||||
|
this.refresh();
|
||||||
|
this.initFlag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnChanges():any {
|
||||||
|
if (this.initFlag) {
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnDestroy():any {
|
||||||
|
if (this.chart) {
|
||||||
|
this.chart.destroy();
|
||||||
|
this.chart = void 0;
|
||||||
|
}
|
||||||
|
if (this.legendTemplate) {
|
||||||
|
this.legendTemplate.destroy();
|
||||||
|
this.legendTemplate = void 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public setLegend():void {
|
||||||
|
let list = this.parent.getElementsByTagName('ul');
|
||||||
|
if (list.length) {
|
||||||
|
list[0].remove();
|
||||||
|
this.parent.insertAdjacentHTML('beforeend', this.chart.generateLegend());
|
||||||
|
} else {
|
||||||
|
this.parent.insertAdjacentHTML('beforeend', this.chart.generateLegend());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getColour(colour:Array<number>):any {
|
||||||
|
return {
|
||||||
|
fillColor: this.rgba(colour, 0.2),
|
||||||
|
strokeColor: this.rgba(colour, 1),
|
||||||
|
pointColor: this.rgba(colour, 1),
|
||||||
|
pointStrokeColor: '#fff',
|
||||||
|
pointHighlightFill: '#fff',
|
||||||
|
pointHighlightStroke: this.rgba(colour, 0.8),
|
||||||
|
color: this.rgba(colour, 1),
|
||||||
|
highlight: this.rgba(colour, 0.8)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public getRandomInt(min:number, max:number):number {
|
||||||
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
public rgba(colour:Array<number>, alpha:number):string {
|
||||||
|
return 'rgba(' + colour.concat(alpha).join(',') + ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
public click(evt:any):void {
|
||||||
|
let atEvent = this.chart.getPointsAtEvent || this.chart.getBarsAtEvent || this.chart.getSegmentsAtEvent;
|
||||||
|
let activePoints = atEvent.call(this.chart, evt);
|
||||||
|
if (activePoints.length > 0) {
|
||||||
|
let activeLabel = activePoints[0].label;
|
||||||
|
this.chartClick.emit({activePoints: activePoints, activeLabel: activeLabel});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public hover(evt:any):void {
|
||||||
|
let atEvent = this.chart.getPointsAtEvent || this.chart.getBarsAtEvent || this.chart.getSegmentsAtEvent;
|
||||||
|
let activePoints = atEvent.call(this.chart, evt);
|
||||||
|
if (activePoints.length > 0) {
|
||||||
|
let activeLabel = activePoints[0].label;
|
||||||
|
let activePoint = activePoints[0].value;
|
||||||
|
this.chartHover.emit({activePoints: activePoints, activePoint: activePoint, activeLabel: activeLabel});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getChartBuilder(ctx:any, data:Array<any>, options:any):any {
|
||||||
|
return new Chart(ctx)[this.chartType](data, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getDataObject(label:string, value:any):any {
|
||||||
|
if (this.chartType === 'Line'
|
||||||
|
|| this.chartType === 'Bar'
|
||||||
|
|| this.chartType === 'Radar') {
|
||||||
|
return {
|
||||||
|
label: label,
|
||||||
|
data: value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.chartType === 'Pie'
|
||||||
|
|| this.chartType === 'Doughnut'
|
||||||
|
|| this.chartType === 'PolarArea') {
|
||||||
|
return {
|
||||||
|
label: label,
|
||||||
|
value: value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return void 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getChartData(labels:any, dataObject:any):any {
|
||||||
|
if (this.chartType === 'Line'
|
||||||
|
|| this.chartType === 'Bar'
|
||||||
|
|| this.chartType === 'Radar') {
|
||||||
|
return {
|
||||||
|
labels: labels,
|
||||||
|
datasets: dataObject
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (this.chartType === 'Pie'
|
||||||
|
|| this.chartType === 'Doughnut'
|
||||||
|
|| this.chartType === 'PolarArea') {
|
||||||
|
return dataObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private refresh():any {
|
||||||
|
if (this.options.responsive && this.parent.clientHeight === 0) {
|
||||||
|
return setTimeout(() => this.refresh(), 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ngOnDestroy();
|
||||||
|
let dataset:Array<any> = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < this.data.length; i++) {
|
||||||
|
let colourDesc:Array<number> = [this.getRandomInt(0, 255), this.getRandomInt(0, 255), this.getRandomInt(0, 255)];
|
||||||
|
let colour = i < this.colours.length ? this.colours[i] : this.defaultsColours[i] || this.getColour(colourDesc);
|
||||||
|
|
||||||
|
let data:any = Object.assign(colour,
|
||||||
|
this.getDataObject(this.series[i] || this.labels[i], this.data[i]));
|
||||||
|
|
||||||
|
dataset.push(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
let data:any = this.getChartData(this.labels, dataset);
|
||||||
|
this.chart = this.getChartBuilder(this.ctx, data, this.options);
|
||||||
|
|
||||||
|
if (this.legend) {
|
||||||
|
this.setLegend();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CHART_DIRECTIVES:Array<any> = [ChartsComponent, BaseChartComponent];
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { TimeSeriesComponent } from './time-series.component';
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
|
||||||
|
.time-series {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-series canvas {
|
||||||
|
margin: 40px 80px 40px 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-series-channels,
|
||||||
|
.time-series-amplitudes {
|
||||||
|
position: absolute;
|
||||||
|
top: 60px;
|
||||||
|
height: 450px;
|
||||||
|
color: rgba(102,102,102,1);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-series-channels {
|
||||||
|
width: 40px;
|
||||||
|
left: 20px;
|
||||||
|
text-align: left;
|
||||||
|
color: #000000;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-series-amplitudes {
|
||||||
|
right: 20px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-series-channels ul {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-series-channels li {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-series-channels ul,
|
||||||
|
.time-series-amplitudes ul {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: space-around;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-series-duration {
|
||||||
|
position: absolute;
|
||||||
|
left: 50px;
|
||||||
|
width: 98%;
|
||||||
|
bottom: 22px;
|
||||||
|
display: flex;
|
||||||
|
color: rgba(102,102,102,1);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-series-duration time {
|
||||||
|
width: 20%;
|
||||||
|
}
|
||||||
+6
-6
@@ -1,9 +1,9 @@
|
|||||||
<section class="time-series" ng-class="{ loading: !$ctrl.amplitudes }">
|
<section class="time-series" [ngClass]="{ 'loading': !amplitudes }">
|
||||||
<h2>Time Series</h2>
|
<h2>Time Series</h2>
|
||||||
<aside class="time-series-channels">
|
<aside class="time-series-channels">
|
||||||
<ul>
|
<ul>
|
||||||
<li ng-repeat="channel in $ctrl.channels track by $index"
|
<li *ngFor="let channel of channels, let i = index"
|
||||||
ng-style="{ 'color': $ctrl.colors[$index].strokeColor }">
|
[ngStyle]="{ 'color': colors[i].strokeColor }">
|
||||||
{{ channel }}
|
{{ channel }}
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -11,14 +11,14 @@
|
|||||||
<canvas id="timeSeries" width="950" height="450"></canvas>
|
<canvas id="timeSeries" width="950" height="450"></canvas>
|
||||||
<aside class="time-series-amplitudes">
|
<aside class="time-series-amplitudes">
|
||||||
<ul>
|
<ul>
|
||||||
<li ng-repeat="amplitude in $ctrl.amplitudes track by $index"
|
<li *ngFor="let amplitude of amplitudes, let i = index"
|
||||||
ng-style="{ 'color': $ctrl.colors[$index].strokeColor }">
|
[ngStyle]="{ 'color': colors[i].strokeColor }">
|
||||||
{{ amplitude }}
|
{{ amplitude }}
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</aside>
|
</aside>
|
||||||
<footer class="time-series-duration">
|
<footer class="time-series-duration">
|
||||||
<time ng-repeat="time in $ctrl.timeline track by $index"
|
<time *ngFor="let time of timeline"
|
||||||
datetime="P1M">
|
datetime="P1M">
|
||||||
{{ time }}
|
{{ time }}
|
||||||
</time>
|
</time>
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import {
|
||||||
|
beforeEach,
|
||||||
|
beforeEachProviders,
|
||||||
|
describe,
|
||||||
|
expect,
|
||||||
|
it,
|
||||||
|
inject,
|
||||||
|
} from '@angular/core/testing';
|
||||||
|
import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing';
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { TimeSeriesComponent } from './time-series.component';
|
||||||
|
|
||||||
|
describe('Component: TimeSeries', () => {
|
||||||
|
let builder: TestComponentBuilder;
|
||||||
|
|
||||||
|
beforeEachProviders(() => [TimeSeriesComponent]);
|
||||||
|
beforeEach(inject([TestComponentBuilder], function (tcb: TestComponentBuilder) {
|
||||||
|
builder = tcb;
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should inject the component', inject([TimeSeriesComponent],
|
||||||
|
(component: TimeSeriesComponent) => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should create the component', inject([], () => {
|
||||||
|
return builder.createAsync(TimeSeriesComponentTestController)
|
||||||
|
.then((fixture: ComponentFixture<any>) => {
|
||||||
|
let query = fixture.debugElement.query(By.directive(TimeSeriesComponent));
|
||||||
|
expect(query).toBeTruthy();
|
||||||
|
expect(query.componentInstance).toBeTruthy();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'test',
|
||||||
|
template: `
|
||||||
|
<bci-time-series></bci-time-series>
|
||||||
|
`,
|
||||||
|
directives: [TimeSeriesComponent]
|
||||||
|
})
|
||||||
|
class TimeSeriesComponentTestController {
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import { Component, ElementRef, OnInit } from '@angular/core';
|
||||||
|
import { SmoothieChart, TimeSeries } from 'smoothie';
|
||||||
|
import { ChartService } from '../shared';
|
||||||
|
import * as io from 'socket.io-client';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
moduleId: module.id,
|
||||||
|
selector: 'bci-time-series',
|
||||||
|
templateUrl: 'time-series.component.html',
|
||||||
|
styleUrls: ['time-series.component.css'],
|
||||||
|
providers: [ChartService]
|
||||||
|
})
|
||||||
|
|
||||||
|
export class TimeSeriesComponent implements OnInit {
|
||||||
|
|
||||||
|
socket: any;
|
||||||
|
constructor(private view: ElementRef, private chartService: ChartService) {
|
||||||
|
this.view = view;
|
||||||
|
this.socket = io('http://localhost:8080');
|
||||||
|
this.chartService = chartService;
|
||||||
|
}
|
||||||
|
|
||||||
|
private options = {
|
||||||
|
millisPerLine: 3000,
|
||||||
|
grid: {
|
||||||
|
fillStyle: '#333333',
|
||||||
|
strokeStyle: 'rgba(0,0,0,0.1)',
|
||||||
|
sharpLines: false,
|
||||||
|
verticalSections: 8,
|
||||||
|
borderVisible: true
|
||||||
|
},
|
||||||
|
labels: {
|
||||||
|
disabled: true
|
||||||
|
},
|
||||||
|
maxValue: 8 * 2,
|
||||||
|
minValue: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
private timeSeries = new SmoothieChart(this.options);
|
||||||
|
private amplitudes = [];
|
||||||
|
private timeline = [];
|
||||||
|
private lines = Array(8).fill(0).map(() => new TimeSeries());
|
||||||
|
private channels = this.chartService.getChannels();
|
||||||
|
private colors = this.chartService.getColors();
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.addTimeSeriesLines();
|
||||||
|
|
||||||
|
this.socket.on('bci:time', (data) => {
|
||||||
|
this.amplitudes = data.amplitudes;
|
||||||
|
this.timeline = data.timeline;
|
||||||
|
this.appendTimeSeriesLines(data.data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit () {
|
||||||
|
this.timeSeries.streamTo(this.view.nativeElement.querySelector('canvas'), 40);
|
||||||
|
}
|
||||||
|
|
||||||
|
addTimeSeriesLines () {
|
||||||
|
this.lines.forEach((line, index) => {
|
||||||
|
this.timeSeries.addTimeSeries(line, {
|
||||||
|
strokeStyle: this.colors[index].strokeColor
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
appendTimeSeriesLines (data) {
|
||||||
|
this.lines.forEach((line, index) => {
|
||||||
|
data[index].forEach((amplitude) => {
|
||||||
|
line.append(new Date().getTime(), amplitude);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Arquivo binário não exibido.
|
Depois Largura: | Altura: | Tamanho: 5.3 KiB |
@@ -0,0 +1,48 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>BCI Dashboard</title>
|
||||||
|
<base href="/">
|
||||||
|
{{content-for 'head'}}
|
||||||
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<style>
|
||||||
|
@import url(https://fonts.googleapis.com/css?family=Roboto:400,700,300);
|
||||||
|
body {
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
font-weight: 300;
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #222222;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<!-- Service worker support is disabled by default.
|
||||||
|
Install the worker script and uncomment to enable.
|
||||||
|
Only enable service workers in production.
|
||||||
|
<script type="text/javascript">
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
navigator.serviceWorker.register('/worker.js').catch(function(err) {
|
||||||
|
console.log('Error installing service worker: ', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
-->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<bci-dashboard>Loading...</bci-dashboard>
|
||||||
|
|
||||||
|
<script src="vendor/chart.js/Chart.js"></script>
|
||||||
|
<script src="vendor/es6-shim/es6-shim.js"></script>
|
||||||
|
<script src="vendor/reflect-metadata/Reflect.js"></script>
|
||||||
|
<script src="vendor/systemjs/dist/system.src.js"></script>
|
||||||
|
<script src="vendor/zone.js/dist/zone.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
System.import('system-config.js').then(function () {
|
||||||
|
System.import('main');
|
||||||
|
}).catch(console.error.bind(console));
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { bootstrap } from '@angular/platform-browser-dynamic';
|
||||||
|
import { enableProdMode } from '@angular/core';
|
||||||
|
import { DashboardComponent, environment } from './app/';
|
||||||
|
|
||||||
|
if (environment.production) {
|
||||||
|
enableProdMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
bootstrap(DashboardComponent);
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/***********************************************************************************************
|
||||||
|
* User Configuration.
|
||||||
|
**********************************************************************************************/
|
||||||
|
/** Map relative paths to URLs. */
|
||||||
|
const map: any = {
|
||||||
|
'smoothie': 'vendor/smoothie/smoothie.js',
|
||||||
|
'ng2-charts': 'vendor/ng2-charts/bundles/ng2-charts.js',
|
||||||
|
'socket.io-client': 'vendor/socket.io-client/socket.io.js'
|
||||||
|
};
|
||||||
|
|
||||||
|
/** User packages configuration. */
|
||||||
|
const packages: any = {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/***********************************************************************************************
|
||||||
|
* Everything underneath this line is managed by the CLI.
|
||||||
|
**********************************************************************************************/
|
||||||
|
const barrels: string[] = [
|
||||||
|
// Angular specific barrels.
|
||||||
|
'@angular/core',
|
||||||
|
'@angular/common',
|
||||||
|
'@angular/compiler',
|
||||||
|
'@angular/http',
|
||||||
|
'@angular/router',
|
||||||
|
'@angular/platform-browser',
|
||||||
|
'@angular/platform-browser-dynamic',
|
||||||
|
|
||||||
|
// Thirdparty barrels.
|
||||||
|
'rxjs',
|
||||||
|
|
||||||
|
// App specific barrels.
|
||||||
|
'app',
|
||||||
|
'app/shared',
|
||||||
|
'app/frequency',
|
||||||
|
'app/time-series',
|
||||||
|
'app/nav',
|
||||||
|
'app/+frequency',
|
||||||
|
'app/+frequency-band',
|
||||||
|
/** @cli-barrel */
|
||||||
|
];
|
||||||
|
|
||||||
|
const cliSystemConfigPackages: any = {};
|
||||||
|
barrels.forEach((barrelName: string) => {
|
||||||
|
cliSystemConfigPackages[barrelName] = { main: 'index' };
|
||||||
|
});
|
||||||
|
|
||||||
|
/** Type declaration for ambient System. */
|
||||||
|
declare var System: any;
|
||||||
|
|
||||||
|
// Apply the CLI SystemJS configuration.
|
||||||
|
System.config({
|
||||||
|
map: {
|
||||||
|
'@angular': 'vendor/@angular',
|
||||||
|
'rxjs': 'vendor/rxjs',
|
||||||
|
'main': 'main.js'
|
||||||
|
},
|
||||||
|
packages: cliSystemConfigPackages
|
||||||
|
});
|
||||||
|
|
||||||
|
// Apply the user's configuration.
|
||||||
|
System.config({ map, packages });
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"compileOnSave": false,
|
||||||
|
"compilerOptions": {
|
||||||
|
"declaration": false,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"mapRoot": "",
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"noEmitOnError": true,
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"outDir": "../dist/",
|
||||||
|
"rootDir": ".",
|
||||||
|
"sourceMap": true,
|
||||||
|
"target": "es5",
|
||||||
|
"inlineSources": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"files": [
|
||||||
|
"main.ts",
|
||||||
|
"typings.d.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
externo
+3
@@ -0,0 +1,3 @@
|
|||||||
|
/// <reference path="../typings/browser.d.ts" />
|
||||||
|
|
||||||
|
declare var module: { id: string };
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"rulesDirectory": ["node_modules/codelyzer"],
|
||||||
|
"rules": {
|
||||||
|
"max-line-length": [true, 100],
|
||||||
|
"no-inferrable-types": true,
|
||||||
|
"class-name": true,
|
||||||
|
"comment-format": [
|
||||||
|
true,
|
||||||
|
"check-space"
|
||||||
|
],
|
||||||
|
"indent": [
|
||||||
|
true,
|
||||||
|
"spaces"
|
||||||
|
],
|
||||||
|
"eofline": true,
|
||||||
|
"no-duplicate-variable": true,
|
||||||
|
"no-eval": true,
|
||||||
|
"no-arg": true,
|
||||||
|
"no-internal-module": true,
|
||||||
|
"no-trailing-whitespace": true,
|
||||||
|
"no-bitwise": true,
|
||||||
|
"no-shadowed-variable": true,
|
||||||
|
"no-unused-expression": true,
|
||||||
|
"no-unused-variable": true,
|
||||||
|
"one-line": [
|
||||||
|
true,
|
||||||
|
"check-catch",
|
||||||
|
"check-else",
|
||||||
|
"check-open-brace",
|
||||||
|
"check-whitespace"
|
||||||
|
],
|
||||||
|
"quotemark": [
|
||||||
|
true,
|
||||||
|
"single",
|
||||||
|
"avoid-escape"
|
||||||
|
],
|
||||||
|
"semicolon": [true, "always"],
|
||||||
|
"typedef-whitespace": [
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
"call-signature": "nospace",
|
||||||
|
"index-signature": "nospace",
|
||||||
|
"parameter": "nospace",
|
||||||
|
"property-declaration": "nospace",
|
||||||
|
"variable-declaration": "nospace"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"curly": true,
|
||||||
|
"variable-name": [
|
||||||
|
true,
|
||||||
|
"ban-keywords",
|
||||||
|
"check-format",
|
||||||
|
"allow-trailing-underscore"
|
||||||
|
],
|
||||||
|
"whitespace": [
|
||||||
|
true,
|
||||||
|
"check-branch",
|
||||||
|
"check-decl",
|
||||||
|
"check-operator",
|
||||||
|
"check-separator",
|
||||||
|
"check-type"
|
||||||
|
],
|
||||||
|
"component-selector-name": [true, "kebab-case"],
|
||||||
|
"component-selector-type": [true, "element"],
|
||||||
|
"host-parameter-decorator": true,
|
||||||
|
"input-parameter-decorator": true,
|
||||||
|
"output-parameter-decorator": true,
|
||||||
|
"attribute-parameter-decorator": true,
|
||||||
|
"input-property-directive": true,
|
||||||
|
"output-property-directive": true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"ambientDevDependencies": {
|
||||||
|
"angular-protractor": "registry:dt/angular-protractor#1.5.0+20160425143459",
|
||||||
|
"jasmine": "registry:dt/jasmine#2.2.0+20160412134438",
|
||||||
|
"selenium-webdriver": "registry:dt/selenium-webdriver#2.44.0+20160317120654"
|
||||||
|
},
|
||||||
|
"ambientDependencies": {
|
||||||
|
"chart": "registry:dt/chart#0.0.0+20160316155526",
|
||||||
|
"es6-shim": "registry:dt/es6-shim#0.31.2+20160317120654",
|
||||||
|
"smoothie": "registry:dt/smoothie#1.25.0+20160316155526",
|
||||||
|
"socket.io-client": "registry:dt/socket.io-client#1.4.4+20160317120654"
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-17
@@ -1,11 +1,8 @@
|
|||||||
var express = require('express');
|
|
||||||
var app = express();
|
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var http = require('http').Server(app);
|
|
||||||
var argv = require('yargs').argv;
|
var argv = require('yargs').argv;
|
||||||
var OpenBCIBoard = require('openbci-sdk');
|
var OpenBCIBoard = require('openbci-sdk');
|
||||||
var dsp = require('dsp.js');
|
var dsp = require('dsp.js');
|
||||||
var io = require('socket.io')(http);
|
var io = require('socket.io')(process.env.app_port || 8080);
|
||||||
var topogrid = require('topogrid');
|
var topogrid = require('topogrid');
|
||||||
var jStat = require('jstat').jStat;
|
var jStat = require('jstat').jStat;
|
||||||
var Fili = require('fili');
|
var Fili = require('fili');
|
||||||
@@ -17,19 +14,6 @@ io.on('connection', function(socket){
|
|||||||
console.log('A user connected');
|
console.log('A user connected');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Server
|
|
||||||
app.use(express.static(path.join(__dirname, '/app')));
|
|
||||||
|
|
||||||
app.use('/node_modules', express.static(path.join(__dirname, '/node_modules')));
|
|
||||||
|
|
||||||
app.get('*', function(req, res) {
|
|
||||||
res.sendFile(path.join(__dirname, '/app/index.html'));
|
|
||||||
});
|
|
||||||
|
|
||||||
http.listen(3060, function () {
|
|
||||||
console.log('listening on port 3060');
|
|
||||||
});
|
|
||||||
|
|
||||||
// OpenBCI
|
// OpenBCI
|
||||||
var board = new OpenBCIBoard.OpenBCIBoard({
|
var board = new OpenBCIBoard.OpenBCIBoard({
|
||||||
verbose: true
|
verbose: true
|
||||||
|
|||||||
Referência em uma Nova Issue
Bloquear um usuário