File: cli_utils.pas

package info (click to toggle)
udm 1.0.0.352-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 28,076 kB
  • sloc: pascal: 72,496; ansic: 6,892; awk: 880; makefile: 768; sh: 493; perl: 34; python: 22; tcl: 18
file content (544 lines) | stat: -rw-r--r-- 19,738 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
unit cli_utils;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, StrUtils
  , synaser, synautil, blcksock

  {$IFDEF Unix}, BaseUnix{$ENDIF}//for fpreadlink
  , vinfo
  , LazSysUtils //Defines NOWUTC.
  , upascaltz //Defines TPascalTZ
  , dateutils //Defines DayOf
  , Math //for Float identifier
  //, epiktimer //define Epiktimer
  ;

procedure FindUSBtty(serialnumber:string);
function OpenComm() : boolean;
function CloseComm() : boolean;
function SendGet(command:string; LeaveOpen:boolean = False; Timeout:Integer=3000; GetAlso:boolean = True; HideStatus:boolean = False) : string;
procedure WriteDLHeader(Setting: String = '' );
procedure LogOneReading();
procedure OnTheClock(const ThisMoment: TDateTime; const Granularity: integer);
procedure testfptimer();
procedure verbose(level:integer; message:string);

var
  verbosity: Integer = 0;
  SelectedPort: String;
  DLRecFile: TextFile;
  RamStatusFile: TextFile;
  LogFileName: AnsiString;
  SelectedUnitSerialNumber: String;
  INISection: String;
  ptz :TPascalTZ;
  TZRegion, TZLocation:String;

  //Used for logging and display
  Temperature, Darkness: Float;

  //Used to check 24 hr log file rollover
  LCStartFileTime: TDateTime;

  Setting: string; //Setting of recorder

  //Fix for multi-language issues
  FPointSeparator, FCommaSeparator: TFormatSettings;

  Recording: boolean; //Flag to indicate that record is being logged
  RecordingMode: boolean = False; //Indicate that user has pressed Record button

  OldSecond: word; //Previous second value used to check timer rollover.

  //Log continuous mode
  LCMode:String;
  LCFreq:Integer;
  LogTimePreset, LogTimeCurrent: Integer;
  DesiredLogTime: TDateTime;
  StopRecording: boolean;
  //ET: TEpikTimer;

const
  verbose_error=1;
  verbose_action=2;
  verbose_debug=3;



implementation

uses
    appsettings;


var
   ser: TBlockSerial;

procedure verbose(level:integer; message:string);
begin
  if (verbosity >= level) then
    writeln(FormatDateTime('yyyy-mm-dd"T"hh:nn:ss.zzz": "', Now)+message);
end;


// Open the communications port
function OpenComm() : boolean;
begin
     verbose(verbose_debug,'Opening port: '+SelectedPort);
     ser.LinuxLock:=False; //lock file sometimes persists stuck if program closes before port
     verbose(verbose_debug,'Trying to connect ...');
     ser.Connect(SelectedPort);
     verbose(verbose_debug,'Connected to: '+SelectedPort);
     ser.config(115200, 8, 'N', SB1, False, False);
     OpenComm:=True;    //Indicate success
     verbose(verbose_debug,'Opened port: '+SelectedPort);
end;

// Close the communications port
function CloseComm() : boolean;
begin
     ser.CloseSocket;
     CloseComm:=True;       //Indicate success
end;

// Send a command strings then return the result
function SendGet(command:string; LeaveOpen:boolean = False; Timeout:Integer=3000; GetAlso:boolean = True; HideStatus:boolean = False) : string;
//LeaveOpen indicates that the communication port should be left open
var
   ErrorString: AnsiString;
begin

     //Initialze output string to nothing.
     SendGet:='';

     if (not LeaveOpen) then
        begin
                OpenComm();
        end;
     ErrorString:='';

      ser.SendString(command);
      if (GetAlso) then
         SendGet:=ser.Recvstring(Timeout);
      If CompareStr(ser.LastErrorDesc,'OK')<>0 then
         ErrorString:='Error: '+ser.LastErrorDesc;


     if (not LeaveOpen) then
        begin
                CloseComm();
        end;
     if not HideStatus then
        begin
          if GetAlso then
            verbose(verbose_debug,'Sent: '+command+'   To: '+SelectedPort+'   Received: '+SendGet+ErrorString)
          else
            verbose(verbose_debug,'Sent: '+command+'   To: '+SelectedPort);
        end;
end;

//Return the tty port name of a specific USB serial number (if found)
procedure FindUSBtty(serialnumber:string);
const
    USBDevicePath = '/dev/serial/by-id/';
Var Info : TSearchRec;
    Count : Longint;
    USBDeviceSerial: String;
    LinuxDeviceFile: String;
    pieces:TStringList;
Begin
  pieces := TStringList.Create;
  pieces.Delimiter := '-';

  verbose(verbose_debug,'Searching for Linux USB devices ...'); //Troubleshooting information
  verbose(verbose_debug,'Searching here : '+USBDevicePath+'usb-FTDI_*'); //Troubleshooting information

  Count:=0;
  If FindFirst (USBDevicePath+'usb-FTDI_*',faAnyFile ,Info)=0 then
    begin
    Repeat
      Inc(Count);
      With Info do
        begin
        verbose(verbose_debug,'Found this : '+Name); //Troubleshooting information
        pieces.DelimitedText:=Name;
        USBDeviceSerial:=AnsiRightStr(pieces.Strings[pieces.Count-3],8);
        LinuxDeviceFile:=ExpandFileName(USBDevicePath+fpReadLink(USBDevicePath+Name));
        verbose(verbose_debug,'USB device serial number='+USBDeviceSerial);

        // Check if srial number matches selected, or
        //   if no serial number was defined, then just use the last one found
        if (serialnumber=USBDeviceSerial) or (serialnumber='') then begin
          SelectedPort:=LinuxDeviceFile;
          verbose(verbose_debug,'Found a match: '+serialnumber+' : ' + LinuxDeviceFile);
        end;

        verbose(verbose_debug,'Connection='+LinuxDeviceFile);

        end;
    Until FindNext(info)<>0;
    end;
  FindClose(Info);
  verbose(verbose_debug,'Finished Linux USB search. Found '+IntToStr(Count)+' devices');


  //If no matches are found, then try to select if only one device is found.
  if not (SelectedPort='') then //Selected port has been set
      begin
        pieces.Delimiter := ',';
        pieces.DelimitedText := SendGet('ix');
        verbose(verbose_debug,'pieces.DelimitedText='+pieces.DelimitedText);
        SelectedUnitSerialNumber:=IntToStr(StrToIntDef(pieces.Strings[4],0));
        verbose(verbose_debug,'SelectedUnitSerialNumber='+SelectedUnitSerialNumber);
        INISection:='Serial:'+SelectedUnitSerialNumber;
        try
          //TZRegion:= vConfigurations.ReadString(INISection,'Local region');
          TZRegion:= appsettings.vConfigurations.ReadString('','','');//vConfigurations.ReadString(INISection,'Local region');
          ptz.ParseDatabaseFromFile(appsettings.TZDirectory+TZRegion);
          verbose(verbose_debug,'INISection='+INISection);
          TZLocation:=vConfigurations.ReadString(INISection,'Local time zone');
          verbose(verbose_debug,'TZLocation='+TZLocation);
        except begin //Default to GMT since no proper definition could be found
           verbose(verbose_error,'failed tz find');
           TZRegion:= 'etcetera';
           ptz.ParseDatabaseFromFile(appsettings.TZDirectory+TZRegion);
           verbose(verbose_debug,'INISection='+INISection);
           TZLocation:=vConfigurations.ReadString(INISection,'Local time zone');
           if (TZLocation='') then begin
             verbose(verbose_error,'TZLocation checking='+TZLocation);
             TZLocation:='Etc/GMT'
           end;
         end;

        end;
      end
    else
        verbose(verbose_error,'No selected port defined and no device found.');

End;

procedure WriteDLHeader(Setting: String = '' );
//Setting describes how UDM was used to create this logfile.
var
  result, HeaderFirmwareVersion: AnsiString;
  ProtocolNumber,ModelNumber,FeatureNumber,SerialNumber : Integer;
  Info:     TVersionInfo;

begin
    verbose(verbose_action,'*****Writing header...');

    {Gather information about the selected unit}
    result:=SendGet('ix');
    ProtocolNumber:=StrToIntDef(AnsiMidStr(result,3,8),0);
    ModelNumber:=StrToIntDef(AnsiMidStr(result,12,8),0);
    FeatureNumber:=StrToIntDef(AnsiMidStr(result,21,8),0);
    SerialNumber:=StrToIntDef(AnsiMidStr(result,30,8),0);
    HeaderFirmwareVersion:=
      IntToStr(ProtocolNumber)+'-'+
      IntToStr(ModelNumber)+'-'+
      IntToStr(FeatureNumber);

    LogFileName:=Format(
        '%s%s_%s.dat',
        [appsettings.LogsDirectory + DirectorySeparator,
         FormatDateTime('yyyymmdd"_"hhnnss',Now),
         vConfigurations.ReadString(INISection,'Instrument ID')
         ]);

    verbose(verbose_action,'LogFileName='+LogFileName);

    //Application.ProcessMessages; //why is this here?
    AssignFile(DLRecFile,LogFileName);

    Rewrite(DLRecFile); //Open file for writing

    { Write header }
    SetTextLineEnding(DLRecFile,#13#10);
    Writeln(DLRecFile,'# Light Pollution Monitoring Data Format 1.0');
    Writeln(DLRecFile,'# URL: http://www.darksky.org/measurements');
    Writeln(DLRecFile,'# Number of header lines: 35');
    Writeln(DLRecFile,'# This data is released under the following license: ODbL 1.0 http://opendatacommons.org/licenses/odbl/summary/');
    Writeln(DLRecFile,'# Device type: USB');
    Writeln(DLRecFile,'# Instrument ID: '+vConfigurations.ReadString(INISection,'Instrument ID'));
    Writeln(DLRecFile,'# Data supplier: '+vConfigurations.ReadString(INISection,'Data Supplier'));
    Writeln(DLRecFile,'# Location name: '+vConfigurations.ReadString(INISection,'Location Name'));
    Writeln(DLRecFile,'# Position (lat, lon, elev(m)): '+vConfigurations.ReadString(INISection,'Position'));
    Writeln(DLRecFile,'# Local timezone: '+TZLocation);
    Writeln(DLRecFile,'# Time Synchronization: '+vConfigurations.ReadString(INISection,'Time Synchronization'));
    Writeln(DLRecFile,'# Moving / Stationary position: '+vConfigurations.ReadString(INISection,'Moving Stationary Position'));
    Writeln(DLRecFile,'# Moving / Fixed look direction: '+vConfigurations.ReadString(INISection,'Moving Stationary Direction'));
    Writeln(DLRecFile,'# Number of channels: '+vConfigurations.ReadString(INISection,'Number Of Channels'));
    Writeln(DLRecFile,'# Filters per channel: '+vConfigurations.ReadString(INISection,'Filters Per Channel'));
    Writeln(DLRecFile,'# Measurement direction per channel: '+vConfigurations.ReadString(INISection,'Measurement Direction Per Channel'));
    Writeln(DLRecFile,'# Field of view (degrees): '+vConfigurations.ReadString(INISection,'Field Of View'));
    Writeln(DLRecFile,'# Number of fields per line: 6');
    Writeln(DLRecFile,Format('# SQM serial number: %d',[SerialNumber]));
    Writeln(DLRecFile,'# SQM firmware version: '+HeaderFirmwareVersion);
    Writeln(DLRecFile,'# SQM cover offset value: '+vConfigurations.ReadString(INISection,'CoverOffset'));
    Writeln(DLRecFile,'# SQM readout test ix: '+result);
    Writeln(DLRecFile,'# SQM readout test rx: '+sendget('rx'));
    Writeln(DLRecFile,'# SQM readout test cx: '+sendget('cx'));
    Writeln(DLRecFile,'# Comment: '+vConfigurations.ReadString(INISection,'UserComment1'));
    Writeln(DLRecFile,'# Comment: '+vConfigurations.ReadString(INISection,'UserComment2'));
    Writeln(DLRecFile,'# Comment: '+vConfigurations.ReadString(INISection,'UserComment3'));
    Writeln(DLRecFile,'# Comment: '+vConfigurations.ReadString(INISection,'UserComment4'));
    Writeln(DLRecFile,'# Comment: '+vConfigurations.ReadString(INISection,'UserComment5'));


    // Log the UDM version.
    Info := TVersionInfo.Create;
    Info.Load(HINSTANCE);
    Writeln(DLRecFile,Format('# UDMC version: %s',
                                [IntToStr(Info.FixedInfo.FileVersion[0])
                                +'.'+IntToStr(Info.FixedInfo.FileVersion[1])
                                +'.'+IntToStr(Info.FixedInfo.FileVersion[2])
                                +'.'+IntToStr(Info.FixedInfo.FileVersion[3])]));
    Info.Free;

    //Log the current UDM settings that were passed here.
    Writeln(DLRecFile,Format('# UDMC setting: %s',[Setting]));

    Writeln(DLRecFile,'# blank line 32');
    Writeln(DLRecFile,'# UTC Date & Time, Local Date & Time, Temperature, Counts, Frequency, MSAS');
    Writeln(DLRecFile,'# YYYY-MM-DDTHH:mm:ss.fff;YYYY-MM-DDTHH:mm:ss.fff;Celsius;number;Hz;mag/arcsec^2');
    Writeln(DLRecFile,'# END OF HEADER');
    Flush(DLRecFile);
end;

procedure LogOneReading();
var
  pieces: TStringList;
  Result: string;
  ThisMoment, ThisMomentUTC: TDateTime;
  subfix: ansistring;

const
    WebPageFile = '/tmp/index.html';

  function CheckRecordCount(): boolean;
  begin
    if (pieces.Count = 6) then
      CheckRecordCount := True
    else
    begin
      CheckRecordCount := False;
      verbose(verbose_error,format( 'CheckRecordCount fail: pieces.Count = %d',[pieces.Count]));
    end;
  end;

  procedure WriteRecord(Special: string = '');
  var
    ComposeString: string;
  begin
    ThisMomentUTC := NowUTC;

    vConfigurations.ReadString(INISection,'Local time zone');
    verbose(verbose_debug,'INISection='+INISection);
    if (TZLocation='') then verbose(verbose_error,'TZLocation is empty');
    verbose(verbose_debug,'TZLocation='+TZLocation);
    ThisMoment := ptz.GMTToLocalTime(ThisMomentUTC, TZLocation, subfix);

    //Create new logfile if over 24hr UTC time.
    if (DayOf(LCStartFileTime) <> DayOf(ThisMoment)) then begin
        // Open new file and store header
        WriteDLHeader(Format('Logged continuously %s.', [setting]));
        LCStartFileTime := ThisMoment;
      end
    else
      begin
        AssignFile(DLRecFile, LogFileName);
        Append(DLRecFile); //Open file for appending
        SetTextLineEnding(DLRecFile, #13#10);
      end;

    // Display LogFileNameText path
    verbose(verbose_debug,'LogFileName='+LogFileName);

    if (Special = '') then begin
      { Pull in values }
      Temperature := StrToFloatDef(
        AnsiLeftStr(pieces.Strings[5], Length(pieces.Strings[5]) - 1), 0,
        FPointSeparator);
      Darkness := StrToFloatDef(AnsiLeftStr(pieces.Strings[1],
        Length(pieces.Strings[1]) - 1), 0, FPointSeparator);

      // YYYY-MM-DDTHH:mm:ss.fff;YYYY-MM-DDTHH:mm:ss.fff;Celsius;number;Hz;mag/arcsec^2')
      ComposeString :=
        FormatDateTime('yyyy-mm-dd"T"hh:nn:ss.zzz";"', ThisMomentUTC) //Date UTC
        //+ FormatDateTime('yyyy-mm-dd"T"hh:nn:ss.zzz";"', Now) //Date Local
        //+ FormatDateTime('yyyy-mm-dd"T"hh:nn:ss.zzz";"', DesiredLogTime)
        + FormatDateTime('yyyy-mm-dd"T"hh:nn:ss.zzz";"', ThisMoment) //Date Local (calculated)
        + FormatFloat('##0.0', Temperature, FPointSeparator) + ';' //Temperature
        + Format('%d;', [StrToIntDef(AnsiLeftStr(pieces.Strings[3],
        length(pieces.Strings[3]) - 1), 0)]) //counts
        + Format('%d;', [StrToIntDef(AnsiLeftStr(pieces.Strings[2],
        length(pieces.Strings[2]) - 2), 0)]) //Hz
        + FormatFloat('#0.00', Darkness, FPointSeparator) //mpsas value
      ;
    end //end of "special" text check when no data was available (missed record).
    else begin
      ComposeString :=
        FormatDateTime('yyyy-mm-dd"T"hh:nn:ss.zzz";"', NowUTC) //Date UTC
        + FormatDateTime('yyyy-mm-dd"T"hh:nn:ss.zzz";"', Now) //Date Local
        + Special;
    end;

    verbose(verbose_action,'Logging one reading.');
    Writeln(DLRecFile, ComposeString);

    //Write HTML page to ram disk (/tmp)
     try
       AssignFile(RamStatusFile,WebPageFile);
       Rewrite(RamStatusFile); //Open file for writing

       //save in text file : last reading with all data and timestamp
       Writeln(RamStatusFile, '<html><head><title>UDMC</title>');
       //Writeln(RamStatusFile, '<meta http-equiv="refresh" content="1" />');
       Writeln(RamStatusFile, '</head><body>');
       Writeln(RamStatusFile,'UTC: '+FormatDateTime('yyyy-mm-dd" "hh:nn:ss', ThisMomentUTC)+'<br>'); //Date UTC
       Writeln(RamStatusFile,'Local: '+FormatDateTime('yyyy-mm-dd" "hh:nn:ss', ThisMoment)+'<br>'); //Local time (calculated)
       Writeln(RamStatusFile,'Darkness: '+FormatFloat('#0.00', Darkness, FPointSeparator) +' mag/arcsec&sup2;<br>');
       Writeln(RamStatusFile,'Temperature at light sensor : ' + FormatFloat('##0.0', Temperature, FPointSeparator) +'&deg;C<br>');
       Writeln(RamStatusFile, '</body></html>');
       Flush(RamStatusFile);
       Close(RamStatusFile);
       verbose(verbose_action,'Wrote to '+WebPageFile);

     //****update graph (if feature exists)
     except
       verbose(verbose_error,'Failed ' + WebPageFile + ' write.');
     end;

  end;

begin
  Recording := True;
  pieces := TStringList.Create;
  pieces.Delimiter := ',';
  pieces.StrictDelimiter := False; //Parse spaces also

  //Try a first time
  Result := SendGet('rx');
  pieces.DelimitedText := Result;
  if CheckRecordCount then
    WriteRecord
  else begin //Try a second time
    Result := SendGet('rx');
    pieces.DelimitedText := Result;
    if CheckRecordCount then
      WriteRecord
    else begin //Try a second time
      Result := SendGet('rx');
      pieces.DelimitedText := Result;
      if CheckRecordCount then
        WriteRecord
      else
        WriteRecord(';;;'); //Empty fields
    end;
  end;

  Flush(DLRecFile);
  Close(DLRecFile);
  Recording := False;
end;

procedure FineTimerTimer();
var
  ThisMoment: TDateTime;
begin
  // Only trigger out once per second with ~20ms accuracy.
  // This prevents general drift in recordings.


  if (SecondOf(Now) <> OldSecond) then
  begin
    OldSecond := SecondOf(Now);
    if (RecordingMode) then
    begin
      //Gets triggered by the fine timer once per second if RecordingMode is true.

      if (Recording = False) then
        //Prevent more recording while already busy saving a record
      begin
        ThisMoment := RecodeMilliSecond(Now, 0);
        //CurrentTime.Caption := FormatDateTime('yyyy-mm-dd hh:nn:ss', ThisMoment);

        // Check if counter has expired while in Seconds or Minutes mode
        if ((LCMode='LCMS') or (LCMode='LCMM')) then begin
          Dec(LogTimeCurrent);
          if (LogTimeCurrent <= 0) then begin

            // Log value//* threshold checking
            LogOneReading;

            // Restart counter for continuous logging
            LogTimeCurrent := LogTimePreset;
          end;
        end;

        if LCMode='LCM' then begin // On-the-clock mode selected
            if (CompareDateTime(ThisMoment, DesiredLogTime) >= 0) then begin
                // Log value//* threshold checking
                LogOneReading;
                OnTheClock(ThisMoment, LCFreq); //Every __ minutes
              end
            else begin //continue counting
                LogTimeCurrent := Round(SecondSpan(ThisMoment, DesiredLogTime));
              end;
        end;

        if StopRecording then begin
          RecordingMode := False;
          LogTimePreset := 0;
          LogTimeCurrent := 0;
        end;
      end; //End of checking Recording flag

      //*** Update reading value to ram file for web access

      //*** Update Chart (when one is created) for web access

    end;

  end;


end;

procedure OnTheClock(const ThisMoment: TDateTime; const Granularity: integer);
{This utility gets the next time from now based on a granularity setting,
 i.e for determining log time and difference for logging every x minutes}
begin
  // Determine new record time and calculate time until record
  DesiredLogTime := ThisMoment; //Copy current time for manipulation
  DesiredLogTime := IncMinute(DesiredLogTime, Granularity -
    MinuteOf(ThisMoment) mod Granularity);
  //Replace seconds with 0
  DesiredLogTime := RecodeSecond(DesiredLogTime, 0);
  LogTimeCurrent := SecondsBetween(ThisMoment, DesiredLogTime);
end;

procedure testfptimer();
begin
  writeln('test fp timer');
end;

initialization

  ser:=TBlockSerial.Create;
  ptz := TPascalTZ.Create();

  // Format seetings to convert a string to a float
  FPointSeparator := DefaultFormatSettings;
  FPointSeparator.DecimalSeparator := '.';
  FPointSeparator.ThousandSeparator := '#';// disable the thousand separator
  FCommaSeparator := DefaultFormatSettings;
  FCommaSeparator.DecimalSeparator := ',';
  FCommaSeparator.ThousandSeparator := '#';// disable the thousand separator

end.