Skip to content

Instantly share code, notes, and snippets.

@Sibicle
Created November 21, 2016 23:58
Show Gist options
  • Save Sibicle/8cea02cc937c2ab5d2720c067e0f6a82 to your computer and use it in GitHub Desktop.
Save Sibicle/8cea02cc937c2ab5d2720c067e0f6a82 to your computer and use it in GitHub Desktop.
Hazmat Classifier
<?php
namespace SparkLib\HazmatClassifier;
use \Exception;
use \SparkLib\HazmatClassifier\LiIonBattery,
\SparkLib\HazmatClassifier\LiMetalBattery;
abstract class Battery {
// Battery chemistries
const LI_ION = 1;
const LI_METAL = 2;
// Battery packaging type
const BATTERY_ONLY = 1;
const PACKED_EQUIPMENT = 2;
const CONTAINED_EQUIPMENT = 3;
// Battery size
const SMALL = 1;
const MEDIUM = 2;
const LARGE = 3;
//
// Classification limits:
// More than 2.5 kilograms of small batteries per package
//
const Q_SM = 2.5;
//
// More than 2 medium batteries per package
//
const Q_CELL_MED = 8;
//
// More than 2 medium batteries per package
//
const Q_BATT_MED = 2;
protected $weight;
protected $voltage;
protected $charge;
protected $cells;
protected $packaging;
protected $exempt = false;
abstract protected function getLithiumContent();
abstract protected function iataSize();
abstract protected function iataClassification();
abstract protected function dotClassification();
abstract protected function getChemistry();
public function __construct ($params = null)
{
if ($params !== null)
{
if (isset($params['weight']))
$this->setWeight($params['weight']);
if (isset($params['voltage']))
$this->setVoltage($params['voltage']);
if (isset($params['charge']))
$this->setCharge($params['charge']);
if (isset($params['cells']))
$this->setCells($params['cells']);
if (isset($params['packaging']))
$this->setPackaging($params['packaging']);
if (isset($params['exempt']))
$this->setExempt($params['exempt']);
}
else
{
$this->setCells(1);
$this->setPackaging(self::BATTERY_ONLY);
}
}
public static function create ($chemistry)
{
if ($chemistry == static::LI_ION)
return new LiIonBattery();
else if ($chemistry == static::LI_METAL)
return new LiMetalBattery();
}
public function setWeight($weight)
{
if (! is_numeric($weight) || $weight < 0)
throw new Exception('Invalid battery weight.');
$this->weight = $weight;
return $this;
}
public function setCells($cells)
{
if (! is_integer($cells) || $cells < 1)
throw new Exception('Invalid number of battery cells.');
$this->cells = $cells;
return $this;
}
// ...
public function getWeight()
{
return $this->weight;
}
public function isLiIon()
{
return $this instanceOf LiIonBattery;
}
public function isLiMetal()
{
return $this instanceOf LiMetalBattery;
}
public function getCells()
{
return $this->cells;
}
public function isBattery()
{
return $this->cells > 1;
}
// ...
public function getEnergy()
{
return $this->voltage * $this->charge;
}
public function getPackaging()
{
return $this->packaging;
}
public function isBatteryOnly()
{
return $this->packaging == static::BATTERY_ONLY;
}
// ...
public function validateBattery()
{
if ($this->weight === null)
throw new Exception('Weight must be set.');
if ($this->voltage === null)
throw new Exception('Voltage must be set.');
if ($this->charge === null)
throw new Exception('Charge must be set.');
if ($this->cells === null)
throw new Exception('Cells must be set.');
if ($this->packaging === null)
throw new Exception('Packaging must be set.');
}
public function qValue()
{
$this->validateBattery();
if ($this->isSmall())
return $this->getWeight() / static::Q_SM;
else if ($this->isMedium() && $this->isCell())
return 1 / static::Q_CELL_MED;
else if ($this->isMedium() && $this->isBattery())
return 1 / static::Q_BATT_MED;
}
}
<?php
namespace SparkLib\HazmatClassifier;
use \Exception;
use \SparkLib\HazmatClassifier\HazmatClassifier,
\SparkLib\HazmatClassifier\Package,
\SparkLib\HazmatClassifier\LineItem,
\SparkLib\HazmatClassifier\Battery;
class IataClassifier extends HazmatClassifier {
// No classification required
const IATA_NONE = 000;
// Lithium ion
const IATA_965_IA = 111;
const IATA_965_IB = 112;
const IATA_965_II = 113;
// Lithium ion packed with equipment
const IATA_966_I = 121;
const IATA_966_II = 122;
// Lithium ion contained in equipment
const IATA_967_I = 131;
const IATA_967_II = 132;
// ... etc ...
protected static $classNames = [
self::IATA_NONE => 'IATA None',
self::IATA_965_IA => 'IATA 965 IA',
self::IATA_965_IB => 'IATA 965 IB',
self::IATA_965_II => 'IATA 965 II',
];
// Li Ion Classes
private $liIonClasses = [
self::IATA_965_IA,
self::IATA_965_IB,
];
// ...
protected function classify()
{
$p = $this->package;
//
// If the package has no batteries, it has no IATA classification
//
if (! $p->hasBatteries()) {
$this->classification = self::IATA_NONE;
}
//
// If there are batteries of multiple packaging types, it is too much for
// the poor classifier to handle, and it should be classified as IATA
// Multiple.
//
else if ($p->hasMultiplePackagingTypes()) {
$this->classification = self::IATA_MULTIPLE;
}
//
// If there are batteries of multiple chemestries ( ion / metal ) it is
// too much for the poor classifier to handle, and it should be classified
// as IATA Multiple.
//
else if ($p->hasMultipleChemistries()) {
$this->classification = self::IATA_MULTIPLE;
}
//
// If there are batteries of multiple sizes ( i.e. more or less than
// 2.5 kWh, etc. ), it is too much for the poor classifier to handle, and
// it should be classified as IATA Multiple.
//
else if ($p->hasMultipleSizes()) {
$this->classification = self::IATA_MULTIPLE;
}
//
// If none of the above criteria are met, it should be classified based on
// the classification of the largest battery in the package.
//
else {
$this->classification = $p->largestBattery()->iataClassification();
}
//
// All packages with any type of classification require a declaration
//
if ($this->getClassification() != self::IATA_NONE) {
$this->setBatteryDecRequired();
}
//
// Set the appropriate sticker for lithium ion
//
if ($this->isLiIonClass()) {
$this->setStickerLiIonRequired();
}
//
// Or for lithium metal
//
else if ($this->isLiMetalClass()) {
$this->setStickerLiMetalRequired();
}
// ...
if ($p->hasSmallAndMediumBatteries()) {
$this->bumpClassTwoClassification();
}
//
// The weight of the small batteries is too large
//
else if ($p->hasSmallBattery() && $p->calcBatteryWeight() > Battery::Q_SM) {
$this->bumpClassTwoClassification();
}
// ...
}
public function isRestrictedClass()
{
return $this->getClassification() != static::IATA_NONE;
}
public static function idToClassificationName($class)
{
if (array_key_exists($class, self::$classNames))
return self::$classNames[$class];
}
private function isLiIonClass()
{
return $this->isClassificationType($this->liIonClasses);
}
private function isLiMetalClass()
{
return $this->isClassificationType($this->liMetalClasses);
}
// ...
private function bumpClassTwoClassification()
{
if ($this->getClassification() == self::IATA_965_II)
{
$this->classification = self::IATA_965_IB;
$this->setStickerHazardRequired();
}
else if ($this->getClassification() == self::IATA_968_II)
{
$this->classification = self::IATA_968_IB;
$this->setStickerHazardRequired();
}
}
}
<?php
namespace SparkLib\HazmatClassifier;
use \Exception;
use \SparkLib\HazmatClassifier\Battery,
\SparkLib\HazmatClassifier\IataClassifier;
class LiIonBattery extends Battery
{
// Classification cutoffs
const LI_ION_SM = 2.7;
const LI_ION_CELL = 20.0;
const LI_ION_BATT = 100.0;
public static function create($chemistry = null)
{
return new static;
}
public function getChemistry()
{
return Battery::LI_ION;
}
public function getLithiumContent()
{
return $this->getEnergy() * 0.3;
}
public function dotClassification()
{
switch ($this->getPackaging()) {
case Battery::BATTERY_ONLY : return DotClassifier::UN_3480;
case Battery::PACKED_EQUIPMENT : return DotClassifier::UN_3481_P;
case Battery::CONTAINED_EQUIPMENT : return DotClassifier::UN_3481_C;
}
}
public function iataSize()
{
if ($this->getEnergy() <= static::LI_ION_SM)
return static::SMALL;
else if ($this->isCell() && $this->getEnergy() <= static::LI_ION_CELL)
return static::MEDIUM;
else if ($this->isBattery() && $this->getEnergy() <= static::LI_ION_BATT)
return static::MEDIUM;
else
return static::LARGE;
}
public function iataClassification()
{
switch ($this->getPackaging())
{
case Battery::BATTERY_ONLY :
switch ($this->iataSize()) {
case static::SMALL : return IataClassifier::IATA_965_II;
case static::MEDIUM : return IataClassifier::IATA_965_II;
case static::LARGE : return IataClassifier::IATA_965_IA;
}
case Battery::PACKED_EQUIPMENT :
switch ($this->iataSize()) {
case static::SMALL : return IataClassifier::IATA_966_II;
case static::MEDIUM : return IataClassifier::IATA_966_II;
case static::LARGE : return IataClassifier::IATA_966_I;
}
case Battery::CONTAINED_EQUIPMENT :
switch ($this->iataSize()) {
case static::SMALL : return IataClassifier::IATA_967_II;
case static::MEDIUM : return IataClassifier::IATA_967_II;
case static::LARGE : return IataClassifier::IATA_967_I;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment