unit CORE; {Ядро модели системы массового обслуживания}
                            INTERFACE
type       
 PSource  = ^CSource; {Указатель на источник}
 PDevice  = ^CDevice; {Указатель на систему обработки (устройство) }
 PBuffer  = ^CBuffer; {Указатель на буфер запросов}

{-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-}

TReq = record  {Запрос на обслуживание}
  Birth : real; {Время появления запроса}
  Death : real; {Время смерти запроса (обработан, либо отклонен)}
  FromSource : PSource; {Кто просил-то ?}
  ToDevice   : PDevice; {А собственно куда ?}
end;
{-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-}


{$R-}
TReqArray = array [0..1000] of TReq;
PReqArray = ^TReqArray;
TSourceArr = array [0..1000] of PSource;
PSourceArr = ^TSourceArr;
PReq = ^TReq;
{-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-}


ESOURCES = (SOURCE_DUMMY,SOURCE_EASIEST,SOURCE_ERLANG); {Типы источников}

CSource = object   {Объект - простейший источник}
  SType : ESOURCES; {Каков наш источник? }
  Name  : integer;  {А как его зовут ?}

  LastTime : real;  {Время генерации последнего запроса}
  LastReq  : TReq;  {Последний сгенерированный запрос}

  NewTime  : real;  {Момент генерации нового запроса}
  Lambda   : real;

  TotalReq : longInt; {Общее число заявок от источника}
  DoneReq : longInt; {число обработанных заявок от источника}  
  RefusedReq:LongInt; {Число отклоненных заявок}
  WaitTime : real; {Общее время пребывания в системе}
  ProbRefuse:real; {Вероятность отказа} 
  MatWait  : real; {Математическое ожидание в буфере}

  Constructor Init(aName : Integer;aLambda:real); {Конструктор с именем}
  Procedure Reset; {Сброс всех счетчиков}
  Procedure GenNewReq; {Генерация нового запроса}
  Destructor Done;virtual;
end;{CSource}

{-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-}

CDevice = object   {Объект - простейшее устойство массового обслуживания}
  LastTime : real; {Время последнего завершения обслуживания}

  BegTime  : real; {Время начала обслуживания}
  DoneTime : real; {Время, в которое закончится обработка}

  Stoppage : real; {Общее время простоя прибора}

  IsWorking : boolean; {Осуществляется ли обслуживание ?}
  CurWorking : TReq; {Текущий обрабатываемый запрос}
  Lambda   : real; 

  Constructor Init(aLambda:real); {}
  Function AddReq(aCurReq : PReq;aCurTime:real):boolean; {Начало обработка запроса}
  Procedure Reset; {Сброс устр-ва}
  Function DoneWork:PReq; {Завершение обработки текущего запроса, с его возвратом}
  Destructor Done;
end; {CDevice}

{-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-}
EBuffers = (BUFFER_DUMMY, BUFFER_FIFO, BUFFER_LIFO);

CBuffer = object      {Буффер запросов}
  BufType      : EBuffers; {Тип буфера}
  BufferLength : integer; {Длина буфера}
  CellCount    : integer; {Индекс текущей свободной ячейки}
  Data         : PReqArray; {Собственно сам буфер}
  Constructor Init(aBufLen:integer); {Инициализация буфера}
  Destructor Done;virtual;{Деструктор, он и есть}
  Function SetBufferSize(aNewSize:integer):boolean;{Изменение размера буфера}
  Procedure FreeBuffer; {Освобождение всего буфера сразу}
  Function AddReq(var aNew, rKicked:TReq):boolean; {Добавляется запрос в буфер.
   Если из буфера какой-то запрос удален, то ф-ция возвр. false, и в rKicked -
   запрос, который отклонен}
  Function GetReq(var rReq:TReq):boolean;virtual; {Вытащить из буфера запрос,
        если успешно - true}
end; {CBuffer}
{-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-}

CModel = object {Наша модель СМО} 
 Sources  : PSourceArr; {Источники} 
 SourceCnt: integer;    {Число источников}
 MaxSrc   : integer;    {Размерность массива источников}

 Buffer   : PBuffer;    {Буфер}
 Device   : PDevice;    {Устройство}

 CurTime  : real;       {Текущий момент времени}
 Delta    : real;
 DovInt   : real;

 NextSource:integer;    {Какой из источников - быстрее}
 RealizSteps:LongInt;   {Необх. число реализаций}
 CurStep  : LongInt;    {Текущий шаг}
{}
 DoneCount,             {Число обработанных заявок}
 RefusedCount,           {Число отклоненных заявок}
 TotalCount : integer;  {Общее число заявок}

 Working  : boolean;    {Работает ли модель ?}
 WorkReq  : TReq;

 CalcVer  : boolean;    {Нужно ли подсчитывать KMIN в зав. от вер ?}

 Constructor Init(aBegSrcCnt:integer;aDev:PDevice;aBuf:PBuffer;
                  aDel,aDov:Real);
 Function SetSourceCount(aNewCnt:integer):boolean; {Установка нового числа
                                              источников, с сохранением старых}
 Function AddNewSource(aNewSrc:PSource):boolean; {Добавление нового источника}
 Procedure SetNextSource; {Находит NextSource}
 Function TestVer:boolean; {}
 Function RemoveSource(aName:integer):boolean; {Удаление источника из модели}
 Procedure Reset; {Сброс всех счетчиков}
 Procedure Start;
 Procedure PrintValues(var F:TEXT);
 Function Step:boolean; {Моделирует следующий шаг, если в }
 Destructor Done;
end;
{-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-}
                        IMPLEMENTATION

Constructor CSource.Init(aName : Integer;aLambda:real);
Begin
  Name := aName;
  SType := SOURCE_EASIEST;
  Lambda := aLambda;
  Reset;
end;{CSource.Init}
{-------------------------------------------------------------------------}
Procedure CSource.Reset;
Begin
  LastTime :=0; NewTime:=0;
  TotalReq :=0;
  DoneReq :=0;
  RefusedReq:=0;
  WaitTime :=0;
  ProbRefuse:=0;
  MatWait  :=0;
end;{CSource.Reset}
{-------------------------------------------------------------------------}
Procedure CSource.GenNewReq;
Begin
  LastTime := NewTime; 
  NewTime := LastTime - Ln(Random)/Lambda;
  Inc(TotalReq);
  LastReq.Birth := NewTime;
  LastReq.Death := -1.0;
  LastReq.FromSource := @Self;
end;{CSource.GenNewReq}
{-------------------------------------------------------------------------}
Destructor CSource.Done;
begin
end;
{-------------------------------------------------------------------------}
{    CDevice    }
Constructor CDevice.Init(aLambda:real);
begin
  Lambda := aLambda;
  Reset;
end;{CDevice.Init}
{=========================================}
Procedure CDevice.Reset;
begin
  LastTime := 0;
  DoneTime := 0;
  Stoppage := 0;
  IsWorking := false;
end;{CDevice.Reset}
{---------------------------}
Function CDevice.AddReq(aCurReq : PReq;aCurTime:real):boolean; {Начало обработка запроса}
begin
  AddReq := false;
  if not isWorking then
   begin
    BegTime   := aCurTime;
    Stoppage  := Stoppage + (BegTime-LastTime);
    DoneTime  := BegTime - Ln(Random)/Lambda;
    IsWorking := true;
    CurWorking := aCurReq^;
    CurWorking.ToDevice := @Self;
    AddReq    := true;      
   end;
end;{CDevice.AddReq}
{=========================================}
Function CDevice.DoneWork:PReq; 
begin
  DoneWork := nil;
  if isWorking then   
   begin
     LastTime := DoneTime;
     CurWorking.Death := DoneTime;
     DoneWork := @CurWorking;
     isWorking := false;
   end;
end;{CDevice.DoneWork}
{=========================================}
Destructor CDevice.Done;
begin
end;{CDevice.Done}
{=========================================}

{  CBuffer  } 
Constructor CBuffer.Init(aBufLen:integer); 
begin
  BufType := BUFFER_LIFO;
  BufferLength := aBufLen;
  CellCount := 0;
  GetMem(Data,Sizeof(TReq)*BufferLength);
  FillChar(Data^,Sizeof(TReq)*BufferLength,0);
end;{CBuffer.Init}
{-----------------------}
Destructor CBuffer.Done;
begin
  FreeMem(Data,Sizeof(TReq)*BufferLength); {Dispose(data)}
  BufferLength:=0;
end;{CBuffer.Done}
{-----------------------}
Function CBuffer.SetBufferSize(aNewSize:integer):boolean;
var
 NewBuf : PReqArray;
begin
 SetBufferSize := false;
 if aNewSize > BufferLength then
  begin
   GetMem(NewBuf,Sizeof(TReq)*aNewSize);
   FillChar(NewBuf^,Sizeof(TReq)*aNewSize,0);
   Move(Data^,NewBuf^,Sizeof(TReq)*BufferLength);
   FreeMem(Data,Sizeof(TReq)*BufferLength);
   Data := NewBuf;
   BufferLength := aNewSize;
   SetBufferSize := true;
  end;
end;{CBuffer.SetBufferSize}
{-----------------------}
Function CBuffer.AddReq(var aNew, rKicked:TReq):boolean;
begin
  if CellCount=BufferLength then {На самом деле, т.к с 0, то больше}
   begin
     AddReq := false;
     rKicked := Data^[0];
     rKicked.Death := aNew.Birth; {Запрос отклонен, в момент, когда новый прибыл,
                                    т.е. когда тот был создан}
     Move(Data^[1],Data^[0], SizeOf(TReq) * (BufferLength-1));
     Data^[CellCount-1] := aNew;
   end
  else
   begin
     AddReq := true;
     Data^[CellCount] := aNew;
     Inc(CellCount);
   end;
end;{CBuffer.AddReq}
{-----------------------}
Function CBuffer.GetReq(var rReq:TReq):boolean;
begin
 GetReq := false;
 if CellCount > 0 then
  begin
   rReq := Data^[CellCount-1];
   Dec(CellCount);
   GetReq := true;
  end;
end;{CBuffer.GetReq}
{---------------------------------------------}
Procedure CBuffer.FreeBuffer;
begin
 CellCount := 0;
end;
{-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-}

Constructor CModel.Init(aBegSrcCnt:integer;aDev:PDevice;aBuf:PBuffer;
                       aDel,aDov:Real);
Begin
  MaxSrc := aBegSrcCnt;
  GetMem(Sources,Sizeof(PSource)*MaxSrc);
  SourceCnt := 0;
  CalcVer := true;
  RealizSteps := 100;
  Buffer := aBuf;
  Device := aDev;
  Delta := aDel;
  DovInt := aDov;
  Reset;
end;{CModel.Init}
{---------------------------------------------------------}
Procedure CModel.Reset;
begin
  NextSource:=0;
  CurTime := 0;
  CurStep := 0;
  DoneCount := 0;
  RefusedCount := 0;
  RealizSteps := 100;
  TotalCount := 0;
  Working := false;
end;{CModel.Reset}
{------------------------------------------------------------------}
Function CModel.SetSourceCount(aNewCnt:integer):boolean;
var
 NewBuf : PSourceArr;
Begin
 SetSourceCount := false;
 if aNewCnt > MaxSrc then
  begin
   GetMem(NewBuf,Sizeof(PSource)*aNewCnt);
   Move(Sources^,NewBuf^,Sizeof(PSource)*MaxSrc);
   FreeMem(Sources,Sizeof(PSource)*MaxSrc);
   Sources := NewBuf;
   MaxSrc := aNewCnt;
   SetSourceCount := true;
  end;
end;{CModel.SetSourceCount}
{------------------------------------------------------------------}
Function CModel.AddNewSource(aNewSrc:PSource):boolean;
Begin
  if SourceCnt >= MaxSrc then AddNewSource := SetSourceCount(MaxSrc+1);
  Sources^[SourceCnt] := aNewSrc;
  Inc(SourceCnt);
end;{CModel.AddNewSource}
{------------------------------------------------------------------}
Function CModel.RemoveSource(aName:integer):boolean;
var
 c : integer;
Begin
  RemoveSource := false;
  if SourceCnt = 1 then exit;  
  for c := 0 to SourceCnt-1 do if Sources^[c]^.Name = aName then break;
  if c = SourceCnt-1 then exit;
  Dispose(Sources^[c],Done);
  while c <> SourceCnt-1 do
   begin
    Sources^[c] := Sources^[c+1];
    inc(c);
   end;
  Dec(SourceCnt);
end;{CModel.RemoveSource}
{------------------------------------------------------------------}
Procedure CModel.SetNextSource; {Находит NextSource}
var
 c      : integer;
begin
 NextSource := 0;
 for c := 0 to SourceCnt-1 do
 begin
  if (Sources^[c]^.NewTime < Sources^[NextSource]^.NewTime) then
   NextSource := c;
 end;
end;{CModel.SetNextSource}
{------------------------------------------------------------------}
Procedure CModel.Start;
var
 c,First : integer;
begin
 if (Device=nil) or (Buffer=nil) or (SourceCnt=0) then exit;
 Reset;
 Device^.Reset;
 Buffer^.FreeBuffer;

 First := 0;
 for c := 0 to SourceCnt-1 do
 begin
  Sources^[c]^.Reset;
  Sources^[c]^.GenNewReq;
  if (Sources^[c]^.NewTime < Sources^[First]^.NewTime) then
    First := c;
 end;
 WorkReq := Sources^[First]^.LastReq;
 Device^.AddReq(@WorkReq,Sources^[First]^.NewTime);
 Sources^[First]^.GenNewReq;

 SetNextSource;

 Working := true;
end;
{------------------------------------------------------------------}
Function CModel.TestVer:boolean;
var
 NewV,c,p:real;
 i : integer;
begin
 NewV:=0;
 TestVer:=true;
 for i := 0 to SourceCnt-1 do if Sources^[i]^.RefusedReq <> 0 then
  begin
   p := 1.0 * Sources^[i]^.RefusedReq / Sources^[i]^.TotalReq;
   c := (DovInt*(1-p))/(Delta*Delta*p);
   if c > NewV then NewV := c;
  end;
 if NewV > RealizSteps then
  begin
   RealizSteps := round(NewV);
   TestVer := false;
  end;
end;{CModel.TextVer}
{------------------------------------------------------------------}
Function CModel.Step:boolean;
var
 Kicked : TReq;
 i      : integer;
 ret    : boolean;
Begin
 Step := false;
 if Not Working then exit;
 ret := false; {Считаем, что продолжать не будем}

 if (not CalcVer) AND (CurStep > RealizSteps) then exit;

 for i:=0 to SourceCnt-1 do if Sources^[i]^.TotalReq < RealizSteps then
   ret:=true;

 if (not ret) AND CalcVer then ret := not TestVer;
  if Device^.DoneTime < Sources^[NextSource]^.NewTime then
   begin
    CurTime := Device^.DoneTime;
    WorkReq := Device^.DoneWork^;
    inc(DoneCount);
    inc(TotalCount);
    inc(WorkReq.FromSource^.DoneReq);

    WorkReq.FromSource^.WaitTime := WorkReq.FromSource^.WaitTime +
      WorkReq.Death - WorkReq.Birth;

    if Buffer^.GetReq(WorkReq) then
     begin {В буфере находилась заявка}
       WorkReq.FromSource^.MatWait := WorkReq.FromSource^.MatWait +
        CurTime-WorkReq.Birth;
       Device^.AddReq(@WorkReq,CurTime);
     end
    else {Need to gen new req}
     begin
       Device^.AddReq(@Sources^[NextSource]^.LastReq,Sources^[NextSource]^.LastReq.Birth);
       Sources^[NextSource]^.GenNewReq;
       SetNextSource;
     end;
   end
  else {Ближайшее событие - генерация запроса}
   begin
     CurTime := Sources^[NextSource]^.NewTime;
     if not Buffer^.AddReq(Sources^[NextSource]^.LastReq,Kicked) then
      begin
        inc(Kicked.FromSource^.RefusedReq);

        {Kicked.FromSource^.WaitTime := Kicked.FromSource^.WaitTime +
         Kicked.Death-Kicked.Birth;}

        Inc(RefusedCount);
        Inc(TotalCount);
      end;
     Sources^[NextSource]^.GenNewReq;
     SetNextSource;
   end; {}
 inc(CurStep);
 Step := ret;
end;{CModel.Step}
{------------------------------------------------------------------}
Procedure CModel.PrintValues(var F:Text);
var
 i : integer;
begin
  WriteLn(F,'Отн. точность - ',Delta*100:0:0,'%, дов. инт в кв. - ',DovInt:0:2);
  WriteLn(F,'Хар-ка потока прибора - ', Device^.Lambda:0:2);
  WriteLn(F,'Ист.|Всг заяв.|Отк заяв.|Вып заяв.|P отк.  |Мат. ож | Общ. вр.');
  for i := 0 to SourceCnt-1 do with Sources^[i]^ do
  begin
    write(F,Name:4,' ',TotalReq:9,' ',RefusedReq:9,' ',DoneReq:9,' ',100.0*RefusedReq/TotalReq:6:2,'%  ');
    if DoneReq <> 0 then Write(F,MatWait/DoneReq:8:3,' ') else Write(F,'  ---    ');
    WriteLN(F,WaitTime:0:2);
  end;
  WriteLn(F,'Коэффициент простоя прибора - ',100*Device^.Stoppage/CurTime:0:2,'%');
  WriteLN(F,'---')
end;{CModel.PrintValues}
{------------------------------------------------------------------}
Destructor CModel.Done;
var
 c : integer;
Begin
  for c:= 0 to SourceCnt-1 do Dispose(Sources^[c],Done);
  FreeMem(Sources,Sizeof(PSource)*MaxSrc);
end;{CModel.Done}
{------------------------------------------------------------------}
END. {EOF}