mirror of
https://github.com/Eugeny/tabby
synced 2024-11-15 01:17:14 +00:00
added hex serial input/output modes
This commit is contained in:
parent
3676b90c9f
commit
36f77c5b63
6 changed files with 89 additions and 13 deletions
|
@ -20,8 +20,10 @@
|
|||
"@types/node": "14.14.14",
|
||||
"@types/ssh2": "^0.5.35",
|
||||
"ansi-colors": "^4.1.1",
|
||||
"binstring": "^0.2.1",
|
||||
"buffer-replace": "^1.0.0",
|
||||
"cli-spinner": "^0.2.10"
|
||||
"cli-spinner": "^0.2.10",
|
||||
"hexer": "^1.5.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/animations": "^9.1.9",
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import hexdump from 'hexer'
|
||||
import colors from 'ansi-colors'
|
||||
import binstring from 'binstring'
|
||||
import stripAnsi from 'strip-ansi'
|
||||
import bufferReplace from 'buffer-replace'
|
||||
import { BaseSession } from 'terminus-terminal'
|
||||
|
@ -30,6 +33,7 @@ export interface SerialConnection {
|
|||
color?: string
|
||||
inputMode?: InputMode
|
||||
inputNewlines?: NewlineMode
|
||||
outputMode?: OutputMode
|
||||
outputNewlines?: NewlineMode
|
||||
}
|
||||
|
||||
|
@ -42,7 +46,8 @@ export interface SerialPortInfo {
|
|||
description?: string
|
||||
}
|
||||
|
||||
export type InputMode = null | 'readline' // eslint-disable-line @typescript-eslint/no-type-alias
|
||||
export type InputMode = null | 'readline' | 'readline-hex' // eslint-disable-line @typescript-eslint/no-type-alias
|
||||
export type OutputMode = null | 'hex' // eslint-disable-line @typescript-eslint/no-type-alias
|
||||
export type NewlineMode = null | 'cr' | 'lf' | 'crlf' // eslint-disable-line @typescript-eslint/no-type-alias
|
||||
|
||||
export class SerialSession extends BaseSession {
|
||||
|
@ -67,14 +72,14 @@ export class SerialSession extends BaseSession {
|
|||
input: this.inputReadlineInStream,
|
||||
output: this.inputReadlineOutStream,
|
||||
terminal: true,
|
||||
prompt: this.connection.inputMode === 'readline-hex' ? 'hex> ' : '> ',
|
||||
} as any)
|
||||
this.inputReadlineOutStream.on('data', data => {
|
||||
if (this.connection.inputMode === 'readline') {
|
||||
this.emitOutput(data)
|
||||
}
|
||||
this.emitOutput(Buffer.from(data))
|
||||
})
|
||||
this.inputReadline.on('line', line => {
|
||||
this.onInput(new Buffer(line + '\n'))
|
||||
this.resetInputPrompt()
|
||||
})
|
||||
this.output$.pipe(debounce(() => interval(500))).subscribe(() => this.onOutputSettled())
|
||||
}
|
||||
|
@ -97,7 +102,7 @@ export class SerialSession extends BaseSession {
|
|||
}
|
||||
|
||||
write (data: Buffer): void {
|
||||
if (this.connection.inputMode === 'readline') {
|
||||
if (this.connection.inputMode?.startsWith('readline')) {
|
||||
this.inputReadlineInStream.write(data)
|
||||
} else {
|
||||
this.onInput(data)
|
||||
|
@ -156,6 +161,16 @@ export class SerialSession extends BaseSession {
|
|||
}
|
||||
|
||||
private onInput (data: Buffer) {
|
||||
if (this.connection.inputMode === 'readline-hex') {
|
||||
const tokens = data.toString().split(/\s/g)
|
||||
data = Buffer.concat(tokens.filter(t => !!t).map(t => {
|
||||
if (t.startsWith('0x')) {
|
||||
t = t.substring(2)
|
||||
}
|
||||
return binstring(t, { 'in': 'hex' })
|
||||
}))
|
||||
}
|
||||
|
||||
data = this.replaceNewlines(data, this.connection.inputNewlines)
|
||||
if (this.serial) {
|
||||
this.serial.write(data.toString())
|
||||
|
@ -163,7 +178,7 @@ export class SerialSession extends BaseSession {
|
|||
}
|
||||
|
||||
private onOutputSettled () {
|
||||
if (this.connection.inputMode === 'readline' && !this.inputPromptVisible) {
|
||||
if (this.connection.inputMode?.startsWith('readline') && !this.inputPromptVisible) {
|
||||
this.resetInputPrompt()
|
||||
}
|
||||
}
|
||||
|
@ -177,7 +192,7 @@ export class SerialSession extends BaseSession {
|
|||
private onOutput (data: Buffer) {
|
||||
const dataString = data.toString()
|
||||
|
||||
if (this.connection.inputMode === 'readline') {
|
||||
if (this.connection.inputMode?.startsWith('readline')) {
|
||||
if (this.inputPromptVisible) {
|
||||
clearLine(this.inputReadlineOutStream, 0)
|
||||
this.inputPromptVisible = false
|
||||
|
@ -185,7 +200,21 @@ export class SerialSession extends BaseSession {
|
|||
}
|
||||
|
||||
data = this.replaceNewlines(data, this.connection.outputNewlines)
|
||||
this.emitOutput(data)
|
||||
|
||||
if (this.connection.outputMode === 'hex') {
|
||||
this.emitOutput(Buffer.concat([
|
||||
new Buffer('\r\n'),
|
||||
Buffer.from(hexdump(data, {
|
||||
group: 1,
|
||||
gutter: 4,
|
||||
divide: colors.gray(' | '),
|
||||
emptyHuman: colors.gray('╳'),
|
||||
}).replace(/\n/g, '\r\n')),
|
||||
new Buffer('\r\n\n'),
|
||||
]))
|
||||
} else {
|
||||
this.emitOutput(data)
|
||||
}
|
||||
|
||||
if (this.scripts) {
|
||||
let found = false
|
||||
|
|
|
@ -62,19 +62,19 @@
|
|||
|
||||
.row
|
||||
.col-6
|
||||
//- .form-line
|
||||
.form-line
|
||||
.header
|
||||
.title Output mode
|
||||
|
||||
.d-flex(ngbDropdown)
|
||||
button.btn.btn-secondary.btn-tab-bar(
|
||||
ngbDropdownToggle,
|
||||
) {{getInputModeName(connection.inputMode)}}
|
||||
) {{getOutputModeName(connection.outputMode)}}
|
||||
|
||||
div(ngbDropdownMenu)
|
||||
a.d-flex.flex-column(
|
||||
*ngFor='let mode of inputModes',
|
||||
(click)='connection.inputMode = mode.key',
|
||||
*ngFor='let mode of outputModes',
|
||||
(click)='connection.outputMode = mode.key',
|
||||
ngbDropdownItem
|
||||
)
|
||||
div {{mode.name}}
|
||||
|
|
|
@ -18,6 +18,11 @@ export class EditConnectionModalComponent {
|
|||
inputModes = [
|
||||
{ key: null, name: 'Normal', description: 'Input is sent as you type' },
|
||||
{ key: 'readline', name: 'Line by line', description: 'Line editor, input is sent after you press Enter' },
|
||||
{ key: 'readline-hex', name: 'Hexadecimal', description: 'Send bytes by typing in hex values' },
|
||||
]
|
||||
outputModes = [
|
||||
{ key: null, name: 'Normal', description: 'Output is shown as it is received' },
|
||||
{ key: 'hex', name: 'Hexadecimal', description: 'Output is shown as a hexdump' },
|
||||
]
|
||||
newlineModes = [
|
||||
{ key: null, name: 'Keep' },
|
||||
|
@ -39,6 +44,10 @@ export class EditConnectionModalComponent {
|
|||
return this.inputModes.find(x => x.key === key)?.name
|
||||
}
|
||||
|
||||
getOutputModeName (key) {
|
||||
return this.outputModes.find(x => x.key === key)?.name
|
||||
}
|
||||
|
||||
portsAutocomplete = text$ => text$.pipe(map(() => {
|
||||
return this.foundPorts.map(x => x.name)
|
||||
}))
|
||||
|
|
|
@ -35,6 +35,7 @@ export class SerialSettingsTabComponent {
|
|||
xoff: false,
|
||||
xon: false,
|
||||
inputMode: null,
|
||||
outputMode: null,
|
||||
inputNewlines: null,
|
||||
outputNewlines: null,
|
||||
}
|
||||
|
|
|
@ -27,11 +27,21 @@
|
|||
"@types/node" "*"
|
||||
"@types/ssh2-streams" "*"
|
||||
|
||||
ansi-color@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-color/-/ansi-color-0.2.1.tgz#3e75c037475217544ed763a8db5709fa9ae5bf9a"
|
||||
integrity sha1-PnXAN0dSF1RO12Oo21cJ+prlv5o=
|
||||
|
||||
ansi-colors@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
|
||||
integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
|
||||
|
||||
binstring@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/binstring/-/binstring-0.2.1.tgz#8a174d301f6d54efda550dd98bb4cb524eacd75d"
|
||||
integrity sha1-ihdNMB9tVO/aVQ3Zi7TLUk6s110=
|
||||
|
||||
buffer-replace@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/buffer-replace/-/buffer-replace-1.0.0.tgz#bc427c40af4c1f06d6933dede57110acba8ade54"
|
||||
|
@ -41,3 +51,28 @@ cli-spinner@^0.2.10:
|
|||
version "0.2.10"
|
||||
resolved "https://registry.yarnpkg.com/cli-spinner/-/cli-spinner-0.2.10.tgz#f7d617a36f5c47a7bc6353c697fc9338ff782a47"
|
||||
integrity sha512-U0sSQ+JJvSLi1pAYuJykwiA8Dsr15uHEy85iCJ6A+0DjVxivr3d+N2Wjvodeg89uP5K6TswFkKBfAD7B3YSn/Q==
|
||||
|
||||
hexer@^1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/hexer/-/hexer-1.5.0.tgz#b86ce808598e8a9d1892c571f3cedd86fc9f0653"
|
||||
integrity sha1-uGzoCFmOip0YksVx887dhvyfBlM=
|
||||
dependencies:
|
||||
ansi-color "^0.2.1"
|
||||
minimist "^1.1.0"
|
||||
process "^0.10.0"
|
||||
xtend "^4.0.0"
|
||||
|
||||
minimist@^1.1.0:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
||||
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
||||
|
||||
process@^0.10.0:
|
||||
version "0.10.1"
|
||||
resolved "https://registry.yarnpkg.com/process/-/process-0.10.1.tgz#842457cc51cfed72dc775afeeafb8c6034372725"
|
||||
integrity sha1-hCRXzFHP7XLcd1r+6vuMYDQ3JyU=
|
||||
|
||||
xtend@^4.0.0:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
|
||||
|
|
Loading…
Reference in a new issue