diff --git a/packages/uhk-agent/src/services/app.service.ts b/packages/uhk-agent/src/services/app.service.ts index e0b7e986..d095bb87 100644 --- a/packages/uhk-agent/src/services/app.service.ts +++ b/packages/uhk-agent/src/services/app.service.ts @@ -31,7 +31,7 @@ export class AppService extends MainServiceBase { addons: this.options.addons || false, autoWriteConfig: this.options['auto-write-config'] || false }, - deviceConnected: this.deviceService.isConnected, + deviceConnected: this.uhkHidDeviceService.deviceConnected(), hasPermission: this.uhkHidDeviceService.hasPermission(), agentVersionInfo: { version: packageJson.version, diff --git a/packages/uhk-agent/src/services/device.service.ts b/packages/uhk-agent/src/services/device.service.ts index 70f9e4fb..8f391df5 100644 --- a/packages/uhk-agent/src/services/device.service.ts +++ b/packages/uhk-agent/src/services/device.service.ts @@ -1,9 +1,8 @@ import { ipcMain } from 'electron'; -import { ConfigurationReply, IpcEvents, IpcResponse, LogService } from 'uhk-common'; -import { Constants, snooze, UhkHidDevice, UhkOperations } from 'uhk-usb'; +import { ConfigurationReply, DeviceConnectionState, IpcEvents, IpcResponse, LogService } from 'uhk-common'; +import { snooze, UhkHidDevice, UhkOperations } from 'uhk-usb'; import { Observable } from 'rxjs/Observable'; import { Subscription } from 'rxjs/Subscription'; -import { Device, devices } from 'node-hid'; import { emptyDir } from 'fs-extra'; import 'rxjs/add/observable/interval'; @@ -19,13 +18,11 @@ import { QueueManager } from './queue-manager'; /** * IpcMain pair of the UHK Communication * Functionality: - * - Detect device is connected or not * - Send UserConfiguration to the UHK Device * - Read UserConfiguration from the UHK Device */ export class DeviceService { private pollTimer$: Subscription; - private connected = false; private queueManager = new QueueManager(); constructor(private logService: LogService, @@ -66,14 +63,6 @@ export class DeviceService { logService.debug('[DeviceService] init success'); } - /** - * Return with true is an UHK Device is connected to the computer. - * @returns {boolean} - */ - public get isConnected(): boolean { - return this.connected; - } - /** * Return with the actual UserConfiguration from UHK Device * @returns {Promise} @@ -102,7 +91,6 @@ export class DeviceService { } public close(): void { - this.connected = false; this.stopPollTimer(); this.logService.info('[DeviceService] Device connection checker stopped.'); } @@ -153,15 +141,16 @@ export class DeviceService { this.pollTimer$ = Observable.interval(1000) .startWith(0) - .map(() => { - return devices().some((dev: Device) => dev.vendorId === Constants.VENDOR_ID && - dev.productId === Constants.PRODUCT_ID); - }) + .map(() => this.device.deviceConnected()) .distinctUntilChanged() .do((connected: boolean) => { - this.connected = connected; - this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, connected); - this.logService.info(`[DeviceService] Device connection state changed to: ${connected}`); + const response: DeviceConnectionState = { + connected, + hasPermission: this.device.hasPermission() + }; + + this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, response); + this.logService.info('[DeviceService] Device connection state changed to:', response); }) .subscribe(); } diff --git a/packages/uhk-agent/src/services/sudo.service.ts b/packages/uhk-agent/src/services/sudo.service.ts index 11c45850..54039332 100644 --- a/packages/uhk-agent/src/services/sudo.service.ts +++ b/packages/uhk-agent/src/services/sudo.service.ts @@ -10,7 +10,7 @@ export class SudoService { constructor(private logService: LogService) { if (isDev) { - this.rootDir = path.join(path.join(process.cwd(), process.argv[1]), '..'); + this.rootDir = path.join(path.join(process.cwd(), process.argv[1]), '../../../../'); } else { this.rootDir = path.dirname(app.getAppPath()); } @@ -40,11 +40,12 @@ export class SudoService { name: 'Setting UHK access rules' }; const command = `sh ${scriptPath}`; - console.log(command); + this.logService.debug('[SudoService] Set privilege command: ', command); sudo.exec(command, options, (error: any) => { const response = new IpcResponse(); if (error) { + this.logService.error('[SudoService] Error when set privilege: ', error); response.success = false; response.error = error; } else { diff --git a/packages/uhk-common/src/models/device-connection-state.ts b/packages/uhk-common/src/models/device-connection-state.ts new file mode 100644 index 00000000..538b48cd --- /dev/null +++ b/packages/uhk-common/src/models/device-connection-state.ts @@ -0,0 +1,4 @@ +export interface DeviceConnectionState { + connected: boolean; + hasPermission: boolean; +} diff --git a/packages/uhk-common/src/models/index.ts b/packages/uhk-common/src/models/index.ts index 76e00ad2..4fee8211 100644 --- a/packages/uhk-common/src/models/index.ts +++ b/packages/uhk-common/src/models/index.ts @@ -4,3 +4,4 @@ export * from './ipc-response'; export * from './app-start-info'; export * from './configuration-reply'; export * from './version-information'; +export * from './device-connection-state'; diff --git a/packages/uhk-usb/src/uhk-hid-device.ts b/packages/uhk-usb/src/uhk-hid-device.ts index d771e9f8..f94134aa 100644 --- a/packages/uhk-usb/src/uhk-hid-device.ts +++ b/packages/uhk-usb/src/uhk-hid-device.ts @@ -25,6 +25,7 @@ export class UhkHidDevice { * @private */ private _device: HID; + private _hasPermission = false; constructor(private logService: LogService) { } @@ -38,8 +39,18 @@ export class UhkHidDevice { */ public hasPermission(): boolean { try { - devices(); - return true; + if (this._hasPermission) { + return true; + } + + if (!this.deviceConnected()) { + return true; + } + + this._hasPermission = this.getDevice() !== null; + this.close(); + + return this._hasPermission; } catch (err) { this.logService.error('[UhkHidDevice] hasPermission', err); } @@ -47,6 +58,21 @@ export class UhkHidDevice { return false; } + /** + * Return with true is an UHK Device is connected to the computer. + * @returns {boolean} + */ + public deviceConnected(): boolean { + const connected = devices().some((dev: Device) => dev.vendorId === Constants.VENDOR_ID && + dev.productId === Constants.PRODUCT_ID); + + if (!connected) { + this._hasPermission = false; + } + + return connected; + } + /** * Send data to the UHK device and wait for the response. * Throw an error when 1st byte of the response is not 0 @@ -225,7 +251,7 @@ export class UhkHidDevice { } } -function kbootKommandName(module: ModuleSlotToI2cAddress): string { +function kbootKommandName(module: ModuleSlotToI2cAddress): string { switch (module) { case ModuleSlotToI2cAddress.leftHalf: return 'leftHalf'; diff --git a/packages/uhk-web/src/app/services/device-renderer.service.ts b/packages/uhk-web/src/app/services/device-renderer.service.ts index ad1f3668..0d636c0f 100644 --- a/packages/uhk-web/src/app/services/device-renderer.service.ts +++ b/packages/uhk-web/src/app/services/device-renderer.service.ts @@ -1,7 +1,7 @@ import { Injectable, NgZone } from '@angular/core'; import { Action, Store } from '@ngrx/store'; -import { IpcEvents, IpcResponse, LogService } from 'uhk-common'; +import { DeviceConnectionState, IpcEvents, IpcResponse, LogService } from 'uhk-common'; import { AppState } from '../store'; import { IpcCommonRenderer } from './ipc-common-renderer'; import { @@ -47,7 +47,7 @@ export class DeviceRendererService { } private registerEvents(): void { - this.ipcRenderer.on(IpcEvents.device.deviceConnectionStateChanged, (event: string, arg: boolean) => { + this.ipcRenderer.on(IpcEvents.device.deviceConnectionStateChanged, (event: string, arg: DeviceConnectionState) => { this.dispachStoreAction(new ConnectionStateChangedAction(arg)); }); diff --git a/packages/uhk-web/src/app/store/actions/device.ts b/packages/uhk-web/src/app/store/actions/device.ts index 9655650a..9497dc73 100644 --- a/packages/uhk-web/src/app/store/actions/device.ts +++ b/packages/uhk-web/src/app/store/actions/device.ts @@ -1,5 +1,5 @@ import { Action } from '@ngrx/store'; -import { IpcResponse, type } from 'uhk-common'; +import { DeviceConnectionState, IpcResponse, type } from 'uhk-common'; const PREFIX = '[device] '; @@ -8,7 +8,6 @@ export const ActionTypes = { SET_PRIVILEGE_ON_LINUX: type(PREFIX + 'set privilege on linux'), SET_PRIVILEGE_ON_LINUX_REPLY: type(PREFIX + 'set privilege on linux reply'), CONNECTION_STATE_CHANGED: type(PREFIX + 'connection state changed'), - PERMISSION_STATE_CHANGED: type(PREFIX + 'permission state changed'), SAVE_CONFIGURATION: type(PREFIX + 'save configuration'), SAVE_CONFIGURATION_REPLY: type(PREFIX + 'save configuration reply'), SAVING_CONFIGURATION: type(PREFIX + 'saving configuration'), @@ -39,14 +38,7 @@ export class SetPrivilegeOnLinuxReplyAction implements Action { export class ConnectionStateChangedAction implements Action { type = ActionTypes.CONNECTION_STATE_CHANGED; - constructor(public payload: boolean) { - } -} - -export class PermissionStateChangedAction implements Action { - type = ActionTypes.PERMISSION_STATE_CHANGED; - - constructor(public payload: boolean) { + constructor(public payload: DeviceConnectionState) { } } @@ -121,7 +113,6 @@ export type Actions = SetPrivilegeOnLinuxAction | SetPrivilegeOnLinuxReplyAction | ConnectionStateChangedAction - | PermissionStateChangedAction | ShowSaveToKeyboardButtonAction | SaveConfigurationAction | SaveConfigurationReplyAction diff --git a/packages/uhk-web/src/app/store/effects/app.ts b/packages/uhk-web/src/app/store/effects/app.ts index 1ab1cddd..01ae17e1 100644 --- a/packages/uhk-web/src/app/store/effects/app.ts +++ b/packages/uhk-web/src/app/store/effects/app.ts @@ -26,7 +26,6 @@ import { AppUpdateRendererService } from '../../services/app-update-renderer.ser import { ActionTypes as DeviceActions, ConnectionStateChangedAction, - PermissionStateChangedAction, SaveToKeyboardSuccessAction } from '../actions/device'; import { AppState, autoWriteUserConfiguration } from '../index'; @@ -64,8 +63,10 @@ export class ApplicationEffects { this.logService.debug('[AppEffect][processStartInfo] payload:', appInfo); return [ new ApplyCommandLineArgsAction(appInfo.commandLineArgs), - new ConnectionStateChangedAction(appInfo.deviceConnected), - new PermissionStateChangedAction(appInfo.hasPermission), + new ConnectionStateChangedAction({ + connected: appInfo.deviceConnected, + hasPermission: appInfo.hasPermission + }), new UpdateAgentVersionInformationAction(appInfo.agentVersionInfo) ]; }); diff --git a/packages/uhk-web/src/app/store/effects/device.ts b/packages/uhk-web/src/app/store/effects/device.ts index 82f1ef45..85ce7d23 100644 --- a/packages/uhk-web/src/app/store/effects/device.ts +++ b/packages/uhk-web/src/app/store/effects/device.ts @@ -13,12 +13,11 @@ import 'rxjs/add/operator/mergeMap'; import 'rxjs/add/operator/withLatestFrom'; import 'rxjs/add/operator/switchMap'; -import { IpcResponse, NotificationType, UhkBuffer, UserConfiguration } from 'uhk-common'; +import { DeviceConnectionState, IpcResponse, NotificationType, UhkBuffer, UserConfiguration } from 'uhk-common'; import { ActionTypes, ConnectionStateChangedAction, HideSaveToKeyboardButton, - PermissionStateChangedAction, SaveConfigurationAction, SaveConfigurationReplyAction, SaveToKeyboardSuccessAction, @@ -47,35 +46,25 @@ export class DeviceEffects { deviceConnectionStateChange$: Observable = this.actions$ .ofType(ActionTypes.CONNECTION_STATE_CHANGED) .map(action => action.payload) - .do((connected: boolean) => { - if (connected) { + .do((state: DeviceConnectionState) => { + if (!state.hasPermission) { + this.router.navigate(['/privilege']); + } + else if (state.connected) { this.router.navigate(['/']); } else { this.router.navigate(['/detection']); } }) - .switchMap((connected: boolean) => { - if (connected) { + .switchMap((state: DeviceConnectionState) => { + if (state.connected && state.hasPermission) { return Observable.of(new LoadConfigFromDeviceAction()); } return Observable.empty(); }); - @Effect({dispatch: false}) - permissionStateChange$: Observable = this.actions$ - .ofType(ActionTypes.PERMISSION_STATE_CHANGED) - .map(action => action.payload) - .do((hasPermission: boolean) => { - if (hasPermission) { - this.router.navigate(['/detection']); - } - else { - this.router.navigate(['/privilege']); - } - }); - @Effect({dispatch: false}) setPrivilegeOnLinux$: Observable = this.actions$ .ofType(ActionTypes.SET_PRIVILEGE_ON_LINUX) @@ -90,8 +79,10 @@ export class DeviceEffects { .mergeMap((response: any) => { if (response.success) { return [ - new ConnectionStateChangedAction(true), - new PermissionStateChangedAction(true) + new ConnectionStateChangedAction({ + connected: true, + hasPermission: true + }) ]; } return [ diff --git a/packages/uhk-web/src/app/store/reducers/device.ts b/packages/uhk-web/src/app/store/reducers/device.ts index 96ffd261..a175e317 100644 --- a/packages/uhk-web/src/app/store/reducers/device.ts +++ b/packages/uhk-web/src/app/store/reducers/device.ts @@ -3,8 +3,8 @@ import { Action } from '@ngrx/store'; import { ActionTypes, ConnectionStateChangedAction, - PermissionStateChangedAction, - SaveConfigurationAction, UpdateFirmwareFailedAction + SaveConfigurationAction, + UpdateFirmwareFailedAction } from '../actions/device'; import { ActionTypes as AppActions, ElectronMainLogReceivedAction } from '../actions/app'; import { initProgressButtonState, ProgressButtonState } from './progress-button-state'; @@ -30,17 +30,14 @@ export const initialState: State = { export function reducer(state = initialState, action: Action) { switch (action.type) { - case ActionTypes.CONNECTION_STATE_CHANGED: + case ActionTypes.CONNECTION_STATE_CHANGED: { + const data = (action).payload; return { ...state, - connected: (action).payload - }; - - case ActionTypes.PERMISSION_STATE_CHANGED: - return { - ...state, - hasPermission: (action).payload + connected: data.connected, + hasPermission: data.hasPermission }; + } case ActionTypes.SAVING_CONFIGURATION: { return {