ecosystem-controlstation.js

// used to append interfaces to omnicore sdk so that created app and AppStudio can use
import API from './ecosystem-base.js';
import {Logger} from './../function/log-helper.js';
import {InfoType, WarningType} from '../information/informationCode.js';
import {ErrorCode} from '../exception/exceptionDesc.js';
import {ControlStation, Pending} from '../store/const.js';
import Store from '../store/store.js';

const factoryApiControlStation = function (es) {
  let logModule = 'ecosystem-controlstation';

  /**
   * The API.CONTROLSTATIONMONITOR namespace provides a set of interfaces for easily and quickly adds control station monitoring and processing transactions.
   * @alias API.CONTROLSTATIONMONITOR
   * @ignore
   * @namespace
   */
  es.CONTROLSTATIONMONITOR = new (function () {
    const subscribedResources = {};
    /**
     * Enum for monitor resources
     */
    this.MonitorResources = {
      'write-access-status': 'write-access-status',
      'release-appeal-counter': 'release-appeal-counter',
      'tpu-safety-protocol-connected': 'tpu-safety-protocol-connected',
      'local-is-connected': 'local-is-connected',
    };

    const checkIfSpocSystem = async function () {
      let bSpoc = await API.CONTROLSTATION.isSpocSystem();
      if (!bSpoc) {
        Logger.e(
          logModule,
          ErrorCode.UnsupportedSystem,
          'ControlStation monitoring is only available on RobotWare 8 or later.',
        );
        throw new Error(ErrorCode.UnsupportedSystem);
      }
    };

    const subscribeRes = async function (monitor, func) {
      if (es.isSubscriptionBlocked) {
        Logger.w(
          WarningType.RWSSubscriptionBlocked,
          'API.CONTROLSTATIONMONITOR: Subscription disabled when trying to monitor control station resource',
        );
        return;
      }
      monitor.addCallbackOnChanged(func);
      await monitor.subscribe(true);
    };

    /**
     * Monitors a control station resource.
     * @alias monitorResource
     * @memberof API.CONTROLSTATIONMONITOR
     * @param {string} resourceType The type of resource to monitor (from MonitorResources enum).
     * @param {function} [callback] The callback function that is called when the resource changes.
     * @returns {Promise<void>}
     * @example
     * API.CONTROLSTATIONMONITOR.monitorResource(
     *   API.CONTROLSTATIONMONITOR.MonitorResources['write-access-status'],
     *   (status) => {
     *     console.log('Status:', status);
     *   }
     * );
     */
    this.monitorResource = async function (resourceType, callback = null) {
      await checkIfSpocSystem();

      const mappingKey = Store.generateMappingKey('ControlStation', [resourceType]);

      // Validate if resourceType is a valid monitor resource
      if (!Object.keys(this.MonitorResources).includes(resourceType)) {
        Logger.w(WarningType.RWSSubscriptionBlocked, `API.CONTROLSTATIONMONITOR: Unknown ${mappingKey}`);
        return;
      }

      if (es.isFetchControllerBlocked) {
        Logger.w(
          WarningType.RWSRequestBlocked,
          `API.CONTROLSTATIONMONITOR: HttpRequest disabled when trying to monitor ${mappingKey}`,
        );
        return;
      }

      const storeValue = Store.getNamespace(ControlStation).getMapping(mappingKey);
      if (storeValue === undefined) {
        try {
          Store.getNamespace(ControlStation).setMapping(mappingKey, Pending);
          let initialValue;
          switch (resourceType) {
            case 'write-access-status':
              initialValue = await RWS.ControlStation.getWriteAccessStatus();
              break;

            case 'release-appeal-counter':
              initialValue = await RWS.ControlStation.getWriteAccessReleaseAppealChangeCounter();
              break;

            case 'tpu-safety-protocol-connected':
              initialValue = await RWS.ControlStation.getTPUSafetyProtocolStatus();
              break;

            case 'local-is-connected':
              initialValue = await RWS.ControlStation.isLocalConnected();
              break;

            default:
              throw new Error(`Unknown resource type: ${resourceType}`);
          }
          Store.getNamespace(ControlStation).setMapping(mappingKey, initialValue);
          typeof callback === 'function' && callback(initialValue);
          es._events.trigger(mappingKey, initialValue);
        } catch (e) {
          return API.rejectWithStatus(
            `Failed to subscribe to control station resource ${resourceType}`,
            e,
            ErrorCode.FailedToSubscribeControlStation,
          );
        }
      } else if (storeValue === Pending) {
        es._events.on(mappingKey, (value) => {
          typeof callback === 'function' && callback(value);
        });
      } else {
        typeof callback === 'function' && callback(storeValue);
      }
      if (es.isSubscriptionBlocked) {
        Logger.w(
          WarningType.RWSSubscriptionBlocked,
          `API.CONTROLSTATION: Subscription disabled when trying to monitor control station resource ${mappingKey}`,
        );
        return;
      }
      // if the resource is not subscribed yet, subscribe to it
      if (!subscribedResources[mappingKey]) {
        try {
          const monitor = RWS.ControlStation.getMonitor(resourceType);

          const cbResource = function (data) {
            Logger.i(logModule, `Control station resource changed: ${resourceType}`);
            Store.getNamespace(ControlStation).setMapping(mappingKey, data);
            es._events.trigger(mappingKey, data);
            Logger.i(InfoType.RobotOperation, `Triggered control station resource: ${resourceType}`);
          };

          subscribeRes(monitor, cbResource.bind(this));
          subscribedResources[mappingKey] = true;

          Logger.i(InfoType.RobotOperation, `Control station resource subscribed:${mappingKey}`);
        } catch (e) {
          return API.rejectWithStatus(
            `Failed to subscribe to control station resource ${resourceType}`,
            e,
            ErrorCode.FailedToSubscribeControlStation,
          );
        }
      }
      es._events.on(mappingKey, callback);
    };

    /**
     * Monitors the write access status of control station.
     * @alias monitorWriteAccessStatus
     * @memberof API.CONTROLSTATIONMONITOR
     * @param {function} [callback] The callback function that is called when write access status changes.
     * @returns {Promise<void>}
     * @example
     * API.CONTROLSTATIONMONITOR.monitorWriteAccessStatus((status) => {
     *   console.log('Write access status:', status);
     *   if (status.status) {
     *     console.log('Write access held by:', status.name);
     *   }
     * });
     */
    this.monitorWriteAccessStatus = async function (callback = null) {
      return this.monitorResource(this.MonitorResources['write-access-status'], callback);
    };

    /**
     * Monitors the write access release appeal counter.
     * @alias monitorReleaseAppealCounter
     * @memberof API.CONTROLSTATIONMONITOR
     * @param {function} [callback] The callback function that is called when counter changes.
     * @returns {Promise<void>}
     * @example
     * API.CONTROLSTATIONMONITOR.monitorReleaseAppealCounter((counter) => {
     *   console.log('Release appeal counter:', counter);
     * });
     */
    this.monitorReleaseAppealCounter = async function (callback = null) {
      return this.monitorResource(this.MonitorResources['release-appeal-counter'], callback);
    };

    /**
     * Monitors the TPU safety protocol connection status.
     * @alias monitorTPUSafetyProtocolConnected
     * @memberof API.CONTROLSTATIONMONITOR
     * @param {function} [callback] The callback function that is called when connection status changes.
     * @returns {Promise<void>}
     * @example
     * API.CONTROLSTATIONMONITOR.monitorTPUSafetyProtocolConnected((isConnected) => {
     *   console.log('TPU safety protocol connected:', isConnected);
     * });
     */
    this.monitorTPUSafetyProtocolConnected = async function (callback = null) {
      return this.monitorResource(this.MonitorResources['tpu-safety-protocol-connected'], callback);
    };

    /**
     * Monitors the local control station connection status.
     * @alias monitorLocalIsConnected
     * @memberof API.CONTROLSTATIONMONITOR
     * @param {function} [callback] The callback function that is called when connection status changes.
     * @returns {Promise<void>}
     * @example
     * API.CONTROLSTATIONMONITOR.monitorLocalIsConnected((isConnected) => {
     *   console.log('Local control station connected:', isConnected);
     * });
     */
    this.monitorLocalIsConnected = async function (callback = null) {
      return this.monitorResource(this.MonitorResources['local-is-connected'], callback);
    };
  })();

  es.constructControlStation = true;
};

if (typeof API.constructControlStation === 'undefined') {
  factoryApiControlStation(API);
}

export default API;
export {factoryApiControlStation};