models/classes/runner/synchronisation/SynchronisationService.php
public function process($data, $serviceContext)
    {
        if (empty($data)) {
            throw new \common_exception_InconsistentData('No action to check. Processing action requires data.');
        }

        // first, extract the actions and build usable instances
        // also compute the total duration to synchronise
        $actions = [];
        $duration = 0;
        foreach ($data as $entry) {
            $action = $this->resolve($entry);
            $actions[] = $action;

            if ($action->hasRequestParameter('itemDuration')) {
                $duration += $action->getRequestParameter('itemDuration') + self::TIMEPOINT_INTERVAL;
            }
        }
        
        // determine the start timestamp of the actions:
        // - check if the total duration of actions to sync is comprised within
        //   the elapsed time since the last TimePoint.
        // - otherwise compute the start timestamp from now minus the duration
        //   (caution! this could introduce inconsistency in the TimeLine as the ranges could be interlaced) 
        $now = microtime(true);
        $last = $serviceContext->getTestSession()->getTimer()->getLastRegisteredTimestamp();
        $elapsed = $now - $last;
        if ($duration > $elapsed) {
            \common_Logger::t('Ignoring the last timestamp to take into account the actual duration to sync. Could introduce TimeLine inconsistency!');
            $last = $now - $duration;
        }

        // ensure the actions are in chronological order
        usort($actions, function($a, $b) {
           return $a->getTimestamp() - $b->getTimestamp(); 
        });

        $response = [];

        /** @var TestRunnerAction $action */
        foreach( $actions as $action) {
            try {
                $serviceContext->setSyncingMode($action->getRequestParameter('offline'));
                if ($action->hasRequestParameter('itemDuration') && $serviceContext->isSyncingMode()) {
                    $last += $action->getRequestParameter('itemDuration') + self::TIMEPOINT_INTERVAL;
                    $action->setTime($last);
                } else {
                    $action->setTime($now);
                }

                $action->setServiceContext($serviceContext);
                if ($serviceContext instanceof QtiRunnerServiceContext) {
                }
                $responseAction = $action->process();
            } catch (\common_Exception $e) {
                $responseAction = ['error' => $e->getMessage()];
                $responseAction['success'] = false;
            }

            $responseAction['name'] = $action->getName();
            $responseAction['timestamp'] = $action->getTimeStamp();
            $responseAction['requestParameters'] = $action->getRequestParameters();

            $response[] = $responseAction;

            if ($responseAction['success'] === false) {
                break;
            }
        }

        $this->getRunnerService()->persist($serviceContext);

        return $response;
    }
public function process($data, $serviceContext)
    {
        if (empty($data)) {
            throw new \common_exception_InconsistentData('No action to check. Processing action requires data.');
        }

        // first, extract the actions and build usable instances
        // also compute the total duration to synchronise
        $actions = [];
        $duration = 0;
        foreach ($data as $entry) {
            $action = $this->resolve($entry);
            $actions[] = $action;

            if ($action->hasRequestParameter('itemDuration')) {
                $duration += $action->getRequestParameter('itemDuration') + self::TIMEPOINT_INTERVAL;
            }
        }
        
        // determine the start timestamp of the actions:
        // - check if the total duration of actions to sync is comprised within
        //   the elapsed time since the last TimePoint.
        // - otherwise compute the start timestamp from now minus the duration
        //   (caution! this could introduce inconsistency in the TimeLine as the ranges could be interlaced) 
        $now = microtime(true);
        $last = $serviceContext->getTestSession()->getTimer()->getLastRegisteredTimestamp();
        $elapsed = $now - $last;
        if ($duration > $elapsed) {
            \common_Logger::t('Ignoring the last timestamp to take into account the actual duration to sync. Could introduce TimeLine inconsistency!');
            $last = $now - $duration;
        }

        // ensure the actions are in chronological order
        usort($actions, function($a, $b) {
           return $a->getTimestamp() - $b->getTimestamp(); 
        });

        $response = [];

        /** @var TestRunnerAction $action */
        foreach( $actions as $action) {
            try {
                $serviceContext->setSyncingMode($action->getRequestParameter('offline'));
                if ($action->hasRequestParameter('itemDuration') && $serviceContext->isSyncingMode()) {
                    $last += $action->getRequestParameter('itemDuration') + self::TIMEPOINT_INTERVAL;
                    $action->setTime($last);
                } else {
                    $action->setTime($now);
                }

                $action->setServiceContext($serviceContext);
                if ($serviceContext instanceof QtiRunnerServiceContext) {
                }
                $responseAction = $action->process();
            } catch (\common_Exception $e) {
                $responseAction = ['error' => $e->getMessage()];
                $responseAction['success'] = false;
            }

            $responseAction['name'] = $action->getName();
            $responseAction['timestamp'] = $action->getTimeStamp();
            $responseAction['requestParameters'] = $action->getRequestParameters();

            $response[] = $responseAction;

            if ($responseAction['success'] === false) {
                break;
            }
        }

        $this->getRunnerService()->persist($serviceContext);

        return $response;
    }
Wrap the process to appropriate action and aggregate results

@param $data
@param $serviceContext QtiRunnerServiceContext
@return array
@throws