Skip to content

Commit

Permalink
Refactor function code descriptions
Browse files Browse the repository at this point in the history
  • Loading branch information
adrianscheit committed Feb 6, 2025
1 parent 0e0840a commit f1c312f
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 39 deletions.
7 changes: 4 additions & 3 deletions src/dom.ts
Original file line number Diff line number Diff line change
@@ -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');
Expand Down Expand Up @@ -118,10 +119,10 @@ export class Dom {
static readonly sendForm = new DomForm<SendFormData>(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);
}

Expand Down
6 changes: 3 additions & 3 deletions src/frame.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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)),
Expand All @@ -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 {
Expand Down
81 changes: 50 additions & 31 deletions src/function-codes.ts
Original file line number Diff line number Diff line change
@@ -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<FunctionCodeDetails> = new Set<FunctionCodeDetails>([
{ 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<number, string> = new Map<number, string>([...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, '<UNKNOWN>');
}
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);
}
}
4 changes: 2 additions & 2 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit f1c312f

Please sign in to comment.