Skip to content
This repository has been archived by the owner on Aug 2, 2023. It is now read-only.

Commit

Permalink
Enable start/stop simulations (#705)
Browse files Browse the repository at this point in the history
* Add settings flyout

* Move strings to lang file

* Remove unneeded fromPromise calls

* Move extra strings to lang file

* Add better messaging on simulation update
  • Loading branch information
spryor authored Oct 23, 2017
1 parent 1cad871 commit d13a0ff
Show file tree
Hide file tree
Showing 14 changed files with 257 additions and 32 deletions.
27 changes: 16 additions & 11 deletions src/common/lang.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,21 +72,20 @@ const lang = {
DEVICEID: 'DeviceId',
DEVICELOCATION: 'Device Location',
DEVICENAME: 'Device Name',
DEVICES_ARE_PROVISIONING: 'devices are provisioning...',
DEVICES_TO_PROVISION: 'devices to provision',
PROVISION_SUMMARY:'Provision summary',
DEVICENUMBER: 'Number of devices',
DEVICEORGANIZATION: 'Device organization',
DEVICES: 'Devices',
DEVICES_ARE_PROVISIONING: 'devices are provisioning...',
DEVICES_TO_PROVISION: 'devices to provision',
DEVICETYPE: 'Device Type',
DEVICETYPES: 'Device Types',
DEVICE_ID_AFFECTED: 'Device Id Affected',
DIAGNOSTICS: 'Diagnostics',
DIAGNOSTICS_DESCRIPTION: 'Diagnostic information from device',
DISABLE: 'Disable',
DISABLED: 'Disabled',
DISABLESELECTEDRULE: 'Disable selected rules',
DISABLED_FILTER: '- - - -',
DISABLESELECTEDRULE: 'Disable selected rules',
DISCONNECTED: 'Disconnected',
DPSTENANT: 'DPS Tenant',
DROUPDOWNPLACEHOLDER: 'Select',
Expand All @@ -98,12 +97,12 @@ const lang = {
ENQUEUED: 'Enqueued',
ENROLLMENT: 'Enrollment',
ENTERFILTERVALUE: 'Enter filter value',
ENTER_DEVICE_ID: 'Enter device Id prefix...',
ENTERNAMEFORFILTER: 'Enter name for filter',
ENTERREPORTED: 'Enter reported or desired property,tag',
ENTERRULEDISCRIPTION: 'Enter rule description...',
ENTERRULENAME: 'Enter rule name...',
ENTERVALUE: 'Enter value...',
ENTER_DEVICE_ID: 'Enter device Id prefix...',
ENTER_KEYS_MANUALLY: 'Enter keys manually',
ENTER_YOUR_KEY: 'Enter your key here...',
EQUALS: '= Equals',
Expand Down Expand Up @@ -175,13 +174,12 @@ const lang = {
NOTIFICATIONTYPE: 'Notification Type',
NOT_COMPLETED : 'Not Completed',
NOW: 'Now',
NO_DATA: 'No data available',
NO_AVAILABLE_COMMON_TAGS: 'No available common tags.',
NO_DATA: 'No data available',
NO_OCCURENCES: 'No occurences selected',
NO_OF_DEVICES: 'No Of Devices',
NUMBEROFDEVICES: 'Number of devices',
NUMBER: 'Number',
STRING: 'String',
NUMBEROFDEVICES: 'Number of devices',
OCCURRENCE: 'Occurrence',
OFFLINE: 'Offline',
ONLINE: 'Online',
Expand All @@ -203,6 +201,7 @@ const lang = {
PROPERTYVALUE: 'Value',
PROVISIONDEVICES: 'Provision',
PROVISION_LOADING: 'Loading...',
PROVISION_SUMMARY:'Provision summary',
QUEUED: 'Queued',
RECONFIGURE: 'Reconfigure',
RECONFIGURE_JOB: 'ReconfigureJob',
Expand All @@ -228,30 +227,36 @@ const lang = {
SELECTMETHOD: 'Select your provisioning method',
SELECTTIMELINE: 'Select timeline',
SELECTTYPE: 'Select an existing device type',
SELECT_ONE: 'Select one',
SELECT_EXISTING_DVICE_TYPE: 'Select existing device type',
SELECT_ONE: 'Select one',
SELECT_PLACEHOLDER: 'Select method',
SENCONDARYTHUMBPRINT: 'Secondary thumbprint',
SEVERITY: 'Severity',
SEVERITYLEVEL: 'Severity level',
SIMULATED: 'Simulated',
SIMULATEDDEVICES: 'Simulated devices(s)',
SIMULATION_DATA: 'Simulation Data',
SIMULATION_SETTINGS_DESC: 'Start or stop the devices simulation data. Stopping the simulation data will minimize the cost of the solution. No telemetry will flow and rules will not be triggered for simulated devices.',
SIM_CONTROL_CENTER: 'SIM Control Center',
SOURCE: 'Source',
SOURCEHELP: 'Edit group of devices on rule',
START: 'Start',
START_TIME: 'Start Time',
STATE: 'State',
STATUS: 'Status',
STOP: 'Stop',
STOPPED: 'Stopped',
STRING: 'String',
SUCCEEDED: 'Succeeded',
SUCCEEDED_CAPITAL: 'Succeeded',
SUMMARY: 'Summary',
SWITCHUSER: 'Switch to another user',
SYMMETRICKEY: 'Symmetric Key',
SYMMETRIC_KEY: 'Symmetric Key',
SYNCING: 'Syncing',
SYSTEMKPI: 'System KPIs',
SYMMETRIC_KEY: 'Symmetric Key',
SYSTEM_STATUS: 'System Status',
SYSTEM_GENERATED_DEVICE_ID: 'System generated device IDs',
SYSTEM_STATUS: 'System Status',
TAG: 'Tag',
TAGS: 'Tags',
TAGS_DESCRIPTION: 'Tags applied to device',
Expand Down
12 changes: 5 additions & 7 deletions src/components/alarmList/alarmList.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,11 @@ class AlarmList extends Component {
return Rx.Observable.of(this.state)
.filter(({ deviceIdList }) => deviceIdList) // Don't make the call if device id list is not defined
.flatMap(({ timeRange, deviceIdList }) =>
Rx.Observable.fromPromise(
ApiService.getAlarmsByRule({
from: `NOW-${timeRange}`,
to: 'NOW',
devices: deviceIdList
})
)
ApiService.getAlarmsByRule({
from: `NOW-${timeRange}`,
to: 'NOW',
devices: deviceIdList
})
)
.map(this.dataFormatter);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,7 @@ class DeviceProvisioningWorkflow extends React.Component {
* @param {Object} event - A DOM event object with the input change details
* @param {Object} extraState - An object with any updated state other than the input
*/
handleInputChange(event, extraState = {}) {
const target = event.target;
handleInputChange({ target }, extraState = {}) {
const name = target.name;
const value = target.type === 'checkbox' ? target.checked : target.value;

Expand Down
2 changes: 1 addition & 1 deletion src/components/flyout/deviceDetailFlyout.js
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ class DeviceDetailFlyout extends Component {
: ((this.props.content || {}).device || {}).Id;
return Rx.Observable.of(deviceId)
.filter(id => id) // Ignore undefined device Ids
.flatMap(id => Rx.Observable.fromPromise(ApiService.getTelemetryMessageByDeviceIdP1M(id)))
.flatMap(id => ApiService.getTelemetryMessageByDeviceIdP1M(id))
.do(response => response.deviceId = deviceId);
};

Expand Down
5 changes: 1 addition & 4 deletions src/components/flyout/deviceReconfigureFlyout.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,7 @@ class DeviceReconfigureFlyout extends React.Component {
)
.distinct()
.filter(deviceId => deviceIdSet.has(deviceId))
.flatMap(deviceId =>
Rx.Observable
.fromPromise(ApiService.getDeviceById(deviceId))
)
.flatMap(deviceId => ApiService.getDeviceById(deviceId))
.reduce((devices, device) => [...devices, device], [])
.subscribe(
devices => this.props.actions.updateDevices(devices),
Expand Down
5 changes: 1 addition & 4 deletions src/components/flyout/deviceTagFlyout.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,7 @@ class DeviceTagFlyout extends React.Component {
)
.distinct()
.filter(deviceId => deviceIdSet.has(deviceId))
.flatMap(deviceId =>
Rx.Observable
.fromPromise(ApiService.getDeviceById(deviceId))
)
.flatMap(deviceId => ApiService.getDeviceById(deviceId))
.reduce((devices, device) => [...devices, device], [])
.subscribe(
devices => this.props.actions.updateDevices(devices),
Expand Down
4 changes: 4 additions & 0 deletions src/components/flyout/flyout.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import DeviceScheduleFlyout from './deviceScheduleFlyout';
import DeviceReconfigureFlyout from './deviceReconfigureFlyout';
import RuleEditor from '../ruleEditor/ruleEditor';
import DeviceProvisioningWorkflow from '../deviceProvisioningWorkflow/deviceProvisioningWorkflow';
import SettingFlyout from '../settingFlyout/settingFlyout';

import CloseIcon from '../../assets/icons/X.svg';

Expand Down Expand Up @@ -40,6 +41,9 @@ const getFlyout = (content, onClose) => {
case 'Provision':
return <DeviceProvisioningWorkflow content={content} onClose={onClose} />;

case 'Settings':
return <SettingFlyout content={content} onClose={onClose} />;

default:
return null;
}
Expand Down
23 changes: 20 additions & 3 deletions src/components/layout/topNav/topNav.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Copyright (c) Microsoft. All rights reserved.

import React, { Component } from 'react';
import { connect } from 'react-redux';
import Profile from '../profile/profile.js';
import PcsTooltip from '../../shared/pcsTooltip/pcsTooltip';
import lang from '../../../common/lang';
import * as actionTypes from '../../../actions/actionTypes';

import Setting from '../../../assets/icons/Setting.svg';

Expand Down Expand Up @@ -40,8 +42,7 @@ class TopNav extends Component {
<div className="user-settings">
<img
className="settings-icon"
onMouseOver={this.showToolTip}
onMouseOut={this.hideToolTip}
onClick={this.props.openSettingsFlyout}
src={Setting}
alt="Setting" />
<PcsTooltip {...tooltipProps} />
Expand All @@ -52,4 +53,20 @@ class TopNav extends Component {
}
}

export default TopNav;
// Connect to Redux store
const mapDispatchToProps = dispatch => {

// A helper method for opening the flyout
const openFlyout = (type, callback) => {
dispatch({
type: actionTypes.FLYOUT_SHOW,
content: { type, callback }
});
}

return {
openSettingsFlyout: () => openFlyout('Settings')
};
};

export default connect(undefined, mapDispatchToProps)(TopNav);
1 change: 1 addition & 0 deletions src/components/layout/topNav/topNav.scss
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ $borderColor: #454A4E;
width: 20px;
margin-right: 20px;
margin-left: 20px;
cursor: pointer;
}
}
}
111 changes: 111 additions & 0 deletions src/components/settingFlyout/settingFlyout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright (c) Microsoft. All rights reserved.

import React from 'react';
import { Observable, BehaviorSubject, Subject } from 'rxjs';
import PcsBtn from '../shared/pcsBtn/pcsBtn';
import PscToggle from '../shared/pcsToggle/pcsToggle';
import DeviceSimulationService from '../../services/deviceSimulationService';
import lang from '../../common/lang';
import Spinner from '../spinner/spinner';

import CloseIconSvg from '../../assets/icons/X.svg';

import './settingFlyout.css';

// Helper objects for choosing the correct label for the simulation service toggle input
const desiredSimulationLabel = {
true: lang.START,
false: lang.STOP
};

const currSimulationLabel = {
true: lang.RUNNING,
false: lang.STOPPED
};

class SettingFlyout extends React.Component {

constructor(props) {
super(props);

this.state = {
currSimulationState: undefined,
desiredSimulationState: undefined,
loading: false
};

this.eTag = new BehaviorSubject(undefined);
this.unmount = new Subject();
this.eTagStream = this.eTag.filter(_ => _);
}

componentDidMount() {
Observable.fromPromise(DeviceSimulationService.getSimulatedDevices())
.takeUntil(this.unmount)
.subscribe(
({ Etag, Enabled }) => {
this.setState({
currSimulationState: Enabled,
desiredSimulationState: Enabled
});
this.eTag.next(Etag);
},
err => this.eTag.error(err)
);
}

componentWillUnmount() {
this.unmount.next(undefined);
this.unmount.unsubscribe();
}

onChange = ({ target }) => {
const { name, value } = target;
this.setState({ [name]: value });
};

apply = () => {
Observable
.of(this.state.desiredSimulationState)
.do(_ => this.setState({ loading: true}))
.zip(this.eTagStream, (Enabled, Etag) => ({ Etag, Enabled }))
.flatMap(({ Etag, Enabled }) => DeviceSimulationService.toggleSimulation(Etag, Enabled))
.takeUntil(this.unmount)
.subscribe(
() => this.props.onClose(),
err => console.error(err)
);
};

render() {
const { currSimulationState, desiredSimulationState, loading } = this.state;
const stillInitializing = currSimulationState === undefined;
const hasChanged = !stillInitializing && currSimulationState !== desiredSimulationState;

const simulationLabel = hasChanged ? desiredSimulationLabel[desiredSimulationState] : currSimulationLabel[currSimulationState];

return (
<div className="setting-workflow-container">
<div className="setting-section">
<div className="section-header">{ lang.SIMULATION_DATA }</div>
<div className="section-description">{ lang.SIMULATION_SETTINGS_DESC }</div>
<div className="section-input-group">
<PscToggle
name="desiredSimulationState"
value={desiredSimulationState}
disabled={stillInitializing}
onChange={this.onChange} />
<label>{ stillInitializing ? lang.LOADING : simulationLabel }</label>
</div>
</div>
<div className="btn-container">
<PcsBtn svg={CloseIconSvg} onClick={this.props.onClose}>{hasChanged ? lang.CANCEL : lang.CLOSE }</PcsBtn>
{ !loading && hasChanged && <PcsBtn onClick={this.apply}>{ lang.APPLY }</PcsBtn> }
{ loading && <Spinner size='small' /> }
</div>
</div>
);
}
}

export default SettingFlyout;
38 changes: 38 additions & 0 deletions src/components/settingFlyout/settingFlyout.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Microsoft. All rights reserved.

.setting-workflow-container {

.setting-section {
padding-top: 24px;
border-bottom: 1px solid #454a4e;

.section-header {
color: #fff;
font-size: 16px;
padding-bottom: 16px;
}

.section-description {
padding-bottom: 24px;
font-size: 14px;
}

.section-input-group {
padding-bottom: 48px;

label {
padding-left: 10px;
font-weight: normal;
margin: 0;
}
}

}

.btn-container {
display: flex;
justify-content: flex-end;
height: 30px;
margin-top: 48px;
}
}
Loading

0 comments on commit d13a0ff

Please sign in to comment.