123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- import { Observable, Subject, takeWhile } from "rxjs";
- import { prepareResponseMessages } from "../../services/utility/prepareFISmessage";
- import { BaseMessage } from "../../dependencies/logging/interface/export";
- import { io, Socket } from "socket.io-client";
- import { WrappedMessage } from "../../interfaces/general.interface";
- import * as fs from 'fs'
- import { ClientInfo } from "./socket.service";
- let onHoldMessagesSubject: Subject<WrappedMessage> = new Subject()
- let toBePassedOverToApp: Subject<BaseMessage> = new Subject()
- // Serve static files (optional)
- let sender: Subject<BaseMessage> = prepareResponseMessages(1, 2000)
- let serverSocketUrl: string = 'http://192.168.100.96:3000'
- let socket: Socket
- establishSocketConnection(serverSocketUrl).then(() => {
- sender.subscribe({
- next: message => {
- makeRequest(message).subscribe({
- complete: () => console.log(`Request ${message.header.messageID} has acquired all responses.`)
- })
- }
- })
- })
- // the interface the client Program will make without having to decide transport protocol
- function makeRequest(request: BaseMessage): Observable<any> {
- return new Observable((response) => {
- sendMessage(request)
- toBePassedOverToApp.subscribe({
- next: (message: BaseMessage) => {
- // console.log(message.header.messageName)
- // The identification of responses mapping to the request be adjusted accordingly
- // For now it's a simple demulti-plexing
- if (message.header.messageID == request.header.messageID && message.header.messageName != 'Complete') {
- response.next(message)
- }
- if (message.header.messageID == request.header.messageID && message.header.messageName == 'Complete') {
- response.complete()
- }
- },
- error: err => console.error(err),
- complete: () => { }
- })
- })
- }
- // socket util: Assuming that the client program would already have something like this in place
- async function establishSocketConnection(serverUrl: string): Promise<any> {
- return new Promise((resolve, reject) => {
- try {
- socket = io(serverUrl, {
- reconnection: true, // Enable automatic reconnections
- reconnectionAttempts: 100, // Retry up to 10 times
- reconnectionDelay: 500, // Start with a 500ms delay
- reconnectionDelayMax: 10000, // Delay can grow to a max of 10 seconds
- randomizationFactor: 0.3,
- })
- // Check if it's a previuos client.
- let data: ClientInfo | null = checkOwnClientInfo('info.json')
- if (data) {
- socket.emit('notification', { agenda: 'existingClient', data: data })
- } else {
- socket.emit('notification', { agenda: 'newClient' })
- }
- // Listen for a connection event
- socket.on('connect', () => {
- // socket.emit('Hello from the client!')
- console.log('Connected to the server:', socket.id)
- });
- // Listen for messages from the server
- socket.on('response', (msg: WrappedMessage) => {
- console.log('Message from server:', msg.payload.header.messageName, ' for ', msg.payload.header.messageID);
- // Check the sequence by ensuring the message value before the current message exists, then pass them over to "App"
- // onHoldMessagesSubject.next(msg)
- // checkMessage(msg).then(() => [
- // toBePassedOverToApp.next(msg.payload as BaseMessage)
- // ]).catch((err) => console.error(err))
- toBePassedOverToApp.next(msg.payload as BaseMessage)
- })
- socket.on('notification', (msg: any) => {
- if (msg.notification == 'Your credentials') {
- console.log(`Assigned client Name: ${msg.socketInfo.clientName}`)
- writeFile(msg.socketInfo as ClientInfo)
- }
- if (msg.notification == 'Your updated credentials') {
- console.log(`Updated socket ID: `, msg)
- // writeFile(msg.socketInfo as ClientInfo)
- }
- if (msg.notification == 'Failed Request') {
- console.log(`Resending request...`, msg.data.header.messageID)
- setTimeout(() => {
- sender.next(msg.data)
- }, 1000)
- }
- })
- resolve('')
- // Handle disconnection
- socket.on('disconnect', () => {
- console.log('Disconnected from the server');
- // receiverConnectionState.next('OFFLINE')
- });
- }
- catch (error) {
- reject(error)
- }
- })
- }
- function checkOwnClientInfo(filename: string): ClientInfo | null {
- // Check if the file exists
- if (fs.existsSync(filename)) {
- try {
- // Read the file contents
- const fileData = fs.readFileSync(filename, 'utf8');
- // If the file is empty, return an error
- if (fileData.trim() === "") {
- throw new Error("File is empty");
- }
- // Parse and return the data if present
- const jsonData = JSON.parse(fileData);
- return jsonData;
- } catch (err) {
- // Handle parsing errors or other file-related errors
- console.error("Error reading or parsing file:", err.message);
- return null;
- }
- } else {
- console.error("File does not exist");
- return null;
- }
- }
- function writeFile(data: ClientInfo) {
- // Write JSON data to a file
- fs.writeFile('info.json', JSON.stringify(data, null, 2), (err) => {
- if (err) {
- console.error('Error writing file', err);
- } else {
- console.log('File has been written');
- }
- });
- }
- async function sendMessage(message: BaseMessage): Promise<any> {
- return new Promise((resolve, reject) => {
- try {
- // extra precaution: According to chatgpt, if disconnected, then the payload will be loaded back in event queue whilst the socket will try to reestablish connection
- // https://socket.io/docs/v4/client-offline-behavior/
- socket.emit('request', message); // inherently an aysnc
- console.log(`SocketEmit() for message to event queue ${message.header.messageID}`)
- resolve('')
- } catch (error) {
- console.error('Error emitting message:', error);
- sender.next(message)
- reject(error)
- }
- })
- }
- // SO concept will be that if the message behind it is received, then
- async function checkMessage(message: WrappedMessage): Promise<any> {
- return new Promise((resolve, reject) => {
- if (message.previousMessageID) {
- onHoldMessagesSubject.pipe(
- takeWhile(item => message.previousMessageID === item.thisMessageID)
- ).subscribe({
- complete: () => {
- resolve('previousMessageID matched')
- // console.log(`${message.payload.header.messageID} : Previous messageID(${message.previousMessageID}) matched`)
- // console.log(`matched`)
- }
- })
- } else {
- console.log('No previous messageID. This should be the first message')
- resolve('No previous message ID. Please Proceed.')
- }
- })
- }
|