Skip to content

Commit

Permalink
improve type definitions for events + better jsdoc comments
Browse files Browse the repository at this point in the history
  • Loading branch information
k-yle committed Jul 22, 2024
1 parent 35477c7 commit 4656ed1
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 93 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
"extends": "kyle",
"rules": {
"no-bitwise": 0,
"import/export": 0,
"@typescript-eslint/no-namespace": 0,
"@typescript-eslint/no-non-null-assertion": 0
}
},
Expand Down
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ main(); // wrapped in a main() function so that we can `await` the promise
| `reuseAddr` | `boolean` | Optional. Allow multiple programs on your computer to send to the same sACN universe. |
| `defaultPacketOptions` | `object` | Optional. You can specify options like `sourceName`, `cid`, `priority` and `useRawDmxValues` here instead of on every packet |
| `iface` | `string` | Optional. Specifies the IPv4 address of the network interface/card to use. | OS default interface (=active internet connection)
| `minRefreshRate` | `number` | Optional. How often the data should be re-sent (*in Hertz/Hz*), even if it hasn't changed. By default data will only be sent once (equivilant of setting `refreshRate: 0`). To re-send data 5 times per second (`5Hz`), set `refreshRate: 5`. This is equivilant to `200ms`. | `0`
| `useUnicastDestination`| `string` | Optional. Setting this attribute to an IPv4 address will cause data to be sent directly to that device, instead of broadcasting to the whole LAN. |

# Contribute
Expand Down
42 changes: 29 additions & 13 deletions src/receiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,53 @@ import { AssertionError } from 'assert';
import { Packet } from './packet';
import { multicastGroup } from './util';

export interface ReceiverProps {
universes?: number[];
port?: number;
iface?: string; // local ip address of network inteface to use
reuseAddr?: boolean;
/** @deprecated - use {@link Receiver.Props} instead */
export type ReceiverProps = Receiver.Props;

export declare namespace Receiver {
export interface Props {
/** List of universes to listen to. Must be within `1-63999 */
universes?: number[];
/** The multicast port to use. All professional consoles broadcast to the default port. */
port?: number;
/** local ip address of network inteface to use */
iface?: string;
/** Allow multiple programs on your computer to listen to the same sACN universe. */
reuseAddr?: boolean;
}

export interface EventMap {
packet: Packet;
PacketCorruption: AssertionError;
PacketOutOfOrder: Error;
error: Error;
}
}

export declare interface Receiver {
on(event: 'packet', listener: (packet: Packet) => void): this;
on(event: 'PacketCorruption', listener: (err: AssertionError) => void): this;
on(event: 'PacketOutOfOrder', listener: (err: Error) => void): this;
on(event: 'error', listener: (err: Error) => void): this;
on<K extends keyof Receiver.EventMap>(
type: K,
listener: (event: Receiver.EventMap[K]) => void,
): this;
}

export class Receiver extends EventEmitter {
private socket: Socket;

private lastSequence: Record<string, number>;

private readonly port: ReceiverProps['port'];
private readonly port: Receiver.Props['port'];

public universes: NonNullable<ReceiverProps['universes']>;
public universes: NonNullable<Receiver.Props['universes']>;

private readonly iface: ReceiverProps['iface'];
private readonly iface: Receiver.Props['iface'];

constructor({
universes = [1],
port = 5568,
iface = undefined,
reuseAddr = false,
}: ReceiverProps) {
}: Receiver.Props) {
super();
this.universes = universes;
this.port = port;
Expand Down
82 changes: 35 additions & 47 deletions src/receiver/abstract.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
import { AssertionError } from 'assert';
import { performance } from 'node:perf_hooks';
import { Packet } from '../packet';
import { Receiver } from '../receiver';
import type { Payload } from '../util';

interface MergeProps {
universes?: number[];
port?: number;
iface?: string;
reuseAddr?: boolean;
timeout?: number;
}

interface Universe {
lastData: Payload;
servers: Map<string, PacketWithTimestamp>;
Expand All @@ -21,8 +13,41 @@ interface PacketWithTimestamp {
readonly lastTimestamp: number;
}

export namespace ReceiverMerge {
export interface Props extends Receiver.Props {
timeout?: number;
}

export interface EventMap extends Receiver.EventMap {
changed: {
universe: number;
addr: number;
newValue: number;
oldValue: number;
};
changesDone: never;
senderConnect: {
cid: number;
universe: number;
firstPacket: Packet;
};
senderDisconnect: {
cid: number;
universe: number;
lastPacket: Packet;
};
}
}

export declare interface ReceiverMerge {
on<K extends keyof Receiver.EventMap>(
type: K,
listener: (event: Receiver.EventMap[K]) => void,
): this;
}

export class ReceiverMerge extends Receiver {
constructor({ timeout = 5000, ...props }: MergeProps) {
constructor({ timeout = 5000, ...props }: ReceiverMerge.Props) {
super(props);
this.timeout = timeout;
super.on('packet', this.mergePacket);
Expand Down Expand Up @@ -128,40 +153,3 @@ export class ReceiverMerge extends Receiver {
}
}
}
export declare interface ReceiverMerge {
// on(event: string, listener: (...args: any[]) => void): this;
on(
event: Parameters<Receiver['on']>[0],
listener: Parameters<Receiver['on']>[1],
): this;
on(
event: 'changed',
listener: (ev: {
universe: number;
addr: number;
newValue: number;
oldValue: number;
}) => void,
): this;
on(event: 'changesDone', listener: () => void): this;
on(
event: 'senderConnect',
listener: (ev: {
cid: number;
universe: number;
firstPacket: Packet;
}) => void,
): this;
on(
event: 'senderDisconnect',
listener: (ev: {
cid: number;
universe: number;
lastPacket: Packet;
}) => void,
): this;
on(event: 'packet', listener: (packet: Packet) => void): this;
on(event: 'PacketCorruption', listener: (err: AssertionError) => void): this;
on(event: 'PacketOutOfOrder', listener: (err: Error) => void): this;
on(event: 'error', listener: (err: Error) => void): this;
}
82 changes: 49 additions & 33 deletions src/sender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,67 @@ import { Socket, createSocket } from 'dgram';
import { multicastGroup } from './util';
import { Packet, Options } from './packet';

export interface SenderProps {
universe: number;
port?: number;
reuseAddr?: boolean;
/**
* How often the data should be re-sent (**in Hertz/Hz**), even if it hasn't changed.
*
* By default data will only be sent once (equivilant of setting `refreshRate: 0`).
*
* To re-send data 5 times per second (`5Hz`), set `refreshRate: 5`. This is equivilant to `200ms`.
*/
minRefreshRate?: number;

/** some options can be sepecified when you instantiate the sender, instead of sepecifying them on every packet */
defaultPacketOptions?: Pick<
Options,
'cid' | 'sourceName' | 'priority' | 'useRawDmxValues'
>;

// IPv4 address of the network interface
iface?: string;

/**
* If you set this option to an IP address, then data will be sent
* purely to this address, instead of the whole network.
*
* This option is not recommended and may not be supported by all devices.
*/
useUnicastDestination?: string;
/** @deprecated - use {@link Sender.Props} instead */
export type SenderProps = Sender.Props;

export namespace Sender {
export interface Props {
/** The universe to send to. Must be within 1-63999 */
universe: number;
/**
* The multicast port to use. All professional consoles broadcast to the default port.
* @default 5568
*/
port?: number;
/**
* Allow multiple programs on your computer to send to the same sACN universe.
* @default false
*/
reuseAddr?: boolean;
/**
* How often the data should be re-sent (**in Hertz/Hz**), even if it hasn't changed.
*
* By default data will only be sent once (equivilant of setting `refreshRate: 0`).
*
* To re-send data 5 times per second (`5Hz`), set `refreshRate: 5`. This is equivilant to `200ms`.
*
* @default 0
*/
minRefreshRate?: number;

/** some options can be sepecified when you instantiate the sender, instead of sepecifying them on every packet */
defaultPacketOptions?: Pick<
Options,
'cid' | 'sourceName' | 'priority' | 'useRawDmxValues'
>;

// IPv4 address of the network interface
iface?: string;

/**
* If you set this option to an IP address, then data will be sent
* purely to this address, instead of the whole network.
*
* This option is not recommended and may not be supported by all devices.
*/
useUnicastDestination?: string;
}
}

export class Sender {
private socket: Socket;

private readonly port: SenderProps['port'];
private readonly port: Sender.Props['port'];

public readonly universe: SenderProps['universe'];
public readonly universe: Sender.Props['universe'];

/**
* this is normally a multicast address, but it could be
* a unicast address if the user configures `useUnicastDestination`
*/
readonly #destinationIp: string;

private readonly defaultPacketOptions: SenderProps['defaultPacketOptions'];
private readonly defaultPacketOptions: Sender.Props['defaultPacketOptions'];

private sequence = 0;

Expand All @@ -67,7 +83,7 @@ export class Sender {
defaultPacketOptions,
iface,
useUnicastDestination,
}: SenderProps) {
}: Sender.Props) {
this.port = port;
this.universe = universe;
this.#destinationIp = useUnicastDestination || multicastGroup(universe);
Expand Down

0 comments on commit 4656ed1

Please sign in to comment.