Home Manual Reference Source Test

src/lib/server/scripts/CallMethodStream.js

import { StatusCodes } from 'node-opcua/lib/datamodel/opcua_status_code';
import QueueStream from '../QueueStream';

/**
 * A stream that calls an OPC-UA method for all input files.
 * @abstract
 */
export default class CallMethodStream extends QueueStream {
  /**
   * **Must be implemented in all subclasses:** The {@link NodeId} of the method to call.
   * @type {NodeId} The method's id.
   */
  get methodId() {
    throw new Error('Must be implemented in all subclasses');
  }

  /**
   * The {@link NodeId} of the object from which the method should get called. Defaults to the value
   * of {@link NodeId#parent} of {@link CallMethodStream#methodId}.
   * @type {NodeId} The call-object's id.
   */
  get methodBaseId() {
    return this.methodId.parent;
  }

  /**
   * The input arguments the method should be called with for a file. Needs to be overridden by
   * subclasses in most cases. Returning `null` indicates no method call is needed.
   * @param {vinyl~File} file The file beeing processed.
   * @return {?node-opcua~Variant[]} The resulting input arguments.
   */
  // eslint-disable-next-line no-unused-vars
  inputArguments(file) {
    return [];
  }

  /**
   * Creates a call method request object for a file.
   * @param {vinyl~File} file The file beeing processed.
   * @return {?node-opcua~CallMethodRequest} The resulting call request.
   */
  callRequest(file) {
    const args = this.inputArguments(file);

    if (args === null) {
      return null;
    }

    return {
      objectId: this.methodBaseId,
      methodId: this.methodId,
      inputArguments: args,
    };
  }

  /**
   * **Must be implemented by all subclasses:** If the method call returns a status code of
   * *{@link node-opcua~StatusCodes}.Good*, this method decides if the output matches the expected
   * results.
   * @param {vinyl~File} file The file beeing processed.
   * @param {node-opcua~Variant[]} outputArgs The output arguments.
   * @param {function(err: Error)} callback Call this method with an error to indicate the method
   * call didn't work as expected.
   */
  // eslint-disable-next-line no-unused-vars
  handleOutputArguments(file, outputArgs, callback) {
    throw new Error('Must be implemented in all subclasses');
  }

  /**
   * Returns an error message specifically for the given file.
   * @param {vinyl~File} file The file to generate the error message for.
   * @return {string} The specific error message.
   */
  processErrorMessage(file) {
    return `Error running ${this.methodId.toString()} for ${file.relative}`;
  }

  /**
   * Performs an opcua method call for the given file.
   * @param {vinyl~File} file The file being processed.
   * @param {function(err: Error, status: node-opcua~StatusCodes, success: function)} handleErrors
   * The error handler to call. See {@link QueueStream#processChunk} for details.
   */
  processChunk(file, handleErrors) {
    try {
      const request = this.callRequest(file);

      if (!request) {
        handleErrors(null, StatusCodes.Good, (done) => done());
        return;
      }

      this.session.call([request], (err, [result] = []) => {
        if (err) {
          handleErrors(err);
        } else if (result.statusCode.value !== StatusCodes.Good.value) {
          handleErrors(err, result.statusCode, (done) => done());
        } else {
          this.handleOutputArguments(file, result.outputArguments, (outputError) => {
            handleErrors(outputError, StatusCodes.Good, (done) => done());
          });
        }
      });
    } catch (e) {
      handleErrors(e);
    }
  }
}