Home Manual Reference Source

src/recognizer/websocket/CdkWSRecognizerUtil.js

import { recognizerLogger as logger } from '../../configuration/LoggerConfig';
import Constants from '../../configuration/Constants';
import * as NetworkWSInterface from './networkWSInterface';
import * as PromiseHelper from '../../util/PromiseHelper';
import * as InkModel from '../../model/InkModel';
import * as RecognizerContext from '../../model/RecognizerContext';

function buildUrl(configuration, suffixUrl) {
  const scheme = (configuration.recognitionParams.server.scheme === 'https') ? 'wss' : 'ws';
  return `${scheme}://${configuration.recognitionParams.server.host}${suffixUrl}`;
}

const commonCallback = (model, err, res, callback) => {
  if (res && res.type === 'close') {
    return callback(err, model, Constants.EventType.CHANGED);
  }
  return callback(err, model);
};

/**
 * Build websocket function
 * @typedef {function} BuildWebSocketFunction
 * @param {DestructuredPromise} destructuredPromise
 * @param {RecognizerContext} recognizerContext
 * @return {Callback}
 */

/**
 * Init the websocket recognizer.
 * Open the connexion and proceed to the hmac challenge.
 * @param {String} suffixUrl
 * @param {RecognizerContext} recognizerContext
 * @param {BuildWebSocketFunction} buildWebSocketCallback
 * @param {function(recognizerContext: RecognizerContext, model: Model, callback: RecognizerCallback)} reconnect
 * @return {Promise} Fulfilled when the init phase is over.
 */
export function init(suffixUrl, recognizerContext, buildWebSocketCallback, reconnect) {
  const recognitionContext = recognizerContext.recognitionContexts[0];
  const recognizerContextReference = RecognizerContext.updateRecognitionPositions(recognizerContext, recognitionContext.model.lastPositions);
  recognizerContextReference.url = buildUrl(recognizerContext.editor.configuration, suffixUrl);
  recognizerContextReference.reconnect = reconnect;

  const destructuredInitPromise = PromiseHelper.destructurePromise();
  recognizerContextReference.initPromise = destructuredInitPromise.promise;

  logger.debug('Opening the websocket for context ', recognizerContext);
  recognizerContextReference.websocketCallback = buildWebSocketCallback(destructuredInitPromise, recognizerContextReference);
  recognizerContextReference.websocket = NetworkWSInterface.openWebSocket(recognizerContextReference);
  return recognizerContextReference.initPromise.then((res) => {
    logger.debug('Init over', res);
    return res;
  });
}

export function retry(func, recognizerContext, model, callback, ...params) {
  if (RecognizerContext.shouldAttemptImmediateReconnect(recognizerContext) && recognizerContext.reconnect) {
    logger.info('Attempting a retry', recognizerContext.currentReconnectionCount);
    recognizerContext.reconnect(recognizerContext, model, (err, res) => {
      if (!err) {
        func(recognizerContext, res, callback, ...params);
      } else {
        logger.error('Failed retry', err);
        retry(func, recognizerContext, model, callback, ...params);
      }
    });
  } else {
    callback('Unable to reconnect', model);
  }
}

/**
 * @param {RecognizerContext} recognizerContext
 * @param {function(params: ...Object): Object} buildMessage
 * @param {...Object} params
 * @return {Promise}
 */
export function sendMessage(recognizerContext, buildMessage, ...params) {
  return recognizerContext.initPromise
    .then(() => {
      logger.trace('Init was done. Sending message');
      const message = buildMessage(...params);
      if (message) {
        NetworkWSInterface.send(recognizerContext, message);
        const positions = recognizerContext.recognitionContexts[0].model.lastPositions;
        if (positions) {
          RecognizerContext.updateRecognitionPositions(recognizerContext, positions);
        }
      } else {
        logger.warn('empty message');
      }
    });
}

/**
 * Do what is needed to clean the server context.
 * @param {RecognizerContext} recognizerContext Current recognizer context
 * @param {Model} model Current model
 * @param {RecognizerCallback} callback
 */
export function clear(recognizerContext, model, callback) {
  const modelRef = InkModel.clearModel(model);
  const recognizerContextReference = RecognizerContext.updateRecognitionPositions(recognizerContext, modelRef.lastPositions);
  if (recognizerContextReference && recognizerContextReference.websocket) {
    // We have to send again all strokes after a clear.
    delete recognizerContextReference.instanceId;
    try {
      NetworkWSInterface.send(recognizerContextReference, { type: 'reset' });
    } catch (sendFailedException) {
      // To force failure without breaking the flow
      // FIXME not working at all
      recognizerContextReference.websocketCallback(PromiseHelper.destructurePromise(), recognizerContextReference, model);
    }
  }
  // We do not keep track of the success of clear.
  callback(undefined, modelRef);
}

/**
 * Close and free all resources that will no longer be used by the recognizer.
 * @param {RecognizerContext} recognizerContext
 * @param {Model} model
 * @param {RecognizerCallback} callback
 */
export function close(recognizerContext, model, callback) {
  const recognitionContext = {
    model,
    callback: (err, res) => commonCallback(model, err, res, callback)
  };
  const recognizerContextRef = recognizerContext;

  recognizerContext.initPromise
    .then(() => {
      recognizerContextRef.recognitionContexts[0] = recognitionContext;
      return recognizerContextRef;
    })
    .then(context => NetworkWSInterface.close(context, 1000, RecognizerContext.CLOSE_RECOGNIZER_MESSAGE));
}