弊局JG6JAVは、通常運用記録とコンテスト運用もExcelを使っている。特に無線機の周波数とモードを自動的に記録することは重要だ。今までいろいろな手段でそれを行っていた。けれども時々問題発生していたのでもっと確実且つリアルタイムにその情報を取得する手段を模索していた。そこで最近頼りにしているChat-GPTに問い合わせたらlocalhost webサーバを使うという手法を提案された。そのwebサーバにExcel VBAからアクセスされたらその時点の最新情報を取得することができる。ということでさっそく組み込んでみたらストレスなく動作してくれている。IC7851RC2の全ソースコードは長いので今回取り組んだ部分のソースを何かの参考になればよいと思い公開する。
C# IC7851RC.exe IC-7851 Remote controler 無線機情報取得
// Rig Info Data Class
public static class RigInfoData
{
public static int RunNr { get; set; }
public static bool Run { get; set; }
public static decimal VFO_Freq { get; set; }
public static string LogMode { get; set; }
public static int RFPower { get; set; }
public static string Callsign { get; set; }
public static string GetString()
{
return $"{RunNr};{Run};{VFO_Freq};{LogMode};{RFPower};{Callsign}";
}
}
//
// Start_RigServer のRigInfoを更新する。
private void Send_RigInfo(int nr, bool run, string callsign = "")
{
RigInfoData.RunNr = nr;
RigInfoData.Run = run;
RigInfoData.Callsign = callsign;
}
//
// 変更後: 戻り値を Task にし、async void は避ける
bool _isRigServerRunning = false;
CancellationTokenSource _cts = new ();
// Excel用 無線機情報サーバー アプリケーション起動時に起動
private async Task Start_RigServer()
{
// サーバーが既に実行中の場合は起動しない
if (_isRigServerRunning) return;
_cts = new CancellationTokenSource();
var token = _cts.Token;
var listener = new HttpListener();
listener.Prefixes.Add("http://localhost:8080/");
try
{
listener.Start();
StatusLabel.Text = "◎Rig Server Started";
_isRigServerRunning = true;
while (!token.IsCancellationRequested) // 停止指示がない間ループを継続
{
var contextTask = listener.GetContextAsync();
await Task.WhenAny(contextTask, Task.Delay(-1, token));
if (token.IsCancellationRequested)
{
break;
}
var ctx = contextTask.Result;
if (ctx.Request.RawUrl == "/riginfo")
{ // アクセスされた。
RigInfoData.VFO_Freq = VFO_Freq.Value; // 周波数表示用 NumericUpDown
RigInfoData.LogMode = LogMode; // Log用モード名
RigInfoData.RFPower = RI.RFP; // 出力値
var RigInfo = RigInfoData.GetString();
byte[] buf = Encoding.UTF8.GetBytes(RigInfo);
ctx.Response.StatusCode = 200;
ctx.Response.ContentType = "text/plain; charset=utf-8";
ctx.Response.ContentEncoding = Encoding.UTF8;
// キャッシュを無効化
ctx.Response.Headers["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0";
ctx.Response.Headers["Pragma"] = "no-cache";
ctx.Response.Headers["Expires"] = "-1";
// 本文書き込み
ctx.Response.OutputStream.Write(buf, 0, buf.Length);
ctx.Response.OutputStream.Flush();
ctx.Response.OutputStream.Close();
ctx.Response.Close();
this.BeginInvoke(()=>StatusLabel.Text = $"◎Rig Server Sent {RigInfo}");
}
}
}
catch (OperationCanceledException)
{
// キャンセルによる例外は無視する
}
catch (Exception ex)
{
// その他のエラー処理
StatusLabel.Text = $"Start_RigServer : Error {ex.Message}";
}
finally
{
if (listener.IsListening)
{
listener.Stop();
}
listener.Close();
_isRigServerRunning = false;
_cts.Dispose();
}
}
//
// CancellationTokenSource をキャンセルし、await 中の GetContextAsync を中断させる
public void Stop_RigServer()
{
if (_isRigServerRunning && _cts != null)
{
_cts.Cancel();
}
}
//
// Run status 設定
private void Set_RunStatus(bool sw, bool post = true)
{
Running = sw;
RunFreq = Running ? VFO_Freq.Value : 0M;
if (post) { RunExcelMacro(1, Running); }
}
//
// Excel マクロ実行
void RunExcelMacro(int RunNr, bool args1 = false, string args2 = "")
{
Excel.Application xl = (Excel.Application)Marshal.GetActiveObject("Excel.Application");
if (xl == null) return;
var macroName = RunNr switch
{
1 => "StatusCheck",
2 => "RcvRigInfo",
_ => ""
};
if (macroName.Length < 1) { return; }
try
{
xl.Run(macroName, RunNr == 1 ? args1 : args2);
}
catch (Exception ex)
{
StatusLabel.Text =
$"[RunExcelMacro] {macroName}({args1},{args2}) Error: {ex.Message}";
}
}
この方法だとExcel VBAからアクセスするだけでよいのが利点だ。当然だが動作確認は、WEBブラウザでもできる。
Excel VBA jg6jav_log.xlsm 情報取得部分
' C# 起動中の RigServerにアクセス
Function HttpGetUTF8(url As String) As String
Dim xml As Object
Set xml = CreateObject("MSXML2.XMLHTTP")
xml.Open "GET", url, False
xml.send
Dim stream As Object
Set stream = CreateObject("ADODB.Stream")
stream.Type = 1 'binary
stream.Open
stream.Write xml.responseBody
stream.Position = 0
stream.Type = 2 'text
stream.Charset = "UTF-8"
HttpGetUTF8 = stream.ReadText
stream.Close
End Function
'
' RigServer で取得したデータを必要データに転記
Sub GetRigInfo(Optional Ret As Boolean = False)
Dim txt As String
txt = HttpGetUTF8("http://localhost:8080/riginfo")
Dim Rcv As Variant
Rcv = Split(txt, ";")
Dim i As Integer
Dim RigInfo As Range
Set RigInfo = Sheets("Settings").Range("RigInfo")
With RigInfo.Cells(1, 1)
For i = 0 To 5
.Offset(i, 0).Value = Rcv(i)
Next
End With
Dim Nr As Integer
Dim sta As Boolean
Dim Callsign As String
With RigInfo
Nr = .Cells(1, 1).Value '// マクロ選択番号
sta = .Cells(2, 1).Value '// Bloolean
rigFreq = .Cells(3, 1).Value '// 周波数情報
rigMode = .Cells(4, 1).Value '// モード
rigPower = .Cells(5, 1).Value '// 出力
Callsign = .Cells(6, 1).Value '// メモリーのコールサイン
End With
rigBand = Band(rigFreq)
If Ret Then Exit Sub
Select Case Nr
Case 1
Call RcvRigInfo(Callsign)
Case 2
Call StatusCheck(sta)
End Select
End Sub
'
'---// IC7851RCへSendkey //---
Sub IC7851(SKey As String)
Dim AC As Range
Set AC = ActiveCell
On Error GoTo ExitIC7851
AppActivate "IC-7851"
SendKeys (SKey)
SendKeys ("{NUMLOCK}")
ExitIC7851:
AppActivate Application.Caption
Err.Clear
On Error GoTo 0
AC.Activate
End Sub
コンテスト運用中は特にCWの自動送出を行っている。その送出文字列は、Excelからクリップボード経由で受け取り送出している。
C# Excel VBA からの SendKeyで反応する
// KeyDown イベント 部分
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
//
//
var Ctrl = e.Control;
var Alt = e.Alt;
if (Ctrl && e.KeyCode == Keys.V) // Ctrl + V 貼付に同じ
{
// ExcelでClipboardに記録された文字列データをCW送出させる
IDataObject data = Clipboard.GetDataObject();
if (data == null) { return; }
if (data.GetDataPresent(DataFormats.Text))
{
var SendText = (string)data.GetData(DataFormats.Text);
if (SendText.StartsWith("#F5:"))
{
var lng = SendText.Length - 4;
F5msg.Text = SendText.Substring(4, lng); // TextBox F5msg に転記
}
else
{
SendCW(SendText); // CW電文送出
}
}
}
if (Alt) // Alt + Keys
{
switch (KeyTop)
{
case Keys.Y:
Set_RunStatus(true, false);
return;
case Keys.N:
Set_RunStatus(false, false);
return;
default:
return;
}
}
//
//
}
Excel VBA CW 送出用文字列渡し部分
Dim Clip As New DataObject
With Clip
.SetText MSG 'CW送出文字列
.PutInClipboard
End With
AppActivate "IC-7851" 'IC7851RC2.exeをアクティブにする
SendKeys ("^V") 'Ctrl+V(貼付)
Excel 側から IC7851RC2.exe への文字列情報渡しは、CW送出文字列の他はないので他の設定変更はSendKeyで済ませいる。出力変更、フィルター設定、Band ScopeのEdge設定などができる。
ログもMySQLなどに移行してすべてC#で構成するとよいのだろうけれどもExcelは何かと使い勝手が良いのでなかなか思い切れないでいる。だからといってアマチュア無線局の皆さんの間で多く使われているアプリケーションを使う気にはなれない。


コメント