Index: cprs/branches/tmg-cprs/CPRS-Chart/UploadImages.pas
===================================================================
--- cprs/branches/tmg-cprs/CPRS-Chart/UploadImages.pas	(revision 453)
+++ cprs/branches/tmg-cprs/CPRS-Chart/UploadImages.pas	(revision 729)
@@ -7,15 +7,17 @@
   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
   Dialogs, StdCtrls, Buttons, ExtCtrls, pngimage, ExtDlgs, OleCtrls,
+  uCore,
   SHDocVw, DKLang;
 
 type
-  TUploadImageInfo = class
+
+  TImageInfo = class
     private
     public
-      TIUIEN :             int64;    //IEN in file# 8925
-      DFN :                AnsiString;    //IEN in Patient File (#2)
-      UploadDUZ :          int64;
+      TIUIEN :             int64;      //IEN in file# 8925
+      DFN :                AnsiString; //IEN in Patient File (#2)
+      UploadDUZ :          int64;      //IEN in NEW PERSON file
       ThumbFPathName :     AnsiString; // local file path name
-      ImageFPathName :     AnsiString;
+      ImageFPathName :     AnsiString; // local file path name
       ServerPath :         AnsiString;
       ServerFName :        AnsiString;
@@ -25,10 +27,36 @@
       ImageDateTime :      AnsiString;
       UploadDateTime:      AnsiString;
-      ObjectType :         Integer;  //pointer to file 2005.02
-      ProcName :           String[10];  //server limit is 10 chars.
-      //AcquisitionSite
-      pLongDesc :          TStrings;
-    published
-  end;
+      ObjectType :         int64;      //pointer to file 2005.02
+      ProcName :           String[10]; //server limit is 10 chars.
+      pLongDesc :          TStrings;  //Won't be owned by this list
+      procedure Assign(Source : TImageInfo);
+      procedure Clear;
+  end;
+
+  TAutoUploadNote = class
+    private
+    public
+      TIUIEN :       int64;      //IEN in file# 8925
+      ErrMsg :       AnsiString;
+      NoteTitle :    AnsiString; //Title of note to be associated with image
+      Patient :      TPatient;
+      ImageInfo :    TImageInfo;
+      Location :     AnsiString; //Location that image if from
+      DOS :          AnsiString; //Date of service
+      Provider :     AnsiString;
+      CurNoteImages: TStringList;
+      UploadError :  Boolean;
+      procedure SetDFN(var ChartNum,Location,FName,LName,MName,DOB,Sex : string);
+      Procedure SetInfo(var DOS,Provider,Location,Title : string);
+      function SameAs(OtherNote: TAutoUploadNote): boolean;
+      procedure MakeNewBlankNote(DFN,DOS,Provider,Location,Title : string);
+      procedure InitFrom(OtherNote: TAutoUploadNote);
+      function IsValid : boolean;
+      procedure Clear;
+      constructor Create();
+      destructor Destroy;  override;
+  end;
+
+
 
 
@@ -54,8 +82,9 @@
     NoteEdit: TEdit;
     PickOtherButton: TBitBtn;
-    Panel1: TPanel;
+    pnlIEHolder: TPanel;
     WebBrowser: TWebBrowser;
     Label6: TLabel;
     MoveCheckBox: TCheckBox;
+    PolTimer: TTimer;
     DKLanguageController1: TDKLanguageController;
     procedure UploadButtonClick(Sender: TObject);
@@ -69,18 +98,31 @@
     procedure PickOtherButtonClick(Sender: TObject);
     procedure FormRefresh(Sender: TObject);
-
+    procedure PolTimerTimer(Sender: TObject);
   private
     { Private declarations }
     Bitmap : TBitmap;
     Picture : TPicture;
-    function MakeThumbNail(Info: TUploadImageInfo): boolean;
+    FScanDir : String;
+    function MakeThumbNail(Info: TImageInfo): boolean;
 
     procedure LoadNotesEdit();
     //procedure LoadNotesList();
-    function UploadFile(Info: TUploadImageInfo): boolean;
+    function UploadFile(Info: TImageInfo; DelOrig : boolean): boolean;
     procedure UploadChosenFiles();
-
+    function ProcessOneLine(Line : string) : string;
+    function ProcessOneFile(FileName : string) : boolean;
+    procedure ScanAndHandleImgTxt;
+    procedure ScanAndHandleImages;
+    procedure DecodeImgTxt(Line : string; out ChartNum, Location,
+                           FName, LName, MName, Sex, DOB, DOS, Provider,
+                           Title : string; FilePaths : TStrings);
+    function EncodeImgTxt(ChartNum, Location, FName, LName, MName, Sex, DOB,
+                           DOS, Provider, Title : string; FilePaths : TStrings) : AnsiString;
+    procedure FinishDocument(UploadNote : TAutoUploadNote);
   public
     { Public declarations }
+    procedure SetScanDir(NewDir : string);
+  published
+    property ScanDir : String read FScanDir write SetScanDir;
   end;
 
@@ -95,9 +137,10 @@
         StrUtils, //for MidStr etc.
         ORFn,  //for PIECE etc.
-        uCore, // for User.DUZ etc
         Trpcb, //for .PType enum
         fImages, //for upload/download files etc.
         //Targa,  //for TGA graphic save
-        ORNet //for RPCBrokerV
+        ORNet, //for RPCBrokerV
+        rTIU,
+        uTMGOptions
         ;
 
@@ -105,6 +148,24 @@
 //  DefShortDesc = '(Short Image Description)';  <-- original line.  //kt 8/7/2007
 
+  type
+    TFileInfo = class
+    private
+    public
+      SrcRec : TSearchRec;
+      STimeStamp : String;
+      SBarCode : String;
+      FPath : String;
+      MetaFileName : String;
+      MetaFileExists : boolean;
+      BatchCount : integer;
+      procedure Assign(Source: TFileInfo);
+      procedure Clear;
+    end;
+
   var
     DefShortDesc : string; //kt
+    PolInterval : integer;
+    AutoUploadNote : TAutoUploadNote;
+
 
   procedure SetupVars;
@@ -115,5 +176,231 @@
   //-------------------------------------------------------------------------
   //-------------------------------------------------------------------------
-  function TUploadForm.MakeThumbNail(Info: TUploadImageInfo) : boolean;
+
+  function NumPieces(const s: string; ADelim : Char) : integer;
+  var List : TStringList;
+  begin
+    List := TStringList.Create;
+    PiecesToList(S, ADelim, List);
+    Result := List.Count;
+  end;
+
+  //-------------------------------------------------------------------------
+  //-------------------------------------------------------------------------
+  procedure TFileInfo.Assign(Source: TFileInfo);
+  begin
+    SrcRec := Source.SrcRec;
+    STimeStamp := Source.STimeStamp;
+    SBarCode := Source.SBarCode;
+    FPath := Source.FPath;
+    BatchCount := Source.BatchCount;
+    MetaFileName := Source.MetaFileName;
+    MetaFileExists := Source.MetaFileExists;
+  end;
+
+  procedure TFileInfo.Clear;
+  begin
+    //SrcRec := ...   //Note sure how to clear this.  Will leave as is...
+    STimeStamp := '';
+    SBarCode := '';
+    FPath := '';
+    BatchCount := 0;
+    MetaFileName := '';
+    MetaFileExists := false;
+  end;
+
+  //-------------------------------------------------------------------------
+  //-------------------------------------------------------------------------
+  procedure TImageInfo.Assign(Source : TImageInfo);
+  begin
+    TIUIEN := Source.TIUIEN;
+    DFN := Source.DFN;
+    UploadDUZ := Source.UploadDUZ;
+    ThumbFPathName := Source.ThumbFPathName;
+    ImageFPathName := Source.ImageFPathName;
+    ServerPath := Source.ServerPath;
+    ServerFName := Source.ServerFName;
+    ServerThumbFName := Source.ServerThumbFName;
+    ShortDesc := Source.ShortDesc;
+    Extension := Source.Extension;
+    ImageDateTime := Source.ImageDateTime;
+    UploadDateTime := Source.UploadDateTime;
+    ObjectType := Source.ObjectType;
+    ProcName := Source.ProcName;
+    pLongDesc := Source.pLongDesc;  //this is only a pointer to object owned elsewhere
+  end;
+
+  procedure TImageInfo.Clear;
+  begin
+    TIUIEN := 0;
+    DFN := '';
+    UploadDUZ := 0;
+    ThumbFPathName := '';
+    ImageFPathName := '';
+    ServerPath := '';
+    ServerFName := '';
+    ServerThumbFName := '';
+    ShortDesc := '';
+    Extension := '';
+    ImageDateTime := '';
+    UploadDateTime:= '';
+    ObjectType :=0;
+    ProcName := '';
+    pLongDesc := nil
+  end;
+
+  //-------------------------------------------------------------------------
+  //-------------------------------------------------------------------------
+  procedure TAutoUploadNote.SetDFN(var ChartNum,Location,FName,LName,MName,DOB,Sex : string);
+  var RPCResult : AnsiString;
+      PMS : AnsiString;
+  begin
+    //Notice: ChartNum, and PMS are optional.  If PMS is 1,2,or 3, then ChartNum
+    //        is used to look up patient.  Otherwise a lookup is based on just
+    //        Name, DOB, Sex.
+    //        To NOT use ChartNum, just set the values to ''
+    //
+    //Note: If LName is in form: `12345, then LName is used for DFN, and call
+    //      to server for lookup is bypassed, and the values for FName,DOB etc
+    //      are ignored
+
+    if MidStr(LName,1,1)='`' then begin
+      Self.Patient.DFN := MidStr(LName,2,999);
+    end else begin
+      //**NOTE**: site-specific code
+      if Location ='Laughlin_Office' then PMS :='2'
+      else if Location ='Peds_Office' then PMS :='3'
+      else PMS := ''; //default
+
+      RPCBrokerV.ClearParameters := true;
+      RPCBrokerV.remoteprocedure := 'TMG GET DFN';
+      RPCBrokerV.param[0].value := ChartNum;  RPCBrokerV.param[0].ptype := literal;
+      RPCBrokerV.param[1].value := PMS;       RPCBrokerV.Param[1].ptype := literal;
+      RPCBrokerV.param[2].value := FName;     RPCBrokerV.Param[2].ptype := literal;
+      RPCBrokerV.param[3].value := LName;     RPCBrokerV.Param[3].ptype := literal;
+      RPCBrokerV.param[4].value := MName;     RPCBrokerV.Param[4].ptype := literal;
+      RPCBrokerV.param[5].value := DOB;       RPCBrokerV.Param[5].ptype := literal;
+      RPCBrokerV.param[6].value := Sex;       RPCBrokerV.Param[6].ptype := literal;
+      RPCBrokerV.Call;
+      RPCResult := RPCBrokerV.Results[0]; //returns: success: DFN;  or  error: -1^ErrMsg
+      if piece(RPCResult,'^',1) <> '-1' then begin
+        self.Patient.DFN := RPCResult;
+      end else begin
+        self.Patient.DFN := '';
+      end;
+    end;
+  end;
+
+  Procedure TAutoUploadNote.SetInfo(var DOS,Provider,Location,Title : string);
+  //Just loads values into structure.  No validation done.
+  begin
+    Self.DOS := DOS;
+    Self.Provider := Provider;
+    Self.Location := Location;
+    Self.NoteTitle := Title;
+  end;
+
+  procedure TAutoUploadNote.InitFrom(OtherNote: TAutoUploadNote);
+  //Will create a blank note for itself.
+  begin
+    Patient.Assign(OtherNote.Patient);
+    ImageInfo.Assign(OtherNote.ImageInfo);
+    Location := OtherNote.Location;
+    DOS := OtherNote.DOS;
+    Provider := OtherNote.Provider;
+    NoteTitle := OtherNote.NoteTitle;
+    CurNoteImages.Assign(OtherNote.CurNoteImages);
+    MakeNewBlankNote(Patient.DFN,DOS,Provider,Location,NoteTitle);
+  end;
+  
+  procedure TAutoUploadNote.MakeNewBlankNote(DFN,DOS,Provider,Location,Title : string);
+  var RPCResult : string;
+  begin
+    RPCResult := '';
+    Self.ErrMsg := '';    //default to no error messages
+    
+    RPCBrokerV.ClearParameters := true;
+    RPCBrokerV.remoteprocedure := 'TMG GET BLANK TIU DOCUMENT';
+    RPCBrokerV.param[0].value := DFN;       RPCBrokerV.param[0].ptype := literal;
+    RPCBrokerV.param[1].value := Provider;  RPCBrokerV.Param[1].ptype := literal;
+    RPCBrokerV.param[2].value := Location;  RPCBrokerV.Param[2].ptype := literal;
+    RPCBrokerV.param[3].value := DOS;       RPCBrokerV.Param[3].ptype := literal;
+    RPCBrokerV.param[4].value := Title;     RPCBrokerV.Param[4].ptype := literal;
+    RPCBrokerV.Call;
+    RPCResult := RPCBrokerV.Results[0];
+    try
+      TIUIEN := StrToInt64(Piece(RPCResult,'^',1));    //returns:  success: TIU IEN;  or  error: -1
+    except
+      on E: EConvertError do begin
+        Self.ErrMsg := 'WHILE CREATING BLANK NOTE FOR UPLOAD, ' + 
+                       'ERROR CONVERTING: ' + RPCBrokerV.Results[0] + ' to document record #.';
+        TIUIEN := -1;
+      end  
+    end;  
+    If TIUIEN <> -1 then begin
+      Self.Patient.DFN := DFN;
+      Self.Provider := Provider;
+      Self.Location := Location;
+      Self.DOS := DOS;
+    end else begin
+      Self.ErrMsg := 'FAILED TO CREATE A BLANK NOTE FOR UPLOAD' +
+                     '  ' + Piece(RPCResult,'^',2);
+      Self.UploadError := true;      
+    end;
+  end;
+
+  function TAutoUploadNote.IsValid : boolean;      
+  begin
+    Result := true;  //default to success.
+    if (Patient.DFN='') {or (TIUIEN < 1)} or (ErrMsg <> '') or (NoteTitle = '')
+    or (Location = '') or (DOS = '') or (Provider = '') then begin
+      Result := false
+    end;
+  end;
+
+  procedure TAutoUploadNote.Clear;
+  begin
+    TIUIEN := 0;
+    if Patient <> nil then Patient.Clear;
+    if ImageInfo <> nil then ImageInfo.Clear;
+    Location := '';
+    DOS := '';
+    Provider := '';
+    NoteTitle := '';
+    UploadError := False;
+    if CurNoteImages <> nil then CurNoteImages.Clear;
+  end;  
+
+  function TAutoUploadNote.SameAs(OtherNote: TAutoUploadNote): boolean;
+  begin
+    Result := true;
+    if (OtherNote = nil) or (OtherNote.Patient = nil)
+    or (Patient.DFN <> OtherNote.Patient.DFN)
+    or (DOS <> OtherNote.DOS)
+    or (Provider <> OtherNote.Provider)
+    or (Location <> OtherNote.Location)
+    or (NoteTitle <> OtherNote.NoteTitle) then begin
+      Result := false;
+    end;      
+  end;
+  
+  constructor TAutoUploadNote.Create;
+  begin
+    Self.TIUIEN := 0;
+    Self.Patient := TPatient.Create;
+    Self.CurNoteImages := TStringList.Create;
+    Self.ImageInfo := TImageInfo.Create;
+    Self.Clear;
+  end;
+
+  destructor TAutoUploadNote.Destroy;
+  begin
+    self.patient.free;
+    Self.CurNoteImages.Free;
+    Self.ImageInfo.Free;
+  end;
+
+  //-------------------------------------------------------------------------
+  //-------------------------------------------------------------------------
+  function TUploadForm.MakeThumbNail(Info: TImageInfo) : boolean;
   //This takes Info.ImageFPathName and creates a 64x64 .bmp file with
   //this same name, and saves in cache directory.
@@ -141,5 +428,5 @@
 
 
-  function TUploadForm.UploadFile(Info: TUploadImageInfo): boolean;
+  function TUploadForm.UploadFile(Info: TImageInfo; DelOrig : boolean): boolean;
   //result: true if success, false if failure
   var
@@ -196,5 +483,5 @@
       Info.ServerPath := Piece(RPCResult,'^',2);
       Info.ServerFName := Piece(RPCResult,'^',3);
-      result := frmImages.UploadFile(Info.ImageFPathName,Info.ServerPath,Info.ServerFName);
+      result := frmImages.UploadFile(Info.ImageFPathName,Info.ServerPath,Info.ServerFName,1,1);
       if result=false then begin
 //      ErrorMsg :='Error uploading image to server';  <-- original line.  //kt 8/7/2007
@@ -227,5 +514,5 @@
       if result then begin
         if MakeThumbNail(Info) then begin;
-          result := frmImages.UploadFile(Info.ThumbFPathName,Info.ServerPath,Info.ServerThumbFName);
+          result := frmImages.UploadFile(Info.ThumbFPathName,Info.ServerPath,Info.ServerThumbFName,1,1);
           if result=false then begin
 //          ErrorMsg :='Error sending thumbnail image to server.';  <-- original line.  //kt 8/7/2007
@@ -234,4 +521,7 @@
           end;
         end;
+        if DelOrig=true then begin
+          DeleteFile(Info.ImageFPathName);
+        end;
       end;
     end;
@@ -243,9 +533,9 @@
   procedure TUploadForm.UploadChosenFiles();
   var i : integer;
-      Info: TUploadImageInfo;
-
-  begin
-    SetupVars; 
-    Info := TUploadImageInfo.Create();
+      Info: TImageInfo;
+
+  begin
+    SetupVars;
+    Info := TImageInfo.Create();
     Info.pLongDesc := nil;
 
@@ -269,5 +559,5 @@
       Info.Extension := MidStr(Info.Extension,2,17); //remove '.'
 
-      if not UploadFile(Info) then begin   //Upload function passes back filename info in Info class
+      if not UploadFile(Info,MoveCheckBox.Checked) then begin   //Upload function passes back filename info in Info class
         //Application.MessageBox('Error uploading image file!','Error');
       end;
@@ -376,4 +666,24 @@
     Bitmap.Width := 64;
     Picture := TPicture.Create;
+
+    AutoUploadNote := TAutoUploadNote.Create;
+    FScanDir := uTMGOptions.ReadString('Pol Directory','??');
+    if FScanDir='??' then begin
+      FScanDir := ExtractFileDir(Application.ExeName);
+      uTMGOptions.WriteString('Pol Directory',FScanDir);
+    end;
+    PolInterval := uTMGOptions.ReadInteger('Pol Interval (milliseconds)',0);
+    if PolInterval=0 then begin
+      PolInterval := 60000;
+      uTMGOptions.WriteInteger('Pol Interval (milliseconds)',PolInterval);
+    end;
+  end;
+
+  procedure TUploadForm.SetScanDir(NewDir : string);
+  begin
+    if DirectoryExists(NewDir) then begin
+      FScanDir := NewDir;
+      uTMGOptions.WriteString('Pol Directory',FScanDir);
+    end;
   end;
 
@@ -412,3 +722,540 @@
   end;
 
+  procedure TUploadForm.DecodeImgTxt(Line : string; out ChartNum, Location,
+                           FName, LName, MName, Sex, DOB, DOS, Provider,
+                           Title : string; FilePaths : TStrings);
+  //format of line is as follows:
+  //ChartNum^Location^FName^LName^MName^Sex^DOB^DOS^Provider^Title^FilePath(s)
+  //NOTE: To provide patient IEN instead of FName etc, use this format:
+  //      ^Location^^`1234567^^^^DOS^Provider^Title^FilePath(s)
+  //      i.e. `IEN  (note ` is not an appostrophy ('))
+  //      `IEN in place of LName, and leave blank: ChartNum,FName,FName,Sex,DOB
+                           
+  var Files: String;                           
+      FileName : String;
+      num,i : integer;
+  begin
+    if Pos('}',Line)>0 then begin
+      Line := Piece(Line,'}',2);  //If error message is present, still allow parse.
+    end;
+    ChartNum := Piece(Line,'^',1);
+    Location := Piece(Line,'^',2);
+    FName := Piece(Line,'^',3);
+    LName := Piece(Line,'^',4);
+    MName := Piece(Line,'^',5);
+    Sex := Piece(Line,'^',6);
+    DOB := Piece(Line,'^',7);
+    DOS := Piece(Line,'^',8);
+    Provider := Piece(Line,'^',9);
+    Title := Piece(Line,'^',10);
+    Files := Piece(Line,'^',11); //may be list of multiple files separated by ;
+    if Pos(';',Files)>0  then begin
+      num := NumPieces(Files,';');
+      for i := 1 to num do begin
+        FileName := piece(files,';',i);
+        if FileName <> '' then FilePaths.Add(FileName);
+      end;  
+    end else begin
+      FilePaths.Add(Files);
+    end;
+      
+  end;  
+
+  function TUploadForm.EncodeImgTxt(ChartNum, Location, FName, LName, MName, Sex, DOB, 
+                           DOS, Provider, Title : string; FilePaths : TStrings) : AnsiString;
+  //format of line is as follows:
+  //ChartNum^Location^FName^LName^MName^Sex^DOB^DOS^Provider^Title^FilePath(s)
+  //NOTE: To provide patient IEN instead of FName etc, use this format:
+  //      ^Location^^`1234567^^^^DOS^Provider^Title^FilePath(s)
+  //      i.e. `IEN  (note ` is not an appostrophy ('))
+  //      `IEN in place of LName, and leave blank: ChartNum,FName,FName,Sex,DOB
+  var i : integer;
+  begin
+    Result := ChartNum + '^' + Location + '^' + FName + '^' + LName + '^' +
+              MName + '^' + Sex + '^' + DOB + '^' + DOS + '@01:00' + '^' + Provider + '^' +
+              Title + '^';    //added time of 1:00    elh   7/8/08
+    for i:= 0 to FilePaths.Count-1 do begin
+      Result := Result + FilePaths.Strings[i];
+      if i <> FilePaths.Count-1 then Result := Result + ';';
+    end;
+  end;                           
+
+  
+  procedure TUploadForm.FinishDocument(UploadNote : TAutoUploadNote);
+  var Text : TStringList;
+      ErrMsg : String;
+      RPCResult : String;
+      i : integer;
+      oneImage: string;
+      //TIUIEN : int64;
+        
+  begin
+    if (UploadNote.TIUIEN>0) and (UploadNote.CurNoteImages.Count>0) 
+    and (UploadNote.UploadError = False) then begin
+      //Add text for note: "See scanned image" -- 
+      //   or later, some HTML code to show note in CPRS directly....
+      Text := TStringList.Create;
+      Text.Add('<!DOCTYPE HTML PUBLIC>');
+      Text.Add('<html>');
+      Text.Add('<head>');
+      Text.Add('<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">');
+      Text.Add('<title>VistA HTML Note</title>');
+      Text.Add('</head>');
+      Text.Add('<body>');
+      Text.Add('<p>');
+      Text.Add('Note created automatically from imported media.');
+      Text.Add('<p>');
+      for i := 0 to UploadNote.CurNoteImages.Count-1 do begin
+        // note: $CPRSDIR$ will be replaced at runtime with directory of CPRS
+        // This will be done as page is passed to TWebBrowser (in rHTMLTools)
+        oneImage := '$CPRSDIR$\Cache\' + UploadNote.CurNoteImages.Strings[i];  
+        //oneImage := CacheDir + '\' + CurNoteImages.Strings[i];
+        Text.Add('<img WIDTH=640 src="'+oneImage+'">');
+        Text.Add('<p>');
+      end;
+      //Text.Add('<small>');
+      //Text.Add('If images don''t display, first view them in IMAGES tab.<br>');
+      //Text.Add('Then return here, click on note and press [F5] key to refresh.');
+      //Text.Add('</small>');
+      //Text.Add('<p>');
+      Text.Add('</body>');
+      Text.Add('</html>');
+      Text.Add(' ');
+      rTIU.SetText(ErrMsg,Text,UploadNote.TIUIEN,1);  //1=commit data, do actual save.
+      Text.Free;
+      //Here I autosign  -- later make this optional?
+      RPCBrokerV.ClearParameters := true;
+      RPCBrokerV.remoteprocedure := 'TMG AUTOSIGN TIU DOCUMENT';
+      RPCBrokerV.param[0].value := IntToStr(UploadNote.TIUIEN);  
+      RPCBrokerV.param[0].ptype := literal;
+      RPCBrokerV.Call;
+      RPCResult := RPCBrokerV.Results[0];    //returns:  error: -1;  success=1
+      if RPCResult='-1' then begin
+        MessageDlg('Unable to set status for scanned document to SIGNED',mtError,[mbOK],0);
+      end;     
+      UploadNote.TIUIEN := 0;
+    end;  
+    UploadNote.Clear;
+  end;
+
+
+  function TUploadForm.ProcessOneLine(Line : string) : string;
+  //Returns: if success, '';  if failure, returns reason
+
+  //format of line is as follows:
+  //ChartNum^Location^FName^LName^MName^Sex^DOB^DOS^Provider^Title^FilePath(s)
+  //NOTE: To provide patient IEN instead of FName etc, use this format:
+  //      ^Location^^`1234567^^^^DOS^Provider^Title^FilePath(s)
+  //      i.e. `IEN  (note ` is not an appostrophy ('))
+  //      `IEN in place of LName, and leave blank: ChartNum,FName,FName,Sex,DOB
+    
+  var
+    ChartNum,FName,LName,MName,Sex,DOB  : String;
+    DOS,Provider,Title : String;
+    ThisNote : TAutoUploadNote;
+    FilePaths : TStringList;
+    i : integer;
+    Location : string;
+   
+  begin
+    Result := '';  //default to success for function
+    ThisNote := TAutoUploadNote.Create;
+    FilePaths := TStringList.Create();
+    DecodeImgTxt(Line, ChartNum, Location, FName, LName, MName, Sex, DOB, DOS, Provider, Title, FilePaths);
+      
+    ThisNote.SetDFN(ChartNum,Location,FName,LName,MName,DOB,Sex);
+    ThisNote.SetInfo(DOS,Provider,Location,Title);
+    if Pos('//Failed',Line)>0 then ThisNote.UploadError := true;
+    if ThisNote.IsValid then begin  //A note can be 'Valid' and still have an 'UploadError'
+      if ThisNote.SameAs(AutoUploadNote)= false then begin
+        ThisNote.TIUIEN := AutoUploadNote.TIUIEN;
+        FinishDocument(AutoUploadNote);  // Close and clear any existing note 
+        AutoUploadNote.InitFrom(ThisNote);
+        Result := AutoUploadNote.ErrMsg; //'' if no error
+      end;  
+      if ThisNote.UploadError then AutoUploadNote.UploadError := true;
+      if (AutoUploadNote.UploadError=false) then for i := 0 to FilePaths.Count-1 do begin
+        AutoUploadNote.ImageInfo.pLongDesc := nil;
+        //Load up info record with data for upload
+        AutoUploadNote.ImageInfo.ShortDesc := 'Scanned document';
+        AutoUploadNote.ImageInfo.UploadDUZ := User.DUZ;
+        AutoUploadNote.ImageInfo.ObjectType := 1; //type 1 is Still Image (jpg).  OK to use with .bmp??
+        AutoUploadNote.ImageInfo.ProcName := 'Scanned'; //max length is 10 characters
+        AutoUploadNote.ImageInfo.ImageDateTime := DOS;
+        AutoUploadNote.ImageInfo.TIUIEN := AutoUploadNote.TIUIEN;
+        AutoUploadNote.ImageInfo.UploadDateTime := 'NOW';
+        AutoUploadNote.ImageInfo.DFN := AutoUploadNote.Patient.DFN;
+        AutoUploadNote.ImageInfo.ImageFPathName := FilePaths.Strings[i];
+        AutoUploadNote.ImageInfo.Extension := ExtractFileExt(AutoUploadNote.ImageInfo.ImageFPathName); //includes '.'
+        AutoUploadNote.ImageInfo.Extension := MidStr(AutoUploadNote.ImageInfo.Extension,2,17); //remove '.'
+        if not UploadFile(AutoUploadNote.ImageInfo,true) then begin   //Upload function passes back filename info in Info class
+          Result := 'ERROR UPLOADING IMAGE FILE';
+        end;    
+        AutoUploadNote.CurNoteImages.Add(AutoUploadNote.ImageInfo.ServerFName);
+      end else begin
+        If Result='' then Result := '(Error found in earlier file entry in batch)';
+      end;
+    end else begin
+      Result := 'NOTE INFO INVALID (Probably: PATIENT NOT FOUND)';
+    end;
+    FilePaths.Free;
+    ThisNote.Free;
+  end;
+
+  
+  function TUploadForm.ProcessOneFile(FileName : string) : boolean;
+  //This will process image(s) indicated in textfile FileName
+  //After uploading image to server, textfile and specified images are deleted
+  //Returns Success
+  //Note: To upload multiple images into one document, one may add multiple
+  //      lines to the ImgTxt text file.  As long as the info is the same
+  //      (i.e. same provider, patient, note type, DOS etc) then they
+  //      will be appended to current note.
+  //      OR, add multiple image file names to one line.
+  //        -- the problem with multiple images on one line is that errors
+  //        can not be reported for just one image.  It will be ONE for any/all
+  //      OR, if the next file in process-order is still has the same info as
+  //        the prior file, then it will be appended.
+  var
+    Lines : TStringList;
+    i : integer;
+    ResultStr : string;
+    OneLine : string;
+  begin 
+    Result := true;  //default is Success=true
+    Lines := TStringList.Create;
+    Lines.LoadFromFile(FileName);
+    //FinishDocument(AutoUploadNote);  //will save and clear any old data.
+    for i := 0 to Lines.Count-1 do begin
+      OneLine := Lines.Strings[i];
+      ResultStr := ProcessOneLine(OneLine);  //Even process with //failed markeers (to preserve batches)
+      if Pos('//Failed',OneLine)> 0 then begin  //If we already have //Failed, don't duplicate another Error Msg
+        Result := false;  //prevent deletion of file containing //Failed//      
+      end else begin
+        if ResultStr <> '' then begin
+          Lines.Strings[i] := '//Failed: '+ResultStr+'}'+Lines.Strings[i];
+          Lines.SaveToFile(FileName);
+          Result := false;
+        end;  
+      end;
+    end;
+    //Temp, for debugging
+    //Lines.SaveToFile(ChangeFileExt(FileName,'.imgtxt-bak'));
+    //end temp    
+    Lines.free;
+  end;
+
+  
+  procedure TUploadForm.ScanAndHandleImgTxt;
+  var
+    FoundFile : string;
+    Found : TSearchRec;
+    FilesList : TStringList;
+    i         : integer;
+    result : boolean;
+  begin
+    //NOTE: Later I may make this spawn a separate thread, so that
+    //  user doesn't encounter sudden unresponsiveness of CPRS
+    //I can use BeginThread, then EndTread
+    //Issues: ProcessOneFile would probably have to be a function
+    //  not in a class/object...
+      
+    FilesList := TStringList.Create;
+
+    //scan for new *.ImgTxt file
+    //FindFirst may not have correct order, so collect all names and then sort.
+    if FindFirst(FScanDir+'*.imgtxt',faAnyFile,Found)=0 then repeat
+      FilesList.Add(FScanDir+Found.Name);
+    until FindNext(Found) <> 0;   
+    FindClose(Found);  
+    FilesList.Sort;  //puts filenames in alphanumeric order 
+
+    //Now process images in correct order.
+    for i := 0 to FilesList.Count-1 do begin
+      FoundFile := FilesList.Strings[i];
+      if ProcessOneFile(FoundFile) = true then begin  {process *.imgtxt file}
+        DeleteFile(FoundFile); 
+        FoundFile := ChangeFileExt(FoundFile,'.barcode.txt');
+        DeleteFile(FoundFile);
+      end; //Note: it is OK to continue, to get other non-error notes afterwards.
+    end;
+    FinishDocument(AutoUploadNote);  // Close and clear any existing note 
+    FilesList.Free
+  end;
+
+  
+  procedure TUploadForm.ScanAndHandleImages;
+  (*  Overview of mechanism of action of automatically uploading images.
+      =================================================================
+    -- For an image to be uploaded, it must first be positively identified.  
+       This can occur 1 of two ways:
+         -- the image contains a datamatrix barcode.
+         -- the image is part of a batch, and the first image of the batch
+            contains a barcode for the entire batch.
+    -- At our site, the scanner program automatically names the files numerically
+       so that sorting on the name will put them in proper order when working
+       with batches.
+    -- The decoding of the barcode requires a special program.  I was not 
+       able to find a way to run this on the Windows client.  I found the
+       libdmtx that does this automatically.  It currently is on unix only.
+       It was too complicated for me to compile it for windows.  I initially
+       wanted everything to run through the RPC broker.  This involved
+       uploading the image to the linux server, running the decoder on the
+       server, then passing the result back.  The code for this is still avail
+       in this CPRS code.  However, the process was too slow and I had to 
+       come up with something faster.  So the following arrangement was setup
+        -- scanned images are stored in a folder that was shared by both the
+           windows network (and thus is available to CPRS), and the linux server.
+        -- At our site, we used a copier/scanner unit that created only TIFF 
+           files.  These are not the needed format for the barcode decoder, so...
+        -- a cron job runs on the linux server that converts the .tif files
+           to .png.  Here is that script:
+             <removed due to frequent changes...>
+           ---------------------------------
+        -- Next the .png files must be checked for a barcode.  Another cron
+           task scans a directory for .png files and creates a metafile for
+           the file giving its barcode reading, or a marker that there is
+           no barcode available for that image.  The file name format is:
+           *.barcode.txt, with the * coorelating to filename of the image.
+           -- The decoding process can take some time (up to several minutes
+              per image.
+           -- A flag file named barcodeRead.working.txt is created when the
+              script is run, and deleted when done.  So if this file is present
+              then the decoding process is not complete.
+           -- if a *.barcode.txt file is present, then no attempts will be made
+              to decode the image a second time.
+           -- CPRS still contains code to upload an image to look for a barcode.
+              At this site, only png's will contain barcodes, so I have commented
+              out support for automatically uploading other file formats.
+           -- Here is the unix bash script that decodes the barcodes.  It is 
+              launched by cron:
+           ---------------------------------
+             <removed due to frequent changes...>
+           ---------------------------------
+    -- After the *.png images are available, and no flag files are present
+       to indicate that the server is working with the files, then the images
+       are processed, using the barcode metafiles.  This is triggered by a 
+       timer in CPRS.  It essentially converts imagename + barcode data -->
+       --> *.imgtxt.
+    -- For each *.png image, there will be a *.imgtxt metafile created.  This
+       will contain information needed by the server, in a special format for
+       the RPC calls.  When an *.imgtxt file is present, this is a flag that
+       the image is ready to be uploaded.
+    -- A timer in CPRS scans for *.imgtxt files.  When found, it uploads the
+       image to the server and creates a container progress note for displaying
+       it in CPRS.
+ *)
+  
+    procedure ScanOneImageType(ImageType : string);
+    //Scan directory for all instances of images of type ImageType
+    //For each one, create a metadata file (if not already present)
+
+    //Note: Batch mode only works for a batch of file ALL OF THE SAME TYPE.
+    //I.e. There can't be a batch of .jpg, then .gif, then .bmp.  This is
+    //because a scanner, if it is scanning a stack of documents for a given
+    //patient will produce all files in the same ImageType
+
+      function DeltaMins(CurrentTime,PriorTime : TDateTime) : integer;
+        //Return ABSOLUTE difference in minutes between Current <--> Prior.
+        //NOTE: if value is > 1440, then 1440 is returned
+      var DeltaDays,FracDays : double;  
+      begin
+        DeltaDays := abs(CurrentTime-PriorTime);
+        FracDays := DeltaDays - Round(DeltaDays);
+        if DeltaDays>1 then FracDays := 1;
+        Result := Round((60*24)*FracDays);
+      end;
+
+    var
+      FoundFile : string;
+      MetaFilename : string;
+      Found : TSearchRec;
+      BarCodeData : AnsiString;
+      DFN,DOS,AuthIEN,LocIEN,NoteTypeIEN : string;
+      OneLine : string;
+      FilePaths : TStringList;
+      AllFiles : TStringList;
+      OutFileLines : TStringList;
+      BatchS : string;
+      tempCount : integer;
+      BatchFInfo : TFileInfo;
+      LastFileTimeStamp,CurFileTimeStamp : TDateTime;
+      DeltaMinutes : integer;
+      pFInfo : TFileInfo;
+      i : integer;
+    Label AbortPoint;      
+
+    const
+      ALLOWED_TIME_GAP = 2;  //time in minutes
+
+    begin    
+      FilePaths := TStringList.Create;
+      OutFileLines := TStringList.Create;
+      AllFiles := TStringList.Create;
+      BatchFInfo := TFileInfo.Create;
+      
+      //NOTE: Later I may make this spawn a separate thread, so that
+      //  user doesn't encounter sudden unresponsiveness of CPRS
+      //I can use BeginThread, then EndTread
+      //Issues: ProcessOneFile would probably have to be a function
+      //  not in a class/object...
+
+      //scan for all instances *.ImageType Image file
+      //Store info for processesing after loop
+      //Do this as a separate step, so files can be processed in proper order
+      if FindFirst(FScanDir+'*.'+ImageType,faAnyFile,Found)=0 then repeat
+        FoundFile := FScanDir+Found.Name;
+        if FileExists(ChangeFileExt(FoundFile,'.imgtxt')) then continue;
+        MetaFilename := ChangeFileExt(FoundFile,'.barcode.txt');
+        pFInfo := TFileInfo.Create;  //will be owned by AllFiles
+        pFInfo.MetaFileName := MetaFilename;
+        pFInfo.FPath := FoundFile;
+        pFInfo.SrcRec := Found;
+        pFInfo.STimeStamp := FloatToStr(FileDateToDateTime(Found.Time));
+        pFInfo.MetaFileExists := FileExists(MetaFilename);
+        pFInfo.SBarCode := '';  //default to empty.
+        pFInfo.BatchCount := 0;        
+        if pFInfo.MetaFileExists = false then begin
+          //Call server via RPC to decode Barcode
+          //This is too slow and buggy.  Will remove for now...
+          //BarCodeData := frmImages.DecodeBarcode(FoundFile,ImageType);
+          //pFInfo.SBarCode := BarCodeData;
+          pFInfo.SBarCode := '';
+          //Here I could optionally create a Metafile for processing below.
+        end;
+        if pFInfo.MetaFileExists then begin  //Retest in case RPC changed status.
+          if FileExists(FScanDir+'barcodeRead.working.txt') then goto AbortPoint;
+          OutFileLines.LoadFromFile(pFInfo.MetaFileName);
+          if OutFileLines.Count>0 then begin
+            pFInfo.SBarCode := OutFileLines.Strings[0];
+            //convert 'No Barcode message into an empty string, to match existing code.            
+            if Pos('//',pFInfo.SBarCode)=1 then pFInfo.SBarCode := '';  
+            if NumPieces(pFInfo.SBarCode,'-') <> 8 then pFInfo.SBarCode := '';  
+          end else begin
+            pFInfo.MetaFileExists := false;  //set empty file to Non-existence status
+          end;
+        end;
+        AllFiles.AddObject(pFInfo.FPath,pFInfo);  //Store filename, to allow sorting on this.
+      until FindNext(Found) <> 0;   
+      AllFiles.Sort; // Sort on timestamp --> put in ascending alpha filename order
+
+      //-------- Now, process files in name order ------------
+      LastFileTimeStamp := 0;
+      BatchFInfo.BatchCount := 0;
+      for i := 0 to AllFiles.Count-1 do begin
+        pFInfo := TFileInfo(AllFiles.Objects[i]);
+        if pFInfo.MetaFileExists = false then continue;
+        CurFileTimeStamp := FileDateToDateTime(pFInfo.SrcRec.Time);
+        DeltaMinutes := DeltaMins(CurFileTimeStamp,LastFileTimeStamp);
+        // *.barcode.txt file exists at this point
+        if pFInfo.SBarCode <> '' then begin  //Found a new barcode
+          LastFileTimeStamp := CurFileTimeStamp;
+          //Note: The expected format of barcode must be same as that 
+          //      created by TfrmPtLabelPrint.PrintButtonClick:
+          //      70685-12-31-2008-73-6-1302-0
+          //      PtIEN-DateOfService-AuthorIEN-LocIEN-NoteTypeIEN-BatchFlag
+          //      THUS there should be 8 pieces in the string.        
+          DFN := piece(pFInfo.SBarCode,'-',1);
+          DOS := pieces(pFInfo.SBarCode,'-',2,4);
+          AuthIEN := piece(pFInfo.SBarCode,'-',5);
+          LocIEN := piece(pFInfo.SBarCode,'-',6);
+          NoteTypeIEN := piece(pFInfo.SBarCode,'-',7);
+          BatchS := piece(pFInfo.SBarCode,'-',8);
+          if BatchS = '*' then begin
+            pFInfo.BatchCount := 9999
+          end else begin
+            try
+              pFInfo.BatchCount := StrToInt(BatchS);
+            except
+              on E:EConvertError do begin
+                pFInfo.BatchCount := 1;
+              end;
+            end;
+          end;
+          //BatchFInfo.SBarCode := pFInfo.SBarCode;
+        end else if (BatchFInfo.BatchCount > 0) then begin
+          if (DeltaMinutes > ALLOWED_TIME_GAP) then begin
+            pFInfo.Clear;
+            BatchFInfo.Clear;
+          end else begin
+            //Apply barcode from last image onto this one (from same batch)
+            pFInfo.SBarCode := BatchFInfo.SBarCode;  
+          end;
+        end;
+        if pFInfo.SBarCode <> '' then begin
+          //Success --> write out ImgTxt file...
+          FilePaths.Add(pFInfo.FPath);
+          OneLine := EncodeImgTxt('', '`'+LocIEN,'', '`'+DFN, '', '', '', 
+                                  DOS,'`'+AuthIEN, '`'+NoteTypeIEN, FilePaths);
+          if pFInfo.BatchCount>0 then begin
+            //A BATCH marker has been found on current barcode.  This means that
+            //Batchmode should be turned on.  This will apply current barcode
+            //data to any subsequent images, providing there is not a gap in 
+            //time > ALLOWED_TIME_GAP
+            BatchFInfo.Assign(pFInfo);  //reset Batch info to current
+          end;  
+          //Decrease use count of Batch Info
+          Dec(BatchFInfo.BatchCount);
+        end else begin
+          OneLine := '';
+        end;
+        OutFileLines.Clear;
+        if OneLine <> '' then begin
+          OutFileLines.Add(OneLine);                                        
+          OutFileLines.SaveToFile(ChangeFileExt(pFInfo.FPath,'.imgtxt'));
+        end;  
+        FilePaths.Clear;
+        OutFileLines.Clear;
+        LastFileTimeStamp := CurFileTimeStamp;
+      end;
+AbortPoint:
+      FindClose(Found);  
+      BatchFInfo.Free;
+      FilePaths.Free;
+      for i := 0 to AllFiles.Count-1 do begin  //free owned objects
+        pFInfo := TFileInfo(AllFiles.Objects[i]);
+        pFInfo.Free;
+      end;
+      AllFiles.Free;
+      OutFileLines.Free;
+    end;
+
+  var flag1Filename,flag2Filename : string;
+  begin
+    flag1Filename := FScanDir+'barcodeRead.working.txt';
+    flag2Filename := FScanDir+'convertTif2Png.working.txt';
+    //if linux server is in middle of a conversion or barcode decode, then skip.
+    if (FileExists(flag1Filename)=false) and (FileExists(flag2Filename)=false) then begin
+      (* Remove {}'s to be able to have jpg's etc that contain barcodes
+        In our site, only png's will have barcodes, and thus these are the
+        only images that can be uploaded automatically.  Uploading jpg's, bmp's
+        etc to look for (nonexistent) barcodes will just waste time and bandwidth. *)
+      {
+      ScanOneImageType('jpg');
+      ScanOneImageType('jpeg');
+      ScanOneImageType('gif');
+      ScanOneImageType('bmp');
+      }
+      //ScanOneImageType('tif');   {Tiff was not showing up in IE for some reason}
+      //ScanOneImageType('tiff');  {Tiff was not showing up in IE for some reason}
+      ScanOneImageType('png');
+    end;
+  end;
+
+  procedure TUploadForm.PolTimerTimer(Sender: TObject);
+  begin
+    PolTimer.Enabled := false;
+    try
+      if Assigned(frmImages) and frmImages.AutoScanUpload.Checked then begin
+        ScanAndHandleImages;  //create metadata for images (if not done already)
+        ScanAndHandleImgTxt;  //process upload file, based on metadata
+      end;
+    finally
+      PolTimer.Enabled := true;
+      PolTimer.Interval := PolInterval;
+    end;
+  end;
+
+
+
 end.
