2 * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 var kPipelineStateChangeListenerNamePrefix = 'MLPipelineStateChangeListener';
18 var kSinkListenerNamePrefix = 'MLPipelineSinkListener';
19 var kCustomFilterListenerNamePrefix = 'MLPipelineCustomFilterListener';
21 //PipelineManager::createPipeline() begin
22 var ValidPipelineDisposeExceptions = ['NotFoundError', 'NotSupportedError', 'AbortError'];
24 var nextPipelineId = 1;
25 function NextPipelineId() {
26 return nextPipelineId++;
29 var ValidPipelineManagerCreatePipelineExceptions = [
37 var CreatePipeline = function() {
38 privUtils_.log('Entered PipelineManager.createPipeline()');
39 var args = validator_.validateArgs(arguments, [
42 type: validator_.Types.STRING
46 type: validator_.Types.FUNCTION,
52 if (!args.has.definition) {
53 throw new WebAPIException(
54 WebAPIException.INVALID_VALUES_ERR,
55 'Invalid parameter: pipeline definition is mandatory'
59 var pipeline = new Pipeline(NextPipelineId());
62 definition: args.definition
66 nativeArgs.listenerName = kPipelineStateChangeListenerNamePrefix + pipeline._id;
67 var stateChangeListener = function(stateObject) {
68 args.listener(stateObject.state);
70 native_.addListener(nativeArgs.listenerName, stateChangeListener);
73 var result = native_.callSync('MLPipelineManagerCreatePipeline', nativeArgs);
75 if (native_.isFailure(result)) {
76 if (nativeArgs.listenerName) {
77 native_.removeListener(nativeArgs.listenerName);
79 throw native_.getErrorObjectAndValidate(
81 ValidPipelineManagerCreatePipelineExceptions,
88 //PipelineManager::createPipeline() end
90 //Pipeline::state begin
91 var ValidPipelineStateExceptions = ['NotSupportedError', 'AbortError'];
92 var Pipeline = function(id) {
93 Object.defineProperties(this, {
97 var result = native_.callSync('MLPipelineGetState', {
100 if (native_.isFailure(result)) {
101 throw native_.getErrorObjectAndValidate(
103 ValidPipelineStateExceptions,
108 return result.result;
116 //Pipeline::state end
118 //Pipeline::start() begin
119 var ValidPipelineStartStopExceptions = [
124 Pipeline.prototype.start = function() {
129 var result = native_.callSync('MLPipelineStart', nativeArgs);
130 if (native_.isFailure(result)) {
131 throw native_.getErrorObjectAndValidate(
133 ValidPipelineStartStopExceptions,
138 //Pipeline::start() end
140 //Pipeline::stop() begin
141 Pipeline.prototype.stop = function() {
146 var result = native_.callSync('MLPipelineStop', nativeArgs);
147 if (native_.isFailure(result)) {
148 throw native_.getErrorObjectAndValidate(
150 ValidPipelineStartStopExceptions,
155 //Pipeline::stop() end
157 //Pipeline::dispose() begin
158 Pipeline.prototype.dispose = function() {
159 var result = native_.callSync('MLPipelineDispose', { id: this._id });
161 if (native_.isFailure(result)) {
162 throw native_.getErrorObjectAndValidate(
164 ValidPipelineDisposeExceptions,
169 //Pipeline::dispose() end
171 //Pipeline::getNodeInfo() begin
172 var NodeInfo = function(name, pipeline_id) {
173 Object.defineProperties(this, {
174 name: { enumerable: true, writable: false, value: name },
175 _pipeline_id: { value: pipeline_id }
179 var ValidPipelineGetNodeInfoExceptions = [
180 'InvalidValuesError',
186 Pipeline.prototype.getNodeInfo = function() {
187 var args = validator_.validateArgs(arguments, [
190 type: validator_.Types.STRING
199 var result = native_.callSync('MLPipelineGetNodeInfo', nativeArgs);
200 if (native_.isFailure(result)) {
201 throw native_.getErrorObjectAndValidate(
203 ValidPipelineGetNodeInfoExceptions,
208 return new NodeInfo(args.name, this._id);
210 //Pipeline::getNodeInfo() end
212 //Pipeline::getSource() begin
213 var ValidInputTensorsInfoExceptions = ['NotFoundError', 'AbortError'];
214 function Source(name, pipeline_id) {
215 Object.defineProperties(this, {
222 var result = native_.callSync('MLPipelineGetInputTensorsInfo', {
223 id: this._pipeline_id,
226 if (native_.isFailure(result)) {
227 throw native_.getErrorObjectAndValidate(
229 ValidInputTensorsInfoExceptions,
234 return new TensorsInfo(result.id);
243 var ValidPipelineGetSourceExceptions = [
245 'InvalidValuesError',
251 Pipeline.prototype.getSource = function() {
252 var args = validator_.validateArgs(arguments, [
255 type: validator_.Types.STRING
259 if (!args.has.name) {
260 throw new WebAPIException(
261 WebAPIException.INVALID_VALUES_ERR,
262 'Invalid parameter: name is mandatory'
271 var result = native_.callSync('MLPipelineGetSource', nativeArgs);
272 if (native_.isFailure(result)) {
273 throw native_.getErrorObjectAndValidate(
275 ValidPipelineGetSourceExceptions,
280 return new Source(args.name, this._id);
282 //Pipeline::getSource() end
284 //Pipeline::getSwitch() begin
285 function Switch(name, type, pipeline_id) {
286 Object.defineProperties(this, {
301 var ValidPipelineGetSwitchExceptions = [
303 'InvalidValuesError',
308 Pipeline.prototype.getSwitch = function() {
309 var args = validator_.validateArgs(arguments, [
312 type: validator_.Types.STRING
320 var result = native_.callSync('MLPipelineGetSwitch', nativeArgs);
321 if (native_.isFailure(result)) {
322 throw native_.getErrorObjectAndValidate(
324 ValidPipelineGetSwitchExceptions,
329 return new Switch(nativeArgs.name, result.type, this._id);
331 //Pipeline::getSwitch() end
333 //Pipeline::getValve() begin
334 var ValidValveIsOpenAndSetOpenExceptions = [
339 function Valve(name, pipeline_id) {
340 Object.defineProperties(this, {
350 var result = native_.callSync('MLPipelineValveIsOpen', {
351 id: this._pipeline_id,
355 if (native_.isFailure(result)) {
356 throw native_.getErrorObjectAndValidate(
358 ValidValveIsOpenAndSetOpenExceptions,
363 return result.result;
371 var ValidPipelineGetValveExceptions = [
372 'InvalidValuesError',
377 Pipeline.prototype.getValve = function() {
378 var args = validator_.validateArgs(arguments, [
381 type: validator_.Types.STRING
390 var result = native_.callSync('MLPipelineGetValve', nativeArgs);
391 if (native_.isFailure(result)) {
392 throw native_.getErrorObjectAndValidate(
394 ValidPipelineGetValveExceptions,
399 return new Valve(nativeArgs.name, this._id);
401 //Pipeline::getValve() end
403 //Pipeline::registerSinkListener() begin
404 var ValidRegisterSinkListenerExceptions = [
405 'InvalidValuesError',
411 Pipeline.prototype.registerSinkListener = function() {
412 var args = validator_.validateArgs(arguments, [
415 type: validator_.Types.STRING
418 name: 'sinkListener',
419 type: types_.FUNCTION
426 listenerName: kSinkListenerNamePrefix + args.name
429 var sinkListener = function(msg) {
430 var sinkData = new TensorsData(msg.tensorsDataId, msg.tensorsInfoId);
431 args.sinkListener(args.name, sinkData);
433 native_.addListener(nativeArgs.listenerName, sinkListener);
435 var result = native_.callSync('MLPipelineRegisterSinkListener', nativeArgs);
436 if (native_.isFailure(result)) {
437 native_.removeListener(nativeArgs.listenerName);
439 throw native_.getErrorObjectAndValidate(
441 ValidRegisterSinkListenerExceptions,
446 //Pipeline::registerSinkListener() end
448 //Pipeline::unregisterSinkListener() begin
449 var ValidUnregisterSinkListenerExceptions = [
450 'InvalidValuesError',
455 Pipeline.prototype.unregisterSinkListener = function() {
456 var args = validator_.validateArgs(arguments, [
459 type: validator_.Types.STRING
463 if (!args.has.name) {
464 throw new WebAPIException(
465 WebAPIException.INVALID_VALUES_ERR,
466 'Invalid parameter: sink name is mandatory'
475 var result = native_.callSync('MLPipelineUnregisterSinkListener', nativeArgs);
476 if (native_.isFailure(result)) {
477 throw native_.getErrorObjectAndValidate(
479 ValidUnregisterSinkListenerExceptions,
484 var listenerName = kSinkListenerNamePrefix + args.name;
485 native_.removeListener(listenerName);
487 //Pipeline::unregisterSinkListener() end
499 //NodeInfo::getProperty() begin
500 var ValidNodeInfoGetPropertyExceptions = [
501 'InvalidValuesError',
507 NodeInfo.prototype.getProperty = function() {
508 var args = validator_.validateArgs(arguments, [
511 type: validator_.Types.STRING
514 name: 'propertyType',
516 values: Object.keys(PropertyType)
521 id: this._pipeline_id,
524 type: args.propertyType
527 var result = native_.callSync('MLPipelineNodeInfoGetProperty', nativeArgs);
528 if (native_.isFailure(result)) {
529 throw native_.getErrorObjectAndValidate(
531 ValidNodeInfoGetPropertyExceptions,
536 return result.property;
538 //NodeInfo::getProperty() end
540 //NodeInfo::setProperty() begin
541 var ValidNodeInfoSetPropertyExceptions = [
542 'InvalidValuesError',
548 NodeInfo.prototype.setProperty = function() {
549 var args = validator_.validateArgs(arguments, [
552 type: validator_.Types.STRING
555 name: 'propertyType',
557 values: Object.keys(PropertyType)
561 type: types_.SIMPLE_TYPE
566 id: this._pipeline_id,
569 type: args.propertyType,
570 property: args.property
573 var result = native_.callSync('MLPipelineNodeInfoSetProperty', nativeArgs);
574 if (native_.isFailure(result)) {
575 throw native_.getErrorObjectAndValidate(
577 ValidNodeInfoSetPropertyExceptions,
582 //NodeInfo::setProperty() end
584 //Source::inputData() begin
585 var ValidSourceInputDataExceptions = [
591 Source.prototype.inputData = function() {
592 var args = validator_.validateArgs(arguments, [
595 type: types_.PLATFORM_OBJECT,
601 id: this._pipeline_id,
603 tensorsDataId: args.data._id
606 var result = native_.callSync('MLPipelineSourceInputData', nativeArgs);
608 if (native_.isFailure(result)) {
609 throw native_.getErrorObjectAndValidate(
611 ValidSourceInputDataExceptions,
616 return result.result;
618 //Source::inputData() end
620 //Switch::getPadList() begin
621 var ValidSwitchGetPadListExceptions = [
627 Switch.prototype.getPadList = function() {
630 id: this._pipeline_id
633 var result = native_.callSync('MLPipelineSwitchGetPadList', nativeArgs);
635 if (native_.isFailure(result)) {
636 throw native_.getErrorObjectAndValidate(
638 ValidSwitchGetPadListExceptions,
643 return result.result;
645 //Switch::getPadList() end
647 //Switch::select() begin
648 var ValidSwitchSelectExceptions = [
649 'InvalidValuesError',
654 Switch.prototype.select = function() {
655 var args = validator_.validateArgs(arguments, [
658 type: validator_.Types.STRING
662 if (!args.has.padName) {
663 throw new WebAPIException(
664 WebAPIException.INVALID_VALUES_ERR,
665 'Invalid parameter: pad name is mandatory'
670 id: this._pipeline_id,
672 padName: args.padName
675 var result = native_.callSync('MLPipelineSwitchSelect', nativeArgs);
676 if (native_.isFailure(result)) {
677 throw native_.getErrorObjectAndValidate(
679 ValidSwitchSelectExceptions,
684 //Switch::select() end
686 //Valve::setOpen() begin
687 Valve.prototype.setOpen = function() {
688 var args = validator_.validateArgs(arguments, [
691 type: validator_.Types.BOOLEAN
695 if (!args.has.open) {
696 throw new WebAPIException(
697 WebAPIException.INVALID_VALUES_ERR,
698 'Invalid parameter: open is mandatory'
703 id: this._pipeline_id,
708 var result = native_.callSync('MLPipelineValveSetOpen', nativeArgs);
709 if (native_.isFailure(result)) {
710 throw native_.getErrorObjectAndValidate(
712 ValidValveIsOpenAndSetOpenExceptions,
717 //Valve::setOpen() end
718 var MachineLearningPipeline = function() {};
720 MachineLearningPipeline.prototype.createPipeline = CreatePipeline;
722 //Pipeline::registerCustomFilter() begin
723 var ValidRegisterCustomFilterExceptions = [
724 'InvalidValuesError',
730 var ValidCustomFilterOutputErrors = ['InvalidValuesError', 'AbortError'];
732 MachineLearningPipeline.prototype.registerCustomFilter = function() {
733 var args = validator_.validateArgs(arguments, [
736 type: validator_.Types.STRING
739 name: 'customFilter',
740 type: types_.FUNCTION
744 type: types_.PLATFORM_OBJECT,
749 type: types_.PLATFORM_OBJECT,
753 name: 'errorCallback',
754 type: types_.FUNCTION,
762 listenerName: kCustomFilterListenerNamePrefix + args.name,
763 inputTensorsInfoId: args.inputInfo._id,
764 outputTensorsInfoId: args.outputInfo._id
768 * CustomFilter processing has 4 stages (the description below assumes
769 * the typical scenario with no errors):
770 * 1. (C++; non-main thread) C++ callback is called by the native API with input data.
771 * The C++ callback wraps native ml_tensors_data_h handles in TensorsData
772 * objects and sends them together with associated TensorsInfo to JS.
773 * 2. (JS; main thread) customFilterWrapper is called with the input data from C++
774 * as one of its arguments. User-provided callback processes the data.
775 * The input/output TensorsData that arrive to JS as CustomFilter arguments
776 * are unique in that they:
777 * - cannot be disposed, i.e. calling {input, output}.dispose() is no-op
778 * - input is immutable, i.e. calling input.setTensorRawData() is no-op
779 * output.setTensorRawData() modify the native nnstreamer object directly.
780 * 3. (C++; main thread) Sleeping callback thread is notified. If anything
781 * goes wrong, C++ function returns an error synchronously to stage 4.
782 * 4. (JS; main thread) If C++ returned a success, the operation stops.
783 * Otherwise, the error callback provided by the user is called.
784 * 5. (C++; non-main thread) C++ callback is woken up and returns the status
785 * received from user to pipeline.
787 var customFilterWrapper = function(msg) {
789 * Check if stage 1. was successful.
791 if (native_.isFailure(msg)) {
792 native_.callIfPossible(args.errorCallback, customFilterErrorInJs);
796 var inputData = new TensorsData(
797 msg.inputTensorsDataId,
798 msg.inputTensorsInfoId,
801 var outputData = new TensorsData(
802 msg.outputTensorsDataId,
803 msg.outputTensorsInfoId,
808 * customFilterErrorInJs records errors caused by the CustomFilter callback
809 * provided by the user.
811 var customFilterErrorInJs = null;
814 name: nativeArgs.name,
815 requestId: msg.requestId
819 jsResponse.status = converter_.toLong(
820 args.customFilter(inputData, outputData)
822 } catch (exception) {
823 var exceptionString =
824 typeof exception.toString === 'function'
825 ? exception.toString()
826 : JSON.stringify(exception);
827 customFilterErrorInJs = new WebAPIException(
828 WebAPIException.ABORT_ERR,
829 'CustomFilter has thrown exception: ' + exceptionString
833 if (!customFilterErrorInJs && jsResponse.status > 0 && jsResponse.status !== 1) {
834 customFilterErrorInJs = new WebAPIException(
835 WebAPIException.INVALID_VALUES_ERR,
836 'The only legal positive value of status returned from CustomFilter is 1'
838 jsResponse.status = -1;
844 var result = native_.callSync('MLPipelineManagerCustomFilterOutput', jsResponse);
849 if (customFilterErrorInJs) {
851 * If we detect that user-provided CustomFilter callback caused
852 * any errors in JS, the C++ layer gets the message to stop the
853 * pipeline (status == -1) and does not reply to JS with errors.
854 * Thus, "result" is a success we call the user-provided error
857 native_.callIfPossible(args.errorCallback, customFilterErrorInJs);
858 } else if (native_.isFailure(result)) {
859 var error = native_.getErrorObjectAndValidate(
861 ValidCustomFilterOutputErrors,
865 native_.callIfPossible(args.errorCallback, error);
869 if (native_.listeners_.hasOwnProperty(nativeArgs.listenerName)) {
870 throw new WebAPIException(
871 WebAPIException.INVALID_VALUES_ERR,
872 '"' + nativeArgs.name + '" custom filter is already registered'
876 native_.addListener(nativeArgs.listenerName, customFilterWrapper);
878 var result = native_.callSync('MLPipelineManagerRegisterCustomFilter', nativeArgs);
879 if (native_.isFailure(result)) {
880 native_.removeListener(nativeArgs.listenerName);
882 throw native_.getErrorObjectAndValidate(
884 ValidRegisterCustomFilterExceptions,
889 //Pipeline::registerCustomFilter() end
891 //Pipeline::unregisterCustomFilter() begin
892 var ValidUnregisterCustomFilterExceptions = [
893 'InvalidValuesError',
897 MachineLearningPipeline.prototype.unregisterCustomFilter = function() {
898 var args = validator_.validateArgs(arguments, [
901 type: validator_.Types.STRING
905 if (!args.has.name) {
906 throw new WebAPIException(
907 WebAPIException.INVALID_VALUES_ERR,
908 'Invalid parameter: custom filter name is mandatory'
912 var result = native_.callSync('MLPipelineManagerUnregisterCustomFilter', {
915 if (native_.isFailure(result)) {
916 throw native_.getErrorObjectAndValidate(
918 ValidUnregisterCustomFilterExceptions,
923 var customFilterListenerName = kCustomFilterListenerNamePrefix + args.name;
924 native_.removeListener(customFilterListenerName);
926 //Pipeline::unregisterCustomFilter() end