mirror of
https://github.com/Eugeny/tabby.git
synced 2024-12-24 11:02:47 +03:00
added hex serial input/output modes
This commit is contained in:
parent
3676b90c9f
commit
36f77c5b63
@ -20,8 +20,10 @@
|
|||||||
"@types/node": "14.14.14",
|
"@types/node": "14.14.14",
|
||||||
"@types/ssh2": "^0.5.35",
|
"@types/ssh2": "^0.5.35",
|
||||||
"ansi-colors": "^4.1.1",
|
"ansi-colors": "^4.1.1",
|
||||||
|
"binstring": "^0.2.1",
|
||||||
"buffer-replace": "^1.0.0",
|
"buffer-replace": "^1.0.0",
|
||||||
"cli-spinner": "^0.2.10"
|
"cli-spinner": "^0.2.10",
|
||||||
|
"hexer": "^1.5.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@angular/animations": "^9.1.9",
|
"@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 stripAnsi from 'strip-ansi'
|
||||||
import bufferReplace from 'buffer-replace'
|
import bufferReplace from 'buffer-replace'
|
||||||
import { BaseSession } from 'terminus-terminal'
|
import { BaseSession } from 'terminus-terminal'
|
||||||
@ -30,6 +33,7 @@ export interface SerialConnection {
|
|||||||
color?: string
|
color?: string
|
||||||
inputMode?: InputMode
|
inputMode?: InputMode
|
||||||
inputNewlines?: NewlineMode
|
inputNewlines?: NewlineMode
|
||||||
|
outputMode?: OutputMode
|
||||||
outputNewlines?: NewlineMode
|
outputNewlines?: NewlineMode
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +46,8 @@ export interface SerialPortInfo {
|
|||||||
description?: string
|
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 type NewlineMode = null | 'cr' | 'lf' | 'crlf' // eslint-disable-line @typescript-eslint/no-type-alias
|
||||||
|
|
||||||
export class SerialSession extends BaseSession {
|
export class SerialSession extends BaseSession {
|
||||||
@ -67,14 +72,14 @@ export class SerialSession extends BaseSession {
|
|||||||
input: this.inputReadlineInStream,
|
input: this.inputReadlineInStream,
|
||||||
output: this.inputReadlineOutStream,
|
output: this.inputReadlineOutStream,
|
||||||
terminal: true,
|
terminal: true,
|
||||||
|
prompt: this.connection.inputMode === 'readline-hex' ? 'hex> ' : '> ',
|
||||||
} as any)
|
} as any)
|
||||||
this.inputReadlineOutStream.on('data', data => {
|
this.inputReadlineOutStream.on('data', data => {
|
||||||
if (this.connection.inputMode === 'readline') {
|
this.emitOutput(Buffer.from(data))
|
||||||
this.emitOutput(data)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
this.inputReadline.on('line', line => {
|
this.inputReadline.on('line', line => {
|
||||||
this.onInput(new Buffer(line + '\n'))
|
this.onInput(new Buffer(line + '\n'))
|
||||||
|
this.resetInputPrompt()
|
||||||
})
|
})
|
||||||
this.output$.pipe(debounce(() => interval(500))).subscribe(() => this.onOutputSettled())
|
this.output$.pipe(debounce(() => interval(500))).subscribe(() => this.onOutputSettled())
|
||||||
}
|
}
|
||||||
@ -97,7 +102,7 @@ export class SerialSession extends BaseSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
write (data: Buffer): void {
|
write (data: Buffer): void {
|
||||||
if (this.connection.inputMode === 'readline') {
|
if (this.connection.inputMode?.startsWith('readline')) {
|
||||||
this.inputReadlineInStream.write(data)
|
this.inputReadlineInStream.write(data)
|
||||||
} else {
|
} else {
|
||||||
this.onInput(data)
|
this.onInput(data)
|
||||||
@ -156,6 +161,16 @@ export class SerialSession extends BaseSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private onInput (data: Buffer) {
|
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)
|
data = this.replaceNewlines(data, this.connection.inputNewlines)
|
||||||
if (this.serial) {
|
if (this.serial) {
|
||||||
this.serial.write(data.toString())
|
this.serial.write(data.toString())
|
||||||
@ -163,7 +178,7 @@ export class SerialSession extends BaseSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private onOutputSettled () {
|
private onOutputSettled () {
|
||||||
if (this.connection.inputMode === 'readline' && !this.inputPromptVisible) {
|
if (this.connection.inputMode?.startsWith('readline') && !this.inputPromptVisible) {
|
||||||
this.resetInputPrompt()
|
this.resetInputPrompt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -177,7 +192,7 @@ export class SerialSession extends BaseSession {
|
|||||||
private onOutput (data: Buffer) {
|
private onOutput (data: Buffer) {
|
||||||
const dataString = data.toString()
|
const dataString = data.toString()
|
||||||
|
|
||||||
if (this.connection.inputMode === 'readline') {
|
if (this.connection.inputMode?.startsWith('readline')) {
|
||||||
if (this.inputPromptVisible) {
|
if (this.inputPromptVisible) {
|
||||||
clearLine(this.inputReadlineOutStream, 0)
|
clearLine(this.inputReadlineOutStream, 0)
|
||||||
this.inputPromptVisible = false
|
this.inputPromptVisible = false
|
||||||
@ -185,7 +200,21 @@ export class SerialSession extends BaseSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data = this.replaceNewlines(data, this.connection.outputNewlines)
|
data = this.replaceNewlines(data, this.connection.outputNewlines)
|
||||||
|
|
||||||
|
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)
|
this.emitOutput(data)
|
||||||
|
}
|
||||||
|
|
||||||
if (this.scripts) {
|
if (this.scripts) {
|
||||||
let found = false
|
let found = false
|
||||||
|
@ -62,19 +62,19 @@
|
|||||||
|
|
||||||
.row
|
.row
|
||||||
.col-6
|
.col-6
|
||||||
//- .form-line
|
.form-line
|
||||||
.header
|
.header
|
||||||
.title Output mode
|
.title Output mode
|
||||||
|
|
||||||
.d-flex(ngbDropdown)
|
.d-flex(ngbDropdown)
|
||||||
button.btn.btn-secondary.btn-tab-bar(
|
button.btn.btn-secondary.btn-tab-bar(
|
||||||
ngbDropdownToggle,
|
ngbDropdownToggle,
|
||||||
) {{getInputModeName(connection.inputMode)}}
|
) {{getOutputModeName(connection.outputMode)}}
|
||||||
|
|
||||||
div(ngbDropdownMenu)
|
div(ngbDropdownMenu)
|
||||||
a.d-flex.flex-column(
|
a.d-flex.flex-column(
|
||||||
*ngFor='let mode of inputModes',
|
*ngFor='let mode of outputModes',
|
||||||
(click)='connection.inputMode = mode.key',
|
(click)='connection.outputMode = mode.key',
|
||||||
ngbDropdownItem
|
ngbDropdownItem
|
||||||
)
|
)
|
||||||
div {{mode.name}}
|
div {{mode.name}}
|
||||||
|
@ -18,6 +18,11 @@ export class EditConnectionModalComponent {
|
|||||||
inputModes = [
|
inputModes = [
|
||||||
{ key: null, name: 'Normal', description: 'Input is sent as you type' },
|
{ 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', 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 = [
|
newlineModes = [
|
||||||
{ key: null, name: 'Keep' },
|
{ key: null, name: 'Keep' },
|
||||||
@ -39,6 +44,10 @@ export class EditConnectionModalComponent {
|
|||||||
return this.inputModes.find(x => x.key === key)?.name
|
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(() => {
|
portsAutocomplete = text$ => text$.pipe(map(() => {
|
||||||
return this.foundPorts.map(x => x.name)
|
return this.foundPorts.map(x => x.name)
|
||||||
}))
|
}))
|
||||||
|
@ -35,6 +35,7 @@ export class SerialSettingsTabComponent {
|
|||||||
xoff: false,
|
xoff: false,
|
||||||
xon: false,
|
xon: false,
|
||||||
inputMode: null,
|
inputMode: null,
|
||||||
|
outputMode: null,
|
||||||
inputNewlines: null,
|
inputNewlines: null,
|
||||||
outputNewlines: null,
|
outputNewlines: null,
|
||||||
}
|
}
|
||||||
|
@ -27,11 +27,21 @@
|
|||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
"@types/ssh2-streams" "*"
|
"@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:
|
ansi-colors@^4.1.1:
|
||||||
version "4.1.1"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
|
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
|
||||||
integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
|
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:
|
buffer-replace@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/buffer-replace/-/buffer-replace-1.0.0.tgz#bc427c40af4c1f06d6933dede57110acba8ade54"
|
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"
|
version "0.2.10"
|
||||||
resolved "https://registry.yarnpkg.com/cli-spinner/-/cli-spinner-0.2.10.tgz#f7d617a36f5c47a7bc6353c697fc9338ff782a47"
|
resolved "https://registry.yarnpkg.com/cli-spinner/-/cli-spinner-0.2.10.tgz#f7d617a36f5c47a7bc6353c697fc9338ff782a47"
|
||||||
integrity sha512-U0sSQ+JJvSLi1pAYuJykwiA8Dsr15uHEy85iCJ6A+0DjVxivr3d+N2Wjvodeg89uP5K6TswFkKBfAD7B3YSn/Q==
|
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 New Issue
Block a user