<?php if (!defined('PmWiki')) highlight_file(__FILE__);
if (!defined('PmWiki')) exit();
/** \file calculate_availability.php
    \brief Extra PmWiki markup for calculating system availability

    Copyright 2006 Christian Ridderstrom (christian.ridderstrom@gmail.com)
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published
    by the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.    

    For documentation, see the recipe page at
	http://pmwiki.org/wiki/Cookbook/CalculateAvailability?action=edit


    Syntax synopsis:
	* Form 1: (:calc-avail <nn>% :)
	          Example: (:calc-avail 99.99% :)

	* Form 2: (:calc-avail mtbf=<MTBF><unit> mttr=<MTTR><unit> :)
		  Example: (:calc-avail mtbf=5.5y mttr=12h :)

	* Form 3: (:calc-avail :)
		  Example: (:calc-avail :)

    The arguments are used as follows
	<nn>	A number 0-100 of availability in percent, e.g. 99.9.
	%	The percent sign after the number is mandatory.

	<MTBF>	A number (e.g. 12.5) indicating mean time between failure. 
	<unit>	A mandatory character indicating the unit, where
		'y' = Year (365 days per week)
		'm' = Month (30 days per week)
		'w' = Week (7 days/per week)
		'd' = Day 
		'h' = Hour (24 hours per day)

	<MTTR>	A number (e.g. .5) indicating mean time to repeair.
        <unit>	A character indicating the unit, same as described above.
*/

$RecipeInfo['CalculateAvailability']['Version'] = '2006-12-02';

// Define the markup for (:calc-avail ... :)
Markup('calculate_availability', 'directives',
       '/\\(:calc-avail\\s+(.*?)\\s*:\\)/ei',
       "CalculateAvailability(\$pagename, PSS('$1'))");

// The array $CalculateAvailabilityD stores all global data
// used by this recipe. The memeber 'current' contains the
// currently estimated availability.
SDVA($CalculateAvailabilityD, array('current'=>1.0));
//
// Function: Calculate availability given arguments from the
// the directive (:calc-avail ... :). See the top of this
// script and the recipe page for details about the syntax and use.
//
// Note that actual calculations when using MTBF/MTTR are
// done in units as specified for the MTBF. This is to avoid
// numerical problems although it could probably be achieved
// using some other way.
//
function CalculateAvailability($pagename, $arguments) {
  global $CalculateAvailabilityD;
  $defaults = array('fmt'=>'default');
  $args = array_change_key_case(
     array_merge($defaults, ParseArgs($arguments)));

  if(array_key_exists('mttr', $args)) { // Use MTTR and MTBF
    $mttr = CalculateAvailabilityGetNumber($args['mttr']);
    $mtbf = CalculateAvailabilityGetNumber($args['mtbf']);
    if(!$mttr['unit'] || !$mtbf['unit'])
      return "<strong>Both mttr and mtbf must have units!</strong>";
    $u = $mtbf['unit'];
    $A = $mtbf[$u] / ($mtbf[$u] + $mttr[$u]);
    $CalculateAvailabilityD['current'] *= $A;
      
    $s = sprintf("(%-10s MTTR=%s)",
                 sprintf('MTBF=%s,', $args['mtbf']), $args['mttr']);
    $s .= sprintf(' %.3f%% ', $A*100);

  } elseif(count($args['']) == 0) {
    $s = sprintf('%.2f%% ', $CalculateAvailabilityD['current'] * 100);
    $CalculateAvailabilityD['current'] = 1.0;
  } else {
    $x = implode('', $args['']);
    if(substr($x, -1) != '%')
      return 
        "<strong>A separate availability argument must end in '%'</strong>";
    $A = $x * 0.01;
    $CalculateAvailabilityD['current'] *= $A;
    $s = sprintf('%.3f%% ', $A*100);
  }

  return "<span style='white-space:pre; font-size:smaller; font-family:monospace;'>$s</span>";
}

function CalculateAvailabilityGetNumber($x) {
  $toDays = array('h'=>1/24, 'd'=>1, 'w'=>7, 'm'=>30, 'y'=>365);
  if(preg_match('/([0-9.]+)([hHdDwWmMyY])/', $x, $matches)) {
    $d = $matches[1] * $toDays[strtolower($matches[2])];
    $res = array('h'=>$d*24, 'd'=>$d, 'w'=>$d/7, 'm'=>$d/30, 'y'=>$d/365,
                 'unit'=>strtolower($matches[2]));
  } else {
    $res = array('h'=>$x, 'unit' => false);
  }
  return $res;
}
?>