/**
  * Add lap to this participant. When the lap number is not set on Lap,
  * this method adds the number.
  *
  * @param   Lap  $lap
  * @return  Participant
  */
 public function addLap(Lap $lap)
 {
     // No lap number set. Set lap number because we can't function without
     if (!$lap->getNumber()) {
         // No laps yet, so is the first
         if (!$this->laps) {
             $lap->setNumber(1);
         } else {
             $lap->setNumber($this->laps[count($this->laps) - 1]->getNumber() + 1);
         }
     }
     $this->laps[] = $lap;
     return $this;
 }
Exemple #2
0
 /**
  * Get participants sorted by ending position
  *
  * @return  array
  */
 protected function getParticipants()
 {
     // Drivers already read
     if ($this->participants !== null) {
         // Return already read participants
         return $this->participants;
     }
     // Init drivers array
     $participants = array();
     // Remember all lap positions to detect corruption later
     $lap_positions = array();
     // Remeber all lap instances per lap number so we fix position
     // corruption on them
     $all_laps_by_lap_number = array();
     // Loop each driver (if any)
     /* @var $driver_xml \DOMNode */
     foreach ($this->dom->getElementsByTagName('Driver') as $driver_xml) {
         // Create new driver
         $main_driver = new Driver();
         // Get position
         $position = (int) $this->dom_value('Position', $driver_xml);
         // Set driver values
         $main_driver->setName($this->dom_value('Name', $driver_xml))->setHuman((bool) $this->dom_value('isPlayer', $driver_xml));
         // Create new vehicle
         $vehicle = new Vehicle();
         // Set vehicle values
         $vehicle->setName($this->dom_value('VehName', $driver_xml))->setType($this->dom_value('CarType', $driver_xml))->setClass($this->dom_value('CarClass', $driver_xml))->setNumber((int) $this->dom_value('CarNumber', $driver_xml));
         // Create participant
         $participant = Participant::createInstance();
         // Set participant values
         $participant->setTeam($this->dom_value('TeamName', $driver_xml))->setPosition((int) $this->dom_value('Position', $driver_xml))->setClassPosition((int) $this->dom_value('ClassPosition', $driver_xml))->setGridPosition((int) $this->dom_value('GridPos', $driver_xml))->setClassGridPosition((int) $this->dom_value('ClassGridPos', $driver_xml))->setPitstops((int) $this->dom_value('Pitstops', $driver_xml));
         // Has finish time
         if ($finish_time = (double) $this->dom_value('FinishTime', $driver_xml)) {
             // Overwrite total time, because rfactor results tend to be
             // corrupted at times
             $participant->setTotalTime($finish_time);
         }
         // Get finish status value
         $finish_status = strtolower($this->dom_value('FinishStatus', $driver_xml));
         // Has finished
         if ($finish_status === 'finished normally') {
             // Set finish status
             $participant->setFinishStatus(Participant::FINISH_NORMAL);
         } elseif ($finish_status === 'dq') {
             // Set disqualified status
             $participant->setFinishStatus(Participant::FINISH_DQ);
         } elseif ($finish_status === 'dnf') {
             // Set did not finish status
             $participant->setFinishStatus(Participant::FINISH_DNF);
             // Set finish comment (if any)
             if ($finish_status = $this->dom_value('DNFReason', $driver_xml)) {
                 $participant->setFinishComment($finish_status);
             }
         } else {
             // Set no finish status
             $participant->setFinishStatus(Participant::FINISH_NONE);
         }
         // Get the driver swaps
         $swaps_xml = $driver_xml->getElementsByTagName('Swap');
         // Init drivers array, a participant can have multiple
         $drivers = array();
         // Remember the drivers per laps
         $drivers_per_laps = array();
         // Remember drivers by name so we can re-use them
         $drivers_by_name = array();
         // Remember the number of swaps (always -1 of number of swap
         // elements in XML, because first driver starts on grid, which is
         // actually not really a swap)
         $number_of_swaps = 0;
         // Loop each swap
         $first_swap = true;
         // First swap reminder, can't use $swap_xml_key
         // to detect because it is bugged in hhvm!
         foreach ($swaps_xml as $swap_xml_key => $swap_xml) {
             // Empty driver name
             if (!($driver_name = $swap_xml->nodeValue)) {
                 // Skip this swap
                 continue;
             }
             // Driver already processed
             if (array_key_exists($driver_name, $drivers_by_name)) {
                 // Use existing found driver instance
                 $swap_driver = $drivers_by_name[$driver_name];
             } else {
                 // Create new driver
                 $swap_driver = new Driver();
                 // Set name
                 $swap_driver->setName($driver_name);
                 // Use human state the same of main driver within XML
                 $swap_driver->setHuman($main_driver->isHuman());
                 // Add swap driver to drivers array
                 $drivers[] = $swap_driver;
                 // Remember swap driver by name
                 $drivers_by_name[$driver_name] = $swap_driver;
             }
             // Add swap driver to drivers per lap
             $drivers_per_laps[] = array('start_lap' => (int) $swap_xml->getAttribute('startLap'), 'end_lap' => (int) $swap_xml->getAttribute('endLap'), 'driver' => $swap_driver);
             // Not first swap element, so this is a real swap that happend
             // within pits
             if (!$first_swap) {
                 // Increment the number of swaps
                 $number_of_swaps++;
             }
             // Not first swap anymore
             $first_swap = false;
         }
         // No drivers yet, so no drivers through swap info
         if (!$drivers) {
             // Add main driver to drivers array because we could not get
             // it from the swap info
             $drivers[] = $main_driver;
         }
         // Pitcounter is lower than number of swaps
         if ($participant->getPitstops() < $number_of_swaps) {
             // Set pitstop counter to the number of swaps
             $participant->setPitstops($number_of_swaps);
         }
         // Add vehicle to participant
         $participant->setVehicle($vehicle);
         // Add drivers to participant
         $participant->setDrivers($drivers);
         // Remember whether the drivers are human or not from the look at
         // the aids
         $is_human_by_aids = null;
         // Get lap aids information and convert to friendly array
         $aids_xml = $driver_xml->getElementsByTagName('ControlAndAids');
         $aids = array();
         foreach ($aids_xml as $aid_xml) {
             // Match the aids
             $matches = array();
             preg_match_all('/([a-z]+)(=([a-z0-9]))?[,]?/i', (string) $aid_xml->nodeValue, $matches);
             // Prepare aid items array
             $aid_items = array();
             // Loop each matched aid
             if (isset($matches[1])) {
                 foreach ($matches[1] as $key => $aid_name) {
                     // Get value
                     $aid_value = $matches[3][$key];
                     // Is float
                     if (is_float($aid_value)) {
                         // Cast to float
                         $aid_value = (double) $aid_value;
                     } elseif (is_numeric($aid_value)) {
                         // Cast to int
                         $aid_value = (int) $aid_value;
                     }
                     // Is a human player
                     if ($aid_name === 'PlayerControl') {
                         // Remember this driver is human
                         $is_human_by_aids = true;
                     } elseif (($aid_name === 'UnknownControl' or $aid_name === 'AIControl') and $is_human_by_aids === null) {
                         // Remember this driver is not human
                         $is_human_by_aids = false;
                     }
                     // Set key => value of aid
                     $aid_items[$aid_name] = $aid_value ? $aid_value : null;
                 }
             }
             // Add aid information per lap
             $aids[] = array('start_lap' => (int) $aid_xml->getAttribute('startLap'), 'end_lap' => (int) $aid_xml->getAttribute('endLap'), 'aids' => $aid_items);
         }
         // No aids
         if (!$aids) {
             // Always human
             $is_human_by_aids = true;
         }
         //-- Set laps
         // Loop each available lap
         /* @var $lap_xml \DOMNode */
         foreach ($driver_xml->getElementsByTagName('Lap') as $lap_xml) {
             // Create new lap
             $lap = new Lap();
             // Lap time zero or lower
             if (($lap_time = (double) $lap_xml->nodeValue) <= 0.0) {
                 // No lap time
                 $lap_time = null;
             }
             // Get lap position and add it to known positions
             $lap_positions[] = $lap_position = (int) $lap_xml->getAttribute('p');
             // Elapsed seconds by default null
             $elapsed_seconds = null;
             // Valid value
             if ($lap_xml->getAttribute('et') !== '--.---') {
                 // Create float value
                 $elapsed_seconds = (double) $lap_xml->getAttribute('et');
             }
             // Default compound values
             $front_compound = null;
             $rear_compound = null;
             // Front compound isset
             if (($fcompound = $lap_xml->getAttribute('fcompound')) !== '') {
                 $front_compound = $fcompound;
             }
             // Rear compound isset
             if (($rcompound = $lap_xml->getAttribute('rcompound')) !== '') {
                 $rear_compound = $rcompound;
             }
             // Has fuel info and is positive
             $fuel = NULL;
             if ($fuel_data = (double) $lap_xml->getAttribute('fuel') and $fuel_data > 0) {
                 // Get proper percentage
                 $fuel = $fuel_data * 100;
             }
             $front_compound_left_wear = NULL;
             if ($wear_data = (double) $lap_xml->getAttribute('twfl') and $wear_data > 0) {
                 // Get proper percentage
                 $front_compound_left_wear = $wear_data * 100;
             }
             $front_compound_right_wear = NULL;
             if ($wear_data = (double) $lap_xml->getAttribute('twfr') and $wear_data > 0) {
                 // Get proper percentage
                 $front_compound_right_wear = $wear_data * 100;
             }
             $rear_compound_left_wear = NULL;
             if ($wear_data = (double) $lap_xml->getAttribute('twrl') and $wear_data > 0) {
                 // Get proper percentage
                 $rear_compound_left_wear = $wear_data * 100;
             }
             $rear_compound_right_wear = NULL;
             if ($wear_data = (double) $lap_xml->getAttribute('twrr') and $wear_data > 0) {
                 // Get proper percentage
                 $rear_compound_right_wear = $wear_data * 100;
             }
             // Set lap values
             $lap->setTime($lap_time)->setPosition($lap_position)->setNumber((int) $lap_xml->getAttribute('num'))->setParticipant($participant)->setElapsedSeconds($elapsed_seconds)->setFrontCompound($front_compound)->setRearCompound($rear_compound)->setFrontCompoundLeftWear($front_compound_left_wear)->setFrontCompoundRightWear($front_compound_right_wear)->setRearCompoundLeftWear($rear_compound_left_wear)->setRearCompoundRightWear($rear_compound_right_wear)->setFuel($fuel)->setPitLap((bool) $lap_xml->getAttribute('pit'));
             // Find lap aids
             foreach ($aids as $aid) {
                 // Lap match
                 if ($aid['start_lap'] <= $lap->getNumber() and $aid['end_lap'] >= $lap->getNumber()) {
                     // Set aids
                     $lap->setAids($aid['aids']);
                     // Stop searching
                     break;
                 }
             }
             // Find lap driver
             foreach ($drivers_per_laps as $driver_lap) {
                 // Lap match
                 if ($driver_lap['start_lap'] <= $lap->getNumber() and $driver_lap['end_lap'] >= $lap->getNumber()) {
                     // Set driver
                     $lap->setDriver($driver_lap['driver']);
                     // Stop searching
                     break;
                 }
             }
             // No driver yet
             if (!$lap->getDriver()) {
                 // Just put first driver on lap
                 $lap->setDriver($drivers[0]);
             }
             // Add each sector available
             $sector = 1;
             while ($lap_xml->hasAttribute($sector_attribute = 's' . $sector)) {
                 // Add sector
                 $lap->addSectorTime((double) $lap_xml->getAttribute($sector_attribute));
                 // Increment number of sector
                 $sector++;
             }
             // Add lap to participant
             $participant->addLap($lap);
             // Remember lap
             $all_laps_by_lap_number[$lap->getNumber()][] = $lap;
         }
         // Detected human state by aids
         if ($is_human_by_aids !== null) {
             // Force human mark on all drivers
             foreach ($drivers as $driver) {
                 $driver->setHuman($is_human_by_aids);
             }
         }
         // Set driver to drivers array based on his position
         $participants[$position - 1] = $participant;
     }
     // Make positions array unique and sort it
     $lap_positions = array_unique($lap_positions);
     sort($lap_positions);
     // Loop each position and detect corrupted and wrong position laps
     $corrupted_lap_positions = array();
     $wrong_lap_positions = array();
     foreach ($lap_positions as $key => $lap_position) {
         // Lap is 10 positions higher than previous, it's a too big gap,
         // we consider this full corruption
         if ($key > 0 and $lap_position - $lap_positions[$key - 1] > 9) {
             // Add lap position to corrupted list
             $corrupted_lap_positions[] = $lap_position;
         }
         // First position aint 1 OR lap is 2 positions higher than previous,
         // we have some wrong lap positions
         if ($key === 0 and $lap_position > 1 or $key > 0 and $lap_position - $lap_positions[$key - 1] > 1) {
             // Add lap position to wrong list
             $wrong_lap_positions[] = $lap_position;
         }
     }
     // We have corrupted lap positions
     if ($corrupted_lap_positions) {
         // Whether we need to refill $all_laps_by_lap_number
         $refill_all_laps_by_lap_number = false;
         // Loop each participant to find out if they are really all
         // corrupted
         foreach ($participants as $participant) {
             // By default all laps of participant are corrupted
             $all_corrupted = true;
             // By default we have no corruption at all
             $corruption = false;
             // Loop each lap to see whether all laps are corrupted
             foreach ($participant->getLaps() as $lap) {
                 // Lap position is not corrupted
                 if (!in_array($lap->getPosition(), $corrupted_lap_positions)) {
                     // Not all corrupted
                     $all_corrupted = false;
                 } else {
                     // No position known
                     $lap->setPosition(null);
                     // We have corruption
                     $corruption = true;
                 }
             }
             // All are corrupted
             if ($all_corrupted) {
                 // Unset all participant laps
                 $participant->setLaps(array());
                 // We need to refill all laps by lap number array
                 $refill_all_laps_by_lap_number = true;
             }
         }
         // Refill all laps by lap number array because laps are removed
         if ($refill_all_laps_by_lap_number) {
             $all_laps_by_lap_number = array();
             foreach ($participants as $participant) {
                 // Loop each lap
                 foreach ($participant->getLaps() as $lap) {
                     // Remember lap
                     $all_laps_by_lap_number[$lap->getNumber()][] = $lap;
                 }
             }
         }
     }
     //--- Fix wrong positions of laps
     // We have wrong lap positions, we need to fix this
     if ($wrong_lap_positions) {
         // Loop all lap numbers and their laps
         foreach ($all_laps_by_lap_number as $lap_number => $laps) {
             // Lap number 1
             if ($lap_number === 1) {
                 // Just set each lap position to grid position
                 foreach ($laps as $lap) {
                     $lap->setPosition($lap->getParticipant()->getGridPosition());
                 }
                 // Ignore futher
                 continue;
             }
             // Sort the laps by elapsed time
             $laps = Helper::sortLapsByElapsedTime($laps);
             // Fix the positions
             foreach ($laps as $lap_key => $lap) {
                 // Set new position if it's not null (null = corruption)
                 if ($lap->getPosition() !== null) {
                     $lap->setPosition($lap_key + 1);
                 }
             }
         }
     }
     // Sort participants by key
     ksort($participants);
     // Fix array keys to 0 - n
     $participants = array_values($participants);
     // Cache and return all participants
     return $this->participants = $participants;
 }