| 1 | #!/usr/bin/env python3 |
| 2 | # Desc: Pauses a random amount of time. Random distribution is inverse gaussian. |
| 3 | # Version: 0.0.6 |
| 4 | # Depends: python 3.7.3 |
| 5 | # Usage: ./sleepRand.py [-v] [-p P] SECONDS |
| 6 | # Input: SECONDS: float seconds (mean of inverse gaussian distribution) |
| 7 | # P: precision (lambda of inverse gaussian distribution) |
| 8 | # Example: python3 sleepRand.py -vv -p 8.0 60.0 |
| 9 | |
| 10 | import argparse; |
| 11 | import math, time, random, sys; |
| 12 | import logging; |
| 13 | |
| 14 | # Set up argument parser (see https://docs.python.org/3.7/library/argparse.html ) |
| 15 | parser = argparse.ArgumentParser( |
| 16 | description='Delay activity for a random number of seconds. Delays sampled from an inverse gaussian distribution.', |
| 17 | epilog="Author: Steven Baltakatei Sandoval. License: GPLv3+"); |
| 18 | parser.add_argument('-v','--verbose', |
| 19 | action='count', |
| 20 | dest='verbosity', |
| 21 | default=0, |
| 22 | help='Verbose output. (repeat for increased verbosity)'); |
| 23 | parser.add_argument('mean', |
| 24 | action='store', |
| 25 | metavar='SECONDS', |
| 26 | nargs=1, |
| 27 | default=1, |
| 28 | type=float, |
| 29 | help='Mean seconds of delay. Is the mean of the inverse gaussian distribution.'); |
| 30 | parser.add_argument('--precision','-p', |
| 31 | action='store', |
| 32 | metavar='P', |
| 33 | nargs=1, |
| 34 | default=[4.0], |
| 35 | type=float, |
| 36 | help='How concentrated delays are around the mean (default: 4.0). Must be a positive integer or floating point value. Is the lambda factor in the inverse gaussian distribution. High values (e.g. > 10.0) cause random delays to rarely stray far from MEAN. Small values (e.g. < 0.10) result in many small delays plus occasional long delays.'); |
| 37 | parser.add_argument('--upper','-u', |
| 38 | action='store', |
| 39 | metavar='U', |
| 40 | nargs=1, |
| 41 | default=[None], |
| 42 | type=float, |
| 43 | help='Upper bound for possible delays (default: no bound). Without bound, extremely high delays are unlikely but possible.'); |
| 44 | args = parser.parse_args(); |
| 45 | |
| 46 | # Define functions |
| 47 | def setup_logging(verbosity): |
| 48 | '''Sets up logging''' |
| 49 | # Depends: module: argparse |
| 50 | # Ref/Attrib: Haas, Florian; Configure logging with argparse; https://xahteiwi.eu/resources/hints-and-kinks/python-cli-logging-options/ |
| 51 | base_loglevel = 30; |
| 52 | verbosity = min(verbosity, 2); |
| 53 | loglevel = base_loglevel - (verbosity * 10); |
| 54 | logging.basicConfig(level=loglevel, |
| 55 | format='%(message)s'); |
| 56 | |
| 57 | def randInvGau(mu, lam): |
| 58 | """Returns random variate of inverse gaussian distribution""" |
| 59 | # input: mu: mean of inverse gaussian distribution |
| 60 | # lam: shape parameter |
| 61 | # output: float sampled from inv. gaus. with range 0 to infinity, mean mu |
| 62 | # example: sample = float(randInvGau(1.0,4.0)); |
| 63 | # Ref/Attrib: Michael, John R. "Generating Random Variates Using Transformations with Multiple Roots" https://doi.org/10.2307/2683801 |
| 64 | nu = random.gauss(0,1); |
| 65 | y = nu ** 2; |
| 66 | xTerm1 = mu; |
| 67 | xTerm2 = mu ** 2 * y / (2 * lam); |
| 68 | xTerm3 = (- mu / (2 * lam)) * math.sqrt(4 * mu * lam * y + mu ** 2 * y ** 2); |
| 69 | x = xTerm1 + xTerm2 + xTerm3; |
| 70 | z = random.uniform(0.0,1.0); |
| 71 | if z <= (mu / (mu + x)): |
| 72 | return x; |
| 73 | else: |
| 74 | return (mu ** 2 / x); |
| 75 | |
| 76 | # Process input |
| 77 | ## Start up logger |
| 78 | setup_logging(args.verbosity); |
| 79 | logging.debug('DEBUG:Debug logging output enabled.'); |
| 80 | logging.debug('DEBUG:args.verbosity:' + str(args.verbosity)); |
| 81 | logging.debug('DEBUG:args:' + str(args)); |
| 82 | |
| 83 | ## Receive input arguments |
| 84 | try: |
| 85 | ### Get desired mean |
| 86 | desMean = args.mean[0]; |
| 87 | logging.debug('DEBUG:Desired mean:' + str(desMean)); |
| 88 | |
| 89 | ### Get lambda precision factor |
| 90 | lambdaFactor = args.precision[0]; |
| 91 | logging.debug('DEBUG:Lambda precision factor:' + str(lambdaFactor)); |
| 92 | |
| 93 | ### Get upper bound |
| 94 | if isinstance(args.upper[0], float): |
| 95 | logging.debug('DEBUG:args.upper[0] is float:' + str(args.upper[0])); |
| 96 | upperBound = args.upper[0]; |
| 97 | elif args.upper[0] is None: |
| 98 | logging.debug('DEBUG:args.upper[0] is None:' + str(args.upper[0])); |
| 99 | upperBound = None; |
| 100 | else: |
| 101 | raise TypeError('Upper bound not set correctly.'); |
| 102 | logging.debug('DEBUG:Upper bound:' + str(upperBound)); |
| 103 | |
| 104 | ### Reject negative floats. |
| 105 | if desMean < 0: |
| 106 | logging.error('ERROR:Desired mean is negative:' + str(desMean)); |
| 107 | raise ValueError('Negative number error.'); |
| 108 | if lambdaFactor < 0: |
| 109 | logging.error('ERROR:Lambda precision factor is negative:' + str(lambdaFactor)); |
| 110 | raise ValueError('Negative number error.'); |
| 111 | except ValueError: |
| 112 | sys.exit(1); |
| 113 | |
| 114 | # Calculate delay |
| 115 | rawDelay = randInvGau(desMean, desMean * lambdaFactor); |
| 116 | logging.debug('DEBUG:rawDelay(seconds):' + str(rawDelay)); |
| 117 | if isinstance(upperBound,float): |
| 118 | delay = min(upperBound, rawDelay); |
| 119 | elif upperBound is None: |
| 120 | delay = rawDelay; |
| 121 | logging.debug('DEBUG:delay(seconds) :' + str(delay)); |
| 122 | |
| 123 | # Sleep |
| 124 | time.sleep(float(delay)); |
| 125 | |
| 126 | # Author: Steven Baltakatei Sandoal |
| 127 | # License: GPLv3+ |