Skip to content

Commit

Permalink
2023-01_TransmitOption (#237)
Browse files Browse the repository at this point in the history
- Fix build
- Updated dependencies
- Fixed tests
- Adding tests to cover new cases.
  • Loading branch information
KD0NKS committed Apr 2, 2024
1 parent 057b31b commit c5d5c36
Show file tree
Hide file tree
Showing 9 changed files with 1,578 additions and 3,371 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node: [ 16.x, 18.x, 20.x ]
node: [ 20.x ]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node }}
Expand Down
3 changes: 1 addition & 2 deletions .mocharc.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
bail: true
spec: 'test/**/*.test.ts'
require:
- ts-node/register
- source-map-support/register
- tsx
full-trace: true
File renamed without changes.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,12 @@ connection.on('data', (data: Buffer) => {
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.
# UPGRADING 1.x.x to 2.x.x
## ISSocket Constructor
The constructor paramaters have changed drastically. Please note the order has changed as well as what is required.
- Callsign is no longer defaulted to discourage useage of erroneous callsigns.
- appId is now required.
- ISSocket.sendLine() is now deprecated.
- Use sendLogin() and send() instead.
- send() will still send server commands even if transmit is disabled.
4,090 changes: 1,080 additions & 3,010 deletions package-lock.json

Large diffs are not rendered by default.

23 changes: 12 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"author": "afairhurst",
"license": "ISC",
"name": "js-aprs-is",
"version": "1.2.0",
"version": "2.0.0",
"lockfileVersion": 3,
"homepage": "https://github.com/KD0NKS/js-aprs-is",
"description": "NodeJs library for connecting to an APRS-IS server.",
"repository": {
Expand Down Expand Up @@ -33,22 +34,22 @@
},
"devDependencies": {
"@istanbuljs/nyc-config-typescript": "^1.0.2",
"@types/chai": "^4.3.5",
"@types/mocha": "^10.0.1",
"@types/uuid": "^9.0.1",
"chai": "^4.3.7",
"@types/chai": "^4.3.14",
"@types/mocha": "^10.0.6",
"@types/uuid": "^9.0.8",
"chai": "^5.1.0",
"coveralls": "^3.1.1",
"mocha": "^10.2.0",
"mocha": "^10.4.0",
"nyc": "^15.1.0",
"source-map-support": "^0.5.21",
"ts-node": "^10.9.1",
"typescript": "^5.2.2"
"tsx": "^4.7.1",
"typescript": "^5.4.3"
},
"dependencies": {
"@types/node": "^20.1.0",
"uuid": "^9.0.0"
"@types/node": "^20.12.2",
"uuid": "^9.0.1"
},
"optionalDependencies": {
"fsevents": "^2.3.2"
"fsevents": "^2.3.3"
}
}
90 changes: 57 additions & 33 deletions src/ISSocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const DISCONNECT_EVENTS: string[] = ['destroy', 'end', 'close', 'error', 'timeou
const CONNECT_EVENTS: string[] = ['connect', 'ready'];

export class ISSocket extends Socket {
private readonly _maxPacketLength: number = 512

// not a fan of this... emit events instead? build it out to be a wrapper around null/readable/writable?
private _isSocketConnected: boolean;
private _bufferedData: string;
Expand All @@ -40,28 +42,29 @@ export class ISSocket extends Socket {
* the host and port to connect to and your client's callsign, and one or more
* optional named options:
*
* @param {string} host - APRS-IS server to connect to.
* @param {number} port - Port number of the APRS-IS server to connect to.
* @param {string} [callsign=N0CALL] - Your station's callsign.
* @param {number} [passcode=-1] - An APRS-IS passcode.
* @param {string} [filter] - An APRS-IS filter string sent to the server.
* @param {string} [appid=IS.js 1.0.0] - Your application's name and version number direction finding. Should not exceed 15 characters.
* @param {string} [appid] - Your application's name and version number direction finding. Should not exceed 15 characters.
* @param {string} [host] - APRS-IS server to connect to.
* @param {number} [port] - Port number of the APRS-IS server to connect to.
* @param {string} [callsign] - Your station's callsign.
* @param {number} [passcode=-1] - An APRS-IS passcode. NOTE: isTransmitEnabled must be set to true even if this is set to be able to send a packet to the server.
* @param {boolean} [isTransmitEnabled=false] - A flag to tell whether or not a packet can be sent on this connection. Intended to prevent accidental sending of data. NOTE: passcode must be properly set, otherwise the server will reject any packets, even if this is true.
* @param {string | null | undefined} [aprsFilter] - An APRS-IS filter string sent to the server.
* @param {string | number} [id=uuidV4()] - A unique id for the application. This is not required, but is here for convenience.
*
* @example let connection = new IS('aprs.server.com', 12345);
* @example let connection = new IS('aprs.server.com', 12345, 'N0CALL', undefined, undefined, 'myapp 3.4b');
* @example let connection = new IS('aprs.server.com', 12345, 'N0CALL', undefined, 'f/*', 'foobar 42');
* @example let connection = new IS('aprs.server.com', 12345, 'N0CALL', 1234, 'f/*', 'myapp 1.2', true);
* @example let connection = new IS('myapp 3.4b', 'aprs.server.com', 12345, 'N0CALL');
* @example let connection = new IS('foobar 42', 'aprs.server.com', 12345, 'N0CALL', undefined, 'f/*');
* @example let connection = new IS('myapp 1.2', 'aprs.server.com', 12345, 'N0CALL', 1234, true, 'f/*', 'abc123');
*/
// Don't provide multiple constructors. Passing undefined parameters is annoying, but ideally, most, if not all
// parameters should be used anyway.
constructor(public host: string
constructor(public appId: string // (appname and version num should not exceed 15 characters) TODO: Figure out how to pass process name and version to the parent app.
, public host: string
, public port: number
, public callsign: string = "N0CALL"
, public callsign: string
, public passcode: number = -1
, public filter?: string
, public appId: string = `IS.js v1` // (appname and version num should not exceed 15 characters) TODO: Figure out how to pass process name and version to the parent app.
, public isTransmitEnabled: boolean = false
, public id: string | number = uuidV4() // This is odd at best... leave it for now
, public aprsFilter?: string | null | undefined
) {
super();

Expand All @@ -71,6 +74,7 @@ export class ISSocket extends Socket {

// TODO: Do we want to throw errors if the host, port, callsign, are null?

// TODO: Should make this public so it can be overwritten.
this.on('data', (data: Buffer) => {
this._bufferedData += data.toString();
let msgs = this._bufferedData.split('\r\n');
Expand Down Expand Up @@ -159,9 +163,9 @@ export class ISSocket extends Socket {
* should be a complete packet but WITHOUT the <CR><LF> separator
* used on the APRS-IS.
*
* @param {string} line - Packet/message to send with <CR><LF> delimiter.
* @param {string} line - Packet/message to send this should not include the <CR><LF> delimiter.
*/
public sendLine(line: string): void {
private sendLine(line: string): void {
if(this._isSocketConnected === false) {
throw new Error('Socket not connected.');
}
Expand All @@ -170,37 +174,57 @@ export class ISSocket extends Socket {
// Trusting the calling appliation to handle this appropriately for now.
const data = `${line}${MESSAGE_DELIMITER}`;

// Does it make sense to have a 'sending' and 'data' event?
this.emit('sending', data);
this.emit('data', data);
if(data.length <= this._maxPacketLength) {
// Does it make sense to have a 'sending' and 'data' event?
this.emit('sending', data);
this.emit('data', data);

// TODO: use callback and emit 'sent' and data events
this.write(data, 'utf8');
} else {
throw new Error("Packet length must be shorter than 512 bytes.")
}
}

/**
* Sends a login message regardless of the value of isTransmitEnabled.
*/
public sendLogin(callback?: any): any {
this.sendLine(`user ${this.callsign} pass ${this.passcode} vers ${this.appId}`
+ (!!this.aprsFilter ? ` filter ${this.aprsFilter}` : ''));

// TODO: use callback and emit 'sent' and data events
this.write(data, 'utf8');
if(callback) {
callback()
}
}

/**
* Sends a packet only if:
* - isTransmitEnabled is set to true. Used as a safeguard to prevent unintentionally sending packets.
* - packet is a control command starting with a '#' character.
*
* @param {string} packet - Packet to send WITHOUT the <CR><LF> separator.
*/
public send(packet: string): void {
if(this.isTransmitEnabled === true || packet.startsWith('#')) {
this.sendLine(packet)
} else {
throw new Error("Transmitting data is not permitted.");
}
}

/**
* In a perfect world, this tells whether the socket is currently connected.
*
* @returns {boolean} True if connected, otherwise false.
*
* @example connection.isConnected()
* @example connection.isConnected
*/
public isConnected(): boolean {
// use socket.writeable instead?
return this._isSocketConnected === true;
}

/**
* Generates a user login packet for an APRS-IS server.
* Replaces perl-aprs-is user_command function.
*
* @returns {string} Formatted user login packet/message without message delimiter.
*/
get userLogin(): string {
return `user ${this.callsign} pass ${this.passcode} vers ${this.appId}`
+ ((this.filter == undefined || !this.filter) ? '' : ` filter ${this.filter}`);
}

private emitPackets(msgs: string[]) {
msgs.forEach(msg => {
this.emit("packet", msg)
Expand Down

0 comments on commit c5d5c36

Please sign in to comment.