added serial port detection

This commit is contained in:
Eugene Pankov 2020-03-01 12:15:02 +01:00
parent f5ffdc1707
commit 10b21ee085
12 changed files with 146 additions and 9 deletions

View File

@ -7,6 +7,7 @@ export { ConfigProvider } from './configProvider'
export { HotkeyProvider, HotkeyDescription } from './hotkeyProvider'
export { Theme } from './theme'
export { TabContextMenuItemProvider } from './tabContextMenuProvider'
export { SelectorOption } from './selector'
export { AppService } from '../services/app.service'
export { ConfigService } from '../services/config.service'

View File

@ -0,0 +1,5 @@
export interface SelectorOption<T> {
name: string
description?: string
result: T
}

View File

@ -0,0 +1,18 @@
.modal-body
input.form-control(
type='text',
[(ngModel)]='filter',
autofocus,
[placeholder]='name',
(ngModelChange)='onFilterChange()',
(keyup.enter)='onFilterEnter()',
(keyup.escape)='close()'
)
.list-group.mt-3(*ngIf='filteredOptions.length')
a.list-group-item.list-group-item-action.d-flex.align-items-center(
(click)='selectOption(option)',
*ngFor='let option of filteredOptions'
)
.mr-2 {{option.name}}
.text-muted {{option.description}}

View File

@ -0,0 +1,47 @@
import { Component, Input } from '@angular/core'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { SelectorOption } from '../api/selector'
/** @hidden */
@Component({
template: require('./selectorModal.component.pug'),
// styles: [require('./selectorModal.component.scss')],
})
export class SelectorModalComponent<T> {
@Input() options: SelectorOption<T>[]
@Input() filteredOptions: SelectorOption<T>[]
@Input() filter = ''
@Input() name: string
constructor (
public modalInstance: NgbActiveModal,
) { }
ngOnInit () {
this.onFilterChange()
}
onFilterChange () {
const f = this.filter.trim().toLowerCase()
if (!f) {
this.filteredOptions = this.options
} else {
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
this.filteredOptions = this.options.filter(x => (x.name + (x.description || '')).toLowerCase().includes(f))
}
}
onFilterEnter () {
if (this.filteredOptions.length === 1) {
this.selectOption(this.filteredOptions[0])
}
}
selectOption (option: SelectorOption<T>) {
this.modalInstance.close(option.result)
}
close () {
this.modalInstance.dismiss()
}
}

View File

@ -16,6 +16,7 @@ import { TitleBarComponent } from './components/titleBar.component'
import { ToggleComponent } from './components/toggle.component'
import { WindowControlsComponent } from './components/windowControls.component'
import { RenameTabModalComponent } from './components/renameTabModal.component'
import { SelectorModalComponent } from './components/selectorModal.component'
import { SplitTabComponent, SplitTabRecoveryProvider } from './components/splitTab.component'
import { SplitTabSpannerComponent } from './components/splitTabSpanner.component'
import { WelcomeTabComponent } from './components/welcomeTab.component'
@ -82,6 +83,7 @@ const PROVIDERS = [
SafeModeModalComponent,
AutofocusDirective,
FastHtmlBindDirective,
SelectorModalComponent,
SplitTabComponent,
SplitTabSpannerComponent,
WelcomeTabComponent,
@ -89,6 +91,7 @@ const PROVIDERS = [
entryComponents: [
RenameTabModalComponent,
SafeModeModalComponent,
SelectorModalComponent,
SplitTabComponent,
WelcomeTabComponent,
],

View File

@ -2,9 +2,12 @@
import { Observable, Subject, AsyncSubject } from 'rxjs'
import { takeUntil } from 'rxjs/operators'
import { Injectable } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { BaseTabComponent } from '../components/baseTab.component'
import { SplitTabComponent } from '../components/splitTab.component'
import { SelectorModalComponent } from '../components/selectorModal.component'
import { SelectorOption } from '../api/selector'
import { ConfigService } from './config.service'
import { HostAppService } from './hostApp.service'
@ -69,6 +72,7 @@ export class AppService {
private hostApp: HostAppService,
private tabRecovery: TabRecoveryService,
private tabsService: TabsService,
private ngbModal: NgbModal,
) {
if (hostApp.getWindow().id === 1) {
if (config.store.terminal.recoverTabs) {
@ -315,4 +319,12 @@ export class AppService {
stopObservingTabCompletion (tab: BaseTabComponent) {
this.completionObservers.delete(tab)
}
showSelector <T> (name: string, options: SelectorOption<T>[]): Promise<T> {
const modal = this.ngbModal.open(SelectorModalComponent)
const instance: SelectorModalComponent<T> = modal.componentInstance
instance.name = name
instance.options = options
return modal.result as Promise<T>
}
}

View File

@ -26,6 +26,15 @@ export interface SerialConnection {
color?: string
}
export const BAUD_RATES = [
110, 150, 300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600,
]
export interface SerialPortInfo {
name: string
description?: string
}
export class SerialSession extends BaseSession {
scripts?: LoginScript[]
serial: SerialPort

View File

@ -24,14 +24,16 @@
input.form-control(
type='text',
[(ngModel)]='connection.port',
[ngbTypeahead]='portsAutocomplete',
[resultFormatter]='portsFormatter',
)
.form-group
label Baud Rate
input.form-control(
type='number',
select.form-control(
[(ngModel)]='connection.baudrate',
)
option([value]='x', *ngFor='let x of baudRates') {{x}}
ngb-tab(id='advanced')
ng-template(ngbTabTitle) Advanced

View File

@ -1,7 +1,9 @@
import { Component } from '@angular/core'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { map } from 'rxjs/operators'
import { ElectronService, HostAppService } from 'terminus-core'
import { SerialConnection, LoginScript } from '../api'
import { SerialConnection, LoginScript, SerialPortInfo, BAUD_RATES } from '../api'
import { SerialService } from '../services/serial.service'
// import { PromptModalComponent } from './promptModal.component'
/** @hidden */
@ -10,17 +12,32 @@ import { SerialConnection, LoginScript } from '../api'
})
export class EditConnectionModalComponent {
connection: SerialConnection
foundPorts: SerialPortInfo[]
baudRates = BAUD_RATES
constructor (
private modalInstance: NgbActiveModal,
private electron: ElectronService,
private hostApp: HostAppService,
// private ngbModal: NgbModal,
private serial: SerialService,
) {
}
portsAutocomplete = text$ => text$.pipe(map(() => {
return this.foundPorts.map(x => x.name)
}))
portsFormatter = port => {
const p = this.foundPorts.find(x => x.name === port)
if (p?.description) {
return `${port} (${p.description})`
}
return port
}
async ngOnInit () {
this.connection.scripts = this.connection.scripts || []
this.foundPorts = await this.serial.listPorts()
}
save () {

View File

@ -30,3 +30,11 @@
)
.mr-2 {{connection.name}}
.text-muted {{connection.port}}
.list-group.mt-3(*ngIf='foundPorts.length')
a.list-group-item.list-group-item-action.d-flex.align-items-center(
(click)='connectFoundPort(port)',
*ngFor='let port of foundPorts'
)
.mr-2 {{port.name}}
.text-muted {{port.description}}

View File

@ -4,7 +4,7 @@ import { ToastrService } from 'ngx-toastr'
import { ConfigService, AppService } from 'terminus-core'
import { SettingsTabComponent } from 'terminus-settings'
import { SerialService } from '../services/serial.service'
import { SerialConnection, SerialConnectionGroup } from '../api'
import { SerialConnection, SerialConnectionGroup, SerialPortInfo, BAUD_RATES } from '../api'
/** @hidden */
@Component({
@ -18,6 +18,7 @@ export class SerialModalComponent {
lastConnection: SerialConnection|null = null
childGroups: SerialConnectionGroup[]
groupCollapsed: {[id: string]: boolean} = {}
foundPorts: SerialPortInfo[] = []
constructor (
public modalInstance: NgbActiveModal,
@ -27,12 +28,14 @@ export class SerialModalComponent {
private toastr: ToastrService,
) { }
ngOnInit () {
async ngOnInit () {
this.connections = this.config.store.serial.connections
if (window.localStorage.lastSerialConnection) {
this.lastConnection = JSON.parse(window.localStorage.lastSerialConnection)
}
this.refresh()
this.foundPorts = await this.serial.listPorts()
}
quickConnect () {
@ -105,4 +108,12 @@ export class SerialModalComponent {
group.connections.push(connection)
}
}
async connectFoundPort (port: SerialPortInfo) {
const rate = await this.app.showSelector('Baud rate', BAUD_RATES.map(x => ({
name: x.toString(), result: x,
})))
this.quickTarget = `${port.name}@${rate}`
this.quickConnect()
}
}

View File

@ -2,19 +2,23 @@ import { Injectable, NgZone } from '@angular/core'
import SerialPort from 'serialport'
import { ToastrService } from 'ngx-toastr'
import { AppService, LogService } from 'terminus-core'
import { SerialConnection, SerialSession } from '../api'
import { SerialConnection, SerialSession, SerialPortInfo } from '../api'
import { SerialTabComponent } from '../components/serialTab.component'
@Injectable({ providedIn: 'root' })
export class SerialService {
private constructor (
private log: LogService,
private app: AppService,
private zone: NgZone,
private toastr: ToastrService,
) {
) { }
async listPorts (): Promise<SerialPortInfo[]> {
return (await SerialPort.list()).map(x => ({
name: x.path,
description: x.manufacturer || x.serialNumber ? `${x.manufacturer || ''} ${x.serialNumber || ''}` : undefined,
}))
}
async openTab (connection: SerialConnection): Promise<SerialTabComponent> {