718 lines
24 KiB
JavaScript
718 lines
24 KiB
JavaScript
import Vue from 'vue';
|
|
import { TrainingMode } from '@/scripts/ConstDic';
|
|
import OperateHandler from '@/scripts/plugin/OperateHandler';
|
|
import deviceType from '../../jmap/constant/deviceType';
|
|
import LangStorage from '@/utils/lang';
|
|
import Handler from '@/scripts/cmdPlugin/Handler';
|
|
import { deepAssign } from '@/utils/index';
|
|
|
|
/**
|
|
* 实训状态数据
|
|
*/
|
|
|
|
const training = {
|
|
namespaced: true,
|
|
|
|
state: {
|
|
mode: TrainingMode.NORMAL, // 模式 (教学系统中教学、练习、测验模式)
|
|
operatemode: null, // 操作模式 管理员(故障、指令、普通)
|
|
started: false, // 是否开始
|
|
switchcount: 0, // 开关标识
|
|
basicInfo: {}, // 实训基本信息数据
|
|
offsetStationCode: '', // 偏移到车站
|
|
rezoomCount: 0, // 车站变更标识
|
|
steps: [], // 实训步骤数据
|
|
order: -1, // 实训进行到第几步
|
|
orderCount: 0, // 步骤变更标识
|
|
operateErrMsg: '', // 操作错误提示信息
|
|
subscribeCount: 0, // 仿真订阅完成标识
|
|
score: 0, // 实训得分
|
|
usedTime: 0, // 实训所需时间
|
|
timeInterval: null, // 计时器
|
|
tipEvent: 0, // 提示刷新检测
|
|
operate: {}, // 操作model,
|
|
initTime: 0, // 当前系统时间
|
|
prdType: '', // 产品类型
|
|
roles: '', // 角色权限类型
|
|
roleDeviceCode:'', // 当前角色对应的deviceCode
|
|
group: '', // 设置全局 group
|
|
centerStationCode:'', // 当前居中的集中站code
|
|
memberList: [], // 综合仿真成员列表
|
|
memberData: {}, // 综合仿真成员列表
|
|
simulationUserList: [] // 综合仿真用户列表
|
|
},
|
|
|
|
getters: {
|
|
steps: (state) => {
|
|
return state.steps;
|
|
},
|
|
order: (state) => {
|
|
return state.order;
|
|
},
|
|
offsetStationCode: (state) => {
|
|
return state.offsetStationCode;
|
|
},
|
|
basicInfo: (state) => {
|
|
return state.basicInfo;
|
|
},
|
|
mode: (state) => {
|
|
return state.mode;
|
|
},
|
|
operatemode: (state) => {
|
|
return state.operatemode;
|
|
},
|
|
started: (state) => {
|
|
return state.started;
|
|
},
|
|
score: (state) => {
|
|
return state.score;
|
|
},
|
|
usedTime: (state) => {
|
|
return state.usedTime;
|
|
},
|
|
tipEvent: (state) => {
|
|
return state.tipEvent;
|
|
},
|
|
initTime: (state) => {
|
|
return state.initTime;
|
|
},
|
|
prdType: (state) => {
|
|
return state.prdType;
|
|
},
|
|
roles: (state) => {
|
|
return state.roles;
|
|
},
|
|
memberList: (state) => {
|
|
return state.memberList;
|
|
},
|
|
simulationUserList: (state) => {
|
|
return state.simulationUserList;
|
|
},
|
|
// 视图中的列车列表
|
|
viewTrainList: (state) => () =>{
|
|
const trainList = [];
|
|
const mapDevice = Vue.prototype.$jlmap.mapDevice;
|
|
Object.values(mapDevice).forEach(device => {
|
|
if (device && device._type === deviceType.Train) {
|
|
trainList.push(device);
|
|
}
|
|
});
|
|
|
|
return trainList;
|
|
},
|
|
commonMenuStep: (state) => {
|
|
return state.commonMenuStep;
|
|
}
|
|
},
|
|
|
|
mutations: {
|
|
changeMode: (state, mode) => {
|
|
state.mode = mode;
|
|
},
|
|
changeOperateMode: (state, mode) => {
|
|
state.operatemode = mode;
|
|
},
|
|
start: (state) => {
|
|
state.started = true;
|
|
state.switchcount += 1;
|
|
},
|
|
over: (state) => {
|
|
state.started = false;
|
|
state.switchcount += 1;
|
|
},
|
|
updateMapState: (state, deviceStatus) => {
|
|
Vue.prototype.$jlmap && Vue.prototype.$jlmap.update(deviceStatus);
|
|
},
|
|
setMapDefaultState: (state) =>{
|
|
Vue.prototype.$jlmap && Vue.prototype.$jlmap.setDefaultState();
|
|
},
|
|
setBasicInfo: (state, basicInfo) => {
|
|
state.basicInfo = basicInfo;
|
|
},
|
|
setOffsetStationCode: (state, offsetStationCode) => {
|
|
state.offsetStationCode = offsetStationCode || null;
|
|
state.rezoomCount += 1;
|
|
},
|
|
setSteps: (state, steps) => {
|
|
state.steps = steps;
|
|
},
|
|
addStep: (state, step) => {
|
|
state.steps.push(step);
|
|
},
|
|
next: (state) => {
|
|
state.order += 1;
|
|
state.orderCount += 1;
|
|
},
|
|
orderCountIncrement: (state) => {
|
|
state.orderCount += 1;
|
|
},
|
|
resetOrder: (state) => {
|
|
state.order = -1;
|
|
},
|
|
backOrder: (state, stepNum) => {
|
|
if (state.order > stepNum) {
|
|
state.order -= stepNum;
|
|
state.orderCount += 1;
|
|
}
|
|
},
|
|
setOperateErrMsg: (state, errMsg) => {
|
|
state.operateErrMsg = errMsg;
|
|
},
|
|
|
|
setHasSubscribed: (state) => {
|
|
state.subscribeCount++;
|
|
},
|
|
|
|
setScore: (state, score) => {
|
|
state.score = score;
|
|
},
|
|
resetScore: (state) => {
|
|
state.score = '';
|
|
},
|
|
tipEventIncrement: (state) => {
|
|
state.tipEvent++;
|
|
},
|
|
resetUsedTime: (state) => {
|
|
state.usedTime = 0;
|
|
},
|
|
countTime: (state, type) => {
|
|
if (state.timeInterval) {
|
|
clearInterval(state.timeInterval);
|
|
state.timeInterval = null;
|
|
}
|
|
if (type == 'Lesson') {
|
|
state.timeInterval = setInterval(() => {
|
|
state.usedTime++;
|
|
}, 1000);
|
|
} else {
|
|
state.timeInterval = setInterval(() => {
|
|
state.initTime += 1000;
|
|
}, 1000);
|
|
}
|
|
},
|
|
stopCountTime: (state) => {
|
|
if (state.timeInterval) {
|
|
clearInterval(state.timeInterval);
|
|
state.timeInterval = null;
|
|
}
|
|
},
|
|
setOperate: (state, operate) => {
|
|
state.operate = operate;
|
|
},
|
|
setInitTime: (state, initTime) => {
|
|
state.initTime = initTime;
|
|
},
|
|
setPrdType: (state, prdType) => {
|
|
state.prdType = prdType;
|
|
},
|
|
setRoles: (state, roles) => {
|
|
state.roles = roles;
|
|
},
|
|
setGroup: (state, group) => {
|
|
state.group = group;
|
|
},
|
|
notifyGetCommandDefinition: (state, lineCode) => {
|
|
Handler.notify(lineCode);
|
|
},
|
|
setCenterStationCode:(state, centerStationCode) => {
|
|
state.centerStationCode = centerStationCode;
|
|
},
|
|
setMemberList: (state, {memberList, userId}) => {
|
|
state.memberList = memberList;
|
|
state.memberData = {};
|
|
if (memberList && memberList.length) {
|
|
memberList.forEach(item => {
|
|
state.memberData[item.id] = deepAssign({userId: ''}, item);
|
|
state.memberData[item.id].disabled = userId == item.userId;
|
|
});
|
|
}
|
|
},
|
|
setSimulationUserList: (state, simulationUserList) => {
|
|
state.simulationUserList = simulationUserList;
|
|
},
|
|
updateMemberTrust:(state, {deviceCode, trust}) => {
|
|
for (const k in state.memberData) {
|
|
if (state.memberData[k].deviceCode == deviceCode) {
|
|
state.memberData[k].trust = trust;
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
updateMemberListInScript:(state, {oldMemberId, newMember, userId, name}) => {
|
|
if (oldMemberId) {
|
|
// 重置旧数据
|
|
delete state.memberData[oldMemberId].userId;
|
|
delete state.memberData[oldMemberId].name;
|
|
state.memberData[oldMemberId].disabled = false;
|
|
}
|
|
if (!state.memberData[newMember.id]) {
|
|
state.memberData[newMember.id] = newMember;
|
|
}
|
|
// 更新新数据
|
|
state.memberData[newMember.id].userId = userId;
|
|
state.memberData[newMember.id].name = name;
|
|
state.memberData[newMember.id].disabled = true;
|
|
|
|
},
|
|
|
|
updateMemberAndUser: (state, {simulationUserList, userId}) => {
|
|
simulationUserList.forEach(item => {
|
|
let memberIndex = -1;
|
|
let memberId = '';
|
|
state.simulationUserList.forEach((user, index) =>{
|
|
if (user.userId === item.userId) {
|
|
memberId = user.memberId;
|
|
user.online = item.online;
|
|
user.memberId = item.memberId;
|
|
user.role = item.role;
|
|
memberIndex = index;
|
|
}
|
|
});
|
|
try {
|
|
if (state.memberData && memberId !== item.memberId && memberId && state.memberData[memberId]) {
|
|
state.memberData[memberId].userId = '';
|
|
state.memberData[memberId].disabled = false;
|
|
}
|
|
if (state.memberData && memberId !== item.memberId && item.memberId && state.memberData[item.memberId]) {
|
|
state.memberData[item.memberId].userId = item.userId;
|
|
state.memberData[item.memberId].disabled = item.userId == userId;
|
|
}
|
|
if (item.messageType === 'ENTER' && memberIndex < 0) {
|
|
delete item.messageType;
|
|
state.simulationUserList.push(item);
|
|
} else if (item.messageType === 'KICK_OUT' && memberIndex > -1) {
|
|
state.simulationUserList.splice(memberIndex, 1);
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
});
|
|
},
|
|
setRoleDeviceCode :(state, roleDeviceCode) => {
|
|
state.roleDeviceCode = roleDeviceCode;
|
|
}
|
|
},
|
|
|
|
actions: {
|
|
/**
|
|
* 清除仿真所在组
|
|
*/
|
|
clearSimulationGroup: ({ commit }, type) => {
|
|
commit('clearSimulationGroup', type);
|
|
},
|
|
/**
|
|
* 设置socke已经连接
|
|
*/
|
|
setHasSubscribed: ({ commit }) => {
|
|
commit('setHasSubscribed');
|
|
},
|
|
/**
|
|
* 模式变更
|
|
*/
|
|
changeMode: ({ commit }, opts) => {
|
|
commit('changeMode', opts.mode);
|
|
},
|
|
|
|
/**
|
|
* 操作模式变更
|
|
*/
|
|
changeOperateMode: ({ commit }, opts) => {
|
|
commit('changeOperateMode', opts.mode);
|
|
},
|
|
|
|
/**
|
|
* 重置实训状态
|
|
*/
|
|
reset: ({ commit }) => {
|
|
commit('over');
|
|
commit('resetOrder');
|
|
// commit('stopCountTime');
|
|
|
|
// 清空计时器以及得分
|
|
commit('resetUsedTime');
|
|
commit('resetScore');
|
|
// 设置其他属性状态
|
|
commit('setOffsetStationCode', null);
|
|
},
|
|
|
|
/**
|
|
* 开始
|
|
*/
|
|
start: ({ commit }) => {
|
|
// 清空操作组
|
|
OperateHandler.cleanOperates();
|
|
Handler.clear(); // 两种方式
|
|
// 清空计时器以及得分
|
|
// commit('stopCountTime');
|
|
commit('resetUsedTime');
|
|
commit('resetScore');
|
|
commit('start');
|
|
},
|
|
|
|
/**
|
|
* 结束
|
|
*/
|
|
over: ({ commit }) => {
|
|
commit('over');
|
|
commit('resetOrder');
|
|
// commit('stopCountTime');
|
|
},
|
|
|
|
/**
|
|
* step步骤是否结束
|
|
*/
|
|
isStepOver: ({ state }) => {
|
|
if (state.order >= state.steps.length) {
|
|
return true;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 停止计时
|
|
*/
|
|
setStopCountTime: ({ commit }) => {
|
|
commit('stopCountTime');
|
|
},
|
|
|
|
/**
|
|
* 主动判断是否结束
|
|
*/
|
|
judgeFinish: ({ dispatch, commit, state }, rtn) => {
|
|
if (state.started) {
|
|
if (state.order >= state.steps.length) {
|
|
if (rtn && rtn.valid) {
|
|
commit('next');
|
|
commit('setOperateErrMsg', { errMsg: LangStorage.getLang() == 'en' ? 'Correct operation! Training is over!' : '操作正确!实训结束!', color: 'green' });
|
|
} else {
|
|
commit('setOperateErrMsg', { errMsg: LangStorage.getLang() == 'en' ? 'Operation error! Training is over!' : '操作错误!实训结束!', color: 'red' });
|
|
}
|
|
dispatch('over');
|
|
dispatch('changeMode', { mode: null });
|
|
if (rtn.hasOwnProperty('score')) {
|
|
commit('setScore', rtn.score || 0);
|
|
}
|
|
} else {
|
|
rtn && rtn.valid && commit('next');
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 开始实训(模式变更和开始的混合操作)
|
|
*/
|
|
startTraining: ({ commit }, opts) => {
|
|
commit('resetOrder');
|
|
commit('changeMode', opts.mode);
|
|
if (opts.start) {
|
|
// 清空计时器以及得分
|
|
// commit('stopCountTime');
|
|
commit('resetUsedTime');
|
|
commit('resetScore');
|
|
// 开始实训
|
|
commit('start');
|
|
commit('next');
|
|
// // 开始计时
|
|
// commit('countUsedTime');
|
|
}
|
|
},
|
|
// 计时(教学正计时与仿真当前时间)
|
|
countTime({ commit }, type) {
|
|
commit('countTime', type);
|
|
},
|
|
|
|
/**
|
|
* 开始实训(模式变更和开始的混合操作)
|
|
*/
|
|
backSteps: ({ commit }, stepNum) => {
|
|
if (Number.isInteger) {
|
|
commit('backOrder', stepNum);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 下一步
|
|
*/
|
|
next: ({ commit, state }, operate) => {
|
|
commit('setOperate', operate);
|
|
return new Promise((resolve, reject) => {
|
|
if (!state.started && !state.mode) {
|
|
commit('setOperateErrMsg', { errMsg: LangStorage.getLang() == 'en' ? 'Please click start, start training!' : '请点击开始,开始实训!', color: 'red' });
|
|
return;
|
|
}
|
|
|
|
// 处理operation
|
|
OperateHandler.handle(operate).then(rtn => {
|
|
if (state.started) {
|
|
// 教学和联系模式需要给出过程步骤提示
|
|
if (TrainingMode.TEACH === state.mode || TrainingMode.PRACTICE === state.mode) {
|
|
if (rtn && rtn.valid) {
|
|
commit('next');
|
|
commit('setOperateErrMsg', { errMsg: LangStorage.getLang() == 'en' ? "Correct operation! That's great!" : '操作正确!真棒!', color: 'green' });
|
|
commit('tipEventIncrement');
|
|
} else {
|
|
if (!operate.repeat) {
|
|
commit('setOperateErrMsg', { errMsg: LangStorage.getLang() == 'en' ? 'operation mistake!' : '操作错误!', color: 'red' });
|
|
}
|
|
}
|
|
} else if (TrainingMode.EXAM === state.mode || TrainingMode.TEST === state.mode) {
|
|
// 测试和考试不给提示
|
|
rtn && rtn.valid && commit('next');
|
|
}
|
|
}
|
|
resolve(rtn);
|
|
}).catch(error => {
|
|
reject(error);
|
|
});
|
|
});
|
|
},
|
|
|
|
/**
|
|
* 下一步
|
|
*/
|
|
nextNew: ({ commit, state }, operate) => {
|
|
return new Promise((resolve, reject) => {
|
|
commit('setOperate', operate);
|
|
if (!state.started && !state.mode) {
|
|
commit('setOperateErrMsg', { errMsg: LangStorage.getLang() == 'en' ? 'Please click start, start training!' : '请点击开始,开始实训!', color: 'red' });
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// 处理operation
|
|
Handler.handle(operate).then(rtn => {
|
|
if (state.started) {
|
|
// 教学和联系模式需要给出过程步骤提示
|
|
if (TrainingMode.TEACH === state.mode || TrainingMode.PRACTICE === state.mode) {
|
|
if (rtn && rtn.valid) {
|
|
commit('next');
|
|
commit('setOperateErrMsg', { errMsg: LangStorage.getLang() == 'en' ? "Correct operation! That's great!" : '操作正确!真棒!', color: 'green' });
|
|
commit('tipEventIncrement');
|
|
} else {
|
|
if (!operate.repeat) {
|
|
commit('setOperateErrMsg', { errMsg: LangStorage.getLang() == 'en' ? 'operation mistake!' : '操作错误!', color: 'red' });
|
|
}
|
|
}
|
|
} else if (TrainingMode.EXAM === state.mode || TrainingMode.TEST === state.mode) {
|
|
// 测试和考试不给提示
|
|
rtn && rtn.valid && commit('next');
|
|
}
|
|
}
|
|
resolve(rtn);
|
|
}).catch(error => {
|
|
console.error(error);
|
|
reject(error);
|
|
});
|
|
} catch (error) {
|
|
console.error(error);
|
|
reject(error);
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* 提示消息重新加载
|
|
*/
|
|
tipReload: ({ commit }) => {
|
|
commit('orderCountIncrement');
|
|
},
|
|
|
|
/**
|
|
* 更新偏移位置车站
|
|
*/
|
|
updateOffsetStationCode: ({ commit }, payLoad) => {
|
|
commit('setOffsetStationCode', payLoad.offsetStationCode);
|
|
},
|
|
|
|
/**
|
|
* 设置教学相关数据
|
|
*/
|
|
setTrainingData: ({ commit }, trainingData) => {
|
|
const basicInfo = {
|
|
id: trainingData.id,
|
|
name: trainingData.name,
|
|
remarks: trainingData.remarks,
|
|
prdType: trainingData.prdType,
|
|
minDuration: trainingData.minDuration,
|
|
maxDuration: trainingData.maxDuration
|
|
};
|
|
commit('setBasicInfo', basicInfo);
|
|
const steps = trainingData.steps;
|
|
commit('setSteps', steps);
|
|
const offsetStationCode = trainingData.locateDeviceCode;
|
|
commit('setOffsetStationCode', offsetStationCode);
|
|
commit('setMapDefaultState');
|
|
},
|
|
|
|
/**
|
|
* 设置步骤数据
|
|
*/
|
|
setSteps: ({ commit }, steps) => {
|
|
commit('setSteps', steps);
|
|
},
|
|
|
|
/**
|
|
* 添加步骤数据
|
|
*/
|
|
addStep: ({ state, commit }, step) => {
|
|
return new Promise((resolve, reject) => {
|
|
try {
|
|
var valid = true;
|
|
const steps = state.steps;
|
|
if (steps && steps.length > 0) {
|
|
const last = steps.length - 1;
|
|
if (steps[last].type === step.type &&
|
|
steps[last].code === step.code &&
|
|
steps[last].operation === step.operation) {
|
|
steps.splice(last, 1);
|
|
step.order = step.order - 1;
|
|
valid = false;
|
|
}
|
|
}
|
|
commit('addStep', step);
|
|
resolve(valid);
|
|
} catch (error) {
|
|
reject(error);
|
|
}
|
|
});
|
|
},
|
|
/**
|
|
* 设置地图默认状态
|
|
*/
|
|
setMapDefaultState: ({ commit }) => {
|
|
commit('setMapDefaultState');
|
|
},
|
|
|
|
/**
|
|
* 更新地图设备状态数据
|
|
*/
|
|
updateMapState: ({ commit }, deviceStatus) => {
|
|
commit('updateMapState', deviceStatus);
|
|
},
|
|
|
|
/**
|
|
* 开始教学模式
|
|
*/
|
|
teachModeStart: ({ dispatch }) => {
|
|
const payLoad = { start: true, mode: TrainingMode.TEACH };
|
|
dispatch('startTraining', payLoad);
|
|
},
|
|
|
|
/**
|
|
* 开始考试模式
|
|
*/
|
|
examModeStart: ({ dispatch }) => {
|
|
const payLoad = { start: true, mode: TrainingMode.EXAM };
|
|
dispatch('startTraining', payLoad);
|
|
},
|
|
|
|
/**
|
|
* 仿真和大屏和计划模式
|
|
*/
|
|
simulationStart: ({ dispatch }) => {
|
|
const payLoad = { start: true, mode: TrainingMode.NORMAL };
|
|
dispatch('startTraining', payLoad);
|
|
},
|
|
/**
|
|
* 结束模式
|
|
*/
|
|
end: ({ commit }, mode) => {
|
|
commit('over');
|
|
commit('resetOrder');
|
|
// commit('stopCountTime');
|
|
commit('changeMode', mode);
|
|
},
|
|
|
|
/**
|
|
* 是否教学模式
|
|
*/
|
|
isTeachMode: ({ state }) => {
|
|
return new Promise((resolve, reject) => {
|
|
if (state.mode === TrainingMode.TEACH) {
|
|
resolve();
|
|
} else {
|
|
reject(new Error('not teach mode'));
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* 设置用户得分
|
|
*/
|
|
setScore: ({ commit }, score) => {
|
|
commit('setScore', score);
|
|
},
|
|
|
|
/**
|
|
* 设置tip变化事件
|
|
*/
|
|
emitTipFresh: ({ commit }) => {
|
|
commit('tipEventIncrement');
|
|
},
|
|
|
|
/**
|
|
* 设置WebSocket链接状态
|
|
*/
|
|
setConnected: ({ commit }, isConnected) => {
|
|
commit('setConnected', isConnected);
|
|
},
|
|
|
|
/**
|
|
* 设置系统时间
|
|
*/
|
|
setInitTime: ({ commit }, initTime) => {
|
|
commit('setInitTime', initTime);
|
|
},
|
|
|
|
/**
|
|
* 设置产品类型
|
|
*/
|
|
setPrdType: ({ commit }, prdType) => {
|
|
commit('setPrdType', prdType);
|
|
},
|
|
|
|
/**
|
|
* 设置角色列表
|
|
*/
|
|
setRoles: ({ commit }, roles) => {
|
|
commit('setRoles', roles);
|
|
},
|
|
|
|
/**
|
|
* 设置仿真组
|
|
*/
|
|
setGroup: ({ commit }, group) => {
|
|
commit('setGroup', group);
|
|
},
|
|
|
|
/** 设置当前居中的集中站code */
|
|
setCenterStationCode:({ commit }, centerStationCode) => {
|
|
commit('setCenterStationCode', centerStationCode);
|
|
},
|
|
/** 设置综合演练仿真成员列表 */
|
|
setMemberList: ({ commit }, data) => {
|
|
commit('setMemberList', data);
|
|
},
|
|
/** 设置综合演练仿真用户列表 */
|
|
setSimulationUserList: ({ commit }, simulationUserList) => {
|
|
commit('setSimulationUserList', simulationUserList);
|
|
},
|
|
updateMemberTrust:({ commit }, data) => {
|
|
commit('updateMemberTrust', data);
|
|
},
|
|
/** 更新综合演练仿真成员和用户列表 */
|
|
updateMemberAndUser: ({ commit }, data) => {
|
|
commit('updateMemberAndUser', data);
|
|
},
|
|
/** 更新剧本所有成员列表 */
|
|
updateMemberListInScript:({ commit }, data) => {
|
|
commit('updateMemberListInScript', data);
|
|
},
|
|
/** 更新当前角色的deviceCode */
|
|
setRoleDeviceCode:({ commit }, roleDeviceCode) => {
|
|
commit('setRoleDeviceCode', roleDeviceCode);
|
|
}
|
|
}
|
|
};
|
|
|
|
export default training;
|