diff --git a/src/dom.ts b/src/dom.ts index f132a19..fa7bc12 100644 --- a/src/dom.ts +++ b/src/dom.ts @@ -1,5 +1,6 @@ import { Converters } from "./converters"; import { Frame } from "./frame"; +import { FunctionCodes } from "./function-codes"; export class TableDataColumn { readonly td: HTMLElement = document.createElement('td'); @@ -118,10 +119,10 @@ export class Dom { static readonly sendForm = new DomForm(document.querySelector('form[name=send]')!); private static readonly functionCodeList = document.getElementById('functionCodeList')!; - static addFunctionCodeListOption(code: string, description: string): void { + static addFunctionCodeListOption(code: number): void { const option = document.createElement('option'); - option.value = code; - option.appendChild(document.createTextNode(`${Converters.byteToHex(+code)} ${description}`)); + option.value = code.toString(); + option.appendChild(document.createTextNode(FunctionCodes.getDescription(code))); Dom.functionCodeList.appendChild(option); } diff --git a/src/frame.ts b/src/frame.ts index ed78677..e4fb946 100644 --- a/src/frame.ts +++ b/src/frame.ts @@ -1,7 +1,7 @@ import { Converters } from "./converters"; import { dataFieldStrategies } from "./data-field"; import { Dom, TableColumnButton, TableDataColumn } from "./dom"; -import { getFunctionCodeDescription } from "./function-codes"; +import { FunctionCodes } from "./function-codes"; export class Frame { readonly slaveAddress: number; @@ -46,7 +46,7 @@ export class Frame { return [ new TableDataColumn(Frame.getDateTime(), this.type), new TableDataColumn(`${this.slaveAddress}=0x${Converters.byteToHex(this.slaveAddress!)}`, this.type), - new TableDataColumn(`${this.functionCode}=${this.functionCode === undefined ? '' : getFunctionCodeDescription(this.functionCode)}`, this.type), + new TableDataColumn(FunctionCodes.getDescription(this.functionCode), this.type), new TableDataColumn(this.getDataLength().toString(), this.type), new TableDataColumn(this.getDataAsText(), this.isNoValidDataFormat() ? 'error' : this.type), this.type === 'error' ? new TableDataColumn('', this.type) : new TableColumnButton('To send form', () => Dom.sendForm.setFormData(this)), @@ -57,7 +57,7 @@ export class Frame { if (this.type === 'error') { return `Invalid frame: 0x${this.hexData}`; } else if (this.isUnknownFrame()) { - return `Data field format is unknown, data field: 0x${this.hexData}`; + return `No strategies to format data field. Raw data field is: 0x${this.hexData}`; } else if (this.isNoValidDataFormat()) { return `This frame format does not fit to the known function code: fromMasterToSlaveError=${this.fromMasterToSlaveError}; fromSlaveToMasterError=${this.fromSlaveToMasterError}; for: 0x${this.hexData}`; } else { diff --git a/src/function-codes.ts b/src/function-codes.ts index bf4b37a..f6371fa 100644 --- a/src/function-codes.ts +++ b/src/function-codes.ts @@ -1,37 +1,56 @@ import { Converters } from "./converters"; +import { DataField } from "./data-field"; -export const functionCodes: { [code: number]: string } = { - 0x01: 'Read Coils', - 0x02: 'Read Discrete Inputs', - 0x03: 'Read Holding Registers', - 0x04: 'Read Input Registers', - 0x05: 'Write Single Coil', - 0x06: 'Write Single Register', - 0x07: 'Read Exceptions Status', - 0x08: 'Diagnostic Test', - 0x0f: 'Write Multiple Coils', - 0x10: 'Write Multiple Registers', - 0x11: 'Identify Device Server', -}; +interface FunctionCodeDetails { + readonly code: number; + readonly description: string; + readonly masterRequestStrategy?: DataField; + readonly slaveResponseStrategy?: DataField; +} -export const errorCodes: { [code: number]: string } = { - 0x81: 'Illegal Function', - 0x82: 'Illegal Data Address', - 0x83: 'Illegal Data Value', - 0x84: 'Server Device Failure', - 0x85: 'Acknowledge', - 0x86: 'Server Device Busy', - 0x87: 'Negative Acknowledge', - 0x88: 'Memory Parity Error', - 0x90: 'Gateway Path Unavailable', - 0x91: 'Gateway Target Device Failed to Respond', -}; +const allFunctionCodeDetails: ReadonlySet = new Set([ + { code: 0x01, description: 'Read Coils' }, + { code: 0x02, description: 'Read Discrete Inputs' }, + { code: 0x03, description: 'Read Holding Registers' }, + { code: 0x04, description: 'Read Input Registers' }, + { code: 0x05, description: 'Write Single Coil' }, + { code: 0x06, description: 'Write Single Register' }, + { code: 0x07, description: 'Read Exceptions Status' }, + { code: 0x08, description: 'Diagnostic Test' }, + { code: 0x0f, description: 'Write Multiple Coils' }, + { code: 0x10, description: 'Write Multiple Registers' }, + { code: 0x11, description: 'Identify Device Server' }, + { code: 0x81, description: 'Illegal Function' }, + { code: 0x82, description: 'Illegal Data Address' }, + { code: 0x83, description: 'Illegal Data Value' }, + { code: 0x84, description: 'Server Device Failure' }, + { code: 0x85, description: 'Acknowledge' }, + { code: 0x86, description: 'Server Device Busy' }, + { code: 0x87, description: 'Negative Acknowledge' }, + { code: 0x88, description: 'Memory Parity Error' }, + { code: 0x90, description: 'Gateway Path Unavailable' }, + { code: 0x91, description: 'Gateway Target Device Failed to Respond' }, +]); +export class FunctionCodes { + static descriptions: ReadonlyMap = new Map([...allFunctionCodeDetails].map((it) => [it.code, it.description])); -export const getFunctionCodeDescription = (code: number): string => { - const description = functionCodes[code] ?? errorCodes[code]; - if (description) { - return `0x${Converters.byteToHex(code)} ${description}`; + static getDescription(code: number | undefined): string { + if (code === undefined) { + return ''; + } + const description = this.descriptions.get(code); + if (description) { + return this._getDescription(code, description); + } + return this._getDescription(code, ''); } - return `0x${Converters.byteToHex(code)} UNKNOWN`; -} + + private static _getDescription(code: number, description: string): string { + return `${code} = 0x${Converters.byteToHex(code)} => ${description} ${this.isError(code) ? '(ERROR)' : ''}`; + } + + static isError(code: number): boolean { + return !!(code & 0x80); + } +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index db496d4..ddaa415 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,8 +3,8 @@ import { AsciiModeStrategy, ModeStrategy, RtuModeStrategy } from "./mode"; import { clearSniffingTable, Dom, downloadAllSniffedEntries, insertFrameRow } from "./dom"; import { Frame } from "./frame"; -import { errorCodes, functionCodes } from "./function-codes"; import { intTest } from "./int.spec"; +import { FunctionCodes } from "./function-codes"; const serial: Serial = navigator.serial; if (!serial) { @@ -68,7 +68,7 @@ const start = (serialOptions: SerialOptions, mode: ModeStrategy) => { }, console.warn); }; -[...Object.entries(functionCodes), ...Object.entries(errorCodes)].forEach(([code, description]) => Dom.addFunctionCodeListOption(code, description)); +[...FunctionCodes.descriptions.keys()].forEach((code: number) => Dom.addFunctionCodeListOption(code)); Dom.sendForm.submit = (formData) => { formData.slaveAddress = +formData.slaveAddress;