Skip to content

Commit

Permalink
testloopback with DZRP.
Browse files Browse the repository at this point in the history
  • Loading branch information
maziac committed Jun 14, 2020
1 parent a412d6e commit 9ba8b9a
Show file tree
Hide file tree
Showing 9 changed files with 205 additions and 95 deletions.
5 changes: 3 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
"program": "${workspaceFolder}/out/main.js",
"args": [
"-socket", "12000",
"-serial", "/dev/cu.usbserial-AQ007PCD",
//"-serial", "/dev/cu.usbserial-AQ007PCD",
"-serial", "/dev/cu.usbserial-14610",
//"-baudrate", "921600",
//"-testserial", "2", "1000"
"-testloopback", "2", "1000"
//"-test"
],
"cwd": "${workspaceRoot}",
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## 0.4.0
Added command line option to do a loopback test with the serial port with dezogif through dzrp.

## 0.3.0
Reconnects now after disconnect.

Expand Down
18 changes: 16 additions & 2 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ The dezogserialinterface is a commandline program and offers a few options:
- -socket port: If you need to use another socket port than the default 12000 then you can change it here.
- -serial serial_if: Enter here the name of the serial port.
- -test: This tests if socket and serial port could be opened and prints a success or error message. Please use this to test if your serial port device can be accessed.
- -testloopback: Sends data to the serial port and receives data. The dezogif.nex on the ZX Next will return the packets. I.e. this is a standalone test to test communication over UART/serial.

The program stays there and waits on a connection from DeZog. DeZog starts the connection when a debug session is entered. It closes the connection when the debug session is terminated.
dezogserialinterface will not terminate but wait for the next connection from DeZog.
Expand All @@ -36,7 +37,7 @@ Using socket=12000, serial=/dev/cu.usbserial-14610, baudrate=921600
Waiting for connection on port 12000
```

Test that serial port is found. Port found:
Test that serial port is found. The USB/serial device needs to be plugged into a USB port but does not need to be connected to anything. Port found:
```
./dezogserialinterface-macos -socket 12000 -serial /dev/cu.usbserial-14610 -test
Socket port 12000 OK.
Expand All @@ -50,7 +51,20 @@ Socket port 12000 OK.
Serial interface /dev/cu.usbserial-14610 @921600baud Error: [Error: Error: No such file or directory, cannot open /dev/cu.usbserial-14610]
```


Loopback test. The USB/serial device needs to be plugged into a USB port and needs to be conencted to the ZX Next (ESP) UART:
```
node out/main.js -socket 12000 -serial /dev/cu.usbserial-AQ007PCD -testloopback 2 200
USB-Serial connection opened!
Draining.
Serial interface '/dev/cu.usbserial-AQ007PCD' @921600 baud.
Bytes sent: 19000
Bytes received: 18894
Bytes/ms: 9.447
Packets sent: 95
Packets received: 95
Packets/s: 47.5
Sucessful. No error.
```

Hints:
- On macos you can attach your USB/serial device and use ```ls /dev/cu*``` to find out it's name. E.g. /dev/cu.usbserial-AQ007PCD
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dezogserialif",
"version": "0.3.0",
"version": "0.4.0",
"description": "Program to allow DeZog communicating with a ZX Next through a serial interface.",
"main": "./out/main.js",
"scripts": {
Expand Down
146 changes: 146 additions & 0 deletions src/dzrpparser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
//import {Utility} from '../../misc/utility';
const {Transform}=require('stream');




/**
* This parser reads the first 4 bytes and interprets it as (little endian) length.
* Then it collects 'length' further bytes.
* When all bytes have been received the data is emitted.
* This is the basic DZRP format.
* Is used here only for loopback testing.
*/
export class DzrpParser extends Transform {
/// State: Either waiting for length (false) or collecting data (true).
protected collectingData: boolean;

/// The number of remaining bytes to collect.
protected remainingLength: number;

/// Timeout. Max time between chunks.
protected timeout=1000; // ms

/// The timer.
protected timer;

// Name, for debugging purposes.
protected name: string|undefined;


/// The constructor.
/// @param name Add a name for debugging purposes.
constructor(options={}, name?: string) {
super(options);
//Utility.assert(options);

// Timeout
if ((options as any).timeout!=undefined)
this.timeout=(options as any).timeout;

// Alloc buffer
this.buffer=Buffer.alloc(0);
this.collectingData=false;
this.remainingLength=0;
this.timer=undefined;
this.name=name;
}


/**
* Creates an error text.
* I.e. Adds name and some constant text to it.
*/
public ErrorWithText(text: string): Error {
let wholeText="DZRP";
if (this.name)
wholeText+=" ("+this.name+")";
wholeText+=": "+text;
const err=new Error(wholeText);
return err;
}


/**
* Clears the timer.
*/
public clearTimer() {
clearTimeout(this.timer); // Stop previous timer.
this.timer=undefined;
}


/**
* Should be started a soon as a response is expected.
* @param errorText The text that is emitted if the timer expires.
*/
public startTimer(errorText: string) {
clearTimeout(this.timer); // Stop previous timer.
this.timer=setTimeout(() => {
this.emit('error', this.ErrorWithText('Timeout: ' + errorText));
}, this.timeout);
}


/**
* Read chunks of data until a complete message has been received.
*/
_transform(chunk, encoding, cb) {
// Stop timer
//console.log(this.name, "0 clear timer, remainingLength=", this.remainingLength, "chunk=", chunk.length, ", buffer=", this.buffer.length, chunk);
clearTimeout(this.timer);
this.timer=undefined;
// Concat data
this.buffer=Buffer.concat([this.buffer, chunk])
while (true) {
// Check state
if (!this.collectingData) {
// Check if all 4 bytes have been received
if (this.buffer.length<4)
break;
const data=this.buffer;
this.remainingLength=data[0]+(data[1]<<8)+(data[2]<<16)+(data[3]*256*65536); // Note: <<24 might return a negative value
//console.log(this.name, "0b new message, (remaining)Length=", this.remainingLength);
this.buffer=this.buffer.subarray(4);
this.collectingData=true;
}

// Collect until all remaining bytes received
const count=this.buffer.length;
if (count<this.remainingLength)
break;

// Enough data
this.collectingData=false;

// Check if there was too many data received
let data=this.buffer;
if (count>this.remainingLength) {
data=data.subarray(0, this.remainingLength);
}
// Enough data collected
//console.log(this.name, "1 push, remainingLength=", this.remainingLength, ", buffer=", this.buffer.length);
this.push(data);
//console.log(this.name, "2a remainingLength=", this.remainingLength, ", buffer=", this.buffer.length);
this.buffer=this.buffer.subarray(this.remainingLength); // Normally clears the buffer
this.remainingLength=this.buffer.length;
//console.log(this.name, "2b remainingLength=", this.remainingLength, ", buffer=", this.buffer.length);
}
// Start timeout
if (this.remainingLength>0) {
this.startTimer('Too much time between two data chunks.');
//console.log(this.name, "3a set timer");
}
// Call callback
//console.log(this.name, "3b return, remainingLength=", this.remainingLength, ", buffer=", this.buffer.length);
cb();
}


_flush(cb) {
this.push(this.buffer)
this.buffer=Buffer.alloc(0)
cb()
}
}

38 changes: 27 additions & 11 deletions src/interfacetests.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as tcpPortUsed from 'tcp-port-used';
import * as SerialPort from 'serialport';
import {WrapperParser} from './wrapperparser';
import {UsbSerial} from './usbserial';
import {DzrpParser} from './dzrpparser';



Expand Down Expand Up @@ -97,13 +97,18 @@ export class InterfaceTests {
resolve();
}, 1000);
// Length was removed.
// Check data.
// Check data.
let k=0;
for (const recByte of data) {
lastByteReceived=(lastByteReceived+1)&0xFF;
if (recByte!=lastByteReceived) {
console.log("Wrong data received after "+bytesReceived+" received bytes");
resolve();
return;
// Skip Seqno
k++;
if (k>1) {
lastByteReceived=(lastByteReceived+1)&0xFF;
if (recByte!=lastByteReceived) {
console.log("Wrong data received after "+bytesReceived+" received bytes");
resolve();
return;
}
}
}
bytesReceived+=data.length;
Expand All @@ -112,10 +117,21 @@ export class InterfaceTests {
batchReceived-=batchSize;
packetsReceived++;
// Send next data
const buffer=new Uint8Array(batchSize);
const buffer=new Uint8Array(batchSize+4+2); // +Length+SeqNo+Command
let k=0;
// Length
const length=2+batchSize;
buffer[k++]=length&0xFF;
buffer[k++]=(length>>>8)&0xFF;
buffer[k++]=0;
buffer[k++]=0;
// SeqNo and command
buffer[k++]=1;
buffer[k++]=15; // CMD_LOOPBACK
// Data
for (let i=0; i<batchSize; i++) {
lastByteSent=(lastByteSent+1)&0xFF;
buffer[i]=lastByteSent;
buffer[k++]=lastByteSent;
}
bytesSent+=batchSize;
packetsSent++;
Expand All @@ -130,7 +146,7 @@ export class InterfaceTests {
}, 1000);

// Open
const parser=new WrapperParser({}, 'ZxNext Serial')
const parser=new DzrpParser({}, 'DZRP loopback')
await serialPort.open(parser);

// Success
Expand All @@ -150,7 +166,7 @@ export class InterfaceTests {
}, time*1000);

// Send first batch
serialPort.emit('data', []);
serialPort.emit('data', Buffer.from([]));
}
catch (e) {
clearTimeout(timeout);
Expand Down
8 changes: 5 additions & 3 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ $ cmdsocket -socket 12000
Starts to listen on port 12000.
General usage:
cmdsocket -socket port -serial serial_if -baudrate rate [-log] [-test] [-testserial time]
cmdsocket -socket port -serial serial_if -baudrate rate [-log] [-test] [-testloopback time]
options:
-h|-help: Prints this help.
-v|-version: Prints the version number.
Expand All @@ -85,7 +85,7 @@ cmdsocket -socket port -serial serial_if -baudrate rate [-log] [-test] [-testser
-log: Enables logging to console.
-test: Use as last argument. If given the program tries to open a
socket and a serial connection. Just to see if it could work.
-testserial time batch-size: Test the serial connection. The remote side needs to loop back
-testloopback time batch-size: Test the serial connection. The remote side needs to loop back
all received data. time is in secs. batch-size determines teh size of the packets used.
`);
}
Expand Down Expand Up @@ -148,7 +148,7 @@ cmdsocket -socket port -serial serial_if -baudrate rate [-log] [-test] [-testser
this.serialBaudrate=parseInt(baudrateString);
break;

case '-testserial':
case '-testloopback':
const timeString=args.shift();
if (timeString==undefined)
throw "Missing time argument.";
Expand All @@ -157,6 +157,8 @@ cmdsocket -socket port -serial serial_if -baudrate rate [-log] [-test] [-testser
if (batchSizeString==undefined)
throw "Missing batch-size argument.";
const batchSize=parseInt(batchSizeString);
if (batchSize>8192)
throw "Size should be smaller/equal to 8192.";
this.checkSerialArguments();
await InterfaceTests.testSerialLoopBack(this.serialPort, this.serialBaudrate, time, batchSize);
process.exit(0);
Expand Down
2 changes: 1 addition & 1 deletion src/usbserial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export class UsbSerial extends EventEmitter {
*/
public async sendBuffer(buffer: Buffer): Promise<void> {
if (this.tmpSendBuffer) {
// Cache until drainign is over
// Cache until draining is over
this.tmpSendBuffer.push(buffer);
}
else {
Expand Down
Loading

0 comments on commit 9ba8b9a

Please sign in to comment.