use new selector in serial plugin

This commit is contained in:
Eugene Pankov 2020-03-23 01:12:46 +01:00
parent 28c58d4ec0
commit db43381f0d
10 changed files with 130 additions and 160 deletions

View file

@ -9,6 +9,7 @@
.list-group.mt-3(*ngIf='filteredOptions.length')
a.list-group-item.list-group-item-action.d-flex.align-items-center(
#item,
(click)='selectOption(option)',
[class.active]='selectedIndex == i',
*ngFor='let option of filteredOptions; let i = index'

View file

@ -0,0 +1,4 @@
.list-group {
max-height: 70vh;
overflow: auto;
}

View file

@ -1,11 +1,11 @@
import { Component, Input, HostListener } from '@angular/core'
import { Component, Input, HostListener, ViewChildren, QueryList, ElementRef } 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')],
styles: [require('./selectorModal.component.scss')],
})
export class SelectorModalComponent<T> {
@Input() options: SelectorOption<T>[]
@ -13,6 +13,7 @@ export class SelectorModalComponent<T> {
@Input() filter = ''
@Input() name: string
@Input() selectedIndex = 0
@ViewChildren('item') itemChildren: QueryList<ElementRef>
constructor (
public modalInstance: NgbActiveModal,
@ -22,7 +23,7 @@ export class SelectorModalComponent<T> {
this.onFilterChange()
}
@HostListener('keyup', ['$event']) onKeyUp (event: KeyboardEvent): void {
@HostListener('keypress', ['$event']) onKeyUp (event: KeyboardEvent): void {
if (event.key === 'ArrowUp') {
this.selectedIndex--
}
@ -37,6 +38,10 @@ export class SelectorModalComponent<T> {
}
this.selectedIndex = (this.selectedIndex + this.filteredOptions.length) % this.filteredOptions.length
Array.from(this.itemChildren)[this.selectedIndex]?.nativeElement.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
})
}
onFilterChange (): void {

View file

@ -1,14 +1,13 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Injectable } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { Injectable, Injector } from '@angular/core'
import { HotkeysService, ToolbarButtonProvider, ToolbarButton } from 'terminus-core'
import { SerialModalComponent } from './components/serialModal.component'
import { SerialService } from './services/serial.service'
/** @hidden */
@Injectable()
export class ButtonProvider extends ToolbarButtonProvider {
constructor (
private ngbModal: NgbModal,
private injector: Injector,
hotkeys: HotkeysService,
) {
super()
@ -20,7 +19,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
}
activate () {
this.ngbModal.open(SerialModalComponent)
this.injector.get(SerialService).showConnectionSelector()
}
provide (): ToolbarButton[] {

View file

@ -1,32 +0,0 @@
.modal-body
input.form-control(
type='text',
[(ngModel)]='quickTarget',
autofocus,
placeholder='Quick connect: path@baudrate',
(ngModelChange)='refresh()',
(keyup.enter)='quickConnect()'
)
.list-group.mt-3(*ngIf='lastConnection')
a.list-group-item.list-group-item-action.d-flex.align-items-center((click)='connect(lastConnection)')
i.fas.fa-fw.fa-history
.mr-auto {{lastConnection.name}}
button.btn.btn-outline-danger.btn-sm((click)='clearLastConnection(); $event.stopPropagation()')
i.fas.fa-trash
.list-group.mt-3.connections-list(*ngIf='connections.length')
a.list-group-item.list-group-item-action.d-flex.align-items-center(
*ngFor='let connection of connections',
(click)='connect(connection)'
)
.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

@ -1,5 +0,0 @@
.list-group.connections-list {
display: block;
max-height: 70vh;
overflow-y: auto;
}

View file

@ -1,102 +0,0 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Component, NgZone } from '@angular/core'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { ToastrService } from 'ngx-toastr'
import { ConfigService, AppService } from 'terminus-core'
import { SettingsTabComponent } from 'terminus-settings'
import { SerialService } from '../services/serial.service'
import { SerialConnection, SerialPortInfo, BAUD_RATES } from '../api'
import { SerialTabComponent } from './serialTab.component'
/** @hidden */
@Component({
template: require('./serialModal.component.pug'),
styles: [require('./serialModal.component.scss')],
})
export class SerialModalComponent {
connections: SerialConnection[]
quickTarget: string
lastConnection: SerialConnection|null = null
foundPorts: SerialPortInfo[] = []
constructor (
public modalInstance: NgbActiveModal,
private config: ConfigService,
private serial: SerialService,
private app: AppService,
private zone: NgZone,
private toastr: ToastrService,
) { }
async ngOnInit () {
this.connections = this.config.store.serial.connections
if (window.localStorage.lastSerialConnection) {
this.lastConnection = JSON.parse(window.localStorage.lastSerialConnection)
}
this.foundPorts = await this.serial.listPorts()
}
quickConnect () {
let path = this.quickTarget
let baudrate = 115200
if (this.quickTarget.includes('@')) {
baudrate = parseInt(path.split('@')[1])
path = path.split('@')[0]
}
const connection: SerialConnection = {
name: this.quickTarget,
port: path,
baudrate: baudrate,
databits: 8,
parity: 'none',
rtscts: false,
stopbits: 1,
xany: false,
xoff: false,
xon: false,
}
window.localStorage.lastSerialConnection = JSON.stringify(connection)
this.connect(connection)
}
clearLastConnection () {
window.localStorage.lastSerialConnection = null
this.lastConnection = null
}
async connect (connection: SerialConnection) {
this.close()
try {
const tab = this.zone.run(() => this.app.openNewTab(
SerialTabComponent,
{ connection }
) as SerialTabComponent)
if (connection.color) {
(this.app.getParentTab(tab) || tab).color = connection.color
}
setTimeout(() => {
this.app.activeTab.emitFocused()
})
} catch (error) {
this.toastr.error(`Could not connect: ${error}`)
}
}
manageConnections () {
this.close()
this.app.openNewTab(SettingsTabComponent, { activeTab: 'serial' })
}
close () {
this.modalInstance.close()
}
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

@ -21,9 +21,9 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
serialPort: any
private homeEndSubscription: Subscription
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
constructor (
injector: Injector,
private serial: SerialService,
) {
super(injector)
}
@ -62,7 +62,7 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
return
}
this.session = this.serial.createSession(this.connection)
this.session = this.injector.get(SerialService).createSession(this.connection)
this.session.serviceMessage$.subscribe(msg => {
this.write('\r\n' + colors.black.bgWhite(' serial ') + ' ' + msg + '\r\n')
this.session.resize(this.size.columns, this.size.rows)
@ -80,11 +80,7 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
spinner.start()
try {
this.serialPort = await this.serial.connectSession(this.session, (message: string) => {
spinner.stop(true)
this.write(message + '\r\n')
spinner.start()
})
this.serialPort = await this.injector.get(SerialService).connectSession(this.session)
spinner.stop(true)
} catch (e) {
spinner.stop(true)

View file

@ -8,7 +8,6 @@ import { SettingsTabProvider } from 'terminus-settings'
import TerminusTerminalModule from 'terminus-terminal'
import { EditConnectionModalComponent } from './components/editConnectionModal.component'
import { SerialModalComponent } from './components/serialModal.component'
import { SerialSettingsTabComponent } from './components/serialSettingsTab.component'
import { SerialTabComponent } from './components/serialTab.component'
@ -37,13 +36,11 @@ import { SerialHotkeyProvider } from './hotkeys'
],
entryComponents: [
EditConnectionModalComponent,
SerialModalComponent,
SerialSettingsTabComponent,
SerialTabComponent,
],
declarations: [
EditConnectionModalComponent,
SerialModalComponent,
SerialSettingsTabComponent,
SerialTabComponent,
],

View file

@ -1,8 +1,10 @@
import { Injectable, NgZone } from '@angular/core'
import SerialPort from 'serialport'
import { ToastrService } from 'ngx-toastr'
import { LogService } from 'terminus-core'
import { SerialConnection, SerialSession, SerialPortInfo } from '../api'
import { LogService, AppService, SelectorOption, ConfigService } from 'terminus-core'
import { SettingsTabComponent } from 'terminus-settings'
import { SerialConnection, SerialSession, SerialPortInfo, BAUD_RATES } from '../api'
import { SerialTabComponent } from '../components/serialTab.component'
@Injectable({ providedIn: 'root' })
export class SerialService {
@ -10,6 +12,8 @@ export class SerialService {
private log: LogService,
private zone: NgZone,
private toastr: ToastrService,
private app: AppService,
private config: ConfigService,
) { }
async listPorts (): Promise<SerialPortInfo[]> {
@ -25,7 +29,7 @@ export class SerialService {
return session
}
async connectSession (session: SerialSession, _?: (s: any) => void): Promise<SerialPort> {
async connectSession (session: SerialSession): Promise<SerialPort> {
const serial = new SerialPort(session.connection.port, { autoOpen: false, baudRate: session.connection.baudrate,
dataBits: session.connection.databits, stopBits: session.connection.stopbits, parity: session.connection.parity,
rtscts: session.connection.rtscts, xon: session.connection.xon, xoff: session.connection.xoff,
@ -57,4 +61,107 @@ export class SerialService {
})
return serial
}
async showConnectionSelector (): Promise<void> {
const options: SelectorOption<void>[] = []
const lastConnection = JSON.parse(window.localStorage.lastSerialConnection)
const foundPorts = await this.listPorts()
if (lastConnection) {
options.push({
name: lastConnection.name,
icon: 'history',
callback: () => this.connect(lastConnection),
})
options.push({
name: 'Clear last connection',
icon: 'eraser',
callback: () => {
window.localStorage.lastSerialConnection = null
},
})
}
for (const port of foundPorts) {
options.push({
name: port.name,
description: port.description,
icon: 'arrow-right',
callback: () => this.connectFoundPort(port),
})
}
for (const connection of this.config.store.serial.connections) {
options.push({
name: connection.name,
description: connection.port,
callback: () => this.connect(connection),
})
}
options.push({
name: 'Manage connections',
icon: 'cog',
callback: () => this.app.openNewTab(SettingsTabComponent, { activeTab: 'serial' }),
})
options.push({
name: 'Quick connect',
freeInputPattern: 'Open device: %s...',
icon: 'arrow-right',
callback: query => this.quickConnect(query),
})
await this.app.showSelector('Open a serial port', options)
}
async connect (connection: SerialConnection): Promise<SerialTabComponent> {
try {
const tab = this.app.openNewTab(
SerialTabComponent,
{ connection }
) as SerialTabComponent
if (connection.color) {
(this.app.getParentTab(tab) || tab).color = connection.color
}
setTimeout(() => {
this.app.activeTab.emitFocused()
})
return tab
} catch (error) {
this.toastr.error(`Could not connect: ${error}`)
throw error
}
}
quickConnect (query: string): Promise<SerialTabComponent> {
let path = query
let baudrate = 115200
if (query.includes('@')) {
baudrate = parseInt(path.split('@')[1])
path = path.split('@')[0]
}
const connection: SerialConnection = {
name: query,
port: path,
baudrate: baudrate,
databits: 8,
parity: 'none',
rtscts: false,
stopbits: 1,
xany: false,
xoff: false,
xon: false,
}
window.localStorage.lastSerialConnection = JSON.stringify(connection)
return this.connect(connection)
}
async connectFoundPort (port: SerialPortInfo): Promise<SerialTabComponent> {
const rate = await this.app.showSelector('Baud rate', BAUD_RATES.map(x => ({
name: x.toString(), result: x,
})))
return this.quickConnect(`${port.name}@${rate}`)
}
}