- 投稿日:2019-09-27T20:21:22+09:00
【Unity】PUN2でRpcTarget.AllBufferedとかOthersBufferedが仕事してない時に見るメモ
はじめに
ここへたどり着いたということは、
PhotonのRpcTarget.AllとRpcTarget.AllBufferedの違いは分かっているということでしょう。
分かっていない方は以下のブログ推奨です(PUN2の記事ではないですが、内容的には同じです)。
【Unity】僕もPhotonを使いたい #08 RPC() PhotonTargets編RpcTarget.○○Bufferedを使うと、後からルームに入室した人にもデータを送信してくれます。
が、この情報だけを見てドツボにハマってしまったのでメモ。Sceneをまたぐときはちょっと特別
単刀直入に言うとここの内容です。
【Unity】僕もPhotonを使いたい #13 isMessageQueueRunning
私はここを見る前に、よっしゃRPC使ったるでー!と先走ってしまい、
いや、Buffered動かないやんけ・・・と、同じ目に遭遇してしまった・・・。上記のリンク先と少しだけ違うのが、PUN2では
PhotonNetwork.IsMessageQueueRunning
IsのIが大文字になってます。
最後に
内容の無い記事になっていますが、
載せたリンク先はPUN2のワードで検索をかけると出会えない可能性があるので、
PUN2を使っている人が検索をかけそうなワードを散りばめているつもりです。
というのは建前で、忘れたころの自分が検索かけそうなワードを散りばめています。
- 投稿日:2019-09-27T15:41:04+09:00
C#でODP.NETの自己流便利クラス(Bind変数利用)
概要
C#でODP.NETを使おうと思ったら大変だったので記事に残します。
自己流の自分用便利クラスなのでベストプラクティスは他にあると思います。
※環境はVisualStudio2019です流れ
Oracle.ManagedDataAccsessのインストール
Oracleとの接続設定
Oracle操作用便利クラスの作成
便利クラスの使い方Oracle.ManagedDataAccsessのインストール
プロジェクトを右クリックし、「NuGet パッケージの管理(N)...」を選択
参照で「Oracle.ManagedDataAccsess」を検索し、インストールOracleとの接続設定
いくつか方法があるようですが、今回はApp.Configを利用します。
Oracle.ManagedDataAccsessをインストールした時点でが生成されています。
そこに必要な情報を入力してください。
○○○:エイリアス
△△△:DBサーバーのホスト名orIPアドレス※ここに入力する内容は、tnsnames.oraに登録してある内容と同じです。
※dataSources内には複数のdataSourceを入れることができます。<oracle.manageddataaccess.client> <version number="*"> <dataSources> <dataSource alias="○○○" descriptor="(DESCRIPTION = (ADDRESS = (PROTOCOL = tcp)(HOST = △△△)(PORT = 1521))(CONNECT_DATA = (SERVICE_NAME = orcl))) "/> </dataSources> </version> </oracle.manageddataaccess.client>Oracle操作用便利クラスの作成
あくまで自己流なので、より良い方法があれば教えてください。
一応Transactionも考慮しています。(未検証)using Oracle.ManagedDataAccess.Client; using System; namespace common { class DbControl { // DB接続 private OracleConnection conn; // SQL発行に必要 private OracleCommand cmd; // SELECT時の結果読み取りに利用 private OracleDataReader reader; // Transactionに利用 private OracleTransaction transaction; // Bind変数に利用する型 public static int DATE = 0; public static int VARCHAR2 = 1; public static int DOUBLE = 2; public static int INT16 = 3; public static int INT32 = 4; /// <summary> /// コンストラクタ:iniファイルから取得した情報でDBと接続し、接続(OracleConnection)を保持する /// </summary> public DbControl() { string connectString = "user id = " + Main.OracleLogonName + "; password = " + Main.OraclePassWord + "; data source = " + Main.OracleDbName; try { conn = new OracleConnection(connectString); conn.Open(); } catch (Exception ex) { Console.WriteLine(ex.Message); conn.Close(); } } /// <summary> /// SQL文を使用し、OracleCommandを作成する /// <para>string sql:assets/sqlから取得したSQL文の文字列</para> /// </summary> public void SetCommand(string sql) { cmd = new OracleCommand(sql, conn); // バインド変数の名前解決 cmd.BindByName = true; } /// <summary> /// バインド変数の設定 /// <para>string parameterName:カラム名</para> /// <para>object parameterValue:カラムの値。string、DateTime等を渡す</para> /// <para>int oracleDbType:カラムのデータタイプ指定。DbManagementの定数を渡す</para> /// </summary> public void SetBindVariable(string parameterName, object parameterValue, int oracleDbType) { OracleParameter dbParameter = cmd.CreateParameter(); switch (oracleDbType) { case 0: dbParameter.OracleDbType = OracleDbType.Date; break; case 1: dbParameter.OracleDbType = OracleDbType.Varchar2; break; case 2: dbParameter.OracleDbType = OracleDbType.Double; break; case 3: dbParameter.OracleDbType = OracleDbType.Int16; break; case 4: dbParameter.OracleDbType = OracleDbType.Int32; break; } dbParameter.ParameterName = parameterName; dbParameter.Value = parameterValue; cmd.Parameters.Add(dbParameter); } /// <summary> /// Transaction:開始(成功すればCommitを呼び、失敗したらRollBackを呼ぶ必要がある) /// </summary> public void BeginTransaction() { transaction = conn.BeginTransaction(); } /// <summary> /// Transaction:コミット /// </summary> public void Commit() { transaction.Commit(); } /// <summary> /// Transaction:ロールバック /// </summary> public void RollBack() { transaction.Rollback(); } /// <summary> /// SELECT系のSQLで使用 /// <returns>return OracleDataReader:</returns> /// </summary> public OracleDataReader ExecuteReader() { try { reader = cmd.ExecuteReader(); } catch (Exception ex) { Console.WriteLine(ex.Message); } return reader; } /// <summary> /// 実行系のSQLで使用 /// <returns>return Boolean:</returns> /// </summary> public Boolean ExecuteNonQuery() { int res = 99; try { res = cmd.ExecuteNonQuery(); } catch (Exception ex) { Console.WriteLine(ex.Message); } if (res >= 1) { return true; } else { return false; } } /// <summary> /// DBのコネクションを閉じる(readerがあればreaderも) /// </summary> public void Close() { // 実行系だとnullになる為 if (reader != null) { reader.Close(); } conn.Close(); } // public static String GetString(OracleDataReader read, string target) { if (read.GetValue(read.GetOrdinal(target)).ToString() == "") { return ""; } else { return read.GetString(read.GetOrdinal(target)); } } // public static int GetInt16(OracleDataReader read, string target) { return read.GetInt16(read.GetOrdinal(target)); } // public static int GetInt32(OracleDataReader read, string target) { return read.GetInt32(read.GetOrdinal(target)); } // public static float GetFloat(OracleDataReader read, string target) { return read.GetFloat(read.GetOrdinal(target)); } // public static String GetDateString(OracleDataReader read, string target, string format) { if (read.GetValue(read.GetOrdinal(target)).ToString() == "") { return ""; } else { return read.GetDateTime(read.GetOrdinal(target)).ToString(format); } } } }便利クラスの使い方
SELECTしてみる
string sql = "SELECT name FROM Something WHERE hobby = :hobby;"; DbControl dbControlt = new DbControl(); dbControlt.SetCommand(sql); // 第一引数はBind変数、第二引数は値、第三引数はカラムの型に合わせたもの dbControlt.SetBindVariable("hobby", "山登り", DbControl.VARCHAR2); OracleDataReader reader = dbControlt.ExecuteReader(); if (reader != null) { if (reader.HasRows) { while (reader.Read()) { Console.WriteLine(DbControl.GetString(reader, "name")); } } } DbControl.Close();UPDATEしてみる
string sql = "UPDATE Something SET hobby = :hobby WHERE name = :name;"; DbControl dbControlt = new DbControl(); dbControlt.SetCommand(sql); dbControlt.SetBindVariable("hobby", "川登り", DbControl.VARCHAR2); dbControlt.SetBindVariable("name", "田中", DbControl.VARCHAR2); OracleDataReader reader = dbControlt.ExecuteReader(); if (dbManagement.ExecuteNonQuery()) { Console.WriteLine("成功"); }else{ Console.WriteLine("失敗"); } DbControl.Close();
- 投稿日:2019-09-27T14:00:19+09:00
AndroidからPCへ加速度センサの値をUDPで送信する
プログラムの概要
今回のプログラムは、C#のコンソールアプリケーションで作成したサーバにAndroidの加速度センサの値をUDP通信で送信するものである。基本的にAndroid側で通信の制御をするので、C#側はただのエコーサーバを用いている。
プログラムのソースコード
activity._main.xml<EditText android:id="@+id/IP_Address" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="52dp" android:layout_marginEnd="16dp" android:ems="10" android:inputType="textPersonName" android:text="192.168.0." android:textSize="24sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <EditText android:id="@+id/Port" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="48dp" android:layout_marginEnd="16dp" android:ems="10" android:inputType="textPersonName" android:text="8080" android:textSize="24sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/IP_Address" /> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="280dp" android:text="X:" android:textSize="32sp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="24dp" android:text="Y:" android:textSize="32sp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/textView" /> <TextView android:id="@+id/textView3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="24dp" android:text="Z:" android:textSize="32sp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/textView2" /> <TextView android:id="@+id/X_Data" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="280dp" android:text="TextView" android:textSize="32sp" app:layout_constraintStart_toEndOf="@+id/textView" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/Y_Data" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="24dp" android:text="TextView" android:textSize="32sp" app:layout_constraintStart_toEndOf="@+id/textView2" app:layout_constraintTop_toBottomOf="@+id/X_Data" /> <TextView android:id="@+id/Z_Data" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="24dp" android:text="TextView" android:textSize="32sp" app:layout_constraintStart_toEndOf="@+id/textView3" app:layout_constraintTop_toBottomOf="@+id/Y_Data" /> <TextView android:id="@+id/textView7" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="230dp" android:text="加速度センサの値" android:textSize="32sp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/Ran" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="87dp" android:layout_marginLeft="87dp" android:layout_marginBottom="70dp" android:text="通信開始" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" /> <TextView android:id="@+id/textView4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:text="IPアドレス" android:textSize="24sp" app:layout_constraintBottom_toTopOf="@+id/IP_Address" tools:layout_editor_absoluteX="16dp" /> <TextView android:id="@+id/textView5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:text="ポート番号" android:textSize="24sp" app:layout_constraintBottom_toTopOf="@+id/Port" tools:layout_editor_absoluteX="16dp" /> <Button android:id="@+id/End" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="87dp" android:layout_marginLeft="87dp" android:layout_marginEnd="87dp" android:layout_marginRight="87dp" android:layout_marginBottom="70dp" android:text="通信解除" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/Ran" />MainActivity.javaimport android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.AsyncTask; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; import java.net.SocketException; public class MainActivity extends AppCompatActivity implements SensorEventListener{ private SensorManager sensorManager; private TextView X_Data_TextView; //加速度センサXの値 private TextView Y_Data_TextView; //加速度センサYの値 private TextView Z_Data_TextView; //加速度センサZの値 private String Data; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE); X_Data_TextView = findViewById(R.id.X_Data); Y_Data_TextView = findViewById(R.id.Y_Data); Z_Data_TextView = findViewById(R.id.Z_Data); Button ran = findViewById(R.id.Ran); Button end = findViewById(R.id.End); ran.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { final String address = ((EditText) findViewById(R.id.IP_Address)).getText().toString(); String port = ((EditText) findViewById(R.id.Port)).getText().toString(); int Port = Integer.parseInt(port); byte buf[] = new byte[Data.length()]; try { buf = Data.getBytes("SHIFT_JIS"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } InetSocketAddress inetSocketAddress = new InetSocketAddress(address, Port); final DatagramPacket datagramPacket = new DatagramPacket(buf, buf.length, inetSocketAddress); AsyncTask<DatagramPacket, Void, Void> task = new AsyncTask<DatagramPacket, Void, Void>() { @Override protected Void doInBackground(DatagramPacket... datagramPackets) { DatagramSocket datagramSocket = null; try { datagramSocket = new DatagramSocket(); datagramSocket.send(datagramPackets[0]); datagramSocket.close(); //TimeUnit.MILLISECONDS.sleep(500); } catch (SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } //catch (InterruptedException e) { // e.printStackTrace(); //} return null; } }; task.execute(datagramPacket); } }); end.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { final String address = ((EditText) findViewById(R.id.IP_Address)).getText().toString(); String port = ((EditText) findViewById(R.id.Port)).getText().toString(); int Port = Integer.parseInt(port); String exit = "exit"; byte buf[] = new byte[exit.length()]; try { buf = exit.getBytes("SHIFT_JIS"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } InetSocketAddress inetSocketAddress = new InetSocketAddress(address, Port); final DatagramPacket datagramPacket = new DatagramPacket(buf, buf.length, inetSocketAddress); AsyncTask<DatagramPacket, Void, Void> task = new AsyncTask<DatagramPacket, Void, Void>() { @Override protected Void doInBackground(DatagramPacket... datagramPackets) { DatagramSocket datagramSocket = null; try { datagramSocket = new DatagramSocket(); datagramSocket.send(datagramPackets[0]); datagramSocket.close(); } catch (SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } }; task.execute(datagramPacket); } }); } @Override protected void onResume(){ super.onResume(); //Event Listener登録 Sensor accel = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); sensorManager.registerListener((SensorEventListener) this,accel,SensorManager.SENSOR_DELAY_NORMAL); } @Override protected void onPause(){ super.onPause(); //Event Listener登録解除 sensorManager.unregisterListener((SensorEventListener) this); } @Override public void onSensorChanged(SensorEvent event){ if(event.sensor.getType() == Sensor.TYPE_ACCELEROMETER){ X_Data_TextView.setText(String.format("%.3f",500+(event.values[0])*25)); Y_Data_TextView.setText(String.format("%.3f",500+(event.values[1])*25)); Z_Data_TextView.setText(String.format("%.3f",500+(event.values[2]) *25)); Data = (500+event.values[0]*25) + " " + (500+event.values[1]*25) + " " + (500+event.values[2]*25); } } @Override public void onAccuracyChanged(Sensor sensor, int accuracy){ } }Program.csstatic void Main(string[] args) { //バインドするローカルIPとポート番号 string localIpString = "192.168.0.16"; System.Net.IPAddress localAddress = System.Net.IPAddress.Parse(localIpString); int localPort = 8080; //UdpClientを作成し、ローカルエンドポイントにバインドする System.Net.IPEndPoint localEP = new System.Net.IPEndPoint(localAddress, localPort); System.Net.Sockets.UdpClient udp = new System.Net.Sockets.UdpClient(localEP); int i = 0; string[] xyz = { "X", "Y", "Z" }; for (;;) { //データを受信する System.Net.IPEndPoint remoteEP = null; byte[] rcvBytes = udp.Receive(ref remoteEP); //データを文字列に変換する string rcvMsg = System.Text.Encoding.UTF8.GetString(rcvBytes); string[] RcvMsg = rcvMsg.Split(' '); //受信したデータと送信者の情報を表示する //"exit"を受信したら終了 if (RcvMsg[0] =="exit") { break; } for (i = 0; i < 3; i++) { Console.WriteLine("{0}:{1}",xyz[i], RcvMsg[i]); } Console.WriteLine("送信元アドレス:{0}/ポート番号:{1}", remoteEP.Address, remoteEP.Port); } //UdpClientを閉じる udp.Close(); Console.WriteLine("終了しました。"); Console.ReadLine(); }動作結果
課題
今回はサーバをコンソールアプリケーションで作成したが、フォームアプリケーションでの作成を目指している。
- 投稿日:2019-09-27T01:26:11+09:00
C# Stack のシリアライズ
問題
コントリビュートをしているプロジェクトでシリアライズ/デシリアライズしているStackが毎回反転していることに気が付いた。公式ドキュメントや、NewtonJsonのIssueを発見して問題を理解したのでメモしておく。
static void Main(string[] args) { var stack = new Stack<int>(); stack.Push(1); stack.Push(2); stack.Push(3); var json = JsonConvert.SerializeObject(stack); Console.WriteLine(json); var restored = JsonConvert.DeserializeObject<Stack<int>>(json); var count = restored.Count; for (int i = 0; i < count; i++) { Console.WriteLine(restored.Pop()); } Console.ReadLine(); }実行結果を見ると反転している。
[3,2,1] 1 2 3分析
原因を知りたいところだが、Stackの公式ドキュメントを見ると、
Stack<T>(IEnumerable<T>)
のコンストラクタは、Stackにリバースして格納するようになっている。StackもIEnumerableを実装しているのでvar stack = new Stack<int>(); var stack = new Stack<int>(stack);とかするとオーダーが反転する。このあたりが問題かもしれない。こちらのチケットを見ると近い将来実装するかもしれない。このチケットを見ると、原因は、Json.NET の実装で次のようにいっている。
Deserialization: if the payload is "[1,2,3]", we return a stack with 3 at the top.
Serialization: if stack's contents are [1, 2, 3] with 3 at the top, we return JSON payload [3,2,1].今デコンパイラが動かないので見れないけど、こういう振る舞いだというのは理解できた。
問題の解決
カスタムのシリアライザを書いているけど、カスタムなので復元の際にリバースさせることにした。
public static TraceContextBase Restore(string json) { if (!string.IsNullOrEmpty(json)) { var typeName = JObject.Parse(json)["$type"]; Type traceContextType = Type.GetType(typeName.Value<string>()); var restored = JsonConvert.DeserializeObject( json, traceContextType, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.Objects, PreserveReferencesHandling = PreserveReferencesHandling.Objects, ReferenceLoopHandling = ReferenceLoopHandling.Serialize, }) as TraceContextBase; restored.OrchestrationTraceContexts = new Stack<TraceContextBase>(restored.OrchestrationTraceContexts); return restored; } else { return TraceContextFactory.Empty; } }
- 投稿日:2019-09-27T00:50:23+09:00
C#でTypeInitializationExceptionが発生する
稀によくある"TypeInitializationException"が発生しがちな原因と原理の説明、および気を付ける点をまとめてみました。
詳細は以下からお願いします。
C#でTypeInitializationExceptionが発生する