diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/JSONEditor/JEditor.js b/SAS/TMSS/frontend/tmss_webapp/src/components/JSONEditor/JEditor.js index 169cedee408a2534b63b6541900ebd7809b00e25..0c940d0346555d4f0b82969f0d0b6e4f04830ee0 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/components/JSONEditor/JEditor.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/components/JSONEditor/JEditor.js @@ -21,6 +21,7 @@ function Jeditor(props) { const editorRef = useRef(null); let pointingProps = useRef(null); let subBandProps = useRef(null); + let channelsPerSubbandProps = useRef([]); // let durationProps = useRef(null); let editor = null; let clockValue = 200000000; @@ -168,6 +169,7 @@ function Jeditor(props) { } pointingProps = []; subBandProps = []; + channelsPerSubbandProps = []; // durationProps = []; // Customize the pointing property to capture angle1 and angle2 to specified format for (const definitionKey in schema.definitions) { @@ -189,7 +191,6 @@ function Jeditor(props) { } } } - // Customize datatype of certain properties like subbands, duration, etc., getCustomProperties(schema.properties); getCustomProperties(schema.definitions); @@ -222,6 +223,11 @@ function Jeditor(props) { message: 'Not a valid input for Subband List' }); } + } else if (schema.validationType === "channelsPerSubband") { + // TO add eventlistener to the channels_per_subband field based on the validation type set + if(_.indexOf(channelsPerSubbandProps, path) == -1) { + channelsPerSubbandProps.push(path) + } } else if (schema.validationType === "time") { if (!timeValidator(value)) { errors.push({ @@ -316,6 +322,47 @@ function Jeditor(props) { } }) } + // Add Onchange event for Channels Per Subband field and Time Integration field to update Frequency Resolution and Time Resolution + for (const channelPath of channelsPerSubbandProps) { + if(editor?.editors[channelPath]?.formname) { + let channelsPerSubElement = document.getElementsByName(editor.editors[channelPath].formname)[0]; + if(channelsPerSubElement) { + channelsPerSubElement.addEventListener('change', (event) => { + if(event.currentTarget.value) { + updateFrequencyResolution(channelPath, event.currentTarget.value); + updateTimeResolution(channelPath, event.currentTarget.value, null); + } + }); + // Add event listener for Time Integration field + let intTimePath = channelPath.split("."); + intTimePath[intTimePath.length-1] = props.observationType==='beamforming observation'?'time_integration_factor':'integration_time'; + intTimePath = intTimePath.join("."); + if (editor.editors[intTimePath]?.formname) { + let intTimeElement = document.getElementsByName(editor.editors[intTimePath].formname)[0]; + if(intTimeElement) { + intTimeElement.addEventListener('keyup', (event) => { + updateTimeResolution(intTimePath, null, event.currentTarget.value || 0); + }); + } + } + // Disable Frequncey Resolution field + let freqResPath = channelPath.split("."); + freqResPath[freqResPath.length-1] = "frequency_resolution"; + freqResPath = freqResPath.join(".") + if(editor.editors[freqResPath]?.formname) { + document.getElementsByName(editor.editors[freqResPath]?.formname)[0].disabled = true; + } + // Disable Time resolution field + let timeResPath = channelPath.split("."); + timeResPath[timeResPath.length-1] = "time_resolution"; + timeResPath = timeResPath.join("."); + if(editor.editors[timeResPath]?.formname) { + document.getElementsByName(editor.editors[timeResPath]?.formname)[0].disabled = true; + } + } + } + } + while (element.childNodes.length > 1) { element.removeChild(element.firstChild); } @@ -408,8 +455,34 @@ function Jeditor(props) { frequency.propertyOrder = 1002; properties['frequency'] = frequency; } - - } + } else if (propertyKey.toLowerCase() === 'channels_per_subband' && propertyValue instanceof Object) { + // Add custom fields to the properties where property 'channels_per_subband' is one of the property. + let freqResolution = { title: 'Frequency Resolution (kHz)', + type: 'string', + default: '', + format: "grid", + // propertyOrder: 1002 + }; + properties['frequency_resolution'] = freqResolution; + let timeResolution = { title: `Time Resolution (${props.observationType==='beamforming observation'?'ms':'sec'})`, + type: 'string', + default: '', + format: "grid", + // propertyOrder: 1003 + }; + properties['time_resolution'] = timeResolution; + // Set validation type to channels_per_subband to add event listener. + properties['channels_per_subband']['validationType'] = "channelsPerSubband"; + // properties['channels_per_subband']['propertyOrder'] = 1001 + //>>>>>> If properyOrder is nor working, remove these lines + let propertyOrder = 10; + for (let property of _.keys(properties)) { + if (['channels_per_subband', 'frequency_resolution', 'time_resolution'].indexOf(property)<0) { + properties[property]['propertyOrder'] = 1; + propertyOrder++; + } + }//<<<<<< + } else if (propertyKey.toLowerCase() === 'duration') { let newProperty = { "type": "string", @@ -502,7 +575,7 @@ function Jeditor(props) { function updateInput(editorInput) { for (const inputKey in editorInput) { const inputValue = editorInput[inputKey]; - if (inputValue instanceof Object) { + if (inputValue instanceof Object) { if (_.indexOf(pointingProps, inputKey) >= 0) { inputValue.angle1 = UnitConverter.getAngleInput(inputValue.angle1); inputValue.angle2 = UnitConverter.getAngleInput(inputValue.angle2, true); @@ -511,11 +584,16 @@ function Jeditor(props) { (inputKey.toLowerCase() === 'list' && inputValue instanceof Array)) { editorInput[inputKey] = getSubbandInput(inputValue); editorInput['frequency'] = null; - } + } else { updateInput(inputValue); } - } + } else if (inputKey.toLowerCase() === 'channels_per_subband') { + // Add calculated values for custom fields frequency_resolution and time_resolution + editorInput['frequency_resolution'] = getFrequencyResolution(inputValue); + const integrationTime = editorInput['integration_time'] || editorInput['time_integration_factor']; + editorInput['time_resolution'] = getTimeResolution(inputValue, integrationTime); + } else if (inputKey.toLowerCase() === 'duration') { // } else if (durationProps.indexOf(inputKey)) { editorInput[inputKey] = getTimeInput(inputValue); @@ -541,9 +619,9 @@ function Jeditor(props) { } } else if ((outputKey.toLowerCase() === 'subbands' && typeof (outputValue) === 'string') || (outputKey.toLowerCase() === 'list' && typeof (outputValue) === 'string')) { - editorOutput[outputKey] = getSubbandOutput(outputValue); - - } else if(outputKey === 'frequency') { + editorOutput[outputKey] = getSubbandOutput(outputValue); + } else if(['frequency', 'frequency_resolution', 'time_resolution'].indexOf(outputKey)>=0 ) { + // Remove custom read only fields before passing the output back to the parent delete editorOutput[outputKey]; } else if (outputKey.toLowerCase() === 'duration') { @@ -592,6 +670,33 @@ function Jeditor(props) { return (hh < 10 ? `0${hh}` : `${hh}`) + ':' + (mm < 10 ? `0${mm}` : `${mm}`) + ':' + (ss < 10 ? `0${ss}` : `${ss}`); } + /** + * Returns the frequency resolution value based on observationType props of the component + * @param {Number} channelsPerSubband + * @returns Number - calculated value + */ + function getFrequencyResolution(channelsPerSubband) { + if (props.observationType) { + if (props.observationType.toLowerCase() === 'beamforming observation') { + return (clockValue / 1024 * channelsPerSubband * 1/1000).toFixed(2); + } else if (props.observationType.toLowerCase() === 'target observation') { + return (clockValue / 1024 / channelsPerSubband * 1/1000).toFixed(2); + } + } else { + return null; + } + } + + /** + * Returns calculated time resolution. + * @param {Number} channelsPerSubband + * @param {Number} timeIntegrationSteps + * @returns Number calculated value + */ + function getTimeResolution(channelsPerSubband, timeIntegrationSteps) { + return 1 / clockValue * 1024 * channelsPerSubband * timeIntegrationSteps * (props.observationType==='beamforming observation'?1000:1); + } + /** * Validates if the subband list custom field * @param {String} prpOutput @@ -656,6 +761,46 @@ function Jeditor(props) { return true; } + /** + * Function to calculate and update the readonly field frequency_resolution when channels_per_subband value is changed + * @param {string} channelsPropPath - path of the channels_per_subband preoperty to get the id or name of the field + * @param {number} channelsPerSubband - value selected in the dropdown + */ + function updateFrequencyResolution(channelsPropPath, channelsPerSubband) { + let freqResPath = channelsPropPath.split("."); + freqResPath[freqResPath.length-1] = "frequency_resolution"; + freqResPath = freqResPath.join("."); + if(editor.editors[freqResPath]?.formname) { + // Set values for Frequency Resolution field. + document.getElementById(editor.editors[freqResPath]?.formname).value = getFrequencyResolution(channelsPerSubband); + } + } + + /** + * Function to calculate and update the readonly field time_resolution when channels_per_subband or integration time value is changed + * @param {string} channelsPropPath - path of the channels_per_subband preoperty to get the id or name of the field + * @param {number} channelsPerSubband - value selected in the dropdown, nullable + * @param {number} integrationTime - value selected in the dropdown, nullable + */ + function updateTimeResolution(propPath, channelsPerSubband, integrationTime) { + let timeResPath = propPath.split("."); + timeResPath[timeResPath.length-1] = "time_resolution"; + timeResPath = timeResPath.join("."); + if (!channelsPerSubband) { + let channelsPath = propPath.split("."); + channelsPath[channelsPath.length-1] = 'channels_per_subband'; + channelsPerSubband = document.getElementById(editor.editors[channelsPath.join('.')]?.formname)?.value; + } + if (!integrationTime) { + let intTimePath = propPath.split("."); + intTimePath[intTimePath.length-1] = props.observationType==='beamforming observation'?'time_integration_factor':'integration_time'; + integrationTime = document.getElementById(editor.editors[intTimePath.join('.')]?.formname)?.value; + } + if(editor.editors[timeResPath]?.formname) { + // Set values for Time Resolution field. + document.getElementById(editor.editors[timeResPath]?.formname).value = getTimeResolution(channelsPerSubband, integrationTime); + } + } /** * Convert the string input for subband list to Array * @param {String} prpOutput diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/Beamformer.js b/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/Beamformer.js index ce67c36e25cfe4ce75f2b5ab6033d5942a4c9c36..7eb86e20fe6df5eb0638c1b0ef9a82c80a3df446 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/Beamformer.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/Beamformer.js @@ -256,7 +256,8 @@ export default class Beamformer extends Component { schema: schema, initValue: this.state.paramsOutput, callback: this.setEditorOutput, - parentFunction: this.setEditorFunction + parentFunction: this.setEditorFunction, + observationType: 'beamforming observation' }); } return ( diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js index 1e057e96251ac18e891b7a9f2e7ac00600bebd8b..812ef4370e5d3e35cb8a949c685b57eb74cc37ee 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js @@ -166,6 +166,7 @@ export class SchedulingUnitCreate extends Component { let schema = { type: 'object', additionalProperties: false, properties: {}, definitions:{} }; + let observationType = null; let bandPassFilter = null; const $strategyRefs = await $RefParser.resolve(observStrategy.template); // TODo: This schema reference resolving code has to be moved to common file and needs to rework @@ -179,6 +180,9 @@ export class SchedulingUnitCreate extends Component { const task = tasks[taskName]; if (task) { const taskTemplate = _.find(this.taskTemplates, {'name': task['specifications_template']}); + if (['target observation', 'beamforming observation'].indexOf(taskTemplate.name.toLowerCase()) >= 0) { + observationType = taskTemplate.name; + } if (taskTemplate.type_value==='observation' && task.specifications_doc.station_groups) { station_group = task.specifications_doc.station_groups; } @@ -208,6 +212,7 @@ export class SchedulingUnitCreate extends Component { } } this.setState({observStrategy: observStrategy, paramsSchema: _.cloneDeep(schema), + observationType: observationType, paramsOutput: paramsOutput, stationGroup: station_group, bandPassFilter:bandPassFilter, isDirty: true}); publish('edit-dirty', true); @@ -571,7 +576,8 @@ export class SchedulingUnitCreate extends Component { initValue: this.state.paramsOutput, callback: this.setEditorOutput, parentFunction: this.setEditorFunction, - bandPassFilter: this.state.bandPassFilter + bandPassFilter: this.state.bandPassFilter, + observationType: this.state.observationType }); } return ( diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/edit.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/edit.js index 4394a790448032f02b436f361f40cf5deec096fc..a502bc0a220a15919bcfb081572808420b4db109 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/edit.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/edit.js @@ -217,6 +217,7 @@ export class TaskEdit extends Component { const { isLoading } = this.state; const taskSchema = this.state.taskSchema; + const template = _.find(this.state.taskTemplates, {'id': this.state.task.specifications_template_id}); let jeditor = null; if (this.state.taskSchema) { @@ -225,7 +226,8 @@ export class TaskEdit extends Component { //initValue: this.state.templateOutput[this.state.task.specifications_template_id], initValue: this.templateOutput[this.state.task.specifications_template_id], callback: this.setEditorOutput, - parentFunction: this.setEditorFunction + parentFunction: this.setEditorFunction, + observationType: template.name }); } @@ -266,17 +268,6 @@ export class TaskEdit extends Component { </label> </div> </div> - {/* <div className="p-field p-grid"> - <label htmlFor="createdAt" className="col-lg-2 col-md-2 col-sm-12">Created At</label> - <div className="col-lg-4 col-md-4 col-sm-12"> - <Calendar showTime={true} hourFormat="24" value={created_at} onChange={(e) => this.setState({date2: e.value})}></Calendar> - </div> - <label htmlFor="updatedAt" className="col-lg-2 col-md-2 col-sm-12">Updated At</label> - <div className="col-lg-4 col-md-4 col-sm-12"> - <Calendar showTime={true} hourFormat="24" value={updated_at} onChange={(e) => this.setState({date2: e.value})}></Calendar> - </div> - </div> - */} <div className="p-field p-grid"> <label htmlFor="tags" className="col-lg-2 col-md-2 col-sm-12">Tags</label> <div className="col-lg-4 col-md-4 col-sm-12"> @@ -298,7 +289,7 @@ export class TaskEdit extends Component { <div className="col-lg-4 col-md-4 col-sm-12"> <Dropdown optionLabel="name" optionValue="id" value={this.state.task.specifications_template_id} - options={this.state.taskTemplates} + options={this.state.taskTemplates} disabled onChange={(e) => {this.changeTaskTemplate(e.value)}} placeholder="Select Task Template"/> </div> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/view.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/view.js index 741fd04ca60ab970256eec535c09a389ba6a30e0..5ad3ca755b7d9b8a2ae7f2839069143ac30f3bc0 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/view.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/view.js @@ -258,7 +258,8 @@ export class TaskView extends Component { disabled: true, // callback: this.setEditorOutput, parentFunction: this.setEditorFunction, - resolveExtRef: true + resolveExtRef: true, + observationType: this.state.taskTemplate.name }); }