unit VC0706;
{                        at camera    in cables       breadboard          USB/serial
  +5V                       red         orange          14, rail             pin 4
  ground                    black        blue           16,gnd               pin 6
  RX (to the camera)        green       green           from divider         pin 3, to divider
  TX (from the camera)      white     brown/white       12                   pin 2
  NTSC video out            yellow      brown           20                    N/C
  NTSC video ground         brown      blue/white       gnd                   N/C
}


  interface
    uses
      AdsData       (* tAdsStringField *),
      AdsFunc       (* used by AdsTable *),
      AdsTable      (* tAdsQuery *),
      AdvGrid       (* TMS *),
      AdvObj        (* TMS *),
      AdvUtil       (* TMS *),
      BaseGrid      (* TMS *),
      Buttons       (* tBitBtn *),
      Classes       (**),
      Controls      (**),
      DB            (* tDataSource, tDateTimeField, tIntegerField,
                         tStringField *),
      DBAdvGrid     (* tDBAdvGrid *),
      DBCtrls       (* tDBNavigator *),
      ExtCtrls      (* tShape; also used by DBCtrls *),
      Forms         (* Forms *),
      Graphics      (* TMS *),
      Grids         (* TMS *),
      ObservedAndWM (* GetObservedName, MeasurementInfo2, tObserved,
                         tWindowsToBase, wm2VC0706Manual, wm2VC0706Motion,
                         wm2VC0706Snap, wtbDeleteJPEGs, wtbSendToXBee,
                         wtbTakeVC0706Image *),
      StdCtrls      (* tButton *),
      Windows       (**);

    type
      tfmVC0706 = class(tForm)
          qBoardsWithCameras: tAdsQuery;
            qBoardsWithCamerasBoardName:    tAdsStringField;
            qBoardsWithCamerasBoardTag:     tAdsStringField;
            qBoardsWithCamerasLocation:     tAdsStringField;
            qBoardsWithCamerasObservedName: tAdsStringField;
            qBoardsWithCamerasBoardUID:    tIntegerField;
            qBoardsWithCamerasObservedUID: tIntegerField;
            qBoardsWithCamerasXBeeMY:      tIntegerField;
            qBoardsWithCamerasXBoardName: tStringField;
            dataBoardsWithCameras: tDataSource;
              dbadvgridBoardsWithCameras: tDBAdvGrid;
          qNewVC0706Image: tAdsQuery;
          qVC0706: tAdsQuery;
            qVC0706ObservedName: tAdsStringField;
            qVC0706WhenRecorded:    tDateTimeField;
            qVC0706WhenRecordedUTC: tDateTimeField;
            qVC0706FileNumber:   tIntegerField;
            qVC0706ObservedUID:  tIntegerField;
            qVC0706WhatMeasured: tIntegerField;
            qVC0706XWhatMeasured: tStringField;
            dataVC0706: tDataSource;
              dbadvgridVC0706: tDBAdvGrid;
              navVC0706: tDBNavigator;
          btnTakeSnapshot: TBitBtn;
          shapeJPEGAlert: tShape;
            btnDeleteJPEGs: TButton;

          procedure qBoardsWithCamerasCalcFields(DataSet: tDataSet);
          procedure qVC0706CalcFields           (DataSet: tDataSet);

          procedure btnDeleteJPEGsClick (Sender: tObject);
          procedure btnTakeSnapshotClick(Sender: tObject);
          procedure FormDblClick        (Sender: tObject);
          procedure FormShow            (Sender: tObject);

          procedure dbadvgridVC0706CanSort(Sender: tObject;
                                           ACol: integer;
                                           var DoSort: boolean);
        private
          procedure BuildVC0706Query;
          procedure MessageToBoard(const WTB: tWindowsToBase;
                                   const NoteFormat: string);
        public
          procedure ProcessCurrentLine;
        end (* tfmVC0706 *);

    var
      fmVC0706: tfmVC0706;

  implementation
    uses
      BoardManager       (* stVC0706 *),
      Dashboard          (* fmDashboard, llblParenData *),
      DBAdvGridUtils     (* FieldReadOnly, MarlettCheck, MarlettCheckField, NormalBrush, NormalPen,
                              ReadOnlyBrush, ReadOnlyPen, SetUpDBAdvGrid,
                              SetUpDBAdvGridReadOnly, SortBrush, SortOn,
                              StretchField *),
      HC3DM              (* dmHC3, Global *),
      HC4Main            (* fmHC4Main *),
      MyConsts           (* UsualFormColor *),
      OutdoorInstruments (* fmOutdoorInstruments *),
      SensorDevice       (* tSensorDevice *),
      SysUtils           (* Format *),
      TableManager       (* tuVC0706 *);
      {$R *.dfm}

    const
      MaxDevices = 3;

{**************** infrastructure **********************************************}
    type
      tSortImages = (siFileNumber, siKind, siSubject, siWhen);

      tVC0706Device = class(tSensorDevice)
        public
          constructor Create(const ObservedUID: tObserved);
          procedure   NewData;
        end (* tVC0706Device *);

    var
      NDevices: integer;
      VC0706Device: array[1 .. MaxDevices] of tVC0706Device;
      SortImages: tSortImages;

    procedure Cleanup;
      (* reclaim storage *)

      var
        I: integer;

      begin (* Cleanup *)
        for I := 1 to NDevices do
          VC0706Device[I].Free
      end   (* Cleanup *);

    procedure tfmVC0706.FormDblClick(Sender: tObject);
      (* show master form *)

      begin (* tfmVC0706.FormDblClick *)
        fmHC4Main.Show
      end   (* tfmVC0706.FormDblClick *);

    procedure tfmVC0706.FormShow(Sender: tObject);
      (* initialize *)
    
      begin (* tfmVC0706.FormShow *)
        self.Color := UsualFormColor;
        qBoardsWithCameras.Open;
          SetUpDBAdvGridReadOnly(dbadvgridBoardsWithCameras);

        SortImages := siWhen;
        BuildVC0706Query;
          SetUpDBAdvGridReadOnly(dbadvgridVC0706);
            SortOn('FileNumber');
            SortOn('XWhatMeasured');
            SortOn('WhenRecorded');
            SortOn('ObservedName');
      end   (* tfmVC0706.FormShow *);

    procedure tfmVC0706.MessageToBoard(const WTB: tWindowsToBase;
                                       const NoteFormat: string);
      (* shared code *)
    
      var
        XBeeMyID: integer;
        BoardName: string;

      begin (* tfmVC0706.MessageToBoard *)
        BoardName := qBoardsWithCameras.FieldByName('BoardName').AsString;
        XBeeMyID  := qBoardsWithCameras.FieldByName('XBeeMY'   ).AsInteger;

        with fmDashboard.SerialManager do
          IntMessageToBaseStation(wtbSendToXBee, [XBeeMyID, ord(WTB)],
                                  Format(NoteFormat, [BoardName]))
      end   (* tfmVC0706.MessageToBoard *);


{**************** end infrastructure ******************************************}
{**************** board-level *************************************************}
    procedure tfmVC0706.btnDeleteJPEGsClick(Sender: tObject);
      (* delete ALL the uSD images *)
    
      begin (* tfmVC0706.btnDeleteJPEGsClick *)
        MessageToBoard(wtbDeleteJPEGs, 'deleting JPEGs from %s board')
      end   (* tfmVC0706.btnDeleteJPEGsClick *);

  procedure tfmVC0706.btnTakeSnapshotClick(Sender: tObject);
      (* take a picture *)

      begin (* tfmVC0706.btnTakeSnapshotClick *)
        MessageToBoard(wtbTakeVC0706Image, 'requested VC0706 image on %s board')
      end   (* tfmVC0706.btnTakeSnapshotClick *);

    procedure tfmVC0706.qBoardsWithCamerasCalcFields(DataSet: tDataSet);
      (* prettyprinting *)

      var
        BoardName, BoardTag: string;

      begin (* tfmVC0706.qBoardsWithCamerasCalcFields *)
        with DataSet do
          begin (* DataSet *)
            BoardName := FieldByName('BoardName').AsString;
            BoardTag  := FieldByName('BoardTag' ).AsString;

            FieldByName('XBoardName').AsString := Format('%s (%s)',
                                                         [BoardName, BoardTag])
          end   (* DataSet *)
      end   (* tfmVC0706.qBoardsWithCamerasCalcFields *);

{**************** end board-level *********************************************}
{**************** view old data ***********************************************}
    procedure tfmVC0706.BuildVC0706Query;
      (* usual sorting trick *)

      var
        OldFileNumber: integer;

      function OrderBy: string;
        (* debulk *)
      
        begin (* OrderBy *)
          case SortImages of
            siFileNumber: Result := 'FileNumber';
                  siKind: Result := 'WhatMeasured, WhenRecordedUTC desc';
               siSubject: Result := 'ob.Observed,  WhenRecordedUTC desc';
                  siWhen: Result := '              WhenRecordedUTC desc'
            end (* case *)
        end   (* OrderBy *);

      begin (* tfmVC0706.BuildVC0706Query *)
        with qVC0706 do
          begin (* qVC0706 *)
            OldFileNumber := FieldByName('FileNumber').AsInteger;
            Close;
            SQL.Text := 'select                         ' +
                        '  v.FileNumber,                ' +
                        '  ob.Observed as ObservedName, ' +
                        '  v.ObservedUID,               ' +
                        '  v.WhatMeasured,              ' +
                        '  v.WhenRecorded,              ' +
                        '  v.WhenRecordedUTC            ' +
                        'from                           ' +
                        '  HomeControl.Observed ob,     ' +
                        '  VC0706               v       ' +
                        'where                          ' +
                        '  ob.UID = v.ObservedUID       ' +
                        'order by                       ' +
                           OrderBy;

            Open;
            Locate('FileNumber', OldFileNumber, [])
          end   (* qVC0706 *)
      end   (* tfmVC0706.BuildVC0706Query *);

    procedure tfmVC0706.dbadvgridVC0706CanSort(Sender: tObject;
                                               ACol: integer;
                                               var DoSort: boolean);
      (* sorting *)
    
      var
        Header: string;

      begin (* tfmVC0706.dbadvgridVC0706CanSort *)
        DoSort := false;
        Header := dbadvgridVC0706.Columns[ACol].Header;

        if      Header =   'file #'   then SortImages := siFileNumber
        else if Header =    'kind'    then SortImages := siKind
        else if Header = 'when taken' then SortImages := siWhen
        else if Header =   'subject'  then SortImages := siSubject;

        BuildVC0706Query
      end   (* tfmVC0706.dbadvgridVC0706CanSort *);

    procedure tfmVC0706.qVC0706CalcFields(DataSet: TDataSet);
      (* prettyprinting *)

      var
        WM: tWhatMeasured2;

      begin (* tfmVC0706.qVC0706CalcFields *)
        with DataSet do
          begin (* DataSet *)
            WM          := tWhatMeasured2(FieldByName('WhatMeasured').AsInteger);

            FieldByName('XWhatMeasured').AsString := MeasurementInfo2[WM].Name
          end   (* DataSet *)
      end   (* tfmVC0706.qVC0706CalcFields *);

{**************** end view old data *******************************************}
{**************** data handling ***********************************************}
    procedure tfmVC0706.ProcessCurrentLine;
      (* device does all the work *)
    
      var
        DeviceIndex: integer;

      function GetDeviceIndex(const ObservedUID: tObserved): integer;
        (* create if no match *)

        function FindDeviceIndex: integer;
          (* if not found, return 0 *)

          var
            I: integer;

          begin (* FindDeviceIndex *)
            Result := 0;
            for I := 1 to NDevices do
              with VC0706Device[I] do
                if TheObservedUID = ObservedUID then
                  begin (* found it *)
                    Result := I;
                    break
                  end   (* found it *)
          end   (* FindDeviceIndex *);

        begin (* GetDeviceIndex *)
          Result := FindDeviceIndex;
          if Result = 0 then
            begin (* create new device *)
              inc(NDevices);
              VC0706Device[NDevices] := tVC0706Device.Create(ObservedUID);
              Result := NDevices
            end   (* create new device *)
        end   (* GetDeviceIndex *);

      begin (* tfmVC0706.ProcessCurrentLine *)
        DeviceIndex := GetDeviceIndex(Global.LatestData.ObservedUID);
        VC0706Device[DeviceIndex].NewData;
        BuildVC0706Query
      end   (* tfmVC0706.ProcessCurrentLine *);

  { tVC0706Device }

    constructor tVC0706Device.Create(const ObservedUID: tObserved);
      (* constructor *)
    
      begin (* tVC0706Device.Create *)
        inherited Create(tuVC0706, ObservedUID, Global.LatestData.UTCStamp, stVC0706);
        TheObservedUID  := ObservedUID;
        TheMeasurements := [wm2VC0706Manual, wm2VC0706Motion, wm2VC0706Snap];
        with fmDashboard do
          ExtendChat(Format('vc_324 created device for %s camera',
                            [GetObservedName(TheObservedUID)]),
                     llblCreated)
      end   (* tVC0706Device.Create *);

    procedure tVC0706Device.NewData;
      (* store and chart *)

      var
        FileNumber: integer;
        WM: tWhatMeasured2;

      begin (* tVC0706Device.NewData *)
        with Global.LatestData do
          begin (* Global.LatestData *)
            WM := WMArg[1];
            FileNumber := IntArg[1];
            fmDashboard.VC0706News(Format('%s.VC0706 took image #%d',
                                          [BoardName, FileNumber]));
            if WM = wm2VC0706Motion then
              fmHC4Main.MotionDetected(GetObservedName(TheObservedUID), LocalStamp);

            with fmVC0706.qNewVC0706Image do
              begin (* fmVC0706.qNewVC0706Image *)
                ParamByName('FileNumber'     ).AsInteger  := FileNumber;
                ParamByName('ObservedUID'    ).AsInteger  := ord(ObservedUID);
                ParamByName('WhatMeasured'   ).AsInteger  := ord(WM);
                ParamByName('WhenRecorded'   ).AsDateTime := LocalStamp;
                ParamByName('WhenRecordedUTC').AsDateTime := UTCStamp;
                ExecSQL
              end   (* fmVC0706.qNewVC0706Image *);

              fmOutdoorInstruments.NewDataVC0706(WM, TheObservedUID, FileNumber)
            end   (* Global.LatestData *)

      end   (* tVC0706Device.NewData *);

{**************** end data handling *******************************************}
initialization
    NDevices := 0;

  finalization
    Cleanup;
end.
