Transfer the layer/keymap/config with node-usb on shortcuts

This commit is contained in:
Farkas József
2017-01-19 19:03:02 +01:00
committed by József Farkas
parent 4ed81331b3
commit 517aed1b1c
11 changed files with 323 additions and 10 deletions

View File

@@ -1,10 +1,16 @@
language: node_js
sudo: false
sudo: required
node_js:
- '5.10.0'
- '6.9.4'
before_install:
- sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
- sudo apt-get update -q
- sudo apt-get install g++-4.8 -y
install:
- npm install -g npm@3.10.7
- npm install
before_script:
@@ -19,3 +25,12 @@ script:
cache:
directories:
- node_modules
env:
- CXX=g++-4.8
addons:
apt:
packages:
- build-essential
- libudev-dev

View File

@@ -14,8 +14,10 @@
"@types/electron": "^1.4.29",
"@types/jquery": "2.0.39",
"@types/node": "6.0.52",
"@types/usb": "^1.1.2",
"copy-webpack-plugin": "^4.0.1",
"electron": "1.4.15",
"electron-rebuild": "^1.5.5",
"expose-loader": "^0.7.1",
"html-loader": "0.4.4",
"node-sass": "^4.3.0",
@@ -54,10 +56,12 @@
"rxjs": "5.0.3",
"select2": "^4.0.3",
"typescript": "2.1.5",
"usb": "git+https://github.com/aktary/node-usb.git",
"xml-loader": "^1.1.0",
"zone.js": "0.7.6"
},
"scripts": {
"postinstall": "electron-rebuild -w usb -p",
"tslint": "tslint \"src/**/*.ts\" \"test-serializer/**/*.ts\"",
"stylelint": "stylelint \"src/**/*.scss\" --syntax scss",
"lint": "run-s -scn tslint stylelint",

View File

@@ -64,6 +64,7 @@ import { CancelableDirective } from './directives';
import { CaptureService } from './services/capture.service';
import { MapperService } from './services/mapper.service';
import { UhkDeviceService } from './services/uhk-device.service';
import { KeymapEffects, MacroEffects } from './store/effects';
import { keymapReducer, macroReducer, presetReducer } from './store/reducers';
@@ -156,7 +157,8 @@ const storeConfig = {
appRoutingProviders,
KeymapEditGuard,
MacroNotFoundGuard,
CaptureService
CaptureService,
UhkDeviceService
],
bootstrap: [MainAppComponent]
})

View File

@@ -1,17 +1,22 @@
import { Component } from '@angular/core';
import { Component, HostListener, ViewChild } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import '@ngrx/core/add/operator/select';
import { Store } from '@ngrx/store';
import 'rxjs/add/operator/first';
import 'rxjs/add/operator/let';
import 'rxjs/add/operator/publishReplay';
import 'rxjs/add/operator/switchMap';
import { Observable } from 'rxjs/Observable';
import { Keymap } from '../../../config-serializer/config-items/Keymap';
import { UhkBuffer } from '../../../config-serializer/UhkBuffer';
import { AppState } from '../../../store';
import { getKeymap, getKeymapEntities } from '../../../store/reducers/keymap';
import { SvgKeyboardWrapComponent } from '../../svg/wrap';
import { UhkDeviceService } from '../../../services/uhk-device.service';
@Component({
selector: 'keymap-edit',
@@ -22,12 +27,16 @@ import { getKeymap, getKeymapEntities } from '../../../store/reducers/keymap';
}
})
export class KeymapEditComponent {
@ViewChild(SvgKeyboardWrapComponent) wrap: SvgKeyboardWrapComponent;
private keymap$: Observable<Keymap>;
private deletable$: Observable<boolean>;
constructor(
private store: Store<AppState>,
private route: ActivatedRoute
private route: ActivatedRoute,
private uhkDevice: UhkDeviceService
) {
this.keymap$ = route
.params
@@ -39,4 +48,58 @@ export class KeymapEditComponent {
this.deletable$ = store.let(getKeymapEntities())
.map((keymaps: Keymap[]) => keymaps.length > 1);
}
@HostListener('window:keydown.control.u', ['$event'])
onCtrlU(event: KeyboardEvent): void {
console.log('ctrl + u pressed');
event.preventDefault();
event.stopPropagation();
this.sendLayer();
}
@HostListener('window:keydown.control.i', ['$event'])
onCtrlI(event: KeyboardEvent): void {
console.log('ctrl + i pressed');
event.preventDefault();
event.stopPropagation();
this.sendKeymap();
}
private sendLayer(): void {
const currentLayer: number = this.wrap.getSelectedLayer();
this.keymap$
.first()
.map(keymap => keymap.layers[currentLayer])
.map(layer => {
const uhkBuffer = new UhkBuffer();
layer.toBinary(uhkBuffer);
return uhkBuffer.getBufferContent();
})
.switchMap((buffer: Buffer) => this.uhkDevice.sendConfig(buffer))
.do(response => console.log('Sending layer finished', response))
.switchMap(() => this.uhkDevice.applyConfig())
.subscribe(
(response) => console.log('Applying layer finished', response),
error => console.error('Error during uploading layer', error),
() => console.log('Layer has been sucessfully uploaded')
);
}
private sendKeymap(): void {
this.keymap$
.first()
.map(keymap => {
const uhkBuffer = new UhkBuffer();
keymap.toBinary(uhkBuffer);
return uhkBuffer.getBufferContent();
})
.switchMap((buffer: Buffer) => this.uhkDevice.sendConfig(buffer))
.do(response => console.log('Sending keymap finished', response))
.switchMap(() => this.uhkDevice.applyConfig())
.subscribe(
(response) => console.log('Applying keymap finished', response),
error => console.error('Error during uploading keymap', error),
() => console.log('Keymap has been sucessfully uploaded')
);
}
}

View File

@@ -227,6 +227,10 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
this.currentLayer = index;
}
getSelectedLayer(): number {
return this.currentLayer;
}
private getKeyActionContent(keyAction: KeyAction): Observable<NameValuePair[]> {
if (keyAction instanceof KeystrokeAction) {
const keystrokeAction: KeystrokeAction = keyAction;

View File

@@ -8,6 +8,10 @@
<link rel="shortcut icon" href="images/favicon.ico">
<script src="polyfills.uhk.js"></script>
<script src="vendor.uhk.js"></script>
<!--<script src="vendor/usb/usb.js"></script>-->
<script>
usb = require('usb');
</script>
</head>
<body>
<!-- Google Tag Manager -->

View File

@@ -1,4 +1,16 @@
import { Component, ViewEncapsulation } from '@angular/core';
import { Component, ViewEncapsulation, HostListener } from '@angular/core';
// import { Observable } from 'rxjs/Observable';
// import { Store } from '@ngrx/store';
// import { AppState } from '../store';
// import { DataStorage } from '../store/storage';
// import { getKeymapEntities, getMacroEntities } from '../store/reducers';
// import { UhkBuffer } from '../config-serializer/UhkBuffer';
// import { UserConfiguration } from '../config-serializer/config-items/UserConfiguration';
// import { UhkDeviceService } from '../services/uhk-device.service';
@Component({
selector: 'main-app',
@@ -6,4 +18,50 @@ import { Component, ViewEncapsulation } from '@angular/core';
styles: [require('./main-app.component.scss')],
encapsulation: ViewEncapsulation.None
})
export class MainAppComponent { }
export class MainAppComponent {
// private configuration$: Observable<UserConfiguration>;
constructor(
// private uhkDevice: UhkDeviceService,
// store: Store<AppState>,
// dataStorage: DataStorage
) {
// this.configuration$ = store.let(getKeymapEntities())
// .combineLatest(store.let(getMacroEntities()))
// .map((pair) => {
// const config = new UserConfiguration();
// Object.assign(config, dataStorage.getConfiguration());
// config.keymaps = pair[0];
// config.macros = pair[1];
// return config;
// });
}
@HostListener('window:keydown.control.o', ['$event'])
onCtrlO(event: KeyboardEvent): void {
console.log('ctrl + o pressed');
event.preventDefault();
event.stopPropagation();
this.sendConfiguration();
}
private sendConfiguration(): void {
// this.configuration$
// .first()
// .map(configuration => {
// const uhkBuffer = new UhkBuffer();
// configuration.toBinary(uhkBuffer);
// return uhkBuffer.getBufferContent();
// })
// .switchMap((buffer: Buffer) => this.uhkDevice.sendConfig(buffer))
// .do(response => console.log('Sending config finished', response))
// .switchMap(() => this.uhkDevice.applyConfig())
// .subscribe(
// (response) => console.log('Applying config finished', response),
// error => console.error('Error during uploading config', error),
// () => console.log('Config has been sucessfully uploaded')
// );
}
}

View File

@@ -2,6 +2,8 @@ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
process.stdout = require('browser-stdout')();
if (!process.stdout) {
process.stdout = require('browser-stdout')();
}
platformBrowserDynamic().bootstrapModule(AppModule);

View File

@@ -1,2 +1,3 @@
import 'core-js/es7/object';
import 'core-js/es7/reflect';
import 'zone.js/dist/zone';

View File

@@ -0,0 +1,159 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Observer } from 'rxjs/Observer';
import { ConnectableObservable } from 'rxjs/observable/ConnectableObservable';
import { Subject } from 'rxjs/Subject';
import { Subscriber } from 'rxjs/Subscriber';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/observable/empty';
import 'rxjs/add/observable/from';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/concat';
import 'rxjs/add/operator/concatMap';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/publish';
import { Device, Interface, InEndpoint, OutEndpoint, findByIds } from 'usb';
import { Layer } from '../config-serializer/config-items/Layer';
import { UhkBuffer } from '../config-serializer/UhkBuffer';
const vendorId = 0x16d3;
const productId = 0x05ea;
const MAX_PAYLOAD_SIZE = 64;
enum Command {
UploadConfig = 8,
ApplyConfig = 9
};
interface SenderMessage {
buffer: Buffer;
observer: Observer<any>;
};
@Injectable()
export class UhkDeviceService {
private device: Device;
private connected: boolean;
private messageIn$: Observable<Buffer>;
private messageOut$: Subject<SenderMessage>;
private outSubscription: Subscription;
constructor() {
this.messageOut$ = new Subject<SenderMessage>();
this.connect();
}
connect(): void {
if (this.connected) {
return;
}
this.device = findByIds(vendorId, productId);
this.device.open();
const usbInterface: Interface = this.device.interface(0);
// https://github.com/tessel/node-usb/issues/147
// The function 'isKernelDriverActive' is not available on Windows and not even needed.
if (process.platform !== 'win32' && usbInterface.isKernelDriverActive()) {
usbInterface.detachKernelDriver();
}
usbInterface.claim();
this.messageIn$ = Observable.create((subscriber: Subscriber<Buffer>) => {
const inEndPoint: InEndpoint = <InEndpoint>usbInterface.endpoints[0];
console.log('Try to read');
inEndPoint.transfer(MAX_PAYLOAD_SIZE, (error: string, receivedBuffer: Buffer) => {
if (error) {
console.error('reading error', error);
subscriber.error(error);
} else {
console.log('read data', receivedBuffer);
subscriber.next(receivedBuffer);
subscriber.complete();
}
});
});
const outEndPoint: OutEndpoint = <OutEndpoint>usbInterface.endpoints[1];
const outSending = this.messageOut$.concatMap(senderPackage => {
return (<Observable<void>>Observable.create((subscriber: Subscriber<void>) => {
console.log('transfering', senderPackage.buffer);
outEndPoint.transfer(senderPackage.buffer, (error) => {
if (error) {
console.error('transfering errored', error);
subscriber.error(error);
} else {
console.log('transfering finished');
subscriber.complete();
}
});
})).concat(this.messageIn$)
.do(buffer => senderPackage.observer.next(buffer) && senderPackage.observer.complete())
.catch((error: string) => {
senderPackage.observer.error(error);
return Observable.empty<void>();
});
}).publish();
this.outSubscription = outSending.connect();
this.connected = true;
}
disconnect() {
if (!this.connected) {
return;
}
this.outSubscription.unsubscribe();
this.messageIn$ = undefined;
this.device.interface(0).release();
this.device.close();
this.connected = false;
}
sendConfig(configBuffer: Buffer): Observable<Buffer> {
return Observable.create((subscriber: Subscriber<Buffer>) => {
console.log('Sending...', configBuffer);
const fragments: Buffer[] = [];
const MAX_SENDING_PAYLOAD_SIZE = MAX_PAYLOAD_SIZE - 4;
for (let offset = 0; offset < configBuffer.length; offset += MAX_SENDING_PAYLOAD_SIZE) {
const length = offset + MAX_SENDING_PAYLOAD_SIZE < configBuffer.length
? MAX_SENDING_PAYLOAD_SIZE
: configBuffer.length - offset;
const header = new Buffer([Command.UploadConfig, length, offset & 0xFF, offset >> 8]);
fragments.push(Buffer.concat([header, configBuffer.slice(offset, offset + length)]));
}
const buffers: Buffer[] = [];
const observer: Observer<Buffer> = {
next: (buffer: Buffer) => buffers.push(buffer),
error: error => subscriber.error(error),
complete: () => {
if (buffers.length === fragments.length) {
subscriber.next(Buffer.concat(buffers));
subscriber.complete();
console.log('Sending finished');
}
}
};
fragments
.map<SenderMessage>(fragment => ({ buffer: fragment, observer }))
.forEach(senderPackage => this.messageOut$.next(senderPackage));
});
}
applyConfig(): Observable<Buffer> {
return Observable.create((subscriber: Subscriber<Buffer>) => {
console.log('Applying configuration');
this.messageOut$.next({
buffer: new Buffer([Command.ApplyConfig]),
observer: subscriber
});
});
}
}

View File

@@ -17,7 +17,8 @@
"jquery",
"core-js",
"select2",
"electron"
"electron",
"usb"
]
},
"exclude": [