program bpress;
{$mode objfpc}{$H+}
uses
Classes, SysUtils, StrUtils, Clipbrd, DateUtils, Interfaces,
LCLProc, LazUTF8;
type
TBloodPressure = record
Systolic: Integer;
Diastolic: Integer;
Pulse: Integer;
end;
var
Sets: array[1..10] of TBloodPressure;
SetCount: Integer = 0;
function CustomDateStr: string;
const
DayNames: array[1..7] of string = ('SUN','MON','TUE','WED','THU','FRI','SAT');
var
y, r: Integer;
dow: string;
begin
y := YearOf(Now);
r := y - 2018; // 令和年号計算
dow := DayNames[DayOfWeek(Now)];
Result := Format('%dR%2.2d-', [y, r]) +
FormatDateTime('mmdd-', Now) +
dow + '-' +
FormatDateTime('hhnn', Now);
end;
function C(const S: string): string;
begin
Result := UTF8ToConsole(S);
end;
procedure ParseBP(const Line: string; out BP: TBloodPressure);
var
Raw, S, P: string;
SlashPos, SpacePos: SizeInt;
begin
Raw := Line;
if Raw <> Trim(Raw) then
raise Exception.Create('No leading/trailing spaces allowed. Format: 150/89 98');
SpacePos := Pos(' ', Raw);
if (SpacePos = 0) or (PosEx(' ', Raw, SpacePos + 1) <> 0) then
raise Exception.Create('Exactly one space required. Format: 150/89 98');
S := Copy(Raw, 1, SpacePos - 1);
P := Copy(Raw, SpacePos + 1, MaxInt);
SlashPos := Pos('/', S);
if (SlashPos = 0) or (PosEx('/', S, SlashPos + 1) <> 0) then
raise Exception.Create('Use one slash only. Format: 150/89 98');
if (SlashPos = 1) or (SlashPos = Length(S)) then
raise Exception.Create('Systolic and diastolic values required. Format: 150/89 98');
if not TryStrToInt(Copy(S, 1, SlashPos - 1), BP.Systolic) then
raise Exception.Create('Invalid systolic value.');
if not TryStrToInt(Copy(S, SlashPos + 1, MaxInt), BP.Diastolic) then
raise Exception.Create('Invalid diastolic value.');
if not TryStrToInt(P, BP.Pulse) then
raise Exception.Create('Invalid pulse value.');
end;
procedure ShowAverageTwo(Index1, Index2: Integer);
var
BP1, BP2: TBloodPressure;
Avg: TBloodPressure;
Diff: TBloodPressure;
avgLine: string;
begin
BP1 := Sets[Index1];
BP2 := Sets[Index2];
Writeln('calc...');
Writeln(Format('%d/%d %d', [BP1.Systolic, BP1.Diastolic, BP1.Pulse]));
Writeln(Format('%d/%d %d', [BP2.Systolic, BP2.Diastolic, BP2.Pulse]));
Diff.Systolic := Abs(BP1.Systolic - BP2.Systolic);
Diff.Diastolic := Abs(BP1.Diastolic - BP2.Diastolic);
Diff.Pulse := Abs(BP1.Pulse - BP2.Pulse);
Writeln(Format('%d/%d %d', [Diff.Systolic, Diff.Diastolic, Diff.Pulse]));
// 平均値(小数)を文字列にして "=" の長さを合わせる
avgLine := Format('%.1f/%.1f %.1f',
[(BP1.Systolic + BP2.Systolic) / 2,
(BP1.Diastolic + BP2.Diastolic) / 2,
(BP1.Pulse + BP2.Pulse) / 2]);
Writeln(StringOfChar('=', Length(avgLine)));
Writeln(avgLine);
Avg.Systolic := Round((BP1.Systolic + BP2.Systolic) / 2);
Avg.Diastolic := Round((BP1.Diastolic + BP2.Diastolic) / 2);
Avg.Pulse := Round((BP1.Pulse + BP2.Pulse) / 2);
Writeln(Format('%d/%d %d', [Avg.Systolic, Avg.Diastolic, Avg.Pulse]));
Writeln('...end');
end;
procedure ShowAverageRange(i1, i2: Integer);
var
sumSys, sumDia, sumPulse, count, idx: Integer;
begin
sumSys := 0; sumDia := 0; sumPulse := 0; count := 0;
for idx := i1 to i2 do
begin
sumSys := sumSys + Sets[idx].Systolic;
sumDia := sumDia + Sets[idx].Diastolic;
sumPulse := sumPulse + Sets[idx].Pulse;
Inc(count);
end;
Writeln('calc...');
Writeln(Format('%.1f/%.1f %.1f',
[sumSys / count, sumDia / count, sumPulse / count]));
Writeln(Format('%d/%d %d',
[Round(sumSys / count), Round(sumDia / count), Round(sumPulse / count)]));
Writeln('...end');
end;
procedure CopyForDocument;
var
avgLine: string;
Output: TStringList;
begin
Output := TStringList.Create;
Writeln('Copy to Clipboard for Document...');
try
Output.Add(CustomDateStr);
Output.Add(Format('%d/%d %d', [Sets[1].Systolic, Sets[1].Diastolic, Sets[1].Pulse]));
Output.Add(Format('%d/%d %d', [Sets[2].Systolic, Sets[2].Diastolic, Sets[2].Pulse]));
Output.Add(Format('%d/%d %d',
[Abs(Sets[1].Systolic - Sets[2].Systolic),
Abs(Sets[1].Diastolic - Sets[2].Diastolic),
Abs(Sets[1].Pulse - Sets[2].Pulse)]));
avgLine := Format('%.1f/%.1f %.1f',
[(Sets[1].Systolic + Sets[2].Systolic) / 2,
(Sets[1].Diastolic + Sets[2].Diastolic) / 2,
(Sets[1].Pulse + Sets[2].Pulse) / 2]);
Output.Add(StringOfChar('=', Length(avgLine)));
Output.Add(avgLine); // ← ここだけでOK
Output.Add(Format('%d/%d %d',
[Round((Sets[1].Systolic + Sets[2].Systolic) / 2),
Round((Sets[1].Diastolic + Sets[2].Diastolic) / 2),
Round((Sets[1].Pulse + Sets[2].Pulse) / 2)]));
Writeln(Output.Text);
Clipboard.AsText := Output.Text;
finally
Output.Free;
end;
end;
procedure CopyForWeb;
var
Avg: TBloodPressure;
Output: TStringList;
begin
Avg.Systolic := Round((Sets[1].Systolic + Sets[2].Systolic) / 2);
Avg.Diastolic := Round((Sets[1].Diastolic + Sets[2].Diastolic) / 2);
Avg.Pulse := Round((Sets[1].Pulse + Sets[2].Pulse) / 2);
Output := TStringList.Create;
Writeln('Copy to Clipboard for Webpage…');
try
Output.Add(CustomDateStr);
Output.Add(Format('%d/%d %d。', [Avg.Systolic, Avg.Diastolic, Avg.Pulse]));
Writeln(Output.Text);
Clipboard.AsText := Output.Text;
finally
Output.Free;
end;
end;
procedure WatchSets;
var
i: Integer;
begin
Writeln('Saved blood pressure sets:');
if SetCount = 0 then
Writeln('(no data)')
else
for i := 1 to SetCount do
Writeln(Format('Set %d: %d/%d %d',
[i, Sets[i].Systolic, Sets[i].Diastolic, Sets[i].Pulse]));
end;
procedure ShowHelpEnglish;
begin
Writeln('bpress console tool - commands:');
Writeln(' --set N : Enter blood pressure data for set number N');
Writeln(' --average N-M : Calculate average between set N to M');
Writeln(' (if N-M covers 2 sets, show differences too)');
Writeln(' --copyd : Copy document-style output to clipboard');
Writeln(' --copyw : Copy webpage-style output to clipboard');
Writeln(' --watch-sets : Show all saved blood pressure sets');
Writeln(' --help : Show this help message (English)');
Writeln(' --hj : Show help message (Japanese)');
Writeln('exit : Quit the program');
end;
procedure ShowHelpJapanese;
begin
Writeln(C('bpress コンソールツール - コマンド一覧:'));
Writeln(C(' --set N : セット番号 N に血圧データを入力します'));
Writeln(C(' --average N-M : セット N から M の平均値を計算します'));
Writeln(C(' (N-M が 2つのセットなら差分も表示します)'));
Writeln(C(' --copyd : ドキュメント用の詳細出力をクリップボードにコピーします'));
Writeln(C(' --copyw : Webページ用の簡易出力をクリップボードにコピーします'));
Writeln(C(' --watch-sets : 保存されたすべての血圧データを表示します'));
Writeln(C(' --help : 英語のヘルプを表示します'));
Writeln(C(' --hj : 日本語のヘルプを表示します'));
Writeln(C('exit : プログラムを終了します'));
end;
var
Line, Cmd, Param: string;
i1, i2: Integer;
begin
Writeln(' --help : Show this help message (English)');
Writeln(' --hj : Show help message (Japanese)');
while True do
begin
Write('$');
ReadLn(Line);
if Pos(' ', Line) > 0 then
begin
Cmd := LowerCase(Trim(Copy(Line, 1, Pos(' ', Line) - 1)));
Param := Trim(Copy(Line, Pos(' ', Line) + 1, MaxInt));
end
else
begin
Cmd := LowerCase(Trim(Line));
Param := '';
end;
if (Cmd = 'bpress') or (Cmd = '$bpress') then
begin
if StartsStr('--set', Param) then
begin
Delete(Param, 1, 5);
i1 := StrToInt(Trim(Param));
WriteLn('Enter blood pressure (e.g., 150/89 98):');
ReadLn(Line);
try
ParseBP(Line, Sets[i1]);
if i1 > SetCount then
SetCount := i1;
except
on E: Exception do
begin
Writeln(C('入力エラー:形式が正しくありません。例: 150/89 98'));
Writeln(C('詳細: ') + C(E.Message));
end;
end;
end
else if StartsStr('--average', Param) then
begin
Delete(Param, 1, 9);
if Param = '' then
Writeln('Usage: bpress --average N-M')
else if Pos('-', Param) > 0 then
begin
i1 := StrToInt(Trim(Copy(Param, 1, Pos('-', Param) - 1)));
i2 := StrToInt(Trim(Copy(Param, Pos('-', Param) + 1, MaxInt)));
if (i1 < 1) or (i2 > SetCount) or (i1 > i2) then
Writeln('Invalid range.')
else if (i2 - i1 + 1 = 2) then
ShowAverageTwo(i1, i2)
else
ShowAverageRange(i1, i2);
end
else
Writeln('Usage: bpress --average N-M');
end
else if (Param = '--copyd') then
CopyForDocument
else if (Param = '--copyw') then
CopyForWeb
else if (Param = '--watch-sets') then
WatchSets
else if (Param = '--help') then
ShowHelpEnglish
else if (Param = '--hj') then
ShowHelpJapanese
else
Writeln('Unknown command.');
end
else if (Cmd = 'exit') or (Cmd = '$exit') then
Break
else
Writeln('Invalid input.');
end;
end.