├── Development ├── .dockerignore ├── .eslintignore ├── src │ ├── assets │ │ └── sea-lion.png │ ├── pages │ │ ├── logs │ │ │ ├── index.js │ │ │ ├── LogsShow.js │ │ │ └── LogsList.js │ │ ├── flows │ │ │ └── index.js │ │ ├── nodes │ │ │ └── index.js │ │ ├── devices │ │ │ └── index.js │ │ ├── sources │ │ │ ├── index.js │ │ │ ├── SourcesShow.js │ │ │ └── SourcesList.js │ │ ├── queryapis │ │ │ ├── index.js │ │ │ ├── QueryAPIsShow.js │ │ │ ├── QueryAPIsList.js │ │ │ └── ConnectButton.js │ │ ├── senders │ │ │ ├── index.js │ │ │ ├── SendersList.js │ │ │ └── SendersEdit.js │ │ ├── receivers │ │ │ ├── index.js │ │ │ └── ConnectButtons.js │ │ ├── subscriptions │ │ │ ├── index.js │ │ │ ├── SubscriptionsCreate.js │ │ │ ├── SubscriptionsShow.js │ │ │ └── SubscriptionsList.js │ │ ├── about.js │ │ └── menu.js │ ├── index.css │ ├── components │ │ ├── CustomNamesContext.js │ │ ├── useCustomNamesContext.js │ │ ├── CustomNamesContextProvider.js │ │ ├── SanitizedDivider.js │ │ ├── AppLogo.js │ │ ├── ResourceShowActions.js │ │ ├── UnsortableDatagrid.js │ │ ├── ItemArrayField.js │ │ ├── RateField.js │ │ ├── RawButton.js │ │ ├── sanitizeRestProps.js │ │ ├── HintTypography.js │ │ ├── LinkChipField.js │ │ ├── URLField.js │ │ ├── ResourceTitle.js │ │ ├── useDebounce.js │ │ ├── ConnectionEditActions.js │ │ ├── CollapseButton.js │ │ ├── MappingButton.js │ │ ├── labelize.js │ │ ├── ListActions.js │ │ ├── TAIField.js │ │ ├── PaginationButtons.js │ │ ├── ObjectField.js │ │ ├── TransportFileViewer.js │ │ ├── ConstraintField.js │ │ ├── MappingShowActions.js │ │ ├── ConnectionEditToolbar.js │ │ ├── ConnectionShowActions.js │ │ ├── ActiveField.js │ │ ├── DeleteButton.js │ │ ├── CustomNameField.js │ │ ├── CardFormIterator.js │ │ └── useGetList.js │ ├── icons │ │ ├── ContentCopy.js │ │ ├── Device.js │ │ ├── Flow.js │ │ ├── Stage.js │ │ ├── svgr-template.js │ │ ├── JsonIcon.js │ │ ├── index.js │ │ ├── Receiver.js │ │ ├── Sender.js │ │ ├── Node.js │ │ ├── ActivateScheduled.js │ │ ├── ActivateImmediate.js │ │ ├── CancelScheduledActivation.js │ │ ├── Source.js │ │ ├── ConnectRegistry.js │ │ ├── Subscription.js │ │ ├── Registry.js │ │ └── RegistryLogs.js │ ├── index.js │ ├── config.json │ ├── LoginButton.js │ ├── theme │ │ └── ThemeContext.js │ ├── App.js │ └── registerServiceWorker.js ├── public │ ├── manifest.json │ └── index.html ├── Dockerfile ├── .gitignore ├── .prettierrc.js ├── .eslintrc └── package.json ├── TestingFacade ├── .flake8 ├── requirements.txt ├── Config.py ├── README.md ├── DataStore.py ├── TestingFacade.py ├── IS0503AutoTest.py └── GenericAutoTest.py ├── Documents ├── images │ ├── sea-lion.png │ └── jt-nm-tested-03-20-controller.png ├── Repository-Structure.md ├── Dependencies.md └── Getting-Started.md ├── .travis.yml ├── is12-client ├── src │ ├── client │ │ ├── DeviceConnect.css │ │ └── DevApp.css │ ├── index.css │ ├── backend │ │ └── DeviceProvider.ts │ └── index.js ├── public │ ├── config.json │ ├── manifest.json │ └── index.html ├── .gitignore ├── README.md └── package.json ├── NOTICE ├── Sandbox ├── make_badges.sh └── run_controller_tests.sh ├── CONTRIBUTING.md └── README.md /Development/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | -------------------------------------------------------------------------------- /TestingFacade/.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 120 3 | -------------------------------------------------------------------------------- /Development/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | lib 4 | esm 5 | -------------------------------------------------------------------------------- /TestingFacade/requirements.txt: -------------------------------------------------------------------------------- 1 | flask>=1.0.0 2 | selenium 3 | requests -------------------------------------------------------------------------------- /Documents/images/sea-lion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sony/nmos-js/HEAD/Documents/images/sea-lion.png -------------------------------------------------------------------------------- /Development/src/assets/sea-lion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sony/nmos-js/HEAD/Development/src/assets/sea-lion.png -------------------------------------------------------------------------------- /Documents/images/jt-nm-tested-03-20-controller.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sony/nmos-js/HEAD/Documents/images/jt-nm-tested-03-20-controller.png -------------------------------------------------------------------------------- /Development/src/pages/logs/index.js: -------------------------------------------------------------------------------- 1 | import LogsList from './LogsList'; 2 | import LogsShow from './LogsShow'; 3 | 4 | export { LogsList, LogsShow }; 5 | -------------------------------------------------------------------------------- /Development/src/pages/flows/index.js: -------------------------------------------------------------------------------- 1 | import FlowsList from './FlowsList'; 2 | import FlowsShow from './FlowsShow'; 3 | 4 | export { FlowsList, FlowsShow }; 5 | -------------------------------------------------------------------------------- /Development/src/pages/nodes/index.js: -------------------------------------------------------------------------------- 1 | import NodesList from './NodesList'; 2 | import NodesShow from './NodesShow'; 3 | 4 | export { NodesList, NodesShow }; 5 | -------------------------------------------------------------------------------- /Development/src/index.css: -------------------------------------------------------------------------------- 1 | html { 2 | overflow-y: scroll; 3 | } 4 | 5 | body { 6 | margin: 0; 7 | padding: 0; 8 | font-family: sans-serif; 9 | } 10 | -------------------------------------------------------------------------------- /Development/src/pages/devices/index.js: -------------------------------------------------------------------------------- 1 | import DevicesList from './DevicesList'; 2 | import DevicesShow from './DevicesShow'; 3 | 4 | export { DevicesList, DevicesShow }; 5 | -------------------------------------------------------------------------------- /Development/src/pages/sources/index.js: -------------------------------------------------------------------------------- 1 | import SourcesList from './SourcesList'; 2 | import SourcesShow from './SourcesShow'; 3 | 4 | export { SourcesList, SourcesShow }; 5 | -------------------------------------------------------------------------------- /Development/src/pages/queryapis/index.js: -------------------------------------------------------------------------------- 1 | import QueryAPIsList from './QueryAPIsList'; 2 | import QueryAPIsShow from './QueryAPIsShow'; 3 | 4 | export { QueryAPIsList, QueryAPIsShow }; 5 | -------------------------------------------------------------------------------- /Development/src/components/CustomNamesContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | export const CustomNamesContext = createContext(); 4 | export default CustomNamesContext; 5 | -------------------------------------------------------------------------------- /Documents/Repository-Structure.md: -------------------------------------------------------------------------------- 1 | # Repository Structure 2 | 3 | - [Development](../Development) 4 | Source code for the software 5 | - [Documents](../Documents) 6 | Documentation 7 | -------------------------------------------------------------------------------- /Development/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "nmos-js", 3 | "start_url": "./index.html", 4 | "display": "standalone", 5 | "theme_color": "rgb(0,47,103)", 6 | "background_color": "#ffffff" 7 | } 8 | -------------------------------------------------------------------------------- /Development/src/pages/senders/index.js: -------------------------------------------------------------------------------- 1 | import SendersEdit from './SendersEdit'; 2 | import SendersList from './SendersList'; 3 | import SendersShow from './SendersShow'; 4 | 5 | export { SendersEdit, SendersList, SendersShow }; 6 | -------------------------------------------------------------------------------- /Development/src/pages/receivers/index.js: -------------------------------------------------------------------------------- 1 | import ReceiversEdit from './ReceiversEdit'; 2 | import ReceiversList from './ReceiversList'; 3 | import ReceiversShow from './ReceiversShow'; 4 | 5 | export { ReceiversEdit, ReceiversList, ReceiversShow }; 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: lts/* 3 | 4 | before_install: cd Development 5 | 6 | install: yarn install 7 | 8 | script: 9 | - yarn run lint-check 10 | - yarn run build 11 | - yarn test --passWithNoTests --coverage --watchAll=false 12 | -------------------------------------------------------------------------------- /Development/src/components/useCustomNamesContext.js: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import CustomNamesContext from './CustomNamesContext'; 3 | 4 | export const useCustomNamesContext = () => useContext(CustomNamesContext); 5 | export default useCustomNamesContext; 6 | -------------------------------------------------------------------------------- /is12-client/src/client/DeviceConnect.css: -------------------------------------------------------------------------------- 1 | .basic { 2 | position: relative; 3 | display: grid; 4 | grid-auto-columns: auto; 5 | justify-content: center; 6 | justify-items: center; 7 | grid-template-rows: auto auto auto auto; 8 | font-size: 1.5rem; 9 | } -------------------------------------------------------------------------------- /Development/src/pages/subscriptions/index.js: -------------------------------------------------------------------------------- 1 | import SubscriptionsCreate from './SubscriptionsCreate'; 2 | import SubscriptionsList from './SubscriptionsList'; 3 | import SubscriptionsShow from './SubscriptionsShow'; 4 | 5 | export { SubscriptionsCreate, SubscriptionsList, SubscriptionsShow }; 6 | -------------------------------------------------------------------------------- /Development/src/components/CustomNamesContextProvider.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import CustomNamesContext from './CustomNamesContext'; 3 | 4 | export const CustomNamesContextProvider = props => ( 5 | 6 | ); 7 | export default CustomNamesContextProvider; 8 | -------------------------------------------------------------------------------- /Development/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:latest AS build 2 | WORKDIR /usr/src/app 3 | COPY package.json yarn.lock ./ 4 | RUN yarn 5 | COPY . ./ 6 | RUN yarn build 7 | 8 | FROM nginx:alpine 9 | COPY --from=build /usr/src/app/build /usr/share/nginx/html 10 | EXPOSE 80 11 | ENTRYPOINT ["nginx", "-g", "daemon off;"] 12 | -------------------------------------------------------------------------------- /is12-client/public/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": "PROGRAM WILL OVERWRITE DEFAULT MENU WHEN 'address' AND 'port' PROVIDED", 3 | "address": "ws://43.195.121.163:7002/x-nmos/ncp/v1.0", 4 | "!port": 8080, 5 | "debug": true, 6 | "showClassManager": false, 7 | "useApp": "mainApp", 8 | "NOTE": "'useApp' should be mainApp or devApp" 9 | } -------------------------------------------------------------------------------- /is12-client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Device Model Browser", 3 | "name": "Device Model Browser", 4 | "icons": [ 5 | { 6 | "src": "icon.png", 7 | "sizes": "377x373", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /Development/src/components/SanitizedDivider.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Divider } from '@material-ui/core'; 3 | import sanitizeRestProps from './sanitizeRestProps'; 4 | 5 | // Passing react-admin props causes console spam 6 | export const SanitizedDivider = ({ ...rest }) => ( 7 | 8 | ); 9 | 10 | export default SanitizedDivider; 11 | -------------------------------------------------------------------------------- /Development/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | /.eslintcache 23 | -------------------------------------------------------------------------------- /Development/src/icons/ContentCopy.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SvgIcon } from '@material-ui/core'; 3 | 4 | const SvgContentCopy = props => ( 5 | 6 | 7 | 8 | ); 9 | 10 | export default SvgContentCopy; 11 | -------------------------------------------------------------------------------- /Development/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | arrowParens: 'avoid', 3 | bracketSpacing: true, 4 | endOfLine: 'auto', 5 | jsxBracketSameLine: false, 6 | jsxSingleQuote: false, 7 | printWidth: 80, 8 | quoteProps: 'as-needed', 9 | rangeStart: 0, 10 | rangeEnd: Infinity, 11 | semi: true, 12 | singleQuote: true, 13 | tabWidth: 4, 14 | trailingComma: 'es5', 15 | useTabs: false, 16 | }; 17 | -------------------------------------------------------------------------------- /Development/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import registerServiceWorker from './registerServiceWorker'; 6 | import { AppThemeProvider } from './theme/ThemeContext'; 7 | 8 | ReactDOM.render( 9 | 10 | 11 | , 12 | document.getElementById('root') 13 | ); 14 | registerServiceWorker(); 15 | -------------------------------------------------------------------------------- /is12-client/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | .vs 21 | .idea 22 | 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | -------------------------------------------------------------------------------- /Development/src/components/AppLogo.js: -------------------------------------------------------------------------------- 1 | import AppLogoAsset from '../assets/sea-lion.png'; 2 | 3 | const AppLogoStyle = { 4 | border: '1px solid lightgray', 5 | borderRadius: '50%', 6 | padding: '4px', 7 | margin: '16px', 8 | maxWidth: '50%', 9 | }; 10 | 11 | export const AppLogo = ({ 12 | src = AppLogoAsset, 13 | style = { ...AppLogoStyle }, 14 | ...props 15 | }) => Logo; 16 | 17 | export default AppLogo; 18 | -------------------------------------------------------------------------------- /Development/src/components/ResourceShowActions.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ListButton, TopToolbar } from 'react-admin'; 3 | import RawButton from './RawButton'; 4 | 5 | const ResourceShowActions = ({ basePath, data, resource }) => ( 6 | 7 | {data ? : null} 8 | 9 | 10 | ); 11 | 12 | export default ResourceShowActions; 13 | -------------------------------------------------------------------------------- /Development/src/components/UnsortableDatagrid.js: -------------------------------------------------------------------------------- 1 | import React, { Children, cloneElement, isValidElement } from 'react'; 2 | import { Datagrid } from 'react-admin'; 3 | 4 | export const UnsortableDatagrid = ({ children, ...props }) => ( 5 | 6 | {Children.map( 7 | children, 8 | child => 9 | isValidElement(child) && 10 | cloneElement(child, { sortable: false }) 11 | )} 12 | 13 | ); 14 | 15 | export default UnsortableDatagrid; 16 | -------------------------------------------------------------------------------- /Development/src/icons/Device.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SvgIcon } from '@material-ui/core'; 3 | 4 | const SvgDevice = props => ( 5 | 6 | 7 | 11 | 12 | 13 | 14 | ); 15 | 16 | export default SvgDevice; 17 | -------------------------------------------------------------------------------- /TestingFacade/Config.py: -------------------------------------------------------------------------------- 1 | # Port for Testing Facade to run on 2 | TESTING_FACADE_PORT = 5001 3 | # URL of nmos-js instance for testing 4 | NCUT_URL = "http://localhost:3000/#/" 5 | # URL of NMOS Testing Tool's mock registry 6 | MOCK_REGISTRY_URL = "http://127.0.0.1:5102/" 7 | # Browser to use for testing. Matching webdriver must be installed 8 | BROWSER = 'Chrome' 9 | # Use browser in headless mode. Set to False to have each test run in a visible window 10 | HEADLESS = True 11 | # Time in seconds to wait for elements to load 12 | WAIT_TIME = 5 13 | -------------------------------------------------------------------------------- /Development/src/components/ItemArrayField.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Typography } from '@material-ui/core'; 3 | import get from 'lodash/get'; 4 | 5 | const ItemArrayField = ({ className, record, source }) => ( 6 | <> 7 | {get(record, source, []).map((item, index) => ( 8 | 9 | {item} 10 | 11 | ))} 12 | 13 | ); 14 | ItemArrayField.defaultProps = { 15 | addLabel: true, 16 | }; 17 | 18 | export default ItemArrayField; 19 | -------------------------------------------------------------------------------- /Development/src/icons/Flow.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SvgIcon } from '@material-ui/core'; 3 | 4 | const SvgFlow = props => ( 5 | 6 | 7 | 11 | 12 | 13 | 14 | ); 15 | 16 | export default SvgFlow; 17 | -------------------------------------------------------------------------------- /is12-client/src/index.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | } 4 | 5 | body { 6 | margin: 0; 7 | font-family: ui-rounded, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 8 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 9 | sans-serif; 10 | -webkit-font-smoothing: antialiased; 11 | -moz-osx-font-smoothing: grayscale; 12 | background-color: rgba(0, 0, 0, 0.80); 13 | color: #f7f7f7; 14 | min-height: 100vh; 15 | } 16 | 17 | code { 18 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 19 | monospace; 20 | } 21 | -------------------------------------------------------------------------------- /Documents/Dependencies.md: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | 3 | The codebase utilizes a number of great open-source projects (licenses vary). 4 | 5 | - [React](https://reactjs.org) 6 | - [react-admin](https://github.com/marmelab/react-admin) 7 | 8 | ## Preparation 9 | 10 | Install the [yarn](https://yarnpkg.com) package manager for your platform. 11 | 12 | Then, to install the required packages, run `yarn install` in your terminal from the [Development](../Development) directory. 13 | 14 | # What Next? 15 | 16 | These [instructions](Getting-Started.md) explain how to build and deploy the nmos-js client itself. 17 | -------------------------------------------------------------------------------- /Development/src/components/RateField.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import get from 'lodash/get'; 3 | import { Typography } from '@material-ui/core'; 4 | 5 | const RateField = ({ record, source }) => { 6 | const rate = get(record, source); 7 | if (!rate) { 8 | return ; 9 | } 10 | return ( 11 | 12 | {rate.numerator} : {rate.denominator ? rate.denominator : 1} 13 | 14 | ); 15 | }; 16 | 17 | RateField.defaultProps = { 18 | addLabel: true, 19 | }; 20 | 21 | export default RateField; 22 | -------------------------------------------------------------------------------- /Development/src/icons/Stage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SvgIcon } from '@material-ui/core'; 3 | 4 | const SvgStage = props => ( 5 | 6 | 7 | 11 | 12 | 13 | 14 | ); 15 | 16 | export default SvgStage; 17 | -------------------------------------------------------------------------------- /Development/src/components/RawButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Button } from 'react-admin'; 3 | import JsonIcon from '../icons/JsonIcon'; 4 | import { resourceUrl } from '../dataProvider'; 5 | 6 | const RawButton = ({ record, resource }) => { 7 | const url = resourceUrl(resource, `/${record.id}`); 8 | return ( 9 | 17 | ); 18 | }; 19 | 20 | export default RawButton; 21 | -------------------------------------------------------------------------------- /Development/src/components/sanitizeRestProps.js: -------------------------------------------------------------------------------- 1 | import omit from 'lodash/omit'; 2 | 3 | const sanitizeRestProps = props => 4 | omit(props, [ 5 | 'addLabel', 6 | 'allowEmpty', 7 | 'basePath', 8 | 'cellClassName', 9 | 'className', 10 | 'formClassName', 11 | 'headerClassName', 12 | 'label', 13 | 'linkType', 14 | 'link', 15 | 'locale', 16 | 'record', 17 | 'resource', 18 | 'sortable', 19 | 'sortBy', 20 | 'source', 21 | 'textAlign', 22 | 'translateChoice', 23 | ]); 24 | 25 | export default sanitizeRestProps; 26 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | nmos-js: An NMOS Client in JavaScript 2 | Copyright (c) 2017 Sony Corporation. All Rights Reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. -------------------------------------------------------------------------------- /Development/src/icons/svgr-template.js: -------------------------------------------------------------------------------- 1 | function template( 2 | { template }, 3 | opts, 4 | { imports, componentName, props, jsx, exports } 5 | ) { 6 | jsx.openingElement.name.name = 'SvgIcon'; 7 | jsx.closingElement.name.name = 'SvgIcon'; 8 | jsx.openingElement.attributes.push({ 9 | type: 'JSXSpreadAttribute', 10 | argument: { 11 | type: 'Identifier', 12 | name: 'props', 13 | }, 14 | }); 15 | return template.ast` 16 | ${imports} 17 | import { SvgIcon } from '@material-ui/core'; 18 | const ${componentName} = (${props}) => ${jsx} 19 | ${exports} 20 | `; 21 | } 22 | module.exports = template; 23 | -------------------------------------------------------------------------------- /Development/src/components/HintTypography.js: -------------------------------------------------------------------------------- 1 | import { forwardRef } from 'react'; 2 | import { Typography, withStyles } from '@material-ui/core'; 3 | 4 | export const InlineTypography = forwardRef(({ style, ...props }, ref) => ( 5 | 6 | )); 7 | 8 | export const hintStyle = theme => ({ 9 | textDecorationLine: 'underline', 10 | textDecorationStyle: 'dotted', 11 | textDecorationColor: theme.palette.type === 'dark' ? '#696969' : '#c8c8c8', 12 | }); 13 | 14 | const HintTypography = withStyles(theme => ({ 15 | root: hintStyle(theme), 16 | }))(InlineTypography); 17 | 18 | export default HintTypography; 19 | -------------------------------------------------------------------------------- /Development/src/components/LinkChipField.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ChipField } from 'react-admin'; 3 | import get from 'lodash/get'; 4 | 5 | // most NMOS resources that we need to link to have a label property 6 | // which makes a sensible default source, but if it's empty, fall back 7 | // to the id property 8 | const LinkChipField = ({ 9 | record, 10 | source = 'label', 11 | transform = _ => _, 12 | ...props 13 | }) => ( 14 | 22 | ); 23 | 24 | export default LinkChipField; 25 | -------------------------------------------------------------------------------- /Sandbox/make_badges.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | output_dir=$1 3 | shift 4 | artifacts_dir=$1 5 | shift 6 | 7 | cd $output_dir 8 | 9 | for file in ${artifacts_dir}/badges/*.txt; do 10 | suite=`basename $file` 11 | suite="${suite%.*}" 12 | pass=true 13 | if ! grep "Pass" ${artifacts_dir}/badges/${suite}.txt > /dev/null; then 14 | pass=false 15 | break 16 | fi 17 | echo "$suite passed: $pass" 18 | if $pass; then 19 | curl -o ${suite}.svg https://img.shields.io/static/v1?label=${suite}\&message=Pass\&color=brightgreen || ( echo "error downloading badge"; exit 1 ) 20 | else 21 | curl -o ${suite}.svg https://img.shields.io/static/v1?label=${suite}\&message=Fail\&color=red || ( echo "error downloading badge"; exit 1 ) 22 | fi 23 | done 24 | -------------------------------------------------------------------------------- /Development/src/components/URLField.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Typography } from '@material-ui/core'; 3 | import get from 'lodash/get'; 4 | 5 | const URLField = ({ record, source }) => ( 6 | 12 | 18 | {get(record, source)} 19 | 20 | 21 | ); 22 | URLField.defaultProps = { 23 | addLabel: true, 24 | }; 25 | 26 | export default URLField; 27 | -------------------------------------------------------------------------------- /Development/src/components/ResourceTitle.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useResourceContext } from 'react-admin'; 3 | import inflection from 'inflection'; 4 | 5 | const ResourceTitle = ({ resourceName, recordLabel, record }) => { 6 | const resource = useResourceContext(); 7 | if (!resourceName) { 8 | resourceName = inflection.transform(resource, [ 9 | 'singularize', 10 | 'titleize', 11 | ]); 12 | } 13 | if (!recordLabel) { 14 | recordLabel = record.label || record.name || record.id; 15 | } 16 | return ( 17 | 18 | {resourceName} 19 | {recordLabel ? ': ' + recordLabel : ''} 20 | 21 | ); 22 | }; 23 | 24 | export default ResourceTitle; 25 | -------------------------------------------------------------------------------- /is12-client/src/backend/DeviceProvider.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * By David Patyk - 2022 - david.patyk@sony.com 3 | * Device Provider: 4 | * Fetches all valid NCA devices from the registry 5 | */ 6 | 7 | /** 8 | * It takes a registry address, checks if it's valid, then fetches the NCA devices from the registry 9 | * @param {string} registryAddress { IP:PORT } - The address of the registry you want to query 10 | * @returns A promised array of NCA device websockets 11 | * @example 12 | * getDevices("192.168.1.0:8080") 13 | * .then((deviceList) => {}) 14 | * .catch((err) => {}); 15 | */ 16 | async function getDevices(registryAddress: string): Promise { 17 | let newList: any[] = []; 18 | newList.push(registryAddress) 19 | return newList; 20 | } 21 | 22 | export default getDevices -------------------------------------------------------------------------------- /Development/src/components/useDebounce.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useMemo, useRef, useState } from 'react'; 2 | import debounce from 'lodash/debounce'; 3 | 4 | export const useDebouncedCallback = (callback, delay) => 5 | useMemo(() => debounce(callback, delay), [callback, delay]); 6 | 7 | const useDebounce = (value, delay) => { 8 | const previousValue = useRef(value); 9 | const [currentValue, setCurrentValue] = useState(value); 10 | const debouncedCallback = useDebouncedCallback( 11 | value => setCurrentValue(value), 12 | delay 13 | ); 14 | useEffect(() => { 15 | if (value !== previousValue.current) { 16 | debouncedCallback(value); 17 | previousValue.current = value; 18 | } 19 | }, [debouncedCallback, value]); 20 | return currentValue; 21 | }; 22 | 23 | export default useDebounce; 24 | -------------------------------------------------------------------------------- /is12-client/src/client/DevApp.css: -------------------------------------------------------------------------------- 1 | .tree_wrap { 2 | justify-self: center; 3 | width: 99vw; 4 | height: 90vh; 5 | border: 2px outset black; 6 | background-color: #333333; 7 | } 8 | 9 | .node__root > circle { 10 | fill: rgb(164, 1, 246); 11 | } 12 | 13 | .node__branch > circle { 14 | fill: rgb(99, 1, 246); 15 | } 16 | 17 | .node__leaf > circle { 18 | fill: rgb(102, 150, 255); 19 | } 20 | 21 | .rd3t-label { 22 | outline-style: groove; 23 | outline-color: rgba(148, 148, 148, 0.57); 24 | fill: #d9d9d9; 25 | } 26 | 27 | .rd3t-label > text { 28 | fill: #b0aeae; 29 | } 30 | 31 | .node__branch > g > text > tspan { 32 | text-anchor: middle; 33 | alignment-baseline: central; 34 | font-size: 0.8rem; 35 | } 36 | 37 | .rd3t-label > .rd3t-label__title { 38 | fill: #62c1ce; 39 | text-decoration: underline; 40 | } -------------------------------------------------------------------------------- /is12-client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 16 | 17 | 18 | NCA Control Client 19 | 20 | 21 | 22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /Development/src/components/ConnectionEditActions.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import { ShowButton, TopToolbar } from 'react-admin'; 4 | import { useTheme } from '@material-ui/styles'; 5 | 6 | export default function ConnectionEditActions({ basePath, id }) { 7 | const theme = useTheme(); 8 | return ( 9 | 20 | 25 | 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /Development/src/components/CollapseButton.js: -------------------------------------------------------------------------------- 1 | import { IconButton } from '@material-ui/core'; 2 | import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown'; 3 | import KeyboardArrowLeftIcon from '@material-ui/icons/KeyboardArrowLeft'; 4 | import KeyboardArrowRightIcon from '@material-ui/icons/KeyboardArrowRight'; 5 | import KeyboardArrowUpIcon from '@material-ui/icons/KeyboardArrowUp'; 6 | 7 | const CollapseButton = ({ 8 | onClick, 9 | isExpanded, 10 | direction = 'vertical', 11 | title, 12 | }) => ( 13 | 14 | {direction === 'horizontal' ? ( 15 | isExpanded ? ( 16 | 17 | ) : ( 18 | 19 | ) 20 | ) : isExpanded ? ( 21 | 22 | ) : ( 23 | 24 | )} 25 | 26 | ); 27 | 28 | export default CollapseButton; 29 | -------------------------------------------------------------------------------- /Development/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "extends": [ 4 | "react-app", 5 | "plugin:prettier/recommended", 6 | "prettier" 7 | ], 8 | "plugins": [ 9 | "@typescript-eslint", 10 | "import", 11 | "jsx-a11y", 12 | "prettier", 13 | "react", 14 | "react-hooks" 15 | ], 16 | "parserOptions": { 17 | "ecmaVersion": 6, 18 | "ecmaFeatures": { 19 | "jsx": true 20 | } 21 | }, 22 | "rules": { 23 | "eqeqeq": "off", 24 | "no-console": "off", 25 | "sort-imports": [ 26 | "error", 27 | { 28 | "ignoreCase": false, 29 | "ignoreDeclarationSort": true, 30 | "ignoreMemberSort": false, 31 | "memberSyntaxSortOrder": [ "none", "all", "multiple", "single" ] 32 | } 33 | ], 34 | "no-var": "warn", 35 | "eol-last": [ "error", "always" ], 36 | "no-use-before-define": "off", 37 | "prettier/prettier": "error" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Development/src/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "nmos-js", 3 | "about": "An NMOS Client", 4 | "palette": { 5 | "primary": { 6 | "main": "rgb(45,117,199)", 7 | "contrastText": "#fff" 8 | }, 9 | "secondary": { 10 | "main": "rgb(0,47,103)", 11 | "contrastText": "#fff" 12 | } 13 | }, 14 | "Query API": { 15 | }, 16 | "DNS-SD API": { 17 | }, 18 | "Logging API": { 19 | }, 20 | "RQL": { 21 | "value": true, 22 | "disabled": false 23 | }, 24 | "Paging Limit": { 25 | }, 26 | "Friendly Parameters": { 27 | "value": true 28 | }, 29 | "Connect Filter": { 30 | "value": { 31 | "$constraint_sets_active": null 32 | } 33 | }, 34 | "refresh time": { 35 | "value": 4 36 | }, 37 | "theme": { 38 | "value": { 39 | "type": "light" 40 | } 41 | }, 42 | "Auth Enabled": { 43 | "value": false 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Development/src/icons/JsonIcon.js: -------------------------------------------------------------------------------- 1 | let _interopRequireDefault = require('@babel/runtime/helpers/interopRequireDefault'); 2 | 3 | Object.defineProperty(exports, '__esModule', { 4 | value: true, 5 | }); 6 | exports.default = void 0; 7 | 8 | const _react = _interopRequireDefault(require('react')); 9 | 10 | const _createSvgIcon = _interopRequireDefault( 11 | require('@material-ui/icons/utils/createSvgIcon') 12 | ); 13 | 14 | const _default = (0, _createSvgIcon.default)( 15 | _react.default.createElement( 16 | 'g', 17 | null, 18 | _react.default.createElement( 19 | 'style', 20 | null, 21 | '.txt { font-size: 14px; font-family: monospace; }' 22 | ), 23 | _react.default.createElement( 24 | 'text', 25 | { 26 | x: 0, 27 | y: 15, 28 | className: 'txt', 29 | }, 30 | '{\u2026}' 31 | ) 32 | ), 33 | 'json' 34 | ); 35 | 36 | exports.default = _default; 37 | -------------------------------------------------------------------------------- /Development/src/components/MappingButton.js: -------------------------------------------------------------------------------- 1 | import { IconButton, withStyles } from '@material-ui/core'; 2 | import CheckCircleOutlineIcon from '@material-ui/icons/CheckCircleOutline'; 3 | import RadioButtonUncheckedIcon from '@material-ui/icons/RadioButtonUnchecked'; 4 | 5 | // de-emphasize the unchecked state 6 | const faded = { opacity: 0.3 }; 7 | 8 | const styles = { 9 | unchecked: faded, 10 | checked: {}, 11 | }; 12 | 13 | // filter out our classes to avoid the Material-UI console warning 14 | const MappingButton = ({ 15 | checked, 16 | classes: { 17 | checked: checkedClass, 18 | unchecked: uncheckedClass, 19 | ...inheritedClasses 20 | }, 21 | ...props 22 | }) => ( 23 | 28 | {checked ? : } 29 | 30 | ); 31 | 32 | export default withStyles(styles)(MappingButton); 33 | -------------------------------------------------------------------------------- /is12-client/README.md: -------------------------------------------------------------------------------- 1 | # IS-12 Device Model Browser 2 | 3 | *A prototype controller for use with IS-12 capable NMOS Nodes* 4 | 5 | ## Overview 6 | 7 | The program is a controller for IS-12 capable NMOS Nodes such as the [NMOS Device Control Mock](https://github.com/AMWA-TV/nmos-device-control-mock). 8 | 9 | 10 | 11 | ## Installation 12 | 13 | ```bash 14 | yarn [install] 15 | ``` 16 | 17 | ## Requirements 18 | 19 | *** 20 | - Node.js (install with chocolaty to get all dependencies) 21 | - Python 3.10+ 22 | 23 | 24 | 25 | ## Usage 26 | 27 | *** 28 | - On launch there is an edit box where the URL of the control protocol endpoint can be set. 29 | - For example, `ws://127.0.0.1:7002/x-nmos/ncp/v1.0` 30 | - Click the `CONNECT` button to connect to the control protocol endpoint. 31 | 32 | ### Running: 33 | 34 | - With program recompiling every time you save changes 35 | 36 | ```bash 37 | yarn start 38 | ``` 39 | 40 | - Alternatively, to simply build and run without effect from changes: 41 | 42 | ```bash 43 | yarn build-and-start 44 | ``` 45 | 46 | -------------------------------------------------------------------------------- /Development/src/components/labelize.js: -------------------------------------------------------------------------------- 1 | // some abbreviations used in IS-04 and IS-05 APIs 2 | // or the nmos-cpp Logging API 3 | const abbreviations = { 4 | api: 'API', 5 | caps: 'Capabilities', 6 | ext: '(Ext)', 7 | fec: 'FEC', 8 | fec1d: 'FEC1D', 9 | fec2d: 'FEC2D', 10 | href: 'Address', 11 | http: 'HTTP', 12 | id: 'ID', 13 | ip: 'IP', 14 | is: 'IS', 15 | mqtt: 'MQTT', 16 | ms: '(ms)', 17 | ptp: 'PTP', 18 | rest: 'REST', 19 | rtcp: 'RTCP', 20 | rtp: 'RTP', 21 | uri: 'URI', 22 | url: 'URL', 23 | ws: 'WebSocket', 24 | }; 25 | 26 | const labelize = source => { 27 | // '/meow.$purr.hiss_yowl_ms' => 'Meow Purr Hiss Yowl (ms)' 28 | const label = source 29 | .replace(/[ /$._]+/g, ' ') 30 | .trim() 31 | .replace( 32 | /\S+/g, 33 | word => 34 | abbreviations[word.toLowerCase()] || 35 | word.charAt(0).toUpperCase() + word.substr(1).toLowerCase() 36 | ); 37 | return label; 38 | }; 39 | 40 | export default labelize; 41 | -------------------------------------------------------------------------------- /Development/src/LoginButton.js: -------------------------------------------------------------------------------- 1 | // in src/LoginButton.js 2 | import * as React from 'react'; 3 | import { forwardRef } from 'react'; 4 | import { useLogin, useNotify } from 'react-admin'; 5 | import Button from '@material-ui/core/Button'; 6 | import AccountCircleIcon from '@material-ui/icons/AccountCircle'; 7 | 8 | const LoginButton = forwardRef((props, ref) => { 9 | const login = useLogin(); 10 | const notify = useNotify(); 11 | 12 | const handleClick = () => { 13 | login().catch(error => { 14 | notify( 15 | typeof error === 'string' 16 | ? error 17 | : 'Authentication error: ' + error.message, 18 | 'error' 19 | ); 20 | }); 21 | }; 22 | 23 | return ( 24 | 33 | ); 34 | }); 35 | 36 | export default LoginButton; 37 | -------------------------------------------------------------------------------- /Documents/Getting-Started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | The following instructions describe how to build and deploy this software. 4 | 5 | ## Preparation 6 | 7 | Set up the [external dependencies](Dependencies.md#preparation) before proceeding. 8 | 9 | Once packages are installed, run `yarn start` to launch the client on a development server at http://localhost:3000. 10 | 11 | To build for release, simply run `yarn build`. The output will be written to a build directory. Instructions on how to serve the build will be provided at this point. 12 | 13 | ESLint and Prettier can be run using `yarn lint`. If you wish to list the warnings without writing changes `yarn lint-check` can be used instead. 14 | 15 | ## Run as a Docker container 16 | 17 | At this point in time, a prebuilt container is not provided. Luckily it is easy to build it from source! 18 | You will need to have git installed as well as [Docker](https://docs.docker.com/install/). 19 | 20 | ```bash 21 | git clone https://github.com/sony/nmos-js 22 | cd nmos-js 23 | docker build -t nmos-js Development 24 | ``` 25 | 26 | Start the Docker container by binding nmos-js to an external port. 27 | 28 | ```bash 29 | docker run -d --name=nmos-js -p 80:80 nmos-js 30 | ``` 31 | -------------------------------------------------------------------------------- /Development/src/components/ListActions.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Button, CreateButton, TopToolbar } from 'react-admin'; 3 | import { useTheme } from '@material-ui/styles'; 4 | import JsonIcon from '../icons/JsonIcon'; 5 | 6 | const ListActions = ({ basePath, hasCreate, url }) => { 7 | const theme = useTheme(); 8 | return ( 9 | 20 | {url && ( 21 | 30 | )} 31 | {hasCreate && } 32 | 33 | ); 34 | }; 35 | 36 | export default ListActions; 37 | -------------------------------------------------------------------------------- /Development/src/icons/index.js: -------------------------------------------------------------------------------- 1 | import ActivateImmediateIcon from './ActivateImmediate'; 2 | import ActivateScheduledIcon from './ActivateScheduled'; 3 | import CancelScheduledActivationIcon from './CancelScheduledActivation'; 4 | import ConnectRegistryIcon from './ConnectRegistry'; 5 | import ContentCopyIcon from './ContentCopy'; 6 | import DeviceIcon from './Device'; 7 | import FlowIcon from './Flow'; 8 | import JsonIcon from './JsonIcon'; 9 | import NodeIcon from './Node'; 10 | import ReceiverIcon from './Receiver'; 11 | import RegistryIcon from './Registry'; 12 | import RegistryLogsIcon from './RegistryLogs'; 13 | import SenderIcon from './Sender'; 14 | import SourceIcon from './Source'; 15 | import StageIcon from './Stage'; 16 | import SubscriptionIcon from './Subscription'; 17 | 18 | // To optimise the SVG files and convert to the correct syntax: 19 | // svgo --enable=removeDimensions --disable=removeViewBox --pretty -f .\src\ -o .\optimised\ 20 | // npx @svgr/cli -d ./out/ --template ./svgr-template.js ./optimised/ 21 | 22 | export { 23 | ActivateImmediateIcon, 24 | ActivateScheduledIcon, 25 | CancelScheduledActivationIcon, 26 | ConnectRegistryIcon, 27 | ContentCopyIcon, 28 | DeviceIcon, 29 | FlowIcon, 30 | JsonIcon, 31 | NodeIcon, 32 | ReceiverIcon, 33 | RegistryIcon, 34 | RegistryLogsIcon, 35 | SenderIcon, 36 | SourceIcon, 37 | StageIcon, 38 | SubscriptionIcon, 39 | }; 40 | -------------------------------------------------------------------------------- /Development/src/components/TAIField.js: -------------------------------------------------------------------------------- 1 | import { Tooltip } from '@material-ui/core'; 2 | import HintTypography from './HintTypography'; 3 | import tai from 't-a-i'; 4 | import dayjs from 'dayjs'; 5 | import utc from 'dayjs/plugin/utc'; 6 | import get from 'lodash/get'; 7 | dayjs.extend(utc); 8 | 9 | const TAIField = ({ record, source, mode }) => ( 10 |
11 | 17 | 18 | {get(record, source)} 19 | 20 | 21 |
22 | ); 23 | 24 | const TAIConversion = (record, source, mode) => { 25 | try { 26 | const taiData = get(record, source).split(':', 2); 27 | if (!taiData[0] || !taiData[1]) return ''; 28 | const taiTimeMilliseconds = 1e3 * taiData[0] + taiData[1] / 1e6; 29 | if (get(record, mode) === 'activate_scheduled_relative') { 30 | return dayjs(taiTimeMilliseconds).utc().format('HH:mm:ss.SSS'); 31 | } 32 | const unixTimeMilliseconds = tai.atomicToUnix(taiTimeMilliseconds); 33 | return dayjs(unixTimeMilliseconds).format('YYYY-MM-DD HH:mm:ss.SSS Z'); 34 | } catch (e) { 35 | return `Conversion Error - ${e}`; 36 | } 37 | }; 38 | 39 | TAIField.defaultProps = { 40 | addLabel: true, 41 | }; 42 | 43 | export default TAIField; 44 | -------------------------------------------------------------------------------- /Development/src/components/PaginationButtons.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | ChevronLeft, 4 | ChevronRight, 5 | FirstPage, 6 | LastPage, 7 | } from '@material-ui/icons'; 8 | import { Button } from '@material-ui/core'; 9 | import { includes, keys } from 'lodash'; 10 | 11 | const components = { 12 | prev: ChevronLeft, 13 | next: ChevronRight, 14 | last: LastPage, 15 | first: FirstPage, 16 | }; 17 | 18 | export const PaginationButton = ({ 19 | pagination, 20 | disabled, 21 | nextPage, 22 | rel, 23 | label = rel, 24 | }) => { 25 | rel.toLowerCase(); 26 | const buttons = keys(pagination); 27 | 28 | const enabled = (() => { 29 | if (disabled) return false; 30 | return includes(buttons, rel); 31 | })(); 32 | 33 | const getIcon = label => { 34 | const ButtonIcon = components[label]; 35 | return ; 36 | }; 37 | 38 | return ( 39 | 43 | ); 44 | }; 45 | 46 | const PaginationButtons = props => ( 47 | <> 48 | 49 |