Home Manual Reference Source Test

src/lib/server/scripts/CallScriptStream.js

import { DataType, VariantArrayType } from 'node-opcua/lib/datamodel/variant';
import { StatusCodes } from 'node-opcua/lib/datamodel/opcua_status_code';
import NodeId from '../../model/opcua/NodeId';
import CallMethodStream from './CallMethodStream';

/**
 * A stream that calls atvise server scripts for all passed nodes.
 * @abstract
 */
export default class CallScriptStream extends CallMethodStream {
  /**
   * The id of the *callScript* method.
   * @type {NodeId}
   */
  get methodId() {
    return new NodeId(NodeId.NodeIdType.STRING, 'AGENT.SCRIPT.METHODS.callScript', 1);
  }

  /**
   * **Must be implemented by all subclasses:** The id of the script to call.
   * @type {NodeId}
   * @abstract
   */
  get scriptId() {
    throw new Error('Must be implemented by all subclasses');
  }

  /**
   * Id of the script's base object.
   * @type {NodeId}
   */
  get scriptBaseId() {
    return this.scriptId.parent;
  }

  /**
   * Returns the parameters to call the script with for the given file.
   * @param {AtviseFile} file The processed file.
   * @return {Object} The parameters passed to the script.
   */
  // eslint-disable-next-line no-unused-vars
  scriptParameters(file) {
    return {};
  }

  /**
   * Creates the raw method input arguments for the given file.
   * @param {AtviseFile} file The processed file.
   * @return {?node-opcua~Variant[]} Input arguments for the *callScript* method.
   */
  inputArguments(file) {
    const params = this.scriptParameters(file);

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

    const paramNames = Object.keys(params);

    return [
      {
        dataType: DataType.NodeId,
        value: this.scriptId,
      },
      {
        dataType: DataType.NodeId,
        value: this.scriptBaseId,
      },
      {
        dataType: DataType.String,
        arrayType: VariantArrayType.Array,
        value: paramNames,
      },
      {
        dataType: DataType.Variant,
        arrayType: VariantArrayType.Array,
        value: paramNames.map((key) => params[key]),
      },
    ];
  }

  /**
   * Returns the error message logged if running the script fails.
   * @param {AtviseFile} file The processed file.
   * @return {string} The resulting error message.
   */
  processErrorMessage(file) {
    return `Error running script ${this.scriptId} for ${file.relative}`;
  }

  /**
   * Calls the script specified in {@link CallScriptStream#scriptId}. If the script does not exist
   * but could be imported by running `atscm import` a special status description is returned.
   * @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) {
    super.processChunk(file, (err, status, success) => {
      const processedStatus = status;

      const atscmScript = this.scriptId.value.match(/SERVERSCRIPTS\.atscm\.(.*)/);
      if (status === StatusCodes.BadMethodInvalid && atscmScript) {
        processedStatus.description = `The '${atscmScript[1]}' script does not exist.
- Did you forget to run 'atscm import'?`;
      }

      handleErrors(err, processedStatus, success);
    });
  }
}