Commit 6f78d410 authored by Georgios Dagkakis's avatar Georgios Dagkakis Committed by Jérome Perrin

outputTrace method moved in CoreObject. this way new CoreObjects wont have to...

outputTrace method moved in CoreObject. this way new CoreObjects wont have to implement their versions. Moreover, if they call getEntity and removeEntity from CoreObject trace will be outputted
parent 612e2ac7
...@@ -184,7 +184,6 @@ class Assembly(CoreObject): ...@@ -184,7 +184,6 @@ class Assembly(CoreObject):
#removes an entity from the Assembly #removes an entity from the Assembly
def removeEntity(self): def removeEntity(self):
activeEntity=CoreObject.removeEntity(self) #run the default method activeEntity=CoreObject.removeEntity(self) #run the default method
self.outputTrace(activeEntity.name, "releases "+ self.objName) #output trace
self.waitToDispose=False self.waitToDispose=False
return activeEntity #the object does not wait to dispose now return activeEntity #the object does not wait to dispose now
...@@ -240,22 +239,6 @@ class Assembly(CoreObject): ...@@ -240,22 +239,6 @@ class Assembly(CoreObject):
self.Working.append(100*self.totalWorkingTime/MaxSimtime) self.Working.append(100*self.totalWorkingTime/MaxSimtime)
self.Blockage.append(100*self.totalBlockageTime/MaxSimtime) self.Blockage.append(100*self.totalBlockageTime/MaxSimtime)
#outputs message to the trace.xls. Format is (Simulation Time | Entity or Frame Name | message)
def outputTrace(self, name, message):
from Globals import G
if(G.trace=="Yes"): #output only if the user has selected to
#handle the 3 columns
G.traceSheet.write(G.traceIndex,0,str(now()))
G.traceSheet.write(G.traceIndex,1,name)
G.traceSheet.write(G.traceIndex,2,message)
G.traceIndex+=1 #increment the row
#if we reach row 65536 we need to create a new sheet (excel limitation)
if(G.traceIndex==65536):
G.traceIndex=0
G.sheetIndex+=1
G.traceSheet=G.traceFile.add_sheet('sheet '+str(G.sheetIndex), cell_overwrite_ok=True)
#outputs data to "output.xls" #outputs data to "output.xls"
def outputResultsXL(self, MaxSimtime=None): def outputResultsXL(self, MaxSimtime=None):
from Globals import G from Globals import G
......
...@@ -207,7 +207,6 @@ class Conveyer(CoreObject): ...@@ -207,7 +207,6 @@ class Conveyer(CoreObject):
def getEntity(self): def getEntity(self):
activeEntity=CoreObject.getEntity(self) activeEntity=CoreObject.getEntity(self)
self.position.append(0) #the entity is placed in the start of the conveyer self.position.append(0) #the entity is placed in the start of the conveyer
self.outputTrace(activeEntity.name, "got into "+ self.objName) #output trace
#check if the conveyer became full to start counting blockage #check if the conveyer became full to start counting blockage
if self.isFull(): if self.isFull():
self.timeBlockageStarted=now() self.timeBlockageStarted=now()
...@@ -218,7 +217,6 @@ class Conveyer(CoreObject): ...@@ -218,7 +217,6 @@ class Conveyer(CoreObject):
#removes an entity from the Conveyer #removes an entity from the Conveyer
def removeEntity(self): def removeEntity(self):
activeEntity=CoreObject.removeEntity(self) #run the default method activeEntity=CoreObject.removeEntity(self) #run the default method
self.outputTrace(activeEntity.name, "releases "+ self.objName)
self.position.pop(0) self.position.pop(0)
self.waitToDispose=False self.waitToDispose=False
...@@ -283,22 +281,6 @@ class Conveyer(CoreObject): ...@@ -283,22 +281,6 @@ class Conveyer(CoreObject):
self.Working.append(100*self.totalWorkingTime/MaxSimtime) self.Working.append(100*self.totalWorkingTime/MaxSimtime)
self.Blockage.append(100*self.totalBlockageTime/MaxSimtime) self.Blockage.append(100*self.totalBlockageTime/MaxSimtime)
#outputs message to the trace.xls. Format is (Simulation Time | Entity or Frame Name | message)
def outputTrace(self, name, message):
from Globals import G
if(G.trace=="Yes"): #output only if the user has selected to
#handle the 3 columns
G.traceSheet.write(G.traceIndex,0,str(now()))
G.traceSheet.write(G.traceIndex,1,name)
G.traceSheet.write(G.traceIndex,2,message)
G.traceIndex+=1 #increment the row
#if we reach row 65536 we need to create a new sheet (excel limitation)
if(G.traceIndex==65536):
G.traceIndex=0
G.sheetIndex+=1
G.traceSheet=G.traceFile.add_sheet('sheet '+str(G.sheetIndex), cell_overwrite_ok=True)
#outputs data to "output.xls" #outputs data to "output.xls"
def outputResultsXL(self, MaxSimtime=None): def outputResultsXL(self, MaxSimtime=None):
from Globals import G from Globals import G
......
...@@ -78,6 +78,11 @@ class CoreObject(Process): ...@@ -78,6 +78,11 @@ class CoreObject(Process):
activeObjectQueue=self.getActiveObjectQueue() activeObjectQueue=self.getActiveObjectQueue()
activeEntity=activeObjectQueue[0] activeEntity=activeObjectQueue[0]
activeObjectQueue.pop(0) #remove the Entity from the queue activeObjectQueue.pop(0) #remove the Entity from the queue
try:
self.outputTrace(activeEntity.name, "released "+self.objName)
except TypeError:
pass
return activeEntity return activeEntity
# ================================== gets an entity from the ==================================== # ================================== gets an entity from the ====================================
...@@ -102,6 +107,10 @@ class CoreObject(Process): ...@@ -102,6 +107,10 @@ class CoreObject(Process):
# the entity enters a new object # the entity enters a new object
activeEntity.schedule.append([activeObject.id,now()]) activeEntity.schedule.append([activeObject.id,now()])
activeEntity.currentStation=self activeEntity.currentStation=self
try:
self.outputTrace(activeEntity.name, "got into "+self.objName)
except TypeError:
pass
return activeEntity return activeEntity
# ========================== actions to be taken after the simulation ends ====================== # ========================== actions to be taken after the simulation ends ======================
...@@ -109,8 +118,20 @@ class CoreObject(Process): ...@@ -109,8 +118,20 @@ class CoreObject(Process):
pass pass
# =========================== outputs message to the trace.xls ================================== # =========================== outputs message to the trace.xls ==================================
def outputTrace(self, message): #outputs message to the trace.xls. Format is (Simulation Time | Entity or Frame Name | message)
pass def outputTrace(self, entityName, message):
from Globals import G
if(G.trace=="Yes"): #output only if the user has selected to
#handle the 3 columns
G.traceSheet.write(G.traceIndex,0,str(now()))
G.traceSheet.write(G.traceIndex,1,entityName)
G.traceSheet.write(G.traceIndex,2,message)
G.traceIndex+=1 #increment the row
#if we reach row 65536 we need to create a new sheet (excel limitation)
if(G.traceIndex==65536):
G.traceIndex=0
G.sheetIndex+=1
G.traceSheet=G.traceFile.add_sheet('sheet '+str(G.sheetIndex), cell_overwrite_ok=True)
# =========================== outputs data to "output.xls" ====================================== # =========================== outputs data to "output.xls" ======================================
def outputResultsXL(self, MaxSimtime=None): def outputResultsXL(self, MaxSimtime=None):
......
...@@ -176,15 +176,12 @@ class Dismantle(CoreObject): ...@@ -176,15 +176,12 @@ class Dismantle(CoreObject):
#move the frame to the end of the internal queue since we want the frame to be disposed first #move the frame to the end of the internal queue since we want the frame to be disposed first
activeObjectQueue.append(activeEntity) activeObjectQueue.append(activeEntity)
activeObjectQueue.pop(0) activeObjectQueue.pop(0)
self.outputTrace(activeEntity.name, "got into "+ self.objName)
return activeEntity return activeEntity
#removes an entity from the Dismantle #removes an entity from the Dismantle
def removeEntity(self): def removeEntity(self):
activeObjectQueue=self.getActiveObjectQueue() activeObjectQueue=self.getActiveObjectQueue()
activeEntity=CoreObject.removeEntity(self) #run the default method activeEntity=CoreObject.removeEntity(self) #run the default method
self.outputTrace(activeEntity.name, " releases "+ self.objName) #output the trace
#update the flags #update the flags
if(len(activeObjectQueue)==0): if(len(activeObjectQueue)==0):
self.waitToDisposeFrame=False self.waitToDisposeFrame=False
...@@ -193,22 +190,6 @@ class Dismantle(CoreObject): ...@@ -193,22 +190,6 @@ class Dismantle(CoreObject):
self.waitToDisposePart=False self.waitToDisposePart=False
return activeEntity return activeEntity
#outputs message to the trace.xls. Format is (Simulation Time | Entity or Frame Name | message)
def outputTrace(self, name, message):
from Globals import G
if(G.trace=="Yes"): #output only if the user has selected to
#handle the 3 columns
G.traceSheet.write(G.traceIndex,0,str(now()))
G.traceSheet.write(G.traceIndex,1,name)
G.traceSheet.write(G.traceIndex,2,message)
G.traceIndex+=1 #increment the row
#if we reach row 65536 we need to create a new sheet (excel limitation)
if(G.traceIndex==65536):
G.traceIndex=0
G.sheetIndex+=1
G.traceSheet=G.traceFile.add_sheet('sheet '+str(G.sheetIndex), cell_overwrite_ok=True)
#actions to be taken after the simulation ends #actions to be taken after the simulation ends
def postProcessing(self, MaxSimtime=None): def postProcessing(self, MaxSimtime=None):
if MaxSimtime==None: if MaxSimtime==None:
......
...@@ -146,7 +146,6 @@ class Exit(CoreObject): ...@@ -146,7 +146,6 @@ class Exit(CoreObject):
def getEntity(self): def getEntity(self):
activeEntity = CoreObject.getEntity(self) #run the default method activeEntity = CoreObject.getEntity(self) #run the default method
self.totalLifespan+=now()-activeEntity.startTime #Add the entity's lifespan to the total one. self.totalLifespan+=now()-activeEntity.startTime #Add the entity's lifespan to the total one.
self.outputTrace(activeEntity.name)
return activeEntity return activeEntity
# ======================================================================= # =======================================================================
...@@ -168,23 +167,6 @@ class Exit(CoreObject): ...@@ -168,23 +167,6 @@ class Exit(CoreObject):
self.TaktTime.append(0) self.TaktTime.append(0)
# ======================================================================= # =======================================================================
# outputs message to the trace.xls.
# Format is (Simulation Time | Entity Name | "generated")
# =======================================================================
def outputTrace(self, message):
from Globals import G
if(G.trace=="Yes"): #output only if the user has selected to
#handle the 3 columns
G.traceSheet.write(G.traceIndex,0,str(now()))
G.traceSheet.write(G.traceIndex,1,message)
G.traceSheet.write(G.traceIndex,2,"exited the system")
G.traceIndex+=1 #increment the row
#if we reach row 65536 we need to create a new sheet (excel limitation)
if(G.traceIndex==65536):
G.traceIndex=0
G.sheetIndex+=1
G.traceSheet=G.traceFile.add_sheet('sheet '+str(G.sheetIndex), cell_overwrite_ok=True)
# =======================================================================
# outputs data to "output.xls" # outputs data to "output.xls"
# ======================================================================= # =======================================================================
def outputResultsXL(self, MaxSimtime=None): def outputResultsXL(self, MaxSimtime=None):
......
{
"_class": "Dream.Simulation",
"edges": {
"0": [
"S1",
"Q1",
{}
],
"1": [
"Q1",
"BD1",
{}
],
"2": [
"BD1",
"M1",
{}
],
"3": [
"M1",
"Q2",
{}
],
"4": [
"Q2",
"M2",
{}
],
"5": [
"M2",
"BR1",
{}
],
"6": [
"BR1",
"Q3",
{}
],
"7": [
"Q3",
"M3",
{}
],
"8": [
"M3",
"E1",
{}
]
},
"general": {
"_class": "Dream.Configuration",
"confidenceLevel": "0.95",
"maxSimTime": "150.0",
"numberOfReplications": "1",
"trace": "Yes"
},
"nodes": {
"S1": {
"_class": "Dream.BatchSource",
"entity": "Batch",
"batchNumberOfUnits": 100,
"interarrivalTime": {
"distributionType": "Fixed",
"mean": "0.5"
},
"left": 0.6968085106382979,
"name": "Source",
"top": 0.9545454545454546
},
"Q1": {
"_class": "Dream.Queue",
"capacity": "-1",
"left": 0.6968085106382979,
"name": "StartQueue",
"top": 0.7727272727272727
},
"Q2": {
"_class": "Dream.LineClearance",
"capacity": "2",
"left": 0.6968085106382979,
"name": "Queue1",
"top": 0.7727272727272727
},
"Q3": {
"_class": "Dream.Queue",
"capacity": "1",
"left": 0.6968085106382979,
"name": "Queue2",
"top": 0.7727272727272727
},
"M1": {
"_class": "Dream.BatchScrapMachine",
"left": 0.4414893617021277,
"name": "Station1",
"failures": {
},
"processingTime": {
"distributionType": "Fixed",
"mean": "0.1"
},
"scrapQuantity":{
"distributionType": "Fixed",
"mean": "1"
},
"top": 0.5909090909090908
},
"M2": {
"_class": "Dream.BatchScrapMachine",
"left": 0.4414893617021277,
"name": "Station2",
"failures": {
},
"processingTime": {
"distributionType": "Fixed",
"mean": "0.2"
},
"scrapQuantity":{
"distributionType": "Fixed",
"mean": "1"
},
"top": 0.5909090909090908
},
"M3": {
"_class": "Dream.BatchScrapMachine",
"left": 0.4414893617021277,
"name": "Station3",
"failures": {
},
"processingTime": {
"distributionType": "Fixed",
"mean": "0.25"
},
"scrapQuantity":{
"distributionType": "Fixed",
"mean": "2"
},
"top": 0.5909090909090908
},
"BD1": {
"_class": "Dream.BatchDecomposition",
"left": 0.4414893617021277,
"name": "Batch_Decomposition",
"numberOfSubBatches": 4,
"processingTime": {
"distributionType": "Fixed",
"mean": "1"
},
"top": 0.5909090909090908
},
"BR1": {
"_class": "Dream.BatchReassembly",
"left": 0.4414893617021277,
"name": "Batch_Reassembly",
"numberOfSubBatches": 4,
"processingTime": {
"distributionType": "Fixed",
"mean": "0"
},
"top": 0.5909090909090908
},
"E1": {
"_class": "Dream.Exit",
"left": 0.2978723404255319,
"name": "Stock",
"top": 0.045454545454545414
}
}
}
\ No newline at end of file
...@@ -103,8 +103,6 @@ class Machine(CoreObject): ...@@ -103,8 +103,6 @@ class Machine(CoreObject):
yield waituntil, self, self.canAcceptAndIsRequested yield waituntil, self, self.canAcceptAndIsRequested
# get the entity from the predecessor # get the entity from the predecessor
self.getEntity() self.getEntity()
# output to whenever an entity enters the Machine (self.objName)
self.outputTrace("got into "+self.objName)
# set the currentEntity as the Entity just received and initialize the timer timeLastEntityEntered # set the currentEntity as the Entity just received and initialize the timer timeLastEntityEntered
self.currentEntity=self.getActiveObjectQueue()[0] # entity is the current entity processed in Machine self.currentEntity=self.getActiveObjectQueue()[0] # entity is the current entity processed in Machine
self.nameLastEntityEntered=self.currentEntity.name # this holds the name of the last entity that got into Machine self.nameLastEntityEntered=self.currentEntity.name # this holds the name of the last entity that got into Machine
...@@ -138,7 +136,7 @@ class Machine(CoreObject): ...@@ -138,7 +136,7 @@ class Machine(CoreObject):
yield hold,self,tinM # getting processed for remaining processing time tinM yield hold,self,tinM # getting processed for remaining processing time tinM
if self.interrupted(): # if a failure occurs while processing the machine is interrupted. if self.interrupted(): # if a failure occurs while processing the machine is interrupted.
# output to trace that the Machine (self.objName) got interrupted # output to trace that the Machine (self.objName) got interrupted
self.outputTrace("Interrupted at "+self.objName) self.outputTrace(self.getActiveObjectQueue()[0].name, "Interrupted at "+self.objName)
# recalculate the processing time left tinM # recalculate the processing time left tinM
tinM=tinM-(now()-tBefore) tinM=tinM-(now()-tBefore)
if(tinM==0): # sometimes the failure may happen exactly at the time that the processing would finish if(tinM==0): # sometimes the failure may happen exactly at the time that the processing would finish
...@@ -156,12 +154,12 @@ class Machine(CoreObject): ...@@ -156,12 +154,12 @@ class Machine(CoreObject):
self.timeLastFailureEnded=now() # set the timeLastFailureEnded self.timeLastFailureEnded=now() # set the timeLastFailureEnded
failureTime+=now()-breakTime # dummy variable keeping track of the failure time failureTime+=now()-breakTime # dummy variable keeping track of the failure time
# output to trace that the Machine self.objName was passivated for the current failure time # output to trace that the Machine self.objName was passivated for the current failure time
self.outputTrace("passivated in "+self.objName+" for "+str(now()-breakTime)) self.outputTrace(self.getActiveObjectQueue()[0].name, "passivated in "+self.objName+" for "+str(now()-breakTime))
# if no interruption occurred the processing in M1 is ended # if no interruption occurred the processing in M1 is ended
else: else:
processingEndedFlag=False processingEndedFlag=False
# output to trace that the processing in the Machine self.objName ended # output to trace that the processing in the Machine self.objName ended
self.outputTrace("ended processing in "+self.objName) self.outputTrace(self.getActiveObjectQueue()[0].name,"ended processing in "+self.objName)
# set the variable that flags an Entity is ready to be disposed # set the variable that flags an Entity is ready to be disposed
self.waitToDispose=True self.waitToDispose=True
# update the total working time # update the total working time
...@@ -290,7 +288,7 @@ class Machine(CoreObject): ...@@ -290,7 +288,7 @@ class Machine(CoreObject):
# ======================================================================= # =======================================================================
def removeEntity(self): def removeEntity(self):
activeObject=self.getActiveObject() activeObject=self.getActiveObject()
activeObject.outputTrace("releases "+activeObject.objName) # output to trace that the Entity was released from the currentObject #activeObject.outputTrace("releases "+activeObject.objName) # output to trace that the Entity was released from the currentObject
activeEntity=CoreObject.removeEntity(self) #run the default method activeEntity=CoreObject.removeEntity(self) #run the default method
activeObject.timeLastEntityLeft=now() # set the time that the last Entity was removed from this object activeObject.timeLastEntityLeft=now() # set the time that the last Entity was removed from this object
activeObject.waitToDispose=False # update the waitToDispose flag activeObject.waitToDispose=False # update the waitToDispose flag
...@@ -390,11 +388,12 @@ class Machine(CoreObject): ...@@ -390,11 +388,12 @@ class Machine(CoreObject):
activeObject.Waiting.append(100*self.totalWaitingTime/MaxSimtime) activeObject.Waiting.append(100*self.totalWaitingTime/MaxSimtime)
activeObject.Working.append(100*self.totalWorkingTime/MaxSimtime) activeObject.Working.append(100*self.totalWorkingTime/MaxSimtime)
'''
# ======================================================================= # =======================================================================
# outputs message to the trace.xls. # outputs message to the trace.xls.
# Format is (Simulation Time | Entity Name | message) # Format is (Simulation Time | Entity Name | message)
# ======================================================================= # =======================================================================
def outputTrace(self, message): def outputTrace(self, name, message):
from Globals import G from Globals import G
if(G.trace=="Yes"): #output only if the user has selected to if(G.trace=="Yes"): #output only if the user has selected to
#handle the 3 columns #handle the 3 columns
...@@ -408,7 +407,7 @@ class Machine(CoreObject): ...@@ -408,7 +407,7 @@ class Machine(CoreObject):
G.traceIndex=0 G.traceIndex=0
G.sheetIndex+=1 G.sheetIndex+=1
G.traceSheet=G.traceFile.add_sheet('sheet '+str(G.sheetIndex), cell_overwrite_ok=True) G.traceSheet=G.traceFile.add_sheet('sheet '+str(G.sheetIndex), cell_overwrite_ok=True)
'''
# ======================================================================= # =======================================================================
# outputs the the "output.xls" # outputs the the "output.xls"
# ======================================================================= # =======================================================================
......
...@@ -154,7 +154,6 @@ class Queue(CoreObject): ...@@ -154,7 +154,6 @@ class Queue(CoreObject):
def removeEntity(self): def removeEntity(self):
activeObject=self.getActiveObject() activeObject=self.getActiveObject()
activeEntity=CoreObject.removeEntity(self) #run the default method activeEntity=CoreObject.removeEntity(self) #run the default method
activeObject.outputTrace(activeEntity.name, "releases "+activeObject.objName) #output trace
return activeEntity return activeEntity
# ======================================================================= # =======================================================================
# checks if the Queue can accept an entity and # checks if the Queue can accept an entity and
...@@ -196,7 +195,6 @@ class Queue(CoreObject): ...@@ -196,7 +195,6 @@ class Queue(CoreObject):
# ======================================================================= # =======================================================================
def getEntity(self): def getEntity(self):
activeEntity=CoreObject.getEntity(self) #run the default behavior activeEntity=CoreObject.getEntity(self) #run the default behavior
self.outputTrace(activeEntity.name, "got into "+self.objName)
return activeEntity return activeEntity
# ======================================================================= # =======================================================================
# sorts the Entities of the Queue according to the scheduling rule # sorts the Entities of the Queue according to the scheduling rule
...@@ -258,20 +256,3 @@ class Queue(CoreObject): ...@@ -258,20 +256,3 @@ class Queue(CoreObject):
entity.nextQueueLength=len(nextObject.Res.activeQ) entity.nextQueueLength=len(nextObject.Res.activeQ)
activeObjectQ.sort(key=lambda x: x.nextQueueLength) activeObjectQ.sort(key=lambda x: x.nextQueueLength)
\ No newline at end of file
# =======================================================================
# outputs message to the trace.xls.
# Format is (Simulation Time | Entity Name | message)
# =======================================================================
def outputTrace(self, entityName, message):
from Globals import G
if(G.trace=="Yes"): #output only if the user has selected to
#handle the 3 columns
G.traceSheet.write(G.traceIndex,0,str(now()))
G.traceSheet.write(G.traceIndex,1,entityName)
G.traceSheet.write(G.traceIndex,2,message)
G.traceIndex+=1 #increment the row
#if we reach row 65536 we need to create a new sheet (excel limitation)
if(G.traceIndex==65536):
G.traceIndex=0
G.sheetIndex+=1
G.traceSheet=G.traceFile.add_sheet('sheet '+str(G.sheetIndex), cell_overwrite_ok=True)
...@@ -109,8 +109,7 @@ class Source(CoreObject): ...@@ -109,8 +109,7 @@ class Source(CoreObject):
entity.creationTime=now() # assign the current simulation time as the Entity's creation time entity.creationTime=now() # assign the current simulation time as the Entity's creation time
entity.startTime=now() # assign the current simulation time as the Entity's start time entity.startTime=now() # assign the current simulation time as the Entity's start time
entity.currentStation=self # update the current station of the Entity entity.currentStation=self # update the current station of the Entity
self.outputTrace(self.item.type+\ self.outputTrace(entity.name, "generated") # output the trace
str(self.numberOfArrivals)) # output the trace
activeObjectQueue.append(entity) # append the entity to the resource activeObjectQueue.append(entity) # append the entity to the resource
self.numberOfArrivals+=1 # we have one new arrival self.numberOfArrivals+=1 # we have one new arrival
G.numberOfEntities+=1 G.numberOfEntities+=1
...@@ -134,17 +133,3 @@ class Source(CoreObject): ...@@ -134,17 +133,3 @@ class Source(CoreObject):
# outputs message to the trace.xls. # outputs message to the trace.xls.
# Format is (Simulation Time | Entity Name | "generated") # Format is (Simulation Time | Entity Name | "generated")
#============================================================================ #============================================================================
def outputTrace(self, message):
from Globals import G
if(G.trace=="Yes"): # output only if the user has selected to
# handle the 3 columns
G.traceSheet.write(G.traceIndex,0,str(now()))
G.traceSheet.write(G.traceIndex,1,message)
G.traceSheet.write(G.traceIndex,2,"generated")
G.traceIndex+=1 #increment the row
# if we reach row 65536 we need to create a new sheet (excel limitation)
if(G.traceIndex==65536):
G.traceIndex=0
G.sheetIndex+=1
G.traceSheet=G.traceFile.add_sheet('sheet '+str(G.sheetIndex), cell_overwrite_ok=True)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment