2台以上のパソコンでwsjtxなどを運用すると、ログファイルを共有した方が何かと便利です。1台のPCでwsjtxとJTDXでログファイルを共有するには、ハードリンクを設定すれば良いのですが、2台のPCとなると、この手は使えません。ネットワークでファイル共有するように設定する手がありますが、危険な匂いがします。私の場合、自作のログファイルでログは管理しているので、QSO済みの相手局やDXCCエンティティーをwsjtxやJTDXのアプリで識別できれば良く、定期的に2つのログファイルを合成するという方法で特に問題ないと思います。最初は、テキストエディターを使って合成していましたが、ファイルサイズが大きくて編集する部分を探すのに手間が掛かるし、同じファイル名のテキストファイルを3つ同時に開いて操作するので、間違いをやらかしてしまいそうで不安でした。そこで、「2つのwsjtx_log.adiを合成するプログラム」を製作することにしました。
Visual Studio 2026 CommunityとVB.NETを使って開発しました。プログラムを書く前に、アルゴリズムを簡単にまとめました。
1)ファイルAとファイルBを入力として、ファイルCに書き出す。
2)これら3つのファイルは、wsjtx_log.adiという同じファイル名である。
3)同じファイル名なので、別のフォルダーに配置されるものとする。
4)ファイルAとファイルBの最初の行は、 WSJT-X ADIF Export<eoh>である。
5)2行目以降の各行は、<call:で始まる。これ等の条件を満たさない場合にはエラー終了とする。
6)ファイルAから一行を読み込み、文字列Aに格納する。
7)ファイルBから一行を読み込み、文字列Bに格納する。
8)A=BならばファイルCに書き込み、6)にジャンプする。
9)日付と時刻の比較して、早いものをファイルCに書き込む。
10)書き込んだ方の文字列の属するファイルから一行を読み込み、文字列に格納し、8)にジャンプする。
11)上記6)7)10)の途中でEOFになったら未書き込みの文字列をファイルCに書き込み、処理を終了する。
日付の比較は、<qso_date:8>以降に続く8文字を整数値に変換して行う
時刻の比較は、<time_on:6>以降に続く6文字を整数値に変換して行う
使い方は、合成したい2つの入力ファイルと合成したファイルを格納するパスを設定して、実行ボタンを押すというものです。約1万件のデータを処理した時、私のマシンで20秒程掛かります。合成後のファイルを、2つのマシンのログファイルを定位置に上書きコピーすれば操作は完了です。以下にソースコードを示します。
'WSJTX Log join program
'author H.NAMVA, JH4ADK
'original issued on 2025.11.25
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
OpenFileDialog1.InitialDirectory = TextBox1.Text
If OpenFileDialog1.ShowDialog() = DialogResult.OK Then
TextBox1.Text = OpenFileDialog1.FileName
Dim selectedFilePath As String = OpenFileDialog1.FileName
Else
MessageBox.Show("キャンセルされました")
End If
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
OpenFileDialog2.InitialDirectory = TextBox2.Text
If OpenFileDialog2.ShowDialog() = DialogResult.OK Then
TextBox2.Text = OpenFileDialog2.FileName
Dim selectedFilePath As String = OpenFileDialog2.FileName
Else
MessageBox.Show("キャンセルされました")
End If
End Sub
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
SaveFileDialog1.InitialDirectory = TextBox3.Text
Dim result As DialogResult = SaveFileDialog1.ShowDialog()
If result = DialogResult.OK Then
Dim filePath As String = SaveFileDialog1.FileName
TextBox3.Text = filePath
End If
End Sub
Private Function parseTime(ByVal line As String) As Integer
Dim pos As Integer = line.IndexOf("<time_on:6>")
Dim str As String = line.Substring(pos + 11, 6)
parseTime = Integer.Parse(str)
End Function
Private Function parseDate(ByVal line As String) As Integer
Dim pos As Integer = line.IndexOf("<qso_date:8>")
Dim str As String = line.Substring(pos + 13, 8)
parseDate = Integer.Parse(str)
End Function
Private Function lineB_IsLater(ByVal lineA As String, ByVal lineB As String) As Boolean
Dim dateA As Integer = parseDate(lineA)
Dim timeA As Integer = parseTime(lineA)
Dim dateB As Integer = parseDate(lineB)
Dim timeB As Integer = parseTime(lineB)
If dateA < dateB Then
lineB_IsLater = True
ElseIf dateA > dateB Then
lineB_IsLater = False
Else
If timeA < timeB Then
lineB_IsLater = True
Else
lineB_IsLater = False
End If
End If
End Function
Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
Dim lineA As String = ""
Dim lineB As String = ""
Button4.Enabled = False
Try
Dim sr1 As New System.IO.StreamReader(OpenFileDialog1.FileName)
Dim sr2 As New System.IO.StreamReader(OpenFileDialog2.FileName)
Dim sw As New System.IO.StreamWriter(SaveFileDialog1.FileName)
lineA = sr1.ReadLine()
If Not lineA.Contains("WSJT-X ADIF Export<eoh>") Then
MsgBox("Log AのヘッダーはWSJT-Xと違います" + lineA)
Exit Sub
End If
lineB = sr2.ReadLine()
If Not lineB.Contains("WSJT-X ADIF Export<eoh>") Then
MsgBox("Log BのヘッダーはWSJT-Xと違います" + lineB)
Exit Sub
End If
sw.Write(lineA + vbCrLf)
lineA = ""
lineB = ""
Dim lineCount As Integer = 0
Do
If (Not sr1.EndOfStream) And (lineA.Length = 0) Then
lineA = sr1.ReadLine()
If Not lineA.Contains("<call:") Then
MsgBox("Log A行はWSJT-Xの形式ではありません")
Exit Sub
End If
End If
If (Not sr2.EndOfStream) And (lineB.Length = 0) Then
lineB = sr2.ReadLine()
If Not lineB.Contains("<call:") Then
MsgBox("Log Bの行はWSJT-Xの形式ではありません")
Exit Sub
End If
End If
If lineA.Equals(lineB) Then
sw.Write(lineA + vbCrLf)
lineA = ""
lineB = ""
Else
If (lineA.Length = 0) And (lineB.Length > 0) Then
sw.Write(lineB + vbCrLf)
lineB = ""
ElseIf (lineA.Length > 0) And (lineB.Length = 0) Then
sw.Write(lineA + vbCrLf)
lineA = ""
ElseIf lineB_IsLater(lineA, lineB) Then
sw.Write(lineA + vbCrLf)
lineA = ""
Else
sw.Write(lineB + vbCrLf)
lineB = ""
End If
End If
lineCount += 1
Debug.Print(lineCount)
Loop Until sr1.EndOfStream And sr2.EndOfStream
If lineA.Length > 0 Then
sw.Write(lineA + vbCrLf)
End If
If lineB.Length > 0 Then
sw.Write(lineB + vbCrLf)
End If
Label5.Text = lineCount.ToString
sr1.Close()
sr2.Close()
sw.Close()
MsgBox("実行を完了しました")
Catch ex As Exception
MsgBox("実行中に例外を検出しました " + ex.Message)
Debug.Print(lineA)
Debug.Print(lineB)
End Try
Button4.Enabled = True
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
TextBox1.Text = My.Settings.PathFileA
TextBox2.Text = My.Settings.PathFileB
TextBox3.Text = My.Settings.PathFileC
Me.Location = New Point(My.Settings.LocationX, My.Settings.LocationY)
End Sub
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
My.Settings.PathFileA = TextBox1.Text
My.Settings.PathFileB = TextBox2.Text
My.Settings.PathFileC = TextBox3.Text
My.Settings.LocationX = Me.Location.X
My.Settings.LocationY = Me.Location.Y
End Sub
End Class


コメント