unit Data;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ImgList,Math, Placemnt,rootsequ, inifiles, comctrls,dates;


const
  MaxBateryCapacity  : float = 6000;
  SafeBateryCoef     : float = 0.1;
  BateryVoltage      : float =  24;
  BatLossesPerHour   : float = 0.01;
  VelocityFactor     : float   = 1.0;
  PwrFactorSteps     : integer = 20;
  PhPower              : float   = 500;
  HZPower              : float   = 5000;
  HZConType            : integer = 0;
  HZCalc               : boolean = true;
  HZTargetCoef         : float   = 0.05; // Καλυψη H/Z 5%
  WGNumber             : integer = 1;
  IsBatteryInitialFull : boolean = true;

  WayopDes           : array[0..2] of string = (
                         'Συνεχής',
                         'Σαββατοκύριακα',
                         'Καθημερινές'
                       );


type
  Float = double;
  TFloatArray = array of float;

  TWindPwrCurve = class;

  Tdm = class(TDataModule)
    ieHots: TImageList;
    ieGrays: TImageList;
    ieDisabled: TImageList;
    SelWGDlg: TOpenDialog;
    Storage: TFormStorage;
    LargeImages: TImageList;
    SmallImages: TImageList;
    SelDevListDlg: TOpenDialog;
    procedure dmCreate(Sender: TObject);
    procedure dmDestroy(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    function SelectPwrCurve : TWindPwrCurve;
    function SelectDevListFile : string;
  end;

  TTabData = class
  public
    Description : string;
    Latitute,Logitute,anemHeight : float;
    SectCount : integer;
    TtlSectFreq  : TFloatArray;
    PerSectFreq  : array of TFloatArray;
    procedure NormTtlFreq;
    procedure NormSectFreq(Sect : integer);
    procedure Normalize;
    procedure LoadFromFile(const FileNm : string);
  end;

  TPoint2D = record
   x,y : float;
  end;


  TWindPwrCurve = class
  private
    VP : array of TPoint2D;
    FRatedPower: float;
    FVR: float;
    function GetPoints(index: integer): TPoint2d;
    function GetCount: integer;
    function GetX(index: integer): float;
    procedure SetX(index: integer; const Value: float);
    function GetY(index: integer): float;
    procedure SetY(index: integer; const Value: float);
    procedure SetRatedPower(const Value: float);
    procedure SetVR(const Value: float);
  public
    ModelName : string;
    PwrFactor : float;
    constructor create; virtual;
    procedure Delete(dp : integer);
    procedure AddPoint(pt : TPoint2d);
    function PwrAt(V : float) : float;
    procedure LoadFromWaspPow(const FileNm : string);
    property Points[index : integer] : TPoint2d read GetPoints; default;
    property X[index : integer] : float read GetX write SetX;
    property Y[index : integer] : float read GetY write SetY;
    property Count: integer read getCount;
    property RatedPower : float read FRatedPower write SetRatedPower;
    property VR : float read FVR write SetVR;
  end;

  TMeasurments = class
  private
    function GetWindVelocity(index: integer): float;
    function GetCount: integer;
    procedure SetWindVelocity(index: integer; const Value: float);
  public
    WVel : TFloatArray;
    DateBase : TDateTime;
    procedure LoadFromAsciiFile(const FileNm : string);
    property  WindVelocity[index : integer] : float read GetWindVelocity write SetWindVelocity; default;
    property  Count : integer read GetCount;
  end;

  THourOp = class
  private
    FHourOp : array[0..23] of float;
    function GetHourOp(index: integer): float;
    procedure SetHourOp(index: integer; const Value: float);
  public
    property HourOp[index : integer] : float read GetHourOp write SetHourOp;
  end;

  THourOpList = class(TList)
  private
    function GetItems(index: integer): THourOp;
    procedure SetItems(index: integer; const Value: THourOp);
    function GetHour(ssn, hour: integer): float;
    procedure SetHour(ssn, hour: integer; const Value: float);
  public
    property items[index : integer] : THourOp read GetItems write SetItems;
    property Hour[ssn,hour : integer] : float read GetHour  write SetHour; default;
  end;

  TDeviceEl = class
  private
  public
    FImageIndex : integer;
    FDevName    : string;
    FRatedPower : float;
    FDevCount   : integer;
    FKnownRatedPower : boolean;
    FNotKnownOp      : boolean;
    FHourOp  : THourOpList;
    function PerDayEnergy : float;
    constructor Create(DevFile : TIniFile); reintroduce;
    function CreateListItem(var ListView : TListView) : TListItem;
    constructor AsCopyOff(CDev : TDeviceEl);
    procedure   SaveToListFile(Ini : TIniFile; const SectName : string);
    procedure   SeasonAdded(index : integer);
    procedure   SeasonDeleted(index : integer);
    constructor LoadFromListFile(Ini : TIniFile; const SectName : string);
  end;

  TDevList = class(TList)
  private
    function GetItem(index: integer): TDeviceEl;
  public
    procedure ConstructPowerDemand(var pl : TFloatArray);
    procedure SaveToFile(const FileNm : string);
    procedure LoadFromFile(const FileNm : string);
    property Items[index : integer] : TDeviceEl read GetItem; default;
  end;

  TPhFactors = class
  private
    function GethFactor(h: integer): float;
    function GetData(m, h: integer): float;
    procedure SetData(m, h: integer; const Value: float);
  public
    mData : array[1..12,0..23] of float;
    procedure LoadFromFile(const FileName : string);
    procedure SaveToFile(const FileName : string);
    property hFactor[h : integer] : float read GethFactor;
    property Data[m,h : integer] : float read GetData write SetData; default;
  end;

  TSeason = class(TCollectionItem)
  private
    FDayStart : integer;
    FDayStop  : integer;
    FDescription: string;
    FWayOp: byte;
    procedure SetDescription(const Value: string);
    function GetDays: integer;
    function GetHourStart: integer;
    procedure SetHourStart(const Value: integer);
    function GetHourStop: integer;
    procedure SetHourStop(const Value: integer);
    procedure SetWayOp(const Value: byte);
  public
    constructor Create(Collection: TCollection); override;

  published
    property DayStart : integer read FDayStart write FDayStart;
    property DayStop : integer read FDayStop write FDayStop;
    property HourStart : integer read GetHourStart write SetHourStart;
    property HourStop : integer read GetHourStop write SetHourStop;
    property Days : integer read GetDays;
    property Description : string read FDescription write SetDescription;
    property WayOp : byte read FWayOp write SetWayOp;
  end;

  TSeasonList = class(TCollection)
  private
    function GetItems(index: integer): TSeason;
    function GetLast: TSeason;
  protected
  public
    procedure SaveToIni(ini : TIniFile);
    procedure LoadFromIni(ini : TIniFile);
    function AddSsn : TSeason;
    property Items[index : integer] : TSeason read GetItems; default;
    property LastSsn : TSeason read GetLast;
  end;


type
  TCalcResult = record
    spw  : float; // Συνολική Παραγωγή Α/Γ
    spph : float; // Συνολική Παραγωγή Φ/Β
    sphz : float; // Συνολική Παραγωγή H/Z
    spdl : float; // Συνολική Περίσεια Ενέργειας
    spl  : float; // Συνολική Ζήτηση Ενέργειας

    cef  : float; // Η αρχική χωρητικότητα μπαταρίας
    cel  : float; // Η τελική χωρητικότητα μπαταρίας

    cpw  : float; // Ποσοστό κάλυψης από A/Γ
    cpph : float; // Ποσοστό κάλυψης από Φ/Β
    cphz : float; // Ποσοστό κάλυψης από H/Z
  end;

  TStepFunc = procedure ( i : integer;
                          pw  : float;
                          pph : float;
                          pl  : float;
                          phz : float;
                          pdl : float;
                          ce  : float) of object;



const
  DirNames : array [0..11] of string [2] =
  (
   'N' ,
   'NE',
   'EN',
   'E' ,
   'ES',
   'SE',
   'S' ,
   'SW',
   'WS',
   'W' ,
   'WN',
   'NW'
   );

   procedure CTextRect(Canvas : TCanvas; Rect : TRect; const Txt : string);
   procedure LTextRect(Canvas : TCanvas; Rect : TRect; const Txt : string);
   function InputFloat(const Prompt : string; var aFloat : float) : boolean;
   procedure FillArray(var arr : array of float; value : float);
   function RandomNorm(Mean,StanDev : float) : float;
   function FitWeibull(Mean,StdDev : float; var C , K,FitMean,FitStdDev : float) : boolean;
   function Wb(C,K,X : float) :  float;
   function Wbp(C,K,V : float) :  float;


   procedure LoadAsciiFile(const FileNm : string; var wvel : TFloatArray);


   function apeCalc(var vi,pph,pl : TFloatArray;
                   WG : TWindPwrCurve;
                   MaxBateryCapacity: Float;
                   StepFunc : TStepFunc) : TCalcResult;

   procedure HoldFormClass(pnlHolder : TWinControl;
                        coolBarHolder : TWinControl;
                        BorderVisible : boolean;
                        ClosePrev     : boolean;
                        fmClass: TFormClass;
                        OnCreate : TNotifyEvent = nil);

   function OpenDialogExec(const AFilter   : string;
                           const AFileName : string) : string;

   function SaveDialogExec(const AFilter   : string;
                           const AFileName : string) : string;




var
  dm        : Tdm;
  dfDecimalSeparator : char;

  phFactors : TPhFactors;
  Vi        ,
  PW        ,
  PL        ,
  PPh       ,
  CE        ,
  PHZ       ,
  PDL       : TFloatArray;
  snl       : TSeasonList;
  WG        : TWindPwrCurve;

  DateBase  : TDateTime;
  FDevList  : TDevList;

implementation

{$R *.DFM}

var
  FilterList : TStringList;

function InputFloat(const Prompt : string; var aFloat : float) : boolean;
var
 sFloat : string;
begin
  Result := False;
  sFloat := FloatToStr(aFloat);
  if InputQuery('Εισαγωγή',prompt,sFloat) then
  begin
    aFloat := StrToFloat(sFloat);
    result := True;
  end;
end;

procedure FillArray(var arr : array of float; value : float);
var
 i : integer;
begin
  for i:=Low(arr) to High(arr) do
     arr[i] := Value;
end;

function RandomNorm(Mean,StanDev : float) : float;
var
 RandomA,RandomB,Radius2, Deviate : float;
begin
  repeat
    RandomA := 2 * Random -1.0;
    RandomB := 2 * Random -1.0;
    Radius2  := sqr(RandomA)+sqr(RandomB);
  until Radius2 < 1.0;
  Deviate := RandomA * sqrt((-2.0*Ln(Radius2))/Radius2);
  Result := Mean + Deviate * StanDev;
end;


function Gamma( x : float ) : float;
const
  Stp  = 2.50662827465;
  Half = 0.5;
  One  = 1.0;
  fpf  = 5.5;
  Cof  : array[1..6] of float =
         (76.18009173    ,
         -86.50532033    ,
          24.01409822    ,
          -1.231739516   ,
           0.120858003e-2,
          -0.536382e-5  );

var
 xx, tmp, ser : float;
 j            : integer;

begin
  if x=0 then
     begin
       Gamma:=0;
       Exit;
     end;
  xx  := x-One;
  tmp := xx+fpf;
  tmp := (xx+Half)*Ln(tmp)-tmp;
  ser := One;
  for j:=1 to 6 do
      begin
        xx  := xx+one;
        ser := ser + cof[j]/xx;
      end;
  Gamma:=Exp(tmp+Ln(stp*ser));
end { Gamma };

function Wb(C,K,X : float) :  float;
begin
  Wb:=(K/C) * power((X/C),(K-1)) * exp( - power(X/C,K));
end;

function wbp(C,K,V : float) :  float;
{
 Η πρώτη παράγωγος
 της συνάρτησης wb
}
var
 x : float;
begin
  X := V/C;
  result := (k/C)* exp(-power(X,K))*power(X,k-1)*((k-1)/V-power(1/C,k)*k*power(V,k-1));
end;

var
 VGlb  ,
 SDGlb : real;


function Kfunc( K : real ) : real;
begin
  KFunc := sqr(SdGlb/VGlb) -( Gamma(1+2/K) / sqr(Gamma(1+1/K)) )  + 1;
end { Kfunc };


function FitWeibull(Mean,StdDev : float;
                    var C , K,FitMean,FitStdDev : float) : boolean;

const
  MaxIter  = 100;
  MaxError = 1e-10;

var
  N          : integer;
  Er         : byte;
  FirstGuess ,
  ActV       : float;

begin
  VGlb  := Mean;
  SdGlb := StdDev;
  FitWeibull := True;
  if StdDev=0 then
     begin
       FitWeibull := False;
       Exit;
     end;

     FirstGuess := Power(Mean/StdDev,-1.086);
     Secant(1,FirstGuess,MaxError,MaxIter,K,ActV,N,Er,@KFunc);
     if (Er<>0) and (ActV >= MaxError ) then
        K:=FirstGuess;
     C:= Mean / ( Gamma(1+1/K) );

     FitMean   :=C*Gamma(1+1/K);
     FitStdDev :=Sqrt(Sqr(C) * ( Gamma(1+2/K) - sqr(Gamma(1+1/K)) ));
  end;

procedure CTextRect(Canvas : TCanvas; Rect : TRect; const Txt : string);
var
 w : integer;
 ps : integer;
begin
  with Canvas do
  begin
    w  := TextWidth(Txt);
    ps := (Rect.Right-Rect.Left-w) div 2;
    //Canvas.TextOut(Rect.Left+ps,Rect.Top+2,Txt);
    //Canvas.Brush.Color := clBlue;
    //Canvas.FillRect(Rect);
    Canvas.TextRect(Rect,Rect.Left+ps,Rect.Top+2,Txt);
  end
end;

procedure LTextRect(Canvas : TCanvas; Rect : TRect; const Txt : string);
begin
  with Canvas do
    TextRect(Rect,Rect.Left+2,Rect.Top+2,Txt);
end;

{ TTabData }

procedure TTabData.LoadFromFile(const FileNm: string);
var
 Txt  : TextFile;
 i    : integer;
 psum : float;
begin
  AssignFile(Txt,FileNm);
  Reset(Txt);
  DecimalSeparator:='.';
  try
    ReadLn(Txt,Description);
    ReadLn(Txt,Latitute,Logitute,anemHeight);
    ReadLn(Txt,SectCount);
    SetLength(ttlSectFreq,SectCount);
    for i:=0 to SectCount-1 do
        Read(Txt,ttlSectFreq[i]);
    ReadLn(Txt);
    while not eof(Txt) do
    begin
      SetLength(PerSectFreq,Length(PerSectFreq)+1);
      SetLength(PerSectFreq[High(PerSectFreq)],SectCount);

      // Για να αποφύγω ένα ενοχλητικό warning
      Read(Txt,PerSectFreq[High(PerSectFreq),0]);

      psum := 0;
      for i:=0 to SectCount-1 do
      begin
        Read(Txt,PerSectFreq[High(PerSectFreq),i]);
        psum := psum + PerSectFreq[High(PerSectFreq),i];
      end;
      ReadLn(Txt);
      if psum=0 then
         break;
      {
      if Sum(PerSectFreq)=0 then
      begin
        SetLength(PerSectFreq,Length(PerSectFreq)-1);
        Break;
      end;
      }
    end;
  finally
    CloseFile(Txt);
  end
end;

procedure TTabData.Normalize;
var
 d: integer;
begin
  NormTtlFreq;
  for d:=0 to SectCount-1 do
    NormSectFreq(d);
end;

procedure TTabData.NormSectFreq(Sect: integer);
var
 ss : float;
 i  : integer;
begin
  ss := 0;
  for i:=Low(PerSectFreq) to High(PerSectFreq) do
      ss := ss + PerSectFreq[i,Sect];
  for i:=Low(PerSectFreq) to High(PerSectFreq) do
     PerSectFreq[i,Sect]:= Round(PerSectFreq[i,Sect]*1000/ss);
end;

procedure TTabData.NormTtlFreq;
var
 ss : float;
 d  : integer;
begin
  ss := Sum(TtlSectFreq);
  for d:=0 to SectCount-1 do
      TtlSectFreq[d] := TtlSectFreq[d]*100/ss;
end;

{ TWindPwrCurve }

procedure TWindPwrCurve.AddPoint(pt: TPoint2d);
begin
  SetLength(VP,Length(vp)+1);
  VP[Count-1] := pt;
end;

constructor TWindPwrCurve.create;
begin
  inherited;
  FVR := 12;
end;

procedure TWindPwrCurve.Delete(dp: integer);
var
 i : integer;
begin
  if dp<=Count-1 then
  begin
    for i:=dp to Count - 1 do
      vp[i] := vp[i+1];
    SetLength(vp,Length(vp)-1);
  end;
end;

function TWindPwrCurve.getCount: integer;
begin
  Result := Length(VP);
end;

function TWindPwrCurve.GetPoints(index: integer): TPoint2d;
begin
  result := VP[index];
end;

function TWindPwrCurve.GetX(index: integer): float;
begin
  result := Points[index].X;
end;

function TWindPwrCurve.GetY(index: integer): float;
begin
  result := Points[index].y;
end;

procedure TWindPwrCurve.LoadFromWaspPow(const FileNm: string);
var
 Txt : TextFile;
 tmp : string;
 VScale ,
 PScale : float;
 i      : integer;
begin
  PwrFactor := 1.0;
  SetLength(VP,0);
  ModelName := ChangeFileExt(ExtractFileName(FileNm),'');;
  AssignFile(Txt,FileNm);
  Reset(Txt);
  RatedPower := 0;
  DecimalSeparator := '.';
  try
    ReadLn(Txt,Tmp); // Header Model Name
    if tmp<>'' then
       ModelName := Tmp;
    ReadLn(Txt,Tmp); // HubHeight , RotorDiameter
    Read(Txt,VScale,PScale);    ReadLn(Txt,Tmp);
    while not eof(txt) do
    begin
      SetLength(VP,Length(VP)+1);
      i := High(VP);
      Read(Txt,VP[i].x,VP[i].y);
      ReadLn(Txt,Tmp);
      VP[i].X := VP[i].X * VScale;
      VP[i].Y := VP[i].Y * PScale;
      if vp[i].y>ratedPower then
         RatedPower := vp[i].y;
    end;
    if (Length(VP)>0) and  (VP[High(VP)].Y = 0)then
       SetLength(VP,Length(VP)-1);
  finally
    CloseFile(Txt);
    DecimalSeparator := dfDecimalSeparator;
  end;
end;


function TWindPwrCurve.PwrAt(V: float): float;
var
 i : integer;
 v1,
 v2,
 p1,
 p2 : float;
begin
  result := 0;
  if (V<VP[0].x) or (V>VP[High(VP)].x) then
     Exit;
  for i:=0 to High(VP) do
   if VP[i].X>=V then
   begin
     v2:=VP[i].x;
     p2:=VP[i].y * PwrFactor;
     if i-1>0 then
     begin
       v1:=VP[i-1].x;
       p1:=VP[i-1].y * PwrFactor;
       result := p1+(V-V1)*((p2-p1)/(v2-v1));
     end;
     Break;
   end;
end;

procedure TWindPwrCurve.SetRatedPower(const Value: float);
begin
  FRatedPower := Value;
end;

procedure TWindPwrCurve.SetVR(const Value: float);
begin
  FVR := Value;
end;

procedure TWindPwrCurve.SetX(index: integer; const Value: float);
begin
  VP[index].x := value;
end;

procedure TWindPwrCurve.SetY(index: integer; const Value: float);
begin
  VP[index].y := value;
end;

{ TMeasurments }

function TMeasurments.GetCount: integer;
begin
  result := Length(WVel);
end;

function TMeasurments.GetWindVelocity(index: integer): float;
begin
  Result := WVel[index];
end;

procedure TMeasurments.LoadFromAsciiFile(const FileNm: string);
var
 Txt : TextFile;
 Tmp : string;
begin
  Assign(Txt,FileNm);
  SetLength(WVel,0);
  Reset(Txt);
  try
    DecimalSeparator := '.';
    ReadLn(Txt,Tmp);
    ReadLn(Txt,Tmp);
    ReadLn(Txt,Tmp);
    while not Eof(Txt) do
    begin
      SetLength(WVel,Length(WVel)+1);
      ReadLn(Txt,WVel[High(WVel)]);
    end;
  finally
    CloseFile(Txt);
    DecimalSeparator:=dfDecimalSeparator;
  end;
end;

procedure TMeasurments.SetWindVelocity(index: integer; const Value: float);
begin
  WVel[index] := Value;
end;

{ Tdm }

function Tdm.SelectPwrCurve: TWindPwrCurve;
begin
  Result := nil;
  if SelWGDlg.Execute then
  begin
    Result := TWindPwrCurve.Create;
    Result.LoadFromWaspPow(SelWGDlg.FileName);
    SelWGDlg.InitialDir := ExtractFileDir(SelWGDlg.FileName);
  end;
end;

procedure Tdm.dmCreate(Sender: TObject);
begin
  Storage.RestoreFormPlacement;
end;

procedure Tdm.dmDestroy(Sender: TObject);
begin
  Storage.SaveFormPlacement;
end;

function Tdm.SelectDevListFile: string;
begin
  Result := '';
  with SelDevListDlg do
  begin
    if Execute then
    begin
      Result      := FileName;
      InitialDir  := ExtractFileDir(FileName);
    end;
  end;
end;

{ TDeviceEl }

constructor TDeviceEl.AsCopyOff(CDev: TDeviceEl);
var
 i,h : integer;
begin
  inherited Create;
  FImageIndex      := CDev.FImageIndex;
  FDevName         := CDev.FDevName;
  FRatedPower      := CDev.FRatedPower;
  FKnownRatedPower := CDev.FKnownRatedPower;
  FDevCount        := CDev.FDevCount;
  FNotKnownOp      := CDev.FNotKnownOp;
  FHourOp          := THourOpList.Create;
  for i:=0 to cdev.FHourOp.Count-1 do
  begin
    FHourOp.Add(THourOp.Create);
    for h:=0 to 23 do
        FHourOp[i,h] := cdev.FHourOp[i,h];
  end;
  //Move(CDev.FHourOp[0],FHourOp,SizeOf(FHourOp));
end;

constructor TDeviceEl.Create(DevFile: TIniFile);
var
 h  : byte;
 FParamx : string;
begin
  inherited create;
  FDevCount := 1;
  DecimalSeparator := '.';
  FHourOp := THourOpList.Create;
  FHourOp.Add(THourOp.Create);
  with DevFile do
  try
    FDevName := ReadString('dev','name','Αγνωστη Συσκευή');
    FRatedPower := ReadFloat('dev','ratedpower',0);
    FKnownRatedPower := ReadBool('dev','knownRatedPower',FRatedPower<>0);
    FImageIndex := ReadInteger('dev','icon',0);
    FNotKnownOp := ReadBool('dev','NotKnownOp',False);
    for h:=0 to 23 do
    begin
      FParamx := format('hourop_%2.2d',[h]);
      FHourOp[0,h]:=ReadFloat('hourop',FParamx,0);
      {
      for m:=1 to 12 do
      begin
        FParamx := format('month%2.2d_%2.2d',[m,h]);
        FHourOp[m,h] :=ReadFloat('monthop',FParamx,0);
      end;
      }
    end;
  finally
    DecimalSeparator := dfDecimalSeparator;
  end;
end;

function TDeviceEl.CreateListItem(var ListView: TListView): TListItem;
var
 rps : string;
begin
  Result := ListView.Items.add;
  with Result do
  begin
    ImageIndex := FImageIndex;
    Caption := FDevName;
    rps :=FloatToStr(FRatedPower);
    if not FKnownRatedPower then
       rps := '?';
    Data := Self;
    SubItems.AddObject(
      rps,
      Self
    );
    SubItems.Add(FloatToStr(PerDayEnergy));
  end;
end;


constructor TDeviceEl.LoadFromListFile(Ini: TIniFile;
  const SectName: string);
var
 m,h : integer;
begin
  inherited Create;
  with ini do
  begin
    FImageIndex := ReadInteger(SectName,'ImageIndex',0);
    FDevName    := ReadString (SectName,'Name','');
    FDevCount   := ReadInteger(SectName,'DevCount',1);
    FRatedPower := ReadFloat  (SectName,'RatedPower',0);
    FNotKnownOp := ReadBool   (SectName,'NotKnownOp',False);
    FHourOp := THourOpList.Create;
    FHourOp.Add(THourOp.Create);
    for m:=0 to FHourOp.Count-1 do
     for h:=0 to 23 do
         FHourOp[m,h] := ReadFloat(SectName,Format('h%2.2d_%2.2d',[m,h]),0);
  end;
end;

procedure TDeviceEl.SeasonAdded(index: integer);
var
 i,h : integer;
begin
  if FHourOp.Count<index+1 then
  for i:=FHourOp.Count to index do
  begin
    FHourOp.Add(THourOp.Create);
    // Αντιγραφή στοιχείων της
    // προηγούμενης περιόδου 
    for h:=0 to 23 do
     FHourOp[i,h] := FHourOp[i-1,h]
  end;
end;

function TDeviceEl.PerDayEnergy: float;
var
 h : integer;
begin
  result := 0;
  for h := 0 to 23 do
    result := result + FRatedPower * FHourOp[0,h];
end;


procedure TDeviceEl.SaveToListFile(Ini: TIniFile; const SectName: string);
var
 m,h : integer;
begin
  with ini do
  begin
    EraseSection(SectName);
    WriteInteger(SectName,'ImageIndex',FImageIndex);
    WriteString (SectName,'Name',FDevName);
    WriteInteger(SectName,'DevCount',FDevCount);
    WriteFloat  (SectName,'RatedPower',FRatedPower);
    for m:=0 to FHourOp.Count-1 do
     for h:=0 to 23 do
      if FHourOp[m,h]<>0 then
         WriteFloat(SectName,Format('h%2.2d_%2.2d',[m,h]),FHourOp[m,h]);
  end;
end;

procedure TDeviceEl.SeasonDeleted(index: integer);
begin
   if index<=FHourOp.Count-1 then
      FHourOp.delete(index);
end;

{ TDevList }

procedure TDevList.ConstructPowerDemand(var pl : TFloatArray);
var
 i  : integer;
 dv : integer;
 h  : integer;
begin
  for i:=0 to Length(pl)-1 do
  begin
    pl[i] := 0;
    h := (i+1) mod 24;
    for dv:=0 to Count-1 do
        pl[i] := pl[i] + Items[dv].FHourOp[0,h] * Items[dv].FRatedPower;
  end;
end;

function TDevList.GetItem(index: integer): TDeviceEl;
begin
  result := TDeviceEl(inherited items[index]) ;
end;

procedure TDevList.LoadFromFile(const FileNm: string);
var
  aIni  : TIniFile;
  aDev  : TDeviceEl;
  cnt,i : integer;
begin
  aIni := TIniFile.Create(FileNm);
  Clear;
  with aIni do
  try
    DecimalSeparator := '.';
    cnt := ReadInteger('devlist','count',0);
    for i:=0 to cnt-1 do
    begin
      aDev := TDeviceEl.LoadFromListFile(aIni,Format('dev%d',[i]));
      Add(ADev);
    end;
  finally
    DecimalSeparator := dfDecimalSeparator;
  end;
end;

procedure TDevList.SaveToFile(const FileNm: string);
var
  aIni : TIniFile;
  aDev : TDeviceEl;
  i    : integer;
begin
  aIni := TIniFile.Create(FileNm);
  with aIni do
  try
    DecimalSeparator := '.';
    WriteInteger('devlist','count',count);
    for i:=0 to count-1 do
    begin
      aDev := Items[i];
      aDev.SaveToListFile(aIni,Format('dev%d',[i]));
    end;
  finally
    DecimalSeparator := dfDecimalSeparator;
  end;
end;

{ TPhFactors }

function TPhFactors.GetData(m, h: integer): float;
begin
  result := mData[m,h]
end;

function TPhFactors.GethFactor(h: integer): float;
var
 m,hh : byte;
begin
  m  := MonthOf(h);
  hh := HourOf(h);
  result := mData[m,hh];
end;


procedure TPhFactors.LoadFromFile(const FileName: string);
var
 io  : TextFile;
 m,h : byte;
begin
  AssignFile(io,FileName);
  Reset(io);
  try
    for h:=0 to 23 do
    begin
      for m:=1 to 11 do
          Read(io,mData[m,h]);
      ReadLn(io,mData[12,h]);
    end;
  finally
    CloseFile(io);
  end;
end;

procedure TPhFactors.SaveToFile(const FileName: string);
var
 io : TextFile;
 m,h : byte;
begin
  AssignFile(io,FileName);
  Rewrite(io);
  try
    for h:=0 to 23 do
    begin
      for m:=1 to 11 do
          Write(io,mData[m,h]);
      WriteLn(io,mData[12,h]);
    end;
  finally
    CloseFile(io);
  end;
end;

procedure TPhFactors.SetData(m, h: integer; const Value: float);
begin
  mData[m,h] := value;
end;

function apeCalc(var vi,pph,pl : TFloatArray;
                 WG : TWindPwrCurve;
                 MaxBateryCapacity: Float;
                 StepFunc : TStepFunc) : TCalcResult;
var
 MinBateryCapacity : float;
 pce               : float;
 i                 : integer;
 pw                : float;
 ave               : float;
 phz               : float;
 pdl               : float;
 ce                : float;
 spape             : float;
 cpape             : float;
begin
  if WG<>Nil then
  begin
    Result.spl  := 0;
    Result.spw  := 0;
    Result.sphz := 0;
    Result.spdl := 0;
    Result.spph := 0;

    // Αρχικώς η Μπαταρία γεμάτη

    MinBateryCapacity := SafeBateryCoef * MaxBateryCapacity;

    pce := MaxBateryCapacity*Byte(IsBatteryInitialFull)+
           MinBateryCapacity*Byte(not IsBatteryInitialFull);

    ce  := pce;
    Result.cef := pce;
    for i:= 0 to High(vi) do
    begin
      PW := WGNumber*WG.PwrAt(VI[i]*VelocityFactor);

      // Διαθέσιμη ενέργεια για αυτό το Χρονικό Διάστημα
      ave :=  PCE      // Οτι υπήρχε στην μπαταρία
            + pw       // ότι παρήγαγε η Α/Γ
            + pph[i];  // Οτι παρήγαγαν τα πανελ

      // Η Διαθέσιμη ενέργεια
      // δεν μπορεί να καλύψει την
      // το σύνολο της ζήτησης
      if ave-pl[i]<=MinBateryCapacity then
      begin
        // Η υπολοιπόμενη ενέργεια καλύπτεται
        // από ηλεκτροπαραγωγό ζεύγος
        phz := pl[i]-(ave-MinBateryCapacity);
        pdl := 0;
        // Η στάθμη της μπαταρίας
        // διατηρείται στο ελάχιστο όριο
        ce  := MinBateryCapacity;
      end
      else
      // Η διαθέσιμη ενέργεια υπέρκαλύπτει
      // την ζήτηση και ένα μέρος της
      // δεν μπορεί να αποθηκευτεί
      if ave-pl[i] >= MaxBateryCapacity then
      begin
        // Αποθεύεται όσο επιτρέπει
        // το μέγεθος της μπαταρίας
        CE  := MaxBateryCapacity;
        // και το υπόλοιπο είναι περίσεια
        pdl := ave-MaxBateryCapacity-pl[i];
        phz := 0;
      end
      else
      // Η διαθέσιμη ενέργεια καλύπτει
      // την ζήτηση και ένα μέρος της
      // μπορει να αποθηκευτεί
      begin
        // καλύπτεται η ζήτηση και
        // ένα μέρος της αποθηκεύεται
        CE  := pce + pph[i]+ pw-pl[i];
        phz := 0;
        pdl := 0;
      end;
      {
      if pw[i]+pph[i]-pdl[i]>0 then
         pwu[i] := pl[i]*pw[i]/(pw[i]+pph[i]-pdl[i])
      else
         pwu[i] := 0;
      }
      Result.spl  := Result.spl  + pl[i];
      Result.spw  := Result.spw  + pw;
      Result.sphz := Result.sphz + phz;
      Result.spdl := Result.spdl + pdl;
      Result.spph := Result.spph + pph[i];

      if Assigned(StepFunc) then
         StepFunc(i,pw,pph[i],pl[i],phz,pdl,ce);

      pce := ce;
    end;

    // Η ενέργεια που απαιτείται για
    // να ξαναγεμίσει η μπαταρία ..

   // Result.sphz := Result.sphz+(MaxBateryCapacity-ce);

    spape := Result.spw+Result.spph-Result.spdl;
    cpape := spape/Result.spl;


    Result.cphz    := Result.sphz/Result.spl;

    if Result.spw+Result.spph<>0 then
    begin
      Result.cpw  := cpape*Result.spw/(Result.spw+Result.spph);
      Result.cpph := cpape*Result.spph/(Result.spw+Result.spph);
    end
    else
    begin
      Result.cpw  := 0;
      Result.cpph := 0;
    end;
    result.cel := ce
  end;
end;


procedure LoadAsciiFile(const FileNm : string; var wvel : TFloatArray);
var
 Txt : TextFile;
 Tmp : string;
begin
  Assign(Txt,FileNm);
  SetLength(WVel,0);
  Reset(Txt);
  try
    DecimalSeparator := '.';
    ReadLn(Txt,Tmp);
    ReadLn(Txt,Tmp);
    ReadLn(Txt,Tmp);
    while not Eof(Txt) do
    begin
      SetLength(WVel,Length(WVel)+1);
      ReadLn(Txt,WVel[High(WVel)]);
    end;
  finally
    CloseFile(Txt);
    DecimalSeparator:=dfDecimalSeparator;
  end;
end;

function appPath : string;
begin
  result := ExtractFileDir(Application.ExeName);
  if (Length(result)>0) and
     (result[length(result)]='\') then
     system.Delete(result,length(result),1);
end;


procedure HoldFormClass(pnlHolder : TWinControl;
                         coolBarHolder : TWinControl;
                         BorderVisible : boolean;
                         ClosePrev     : boolean;
                         fmClass: TFormClass;
                         OnCreate : TNotifyEvent = nil);
var
 nf : TForm;
 i  : integer;
 toDel : TList;
begin
  with pnlHolder do
  begin
  //if pnlHolder.ComponentCount>0 then

    // fm := TForm(Components[0]);
    for i:=0 to ComponentCount-1 do
     if AnsiCompareText(Components[i].ClassName,fmClass.ClassName)=0 then
        Exit;
  end;


  nf := fmClass.Create(pnlHolder);
  if Assigned(OnCreate) then
     OnCreate(nf);
  nf.Align   := alClient;

  if CoolBarHolder<> nil then
  for i := 0 to nf.ComponentCount-1 do
  begin
    if nf.Components[i] is TCoolBar then
      TCoolBar(nf.Components[i]).Parent := CoolBarHolder;
  end;

  nf.BorderIcons := [];
  if not BorderVisible then
     nf.BorderStyle := bsNone;
  nf.Parent  := pnlHolder;
  nf.Visible := True;


 if ClosePrev then
 with pnlHolder do
 begin
   toDel := TList.Create;
   try
    for i:=0 to ComponentCount-1 do
      if Components[i]<>nf then
         ToDel.Add(Components[i]);

    for i:=0 to ToDel.Count-1 do
        TComponent(toDel[i]).Free;
   finally
     toDel.Free;
   end;
 end;


end;

procedure LoadDefaults;
begin
  if FileExists(AppPath+'\default.pph') then
     phFactors.LoadFromFile(appPath+'\default.pph');
  if FileExists(AppPath+'\default.pow') then
     wg.LoadFromWaspPow(AppPath+'\default.pow');
  if FileExists(AppPath+'\default.dat') then
     LoadAsciiFile(AppPath+'\default.dat',vi);
  {
  if FileExists(AppPath+'\default.ssn') then
     snl.LoadFromIni(AppPath+'\default.ssn');
  }   
     //LoadAsciiFile(AppPath+'\default.dat',vi);
end;


{ TSeasonList }

function TSeasonList.AddSsn: TSeason;
begin
  result := TSeason(inherited add);
end;

function TSeasonList.GetItems(index: integer): TSeason;
begin
  result := TSeason(inherited items[index]);
end;

function TSeasonList.GetLast: TSeason;
begin
  result := nil;
  if count>0 then
     result := items[count-1];
end;

procedure TSeasonList.LoadFromIni(ini: TIniFile);
var
 cnt,
 i : integer;

begin
  with ini do
  begin
    Clear;
    cnt := ReadInteger('Seasons','count',1);
    for i:=0 to cnt-1 do
    with AddSsn do
    begin
      Description := ReadString (format('season%d',[i]),'Description',Description);
      DayStart    := ReadInteger(format('season%d',[i]),'DayStart',1);
      DayStop     := ReadInteger(format('season%d',[i]),'DayStop',365);
      WayOp       := ReadInteger(format('season%d',[i]),'WayOp',0);
    end;
  end;
end;

procedure TSeasonList.SaveToIni(ini: TIniFile);
var
 i : integer;
begin
  with ini do
  begin
    writeInteger('Seasons','count',Count);
    for i:=0 to count-1 do
    with Items[i] do
    begin
      WriteString (format('season%d',[i]), 'Description',Description);
      WriteInteger(format('season%d',[i]),'DayStart',DayStart);
      WriteInteger(format('season%d',[i]),'DayStop',DayStop);
      WriteInteger(format('season%d',[i]),'WayOp',WayOp);
    end;
  end;
end;

{ TSeason }

constructor TSeason.create;
begin
  inherited Create(collection);

  FDayStart :=   1;
  FDayStop  := 365;
end;

function TSeason.GetDays: integer;
begin
  if FDayStop>=FDayStart then
     Result := FDayStop - FDayStart + 1
  else
     result := FDayStop +( 365 - FDayStart) + 1;
end;


function TSeason.GetHourStart: integer;
begin
  result := DayStart * 24
end;

function TSeason.GetHourStop: integer;
begin
  result := DayStop * 24;
end;


procedure TSeason.SetDescription(const Value: string);
begin
  FDescription := Value;
end;

procedure TSeason.SetHourStart(const Value: integer);
begin
  DayStart := (Value div 24) + 1;
end;

procedure TSeason.SetHourStop(const Value: integer);
begin
  FDayStop := (value div 24) + 1
end;

procedure TSeason.SetWayOp(const Value: byte);
begin
  FWayOp := Value;
end;

{ THourOp }

function THourOp.GetHourOp(index: integer): float;
begin
  result := FHourOp[index];
end;

procedure THourOp.SetHourOp(index: integer; const Value: float);
begin
  FHourOp[index] := value;
end;

{ THourOpList }

function THourOpList.GetHour(ssn, hour: integer): float;
begin
  result := items[ssn].Hourop[hour];
end;

function THourOpList.GetItems(index: integer): THourOp;
begin
  result := THourOp(inherited items[index]);
end;

procedure THourOpList.SetHour(ssn, hour: integer; const Value: float);
begin
  items[ssn].Hourop[hour] := value;
end;

procedure THourOpList.SetItems(index: integer; const Value: THourOp);
begin
  items[index] := value;
end;


function OpenDialogExec;
begin
  Result := '';
  with TOpenDialog.Create(Application) do
  try
    with FilterList do
    begin
      if FilterList.IndexOfName(AFilter)>=0 then
         InitialDir := Values[AFilter]
      else
         InitialDir := ExtractFileDir(Application.ExeName);
    end;
    Filter     := AFilter;
    FileName   := AFileName;
    DefaultExt := Copy(AFilter,Length(AFilter)-3,3);
    if Execute then
    begin
      Result := FileName;
      FilterList.Values[AFilter]:=ExtractFileDir(FileName);
    end;
  finally
    Free;
  end;
end;


function SaveDialogExec;
begin
  Result := '';
  with TSaveDialog.Create(Application) do
  try
    Filter     := AFilter;
    FileName   := AFileName;
    DefaultExt := Copy(AFilter,Length(AFilter)-3,3);
    InitialDir := ExtractFileDir(Application.ExeName);
    if Execute then
       Result := FileName;
  finally
    Free;
  end;
end;

initialization
  dfDecimalSeparator := DecimalSeparator;
  phFactors := TphFactors.Create;
  FDevList  := TDevList.Create;

  SetLength(Vi ,YearParts);
  SetLength(PW ,YearParts);
  SetLength(PL ,YearParts);
  SetLength(PPh,YearParts);
  SetLength(CE ,YearParts);
  SetLength(PHZ,YearParts);
  SetLength(PDL,YearParts);

  DateBase := EncodeDate(1991,1,1);
  snl      := TSeasonList.Create(TSeason);
  with snl.AddSsn do
  begin
    Description := 'Ολο το έτος'; // Ολος ο χρόνος
    DayStop     := 365;
  end;

  WG := TWindPwrCurve.Create;

  FilterList := TStringList.Create;

  LoadDefaults;

finalization
  SetLength(Vi ,0);
  SetLength(PW ,0);
  SetLength(PL ,0);
  SetLength(PPh,0);
  SetLength(CE ,0);
  SetLength(PHZ,0);
  SetLength(PDL,0);

  FDevList.Free;
  phFactors.Free;
  FilterList.Free;
end.
