20211012のC#に関する記事は9件です。

OpenXMLでExcelファイルを操作しよう (3) - セルの値を読み込みたい

はじめに 『C#コードレシピ集』では掲載できなかったOpen XMLを使ってExcelを操作するレシピを5回に分けて紹介しています。 OpenXMLでExcelファイルを操作しよう (1) - Excelファイルを新規作成したい OpenXMLでExcelファイルを操作しよう (2) - セルに値を設定したい OpenXMLでExcelファイルを操作しよう (3) - セルの値を読み込みたい ← 今ここ OpenXMLでExcelファイルを操作しよう (4) - 全てのセルの値を取得したい OpenXMLでExcelファイルを操作しよう (5) - セルに書式を設定したい コードレシピの環境は、C#9.0 + .NET 5 のコンソールアプリケーションです。 なお、『C#コードレシピ集』では、NPOIを使ってエクセルファイルを操作するレシピを掲載しています。 定義するメソッド 前回は、指定したセルに値を設定するコードを示しまましたが、今度は指定したセルの値を取得するコードを示しましょう。 前回同様、MyExcelBookクラスに新しい機能を追加します。追加するのは以下の4つのメソッドです。 メソッド 機能 Open エクセルファイルをオープン (静的メソッド) GetCell セルオブジェクトを取得 GetValueAtCell セルの値を取得 (column, row指定) GetValueAtCell セルの値を取得 (Cell指定) 今回は、既存のエクセルファイルを扱えるように、Openメソッドも定義しています。 MyExcelBookクラス では、MyExcelBookクラスを示します。コードが長くなるので、前回までのコードは省略します。 using System; using System.Linq; using System.Text; using System.Collections.Generic; using DocumentFormat.OpenXml; using DocumentFormat.OpenXml.Spreadsheet; using DocumentFormat.OpenXml.Packaging; sealed class MyExcelBook : IDisposable { private MyExcelBook() { } public void Dispose() => _document?.Dispose(); private SpreadsheetDocument _document; private WorkbookPart _workbookpart; private WorksheetPart _worksheetPart; private Sheets _sheets; // 前回までに定義したメソッド. 中身は省略 public static MyExcelBook CreateBook(string filepath) { ... } public void Save() { ... } public void CreateSheet(string sheetname) { ... } public void SelectSheet(string sheetName) { ... } public Cell CreateCell(string columnName, uint rowIndex) { ... } public void SetValueAtCell(Cell cell, string text) { ... } public void SetValue(string text, string columnName, uint rowIndex) { ... } // 以降が追加したメンバー // 既存のエクセルファイルをオープンする public static MyExcelBook Open(string filePath) { var obj = new MyExcelBook(); obj._document = SpreadsheetDocument.Open(filePath, false); obj._workbookpart = obj._document.WorkbookPart; return obj; } // 指定したセルオブジェクトを取得する public Cell GetCell(string columnName, uint rowIndex) { string addressName = columnName + rowIndex; return _worksheetPart.Worksheet.Descendants<Cell>() .Where(c => c.CellReference == addressName) .FirstOrDefault(); } // セルの値を取得する (column, row指定) public string GetCellValue(string columnName, uint rowIndex) { return GetCellValue(GetCell(columnName, rowIndex)); } // セルの値を取得する (Cell指定) public string GetCellValue(Cell cell) { if (cell == null) return null; // 式かどうか判定 if (cell.CellFormula != null) // 式ならTextプロパティを返す。 return cell.CellValue.Text; var value = cell.InnerText; if (cell.DataType == null) return value; // セルのデータタイプによって処理を振り分ける switch (cell.DataType.Value) { case CellValues.SharedString: // 文字列 // 単一の SharedStringTablePart への参照を取得する必要がある var stringTable = _workbookpart .GetPartsOfType<SharedStringTablePart>() .FirstOrDefault(); if (stringTable != null) { // valueはindexを表している。SharedStringTableから要素を取得 var element = stringTable.SharedStringTable .ElementAt(int.Parse(value)); // そのInnterTextがセルの値 value = element.InnerText; } break; case CellValues.Boolean: // ブール値 value = (value != "0").ToString().ToLower(); break; } return value; } } MyExcelBookクラスを使ってセルの値を取得する 上記のMyExcelBookクラスを使って、セルの値を取得するコードを示します。 const string fileName = "example.xlsx"; using var xls = MyExcelBook.Open(fileName); xls.SelectSheet("Sheet1"); var value = xls.GetCellValue("A", 1); Console.WriteLine(value); value = xls.GetCellValue("B", 2); Console.WriteLine(value); value = xls.GetCellValue("C", 3); Console.WriteLine(value); value = xls.GetCellValue("D", 4); var v = DateTime.FromOADate(double.Parse(value)); Console.WriteLine(v); 実行結果 上記のエクセルファイルを入力ファイルとした場合の結果を以下に示します。 Text 1 Text 2 Text 3 2020/12/06 12:40:00 補足 このサンプルコードでは、日付のデータは数字文字列として取得されます。DateTime型にしたい場合には、この値をdouble型に変換後、DateTime.FromOADateメソッドでDateTime型に変換します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UnityのOnCollisionEnter、OnTriggerEnter終止符

っしゃす。 なんか衝突でヒットする記事だいたい間違ってるので、自分のために検証して残しておきます。 検証内容・結果は最後に回します。暇な人は見てください。 結論 衝突検知にはRigidBodyが必要だが、どちらかについてれば良い。 誰ですか、「両方RigidBodyが必要」とか言ってる人は。 衝突検知の際には動いてるかに関わらず、両方のコールバックが呼び出される。 2つともisTriggerがfalseの場合はOnCollisionEnter、それ以外はOnTriggerEnterが呼び出される。 なるべく動く方にRigidBodyを付ける(結果の表の注参照) ところで、Colliderは衝突を検知するためのコンポーネントだが、RigidBodyは動くためのコンポーネントであるため、ステージの「動かない壁」などに用いるのはお勧めしない。ただし衝突検知にはRigidBodyが必要となる。 追記 衝突判定はColliderの空間にRigidBodyが含まれる場合に呼び出される。オブジェクトの速度が速すぎてCollider空間に入るフレームがない場合、呼び出されない。 例えばtフレームでcolliderの寸前まで来ていたオブジェクトがt+1フレームでColliderより奥に移動した場合、衝突は発生しない。 銃弾のような速さのオブジェクトの場合、1フレームの移動量分のRaycastで近似することでのみ衝突検知が可能で、自作した方が早い。 検証環境 Unity2020.3.1f1 RigidBodyのCollisionDetectionはContinuousSpeculative。 コード ColliderTest.cs using UnityEngine; public class ColliderTest : MonoBehaviour { private void OnTriggerEnter(Collider other) { Debug.Log("OnTriggerEnterが起きたよ", gameObject); } private void OnCollisionEnter(Collision collision) { Debug.Log("OnCollisionEnterが起きたよ", gameObject); } } シーン この表の前提 ここでは二つのオブジェクトA、Bとする。 動くオブジェクトは片方のみか、両方の場合がある。どちらも動かない場合衝突しない。 Colliderは当たり前だが、2つともついてるとする。 RigidBody列の〇はRididbodyあり、×はなし。 isTrigger列は〇はisTrigger=true、×はfalse。 コールバック列はOnTriggerEnterが呼び出される場合T、OnCollisionEnterが呼び出される場合C、いずれも呼び出されない場合×。 結果 動くオブジェクト RigidBody(A) RigidBody(B) isTrigger(A) isTrigger(B) コールバック(A) コールバック(B) A × × × × × × A × × 〇 × × × A × × × 〇 × × A × × 〇 〇 × × A 〇 × × × C C A 〇 × 〇 × T T A 〇 × × 〇 T T A 〇 × 〇 〇 T T A × 〇 × × C△ C△ A × 〇 〇 × T T A × 〇 × 〇 T T A × 〇 〇 〇 T T A 〇 〇 × × C C A 〇 〇 〇 × T T A 〇 〇 × 〇 T T A 〇 〇 〇 〇 T T A、B × × × × × × A、B × × 〇 × × × A、B × × × 〇 × × A、B × × 〇 〇 × × A、B 〇 × × × C C A、B 〇 × 〇 × T T A、B 〇 × × 〇 T T A、B 〇 × 〇 〇 T T A、B × 〇 × × C C A、B × 〇 〇 × T T A、B × 〇 × 〇 T T A、B × 〇 〇 〇 T T A、B 〇 〇 × × C C A、B 〇 〇 〇 × T T A、B 〇 〇 × 〇 T T A、B 〇 〇 〇 〇 T T 注)結果のうち△は非常に稀にコールバックが確認された。使うべきではない。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rider 2021.3 EAP3 でファイル保存時のコード整形が標準機能になったらしい

概要 Rider 2021.3 EAP3 からファイル保存時にコード整形を行う設定が追加されました。 従来はマクロを使用して ⌘S のショートカットを上書きするなどしてファイル保存時のコード整形を行なっていましたが、今回のアップデートで簡単に設定できるようになったようです。 設定方法 File | Settings | Tools | Actions on Save で Reformat and Cleanup Code を有効にします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[ C# .Net Core ] GitHubから最新のreleaseのダウンロードリンクを取得する方法、コード

はじめに 自動ダウンロードのプログラムを作っててGitHubからのダウンロードをしたくなって作った関数の見直し用及び紹介です。 詳細 ユーザーネームとリポジトリ名で最新のReleaseを取得します。 単純に取得することだけを目的にしているため、非同期処理ではありません。 必要に応じて追加することをお勧めします。 使用ライブラリ AngleSharp NuGetからインストールします。 コード using AngleSharp.Html.Parser; using System.Collections.Generic; using System.IO; using System.Net; public static class ClassName { public static string[] GetGitHubLatestRelease(string username, string repository, bool getArchive = false) { var targetURL = Combine("https://github.com/", username, repository, "releases", "latest"); var html = new WebClient().DownloadString(targetURL); var htmlDocument = new HtmlParser().ParseDocument(html); var urlElements = htmlDocument.QuerySelectorAll("div.Box--condensed a"); var downloadLinks = new List<string>(); foreach (var urlElement in urlElements) { var url = urlElement.GetAttribute("href"); if (!url.Contains("archive") ^ getArchive) downloadLinks.Add(Combine("https://github.com/" + url)); } return downloadLinks.ToArray(); } private static string Combine(params string[] paths) { string path = string.Empty; foreach (var str in paths) path = Path.Combine(path, str); return path.Replace(@"\", "/"); } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[ C# .Net Core ] GitHubから最新のreleaseのダウンロードリンクを取得する方法。 APIあり、なし、どちらも

はじめに 自動ダウンロードのプログラムを作っててGitHubからのダウンロードをしたくなって作った関数の見直し用及び紹介です。 詳細 ユーザーネームとリポジトリ名で最新のReleaseを取得します。 単純に取得することだけを目的にしているため、非同期処理ではありません。 必要に応じて追加することをお勧めします。 GitHub API使用 使用ライブラリ Newtonsoft.Json NuGetからインストールします。 コード using System.Collections.Generic; using Newtonsoft.Json.Linq; using System.IO; using System.Net; public static class ClassNamee { public static string[] GetGitHubLatestRelease(string owner, string repo) { var api = Combine("https://api.github.com/", "repos", owner, repo, "releases", "latest"); var links = new List<string>(); var request = WebRequest.Create(api); request.Headers.Add("User-Agent", "0"); using var reader = new StreamReader(request.GetResponse().GetResponseStream()); var json = JObject.Parse(reader.ReadToEnd()); foreach (var link in json["assets"]) links.Add((string)link["browser_download_url"]); return links.ToArray(); } private static string Combine(params string[] paths) { string path = string.Empty; foreach (var str in paths) path = Path.Combine(path, str); return path.Replace(@"\", "/"); } } APIなし 使用ライブラリ AngleSharp NuGetからインストールします。 コード using AngleSharp.Html.Parser; using System.Collections.Generic; using System.IO; using System.Net; public static class ClassName { public static string[] GetGitHubLatestRelease(string username, string repository, bool getArchive = false) { var targetURL = Combine("https://github.com/", username, repository, "releases", "latest"); var html = new WebClient().DownloadString(targetURL); var htmlDocument = new HtmlParser().ParseDocument(html); var urlElements = htmlDocument.QuerySelectorAll("div.Box--condensed a"); var downloadLinks = new List<string>(); foreach (var urlElement in urlElements) { var url = urlElement.GetAttribute("href"); if (!url.Contains("archive") ^ getArchive) downloadLinks.Add(Combine("https://github.com/" + url)); } return downloadLinks.ToArray(); } private static string Combine(params string[] paths) { string path = string.Empty; foreach (var str in paths) path = Path.Combine(path, str); return path.Replace(@"\", "/"); } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LINQ to SQL で Nullable object must have a value. で怒られたときは

前置き 市区町村コードとそれに紐づく名前の一覧をDBからとってくる、という簡単な機能を作成している。 Cityテーブルには市区町村コード、都道府県コード、名称、カナ名称を含むが、そのまま一覧にすると以下のような問題があった。 * 福岡県の県庁所在地である福岡市よりも北九州市のほうが前にきてしまう。(北九州市のほうが先に政令指定都市になったため市区町村コードの建制順が北九州市、福岡市の順になっている) * 政令指定都市(大阪市など)や、県名と同名の県庁所在地(奈良市など)は、郵便物の宛名などに表示するときは都道府県名を省略したい Cityテーブルは日本郵便の郵便番号簿からバッチ処理によって毎月自動生成される仕様なので、上記を実現するための情報は別テーブルCityExtに持たせ、利用するときは Cityに左外部結合 (LEFT OUTER JOIN) することにする。 発生した問題 C# のLINQ to SQLで、以下のようなクエリを書いて実行したところ Nullable object must have a value. というお叱りを受けてしまった。 var q = from city in _context.City from ext in _context.CityExt.Where(r => r.Code == city.Code).DefaultIfEmpty() where city.Code == code select new { city.Name, ext.Designated, ext.SameNameCapital, ext.SortModifier }; 原因 DefaultIfEmpty を使って、Nullのときはちゃんと空のCityExt インスタンスを作成してくれるから問題ないはず、と思っていたのだが、実のところエラーが発生するのはそのインスタンスを作成しようとして作成できないからだ。 なぜなら、CityExt.Designatedも、CityExt.SameNameCapitalも、型は Null非許容のboolに設定している。Nullを許容しないからといって勝手にfalseをセットしてくれるわけではない。 解決法 これを解決するには、CityExtモデルクラスのコンストラクタでデフォルト値を入れてやるか、以下のように DefaultIfEmpty の引数にデフォルト値を明示してやるといい。 var q = from city in _context.City from ext in _context.CityExt.Where(r => r.Code == city.Code) .DefaultIfEmpty(new CityExt { Code = city.Code, Designated = false, SameNameCapital = false, SortModifier = null }) where city.Code == code select new { city.Name, ext.Designated, ext.SameNameCapital, ext.SortModifier };
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

今さら OPC DA で接続してみる

OPC クライアントのアプリケーション(OPC DA Classic)を C#で作成した時のメモです。 OPC Server に [接続]して [タグ値の読込] [タグへの値書込]と[クローズ] をおこないました。 出来る限り,C#コードの説明を中心に,関連事項は簡素化してます。 準備したもの OPC Server と PLC PLCをOPC ServerにEthernetで接続しタグの現在値を表示できるようにします。 ・OPC Server: たけびしDeviceXPlore v6.4.0.1(x86)デモ版をインストール。(1時間動きます) ・PLC: 三菱電機 FX5UCPU。 ・GX Works3 でPLCの内蔵Ethernetポートを設定します。 (GX Works3 Version1 1.066U を使いました) ・OPC Serverを操作して PLCとの接続, 数点のタグを登録します。 (OPC Server と GX Works3 の操作は,説明書を参照しました) ・OPC Serverをインストールすると同時にインストールされるOPC Clientを使ってタグの値の 読込/書込の操作を行っておくとアプリケーション作成の理解に手助けとなります。 C# ・Visual Studio 2017 で 新規プロジェクト [Windows フォームアプリケーション(.NET Framework) Visual C#] を作成しました。 ・[プロジェクト] -> [参照の追加] を選択して表示される [参照マネージャー] から [タイプライブラリ]を選択し[OPC DA Automation Wrapper] にチェックを入れて[OK]します。 (VS2019では [OPC DA Automation Wrapper]は表示されませんでした。) ・Form1のコード Form1.cs を開きます。 using OPCAutomation を追加します。 オブジェクトブラウザーでOPCAutomationが確認できます。 using System; using System.xxxxxxx; // // using OPCAutomation; ・OPC ServerをインストールしたPCとC#で作成のOPC Client アプリケーションは同じPCで 起動してください。別のPCで起動する場合,接続のための設定が必要となります。 参考コード (ひとつの例に過ぎません。間違いがあったらお知らせ頂くと有り難いです) OPCServer object を 宣言 // OPC Server object public OPCServer ConnectedOPCServer; // OPCGroups object public OPCGroup ConnectedGroup; 引数にOPC Server の情報を入れて, sProgID="Takebishi.Dxp.6"; OPC ServerのID sNode="XXX.XXX.XXX.XXX"; ipアドレス さて, OPC Server へ Connect で [接続]...。 public bool StartOpc(string sProgID, string sNode) { sErrMsg = ""; try { ConnectedOPCServer = new OPCServer(); ConnectedOPCServer.Connect(sProgID, sNode); } catch (Exception ex) { sErrMsg = ex.Message; return true; } return false; } グループ名のAdd 次にグループ名をAddします。そして,OPC Serverに既に登録されているタグ名をAdd します。 ・引数 sIOGroup="Group1"; として OPCGroups.Add(sIOGroup) しました。 グループ名は,識別できる文字列を設定します。 public bool AddGroupOPC(string sIOGroup) { sErrMsg = ""; try { ConnectedGroup = ConnectedOPCServer.OPCGroups.Add(sIOGroup); ConnectedGroup.UpdateRate = 1000; } catch (Exception ex) { sErrMsg = ex.Message; return true; } return false; } TAG名称のAdd ・OPCItems.AddItems には配列(OPCItemIDs)でTAG名を渡します。 この他にもパラメータとして渡す配列が必要です。(全部で6コ) また,これらの配列は基数が[1]から処理されます。配列に値を設定する場合, その添字には注意が必要です。 ・OPC Serverに,タグを3つ登録し名称を, [1運転], [2停止], [3出力]としました。 それぞれ PLCレジスタは M0, M1, M100 に設定しています。 一方,PLC側に M0, M1, M100で自己保持回路をセットし,M100 の接点を Y0 に接続しています。 ・配列の宣言,割付です。タグの数はiNumOfItem=4として設定します。 public int iNumOfItem { get; set; } public Array OPCItemIDs; public Array ClientHandles; public Array ItemServerHandles; public Array ItemServerErrors; public Array RequestedDataTypes; public Array AccessPaths; // ・ // ・ OPCItemIDs = Array.CreateInstance(typeof(string), iNumOfItem); ClientHandles = Array.CreateInstance(typeof(Int32), iNumOfItem); ItemServerHandles = Array.CreateInstance(typeof(Int32), iNumOfItem); ItemServerErrors = Array.CreateInstance(typeof(Int32), iNumOfItem); RequestedDataTypes = Array.CreateInstance(typeof(Int16), iNumOfItem); AccessPaths = Array.CreateInstance(typeof(string), iNumOfItem); // ・ // ・ ・OPCItemIDsにタグ名称を設定します。配列[1]からの扱いとなります。タグ名称には [Device1.]を付加しておきます。([Device1.]は,OPC Serverに依る文字列ですが設定も可能です) OPCItemIDs.SetValue("Device1.1運転", 1); OPCItemIDs.SetValue("Device1.2停止", 2); OPCItemIDs.SetValue("Device1.3出力", 3); ・それでは,OPCItems.AddItems にパラメータを渡します。 public bool AddItemOPC() { sErrMsg = ""; try { ConnectedGroup.OPCItems.DefaultIsActive = true; ConnectedGroup.OPCItems.AddItems(iNumOfItem - 1, ref OPCItemIDs, ref ClientHandles, out ItemServerHandles, out ItemServerErrors, RequestedDataTypes, AccessPaths); } catch (Exception ex) { sErrMsg = ex.Message; return true; } return false; } タグへの値書込と読込 ・これには同期と非同期の2つの方式があります。今回は,同期の方式のみを扱います。 非同期はデリゲートを設定して動作完了時に呼び出しされることになります。 次回の機会に扱うことにします。 ・配列に値を代入して引数としますので宣言, 割付けします。 public Array aryValues; public Array aryErrors; // ・ // ・ aryValues = Array.CreateInstance(typeof(object), iNumOfItem); aryErrors = null; // ・ // ・ 同期書込 ・引数:タグのID(sIdItem)="1",書込値(sValWrt)="1"で書込むとタグ名称[1運転]の値が[ON] となります。PLCのラダーが処理して,[3出力]:ON, PLCの[Y0]:ON となります。 すると同時に,PLC本体前面の[Y0]位置LEDが点灯するはずです。同様に, 引数:タグのID(sIdItem)="2",書込値(sValWrt)="1"で書込むとタグ名称[2停止]の値が[ON] となります。PLCのラダーが処理して[3出力]:OFF,PLCの[Y0]:OFFとなります。 今度は,PLC本体前面の[Y0]位置LEDが消灯するはずです。 ・実際に,sIdItem="1", sValWrt="1" としてSyncWrt_OPCに引数を設定したところ, PLCの[Y0]位置のLEDが点灯しました。 public void SyncWrt_OPC(string sIdItem, string sValWrt) { sErrMsg = ""; int iIdItem = Convert.ToInt32(sIdItem); Array.Clear(aryValues, 0, aryValues.Length); aryValues.SetValue(sValWrt, iIdItem); try { ConnectedGroup.SyncWrite(iIdItem, ref ItemServerHandles, ref aryValues, out aryErrors); } catch (Exception ex) { sErrMsg = ex.Message; } } ・次に,sIdItem="2", sValWrt="1" で試ました。PLCの[Y0]位置のLEDが消灯しました。 同期読込 ・引数:タグのID(sIdItem)="1" とすれば RdValueにはタグの値, shQualitiesに品質, dtTimeStampにタイムスタンプが入ってきます。 public bool SyncRead_OPC(string sIdItem, ref string RdValue, ref short shQualities, ref DateTime dtTimeStamp) { sErrMsg = ""; int iIdItem = Convert.ToInt32(sIdItem); System.Array aryRdValues; System.Array aryErrors; object obQualities = new object(); object obTimeStamp = new object(); int iNumItems = 1; try { Array SyncServerHandles = Array.CreateInstance(typeof(Int32), iNumOfItem); SyncServerHandles.SetValue(ItemServerHandles.GetValue(iIdItem), 1); // SyncServerHandles[1]に書込む ConnectedGroup.SyncRead((short)OPCDataSource.OPCDevice, iNumItems, ref SyncServerHandles, out aryRdValues, out aryErrors, out obQualities, out obTimeStamp); var RdError = aryErrors.GetValue(1); bool bErr = Object.Equals(RdError, 0); if (!bErr) { return true; } var vdRdValue = aryRdValues.GetValue(1); RdValue = Convert.ToString (vdRdValue); var aryQualities = (Array)obQualities; shQualities = (short)aryQualities.GetValue(1); var aryTimeStamp = (Array)obTimeStamp; dtTimeStamp = (DateTime)aryTimeStamp.GetValue(1); } catch (Exception ex) { sErrMsg = ex.Message; return true; } return false; } クローズ処理 ・引数 sGrpName="Group1"として OPCItemsをRemove, OPCGroupsをRemovesして Disconnect()します。 public bool FinishOpc(string sGrpName) { sErrMsg = ""; try { if (ConnectedGroup != null) { ConnectedGroup.OPCItems.Remove(iNumOfItem - 1, ref ItemServerHandles, out aryErrors); ConnectedOPCServer.OPCGroups.Remove(sGrpName); ConnectedGroup.IsSubscribed = false; } ConnectedOPCServer.Disconnect(); } catch (Exception ex) { sErrMsg = ex.Message; return false; } return true; } 終わり ・OPC Classic で 単純な[接続],[読込],[書込],[クローズ]を行ってみました。 OPC Server のタグの登録するにあたって(余談) ・OPC Serverにタグを登録する際,PLCの説明書でPLCデバイスの種類と目的を確認 してください。 OPC Serverのタグ登録ではPLCデバイス種類を X,Y,M,B... と簡単に選択できますが,PLC側では デバイスの種類は用途によって大きく異なります。例えば, PLC:FX5UCPUのデバイス種類 の[Y]の目的は出力です。OPCタグを[Y]に割付けるとOPCタグ値の1/0 がPLCの出力端子に ON/OFFとしてそのまま出力されます。 (最終の出力はIOリフレッシュがあるのでPLC側プログラムに依ります) PLCが設置される"XX盤"ではOPCのタグといえども外部信号には違いありません。外部信号は 端子台からPLCへ結線されるものです。ところが結線は不可能なので,OPCタグの登録には 慎重さが必要となります。OPCタグをいきなり[Y]に割付したりして... たとえが古いですが,操作盤の[遠方]<->[手元]のセレクタを[遠方]にした途端にマグネットが どうなるか... 推して知るべし です。 参照した資料 https://www.codeproject.com/Articles/490072/DA-1-OPC-Wrapper-DLL-and-Client-Example Data Access Automation Interface Standard Version 2.02 February 4, 1999
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unityのインスペクター上で列挙型リストを動的に表示する

Dictionaryに追加されたKeyや要素をインスペクター上に"列挙型"として表示するためのスクリプトです。DictionaryはSeiralizeできないので、インスペクター上で内容を確認することはできません。 ただ内容を表示するだけならDebug.logで構わないのですが、要素にdelegate型等を指定して「keyに応じたなにかしらの動作のチェックを行いたい」場合、インスペクターにGUILayout.Buttonで全てのkeyを書くのは非常に大変です。 そこでDictionaryの内容を列挙型で取得できたら非常に便利なんじゃないかと組んでみました。 InspectorEnumPopup.cs using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEditor; public class InspectorEnumPopup : MonoBehaviour { public Dictionary<string, string> Dic = new Dictionary<string, string>(); void Start() { Dic.Add("Hello", "こんにちわ"); Dic.Add("Bye", "さようなら"); } } #if UNITY_EDITOR //CustomEditor [CustomEditor(typeof(InspectorEnumPopup))] public class InspectorEnumPopupCustom : Editor { private string[] EnumList = new string[] { "None" }; private int Idx = 0; private InspectorEnumPopup Content; public void EnumUpdate() { EnumList = Content.Dic.Keys.ToArray(); } public override void OnInspectorGUI() { Content = target as InspectorEnumPopup; base.OnInspectorGUI(); Idx = EditorGUILayout.Popup("List", Idx, EnumList); if (GUILayout.Button("Enum.Update")) { if (Application.isPlaying) { EnumUpdate(); } } if (GUILayout.Button("Check Dictionary")) { if (Application.isPlaying) { Debug.Log("Key:" + EnumList[Idx] + " Element:" + Content.Dic[EnumList[Idx]]); } } } } #endif ここではDictionaryの内容を表示しているだけですが、本来の目的はエディターでの動作確認を簡単にする為のスクリプトです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

非同期処理メモ

参考資料 スレッドプールとタスク ++C++; // 未確認飛行 C 同期処理とは 処理が文字通り順番に処理されていく 一つの処理が終わるまで次の処理に移ることができない 非同期処理とは 処理が順番に処理されていかない 一つの処理を行っている最中、他の処理を行うことができる 非同期処理の種類 ここでは同期処理のやり方は省きます。 非同期学ぶ方は既にご存知だと思うので。 詳細 1.スレッド(Thread) CPUでよく聞くスレッドと同じもの 既定値は各パソコンのCPUのコア数と同じ。 スレッドは規定値よりも多くのスレッドを生成することが難しい(または遅延が発生する)為、 スレッドを用いた非同期処理ができる量は限られている。 Thread.Thread(ThreadStart start) // クラスの新しいインスタンスを初期化 delegate void system.Threading.ThreadStart() // 実行するメソッドを表すThread 2.タスク(Task) タスク = やること(日本語訳) タスクはスレッドと違い制限がない スレッド数を気にせず多くの非同期処理を実装することが可能 Task Task.Run(Action action) // スレッドプール上で実行する指定された作業をキューに配置 スレッドプールとは: 幾つかのスレッドを予め生成しておき、 一度作ったスレッドを可能な限り使いまわすような仕組みのこと。 タスクが限りなく多くの非同期処理を行うことができる理由。 キューとは: 限りあるスレッドに対して幾つかのタスクをリスト化し、並べているものである。 タスクとスレッドどっちがいいの? 使い方にもよると思うけど、タスクの方が柔軟性は高いんじゃないかなと思います。 例えば一時処理を停止する時: スレッド スレッド自体を止めてしまうため、中断中のスレッドが一つ丸ごと指定されたミリ秒の間使用不可となる。 Thread.Sleep(1000); // 指定したミリ秒の間現在のスレッドを中断します。 タスク Task.Delay()の下にある処理を遅延後に実行されるタスクとして作成。 そして再びタスクとしてキューへ入れるため、この遅延している間は他のタスクが処理することが可能。 Task.Delay(1000); // 遅延後に完了するタスクを作成します。 これだけでもタスクの方が便利であるということが言えると思います。 他にもいろいろとタスクの方が便利じゃない?っていうものがありますが、今回は割愛。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む