awv3
Version:
⚡ AWV3 embedded CAD
231 lines (215 loc) • 9.99 kB
JavaScript
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;