%% Library=USER_DRIVER
%% UserDriver=UserDriver
LIBRARY %Library%;

{$I _sysdef}

uses ShareMem, SysUtils, Windows, Math, Classes, mmsystem, ShellApi, _CrwApi;

 //////////////////////////////////////////////////////////////////////////////
 // General purpose constants.
 //////////////////////////////////////////////////////////////////////////////
type
 TDebugMode	= (dmException, dmGreeting);

const
 DebugSet		: set of TDebugMode	= [dmException, dmGreeting];
 sndClick						= 'CLICK';	// Sound on button click
 sndFails						= 'FAILS';	// Sound on failure
 sndException					= 'EXCEPTION';	// Sound on exception

 //////////////////////////////////////////////////////////////////////////////
 // User hardware dependent procedures and constants.
 //////////////////////////////////////////////////////////////////////////////
procedure SoundOn(CrwApi:TCrwApi; wFrequency : Word);
const
 QuartzFrequency = 1193180.0;
var
 wTone : Word;
begin
 with CrwApi,DaqApi do
 if wFrequency>0 then begin
  wTone:=Round(QuartzFrequency/wFrequency);
  PortB[$61]:=PortB[$61] or $03;
  PortB[$43]:=$B6;
  PortB[$42]:=Lo(wTone);
  PortB[$42]:=Hi(wTone);
 end;
end;

procedure SoundOff(CrwApi:TCrwApi);
begin
 with CrwApi,DaqApi do
 PortB[$61]:=(PortB[$61] and $FC);
end;

procedure Sound(CrwApi:TCrwApi; wFrequency : Word; wDelay : LongWord);
begin
 SoundOn(CrwApi,wFrequency);
 Sleep(wDelay);
 SoundOff(CrwApi);
end;

 //////////////////////////////////////////////////////////////////////////////
 // General user driver object. To be placed in DaqDataSheet area.
 //////////////////////////////////////////////////////////////////////////////
type
 P%UserDriver% = ^T%UserDriver%;					// Pointer to user driver data
 T%UserDriver% = object						// General user driver object
 public
  CrwApi        : TCrwApi;					// Points to CrwApi interface
  dllFilePath   : ShortString;				// Executable DLL file path
  OwnDevice     : Integer;					// Device reference
 public
  procedure DAQ_CMD_INIT(TheCrwApi:TCrwApi);	// Driver initialization
  procedure DAQ_CMD_FREE;					// Driver finalization
  procedure DAQ_CMD_POLL;					// Driver polling loop
  procedure DAQ_CMD_FAIL;					// Driver failure handler
  procedure HardwareStop;					// Hardware specific stop
  procedure HardwareStart;					// Hardware specific start
  procedure HardwareReadout;					// Hardware specific polling
 end;

 //////////////////////////////////////////////////////////////////////////////
 // User driver initialization
 //////////////////////////////////////////////////////////////////////////////
procedure T%UserDriver%.DAQ_CMD_INIT(TheCrwApi:TCrwApi);
 //
 // Initialize tag with given name and type
 //
 procedure InitTag(var tag:integer; const name:ShortString; typ:integer);
 begin
  with CrwApi,SysApi,DaqApi do begin
   tag:=findtag(name);
   if (typ>0) and (typetag(tag)<>typ)
   then RAISE EDaqApi.Create(Format('%UserDriver%: wrong tag "%s".',[name]));
  end;
 end;
 //
 // Initialize reference
 //
 procedure InitRef(var ref:integer; value:integer);
 begin
  ref:=value;
  if ref=0 then RAISE EDaqApi.Create('%UserDriver%: Invalid reference.');
 end;
 //
 // Get environment variable
 //
 function GetEnv(const Name:LongString):ShortString;
 var
  Buffer : packed array[0..255] of Char;
 begin
  if GetEnvironmentVariable(PChar(Name), Buffer, SizeOf(Buffer))>0
  then Result:=StrPas(Buffer)
  else Result:='';
 end;
begin
 FillChar(Self,sizeof(Self),0);
 CrwApi:=TheCrwApi;
 with CrwApi,SysApi,GuiApi,DaqApi do begin
  RedirectStdIn(Input);
  RedirectStdOut(Output);
  dllFilePath:=readini('DLL_FILE_PATH');
  InitRef(OwnDevice,  FindObjectRef('Device',''));
  if dmGreeting in DebugSet then Echo(Format('%s: Initializing %s ...',[DeviceName(OwnDevice),dllFilePath]));
  // InitTag(tag1,	Format('%s',['tag1']),		1);
  // InitTag(tag2,	Format('%s',['tag2']),		1);
  if dmGreeting in DebugSet then Echo(Format('%s: Ok.',[DeviceName(OwnDevice)]));
  HardwareStart;
 end;
end;

 //////////////////////////////////////////////////////////////////////////////
 // %UserDriver% driver finalization, free DaqDataSheet
 //////////////////////////////////////////////////////////////////////////////
procedure T%UserDriver%.DAQ_CMD_FREE;
begin
 if Assigned(CrwApi) then
 with CrwApi,SysApi,GuiApi,DaqApi do begin
  if dmGreeting in DebugSet then Echo(Format('%s: Finalizing %s ...',[DeviceName(OwnDevice),dllFilePath]));
  HardwareStop;
  if dmGreeting in DebugSet then Echo(Format('%s: Ok.',[DeviceName(OwnDevice)]));
  DaqDataSheet(0);
 end;
end;

 //////////////////////////////////////////////////////////////////////////////
 // %UserDriver% driver main polling loop
 //////////////////////////////////////////////////////////////////////////////
procedure T%UserDriver%.DAQ_CMD_POLL;
begin
 if Assigned(CrwApi) then
 with CrwApi,SysApi,GuiApi,DaqApi do begin
  HardwareReadout;
 end;
end;

 //////////////////////////////////////////////////////////////////////////////
 // %UserDriver% driver failure if unrecognized command detected
 //////////////////////////////////////////////////////////////////////////////
procedure T%UserDriver%.DAQ_CMD_FAIL;
begin
 if Assigned(CrwApi) then
 with CrwApi,SysApi,GuiApi,DaqApi do
 RAISE EDaqApi.Create(Format('%s: wrong DaqCommand=%d!',[DeviceName(OwnDevice),DaqCommand]));
end;

 //////////////////////////////////////////////////////////////////////////////
 // %UserDriver% hardware stop
 //////////////////////////////////////////////////////////////////////////////
procedure T%UserDriver%.HardwareStop;
begin
 if Assigned(CrwApi) then
 with CrwApi,SysApi,DaqApi do begin
  SoundOff(CrwApi);
 end;
end;

 //////////////////////////////////////////////////////////////////////////////
 // %UserDriver% hardware start
 //////////////////////////////////////////////////////////////////////////////
procedure T%UserDriver%.HardwareStart;
begin
 if Assigned(CrwApi) then
 with CrwApi,SysApi,GuiApi,DaqApi do begin
  SoundOn(CrwApi,10000);
 end;
end;

 //////////////////////////////////////////////////////////////////////////////
 // %UserDriver% hardware readout
 //////////////////////////////////////////////////////////////////////////////
procedure T%UserDriver%.HardwareReadout;
 procedure Play;
 const t0:double=0;
 var t:Double;
 begin
  with CrwApi,SysApi,GuiApi,DaqApi do begin
   t:=int(msecnow/1000);
   if t<>t0 then begin
    t0:=t;
    t:=1000+(round(t) mod 10)*1000;
    SoundOn(CrwApi,round(t));
    Echo(Format('Sound %5.0f',[t]));
   end;
  end; 
 end;
begin
 if Assigned(CrwApi) then
 with CrwApi,SysApi,GuiApi,DaqApi do begin
  Play;
 end;
end;


 //////////////////////////////////////////////////////////////////////////////
 // MAIN PLUGIN FUNCTION
 //////////////////////////////////////////////////////////////////////////////
function CRW32_PLUGIN(TheCrwApi:TCrwApi):Integer; StdCall;
var %UserDriver%:P%UserDriver%;
begin
 Result:=0;
 with TheCrwApi,SysApi,GuiApi,DaqApi do
 try
  if Target <> ForDataAcquisition then RAISE EDaqApi.Create('%UserDriver%: Invalid Target!');
  %UserDriver%:=DaqDataSheet(sizeof(%UserDriver%^));
  if not Assigned(%UserDriver%)
  then RAISE EDaqApi.Create('%UserDriver%: Out of memory!');
  if Assigned(%UserDriver%.CrwApi) and (%UserDriver%.CrwApi<>TheCrwApi)
  then RAISE EDaqApi.Create('%UserDriver%: Invalid CrwApi.');
  case DaqCommand of
   DAQ_CMD_INIT: %UserDriver%.DAQ_CMD_INIT(TheCrwApi);	// Initialize %UserDriver% driver
   DAQ_CMD_FREE: %UserDriver%.DAQ_CMD_FREE;			// Finalize   %UserDriver% driver
   DAQ_CMD_POLL: %UserDriver%.DAQ_CMD_POLL;			// General    %UserDriver% data acquisition loop
   else          %UserDriver%.DAQ_CMD_FAIL;			// Detected   %UserDriver% driver failure
  end;
 except
  on E:Exception do begin
   Result:=-1;
   if dmException in DebugSet then begin
    if UsesBlaster then Voice(sndException);
    Echo(E.Message);
   end;
  end;
 end;			
end;

exports CRW32_PLUGIN name CRW32_PLUGIN_ID;
begin
end.

 
 