1cd789508SIftekharul Islam/** 2cd789508SIftekharul Islam * Controller for date-time 3cd789508SIftekharul Islam * 4cd789508SIftekharul Islam * @module app/configuration 5cd789508SIftekharul Islam * @exports dateTimeController 6cd789508SIftekharul Islam * @name dateTimeController 7cd789508SIftekharul Islam */ 8cd789508SIftekharul Islam 9cd789508SIftekharul Islamwindow.angular && (function(angular) { 10cd789508SIftekharul Islam 'use strict'; 11cd789508SIftekharul Islam 12d27bb135SAndrew Geissler angular.module('app.configuration').controller('dateTimeController', [ 13fdcb35eeSDixsie Wolmers '$scope', 'APIUtils', '$route', '$q', 'toastService', '$timeout', 14fdcb35eeSDixsie Wolmers function($scope, APIUtils, $route, $q, toastService, $timeout) { 15b7ea2790SGunnar Mills $scope.bmc = {}; 16eab3213cSGunnar Mills // Only used when the owner is "Split" 17b7ea2790SGunnar Mills $scope.host = {}; 18b7ea2790SGunnar Mills $scope.ntp = {servers: []}; 192f955bd8SGunnar Mills $scope.time = {mode: '', owner: ''}; 202f955bd8SGunnar Mills // Possible time owners 212f955bd8SGunnar Mills // https://github.com/openbmc/phosphor-dbus-interfaces/blob/master/xyz/openbmc_project/Time/Owner.interface.yaml#L13 222f955bd8SGunnar Mills $scope.timeOwners = ['BMC', 'Host', 'Both', 'Split']; 237de38662SGunnar Mills $scope.loading = true; 242f955bd8SGunnar Mills var timePath = '/xyz/openbmc_project/time/'; 257de38662SGunnar Mills 26c74d434cSGunnar Mills var getTimePromise = APIUtils.getTime().then( 277de38662SGunnar Mills function(data) { 28b7ea2790SGunnar Mills // The time is returned as Epoch microseconds convert to 29b7ea2790SGunnar Mills // milliseconds. 302f955bd8SGunnar Mills if (data.data[timePath + 'bmc'] && 312f955bd8SGunnar Mills data.data[timePath + 'bmc'].hasOwnProperty('Elapsed')) { 32b7ea2790SGunnar Mills $scope.bmc.date = 332f955bd8SGunnar Mills new Date(data.data[timePath + 'bmc'].Elapsed / 1000); 34b7ea2790SGunnar Mills // Don't care about milliseconds and don't want them displayed 35b7ea2790SGunnar Mills $scope.bmc.date.setMilliseconds(0); 36dbf04811SAlexander Filippov 37dbf04811SAlexander Filippov // Examples: 38dbf04811SAlexander Filippov // Central Standard Time (UTC-06:00) 39dbf04811SAlexander Filippov // Moscow Standard Time (UTC+03:00) 40dbf04811SAlexander Filippov $scope.bmc.timezone = getUserTimezone($scope.bmc.date) + ' ' + 41dbf04811SAlexander Filippov createOffset($scope.bmc.date); 42b7ea2790SGunnar Mills } 432f955bd8SGunnar Mills if (data.data[timePath + 'host'] && 442f955bd8SGunnar Mills data.data[timePath + 'host'].hasOwnProperty('Elapsed')) { 45b7ea2790SGunnar Mills $scope.host.date = 462f955bd8SGunnar Mills new Date(data.data[timePath + 'host'].Elapsed / 1000); 47b7ea2790SGunnar Mills $scope.host.date.setMilliseconds(0); 48dbf04811SAlexander Filippov $scope.host.timezone = getUserTimezone($scope.bmc.date) + ' ' + 49dbf04811SAlexander Filippov createOffset($scope.bmc.date); 50b7ea2790SGunnar Mills } 512f955bd8SGunnar Mills if (data.data[timePath + 'owner'] && 522f955bd8SGunnar Mills data.data[timePath + 'owner'].hasOwnProperty('TimeOwner')) { 532f955bd8SGunnar Mills $scope.time.owner = 542f955bd8SGunnar Mills data.data[timePath + 'owner'].TimeOwner.split('.').pop(); 55b7ea2790SGunnar Mills } 562f955bd8SGunnar Mills if (data.data[timePath + 'sync_method'] && 572f955bd8SGunnar Mills data.data[timePath + 'sync_method'].hasOwnProperty( 58b7ea2790SGunnar Mills 'TimeSyncMethod')) { 592f955bd8SGunnar Mills $scope.time.mode = data.data[timePath + 'sync_method'] 60c74d434cSGunnar Mills .TimeSyncMethod.split('.') 61c74d434cSGunnar Mills .pop(); 62b7ea2790SGunnar Mills } 637de38662SGunnar Mills }, 647de38662SGunnar Mills function(error) { 657de38662SGunnar Mills console.log(JSON.stringify(error)); 667de38662SGunnar Mills }); 677de38662SGunnar Mills 68b7ea2790SGunnar Mills var getNTPPromise = APIUtils.getNTPServers().then( 69b7ea2790SGunnar Mills function(data) { 70b7ea2790SGunnar Mills $scope.ntp.servers = data.data; 71b7ea2790SGunnar Mills }, 72b7ea2790SGunnar Mills function(error) { 73b7ea2790SGunnar Mills console.log(JSON.stringify(error)); 74b7ea2790SGunnar Mills }); 75b7ea2790SGunnar Mills 76fdcb35eeSDixsie Wolmers var promises = [getTimePromise, getNTPPromise]; 77b7ea2790SGunnar Mills 78b7ea2790SGunnar Mills $q.all(promises).finally(function() { 797de38662SGunnar Mills $scope.loading = false; 807de38662SGunnar Mills }); 81b7ea2790SGunnar Mills 82fdcb35eeSDixsie Wolmers /** 83fdcb35eeSDixsie Wolmers * https://github.com/openbmc/phosphor-time-manager/blob/master/README.md#special-note-on-changing-ntp-setting 84fdcb35eeSDixsie Wolmers * When time mode is initially set to Manual from NTP, 85fdcb35eeSDixsie Wolmers * NTP service is disabled and the NTP service is 86fdcb35eeSDixsie Wolmers * stopping but not stopped, setting time will return an error. 87fdcb35eeSDixsie Wolmers * There are no responses from backend to notify when NTP is stopped. 88fdcb35eeSDixsie Wolmers * To work around, a timeout is set to allow NTP to fully stop 89fdcb35eeSDixsie Wolmers * TODO: remove timeout if backend solves 90fdcb35eeSDixsie Wolmers * https://github.com/openbmc/openbmc/issues/3459 91fdcb35eeSDixsie Wolmers */ 92fdcb35eeSDixsie Wolmers $scope.saveDateTimeSettings = function() { 93b7ea2790SGunnar Mills $scope.loading = true; 94fdcb35eeSDixsie Wolmers if ($scope.time.mode == 'Manual' || $scope.time.owner == 'Split') { 95fdcb35eeSDixsie Wolmers setTimeMode() 96fdcb35eeSDixsie Wolmers .then(setTimeOwner) 97fdcb35eeSDixsie Wolmers .then(setNTPServers) 98fdcb35eeSDixsie Wolmers .then($timeout(setDateTime, 20000)); 99fdcb35eeSDixsie Wolmers } else { 100fdcb35eeSDixsie Wolmers setTimeMode() 101fdcb35eeSDixsie Wolmers .then(setTimeOwner) 102fdcb35eeSDixsie Wolmers .then(setNTPServers) 103b7ea2790SGunnar Mills .then( 104b7ea2790SGunnar Mills function() { 10527ce84d2Sbeccabroek toastService.success('Date and time settings saved'); 106b7ea2790SGunnar Mills }, 107b7ea2790SGunnar Mills function(errors) { 108b7ea2790SGunnar Mills console.log(JSON.stringify(errors)); 10927ce84d2Sbeccabroek toastService.error( 11030444c6cSbeccabroek 'Date and time settings could not be saved'); 111b7ea2790SGunnar Mills }) 112b7ea2790SGunnar Mills .finally(function() { 113b7ea2790SGunnar Mills $scope.loading = false; 114b7ea2790SGunnar Mills }); 115fdcb35eeSDixsie Wolmers } 116fdcb35eeSDixsie Wolmers }; 117fdcb35eeSDixsie Wolmers 118fdcb35eeSDixsie Wolmers const setDateTime = function() { 119fdcb35eeSDixsie Wolmers var manualPromises = []; 120fdcb35eeSDixsie Wolmers if ($scope.time.mode == 'Manual') { 121fdcb35eeSDixsie Wolmers // If owner is 'Split' set both. 122fdcb35eeSDixsie Wolmers // If owner is 'Host' set only it. 123fdcb35eeSDixsie Wolmers if ($scope.time.owner != 'Host') { 124fdcb35eeSDixsie Wolmers manualPromises.push(setBMCTime($scope.bmc.date.getTime() * 1000)); 125fdcb35eeSDixsie Wolmers } 126fdcb35eeSDixsie Wolmers // Even though we are setting Host time, we are setting from 127fdcb35eeSDixsie Wolmers // the BMC date and time fields labeled "BMC and Host Time" 128fdcb35eeSDixsie Wolmers // currently. 129fdcb35eeSDixsie Wolmers if ($scope.time.owner == 'Host') { 130fdcb35eeSDixsie Wolmers manualPromises.push(setHostTime($scope.bmc.date.getTime() * 1000)); 131fdcb35eeSDixsie Wolmers } 132fdcb35eeSDixsie Wolmers } 133fdcb35eeSDixsie Wolmers // Set the Host if Split even if NTP. In split mode, the host has 134fdcb35eeSDixsie Wolmers // its own date and time field set from it. 135fdcb35eeSDixsie Wolmers if ($scope.time.owner == 'Split') { 136fdcb35eeSDixsie Wolmers manualPromises.push(setHostTime($scope.host.date.getTime() * 1000)); 137fdcb35eeSDixsie Wolmers } 138fdcb35eeSDixsie Wolmers 139fdcb35eeSDixsie Wolmers $q.all(manualPromises) 140fdcb35eeSDixsie Wolmers .then( 141fdcb35eeSDixsie Wolmers function() { 142fdcb35eeSDixsie Wolmers toastService.success('Date and time settings saved'); 143b7ea2790SGunnar Mills }, 144b7ea2790SGunnar Mills function(errors) { 145b7ea2790SGunnar Mills console.log(JSON.stringify(errors)); 146fdcb35eeSDixsie Wolmers toastService.error( 147fdcb35eeSDixsie Wolmers 'Date and time settings could not be saved'); 148fdcb35eeSDixsie Wolmers }) 149fdcb35eeSDixsie Wolmers .finally(function() { 150b7ea2790SGunnar Mills $scope.loading = false; 151b7ea2790SGunnar Mills }); 152b7ea2790SGunnar Mills }; 153fdcb35eeSDixsie Wolmers 154b7ea2790SGunnar Mills $scope.refresh = function() { 155b7ea2790SGunnar Mills $route.reload(); 156b7ea2790SGunnar Mills }; 157b7ea2790SGunnar Mills 158b7ea2790SGunnar Mills $scope.addNTPField = function() { 159b7ea2790SGunnar Mills $scope.ntp.servers.push(''); 160b7ea2790SGunnar Mills }; 161b7ea2790SGunnar Mills 16290121f3fSGunnar Mills $scope.removeNTPField = function(index) { 16390121f3fSGunnar Mills $scope.ntp.servers.splice(index, 1); 16490121f3fSGunnar Mills }; 16590121f3fSGunnar Mills 166b7ea2790SGunnar Mills function setNTPServers() { 167*254b16eeSGunnar Mills // Remove any empty strings from the array. If the 168b7ea2790SGunnar Mills // user doesn't fill out the field, we don't want to add. 169b7ea2790SGunnar Mills $scope.ntp.servers = $scope.ntp.servers.filter(Boolean); 170*254b16eeSGunnar Mills 171b7ea2790SGunnar Mills return APIUtils.setNTPServers($scope.ntp.servers); 172b7ea2790SGunnar Mills } 173b7ea2790SGunnar Mills 174b7ea2790SGunnar Mills function setTimeMode() { 175b7ea2790SGunnar Mills return APIUtils.setTimeMode( 176b7ea2790SGunnar Mills 'xyz.openbmc_project.Time.Synchronization.Method.' + 1772f955bd8SGunnar Mills $scope.time.mode); 178b7ea2790SGunnar Mills } 179b7ea2790SGunnar Mills 180b7ea2790SGunnar Mills function setTimeOwner() { 181b7ea2790SGunnar Mills return APIUtils.setTimeOwner( 1822f955bd8SGunnar Mills 'xyz.openbmc_project.Time.Owner.Owners.' + $scope.time.owner); 183b7ea2790SGunnar Mills } 184b7ea2790SGunnar Mills 185eab3213cSGunnar Mills function setBMCTime(time) { 186b7ea2790SGunnar Mills // Add the separate date and time objects and convert to Epoch time in 187b7ea2790SGunnar Mills // microseconds. 188eab3213cSGunnar Mills return APIUtils.setBMCTime(time); 189b7ea2790SGunnar Mills } 190b7ea2790SGunnar Mills 191eab3213cSGunnar Mills function setHostTime(time) { 192b7ea2790SGunnar Mills // Add the separate date and time objects and convert to Epoch time 193b7ea2790SGunnar Mills // microseconds. 194eab3213cSGunnar Mills return APIUtils.setHostTime(time); 195b7ea2790SGunnar Mills } 196569ccf66Sbeccabroek function createOffset(date) { 197569ccf66Sbeccabroek // https://stackoverflow.com/questions/9149556/how-to-get-utc-offset-in-javascript-analog-of-timezoneinfo-getutcoffset-in-c 198569ccf66Sbeccabroek var sign = (date.getTimezoneOffset() > 0) ? '-' : '+'; 199569ccf66Sbeccabroek var offset = Math.abs(date.getTimezoneOffset()); 200569ccf66Sbeccabroek var hours = pad(Math.floor(offset / 60)); 201569ccf66Sbeccabroek var minutes = pad(offset % 60); 202569ccf66Sbeccabroek return '(UTC' + sign + hours + ':' + minutes + ')'; 203569ccf66Sbeccabroek } 204dbf04811SAlexander Filippov function getUserTimezone(date) { 205dbf04811SAlexander Filippov const ro = Intl.DateTimeFormat().resolvedOptions(); 206dbf04811SAlexander Filippov // A safe, easy way to get the timezone (e.g. Central Standard Time) is 207dbf04811SAlexander Filippov // to subtract the time string without a timezone from the time string 208dbf04811SAlexander Filippov // with a timezone. 209dbf04811SAlexander Filippov // Hardcoded to 'en-US' so all timezones are displayed in English 210dbf04811SAlexander Filippov // (e.g. Moscow Standard Time). 211dbf04811SAlexander Filippov var ret = date.toLocaleTimeString('en-US', {timeZoneName: 'long'}) 212dbf04811SAlexander Filippov .replace(date.toLocaleTimeString('en-US'), '') 213dbf04811SAlexander Filippov .trim(); 214dbf04811SAlexander Filippov // Do not return GMT+/-offset. 215dbf04811SAlexander Filippov if (ret.indexOf('GMT') >= 0) { 216dbf04811SAlexander Filippov return ''; 217dbf04811SAlexander Filippov } 218dbf04811SAlexander Filippov return ret; 219dbf04811SAlexander Filippov } 220569ccf66Sbeccabroek function pad(value) { 221569ccf66Sbeccabroek return value < 10 ? '0' + value : value; 222569ccf66Sbeccabroek } 223cd789508SIftekharul Islam } 224ba5e3f34SAndrew Geissler ]); 225cd789508SIftekharul Islam})(angular); 226