UNPKG

awv3

Version:
231 lines (215 loc) 9.99 kB
import without from 'lodash/without'; import { apply_patch } from 'jsonpatch'; import { mixin } from '../lifecycle'; import { buildFeaturePath, normalizeName } from '../helpers'; import { createContext } from '../../core/parser'; import Parser from '../../core/parser'; import { actions as globalActions } from './globals'; const scope = 'connections'; const parser = new Parser(); export const base = mixin(scope, connection); export const types = { ...base.types, init: `${scope}/init`, setTree: `${scope}/setTree`, destroyTree: `${scope}/destroyTree`, patchTree: `${scope}/patchTree`, request: `${scope}/request`, message: `${scope}/message`, recalc: `${scope}/recalc`, setName: `${scope}/setName`, setColor: `${scope}/setColor`, setActiveFeature: `${scope}/setActiveFeature`, setDefaultFeatureVisibility: `${scope}/setDefaultFeatureVisibility`, linkPlugins: `${scope}/linkPlugins`, unlinkPlugins: `${scope}/unlinkPlugins`, disableAllPlugins: `${scope}/disableAllPlugins`, updateTree: `${scope}/updateTree` }; export const actions = { // Lifecycle actions ...base.actions, // Basic Redux actions setTree: (id, tree) => ({ type: types.setTree, id, tree }), destroyTree: id => ({ type: types.destroyTree, id }), patchTree: (id, patches) => ({ type: types.patchTree, id, patches }), message: (id, type, message, reset) => ({ type: types.message, id, type, message, reset }), setActiveFeature: (id, feature = undefined) => ({ type: types.setActiveFeature, id, feature }), setDefaultFeatureVisibility: (id, visible) => ({ type: types.setDefaultFeatureVisibility, id, visible }), linkPlugins: (id, plugins) => ({ type: types.linkPlugins, id, plugins }), unlinkPlugins: (id, plugins) => ({ type: types.unlinkPlugins, id, plugins }), updateTree: (id, node, attributes) => ({ type: types.updateTree, id, node, attributes }), // Redux thunks connect: (id, url, protocol) => async dispatch => { const connection = base.references[id]; connection.socket = await new protocol().connect(url); await dispatch(actions.init(id)); connection.emit('connected', connection); }, init: id => async dispatch => { const connection = base.references[id]; await connection.socket.execute('CADH_SetVar("CC_ProductRefCreation",3);'); const context = await connection.socket.request({ command: 'GetTree' }); dispatch({ type: types.setTree, id, tree: context.firstResult }); dispatch(actions.message(id, 'success', `Connected to ${connection.socket.url}`)); return context; }, request: (id, command, factory) => dispatch => { const connection = base.references[id]; const task = patchContext(dispatch, connection, factory); dispatch(globalActions.beginTask(task.id, command)); // Make request right away as to not waste time const serverCall = connection.socket.request(command, task); // Results will be handled in queued order instead connection.sequence = connection.sequence.then(() => serverCall.then(context => { // If context contains patches, run them context.patches && dispatch(actions.patchTree(id, context.patches)); // Error will be collected, too context.errors && context.errors.forEach(({ errorMessage }) => dispatch(actions.message(id, 'error', errorMessage))); // Mark task as finished and return full context dispatch(globalActions.finishTask(task.id)); return context; })); // Return promise return connection.sequence; }, stream: (id, url, factory) => async dispatch => { const connection = base.references[id]; const context = await parser.stream(url, patchContext(dispatch, connection, factory)); context.patches && dispatch(actions.patchTree(id, context.patches)); return context; }, parse: (id, blob, factory) => async dispatch => { const connection = base.references[id]; const context = await parser.parse(blob, patchContext(dispatch, connection, factory)); context.patches && dispatch(actions.patchTree(id, context.patches)); return context; }, execute: (id, command) => dispatch => base.references[id].execute(command), exportStep: (id, path) => dispatch => base.references[id].execute(`_C.CADApplication.ExportStep("${path}");`), exportOf1: (id, path) => dispatch => base.references[id].execute(`_C.CADApplication.ExportOf1("${path}");`), recalc: id => dispatch => base.references[id].execute(`_C.GlobaleFunktionen.UseOnStartRecalc(_O);`), setColor: (id, ccid, rgb) => dispatch => base.references[id].execute(`_C.CADApplication.SetColor(CADH_RealToId(${ccid}),${rgb.join(',')});`), setName: (id, ccid, name) => dispatch => { const connection = base.references[id]; return connection.execute( `${buildFeaturePath(connection.tree, ccid)}.OBJ_ChangeName("${normalizeName(name)}");` ); }, newPart: id => dispatch => { dispatch(actions.setActiveFeature(id, undefined)); dispatch(actions.setDefaultFeatureVisibility(id, true)); return base.references[id].execute(`_C.CADApplication.NewPart();`); }, readStream: (id, ccid, data, format = 'step') => dispatch => { const connection = base.references[id]; return connection.request({ command: 'Execute', task: `${buildFeaturePath(connection.tree, ccid)}.ReadStream("data", VOID, "${format}");`, streamData: { data } }); }, load: (id, data) => dispatch => { dispatch(actions.setActiveFeature(id, undefined)); dispatch(actions.setDefaultFeatureVisibility(id, false)); return base.references[id].request({ command: 'Execute', task: `CADH_SetVertexFilter(TRUE); _C.CADApplication.LoadPart("data");`, streamData: { data } }); } }; function connection(state, { type, ...payload }) { switch (type) { case types.setTree: return { ...state, tree: payload.tree }; case types.destroyTree: return { ...state, tree: { '1': { id: 1, name: '', class: '', parent: null }, root: 1 } }; case types.patchTree: return { ...state, tree: apply_patch(state.tree, payload.patches) }; case types.message: return { ...state, messages: [...state.messages, { type: payload.type, message: payload.message, reset: payload.reset }] }; case types.setActiveFeature: return { ...state, activeFeature: payload.feature }; case types.setDefaultFeatureVisibility: return { ...state, defaultFeatureVisibility: payload.visible }; case types.linkPlugins: return { ...state, plugins: [...state.plugins, ...(Array.isArray(payload.plugins) ? payload.plugins : [payload.plugins])] }; case types.unlinkPlugins: return { ...state, plugins: without( state.plugins, ...(Array.isArray(payload.plugins) ? payload.plugins : [payload.plugins]) ) }; case types.updateTree: return { ...state, tree: { ...state.tree, [payload.node]: { ...state.tree[payload.node], ...payload.attributes } } }; default: return state; } } function patchContext(dispatch, connection, factory = {}) { let { session, primitives, waiting, pool } = connection; let { options } = createContext(factory); return { ...options, ...session.options, session, callback(args) { if (args.type === 'Model') { session.options.updateMaterials && args.model.updateMaterials(); session.options.centerGeometry && args.model.centerGeometry(); let id = args.model.userData.id; let previous = primitives[id]; if (previous) { // TODO: Maybe it would be better to cause actions here // Primitive is already known and needs to be replaced let references = previous.references.slice(); previous.destroy(); primitives[id] = args.model; args.model.references = []; references.forEach(clone => { // Get clones parent let parent = clone.parent; // Destroy clone clone.destroyAsync(); // Add new clone to old clones parent if (parent) { // Create new clone and reference it let newClone = args.model.clone(); parent.addAsync(newClone); // Reference new clone args.model.references.push(newClone); } }); } else { // New primitive primitives[id] = args.model; args.model.references = []; if (waiting[id]) { waiting[id].forEach(callback => callback(args.model)); delete waiting[id]; connection.updateView(); } } } // Call original callback options.callback(args); // Call session callback session.options.callback && session.options.callback(args); } }; } export const reducer = base.reducer;