{ Предшественник для эмуляторов контроллера } unit MI1201AGM_Controller_Emulator; interface USES Windows, SysUtils, Classes, Emulator_Logger, TimeInterval_Emulator, MI1201AGM_Emulator_Notificator, MI1201AGM_Hardware_Emulator; const cBasePort=$ED00; cDataSignature='Controller emulator data. '^M^L; type tPortOperation=(opRead, opWrite); tPortOperations=set of tPortOperation; tPortComment=array[tPortOperation] of string; tEventID=integer; tBaseEvent=( evNothing, evDestroying, evDestroyed, evDataRestored, evReInit, evPortRead, evPortWrite, evCustom, evLast ); const cBaseEventRange=0; cCustomEventRange=1 shl 4; type // Исключительная ситуация - неверный порт EInvalidPort=class(Exception) constructor CreateEx(const AControllerName:string; APort:word); end; // Исключительная ситуация - общий для всех контроллеров EController=class(Exception) function ControllerName:string; dynamic; constructor CreateEx(const AControllerName:string); constructor Create(const Msg:string); constructor CreateFmt(const Msg: string; const Args: array of const); end; tDataSignature=array[1..Length(cDataSignature)] of char; tControlFlag=( cfExceptionOnInvalidPort, cfInvalidPort, cfReadFromInvalidPort, cfWriteToInvalidPort, cfPortIOLoggingON, cfPortIOLoggingRewrite, cfStoreLastPortValues, cfLast ); tControlFlags=set of tControlFlag; tData=record Signature:tDataSignature; ControlFlags:tControlFlags; LoggingSize:cardinal; LoggingMode:tEmulatorLoggerMode; end; tPData=^tData; tReadWriteValues=record Read,Write:byte; end; tPortValues=array[tPortOperation] of byte; tPortReadWriteValues=record Current,Previos:tPortValues; end; tLastPortsList=record Lock:tHandle; TimeOut:cardinal; Length:cardinal; Ports:array of word; Values:array of tReadWriteValues; end; tTmpData=record ErrorCode:cardinal; OnError:tNotifyEvent; LastPort:word; LastPortOperation:tPortOperation; LastPortValue:byte; NotifyEvent:tEmulatorNotifyEvent; LogFileName:string; LogFile:TEmulatorLogger; // LastPorts:tLastPortsList; PortValues:array of tPortReadWriteValues; end; tController=class(TObject) private prHardware:tMi1201HardwareEmulator; prEntranceCounter:integer; prData:tData; prTmpData:tTmpData; // function WaitLastPortData:boolean; // function ReleasetLastPortData:boolean; // function LocatePort(APort:word):integer; // function AddPort(APort:word):integer; // function GetLastReadValue(APort:word):byte; // function GetLastWriteValue(APort:word):byte; function xGetLastValue(APort:word; AOperation:tPortOperation):byte; function xGetPreviosValue(APort:word; AOperation:tPortOperation):byte; protected // procedure SetLastReadValue(APort:word; AValue:byte); // procedure SetLastWriteValue(APort:word; AValue:byte); procedure xSetLastValue(APort:word; AOperation:tPortOperation; AValue:byte); function CheckData(Data:pointer):boolean; virtual; function DataSize:integer; virtual; function SubPath:string; virtual; abstract; function ReadData:boolean; virtual; function WriteData:boolean; virtual; function SetData(aData:pointer):boolean; virtual; function GetData(aData:pointer):boolean; virtual; function SaveData(var F:File):boolean; virtual; function RestData(var F:File):boolean; virtual; procedure prOutByte(b:Byte; port:word); virtual; abstract; function prInByte(port:word):Byte; virtual; abstract; function GetRaiseOnInvalidPort:boolean; procedure SetRaiseOnInvalidPort(AState:boolean); function GetInvalidPort:boolean; procedure SetInvalidPort(AState:boolean); procedure SetFlag(AState:boolean; AFlag:tControlFlag); function GetWriteToInvalidPort:boolean; procedure SetWriteToInvalidPort(AState:boolean); function GetReadFromInvalidPort:boolean; procedure SetReadFromInvalidPort(AState:boolean); procedure SetErrorCode(ACode:cardinal); procedure DoOnError; virtual; function GetErrorMsgs(AError:cardinal):string; virtual; function GetErrorMsg:string; procedure DoNotify(AEvent:tEventID); overload; virtual; procedure Notify(AEvent:tBaseEvent); overload; procedure DoNotify(AEvent:tBaseEvent; ACustomEventID:integer); overload; virtual; procedure Notify(ACustomEvent:integer); overload; procedure Notify(AEvent:tBaseEvent; ACustomEvent:integer); overload; procedure SetLogFileName(AFileName:string); function InitLogging:boolean; function StopLogging:boolean; function LogIsON:boolean; procedure SetLogIsOn(state:boolean); procedure SetLoggingSize(ASize:cardinal); procedure SetLoggingMode(AMode:tEmulatorLoggerMode); function GetPortsCount:cardinal; virtual; function GetPort(Number:cardinal):word; virtual; function GetPortName(Number:cardinal):string; virtual; function GetPortOperation(APort:word):tPortOperations; virtual; function GetPortComment(APort:word; Regime:tPortOperation):string; virtual; function GetPortNumber(APort:word):integer; virtual; function GetParametersCount:cardinal; virtual; function GetParameter(i:cardinal):variant; virtual; function GetParameterName(i:cardinal):string; virtual; public constructor Create(Hardware:tMi1201HardwareEmulator); destructor Destroy; override; procedure ReInit; virtual; abstract; procedure SetDefaultData; virtual; abstract; function CheckSelfData:boolean; function ValidPort(APort:word):boolean; function ValidReadPort(APort:word):boolean; function ValidWritePort(APort:word):boolean; procedure OutByte(b:Byte; port:word); function InByte(port:word):Byte; property EntranceCount:integer read prEntranceCounter; property ControlFlags:tControlFlags read prData.ControlFlags; property ExceptionOnInvalidPort:boolean read GetRaiseOnInvalidPort; property InvalidPort:boolean read GetInvalidPort write SetInvalidPort; property ReadFromInvalidPort:boolean read GetReadFromInvalidPort write SetReadFromInvalidPort; property WriteToInvalidPort:boolean read GetWriteToInvalidPort write SetWriteToInvalidPort; property Hardware:tMi1201HardwareEmulator read prHardware; property ErrorCode:cardinal read prTmpData.ErrorCode write SetErrorCode; property OnError:tNotifyEvent read prTmpData.OnError write prTmpData.OnError; property ErrorMsg:string read GetErrorMsg; property ErrorMsgs[AError:cardinal]:string read GetErrorMsgs; property LastPort:word read prTmpData.LastPort write prTmpData.LastPort; property LastPortOperation:tPortOperation read prTmpData.LastPortOperation write prTmpData.LastPortOperation; property LastPortValue:byte read prTmpData.LastPortValue write prTmpData.LastPortValue; // property PortReadValue[Port:word]:byte read GetLastReadValue; // property PortWriteValue[Port:word]:byte read GetLastWriteValue; // процедура оповещения property NotifyEvent:tEmulatorNotifyEvent read prTmpData.NotifyEvent write prTmpData.NotifyEvent; property LogFileName:string read prTmpData.LogFileName write SetLogFileName; property LoggingOn:boolean read LogIsON write SetLogIsOn; property LoggingSize:cardinal read prData.LoggingSize write SetLoggingSize; property LoggingMode:tEmulatorLoggerMode read prData.LoggingMode write SetLoggingMode; property PortsCount:cardinal read GetPortsCount; property Port[n:cardinal]:word read GetPort; property PortName[n:cardinal]:string read GetPortName; property PortOperations[Port:word]:tPortOperations read GetPortOperation; property PortComment[Port:word; Regime:tPortOperation]:string read GetPortComment; property PortNumber[Port:word]:integer read GetPortNumber; property PortLastValue[Port:word; Operation:tPortOperation]:byte read xGetLastValue write xSetLastValue; property PortPreviosValue[Port:word; Operation:tPortOperation]:byte read xGetPreviosValue; property ParametersCount:cardinal read GetParametersCount; property Parameter[i:cardinal]:variant read GetParameter; property ParameterName[i:cardinal]:string read GetParameterName; function ParameterIndex(AName:string):integer; end; implementation USES Emulator_Registry; function tController.GetParametersCount:cardinal; begin Result:=0; end; function tController.GetParameter(i:cardinal):variant; begin Result:=0; end; function tController.GetParameterName(i:cardinal):string; begin Result:='?'; end; function tController.ParameterIndex(AName:string):integer; var i:integer; begin Result:=-1; i:=ParametersCount; for i:=0 to Pred(i) do begin if AName=ParameterName[i] then begin Result:=i; Exit; end; end; end; function tController.GetPort(Number:cardinal):word; begin Result:=0; end; function tController.GetPortName(Number:cardinal):string; begin if Number0 then begin il:=0; while (ilAPort then ir:=Result else il:=Succ(Result); end; Result:=-1; end; end; (*function tController.ReleasetLastPortData:boolean; begin Result:=(prTmpData.LastPorts.Lock<>0) and ReleaseMutex(prTmpData.LastPorts.Lock); end; function tController.WaitLastPortData:boolean; begin Result:=(prTmpdata.LastPorts.Lock<>0) and (WaitForSingleObject(prTmpdata.LastPorts.Lock,prTmpData.LastPorts.TimeOut)<>WAIT_TIMEOUT); end; function tController.LocatePort(APort:word):integer; var il,ir:integer; x:word; begin Result:=-High(Result); if (prTmpdata.LastPorts.Length>0) and WaitLastPortData then begin ir:=prTmpdata.LastPorts.Length; if ir>0 then begin il:=0; while (ilAPort then ir:=Result else il:=Succ(Result); end; Result:=-ir; end; ReleasetLastPortData; end; end; function tController.AddPort(APort:word):integer; var s:cardinal; begin Result:=-1; if WaitLastPortData then begin Result:=LocatePort(APort); if (Result<0) then begin Inc(prTmpdata.LastPorts.Length); s:=prTmpdata.LastPorts.Length; if s>Length(prTmpdata.LastPorts.Ports) then begin SetLength(prTmpdata.LastPorts.Ports, s+10); SetLength(prTmpdata.LastPorts.Values, s+10); end; if (Result=-High(Result)) then begin Result:=Pred(s); end else begin Result:=-Result; Dec(s,Result); System.Move( prTmpdata.LastPorts.Ports[Result], prTmpdata.LastPorts.Ports[Succ(Result)], SizeOf(prTmpdata.LastPorts.Ports[0])*s ); System.Move( prTmpdata.LastPorts.Values[Result], prTmpdata.LastPorts.Values[Succ(Result)], SizeOf(prTmpdata.LastPorts.Values[0])*s ); end; prTmpdata.LastPorts.Ports[Result]:=APort; prTmpdata.LastPorts.Values[Result].Read:=0; prTmpdata.LastPorts.Values[Result].Write:=0; end; ReleasetLastPortData; end; end; function tController.GetLastReadValue(APort:word):byte; var i:integer; begin If WaitLastPortData then begin i:=LocatePort(APort); if i>=0 then begin Result:=prTmpData.LastPorts.Values[i].Read; end else begin Result:=$00; end; ReleasetLastPortData; end else begin Result:=$FF; end; end; *) function tController.xGetLastValue(APort:word; AOperation:tPortOperation):byte; var i,l:integer; begin Result:=0; l:=Length(prTmpData.PortValues); If l>0 then begin i:=PortNumber[APort]; if (i>=0) and (i0 then begin i:=PortNumber[APort]; if (i>=0) and (i=0 then begin Result:=prTmpData.LastPorts.Values[i].Write; end else begin Result:=$00; end; ReleasetLastPortData; end else begin Result:=$FF; end; end; procedure tController.SetLastReadValue(APort:word; AValue:byte); var i:integer; begin If WaitLastPortData then begin i:=AddPort(APort); if i>=0 then begin prTmpData.LastPorts.Values[i].Read:=AValue; end; ReleasetLastPortData; end; end; *) procedure tController.xSetLastValue(APort:word; AOperation:tPortOperation; AValue:byte); var i,l:integer; begin l:=Length(prTmpData.PortValues); If l=0 then begin try SetLength(prTmpData.PortValues, PortsCount); l:=Length(prTmpData.PortValues); except end; end; If l>0 then begin i:=PortNumber[APort]; if (i>=0) and (i=0 then begin prTmpData.LastPorts.Values[i].Write:=AValue; end; ReleasetLastPortData; end; end; *) function tController.GetPortsCount:cardinal; begin Result:=0; end; function tController.GetPortOperation(APort:word):tPortOperations; begin Result:=[]; end; function tController.GetPortComment(APort:word; Regime:tPortOperation):string; begin Result:='?'; end; procedure tController.SetLoggingSize(ASize:cardinal); begin if Assigned(prTmpData.LogFile) then begin try prTmpData.LogFile.SizeLimit:=ASize; prData.LoggingSize:=prTmpData.LogFile.SizeLimit; except end; end else begin prData.LoggingSize:=ASize; end; end; procedure tController.SetLoggingMode(AMode:tEmulatorLoggerMode); begin if Assigned(prTmpData.LogFile) then begin try prTmpData.LogFile.Mode:=AMode; prData.LoggingMode:=prTmpData.LogFile.Mode; except end; end else begin prData.LoggingMode:=AMode; end; end; function tController.InitLogging:boolean; begin if not Assigned(prTmpData.LogFile) then begin try prTmpData.LogFile:=TEmulatorLogger.Create(LogFileName,LoggingMode,LoggingSize); except end; end; Result:=Assigned(prTmpData.LogFile); end; function tController.StopLogging:boolean; begin if Assigned(prTmpData.LogFile) then begin FreeAndNil(prTmpData.LogFile); StopLogging:=TRUE; end else begin StopLogging:=FALSE; end; end; function tController.LogIsON:boolean; begin Result:=Assigned(prTmpData.LogFile) and prTmpData.LogFile.Enable; end; procedure tController.SetLogIsOn(state:boolean); begin if state then begin if not Assigned(prTmpData.LogFile) then try prTmpData.LogFile:=tEmulatorLogger.Create(LogFileName); Include(prData.ControlFlags,cfPortIOLoggingON); except end; end else begin Exclude(prData.ControlFlags,cfPortIOLoggingON); end; if Assigned(prTmpData.LogFile) then try prTmpData.LogFile.Enable:=state; except end; end; procedure tController.SetLogFileName(AFileName:string); begin if Assigned(prTmpData.LogFile) then begin prTmpData.LogFile.DestinationName:=AFileName; end; prTmpData.LogFileName:=AFileName; end; procedure tController.DoNotify(AEvent:tEventID); var localNotifyEvent:tEmulatorNotifyEvent; localEvent:tEvent; begin localNotifyEvent:=NotifyEvent; if Assigned(localNotifyEvent) then begin try localEvent.Base.ID:=evController; localEvent.Base.SubID:=AEvent; localNotifyEvent(Self,localEvent); except end; end; end; procedure tController.DoNotify(AEvent:tBaseEvent; ACustomEventID:integer); var localNotifyEvent:tEmulatorNotifyEvent; localEvent:tEvent; begin localNotifyEvent:=NotifyEvent; if Assigned(localNotifyEvent) then begin try localEvent.Base.ID:=evController; localEvent.Base.SubID:=Ord(AEvent); localEvent.Base.CustomID:=ACustomEventID; localNotifyEvent(Self,localEvent); except end; end; end; procedure tController.Notify(AEvent:tBaseEvent); begin DoNotify(Ord(AEvent)); end; procedure tController.Notify(AEvent:tBaseEvent; ACustomEvent:integer); begin DoNotify(AEvent, ACustomEvent); end; procedure tController.Notify(ACustomEvent:integer); begin DoNotify(evCustom, ACustomEvent); end; function tController.GetErrorMsgs(AError:cardinal):string; resourcestring cUnknown='Неизвестная ошибка'; begin Result:=cUnknown; end; function tController.GetErrorMsg:string; begin Result:=GetErrorMsgs(Error); end; procedure tController.DoOnError; var localOnError:tNotifyEvent; begin localOnError:=OnError; if Assigned(localOnError) then begin try localOnError(Self); except end; end else DoNotify(-Error); end; function tController.DataSize:integer; begin Result:=SizeOf(prData); end; function tController.ReadData:boolean; var aData:pointer; sz:integer; begin Result:=FALSE; if EmulatorRegistry.OpenRead(SubPath) then begin sz:=DataSize; try GetMem(aData,sz); except aData:=NIL; end; if Assigned(aData) then begin try EmulatorRegistry.ReadBinaryData('Data',aData,SizeOf(aData)); Result:=SetData(aData); except end; FreeMem(aData,sz); end; EmulatorRegistry.CloseKey; end; end; constructor tController.Create(Hardware:tMi1201HardwareEmulator); begin Inherited Create; prData.Signature:=cDataSignature; prHardware:=Hardware; SetDefaultData; ReadData; ReInit; if cfPortIOLoggingON in ControlFlags then InitLogging; // prTmpData.LastPorts.Lock:=CreateMutex(NIL,FALSE,''); // prTmpData.LastPorts.TimeOut:=10000; end; destructor tController.Destroy; var h:tHandle; begin Inherited; Notify(evDestroying); prHardware:=NIL; WriteData; Notify(evDestroyed); StopLogging; // h:=prTmpData.LastPorts.Lock; // prTmpData.LastPorts.Lock:=0; // if h<>0 then // CloseHandle(h); end; function tController.WriteData:boolean; var aData:pointer; sz:integer; begin Result:=FALSE; if EmulatorRegistry.OpenWrite(SubPath) then begin sz:=DataSize; try GetMem(aData,sz); except aData:=NIL; end; if Assigned(aData) then begin try EmulatorRegistry.ReadBinaryData('Data',aData^,sz); Result:=TRUE; except end; FreeMem(aData,sz); end; EmulatorRegistry.CloseKey; end; end; function tController.CheckData(Data:pointer):boolean; begin Result:=(tPData(Data)^.Signature=prData.Signature); end; function tController.CheckSelfData:boolean; begin Result:=CheckData(@prData); end; function tController.SetData(aData:pointer):boolean; begin Result:=CheckData(aData); if Result then begin Move(aData^,prData,SizeOf(prData)); end; end; function tController.GetData(aData:pointer):boolean; begin Move(prData,aData^,SizeOf(prData)); Result:=TRUE; end; function tController.SaveData(var F:File):boolean; var pos:cardinal; c:integer; begin pos:=FilePos(f); BlockWrite(f,prData,SizeOf(prData),c); if c<>SizeOf(prData) then begin Seek(F,pos); Result:=False; end else begin Result:=TRUE; end; end; function tController.RestData(var F:File):boolean; var pos:integer; c:integer; aData:pointer; sz:integer; begin Result:=False; pos:=FilePos(f); sz:=DataSize; if (FileSize(f)-pos)>=sz then begin try GetMem(aData,sz); except aData:=NIL; end; if Assigned(aData) then begin BlockRead(f,aData^,sz,c); if c<>sz then begin Seek(F,pos); end else if SetData(aData) then begin Result:=TRUE; end else begin Seek(F,pos); end; FreeMem(aData,sz); end; end; end; procedure tController.OutByte(b:Byte; port:word); begin InterlockedIncrement(prEntranceCounter); LastPort:=port; LastPortValue:=b; LastPortOperation:=opWrite; // if cfStoreLastPortValues in ControlFlags then // SetLastWriteValue(Port,b); PortLastValue[Port,opWrite]:=b; prOutByte(b, Port); Notify(evPortWrite); InterlockedDecrement(prEntranceCounter); end; function tController.InByte(port:word):Byte; begin InterlockedIncrement(prEntranceCounter); LastPort:=port; LastPortOperation:=opRead; Result:=prInByte(Port); LastPortValue:=Result; // if cfStoreLastPortValues in ControlFlags then // SetLastReadValue(Port,Result); PortLastValue[Port,opRead]:=Result; Notify(evPortRead); InterlockedDecrement(prEntranceCounter); end; constructor EInvalidPort.CreateEx(const AControllerName:string; APort:word); resourcestring cEInvalidPort='%s: неверный номер порта %u (%xh).'; begin Inherited CreateFmt(cEInvalidPort,[AControllerName,APort,APort]); end; function tController.GetRaiseOnInvalidPort:boolean; begin Result:=cfExceptionOnInvalidPort in ControlFlags; end; procedure tController.SetRaiseOnInvalidPort(AState:boolean); begin SetFlag(AState,cfExceptionOnInvalidPort); end; function tController.GetInvalidPort:boolean; begin Result:=cfInvalidPort in ControlFlags; end; procedure tController.SetInvalidPort(AState:boolean); begin SetFlag(AState,cfInvalidPort); end; procedure tController.SetFlag(AState:boolean; AFlag:tControlFlag); begin if AState then Include(prData.ControlFlags,AFlag) else Exclude(prData.ControlFlags,AFlag); end; function tController.GetWriteToInvalidPort:boolean; begin Result:=cfWriteToInvalidPort in ControlFlags; end; procedure tController.SetWriteToInvalidPort(AState:boolean); begin SetFlag(AState,cfWriteToInvalidPort); end; function tController.GetReadFromInvalidPort:boolean; begin Result:=cfReadFromInvalidPort in ControlFlags; end; procedure tController.SetReadFromInvalidPort(AState:boolean); begin SetFlag(AState,cfReadFromInvalidPort); end; function tController.ValidPort(APort:word):boolean; begin Result:=FALSE; end; function tController.ValidReadPort(APort:word):boolean; begin Result:=FALSE; end; function tController.ValidWritePort(APort:word):boolean; begin Result:=FALSE; end; constructor EController.CreateEx(const AControllerName:string); resourcestring cInvalidController=': ошибка!'; begin Inherited Create(AControllerName+cInvalidController); end; constructor EController.Create(const Msg:string); begin Inherited Create(ControllerName+': '+Msg); end; constructor EController.CreateFmt(const Msg: string; const Args: array of const); begin Inherited CreateFmt(ControllerName+': '+Msg, Args); end; function EController.ControllerName:string; resourcestring cIonCounterName='Контроллер'; begin Result:=cIonCounterName; end; procedure tController.SetErrorCode(ACode:cardinal); begin if (ErrorCode<>ACode) then begin if (ErrorCode=0) or (ACode=0) then begin prTmpData.ErrorCode:=ACode; end; DoOnError; end; end; end.