3.3 Organization Model Implementation
This section contains the complete descriptions of the implementations
related to the proposed maintenance organization model presented in Section
2.3.
3.3.1 Organization Model Example
This section contains the code listing for the example organization model discussed in
Section 2.3.3.
#!/usr/bin/python import OrgModel # Simulation setup class ComponentTypes:
A = OrgModel.EquipmentComponentType("A", 200, 1.5, 2, 10000)
B = OrgModel.EquipmentComponentType("B", 400, 1.5, 4, 5000)
C = OrgModel.EquipmentComponentType("C", 600, 3.0, 6, 40000)
class UnitTypes: ARCHER = OrgModel.EquipmentUnitType("ARCHER",
[(1, ComponentTypes.A),
(2, ComponentTypes.B),
(2, ComponentTypes.C)])
g_evaluationScore = OrgModel.EvaluationScore()
g_components = OrgModel.EquipmentComponentObjects(g_evaluationScore)
g_units = OrgModel.EquipmentUnitObjects(g_evaluationScore,g_components)
# Setup for downstream location 1 policyDict1 = {
ComponentTypes.A : { "Max Stock Level" : 5,
"Reorder Level" : 2,
"Preventive Hazard Threshold" : 0.008},
ComponentTypes.B : { "Max Stock Level" : 5,
"Reorder Level" : 2,
"Preventive Hazard Threshold" : 0.008},
ComponentTypes.C : { "Max Stock Level" : 5,
"Reorder Level" : 2,
"Preventive Hazard Threshold" : 0.005} }
policy = OrgModel.MaintenancePolicyObject(policyDict1)
units = [ g_units.new(UnitTypes.ARCHER), g_units.new(UnitTypes.ARCHER),
g_units.new(UnitTypes.ARCHER), g_units.new(UnitTypes.ARCHER) ]
maintainers = [ OrgModel.MaintainerAgent("M1", g_evaluationScore, 200),
OrgModel.MaintainerAgent("M2", g_evaluationScore, 200) ]
spares = [] for k,v in policyDict1.iteritems(): n = v["Max Stock Level"]
while n > 0: spares.append(g_components.new(k))
n -= 1 location1 = OrgModel.LocationManagerAgent("L1",
g_evaluationScore,
spares,
units,
maintainers,
policy,
1.0, 10.0) # Setup for downstream location 2
policyDict2 = { ComponentTypes.A : { "Max Stock Level" : 5,
"Reorder Level" : 2,
"Preventive Hazard Threshold" : 0.008},
ComponentTypes.B : { "Max Stock Level" : 5,
"Reorder Level" : 2,
"Preventive Hazard Threshold" : 0.008},
ComponentTypes.C : { "Max Stock Level" : 5,
"Reorder Level" : 2,
"Preventive Hazard Threshold" : 0.005} }
units = [ g_units.new(UnitTypes.ARCHER), g_units.new(UnitTypes.ARCHER) ]
maintainers = [ OrgModel.MaintainerAgent("M3", g_evaluationScore, 200) ]
policy = OrgModel.MaintenancePolicyObject(policyDict2) spares = []
for k,v in policyDict2.iteritems(): n = v["Max Stock Level"]
while n > 0: spares.append(g_components.new(k))
n -= 1 location2 = OrgModel.LocationManagerAgent("L2",
g_evaluationScore,
spares,
units,
maintainers,
policy,
1.0, 10.0) # Setup for upstream location
spares = [] for t in [ComponentTypes.A,ComponentTypes.B,ComponentTypes.C]:
for i in range(policyDict1[t]["Max Stock Level"] +
policyDict2[t]["Max Stock Level"], 30):
spares.append(g_components.new(t))
upstream_loc = OrgModel.LocationManagerAgent("L0", g_evaluationScore, spares)
# Distribution Channels channel1 = OrgModel.DistributionChannelObject("DC1",
g_evaluationScore,
upstream_loc,
location1,
72.0,1000)
channel2 = OrgModel.DistributionChannelObject("DC2",
g_evaluationScore,
upstream_loc,
location2,
2.0,100) # Simulation loop t = 0
while t < 720: dt = 1.0 location1.step(dt) location2.step(dt)
upstream_loc.step(dt) channel1.step(dt) channel2.step(dt) t += 1
# Print results print "Spares status:" for l in [upstream_loc,location1,location2]:
print l.toStr() for c in [channel1,channel2]: print c.toStr()
print g_evaluationScore.toStr() print "Done after %d iterations"%(t,)
3.3.2 Organization Model Framework
This section contains the code listing for the proof of concept maintenance
organization model framework produced to examine the feasibility of the proposed
maintenance organization model discussed in 2.3.2.
import random ###################################################################
# Maintenance policy object
###################################################################
class MaintenancePolicyObject: def __init__(self, policy):
self.policyDict = policy def getMaxStockLevel(self, componentType):
return self.policyDict[componentType][ "Max Stock Level"]
def getReorderLevel(self, componentType):
return self.policyDict[componentType][ "Reorder Level"]
def getPrevThreshold(self, componentType):
return self.policyDict[componentType][ "Preventive Hazard Threshold"]
def keys(self): return self.policyDict.keys()
###################################################################
# Equipment component related classes
###################################################################
class EquipmentComponentType: def __init__(self, label, scale, shape, MTTR, cost):
self.scale = float(scale) self.shape = float(shape)
self.MTTR = float(MTTR) self.cost = cost self.label = label
def getHazardRate(self, age): return ((self.shape/self.scale) *
pow(age/self.scale,self.shape-1)) def getMTTR(self):
return self.MTTR class EquipmentComponentObject:
def __init__(self, id, evaluationScore, componentType, age=0):
self.id = id self.evaluationScore = evaluationScore
self.componentType = componentType self.age = float(age)
self.functioning = True self.used = False
def step(self, time): if not self.used:
self.used = True self.evaluationScore.addComponentCost(
self.componentType.cost) if (self.functioning):
if (pow(0.5, self.componentType.getHazardRate(self.age) *
float(time)) < random.random()):
self.functioning = False self.age += time
def isOk(self): return self.functioning def getType(self):
return self.componentType def getTTR(self):
return self.componentType.getMTTR() def getHazardRate(self):
return self.componentType.getHazardRate(self.age)
def toStr(self): return "%s(%d)"%(self.componentType.label,self.id)
class EquipmentComponentContainer(object): def __init__(self):
self.reset() def reset(self): self.components = dict()
self.componentsByType = dict() def addComponent(self, component):
if not self.hasComponent(component.id):
self.components[component.id] = component
if not component.getType() in self.componentsByType:
self.componentsByType[component.getType()] = []
self.componentsByType[ component.getType()].append(component)
def getComponent(self, componentId): return self.components[componentId]
def getComponents(self): return self.components.values()
def getComponentsByType(self, componentType):
if self.hasComponentType(componentType):
return self.componentsByType[componentType] return []
def hasComponent(self, componentId): return componentId in self.components
def hasComponentType(self, componentType):
return componentType in self.componentsByType
def removeComponent(self, component): cT = component.getType()
self.componentsByType[cT].remove(component)
if (len(self.componentsByType[cT]) == 0):
del self.componentsByType[cT] del self.components[component.id]
###################################################################
# Equipment unit related classes
###################################################################
class EquipmentUnitType: def __init__(self, label, componentDict):
self.componentDict = componentDict self.label = label
class EquipmentUnitStatus: OK = 0 PREVENTIVE = 1
CORRECTIVE = 2 class EquipmentUnitObject(EquipmentComponentContainer):
def __init__(self, id, evaluationScore, unitType, components):
super(EquipmentUnitObject,self).__init__() self.id = id
self.evaluationScore = evaluationScore self.unitType = unitType
for c in components: self.addComponent(c)
def isOk(self): ok = True for c in self.getComponents():
if not c.isOk(): ok = False break
return ok def getStatus(self, policy): if not self.isOk():
return EquipmentUnitStatus.CORRECTIVE else: prev = False
for c in self.getComponents(): if (c.getHazardRate() >
policy.getPrevThreshold(c.getType())):
prev = True break if prev:
return EquipmentUnitStatus.PREVENTIVE return EquipmentUnitStatus.OK
def getPreventiveComponents(self, policy): comps = []
for c in self.getComponents(): if (c.getHazardRate() >
policy.getPrevThreshold(c.getType())): comps.append(c)
return comps def getCorrectiveComponents(self): comps = []
for c in self.getComponents(): if not c.isOk():
comps.append(c) return comps def step(self,time):
if self.isOk(): self.evaluationScore.addAvailableHours(time)
for c in self.getComponents(): c.step(time)
else: self.evaluationScore.addUnavailableHours(time)
def toStr(self): return "%s(%d)"%(self.unitType.label,self.id)
###################################################################
# Maintainer Agent ###################################################################
class MaintainerAgent(EquipmentComponentContainer):
def __init__(self, label, evaluationScore, hourly_cost):
super(MaintainerAgent,self).__init__() self.label = label
self.evaluationScore = evaluationScore self.hourly_cost = hourly_cost
self.location = None self.time_to_next_action = 0.0
self.current_component = None self.unit = None
def setLocation(self, location): self.location = location
def isAvailable(self): return self.time_to_next_action <= 0.0
def assign(self, unit, setup_time): self.unit = unit
self.time_to_next_action = setup_time # Wait for setup
if (self.time_to_next_action > 0.0): return
# No setup time, start corrective maintenance
for c in unit.getCorrectiveComponents(): self.current_component = c
break if self.current_component is None:
# No setup or corrective maintennace, start preventive
for c in unit.getPreventiveComponents(
self.location.getPolicy()):
self.current_component = c break
self.time_to_next_action = self.current_component.getTTR()
def step(self, time): # Score keeping if self.unit is not None:
self.evaluationScore.addMaintainerCost(time*
self.hourly_cost)
self.evaluationScore.addUnavailableHours(time)
self.time_to_next_action -= time # Time to do something?
if (self.time_to_next_action <= 0.0 and self.unit is not None):
if self.current_component is not None:
# Replace repaired component
self.unit.removeComponent(self.current_component)
self.location.addComponent(self.current_component)
spare = None for c in self.getComponentsByType(
self.current_component.getType()): spare = c
break self.unit.addComponent(spare)
self.removeComponent(spare) self.current_component = None
# Find next corrective component to repair (if any)
for c in self.unit.getCorrectiveComponents():
self.current_component = c break
if self.current_component is None:
# No corrective, check for preventive
for c in self.unit.getPreventiveComponents(
self.location.getPolicy()):
if self.hasComponentType(c.getType()):
self.current_component = c
break if self.current_component is not None:
self.time_to_next_action = \
self.current_component.getTTR() else:
self.location.unAssignUnit(self.unit) self.unit = None
def toStr(self): return "MaintainerAgent %s"%(self.label,)
###################################################################
# Location manager agent ###################################################################
class LocationManagerAgent(EquipmentComponentContainer):
def __init__(self, label, evaluationScore, equipment_components,
equipment_units=[], maintainers=[],
policy=None, preventive_setup_time=0.0,
corrective_setup_time=0.0):
super(LocationManagerAgent,self).__init__()
self.label = label self.evaluationScore = evaluationScore
for c in equipment_components: self.addComponent(c)
self.maintainers = maintainers for m in self.maintainers:
m.setLocation(self) self.policy = policy
self.preventive_setup_time = preventive_setup_time
self.corrective_setup_time = corrective_setup_time
# Setup initial set of units self.assigned_units = []
self.available_units = [] self.corrective_queue = []
self.preventive_queue = [] for u in equipment_units:
status = u.getStatus(policy)
if status == EquipmentUnitStatus.CORRECTIVE:
self.corrective_queue.append(u) else:
if status == EquipmentUnitStatus.PREVENTIVE:
self.preventive_queue.append(u)
self.available_units.append(u)
# Setup component delivery handling self.received_requests = []
self.sent_requests = [] self.upstream_channel = None
def getPolicy(self): return self.policy
def getAnySpare(self, componentType): spare = None
for s in self.getComponentsByType(componentType):
if (s.isOk() and (spare is None or
s.getHazardRate() < spare.getHazardRate())): spare = s
if spare is not None: self.removeComponent(spare)
return spare def getNewSpare(self, componentType):
spare = None for s in self.getComponentsByType(componentType):
if (s.isOk() and (s.getHazardRate() <
self.policy.getPrevThreshold(componentType)) and
(spare is None or
s.getHazardRate() < spare.getHazardRate())): spare = s
if spare is not None: self.removeComponent(spare)
return spare def step(self, time):
# Step all available units for u in self.available_units:
u.step(time) # Accumulate downtime for queued broken units
for u in self.corrective_queue:
self.evaluationScore.addUnavailableHours(time)
# Step all maintainers for m in self.maintainers:
m.step(time) # Make maintenance decisions and allocate work
self.doMaintenance() # Handle delivery requests from downstream locations
self.doDeliveries() # Place component requests from upstream locations
self.doRequests() def doMaintenance(self):
# Check for broken units needing corrective maintenance
for u in self.available_units: status = u.getStatus(self.policy)
if status == EquipmentUnitStatus.CORRECTIVE:
self.corrective_queue.append(u)
elif (status == EquipmentUnitStatus.PREVENTIVE and
u not in self.preventive_queue):
self.preventive_queue.append(u)
for u in self.corrective_queue: if u in self.available_units:
self.available_units.remove(u) if u in self.preventive_queue:
self.preventive_queue.remove(u)
# Assign units for corrective maintenance
self.assignUnits(self.corrective_queue,
self.corrective_setup_time, True)
# Assign units for preventive maintenance
self.assignUnits(self.preventive_queue,
self.preventive_setup_time, False)
def assignUnits(self, queue, setup_time, is_corrective):
# Assign units to available maintainers is possible
for u in queue: # Check for an available maintainer
available_maintainer = None for m in self.maintainers:
if m.isAvailable(): available_maintainer = m
break # Check for spares
has_spares = True if available_maintainer is not None:
spares = [] if is_corrective:
for c in u.getCorrectiveComponents():
s = self.getAnySpare(c.getType())
if s is not None: spares.append(s)
else: has_spares = False
break else:
for c in u.getPreventiveComponents(self.policy):
s = self.getNewSpare(c.getType())
if s is not None:
spares.append(s) else:
has_spares = False break
if has_spares: for s in spares:
available_maintainer.addComponent(s) else:
for s in spares: self.addComponent(s)
continue # Assign maintenance
if has_spares and available_maintainer is not None:
available_maintainer.assign(u, setup_time)
self.assigned_units.append(u)
# Unqueue units that are currently being maintained
for u in self.assigned_units: if u in self.corrective_queue:
self.corrective_queue.remove(u)
if u in self.preventive_queue: self.preventive_queue.remove(u)
if u in self.available_units: self.available_units.remove(u)
def unAssignUnit(self, unit): self.assigned_units.remove(unit)
self.available_units.append(unit) def doDeliveries(self):
completedRequests = [] for request in self.received_requests:
# Find and deliver any spares matching the request
delivery = [] for c in request[1]:
s = self.getAnySpare(c) if s is not None:
delivery.append(s) if len(delivery) > 0:
request[0].sendComponents(delivery)
# Remove already delivered spares from the request
for c in delivery: request[1].remove(c.getType())
# Remove request when everything has been delivered
if len(request[1]) == 0: completedRequests.append(request)
for r in completedRequests: self.received_requests.remove(r)
def receiveComponentsRequest(self, channel, componentsList):
self.received_requests.append((channel, componentsList))
def receiveDeliveredComponents(self, components): for c in components:
self.addComponent(c) self.sent_requests.remove(c.getType())
def setUpstreamChannel(self, upstream_channel):
self.upstream_channel = upstream_channel def doRequests(self):
# Request any spares if self.upstream_channel is not None:
request = [] for t in self.policy.keys():
# How many do we have? n = 0;
for c in self.getComponentsByType(t):
if (c.isOk() and (c.getHazardRate() <
self.policy.getPrevThreshold(t))):
n += 1 # How many are on order?
for ct in self.sent_requests: if ct == t:
n += 1 # How many do we need?
if n <= self.policy.getReorderLevel(t):
for i in xrange(n,
self.policy.getMaxStockLevel(t)):
request.append(t)
self.sent_requests.append(t) if len(request) > 0:
self.upstream_channel.requestComponents(request)
def toStr(self): s = "%s: "%(self.label,)
component_types = set() for c in self.getComponents():
if not c.getType() in component_types:
component_types.add(c.getType())
for t in component_types: c = 0 p = 0
d = 0 for comp in self.getComponentsByType(t):
d += 1 if not comp.isOk():
c += 1 elif (self.policy is not None and
(comp.getHazardRate() >
self.policy.getPrevThreshold(t))): p += 1
s += "%s %d/%d/%d\t"%(t.label,c,p,d) return s
###################################################################
# Distribution channel object
###################################################################
class DistributionChannelObject: def __init__(self, label, evaluationScore,
upstream, downstream, lead_time, cost):
self.label = label self.evaluationScore = evaluationScore
self.upstream = upstream self.downstream = downstream
downstream.setUpstreamChannel(self)
self.lead_time = float(lead_time) self.cost = cost
self.transports = [] def requestComponents(self, componentsList):
self.upstream.receiveComponentsRequest(
self, componentsList) def sendComponents(self, components):
self.transports.append([self.lead_time, components])
def step(self, time): delivered = [] for t in self.transports:
self.evaluationScore.addTransportCost(time*self.cost)
t[0] -= time if t[0] <= 0.0:
self.downstream.receiveDeliveredComponents(t[1])
delivered.append(t) for t in delivered:
self.transports.remove(t) def getComponents(self):
comps = [] for t in self.transports: for c in t[1]:
comps.append(c) return comps def toStr(self):
return "%s: %d"%(self.label,len(self.getComponents()))
###################################################################
# Resource tracking classes
###################################################################
class EquipmentComponentObjects(EquipmentComponentContainer):
def __init__(self, evaluationScore):
super(EquipmentComponentObjects,self).__init__()
self.reset() self.evaluationScore = evaluationScore
def reset(self): super(EquipmentComponentObjects,self).reset()
self.ctr = 0 def new(self, componentType, age=0):
eco = EquipmentComponentObject(self.ctr,
self.evaluationScore,
componentType,
age) self.addComponent(eco)
self.ctr = self.ctr + 1 return eco class EquipmentUnitObjects:
def __init__(self, evaluationScore, components_container):
self.reset() self.evaluationScore = evaluationScore
self.components_container = components_container def reset(self):
self.ctr = 0 self.objects = [] def new(self, unitType):
components = [] for n,t in unitType.componentDict:
for i in xrange(n): components.append(
self.components_container.new(t))
eqUnit = EquipmentUnitObject(self.ctr,
self.evaluationScore,
unitType,
components)
self.objects.append(eqUnit) self.ctr = self.ctr + 1
return eqUnit def get(self, id): return self.objects[id]
###################################################################
# Evaluation score ###################################################################
class EvaluationScore: def __init__(self):
self.reset() def reset(self):
self.maintainer_cost = 0.0 self.component_cost = 0.0
self.transport_cost = 0.0 self.available_hours = 0.0
self.unavailable_hours = 0.0 def addMaintainerCost(self, cost):
self.maintainer_cost += cost def addComponentCost(self, cost):
self.component_cost += cost def addTransportCost(self, cost):
self.transport_cost += cost def addAvailableHours(self, hrs):
self.available_hours += hrs def addUnavailableHours(self, hrs):
self.unavailable_hours += hrs def getCost(self):
return self.maintainer_cost + \ self.component_cost + \
self.transport_cost def getAvailability(self):
return self.available_hours/(self.available_hours +
self.unavailable_hours) def toStr(self):
s = "Evaluation Score\n" s += "----------------\n"
s += " Maintainers: %d\n"%(self.maintainer_cost,)
s += " Components: %d\n"%(self.component_cost,)
s += " Transport: %d\n"%(self.transport_cost,)
s += "Total cost: %d\n"%(self.getCost(),)
s += " Available hours: %d\n"%(self.available_hours,)
s += " Unavailable hours: %d\n"%(self.unavailable_hours,)
s += "Overall availability: %d%%\n"%(
self.getAvailability()*100,) return s