forked from nrkno/sofie-timeline-state-resolver
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
197 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
packages/timeline-state-resolver-types/src/integrations/websocketTcpClient.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { DeviceType } from 'timeline-state-resolver-types/src' | ||
|
||
export enum TimelineContentTypeWebSocketTcpClient { | ||
WEBSOCKET_MESSAGE = 'websocketMessage', | ||
TCP_COMMAND = 'tcpCommand', | ||
} | ||
|
||
export interface TimelineContentWebSocketTcpClientBase { | ||
deviceType: DeviceType.WEBSOCKET_TCP_CLIENT | ||
type: TimelineContentTypeWebSocketTcpClient | ||
} | ||
|
||
// We might end up using only 1 datatype as it's the same data being sent over different channels: | ||
export interface TimelineContentWebSocketMessage extends TimelineContentWebSocketTcpClientBase { | ||
type: TimelineContentTypeWebSocketTcpClient.WEBSOCKET_MESSAGE | ||
message: string | Uint8Array // Data to send over WebSocket | ||
} | ||
|
||
export interface TimelineContentTcpCommand extends TimelineContentWebSocketTcpClientBase { | ||
type: TimelineContentTypeWebSocketTcpClient.TCP_COMMAND | ||
command: string | Uint8Array // Data to send over TCP | ||
} | ||
|
||
export type TimelineContentWebSocketTcpClientAny = TimelineContentWebSocketMessage | TimelineContentTcpCommand |
65 changes: 65 additions & 0 deletions
65
packages/timeline-state-resolver/src/integrations/websocketTcpClient/connection.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import * as WebSocket from 'ws' | ||
import { Socket } from 'net' | ||
import { WebSocketTcpClientOptions } from 'timeline-state-resolver-types/src' | ||
|
||
export class WebSocketTcpConnection { | ||
private ws: WebSocket | null = null | ||
private tcp: Socket | null = null | ||
private options: WebSocketTcpClientOptions | ||
|
||
constructor(options: WebSocketTcpClientOptions) { | ||
this.options = options | ||
} | ||
|
||
async connect(): Promise<void> { | ||
// WebSocket connection | ||
this.ws = new WebSocket(this.options.webSocket.uri) | ||
this.ws.on('open', () => console.log('WebSocket connected')) | ||
this.ws.on('error', (err) => console.error('WebSocket error:', err)) | ||
this.ws.on('close', () => { | ||
console.log('WebSocket closed') | ||
if (this.options.webSocket.reconnectInterval) { | ||
setTimeout(() => this.connect(), this.options.webSocket.reconnectInterval) | ||
} | ||
}) | ||
|
||
// TCP connection | ||
this.tcp = new Socket() | ||
this.tcp.connect(this.options.tcp.port, this.options.tcp.host, () => { | ||
console.log('TCP connected') | ||
}) | ||
this.tcp.on('error', (err) => console.error('TCP error:', err)) | ||
this.tcp.on('close', () => console.log('TCP closed')) | ||
} | ||
|
||
connected(): boolean { | ||
return (this.ws?.readyState === WebSocket.OPEN && this.tcp?.writable) || false | ||
} | ||
|
||
sendWebSocketMessage(message: string | Uint8Array): void { | ||
if (this.ws?.readyState === WebSocket.OPEN) { | ||
this.ws.send(message) | ||
} else { | ||
console.warn('WebSocket not connected') | ||
} | ||
} | ||
|
||
sendTcpCommand(command: string | Uint8Array): void { | ||
if (this.tcp?.writable) { | ||
this.tcp.write(command) | ||
} else { | ||
console.warn('TCP not connected') | ||
} | ||
} | ||
|
||
async disconnect(): Promise<void> { | ||
if (this.ws) { | ||
this.ws.close() | ||
this.ws = null | ||
} | ||
if (this.tcp) { | ||
this.tcp.destroy() | ||
this.tcp = null | ||
} | ||
} | ||
} |
79 changes: 79 additions & 0 deletions
79
packages/timeline-state-resolver/src/integrations/websocketTcpClient/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { CommandWithContext, Device, DeviceContextAPI } from '../../service/device' | ||
import { DeviceStatus, WebSocketTcpClientOptions } from 'timeline-state-resolver-types' | ||
import { WebSocketTcpConnection } from './connection' | ||
|
||
interface WebSocketTcpCommand extends CommandWithContext{ | ||
command: any // need to fix command structure | ||
context: string | ||
timelineObjId: string | ||
value?: any // | ||
} | ||
|
||
export class WebSocketTcpClientDevice extends Device< | ||
WebSocketTcpClientOptions, | ||
any, //Add state later | ||
WebSocketTcpCommand | ||
> { | ||
private connection: WebSocketTcpConnection | ||
|
||
constructor(context: DeviceContextAPI<any>, _options: WebSocketTcpClientOptions) { | ||
super(context) | ||
this.connection = new WebSocketTcpConnection(_options) | ||
} | ||
|
||
public async init(): Promise<boolean> { | ||
await this.connection.connect() | ||
return true | ||
} | ||
|
||
public get actions(): any { | ||
// Placeholder implementation | ||
return {} | ||
} | ||
|
||
public get connected(): boolean { | ||
return this.connection?.connected() ?? false | ||
} | ||
|
||
public convertTimelineStateToDeviceState( | ||
state: any // ToDo | ||
): any { | ||
return state | ||
} | ||
|
||
public diffStates(oldState: any, newState: any): WebSocketTcpCommand[] { | ||
// ToDo: Implement state diffing | ||
const commands: WebSocketTcpCommand[] = [] | ||
if (oldState !== newState) { | ||
commands.push({ | ||
command: 'update', | ||
context: 'state_change', | ||
timelineObjId: 'example_id', | ||
value: newState, | ||
}) | ||
} | ||
return commands | ||
} | ||
|
||
public getStatus(): Omit<DeviceStatus, "active"> { | ||
return { | ||
statusCode: this.connected ? 0 : 1, // 0 = GOOD, 1 = BAD (based on StatusCode enum) | ||
messages: this.connected ? ['Connected'] : ['Disconnected'], | ||
} | ||
} | ||
|
||
public async sendCommand(command: WebSocketTcpCommand): Promise<void> { | ||
// Send the command via the WebSocket connection | ||
await this.connection.sendWebSocketMessage(command.value) | ||
} | ||
// We might end up using just one sendCommand() with a switch-case for the command type: | ||
public async sendTcpCommand(command: WebSocketTcpCommand): Promise<void> { | ||
// Send the command via the TCP connection | ||
await this.connection.sendTcpCommand(command.value) | ||
} | ||
|
||
public async terminate(): Promise<void> { | ||
await this.connection.disconnect() | ||
// Perform any cleanup if needed | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters