diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 14cf99ca5..92bf792c9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,9 +9,12 @@ repos: rev: v2.2.1 hooks: - id: prettier - types: [javascript] + files: \.[j]sx?$ # *.js and *.jsx + types: [file] - repo: https://github.com/pre-commit/mirrors-eslint rev: v7.19.0 hooks: - - id: eslint \ No newline at end of file + - id: eslint + files: \.[j]sx?$ # *.js and *.jsx + types: [file] diff --git a/Jenkinsfile b/Jenkinsfile index d0966a68c..323eec94c 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -17,6 +17,7 @@ pipeline { branch "bugfix/*" branch "hotfix/*" branch "release/*" + branch "tickets/*" } } steps { @@ -27,7 +28,7 @@ pipeline { if (slashPosition > 0) { git_tag = git_branch.substring(slashPosition + 1, git_branch.length()) git_branch = git_branch.substring(0, slashPosition) - if (git_branch == "release" || git_branch == "hotfix" || git_branch == "bugfix") { + if (git_branch == "release" || git_branch == "hotfix" || git_branch == "bugfix" || git_branch == "tickets") { image_tag = git_tag } } @@ -45,6 +46,7 @@ pipeline { branch "bugfix/*" branch "hotfix/*" branch "release/*" + branch "tickets/*" } } steps { diff --git a/love/package.json b/love/package.json index c1e76fcb7..1fd086b77 100644 --- a/love/package.json +++ b/love/package.json @@ -17,6 +17,7 @@ "html2canvas": "^1.0.0-rc.5", "lodash": "^4.17.19", "lodash.throttle": "^4.1.1", + "lodash.debounce": "^4.0.8", "luxon": "^1.23.0", "ml-matrix": "^6.5.0", "moment-range": "^4.0.0", @@ -45,6 +46,7 @@ "redux-logger": "^3.0.6", "redux-thunk": "^2.3.0", "redux-undo": "^1.0.0", + "remarkable": "^2.0.1", "reselect": "^4.0.0", "resize-observer-polyfill": "^1.5.1", "rfdc": "^1.1.4", diff --git a/love/src/Config.js b/love/src/Config.js index a388978e9..1803f43a2 100644 --- a/love/src/Config.js +++ b/love/src/Config.js @@ -14,7 +14,7 @@ export const DATE_TIME_FORMAT = 'YYYY/MM/DD, HH:mm:ss'; // eslint-disable-next-line export const CSCSummaryHierarchy = { - 'Aux Telescope': { + 'Auxiliary Telescope': { ATTCS: [ { name: 'ATMCS', salindex: 0 }, { name: 'ATPtg', salindex: 0 }, @@ -23,7 +23,6 @@ export const CSCSummaryHierarchy = { { name: 'ATAOS', salindex: 0 }, { name: 'ATPneumatics', salindex: 0 }, { name: 'ATHexapod', salindex: 0 }, - { name: 'GenericCamera', salindex: 1 }, ], ATCalSys: [ { name: 'ATMonochromator', salindex: 0 }, @@ -42,18 +41,31 @@ export const CSCSummaryHierarchy = { ], }, 'Main Telescope': { - 'CSC Group 1': [ - { name: 'Test', salindex: 1 }, - { name: 'Test', salindex: 2 }, + MTCS: [ + { name: 'MTMount', salindex: 0 }, + { name: 'MTPtg', salindex: 0 }, + { name: 'MTAOS', salindex: 0 }, + { name: 'MTM1M3', salindex: 0 }, + { name: 'MTM2', salindex: 0 }, + { name: 'MTHexapod', salindex: 1 }, + { name: 'MTHexapod', salindex: 2 }, + { name: 'MTRotator', salindex: 0 }, + { name: 'MTDome', salindex: 0 }, + { name: 'MTDomeTrajectory', salindex: 0 }, + ], + ComCam: [ + { name: 'CCCamera', salindex: 0 }, + { name: 'CCArchiver', salindex: 0 }, + { name: 'CCHeaderService', salindex: 0 }, ], - 'CSC Group 2': [], }, Observatory: { - Queue: [ + HigherLevel: [ { name: 'ScriptQueue', salindex: 1 }, { name: 'ScriptQueue', salindex: 2 }, + { name: 'Watcher', salindex: 0 }, ], - WeatherStation: [ + Environment: [ { name: 'DIMM', salindex: 1 }, { name: 'DIMM', salindex: 2 }, { name: 'WeatherStation', salindex: 1 }, @@ -289,12 +301,10 @@ export const stateToStyleMotorBrake = { export const HEARTBEAT_COMPONENTS = { MANAGER: 'Manager', - EVENTS: 'Events', - TELEMETRIES: 'Telemetries', - HEARTBEATS: 'CSCHeartbeats', - LOVE: 'LOVE CSC', - SCRIPTQUEUE: 'ScriptQueue-1', COMMANDER: 'Commander', + /** Deprecated: used for old producer version */ + /* EVENTS: 'Events', + TELEMETRIES: 'Telemetries', */ }; export const severityToStatus = { @@ -313,161 +323,232 @@ export const severityEnum = { critical: 4, }; +/** + * Available commands in the TCS and their parameters. Each command is represented + * as a dictionary key and their parameters as the values of said dictionary. + * Within each dictionary value, each parameter is represented in a separate dictionary. + * Each key in this new dictionary contains the parameter name and each corresponding + * value contains a 2 item array with its first element being the param type, e.g. + * 'string', 'angle', 'number', 'boolean'. The second element contains the default + * value. + * + */ + +const rotTypes = [ + { label: 'Sky', value: 0 }, + { label: 'SkyAuto', value: 1 }, + { label: 'Parallactic', value: 2 }, + { label: 'PhysicalSky', value: 3 }, + { label: 'Physical', value: 4 }, +]; + +export const TCSCommands = { + slew_object: { + name: ['string', undefined], + rot: ['angle', 0.0], + rot_type: [rotTypes, rotTypes[1]], + slew_timeout: ['number', 240.0], + }, + slew_icrs: { + ra: ['angle', undefined], + dec: ['angle', undefined], + rot: ['angle', 0.0], + rot_type: [rotTypes, rotTypes[1]], + target_name: ['string', 'slew_icrs'], + slew_timeout: ['number', 240.0], + stop_before_slew: ['boolean', true], + wait_settle: ['boolean', true], + }, + point_azel: { + az: ['angle', undefined], + el: ['angle', undefined], + rot_tel: ['angle', 0.0], + target_name: ['string', 'azel_target'], + wait_dome: ['boolean', false], + slew_timeout: ['number', 1200.0], + }, + offset_xy: { + x: ['number', undefined], + y: ['number', undefined], + relative: ['boolean', false], + persistent: ['boolean', false], + }, + offset_azel: { + az: ['number', undefined], + el: ['number', undefined], + relative: ['boolean', false], + persistent: ['boolean', false], + }, + offset_radec: { + ra: ['angle', undefined], + dec: ['angle', undefined], + }, + slew_dome_to: { + az: ['number', undefined], + }, + // focus_offset: {}, + home_dome: {}, +}; + +export const ATCSCommands = { + ...TCSCommands, +}; + export const M1M3ActuatorPositions = [ - {id: 101, position: [-3.0582000e+01, 0.0000000e+00]}, - {id: 102, position: [-5.6794000e+01, 0.0000000e+00]}, - {id: 103, position: [-8.3007000e+01, 0.0000000e+00]}, - {id: 104, position: [-1.0922000e+02, 0.0000000e+00]}, - {id: 105, position: [-1.3543300e+02, 0.0000000e+00]}, - {id: 106, position: [-1.5622100e+02, 0.0000000e+00]}, - {id: 107, position: [-1.7475000e+01, -2.2701000e+01]}, - {id: 108, position: [-4.3688000e+01, -2.2701000e+01]}, - {id: 109, position: [-6.9901000e+01, -2.2701000e+01]}, - {id: 110, position: [-9.6114000e+01, -2.2701000e+01]}, - {id: 111, position: [-1.2232600e+02, -2.2701000e+01]}, - {id: 112, position: [-1.4853900e+02, -2.2701000e+01]}, - {id: 113, position: [ 0.0000000e+00, -4.5402000e+01]}, - {id: 114, position: [-3.0582000e+01, -4.5402000e+01]}, - {id: 115, position: [-5.6794000e+01, -4.5402000e+01]}, - {id: 116, position: [-8.3007000e+01, -4.5402000e+01]}, - {id: 117, position: [-1.0922000e+02, -4.5402000e+01]}, - {id: 118, position: [-1.3543300e+02, -4.5402000e+01]}, - {id: 119, position: [-1.5356300e+02, -3.9279000e+01]}, - {id: 120, position: [-1.7475000e+01, -6.8103000e+01]}, - {id: 121, position: [-4.3688000e+01, -6.8103000e+01]}, - {id: 122, position: [-6.9901000e+01, -6.8103000e+01]}, - {id: 123, position: [-9.6113000e+01, -6.8103000e+01]}, - {id: 124, position: [-1.2232600e+02, -6.8103000e+01]}, - {id: 125, position: [-1.4663200e+02, -5.9762000e+01]}, - {id: 126, position: [ 0.0000000e+00, -9.0804000e+01]}, - {id: 127, position: [-3.0582000e+01, -9.0804000e+01]}, - {id: 128, position: [-5.6794000e+01, -9.0804000e+01]}, - {id: 129, position: [-8.3007000e+01, -9.0804000e+01]}, - {id: 130, position: [-1.0922000e+02, -9.0804000e+01]}, - {id: 131, position: [-1.3338400e+02, -8.5331000e+01]}, - {id: 132, position: [-1.7475000e+01, -1.1350500e+02]}, - {id: 133, position: [-4.3688000e+01, -1.1350500e+02]}, - {id: 134, position: [-6.9901000e+01, -1.1350500e+02]}, - {id: 135, position: [-9.6113000e+01, -1.1350500e+02]}, - {id: 136, position: [-1.1572300e+02, -1.0807800e+02]}, - {id: 137, position: [-8.7380000e+00, -1.3620600e+02]}, - {id: 138, position: [-3.4950000e+01, -1.3620600e+02]}, - {id: 139, position: [-6.1163000e+01, -1.2863900e+02]}, - {id: 140, position: [-8.2273000e+01, -1.3529100e+02]}, - {id: 141, position: [-1.4399000e+01, -1.5768700e+02]}, - {id: 142, position: [-4.2720000e+01, -1.5247100e+02]}, - {id: 143, position: [-6.3150000e+01, -1.4538500e+02]}, - {id: 207, position: [ 1.7475000e+01, -2.2701000e+01]}, - {id: 208, position: [ 4.3688000e+01, -2.2701000e+01]}, - {id: 209, position: [ 6.9901000e+01, -2.2701000e+01]}, - {id: 210, position: [ 9.6114000e+01, -2.2701000e+01]}, - {id: 211, position: [ 1.2232600e+02, -2.2701000e+01]}, - {id: 212, position: [ 1.4853900e+02, -2.2701000e+01]}, - {id: 214, position: [ 3.0582000e+01, -4.5402000e+01]}, - {id: 215, position: [ 5.6794000e+01, -4.5402000e+01]}, - {id: 216, position: [ 8.3007000e+01, -4.5402000e+01]}, - {id: 217, position: [ 1.0922000e+02, -4.5402000e+01]}, - {id: 218, position: [ 1.3543300e+02, -4.5402000e+01]}, - {id: 219, position: [ 1.5356300e+02, -3.9279000e+01]}, - {id: 220, position: [ 1.7475000e+01, -6.8103000e+01]}, - {id: 221, position: [ 4.3688000e+01, -6.8103000e+01]}, - {id: 222, position: [ 6.9901000e+01, -6.8103000e+01]}, - {id: 223, position: [ 9.6113000e+01, -6.8103000e+01]}, - {id: 224, position: [ 1.2232600e+02, -6.8103000e+01]}, - {id: 225, position: [ 1.4663200e+02, -5.9762000e+01]}, - {id: 227, position: [ 3.0582000e+01, -9.0804000e+01]}, - {id: 228, position: [ 5.6794000e+01, -9.0804000e+01]}, - {id: 229, position: [ 8.3007000e+01, -9.0804000e+01]}, - {id: 230, position: [ 1.0922000e+02, -9.0804000e+01]}, - {id: 231, position: [ 1.3338400e+02, -8.5331000e+01]}, - {id: 232, position: [ 1.7475000e+01, -1.1350500e+02]}, - {id: 233, position: [ 4.3688000e+01, -1.1350500e+02]}, - {id: 234, position: [ 6.9901000e+01, -1.1350500e+02]}, - {id: 235, position: [ 9.6113000e+01, -1.1350500e+02]}, - {id: 236, position: [ 1.1572300e+02, -1.0807800e+02]}, - {id: 237, position: [ 8.7380000e+00, -1.3620600e+02]}, - {id: 238, position: [ 3.4950000e+01, -1.3620600e+02]}, - {id: 239, position: [ 6.1163000e+01, -1.2863900e+02]}, - {id: 240, position: [ 8.2273000e+01, -1.3529100e+02]}, - {id: 241, position: [ 1.4399000e+01, -1.5768700e+02]}, - {id: 242, position: [ 4.2720000e+01, -1.5247100e+02]}, - {id: 243, position: [ 6.3150000e+01, -1.4538500e+02]}, - {id: 301, position: [ 3.0582000e+01, 0.0000000e+00]}, - {id: 302, position: [ 5.6794000e+01, 0.0000000e+00]}, - {id: 303, position: [ 8.3007000e+01, 0.0000000e+00]}, - {id: 304, position: [ 1.0922000e+02, 0.0000000e+00]}, - {id: 305, position: [ 1.3543300e+02, 0.0000000e+00]}, - {id: 306, position: [ 1.5622100e+02, -9.7000000e-02]}, - {id: 307, position: [ 1.7475000e+01, 2.2701000e+01]}, - {id: 308, position: [ 4.3688000e+01, 2.2701000e+01]}, - {id: 309, position: [ 6.9901000e+01, 2.2701000e+01]}, - {id: 310, position: [ 9.6114000e+01, 2.2701000e+01]}, - {id: 311, position: [ 1.2232600e+02, 2.2701000e+01]}, - {id: 312, position: [ 1.4853900e+02, 2.2701000e+01]}, - {id: 313, position: [ 0.0000000e+00, 4.5402000e+01]}, - {id: 314, position: [ 3.0582000e+01, 4.5402000e+01]}, - {id: 315, position: [ 5.6794000e+01, 4.5402000e+01]}, - {id: 316, position: [ 8.3007000e+01, 4.5402000e+01]}, - {id: 317, position: [ 1.0922000e+02, 4.5402000e+01]}, - {id: 318, position: [ 1.3543300e+02, 4.5402000e+01]}, - {id: 319, position: [ 1.5356300e+02, 3.9279000e+01]}, - {id: 320, position: [ 1.7475000e+01, 6.8103000e+01]}, - {id: 321, position: [ 4.3688000e+01, 6.8103000e+01]}, - {id: 322, position: [ 6.9901000e+01, 6.8103000e+01]}, - {id: 323, position: [ 9.6113000e+01, 6.8103000e+01]}, - {id: 324, position: [ 1.2232600e+02, 6.8103000e+01]}, - {id: 325, position: [ 1.4663200e+02, 5.9762000e+01]}, - {id: 326, position: [ 0.0000000e+00, 9.0804000e+01]}, - {id: 327, position: [ 3.0582000e+01, 9.0804000e+01]}, - {id: 328, position: [ 5.6794000e+01, 9.0804000e+01]}, - {id: 329, position: [ 8.3007000e+01, 9.0804000e+01]}, - {id: 330, position: [ 1.0922000e+02, 9.0804000e+01]}, - {id: 331, position: [ 1.3338400e+02, 8.5331000e+01]}, - {id: 332, position: [ 1.7475000e+01, 1.1350500e+02]}, - {id: 333, position: [ 4.3688000e+01, 1.1350500e+02]}, - {id: 334, position: [ 6.9901000e+01, 1.1350500e+02]}, - {id: 335, position: [ 9.6113000e+01, 1.1350500e+02]}, - {id: 336, position: [ 1.1572300e+02, 1.0807800e+02]}, - {id: 337, position: [ 8.7380000e+00, 1.3620600e+02]}, - {id: 338, position: [ 3.4950000e+01, 1.3620600e+02]}, - {id: 339, position: [ 6.1163000e+01, 1.2863900e+02]}, - {id: 340, position: [ 8.2273000e+01, 1.3529100e+02]}, - {id: 341, position: [ 1.4399000e+01, 1.5768700e+02]}, - {id: 342, position: [ 4.2720000e+01, 1.5247100e+02]}, - {id: 343, position: [ 6.3150000e+01, 1.4538500e+02]}, - {id: 407, position: [-1.7475000e+01, 2.2701000e+01]}, - {id: 408, position: [-4.3688000e+01, 2.2701000e+01]}, - {id: 409, position: [-6.9901000e+01, 2.2701000e+01]}, - {id: 410, position: [-9.6114000e+01, 2.2701000e+01]}, - {id: 411, position: [-1.2232600e+02, 2.2701000e+01]}, - {id: 412, position: [-1.4853900e+02, 2.2701000e+01]}, - {id: 414, position: [-3.0582000e+01, 4.5402000e+01]}, - {id: 415, position: [-5.6794000e+01, 4.5402000e+01]}, - {id: 416, position: [-8.3007000e+01, 4.5402000e+01]}, - {id: 417, position: [-1.0922000e+02, 4.5402000e+01]}, - {id: 418, position: [-1.3543300e+02, 4.5402000e+01]}, - {id: 419, position: [-1.5356300e+02, 3.9279000e+01]}, - {id: 420, position: [-1.7475000e+01, 6.8103000e+01]}, - {id: 421, position: [-4.3688000e+01, 6.8103000e+01]}, - {id: 422, position: [-6.9901000e+01, 6.8103000e+01]}, - {id: 423, position: [-9.6113000e+01, 6.8103000e+01]}, - {id: 424, position: [-1.2232600e+02, 6.8103000e+01]}, - {id: 425, position: [-1.4663200e+02, 5.9762000e+01]}, - {id: 427, position: [-3.0582000e+01, 9.0804000e+01]}, - {id: 428, position: [-5.6794000e+01, 9.0804000e+01]}, - {id: 429, position: [-8.3007000e+01, 9.0804000e+01]}, - {id: 430, position: [-1.0922000e+02, 9.0804000e+01]}, - {id: 431, position: [-1.3338400e+02, 8.5331000e+01]}, - {id: 432, position: [-1.7475000e+01, 1.1350500e+02]}, - {id: 433, position: [-4.3688000e+01, 1.1350500e+02]}, - {id: 434, position: [-6.9901000e+01, 1.1350500e+02]}, - {id: 435, position: [-9.6113000e+01, 1.1350500e+02]}, - {id: 436, position: [-1.1572300e+02, 1.0807800e+02]}, - {id: 437, position: [-8.7380000e+00, 1.3620600e+02]}, - {id: 438, position: [-3.4950000e+01, 1.3620600e+02]}, - {id: 439, position: [-6.1163000e+01, 1.2863900e+02]}, - {id: 440, position: [-8.2273000e+01, 1.3529100e+02]}, - {id: 441, position: [-1.4399000e+01, 1.5768700e+02]}, - {id: 442, position: [-4.2720000e+01, 1.5247100e+02]}, - {id: 443, position: [-6.3150000e+01, 1.4538500e+02]} - ] \ No newline at end of file + { id: 101, position: [-3.0582e1, 0.0] }, + { id: 102, position: [-5.6794e1, 0.0] }, + { id: 103, position: [-8.3007e1, 0.0] }, + { id: 104, position: [-1.0922e2, 0.0] }, + { id: 105, position: [-1.35433e2, 0.0] }, + { id: 106, position: [-1.56221e2, 0.0] }, + { id: 107, position: [-1.7475e1, -2.2701e1] }, + { id: 108, position: [-4.3688e1, -2.2701e1] }, + { id: 109, position: [-6.9901e1, -2.2701e1] }, + { id: 110, position: [-9.6114e1, -2.2701e1] }, + { id: 111, position: [-1.22326e2, -2.2701e1] }, + { id: 112, position: [-1.48539e2, -2.2701e1] }, + { id: 113, position: [0.0, -4.5402e1] }, + { id: 114, position: [-3.0582e1, -4.5402e1] }, + { id: 115, position: [-5.6794e1, -4.5402e1] }, + { id: 116, position: [-8.3007e1, -4.5402e1] }, + { id: 117, position: [-1.0922e2, -4.5402e1] }, + { id: 118, position: [-1.35433e2, -4.5402e1] }, + { id: 119, position: [-1.53563e2, -3.9279e1] }, + { id: 120, position: [-1.7475e1, -6.8103e1] }, + { id: 121, position: [-4.3688e1, -6.8103e1] }, + { id: 122, position: [-6.9901e1, -6.8103e1] }, + { id: 123, position: [-9.6113e1, -6.8103e1] }, + { id: 124, position: [-1.22326e2, -6.8103e1] }, + { id: 125, position: [-1.46632e2, -5.9762e1] }, + { id: 126, position: [0.0, -9.0804e1] }, + { id: 127, position: [-3.0582e1, -9.0804e1] }, + { id: 128, position: [-5.6794e1, -9.0804e1] }, + { id: 129, position: [-8.3007e1, -9.0804e1] }, + { id: 130, position: [-1.0922e2, -9.0804e1] }, + { id: 131, position: [-1.33384e2, -8.5331e1] }, + { id: 132, position: [-1.7475e1, -1.13505e2] }, + { id: 133, position: [-4.3688e1, -1.13505e2] }, + { id: 134, position: [-6.9901e1, -1.13505e2] }, + { id: 135, position: [-9.6113e1, -1.13505e2] }, + { id: 136, position: [-1.15723e2, -1.08078e2] }, + { id: 137, position: [-8.738, -1.36206e2] }, + { id: 138, position: [-3.495e1, -1.36206e2] }, + { id: 139, position: [-6.1163e1, -1.28639e2] }, + { id: 140, position: [-8.2273e1, -1.35291e2] }, + { id: 141, position: [-1.4399e1, -1.57687e2] }, + { id: 142, position: [-4.272e1, -1.52471e2] }, + { id: 143, position: [-6.315e1, -1.45385e2] }, + { id: 207, position: [1.7475e1, -2.2701e1] }, + { id: 208, position: [4.3688e1, -2.2701e1] }, + { id: 209, position: [6.9901e1, -2.2701e1] }, + { id: 210, position: [9.6114e1, -2.2701e1] }, + { id: 211, position: [1.22326e2, -2.2701e1] }, + { id: 212, position: [1.48539e2, -2.2701e1] }, + { id: 214, position: [3.0582e1, -4.5402e1] }, + { id: 215, position: [5.6794e1, -4.5402e1] }, + { id: 216, position: [8.3007e1, -4.5402e1] }, + { id: 217, position: [1.0922e2, -4.5402e1] }, + { id: 218, position: [1.35433e2, -4.5402e1] }, + { id: 219, position: [1.53563e2, -3.9279e1] }, + { id: 220, position: [1.7475e1, -6.8103e1] }, + { id: 221, position: [4.3688e1, -6.8103e1] }, + { id: 222, position: [6.9901e1, -6.8103e1] }, + { id: 223, position: [9.6113e1, -6.8103e1] }, + { id: 224, position: [1.22326e2, -6.8103e1] }, + { id: 225, position: [1.46632e2, -5.9762e1] }, + { id: 227, position: [3.0582e1, -9.0804e1] }, + { id: 228, position: [5.6794e1, -9.0804e1] }, + { id: 229, position: [8.3007e1, -9.0804e1] }, + { id: 230, position: [1.0922e2, -9.0804e1] }, + { id: 231, position: [1.33384e2, -8.5331e1] }, + { id: 232, position: [1.7475e1, -1.13505e2] }, + { id: 233, position: [4.3688e1, -1.13505e2] }, + { id: 234, position: [6.9901e1, -1.13505e2] }, + { id: 235, position: [9.6113e1, -1.13505e2] }, + { id: 236, position: [1.15723e2, -1.08078e2] }, + { id: 237, position: [8.738, -1.36206e2] }, + { id: 238, position: [3.495e1, -1.36206e2] }, + { id: 239, position: [6.1163e1, -1.28639e2] }, + { id: 240, position: [8.2273e1, -1.35291e2] }, + { id: 241, position: [1.4399e1, -1.57687e2] }, + { id: 242, position: [4.272e1, -1.52471e2] }, + { id: 243, position: [6.315e1, -1.45385e2] }, + { id: 301, position: [3.0582e1, 0.0] }, + { id: 302, position: [5.6794e1, 0.0] }, + { id: 303, position: [8.3007e1, 0.0] }, + { id: 304, position: [1.0922e2, 0.0] }, + { id: 305, position: [1.35433e2, 0.0] }, + { id: 306, position: [1.56221e2, -9.7e-2] }, + { id: 307, position: [1.7475e1, 2.2701e1] }, + { id: 308, position: [4.3688e1, 2.2701e1] }, + { id: 309, position: [6.9901e1, 2.2701e1] }, + { id: 310, position: [9.6114e1, 2.2701e1] }, + { id: 311, position: [1.22326e2, 2.2701e1] }, + { id: 312, position: [1.48539e2, 2.2701e1] }, + { id: 313, position: [0.0, 4.5402e1] }, + { id: 314, position: [3.0582e1, 4.5402e1] }, + { id: 315, position: [5.6794e1, 4.5402e1] }, + { id: 316, position: [8.3007e1, 4.5402e1] }, + { id: 317, position: [1.0922e2, 4.5402e1] }, + { id: 318, position: [1.35433e2, 4.5402e1] }, + { id: 319, position: [1.53563e2, 3.9279e1] }, + { id: 320, position: [1.7475e1, 6.8103e1] }, + { id: 321, position: [4.3688e1, 6.8103e1] }, + { id: 322, position: [6.9901e1, 6.8103e1] }, + { id: 323, position: [9.6113e1, 6.8103e1] }, + { id: 324, position: [1.22326e2, 6.8103e1] }, + { id: 325, position: [1.46632e2, 5.9762e1] }, + { id: 326, position: [0.0, 9.0804e1] }, + { id: 327, position: [3.0582e1, 9.0804e1] }, + { id: 328, position: [5.6794e1, 9.0804e1] }, + { id: 329, position: [8.3007e1, 9.0804e1] }, + { id: 330, position: [1.0922e2, 9.0804e1] }, + { id: 331, position: [1.33384e2, 8.5331e1] }, + { id: 332, position: [1.7475e1, 1.13505e2] }, + { id: 333, position: [4.3688e1, 1.13505e2] }, + { id: 334, position: [6.9901e1, 1.13505e2] }, + { id: 335, position: [9.6113e1, 1.13505e2] }, + { id: 336, position: [1.15723e2, 1.08078e2] }, + { id: 337, position: [8.738, 1.36206e2] }, + { id: 338, position: [3.495e1, 1.36206e2] }, + { id: 339, position: [6.1163e1, 1.28639e2] }, + { id: 340, position: [8.2273e1, 1.35291e2] }, + { id: 341, position: [1.4399e1, 1.57687e2] }, + { id: 342, position: [4.272e1, 1.52471e2] }, + { id: 343, position: [6.315e1, 1.45385e2] }, + { id: 407, position: [-1.7475e1, 2.2701e1] }, + { id: 408, position: [-4.3688e1, 2.2701e1] }, + { id: 409, position: [-6.9901e1, 2.2701e1] }, + { id: 410, position: [-9.6114e1, 2.2701e1] }, + { id: 411, position: [-1.22326e2, 2.2701e1] }, + { id: 412, position: [-1.48539e2, 2.2701e1] }, + { id: 414, position: [-3.0582e1, 4.5402e1] }, + { id: 415, position: [-5.6794e1, 4.5402e1] }, + { id: 416, position: [-8.3007e1, 4.5402e1] }, + { id: 417, position: [-1.0922e2, 4.5402e1] }, + { id: 418, position: [-1.35433e2, 4.5402e1] }, + { id: 419, position: [-1.53563e2, 3.9279e1] }, + { id: 420, position: [-1.7475e1, 6.8103e1] }, + { id: 421, position: [-4.3688e1, 6.8103e1] }, + { id: 422, position: [-6.9901e1, 6.8103e1] }, + { id: 423, position: [-9.6113e1, 6.8103e1] }, + { id: 424, position: [-1.22326e2, 6.8103e1] }, + { id: 425, position: [-1.46632e2, 5.9762e1] }, + { id: 427, position: [-3.0582e1, 9.0804e1] }, + { id: 428, position: [-5.6794e1, 9.0804e1] }, + { id: 429, position: [-8.3007e1, 9.0804e1] }, + { id: 430, position: [-1.0922e2, 9.0804e1] }, + { id: 431, position: [-1.33384e2, 8.5331e1] }, + { id: 432, position: [-1.7475e1, 1.13505e2] }, + { id: 433, position: [-4.3688e1, 1.13505e2] }, + { id: 434, position: [-6.9901e1, 1.13505e2] }, + { id: 435, position: [-9.6113e1, 1.13505e2] }, + { id: 436, position: [-1.15723e2, 1.08078e2] }, + { id: 437, position: [-8.738, 1.36206e2] }, + { id: 438, position: [-3.495e1, 1.36206e2] }, + { id: 439, position: [-6.1163e1, 1.28639e2] }, + { id: 440, position: [-8.2273e1, 1.35291e2] }, + { id: 441, position: [-1.4399e1, 1.57687e2] }, + { id: 442, position: [-4.272e1, 1.52471e2] }, + { id: 443, position: [-6.315e1, 1.45385e2] }, +]; diff --git a/love/src/Utils.js b/love/src/Utils.js index e49a3369f..4caf8d93b 100644 --- a/love/src/Utils.js +++ b/love/src/Utils.js @@ -3,6 +3,7 @@ import React, { useState } from 'react'; import html2canvas from 'html2canvas'; import { DateTime } from 'luxon'; +import { toast } from 'react-toastify'; import { WEBSOCKET_SIMULATION } from 'Config.js'; import { SALCommandStatus } from 'redux/actions/ws'; @@ -325,6 +326,68 @@ export default class ManagerInterface { }); }); } + + static runATCSCommand(commandName, params = {}) { + const token = ManagerInterface.getToken(); + if (token === null) { + // console.log('Token not found during validation'); + return new Promise((resolve) => resolve(false)); + } + const url = `${this.getApiBaseUrl()}tcs/aux`; + return fetch(url, { + method: 'POST', + headers: this.getHeaders(), + body: JSON.stringify({ + command_name: commandName, + params, + }), + }).then((response) => { + if (response.status >= 500) { + toast.error('Error communicating with the server.'); + return false; + } + if (response.status === 401 || response.status === 403) { + toast.error('Session expired. Logging out.'); + ManagerInterface.removeToken(); + return false; + } + if (response.status === 400) { + return response.json().then((resp) => { + toast.error(resp.ack); + }); + } + return response.json().then((resp) => { + toast.info(resp.ack); + return resp; + }); + }); + } + + static getATCSDocstrings() { + const token = ManagerInterface.getToken(); + if (token === null) { + // console.log('Token not found during validation'); + return new Promise((resolve) => resolve(false)); + } + const url = `${this.getApiBaseUrl()}tcs/docstrings`; + return fetch(url, { + method: 'GET', + headers: this.getHeaders(), + }).then((response) => { + if (response.status >= 500) { + // console.error('Error communicating with the server.); + return false; + } + if (response.status === 401 || response.status === 403) { + // console.log('Session expired. Logging out'); + ManagerInterface.removeToken(); + return false; + } + return response.json().then((resp) => { + return resp; + }); + }); + } } /** @@ -653,9 +716,9 @@ export const parseCommanderData = (data, tsLabel = 'x', valueLabel = 'y') => { }; export function radians(degrees) { - return degrees * Math.PI / 180; -}; + return (degrees * Math.PI) / 180; +} export function degrees(radians) { - return radians * 180 / Math.PI; -}; + return (radians * 180) / Math.PI; +} diff --git a/love/src/components/AuxTel/Camera/Camera.jsx b/love/src/components/AuxTel/Camera/Camera.jsx index 4983e4f49..da02d0dd7 100644 --- a/love/src/components/AuxTel/Camera/Camera.jsx +++ b/love/src/components/AuxTel/Camera/Camera.jsx @@ -15,6 +15,8 @@ export default class Camera extends Component { calibrationDetailedState: PropTypes.string, shutterDetailedState: PropTypes.string, imageSequence: PropTypes.object, + subscribeToStreams: PropTypes.func, + unsubscribeToStreams: PropTypes.func, }; constructor(props) { diff --git a/love/src/components/CSCSummary/CSCDetail/CSCDetail.container.jsx b/love/src/components/CSCSummary/CSCDetail/CSCDetail.container.jsx index 847f05def..07f339637 100644 --- a/love/src/components/CSCSummary/CSCDetail/CSCDetail.container.jsx +++ b/love/src/components/CSCSummary/CSCDetail/CSCDetail.container.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { connect } from 'react-redux'; import CSCDetail from './CSCDetail'; -import { addGroup } from '../../../redux/actions/ws'; +import { addGroup, removeGroup } from '../../../redux/actions/ws'; import { getStreamData, getCSCHeartbeat } from '../../../redux/selectors'; export const schema = { @@ -67,6 +67,7 @@ const CSCDetailContainer = ({ summaryStateData, onCSCClick, subscribeToStreams, + unsubscribeToStreams, heartbeatData, embedded, }) => { @@ -79,6 +80,7 @@ const CSCDetailContainer = ({ summaryStateData={summaryStateData} onCSCClick={onCSCClick} subscribeToStreams={subscribeToStreams} + unsubscribeToStreams={unsubscribeToStreams} heartbeatData={heartbeatData} embedded={embedded} /> @@ -93,6 +95,12 @@ const mapDispatchToProps = (dispatch) => { dispatch(addGroup(`event-${cscName}-${index}-logMessage`)); dispatch(addGroup(`event-${cscName}-${index}-errorCode`)); }, + unsubscribeToStreams: (cscName, index) => { + dispatch(removeGroup('event-Heartbeat-0-stream')); + dispatch(removeGroup(`event-${cscName}-${index}-summaryState`)); + dispatch(removeGroup(`event-${cscName}-${index}-logMessage`)); + dispatch(removeGroup(`event-${cscName}-${index}-errorCode`)); + }, }; }; diff --git a/love/src/components/CSCSummary/CSCDetail/CSCDetail.jsx b/love/src/components/CSCSummary/CSCDetail/CSCDetail.jsx index a08d7140e..73a709c46 100644 --- a/love/src/components/CSCSummary/CSCDetail/CSCDetail.jsx +++ b/love/src/components/CSCSummary/CSCDetail/CSCDetail.jsx @@ -14,6 +14,7 @@ export default class CSCDetail extends Component { heartbeatData: PropTypes.object, summaryStateData: PropTypes.object, subscribeToStreams: PropTypes.func, + unsubscribeToStreams: PropTypes.func, embedded: PropTypes.bool, /* Whether the component should subscribe to streams*/ shouldSubscribe: PropTypes.bool, @@ -27,6 +28,7 @@ export default class CSCDetail extends Component { heartbeatData: null, summaryStateData: undefined, subscribeToStreams: () => {}, + unsubscribeToStreams: () => {}, embedded: false, shouldSubscribe: true, }; @@ -74,6 +76,10 @@ export default class CSCDetail extends Component { if (!this.props.shouldSubscribe) this.props.subscribeToStreams(this.props.name, this.props.salindex); }; + componentWillUnmount = () => { + if (!this.props.shouldSubscribe) this.props.unsubscribeToStreams(this.props.name, this.props.salindex); + } + render() { const { props } = this; let heartbeatStatus = 'unknown'; diff --git a/love/src/components/CSCSummary/CSCExpanded/CSCExpanded.container.jsx b/love/src/components/CSCSummary/CSCExpanded/CSCExpanded.container.jsx index f4041739b..1e3d2e815 100644 --- a/love/src/components/CSCSummary/CSCExpanded/CSCExpanded.container.jsx +++ b/love/src/components/CSCSummary/CSCExpanded/CSCExpanded.container.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { connect } from 'react-redux'; import CSCExpanded from './CSCExpanded'; -import { addGroup } from '../../../redux/actions/ws'; +import { addGroup, removeGroup } from '../../../redux/actions/ws'; import { removeCSCLogMessages, removeCSCErrorCodeData } from '../../../redux/actions/summaryData'; import { getStreamData, getCSCHeartbeat, getCSCLogMessages, getCSCErrorCodeData } from '../../../redux/selectors'; @@ -59,8 +59,10 @@ const CSCExpandedContainer = ({ logMessageData, errorCodeData, subscribeToStreams, + unsubscribeToStreams, heartbeatData, displaySummaryState = true, + hideTitle = false, }) => { return ( ); }; @@ -87,7 +91,12 @@ const mapDispatchToProps = (dispatch) => { dispatch(addGroup(`event-${cscName}-${index}-summaryState`)); dispatch(addGroup(`event-${cscName}-${index}-logMessage`)); dispatch(addGroup(`event-${cscName}-${index}-errorCode`)); - dispatch(addGroup('event-Heartbeat-0-stream')); + }, + unsubscribeToStreams: (cscName, index) => { + dispatch(removeGroup('event-Heartbeat-0-stream')); + dispatch(removeGroup(`event-${cscName}-${index}-summaryState`)); + dispatch(removeGroup(`event-${cscName}-${index}-logMessage`)); + dispatch(removeGroup(`event-${cscName}-${index}-errorCode`)); }, clearCSCLogMessages: (csc, salindex) => { dispatch(removeCSCLogMessages(csc, salindex)); diff --git a/love/src/components/CSCSummary/CSCExpanded/CSCExpanded.jsx b/love/src/components/CSCSummary/CSCExpanded/CSCExpanded.jsx index c22b83cc0..603d1d110 100644 --- a/love/src/components/CSCSummary/CSCExpanded/CSCExpanded.jsx +++ b/love/src/components/CSCSummary/CSCExpanded/CSCExpanded.jsx @@ -18,6 +18,8 @@ export default class CSCExpanded extends PureComponent { summaryStateData: PropTypes.object, logMessageData: PropTypes.array, errorCodeData: PropTypes.array, + subscribeToStreams: PropTypes.func, + unsubscribeToStreams: PropTypes.func, }; static defaultProps = { @@ -36,17 +38,16 @@ export default class CSCExpanded extends PureComponent { this.props.subscribeToStreams(this.props.name, this.props.salindex); }; - constructor(props) { - super(props); - this.state = { - messageFilters: { - 10: { value: true, name: 'Debug' }, - 20: { value: true, name: 'Info' }, - 30: { value: true, name: 'Warning' }, - 40: { value: true, name: 'Error' }, - }, - }; - } + componentWillUnmount = () => { + this.props.unsubscribeToStreams(this.props.name, this.props.salindex); + }; + + componentDidUpdate = (prevProps, prevState) => { + if (prevProps.name !== this.props.name || prevProps.salindex !== this.props.salindex) { + this.props.unsubscribeToStreams(prevProps.name, prevProps.salindex); + this.props.subscribeToStreams(this.props.name, this.props.salindex); + } + }; static states = { 0: { @@ -87,14 +88,6 @@ export default class CSCExpanded extends PureComponent { }, }; - updateFilter = (key, value) => { - const filters = this.state.messageFilters; - filters[key].value = value; - this.setState({ - messageFilters: { ...filters }, - }); - }; - render() { const summaryStateValue = this.props.summaryStateData ? this.props.summaryStateData.summaryState.value : 0; const summaryState = CSCExpanded.states[summaryStateValue]; @@ -152,7 +145,7 @@ export default class CSCExpanded extends PureComponent { > )} - {cscText(this.props.name, this.props.salindex)} + {!this.props.hideTitle && {cscText(this.props.name, this.props.salindex)}} {this.props.displaySummaryState && (
diff --git a/love/src/components/CSCSummary/CSCGroup/CSCGroup.container.jsx b/love/src/components/CSCSummary/CSCGroup/CSCGroup.container.jsx index 7cd115bb9..c9a54eafe 100644 --- a/love/src/components/CSCSummary/CSCGroup/CSCGroup.container.jsx +++ b/love/src/components/CSCSummary/CSCGroup/CSCGroup.container.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { connect } from 'react-redux'; import CSCGroup from './CSCGroup'; -import { addGroup } from '../../../redux/actions/ws'; +import { addGroup, removeGroup } from '../../../redux/actions/ws'; import { removeCSCLogMessages, removeCSCErrorCodeData } from '../../../redux/actions/summaryData'; import { getStreamData, getCSCHeartbeat, getCSCLogMessages, getCSCErrorCodeData } from '../../../redux/selectors'; @@ -90,20 +90,26 @@ export const schema = { }, }; -const CSCGroupContainer = ({ subscribeToStreams, ...props }) => { - return ; +const CSCGroupContainer = ({ subscribeToStreams, unsubscribeToStreams, ...props }) => { + return ; }; const mapDispatchToProps = (dispatch, ownProps) => { return { subscribeToStreams: (cscName, index) => { + console.log(`SUBSCRIBING TO ${cscName}-${index}...`); dispatch(addGroup('event-Heartbeat-0-stream')); dispatch(addGroup(`event-${cscName}-${index}-summaryState`)); dispatch(addGroup(`event-${cscName}-${index}-logMessage`)); dispatch(addGroup(`event-${cscName}-${index}-errorCode`)); - dispatch(addGroup('event-Heartbeat-0-stream')); ownProps.subscribeToStreamCallback(cscName, index); }, + unsubscribeToStreams: (cscName, index) => { + dispatch(removeGroup('event-Heartbeat-0-stream')); + dispatch(removeGroup(`event-${cscName}-${index}-summaryState`)); + dispatch(removeGroup(`event-${cscName}-${index}-logMessage`)); + dispatch(removeGroup(`event-${cscName}-${index}-errorCode`)); + }, clearCSCLogMessages: (csc, salindex) => { dispatch(removeCSCLogMessages(csc, salindex)); }, diff --git a/love/src/components/CSCSummary/CSCGroup/CSCGroup.jsx b/love/src/components/CSCSummary/CSCGroup/CSCGroup.jsx index 0be7ee833..8760d5b14 100644 --- a/love/src/components/CSCSummary/CSCGroup/CSCGroup.jsx +++ b/love/src/components/CSCSummary/CSCGroup/CSCGroup.jsx @@ -12,17 +12,21 @@ export default class CSCGroup extends Component { selectedCSC: undefined, }; } + static propTypes = { name: PropTypes.string, cscs: PropTypes.array, onCSCClick: PropTypes.func, embedded: PropTypes.bool, + subscribeToStreams: PropTypes.func, + unsubscribeToStreams: PropTypes.func, }; static defaultProps = { name: '', cscs: [], subscribeToStreams: () => 0, + unsubscribeToStreams: () => 0, selectedCSC: undefined, embedded: false, }; @@ -35,6 +39,14 @@ export default class CSCGroup extends Component { } }; + componentWillUnmount = () => { + if (this.props.csc !== undefined) { + this.props.cscs.forEach((csc) => { + this.props.unsubscribeToStreams(csc.name, csc.salindex); + }); + } + } + renderExpandedView = (selectedCSC) => { const groupView = selectedCSC.csc === 'all'; diff --git a/love/src/components/CSCSummary/CSCGroupLog/CSCGroupLog.container.jsx b/love/src/components/CSCSummary/CSCGroupLog/CSCGroupLog.container.jsx index 22c1cdd51..6beb4a5a7 100644 --- a/love/src/components/CSCSummary/CSCGroupLog/CSCGroupLog.container.jsx +++ b/love/src/components/CSCSummary/CSCGroupLog/CSCGroupLog.container.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { connect } from 'react-redux'; import CSCGroupLog from './CSCGroupLog'; -import { addGroup } from '../../../redux/actions/ws'; +import { addGroup, removeGroup } from '../../../redux/actions/ws'; import { getGroupSortedErrorCodeData } from '../../../redux/selectors'; import { removeCSCErrorCodeData } from '../../../redux/actions/summaryData'; @@ -61,6 +61,7 @@ const CSCGroupLogContainer = ({ onCSCClick, clearCSCErrorCodes, subscribeToStream, + unsubscribeToStream, errorCodeData, cscList, embedded, @@ -72,6 +73,7 @@ const CSCGroupLogContainer = ({ onCSCClick={onCSCClick} clearCSCErrorCodes={clearCSCErrorCodes} subscribeToStream={subscribeToStream} + unsubscribeToStream={unsubscribeToStream} errorCodeData={errorCodeData} cscList={cscList} embedded={embedded} @@ -84,6 +86,9 @@ const mapDispatchtoProps = (dispatch) => { subscribeToStream: (cscName, index) => { dispatch(addGroup(`event-${cscName}-${index}-errorCode`)); }, + unsubscribeToStream: (cscName, index) => { + dispatch(removeGroup(`event-${cscName}-${index}-errorCode`)); + }, clearCSCErrorCodes: (csc, salindex) => { dispatch(removeCSCErrorCodeData(csc, salindex)); }, diff --git a/love/src/components/CSCSummary/CSCGroupLog/CSCGroupLog.jsx b/love/src/components/CSCSummary/CSCGroupLog/CSCGroupLog.jsx index 525859507..8c59bf7a1 100644 --- a/love/src/components/CSCSummary/CSCGroupLog/CSCGroupLog.jsx +++ b/love/src/components/CSCSummary/CSCGroupLog/CSCGroupLog.jsx @@ -15,8 +15,10 @@ export default class CSCGroupLog extends Component { clearCSCErrorCodes: PropTypes.func, clearCSCLogMessages: PropTypes.func, subscribeToStream: PropTypes.func, + unsubscribeToStream: PropTypes.func, errorCodeData: PropTypes.array, embedded: PropTypes.bool, + cscList: PropTypes.array, }; static defaultProps = { @@ -36,6 +38,12 @@ export default class CSCGroupLog extends Component { }); }; + componentWillUnmount = () => { + this.props.cscList.forEach(({ name, salindex }) => { + this.props.unsubscribeToStream(name, salindex); + }); + } + clearGroupErrorCodes = () => { this.props.cscList.forEach(({ name, salindex }) => { this.props.clearCSCErrorCodes(name, salindex); diff --git a/love/src/components/CSCSummary/CSCSummary.container.jsx b/love/src/components/CSCSummary/CSCSummary.container.jsx index 1b90b4ab8..000e66a3b 100644 --- a/love/src/components/CSCSummary/CSCSummary.container.jsx +++ b/love/src/components/CSCSummary/CSCSummary.container.jsx @@ -25,7 +25,6 @@ export const schema = { const CSCSummaryContainer = ({ hierarchy = CSCSummaryHierarchy, expandHeight, - subscribeToStreamsWithCallback, ...props }) => { const [subscriptions, setSubscriptions] = useState([]); diff --git a/love/src/components/CommandPanel/CommandPanel.container.jsx b/love/src/components/CommandPanel/CommandPanel.container.jsx index bbbff2a74..62784c9b0 100644 --- a/love/src/components/CommandPanel/CommandPanel.container.jsx +++ b/love/src/components/CommandPanel/CommandPanel.container.jsx @@ -1,6 +1,7 @@ import React from 'react'; import { connect } from 'react-redux'; import { requestSALCommand } from '../../redux/actions/ws'; +import { getPermCmdExec, getScriptQueueState } from '../../redux/selectors'; import CommandPanel from './CommandPanel'; export const schema = { @@ -29,9 +30,19 @@ const CommandPanelContainer = ({ ...props }) => { const mapDispatchToProps = (dispatch, ownProps) => { return { requestSALCommand: (component, salindex, cmd) => { - return dispatch(requestSALCommand({ ...cmd, component, salindex })); + return; + dispatch(requestSALCommand({ ...cmd, component, salindex })); }, }; }; -export default connect(() => {}, mapDispatchToProps)(CommandPanelContainer); +const mapStateToProps = (state) => { + const commandExecutePermission = getPermCmdExec(state); + const queueState = getScriptQueueState(state, 1); + return { + commandExecutePermission: commandExecutePermission, + queueState: queueState, + }; +}; + +export default connect(mapStateToProps, mapDispatchToProps)(CommandPanelContainer); diff --git a/love/src/components/CommandPanel/CommandPanel.jsx b/love/src/components/CommandPanel/CommandPanel.jsx index a2bae37ae..1b0b89d40 100644 --- a/love/src/components/CommandPanel/CommandPanel.jsx +++ b/love/src/components/CommandPanel/CommandPanel.jsx @@ -1,12 +1,14 @@ import React, { Component } from 'react'; -import DomeCloseButton from './DomeCloseButton/DomeCloseButton'; +// import DomeCloseButton from './DomeCloseButton/DomeCloseButton'; +import StopAllTSCButton from './StopAllTSCButton/StopAllTSCButton'; import styles from './CommandPanel.module.css'; export default class CommandPanel extends Component { render() { return (
- + {/* */} +
); } diff --git a/love/src/components/CommandPanel/CommandPanel.module.css b/love/src/components/CommandPanel/CommandPanel.module.css index 674249d83..bd3612306 100644 --- a/love/src/components/CommandPanel/CommandPanel.module.css +++ b/love/src/components/CommandPanel/CommandPanel.module.css @@ -2,5 +2,8 @@ display: flex; /* justify-content: center; align-items: center; */ - height: 100%; +} + +.container div { + margin-right: 32px; } diff --git a/love/src/components/CommandPanel/DomeCloseButton/DomeCloseButton.jsx b/love/src/components/CommandPanel/DomeCloseButton/DomeCloseButton.jsx index edd0f89f2..b200bc5ad 100644 --- a/love/src/components/CommandPanel/DomeCloseButton/DomeCloseButton.jsx +++ b/love/src/components/CommandPanel/DomeCloseButton/DomeCloseButton.jsx @@ -6,17 +6,11 @@ export default class DomeCloseButton extends Component { return (
diff --git a/love/src/components/CommandPanel/DomeCloseButton/DomeCloseButton.module.css b/love/src/components/CommandPanel/DomeCloseButton/DomeCloseButton.module.css index 0f5ea2bda..b4108ecb4 100644 --- a/love/src/components/CommandPanel/DomeCloseButton/DomeCloseButton.module.css +++ b/love/src/components/CommandPanel/DomeCloseButton/DomeCloseButton.module.css @@ -1,15 +1,15 @@ .buttonWrapper { + height: 200px; + width: 200px; + text-align: center; } .button { - display: grid; - grid-template-columns: min-content; - grid-template-rows: min-content min-content; + width: 100%; + height: 100%; border-radius: 5px; - overflow: hidden; - width: fit-content; - border: none; - padding: 0; + border: 0; + padding: 16px; cursor: pointer; background: var(--status-alert-color); } @@ -18,32 +18,23 @@ background: var(--status-alert-dimmed-color-2); } -.domeSVG { -} - -.domeSVG path { - transform: translate(-140px, -120px); - fill: #fde8df; -} - -.domePath { -} - -.domeXPath { - stroke: var(--status-alert-color); - stroke-width: 3; - stroke-miterlimit: 10; +.button[disabled] { + cursor: not-allowed; + background: var(--status-alert-dimmed-color-2); } -.button:hover .domeXPath { - stroke: var(--status-alert-dimmed-color-2); - stroke-width: 3; - stroke-miterlimit: 10; +.svg { + height: 100px; + width: 100px; } .buttonLabel { + display: block; padding: 0.5em 1em; - padding-top: 0; color: white; font-size: var(--font-size-large); } + +.cls-1 { + fill: #fbe6dd; +} diff --git a/love/src/components/CommandPanel/StopAllTSCButton/StopAllTSCButton.jsx b/love/src/components/CommandPanel/StopAllTSCButton/StopAllTSCButton.jsx new file mode 100644 index 000000000..db9d2dd33 --- /dev/null +++ b/love/src/components/CommandPanel/StopAllTSCButton/StopAllTSCButton.jsx @@ -0,0 +1,37 @@ +import React, { Component } from 'react'; +import styles from './StopAllTSCButton.module.css'; +import ManagerInterface from 'Utils'; +// import { TCSCommands } from 'Config.js'; + +export default class StopAllTSCButton extends Component { + constructor(props) { + super(props); + this.state = {}; + } + + callTSCStopAll() { + // TODO: Reference command from variable + ManagerInterface.runATCSCommand('stop_all'); + } + + render() { + return ( +
+ +
+ ); + } +} diff --git a/love/src/components/CommandPanel/StopAllTSCButton/StopAllTSCButton.module.css b/love/src/components/CommandPanel/StopAllTSCButton/StopAllTSCButton.module.css new file mode 100644 index 000000000..b4108ecb4 --- /dev/null +++ b/love/src/components/CommandPanel/StopAllTSCButton/StopAllTSCButton.module.css @@ -0,0 +1,40 @@ +.buttonWrapper { + height: 200px; + width: 200px; + text-align: center; +} + +.button { + width: 100%; + height: 100%; + border-radius: 5px; + border: 0; + padding: 16px; + cursor: pointer; + background: var(--status-alert-color); +} + +.button:hover { + background: var(--status-alert-dimmed-color-2); +} + +.button[disabled] { + cursor: not-allowed; + background: var(--status-alert-dimmed-color-2); +} + +.svg { + height: 100px; + width: 100px; +} + +.buttonLabel { + display: block; + padding: 0.5em 1em; + color: white; + font-size: var(--font-size-large); +} + +.cls-1 { + fill: #fbe6dd; +} diff --git a/love/src/components/EventLog/EventLog.container.jsx b/love/src/components/EventLog/EventLog.container.jsx index c996812ce..2432c3e36 100644 --- a/love/src/components/EventLog/EventLog.container.jsx +++ b/love/src/components/EventLog/EventLog.container.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { connect } from 'react-redux'; import EventLog from './EventLog'; -import { addGroup } from '../../redux/actions/ws'; +import { addGroup, removeGroup } from '../../redux/actions/ws'; import { removeCSCLogMessages, removeCSCErrorCodeData } from '../../redux/actions/summaryData'; import { getGroupSortedErrorCodeData, getGroupSortedLogMessageData } from '../../redux/selectors'; import { CSCSummaryHierarchy } from '../../Config'; @@ -48,6 +48,7 @@ const EventLogContainer = ({ logMessageData, errorCodeData, subscribeToStreams, + unsubscribeToStreams, heartbeatData, ...props }) => { @@ -61,6 +62,7 @@ const EventLogContainer = ({ errorCodeData={errorCodeData} summaryStateData={summaryStateData} subscribeToStreams={subscribeToStreams} + unsubscribeToStreams={unsubscribeToStreams} logMessageData={logMessageData} heartbeatData={heartbeatData} clearCSCLogMessages={clearCSCLogMessages} @@ -77,7 +79,13 @@ const mapDispatchToProps = (dispatch) => { dispatch(addGroup(`event-${nameIndex}-logMessage`)); dispatch(addGroup(`event-${nameIndex}-errorCode`)); }); - // dispatch(addGroup('event-Heartbeat-0-stream')); + }, + unsubscribeToStreams: (cscList) => { + cscList.forEach((cscNameIndex) => { + const nameIndex = Object.values(cscNameIndex).join('-'); + dispatch(removeGroup(`event-${nameIndex}-logMessage`)); + dispatch(removeGroup(`event-${nameIndex}-errorCode`)); + }); }, clearCSCLogMessages: (cscList) => { cscList.forEach((cscNameIndex) => { diff --git a/love/src/components/EventLog/EventLog.jsx b/love/src/components/EventLog/EventLog.jsx index 65d8e657e..c3f64674b 100644 --- a/love/src/components/EventLog/EventLog.jsx +++ b/love/src/components/EventLog/EventLog.jsx @@ -17,6 +17,7 @@ export default class EventLog extends PureComponent { clearCSCErrorCodes: PropTypes.func, clearCSCLogMessages: PropTypes.func, subscribeToStream: PropTypes.func, + unsubscribeToStream: PropTypes.func, errorCodeData: PropTypes.array, embedded: PropTypes.bool, }; @@ -58,6 +59,10 @@ export default class EventLog extends PureComponent { this.props.subscribeToStreams(this.props.cscList); }; + componentWillUnmount = () => { + this.props.unsubscribeToStreams(this.props.cscList); + }; + clearGroupErrorCodes = () => { // this.props.cscList.forEach(({ name, salindex }) => { // this.props.clearCSCErrorCodes(name, salindex); diff --git a/love/src/components/GeneralPurpose/LogMessageDisplay/LogMessageDisplay.jsx b/love/src/components/GeneralPurpose/LogMessageDisplay/LogMessageDisplay.jsx index 8f4b0a140..16fa86497 100644 --- a/love/src/components/GeneralPurpose/LogMessageDisplay/LogMessageDisplay.jsx +++ b/love/src/components/GeneralPurpose/LogMessageDisplay/LogMessageDisplay.jsx @@ -1,18 +1,19 @@ import React, { useState, memo } from 'react'; import PropTypes from 'prop-types'; -import InfoIcon from '../../icons/InfoIcon/InfoIcon'; -import WarningIcon from '../../icons/WarningIcon/WarningIcon'; -import ErrorIcon from '../../icons/ErrorIcon/ErrorIcon'; +import DebugIcon from '../../icons/CSCExpanded/DebugIcon/DebugIcon'; +import InfoIcon from '../../icons/CSCExpanded/InfoIcon/InfoIcon'; +import WarningIcon from '../../icons/CSCExpanded/WarningIcon/WarningIcon'; +import ErrorIcon from '../../icons/CSCExpanded/ErrorIcon/ErrorIcon'; import Button from '../../GeneralPurpose/Button/Button'; import styles from './LogMessageDisplay.module.css'; import { formatTimestamp } from '../../../Utils'; function LogMessageDisplay({ logMessageData, clearCSCLogMessages }) { const [messageFilters, setMessageFilters] = useState({ - 10: { value: true, name: 'Debug' }, - 20: { value: true, name: 'Info' }, - 30: { value: true, name: 'Warning' }, - 40: { value: true, name: 'Error' }, + 10: { value: true, name: 'Debug', icon: }, + 20: { value: true, name: 'Info', icon: }, + 30: { value: true, name: 'Warning', icon: }, + 40: { value: true, name: 'Error', icon: }, }); const updateFilter = (key, value) => { @@ -35,13 +36,14 @@ function LogMessageDisplay({ logMessageData, clearCSCLogMessages }) { return (
); diff --git a/love/src/components/GeneralPurpose/Plot/PolarPlot/PolarPlot.container.jsx b/love/src/components/GeneralPurpose/Plot/PolarPlot/PolarPlot.container.jsx index a2da2aad1..b0eac2e6d 100644 --- a/love/src/components/GeneralPurpose/Plot/PolarPlot/PolarPlot.container.jsx +++ b/love/src/components/GeneralPurpose/Plot/PolarPlot/PolarPlot.container.jsx @@ -1,13 +1,9 @@ import React from 'react'; import { connect } from 'react-redux'; -import { addGroup, requestGroupRemoval } from 'redux/actions/ws'; +import { addGroup, removeGroup } from 'redux/actions/ws'; import { getStreamsData, getTaiToUtc } from 'redux/selectors/selectors'; import PolarPlot from './PolarPlot'; import ManagerInterface, { parseTimestamp, parsePlotInputs, parseCommanderData } from 'Utils'; -import Moment from 'moment'; -import { extendMoment } from 'moment-range'; - -const moment = extendMoment(Moment); export const defaultStyles = [ { @@ -384,7 +380,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { const displayDome = ownProps.displayDome || schema.props.displayDome.default; const groupNames = getGroupNames(inputs, displayDome); groupNames.forEach((groupName) => { - dispatch(requestGroupRemoval(groupName)); + dispatch(removeGroup(groupName)); }); }, }; diff --git a/love/src/components/Layout/Layout.jsx b/love/src/components/Layout/Layout.jsx index 08cda3a84..b40ce3cca 100644 --- a/love/src/components/Layout/Layout.jsx +++ b/love/src/components/Layout/Layout.jsx @@ -75,7 +75,7 @@ class Layout extends Component { tokenSwapStatus: PropTypes.string, /** Function to be called when requiring a user swap, similar to a logout */ requireUserSwap: PropTypes.func, - /** Function to call in order to rese4t subscriptions (when the manager heartbeat is missed) */ + /** Function to call in order to reset subscriptions (when the manager heartbeat is missed) */ resetSubscriptions: PropTypes.func, }; @@ -116,9 +116,10 @@ class Layout extends Component { componentDidMount = () => { this.moveCustomTopbar(); this.props.subscribeToStreams(); + this.checkHeartbeat(); this.heartbeatInterval = setInterval(() => { this.checkHeartbeat(); - }, 3000); + }, 10000); }; componentWillUnmount = () => { @@ -305,20 +306,22 @@ class Layout extends Component { }; renderHeartbeatsMenu = () => { - const producerHeartbeats = [ - HEARTBEAT_COMPONENTS.EVENTS, - HEARTBEAT_COMPONENTS.TELEMETRIES, - HEARTBEAT_COMPONENTS.HEARTBEATS, - HEARTBEAT_COMPONENTS.LOVE, - HEARTBEAT_COMPONENTS.SCRIPTQUEUE, - ].map((source) => this.state.heartbeatStatus[source]); - let producerHeartbeatStatus; - if (!producerHeartbeats.every((hb) => hb === undefined)) { - producerHeartbeatStatus = producerHeartbeats.includes('alert') ? 'alert' : 'ok'; - } + // const producerHeartbeats = [ + // HEARTBEAT_COMPONENTS['LOVE:0'], + // HEARTBEAT_COMPONENTS['ATDome:0'], + // HEARTBEAT_COMPONENTS['ATMCS:0'], + // HEARTBEAT_COMPONENTS['Watcher:0'], + // HEARTBEAT_COMPONENTS['GenericCamera:0'], + // HEARTBEAT_COMPONENTS['ScriptQueue:1'], + // HEARTBEAT_COMPONENTS['WeatherStation:1'], + // ].map((source) => this.state.heartbeatStatus[source]); + // let producerHeartbeatStatus; + // if (!producerHeartbeats.every((hb) => hb === undefined)) { + // producerHeartbeatStatus = producerHeartbeats.includes('alert') ? 'alert' : 'ok'; + // } const summaryHeartbeats = [ - producerHeartbeatStatus, + // producerHeartbeatStatus, this.state.heartbeatStatus[HEARTBEAT_COMPONENTS.MANAGER], this.state.heartbeatStatus[HEARTBEAT_COMPONENTS.COMMANDER], ]; @@ -326,6 +329,7 @@ class Layout extends Component { if (!summaryHeartbeats.every((hb) => hb === undefined)) { summaryHeartbeatStatus = summaryHeartbeats.includes('alert') ? 'alert' : 'ok'; } + return (
-
+ {/** Deprecated: used for old producer version */ + /*
LOVE producers: @@ -406,7 +411,7 @@ class Layout extends Component { > {HEARTBEAT_COMPONENTS.SCRIPTQUEUE} -
+
*/}
{ }; const mapDispatchToProps = (dispatch) => { - const subscriptions = [ - ]; + const subscriptions = []; return { subscriptions, subscribeToStreams: () => { diff --git a/love/src/components/MainTel/CableWraps/CableWraps.jsx b/love/src/components/MainTel/CableWraps/CableWraps.jsx index b1826d3a6..57c60dd21 100755 --- a/love/src/components/MainTel/CableWraps/CableWraps.jsx +++ b/love/src/components/MainTel/CableWraps/CableWraps.jsx @@ -22,6 +22,7 @@ class CableWraps extends Component { } componentDidMount() { + this.props.subscribeToStreams(); // Replace the following code with data from redux selectors setInterval( () => @@ -39,6 +40,10 @@ class CableWraps extends Component { ); } + componentWillUnmount() { + this.props.unsubscribeToStreams(); + } + receiveMsg(msg) { this.setState({ cable_wraps: msg, diff --git a/love/src/components/MainTel/M1M3/M1M3.jsx b/love/src/components/MainTel/M1M3/M1M3.jsx index 190497fa6..7a5cea296 100644 --- a/love/src/components/MainTel/M1M3/M1M3.jsx +++ b/love/src/components/MainTel/M1M3/M1M3.jsx @@ -19,6 +19,8 @@ export default class M1M3 extends Component { } componentDidMount() { + this.props.subscribeToStreams(); + let yMax = -Infinity; let xMax = -Infinity; let yMin = Infinity; @@ -42,6 +44,10 @@ export default class M1M3 extends Component { }); } + componentWillUnmount() { + this.props.unsubscribeToStreams(); + } + componentDidUpdate(prevProps, prevState) { d3.select('#circle-overlay-' + this.props.id).call(d3.zoom().scaleExtent([1, Infinity]).on('zoom', this.zoomed)); } diff --git a/love/src/components/ScriptQueue/ConfigPanel/ConfigPanel.jsx b/love/src/components/ScriptQueue/ConfigPanel/ConfigPanel.jsx index 242f68fe0..49cc90d34 100644 --- a/love/src/components/ScriptQueue/ConfigPanel/ConfigPanel.jsx +++ b/love/src/components/ScriptQueue/ConfigPanel/ConfigPanel.jsx @@ -6,6 +6,8 @@ import YAML from 'yaml'; import 'brace/mode/yaml'; import 'brace/theme/solarized_dark'; +import { SCRIPT_DOCUMENTATION_BASE_URL } from 'Config'; +import Select from 'components/GeneralPurpose/Select/Select'; import styles from './ConfigPanel.module.css'; import Button from '../../GeneralPurpose/Button/Button'; import Input from '../../GeneralPurpose/Input/Input'; @@ -14,9 +16,7 @@ import RotateIcon from '../../icons/RotateIcon/RotateIcon'; import CloseIcon from '../../icons/CloseIcon/CloseIcon'; import Hoverable from '../../GeneralPurpose/Hoverable/Hoverable'; import InfoPanel from '../../GeneralPurpose/InfoPanel/InfoPanel'; -import Select from 'components/GeneralPurpose/Select/Select'; import ManagerInterface from '../../../Utils'; -import { SCRIPT_DOCUMENTATION_BASE_URL } from 'Config'; const NO_SCHEMA_MESSAGE = '# ( waiting for schema . . .)'; diff --git a/love/src/components/ScriptQueue/GlobalState/GlobalState.jsx b/love/src/components/ScriptQueue/GlobalState/GlobalState.jsx index d41540eea..02b67a559 100644 --- a/love/src/components/ScriptQueue/GlobalState/GlobalState.jsx +++ b/love/src/components/ScriptQueue/GlobalState/GlobalState.jsx @@ -4,7 +4,7 @@ import styles from './GlobalState.module.css'; import StatusText from 'components/GeneralPurpose/StatusText/StatusText.jsx'; import ResumeIcon from 'components/icons/ScriptQueue/ResumeIcon/ResumeIcon'; import PauseIcon from 'components/icons/ScriptQueue/PauseIcon/PauseIcon'; -import GearIcon from 'components/icons/GearIcon/GearIcon.jsx'; +import GearIcon from 'components/icons/ScriptQueue/GearIcon/GearIcon.jsx'; import ContextMenu from '../Scripts/ContextMenu/ContextMenu'; import CSCDetail from 'components/CSCSummary/CSCDetail/CSCDetail.jsx'; @@ -30,11 +30,13 @@ const GlobalState = ({ }) => { const [contextMenuIsOpen, setContextMenuIsOpen] = React.useState(false); const [contextMenuData, setContextMenuData] = React.useState({}); + const [contextMenuTarget, setContextMenuTarget] = React.useState(undefined); const onClickContextMenu = React.useCallback((event) => { event.stopPropagation(); setContextMenuIsOpen((state) => !state); - setContextMenuData(event.target.getBoundingClientRect()); + setContextMenuData(event.currentTarget.getBoundingClientRect()); + setContextMenuTarget(event.currentTarget); }, []); React.useEffect(() => { @@ -137,7 +139,12 @@ const GlobalState = ({
- + ); diff --git a/love/src/components/ScriptQueue/GlobalState/GlobalState.module.css b/love/src/components/ScriptQueue/GlobalState/GlobalState.module.css index 7a2dce49e..609b994aa 100644 --- a/love/src/components/ScriptQueue/GlobalState/GlobalState.module.css +++ b/love/src/components/ScriptQueue/GlobalState/GlobalState.module.css @@ -7,6 +7,7 @@ align-items: flex-start; padding-bottom: 1em; padding-left: 0.5rem; + border-right: 1px solid var(--second-tertiary-background-color); } .globalStateContainer { @@ -15,6 +16,7 @@ padding: 0.5em 0.5em; height: 100%; width: 100%; + box-sizing: border-box; } .title { @@ -39,7 +41,7 @@ .stateCell { display: grid; grid-template-columns: 8em 1fr; - align-items: baseline; + align-items: center; grid-column-gap: 0.5rem; } .stateLabel { @@ -74,13 +76,6 @@ } .pauseIconWrapper { - width: 1em; - background: var(--commandable-background); - border: 1px solid var(--commandable-background); - padding: 0.25em 0.5em; + width: 2em; border-radius: 100%; } - -.gearIcon > path { - fill: var(--commandable-font-color); -} diff --git a/love/src/components/ScriptQueue/ScriptQueue.jsx b/love/src/components/ScriptQueue/ScriptQueue.jsx index 7aeb5596d..0a47e9032 100644 --- a/love/src/components/ScriptQueue/ScriptQueue.jsx +++ b/love/src/components/ScriptQueue/ScriptQueue.jsx @@ -17,8 +17,9 @@ import MoveToBottomIcon from '../icons/ScriptQueue/MoveToBottomIcon/MoveToBottom import { SALCommandStatus } from '../../redux/actions/ws'; import Input from '../GeneralPurpose/Input/Input'; import GlobalState from './GlobalState/GlobalState'; -import Modal from '../GeneralPurpose/Modal/Modal'; import ScriptDetails from './Scripts/ScriptDetails'; +import CSCExpandedContainer from 'components/CSCSummary/CSCExpanded/CSCExpanded.container'; +import debounce from 'lodash.debounce'; /** * Display lists of scripts from the ScriptQueue SAL object. It includes: Available scripts list, Waiting scripts list and Finished scripts list. @@ -36,8 +37,6 @@ export default class ScriptQueue extends Component { x: 100, y: 100, configSchema: '', - // name: undefined, - // script: {}, }, state: 'Unknown', summaryStateValue: 0, @@ -49,8 +48,15 @@ export default class ScriptQueue extends Component { availableScriptsStandardExpanded: true, availableScriptsExternalExpanded: true, availableScriptsFilter: '', - scriptModal: null, + currentScriptDetailState: { + height: 'initial', + initialHeight: 350, + }, + resetButton: Hide details ▲, }; + + this.observer = null; + this.currentScriptDetailsContainer = React.createRef(); } static defaultProps = { @@ -99,6 +105,28 @@ export default class ScriptQueue extends Component { }; componentDidUpdate = (prevProps, _prevState) => { + if (this.props.current !== prevProps.current) { + if (this.props.current === 'None') { + this.setState((state) => ({ + currentScriptDetailState: { ...state.currentScriptDetailState, height: 0 }, + })); + } else { + this.setState((state) => ({ + currentScriptDetailState: { + ...state.currentScriptDetailState, + height: state.currentScriptDetailState.initialHeight, + }, + })); + } + } + if (this.state.currentScriptDetailState !== _prevState.currentScriptDetailState) { + if (this.state.currentScriptDetailState.height < this.state.currentScriptDetailState.initialHeight) { + this.setState({ resetButton: Show details ▼ }); + } else { + this.setState({ resetButton: Hide details ▲ }); + } + } + if (this.props.availableScriptList && this.props.availableScriptList !== prevProps.availableScriptList) { this.props.availableScriptList.sort((a, b) => { return a.path.localeCompare(b.path, 'en', { sensitivity: 'base' }); @@ -141,10 +169,60 @@ export default class ScriptQueue extends Component { componentDidMount = () => { this.props.subscribeToStreams(); + + const debouncedResizeCallback = debounce((entries) => { + const newHeight = entries[0].target.clientHeight; + // console.log("New height: ", newHeight); + if (newHeight >= this.state.currentScriptDetailState.initialHeight) { + this.setState((state) => ({ + currentScriptDetailState: { + ...state.currentScriptDetailState, + height: state.currentScriptDetailState.initialHeight, + }, + })); + } else { + this.setState((state) => ({ + currentScriptDetailState: { + ...state.currentScriptDetailState, + height: newHeight, + }, + })); + } + }, 100); + this.observer = new ResizeObserver(debouncedResizeCallback); + if (this.currentScriptDetailsContainer.current) { + const currentHeight = this.currentScriptDetailsContainer.current.clientHeight; + this.setState((state) => ({ + currentScriptDetailState: { + ...state.currentScriptDetailState, + initialHeight: currentHeight, + height: this.props.current === 'None' ? 0 : currentHeight, + }, + })); + this.observer.observe(this.currentScriptDetailsContainer.current); + } }; componentWillUnmount = () => { this.props.unsubscribeToStreams(); + + if (this.observer) { + this.observer.unobserve(this.currentScriptDetailsContainer.current); + } + }; + + handleResizeButton = () => { + const { height, initialHeight } = this.state.currentScriptDetailState; + this.setState((state) => ({ + currentScriptDetailState: { + ...state.currentScriptDetailState, + height: height >= initialHeight ? 0 : initialHeight, + }, + })); + }; + + onShowScriptDetails = (script) => { + // console.log(script); }; displayAvailableScripts = () => { @@ -165,18 +243,6 @@ export default class ScriptQueue extends Component { return null; }; - onScriptModalOpen = (script) => () => { - this.setState({ - scriptModal: script, - }); - }; - - onScriptModalClose = (event) => { - this.setState({ - scriptModal: null, - }); - }; - onDragStart = (e, draggingId) => { if (!this.props.commandExecutePermission) return; const draggingScriptInstance = this.getScriptFromId(draggingId); @@ -270,13 +336,13 @@ export default class ScriptQueue extends Component { }; launchScriptConfig = (e, script) => { - let { x } = e.target.getBoundingClientRect(); + const { x } = e.target.getBoundingClientRect(); this.setState({ configPanel: { - script: script, + script, name: script.name, show: true, - x: x, + x, y: 100, configSchema: script.configSchema, }, @@ -399,6 +465,7 @@ export default class ScriptQueue extends Component { }); }; onClickContextMenu = (event, index, currentMenuSelected = false) => { + console.log('Click context menu'); event.stopPropagation(); this.setState({ isContextMenuOpen: !this.state.isContextMenuOpen }); this.setState({ @@ -490,7 +557,6 @@ export default class ScriptQueue extends Component { const finishedScriptListClass = this.state.isFinishedScriptListListVisible ? '' : styles.collapsedScriptList; const availableScriptListClass = this.state.isAvailableScriptListVisible ? '' : styles.collapsedScriptList; const current = this.props.current === 'None' ? {} : { ...this.props.current }; - // const now = new Date(); // Fix time zones for next line // const currentScriptElapsedTime = @@ -551,6 +617,19 @@ export default class ScriptQueue extends Component { contextMenuData={this.state.contextMenuData} options={contextMenuOption} /> + + +
@@ -569,23 +648,40 @@ export default class ScriptQueue extends Component { onClickContextMenu={this.onClickContextMenu} commandExecutePermission={this.props.commandExecutePermission} resumeScript={this.resumeScript} - onClick={this.onScriptModalOpen(current)} + onClick={() => null} + /> +
+
+
+ +
+
+ {this.state.resetButton} +
+
+
+ +
+
+ null} + displaySummaryState={false} + hideTitle={true} />
- {/* LISTS BODY */}
@@ -703,21 +799,25 @@ export default class ScriptQueue extends Component { draggingScriptInstance={this.state.draggingScriptInstance} disabled={!this.props.commandExecutePermission} > - +
+ this.onShowScriptDetails(script)} + /> +
); })} @@ -768,19 +868,21 @@ export default class ScriptQueue extends Component { : script.timestampProcessEnd - script.timestampRunStart; return ( - +
+ this.onShowScriptDetails(script)} + /> +
); })} @@ -789,17 +891,6 @@ export default class ScriptQueue extends Component {
- - document.querySelector('#container')} - size={50} - /* footerChildren={} */ - > - - ); } diff --git a/love/src/components/ScriptQueue/ScriptQueue.module.css b/love/src/components/ScriptQueue/ScriptQueue.module.css index a8a7c4755..64082074c 100644 --- a/love/src/components/ScriptQueue/ScriptQueue.module.css +++ b/love/src/components/ScriptQueue/ScriptQueue.module.css @@ -1,9 +1,10 @@ .scriptQueueContainer { display: grid; grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr; - grid-template-rows: min-content 1fr; + grid-template-rows: min-content min-content 1fr; grid-template-areas: - 'current current current current global global' + 'global global current current current current' + 'current-details current-details current-details current-details current-details current-details' 'body body body body body body'; color: var(--second-base-font-color); min-width: 1050px; @@ -58,13 +59,12 @@ display: flex; flex-direction: column; justify-content: center; - border-right: 1px solid var(--second-tertiary-background-color); } .currentScriptContainer { grid-area: current; text-align: left; - padding: 0.5em 0.5em; + padding: 0.5em 1em; } .currentScriptWrapper { @@ -77,6 +77,36 @@ display: flex; } +.currentScriptDetailsWrapper { + grid-area: current-details; +} + +.currentScriptResetSize { + text-align: right; + margin-right: 1em; + padding: 0.5em 0; +} + +.currentScriptDetails { + display: grid; + grid-template-columns: 40% 60%; + margin-bottom: 1em; + border-bottom: 1px solid var(--second-tertiary-background-color); + /* resize: vertical; */ + /* overflow-x: hidden; */ + overflow-y: hidden; + transition: all 300ms ease-in-out; +} + +.currentScriptDescription { + background: var(--second-secondary-background-color); + border-right: 1px solid var(--second-tertiary-background-color); +} + +.currentScriptLogs { + overflow-y: scroll; +} + .scriptList { display: grid; grid-template-rows: min-content 1fr; diff --git a/love/src/components/ScriptQueue/Scripts/ContextMenu/ContextMenu.jsx b/love/src/components/ScriptQueue/Scripts/ContextMenu/ContextMenu.jsx index 63bbb6a96..f6e6bf57c 100644 --- a/love/src/components/ScriptQueue/Scripts/ContextMenu/ContextMenu.jsx +++ b/love/src/components/ScriptQueue/Scripts/ContextMenu/ContextMenu.jsx @@ -1,9 +1,9 @@ -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import styles from './ContextMenu.module.css'; -export default class ContextMenu extends Component { +export default class ContextMenu extends PureComponent { static propTypes = { /**Position data for the context menu. * Usually from event.target.getBoundingClientRect() @@ -26,26 +26,41 @@ export default class ContextMenu extends Component { disabled: PropTypes.bool, }), ), + /** Target element which triggered the contextmenu */ + target: PropTypes.object, }; static defaultProps = { contextMenuData: {}, isOpen: false, options: [], + target: undefined, }; constructor(props) { super(props); - this.state = {}; + this.state = { + offset: 0, + }; } + componentDidUpdate = (nextState, nextProps) => { + if (this.props.target !== nextProps.target) { + const parentCustomView = this.props.target ? this.props.target.closest('.react-grid-item') : undefined; + const offset = parentCustomView ? parentCustomView.getBoundingClientRect().x : 0; + this.setState({ + offset, + }); + } + }; + render() { return ( this.props.isOpen && (
e.stopPropagation()} diff --git a/love/src/components/ScriptQueue/Scripts/ContextMenu/ContextMenu.module.css b/love/src/components/ScriptQueue/Scripts/ContextMenu/ContextMenu.module.css index b751be791..4f1b905db 100644 --- a/love/src/components/ScriptQueue/Scripts/ContextMenu/ContextMenu.module.css +++ b/love/src/components/ScriptQueue/Scripts/ContextMenu/ContextMenu.module.css @@ -38,11 +38,9 @@ div.buttonText { .iconWrapper { background: none; - border: 1px solid var(--commandable-font-color); - padding: 0.15em; border-radius: 100%; margin-right: 0.4em; - width: 0.8em; + width: 2em; } .enabled { @@ -50,11 +48,6 @@ div.buttonText { cursor: pointer; } -.enabled .iconWrapper { - border: 1px solid var(--commandable-background); - background: var(--commandable-background); +.row:not(.enabled) .iconWrapper path{ + fill: initial; } - -/* .disabled:hover > svg { - background: none; -} */ diff --git a/love/src/components/ScriptQueue/Scripts/CurrentScript/CurrentScript.module.css b/love/src/components/ScriptQueue/Scripts/CurrentScript/CurrentScript.module.css index 88d115cfc..f7355b9fc 100644 --- a/love/src/components/ScriptQueue/Scripts/CurrentScript/CurrentScript.module.css +++ b/love/src/components/ScriptQueue/Scripts/CurrentScript/CurrentScript.module.css @@ -1,6 +1,5 @@ .currentScriptContainer { padding: 0.5em; - cursor: pointer; user-select: none; } diff --git a/love/src/components/ScriptQueue/Scripts/FinishedScript/FinishedScript.jsx b/love/src/components/ScriptQueue/Scripts/FinishedScript/FinishedScript.jsx index f319143ad..d6723e7d6 100644 --- a/love/src/components/ScriptQueue/Scripts/FinishedScript/FinishedScript.jsx +++ b/love/src/components/ScriptQueue/Scripts/FinishedScript/FinishedScript.jsx @@ -42,9 +42,13 @@ export default class FinishedScript extends PureComponent { constructor(props) { super(props); + this.state = { + expanded: false, + }; } onClick = () => { + this.setState((state) => ({ expanded: !state.expanded })); this.props.onClick(); }; @@ -126,6 +130,11 @@ export default class FinishedScript extends PureComponent {
+
+ +
{this.props.commandExecutePermission && (
this.props.requeueScript(this.props.index)}> diff --git a/love/src/components/ScriptQueue/Scripts/ScriptDetails.jsx b/love/src/components/ScriptQueue/Scripts/ScriptDetails.jsx index 7f3452e49..2df9c7367 100644 --- a/love/src/components/ScriptQueue/Scripts/ScriptDetails.jsx +++ b/love/src/components/ScriptQueue/Scripts/ScriptDetails.jsx @@ -87,15 +87,6 @@ export default ({
); })} -
- console.log(a)} - displaySummaryState={false} - /> -
); diff --git a/love/src/components/ScriptQueue/Scripts/Scripts.module.css b/love/src/components/ScriptQueue/Scripts/Scripts.module.css index 60f6b980b..b6fc31c15 100644 --- a/love/src/components/ScriptQueue/Scripts/Scripts.module.css +++ b/love/src/components/ScriptQueue/Scripts/Scripts.module.css @@ -147,6 +147,7 @@ } .expandedSectionWrapper { + border-top: 1px solid gray; padding: 0.5em; } @@ -172,6 +173,7 @@ color: var(--highlighted-font-color); font-size: var(--font-size-large); grid-column-start: span 2; + text-align: left; } .subSectionLabel { @@ -194,47 +196,27 @@ grid-column-gap: 0.2em; } -.buttonContainer { - padding: 0; - border: 1px solid transparent; - white-space: pre; - background: var(--commandable-background); - border-radius: 100%; - height: 1.5em; - width: 1.5em; - display: flex; - justify-content: center; -} - .noBackgroundButton { background: none; line-height: 1em; } .buttonContainer:hover { - /* background: var(--script-ok-color); */ background: var(--commandable-background-hover); - border: 1px solid var(--commandable-background); } .commandButton { display: grid; - /* grid-template-columns: 2em 1fr; */ padding: 0em; text-align: left; align-items: center; user-select: none; } -.commandButton div { - padding: 0 0.3em; -} - div.commandButtonText { padding-left: 0em; } .compact { - /* grid-template-columns: 2em 0; */ width: 1.8em; } diff --git a/love/src/components/ScriptQueue/Scripts/WaitingScript/WaitingScript.jsx b/love/src/components/ScriptQueue/Scripts/WaitingScript/WaitingScript.jsx index 3007186a8..887d94406 100644 --- a/love/src/components/ScriptQueue/Scripts/WaitingScript/WaitingScript.jsx +++ b/love/src/components/ScriptQueue/Scripts/WaitingScript/WaitingScript.jsx @@ -61,9 +61,13 @@ export default class WaitingScript extends PureComponent { constructor(props) { super(props); + this.state = { + expanded: false, + }; } onClick = () => { + this.setState((state) => ({ expanded: !state.expanded })); this.props.onClick(); }; @@ -222,6 +226,11 @@ export default class WaitingScript extends PureComponent { +
+ +
); diff --git a/love/src/components/TCSCommands/TCSCommands.container.jsx b/love/src/components/TCSCommands/TCSCommands.container.jsx new file mode 100644 index 000000000..076841c15 --- /dev/null +++ b/love/src/components/TCSCommands/TCSCommands.container.jsx @@ -0,0 +1,69 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { addGroup, removeGroup, requestSALCommand } from '../../redux/actions/ws'; +import { getPermCmdExec, getScriptQueueState } from '../../redux/selectors'; +import TCSCommands from './TCSCommands'; + +export const schema = { + description: 'Panel containing multiple buttons that execute different commands, such as closing the dome', + defaultSize: [57, 35], + props: { + title: { + type: 'string', + description: 'Name diplayed in the title bar (if visible)', + isPrivate: false, + default: 'TCS Commands', + }, + hasRawMode: { + type: 'boolean', + description: 'Whether the component has a raw mode version', + isPrivate: true, + default: false, + }, + }, +}; + +const TCSCommandsContainer = ({ + queueState, + commandExecutePermission, + subscribeToStreams, + unsubscribeToStreams, + ...props +}) => { + return ( + + ); +}; + +const mapDispatchToProps = (dispatch, ownProps) => { + const subscriptions = [`event-ScriptQueueState-1-stream`]; + return { + subscriptions, + subscribeToStreams: () => { + subscriptions.forEach((stream) => dispatch(addGroup(stream))); + }, + unsubscribeToStreams: () => { + subscriptions.forEach((stream) => dispatch(removeGroup(stream))); + }, + requestSALCommand: (component, salindex, cmd) => { + return dispatch(requestSALCommand({ ...cmd, component, salindex })); + }, + }; +}; + +const mapStateToProps = (state) => { + const commandExecutePermission = getPermCmdExec(state); + const queueState = getScriptQueueState(state, 1); + return { + commandExecutePermission: commandExecutePermission, + queueState: queueState, + }; +}; + +export default connect(mapStateToProps, mapDispatchToProps)(TCSCommandsContainer); diff --git a/love/src/components/TCSCommands/TCSCommands.jsx b/love/src/components/TCSCommands/TCSCommands.jsx new file mode 100644 index 000000000..a2f023616 --- /dev/null +++ b/love/src/components/TCSCommands/TCSCommands.jsx @@ -0,0 +1,189 @@ +import React, { Component } from 'react'; +import styles from './TCSCommands.module.css'; +import Select from 'components/GeneralPurpose/Select/Select'; +import Input from 'components/GeneralPurpose/Input/Input'; +import Button from 'components/GeneralPurpose/Button/Button'; +import Modal from 'components/GeneralPurpose/Modal/Modal'; +import ScriptQueue from 'components/ScriptQueue/ScriptQueue'; +import StatusText from 'components/GeneralPurpose/StatusText/StatusText.jsx'; +import HelpIcon from 'components/icons/HelpIcon/HelpIcon'; +import WarningIcon from 'components/icons/WarningIcon/WarningIcon'; +import { TCSCommands } from 'Config.js'; +import ManagerInterface from 'Utils'; +import { Remarkable } from 'remarkable'; + +var md = new Remarkable(); + +const angleRegExp = new RegExp(/(\d\d(:| )\d\d(:| )\d\d)+(\.\d{1,10})?$/); +const floatRegExp = new RegExp(/^-?\d*(\.\d+)?$/); + +export default class CommandPanel extends Component { + constructor(props) { + super(props); + this.state = { + selectedCommand: null, + paramValues: {}, + paramWarnings: {}, + docstrings: {}, + isModalOpen: false, + }; + } + + componentDidMount = () => { + this.props.subscribeToStreams(); + ManagerInterface.getATCSDocstrings().then((data) => { + this.setState({ + docstrings: data, + }); + }); + }; + + componentWillUnmount = () => { + this.props.unsubscribeToStreams(); + }; + + updateParamValue = (name, value, paramType) => { + this.setState({ + paramValues: { + ...this.state.paramValues, + [name]: value, + }, + }); + }; + + checkInvalidAngle = (name, value) => { + const testValue = value ?? ''; + this.setState({ + paramWarnings: { + ...this.state.paramWarnings, + [name]: isNaN(parseFloat(testValue.match(floatRegExp))) && !angleRegExp.test(testValue), + }, + }); + }; + + renderParam = (name, param) => { + const [paramType, defaultValue] = param; + const { paramValues } = this.state; + return ( +
+
{name}
+ {paramType == 'string' && ( + this.updateParamValue(name, e.target.value, paramType)} /> + )} + {paramType == 'number' && ( + this.updateParamValue(name, e.target.value, paramType)} /> + )} + {paramType == 'angle' && ( + <> + this.updateParamValue(name, e.target.value, paramType)} + onBlur={() => this.checkInvalidAngle(name, paramValues[name])} + /> + {this.state.paramWarnings[name] && ( +
+ Angle should be a float (deg) or a sexagesimal string (DD:MM:SS.S or DD MM SS.S) +
+ )} + + )} + {paramType == 'boolean' && ( + this.updateParamValue(name, e.target.checked, paramType)} + /> + )} + {Array.isArray(paramType) && ( + this.selectCommand(selection?.value)} + /> +
this.setState({ isModalOpen: true })} + className={this.state.selectedCommand ? styles.buttonWrapper : styles.hidden} + > + +
+
+
+ {Object.keys(paramsDict).map((key, index) => { + const param = paramsDict[key]; + return
{this.renderParam(key, param)}
; + })} +
+ {this.state.selectedCommand && ( +
+ +
+ )} + + ); + } +} diff --git a/love/src/components/TCSCommands/TCSCommands.module.css b/love/src/components/TCSCommands/TCSCommands.module.css new file mode 100644 index 000000000..0d2410a2c --- /dev/null +++ b/love/src/components/TCSCommands/TCSCommands.module.css @@ -0,0 +1,100 @@ +.container { + display: grid; + grid-template-rows: min-content 1fr min-content; + row-gap: 1em; + height: 100%; +} + +.containerExtraRow { + grid-template-rows: min-content min-content 1fr min-content; +} + +.selectContainer { + cursor: pointer; + display: grid; + grid-template-columns: 1fr min-content; + align-items: center; +} + +.select { + cursor: pointer; +} + +.commandParamsContainer { +} + +.paramContainer { + display: grid; + grid-template-columns: 1fr; + padding: 0.5em; + text-align: left; +} + +.paramLabel { + padding-left: 0.5em; +} + +.checkboxParam { + grid-template-columns: 1fr min-content; +} + +.sendButtonContainer { + padding: 1em; +} + +.markdown { + text-align: left; +} + +.markdown pre { + white-space: pre-wrap; +} + +.buttonWrapper { + padding-left: 0.5em; + width: 1em; +} + +.hidden { + visibility: hidden; +} + +.queueStateContainer { + display: grid; + grid-template-columns: 1fr 12em; + border-bottom: solid var(--second-tertiary-background-color) 1px; + padding: 0.5em 0em; + align-items: center; +} + +.queueStateLabel { + color: var(--base-font-color); + font-size: var(--font-size-medium); + font-weight: bold; + justify-self: left; +} + +.warningText { + justify-self: left; + display: grid; + grid-template-columns: min-content 1fr; + grid-column: 1 / span 2; + align-items: center; +} + +.warningIcon { + width: 1em; + min-width: 1em; + padding: 0.5em; + text-align: center; + user-select: none; + vertical-align: top; +} + +.warningIcon svg { + vertical-align: middle; +} + +.removed { + display: none; +} diff --git a/love/src/components/UIF/ComponentIndex.jsx b/love/src/components/UIF/ComponentIndex.jsx index dc1a3dd11..050ced216 100644 --- a/love/src/components/UIF/ComponentIndex.jsx +++ b/love/src/components/UIF/ComponentIndex.jsx @@ -320,6 +320,16 @@ export const utilitiesIndex = { }, }, }, + TCSCommands: { + component: require('../TCSCommands/TCSCommands.container').default, + schema: { + ...require('../TCSCommands/TCSCommands.container').schema, + props: { + ...defaultSchemaProps, + ...require('../TCSCommands/TCSCommands.container').schema.props, + }, + }, + }, }; export const internalIndex = { diff --git a/love/src/components/icons/CSCExpanded/DebugIcon/DebugIcon.jsx b/love/src/components/icons/CSCExpanded/DebugIcon/DebugIcon.jsx new file mode 100644 index 000000000..90cb5e828 --- /dev/null +++ b/love/src/components/icons/CSCExpanded/DebugIcon/DebugIcon.jsx @@ -0,0 +1,21 @@ +import React from 'react'; +import styles from './DebugIcon.module.css'; + +export default function DebugIcon(props) { + const className = [styles.svg, props.className].join(' '); + return ( + + + + + + ); +} diff --git a/love/src/components/icons/CSCExpanded/DebugIcon/DebugIcon.module.css b/love/src/components/icons/CSCExpanded/DebugIcon/DebugIcon.module.css new file mode 100644 index 000000000..569c34455 --- /dev/null +++ b/love/src/components/icons/CSCExpanded/DebugIcon/DebugIcon.module.css @@ -0,0 +1,11 @@ +.svg { + vertical-align: middle; +} + +.cls-1 { + fill: #70b5ba; +} + +.cls-2 { + fill: #2a3f4b; +} diff --git a/love/src/components/icons/CSCExpanded/ErrorIcon/ErrorIcon.jsx b/love/src/components/icons/CSCExpanded/ErrorIcon/ErrorIcon.jsx new file mode 100644 index 000000000..435199189 --- /dev/null +++ b/love/src/components/icons/CSCExpanded/ErrorIcon/ErrorIcon.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import styles from './ErrorIcon.module.css'; + +export default function ErrorIcon(props) { + const className = [styles.svg, props.className].join(' '); + return ( + + + + + ); +} diff --git a/love/src/components/icons/CSCExpanded/ErrorIcon/ErrorIcon.module.css b/love/src/components/icons/CSCExpanded/ErrorIcon/ErrorIcon.module.css new file mode 100644 index 000000000..e3c4320ef --- /dev/null +++ b/love/src/components/icons/CSCExpanded/ErrorIcon/ErrorIcon.module.css @@ -0,0 +1,14 @@ +.svg { + vertical-align: middle; +} + +.cls-1 { + fill: #ff6900; +} + +.cls-2 { + fill: #2a3f4b; + stroke: #2a3f4b; + stroke-miterlimit: 10; + stroke-width: 2px; +} diff --git a/love/src/components/icons/CSCExpanded/InfoIcon/InfoIcon.jsx b/love/src/components/icons/CSCExpanded/InfoIcon/InfoIcon.jsx new file mode 100644 index 000000000..ec3782675 --- /dev/null +++ b/love/src/components/icons/CSCExpanded/InfoIcon/InfoIcon.jsx @@ -0,0 +1,21 @@ +import React from 'react'; +import styles from './InfoIcon.module.css'; + +export default function InfoIcon(props) { + const className = [styles.svg, props.className].join(' '); + return ( + + + + + + ); +} diff --git a/love/src/components/icons/CSCExpanded/InfoIcon/InfoIcon.module.css b/love/src/components/icons/CSCExpanded/InfoIcon/InfoIcon.module.css new file mode 100644 index 000000000..cb3cdaa8d --- /dev/null +++ b/love/src/components/icons/CSCExpanded/InfoIcon/InfoIcon.module.css @@ -0,0 +1,11 @@ +.svg { + vertical-align: middle; +} + +.cls-1 { + fill: #39b2c0; +} + +.cls-2 { + fill: #2a3f4b; +} diff --git a/love/src/components/icons/CSCExpanded/WarningIcon/WarningIcon.jsx b/love/src/components/icons/CSCExpanded/WarningIcon/WarningIcon.jsx new file mode 100644 index 000000000..dc2a89bb4 --- /dev/null +++ b/love/src/components/icons/CSCExpanded/WarningIcon/WarningIcon.jsx @@ -0,0 +1,25 @@ +import React from 'react'; +import styles from './WarningIcon.module.css'; + +export default function WarningIcon(props) { + const className = [styles.svg, props.className].join(' '); + return ( + + + + + + ); +} diff --git a/love/src/components/icons/CSCExpanded/WarningIcon/WarningIcon.module.css b/love/src/components/icons/CSCExpanded/WarningIcon/WarningIcon.module.css new file mode 100644 index 000000000..6b30c071c --- /dev/null +++ b/love/src/components/icons/CSCExpanded/WarningIcon/WarningIcon.module.css @@ -0,0 +1,11 @@ +.svg { + vertical-align: middle; +} + +.cls-1 { + fill: #eee200; +} + +.cls-2 { + fill: #2a3f4b; +} diff --git a/love/src/components/icons/GearIcon/GearIcon.module.css b/love/src/components/icons/GearIcon/GearIcon.module.css index 250b34895..ce73d25cb 100644 --- a/love/src/components/icons/GearIcon/GearIcon.module.css +++ b/love/src/components/icons/GearIcon/GearIcon.module.css @@ -1,13 +1,13 @@ .gearIcon { display: inline-block; - max-height: 100%; - stroke-miterlimit: 10; - stroke-width: 2px; + max-height:100%; + stroke-miterlimit:10; + stroke-width:2px; vertical-align: middle; } .active { - fill: var(--base-font-color); + fill:var(--base-font-color); } .inactive { diff --git a/love/src/components/icons/HelpIcon/HelpIcon.jsx b/love/src/components/icons/HelpIcon/HelpIcon.jsx new file mode 100644 index 000000000..cc98318bd --- /dev/null +++ b/love/src/components/icons/HelpIcon/HelpIcon.jsx @@ -0,0 +1,18 @@ +import React from 'react'; +import styles from './HelpIcon.module.css'; + +class HelpIcon extends React.Component { + render() { + return ( + + + + ); + } +} + +export default HelpIcon; diff --git a/love/src/components/icons/HelpIcon/HelpIcon.module.css b/love/src/components/icons/HelpIcon/HelpIcon.module.css new file mode 100644 index 000000000..fff2b02f4 --- /dev/null +++ b/love/src/components/icons/HelpIcon/HelpIcon.module.css @@ -0,0 +1,8 @@ +.helpIcon { + display: inline-block; + fill: var(--secondary-font-color); + fill-rule: evenodd; + width: 1em; + padding-right: 0.5em; + padding-left: 0.25em; +} diff --git a/love/src/components/icons/ScriptQueue/GearIcon/GearIcon.jsx b/love/src/components/icons/ScriptQueue/GearIcon/GearIcon.jsx new file mode 100644 index 000000000..9eb2754ef --- /dev/null +++ b/love/src/components/icons/ScriptQueue/GearIcon/GearIcon.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import styles from './GearIcon.module.css'; + +export default function GearIcon(props) { + const className = [styles.gearIcon, props.className].join(' '); + return ( + + + + + ); +} diff --git a/love/src/components/icons/ScriptQueue/GearIcon/GearIcon.module.css b/love/src/components/icons/ScriptQueue/GearIcon/GearIcon.module.css new file mode 100644 index 000000000..df70827df --- /dev/null +++ b/love/src/components/icons/ScriptQueue/GearIcon/GearIcon.module.css @@ -0,0 +1,14 @@ +.gearIcon { + vertical-align: middle; +} + +.cls-1{ + fill:none; + stroke:#1ecfe8; + stroke-miterlimit:10; + stroke-width:2px; +} + +.cls-2{ + fill:#1ecfe8; +} \ No newline at end of file diff --git a/love/src/components/icons/ScriptQueue/LaunchScriptIcon/LaunchScriptIcon.jsx b/love/src/components/icons/ScriptQueue/LaunchScriptIcon/LaunchScriptIcon.jsx index 962bdc567..377c74393 100644 --- a/love/src/components/icons/ScriptQueue/LaunchScriptIcon/LaunchScriptIcon.jsx +++ b/love/src/components/icons/ScriptQueue/LaunchScriptIcon/LaunchScriptIcon.jsx @@ -9,29 +9,31 @@ export default class LaunchScriptIcon extends Component { render() { return ( - + {this.props.title} - - + ); diff --git a/love/src/components/icons/ScriptQueue/LaunchScriptIcon/LaunchScriptIcon.module.css b/love/src/components/icons/ScriptQueue/LaunchScriptIcon/LaunchScriptIcon.module.css index c4ab0b91d..7bdb584d9 100644 --- a/love/src/components/icons/ScriptQueue/LaunchScriptIcon/LaunchScriptIcon.module.css +++ b/love/src/components/icons/ScriptQueue/LaunchScriptIcon/LaunchScriptIcon.module.css @@ -1,11 +1,24 @@ -.classOne { - fill: none; - /* stroke: var(--second-base-font-color); */ - stroke: var(--commandable-font-color); +.svg { + vertical-align: middle; +} + +.cls-1 { + fill: #122632; + opacity: 0.99; +} + +.cls-2 { + fill: #1ecfe8; +} + +.cls-1, .cls-2 { + fill-rule: evenodd; +} + +.cls-3 { + fill: #2b414d; + stroke: #1ecfe8; stroke-linecap: round; stroke-linejoin: round; -} -.classTwo { - /* fill: var(--second-base-font-color); */ - fill: var(--commandable-font-color); + stroke-width: 2px; } diff --git a/love/src/components/icons/ScriptQueue/MoveDownIcon/MoveDownIcon.jsx b/love/src/components/icons/ScriptQueue/MoveDownIcon/MoveDownIcon.jsx index 2c416ade7..5d0e7faad 100644 --- a/love/src/components/icons/ScriptQueue/MoveDownIcon/MoveDownIcon.jsx +++ b/love/src/components/icons/ScriptQueue/MoveDownIcon/MoveDownIcon.jsx @@ -4,13 +4,12 @@ import styles from './MoveDownIcon.module.css'; export default class MoveDownIcon extends Component { render() { return ( - + {'Move script down'} ); diff --git a/love/src/components/icons/ScriptQueue/MoveDownIcon/MoveDownIcon.module.css b/love/src/components/icons/ScriptQueue/MoveDownIcon/MoveDownIcon.module.css index c85578dd0..3dad44dfa 100644 --- a/love/src/components/icons/ScriptQueue/MoveDownIcon/MoveDownIcon.module.css +++ b/love/src/components/icons/ScriptQueue/MoveDownIcon/MoveDownIcon.module.css @@ -1,8 +1,7 @@ -.path { - fill: var(--commandable-font-color); - stroke: var(--commandable-font-color); -} - .svg { vertical-align: middle; } + +.cls-1 { + fill: #1ecfe8; +} diff --git a/love/src/components/icons/ScriptQueue/MoveUpIcon/MoveUpIcon.jsx b/love/src/components/icons/ScriptQueue/MoveUpIcon/MoveUpIcon.jsx index 303a34dba..a1444fb69 100644 --- a/love/src/components/icons/ScriptQueue/MoveUpIcon/MoveUpIcon.jsx +++ b/love/src/components/icons/ScriptQueue/MoveUpIcon/MoveUpIcon.jsx @@ -4,14 +4,12 @@ import styles from './MoveUpIcon.module.css'; export default class MoveUpIcon extends Component { render() { return ( - - {'Move script up'} + + Move script up ); diff --git a/love/src/components/icons/ScriptQueue/MoveUpIcon/MoveUpIcon.module.css b/love/src/components/icons/ScriptQueue/MoveUpIcon/MoveUpIcon.module.css index 5f4358aff..3dad44dfa 100644 --- a/love/src/components/icons/ScriptQueue/MoveUpIcon/MoveUpIcon.module.css +++ b/love/src/components/icons/ScriptQueue/MoveUpIcon/MoveUpIcon.module.css @@ -1,19 +1,7 @@ -.st0 { - fill: none; - stroke: var(--second-base-font-color); - stroke-linecap: round; - stroke-linejoin: round; -} - -.st1 { - fill: var(--second-base-font-color); -} - .svg { vertical-align: middle; } -.svg path { - fill: var(--commandable-font-color); - stroke: var(--commandable-font-color); +.cls-1 { + fill: #1ecfe8; } diff --git a/love/src/components/icons/ScriptQueue/PauseIcon/PauseIcon.jsx b/love/src/components/icons/ScriptQueue/PauseIcon/PauseIcon.jsx index ec1c9f579..1f26019cf 100644 --- a/love/src/components/icons/ScriptQueue/PauseIcon/PauseIcon.jsx +++ b/love/src/components/icons/ScriptQueue/PauseIcon/PauseIcon.jsx @@ -4,14 +4,11 @@ import styles from './PauseIcon.module.css'; export default class PauseIcon extends Component { render() { return ( - + - ); diff --git a/love/src/components/icons/ScriptQueue/PauseIcon/PauseIcon.module.css b/love/src/components/icons/ScriptQueue/PauseIcon/PauseIcon.module.css index a75696d28..77c076a80 100644 --- a/love/src/components/icons/ScriptQueue/PauseIcon/PauseIcon.module.css +++ b/love/src/components/icons/ScriptQueue/PauseIcon/PauseIcon.module.css @@ -1,14 +1,5 @@ -.st0 { - fill: var(--second-primary-background-color); - stroke: var(--second-primary-background-color); - stroke-miterlimit: 10; -} - -.svg { +.pauseIcon { vertical-align: middle; } -.svg path { - fill: var(--commandable-font-color); - stroke: var(--commandable-font-color); -} +.cls-1{fill:#1ecfe8;} \ No newline at end of file diff --git a/love/src/components/icons/ScriptQueue/RequeueIcon/RequeueIcon.jsx b/love/src/components/icons/ScriptQueue/RequeueIcon/RequeueIcon.jsx index dfbbdf8a1..aa2d47bae 100644 --- a/love/src/components/icons/ScriptQueue/RequeueIcon/RequeueIcon.jsx +++ b/love/src/components/icons/ScriptQueue/RequeueIcon/RequeueIcon.jsx @@ -3,35 +3,41 @@ import styles from './RequeueIcon.module.css'; export default class RequeueIcon extends Component { render() { - const status = this.props.active !== undefined && this.props.active === false ? styles.inactive : styles.active; - const title = this.props.title ? this.props.title : 'Requeue script'; return ( - - {title} + + {this.props.title} + ); diff --git a/love/src/components/icons/ScriptQueue/RequeueIcon/RequeueIcon.module.css b/love/src/components/icons/ScriptQueue/RequeueIcon/RequeueIcon.module.css index b6029e4c3..b1ba2949c 100644 --- a/love/src/components/icons/ScriptQueue/RequeueIcon/RequeueIcon.module.css +++ b/love/src/components/icons/ScriptQueue/RequeueIcon/RequeueIcon.module.css @@ -1,26 +1,22 @@ -.st0 { - fill: none; - stroke: var(--second-primary-background-color); - stroke-linecap: round; - stroke-linejoin: round; -} - -.st1 { - fill: var(--second-primary-background-color); -} - .svg { vertical-align: middle; } -.active { - stroke: var(--second-primary-background-color); +.cls-1 { + fill: #122632; + opacity: 0.99; +} + +.cls-3 { + stroke: #2b414d; + stroke-linecap: round; + stroke-linejoin: round; } -.inactive { - stroke: var(--secondary-font-color); +.cls-1, .cls-2 { + fill-rule: evenodd; } -.svg path { - stroke: var(--commandable-font-color); +.cls-2, .cls-3 { + fill: #1ecfe8; } diff --git a/love/src/components/icons/ScriptQueue/ResumeIcon/ResumeIcon.jsx b/love/src/components/icons/ScriptQueue/ResumeIcon/ResumeIcon.jsx index a942b5b81..e4700db41 100644 --- a/love/src/components/icons/ScriptQueue/ResumeIcon/ResumeIcon.jsx +++ b/love/src/components/icons/ScriptQueue/ResumeIcon/ResumeIcon.jsx @@ -4,10 +4,11 @@ import styles from './ResumeIcon.module.css'; export default class ResumeIcon extends Component { render() { return ( - + ); diff --git a/love/src/components/icons/ScriptQueue/ResumeIcon/ResumeIcon.module.css b/love/src/components/icons/ScriptQueue/ResumeIcon/ResumeIcon.module.css index 9e8a00a5a..c4131e41f 100644 --- a/love/src/components/icons/ScriptQueue/ResumeIcon/ResumeIcon.module.css +++ b/love/src/components/icons/ScriptQueue/ResumeIcon/ResumeIcon.module.css @@ -1,7 +1,5 @@ -.st0 { - fill: var(--commandable-font-color); - stroke: var(--commandable-font-color); - stroke-miterlimit: 10; +.cls-1{ + fill:#1ecfe8; } .svg { diff --git a/love/src/components/icons/ScriptQueue/StopIcon/StopIcon.jsx b/love/src/components/icons/ScriptQueue/StopIcon/StopIcon.jsx index 1e9ef93bd..ecfffbd5d 100644 --- a/love/src/components/icons/ScriptQueue/StopIcon/StopIcon.jsx +++ b/love/src/components/icons/ScriptQueue/StopIcon/StopIcon.jsx @@ -4,13 +4,13 @@ import styles from './StopIcon.module.css'; export default class StopIcon extends Component { render() { return ( - + {'Stop script'} - ); } diff --git a/love/src/components/icons/ScriptQueue/StopIcon/StopIcon.module.css b/love/src/components/icons/ScriptQueue/StopIcon/StopIcon.module.css index 94a78c8d4..3dad44dfa 100644 --- a/love/src/components/icons/ScriptQueue/StopIcon/StopIcon.module.css +++ b/love/src/components/icons/ScriptQueue/StopIcon/StopIcon.module.css @@ -1,14 +1,7 @@ -.st0 { - fill: #798d99; - stroke: #798d99; - stroke-miterlimit: 10; -} - .svg { vertical-align: middle; } -.svg path { - fill: var(--commandable-font-color); - stroke: var(--commandable-font-color); +.cls-1 { + fill: #1ecfe8; } diff --git a/love/src/components/icons/ScriptQueue/TerminateIcon/TerminateIcon.jsx b/love/src/components/icons/ScriptQueue/TerminateIcon/TerminateIcon.jsx index 6258f27a9..d4203001b 100644 --- a/love/src/components/icons/ScriptQueue/TerminateIcon/TerminateIcon.jsx +++ b/love/src/components/icons/ScriptQueue/TerminateIcon/TerminateIcon.jsx @@ -4,15 +4,12 @@ import styles from './TerminateIcon.module.css'; export default class TerminateIcon extends Component { render() { return ( - + {'Terminate script'} - ); diff --git a/love/src/components/icons/ScriptQueue/TerminateIcon/TerminateIcon.module.css b/love/src/components/icons/ScriptQueue/TerminateIcon/TerminateIcon.module.css index 8ffbe104e..3dad44dfa 100644 --- a/love/src/components/icons/ScriptQueue/TerminateIcon/TerminateIcon.module.css +++ b/love/src/components/icons/ScriptQueue/TerminateIcon/TerminateIcon.module.css @@ -1,11 +1,7 @@ -.st0 { - stroke-miterlimit: 10; -} - .svg { vertical-align: middle; } -.svg path { - stroke: var(--commandable-font-color); +.cls-1 { + fill: #1ecfe8; } diff --git a/love/src/redux/actions/ws.js b/love/src/redux/actions/ws.js index 52a0e91a9..10932b893 100644 --- a/love/src/redux/actions/ws.js +++ b/love/src/redux/actions/ws.js @@ -131,13 +131,12 @@ export const resetSubscriptions = (subscriptions = null) => { resetSubsTimer = setInterval(() => dispatch(resetSubscriptions()), RESET_SUBS_PERIOD); dispatch({ type: RESET_SUBSCRIPTIONS, - subscriptions: subs - ? subs.map((sub) => ({ - ...sub, - status: groupStates.PENDING, - confirmationMessage: undefined, - })) - : [], + subscriptions: + subs?.map((sub) => ({ + ...sub, + status: groupStates.PENDING, + confirmationMessage: undefined, + })) || [], }); dispatch(_requestSubscriptions()); }; diff --git a/love/src/redux/selectors/selectors.js b/love/src/redux/selectors/selectors.js index 8d7f2bec0..eca3f8beb 100644 --- a/love/src/redux/selectors/selectors.js +++ b/love/src/redux/selectors/selectors.js @@ -445,7 +445,7 @@ export const getCSCHeartbeat = (state, csc, salindex) => { */ export const getLastManagerHeartbeat = (state) => { if (state.heartbeats === undefined) return undefined; - return state.heartbeats?.lastHeartbeatInfo?.manager; + return state.heartbeats?.lastHeartbeatInfo?.Manager; }; /** diff --git a/love/src/redux/tests/heartbeats.test.js b/love/src/redux/tests/heartbeats.test.js index 4ce9de926..67ca7f93a 100644 --- a/love/src/redux/tests/heartbeats.test.js +++ b/love/src/redux/tests/heartbeats.test.js @@ -20,17 +20,17 @@ let server; const heartbeatsInfo = [ { category: 'heartbeat', - data: [{ csc: 'manager', salindex: 0, data: { timestamp: 1582141499.626869 } }], + data: [{ csc: 'Manager', salindex: 0, data: { timestamp: 1582141499.626869 } }], subscription: 'heartbeat', }, { category: 'heartbeat', - data: [{ csc: 'manager', salindex: 0, data: { timestamp: 1692141499.626869 } }], + data: [{ csc: 'Manager', salindex: 0, data: { timestamp: 1692141499.626869 } }], subscription: 'heartbeat', }, { category: 'heartbeat', - data: [{ csc: HEARTBEAT_COMPONENTS.EVENTS, salindex: 0, data: { timestamp: 1992141499.626869 } }], + data: [{ csc: 'Commander', salindex: 0, data: { timestamp: 1792141499.626869 } }], subscription: 'heartbeat', }, ]; @@ -91,16 +91,6 @@ describe('GIVEN we are subscribed to the manager heartbeat', () => { }); }); - describe('WHEN we receive a producer heartbeat', () => { - it('THEN we store it the state ', async () => { - // Arrange: - await server.send(heartbeatsInfo[2]); - const lastProducerHeartbeat = getLastComponentHeartbeat(store.getState(), HEARTBEAT_COMPONENTS.EVENTS); - // Assert: - expect(lastProducerHeartbeat).toEqual(heartbeatsInfo[2].data[0]); - }); - }); - describe('WHEN we receive 2 manager heartbeats', () => { it('THEN we store the last one ', async () => { // Arrange: @@ -112,6 +102,16 @@ describe('GIVEN we are subscribed to the manager heartbeat', () => { }); }); + describe('WHEN we receive a commander heartbeat', () => { + it('THEN we store it the state ', async () => { + // Arrange: + await server.send(heartbeatsInfo[2]); + const lastProducerHeartbeat = getLastComponentHeartbeat(store.getState(), HEARTBEAT_COMPONENTS.COMMANDER); + // Assert: + expect(lastProducerHeartbeat).toEqual(heartbeatsInfo[2].data[0]); + }); + }); + describe('WHEN we lose server connection', () => { it('THEN we dont receive new manager heartbeats ', async () => { // Arrange: diff --git a/love/yarn.lock b/love/yarn.lock index cbb6f9135..496f2284a 100644 --- a/love/yarn.lock +++ b/love/yarn.lock @@ -2454,7 +2454,7 @@ arch@^2.1.2: resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== -argparse@^1.0.7: +argparse@^1.0.10, argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== @@ -2673,6 +2673,13 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +autolinker@^3.11.0: + version "3.14.2" + resolved "https://registry.yarnpkg.com/autolinker/-/autolinker-3.14.2.tgz#71856274eb768fb7149039e24d3a2be2f5c55a63" + integrity sha512-VO66nXUCZFxTq7fVHAaiAkZNXRQ1l3IFi6D5P7DLoyIEAn2E8g7TWbyEgLlz1uW74LfWmu1A17IPWuPQyGuNVg== + dependencies: + tslib "^1.9.3" + autoprefixer@^9.6.1: version "9.8.6" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f" @@ -11694,6 +11701,14 @@ remark@^11.0.1: remark-stringify "^7.0.0" unified "^8.2.0" +remarkable@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/remarkable/-/remarkable-2.0.1.tgz#280ae6627384dfb13d98ee3995627ca550a12f31" + integrity sha512-YJyMcOH5lrR+kZdmB0aJJ4+93bEojRZ1HGDn9Eagu6ibg7aVZhc3OWbbShRid+Q5eAfsEqWxpe+g5W5nYNfNiA== + dependencies: + argparse "^1.0.10" + autolinker "^3.11.0" + remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -12562,9 +12577,9 @@ sshpk@^1.7.0: tweetnacl "~0.14.0" ssri@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8" - integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA== + version "6.0.2" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.2.tgz#157939134f20464e7301ddba3e90ffa8f7728ac5" + integrity sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q== dependencies: figgy-pudding "^3.5.1" @@ -13261,7 +13276,7 @@ tslib@2.0.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.0.tgz#18d13fc2dce04051e20f074cc8387fd8089ce4f3" integrity sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g== -tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0: +tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== @@ -14814,9 +14829,9 @@ xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== y18n@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" - integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + version "4.0.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" + integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== y18n@^5.0.1: version "5.0.5"