Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 38 additions & 14 deletions packages/uhk-usb/src/uhk-operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -812,29 +812,53 @@ export class UhkOperations {
return convertSlaveI2cErrorBuffer(responseBuffer, slaveId);
}

public async getVariable(variableId: UsbVariables, iteration: number = 0): Promise<number | string> {
this.logService.usbOps(`[DeviceOperation] USB[T]: get variable: ${UsbVariables[variableId]}. Iteration: ${iteration}`);
public async getVariable(variableId: UsbVariables): Promise<number | string> {
if (variableId === UsbVariables.statusBuffer || variableId === UsbVariables.ShellBuffer) {
return this.getVariableWithIteration(variableId);
}

this.logService.usbOps(`[DeviceOperation] USB[T]: get variable: ${UsbVariables[variableId]}`);
const buffer = Buffer.from([UsbCommand.GetVariable, variableId]);
const responseBuffer = await this.device.write(buffer);

if (variableId === UsbVariables.statusBuffer || variableId === UsbVariables.ShellBuffer) {
let message = readUhkResponseAs0EndString(UhkBuffer.fromArray(convertBufferToIntArray(responseBuffer)));
this.logService.misc(`[DeviceOperation] status buffer segment: ${message}`);
if (message.length === responseBuffer.length - 1 && iteration < 20) {
message += await this.getVariable(variableId, iteration + 1);
return responseBuffer[1];
}

private async getVariableWithIteration(variableId: UsbVariables): Promise<string> {
// Firmware status buffer is STATUS_BUFFER_MAX_LENGTH (3000); shell buffer is 2048.
// Each USB transfer returns at most (report length - 1) payload bytes (~62).
// The buffers are NUL terminated strings.
// The maxIterations is a safeguard against infinite loops.
const maxIterations = 100;
let message = '';

for (let iteration = 0; iteration < maxIterations; iteration++) {
this.logService.usbOps(`[DeviceOperation] USB[T]: get variable: ${UsbVariables[variableId]}. Iteration: ${iteration}`);
const buffer = Buffer.from([UsbCommand.GetVariable, variableId]);
const responseBuffer = await this.device.write(buffer);
const segment = readUhkResponseAs0EndString(UhkBuffer.fromArray(convertBufferToIntArray(responseBuffer)));
this.logService.misc(`[DeviceOperation] status buffer segment: ${segment}`);
message += segment;

// The content of the variable is a NUL terminated string.
// When the segment length is not equal to the buffer length - 1, the buffer is complete.
if (segment.length !== responseBuffer.length - 1) {
break;
}

// The shell buffer carries a raw VT100 stream (colors, cursor control) that must be
// forwarded verbatim to the terminal emulator. Only the macro status buffer gets the
// dedup/reorder normalization.
if (iteration === 0 && variableId === UsbVariables.statusBuffer) {
message = normalizeStatusBuffer(message);
if (iteration === maxIterations) {
this.logService.error(`[DeviceOperation] ${UsbVariables[variableId]} truncated after ${maxIterations} USB transfers`);
}
}

return message;
// The shell buffer carries a raw VT100 stream (colors, cursor control) that must be
// forwarded verbatim to the terminal emulator. Only the macro status buffer gets the
// dedup/reorder normalization.
if (variableId === UsbVariables.statusBuffer) {
message = normalizeStatusBuffer(message);
}

return responseBuffer[1];
return message;
}

public async pairToDongle(dongle: UhkHidDevice) : Promise<BleAddressPair> {
Expand Down
7 changes: 6 additions & 1 deletion packages/uhk-web/src/app/util/status-buffer-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ function transformToErrorBlock(macros: Macro[], block: string): string {

const url = `#/macro/${macro.id}?actionIndex=${macroActionIndex}&lineNr=${lineNr}&columnNr=${columnNr}&inlineEdit=true`;
const newLine2 = `${escapeHtml(line1Result[1])}<a href="${url}">${escapeHtml(line1Result[2])}</a>`;
// Keep binding-site lines and nested location-stack lines (firmware may emit more than 3).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a future plan would be nice to write some test for these usecases

const extraLines = lines
.slice(3)
.map(line => escapeHtml(line))
.join('\n');

return `${escapeHtml(lines[0])}\n${newLine2}\n${escapeHtml(lines[2])}\n`;
return `${escapeHtml(lines[0])}\n${newLine2}\n${escapeHtml(lines[2])}\n${extraLines}`;
}