package com.dreikraft.axbo.task; import com.dreikraft.events.ApplicationEventDispatcher; import com.dreikraft.events.ApplicationEventEnabled; import com.dreikraft.events.ApplicationMessageEvent; import com.dreikraft.axbo.Axbo; import com.dreikraft.axbo.data.AxboCommandUtil; import com.dreikraft.axbo.data.AxboResponseProtocol; import com.dreikraft.axbo.data.DeviceContext; import com.dreikraft.axbo.data.DeviceType; import com.dreikraft.axbo.data.MovementData; import com.dreikraft.axbo.data.SensorID; import com.dreikraft.axbo.data.SleepData; import com.dreikraft.axbo.data.WakeInterval; import com.dreikraft.axbo.data.WakeType; import com.dreikraft.axbo.events.MovementEvent; import com.dreikraft.axbo.events.SleepDataAdded; import com.dreikraft.axbo.events.SleepDataImported; import com.dreikraft.axbo.util.BundleUtil; import com.dreikraft.axbo.util.ByteUtil; import java.beans.XMLEncoder; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * SleepDataImportTask * * @author jan.illetschko@3kraft.com */ public class SleepDataImportTask extends AxboTask<Integer, Integer> implements ApplicationEventEnabled { private static final Log log = LogFactory.getLog(SleepDataImportTask.class); private final List<MovementEvent> movementEventsP1; private final List<MovementEvent> movementEventsP2; private final List<SleepData> sleepDates; private PrintWriter dump; private int dataCount = 0; private int newSleepDataCount = 0; public SleepDataImportTask(final List<SleepData> sleepDates) { super(); this.sleepDates = sleepDates; movementEventsP1 = new ArrayList<>(); movementEventsP2 = new ArrayList<>(); } @Override protected Integer doInBackground() throws Exception { log.info("performing task" + getClass().getSimpleName() + " ..."); // register task to receive MovementEvents ApplicationEventDispatcher.getInstance().registerApplicationEventHandler( MovementEvent.class, this); // create a new file for dumping the raw data dump = new PrintWriter(new FileWriter( new File(Axbo.PROJECT_DIR_DEFAULT, "import.dmp"))); // run the command AxboCommandUtil.runLogDataCmd(Axbo.getPortName()); // read data until no more data is send int oldDataCount = -1; try { synchronized (this) { while (dataCount > oldDataCount) { oldDataCount = dataCount; wait(1000); } } } catch (InterruptedException ex) { log.error(ex.getMessage(), ex); } // store data ffrom aXbo to file processLogData(movementEventsP1); processLogData(movementEventsP2); return newSleepDataCount; } @Override protected void done() { try { final Integer newCount = get(); log.info("task " + getClass().getSimpleName() + " performed successfully"); setResult(Result.SUCCESS); ApplicationEventDispatcher.getInstance().dispatchGUIEvent( new SleepDataImported( this, newCount)); } catch (InterruptedException ex) { log.error("task " + getClass().getSimpleName() + " interrupted", ex); setResult(Result.INTERRUPTED); } catch (ExecutionException ex) { log.error("task " + getClass().getSimpleName() + " failed", ex.getCause()); setResult(Result.FAILED); } finally { DeviceContext.getDeviceType().getDataInterface().stop(); ApplicationEventDispatcher.getInstance() .deregisterApplicationEventHandler( MovementEvent.class, this); dump.close(); } } /** * Process movement records. * * @param movementEvent */ public void handle(final MovementEvent movementEvent) { // notify swingworker about data synchronized (this) { dataCount = AxboResponseProtocol.END.getLetterAsString().equals( movementEvent.getCmd()) ? 0 : dataCount + 1; notifyAll(); } // add movement record to corresponding person if (movementEvent.getId().equals(SensorID.P1.toString())) { movementEventsP1.add(movementEvent); } else if (movementEvent.getId().equals(SensorID.P2.toString())) { movementEventsP2.add(movementEvent); } dump.println(ByteUtil.dumpByteArray(Arrays.copyOf( movementEvent.getRawData(), 18))); } @SuppressWarnings("fallthrough") private void processLogData(final List<MovementEvent> movementEvents) { // sort movements Collections.sort(movementEvents); if (movementEvents.size() > 0) { // get sensor final SensorID sensorId = SensorID.valueOf(movementEvents.get(0).getId()); final String name = Axbo.getApplicationPreferences().get( sensorId.toString(), sensorId.getDefaultName()); // create initial sleep data object SleepData sleepData = new SleepData(sensorId.toString(), name, DeviceType.AXBO, ""); long currentSleepEnd = movementEvents.get(0).getMovementData(). getTimestamp().getTime() + Axbo.MAXIMUM_SLEEP_DURATION * SleepData.HOUR; // iterate over all movements for current sensor id for (int i = 0; i < movementEvents.size(); i++) { // get current movement final MovementData movement = movementEvents.get(i).getMovementData(); // retrieve protocol type of movement data final AxboResponseProtocol protocolType = AxboResponseProtocol. valueOfLetter(movementEvents.get(i).getCmd()); // calculate the time difference between previous and current movement long delta = 0; final MovementData prevMovement = sleepData.findLastMovement(); if (prevMovement != null && movement.isMovement()) delta = movement.getTimestamp().getTime() - prevMovement .getTimestamp().getTime(); if (currentSleepEnd + SleepData.SNOOZE_WAIT_INTERVAL < movement.getTimestamp().getTime() || delta > Axbo.CLEANER_INTERVAL_DEFAULT) { // store current sleepdata storeSleepData(sleepData); sleepData = new SleepData(sensorId.toString(), name, DeviceType.AXBO, ""); currentSleepEnd = movement.getTimestamp().getTime() + Axbo.MAXIMUM_SLEEP_DURATION * SleepData.HOUR; } sleepData.addMovement(movement); // handle different protocols switch (protocolType) { case BEGIN: case NEXT: case END: break; case KEY: if (sleepData.getWakeIntervalStart() == null) { movement.setMovementsZ(MovementData.KEY); } break; case SNOOZE: movement.setMovementsZ(MovementData.SNOOZE); currentSleepEnd = movement.getTimestamp().getTime() + sleepData.getWakeInterval().getTime(); break; case RANDOM_WAKE: case GOOD_WAKE: // set wake time and mark sleep data for saving sleepData.setWakeupTime(movement.getTimestamp()); sleepData.setWakeType(WakeType.GOOD); if (movement.getTimestamp().getTime() + SleepData.SNOOZE_WAIT_INTERVAL > currentSleepEnd) { currentSleepEnd = movement.getTimestamp().getTime() + SleepData.SNOOZE_WAIT_INTERVAL ; } break; case WAKE: // set wake time and mark sleepdata for saving sleepData.setWakeupTime(movement.getTimestamp()); sleepData.setWakeType(WakeType.LAST); if (movement.getTimestamp().getTime() + SleepData.SNOOZE_WAIT_INTERVAL > currentSleepEnd) { currentSleepEnd = movement.getTimestamp().getTime() + SleepData.SNOOZE_WAIT_INTERVAL ; } break; case POWER_NAPPING: // create a sleepdata when powernapping starts storeSleepData(sleepData); sleepData = new SleepData(sensorId.toString(), name, DeviceType.AXBO, "Power Nap"); sleepData.setPowerNap(true); sleepData.setWakeIntervalStart(movement.getTimestamp()); break; case WAKE_INTERVAL_START: case WAKE_INTERVAL_SHORT: if (sleepData.getWakeIntervalStart() == null) { sleepData.setWakeIntervalStart(movement.getTimestamp()); sleepData.setWakeInterval(WakeInterval .getWakeIntervalFromProtocol(protocolType)); currentSleepEnd = movement.getTimestamp().getTime() + sleepData .getWakeInterval().getTime(); } break; default: if (log.isDebugEnabled()) log.debug("unprocessed protocol type: " + protocolType); break; } } // store last sleepData Object storeSleepData(sleepData); } } private void storeSleepData(final SleepData sleepData) { if (sleepData.getMovements().size() < 2) { return; } if (!sleepData.isPowerNap() && (sleepData.calculateDuration() < Axbo.MINIMUM_SLEEP_DURATION || sleepData.calculateMovementCount() < Axbo.MINIMUM_MOVEMENTS || sleepData.calculateMovementsPerHour() < Axbo.AVERAGE_MOVEMENTS_THRESHOLD || (sleepData.getWakeupTime() == null && sleepData .getWakeIntervalStart() == null))) { return; } if (sleepDates.contains(sleepData)) { return; } final File dir = new File(Axbo.PROJECT_DIR_DEFAULT); final File f = new File(dir, sleepData.getSleepDataFilename()); try { if (f.exists()) { return; } try (XMLEncoder encoder = new XMLEncoder(new BufferedOutputStream( new FileOutputStream(f)))) { encoder.writeObject(sleepData); } // add sleep data to project table sleepData.setDataFile(f); newSleepDataCount++; ApplicationEventDispatcher.getInstance().dispatchGUIEvent( new SleepDataAdded( this, sleepData)); final String statusMsg = BundleUtil.getMessage( "statusLabel.fileSaved", f.getName(), dir.getAbsolutePath()); ApplicationEventDispatcher.getInstance().dispatchGUIEvent( new ApplicationMessageEvent( this, statusMsg, false)); } catch (Exception ex) { final String msg = BundleUtil.getErrorMessage("sleepData.saveFailed", f.getName(), dir.getAbsolutePath()); log.error(ex.getMessage(), ex); ApplicationEventDispatcher.getInstance().dispatchGUIEvent( new ApplicationMessageEvent( this, msg, true)); } } }