パソコン練習日記

やってみたことを記録しています。Bloggerからこちらに引っ越して来ました。一部、画像がリンク切れしていますがひとつひとつ直すのは心が折れたのでそのままにします。

Visual Studioとコンテナを使ったCICDお勉強の記録3 単体テストの前後処理追加

ここの続き

このままテストを繰り返すとデータが溜まっていってテスト整合性保てなくなる場合があることや、テスト用のDBはコンテナで使い捨てにすることを踏まえ、前処理後処理をいれる。

前処理はこんな感じにしておく。DBが存在したら、どういう使われ方したかわからんからDropしてからDBを作成。そのあと、テスト用のテーブル作成。

[TestInitialize]
public void Setup()
{
    using (var connection = new SqlConnection(connectionString))
    {
        connection.Open();

        // データベースが存在するか確認し、存在する場合は削除
        string checkDbExistenceQuery = $"SELECT database_id FROM sys.databases WHERE Name = '{testDatabase}'";
        using (var command = new SqlCommand(checkDbExistenceQuery, connection))
        {
            var databaseId = command.ExecuteScalar();
            if (databaseId != null)
            {
                string dropDbQuery = $"DROP DATABASE {testDatabase}";
                using (var dropCommand = new SqlCommand(dropDbQuery, connection))
                {
                    dropCommand.ExecuteNonQuery();
                }
            }
        }

        // テスト用データベースの作成
        string createDbQuery = $"CREATE DATABASE {testDatabase}";
        using (var command = new SqlCommand(createDbQuery, connection))
        {
            command.ExecuteNonQuery();
        }

        // テスト用テーブルの作成
        string useDbQuery = $"USE {testDatabase}";
        string createTableQuery = "CREATE TABLE TestTable (date DATETIME NOT NULL, num INT NOT NULL)";
        using (var command = new SqlCommand($"{useDbQuery}; {createTableQuery}", connection))
        {
            command.ExecuteNonQuery();
        }
    }
}

後処理でも、使ったDBを削除するお片付け処理を入れとく

[TestCleanup]
        public void Cleanup()
        {
            using (var connection = new SqlConnection(connectionString))
            {
                connection.Open();

                // テスト用データベースの削除
                string dropDbQuery = $"DROP DATABASE {testDatabase}";
                using (var command = new SqlCommand(dropDbQuery, connection))
                {
                    command.ExecuteNonQuery();
                }
            }
        }

いちおう、動作確認。ブレークポイント置いてテストデバッグ

DB消えた

DB作成された

テーブル作成された。

もちろん、データなし。

テストも正常に終了

ということでもう一回テスト実行したら、前処理でDBがDropできなくてエラー。なんかがDB使っているからDropできんよ。とのこと
TestCleanup method DatabaseOperationTests.DataInserterTests.Cleanup threw exception. System.Data.SqlClient.SqlException: Cannot drop database "TestDB" because it is currently in use..

前処理と後処理それぞれのDrop処理の前にコネクション強制切断処理を追加
// データベースへのすべての接続を切断
        string alterDbQuery = $"ALTER DATABASE {testDatabase} SET SINGLE_USER WITH ROLLBACK IMMEDIATE";
        using (var alterCommand = new SqlCommand(alterDbQuery, connection))
        {
            alterCommand.ExecuteNonQuery();
        }

これでとりあえず、テストを何回繰り返してもエラーにならなくなった

 

今時点で、最終的なテストプログラムは以下

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Data.SqlClient;
using SqlServerOperation;

namespace DatabaseOperationTests
{
    [TestClass]
    public class DataInserterTests
    {
        private readonly string server = "192.168.10.13";
        private readonly string username = "sa";
        private readonly string password = "P@$$w0rd";
        private readonly string testDatabase = "TestDB";
        private readonly string connectionString;

        public DataInserterTests()
        {
            connectionString = $"Server={server};Database=master;User Id={username};Password={password};";
        }

        [TestInitialize]
        public void Setup()
        {
            using (var connection = new SqlConnection(connectionString))
            {
                connection.Open();

                

                // データベースが存在するか確認し、存在する場合は削除
                string checkDbExistenceQuery = $"SELECT database_id FROM sys.databases WHERE Name = '{testDatabase}'";
                using (var command = new SqlCommand(checkDbExistenceQuery, connection))
                {
                    var databaseId = command.ExecuteScalar();
                    if (databaseId != null)
                    {
                        // データベースへのすべての接続を切断
                        string alterDbQuery = $"ALTER DATABASE {testDatabase} SET SINGLE_USER WITH ROLLBACK IMMEDIATE";
                        using (var alterCommand = new SqlCommand(alterDbQuery, connection))
                        {
                            alterCommand.ExecuteNonQuery();
                        }

                        string dropDbQuery = $"DROP DATABASE {testDatabase}";
                        using (var dropCommand = new SqlCommand(dropDbQuery, connection))
                        {
                            dropCommand.ExecuteNonQuery();
                        }
                    }
                }

                // テスト用データベースの作成
                string createDbQuery = $"CREATE DATABASE {testDatabase}";
                using (var command = new SqlCommand(createDbQuery, connection))
                {
                    command.ExecuteNonQuery();
                }

                // テスト用テーブルの作成
                string useDbQuery = $"USE {testDatabase}";
                string createTableQuery = "CREATE TABLE TestTable (date DATETIME NOT NULL, num INT NOT NULL)";
                using (var command = new SqlCommand($"{useDbQuery}; {createTableQuery}", connection))
                {
                    command.ExecuteNonQuery();
                }
            }
        }

 

        [TestMethod]
        public void InsertData_InsertsCorrectly()
        {
            // Arrange
            var testConnectionString = $"Server={server};Database={testDatabase};User Id={username};Password={password};";
            var dataInserter = new DataInserter(server, testDatabase, username, password);
            var testDate = new DateTime(2021, 1, 1);
            int testNum = 1;

            // Act
            dataInserter.InsertData(testDate, testNum);

            // Assert
            using (var connection = new SqlConnection(testConnectionString))
            {
                connection.Open();
                string selectQuery = "SELECT date, num FROM TestTable WHERE num = @Value2";
                using (var command = new SqlCommand(selectQuery, connection))
                {
                    command.Parameters.AddWithValue("@Value2", testNum);
                    using (var reader = command.ExecuteReader())
                    {
                        Assert.IsTrue(reader.Read()); // レコードが存在することを確認
                        Assert.AreEqual(testDate, reader.GetDateTime(0)); // 日付が一致することを確認
                        Assert.AreEqual(testNum, reader.GetInt32(1)); // num が一致することを確認
                    }
                }
            }
        }

        [TestCleanup]
        public void Cleanup()
        {
            using (var connection = new SqlConnection(connectionString))
            {
                connection.Open();

                // データベースへのすべての接続を切断
                string alterDbQuery = $"ALTER DATABASE {testDatabase} SET SINGLE_USER WITH ROLLBACK IMMEDIATE";
                using (var alterCommand = new SqlCommand(alterDbQuery, connection))
                {
                    alterCommand.ExecuteNonQuery();
                }

                // テスト用データベースの削除
                string dropDbQuery = $"DROP DATABASE {testDatabase}";
                using (var command = new SqlCommand(dropDbQuery, connection))
                {
                    command.ExecuteNonQuery();
                }
            }
        }

    }
}

 

 

Visual Studioとコンテナを使ったCICDお勉強の記録2 とりあえず単体テストが動くようになるまで

これの続き

今度は、とりあえず単体テストが動くようになるまでをやっていく

単体テストを追加

ソリューションを右クリックし、「追加」 > 「新しいプロジェクト」を選択

MSTest、NUnit、xUnitとかある。 どれ使えばいいのかわからん。

Bingチャットに相談してみて、「ユニットテストの基本的な概念は同じであるため、一つを学べば他のフレームワークも理解しやすくなるし、とりあえずどれでもいいと思うよ。」という結論になり、MSTestで行くことにした。

Testプロジェクトの名前は"DatabaseOperationTests"にしとく。

メインプログラムのプロジェクトの名前を"SQLDB_SimpleTests"していたので、どちらがテストプロジェクトなのかわかりづらい。失敗したと思った。

単体テストプロジェクトからメインプログラムのプロジェクト参照追加

テストプロジェクトを右クリック、「追加」>「プロジェクト参照」

テスト対象のプロジェクト"SQLDB_SimpleTests"を参照

テスト対象のプログラムのnamespace名をテストのほうでusing

単体テストとテスト対象プログラムのリファクタリング

テストプログラムをとりあえず以下な感じに

テスト対象のプログラムが今はProgramクラスのMainメソッドだけど、データをInsertするプログラムなので、DataInserterクラスに変更する前提でテストを作る。


using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Data.SqlClient;
using SqlServerOperation;

namespace DatabaseOperationTests
{
    [TestClass]
    public class DataInserterTests
    {
        private readonly string server = "192.168.10.13";
        private readonly string username = "sa";
        private readonly string password = "P@$$w0rd";
        private readonly string testDatabase = "TestDB";
        private readonly string connectionString;

        public DataInserterTests()
        {
            connectionString = $"Server={server};Database=master;User Id={username};Password={password};";
        }

 

        [TestMethod]
        public void InsertData_InsertsCorrectly()
        {
            // Arrange
            var testConnectionString = $"Server={server};Database={testDatabase};User Id={username};Password={password};";
            var dataInserter = new DataInserter(server, testDatabase, username, password);
            var testDate = new DateTime(2021, 1, 1);
            int testNum = 1;

            // Act
            dataInserter.InsertData(testDate, testNum);

            // Assert
            using (var connection = new SqlConnection(testConnectionString))
            {
                connection.Open();
                string selectQuery = "SELECT date, num FROM TestTable WHERE num = @Value2";
                using (var command = new SqlCommand(selectQuery, connection))
                {
                    command.Parameters.AddWithValue("@Value2", testNum);
                    using (var reader = command.ExecuteReader())
                    {
                        Assert.IsTrue(reader.Read()); // レコードが存在することを確認
                        Assert.AreEqual(testDate, reader.GetDateTime(0)); // 日付が一致することを確認
                        Assert.AreEqual(testNum, reader.GetInt32(1)); // num が一致することを確認
                    }
                }
            }
        }
    }
}

テスト対象プログラムもテストし易いようにリファクタリング

using System;
using System.Data.SqlClient;

namespace SqlServerOperation
{
    class Program
    {
        static void Main(string[] args)
        {
            var dataInserter = new DataInserter("192.168.10.13,1433", "TestDB", "sa", "P@$$w0rd");
            dataInserter.InsertData(DateTime.Now, 1);
        }
    }

    public class DataInserter
    {
        private readonly string _connectionString;

        public DataInserter(string server, string database, string username, string password)
        {
            _connectionString = $"Server={server};Database={database};User Id={username};Password={password};";
        }

        public void InsertData(DateTime date, int num)
        {
            using (var connection = new SqlConnection(_connectionString))
            {
                connection.Open();

                string insertQuery = "INSERT INTO TestTable (date, num) VALUES (@Value1, @Value2)";
                using (var command = new SqlCommand(insertQuery, connection))
                {
                    command.Parameters.AddWithValue("@Value1", date);
                    command.Parameters.AddWithValue("@Value2", num);
                    command.ExecuteNonQuery();
                }
            }
        }
    }
}

テスト実行前に今DBのテーブルにあるデータを削除

USE [TestDB]
GO
 
/****** Object:  Table [dbo].[TestTable]    Script Date: 2024/01/01 14:29:32 ******/
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TestTable]') AND type in (N'U'))
DROP TABLE [dbo].[TestTable]
GO
 
/****** Object:  Table [dbo].[TestTable]    Script Date: 2024/01/01 14:29:32 ******/
SET ANSI_NULLS ON
GO
 
SET QUOTED_IDENTIFIER ON
GO
 
CREATE TABLE [dbo].[TestTable](
[date] [datetime] NOT NULL,
[num] [int] NOT NULL
) ON [PRIMARY]
GO

テスト実行

テスト完了

DBも一応確認。テストデータがちゃんと登録されている

 

Visual Studioとコンテナを使ったCICDお勉強の記録1 とりあえずDBに接続するまで

最終的にAKSにデプロイするまでのCICDを構築していきたいと思っています。

まずは、コンテナで動かすまで

 

開発デバイスの環境

コンテナを使っていきたいので、また、開発はVisual Studioを使う。

そのために以下をインストールしておく。

  • Visual Studio2022
  • Docker desktop
プログラム作成

とりあえず、コードはこんな感じにしておく。単体テスト書く段階になったらリファクタリングしていくつもり。

現在時刻と数字の"1"をDBにInsertするだけの簡単なプログラム。

using System;
using System.Data.SqlClient;

namespace SqlServerOperation
{
    class Program
    {
        static void Main(string[] args)
        {
            string server = "192.168.10.13,1433"; // ホスト名と動的に割り当てられたポート
            string database = "TestDB"; // データベース名
            string username = "sa";
            string password = "P@$$w0rd";

            string connectionString = $"Server={server};Database={database};User Id={username};Password={password};";

            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                connection.Open();

                //現在日時取得
                DateTime now = DateTime.Now;

                // テーブルに挿入する数値
                int testNum = 1;

                //データ登録
                string insertQuery = "INSERT INTO TestTable (date,num) VALUES (@Value1, @Value2)";
                using (SqlCommand command = new SqlCommand(insertQuery, connection))
                {
                    command.Parameters.AddWithValue("@Value1", now);
                    command.Parameters.AddWithValue("@Value2", testNum);
                    command.ExecuteNonQuery();
                }

                Console.WriteLine("Data inserted successfully.");
            }
        }
    }
}

using System.Data.SqlClientしているのに、SqlConnectionとかでCS1069エラーになっている。

調べたところ、System.Data.SqlClientをNugetしないといけないらしい。

感覚的には、using System.Data.SqlClientのところがエラーになるんじゃないの?って思ってしまうんだけど、そういうもんなのかなぁ。と思いながらNuget

とりあえず、エラー無くなった。

 

Dockerコンテナ化対応(Docker Compose)

開発デバイス上でのDockerコンテナを使ったCICD環境は、Docker Cmposeが良いとのことなので、

プロジェクトを右クリックし、「追加」 > 「コンテナーオーケストレーターのサポート」を選択

Windowsライセンスのこととか考えたくないのでLinux

 

docker-compose.ymlと

Dockerfileが自動で作成

 

デバッグ実行が、"Docker Compose"に変わった。

DBはまだないけど、デバッグ実行。

あたりまえだけどDB接続エラー。それはいいとして、コンテナを見てみる

コンテナが作られて動いてる

っていうか、コンテナイメージのほうを見てみると脆弱性が21あるんだけど

いちおう、12日前に作られたイメージっぽいけど・・・

dockerfileの中で、apt update upgradeしようとしたけど

 

.NETの公式DockerイメージはMicrosoftが提供し、セキュリティパッチを含む定期的な更新が行われています。一般的に、これらのイメージに対して apt-get update や apt-get upgrade を実行することは、特に必要がない限り推奨されません。


とのことなので、止めとくことにした。

 

デバッグのとき接続する用のDB作成

このプログラムが接続しに行くDBを作成。

UbuntuベースのSQLServer Dockerコンテナを使う。

Microsoft SQL Server - Ubuntu based images by Microsoft | Docker Hub

 

単体テスト用はDocker Composeを使ってコンテナを作るつもりだが、デバッグ用は単純にコンテナを稼働させておくだけにしておく。

 

SQLServer2022のイメージを使う。まずはdocker pull

docker pull mcr.microsoft.com/mssql/server:2022-latest
Microsoft Learnを参考にしつつ、

learn.microsoft.com

今のところ特に何か理由があるわけではないけど、SQL Server照合順序を"Japanese_CI_AS"に、コンテナの名前を"sqlserver_express_debug"にして、1433ポートをマッピング

docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=P@$$w0rd" -e "MSSQL_COLLATION=Japanese_CI_AS" -p 1433:1433 --name sqlserver_express_debug -d mcr.microsoft.com/mssql/server:2022-latest

コンテナが稼働したので、

SQLServer Management Studioで接続

 プログラムで定義した通りのDB、テーブルを作成。

DBも作ったので、デバッグを実行。エラーなしで処理終了

標準出力がコンテナのログとして出力されていることを確認

データがDBに登録されたことを確認


いったんこれで区切って、次は単体テストを作ることを行う

 

 

Visual StudioのBlazorサンプルアプリを使ってフロントエンドの単体テストを学習

ちょっと作りたいアプリがあり、フロントどうしようかと。

VueやReact、どれも一から学習で、どれ使おうか迷っていたのですが、なんとなくBlazorにしてみようと

で、DevOpsに関することも学習していきたいので、単体テストも自動化したいと。

なので、書き方とか含めそもそもどうやって作っていくのかもが一緒に学習してみようと思っていまして

とりあえず、Visual Studioで作成されるBlazorのサンプル?アプリを題材に簡単な単体テストを作ってみようと

ChatGPTにおんぶにだっこで進める。

Blazor WebAssemblyの

ASP.NET Core Hostedなし(バックエンドなしのスタンドアロン)のやつを使いました。

既定でこんなのが作られる

 

実行したらこんな感じ

 

まずは、この"Click me"ボタンを押すとカウントアップしていくCounterページがシンプルっぽい気がするので、ここの単体テストを作ってみる

 

とりあえず、テストプロジェクトを追加したいので、ソリューションに新しいプロジェクトを追加。追加しない方法もあるっぽいが、いったんこれで。

 

いろんなテストプロジェクトがあるんだけど、どれがいいのかChatGPTに聞いたところ、Blazorアプリケーション専用のテストライブラリとして、bUnitというのがあり、bUnitはxUnit.netと組み合わせて使用されることが推奨されているとのこと。

ということでxUnitのテストプロジェクトを選択。

 

NuGetでbunitをインストール。

Preview版ではなく、なんとなく最新安定版の1.25.3を選択

 

テストプロジェクトからサンプルアプリが参照できるように、

サンプルアプリを参照追加


で、ChatGPTにテストコード作ってもらった

ボタンを1回押したらCurrent countが1になるかのテスト

using Bunit;
using YourApp.Components; // Counterコンポーネントが含まれている名前空間を使用
using Xunit;

public class CounterTests
{
    [Fact]
    public void CounterShouldIncrementWhenClicked()
    {
        // テストコンテキストのセットアップ
        using var ctx = new TestContext();

        // Counterコンポーネントレンダリング
        var cut = ctx.RenderComponent<Counter>();

        // ボタンを見つける
        var button = cut.Find("button");

        // ボタンをクリックする
        button.Click();

        // 状態の検証
        // クリック後にカウントが1増えているか確認
        cut.Find("p").MarkupMatches("<p role=\"status\">Current count: 1</p>");
    }
}

 

ちなみにAssertがないので、これって大丈夫なの?って質問をしたら、

MarkupMatches というメソッドが bUnit の機能の一つで、指定した期待値のマークアップと一致するかを検証する処理だからAssertと同じだよ。とのこと。ふーん。

      // クリック後にカウントが1増えているか確認
        cut.Find("p").MarkupMatches("<p role=\"status\">Current count: 1</p>");

 

ちなみにその<p role="status">の箇所はここ

 

 

これをUnitTest1.csに貼り付け。

using YourApp.Components; 

の箇所をサンプルアプリのCounter.razorを読み込めるようにしないといけないんだけど、どう書けばいいのかわからんのでChatGPTに聞いてみると

using BlazorApp1.Pages;でいいっぽい。

コンパイル時にCounter.razorがクラスに変換され、Pagesが名前空間になるので、とのこと

 

では、テストを実行

 

お、一発で動いた。



試しにもうひとつテストを追加。これもChatGPTに作ってもらった。

ボタンを2回押したらCurrent countが2になるかのテスト

ご丁寧にAssert使ってくれた。

using Bunit;
using BlazorApp1.Pages; // Counterコンポーネントが含まれている名前空間を使用
using Xunit;

public class CounterTests
{
    [Fact]
    public void CounterShouldIncrementWhenClicked()
    {
        // テストコンテキストのセットアップ
        using var ctx = new TestContext();

        // Counterコンポーネントレンダリング
        var cut = ctx.RenderComponent<Counter>();

        // ボタンを見つける
        var button = cut.Find("button");

        // ボタンをクリックする
        button.Click();

        // 状態の検証
        // クリック後にカウントが1増えているか確認
        cut.Find("p").MarkupMatches("<p role=\"status\">Current count: 1</p>");
    }

    [Fact]
    public void CounterShouldIncrementTwiceWhenClickedTwice()
    {
        // Arrange
        using var ctx = new TestContext();
        var cut = ctx.RenderComponent<Counter>();

        // Act
        var button = cut.Find("button");
        button.Click(); // First click
        button.Click(); // Second click

        // Assert
        var paragraph = cut.Find("p[role='status']");
        Assert.Equal("Current count: 2", paragraph.TextContent);
    }
}

 

テストケースが2つに増えている。


とりあえず、なんとなくわかってきたので次に進もうと思う。

 

長年使っているMicrosoftアカウントが「このMicrosoftアカウントは存在していません。」て出てビビったけど数時間で復旧

朝、MicrosoftアカウントでSSOしているChatGPTへログインするため認証を行ったところ、認証失敗と出た。

なんか間違えたかな?と思いながらもう一度認証すると、

「このMicrosoftアカウントは存在していません。」

メールアドレス入力し間違えたかな?と思ってやり直したり、スペルチェック?したりしたけど

あってんだよなぁ。。

 

別のデバイスから入ってみよう。と、やったけど、やっぱりダメ。

 

障害かなぁ?と思いながら、"Microsoftアカウント 障害"とかで検索したけど、Microsoft365の障害情報ページしか見当たらない。だし、いちおうMicrosoft365も障害では無さげ。

次にX(Twitter)見たけど、リアルタイムで同じ事象にあっている感じの人が誰もいない。

 

え、マジでアカウント消えた?

え、ChatGPTってMicrosoftアカウントでのSSOでしかログインできんのに、解約もできんじゃん。

10年くらい使ってるんだけど。そこそこ大事なメールとかあるし、Microsoft資格もこのアカウントに紐づいているんだけど。

とか、二要素のメール認証、このMicrosoftアカウントのメールにしてるサービス、結構あるのにログインできなくなるじゃん。全部サポートに連絡したりして、復旧させるの?コレ。

 

え、うそでしょ

マジでめんどくさい。うそでしょ

 

すでにセッションが確立しているiPhoneやPCからメールなどは見れるし操作できることを確認。

アカウントはまだ存在するっぽい。切れそうな細い紐だけどアクセスできる手段は確保。

とはいえ、コレも今のセッションが切れるとアクセスできなくなって終わり。ってなったらヤバイ。ぜんぜん安心できない。

 

X(Twitter)で、「よくある。次の日には復旧してる」みたいなものがあったけど、仕事で移動中もずっと「マジかよ。ほんとうに消えてたらどうしよう。」とモヤモヤ。

 

で、2時間後にスマホで認証してみたら、普通にログインできた。

障害かよ。マジで障害情報どっかに出してくれよ。いや、マジで

 

とはいえ、SSOとか2要素にメールアドレス指定しているものは、見直したほうがいいわ。コレ

 

Logicoolのハンコン G29の値をC#で取得

やりたいことがあるので、ハンコンのアクセル開度やハンドルの値をC#で取得したいのです。

 

 

ということで

ちゃんと取得できるようになるまでの記録です。

実際に動いたプログラムはここに置いています。

github.com

 

まずは、ChatGPTにサンプル作ってとお願い。

「とりあえずハンドル角度とアクセル開度取得のプログラムお願いします。」

で、もらったのが以下。

ちなみにSharpDX.DirectInputをNuGetでインストールが必要。

using System;
using SharpDX.DirectInput;

class Program
{
    static void Main()
    {
        // DirectInputインスタンスを作成
        var directInput = new DirectInput();

        // 接続されているすべてのジョイスティックを取得
        var joystickGuids = directInput.GetDevices(DeviceType.Joystick, DeviceEnumerationFlags.AllDevices);
        Joystick joystick = null;

        // G29を検索
        foreach (var guid in joystickGuids)
        {
            var deviceInstance = directInput.GetDeviceDescriptor(guid.InstanceGuid);
            if (deviceInstance.ProductName.Contains("G29"))
            {
                joystick = new Joystick(directInput, guid.InstanceGuid);
                break;
            }
        }

        if (joystick == null)
        {
            Console.WriteLine("G29が見つかりませんでした。");
            return;
        }

        joystick.Acquire();

        while (true)
        {
            joystick.Poll();
            var joystickState = joystick.GetCurrentState();

            // ハンドルの角度 (通常はX軸)
            int wheelPosition = joystickState.X;

            // アクセルの開度 (通常は1つ目のスライダー)
            int acceleratorPosition = joystickState.Sliders[0];

            Console.WriteLine($"Wheel: {wheelPosition}, Accelerator: {acceleratorPosition}");
        }
    }
}

 

G29の検索する処理までちゃんと入っている。

素晴らしい。これは、一撃で終わるんじゃないか?と思いきやエラー。

 

エラー    CS1061    'DirectInput' に 'GetDeviceDescriptor' の定義が含まれておらず、型 'DirectInput' の最初の引数を受け付けるアクセス可能な拡張メソッド 'GetDeviceDescriptor' が見つかりませんでした。using ディレクティブまたはアセンブリ参照が不足していないことを確認してください    logicoolG29    E:\work\logicoolG29\logicoolG29\Program.cs    18    アクティブ

 

エラーを伝えると修正を教えてくれる。

foreach (var deviceInstance in directInput.GetDevices(DeviceType.Joystick, DeviceEnumerationFlags.AllDevices))
{
    if (deviceInstance.ProductName.Contains("G29"))
    {
        joystick = new Joystick(directInput, deviceInstance.InstanceGuid);
        break;
    }
}

 

接続しているのに見つからんかった。

 

ChatGPTに相談したら

G29は実際にはDeviceType.JoystickよりもDeviceType.DrivingやDeviceType.Gamepadとして認識されることがあります。

ってことで、修正を作ってくれる。何回かやり取りして以下の修正案

class Program
{
    static void Main()
    {
        // ...

        var joystick = FindG29(directInput);

        if (joystick == null)
        {
            Console.WriteLine("G29が見つかりませんでした。");
            return;
        }

        // ...
    }

    static Joystick? FindG29(DirectInput directInput)  // static を追加
    {
        foreach (var deviceType in new { DeviceType.Driving, DeviceType.Gamepad, DeviceType.Joystick })
        {
            foreach (var deviceInstance in directInput.GetDevices(deviceType, DeviceEnumerationFlags.AllDevices))
            {
                if (deviceInstance.ProductName.Contains("G29"))
                {
                    return new Joystick(directInput, deviceInstance.InstanceGuid);
                }
            }
        }
        return null;
    }
}

 

取れた。と思ったけど、
アクセルの値は、実はクラッチの値。

ステアリングはあってる。

 

 

色々試したいので、ブレーキとクラッチも追加

// ハンドルの角度 (通常はX軸)
int wheelPosition = joystickState.X;

// アクセルの開度
int acceleratorPosition = joystickState.Sliders[0];

// ブレーキの開度
int brakePosition = joystickState.Sliders[1];

// クラッチの開度 (RotationZを使用)
int clutchPosition = joystickState.RotationZ;

Console.WriteLine($"Wheel: {wheelPosition}, Accelerator: {acceleratorPosition}, Brake: {brakePosition}, Clutch: {clutchPosition}");

クラッチのところがブレーキ。アクセルのところがクラッチがとれているのはいいとして、アクセルの値が取れていない。

 

まぁ、でもどうせアクセルだから

AccelerationXとか、AccelerationYとかAcceleration系の値のどれかが正解だろ?

と思いながら

// アクセルの開度
int acceleratorPosition =

に入るものを変えて見ていったけど、ぜんぜん正解にたどり着かない。

 

やり方を変えて見る。

ブレークポイントおいて、変数の中を見てみる。

 

これを見て、アクセルだけオンとオフの状態を比較すれば、それだろ。と

 

で、これがアクセルオフ

{X: 32864, Y: 65535, Z: 0, RotationX: 0, RotationY: 0, RotationZ: 65535, Sliders: 65535;0, PointOfViewControllers: -1;-1;-1;-1, Buttons: False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False, VelocityX: 0, VelocityY: 0, VelocityZ: 0, AngularVelocityX: 0, AngularVelocityY: 0, AngularVelocityZ: 0, VelocitySliders: 0;0, AccelerationX: 0, AccelerationY: 0, AccelerationZ: 0, AngularAccelerationX: 0, AngularAccelerationY: 0, AngularAccelerationZ: 0, AccelerationSliders: 0;0, ForceX: 0, ForceY: 0, ForceZ: 0, TorqueX: 0, TorqueY: 0, TorqueZ: 0, ForceSliders: 0;0}

 

これがアクセルオン

{X: 32864, Y: 0, Z: 0, RotationX: 0, RotationY: 0, RotationZ: 65535, Sliders: 65535;0, PointOfViewControllers: -1;-1;-1;-1, Buttons: False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False, VelocityX: 0, VelocityY: 0, VelocityZ: 0, AngularVelocityX: 0, AngularVelocityY: 0, AngularVelocityZ: 0, VelocitySliders: 0;0, AccelerationX: 0, AccelerationY: 0, AccelerationZ: 0, AngularAccelerationX: 0, AngularAccelerationY: 0, AngularAccelerationZ: 0, AccelerationSliders: 0;0, ForceX: 0, ForceY: 0, ForceZ: 0, TorqueX: 0, TorqueY: 0, TorqueZ: 0, ForceSliders: 0;0}

 

Yが変わってる。Yじゃね?

ってことで修正。

using System;
using SharpDX.DirectInput;

class Program
{
    static void Main()
    {
        // DirectInputのインスタンスを作成
        var directInput = new DirectInput();

        // 接続されているすべてのジョイスティックを取得
        var joystickGuids = directInput.GetDevices(DeviceType.Joystick, DeviceEnumerationFlags.AllDevices);
        var joystick = FindG29(directInput);

        if (joystick == null)
        {
            Console.WriteLine("G29が見つかりませんでした。");
            return;
        }

        joystick.Acquire();

        while (true)
        {
            joystick.Poll();
            var joystickState = joystick.GetCurrentState();

            // ハンドルの角度 (通常はX軸)
            int wheelPosition = joystickState.X;

            // アクセルの開度 (通常はY軸)
            int acceleratorPosition = joystickState.Y;

            // クラッチの開度 (RotationZを使用する場合)
            int clutchPosition = joystickState.Sliders[0];

            // ブレーキの開度
            int brakePosition = joystickState.RotationZ;

            Console.WriteLine($"Wheel: {wheelPosition}, Accelerator: {acceleratorPosition}, Brake: {brakePosition}, Clutch: {clutchPosition}");
        }
    }

    static Joystick? FindG29(DirectInput directInput)  // static を追加
    {
        foreach (var deviceType in new { DeviceType.Driving, DeviceType.Gamepad, DeviceType.Joystick })
        {
            foreach (var deviceInstance in directInput.GetDevices(deviceType, DeviceEnumerationFlags.AllDevices))
            {
                if (deviceInstance.ProductName.Contains("G29"))
                {
                    return new Joystick(directInput, deviceInstance.InstanceGuid);
                }
            }
        }
        return null;
    }
}

 

で、実行。

ちなみに、Gifにしているからカクカクしているけど、

実際は、0.001秒に30回ほど値を取得して表示している。

 

これでとりあえず、C#でG29の値が取れることはわかったので、

次に進もうと思う。

 

[blog:g:4207112889945342980:banner][blog:g:11696248318754550877:banner][blog:g:11696248318754550880:banner]

AnsibleプレイブックでWindowsServerの日本語化

WindowsServerの日本語化のAnsibleプレイブックを作ったので、Githubに置きました。

ansible/configure_windows_lang_jp at master · aki-sfarm/ansible (github.com)

 

 

Azure側で提供されているWindowsServerイメージを利用すると英語設定なので、

新しく作るたびに日本語化を自動化自体は、前からチャレンジしたりしてました。

blog.sfarm.okinawa.jp

 

そして、Ansibleプレイブックでやってみたいなとは思っていました。

Ansible勉強したいな。使ってみたいな。

でも全然勉強しない、が続いていました。が、

今回、やっとやってみました。

 

まずは、とてもお世話になった以下の記事内のスクリプトをAnsibleプレイブック化しました。ちょっとアレンジした気がしますが

www.intellilink.co.jp

 

それがconfigure_windows_lang_jp_autologon.ymlです。

使ってみた感じ、ローカルのWindowsServerを日本語化するには便利だなと思う反面、ローカルだったら最初から日本語で入れてるわ。と

で、Azure上のWindowsServerだと上の記事で紹介されているRunPowerShellScriptを使ったほうが全然良い気がする。

 

そして、PowerShellスクリプトを作成してオートログオンでRunOnceで実行させる箇所があるのですが、それやるとAnsibleでやる意味が薄れるので作ったのが、

configure_windows_lang_jp_wait.ymlです。

2回再起動が必要なのですがそこもすべてAnsibleプレイブックでやっています。

処理を並べて書いているだけですが

 

ちょっとですが、

Ansibleちゃんと使ってる感が出ました。