package org.jnect.emfstore; import java.security.AccessControlException; import java.util.Calendar; import java.util.Iterator; import java.util.List; import java.util.Observable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EContentAdapter; import org.eclipse.emf.emfstore.client.model.CompositeOperationHandle; import org.eclipse.emf.emfstore.client.model.ModelFactory; import org.eclipse.emf.emfstore.client.model.ProjectSpace; import org.eclipse.emf.emfstore.client.model.Usersession; import org.eclipse.emf.emfstore.client.model.Workspace; import org.eclipse.emf.emfstore.client.model.WorkspaceManager; import org.eclipse.emf.emfstore.client.model.exceptions.InvalidHandleException; import org.eclipse.emf.emfstore.client.model.impl.WorkspaceImpl; import org.eclipse.emf.emfstore.client.model.util.EMFStoreClientUtil; import org.eclipse.emf.emfstore.client.model.util.EMFStoreCommand; import org.eclipse.emf.emfstore.common.model.ModelElementId; import org.eclipse.emf.emfstore.common.model.Project; import org.eclipse.emf.emfstore.common.model.util.ModelUtil; import org.eclipse.emf.emfstore.server.exceptions.EmfStoreException; import org.eclipse.emf.emfstore.server.model.ProjectInfo; import org.eclipse.emf.emfstore.server.model.versioning.ChangePackage; import org.eclipse.emf.emfstore.server.model.versioning.LogMessage; import org.eclipse.emf.emfstore.server.model.versioning.PrimaryVersionSpec; import org.eclipse.emf.emfstore.server.model.versioning.VersioningFactory; import org.eclipse.emf.emfstore.server.model.versioning.operations.AbstractOperation; import org.eclipse.emf.emfstore.server.model.versioning.operations.AttributeOperation; import org.jnect.bodymodel.Body; import org.jnect.bodymodel.BodymodelFactory; import org.jnect.bodymodel.CenterHip; import org.jnect.bodymodel.CenterShoulder; import org.jnect.bodymodel.Head; import org.jnect.bodymodel.HumanLink; import org.jnect.bodymodel.LeftAnkle; import org.jnect.bodymodel.LeftElbow; import org.jnect.bodymodel.LeftFoot; import org.jnect.bodymodel.LeftHand; import org.jnect.bodymodel.LeftHip; import org.jnect.bodymodel.LeftKnee; import org.jnect.bodymodel.LeftShoulder; import org.jnect.bodymodel.LeftWrist; import org.jnect.bodymodel.PositionedElement; import org.jnect.bodymodel.RightAnkle; import org.jnect.bodymodel.RightElbow; import org.jnect.bodymodel.RightFoot; import org.jnect.bodymodel.RightHand; import org.jnect.bodymodel.RightHip; import org.jnect.bodymodel.RightKnee; import org.jnect.bodymodel.RightShoulder; import org.jnect.bodymodel.RightWrist; import org.jnect.bodymodel.Spine; /** * @author aleaum * This class offers the backbone for recording changing body models e.g. for storing Kinect data. */ public class EMFStorage extends Observable implements ICommitter { private static EMFStorage INSTANCE; private static String PROJECT_NAME = "jnectEMFStorage"; private int NEEDED_CHANGES; ProjectSpace projectSpace; Usersession usersession; private Body replayBody; private Body recordingBody; private Body outwardRecordingBody; private List<ChangePackage> changePackages; private boolean changePackagesUpdateNeeded; private int replayStatesCount = 0; private final int BODY_ELEMENTS_COUNT; private ReplayRunnable replayRunnable; /** * Counts how many new bodies have been recorded since the last commit. */ private int recordedBodyCount = 0; private CompositeOperationHandle compOpHandle; private boolean isRecording = false; /** * @return The singleton EMFStorage object. Tries to setup the connection to the EMFStore Server. */ public static EMFStorage getInstance() { if (INSTANCE == null) { INSTANCE = new EMFStorage(); } return INSTANCE; } protected EMFStorage() { this.changePackagesUpdateNeeded = true; replayBody = createAndFillBody(); outwardRecordingBody = createAndFillBody(); outwardRecordingBody.eAdapters().add(new BundleBodyChangesAdapter()); BODY_ELEMENTS_COUNT = outwardRecordingBody.eContents().size(); // 3 changes (x, y, z) in every body element NEEDED_CHANGES = BODY_ELEMENTS_COUNT * 3; } /** * This method sets up a connection to the EMFStore to retrieve a stored body model, if one is present. If the * connection is already accomplished the call to this method is still safe, nothing will happen. * * @return true if the connection was successful, false otherwise */ private boolean connectToEMFStoreAndInit() { if (projectSpace == null) { new EMFStoreCommand() { @Override protected void doRun() { try { // create a default Usersession and log in usersession = EMFStoreClientUtil.createUsersession(); Workspace currentWorkspace = WorkspaceManager.getInstance().getCurrentWorkspace(); currentWorkspace.getUsersessions().add(usersession); usersession.logIn(); // search for existing storage project on server Iterator<ProjectInfo> projectInfos = currentWorkspace.getRemoteProjectList(usersession) .iterator(); ProjectInfo projectInfo = null; while (projectInfos.hasNext()) { ProjectInfo currentProjectInfo = projectInfos.next(); if (currentProjectInfo.getName().equals(PROJECT_NAME)) { projectInfo = currentProjectInfo; break; } } // if storage project is not existing on server create one, else retrieve it if (projectInfo == null) { projectSpace = ModelFactory.eINSTANCE.createProjectSpace(); projectSpace.setProject(org.eclipse.emf.emfstore.common.model.ModelFactory.eINSTANCE .createProject()); projectSpace.setProjectName(PROJECT_NAME); projectSpace.setProjectDescription("Project for jnect-storage"); projectSpace.setLocalOperations(ModelFactory.eINSTANCE.createOperationComposite()); projectSpace.initResources(currentWorkspace.eResource().getResourceSet()); ((WorkspaceImpl) currentWorkspace).addProjectSpace(projectSpace); currentWorkspace.save(); projectSpace.shareProject(usersession, new NullProgressMonitor()); } else { // check if we already have a local copy, else checkout the project boolean found = false; for (ProjectSpace ps : currentWorkspace.getProjectSpaces()) { if (ps.getProjectInfo().getName().equals(PROJECT_NAME)) { projectSpace = ps; projectSpace.setUsersession(usersession); found = true; break; } } if (!found) { projectSpace = currentWorkspace.checkout(usersession, projectInfo); } } Project project = projectSpace.getProject(); boolean found = false; for (EObject obj : project.getAllModelElements()) { if (obj instanceof Body) { recordingBody = (Body) obj; found = true; break; } } if (!found) { recordingBody = createAndFillBody(); project.addModelElement(recordingBody); } projectSpace.commit(createLogMessage(usersession.getUsername(), "commit initial body"), null, new NullProgressMonitor()); org.eclipse.emf.emfstore.client.model.Configuration.setAutoSave(false); } catch (AccessControlException e) { ModelUtil.logException(e); projectSpace = null; recordingBody = null; } catch (EmfStoreException e) { ModelUtil.logException(e); projectSpace = null; recordingBody = null; } } }.run(false); } if (projectSpace == null) { return false; } else { assert recordingBody != null; return true; } } public static Body createAndFillBody() { Body body = BodymodelFactory.eINSTANCE.createBody(); BodymodelFactory factory = BodymodelFactory.eINSTANCE; // create Elements Head head = factory.createHead(); CenterShoulder shoulderCenter = factory.createCenterShoulder(); LeftShoulder shoulderLeft = factory.createLeftShoulder(); RightShoulder shoulderRight = factory.createRightShoulder(); LeftElbow elbowLeft = factory.createLeftElbow(); RightElbow elbowRight = factory.createRightElbow(); LeftWrist wristLeft = factory.createLeftWrist(); RightWrist wristRight = factory.createRightWrist(); LeftHand handLeft = factory.createLeftHand(); RightHand handRight = factory.createRightHand(); Spine spine = factory.createSpine(); CenterHip hipCenter = factory.createCenterHip(); LeftHip hipLeft = factory.createLeftHip(); RightHip hipRight = factory.createRightHip(); LeftKnee kneeLeft = factory.createLeftKnee(); RightKnee kneeRight = factory.createRightKnee(); LeftAnkle ankleLeft = factory.createLeftAnkle(); RightAnkle ankleRight = factory.createRightAnkle(); LeftFoot footLeft = factory.createLeftFoot(); RightFoot footRight = factory.createRightFoot(); // set color footLeft.setColor_g(255); footRight.setColor_g(255); handLeft.setColor_r(255); handLeft.setColor_g(0); handLeft.setColor_b(0); handRight.setColor_r(255); head.setColor_b(255); // add elements to body body.setHead(head); body.setLeftAnkle(ankleLeft); body.setRightAnkle(ankleRight); body.setLeftElbow(elbowLeft); body.setRightElbow(elbowRight); body.setLeftFoot(footLeft); body.setRightFoot(footRight); body.setLeftHand(handLeft); body.setRightHand(handRight); body.setCenterHip(hipCenter); body.setLeftHip(hipLeft); body.setRightHip(hipRight); body.setLeftKnee(kneeLeft); body.setRightKnee(kneeRight); body.setCenterShoulder(shoulderCenter); body.setLeftShoulder(shoulderLeft); body.setRightShoulder(shoulderRight); body.setSpine(spine); body.setLeftWrist(wristLeft); body.setRightWrist(wristRight); // create links createLink(head, shoulderCenter, body); createLink(shoulderCenter, shoulderLeft, body); createLink(shoulderCenter, shoulderRight, body); createLink(shoulderLeft, elbowLeft, body); createLink(shoulderRight, elbowRight, body); createLink(elbowLeft, wristLeft, body); createLink(elbowRight, wristRight, body); createLink(wristLeft, handLeft, body); createLink(wristRight, handRight, body); createLink(shoulderCenter, spine, body); createLink(spine, hipCenter, body); createLink(hipCenter, hipLeft, body); createLink(hipCenter, hipRight, body); createLink(hipLeft, kneeLeft, body); createLink(hipRight, kneeRight, body); createLink(kneeLeft, ankleLeft, body); createLink(kneeRight, ankleRight, body); createLink(ankleLeft, footLeft, body); createLink(ankleRight, footRight, body); return body; } private static void createLink(PositionedElement source, PositionedElement target, Body body) { HumanLink link = BodymodelFactory.eINSTANCE.createHumanLink(); link.setSource(source); link.setTarget(target); source.getOutgoingLinks().add(link); target.getIncomingLinks().add(link); body.getLinks().add(link); } private LogMessage createLogMessage(String name, String message) { LogMessage logMessage = VersioningFactory.eINSTANCE.createLogMessage(); logMessage.setAuthor(name); logMessage.setDate(Calendar.getInstance().getTime()); logMessage.setClientDate(Calendar.getInstance().getTime()); logMessage.setMessage(message); return logMessage; } public Body getRecordingBody() { return outwardRecordingBody; } public Body getReplayingBody() { return replayBody; } public int getReplayStatesCount() { return replayStatesCount; } public void initReplay() { if (!connectToEMFStoreAndInit()) { return; } if (changePackagesUpdateNeeded) { PrimaryVersionSpec start = VersioningFactory.eINSTANCE.createPrimaryVersionSpec(); start.setIdentifier(1); try { changePackages = projectSpace.getChanges(start, projectSpace.getBaseVersion()); changePackagesUpdateNeeded = false; replayStatesCount = 0; for (ChangePackage cp : changePackages) { replayStatesCount += cp.getOperations().size(); } } catch (EmfStoreException e) { e.printStackTrace(); } } } /** * Replays the body model from emfstore * * @param initCommit * @throws EmfStoreException */ public void replay(final int version) { if (!connectToEMFStoreAndInit()) return; if (replayRunnable == null) { replayRunnable = new ReplayRunnable(); } if (replayRunnable.isStopped()) { final CommitVersionAndOffset versAndOffset = getCommitVersionForReplayVersion(version); replayRunnable.prepare(versAndOffset, version); Thread replayThread = new Thread(replayRunnable); replayThread.start(); } } protected void replayOneChange(List<AbstractOperation> operations, int bodyOffset) { int startPos = bodyOffset; assert operations.size() >= startPos - 1 : "Operations should contain the requested body index " + startPos + " but had size " + operations.size() + "."; List<AbstractOperation> leafOperations = operations.get(bodyOffset).getLeafOperations(); for (AbstractOperation o : leafOperations) { replayElement(replayBody, o); } } private CommitVersionAndOffset getCommitVersionForReplayVersion(int repVersion) { int countedBodies = 0; for (int i = 0; i < changePackages.size(); i++) { ChangePackage cp = changePackages.get(i); int currentCount = cp.getOperations().size(); if (countedBodies + currentCount > repVersion) return new CommitVersionAndOffset(i, repVersion - countedBodies); countedBodies += currentCount; } assert false : "The last change package should at the very least contain the searched repVersion!"; return new CommitVersionAndOffset(changePackages.size() - 1, 0); } private void replayElement(Body targetBody, AbstractOperation o) { if (o instanceof AttributeOperation) { AttributeOperation ao = (AttributeOperation) o; ModelElementId id = ao.getModelElementId(); EObject element = projectSpace.getProject().getModelElement(id); Object newValue = ao.getNewValue(); String attribute = ao.getFeatureName(); // gets attribute name if (element instanceof Head) { setValue(attribute, targetBody.getHead(), newValue); } else if (element instanceof CenterShoulder) { setValue(attribute, targetBody.getCenterShoulder(), newValue); } else if (element instanceof LeftShoulder) { setValue(attribute, targetBody.getLeftShoulder(), newValue); } else if (element instanceof RightShoulder) { setValue(attribute, targetBody.getRightShoulder(), newValue); } else if (element instanceof LeftElbow) { setValue(attribute, targetBody.getLeftElbow(), newValue); } else if (element instanceof RightElbow) { setValue(attribute, targetBody.getRightElbow(), newValue); } else if (element instanceof LeftWrist) { setValue(attribute, targetBody.getLeftWrist(), newValue); } else if (element instanceof RightWrist) { setValue(attribute, targetBody.getRightWrist(), newValue); } else if (element instanceof LeftHand) { setValue(attribute, targetBody.getLeftHand(), newValue); } else if (element instanceof RightHand) { setValue(attribute, targetBody.getRightHand(), newValue); } else if (element instanceof Spine) { setValue(attribute, targetBody.getSpine(), newValue); } else if (element instanceof CenterHip) { setValue(attribute, targetBody.getCenterHip(), newValue); } else if (element instanceof LeftHip) { setValue(attribute, targetBody.getLeftHip(), newValue); } else if (element instanceof RightHip) { setValue(attribute, targetBody.getRightHip(), newValue); } else if (element instanceof LeftKnee) { setValue(attribute, targetBody.getLeftKnee(), newValue); } else if (element instanceof RightKnee) { setValue(attribute, targetBody.getRightKnee(), newValue); } else if (element instanceof LeftAnkle) { setValue(attribute, targetBody.getLeftAnkle(), newValue); } else if (element instanceof RightAnkle) { setValue(attribute, targetBody.getRightAnkle(), newValue); } else if (element instanceof LeftFoot) { setValue(attribute, targetBody.getLeftFoot(), newValue); } else if (element instanceof RightFoot) { setValue(attribute, targetBody.getRightFoot(), newValue); } } } private void setValue(String attribute, PositionedElement element, Object value) { if (attribute.equalsIgnoreCase("x")) { element.setX((Float) value); } else if (attribute.equalsIgnoreCase("y")) { element.setY((Float) value); } else if (attribute.equalsIgnoreCase("z")) { element.setZ((Float) value); } } public void setReplayToState(int state) { stopReplay(); if (!changePackages.isEmpty()) { CommitVersionAndOffset versAndOff = getCommitVersionForReplayVersion(state); ChangePackage cp = changePackages.get(versAndOff.version); // replay the desired state to show the correct shape replayOneChange(cp.getOperations(), versAndOff.offset); } } private void commitBodyChanges(IProgressMonitor monitor) { if (!connectToEMFStoreAndInit()) return; // commit the pending changes of the project to the EMF Store try { // projectSpace.setDirty(true); // ((ProjectSpaceBase) projectSpace).save(); projectSpace.commit( createLogMessage(usersession.getUsername(), "Commiting " + recordedBodyCount + " new body frames."), null, monitor); changePackagesUpdateNeeded = true; recordedBodyCount = 0; } catch (EmfStoreException e) { e.printStackTrace(); } } @Override public void commit() { Job commitJob = new Job("Saving recorded data.") { @Override protected IStatus run(IProgressMonitor monitor) { commitBodyChanges(monitor); return Status.OK_STATUS; } }; commitJob.setUser(true); // show dialog commitJob.schedule(); } private class CommitVersionAndOffset { public int version; public int offset; public CommitVersionAndOffset(int version, int offset) { this.version = version; this.offset = offset; } } public void startStopRecording(boolean on) { if (on && connectToEMFStoreAndInit()) { isRecording = true; } else { isRecording = false; } } private class ReplayRunnable implements Runnable { private CommitVersionAndOffset replayFrom; private int replayFromBodyNr; private boolean stop; public ReplayRunnable() { stop = true; } public void prepare(CommitVersionAndOffset replayFromCS, int replayFromBodyNr) { this.replayFrom = replayFromCS; this.replayFromBodyNr = replayFromBodyNr; } public synchronized void stop() { stop = true; } public synchronized boolean isStopped() { return stop; } @Override public void run() { stop = false; List<AbstractOperation> compositeBodyOps; int currentVersion = replayFromBodyNr; int innerOffset = replayFrom.offset; for (int i = replayFrom.version; i < changePackages.size() && !isStopped(); i++) { ChangePackage cp = changePackages.get(i); compositeBodyOps = cp.getOperations(); for (int j = innerOffset; j < compositeBodyOps.size() && !isStopped(); j++) { replayOneChange(compositeBodyOps, j); currentVersion++; setChanged(); notifyObservers(currentVersion); try { // pause for a moment to see changes Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } innerOffset = 0; } stop(); } } protected void syncBodies(Body outwardBody, Body emfBody) { EList<EObject> bodyContents = outwardBody.eContents(); EList<EObject> recBodyContents = emfBody.eContents(); assert BODY_ELEMENTS_COUNT == bodyContents.size() && BODY_ELEMENTS_COUNT == recBodyContents.size() : "Unexpected amount of body elements. The being is not human ;-)"; for (int i = 0; i < BODY_ELEMENTS_COUNT; i++) { PositionedElement outwardBodyEl = (PositionedElement) bodyContents.get(i); PositionedElement recBodyEl = (PositionedElement) recBodyContents.get(i); recBodyEl.setX(outwardBodyEl.getX()); recBodyEl.setY(outwardBodyEl.getY()); recBodyEl.setZ(outwardBodyEl.getZ()); } } public void stopReplay() { if (replayRunnable != null) replayRunnable.stop(); } private class BundleBodyChangesAdapter extends EContentAdapter { private int currChanges = 0; @Override public void notifyChanged(Notification notification) { super.notifyChanged(notification); if (++currChanges == NEEDED_CHANGES) { currChanges = 0; if (isRecording && projectSpace != null) { assert recordingBody != null; try { compOpHandle = projectSpace.beginCompositeOperation(); syncBodies(outwardRecordingBody, recordingBody); compOpHandle.end("New Body frame", "Added new frame to store", projectSpace.getProject() .getModelElementId(recordingBody)); projectSpace.setDirty(true); recordedBodyCount++; } catch (InvalidHandleException e) { e.printStackTrace(); } } } } } public boolean isRecording() { return isRecording; } }